--- linux-2.6.9-rc1/arch/alpha/kernel/alpha_ksyms.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/alpha/kernel/alpha_ksyms.c 2004-09-02 20:51:51.000000000 -0700 @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -136,7 +137,9 @@ EXPORT_SYMBOL(pci_dac_dma_supported); EXPORT_SYMBOL(pci_dac_page_to_dma); EXPORT_SYMBOL(pci_dac_dma_to_page); EXPORT_SYMBOL(pci_dac_dma_to_offset); +EXPORT_SYMBOL(alpha_gendev_to_pci); #endif +EXPORT_SYMBOL(dma_set_mask); EXPORT_SYMBOL(dump_thread); EXPORT_SYMBOL(dump_elf_thread); --- linux-2.6.9-rc1/arch/alpha/kernel/console.c 2004-08-24 00:03:49.000000000 -0700 +++ 25/arch/alpha/kernel/console.c 2004-09-03 00:56:33.060134744 -0700 @@ -47,7 +47,7 @@ locate_and_init_vga(void *(*sel_func)(vo if (!sel_func) sel_func = (void *)default_vga_hose_select; - for(dev=NULL; (dev=pci_find_class(PCI_CLASS_DISPLAY_VGA << 8, dev));) { + for(dev=NULL; (dev=pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, dev));) { if (!hose) hose = dev->sysdata; else hose = sel_func(hose, dev->sysdata); } --- linux-2.6.9-rc1/arch/alpha/kernel/irq.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/alpha/kernel/irq.c 2004-09-02 20:51:51.000000000 -0700 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -83,6 +84,7 @@ handle_IRQ_event(unsigned int irq, struc struct irqaction *action) { int status = 1; /* Force the "do bottom halves" bit */ + int ret; do { if (!(action->flags & SA_INTERRUPT)) @@ -90,8 +92,9 @@ handle_IRQ_event(unsigned int irq, struc else local_irq_disable(); - status |= action->flags; - action->handler(irq, action->dev_id, regs); + ret = action->handler(irq, action->dev_id, regs); + if (ret == IRQ_HANDLED) + status |= action->flags; action = action->next; } while (action); if (status & SA_SAMPLE_RANDOM) @@ -288,31 +291,6 @@ irq_affinity_write_proc(struct file *fil return full_count; } -static int -prof_cpu_mask_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - int len = cpumask_scnprintf(page, count, *(cpumask_t *)data); - if (count - len < 2) - return -EINVAL; - len += sprintf(page + len, "\n"); - return len; -} - -static int -prof_cpu_mask_write_proc(struct file *file, const char __user *buffer, - unsigned long count, void *data) -{ - unsigned long full_count = count, err; - cpumask_t new_value, *mask = (cpumask_t *)data; - - err = cpumask_parse(buffer, count, new_value); - if (err) - return err; - - *mask = new_value; - return full_count; -} #endif /* CONFIG_SMP */ #define MAX_NAMELEN 10 @@ -350,14 +328,9 @@ register_irq_proc (unsigned int irq) #endif } -unsigned long prof_cpu_mask = ~0UL; - void init_irq_proc (void) { -#ifdef CONFIG_SMP - struct proc_dir_entry *entry; -#endif int i; /* create /proc/irq */ @@ -365,12 +338,7 @@ init_irq_proc (void) #ifdef CONFIG_SMP /* create /proc/irq/prof_cpu_mask */ - entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); - - entry->nlink = 1; - entry->data = (void *)&prof_cpu_mask; - entry->read_proc = prof_cpu_mask_read_proc; - entry->write_proc = prof_cpu_mask_write_proc; + create_prof_cpu_mask(root_irq_dir); #endif /* --- linux-2.6.9-rc1/arch/alpha/kernel/irq_impl.h 2004-08-24 00:03:49.000000000 -0700 +++ 25/arch/alpha/kernel/irq_impl.h 2004-09-02 20:51:51.000000000 -0700 @@ -40,32 +40,3 @@ extern struct hw_interrupt_type i8259a_i extern void init_i8259a_irqs(void); extern void handle_irq(int irq, struct pt_regs * regs); - -extern unsigned long prof_cpu_mask; - -static inline void -alpha_do_profile(unsigned long pc) -{ - extern char _stext; - - if (!prof_buffer) - return; - - /* - * Only measure the CPUs specified by /proc/irq/prof_cpu_mask. - * (default is all CPUs.) - */ - if (!((1<>= prof_shift; - /* - * Don't ignore out-of-bounds PC values silently, - * put them into the last histogram slot, so if - * present, they will show up as a sharp peak. - */ - if (pc > prof_len - 1) - pc = prof_len - 1; - atomic_inc((atomic_t *)&prof_buffer[pc]); -} --- linux-2.6.9-rc1/arch/alpha/kernel/osf_sys.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/arch/alpha/kernel/osf_sys.c 2004-09-02 20:51:51.000000000 -0700 @@ -1079,18 +1079,10 @@ osf_getrusage(int who, struct rusage32 _ r.ru_majflt = current->maj_flt; break; case RUSAGE_CHILDREN: - jiffies_to_timeval32(current->cutime, &r.ru_utime); - jiffies_to_timeval32(current->cstime, &r.ru_stime); - r.ru_minflt = current->cmin_flt; - r.ru_majflt = current->cmaj_flt; - break; - default: - jiffies_to_timeval32(current->utime + current->cutime, - &r.ru_utime); - jiffies_to_timeval32(current->stime + current->cstime, - &r.ru_stime); - r.ru_minflt = current->min_flt + current->cmin_flt; - r.ru_majflt = current->maj_flt + current->cmaj_flt; + jiffies_to_timeval32(current->signal->cutime, &r.ru_utime); + jiffies_to_timeval32(current->signal->cstime, &r.ru_stime); + r.ru_minflt = current->signal->cmin_flt; + r.ru_majflt = current->signal->cmaj_flt; break; } --- linux-2.6.9-rc1/arch/alpha/kernel/pci_iommu.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/alpha/kernel/pci_iommu.c 2004-09-02 20:51:51.000000000 -0700 @@ -930,3 +930,42 @@ pci_dac_dma_to_offset(struct pci_dev *pd { return (dma_addr & ~PAGE_MASK); } + + +/* Helper for generic DMA-mapping functions. */ + +struct pci_dev * +alpha_gendev_to_pci(struct device *dev) +{ + if (dev && dev->bus == &pci_bus_type) + return to_pci_dev(dev); + + /* Assume that non-PCI devices asking for DMA are either ISA or EISA, + BUG() otherwise. */ + BUG_ON(!isa_bridge); + + /* Assume non-busmaster ISA DMA when dma_mask is not set (the ISA + bridge is bus master then). */ + if (!dev || !dev->dma_mask || !*dev->dma_mask) + return isa_bridge; + + /* For EISA bus masters, return isa_bridge (it might have smaller + dma_mask due to wiring limitations). */ + if (*dev->dma_mask >= isa_bridge->dma_mask) + return isa_bridge; + + /* This assumes ISA bus master with dma_mask 0xffffff. */ + return NULL; +} + +int +dma_set_mask(struct device *dev, u64 mask) +{ + if (!dev->dma_mask || + !pci_dma_supported(alpha_gendev_to_pci(dev), mask)) + return -EIO; + + *dev->dma_mask = mask; + + return 0; +} --- linux-2.6.9-rc1/arch/alpha/kernel/pci-noop.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/alpha/kernel/pci-noop.c 2004-09-02 20:51:51.000000000 -0700 @@ -7,8 +7,10 @@ #include #include #include +#include #include #include +#include #include "proto.h" @@ -101,41 +103,100 @@ sys_pciconfig_write(unsigned long bus, u else return -ENODEV; } -/* stubs for the routines in pci_iommu.c */ + +/* Stubs for the routines in pci_iommu.c: */ + void * pci_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_addrp) { return NULL; } + void pci_free_consistent(struct pci_dev *pdev, size_t size, void *cpu_addr, dma_addr_t dma_addr) { } + dma_addr_t pci_map_single(struct pci_dev *pdev, void *cpu_addr, size_t size, int direction) { return (dma_addr_t) 0; } + void pci_unmap_single(struct pci_dev *pdev, dma_addr_t dma_addr, size_t size, int direction) { } + int pci_map_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents, int direction) { return 0; } + void pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents, int direction) { } + int pci_dma_supported(struct pci_dev *hwdev, dma_addr_t mask) { return 0; } + +/* Generic DMA mapping functions: */ + +void * +dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, int gfp) +{ + void *ret; + + if (!dev || *dev->dma_mask >= 0xffffffffUL) + gfp &= ~GFP_DMA; + ret = (void *)__get_free_pages(gfp, get_order(size)); + if (ret) { + memset(ret, 0, size); + *dma_handle = virt_to_bus(ret); + } + return ret; +} + +EXPORT_SYMBOL(dma_alloc_coherent); + +int +dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, + enum dma_data_direction direction) +{ + int i; + + for (i = 0; i < nents; i++ ) { + void *va; + + BUG_ON(!sg[i].page); + va = page_address(sg[i].page) + sg[i].offset; + sg_dma_address(sg + i) = (dma_addr_t)virt_to_bus(va); + sg_dma_len(sg + i) = sg[i].length; + } + + return nents; +} + +EXPORT_SYMBOL(dma_map_sg); + +int +dma_set_mask(struct device *dev, u64 mask) +{ + if (!dev->dma_mask || !dma_supported(dev, mask)) + return -EIO; + + *dev->dma_mask = mask; + + return 0; +} --- linux-2.6.9-rc1/arch/alpha/kernel/process.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/alpha/kernel/process.c 2004-09-02 20:51:51.000000000 -0700 @@ -246,8 +246,7 @@ alpha_clone(unsigned long clone_flags, u if (!usp) usp = rdusp(); - return do_fork(clone_flags & ~CLONE_IDLETASK, usp, regs, 0, - parent_tid, child_tid); + return do_fork(clone_flags, usp, regs, 0, parent_tid, child_tid); } int --- linux-2.6.9-rc1/arch/alpha/kernel/signal.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/alpha/kernel/signal.c 2004-09-02 20:51:51.000000000 -0700 @@ -469,9 +469,7 @@ setup_frame(int sig, struct k_sigaction return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } static void @@ -533,9 +531,7 @@ setup_rt_frame(int sig, struct k_sigacti return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } @@ -608,21 +604,20 @@ do_signal(sigset_t *oldset, struct pt_re siginfo_t info; int signr; unsigned long single_stepping = ptrace_cancel_bpt(current); + struct k_sigaction ka; if (!oldset) oldset = ¤t->blocked; /* This lets the debugger run, ... */ - signr = get_signal_to_deliver(&info, regs, NULL); + signr = get_signal_to_deliver(&info, &ka, regs, NULL); /* ... so re-check the single stepping. */ single_stepping |= ptrace_cancel_bpt(current); if (signr > 0) { /* Whee! Actually deliver the signal. */ - struct k_sigaction *ka = ¤t->sighand->action[signr-1]; - - if (r0) syscall_restart(r0, r19, regs, ka); - handle_signal(signr, ka, &info, oldset, regs, sw); + if (r0) syscall_restart(r0, r19, regs, &ka); + handle_signal(signr, &ka, &info, oldset, regs, sw); if (single_stepping) ptrace_set_bpt(current); /* re-set bpt */ return 1; --- linux-2.6.9-rc1/arch/alpha/kernel/smp.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/alpha/kernel/smp.c 2004-09-03 00:56:40.978930904 -0700 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -35,7 +36,6 @@ #include #include #include -#include #include #include @@ -411,15 +411,6 @@ secondary_cpu_start(int cpuid, struct ta return 0; } -static struct task_struct * __init -fork_by_hand(void) -{ - /* Don't care about the contents of regs since we'll never - reschedule the forked task. */ - struct pt_regs regs; - return copy_process(CLONE_VM|CLONE_IDLETASK, 0, ®s, 0, NULL, NULL); -} - /* * Bring one cpu online. */ @@ -435,15 +426,10 @@ smp_boot_one_cpu(int cpuid) the other task-y sort of data structures set up like we wish. We can't use kernel_thread since we must avoid rescheduling the child. */ - idle = fork_by_hand(); + idle = fork_idle(cpuid); if (IS_ERR(idle)) panic("failed fork for CPU %d", cpuid); - wake_up_forked_process(idle); - - init_idle(idle, cpuid); - unhash_process(idle); - DBGS(("smp_boot_one_cpu: CPU %d state 0x%lx flags 0x%lx\n", cpuid, idle->state, idle->flags)); @@ -613,8 +599,7 @@ smp_percpu_timer_interrupt(struct pt_reg struct cpuinfo_alpha *data = &cpu_data[cpu]; /* Record kernel PC. */ - if (!user) - alpha_do_profile(regs->pc); + profile_tick(CPU_PROFILING, regs); if (!--data->prof_counter) { /* We need to make like a normal interrupt -- otherwise --- linux-2.6.9-rc1/arch/alpha/kernel/time.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/alpha/kernel/time.c 2004-09-02 20:51:51.000000000 -0700 @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -118,8 +119,7 @@ irqreturn_t timer_interrupt(int irq, voi #ifndef CONFIG_SMP /* Not SMP, do kernel PC profiling here. */ - if (!user_mode(regs)) - alpha_do_profile(regs->pc); + profile_tick(CPU_PROFILING, regs); #endif write_seqlock(&xtime_lock); @@ -366,7 +366,7 @@ time_init(void) BCD_TO_BIN(year); } - /* PC-like is standard; used for year < 20 || year >= 70 */ + /* PC-like is standard; used for year >= 70 */ epoch = 1900; if (year < 20) epoch = 2000; --- linux-2.6.9-rc1/arch/alpha/kernel/vmlinux.lds.S 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/alpha/kernel/vmlinux.lds.S 2004-09-02 20:51:51.000000000 -0700 @@ -46,11 +46,6 @@ SECTIONS __setup_end = .; . = ALIGN(8); - __start___param = .; - __param : { *(__param) } - __stop___param = .; - - . = ALIGN(8); __initcall_start = .; .initcall.init : { *(.initcall1.init) --- linux-2.6.9-rc1/arch/alpha/lib/csum_partial_copy.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/alpha/lib/csum_partial_copy.c 2004-09-02 20:51:51.000000000 -0700 @@ -193,7 +193,8 @@ csum_partial_cfu_dest_aligned(const unsi * This is slightly less fun than the above.. */ static inline unsigned long -csum_partial_cfu_src_aligned(const unsigned long *src, unsigned long *dst, +csum_partial_cfu_src_aligned(const unsigned long __user *src, + unsigned long *dst, unsigned long doff, long len, unsigned long checksum, unsigned long partial_dest, @@ -372,7 +373,7 @@ unsigned int csum_partial_copy_from_user(const char __user *src, char *dst, int len, unsigned int sum, int *errp) { - if (!access_ok(src, len, VERIFY_READ)) { + if (!access_ok(VERIFY_READ, src, len)) { *errp = -EFAULT; memset(dst, 0, len); return sum; --- linux-2.6.9-rc1/arch/alpha/mm/numa.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/arch/alpha/mm/numa.c 2004-09-02 20:51:51.000000000 -0700 @@ -313,7 +313,7 @@ void __init paging_init(void) zones_size[ZONE_DMA] = dma_local_pfn; zones_size[ZONE_NORMAL] = (end_pfn - start_pfn) - dma_local_pfn; } - free_area_init_node(nid, NODE_DATA(nid), NULL, zones_size, start_pfn, NULL); + free_area_init_node(nid, NODE_DATA(nid), zones_size, start_pfn, NULL); } /* Initialize the kernel's ZERO_PGE. */ --- linux-2.6.9-rc1/arch/arm26/kernel/irq.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/arm26/kernel/irq.c 2004-09-02 20:51:51.000000000 -0700 @@ -187,6 +187,7 @@ static void __do_irq(unsigned int irq, struct irqaction *action, struct pt_regs *regs) { unsigned int status; + int ret; spin_unlock(&irq_controller_lock); if (!(action->flags & SA_INTERRUPT)) @@ -194,8 +195,9 @@ __do_irq(unsigned int irq, struct irqact status = 0; do { - status |= action->flags; - action->handler(irq, action->dev_id, regs); + ret = action->handler(irq, action->dev_id, regs); + if (ret == IRQ_HANDLED) + status |= action->flags; action = action->next; } while (action); --- linux-2.6.9-rc1/arch/arm26/kernel/ptrace.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/arm26/kernel/ptrace.c 2004-09-03 00:56:56.196617464 -0700 @@ -729,11 +729,8 @@ asmlinkage void syscall_trace(int why, s /* the 0x80 provides a way for the tracing parent to distinguish between a syscall stop and SIGTRAP delivery */ - current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0); - current->state = TASK_STOPPED; - notify_parent(current, SIGCHLD); - schedule(); + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); /* * this isn't the same as continuing with a signal, but it will do * for normal use. strace only continues with a signal if the --- linux-2.6.9-rc1/arch/arm26/kernel/signal.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/arm26/kernel/signal.c 2004-09-02 20:51:51.000000000 -0700 @@ -465,9 +465,7 @@ handle_signal(unsigned long sig, siginfo return; } - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, tsk); + force_sigsegv(sig, tsk); } /* --- linux-2.6.9-rc1/arch/arm26/kernel/sys_arm.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/arm26/kernel/sys_arm.c 2004-09-02 20:51:51.000000000 -0700 @@ -256,7 +256,7 @@ asmlinkage int sys_clone(unsigned long c if (!newsp) newsp = regs->ARM_sp; - return do_fork(clone_flags & ~CLONE_IDLETASK, newsp, regs, 0, NULL, NULL); + return do_fork(clone_flags, newsp, regs, 0, NULL, NULL); } asmlinkage int sys_vfork(struct pt_regs *regs) --- linux-2.6.9-rc1/arch/arm26/kernel/time.c 2004-08-24 00:03:14.000000000 -0700 +++ 25/arch/arm26/kernel/time.c 2004-09-02 20:51:51.000000000 -0700 @@ -67,28 +67,6 @@ static unsigned long dummy_gettimeoffset */ unsigned long (*gettimeoffset)(void) = dummy_gettimeoffset; -/* - * Handle kernel profile stuff... - */ -static inline void do_profile(struct pt_regs *regs) -{ - if (!user_mode(regs) && - prof_buffer && - current->pid) { - unsigned long pc = instruction_pointer(regs); - extern int _stext; - - pc -= (unsigned long)&_stext; - - pc >>= prof_shift; - - if (pc >= prof_len) - pc = prof_len - 1; - - prof_buffer[pc] += 1; - } -} - static unsigned long next_rtc_update; /* @@ -189,7 +167,7 @@ static irqreturn_t timer_interrupt(int i { do_timer(regs); do_set_rtc(); //FIME - EVERY timer IRQ? - do_profile(regs); + profile_tick(CPU_PROFILING, regs); return IRQ_HANDLED; //FIXME - is this right? } --- linux-2.6.9-rc1/arch/arm26/kernel/vmlinux-arm26.lds.in 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/arm26/kernel/vmlinux-arm26.lds.in 2004-09-02 20:51:51.000000000 -0700 @@ -35,9 +35,6 @@ SECTIONS __early_begin = .; *(__early_param) __early_end = .; - __start___param = .; - *(__param) - __stop___param = .; __initcall_start = .; *(.initcall1.init) *(.initcall2.init) --- linux-2.6.9-rc1/arch/arm26/kernel/vmlinux-arm26-xip.lds.in 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/arm26/kernel/vmlinux-arm26-xip.lds.in 2004-09-02 20:51:51.000000000 -0700 @@ -34,9 +34,6 @@ SECTIONS __early_begin = .; *(__early_param) __early_end = .; - __start___param = .; - *(__param) - __stop___param = .; __initcall_start = .; *(.initcall1.init) *(.initcall2.init) --- linux-2.6.9-rc1/arch/arm26/mm/init.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/arm26/mm/init.c 2004-09-02 20:51:51.000000000 -0700 @@ -305,8 +305,8 @@ void __init paging_init(struct meminfo * (bdata->node_boot_start >> PAGE_SHIFT); if (!zone_size[0]) BUG(); - - free_area_init_node(0, pgdat, 0, zone_size, + pgdat->node_mem_map = NULL; + free_area_init_node(0, pgdat, zone_size, bdata->node_boot_start >> PAGE_SHIFT, zhole_size); mem_map = NODE_DATA(0)->node_mem_map; --- linux-2.6.9-rc1/arch/arm/boot/compressed/Makefile 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/arm/boot/compressed/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -8,11 +8,13 @@ HEAD = head.o OBJS = misc.o FONTC = drivers/video/console/font_acorn_8x8.c +FONT = $(addprefix ../../../../drivers/video/console/, font_acorn_8x8.o) + # # Architecture dependencies # ifeq ($(CONFIG_ARCH_ACORN),y) -OBJS += ll_char_wr.o font.o +OBJS += ll_char_wr.o $(FONT) endif ifeq ($(CONFIG_ARCH_SHARK),y) @@ -66,7 +68,7 @@ endif SEDFLAGS = s/TEXT_START/$(ZTEXTADDR)/;s/BSS_START/$(ZBSSADDR)/ -targets := vmlinux vmlinux.lds piggy.gz piggy.o font.o \ +targets := vmlinux vmlinux.lds piggy.gz piggy.o $(FONT) \ head.o misc.o $(OBJS) EXTRA_CFLAGS := -fpic EXTRA_AFLAGS := @@ -98,8 +100,7 @@ $(obj)/piggy.gz: $(obj)/../Image FORCE $(obj)/piggy.o: $(obj)/piggy.gz FORCE -CFLAGS_font.o := -Dstatic= -$(obj)/font.o: $(FONTC) +CFLAGS_font_acorn_8x8.o := -Dstatic= $(obj)/vmlinux.lds: $(obj)/vmlinux.lds.in arch/arm/boot/Makefile .config @sed "$(SEDFLAGS)" < $< > $@ --- linux-2.6.9-rc1/arch/arm/common/dmabounce.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/arch/arm/common/dmabounce.c 2004-09-02 20:51:51.000000000 -0700 @@ -121,9 +121,9 @@ alloc_safe_buffer(struct dmabounce_devic DO_STATS ( device_info->total_allocs++ ); buf = kmalloc(sizeof(struct safe_buffer), GFP_ATOMIC); - if (buf == 0) { + if (buf == NULL) { dev_warn(dev, "%s: kmalloc failed\n", __func__); - return 0; + return NULL; } if (size <= device_info->small_buffer_size) { @@ -137,16 +137,16 @@ alloc_safe_buffer(struct dmabounce_devic DO_STATS ( device_info->lbp_allocs++ ); } else { - pool = 0; + pool = NULL; safe = dma_alloc_coherent(dev, size, &safe_dma_addr, GFP_ATOMIC); } - if (safe == 0) { + if (safe == NULL) { dev_warn(device_info->dev, "%s: could not alloc dma memory (size=%d)\n", __func__, size); kfree(buf); - return 0; + return NULL; } #ifdef STATS @@ -216,27 +216,33 @@ static inline dma_addr_t map_single(struct device *dev, void *ptr, size_t size, enum dma_data_direction dir) { - dma_addr_t dma_addr; struct dmabounce_device_info *device_info = find_dmabounce_dev(dev); + dma_addr_t dma_addr; + int needs_bounce = 0; if (device_info) DO_STATS ( device_info->map_op_count++ ); + dma_addr = virt_to_dma(dev, ptr); + if (dev->dma_mask) { + unsigned long mask = *dev->dma_mask; unsigned long limit; - limit = (*dev->dma_mask + 1) & ~(*dev->dma_mask); - if (limit && (size > limit)) { - dev_err(dev, "DMA mapping too big " - "(requested %#x mask %#Lx)\n", - size, *dev->dma_mask); + limit = (mask + 1) & ~mask; + if (limit && size > limit) { + dev_err(dev, "DMA mapping too big (requested %#x " + "mask %#Lx)\n", size, *dev->dma_mask); return ~0; } - } - dma_addr = virt_to_dma(dev, ptr); + /* + * Figure out if we need to bounce from the DMA mask. + */ + needs_bounce = (dma_addr | (dma_addr + size - 1)) & ~mask; + } - if (device_info && dma_needs_bounce(dev, dma_addr, size)) { + if (device_info && (needs_bounce || dma_needs_bounce(dev, dma_addr, size))) { struct safe_buffer *buf; buf = alloc_safe_buffer(device_info, ptr, size, dir); --- linux-2.6.9-rc1/arch/arm/common/sa1111.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/arm/common/sa1111.c 2004-09-02 20:51:51.000000000 -0700 @@ -761,9 +761,6 @@ static void __sa1111_remove(struct sa111 */ int dma_needs_bounce(struct device *dev, dma_addr_t addr, size_t size) { - unsigned int physaddr = SA1111_DMA_ADDR((unsigned int)addr); - u32 dma_mask = *dev->dma_mask; - /* * Section 4.6 of the "Intel StrongARM SA-1111 Development Module * User's Guide" mentions that jumpers R51 and R52 control the @@ -771,14 +768,8 @@ int dma_needs_bounce(struct device *dev, * SDRAM bank 1 on Neponset). The default configuration selects * Assabet, so any address in bank 1 is necessarily invalid. */ - if ((machine_is_assabet() || machine_is_pfs168()) && - (addr >= 0xc8000000 || (addr + size) >= 0xc8000000)) - return 1; - - /* - * Check to see if either the start or end are illegal. - */ - return ((addr & ~dma_mask)) || ((addr + size - 1) & ~dma_mask); + return ((machine_is_assabet() || machine_is_pfs168()) && + (addr >= 0xc8000000 || (addr + size) >= 0xc8000000)); } struct sa1111_save_data { --- linux-2.6.9-rc1/arch/arm/Kconfig 2004-08-24 00:02:48.000000000 -0700 +++ 25/arch/arm/Kconfig 2004-09-02 20:51:51.000000000 -0700 @@ -402,7 +402,7 @@ config FPE_NWFPE config FPE_NWFPE_XP bool "Support extended precision" - depends on FPE_NWFPE + depends on FPE_NWFPE && !CPU_BIG_ENDIAN help Say Y to include 80-bit support in the kernel floating-point emulator. Otherwise, only 32 and 64-bit support is compiled in. @@ -428,6 +428,7 @@ config FPE_FASTFPE config VFP bool "VFP-format floating point maths" + depends on CPU_V6 || CPU_ARM926T help Say Y to include VFP support code in the kernel. This is needed if your hardware includes a VFP unit. --- linux-2.6.9-rc1/arch/arm/kernel/armksyms.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/arch/arm/kernel/armksyms.c 2004-09-02 20:51:51.000000000 -0700 @@ -105,6 +105,7 @@ EXPORT_SYMBOL_NOVERS(memcpy); EXPORT_SYMBOL_NOVERS(memmove); EXPORT_SYMBOL_NOVERS(memcmp); EXPORT_SYMBOL_NOVERS(memscan); +EXPORT_SYMBOL_NOVERS(memchr); EXPORT_SYMBOL_NOVERS(__memzero); /* user mem (segment) */ --- linux-2.6.9-rc1/arch/arm/kernel/irq.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/arch/arm/kernel/irq.c 2004-09-02 20:51:51.000000000 -0700 @@ -261,7 +261,7 @@ static int __do_irq(unsigned int irq, struct irqaction *action, struct pt_regs *regs) { unsigned int status; - int retval = 0; + int ret, retval = 0; spin_unlock(&irq_controller_lock); @@ -270,8 +270,10 @@ __do_irq(unsigned int irq, struct irqact status = 0; do { - status |= action->flags; - retval |= action->handler(irq, action->dev_id, regs); + ret = action->handler(irq, action->dev_id, regs); + if (ret == IRQ_HANDLED) + status |= action->flags; + retval |= ret; action = action->next; } while (action); --- linux-2.6.9-rc1/arch/arm/kernel/ptrace.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/arm/kernel/ptrace.c 2004-09-03 00:56:56.197617312 -0700 @@ -792,11 +792,8 @@ asmlinkage void syscall_trace(int why, s /* the 0x80 provides a way for the tracing parent to distinguish between a syscall stop and SIGTRAP delivery */ - current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0); - current->state = TASK_STOPPED; - notify_parent(current, SIGCHLD); - schedule(); + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); /* * this isn't the same as continuing with a signal, but it will do * for normal use. strace only continues with a signal if the --- linux-2.6.9-rc1/arch/arm/kernel/signal.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/arch/arm/kernel/signal.c 2004-09-02 20:51:51.000000000 -0700 @@ -573,12 +573,12 @@ static inline void restart_syscall(struc * OK, we're invoking a handler */ static void -handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset, +handle_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs, int syscall) { struct thread_info *thread = current_thread_info(); struct task_struct *tsk = current; - struct k_sigaction *ka = &tsk->sighand->action[sig-1]; int usig = sig; int ret; @@ -633,15 +633,10 @@ handle_signal(unsigned long sig, siginfo spin_unlock_irq(&tsk->sighand->siglock); } - if (ret == 0) { - if (ka->sa.sa_flags & SA_ONESHOT) - ka->sa.sa_handler = SIG_DFL; + if (ret == 0) return; - } - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, tsk); + force_sigsegv(sig, tsk); } /* @@ -655,6 +650,7 @@ handle_signal(unsigned long sig, siginfo */ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall) { + struct k_sigaction ka; siginfo_t info; int signr; @@ -675,9 +671,9 @@ static int do_signal(sigset_t *oldset, s if (current->ptrace & PT_SINGLESTEP) ptrace_cancel_bpt(current); - signr = get_signal_to_deliver(&info, regs, NULL); + signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (signr > 0) { - handle_signal(signr, &info, oldset, regs, syscall); + handle_signal(signr, &ka, &info, oldset, regs, syscall); if (current->ptrace & PT_SINGLESTEP) ptrace_set_bpt(current); return 1; --- linux-2.6.9-rc1/arch/arm/kernel/sys_arm.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/arm/kernel/sys_arm.c 2004-09-02 20:51:51.000000000 -0700 @@ -217,11 +217,8 @@ asmlinkage int sys_ipc(uint call, int fi return ret; return put_user(raddr, (ulong __user *)third); } - case 1: /* iBCS2 emulator entry point */ - if (!segment_eq(get_fs(), get_ds())) - return -EINVAL; - return do_shmat(first, (char __user *) ptr, - second, (ulong __user *) third); + case 1: /* Of course, we don't support iBCS2! */ + return -EINVAL; } case SHMDT: return sys_shmdt ((char __user *)ptr); @@ -257,7 +254,7 @@ asmlinkage int sys_clone(unsigned long c if (!newsp) newsp = regs->ARM_sp; - return do_fork(clone_flags & ~CLONE_IDLETASK, newsp, regs, 0, NULL, NULL); + return do_fork(clone_flags, newsp, regs, 0, NULL, NULL); } asmlinkage int sys_vfork(struct pt_regs *regs) --- linux-2.6.9-rc1/arch/arm/kernel/time.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/arch/arm/kernel/time.c 2004-09-02 20:51:51.000000000 -0700 @@ -79,31 +79,6 @@ unsigned long long __attribute__((weak)) return (unsigned long long)jiffies * (1000000000 / HZ); } -/* - * Handle kernel profile stuff... - */ -static inline void do_profile(struct pt_regs *regs) -{ - - profile_hook(regs); - - if (!user_mode(regs) && - prof_buffer && - current->pid) { - unsigned long pc = instruction_pointer(regs); - extern int _stext; - - pc -= (unsigned long)&_stext; - - pc >>= prof_shift; - - if (pc >= prof_len) - pc = prof_len - 1; - - prof_buffer[pc] += 1; - } -} - static unsigned long next_rtc_update; /* @@ -317,7 +292,7 @@ EXPORT_SYMBOL(do_settimeofday); void timer_tick(struct pt_regs *regs) { - do_profile(regs); + profile_tick(CPU_PROFILING, regs); do_leds(); do_set_rtc(); do_timer(regs); --- linux-2.6.9-rc1/arch/arm/kernel/vmlinux.lds.S 2004-08-24 00:02:26.000000000 -0700 +++ 25/arch/arm/kernel/vmlinux.lds.S 2004-09-02 20:51:51.000000000 -0700 @@ -38,9 +38,6 @@ SECTIONS __early_begin = .; *(__early_param) __early_end = .; - __start___param = .; - *(__param) - __stop___param = .; __initcall_start = .; *(.initcall1.init) *(.initcall2.init) --- linux-2.6.9-rc1/arch/arm/mach-s3c2410/clock.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/arm/mach-s3c2410/clock.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,4 +1,4 @@ -/* linux/arch/arm/mach-s3c2410/gpio.c +/* linux/arch/arm/mach-s3c2410/clock.c * * Copyright (c) 2004 Simtec Electronics * Ben Dooks @@ -99,7 +99,9 @@ void clk_put(struct clk *clk) int clk_enable(struct clk *clk) { - s3c2410_clk_enable(clk->ctrlbit, 1); + if (clk->ctrlbit != 0) + s3c2410_clk_enable(clk->ctrlbit, 1); + return 0; } @@ -240,6 +242,10 @@ static struct clk init_clocks[] = { { .name = "spi", .parent = &clk_p, .ctrlbit = S3C2410_CLKCON_SPI + }, + { .name = "watchdog", + .parent = &clk_p, + .ctrlbit = 0 } }; @@ -284,10 +290,10 @@ static int __init s3c2410_init_clocks(vo printk(KERN_ERR "failed to register cpu fclk\n"); if (s3c2410_register_clock(&clk_h) < 0) - printk(KERN_ERR "failed to register cpu fclk\n"); + printk(KERN_ERR "failed to register cpu hclk\n"); if (s3c2410_register_clock(&clk_p) < 0) - printk(KERN_ERR "failed to register cpu fclk\n"); + printk(KERN_ERR "failed to register cpu pclk\n"); for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { ret = s3c2410_register_clock(clkp); --- linux-2.6.9-rc1/arch/arm/mach-s3c2410/devs.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/arm/mach-s3c2410/devs.c 2004-09-02 20:51:51.000000000 -0700 @@ -10,6 +10,7 @@ * published by the Free Software Foundation. * * Modifications: + * 21-Aug-2004 BJD Added IRQ_TICK to RTC resources * 18-Aug-2004 BJD Created initial version */ @@ -229,8 +230,12 @@ static struct resource s3c_rtc_resource[ .start = IRQ_RTC, .end = IRQ_RTC, .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = IRQ_TICK, + .end = IRQ_TICK, + .flags = IORESOURCE_IRQ } - }; struct platform_device s3c_device_rtc = { --- linux-2.6.9-rc1/arch/arm/mach-s3c2410/mach-bast.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/arch/arm/mach-s3c2410/mach-bast.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,6 +1,6 @@ /* linux/arch/arm/mach-s3c2410/mach-bast.c * - * Copyright (c) 2003 Simtec Electronics + * Copyright (c) 2003,2004 Simtec Electronics * Ben Dooks * * http://www.simtec.co.uk/products/EB2410ITX/ @@ -10,6 +10,8 @@ * published by the Free Software Foundation. * * Modifications: + * 20-Aug-2004 BJD Added s3c2410_board struct + * 18-Aug-2004 BJD Added platform devices from default set * 16-May-2003 BJD Created initial version * 16-Aug-2003 BJD Fixed header files and copyright, added URL * 05-Sep-2003 BJD Moved to v2.6 kernel @@ -23,6 +25,7 @@ #include #include #include +#include #include #include @@ -39,6 +42,7 @@ #include #include "s3c2410.h" +#include "devs.h" /* macros for virtual address mods for the io space entries */ #define VA_C5(item) ((item) + BAST_VAM_CS5) @@ -154,6 +158,7 @@ static struct s3c2410_uartcfg bast_uartc [1] = { .hwport = 1, .flags = 0, + .clock = &bast_serial_clock, .ucon = UCON, .ulcon = ULCON, @@ -170,19 +175,30 @@ static struct s3c2410_uartcfg bast_uartc } }; +static struct platform_device *bast_devices[] __initdata = { + &s3c_device_usb, + &s3c_device_lcd, + &s3c_device_wdt, + &s3c_device_i2c, + &s3c_device_iis, +}; + +static struct s3c2410_board bast_board __initdata = { + .devices = bast_devices, + .devices_count = ARRAY_SIZE(bast_devices) +}; void __init bast_map_io(void) { s3c2410_map_io(bast_iodesc, ARRAY_SIZE(bast_iodesc)); s3c2410_uartcfgs = bast_uartcfgs; + + s3c2410_set_board(&bast_board); } void __init bast_init_irq(void) { - //llprintk("bast_init_irq:\n"); - s3c2410_init_irq(); - } void __init bast_init_time(void) --- linux-2.6.9-rc1/arch/arm/mach-s3c2410/mach-h1940.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/arm/mach-s3c2410/mach-h1940.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,6 +1,6 @@ -/* linux/arch/arm/mach-s3c2410/mach-ipaq.c +/* linux/arch/arm/mach-s3c2410/mach-h1940.c * - * Copyright (c) 2003 Simtec Electronics + * Copyright (c) 2003,2004 Simtec Electronics * Ben Dooks * * http://www.handhelds.org/projects/h1940.html @@ -16,6 +16,7 @@ * 06-Jan-2003 BJD Updates for * 18-Jan-2003 BJD Added serial port configuration * 17-Feb-2003 BJD Copied to mach-ipaq.c + * 21-Aug-2004 BJD Added struct s3c2410_board */ #include @@ -39,6 +40,7 @@ #include #include "s3c2410.h" +#include "devs.h" static struct map_desc ipaq_iodesc[] __initdata = { /* nothing here yet */ @@ -77,10 +79,27 @@ static struct s3c2410_uartcfg ipaq_uartc }; + + +static struct platform_device *h1940_devices[] __initdata = { + &s3c_device_usb, + &s3c_device_lcd, + &s3c_device_wdt, + &s3c_device_i2c, + &s3c_device_iis, +}; + +static struct s3c2410_board h1940_board __initdata = { + .devices = h1940_devices, + .devices_count = ARRAY_SIZE(h1940_devices) +}; + void __init ipaq_map_io(void) { s3c2410_map_io(ipaq_iodesc, ARRAY_SIZE(ipaq_iodesc)); s3c2410_uartcfgs = ipaq_uartcfgs; + + s3c2410_set_board(&h1940_board); } void __init ipaq_init_irq(void) --- linux-2.6.9-rc1/arch/arm/mach-s3c2410/mach-smdk2410.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/arm/mach-s3c2410/mach-smdk2410.c 2004-09-02 20:51:51.000000000 -0700 @@ -47,7 +47,7 @@ #include #include "s3c2410.h" - +#include "devs.h" static struct map_desc smdk2410_iodesc[] __initdata = { /* nothing here yet */ @@ -87,11 +87,25 @@ static struct s3c2410_uartcfg smdk2410_u } }; +static struct platform_device *smdk2410_devices[] __initdata = { + &s3c_device_usb, + &s3c_device_lcd, + &s3c_device_wdt, + &s3c_device_i2c, + &s3c_device_iis, +}; + +static struct s3c2410_board smdk2410_board __initdata = { + .devices = smdk2410_devices, + .devices_count = ARRAY_SIZE(smdk2410_devices) +}; void __init smdk2410_map_io(void) { s3c2410_map_io(smdk2410_iodesc, ARRAY_SIZE(smdk2410_iodesc)); s3c2410_uartcfgs = smdk2410_uartcfgs; + + s3c2410_set_board(&smdk2410_board); } void __init smdk2410_init_irq(void) --- linux-2.6.9-rc1/arch/arm/mach-s3c2410/mach-vr1000.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/arm/mach-s3c2410/mach-vr1000.c 2004-09-02 20:51:51.000000000 -0700 @@ -11,13 +11,8 @@ * published by the Free Software Foundation. * * Modifications: + * 21-Aug-2004 BJD Added struct s3c2410_board * 06-Aug-2004 BJD Fixed call to time initialisation - * 12-Jul-2004 BJD Renamed machine - * 16-May-2003 BJD Created initial version - * 16-Aug-2003 BJD Fixed header files and copyright, added URL - * 05-Sep-2003 BJD Moved to v2.6 kernel - * 06-Jan-2003 BJD Updates for - * 18-Jan-2003 BJD Added serial port configuration * 05-Apr-2004 BJD Copied to make mach-vr1000.c */ @@ -44,6 +39,7 @@ #include #include "s3c2410.h" +#include "devs.h" /* macros for virtual address mods for the io space entries */ #define VA_C5(item) ((item) + BAST_VAM_CS5) @@ -143,11 +139,26 @@ static struct s3c2410_uartcfg vr1000_uar } }; +static struct platform_device *vr1000_devices[] __initdata = { + &s3c_device_usb, + &s3c_device_lcd, + &s3c_device_wdt, + &s3c_device_i2c, + &s3c_device_iis, +}; + +static struct s3c2410_board vr1000_board __initdata = { + .devices = vr1000_devices, + .devices_count = ARRAY_SIZE(vr1000_devices) +}; + void __init vr1000_map_io(void) { s3c2410_map_io(vr1000_iodesc, ARRAY_SIZE(vr1000_iodesc)); s3c2410_uartcfgs = vr1000_uartcfgs; + + s3c2410_set_board(&vr1000_board); } void __init vr1000_init_irq(void) --- linux-2.6.9-rc1/arch/arm/mach-s3c2410/s3c2410.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/arm/mach-s3c2410/s3c2410.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,6 +1,6 @@ /* linux/arch/arm/mach-s3c2410/s3c2410.c * - * Copyright (c) 2003 Simtec Electronics + * Copyright (c) 2003,2004 Simtec Electronics * Ben Dooks * * http://www.simtec.co.uk/products/EB2410ITX/ @@ -13,7 +13,8 @@ * 16-May-2003 BJD Created initial version * 16-Aug-2003 BJD Fixed header files and copyright, added URL * 05-Sep-2003 BJD Moved to kernel v2.6 - * 18-Jan-2003 BJD Added serial port configuration + * 18-Jan-2004 BJD Added serial port configuration + * 21-Aug-2004 BJD Added new struct s3c2410_board handler */ #include @@ -35,6 +36,8 @@ #include #include +#include "s3c2410.h" + int s3c2410_clock_tick_rate = 12*1000*1000; /* current timers at 12MHz */ /* serial port setup */ @@ -56,24 +59,15 @@ unsigned long s3c2410_pclk; #define IODESC_ENT(x) { S3C2410_VA_##x, S3C2410_PA_##x, S3C2410_SZ_##x, MT_DEVICE } static struct map_desc s3c2410_iodesc[] __initdata = { - IODESC_ENT(IRQ), - IODESC_ENT(MEMCTRL), - IODESC_ENT(USBHOST), - IODESC_ENT(DMA), - IODESC_ENT(CLKPWR), - IODESC_ENT(LCD), - IODESC_ENT(NAND), - IODESC_ENT(UART), - IODESC_ENT(TIMER), - IODESC_ENT(USBDEV), - IODESC_ENT(WATCHDOG), - IODESC_ENT(IIC), - IODESC_ENT(IIS), - IODESC_ENT(GPIO), - IODESC_ENT(RTC), - IODESC_ENT(ADC), - IODESC_ENT(SPI), - IODESC_ENT(SDI) + IODESC_ENT(IRQ), + IODESC_ENT(MEMCTRL), + IODESC_ENT(USBHOST), + IODESC_ENT(CLKPWR), + IODESC_ENT(LCD), + IODESC_ENT(UART), + IODESC_ENT(TIMER), + IODESC_ENT(GPIO), + IODESC_ENT(ADC), }; static struct resource s3c_uart0_resource[] = { @@ -175,6 +169,12 @@ void __init s3c2410_map_io(struct map_de print_mhz(s3c2410_pclk)); } +static struct s3c2410_board *board; + +void s3c2410_set_board(struct s3c2410_board *b) +{ + board = b; +} static int __init s3c2410_init(void) { @@ -183,6 +183,22 @@ static int __init s3c2410_init(void) printk("S3C2410: Initialising architecture\n"); ret = platform_add_devices(uart_devices, ARRAY_SIZE(uart_devices)); + if (ret) + return ret; + + if (board != NULL) { + if (board->devices != NULL) { + ret = platform_add_devices(board->devices, + board->devices_count); + + if (ret) { + printk(KERN_ERR "s3c2410: failed to add board devices (%d)\n", ret); + } + } + + /* not adding board devices may not be fatal */ + ret = 0; + } return ret; } --- linux-2.6.9-rc1/arch/arm/mach-s3c2410/s3c2410.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/arm/mach-s3c2410/s3c2410.h 2004-09-02 20:51:51.000000000 -0700 @@ -11,7 +11,7 @@ * * Modifications: * 18-Aug-2004 BJD Created initial version - * + * 20-Aug-2004 BJD Added s3c2410_board struct */ extern void s3c2410_map_io(struct map_desc *, int count); @@ -20,3 +20,16 @@ extern void s3c2410_init_irq(void); extern void s3c2410_init_time(void); +/* the board structure is used at first initialsation time + * to get info such as the devices to register for this + * board. This is done because platfrom_add_devices() cannot + * be called from the map_io entry. + * +*/ + +struct s3c2410_board { + struct platform_device **devices; + unsigned int devices_count; +}; + +extern void s3c2410_set_board(struct s3c2410_board *board); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/arm/mach-s3c2410/s3c2440-dsc.c 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,57 @@ +/* linux/arch/arm/mach-s3c2410/s3c2440-dsc.c + * + * Copyright (c) 2004 Simtec Electronics + * Ben Dooks + * + * Samsung S3C2440 Drive Strength Control support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Modifications: + * 29-Aug-2004 BJD Start of drive-strength control +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "s3c2440.h" +#include "cpu.h" + +int s3c2440_set_dsc(unsigned int pin, unsigned int value) +{ + unsigned long base; + unsigned long val; + unsigned long flags; + unsigned long mask; + + base = (pin & S3C2440_SELECT_DSC1) ? S3C2440_DSC1 : S3C2440_DSC0; + mask = 3 << S3C2440_DSC_GETSHIFT(pin); + + local_irq_save(flags); + + val = __raw_readl(base); + val &= ~mask; + val |= value & mask; + __raw_writel(val, base); + + local_irq_restore(flags); + return 0; +} --- linux-2.6.9-rc1/arch/arm/mach-s3c2410/time.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/arch/arm/mach-s3c2410/time.c 2004-09-02 20:51:51.000000000 -0700 @@ -31,14 +31,12 @@ #include #include #include +#include #include static unsigned long timer_startval; static unsigned long timer_ticks_usec; -#ifdef CONFIG_S3C2410_RTC -extern void s3c2410_rtc_check(); -#endif /* with an 12MHz clock, we get 12 ticks per-usec */ @@ -49,15 +47,30 @@ extern void s3c2410_rtc_check(); * will have been disabled by do_gettimeoffset() * IRQs are disabled before entering here from do_gettimeofday() */ + +#define SRCPND_TIMER4 (1<<(IRQ_TIMER4 - IRQ_EINT0)) + static unsigned long s3c2410_gettimeoffset (void) { unsigned long tdone; unsigned long usec; + unsigned long irqpend; /* work out how many ticks have gone since last timer interrupt */ tdone = timer_startval - __raw_readl(S3C2410_TCNTO(4)); + /* check to see if there is an interrupt pending */ + + irqpend = __raw_readl(S3C2410_SRCPND); + if (irqpend & SRCPND_TIMER4) { + /* re-read the timer, and try and fix up for the missed + * interrupt */ + + tdone = timer_startval - __raw_readl(S3C2410_TCNTO(4)); + tdone += 1<<16; + } + /* currently, tcnt is in 12MHz units, but this may change * for non-bast machines... */ --- linux-2.6.9-rc1/arch/arm/mach-sa1100/cpu-sa1100.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/arm/mach-sa1100/cpu-sa1100.c 2004-09-03 00:56:30.564514136 -0700 @@ -230,9 +230,7 @@ static int __init sa1100_cpu_init(struct } static struct cpufreq_driver sa1100_driver = { - .flags = CPUFREQ_STICKY | - CPUFREQ_PANIC_OUTOFSYNC | - CPUFREQ_PANIC_RESUME_OUTOFSYNC, + .flags = CPUFREQ_STICKY, .verify = sa11x0_verify_speed, .target = sa1100_target, .get = sa11x0_getspeed, --- linux-2.6.9-rc1/arch/arm/mach-sa1100/cpu-sa1110.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/arch/arm/mach-sa1100/cpu-sa1110.c 2004-09-03 00:56:30.565513984 -0700 @@ -329,9 +329,7 @@ static int __init sa1110_cpu_init(struct } static struct cpufreq_driver sa1110_driver = { - .flags = CPUFREQ_STICKY | - CPUFREQ_PANIC_OUTOFSYNC | - CPUFREQ_PANIC_RESUME_OUTOFSYNC, + .flags = CPUFREQ_STICKY, .verify = sa11x0_verify_speed, .target = sa1110_target, .get = sa11x0_getspeed, --- linux-2.6.9-rc1/arch/arm/mm/init.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/arm/mm/init.c 2004-09-02 20:51:51.000000000 -0700 @@ -495,7 +495,7 @@ void __init paging_init(struct meminfo * */ arch_adjust_zones(node, zone_size, zhole_size); - free_area_init_node(node, pgdat, NULL, zone_size, + free_area_init_node(node, pgdat, zone_size, bdata->node_boot_start >> PAGE_SHIFT, zhole_size); } --- linux-2.6.9-rc1/arch/arm/nwfpe/fpa11_cpdt.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/arm/nwfpe/fpa11_cpdt.c 2004-09-02 20:51:51.000000000 -0700 @@ -42,8 +42,13 @@ static inline void loadDouble(const unsi unsigned int *p; p = (unsigned int *) &fpa11->fpreg[Fn].fDouble; fpa11->fType[Fn] = typeDouble; +#ifdef __ARMEB__ + get_user(p[0], &pMem[0]); /* sign & exponent */ + get_user(p[1], &pMem[1]); +#else get_user(p[0], &pMem[1]); get_user(p[1], &pMem[0]); /* sign & exponent */ +#endif } #ifdef CONFIG_FPE_NWFPE_XP @@ -140,8 +145,13 @@ static inline void storeDouble(const uns val.f = fpa11->fpreg[Fn].fDouble; } +#ifdef __ARMEB__ + put_user(val.i[0], &pMem[0]); /* msw */ + put_user(val.i[1], &pMem[1]); /* lsw */ +#else put_user(val.i[1], &pMem[0]); /* msw */ put_user(val.i[0], &pMem[1]); /* lsw */ +#endif } #ifdef CONFIG_FPE_NWFPE_XP --- linux-2.6.9-rc1/arch/arm/oprofile/op_model_xscale.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/arch/arm/oprofile/op_model_xscale.c 2004-09-02 20:51:51.000000000 -0700 @@ -30,6 +30,7 @@ #define PMN_RESET 0x002 /* Reset event counters */ #define CCNT_RESET 0x004 /* Reset clock counter */ #define PMU_RESET (CCNT_RESET | PMN_RESET) +#define PMU_CNT64 0x008 /* Make CCNT count every 64th cycle */ /* TODO do runtime detection */ #ifdef CONFIG_ARCH_IOP310 @@ -125,12 +126,15 @@ static struct pmu_type *pmu; static void write_pmnc(u32 val) { - /* upper 4bits and 7, 11 are write-as-0 */ - val &= 0xffff77f; - if (pmu->id == PMU_XSC1) + if (pmu->id == PMU_XSC1) { + /* upper 4bits and 7, 11 are write-as-0 */ + val &= 0xffff77f; __asm__ __volatile__ ("mcr p14, 0, %0, c0, c0, 0" : : "r" (val)); - else + } else { + /* bits 4-23 are write-as-0, 24-31 are write ignored */ + val &= 0xf; __asm__ __volatile__ ("mcr p14, 0, %0, c0, c1, 0" : : "r" (val)); + } } static u32 read_pmnc(void) @@ -139,8 +143,11 @@ static u32 read_pmnc(void) if (pmu->id == PMU_XSC1) __asm__ __volatile__ ("mrc p14, 0, %0, c0, c0, 0" : "=r" (val)); - else + else { __asm__ __volatile__ ("mrc p14, 0, %0, c0, c1, 0" : "=r" (val)); + /* bits 1-2 and 4-23 are read-unpredictable */ + val &= 0xff000009; + } return val; } @@ -386,8 +393,10 @@ static int xscale_pmu_start(void) if (pmu->id == PMU_XSC1) pmnc |= pmu->int_enable; - else + else { __asm__ __volatile__ ("mcr p14, 0, %0, c4, c1, 0" : : "r" (pmu->int_enable)); + pmnc &= ~PMU_CNT64; + } pmnc |= PMU_ENABLE; write_pmnc(pmnc); --- linux-2.6.9-rc1/arch/cris/arch-v10/drivers/ethernet.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/cris/arch-v10/drivers/ethernet.c 2004-09-03 00:56:32.518217128 -0700 @@ -401,7 +401,6 @@ static irqreturn_t e100nw_interrupt(int static void e100_rx(struct net_device *dev); static int e100_close(struct net_device *dev); static int e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); -static int e100_ethtool_ioctl(struct net_device* dev, struct ifreq *ifr); static int e100_set_config(struct net_device* dev, struct ifmap* map); static void e100_tx_timeout(struct net_device *dev); static struct net_device_stats *e100_get_stats(struct net_device *dev); @@ -410,6 +409,7 @@ static void e100_hardware_send_packet(ch static void update_rx_stats(struct net_device_stats *); static void update_tx_stats(struct net_device_stats *); static int e100_probe_transceiver(void); +static struct ethtool_ops ethtool_ops; static void e100_check_speed(unsigned long dummy); static void e100_set_speed(unsigned long speed); @@ -483,6 +483,7 @@ etrax_ethernet_init(void) dev->do_ioctl = e100_ioctl; dev->set_config = e100_set_config; dev->tx_timeout = e100_tx_timeout; + SET_ETHTOOL_OPS(dev, ðtool_ops); /* Initialise the list of Etrax DMA-descriptors */ @@ -1401,8 +1402,6 @@ e100_ioctl(struct net_device *dev, struc spin_lock(&np->lock); /* Preempt protection */ switch (cmd) { - case SIOCETHTOOL: - return e100_ethtool_ioctl(dev,ifr); case SIOCGMIIPHY: /* Get PHY address */ data->phy_id = mdio_phy_addr; break; @@ -1439,88 +1438,71 @@ e100_ioctl(struct net_device *dev, struc return 0; } -static int -e100_ethtool_ioctl(struct net_device *dev, struct ifreq *ifr) +static int e100_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) { - struct ethtool_cmd ecmd; - - if (copy_from_user(&ecmd, ifr->ifr_data, sizeof (ecmd))) - return -EFAULT; + ecmd->supported = + SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII | + SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full; + ecmd->port = PORT_TP; + ecmd->transceiver = XCVR_EXTERNAL; + ecmd->phy_address = mdio_phy_addr; + ecmd->speed = current_speed; + ecmd->duplex = full_duplex ? DUPLEX_FULL : DUPLEX_HALF; + ecmd->advertising = ADVERTISED_TP; + if (current_duplex == autoneg && current_speed_selection == 0) + ecmd->advertising |= ADVERTISED_Autoneg; + else { + ecmd->advertising |= + ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full; + if (current_speed_selection == 10) + ecmd->advertising &= ~(ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full); + else if (current_speed_selection == 100) + ecmd->advertising &= ~(ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full); + if (current_duplex == half) + ecmd->advertising &= ~(ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Full); + else if (current_duplex == full) + ecmd->advertising &= ~(ADVERTISED_10baseT_Half | ADVERTISED_100baseT_Half); + } + ecmd->autoneg = AUTONEG_ENABLE; + return 0; +} - switch (ecmd.cmd) { - case ETHTOOL_GSET: - { - memset((void *) &ecmd, 0, sizeof (ecmd)); - ecmd.supported = - SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII | - SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full; - ecmd.port = PORT_TP; - ecmd.transceiver = XCVR_EXTERNAL; - ecmd.phy_address = mdio_phy_addr; - ecmd.speed = current_speed; - ecmd.duplex = full_duplex ? DUPLEX_FULL : DUPLEX_HALF; - ecmd.advertising = ADVERTISED_TP; - if (current_duplex == autoneg && current_speed_selection == 0) - ecmd.advertising |= ADVERTISED_Autoneg; - else { - ecmd.advertising |= - ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | - ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full; - if (current_speed_selection == 10) - ecmd.advertising &= ~(ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full); - else if (current_speed_selection == 100) - ecmd.advertising &= ~(ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full); - if (current_duplex == half) - ecmd.advertising &= ~(ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Full); - else if (current_duplex == full) - ecmd.advertising &= ~(ADVERTISED_10baseT_Half | ADVERTISED_100baseT_Half); - } - ecmd.autoneg = AUTONEG_ENABLE; - if (copy_to_user(ifr->ifr_data, &ecmd, sizeof (ecmd))) - return -EFAULT; - } - break; - case ETHTOOL_SSET: - { - if (!capable(CAP_NET_ADMIN)) { - return -EPERM; - } - if (ecmd.autoneg == AUTONEG_ENABLE) { - e100_set_duplex(autoneg); - e100_set_speed(0); - } else { - e100_set_duplex(ecmd.duplex == DUPLEX_HALF ? half : full); - e100_set_speed(ecmd.speed == SPEED_10 ? 10: 100); - } - } - break; - case ETHTOOL_GDRVINFO: - { - struct ethtool_drvinfo info; - memset((void *) &info, 0, sizeof (info)); - strncpy(info.driver, "ETRAX 100LX", sizeof(info.driver) - 1); - strncpy(info.version, "$Revision: 1.22 $", sizeof(info.version) - 1); - strncpy(info.fw_version, "N/A", sizeof(info.fw_version) - 1); - strncpy(info.bus_info, "N/A", sizeof(info.bus_info) - 1); - info.regdump_len = 0; - info.eedump_len = 0; - info.testinfo_len = 0; - if (copy_to_user(ifr->ifr_data, &info, sizeof (info))) - return -EFAULT; - } - break; - case ETHTOOL_NWAY_RST: - if (current_duplex == autoneg && current_speed_selection == 0) - e100_negotiate(); - break; - default: - return -EOPNOTSUPP; - break; +static int e100_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + if (ecmd->autoneg == AUTONEG_ENABLE) { + e100_set_duplex(autoneg); + e100_set_speed(0); + } else { + e100_set_duplex(ecmd->duplex == DUPLEX_HALF ? half : full); + e100_set_speed(ecmd->speed == SPEED_10 ? 10: 100); } return 0; } +static void e100_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + strncpy(info->driver, "ETRAX 100LX", sizeof(info->driver) - 1); + strncpy(info->version, "$Revision: 1.22 $", sizeof(info->version) - 1); + strncpy(info->fw_version, "N/A", sizeof(info->fw_version) - 1); + strncpy(info->bus_info, "N/A", sizeof(info->bus_info) - 1); +} + +static int e100_nway_reset(struct net_device *dev) +{ + if (current_duplex == autoneg && current_speed_selection == 0) + e100_negotiate(); + return 0; +} + +static struct ethtool_ops ethtool_ops = { + .get_settings = e100_get_settings, + .set_settings = e100_set_settings, + .get_drvinfo = e100_get_drvinfo, + .nway_reset = e100_nway_reset, +}; + static int e100_set_config(struct net_device *dev, struct ifmap *map) { --- linux-2.6.9-rc1/arch/cris/arch-v10/kernel/debugport.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/arch/cris/arch-v10/kernel/debugport.c 2004-09-02 20:51:51.000000000 -0700 @@ -259,7 +259,7 @@ static struct console sercons = { void __init init_etrax_debug(void) { -#if CONFIG_ETRAX_DEBUG_PORT_NULL +#ifdef CONFIG_ETRAX_DEBUG_PORT_NULL return; #endif --- linux-2.6.9-rc1/arch/cris/arch-v10/kernel/process.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/cris/arch-v10/kernel/process.c 2004-09-02 20:51:51.000000000 -0700 @@ -180,7 +180,7 @@ asmlinkage int sys_clone(unsigned long n { if (!newusp) newusp = rdusp(); - return do_fork(flags & ~CLONE_IDLETASK, newusp, regs, 0, parent_tid, child_tid); + return do_fork(flags, newusp, regs, 0, parent_tid, child_tid); } /* vfork is a system call in i386 because of register-pressure - maybe --- linux-2.6.9-rc1/arch/cris/arch-v10/kernel/ptrace.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/cris/arch-v10/kernel/ptrace.c 2004-09-03 00:56:56.197617312 -0700 @@ -85,17 +85,8 @@ sys_ptrace(long request, long pid, long goto out_tsk; } - ret = -ESRCH; - - if (!(child->ptrace & PT_PTRACED)) - goto out_tsk; - - if (child->state != TASK_STOPPED) { - if (request != PTRACE_KILL) - goto out_tsk; - } - - if (child->parent != current) + ret = ptrace_check_attach(child, request == PTRACE_KILL); + if (ret < 0) goto out_tsk; switch (request) { --- linux-2.6.9-rc1/arch/cris/arch-v10/kernel/signal.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/arch/cris/arch-v10/kernel/signal.c 2004-09-02 20:51:51.000000000 -0700 @@ -409,9 +409,7 @@ static void setup_frame(int sig, struct return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, @@ -475,9 +473,7 @@ static void setup_rt_frame(int sig, stru return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } /* --- linux-2.6.9-rc1/arch/cris/arch-v10/mm/init.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/cris/arch-v10/mm/init.c 2004-09-02 20:51:51.000000000 -0700 @@ -183,7 +183,7 @@ paging_init(void) * mem_map page array. */ - free_area_init_node(0, &contig_page_data, 0, zones_size, PAGE_OFFSET >> PAGE_SHIFT, 0); + free_area_init_node(0, &contig_page_data, zones_size, PAGE_OFFSET >> PAGE_SHIFT, 0); mem_map = contig_page_data.node_mem_map; } --- linux-2.6.9-rc1/arch/cris/arch-v10/vmlinux.lds.S 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/cris/arch-v10/vmlinux.lds.S 2004-09-02 20:51:51.000000000 -0700 @@ -64,9 +64,6 @@ SECTIONS __setup_start = .; .init.setup : { *(.init.setup) } __setup_end = .; - __start___param = .; - __param : { *(__param) } - __stop___param = .; .initcall.init : { __initcall_start = .; *(.initcall1.init); --- linux-2.6.9-rc1/arch/cris/kernel/crisksyms.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/cris/kernel/crisksyms.c 2004-09-03 00:56:40.978930904 -0700 @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include --- linux-2.6.9-rc1/arch/cris/kernel/irq.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/cris/kernel/irq.c 2004-09-02 20:51:51.000000000 -0700 @@ -125,7 +125,7 @@ asmlinkage void do_IRQ(int irq, struct p { struct irqaction *action; int do_random, cpu; - int retval = 0; + int ret, retval = 0; cpu = smp_processor_id(); irq_enter(); @@ -137,8 +137,10 @@ asmlinkage void do_IRQ(int irq, struct p local_irq_enable(); do_random = 0; do { - do_random |= action->flags; - retval |= action->handler(irq, action->dev_id, regs); + ret = action->handler(irq, action->dev_id, regs); + if (ret == IRQ_HANDLED) + do_random |= action->flags; + retval |= ret; action = action->next; } while (action); --- linux-2.6.9-rc1/arch/h8300/kernel/asm-offsets.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/arch/h8300/kernel/asm-offsets.c 2004-09-03 00:56:40.978930904 -0700 @@ -12,9 +12,9 @@ #include #include #include +#include #include #include -#include #include #define DEFINE(sym, val) \ --- linux-2.6.9-rc1/arch/h8300/kernel/h8300_ksyms.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/h8300/kernel/h8300_ksyms.c 2004-09-03 00:56:40.979930752 -0700 @@ -15,7 +15,6 @@ #include #include #include -#include #include #include --- linux-2.6.9-rc1/arch/h8300/kernel/ints.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/h8300/kernel/ints.c 2004-09-03 00:56:40.979930752 -0700 @@ -22,13 +22,13 @@ #include #include #include +#include #include #include #include #include #include -#include #include /* --- linux-2.6.9-rc1/arch/h8300/kernel/process.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/arch/h8300/kernel/process.c 2004-09-02 20:51:51.000000000 -0700 @@ -189,7 +189,7 @@ asmlinkage int h8300_clone(struct pt_reg newsp = regs->er2; if (!newsp) newsp = rdusp(); - return do_fork(clone_flags & ~CLONE_IDLETASK, newsp, regs, 0, NULL, NULL); + return do_fork(clone_flags, newsp, regs, 0, NULL, NULL); } --- linux-2.6.9-rc1/arch/h8300/kernel/ptrace.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/h8300/kernel/ptrace.c 2004-09-03 00:56:56.197617312 -0700 @@ -89,13 +89,6 @@ asmlinkage int sys_ptrace(long request, ret = ptrace_attach(child); goto out_tsk; } - ret = -ESRCH; - if (!(child->ptrace & PT_PTRACED)) - goto out_tsk; - if (child->state != TASK_STOPPED) { - if (request != PTRACE_KILL) - goto out_tsk; - } ret = ptrace_check_attach(child, request == PTRACE_KILL); if (ret < 0) goto out_tsk; @@ -270,10 +263,8 @@ asmlinkage void syscall_trace(void) return; if (!(current->ptrace & PT_PTRACED)) return; - current->exit_code = SIGTRAP; - current->state = TASK_STOPPED; - notify_parent(current, SIGCHLD); - schedule(); + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); /* * this isn't the same as continuing with a signal, but it will do * for normal use. strace only continues with a signal if the --- linux-2.6.9-rc1/arch/h8300/kernel/signal.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/h8300/kernel/signal.c 2004-09-02 20:51:51.000000000 -0700 @@ -391,9 +391,7 @@ static void setup_frame (int sig, struct return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } static void setup_rt_frame (int sig, struct k_sigaction *ka, siginfo_t *info, @@ -443,9 +441,7 @@ static void setup_rt_frame (int sig, str return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } static inline void --- linux-2.6.9-rc1/arch/h8300/kernel/time.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/h8300/kernel/time.c 2004-09-02 20:51:51.000000000 -0700 @@ -36,24 +36,6 @@ u64 jiffies_64; EXPORT_SYMBOL(jiffies_64); -static inline void do_profile (unsigned long pc) -{ - if (prof_buffer && current->pid) { - extern int _stext; - pc -= (unsigned long) &_stext; - pc >>= prof_shift; - if (pc < prof_len) - ++prof_buffer[pc]; - else - /* - * Don't ignore out-of-bounds PC values silently, - * put them into the last histogram slot, so if - * present, they will show up as a sharp peak. - */ - ++prof_buffer[prof_len-1]; - } -} - /* * timer_interrupt() needs to keep up the real-time clock, * as well as call the "do_timer()" routine every clocktick @@ -64,10 +46,7 @@ static void timer_interrupt(int irq, voi platform_timer_eoi(); do_timer(regs); - - if (!user_mode(regs)) - do_profile(regs->pc); - + profile_tick(CPU_PROFILING, regs); } void time_init(void) --- linux-2.6.9-rc1/arch/h8300/kernel/vmlinux.lds.S 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/h8300/kernel/vmlinux.lds.S 2004-09-02 20:51:51.000000000 -0700 @@ -150,9 +150,6 @@ SECTIONS *(.init.setup) . = ALIGN(0x4) ; ___setup_end = .; - ___start___param = .; - *(__param) - ___stop___param = .; ___initcall_start = .; *(.initcall1.init) *(.initcall2.init) --- linux-2.6.9-rc1/arch/h8300/platform/h8s/ints.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/arch/h8300/platform/h8s/ints.c 2004-09-03 00:56:40.980930600 -0700 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -29,7 +30,6 @@ #include #include #include -#include #include #include --- linux-2.6.9-rc1/arch/i386/Kconfig 2004-08-24 00:01:55.000000000 -0700 +++ 25/arch/i386/Kconfig 2004-09-03 00:56:59.177164352 -0700 @@ -864,6 +864,25 @@ config REGPARM generate incorrect output with certain kernel constructs when -mregparm=3 is used. +source "drivers/perfctr/Kconfig" + +config KEXEC + bool "kexec system call (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + kexec is a system call that implements the ability to shutdown your + current kernel, and to start another kernel. It is like a reboot + but it is indepedent of the system firmware. And like a reboot + you can start any kernel with it, not just Linux. + + The name comes from the similiarity to the exec system call. + + It is an ongoing process to be certain the hardware in a machine + is properly shutdown, so do not be surprised if this code does not + initially work for you. It may help to enable device hotplugging + support. As of this writing the exact hardware interface is + strongly in flux, so no good recommendation can be made. + endmenu @@ -1164,6 +1183,15 @@ config SCx200 This support is also available as a module. If compiled as a module, it will be called scx200. +config HOTPLUG_CPU + bool "Support for hot-pluggable CPUs (EXPERIMENTAL)" + depends on SMP && HOTPLUG && EXPERIMENTAL + ---help--- + Say Y here to experiment with turning CPUs off and on. CPUs + can be controlled through /sys/devices/system/cpu. + + Say N. + source "drivers/pcmcia/Kconfig" source "drivers/pci/hotplug/Kconfig" @@ -1174,6 +1202,14 @@ menu "Executable file formats" source "fs/Kconfig.binfmt" +config TRAP_BAD_SYSCALL_EXITS + bool "Debug bad system call exits" + depends on KGDB + help + If you say Y here the kernel will check for system calls which + return without clearing preempt. + default n + endmenu source "drivers/Kconfig" --- linux-2.6.9-rc1/arch/i386/Kconfig.debug 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/i386/Kconfig.debug 2004-09-03 00:56:42.842647576 -0700 @@ -19,6 +19,16 @@ config DEBUG_STACKOVERFLOW bool "Check for stack overflows" depends on DEBUG_KERNEL +config KPROBES + bool "Kprobes" + depends on DEBUG_KERNEL + help + Kprobes allows you to trap at almost any kernel address and + execute a callback function. register_kprobe() establishes + a probepoint and specifies the callback. Kprobes is useful + for kernel debugging, non-intrusive instrumentation and testing. + If in doubt, say "N". + config DEBUG_STACK_USAGE bool "Stack utilization instrumentation" depends on DEBUG_KERNEL @@ -45,6 +55,25 @@ config 4KSTACKS on the VM subsystem for higher order allocations. This option will also use IRQ stacks to compensate for the reduced stackspace. +config SCHEDSTATS + bool "Collect scheduler statistics" + depends on DEBUG_KERNEL && PROC_FS + help + If you say Y here, additional code will be inserted into the + scheduler and related routines to collect statistics about + scheduler behavior and provide them in /proc/schedstat. These + stats may be useful for both tuning and debugging the scheduler + If you aren't debugging the scheduler or trying to tune a specific + application, you can say N to avoid the very slight overhead + this adds. + +config LOCKMETER + bool "Kernel lock metering" + depends on SMP + help + Say Y to enable kernel lock metering, which adds overhead to SMP locks, + but allows you to see various statistics using the lockstat command. + config X86_FIND_SMP_CONFIG bool depends on X86_LOCAL_APIC || X86_VOYAGER @@ -55,4 +84,6 @@ config X86_MPPARSE depends on X86_LOCAL_APIC && !X86_VISWS default y +source "arch/i386/Kconfig.kgdb" + endmenu --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/i386/Kconfig.kgdb 2004-09-03 00:56:36.374630864 -0700 @@ -0,0 +1,175 @@ +config KGDB + bool "Include kgdb kernel debugger" + depends on DEBUG_KERNEL && !KPROBES + help + If you say Y here, the system will be compiled with the debug + option (-g) and a debugging stub will be included in the + kernel. This stub communicates with gdb on another (host) + computer via a serial port. The host computer should have + access to the kernel binary file (vmlinux) and a serial port + that is connected to the target machine. Gdb can be made to + configure the serial port or you can use stty and setserial to + do this. See the 'target' command in gdb. This option also + configures in the ability to request a breakpoint early in the + boot process. To request the breakpoint just include 'kgdb' + as a boot option when booting the target machine. The system + will then break as soon as it looks at the boot options. This + option also installs a breakpoint in panic and sends any + kernel faults to the debugger. For more information see the + Documentation/i386/kgdb/kgdb.txt file. + +choice + depends on KGDB + prompt "Debug serial port BAUD" + default KGDB_115200BAUD + help + Gdb and the kernel stub need to agree on the baud rate to be + used. Some systems (x86 family at this writing) allow this to + be configured. + +config KGDB_9600BAUD + bool "9600" + +config KGDB_19200BAUD + bool "19200" + +config KGDB_38400BAUD + bool "38400" + +config KGDB_57600BAUD + bool "57600" + +config KGDB_115200BAUD + bool "115200" +endchoice + +config KGDB_PORT + hex "hex I/O port address of the debug serial port" + depends on KGDB + default 3f8 + help + Some systems (x86 family at this writing) allow the port + address to be configured. The number entered is assumed to be + hex, don't put 0x in front of it. The standard address are: + COM1 3f8 , irq 4 and COM2 2f8 irq 3. Setserial /dev/ttySx + will tell you what you have. It is good to test the serial + connection with a live system before trying to debug. + +config KGDB_IRQ + int "IRQ of the debug serial port" + depends on KGDB + default 4 + help + This is the irq for the debug port. If everything is working + correctly and the kernel has interrupts on a control C to the + port should cause a break into the kernel debug stub. + +config DEBUG_INFO + bool + depends on KGDB + default y + +config KGDB_MORE + bool "Add any additional compile options" + depends on KGDB + default n + help + Saying yes here turns on the ability to enter additional + compile options. + + +config KGDB_OPTIONS + depends on KGDB_MORE + string "Additional compile arguments" + default "-O1" + help + This option allows you enter additional compile options for + the whole kernel compile. Each platform will have a default + that seems right for it. For example on PPC "-ggdb -O1", and + for i386 "-O1". Note that by configuring KGDB "-g" is already + turned on. In addition, on i386 platforms + "-fomit-frame-pointer" is deleted from the standard compile + options. + +config NO_KGDB_CPUS + int "Number of CPUs" + depends on KGDB && SMP + default NR_CPUS + help + + This option sets the number of cpus for kgdb ONLY. It is used + to prune some internal structures so they look "nice" when + displayed with gdb. This is to overcome possibly larger + numbers that may have been entered above. Enter the real + number to get nice clean kgdb_info displays. + +config KGDB_TS + bool "Enable kgdb time stamp macros?" + depends on KGDB + default n + help + Kgdb event macros allow you to instrument your code with calls + to the kgdb event recording function. The event log may be + examined with gdb at a break point. Turning on this + capability also allows you to choose how many events to + keep. Kgdb always keeps the lastest events. + +choice + depends on KGDB_TS + prompt "Max number of time stamps to save?" + default KGDB_TS_128 + +config KGDB_TS_64 + bool "64" + +config KGDB_TS_128 + bool "128" + +config KGDB_TS_256 + bool "256" + +config KGDB_TS_512 + bool "512" + +config KGDB_TS_1024 + bool "1024" + +endchoice + +config STACK_OVERFLOW_TEST + bool "Turn on kernel stack overflow testing?" + depends on KGDB + default n + help + This option enables code in the front line interrupt handlers + to check for kernel stack overflow on interrupts and system + calls. This is part of the kgdb code on x86 systems. + +config KGDB_CONSOLE + bool "Enable serial console thru kgdb port" + depends on KGDB + default n + help + This option enables the command line "console=kgdb" option. + When the system is booted with this option in the command line + all kernel printk output is sent to gdb (as well as to other + consoles). For this to work gdb must be connected. For this + reason, this command line option will generate a breakpoint if + gdb has not yet connected. After the gdb continue command is + given all pent up console output will be printed by gdb on the + host machine. Neither this option, nor KGDB require the + serial driver to be configured. + +config KGDB_SYSRQ + bool "Turn on SysRq 'G' command to do a break?" + depends on KGDB + default y + help + This option includes an option in the SysRq code that allows + you to enter SysRq G which generates a breakpoint to the KGDB + stub. This will work if the keyboard is alive and can + interrupt the system. Because of constraints on when the + serial port interrupt can be enabled, this code may allow you + to interrupt the system before the serial port control C is + available. Just say yes here. + --- linux-2.6.9-rc1/arch/i386/kernel/acpi/boot.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/i386/kernel/acpi/boot.c 2004-09-03 00:56:30.109583296 -0700 @@ -71,6 +71,7 @@ int acpi_ht __initdata = 1; /* enable HT int acpi_lapic; int acpi_ioapic; int acpi_strict; +EXPORT_SYMBOL(acpi_strict); acpi_interrupt_flags acpi_sci_flags __initdata; int acpi_sci_override_gsi __initdata; @@ -829,9 +830,15 @@ acpi_boot_init (void) */ error = acpi_blacklisted(); if (error) { - printk(KERN_WARNING PREFIX "BIOS listed in blacklist, disabling ACPI support\n"); - disable_acpi(); - return error; + extern int acpi_force; + + if (acpi_force) { + printk(KERN_WARNING PREFIX "acpi=force override\n"); + } else { + printk(KERN_WARNING PREFIX "Disabling ACPI support\n"); + disable_acpi(); + return error; + } } /* --- linux-2.6.9-rc1/arch/i386/kernel/apic.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/i386/kernel/apic.c 2004-09-03 00:56:57.968348120 -0700 @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -201,6 +202,36 @@ void disconnect_bsp_APIC(void) outb(0x70, 0x22); outb(0x00, 0x23); } + else { + /* Go back to Virtual Wire compatibility mode */ + unsigned long value; + + /* For the spurious interrupt use vector F, and enable it */ + value = apic_read(APIC_SPIV); + value &= ~APIC_VECTOR_MASK; + value |= APIC_SPIV_APIC_ENABLED; + value |= 0xf; + apic_write_around(APIC_SPIV, value); + + /* For LVT0 make it edge triggered, active high, external and enabled */ + value = apic_read(APIC_LVT0); + value &= ~(APIC_MODE_MASK | APIC_SEND_PENDING | + APIC_INPUT_POLARITY | APIC_LVT_REMOTE_IRR | + APIC_LVT_LEVEL_TRIGGER | APIC_LVT_MASKED ); + value |= APIC_LVT_REMOTE_IRR | APIC_SEND_PENDING; + value = SET_APIC_DELIVERY_MODE(value, APIC_MODE_EXINT); + apic_write_around(APIC_LVT0, value); + + /* For LVT1 make it edge triggered, active high, nmi and enabled */ + value = apic_read(APIC_LVT1); + value &= ~( + APIC_MODE_MASK | APIC_SEND_PENDING | + APIC_INPUT_POLARITY | APIC_LVT_REMOTE_IRR | + APIC_LVT_LEVEL_TRIGGER | APIC_LVT_MASKED); + value |= APIC_LVT_REMOTE_IRR | APIC_SEND_PENDING; + value = SET_APIC_DELIVERY_MODE(value, APIC_MODE_NMI); + apic_write_around(APIC_LVT1, value); + } } void disable_local_APIC(void) @@ -343,7 +374,7 @@ void __init init_bsp_APIC(void) void __init setup_local_APIC (void) { - unsigned long value, ver, maxlvt; + unsigned long oldvalue, value, ver, maxlvt; /* Pound the ESR really hard over the head with a big hammer - mbligh */ if (esr_disable) { @@ -459,9 +490,7 @@ void __init setup_local_APIC (void) maxlvt = get_maxlvt(); if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */ apic_write(APIC_ESR, 0); - value = apic_read(APIC_ESR); - apic_printk(APIC_VERBOSE, "ESR value before enabling vector:" - " %08lx\n", value); + oldvalue = apic_read(APIC_ESR); value = ERROR_APIC_VECTOR; // enables sending errors apic_write_around(APIC_LVTERR, value); @@ -471,8 +500,10 @@ void __init setup_local_APIC (void) if (maxlvt > 3) apic_write(APIC_ESR, 0); value = apic_read(APIC_ESR); - apic_printk(APIC_VERBOSE, "ESR value after enabling vector:" - " %08lx\n", value); + if (value != oldvalue) + apic_printk(APIC_VERBOSE, "ESR value before enabling " + "vector: 0x%08lx after: 0x%08lx\n", + oldvalue, value); } else { if (esr_disable) /* @@ -1007,7 +1038,7 @@ void __init setup_secondary_APIC_clock(v local_irq_enable(); } -void __init disable_APIC_timer(void) +void __devinit disable_APIC_timer(void) { if (using_apic_timer) { unsigned long v; @@ -1071,8 +1102,7 @@ inline void smp_local_timer_interrupt(st { int cpu = smp_processor_id(); - x86_do_profile(regs); - + profile_tick(CPU_PROFILING, regs); if (--per_cpu(prof_counter, cpu) <= 0) { /* * The multiplier may have changed since the last time we got @@ -1191,7 +1221,7 @@ asmlinkage void smp_error_interrupt(void 6: Received illegal vector 7: Illegal register address */ - printk (KERN_INFO "APIC error on CPU%d: %02lx(%02lx)\n", + printk (KERN_DEBUG "APIC error on CPU%d: %02lx(%02lx)\n", smp_processor_id(), v , v1); irq_exit(); } --- linux-2.6.9-rc1/arch/i386/kernel/apm.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/arch/i386/kernel/apm.c 2004-09-02 20:51:51.000000000 -0700 @@ -2271,10 +2271,12 @@ static int __init apm_init(void) } if ((num_online_cpus() > 1) && !power_off && !smp) { printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n"); + apm_info.disabled = 1; return -ENODEV; } if (PM_IS_ACTIVE()) { printk(KERN_NOTICE "apm: overridden by ACPI.\n"); + apm_info.disabled = 1; return -ENODEV; } pm_active = 1; --- linux-2.6.9-rc1/arch/i386/kernel/asm-offsets.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/arch/i386/kernel/asm-offsets.c 2004-09-02 20:51:51.000000000 -0700 @@ -62,4 +62,5 @@ void foo(void) sizeof(struct tss_struct)); DEFINE(PAGE_SIZE_asm, PAGE_SIZE); + DEFINE(VSYSCALL_BASE, __fix_to_virt(FIX_VSYSCALL)); } --- linux-2.6.9-rc1/arch/i386/kernel/cpu/centaur.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/i386/kernel/cpu/centaur.c 2004-09-02 20:51:51.000000000 -0700 @@ -286,23 +286,20 @@ static void __init init_c3(struct cpuinf c->x86_capability[5] = cpuid_edx(0xC0000001); } - switch (c->x86_model) { - case 6 ... 8: /* Cyrix III family */ - rdmsr (MSR_VIA_FCR, lo, hi); - lo |= (1<<1 | 1<<7); /* Report CX8 & enable PGE */ - wrmsr (MSR_VIA_FCR, lo, hi); - - set_bit(X86_FEATURE_CX8, c->x86_capability); - set_bit(X86_FEATURE_3DNOW, c->x86_capability); + /* Cyrix III family needs CX8 & PGE explicity enabled. */ + if (c->x86_model >=6 && c->x86_model <= 9) { + rdmsr (MSR_VIA_FCR, lo, hi); + lo |= (1<<1 | 1<<7); + wrmsr (MSR_VIA_FCR, lo, hi); + set_bit(X86_FEATURE_CX8, c->x86_capability); + } - /* fall through */ + /* Before Nehemiah, the C3's had 3dNOW! */ + if (c->x86_model >=6 && c->x86_model <9) + set_bit(X86_FEATURE_3DNOW, c->x86_capability); - case 9: /* Nehemiah */ - default: - get_model_name(c); - display_cacheinfo(c); - break; - } + get_model_name(c); + display_cacheinfo(c); } static void __init init_centaur(struct cpuinfo_x86 *c) --- linux-2.6.9-rc1/arch/i386/kernel/cpu/common.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/arch/i386/kernel/cpu/common.c 2004-09-02 20:51:51.000000000 -0700 @@ -559,13 +559,11 @@ void __init cpu_init (void) load_esp0(t, thread); set_tss_desc(cpu,t); - per_cpu(cpu_gdt_table,cpu)[GDT_ENTRY_TSS].b &= 0xfffffdff; load_TR_desc(); load_LDT(&init_mm.context); /* Set up doublefault TSS pointer in the GDT */ __set_tss_desc(cpu, GDT_ENTRY_DOUBLEFAULT_TSS, &doublefault_tss); - per_cpu(cpu_gdt_table, cpu)[GDT_ENTRY_DOUBLEFAULT_TSS].b &= 0xfffffdff; /* Clear %fs and %gs. */ asm volatile ("xorl %eax, %eax; movl %eax, %fs; movl %eax, %gs"); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/i386/kernel/cpu/cpufreq/cpufreq-nforce2.c 2004-09-03 00:56:50.672457264 -0700 @@ -0,0 +1,468 @@ +/* + * (C) 2004 Sebastian Witt + * + * Licensed under the terms of the GNU GPL License version 2. + * Based upon reverse engineered information + * + * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous* + */ + +#include +#include +#include +#include +#include +#include +#include + +#define NFORCE2_XTAL 25 +#define NFORCE2_BOOTFSB 0x48 +#define NFORCE2_PLLENABLE 0xa8 +#define NFORCE2_PLLREG 0xa4 +#define NFORCE2_PLLADR 0xa0 +#define NFORCE2_PLL(mul, div) (0x100000 | (mul << 8) | div) + +#define NFORCE2_MIN_FSB 50 +#define NFORCE2_MAX_FSB 300 +#define NFORCE2_SAFE_DISTANCE 50 + +/* Delay in ms between FSB changes */ +//#define NFORCE2_DELAY 10 + +/* nforce2_chipset: + * FSB is changed using the chipset + */ +static struct pci_dev *nforce2_chipset_dev; + +/* fid: + * multiplier * 10 + */ +static int fid = 0; + +/* min_fsb, max_fsb: + * maximum and minimum available FSB + */ +static int min_fsb = 0; +static int max_fsb = 0; + +MODULE_AUTHOR("Sebastian Witt "); +MODULE_DESCRIPTION("nForce2 FSB changing cpufreq driver"); +MODULE_LICENSE("GPL"); + +module_param(fid, int, 0444); +module_param(min_fsb, int, 0444); +module_param(max_fsb, int, 0444); + +MODULE_PARM_DESC(fid, "CPU multiplier to use (11.5 = 115)"); +MODULE_PARM_DESC(min_fsb, + "Minimum FSB to use, if not defined: current FSB - 50"); +MODULE_PARM_DESC(max_fsb, "Maximum FSB to use, if not defined: current FSB"); + +/* DEBUG + * Define it if you want verbose debug output, e.g. for bug reporting + */ +#define NFORCE2_DEBUG + +#ifdef NFORCE2_DEBUG +#define dprintk(msg...) printk(msg) +#else +#define dprintk(msg...) do { } while(0) +#endif + +/* + * nforce2_calc_fsb - calculate FSB + * @pll: PLL value + * + * Calculates FSB from PLL value + */ +static int nforce2_calc_fsb(int pll) +{ + unsigned char mul, div; + + mul = (pll >> 8) & 0xff; + div = pll & 0xff; + + if (div > 0) + return NFORCE2_XTAL * mul / div; + + return 0; +} + +/* + * nforce2_calc_pll - calculate PLL value + * @fsb: FSB + * + * Calculate PLL value for given FSB + */ +static int nforce2_calc_pll(unsigned int fsb) +{ + unsigned char xmul, xdiv; + unsigned char mul = 0, div = 0; + int tried = 0; + + /* Try to calculate multiplier and divider up to 4 times */ + while (((mul == 0) || (div == 0)) && (tried <= 3)) { + for (xdiv = 1; xdiv <= 0x80; xdiv++) + for (xmul = 1; xmul <= 0xfe; xmul++) + if (nforce2_calc_fsb(NFORCE2_PLL(xmul, xdiv)) == + fsb + tried) { + mul = xmul; + div = xdiv; + } + tried++; + } + + if ((mul == 0) || (div == 0)) + return -1; + + return NFORCE2_PLL(mul, div); +} + +/* + * nforce2_write_pll - write PLL value to chipset + * @pll: PLL value + * + * Writes new FSB PLL value to chipset + */ +static void nforce2_write_pll(int pll) +{ + int temp; + + /* Set the pll addr. to 0x00 */ + temp = 0x00; + pci_write_config_dword(nforce2_chipset_dev, NFORCE2_PLLADR, temp); + + /* Now write the value in all 64 registers */ + for (temp = 0; temp <= 0x3f; temp++) { + pci_write_config_dword(nforce2_chipset_dev, + NFORCE2_PLLREG, pll); + } + + return; +} + +/* + * nforce2_fsb_read - Read FSB + * + * Read FSB from chipset + */ +static unsigned int nforce2_fsb_read(void) +{ + struct pci_dev *nforce2_sub5; + u32 fsb, temp = 0; + + /* PLL reg. already set? */ + pci_read_config_byte(nforce2_chipset_dev, + NFORCE2_PLLENABLE, (u8 *)&temp); + + if (!temp) { + /* Get chipset boot FSB from subdevice 5 */ + nforce2_sub5 = pci_get_subsys(PCI_VENDOR_ID_NVIDIA, + 0x01EF, + PCI_ANY_ID, + PCI_ANY_ID, + NULL); + + if (!nforce2_sub5) + return 0; + + pci_read_config_dword(nforce2_sub5, NFORCE2_BOOTFSB, &fsb); + fsb = fsb / 1000000; + } else { + /* Use PLL reg. value */ + pci_read_config_dword(nforce2_chipset_dev, + NFORCE2_PLLREG, &temp); + fsb = nforce2_calc_fsb(temp); + } + + return fsb; +} + +/* + * nforce2_set_fsb - set new FSB + * @fsb: New FSB + * + * Sets new FSB + */ +int nforce2_set_fsb(unsigned int fsb) +{ + u32 pll, temp = 0; + unsigned int tfsb; + int diff; + + if ((fsb > NFORCE2_MAX_FSB) || (fsb < NFORCE2_MIN_FSB)) { + printk(KERN_ERR "cpufreq: FSB %d is out of range!\n", fsb); + return -EINVAL; + } + + tfsb = nforce2_fsb_read(); + if (!tfsb) { + printk(KERN_ERR "cpufreq: Error while reading the FSB\n"); + return -EINVAL; + } + + /* First write? Then set actual value */ + pci_read_config_byte(nforce2_chipset_dev, + NFORCE2_PLLENABLE, (u8 *)&temp); + if (!temp) { + pll = nforce2_calc_pll(tfsb); + + if (pll < 0) + return -EINVAL; + + nforce2_write_pll(pll); + } + + /* Enable write access */ + temp = 0x01; + pci_write_config_byte(nforce2_chipset_dev, NFORCE2_PLLENABLE, (u8)temp); + + diff = tfsb - fsb; + + if (!diff) + return 0; + + while ((tfsb != fsb) && (tfsb <= max_fsb) && (tfsb >= min_fsb)) { + if (diff < 0) + tfsb++; + else + tfsb--; + + /* Calculate the PLL reg. value */ + if ((pll = nforce2_calc_pll(tfsb)) == -1) + return -EINVAL; + + nforce2_write_pll(pll); +#ifdef NFORCE2_DELAY + mdelay(NFORCE2_DELAY); +#endif + } + + temp = 0x40; + pci_write_config_byte(nforce2_chipset_dev, NFORCE2_PLLADR, (u8)temp); + + return 0; +} + +/** + * nforce2_get - get the CPU frequency + * @cpu: CPU number + * + * Returns the CPU frequency + */ +static unsigned int nforce2_get(unsigned int cpu) +{ + if (cpu) + return 0; + return nforce2_fsb_read() * fid * 100; +} + +/** + * nforce2_target - set a new CPUFreq policy + * @policy: new policy + * @target_freq: the target frequency + * @relation: how that frequency relates to achieved frequency (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H) + * + * Sets a new CPUFreq policy. + */ +static int nforce2_target(struct cpufreq_policy *policy, + unsigned int target_freq, unsigned int relation) +{ +// unsigned long flags; + struct cpufreq_freqs freqs; + unsigned int target_fsb; + + if ((target_freq > policy->max) || (target_freq < policy->min)) + return -EINVAL; + + target_fsb = target_freq / (fid * 100); + + freqs.old = nforce2_get(policy->cpu); + freqs.new = target_fsb * fid * 100; + freqs.cpu = 0; /* Only one CPU on nForce2 plattforms */ + + if (freqs.old == freqs.new) + return 0; + + printk(KERN_INFO "cpufreq: Old CPU frequency %d kHz, new %d kHz\n", + freqs.old, freqs.new); + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + /* Disable IRQs */ + //local_irq_save(flags); + + if (nforce2_set_fsb(target_fsb) < 0) + printk(KERN_ERR "cpufreq: Changing FSB to %d failed\n", + target_fsb); + else + printk(KERN_INFO "cpufreq: Changed FSB successfully to %d\n", + target_fsb); + + /* Enable IRQs */ + //local_irq_restore(flags); + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return 0; +} + +/** + * nforce2_verify - verifies a new CPUFreq policy + * @policy: new policy + */ +static int nforce2_verify(struct cpufreq_policy *policy) +{ + unsigned int fsb_pol_max; + + fsb_pol_max = policy->max / (fid * 100); + + if (policy->min < (fsb_pol_max * fid * 100)) + policy->max = (fsb_pol_max + 1) * fid * 100; + + cpufreq_verify_within_limits(policy, + policy->cpuinfo.min_freq, + policy->cpuinfo.max_freq); + return 0; +} + +static int nforce2_cpu_init(struct cpufreq_policy *policy) +{ + unsigned int fsb; + unsigned int rfid; + + /* capability check */ + if (policy->cpu != 0) + return -ENODEV; + + fsb = nforce2_fsb_read(); + + if (!fsb) + return -EIO; + + /* FIX: Get FID from CPU */ + if (!fid) { + if (!cpu_khz) { + printk(KERN_WARNING + "cpufreq: cpu_khz not set, can't calculate multiplier!\n"); + return -ENODEV; + } + + fid = cpu_khz / (fsb * 100); + rfid = fid % 5; + + if (rfid) { + if (rfid > 2) + fid += 5 - rfid; + else + fid -= rfid; + } + } + + printk(KERN_INFO "cpufreq: FSB currently at %i MHz, FID %d.%d\n", fsb, + fid / 10, fid % 10); + + if (!max_fsb) + max_fsb = fsb; + if (!min_fsb) + min_fsb = max_fsb - NFORCE2_SAFE_DISTANCE; + + if (max_fsb > NFORCE2_MAX_FSB) + max_fsb = NFORCE2_MAX_FSB; + if (min_fsb < NFORCE2_MIN_FSB) + min_fsb = NFORCE2_MIN_FSB; + + if (max_fsb > fsb) + printk(KERN_WARNING + "cpufreq: Warning! Overclocking the FSB from %d to %d can cause unstability and data loss!\n", + fsb, max_fsb); + + /* cpuinfo and default policy values */ + policy->cpuinfo.min_freq = min_fsb * fid * 100; + policy->cpuinfo.max_freq = max_fsb * fid * 100; + policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; + policy->cur = nforce2_get(policy->cpu); + policy->min = policy->cpuinfo.min_freq; + policy->max = policy->cpuinfo.max_freq; + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; + + return 0; +} + +static int nforce2_cpu_exit(struct cpufreq_policy *policy) +{ + return 0; +} + +static struct cpufreq_driver nforce2_driver = { + .name = "nforce2", + .verify = nforce2_verify, + .target = nforce2_target, + .get = nforce2_get, + .init = nforce2_cpu_init, + .exit = nforce2_cpu_exit, + .owner = THIS_MODULE, +}; + +/** + * nforce2_detect_chipset - detect the Southbridge which contains FSB PLL logic + * + * Detects nForce2 A2 and C1 stepping + * + */ +static unsigned int nforce2_detect_chipset(void) +{ + u8 revision; + + nforce2_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_NVIDIA, + PCI_DEVICE_ID_NVIDIA_NFORCE2, + PCI_ANY_ID, + PCI_ANY_ID, + NULL); + + if (nforce2_chipset_dev == NULL) + return -ENODEV; + + pci_read_config_byte(nforce2_chipset_dev, PCI_REVISION_ID, &revision); + + printk(KERN_INFO "cpufreq: Detected nForce2 chipset revision %X\n", + revision); + printk(KERN_INFO + "cpufreq: FSB changing is maybe unstable and can lead to crashes and data loss.\n"); + + return 0; +} + +/** + * nforce2_init - initializes the nForce2 CPUFreq driver + * + * Initializes the nForce2 FSB support. Returns -ENODEV on unsupported + * devices, -EINVAL on problems during initiatization, and zero on + * success. + */ +static int __init nforce2_init(void) +{ + /* TODO: do we need to detect the processor? */ + + /* detect chipset */ + if (nforce2_detect_chipset()) { + printk(KERN_ERR "cpufreq: No nForce2 chipset.\n"); + return -ENODEV; + } + + return cpufreq_register_driver(&nforce2_driver); +} + +/** + * nforce2_exit - unregisters cpufreq module + * + * Unregisters nForce2 FSB change support. + */ +static void __exit nforce2_exit(void) +{ + cpufreq_unregister_driver(&nforce2_driver); +} + +module_init(nforce2_init); +module_exit(nforce2_exit); + --- linux-2.6.9-rc1/arch/i386/kernel/cpu/cpufreq/Kconfig 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/i386/kernel/cpu/cpufreq/Kconfig 2004-09-03 00:56:50.672457264 -0700 @@ -196,6 +196,17 @@ config X86_SPEEDSTEP_RELAXED_CAP_CHECK option let's the probing code bypass some of those checks if the parameter "relaxed_check=1" is passed to the module. +config X86_CPUFREQ_NFORCE2 + tristate "nVidia nForce2 FSB changing" + depends on CPU_FREQ && EXPERIMENTAL + help + This adds the CPUFreq driver for FSB changing on nVidia nForce2 + plattforms. + + For details, take a look at . + + If in doubt, say N. + config X86_LONGRUN tristate "Transmeta LongRun" depends on CPU_FREQ --- linux-2.6.9-rc1/arch/i386/kernel/cpu/cpufreq/longhaul.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/arch/i386/kernel/cpu/cpufreq/longhaul.c 2004-09-02 20:51:51.000000000 -0700 @@ -38,6 +38,17 @@ #define PFX "longhaul: " +#define TYPE_LONGHAUL_V1 1 +#define TYPE_LONGHAUL_V2 2 +#define TYPE_POWERSAVER 3 + +#define CPU_SAMUEL 1 +#define CPU_SAMUEL2 2 +#define CPU_EZRA 3 +#define CPU_EZRA_T 4 +#define CPU_NEHEMIAH 5 + +static int cpu_model; static unsigned int numscales=16, numvscales; static unsigned int fsb; static int minvid, maxvid; @@ -73,9 +84,23 @@ static int voltage_table[32]; static unsigned int highest_speed, lowest_speed; /* kHz */ static int longhaul_version; static struct cpufreq_frequency_table *longhaul_table; +static char speedbuffer[8]; + +static char *print_speed(int speed) +{ + if (speed > 1000) { + if (speed%1000 == 0) + sprintf (speedbuffer, "%dGHz", speed/1000); + else + sprintf (speedbuffer, "%d.%dGHz", speed/1000, (speed%1000)/100); + } else + sprintf (speedbuffer, "%dMHz", speed); + + return speedbuffer; +} -static unsigned int calc_speed(int mult, int fsb) +static unsigned int calc_speed(int mult) { int khz; khz = (mult/10)*fsb; @@ -92,7 +117,7 @@ static int longhaul_get_cpu_mult(void) rdmsr (MSR_IA32_EBL_CR_POWERON, lo, hi); invalue = (lo & (1<<22|1<<23|1<<24|1<<25)) >>22; - if (longhaul_version==2 || longhaul_version==3) { + if (longhaul_version==TYPE_LONGHAUL_V2 || longhaul_version==TYPE_POWERSAVER) { if (lo & (1<<27)) invalue+=16; } @@ -101,8 +126,21 @@ static int longhaul_get_cpu_mult(void) static void do_powersaver(union msr_longhaul *longhaul, - unsigned int clock_ratio_index, int version) + unsigned int clock_ratio_index) { + int version; + + switch (cpu_model) { + case CPU_EZRA_T: + version = 3; + break; + case CPU_NEHEMIAH: + version = 0xf; + break; + default: + return; + } + rdmsrl(MSR_VIA_LONGHAUL, longhaul->val); longhaul->bits.SoftBusRatio = clock_ratio_index & 0xf; longhaul->bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4; @@ -125,7 +163,7 @@ static void do_powersaver(union msr_long * longhaul_set_cpu_frequency() * @clock_ratio_index : bitpattern of the new multiplier. * - * Sets a new clock ratio, and -if applicable- a new Front Side Bus + * Sets a new clock ratio. */ static void longhaul_setstate(unsigned int clock_ratio_index) @@ -134,22 +172,28 @@ static void longhaul_setstate(unsigned i struct cpufreq_freqs freqs; union msr_longhaul longhaul; union msr_bcr2 bcr2; + static unsigned int old_ratio=-1; + + if (old_ratio == clock_ratio_index) + return; + old_ratio = clock_ratio_index; mult = clock_ratio[clock_ratio_index]; if (mult == -1) return; - speed = calc_speed (mult, fsb); + speed = calc_speed(mult); if ((speed > highest_speed) || (speed < lowest_speed)) return; - freqs.old = calc_speed (longhaul_get_cpu_mult(), fsb); + freqs.old = calc_speed(longhaul_get_cpu_mult()); freqs.new = speed; freqs.cpu = 0; /* longhaul.c is UP only driver */ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); - dprintk (KERN_INFO PFX "FSB:%d Mult:%d.%dx\n", fsb, mult/10, mult%10); + dprintk (KERN_INFO PFX "Setting to FSB:%dMHz Mult:%d.%dx (%s)\n", + fsb, mult/10, mult%10, print_speed(speed/1000)); switch (longhaul_version) { @@ -160,7 +204,8 @@ static void longhaul_setstate(unsigned i * *NB* Until we get voltage scaling working v1 & v2 are the same code. * Longhaul v2 appears in Samuel2 Steppings 1->7 [C5b] and Ezra [C5C] */ - case 1: + case TYPE_LONGHAUL_V1: + case TYPE_LONGHAUL_V2: rdmsrl (MSR_VIA_BCR2, bcr2.val); /* Enable software clock multiplier */ bcr2.bits.ESOFTBF = 1; @@ -180,26 +225,18 @@ static void longhaul_setstate(unsigned i break; /* - * Longhaul v3 (aka Powersaver). (Ezra-T [C5M]) + * Longhaul v3 (aka Powersaver). (Ezra-T [C5M] & Nehemiah [C5N]) * We can scale voltage with this too, but that's currently * disabled until we come up with a decent 'match freq to voltage' * algorithm. * When we add voltage scaling, we will also need to do the * voltage/freq setting in order depending on the direction * of scaling (like we do in powernow-k7.c) - */ - case 2: - do_powersaver(&longhaul, clock_ratio_index, 3); - break; - - /* - * Powersaver. (Nehemiah [C5N]) - * As for Ezra-T, we don't do voltage yet. - * This can do FSB scaling too, but it has never been proven + * Nehemiah can do FSB scaling too, but this has never been proven * to work in practice. */ - case 3: - do_powersaver(&longhaul, clock_ratio_index, 0xf); + case TYPE_POWERSAVER: + do_powersaver(&longhaul, clock_ratio_index); break; } @@ -249,7 +286,6 @@ static int guess_fsb(void) static int __init longhaul_get_ranges(void) { - struct cpuinfo_x86 *c = cpu_data; unsigned long invalue; unsigned int multipliers[32]= { 50,30,40,100,55,35,45,95,90,70,80,60,120,75,85,65, @@ -261,62 +297,66 @@ static int __init longhaul_get_ranges(vo unsigned int eblcr_fsb_table_v2[] = { 133, 100, -1, 66 }; switch (longhaul_version) { - case 1: + case TYPE_LONGHAUL_V1: + case TYPE_LONGHAUL_V2: /* Ugh, Longhaul v1 didn't have the min/max MSRs. Assume min=3.0x & max = whatever we booted at. */ minmult = 30; maxmult = longhaul_get_cpu_mult(); rdmsr (MSR_IA32_EBL_CR_POWERON, lo, hi); invalue = (lo & (1<<18|1<<19)) >>18; - if (c->x86_model==6) + if (cpu_model==CPU_SAMUEL || cpu_model==CPU_SAMUEL2) fsb = eblcr_fsb_table_v1[invalue]; else fsb = guess_fsb(); break; - case 2: - rdmsrl (MSR_VIA_LONGHAUL, longhaul.val); - - invalue = longhaul.bits.MaxMHzBR; - if (longhaul.bits.MaxMHzBR4) - invalue += 16; - maxmult=multipliers[invalue]; - - invalue = longhaul.bits.MinMHzBR; - if (longhaul.bits.MinMHzBR4 == 1) - minmult = 30; - else - minmult = multipliers[invalue]; - - fsb = eblcr_fsb_table_v2[longhaul.bits.MaxMHzFSB]; - break; - - case 3: - rdmsrl (MSR_VIA_LONGHAUL, longhaul.val); - - /* - * TODO: This code works, but raises a lot of questions. - * - Some Nehemiah's seem to have broken Min/MaxMHzBR's. - * We get around this by using a hardcoded multiplier of 5.0x - * for the minimimum speed, and the speed we booted up at for the max. - * This is done in longhaul_get_cpu_mult() by reading the EBLCR register. - * - According to some VIA documentation EBLCR is only - * in pre-Nehemiah C3s. How this still works is a mystery. - * We're possibly using something undocumented and unsupported, - * But it works, so we don't grumble. - */ - minmult=50; - maxmult=longhaul_get_cpu_mult(); - - /* Starting with the 1.2GHz parts, theres a 200MHz bus. */ - if ((cpu_khz/1000) > 1200) - fsb = 200; - else + case TYPE_POWERSAVER: + /* Ezra-T */ + if (cpu_model==CPU_EZRA_T) { + rdmsrl (MSR_VIA_LONGHAUL, longhaul.val); + invalue = longhaul.bits.MaxMHzBR; + if (longhaul.bits.MaxMHzBR4) + invalue += 16; + maxmult=multipliers[invalue]; + + invalue = longhaul.bits.MinMHzBR; + if (longhaul.bits.MinMHzBR4 == 1) + minmult = 30; + else + minmult = multipliers[invalue]; fsb = eblcr_fsb_table_v2[longhaul.bits.MaxMHzFSB]; - break; + break; + } + + /* Nehemiah */ + if (cpu_model==CPU_NEHEMIAH) { + rdmsrl (MSR_VIA_LONGHAUL, longhaul.val); + + /* + * TODO: This code works, but raises a lot of questions. + * - Some Nehemiah's seem to have broken Min/MaxMHzBR's. + * We get around this by using a hardcoded multiplier of 4.0x + * for the minimimum speed, and the speed we booted up at for the max. + * This is done in longhaul_get_cpu_mult() by reading the EBLCR register. + * - According to some VIA documentation EBLCR is only + * in pre-Nehemiah C3s. How this still works is a mystery. + * We're possibly using something undocumented and unsupported, + * But it works, so we don't grumble. + */ + minmult=40; + maxmult=longhaul_get_cpu_mult(); + + /* Starting with the 1.2GHz parts, theres a 200MHz bus. */ + if ((cpu_khz/1000) > 1200) + fsb = 200; + else + fsb = eblcr_fsb_table_v2[longhaul.bits.MaxMHzFSB]; + break; + } } - dprintk (KERN_INFO PFX "MinMult=%d.%dx MaxMult=%d.%dx\n", + dprintk (KERN_INFO PFX "MinMult:%d.%dx MaxMult:%d.%dx\n", minmult/10, minmult%10, maxmult/10, maxmult%10); if (fsb == -1) { @@ -324,10 +364,11 @@ static int __init longhaul_get_ranges(vo return -EINVAL; } - highest_speed = calc_speed (maxmult, fsb); - lowest_speed = calc_speed (minmult,fsb); - dprintk (KERN_INFO PFX "FSB: %dMHz Lowestspeed=%dMHz Highestspeed=%dMHz\n", - fsb, lowest_speed/1000, highest_speed/1000); + highest_speed = calc_speed(maxmult); + lowest_speed = calc_speed(minmult); + dprintk (KERN_INFO PFX "FSB:%dMHz ", fsb); + dprintk ("Lowest speed:%s ", print_speed(lowest_speed/1000)); + dprintk ("Highest speed:%s\n", print_speed(highest_speed/1000)); if (lowest_speed == highest_speed) { printk (KERN_INFO PFX "highestspeed == lowest, aborting.\n"); @@ -350,7 +391,7 @@ static int __init longhaul_get_ranges(vo continue; if (ratio > maxmult || ratio < minmult) continue; - longhaul_table[k].frequency = calc_speed (ratio, fsb); + longhaul_table[k].frequency = calc_speed(ratio); longhaul_table[k].index = j; k++; } @@ -426,8 +467,7 @@ static int longhaul_verify(struct cpufre static int longhaul_target(struct cpufreq_policy *policy, - unsigned int target_freq, - unsigned int relation) + unsigned int target_freq, unsigned int relation) { unsigned int table_index = 0; unsigned int new_clock_ratio = 0; @@ -442,13 +482,15 @@ static int longhaul_target(struct cpufre return 0; } + static unsigned int longhaul_get(unsigned int cpu) { if (cpu) return 0; - return (calc_speed (longhaul_get_cpu_mult(), fsb)); + return calc_speed(longhaul_get_cpu_mult()); } + static int __init longhaul_cpu_init(struct cpufreq_policy *policy) { struct cpuinfo_x86 *c = cpu_data; @@ -457,26 +499,31 @@ static int __init longhaul_cpu_init(stru switch (c->x86_model) { case 6: + cpu_model = CPU_SAMUEL; cpuname = "C3 'Samuel' [C5A]"; - longhaul_version=1; + longhaul_version = TYPE_LONGHAUL_V1; memcpy (clock_ratio, samuel1_clock_ratio, sizeof(samuel1_clock_ratio)); memcpy (eblcr_table, samuel1_eblcr, sizeof(samuel1_eblcr)); break; - case 7: /* C5B / C5C */ - longhaul_version=1; + case 7: + longhaul_version = TYPE_LONGHAUL_V1; switch (c->x86_mask) { case 0: + cpu_model = CPU_SAMUEL2; cpuname = "C3 'Samuel 2' [C5B]"; /* Note, this is not a typo, early Samuel2's had Samuel1 ratios. */ memcpy (clock_ratio, samuel1_clock_ratio, sizeof(samuel1_clock_ratio)); memcpy (eblcr_table, samuel2_eblcr, sizeof(samuel2_eblcr)); break; case 1 ... 15: - if (c->x86_mask < 8) + if (c->x86_mask < 8) { + cpu_model = CPU_SAMUEL2; cpuname = "C3 'Samuel 2' [C5B]"; - else + } else { + cpu_model = CPU_EZRA; cpuname = "C3 'Ezra' [C5C]"; + } memcpy (clock_ratio, ezra_clock_ratio, sizeof(ezra_clock_ratio)); memcpy (eblcr_table, ezra_eblcr, sizeof(ezra_eblcr)); break; @@ -484,15 +531,17 @@ static int __init longhaul_cpu_init(stru break; case 8: + cpu_model = CPU_EZRA_T; cpuname = "C3 'Ezra-T' [C5M]"; - longhaul_version=2; + longhaul_version = TYPE_POWERSAVER; numscales=32; memcpy (clock_ratio, ezrat_clock_ratio, sizeof(ezrat_clock_ratio)); memcpy (eblcr_table, ezrat_eblcr, sizeof(ezrat_eblcr)); break; case 9: - longhaul_version=3; + cpu_model = CPU_NEHEMIAH; + longhaul_version = TYPE_POWERSAVER; numscales=32; switch (c->x86_mask) { case 0 ... 1: @@ -518,19 +567,28 @@ static int __init longhaul_cpu_init(stru break; } - printk (KERN_INFO PFX "VIA %s CPU detected. Longhaul v%d supported.\n", - cpuname, longhaul_version); + printk (KERN_INFO PFX "VIA %s CPU detected. ", cpuname); + switch (longhaul_version) { + case TYPE_LONGHAUL_V1: + case TYPE_LONGHAUL_V2: + printk ("Longhaul v%d supported.\n", longhaul_version); + break; + case TYPE_POWERSAVER: + printk ("Powersaver supported.\n"); + break; + }; ret = longhaul_get_ranges(); if (ret != 0) return ret; - if ((longhaul_version==2) && (dont_scale_voltage==0)) + if ((longhaul_version==TYPE_LONGHAUL_V2 || longhaul_version==TYPE_POWERSAVER) && + (dont_scale_voltage==0)) longhaul_setup_voltagescaling(); policy->governor = CPUFREQ_DEFAULT_GOVERNOR; policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; - policy->cur = calc_speed (longhaul_get_cpu_mult(), fsb); + policy->cur = calc_speed(longhaul_get_cpu_mult()); ret = cpufreq_frequency_table_cpuinfo(policy, longhaul_table); if (ret) @@ -563,6 +621,7 @@ static struct cpufreq_driver longhaul_dr .attr = longhaul_attr, }; + static int __init longhaul_init(void) { struct cpuinfo_x86 *c = cpu_data; @@ -580,16 +639,17 @@ static int __init longhaul_init(void) return -ENODEV; } + static void __exit longhaul_exit(void) { int i=0; - unsigned int new_clock_ratio; - - while (clock_ratio[i] != maxmult) - i++; - new_clock_ratio = longhaul_table[i].index & 0xFF; - longhaul_setstate(new_clock_ratio); + for (i=0; i < numscales; i++) { + if (clock_ratio[i] == maxmult) { + longhaul_setstate(i); + break; + } + } cpufreq_unregister_driver(&longhaul_driver); kfree(longhaul_table); --- linux-2.6.9-rc1/arch/i386/kernel/cpu/cpufreq/Makefile 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/i386/kernel/cpu/cpufreq/Makefile 2004-09-03 00:56:50.672457264 -0700 @@ -11,6 +11,7 @@ obj-$(CONFIG_X86_SPEEDSTEP_LIB) += speed obj-$(CONFIG_X86_SPEEDSTEP_SMI) += speedstep-smi.o obj-$(CONFIG_X86_ACPI_CPUFREQ) += acpi.o obj-$(CONFIG_X86_P4_CLOCKMOD) += p4-clockmod.o +obj-$(CONFIG_X86_CPUFREQ_NFORCE2) += cpufreq-nforce2.o ifdef CONFIG_X86_ACPI_CPUFREQ ifdef CONFIG_ACPI_DEBUG --- linux-2.6.9-rc1/arch/i386/kernel/cpu/cpufreq/powernow-k8.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/i386/kernel/cpu/cpufreq/powernow-k8.c 2004-09-03 00:56:30.566513832 -0700 @@ -948,13 +948,13 @@ static int __init powernowk8_cpu_init(st * an UP version, and is deprecated by AMD. */ - if (pol->cpu != 0) { - printk(KERN_ERR PFX "init not cpu 0\n"); + if ((num_online_cpus() != 1) || (num_possible_cpus() != 1)) { + printk(KERN_INFO PFX "MP systems not supported by PSB BIOS structure\n"); kfree(data); return -ENODEV; } - if ((num_online_cpus() != 1) || (num_possible_cpus() != 1)) { - printk(KERN_INFO PFX "MP systems not supported by PSB BIOS structure\n"); + if (pol->cpu != 0) { + printk(KERN_ERR PFX "init not cpu 0\n"); kfree(data); return -ENODEV; } --- linux-2.6.9-rc1/arch/i386/kernel/cpu/cpufreq/speedstep-ich.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/arch/i386/kernel/cpu/cpufreq/speedstep-ich.c 2004-09-03 00:56:33.060134744 -0700 @@ -171,7 +171,7 @@ static int speedstep_activate (void) */ static unsigned int speedstep_detect_chipset (void) { - speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL, + speedstep_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, PCI_ANY_ID, PCI_ANY_ID, @@ -179,7 +179,7 @@ static unsigned int speedstep_detect_chi if (speedstep_chipset_dev) return 4; /* 4-M */ - speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL, + speedstep_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, PCI_ANY_ID, PCI_ANY_ID, @@ -188,7 +188,7 @@ static unsigned int speedstep_detect_chi return 3; /* 3-M */ - speedstep_chipset_dev = pci_find_subsys(PCI_VENDOR_ID_INTEL, + speedstep_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_10, PCI_ANY_ID, PCI_ANY_ID, @@ -201,7 +201,7 @@ static unsigned int speedstep_detect_chi static struct pci_dev *hostbridge; u8 rev = 0; - hostbridge = pci_find_subsys(PCI_VENDOR_ID_INTEL, + hostbridge = pci_get_subsys(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82815_MC, PCI_ANY_ID, PCI_ANY_ID, @@ -214,9 +214,11 @@ static unsigned int speedstep_detect_chi if (rev < 5) { dprintk(KERN_INFO "cpufreq: hostbridge does not support speedstep\n"); speedstep_chipset_dev = NULL; + pci_dev_put(hostbridge); return 0; } + pci_dev_put(hostbridge); return 2; /* 2-M */ } @@ -411,8 +413,10 @@ static int __init speedstep_init(void) } /* activate speedstep support */ - if (speedstep_activate()) + if (speedstep_activate()) { + pci_dev_put(speedstep_chipset_dev); return -EINVAL; + } return cpufreq_register_driver(&speedstep_driver); } @@ -425,6 +429,7 @@ static int __init speedstep_init(void) */ static void __exit speedstep_exit(void) { + pci_dev_put(speedstep_chipset_dev); cpufreq_unregister_driver(&speedstep_driver); } --- linux-2.6.9-rc1/arch/i386/kernel/cpu/intel.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/i386/kernel/cpu/intel.c 2004-09-02 20:51:51.000000000 -0700 @@ -72,44 +72,44 @@ struct _cache_table /* all the cache descriptor types we care about (no TLB or trace cache entries) */ static struct _cache_table cache_table[] __initdata = { - { 0x06, LVL_1_INST, 8 }, - { 0x08, LVL_1_INST, 16 }, - { 0x0a, LVL_1_DATA, 8 }, - { 0x0c, LVL_1_DATA, 16 }, - { 0x22, LVL_3, 512 }, - { 0x23, LVL_3, 1024 }, - { 0x25, LVL_3, 2048 }, - { 0x29, LVL_3, 4096 }, - { 0x2c, LVL_1_DATA, 32 }, - { 0x30, LVL_1_INST, 32 }, - { 0x39, LVL_2, 128 }, - { 0x3b, LVL_2, 128 }, - { 0x3c, LVL_2, 256 }, - { 0x41, LVL_2, 128 }, - { 0x42, LVL_2, 256 }, - { 0x43, LVL_2, 512 }, - { 0x44, LVL_2, 1024 }, - { 0x45, LVL_2, 2048 }, - { 0x60, LVL_1_DATA, 16 }, - { 0x66, LVL_1_DATA, 8 }, - { 0x67, LVL_1_DATA, 16 }, - { 0x68, LVL_1_DATA, 32 }, - { 0x70, LVL_TRACE, 12 }, - { 0x71, LVL_TRACE, 16 }, - { 0x72, LVL_TRACE, 32 }, - { 0x78, LVL_2, 1024 }, - { 0x79, LVL_2, 128 }, - { 0x7a, LVL_2, 256 }, - { 0x7b, LVL_2, 512 }, - { 0x7c, LVL_2, 1024 }, - { 0x7d, LVL_2, 2048 }, - { 0x7f, LVL_2, 512 }, - { 0x82, LVL_2, 256 }, - { 0x83, LVL_2, 512 }, - { 0x84, LVL_2, 1024 }, - { 0x85, LVL_2, 2048 }, - { 0x86, LVL_2, 512 }, - { 0x87, LVL_2, 1024 }, + { 0x06, LVL_1_INST, 8 }, /* 4-way set assoc, 32 byte line size */ + { 0x08, LVL_1_INST, 16 }, /* 4-way set assoc, 32 byte line size */ + { 0x0a, LVL_1_DATA, 8 }, /* 2 way set assoc, 32 byte line size */ + { 0x0c, LVL_1_DATA, 16 }, /* 4-way set assoc, 32 byte line size */ + { 0x22, LVL_3, 512 }, /* 4-way set assoc, sectored cache, 64 byte line size */ + { 0x23, LVL_3, 1024 }, /* 8-way set assoc, sectored cache, 64 byte line size */ + { 0x25, LVL_3, 2048 }, /* 8-way set assoc, sectored cache, 64 byte line size */ + { 0x29, LVL_3, 4096 }, /* 8-way set assoc, sectored cache, 64 byte line size */ + { 0x2c, LVL_1_DATA, 32 }, /* 8-way set assoc, 64 byte line size */ + { 0x30, LVL_1_INST, 32 }, /* 8-way set assoc, 64 byte line size */ + { 0x39, LVL_2, 128 }, /* 4-way set assoc, sectored cache, 64 byte line size */ + { 0x3b, LVL_2, 128 }, /* 2-way set assoc, sectored cache, 64 byte line size */ + { 0x3c, LVL_2, 256 }, /* 4-way set assoc, sectored cache, 64 byte line size */ + { 0x41, LVL_2, 128 }, /* 4-way set assoc, 32 byte line size */ + { 0x42, LVL_2, 256 }, /* 4-way set assoc, 32 byte line size */ + { 0x43, LVL_2, 512 }, /* 4-way set assoc, 32 byte line size */ + { 0x44, LVL_2, 1024 }, /* 4-way set assoc, 32 byte line size */ + { 0x45, LVL_2, 2048 }, /* 4-way set assoc, 32 byte line size */ + { 0x60, LVL_1_DATA, 16 }, /* 8-way set assoc, sectored cache, 64 byte line size */ + { 0x66, LVL_1_DATA, 8 }, /* 4-way set assoc, sectored cache, 64 byte line size */ + { 0x67, LVL_1_DATA, 16 }, /* 4-way set assoc, sectored cache, 64 byte line size */ + { 0x68, LVL_1_DATA, 32 }, /* 4-way set assoc, sectored cache, 64 byte line size */ + { 0x70, LVL_TRACE, 12 }, /* 8-way set assoc */ + { 0x71, LVL_TRACE, 16 }, /* 8-way set assoc */ + { 0x72, LVL_TRACE, 32 }, /* 8-way set assoc */ + { 0x78, LVL_2, 1024 }, /* 4-way set assoc, 64 byte line size */ + { 0x79, LVL_2, 128 }, /* 8-way set assoc, sectored cache, 64 byte line size */ + { 0x7a, LVL_2, 256 }, /* 8-way set assoc, sectored cache, 64 byte line size */ + { 0x7b, LVL_2, 512 }, /* 8-way set assoc, sectored cache, 64 byte line size */ + { 0x7c, LVL_2, 1024 }, /* 8-way set assoc, sectored cache, 64 byte line size */ + { 0x7d, LVL_2, 2048 }, /* 8-way set assoc, 64 byte line size */ + { 0x7f, LVL_2, 512 }, /* 2-way set assoc, 64 byte line size */ + { 0x82, LVL_2, 256 }, /* 8-way set assoc, 32 byte line size */ + { 0x83, LVL_2, 512 }, /* 8-way set assoc, 32 byte line size */ + { 0x84, LVL_2, 1024 }, /* 8-way set assoc, 32 byte line size */ + { 0x85, LVL_2, 2048 }, /* 8-way set assoc, 32 byte line size */ + { 0x86, LVL_2, 512 }, /* 4-way set assoc, 64 byte line size */ + { 0x87, LVL_2, 1024 }, /* 8-way set assoc, 64 byte line size */ { 0x00, 0, 0} }; --- linux-2.6.9-rc1/arch/i386/kernel/cpu/mtrr/main.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/arch/i386/kernel/cpu/mtrr/main.c 2004-09-03 00:56:33.061134592 -0700 @@ -77,22 +77,24 @@ static int have_wrcomb(void) { struct pci_dev *dev; - if ((dev = pci_find_class(PCI_CLASS_BRIDGE_HOST << 8, NULL)) != NULL) { + if ((dev = pci_get_class(PCI_CLASS_BRIDGE_HOST << 8, NULL)) != NULL) { /* ServerWorks LE chipsets have problems with write-combining Don't allow it and leave room for other chipsets to be tagged */ if (dev->vendor == PCI_VENDOR_ID_SERVERWORKS && dev->device == PCI_DEVICE_ID_SERVERWORKS_LE) { printk(KERN_INFO "mtrr: Serverworks LE detected. Write-combining disabled.\n"); + pci_dev_put(dev); return 0; } /* Intel 450NX errata # 23. Non ascending cachline evictions to write combining memory may resulting in data corruption */ if (dev->vendor == PCI_VENDOR_ID_INTEL && - dev->device == PCI_DEVICE_ID_INTEL_82451NX) - { + dev->device == PCI_DEVICE_ID_INTEL_82451NX) { printk(KERN_INFO "mtrr: Intel 450NX MMC detected. Write-combining disabled.\n"); + pci_dev_put(dev); return 0; } + pci_dev_put(dev); } return (mtrr_if->have_wrcomb ? mtrr_if->have_wrcomb() : 0); } --- linux-2.6.9-rc1/arch/i386/kernel/cpu/proc.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/i386/kernel/cpu/proc.c 2004-09-02 20:51:51.000000000 -0700 @@ -44,8 +44,8 @@ static int show_cpuinfo(struct seq_file NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* Intel-defined (#2) */ - "pni", NULL, NULL, "monitor", "ds_cpl", NULL, NULL, "tm2", - "est", NULL, "cid", NULL, NULL, NULL, NULL, NULL, + "pni", NULL, NULL, "monitor", "ds_cpl", NULL, NULL, "est", + "tm2", NULL, "cid", NULL, NULL, NULL, "xtpr", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, --- linux-2.6.9-rc1/arch/i386/kernel/dmi_scan.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/arch/i386/kernel/dmi_scan.c 2004-09-03 00:56:30.110583144 -0700 @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include #include @@ -443,41 +443,6 @@ static __initdata struct dmi_blacklist d { NULL, } }; - - -/* - * Walk the blacklist table running matching functions until someone - * returns 1 or we hit the end. - */ - - -static __init void dmi_check_blacklist(void) -{ -#ifdef CONFIG_ACPI_BOOT -#define ACPI_BLACKLIST_CUTOFF_YEAR 2001 - - if (dmi_ident[DMI_BIOS_DATE]) { - char *s = strrchr(dmi_ident[DMI_BIOS_DATE], '/'); - if (s) { - int year, disable = 0; - s++; - year = simple_strtoul(s,NULL,0); - if (year >= 1000) - disable = year < ACPI_BLACKLIST_CUTOFF_YEAR; - else if (year < 1 || (year > 90 && year <= 99)) - disable = 1; - if (disable && !acpi_force) { - printk(KERN_NOTICE "ACPI disabled because your bios is from %s and too old\n", s); - printk(KERN_NOTICE "You can enable it with acpi=force\n"); - disable_acpi(); - } - } - } -#endif - dmi_check_system(dmi_blacklist); -} - - /* * Process a DMI table entry. Right now all we care about are the BIOS @@ -535,7 +500,7 @@ void __init dmi_scan_machine(void) { int err = dmi_iterate(dmi_decode); if(err == 0) - dmi_check_blacklist(); + dmi_check_system(dmi_blacklist); else printk(KERN_INFO "DMI not present.\n"); } --- linux-2.6.9-rc1/arch/i386/kernel/doublefault.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/i386/kernel/doublefault.c 2004-09-02 20:51:51.000000000 -0700 @@ -13,7 +13,7 @@ static unsigned long doublefault_stack[DOUBLEFAULT_STACKSIZE]; #define STACK_START (unsigned long)(doublefault_stack+DOUBLEFAULT_STACKSIZE) -#define ptr_ok(x) ((x) > 0xc0000000 && (x) < 0xc1000000) +#define ptr_ok(x) ((x) > PAGE_OFFSET && (x) < PAGE_OFFSET + 0x1000000) static void doublefault_fn(void) { --- linux-2.6.9-rc1/arch/i386/kernel/entry.S 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/i386/kernel/entry.S 2004-09-03 00:56:59.178164200 -0700 @@ -48,6 +48,18 @@ #include #include #include "irq_vectors.h" + /* We do not recover from a stack overflow, but at least + * we know it happened and should be able to track it down. + */ +#ifdef CONFIG_STACK_OVERFLOW_TEST +#define STACK_OVERFLOW_TEST \ + testl $(THREAD_SIZE - 512),%esp; \ + jnz 10f; \ + call stack_overflow; \ +10: +#else +#define STACK_OVERFLOW_TEST +#endif #define nr_syscalls ((syscall_table_size)/4) @@ -94,7 +106,8 @@ VM_MASK = 0x00020000 pushl %ebx; \ movl $(__USER_DS), %edx; \ movl %edx, %ds; \ - movl %edx, %es; + movl %edx, %es; \ + STACK_OVERFLOW_TEST #define RESTORE_INT_REGS \ popl %ebx; \ @@ -232,6 +245,7 @@ need_resched: # sysenter call handler stub ENTRY(sysenter_entry) movl TSS_sysenter_esp0(%esp),%esp + .globl sysenter_past_esp sysenter_past_esp: sti pushl $(__USER_DS) @@ -255,11 +269,11 @@ sysenter_past_esp: pushl %eax SAVE_ALL GET_THREAD_INFO(%ebp) - cmpl $(nr_syscalls), %eax - jae syscall_badsys testb $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT),TI_flags(%ebp) jnz syscall_trace_entry + cmpl $(nr_syscalls), %eax + jae syscall_badsys call *sys_call_table(,%eax,4) movl %eax,EAX(%esp) cli @@ -278,11 +292,11 @@ ENTRY(system_call) pushl %eax # save orig_eax SAVE_ALL GET_THREAD_INFO(%ebp) - cmpl $(nr_syscalls), %eax - jae syscall_badsys # system call tracing in operation testb $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT),TI_flags(%ebp) jnz syscall_trace_entry + cmpl $(nr_syscalls), %eax + jae syscall_badsys syscall_call: call *sys_call_table(,%eax,4) movl %eax,EAX(%esp) # store the return value @@ -294,6 +308,19 @@ syscall_exit: testw $_TIF_ALLWORK_MASK, %cx # current->work jne syscall_exit_work restore_all: +#ifdef CONFIG_TRAP_BAD_SYSCALL_EXITS + movl EFLAGS(%esp), %eax # mix EFLAGS and CS + movb CS(%esp), %al + testl $(VM_MASK | 3), %eax + jz resume_kernelX # returning to kernel or vm86-space + + cmpl $0,TI_preempt_count(%ebp) # non-zero preempt_count ? + jz resume_kernelX + + int $3 + +resume_kernelX: +#endif RESTORE_ALL # perform work that needs to be done immediately before resumption @@ -406,6 +433,16 @@ ENTRY(name) \ /* The include is where all of the SMP etc. interrupts come from */ #include "entry_arch.h" +#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_PERFCTR) +ENTRY(perfctr_interrupt) + pushl $LOCAL_PERFCTR_VECTOR-256 + SAVE_ALL + pushl %esp + call smp_perfctr_interrupt + addl $4, %esp + jmp ret_from_intr +#endif + ENTRY(divide_error) pushl $0 # no error code pushl $do_divide_error @@ -489,9 +526,16 @@ ENTRY(debug) jne debug_stack_correct FIX_STACK(12, debug_stack_correct, debug_esp_fix_insn) debug_stack_correct: - pushl $0 - pushl $do_debug - jmp error_code + pushl $-1 # mark this as an int + SAVE_ALL + movl %esp,%edx + pushl $0 + pushl %edx + call do_debug + addl $8,%esp + testl %eax,%eax + jnz restore_all + jmp ret_from_exception /* * NMI is doubly nasty. It can happen _while_ we're handling @@ -540,9 +584,16 @@ nmi_debug_stack_fixup: jmp nmi_stack_correct ENTRY(int3) + pushl $-1 # mark this as an int + SAVE_ALL + movl %esp,%edx pushl $0 - pushl $do_int3 - jmp error_code + pushl %edx + call do_int3 + addl $8,%esp + testl %eax,%eax + jnz restore_all + jmp ret_from_exception ENTRY(overflow) pushl $0 @@ -885,6 +936,13 @@ ENTRY(sys_call_table) .long sys_mq_timedreceive /* 280 */ .long sys_mq_notify .long sys_mq_getsetattr - .long sys_ni_syscall /* reserved for kexec */ + .long sys_kexec_load + .long sys_waitid + .long sys_perfctr_info + .long sys_vperfctr_open + .long sys_vperfctr_control + .long sys_vperfctr_unlink + .long sys_vperfctr_iresume + .long sys_vperfctr_read syscall_table_size=(.-sys_call_table) --- linux-2.6.9-rc1/arch/i386/kernel/i386_ksyms.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/i386/kernel/i386_ksyms.c 2004-09-03 00:56:40.980930600 -0700 @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -32,6 +31,7 @@ #include #include #include +#include extern void dump_thread(struct pt_regs *, struct user *); extern spinlock_t rtc_lock; @@ -177,6 +177,7 @@ EXPORT_SYMBOL_GPL(unset_nmi_callback); extern int memcmp(const void *,const void *,__kernel_size_t); EXPORT_SYMBOL_NOVERS(memcmp); +EXPORT_SYMBOL(register_die_notifier); #ifdef CONFIG_HAVE_DEC_LOCK EXPORT_SYMBOL(atomic_dec_and_lock); #endif --- linux-2.6.9-rc1/arch/i386/kernel/i8259.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/i386/kernel/i8259.c 2004-09-03 00:56:57.721385664 -0700 @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -226,7 +227,7 @@ spurious_8259A_irq: * lets ACK and report it. [once per IRQ] */ if (!(spurious_irq_mask & irqmask)) { - printk("spurious 8259A interrupt: IRQ%d.\n", irq); + printk(KERN_DEBUG "spurious 8259A interrupt: IRQ%d.\n", irq); spurious_irq_mask |= irqmask; } atomic_inc(&irq_err_count); @@ -269,10 +270,22 @@ static int i8259A_suspend(struct sys_dev return 0; } +static int i8259A_shutdown(struct sys_device *dev) +{ + /* Put the i8259A into a quiescent state that + * the kernel initialization code can get it + * out of. + */ + outb(0xff, 0x21); /* mask all of 8259A-1 */ + outb(0xff, 0xA1); /* mask all of 8259A-1 */ + return 0; +} + static struct sysdev_class i8259_sysdev_class = { set_kset_name("i8259"), .suspend = i8259A_suspend, .resume = i8259A_resume, + .shutdown = i8259A_shutdown, }; static struct sys_device device_i8259A = { @@ -413,6 +426,8 @@ void __init init_IRQ(void) */ intr_init_hook(); + perfctr_vector_init(); + /* * Set the clock to HZ Hz, we already have a valid * vector now: --- linux-2.6.9-rc1/arch/i386/kernel/io_apic.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/i386/kernel/io_apic.c 2004-09-03 00:56:58.233307840 -0700 @@ -574,9 +574,11 @@ static int balanced_irq(void *unused) time_remaining = schedule_timeout(time_remaining); if (time_after(jiffies, prev_balance_time+balanced_irq_interval)) { + preempt_disable(); do_irq_balance(); prev_balance_time = jiffies; time_remaining = balanced_irq_interval; + preempt_enable(); } } return 0; @@ -745,7 +747,7 @@ static int __init find_irq_entry(int api /* * Find the pin to which IRQ[irq] (ISA) is connected */ -static int __init find_isa_irq_pin(int irq, int type) +static int find_isa_irq_pin(int irq, int type) { int i; @@ -1120,11 +1122,7 @@ static inline int IO_APIC_irq_trigger(in /* irq_vectors is indexed by the sum of all RTEs in all I/O APICs. */ u8 irq_vector[NR_IRQ_VECTORS] = { FIRST_DEVICE_VECTOR , 0 }; -#ifdef CONFIG_PCI_MSI int assign_irq_vector(int irq) -#else -int __init assign_irq_vector(int irq) -#endif { static int current_vector = FIRST_DEVICE_VECTOR, offset = 0; @@ -1626,11 +1624,42 @@ static void __init enable_IO_APIC(void) */ void disable_IO_APIC(void) { + int pin; /* * Clear the IO-APIC before rebooting: */ clear_IO_APIC(); + /* + * If the i82559 is routed through an IOAPIC + * Put that IOAPIC in virtual wire mode + * so legacy interrups can be delivered. + */ + pin = find_isa_irq_pin(0, mp_ExtINT); + if (pin != -1) { + struct IO_APIC_route_entry entry; + unsigned long flags; + + memset(&entry, 0, sizeof(entry)); + entry.mask = 0; /* Enabled */ + entry.trigger = 0; /* Edge */ + entry.irr = 0; + entry.polarity = 0; /* High */ + entry.delivery_status = 0; + entry.dest_mode = 0; /* Physical */ + entry.delivery_mode = 7; /* ExtInt */ + entry.vector = 0; + entry.dest.physical.physical_dest = 0; + + + /* + * Add it to the IO-APIC irq-routing table: + */ + spin_lock_irqsave(&ioapic_lock, flags); + io_apic_write(0, 0x11+2*pin, *(((int *)&entry)+1)); + io_apic_write(0, 0x10+2*pin, *(((int *)&entry)+0)); + spin_unlock_irqrestore(&ioapic_lock, flags); + } disconnect_bsp_APIC(); } @@ -1739,7 +1768,7 @@ static void __init setup_ioapic_ids_from reg_00.raw = io_apic_read(apic, 0); spin_unlock_irqrestore(&ioapic_lock, flags); if (reg_00.bits.ID != mp_ioapics[apic].mpc_apicid) - panic("could not set ID!\n"); + printk("could not set ID!\n"); else apic_printk(APIC_VERBOSE, " ok.\n"); } @@ -2384,7 +2413,7 @@ device_initcall(ioapic_init_sysfs); ACPI-based IOAPIC Configuration -------------------------------------------------------------------------- */ -#ifdef CONFIG_ACPI_BOOT +#ifdef CONFIG_ACPI int __init io_apic_get_unique_id (int ioapic, int apic_id) { @@ -2524,15 +2553,7 @@ int io_apic_set_pci_routing (int ioapic, mp_ioapics[ioapic].mpc_apicid, pin, entry.vector, irq, edge_level, active_high_low); - if (use_pci_vector() && !platform_legacy_irq(irq)) - irq = IO_APIC_VECTOR(irq); - if (edge_level) { - irq_desc[irq].handler = &ioapic_level_type; - } else { - irq_desc[irq].handler = &ioapic_edge_type; - } - - set_intr_gate(entry.vector, interrupt[irq]); + ioapic_register_intr(irq, entry.vector, edge_level); if (!ioapic && (irq < 16)) disable_8259A_irq(irq); @@ -2545,4 +2566,4 @@ int io_apic_set_pci_routing (int ioapic, return 0; } -#endif /*CONFIG_ACPI_BOOT*/ +#endif /* CONFIG_ACPI */ --- linux-2.6.9-rc1/arch/i386/kernel/irq.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/arch/i386/kernel/irq.c 2004-09-03 00:56:52.580167248 -0700 @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include #include @@ -149,9 +151,8 @@ int show_interrupts(struct seq_file *p, if (i == 0) { seq_printf(p, " "); - for (j=0; jtypename); seq_printf(p, " %s", action->name); @@ -179,15 +179,13 @@ skip: spin_unlock_irqrestore(&irq_desc[i].lock, flags); } else if (i == NR_IRQS) { seq_printf(p, "NMI: "); - for (j = 0; j < NR_CPUS; j++) - if (cpu_online(j)) - seq_printf(p, "%10u ", nmi_count(j)); + for_each_cpu(j) + seq_printf(p, "%10u ", nmi_count(j)); seq_putc(p, '\n'); #ifdef CONFIG_X86_LOCAL_APIC seq_printf(p, "LOC: "); - for (j = 0; j < NR_CPUS; j++) - if (cpu_online(j)) - seq_printf(p, "%10u ", irq_stat[j].apic_timer_irqs); + for_each_cpu(j) + seq_printf(p, "%10u ", irq_stat[j].apic_timer_irqs); seq_putc(p, '\n'); #endif seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count)); @@ -220,14 +218,16 @@ asmlinkage int handle_IRQ_event(unsigned struct pt_regs *regs, struct irqaction *action) { int status = 1; /* Force the "do bottom halves" bit */ - int retval = 0; + int ret, retval = 0; if (!(action->flags & SA_INTERRUPT)) local_irq_enable(); do { - status |= action->flags; - retval |= action->handler(irq, action->dev_id, regs); + ret = action->handler(irq, action->dev_id, regs); + if (ret == IRQ_HANDLED) + status |= action->flags; + retval |= ret; action = action->next; } while (action); if (status & SA_SAMPLE_RANDOM) @@ -408,6 +408,20 @@ void enable_irq(unsigned int irq) spin_unlock_irqrestore(&desc->lock, flags); } +#ifdef CONFIG_HOTPLUG_CPU +static int irqs_stabilizing; + +#define irq_is_cpu_offline() do { \ + if (cpu_is_offline(smp_processor_id()) \ + && (system_state == SYSTEM_RUNNING) \ + && !irqs_stabilizing) \ + printk(KERN_ERR "IRQ %i on offline %i\n", \ + irq, smp_processor_id()); \ +} while (0) +#else +#define irq_is_cpu_offline() do {} while (0) +#endif + /* * do_IRQ handles all normal device IRQ's (the special * SMP cross-CPU interrupts have their own specific @@ -431,6 +445,7 @@ asmlinkage unsigned int do_IRQ(struct pt unsigned int status; irq_enter(); + irq_is_cpu_offline(); #ifdef CONFIG_DEBUG_STACKOVERFLOW /* Debugging check for stack overflow: is there less than 1KB free? */ @@ -569,6 +584,8 @@ out: irq_exit(); + kgdb_process_breakpoint(); + return 1; } @@ -1023,34 +1040,52 @@ static int irq_affinity_write_proc(struc return full_count; } - #endif -static int prof_cpu_mask_read_proc (char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - int len = cpumask_scnprintf(page, count, *(cpumask_t *)data); - if (count - len < 2) - return -EINVAL; - len += sprintf(page + len, "\n"); - return len; -} +#ifdef CONFIG_HOTPLUG_CPU +#include -static int prof_cpu_mask_write_proc (struct file *file, const char __user *buffer, - unsigned long count, void *data) +void fixup_irqs(void) { - cpumask_t *mask = (cpumask_t *)data; - unsigned long full_count = count, err; - cpumask_t new_value; - - err = cpumask_parse(buffer, count, new_value); - if (err) - return err; + unsigned int irq; + static int warned; - *mask = new_value; - return full_count; + for (irq = 0; irq < NR_IRQS; irq++) { + cpumask_t mask; + if (irq == 2) + continue; + + cpus_and(mask, irq_affinity[irq], cpu_online_map); + if (any_online_cpu(mask) == NR_CPUS) { + printk("Breaking affinity for irq %i\n", irq); + mask = cpu_online_map; + } + if (irq_desc[irq].handler->set_affinity) + irq_desc[irq].handler->set_affinity(irq, mask); + else if (irq_desc[irq].action && !(warned++)) + printk("Cannot set affinity for irq %i\n", irq); + } + +#if 0 + irqs_stabilizing = 1; + barrier(); + /* Ingo Molnar says: "after the IO-APIC masks have been redirected + [note the nop - the interrupt-enable boundary on x86 is two + instructions from sti] - to flush out pending hardirqs and + IPIs. After this point nothing is supposed to reach this CPU." */ + __asm__ __volatile__("sti; nop; cli"); + barrier(); + irqs_stabilizing = 0; +#else + /* That doesn't seem sufficient. Give it 0.02ms. */ + irqs_stabilizing = 1; + local_irq_enable(); + udelay(20); + local_irq_disable(); + irqs_stabilizing = 0; +#endif } - +#endif #define MAX_NAMELEN 10 static void register_irq_proc (unsigned int irq) @@ -1086,27 +1121,13 @@ static void register_irq_proc (unsigned #endif } -unsigned long prof_cpu_mask = -1; - void init_irq_proc (void) { - struct proc_dir_entry *entry; int i; /* create /proc/irq */ root_irq_dir = proc_mkdir("irq", NULL); - - /* create /proc/irq/prof_cpu_mask */ - entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); - - if (!entry) - return; - - entry->nlink = 1; - entry->data = (void *)&prof_cpu_mask; - entry->read_proc = prof_cpu_mask_read_proc; - entry->write_proc = prof_cpu_mask_write_proc; - + create_prof_cpu_mask(root_irq_dir); /* * Create entries for all existing IRQs. */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/i386/kernel/kgdb_stub.c 2004-09-03 00:56:36.521608520 -0700 @@ -0,0 +1,2454 @@ +/* + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +/* + * Copyright (c) 2000 VERITAS Software Corporation. + * + */ +/**************************************************************************** + * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ + * + * Module name: remcom.c $ + * Revision: 1.34 $ + * Date: 91/03/09 12:29:49 $ + * Contributor: Lake Stevens Instrument Division$ + * + * Description: low level support for gdb debugger. $ + * + * Considerations: only works on target hardware $ + * + * Written by: Glenn Engel $ + * Updated by: David Grothe + * Updated by: Robert Walsh + * Updated by: wangdi + * ModuleState: Experimental $ + * + * NOTES: See Below $ + * + * Modified for 386 by Jim Kingdon, Cygnus Support. + * Compatibility with 2.1.xx kernel by David Grothe + * + * Changes to allow auto initilization. All that is needed is that it + * be linked with the kernel and a break point (int 3) be executed. + * The header file defines BREAKPOINT to allow one to do + * this. It should also be possible, once the interrupt system is up, to + * call putDebugChar("+"). Once this is done, the remote debugger should + * get our attention by sending a ^C in a packet. George Anzinger + * + * Integrated into 2.2.5 kernel by Tigran Aivazian + * Added thread support, support for multiple processors, + * support for ia-32(x86) hardware debugging. + * Amit S. Kale ( akale@veritas.com ) + * + * Modified to support debugging over ethernet by Robert Walsh + * and wangdi , based on + * code by San Mehat. + * + * + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing an int 3. + * + ************* + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $#. + * + * where + * :: + * :: < two hex digits computed as modulo 256 sum of > + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + ****************************************************************************/ +#define KGDB_VERSION "<20030915.1651.33>" +#include +#include +#include /* for strcpy */ +#include +#include +#include +#include +#include /* for linux pt_regs struct */ +#include +#include +#include +#include +#include +#include +#include +#include + +/************************************************************************ + * + * external low-level support routines + */ +typedef void (*Function) (void); /* pointer to a function */ + +/* Thread reference */ +typedef unsigned char threadref[8]; + +extern int tty_putDebugChar(int); /* write a single character */ +extern int tty_getDebugChar(void); /* read and return a single char */ +extern void tty_flushDebugChar(void); /* flush pending characters */ +extern int eth_putDebugChar(int); /* write a single character */ +extern int eth_getDebugChar(void); /* read and return a single char */ +extern void eth_flushDebugChar(void); /* flush pending characters */ + +/************************************************************************/ +/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/ +/* at least NUMREGBYTES*2 are needed for register packets */ +/* Longer buffer is needed to list all threads */ +#define BUFMAX 400 + +char *kgdb_version = KGDB_VERSION; + +/* debug > 0 prints ill-formed commands in valid packets & checksum errors */ +int debug_regs = 0; /* set to non-zero to print registers */ + +/* filled in by an external module */ +char *gdb_module_offsets; + +static const char hexchars[] = "0123456789abcdef"; + +/* Number of bytes of registers. */ +#define NUMREGBYTES 64 +/* + * Note that this register image is in a different order than + * the register image that Linux produces at interrupt time. + * + * Linux's register image is defined by struct pt_regs in ptrace.h. + * Just why GDB uses a different order is a historical mystery. + */ +enum regnames { _EAX, /* 0 */ + _ECX, /* 1 */ + _EDX, /* 2 */ + _EBX, /* 3 */ + _ESP, /* 4 */ + _EBP, /* 5 */ + _ESI, /* 6 */ + _EDI, /* 7 */ + _PC /* 8 also known as eip */ , + _PS /* 9 also known as eflags */ , + _CS, /* 10 */ + _SS, /* 11 */ + _DS, /* 12 */ + _ES, /* 13 */ + _FS, /* 14 */ + _GS /* 15 */ +}; + +/*************************** ASSEMBLY CODE MACROS *************************/ +/* + * Put the error code here just in case the user cares. + * Likewise, the vector number here (since GDB only gets the signal + * number through the usual means, and that's not very specific). + * The called_from is the return address so he can tell how we entered kgdb. + * This will allow him to seperate out the various possible entries. + */ +#define REMOTE_DEBUG 0 /* set != to turn on printing (also available in info) */ + +#define PID_MAX PID_MAX_DEFAULT + +#ifdef CONFIG_SMP +void smp_send_nmi_allbutself(void); +#define IF_SMP(x) x +#undef MAX_NO_CPUS +#ifndef CONFIG_NO_KGDB_CPUS +#define CONFIG_NO_KGDB_CPUS 2 +#endif +#if CONFIG_NO_KGDB_CPUS > NR_CPUS +#define MAX_NO_CPUS NR_CPUS +#else +#define MAX_NO_CPUS CONFIG_NO_KGDB_CPUS +#endif +#define hold_init hold_on_sstep: 1, +#define MAX_CPU_MASK (unsigned long)((1LL << MAX_NO_CPUS) - 1LL) +#define NUM_CPUS num_online_cpus() +#else +#define IF_SMP(x) +#define hold_init +#undef MAX_NO_CPUS +#define MAX_NO_CPUS 1 +#define NUM_CPUS 1 +#endif +#define NOCPU (struct task_struct *)0xbad1fbad +/* *INDENT-OFF* */ +struct kgdb_info { + int used_malloc; + void *called_from; + long long entry_tsc; + int errcode; + int vector; + int print_debug_info; +#ifdef CONFIG_SMP + int hold_on_sstep; + struct { + volatile struct task_struct *task; + int pid; + int hold; + struct pt_regs *regs; + } cpus_waiting[MAX_NO_CPUS]; +#endif +} kgdb_info = {hold_init print_debug_info:REMOTE_DEBUG, vector:-1}; + +/* *INDENT-ON* */ + +#define used_m kgdb_info.used_malloc +/* + * This is little area we set aside to contain the stack we + * need to build to allow gdb to call functions. We use one + * per cpu to avoid locking issues. We will do all this work + * with interrupts off so that should take care of the protection + * issues. + */ +#define LOOKASIDE_SIZE 200 /* should be more than enough */ +#define MALLOC_MAX 200 /* Max malloc size */ +struct { + unsigned int esp; + int array[LOOKASIDE_SIZE]; +} fn_call_lookaside[MAX_NO_CPUS]; + +static int trap_cpu; +static unsigned int OLD_esp; + +#define END_OF_LOOKASIDE &fn_call_lookaside[trap_cpu].array[LOOKASIDE_SIZE] +#define IF_BIT 0x200 +#define TF_BIT 0x100 + +#define MALLOC_ROUND 8-1 + +static char malloc_array[MALLOC_MAX]; +IF_SMP(static void to_gdb(const char *mess)); +void * +malloc(int size) +{ + + if (size <= (MALLOC_MAX - used_m)) { + int old_used = used_m; + used_m += ((size + MALLOC_ROUND) & (~MALLOC_ROUND)); + return &malloc_array[old_used]; + } else { + return NULL; + } +} + +/* + * I/O dispatch functions... + * Based upon kgdboe, either call the ethernet + * handler or the serial one.. + */ +void +putDebugChar(int c) +{ + if (!kgdboe) { + tty_putDebugChar(c); + } else { + eth_putDebugChar(c); + } +} + +int +getDebugChar(void) +{ + if (!kgdboe) { + return tty_getDebugChar(); + } else { + return eth_getDebugChar(); + } +} + +void +flushDebugChar(void) +{ + if (!kgdboe) { + tty_flushDebugChar(); + } else { + eth_flushDebugChar(); + } +} + +/* + * Gdb calls functions by pushing agruments, including a return address + * on the stack and the adjusting EIP to point to the function. The + * whole assumption in GDB is that we are on a different stack than the + * one the "user" i.e. code that hit the break point, is on. This, of + * course is not true in the kernel. Thus various dodges are needed to + * do the call without directly messing with EIP (which we can not change + * as it is just a location and not a register. To adjust it would then + * require that we move every thing below EIP up or down as needed. This + * will not work as we may well have stack relative pointer on the stack + * (such as the pointer to regs, for example). + + * So here is what we do: + * We detect gdb attempting to store into the stack area and instead, store + * into the fn_call_lookaside.array at the same relative location as if it + * were the area ESP pointed at. We also trap ESP modifications + * and uses these to adjust fn_call_lookaside.esp. On entry + * fn_call_lookaside.esp will be set to point at the last entry in + * fn_call_lookaside.array. This allows us to check if it has changed, and + * if so, on exit, we add the registers we will use to do the move and a + * trap/ interrupt return exit sequence. We then adjust the eflags in the + * regs array (remember we now have a copy in the fn_call_lookaside.array) to + * kill the interrupt bit, AND we change EIP to point at our set up stub. + * As part of the register set up we preset the registers to point at the + * begining and end of the fn_call_lookaside.array, so all the stub needs to + * do is move words from the array to the stack until ESP= the desired value + * then do the rti. This will then transfer to the desired function with + * all the correct registers. Nifty huh? + */ +extern asmlinkage void fn_call_stub(void); +extern asmlinkage void fn_rtn_stub(void); +/* *INDENT-OFF* */ +__asm__("fn_rtn_stub:\n\t" + "movl %eax,%esp\n\t" + "fn_call_stub:\n\t" + "1:\n\t" + "addl $-4,%ebx\n\t" + "movl (%ebx), %eax\n\t" + "pushl %eax\n\t" + "cmpl %esp,%ecx\n\t" + "jne 1b\n\t" + "popl %eax\n\t" + "popl %ebx\n\t" + "popl %ecx\n\t" + "iret \n\t"); +/* *INDENT-ON* */ +#define gdb_i386vector kgdb_info.vector +#define gdb_i386errcode kgdb_info.errcode +#define waiting_cpus kgdb_info.cpus_waiting +#define remote_debug kgdb_info.print_debug_info +#define hold_cpu(cpu) kgdb_info.cpus_waiting[cpu].hold +/* gdb locks */ + +#ifdef CONFIG_SMP +static int in_kgdb_called; +static spinlock_t waitlocks[MAX_NO_CPUS] = + {[0 ... MAX_NO_CPUS - 1] = SPIN_LOCK_UNLOCKED }; +/* + * The following array has the thread pointer of each of the "other" + * cpus. We make it global so it can be seen by gdb. + */ +volatile int in_kgdb_entry_log[MAX_NO_CPUS]; +volatile struct pt_regs *in_kgdb_here_log[MAX_NO_CPUS]; +/* +static spinlock_t continuelocks[MAX_NO_CPUS]; +*/ +spinlock_t kgdb_spinlock = SPIN_LOCK_UNLOCKED; +/* waiters on our spinlock plus us */ +static atomic_t spinlock_waiters = ATOMIC_INIT(1); +static int spinlock_count = 0; +static int spinlock_cpu = 0; +/* + * Note we use nested spin locks to account for the case where a break + * point is encountered when calling a function by user direction from + * kgdb. Also there is the memory exception recursion to account for. + * Well, yes, but this lets other cpus thru too. Lets add a + * cpu id to the lock. + */ +#define KGDB_SPIN_LOCK(x) if( spinlock_count == 0 || \ + spinlock_cpu != smp_processor_id()){\ + atomic_inc(&spinlock_waiters); \ + while (! spin_trylock(x)) {\ + in_kgdb(®s);\ + }\ + atomic_dec(&spinlock_waiters); \ + spinlock_count = 1; \ + spinlock_cpu = smp_processor_id(); \ + }else{ \ + spinlock_count++; \ + } +#define KGDB_SPIN_UNLOCK(x) if( --spinlock_count == 0) spin_unlock(x) +#else +unsigned kgdb_spinlock = 0; +#define KGDB_SPIN_LOCK(x) --*x +#define KGDB_SPIN_UNLOCK(x) ++*x +#endif + +int +hex(char ch) +{ + if ((ch >= 'a') && (ch <= 'f')) + return (ch - 'a' + 10); + if ((ch >= '0') && (ch <= '9')) + return (ch - '0'); + if ((ch >= 'A') && (ch <= 'F')) + return (ch - 'A' + 10); + return (-1); +} + +/* scan for the sequence $# */ +void +getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + char ch; + + do { + /* wait around for the start character, ignore all other characters */ + while ((ch = (getDebugChar() & 0x7f)) != '$') ; + checksum = 0; + xmitcsum = -1; + + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX) { + ch = getDebugChar() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum += hex(getDebugChar() & 0x7f); + if ((remote_debug) && (checksum != xmitcsum)) { + printk + ("bad checksum. My count = 0x%x, sent=0x%x. buf=%s\n", + checksum, xmitcsum, buffer); + } + + if (checksum != xmitcsum) + putDebugChar('-'); /* failed checksum */ + else { + putDebugChar('+'); /* successful transfer */ + /* if a sequence char is present, reply the sequence ID */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + /* remove sequence chars from buffer */ + count = strlen(buffer); + for (i = 3; i <= count; i++) + buffer[i - 3] = buffer[i]; + } + } + } + } while (checksum != xmitcsum); + + if (remote_debug) + printk("R:%s\n", buffer); + flushDebugChar(); +} + +/* send the packet in buffer. */ + +void +putpacket(char *buffer) +{ + unsigned char checksum; + int count; + char ch; + + /* $#. */ + + if (!kgdboe) { + do { + if (remote_debug) + printk("T:%s\n", buffer); + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count])) { + putDebugChar(ch); + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum % 16]); + flushDebugChar(); + + } while ((getDebugChar() & 0x7f) != '+'); + } else { + /* + * For udp, we can not transfer too much bytes once. + * We only transfer MAX_SEND_COUNT size bytes each time + */ + +#define MAX_SEND_COUNT 30 + + int send_count = 0, i = 0; + char send_buf[MAX_SEND_COUNT]; + + do { + if (remote_debug) + printk("T:%s\n", buffer); + putDebugChar('$'); + checksum = 0; + count = 0; + send_count = 0; + while ((ch = buffer[count])) { + if (send_count >= MAX_SEND_COUNT) { + for(i = 0; i < MAX_SEND_COUNT; i++) { + putDebugChar(send_buf[i]); + } + flushDebugChar(); + send_count = 0; + } else { + send_buf[send_count] = ch; + checksum += ch; + count ++; + send_count++; + } + } + for(i = 0; i < send_count; i++) + putDebugChar(send_buf[i]); + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum % 16]); + flushDebugChar(); + } while ((getDebugChar() & 0x7f) != '+'); + } +} + +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; +static short error; + +void +debug_error(char *format, char *parm) +{ + if (remote_debug) + printk(format, parm); +} + +static void +print_regs(struct pt_regs *regs) +{ + printk("EAX=%08lx ", regs->eax); + printk("EBX=%08lx ", regs->ebx); + printk("ECX=%08lx ", regs->ecx); + printk("EDX=%08lx ", regs->edx); + printk("\n"); + printk("ESI=%08lx ", regs->esi); + printk("EDI=%08lx ", regs->edi); + printk("EBP=%08lx ", regs->ebp); + printk("ESP=%08lx ", (long) ®s->esp); + printk("\n"); + printk(" DS=%08x ", regs->xds); + printk(" ES=%08x ", regs->xes); + printk(" SS=%08x ", __KERNEL_DS); + printk(" FL=%08lx ", regs->eflags); + printk("\n"); + printk(" CS=%08x ", regs->xcs); + printk(" IP=%08lx ", regs->eip); +#if 0 + printk(" FS=%08x ", regs->fs); + printk(" GS=%08x ", regs->gs); +#endif + printk("\n"); + +} /* print_regs */ + +#define NEW_esp fn_call_lookaside[trap_cpu].esp + +static void +regs_to_gdb_regs(int *gdb_regs, struct pt_regs *regs) +{ + gdb_regs[_EAX] = regs->eax; + gdb_regs[_EBX] = regs->ebx; + gdb_regs[_ECX] = regs->ecx; + gdb_regs[_EDX] = regs->edx; + gdb_regs[_ESI] = regs->esi; + gdb_regs[_EDI] = regs->edi; + gdb_regs[_EBP] = regs->ebp; + gdb_regs[_DS] = regs->xds; + gdb_regs[_ES] = regs->xes; + gdb_regs[_PS] = regs->eflags; + gdb_regs[_CS] = regs->xcs; + gdb_regs[_PC] = regs->eip; + /* Note, as we are a debugging the kernel, we will always + * trap in kernel code, this means no priviledge change, + * and so the pt_regs structure is not completely valid. In a non + * privilege change trap, only EFLAGS, CS and EIP are put on the stack, + * SS and ESP are not stacked, this means that the last 2 elements of + * pt_regs is not valid (they would normally refer to the user stack) + * also, using regs+1 is no good because you end up will a value that is + * 2 longs (8) too high. This used to cause stepping over functions + * to fail, so my fix is to use the address of regs->esp, which + * should point at the end of the stack frame. Note I have ignored + * completely exceptions that cause an error code to be stacked, such + * as double fault. Stuart Hughes, Zentropix. + * original code: gdb_regs[_ESP] = (int) (regs + 1) ; + + * this is now done on entry and moved to OLD_esp (as well as NEW_esp). + */ + gdb_regs[_ESP] = NEW_esp; + gdb_regs[_SS] = __KERNEL_DS; + gdb_regs[_FS] = 0xFFFF; + gdb_regs[_GS] = 0xFFFF; +} /* regs_to_gdb_regs */ + +static void +gdb_regs_to_regs(int *gdb_regs, struct pt_regs *regs) +{ + regs->eax = gdb_regs[_EAX]; + regs->ebx = gdb_regs[_EBX]; + regs->ecx = gdb_regs[_ECX]; + regs->edx = gdb_regs[_EDX]; + regs->esi = gdb_regs[_ESI]; + regs->edi = gdb_regs[_EDI]; + regs->ebp = gdb_regs[_EBP]; + regs->xds = gdb_regs[_DS]; + regs->xes = gdb_regs[_ES]; + regs->eflags = gdb_regs[_PS]; + regs->xcs = gdb_regs[_CS]; + regs->eip = gdb_regs[_PC]; + NEW_esp = gdb_regs[_ESP]; /* keep the value */ +#if 0 /* can't change these */ + regs->esp = gdb_regs[_ESP]; + regs->xss = gdb_regs[_SS]; + regs->fs = gdb_regs[_FS]; + regs->gs = gdb_regs[_GS]; +#endif + +} /* gdb_regs_to_regs */ + +int thread_list = 0; + +void +get_gdb_regs(struct task_struct *p, struct pt_regs *regs, int *gdb_regs) +{ + unsigned long stack_page; + int count = 0; + IF_SMP(int i); + if (!p || p == current) { + regs_to_gdb_regs(gdb_regs, regs); + return; + } +#ifdef CONFIG_SMP + for (i = 0; i < MAX_NO_CPUS; i++) { + if (p == kgdb_info.cpus_waiting[i].task) { + regs_to_gdb_regs(gdb_regs, + kgdb_info.cpus_waiting[i].regs); + gdb_regs[_ESP] = + (int) &kgdb_info.cpus_waiting[i].regs->esp; + + return; + } + } +#endif + memset(gdb_regs, 0, NUMREGBYTES); + gdb_regs[_ESP] = p->thread.esp; + gdb_regs[_PC] = p->thread.eip; + gdb_regs[_EBP] = *(int *) gdb_regs[_ESP]; + gdb_regs[_EDI] = *(int *) (gdb_regs[_ESP] + 4); + gdb_regs[_ESI] = *(int *) (gdb_regs[_ESP] + 8); + +/* + * This code is to give a more informative notion of where a process + * is waiting. It is used only when the user asks for a thread info + * list. If he then switches to the thread, s/he will find the task + * is in schedule, but a back trace should show the same info we come + * up with. This code was shamelessly purloined from process.c. It was + * then enhanced to provide more registers than simply the program + * counter. + */ + + if (!thread_list) { + return; + } + + if (p->state == TASK_RUNNING) + return; + stack_page = (unsigned long) p->thread_info; + if (gdb_regs[_ESP] < stack_page || gdb_regs[_ESP] > + THREAD_SIZE - sizeof(long) + stack_page) + return; + /* include/asm-i386/system.h:switch_to() pushes ebp last. */ + do { + if (gdb_regs[_EBP] < stack_page || + gdb_regs[_EBP] > THREAD_SIZE - 2*sizeof(long) + stack_page) + return; + gdb_regs[_PC] = *(unsigned long *) (gdb_regs[_EBP] + 4); + gdb_regs[_ESP] = gdb_regs[_EBP] + 8; + gdb_regs[_EBP] = *(unsigned long *) gdb_regs[_EBP]; + if (!in_sched_functions(gdb_regs[_PC])) + return; + } while (count++ < 16); + return; +} + +/* Indicate to caller of mem2hex or hex2mem that there has been an + error. */ +static volatile int mem_err = 0; +static volatile int mem_err_expected = 0; +static volatile int mem_err_cnt = 0; +static int garbage_loc = -1; + +int +get_char(char *addr) +{ + return *addr; +} + +void +set_char(char *addr, int val, int may_fault) +{ + /* + * This code traps references to the area mapped to the kernel + * stack as given by the regs and, instead, stores to the + * fn_call_lookaside[cpu].array + */ + if (may_fault && + (unsigned int) addr < OLD_esp && + ((unsigned int) addr > (OLD_esp - (unsigned int) LOOKASIDE_SIZE))) { + addr = (char *) END_OF_LOOKASIDE - ((char *) OLD_esp - addr); + } + *addr = val; +} + +/* convert the memory pointed to by mem into hex, placing result in buf */ +/* return a pointer to the last char put in buf (null) */ +/* If MAY_FAULT is non-zero, then we should set mem_err in response to + a fault; if zero treat a fault like any other fault in the stub. */ +char * +mem2hex(char *mem, char *buf, int count, int may_fault) +{ + int i; + unsigned char ch; + + if (may_fault) { + mem_err_expected = 1; + mem_err = 0; + } + for (i = 0; i < count; i++) { + /* printk("%lx = ", mem) ; */ + + ch = get_char(mem++); + + /* printk("%02x\n", ch & 0xFF) ; */ + if (may_fault && mem_err) { + if (remote_debug) + printk("Mem fault fetching from addr %lx\n", + (long) (mem - 1)); + *buf = 0; /* truncate buffer */ + return (buf); + } + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch % 16]; + } + *buf = 0; + if (may_fault) + mem_err_expected = 0; + return (buf); +} + +/* convert the hex array pointed to by buf into binary to be placed in mem */ +/* return a pointer to the character AFTER the last byte written */ +/* NOTE: We use the may fault flag to also indicate if the write is to + * the registers (0) or "other" memory (!=0) + */ +char * +hex2mem(char *buf, char *mem, int count, int may_fault) +{ + int i; + unsigned char ch; + + if (may_fault) { + mem_err_expected = 1; + mem_err = 0; + } + for (i = 0; i < count; i++) { + ch = hex(*buf++) << 4; + ch = ch + hex(*buf++); + set_char(mem++, ch, may_fault); + + if (may_fault && mem_err) { + if (remote_debug) + printk("Mem fault storing to addr %lx\n", + (long) (mem - 1)); + return (mem); + } + } + if (may_fault) + mem_err_expected = 0; + return (mem); +} + +/**********************************************/ +/* WHILE WE FIND NICE HEX CHARS, BUILD AN INT */ +/* RETURN NUMBER OF CHARS PROCESSED */ +/**********************************************/ +int +hexToInt(char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + while (**ptr) { + hexValue = hex(**ptr); + if (hexValue >= 0) { + *intValue = (*intValue << 4) | hexValue; + numChars++; + } else + break; + + (*ptr)++; + } + + return (numChars); +} + +#define stubhex(h) hex(h) +#ifdef old_thread_list + +static int +stub_unpack_int(char *buff, int fieldlength) +{ + int nibble; + int retval = 0; + + while (fieldlength) { + nibble = stubhex(*buff++); + retval |= nibble; + fieldlength--; + if (fieldlength) + retval = retval << 4; + } + return retval; +} +#endif +static char * +pack_hex_byte(char *pkt, int byte) +{ + *pkt++ = hexchars[(byte >> 4) & 0xf]; + *pkt++ = hexchars[(byte & 0xf)]; + return pkt; +} + +#define BUF_THREAD_ID_SIZE 16 + +static char * +pack_threadid(char *pkt, threadref * id) +{ + char *limit; + unsigned char *altid; + + altid = (unsigned char *) id; + limit = pkt + BUF_THREAD_ID_SIZE; + while (pkt < limit) + pkt = pack_hex_byte(pkt, *altid++); + return pkt; +} + +#ifdef old_thread_list +static char * +unpack_byte(char *buf, int *value) +{ + *value = stub_unpack_int(buf, 2); + return buf + 2; +} + +static char * +unpack_threadid(char *inbuf, threadref * id) +{ + char *altref; + char *limit = inbuf + BUF_THREAD_ID_SIZE; + int x, y; + + altref = (char *) id; + + while (inbuf < limit) { + x = stubhex(*inbuf++); + y = stubhex(*inbuf++); + *altref++ = (x << 4) | y; + } + return inbuf; +} +#endif +void +int_to_threadref(threadref * id, int value) +{ + unsigned char *scan; + + scan = (unsigned char *) id; + { + int i = 4; + while (i--) + *scan++ = 0; + } + *scan++ = (value >> 24) & 0xff; + *scan++ = (value >> 16) & 0xff; + *scan++ = (value >> 8) & 0xff; + *scan++ = (value & 0xff); +} +int +int_to_hex_v(unsigned char * id, int value) +{ + unsigned char *start = id; + int shift; + int ch; + + for (shift = 28; shift >= 0; shift -= 4) { + if ((ch = (value >> shift) & 0xf) || (id != start)) { + *id = hexchars[ch]; + id++; + } + } + if (id == start) + *id++ = '0'; + return id - start; +} +#ifdef old_thread_list + +static int +threadref_to_int(threadref * ref) +{ + int i, value = 0; + unsigned char *scan; + + scan = (char *) ref; + scan += 4; + i = 4; + while (i-- > 0) + value = (value << 8) | ((*scan++) & 0xff); + return value; +} +#endif +static int +cmp_str(char *s1, char *s2, int count) +{ + while (count--) { + if (*s1++ != *s2++) + return 0; + } + return 1; +} + +#if 1 /* this is a hold over from 2.4 where O(1) was "sometimes" */ +extern struct task_struct *kgdb_get_idle(int cpu); +#define idle_task(cpu) kgdb_get_idle(cpu) +#else +#define idle_task(cpu) init_tasks[cpu] +#endif + +extern int kgdb_pid_init_done; + +struct task_struct * +getthread(int pid) +{ + struct task_struct *thread; + if (pid >= PID_MAX && pid <= (PID_MAX + MAX_NO_CPUS)) { + + return idle_task(pid - PID_MAX); + } else { + /* + * find_task_by_pid is relatively safe all the time + * Other pid functions require lock downs which imply + * that we may be interrupting them (as we get here + * in the middle of most any lock down). + * Still we don't want to call until the table exists! + */ + if (kgdb_pid_init_done){ + thread = find_task_by_pid(pid); + if (thread) { + return thread; + } + } + } + return NULL; +} +/* *INDENT-OFF* */ +struct hw_breakpoint { + unsigned enabled; + unsigned type; + unsigned len; + unsigned addr; +} breakinfo[4] = { {enabled:0}, + {enabled:0}, + {enabled:0}, + {enabled:0}}; +/* *INDENT-ON* */ +unsigned hw_breakpoint_status; +void +correct_hw_break(void) +{ + int breakno; + int correctit; + int breakbit; + unsigned dr7; + + asm volatile ("movl %%db7, %0\n":"=r" (dr7) + :); + /* *INDENT-OFF* */ + do { + unsigned addr0, addr1, addr2, addr3; + asm volatile ("movl %%db0, %0\n" + "movl %%db1, %1\n" + "movl %%db2, %2\n" + "movl %%db3, %3\n" + :"=r" (addr0), "=r"(addr1), + "=r"(addr2), "=r"(addr3) + :); + } while (0); + /* *INDENT-ON* */ + correctit = 0; + for (breakno = 0; breakno < 3; breakno++) { + breakbit = 2 << (breakno << 1); + if (!(dr7 & breakbit) && breakinfo[breakno].enabled) { + correctit = 1; + dr7 |= breakbit; + dr7 &= ~(0xf0000 << (breakno << 2)); + dr7 |= (((breakinfo[breakno].len << 2) | + breakinfo[breakno].type) << 16) << + (breakno << 2); + switch (breakno) { + case 0: + asm volatile ("movl %0, %%dr0\n"::"r" + (breakinfo[breakno].addr)); + break; + + case 1: + asm volatile ("movl %0, %%dr1\n"::"r" + (breakinfo[breakno].addr)); + break; + + case 2: + asm volatile ("movl %0, %%dr2\n"::"r" + (breakinfo[breakno].addr)); + break; + + case 3: + asm volatile ("movl %0, %%dr3\n"::"r" + (breakinfo[breakno].addr)); + break; + } + } else if ((dr7 & breakbit) && !breakinfo[breakno].enabled) { + correctit = 1; + dr7 &= ~breakbit; + dr7 &= ~(0xf0000 << (breakno << 2)); + } + } + if (correctit) { + asm volatile ("movl %0, %%db7\n"::"r" (dr7)); + } +} + +int +remove_hw_break(unsigned breakno) +{ + if (!breakinfo[breakno].enabled) { + return -1; + } + breakinfo[breakno].enabled = 0; + return 0; +} + +int +set_hw_break(unsigned breakno, unsigned type, unsigned len, unsigned addr) +{ + if (breakinfo[breakno].enabled) { + return -1; + } + breakinfo[breakno].enabled = 1; + breakinfo[breakno].type = type; + breakinfo[breakno].len = len; + breakinfo[breakno].addr = addr; + return 0; +} + +#ifdef CONFIG_SMP +static int in_kgdb_console = 0; + +int +in_kgdb(struct pt_regs *regs) +{ + unsigned flags; + int cpu = smp_processor_id(); + in_kgdb_called = 1; + if (!spin_is_locked(&kgdb_spinlock)) { + if (in_kgdb_here_log[cpu] || /* we are holding this cpu */ + in_kgdb_console) { /* or we are doing slow i/o */ + return 1; + } + return 0; + } + + /* As I see it the only reason not to let all cpus spin on + * the same spin_lock is to allow selected ones to proceed. + * This would be a good thing, so we leave it this way. + * Maybe someday.... Done ! + + * in_kgdb() is called from an NMI so we don't pretend + * to have any resources, like printk() for example. + */ + + kgdb_local_irq_save(flags); /* only local here, to avoid hanging */ + /* + * log arival of this cpu + * The NMI keeps on ticking. Protect against recurring more + * than once, and ignor the cpu that has the kgdb lock + */ + in_kgdb_entry_log[cpu]++; + in_kgdb_here_log[cpu] = regs; + if (cpu == spinlock_cpu || waiting_cpus[cpu].task) + goto exit_in_kgdb; + + /* + * For protection of the initilization of the spin locks by kgdb + * it locks the kgdb spinlock before it gets the wait locks set + * up. We wait here for the wait lock to be taken. If the + * kgdb lock goes away first?? Well, it could be a slow exit + * sequence where the wait lock is removed prior to the kgdb lock + * so if kgdb gets unlocked, we just exit. + */ + + while (spin_is_locked(&kgdb_spinlock) && + !spin_is_locked(waitlocks + cpu)) ; + if (!spin_is_locked(&kgdb_spinlock)) + goto exit_in_kgdb; + + waiting_cpus[cpu].task = current; + waiting_cpus[cpu].pid = (current->pid) ? : (PID_MAX + cpu); + waiting_cpus[cpu].regs = regs; + + spin_unlock_wait(waitlocks + cpu); + + /* + * log departure of this cpu + */ + waiting_cpus[cpu].task = 0; + waiting_cpus[cpu].pid = 0; + waiting_cpus[cpu].regs = 0; + correct_hw_break(); + exit_in_kgdb: + in_kgdb_here_log[cpu] = 0; + kgdb_local_irq_restore(flags); + return 1; + /* + spin_unlock(continuelocks + smp_processor_id()); + */ +} + +void +smp__in_kgdb(struct pt_regs regs) +{ + ack_APIC_irq(); + in_kgdb(®s); +} +#else +int +in_kgdb(struct pt_regs *regs) +{ + return (kgdb_spinlock); +} +#endif + +void +printexceptioninfo(int exceptionNo, int errorcode, char *buffer) +{ + unsigned dr6; + int i; + switch (exceptionNo) { + case 1: /* debug exception */ + break; + case 3: /* breakpoint */ + sprintf(buffer, "Software breakpoint"); + return; + default: + sprintf(buffer, "Details not available"); + return; + } + asm volatile ("movl %%db6, %0\n":"=r" (dr6) + :); + if (dr6 & 0x4000) { + sprintf(buffer, "Single step"); + return; + } + for (i = 0; i < 4; ++i) { + if (dr6 & (1 << i)) { + sprintf(buffer, "Hardware breakpoint %d", i); + return; + } + } + sprintf(buffer, "Unknown trap"); + return; +} + +/* + * This function does all command procesing for interfacing to gdb. + * + * NOTE: The INT nn instruction leaves the state of the interrupt + * enable flag UNCHANGED. That means that when this routine + * is entered via a breakpoint (INT 3) instruction from code + * that has interrupts enabled, then interrupts will STILL BE + * enabled when this routine is entered. The first thing that + * we do here is disable interrupts so as to prevent recursive + * entries and bothersome serial interrupts while we are + * trying to run the serial port in polled mode. + * + * For kernel version 2.1.xx the kgdb_cli() actually gets a spin lock so + * it is always necessary to do a restore_flags before returning + * so as to let go of that lock. + */ +int +kgdb_handle_exception(int exceptionVector, + int signo, int err_code, struct pt_regs *linux_regs) +{ + struct task_struct *usethread = NULL; + struct task_struct *thread_list_start = 0, *thread = NULL; + int addr, length; + int breakno, breaktype; + char *ptr; + int newPC; + threadref thref; + int threadid; + int thread_min = PID_MAX + MAX_NO_CPUS; +#ifdef old_thread_list + int maxthreads; +#endif + int nothreads; + unsigned long flags; + int gdb_regs[NUMREGBYTES / 4]; + int dr6; + IF_SMP(int entry_state = 0); /* 0, ok, 1, no nmi, 2 sync failed */ +#define NO_NMI 1 +#define NO_SYNC 2 +#define regs (*linux_regs) +#define NUMREGS NUMREGBYTES/4 + /* + * If the entry is not from the kernel then return to the Linux + * trap handler and let it process the interrupt normally. + */ + if ((linux_regs->eflags & VM_MASK) || (3 & linux_regs->xcs)) { + printk("ignoring non-kernel exception\n"); + print_regs(®s); + return (0); + } + /* + * If we're using eth mode, set the 'mode' in the netdevice. + */ + + if (kgdboe) + netpoll_set_trap(1); + + kgdb_local_irq_save(flags); + + /* Get kgdb spinlock */ + + KGDB_SPIN_LOCK(&kgdb_spinlock); + rdtscll(kgdb_info.entry_tsc); + /* + * We depend on this spinlock and the NMI watch dog to control the + * other cpus. They will arrive at "in_kgdb()" as a result of the + * NMI and will wait there for the following spin locks to be + * released. + */ +#ifdef CONFIG_SMP + +#if 0 + if (cpu_callout_map & ~MAX_CPU_MASK) { + printk("kgdb : too many cpus, possibly not mapped" + " in contiguous space, change MAX_NO_CPUS" + " in kgdb_stub and make new kernel.\n" + " cpu_callout_map is %lx\n", cpu_callout_map); + goto exit_just_unlock; + } +#endif + if (spinlock_count == 1) { + int time = 0, end_time, dum = 0; + int i; + int cpu_logged_in[MAX_NO_CPUS] = {[0 ... MAX_NO_CPUS - 1] = (0) + }; + if (remote_debug) { + printk("kgdb : cpu %d entry, syncing others\n", + smp_processor_id()); + } + for (i = 0; i < MAX_NO_CPUS; i++) { + /* + * Use trylock as we may already hold the lock if + * we are holding the cpu. Net result is all + * locked. + */ + spin_trylock(&waitlocks[i]); + } + for (i = 0; i < MAX_NO_CPUS; i++) + cpu_logged_in[i] = 0; + /* + * Wait for their arrival. We know the watch dog is active if + * in_kgdb() has ever been called, as it is always called on a + * watchdog tick. + */ + rdtsc(dum, time); + end_time = time + 2; /* Note: we use the High order bits! */ + i = 1; + if (num_online_cpus() > 1) { + int me_in_kgdb = in_kgdb_entry_log[smp_processor_id()]; + smp_send_nmi_allbutself(); + + while (i < num_online_cpus() && time != end_time) { + int j; + for (j = 0; j < MAX_NO_CPUS; j++) { + if (waiting_cpus[j].task && + waiting_cpus[j].task != NOCPU && + !cpu_logged_in[j]) { + i++; + cpu_logged_in[j] = 1; + if (remote_debug) { + printk + ("kgdb : cpu %d arrived at kgdb\n", + j); + } + break; + } else if (!waiting_cpus[j].task && + !cpu_online(j)) { + waiting_cpus[j].task = NOCPU; + cpu_logged_in[j] = 1; + waiting_cpus[j].hold = 1; + break; + } + if (!waiting_cpus[j].task && + in_kgdb_here_log[j]) { + + int wait = 100000; + while (wait--) ; + if (!waiting_cpus[j].task && + in_kgdb_here_log[j]) { + printk + ("kgdb : cpu %d stall" + " in in_kgdb\n", + j); + i++; + cpu_logged_in[j] = 1; + waiting_cpus[j].task = + (struct task_struct + *) 1; + } + } + } + + if (in_kgdb_entry_log[smp_processor_id()] > + (me_in_kgdb + 10)) { + break; + } + + rdtsc(dum, time); + } + if (i < num_online_cpus()) { + printk + ("kgdb : time out, proceeding without sync\n"); +#if 0 + printk("kgdb : Waiting_cpus: 0 = %d, 1 = %d\n", + waiting_cpus[0].task != 0, + waiting_cpus[1].task != 0); + printk("kgdb : Cpu_logged in: 0 = %d, 1 = %d\n", + cpu_logged_in[0], cpu_logged_in[1]); + printk + ("kgdb : in_kgdb_here_log in: 0 = %d, 1 = %d\n", + in_kgdb_here_log[0] != 0, + in_kgdb_here_log[1] != 0); +#endif + entry_state = NO_SYNC; + } else { +#if 0 + int ent = + in_kgdb_entry_log[smp_processor_id()] - + me_in_kgdb; + printk("kgdb : sync after %d entries\n", ent); +#endif + } + } else { + if (remote_debug) { + printk + ("kgdb : %d cpus, but watchdog not active\n" + "proceeding without locking down other cpus\n", + num_online_cpus()); + entry_state = NO_NMI; + } + } + } +#endif + + if (remote_debug) { + unsigned long *lp = (unsigned long *) &linux_regs; + + printk("handle_exception(exceptionVector=%d, " + "signo=%d, err_code=%d, linux_regs=%p)\n", + exceptionVector, signo, err_code, linux_regs); + if (debug_regs) { + print_regs(®s); + printk("Stk: %8lx %8lx %8lx %8lx" + " %8lx %8lx %8lx %8lx\n", + lp[0], lp[1], lp[2], lp[3], + lp[4], lp[5], lp[6], lp[7]); + printk(" %8lx %8lx %8lx %8lx" + " %8lx %8lx %8lx %8lx\n", + lp[8], lp[9], lp[10], lp[11], + lp[12], lp[13], lp[14], lp[15]); + printk(" %8lx %8lx %8lx %8lx " + "%8lx %8lx %8lx %8lx\n", + lp[16], lp[17], lp[18], lp[19], + lp[20], lp[21], lp[22], lp[23]); + printk(" %8lx %8lx %8lx %8lx " + "%8lx %8lx %8lx %8lx\n", + lp[24], lp[25], lp[26], lp[27], + lp[28], lp[29], lp[30], lp[31]); + } + } + + /* Disable hardware debugging while we are in kgdb */ + /* Get the debug register status register */ +/* *INDENT-OFF* */ + __asm__("movl %0,%%db7" + : /* no output */ + :"r"(0)); + + asm volatile ("movl %%db6, %0\n" + :"=r" (hw_breakpoint_status) + :); + +/* *INDENT-ON* */ + switch (exceptionVector) { + case 0: /* divide error */ + case 1: /* debug exception */ + case 2: /* NMI */ + case 3: /* breakpoint */ + case 4: /* overflow */ + case 5: /* bounds check */ + case 6: /* invalid opcode */ + case 7: /* device not available */ + case 8: /* double fault (errcode) */ + case 10: /* invalid TSS (errcode) */ + case 12: /* stack fault (errcode) */ + case 16: /* floating point error */ + case 17: /* alignment check (errcode) */ + default: /* any undocumented */ + break; + case 11: /* segment not present (errcode) */ + case 13: /* general protection (errcode) */ + case 14: /* page fault (special errcode) */ + case 19: /* cache flush denied */ + if (mem_err_expected) { + /* + * This fault occured because of the + * get_char or set_char routines. These + * two routines use either eax of edx to + * indirectly reference the location in + * memory that they are working with. + * For a page fault, when we return the + * instruction will be retried, so we + * have to make sure that these + * registers point to valid memory. + */ + mem_err = 1; /* set mem error flag */ + mem_err_expected = 0; + mem_err_cnt++; /* helps in debugging */ + /* make valid address */ + regs.eax = (long) &garbage_loc; + /* make valid address */ + regs.edx = (long) &garbage_loc; + if (remote_debug) + printk("Return after memory error: " + "mem_err_cnt=%d\n", mem_err_cnt); + if (debug_regs) + print_regs(®s); + goto exit_kgdb; + } + break; + } + if (remote_debug) + printk("kgdb : entered kgdb on cpu %d\n", smp_processor_id()); + + gdb_i386vector = exceptionVector; + gdb_i386errcode = err_code; + kgdb_info.called_from = __builtin_return_address(0); +#ifdef CONFIG_SMP + /* + * OK, we can now communicate, lets tell gdb about the sync. + * but only if we had a problem. + */ + switch (entry_state) { + case NO_NMI: + to_gdb("NMI not active, other cpus not stopped\n"); + break; + case NO_SYNC: + to_gdb("Some cpus not stopped, see 'kgdb_info' for details\n"); + default:; + } + +#endif +/* + * Set up the gdb function call area. + */ + trap_cpu = smp_processor_id(); + OLD_esp = NEW_esp = (int) (&linux_regs->esp); + + IF_SMP(once_again:) + /* reply to host that an exception has occurred */ + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[signo >> 4]; + remcomOutBuffer[2] = hexchars[signo % 16]; + remcomOutBuffer[3] = 0; + + putpacket(remcomOutBuffer); + + while (1 == 1) { + error = 0; + remcomOutBuffer[0] = 0; + getpacket(remcomInBuffer); + switch (remcomInBuffer[0]) { + case '?': + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[signo >> 4]; + remcomOutBuffer[2] = hexchars[signo % 16]; + remcomOutBuffer[3] = 0; + break; + case 'd': + remote_debug = !(remote_debug); /* toggle debug flag */ + printk("Remote debug %s\n", + remote_debug ? "on" : "off"); + break; + case 'g': /* return the value of the CPU registers */ + get_gdb_regs(usethread, ®s, gdb_regs); + mem2hex((char *) gdb_regs, + remcomOutBuffer, NUMREGBYTES, 0); + break; + case 'G': /* set the value of the CPU registers - return OK */ + hex2mem(&remcomInBuffer[1], + (char *) gdb_regs, NUMREGBYTES, 0); + if (!usethread || usethread == current) { + gdb_regs_to_regs(gdb_regs, ®s); + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "E00"); + } + break; + + case 'P':{ /* set the value of a single CPU register - + return OK */ + /* + * For some reason, gdb wants to talk about psudo + * registers (greater than 15). These may have + * meaning for ptrace, but for us it is safe to + * ignor them. We do this by dumping them into + * _GS which we also ignor, but do have memory for. + */ + int regno; + + ptr = &remcomInBuffer[1]; + regs_to_gdb_regs(gdb_regs, ®s); + if ((!usethread || usethread == current) && + hexToInt(&ptr, ®no) && + *ptr++ == '=' && (regno >= 0)) { + regno = + (regno >= NUMREGS ? _GS : regno); + hex2mem(ptr, (char *) &gdb_regs[regno], + 4, 0); + gdb_regs_to_regs(gdb_regs, ®s); + strcpy(remcomOutBuffer, "OK"); + break; + } + strcpy(remcomOutBuffer, "E01"); + break; + } + + /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + case 'm': + /* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */ + ptr = &remcomInBuffer[1]; + if (hexToInt(&ptr, &addr) && + (*(ptr++) == ',') && (hexToInt(&ptr, &length))) { + ptr = 0; + /* + * hex doubles the byte count + */ + if (length > (BUFMAX / 2)) + length = BUFMAX / 2; + mem2hex((char *) addr, + remcomOutBuffer, length, 1); + if (mem_err) { + strcpy(remcomOutBuffer, "E03"); + debug_error("memory fault\n", NULL); + } + } + + if (ptr) { + strcpy(remcomOutBuffer, "E01"); + debug_error + ("malformed read memory command: %s\n", + remcomInBuffer); + } + break; + + /* MAA..AA,LLLL: + Write LLLL bytes at address AA.AA return OK */ + case 'M': + /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */ + ptr = &remcomInBuffer[1]; + if (hexToInt(&ptr, &addr) && + (*(ptr++) == ',') && + (hexToInt(&ptr, &length)) && (*(ptr++) == ':')) { + hex2mem(ptr, (char *) addr, length, 1); + + if (mem_err) { + strcpy(remcomOutBuffer, "E03"); + debug_error("memory fault\n", NULL); + } else { + strcpy(remcomOutBuffer, "OK"); + } + + ptr = 0; + } + if (ptr) { + strcpy(remcomOutBuffer, "E02"); + debug_error + ("malformed write memory command: %s\n", + remcomInBuffer); + } + break; + case 'S': + remcomInBuffer[0] = 's'; + case 'C': + /* Csig;AA..AA where ;AA..AA is optional + * continue with signal + * Since signals are meaning less to us, delete that + * part and then fall into the 'c' code. + */ + ptr = &remcomInBuffer[1]; + length = 2; + while (*ptr && *ptr != ';') { + length++; + ptr++; + } + if (*ptr) { + do { + ptr++; + *(ptr - length++) = *ptr; + } while (*ptr); + } else { + remcomInBuffer[1] = 0; + } + + /* cAA..AA Continue at address AA..AA(optional) */ + /* sAA..AA Step one instruction from AA..AA(optional) */ + /* D detach, reply OK and then continue */ + case 'c': + case 's': + case 'D': + + /* try to read optional parameter, + pc unchanged if no parm */ + ptr = &remcomInBuffer[1]; + if (hexToInt(&ptr, &addr)) { + if (remote_debug) + printk("Changing EIP to 0x%x\n", addr); + + regs.eip = addr; + } + + newPC = regs.eip; + + /* clear the trace bit */ + regs.eflags &= 0xfffffeff; + + /* set the trace bit if we're stepping */ + if (remcomInBuffer[0] == 's') + regs.eflags |= 0x100; + + /* detach is a friendly version of continue. Note that + debugging is still enabled (e.g hit control C) + */ + if (remcomInBuffer[0] == 'D') { + strcpy(remcomOutBuffer, "OK"); + putpacket(remcomOutBuffer); + } + + if (remote_debug) { + printk("Resuming execution\n"); + print_regs(®s); + } + asm volatile ("movl %%db6, %0\n":"=r" (dr6) + :); + if (!(dr6 & 0x4000)) { + for (breakno = 0; breakno < 4; ++breakno) { + if (dr6 & (1 << breakno) && + (breakinfo[breakno].type == 0)) { + /* Set restore flag */ + regs.eflags |= 0x10000; + break; + } + } + } + + if (kgdboe) + netpoll_set_trap(0); + + correct_hw_break(); + asm volatile ("movl %0, %%db6\n"::"r" (0)); + goto exit_kgdb; + + /* kill the program */ + case 'k': /* do nothing */ + break; + + /* query */ + case 'q': + nothreads = 0; + switch (remcomInBuffer[1]) { + case 'f': + threadid = 1; + thread_list = 2; + thread_list_start = (usethread ? : current); + case 's': + if (!cmp_str(&remcomInBuffer[2], + "ThreadInfo", 10)) + break; + + remcomOutBuffer[nothreads++] = 'm'; + for (; threadid < PID_MAX + MAX_NO_CPUS; + threadid++) { + thread = getthread(threadid); + if (thread) { + nothreads += int_to_hex_v( + &remcomOutBuffer[ + nothreads], + threadid); + if (thread_min > threadid) + thread_min = threadid; + remcomOutBuffer[ + nothreads] = ','; + nothreads++; + if (nothreads > BUFMAX - 10) + break; + } + } + if (remcomOutBuffer[nothreads - 1] == 'm') { + remcomOutBuffer[nothreads - 1] = 'l'; + } else { + nothreads--; + } + remcomOutBuffer[nothreads] = 0; + break; + +#ifdef old_thread_list /* Old thread info request */ + case 'L': + /* List threads */ + thread_list = 2; + thread_list_start = (usethread ? : current); + unpack_byte(remcomInBuffer + 3, &maxthreads); + unpack_threadid(remcomInBuffer + 5, &thref); + do { + int buf_thread_limit = + (BUFMAX - 22) / BUF_THREAD_ID_SIZE; + if (maxthreads > buf_thread_limit) { + maxthreads = buf_thread_limit; + } + } while (0); + remcomOutBuffer[0] = 'q'; + remcomOutBuffer[1] = 'M'; + remcomOutBuffer[4] = '0'; + pack_threadid(remcomOutBuffer + 5, &thref); + + threadid = threadref_to_int(&thref); + for (nothreads = 0; + nothreads < maxthreads && + threadid < PID_MAX + MAX_NO_CPUS; + threadid++) { + thread = getthread(threadid); + if (thread) { + int_to_threadref(&thref, + threadid); + pack_threadid(remcomOutBuffer + + 21 + + nothreads * 16, + &thref); + nothreads++; + if (thread_min > threadid) + thread_min = threadid; + } + } + + if (threadid == PID_MAX + MAX_NO_CPUS) { + remcomOutBuffer[4] = '1'; + } + pack_hex_byte(remcomOutBuffer + 2, nothreads); + remcomOutBuffer[21 + nothreads * 16] = '\0'; + break; +#endif + case 'C': + /* Current thread id */ + remcomOutBuffer[0] = 'Q'; + remcomOutBuffer[1] = 'C'; + threadid = current->pid; + if (!threadid) { + /* + * idle thread + */ + for (threadid = PID_MAX; + threadid < PID_MAX + MAX_NO_CPUS; + threadid++) { + if (current == + idle_task(threadid - + PID_MAX)) + break; + } + } + int_to_threadref(&thref, threadid); + pack_threadid(remcomOutBuffer + 2, &thref); + remcomOutBuffer[18] = '\0'; + break; + + case 'E': + /* Print exception info */ + printexceptioninfo(exceptionVector, + err_code, remcomOutBuffer); + break; + case 'T':{ + char * nptr; + /* Thread extra info */ + if (!cmp_str(&remcomInBuffer[2], + "hreadExtraInfo,", 15)) { + break; + } + ptr = &remcomInBuffer[17]; + hexToInt(&ptr, &threadid); + thread = getthread(threadid); + nptr = &thread->comm[0]; + length = 0; + ptr = &remcomOutBuffer[0]; + do { + length++; + ptr = pack_hex_byte(ptr, *nptr++); + } while (*nptr && length < 16); + /* + * would like that 16 to be the size of + * task_struct.comm but don't know the + * syntax.. + */ + *ptr = 0; + } + } + break; + + /* task related */ + case 'H': + switch (remcomInBuffer[1]) { + case 'g': + ptr = &remcomInBuffer[2]; + hexToInt(&ptr, &threadid); + thread = getthread(threadid); + if (!thread) { + remcomOutBuffer[0] = 'E'; + remcomOutBuffer[1] = '\0'; + break; + } + /* + * Just in case I forget what this is all about, + * the "thread info" command to gdb causes it + * to ask for a thread list. It then switches + * to each thread and asks for the registers. + * For this (and only this) usage, we want to + * fudge the registers of tasks not on the run + * list (i.e. waiting) to show the routine that + * called schedule. Also, gdb, is a minimalist + * in that if the current thread is the last + * it will not re-read the info when done. + * This means that in this case we must show + * the real registers. So here is how we do it: + * Each entry we keep track of the min + * thread in the list (the last that gdb will) + * get info for. We also keep track of the + * starting thread. + * "thread_list" is cleared when switching back + * to the min thread if it is was current, or + * if it was not current, thread_list is set + * to 1. When the switch to current comes, + * if thread_list is 1, clear it, else do + * nothing. + */ + usethread = thread; + if ((thread_list == 1) && + (thread == thread_list_start)) { + thread_list = 0; + } + if (thread_list && (threadid == thread_min)) { + if (thread == thread_list_start) { + thread_list = 0; + } else { + thread_list = 1; + } + } + /* follow through */ + case 'c': + remcomOutBuffer[0] = 'O'; + remcomOutBuffer[1] = 'K'; + remcomOutBuffer[2] = '\0'; + break; + } + break; + + /* Query thread status */ + case 'T': + ptr = &remcomInBuffer[1]; + hexToInt(&ptr, &threadid); + thread = getthread(threadid); + if (thread) { + remcomOutBuffer[0] = 'O'; + remcomOutBuffer[1] = 'K'; + remcomOutBuffer[2] = '\0'; + if (thread_min > threadid) + thread_min = threadid; + } else { + remcomOutBuffer[0] = 'E'; + remcomOutBuffer[1] = '\0'; + } + break; + + case 'Y': /* set up a hardware breakpoint */ + ptr = &remcomInBuffer[1]; + hexToInt(&ptr, &breakno); + ptr++; + hexToInt(&ptr, &breaktype); + ptr++; + hexToInt(&ptr, &length); + ptr++; + hexToInt(&ptr, &addr); + if (set_hw_break(breakno & 0x3, + breaktype & 0x3, + length & 0x3, addr) == 0) { + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "ERROR"); + } + break; + + /* Remove hardware breakpoint */ + case 'y': + ptr = &remcomInBuffer[1]; + hexToInt(&ptr, &breakno); + if (remove_hw_break(breakno & 0x3) == 0) { + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "ERROR"); + } + break; + + case 'r': /* reboot */ + strcpy(remcomOutBuffer, "OK"); + putpacket(remcomOutBuffer); + /*to_gdb("Rebooting\n"); */ + /* triplefault no return from here */ + { + static long no_idt[2]; + __asm__ __volatile__("lidt %0"::"m"(no_idt[0])); + BREAKPOINT; + } + + } /* switch */ + + /* reply to the request */ + putpacket(remcomOutBuffer); + } /* while(1==1) */ + /* + * reached by goto only. + */ + exit_kgdb: + /* + * Here is where we set up to trap a gdb function call. NEW_esp + * will be changed if we are trying to do this. We handle both + * adding and subtracting, thus allowing gdb to put grung on + * the stack which it removes later. + */ + if (NEW_esp != OLD_esp) { + int *ptr = END_OF_LOOKASIDE; + if (NEW_esp < OLD_esp) + ptr -= (OLD_esp - NEW_esp) / sizeof (int); + *--ptr = linux_regs->eflags; + *--ptr = linux_regs->xcs; + *--ptr = linux_regs->eip; + *--ptr = linux_regs->ecx; + *--ptr = linux_regs->ebx; + *--ptr = linux_regs->eax; + linux_regs->ecx = NEW_esp - (sizeof (int) * 6); + linux_regs->ebx = (unsigned int) END_OF_LOOKASIDE; + if (NEW_esp < OLD_esp) { + linux_regs->eip = (unsigned int) fn_call_stub; + } else { + linux_regs->eip = (unsigned int) fn_rtn_stub; + linux_regs->eax = NEW_esp; + } + linux_regs->eflags &= ~(IF_BIT | TF_BIT); + } +#ifdef CONFIG_SMP + /* + * Release gdb wait locks + * Sanity check time. Must have at least one cpu to run. Also single + * step must not be done if the current cpu is on hold. + */ + if (spinlock_count == 1) { + int ss_hold = (regs.eflags & 0x100) && kgdb_info.hold_on_sstep; + int cpu_avail = 0; + int i; + + for (i = 0; i < MAX_NO_CPUS; i++) { + if (!cpu_online(i)) + break; + if (!hold_cpu(i)) { + cpu_avail = 1; + } + } + /* + * Early in the bring up there will be NO cpus on line... + */ + if (!cpu_avail && !cpus_empty(cpu_online_map)) { + to_gdb("No cpus unblocked, see 'kgdb_info.hold_cpu'\n"); + goto once_again; + } + if (hold_cpu(smp_processor_id()) && (regs.eflags & 0x100)) { + to_gdb + ("Current cpu must be unblocked to single step\n"); + goto once_again; + } + if (!(ss_hold)) { + int i; + for (i = 0; i < MAX_NO_CPUS; i++) { + if (!hold_cpu(i)) { + spin_unlock(&waitlocks[i]); + } + } + } else { + spin_unlock(&waitlocks[smp_processor_id()]); + } + /* Release kgdb spinlock */ + KGDB_SPIN_UNLOCK(&kgdb_spinlock); + /* + * If this cpu is on hold, this is where we + * do it. Note, the NMI will pull us out of here, + * but will return as the above lock is not held. + * We will stay here till another cpu releases the lock for us. + */ + spin_unlock_wait(waitlocks + smp_processor_id()); + kgdb_local_irq_restore(flags); + return (0); + } +#if 0 +exit_just_unlock: +#endif +#endif + /* Release kgdb spinlock */ + KGDB_SPIN_UNLOCK(&kgdb_spinlock); + kgdb_local_irq_restore(flags); + return (0); +} + +/* this function is used to set up exception handlers for tracing and + * breakpoints. + * This function is not needed as the above line does all that is needed. + * We leave it for backward compatitability... + */ +void +set_debug_traps(void) +{ + /* + * linux_debug_hook is defined in traps.c. We store a pointer + * to our own exception handler into it. + + * But really folks, every hear of labeled common, an old Fortran + * concept. Lots of folks can reference it and it is define if + * anyone does. Only one can initialize it at link time. We do + * this with the hook. See the statement above. No need for any + * executable code and it is ready as soon as the kernel is + * loaded. Very desirable in kernel debugging. + + linux_debug_hook = handle_exception ; + */ + + /* In case GDB is started before us, ack any packets (presumably + "$?#xx") sitting there. + putDebugChar ('+'); + + initialized = 1; + */ +} + +/* This function will generate a breakpoint exception. It is used at the + beginning of a program to sync up with a debugger and can be used + otherwise as a quick means to stop program execution and "break" into + the debugger. */ +/* But really, just use the BREAKPOINT macro. We will handle the int stuff + */ + +#ifdef later +/* + * possibly we should not go thru the traps.c code at all? Someday. + */ +void +do_kgdb_int3(struct pt_regs *regs, long error_code) +{ + kgdb_handle_exception(3, 5, error_code, regs); + return; +} +#endif +#undef regs +#ifdef CONFIG_TRAP_BAD_SYSCALL_EXITS +asmlinkage void +bad_sys_call_exit(int stuff) +{ + struct pt_regs *regs = (struct pt_regs *) &stuff; + printk("Sys call %d return with %x preempt_count\n", + (int) regs->orig_eax, preempt_count()); +} +#endif +#ifdef CONFIG_STACK_OVERFLOW_TEST +#include +asmlinkage void +stack_overflow(void) +{ +#ifdef BREAKPOINT + BREAKPOINT; +#else + printk("Kernel stack overflow, looping forever\n"); +#endif + while (1) { + } +} +#endif + +#if defined(CONFIG_SMP) || defined(CONFIG_KGDB_CONSOLE) +char gdbconbuf[BUFMAX]; + +static void +kgdb_gdb_message(const char *s, unsigned count) +{ + int i; + int wcount; + char *bufptr; + /* + * This takes care of NMI while spining out chars to gdb + */ + IF_SMP(in_kgdb_console = 1); + gdbconbuf[0] = 'O'; + bufptr = gdbconbuf + 1; + while (count > 0) { + if ((count << 1) > (BUFMAX - 2)) { + wcount = (BUFMAX - 2) >> 1; + } else { + wcount = count; + } + count -= wcount; + for (i = 0; i < wcount; i++) { + bufptr = pack_hex_byte(bufptr, s[i]); + } + *bufptr = '\0'; + s += wcount; + + putpacket(gdbconbuf); + + } + IF_SMP(in_kgdb_console = 0); +} +#endif +#ifdef CONFIG_SMP +static void +to_gdb(const char *s) +{ + int count = 0; + while (s[count] && (count++ < BUFMAX)) ; + kgdb_gdb_message(s, count); +} +#endif +#ifdef CONFIG_KGDB_CONSOLE +#include +#include +#include +#include +#include + +void +kgdb_console_write(struct console *co, const char *s, unsigned count) +{ + + if (gdb_i386vector == -1) { + /* + * We have not yet talked to gdb. What to do... + * lets break, on continue we can do the write. + * But first tell him whats up. Uh, well no can do, + * as this IS the console. Oh well... + * We do need to wait or the messages will be lost. + * Other option would be to tell the above code to + * ignore this breakpoint and do an auto return, + * but that might confuse gdb. Also this happens + * early enough in boot up that we don't have the traps + * set up yet, so... + */ + breakpoint(); + } + kgdb_gdb_message(s, count); +} + +/* + * ------------------------------------------------------------ + * Serial KGDB driver + * ------------------------------------------------------------ + */ + +static struct console kgdbcons = { + name:"kgdb", + write:kgdb_console_write, +#ifdef CONFIG_KGDB_USER_CONSOLE + device:kgdb_console_device, +#endif + flags:CON_PRINTBUFFER | CON_ENABLED, + index:-1, +}; + +/* + * The trick here is that this file gets linked before printk.o + * That means we get to peer at the console info in the command + * line before it does. If we are up, we register, otherwise, + * do nothing. By returning 0, we allow printk to look also. + */ +static int kgdb_console_enabled; + +int __init +kgdb_console_init(char *str) +{ + if ((strncmp(str, "kgdb", 4) == 0) || (strncmp(str, "gdb", 3) == 0)) { + register_console(&kgdbcons); + kgdb_console_enabled = 1; + } + return 0; /* let others look at the string */ +} + +__setup("console=", kgdb_console_init); + +#ifdef CONFIG_KGDB_USER_CONSOLE +static kdev_t kgdb_console_device(struct console *c); +/* This stuff sort of works, but it knocks out telnet devices + * we are leaving it here in case we (or you) find time to figure it out + * better.. + */ + +/* + * We need a real char device as well for when the console is opened for user + * space activities. + */ + +static int +kgdb_consdev_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t +kgdb_consdev_write(struct file *file, const char *buf, + size_t count, loff_t * ppos) +{ + int size, ret = 0; + static char kbuf[128]; + static DECLARE_MUTEX(sem); + + /* We are not reentrant... */ + if (down_interruptible(&sem)) + return -ERESTARTSYS; + + while (count > 0) { + /* need to copy the data from user space */ + size = count; + if (size > sizeof (kbuf)) + size = sizeof (kbuf); + if (copy_from_user(kbuf, buf, size)) { + ret = -EFAULT; + break;; + } + kgdb_console_write(&kgdbcons, kbuf, size); + count -= size; + ret += size; + buf += size; + } + + up(&sem); + + return ret; +} + +struct file_operations kgdb_consdev_fops = { + open:kgdb_consdev_open, + write:kgdb_consdev_write +}; +static kdev_t +kgdb_console_device(struct console *c) +{ + return MKDEV(TTYAUX_MAJOR, 1); +} + +/* + * This routine gets called from the serial stub in the i386/lib + * This is so it is done late in bring up (just before the console open). + */ +void +kgdb_console_finit(void) +{ + if (kgdb_console_enabled) { + char *cptr = cdevname(MKDEV(TTYAUX_MAJOR, 1)); + char *cp = cptr; + while (*cptr && *cptr != '(') + cptr++; + *cptr = 0; + unregister_chrdev(TTYAUX_MAJOR, cp); + register_chrdev(TTYAUX_MAJOR, "kgdb", &kgdb_consdev_fops); + } +} +#endif +#endif +#ifdef CONFIG_KGDB_TS +#include /* time stamp code */ +#include /* in_interrupt */ +#ifdef CONFIG_KGDB_TS_64 +#define DATA_POINTS 64 +#endif +#ifdef CONFIG_KGDB_TS_128 +#define DATA_POINTS 128 +#endif +#ifdef CONFIG_KGDB_TS_256 +#define DATA_POINTS 256 +#endif +#ifdef CONFIG_KGDB_TS_512 +#define DATA_POINTS 512 +#endif +#ifdef CONFIG_KGDB_TS_1024 +#define DATA_POINTS 1024 +#endif +#ifndef DATA_POINTS +#define DATA_POINTS 128 /* must be a power of two */ +#endif +#define INDEX_MASK (DATA_POINTS - 1) +#if (INDEX_MASK & DATA_POINTS) +#error "CONFIG_KGDB_TS_COUNT must be a power of 2" +#endif +struct kgdb_and_then_struct { +#ifdef CONFIG_SMP + int on_cpu; +#endif + struct task_struct *task; + long long at_time; + int from_ln; + char *in_src; + void *from; + int *with_shpf; + int data0; + int data1; +}; +struct kgdb_and_then_struct2 { +#ifdef CONFIG_SMP + int on_cpu; +#endif + struct task_struct *task; + long long at_time; + int from_ln; + char *in_src; + void *from; + int *with_shpf; + struct task_struct *t1; + struct task_struct *t2; +}; +struct kgdb_and_then_struct kgdb_data[DATA_POINTS]; + +struct kgdb_and_then_struct *kgdb_and_then = &kgdb_data[0]; +int kgdb_and_then_count; + +void +kgdb_tstamp(int line, char *source, int data0, int data1) +{ + static spinlock_t ts_spin = SPIN_LOCK_UNLOCKED; + int flags; + kgdb_local_irq_save(flags); + spin_lock(&ts_spin); + rdtscll(kgdb_and_then->at_time); +#ifdef CONFIG_SMP + kgdb_and_then->on_cpu = smp_processor_id(); +#endif + kgdb_and_then->task = current; + kgdb_and_then->from_ln = line; + kgdb_and_then->in_src = source; + kgdb_and_then->from = __builtin_return_address(0); + kgdb_and_then->with_shpf = (int *) (((flags & IF_BIT) >> 9) | + (preempt_count() << 8)); + kgdb_and_then->data0 = data0; + kgdb_and_then->data1 = data1; + kgdb_and_then = &kgdb_data[++kgdb_and_then_count & INDEX_MASK]; + spin_unlock(&ts_spin); + kgdb_local_irq_restore(flags); +#ifdef CONFIG_PREEMPT + +#endif + return; +} +#endif +typedef int gdb_debug_hook(int exceptionVector, + int signo, int err_code, struct pt_regs *linux_regs); +gdb_debug_hook *linux_debug_hook = &kgdb_handle_exception; /* histerical reasons... */ + +static int kgdb_need_breakpoint[NR_CPUS]; + +void kgdb_schedule_breakpoint(void) +{ + kgdb_need_breakpoint[smp_processor_id()] = 1; +} + +void kgdb_process_breakpoint(void) +{ + /* + * Handle a breakpoint queued from inside network driver code + * to avoid reentrancy issues + */ + if (kgdb_need_breakpoint[smp_processor_id()]) { + kgdb_need_breakpoint[smp_processor_id()] = 0; + BREAKPOINT; + } +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/i386/kernel/kprobes.c 2004-09-02 22:09:10.000000000 -0700 @@ -0,0 +1,349 @@ +/* + * Kernel Probes (KProbes) + * arch/i386/kernel/kprobes.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2002, 2004 + * + * 2002-Oct Created by Vamsi Krishna S Kernel + * Probes initial implementation ( includes contributions from + * Rusty Russell). + * 2004-July Suparna Bhattacharya added jumper probes + * interface to access function arguments. + */ + +#include +#include +#include +#include +#include +#include + +/* kprobe_status settings */ +#define KPROBE_HIT_ACTIVE 0x00000001 +#define KPROBE_HIT_SS 0x00000002 + +static struct kprobe *current_kprobe; +static unsigned long kprobe_status, kprobe_old_eflags, kprobe_saved_eflags; +static struct pt_regs jprobe_saved_regs; +static long *jprobe_saved_esp; +/* copy of the kernel stack at the probe fire time */ +static kprobe_opcode_t jprobes_stack[MAX_STACK_SIZE]; + +/* + * returns non-zero if opcode modifies the interrupt flag. + */ +static inline int is_IF_modifier(kprobe_opcode_t opcode) +{ + switch (opcode) { + case 0xfa: /* cli */ + case 0xfb: /* sti */ + case 0xcf: /* iret/iretd */ + case 0x9d: /* popf/popfd */ + return 1; + } + return 0; +} + +void arch_prepare_kprobe(struct kprobe *p) +{ + memcpy(p->insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)); +} + +static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs) +{ + *p->addr = p->opcode; + regs->eip = (unsigned long)p->addr; +} + +static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) +{ + regs->eflags |= TF_MASK; + regs->eflags &= ~IF_MASK; + regs->eip = (unsigned long)&p->insn; +} + +/* + * Interrupts are disabled on entry as trap3 is an interrupt gate and they + * remain disabled thorough out this function. + */ +static inline int kprobe_handler(struct pt_regs *regs) +{ + struct kprobe *p; + int ret = 0; + u8 *addr = (u8 *) (regs->eip - 1); + + /* We're in an interrupt, but this is clear and BUG()-safe. */ + preempt_disable(); + + /* Check we're not actually recursing */ + if (kprobe_running()) { + /* We *are* holding lock here, so this is safe. + Disarm the probe we just hit, and ignore it. */ + p = get_kprobe(addr); + if (p) { + disarm_kprobe(p, regs); + ret = 1; + } else { + p = current_kprobe; + if (p->break_handler && p->break_handler(p, regs)) { + goto ss_probe; + } + } + /* If it's not ours, can't be delete race, (we hold lock). */ + goto no_kprobe; + } + + lock_kprobes(); + p = get_kprobe(addr); + if (!p) { + unlock_kprobes(); + if (*addr != BREAKPOINT_INSTRUCTION) { + /* + * The breakpoint instruction was removed right + * after we hit it. Another cpu has removed + * either a probepoint or a debugger breakpoint + * at this address. In either case, no further + * handling of this interrupt is appropriate. + */ + ret = 1; + } + /* Not one of ours: let kernel handle it */ + goto no_kprobe; + } + + kprobe_status = KPROBE_HIT_ACTIVE; + current_kprobe = p; + kprobe_saved_eflags = kprobe_old_eflags + = (regs->eflags & (TF_MASK | IF_MASK)); + if (is_IF_modifier(p->opcode)) + kprobe_saved_eflags &= ~IF_MASK; + + if (p->pre_handler(p, regs)) { + /* handler has already set things up, so skip ss setup */ + return 1; + } + + ss_probe: + prepare_singlestep(p, regs); + kprobe_status = KPROBE_HIT_SS; + return 1; + + no_kprobe: + preempt_enable_no_resched(); + return ret; +} + +/* + * Called after single-stepping. p->addr is the address of the + * instruction whose first byte has been replaced by the "int 3" + * instruction. To avoid the SMP problems that can occur when we + * temporarily put back the original opcode to single-step, we + * single-stepped a copy of the instruction. The address of this + * copy is p->insn. + * + * This function prepares to return from the post-single-step + * interrupt. We have to fix up the stack as follows: + * + * 0) Except in the case of absolute or indirect jump or call instructions, + * the new eip is relative to the copied instruction. We need to make + * it relative to the original instruction. + * + * 1) If the single-stepped instruction was pushfl, then the TF and IF + * flags are set in the just-pushed eflags, and may need to be cleared. + * + * 2) If the single-stepped instruction was a call, the return address + * that is atop the stack is the address following the copied instruction. + * We need to make it the address following the original instruction. + */ +static void resume_execution(struct kprobe *p, struct pt_regs *regs) +{ + unsigned long *tos = (unsigned long *)®s->esp; + unsigned long next_eip = 0; + unsigned long copy_eip = (unsigned long)&p->insn; + unsigned long orig_eip = (unsigned long)p->addr; + + switch (p->insn[0]) { + case 0x9c: /* pushfl */ + *tos &= ~(TF_MASK | IF_MASK); + *tos |= kprobe_old_eflags; + break; + case 0xe8: /* call relative - Fix return addr */ + *tos = orig_eip + (*tos - copy_eip); + break; + case 0xff: + if ((p->insn[1] & 0x30) == 0x10) { + /* call absolute, indirect */ + /* Fix return addr; eip is correct. */ + next_eip = regs->eip; + *tos = orig_eip + (*tos - copy_eip); + } else if (((p->insn[1] & 0x31) == 0x20) || /* jmp near, absolute indirect */ + ((p->insn[1] & 0x31) == 0x21)) { /* jmp far, absolute indirect */ + /* eip is correct. */ + next_eip = regs->eip; + } + break; + case 0xea: /* jmp absolute -- eip is correct */ + next_eip = regs->eip; + break; + default: + break; + } + + regs->eflags &= ~TF_MASK; + if (next_eip) { + regs->eip = next_eip; + } else { + regs->eip = orig_eip + (regs->eip - copy_eip); + } +} + +/* + * Interrupts are disabled on entry as trap1 is an interrupt gate and they + * remain disabled thoroughout this function. And we hold kprobe lock. + */ +static inline int post_kprobe_handler(struct pt_regs *regs) +{ + if (!kprobe_running()) + return 0; + + if (current_kprobe->post_handler) + current_kprobe->post_handler(current_kprobe, regs, 0); + + resume_execution(current_kprobe, regs); + regs->eflags |= kprobe_saved_eflags; + + unlock_kprobes(); + preempt_enable_no_resched(); + + /* + * if somebody else is singlestepping across a probe point, eflags + * will have TF set, in which case, continue the remaining processing + * of do_debug, as if this is not a probe hit. + */ + if (regs->eflags & TF_MASK) + return 0; + + return 1; +} + +/* Interrupts disabled, kprobe_lock held. */ +static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr) +{ + if (current_kprobe->fault_handler + && current_kprobe->fault_handler(current_kprobe, regs, trapnr)) + return 1; + + if (kprobe_status & KPROBE_HIT_SS) { + resume_execution(current_kprobe, regs); + regs->eflags |= kprobe_old_eflags; + + unlock_kprobes(); + preempt_enable_no_resched(); + } + return 0; +} + +/* + * Wrapper routine to for handling exceptions. + */ +int kprobe_exceptions_notify(struct notifier_block *self, unsigned long val, + void *data) +{ + struct die_args *args = (struct die_args *)data; + switch (val) { + case DIE_INT3: + if (kprobe_handler(args->regs)) + return NOTIFY_OK; + break; + case DIE_DEBUG: + if (post_kprobe_handler(args->regs)) + return NOTIFY_OK; + break; + case DIE_GPF: + if (kprobe_running() && + kprobe_fault_handler(args->regs, args->trapnr)) + return NOTIFY_OK; + break; + case DIE_PAGE_FAULT: + if (kprobe_running() && + kprobe_fault_handler(args->regs, args->trapnr)) + return NOTIFY_OK; + break; + default: + break; + } + return NOTIFY_BAD; +} + +int setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct jprobe *jp = container_of(p, struct jprobe, kp); + unsigned long addr; + + jprobe_saved_regs = *regs; + jprobe_saved_esp = ®s->esp; + addr = (unsigned long)jprobe_saved_esp; + + /* + * TBD: As Linus pointed out, gcc assumes that the callee + * owns the argument space and could overwrite it, e.g. + * tailcall optimization. So, to be absolutely safe + * we also save and restore enough stack bytes to cover + * the argument area. + */ + memcpy(jprobes_stack, (kprobe_opcode_t *) addr, MIN_STACK_SIZE(addr)); + regs->eflags &= ~IF_MASK; + regs->eip = (unsigned long)(jp->entry); + return 1; +} + +void jprobe_return(void) +{ + preempt_enable_no_resched(); + asm volatile (" xchgl %%ebx,%%esp \n" + " int3 \n"::"b" + (jprobe_saved_esp):"memory"); +} +void jprobe_return_end(void) +{ +}; + +int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) +{ + u8 *addr = (u8 *) (regs->eip - 1); + unsigned long stack_addr = (unsigned long)jprobe_saved_esp; + struct jprobe *jp = container_of(p, struct jprobe, kp); + + if ((addr > (u8 *) jprobe_return) && (addr < (u8 *) jprobe_return_end)) { + if (®s->esp != jprobe_saved_esp) { + struct pt_regs *saved_regs = + container_of(jprobe_saved_esp, struct pt_regs, esp); + printk("current esp %p does not match saved esp %p\n", + ®s->esp, jprobe_saved_esp); + printk("Saved registers for jprobe %p\n", jp); + show_registers(saved_regs); + printk("Current registers\n"); + show_registers(regs); + BUG(); + } + *regs = jprobe_saved_regs; + memcpy((kprobe_opcode_t *) stack_addr, jprobes_stack, + MIN_STACK_SIZE(stack_addr)); + return 1; + } + return 0; +} --- linux-2.6.9-rc1/arch/i386/kernel/ldt.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/arch/i386/kernel/ldt.c 2004-09-03 00:57:12.367159168 -0700 @@ -142,12 +142,17 @@ static int read_ldt(void __user * ptr, u err = -EFAULT; up(&mm->context.sem); if (err < 0) - return err; + goto error_return; if (size != bytecount) { /* zero-fill the rest */ - clear_user(ptr+size, bytecount-size); + if (clear_user(ptr+size, bytecount-size) != 0) { + err = -EFAULT; + goto error_return; + } } return bytecount; +error_return: + return err; } static int read_default_ldt(void __user * ptr, unsigned long bytecount) --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/i386/kernel/machine_kexec.c 2004-09-03 00:56:59.179164048 -0700 @@ -0,0 +1,208 @@ +/* + * machine_kexec.c - handle transition of Linux booting another kernel + * Copyright (C) 2002-2004 Eric Biederman + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static inline unsigned long read_cr3(void) +{ + unsigned long cr3; + asm volatile("movl %%cr3,%0": "=r"(cr3)); + return cr3; +} + +#define PAGE_ALIGNED __attribute__ ((__aligned__(PAGE_SIZE))) + +#define L0_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) +#define L1_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) +#define L2_ATTR (_PAGE_PRESENT) + +#define LEVEL0_SIZE (1UL << 12UL) + +#ifndef CONFIG_X86_PAE +#define LEVEL1_SIZE (1UL << 22UL) +static u32 pgtable_level1[1024] PAGE_ALIGNED; + +static void identity_map_page(unsigned long address) +{ + unsigned long level1_index, level2_index; + u32 *pgtable_level2; + + /* Find the current page table */ + pgtable_level2 = __va(read_cr3()); + + /* Find the indexes of the physical address to identity map */ + level1_index = (address % LEVEL1_SIZE)/LEVEL0_SIZE; + level2_index = address / LEVEL1_SIZE; + + /* Identity map the page table entry */ + pgtable_level1[level1_index] = address | L0_ATTR; + pgtable_level2[level2_index] = __pa(pgtable_level1) | L1_ATTR; + + /* Flush the tlb so the new mapping takes effect. + * Global tlb entries are not flushed but that is not an issue. + */ + load_cr3(pgtable_level2); +} + +#else +#define LEVEL1_SIZE (1UL << 21UL) +#define LEVEL2_SIZE (1UL << 30UL) +static u64 pgtable_level1[512] PAGE_ALIGNED; +static u64 pgtable_level2[512] PAGE_ALIGNED; + +static void identity_map_page(unsigned long address) +{ + unsigned long level1_index, level2_index, level3_index; + u64 *pgtable_level3; + + /* Find the current page table */ + pgtable_level3 = __va(read_cr3()); + + /* Find the indexes of the physical address to identity map */ + level1_index = (address % LEVEL1_SIZE)/LEVEL0_SIZE; + level2_index = (address % LEVEL2_SIZE)/LEVEL1_SIZE; + level3_index = address / LEVEL2_SIZE; + + /* Identity map the page table entry */ + pgtable_level1[level1_index] = address | L0_ATTR; + pgtable_level2[level2_index] = __pa(pgtable_level1) | L1_ATTR; + set_64bit(&pgtable_level3[level3_index], __pa(pgtable_level2) | L2_ATTR); + + /* Flush the tlb so the new mapping takes effect. + * Global tlb entries are not flushed but that is not an issue. + */ + load_cr3(pgtable_level3); +} +#endif + + +static void set_idt(void *newidt, __u16 limit) +{ + unsigned char curidt[6]; + + /* ia32 supports unaliged loads & stores */ + (*(__u16 *)(curidt)) = limit; + (*(__u32 *)(curidt +2)) = (unsigned long)(newidt); + + __asm__ __volatile__ ( + "lidt %0\n" + : "=m" (curidt) + ); +}; + + +static void set_gdt(void *newgdt, __u16 limit) +{ + unsigned char curgdt[6]; + + /* ia32 supports unaligned loads & stores */ + (*(__u16 *)(curgdt)) = limit; + (*(__u32 *)(curgdt +2)) = (unsigned long)(newgdt); + + __asm__ __volatile__ ( + "lgdt %0\n" + : "=m" (curgdt) + ); +}; + +static void load_segments(void) +{ +#define __STR(X) #X +#define STR(X) __STR(X) + + __asm__ __volatile__ ( + "\tljmp $"STR(__KERNEL_CS)",$1f\n" + "\t1:\n" + "\tmovl $"STR(__KERNEL_DS)",%eax\n" + "\tmovl %eax,%ds\n" + "\tmovl %eax,%es\n" + "\tmovl %eax,%fs\n" + "\tmovl %eax,%gs\n" + "\tmovl %eax,%ss\n" + ); +#undef STR +#undef __STR +} + +typedef asmlinkage void (*relocate_new_kernel_t)( + unsigned long indirection_page, unsigned long reboot_code_buffer, + unsigned long start_address, unsigned int has_pae); + +const extern unsigned char relocate_new_kernel[]; +extern void relocate_new_kernel_end(void); +const extern unsigned int relocate_new_kernel_size; + +/* + * Do what every setup is needed on image and the + * reboot code buffer to allow us to avoid allocations + * later. Currently nothing. + */ +int machine_kexec_prepare(struct kimage *image) +{ + return 0; +} + +void machine_kexec_cleanup(struct kimage *image) +{ +} + +/* + * Do not allocate memory (or fail in any way) in machine_kexec(). + * We are past the point of no return, committed to rebooting now. + */ +void machine_kexec(struct kimage *image) +{ + unsigned long indirection_page; + unsigned long reboot_code_buffer; + relocate_new_kernel_t rnk; + + /* Interrupts aren't acceptable while we reboot */ + local_irq_disable(); + + /* Compute some offsets */ + reboot_code_buffer = page_to_pfn(image->control_code_page) << PAGE_SHIFT; + indirection_page = image->head & PAGE_MASK; + + /* Set up an identity mapping for the reboot_code_buffer */ + identity_map_page(reboot_code_buffer); + + /* copy it out */ + memcpy((void *)reboot_code_buffer, relocate_new_kernel, relocate_new_kernel_size); + + /* The segment registers are funny things, they are + * automatically loaded from a table, in memory wherever you + * set them to a specific selector, but this table is never + * accessed again you set the segment to a different selector. + * + * The more common model is are caches where the behide + * the scenes work is done, but is also dropped at arbitrary + * times. + * + * I take advantage of this here by force loading the + * segments, before I zap the gdt with an invalid value. + */ + load_segments(); + /* The gdt & idt are now invalid. + * If you want to load them you must set up your own idt & gdt. + */ + set_gdt(phys_to_virt(0),0); + set_idt(phys_to_virt(0),0); + + /* now call it */ + rnk = (relocate_new_kernel_t) reboot_code_buffer; + (*rnk)(indirection_page, reboot_code_buffer, image->start, cpu_has_pae); +} --- linux-2.6.9-rc1/arch/i386/kernel/Makefile 2004-08-24 00:02:22.000000000 -0700 +++ 25/arch/i386/kernel/Makefile 2004-09-03 00:56:59.179164048 -0700 @@ -11,9 +11,10 @@ obj-y := process.o semaphore.o signal.o obj-y += cpu/ obj-y += timers/ -obj-$(CONFIG_ACPI_BOOT) += acpi/ +obj-$(CONFIG_ACPI) += acpi/ obj-$(CONFIG_X86_BIOS_REBOOT) += reboot.o obj-$(CONFIG_MCA) += mca.o +obj-$(CONFIG_KGDB) += kgdb_stub.o obj-$(CONFIG_X86_MSR) += msr.o obj-$(CONFIG_X86_CPUID) += cpuid.o obj-$(CONFIG_MICROCODE) += microcode.o @@ -23,8 +24,10 @@ obj-$(CONFIG_X86_TRAMPOLINE) += trampoli obj-$(CONFIG_X86_MPPARSE) += mpparse.o obj-$(CONFIG_X86_LOCAL_APIC) += apic.o nmi.o obj-$(CONFIG_X86_IO_APIC) += io_apic.o +obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o obj-$(CONFIG_X86_NUMAQ) += numaq.o obj-$(CONFIG_X86_SUMMIT_NUMA) += summit.o +obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_MODULES) += module.o obj-y += sysenter.o vsyscall.o obj-$(CONFIG_ACPI_SRAT) += srat.o @@ -41,12 +44,15 @@ obj-$(CONFIG_SCx200) += scx200.o # Note: kbuild does not track this dependency due to usage of .incbin $(obj)/vsyscall.o: $(obj)/vsyscall-int80.so $(obj)/vsyscall-sysenter.so targets += $(foreach F,int80 sysenter,vsyscall-$F.o vsyscall-$F.so) +targets += vsyscall.lds # The DSO images are built using a special linker script. quiet_cmd_syscall = SYSCALL $@ cmd_syscall = $(CC) -nostdlib $(SYSCFLAGS_$(@F)) \ -Wl,-T,$(filter-out FORCE,$^) -o $@ +export CPPFLAGS_vsyscall.lds += -P -C -U$(ARCH) + vsyscall-flags = -shared -s -Wl,-soname=linux-gate.so.1 SYSCFLAGS_vsyscall-sysenter.so = $(vsyscall-flags) SYSCFLAGS_vsyscall-int80.so = $(vsyscall-flags) --- linux-2.6.9-rc1/arch/i386/kernel/microcode.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/i386/kernel/microcode.c 2004-09-02 20:51:51.000000000 -0700 @@ -485,6 +485,7 @@ static struct file_operations microcode_ static struct miscdevice microcode_dev = { .minor = MICROCODE_MINOR, .name = "microcode", + .devfs_name = "cpu/microcode", .fops = µcode_fops, }; --- linux-2.6.9-rc1/arch/i386/kernel/msr.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/i386/kernel/msr.c 2004-09-03 00:56:52.581167096 -0700 @@ -260,7 +260,7 @@ static struct file_operations msr_fops = .open = msr_open, }; -static int msr_class_simple_device_add(int i) +static int __devinit msr_class_simple_device_add(int i) { int err = 0; struct class_device *class_err; --- linux-2.6.9-rc1/arch/i386/kernel/nmi.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/arch/i386/kernel/nmi.c 2004-09-03 00:56:35.759724344 -0700 @@ -34,7 +34,17 @@ #include "mach_traps.h" +#ifdef CONFIG_KGDB +#include +#ifdef CONFIG_SMP +unsigned int nmi_watchdog = NMI_IO_APIC; +#else +unsigned int nmi_watchdog = NMI_LOCAL_APIC; +#endif +#else unsigned int nmi_watchdog = NMI_NONE; +#endif + extern int unknown_nmi_panic; static unsigned int nmi_hz = HZ; static unsigned int nmi_perfctr_msr; /* the MSR to reset in NMI handler */ @@ -380,7 +390,13 @@ static int setup_p4_watchdog(void) clear_msr_range(0x3F1, 2); /* MSR 0x3F0 seems to have a default value of 0xFC00, but current docs doesn't fully define it, so leave it alone for now. */ - clear_msr_range(0x3A0, 31); + if (boot_cpu_data.x86_model >= 0x3) { + /* MSR_P4_IQ_ESCR0/1 (0x3ba/0x3bb) removed */ + clear_msr_range(0x3A0, 26); + clear_msr_range(0x3BC, 3); + } else { + clear_msr_range(0x3A0, 31); + } clear_msr_range(0x3C0, 6); clear_msr_range(0x3C8, 6); clear_msr_range(0x3E0, 2); @@ -460,6 +476,9 @@ void touch_nmi_watchdog (void) for (i = 0; i < NR_CPUS; i++) alert_counter[i] = 0; } +#ifdef CONFIG_KGDB +int tune_watchdog = 5*HZ; +#endif extern void die_nmi(struct pt_regs *, const char *msg); @@ -475,12 +494,24 @@ void nmi_watchdog_tick (struct pt_regs * sum = irq_stat[cpu].apic_timer_irqs; +#ifdef CONFIG_KGDB + if (!in_kgdb(regs) && last_irq_sums[cpu] == sum) { + +#else if (last_irq_sums[cpu] == sum) { +#endif /* * Ayiee, looks like this CPU is stuck ... * wait a few IRQs (5 seconds) before doing the oops ... */ alert_counter[cpu]++; +#ifdef CONFIG_KGDB + if (alert_counter[cpu] == tune_watchdog) { + kgdb_handle_exception(2, SIGPWR, 0, regs); + last_irq_sums[cpu] = sum; + alert_counter[cpu] = 0; + } +#endif if (alert_counter[cpu] == 5*nmi_hz) die_nmi(regs, "NMI Watchdog detected LOCKUP"); } else { --- linux-2.6.9-rc1/arch/i386/kernel/numaq.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/arch/i386/kernel/numaq.c 2004-09-03 00:56:51.384349040 -0700 @@ -28,6 +28,7 @@ #include #include #include +#include #include /* These are needed before the pgdat's are created */ --- linux-2.6.9-rc1/arch/i386/kernel/pci-dma.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/i386/kernel/pci-dma.c 2004-09-02 20:51:51.000000000 -0700 @@ -13,17 +13,40 @@ #include #include +struct dma_coherent_mem { + void *virt_base; + u32 device_base; + int size; + int flags; + unsigned long *bitmap; +}; + void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, int gfp) { void *ret; + struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL; + int order = get_order(size); /* ignore region specifiers */ gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); + if (mem) { + int page = bitmap_find_free_region(mem->bitmap, mem->size, + order); + if (page >= 0) { + *dma_handle = mem->device_base + (page << PAGE_SHIFT); + ret = mem->virt_base + (page << PAGE_SHIFT); + memset(ret, 0, size); + return ret; + } + if (mem->flags & DMA_MEMORY_EXCLUSIVE) + return NULL; + } + if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff)) gfp |= GFP_DMA; - ret = (void *)__get_free_pages(gfp, get_order(size)); + ret = (void *)__get_free_pages(gfp, order); if (ret != NULL) { memset(ret, 0, size); @@ -35,5 +58,89 @@ void *dma_alloc_coherent(struct device * void dma_free_coherent(struct device *dev, size_t size, void *vaddr, dma_addr_t dma_handle) { - free_pages((unsigned long)vaddr, get_order(size)); + struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL; + int order = get_order(size); + + if (mem && vaddr >= mem->virt_base && vaddr < (mem->virt_base + (mem->size << PAGE_SHIFT))) { + int page = (vaddr - mem->virt_base) >> PAGE_SHIFT; + + bitmap_release_region(mem->bitmap, page, order); + } else + free_pages((unsigned long)vaddr, order); +} + +int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, + dma_addr_t device_addr, size_t size, int flags) +{ + void *mem_base; + int pages = size >> PAGE_SHIFT; + int bitmap_size = (pages + 31)/32; + + if ((flags & (DMA_MEMORY_MAP | DMA_MEMORY_IO)) == 0) + goto out; + if (!size) + goto out; + if (dev->dma_mem) + goto out; + + /* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */ + + mem_base = ioremap(bus_addr, size); + if (!mem_base) + goto out; + + dev->dma_mem = kmalloc(GFP_KERNEL, sizeof(struct dma_coherent_mem)); + if (!dev->dma_mem) + goto out; + memset(dev->dma_mem, 0, sizeof(struct dma_coherent_mem)); + dev->dma_mem->bitmap = kmalloc(GFP_KERNEL, bitmap_size); + if (!dev->dma_mem->bitmap) + goto free1_out; + memset(dev->dma_mem->bitmap, 0, bitmap_size); + + dev->dma_mem->virt_base = mem_base; + dev->dma_mem->device_base = device_addr; + dev->dma_mem->size = pages; + dev->dma_mem->flags = flags; + + if (flags & DMA_MEMORY_MAP) + return DMA_MEMORY_MAP; + + return DMA_MEMORY_IO; + + free1_out: + kfree(dev->dma_mem->bitmap); + out: + return 0; +} +EXPORT_SYMBOL(dma_declare_coherent_memory); + +void dma_release_declared_memory(struct device *dev) +{ + struct dma_coherent_mem *mem = dev->dma_mem; + + if(!mem) + return; + dev->dma_mem = NULL; + kfree(mem->bitmap); + kfree(mem); +} +EXPORT_SYMBOL(dma_release_declared_memory); + +void *dma_mark_declared_memory_occupied(struct device *dev, + dma_addr_t device_addr, size_t size) +{ + struct dma_coherent_mem *mem = dev->dma_mem; + int pages = (size + (device_addr & ~PAGE_MASK) + PAGE_SIZE - 1) >> PAGE_SHIFT; + int pos, err; + + if (!mem) + return ERR_PTR(-EINVAL); + + pos = (device_addr - mem->device_base) >> PAGE_SHIFT; + err = bitmap_allocate_region(mem->bitmap, pos, get_order(pages)); + if (err != 0) + return ERR_PTR(err); + return mem->virt_base + (pos << PAGE_SHIFT); } +EXPORT_SYMBOL(dma_mark_declared_memory_occupied); --- linux-2.6.9-rc1/arch/i386/kernel/process.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/arch/i386/kernel/process.c 2004-09-03 00:56:52.582166944 -0700 @@ -13,6 +13,7 @@ #include +#include #include #include #include @@ -32,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -53,6 +55,9 @@ #include #include +#include +#include + asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); int hlt_counter; @@ -131,6 +136,34 @@ static void poll_idle (void) } } +#ifdef CONFIG_HOTPLUG_CPU +#include +/* We don't actually take CPU down, just spin without interrupts. */ +static inline void play_dead(void) +{ + /* Ack it */ + __get_cpu_var(cpu_state) = CPU_DEAD; + + /* We shouldn't have to disable interrupts while dead, but + * some interrupts just don't seem to go away, and this makes + * it "work" for testing purposes. */ + /* Death loop */ + while (__get_cpu_var(cpu_state) != CPU_UP_PREPARE) + cpu_relax(); + + local_irq_disable(); + __flush_tlb_all(); + cpu_set(smp_processor_id(), cpu_online_map); + enable_APIC_timer(); + local_irq_enable(); +} +#else +static inline void play_dead(void) +{ + BUG(); +} +#endif /* CONFIG_HOTPLUG_CPU */ + /* * The idle thread. There's no useful work to be * done, so just try to conserve power and have a @@ -147,6 +180,8 @@ void cpu_idle (void) if (!idle) idle = default_idle; + if (cpu_is_offline(smp_processor_id())) + play_dead(); irq_stat[smp_processor_id()].idle_timestamp = jiffies; idle(); } @@ -183,18 +218,13 @@ void __init select_idle_routine(const st printk("monitor/mwait feature present.\n"); /* * Skip, if setup has overridden idle. - * Also, take care of system with asymmetric CPUs. - * Use, mwait_idle only if all cpus support it. - * If not, we fallback to default_idle() + * One CPU supports mwait => All CPUs supports mwait */ if (!pm_idle) { printk("using mwait in idle threads.\n"); pm_idle = mwait_idle; } - return; } - pm_idle = default_idle; - return; } static int __init idle_setup (char *str) @@ -310,6 +340,7 @@ void exit_thread(void) t->io_bitmap_max = 0; put_cpu(); } + perfctr_exit_thread(&tsk->thread); } void flush_thread(void) @@ -372,6 +403,8 @@ int copy_thread(int nr, unsigned long cl savesegment(fs,p->thread.fs); savesegment(gs,p->thread.gs); + perfctr_copy_task(p, regs); + tsk = current; if (unlikely(NULL != tsk->thread.io_bitmap_ptr)) { p->thread.io_bitmap_ptr = kmalloc(IO_BITMAP_BYTES, GFP_KERNEL); @@ -521,6 +554,8 @@ struct task_struct fastcall * __switch_t /* never put a printk in __switch_to... printk() calls wake_up*() indirectly */ + perfctr_suspend_thread(prev); + __unlazy_fpu(prev_p); /* @@ -575,6 +610,9 @@ struct task_struct fastcall * __switch_t */ memset(tss->io_bitmap, 0xff, prev->io_bitmap_max); } + + perfctr_resume_thread(next); + return prev_p; } @@ -595,7 +633,7 @@ asmlinkage int sys_clone(struct pt_regs child_tidptr = (int __user *)regs.edi; if (!newsp) newsp = regs.esp; - return do_fork(clone_flags & ~CLONE_IDLETASK, newsp, ®s, 0, parent_tidptr, child_tidptr); + return do_fork(clone_flags, newsp, ®s, 0, parent_tidptr, child_tidptr); } /* --- linux-2.6.9-rc1/arch/i386/kernel/reboot.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/i386/kernel/reboot.c 2004-09-03 00:56:59.046184264 -0700 @@ -23,7 +23,6 @@ static int reboot_mode; int reboot_thru_bios; #ifdef CONFIG_SMP -int reboot_smp = 0; static int reboot_cpu = -1; /* shamelessly grabbed from lib/vsprintf.c for readability */ #define is_digit(c) ((c) >= '0' && (c) <= '9') @@ -46,7 +45,6 @@ static int __init reboot_setup(char *str break; #ifdef CONFIG_SMP case 's': /* "smp" reboot by executing reset on BSP or other CPU*/ - reboot_smp = 1; if (is_digit(*(str+1))) { reboot_cpu = (int) (*(str+1) - '0'); if (is_digit(*(str+2))) @@ -85,33 +83,9 @@ static int __init set_bios_reboot(struct return 0; } -/* - * Some machines require the "reboot=s" commandline option, this quirk makes that automatic. - */ -static int __init set_smp_reboot(struct dmi_system_id *d) -{ -#ifdef CONFIG_SMP - if (!reboot_smp) { - reboot_smp = 1; - printk(KERN_INFO "%s series board detected. Selecting SMP-method for reboots.\n", d->ident); - } -#endif - return 0; -} - -/* - * Some machines require the "reboot=b,s" commandline option, this quirk makes that automatic. - */ -static int __init set_smp_bios_reboot(struct dmi_system_id *d) -{ - set_smp_reboot(d); - set_bios_reboot(d); - return 0; -} - static struct dmi_system_id __initdata reboot_dmi_table[] = { { /* Handle problems with rebooting on Dell 1300's */ - .callback = set_smp_bios_reboot, + .callback = set_bios_reboot, .ident = "Dell PowerEdge 1300", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), @@ -295,41 +269,32 @@ void machine_real_restart(unsigned char : "i" ((void *) (0x1000 - sizeof (real_mode_switch) - 100))); } -void machine_restart(char * __unused) +void machine_shutdown(void) { #ifdef CONFIG_SMP - int cpuid; - - cpuid = GET_APIC_ID(apic_read(APIC_ID)); - - if (reboot_smp) { - - /* check to see if reboot_cpu is valid - if its not, default to the BSP */ - if ((reboot_cpu == -1) || - (reboot_cpu > (NR_CPUS -1)) || - !physid_isset(cpuid, phys_cpu_present_map)) - reboot_cpu = boot_cpu_physical_apicid; - - reboot_smp = 0; /* use this as a flag to only go through this once*/ - /* re-run this function on the other CPUs - it will fall though this section since we have - cleared reboot_smp, and do the reboot if it is the - correct CPU, otherwise it halts. */ - if (reboot_cpu != cpuid) - smp_call_function((void *)machine_restart , NULL, 1, 0); + int reboot_cpu_id; + + /* The boot cpu is always logical cpu 0 */ + reboot_cpu_id = 0; + + /* See if there has been given a command line override */ + if ((reboot_cpu_id != -1) && (reboot_cpu < NR_CPUS) && + cpu_isset(reboot_cpu, cpu_online_map)) { + reboot_cpu_id = reboot_cpu; } - /* if reboot_cpu is still -1, then we want a tradional reboot, - and if we are not running on the reboot_cpu,, halt */ - if ((reboot_cpu != -1) && (cpuid != reboot_cpu)) { - for (;;) - __asm__ __volatile__ ("hlt"); + /* Make certain the cpu I'm rebooting on is online */ + if (!cpu_isset(reboot_cpu_id, cpu_online_map)) { + reboot_cpu_id = smp_processor_id(); } - /* - * Stop all CPUs and turn off local APICs and the IO-APIC, so - * other OSs see a clean IRQ state. + + /* Make certain I only run on the appropriate processor */ + set_cpus_allowed(current, cpumask_of_cpu(reboot_cpu_id)); + + /* O.K. Now that I'm on the appropriate processor, stop + * all of the others, and disable their local APICs. */ + smp_send_stop(); #elif defined(CONFIG_X86_LOCAL_APIC) if (cpu_has_apic) { @@ -341,6 +306,11 @@ void machine_restart(char * __unused) #ifdef CONFIG_X86_IO_APIC disable_IO_APIC(); #endif +} + +void machine_restart(char * __unused) +{ + machine_shutdown(); if (!reboot_thru_bios) { if (efi_enabled) { --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/i386/kernel/relocate_kernel.S 2004-09-03 00:56:59.180163896 -0700 @@ -0,0 +1,118 @@ +/* + * relocate_kernel.S - put the kernel image in place to boot + * Copyright (C) 2002-2004 Eric Biederman + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include + + /* + * Must be relocatable PIC code callable as a C function, that once + * it starts can not use the previous processes stack. + */ + .globl relocate_new_kernel +relocate_new_kernel: + /* read the arguments and say goodbye to the stack */ + movl 4(%esp), %ebx /* indirection_page */ + movl 8(%esp), %ebp /* reboot_code_buffer */ + movl 12(%esp), %edx /* start address */ + movl 16(%esp), %ecx /* cpu_has_pae */ + + /* zero out flags, and disable interrupts */ + pushl $0 + popfl + + /* set a new stack at the bottom of our page... */ + lea 4096(%ebp), %esp + + /* store the parameters back on the stack */ + pushl %edx /* store the start address */ + + /* Set cr0 to a known state: + * 31 0 == Paging disabled + * 18 0 == Alignment check disabled + * 16 0 == Write protect disabled + * 3 0 == No task switch + * 2 0 == Don't do FP software emulation. + * 0 1 == Proctected mode enabled + */ + movl %cr0, %eax + andl $~((1<<31)|(1<<18)|(1<<16)|(1<<3)|(1<<2)), %eax + orl $(1<<0), %eax + movl %eax, %cr0 + + /* clear cr4 if applicable */ + testl %ecx, %ecx + jz 1f + /* Set cr4 to a known state: + * Setting everything to zero seems safe. + */ + movl %cr4, %eax + andl $0, %eax + movl %eax, %cr4 + + jmp 1f +1: + + /* Flush the TLB (needed?) */ + xorl %eax, %eax + movl %eax, %cr3 + + /* Do the copies */ + cld +0: /* top, read another word for the indirection page */ + movl %ebx, %ecx + movl (%ebx), %ecx + addl $4, %ebx + testl $0x1, %ecx /* is it a destination page */ + jz 1f + movl %ecx, %edi + andl $0xfffff000, %edi + jmp 0b +1: + testl $0x2, %ecx /* is it an indirection page */ + jz 1f + movl %ecx, %ebx + andl $0xfffff000, %ebx + jmp 0b +1: + testl $0x4, %ecx /* is it the done indicator */ + jz 1f + jmp 2f +1: + testl $0x8, %ecx /* is it the source indicator */ + jz 0b /* Ignore it otherwise */ + movl %ecx, %esi /* For every source page do a copy */ + andl $0xfffff000, %esi + + movl $1024, %ecx + rep ; movsl + jmp 0b + +2: + + /* To be certain of avoiding problems with self-modifying code + * I need to execute a serializing instruction here. + * So I flush the TLB, it's handy, and not processor dependent. + */ + xorl %eax, %eax + movl %eax, %cr3 + + /* set all of the registers to known values */ + /* leave %esp alone */ + + xorl %eax, %eax + xorl %ebx, %ebx + xorl %ecx, %ecx + xorl %edx, %edx + xorl %esi, %esi + xorl %edi, %edi + xorl %ebp, %ebp + ret +relocate_new_kernel_end: + + .globl relocate_new_kernel_size +relocate_new_kernel_size: + .long relocate_new_kernel_end - relocate_new_kernel --- linux-2.6.9-rc1/arch/i386/kernel/setup.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/i386/kernel/setup.c 2004-09-03 00:56:30.113582688 -0700 @@ -75,14 +75,14 @@ struct cpuinfo_x86 boot_cpu_data = { 0, unsigned long mmu_cr4_features; EXPORT_SYMBOL_GPL(mmu_cr4_features); -#ifdef CONFIG_ACPI_INTERPRETER +#ifdef CONFIG_ACPI int acpi_disabled = 0; #else int acpi_disabled = 1; #endif EXPORT_SYMBOL(acpi_disabled); -#ifdef CONFIG_ACPI_BOOT +#ifdef CONFIG_ACPI int __initdata acpi_force = 0; extern acpi_interrupt_flags acpi_sci_flags; #endif @@ -219,9 +219,14 @@ static struct resource standard_io_resou .end = 0x0021, .flags = IORESOURCE_BUSY | IORESOURCE_IO }, { - .name = "timer", + .name = "timer0", .start = 0x0040, - .end = 0x005f, + .end = 0x0043, + .flags = IORESOURCE_BUSY | IORESOURCE_IO +}, { + .name = "timer1", + .start = 0x0050, + .end = 0x0053, .flags = IORESOURCE_BUSY | IORESOURCE_IO }, { .name = "keyboard", @@ -744,7 +749,7 @@ static void __init parse_cmdline_early ( } #endif -#ifdef CONFIG_ACPI_BOOT +#ifdef CONFIG_ACPI /* "acpi=off" disables both ACPI table parsing and interpreter */ else if (!memcmp(from, "acpi=off", 8)) { disable_acpi(); @@ -800,7 +805,7 @@ static void __init parse_cmdline_early ( else if (!memcmp(from, "noapic", 6)) disable_ioapic_setup(); #endif /* CONFIG_X86_LOCAL_APIC */ -#endif /* CONFIG_ACPI_BOOT */ +#endif /* CONFIG_ACPI */ /* * highmem=size forces highmem to be exactly 'size' bytes. @@ -1350,7 +1355,12 @@ void __init setup_arch(char **cmdline_p) /* * NOTE: before this point _nobody_ is allowed to allocate - * any memory using the bootmem allocator. + * any memory using the bootmem allocator. Although the + * alloctor is now initialised only the first 8Mb of the kernel + * virtual address space has been mapped. All allocations before + * paging_init() has completed must use the alloc_bootmem_low_pages() + * variant (which allocates DMA'able memory) and care must be taken + * not to exceed the 8Mb limit. */ #ifdef CONFIG_SMP @@ -1358,6 +1368,10 @@ void __init setup_arch(char **cmdline_p) #endif paging_init(); + /* + * NOTE: at this point the bootmem allocator is fully available. + */ + #ifdef CONFIG_EARLY_PRINTK { char *s = strstr(*cmdline_p, "earlyprintk="); --- linux-2.6.9-rc1/arch/i386/kernel/signal.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/i386/kernel/signal.c 2004-09-03 00:57:12.705107792 -0700 @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -345,18 +346,20 @@ static void setup_frame(int sig, struct void __user *restorer; struct sigframe __user *frame; int err = 0; + int usig; frame = get_sigframe(ka, regs, sizeof(*frame)); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) goto give_sigsegv; - err |= __put_user((current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig), - &frame->sig); + usig = current_thread_info()->exec_domain + && current_thread_info()->exec_domain->signal_invmap + && sig < 32 + ? current_thread_info()->exec_domain->signal_invmap[sig] + : sig; + + err |= __put_user(usig, &frame->sig); if (err) goto give_sigsegv; @@ -395,13 +398,22 @@ static void setup_frame(int sig, struct /* Set up registers for signal handler */ regs->esp = (unsigned long) frame; regs->eip = (unsigned long) ka->sa.sa_handler; + regs->eax = (unsigned long) sig; + regs->edx = (unsigned long) 0; + regs->ecx = (unsigned long) 0; set_fs(USER_DS); regs->xds = __USER_DS; regs->xes = __USER_DS; regs->xss = __USER_DS; regs->xcs = __USER_CS; - regs->eflags &= ~TF_MASK; + if (regs->eflags & TF_MASK) { + if (current->ptrace & PT_PTRACED) { + ptrace_notify(SIGTRAP); + } else { + regs->eflags &= ~TF_MASK; + } + } #if DEBUG_SIG printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", @@ -411,9 +423,7 @@ static void setup_frame(int sig, struct return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, @@ -422,18 +432,20 @@ static void setup_rt_frame(int sig, stru void __user *restorer; struct rt_sigframe __user *frame; int err = 0; + int usig; frame = get_sigframe(ka, regs, sizeof(*frame)); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) goto give_sigsegv; - err |= __put_user((current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig), - &frame->sig); + usig = current_thread_info()->exec_domain + && current_thread_info()->exec_domain->signal_invmap + && sig < 32 + ? current_thread_info()->exec_domain->signal_invmap[sig] + : sig; + + err |= __put_user(usig, &frame->sig); err |= __put_user(&frame->info, &frame->pinfo); err |= __put_user(&frame->uc, &frame->puc); err |= copy_siginfo_to_user(&frame->info, info); @@ -476,13 +488,22 @@ static void setup_rt_frame(int sig, stru /* Set up registers for signal handler */ regs->esp = (unsigned long) frame; regs->eip = (unsigned long) ka->sa.sa_handler; + regs->eax = (unsigned long) usig; + regs->edx = (unsigned long) &frame->info; + regs->ecx = (unsigned long) &frame->uc; set_fs(USER_DS); regs->xds = __USER_DS; regs->xes = __USER_DS; regs->xss = __USER_DS; regs->xcs = __USER_CS; - regs->eflags &= ~TF_MASK; + if (regs->eflags & TF_MASK) { + if (current->ptrace & PT_PTRACED) { + ptrace_notify(SIGTRAP); + } else { + regs->eflags &= ~TF_MASK; + } + } #if DEBUG_SIG printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", @@ -492,9 +513,7 @@ static void setup_rt_frame(int sig, stru return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } /* @@ -502,11 +521,9 @@ give_sigsegv: */ static void -handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset, - struct pt_regs * regs) +handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, + sigset_t *oldset, struct pt_regs * regs) { - struct k_sigaction *ka = ¤t->sighand->action[sig-1]; - /* Are we from a system call? */ if (regs->orig_eax >= 0) { /* If so, check system call restarting.. */ @@ -534,9 +551,6 @@ handle_signal(unsigned long sig, siginfo else setup_frame(sig, ka, oldset, regs); - if (ka->sa.sa_flags & SA_ONESHOT) - ka->sa.sa_handler = SIG_DFL; - if (!(ka->sa.sa_flags & SA_NODEFER)) { spin_lock_irq(¤t->sighand->siglock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); @@ -555,6 +569,7 @@ int fastcall do_signal(struct pt_regs *r { siginfo_t info; int signr; + struct k_sigaction ka; /* * We want the common case to go fast, which @@ -573,7 +588,7 @@ int fastcall do_signal(struct pt_regs *r if (!oldset) oldset = ¤t->blocked; - signr = get_signal_to_deliver(&info, regs, NULL); + signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (signr > 0) { /* Reenable any watchpoints before delivering the * signal to user space. The processor register will @@ -583,7 +598,7 @@ int fastcall do_signal(struct pt_regs *r __asm__("movl %0,%%db7" : : "r" (current->thread.debugreg[7])); /* Whee! Actually deliver the signal. */ - handle_signal(signr, &info, oldset, regs); + handle_signal(signr, &info, &ka, oldset, regs); return 1; } --- linux-2.6.9-rc1/arch/i386/kernel/smpboot.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/i386/kernel/smpboot.c 2004-09-03 00:56:52.900118608 -0700 @@ -44,6 +44,9 @@ #include #include #include +#include +#include +#include #include #include @@ -88,6 +91,9 @@ extern unsigned char trampoline_end []; static unsigned char *trampoline_base; static int trampoline_exec; +/* State of each CPU. */ +DEFINE_PER_CPU(int, cpu_state) = { 0 }; + /* * Currently trivial. Write the real->protected mode * bootstrap into the page concerned. The caller @@ -496,16 +502,6 @@ extern struct { unsigned short ss; } stack_start; -static struct task_struct * __init fork_by_hand(void) -{ - struct pt_regs regs; - /* - * don't care about the eip and regs settings since - * we'll never reschedule the forked task. - */ - return copy_process(CLONE_VM|CLONE_IDLETASK, 0, ®s, 0, NULL, NULL); -} - #ifdef CONFIG_NUMA /* which logical CPUs are on which nodes */ @@ -801,21 +797,10 @@ static int __init do_boot_cpu(int apicid * We can't use kernel_thread since we must avoid to * reschedule the child. */ - idle = fork_by_hand(); + idle = fork_idle(cpu); if (IS_ERR(idle)) panic("failed fork for CPU %d", cpu); - wake_up_forked_process(idle); - - /* - * We remove it from the pidhash and the runqueue - * once we got the process: - */ - init_idle(idle, cpu); - idle->thread.eip = (unsigned long) start_secondary; - - unhash_process(idle); - /* start_eip had better be page-aligned! */ start_eip = setup_trampoline(); @@ -1138,240 +1123,112 @@ static void __init smp_boot_cpus(unsigne synchronize_tsc_bp(); } -#ifdef CONFIG_SCHED_SMT -#ifdef CONFIG_NUMA -static struct sched_group sched_group_cpus[NR_CPUS]; -static struct sched_group sched_group_phys[NR_CPUS]; -static struct sched_group sched_group_nodes[MAX_NUMNODES]; -static DEFINE_PER_CPU(struct sched_domain, cpu_domains); -static DEFINE_PER_CPU(struct sched_domain, phys_domains); -static DEFINE_PER_CPU(struct sched_domain, node_domains); -__init void arch_init_sched_domains(void) +/* These are wrappers to interface to the new boot process. Someone + who understands all this stuff should rewrite it properly. --RR 15/Jul/02 */ +void __init smp_prepare_cpus(unsigned int max_cpus) { - int i; - struct sched_group *first = NULL, *last = NULL; - - /* Set up domains */ - for_each_cpu(i) { - struct sched_domain *cpu_domain = &per_cpu(cpu_domains, i); - struct sched_domain *phys_domain = &per_cpu(phys_domains, i); - struct sched_domain *node_domain = &per_cpu(node_domains, i); - int node = cpu_to_node(i); - cpumask_t nodemask = node_to_cpumask(node); - - *cpu_domain = SD_SIBLING_INIT; - cpu_domain->span = cpu_sibling_map[i]; - cpu_domain->parent = phys_domain; - cpu_domain->groups = &sched_group_cpus[i]; - - *phys_domain = SD_CPU_INIT; - phys_domain->span = nodemask; - phys_domain->parent = node_domain; - phys_domain->groups = &sched_group_phys[first_cpu(cpu_domain->span)]; - - *node_domain = SD_NODE_INIT; - node_domain->span = cpu_possible_map; - node_domain->groups = &sched_group_nodes[cpu_to_node(i)]; - } - - /* Set up CPU (sibling) groups */ - for_each_cpu(i) { - struct sched_domain *cpu_domain = &per_cpu(cpu_domains, i); - int j; - first = last = NULL; - - if (i != first_cpu(cpu_domain->span)) - continue; - - for_each_cpu_mask(j, cpu_domain->span) { - struct sched_group *cpu = &sched_group_cpus[j]; - - cpu->cpumask = CPU_MASK_NONE; - cpu_set(j, cpu->cpumask); - cpu->cpu_power = SCHED_LOAD_SCALE; - - if (!first) - first = cpu; - if (last) - last->next = cpu; - last = cpu; - } - last->next = first; - } - - for (i = 0; i < MAX_NUMNODES; i++) { - int j; - cpumask_t nodemask; - struct sched_group *node = &sched_group_nodes[i]; - cpumask_t node_cpumask = node_to_cpumask(i); - - cpus_and(nodemask, node_cpumask, cpu_possible_map); - - if (cpus_empty(nodemask)) - continue; - - first = last = NULL; - /* Set up physical groups */ - for_each_cpu_mask(j, nodemask) { - struct sched_domain *cpu_domain = &per_cpu(cpu_domains, j); - struct sched_group *cpu = &sched_group_phys[j]; - - if (j != first_cpu(cpu_domain->span)) - continue; - - cpu->cpumask = cpu_domain->span; - /* - * Make each extra sibling increase power by 10% of - * the basic CPU. This is very arbitrary. - */ - cpu->cpu_power = SCHED_LOAD_SCALE + SCHED_LOAD_SCALE*(cpus_weight(cpu->cpumask)-1) / 10; - node->cpu_power += cpu->cpu_power; - - if (!first) - first = cpu; - if (last) - last->next = cpu; - last = cpu; - } - last->next = first; - } - - /* Set up nodes */ - first = last = NULL; - for (i = 0; i < MAX_NUMNODES; i++) { - struct sched_group *cpu = &sched_group_nodes[i]; - cpumask_t nodemask; - cpumask_t node_cpumask = node_to_cpumask(i); - - cpus_and(nodemask, node_cpumask, cpu_possible_map); - - if (cpus_empty(nodemask)) - continue; - - cpu->cpumask = nodemask; - /* ->cpu_power already setup */ - - if (!first) - first = cpu; - if (last) - last->next = cpu; - last = cpu; - } - last->next = first; - + smp_commenced_mask = cpumask_of_cpu(0); + cpu_callin_map = cpumask_of_cpu(0); mb(); - for_each_cpu(i) { - struct sched_domain *cpu_domain = &per_cpu(cpu_domains, i); - cpu_attach_domain(cpu_domain, i); - } + smp_boot_cpus(max_cpus); } -#else /* !CONFIG_NUMA */ -static struct sched_group sched_group_cpus[NR_CPUS]; -static struct sched_group sched_group_phys[NR_CPUS]; -static DEFINE_PER_CPU(struct sched_domain, cpu_domains); -static DEFINE_PER_CPU(struct sched_domain, phys_domains); -__init void arch_init_sched_domains(void) + +void __devinit smp_prepare_boot_cpu(void) { - int i; - struct sched_group *first = NULL, *last = NULL; + cpu_set(smp_processor_id(), cpu_online_map); + cpu_set(smp_processor_id(), cpu_callout_map); +} - /* Set up domains */ - for_each_cpu(i) { - struct sched_domain *cpu_domain = &per_cpu(cpu_domains, i); - struct sched_domain *phys_domain = &per_cpu(phys_domains, i); - - *cpu_domain = SD_SIBLING_INIT; - cpu_domain->span = cpu_sibling_map[i]; - cpu_domain->parent = phys_domain; - cpu_domain->groups = &sched_group_cpus[i]; - - *phys_domain = SD_CPU_INIT; - phys_domain->span = cpu_possible_map; - phys_domain->groups = &sched_group_phys[first_cpu(cpu_domain->span)]; - } - - /* Set up CPU (sibling) groups */ - for_each_cpu(i) { - struct sched_domain *cpu_domain = &per_cpu(cpu_domains, i); - int j; - first = last = NULL; +#ifdef CONFIG_HOTPLUG_CPU +extern void fixup_irqs(void); - if (i != first_cpu(cpu_domain->span)) - continue; +/* must be called with the cpucontrol mutex held */ +static int __devinit cpu_enable(unsigned int cpu) +{ + /* get the target out of its holding state */ + per_cpu(cpu_state, cpu) = CPU_UP_PREPARE; + wmb(); - for_each_cpu_mask(j, cpu_domain->span) { - struct sched_group *cpu = &sched_group_cpus[j]; + /* wait for the processor to ack it. timeout? */ + while (!cpu_online(cpu)) + cpu_relax(); - cpus_clear(cpu->cpumask); - cpu_set(j, cpu->cpumask); - cpu->cpu_power = SCHED_LOAD_SCALE; - - if (!first) - first = cpu; - if (last) - last->next = cpu; - last = cpu; - } - last->next = first; - } + fixup_irqs(); + /* counter the disable in fixup_irqs() */ + local_irq_enable(); + return 0; +} - first = last = NULL; - /* Set up physical groups */ - for_each_cpu(i) { - struct sched_domain *cpu_domain = &per_cpu(cpu_domains, i); - struct sched_group *cpu = &sched_group_phys[i]; +int __cpu_disable(void) +{ + /* + * Perhaps use cpufreq to drop frequency, but that could go + * into generic code. + * + * We won't take down the boot processor on i386 due to some + * interrupts only being able to be serviced by the BSP. + * Especially so if we're not using an IOAPIC -zwane + */ + if (smp_processor_id() == 0) + return -EBUSY; - if (i != first_cpu(cpu_domain->span)) - continue; + /* We enable the timer again on the exit path of the death loop */ + disable_APIC_timer(); + /* Allow any queued timer interrupts to get serviced */ + local_irq_enable(); + mdelay(1); + local_irq_disable(); - cpu->cpumask = cpu_domain->span; - /* See SMT+NUMA setup for comment */ - cpu->cpu_power = SCHED_LOAD_SCALE + SCHED_LOAD_SCALE*(cpus_weight(cpu->cpumask)-1) / 10; - - if (!first) - first = cpu; - if (last) - last->next = cpu; - last = cpu; - } - last->next = first; + /* It's now safe to remove this processor from the online map */ + cpu_clear(smp_processor_id(), cpu_online_map); + fixup_irqs(); + return 0; +} - mb(); - for_each_cpu(i) { - struct sched_domain *cpu_domain = &per_cpu(cpu_domains, i); - cpu_attach_domain(cpu_domain, i); +void __cpu_die(unsigned int cpu) +{ + /* We don't do anything here: idle task is faking death itself. */ + unsigned int i; + + for (i = 0; i < 10; i++) { + /* They ack this in play_dead by setting CPU_DEAD */ + if (per_cpu(cpu_state, cpu) == CPU_DEAD) + return; + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(HZ/10); } + printk(KERN_ERR "CPU %u didn't die...\n", cpu); } -#endif /* CONFIG_NUMA */ -#endif /* CONFIG_SCHED_SMT */ - -/* These are wrappers to interface to the new boot process. Someone - who understands all this stuff should rewrite it properly. --RR 15/Jul/02 */ -void __init smp_prepare_cpus(unsigned int max_cpus) +#else /* ... !CONFIG_HOTPLUG_CPU */ +int __cpu_disable(void) { - smp_boot_cpus(max_cpus); + return -ENOSYS; } -void __devinit smp_prepare_boot_cpu(void) +void __cpu_die(unsigned int cpu) { - cpu_set(smp_processor_id(), cpu_online_map); - cpu_set(smp_processor_id(), cpu_callout_map); + /* We said "no" in __cpu_disable */ + BUG(); } +#endif /* CONFIG_HOTPLUG_CPU */ int __devinit __cpu_up(unsigned int cpu) { - /* This only works at boot for x86. See "rewrite" above. */ - if (cpu_isset(cpu, smp_commenced_mask)) { - local_irq_enable(); - return -ENOSYS; - } - /* In case one didn't come up */ if (!cpu_isset(cpu, cpu_callin_map)) { + printk(KERN_DEBUG "skipping cpu%d, didn't come online\n", cpu); local_irq_enable(); return -EIO; } +#ifdef CONFIG_HOTPLUG_CPU + /* Already up, and in cpu_quiescent now? */ + if (cpu_isset(cpu, smp_commenced_mask)) { + cpu_enable(cpu); + return 0; + } +#endif + local_irq_enable(); /* Unleash the CPU! */ cpu_set(cpu, smp_commenced_mask); --- linux-2.6.9-rc1/arch/i386/kernel/smp.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/i386/kernel/smp.c 2004-09-03 00:56:52.584166640 -0700 @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -163,7 +164,7 @@ void send_IPI_mask_bitmask(cpumask_t cpu unsigned long flags; local_irq_save(flags); - + WARN_ON(mask & ~cpus_addr(cpu_online_map)[0]); /* * Wait for idle. */ @@ -345,21 +346,21 @@ out: static void flush_tlb_others(cpumask_t cpumask, struct mm_struct *mm, unsigned long va) { - cpumask_t tmp; /* * A couple of (to be removed) sanity checks: * - * - we do not send IPIs to not-yet booted CPUs. * - current CPU must not be in mask * - mask must exist :) */ BUG_ON(cpus_empty(cpumask)); - - cpus_and(tmp, cpumask, cpu_online_map); - BUG_ON(!cpus_equal(cpumask, tmp)); BUG_ON(cpu_isset(smp_processor_id(), cpumask)); BUG_ON(!mm); + /* If a CPU which we ran on has gone down, OK. */ + cpus_and(cpumask, cpumask, cpu_online_map); + if (cpus_empty(cpumask)) + return; + /* * i'm not happy about this global shared spinlock in the * MM hot path, but we'll see how contended it is. @@ -466,7 +467,17 @@ void flush_tlb_all(void) { on_each_cpu(do_flush_tlb_all, NULL, 1, 1); } - +#ifdef CONFIG_KGDB +/* + * By using the NMI code instead of a vector we just sneak thru the + * word generator coming out with just what we want. AND it does + * not matter if clustered_apic_mode is set or not. + */ +void smp_send_nmi_allbutself(void) +{ + send_IPI_allbutself(APIC_DM_NMI); +} +#endif /* * this function sends a 'reschedule' IPI to another CPU. * it goes straight through and wastes no time serializing @@ -474,6 +485,7 @@ void flush_tlb_all(void) */ void smp_send_reschedule(int cpu) { + WARN_ON(cpu_is_offline(cpu)); send_IPI_mask(cpumask_of_cpu(cpu), RESCHEDULE_VECTOR); } @@ -514,10 +526,16 @@ int smp_call_function (void (*func) (voi */ { struct call_data_struct data; - int cpus = num_online_cpus()-1; + int cpus; + + /* Holding any lock stops cpus from going down. */ + spin_lock(&call_lock); + cpus = num_online_cpus()-1; - if (!cpus) + if (!cpus) { + spin_unlock(&call_lock); return 0; + } /* Can deadlock when called with interrupts disabled */ WARN_ON(irqs_disabled()); @@ -529,7 +547,6 @@ int smp_call_function (void (*func) (voi if (wait) atomic_set(&data.finished, 0); - spin_lock(&call_lock); call_data = &data; mb(); --- linux-2.6.9-rc1/arch/i386/kernel/srat.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/i386/kernel/srat.c 2004-09-03 00:56:51.384349040 -0700 @@ -28,6 +28,7 @@ #include #include #include +#include #include /* --- linux-2.6.9-rc1/arch/i386/kernel/sysenter.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/i386/kernel/sysenter.c 2004-09-02 20:51:51.000000000 -0700 @@ -43,18 +43,18 @@ extern const char vsyscall_sysenter_star static int __init sysenter_setup(void) { - unsigned long page = get_zeroed_page(GFP_ATOMIC); + void *page = (void *)get_zeroed_page(GFP_ATOMIC); __set_fixmap(FIX_VSYSCALL, __pa(page), PAGE_READONLY_EXEC); if (!boot_cpu_has(X86_FEATURE_SEP)) { - memcpy((void *) page, + memcpy(page, &vsyscall_int80_start, &vsyscall_int80_end - &vsyscall_int80_start); return 0; } - memcpy((void *) page, + memcpy(page, &vsyscall_sysenter_start, &vsyscall_sysenter_end - &vsyscall_sysenter_start); --- linux-2.6.9-rc1/arch/i386/kernel/traps.c 2004-08-24 00:02:20.000000000 -0700 +++ 25/arch/i386/kernel/traps.c 2004-09-03 00:56:52.585166488 -0700 @@ -26,6 +26,7 @@ #include #include #include +#include #ifdef CONFIG_EISA #include @@ -48,6 +49,7 @@ #include #include +#include #include #include @@ -92,6 +94,18 @@ asmlinkage void spurious_interrupt_bug(v asmlinkage void machine_check(void); static int kstack_depth_to_print = 24; +struct notifier_block *i386die_chain; +static spinlock_t die_notifier_lock = SPIN_LOCK_UNLOCKED; + +int register_die_notifier(struct notifier_block *nb) +{ + int err = 0; + unsigned long flags; + spin_lock_irqsave(&die_notifier_lock, flags); + err = notifier_chain_register(&i386die_chain, nb); + spin_unlock_irqrestore(&die_notifier_lock, flags); + return err; +} static int valid_stack_ptr(struct task_struct *task, void *p) { @@ -102,6 +116,40 @@ static int valid_stack_ptr(struct task_s return 1; } +#ifdef CONFIG_KGDB +extern void sysenter_past_esp(void); +#include +#include +void set_intr_gate(unsigned int n, void *addr); +static void set_intr_usr_gate(unsigned int n, void *addr); +/* + * Should be able to call this breakpoint() very early in + * bring up. Just hard code the call where needed. + * The breakpoint() code is here because set_?_gate() functions + * are local (static) to trap.c. They need be done only once, + * but it does not hurt to do them over. + */ +void breakpoint(void) +{ + set_intr_usr_gate(3,&int3); /* disable ints on trap */ + set_intr_gate(1,&debug); + set_intr_gate(14,&page_fault); + + BREAKPOINT; +} +#define CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,after) \ + { \ + if (!user_mode(regs) ) \ + { \ + kgdb_handle_exception(trapnr, signr, error_code, regs); \ + after; \ + } else if ((trapnr == 3) && (regs->eflags &0x200)) local_irq_enable(); \ + } +#else +#define CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,after) +#endif + + #ifdef CONFIG_FRAME_POINTER static void print_context_stack(struct task_struct *task, unsigned long *stack, unsigned long ebp) @@ -333,6 +381,16 @@ void die(const char * str, struct pt_reg #endif if (nl) printk("\n"); +#ifdef CONFIG_KGDB + /* This is about the only place we want to go to kgdb even if in + * user mode. But we must go in via a trap so within kgdb we will + * always be in kernel mode. + */ + if (user_mode(regs)) + BREAKPOINT; +#endif + CHK_REMOTE_DEBUG(0,SIGTRAP,err,regs,) + notify_die(DIE_OOPS, (char *)str, regs, err, 255, SIGSEGV); show_registers(regs); } else printk(KERN_ERR "Recursive die() failure, output suppressed\n"); @@ -406,6 +464,10 @@ static inline void do_trap(int trapnr, i #define DO_ERROR(trapnr, signr, str, name) \ asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ { \ + CHK_REMOTE_DEBUG(trapnr,signr,error_code,regs,) \ + if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ + == NOTIFY_OK) \ + return; \ do_trap(trapnr, signr, str, 0, regs, error_code, NULL); \ } @@ -417,12 +479,19 @@ asmlinkage void do_##name(struct pt_regs info.si_errno = 0; \ info.si_code = sicode; \ info.si_addr = (void __user *)siaddr; \ + if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ + == NOTIFY_BAD) \ + return; \ do_trap(trapnr, signr, str, 0, regs, error_code, &info); \ } #define DO_VM86_ERROR(trapnr, signr, str, name) \ asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ { \ + CHK_REMOTE_DEBUG(trapnr, signr, error_code,regs, return) \ + if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ + == NOTIFY_OK) \ + return; \ do_trap(trapnr, signr, str, 1, regs, error_code, NULL); \ } @@ -434,11 +503,16 @@ asmlinkage void do_##name(struct pt_regs info.si_errno = 0; \ info.si_code = sicode; \ info.si_addr = (void __user *)siaddr; \ + if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ + == NOTIFY_OK) \ + return; \ do_trap(trapnr, signr, str, 1, regs, error_code, &info); \ } DO_VM86_ERROR_INFO( 0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->eip) +#ifndef CONFIG_KPROBES DO_VM86_ERROR( 3, SIGTRAP, "int3", int3) +#endif DO_VM86_ERROR( 4, SIGSEGV, "overflow", overflow) DO_VM86_ERROR( 5, SIGSEGV, "bounds", bounds) DO_ERROR_INFO( 6, SIGILL, "invalid operand", invalid_op, ILL_ILLOPN, regs->eip) @@ -467,8 +541,13 @@ gp_in_vm86: return; gp_in_kernel: - if (!fixup_exception(regs)) + if (!fixup_exception(regs)) { + CHK_REMOTE_DEBUG(13,SIGSEGV,error_code,regs,) + if (notify_die(DIE_GPF, "general protection fault", regs, + error_code, 13, SIGSEGV) == NOTIFY_OK); + return; die("general protection fault", regs, error_code); + } } static void mem_parity_error(unsigned char reason, struct pt_regs * regs) @@ -538,6 +617,9 @@ static void default_do_nmi(struct pt_reg unsigned char reason = get_nmi_reason(); if (!(reason & 0xc0)) { + if (notify_die(DIE_NMI_IPI, "nmi_ipi", regs, reason, 0, SIGINT) + == NOTIFY_BAD) + return; #ifdef CONFIG_X86_LOCAL_APIC /* * Ok, so this is none of the documented NMI sources, @@ -551,6 +633,8 @@ static void default_do_nmi(struct pt_reg unknown_nmi_error(reason, regs); return; } + if (notify_die(DIE_NMI, "nmi", regs, reason, 0, SIGINT) == NOTIFY_BAD) + return; if (reason & 0x80) mem_parity_error(reason, regs); if (reason & 0x40) @@ -576,6 +660,14 @@ asmlinkage void do_nmi(struct pt_regs * nmi_enter(); cpu = smp_processor_id(); + +#ifdef CONFIG_HOTPLUG_CPU + if (!cpu_online(cpu)) { + nmi_exit(); + return; + } +#endif + ++nmi_count(cpu); if (!nmi_callback(regs, cpu)) @@ -594,6 +686,20 @@ void unset_nmi_callback(void) nmi_callback = dummy_nmi_callback; } +#ifdef CONFIG_KPROBES +asmlinkage int do_int3(struct pt_regs *regs, long error_code) +{ + if (notify_die(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP) + == NOTIFY_OK) + return 1; + /* This is an interrupt gate, because kprobes wants interrupts + disabled. Normal trap handlers don't. */ + restore_interrupts(regs); + do_trap(3, SIGTRAP, "int3", 1, regs, error_code, NULL); + return 0; +} +#endif + /* * Our handling of the processor debug registers is non-trivial. * We do not clear them on entry and exit from the kernel. Therefore @@ -624,6 +730,9 @@ asmlinkage void do_debug(struct pt_regs __asm__ __volatile__("movl %%db6,%0" : "=r" (condition)); + if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code, + SIGTRAP) == NOTIFY_OK) + return; /* It's safe to allow irq's after DR6 has been saved */ if (regs->eflags & X86_EFLAGS_IF) local_irq_enable(); @@ -651,8 +760,18 @@ asmlinkage void do_debug(struct pt_regs * allowing programs to debug themselves without the ptrace() * interface. */ +#ifdef CONFIG_KGDB + /* + * I think this is the only "real" case of a TF in the kernel + * that really belongs to user space. Others are + * "Ours all ours!" + */ + if (((regs->xcs & 3) == 0) && ((void *)regs->eip == sysenter_past_esp)) + goto clear_TF_reenable; +#else if ((regs->xcs & 3) == 0) goto clear_TF_reenable; +#endif if ((tsk->ptrace & (PT_DTRACE|PT_PTRACED)) == PT_DTRACE) goto clear_TF; } @@ -664,6 +783,17 @@ asmlinkage void do_debug(struct pt_regs info.si_errno = 0; info.si_code = TRAP_BRKPT; +#ifdef CONFIG_KGDB + /* + * If this is a kernel mode trap, we need to reset db7 to allow us + * to continue sanely ALSO skip the signal delivery + */ + if ((regs->xcs & 3) == 0) + goto clear_dr7; + + /* if not kernel, allow ints but only if they were on */ + if ( regs->eflags & 0x200) local_irq_enable(); +#endif /* If this is a kernel mode trap, save the user PC on entry to * the kernel, that's what the debugger can make sense of. */ @@ -678,6 +808,7 @@ clear_dr7: __asm__("movl %0,%%db7" : /* no output */ : "r" (0)); + CHK_REMOTE_DEBUG(1,SIGTRAP,error_code,regs,) return; debug_vm86: @@ -912,6 +1043,14 @@ void set_intr_gate(unsigned int n, void _set_gate(idt_table+n,14,0,addr,__KERNEL_CS); } +/* + * This routine sets up an interrupt gate at directory privilege level 3. + */ +static inline void set_system_intr_gate(unsigned int n, void *addr) +{ + _set_gate(idt_table+n, 14, 3, addr, __KERNEL_CS); +} + static void __init set_trap_gate(unsigned int n, void *addr) { _set_gate(idt_table+n,15,0,addr,__KERNEL_CS); @@ -926,6 +1065,12 @@ static void __init set_call_gate(void *a { _set_gate(a,12,3,addr,__KERNEL_CS); } +#ifdef CONFIG_KGDB +void set_intr_usr_gate(unsigned int n, void *addr) +{ + _set_gate(idt_table+n,14,3,addr,__KERNEL_CS); +} +#endif static void __init set_task_gate(unsigned int n, unsigned int gdt_entry) { @@ -948,7 +1093,11 @@ void __init trap_init(void) set_trap_gate(0,÷_error); set_intr_gate(1,&debug); set_intr_gate(2,&nmi); - set_system_gate(3,&int3); /* int3-5 can be called from all */ +#ifndef CONFIG_KGDB + set_system_intr_gate(3, &int3); /* int3-5 can be called from all */ +#else + set_intr_usr_gate(3,&int3); /* int3-5 can be called from all */ +#endif set_system_gate(4,&overflow); set_system_gate(5,&bounds); set_trap_gate(6,&invalid_op); --- linux-2.6.9-rc1/arch/i386/kernel/vmlinux.lds.S 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/i386/kernel/vmlinux.lds.S 2004-09-02 20:51:51.000000000 -0700 @@ -4,6 +4,7 @@ #include #include +#include OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") OUTPUT_ARCH(i386) @@ -11,7 +12,7 @@ ENTRY(startup_32) jiffies = jiffies_64; SECTIONS { - . = 0xC0000000 + 0x100000; + . = __PAGE_OFFSET + 0x100000; /* read-only */ _text = .; /* Text and read-only data */ .text : { @@ -66,9 +67,6 @@ SECTIONS __setup_start = .; .init.setup : { *(.init.setup) } __setup_end = .; - __start___param = .; - __param : { *(__param) } - __stop___param = .; __initcall_start = .; .initcall.init : { *(.initcall1.init) --- linux-2.6.9-rc1/arch/i386/kernel/vsyscall.lds 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/i386/kernel/vsyscall.lds 2004-09-02 21:07:49.000000000 -0700 @@ -4,12 +4,54 @@ * segment (that fits in one page). This script controls its layout. */ -/* This must match . */ -VSYSCALL_BASE = 0xffffe000; + +/* + * DO NOT MODIFY. + * + * This file was generated by arch/i386/Makefile + * + */ + +/* offsetof(struct sigcontext, eax) */ +/* offsetof(struct sigcontext, ebx) */ +/* offsetof(struct sigcontext, ecx) */ +/* offsetof(struct sigcontext, edx) */ +/* offsetof(struct sigcontext, esi) */ +/* offsetof(struct sigcontext, edi) */ +/* offsetof(struct sigcontext, ebp) */ +/* offsetof(struct sigcontext, esp) */ +/* offsetof(struct sigcontext, eip) */ + +/* offsetof(struct cpuinfo_x86, x86) */ +/* offsetof(struct cpuinfo_x86, x86_vendor) */ +/* offsetof(struct cpuinfo_x86, x86_model) */ +/* offsetof(struct cpuinfo_x86, x86_mask) */ +/* offsetof(struct cpuinfo_x86, hard_math) */ +/* offsetof(struct cpuinfo_x86, cpuid_level) */ +/* offsetof(struct cpuinfo_x86, x86_capability) */ +/* offsetof(struct cpuinfo_x86, x86_vendor_id) */ + +/* offsetof(struct thread_info, task) */ +/* offsetof(struct thread_info, exec_domain) */ +/* offsetof(struct thread_info, flags) */ +/* offsetof(struct thread_info, status) */ +/* offsetof(struct thread_info, cpu) */ +/* offsetof(struct thread_info, preempt_count) */ +/* offsetof(struct thread_info, addr_limit) */ +/* offsetof(struct thread_info, restart_block) */ + +/* offsetof(struct exec_domain, handler) */ +/* offsetof(struct rt_sigframe, uc.uc_mcontext) */ +/* offsetof(struct tss_struct, esp0) - sizeof(struct tss_struct) */ +/* PAGE_SIZE */ +/* __fix_to_virt(FIX_VSYSCALL) */ + + + SECTIONS { - . = VSYSCALL_BASE + SIZEOF_HEADERS; + . = -8192 + SIZEOF_HEADERS; .hash : { *(.hash) } :text .dynsym : { *(.dynsym) } @@ -22,7 +64,7 @@ SECTIONS For the layouts to match, we need to skip more than enough space for the dynamic symbol table et al. If this amount is insufficient, ld -shared will barf. Just increase it here. */ - . = VSYSCALL_BASE + 0x400; + . = -8192 + 0x400; .text : { *(.text) } :text =0x90909090 --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/i386/kernel/vsyscall.lds.S 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,65 @@ +/* + * Linker script for vsyscall DSO. The vsyscall page is an ELF shared + * object prelinked to its virtual address, and with only one read-only + * segment (that fits in one page). This script controls its layout. + */ +#include + +SECTIONS +{ + . = VSYSCALL_BASE + SIZEOF_HEADERS; + + .hash : { *(.hash) } :text + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + + /* This linker script is used both with -r and with -shared. + For the layouts to match, we need to skip more than enough + space for the dynamic symbol table et al. If this amount + is insufficient, ld -shared will barf. Just increase it here. */ + . = VSYSCALL_BASE + 0x400; + + .text : { *(.text) } :text =0x90909090 + + .eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr + .eh_frame : { KEEP (*(.eh_frame)) } :text + .dynamic : { *(.dynamic) } :text :dynamic + .useless : { + *(.got.plt) *(.got) + *(.data .data.* .gnu.linkonce.d.*) + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + } :text +} + +/* + * We must supply the ELF program headers explicitly to get just one + * PT_LOAD segment, and set the flags explicitly to make segments read-only. + */ +PHDRS +{ + text PT_LOAD FILEHDR PHDRS FLAGS(5); /* PF_R|PF_X */ + dynamic PT_DYNAMIC FLAGS(4); /* PF_R */ + eh_frame_hdr 0x6474e550; /* PT_GNU_EH_FRAME, but ld doesn't match the name */ +} + +/* + * This controls what symbols we export from the DSO. + */ +VERSION +{ + LINUX_2.5 { + global: + __kernel_vsyscall; + __kernel_sigreturn; + __kernel_rt_sigreturn; + + local: *; + }; +} + +/* The ELF entry point can be used to set the AT_SYSINFO value. */ +ENTRY(__kernel_vsyscall); --- linux-2.6.9-rc1/arch/i386/lib/dec_and_lock.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/i386/lib/dec_and_lock.c 2004-09-03 00:56:42.825650160 -0700 @@ -10,6 +10,7 @@ #include #include +#ifndef ATOMIC_DEC_AND_LOCK int atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock) { int counter; @@ -38,3 +39,5 @@ slow_path: spin_unlock(lock); return 0; } +#endif + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/i386/lib/kgdb_serial.c 2004-09-03 00:56:36.522608368 -0700 @@ -0,0 +1,499 @@ +/* + * Serial interface GDB stub + * + * Written (hacked together) by David Grothe (dave@gcom.com) + * Modified to allow invokation early in boot see also + * kgdb.h for instructions by George Anzinger(george@mvista.com) + * Modified to handle debugging over ethernet by Robert Walsh + * and wangdi , based on + * code by San Mehat. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_KGDB_USER_CONSOLE +extern void kgdb_console_finit(void); +#endif +#define PRNT_off +#define TEST_EXISTANCE +#ifdef PRNT +#define dbprintk(s) printk s +#else +#define dbprintk(s) +#endif +#define TEST_INTERRUPT_off +#ifdef TEST_INTERRUPT +#define intprintk(s) printk s +#else +#define intprintk(s) +#endif + +#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT) + +#define GDB_BUF_SIZE 512 /* power of 2, please */ + +static char gdb_buf[GDB_BUF_SIZE]; +static int gdb_buf_in_inx; +static atomic_t gdb_buf_in_cnt; +static int gdb_buf_out_inx; + +struct async_struct *gdb_async_info; +static int gdb_async_irq; + +#define outb_px(a,b) outb_p(b,a) + +static void program_uart(struct async_struct *info); +static void write_char(struct async_struct *info, int chr); +/* + * Get a byte from the hardware data buffer and return it + */ +static int +read_data_bfr(struct async_struct *info) +{ + char it = inb_p(info->port + UART_LSR); + + if (it & UART_LSR_DR) + return (inb_p(info->port + UART_RX)); + /* + * If we have a framing error assume somebody messed with + * our uart. Reprogram it and send '-' both ways... + */ + if (it & 0xc) { + program_uart(info); + write_char(info, '-'); + return ('-'); + } + return (-1); + +} /* read_data_bfr */ + +/* + * Get a char if available, return -1 if nothing available. + * Empty the receive buffer first, then look at the interface hardware. + + * Locking here is a bit of a problem. We MUST not lock out communication + * if we are trying to talk to gdb about a kgdb entry. ON the other hand + * we can loose chars in the console pass thru if we don't lock. It is also + * possible that we could hold the lock or be waiting for it when kgdb + * NEEDS to talk. Since kgdb locks down the world, it does not need locks. + * We do, of course have possible issues with interrupting a uart operation, + * but we will just depend on the uart status to help keep that straight. + + */ +static spinlock_t uart_interrupt_lock = SPIN_LOCK_UNLOCKED; +#ifdef CONFIG_SMP +extern spinlock_t kgdb_spinlock; +#endif + +static int +read_char(struct async_struct *info) +{ + int chr; + unsigned long flags; + local_irq_save(flags); +#ifdef CONFIG_SMP + if (!spin_is_locked(&kgdb_spinlock)) { + spin_lock(&uart_interrupt_lock); + } +#endif + if (atomic_read(&gdb_buf_in_cnt) != 0) { /* intr routine has q'd chars */ + chr = gdb_buf[gdb_buf_out_inx++]; + gdb_buf_out_inx &= (GDB_BUF_SIZE - 1); + atomic_dec(&gdb_buf_in_cnt); + } else { + chr = read_data_bfr(info); + } +#ifdef CONFIG_SMP + if (!spin_is_locked(&kgdb_spinlock)) { + spin_unlock(&uart_interrupt_lock); + } +#endif + local_irq_restore(flags); + return (chr); +} + +/* + * Wait until the interface can accept a char, then write it. + */ +static void +write_char(struct async_struct *info, int chr) +{ + while (!(inb_p(info->port + UART_LSR) & UART_LSR_THRE)) ; + + outb_p(chr, info->port + UART_TX); + +} /* write_char */ + +/* + * Mostly we don't need a spinlock, but since the console goes + * thru here with interrutps on, well, we need to catch those + * chars. + */ +/* + * This is the receiver interrupt routine for the GDB stub. + * It will receive a limited number of characters of input + * from the gdb host machine and save them up in a buffer. + * + * When the gdb stub routine tty_getDebugChar() is called it + * draws characters out of the buffer until it is empty and + * then reads directly from the serial port. + * + * We do not attempt to write chars from the interrupt routine + * since the stubs do all of that via tty_putDebugChar() which + * writes one byte after waiting for the interface to become + * ready. + * + * The debug stubs like to run with interrupts disabled since, + * after all, they run as a consequence of a breakpoint in + * the kernel. + * + * Perhaps someone who knows more about the tty driver than I + * care to learn can make this work for any low level serial + * driver. + */ +static irqreturn_t +gdb_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct async_struct *info; + unsigned long flags; + + info = gdb_async_info; + if (!info || !info->tty || irq != gdb_async_irq) + return IRQ_NONE; + + local_irq_save(flags); + spin_lock(&uart_interrupt_lock); + do { + int chr = read_data_bfr(info); + intprintk(("Debug char on int: %x hex\n", chr)); + if (chr < 0) + continue; + + if (chr == 3) { /* Ctrl-C means remote interrupt */ + BREAKPOINT; + continue; + } + + if (atomic_read(&gdb_buf_in_cnt) >= GDB_BUF_SIZE) { + /* buffer overflow tosses early char */ + read_char(info); + } + gdb_buf[gdb_buf_in_inx++] = chr; + gdb_buf_in_inx &= (GDB_BUF_SIZE - 1); + } while (inb_p(info->port + UART_IIR) & UART_IIR_RDI); + spin_unlock(&uart_interrupt_lock); + local_irq_restore(flags); + return IRQ_HANDLED; +} /* gdb_interrupt */ + +/* + * Just a NULL routine for testing. + */ +void +gdb_null(void) +{ +} /* gdb_null */ + +/* These structure are filled in with values defined in asm/kgdb_local.h + */ +static struct serial_state state = SB_STATE; +static struct async_struct local_info = SB_INFO; +static int ok_to_enable_ints = 0; +static void kgdb_enable_ints_now(void); + +extern char *kgdb_version; +/* + * Hook an IRQ for KGDB. + * + * This routine is called from tty_putDebugChar, below. + */ +static int ints_disabled = 1; +int +gdb_hook_interrupt(struct async_struct *info, int verb) +{ + struct serial_state *state = info->state; + unsigned long flags; + int port; +#ifdef TEST_EXISTANCE + int scratch, scratch2; +#endif + + /* The above fails if memory managment is not set up yet. + * Rather than fail the set up, just keep track of the fact + * and pick up the interrupt thing later. + */ + gdb_async_info = info; + port = gdb_async_info->port; + gdb_async_irq = state->irq; + if (verb) { + printk("kgdb %s : port =%x, IRQ=%d, divisor =%d\n", + kgdb_version, + port, + gdb_async_irq, gdb_async_info->state->custom_divisor); + } + local_irq_save(flags); +#ifdef TEST_EXISTANCE + /* Existance test */ + /* Should not need all this, but just in case.... */ + + scratch = inb_p(port + UART_IER); + outb_px(port + UART_IER, 0); + outb_px(0xff, 0x080); + scratch2 = inb_p(port + UART_IER); + outb_px(port + UART_IER, scratch); + if (scratch2) { + printk + ("gdb_hook_interrupt: Could not clear IER, not a UART!\n"); + local_irq_restore(flags); + return 1; /* We failed; there's nothing here */ + } + scratch2 = inb_p(port + UART_LCR); + outb_px(port + UART_LCR, 0xBF); /* set up for StarTech test */ + outb_px(port + UART_EFR, 0); /* EFR is the same as FCR */ + outb_px(port + UART_LCR, 0); + outb_px(port + UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = inb_p(port + UART_IIR) >> 6; + if (scratch == 1) { + printk("gdb_hook_interrupt: Undefined UART type!" + " Not a UART! \n"); + local_irq_restore(flags); + return 1; + } else { + dbprintk(("gdb_hook_interrupt: UART type " + "is %d where 0=16450, 2=16550 3=16550A\n", scratch)); + } + scratch = inb_p(port + UART_MCR); + outb_px(port + UART_MCR, UART_MCR_LOOP | scratch); + outb_px(port + UART_MCR, UART_MCR_LOOP | 0x0A); + scratch2 = inb_p(port + UART_MSR) & 0xF0; + outb_px(port + UART_MCR, scratch); + if (scratch2 != 0x90) { + printk("gdb_hook_interrupt: " + "Loop back test failed! Not a UART!\n"); + local_irq_restore(flags); + return scratch2 + 1000; /* force 0 to fail */ + } +#endif /* test existance */ + program_uart(info); + local_irq_restore(flags); + + return (0); + +} /* gdb_hook_interrupt */ + +static void +program_uart(struct async_struct *info) +{ + int port = info->port; + + (void) inb_p(port + UART_RX); + outb_px(port + UART_IER, 0); + + (void) inb_p(port + UART_RX); /* serial driver comments say */ + (void) inb_p(port + UART_IIR); /* this clears the interrupt regs */ + (void) inb_p(port + UART_MSR); + outb_px(port + UART_LCR, UART_LCR_WLEN8 | UART_LCR_DLAB); + outb_px(port + UART_DLL, info->state->custom_divisor & 0xff); /* LS */ + outb_px(port + UART_DLM, info->state->custom_divisor >> 8); /* MS */ + outb_px(port + UART_MCR, info->MCR); + + outb_px(port + UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1 | UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR); /* set fcr */ + outb_px(port + UART_LCR, UART_LCR_WLEN8); /* reset DLAB */ + outb_px(port + UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1); /* set fcr */ + if (!ints_disabled) { + intprintk(("KGDB: Sending %d to port %x offset %d\n", + gdb_async_info->IER, + (int) gdb_async_info->port, UART_IER)); + outb_px(gdb_async_info->port + UART_IER, gdb_async_info->IER); + } + return; +} + +/* + * tty_getDebugChar + * + * This is a GDB stub routine. It waits for a character from the + * serial interface and then returns it. If there is no serial + * interface connection then it returns a bogus value which will + * almost certainly cause the system to hang. In the + */ +int kgdb_in_isr = 0; +int kgdb_in_lsr = 0; +extern spinlock_t kgdb_spinlock; + +/* Caller takes needed protections */ + +int +tty_getDebugChar(void) +{ + volatile int chr, dum, time, end_time; + + dbprintk(("tty_getDebugChar(port %x): ", gdb_async_info->port)); + + if (gdb_async_info == NULL) { + gdb_hook_interrupt(&local_info, 0); + } + /* + * This trick says if we wait a very long time and get + * no char, return the -1 and let the upper level deal + * with it. + */ + rdtsc(dum, time); + end_time = time + 2; + while (((chr = read_char(gdb_async_info)) == -1) && + (end_time - time) > 0) { + rdtsc(dum, time); + }; + /* + * This covers our butts if some other code messes with + * our uart, hay, it happens :o) + */ + if (chr == -1) + program_uart(gdb_async_info); + + dbprintk(("%c\n", chr > ' ' && chr < 0x7F ? chr : ' ')); + return (chr); + +} /* tty_getDebugChar */ + +static int count = 3; +static spinlock_t one_at_atime = SPIN_LOCK_UNLOCKED; + +static int __init +kgdb_enable_ints(void) +{ + if (kgdboe) { + return 0; + } + if (gdb_async_info == NULL) { + gdb_hook_interrupt(&local_info, 1); + } + ok_to_enable_ints = 1; + kgdb_enable_ints_now(); +#ifdef CONFIG_KGDB_USER_CONSOLE + kgdb_console_finit(); +#endif + return 0; +} + +#ifdef CONFIG_SERIAL_8250 +void shutdown_for_kgdb(struct async_struct *gdb_async_info); +#endif + +#ifdef CONFIG_DISCONTIGMEM +static inline int kgdb_mem_init_done(void) +{ + return highmem_start_page != NULL; +} +#else +static inline int kgdb_mem_init_done(void) +{ + return max_mapnr != 0; +} +#endif + +static void +kgdb_enable_ints_now(void) +{ + if (!spin_trylock(&one_at_atime)) + return; + if (!ints_disabled) + goto exit; + if (kgdb_mem_init_done() && + ints_disabled) { /* don't try till mem init */ +#ifdef CONFIG_SERIAL_8250 + /* + * The ifdef here allows the system to be configured + * without the serial driver. + * Don't make it a module, however, it will steal the port + */ + shutdown_for_kgdb(gdb_async_info); +#endif + ints_disabled = request_irq(gdb_async_info->state->irq, + gdb_interrupt, + IRQ_T(gdb_async_info), + "KGDB-stub", NULL); + intprintk(("KGDB: request_irq returned %d\n", ints_disabled)); + } + if (!ints_disabled) { + intprintk(("KGDB: Sending %d to port %x offset %d\n", + gdb_async_info->IER, + (int) gdb_async_info->port, UART_IER)); + outb_px(gdb_async_info->port + UART_IER, gdb_async_info->IER); + } + exit: + spin_unlock(&one_at_atime); +} + +/* + * tty_putDebugChar + * + * This is a GDB stub routine. It waits until the interface is ready + * to transmit a char and then sends it. If there is no serial + * interface connection then it simply returns to its caller, having + * pretended to send the char. Caller takes needed protections. + */ +void +tty_putDebugChar(int chr) +{ + dbprintk(("tty_putDebugChar(port %x): chr=%02x '%c', ints_on=%d\n", + gdb_async_info->port, + chr, + chr > ' ' && chr < 0x7F ? chr : ' ', ints_disabled ? 0 : 1)); + + if (gdb_async_info == NULL) { + gdb_hook_interrupt(&local_info, 0); + } + + write_char(gdb_async_info, chr); /* this routine will wait */ + count = (chr == '#') ? 0 : count + 1; + if ((count == 2)) { /* try to enable after */ + if (ints_disabled & ok_to_enable_ints) + kgdb_enable_ints_now(); /* try to enable after */ + + /* We do this a lot because, well we really want to get these + * interrupts. The serial driver will clear these bits when it + * initializes the chip. Every thing else it does is ok, + * but this. + */ + if (!ints_disabled) { + outb_px(gdb_async_info->port + UART_IER, + gdb_async_info->IER); + } + } + +} /* tty_putDebugChar */ + +/* + * This does nothing for the serial port, since it doesn't buffer. + */ + +void tty_flushDebugChar(void) +{ +} + +module_init(kgdb_enable_ints); --- linux-2.6.9-rc1/arch/i386/lib/Makefile 2004-08-24 00:01:53.000000000 -0700 +++ 25/arch/i386/lib/Makefile 2004-09-03 00:56:35.764723584 -0700 @@ -8,3 +8,4 @@ lib-y = checksum.o delay.o usercopy.o ge lib-$(CONFIG_X86_USE_3DNOW) += mmx.o lib-$(CONFIG_HAVE_DEC_LOCK) += dec_and_lock.o +lib-$(CONFIG_KGDB) += kgdb_serial.o --- linux-2.6.9-rc1/arch/i386/lib/mmx.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/i386/lib/mmx.c 2004-09-03 00:56:40.981930448 -0700 @@ -2,9 +2,9 @@ #include #include #include +#include #include -#include /* --- linux-2.6.9-rc1/arch/i386/lib/usercopy.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/arch/i386/lib/usercopy.c 2004-09-02 20:51:51.000000000 -0700 @@ -31,6 +31,7 @@ static inline int __movsl_is_ok(unsigned #define __do_strncpy_from_user(dst,src,count,res) \ do { \ int __d0, __d1, __d2; \ + might_sleep(); \ __asm__ __volatile__( \ " testl %1,%1\n" \ " jz 2f\n" \ @@ -119,6 +120,7 @@ strncpy_from_user(char *dst, const char #define __do_clear_user(addr,size) \ do { \ int __d0; \ + might_sleep(); \ __asm__ __volatile__( \ "0: rep; stosl\n" \ " movl %2,%0\n" \ --- linux-2.6.9-rc1/arch/i386/mach-default/topology.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/i386/mach-default/topology.c 2004-09-03 00:56:51.385348888 -0700 @@ -27,6 +27,7 @@ */ #include #include +#include #include struct i386_cpu cpu_devices[NR_CPUS]; --- linux-2.6.9-rc1/arch/i386/mach-es7000/es7000.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/i386/mach-es7000/es7000.h 2004-09-02 23:57:37.410635168 -0700 @@ -29,7 +29,7 @@ #define MIP_BUSY 1 #define MIP_SPIN 0xf0000 -#define MIP_VALID 0x0100000000000000 +#define MIP_VALID 0x0100000000000000ULL #define MIP_PORT(VALUE) ((VALUE >> 32) & 0xffff) #define MIP_RD_LO(VALUE) (VALUE & 0xffffffff) @@ -104,6 +104,13 @@ struct mip_reg { #define MIP_SW_APIC 0x1020b #define MIP_FUNC(VALUE) (VALUE & 0xff) +#if defined(CONFIG_X86_IO_APIC) && (defined(CONFIG_ACPI_INTERPRETER) || defined(CONFIG_ACPI_BOOT)) +#define IOAPIC_GSI_BOUND(ioapic) ((ioapic+1) * (nr_ioapic_registers[ioapic]-1)) +#define MAX_GSI_MAPSIZE 32 +#endif + +extern unsigned long io_apic_irqs; + extern int parse_unisys_oem (char *oemptr, int oem_entries); extern int find_unisys_acpi_oem_table(unsigned long *oem_addr, int *length); extern int es7000_start_cpu(int cpu, unsigned long eip); --- linux-2.6.9-rc1/arch/i386/mach-es7000/es7000plat.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/i386/mach-es7000/es7000plat.c 2004-09-02 23:57:37.411635016 -0700 @@ -51,27 +51,81 @@ struct mip_reg *host_reg; int mip_port; unsigned long mip_addr, host_addr; +EXPORT_SYMBOL(mip_reg); +EXPORT_SYMBOL(host_reg); +EXPORT_SYMBOL(mip_port); +EXPORT_SYMBOL(mip_addr); +EXPORT_SYMBOL(host_addr); + +#if defined(CONFIG_X86_IO_APIC) && (defined(CONFIG_ACPI_INTERPRETER) || defined(CONFIG_ACPI_BOOT)) +static unsigned long cycle_irqs = 0; +static unsigned long free_irqs = 0; +static int gsi_map[MAX_GSI_MAPSIZE] = { [0 ... MAX_GSI_MAPSIZE-1] = -1 }; + +/* + * GSI override for ES7000 platforms. + */ + +static int __init +es7000_gsi_override(int ioapic, int gsi) +{ + static int newgsi = 0; + + if (gsi_map[gsi] != -1) + gsi = gsi_map[gsi]; + else if (cycle_irqs ^ free_irqs) + { + newgsi = find_next_bit(&cycle_irqs, IOAPIC_GSI_BOUND(0), newgsi); + __set_bit(newgsi, &free_irqs); + gsi_map[gsi] = newgsi; + gsi = newgsi; + newgsi++; + Dprintk("es7000_gsi_override: free_irqs = 0x%lx\n", free_irqs); + } + + return gsi; +} + static int __init es7000_rename_gsi(int ioapic, int gsi) { - if (ioapic) + static int initialized = 0; + int i; + + /* + * These should NEVER be true at this point but we'd rather be + * safe than sorry. + */ + if (acpi_disabled || acpi_pci_disabled || acpi_noirq) return gsi; - else { - if (gsi == 0) - return 13; - if (gsi == 1) - return 16; - if (gsi == 4) - return 17; - if (gsi == 6) - return 18; - if (gsi == 7) - return 19; - if (gsi == 8) - return 20; + + if (ioapic) return gsi; - } + + if (!initialized) { + unsigned long tmp_irqs = 0; + + for (i = 0; i < nr_ioapic_registers[0]; i++) + __set_bit(mp_irqs[i].mpc_srcbusirq, &tmp_irqs); + + cycle_irqs = (~tmp_irqs & io_apic_irqs & ((1 << IOAPIC_GSI_BOUND(0)) - 1)); + + initialized = 1; + Dprintk("es7000_rename_gsi: cycle_irqs = 0x%lx\n", cycle_irqs); + } + + for (i = 0; i < nr_ioapic_registers[0]; i++) { + if (mp_irqs[i].mpc_srcbusirq == gsi) { + if (mp_irqs[i].mpc_dstirq == gsi) + return gsi; + else + return es7000_gsi_override(0, gsi); + } + } + + return gsi; } +#endif // (CONFIG_X86_IO_APIC) && (CONFIG_ACPI_INTERPRETER || CONFIG_ACPI_BOOT) /* * Parse the OEM Table @@ -237,7 +291,7 @@ es7000_mip_write(struct mip_reg *mip_reg } status = ((unsigned long long)mip_reg->off_0 & - (unsigned long long)0xffff0000000000) >> 48; + (unsigned long long)0xffff0000000000ULL) >> 48; mip_reg->off_38 = ((unsigned long long)mip_reg->off_38 & (unsigned long long)~MIP_VALID); return status; --- linux-2.6.9-rc1/arch/i386/mach-generic/bigsmp.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/i386/mach-generic/bigsmp.c 2004-09-02 20:51:51.000000000 -0700 @@ -13,15 +13,41 @@ #include #include #include +#include #include #include #include #include -int dmi_bigsmp; /* can be set by dmi scanners */ +static int dmi_bigsmp; /* can be set by dmi scanners */ + +static __init int hp_ht_bigsmp(struct dmi_system_id *d) +{ +#ifdef CONFIG_X86_GENERICARCH + printk(KERN_NOTICE "%s detected: force use of apic=bigsmp\n", d->ident); + dmi_bigsmp = 1; +#endif + return 0; +} + + +static struct dmi_system_id __initdata bigsmp_dmi_table[] = { + { hp_ht_bigsmp, "HP ProLiant DL760 G2", { + DMI_MATCH(DMI_BIOS_VENDOR, "HP"), + DMI_MATCH(DMI_BIOS_VERSION, "P44-"), + }}, + + { hp_ht_bigsmp, "HP ProLiant DL740", { + DMI_MATCH(DMI_BIOS_VENDOR, "HP"), + DMI_MATCH(DMI_BIOS_VERSION, "P47-"), + }}, + { } +}; + static __init int probe_bigsmp(void) { + dmi_check_system(bigsmp_dmi_table); return dmi_bigsmp; } --- linux-2.6.9-rc1/arch/i386/mach-voyager/voyager_smp.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/arch/i386/mach-voyager/voyager_smp.c 2004-09-02 20:51:51.000000000 -0700 @@ -523,15 +523,6 @@ start_secondary(void *unused) return cpu_idle(); } -static struct task_struct * __init -fork_by_hand(void) -{ - struct pt_regs regs; - /* don't care about the eip and regs settings since we'll - * never reschedule the forked task. */ - return copy_process(CLONE_VM|CLONE_IDLETASK, 0, ®s, 0, NULL, NULL); -} - /* Routine to kick start the given CPU and wait for it to report ready * (or timeout in startup). When this routine returns, the requested @@ -587,16 +578,10 @@ do_boot_cpu(__u8 cpu) hijack_source.idt.Segment = (start_phys_address >> 4) & 0xFFFF; cpucount++; - idle = fork_by_hand(); + idle = fork_idle(cpu); if(IS_ERR(idle)) panic("failed fork for CPU%d", cpu); - - wake_up_forked_process(idle); - - init_idle(idle, cpu); - idle->thread.eip = (unsigned long) start_secondary; - unhash_process(idle); /* init_tasks (in sched.c) is indexed logically */ stack_start.esp = (void *) idle->thread.esp; @@ -1302,8 +1287,7 @@ smp_local_timer_interrupt(struct pt_regs int cpu = smp_processor_id(); long weight; - x86_do_profile(regs); - + profile_tick(CPU_PROFILING, regs); if (--per_cpu(prof_counter, cpu) <= 0) { /* * The multiplier may have changed since the last time we got --- linux-2.6.9-rc1/arch/i386/Makefile 2004-08-24 00:02:26.000000000 -0700 +++ 25/arch/i386/Makefile 2004-09-03 00:56:35.764723584 -0700 @@ -98,6 +98,9 @@ core-$(CONFIG_X86_ES7000) := arch/i386/m # default subarch .h files mflags-y += -Iinclude/asm-i386/mach-default +mflags-$(CONFIG_KGDB) += -gdwarf-2 +mflags-$(CONFIG_KGDB_MORE) += $(shell echo $(CONFIG_KGDB_OPTIONS) | sed -e 's/"//g') + head-y := arch/i386/kernel/head.o arch/i386/kernel/init_task.o libs-y += arch/i386/lib/ --- linux-2.6.9-rc1/arch/i386/mm/discontig.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/arch/i386/mm/discontig.c 2004-09-03 00:56:51.385348888 -0700 @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -417,15 +418,15 @@ void __init zone_sizes_init(void) * remapped KVA area - mbligh */ if (!nid) - free_area_init_node(nid, NODE_DATA(nid), 0, - zones_size, start, zholes_size); + free_area_init_node(nid, NODE_DATA(nid), + zones_size, start, zholes_size); else { unsigned long lmem_map; lmem_map = (unsigned long)node_remap_start_vaddr[nid]; lmem_map += sizeof(pg_data_t) + PAGE_SIZE - 1; lmem_map &= PAGE_MASK; - free_area_init_node(nid, NODE_DATA(nid), - (struct page *)lmem_map, zones_size, + NODE_DATA(nid)->node_mem_map = (struct page *)lmem_map; + free_area_init_node(nid, NODE_DATA(nid), zones_size, start, zholes_size); } } --- linux-2.6.9-rc1/arch/i386/mm/fault.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/arch/i386/mm/fault.c 2004-09-03 00:56:40.981930448 -0700 @@ -24,8 +24,8 @@ #include #include -#include #include +#include extern void die(const char *,struct pt_regs *,long); @@ -226,6 +226,9 @@ asmlinkage void do_page_fault(struct pt_ /* get the address */ __asm__("movl %%cr2,%0":"=r" (address)); + if (notify_die(DIE_PAGE_FAULT, "page fault", regs, error_code, 14, + SIGSEGV) == NOTIFY_OK) + return; /* It's safe to allow irq's after cr2 has been saved */ if (regs->eflags & (X86_EFLAGS_IF|VM_MASK)) local_irq_enable(); @@ -427,6 +430,12 @@ no_context: * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice. */ +#ifdef CONFIG_KGDB + if (!user_mode(regs)){ + kgdb_handle_exception(14,SIGBUS, error_code, regs); + return; + } +#endif bust_spinlocks(1); @@ -513,12 +522,13 @@ vmalloc_fault: * an interrupt in the middle of a task switch.. */ int index = pgd_index(address); + unsigned long pgd_paddr; pgd_t *pgd, *pgd_k; pmd_t *pmd, *pmd_k; pte_t *pte_k; - asm("movl %%cr3,%0":"=r" (pgd)); - pgd = index + (pgd_t *)__va(pgd); + asm("movl %%cr3,%0":"=r" (pgd_paddr)); + pgd = index + (pgd_t *)__va(pgd_paddr); pgd_k = init_mm.pgd + index; if (!pgd_present(*pgd_k)) --- linux-2.6.9-rc1/arch/i386/mm/init.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/i386/mm/init.c 2004-09-03 00:56:41.908789544 -0700 @@ -223,6 +223,25 @@ static inline int page_is_ram(unsigned l return 0; } +/* + * devmem_is_allowed() checks to see if /dev/mem access to a certain address is + * valid. The argument is a physical page number. + * + * + * On x86, access has to be given to the first megabyte of ram because that area + * contains bios code and data regions used by X and dosemu and similar apps. + * Access has to be given to non-kernel-ram areas as well, these contain the PCI + * mmio resources as well as potential bios/acpi data regions. + */ +int devmem_is_allowed(unsigned long pagenr) +{ + if (pagenr <= 256) + return 1; + if (!page_is_ram(pagenr)) + return 1; + return 0; +} + #ifdef CONFIG_HIGHMEM pte_t *kmap_pte; pgprot_t kmap_prot; --- linux-2.6.9-rc1/arch/i386/mm/Makefile 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/i386/mm/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -2,7 +2,7 @@ # Makefile for the linux i386-specific parts of the memory manager. # -obj-y := init.o pgtable.o fault.o ioremap.o extable.o pageattr.o +obj-y := init.o pgtable.o fault.o ioremap.o extable.o pageattr.o mmap.o obj-$(CONFIG_DISCONTIGMEM) += discontig.o obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/i386/mm/mmap.c 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,71 @@ +/* + * linux/arch/i386/mm/mmap.c + * + * flexible mmap layout support + * + * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Started by Ingo Molnar + */ + +#include +#include + +/* + * Top of mmap area (just below the process stack). + * + * Leave an at least ~128 MB hole. + */ +#define MIN_GAP (128*1024*1024) +#define MAX_GAP (TASK_SIZE/6*5) + +static inline unsigned long mmap_base(struct mm_struct *mm) +{ + unsigned long gap = current->rlim[RLIMIT_STACK].rlim_cur; + + if (gap < MIN_GAP) + gap = MIN_GAP; + else if (gap > MAX_GAP) + gap = MAX_GAP; + + return TASK_SIZE - (gap & PAGE_MASK); +} + +/* + * This function, called very early during the creation of a new + * process VM image, sets up which VM layout function to use: + */ +void arch_pick_mmap_layout(struct mm_struct *mm) +{ + /* + * Fall back to the standard layout if the personality + * bit is set, or if the expected stack growth is unlimited: + */ + if (sysctl_legacy_va_layout || + (current->personality & ADDR_COMPAT_LAYOUT) || + current->rlim[RLIMIT_STACK].rlim_cur == RLIM_INFINITY) { + mm->mmap_base = TASK_UNMAPPED_BASE; + mm->get_unmapped_area = arch_get_unmapped_area; + mm->unmap_area = arch_unmap_area; + } else { + mm->mmap_base = mmap_base(mm); + mm->get_unmapped_area = arch_get_unmapped_area_topdown; + mm->unmap_area = arch_unmap_area_topdown; + } +} --- linux-2.6.9-rc1/arch/i386/mm/pageattr.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/i386/mm/pageattr.c 2004-09-02 20:51:51.000000000 -0700 @@ -114,7 +114,7 @@ __change_page_attr(struct page *page, pg kpte = lookup_address(address); if (!kpte) return -EINVAL; - kpte_page = virt_to_page(((unsigned long)kpte) & PAGE_MASK); + kpte_page = virt_to_page(kpte); if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL)) { if ((pte_val(*kpte) & _PAGE_PSE) == 0) { pte_t old = *kpte; --- linux-2.6.9-rc1/arch/i386/oprofile/op_model_p4.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/arch/i386/oprofile/op_model_p4.c 2004-09-02 20:51:51.000000000 -0700 @@ -419,9 +419,28 @@ static void p4_fill_in_addresses(struct msrs->controls[i].addr = addr; } - /* 43 ESCR registers in three discontiguous group */ + /* 43 ESCR registers in three or four discontiguous group */ for (addr = MSR_P4_BSU_ESCR0 + stag; - addr <= MSR_P4_SSU_ESCR0; ++i, addr += addr_increment()) { + addr < MSR_P4_IQ_ESCR0; ++i, addr += addr_increment()) { + msrs->controls[i].addr = addr; + } + + /* no IQ_ESCR0/1 on some models, we save a seconde time BSU_ESCR0/1 + * to avoid special case in nmi_{save|restore}_registers() */ + if (boot_cpu_data.x86_model >= 0x3) { + for (addr = MSR_P4_BSU_ESCR0 + stag; + addr <= MSR_P4_BSU_ESCR1; ++i, addr += addr_increment()) { + msrs->controls[i].addr = addr; + } + } else { + for (addr = MSR_P4_IQ_ESCR0 + stag; + addr <= MSR_P4_IQ_ESCR1; ++i, addr += addr_increment()) { + msrs->controls[i].addr = addr; + } + } + + for (addr = MSR_P4_RAT_ESCR0 + stag; + addr <= MSR_P4_SSU_ESCR0; ++i, addr += addr_increment()) { msrs->controls[i].addr = addr; } @@ -553,7 +572,18 @@ static void p4_setup_ctrs(struct op_msrs /* clear all escrs (including those outside our concern) */ for (addr = MSR_P4_BSU_ESCR0 + stag; - addr <= MSR_P4_SSU_ESCR0; addr += addr_increment()) { + addr < MSR_P4_IQ_ESCR0; addr += addr_increment()) { + wrmsr(addr, 0, 0); + } + + /* On older models clear also MSR_P4_IQ_ESCR0/1 */ + if (boot_cpu_data.x86_model < 0x3) { + wrmsr(MSR_P4_IQ_ESCR0, 0, 0); + wrmsr(MSR_P4_IQ_ESCR1, 0, 0); + } + + for (addr = MSR_P4_RAT_ESCR0 + stag; + addr <= MSR_P4_SSU_ESCR0; ++i, addr += addr_increment()) { wrmsr(addr, 0, 0); } --- linux-2.6.9-rc1/arch/i386/pci/acpi.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/i386/pci/acpi.c 2004-09-03 00:56:53.661002936 -0700 @@ -15,6 +15,7 @@ struct pci_bus * __devinit pci_acpi_scan return pcibios_scan_root(busnum); } +extern int pci_routeirq; static int __init pci_acpi_init(void) { struct pci_dev *dev = NULL; @@ -30,13 +31,27 @@ static int __init pci_acpi_init(void) pcibios_scanned++; pcibios_enable_irq = acpi_pci_irq_enable; - /* - * PCI IRQ routing is set up by pci_enable_device(), but we - * also do it here in case there are still broken drivers that - * don't use pci_enable_device(). - */ - while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) - acpi_pci_irq_enable(dev); + if (pci_routeirq) { + /* + * PCI IRQ routing is set up by pci_enable_device(), but we + * also do it here in case there are still broken drivers that + * don't use pci_enable_device(). + */ + printk(KERN_INFO "** Routing PCI interrupts for all devices because \"pci=routeirq\"\n"); + printk(KERN_INFO "** was specified. If this was required to make a driver work,\n"); + printk(KERN_INFO "** please email the output of \"lspci\" to bjorn.helgaas@hp.com\n"); + printk(KERN_INFO "** so I can fix the driver.\n"); + while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) + acpi_pci_irq_enable(dev); + } else { + printk(KERN_INFO "** PCI interrupts are no longer routed automatically. If this\n"); + printk(KERN_INFO "** causes a device to stop working, it is probably because the\n"); + printk(KERN_INFO "** driver failed to call pci_enable_device(). As a temporary\n"); + printk(KERN_INFO "** workaround, the \"pci=routeirq\" argument restores the old\n"); + printk(KERN_INFO "** behavior. If this argument makes the device work again,\n"); + printk(KERN_INFO "** please email the output of \"lspci\" to bjorn.helgaas@hp.com\n"); + printk(KERN_INFO "** so I can fix the driver.\n"); + } #ifdef CONFIG_X86_IO_APIC if (acpi_ioapic) --- linux-2.6.9-rc1/arch/i386/pci/common.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/i386/pci/common.c 2004-09-03 00:56:53.661002936 -0700 @@ -23,6 +23,7 @@ extern void pcibios_sort(void); unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2 | PCI_PROBE_MMCONF; +int pci_routeirq; int pcibios_last_bus = -1; struct pci_bus *pci_root_bus = NULL; struct pci_raw_ops *raw_pci_ops; @@ -227,6 +228,9 @@ char * __devinit pcibios_setup(char *st } else if (!strcmp(str, "assign-busses")) { pci_probe |= PCI_ASSIGN_ALL_BUSSES; return NULL; + } else if (!strcmp(str, "routeirq")) { + pci_routeirq = 1; + return NULL; } return str; } --- linux-2.6.9-rc1/arch/i386/pci/i386.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/i386/pci/i386.c 2004-09-02 20:51:51.000000000 -0700 @@ -142,7 +142,7 @@ static void __init pcibios_allocate_reso DBG("PCI: Resource %08lx-%08lx (f=%lx, d=%d, p=%d)\n", r->start, r->end, r->flags, disabled, pass); pr = pci_find_parent_resource(dev, r); - if (!pr || request_resource(pr, r) < 0) { + if (!pr || insert_resource(pr, r) < 0) { printk(KERN_ERR "PCI: Cannot allocate resource region %d of device %s\n", idx, pci_name(dev)); /* We'll assign a new address later */ r->end -= r->start; --- linux-2.6.9-rc1/arch/i386/power/Makefile 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/i386/power/Makefile 2004-09-03 00:56:33.438077288 -0700 @@ -1,3 +1,2 @@ obj-$(CONFIG_PM) += cpu.o -obj-$(CONFIG_PM_DISK) += pmdisk.o obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o --- linux-2.6.9-rc1/arch/i386/power/pmdisk.S 2004-08-24 00:02:48.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,56 +0,0 @@ -/* Originally gcc generated, modified by hand */ - -#include -#include -#include - - .text - -ENTRY(pmdisk_arch_suspend) - cmpl $0,4(%esp) - jne .L1450 - - movl %esp, saved_context_esp - movl %ebx, saved_context_ebx - movl %ebp, saved_context_ebp - movl %esi, saved_context_esi - movl %edi, saved_context_edi - pushfl ; popl saved_context_eflags - - call pmdisk_suspend - jmp .L1449 - .p2align 4,,7 -.L1450: - movl $swsusp_pg_dir-__PAGE_OFFSET,%ecx - movl %ecx,%cr3 - - movl pm_pagedir_nosave,%ebx - xorl %eax, %eax - xorl %edx, %edx - .p2align 4,,7 -.L1455: - movl 4(%ebx,%edx),%edi - movl (%ebx,%edx),%esi - - movl $1024, %ecx - rep - movsl - - movl %cr3, %ecx; - movl %ecx, %cr3; # flush TLB - - incl %eax - addl $16, %edx - cmpl pmdisk_pages,%eax - jb .L1455 - .p2align 4,,7 -.L1453: - movl saved_context_esp, %esp - movl saved_context_ebp, %ebp - movl saved_context_ebx, %ebx - movl saved_context_esi, %esi - movl saved_context_edi, %edi - pushl saved_context_eflags ; popfl - call pmdisk_resume -.L1449: - ret --- linux-2.6.9-rc1/arch/i386/power/swsusp.S 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/i386/power/swsusp.S 2004-09-03 00:56:33.439077136 -0700 @@ -15,83 +15,47 @@ .text -ENTRY(do_magic) - pushl %ebx - cmpl $0,8(%esp) - jne resume - call do_magic_suspend_1 - call save_processor_state +ENTRY(swsusp_arch_suspend) movl %esp, saved_context_esp - movl %eax, saved_context_eax movl %ebx, saved_context_ebx - movl %ecx, saved_context_ecx - movl %edx, saved_context_edx movl %ebp, saved_context_ebp movl %esi, saved_context_esi movl %edi, saved_context_edi pushfl ; popl saved_context_eflags - call do_magic_suspend_2 - popl %ebx + call swsusp_save ret -resume: +ENTRY(swsusp_arch_resume) movl $swsusp_pg_dir-__PAGE_OFFSET,%ecx movl %ecx,%cr3 - call do_magic_resume_1 - movl $0,loop - cmpl $0,nr_copy_pages - je copy_done -copy_loop: - movl $0,loop2 + movl pagedir_nosave,%ebx + xorl %eax, %eax + xorl %edx, %edx .p2align 4,,7 -copy_one_page: - movl pagedir_nosave,%ecx - movl loop,%eax - movl loop2,%edx - sall $4,%eax - movl 4(%ecx,%eax),%ebx - movl (%ecx,%eax),%eax - movb (%edx,%eax),%al - movb %al,(%edx,%ebx) - - movl loop2,%eax - leal 1(%eax),%edx - movl %edx,loop2 - movl %edx,%eax - cmpl $4095,%eax - jbe copy_one_page - movl loop,%eax - leal 1(%eax),%edx - movl %edx,loop - movl %edx,%eax - cmpl nr_copy_pages,%eax - jb copy_loop -copy_done: - movl $__USER_DS,%eax +copy_loop: + movl 4(%ebx,%edx),%edi + movl (%ebx,%edx),%esi + + movl $1024, %ecx + rep + movsl + + incl %eax + addl $16, %edx + cmpl nr_copy_pages,%eax + jb copy_loop + .p2align 4,,7 - movw %ax, %ds - movw %ax, %es movl saved_context_esp, %esp movl saved_context_ebp, %ebp - movl saved_context_eax, %eax movl saved_context_ebx, %ebx - movl saved_context_ecx, %ecx - movl saved_context_edx, %edx movl saved_context_esi, %esi movl saved_context_edi, %edi - call restore_processor_state + pushl saved_context_eflags ; popfl - call do_magic_resume_2 - popl %ebx + call swsusp_restore ret - - .section .data.nosave -loop: - .quad 0 -loop2: - .quad 0 - .previous --- linux-2.6.9-rc1/arch/ia64/configs/generic_defconfig 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/ia64/configs/generic_defconfig 2004-09-03 00:56:30.904462456 -0700 @@ -1,5 +1,7 @@ # # Automatically generated make config: don't edit +# Linux kernel version: 2.6.9-rc1 +# Tue Aug 24 15:08:24 2004 # # @@ -126,6 +128,7 @@ CONFIG_PCI_NAMES=y CONFIG_HOTPLUG_PCI=m # CONFIG_HOTPLUG_PCI_FAKE is not set CONFIG_HOTPLUG_PCI_ACPI=m +# CONFIG_HOTPLUG_PCI_ACPI_IBM is not set # CONFIG_HOTPLUG_PCI_CPCI is not set # CONFIG_HOTPLUG_PCI_PCIE is not set # CONFIG_HOTPLUG_PCI_SHPC is not set @@ -172,6 +175,7 @@ CONFIG_BLK_DEV_LOOP=m CONFIG_BLK_DEV_CRYPTOLOOP=m CONFIG_BLK_DEV_NBD=m # CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_UB is not set CONFIG_BLK_DEV_RAM=m CONFIG_BLK_DEV_RAM_SIZE=4096 @@ -313,6 +317,7 @@ CONFIG_BLK_DEV_MD=m CONFIG_MD_LINEAR=m CONFIG_MD_RAID0=m CONFIG_MD_RAID1=m +# CONFIG_MD_RAID10 is not set CONFIG_MD_RAID5=m CONFIG_MD_RAID6=m CONFIG_MD_MULTIPATH=m @@ -365,6 +370,7 @@ CONFIG_SYN_COOKIES=y # CONFIG_INET_AH is not set # CONFIG_INET_ESP is not set # CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set # CONFIG_IPV6 is not set # CONFIG_NETFILTER is not set @@ -767,6 +773,7 @@ CONFIG_USB=m CONFIG_USB_DEVICEFS=y # CONFIG_USB_BANDWIDTH is not set # CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set # # USB Host Controller Drivers @@ -919,6 +926,8 @@ CONFIG_UDF_NLS=y CONFIG_FAT_FS=y # CONFIG_MSDOS_FS is not set CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" CONFIG_NTFS_FS=m # CONFIG_NTFS_DEBUG is not set # CONFIG_NTFS_RW is not set @@ -946,7 +955,6 @@ CONFIG_RAMFS=y # CONFIG_BEFS_FS is not set # CONFIG_BFS_FS is not set # CONFIG_EFS_FS is not set -# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set # CONFIG_CRAMFS is not set # CONFIG_VXFS_FS is not set # CONFIG_HPFS_FS is not set @@ -971,6 +979,7 @@ CONFIG_EXPORTFS=m CONFIG_SUNRPC=m CONFIG_SUNRPC_GSS=m CONFIG_RPCSEC_GSS_KRB5=m +# CONFIG_RPCSEC_GSS_SPKM3 is not set CONFIG_SMB_FS=m CONFIG_SMB_NLS_DEFAULT=y CONFIG_SMB_NLS_REMOTE="cp437" @@ -1007,7 +1016,7 @@ CONFIG_EFI_PARTITION=y # CONFIG_NLS=y CONFIG_NLS_DEFAULT="iso8859-1" -CONFIG_NLS_CODEPAGE_437=m +CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_CODEPAGE_737=m CONFIG_NLS_CODEPAGE_775=m CONFIG_NLS_CODEPAGE_850=m @@ -1031,7 +1040,7 @@ CONFIG_NLS_ISO8859_8=m CONFIG_NLS_CODEPAGE_1250=m CONFIG_NLS_CODEPAGE_1251=m # CONFIG_NLS_ASCII is not set -CONFIG_NLS_ISO8859_1=m +CONFIG_NLS_ISO8859_1=y CONFIG_NLS_ISO8859_2=m CONFIG_NLS_ISO8859_3=m CONFIG_NLS_ISO8859_4=m @@ -1068,18 +1077,18 @@ CONFIG_CRC32=y # # Kernel hacking # -CONFIG_IA64_GRANULE_16MB=y -# CONFIG_IA64_GRANULE_64MB is not set CONFIG_DEBUG_KERNEL=y -# CONFIG_IA64_PRINT_HAZARDS is not set -# CONFIG_DISABLE_VHPT is not set CONFIG_MAGIC_SYSRQ=y # CONFIG_DEBUG_SLAB is not set # CONFIG_DEBUG_SPINLOCK is not set # CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_INFO is not set +CONFIG_IA64_GRANULE_16MB=y +# CONFIG_IA64_GRANULE_64MB is not set +# CONFIG_IA64_PRINT_HAZARDS is not set +# CONFIG_DISABLE_VHPT is not set # CONFIG_IA64_DEBUG_CMPXCHG is not set # CONFIG_IA64_DEBUG_IRQ is not set -# CONFIG_DEBUG_INFO is not set CONFIG_SYSVIPC_COMPAT=y # @@ -1102,11 +1111,12 @@ CONFIG_CRYPTO_DES=m # CONFIG_CRYPTO_BLOWFISH is not set # CONFIG_CRYPTO_TWOFISH is not set # CONFIG_CRYPTO_SERPENT is not set -# CONFIG_CRYPTO_AES_GENERIC is not set +# CONFIG_CRYPTO_AES is not set # CONFIG_CRYPTO_CAST5 is not set # CONFIG_CRYPTO_CAST6 is not set # CONFIG_CRYPTO_TEA is not set # CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_KHAZAD is not set # CONFIG_CRYPTO_DEFLATE is not set # CONFIG_CRYPTO_MICHAEL_MIC is not set # CONFIG_CRYPTO_CRC32C is not set --- linux-2.6.9-rc1/arch/ia64/configs/sn2_defconfig 2004-08-24 00:01:51.000000000 -0700 +++ 25/arch/ia64/configs/sn2_defconfig 2004-09-03 00:56:30.905462304 -0700 @@ -1,5 +1,7 @@ # # Automatically generated make config: don't edit +# Linux kernel version: 2.6.9-rc1 +# Fri Aug 27 22:00:00 2004 # # @@ -7,7 +9,6 @@ # CONFIG_EXPERIMENTAL=y CONFIG_CLEAN_COMPILE=y -CONFIG_STANDALONE=y # # General setup @@ -74,8 +75,8 @@ CONFIG_IA64_SGI_SN_SIM=y CONFIG_FORCE_MAX_ZONEORDER=18 CONFIG_SMP=y CONFIG_NR_CPUS=512 -# CONFIG_HOTPLUG_CPU is not set -# CONFIG_PREEMPT is not set +CONFIG_HOTPLUG_CPU=y +CONFIG_PREEMPT=y CONFIG_HAVE_DEC_LOCK=y CONFIG_IA32_SUPPORT=y CONFIG_COMPAT=y @@ -115,7 +116,7 @@ CONFIG_ACPI_SYSTEM=y # CONFIG_PCI=y CONFIG_PCI_DOMAINS=y -# CONFIG_PCI_USE_VECTOR is not set +# CONFIG_PCI_MSI is not set CONFIG_PCI_LEGACY_PROC=y CONFIG_PCI_NAMES=y @@ -142,6 +143,7 @@ CONFIG_HOTPLUG_PCI_SGI=y # # Generic Driver Options # +CONFIG_STANDALONE=y CONFIG_PREVENT_FIRMWARE_BUILD=y CONFIG_FW_LOADER=m # CONFIG_DEBUG_DRIVER is not set @@ -171,6 +173,7 @@ CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_CRYPTOLOOP=m CONFIG_BLK_DEV_NBD=m # CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_UB is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y @@ -185,7 +188,8 @@ CONFIG_BLK_DEV_IDE=y # Please see Documentation/ide.txt for help/info on IDE drives # # CONFIG_BLK_DEV_IDE_SATA is not set -# CONFIG_BLK_DEV_IDEDISK is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set CONFIG_BLK_DEV_IDECD=y # CONFIG_BLK_DEV_IDETAPE is not set # CONFIG_BLK_DEV_IDEFLOPPY is not set @@ -316,6 +320,7 @@ CONFIG_BLK_DEV_MD=y CONFIG_MD_LINEAR=y CONFIG_MD_RAID0=y CONFIG_MD_RAID1=y +# CONFIG_MD_RAID10 is not set CONFIG_MD_RAID5=y # CONFIG_MD_RAID6 is not set CONFIG_MD_MULTIPATH=y @@ -368,11 +373,13 @@ CONFIG_SYN_COOKIES=y # CONFIG_INET_AH is not set # CONFIG_INET_ESP is not set # CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set CONFIG_IPV6=m # CONFIG_IPV6_PRIVACY is not set # CONFIG_INET6_AH is not set # CONFIG_INET6_ESP is not set # CONFIG_INET6_IPCOMP is not set +# CONFIG_INET6_TUNNEL is not set # CONFIG_IPV6_TUNNEL is not set # CONFIG_NETFILTER is not set @@ -538,6 +545,7 @@ CONFIG_SGI_SNSC=y # Non-8250 serial port support # CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y CONFIG_SERIAL_SGI_L1_CONSOLE=y CONFIG_UNIX98_PTYS=y CONFIG_LEGACY_PTYS=y @@ -601,7 +609,6 @@ CONFIG_MAX_RAW_DEVS=256 # Console display driver support # # CONFIG_VGA_CONSOLE is not set -# CONFIG_MDA_CONSOLE is not set CONFIG_DUMMY_CONSOLE=y # @@ -621,6 +628,7 @@ CONFIG_USB=m # CONFIG_USB_DEVICEFS is not set # CONFIG_USB_BANDWIDTH is not set # CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG is not set # # USB Host Controller Drivers @@ -717,7 +725,7 @@ CONFIG_USB_HIDINPUT=y # # File systems # -CONFIG_EXT2_FS=m +CONFIG_EXT2_FS=y CONFIG_EXT2_FS_XATTR=y CONFIG_EXT2_FS_POSIX_ACL=y CONFIG_EXT2_FS_SECURITY=y @@ -792,7 +800,6 @@ CONFIG_RAMFS=y # CONFIG_BEFS_FS is not set # CONFIG_BFS_FS is not set # CONFIG_EFS_FS is not set -# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set # CONFIG_CRAMFS is not set # CONFIG_VXFS_FS is not set # CONFIG_HPFS_FS is not set @@ -817,10 +824,12 @@ CONFIG_EXPORTFS=m CONFIG_SUNRPC=m CONFIG_SUNRPC_GSS=m CONFIG_RPCSEC_GSS_KRB5=m +# CONFIG_RPCSEC_GSS_SPKM3 is not set CONFIG_SMB_FS=m # CONFIG_SMB_NLS_DEFAULT is not set CONFIG_CIFS=m # CONFIG_CIFS_STATS is not set +# CONFIG_CIFS_XATTR is not set # CONFIG_CIFS_POSIX is not set # CONFIG_NCP_FS is not set # CONFIG_CODA_FS is not set @@ -907,18 +916,18 @@ CONFIG_ZLIB_DEFLATE=m # # Kernel hacking # -CONFIG_IA64_GRANULE_16MB=y -# CONFIG_IA64_GRANULE_64MB is not set CONFIG_DEBUG_KERNEL=y -# CONFIG_IA64_PRINT_HAZARDS is not set -# CONFIG_DISABLE_VHPT is not set CONFIG_MAGIC_SYSRQ=y # CONFIG_DEBUG_SLAB is not set # CONFIG_DEBUG_SPINLOCK is not set # CONFIG_DEBUG_SPINLOCK_SLEEP is not set +CONFIG_DEBUG_INFO=y +CONFIG_IA64_GRANULE_16MB=y +# CONFIG_IA64_GRANULE_64MB is not set +# CONFIG_IA64_PRINT_HAZARDS is not set +# CONFIG_DISABLE_VHPT is not set # CONFIG_IA64_DEBUG_CMPXCHG is not set # CONFIG_IA64_DEBUG_IRQ is not set -CONFIG_DEBUG_INFO=y CONFIG_SYSVIPC_COMPAT=y # @@ -937,6 +946,7 @@ CONFIG_CRYPTO_MD5=m CONFIG_CRYPTO_SHA1=m # CONFIG_CRYPTO_SHA256 is not set # CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_WHIRLPOOL is not set CONFIG_CRYPTO_DES=m # CONFIG_CRYPTO_BLOWFISH is not set # CONFIG_CRYPTO_TWOFISH is not set @@ -946,6 +956,7 @@ CONFIG_CRYPTO_DES=m # CONFIG_CRYPTO_CAST6 is not set # CONFIG_CRYPTO_TEA is not set # CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_KHAZAD is not set CONFIG_CRYPTO_DEFLATE=m # CONFIG_CRYPTO_MICHAEL_MIC is not set # CONFIG_CRYPTO_CRC32C is not set --- linux-2.6.9-rc1/arch/ia64/hp/sim/simserial.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/ia64/hp/sim/simserial.c 2004-09-02 20:51:51.000000000 -0700 @@ -1055,7 +1055,7 @@ simrs_init (void) ia64_ssc_connect_irq(KEYBOARD_INTR, state->irq); } - printk(KERN_INFO "ttyS%02d at 0x%04lx (irq = %d) is a %s\n", + printk(KERN_INFO "ttyS%d at 0x%04lx (irq = %d) is a %s\n", state->line, state->port, state->irq, uart_config[state->type].name); --- linux-2.6.9-rc1/arch/ia64/ia32/binfmt_elf32.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/ia64/ia32/binfmt_elf32.c 2004-09-02 20:51:51.000000000 -0700 @@ -187,7 +187,7 @@ ia32_setup_arg_pages (struct linux_binpr mpnt->vm_page_prot = (mpnt->vm_flags & VM_EXEC)? PAGE_COPY_EXEC: PAGE_COPY; insert_vm_struct(current->mm, mpnt); - current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT; + current->mm->stack_vm = current->mm->total_vm = vma_pages(mpnt); } for (i = 0 ; i < MAX_ARG_PAGES ; i++) { --- linux-2.6.9-rc1/arch/ia64/ia32/ia32_entry.S 2004-08-24 00:02:48.000000000 -0700 +++ 25/arch/ia64/ia32/ia32_entry.S 2004-09-02 20:51:51.000000000 -0700 @@ -41,7 +41,7 @@ ENTRY(ia32_clone) zxt4 out1=in1 // newsp mov out3=16 // stacksize (compensates for 16-byte scratch area) adds out2=IA64_SWITCH_STACK_SIZE+16,sp // out2 = ®s - dep out0=0,in0,CLONE_IDLETASK_BIT,1 // out0 = clone_flags & ~CLONE_IDLETASK + mov out0=in0 // out0 = clone_flags zxt4 out4=in2 // out4 = parent_tidptr zxt4 out5=in4 // out5 = child_tidptr br.call.sptk.many rp=do_fork --- linux-2.6.9-rc1/arch/ia64/ia32/ia32_signal.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/arch/ia64/ia32/ia32_signal.c 2004-09-02 20:51:51.000000000 -0700 @@ -882,9 +882,7 @@ setup_frame_ia32 (int sig, struct k_siga return 1; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); return 0; } @@ -952,9 +950,7 @@ setup_rt_frame_ia32 (int sig, struct k_s return 1; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); return 0; } --- linux-2.6.9-rc1/arch/ia64/ia32/ia32_support.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/ia64/ia32/ia32_support.c 2004-09-02 20:51:51.000000000 -0700 @@ -145,6 +145,9 @@ ia32_gdt_init (void) int cpu = smp_processor_id(); ia32_shared_page[cpu] = alloc_page(GFP_KERNEL); + if (!ia32_shared_page[cpu]) + panic("failed to allocate ia32_shared_page[%d]\n", cpu); + cpu_gdt_table[cpu] = page_address(ia32_shared_page[cpu]); /* Copy from the boot cpu's GDT */ @@ -161,6 +164,9 @@ ia32_boot_gdt_init (void) unsigned long ldt_size; ia32_shared_page[0] = alloc_page(GFP_KERNEL); + if (!ia32_shared_page[0]) + panic("failed to allocate ia32_shared_page[0]\n"); + ia32_boot_gdt = page_address(ia32_shared_page[0]); cpu_gdt_table[0] = ia32_boot_gdt; --- linux-2.6.9-rc1/arch/ia64/Kconfig 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/ia64/Kconfig 2004-09-03 00:56:37.469464424 -0700 @@ -178,6 +178,210 @@ config DISCONTIGMEM or have huge holes in the physical address space for other reasons. See for more. +config KGDB + bool "Include kgdb kernel debugger" + depends on DEBUG_KERNEL + help + If you say Y here, the system will be compiled with the debug + option (-g) and a debugging stub will be included in the + kernel. This stub communicates with gdb on another (host) + computer via a serial port. The host computer should have + access to the kernel binary file (vmlinux) and a serial port + that is connected to the target machine. Gdb can be made to + configure the serial port or you can use stty and setserial to + do this. See the 'target' command in gdb. This option also + configures in the ability to request a breakpoint early in the + boot process. To request the breakpoint just include 'kgdb' + as a boot option when booting the target machine. The system + will then break as soon as it looks at the boot options. This + option also installs a breakpoint in panic and sends any + kernel faults to the debugger. For more information see the + Documentation/i386/kgdb/kgdb.txt file. + +config KGDB_EARLY + bool + depends on KGDB + default n + prompt "KGDB Early" + help + Kgdb debugging in kernel can start shortly before/after setup_arch routine exits. + +choice + depends on KGDB + prompt "Debug serial port BAUD" + default KGDB_115200BAUD + help + Gdb and the kernel stub need to agree on the baud rate to be + used. Some systems (x86 family at this writing) allow this to + be configured. + +config KGDB_9600BAUD + bool "9600" + +config KGDB_19200BAUD + bool "19200" + +config KGDB_38400BAUD + bool "38400" + +config KGDB_57600BAUD + bool "57600" + +config KGDB_115200BAUD + bool "115200" +endchoice + +choice + depends on KGDB + prompt "I/O port address" + default KGDB_USE_IOMEM + +config KGDB_USE_IOMEM + bool "use I/O port IOMEM address" + help + Say yes if your system uses IOMEM address for the port (e.g. rx2600). + +config KGDB_USE_PORT + bool "use I/O port address" + help + Say yes if your systems uses I/O ports (e.g. Novascale). +endchoice + +config KGDB_IOMEM + hex "hex I/O port IOMEM address" + depends on KGDB_USE_IOMEM + default 0xc0000000ff5e0000 + help + Some systems use IOMEM address for the port. This value is from + the rx2600 chassis console port. + +config KGDB_IOMEM_REG_SHIFT + hex "hex I/O port IOMEM reg shift" + depends on KGDB_USE_IOMEM + default 0x0 + help + This is the memory shift for IOMEM. + +config KGDB_PORT + hex "hex I/O port address" + default 0x3f8 + depends on KGDB + depends on KGDB_USE_PORT + help + Some systems use I/O port. + On Novascale use 0x3f8 for serial port 1 + or 0x2f8 for serial port 2. + +config KGDB_IRQ + int "IRQ of the debug serial port" + depends on KGDB + default 59 + help + This is the irq for the debug port. If everything is working + correctly and the kernel has interrupts on a control C to the + port should cause a break into the kernel debug stub. This value + is the rx2600 chassis's console port. + +config DEBUG_INFO + bool + depends on KGDB + default y + +config KGDB_MORE + bool "Add any additional compile options" + depends on KGDB + default n + help + Saying yes here turns on the ability to enter additional + compile options. + + +config KGDB_OPTIONS + depends on KGDB_MORE + string "Additional compile arguments" + default "-O1" + help + This option allows you enter additional compile options for + the whole kernel compile. Each platform will have a default + that seems right for it. For example on PPC "-ggdb -O1", and + for i386 "-O1". Note that by configuring KGDB "-g" is already + turned on. In addition, on i386 platforms + "-fomit-frame-pointer" is deleted from the standard compile + options. + +config NO_KGDB_CPUS + int "Number of CPUs" + depends on KGDB && SMP + default NR_CPUS + help + + This option sets the number of cpus for kgdb ONLY. It is used + to prune some internal structures so they look "nice" when + displayed with gdb. This is to overcome possibly larger + numbers that may have been entered above. Enter the real + number to get nice clean kgdb_info displays. + +config KGDB_TS + bool "Enable kgdb time stamp macros?" + depends on KGDB + default n + help + Kgdb event macros allow you to instrument your code with calls + to the kgdb event recording function. The event log may be + examined with gdb at a break point. Turning on this + capability also allows you to choose how many events to + keep. Kgdb always keeps the lastest events. + +choice + depends on KGDB_TS + prompt "Max number of time stamps to save?" + default KGDB_TS_128 + +config KGDB_TS_64 + bool "64" + +config KGDB_TS_128 + bool "128" + +config KGDB_TS_256 + bool "256" + +config KGDB_TS_512 + bool "512" + +config KGDB_TS_1024 + bool "1024" + +endchoice + +config KGDB_CONSOLE + bool "Enable serial console thru kgdb port" + depends on KGDB + default n + help + This option enables the command line "console=kgdb" option. + When the system is booted with this option in the command line + all kernel printk output is sent to gdb (as well as to other + consoles). For this to work gdb must be connected. For this + reason, this command line option will generate a breakpoint if + gdb has not yet connected. After the gdb continue command is + given all pent up console output will be printed by gdb on the + host machine. Neither this option, nor KGDB require the + serial driver to be configured. + +config KGDB_SYSRQ + bool "Turn on SysRq 'G' command to do a break?" + depends on KGDB + default y + help + This option includes an option in the SysRq code that allows + you to enter SysRq G which generates a breakpoint to the KGDB + stub. This will work if the keyboard is alive and can + interrupt the system. Because of constraints on when the + serial port interrupt can be enabled, this code may allow you + to interrupt the system before the serial port control C is + available. Just say yes here. + config IA64_CYCLONE bool "Cyclone (EXA) Time Source support" help --- linux-2.6.9-rc1/arch/ia64/Kconfig.debug 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/ia64/Kconfig.debug 2004-09-03 00:56:42.842647576 -0700 @@ -61,4 +61,11 @@ config SYSVIPC_COMPAT depends on COMPAT && SYSVIPC default y +config LOCKMETER + bool "Kernel lock metering" + depends on SMP + help + Say Y to enable kernel lock metering, which adds overhead to SMP locks, + but allows you to see various statistics using the lockstat command. + endmenu --- linux-2.6.9-rc1/arch/ia64/kernel/acpi.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/ia64/kernel/acpi.c 2004-09-03 00:56:51.386348736 -0700 @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include --- linux-2.6.9-rc1/arch/ia64/kernel/asm-offsets.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/ia64/kernel/asm-offsets.c 2004-09-03 00:56:30.906462152 -0700 @@ -188,18 +188,10 @@ void foo(void) DEFINE(IA64_CLONE_VM, CLONE_VM); BLANK(); - /* used by fsys_gettimeofday in arch/ia64/kernel/fsys.S */ - DEFINE(IA64_CPUINFO_ITM_DELTA_OFFSET, offsetof (struct cpuinfo_ia64, itm_delta)); - DEFINE(IA64_CPUINFO_ITM_NEXT_OFFSET, offsetof (struct cpuinfo_ia64, itm_next)); DEFINE(IA64_CPUINFO_NSEC_PER_CYC_OFFSET, offsetof (struct cpuinfo_ia64, nsec_per_cyc)); DEFINE(IA64_TIMESPEC_TV_NSEC_OFFSET, offsetof (struct timespec, tv_nsec)); - DEFINE(CLONE_IDLETASK_BIT, 12); -#if CLONE_IDLETASK != (1 << 12) -# error "CLONE_IDLETASK_BIT incorrect, please fix" -#endif - DEFINE(CLONE_SETTLS_BIT, 19); #if CLONE_SETTLS != (1<<19) # error "CLONE_SETTLS_BIT incorrect, please fix" @@ -207,5 +199,21 @@ void foo(void) BLANK(); DEFINE(IA64_MCA_TLB_INFO_SIZE, sizeof (struct ia64_mca_tlb_info)); + /* used by head.S */ + DEFINE(IA64_CPUINFO_NSEC_PER_CYC_OFFSET, offsetof (struct cpuinfo_ia64, nsec_per_cyc)); + BLANK(); + /* used by fsys_gettimeofday in arch/ia64/kernel/fsys.S */ + DEFINE(IA64_TIME_INTERPOLATOR_ADDRESS_OFFSET, offsetof (struct time_interpolator, addr)); + DEFINE(IA64_TIME_INTERPOLATOR_SOURCE_OFFSET, offsetof (struct time_interpolator, source)); + DEFINE(IA64_TIME_INTERPOLATOR_SHIFT_OFFSET, offsetof (struct time_interpolator, shift)); + DEFINE(IA64_TIME_INTERPOLATOR_NSEC_OFFSET, offsetof (struct time_interpolator, nsec_per_cyc)); + DEFINE(IA64_TIME_INTERPOLATOR_OFFSET_OFFSET, offsetof (struct time_interpolator, offset)); + DEFINE(IA64_TIME_INTERPOLATOR_LAST_CYCLE_OFFSET, offsetof (struct time_interpolator, last_cycle)); + DEFINE(IA64_TIME_INTERPOLATOR_LAST_COUNTER_OFFSET, offsetof (struct time_interpolator, last_counter)); + DEFINE(IA64_TIME_INTERPOLATOR_JITTER_OFFSET, offsetof (struct time_interpolator, jitter)); + DEFINE(IA64_TIME_SOURCE_CPU, TIME_SOURCE_CPU); + DEFINE(IA64_TIME_SOURCE_MMIO64, TIME_SOURCE_MMIO64); + DEFINE(IA64_TIME_SOURCE_MMIO32, TIME_SOURCE_MMIO32); + DEFINE(IA64_TIMESPEC_TV_NSEC_OFFSET, offsetof (struct timespec, tv_nsec)); } --- linux-2.6.9-rc1/arch/ia64/kernel/cyclone.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/ia64/kernel/cyclone.c 2004-09-03 00:56:30.906462152 -0700 @@ -1,6 +1,8 @@ +#include #include #include #include +#include /* IBM Summit (EXA) Cyclone counter code*/ #define CYCLONE_CBAR_ADDR 0xFEB00CD0 @@ -15,62 +17,10 @@ void __init cyclone_setup(void) use_cyclone = 1; } -static u32* volatile cyclone_timer; /* Cyclone MPMC0 register */ -static u32 last_update_cyclone; - -static unsigned long offset_base; - -static unsigned long get_offset_cyclone(void) -{ - u32 now; - unsigned long offset; - - /* Read the cyclone timer */ - now = readl(cyclone_timer); - /* .. relative to previous update*/ - offset = now - last_update_cyclone; - - /* convert cyclone ticks to nanoseconds */ - offset = (offset*NSEC_PER_SEC)/CYCLONE_TIMER_FREQ; - - /* our adjusted time in nanoseconds */ - return offset_base + offset; -} - -static void update_cyclone(long delta_nsec) -{ - u32 now; - unsigned long offset; - - /* Read the cyclone timer */ - now = readl(cyclone_timer); - /* .. relative to previous update*/ - offset = now - last_update_cyclone; - - /* convert cyclone ticks to nanoseconds */ - offset = (offset*NSEC_PER_SEC)/CYCLONE_TIMER_FREQ; - - offset += offset_base; - - /* Be careful about signed/unsigned comparisons here: */ - if (delta_nsec < 0 || (unsigned long) delta_nsec < offset) - offset_base = offset - delta_nsec; - else - offset_base = 0; - - last_update_cyclone = now; -} - -static void reset_cyclone(void) -{ - offset_base = 0; - last_update_cyclone = readl(cyclone_timer); -} struct time_interpolator cyclone_interpolator = { - .get_offset = get_offset_cyclone, - .update = update_cyclone, - .reset = reset_cyclone, + .source = TIME_SOURCE_MMIO32, + .shift = 32, .frequency = CYCLONE_TIMER_FREQ, .drift = -100, }; @@ -81,6 +31,7 @@ int __init init_cyclone_clock(void) u64 base; /* saved cyclone base address */ u64 offset; /* offset from pageaddr to cyclone_timer register */ int i; + u32* volatile cyclone_timer; /* Cyclone MPMC0 register */ if (!use_cyclone) return -ENODEV; @@ -148,7 +99,7 @@ int __init init_cyclone_clock(void) } } /* initialize last tick */ - last_update_cyclone = readl(cyclone_timer); + cyclone_interpolator.addr = cyclone_timer; register_time_interpolator(&cyclone_interpolator); return 0; --- linux-2.6.9-rc1/arch/ia64/kernel/efi.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/arch/ia64/kernel/efi.c 2004-09-03 00:56:30.907462000 -0700 @@ -357,8 +357,10 @@ efi_memmap_walk (efi_freemem_callback_t if (total_mem >= mem_limit) continue; total_mem += (md->num_pages << EFI_PAGE_SHIFT); - if (total_mem > mem_limit) + if (total_mem > mem_limit) { md->num_pages -= ((total_mem - mem_limit) >> EFI_PAGE_SHIFT); + max_addr = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT); + } if (md->num_pages == 0) continue; --- linux-2.6.9-rc1/arch/ia64/kernel/entry.S 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/ia64/kernel/entry.S 2004-09-02 20:51:51.000000000 -0700 @@ -128,7 +128,7 @@ GLOBAL_ENTRY(sys_clone2) (p6) st8 [r2]=in5 // store TLS in r16 for copy_thread() mov out5=in4 // child_tidptr: valid only w/CLONE_CHILD_SETTID or CLONE_CHILD_CLEARTID adds out2=IA64_SWITCH_STACK_SIZE+16,sp // out2 = ®s - dep out0=0,in0,CLONE_IDLETASK_BIT,1 // out0 = clone_flags & ~CLONE_IDLETASK + mov out0=in0 // out0 = clone_flags br.call.sptk.many rp=do_fork .ret1: .restore sp adds sp=IA64_SWITCH_STACK_SIZE,sp // pop the switch stack @@ -157,7 +157,7 @@ GLOBAL_ENTRY(sys_clone) (p6) st8 [r2]=in4 // store TLS in r13 (tp) mov out5=in3 // child_tidptr: valid only w/CLONE_CHILD_SETTID or CLONE_CHILD_CLEARTID adds out2=IA64_SWITCH_STACK_SIZE+16,sp // out2 = ®s - dep out0=0,in0,CLONE_IDLETASK_BIT,1 // out0 = clone_flags & ~CLONE_IDLETASK + mov out0=in0 // out0 = clone_flags br.call.sptk.many rp=do_fork .ret2: .restore sp adds sp=IA64_SWITCH_STACK_SIZE,sp // pop the switch stack --- linux-2.6.9-rc1/arch/ia64/kernel/fsys.S 2004-08-24 00:03:50.000000000 -0700 +++ 25/arch/ia64/kernel/fsys.S 2004-09-03 00:56:30.910461544 -0700 @@ -8,6 +8,8 @@ * 18-Feb-03 louisk Implement fsys_gettimeofday(). * 28-Feb-03 davidm Fixed several bugs in fsys_gettimeofday(). Tuned it some more, * probably broke it along the way... ;-) + * 13-Jul-04 clameter Implement fsys_clock_gettime and revise fsys_gettimeofday to make + * it capable of using memory based clocks without falling back to C code. */ #include @@ -144,195 +146,206 @@ ENTRY(fsys_set_tid_address) END(fsys_set_tid_address) /* - * Note 1: This routine uses floating-point registers, but only with registers that - * operate on integers. Because of that, we don't need to set ar.fpsr to the - * kernel default value. - * - * Note 2: For now, we will assume that all CPUs run at the same clock-frequency. - * If that wasn't the case, we would have to disable preemption (e.g., - * by disabling interrupts) between reading the ITC and reading - * local_cpu_data->nsec_per_cyc. - * - * Note 3: On platforms where the ITC-drift bit is set in the SAL feature vector, - * we ought to either skip the ITC-based interpolation or run an ntp-like - * daemon to keep the ITCs from drifting too far apart. + * Ensure that the time interpolator structure is compatible with the asm code */ +#if IA64_TIME_INTERPOLATOR_SOURCE_OFFSET !=0 || IA64_TIME_INTERPOLATOR_SHIFT_OFFSET != 2 \ + || IA64_TIME_INTERPOLATOR_JITTER_OFFSET != 3 || IA64_TIME_INTERPOLATOR_NSEC_OFFSET != 4 +#error fsys_gettimeofday incompatible with changes to struct time_interpolator +#endif +#define CLOCK_REALTIME 0 +#define CLOCK_MONOTONIC 1 +#define CLOCK_DIVIDE_BY_1000 0x4000 +#define CLOCK_ADD_MONOTONIC 0x8000 ENTRY(fsys_gettimeofday) .prologue .altrp b6 .body - add r9=TI_FLAGS+IA64_TASK_SIZE,r16 - addl r3=THIS_CPU(cpu_info),r0 - -#ifdef CONFIG_SMP - movl r10=__per_cpu_offset - movl r2=sal_platform_features - ;; - - ld8 r2=[r2] - movl r19=xtime // xtime is a timespec struct - - ld8 r10=[r10] // r10 <- __per_cpu_offset[0] - addl r21=THIS_CPU(cpu_info),r0 - ;; - add r10=r21, r10 // r10 <- &cpu_data(time_keeper_id) - tbit.nz p8,p0 = r2, IA64_SAL_PLATFORM_FEATURE_ITC_DRIFT_BIT -(p8) br.spnt.many fsys_fallback_syscall -#else - ;; - mov r10=r3 - movl r19=xtime // xtime is a timespec struct -#endif - ld4 r9=[r9] - movl r17=xtime_lock - ;; - - // r32, r33 should contain the 2 args of gettimeofday - adds r21=IA64_CPUINFO_ITM_NEXT_OFFSET, r10 - mov r2=-1 - tnat.nz p6,p7=r32 // guard against NaT args - ;; - - adds r10=IA64_CPUINFO_ITM_DELTA_OFFSET, r10 -(p7) tnat.nz p6,p0=r33 -(p6) br.cond.spnt.few .fail_einval - - adds r8=IA64_CPUINFO_NSEC_PER_CYC_OFFSET, r3 - movl r24=2361183241434822607 // for division hack (only for / 1000) - ;; - - ldf8 f7=[r10] // f7 now contains itm_delta - setf.sig f11=r2 - adds r10=8, r32 - - adds r20=IA64_TIMESPEC_TV_NSEC_OFFSET, r19 // r20 = &xtime->tv_nsec - movl r26=jiffies - - setf.sig f9=r24 // f9 is used for division hack - movl r27=wall_jiffies - - and r9=TIF_ALLWORK_MASK,r9 - movl r25=last_nsec_offset - ;; - - /* - * Verify that we have permission to write to struct timeval. Note: - * Another thread might unmap the mapping before we actually get - * to store the result. That's OK as long as the stores are also - * protect by EX(). - */ -EX(.fail_efault, probe.w.fault r32, 3) // this must come _after_ NaT-check -EX(.fail_efault, probe.w.fault r10, 3) // this must come _after_ NaT-check - nop 0 - - ldf8 f10=[r8] // f10 <- local_cpu_data->nsec_per_cyc value - cmp.ne p8, p0=0, r9 -(p8) br.spnt.many fsys_fallback_syscall - ;; -.retry: // *** seq = read_seqbegin(&xtime_lock); *** - ld4.acq r23=[r17] // since &xtime_lock == &xtime_lock->sequence - ld8 r14=[r25] // r14 (old) = last_nsec_offset - - ld8 r28=[r26] // r28 = jiffies - ld8 r29=[r27] // r29 = wall_jiffies - ;; - - ldf8 f8=[r21] // f8 now contains itm_next - mov.m r31=ar.itc // put time stamp into r31 (ITC) == now - sub r28=r29, r28, 1 // r28 now contains "-(lost + 1)" - ;; - - ld8 r2=[r19] // r2 = sec = xtime.tv_sec - ld8 r29=[r20] // r29 = nsec = xtime.tv_nsec - tbit.nz p9, p10=r23, 0 // p9 <- is_odd(r23), p10 <- is_even(r23) - - setf.sig f6=r28 // f6 <- -(lost + 1) (6 cyc) - ;; - + mov r31 = r32 + tnat.nz p6,p0 = r33 // guard against NaT argument +(p6) br.cond.spnt.few .fail_einval + mov r30 = CLOCK_DIVIDE_BY_1000 + ;; +.gettime: + // Register map + // Incoming r31 = pointer to address where to place result + // r30 = flags determining how time is processed + // r2,r3 = temp r4-r7 preserved + // r8 = result nanoseconds + // r9 = result seconds + // r10 = temporary storage for clock difference + // r11 = preserved: saved ar.pfs + // r12 = preserved: memory stack + // r13 = preserved: thread pointer + // r14 = debug pointer / usable + // r15 = preserved: system call number + // r16 = preserved: current task pointer + // r17 = wall to monotonic use + // r18 = time_interpolator->offset + // r19 = address of wall_to_monotonic + // r20 = pointer to struct time_interpolator / pointer to time_interpolator->address + // r21 = shift factor + // r22 = address of time interpolator->last_counter + // r23 = address of time_interpolator->last_cycle + // r24 = adress of time_interpolator->offset + // r25 = last_cycle value + // r26 = last_counter value + // r27 = pointer to xtime + // r28 = sequence number at the beginning of critcal section + // r29 = address of seqlock + // r30 = time processing flags / memory address + // r31 = pointer to result + // Predicates + // p6,p7 short term use + // p8 = timesource ar.itc + // p9 = timesource mmio64 + // p10 = timesource mmio32 + // p11 = timesource not to be handled by asm code + // p12 = memory time source ( = p9 | p10) + // p13 = do cmpxchg with time_interpolator_last_cycle + // p14 = Divide by 1000 + // p15 = Add monotonic + // + // Note that instructions are optimized for McKinley. McKinley can process two + // bundles simultaneously and therefore we continuously try to feed the CPU + // two bundles and then a stop. + tnat.nz p6,p0 = r31 // branch deferred since it does not fit into bundle structure + mov pr = r30,0xc000 // Set predicates according to function + add r2 = TI_FLAGS+IA64_TASK_SIZE,r16 + movl r20 = time_interpolator + ;; + ld8 r20 = [r20] // get pointer to time_interpolator structure + movl r29 = xtime_lock + ld4 r2 = [r2] // process work pending flags + movl r27 = xtime + ;; // only one bundle here + ld8 r21 = [r20] // first quad with control information + and r2 = TIF_ALLWORK_MASK,r2 +(p6) br.cond.spnt.few .fail_einval // deferred branch + ;; + add r10 = IA64_TIME_INTERPOLATOR_ADDRESS_OFFSET,r20 + extr r3 = r21,32,32 // time_interpolator->nsec_per_cyc + extr r8 = r21,0,16 // time_interpolator->source + nop.i 123 + cmp.ne p6, p0 = 0, r2 // Fallback if work is scheduled +(p6) br.cond.spnt.many fsys_fallback_syscall + ;; + cmp.eq p8,p12 = 0,r8 // Check for cpu timer + cmp.eq p9,p0 = 1,r8 // MMIO64 ? + extr r2 = r21,24,8 // time_interpolator->jitter + cmp.eq p10,p0 = 2,r8 // MMIO32 ? + cmp.lt p11,p0 = 2,r8 // function? +(p11) br.cond.spnt.many fsys_fallback_syscall + ;; + setf.sig f7 = r3 // Setup for scaling of counter +(p15) movl r19 = wall_to_monotonic +(p12) ld8 r30 = [r10] + cmp.ne p13,p0 = r2,r0 // need jitter compensation? + extr r21 = r21,16,8 // shift factor + ;; +.time_redo: + .pred.rel.mutex p8,p9,p10 + ld4.acq r28 = [r29] // xtime_lock.sequence. Must come first for locking purposes +(p8) mov r2 = ar.itc // CPU_TIMER. 36 clocks latency!!! + add r22 = IA64_TIME_INTERPOLATOR_LAST_COUNTER_OFFSET,r20 +(p9) ld8 r2 = [r30] // readq(ti->address). Could also have latency issues.. +(p10) ld4 r2 = [r30] // readw(ti->address) +(p13) add r23 = IA64_TIME_INTERPOLATOR_LAST_CYCLE_OFFSET,r20 + ;; // could be removed by moving the last add upward + ld8 r26 = [r22] // time_interpolator->last_counter +(p13) ld8 r25 = [r23] // time interpolator->last_cycle + add r24 = IA64_TIME_INTERPOLATOR_OFFSET_OFFSET,r20 +(p15) ld8 r17 = [r19],IA64_TIMESPEC_TV_NSEC_OFFSET + ld8 r9 = [r27],IA64_TIMESPEC_TV_NSEC_OFFSET + nop.i 123 + ;; + ld8 r18 = [r24] // time_interpolator->offset + ld8 r8 = [r27],-IA64_TIMESPEC_TV_NSEC_OFFSET // xtime.tv_nsec +(p13) sub r3 = r25,r2 // Diff needed before comparison (thanks davidm) + ;; +(p13) cmp.gt.unc p6,p7 = r3,r0 // check if it is less than last. p6,p7 cleared + sub r10 = r2,r26 // current_counter - last_counter + ;; +(p6) sub r10 = r25,r26 // time we got was less than last_cycle +(p7) mov ar.ccv = r25 // more than last_cycle. Prep for cmpxchg + ;; + setf.sig f8 = r10 + nop.i 123 + ;; +(p7) cmpxchg8.rel r3 = [r23],r2,ar.ccv +EX(.fail_efault, probe.w.fault r31, 3) // This takes 5 cycles and we have spare time + xmpy.l f8 = f8,f7 // nsec_per_cyc*(counter-last_counter) +(p15) add r9 = r9,r17 // Add wall to monotonic.secs to result secs + ;; +(p15) ld8 r17 = [r19],-IA64_TIMESPEC_TV_NSEC_OFFSET +(p7) cmp.ne p7,p0 = r25,r3 // if cmpxchg not successful redo + // simulate tbit.nz.or p7,p0 = r28,0 + and r28 = ~1,r28 // Make sequence even to force retry if odd + getf.sig r2 = f8 mf - xma.l f8=f6, f7, f8 // f8 (last_tick) <- -(lost + 1)*itm_delta + itm_next (5 cyc) - nop 0 - - setf.sig f12=r31 // f12 <- ITC (6 cyc) - // *** if (unlikely(read_seqretry(&xtime_lock, seq))) continue; *** - ld4 r24=[r17] // r24 = xtime_lock->sequence (re-read) - nop 0 - ;; - - xma.l f8=f11, f8, f12 // f8 (elapsed_cycles) <- (-1*last_tick + now) = (now - last_tick) - nop 0 - ;; - - getf.sig r18=f8 // r18 <- (now - last_tick) - xmpy.l f8=f8, f10 // f8 <- elapsed_cycles*nsec_per_cyc (5 cyc) - add r3=r29, r14 // r3 = (nsec + old) - ;; - - cmp.lt p7, p8=r18, r0 // if now < last_tick, set p7 = 1, p8 = 0 - getf.sig r18=f8 // r18 = elapsed_cycles*nsec_per_cyc (6 cyc) - nop 0 - ;; - -(p10) cmp.ne p9, p0=r23, r24 // if xtime_lock->sequence != seq, set p9 - shr.u r18=r18, IA64_NSEC_PER_CYC_SHIFT // r18 <- offset -(p9) br.spnt.many .retry - ;; - - mov ar.ccv=r14 // ar.ccv = old (1 cyc) - cmp.leu p7, p8=r18, r14 // if (offset <= old), set p7 = 1, p8 = 0 + add r8 = r8,r18 // Add time interpolator offset ;; - -(p8) cmpxchg8.rel r24=[r25], r18, ar.ccv // compare-and-exchange (atomic!) -(p8) add r3=r29, r18 // r3 = (nsec + offset) - ;; - shr.u r3=r3, 3 // initiate dividing r3 by 1000 - ;; - setf.sig f8=r3 // (6 cyc) - mov r10=1000000 // r10 = 1000000 + ld4 r10 = [r29] // xtime_lock.sequence +(p15) add r8 = r8, r17 // Add monotonic.nsecs to nsecs + shr.u r2 = r2,r21 + ;; // overloaded 3 bundles! + // End critical section. + add r8 = r8,r2 // Add xtime.nsecs + cmp4.ne.or p7,p0 = r28,r10 +(p7) br.cond.dpnt.few .time_redo // sequence number changed ? + // Now r8=tv->tv_nsec and r9=tv->tv_sec + mov r10 = r0 + movl r2 = 1000000000 + add r23 = IA64_TIMESPEC_TV_NSEC_OFFSET, r31 +(p14) movl r3 = 2361183241434822607 // Prep for / 1000 hack + ;; +.time_normalize: + mov r21 = r8 + cmp.ge p6,p0 = r8,r2 +(p14) shr.u r20 = r8, 3 // We can repeat this if necessary just wasting some time + ;; +(p14) setf.sig f8 = r20 +(p6) sub r8 = r8,r2 +(p6) add r9 = 1,r9 // two nops before the branch. +(p14) setf.sig f7 = r3 // Chances for repeats are 1 in 10000 for gettod +(p6) br.cond.dpnt.few .time_normalize + ;; + // Divided by 8 though shift. Now divide by 125 + // The compiler was able to do that with a multiply + // and a shift and we do the same +EX(.fail_efault, probe.w.fault r23, 3) // This also costs 5 cycles +(p14) xmpy.hu f8 = f8, f7 // xmpy has 5 cycles latency so use it... ;; -(p8) cmp.ne.unc p9, p0=r24, r14 - xmpy.hu f6=f8, f9 // (5 cyc) -(p9) br.spnt.many .retry - ;; - - getf.sig r3=f6 // (6 cyc) + mov r8 = r0 +(p14) getf.sig r2 = f8 ;; - shr.u r3=r3, 4 // end of division, r3 is divided by 1000 (=usec) - ;; - -1: cmp.geu p7, p0=r3, r10 // while (usec >= 1000000) - ;; -(p7) sub r3=r3, r10 // usec -= 1000000 -(p7) adds r2=1, r2 // ++sec -(p7) br.spnt.many 1b - - // finally: r2 = sec, r3 = usec -EX(.fail_efault, st8 [r32]=r2) - adds r9=8, r32 - mov r8=r0 // success +(p14) shr.u r21 = r2, 4 ;; -EX(.fail_efault, st8 [r9]=r3) // store them in the timeval struct - mov r10=0 +EX(.fail_efault, st8 [r31] = r9) +EX(.fail_efault, st8 [r23] = r21) FSYS_RETURN - /* - * Note: We are NOT clearing the scratch registers here. Since the only things - * in those registers are time-related variables and some addresses (which - * can be obtained from System.map), none of this should be security-sensitive - * and we should be fine. - */ - .fail_einval: - mov r8=EINVAL // r8 = EINVAL - mov r10=-1 // r10 = -1 + mov r8 = EINVAL + mov r10 = -1 FSYS_RETURN - .fail_efault: - mov r8=EFAULT // r8 = EFAULT - mov r10=-1 // r10 = -1 + mov r8 = EFAULT + mov r10 = -1 FSYS_RETURN END(fsys_gettimeofday) +ENTRY(fsys_clock_gettime) + .prologue + .altrp b6 + .body + cmp4.lt p6, p0 = CLOCK_MONOTONIC, r32 + // Fallback if this is not CLOCK_REALTIME or CLOCK_MONOTONIC +(p6) br.spnt.few fsys_fallback_syscall + mov r31 = r33 + shl r30 = r32,15 + br.many .gettime +END(fsys_clock_gettime) + /* * long fsys_rt_sigprocmask (int how, sigset_t *set, sigset_t *oset, size_t sigsetsize). */ @@ -838,7 +851,7 @@ fsyscall_table: data8 0 // timer_getoverrun data8 0 // timer_delete data8 0 // clock_settime - data8 0 // clock_gettime + data8 fsys_clock_gettime // clock_gettime data8 0 // clock_getres // 1255 data8 0 // clock_nanosleep data8 0 // fstatfs64 --- linux-2.6.9-rc1/arch/ia64/kernel/head.S 2004-08-24 00:03:14.000000000 -0700 +++ 25/arch/ia64/kernel/head.S 2004-09-02 20:51:51.000000000 -0700 @@ -781,8 +781,7 @@ GLOBAL_ENTRY(ia64_switch_mode_virt) // going to virtual // - for code addresses, set upper bits of addr to KERNEL_START - // - for stack addresses, set upper 3 bits to 0xe.... Dont change any of the - // lower bits since we want it to stay identity mapped + // - for stack addresses, copy from input argument movl r18=KERNEL_START dep r3=0,r3,KERNEL_TR_PAGE_SHIFT,64-KERNEL_TR_PAGE_SHIFT dep r14=0,r14,KERNEL_TR_PAGE_SHIFT,64-KERNEL_TR_PAGE_SHIFT --- linux-2.6.9-rc1/arch/ia64/kernel/irq.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/ia64/kernel/irq.c 2004-09-03 00:56:37.231500600 -0700 @@ -255,14 +255,16 @@ int handle_IRQ_event(unsigned int irq, struct pt_regs *regs, struct irqaction *action) { int status = 1; /* Force the "do bottom halves" bit */ - int retval = 0; + int ret, retval = 0; if (!(action->flags & SA_INTERRUPT)) local_irq_enable(); do { - status |= action->flags; - retval |= action->handler(irq, action->dev_id, regs); + ret = action->handler(irq, action->dev_id, regs); + if (ret == IRQ_HANDLED) + status |= action->flags; + retval |= ret; action = action->next; } while (action); if (status & SA_SAMPLE_RANDOM) @@ -539,6 +541,11 @@ unsigned int do_IRQ(unsigned long irq, s desc->handler->end(irq); spin_unlock(&desc->lock); } + +#ifdef CONFIG_KGDB + kgdb_process_breakpoint(); +#endif + return 1; } @@ -1137,31 +1144,6 @@ void fixup_irqs(void) } #endif -static int prof_cpu_mask_read_proc (char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - int len = cpumask_scnprintf(page, count, *(cpumask_t *)data); - if (count - len < 2) - return -EINVAL; - len += sprintf(page + len, "\n"); - return len; -} - -static int prof_cpu_mask_write_proc (struct file *file, const char *buffer, - unsigned long count, void *data) -{ - cpumask_t *mask = (cpumask_t *)data; - unsigned long full_count = count, err; - cpumask_t new_value; - - err = cpumask_parse(buffer, count, new_value); - if (err) - return err; - - *mask = new_value; - return full_count; -} - #define MAX_NAMELEN 10 static void register_irq_proc (unsigned int irq) @@ -1196,26 +1178,15 @@ static void register_irq_proc (unsigned #endif } -cpumask_t prof_cpu_mask = CPU_MASK_ALL; - void init_irq_proc (void) { - struct proc_dir_entry *entry; int i; /* create /proc/irq */ root_irq_dir = proc_mkdir("irq", 0); /* create /proc/irq/prof_cpu_mask */ - entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); - - if (!entry) - return; - - entry->nlink = 1; - entry->data = (void *)&prof_cpu_mask; - entry->read_proc = prof_cpu_mask_read_proc; - entry->write_proc = prof_cpu_mask_write_proc; + create_prof_cpu_mask(root_irq_dir); /* * Create entries for all existing IRQs. --- linux-2.6.9-rc1/arch/ia64/kernel/ivt.S 2004-08-24 00:01:55.000000000 -0700 +++ 25/arch/ia64/kernel/ivt.S 2004-09-03 00:56:37.232500448 -0700 @@ -68,6 +68,13 @@ # define DBG_FAULT(i) #endif +#ifdef CONFIG_KGDB +#define KGDB_ENABLE_PSR_DB mov r31=psr;; movl r30=IA64_PSR_DB;; or r31=r31,r30;; \ + mov psr.l=r31;; srlz.i;; +#else +#define KGDB_ENABLE_PSR_DB +#endif + #define MINSTATE_VIRT /* needed by minstate.h */ #include "minstate.h" @@ -473,6 +480,7 @@ ENTRY(page_fault) movl r14=ia64_leave_kernel ;; SAVE_REST + KGDB_ENABLE_PSR_DB mov rp=r14 ;; adds out2=16,r12 // out2 = pointer to pt_regs @@ -733,6 +741,8 @@ ENTRY(break_fault) ;; srlz.i // guarantee that interruption collection is on ;; + KGDB_ENABLE_PSR_DB + ;; (p15) ssm psr.i // restore psr.i ;; mov r3=NR_syscalls - 1 @@ -776,6 +786,7 @@ ENTRY(interrupt) srlz.i // ensure everybody knows psr.ic is back on ;; SAVE_REST + KGDB_ENABLE_PSR_DB ;; alloc r14=ar.pfs,0,0,2,0 // must be first in an insn group mov out0=cr.ivr // pass cr.ivr as first arg @@ -1003,6 +1014,7 @@ ENTRY(non_syscall) movl r15=ia64_leave_kernel ;; SAVE_REST + KGDB_ENABLE_PSR_DB mov rp=r15 ;; br.call.sptk.many b6=ia64_bad_break // avoid WAW on CFM and ignore return addr @@ -1036,6 +1048,7 @@ ENTRY(dispatch_unaligned_handler) adds r3=8,r2 // set up second base pointer ;; SAVE_REST + KGDB_ENABLE_PSR_DB movl r14=ia64_leave_kernel ;; mov rp=r14 @@ -1078,6 +1091,7 @@ ENTRY(dispatch_to_fault_handler) adds r3=8,r2 // set up second base pointer for SAVE_REST ;; SAVE_REST + KGDB_ENABLE_PSR_DB movl r14=ia64_leave_kernel ;; mov rp=r14 --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/ia64/kernel/kgdb_stub.c 2004-09-03 00:56:37.245498472 -0700 @@ -0,0 +1,3010 @@ +/* + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +/* + * Copyright (c) 2000 VERITAS Software Corporation. + * + */ +/**************************************************************************** + * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ + * + * Module name: remcom.c $ + * Revision: 1.34 $ + * Date: 91/03/09 12:29:49 $ + * Contributor: Lake Stevens Instrument Division$ + * + * Description: low level support for gdb debugger. $ + * + * Considerations: only works on target hardware $ + * + * Written by: Glenn Engel $ + * Updated by: David Grothe + * Updated by: Robert Walsh + * Updated by: wangdi + * ModuleState: Experimental $ + * + * NOTES: See Below $ + * + * Modified for 386 by Jim Kingdon, Cygnus Support. + * Compatibility with 2.1.xx kernel by David Grothe + * + * Changes to allow auto initilization. All that is needed is that it + * be linked with the kernel and a break point (int 3) be executed. + * The header file defines BREAKPOINT to allow one to do + * this. It should also be possible, once the interrupt system is up, to + * call putDebugChar("+"). Once this is done, the remote debugger should + * get our attention by sending a ^C in a packet. George Anzinger + * + * Integrated into 2.2.5 kernel by Tigran Aivazian + * Added thread support, support for multiple processors, + * support for ia-32(x86) hardware debugging. + * Amit S. Kale ( akale@veritas.com ) + * + * Modified to support debugging over ethernet by Robert Walsh + * and wangdi , based on + * code by San Mehat. + * + * + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing an int 3. + * + ************* + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $#. + * + * where + * :: + * :: < two hex digits computed as modulo 256 sum of > + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + ****************************************************************************/ +#define KGDB_VERSION "<20030915.1651.33>" +#include +#include +#include /* for strcpy */ +#include +#include +#include +#include /* for linux pt_regs struct */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * IA64 ToDo: + * 1) more testing needs to be done when modifying registers. + * 2) probably could use cleanup in register get/put from unw_info but gdb work + * is on going (WIP) to reduce g-packet on wire for serial + * 3) until gdb work is complete and accepted by gdb folks, the serial interface + * isn't complete. the current g-packet is over 10,000 bytes which is too + * large for a serial line. without a g-packet a delay is used with large putpacket + requests. however, this won't solve a gdb sent packet which is a g-packet. + * 4) NMI for hung machine. Well we don't have real NMI! + */ + +/************************************************************************ + * + * external low-level support routines + */ +typedef void (*Function) (void); /* pointer to a function */ + +/* Thread reference */ +typedef unsigned char threadref[8]; + +extern int tty_putDebugChar(int); /* write a single character */ +extern int tty_getDebugChar(void); /* read and return a single char */ +extern void tty_flushDebugChar(void); /* flush pending characters */ +extern int eth_putDebugChar(int); /* write a single character */ +extern int eth_getDebugChar(void); /* read and return a single char */ +extern void eth_flushDebugChar(void); /* flush pending characters */ + +/************************************************************************/ +/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/ +/* at least NUMREGBYTES*2 are needed for register packets */ +/* Longer buffer is needed to list all threads */ +/* purloined from gdb ia64 config support */ +#define NUM_REGS 590 +#define REGISTER_BYTES (NUM_REGS*8+128*8) +#define REGISTER_BYTE(N) (((N) * 8) \ + + ((N) <= IA64_FR0_REGNUM ? 0 : 8 * (((N) > IA64_FR127_REGNUM) ? 128 : (N) - IA64_FR0_REGNUM))) +#define REGISTER_SIZE(N) (((N) >= IA64_FR0_REGNUM && (N) <= IA64_FR127_REGNUM) ? 16 : 8) +#define IA64_GR0_REGNUM 0 +#define IA64_FR0_REGNUM 128 +#define IA64_FR127_REGNUM (IA64_FR0_REGNUM+127) +#define IA64_PR0_REGNUM 256 +#define IA64_BR0_REGNUM 320 +#define IA64_VFP_REGNUM 328 +#define IA64_PR_REGNUM 330 +#define IA64_IP_REGNUM 331 +#define IA64_PSR_REGNUM 332 +#define IA64_CFM_REGNUM 333 +#define IA64_AR0_REGNUM 334 +#define IA64_NAT0_REGNUM 462 +#define IA64_NAT31_REGNUM (IA64_NAT0_REGNUM+31) +#define IA64_NAT32_REGNUM (IA64_NAT0_REGNUM+32) +#define IA64_RSC_REGNUM (IA64_AR0_REGNUM+16) +#define IA64_BSP_REGNUM (IA64_AR0_REGNUM+17) +#define IA64_BSPSTORE_REGNUM (IA64_AR0_REGNUM+18) +#define IA64_RNAT_REGNUM (IA64_AR0_REGNUM+19) +#define IA64_FCR_REGNUM (IA64_AR0_REGNUM+21) +#define IA64_EFLAG_REGNUM (IA64_AR0_REGNUM+24) +#define IA64_CSD_REGNUM (IA64_AR0_REGNUM+25) +#define IA64_SSD_REGNUM (IA64_AR0_REGNUM+26) +#define IA64_CFLG_REGNUM (IA64_AR0_REGNUM+27) +#define IA64_FSR_REGNUM (IA64_AR0_REGNUM+28) +#define IA64_FIR_REGNUM (IA64_AR0_REGNUM+29) +#define IA64_FDR_REGNUM (IA64_AR0_REGNUM+30) +#define IA64_CCV_REGNUM (IA64_AR0_REGNUM+32) +#define IA64_UNAT_REGNUM (IA64_AR0_REGNUM+36) +#define IA64_FPSR_REGNUM (IA64_AR0_REGNUM+40) +#define IA64_ITC_REGNUM (IA64_AR0_REGNUM+44) +#define IA64_PFS_REGNUM (IA64_AR0_REGNUM+64) +#define IA64_LC_REGNUM (IA64_AR0_REGNUM+65) +#define IA64_EC_REGNUM (IA64_AR0_REGNUM+66) + +#define REGISTER_INDEX(N) (REGISTER_BYTE(N) / sizeof (unsigned long)) +#define BUFMAX (REGISTER_BYTES*2+10) +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; +static short error; + + +char *kgdb_version = KGDB_VERSION; + +/* debug > 0 prints ill-formed commands in valid packets & checksum errors */ +int debug_regs = 0; /* set to non-zero to print registers */ + +/* filled in by an external module */ +char *gdb_module_offsets; + +static const char hexchars[] = "0123456789abcdef"; + +/* Number of bytes of registers. */ +#define NUMREGBYTES REGISTER_BYTES +#define BREAKNUM 0x00003333300LL +#define KGDBBREAKNUM 0x6665UL + +static void inline +kgdb_pc(struct pt_regs *regs, unsigned long pc) +{ + regs->cr_iip = pc & ~0xf; + ia64_psr(regs)->ri = pc & 0x3; + return; +} + +void +breakpoint(void) +{ + asm volatile ("break.m 0x6665"); +} + +/*************************** ASSEMBLY CODE MACROS *************************/ +/* + * Put the error code here just in case the user cares. + * Likewise, the vector number here (since GDB only gets the signal + * number through the usual means, and that's not very specific). + * The called_from is the return address so he can tell how we entered kgdb. + * This will allow him to seperate out the various possible entries. + */ +#define REMOTE_DEBUG 0 /* set != to turn on printing (also available in info) */ + +#define PID_MAX PID_MAX_DEFAULT + +#ifdef CONFIG_SMP +void smp_send_nmi_allbutself(void); +#define IF_SMP(x) x +#undef MAX_NO_CPUS +#ifndef CONFIG_NO_KGDB_CPUS +#define CONFIG_NO_KGDB_CPUS 2 +#endif +#if CONFIG_NO_KGDB_CPUS > NR_CPUS +#define MAX_NO_CPUS NR_CPUS +#else +#define MAX_NO_CPUS CONFIG_NO_KGDB_CPUS +#endif +#define hold_init hold_on_sstep: 1, +#define MAX_CPU_MASK (unsigned long)((1LL << MAX_NO_CPUS) - 1LL) +#define NUM_CPUS num_online_cpus() +#else +#define IF_SMP(x) +#define hold_init +#undef MAX_NO_CPUS +#define MAX_NO_CPUS 1 +#define NUM_CPUS 1 +#endif +#define NOCPU (struct task_struct *)0xbad1fbad +struct kgdb_state { + int exceptionVector; + int signo; + unsigned long err_code; + struct pt_regs *regs; + struct unw_frame_info + *unw; + int ret; +}; +/* *INDENT-OFF* */ +struct kgdb_info { + int used_malloc; + void *called_from; + long long entry_itc; + int errcode; + unsigned long vector; + int print_debug_info; + unsigned long ia64_regs[REGISTER_BYTES / sizeof(long)]; +#ifdef CONFIG_SMP + int hold_on_sstep; + struct { + volatile struct task_struct *task; + int pid; + int hold; + struct pt_regs *regs; + unsigned long ia64_regs[REGISTER_BYTES / sizeof(long)]; + } cpus_waiting[MAX_NO_CPUS]; +#endif +} kgdb_info = {hold_init print_debug_info:REMOTE_DEBUG, vector:-1UL}; + +/* *INDENT-ON* */ + +#define used_m kgdb_info.used_malloc +/* + * This is little area we set aside to contain the stack we + * need to build to allow gdb to call functions. We use one + * per cpu to avoid locking issues. We will do all this work + * with interrupts off so that should take care of the protection + * issues. + */ +#define MAX_HW_BREAKPOINT (20) +long hw_break_total_dbr, hw_break_total_ibr; +#define HW_BREAKPOINT (hw_break_total_dbr + hw_break_total_ibr) +#define WATCH_INSTRUCTION 0x0 +#define WATCH_WRITE 0x1 +#define WATCH_READ 0x2 +#define WATCH_ACCESS 0x3 + +#define HWCAP_DBR ((1 << WATCH_WRITE) | (1 << WATCH_READ)) +#define HWCAP_IBR (1 << WATCH_INSTRUCTION) +struct hw_breakpoint { + unsigned enabled; + unsigned long capable; + unsigned long type; + unsigned long mask; + unsigned long addr; +} *breakinfo; + +#define LOOKASIDE_SIZE (200 + (sizeof(struct hw_breakpoint) * MAX_HW_BREAKPOINT)) +#define MALLOC_MAX LOOKASIDE_SIZE /* Max malloc size */ +struct { + unsigned int esp; + int array[LOOKASIDE_SIZE]; +} fn_call_lookaside[MAX_NO_CPUS]; + +#define IF_BIT 0x200 +#define TF_BIT 0x100 + +#define MALLOC_ROUND 8-1 + +static char malloc_array[MALLOC_MAX]; +IF_SMP(static void to_gdb(const char *mess)); +void * +malloc(int size) +{ + + if (size <= (MALLOC_MAX - used_m)) { + int old_used = used_m; + used_m += ((size + MALLOC_ROUND) & (~MALLOC_ROUND)); + return &malloc_array[old_used]; + } else { + return NULL; + } +} + +/* + * I/O dispatch functions... + * Based upon kgdboe, either call the ethernet + * handler or the serial one.. + */ +void +putDebugChar(int c) +{ + if (!kgdboe) { + tty_putDebugChar(c); + } else { + eth_putDebugChar(c); + } +} + +int +getDebugChar(void) +{ + if (!kgdboe) { + char ch; + while (1) { + ch = tty_getDebugChar() & 0x7f; + if (ch != 0x7f) + return ch; + } + } else { + return eth_getDebugChar(); + } +} + +void +flushDebugChar(void) +{ + if (!kgdboe) { + tty_flushDebugChar(); + } else { + eth_flushDebugChar(); + } +} + +/* + * Gdb calls functions by pushing agruments, including a return address + * on the stack and the adjusting EIP to point to the function. The + * whole assumption in GDB is that we are on a different stack than the + * one the "user" i.e. code that hit the break point, is on. This, of + * course is not true in the kernel. Thus various dodges are needed to + * do the call without directly messing with EIP (which we can not change + * as it is just a location and not a register. To adjust it would then + * require that we move every thing below EIP up or down as needed. This + * will not work as we may well have stack relative pointer on the stack + * (such as the pointer to regs, for example). + + * So here is what we do: + * We detect gdb attempting to store into the stack area and instead, store + * into the fn_call_lookaside.array at the same relative location as if it + * were the area ESP pointed at. We also trap ESP modifications + * and uses these to adjust fn_call_lookaside.esp. On entry + * fn_call_lookaside.esp will be set to point at the last entry in + * fn_call_lookaside.array. This allows us to check if it has changed, and + * if so, on exit, we add the registers we will use to do the move and a + * trap/ interrupt return exit sequence. We then adjust the eflags in the + * regs array (remember we now have a copy in the fn_call_lookaside.array) to + * kill the interrupt bit, AND we change EIP to point at our set up stub. + * As part of the register set up we preset the registers to point at the + * begining and end of the fn_call_lookaside.array, so all the stub needs to + * do is move words from the array to the stack until ESP= the desired value + * then do the rti. This will then transfer to the desired function with + * all the correct registers. Nifty huh? + */ +extern asmlinkage void fn_call_stub(void); +extern asmlinkage void fn_rtn_stub(void); +/* *INDENT-OFF* */ +/*__asm__("fn_rtn_stub:\n\t" + "movl %eax,%esp\n\t" + "fn_call_stub:\n\t" + "1:\n\t" + "addl $-4,%ebx\n\t" + "movl (%ebx), %eax\n\t" + "pushl %eax\n\t" + "cmpl %esp,%ecx\n\t" + "jne 1b\n\t" + "popl %eax\n\t" + "popl %ebx\n\t" + "popl %ecx\n\t" + "iret \n\t"); +*/ +/* *INDENT-ON* */ +#define gdb_ia64vector kgdb_info.vector +#define gdb_ia64errcode kgdb_info.errcode +#define waiting_cpus kgdb_info.cpus_waiting +#define remote_debug kgdb_info.print_debug_info +#define hold_cpu(cpu) kgdb_info.cpus_waiting[cpu].hold +/* gdb locks */ + +#ifdef CONFIG_SMP +static int in_kgdb_called; +static spinlock_t waitlocks[MAX_NO_CPUS] = + {[0 ... MAX_NO_CPUS - 1] = SPIN_LOCK_UNLOCKED }; +/* + * The following array has the thread pointer of each of the "other" + * cpus. We make it global so it can be seen by gdb. + */ +volatile int in_kgdb_entry_log[MAX_NO_CPUS]; +volatile struct pt_regs *in_kgdb_here_log[MAX_NO_CPUS]; +/* +static spinlock_t continuelocks[MAX_NO_CPUS]; +*/ +spinlock_t kgdb_spinlock = SPIN_LOCK_UNLOCKED; +/* waiters on our spinlock plus us */ +static atomic_t spinlock_waiters = ATOMIC_INIT(1); +static int spinlock_count = 0; +static int spinlock_cpu = 0; +/* + * Note we use nested spin locks to account for the case where a break + * point is encountered when calling a function by user direction from + * kgdb. Also there is the memory exception recursion to account for. + * Well, yes, but this lets other cpus thru too. Lets add a + * cpu id to the lock. + */ +#define KGDB_SPIN_LOCK(x) if( spinlock_count == 0 || \ + spinlock_cpu != smp_processor_id()){\ + atomic_inc(&spinlock_waiters); \ + while (! spin_trylock(x)) {\ + in_kgdb(linux_regs, unw_info);\ + }\ + atomic_dec(&spinlock_waiters); \ + spinlock_count = 1; \ + spinlock_cpu = smp_processor_id(); \ + }else{ \ + spinlock_count++; \ + } +#define KGDB_SPIN_UNLOCK(x) if( --spinlock_count == 0) spin_unlock(x) +#else +unsigned kgdb_spinlock = 0; +#define KGDB_SPIN_LOCK(x) --*x +#define KGDB_SPIN_UNLOCK(x) ++*x +#endif + +int +hex(char ch) +{ + if ((ch >= 'a') && (ch <= 'f')) + return (ch - 'a' + 10); + if ((ch >= '0') && (ch <= '9')) + return (ch - '0'); + if ((ch >= 'A') && (ch <= 'F')) + return (ch - 'A' + 10); + return (-1); +} + +/* scan for the sequence $# */ +void +getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + char ch; + + do { + /* wait around for the start character, ignore all other characters */ + while ((ch = (getDebugChar() & 0x7f)) != '$') ; + checksum = 0; + xmitcsum = -1; + + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX) { + ch = getDebugChar() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum += hex(getDebugChar() & 0x7f); + if ((remote_debug) && (checksum != xmitcsum)) { + printk + ("bad checksum. My count = 0x%x, sent=0x%x. buf=%s\n", + checksum, xmitcsum, buffer); + } + + if (checksum != xmitcsum) { + putDebugChar('-'); /* failed checksum */ + } + else { + putDebugChar('+'); /* successful transfer */ + /* if a sequence char is present, reply the sequence ID */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + /* remove sequence chars from buffer */ + count = strlen(buffer); + for (i = 3; i <= count; i++) + buffer[i - 3] = buffer[i]; + } + } + } + } while (checksum != xmitcsum); + + if (remote_debug) + printk("R:%s\n", buffer); + flushDebugChar(); +} + +/* send the packet in buffer. */ + +void +putpacket(char *buffer) +{ + unsigned char checksum; + int count; + char ch; + + /* $#. */ + + if (!kgdboe) { + do { + if (remote_debug) + printk("T:%s\n", buffer); + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count])) { + putDebugChar(ch); + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum % 16]); + flushDebugChar(); + + } while ((getDebugChar() & 0x7f) != '+'); + } else { + /* + * For udp, we can not transfer too much bytes once. + * We only transfer MAX_SEND_COUNT size bytes each time + */ + +#define MAX_SEND_COUNT 30 + + int send_count = 0, i = 0; + char send_buf[MAX_SEND_COUNT]; + + do { + if (remote_debug) + printk("T:%s\n", buffer); + putDebugChar('$'); + checksum = 0; + count = 0; + send_count = 0; + while ((ch = buffer[count])) { + if (send_count >= MAX_SEND_COUNT) { + for(i = 0; i < MAX_SEND_COUNT; i++) { + putDebugChar(send_buf[i]); + } + flushDebugChar(); + send_count = 0; + } else { + send_buf[send_count] = ch; + checksum += ch; + count ++; + send_count++; + } + } + for(i = 0; i < send_count; i++) + putDebugChar(send_buf[i]); + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum % 16]); + flushDebugChar(); + } while ((getDebugChar() & 0x7f) != '+'); + } +} + + +void +debug_error(char *format, char *parm) +{ + if (remote_debug) + printk(format, parm); +} + +static void +print_regs(struct pt_regs *regs) +{ + printk("B0=0x%lx\n", regs->b0); + printk("IPSR=0x%lx\n", regs->cr_ipsr); + printk("IIP=0x%lx\n", regs->cr_iip); + printk("\n"); + +} /* print_regs */ + + + +static void +unw_ar_regs(struct unw_frame_info *unw, unsigned long *regs, int put) +{ + if (put) + unw_access_ar(unw, UNW_AR_BSP, ®s[REGISTER_INDEX(IA64_BSP_REGNUM)], put); + else + unw_get_bsp(unw, ®s[REGISTER_INDEX(IA64_BSP_REGNUM)]); + unw_access_ar(unw, UNW_AR_BSPSTORE, ®s[REGISTER_INDEX(IA64_BSPSTORE_REGNUM)], put); + unw_access_ar(unw, UNW_AR_PFS, ®s[REGISTER_INDEX(IA64_PFS_REGNUM)], put); + unw_access_ar(unw, UNW_AR_RNAT, ®s[REGISTER_INDEX(IA64_RNAT_REGNUM)], put); + unw_access_ar(unw, UNW_AR_UNAT, ®s[REGISTER_INDEX(IA64_UNAT_REGNUM)], put); + unw_access_ar(unw, UNW_AR_LC, ®s[REGISTER_INDEX(IA64_LC_REGNUM)], put); + unw_access_ar(unw, UNW_AR_EC, ®s[REGISTER_INDEX(IA64_EC_REGNUM)], put); + unw_access_ar(unw, UNW_AR_FPSR, ®s[REGISTER_INDEX(IA64_FPSR_REGNUM)], put); + unw_access_ar(unw, UNW_AR_RSC, ®s[REGISTER_INDEX(IA64_RSC_REGNUM)], put); + unw_access_ar(unw, UNW_AR_CCV, ®s[REGISTER_INDEX(IA64_CCV_REGNUM)], put); + unw_access_ar(unw, UNW_AR_CSD, ®s[REGISTER_INDEX(IA64_CSD_REGNUM)], put); + unw_access_ar(unw, UNW_AR_SSD, ®s[REGISTER_INDEX(IA64_SSD_REGNUM)], put); + return; +} + +static void +unw_get_regs(struct unw_frame_info *unw, unsigned long *regs, struct pt_regs *ptregs) +{ + int i, j; + char nat; + unsigned long *preg; + struct ia64_fpreg *fr, freg; + + memset(regs, 0, sizeof (kgdb_info.ia64_regs)); + + for (i = 1; i < 32; i++) { + if (ptregs && ((i >= 8 && i <= 11) || (i >= 12 && i <= 15))) + continue; + unw_access_gr(unw, i, ®s[REGISTER_INDEX(IA64_GR0_REGNUM + i)], &nat, 0); + regs[REGISTER_INDEX(IA64_NAT0_REGNUM + i)] = nat; + } + + if (ptregs) { + for (preg = &ptregs->r8, i = 8; i < 12; i++, preg++) + regs[REGISTER_INDEX(IA64_GR0_REGNUM + i)] = *preg; + regs[REGISTER_INDEX(IA64_GR0_REGNUM + 12)] = ptregs->r12; + regs[REGISTER_INDEX(IA64_GR0_REGNUM + 13)] = ptregs->r13; + regs[REGISTER_INDEX(IA64_GR0_REGNUM + 14)] = ptregs->r14; + regs[REGISTER_INDEX(IA64_GR0_REGNUM + 15)] = ptregs->r15; + } + + for (i = 1; i < 8; i++) { + if (ptregs && (i == 6 || i == 7)) + continue; + unw_access_br(unw, i, ®s[REGISTER_INDEX(IA64_BR0_REGNUM + i)], 0); + } + + if (ptregs) { + regs[REGISTER_INDEX(IA64_BR0_REGNUM)] = ptregs->b0; + regs[REGISTER_INDEX(IA64_BR0_REGNUM + 6)] = ptregs->b6; + regs[REGISTER_INDEX(IA64_BR0_REGNUM + 7)] = ptregs->b7; + } else { + unw_access_br(unw, i, ®s[REGISTER_INDEX(IA64_BR0_REGNUM + 6)], 0); + unw_access_br(unw, i, ®s[REGISTER_INDEX(IA64_BR0_REGNUM + 7)], 0); + unw_access_br(unw, 0, ®s[REGISTER_INDEX(IA64_BR0_REGNUM)], 0); + } + + if (ptregs) + fr = &ptregs->f6; + else + fr = &freg; + + for (i = 6; i < 12; i++) { + if (!ptregs) + unw_access_fr(unw, i, fr, 0); + regs[REGISTER_INDEX(IA64_FR0_REGNUM + 6 + i)] = fr->u.bits[0]; + regs[REGISTER_INDEX(IA64_FR0_REGNUM + 6 + i + 1)] = fr->u.bits[1]; + if (ptregs) + fr++; + } + + unw_ar_regs(unw, regs, 0); + + unw_access_pr(unw, ®s[REGISTER_INDEX(IA64_PR_REGNUM)], 0); + for (j = 1, i = 0; i < 64; i++, j <<= 1, i++) + regs[REGISTER_INDEX(IA64_PR0_REGNUM + i)] = + (regs[REGISTER_INDEX(IA64_PR_REGNUM)] & j) ? 1 : 0; + + + if (!ptregs) { + unw_get_ip(unw, ®s[REGISTER_INDEX(IA64_IP_REGNUM)]); + unw_get_cfm(unw, ®s[REGISTER_INDEX(IA64_CFM_REGNUM)]); + } else { + regs[REGISTER_INDEX(IA64_IP_REGNUM)] = ptregs->cr_iip; + regs[REGISTER_INDEX(IA64_PSR_REGNUM)] = ptregs->cr_ipsr; + regs[REGISTER_INDEX(IA64_CFM_REGNUM)] = ptregs->cr_ifs; + } + + return; +} + +static void +kgdb_get_block_task(struct task_struct *p, unsigned long *ia64regs) +{ + struct unw_frame_info info; + unsigned long ip; + int count = 0; + + unw_init_from_blocked_task(&info, p); + ip = 0UL; + do { + if (unw_unwind(&info) < 0) + return; + unw_get_ip(&info, &ip); + if (!in_sched_functions(ip)) + break; + } while (count++ < 16); + + if (!ip) + return; + + unw_get_regs(&info, ia64regs, (struct pt_regs *) 0); + + return; +} + +static void +regs_to_gdb_regs(struct kgdb_state *state) +{ + unw_get_regs(state->unw, kgdb_info.ia64_regs, state->regs); + return; +} /* regs_to_gdb_regs */ + +static void +gdb_regs_to_regs(struct kgdb_state *state) +{ + int i; + char nat; + unsigned long *regs, *preg; + struct ia64_fpreg *fr; + struct unw_frame_info *unw; + + unw = state->unw; + regs = kgdb_info.ia64_regs; + + for (i = 1; i < 32; i++) { + if ((i >= 8 && i <= 11) || (i >= 12 && i <= 15)) + continue; + nat = (char) regs[REGISTER_INDEX(IA64_NAT0_REGNUM + i)]; + unw_access_gr(unw, i, ®s[REGISTER_INDEX(IA64_GR0_REGNUM + i)], &nat, 1); + } + + for (preg = &state->regs->r8, i = 8; i < 12; i++, preg++) + regs[REGISTER_INDEX(IA64_GR0_REGNUM + i)] = *preg; + state->regs->r12 = regs[REGISTER_INDEX(IA64_GR0_REGNUM + 12)]; + state->regs->r12 = regs[REGISTER_INDEX(IA64_GR0_REGNUM + 13)]; + state->regs->r12 = regs[REGISTER_INDEX(IA64_GR0_REGNUM + 14)]; + state->regs->r12 = regs[REGISTER_INDEX(IA64_GR0_REGNUM + 15)]; + + for (i = 1; i < 8; i++) { + if ((i == 6 || i == 7)) + continue; + unw_access_br(unw, i, ®s[REGISTER_INDEX(IA64_BR0_REGNUM + i)], 1); + } + state->regs->b0 = regs[REGISTER_INDEX(IA64_BR0_REGNUM)]; + state->regs->b6 = regs[REGISTER_INDEX(IA64_BR0_REGNUM + 6)]; + state->regs->b7 = regs[REGISTER_INDEX(IA64_BR0_REGNUM + 7)]; + + for (fr = &state->regs->f6, i = 6; i < 12; i++, fr++) { + fr->u.bits[0] = regs[REGISTER_INDEX(IA64_FR0_REGNUM + 6 + i)]; + fr->u.bits[1] = regs[REGISTER_INDEX(IA64_FR0_REGNUM + 6 + i + 1)]; + } + + unw_ar_regs(unw, regs, 1); + + unw_access_pr(unw, ®s[IA64_PR_REGNUM], 1); + + state->regs->cr_iip = regs[REGISTER_INDEX(IA64_IP_REGNUM)]; + state->regs->cr_ipsr = regs[REGISTER_INDEX(IA64_PSR_REGNUM)]; + state->regs->cr_ifs = regs[REGISTER_INDEX(IA64_CFM_REGNUM)]; + + return; + +} /* gdb_regs_to_regs */ + +int thread_list = 0; + +void +get_gdb_regs(struct task_struct *p, struct kgdb_state *state) +{ + IF_SMP(int i); + if (!p || p == current) { + regs_to_gdb_regs(state); + return; + } +#ifdef CONFIG_SMP + for (i = 0; i < MAX_NO_CPUS; i++) { + if (p == kgdb_info.cpus_waiting[i].task && + !user_mode(kgdb_info.cpus_waiting[i].regs)){ + memcpy(kgdb_info.ia64_regs, kgdb_info.cpus_waiting[i].ia64_regs, + sizeof(kgdb_info.ia64_regs)); + return; + } + } +#endif +/* + * This code is to give a more informative notion of where a process + * is waiting. It is used only when the user asks for a thread info + * list. If he then switches to the thread, s/he will find the task + * is in schedule, but a back trace should show the same info we come + * up with. Some of this code was purloined from get_wchan; + */ + + if (!thread_list) + return; + else if (p->state == TASK_RUNNING) + return; + + kgdb_get_block_task(p, kgdb_info.ia64_regs); + + return; + +} + +/* Indicate to caller of mem2hex or hex2mem that there has been an + error. */ +static volatile int mem_err = 0; +static volatile int mem_err_expected = 0; +static volatile int mem_err_cnt = 0; + +int +get_char(char *addr, unsigned char *data) +{ + mm_segment_t fs; + int ret; + + if ((unsigned long) addr < DEFAULT_TASK_SIZE) + return -EFAULT; + + wmb(); + fs = get_fs(); + set_fs(KERNEL_DS); + + if (get_user(*data, addr) != 0) + ret = -EFAULT; + else + ret = 0; + + set_fs(fs); + return ret; +} + +int +set_char(char *addr, int val) +{ + mm_segment_t fs; + int ret; + + if ((unsigned long) addr < DEFAULT_TASK_SIZE) + return -EFAULT; + + wmb(); + fs = get_fs(); + set_fs(KERNEL_DS); + + if (put_user(val, addr) != 0) + ret = -EFAULT; + else + ret = 0; + + set_fs(fs); + return ret; +} + +/* convert the memory pointed to by mem into hex, placing result in buf */ +/* return a pointer to the last char put in buf (null) */ +/* If MAY_FAULT is non-zero, then we should set mem_err in response to + a fault; if zero treat a fault like any other fault in the stub. */ +char * +mem2hex(char *mem, char *buf, int count, int may_fault) +{ + int i; + unsigned char ch; + + if (may_fault) { + mem_err_expected = 1; + mem_err = 0; + } + + for (i = 0; i < count; i++) { + /* printk("%lx = ", mem) ; */ + + if (get_char(mem++, &ch)) { + if (remote_debug) + printk("Mem fault fetching from addr %lx\n", (long) (mem - 1)); + *buf = 0; /* truncate buffer */ + return buf; + } + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch % 16]; + } + *buf = 0; + if (may_fault) + mem_err_expected = 0; + return (buf); +} + +/* convert the hex array pointed to by buf into binary to be placed in mem */ +/* return a pointer to the character AFTER the last byte written */ +/* NOTE: We use the may fault flag to also indicate if the write is to + * the registers (0) or "other" memory (!=0) + */ +char * +hex2mem(char *buf, char *mem, int count, int may_fault) +{ + int i; + unsigned char ch; + + if (may_fault) { + mem_err_expected = 1; + mem_err = 0; + } + for (i = 0; i < count; i++) { + ch = hex(*buf++) << 4; + ch = ch + hex(*buf++); + if (set_char(mem++, ch)) { + if (remote_debug) + printk("Mem fault storing to addr %lx\n", + (long) (mem - 1)); + return (mem); + } + } + if (may_fault) + mem_err_expected = 0; + return (mem); +} + +/**********************************************/ +/* WHILE WE FIND NICE HEX CHARS, BUILD AN INT */ +/* RETURN NUMBER OF CHARS PROCESSED */ +/**********************************************/ +int +hexToLong(char **ptr, unsigned long *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0UL; + + while (**ptr) { + hexValue = hex(**ptr); + if (hexValue >= 0) { + *intValue = (*intValue << 4) | hexValue; + numChars++; + } else + break; + + (*ptr)++; + } + + return (numChars); +} + +int +hexToInt(char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + while (**ptr) { + hexValue = hex(**ptr); + if (hexValue >= 0) { + *intValue = (*intValue << 4) | hexValue; + numChars++; + } else + break; + + (*ptr)++; + } + + return (numChars); +} + +#define stubhex(h) hex(h) +#ifdef old_thread_list + +static int +stub_unpack_int(char *buff, int fieldlength) +{ + int nibble; + int retval = 0; + + while (fieldlength) { + nibble = stubhex(*buff++); + retval |= nibble; + fieldlength--; + if (fieldlength) + retval = retval << 4; + } + return retval; +} +#endif +static char * +pack_hex_byte(char *pkt, int byte) +{ + *pkt++ = hexchars[(byte >> 4) & 0xf]; + *pkt++ = hexchars[(byte & 0xf)]; + return pkt; +} + +#define BUF_THREAD_ID_SIZE 16 + +static char * +pack_threadid(char *pkt, threadref * id) +{ + char *limit; + unsigned char *altid; + + altid = (unsigned char *) id; + limit = pkt + BUF_THREAD_ID_SIZE; + while (pkt < limit) + pkt = pack_hex_byte(pkt, *altid++); + return pkt; +} + +#ifdef old_thread_list +static char * +unpack_byte(char *buf, int *value) +{ + *value = stub_unpack_int(buf, 2); + return buf + 2; +} + +static char * +unpack_threadid(char *inbuf, threadref * id) +{ + char *altref; + char *limit = inbuf + BUF_THREAD_ID_SIZE; + int x, y; + + altref = (char *) id; + + while (inbuf < limit) { + x = stubhex(*inbuf++); + y = stubhex(*inbuf++); + *altref++ = (x << 4) | y; + } + return inbuf; +} +#endif +void +int_to_threadref(threadref * id, int value) +{ + unsigned char *scan; + + scan = (unsigned char *) id; + { + int i = 4; + while (i--) + *scan++ = 0; + } + *scan++ = (value >> 24) & 0xff; + *scan++ = (value >> 16) & 0xff; + *scan++ = (value >> 8) & 0xff; + *scan++ = (value & 0xff); +} +int +int_to_hex_v(unsigned char * id, int value) +{ + unsigned char *start = id; + int shift; + int ch; + + for (shift = 28; shift >= 0; shift -= 4) { + if ((ch = (value >> shift) & 0xf) || (id != start)) { + *id = hexchars[ch]; + id++; + } + } + if (id == start) + *id++ = '0'; + return id - start; +} +#ifdef old_thread_list + +static int +threadref_to_int(threadref * ref) +{ + int i, value = 0; + unsigned char *scan; + + scan = (char *) ref; + scan += 4; + i = 4; + while (i-- > 0) + value = (value << 8) | ((*scan++) & 0xff); + return value; +} +#endif +static int +cmp_str(char *s1, char *s2, int count) +{ + while (count--) { + if (*s1++ != *s2++) + return 0; + } + return 1; +} + +#if 1 /* this is a hold over from 2.4 where O(1) was "sometimes" */ +extern struct task_struct *kgdb_get_idle(int cpu); +#define idle_task(cpu) kgdb_get_idle(cpu) +#else +#define idle_task(cpu) init_tasks[cpu] +#endif + +extern int kgdb_pid_init_done; + +#ifdef CONFIG_KGDB_EARLY +struct task_struct kgdb_task = {.comm = "kgdb-dummy"}; +#endif + +struct task_struct * +getthread(int pid) +{ + struct task_struct *thread; + if (pid >= PID_MAX && pid <= (PID_MAX + MAX_NO_CPUS)) { + + return idle_task(pid - PID_MAX); + } else { + /* + * find_task_by_pid is relatively safe all the time + * Other pid functions require lock downs which imply + * that we may be interrupting them (as we get here + * in the middle of most any lock down). + * Still we don't want to call until the table exists! + */ + if (kgdb_pid_init_done){ + thread = find_task_by_pid(pid); + if (thread) { + return thread; + } + } + } +#ifdef CONFIG_KGDB_EARLY + if (!kgdb_pid_init_done) + return &kgdb_task; +#endif + return NULL; +} +/* *INDENT-OFF* */ + +enum instruction_type {A, I, M, F, B, L, X, u}; + +static enum instruction_type bundle_encoding[32][3] = { + { M, I, I }, /* 00 */ + { M, I, I }, /* 01 */ + { M, I, I }, /* 02 */ + { M, I, I }, /* 03 */ + { M, L, X }, /* 04 */ + { M, L, X }, /* 05 */ + { u, u, u }, /* 06 */ + { u, u, u }, /* 07 */ + { M, M, I }, /* 08 */ + { M, M, I }, /* 09 */ + { M, M, I }, /* 0A */ + { M, M, I }, /* 0B */ + { M, F, I }, /* 0C */ + { M, F, I }, /* 0D */ + { M, M, F }, /* 0E */ + { M, M, F }, /* 0F */ + { M, I, B }, /* 10 */ + { M, I, B }, /* 11 */ + { M, B, B }, /* 12 */ + { M, B, B }, /* 13 */ + { u, u, u }, /* 14 */ + { u, u, u }, /* 15 */ + { B, B, B }, /* 16 */ + { B, B, B }, /* 17 */ + { M, M, B }, /* 18 */ + { M, M, B }, /* 19 */ + { u, u, u }, /* 1A */ + { u, u, u }, /* 1B */ + { M, F, B }, /* 1C */ + { M, F, B }, /* 1D */ + { u, u, u }, /* 1E */ + { u, u, u }, /* 1F */ +}; + +#define MAX_BREAK_POINTS (20) + +struct z0_break_point { + unsigned long addr; + unsigned long bundle[2]; + unsigned int enabled; +} z0_break_point[MAX_BREAK_POINTS]; + +int kgdb_arch_set_breakpoint(unsigned long addr) +{ + unsigned long slot = addr & 0xf, bundle_addr; + unsigned long template; + struct bundle { + struct { + unsigned long long template : 5; + unsigned long long slot0 : 41; + unsigned long long slot1_p0 : 64-46; + } quad0; + struct { + unsigned long long slot1_p1 : 41 - (64-46); + unsigned long long slot2 : 41; + } quad1; + } bundle; + int i; + struct z0_break_point *z0 = NULL; + unsigned long valid; + + asm volatile("probe.w %0 = %1, 0" : "=r" (valid) : "r" (addr) : "memory"); + if (!valid) + return 0; + asm volatile("probe.w %0 = %1, 0" : "=r" (valid) : "r" (addr+8) : "memory"); + if (!valid) + return 0; + + + for (i = 0; i < MAX_BREAK_POINTS; i++) + if (!z0_break_point[i].enabled) { + z0 = &z0_break_point[i]; + break; + } + + if (!z0) + return 0; + + if (slot > 2) + slot = 0; + + bundle_addr = addr & ~0xFULL; + memcpy(&bundle, (unsigned long *)bundle_addr, sizeof(bundle)); + memcpy(z0->bundle, &bundle, sizeof(bundle)); + + template = bundle.quad0.template; + if (slot == 1 && bundle_encoding[template][1] == L) + slot = 2; + switch (slot) { + case 0: + bundle.quad0.slot0 = BREAKNUM; + break; + case 1: + bundle.quad0.slot1_p0 = BREAKNUM; + bundle.quad1.slot1_p1 = (BREAKNUM >> (64-46)); + break; + case 2: + bundle.quad1.slot2 = BREAKNUM; + break; + } + + memcpy((char *) bundle_addr, (char *) &bundle, sizeof(bundle)); + flush_icache_range(bundle_addr, bundle_addr + sizeof(bundle)); + z0->addr = addr; + z0->enabled = 1; + + return 1; +} + +int kgdb_arch_remove_breakpoint(unsigned long addr) +{ + struct z0_break_point *z0 = NULL; + int i; + + for (i = 0; i < MAX_BREAK_POINTS; i++) + if (z0_break_point[i].enabled && z0_break_point[i].addr == addr) { + z0 = &z0_break_point[i]; + break; + } + + if (!z0) + return 0; + + addr = addr & ~0xFULL; + (void) memcpy((char *) addr, (char *) z0->bundle, sizeof (z0->bundle)); + flush_icache_range(addr, addr + sizeof(z0->bundle)); + z0->enabled = 0; + return 1; +} + + +unsigned long hw_breakpoint_status; + +int hw_breakpoint_init; + +void +do_init_hw_break(void) +{ + s64 status; + int i; + + hw_breakpoint_init = 1; + +#ifdef CONFIG_IA64_HP_SIM + hw_break_total_ibr = 8; + hw_break_total_dbr = 8; + status = 0; +#else + status = ia64_pal_debug_info(&hw_break_total_ibr, &hw_break_total_dbr); +#endif + + if (status) { + printk(KERN_INFO "do_init_hw_break: pal call failed %d\n", (int) status); + return; + } + + if (HW_BREAKPOINT > MAX_HW_BREAKPOINT) { + printk(KERN_INFO "do_init_hw_break: %d exceeds max %d\n", (int) HW_BREAKPOINT, + (int) MAX_HW_BREAKPOINT); + + while ((HW_BREAKPOINT > MAX_HW_BREAKPOINT) && hw_break_total_ibr != 1) + hw_break_total_ibr--; + while (HW_BREAKPOINT > MAX_HW_BREAKPOINT) + hw_break_total_dbr--; + } + + breakinfo = malloc(HW_BREAKPOINT * sizeof(struct hw_breakpoint)); + + if (!breakinfo) { + printk(KERN_INFO "Failed to allocate hardware break array\n"); + return; + } + + memset(breakinfo, 0, HW_BREAKPOINT * sizeof(struct hw_breakpoint)); + + for (i = 0; i < hw_break_total_dbr; i++) + breakinfo[i].capable = HWCAP_DBR; + + for (; i < HW_BREAKPOINT; i++) + breakinfo[i].capable = HWCAP_IBR; + + return; +} + +void +correct_hw_break(void) +{ + int breakno; + + if (!breakinfo) + return; + + for (breakno = 0; breakno < HW_BREAKPOINT; breakno++) { + if (breakinfo[breakno].enabled) { + if (breakinfo[breakno].capable & HWCAP_IBR) { + int ibreakno = breakno - hw_break_total_dbr; + ia64_set_ibr(ibreakno << 1, breakinfo[breakno].addr); + ia64_set_ibr((ibreakno << 1) + 1, + (~breakinfo[breakno].mask & ((1UL << 56UL) - 1)) | + (1UL << 56UL) | (1UL << 63UL)); + } + else { + ia64_set_dbr(breakno << 1, breakinfo[breakno].addr); + ia64_set_dbr((breakno << 1) + 1, + (~breakinfo[breakno].mask & ((1UL << 56UL) - 1)) | + (1UL << 56UL) | (breakinfo[breakno].type << 62UL)); + } + } + else { + if (breakinfo[breakno].capable & HWCAP_IBR) + ia64_set_ibr(((breakno - hw_break_total_dbr) << 1) + 1, 0); + else + ia64_set_dbr((breakno << 1) + 1, 0); + } + } + + return; +} + +int +hardware_breakpoint(unsigned long addr, int length, int type, int action) +{ + int breakno, found, watch; + unsigned long mask; + extern unsigned long _start[]; + + if (!hw_breakpoint_init) + do_init_hw_break(); + + if (!breakinfo) + return 0; + else if (addr == (unsigned long) _start) + return 1; + + if (type == WATCH_ACCESS) + mask = HWCAP_DBR; + else + mask = 1UL << type; + + for (watch = 0, found = 0, breakno = 0; breakno < HW_BREAKPOINT; breakno++) { + if (action) { + if (breakinfo[breakno].enabled || !(breakinfo[breakno].capable & mask)) + continue; + breakinfo[breakno].enabled = 1; + breakinfo[breakno].type = type; + breakinfo[breakno].mask = length - 1; + breakinfo[breakno].addr = addr; + watch = breakno; + } else if (breakinfo[breakno].enabled && + ((length < 0 && breakinfo[breakno].addr == addr) || + ((breakinfo[breakno].capable & mask) && + (breakinfo[breakno].mask == (length - 1)) && + (breakinfo[breakno].addr == addr)))) { + breakinfo[breakno].enabled = 0; + breakinfo[breakno].type = 0UL; + } + else + continue; + found++; + if (type != WATCH_ACCESS) + break; + else if (found == 2) + break; + else + mask = HWCAP_IBR; + } + + if (type == WATCH_ACCESS && found == 1) { + breakinfo[watch].enabled = 0; + found = 0; + } + + return found; +} + +#ifdef oldbreak_protocol +int +remove_hw_break(unsigned breakno) +{ + if (!breakinfo[breakno].enabled) + return -1; + + breakinfo[breakno].enabled = 0; + + return 0; +} + +int +set_hw_break(unsigned breakno, unsigned type, unsigned len, unsigned addr) +{ + if (breakinfo[breakno].enabled) + return -1; + + breakinfo[breakno].enabled = 1; + breakinfo[breakno].type = type; + breakinfo[breakno].mask = len - 1; + breakinfo[breakno].addr = addr; + return 0; +} +#endif + +static void inline +normalize(struct unw_frame_info *running, struct pt_regs *regs) +{ + unsigned long sp; + + /* + * unwind to last frame before exception which will be an error + * and then fetch the bsp at exception time. + */ + + do { + unw_get_sp(running, &sp); + if ((sp + 0x10) >= (unsigned long) regs) + break; + } while (unw_unwind(running) >= 0); + + return; +} + +#ifdef CONFIG_SMP +static int in_kgdb_console = 0; + + +static void +snap_regs(struct unw_frame_info *unw_info, void *data) +{ + struct pt_regs *regs; + int cpu; + + regs = data; + normalize(unw_info, regs); + cpu = smp_processor_id(); + unw_get_regs(unw_info, kgdb_info.cpus_waiting[cpu].ia64_regs, regs); + + return; +} + +int +in_kgdb(struct pt_regs *regs, struct unw_frame_info *unw_info) +{ + unsigned flags; + int cpu = smp_processor_id(); + in_kgdb_called = 1; + + preempt_disable(); + + if (!spin_is_locked(&kgdb_spinlock)) { + if (in_kgdb_here_log[cpu] || /* we are holding this cpu */ + in_kgdb_console) { /* or we are doing slow i/o */ + preempt_enable_no_resched(); + return 1; + } + preempt_enable_no_resched(); + return 0; + } + + /* As I see it the only reason not to let all cpus spin on + * the same spin_lock is to allow selected ones to proceed. + * This would be a good thing, so we leave it this way. + * Maybe someday.... Done ! + + * in_kgdb() is called from an NMI so we don't pretend + * to have any resources, like printk() for example. + */ + + kgdb_local_irq_save(flags); /* only local here, to avoid hanging */ + /* + * log arival of this cpu + * The NMI keeps on ticking. Protect against recurring more + * than once, and ignor the cpu that has the kgdb lock + */ + in_kgdb_entry_log[cpu]++; + in_kgdb_here_log[cpu] = regs; + if (cpu == spinlock_cpu || waiting_cpus[cpu].task) + goto exit_in_kgdb; + + /* + * For protection of the initilization of the spin locks by kgdb + * it locks the kgdb spinlock before it gets the wait locks set + * up. We wait here for the wait lock to be taken. If the + * kgdb lock goes away first?? Well, it could be a slow exit + * sequence where the wait lock is removed prior to the kgdb lock + * so if kgdb gets unlocked, we just exit. + */ + + while (spin_is_locked(&kgdb_spinlock) && + !spin_is_locked(waitlocks + cpu)) ; + if (!spin_is_locked(&kgdb_spinlock)) + goto exit_in_kgdb; + + if (unw_info) + unw_get_regs(unw_info, kgdb_info.cpus_waiting[cpu].ia64_regs, regs); + else if (!user_mode(regs)) { + if (current->state == TASK_RUNNING) + unw_init_running(snap_regs, regs); + else + kgdb_get_block_task(current, kgdb_info.cpus_waiting[cpu].ia64_regs); + } + waiting_cpus[cpu].task = current; + waiting_cpus[cpu].pid = (current->pid) ? : (PID_MAX + cpu); + waiting_cpus[cpu].regs = regs; + + spin_unlock_wait(waitlocks + cpu); + + + /* + * log departure of this cpu + */ + waiting_cpus[cpu].task = 0; + waiting_cpus[cpu].pid = 0; + waiting_cpus[cpu].regs = 0; + correct_hw_break(); + exit_in_kgdb: + in_kgdb_here_log[cpu] = 0; + kgdb_local_irq_restore(flags); + preempt_enable_no_resched(); + return 1; + /* + spin_unlock(continuelocks + smp_processor_id()); + */ +} + +void +smp__in_kgdb(struct pt_regs regs) +{ + in_kgdb(®s, NULL); +} +#else +int +in_kgdb(struct pt_regs *regs, struct unw_frame_info *unw) +{ + return (kgdb_spinlock); +} +#endif + +void +printexceptioninfo(int exceptionNo, int errorcode, char *buffer) +{ + switch (exceptionNo) { + case 1: /* debug exception */ + break; + case 3: /* breakpoint */ + sprintf(buffer, "Software breakpoint"); + return; + default: + sprintf(buffer, "Details not available"); + return; + } + sprintf(buffer, "Unknown trap"); + return; +} + +/* + * This function does all command procesing for interfacing to gdb. + * + * NOTE: The INT nn instruction leaves the state of the interrupt + * enable flag UNCHANGED. That means that when this routine + * is entered via a breakpoint (INT 3) instruction from code + * that has interrupts enabled, then interrupts will STILL BE + * enabled when this routine is entered. The first thing that + * we do here is disable interrupts so as to prevent recursive + * entries and bothersome serial interrupts while we are + * trying to run the serial port in polled mode. + * + * For kernel version 2.1.xx the kgdb_cli() actually gets a spin lock so + * it is always necessary to do a restore_flags before returning + * so as to let go of that lock. + */ + +static void do_kgdb_handle_exception(struct unw_frame_info *, void *data); + +int +kgdb_handle_exception(int exceptionVector, int signo, unsigned long err_code, struct pt_regs *linux_regs) +{ + struct kgdb_state info; + + /* + * If the entry is not from the kernel then return to the Linux + * trap handler and let it process the interrupt normally. + */ + if (user_mode(linux_regs)) { + printk("ignoring non-kernel exception\n"); + print_regs(linux_regs); + return (0); + } + + preempt_disable(); + + kgdb_info.called_from = __builtin_return_address(0); + + info.exceptionVector = exceptionVector; + info.signo = signo; + info.err_code = err_code; + info.regs = linux_regs; + info.unw = (void *) 0; + unw_init_running(do_kgdb_handle_exception, &info); + + preempt_enable_no_resched(); + + return info.ret; +} + +static int kgdb_dbregs_enabled; + +static void +do_kgdb_handle_exception(struct unw_frame_info *unw_info, void *data) +{ + int exceptionVector, signo, have_regs = 0; + unsigned long err_code; + struct pt_regs *linux_regs; + struct kgdb_state *info; + struct task_struct *usethread = NULL; + struct task_struct *thread_list_start = 0, *thread = NULL; + int length; + unsigned long addr; + char *ptr; + int newPC; + threadref thref; + int threadid; + int thread_min = PID_MAX + MAX_NO_CPUS; +#ifdef old_thread_list + int maxthreads; +#endif + int nothreads; + unsigned long flags; +#define gdb_regs kgdb_info.ia64_regs + IF_SMP(int entry_state = 0); /* 0, ok, 1, no nmi, 2 sync failed */ +#define NO_NMI 1 +#define NO_SYNC 2 +#define ptregs (*linux_regs) +#define NUMREGS NUM_REGS + + info = data; + info->unw = unw_info; + exceptionVector = info->exceptionVector; + signo = info->signo; + err_code = info->err_code; + linux_regs = info->regs; + + normalize(unw_info, linux_regs); + + /* + * If we're using eth mode, set the 'mode' in the netdevice. + */ + if (kgdboe) + netpoll_set_trap(1); + + kgdb_local_irq_save(flags); + + /* Get kgdb spinlock */ + + KGDB_SPIN_LOCK(&kgdb_spinlock); + kgdb_info.entry_itc = ia64_get_itc(); + /* + * We depend on this spinlock and the NMI watch dog to control the + * other cpus. They will arrive at "in_kgdb()" as a result of the + * NMI and will wait there for the following spin locks to be + * released. + */ +#ifdef CONFIG_SMP + +#if 0 + if (cpu_callout_map & ~MAX_CPU_MASK) { + printk("kgdb : too many cpus, possibly not mapped" + " in contiguous space, change MAX_NO_CPUS" + " in kgdb_stub and make new kernel.\n" + " cpu_callout_map is %lx\n", cpu_callout_map); + goto exit_just_unlock; + } +#endif + if (spinlock_count == 1) { + long time = 0, end_time; + int i; + int cpu_logged_in[MAX_NO_CPUS] = {[0 ... MAX_NO_CPUS - 1] = (0) + }; + if (remote_debug) { + printk("kgdb : cpu %d entry, syncing others\n", + smp_processor_id()); + } + for (i = 0; i < MAX_NO_CPUS; i++) { + /* + * Use trylock as we may already hold the lock if + * we are holding the cpu. Net result is all + * locked. + */ + spin_trylock(&waitlocks[i]); + } + for (i = 0; i < MAX_NO_CPUS; i++) + cpu_logged_in[i] = 0; + /* + * Wait for their arrival. We know the watch dog is active if + * in_kgdb() has ever been called, as it is always called on a + * watchdog tick. + */ + time = ia64_get_itc(); + end_time = time + 2; /* Note: we use the High order bits! */ + i = 1; + if (num_online_cpus() > 1) { + int me_in_kgdb = in_kgdb_entry_log[smp_processor_id()]; + smp_send_nmi_allbutself(); + + while (i < num_online_cpus() && time != end_time) { + int j; + for (j = 0; j < MAX_NO_CPUS; j++) { + if (waiting_cpus[j].task && + waiting_cpus[j].task != NOCPU && + !cpu_logged_in[j]) { + i++; + cpu_logged_in[j] = 1; + if (remote_debug) { + printk + ("kgdb : cpu %d arrived at kgdb\n", + j); + } + break; + } else if (!waiting_cpus[j].task && + !cpu_online(j)) { + waiting_cpus[j].task = NOCPU; + cpu_logged_in[j] = 1; + waiting_cpus[j].hold = 1; + break; + } + if (!waiting_cpus[j].task && in_kgdb_here_log[j]) { + int wait = 100000; + while (wait-- && !waiting_cpus[j].task ); + if (!waiting_cpus[j].task && + in_kgdb_here_log[j]) { + printk + ("kgdb : cpu %d stall" + " in in_kgdb\n", + j); + i++; + cpu_logged_in[j] = 1; + waiting_cpus[j].task = + (struct task_struct *) 1; + } + } + } + + if (in_kgdb_entry_log[smp_processor_id()] > + (me_in_kgdb + 10)) { + break; + } + + time = ia64_get_itc(); + } + if (i < num_online_cpus()) { + printk + ("kgdb : time out, proceeding without sync\n"); +#if 0 + printk("kgdb : Waiting_cpus: 0 = %d, 1 = %d\n", + waiting_cpus[0].task != 0, + waiting_cpus[1].task != 0); + printk("kgdb : Cpu_logged in: 0 = %d, 1 = %d\n", + cpu_logged_in[0], cpu_logged_in[1]); + printk + ("kgdb : in_kgdb_here_log in: 0 = %d, 1 = %d\n", + in_kgdb_here_log[0] != 0, + in_kgdb_here_log[1] != 0); +#endif + entry_state = NO_SYNC; + } else { +#if 0 + int ent = + in_kgdb_entry_log[smp_processor_id()] - + me_in_kgdb; + printk("kgdb : sync after %d entries\n", ent); +#endif + } + } else { + if (remote_debug) { + printk + ("kgdb : %ld cpus, but watchdog not active\n" + "proceeding without locking down other cpus\n", + num_online_cpus()); + entry_state = NO_NMI; + } + } + } +#endif + + if (remote_debug) { + unsigned long *lp = (unsigned long *) &linux_regs; + + printk("handle_exception(exceptionVector=%d, " + "signo=%d, err_code=%ld, linux_regs=%p)\n", + exceptionVector, signo, err_code, linux_regs); + if (debug_regs) { + print_regs(&ptregs); + printk("Stk: %8lx %8lx %8lx %8lx" + " %8lx %8lx %8lx %8lx\n", + lp[0], lp[1], lp[2], lp[3], + lp[4], lp[5], lp[6], lp[7]); + printk(" %8lx %8lx %8lx %8lx" + " %8lx %8lx %8lx %8lx\n", + lp[8], lp[9], lp[10], lp[11], + lp[12], lp[13], lp[14], lp[15]); + printk(" %8lx %8lx %8lx %8lx " + "%8lx %8lx %8lx %8lx\n", + lp[16], lp[17], lp[18], lp[19], + lp[20], lp[21], lp[22], lp[23]); + printk(" %8lx %8lx %8lx %8lx " + "%8lx %8lx %8lx %8lx\n", + lp[24], lp[25], lp[26], lp[27], + lp[28], lp[29], lp[30], lp[31]); + } + } + + /* Disable hardware debugging while we are in kgdb */ + /* Get the debug register status register */ + hw_breakpoint_status = ia64_getreg(_IA64_REG_PSR); + if (hw_breakpoint_status & IA64_PSR_DB) + ia64_setreg(_IA64_REG_PSR_L, hw_breakpoint_status ^ IA64_PSR_DB); + + + switch (exceptionVector) { + case -1: /* general death */ + case 11: /* break fix signo */ + switch (err_code) { /* break_num */ + case BREAKNUM: + signo = SIGTRAP; + break; + case KGDBBREAKNUM: + signo = SIGTRAP; + if (ia64_psr(linux_regs)->ri < 2) + kgdb_pc(linux_regs, linux_regs->cr_iip + + ia64_psr(linux_regs)->ri + 1); + else + kgdb_pc(linux_regs, linux_regs->cr_iip + 16); + break; + } + break; + case 29: /* hardware breakpoint ibr/dbr fault */ + case 36: /* single step trap */ + signo = SIGTRAP; + break; + case 6: /* ikey_miss */ + case 7: /* dkey_miss */ + case 24: /* general exception */ + case 31: /* unsupported data reference */ + signo = SIGSEGV; + break; + case 13: /* reserved */ + case 14: /* " "" */ + case 15: /* " "" */ + case 16: /* " "" */ + case 17: /* " "" */ + case 18: /* " "" */ + case 19: /* " "" */ + case 28: /* " "" */ + case 25: /* disable fp */ + case 32: /* floating point */ + case 33: /* floating point trap */ + case 34: /* lower privilege fault */ + case 35: /* taken branch trap */ + case 37: /* reserved */ + case 38: /* reserved */ + case 39: /* reserved */ + case 40: /* reserved */ + case 41: /* reserved */ + case 42: /* reserved */ + case 43: /* reserved */ + case 44: /* reserved */ + case 45: /* ia32_exception */ + case 47: /* ia32 interrupt */ + default: /* reserved and undefined */ + signo = SIGILL; + break; + case 5: /* kernel page fault */ + if (mem_err_expected) { + /* + * This fault occured because of the + * get_char or set_char routines. These + * two routines use either eax of edx to + * indirectly reference the location in + * memory that they are working with. + * For a page fault, when we return the + * instruction will be retried, so we + * have to make sure that these + * registers point to valid memory. + */ + mem_err = 1; /* set mem error flag */ + mem_err_expected = 0; + mem_err_cnt++; /* helps in debugging */ + if (ia64_psr(linux_regs)->ri < 2) + kgdb_pc(linux_regs, linux_regs->cr_iip + + ia64_psr(linux_regs)->ri + 1); + else + kgdb_pc(linux_regs, linux_regs->cr_iip + 16); + if (remote_debug) + printk("Return after memory error: " + "mem_err_cnt=%d\n", mem_err_cnt); + if (debug_regs) + print_regs(&ptregs); + goto exit_kgdb; + } + break; + } + if (remote_debug) + printk("kgdb : entered kgdb on cpu %d\n", smp_processor_id()); + + gdb_ia64vector = exceptionVector; + gdb_ia64errcode = err_code; +#ifdef CONFIG_SMP + /* + * OK, we can now communicate, lets tell gdb about the sync. + * but only if we had a problem. + */ + switch (entry_state) { + case NO_NMI: + to_gdb("NMI not active, other cpus not stopped\n"); + break; + case NO_SYNC: + to_gdb("Some cpus not stopped, see 'kgdb_info' for details\n"); + default:; + } + +#endif +/* + * Set up the gdb function call area. + */ + + IF_SMP(once_again:) + /* reply to host that an exception has occurred */ + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[signo >> 4]; + remcomOutBuffer[2] = hexchars[signo % 16]; + remcomOutBuffer[3] = 0; + + putpacket(remcomOutBuffer); + + while (1 == 1) { + error = 0; + remcomOutBuffer[0] = 0; + getpacket(remcomInBuffer); + switch (remcomInBuffer[0]) { + case '?': + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[signo >> 4]; + remcomOutBuffer[2] = hexchars[signo % 16]; + remcomOutBuffer[3] = 0; + break; + case 'd': + remote_debug = !(remote_debug); /* toggle debug flag */ + printk("Remote debug %s\n", + remote_debug ? "on" : "off"); + break; + case 'p': /* fetch register */ + { + int regnum; + + if (!have_regs) { + get_gdb_regs(usethread, info); + have_regs = 1; + } + + hex2mem(&remcomInBuffer[1], (char *) ®num, sizeof(regnum), 0); + if (regnum >= NUMREGS) { + remcomOutBuffer[0] = 'E'; + remcomOutBuffer[1] = 0; + break; + } + mem2hex((char *) &gdb_regs[REGISTER_INDEX(regnum)], remcomOutBuffer, + REGISTER_SIZE(regnum), 0); + remcomOutBuffer[REGISTER_SIZE(regnum) * 2] = 0; + break; + } + case 'g': /* return the value of the CPU registers */ + get_gdb_regs(usethread, info); + mem2hex((char *) gdb_regs, remcomOutBuffer, NUMREGBYTES, 0); + break; + case 'G': /* set the value of the CPU registers - return OK */ + hex2mem(&remcomInBuffer[1], (char *) gdb_regs, NUMREGBYTES, 0); + if (!usethread || usethread == current) { + gdb_regs_to_regs(info); + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "E00"); + } + break; + + case 'P':{ /* set the value of a single CPU register - + return OK */ + /* + * For some reason, gdb wants to talk about psudo + * registers (greater than 15). These may have + * meaning for ptrace, but for us it is safe to + * ignor them. We do this by dumping them into + * _GS which we also ignor, but do have memory for. + */ + int regno; + + ptr = &remcomInBuffer[1]; + regs_to_gdb_regs(info); + if ((!usethread || usethread == current) && + hexToInt(&ptr, ®no) && + *ptr++ == '=' && (regno >= 0)) { + regno = + (regno >= NUMREGS ? 0 : regno); + hex2mem(ptr, (char *) &gdb_regs[REGISTER_INDEX(regno)], + 4, 0); + gdb_regs_to_regs(info); + strcpy(remcomOutBuffer, "OK"); + break; + } + strcpy(remcomOutBuffer, "E01"); + break; + } + + /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + case 'm': + /* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */ + ptr = &remcomInBuffer[1]; + if (hexToLong(&ptr, &addr) && + (*(ptr++) == ',') && (hexToInt(&ptr, &length))) { + ptr = 0; + /* + * hex doubles the byte count + */ + if (length > (BUFMAX / 2)) + length = BUFMAX / 2; + mem2hex((char *) addr, + remcomOutBuffer, length, 1); + if (mem_err) { + strcpy(remcomOutBuffer, "E03"); + debug_error("memory fault\n", NULL); + } + } + + if (ptr) { + strcpy(remcomOutBuffer, "E01"); + debug_error + ("malformed read memory command: %s\n", + remcomInBuffer); + } + break; + + /* MAA..AA,LLLL: + Write LLLL bytes at address AA.AA return OK */ + case 'M': + /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */ + ptr = &remcomInBuffer[1]; + if (hexToLong(&ptr, &addr) && + (*(ptr++) == ',') && + (hexToInt(&ptr, &length)) && (*(ptr++) == ':')) { + extern unsigned long _start[]; + + if (addr == (unsigned long) _start) + strcpy(remcomOutBuffer, "OK"); + + else { + hex2mem(ptr, (char *) addr, length, 1); + + if (mem_err) { + strcpy(remcomOutBuffer, "E03"); + debug_error("memory fault\n", NULL); + } else { + if (kernel_text_address(addr)) + flush_icache_range(addr, addr + length); + strcpy(remcomOutBuffer, "OK"); + } + } + + ptr = 0; + } + if (ptr) { + strcpy(remcomOutBuffer, "E02"); + debug_error + ("malformed write memory command: %s\n", + remcomInBuffer); + } + break; + case 'S': + remcomInBuffer[0] = 's'; + case 'C': + /* Csig;AA..AA where ;AA..AA is optional + * continue with signal + * Since signals are meaning less to us, delete that + * part and then fall into the 'c' code. + */ + ptr = &remcomInBuffer[1]; + length = 2; + while (*ptr && *ptr != ';') { + length++; + ptr++; + } + if (*ptr) { + do { + ptr++; + *(ptr - length++) = *ptr; + } while (*ptr); + } else { + remcomInBuffer[1] = 0; + } + + /* cAA..AA Continue at address AA..AA(optional) */ + /* sAA..AA Step one instruction from AA..AA(optional) */ + /* D detach, reply OK and then continue */ + case 'c': + case 's': + case 'D': + + /* try to read optional parameter, + pc unchanged if no parm */ + ptr = &remcomInBuffer[1]; + if (hexToLong(&ptr, &addr)) { + if (remote_debug) + printk("Changing EIP to 0x%lx\n", addr); + + ptregs.cr_iip = addr; + } + + newPC = ptregs.cr_iip; + + /* clear the trace bit */ + ptregs.cr_ipsr &= ~IA64_PSR_SS; + + /* set the trace bit if we're stepping */ + if (remcomInBuffer[0] == 's') + ptregs.cr_ipsr |= IA64_PSR_SS; + + /* detach is a friendly version of continue. Note that + debugging is still enabled (e.g hit control C) + */ + if (remcomInBuffer[0] == 'D') { + strcpy(remcomOutBuffer, "OK"); + putpacket(remcomOutBuffer); + } + + if (remote_debug) { + printk("Resuming execution\n"); + print_regs(&ptregs); + } + + if (kgdboe) + netpoll_set_trap(0); + + correct_hw_break(); + ptregs.cr_ipsr |= IA64_PSR_DB; + goto exit_kgdb; + + /* kill the program */ + case 'k': /* do nothing */ + break; + + /* query */ + case 'q': + nothreads = 0; + switch (remcomInBuffer[1]) { + case 'f': + threadid = 1; + thread_list = 2; + thread_list_start = (usethread ? : current); + case 's': + if (!cmp_str(&remcomInBuffer[2], + "ThreadInfo", 10)) + break; + + remcomOutBuffer[nothreads++] = 'm'; + for (; threadid < PID_MAX + MAX_NO_CPUS; + threadid++) { + thread = getthread(threadid); + if (thread) { + nothreads += int_to_hex_v( + &remcomOutBuffer[ + nothreads], + threadid); + if (thread_min > threadid) + thread_min = threadid; + remcomOutBuffer[ + nothreads] = ','; + nothreads++; + if (nothreads > BUFMAX - 10) + break; + } + } + if (remcomOutBuffer[nothreads - 1] == 'm') { + remcomOutBuffer[nothreads - 1] = 'l'; + } else { + nothreads--; + } + remcomOutBuffer[nothreads] = 0; + break; + +#ifdef old_thread_list /* Old thread info request */ + case 'L': + /* List threads */ + thread_list = 2; + thread_list_start = (usethread ? : current); + unpack_byte(remcomInBuffer + 3, &maxthreads); + unpack_threadid(remcomInBuffer + 5, &thref); + do { + int buf_thread_limit = + (BUFMAX - 22) / BUF_THREAD_ID_SIZE; + if (maxthreads > buf_thread_limit) { + maxthreads = buf_thread_limit; + } + } while (0); + remcomOutBuffer[0] = 'q'; + remcomOutBuffer[1] = 'M'; + remcomOutBuffer[4] = '0'; + pack_threadid(remcomOutBuffer + 5, &thref); + + threadid = threadref_to_int(&thref); + for (nothreads = 0; + nothreads < maxthreads && + threadid < PID_MAX + MAX_NO_CPUS; + threadid++) { + thread = getthread(threadid); + if (thread) { + int_to_threadref(&thref, + threadid); + pack_threadid(remcomOutBuffer + + 21 + + nothreads * 16, + &thref); + nothreads++; + if (thread_min > threadid) + thread_min = threadid; + } + } + + if (threadid == PID_MAX + MAX_NO_CPUS) { + remcomOutBuffer[4] = '1'; + } + pack_hex_byte(remcomOutBuffer + 2, nothreads); + remcomOutBuffer[21 + nothreads * 16] = '\0'; + break; +#endif + case 'C': + /* Current thread id */ + remcomOutBuffer[0] = 'Q'; + remcomOutBuffer[1] = 'C'; + threadid = current->pid; + if (!threadid) { + /* + * idle thread + */ + for (threadid = PID_MAX; + threadid < PID_MAX + MAX_NO_CPUS; + threadid++) { + if (current == + idle_task(threadid - + PID_MAX)) + break; + } + } + int_to_threadref(&thref, threadid); + pack_threadid(remcomOutBuffer + 2, &thref); + remcomOutBuffer[18] = '\0'; + break; + + case 'E': + /* Print exception info */ + printexceptioninfo(exceptionVector, + err_code, remcomOutBuffer); + break; + case 'T':{ + char * nptr; + /* Thread extra info */ + if (!cmp_str(&remcomInBuffer[2], + "hreadExtraInfo,", 15)) { + break; + } + ptr = &remcomInBuffer[17]; + hexToInt(&ptr, &threadid); + thread = getthread(threadid); + nptr = &thread->comm[0]; + length = 0; + ptr = &remcomOutBuffer[0]; + do { + length++; + ptr = pack_hex_byte(ptr, *nptr++); + } while (*nptr && length < 16); + /* + * would like that 16 to be the size of + * task_struct.comm but don't know the + * syntax.. + */ + *ptr = 0; + } + } + break; + + /* task related */ + case 'H': + switch (remcomInBuffer[1]) { + case 'g': + ptr = &remcomInBuffer[2]; + hexToInt(&ptr, &threadid); + thread = getthread(threadid); + if (!thread) { + remcomOutBuffer[0] = 'E'; + remcomOutBuffer[1] = '\0'; + break; + } + /* + * Just in case I forget what this is all about, + * the "thread info" command to gdb causes it + * to ask for a thread list. It then switches + * to each thread and asks for the registers. + * For this (and only this) usage, we want to + * fudge the registers of tasks not on the run + * list (i.e. waiting) to show the routine that + * called schedule. Also, gdb, is a minimalist + * in that if the current thread is the last + * it will not re-read the info when done. + * This means that in this case we must show + * the real registers. So here is how we do it: + * Each entry we keep track of the min + * thread in the list (the last that gdb will) + * get info for. We also keep track of the + * starting thread. + * "thread_list" is cleared when switching back + * to the min thread if it is was current, or + * if it was not current, thread_list is set + * to 1. When the switch to current comes, + * if thread_list is 1, clear it, else do + * nothing. + */ + usethread = thread; + have_regs = 0; + if ((thread_list == 1) && + (thread == thread_list_start)) { + thread_list = 0; + } + if (thread_list && (threadid == thread_min)) { + if (thread == thread_list_start) { + thread_list = 0; + } else { + thread_list = 1; + } + } + /* follow through */ + case 'c': + remcomOutBuffer[0] = 'O'; + remcomOutBuffer[1] = 'K'; + remcomOutBuffer[2] = '\0'; + break; + } + break; + + /* Query thread status */ + case 'T': + ptr = &remcomInBuffer[1]; + hexToInt(&ptr, &threadid); + thread = getthread(threadid); + if (thread) { + remcomOutBuffer[0] = 'O'; + remcomOutBuffer[1] = 'K'; + remcomOutBuffer[2] = '\0'; + if (thread_min > threadid) + thread_min = threadid; + } else { + remcomOutBuffer[0] = 'E'; + remcomOutBuffer[1] = '\0'; + } + break; + +#ifdef oldbreak_protocol + case 'Y': /* set up a hardware breakpoint */ + ptr = &remcomInBuffer[1]; + hexToInt(&ptr, &breakno); + ptr++; + hexToInt(&ptr, &breaktype); + ptr++; + hexToInt(&ptr, &length); + ptr++; + hexToLong(&ptr, &addr); + if (set_hw_break(breakno & 0x3, + breaktype & 0x3, + length & 0x3, addr) == 0) { + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "ERROR"); + } + break; + + /* Remove hardware breakpoint */ + case 'y': + ptr = &remcomInBuffer[1]; + hexToInt(&ptr, &breakno); + if (remove_hw_break(breakno & 0x3) == 0) { + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "ERROR"); + } + break; +#endif + /* + * Set/Remove watchpoint + * + */ + case 'Z': + case 'z': + if (!kgdb_dbregs_enabled) + break; + + switch (remcomInBuffer[1]) { + case '0': /* insert hwd break */ + case '1': /* hardware breakpoint */ + case '2': /* write watchpoint */ + case '3': /* read watchpoint */ + case '4': /* access watchpoint */ + { + int ret; + if (remcomInBuffer[2] != ',') { + strcpy(remcomOutBuffer, "ERROR"); + break; + } + ptr = &remcomInBuffer[3]; + if (hexToLong(&ptr, &addr)) { + ptr++; + if (!hexToInt(&ptr, &length)) { + strcpy(remcomOutBuffer, "ERROR"); + break; + } + } + else { + strcpy(remcomOutBuffer, "ERROR"); + break; + } + + if (remcomInBuffer[1] == '0') { + if (remcomInBuffer[0] == 'z') + ret = kgdb_arch_remove_breakpoint(addr); + else + ret = kgdb_arch_set_breakpoint(addr); + } + else ret = hardware_breakpoint(addr, length, + remcomInBuffer[1] - '1', + remcomInBuffer[0] == 'Z'); + if (ret) + strcpy(remcomOutBuffer, "OK"); + else + strcpy(remcomOutBuffer, "ERROR"); + break; + } + default: + strcpy(remcomOutBuffer, "ERROR"); + break; + } + break; + case 'R': /* reboot */ + strcpy(remcomOutBuffer, "OK"); + putpacket(remcomOutBuffer); + /*to_gdb("Rebooting\n"); */ + machine_restart(NULL); + + } /* switch */ + + /* reply to the request */ + putpacket(remcomOutBuffer); + } /* while(1==1) */ + /* + * reached by goto only. + */ + exit_kgdb: +#ifdef CONFIG_SMP + /* + * Release gdb wait locks + * Sanity check time. Must have at least one cpu to run. Also single + * step must not be done if the current cpu is on hold. + */ + if (spinlock_count == 1) { + int ss_hold = (ptregs.cr_ipsr & IA64_PSR_SS) && kgdb_info.hold_on_sstep; + int cpu_avail = 0; + int i; + + for (i = 0; i < MAX_NO_CPUS; i++) { + if (!cpu_online(i)) + break; + if (!hold_cpu(i)) { + cpu_avail = 1; + } + } + /* + * Early in the bring up there will be NO cpus on line... + */ + if (!cpu_avail && !cpus_empty(cpu_online_map)) { + to_gdb("No cpus unblocked, see 'kgdb_info.hold_cpu'\n"); + goto once_again; + } + if (hold_cpu(smp_processor_id()) && (ptregs.cr_ipsr & IA64_PSR_SS)) { + to_gdb + ("Current cpu must be unblocked to single step\n"); + goto once_again; + } + + if (!ss_hold) { + int i; + for (i = 0; i < MAX_NO_CPUS; i++) { + if (!hold_cpu(i)) { + spin_unlock(&waitlocks[i]); + } + } + } else { + spin_unlock(&waitlocks[smp_processor_id()]); + } + /* Release kgdb spinlock */ + KGDB_SPIN_UNLOCK(&kgdb_spinlock); + /* + * If this cpu is on hold, this is where we + * do it. Note, the NMI will pull us out of here, + * but will return as the above lock is not held. + * We will stay here till another cpu releases the lock for us. + */ + spin_unlock_wait(waitlocks + smp_processor_id()); + kgdb_local_irq_restore(flags); + info->ret = 0; + return; + } +#if 0 +exit_just_unlock: +#endif +#endif + /* Release kgdb spinlock */ + KGDB_SPIN_UNLOCK(&kgdb_spinlock); + kgdb_local_irq_restore(flags); + info->ret = 0; + return; +} + +/* this function is used to set up exception handlers for tracing and + * breakpoints. + * This function is not needed as the above line does all that is needed. + * We leave it for backward compatitability... + */ +void +set_debug_traps(void) +{ + /* + * linux_debug_hook is defined in traps.c. We store a pointer + * to our own exception handler into it. + + * But really folks, every hear of labeled common, an old Fortran + * concept. Lots of folks can reference it and it is define if + * anyone does. Only one can initialize it at link time. We do + * this with the hook. See the statement above. No need for any + * executable code and it is ready as soon as the kernel is + * loaded. Very desirable in kernel debugging. + + linux_debug_hook = handle_exception ; + */ + + /* In case GDB is started before us, ack any packets (presumably + "$?#xx") sitting there. + putDebugChar ('+'); + + initialized = 1; + */ +} + +/* This function will generate a breakpoint exception. It is used at the + beginning of a program to sync up with a debugger and can be used + otherwise as a quick means to stop program execution and "break" into + the debugger. */ +/* But really, just use the BREAKPOINT macro. We will handle the int stuff + */ + +#ifdef later +/* + * possibly we should not go thru the traps.c code at all? Someday. + */ +void +do_kgdb_int3(struct pt_regs *regs, long error_code) +{ + kgdb_handle_exception(3, 5, error_code, regs); + return; +} +#endif +#undef regs +#ifdef CONFIG_TRAP_BAD_SYSCALL_EXITS +asmlinkage void +bad_sys_call_exit(int stuff) +{ + struct pt_regs *regs = (struct pt_regs *) &stuff; + printk("Sys call %d return with %x preempt_count\n", + (int) regs->orig_eax, preempt_count()); +} +#endif +#ifdef CONFIG_STACK_OVERFLOW_TEST +#include +asmlinkage void +stack_overflow(void) +{ +#ifdef BREAKPOINT + BREAKPOINT; +#else + printk("Kernel stack overflow, looping forever\n"); +#endif + while (1) { + } +} +#endif + +#if defined(CONFIG_SMP) || defined(CONFIG_KGDB_CONSOLE) +char gdbconbuf[BUFMAX]; + +static void +kgdb_gdb_message(const char *s, unsigned count) +{ + int i; + int wcount; + char *bufptr; + /* + * This takes care of NMI while spining out chars to gdb + */ + IF_SMP(in_kgdb_console = 1); + gdbconbuf[0] = 'O'; + bufptr = gdbconbuf + 1; + while (count > 0) { + if ((count << 1) > (BUFMAX - 2)) { + wcount = (BUFMAX - 2) >> 1; + } else { + wcount = count; + } + count -= wcount; + for (i = 0; i < wcount; i++) { + bufptr = pack_hex_byte(bufptr, s[i]); + } + *bufptr = '\0'; + s += wcount; + + putpacket(gdbconbuf); + + } + IF_SMP(in_kgdb_console = 0); +} +#endif +#ifdef CONFIG_SMP +static void +to_gdb(const char *s) +{ + int count = 0; + while (s[count] && (count++ < BUFMAX)) ; + kgdb_gdb_message(s, count); +} +#endif +#ifdef CONFIG_KGDB_CONSOLE +#include +#include +#include +#include +#include + +void +kgdb_console_write(struct console *co, const char *s, unsigned count) +{ + + if (gdb_ia64vector == -1) { + /* + * We have not yet talked to gdb. What to do... + * lets break, on continue we can do the write. + * But first tell him whats up. Uh, well no can do, + * as this IS the console. Oh well... + * We do need to wait or the messages will be lost. + * Other option would be to tell the above code to + * ignore this breakpoint and do an auto return, + * but that might confuse gdb. Also this happens + * early enough in boot up that we don't have the traps + * set up yet, so... + */ + breakpoint(); + } + kgdb_gdb_message(s, count); +} + +/* + * ------------------------------------------------------------ + * Serial KGDB driver + * ------------------------------------------------------------ + */ + +static struct console kgdbcons = { + name:"kgdb", + write:kgdb_console_write, +#ifdef CONFIG_KGDB_USER_CONSOLE + device:kgdb_console_device, +#endif + flags:CON_PRINTBUFFER | CON_ENABLED, + index:-1, +}; + +/* + * The trick here is that this file gets linked before printk.o + * That means we get to peer at the console info in the command + * line before it does. If we are up, we register, otherwise, + * do nothing. By returning 0, we allow printk to look also. + */ +static int kgdb_console_enabled; + +int __init +kgdb_console_init(char *str) +{ + if ((strncmp(str, "kgdb", 4) == 0) || (strncmp(str, "gdb", 3) == 0)) { + register_console(&kgdbcons); + kgdb_console_enabled = 1; + } + return 0; /* let others look at the string */ +} + +__setup("console=", kgdb_console_init); + +int __init +kgdb_enable_dbregs(char *str) +{ + kgdb_dbregs_enabled = 1; + return 1; +} + +__setup("kgdb_debug_regs=", kgdb_enable_dbregs); + +#ifdef CONFIG_KGDB_USER_CONSOLE +static kdev_t kgdb_console_device(struct console *c); +/* This stuff sort of works, but it knocks out telnet devices + * we are leaving it here in case we (or you) find time to figure it out + * better.. + */ + +/* + * We need a real char device as well for when the console is opened for user + * space activities. + */ + +static int +kgdb_consdev_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t +kgdb_consdev_write(struct file *file, const char *buf, + size_t count, loff_t * ppos) +{ + int size, ret = 0; + static char kbuf[128]; + static DECLARE_MUTEX(sem); + + /* We are not reentrant... */ + if (down_interruptible(&sem)) + return -ERESTARTSYS; + + while (count > 0) { + /* need to copy the data from user space */ + size = count; + if (size > sizeof (kbuf)) + size = sizeof (kbuf); + if (copy_from_user(kbuf, buf, size)) { + ret = -EFAULT; + break;; + } + kgdb_console_write(&kgdbcons, kbuf, size); + count -= size; + ret += size; + buf += size; + } + + up(&sem); + + return ret; +} + +struct file_operations kgdb_consdev_fops = { + open:kgdb_consdev_open, + write:kgdb_consdev_write +}; +static kdev_t +kgdb_console_device(struct console *c) +{ + return MKDEV(TTYAUX_MAJOR, 1); +} + +/* + * This routine gets called from the serial stub in the i386/lib + * This is so it is done late in bring up (just before the console open). + */ +void +kgdb_console_finit(void) +{ + if (kgdb_console_enabled) { + char *cptr = cdevname(MKDEV(TTYAUX_MAJOR, 1)); + char *cp = cptr; + while (*cptr && *cptr != '(') + cptr++; + *cptr = 0; + unregister_chrdev(TTYAUX_MAJOR, cp); + register_chrdev(TTYAUX_MAJOR, "kgdb", &kgdb_consdev_fops); + } +} +#endif +#endif +#ifdef CONFIG_KGDB_TS +#include /* time stamp code */ +#include /* in_interrupt */ +#ifdef CONFIG_KGDB_TS_64 +#define DATA_POINTS 64 +#endif +#ifdef CONFIG_KGDB_TS_128 +#define DATA_POINTS 128 +#endif +#ifdef CONFIG_KGDB_TS_256 +#define DATA_POINTS 256 +#endif +#ifdef CONFIG_KGDB_TS_512 +#define DATA_POINTS 512 +#endif +#ifdef CONFIG_KGDB_TS_1024 +#define DATA_POINTS 1024 +#endif +#ifndef DATA_POINTS +#define DATA_POINTS 128 /* must be a power of two */ +#endif +#define INDEX_MASK (DATA_POINTS - 1) +#if (INDEX_MASK & DATA_POINTS) +#error "CONFIG_KGDB_TS_COUNT must be a power of 2" +#endif +struct kgdb_and_then_struct { +#ifdef CONFIG_SMP + int on_cpu; +#endif + struct task_struct *task; + long long at_time; + int from_ln; + char *in_src; + void *from; + int *with_shpf; + int data0; + int data1; +}; +struct kgdb_and_then_struct2 { +#ifdef CONFIG_SMP + int on_cpu; +#endif + struct task_struct *task; + long long at_time; + int from_ln; + char *in_src; + void *from; + int *with_shpf; + struct task_struct *t1; + struct task_struct *t2; +}; +struct kgdb_and_then_struct kgdb_data[DATA_POINTS]; + +struct kgdb_and_then_struct *kgdb_and_then = &kgdb_data[0]; +int kgdb_and_then_count; + +void +kgdb_tstamp(int line, char *source, int data0, int data1) +{ + static spinlock_t ts_spin = SPIN_LOCK_UNLOCKED; + int flags; + kgdb_local_irq_save(flags); + spin_lock(&ts_spin); + kgdb_and_then->at_time = ia64_get_itc(); +#ifdef CONFIG_SMP + kgdb_and_then->on_cpu = smp_processor_id(); +#endif + kgdb_and_then->task = current; + kgdb_and_then->from_ln = line; + kgdb_and_then->in_src = source; + kgdb_and_then->from = __builtin_return_address(0); + kgdb_and_then->with_shpf = (int *) (((flags & IF_BIT) >> 9) | + (preempt_count() << 8)); + kgdb_and_then->data0 = data0; + kgdb_and_then->data1 = data1; + kgdb_and_then = &kgdb_data[++kgdb_and_then_count & INDEX_MASK]; + spin_unlock(&ts_spin); + kgdb_local_irq_restore(flags); +#ifdef CONFIG_PREEMPT + +#endif + return; +} +#endif +typedef int gdb_debug_hook(int exceptionVector, + int signo, int err_code, struct pt_regs *linux_regs); +gdb_debug_hook *linux_debug_hook = &kgdb_handle_exception; /* histerical reasons... */ + +static int kgdb_need_breakpoint[NR_CPUS]; + +void kgdb_schedule_breakpoint(void) +{ + kgdb_need_breakpoint[smp_processor_id()] = 1; +} + +void kgdb_process_breakpoint(void) +{ + /* + * Handle a breakpoint queued from inside network driver code + * to avoid reentrancy issues + */ + if (kgdb_need_breakpoint[smp_processor_id()]) { + kgdb_need_breakpoint[smp_processor_id()] = 0; + BREAKPOINT; + } +} + --- linux-2.6.9-rc1/arch/ia64/kernel/Makefile 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/ia64/kernel/Makefile 2004-09-03 00:56:37.245498472 -0700 @@ -17,6 +17,7 @@ obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_SMP) += smp.o smpboot.o obj-$(CONFIG_PERFMON) += perfmon_default_smpl.o obj-$(CONFIG_IA64_CYCLONE) += cyclone.o +obj-$(CONFIG_KGDB) += kgdb_stub.o # The gate DSO image is built using a special linker script. targets += gate.so gate-syms.o --- linux-2.6.9-rc1/arch/ia64/kernel/perfmon.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/ia64/kernel/perfmon.c 2004-09-03 00:56:30.914460936 -0700 @@ -2227,6 +2227,15 @@ out: static void pfm_free_fd(int fd, struct file *file) { + struct files_struct *files = current->files; + + /* + * there ie no fd_uninstall(), so we do it here + */ + spin_lock(&files->file_lock); + files->fd[fd] = NULL; + spin_unlock(&files->file_lock); + if (file) put_filp(file); put_unused_fd(fd); } @@ -2352,7 +2361,7 @@ pfm_smpl_buffer_alloc(struct task_struct insert_vm_struct(mm, vma); mm->total_vm += size >> PAGE_SHIFT; - + vm_stat_account(vma); up_write(&task->mm->mmap_sem); /* @@ -2659,8 +2668,10 @@ pfm_context_create(pfm_context_t *ctx, v ctx = pfm_context_alloc(); if (!ctx) goto error; - req->ctx_fd = ctx->ctx_fd = pfm_alloc_fd(&filp); - if (req->ctx_fd < 0) goto error_file; + ret = pfm_alloc_fd(&filp); + if (ret < 0) goto error_file; + + req->ctx_fd = ctx->ctx_fd = ret; /* * attach context to file --- linux-2.6.9-rc1/arch/ia64/kernel/process.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/ia64/kernel/process.c 2004-09-03 00:56:37.246498320 -0700 @@ -407,6 +407,9 @@ copy_thread (int nr, unsigned long clone */ child_ptregs->cr_ipsr = ((child_ptregs->cr_ipsr | IA64_PSR_BITS_TO_SET) & ~(IA64_PSR_BITS_TO_CLEAR | IA64_PSR_PP | IA64_PSR_UP)); +#ifdef CONFIG_KGDB + child_ptregs->cr_ipsr |= IA64_PSR_DB; +#endif /* * NOTE: The calling convention considers all floating point @@ -633,6 +636,9 @@ kernel_thread (int (*fn)(void *), void * regs.pt.r11 = (unsigned long) arg; /* 2nd argument */ /* Preserve PSR bits, except for bits 32-34 and 37-45, which we can't read. */ regs.pt.cr_ipsr = ia64_getreg(_IA64_REG_PSR) | IA64_PSR_BN; +#ifdef CONFIG_KGDB + regs.pt.cr_ipsr |= IA64_PSR_DB; +#endif regs.pt.cr_ifs = 1UL << 63; /* mark as valid, empty frame */ regs.sw.ar_fpsr = regs.pt.ar_fpsr = ia64_getreg(_IA64_REG_AR_FPSR); regs.sw.ar_bspstore = (unsigned long) current + IA64_RBS_OFFSET; --- linux-2.6.9-rc1/arch/ia64/kernel/sal.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/ia64/kernel/sal.c 2004-09-03 00:56:30.915460784 -0700 @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -262,3 +263,40 @@ ia64_sal_init (struct ia64_sal_systab *s p += SAL_DESC_SIZE(*p); } } + +int +ia64_sal_oemcall(struct ia64_sal_retval *isrvp, u64 oemfunc, u64 arg1, + u64 arg2, u64 arg3, u64 arg4, u64 arg5, u64 arg6, u64 arg7) +{ + if (oemfunc < IA64_SAL_OEMFUNC_MIN || oemfunc > IA64_SAL_OEMFUNC_MAX) + return -1; + SAL_CALL(*isrvp, oemfunc, arg1, arg2, arg3, arg4, arg5, arg6, arg7); + return 0; +} +EXPORT_SYMBOL(ia64_sal_oemcall); + +int +ia64_sal_oemcall_nolock(struct ia64_sal_retval *isrvp, u64 oemfunc, u64 arg1, + u64 arg2, u64 arg3, u64 arg4, u64 arg5, u64 arg6, + u64 arg7) +{ + if (oemfunc < IA64_SAL_OEMFUNC_MIN || oemfunc > IA64_SAL_OEMFUNC_MAX) + return -1; + SAL_CALL_NOLOCK(*isrvp, oemfunc, arg1, arg2, arg3, arg4, arg5, arg6, + arg7); + return 0; +} +EXPORT_SYMBOL(ia64_sal_oemcall_nolock); + +int +ia64_sal_oemcall_reentrant(struct ia64_sal_retval *isrvp, u64 oemfunc, + u64 arg1, u64 arg2, u64 arg3, u64 arg4, u64 arg5, + u64 arg6, u64 arg7) +{ + if (oemfunc < IA64_SAL_OEMFUNC_MIN || oemfunc > IA64_SAL_OEMFUNC_MAX) + return -1; + SAL_CALL_REENTRANT(*isrvp, oemfunc, arg1, arg2, arg3, arg4, arg5, arg6, + arg7); + return 0; +} +EXPORT_SYMBOL(ia64_sal_oemcall_reentrant); --- linux-2.6.9-rc1/arch/ia64/kernel/setup.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/ia64/kernel/setup.c 2004-09-03 00:56:37.247498168 -0700 @@ -51,6 +51,7 @@ #include #include #include +#include #if defined(CONFIG_SMP) && (IA64_CPU_SIZE > PAGE_SIZE) # error "struct cpuinfo_ia64 too big!" @@ -375,6 +376,28 @@ setup_arch (char **cmdline_p) } #endif +#ifndef CONFIG_IA64_HP_SIM +#ifdef CONFIG_KGDB + { + unsigned long total_ibr, total_dbr; + long status; + int dbr; + + status = ia64_pal_debug_info(&total_ibr, &total_dbr); + + if (!status) { + printk(KERN_INFO "kgdb has DBR = %d IBR = %d\n", + (int) total_dbr, (int) total_ibr); + + for (dbr = 0; dbr < total_dbr; dbr++) + ia64_set_dbr((dbr << 1) + 1, 0); + for (dbr = 0; dbr < total_ibr; dbr++) + ia64_set_ibr((dbr << 1) + 1, 0); + } + } +#endif +#endif + /* enable IA-64 Machine Check Abort Handling unless disabled */ if (!strstr(saved_command_line, "nomca")) ia64_mca_init(); --- linux-2.6.9-rc1/arch/ia64/kernel/signal.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/ia64/kernel/signal.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,7 +1,7 @@ /* * Architecture-specific signal handling support. * - * Copyright (C) 1999-2003 Hewlett-Packard Co + * Copyright (C) 1999-2004 Hewlett-Packard Co * David Mosberger-Tang * * Derived from i386 and Alpha versions. @@ -352,13 +352,42 @@ rbs_on_sig_stack (unsigned long bsp) } static long +force_sigsegv_info (int sig, void *addr) +{ + unsigned long flags; + struct siginfo si; + + if (sig == SIGSEGV) { + /* + * Acquiring siglock around the sa_handler-update is almost + * certainly overkill, but this isn't a + * performance-critical path and I'd rather play it safe + * here than having to debug a nasty race if and when + * something changes in kernel/signal.c that would make it + * no longer safe to modify sa_handler without holding the + * lock. + */ + spin_lock_irqsave(¤t->sighand->siglock, flags); + current->sighand->action[sig - 1].sa.sa_handler = SIG_DFL; + spin_unlock_irqrestore(¤t->sighand->siglock, flags); + } + si.si_signo = SIGSEGV; + si.si_errno = 0; + si.si_code = SI_KERNEL; + si.si_pid = current->pid; + si.si_uid = current->uid; + si.si_addr = addr; + force_sig_info(SIGSEGV, &si, current); + return 0; +} + +static long setup_frame (int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct sigscratch *scr) { extern char __kernel_sigtramp[]; unsigned long tramp_addr, new_rbs = 0; struct sigframe *frame; - struct siginfo si; long err; frame = (void *) scr->pt.r12; @@ -377,7 +406,7 @@ setup_frame (int sig, struct k_sigaction frame = (void *) frame - ((sizeof(*frame) + STACK_ALIGN - 1) & ~(STACK_ALIGN - 1)); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) - goto give_sigsegv; + return force_sigsegv_info(sig, frame); err = __put_user(sig, &frame->arg0); err |= __put_user(&frame->info, &frame->arg1); @@ -393,8 +422,8 @@ setup_frame (int sig, struct k_sigaction err |= __put_user(sas_ss_flags(scr->pt.r12), &frame->sc.sc_stack.ss_flags); err |= setup_sigcontext(&frame->sc, set, scr); - if (err) - goto give_sigsegv; + if (unlikely(err)) + return force_sigsegv_info(sig, frame); scr->pt.r12 = (unsigned long) frame - 16; /* new stack pointer */ scr->pt.ar_fpsr = FPSR_DEFAULT; /* reset fpsr for signal handler */ @@ -422,18 +451,6 @@ setup_frame (int sig, struct k_sigaction current->comm, current->pid, sig, scr->pt.r12, frame->sc.sc_ip, frame->handler); #endif return 1; - - give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - si.si_signo = SIGSEGV; - si.si_errno = 0; - si.si_code = SI_KERNEL; - si.si_pid = current->pid; - si.si_uid = current->uid; - si.si_addr = frame; - force_sig_info(SIGSEGV, &si, current); - return 0; } static long @@ -449,9 +466,6 @@ handle_signal (unsigned long sig, struct if (!setup_frame(sig, ka, info, oldset, scr)) return 0; - if (ka->sa.sa_flags & SA_ONESHOT) - ka->sa.sa_handler = SIG_DFL; - if (!(ka->sa.sa_flags & SA_NODEFER)) { spin_lock_irq(¤t->sighand->siglock); { @@ -471,7 +485,7 @@ handle_signal (unsigned long sig, struct long ia64_do_signal (sigset_t *oldset, struct sigscratch *scr, long in_syscall) { - struct k_sigaction *ka; + struct k_sigaction ka; siginfo_t info; long restart = in_syscall; long errno = scr->pt.r8; @@ -493,7 +507,7 @@ ia64_do_signal (sigset_t *oldset, struct * need to push through a forced SIGSEGV. */ while (1) { - int signr = get_signal_to_deliver(&info, &scr->pt, NULL); + int signr = get_signal_to_deliver(&info, &ka, &scr->pt, NULL); /* * get_signal_to_deliver() may have run a debugger (via notify_parent()) @@ -520,8 +534,6 @@ ia64_do_signal (sigset_t *oldset, struct if (signr <= 0) break; - ka = ¤t->sighand->action[signr - 1]; - if (unlikely(restart)) { switch (errno) { case ERESTART_RESTARTBLOCK: @@ -531,7 +543,7 @@ ia64_do_signal (sigset_t *oldset, struct break; case ERESTARTSYS: - if ((ka->sa.sa_flags & SA_RESTART) == 0) { + if ((ka.sa.sa_flags & SA_RESTART) == 0) { scr->pt.r8 = ERR_CODE(EINTR); /* note: scr->pt.r10 is already -1 */ break; @@ -550,7 +562,7 @@ ia64_do_signal (sigset_t *oldset, struct * Whee! Actually deliver the signal. If the delivery failed, we need to * continue to iterate in this loop so we can deliver the SIGSEGV... */ - if (handle_signal(signr, ka, &info, oldset, scr)) + if (handle_signal(signr, &ka, &info, oldset, scr)) return 1; } --- linux-2.6.9-rc1/arch/ia64/kernel/smpboot.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/ia64/kernel/smpboot.c 2004-09-03 00:56:52.901118456 -0700 @@ -356,19 +356,15 @@ start_secondary (void *unused) return cpu_idle(); } -static struct task_struct * __devinit -fork_by_hand (void) +struct pt_regs * __init idle_regs(struct pt_regs *regs) { - /* - * Don't care about the IP and regs settings since we'll never reschedule the - * forked task. - */ - return copy_process(CLONE_VM|CLONE_IDLETASK, 0, 0, 0, NULL, NULL); + return NULL; } struct create_idle { struct task_struct *idle; struct completion done; + int cpu; }; void @@ -376,7 +372,7 @@ do_fork_idle(void *_c_idle) { struct create_idle *c_idle = _c_idle; - c_idle->idle = fork_by_hand(); + c_idle->idle = fork_idle(c_idle->cpu); complete(&c_idle->done); } @@ -384,10 +380,11 @@ static int __devinit do_boot_cpu (int sapicid, int cpu) { int timeout; - struct create_idle c_idle; + struct create_idle c_idle = { + .cpu = cpu, + .done = COMPLETION_INITIALIZER(c_idle.done), + }; DECLARE_WORK(work, do_fork_idle, &c_idle); - - init_completion(&c_idle.done); /* * We can't use kernel_thread since we must avoid to reschedule the child. */ @@ -400,16 +397,6 @@ do_boot_cpu (int sapicid, int cpu) if (IS_ERR(c_idle.idle)) panic("failed fork for CPU %d", cpu); - wake_up_forked_process(c_idle.idle); - - /* - * We remove it from the pidhash and the runqueue - * once we got the process: - */ - init_idle(c_idle.idle, cpu); - - unhash_process(c_idle.idle); - task_for_booting_cpu = c_idle.idle; Dprintk("Sending wakeup vector %lu to AP 0x%x/0x%x.\n", ap_wakeup_vector, cpu, sapicid); @@ -604,9 +591,10 @@ int __cpu_disable(void) if (cpu == 0) return -EBUSY; + cpu_clear(cpu, cpu_online_map); fixup_irqs(); local_flush_tlb_all(); - printk ("Disabled cpu %u\n", smp_processor_id()); + printk("Disabled cpu %u\n", cpu); return 0; } @@ -719,3 +707,4 @@ init_smp_config(void) printk(KERN_ERR "SMP: Can't set SAL AP Boot Rendezvous: %s\n", ia64_sal_strerror(sal_ret)); } + --- linux-2.6.9-rc1/arch/ia64/kernel/smp.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/ia64/kernel/smp.c 2004-09-03 00:56:37.248498016 -0700 @@ -47,6 +47,7 @@ #include #include #include +#include /* * Structure and data for smp_call_function(). This is designed to minimise static memory @@ -66,6 +67,9 @@ static volatile struct call_data_struct #define IPI_CALL_FUNC 0 #define IPI_CPU_STOP 1 +#ifdef CONFIG_KGDB +#define IPI_KGDB_INTERRUPT 2 +#endif /* This needs to be cacheline aligned because it is written to by *other* CPUs. */ static DEFINE_PER_CPU(u64, ipi_operation) ____cacheline_aligned; @@ -156,6 +160,12 @@ handle_IPI (int irq, void *dev_id, struc stop_this_cpu(); break; +#ifdef CONFIG_KGDB + case IPI_KGDB_INTERRUPT: + (void) in_kgdb(regs, NULL); + break; +#endif + default: printk(KERN_CRIT "Unknown IPI on CPU %d: %lu\n", this_cpu, which); break; @@ -361,6 +371,14 @@ smp_call_function (void (*func) (void *i } EXPORT_SYMBOL(smp_call_function); +#ifdef CONFIG_KGDB +void +smp_send_nmi_allbutself(void) +{ + send_IPI_allbutself(IPI_KGDB_INTERRUPT); +} +#endif + /* * this function calls the 'stop' function on all other CPUs in the system. */ --- linux-2.6.9-rc1/arch/ia64/kernel/time.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/ia64/kernel/time.c 2004-09-03 00:56:30.917460480 -0700 @@ -45,46 +45,7 @@ EXPORT_SYMBOL(last_cli_ip); #endif -static void -itc_reset (void) -{ -} - -/* - * Adjust for the fact that xtime has been advanced by delta_nsec (may be negative and/or - * larger than NSEC_PER_SEC. - */ -static void -itc_update (long delta_nsec) -{ -} - -/* - * Return the number of nano-seconds that elapsed since the last - * update to jiffy. It is quite possible that the timer interrupt - * will interrupt this and result in a race for any of jiffies, - * wall_jiffies or itm_next. Thus, the xtime_lock must be at least - * read synchronised when calling this routine (see do_gettimeofday() - * below for an example). - */ -unsigned long -itc_get_offset (void) -{ - unsigned long elapsed_cycles, lost = jiffies - wall_jiffies; - unsigned long now = ia64_get_itc(), last_tick; - - last_tick = (cpu_data(TIME_KEEPER_ID)->itm_next - - (lost + 1)*cpu_data(TIME_KEEPER_ID)->itm_delta); - - elapsed_cycles = now - last_tick; - return (elapsed_cycles*local_cpu_data->nsec_per_cyc) >> IA64_NSEC_PER_CYC_SHIFT; -} - -static struct time_interpolator itc_interpolator = { - .get_offset = itc_get_offset, - .update = itc_update, - .reset = itc_reset -}; +static struct time_interpolator itc_interpolator; int do_settimeofday (struct timespec *tv) @@ -127,51 +88,13 @@ EXPORT_SYMBOL(do_settimeofday); void do_gettimeofday (struct timeval *tv) { - unsigned long seq, nsec, usec, sec, old, offset; - - while (1) { + unsigned long seq, nsec, usec, sec, offset; + do { seq = read_seqbegin(&xtime_lock); - { - old = last_nsec_offset; - offset = time_interpolator_get_offset(); - sec = xtime.tv_sec; - nsec = xtime.tv_nsec; - } - if (unlikely(read_seqretry(&xtime_lock, seq))) - continue; - /* - * Ensure that for any pair of causally ordered gettimeofday() calls, time - * never goes backwards (even when ITC on different CPUs are not perfectly - * synchronized). (A pair of concurrent calls to gettimeofday() is by - * definition non-causal and hence it makes no sense to talk about - * time-continuity for such calls.) - * - * Doing this in a lock-free and race-free manner is tricky. Here is why - * it works (most of the time): read_seqretry() just succeeded, which - * implies we calculated a consistent (valid) value for "offset". If the - * cmpxchg() below succeeds, we further know that last_nsec_offset still - * has the same value as at the beginning of the loop, so there was - * presumably no timer-tick or other updates to last_nsec_offset in the - * meantime. This isn't 100% true though: there _is_ a possibility of a - * timer-tick occurring right right after read_seqretry() and then getting - * zero or more other readers which will set last_nsec_offset to the same - * value as the one we read at the beginning of the loop. If this - * happens, we'll end up returning a slightly newer time than we ought to - * (the jump forward is at most "offset" nano-seconds). There is no - * danger of causing time to go backwards, though, so we are safe in that - * sense. We could make the probability of this unlucky case occurring - * arbitrarily small by encoding a version number in last_nsec_offset, but - * even without versioning, the probability of this unlucky case should be - * so small that we won't worry about it. - */ - if (offset <= old) { - offset = old; - break; - } else if (likely(cmpxchg(&last_nsec_offset, old, offset) == old)) - break; - - /* someone else beat us to updating last_nsec_offset; try again */ - } + offset = time_interpolator_get_offset(); + sec = xtime.tv_sec; + nsec = xtime.tv_nsec; + } while (unlikely(read_seqretry(&xtime_lock, seq))); usec = (nsec + offset) / 1000; @@ -186,52 +109,6 @@ do_gettimeofday (struct timeval *tv) EXPORT_SYMBOL(do_gettimeofday); -/* - * The profiling function is SMP safe. (nothing can mess - * around with "current", and the profiling counters are - * updated with atomic operations). This is especially - * useful with a profiling multiplier != 1 - */ -static inline void -ia64_do_profile (struct pt_regs * regs) -{ - unsigned long ip, slot; - extern cpumask_t prof_cpu_mask; - - profile_hook(regs); - - if (user_mode(regs)) - return; - - if (!prof_buffer) - return; - - ip = instruction_pointer(regs); - /* Conserve space in histogram by encoding slot bits in address - * bits 2 and 3 rather than bits 0 and 1. - */ - slot = ip & 3; - ip = (ip & ~3UL) + 4*slot; - - /* - * Only measure the CPUs specified by /proc/irq/prof_cpu_mask. - * (default is all CPUs.) - */ - if (!cpu_isset(smp_processor_id(), prof_cpu_mask)) - return; - - ip -= (unsigned long) &_stext; - ip >>= prof_shift; - /* - * Don't ignore out-of-bounds IP values silently, - * put them into the last histogram slot, so if - * present, they will show up as a sharp peak. - */ - if (ip > prof_len-1) - ip = prof_len-1; - atomic_inc((atomic_t *)&prof_buffer[ip]); -} - static irqreturn_t timer_interrupt (int irq, void *dev_id, struct pt_regs *regs) { @@ -249,7 +126,7 @@ timer_interrupt (int irq, void *dev_id, printk(KERN_ERR "Oops: timer tick before it's due (itc=%lx,itm=%lx)\n", ia64_get_itc(), new_itm); - ia64_do_profile(regs); + profile_tick(CPU_PROFILING, regs); while (1) { #ifdef CONFIG_SMP @@ -323,6 +200,18 @@ ia64_cpu_local_tick (void) ia64_set_itm(local_cpu_data->itm_next); } +static int nojitter; + +static int __init nojitter_setup(char *str) +{ + nojitter = 1; + printk("Jitter checking for ITC timers disabled\n"); + return 1; +} + +__setup("nojitter", nojitter_setup); + + void __devinit ia64_init_itm (void) { @@ -371,7 +260,7 @@ ia64_init_itm (void) itc_drift = -1; local_cpu_data->itm_delta = (itc_freq + HZ/2) / HZ; - printk(KERN_INFO "CPU %d: base freq=%lu.%03luMHz, ITC ratio=%lu/%lu, " + printk(KERN_DEBUG "CPU %d: base freq=%lu.%03luMHz, ITC ratio=%lu/%lu, " "ITC freq=%lu.%03luMHz+/-%ldppm\n", smp_processor_id(), platform_base_freq / 1000000, (platform_base_freq / 1000) % 1000, itc_ratio.num, itc_ratio.den, itc_freq / 1000000, (itc_freq / 1000) % 1000, @@ -385,7 +274,23 @@ ia64_init_itm (void) if (!(sal_platform_features & IA64_SAL_PLATFORM_FEATURE_ITC_DRIFT)) { itc_interpolator.frequency = local_cpu_data->itc_freq; + itc_interpolator.shift = 16; itc_interpolator.drift = itc_drift; + itc_interpolator.source = TIME_SOURCE_CPU; +#ifdef CONFIG_SMP + /* On IA64 in an SMP configuration ITCs are never accurately synchronized. + * Jitter compensation requires a cmpxchg which may limit + * the scalability of the syscalls for retrieving time. + * The ITC synchronization is usually successful to within a few + * ITC ticks but this is not a sure thing. If you need to improve + * timer performance in SMP situations then boot the kernel with the + * "nojitter" option. However, doing so may result in time fluctuating (maybe + * even going backward) if the ITC offsets between the individual CPUs + * are too large. + */ + if (!nojitter) itc_interpolator.jitter = 1; +#endif + itc_interpolator.addr = NULL; register_time_interpolator(&itc_interpolator); } --- linux-2.6.9-rc1/arch/ia64/kernel/traps.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/ia64/kernel/traps.c 2004-09-03 00:56:40.982930296 -0700 @@ -14,9 +14,9 @@ #include #include /* For unblank_screen() */ #include /* for EXPORT_SYMBOL */ +#include #include -#include #include #include #include @@ -35,6 +35,19 @@ trap_init (void) fpswa_interface = __va(ia64_boot_param->fpswa); } +#ifdef CONFIG_KGDB +extern int kgdb_handle_exception(int, int, int, struct pt_regs *); +#define CHK_REMOTE_DEBUG(trapnr, signr, error_code, regs, after) \ + { \ + if (!user_mode(regs)) { \ + kgdb_handle_exception(trapnr, signr, error_code, regs); \ + after; \ + } \ + } +#else +#define CHK_REMOTE_DEBUG(trapnr, signr, error_code, regs, after) +#endif + /* * Unlock any spinlocks which will prevent us from getting the message out (timerlist_lock * is acquired through the console unblank code) @@ -85,6 +98,8 @@ die (const char *str, struct pt_regs *re bust_spinlocks(1); } + CHK_REMOTE_DEBUG(-1, SIGTRAP, err, regs,) + if (++die.lock_owner_depth < 3) { printk("%s[%d]: %s %ld [%d]\n", current->comm, current->pid, str, err, ++die_counter); @@ -117,9 +132,13 @@ ia64_bad_break (unsigned long break_num, siginfo.si_flags = 0; /* clear __ISR_VALID */ siginfo.si_isr = 0; + + switch (break_num) { case 0: /* unknown error (used by GCC for __builtin_abort()) */ +#ifndef CONFIG_KGDB die_if_kernel("bugcheck!", regs, break_num); +#endif sig = SIGILL; code = ILL_ILLOPC; break; @@ -172,8 +191,10 @@ ia64_bad_break (unsigned long break_num, break; default: +#ifndef CONFIG_KGDB if (break_num < 0x40000 || break_num > 0x100000) die_if_kernel("Bad break", regs, break_num); +#endif if (break_num < 0x80000) { sig = SIGILL; code = __ILL_BREAK; @@ -181,6 +202,13 @@ ia64_bad_break (unsigned long break_num, sig = SIGTRAP; code = TRAP_BRKPT; } } +#ifdef CONFIG_KGDB + /* + * We don't want to trap simulator system calls. + */ + if (break_num != 0x80001) + CHK_REMOTE_DEBUG(11, sig, break_num, regs, return) +#endif siginfo.si_signo = sig; siginfo.si_errno = 0; siginfo.si_code = code; @@ -488,8 +516,9 @@ ia64_fault (unsigned long vector, unsign break; case 29: /* Debug */ - case 35: /* Taken Branch Trap */ case 36: /* Single Step Trap */ + CHK_REMOTE_DEBUG(vector, SIGTRAP, isr, regs, return) + case 35: /* Taken Branch Trap */ if (fsys_mode(current, regs)) { extern char __kernel_syscall_via_break[]; /* @@ -603,6 +632,7 @@ ia64_fault (unsigned long vector, unsign sprintf(buf, "Fault %lu", vector); break; } + CHK_REMOTE_DEBUG(vector, SIGTRAP, isr, regs,) die_if_kernel(buf, regs, error); force_sig(SIGILL, current); } --- linux-2.6.9-rc1/arch/ia64/kernel/unwind.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/arch/ia64/kernel/unwind.c 2004-09-03 00:56:37.251497560 -0700 @@ -75,10 +75,69 @@ # define STAT(x...) #endif + +#ifdef CONFIG_KGDB_EARLY +#define KGDB_EARLY_SIZE 100 +static struct unw_reg_state __initdata kgdb_reg_state[KGDB_EARLY_SIZE]; +static struct unw_labeled_state __initdata kgdb_labeled_state[KGDB_EARLY_SIZE]; +void __initdata *kgdb_reg_state_free, __initdata *kgdb_labeled_state_free; + +static void __init +kgdb_malloc_init(void) +{ + int i; + + kgdb_reg_state_free = kgdb_reg_state; + for (i = 1; i < KGDB_EARLY_SIZE; i++) { + *((unsigned long *) &kgdb_reg_state[i]) = (unsigned long) kgdb_reg_state_free; + kgdb_reg_state_free = &kgdb_reg_state[i]; + } + + kgdb_labeled_state_free = kgdb_labeled_state; + for (i = 1; i < KGDB_EARLY_SIZE; i++) { + *((unsigned long *) &kgdb_labeled_state[i]) = + (unsigned long) kgdb_labeled_state_free; + kgdb_labeled_state_free = &kgdb_labeled_state[i]; + } + +} + +static void * __init +kgdb_malloc(void **mem) +{ + void *p; + + p = *mem; + *mem = *((void **) p); + return p; +} + +static void __init +kgdb_free(void **mem, void *p) +{ + *((void **)p) = *mem; + *mem = p; +} + +#define alloc_reg_state() (!malloc_sizes[0].cs_cachep ? \ + kgdb_malloc(&kgdb_reg_state_free) : \ + kmalloc(sizeof(struct unw_reg_state), GFP_ATOMIC)) +#define free_reg_state(usr) (!malloc_sizes[0].cs_cachep ? \ + kgdb_free(&kgdb_reg_state_free, usr) : \ + kfree(usr)) +#define alloc_labeled_state() (!malloc_sizes[0].cs_cachep ? \ + kgdb_malloc(&kgdb_labeled_state_free) : \ + kmalloc(sizeof(struct unw_labeled_state), GFP_ATOMIC)) +#define free_labeled_state(usr) (!malloc_sizes[0].cs_cachep ? \ + kgdb_free(&kgdb_labeled_state_free, usr) : \ + kfree(usr)) + +#else #define alloc_reg_state() kmalloc(sizeof(struct unw_reg_state), GFP_ATOMIC) #define free_reg_state(usr) kfree(usr) #define alloc_labeled_state() kmalloc(sizeof(struct unw_labeled_state), GFP_ATOMIC) #define free_labeled_state(usr) kfree(usr) +#endif typedef unsigned long unw_word; typedef unsigned char unw_hash_index_t; @@ -2267,6 +2326,10 @@ unw_init (void) init_unwind_table(&unw.kernel_table, "kernel", KERNEL_START, (unsigned long) __gp, __start_unwind, __end_unwind); + +#ifdef CONFIG_KGDB_EARLY + kgdb_malloc_init(); +#endif } /* --- linux-2.6.9-rc1/arch/ia64/kernel/vmlinux.lds.S 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/ia64/kernel/vmlinux.lds.S 2004-09-02 20:51:51.000000000 -0700 @@ -135,12 +135,6 @@ SECTIONS *(.init.setup) __setup_end = .; } - __param : AT(ADDR(__param) - LOAD_OFFSET) - { - __start___param = .; - *(__param) - __stop___param = .; - } .initcall.init : AT(ADDR(.initcall.init) - LOAD_OFFSET) { __initcall_start = .; --- linux-2.6.9-rc1/arch/ia64/lib/dec_and_lock.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/ia64/lib/dec_and_lock.c 2004-09-03 00:56:42.825650160 -0700 @@ -13,6 +13,7 @@ #include #include +#ifndef CONFIG_LOCKMETER /* * Decrement REFCOUNT and if the count reaches zero, acquire the spinlock. Both of these * operations have to be done atomically, so that the count doesn't drop to zero without @@ -40,3 +41,4 @@ atomic_dec_and_lock (atomic_t *refcount, } EXPORT_SYMBOL(atomic_dec_and_lock); +#endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/ia64/lib/kgdb_serial.c 2004-09-03 00:56:37.469464424 -0700 @@ -0,0 +1,607 @@ +/* + * Serial interface GDB stub + * + * Written (hacked together) by David Grothe (dave@gcom.com) + * Modified to allow invokation early in boot see also + * kgdb.h for instructions by George Anzinger(george@mvista.com) + * Modified to handle debugging over ethernet by Robert Walsh + * and wangdi , based on + * code by San Mehat. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_KGDB_USER_CONSOLE +extern void kgdb_console_finit(void); +#endif +#define PRNT_off +#define TEST_EXISTANCE +#ifdef PRNT +#define dbprintk(s) printk s +#else +#define dbprintk(s) +#endif +#define TEST_INTERRUPT_off +#ifdef TEST_INTERRUPT +#define intprintk(s) printk s +#else +#define intprintk(s) +#endif + +#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT) + +#define GDB_BUF_SIZE 512 /* power of 2, please */ + +static char gdb_buf[GDB_BUF_SIZE]; +static int gdb_buf_in_inx; +static atomic_t gdb_buf_in_cnt; +static int gdb_buf_out_inx; + +struct async_struct *gdb_async_info; +static int gdb_async_irq; + +#define outb_px(a,b,c) kgdb_serial_out(a, b, c) +#define outb_py(a, b, c) kgdb_serial_out(b, c, a) +#define inb_py(ASYNC, OFF) kgdb_serial_in(ASYNC, OFF) + +static void inline +kgdb_serial_out(struct async_struct *serial, unsigned long offset, char ch) +{ + offset <<= serial->iomem_reg_shift; + + switch(serial->io_type) { + case SERIAL_IO_MEM: + writeb(ch, serial->iomem_base + offset); + break; + default: + /* fix parameter order */ + outb( ch, serial->port + offset); + break; + } + return; +} + +static inline unsigned int +kgdb_serial_in(struct async_struct *serial, unsigned long offset) +{ + unsigned int ch; + + offset <<= serial->iomem_reg_shift; + + switch(serial->io_type) { + case SERIAL_IO_MEM: + ch = readb(serial->iomem_base + offset); + break; + default: + ch = inb(serial->port + offset); + break; + } + + return ch; +} + +static void program_uart(struct async_struct *info); +static void write_char(struct async_struct *info, int chr); +/* + * Get a byte from the hardware data buffer and return it + */ +static int +read_data_bfr(struct async_struct *info) +{ + char it = inb_py(info, UART_LSR); + + if (it & UART_LSR_DR) + return (inb_py(info, UART_RX)); + /* + * If we have a framing error assume somebody messed with + * our uart. Reprogram it and send '-' both ways... + */ + if (it & 0xc) { + program_uart(info); + write_char(info, '-'); + return ('-'); + } + return (-1); + +} /* read_data_bfr */ + +/* + * Get a char if available, return -1 if nothing available. + * Empty the receive buffer first, then look at the interface hardware. + + * Locking here is a bit of a problem. We MUST not lock out communication + * if we are trying to talk to gdb about a kgdb entry. ON the other hand + * we can loose chars in the console pass thru if we don't lock. It is also + * possible that we could hold the lock or be waiting for it when kgdb + * NEEDS to talk. Since kgdb locks down the world, it does not need locks. + * We do, of course have possible issues with interrupting a uart operation, + * but we will just depend on the uart status to help keep that straight. + + */ +static spinlock_t uart_interrupt_lock = SPIN_LOCK_UNLOCKED; +#ifdef CONFIG_SMP +extern spinlock_t kgdb_spinlock; +#endif + +static int +read_char(struct async_struct *info) +{ + int chr; + unsigned long flags; + local_irq_save(flags); +#ifdef CONFIG_SMP + if (!spin_is_locked(&kgdb_spinlock)) { + spin_lock(&uart_interrupt_lock); + } +#endif + if (atomic_read(&gdb_buf_in_cnt) != 0) { /* intr routine has q'd chars */ + chr = gdb_buf[gdb_buf_out_inx++]; + gdb_buf_out_inx &= (GDB_BUF_SIZE - 1); + atomic_dec(&gdb_buf_in_cnt); + } else { + chr = read_data_bfr(info); + } +#ifdef CONFIG_SMP + if (!spin_is_locked(&kgdb_spinlock)) { + spin_unlock(&uart_interrupt_lock); + } +#endif + local_irq_restore(flags); + return (chr); +} + +/* + * Wait until the interface can accept a char, then write it. + */ +static void +write_char(struct async_struct *info, int chr) +{ + while (!(inb_py(info, UART_LSR) & UART_LSR_THRE)) ; + + outb_py(chr, info, UART_TX); + +} /* write_char */ + +/* + * Mostly we don't need a spinlock, but since the console goes + * thru here with interrutps on, well, we need to catch those + * chars. + */ +/* + * This is the receiver interrupt routine for the GDB stub. + * It will receive a limited number of characters of input + * from the gdb host machine and save them up in a buffer. + * + * When the gdb stub routine tty_getDebugChar() is called it + * draws characters out of the buffer until it is empty and + * then reads directly from the serial port. + * + * We do not attempt to write chars from the interrupt routine + * since the stubs do all of that via tty_putDebugChar() which + * writes one byte after waiting for the interface to become + * ready. + * + * The debug stubs like to run with interrupts disabled since, + * after all, they run as a consequence of a breakpoint in + * the kernel. + * + * Perhaps someone who knows more about the tty driver than I + * care to learn can make this work for any low level serial + * driver. + */ +static irqreturn_t +gdb_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct async_struct *info; + unsigned long flags; + + info = gdb_async_info; + if (!info || !info->tty || irq != gdb_async_irq) + return IRQ_NONE; + + local_irq_save(flags); + spin_lock(&uart_interrupt_lock); + do { + int chr = read_data_bfr(info); + intprintk(("Debug char on int: %x hex\n", chr)); + if (chr < 0) + continue; + + if (chr == 3) { /* Ctrl-C means remote interrupt */ + BREAKPOINT; + continue; + } + + if (atomic_read(&gdb_buf_in_cnt) >= GDB_BUF_SIZE) { + /* buffer overflow tosses early char */ + read_char(info); + } + gdb_buf[gdb_buf_in_inx++] = chr; + gdb_buf_in_inx &= (GDB_BUF_SIZE - 1); + } while (inb_py(info, UART_IIR) & UART_IIR_RDI); + spin_unlock(&uart_interrupt_lock); + local_irq_restore(flags); + return IRQ_HANDLED; +} /* gdb_interrupt */ + +/* + * Just a NULL routine for testing. + */ +void +gdb_null(void) +{ +} /* gdb_null */ + +/* These structure are filled in with values defined in asm/kgdb_local.h + */ +static struct serial_state state = SB_STATE; +static struct async_struct local_info = SB_INFO; +static int ok_to_enable_ints = 0; +static void kgdb_enable_ints_now(void); + +extern char *kgdb_version; +/* + * Hook an IRQ for KGDB. + * + * This routine is called from tty_putDebugChar, below. + */ +static int ints_disabled = 1; +int +gdb_hook_interrupt(struct async_struct *info, int verb) +{ + struct serial_state *state = info->state; + unsigned long flags; + int port; +#ifdef TEST_EXISTANCE + int scratch, scratch2; +#endif + + /* The above fails if memory managment is not set up yet. + * Rather than fail the set up, just keep track of the fact + * and pick up the interrupt thing later. + */ + gdb_async_info = info; + port = gdb_async_info->port; + gdb_async_irq = state->irq; + if (verb) { + printk("kgdb %s : port =%x, IRQ=%d, divisor =%d\n", + kgdb_version, + port, + gdb_async_irq, gdb_async_info->state->custom_divisor); + } + local_irq_save(flags); +#ifdef TEST_EXISTANCE + /* Existance test */ + /* Should not need all this, but just in case.... */ + + scratch = inb_py(info, UART_IER); + outb_px(info, UART_IER, 0); + scratch2 = inb_py(info, UART_IER); + outb_px(info, UART_IER, scratch); + if (scratch2) { + printk + ("gdb_hook_interrupt: Could not clear IER, not a UART!\n"); + local_irq_restore(flags); + return 1; /* We failed; there's nothing here */ + } + scratch2 = inb_py(info, UART_LCR); + outb_px(info, UART_LCR, 0xBF); /* set up for StarTech test */ + outb_px(info, UART_EFR, 0); /* EFR is the same as FCR */ + outb_px(info, UART_LCR, 0); + outb_px(info, UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = inb_py(info, UART_IIR) >> 6; + if (scratch == 1) { + printk("gdb_hook_interrupt: Undefined UART type!" + " Not a UART! \n"); + local_irq_restore(flags); + return 1; + } else { + dbprintk(("gdb_hook_interrupt: UART type " + "is %d where 0=16450, 2=16550 3=16550A\n", scratch)); + } + scratch = inb_py(info, UART_MCR); + outb_px(info, UART_MCR, UART_MCR_LOOP | scratch); + outb_px(info, UART_MCR, UART_MCR_LOOP | 0x0A); + scratch2 = inb_py(info, UART_MSR) & 0xF0; + outb_px(info, UART_MCR, scratch); +#ifndef CONFIG_SERIAL_8250_HCDP /* HCDP seems to skip this test */ + if (scratch2 != 0x90) { + printk("gdb_hook_interrupt: " + "Loop back test failed! Not a UART!\n"); + local_irq_restore(flags); + return scratch2 + 1000; /* force 0 to fail */ + } +#endif +#endif /* test existance */ + program_uart(info); + local_irq_restore(flags); + + return (0); + +} /* gdb_hook_interrupt */ + +static void +program_uart(struct async_struct *info) +{ + (void) inb_py(info, UART_RX); + outb_px(info, UART_IER, 0); + + (void) inb_py(info, UART_RX); /* serial driver comments say */ + (void) inb_py(info, UART_IIR); /* this clears the interrupt regs */ + (void) inb_py(info, UART_MSR); + outb_px(info, UART_LCR, UART_LCR_WLEN8 | UART_LCR_DLAB); + outb_px(info, UART_DLL, info->state->custom_divisor & 0xff); /* LS */ + outb_px(info, UART_DLM, info->state->custom_divisor >> 8); /* MS */ + outb_px(info, UART_MCR, info->MCR); + printk("UART_LCR = 0x%x\n", inb_py(info, UART_LCR)); + + outb_px(info, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1 | UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR); /* set fcr */ + outb_px(info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */ + outb_px(info, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1); /* set fcr */ + if (!ints_disabled) { + intprintk(("KGDB: Sending %d to port %x offset %d\n", + gdb_async_info->IER, + (int) gdb_async_info->port, UART_IER)); + outb_px(gdb_async_info, UART_IER, gdb_async_info->IER); + } + return; +} + +/* + * tty_getDebugChar + * + * This is a GDB stub routine. It waits for a character from the + * serial interface and then returns it. If there is no serial + * interface connection then it returns a bogus value which will + * almost certainly cause the system to hang. In the + */ +int kgdb_in_isr = 0; +int kgdb_in_lsr = 0; +extern spinlock_t kgdb_spinlock; + +/* Caller takes needed protections */ + +int +tty_getDebugChar(void) +{ + volatile int chr; + volatile long time, end_time; + + dbprintk(("tty_getDebugChar(port %x): ", gdb_async_info->port)); + + if (gdb_async_info == NULL) { + gdb_hook_interrupt(&local_info, 0); + } + /* + * This trick says if we wait a very long time and get + * no char, return the -1 and let the upper level deal + * with it. + */ + time = ia64_get_itc(); + end_time = time + local_cpu_data->itm_delta * (HZ / 10); + if (!local_cpu_data->itm_delta) + end_time = time + 500; + while (((chr = read_char(gdb_async_info)) == -1) && + (end_time - time) > 0) { + time = ia64_get_itc(); + }; + /* + * This covers our butts if some other code messes with + * our uart, hay, it happens :o) + */ + if (chr == -1) + udelay(10000); +// program_uart(gdb_async_info); + + dbprintk(("%c\n", chr > ' ' && chr < 0x7F ? chr : ' ')); + return (chr); + +} /* tty_getDebugChar */ + +static int count = 3; +static spinlock_t one_at_atime = SPIN_LOCK_UNLOCKED; + +static int __init +kgdb_enable_ints(void) +{ + if (kgdboe) { + return 0; + } + if (gdb_async_info == NULL) { + gdb_hook_interrupt(&local_info, 1); + } + ok_to_enable_ints = 1; + kgdb_enable_ints_now(); +#ifdef CONFIG_KGDB_USER_CONSOLE + kgdb_console_finit(); +#endif + return 0; +} + +static int mem_init_done = 1; + +#ifdef CONFIG_SERIAL_8250 +void shutdown_for_kgdb(struct async_struct *gdb_async_info); +#endif + + +static inline int kgdb_mem_init_done(void) +{ + return mem_init_done; +} + +#ifdef CONFIG_KGDB_EARLY +static struct irqaction kgdb_action; +#endif + +static void +kgdb_enable_ints_now(void) +{ + if (!spin_trylock(&one_at_atime)) + return; + if (!ints_disabled) + goto exit; + if (kgdb_mem_init_done() && + ints_disabled) { /* don't try till mem init */ +#ifdef CONFIG_SERIAL_8250 + /* + * The ifdef here allows the system to be configured + * without the serial driver. + * Don't make it a module, however, it will steal the port + */ + shutdown_for_kgdb(gdb_async_info); +#endif +#ifndef CONFIG_KGDB_EARLY + ints_disabled = request_irq(gdb_async_info->state->irq, + gdb_interrupt, + IRQ_T(gdb_async_info), + "KGDB-stub", NULL); +#else +{ + irq_desc_t *desc; + kgdb_action.handler = gdb_interrupt; + kgdb_action.flags = IRQ_T(gdb_async_info); + kgdb_action.mask = CPU_MASK_NONE; + kgdb_action.name = "KGDB-stub"; + kgdb_action.next = NULL; + kgdb_action.dev_id = NULL; + desc = irq_descp(gdb_async_info->state->irq); + if (desc->handler != &no_irq_type) { + desc->action = &kgdb_action; + ints_disabled = 0; + desc->depth = 0; + desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | + IRQ_WAITING | IRQ_INPROGRESS); + desc->handler->startup(gdb_async_info->state->irq); + } + if (ints_disabled) + printk("kgdb_enable_ints_now: setup_irq failed\n"); + else + printk("kgdb early access enabled\n"); +} +#endif + intprintk(("KGDB: request_irq returned %d\n", ints_disabled)); + } + if (!ints_disabled) { + intprintk(("KGDB: Sending %d to port %x offset %d\n", + gdb_async_info->IER, + (int) gdb_async_info->port, UART_IER)); + outb_px(gdb_async_info, UART_IER, gdb_async_info->IER); + } + exit: + spin_unlock(&one_at_atime); +} + +/* + * tty_putDebugChar + * + * This is a GDB stub routine. It waits until the interface is ready + * to transmit a char and then sends it. If there is no serial + * interface connection then it simply returns to its caller, having + * pretended to send the char. Caller takes needed protections. + */ +void +tty_putDebugChar(int chr) +{ + dbprintk(("tty_putDebugChar(port %x): chr=%02x '%c', ints_on=%d\n", + gdb_async_info->port, + chr, + chr > ' ' && chr < 0x7F ? chr : ' ', ints_disabled ? 0 : 1)); + + if (gdb_async_info == NULL) { + gdb_hook_interrupt(&local_info, 0); + } + + write_char(gdb_async_info, chr); /* this routine will wait */ + count = (chr == '#') ? 0 : count + 1; + if ((count == 2)) { /* try to enable after */ + if (ints_disabled & ok_to_enable_ints) + kgdb_enable_ints_now(); /* try to enable after */ + + /* We do this a lot because, well we really want to get these + * interrupts. The serial driver will clear these bits when it + * initializes the chip. Every thing else it does is ok, + * but this. + */ + if (!ints_disabled) { + outb_px(gdb_async_info, UART_IER, + gdb_async_info->IER); + } + } + +} /* tty_putDebugChar */ + +/* + * This does nothing for the serial port, since it doesn't buffer. + */ + +void tty_flushDebugChar(void) +{ +} + +void __init +kgdb_serial_init(void) +{ + extern char saved_command_line[]; + char *cp, *str; + int kgdbbreak = 0; + + for (cp = saved_command_line; *cp; cp++) { + if (memcmp(cp, "kgdb=1", 6) == 0) { + kgdbbreak = 1; + } + else if (memcmp(cp,"kgdbio=", 7) == 0) { + int baud; + + cp += 7; + baud = simple_strtoul(cp, &str, 10); + state.custom_divisor = SB_BASE/baud; + if (*str == ',') { + str++; + state.irq = simple_strtoul(str, &str, 10); + if (*str == ',') { + str++; + local_info.iomem_base = state.iomem_base = (char *) + simple_strtoul(str, &str, 0); + local_info.io_type = state.io_type = SERIAL_IO_MEM; + } + } + printk("kgdb_serial_init: irq = %d iomem_base = 0x%lx baud = %d\n", + state.irq, state.iomem_base, baud); + } + } + + kgdb_enable_ints(); + if (!ints_disabled && kgdbbreak) + BREAKPOINT; +} + +#ifndef CONFIG_IA64_HP_SIM +late_initcall(kgdb_enable_ints); +#endif --- linux-2.6.9-rc1/arch/ia64/lib/Makefile 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/ia64/lib/Makefile 2004-09-03 00:56:37.254497104 -0700 @@ -16,6 +16,7 @@ lib-$(CONFIG_MCKINLEY) += copy_page_mck. lib-$(CONFIG_PERFMON) += carta_random.o lib-$(CONFIG_MD_RAID5) += xor.o lib-$(CONFIG_HAVE_DEC_LOCK) += dec_and_lock.o +lib-$(CONFIG_KGDB) += kgdb_serial.o AFLAGS___divdi3.o = AFLAGS___udivdi3.o = -DUNSIGNED --- linux-2.6.9-rc1/arch/ia64/lib/swiotlb.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/arch/ia64/lib/swiotlb.c 2004-09-02 20:51:51.000000000 -0700 @@ -11,6 +11,7 @@ * 03/05/07 davidm Switch from PCI-DMA to generic device DMA API. * 00/12/13 davidm Rename to swiotlb.c and add mark_clean() to avoid * unnecessary i-cache flushing. + * 04/07/.. ak Better overflow handling. Assorted fixes. */ #include @@ -20,6 +21,7 @@ #include #include #include +#include #include #include @@ -46,6 +48,8 @@ */ #define IO_TLB_SHIFT 11 +int swiotlb_force; + /* * Used to do a quick range check in swiotlb_unmap_single and swiotlb_sync_single_*, to see * if the memory was in fact allocated by this API. @@ -55,8 +59,16 @@ static char *io_tlb_start, *io_tlb_end; /* * The number of IO TLB blocks (in groups of 64) betweeen io_tlb_start and io_tlb_end. * This is command line adjustable via setup_io_tlb_npages. + * Default to 64MB. + */ +static unsigned long io_tlb_nslabs = 32768; + +/* + * When the IOMMU overflows we return a fallback buffer. This sets the size. */ -static unsigned long io_tlb_nslabs = 1024; +static unsigned long io_tlb_overflow = 32*1024; + +void *io_tlb_overflow_buffer; /* * This is a free list describing the number of free entries available from each index @@ -78,15 +90,19 @@ static spinlock_t io_tlb_lock = SPIN_LOC static int __init setup_io_tlb_npages (char *str) { - io_tlb_nslabs = simple_strtoul(str, NULL, 0) << (PAGE_SHIFT - IO_TLB_SHIFT); - - /* avoid tail segment of size < IO_TLB_SEGSIZE */ - io_tlb_nslabs = ALIGN(io_tlb_nslabs, IO_TLB_SEGSIZE); - + if (isdigit(*str)) { + io_tlb_nslabs = simple_strtoul(str, &str, 0) << (PAGE_SHIFT - IO_TLB_SHIFT); + /* avoid tail segment of size < IO_TLB_SEGSIZE */ + io_tlb_nslabs = ALIGN(io_tlb_nslabs, IO_TLB_SEGSIZE); + } + if (*str == ',') + ++str; + if (!strcmp(str, "force")) + swiotlb_force = 1; return 1; } __setup("swiotlb=", setup_io_tlb_npages); - +/* make io_tlb_overflow tunable too? */ /* * Statically reserve bounce buffer space and initialize bounce buffer data structures for @@ -102,7 +118,7 @@ swiotlb_init (void) */ io_tlb_start = alloc_bootmem_low_pages(io_tlb_nslabs * (1 << IO_TLB_SHIFT)); if (!io_tlb_start) - BUG(); + panic("Cannot allocate SWIOTLB buffer"); io_tlb_end = io_tlb_start + io_tlb_nslabs * (1 << IO_TLB_SHIFT); /* @@ -115,10 +131,22 @@ swiotlb_init (void) io_tlb_list[i] = IO_TLB_SEGSIZE - OFFSET(i, IO_TLB_SEGSIZE); io_tlb_index = 0; io_tlb_orig_addr = alloc_bootmem(io_tlb_nslabs * sizeof(char *)); - - printk(KERN_INFO "Placing software IO TLB between 0x%p - 0x%p\n", - (void *) io_tlb_start, (void *) io_tlb_end); -} + + /* + * Get the overflow emergency buffer + */ + io_tlb_overflow_buffer = alloc_bootmem_low(io_tlb_overflow); + printk(KERN_INFO "Placing software IO TLB between 0x%lx - 0x%lx\n", + virt_to_phys(io_tlb_start), virt_to_phys(io_tlb_end)); +} + +static inline int address_needs_mapping(struct device *hwdev, dma_addr_t addr) +{ + dma_addr_t mask = 0xffffffff; + if (hwdev && hwdev->dma_mask) + mask = *hwdev->dma_mask; + return (addr & ~mask) != 0; +} /* * Allocates bounce buffer and returns its kernel virtual address. @@ -184,11 +212,8 @@ map_single (struct device *hwdev, char * index = 0; } while (index != wrap); - /* - * XXX What is a suitable recovery mechanism here? We cannot - * sleep because we are called from with in interrupts! - */ - panic("map_single: could not allocate software IO TLB (%ld bytes)", size); + spin_unlock_irqrestore(&io_tlb_lock, flags); + return NULL; } found: spin_unlock_irqrestore(&io_tlb_lock, flags); @@ -285,7 +310,7 @@ swiotlb_alloc_coherent (struct device *h memset(ret, 0, size); dev_addr = virt_to_phys(ret); - if (hwdev && hwdev->dma_mask && (dev_addr & ~*hwdev->dma_mask) != 0) + if (address_needs_mapping(hwdev,dev_addr)) panic("swiotlb_alloc_consistent: allocated memory is out of range for device"); *dma_handle = dev_addr; return ret; @@ -297,6 +322,28 @@ swiotlb_free_coherent (struct device *hw free_pages((unsigned long) vaddr, get_order(size)); } +static void swiotlb_full(struct device *dev, size_t size, int dir, int do_panic) +{ + /* + * Ran out of IOMMU space for this operation. This is very bad. + * Unfortunately the drivers cannot handle this operation properly. + * unless they check for pci_dma_mapping_error (most don't) + * When the mapping is small enough return a static buffer to limit + * the damage, or panic when the transfer is too big. + */ + + printk(KERN_ERR + "PCI-DMA: Out of SW-IOMMU space for %lu bytes at device %s\n", + size, dev ? dev->bus_id : "?"); + + if (size > io_tlb_overflow && do_panic) { + if (dir == PCI_DMA_FROMDEVICE || dir == PCI_DMA_BIDIRECTIONAL) + panic("PCI-DMA: Memory would be corrupted\n"); + if (dir == PCI_DMA_TODEVICE || dir == PCI_DMA_BIDIRECTIONAL) + panic("PCI-DMA: Random memory would be DMAed\n"); + } +} + /* * Map a single buffer of the indicated size for DMA in streaming mode. The PCI address * to use is returned. @@ -308,13 +355,14 @@ dma_addr_t swiotlb_map_single (struct device *hwdev, void *ptr, size_t size, int dir) { unsigned long dev_addr = virt_to_phys(ptr); + void *map; if (dir == DMA_NONE) BUG(); /* * Check if the PCI device can DMA to ptr... if so, just return ptr */ - if (hwdev && hwdev->dma_mask && (dev_addr & ~*hwdev->dma_mask) == 0) + if (!address_needs_mapping(hwdev, dev_addr) && !swiotlb_force) /* * Device is bit capable of DMA'ing to the buffer... just return the PCI * address of ptr @@ -324,12 +372,18 @@ swiotlb_map_single (struct device *hwdev /* * get a bounce buffer: */ - dev_addr = virt_to_phys(map_single(hwdev, ptr, size, dir)); + map = map_single(hwdev, ptr, size, dir); + if (!map) { + swiotlb_full(hwdev, size, dir, 1); + map = io_tlb_overflow_buffer; + } + + dev_addr = virt_to_phys(map); /* * Ensure that the address returned is DMA'ble: */ - if (hwdev && hwdev->dma_mask && (dev_addr & ~*hwdev->dma_mask) != 0) + if (address_needs_mapping(hwdev, dev_addr)) panic("map_single: bounce buffer is not DMA'ble"); return dev_addr; @@ -437,9 +491,17 @@ swiotlb_map_sg (struct device *hwdev, st for (i = 0; i < nelems; i++, sg++) { addr = SG_ENT_VIRT_ADDRESS(sg); dev_addr = virt_to_phys(addr); - if (hwdev && hwdev->dma_mask && (dev_addr & ~*hwdev->dma_mask) != 0) - sg->dma_address = (dma_addr_t) map_single(hwdev, addr, sg->length, dir); - else + if (swiotlb_force || address_needs_mapping(hwdev, dev_addr)) { + sg->dma_address = (dma_addr_t) virt_to_phys(map_single(hwdev, addr, sg->length, dir)); + if (!sg->dma_address) { + /* Don't panic here, we expect pci_map_sg users + to do proper error handling. */ + swiotlb_full(hwdev, sg->length, dir, 0); + swiotlb_unmap_sg(hwdev, sg - i, i, dir); + sg[0].dma_length = 0; + return 0; + } + } else sg->dma_address = dev_addr; sg->dma_length = sg->length; } @@ -460,7 +522,7 @@ swiotlb_unmap_sg (struct device *hwdev, for (i = 0; i < nelems; i++, sg++) if (sg->dma_address != SG_ENT_PHYS_ADDRESS(sg)) - unmap_single(hwdev, (void *) sg->dma_address, sg->dma_length, dir); + unmap_single(hwdev, (void *) phys_to_virt(sg->dma_address), sg->dma_length, dir); else if (dir == DMA_FROM_DEVICE) mark_clean(SG_ENT_VIRT_ADDRESS(sg), sg->dma_length); } @@ -501,7 +563,7 @@ swiotlb_sync_sg_for_device (struct devic int swiotlb_dma_mapping_error (dma_addr_t dma_addr) { - return 0; + return (dma_addr == virt_to_phys(io_tlb_overflow_buffer)); } /* --- linux-2.6.9-rc1/arch/ia64/mm/contig.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/ia64/mm/contig.c 2004-09-03 00:56:30.917460480 -0700 @@ -116,19 +116,19 @@ find_bootmap_location (unsigned long sta range_start = max(start, free_start); range_end = min(end, rsvd_region[i].start & PAGE_MASK); + free_start = PAGE_ALIGN(rsvd_region[i].end); + if (range_end <= range_start) continue; /* skip over empty range */ - if (range_end - range_start >= needed) { + if (range_end - range_start >= needed) { bootmap_start = __pa(range_start); - return 1; /* done */ + return -1; /* done */ } /* nothing more available in this segment */ if (range_end == end) return 0; - - free_start = PAGE_ALIGN(rsvd_region[i].end); } return 0; } @@ -267,7 +267,7 @@ paging_init (void) efi_memmap_walk(find_largest_hole, (u64 *)&max_gap); if (max_gap < LARGE_GAP) { vmem_map = (struct page *) 0; - free_area_init_node(0, &contig_page_data, NULL, zones_size, 0, + free_area_init_node(0, &contig_page_data, zones_size, 0, zholes_size); mem_map = contig_page_data.node_mem_map; } else { @@ -280,7 +280,8 @@ paging_init (void) vmem_map = (struct page *) vmalloc_end; efi_memmap_walk(create_mem_map_page_table, 0); - free_area_init_node(0, &contig_page_data, vmem_map, zones_size, + contig_page_data.node_mem_map = vmem_map; + free_area_init_node(0, &contig_page_data, zones_size, 0, zholes_size); mem_map = contig_page_data.node_mem_map; --- linux-2.6.9-rc1/arch/ia64/mm/discontig.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/ia64/mm/discontig.c 2004-09-03 00:56:51.387348584 -0700 @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -664,8 +665,8 @@ void paging_init(void) pfn_offset = mem_data[node].min_pfn; - free_area_init_node(node, NODE_DATA(node), - vmem_map + pfn_offset, zones_size, + NODE_DATA(node)->node_mem_map = vmem_map + pfn_offset; + free_area_init_node(node, NODE_DATA(node), zones_size, pfn_offset, zholes_size); } --- linux-2.6.9-rc1/arch/ia64/mm/fault.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/ia64/mm/fault.c 2004-09-03 00:56:40.982930296 -0700 @@ -14,7 +14,7 @@ #include #include #include -#include +#include extern void die (char *, struct pt_regs *, long); @@ -40,6 +40,7 @@ expand_backing_store (struct vm_area_str vma->vm_mm->total_vm += grow; if (vma->vm_flags & VM_LOCKED) vma->vm_mm->locked_vm += grow; + __vm_stat_account(vma->vm_mm, vma->vm_flags, vma->vm_file, grow); return 0; } @@ -232,6 +233,11 @@ ia64_do_page_fault (unsigned long addres */ bust_spinlocks(1); +#ifdef CONFIG_KGDB + kgdb_handle_exception(5, SIGBUS, isr, regs); + return; +#endif + if (address < PAGE_SIZE) printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference (address %016lx)\n", address); else --- linux-2.6.9-rc1/arch/ia64/mm/init.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/ia64/mm/init.c 2004-09-02 20:51:51.000000000 -0700 @@ -429,20 +429,22 @@ virtual_memmap_init (u64 start, u64 end, / sizeof(struct page)); if (map_start < map_end) - memmap_init_zone(map_start, (unsigned long) (map_end - map_start), + memmap_init_zone((unsigned long)(map_end - map_start), args->nid, args->zone, page_to_pfn(map_start)); return 0; } void -memmap_init (struct page *start, unsigned long size, int nid, - unsigned long zone, unsigned long start_pfn) +memmap_init (unsigned long size, int nid, unsigned long zone, + unsigned long start_pfn) { if (!vmem_map) - memmap_init_zone(start, size, nid, zone, start_pfn); + memmap_init_zone(size, nid, zone, start_pfn); else { + struct page *start; struct memmap_init_callback_data args; + start = pfn_to_page(start_pfn); args.start = start; args.end = start + size; args.nid = nid; --- linux-2.6.9-rc1/arch/ia64/oprofile/init.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/ia64/oprofile/init.c 2004-09-03 00:56:30.918460328 -0700 @@ -12,14 +12,21 @@ #include #include -extern void timer_init(struct oprofile_operations ** ops); +extern int perfmon_init(struct oprofile_operations ** ops); +extern void perfmon_exit(void); int __init oprofile_arch_init(struct oprofile_operations ** ops) { +#ifdef CONFIG_PERFMON + return perfmon_init(ops); +#endif return -ENODEV; } void oprofile_arch_exit(void) { +#ifdef CONFIG_PERFMON + perfmon_exit(); +#endif } --- linux-2.6.9-rc1/arch/ia64/oprofile/Kconfig 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/ia64/oprofile/Kconfig 2004-09-03 00:56:30.918460328 -0700 @@ -16,6 +16,10 @@ config OPROFILE whole system, include the kernel, kernel modules, libraries, and applications. + Due to firmware bugs, you may need to use the "nohalt" boot + option if you're using OProfile with the hardware performance + counters. + If unsure, say N. endmenu --- linux-2.6.9-rc1/arch/ia64/oprofile/Makefile 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/ia64/oprofile/Makefile 2004-09-03 00:56:30.918460328 -0700 @@ -7,3 +7,4 @@ DRIVER_OBJS := $(addprefix ../../../driv timer_int.o ) oprofile-y := $(DRIVER_OBJS) init.o +oprofile-$(CONFIG_PERFMON) += perfmon.o --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/ia64/oprofile/perfmon.c 2004-09-03 00:56:30.919460176 -0700 @@ -0,0 +1,105 @@ +/** + * @file perfmon.c + * + * @remark Copyright 2003 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + */ + +#include +#include +#include +#include +#include +#include +#include + +static int allow_ints; + +static int +perfmon_handler(struct task_struct *task, void *buf, pfm_ovfl_arg_t *arg, + struct pt_regs *regs, unsigned long stamp) +{ + int cpu = smp_processor_id(); + unsigned long eip = instruction_pointer(regs); + int event = arg->pmd_eventid; + + arg->ovfl_ctrl.bits.reset_ovfl_pmds = 1; + + /* the owner of the oprofile event buffer may have exited + * without perfmon being shutdown (e.g. SIGSEGV) + */ + if (allow_ints) + oprofile_add_sample(eip, !user_mode(regs), event, cpu); + return 0; +} + + +static int perfmon_start(void) +{ + allow_ints = 1; + return 0; +} + + +static void perfmon_stop(void) +{ + allow_ints = 0; +} + + +#define OPROFILE_FMT_UUID { \ + 0x77, 0x7a, 0x6e, 0x61, 0x20, 0x65, 0x73, 0x69, 0x74, 0x6e, 0x72, 0x20, 0x61, 0x65, 0x0a, 0x6c } + +static pfm_buffer_fmt_t oprofile_fmt = { + .fmt_name = "oprofile_format", + .fmt_uuid = OPROFILE_FMT_UUID, + .fmt_handler = perfmon_handler, +}; + + +static char * get_cpu_type(void) +{ + __u8 family = local_cpu_data->family; + + switch (family) { + case 0x07: + return "ia64/itanium"; + case 0x1f: + return "ia64/itanium2"; + default: + return "ia64/ia64"; + } +} + + +/* all the ops are handled via userspace for IA64 perfmon */ +static struct oprofile_operations perfmon_ops = { + .start = perfmon_start, + .stop = perfmon_stop, +}; + +static int using_perfmon; + +int perfmon_init(struct oprofile_operations ** ops) +{ + int ret = pfm_register_buffer_fmt(&oprofile_fmt); + if (ret) + return -ENODEV; + + perfmon_ops.cpu_type = get_cpu_type(); + *ops = &perfmon_ops; + using_perfmon = 1; + printk(KERN_INFO "oprofile: using perfmon.\n"); + return 0; +} + + +void perfmon_exit(void) +{ + if (!using_perfmon) + return; + + pfm_unregister_buffer_fmt(oprofile_fmt.fmt_uuid); +} --- linux-2.6.9-rc1/arch/ia64/pci/pci.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/ia64/pci/pci.c 2004-09-03 00:56:53.662002784 -0700 @@ -46,6 +46,8 @@ #define DBG(x...) #endif +static int pci_routeirq; + /* * Low-level SAL-based PCI configuration access functions. Note that SAL * calls are already serialized (via sal_lock), so we don't need another @@ -136,13 +138,27 @@ pci_acpi_init (void) printk(KERN_INFO "PCI: Using ACPI for IRQ routing\n"); - /* - * PCI IRQ routing is set up by pci_enable_device(), but we - * also do it here in case there are still broken drivers that - * don't use pci_enable_device(). - */ - while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) - acpi_pci_irq_enable(dev); + if (pci_routeirq) { + /* + * PCI IRQ routing is set up by pci_enable_device(), but we + * also do it here in case there are still broken drivers that + * don't use pci_enable_device(). + */ + printk(KERN_INFO "** Routing PCI interrupts for all devices because \"pci=routeirq\"\n"); + printk(KERN_INFO "** was specified. If this was required to make a driver work,\n"); + printk(KERN_INFO "** please email the output of \"lspci\" to bjorn.helgaas@hp.com\n"); + printk(KERN_INFO "** so I can fix the driver.\n"); + while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) + acpi_pci_irq_enable(dev); + } else { + printk(KERN_INFO "** PCI interrupts are no longer routed automatically. If this\n"); + printk(KERN_INFO "** causes a device to stop working, it is probably because the\n"); + printk(KERN_INFO "** driver failed to call pci_enable_device(). As a temporary\n"); + printk(KERN_INFO "** workaround, the \"pci=routeirq\" argument restores the old\n"); + printk(KERN_INFO "** behavior. If this argument makes the device work again,\n"); + printk(KERN_INFO "** please email the output of \"lspci\" to bjorn.helgaas@hp.com\n"); + printk(KERN_INFO "** so I can fix the driver.\n"); + } return 0; } @@ -438,6 +454,8 @@ pcibios_align_resource (void *data, stru char * __init pcibios_setup (char *str) { + if (!strcmp(str, "routeirq")) + pci_routeirq = 1; return NULL; } --- linux-2.6.9-rc1/arch/ia64/sn/fakeprom/fpmem.c 2004-08-24 00:02:32.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,252 +0,0 @@ -/* - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2000-2003 Silicon Graphics, Inc. All rights reserved. - */ - - - -/* - * FPROM EFI memory descriptor build routines - * - * - Routines to build the EFI memory descriptor map - * - Should also be usable by the SGI prom to convert - * klconfig to efi_memmap - */ - -#include -#include -#include "fpmem.h" - -/* - * args points to a layout in memory like this - * - * 32 bit 32 bit - * - * numnodes numcpus - * - * 16 bit 16 bit 32 bit - * nasid0 cpuconf membankdesc0 - * nasid1 cpuconf membankdesc1 - * . - * . - * . - * . - * . - */ - -sn_memmap_t *sn_memmap ; -sn_config_t *sn_config ; - -/* - * There is a hole in the node 0 address space. Dont put it - * in the memory map - */ -#define NODE0_HOLE_SIZE (20*MB) -#define NODE0_HOLE_END (4UL*GB) - -#define MB (1024*1024) -#define GB (1024*MB) -#define KERNEL_SIZE (4*MB) -#define PROMRESERVED_SIZE (1*MB) - -#ifdef SGI_SN2 -#define PHYS_ADDRESS(_n, _x) (((long)_n<<38) | (long)_x | 0x3000000000UL) -#define MD_BANK_SHFT 34 -#endif - -/* - * For SN, this may not take an arg and gets the numnodes from - * the prom variable or by traversing klcfg or promcfg - */ -int -GetNumNodes(void) -{ - return sn_config->nodes; -} - -int -GetNumCpus(void) -{ - return sn_config->cpus; -} - -/* For SN, get the index th nasid */ - -int -GetNasid(int index) -{ - return sn_memmap[index].nasid ; -} - -node_memmap_t -GetMemBankInfo(int index) -{ - return sn_memmap[index].node_memmap ; -} - -int -IsCpuPresent(int cnode, int cpu) -{ - return sn_memmap[cnode].cpuconfig & (1UL<type = type; - md->phys_addr = paddr; - md->virt_addr = 0; - md->num_pages = numbytes >> 12; - md->attribute = attr; -} - -int -build_efi_memmap(void *md, int mdsize) -{ - int numnodes = GetNumNodes() ; - int cnode,bank ; - int nasid ; - node_memmap_t membank_info ; - int bsize; - int count = 0 ; - long paddr, hole, numbytes; - - - for (cnode=0;cnode 128*1024*1024) { - numbytes -= 1000; - } - - /* - * Check for the node 0 hole. Since banks cant - * span the hole, we only need to check if the end of - * the range is the end of the hole. - */ - if (paddr+numbytes == NODE0_HOLE_END) - numbytes -= NODE0_HOLE_SIZE; - /* - * UGLY hack - we must skip overr the kernel and - * PROM runtime services but we dont exactly where it is. - * So lets just reserve: - * node 0 - * 0-1MB for PAL - * 1-4MB for SAL - * node 1-N - * 0-1 for SAL - */ - if (bank == 0) { - if (cnode == 0) { - hole = 2*1024*1024; - build_mem_desc(md, EFI_PAL_CODE, paddr, hole, EFI_MEMORY_WB|EFI_MEMORY_WB); - numbytes -= hole; - paddr += hole; - count++ ; - md += mdsize; - hole = 1*1024*1024; - build_mem_desc(md, EFI_CONVENTIONAL_MEMORY, paddr, hole, EFI_MEMORY_UC); - numbytes -= hole; - paddr += hole; - count++ ; - md += mdsize; - hole = 1*1024*1024; - build_mem_desc(md, EFI_RUNTIME_SERVICES_DATA, paddr, hole, EFI_MEMORY_WB|EFI_MEMORY_WB); - numbytes -= hole; - paddr += hole; - count++ ; - md += mdsize; - } else { - hole = 2*1024*1024; - build_mem_desc(md, EFI_RUNTIME_SERVICES_DATA, paddr, hole, EFI_MEMORY_WB|EFI_MEMORY_WB); - numbytes -= hole; - paddr += hole; - count++ ; - md += mdsize; - hole = 2*1024*1024; - build_mem_desc(md, EFI_RUNTIME_SERVICES_DATA, paddr, hole, EFI_MEMORY_UC); - numbytes -= hole; - paddr += hole; - count++ ; - md += mdsize; - } - } - build_mem_desc(md, EFI_CONVENTIONAL_MEMORY, paddr, numbytes, EFI_MEMORY_WB|EFI_MEMORY_WB); - - md += mdsize ; - count++ ; - } - } - } - return count ; -} - -void -build_init(unsigned long args) -{ - sn_config = (sn_config_t *) (args); - sn_memmap = (sn_memmap_t *)(args + 8) ; /* SN equiv for this is */ - /* init to klconfig start */ -} --- linux-2.6.9-rc1/arch/ia64/sn/fakeprom/fpmem.h 2004-08-24 00:02:57.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,76 +0,0 @@ -/* - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2000-2003 Silicon Graphics, Inc. All rights reserved. - */ - -#include - -/* - * Structure of the mem config of the node as a SN MI reg - * Medusa supports this reg config. - * - * BankSize nibble to bank size mapping - * - * 1 - 64 MB - * 2 - 128 MB - * 3 - 256 MB - * 4 - 512 MB - * 5 - 1024 MB (1GB) - */ - -#define MBSHIFT 20 - -#ifdef SGI_SN2 -typedef struct node_memmap_s -{ - unsigned int b0size :3, /* 0-2 bank 0 size */ - b0dou :1, /* 3 bank 0 is 2-sided */ - ena0 :1, /* 4 bank 0 enabled */ - r0 :3, /* 5-7 reserved */ - b1size :3, /* 8-10 bank 1 size */ - b1dou :1, /* 11 bank 1 is 2-sided */ - ena1 :1, /* 12 bank 1 enabled */ - r1 :3, /* 13-15 reserved */ - b2size :3, /* 16-18 bank 2 size */ - b2dou :1, /* 19 bank 1 is 2-sided */ - ena2 :1, /* 20 bank 2 enabled */ - r2 :3, /* 21-23 reserved */ - b3size :3, /* 24-26 bank 3 size */ - b3dou :1, /* 27 bank 3 is 2-sided */ - ena3 :1, /* 28 bank 3 enabled */ - r3 :3; /* 29-31 reserved */ -} node_memmap_t ; - -#define SN2_BANK_SIZE_SHIFT (MBSHIFT+6) /* 64 MB */ -#define BankPresent(bsize) (bsize<6) -#define BankSizeBytes(bsize) (BankPresent(bsize) ? 1UL<<((bsize)+SN2_BANK_SIZE_SHIFT) : 0) -#define MD_BANKS_PER_NODE 4 -#define MD_BANKSIZE (1UL << 34) -#endif - -typedef struct sn_memmap_s -{ - short nasid ; - short cpuconfig; - node_memmap_t node_memmap ; -} sn_memmap_t ; - -typedef struct sn_config_s -{ - int cpus; - int nodes; - sn_memmap_t memmap[1]; /* start of array */ -} sn_config_t; - - - -extern void build_init(unsigned long); -extern int build_efi_memmap(void *, int); -extern int GetNumNodes(void); -extern int GetNumCpus(void); -extern int IsCpuPresent(int, int); -extern int GetNasid(int); --- linux-2.6.9-rc1/arch/ia64/sn/fakeprom/fpromasm.S 2004-08-24 00:02:24.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,395 +0,0 @@ -/* - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * (Code copied from or=ther files) - * Copyright (C) 1998-2000 Hewlett-Packard Co - * Copyright (C) 1998-2000 David Mosberger-Tang - * - * Copyright (C) 2000-2003 Silicon Graphics, Inc. All rights reserved. - */ - - - -#define __ASSEMBLY__ 1 -#include -#include -#include -#include - -/* - * This file contains additional set up code that is needed to get going on - * Medusa. This code should disappear once real hw is available. - * - * On entry to this routine, the following register values are assumed: - * - * gr[8] - BSP cpu - * pr[9] - kernel entry address - * pr[10] - cpu number on the node - * - * NOTE: - * This FPROM may be loaded/executed at an address different from the - * address that it was linked at. The FPROM is linked to run on node 0 - * at address 0x100000. If the code in loaded into another node, it - * must be loaded at offset 0x100000 of the node. In addition, the - * FPROM does the following things: - * - determine the base address of the node it is loaded on - * - add the node base to _gp. - * - add the node base to all addresses derived from "movl" - * instructions. (I couldnt get GPREL addressing to work) - * (maybe newer versions of the tools will support this) - * - scan the .got section and add the node base to all - * pointers in this section. - * - add the node base to all physical addresses in the - * SAL/PAL/EFI table built by the C code. (This is done - * in the C code - not here) - * - add the node base to the TLB entries for vmlinux - */ - -#define KERNEL_BASE 0xe000000000000000 -#define BOOT_PARAM_ADDR 0x40000 - - -/* - * ar.k0 gets set to IOPB_PA value, on 460gx chipset it should - * be 0x00000ffffc000000, but on snia we use the (inverse swizzled) - * IOSPEC_BASE value - */ -#ifdef SGI_SN2 -#define IOPB_PA 0xc000000fcc000000 -#endif - -#define RR_RID 8 - - - -// ==================================================================================== - .text - .align 16 - .global _start - .proc _start -_start: - -// Setup psr and rse for system init - mov psr.l = r0;; - srlz.d;; - invala - mov ar.rsc = r0;; - loadrs - ;; - -// Isolate node number we are running on. - mov r6 = ip;; -#ifdef SGI_SN2 - shr r5 = r6,38 // r5 = node number - dep r6 = 0,r6,0,36 // r6 = base memory address of node - -#endif - - -// Set & relocate gp. - movl r1= __gp;; // Add base memory address - or r1 = r1,r6 // Relocate to boot node - -// Lets figure out who we are & put it in the LID register. -#ifdef SGI_SN2 -// On SN2, we (currently) pass the cpu number in r10 at boot - and r25=3,r10;; - movl r16=0x8000008110000400 // Allow IPIs - mov r17=-1;; - st8 [r16]=r17 - movl r16=0x8000008110060580;; // SHUB_ID - ld8 r27=[r16];; - extr.u r27=r27,32,11;; - shl r26=r25,28;; // Align local cpu# to lid.eid - shl r27=r27,16;; // Align NASID to lid.id - or r26=r26,r27;; // build the LID -#else -// The BR_PI_SELF_CPU_NUM register gives us a value of 0-3. -// This identifies the cpu on the node. -// Merge the cpu number with the NASID to generate the LID. - movl r24=0x80000a0001000020;; // BR_PI_SELF_CPU_NUM - ld8 r25=[r24] // Fetch PI_SELF - movl r27=0x80000a0001600000;; // Fetch REVID to get local NASID - ld8 r27=[r27];; - extr.u r27=r27,32,8;; - shl r26=r25,16;; // Align local cpu# to lid.eid - shl r27=r27,24;; // Align NASID to lid.id - or r26=r26,r27;; // build the LID -#endif - mov cr.lid=r26 // Now put in in the LID register - - movl r2=FPSR_DEFAULT;; - mov ar.fpsr=r2 - movl sp = bootstacke-16;; - or sp = sp,r6 // Relocate to boot node - -// Save the NASID that we are loaded on. - movl r2=base_nasid;; // Save base_nasid for C code - or r2 = r2,r6;; // Relocate to boot node - st8 [r2]=r5 // Uncond st8 - same on all cpus - -// Save the kernel entry address. It is passed in r9 on one of -// the cpus. - movl r2=bsp_entry_pc - cmp.ne p6,p0=r9,r0;; - or r2 = r2,r6;; // Relocate to boot node -(p6) st8 [r2]=r9 // Uncond st8 - same on all cpus - - -// The following can ONLY be done by 1 cpu. Lets set a lock - the -// cpu that gets it does the initilization. The rest just spin waiting -// til initilization is complete. - movl r22 = initlock;; - or r22 = r22,r6 // Relocate to boot node - mov r23 = 1;; - xchg8 r23 = [r22],r23;; - cmp.eq p6,p0 = 0,r23 -(p6) br.cond.spnt.few init -1: ld4 r23 = [r22];; - cmp.eq p6,p0 = 1,r23 -(p6) br.cond.sptk 1b - br initx - -// Add base address of node memory to each pointer in the .got section. -init: movl r16 = _GLOBAL_OFFSET_TABLE_;; - or r16 = r16,r6;; // Relocate to boot node -1: ld8 r17 = [r16];; - cmp.eq p6,p7=0,r17 -(p6) br.cond.sptk.few.clr 2f;; - or r17 = r17,r6;; // Relocate to boot node - st8 [r16] = r17,8 - br 1b -2: - mov r23 = 2;; // All done, release the spinning cpus - st4 [r22] = r23 -initx: - -// -// I/O-port space base address: -// - movl r2 = IOPB_PA;; - mov ar.k0 = r2 - - -// Now call main & pass it the current LID value. - alloc r2=ar.pfs,0,0,2,0 - mov r32=r26 - mov r33=r8;; - br.call.sptk.few rp=fmain - -// Initialize Region Registers -// - mov r10 = r0 - mov r2 = (13<<2) - mov r3 = r0;; -1: cmp4.gtu p6,p7 = 7, r3 - dep r10 = r3, r10, 61, 3 - dep r2 = r3, r2, RR_RID, 4;; -(p7) dep r2 = 0, r2, 0, 1;; -(p6) dep r2 = -1, r2, 0, 1;; - mov rr[r10] = r2 - add r3 = 1, r3;; - srlz.d;; - cmp4.gtu p6,p0 = 8, r3 -(p6) br.cond.sptk.few.clr 1b - -// -// Return value indicates if we are the BSP or AP. -// 1 = BSP, 0 = AP - mov cr.tpr=r0;; - cmp.eq p6,p0=r8,r0 -(p6) br.cond.spnt slave - -// -// Go to kernel C startup routines -// Need to do a "rfi" in order set "it" and "ed" bits in the PSR. -// This is the only way to set them. - - movl r28=BOOT_PARAM_ADDR - movl r2=bsp_entry_pc;; - or r28 = r28,r6;; // Relocate to boot node - or r2 = r2,r6;; // Relocate to boot node - ld8 r2=[r2];; - or r2=r2,r6;; - dep r2=0,r2,61,3;; // convert to phys mode - -// -// Turn on address translation, interrupt collection, psr.ed, protection key. -// Interrupts (PSR.i) are still off here. -// - - movl r3 = ( IA64_PSR_BN | \ - IA64_PSR_AC | \ - IA64_PSR_DB | \ - IA64_PSR_DA | \ - IA64_PSR_IC \ - ) - ;; - mov cr.ipsr = r3 - -// -// Go to kernel C startup routines -// Need to do a "rfi" in order set "it" and "ed" bits in the PSR. -// This is the only way to set them. - - mov r8=r28;; - bsw.1 ;; - mov r28=r8;; - bsw.0 ;; - mov cr.iip = r2 - srlz.d;; - rfi;; - - .endp _start - - - -// Slave processors come here to spin til they get an interrupt. Then they launch themselves to -// the place ap_entry points. No initialization is necessary - the kernel makes no -// assumptions about state on this entry. -// Note: should verify that the interrupt we got was really the ap_wakeup -// interrupt but this should not be an issue on medusa -slave: - nop.i 0x8beef // Medusa - put cpu to sleep til interrupt occurs - mov r8=cr.irr0;; // Check for interrupt pending. - cmp.eq p6,p0=r8,r0 -(p6) br.cond.sptk slave;; - - mov r8=cr.ivr;; // Got one. Must read ivr to accept it - srlz.d;; - mov cr.eoi=r0;; // must write eoi to clear - movl r8=ap_entry;; // now jump to kernel entry - or r8 = r8,r6;; // Relocate to boot node - ld8 r9=[r8],8;; - ld8 r1=[r8] - mov b0=r9;; - br b0 - -// Here is the kernel stack used for the fake PROM - .bss - .align 16384 -bootstack: - .skip 16384 -bootstacke: -initlock: - data4 - - - -////////////////////////////////////////////////////////////////////////////////////////////////////////// -// This code emulates the PAL. Only essential interfaces are emulated. - - - .text - .global pal_emulator - .proc pal_emulator -pal_emulator: - mov r8=-1 - - mov r9=256 - ;; - cmp.gtu p6,p7=r9,r28 /* r28 <= 255? */ -(p6) br.cond.sptk.few static - ;; - mov r9=512 - ;; - cmp.gtu p6,p7=r9,r28 -(p6) br.cond.sptk.few stacked - ;; - -static: cmp.eq p6,p7=6,r28 /* PAL_PTCE_INFO */ -(p7) br.cond.sptk.few 1f - movl r8=0 /* status = 0 */ - movl r9=0x100000000 /* tc.base */ - movl r10=0x0000000200000003 /* count[0], count[1] */ - movl r11=0x1000000000002000 /* stride[0], stride[1] */ - ;; - -1: cmp.eq p6,p7=14,r28 /* PAL_FREQ_RATIOS */ -(p7) br.cond.sptk.few 1f - movl r8=0 /* status = 0 */ - movl r9 =0x100000064 /* proc_ratio (1/100) */ - movl r10=0x100000100 /* bus_ratio<<32 (1/256) */ - movl r11=0x10000000a /* itc_ratio<<32 (1/100) */ - ;; - -1: cmp.eq p6,p7=8,r28 /* PAL_VM_SUMMARY */ -(p7) br.cond.sptk.few 1f - movl r8=0 -#ifdef SGI_SN2 - movl r9=0x0203083001151065 - movl r10=0x183f -#endif - movl r11=0 - ;; - -1: cmp.eq p6,p7=19,r28 /* PAL_RSE_INFO */ -(p7) br.cond.sptk.few 1f - movl r8=0 - movl r9=0x60 - movl r10=0x0 - movl r11=0 - ;; - -1: cmp.eq p6,p7=15,r28 /* PAL_PERF_MON_INFO */ -(p7) br.cond.sptk.few 1f - movl r8=0 - movl r9=0x08122004 - movl r10=0x0 - movl r11=0 - mov r2=ar.lc - mov r3=16;; - mov ar.lc=r3 - mov r3=r29;; -5: st8 [r3]=r0,8 - br.cloop.sptk.few 5b;; - mov ar.lc=r2 - mov r3=r29 - movl r2=0x1fff;; /* PMC regs */ - st8 [r3]=r2 - add r3=32,r3 - movl r2=0x3ffff;; /* PMD regs */ - st8 [r3]=r2 - add r3=32,r3 - movl r2=0xf0;; /* cycle regs */ - st8 [r3]=r2 - add r3=32,r3 - movl r2=0x10;; /* retired regs */ - st8 [r3]=r2 - ;; - -1: cmp.eq p6,p7=19,r28 /* PAL_RSE_INFO */ -(p7) br.cond.sptk.few 1f - movl r8=0 /* status = 0 */ - movl r9=96 /* num phys stacked */ - movl r10=0 /* hints */ - movl r11=0 - ;; - -1: cmp.eq p6,p7=1,r28 /* PAL_CACHE_FLUSH */ -(p7) br.cond.sptk.few 1f - mov r9=ar.lc - movl r8=524288 /* flush 512k million cache lines (16MB) */ - ;; - mov ar.lc=r8 - movl r8=0xe000000000000000 - ;; -.loop: fc r8 - add r8=32,r8 - br.cloop.sptk.few .loop - sync.i - ;; - srlz.i - ;; - mov ar.lc=r9 - mov r8=r0 -1: br.cond.sptk.few rp - -stacked: - br.ret.sptk.few rp - - .endp pal_emulator - --- linux-2.6.9-rc1/arch/ia64/sn/fakeprom/fprom.lds 2004-08-24 00:03:30.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,103 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (c) 2002-2003 Silicon Graphics, Inc. All Rights Reserved. - */ - -OUTPUT_FORMAT("elf64-ia64-little") -OUTPUT_ARCH(ia64) -ENTRY(_start) -SECTIONS -{ - v = 0x0000000000000000 ; /* this symbol is here to make debugging with kdb easier... */ - - . = (0x000000000000000 + 0x100000) ; - - _text = .; - .text : AT(ADDR(.text) - 0x0000000000000000 ) - { - *(__ivt_section) - /* these are not really text pages, but the zero page needs to be in a fixed location: */ - *(__special_page_section) - __start_gate_section = .; - *(__gate_section) - __stop_gate_section = .; - *(.text) - } - - /* Global data */ - _data = .; - - .rodata : AT(ADDR(.rodata) - 0x0000000000000000 ) - { *(.rodata) *(.rodata.*) } - .opd : AT(ADDR(.opd) - 0x0000000000000000 ) - { *(.opd) } - .data : AT(ADDR(.data) - 0x0000000000000000 ) - { *(.data) *(.gnu.linkonce.d*) CONSTRUCTORS } - - __gp = ALIGN (8) + 0x200000; - - .got : AT(ADDR(.got) - 0x0000000000000000 ) - { *(.got.plt) *(.got) } - /* We want the small data sections together, so single-instruction offsets - can access them all, and initialized data all before uninitialized, so - we can shorten the on-disk segment size. */ - .sdata : AT(ADDR(.sdata) - 0x0000000000000000 ) - { *(.sdata) } - _edata = .; - _bss = .; - .sbss : AT(ADDR(.sbss) - 0x0000000000000000 ) - { *(.sbss) *(.scommon) } - .bss : AT(ADDR(.bss) - 0x0000000000000000 ) - { *(.bss) *(COMMON) } - . = ALIGN(64 / 8); - _end = .; - - /* Sections to be discarded */ - /DISCARD/ : { - *(.text.exit) - *(.data.exit) - } - - /* Stabs debugging sections. */ - .stab 0 : { *(.stab) } - .stabstr 0 : { *(.stabstr) } - .stab.excl 0 : { *(.stab.excl) } - .stab.exclstr 0 : { *(.stab.exclstr) } - .stab.index 0 : { *(.stab.index) } - .stab.indexstr 0 : { *(.stab.indexstr) } - /* DWARF debug sections. - Symbols in the DWARF debugging sections are relative to the beginning - of the section so we begin them at 0. */ - /* DWARF 1 */ - .debug 0 : { *(.debug) } - .line 0 : { *(.line) } - /* GNU DWARF 1 extensions */ - .debug_srcinfo 0 : { *(.debug_srcinfo) } - .debug_sfnames 0 : { *(.debug_sfnames) } - /* DWARF 1.1 and DWARF 2 */ - .debug_aranges 0 : { *(.debug_aranges) } - .debug_pubnames 0 : { *(.debug_pubnames) } - /* DWARF 2 */ - .debug_info 0 : { *(.debug_info) } - .debug_abbrev 0 : { *(.debug_abbrev) } - .debug_line 0 : { *(.debug_line) } - .debug_frame 0 : { *(.debug_frame) } - .debug_str 0 : { *(.debug_str) } - .debug_loc 0 : { *(.debug_loc) } - .debug_macinfo 0 : { *(.debug_macinfo) } - /* SGI/MIPS DWARF 2 extensions */ - .debug_weaknames 0 : { *(.debug_weaknames) } - .debug_funcnames 0 : { *(.debug_funcnames) } - .debug_typenames 0 : { *(.debug_typenames) } - .debug_varnames 0 : { *(.debug_varnames) } - /* These must appear regardless of . */ - /* Discard them for now since Intel SoftSDV cannot handle them. - .comment 0 : { *(.comment) } - .note 0 : { *(.note) } - */ - /DISCARD/ : { *(.comment) } - /DISCARD/ : { *(.note) } -} --- linux-2.6.9-rc1/arch/ia64/sn/fakeprom/fw-emu.c 2004-08-24 00:03:30.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,775 +0,0 @@ -/* - * PAL & SAL emulation. - * - * Copyright (C) 1998-2000 Hewlett-Packard Co - * Copyright (C) 1998-2000 David Mosberger-Tang - * - * - * Copyright (C) 2000-2003 Silicon Graphics, Inc. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it would be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - * Further, this software is distributed without any warranty that it is - * free of the rightful claim of any third person regarding infringement - * or the like. Any license provided herein, whether implied or - * otherwise, applies only to this software file. Patent licenses, if - * any, provided herein do not apply to combinations of this program with - * other software, or any other product whatsoever. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - * - * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, - * Mountain View, CA 94043, or: - * - * http://www.sgi.com - * - * For further information regarding this notice, see: - * - * http://oss.sgi.com/projects/GenInfo/NoticeExplan - */ -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef SGI_SN2 -#include -#include -#endif -#include -#include "fpmem.h" - -#define RSDP_NAME "RSDP" -#define RSDP_SIG "RSD PTR " /* RSDT Pointer signature */ -#define APIC_SIG "APIC" /* Multiple APIC Description Table */ -#define DSDT_SIG "DSDT" /* Differentiated System Description Table */ -#define FADT_SIG "FACP" /* Fixed ACPI Description Table */ -#define FACS_SIG "FACS" /* Firmware ACPI Control Structure */ -#define PSDT_SIG "PSDT" /* Persistent System Description Table */ -#define RSDT_SIG "RSDT" /* Root System Description Table */ -#define XSDT_SIG "XSDT" /* Extended System Description Table */ -#define SSDT_SIG "SSDT" /* Secondary System Description Table */ -#define SBST_SIG "SBST" /* Smart Battery Specification Table */ -#define SPIC_SIG "SPIC" /* IOSAPIC table */ -#define SRAT_SIG "SRAT" /* SRAT table */ -#define SLIT_SIG "SLIT" /* SLIT table */ -#define BOOT_SIG "BOOT" /* Boot table */ -#define ACPI_SRAT_REVISION 1 -#define ACPI_SLIT_REVISION 1 - -#define OEMID "SGI" -#ifdef SGI_SN2 -#define PRODUCT "SN2" -#define PROXIMITY_DOMAIN(nasid) (((nasid)>>1) & 255) -#endif - -#define MB (1024*1024UL) -#define GB (MB*1024UL) -#define BOOT_PARAM_ADDR 0x40000 -#define MAX(i,j) ((i) > (j) ? (i) : (j)) -#define MIN(i,j) ((i) < (j) ? (i) : (j)) -#define ALIGN8(p) (((long)(p) +7) & ~7) - -#define FPROM_BUG() do {while (1);} while (0) -#define MAX_SN_NODES 128 -#define MAX_LSAPICS 512 -#define MAX_CPUS 512 -#define MAX_CPUS_NODE 4 -#define CPUS_PER_NODE 4 -#define CPUS_PER_FSB 2 -#define CPUS_PER_FSB_MASK (CPUS_PER_FSB-1) - -#define NUM_EFI_DESCS 2 - -#define RSDP_CHECKSUM_LENGTH 20 - -typedef union ia64_nasid_va { - struct { -#if defined(SGI_SN2) - unsigned long off : 36; /* intra-region offset */ - unsigned long attr : 2; - unsigned long nasid : 11; /* NASID */ - unsigned long off2 : 12; /* fill */ - unsigned long reg : 3; /* region number */ -#endif - } f; - unsigned long l; - void *p; -} ia64_nasid_va; - -typedef struct { - unsigned long pc; - unsigned long gp; -} func_ptr_t; - -#define IS_VIRTUAL_MODE() ({struct ia64_psr psr; asm("mov %0=psr" : "=r"(psr)); psr.dt;}) -#define ADDR_OF(p) (IS_VIRTUAL_MODE() ? ((void*)((long)(p)+PAGE_OFFSET)) : ((void*) (p))) - -#if defined(SGI_SN2) -#define __fwtab_pa(n,x) ({ia64_nasid_va _v; _v.l = (long) (x); _v.f.nasid = (x) ? (n) : 0; _v.f.reg = 0; _v.f.attr = 3; _v.l;}) -#endif - -/* - * The following variables are passed thru registersfrom the configuration file and - * are set via the _start function. - */ -long base_nasid; -long num_cpus; -long bsp_entry_pc=0; -long num_nodes; -long app_entry_pc; -int bsp_lid; -func_ptr_t ap_entry; - - -extern void pal_emulator(void); -static efi_runtime_services_t *efi_runtime_p; -static char fw_mem[( sizeof(efi_system_table_t) - + sizeof(efi_runtime_services_t) - + NUM_EFI_DESCS*sizeof(efi_config_table_t) - + sizeof(struct ia64_sal_systab) - + sizeof(struct ia64_sal_desc_entry_point) - + sizeof(struct ia64_sal_desc_ap_wakeup) - + sizeof(struct acpi20_table_rsdp) - + sizeof(struct acpi_table_xsdt) - + sizeof(struct acpi_table_slit) - + MAX_SN_NODES*MAX_SN_NODES+8 - + sizeof(struct acpi_table_madt) - + 16*MAX_CPUS - + (1+8*MAX_SN_NODES)*(sizeof(efi_memory_desc_t)) - + sizeof(struct acpi_table_srat) - + MAX_CPUS*sizeof(struct acpi_table_processor_affinity) - + MAX_SN_NODES*sizeof(struct acpi_table_memory_affinity) - + sizeof(ia64_sal_desc_ptc_t) + - + MAX_SN_NODES*sizeof(ia64_sal_ptc_domain_info_t) + - + MAX_CPUS*sizeof(ia64_sal_ptc_domain_proc_entry_t) + - + 1024)] __attribute__ ((aligned (8))); - - -static efi_status_t -efi_get_time (efi_time_t *tm, efi_time_cap_t *tc) -{ - if (tm) { - memset(tm, 0, sizeof(*tm)); - tm->year = 2000; - tm->month = 2; - tm->day = 13; - tm->hour = 10; - tm->minute = 11; - tm->second = 12; - } - - if (tc) { - tc->resolution = 10; - tc->accuracy = 12; - tc->sets_to_zero = 1; - } - - return EFI_SUCCESS; -} - -static void -efi_reset_system (int reset_type, efi_status_t status, unsigned long data_size, efi_char16_t *data) -{ - while(1); /* Is there a pseudo-op to stop medusa */ -} - -static efi_status_t -efi_success (void) -{ - return EFI_SUCCESS; -} - -static efi_status_t -efi_unimplemented (void) -{ - return EFI_UNSUPPORTED; -} - -#ifdef SGI_SN2 - -#undef cpu_physical_id -#define cpu_physical_id(cpuid) ((ia64_getreg(_IA64_REG_CR_LID) >> 16) & 0xffff) - -void -fprom_send_cpei(void) { - long *p, val; - long physid; - long nasid, slice; - - physid = cpu_physical_id(0); - nasid = cpu_physical_id_to_nasid(physid); - slice = cpu_physical_id_to_slice(physid); - - p = (long*)GLOBAL_MMR_ADDR(nasid, SH_IPI_INT); - val = (1UL<pc = in2; - fp->gp = in3; - } else if (in1 == SAL_VECTOR_OS_MCA || in1 == SAL_VECTOR_OS_INIT) { - } else { - status = -1; - } - ; - } else if (index == SAL_GET_STATE_INFO) { - ; - } else if (index == SAL_GET_STATE_INFO_SIZE) { - r9 = 10000; - ; - } else if (index == SAL_CLEAR_STATE_INFO) { - ; - } else if (index == SAL_MC_RENDEZ) { - ; - } else if (index == SAL_MC_SET_PARAMS) { - ; - } else if (index == SAL_CACHE_FLUSH) { - ; - } else if (index == SAL_CACHE_INIT) { - ; - } else if (index == SAL_UPDATE_PAL) { - ; -#ifdef SGI_SN2 - } else if (index == SN_SAL_LOG_CE) { -#ifdef ajmtestcpei - fprom_send_cpei(); -#else /* ajmtestcpei */ - ; -#endif /* ajmtestcpei */ -#endif - } else if (index == SN_SAL_PROBE) { - r9 = 0UL; - if (in2 == 4) { - r9 = *(unsigned *)in1; - if (r9 == -1) { - status = 1; - } - } else if (in2 == 2) { - r9 = *(unsigned short *)in1; - if (r9 == -1) { - status = 1; - } - } else if (in2 == 1) { - r9 = *(unsigned char *)in1; - if (r9 == -1) { - status = 1; - } - } else if (in2 == 8) { - r9 = *(unsigned long *)in1; - if (r9 == -1) { - status = 1; - } - } else { - status = 2; - } - } else if (index == SN_SAL_GET_KLCONFIG_ADDR) { - r9 = 0x30000; - } else if (index == SN_SAL_CONSOLE_PUTC) { - status = -1; - } else if (index == SN_SAL_CONSOLE_GETC) { - status = -1; - } else if (index == SN_SAL_CONSOLE_POLL) { - status = -1; - } else if (index == SN_SAL_SYSCTL_IOBRICK_MODULE_GET) { - status = -1; - } else { - status = -1; - } - - asm volatile ("" :: "r"(r9), "r"(r10), "r"(r11)); - return ((struct sal_ret_values) {status, r9, r10, r11}); -} - - -/* - * This is here to work around a bug in egcs-1.1.1b that causes the - * compiler to crash (seems like a bug in the new alias analysis code. - */ -void * -id (long addr) -{ - return (void *) addr; -} - - -/* - * Fix the addresses in a function pointer by adding base node address - * to pc & gp. - */ -void -fix_function_pointer(void *fp) -{ - func_ptr_t *_fp; - - _fp = fp; - _fp->pc = __fwtab_pa(base_nasid, _fp->pc); - _fp->gp = __fwtab_pa(base_nasid, _fp->gp); -} - -void -fix_virt_function_pointer(void **fptr) -{ - func_ptr_t *fp; - long *p; - - p = (long*)fptr; - fp = *fptr; - fp->pc = fp->pc | PAGE_OFFSET; - fp->gp = fp->gp | PAGE_OFFSET; - *p |= PAGE_OFFSET; -} - - -int -efi_set_virtual_address_map(void) -{ - efi_runtime_services_t *runtime; - - runtime = efi_runtime_p; - fix_virt_function_pointer((void**)&runtime->get_time); - fix_virt_function_pointer((void**)&runtime->set_time); - fix_virt_function_pointer((void**)&runtime->get_wakeup_time); - fix_virt_function_pointer((void**)&runtime->set_wakeup_time); - fix_virt_function_pointer((void**)&runtime->set_virtual_address_map); - fix_virt_function_pointer((void**)&runtime->get_variable); - fix_virt_function_pointer((void**)&runtime->get_next_variable); - fix_virt_function_pointer((void**)&runtime->set_variable); - fix_virt_function_pointer((void**)&runtime->get_next_high_mono_count); - fix_virt_function_pointer((void**)&runtime->reset_system); - return EFI_SUCCESS; -} - -void -acpi_table_initx(struct acpi_table_header *p, char *sig, int siglen, int revision, int oem_revision) -{ - memcpy(p->signature, sig, siglen); - memcpy(p->oem_id, OEMID, 6); - memcpy(p->oem_table_id, sig, 4); - memcpy(p->oem_table_id+4, PRODUCT, 4); - p->revision = revision; - p->oem_revision = (revision<<16) + oem_revision; - memcpy(p->asl_compiler_id, "FPRM", 4); - p->asl_compiler_revision = 1; -} - -void -acpi_checksum(struct acpi_table_header *p, int length) -{ - u8 *cp, *cpe, checksum; - - p->checksum = 0; - p->length = length; - checksum = 0; - for (cp=(u8*)p, cpe=cp+p->length; cpchecksum = -checksum; -} - -void -acpi_checksum_rsdp20(struct acpi20_table_rsdp *p, int length) -{ - u8 *cp, *cpe, checksum; - - p->checksum = 0; - p->ext_checksum = 0; - p->length = length; - checksum = 0; - for (cp=(u8*)p, cpe=cp+20; cpchecksum = -checksum; - - checksum = 0; - for (cp=(u8*)p, cpe=cp+length; cpext_checksum = -checksum; -} - -int -nasid_present(int nasid) -{ - int cnode; - for (cnode=0; cnode= 1024) - arglen = 1023; - memcpy(cmd_line, args, arglen); - } else { - arglen = 0; - } - cmd_line[arglen] = '\0'; - /* - * For now, just bring up bash. - * If you want to execute all the startup scripts, delete the "init=..". - * You can also edit this line to pass other arguments to the kernel. - * Note: disable kernel text replication. - */ - strcpy(cmd_line, "init=/bin/bash console=ttyS0"); - - memset(efi_systab, 0, sizeof(efi_systab)); - efi_systab->hdr.signature = EFI_SYSTEM_TABLE_SIGNATURE; - efi_systab->hdr.revision = EFI_SYSTEM_TABLE_REVISION; - efi_systab->hdr.headersize = sizeof(efi_systab->hdr); - efi_systab->fw_vendor = __fwtab_pa(base_nasid, vendor); - efi_systab->fw_revision = 1; - efi_systab->runtime = __fwtab_pa(base_nasid, efi_runtime); - efi_systab->nr_tables = 2; - efi_systab->tables = __fwtab_pa(base_nasid, efi_tables); - memcpy(vendor, "S\0i\0l\0i\0c\0o\0n\0-\0G\0r\0a\0p\0h\0i\0c\0s\0\0", 40); - - efi_runtime->hdr.signature = EFI_RUNTIME_SERVICES_SIGNATURE; - efi_runtime->hdr.revision = EFI_RUNTIME_SERVICES_REVISION; - efi_runtime->hdr.headersize = sizeof(efi_runtime->hdr); - efi_runtime->get_time = __fwtab_pa(base_nasid, &efi_get_time); - efi_runtime->set_time = __fwtab_pa(base_nasid, &efi_unimplemented); - efi_runtime->get_wakeup_time = __fwtab_pa(base_nasid, &efi_unimplemented); - efi_runtime->set_wakeup_time = __fwtab_pa(base_nasid, &efi_unimplemented); - efi_runtime->set_virtual_address_map = __fwtab_pa(base_nasid, &efi_set_virtual_address_map); - efi_runtime->get_variable = __fwtab_pa(base_nasid, &efi_unimplemented); - efi_runtime->get_next_variable = __fwtab_pa(base_nasid, &efi_unimplemented); - efi_runtime->set_variable = __fwtab_pa(base_nasid, &efi_unimplemented); - efi_runtime->get_next_high_mono_count = __fwtab_pa(base_nasid, &efi_unimplemented); - efi_runtime->reset_system = __fwtab_pa(base_nasid, &efi_reset_system); - - efi_tables->guid = SAL_SYSTEM_TABLE_GUID; - efi_tables->table = __fwtab_pa(base_nasid, sal_systab); - efi_tables++; - efi_tables->guid = ACPI_20_TABLE_GUID; - efi_tables->table = __fwtab_pa(base_nasid, acpi20_rsdp); - efi_tables++; - - fix_function_pointer(&efi_unimplemented); - fix_function_pointer(&efi_get_time); - fix_function_pointer(&efi_success); - fix_function_pointer(&efi_reset_system); - fix_function_pointer(&efi_set_virtual_address_map); - - - /* fill in the ACPI20 system table - has a pointer to the ACPI table header */ - memcpy(acpi20_rsdp->signature, "RSD PTR ", 8); - acpi20_rsdp->xsdt_address = (u64)__fwtab_pa(base_nasid, acpi_xsdt); - acpi20_rsdp->revision = 2; - acpi_checksum_rsdp20(acpi20_rsdp, sizeof(struct acpi20_table_rsdp)); - - /* Set up the XSDT table - contains pointers to the other ACPI tables */ - acpi_table_initx(&acpi_xsdt->header, XSDT_SIG, 4, 1, 1); - acpi_xsdt->entry[0] = __fwtab_pa(base_nasid, acpi_madt); - acpi_xsdt->entry[1] = __fwtab_pa(base_nasid, acpi_slit); - acpi_xsdt->entry[2] = __fwtab_pa(base_nasid, acpi_srat); - acpi_checksum(&acpi_xsdt->header, sizeof(struct acpi_table_xsdt) + 16); - - /* Set up the APIC table */ - acpi_table_initx(&acpi_madt->header, APIC_SIG, 4, 1, 1); - lsapic20 = (struct acpi_table_lsapic*) (acpi_madt + 1); - for (cnode=0; cnodeheader.type = ACPI_MADT_LSAPIC; - lsapic20->header.length = sizeof(struct acpi_table_lsapic); - lsapic20->acpi_id = cnode*4+cpu; - lsapic20->flags.enabled = 1; -#if defined(SGI_SN2) - lsapic20->eid = nasid&0xffff; - lsapic20->id = (cpu<<4) | (nasid>>16); -#endif - lsapic20 = (struct acpi_table_lsapic*) ((long)lsapic20+sizeof(struct acpi_table_lsapic)); - } - } - acpi_checksum(&acpi_madt->header, (char*)lsapic20 - (char*)acpi_madt); - - /* Set up the SRAT table */ - acpi_table_initx(&acpi_srat->header, SRAT_SIG, 4, ACPI_SRAT_REVISION, 1); - ptr = acpi_srat+1; - for (cnode=0; cnodeheader.type = ACPI_SRAT_MEMORY_AFFINITY; - srat_memory_affinity->header.length = sizeof(struct acpi_table_memory_affinity); - srat_memory_affinity->proximity_domain = PROXIMITY_DOMAIN(nasid); - srat_memory_affinity->base_addr_lo = 0; - srat_memory_affinity->length_lo = 0; -#if defined(SGI_SN2) - srat_memory_affinity->base_addr_hi = (nasid<<6) | (3<<4); - srat_memory_affinity->length_hi = (MD_BANKSIZE*MD_BANKS_PER_NODE)>>32; -#endif - srat_memory_affinity->memory_type = ACPI_ADDRESS_RANGE_MEMORY; - srat_memory_affinity->flags.enabled = 1; - } - - for (cnode=0; cnodeheader.type = ACPI_SRAT_PROCESSOR_AFFINITY; - srat_cpu_affinity->header.length = sizeof(struct acpi_table_processor_affinity); - srat_cpu_affinity->proximity_domain = PROXIMITY_DOMAIN(nasid); - srat_cpu_affinity->flags.enabled = 1; -#if defined(SGI_SN2) - srat_cpu_affinity->lsapic_eid = nasid&0xffff; - srat_cpu_affinity->apic_id = (cpu<<4) | (nasid>>16); -#endif - } - } - acpi_checksum(&acpi_srat->header, (char*)ptr - (char*)acpi_srat); - - - /* Set up the SLIT table */ - acpi_table_initx(&acpi_slit->header, SLIT_SIG, 4, ACPI_SLIT_REVISION, 1); - acpi_slit->localities = PROXIMITY_DOMAIN(max_nasid)+1; - cp=acpi_slit->entry; - memset(cp, 255, acpi_slit->localities*acpi_slit->localities); - - for (i=0; i<=max_nasid; i++) - for (j=0; j<=max_nasid; j++) - if (nasid_present(i) && nasid_present(j)) - *(cp+PROXIMITY_DOMAIN(i)*acpi_slit->localities+PROXIMITY_DOMAIN(j)) = 10 + MIN(254, 5*abs(i-j)); - - cp = acpi_slit->entry + acpi_slit->localities*acpi_slit->localities; - acpi_checksum(&acpi_slit->header, cp - (char*)acpi_slit); - - - /* fill in the SAL system table: */ - memcpy(sal_systab->signature, "SST_", 4); - sal_systab->size = sizeof(*sal_systab); - sal_systab->sal_rev_minor = 1; - sal_systab->sal_rev_major = 0; - sal_systab->entry_count = 3; - sal_systab->sal_b_rev_major = 0x1; /* set the SN SAL rev to */ - sal_systab->sal_b_rev_minor = 0x0; /* 1.00 */ - - strcpy(sal_systab->oem_id, "SGI"); - strcpy(sal_systab->product_id, "SN2"); - - /* fill in an entry point: */ - sal_ed->type = SAL_DESC_ENTRY_POINT; - sal_ed->pal_proc = __fwtab_pa(base_nasid, pal_desc[0]); - sal_ed->sal_proc = __fwtab_pa(base_nasid, sal_desc[0]); - sal_ed->gp = __fwtab_pa(base_nasid, sal_desc[1]); - - /* kludge the PTC domain info */ - sal_ptc->type = SAL_DESC_PTC; - sal_ptc->num_domains = 0; - sal_ptc->domain_info = __fwtab_pa(base_nasid, sal_ptcdi); - cpus_found = 0; - last_domain = -1; - sal_ptcdi--; - for (cnode=0; cnodenum_domains++; - sal_ptcdi++; - sal_ptcdi->proc_count = 0; - sal_ptcdi->proc_list = __fwtab_pa(base_nasid, sal_ptclid); - last_domain = domain; - } - sal_ptcdi->proc_count++; - sal_ptclid->id = nasid; - sal_ptclid->eid = cpu; - sal_ptclid++; - cpus_found++; - } - } - } - - if (cpus_found != num_cpus) - FPROM_BUG(); - - /* Make the AP WAKEUP entry */ - sal_apwake->type = SAL_DESC_AP_WAKEUP; - sal_apwake->mechanism = IA64_SAL_AP_EXTERNAL_INT; - sal_apwake->vector = 18; - - for (checksum=0, cp=(char*)sal_systab; cp < (char *)efi_memmap; ++cp) - checksum += *cp; - sal_systab->checksum = -checksum; - - /* If the checksum is correct, the kernel tries to use the - * table. We dont build enough table & the kernel aborts. - * Note that the PROM hasd thhhe same problem!! - */ - - md = &efi_memmap[0]; - num_memmd = build_efi_memmap((void *)md, mdsize) ; - - bp = (struct ia64_boot_param*) __fwtab_pa(base_nasid, BOOT_PARAM_ADDR); - bp->efi_systab = __fwtab_pa(base_nasid, &fw_mem); - bp->efi_memmap = __fwtab_pa(base_nasid, efi_memmap); - bp->efi_memmap_size = num_memmd*mdsize; - bp->efi_memdesc_size = mdsize; - bp->efi_memdesc_version = 0x101; - bp->command_line = __fwtab_pa(base_nasid, cmd_line); - bp->console_info.num_cols = 80; - bp->console_info.num_rows = 25; - bp->console_info.orig_x = 0; - bp->console_info.orig_y = 24; - bp->fpswa = 0; - - /* - * Now pick the BSP & store it LID value in - * a global variable. Note if BSP is greater than last cpu, - * pick the last cpu. - */ - for (cnode=0; cnode 0) - continue; - return; - } - } -} --- linux-2.6.9-rc1/arch/ia64/sn/fakeprom/klgraph_init.c 2004-08-24 00:01:51.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,205 +0,0 @@ -/* $Id: klgraph_init.c,v 1.1 2002/02/28 17:31:25 marcelo Exp $ - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 1992 - 1997, 2000-2003 Silicon Graphics, Inc. All Rights Reserved. - */ - - -/* - * This is a temporary file that statically initializes the expected - * initial klgraph information that is normally provided by prom. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define SYNERGY_WIDGET ((char *)0xc0000e0000000000) -#define SYNERGY_SWIZZLE ((char *)0xc0000e0000000400) -#define HUBREG ((char *)0xc0000a0001e00000) -#define WIDGET0 ((char *)0xc0000a0000000000) -#define WIDGET4 ((char *)0xc0000a0000000004) - -#define SYNERGY_WIDGET ((char *)0xc0000e0000000000) -#define SYNERGY_SWIZZLE ((char *)0xc0000e0000000400) -#define HUBREG ((char *)0xc0000a0001e00000) -#define WIDGET0 ((char *)0xc0000a0000000000) - -#define convert(a,b,c) temp = (u64 *)a; *temp = b; temp++; *temp = c -void -klgraph_init(void) -{ - - u64 *temp; - /* - * Initialize some hub/xbow registers that allows access to - * Xbridge etc. These are normally done in PROM. - */ - - /* Write IOERR clear to clear the CRAZY bit in the status */ - *(volatile uint64_t *)0xc000000801c001f8 = (uint64_t)0xffffffff; - - /* set widget control register...setting bedrock widget id to a */ - *(volatile uint64_t *)0xc000000801c00020 = (uint64_t)0x801a; - - /* set io outbound widget access...allow all */ - *(volatile uint64_t *)0xc000000801c00110 = (uint64_t)0xff01; - - /* set io inbound widget access...allow all */ - *(volatile uint64_t *)0xc000000801c00118 = (uint64_t)0xff01; - - /* set io crb timeout to max */ - *(volatile uint64_t *)0xc000000801c003c0 = (uint64_t)0xffffff; - *(volatile uint64_t *)0xc000000801c003c0 = (uint64_t)0xffffff; - - /* set local block io permission...allow all */ -// [LB] *(volatile uint64_t *)0xc000000801e04010 = (uint64_t)0xfffffffffffffff; - - /* clear any errors */ - /* clear_ii_error(); medusa should have cleared these */ - - /* set default read response buffers in bridge */ -// [PI] *(volatile u32 *)0xc00000080f000280L = 0xba98; -// [PI] *(volatile u32 *)0xc00000080f000288L = 0xba98; - - /* - * klconfig entries initialization - mankato - */ - convert(0xe000003000030000, 0x00000000beedbabe, 0x0000004800000000); - convert(0xe000003000030010, 0x0003007000000018, 0x800002000f820178); - convert(0xe000003000030020, 0x80000a000f024000, 0x800002000f800000); - convert(0xe000003000030030, 0x0300fafa00012580, 0x00000000040f0000); - convert(0xe000003000030040, 0x0000000000000000, 0x0003097000030070); - convert(0xe000003000030050, 0x00030970000303b0, 0x0003181000033f70); - convert(0xe000003000030060, 0x0003d51000037570, 0x0000000000038330); - convert(0xe000003000030070, 0x0203110100030140, 0x0001000000000101); - convert(0xe000003000030080, 0x0900000000000000, 0x000000004e465e67); - convert(0xe000003000030090, 0x0003097000000000, 0x00030b1000030a40); - convert(0xe0000030000300a0, 0x00030cb000030be0, 0x000315a0000314d0); - convert(0xe0000030000300b0, 0x0003174000031670, 0x0000000000000000); - convert(0xe000003000030100, 0x000000000000001a, 0x3350490000000000); - convert(0xe000003000030110, 0x0000000000000037, 0x0000000000000000); - convert(0xe000003000030140, 0x0002420100030210, 0x0001000000000101); - convert(0xe000003000030150, 0x0100000000000000, 0xffffffffffffffff); - convert(0xe000003000030160, 0x00030d8000000000, 0x0000000000030e50); - convert(0xe0000030000301c0, 0x0000000000000000, 0x0000000000030070); - convert(0xe0000030000301d0, 0x0000000000000025, 0x424f490000000000); - convert(0xe0000030000301e0, 0x000000004b434952, 0x0000000000000000); - convert(0xe000003000030210, 0x00027101000302e0, 0x00010000000e4101); - convert(0xe000003000030220, 0x0200000000000000, 0xffffffffffffffff); - convert(0xe000003000030230, 0x00030f2000000000, 0x0000000000030ff0); - convert(0xe000003000030290, 0x0000000000000000, 0x0000000000030140); - convert(0xe0000030000302a0, 0x0000000000000026, 0x7262490000000000); - convert(0xe0000030000302b0, 0x00000000006b6369, 0x0000000000000000); - convert(0xe0000030000302e0, 0x0002710100000000, 0x00010000000f3101); - convert(0xe0000030000302f0, 0x0500000000000000, 0xffffffffffffffff); - convert(0xe000003000030300, 0x000310c000000000, 0x0003126000031190); - convert(0xe000003000030310, 0x0003140000031330, 0x0000000000000000); - convert(0xe000003000030360, 0x0000000000000000, 0x0000000000030140); - convert(0xe000003000030370, 0x0000000000000029, 0x7262490000000000); - convert(0xe000003000030380, 0x00000000006b6369, 0x0000000000000000); - convert(0xe000003000030970, 0x0000000002010102, 0x0000000000000000); - convert(0xe000003000030980, 0x000000004e465e67, 0xffffffff00000000); - /* convert(0x00000000000309a0, 0x0000000000037570, 0x0000000100000000); */ - convert(0xe0000030000309a0, 0x0000000000037570, 0xffffffff00000000); - convert(0xe0000030000309b0, 0x0000000000030070, 0x0000000000000000); - convert(0xe0000030000309c0, 0x000000000003f420, 0x0000000000000000); - convert(0xe000003000030a40, 0x0000000002010125, 0x0000000000000000); - convert(0xe000003000030a50, 0xffffffffffffffff, 0xffffffff00000000); - convert(0xe000003000030a70, 0x0000000000037b78, 0x0000000000000000); - convert(0xe000003000030b10, 0x0000000002010125, 0x0000000000000000); - convert(0xe000003000030b20, 0xffffffffffffffff, 0xffffffff00000000); - convert(0xe000003000030b40, 0x0000000000037d30, 0x0000000000000001); - convert(0xe000003000030be0, 0x00000000ff010203, 0x0000000000000000); - convert(0xe000003000030bf0, 0xffffffffffffffff, 0xffffffff000000ff); - convert(0xe000003000030c10, 0x0000000000037ee8, 0x0100010000000200); - convert(0xe000003000030cb0, 0x00000000ff310111, 0x0000000000000000); - convert(0xe000003000030cc0, 0xffffffffffffffff, 0x0000000000000000); - convert(0xe000003000030d80, 0x0000000002010104, 0x0000000000000000); - convert(0xe000003000030d90, 0xffffffffffffffff, 0x00000000000000ff); - convert(0xe000003000030db0, 0x0000000000037f18, 0x0000000000000000); - convert(0xe000003000030dc0, 0x0000000000000000, 0x0003007000060000); - convert(0xe000003000030de0, 0x0000000000000000, 0x0003021000050000); - convert(0xe000003000030df0, 0x000302e000050000, 0x0000000000000000); - convert(0xe000003000030e30, 0x0000000000000000, 0x000000000000000a); - convert(0xe000003000030e50, 0x00000000ff00011a, 0x0000000000000000); - convert(0xe000003000030e60, 0xffffffffffffffff, 0x0000000000000000); - convert(0xe000003000030e80, 0x0000000000037fe0, 0x9e6e9e9e9e9e9e9e); - convert(0xe000003000030e90, 0x000000000000bc6e, 0x0000000000000000); - convert(0xe000003000030f20, 0x0000000002010205, 0x00000000d0020000); - convert(0xe000003000030f30, 0xffffffffffffffff, 0x0000000e0000000e); - convert(0xe000003000030f40, 0x000000000000000e, 0x0000000000000000); - convert(0xe000003000030f50, 0x0000000000038010, 0x00000000000007ff); - convert(0xe000003000030f70, 0x0000000000000000, 0x0000000022001077); - convert(0xe000003000030fa0, 0x0000000000000000, 0x000000000003f4a8); - convert(0xe000003000030ff0, 0x0000000000310120, 0x0000000000000000); - convert(0xe000003000031000, 0xffffffffffffffff, 0xffffffff00000002); - convert(0xe000003000031010, 0x000000000000000e, 0x0000000000000000); - convert(0xe000003000031020, 0x0000000000038088, 0x0000000000000000); - convert(0xe0000030000310c0, 0x0000000002010205, 0x00000000d0020000); - convert(0xe0000030000310d0, 0xffffffffffffffff, 0x0000000f0000000f); - convert(0xe0000030000310e0, 0x000000000000000f, 0x0000000000000000); - convert(0xe0000030000310f0, 0x00000000000380b8, 0x00000000000007ff); - convert(0xe000003000031120, 0x0000000022001077, 0x00000000000310a9); - convert(0xe000003000031130, 0x00000000580211c1, 0x000000008009104c); - convert(0xe000003000031140, 0x0000000000000000, 0x000000000003f4c0); - convert(0xe000003000031190, 0x0000000000310120, 0x0000000000000000); - convert(0xe0000030000311a0, 0xffffffffffffffff, 0xffffffff00000003); - convert(0xe0000030000311b0, 0x000000000000000f, 0x0000000000000000); - convert(0xe0000030000311c0, 0x0000000000038130, 0x0000000000000000); - convert(0xe000003000031260, 0x0000000000110106, 0x0000000000000000); - convert(0xe000003000031270, 0xffffffffffffffff, 0xffffffff00000004); - convert(0xe000003000031270, 0xffffffffffffffff, 0xffffffff00000004); - convert(0xe000003000031280, 0x000000000000000f, 0x0000000000000000); - convert(0xe0000030000312a0, 0x00000000ff110013, 0x0000000000000000); - convert(0xe0000030000312b0, 0xffffffffffffffff, 0xffffffff00000000); - convert(0xe0000030000312c0, 0x000000000000000f, 0x0000000000000000); - convert(0xe0000030000312e0, 0x0000000000110012, 0x0000000000000000); - convert(0xe0000030000312f0, 0xffffffffffffffff, 0xffffffff00000000); - convert(0xe000003000031300, 0x000000000000000f, 0x0000000000000000); - convert(0xe000003000031310, 0x0000000000038160, 0x0000000000000000); - convert(0xe000003000031330, 0x00000000ff310122, 0x0000000000000000); - convert(0xe000003000031340, 0xffffffffffffffff, 0xffffffff00000005); - convert(0xe000003000031350, 0x000000000000000f, 0x0000000000000000); - convert(0xe000003000031360, 0x0000000000038190, 0x0000000000000000); - convert(0xe000003000031400, 0x0000000000310121, 0x0000000000000000); - convert(0xe000003000031400, 0x0000000000310121, 0x0000000000000000); - convert(0xe000003000031410, 0xffffffffffffffff, 0xffffffff00000006); - convert(0xe000003000031420, 0x000000000000000f, 0x0000000000000000); - convert(0xe000003000031430, 0x00000000000381c0, 0x0000000000000000); - convert(0xe0000030000314d0, 0x00000000ff010201, 0x0000000000000000); - convert(0xe0000030000314e0, 0xffffffffffffffff, 0xffffffff00000000); - convert(0xe000003000031500, 0x00000000000381f0, 0x000030430000ffff); - convert(0xe000003000031510, 0x000000000000ffff, 0x0000000000000000); - convert(0xe0000030000315a0, 0x00000020ff000201, 0x0000000000000000); - convert(0xe0000030000315b0, 0xffffffffffffffff, 0xffffffff00000001); - convert(0xe0000030000315d0, 0x0000000000038240, 0x00003f3f0000ffff); - convert(0xe0000030000315e0, 0x000000000000ffff, 0x0000000000000000); - convert(0xe000003000031670, 0x00000000ff010201, 0x0000000000000000); - convert(0xe000003000031680, 0xffffffffffffffff, 0x0000000100000002); - convert(0xe0000030000316a0, 0x0000000000038290, 0x000030430000ffff); - convert(0xe0000030000316b0, 0x000000000000ffff, 0x0000000000000000); - convert(0xe000003000031740, 0x00000020ff000201, 0x0000000000000000); - convert(0xe000003000031750, 0xffffffffffffffff, 0x0000000500000003); - convert(0xe000003000031770, 0x00000000000382e0, 0x00003f3f0000ffff); - convert(0xe000003000031780, 0x000000000000ffff, 0x0000000000000000); -} --- linux-2.6.9-rc1/arch/ia64/sn/fakeprom/main.c 2004-08-24 00:02:47.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,109 +0,0 @@ -/* - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2000-2003 Silicon Graphics, Inc. All rights reserved. - */ - - - -#include -#include -#include - -extern void klgraph_init(void); -void bedrock_init(int); -void synergy_init(int, int); -void sys_fw_init (const char *args, int arglen, int bsp); - -volatile int bootmaster=0; /* Used to pick bootmaster */ -volatile int nasidmaster[128]={0}; /* Used to pick node/synergy masters */ -int init_done=0; -extern int bsp_lid; - -#define get_bit(b,p) (((*p)>>(b))&1) - -int -fmain(int lid, int bsp) { - int syn, nasid, cpu; - - /* - * First lets figure out who we are. This is done from the - * LID passed to us. - */ - nasid = (lid>>16)&0xfff; - cpu = (lid>>28)&3; - syn = 0; - - /* - * Now pick a nasid master to initialize Bedrock registers. - */ - if (test_and_set_bit(8, &nasidmaster[nasid]) == 0) { - bedrock_init(nasid); - test_and_set_bit(9, &nasidmaster[nasid]); - } else - while (get_bit(9, &nasidmaster[nasid]) == 0); - - - /* - * Now pick a BSP & finish init. - */ - if (test_and_set_bit(0, &bootmaster) == 0) { - sys_fw_init(0, 0, bsp); - test_and_set_bit(1, &bootmaster); - } else - while (get_bit(1, &bootmaster) == 0); - - return (lid == bsp_lid); -} - - -void -bedrock_init(int nasid) -{ - nasid = nasid; /* to quiet gcc */ -#if 0 - /* - * Undef if you need fprom to generate a 1 node klgraph - * information .. only works for 1 node for nasid 0. - */ - klgraph_init(); -#endif -} - - -void -synergy_init(int nasid, int syn) -{ - long *base; - long off; - - /* - * Enable all FSB flashed interrupts. - * I'd really like defines for this...... - */ - base = (long*)0x80000e0000000000LL; /* base of synergy regs */ - for (off = 0x2a0; off < 0x2e0; off+=8) /* offset for VEC_MASK_{0-3}_A/B */ - *(base+off/8) = -1LL; - - /* - * Set the NASID in the FSB_CONFIG register. - */ - base = (long*)0x80000e0000000450LL; - *base = (long)((nasid<<16)|(syn<<9)); -} - - -/* Why isnt there a bcopy/memcpy in lib64.a */ - -void* -memcpy(void * dest, const void *src, size_t count) -{ - char *s, *se, *d; - - for(d=dest, s=(char*)src, se=s+count; s vmlinux.sym - $(call cmd,cptotop) --- linux-2.6.9-rc1/arch/ia64/sn/fakeprom/make_textsym 2004-08-24 00:01:51.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,174 +0,0 @@ -#!/bin/sh -# -# Build a textsym file for use in the Arium ITP probe. -# -# -# This file is subject to the terms and conditions of the GNU General Public -# License. See the file "COPYING" in the main directory of this archive -# for more details. -# -# Copyright (c) 2001-2003 Silicon Graphics, Inc. All rights reserved. -# - -help() { -cat < []] - If no input file is specified, it defaults to vmlinux. - If no output file name is specified, it defaults to "textsym". -END -exit 1 -} - -err () { - echo "ERROR - $*" >&2 - exit 1 -} - - -OPTS="H" -while getopts "$OPTS" c ; do - case $c in - H) help;; - \?) help;; - esac - -done -shift `expr $OPTIND - 1` - -#OBJDUMP=/usr/bin/ia64-linux-objdump -LINUX=${1:-vmlinux} -TEXTSYM=${2:-${LINUX}.sym} -TMPSYM=${2:-${LINUX}.sym.tmp} -trap "/bin/rm -f $TMPSYM" 0 - -[ -f $VMLINUX ] || help - -$OBJDUMP -t $LINUX | egrep -v '__ks' | sort > $TMPSYM -SN1=`egrep "dig_setup|Synergy_da_indr" $TMPSYM|wc -l` - -# Dataprefix and textprefix correspond to the VGLOBAL_BASE and VPERNODE_BASE. -# Eventually, these values should be: -# dataprefix ffffffff -# textprefix fffffffe -# but right now they're still changing, so make them dynamic. -dataprefix=`awk ' / \.data / { print substr($1, 0, 8) ; exit ; }' $TMPSYM` -textprefix=`awk ' / \.text / { print substr($1, 0, 8) ; exit ; }' $TMPSYM` - -# pipe everything thru sort -echo "TEXTSYM V1.0" -(cat < 0) { - n = n*16 + (index("0123456789abcdef", substr(s,1,1)) - 1) - s = substr(s,2) - } - printf "GLOBAL | %s | DATA | %s | %d\n", $1, $NF, n - } - } - if($NF == "_end") - exit - -} -' $TMPSYM ) | egrep -v " __device| __vendor" | awk -v sn1="$SN1" ' -/GLOBAL/ { - print $0 - if (sn1 != 0) { - /* 32 bits of sn1 physical addrs, */ - print substr($0,1,9) "04" substr($0,20,16) "Phy_" substr($0,36) - } else { - /* 38 bits of sn2 physical addrs, need addr space bits */ - print substr($0,1,9) "3004" substr($0,20,16) "Phy_" substr($0,36) - } - -} ' | sort -k3 - -N=`wc -l $TEXTSYM|awk '{print $1}'` -echo "Generated TEXTSYM file" >&2 -echo " $LINUX --> $TEXTSYM" >&2 -echo " Found $N symbols" >&2 --- linux-2.6.9-rc1/arch/ia64/sn/fakeprom/README 2004-08-24 00:03:49.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,93 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (c) 2002-2003 Silicon Graphics, Inc. All Rights Reserved. - */ - -This directory contains the files required to build -the fake PROM image that is currently being used to -boot IA64 kernels running under the SGI Medusa kernel. - -The FPROM currently provides the following functions: - - - PAL emulation for all PAL calls we've made so far. - - SAL emulation for all SAL calls we've made so far. - - EFI emulation for all EFI calls we've made so far. - - builds the "ia64_bootparam" structure that is - passed to the kernel from SAL. This structure - shows the cpu & memory configurations. - - supports medusa boottime options for changing - the number of cpus present - - supports medusa boottime options for changing - the memory configuration. - - - -At some point, this fake PROM will be replaced by the -real PROM. - - - - -To build a fake PROM, cd to this directory & type: - - make - -This will (or should) build a fake PROM named "fprom". - - - - -Use this fprom image when booting the Medusa simulator. The -control file used to boot Medusa should include the -following lines: - - load fprom - load vmlinux - sr pc 0x100000 - sr g 9
#(currently 0xe000000000520000) - -NOTE: There is a script "runsim" in this directory that can be used to -simplify setting up an environment for running under Medusa. - - - - -The following parameters may be passed to the fake PROM to -control the PAL/SAL/EFI parameters passed to the kernel: - - GR[8] = # of cpus - GR[9] = address of primary entry point into the kernel - GR[20] = memory configuration for node 0 - GR[21] = memory configuration for node 1 - GR[22] = memory configuration for node 2 - GR[23] = memory configuration for node 3 - - -Registers GR[20] - GR[23] contain information to specify the -amount of memory present on nodes 0-3. - - - if nothing is specified (all registers are 0), the configuration - defaults to 8 MB on node 0. - - - a mem config entry for node N is passed in GR[20+N] - - - a mem config entry consists of 8 hex digits. Each digit gives the - amount of physical memory available on the node starting at - 1GB*, where dn is the digit number. The amount of memory - is 8MB*2**. (If = 0, the memory size is 0). - - SN1 doesn't support dimms this small but small memory systems - boot faster on Medusa. - - - -An example helps a lot. The following specifies that node 0 has -physical memory 0 to 8MB and 1GB to 1GB+32MB, and that node 1 has -64MB starting at address 0 of the node which is 8GB. - - gr[20] = 0x21 # 0 to 8MB, 1GB to 1GB+32MB - gr[21] = 0x4 # 8GB to 8GB+64MB - --- linux-2.6.9-rc1/arch/ia64/sn/fakeprom/runsim 2004-08-24 00:02:25.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,387 +0,0 @@ -#!/bin/sh - -# Script for running PROMs and LINUX kernwls on medusa. -# Type "sim -H" for instructions. - -MEDUSA=${MEDUSA:-/home/rickc/official_medusa/medusa} - -# ------------------ err ----------------------- -err() { - echo "ERROR - $1" - exit 1 -} - -# ---------------- help ---------------------- -help() { -cat <] <-p> | <-k> [] - -p Create PROM control file & links - -k Create LINUX control file & links - -c Control file name [Default: cf] - Path to directory that contains the linux or PROM files. - The directory can be any of the following: - (linux simulations) - worktree - worktree/linux - any directory with vmlinux, vmlinux.sym & fprom files - (prom simulations) - worktree - worktree/stand/arcs/IP37prom/dev - any directory with fw.bin & fw.sim files - - Simulations: - sim [-X ] [-o ] [-M] [] - -c Control file name [Default: cf] - -M Pipe output thru fmtmedusa - -o Output filename (copy of all commands/output) [Default: simout] - -X Specifies number of instructions to execute [Default: 0] - (Used only in auto test mode - not described here) - -Examples: - sim -p # create control file (cf) & links for prom simulations - sim -k # create control file (cf) & links for linux simulations - sim -p -c cfprom # create a prom control file (cfprom) only. No links are made. - - sim # run medusa using previously created links & - # control file (cf). -END -exit 1 -} - -# ----------------------- create control file header -------------------- -create_cf_header() { -cat <>$CF -# -# Template for a control file for running linux kernels under medusa. -# You probably want to make mods here but this is a good starting point. -# - -# Preferences -setenv cpu_stepping A -setenv exceptionPrint off -setenv interrupt_messages off -setenv lastPCsize 100000 -setenv low_power_mode on -setenv partialIntelChipSet on -setenv printIntelMessages off -setenv prom_write_action halt -setenv prom_write_messages on -setenv step_quantum 100 -setenv swizzling on -setenv tsconsole on -setenv uart_echo on -symbols on - -# IDE disk params -setenv diskCylinders 611 -setenv bootDrive C -setenv diskHeads 16 -setenv diskPath idedisk -setenv diskPresent 1 -setenv diskSpt 63 - -# Hardware config -setenv coherency_type nasid -setenv cpu_cache_type default -setenv synergy_cache_type syn_cac_64m_8w -setenv l4_uc_snoop off - -# Numalink config -setenv route_enable on -setenv network_type router # Select [xbar|router] -setenv network_warning 0xff - -END -} - - -# ------------------ create control file entries for linux simulations ------------- -create_cf_linux() { -cat <>$CF -# Kernel specific options -setenv calias_size 0 -setenv mca_on_memory_failure off -setenv LOADPC 0x00100000 # FPROM load address/entry point (8 digits!) -setenv symbol_table vmlinux.sym -load fprom -load vmlinux - -# Useful breakpoints to always have set. Add more if desired. -break 0xe000000000505e00 all # dispatch_to_fault_handler -break panic all # stop on panic -break die_if_kernel all # may as well stop - -END -} - -# ------------------ create control file entries for prom simulations --------------- -create_cf_prom() { - SYM2="" - ADDR="0x80000000ff800000" - [ "$EMBEDDED_LINUX" != "0" ] || SYM2="setenv symbol_table2 vmlinux.sym" - [ "$SIZE" = "8MB" ] || ADDR="0x80000000ffc00000" - cat <>$CF -# PROM specific options -setenv mca_on_memory_failure on -setenv LOADPC 0x80000000ffffffb0 -setenv promFile fw.bin -setenv promAddr $ADDR -setenv symbol_table fw.sym -$SYM2 - -# Useful breakpoints to always have set. Add more if desired. -break ivt_gexx all -break ivt_brk all -break PROM_Panic_Spin all -break PROM_Panic all -break PROM_C_Panic all -break fled_die all -break ResetNow all -break zzzbkpt all - -END -} - - -# ------------------ create control file entries for memory configuration ------------- -create_cf_memory() { -cat <>$CF -# CPU/Memory map format: -# setenv nodeN_memory_config 0xBSBSBSBS -# B=banksize (0=unused, 1=64M, 2=128M, .., 5-1G, c=8M, d=16M, e=32M) -# S=bank enable (0=both disable, 3=both enable, 2=bank1 enable, 1=bank0 enable) -# rightmost digits are for bank 0, the lowest address. -# setenv nodeN_nasid -# specifies the NASID for the node. This is used ONLY if booting the kernel. -# On PROM configurations, set to 0 - PROM will change it later. -# setenv nodeN_cpu_config -# Set bit number N to 1 to enable cpu N. Ex., a value of 5 enables cpu 0 & 2. -# -# Repeat the above 3 commands for each node. -# -# For kernel, default to 32MB. Although this is not a valid hardware configuration, -# it runs faster on medusa. For PROM, 64MB is smallest allowed value. - -setenv node0_cpu_config 0x1 # Enable only cpu 0 on the node -END - -if [ $LINUX -eq 1 ] ; then -cat <>$CF -setenv node0_nasid 0 # cnode 0 has NASID 0 -setenv node0_memory_config 0xe1 # 32MB -END -else -cat <>$CF -setenv node0_memory_config 0x31 # 256MB -END -fi -} - -# -------------------- set links to linux files ------------------------- -set_linux_links() { - if [ -d $D/linux/arch ] ; then - D=$D/linux - elif [ -d $D/arch -o -e vmlinux.sym -o -e $D/vmlinux ] ; then - D=$D - else - err "cant determine directory for linux binaries" - fi - rm -rf vmlinux vmlinux.sym fprom - ln -s $D/vmlinux vmlinux - if [ -f $D/vmlinux.sym ] ; then - ln -s $D/vmlinux.sym vmlinux.sym - elif [ -f $D/System.map ] ; then - ln -s $D/System.map vmlinux.sym - fi - if [ -d $D/arch ] ; then - ln -s $D/arch/ia64/sn/fprom/fprom fprom - else - ln -s $D/fprom fprom - fi - echo " .. Created links to linux files" -} - -# -------------------- set links to prom files ------------------------- -set_prom_links() { - if [ -d $D/stand ] ; then - D=$D/stand/arcs/IP37prom/dev - elif [ -d $D/sal ] ; then - D=$D - else - err "cant determine directory for PROM binaries" - fi - SETUP="/tmp/tmp.$$" - rm -r -f $SETUP - sed 's/export/setenv/' < $D/../../../../.setup | sed 's/=/ /' >$SETUP - egrep -q '^ *setenv *PROMSIZE *8MB|^ *export' $SETUP - if [ $? -eq 0 ] ; then - SIZE="8MB" - else - SIZE="4MB" - fi - grep -q '^ *setenv *LAUNCH_VMLINUX' $SETUP - EMBEDDED_LINUX=$? - PRODUCT=`grep '^ *setenv *PRODUCT' $SETUP | cut -d" " -f3` - rm -f fw.bin fw.map fw.sym vmlinux vmlinux.sym fprom $SETUP - SDIR="${PRODUCT}${SIZE}.O" - BIN="${PRODUCT}ip37prom${SIZE}" - ln -s $D/$SDIR/$BIN.bin fw.bin - ln -s $D/$SDIR/$BIN.map fw.map - ln -s $D/$SDIR/$BIN.sym fw.sym - echo " .. Created links to $SIZE prom files" - if [ $EMBEDDED_LINUX -eq 0 ] ; then - ln -s $D/linux/vmlinux vmlinux - ln -s $D/linux/vmlinux.sym vmlinux.sym - if [ -d linux/arch ] ; then - ln -s $D/linux/arch/ia64/sn/fprom/fprom fprom - else - ln -s $D/linux/fprom fprom - fi - echo " .. Created links to embedded linux files in prom tree" - fi -} - -# --------------- start of shell script -------------------------------- -OUT="simout" -FMTMED=0 -STEPCNT=0 -PROM=0 -LINUX=0 -NCF="cf" -while getopts "HMX:c:o:pk" c ; do - case ${c} in - H) help;; - M) FMTMED=1;; - X) STEPCNT=${OPTARG};; - c) NCF=${OPTARG};; - k) PROM=0;LINUX=1;; - p) PROM=1;LINUX=0;; - o) OUT=${OPTARG};; - \?) exit 1;; - esac -done -shift `expr ${OPTIND} - 1` - -# Check if command is for creating control file and/or links to images. -if [ $PROM -eq 1 -o $LINUX -eq 1 ] ; then - CF=$NCF - [ ! -f $CF ] || err "wont overwrite an existing control file ($CF)" - if [ $# -gt 0 ] ; then - D=$1 - [ -d $D ] || err "cannot find directory $D" - [ $PROM -eq 0 ] || set_prom_links - [ $LINUX -eq 0 ] || set_linux_links - fi - create_cf_header - [ $PROM -eq 0 ] || create_cf_prom - [ $LINUX -eq 0 ] || create_cf_linux - [ ! -f ../idedisk ] || ln -s ../idedisk . - create_cf_memory - echo " .. Basic control file created (in $CF). You might want to edit" - echo " this file (at least, look at it)." - exit 0 -fi - -# Verify that the control file exists -CF=${1:-$NCF} -[ -f $CF ] || err "No control file exists. For help, type: $0 -H" - -# Build the .cf files from the user control file. The .cf file is -# identical except that the actual start & load addresses are inserted -# into the file. In addition, the FPROM commands for configuring memory -# and LIDs are generated. - -rm -f .cf .cf1 .cf2 -awk ' -function strtonum(n) { - if (substr(n,1,2) != "0x") - return int(n) - n = substr(n,3) - r=0 - while (length(n) > 0) { - r = r*16+(index("0123456789abcdef", substr(n,1,1))-1) - n = substr(n,2) - } - return r - } -/^#/ {next} -/^$/ {next} -/^setenv *LOADPC/ {loadpc = $3; next} -/^setenv *node.._cpu_config/ {n=int(substr($2,5,2)); cpuconf[n] = strtonum($3); print; next} -/^setenv *node.._memory_config/ {n=int(substr($2,5,2)); memconf[n] = strtonum($3); print; next} -/^setenv *node.._nasid/ {n=int(substr($2,5,2)); nasid[n] = strtonum($3); print; next} -/^setenv *node._cpu_config/ {n=int(substr($2,5,1)); cpuconf[n] = strtonum($3); print; next} -/^setenv *node._memory_config/ {n=int(substr($2,5,1)); memconf[n] = strtonum($3); print; next} -/^setenv *node._nasid/ {n=int(substr($2,5,1)); nasid[n] = strtonum($3); print; next} - {print} -END { - # Generate the memmap info that starts at the beginning of - # the node the kernel was loaded on. - loadnasid = nasid[0] - cnode = 0 - for (i=0; i<128; i++) { - if (memconf[i] != "") { - printf "sm 0x%x%08x 0x%x%04x%04x\n", - 2*loadnasid, 8*cnodes+8, memconf[i], cpuconf[i], nasid[i] - cnodes++ - cpus += substr("0112122312232334", cpuconf[i]+1,1) - } - } - printf "sm 0x%x00000000 0x%x%08x\n", 2*loadnasid, cnodes, cpus - printf "setenv number_of_nodes %d\n", cnodes - - # Now set the starting PC for each cpu. - cnode = 0 - lowcpu=-1 - for (i=0; i<128; i++) { - if (memconf[i] != "") { - printf "setnode %d\n", cnode - conf = cpuconf[i] - for (j=0; j<4; j++) { - if (conf != int(conf/2)*2) { - printf "setcpu %d\n", j - if (length(loadpc) == 18) - printf "sr pc %s\n", loadpc - else - printf "sr pc 0x%x%s\n", 2*loadnasid, substr(loadpc,3) - if (lowcpu == -1) - lowcpu = j - } - conf = int(conf/2) - } - cnode++ - } - } - printf "setnode 0\n" - printf "setcpu %d\n", lowcpu - } -' <$CF >.cf - -# Now build the .cf1 & .cf2 control files. -CF2_LINES="^sm |^break |^run |^si |^quit |^symbols " -egrep "$CF2_LINES" .cf >.cf2 -egrep -v "$CF2_LINES" .cf >.cf1 -if [ $STEPCNT -ne 0 ] ; then - echo "s $STEPCNT" >>.cf2 - echo "lastpc 1000" >>.cf2 - echo "q" >>.cf2 -fi -if [ -f vmlinux.sym ] ; then - awk '/ _start$/ {print "sr g 9 0x" $3}' < vmlinux.sym >> .cf2 -fi -echo "script-on $OUT" >>.cf2 - -# Now start medusa.... -if [ $FMTMED -ne 0 ] ; then - $MEDUSA -system mpsn1 -c .cf1 -i .cf2 | fmtmedusa -elif [ $STEPCNT -eq 0 ] ; then - $MEDUSA -system mpsn1 -c .cf1 -i .cf2 -else - $MEDUSA -system mpsn1 -c .cf1 -i .cf2 2>&1 -fi --- linux-2.6.9-rc1/arch/ia64/sn/io/machvec/pci_bus_cvlink.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/ia64/sn/io/machvec/pci_bus_cvlink.c 2004-09-02 20:51:51.000000000 -0700 @@ -290,6 +290,7 @@ sn_pci_fixup_slot(struct pci_dev *dev) addr |= __IA64_UNCACHED_OFFSET; dev->resource[idx].start = addr; dev->resource[idx].end = addr + size; + dev->resource[idx].parent = &ioport_resource; } if (dev->resource[idx].flags & IORESOURCE_IO) @@ -322,6 +323,7 @@ sn_pci_fixup_slot(struct pci_dev *dev) addr |= __IA64_UNCACHED_OFFSET; dev->resource[idx].start = addr; dev->resource[idx].end = addr + size; + dev->resource[idx].parent = &iomem_resource; } if (dev->resource[idx].flags & IORESOURCE_MEM) @@ -351,6 +353,7 @@ sn_pci_fixup_slot(struct pci_dev *dev) addr |= __IA64_UNCACHED_OFFSET; dev->resource[PCI_ROM_RESOURCE].start = addr; dev->resource[PCI_ROM_RESOURCE].end = addr + size; + dev->resource[idx].parent = &iomem_resource; if (dev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_MEM) cmd |= PCI_COMMAND_MEMORY; } --- linux-2.6.9-rc1/arch/ia64/sn/kernel/bte.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/arch/ia64/sn/kernel/bte.c 2004-09-02 20:51:51.000000000 -0700 @@ -421,9 +421,10 @@ bte_init_node(nodepda_t * mynodepda, cno mynodepda->bte_recovery_timer.data = (unsigned long) mynodepda; for (i = 0; i < BTES_PER_NODE; i++) { - (u64) mynodepda->bte_if[i].bte_base_addr = - REMOTE_HUB_ADDR(cnodeid_to_nasid(cnode), - (i == 0 ? IIO_IBLS0 : IIO_IBLS1)); + /* Which link status register should we use? */ + unsigned long link_status = (i == 0 ? IIO_IBLS0 : IIO_IBLS1); + mynodepda->bte_if[i].bte_base_addr = (u64 *) + REMOTE_HUB_ADDR(cnodeid_to_nasid(cnode), link_status); /* * Initialize the notification and spinlock --- linux-2.6.9-rc1/arch/ia64/sn/kernel/sn2/prominfo_proc.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/ia64/sn/kernel/sn2/prominfo_proc.c 2004-09-03 00:56:30.920460024 -0700 @@ -3,10 +3,10 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1999,2001-2003 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (C) 1999,2001-2004 Silicon Graphics, Inc. All Rights Reserved. * * Module to export the system's Firmware Interface Tables, including - * PROM revision numbers, in /proc + * PROM revision numbers and banners, in /proc */ #include #include @@ -45,9 +45,18 @@ MODULE_LICENSE("GPL"); # define TRACE() #endif +/* Architected IA64 firmware space */ +#define FW_BASE 0x00000000FF000000 +#define FW_TOP 0x0000000100000000 + /* Sub-regions determined by bits in Node Offset */ #define LB_PROM_SPACE 0x0000000700000000ul /* Local LB PROM */ +/* Offset of PROM banner pointers in SAL A and SAL B */ +#define SAL_A_BANNER_OFFSET (1 * 16) +#define SAL_B_BANNER_OFFSET (3 * 16) + + #define FIT_SIGNATURE 0x2020205f5449465ful /* Standard Intel FIT entry types */ #define FIT_ENTRY_FIT_HEADER 0x00 /* FIT header entry */ @@ -125,6 +134,26 @@ fit_type_name(unsigned char type) return "Unknown type"; } +static unsigned long +convert_fw_addr(nasid_t nasid, unsigned long addr) +{ + /* snag just the node-relative offset */ + addr &= ~0ul >> (63-35); + /* the pointer to SAL A is relative to IA-64 compatibility + * space. However, the PROM is mapped at a different offset + * in MMR space (both local and global) + */ + addr += 0x700000000; + return GLOBAL_MMR_ADDR(nasid, addr); +} + +static int +valid_fw_addr(unsigned long addr) +{ + addr &= ~(1ul << 63); /* Clear cached/uncached bit */ + return (addr >= FW_BASE && addr < FW_TOP); +} + /* These two routines read the FIT table directly from the FLASH PROM * on a specific node. The PROM can only be accessed using aligned 64 * bit reads, so we do that and then shift and mask the result to get @@ -194,6 +223,8 @@ dump_version(char *page, unsigned long * int nentries; int fentry; unsigned long qw; + int len; + nasid_t nasid = NASID_GET(fit); TRACE(); @@ -203,10 +234,31 @@ dump_version(char *page, unsigned long * for (fentry = 0; fentry < nentries; fentry++) { qw = readq(fit + 2 * fentry + 1); if (FIT_TYPE(qw) == FIT_ENTRY_SAL_A) - return sprintf(page, "%x.%02x\n", - FIT_MAJOR(qw), FIT_MINOR(qw)); + break; } - return 0; + if (fentry >= nentries) + return 0; + + len = sprintf(page, "%x.%02x\n", FIT_MAJOR(qw), FIT_MINOR(qw)); + page += len; + + qw = readq(fit + 2 * fentry); /* Address of SAL A */ + DPRINTK("SAL A at %p\n", (void *)qw); + if (!valid_fw_addr(qw)) + return len; + + qw += SAL_A_BANNER_OFFSET; + qw = convert_fw_addr(nasid, qw); + DPRINTK("Banner ptr at %p\n", (void *)qw); + + qw = readq(qw); /* Address of banner */ + if (!valid_fw_addr(qw)) + return len; + qw = convert_fw_addr(nasid, qw); + DPRINTK("Banner at %p\n", (void *)qw); + + len += snprintf(page, PAGE_SIZE-len, "%s\n", (char *)qw); + return len; } /* same as in proc_misc.c */ @@ -278,14 +330,10 @@ lookup_fit(int nasid) DPRINTK("pointer to fit at %p\n", (void *)fitp); fit_paddr = readq(fitp); DPRINTK("fit pointer contains %lx\n", fit_paddr); - /* snag just the node-relative offset */ - fit_paddr &= ~0ul >> (63-35); - /* the pointer to the FIT is relative to IA-64 compatibility - * space. However, the PROM is mapped at a different offset - * in MMR space (both local and global) - */ - fit_paddr += 0x700000000; - fit_vaddr = (void *)GLOBAL_MMR_ADDR(nasid, fit_paddr); + + BUG_ON(!valid_fw_addr(fit_paddr)); + fit_vaddr = (void *)convert_fw_addr(nasid, fit_paddr); + DPRINTK("fit at %p\n", (void *)fit_vaddr); return fit_vaddr; } --- linux-2.6.9-rc1/arch/ia64/sn/kernel/sn2/sn_proc_fs.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/ia64/sn/kernel/sn2/sn_proc_fs.c 2004-09-02 20:51:51.000000000 -0700 @@ -3,7 +3,7 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 2000-2003 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2000-2004 Silicon Graphics, Inc. All rights reserved. */ #include #include @@ -118,11 +118,33 @@ register_sn_force_interrupt(void) { } } +static int coherence_id_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) { + return sprintf(page, "%d\n", cpuid_to_coherence_id(smp_processor_id())); +} + +void +register_sn_coherence_id(void) { + struct proc_dir_entry *entry; + + if (!sgi_proc_dir) { + sgi_proc_dir = proc_mkdir("sgi_sn", 0); + } + entry = create_proc_entry("coherence_id", 0444, sgi_proc_dir); + if (entry) { + entry->nlink = 1; + entry->data = 0; + entry->read_proc = coherence_id_read_proc; + entry->write_proc = NULL; + } +} + void register_sn_procfs(void) { register_sn_partition_id(); register_sn_serial_numbers(); register_sn_force_interrupt(); + register_sn_coherence_id(); } #endif /* CONFIG_PROC_FS */ --- linux-2.6.9-rc1/arch/ia64/sn/kernel/sn2/timer.c 2004-08-24 00:02:57.000000000 -0700 +++ 25/arch/ia64/sn/kernel/sn2/timer.c 2004-09-03 00:56:30.920460024 -0700 @@ -20,57 +20,16 @@ extern unsigned long sn_rtc_cycles_per_second; -static volatile unsigned long last_wall_rtc; -static unsigned long rtc_offset; /* updated only when xtime write-lock is held! */ -static long rtc_nsecs_per_cycle; -static long rtc_per_timer_tick; - -static unsigned long -getoffset(void) -{ - return rtc_offset + (GET_RTC_COUNTER() - last_wall_rtc)*rtc_nsecs_per_cycle; -} - - -static void -update(long delta_nsec) -{ - unsigned long rtc_counter = GET_RTC_COUNTER(); - unsigned long offset = rtc_offset + (rtc_counter - last_wall_rtc)*rtc_nsecs_per_cycle; - - /* Be careful about signed/unsigned comparisons here: */ - if (delta_nsec < 0 || (unsigned long) delta_nsec < offset) - rtc_offset = offset - delta_nsec; - else - rtc_offset = 0; - last_wall_rtc = rtc_counter; -} - - -static void -reset(void) -{ - rtc_offset = 0; - last_wall_rtc = GET_RTC_COUNTER(); -} - - -static struct time_interpolator sn2_interpolator = { - .get_offset = getoffset, - .update = update, - .reset = reset -}; +static struct time_interpolator sn2_interpolator; void __init sn_timer_init(void) { sn2_interpolator.frequency = sn_rtc_cycles_per_second; sn2_interpolator.drift = -1; /* unknown */ + sn2_interpolator.shift = 10; /* RTC is 54 bits maximum shift is 10 */ + sn2_interpolator.addr = RTC_COUNTER_ADDR; + sn2_interpolator.source = TIME_SOURCE_MMIO64; register_time_interpolator(&sn2_interpolator); - - rtc_per_timer_tick = sn_rtc_cycles_per_second / HZ; - rtc_nsecs_per_cycle = 1000000000 / sn_rtc_cycles_per_second; - - last_wall_rtc = GET_RTC_COUNTER(); } --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/boot/compressed/boot.h 2004-09-03 00:57:17.230419840 -0700 @@ -0,0 +1,59 @@ +/* + * 1. load vmlinuz + * + * CONFIG_MEMORY_START +-----------------------+ + * | vmlinuz | + * +-----------------------+ + * 2. decompressed + * + * CONFIG_MEMORY_START +-----------------------+ + * | vmlinuz | + * +-----------------------+ + * | | + * BOOT_RELOC_ADDR +-----------------------+ + * | | + * KERNEL_DECOMPRESS_ADDR +-----------------------+ + * | vmlinux | + * +-----------------------+ + * + * 3. relocate copy & jump code + * + * CONFIG_MEMORY_START +-----------------------+ + * | vmlinuz | + * +-----------------------+ + * | | + * BOOT_RELOC_ADDR +-----------------------+ + * | boot(copy&jump) | + * KERNEL_DECOMPRESS_ADDR +-----------------------+ + * | vmlinux | + * +-----------------------+ + * + * 4. relocate decompressed kernel + * + * CONFIG_MEMORY_START +-----------------------+ + * | vmlinux | + * +-----------------------+ + * | | + * BOOT_RELOC_ADDR +-----------------------+ + * | boot(copy&jump) | + * KERNEL_DECOMPRESS_ADDR +-----------------------+ + * | | + * +-----------------------+ + * + */ +#ifdef __ASSEMBLY__ +#define __val(x) x +#else +#define __val(x) (x) +#endif + +#define DECOMPRESS_OFFSET_BASE __val(0x00900000) +#define BOOT_RELOC_SIZE __val(0x00001000) + +#define KERNEL_EXEC_ADDR __val(CONFIG_MEMORY_START) +#define KERNEL_DECOMPRESS_ADDR __val(CONFIG_MEMORY_START + \ + DECOMPRESS_OFFSET_BASE + BOOT_RELOC_SIZE) +#define KERNEL_ENTRY __val(CONFIG_MEMORY_START + 0x1000) + +#define BOOT_EXEC_ADDR __val(CONFIG_MEMORY_START) +#define BOOT_RELOC_ADDR __val(CONFIG_MEMORY_START + DECOMPRESS_OFFSET_BASE) --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/boot/compressed/head.S 2004-09-03 00:57:19.057142136 -0700 @@ -0,0 +1,115 @@ +/* + * linux/arch/m32r/boot/compressed/head.S + * + * Copyright (c) 2001-2003 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto, Takeo Takahashi + * Copyright (c) 2004 Hirokazu Takata + */ + + .text +#include +#include +#include +#include +#include + + .global startup + __ALIGN +startup: + ldi r0, #0x0000 /* SPI, disable EI */ + mvtc r0, psw + +/* + * Clear BSS first so that there are no surprises... + */ +#ifdef CONFIG_ISA_DUAL_ISSUE + + LDIMM (r2, __bss_start) + LDIMM (r3, _end) + sub r3, r2 ; BSS size in bytes + ; R4 = BSS size in longwords (rounded down) + mv r4, r3 || ldi r1, #0 + srli r4, #4 || addi r2, #-4 + beqz r4, .Lendloop1 +.Lloop1: +#ifndef CONFIG_CHIP_M32310 + ; Touch memory for the no-write-allocating cache. + ld r0, @(4,r2) +#endif + st r1, @+r2 || addi r4, #-1 + st r1, @+r2 + st r1, @+r2 + st r1, @+r2 || cmpeq r1, r4 ; R4 = 0? + bnc .Lloop1 +.Lendloop1: + and3 r4, r3, #15 + addi r2, #4 + beqz r4, .Lendloop2 +.Lloop2: + stb r1, @r2 || addi r4, #-1 + addi r2, #1 + bnez r4, .Lloop2 +.Lendloop2: + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + + LDIMM (r2, __bss_start) + LDIMM (r3, _end) + sub r3, r2 ; BSS size in bytes + mv r4, r3 + srli r4, #2 ; R4 = BSS size in longwords (rounded down) + ldi r1, #0 ; clear R1 for longwords store + addi r2, #-4 ; account for pre-inc store + beqz r4, .Lendloop1 ; any more to go? +.Lloop1: + st r1, @+r2 ; yep, zero out another longword + addi r4, #-1 ; decrement count + bnez r4, .Lloop1 ; go do some more +.Lendloop1: + and3 r4, r3, #3 ; get no. of remaining BSS bytes to clear + addi r2, #4 ; account for pre-inc store + beqz r4, .Lendloop2 ; any more to go? +.Lloop2: + stb r1, @r2 ; yep, zero out another byte + addi r2, #1 ; bump address + addi r4, #-1 ; decrement count + bnez r4, .Lloop2 ; go do some more +.Lendloop2: + +#endif /* not CONFIG_ISA_DUAL_ISSUE */ + + seth r0, #shigh(stack_start) + ld sp, @(r0, low(stack_start)) /* set stack point */ + +/* + * decompress the kernel + */ + bl decompress_kernel + +#if defined(CONFIG_CHIP_M32700) + /* Cache flush */ + ldi r0, -1 + ldi r1, 0xd0 ; invalidate i-cache, copy back d-cache + stb r1, @r0 +#else +#error "put your cache flush function, please" +#endif + seth r0, #high(CONFIG_MEMORY_START) + or3 r0, r0, #0x2000 + jmp r0 + + .balign 512 +fake_headers_as_bzImage: + .short 0 + .ascii "HdrS" + .short 0x0202 + .short 0 + .short 0 + .byte 0x00, 0x10 + .short 0 + .byte 0 + .byte 1 + .byte 0x00, 0x80 + .long 0 + .long 0 + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/boot/compressed/install.sh 2004-09-03 00:57:17.232419536 -0700 @@ -0,0 +1,57 @@ +#!/bin/sh +# +# arch/sh/boot/install.sh +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1995 by Linus Torvalds +# +# Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin +# Adapted from code in arch/i386/boot/install.sh by Russell King +# Adapted from code in arch/arm/boot/install.sh by Stuart Menefy +# Adapted from code in arch/sh/boot/install.sh by Takeo Takahashi +# +# "make install" script for sh architecture +# +# Arguments: +# $1 - kernel version +# $2 - kernel image file +# $3 - kernel map file +# $4 - default install path (blank if root directory) +# + +# User may have a custom install script + +if [ -x /sbin/installkernel ]; then + exec /sbin/installkernel "$@" +fi + +if [ "$2" = "zImage" ]; then +# Compressed install + echo "Installing compressed kernel" + if [ -f $4/vmlinuz-$1 ]; then + mv $4/vmlinuz-$1 $4/vmlinuz.old + fi + + if [ -f $4/System.map-$1 ]; then + mv $4/System.map-$1 $4/System.old + fi + + cat $2 > $4/vmlinuz-$1 + cp $3 $4/System.map-$1 +else +# Normal install + echo "Installing normal kernel" + if [ -f $4/vmlinux-$1 ]; then + mv $4/vmlinux-$1 $4/vmlinux.old + fi + + if [ -f $4/System.map ]; then + mv $4/System.map $4/System.old + fi + + cat $2 > $4/vmlinux-$1 + cp $3 $4/System.map +fi --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/boot/compressed/m32r_sio.c 2004-09-03 00:57:19.057142136 -0700 @@ -0,0 +1,53 @@ +/* + * arch/m32r/boot/compressed/m32r_sio.c + * + * 2003-02-12: Takeo Takahashi + * + */ + +#include +#include +#include + +void putc(char c); + +int puts(const char *s) +{ + char c; + while ((c = *s++)) putc(c); + return 0; +} + +#if defined(CONFIG_PLAT_M32700UT_Alpha) || defined(CONFIG_PLAT_M32700UT) +#define USE_FPGA_MAP 0 + +#if USE_FPGA_MAP +/* + * fpga configuration program uses MMU, and define map as same as + * M32104 uT-Engine board. + */ +#define BOOT_SIO0STS (volatile unsigned short *)(0x02c00000 + 0x20006) +#define BOOT_SIO0TXB (volatile unsigned short *)(0x02c00000 + 0x2000c) +#else +#undef PLD_BASE +#define PLD_BASE 0xa4c00000 +#define BOOT_SIO0STS PLD_ESIO0STS +#define BOOT_SIO0TXB PLD_ESIO0TXB +#endif + +void putc(char c) +{ + + while ((*BOOT_SIO0STS & 0x3) != 0x3) ; + if (c == '\n') { + *BOOT_SIO0TXB = '\r'; + while ((*BOOT_SIO0STS & 0x3) != 0x3) ; + } + *BOOT_SIO0TXB = c; +} +#else +void putc(char c) +{ + /* do nothing */ +} +#endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/boot/compressed/Makefile 2004-09-03 00:57:19.058141984 -0700 @@ -0,0 +1,38 @@ +# +# linux/arch/sh/boot/compressed/Makefile +# +# create a compressed vmlinux image from the original vmlinux +# + +targets := vmlinux vmlinux.bin vmlinux.bin.gz head.o misc.o \ + m32r-sio.o piggy.o vmlinux.lds +EXTRA_AFLAGS := -traditional + +OBJECTS = $(obj)/head.o $(obj)/misc.o $(obj)/m32r_sio.o + +# +# IMAGE_OFFSET is the load offset of the compression loader +# +#IMAGE_OFFSET := $(shell printf "0x%08x" $$[$(CONFIG_MEMORY_START)+0x2000]) +#IMAGE_OFFSET := $(shell printf "0x%08x" $$[$(CONFIG_MEMORY_START)+0x00400000]) + +LDFLAGS_vmlinux := -T + +$(obj)/vmlinux: $(obj)/vmlinux.lds $(OBJECTS) $(obj)/piggy.o FORCE + $(call if_changed,ld) + @: + +$(obj)/vmlinux.bin: vmlinux FORCE + $(call if_changed,objcopy) + +$(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE + $(call if_changed,gzip) + +$(obj)/vmlinux.lds: $(obj)/vmlinux.lds.S FORCE + $(CPP) $(EXTRA_AFLAGS) -C -P -I include $< >$@ + +LDFLAGS_piggy.o := -r --format binary --oformat elf32-m32r-linux -T +OBJCOPYFLAGS += -R .empty_zero_page + +$(obj)/piggy.o: $(obj)/vmlinux.scr $(obj)/vmlinux.bin.gz FORCE + $(call if_changed,ld) --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/boot/compressed/misc.c 2004-09-03 00:57:19.058141984 -0700 @@ -0,0 +1,223 @@ +/* + * arch/m32r/boot/compressed/misc.c + * + * This is a collection of several routines from gzip-1.0.3 + * adapted for Linux. + * + * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 + * + * Adapted for SH by Stuart Menefy, Aug 1999 + * + * Modified to use standard LinuxSH BIOS by Greg Banks 7Jul2000 + * + * 2003-02-12: Support M32R by Takeo Takahashi + * This is based on arch/sh/boot/compressed/misc.c. + */ + +#include +#include + +/* + * gzip declarations + */ + +#define OF(args) args +#define STATIC static + +#undef memset +#undef memcpy +#define memzero(s, n) memset ((s), 0, (n)) + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; + +#define WSIZE 0x8000 /* Window size must be at least 32k, */ + /* and a power of two */ + +static uch *inbuf; /* input buffer */ +static uch window[WSIZE]; /* Sliding window buffer */ + +static unsigned insize; /* valid bytes in inbuf */ +static unsigned inptr; /* index of next byte to be processed in inbuf */ +static unsigned outcnt; /* bytes in output buffer */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) + +/* Diagnostic functions */ +#ifdef DEBUG +# define Assert(cond,msg) {if(!(cond)) error(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +static int fill_inbuf(void); +static void flush_window(void); +static void error(char *m); +static void gzip_mark(void **); +static void gzip_release(void **); + +extern char input_data[]; +extern int input_len; + +static long bytes_out; +static uch *output_data; +static unsigned long output_ptr; + + +static void *malloc(int size); +static void free(void *where); +static void error(char *m); +static void gzip_mark(void **); +static void gzip_release(void **); + +extern int puts(const char *); + +extern int _text; /* Defined in vmlinux.lds.S */ +extern int _end; +static unsigned long free_mem_ptr; +static unsigned long free_mem_end_ptr; + +#define HEAP_SIZE 0x10000 + +#include "../../../../lib/inflate.c" + +static void *malloc(int size) +{ + void *p; + + if (size <0) error("Malloc error\n"); + if (free_mem_ptr == 0) error("Memory error\n"); + + free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */ + + p = (void *)free_mem_ptr; + free_mem_ptr += size; + + if (free_mem_ptr >= free_mem_end_ptr) + error("\nOut of memory\n"); + + return p; +} + +static void free(void *where) +{ /* Don't care */ +} + +static void gzip_mark(void **ptr) +{ + *ptr = (void *) free_mem_ptr; +} + +static void gzip_release(void **ptr) +{ + free_mem_ptr = (long) *ptr; +} + +void* memset(void* s, int c, size_t n) +{ + int i; + char *ss = (char*)s; + + for (i=0;i> 8); + } + crc = c; + bytes_out += (ulg)outcnt; + output_ptr += (ulg)outcnt; + outcnt = 0; +} + +static void error(char *x) +{ + puts("\n\n"); + puts(x); + puts("\n\n -- System halted"); + + while(1); /* Halt */ +} + +#define STACK_SIZE (4096) +long user_stack [STACK_SIZE]; +long* stack_start = &user_stack[STACK_SIZE]; + +/* return decompressed size */ +long decompress_kernel(void) +{ + insize = 0; + inptr = 0; + bytes_out = 0; + outcnt = 0; + output_data = 0; + output_ptr = CONFIG_MEMORY_START + 0x2000; + free_mem_ptr = (unsigned long)&_end; + free_mem_end_ptr = free_mem_ptr + HEAP_SIZE; + + makecrc(); + puts("Uncompressing Linux... "); + gunzip(); + puts("Ok, booting the kernel.\n"); + return bytes_out; +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/boot/compressed/vmlinux.lds.S 2004-09-03 00:57:19.058141984 -0700 @@ -0,0 +1,23 @@ +#include + +OUTPUT_ARCH(m32r) +ENTRY(startup) +SECTIONS +{ + . = CONFIG_MEMORY_START + 0x00400000; + + _text = .; + .text : { *(.text) } = 0 + .rodata : { *(.rodata) } + _etext = .; + + . = ALIGN(32) + (. & (32 - 1)); + .data : { *(.data) } + _edata = .; + + . = ALIGN(32 / 8); + __bss_start = .; + .bss : { *(.bss) } + . = ALIGN(32 / 8); + _end = . ; +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/boot/compressed/vmlinux.scr 2004-09-03 00:57:17.234419232 -0700 @@ -0,0 +1,9 @@ +SECTIONS +{ + .data : { + input_len = .; + LONG(input_data_end - input_data) input_data = .; + *(.data) + input_data_end = .; + } +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/boot/Makefile 2004-09-03 00:57:17.235419080 -0700 @@ -0,0 +1,19 @@ +# +# arch/m32r/boot/Makefile +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. + +targets := zImage +subdir- := compressed + +obj-y := setup.o + +$(obj)/zImage: $(obj)/compressed/vmlinux FORCE + $(call if_changed,objcopy) + @echo 'Kernel: $@ is ready' + +$(obj)/compressed/vmlinux: FORCE + $(Q)$(MAKE) $(build)=$(obj)/compressed $@ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/boot/setup.S 2004-09-03 00:57:19.059141832 -0700 @@ -0,0 +1,162 @@ +/* + * linux/arch/m32r/boot/setup.S -- A setup code. + * + * Copyright (C) 2001, 2002 Hiroyuki Kondo, Hirokazu Takata, + * and Hitoshi Yamamoto + * + */ +/* $Id$ */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * References to members of the boot_cpu_data structure. + */ + +#define CPU_PARAMS boot_cpu_data +#define M32R_MCICAR 0xfffffff0 +#define M32R_MCDCAR 0xfffffff4 +#define M32R_MCCR 0xfffffffc +#define M32R_BSCR0 0xffffffd2 + +;BSEL +#define BSEL0CR0 0x00ef5000 +#define BSEL0CR1 0x00ef5004 +#define BSEL1CR0 0x00ef5100 +#define BSEL1CR1 0x00ef5104 +#define BSEL0CR0_VAL 0x00000000 +#define BSEL0CR1_VAL 0x01200100 +#define BSEL1CR0_VAL 0x01018000 +#define BSEL1CR1_VAL 0x00200001 + +;SDRAMC +#define SDRAMC_SDRF0 0x00ef6000 +#define SDRAMC_SDRF1 0x00ef6004 +#define SDRAMC_SDIR0 0x00ef6008 +#define SDRAMC_SDIR1 0x00ef600c +#define SDRAMC_SD0ADR 0x00ef6020 +#define SDRAMC_SD0ER 0x00ef6024 +#define SDRAMC_SD0TR 0x00ef6028 +#define SDRAMC_SD0MOD 0x00ef602c +#define SDRAMC_SD1ADR 0x00ef6040 +#define SDRAMC_SD1ER 0x00ef6044 +#define SDRAMC_SD1TR 0x00ef6048 +#define SDRAMC_SD1MOD 0x00ef604c +#define SDRAM0 0x18000000 +#define SDRAM1 0x1c000000 + +/*------------------------------------------------------------------------ + * start up + */ + +/*------------------------------------------------------------------------ + * Kernel entry + */ + .section .boot, "ax" +ENTRY(boot) + +/* Set cache mode */ +#if defined(CONFIG_CHIP_XNUX2) + ldi r0, #-2 ;LDIMM (r0, M32R_MCCR) + ldi r1, #0x0101 ; cache on (with invalidation) +; ldi r1, #0x00 ; cache off + sth r1, @r0 +#elif defined(CONFIG_CHIP_M32700) || defined(CONFIG_CHIP_VDEC2) \ + || defined(CONFIG_CHIP_OPSP) + ldi r0, #-4 ;LDIMM (r0, M32R_MCCR) + ldi r1, #0x73 ; cache on (with invalidation) +; ldi r1, #0x00 ; cache off + st r1, @r0 +#else +#error unknown chip configuration +#endif + +#ifdef CONFIG_SMP + ;; if not BSP (CPU#0) goto AP_loop + seth r5, #shigh(M32R_CPUID_PORTL) + ld r5, @(low(M32R_CPUID_PORTL), r5) + bnez r5, AP_loop +#if !defined(CONFIG_PLAT_USRV) + ;; boot AP + ld24 r5, #0xeff2f8 ; IPICR7 + ldi r6, #0x2 ; IPI to CPU1 + st r6, @r5 +#endif +#endif + +/* + * Now, Jump to stext + * if with MMU, TLB on. + * if with no MMU, only jump. + */ + .global eit_vector +mmu_on: + LDIMM (r13, stext) +#ifdef CONFIG_MMU + bl init_tlb + LDIMM (r2, eit_vector) ; set EVB(cr5) + mvtc r2, cr5 + seth r0, #high(MMU_REG_BASE) ; Set MMU_REG_BASE higher + or3 r0, r0, #low(MMU_REG_BASE) ; Set MMU_REG_BASE lower + ldi r1, #0x01 + st r1, @(MATM_offset,r0) ; Set MATM (T bit ON) + ld r0, @(MATM_offset,r0) ; Check +#else + seth r0,#high(M32R_MCDCAR) + or3 r0,r0,#low(M32R_MCDCAR) + ld24 r1,#0x8080 + st r1,@r0 +#endif /* CONFIG_MMU */ + jmp r13 + nop + nop + +#ifdef CONFIG_SMP +/* + * AP wait loop + */ +ENTRY(AP_loop) + ;; disable interrupt + clrpsw #0x40 + ;; reset EVB + LDIMM (r4, _AP_RE) + seth r5, #high(__PAGE_OFFSET) + or3 r5, r5, #low(__PAGE_OFFSET) + not r5, r5 + and r4, r5 + mvtc r4, cr5 + ;; disable maskable interrupt + seth r4, #high(M32R_ICU_IMASK_PORTL) + or3 r4, r4, #low(M32R_ICU_IMASK_PORTL) + ldi r5, #0 + st r5, @r4 + ld r5, @r4 + ;; enable only IPI + setpsw #0x40 + ;; LOOOOOOOOOOOOOOP!!! + .fillinsn +2: + nop + nop + bra 2b + nop + nop + +#ifdef CONFIG_CHIP_M32700_TS1 + .global dcache_dummy + .balign 16, 0 +dcache_dummy: + .byte 16 +#endif /* CONFIG_CHIP_M32700_TS1 */ +#endif /* CONFIG_SMP */ + + .end + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/defconfig 2004-09-03 00:57:18.714194272 -0700 @@ -0,0 +1,635 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_M32R=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_HOTPLUG=y +CONFIG_IKCONFIG=y +# CONFIG_IKCONFIG_PROC is not set +CONFIG_EMBEDDED=y +# CONFIG_KALLSYMS is not set +# CONFIG_FUTEX is not set +# CONFIG_EPOLL is not set +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# Processor type and features +# +# CONFIG_PLAT_MAPPI is not set +# CONFIG_PLAT_USRV is not set +CONFIG_PLAT_M32700UT=y +# CONFIG_PLAT_OPSPUT is not set +# CONFIG_PLAT_OAKS32R is not set +# CONFIG_PLAT_MAPPI2 is not set +CONFIG_CHIP_M32700=y +# CONFIG_CHIP_M32102 is not set +# CONFIG_CHIP_VDEC2 is not set +# CONFIG_CHIP_OPSP is not set +CONFIG_MMU=y +CONFIG_TLB_ENTRIES=32 +CONFIG_ISA_M32R2=y +CONFIG_ISA_DSP_LEVEL2=y +CONFIG_ISA_DUAL_ISSUE=y +CONFIG_BUS_CLOCK=50000000 +CONFIG_TIMER_DIVIDE=128 +# CONFIG_CPU_LITTLE_ENDIAN is not set +CONFIG_MEMORY_START=0x08000000 +CONFIG_MEMORY_SIZE=0x01000000 +CONFIG_NOHIGHMEM=y +# CONFIG_DISCONTIGMEM is not set +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +CONFIG_PREEMPT=y +# CONFIG_HAVE_DEC_LOCK is not set +# CONFIG_SMP is not set + +# +# M32R drivers +# +# CONFIG_M32RPCC is not set +CONFIG_M32R_CFC=y +CONFIG_M32700UT_CFC=y +CONFIG_CFC_NUM=1 +# CONFIG_MTD_M32R is not set +CONFIG_M32R_SMC91111=y +CONFIG_M32700UT_DS1302=y + +# +# Power management options (ACPI, APM) +# +# CONFIG_PM is not set + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +# CONFIG_PCI is not set +# CONFIG_ISA is not set + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=y +# CONFIG_PCMCIA_DEBUG is not set +# CONFIG_TCIC is not set + +# +# PCI Hotplug Support +# + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=y +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +CONFIG_BLK_DEV_IDECS=y +CONFIG_BLK_DEV_IDECD=m +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=y +# CONFIG_IDE_ARM is not set +# CONFIG_BLK_DEV_IDEDMA is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +CONFIG_SCSI=m +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=m +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=m +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_CHR_DEV_SG=m + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI Transport Attributes +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_DEBUG is not set + +# +# PCMCIA SCSI adapter support +# +# CONFIG_PCMCIA_AHA152X is not set +# CONFIG_PCMCIA_FDOMAIN is not set +# CONFIG_PCMCIA_NINJA_SCSI is not set +# CONFIG_PCMCIA_QLOGIC is not set +# CONFIG_PCMCIA_SYM53C500 is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_NETDEVICES is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_M32R_SIO is not set +CONFIG_SERIAL_M32R_PLDSIO=y +CONFIG_SERIAL_M32R_PLDSIO_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_QIC02_TAPE is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +CONFIG_VIDEO_DEV=y + +# +# Video For Linux +# + +# +# Video Adapters +# +# CONFIG_VIDEO_CPIA is not set +CONFIG_M32R_AR=y +CONFIG_M32R_AR_VGA=y + +# +# Radio Adapters +# +# CONFIG_RADIO_MAESTRO is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +CONFIG_EXT3_FS=m +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +CONFIG_JBD=m +CONFIG_JBD_DEBUG=y +CONFIG_FS_MBCACHE=y +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_REISERFS_FS_XATTR is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +# CONFIG_ZISOFS is not set +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=m +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_FRAME_POINTER is not set + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/drivers/8390.c 2004-09-03 00:57:17.238418624 -0700 @@ -0,0 +1 @@ +#include "../../../drivers/net/8390.c" --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/drivers/8390.h 2004-09-03 00:57:17.239418472 -0700 @@ -0,0 +1 @@ +#include "../../../drivers/net/8390.h" --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/drivers/cs_internal.h 2004-09-03 00:57:17.239418472 -0700 @@ -0,0 +1,2 @@ +#include "../../../drivers/pcmcia/cs_internal.h" + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/drivers/ds1302.c 2004-09-03 00:57:17.241418168 -0700 @@ -0,0 +1,432 @@ +/*!*************************************************************************** +*! +*! FILE NAME : ds1302.c +*! +*! DESCRIPTION: Implements an interface for the DS1302 RTC through Etrax I/O +*! +*! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init, get_rtc_status +*! +*! $Log: ds1302.c,v $ +*! Revision 1.2 2003/10/29 08:42:58 fujiwara +*! Set PLD_RTCBAUR from bus clock +*! +*! Revision 1.12 2002/04/10 15:35:25 johana +*! Moved probe function closer to init function and marked it __init. +*! +*! Revision 1.11 2001/06/14 12:35:52 jonashg +*! The ATA hack is back. It is unfortunately the only way to set g27 to output. +*! +*! Revision 1.9 2001/06/14 10:00:14 jonashg +*! No need for tempudelay to be inline anymore (had to adjust the usec to +*! loops conversion because of this to make it slow enough to be a udelay). +*! +*! Revision 1.8 2001/06/14 08:06:32 jonashg +*! Made tempudelay delay usecs (well, just a tad more). +*! +*! Revision 1.7 2001/06/13 14:18:11 jonashg +*! Only allow processes with SYS_TIME capability to set time and charge. +*! +*! Revision 1.6 2001/06/12 15:22:07 jonashg +*! * Made init function __init. +*! * Parameter to out_byte() is unsigned char. +*! * The magic number 42 has got a name. +*! * Removed comment about /proc (nothing is exported there). +*! +*! Revision 1.5 2001/06/12 14:35:13 jonashg +*! Gave the module a name and added it to printk's. +*! +*! Revision 1.4 2001/05/31 14:53:40 jonashg +*! Made tempudelay() inline so that the watchdog doesn't reset (see +*! function comment). +*! +*! Revision 1.3 2001/03/26 16:03:06 bjornw +*! Needs linux/config.h +*! +*! Revision 1.2 2001/03/20 19:42:00 bjornw +*! Use the ETRAX prefix on the DS1302 options +*! +*! Revision 1.1 2001/03/20 09:13:50 magnusmn +*! Linux 2.4 port +*! +*! Revision 1.10 2000/07/05 15:38:23 bjornw +*! Dont update kernel time when a RTC_SET_TIME is done +*! +*! Revision 1.9 2000/03/02 15:42:59 macce +*! * Hack to make RTC work on all 2100/2400 +*! +*! Revision 1.8 2000/02/23 16:59:18 torbjore +*! added setup of R_GEN_CONFIG when RTC is connected to the generic port. +*! +*! Revision 1.7 2000/01/17 15:51:43 johana +*! Added RTC_SET_CHARGE ioctl to enable trickle charger. +*! +*! Revision 1.6 1999/10/27 13:19:47 bjornw +*! Added update_xtime_from_cmos which reads back the updated RTC into the kernel. +*! /dev/rtc calls it now. +*! +*! Revision 1.5 1999/10/27 12:39:37 bjornw +*! Disabled superuser check. Anyone can now set the time. +*! +*! Revision 1.4 1999/09/02 13:27:46 pkj +*! Added shadow for R_PORT_PB_CONFIG. +*! Renamed port_g_shadow to port_g_data_shadow. +*! +*! Revision 1.3 1999/09/02 08:28:06 pkj +*! Made it possible to select either port PB or the generic port for the RST +*! signal line to the DS1302 RTC. +*! Also make sure the RST bit is configured as output on Port PB (if used). +*! +*! Revision 1.2 1999/09/01 14:47:20 bjornw +*! Added support for /dev/rtc operations with ioctl RD_TIME and SET_TIME to read +*! and set the date. Register as major 121. +*! +*! Revision 1.1 1999/09/01 09:45:29 bjornw +*! Implemented a DS1302 RTC driver. +*! +*! +*! --------------------------------------------------------------------------- +*! +*! (C) Copyright 1999, 2000, 2001 Axis Communications AB, LUND, SWEDEN +*! +*! $Id: ds1302.c,v 1.2 2003/10/29 08:42:58 fujiwara Exp $ +*! +*!***************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define RTC_MAJOR_NR 121 /* local major, change later */ + +static const char ds1302_name[] = "ds1302"; + +/* Send 8 bits. */ +static void +out_byte_rtc(unsigned int reg_addr, unsigned char x) +{ + //RST H + outw(0x0001,(unsigned long)PLD_RTCRSTODT); + //write data + outw(((x<<8)|(reg_addr&0xff)),(unsigned long)PLD_RTCWRDATA); + //WE + outw(0x0002,(unsigned long)PLD_RTCCR); + //wait + while(inw((unsigned long)PLD_RTCCR)); + + //RST L + outw(0x0000,(unsigned long)PLD_RTCRSTODT); + +} + +static unsigned char +in_byte_rtc(unsigned int reg_addr) +{ + unsigned char retval; + + //RST H + outw(0x0001,(unsigned long)PLD_RTCRSTODT); + //write data + outw((reg_addr&0xff),(unsigned long)PLD_RTCRDDATA); + //RE + outw(0x0001,(unsigned long)PLD_RTCCR); + //wait + while(inw((unsigned long)PLD_RTCCR)); + + //read data + retval=(inw((unsigned long)PLD_RTCRDDATA) & 0xff00)>>8; + + //RST L + outw(0x0000,(unsigned long)PLD_RTCRSTODT); + + return retval; +} + +/* Enable writing. */ + +static void +ds1302_wenable(void) +{ + out_byte_rtc(0x8e,0x00); +} + +/* Disable writing. */ + +static void +ds1302_wdisable(void) +{ + out_byte_rtc(0x8e,0x80); +} + + + +/* Read a byte from the selected register in the DS1302. */ + +unsigned char +ds1302_readreg(int reg) +{ + unsigned char x; + + x=in_byte_rtc((0x81 | (reg << 1))); /* read register */ + + return x; +} + +/* Write a byte to the selected register. */ + +void +ds1302_writereg(int reg, unsigned char val) +{ + ds1302_wenable(); + out_byte_rtc((0x80 | (reg << 1)),val); + ds1302_wdisable(); +} + +void +get_rtc_time(struct rtc_time *rtc_tm) +{ + unsigned long flags; + + local_irq_save(flags); + local_irq_disable(); + + rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS); + rtc_tm->tm_min = CMOS_READ(RTC_MINUTES); + rtc_tm->tm_hour = CMOS_READ(RTC_HOURS); + rtc_tm->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH); + rtc_tm->tm_mon = CMOS_READ(RTC_MONTH); + rtc_tm->tm_year = CMOS_READ(RTC_YEAR); + + local_irq_restore(flags); + + BCD_TO_BIN(rtc_tm->tm_sec); + BCD_TO_BIN(rtc_tm->tm_min); + BCD_TO_BIN(rtc_tm->tm_hour); + BCD_TO_BIN(rtc_tm->tm_mday); + BCD_TO_BIN(rtc_tm->tm_mon); + BCD_TO_BIN(rtc_tm->tm_year); + + /* + * Account for differences between how the RTC uses the values + * and how they are defined in a struct rtc_time; + */ + + if (rtc_tm->tm_year <= 69) + rtc_tm->tm_year += 100; + + rtc_tm->tm_mon--; +} + +static unsigned char days_in_mo[] = + {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +/* ioctl that supports RTC_RD_TIME and RTC_SET_TIME (read and set time/date). */ + +static int +rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + unsigned long flags; + + switch(cmd) { + case RTC_RD_TIME: /* read the time/date from RTC */ + { + struct rtc_time rtc_tm; + + memset(&rtc_tm, 0, sizeof (struct rtc_time)); + get_rtc_time(&rtc_tm); + if (copy_to_user((struct rtc_time*)arg, &rtc_tm, sizeof(struct rtc_time))) + return -EFAULT; + return 0; + } + + case RTC_SET_TIME: /* set the RTC */ + { + struct rtc_time rtc_tm; + unsigned char mon, day, hrs, min, sec, leap_yr; + unsigned int yrs; + + if (!capable(CAP_SYS_TIME)) + return -EPERM; + + if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, sizeof(struct rtc_time))) + return -EFAULT; + + yrs = rtc_tm.tm_year + 1900; + mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */ + day = rtc_tm.tm_mday; + hrs = rtc_tm.tm_hour; + min = rtc_tm.tm_min; + sec = rtc_tm.tm_sec; + + + if ((yrs < 1970) || (yrs > 2069)) + return -EINVAL; + + leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); + + if ((mon > 12) || (day == 0)) + return -EINVAL; + + if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr))) + return -EINVAL; + + if ((hrs >= 24) || (min >= 60) || (sec >= 60)) + return -EINVAL; + + if (yrs >= 2000) + yrs -= 2000; /* RTC (0, 1, ... 69) */ + else + yrs -= 1900; /* RTC (70, 71, ... 99) */ + + BIN_TO_BCD(sec); + BIN_TO_BCD(min); + BIN_TO_BCD(hrs); + BIN_TO_BCD(day); + BIN_TO_BCD(mon); + BIN_TO_BCD(yrs); + + local_irq_save(flags); + local_irq_disable(); + CMOS_WRITE(yrs, RTC_YEAR); + CMOS_WRITE(mon, RTC_MONTH); + CMOS_WRITE(day, RTC_DAY_OF_MONTH); + CMOS_WRITE(hrs, RTC_HOURS); + CMOS_WRITE(min, RTC_MINUTES); + CMOS_WRITE(sec, RTC_SECONDS); + local_irq_restore(flags); + + /* Notice that at this point, the RTC is updated but + * the kernel is still running with the old time. + * You need to set that separately with settimeofday + * or adjtimex. + */ + return 0; + } + + case RTC_SET_CHARGE: /* set the RTC TRICKLE CHARGE register */ + { + int tcs_val; + + if (!capable(CAP_SYS_TIME)) + return -EPERM; + + if(copy_from_user(&tcs_val, (int*)arg, sizeof(int))) + return -EFAULT; + + tcs_val = RTC_TCR_PATTERN | (tcs_val & 0x0F); + ds1302_writereg(RTC_TRICKLECHARGER, tcs_val); + return 0; + } + default: + return -EINVAL; + } +} + +int +get_rtc_status(char *buf) +{ + char *p; + struct rtc_time tm; + + p = buf; + + get_rtc_time(&tm); + + /* + * There is no way to tell if the luser has the RTC set for local + * time or for Universal Standard Time (GMT). Probably local though. + */ + + p += sprintf(p, + "rtc_time\t: %02d:%02d:%02d\n" + "rtc_date\t: %04d-%02d-%02d\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); + + return p - buf; +} + + +/* The various file operations we support. */ + +static struct file_operations rtc_fops = { + .owner = THIS_MODULE, + .ioctl = rtc_ioctl, +}; + +/* Probe for the chip by writing something to its RAM and try reading it back. */ + +#define MAGIC_PATTERN 0x42 + +static int __init +ds1302_probe(void) +{ + int retval, res, baur; + + baur=(boot_cpu_data.bus_clock/(2*1000*1000)); + + printk("%s: Set PLD_RTCBAUR = %d\n", ds1302_name,baur); + + outw(0x0000,(unsigned long)PLD_RTCCR); + outw(0x0000,(unsigned long)PLD_RTCRSTODT); + outw(baur,(unsigned long)PLD_RTCBAUR); + + /* Try to talk to timekeeper. */ + + ds1302_wenable(); + /* write RAM byte 0 */ + /* write something magic */ + out_byte_rtc(0xc0,MAGIC_PATTERN); + + /* read RAM byte 0 */ + if((res = in_byte_rtc(0xc1)) == MAGIC_PATTERN) { + char buf[100]; + ds1302_wdisable(); + printk("%s: RTC found.\n", ds1302_name); + get_rtc_status(buf); + printk(buf); + retval = 1; + } else { + printk("%s: RTC not found.\n", ds1302_name); + retval = 0; + } + + return retval; +} + + +/* Just probe for the RTC and register the device to handle the ioctl needed. */ + +int __init +ds1302_init(void) +{ + if (!ds1302_probe()) { + return -1; + } + return 0; +} + +static int __init ds1302_register(void) +{ + ds1302_init(); + if (register_chrdev(RTC_MAJOR_NR, ds1302_name, &rtc_fops)) { + printk(KERN_INFO "%s: unable to get major %d for rtc\n", + ds1302_name, RTC_MAJOR_NR); + return -1; + } + return 0; +} + +module_init(ds1302_register); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/drivers/Kconfig 2004-09-03 00:57:17.241418168 -0700 @@ -0,0 +1,42 @@ +# +# For a description of the syntax of this configuration file, +# see Documentation/kbuild/kconfig-language.txt. +# + +menu "M32R drivers" + +config M32RPCC + bool "M32R PCMCIA I/F" + depends on CHIP_M32700 + +config M32R_NE2000 + bool "On board NE2000 Network Interface Chip" + depends on PLAT_MAPPI || PLAT_OAKS32R + +config M32R_CFC + bool "CF I/F Controller" + depends on PLAT_USRV || PLAT_M32700UT || PLAT_MAPPI2 || PLAT_OPSPUT + +config M32700UT_CFC + bool + depends on M32R_CFC + default y + +config CFC_NUM + int "CF I/F number" + depends on PLAT_USRV || PLAT_M32700UT + default "1" if PLAT_USRV || PLAT_M32700UT || PLAT_MAPPI2 || PLAT_OPSPUT + +config MTD_M32R + bool "Flash device mapped on M32R" + depends on PLAT_USRV || PLAT_M32700UT || PLAT_MAPPI2 + +config M32R_SMC91111 + bool "On board SMC91111 Network Interface Chip" + depends on PLAT_M32700UT || PLAT_MAPPI2 || PLAT_OPSPUT + +config M32700UT_DS1302 + bool "DS1302 Real Time Clock support" + depends on PLAT_M32700UT || PLAT_OPSPUT + +endmenu --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/drivers/m32r_cfc.c 2004-09-03 00:57:17.245417560 -0700 @@ -0,0 +1,910 @@ +/* + * linux/arch/m32r/drivers/m32r_cfc.c + * + * Device driver for the CFC functionality of M32R. + * + * Copyright (c) 2001, 2002, 2003, 2004 + * Hiroyuki Kondo, Sugai Naotom, Hayato Fujiwara + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#undef MAX_IO_WIN /* FIXME */ +#define MAX_IO_WIN 1 +#undef MAX_WIN /* FIXME */ +#define MAX_WIN 1 + +#include "m32r_cfc.h" + +#if !defined(COFIG_PLAT_USRV) +#define PCMCIA_DEBUG 3 +#endif /* COFIG_PLAT_USRV */ + +#ifdef PCMCIA_DEBUG +int m32r_cfc_debug = PCMCIA_DEBUG; +module_param(m32r_cfc_debug, int, 0444); +#define DEBUG(n, args...) if (m32r_cfc_debug>(n)) printk(args) +#else +#define DEBUG(n, args...) do { } while (0) +#endif + +/* Poll status interval -- 0 means default to interrupt */ +static int poll_interval = 0; + +typedef enum pcc_space { as_none = 0, as_comm, as_attr, as_io } pcc_as_t; + +typedef struct pcc_socket { + u_short type, flags; + struct pcmcia_socket socket; + unsigned int number; + ioaddr_t ioaddr; + u_long mapaddr; + u_long base; /* PCC register base */ + u_char cs_irq1, cs_irq2, intr; + pccard_io_map io_map[MAX_IO_WIN]; + pccard_mem_map mem_map[MAX_WIN]; + u_char io_win; + u_char mem_win; + pcc_as_t current_space; + u_char last_iodbex; +#ifdef CHAOS_PCC_DEBUG + u_char last_iosize; +#endif +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *proc; +#endif +} pcc_socket_t; + +static int pcc_sockets = 0; +static pcc_socket_t socket[M32R_MAX_PCC] = { + { 0, }, /* ... */ +}; + +#define ISA_LOCK(n, f) do { } while (0) +#define ISA_UNLOCK(n, f) do { } while (0) + +/*====================================================================*/ + +static unsigned int pcc_get(u_short, unsigned int); +static void pcc_set(u_short, unsigned int , unsigned int ); + +static spinlock_t pcc_lock = SPIN_LOCK_UNLOCKED; + +#if !defined(CONFIG_PLAT_USRV) +static __inline__ u_long pcc_port2addr(unsigned long port, int size) { + u_long addr = 0; + u_long odd; + + if (size == 1) { /* byte access */ + odd = (port&1) << 11; + port -= port & 1; + addr = CFC_IO_MAPBASE_BYTE - CFC_IOPORT_BASE + odd + port; + } else if (size == 2) + addr = CFC_IO_MAPBASE_WORD - CFC_IOPORT_BASE + port; + + return addr; +} +#else /* CONFIG_PLAT_USRV */ +static __inline__ u_long pcc_port2addr(unsigned long port, int size) { + u_long odd; + u_long addr = ((port - CFC_IOPORT_BASE) & 0xf000) << 8; + + if (size == 1) { /* byte access */ + odd = port & 1; + port -= odd; + odd <<= 11; + addr = (addr | CFC_IO_MAPBASE_BYTE) + odd + (port & 0xfff); + } else if (size == 2) /* word access */ + addr = (addr | CFC_IO_MAPBASE_WORD) + (port & 0xfff); + + return addr; +} +#endif /* CONFIG_PLAT_USRV */ + +void pcc_ioread_byte(int sock, unsigned long port, void *buf, size_t size, + size_t nmemb, int flag) +{ + u_long addr; + unsigned char *bp = (unsigned char *)buf; + unsigned long flags; + + DEBUG(3, "m32r_cfc: pcc_ioread_byte: sock=%d, port=%#lx, buf=%p, " + "size=%u, nmemb=%d, flag=%d\n", + sock, port, buf, size, nmemb, flag); + + addr = pcc_port2addr(port, 1); + if (!addr) { + printk("m32r_cfc:ioread_byte null port :%#lx\n",port); + return; + } + DEBUG(3, "m32r_cfc: pcc_ioread_byte: addr=%#lx\n", addr); + + spin_lock_irqsave(&pcc_lock, flags); + /* read Byte */ + while (nmemb--) + *bp++ = readb(addr); + spin_unlock_irqrestore(&pcc_lock, flags); +} + +void pcc_ioread_word(int sock, unsigned long port, void *buf, size_t size, + size_t nmemb, int flag) +{ + u_long addr; + unsigned short *bp = (unsigned short *)buf; + unsigned long flags; + + DEBUG(3, "m32r_cfc: pcc_ioread_word: sock=%d, port=%#lx, " + "buf=%p, size=%u, nmemb=%d, flag=%d\n", + sock, port, buf, size, nmemb, flag); + + if (size != 2) + printk("m32r_cfc: ioread_word :illigal size %u : %#lx\n", size, + port); + if (size == 9) + printk("m32r_cfc: ioread_word :insw \n"); + + addr = pcc_port2addr(port, 2); + if (!addr) { + printk("m32r_cfc:ioread_word null port :%#lx\n",port); + return; + } + DEBUG(3, "m32r_cfc: pcc_ioread_word: addr=%#lx\n", addr); + + spin_lock_irqsave(&pcc_lock, flags); + /* read Word */ + while (nmemb--) + *bp++ = readw(addr); + spin_unlock_irqrestore(&pcc_lock, flags); +} + +void pcc_iowrite_byte(int sock, unsigned long port, void *buf, size_t size, + size_t nmemb, int flag) +{ + u_long addr; + unsigned char *bp = (unsigned char *)buf; + unsigned long flags; + + DEBUG(3, "m32r_cfc: pcc_iowrite_byte: sock=%d, port=%#lx, " + "buf=%p, size=%u, nmemb=%d, flag=%d\n", + sock, port, buf, size, nmemb, flag); + + /* write Byte */ + addr = pcc_port2addr(port, 1); + if (!addr) { + printk("m32r_cfc:iowrite_byte null port:%#lx\n",port); + return; + } + DEBUG(3, "m32r_cfc: pcc_iowrite_byte: addr=%#lx\n", addr); + + spin_lock_irqsave(&pcc_lock, flags); + while (nmemb--) + writeb(*bp++, addr); + spin_unlock_irqrestore(&pcc_lock, flags); +} + +void pcc_iowrite_word(int sock, unsigned long port, void *buf, size_t size, + size_t nmemb, int flag) +{ + u_long addr; + unsigned short *bp = (unsigned short *)buf; + unsigned long flags; + + DEBUG(3, "m32r_cfc: pcc_iowrite_word: sock=%d, port=%#lx, " + "buf=%p, size=%u, nmemb=%d, flag=%d\n", + sock, port, buf, size, nmemb, flag); + + if(size != 2) + printk("m32r_cfc: iowrite_word :illigal size %u : %#lx\n", + size, port); + if(size == 9) + printk("m32r_cfc: iowrite_word :outsw \n"); + + addr = pcc_port2addr(port, 2); + if (!addr) { + printk("m32r_cfc:iowrite_word null addr :%#lx\n",port); + return; + } +#if 1 + if (addr & 1) { + printk("m32r_cfc:iowrite_word port addr (%#lx):%#lx\n", port, + addr); + return; + } +#endif + DEBUG(3, "m32r_cfc: pcc_iowrite_word: addr=%#lx\n", addr); + + spin_lock_irqsave(&pcc_lock, flags); + while (nmemb--) + writew(*bp++, addr); + spin_unlock_irqrestore(&pcc_lock, flags); +} + +/*====================================================================*/ + +#define IS_ALIVE 0x8000 + +typedef struct pcc_t { + char *name; + u_short flags; +} pcc_t; + +static pcc_t pcc[] = { +#if !defined(CONFIG_PLAT_USRV) + { "m32r_cfc", 0 }, { "", 0 }, +#else /* CONFIG_PLAT_USRV */ + { "m32r_cfc", 0 }, { "m32r_cfc", 0 }, { "m32r_cfc", 0 }, + { "m32r_cfc", 0 }, { "m32r_cfc", 0 }, { "", 0 }, +#endif /* CONFIG_PLAT_USRV */ +}; + +static irqreturn_t pcc_interrupt(int, void *, struct pt_regs *regs); + +/*====================================================================*/ + +static struct timer_list poll_timer; + +static unsigned int pcc_get(u_short sock, unsigned int reg) +{ + unsigned int val = inw(reg); + DEBUG(3, "m32r_cfc: pcc_get: reg(0x%08x)=0x%04x\n", reg, val); + return val; +} + + +static void pcc_set(u_short sock, unsigned int reg, unsigned int data) +{ + outw(data, reg); + DEBUG(3, "m32r_cfc: pcc_set: reg(0x%08x)=0x%04x\n", reg, data); +} + +/*====================================================================== + + See if a card is present, powered up, in IO mode, and already + bound to a (non PC Card) Linux driver. We leave these alone. + + We make an exception for cards that seem to be serial devices. + +======================================================================*/ + +static int __init is_alive(u_short sock) +{ + unsigned int stat; + + DEBUG(3, "m32r_cfc: is_alive:\n"); + + printk("CF: "); + stat = pcc_get(sock, (unsigned int)PLD_CFSTS); + if (!stat) + printk("No "); + printk("Card is detected at socket %d : stat = 0x%08x\n", sock, stat); + DEBUG(3, "m32r_cfc: is_alive: sock stat is 0x%04x\n", stat); + + return 0; +} + +static void add_pcc_socket(ulong base, int irq, ulong mapaddr, ioaddr_t ioaddr) +{ + pcc_socket_t *t = &socket[pcc_sockets]; + + DEBUG(3, "m32r_cfc: add_pcc_socket: base=%#lx, irq=%d, " + "mapaddr=%#lx, ioaddr=%08x\n", + base, irq, mapaddr, ioaddr); + + /* add sockets */ + t->ioaddr = ioaddr; + t->mapaddr = mapaddr; +#if !defined(CONFIG_PLAT_USRV) + t->base = 0; + t->flags = 0; + t->cs_irq1 = irq; // insert irq + t->cs_irq2 = irq + 1; // eject irq +#else /* CONFIG_PLAT_USRV */ + t->base = base; + t->flags = 0; + t->cs_irq1 = 0; // insert irq + t->cs_irq2 = 0; // eject irq +#endif /* CONFIG_PLAT_USRV */ + + if (is_alive(pcc_sockets)) + t->flags |= IS_ALIVE; + + /* add pcc */ +#if !defined(CONFIG_PLAT_USRV) + request_region((unsigned int)PLD_CFRSTCR, 0x20, "m32r_cfc"); +#else /* CONFIG_PLAT_USRV */ + { + unsigned int reg_base; + + reg_base = (unsigned int)PLD_CFRSTCR; + reg_base |= pcc_sockets << 8; + request_region(reg_base, 0x20, "m32r_cfc"); + } +#endif /* CONFIG_PLAT_USRV */ + printk(KERN_INFO " %s ", pcc[pcc_sockets].name); + printk("pcc at 0x%08lx\n", t->base); + + /* Update socket interrupt information, capabilities */ + t->socket.features |= (SS_CAP_PCCARD | SS_CAP_STATIC_MAP); + t->socket.map_size = M32R_PCC_MAPSIZE; + t->socket.io_offset = ioaddr; /* use for io access offset */ + t->socket.irq_mask = 0; +#if !defined(CONFIG_PLAT_USRV) + t->socket.pci_irq = PLD_IRQ_CFIREQ ; /* card interrupt */ +#else /* CONFIG_PLAT_USRV */ + t->socket.pci_irq = PLD_IRQ_CF0 + pcc_sockets; +#endif /* CONFIG_PLAT_USRV */ + +#ifndef CONFIG_PLAT_USRV + /* insert interrupt */ + request_irq(irq, pcc_interrupt, 0, "m32r_cfc", pcc_interrupt); + /* eject interrupt */ + request_irq(irq+1, pcc_interrupt, 0, "m32r_cfc", pcc_interrupt); + + DEBUG(3, "m32r_cfc: enable CFMSK, RDYSEL\n"); + pcc_set(pcc_sockets, (unsigned int)PLD_CFIMASK, 0x01); +#endif /* CONFIG_PLAT_USRV */ +#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT) + pcc_set(pcc_sockets, (unsigned int)PLD_CFCR1, 0x0200); +#endif + pcc_sockets++; + + return; +} + + +/*====================================================================*/ + +static irqreturn_t pcc_interrupt(int irq, void *dev, struct pt_regs *regs) +{ + int i; + u_int events = 0; + int handled = 0; + + DEBUG(3, "m32r_cfc: pcc_interrupt: irq=%d, dev=%p, regs=%p\n", + irq, dev, regs); + for (i = 0; i < pcc_sockets; i++) { + if (socket[i].cs_irq1 != irq && socket[i].cs_irq2 != irq) + continue; + + handled = 1; + DEBUG(3, "m32r_cfc: pcc_interrupt: socket %d irq 0x%02x ", + i, irq); + events |= SS_DETECT; /* insert or eject */ + if (events) + pcmcia_parse_events(&socket[i].socket, events); + } + DEBUG(3, "m32r_cfc: pcc_interrupt: done\n"); + + return IRQ_RETVAL(handled); +} /* pcc_interrupt */ + +static void pcc_interrupt_wrapper(u_long data) +{ + DEBUG(3, "m32r_cfc: pcc_interrupt_wrapper:\n"); + pcc_interrupt(0, NULL, NULL); + init_timer(&poll_timer); + poll_timer.expires = jiffies + poll_interval; + add_timer(&poll_timer); +} + +/*====================================================================*/ + +static int _pcc_get_status(u_short sock, u_int *value) +{ + u_int status; + + DEBUG(3, "m32r_cfc: _pcc_get_status:\n"); + status = pcc_get(sock, (unsigned int)PLD_CFSTS); + *value = (status) ? SS_DETECT : 0; + DEBUG(3, "m32r_cfc: _pcc_get_status: status=0x%08x\n", status); + +#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT) + if ( status ) { + /* enable CF power */ + status = inw((unsigned int)PLD_CPCR); + if (!(status & PLD_CPCR_CF)) { + DEBUG(3, "m32r_cfc: _pcc_get_status: " + "power on (CPCR=0x%08x)\n", status); + status |= PLD_CPCR_CF; + outw(status, (unsigned int)PLD_CPCR); + udelay(100); + } + *value |= SS_POWERON; + + pcc_set(sock, (unsigned int)PLD_CFBUFCR,0);/* enable buffer */ + udelay(100); + + *value |= SS_READY; /* always ready */ + *value |= SS_3VCARD; + } else { + /* disable CF power */ + status = inw((unsigned int)PLD_CPCR); + status &= ~PLD_CPCR_CF; + outw(status, (unsigned int)PLD_CPCR); + udelay(100); + DEBUG(3, "m32r_cfc: _pcc_get_status: " + "power off (CPCR=0x%08x)\n", status); + } +#elif defined(CONFIG_PLAT_MAPPI2) + if ( status ) { + status = pcc_get(sock, (unsigned int)PLD_CPCR); + if (status == 0) { /* power off */ + pcc_set(sock, (unsigned int)PLD_CPCR, 1); + pcc_set(sock, (unsigned int)PLD_CFBUFCR,0); /* force buffer off for ZA-36 */ + udelay(50); + } + status = pcc_get(sock, (unsigned int)PLD_CFBUFCR); + if (status != 0) { /* buffer off */ + pcc_set(sock, (unsigned int)PLD_CFBUFCR,0); + udelay(50); + pcc_set(sock, (unsigned int)PLD_CFRSTCR, 0x0101); + udelay(25); /* for IDE reset */ + pcc_set(sock, (unsigned int)PLD_CFRSTCR, 0x0100); + mdelay(2); /* for IDE reset */ + } else { + *value |= SS_POWERON; + *value |= SS_READY; + } + } +#else +#error no platform configuration +#endif + DEBUG(3, "m32r_cfc: _pcc_get_status: GetStatus(%d) = %#4.4x\n", + sock, *value); + return 0; +} /* _get_status */ + +/*====================================================================*/ + +static int _pcc_get_socket(u_short sock, socket_state_t *state) +{ +// pcc_socket_t *t = &socket[sock]; + +#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT) + state->flags = 0; + state->csc_mask = SS_DETECT; /* ??? */ + state->csc_mask |= SS_READY; /* ??? */ + state->io_irq = 0; + state->Vcc = 33; /* 3.3V fixed */ + state->Vpp = 33; +#endif + DEBUG(3, "m32r_cfc: GetSocket(%d) = flags %#3.3x, Vcc %d, Vpp %d, " + "io_irq %d, csc_mask %#2.2x\n", sock, state->flags, + state->Vcc, state->Vpp, state->io_irq, state->csc_mask); + return 0; +} /* _get_socket */ + +/*====================================================================*/ + +static int _pcc_set_socket(u_short sock, socket_state_t *state) +{ +#if defined(CONFIG_PLAT_MAPPI2) + u_long reg = 0; +#endif + DEBUG(3, "m32r_cfc: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, " + "io_irq %d, csc_mask %#2.2x)\n", sock, state->flags, + state->Vcc, state->Vpp, state->io_irq, state->csc_mask); + +#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) || defined(CONFIG_PLAT_OPSPUT) + if (state->Vcc) { + if ((state->Vcc != 50) && (state->Vcc != 33)) + return -EINVAL; + /* accept 5V and 3.3V */ + } +#elif defined(CONFIG_PLAT_MAPPI2) + if (state->Vcc) { + /* + * 5V only + */ + if (state->Vcc == 50) { + reg |= PCCSIGCR_VEN; + } else { + return -EINVAL; + } + } +#endif + + if (state->flags & SS_RESET) { + DEBUG(3, ":RESET\n"); + pcc_set(sock,(unsigned int)PLD_CFRSTCR,0x101); + }else{ + pcc_set(sock,(unsigned int)PLD_CFRSTCR,0x100); + } + if (state->flags & SS_OUTPUT_ENA){ + DEBUG(3, ":OUTPUT_ENA\n"); + /* bit clear */ + pcc_set(sock,(unsigned int)PLD_CFBUFCR,0); + } else { + pcc_set(sock,(unsigned int)PLD_CFBUFCR,1); + } + +#ifdef PCMCIA_DEBUG + if(state->flags & SS_IOCARD){ + DEBUG(3, ":IOCARD"); + } + if (state->flags & SS_PWR_AUTO) { + DEBUG(3, ":PWR_AUTO"); + } + if (state->csc_mask & SS_DETECT) + DEBUG(3, ":csc-SS_DETECT"); + if (state->flags & SS_IOCARD) { + if (state->csc_mask & SS_STSCHG) + DEBUG(3, ":STSCHG"); + } else { + if (state->csc_mask & SS_BATDEAD) + DEBUG(3, ":BATDEAD"); + if (state->csc_mask & SS_BATWARN) + DEBUG(3, ":BATWARN"); + if (state->csc_mask & SS_READY) + DEBUG(3, ":READY"); + } + DEBUG(3, "\n"); +#endif + return 0; +} /* _set_socket */ + +/*====================================================================*/ + +static int _pcc_set_io_map(u_short sock, struct pccard_io_map *io) +{ + u_char map; + + DEBUG(3, "m32r_cfc: SetIOMap(%d, %d, %#2.2x, %d ns, " + "%#4.4x-%#4.4x)\n", sock, io->map, io->flags, + io->speed, io->start, io->stop); + map = io->map; + + return 0; +} /* _set_io_map */ + +/*====================================================================*/ + +static int _pcc_set_mem_map(u_short sock, struct pccard_mem_map *mem) +{ + + u_char map = mem->map; + u_long mode; + u_long addr; + pcc_socket_t *t = &socket[sock]; + + DEBUG(3, "m32r_cfc: SetMemMap(%d, %d, %#2.2x, %d ns, %#5.5lx-%#5.5" + "lx, %#5.5x)\n", sock, map, mem->flags, mem->speed, + mem->sys_start, mem->sys_stop, mem->card_start); + + /* + * sanity check + */ + if ((map > MAX_WIN) || (mem->card_start > 0x3ffffff) || + (mem->sys_start > mem->sys_stop)) { + return -EINVAL; + } + + /* + * de-activate + */ + if ((mem->flags & MAP_ACTIVE) == 0) { + t->current_space = as_none; + return 0; + } + + /* + * Disable first + */ +// pcc_set(sock, PCCR, 0); + + /* + * Set mode + */ + if (mem->flags & MAP_ATTRIB) { + mode = PCMOD_AS_ATTRIB | PCMOD_CBSZ; + t->current_space = as_attr; + } else { + mode = 0; /* common memory */ + t->current_space = as_comm; + } +// pcc_set(sock, PCMOD, mode); + + /* + * Set address + */ + addr = t->mapaddr + (mem->card_start & M32R_PCC_MAPMASK); +// pcc_set(sock, PCADR, addr); + + mem->sys_start = addr + mem->card_start; + mem->sys_stop = mem->sys_start + (M32R_PCC_MAPSIZE - 1); + + /* + * Set timing + */ +// pcc_set(sock, PCATCR, pcc_access_timing); + + /* + * Enable again + */ +// pcc_set(sock, PCCR, 1); + + return 0; + +} /* _set_mem_map */ + +#if 0 /* driver model ordering issue */ +/*====================================================================== + + Routines for accessing socket information and register dumps via + /proc/bus/pccard/... + +======================================================================*/ + +static ssize_t show_info(struct class_device *class_dev, char *buf) +{ + pcc_socket_t *s = container_of(class_dev, struct pcc_socket, + socket.dev); + + return sprintf(buf, "type: %s\nbase addr: 0x%08lx\n", + pcc[s->type].name, s->base); +} + +static ssize_t show_exca(struct class_device *class_dev, char *buf) +{ + /* FIXME */ + + return 0; +} + +static CLASS_DEVICE_ATTR(info, S_IRUGO, show_info, NULL); +static CLASS_DEVICE_ATTR(exca, S_IRUGO, show_exca, NULL); +#endif + +/*====================================================================*/ + +/* this is horribly ugly... proper locking needs to be done here at + * some time... */ +#define LOCKED(x) do { \ + int retval; \ + unsigned long flags; \ + spin_lock_irqsave(&pcc_lock, flags); \ + retval = x; \ + spin_unlock_irqrestore(&pcc_lock, flags); \ + return retval; \ +} while (0) + + +static int pcc_get_status(struct pcmcia_socket *s, u_int *value) +{ + unsigned int sock = container_of(s, struct pcc_socket, socket)->number; + + if (socket[sock].flags & IS_ALIVE) { + DEBUG(3, "m32r_cfc: pcc_get_status: sock(%d) -EINVAL\n", sock); + *value = 0; + return -EINVAL; + } + DEBUG(3, "m32r_cfc: pcc_get_status: sock(%d)\n", sock); + LOCKED(_pcc_get_status(sock, value)); +} + +static int pcc_get_socket(struct pcmcia_socket *s, socket_state_t *state) +{ + unsigned int sock = container_of(s, struct pcc_socket, socket)->number; + + if (socket[sock].flags & IS_ALIVE) { + DEBUG(3, "m32r_cfc: pcc_get_socket: sock(%d) -EINVAL\n", sock); + return -EINVAL; + } + DEBUG(3, "m32r_cfc: pcc_get_socket: sock(%d)\n", sock); + LOCKED(_pcc_get_socket(sock, state)); +} + +static int pcc_set_socket(struct pcmcia_socket *s, socket_state_t *state) +{ + unsigned int sock = container_of(s, struct pcc_socket, socket)->number; + + if (socket[sock].flags & IS_ALIVE) { + DEBUG(3, "m32r_cfc: pcc_set_socket: sock(%d) -EINVAL\n", sock); + return -EINVAL; + } + DEBUG(3, "m32r_cfc: pcc_set_socket: sock(%d)\n", sock); + LOCKED(_pcc_set_socket(sock, state)); +} + +static int pcc_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io) +{ + unsigned int sock = container_of(s, struct pcc_socket, socket)->number; + + if (socket[sock].flags & IS_ALIVE) { + DEBUG(3, "m32r_cfc: pcc_set_io_map: sock(%d) -EINVAL\n", sock); + return -EINVAL; + } + DEBUG(3, "m32r_cfc: pcc_set_io_map: sock(%d)\n", sock); + LOCKED(_pcc_set_io_map(sock, io)); +} + +static int pcc_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem) +{ + unsigned int sock = container_of(s, struct pcc_socket, socket)->number; + + if (socket[sock].flags & IS_ALIVE) { + DEBUG(3, "m32r_cfc: pcc_set_mem_map: sock(%d) -EINVAL\n", sock); + return -EINVAL; + } + DEBUG(3, "m32r_cfc: pcc_set_mem_map: sock(%d)\n", sock); + LOCKED(_pcc_set_mem_map(sock, mem)); +} + +static int pcc_init(struct pcmcia_socket *s) +{ + DEBUG(3, "m32r_cfc: pcc_init()\n"); + return 0; +} + +static int pcc_suspend(struct pcmcia_socket *sock) +{ + DEBUG(3, "m32r_cfc: pcc_suspend()\n"); + return pcc_set_socket(sock, &dead_socket); +} + +static struct pccard_operations pcc_operations = { + .init = pcc_init, + .suspend = pcc_suspend, + .get_status = pcc_get_status, + .get_socket = pcc_get_socket, + .set_socket = pcc_set_socket, + .set_io_map = pcc_set_io_map, + .set_mem_map = pcc_set_mem_map, +}; + +/*====================================================================*/ + +static int m32rpcc_suspend(struct device *dev, u32 state, u32 level) +{ + int ret = 0; + if (level == SUSPEND_SAVE_STATE) + ret = pcmcia_socket_dev_suspend(dev, state); + return ret; +} + +static int m32rpcc_resume(struct device *dev, u32 level) +{ + int ret = 0; + if (level == RESUME_RESTORE_STATE) + ret = pcmcia_socket_dev_resume(dev); + return ret; +} + + +static struct device_driver pcc_driver = { + .name = "cfc", + .bus = &platform_bus_type, + .suspend = m32rpcc_suspend, + .resume = m32rpcc_resume, +}; + +static struct platform_device pcc_device = { + .name = "cfc", + .id = 0, +}; + +/*====================================================================*/ + +#define UT_CFC +static int __init init_m32r_pcc(void) +{ + int i, ret; + + ret = driver_register(&pcc_driver); + if (ret) + return ret; + + ret = platform_device_register(&pcc_device); + if (ret){ + driver_unregister(&pcc_driver); + return ret; + } + +#if defined(CONFIG_PLAT_MAPPI2) + pcc_set(0, (unsigned int)PLD_CFCR0, 0x0f0f); + pcc_set(0, (unsigned int)PLD_CFCR1, 0x0200); +#endif + + pcc_sockets = 0; + +#if !defined(CONFIG_PLAT_USRV) + add_pcc_socket(M32R_PCC0_BASE, PLD_IRQ_CFC_INSERT, CFC_ATTR_MAPBASE, + CFC_IOPORT_BASE); +#else /* CONFIG_PLAT_USRV */ + { + ulong base, mapaddr; + ioaddr_t ioaddr; + + for (i = 0 ; i < M32R_MAX_PCC ; i++) { + base = (ulong)PLD_CFRSTCR; + base = base | (i << 8); + ioaddr = (i + 1) << 12; + mapaddr = CFC_ATTR_MAPBASE | (i << 20); + add_pcc_socket(base, 0, mapaddr, ioaddr); + } + } +#endif /* CONFIG_PLAT_USRV */ + + if (pcc_sockets == 0) { + printk("socket is not found.\n"); + platform_device_unregister(&pcc_device); + driver_unregister(&pcc_driver); + return -ENODEV; + } + + /* Set up interrupt handler(s) */ + + for (i = 0 ; i < pcc_sockets ; i++) { + socket[i].socket.dev.dev = &pcc_device.dev; + socket[i].socket.ops = &pcc_operations; + socket[i].socket.owner = THIS_MODULE; + socket[i].number = i; + ret = pcmcia_register_socket(&socket[i].socket); + if (ret && i--) { + for (; i>= 0; i--) + pcmcia_unregister_socket(&socket[i].socket); + break; + } +#if 0 /* driver model ordering issue */ + class_device_create_file(&socket[i].socket.dev, + &class_device_attr_info); + class_device_create_file(&socket[i].socket.dev, + &class_device_attr_exca); +#endif + } + + /* Finally, schedule a polling interrupt */ + if (poll_interval != 0) { + poll_timer.function = pcc_interrupt_wrapper; + poll_timer.data = 0; + init_timer(&poll_timer); + poll_timer.expires = jiffies + poll_interval; + add_timer(&poll_timer); + } + + return 0; +} /* init_m32r_pcc */ + +static void __exit exit_m32r_pcc(void) +{ + int i; + + for (i = 0; i < pcc_sockets; i++) + pcmcia_unregister_socket(&socket[i].socket); + + platform_device_unregister(&pcc_device); + if (poll_interval != 0) + del_timer_sync(&poll_timer); + + driver_unregister(&pcc_driver); +} /* exit_m32r_pcc */ + +module_init(init_m32r_pcc); +module_exit(exit_m32r_pcc); +MODULE_LICENSE("Dual MPL/GPL"); +/*====================================================================*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/drivers/m32r_cfc.h 2004-09-03 00:57:17.246417408 -0700 @@ -0,0 +1,85 @@ +/* + * $Id$ + * + * Copyright (C) 2001 by Hiroyuki Kondo + */ + +#if !defined(CONFIG_PLAT_USRV) +#define M32R_MAX_PCC 2 +#else /* CONFIG_PLAT_USRV */ +#define M32R_MAX_PCC CONFIG_CFC_NUM +#endif /* CONFIG_PLAT_USRV */ + +/* + * M32R PC Card Controler + */ +#define M32R_PCC0_BASE 0x00ef7000 +#define M32R_PCC1_BASE 0x00ef7020 + +/* + * Register offsets + */ +#define PCCR 0x00 +#define PCADR 0x04 +#define PCMOD 0x08 +#define PCIRC 0x0c +#define PCCSIGCR 0x10 +#define PCATCR 0x14 + +/* + * PCCR + */ +#define PCCR_PCEN (1UL<<(31-31)) + +/* + * PCIRC + */ +#define PCIRC_BWERR (1UL<<(31-7)) +#define PCIRC_CDIN1 (1UL<<(31-14)) +#define PCIRC_CDIN2 (1UL<<(31-15)) +#define PCIRC_BEIEN (1UL<<(31-23)) +#define PCIRC_CIIEN (1UL<<(31-30)) +#define PCIRC_COIEN (1UL<<(31-31)) + +/* + * PCCSIGCR + */ +#define PCCSIGCR_SEN (1UL<<(31-3)) +#define PCCSIGCR_VEN (1UL<<(31-7)) +#define PCCSIGCR_CRST (1UL<<(31-15)) +#define PCCSIGCR_COCR (1UL<<(31-31)) + +/* + * + */ +#define PCMOD_AS_ATTRIB (1UL<<(31-19)) +#define PCMOD_AS_IO (1UL<<(31-18)) + +#define PCMOD_CBSZ (1UL<<(31-23)) /* set for 8bit */ + +#define PCMOD_DBEX (1UL<<(31-31)) /* set for excahnge */ + +/* + * M32R PCC Map addr + */ + +#define M32R_PCC0_MAPBASE 0x14000000 +#define M32R_PCC1_MAPBASE 0x16000000 + +#define M32R_PCC_MAPMAX 0x02000000 + +#define M32R_PCC_MAPSIZE 0x00001000 /* XXX */ +#define M32R_PCC_MAPMASK (~(M32R_PCC_MAPMAX-1)) + +#define CFC_IOPORT_BASE 0x1000 + +#if !defined(CONFIG_PLAT_USRV) +#define CFC_ATTR_MAPBASE 0x0c014000 +#define CFC_IO_MAPBASE_BYTE 0xac012000 +#define CFC_IO_MAPBASE_WORD 0xac002000 +#else /* CONFIG_PLAT_USRV */ +#define CFC_ATTR_MAPBASE 0x04014000 +#define CFC_IO_MAPBASE_BYTE 0xa4012000 +#define CFC_IO_MAPBASE_WORD 0xa4002000 +#endif /* CONFIG_PLAT_USRV */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/drivers/m32r_pcc.c 2004-09-03 00:57:17.249416952 -0700 @@ -0,0 +1,818 @@ +/*====================================================================== + + Device driver for the PCMCIA functionality of M32R. + +======================================================================*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* XXX: should be moved into asm/irq.h */ +#define PCC0_IRQ 24 +#define PCC1_IRQ 25 + +#define CHAOS_PCC_DEBUG +#ifdef CHAOS_PCC_DEBUG + static volatile u_short dummy_readbuf; +#endif + +#define PCC_DEBUG_DBEX + +#define PCMCIA_DEBUG 0 + +#include "m32r_pcc.h" + +#ifdef PCMCIA_DEBUG +int pc_debug = PCMCIA_DEBUG; +module_param(pc_debug, int, 0444); +#define DEBUG(n, args...) if (pc_debug>(n)) printk(args) +#else +#define DEBUG(n, args...) do { } while (0) +#endif + +/* Poll status interval -- 0 means default to interrupt */ +static int poll_interval = 0; + +typedef enum pcc_space { as_none = 0, as_comm, as_attr, as_io } pcc_as_t; + +typedef struct pcc_socket { + u_short type, flags; + struct pcmcia_socket socket; + unsigned int number; + ioaddr_t ioaddr; + u_long mapaddr; + u_long base; /* PCC register base */ + u_char cs_irq, intr; + pccard_io_map io_map[MAX_IO_WIN]; + pccard_mem_map mem_map[MAX_WIN]; + u_char io_win; + u_char mem_win; + pcc_as_t current_space; + u_char last_iodbex; +#ifdef CHAOS_PCC_DEBUG + u_char last_iosize; +#endif +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *proc; +#endif +} pcc_socket_t; + +static int pcc_sockets = 0; +static pcc_socket_t socket[M32R_MAX_PCC] = { + { 0, }, /* ... */ +}; + +#define ISA_LOCK(n, f) do { } while (0) +#define ISA_UNLOCK(n, f) do { } while (0) + +/*====================================================================*/ + +static unsigned int pcc_get(u_short, unsigned int); +static void pcc_set(u_short, unsigned int , unsigned int ); + +static spinlock_t pcc_lock = SPIN_LOCK_UNLOCKED; + +void pcc_iorw(int sock, unsigned long port, void *buf, size_t size, size_t nmemb, int wr, int flag) +{ + u_long addr; + u_long flags; + int need_ex; +#ifdef PCC_DEBUG_DBEX + int _dbex; +#endif + pcc_socket_t *t = &socket[sock]; +#ifdef CHAOS_PCC_DEBUG + int map_changed = 0; +#endif + + /* Need lock ? */ + spin_lock_irqsave(&pcc_lock, flags); + + /* + * Check if need dbex + */ + need_ex = (size > 1 && flag == 0) ? PCMOD_DBEX : 0; +#ifdef PCC_DEBUG_DBEX + _dbex = need_ex; + need_ex = 0; +#endif + + /* + * calculate access address + */ + addr = t->mapaddr + port - t->ioaddr + KSEG1; /* XXX */ + + /* + * Check current mapping + */ + if (t->current_space != as_io || t->last_iodbex != need_ex) { + + u_long cbsz; + + /* + * Disable first + */ + pcc_set(sock, PCCR, 0); + + /* + * Set mode and io address + */ + cbsz = (t->flags & MAP_16BIT) ? 0 : PCMOD_CBSZ; + pcc_set(sock, PCMOD, PCMOD_AS_IO | cbsz | need_ex); + pcc_set(sock, PCADR, addr & 0x1ff00000); + + /* + * Enable and read it + */ + pcc_set(sock, PCCR, 1); + +#ifdef CHAOS_PCC_DEBUG +#if 0 + map_changed = (t->current_space == as_attr && size == 2); /* XXX */ +#else + map_changed = 1; +#endif +#endif + t->current_space = as_io; + } + + /* + * access to IO space + */ + if (size == 1) { + /* Byte */ + unsigned char *bp = (unsigned char *)buf; + +#ifdef CHAOS_DEBUG + if (map_changed) { + dummy_readbuf = readb(addr); + } +#endif + if (wr) { + /* write Byte */ + while (nmemb--) { + writeb(*bp++, addr); + } + } else { + /* read Byte */ + while (nmemb--) { + *bp++ = readb(addr); + } + } + } else { + /* Word */ + unsigned short *bp = (unsigned short *)buf; + +#ifdef CHAOS_PCC_DEBUG + if (map_changed) { + dummy_readbuf = readw(addr); + } +#endif + if (wr) { + /* write Word */ + while (nmemb--) { +#ifdef PCC_DEBUG_DBEX + if (_dbex) { + unsigned char *cp = (unsigned char *)bp; + unsigned short tmp; + tmp = cp[1] << 8 | cp[0]; + writew(tmp, addr); + bp++; + } else +#endif + writew(*bp++, addr); + } + } else { + /* read Word */ + while (nmemb--) { +#ifdef PCC_DEBUG_DBEX + if (_dbex) { + unsigned char *cp = (unsigned char *)bp; + unsigned short tmp; + tmp = readw(addr); + cp[0] = tmp & 0xff; + cp[1] = (tmp >> 8) & 0xff; + bp++; + } else +#endif + *bp++ = readw(addr); + } + } + } + +#if 1 + /* addr is no longer used */ + if ((addr = pcc_get(sock, PCIRC)) & PCIRC_BWERR) { + printk("m32r_pcc: BWERR detected : port 0x%04lx : iosize %dbit\n", + port, size * 8); + pcc_set(sock, PCIRC, addr); + } +#endif + /* + * save state + */ + t->last_iosize = size; + t->last_iodbex = need_ex; + + /* Need lock ? */ + + spin_unlock_irqrestore(&pcc_lock,flags); + + return; +} + +void pcc_ioread(int sock, unsigned long port, void *buf, size_t size, size_t nmemb, int flag) { + pcc_iorw(sock, port, buf, size, nmemb, 0, flag); +} + +void pcc_iowrite(int sock, unsigned long port, void *buf, size_t size, size_t nmemb, int flag) { + pcc_iorw(sock, port, buf, size, nmemb, 1, flag); +} + +/*====================================================================*/ + +#define IS_ALIVE 0x8000 + +typedef struct pcc_t { + char *name; + u_short flags; +} pcc_t; + +static pcc_t pcc[] = { + { "xnux2", 0 }, { "xnux2", 0 }, +}; + +static irqreturn_t pcc_interrupt(int, void *, struct pt_regs *); + +/*====================================================================*/ + +static struct timer_list poll_timer; + +static unsigned int pcc_get(u_short sock, unsigned int reg) +{ + return inl(socket[sock].base + reg); +} + + +static void pcc_set(u_short sock, unsigned int reg, unsigned int data) +{ + outl(data, socket[sock].base + reg); +} + +/*====================================================================== + + See if a card is present, powered up, in IO mode, and already + bound to a (non PC Card) Linux driver. We leave these alone. + + We make an exception for cards that seem to be serial devices. + +======================================================================*/ + +static int __init is_alive(u_short sock) +{ + unsigned int stat; + unsigned int f; + + stat = pcc_get(sock, PCIRC); + f = (stat & (PCIRC_CDIN1 | PCIRC_CDIN2)) >> 16; + if(!f){ + printk("m32r_pcc: No Card is detected at socket %d : stat = 0x%08x\n",stat,sock); + return 0; + } + if(f!=3) + printk("m32r_pcc: Insertion fail (%.8x) at socket %d\n",stat,sock); + else + printk("m32r_pcc: Card is Inserted at socket %d(%.8x)\n",sock,stat); + return 0; +} + +static void add_pcc_socket(ulong base, int irq, ulong mapaddr, ioaddr_t ioaddr) +{ + pcc_socket_t *t = &socket[pcc_sockets]; + + /* add sockets */ + t->ioaddr = ioaddr; + t->mapaddr = mapaddr; + t->base = base; +#ifdef CHAOS_PCC_DEBUG + t->flags = MAP_16BIT; +#else + t->flags = 0; +#endif + if (is_alive(pcc_sockets)) + t->flags |= IS_ALIVE; + + /* add pcc */ + if (t->base > 0) { + request_region(t->base, 0x20, "m32r-pcc"); + } + + printk(KERN_INFO " %s ", pcc[pcc_sockets].name); + printk("pcc at 0x%08lx\n", t->base); + + /* Update socket interrupt information, capabilities */ + t->socket.features |= (SS_CAP_PCCARD | SS_CAP_STATIC_MAP); + t->socket.map_size = M32R_PCC_MAPSIZE; + t->socket.io_offset = ioaddr; /* use for io access offset */ + t->socket.irq_mask = 0; + t->socket.pci_irq = 2 + pcc_sockets; /* XXX */ + + request_irq(irq, pcc_interrupt, 0, "m32r-pcc", pcc_interrupt); + + pcc_sockets++; + + return; +} + + +/*====================================================================*/ + +static irqreturn_t pcc_interrupt(int irq, void *dev, struct pt_regs *regs) +{ + int i, j, irc; + u_int events, active; + int handled = 0; + + DEBUG(4, "m32r: pcc_interrupt(%d)\n", irq); + + for (j = 0; j < 20; j++) { + active = 0; + for (i = 0; i < pcc_sockets; i++) { + if ((socket[i].cs_irq != irq) && + (socket[i].socket.pci_irq != irq)) + continue; + handled = 1; + irc = pcc_get(i, PCIRC); + irc >>=16; + DEBUG(2, "m32r-pcc:interrput: socket %d pcirc 0x%02x ", i, irc); + if (!irc) + continue; + + events = (irc) ? SS_DETECT : 0; + events |= (pcc_get(i,PCCR) & PCCR_PCEN) ? SS_READY : 0; + DEBUG(2, " event 0x%02x\n", events); + + if (events) + pcmcia_parse_events(&socket[i].socket, events); + + active |= events; + active = 0; + } + if (!active) break; + } + if (j == 20) + printk(KERN_NOTICE "m32r-pcc: infinite loop in interrupt handler\n"); + + DEBUG(4, "m32r-pcc: interrupt done\n"); + + return IRQ_RETVAL(handled); +} /* pcc_interrupt */ + +static void pcc_interrupt_wrapper(u_long data) +{ + pcc_interrupt(0, NULL, NULL); + init_timer(&poll_timer); + poll_timer.expires = jiffies + poll_interval; + add_timer(&poll_timer); +} + +/*====================================================================*/ + +static int _pcc_get_status(u_short sock, u_int *value) +{ + u_int status; + + status = pcc_get(sock,PCIRC); + *value = ((status & PCIRC_CDIN1) && (status & PCIRC_CDIN2)) + ? SS_DETECT : 0; + + status = pcc_get(sock,PCCR); + +#if 0 + *value |= (status & PCCR_PCEN) ? SS_READY : 0; +#else + *value |= SS_READY; /* XXX: always */ +#endif + + status = pcc_get(sock,PCCSIGCR); + *value |= (status & PCCSIGCR_VEN) ? SS_POWERON : 0; + + DEBUG(3, "m32r-pcc: GetStatus(%d) = %#4.4x\n", sock, *value); + return 0; +} /* _get_status */ + +/*====================================================================*/ + +static int _pcc_get_socket(u_short sock, socket_state_t *state) +{ + DEBUG(3, "m32r-pcc: GetSocket(%d) = flags %#3.3x, Vcc %d, Vpp %d, " + "io_irq %d, csc_mask %#2.2x\n", sock, state->flags, + state->Vcc, state->Vpp, state->io_irq, state->csc_mask); + return 0; +} /* _get_socket */ + +/*====================================================================*/ + +static int _pcc_set_socket(u_short sock, socket_state_t *state) +{ + u_long reg = 0; + + DEBUG(3, "m32r-pcc: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, " + "io_irq %d, csc_mask %#2.2x)", sock, state->flags, + state->Vcc, state->Vpp, state->io_irq, state->csc_mask); + + if (state->Vcc) { + /* + * 5V only + */ + if (state->Vcc == 50) { + reg |= PCCSIGCR_VEN; + } else { + return -EINVAL; + } + } + + if (state->flags & SS_RESET) { + DEBUG(3, ":RESET\n"); + reg |= PCCSIGCR_CRST; + } + if (state->flags & SS_OUTPUT_ENA){ + DEBUG(3, ":OUTPUT_ENA\n"); + /* bit clear */ + } else { + reg |= PCCSIGCR_SEN; + } + + pcc_set(sock,PCCSIGCR,reg); + +#ifdef PCMCIA_DEBUG + if(state->flags & SS_IOCARD){ + DEBUG(3, ":IOCARD"); + } + if (state->flags & SS_PWR_AUTO) { + DEBUG(3, ":PWR_AUTO"); + } + if (state->csc_mask & SS_DETECT) + DEBUG(3, ":csc-SS_DETECT"); + if (state->flags & SS_IOCARD) { + if (state->csc_mask & SS_STSCHG) + DEBUG(3, ":STSCHG"); + } else { + if (state->csc_mask & SS_BATDEAD) + DEBUG(3, ":BATDEAD"); + if (state->csc_mask & SS_BATWARN) + DEBUG(3, ":BATWARN"); + if (state->csc_mask & SS_READY) + DEBUG(3, ":READY"); + } + DEBUG(3, "\n"); +#endif + return 0; +} /* _set_socket */ + +/*====================================================================*/ + +static int _pcc_set_io_map(u_short sock, struct pccard_io_map *io) +{ + u_char map; + + DEBUG(3, "m32r-pcc: SetIOMap(%d, %d, %#2.2x, %d ns, " + "%#4.4x-%#4.4x)\n", sock, io->map, io->flags, + io->speed, io->start, io->stop); + map = io->map; + + return 0; +} /* _set_io_map */ + +/*====================================================================*/ + +static int _pcc_set_mem_map(u_short sock, struct pccard_mem_map *mem) +{ + + u_char map = mem->map; + u_long mode; + u_long addr; + pcc_socket_t *t = &socket[sock]; +#ifdef CHAOS_PCC_DEBUG +#if 0 + pcc_as_t last = t->current_space; +#endif +#endif + + DEBUG(3, "m32r-pcc: SetMemMap(%d, %d, %#2.2x, %d ns, %#5.5lx-%#5.5" + "lx, %#5.5x)\n", sock, map, mem->flags, mem->speed, + mem->sys_start, mem->sys_stop, mem->card_start); + + /* + * sanity check + */ + if ((map > MAX_WIN) || (mem->card_start > 0x3ffffff) || + (mem->sys_start > mem->sys_stop)) { + return -EINVAL; + } + + /* + * de-activate + */ + if ((mem->flags & MAP_ACTIVE) == 0) { + t->current_space = as_none; + return 0; + } + + /* + * Disable first + */ + pcc_set(sock, PCCR, 0); + + /* + * Set mode + */ + if (mem->flags & MAP_ATTRIB) { + mode = PCMOD_AS_ATTRIB | PCMOD_CBSZ; + t->current_space = as_attr; + } else { + mode = 0; /* common memory */ + t->current_space = as_comm; + } + pcc_set(sock, PCMOD, mode); + + /* + * Set address + */ + addr = t->mapaddr + (mem->card_start & M32R_PCC_MAPMASK); + pcc_set(sock, PCADR, addr); + + mem->sys_start = addr + mem->card_start; + mem->sys_stop = mem->sys_start + (M32R_PCC_MAPSIZE - 1); + + /* + * Enable again + */ + pcc_set(sock, PCCR, 1); + +#ifdef CHAOS_PCC_DEBUG +#if 0 + if (last != as_attr) { +#else + if (1) { +#endif + + dummy_readbuf = *(u_char *)(addr + KSEG1); + } +#endif + + return 0; + +} /* _set_mem_map */ + +#if 0 /* driver model ordering issue */ +/*====================================================================== + + Routines for accessing socket information and register dumps via + /proc/bus/pccard/... + +======================================================================*/ + +static ssize_t show_info(struct class_device *class_dev, char *buf) +{ + pcc_socket_t *s = container_of(class_dev, struct pcc_socket, + socket.dev); + + return sprintf(buf, "type: %s\nbase addr: 0x%08lx\n", + pcc[s->type].name, s->base); +} + +static ssize_t show_exca(struct class_device *class_dev, char *buf) +{ + /* FIXME */ + + return 0; +} + +static CLASS_DEVICE_ATTR(info, S_IRUGO, show_info, NULL); +static CLASS_DEVICE_ATTR(exca, S_IRUGO, show_exca, NULL); +#endif + +/*====================================================================*/ + +/* this is horribly ugly... proper locking needs to be done here at + * some time... */ +#define LOCKED(x) do { \ + int retval; \ + unsigned long flags; \ + spin_lock_irqsave(&pcc_lock, flags); \ + retval = x; \ + spin_unlock_irqrestore(&pcc_lock, flags); \ + return retval; \ +} while (0) + + +static int pcc_get_status(struct pcmcia_socket *s, u_int *value) +{ + unsigned int sock = container_of(s, struct pcc_socket, socket)->number; + + if (socket[sock].flags & IS_ALIVE) { + *value = 0; + return -EINVAL; + } + LOCKED(_pcc_get_status(sock, value)); +} + +static int pcc_get_socket(struct pcmcia_socket *s, socket_state_t *state) +{ + unsigned int sock = container_of(s, struct pcc_socket, socket)->number; + + if (socket[sock].flags & IS_ALIVE) + return -EINVAL; + LOCKED(_pcc_get_socket(sock, state)); +} + +static int pcc_set_socket(struct pcmcia_socket *s, socket_state_t *state) +{ + unsigned int sock = container_of(s, struct pcc_socket, socket)->number; + + if (socket[sock].flags & IS_ALIVE) + return -EINVAL; + + LOCKED(_pcc_set_socket(sock, state)); +} + +static int pcc_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io) +{ + unsigned int sock = container_of(s, struct pcc_socket, socket)->number; + + if (socket[sock].flags & IS_ALIVE) + return -EINVAL; + LOCKED(_pcc_set_io_map(sock, io)); +} + +static int pcc_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem) +{ + unsigned int sock = container_of(s, struct pcc_socket, socket)->number; + + if (socket[sock].flags & IS_ALIVE) + return -EINVAL; + LOCKED(_pcc_set_mem_map(sock, mem)); +} + +static int pcc_init(struct pcmcia_socket *s) +{ + DEBUG(4, "m32r-pcc: init call\n"); + return 0; +} + +static int pcc_suspend(struct pcmcia_socket *sock) +{ + return pcc_set_socket(sock, &dead_socket); +} + +static struct pccard_operations pcc_operations = { + .init = pcc_init, + .suspend = pcc_suspend, + .get_status = pcc_get_status, + .get_socket = pcc_get_socket, + .set_socket = pcc_set_socket, + .set_io_map = pcc_set_io_map, + .set_mem_map = pcc_set_mem_map, +}; + +/*====================================================================*/ + +static int m32r_pcc_suspend(struct device *dev, u32 state, u32 level) +{ + int ret = 0; + if (level == SUSPEND_SAVE_STATE) + ret = pcmcia_socket_dev_suspend(dev, state); + return ret; +} + +static int m32r_pcc_resume(struct device *dev, u32 level) +{ + int ret = 0; + if (level == RESUME_RESTORE_STATE) + ret = pcmcia_socket_dev_resume(dev); + return ret; +} + + +static struct device_driver pcc_driver = { + .name = "pcc", + .bus = &platform_bus_type, + .suspend = m32r_pcc_suspend, + .resume = m32r_pcc_resume, +}; + +static struct platform_device pcc_device = { + .name = "pcc", + .id = 0, +}; + +/*====================================================================*/ + +static int __init init_m32r_pcc(void) +{ + int i, ret; + + ret = driver_register(&pcc_driver); + if (ret) + return ret; + + ret = platform_device_register(&pcc_device); + if (ret){ + driver_unregister(&pcc_driver); + return ret; + } + + printk(KERN_INFO "m32r PCC probe:\n"); + + pcc_sockets = 0; + + add_pcc_socket(M32R_PCC0_BASE, PCC0_IRQ, M32R_PCC0_MAPBASE, 0x1000); + +#ifdef CONFIG_M32RPCC_SLOT2 + add_pcc_socket(M32R_PCC1_BASE, PCC1_IRQ, M32R_PCC1_MAPBASE, 0x2000); +#endif + + if (pcc_sockets == 0) { + printk("socket is not found.\n"); + platform_device_unregister(&pcc_device); + driver_unregister(&pcc_driver); + return -ENODEV; + } + + /* Set up interrupt handler(s) */ + + for (i = 0 ; i < pcc_sockets ; i++) { + socket[i].socket.dev.dev = &pcc_device.dev; + socket[i].socket.ops = &pcc_operations; + socket[i].socket.owner = THIS_MODULE; + socket[i].number = i; + ret = pcmcia_register_socket(&socket[i].socket); + if (ret && i--) { + for (; i>= 0; i--) + pcmcia_unregister_socket(&socket[i].socket); + break; + } +#if 0 /* driver model ordering issue */ + class_device_create_file(&socket[i].socket.dev, + &class_device_attr_info); + class_device_create_file(&socket[i].socket.dev, + &class_device_attr_exca); +#endif + } + + /* Finally, schedule a polling interrupt */ + if (poll_interval != 0) { + poll_timer.function = pcc_interrupt_wrapper; + poll_timer.data = 0; + init_timer(&poll_timer); + poll_timer.expires = jiffies + poll_interval; + add_timer(&poll_timer); + } + + return 0; +} /* init_m32r_pcc */ + +static void __exit exit_m32r_pcc(void) +{ + int i; + + for (i = 0; i < pcc_sockets; i++) + pcmcia_unregister_socket(&socket[i].socket); + + platform_device_unregister(&pcc_device); + if (poll_interval != 0) + del_timer_sync(&poll_timer); + + driver_unregister(&pcc_driver); +} /* exit_m32r_pcc */ + +module_init(init_m32r_pcc); +module_exit(exit_m32r_pcc); +MODULE_LICENSE("Dual MPL/GPL"); +/*====================================================================*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/drivers/m32r_pcc.h 2004-09-03 00:57:17.249416952 -0700 @@ -0,0 +1,70 @@ +/* + * $Id$ + * + * Copyright (C) 2001 by Hiroyuki Kondo + */ + + + +#define M32R_MAX_PCC 2 + +/* + * M32R PC Card Controler + */ +#define M32R_PCC0_BASE 0x00ef7000 +#define M32R_PCC1_BASE 0x00ef7020 + +/* + * Register offsets + */ +#define PCCR 0x00 +#define PCADR 0x04 +#define PCMOD 0x08 +#define PCIRC 0x0c +#define PCCSIGCR 0x10 +#define PCATCR 0x14 + +/* + * PCCR + */ +#define PCCR_PCEN (1UL<<(31-31)) + +/* + * PCIRC + */ +#define PCIRC_BWERR (1UL<<(31-7)) +#define PCIRC_CDIN1 (1UL<<(31-14)) +#define PCIRC_CDIN2 (1UL<<(31-15)) +#define PCIRC_BEIEN (1UL<<(31-23)) +#define PCIRC_CIIEN (1UL<<(31-30)) +#define PCIRC_COIEN (1UL<<(31-31)) + +/* + * PCCSIGCR + */ +#define PCCSIGCR_SEN (1UL<<(31-3)) +#define PCCSIGCR_VEN (1UL<<(31-7)) +#define PCCSIGCR_CRST (1UL<<(31-15)) +#define PCCSIGCR_COCR (1UL<<(31-31)) + +/* + * + */ +#define PCMOD_AS_ATTRIB (1UL<<(31-19)) +#define PCMOD_AS_IO (1UL<<(31-18)) + +#define PCMOD_CBSZ (1UL<<(31-23)) /* set for 8bit */ + +#define PCMOD_DBEX (1UL<<(31-31)) /* set for excahnge */ + +/* + * M32R PCC Map addr + */ +#define M32R_PCC0_MAPBASE 0x14000000 +#define M32R_PCC1_MAPBASE 0x16000000 + +#define M32R_PCC_MAPMAX 0x02000000 + +#define M32R_PCC_MAPSIZE 0x00001000 /* XXX */ +#define M32R_PCC_MAPMASK (~(M32R_PCC_MAPMAX-1)) + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/drivers/m32r-pldsio.c 2004-09-03 00:57:17.263414824 -0700 @@ -0,0 +1,3067 @@ +/* $Id$ + * + * M32R onboard PLD serial module support. + * + * Much of the design and some of the code came from serial.c: + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, + * 1998, 1999 Theodore Ts'o + * + * M32R work: + * Copyright 1996, 2001, Mitsubishi Electric Corporation + * Copyright (C) 2000,2001 by Hiro Kondo, Hiro Takata, and Hitoshi Yamamoto. + * + * 2002-12-25: Support M32700UT Platform by Takeo Takahashi + * Derived from dbg_console.c. + */ + +static char *serial_version = "kondo"; +static char *serial_revdate = "2002-09-11"; +static char *serial_name = "M32R Serial driver"; + +#define LOCAL_VERSTRING "" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* serial_state */ +#include /* kmalloc */ + +#include +#include +#include +#include +#include + +extern struct console console_for_debug; + +static int psio_write(struct tty_struct *tty, int from_user, + const unsigned char *buf, int count); +static int psio_write_room(struct tty_struct *tty); +static int psio_chars_in_buffer(struct tty_struct *tty); + +static void dbg_console_write(struct console *, const char *, unsigned); +static kdev_t dbg_console_device(struct console *c); +//static void psio_interrupt_single(int, void *, struct pt_regs *); +void psio_interrupt_single(int, void *, struct pt_regs *); +static void psio_receive_chars(struct async_struct *, int *); + +static void psio_wait_until_sent(struct tty_struct *, int); +static void change_speed(struct async_struct *,struct termios *); +static void autoconfig(struct serial_state *); +static unsigned detect_uart_irq (struct serial_state *); + +static struct tty_driver psio_driver; +static int psio_refcount; + +#define RS_STROBE_TIME (10*HZ) +#define RS_ISR_PASS_LIMIT 256 + +/* number of characters left in xmit buffer before we ask for more */ +#define WAKEUP_CHARS 256 + +static struct async_struct *IRQ_ports[NR_IRQS]; +static int IRQ_timeout[NR_IRQS]; +#ifdef CONFIG_SERIAL_CONSOLE +static struct console cons; +static int lsr_break_flag; +#endif + +#define BAUDRATE 115200 /* Set Baudrate */ + +/* + * Here we define the default xmit fifo size used for each type of + * UART + */ +static struct serial_uart_config uart_config[] = { + { "unknown", 1, 0 }, + { "8250", 1, 0 }, + { "16450", 1, 0 }, + { "16550", 1, 0 }, + { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "cirrus", 1, 0 }, /* usurped by cyclades.c */ + { "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH }, + { "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO | + UART_STARTECH }, + { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO}, + { "Startech", 1, 0}, /* usurped by cyclades.c */ + { "16C950/954", 128, UART_CLEAR_FIFO | UART_USE_FIFO}, + { "ST16654", 64, UART_CLEAR_FIFO | UART_USE_FIFO | + UART_STARTECH }, + { "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO | + UART_STARTECH }, + { "RSA", 2048, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "m32102", 1, 0 }, + { 0, 0} +}; + +static struct serial_state rs_table[RS_TABLE_SIZE] = { +/* UART CLK PORT IRQ FLAGS */ + { 0, BAUDRATE, ((int)PLD_ESIO0CR + NONCACHE_OFFSET), PLD_IRQ_SIO0_RCV, STD_COM_FLAGS }, +}; +#define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state)) + + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) + +static DECLARE_TASK_QUEUE(tq_psio_serial); +static struct tty_struct *psio_table[NR_PORTS]; +static struct termios *psio_termios[NR_PORTS]; +static struct termios *psio_termios_locked[NR_PORTS]; + +#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT) +#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \ + kdevname(tty->device), (info->flags), serial_refcount,info->count,tty->count,s +) +#else +#define DBG_CNT(s) +#endif + + +static struct timer_list serial_timer; + +static unsigned char *tmp_buf; +#ifdef DECLARE_MUTEX +static DECLARE_MUTEX(tmp_buf_sem); +#else +static struct semaphore tmp_buf_sem = MUTEX; +#endif + +static int dbg_console_setup(struct console *console, char *options); + +#undef SIO0CR +#undef SIO0MOD0 +#undef SIO0MOD1 +#undef SIO0STS +#undef SIO0IMASK +#undef SIO0BAUR +#undef SIO0TXB +#undef SIO0RXB +#define SIO0CR PLD_ESIO0CR +#define SIO0MOD0 PLD_ESIO0MOD0 +#define SIO0MOD1 PLD_ESIO0MOD1 +#define SIO0STS PLD_ESIO0STS +#define SIO0IMASK PLD_ESIO0INTCR +#define SIO0BAUR PLD_ESIO0BAUR +#define SIO0TXB PLD_ESIO0TXB +#define SIO0RXB PLD_ESIO0RXB + +#define SIO_IMASK_TEMPIE (1UL<<1) /* b14: enable */ +#define SIO_IMASK_RXCEN (1UL<<2) /* b13: enable */ +#define SIO_IMASK_REIE (0UL) +#define SIO_SIO0STS_TEMP (1UL<<0) /* Transmitter Register Empty */ +#define SIO_SIO0STS_TXCP (1UL<<1) +#define SIO_SIO0STS_RXCP (1UL<<2) +#define SIO_SIO0STS_OERR (0UL) +#define SIO_SIO0STS_PERR (0UL) +#define SIO_SIO0STS_FERR (0UL) +#define SIO_SIO0MOD0_CTSS (1UL<<6) +#define SIO_SIO0MOD0_RTSS (1UL<<7) +#define SIO_NONE (0UL) + +#define UART_TX ((unsigned char *)SIO0TXB - (unsigned char *)SIO0CR) +#define UART_RX ((unsigned char *)SIO0RXB - (unsigned char *)SIO0CR) +#define UART_IER ((unsigned char *)SIO0IMASK - (unsigned char *)SIO0CR) +#define UART_IER_THRI SIO_IMASK_TEMPIE +#define UART_IER_MSI SIO_NONE +#define UART_IER_RLSI SIO_IMASK_RXCEN +#define UART_IER_RDI SIO_IMASK_REIE +#define UART_LSR ((unsigned char *)SIO0STS - (unsigned char *)SIO0CR) +#define UART_LSR_DR SIO_SIO0STS_RXCP +#define UART_LSR_THRE SIO_SIO0STS_TEMP +#define UART_LSR_TEMT SIO_SIO0STS_TXCP +#define UART_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) +#define UART_LSR_BI SIO_NONE +#define UART_LSR_PE SIO_SIO0STS_PERR +#define UART_LSR_FE SIO_SIO0STS_FERR +#define UART_LSR_OE SIO_SIO0STS_OERR +#define UART_IIR ((unsigned char *)SIO0STS - (unsigned char *)SIO0CR) +#define UART_LCR ((unsigned char *)SIO0CR - (unsigned char *)SIO0CR) +#define UART_LCR_SBC SIO_NONE +#define UART_LCR_PARITY SIO_NONE +#define UART_LCR_EPAR SIO_NONE +#define UART_LCR_SPAR SIO_NONE +#define UART_MCR ((unsigned char *)SIO0MOD0 - (unsigned char *)SIO0CR) +#define UART_MCR_RTS SIO_SIO0MOD0_RTSS +#define UART_MCR_DTR SIO_NONE /* SIO_SIO0MOD0_CTSS */ +#define UART_MCR_LOOP SIO_NONE +#define UART_MCR_OUT1 SIO_NONE +#define UART_MCR_OUT2 SIO_NONE +#define UART_MSR ((unsigned char *)SIO0MOD0 - (unsigned char *)SIO0CR) +#define UART_MSR_DCD SIO_NONE +#define UART_MSR_RI SIO_NONE +#define UART_MSR_DSR SIO_NONE +#define UART_MSR_CTS SIO_NONE + +#define UART_BAUR ((unsigned char *)SIO0BAUR - (unsigned char *)SIO0CR) +#define UART_MOD0 ((unsigned char *)SIO0MOD0 - (unsigned char *)SIO0CR) +#define UART_MOD1 ((unsigned char *)SIO0MOD1 - (unsigned char *)SIO0CR) + +static inline unsigned int psio_in(struct async_struct *info,int offset) +{ + return *(volatile unsigned short *)(info->port + offset); +} +static inline void psio_out(struct async_struct *info,int offset,int value) +{ + *(volatile unsigned short *)(info->port + offset) = value; +} + +#define serial_in(info, offset) psio_in(info,(int)offset) +#define serial_out(info, offset, value) psio_out(info,(int)offset, value) +#define serial_inp(info, offset) psio_in(info,(int)offset) +#define serial_outp(info, offset, value) psio_out(info,(int)offset, value) + + +static inline int serial_paranoia_check(struct async_struct *info, + kdev_t device, const char *routine) +{ +#ifdef SERIAL_PARANOIA_CHECK + static const char *badmagic = + "Warning: bad magic number for serial struct (%s) in %s\n"; + static const char *badinfo = + "Warning: null async_struct for (%s) in %s\n"; + + if (!info) { + printk(badinfo, kdevname(device), routine); + return 1; + } + if (info->magic != SERIAL_MAGIC) { + printk(badmagic, kdevname(device), routine); + return 1; + } +#endif + return 0; +} + +/* + * ------------------------------------------------------------ + * psio_stop() and psio_start() + * + * This routines are called before setting or resetting tty->stopped. + * They enable or disable transmitter interrupts, as necessary. + * ------------------------------------------------------------ + */ +static void psio_stop(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "psio_stop")) + return; + + save_flags(flags); cli(); + if (info->IER & UART_IER_THRI) { + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } + restore_flags(flags); + +} + +static void psio_start(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "psio_start")) + return; + + save_flags(flags); cli(); + if (info->xmit.head != info->xmit.tail + && info->xmit.buf + && !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + serial_out(info, UART_TX, info->xmit.buf[info->xmit.tail]); + info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1); + info->state->icount.tx++; + } + restore_flags(flags); +} + + +/* + * This routine is used by the interrupt handler to schedule + * processing in the software interrupt portion of the driver. + */ +static inline void psio_sched_event(struct async_struct *info,int event) +{ + info->event |= 1 << event; + queue_task(&info->tqueue, &tq_psio_serial); + mark_bh(SERIAL_BH); +} + +static inline void psio_receive_chars(struct async_struct *info,int *status) +{ + struct tty_struct *tty = info->tty; + unsigned char ch; + struct async_icount *icount; + int max_count = 256; + + icount = &info->state->icount; + do { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + tty->flip.tqueue.routine((void *) tty); + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + return; // if TTY_DONT_FLIP is set + } + ch = serial_inp(info, UART_RX); + *tty->flip.char_buf_ptr = ch; + icount->rx++; + +#ifdef SERIAL_DEBUG_INTR + printk("DR%02x:%02x...", ch, *status); +#endif + *tty->flip.flag_buf_ptr = 0; + if (*status & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE)) { + /* + * For statistics only + */ + if (*status & UART_LSR_BI) { + *status &= ~(UART_LSR_FE | UART_LSR_PE); + icount->brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ +#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) + if (info->line == cons.index) { + if (!break_pressed) { + break_pressed = jiffies; + goto ignore_char; + } + break_pressed = 0; + } +#endif + if (info->flags & ASYNC_SAK) + do_SAK(tty); + } else if (*status & UART_LSR_PE) + icount->parity++; + else if (*status & UART_LSR_FE) + icount->frame++; + if (*status & UART_LSR_OE) + icount->overrun++; + + /* + * Mask off conditions which should be ignored. + */ + *status &= info->read_status_mask; + +#ifdef CONFIG_SERIAL_CONSOLE + if (info->line == cons.index) { + /* Recover the break flag from console xmit */ + *status |= lsr_break_flag; + lsr_break_flag = 0; + } +#endif + if (*status & (UART_LSR_BI)) { +#ifdef SERIAL_DEBUG_INTR + printk("handling break...."); +#endif + *tty->flip.flag_buf_ptr = TTY_BREAK; + } else if (*status & UART_LSR_PE) + *tty->flip.flag_buf_ptr = TTY_PARITY; + else if (*status & UART_LSR_FE) + *tty->flip.flag_buf_ptr = TTY_FRAME; + } +#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) + if (break_pressed && info->line == cons.index) { + if (ch != 0 && + time_before(jiffies, break_pressed + HZ*5)) { + handle_sysrq(ch, regs, NULL, NULL); + break_pressed = 0; + goto ignore_char; + } + break_pressed = 0; + } +#endif + if ((*status & info->ignore_status_mask) == 0) { + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } +#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) + ignore_char: +#endif + *status = serial_inp(info, UART_LSR); + } while ((*status & UART_LSR_DR) && (max_count-- > 0)); +#if (LINUX_VERSION_CODE > 131394) /* 2.1.66 */ + tty_flip_buffer_push(tty); +#else + queue_task_irq_off(&tty->flip.tqueue, &tq_timer); +#endif +} + +static void transmit_chars(struct async_struct *info, int *intr_done) +{ + int count; + + if (info->x_char) { + // for M32102 serial + // serial_outp(info, UART_TX, info->x_char); + info->state->icount.tx++; + info->x_char = 0; + if (intr_done) + *intr_done = 0; + while((serial_in(info,UART_LSR) & UART_LSR_TEMT) == 0); + return; + } + if (info->xmit.head == info->xmit.tail + || info->tty->stopped + || info->tty->hw_stopped) { + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + return; + } + count = info->xmit_fifo_size; + count = SERIAL_XMIT_SIZE-1; + do { + serial_out(info, UART_TX, info->xmit.buf[info->xmit.tail]); + info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1); + info->state->icount.tx++; + + if (info->xmit.head == info->xmit.tail) + break; + + while((serial_in(info,UART_LSR) & UART_LSR_THRE) == 0); + } while (--count > 0); + while((serial_in(info,UART_LSR) & UART_LSR_TEMT) == 0); + + if (CIRC_CNT(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) < WAKEUP_CHARS) + psio_sched_event(info, RS_EVENT_WRITE_WAKEUP); + +#ifdef SERIAL_DEBUG_INTR + printk("THRE..."); +#endif + if (intr_done) + *intr_done = 0; + + if (info->xmit.head == info->xmit.tail) { + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } +} + + +/* + +#ifdef CONFIG_SERIAL_SHARE_IRQ +static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs) +#endif +*/ + +static void sio_reset(struct async_struct *info) +{ + unsigned int dummy; + /* reset sio */ + /* read receive buffer */ + dummy = serial_inp(info, UART_RX); + dummy = serial_inp(info, UART_RX); + dummy = serial_inp(info, UART_LSR); + serial_outp(info, UART_LCR, 0x0300); /* RSCLR:1, TSCLR:1 */ + serial_outp(info, UART_LCR, 0x0003); /* RSEN:1, TXEN:1 */ +} + +static void sio_error(struct async_struct *info,int status) +{ + unsigned int dummy; + /* reset sio */ + printk("sio[%d] error[%04x]\n", info->line,status); + /* read receive buffer */ + dummy = serial_inp(info, UART_RX); + dummy = serial_inp(info, UART_RX); + dummy = serial_inp(info, UART_LSR); + serial_outp(info, UART_LCR, 0x0300); /* RSCLR:1, TSCLR:1 */ + serial_outp(info, UART_LCR, 0x0003); /* RSEN:1, TXEN:1 */ +} + +//static void psio_interrupt_single(int irq, void *dev_id, struct pt_regs * regs) +void psio_interrupt_single(int irq, void *dev_id, struct pt_regs * regs) +{ + int status; + // int pass_counter = 0; + struct async_struct * info; + +#ifdef CONFIG_SERIAL_MULTIPORT + int first_multi = 0; + struct rs_multiport_struct *multi; +#endif + +#ifdef SERIAL_DEBUG_INTR + printk("psio_interrupt_single(%d)...", irq); +#endif + + info = IRQ_ports[irq&(~1)]; + if (!info || !info->tty) + return; + +#ifdef CONFIG_SERIAL_MULTIPORT + multi = &rs_multiport[irq]; + if (multi->port_monitor) + first_multi = inb(multi->port_monitor); +#endif + + { + status = serial_inp(info, UART_LSR); +#ifdef SERIAL_DEBUG_INTR + printk("status = %x...", status); +#endif + if (status & UART_LSR_DR){ + psio_receive_chars(info, &status); + } + if ((serial_in(info,UART_LSR) & UART_EMPTY) != UART_EMPTY) + sio_error(info, status); + if (status & UART_LSR_THRE) + transmit_chars(info, 0); +#ifdef SERIAL_DEBUG_INTR + printk("IIR = %x...", serial_in(info, UART_IIR)); +#endif + } + + info->last_active = jiffies; +#ifdef CONFIG_SERIAL_MULTIPORT + if (multi->port_monitor) + printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n", + info->state->irq, first_multi, + inb(multi->port_monitor)); +#endif +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} +#ifdef CONFIG_SERIAL_MULTIPORT +static void rs_interrupt_multi(int irq, void *dev_id, struct pt_regs * regs) +{} +#endif + +static void do_psio_serial_bh(void) +{ + run_task_queue(&tq_psio_serial); +} + +static void do_softint(void *private_) +{ + struct async_struct *info = (struct async_struct *) private_; + struct tty_struct *tty; + + tty = info->tty; + if (!tty) + return; + + if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); +#ifdef SERIAL_HAVE_POLL_WAIT + wake_up_interruptible(&tty->poll_wait); +#endif + } +} + +static void psio_timer(void) +{ + static unsigned long last_strobe = 0; + struct async_struct *info; + unsigned int i; + unsigned long flags; + + if ((jiffies - last_strobe) >= RS_STROBE_TIME) { + for (i=0; i < NR_IRQS; i++) { + info = IRQ_ports[i]; + if (!info) + continue; + save_flags(flags); cli(); + psio_interrupt_single(i, NULL, NULL); + restore_flags(flags); + } + } + last_strobe = jiffies; + +#if 1 + mod_timer(&serial_timer, jiffies + 10); +#else + mod_timer(&serial_timer, jiffies + RS_STROBE_TIME); +#endif + + if (IRQ_ports[0]) { + save_flags(flags); cli(); +#ifdef CONFIG_SERIAL_SHARE_IRQ + psio_interrupt(0, NULL, NULL); +#else + psio_interrupt_single(0, NULL, NULL); +#endif + restore_flags(flags); + + mod_timer(&serial_timer, jiffies + IRQ_timeout[0]); + } +} + +/* + * --------------------------------------------------------------- + * Low level utility subroutines for the serial driver: routines to + * figure out the appropriate timeout for an interrupt chain, routines + * to initialize and startup a serial port, and routines to shutdown a + * serial port. Useful stuff like that. + * --------------------------------------------------------------- + */ + +/* + * This routine figures out the correct timeout for a particular IRQ. + * It uses the smallest timeout of all of the serial ports in a + * particular interrupt chain. Now only used for IRQ 0.... + */ +static void figure_IRQ_timeout(int irq) +{ + struct async_struct *info; + int timeout = 60*HZ; /* 60 seconds === a long time :-) */ + + info = IRQ_ports[irq]; + if (!info) { + IRQ_timeout[irq] = 60*HZ; + return; + } + while (info) { + if (info->timeout < timeout) + timeout = info->timeout; + info = info->next_port; + } + if (!irq) + timeout = timeout / 2; + IRQ_timeout[irq] = timeout ? timeout : 1; +} + +#ifdef CONFIG_SERIAL_RSA +/* Attempts to turn on the RSA FIFO. Returns zero on failure */ +static int enable_rsa(struct async_struct *info) {} + +/* Attempts to turn off the RSA FIFO. Returns zero on failure */ +static int disable_rsa(struct async_struct *info) { } +#endif /* CONFIG_SERIAL_RSA */ + +static int startup(struct async_struct * info) +{ + unsigned long flags; + int retval=0; + void (*handler)(int, void *, struct pt_regs *); + struct serial_state *state= info->state; + unsigned long page; +#ifdef CONFIG_SERIAL_MANY_PORTS + unsigned short ICP; +#endif + + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + save_flags(flags); cli(); + + if (info->flags & ASYNC_INITIALIZED) { + free_page(page); + goto errout; + } + if (info->xmit.buf) + free_page(page); + else + info->xmit.buf = (unsigned char *) page; + +#ifdef SERIAL_DEBUG_OPEN + printk("starting up ttyD%d (irq %d)...", info->line, state->irq); +#endif + + /* + * Clear the FIFO buffers and disable them + * (they will be reenabled in change_speed()) + */ + + /* + * Clear the interrupt registers. + */ + sio_reset(info); + + /* + * Allocate the IRQ if necessary + */ + if (state->irq && (!IRQ_ports[state->irq] || + !IRQ_ports[state->irq]->next_port)) { + if (IRQ_ports[state->irq]) { +#ifdef CONFIG_SERIAL_SHARE_IRQ + free_irq(state->irq, &IRQ_ports[state->irq]); + free_irq(state->irq+1, &IRQ_ports[state->irq]); +#ifdef CONFIG_SERIAL_MULTIPORT + if (rs_multiport[state->irq].port1) + handler = rs_interrupt_multi; + else +#endif + handler = psio_interrupt; +#else + retval = -EBUSY; + goto errout; +#endif /* CONFIG_SERIAL_SHARE_IRQ */ + } else + handler = psio_interrupt_single; + + /* 020116 */ + retval = request_irq(state->irq, handler, SA_SHIRQ, + "serial_rx", &IRQ_ports[state->irq]); + retval = request_irq(state->irq+1, handler, SA_SHIRQ, + "serial_tx", &IRQ_ports[state->irq]); + if (retval) { + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, + &info->tty->flags); + retval = 0; + } + goto errout; + } + } + + /* + * Insert serial port into IRQ chain. + */ + info->prev_port = 0; + info->next_port = IRQ_ports[state->irq]; + if (info->next_port) + info->next_port->prev_port = info; + IRQ_ports[state->irq] = info; + figure_IRQ_timeout(state->irq); + + /* + * Now, initialize the UART + */ + /* for m32r @020113 */ + sio_reset(info); + + info->MCR = 0; + if (info->tty->termios->c_cflag & CBAUD) + info->MCR = UART_MCR_DTR | UART_MCR_RTS; +#ifdef CONFIG_SERIAL_MANY_PORTS + if (info->flags & ASYNC_FOURPORT) { + if (state->irq == 0) + info->MCR |= UART_MCR_OUT1; + } else +#endif + { + if (state->irq != 0) + info->MCR |= UART_MCR_OUT2; + } + info->MCR |= ALPHA_KLUDGE_MCR; /* Don't ask */ + serial_outp(info, UART_MCR, info->MCR); + + /* + * Finally, enable interrupts + */ + info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; + serial_outp(info, UART_IER, info->IER); /* enable interrupts */ + +#ifdef CONFIG_SERIAL_MANY_PORTS + if (info->flags & ASYNC_FOURPORT) { + /* Enable interrupts on the AST Fourport board */ + ICP = (info->port & 0xFE0) | 0x01F; + outb_p(0x80, ICP); + (void) inb_p(ICP); + } +#endif + + /* + * And clear the interrupt registers again for luck. + */ + (void)serial_inp(info, UART_LSR); + (void)serial_inp(info, UART_RX); + (void)serial_inp(info, UART_IIR); + (void)serial_inp(info, UART_MSR); + + if (info->tty) + clear_bit(TTY_IO_ERROR, &info->tty->flags); + info->xmit.head = info->xmit.tail = 0; + + /* + * Set up serial timers... + */ + mod_timer(&serial_timer, jiffies + 2*HZ/100); + + /* + * Set up the tty->alt_speed kludge + */ +#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ + if (info->tty) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; + } +#endif + + /* + * and set the speed of the serial port + */ + change_speed(info, 0); + + info->flags |= ASYNC_INITIALIZED; + restore_flags(flags); + return 0; + +errout: + restore_flags(flags); + return retval; +} + +/* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ +static void shutdown(struct async_struct * info) +{ + unsigned long flags; + struct serial_state *state; + int retval; + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + + state = info->state; + +#ifdef SERIAL_DEBUG_OPEN + printk("Shutting down serial port %d (irq %d)....", info->line, + state->irq); +#endif + + save_flags(flags); cli(); /* Disable interrupts */ + + /* + * clear delta_msr_wait queue to avoid mem leaks: we may free the irq + * here so the queue might never be waken up + */ + wake_up_interruptible(&info->delta_msr_wait); + + /* + * First unlink the serial port from the IRQ chain... + */ + if (info->next_port) + info->next_port->prev_port = info->prev_port; + if (info->prev_port) + info->prev_port->next_port = info->next_port; + else + IRQ_ports[state->irq] = info->next_port; + figure_IRQ_timeout(state->irq); + + /* + * Free the IRQ, if necessary + */ + if (state->irq && (!IRQ_ports[state->irq] || + !IRQ_ports[state->irq]->next_port)) { + if (IRQ_ports[state->irq]) { + /* 020116 */ + free_irq(state->irq+1, &IRQ_ports[state->irq]); + retval = request_irq(state->irq+1, psio_interrupt_single, + SA_SHIRQ, "serial_xx", + &IRQ_ports[state->irq]); + free_irq(state->irq, &IRQ_ports[state->irq]); + retval = request_irq(state->irq, psio_interrupt_single, + SA_SHIRQ, "serial", + &IRQ_ports[state->irq]); + + if (retval) + printk("serial shutdown: request_irq: error %d" + " Couldn't reacquire IRQ.\n", retval); + } else{ + free_irq(state->irq, &IRQ_ports[state->irq]); + /* 020116 */ + free_irq(state->irq+1, &IRQ_ports[state->irq]); + } + } + if (info->xmit.buf) { + unsigned long pg = (unsigned long) info->xmit.buf; + info->xmit.buf = 0; + free_page(pg); + } + + info->IER = 0; + serial_outp(info, UART_IER, 0x00); /* disable all intrs */ +#ifdef CONFIG_SERIAL_MANY_PORTS + if (info->flags & ASYNC_FOURPORT) { + /* reset interrupts on the AST Fourport board */ + (void) inb((info->port & 0xFE0) | 0x01F); + info->MCR |= UART_MCR_OUT1; + } else +#endif + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); + serial_outp(info, UART_MCR, info->MCR); + +#ifdef CONFIG_SERIAL_RSA + /* + * Reset the RSA board back to 115kbps compat mode. + */ + if ((state->type == PORT_RSA) && + (state->baud_base == SERIAL_RSA_BAUD_BASE && + disable_rsa(info))) + state->baud_base = SERIAL_RSA_BAUD_BASE_LO; +#endif + + + (void)serial_in(info, UART_RX); /* read data port to reset things */ + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + sio_reset(info); + + info->flags &= ~ASYNC_INITIALIZED; + restore_flags(flags); +} + +static void change_speed(struct async_struct *info,struct termios *old_termios) +{ + int quot = 0, baud_base, baud; + unsigned cflag, cval = 0; + int bits; + unsigned long flags; + unsigned mod0, mod1; + + if (!info->tty || !info->tty->termios) + return; + cflag = info->tty->termios->c_cflag; + if (!CONFIGURED_SERIAL_PORT(info)) + return; + + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: mod1 = 0x05; bits = 7; break; + case CS6: mod1 = 0x06; bits = 8; break; + case CS7: mod1 = 0x07; bits = 9; break; + case CS8: mod1 = 0x08; bits = 10; break; + /* Never happens, but GCC is too dumb to figure it out */ + default: mod1 = 0x05; bits = 7; break; + } + mod1 <<= 8; + mod0 = 0; + if (cflag & CSTOPB) { + mod0 |= 0x03; + bits++; + } + if (cflag & PARENB) { + mod0 |= 0x10; + bits++; + } + if (!(cflag & PARODD)) { + mod0 |= 0x4; + } + mod0 = 0x80; /* Use RTS# output only */ + + serial_outp(info, UART_MOD0, mod0); /* */ + //serial_outp(info, UART_MOD1, mod1); + //mod1 = 0; + info->LCR = mod1; /* Save LCR */ + + /* Determine divisor based on baud rate */ + baud = tty_get_baud_rate(info->tty); + if (!baud) + baud = 9600; /* B0 transition handled in rs_set_termios */ +#ifdef CONFIG_SERIAL_RSA + if ((info->state->type == PORT_RSA) && + (info->state->baud_base != SERIAL_RSA_BAUD_BASE) && + enable_rsa(info)) + info->state->baud_base = SERIAL_RSA_BAUD_BASE; +#endif + baud_base = info->state->baud_base; + + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->state->custom_divisor; + else { + if (baud == 134) + /* Special case since 134 is really 134.5 */ + quot = (2*baud_base / 269); + else if (baud) + quot = baud_base / baud; + } + /* If the quotient is zero refuse the change */ + if (!quot && old_termios) { + info->tty->termios->c_cflag &= ~CBAUD; + info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); + baud = tty_get_baud_rate(info->tty); + if (!baud) + baud = 9600; + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->state->custom_divisor; + else { + if (baud == 134) + /* Special case since 134 is really 134.5 */ + quot = (2*baud_base / 269); + else if (baud) + quot = baud_base / baud ; + } + } + quot = baud_base / (baud*4) ; + /* As a last resort, if the quotient is zero, default to 9600 bps */ + if (!quot) + quot = baud_base / 9600; + /* + * Work around a bug in the Oxford Semiconductor 952 rev B + * chip which causes it to seriously miscalculate baud rates + * when DLL is 0. + */ + if (((quot & 0xFF) == 0) && (info->state->type == PORT_16C950) && + (info->state->revision == 0x5201)) + quot++; + + info->quot = quot; + info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base); + info->timeout += HZ/50; /* Add .02 seconds of slop */ + + /* CTS flow control flag and modem status interrupts */ + info->IER &= ~UART_IER_MSI; + if (info->flags & ASYNC_HARDPPS_CD) + info->IER |= UART_IER_MSI; + if (cflag & CRTSCTS) { + info->flags |= ASYNC_CTS_FLOW; + info->IER |= UART_IER_MSI; + } else + info->flags &= ~ASYNC_CTS_FLOW; + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else { + info->flags |= ASYNC_CHECK_CD; + info->IER |= UART_IER_MSI; + } + serial_out(info, UART_IER, info->IER); + + /* + * Set up parity check flag + */ +#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + + info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (I_INPCK(info->tty)) + info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) + info->read_status_mask |= UART_LSR_BI; + + /* + * Characters to ignore + */ + info->ignore_status_mask = 0; + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (I_IGNBRK(info->tty)) { + info->ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignore parity and break indicators, ignore + * overruns too. (For real raw support). + */ + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= UART_LSR_OE; + } + /* + * !!! ignore all characters if CREAD is not set + */ + if ((cflag & CREAD) == 0) + info->ignore_status_mask |= UART_LSR_DR; + cval = (baud_base / (baud * 4)) - 1; + + save_flags(flags); cli(); + serial_outp(info, UART_BAUR, cval ); /* set baurate reg */ + serial_outp(info, UART_LCR, 0x03); + restore_flags(flags); +} + +static void psio_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "psio_put_char")) + return; + + if (!tty || !info->xmit.buf) + return; + + save_flags(flags); cli(); + if (CIRC_SPACE(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) == 0) { + restore_flags(flags); + return; + } + + info->xmit.buf[info->xmit.head] = ch; + info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1); + restore_flags(flags); +} + +static void psio_flush_chars(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "psio_flush_chars")) + return; + + if (info->xmit.head == info->xmit.tail + || tty->stopped + || tty->hw_stopped + || !info->xmit.buf) + return; + + save_flags(flags); cli(); + if (!(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + serial_out(info, UART_TX, info->xmit.buf[info->xmit.tail]); + info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1); + info->state->icount.tx++; + } + restore_flags(flags); + while((serial_in(info,UART_LSR) & UART_EMPTY) != UART_EMPTY); +} + +static int psio_write(struct tty_struct *tty, int from_user, + const unsigned char *buf, int count) +{ + int c, ret = 0; + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "psio_write")) + return 0; + + if (!tty || !info->xmit.buf || !tmp_buf) + return 0; + + save_flags(flags); + if (from_user) { + down(&tmp_buf_sem); + while (1) { + int c1; + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + + c -= copy_from_user(tmp_buf, buf, c); + if (!c) { + if (!ret) + ret = -EFAULT; + break; + } + cli(); + c1 = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (c1 < c) + c = c1; + memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); + info->xmit.head = ((info->xmit.head + c) & + (SERIAL_XMIT_SIZE-1)); + restore_flags(flags); + buf += c; + count -= c; + ret += c; + } + up(&tmp_buf_sem); + } else { + cli(); + while (1) { + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) { + break; + } + memcpy(info->xmit.buf + info->xmit.head, buf, c); + info->xmit.head = ((info->xmit.head + c) & + (SERIAL_XMIT_SIZE-1)); + buf += c; + count -= c; + ret += c; + } + restore_flags(flags); + } + save_flags(flags); cli(); + if (info->xmit.head != info->xmit.tail + && !tty->stopped + && !tty->hw_stopped + && !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + serial_out(info, UART_TX, info->xmit.buf[info->xmit.tail]); + info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1); + info->state->icount.tx++; + } + restore_flags(flags); + while((serial_in(info,UART_LSR) & UART_EMPTY) != UART_EMPTY); + return ret; +} + +static int psio_write_room(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "psio_write_room")) + return 0; + return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); +} + +static int psio_chars_in_buffer(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "psio_chars_in_buffer")) + return 0; + return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); +} + +static void psio_flush_buffer(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "psio_flush_buffer")) + return; + save_flags(flags); cli(); + info->xmit.head = info->xmit.tail = 0; + restore_flags(flags); + wake_up_interruptible(&tty->write_wait); +#ifdef SERIAL_HAVE_POLL_WAIT + wake_up_interruptible(&tty->poll_wait); +#endif + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + +/* + * This function is used to send a high-priority XON/XOFF character to + * the device + */ +static void psio_send_xchar(struct tty_struct *tty, char ch) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "psio_send_char")) + return; + + info->x_char = ch; + if (ch) { + unsigned long flags; + save_flags(flags); cli(); + if (!(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + serial_out(info, UART_TX, info->x_char); + } + restore_flags(flags); + } +} + +/* + * ------------------------------------------------------------ + * rs_throttle() + * + * This routine is called by the upper-layer tty layer to signal that + * incoming characters should be throttled. + * ------------------------------------------------------------ + */ +static void psio_throttle(struct tty_struct * tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("throttle %s: %d....\n", tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (serial_paranoia_check(info, tty->device, "psio_throttle")) + return; + + if (I_IXOFF(tty)) + psio_send_xchar(tty, STOP_CHAR(tty)); + + if (tty->termios->c_cflag & CRTSCTS) + info->MCR &= ~UART_MCR_RTS; + + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); +} + +static void psio_unthrottle(struct tty_struct * tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("unthrottle %s: %d....\n", tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (serial_paranoia_check(info, tty->device, "psio_unthrottle")) + return; + + if (I_IXOFF(tty)) { + if (info->x_char) info->x_char = 0; + } + if (tty->termios->c_cflag & CRTSCTS) + info->MCR |= UART_MCR_RTS; + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); +} + +/* + * ------------------------------------------------------------ + * rs_ioctl() and friends + * ------------------------------------------------------------ + */ + +static int get_serial_info(struct async_struct * info, + struct serial_struct * retinfo) +{ + struct serial_struct tmp; + struct serial_state *state = info->state; + + if (!retinfo) + return -EFAULT; + memset(&tmp, 0, sizeof(tmp)); + tmp.type = state->type; + tmp.line = state->line; + tmp.port = state->port; + if (HIGH_BITS_OFFSET) + tmp.port_high = state->port >> HIGH_BITS_OFFSET; + else + tmp.port_high = 0; + tmp.irq = state->irq; + tmp.flags = state->flags; + tmp.xmit_fifo_size = state->xmit_fifo_size; + tmp.baud_base = state->baud_base; + tmp.close_delay = state->close_delay; + tmp.closing_wait = state->closing_wait; + tmp.custom_divisor = state->custom_divisor; + tmp.hub6 = state->hub6; + tmp.io_type = state->io_type; + if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int set_serial_info(struct async_struct * info, + struct serial_struct * new_info) +{ + struct serial_struct new_serial; + struct serial_state old_state, *state; + unsigned int i,change_irq,change_port; + int retval = 0; + unsigned long new_port; + + if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) + return -EFAULT; + state = info->state; + old_state = *state; + + new_port = new_serial.port; + if (HIGH_BITS_OFFSET) + new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; + + change_irq = new_serial.irq != state->irq; + change_port = (new_port != ((int) state->port)) || + (new_serial.hub6 != state->hub6); + + if (!capable(CAP_SYS_ADMIN)) { + if (change_irq || change_port || + (new_serial.baud_base != state->baud_base) || + (new_serial.type != state->type) || + (new_serial.close_delay != state->close_delay) || + (new_serial.xmit_fifo_size != state->xmit_fifo_size) || + ((new_serial.flags & ~ASYNC_USR_MASK) != + (state->flags & ~ASYNC_USR_MASK))) + return -EPERM; + state->flags = ((state->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + info->flags = ((info->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + state->custom_divisor = new_serial.custom_divisor; + goto check_and_exit; + } + + new_serial.irq = irq_cannonicalize(new_serial.irq); + + if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || + (new_serial.baud_base < 9600)|| (new_serial.type < PORT_UNKNOWN) || + (new_serial.type > PORT_MAX) || (new_serial.type == PORT_CIRRUS) || + (new_serial.type == PORT_STARTECH)) { + return -EINVAL; + } + + if ((new_serial.type != state->type) || + (new_serial.xmit_fifo_size <= 0)) + new_serial.xmit_fifo_size = + uart_config[new_serial.type].dfl_xmit_fifo_size; + + /* Make sure address is not already in use */ + if (new_serial.type) { + for (i = 0 ; i < NR_PORTS; i++) + if ((state != &rs_table[i]) && + (rs_table[i].port == new_port) && + rs_table[i].type) + return -EADDRINUSE; + } + + if ((change_port || change_irq) && (state->count > 1)) + return -EBUSY; + + /* + * OK, past this point, all the error checking has been done. + * At this point, we start making changes..... + */ + + state->baud_base = new_serial.baud_base; + state->flags = ((state->flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); + info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) | + (info->flags & ASYNC_INTERNAL_FLAGS)); + state->custom_divisor = new_serial.custom_divisor; + state->close_delay = new_serial.close_delay * HZ/100; + state->closing_wait = new_serial.closing_wait * HZ/100; +#if (LINUX_VERSION_CODE > 0x20100) + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; +#endif + info->xmit_fifo_size = state->xmit_fifo_size = + new_serial.xmit_fifo_size; + + if ((state->type != PORT_UNKNOWN) && state->port) { +#ifdef CONFIG_SERIAL_RSA + if (old_state.type == PORT_RSA) + release_region(state->port + UART_RSA_BASE, 16); + else +#endif + release_region(state->port,8); + } + state->type = new_serial.type; + if (change_port || change_irq) { + /* + * We need to shutdown the serial port at the old + * port/irq combination. + */ + shutdown(info); + state->irq = new_serial.irq; + info->port = state->port = new_port; + info->hub6 = state->hub6 = new_serial.hub6; + if (info->hub6) + info->io_type = state->io_type = SERIAL_IO_HUB6; + else if (info->io_type == SERIAL_IO_HUB6) + info->io_type = state->io_type = SERIAL_IO_PORT; + } + if ((state->type != PORT_UNKNOWN) && state->port) { +#ifdef CONFIG_SERIAL_RSA + if (state->type == PORT_RSA) + request_region(state->port + UART_RSA_BASE, + 16, "serial_rsa(set)"); + else +#endif + request_region(state->port,8,"serial(set)"); + } + + +check_and_exit: + if (!state->port || !state->type) + return 0; + if (info->flags & ASYNC_INITIALIZED) { + if (((old_state.flags & ASYNC_SPD_MASK) != + (state->flags & ASYNC_SPD_MASK)) || + (old_state.custom_divisor != state->custom_divisor)) { +#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; +#endif + change_speed(info, 0); + } + } else + retval = startup(info); + return retval; +} + +/* + * get_lsr_info - get line status register info + * + * Purpose: Let user call ioctl() to get info when the UART physically + * is emptied. On bus types like RS485, the transmitter must + * release the bus after transmitting. This must be done when + * the transmit shift register is empty, not be done when the + * transmit holding register is empty. This functionality + * allows an RS485 driver to be written in user space. + */ +static int get_lsr_info(struct async_struct * info, unsigned int *value) +{ + unsigned char status; + unsigned int result; + unsigned long flags; + + save_flags(flags); cli(); + status = serial_in(info, UART_LSR); + restore_flags(flags); + result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); + + /* + * If we're about to load something into the transmit + * register, we'll pretend the transmitter isn't empty to + * avoid a race condition (depending on when the transmit + * interrupt happens). + */ + if (info->x_char || + ((CIRC_CNT(info->xmit.head, info->xmit.tail, + SERIAL_XMIT_SIZE) > 0) && + !info->tty->stopped && !info->tty->hw_stopped)) + result &= ~TIOCSER_TEMT; + + if (copy_to_user(value, &result, sizeof(int))) + return -EFAULT; + return 0; +} + + +static int get_modem_info(struct async_struct * info, unsigned int *value) +{ + unsigned char control, status; + unsigned int result; + unsigned long flags; + + control = info->MCR; + save_flags(flags); cli(); + status = serial_in(info, UART_MSR); + restore_flags(flags); + result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) + | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) +#ifdef TIOCM_OUT1 + | ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0) + | ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0) +#endif + | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) + | ((status & UART_MSR_RI) ? TIOCM_RNG : 0) + | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) + | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); + + if (copy_to_user(value, &result, sizeof(int))) + return -EFAULT; + return 0; +} +static int set_modem_info(struct async_struct * info, unsigned int cmd, + unsigned int *value) +{ + unsigned int arg; + unsigned long flags; + + if (copy_from_user(&arg, value, sizeof(int))) + return -EFAULT; + + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS) + info->MCR |= UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR |= UART_MCR_DTR; +#ifdef TIOCM_OUT1 + if (arg & TIOCM_OUT1) + info->MCR |= UART_MCR_OUT1; + if (arg & TIOCM_OUT2) + info->MCR |= UART_MCR_OUT2; +#endif + if (arg & TIOCM_LOOP) + info->MCR |= UART_MCR_LOOP; + break; + case TIOCMBIC: + if (arg & TIOCM_RTS) + info->MCR &= ~UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR &= ~UART_MCR_DTR; +#ifdef TIOCM_OUT1 + if (arg & TIOCM_OUT1) + info->MCR &= ~UART_MCR_OUT1; + if (arg & TIOCM_OUT2) + info->MCR &= ~UART_MCR_OUT2; +#endif + if (arg & TIOCM_LOOP) + info->MCR &= ~UART_MCR_LOOP; + break; + case TIOCMSET: + info->MCR = ((info->MCR & ~(UART_MCR_RTS | +#ifdef TIOCM_OUT1 + UART_MCR_OUT1 | + UART_MCR_OUT2 | +#endif + UART_MCR_LOOP | + UART_MCR_DTR)) + | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) +#ifdef TIOCM_OUT1 + | ((arg & TIOCM_OUT1) ? UART_MCR_OUT1 : 0) + | ((arg & TIOCM_OUT2) ? UART_MCR_OUT2 : 0) +#endif + | ((arg & TIOCM_LOOP) ? UART_MCR_LOOP : 0) + | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); + break; + default: + return -EINVAL; + } + save_flags(flags); cli(); + info->MCR |= ALPHA_KLUDGE_MCR; /* Don't ask */ + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + return 0; +} + +static int do_autoconfig(struct async_struct * info) +{ + int irq, retval; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (info->state->count > 1) + return -EBUSY; + + shutdown(info); + + autoconfig(info->state); + if ((info->state->flags & ASYNC_AUTO_IRQ) && + (info->state->port != 0 || info->state->iomem_base != 0) && + (info->state->type != PORT_UNKNOWN)) { + irq = detect_uart_irq(info->state); + if (irq > 0) + info->state->irq = irq; + } + + retval = startup(info); + if (retval) + return retval; + return 0; +} + +/* + * rs_break() --- routine which turns the break handling on or off + */ +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ +static void send_break( struct async_struct * info, int duration) +{ + if (!CONFIGURED_SERIAL_PORT(info)) + return; + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + duration; + cli(); + info->LCR |= UART_LCR_SBC; + serial_out(info, UART_LCR, 0x3); + schedule(); + info->LCR &= ~UART_LCR_SBC; + serial_out(info, UART_LCR, 0x3); + sti(); +} +#else +static void psio_break(struct tty_struct *tty, int break_state) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_break")) + return; + + if (!CONFIGURED_SERIAL_PORT(info)) + return; + save_flags(flags); cli(); + if (break_state == -1) + info->LCR |= UART_LCR_SBC; + else + info->LCR &= ~UART_LCR_SBC; + restore_flags(flags); +} +#endif + +#ifdef CONFIG_SERIAL_MULTIPORT +static int get_multiport_struct(struct async_struct * info, + struct serial_multiport_struct *retinfo) +{ + struct serial_multiport_struct ret; + struct rs_multiport_struct *multi; + + multi = &rs_multiport[info->state->irq]; + + ret.port_monitor = multi->port_monitor; + + ret.port1 = multi->port1; + ret.mask1 = multi->mask1; + ret.match1 = multi->match1; + + ret.port2 = multi->port2; + ret.mask2 = multi->mask2; + ret.match2 = multi->match2; + + ret.port3 = multi->port3; + ret.mask3 = multi->mask3; + ret.match3 = multi->match3; + + ret.port4 = multi->port4; + ret.mask4 = multi->mask4; + ret.match4 = multi->match4; + + ret.irq = info->state->irq; + + if (copy_to_user(retinfo,&ret,sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int set_multiport_struct(struct async_struct * info, + struct serial_multiport_struct *in_multi) +{ + struct serial_multiport_struct new_multi; + struct rs_multiport_struct *multi; + struct serial_state *state; + int was_multi, now_multi; + int retval; + void (*handler)(int, void *, struct pt_regs *); + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + state = info->state; + + if (copy_from_user(&new_multi, in_multi, + sizeof(struct serial_multiport_struct))) + return -EFAULT; + + if (new_multi.irq != state->irq || state->irq == 0 || + !IRQ_ports[state->irq]) + return -EINVAL; + + multi = &rs_multiport[state->irq]; + was_multi = (multi->port1 != 0); + + multi->port_monitor = new_multi.port_monitor; + + if (multi->port1) + release_region(multi->port1,1); + multi->port1 = new_multi.port1; + multi->mask1 = new_multi.mask1; + multi->match1 = new_multi.match1; + if (multi->port1) + request_region(multi->port1,1,"serial(multiport1)"); + + if (multi->port2) + release_region(multi->port2,1); + multi->port2 = new_multi.port2; + multi->mask2 = new_multi.mask2; + multi->match2 = new_multi.match2; + if (multi->port2) + request_region(multi->port2,1,"serial(multiport2)"); + + if (multi->port3) + release_region(multi->port3,1); + multi->port3 = new_multi.port3; + multi->mask3 = new_multi.mask3; + multi->match3 = new_multi.match3; + if (multi->port3) + request_region(multi->port3,1,"serial(multiport3)"); + + if (multi->port4) + release_region(multi->port4,1); + multi->port4 = new_multi.port4; + multi->mask4 = new_multi.mask4; + multi->match4 = new_multi.match4; + if (multi->port4) + request_region(multi->port4,1,"serial(multiport4)"); + + now_multi = (multi->port1 != 0); + + if (IRQ_ports[state->irq]->next_port && + (was_multi != now_multi)) { + free_irq(state->irq, &IRQ_ports[state->irq]); + if (now_multi) + handler = rs_interrupt_multi; + else + handler = rs_interrupt; + + retval = request_irq(state->irq, handler, SA_SHIRQ, + "serial", &IRQ_ports[state->irq]); + if (retval) { + printk("Couldn't reallocate serial interrupt " + "driver!!\n"); + } + } + return 0; +} +#endif + +static int psio_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + struct async_icount cprev, cnow; /* kernel counter temps */ + struct serial_icounter_struct icount; + unsigned long flags; +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ + int retval, tmp; +#endif + + if (serial_paranoia_check(info, tty->device, "rs_ioctl")) + return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && + (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ + case TCSBRK: /* SVID version: non-zero arg --> no break */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) + return -EINTR; + if (!arg) { + send_break(info, HZ/4); /* 1/4 second */ + if (signal_pending(current)) + return -EINTR; + } + return 0; + case TCSBRKP: /* support for POSIX tcsendbreak() */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) + return -EINTR; + send_break(info, arg ? arg*(HZ/10) : HZ/4); + if (signal_pending(current)) + return -EINTR; + return 0; + case TIOCGSOFTCAR: + tmp = C_CLOCAL(tty) ? 1 : 0; + if (copy_to_user((void *)arg, &tmp, sizeof(int))) + return -EFAULT; + return 0; + case TIOCSSOFTCAR: + if (copy_from_user(&tmp, (void *)arg, sizeof(int))) + return -EFAULT; + + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (tmp ? CLOCAL : 0)); + return 0; +#endif + case TIOCMGET: + return get_modem_info(info, (unsigned int *) arg); + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + return set_modem_info(info, cmd, (unsigned int *) arg); + case TIOCGSERIAL: + return get_serial_info(info, + (struct serial_struct *) arg); + case TIOCSSERIAL: + return set_serial_info(info, + (struct serial_struct *) arg); + case TIOCSERCONFIG: + return do_autoconfig(info); + + case TIOCSERGETLSR: /* Get line status register */ + return get_lsr_info(info, (unsigned int *) arg); + + case TIOCSERGSTRUCT: + if (copy_to_user((struct async_struct *) arg, + info, sizeof(struct async_struct))) + return -EFAULT; + return 0; + +#ifdef CONFIG_SERIAL_MULTIPORT + case TIOCSERGETMULTI: + return get_multiport_struct(info, + (struct serial_multiport_struct *) arg); + case TIOCSERSETMULTI: + return set_multiport_struct(info, + (struct serial_multiport_struct *) arg); +#endif + + /* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + * Caller should use TIOCGICOUNT to see which one it was + */ + case TIOCMIWAIT: + save_flags(flags); cli(); + /* note the counters on entry */ + cprev = info->state->icount; + restore_flags(flags); + /* Force modem status interrupts on */ + info->IER |= UART_IER_MSI; + serial_out(info, UART_IER, info->IER); + while (1) { + interruptible_sleep_on(&info->delta_msr_wait); + /* see if a signal did it */ + if (signal_pending(current)) + return -ERESTARTSYS; + save_flags(flags); cli(); + cnow = info->state->icount; /* atomic copy */ + restore_flags(flags); + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) + return -EIO; /* no change => error */ + if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { + return 0; + } + cprev = cnow; + } + /* NOTREACHED */ + + /* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ + case TIOCGICOUNT: + save_flags(flags); cli(); + cnow = info->state->icount; + restore_flags(flags); + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + + if (copy_to_user((void *)arg, &icount, sizeof(icount))) + return -EFAULT; + return 0; + case TIOCSERGWILD: + case TIOCSERSWILD: + /* "setserial -W" is called in Debian boot */ + printk ("TIOCSER?WILD ioctl obsolete, ignored.\n"); + return 0; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static void psio_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + unsigned int cflag = tty->termios->c_cflag; + + if ( (cflag == old_termios->c_cflag) + && ( RELEVANT_IFLAG(tty->termios->c_iflag) + == RELEVANT_IFLAG(old_termios->c_iflag))) + return; + + change_speed(info, old_termios); + + /* Handle transition to B0 status */ + if ((old_termios->c_cflag & CBAUD) && + !(cflag & CBAUD)) { + info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + } + + /* Handle transition away from B0 status */ + if (!(old_termios->c_cflag & CBAUD) && + (cflag & CBAUD)) { + info->MCR |= UART_MCR_DTR; + if (!(tty->termios->c_cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) { + info->MCR |= UART_MCR_RTS; + } + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + } + + /* Handle turning off CRTSCTS */ + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + psio_start(tty); + } +} + +/* + * ----------------------------------------------------------- + * psio_close() + * + * This routine is called when the debug console port gets closed. + * First, we wait for the last remaining data to be sent. Then, we unlink + * its async structure from the interrupt chain if necessary, and we free + * that IRQ if nothing is left in the chain. + * ----------------------------------------------------------- + */ +static void psio_close(struct tty_struct *tty, struct file *filp) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + struct serial_state *state; + unsigned long flags; + + if (!info || serial_paranoia_check(info, tty->device, "rs_close")) + return; + + state = info->state; + + save_flags(flags); cli(); + + if (tty_hung_up_p(filp)) { + DBG_CNT("before DEC-hung"); + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + +#ifdef SERIAL_DEBUG_OPEN + printk("psio_close ttyD%d, count = %d\n", info->line, state->count); +#endif + if ((tty->count == 1) && (state->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. state->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("rs_close: bad serial port count; tty->count is 1, " + "state->count is %d\n", state->count); + state->count = 1; + } + if (--state->count < 0) { + printk("psio_close: bad serial port count for ttyD%d: %d\n", + info->line, state->count); + state->count = 0; + } + if (state->count) { + DBG_CNT("before DEC-2"); + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + info->flags |= ASYNC_CLOSING; + restore_flags(flags); + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->state->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->state->callout_termios = *tty->termios; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->closing_wait); + /* + * At this point we stop accepting input. To do this, we + * disable the receive line status interrupts, and tell the + * interrupt driver to stop checking the data ready bit in the + * line status register. + */ + info->IER &= ~UART_IER_RLSI; + info->read_status_mask &= ~UART_LSR_DR; + if (info->flags & ASYNC_INITIALIZED) { + serial_out(info, UART_IER, info->IER); + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + psio_wait_until_sent(tty, info->timeout); + } + shutdown(info); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + info->event = 0; + info->tty = 0; + if (info->blocked_open) { + if (info->close_delay) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(info->close_delay); + } + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); + MOD_DEC_USE_COUNT; +} + +/* + * rs_wait_until_sent() --- wait until the transmitter is empty + */ +static void psio_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned long orig_jiffies, char_time; + int lsr; + + if (serial_paranoia_check(info, tty->device, "psio_wait_until_sent")) + return; + + if (info->state->type == PORT_UNKNOWN) + return; + + if (info->xmit_fifo_size == 0) + return; /* Just in case.... */ + + orig_jiffies = jiffies; + /* + * Set the check interval to be 1/5 of the estimated time to + * send a single character, and make it at least 1. The check + * interval should also be less than the timeout. + * + * Note: we have to use pretty tight timings here to satisfy + * the NIST-PCTS. + */ + char_time = (info->timeout - HZ/50) / info->xmit_fifo_size; + char_time = char_time / 5; + if (char_time == 0) + char_time = 1; + if (timeout && timeout < char_time) + char_time = timeout; + /* + * If the transmitter hasn't cleared in twice the approximate + * amount of time to send the entire FIFO, it probably won't + * ever clear. This assumes the UART isn't doing flow + * control, which is currently the case. Hence, if it ever + * takes longer than info->timeout, this is probably due to a + * UART bug of some kind. So, we clamp the timeout parameter at + * 2*info->timeout. + */ + if (!timeout || timeout > 2*info->timeout) + timeout = 2*info->timeout; +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time); + printk("jiff=%lu...", jiffies); +#endif + while (!((lsr = serial_inp(info, UART_LSR)) & UART_LSR_TEMT)) { +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...", lsr, jiffies); +#endif + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (timeout && time_after(jiffies, orig_jiffies + timeout)) + break; + } +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); +#endif +} + +/* + * psio_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +static void psio_hangup(struct tty_struct *tty) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + struct serial_state *state = info->state; + + if (serial_paranoia_check(info, tty->device, "psio_hangup")) + return; + + state = info->state; + + psio_flush_buffer(tty); + if (info->flags & ASYNC_CLOSING) + return; + shutdown(info); + info->event = 0; + state->count = 0; + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + info->tty = 0; + wake_up_interruptible(&info->open_wait); +} + + + +/* +static void rs_wait_until_sent(struct tty_struct *tty, int timeout) +static void rs_hangup(struct tty_struct *tty) +*/ + +/* + * ------------------------------------------------------------ + * psio_open() and friends + * ------------------------------------------------------------ + */ +#define SERIAL_DEBUG_OPEN +static int block_til_ready(struct tty_struct *tty, struct file * filp, + struct async_struct *info) +{ + DECLARE_WAITQUEUE(wait, current); + struct serial_state *state = info->state; + int retval; + int do_clocal = 0, extra_count = 0; + unsigned long flags; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +#else + return -EAGAIN; +#endif + } + + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (info->flags & ASYNC_NORMAL_ACTIVE) + return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_SESSION_LOCKOUT) && + (info->session != current->session)) + return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)) + return -EBUSY; + info->flags |= ASYNC_CALLOUT_ACTIVE; + return 0; + } + +#if 1 /* ? 020906 */ +filp->f_flags |= O_NONBLOCK; +#endif /* ? 020906 */ + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + if (info->flags & ASYNC_CALLOUT_ACTIVE) + return -EBUSY; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (info->flags & ASYNC_CALLOUT_ACTIVE) { + if (state->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } else { + if (tty->termios->c_cflag & CLOCAL) + do_clocal = 1; + } + + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, state->count is dropped by one, so that + * rs_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&info->open_wait, &wait); +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready before block: ttyD%d, count = %d\n", + state->line, state->count); +#endif + save_flags(flags); cli(); + if (!tty_hung_up_p(filp)) { + extra_count = 1; + state->count--; + } + restore_flags(flags); + info->blocked_open++; + while (1) { + save_flags(flags); cli(); + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + (tty->termios->c_cflag & CBAUD)) + serial_out(info, UART_MCR, + serial_inp(info, UART_MCR) | + (UART_MCR_DTR | UART_MCR_RTS)); + restore_flags(flags); + set_current_state(TASK_INTERRUPTIBLE); + if (tty_hung_up_p(filp) || + !(info->flags & ASYNC_INITIALIZED)) { +#ifdef SERIAL_DO_RESTART + if (info->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; +#else + retval = -EAGAIN; +#endif + break; + } + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + !(info->flags & ASYNC_CLOSING) && + (do_clocal || (serial_in(info, UART_MSR) & + UART_MSR_DCD))) + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready blocking: ttyD%d, count = %d\n", + info->line, state->count); +#endif + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); + if (extra_count) + state->count++; + info->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready after blocking: ttyD%d, count = %d\n", + info->line, state->count); +#endif + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} +#undef SERIAL_DEBUG_OPEN + + +static int get_async_struct(int line, struct async_struct **ret_info) +{ + struct async_struct *info; + struct serial_state *sstate; + + sstate = rs_table + line; + sstate->count++; + if (sstate->info) { + *ret_info = sstate->info; + return 0; + } + info = kmalloc(sizeof(struct async_struct), GFP_KERNEL); + if (!info) { + sstate->count--; + return -ENOMEM; + } + memset(info, 0, sizeof(struct async_struct)); + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + init_waitqueue_head(&info->delta_msr_wait); + info->magic = SERIAL_MAGIC; + info->port = sstate->port; + info->flags = sstate->flags; + info->io_type = sstate->io_type; + info->iomem_base = sstate->iomem_base; + info->iomem_reg_shift = sstate->iomem_reg_shift; + info->xmit_fifo_size = sstate->xmit_fifo_size=0; + info->line = line; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->state = sstate; + + if (sstate->info) { + kfree(info); + + *ret_info = sstate->info; + return 0; + } + *ret_info = sstate->info = info; + return 0; +} + +/* + * ----------------------------------------------------------- + * psio_open() + * + * This routine is called whenever a debug console port is opened. It + * enables interrupts for a serial port, linking in its async structure into + * the IRQ chain. It also performs the serial-specific + * initialization for the tty structure. + * ----------------------------------------------------------- + */ +static int psio_open(struct tty_struct *tty, struct file *filp) +{ + struct async_struct *info; + int retval,line=0; + unsigned long page; + + MOD_INC_USE_COUNT; + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= NR_PORTS)) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + + retval = get_async_struct(line, &info); + if (retval) { + printk("psio_open ttyD%d fail...",line); + MOD_DEC_USE_COUNT; + return retval; + } + tty->driver_data = info; + info->tty = tty; + if (serial_paranoia_check(info, tty->device, "psio_open")) + return -ENODEV; + +#ifdef SERIAL_DEBUG_OPEN + printk("psio_open %s%d, count = %d\n", tty->driver.name, info->line, + info->state->count); +#endif + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + + /* + * This relies on lock_kernel() stuff so wants tidying for 2.5 + */ + if (!tmp_buf) { + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + if (tmp_buf) + free_page(page); + else + tmp_buf = (unsigned char *) page; + } + /* + * If the port is the middle of closing, bail out now + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +#else + return -EAGAIN; +#endif + } + + /* + * Start up serial port + */ + retval=startup(info); + if (retval) + return retval; + + retval = block_til_ready(tty, filp, info); + if (retval) { +#ifdef SERIAL_DEBUG_OPEN + printk("psio_open returning after block_til_ready with %d\n", + retval); +#endif + return retval; + } + + if ((info->state->count == 1) && + (info->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->state->normal_termios; + else + *tty->termios = info->state->callout_termios; + change_speed(info, 0); + } +#ifdef CONFIG_SERIAL_CONSOLE + if (cons.cflag && cons.index == line) { + tty->termios->c_cflag = cons.cflag; + cons.cflag = 0; + change_speed(info, 0); + } +#endif + info->session = current->session; + info->pgrp = current->pgrp; + + return 0; +} + +/* + * /proc fs routines.... + */ +static inline int line_info(char *buf, struct serial_state *state) +{ + struct async_struct *info = state->info, scr_info; + char stat_buf[30], control, status; + int ret; + unsigned long flags; + + ret = sprintf(buf, "%d: uart:%s port:%lX irq:%d", + state->line, uart_config[state->type].name, + state->port, state->irq); + + if (!state->port || (state->type == PORT_UNKNOWN)) { + ret += sprintf(buf+ret, "\n"); + return ret; + } + + /* + * Figure out the current RS-232 lines + */ + if (!info) { + info = &scr_info; /* This is just for serial_{in,out} */ + + info->magic = SERIAL_MAGIC; + info->port = state->port; + info->flags = state->flags; + info->hub6 = state->hub6; + info->io_type = state->io_type; + info->iomem_base = state->iomem_base; + info->iomem_reg_shift = state->iomem_reg_shift; + info->quot = 0; + info->tty = 0; + } + save_flags(flags); cli(); + status = serial_in(info, UART_MSR); + control = info != &scr_info ? info->MCR : serial_in(info, UART_MCR); + restore_flags(flags); + + stat_buf[0] = 0; + stat_buf[1] = 0; + if (control & UART_MCR_RTS) + strcat(stat_buf, "|RTS"); + if (status & UART_MSR_CTS) + strcat(stat_buf, "|CTS"); + if (control & UART_MCR_DTR) + strcat(stat_buf, "|DTR"); + if (status & UART_MSR_DSR) + strcat(stat_buf, "|DSR"); + if (status & UART_MSR_DCD) + strcat(stat_buf, "|CD"); + if (status & UART_MSR_RI) + strcat(stat_buf, "|RI"); + + if (info->quot) { + ret += sprintf(buf+ret, " baud:%d", + state->baud_base / (16*info->quot)); + } + + ret += sprintf(buf+ret, " tx:%d rx:%d", + state->icount.tx, state->icount.rx); + + if (state->icount.frame) + ret += sprintf(buf+ret, " fe:%d", state->icount.frame); + + if (state->icount.parity) + ret += sprintf(buf+ret, " pe:%d", state->icount.parity); + + if (state->icount.brk) + ret += sprintf(buf+ret, " brk:%d", state->icount.brk); + + if (state->icount.overrun) + ret += sprintf(buf+ret, " oe:%d", state->icount.overrun); + + /* + * Last thing is the RS-232 status lines + */ + ret += sprintf(buf+ret, " %s\n", stat_buf+1); + return ret; +} + + + +int psio_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int i, len = 0, l; + off_t begin = 0; + + len += sprintf(page, "sioinfo:1.0 driver:%s%s revision:%s\n", + serial_version, LOCAL_VERSTRING, serial_revdate); + for (i = 0; i < NR_PORTS && len < 4000; i++) { + l = line_info(page + len, &rs_table[i]); + len += l; + if (len+begin > off+count) + goto done; + if (len+begin < off) { + begin += len; + len = 0; + } + } + *eof = 1; +done: + if (off >= len+begin) + return 0; + *start = page + (off-begin); + return ((count < begin+len-off) ? count : begin+len-off); +} + +static char serial_options[] __initdata = + " no serial options enabled\n"; + + +static inline void show_serial_version(void) +{ + printk(KERN_INFO "%s version %s%s (%s) with%s", serial_name, + serial_version, LOCAL_VERSTRING, serial_revdate, + serial_options); +} +static unsigned detect_uart_irq (struct serial_state * state) +{ + if(! state->irq) + printk(KERN_INFO "detect_uart_irq: Ohh irq = 0\n"); + + return state->irq; +} + +static void autoconfig(struct serial_state * state) +{ + struct async_struct *info, scr_info; + //unsigned long flags; + + state->type = PORT_UNKNOWN; + +#ifdef SERIAL_DEBUG_AUTOCONF + printk("Testing ttyD%d (0x%04lx, 0x%04x)...\n", state->line, + state->port, (unsigned) state->iomem_base); +#endif + + if (!CONFIGURED_SERIAL_PORT(state)) + return; + + info = &scr_info; /* This is just for serial_{in,out} */ + + info->magic = SERIAL_MAGIC; + info->state = state; + info->port = state->port; + info->flags = state->flags; + sio_reset(info); +} + +/* + * The debug console driver boot-time initialization code! + */ +/* 20020830 */ +int __init psio_init(void) +{ + int i; + struct serial_state * state; + + init_bh(SERIAL_BH, do_psio_serial_bh); + init_timer(&serial_timer); + serial_timer.function = (void *)psio_timer; +#if 1 + mod_timer(&serial_timer, jiffies + 10); +#else /* 1 */ + mod_timer(&serial_timer, jiffies + RS_STROBE_TIME); +#endif /* 1 */ + + for (i = 0; i < NR_IRQS; i++) { + IRQ_ports[i] = 0; + IRQ_timeout[i] = 0; + } + + /* + * Initialize the tty_driver structure + */ + memset(&psio_driver, 0, sizeof(struct tty_driver)); + psio_driver.magic = TTY_DRIVER_MAGIC; +#if (LINUX_VERSION_CODE > 0x20100) + psio_driver.driver_name = "serial_m32102"; +#endif +#if 1 + psio_driver.name = "ttyD"; +#else +#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) + psio_driver.name = "ttyd/%d"; +#else + psio_driver.name = "ttyD"; +#endif +#endif /* 1 */ + + psio_driver.major = TTY_MAJOR; + psio_driver.minor_start = 80; + psio_driver.name_base = 0; + psio_driver.num = NR_PORTS; + psio_driver.type = TTY_DRIVER_TYPE_SERIAL; + + psio_driver.subtype = SERIAL_TYPE_NORMAL; + psio_driver.init_termios = tty_std_termios; + psio_driver.init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + psio_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + psio_driver.refcount = &psio_refcount; + psio_driver.table = psio_table; + psio_driver.termios = psio_termios; + psio_driver.termios_locked = psio_termios_locked; + + psio_driver.open = psio_open; + psio_driver.close = psio_close; + psio_driver.write = psio_write; + psio_driver.put_char = psio_put_char; + psio_driver.flush_chars = psio_flush_chars; + psio_driver.write_room = psio_write_room; + psio_driver.chars_in_buffer = psio_chars_in_buffer; + psio_driver.flush_buffer = psio_flush_buffer; + psio_driver.ioctl = psio_ioctl; + psio_driver.throttle = psio_throttle; + psio_driver.unthrottle = psio_unthrottle; + psio_driver.set_termios = psio_set_termios; + psio_driver.stop = psio_stop; + psio_driver.start = psio_start; + psio_driver.hangup = psio_hangup; + +#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ + psio_driver.break_ctl = psio_break; +#endif +#if (LINUX_VERSION_CODE >= 131343) + psio_driver.send_xchar = psio_send_xchar; + psio_driver.wait_until_sent = psio_wait_until_sent; + psio_driver.read_proc = psio_read_proc; +#endif + + if (tty_register_driver(&psio_driver)) + panic("Couldn't register debug console driver\n"); + + for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { + state->magic = SSTATE_MAGIC; + state->line = i; + state->type = 14; + state->custom_divisor = 0; + state->close_delay = 5*HZ/10; + state->closing_wait = 30*HZ; + state->callout_termios = psio_driver.init_termios; + state->normal_termios = psio_driver.init_termios; + state->icount.cts = state->icount.dsr = + state->icount.rng = state->icount.dcd = 0; + state->icount.rx = state->icount.tx = 0; + state->icount.frame = state->icount.parity = 0; + state->icount.overrun = state->icount.brk = 0; + state->irq = irq_cannonicalize(state->irq); + +#if 0 + if (check_region(state->port,8)) + continue; + if (state->flags & ASYNC_BOOT_AUTOCONF) + autoconfig(psio_table); +#endif + state->baud_base = boot_cpu_data.bus_clock; + printk(KERN_INFO "ttyD%d initialized.\n",i); + tty_register_devfs(&psio_driver, 0, + psio_driver.minor_start + state->line); + } + + return 0; +} + +/* 20020830 */ +static void __exit psio_fini(void) +{ + unsigned long flags; + // int e1, e2; + int e1; + // int i; + // struct async_struct *info; + + /* printk("Unloading %s: version %s\n", serial_name, serial_version); */ + del_timer_sync(&serial_timer); + save_flags(flags); cli(); + remove_bh(SERIAL_BH); + if ((e1 = tty_unregister_driver(&psio_driver))) + printk("psio_serial: failed to unregister serial driver (%d)\n",e1); + restore_flags(flags); + + if (tmp_buf) { + unsigned long pg = (unsigned long) tmp_buf; + tmp_buf = NULL; + free_page(pg); + } +} + +module_init(psio_init); +module_exit(psio_fini); +MODULE_DESCRIPTION("M32R/M32102 (dumb) serial driver"); +MODULE_AUTHOR("Hiroyuki Kondo , Takeo Takahashi "); +MODULE_LICENSE("GPL"); + + +/* + * ----------------------------------------------------------- + * Debug console driver + * ----------------------------------------------------------- + */ + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +static struct async_struct async_dbgcons; + +/* + * Wait for transmitter & holding register to empty + */ +static inline void wait_for_xmitr(struct async_struct *info) +{ + unsigned int status, tmout = 1000000; + + do { + status = serial_in(info, UART_LSR); + + if (status & UART_LSR_BI) + lsr_break_flag = UART_LSR_BI; + + if (--tmout == 0) + break; + } while((status & BOTH_EMPTY) != BOTH_EMPTY); + + /* Wait for flow control if necessary */ + if (info->flags & ASYNC_CONS_FLOW) { + tmout = 1000000; + while (--tmout && + ((serial_in(info, UART_MSR) & UART_MSR_CTS) == 0)); + } +} +static void dbg_console_write(struct console *co, const char *s, + unsigned count) +{ + static struct async_struct *info = &async_dbgcons; + int ier; + unsigned i; + + /* + * First save the IER then disable the interrupts + */ + ier = serial_in(info, UART_IER); + serial_out(info, UART_IER, 0x00); + + /* + * Now, do each character + */ + for (i = 0; i < count; i++, s++) { + wait_for_xmitr(info); + + /* + * Send the character out. + * If a LF, also do CR... + */ + serial_out(info, UART_TX, *s); + if (*s == 10) { + wait_for_xmitr(info); + serial_out(info, UART_TX, 13); + } + } + + /* + * Finally, Wait for transmitter & holding register to empty + * and restore the IER + */ + wait_for_xmitr(info); + serial_out(info, UART_IER, ier); +} + +#if (LINUX_VERSION_CODE <= 132114) /* Linux 2.4.18 */ +/* + * Receive character from the serial port + */ +static int dbg_console_wait_key(struct console *console) +{ + static struct async_struct *info; + int ier, c; + + info = &async_dbgcons; + + /* + * First save the IER then disable the interrupts so + * that the real driver for the port does not get the + * character. + */ + ier = serial_in(info, UART_IER); + serial_out(info, UART_IER, 0x00); + + while ((serial_in(info, UART_LSR) & UART_LSR_DR) == 0); + c = serial_in(info, UART_RX); + + /* + * Restore the interrupts + */ + serial_out(info, UART_IER, ier); + + return c; +} +#endif + +static kdev_t dbg_console_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, 80 + c->index); +} + + +static int __init dbg_console_setup(struct console *co, char *options) +{ + static struct async_struct *info; + struct serial_state *state; + int baud = BAUDRATE; + int baud_base= boot_cpu_data.bus_clock; + int bits = 8; + int parity = 'n'; + int doflow = 0; + unsigned int cflag = CREAD | HUPCL | CLOCAL | CRTSCTS; + int cval; + char *s; + + if (options) { + baud = simple_strtoul(options, NULL, 10); + s = options; + while(*s >= '0' && *s <= '9') + s++; + if (*s) parity = *s++; + if (*s) bits = *s++ - '0'; + if (*s) doflow = (*s++ == 'r'); + } + + co->flags |= CON_ENABLED; + + /* + * Now construct a cflag setting. + */ + switch(baud) { + case 1200: + cflag |= B1200; + break; + case 2400: + cflag |= B2400; + break; + case 4800: + cflag |= B4800; + break; + case 19200: + cflag |= B19200; + break; + case 38400: + cflag |= B38400; + break; + case 57600: + cflag |= B57600; + break; + case 115200: + cflag |= B115200; + break; + case 9600: + default: + cflag |= B9600; + baud = 9600; + break; + } + switch(bits) { + case 7: + cflag |= CS7; + break; + default: + case 8: + cflag |= CS8; + break; + } + switch(parity) { + case 'o': case 'O': + cflag |= PARODD; + break; + case 'e': case 'E': + cflag |= PARENB; + break; + } + co->cflag = cflag; + + state = rs_table + co->index; + if (doflow) + state->flags |= ASYNC_CONS_FLOW; + info = &async_dbgcons; + info->magic = SERIAL_MAGIC; + info->state = state; + info->port = state->port; + info->flags = state->flags; + info->io_type = state->io_type; + info->iomem_base = state->iomem_base; + info->iomem_reg_shift = state->iomem_reg_shift; + + cval = (baud_base / (baud * 4)) - 1; + + serial_outp(info, UART_LCR, 0x0300); /* init status */ + //serial_outp(info, UART_MOD1, 0x0800); /* 8bit */ + serial_outp(info, UART_MOD0, 0x80); /* cts/rts 1stop nonpari */ + //serial_outp(info, UART_MOD0, 0x180); /* rts 1stop nonpari */ + //serial_outp(info, UART_MOD0, 0xc0); /* cts/rts 1stop nonpari */ + + serial_outp(info, UART_BAUR, cval); /* set baurate reg */ + //serial_outp(info, UART_RBAUR, adj); /* set adj baurate reg */ + serial_outp(info, UART_IER, 0x00); /* intr mask */ + serial_outp(info, UART_LCR, 0x03); + + return 0; +} + +static struct console cons = { + name: "ttyD", + write: dbg_console_write, + device: dbg_console_device, +#if (LINUX_VERSION_CODE <= 132114) /* Linux 2.4.18 */ + wait_key: dbg_console_wait_key, +#endif + setup: dbg_console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; + + +/* + * Register console. + */ +void __init psio_console_init(void) +{ + register_console(&cons); +} + + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/drivers/m5.c 2004-09-03 00:57:17.267414216 -0700 @@ -0,0 +1,913 @@ +/* + * Flash Memory Driver for M32700UT-CPU + * + * [support chips] + * - M5M29GT320VP + * + * Copyright (C) 2003 Takeo Takahashi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * 2003-02-01: Takeo Takahashi, support M5M29GT320VP page program. + * + */ + +#ifndef __KERNEL__ +# define __KERNEL__ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "m5.h" + +static const char version[] = "M5 Flash Driver"; +static const char date[] = __DATE__; +static const char time[] = __TIME__; + +/* + * Special function + */ +#define M5_SUPPORT_PROBE 1 +#define M5_MAX_ERROR 5 + +/* + * flash memory start address: + */ +//#define M5_BASE_ADDR (0x00000000 + NONCACHE_OFFSET) +#define M5_BASE_ADDR (0x00000000 + PAGE_OFFSET) +#define M5_IDENT_ADDR (M5_BASE_ADDR + 0) + +/* + * This driver does not have a real partition block, but + * it can support simulation of partition in single chip. + */ +#define M5_PARTITIONS (3) +static int m5_len[M5_PARTITIONS] = { + 192 * 1024, /* 192kB: 0x00000000 - 0x0002ffff */ + 1088 * 1024, /* 1088kB: 0x00030000 - 0x0013ffff */ + 2816 * 1024 /* 2816kB: 0x00140000 - 0x003fffff */ +}; + +static int major = 240; +static int debug = 2; +static int led=0; +static int m5_addr[M5_PARTITIONS]; +static int m5_blk_size[M5_PARTITIONS]; +static int m5_blksize_size[M5_PARTITIONS]; +static int m5_hardsect_size[M5_PARTITIONS]; +static int m5_read_ahead = 1; +MODULE_PARM(major, "i"); +//MODULE_PARM_DESC(major, "major number"); +MODULE_PARM(debug, "i"); +//MODULE_PARM_DESC(debug, "debug flag"); +MODULE_PARM(led, "i"); +//MODULE_PARM_DESC(led, "LED2 support"); + +static devfs_handle_t devfs_handle = NULL; +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *proc_m5; +#endif + +#define MAJOR_NR major +#define DEVICE_NAME "m5" +#define DEVICE_NR(device) MINOR(device) +#define DEVICE_NO_RANDOM +#define DEVICE_OFF(device) +#define DEVICE_ON(device) +#define DEVICE_REQUEST m5_request +#include + +#define WAIT_TIME 10 /* 10 usec */ +#define WAIT_COUNT (50000/WAIT_TIME) /* 50ms = 10us*5000times */ + +#define SUCCESS 0 +#define FAILED (-1) + +/* + * definitions for cache + */ +static struct cache { + int blk; /* block number, -1:invalid */ + int status; /* cache status */ +#define B_CLEAN 0 +#define B_DIRTY 1 + m5_t *addr; /* block base address */ + m5_t cache[M5_BLOCK_SIZE64]; +} m5_cache; +#define M5_BLK_NOCACHED() (m5_cache.blk == -1) +#define M5_BLK_CACHED(blk) (m5_cache.blk == (blk)) +#define M5_STATE_DIRTY() (m5_cache.status == B_DIRTY) +#define M5_STATE_CLEAN() (m5_cache.status == B_CLEAN) +#define M5_MARK_DIRTY() (m5_cache.status = B_DIRTY) +#define M5_MARK_CLEAN() (m5_cache.status = B_CLEAN) +#define M5_MARK_NOCACHED() (m5_cache.blk = -1) +#define M5_MARK_CACHED(blk, addr) (m5_cache.blk = (blk), \ + m5_cache.addr = (addr)) +#define M5_CACHED_BLK m5_cache.blk +#define M5_CACHED_ADDR m5_cache.addr +#define M5_CACHED_BASE m5_cache.cache + +/* + * Prototype declarations + */ +static int m5_erase_blk(m5_t *reg); +static void m5_led_toggle(void); +static void m5_led_on(void); +static void m5_led_off(void); +static m5_t m5_in(m5_t *addr); +static void m5_out(m5_t val, m5_t *addr); +static int m5_probe(m5_t *addr); +static int m5_get_blk_num(m5_t *addr); +static int m5_get_blk_offset(int blk, m5_t *addr); +static int m5_get_blk_size(int blk); +static int m5_read_blk_cache(m5_t *addr); +static int m5_write_blk_cache(void); +static int m5_full_status_check(m5_t status, m5_t *reg); +static void m5_clear_status(m5_t *reg); +#if 0 +static int m5_status_check(m5_t *reg); +#endif +static int m5_wait_for_ready(m5_t *reg); +static int m5_write_page(m5_t *addr, const m5_t *buf); +static int m5_write(const m5_t *buf, int offset, int len); +static int m5_read_page(m5_t *buf, m5_t *addr); +static int m5_read(m5_t *buf, int offset, int len); +static int m5_erase_blk(m5_t *addr); +static void m5_request(request_queue_t *req); +static int m5_ioctl(struct inode *inode, struct file *fp, unsigned int cmd, unsigned long arg); +static int m5_open(struct inode *inode, struct file *fp); +static int m5_release(struct inode *inode, struct file *fp); +static int proc_m5_read(char *buf, char **start, off_t offset, int len, int *eof, void *unused); +static int __init m5_init(void); +static int __init m5_init_module(void); +static void __exit m5_cleanup_module(void); + +/* + * special function + */ +static inline void m5_led_toggle(void) +{ + unsigned short status; + if (!led) return; + status = inw((unsigned long)PLD_IOLEDCR); + if (status & PLD_IOLED_2_ON) + status &= ~PLD_IOLED_2_ON; + else + status |= PLD_IOLED_2_ON; + outw(status, (unsigned long)PLD_IOLEDCR); +} + +static inline void m5_led_on(void) +{ + unsigned short status; + if (!led) return; + status = inw((unsigned long)PLD_IOLEDCR); + status |= PLD_IOLED_2_ON; + outw(status, (unsigned long)PLD_IOLEDCR); +} + +static inline void m5_led_off(void) +{ + unsigned short status; + if (!led) return; + status = inw((unsigned long)PLD_IOLEDCR); + status &= ~PLD_IOLED_2_ON; + outw(status, (unsigned long)PLD_IOLEDCR); +} + +/* + * I/O function + */ +static inline m5_t m5_in(m5_t *addr) +{ + return *addr; +} + +static inline void m5_out(m5_t val, m5_t *addr) +{ + *addr = val; +} + +#if M5_SUPPORT_PROBE +/* + * int m5_probe() + * m5_t *addr: probed address + * int SUCCESS: supported device + * int FAILED: unsupported device + */ +static int m5_probe(m5_t *addr) +{ +#if 1 + /* + * FIXME: cannot read maker & device codes. + */ + m5_t val; + m5_out(M5_CMD_DEVICE_IDENT, addr); + val = m5_in((m5_t *)M5_IDENT_ADDR); + val &= 0xff; + if (val == M5_MAKER) + printk("m5: detected M5M29GT320VP\n"); + else + printk("m5: unknown maker(0x%02x)\n", val); + m5_out(M5_CMD_READ_ARRAY, addr); /* array mode */ + /* always success */ + return SUCCESS; +#else + + int result; + m5_t val; + unsigned char maker, device; + + result = SUCCESS; + m5_out(M5_CMD_DEVICE_IDENT, addr); + val = m5_in((m5_t *)M5_IDENT_ADDR); + maker = (val >> 16); + device = (val & 0xff); + if (maker != M5_MAKER) { + printk("m5: unknown maker(0x%02x)\n", maker); + // result = FAILED; + // ignore error + } + if (device != M5_M5M29GT320VP) { + printk("m5: unknown device(0x%02x)\n", device); + // result = FAILED; + // ignore error + } + if (result != FAILED) + printk("m5: detected M5M29GT320VP\n"); + m5_out(M5_CMD_READ_ARRAY, addr); + return result; +#endif +} +#endif /* M5_SUPPORT_PROBE */ + +/* + * int m5_get_blk_num() + * m5_t *addr: + * blk: OK, return block number + * -1: NG + */ +static int m5_get_blk_num(m5_t *addr) +{ + int blk; + + blk = (((int)addr & 0x0fff0000) >> 16); + if (blk > 0x3f) { + printk("m5: out of block address (0x%08x)\n", (int)addr); + return -1; + } + if (blk == 0x3f) + blk += (int)(((int)addr & 0x0000e000) >> 13); + if (blk > MAX_BLOCK_NUM) { + printk("m5: out of block address (addr=0x%08x, blk=%d)\n", + (int)addr, blk); + return -1; + } + return blk; +} + +/* + * m5_t *m5_get_blk_base() + * return block base address + */ +static inline m5_t *m5_get_blk_base(int blk, m5_t *addr) +{ + if (blk < 0x3f) return (m5_t *)((int)addr & 0xffff0000); + return (m5_t *)((int)addr & 0xffffe000); +} + +/* + * int m5_get_blk_offset() + * return offset of specified address in blk + */ +static inline int m5_get_blk_offset(int blk, m5_t *addr) +{ + if (blk < 0x3f) return ((int)addr & 0xffff); + return ((int)addr & 0x1fff); +} + +/* + * int m5_get_blk_size() + * return block size in byte + */ +static inline int m5_get_blk_size(int blk) +{ + if (blk >= 63) return M5_BLOCK_SIZE8; + return M5_BLOCK_SIZE64; +} + +/* + * cache operations + */ +static int m5_read_blk_cache(m5_t *addr) +{ + int blk; + int size; + int result; + + result = SUCCESS; + blk = m5_get_blk_num(addr); + if (blk < 0) return FAILED; + if (M5_BLK_CACHED(blk)) + return result; + if (M5_STATE_DIRTY()) { /* cached other block */ + result = m5_write_blk_cache(); + if (result != SUCCESS) return result; + } + /* ok, we can use cache */ + size = m5_get_blk_size(blk); + addr = m5_get_blk_base(blk, addr); + memcpy((void *)M5_CACHED_BASE, (void *)addr, size); + M5_MARK_CACHED(blk, addr); + M5_MARK_CLEAN(); + return result; +} + +static int m5_write_blk_cache(void) +{ + int size; + int pages; + int i; + m5_t *reg, *addr, *buf; + int result; + + result = SUCCESS; + + if (M5_BLK_NOCACHED()) + return result; + if (! M5_STATE_DIRTY()) + return result; + + /* we have to write cache */ + m5_led_on(); + size = m5_get_blk_size(M5_CACHED_BLK); + addr = M5_CACHED_ADDR; + buf = M5_CACHED_BASE; + reg = addr; + + result = m5_erase_blk(reg); + if (result != SUCCESS) return result; + + DEBUG(1, "m5: wrting addr=0x%08x, buf=0x%08x, size=%d\n",\ + (int)addr, (int)buf, size); + for (pages = 0; pages < (size/M5_PAGE_SIZE); pages++) { + m5_out(M5_CMD_PROGRAM_PAGE, reg); + for (i = 0; i < (M5_PAGE_SIZE/sizeof(m5_t)); i++) + *addr++ = *buf++; + if (m5_wait_for_ready(reg) != SUCCESS) + result = FAILED; + reg = addr; + } + m5_out(M5_CMD_READ_ARRAY, reg); /* array mode */ + if (result == SUCCESS) + M5_MARK_CLEAN(); + m5_led_off(); + return result; /* SUCCESS or FAILED */ +} + +/* + * int m5_full_status_check() + * m5_t status: status + * m5_t *reg: bank address + * SUCCESS: no error + * FAILED: error happen + */ +static inline int m5_full_status_check(m5_t status, m5_t *reg) +{ + if ((status & M5_STATUS_PROGRAM) && + (status & M5_STATUS_ERASE)) { + printk("m5: command sequence error (addr=0x%08x, sts=0x%02x).\n", + (int)reg, status); + return FAILED; + } + if(status & M5_STATUS_ERASE){ + printk("m5: erase error (addr=0x%08x, sts=0x%02x).\n", + (int)reg, status); + return FAILED; + } + if(status & M5_STATUS_PROGRAM){ + printk("m5: program write error (addr=0x%08x, sts=0x%02x).\n", + (int)reg, status); + return FAILED; + } + if(status & M5_STATUS_BLOCK){ + printk("m5: block write error (addr=0x%08x, sts=0x%02x).\n", + (int)reg, status); + return FAILED; + } + /* passed */ + return SUCCESS; +} + +/* + * void m5_clear_status() + * m5_t *reg: address of status register + */ +static inline void m5_clear_status(m5_t *reg) +{ + m5_out(M5_CMD_CLEAR_STATUS, reg); +} + +#if 0 +/* + * int m5_status_check() + * m5_t *reg: address of command register + * SUCCESS: ready now + * FAILED: error happen while waiting + */ +static int m5_status_check(m5_t *reg) +{ + m5_t status; + int result; + int n; + static int error_count = 0; + + m5_out(M5_CMD_READ_STATUS, reg); + for (n = WAIT_COUNT; n > 0; n--) { + status = m5_in(reg); + if (status & M5_STATUS_READY) + break; + udelay(WAIT_TIME); + } + if (n <= 0) { + if (error_count++ < M5_MAX_ERROR) + printk("m5: time out (sts=0x%02x).\n", status); + m5_clear_status(reg); + return FAILED; + } + result = m5_full_status_check(status, reg); + if (result != SUCCESS) { + m5_clear_status(reg); + return FAILED; + } + m5_clear_status(reg); + return SUCCESS; +} +#endif + +/* + * int m5_wait_for_ready() + * m5_t *reg: address of command register + * SUCCESS: ready now + * FAILED: error happen while waiting + */ +static int m5_wait_for_ready(m5_t *reg) +{ + m5_t status; + int result; + int n; + static int error_count = 0; + + for (n = WAIT_COUNT; n > 0; n--) { + status = m5_in(reg); + if (status & M5_STATUS_READY) + break; + udelay(WAIT_TIME); + } + if (n <= 0) { + if (error_count++ < M5_MAX_ERROR) + printk("m5: time out (sts=0x%02x).\n", status); + m5_clear_status(reg); + return FAILED; + } + result = m5_full_status_check(status, reg); + if (result != SUCCESS) { + m5_clear_status(reg); + return FAILED; + } + return SUCCESS; +} + +/* + * int m5_write_page(m5_t *addr, const m5_t *buf) + * m5_t *addr: sector address + * m5_t *buf: buffer address + * SUCCESS: ok + * FAILED: error + */ +static int m5_write_page(m5_t *addr, const m5_t *buf) +{ + int blk; + int offset; + + if (((int)addr % M5_PAGE_SIZE) != 0) + printk("m5: invalid sector addres (0x%08x)\n", (int)addr); + + if (m5_read_blk_cache(addr) != SUCCESS) + return FAILED; + if ((blk = m5_get_blk_num(addr)) < 0) + return FAILED; + offset = m5_get_blk_offset(blk, addr); + memcpy((void *)M5_CACHED_BASE + offset, (void *)buf, M5_PAGE_SIZE); + M5_MARK_DIRTY(); + return SUCCESS; +} + +/* + * int m5_write(const m5_t *buf, int offset, int len) + * m5_t *buf: write buffer address + * int offset: offset from flash base address + * int len: write length + * SUCCESS: ok + * FAILE: error + */ +static int m5_write(const m5_t *buf, int offset, int len) +{ + m5_t *addr; + int result; + + if (len % M5_PAGE_SIZE) { + printk("m5: invalid write size (%d).\n", len); + return FAILED; + } + addr = (m5_t *)(m5_addr[MINOR(CURRENT_DEV)] + offset); + + DEBUG(1, "m5: writing 0x%08x - 0x%08x\n", \ + (int)addr, (int)addr + len); + + for (; len > 0; len -= M5_PAGE_SIZE) { + result = m5_write_page(addr, buf); + if (result != SUCCESS) + return result; + addr = (m5_t *)((int)addr + M5_PAGE_SIZE); + buf = (m5_t *)((int)buf + M5_PAGE_SIZE); + } + return SUCCESS; +} + +/* + * int m5_read_page() + * m5_t *buf: read buffer address + * m5_t *addr: page address + * SUCCESS: ok + * FAILED: error + */ +static int m5_read_page(m5_t *buf, m5_t *addr) +{ + int blk; + int offset; + + if ((blk = m5_get_blk_num(addr)) < 0) + return FAILED; + if (M5_BLK_CACHED(blk)) { + offset = m5_get_blk_offset(blk, addr); + memcpy((void *)buf, (void *)M5_CACHED_BASE + offset, + M5_PAGE_SIZE); + } else { + /* read from flash memory directory */ + memcpy((void *)buf, (void *)addr, M5_PAGE_SIZE); + } + return SUCCESS; +} + +/* + * int m5_read() + * m5_t *buf: read buffer address + * int offset: offset from flash base address + * int len: read length + * SUCCESS: ok + * FAILED: error + */ +static int m5_read(m5_t *buf, int offset, int len) +{ + m5_t *addr; + + if (len % M5_PAGE_SIZE) { + printk("m5: invalid read size (%d).\n", len); + return FAILED; + } + + addr = (m5_t *)(m5_addr[MINOR(CURRENT_DEV)] + offset); + + DEBUG(1, "m5: reading 0x%08x - 0x%08x\n", \ + (int)addr, (int)addr + len); + + for (; len > 0; len -= M5_PAGE_SIZE) { + if (m5_read_page(buf, addr) != SUCCESS) + return FAILED; + buf = (m5_t *)((int)buf + M5_PAGE_SIZE); + addr = (m5_t *)((int)addr + M5_PAGE_SIZE); + } + return SUCCESS; +} + +/* + * int m5_erase_blk() + * m5_t *addr: + */ +static int m5_erase_blk(m5_t *addr) +{ + int result; + + DEBUG(1, "m5: erasing addr=0x%08x\n", (int)addr); + m5_out(M5_CMD_BLOCK_ERASE, addr); + m5_out(M5_CMD_CONFIRM, addr); + result = m5_wait_for_ready(addr); + return result; +} + +/* + * void m5_request() + * request_queue_t *req: I/O request + * end_request(0): error (-EIO) + * end_request(1): ok + */ +static void m5_request(request_queue_t *req) +{ + unsigned int minor; + int offset; + int len; + static int error_count = 0; + + sti(); + while (1) { + INIT_REQUEST; + minor = MINOR(CURRENT_DEV); + if (minor >= M5_PARTITIONS) { + printk( "m5: out of partition (%d)\n", minor ); + end_request(0); + continue; + } + offset = CURRENT->sector * m5_hardsect_size[minor]; + len = (CURRENT->current_nr_sectors * + m5_hardsect_size[minor]); + if ((offset + len) > m5_len[minor]) { + printk( "m5: out of sector (sector=%d, nr=%d)\n", + (int)(CURRENT->sector), + (int)(CURRENT->current_nr_sectors)); + end_request(0); + continue; + } + switch(CURRENT->cmd) { + case READ: + if (m5_read((m5_t *)CURRENT->buffer, + offset, len) != SUCCESS) + end_request(0); + else + end_request(1); + break; + case WRITE: + if (m5_write((m5_t *)(CURRENT->buffer), + offset, len) != SUCCESS) + end_request(0); + else + end_request(1); + break; + default: + if (error_count++ < M5_MAX_ERROR) { + printk("m5: invalid I/O request(%d)\n", + CURRENT->cmd); + } + end_request(0); + break; + } + } +} + +/* + * int m5_ioctl() + * struct inode *inode: + * struct file *file: + * unsigned int cmd: + * unsigned long arg: + */ +static int m5_ioctl(struct inode *inode, struct file *fp, unsigned int cmd, unsigned long arg) +{ + int size; + + DEBUG(2, "m5_ioctl()\n"); + switch (cmd) { + case BLKGETSIZE: + if (!arg) return -EINVAL; + size = (1024 * m5_blk_size[MINOR(inode->i_rdev)] / + m5_hardsect_size[MINOR(inode->i_rdev)]); + return put_user(size, (long __user *)arg); + case BLKFLSBUF: + if (!capable(CAP_SYS_ADMIN)) return -EACCES; + fsync_dev(inode->i_rdev); + invalidate_buffers(inode->i_rdev); + return 0; + case BLKRRPART: + return -EINVAL; + + case BLKRAGET: + case BLKRASET: + case BLKGETSIZE64: + case BLKROSET: + case BLKROGET: + case BLKSSZGET: + return blk_ioctl(inode->i_rdev, cmd, arg); + default: + printk( "m5: unsupported ioctl(0x%08x)\n", cmd); + return -EINVAL; + } + return 0; +} + +/* + * int m5_open() + * struct inode *inode: + * struct file *fp: + */ +static int m5_open(struct inode *inode, struct file *fp) +{ + if (DEVICE_NR(inode->i_rdev) >= M5_PARTITIONS) return -ENXIO; + MOD_INC_USE_COUNT; + return 0; +} + +/* + * int m5_release() + * struct inode *inode: + * struct file *fp: + */ +static int m5_release(struct inode *inode, struct file *fp) +{ + sync_dev(inode->i_rdev); + MOD_DEC_USE_COUNT; + return 0; +} + +#ifdef CONFIG_PROC_FS +/* + * int proc_m5_read() + * char *buf: + * char **start: + * off_t offset: + * int len: + * int *eof: + * void *unused: + */ +static int proc_m5_read(char *buf, char **start, off_t offset, int len, int *eof, void *unused) +{ + len = sprintf(buf, "partition: %d\n", M5_PARTITIONS); + return len; +} +#endif + +static struct block_device_operations m5_fops = { + open: m5_open, + release: m5_release, + ioctl: m5_ioctl, + /* check_media_change: */ + /* revalidate: */ + owner: THIS_MODULE, +}; + +/* + * int m5_init() + */ +static int __init m5_init(void) +{ + int i; + int result; + + result = -EIO; + + printk("%s (%s %s)\n", version, date, time); +#if M5_SUPPORT_PROBE + if (m5_probe((m5_t *)M5_BASE_ADDR) != SUCCESS) + return result; +#endif /* M5_SUPPORT_PROBE */ + + if (register_blkdev(major, DEVICE_NAME, &m5_fops) ) { + printk("m5: can not not get major %d", major); + return result; + } + + if (devfs_register_blkdev(major, DEVICE_NAME, &m5_fops) ) { + printk("m5: can not not get major %d", major); + goto fail_devfs; + } + devfs_handle = devfs_mk_dir(NULL, "m5", NULL); + devfs_register_series(devfs_handle, "%u", M5_PARTITIONS, + DEVFS_FL_DEFAULT, major, 0, + S_IFBLK | S_IRUSR | S_IWUSR, + &m5_fops, NULL); + + for (i = 0; i < M5_PARTITIONS; i++) { + if (i) { + m5_addr[i] = m5_addr[i-1] + m5_len[i-1]; + } else { + m5_addr[i] = M5_BASE_ADDR; + } + m5_blk_size[i] = m5_len[i]/1024; /* KB order */ + m5_blksize_size[i] = BLOCK_SIZE; /* 1024 byte */ + m5_hardsect_size[i] = BLOCK_SIZE/2; /* 512 byte */ + } + /* defined in ll_rw_blk.c */ + blk_size[major] = m5_blk_size; + blksize_size[major] = m5_blksize_size; + hardsect_size[major] = m5_hardsect_size; + read_ahead[major] = m5_read_ahead; + + blk_init_queue(BLK_DEFAULT_QUEUE(major), &m5_request); + + for (i = 0; i < M5_PARTITIONS; i++) + register_disk( NULL, MKDEV(major,i), 1, + &m5_fops, m5_len[i]>>9 ); + +#ifdef CONFIG_PROC_FS +#if 1 + proc_m5 = proc_mkdir("m5", proc_root_driver); + if (!proc_m5) { + printk(KERN_ERR "m5: unable to register driver/m5\n"); + goto fail_proc; + } + create_proc_read_entry("0", 0, proc_m5, proc_m5_read, 0); + create_proc_read_entry("1", 0, proc_m5, proc_m5_read, 0); + create_proc_read_entry("2", 0, proc_m5, proc_m5_read, 0); +#else + proc_m5 = create_proc_entry("driver/m5", 666, 0); + if (!proc_m5) { + printk(KERN_ERR "m5: unable to register driver/m5\n"); + goto fail_proc; + } + proc_m5->read_proc = proc_m5_read; +#endif +#endif + + printk("m5: Major=%d Partitions=%d\n", major,M5_PARTITIONS); + for (i = 0; i < M5_PARTITIONS; i++) { + printk(" %d: 0x%08x-0x%08x (all=%dKB,blk=%dB,sect=%dB,ahead=%d)\n", + i, m5_addr[i], + m5_addr[i] + m5_len[i], + m5_blk_size[i], + m5_blksize_size[i], + m5_hardsect_size[i], + m5_read_ahead); + } + + /* clear cache */ + M5_MARK_CLEAN(); + M5_MARK_NOCACHED(); + + m5_led_off(); + return 0; + +fail_proc: + devfs_unregister(devfs_handle); +fail_devfs: + unregister_blkdev(major, DEVICE_NAME); + return result; +} + +static int __init m5_init_module(void) +{ + return m5_init(); +} + +static void __exit m5_cleanup_module(void) +{ + int i; + + for (i = 0 ; i < M5_PARTITIONS; i++) { + fsync_dev(MKDEV(major, i)); /* flush buffer */ + destroy_buffers(MKDEV(major, i)); + } + m5_write_blk_cache(); /* flush m5 cache */ + devfs_unregister(devfs_handle); + unregister_blkdev(major, DEVICE_NAME); +#ifdef CONFIG_PROC_FS + remove_proc_entry("0", proc_m5); + remove_proc_entry("1", proc_m5); + remove_proc_entry("2", proc_m5); + remove_proc_entry("m5", proc_root_driver); +#endif + blk_cleanup_queue(BLK_DEFAULT_QUEUE(major)); + blk_size[major] = NULL; + blksize_size[major] = NULL; + hardsect_size[major] = NULL; + read_ahead[major] = 0; +} + +module_init(m5_init_module); +module_exit(m5_cleanup_module); + +MODULE_AUTHOR("Takeo Takahashi"); +MODULE_DESCRIPTION("M5 Flash Driver for M32700UT-CPU"); +MODULE_LICENSE("GPL"); +EXPORT_NO_SYMBOLS; + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/drivers/m5drv.c 2004-09-03 00:57:17.270413760 -0700 @@ -0,0 +1,664 @@ +/* + * MTD chip driver for M5M29GT320VP + * + * Copyright (C) 2003 Takeo Takahashi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * $Id$ + */ + +#ifndef __KERNEL__ +# define __KERNEL__ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define M5DRV_DEBUG(n, args...) if ((n) & m5drv_debug) printk(KERN_DEBUG args) + +#undef UNLOCK_BEFORE_ERASE + +#define M5DRV_PAGE_SIZE (256) /* page program size */ +#define M5DRV_BLOCK_SIZE8 (8*1024) /* 8K block size in byte */ +#define M5DRV_BLOCK_SIZE64 (64*1024) /* 64K block size in byte */ +#define M5DRV_MAX_BLOCK_NUM 70 /* number of blocks */ +#define M5DRV_ERASE_REGION 2 /* 64KB and 8KB */ + +/* + * Software commands + */ +#define CMD_READ_ARRAY 0xff +#define CMD_DEVICE_IDENT 0x90 +#define CMD_READ_STATUS 0x70 +#define CMD_CLEAR_STATUS 0x50 +#define CMD_BLOCK_ERASE 0x20 +#define CMD_CONFIRM 0xd0 +#define CMD_PROGRAM_BYTE 0x40 +#define CMD_PROGRAM_WORD CMD_PROGRAM_BYTE +#define CMD_PROGRAM_PAGE 0x41 +#define CMD_SINGLE_LOAD_DATA 0x74 +#define CMD_BUFF2FLASH 0x0e +#define CMD_FLASH2BUFF 0xf1 +#define CMD_CLEAR_BUFF 0x55 +#define CMD_SUSPEND 0xb0 +#define CMD_RESUME 0xd0 +#define IDENT_OFFSET 0 /* indent command offset */ + +/* + * Status + */ +#define STATUS_READY 0x80 /* 0:busy 1:ready */ +#define STATUS_SUSPEND 0x40 /* 0:progress/complete 1:suspend */ +#define STATUS_ERASE 0x20 /* 0:pass 1:error */ +#define STATUS_PROGRAM 0x10 /* 0:pass 1:error */ +#define STATUS_BLOCK 0x08 /* 0:pass 1:error */ + +/* + * Device Code + */ +#define MAKER (0x1c) +#define M5M29GT320VP (0x20) +#define M5M29GB320VP (0x21) + +static const char version[] = "M5DRV Flash Driver"; +static const char date[] = __DATE__; +static const char time[] = __TIME__; +static int m5drv_debug = 0; +MODULE_PARM(m5drv_debug, "i"); + +struct m5drv_info { + struct flchip *chip; + int chipshift; + int numchips; + struct flchip chips[1]; + unsigned char buf[M5DRV_BLOCK_SIZE64]; +#define M5BUF (m5drv->buf) +}; + +struct mtd_info *m5drv_probe(struct map_info *map); +static int m5drv_probe_map(struct map_info *map, struct mtd_info *mtd); +static int m5drv_wait(struct map_info *map, struct flchip *chip, loff_t adr); +static void m5drv_release(struct flchip *chip); +static int m5drv_query_blksize(loff_t ofs); +static int m5drv_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); +static int m5drv_read_oneblock(struct map_info *map, loff_t from); +static int m5drv_write(struct mtd_info *mtd, loff_t adr, size_t len, size_t *retlen, const u_char *buf); +static int m5drv_write_oneblock(struct map_info *map, loff_t adr, size_t len, const u_char *buf); +static int m5drv_write_onepage(struct map_info *map, struct flchip *chip, unsigned long adr, const u_char *buf); +static int m5drv_erase(struct mtd_info *mtd, struct erase_info *instr); +static int m5drv_do_wait_for_ready(struct map_info *map, struct flchip *chip, unsigned long adr); +static int m5drv_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr); +static void m5drv_sync(struct mtd_info *mtd); +static int m5drv_suspend(struct mtd_info *mtd); +static void m5drv_resume(struct mtd_info *mtd); +static void m5drv_destroy(struct mtd_info *mtd); +#ifdef UNLOCK_BEFORE_ERASE +static void m5drv_unlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr); +#endif + +static struct mtd_chip_driver m5drv_chipdrv = { + probe: m5drv_probe, + destroy: m5drv_destroy, + name: "m5drv", + module: THIS_MODULE +}; + +struct mtd_info *m5drv_probe(struct map_info *map) +{ + struct mtd_info *mtd = NULL; + struct m5drv_info *m5drv = NULL; + int width; + + mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + if (!mtd) { + printk("m5drv: can not allocate memory for mtd_info\n"); + return NULL; + } + + m5drv = kmalloc(sizeof(*m5drv), GFP_KERNEL); + if (!m5drv) { + printk("m5drv: can not allocate memory for m5drv_info\n"); + kfree(mtd); + return NULL; + } + + memset(mtd, 0, sizeof(*mtd)); + width = m5drv_probe_map(map, mtd); + if (!width) { + printk("m5drv: m5drv_probe_map error (width=%d)\n", width); + kfree(mtd); + kfree(m5drv); + return NULL; + } + mtd->priv = map; + mtd->type = MTD_OTHER; + mtd->erase = m5drv_erase; + mtd->read = m5drv_read; + mtd->write = m5drv_write; + mtd->sync = m5drv_sync; + mtd->suspend = m5drv_suspend; + mtd->resume = m5drv_resume; + mtd->flags = MTD_CAP_NORFLASH; /* ??? */ + mtd->name = map->name; + + memset(m5drv, 0, sizeof(*m5drv)); + m5drv->chipshift = 23; + m5drv->numchips = 1; + m5drv->chips[0].start = 0; + m5drv->chips[0].state = FL_READY; + m5drv->chips[0].mutex = &m5drv->chips[0]._spinlock; + m5drv->chips[0].word_write_time = 0; + init_waitqueue_head(&m5drv->chips[0].wq); + spin_lock_init(&m5drv->chips[0]._spinlock); + + map->fldrv = &m5drv_chipdrv; + map->fldrv_priv = m5drv; + + MOD_INC_USE_COUNT; + return mtd; +} + +static int m5drv_probe_map(struct map_info *map, struct mtd_info *mtd) +{ + u16 tmp; + u16 maker, device; + int width = 2; + struct mtd_erase_region_info *einfo; + + map->write16(map, CMD_READ_ARRAY, IDENT_OFFSET); + tmp = map->read16(map, IDENT_OFFSET); + map->write16(map, CMD_DEVICE_IDENT, IDENT_OFFSET); + maker = map->read16(map, IDENT_OFFSET); + maker &= 0xff; + if (maker == MAKER) { + /* FIXME: check device */ + device = maker >> 8; + printk("m5drv: detected M5M29GT320VP\n"); + einfo = kmalloc(sizeof(*einfo) * M5DRV_ERASE_REGION, GFP_KERNEL); + if (!einfo) { + printk("m5drv: cannot allocate memory for erase_region\n"); + return 0; + } + /* 64KB erase block (blk no# 0-62) */ + einfo[0].offset = 0; + einfo[0].erasesize = 0x8000 * width; + einfo[0].numblocks = (7 + 8 + 24 + 24); + /* 8KB erase block (blk no# 63-70) */ + einfo[1].offset = 0x3f0000; + einfo[1].erasesize = 0x1000 * width; + einfo[1].numblocks = (2 + 8); + mtd->numeraseregions = M5DRV_ERASE_REGION; + mtd->eraseregions = einfo; + mtd->size = 0x200000 * width; /* total 4MB */ + /* + * mtd->erasesize is used in parse_xxx_partitions. + * last erase block has a partition table. + */ + mtd->erasesize = 0x8000 * width; + return width; + } else if (map->read16(map, IDENT_OFFSET) == CMD_DEVICE_IDENT) { + printk("m5drv: looks like RAM\n"); + map->write16(map, tmp, IDENT_OFFSET); + } else { + printk("m5drv: can not detect flash memory (0x%04x)\n", maker); + } + map->write16(map, CMD_READ_ARRAY, IDENT_OFFSET); + return 0; +} + +static int m5drv_query_blksize(loff_t ofs) +{ + int blk; + + blk = ofs >> 16; + if (blk > 0x3f) { + printk("m5drv: out of block address (0x%08x)\n", (u32)ofs); + return M5DRV_BLOCK_SIZE64; + } + if (blk == 63) blk += ((ofs & 0x0000e000) >> 13); + if (blk > M5DRV_MAX_BLOCK_NUM) { + printk("m5drv: out of block address (0x%08x)\n", (u32)ofs); + return M5DRV_BLOCK_SIZE64; + } + return ((blk >= 63)? M5DRV_BLOCK_SIZE8:M5DRV_BLOCK_SIZE64); +} + +static int m5drv_wait(struct map_info *map, struct flchip *chip, loff_t adr) +{ + __u16 status; + unsigned long timeo; + DECLARE_WAITQUEUE(wait, current); + + timeo = jiffies + HZ; + adr &= ~1; /* align 2 */ + +retry: + spin_lock_bh(chip->mutex); + + switch (chip->state) { + case FL_READY: + map->write16(map, CMD_READ_STATUS, adr); + chip->state = FL_STATUS; + case FL_STATUS: + status = map->read16(map, adr); + if ((status & STATUS_READY) != STATUS_READY) { + udelay(100); + } + break; + default: + printk("m5drv: waiting for chip\n"); + if (time_after(jiffies, timeo)) { /* jiffies is after timeo */ + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + spin_unlock_bh(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); + spin_lock_bh(chip->mutex); // by takeo + if (signal_pending(current)) { + printk("m5drv: canceled\n"); + map->write16(map, CMD_CLEAR_STATUS, adr); + map->write16(map, CMD_READ_ARRAY, adr); + chip->state = FL_READY; + return -EINTR; + } + } + timeo = jiffies + HZ; + goto retry; + } + map->write16(map, CMD_READ_ARRAY, adr); + chip->state = FL_READY; + return 0; +} + +static void m5drv_release(struct flchip *chip) +{ + M5DRV_DEBUG(1, "m5drv_release\n"); + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); +} + +static int m5drv_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + struct map_info *map = mtd->priv; + struct m5drv_info *m5drv = map->fldrv_priv; + int chipnum; + int ret; + + *retlen = 0; + + chipnum = (from >> m5drv->chipshift); + if (chipnum >= m5drv->numchips) { + printk("m5drv: out of chip number (%d)\n", chipnum); + return -EIO; + } + + /* We don't support erase suspend */ + ret = m5drv_wait(map, &m5drv->chips[chipnum], from); + if (ret < 0) return ret; + + map->copy_from(map, buf, from, len); + + m5drv_release(&m5drv->chips[chipnum]); + *retlen = len; + return 0; +} + +static int m5drv_read_oneblock(struct map_info *map, loff_t from) +{ + struct m5drv_info *m5drv = map->fldrv_priv; + int ofs; + int ret; + int blksize; + int chipnum; + + M5DRV_DEBUG(1, "m5drv_read_oneblock(0x%08x)\n", (u32)from); + chipnum = (from >> m5drv->chipshift); + blksize = m5drv_query_blksize(from); + ofs = (from & ~(blksize - 1)); + + ret = m5drv_wait(map, &m5drv->chips[chipnum], from); + if (ret < 0) return ret; + + map->copy_from(map, M5BUF, ofs, blksize); + + m5drv_release(&m5drv->chips[chipnum]); + return 0; +} + +static int m5drv_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) +{ + struct map_info *map = mtd->priv; + struct m5drv_info *m5drv = map->fldrv_priv; + int ret = 0; + int blksize; + int chipnum; + int thislen; + + M5DRV_DEBUG(1, "m5drv_write(to=0x%08x, len=%d, buf=0x%08x\n", (u32)to, (u32)len, (u32)buf); + *retlen = 0; + blksize = m5drv_query_blksize(to); + chipnum = (to >> m5drv->chipshift); + + /* + * we does not support byte/word program yet. + */ + for (thislen = len; thislen > 0; thislen -= blksize) { + thislen = ((thislen >= blksize)? blksize:thislen); + ret = m5drv_write_oneblock(map, to, thislen, buf); + if (ret < 0) return ret; + to += blksize; + buf += blksize; + *retlen += thislen; + } + return 0; +} + +static int m5drv_write_oneblock(struct map_info *map, loff_t adr, size_t len, const u_char *buf) +{ + struct m5drv_info *m5drv = map->fldrv_priv; + int ofs; + int blksize; + int ret; + int chipnum; + int i; + + M5DRV_DEBUG(1, "m5drv_write_oneblock(0x%08x, %d)\n", (u32)adr, (u32)len); + chipnum = (adr >> m5drv->chipshift); + ret = m5drv_read_oneblock(map, adr); + if (ret < 0) return ret; + blksize = m5drv_query_blksize(adr); + ofs = (adr & (blksize - 1)); + adr = adr & ~(blksize - 1); + memcpy(M5BUF + ofs, buf, len); /* copy to block buffer */ +#if 0 /* + * FIXME: erasing is unnecessary. + */ + ret = m5drv_erase_oneblock(map, &m5drv->chips[chipnum], adr); + if (ret < 0) return ret; +#endif + for (i = 0; i < len; i += M5DRV_PAGE_SIZE) { + ret = m5drv_write_onepage(map, &m5drv->chips[chipnum], adr, M5BUF+i); + if (ret < 0) return ret; + adr += M5DRV_PAGE_SIZE; + } + return 0; +} + +static int m5drv_write_onepage(struct map_info *map, struct flchip *chip, unsigned long adr, const u_char *buf) +{ + int ret; + int i; + u_short data; + long padr; /* page address */ + u_short status; + int chipnum; + struct m5drv_info *m5drv = map->fldrv_priv; + + M5DRV_DEBUG(1, "m5drv_write_onepage(0x%08x, 0x%08x)\n", (u32)adr, (u32)buf); + padr = adr; + padr &= ~1; /* align 2 */ + chipnum = (adr >> m5drv->chipshift); + + ret = m5drv_wait(map, chip, padr); + if (ret < 0) return ret; + + map->write16(map, CMD_PROGRAM_PAGE, padr); + chip->state = FL_WRITING; + for (i = 0; i < M5DRV_PAGE_SIZE; i += map->buswidth) { + data = ((*buf << 8)| *(buf + 1)); + /* + * FIXME: convert be->le ? + */ + map->write16(map, data, adr); + adr += map->buswidth; + buf += map->buswidth; + } + + ret = m5drv_do_wait_for_ready(map, chip, padr); + if (ret < 0) { + m5drv_release(&m5drv->chips[chipnum]); + return ret; + } + + status = map->read16(map, padr); + if ((status & STATUS_READY) != STATUS_READY) { + printk("m5drv: error page writing at addr=0x%08x status=0x%08x\n", + (u32)padr, (u32)status); + map->write16(map, CMD_CLEAR_STATUS, padr); + } + map->write16(map, CMD_READ_ARRAY, padr); + chip->state = FL_READY; + m5drv_release(&m5drv->chips[chipnum]); + return 0; +} + +static int m5drv_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct map_info *map = mtd->priv; + struct m5drv_info *m5drv = map->fldrv_priv; + unsigned long adr,len; + int chipnum, ret=0; + int erasesize = 0; + int i; + + M5DRV_DEBUG(2, "m5drv_erase(0x%08x)\n", instr->addr); + chipnum = instr->addr >> m5drv->chipshift; + if (chipnum >= m5drv->numchips) { + printk("m5drv: out of chip number (%d)\n", chipnum); + return -EIO; + } + adr = instr->addr & ((1<chipshift)-1); + len = instr->len; + if (mtd->numeraseregions == 0) { + erasesize = mtd->erasesize; + } else if (mtd->numeraseregions == 1) { + erasesize = mtd->eraseregions->erasesize; + } else { + for (i = 0; i < (mtd->numeraseregions - 1); i++) { + if (adr < mtd->eraseregions[i+1].offset) { + erasesize = mtd->eraseregions[i].erasesize; + break; + } + } + if (i == (mtd->numeraseregions - 1)) { /* last region */ + erasesize = mtd->eraseregions[i].erasesize; + } + } + M5DRV_DEBUG(2, "erasesize=%d, len=%ld\n", erasesize, len); + if (erasesize == 0) return -EINVAL; + if(instr->addr & (erasesize - 1)) + return -EINVAL; + if(instr->len & (erasesize - 1)) + return -EINVAL; + if(instr->len + instr->addr > mtd->size) + return -EINVAL; + + while (len) { + ret = m5drv_erase_oneblock(map, &m5drv->chips[chipnum], adr); + if (ret < 0) return ret; + + adr += erasesize; + len -= erasesize; + if(adr >> m5drv->chipshift){ + adr = 0; + chipnum++; + if(chipnum >= m5drv->numchips) + break; + } + } + instr->state = MTD_ERASE_DONE; + if(instr->callback) { + M5DRV_DEBUG(1, "m5drv: call callback\n"); + instr->callback(instr); + } + return 0; +} + +static int m5drv_do_wait_for_ready(struct map_info *map, struct flchip *chip, unsigned long adr) +{ + int ret; + int timeo; + u_short status; + DECLARE_WAITQUEUE(wait, current); + + /* unnecessary CMD_READ_STATUS */ +/* + map->write16(map, CMD_READ_STATUS, adr); + status = map->read16(map, adr); +*/ + + timeo = jiffies + HZ; + + while (time_before(jiffies, timeo)) { +/* + map->write16(map, CMD_READ_STATUS, adr); +*/ + status = map->read16(map, adr); + if ((status & STATUS_READY) == STATUS_READY) { + M5DRV_DEBUG(1, "m5drv_wait_for_ready: ok, ready\n"); + /* + * FIXME: do full status check + */ + ret = 0; + goto out; + } + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + + // enabled by takeo + spin_unlock_bh(chip->mutex); + + schedule_timeout(1); + schedule(); + remove_wait_queue(&chip->wq, &wait); + + // enabled by takeo + spin_lock_bh(chip->mutex); + + if (signal_pending(current)) { + ret = -EINTR; + goto out; + } + //timeo = jiffies + HZ; + } + ret = -ETIME; +out: + if (ret < 0) { + map->write16(map, CMD_CLEAR_STATUS, adr); + map->write16(map, CMD_READ_ARRAY, adr); + chip->state = FL_READY; + } + return ret; +} + +static int m5drv_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) +{ + int ret; + u_short status; + struct m5drv_info *m5drv = map->fldrv_priv; + int chipnum; + + M5DRV_DEBUG(1, "m5drv_erase_oneblock()\n"); + +#ifdef UNLOCK_BEFORE_ERASE + m5drv_unlock_oneblock(map, chip, adr); +#endif + + chipnum = (adr >> m5drv->chipshift); + adr &= ~1; /* align 2 */ + + ret = m5drv_wait(map, chip, adr); + if (ret < 0) return ret; + + map->write16(map, CMD_BLOCK_ERASE, adr); + map->write16(map, CMD_CONFIRM, adr); + chip->state = FL_ERASING; + + ret = m5drv_do_wait_for_ready(map, chip, adr); + if(ret < 0) { + m5drv_release(&m5drv->chips[chipnum]); + return ret; + } + + status = map->read16(map, adr); + if ((status & STATUS_READY) == STATUS_READY) { + M5DRV_DEBUG(1, "m5drv: erase completed status=%04x\n", status); + map->write16(map, CMD_READ_ARRAY, adr); + chip->state = FL_READY; + m5drv_release(&m5drv->chips[chipnum]); + return 0; /* ok, erasing completed */ + } + + printk("m5drv: error erasing block at addr=%08lx status=%08x\n", + adr,status); + map->write16(map, CMD_READ_ARRAY, adr); /* cancel erasing */ + chip->state = FL_READY; + m5drv_release(&m5drv->chips[chipnum]); + return -EIO; +} + + +#ifdef UNLOCK_BEFORE_ERASE +/* + * we don't support unlock yet + */ +static void m5drv_unlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) +{ + M5DRV_DEBUG(1, "m5drv_unlock_oneblock\n"); +} +#endif + +static void m5drv_sync(struct mtd_info *mtd) +{ + M5DRV_DEBUG(1, "m5drv_sync()\n"); +} + +static int m5drv_suspend(struct mtd_info *mtd) +{ + M5DRV_DEBUG(1, "m5drv_suspend()\n"); + return -EINVAL; +} + +static void m5drv_resume(struct mtd_info *mtd) +{ + M5DRV_DEBUG(1, "m5drv_resume()\n"); +} + +static void m5drv_destroy(struct mtd_info *mtd) +{ + M5DRV_DEBUG(1, "m5drv_destroy()\n"); +} + +int __init m5drv_probe_init(void) +{ + printk("MTD chip driver\n"); + register_mtd_chip_driver(&m5drv_chipdrv); + return 0; +} + +static void __exit m5drv_probe_exit(void) +{ + M5DRV_DEBUG(1, "m5drv_probe_exit()\n"); + unregister_mtd_chip_driver(&m5drv_chipdrv); +} + +module_init(m5drv_probe_init); +module_exit(m5drv_probe_exit); + +MODULE_AUTHOR("Takeo Takahashi"); +MODULE_DESCRIPTION("MTD chip driver for M5M29GT320VP"); +MODULE_LICENSE("GPL"); +EXPORT_NO_SYMBOLS; --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/drivers/m5.h 2004-09-03 00:57:17.270413760 -0700 @@ -0,0 +1,73 @@ +/* + * Flash Memory Driver for M32700UT-CPU + * + * Copyright 2003 (C) Takeo Takahashi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * 2003-02-01: Takeo Takahashi, support M5M29GT320VP. + */ + +#include + +#ifdef __KERNEL__ +#undef DEBUG +/* debug routine: + * 0x00000001: print debug information + */ +# define DEBUG(n, args...) if ((n) & debug) \ + printk(KERN_DEBUG args) +#endif /* __KERNEL__ */ + +/* + * data type to access flash memory + */ +typedef volatile unsigned short m5_t; + +/* + * - Page program buffer size in byte + * - block size in byte + * - number of block + */ +#define M5_PAGE_SIZE (256) +#define M5_BLOCK_SIZE8 (8*1024) +#define M5_BLOCK_SIZE64 (64*1024) +#define MAX_BLOCK_NUM 70 + +/* + * Software commands + */ +#define M5_CMD_READ_ARRAY 0xff +#define M5_CMD_DEVICE_IDENT 0x90 +#define M5_CMD_READ_STATUS 0x70 +#define M5_CMD_CLEAR_STATUS 0x50 +#define M5_CMD_BLOCK_ERASE 0x20 +#define M5_CMD_CONFIRM 0xd0 +#define M5_CMD_PROGRAM_BYTE 0x40 +#define M5_CMD_PROGRAM_WORD M5_CMD_PROGRAM_BYTE +#define M5_CMD_PROGRAM_PAGE 0x41 +#define M5_CMD_SINGLE_LOAD_DATA 0x74 +#define M5_CMD_BUFF2FLASH 0x0e +#define M5_CMD_FLASH2BUFF 0xf1 +#define M5_CMD_CLEAR_BUFF 0x55 +#define M5_CMD_SUSPEND 0xb0 +#define M5_CMD_RESUME 0xd0 + +/* + * Status + */ +#define M5_STATUS_READY 0x80 /* 0:busy 1:ready */ +#define M5_STATUS_SUSPEND 0x40 /* 0:progress/complete 1:suspend */ +#define M5_STATUS_ERASE 0x20 /* 0:pass 1:error */ +#define M5_STATUS_PROGRAM 0x10 /* 0:pass 1:error */ +#define M5_STATUS_BLOCK 0x08 /* 0:pass 1:error */ + +/* + * Device Code + */ +#define M5_MAKER (0x1c) +#define M5_M5M29GT320VP (0x20) +#define M5_M5M29GB320VP (0x21) --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/drivers/Makefile 2004-09-03 00:57:17.270413760 -0700 @@ -0,0 +1,9 @@ +# +# Makefile for the Linux/M32R driver +# + +obj-$(CONFIG_M32R_SMC91111) += smc91111.o +obj-$(CONFIG_M32R_NE2000) += mappi_ne.o 8390.o +obj-$(CONFIG_M32RPCC) += m32r_pcc.o +obj-$(CONFIG_M32R_CFC) += m32r_cfc.o +obj-$(CONFIG_M32700UT_DS1302) += ds1302.o --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/drivers/mappi_ne.c 2004-09-03 00:57:17.275413000 -0700 @@ -0,0 +1,861 @@ +/* mappi_ne.c: A general non-shared-memory NS8390 ethernet driver for linux. */ +/* + Written 1992-94 by Donald Becker. + + Copyright 1993 United States Government as represented by the + Director, National Security Agency. + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + The author may be reached as becker@scyld.com, or C/O + Scyld Computing Corporation, 410 Severn Ave., Suite 210, Annapolis MD 21403 + + This driver should work with many programmed-I/O 8390-based ethernet + boards. Currently it supports the NE1000, NE2000, many clones, + and some Cabletron products. + + Changelog: + + Paul Gortmaker : use ENISR_RDC to monitor Tx PIO uploads, made + sanity checks and bad clone support optional. + Paul Gortmaker : new reset code, reset card after probe at boot. + Paul Gortmaker : multiple card support for module users. + Paul Gortmaker : Support for PCI ne2k clones, similar to lance.c + Paul Gortmaker : Allow users with bad cards to avoid full probe. + Paul Gortmaker : PCI probe changes, more PCI cards supported. + rjohnson@analogic.com : Changed init order so an interrupt will only + occur after memory is allocated for dev->priv. Deallocated memory + last in cleanup_modue() + Richard Guenther : Added support for ISAPnP cards + Paul Gortmaker : Discontinued PCI support - use ne2k-pci.c instead. + +*/ + +/* Routines for the NatSemi-based designs (NE[12]000). */ + +static const char version1[] = +"ne.c:v1.10 9/23/94 Donald Becker (becker@scyld.com)\n"; +static const char version2[] = +"Last modified Nov 1, 2000 by Paul Gortmaker\n"; + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "8390.h" + +/* Some defines that people can play with if so inclined. */ + +/* Do we support clones that don't adhere to 14,15 of the SAprom ? */ +#define SUPPORT_NE_BAD_CLONES + +/* Do we perform extra sanity checks on stuff ? */ +/* #define NE_SANITY_CHECK */ + +/* Do we implement the read before write bugfix ? */ +/* #define NE_RW_BUGFIX */ + +/* Do we have a non std. amount of memory? (in units of 256 byte pages) */ +/* #define PACKETBUF_MEMSIZE 0x40 */ + +/* A zero-terminated list of I/O addresses to be probed at boot. */ +#ifndef MODULE +static unsigned int netcard_portlist[] __initdata = { + 0x300, 0x280, 0x320, 0x340, 0x360, 0x380, 0 +}; +#endif + +static struct isapnp_device_id isapnp_clone_list[] __initdata = { + { ISAPNP_CARD_ID('A','X','E',0x2011), + ISAPNP_VENDOR('A','X','E'), ISAPNP_FUNCTION(0x2011), + (long) "NetGear EA201" }, + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('E','D','I'), ISAPNP_FUNCTION(0x0216), + (long) "NN NE2000" }, + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('P','N','P'), ISAPNP_FUNCTION(0x80d6), + (long) "Generic PNP" }, + { } /* terminate list */ +}; + +MODULE_DEVICE_TABLE(isapnp, isapnp_clone_list); + +#ifdef SUPPORT_NE_BAD_CLONES +/* A list of bad clones that we none-the-less recognize. */ +static struct { const char *name8, *name16; unsigned char SAprefix[4];} +bad_clone_list[] __initdata = { + {"DE100", "DE200", {0x00, 0xDE, 0x01,}}, + {"DE120", "DE220", {0x00, 0x80, 0xc8,}}, + {"DFI1000", "DFI2000", {'D', 'F', 'I',}}, /* Original, eh? */ + {"EtherNext UTP8", "EtherNext UTP16", {0x00, 0x00, 0x79}}, + {"NE1000","NE2000-invalid", {0x00, 0x00, 0xd8}}, /* Ancient real NE1000. */ + {"NN1000", "NN2000", {0x08, 0x03, 0x08}}, /* Outlaw no-name clone. */ + {"4-DIM8","4-DIM16", {0x00,0x00,0x4d,}}, /* Outlaw 4-Dimension cards. */ + {"Con-Intl_8", "Con-Intl_16", {0x00, 0x00, 0x24}}, /* Connect Int'nl */ + {"ET-100","ET-200", {0x00, 0x45, 0x54}}, /* YANG and YA clone */ + {"COMPEX","COMPEX16",{0x00,0x80,0x48}}, /* Broken ISA Compex cards */ + {"E-LAN100", "E-LAN200", {0x00, 0x00, 0x5d}}, /* Broken ne1000 clones */ + {"PCM-4823", "PCM-4823", {0x00, 0xc0, 0x6c}}, /* Broken Advantech MoBo */ + {"REALTEK", "RTL8019", {0x00, 0x00, 0xe8}}, /* no-name with Realtek chip */ + {"LCS-8834", "LCS-8836", {0x04, 0x04, 0x37}}, /* ShinyNet (SET) */ + {0,} +}; +#endif + +/* ---- No user-serviceable parts below ---- */ + +#define NE_BASE (dev->base_addr) +#define NE_CMD 0x00 +#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */ +#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */ +#define NE_IO_EXTENT 0x20 + +#define NE1SM_START_PG 0x20 /* First page of TX buffer */ +#define NE1SM_STOP_PG 0x40 /* Last page +1 of RX ring */ +#define NESM_START_PG 0x40 /* First page of TX buffer */ +#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */ + +static int ne_probe1(struct net_device *dev, int ioaddr); +static int ne_probe_isapnp(struct net_device *dev); + +static int ne_open(struct net_device *dev); +static int ne_close(struct net_device *dev); + +static void ne_reset_8390(struct net_device *dev); +static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, + int ring_page); +static void ne_block_input(struct net_device *dev, int count, + struct sk_buff *skb, int ring_offset); +static void ne_block_output(struct net_device *dev, const int count, + const unsigned char *buf, const int start_page); + + +/* Probe for various non-shared-memory ethercards. + + NEx000-clone boards have a Station Address PROM (SAPROM) in the packet + buffer memory space. NE2000 clones have 0x57,0x57 in bytes 0x0e,0x0f of + the SAPROM, while other supposed NE2000 clones must be detected by their + SA prefix. + + Reading the SAPROM from a word-wide card with the 8390 set in byte-wide + mode results in doubled values, which can be detected and compensated for. + + The probe is also responsible for initializing the card and filling + in the 'dev' and 'ei_status' structures. + + We use the minimum memory size for some ethercard product lines, iff we can't + distinguish models. You can increase the packet buffer size by setting + PACKETBUF_MEMSIZE. Reported Cabletron packet buffer locations are: + E1010 starts at 0x100 and ends at 0x2000. + E1010-x starts at 0x100 and ends at 0x8000. ("-x" means "more memory") + E2010 starts at 0x100 and ends at 0x4000. + E2010-x starts at 0x100 and ends at 0xffff. */ + +static int __init do_ne_probe(struct net_device *dev) +{ + unsigned int base_addr = dev->base_addr; +#ifndef MODULE + int orig_irq = dev->irq; +#endif + + SET_MODULE_OWNER(dev); + + /* First check any supplied i/o locations. User knows best. */ + if (base_addr > 0x1ff) /* Check a single specified location. */ + return ne_probe1(dev, base_addr); + else if (base_addr != 0) /* Don't probe at all. */ + return -ENXIO; + + /* Then look for any installed ISAPnP clones */ + if (isapnp_present() && (ne_probe_isapnp(dev) == 0)) + return 0; + +#ifndef MODULE + /* Last resort. The semi-risky ISA auto-probe. */ + for (base_addr = 0; netcard_portlist[base_addr] != 0; base_addr++) { + int ioaddr = netcard_portlist[base_addr]; + dev->irq = orig_irq; + if (ne_probe1(dev, ioaddr) == 0) + return 0; + } +#endif + + return -ENODEV; +} + +static void cleanup_card(struct net_device *dev) +{ + struct pnp_dev *idev = (struct pnp_dev *)ei_status.priv; + if (idev) + pnp_device_detach(idev); + free_irq(dev->irq, dev); + release_region(dev->base_addr, NE_IO_EXTENT); +} + +struct net_device * __init ne_probe(int unit) +{ + struct net_device *dev = alloc_ei_netdev(); + int err; + + if (!dev) + return ERR_PTR(-ENOMEM); + + sprintf(dev->name, "eth%d", unit); + netdev_boot_setup_check(dev); + + err = do_ne_probe(dev); + if (err) + goto out; + err = register_netdev(dev); + if (err) + goto out1; + return dev; +out1: + cleanup_card(dev); +out: + free_netdev(dev); + return ERR_PTR(err); +} + +static int __init ne_probe_isapnp(struct net_device *dev) +{ + int i; + + for (i = 0; isapnp_clone_list[i].vendor != 0; i++) { + struct pnp_dev *idev = NULL; + + while ((idev = pnp_find_dev(NULL, + isapnp_clone_list[i].vendor, + isapnp_clone_list[i].function, + idev))) { + /* Avoid already found cards from previous calls */ + if (pnp_device_attach(idev) < 0) + continue; + if (pnp_activate_dev(idev) < 0) { + pnp_device_detach(idev); + continue; + } + /* if no io and irq, search for next */ + if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0)) { + pnp_device_detach(idev); + continue; + } + /* found it */ + dev->base_addr = pnp_port_start(idev, 0); + dev->irq = pnp_irq(idev, 0); + printk(KERN_INFO "ne.c: ISAPnP reports %s at i/o %#lx, irq %d.\n", + (char *) isapnp_clone_list[i].driver_data, + dev->base_addr, dev->irq); + if (ne_probe1(dev, dev->base_addr) != 0) { /* Shouldn't happen. */ + printk(KERN_ERR "ne.c: Probe of ISAPnP card at %#lx failed.\n", dev->base_addr); + pnp_device_detach(idev); + return -ENXIO; + } + ei_status.priv = (unsigned long)idev; + break; + } + if (!idev) + continue; + return 0; + } + + return -ENODEV; +} + +static int __init ne_probe1(struct net_device *dev, int ioaddr) +{ + int i; + unsigned char SA_prom[32]; + int wordlength = 2; + const char *name = NULL; + int start_page, stop_page; + int neX000, ctron, copam, bad_card; + int reg0, ret; + static unsigned version_printed; + + if (!request_region(ioaddr, NE_IO_EXTENT, dev->name)) + return -EBUSY; + + reg0 = inb_p(ioaddr); + if (reg0 == 0xFF) { + ret = -ENODEV; + goto err_out; + } + + /* Do a preliminary verification that we have a 8390. */ + { + int regd; + outb_p(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD); + regd = inb_p(ioaddr + 0x0d); + outb_p(0xff, ioaddr + 0x0d); + outb_p(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD); + inb_p(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */ + if (inb_p(ioaddr + EN0_COUNTER0) != 0) { + outb_p(reg0, ioaddr); + outb_p(regd, ioaddr + 0x0d); /* Restore the old values. */ + ret = -ENODEV; + goto err_out; + } + } + + if (ei_debug && version_printed++ == 0) + printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); + + printk(KERN_INFO "NE*000 ethercard probe at %#3x:", ioaddr); + + /* A user with a poor card that fails to ack the reset, or that + does not have a valid 0x57,0x57 signature can still use this + without having to recompile. Specifying an i/o address along + with an otherwise unused dev->mem_end value of "0xBAD" will + cause the driver to skip these parts of the probe. */ + + bad_card = ((dev->base_addr != 0) && (dev->mem_end == 0xbad)); + + /* Reset card. Who knows what dain-bramaged state it was left in. */ + + { + unsigned long reset_start_time = jiffies; + + /* DON'T change these to inb_p/outb_p or reset will fail on clones. */ + outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET); + + while ((inb_p(ioaddr + EN0_ISR) & ENISR_RESET) == 0) + if (jiffies - reset_start_time > 2*HZ/100) { + if (bad_card) { + printk(" (warning: no reset ack)"); + break; + } else { + printk(" not found (no reset ack).\n"); + ret = -ENODEV; + goto err_out; + } + } + + outb_p(0xff, ioaddr + EN0_ISR); /* Ack all intr. */ + } + + /* Read the 16 bytes of station address PROM. + We must first initialize registers, similar to NS8390_init(eifdev, 0). + We can't reliably read the SAPROM address without this. + (I learned the hard way!). */ + { + struct {unsigned char value, offset; } program_seq[] = + { + {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/ + {0x48, EN0_DCFG}, /* Set byte-wide (0x48) access. */ + {0x00, EN0_RCNTLO}, /* Clear the count regs. */ + {0x00, EN0_RCNTHI}, + {0x00, EN0_IMR}, /* Mask completion irq. */ + {0xFF, EN0_ISR}, + {E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */ + {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */ + {32, EN0_RCNTLO}, + {0x00, EN0_RCNTHI}, + {0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */ + {0x00, EN0_RSARHI}, + {E8390_RREAD+E8390_START, E8390_CMD}, + }; + + for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++) + outb_p(program_seq[i].value, ioaddr + program_seq[i].offset); + + } + for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) { + SA_prom[i] = inb(ioaddr + NE_DATAPORT); + SA_prom[i+1] = inb(ioaddr + NE_DATAPORT); + if (SA_prom[i] != SA_prom[i+1]) + wordlength = 1; + } + + if (wordlength == 2) + { + for (i = 0; i < 16; i++) + SA_prom[i] = SA_prom[i+i]; + /* We must set the 8390 for word mode. */ +#ifdef CONFIG_PLAT_MAPPI + outb_p(0x4b, ioaddr + EN0_DCFG); +#elif CONFIG_PLAT_OAKS32R + outb_p(0x48, ioaddr + EN0_DCFG); +#else + outb_p(0x49, ioaddr + EN0_DCFG); +#endif + start_page = NESM_START_PG; + stop_page = NESM_STOP_PG; + } else { + start_page = NE1SM_START_PG; + stop_page = NE1SM_STOP_PG; + } + +#if defined(CONFIG_PLAT_MAPPI) || defined(CONFIG_PLAT_OAKS32R) + neX000 = ((SA_prom[14] == 0x57 && SA_prom[15] == 0x57) + || (SA_prom[14] == 0x42 && SA_prom[15] == 0x42)); +#else + neX000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57); +#endif + ctron = (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d); + copam = (SA_prom[14] == 0x49 && SA_prom[15] == 0x00); + + /* Set up the rest of the parameters. */ + if (neX000 || bad_card || copam) { + name = (wordlength == 2) ? "NE2000" : "NE1000"; + } + else if (ctron) + { + name = (wordlength == 2) ? "Ctron-8" : "Ctron-16"; + start_page = 0x01; + stop_page = (wordlength == 2) ? 0x40 : 0x20; + } + else + { +#ifdef SUPPORT_NE_BAD_CLONES + /* Ack! Well, there might be a *bad* NE*000 clone there. + Check for total bogus addresses. */ + for (i = 0; bad_clone_list[i].name8; i++) + { + if (SA_prom[0] == bad_clone_list[i].SAprefix[0] && + SA_prom[1] == bad_clone_list[i].SAprefix[1] && + SA_prom[2] == bad_clone_list[i].SAprefix[2]) + { + if (wordlength == 2) + { + name = bad_clone_list[i].name16; + } else { + name = bad_clone_list[i].name8; + } + break; + } + } + if (bad_clone_list[i].name8 == NULL) + { + printk(" not found (invalid signature %2.2x %2.2x).\n", + SA_prom[14], SA_prom[15]); + ret = -ENXIO; + goto err_out; + } +#else + printk(" not found.\n"); + ret = -ENXIO; + goto err_out; +#endif + } + + if (dev->irq < 2) + { + unsigned long cookie = probe_irq_on(); + outb_p(0x50, ioaddr + EN0_IMR); /* Enable one interrupt. */ + outb_p(0x00, ioaddr + EN0_RCNTLO); + outb_p(0x00, ioaddr + EN0_RCNTHI); + outb_p(E8390_RREAD+E8390_START, ioaddr); /* Trigger it... */ + mdelay(10); /* wait 10ms for interrupt to propagate */ + outb_p(0x00, ioaddr + EN0_IMR); /* Mask it again. */ + dev->irq = probe_irq_off(cookie); + if (ei_debug > 2) + printk(" autoirq is %d\n", dev->irq); + } else if (dev->irq == 2) + /* Fixup for users that don't know that IRQ 2 is really IRQ 9, + or don't know which one to set. */ + dev->irq = 9; + + if (! dev->irq) { + printk(" failed to detect IRQ line.\n"); + ret = -EAGAIN; + goto err_out; + } + + /* Snarf the interrupt now. There's no point in waiting since we cannot + share and the board will usually be enabled. */ + ret = request_irq(dev->irq, ei_interrupt, 0, name, dev); + if (ret) { + printk (" unable to get IRQ %d (errno=%d).\n", dev->irq, ret); + goto err_out; + } + + dev->base_addr = ioaddr; + +#ifdef CONFIG_PLAT_MAPPI + outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, + ioaddr + E8390_CMD); /* 0x61 */ + for (i = 0 ; i < ETHER_ADDR_LEN ; i++) { + dev->dev_addr[i] = SA_prom[i] + = inb_p(ioaddr + EN1_PHYS_SHIFT(i)); + printk(" %2.2x", SA_prom[i]); + } +#else + for(i = 0; i < ETHER_ADDR_LEN; i++) { + printk(" %2.2x", SA_prom[i]); + dev->dev_addr[i] = SA_prom[i]; + } +#endif + + printk("\n%s: %s found at %#x, using IRQ %d.\n", + dev->name, name, ioaddr, dev->irq); + + ei_status.name = name; + ei_status.tx_start_page = start_page; + ei_status.stop_page = stop_page; +#ifdef CONFIG_PLAT_OAKS32R + ei_status.word16 = 0; +#else + ei_status.word16 = (wordlength == 2); +#endif + + ei_status.rx_start_page = start_page + TX_PAGES; +#ifdef PACKETBUF_MEMSIZE + /* Allow the packet buffer size to be overridden by know-it-alls. */ + ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE; +#endif + + ei_status.reset_8390 = &ne_reset_8390; + ei_status.block_input = &ne_block_input; + ei_status.block_output = &ne_block_output; + ei_status.get_8390_hdr = &ne_get_8390_hdr; + ei_status.priv = 0; + dev->open = &ne_open; + dev->stop = &ne_close; + NS8390_init(dev, 0); + return 0; + +err_out: + release_region(ioaddr, NE_IO_EXTENT); + return ret; +} + +static int ne_open(struct net_device *dev) +{ + ei_open(dev); + return 0; +} + +static int ne_close(struct net_device *dev) +{ + if (ei_debug > 1) + printk(KERN_DEBUG "%s: Shutting down ethercard.\n", dev->name); + ei_close(dev); + return 0; +} + +/* Hard reset the card. This used to pause for the same period that a + 8390 reset command required, but that shouldn't be necessary. */ + +static void ne_reset_8390(struct net_device *dev) +{ + unsigned long reset_start_time = jiffies; + + if (ei_debug > 1) + printk(KERN_DEBUG "resetting the 8390 t=%ld...", jiffies); + + /* DON'T change these to inb_p/outb_p or reset will fail on clones. */ + outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET); + + ei_status.txing = 0; + ei_status.dmaing = 0; + + /* This check _should_not_ be necessary, omit eventually. */ + while ((inb_p(NE_BASE+EN0_ISR) & ENISR_RESET) == 0) + if (jiffies - reset_start_time > 2*HZ/100) { + printk(KERN_WARNING "%s: ne_reset_8390() did not complete.\n", dev->name); + break; + } + outb_p(ENISR_RESET, NE_BASE + EN0_ISR); /* Ack intr. */ +} + +/* Grab the 8390 specific header. Similar to the block_input routine, but + we don't need to be concerned with ring wrap as the header will be at + the start of a page, so we optimize accordingly. */ + +static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) +{ + int nic_base = dev->base_addr; + + /* This *shouldn't* happen. If it does, it's the last thing you'll see */ + + if (ei_status.dmaing) + { + printk(KERN_EMERG "%s: DMAing conflict in ne_get_8390_hdr " + "[DMAstat:%d][irqlock:%d].\n", + dev->name, ei_status.dmaing, ei_status.irqlock); + return; + } + + ei_status.dmaing |= 0x01; + outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD); + outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO); + outb_p(0, nic_base + EN0_RCNTHI); + outb_p(0, nic_base + EN0_RSARLO); /* On page boundary */ + outb_p(ring_page, nic_base + EN0_RSARHI); + outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD); + + if (ei_status.word16) + insw(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1); + else + insb(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)); + + outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ + ei_status.dmaing &= ~0x01; + + le16_to_cpus(&hdr->count); +} + +/* Block input and output, similar to the Crynwr packet driver. If you + are porting to a new ethercard, look at the packet driver source for hints. + The NEx000 doesn't share the on-board packet memory -- you have to put + the packet out through the "remote DMA" dataport using outb. */ + +static void ne_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) +{ +#ifdef NE_SANITY_CHECK + int xfer_count = count; +#endif + int nic_base = dev->base_addr; + char *buf = skb->data; + + /* This *shouldn't* happen. If it does, it's the last thing you'll see */ + if (ei_status.dmaing) + { + printk(KERN_EMERG "%s: DMAing conflict in ne_block_input " + "[DMAstat:%d][irqlock:%d].\n", + dev->name, ei_status.dmaing, ei_status.irqlock); + return; + } + ei_status.dmaing |= 0x01; + outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD); + outb_p(count & 0xff, nic_base + EN0_RCNTLO); + outb_p(count >> 8, nic_base + EN0_RCNTHI); + outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO); + outb_p(ring_offset >> 8, nic_base + EN0_RSARHI); + outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD); + if (ei_status.word16) + { + insw(NE_BASE + NE_DATAPORT,buf,count>>1); + if (count & 0x01) + { + buf[count-1] = inb(NE_BASE + NE_DATAPORT); +#ifdef NE_SANITY_CHECK + xfer_count++; +#endif + } + } else { + insb(NE_BASE + NE_DATAPORT, buf, count); + } + +#ifdef NE_SANITY_CHECK + /* This was for the ALPHA version only, but enough people have + been encountering problems so it is still here. If you see + this message you either 1) have a slightly incompatible clone + or 2) have noise/speed problems with your bus. */ + + if (ei_debug > 1) + { + /* DMA termination address check... */ + int addr, tries = 20; + do { + /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here + -- it's broken for Rx on some cards! */ + int high = inb_p(nic_base + EN0_RSARHI); + int low = inb_p(nic_base + EN0_RSARLO); + addr = (high << 8) + low; + if (((ring_offset + xfer_count) & 0xff) == low) + break; + } while (--tries > 0); + if (tries <= 0) + printk(KERN_WARNING "%s: RX transfer address mismatch," + "%#4.4x (expected) vs. %#4.4x (actual).\n", + dev->name, ring_offset + xfer_count, addr); + } +#endif + outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ + ei_status.dmaing &= ~0x01; +} + +static void ne_block_output(struct net_device *dev, int count, + const unsigned char *buf, const int start_page) +{ + int nic_base = NE_BASE; + unsigned long dma_start; +#ifdef NE_SANITY_CHECK + int retries = 0; +#endif + + /* Round the count up for word writes. Do we need to do this? + What effect will an odd byte count have on the 8390? + I should check someday. */ + + if (ei_status.word16 && (count & 0x01)) + count++; + + /* This *shouldn't* happen. If it does, it's the last thing you'll see */ + if (ei_status.dmaing) + { + printk(KERN_EMERG "%s: DMAing conflict in ne_block_output." + "[DMAstat:%d][irqlock:%d]\n", + dev->name, ei_status.dmaing, ei_status.irqlock); + return; + } + ei_status.dmaing |= 0x01; + /* We should already be in page 0, but to be safe... */ + outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD); + +#ifdef NE_SANITY_CHECK +retry: +#endif + +#ifdef NE8390_RW_BUGFIX + /* Handle the read-before-write bug the same way as the + Crynwr packet driver -- the NatSemi method doesn't work. + Actually this doesn't always work either, but if you have + problems with your NEx000 this is better than nothing! */ + + outb_p(0x42, nic_base + EN0_RCNTLO); + outb_p(0x00, nic_base + EN0_RCNTHI); + outb_p(0x42, nic_base + EN0_RSARLO); + outb_p(0x00, nic_base + EN0_RSARHI); + outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD); + /* Make certain that the dummy read has occurred. */ + udelay(6); +#endif + + outb_p(ENISR_RDC, nic_base + EN0_ISR); + + /* Now the normal output. */ + outb_p(count & 0xff, nic_base + EN0_RCNTLO); + outb_p(count >> 8, nic_base + EN0_RCNTHI); + outb_p(0x00, nic_base + EN0_RSARLO); + outb_p(start_page, nic_base + EN0_RSARHI); + + outb_p(E8390_RWRITE+E8390_START, nic_base + NE_CMD); + if (ei_status.word16) { + outsw(NE_BASE + NE_DATAPORT, buf, count>>1); + } else { + outsb(NE_BASE + NE_DATAPORT, buf, count); + } + + dma_start = jiffies; + +#ifdef NE_SANITY_CHECK + /* This was for the ALPHA version only, but enough people have + been encountering problems so it is still here. */ + + if (ei_debug > 1) + { + /* DMA termination address check... */ + int addr, tries = 20; + do { + int high = inb_p(nic_base + EN0_RSARHI); + int low = inb_p(nic_base + EN0_RSARLO); + addr = (high << 8) + low; + if ((start_page << 8) + count == addr) + break; + } while (--tries > 0); + + if (tries <= 0) + { + printk(KERN_WARNING "%s: Tx packet transfer address mismatch," + "%#4.4x (expected) vs. %#4.4x (actual).\n", + dev->name, (start_page << 8) + count, addr); + if (retries++ == 0) + goto retry; + } + } +#endif + + while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0) + if (jiffies - dma_start > 2*HZ/100) { /* 20ms */ + printk(KERN_WARNING "%s: timeout waiting for Tx RDC.\n", dev->name); + ne_reset_8390(dev); + NS8390_init(dev,1); + break; + } + + outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */ + ei_status.dmaing &= ~0x01; + return; +} + + +#ifdef MODULE +#define MAX_NE_CARDS 4 /* Max number of NE cards per module */ +static struct net_device *dev_ne[MAX_NE_CARDS]; +static int io[MAX_NE_CARDS]; +static int irq[MAX_NE_CARDS]; +static int bad[MAX_NE_CARDS]; /* 0xbad = bad sig or no reset ack */ + +MODULE_PARM(io, "1-" __MODULE_STRING(MAX_NE_CARDS) "i"); +MODULE_PARM(irq, "1-" __MODULE_STRING(MAX_NE_CARDS) "i"); +MODULE_PARM(bad, "1-" __MODULE_STRING(MAX_NE_CARDS) "i"); +MODULE_PARM_DESC(io, "I/O base address(es),required"); +MODULE_PARM_DESC(irq, "IRQ number(s)"); +MODULE_PARM_DESC(bad, "Accept card(s) with bad signatures"); +MODULE_DESCRIPTION("NE1000/NE2000 ISA/PnP Ethernet driver"); +MODULE_LICENSE("GPL"); + +/* This is set up so that no ISA autoprobe takes place. We can't guarantee +that the ne2k probe is the last 8390 based probe to take place (as it +is at boot) and so the probe will get confused by any other 8390 cards. +ISA device autoprobes on a running machine are not recommended anyway. */ + +int init_module(void) +{ + int this_dev, found = 0; + + for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) { + struct net_device *dev = alloc_ei_netdev(); + if (!dev) + break; + dev->irq = irq[this_dev]; + dev->mem_end = bad[this_dev]; + dev->base_addr = io[this_dev]; + if (do_ne_probe(dev) == 0) { + if (register_netdev(dev) == 0) { + dev_ne[found++] = dev; + continue; + } + cleanup_card(dev); + } + free_netdev(dev); + if (found) + break; + if (io[this_dev] != 0) + printk(KERN_WARNING "ne.c: No NE*000 card found at i/o = %#x\n", io[this_dev]); + else + printk(KERN_NOTICE "ne.c: You must supply \"io=0xNNN\" value(s) for ISA cards.\n"); + return -ENXIO; + } + if (found) + return 0; + return -ENODEV; +} + +void cleanup_module(void) +{ + int this_dev; + + for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) { + struct net_device *dev = dev_ne[this_dev]; + if (dev) { + unregister_netdev(dev); + cleanup_card(dev); + free_netdev(dev); + } + } +} +#endif /* MODULE */ + + +/* + * Local variables: + * compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c ne.c" + * version-control: t + * kept-new-versions: 5 + * End: + */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/drivers/smc91111.c 2004-09-03 00:57:18.718193664 -0700 @@ -0,0 +1,3894 @@ +/*------------------------------------------------------------------------ + . smc91111.c + . This is a driver for SMSC's 91C111 single-chip Ethernet device. + . + . Copyright (C) 2001 Standard Microsystems Corporation (SMSC) + . Developed by Simple Network Magic Corporation (SNMC) + . Copyright (C) 1996 by Erik Stahlman (ES) + . + . This program is free software; you can redistribute it and/or modify + . it under the terms of the GNU General Public License as published by + . the Free Software Foundation; either version 2 of the License, or + . (at your option) any later version. + . + . This program is distributed in the hope that it will be useful, + . but WITHOUT ANY WARRANTY; without even the implied warranty of + . MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + . GNU General Public License for more details. + . + . You should have received a copy of the GNU General Public License + . along with this program; if not, write to the Free Software + . Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + . + . Information contained in this file was obtained from the LAN91C111 + . manual from SMC. To get a copy, if you really want one, you can find + . information under www.smsc.com. + . + . + . "Features" of the SMC chip: + . Integrated PHY/MAC for 10/100BaseT Operation + . Supports internal and external MII + . Integrated 8K packet memory + . EEPROM interface for configuration + . + . Arguments: + . io = for the base address + . irq = for the IRQ + . nowait = 0 for normal wait states, 1 eliminates additional wait states + . + . author: + . Erik Stahlman ( erik@vt.edu ) + . Daris A Nevil ( dnevil@snmc.com ) + . Pramod B Bhardwaj (pramod.bhardwaj@smsc.com) + . + . + . Hardware multicast code from Peter Cammaert ( pc@denkart.be ) + . + . Sources: + . o SMSC LAN91C111 databook (www.smsc.com) + . o smc9194.c by Erik Stahlman + . o skeleton.c by Donald Becker ( becker@cesdis.gsfc.nasa.gov ) + . + . History: + . 09/24/01 Pramod B Bhardwaj, Added the changes for Kernel 2.4 + . 08/21/01 Pramod B Bhardwaj Added support for RevB of LAN91C111 + . 04/25/01 Daris A Nevil Initial public release through SMSC + . 03/16/01 Daris A Nevil Modified smc9194.c for use with LAN91C111 + . 01/14/03 Takeo Takahashi Support M32RUT-LAN(Rev.B) + . 02/25/04 Hirokazu Takata, Hayato Fujiwara, Mamoru Sakugawa + . SMP support for Linux/M32R. + ----------------------------------------------------------------------------*/ +#include +#if defined(__m32r__) +#define SMC_DEBUG 0 +#define smc_init m32r_smc_init +#endif +#if defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_MAPPI2) || defined(CONFIG_PLAT_OPSPUT) +#define NO_AUTOPROBE +#endif + +// Use power-down feature of the chip +#define POWER_DOWN 0 + + +static const char version[] = + "SMSC LAN91C111 Driver (v2.0), (Linux Kernel 2.4 + Support for Odd Byte) 09/24/01 - by Pramod Bhardwaj (pramod.bhardwaj@smsc.com)\n"; + +#ifdef MODULE +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#if defined(__m32r__) +#include +#else /* Not __m32r__ */ +#include +#endif +#include +#include +#include +#include +#include +#include + +#include +#include +#include +//#include + +#include + +#ifdef CONFIG_SYSCTL +#include +#include +#endif + +#include "smc91111.h" +/*------------------------------------------------------------------------ + . + . Configuration options, for the experienced user to change. + . + -------------------------------------------------------------------------*/ + +/* + . Do you want to use 32 bit xfers? This should work on all chips, as + . the chipset is designed to accommodate them. +*/ +#if !defined(__m32r__) +#define USE_32_BIT 1 +#endif + + +/* + .the LAN91C111 can be at any of the following port addresses. To change, + .for a slightly different card, you can add it to the array. Keep in + .mind that the array must end in zero. +*/ +static unsigned int smc_portlist[] __initdata = + { 0x200, 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x2E0, + 0x300, 0x320, 0x340, 0x360, 0x380, 0x3A0, 0x3C0, 0x3E0, 0}; + + +/* + . Wait time for memory to be free. This probably shouldn't be + . tuned that much, as waiting for this means nothing else happens + . in the system +*/ +#define MEMORY_WAIT_TIME 16 + +/* + . This selects whether TX packets are sent one by one to the SMC91x internal + . memory ans throttled until transmission completes. This may prevent + . RX overruns a litle by keeping much of the memory free for RX packets + . but to the expense of reduced TX throughput and increased IRQ overhead. + . Note this is not a cure for a too slow data bus or too high IRQ latency. + */ +#define THROTTLE_TX_PKTS 1 + +/* + . DEBUGGING LEVELS + . + . 0 for normal operation + . 1 for slightly more details + . >2 for various levels of increasingly useless information + . 2 for interrupt tracking, status flags + . 3 for packet info + . 4 for complete packet dumps +*/ +//#define SMC_DEBUG 3 // Must be defined in makefile + +#if (SMC_DEBUG > 2 ) +#define PRINTK3(args...) printk(args) +#else +#define PRINTK3(args...) +#endif + +#if SMC_DEBUG > 1 +#define PRINTK2(args...) printk(args) +#else +#define PRINTK2(args...) +#endif + +#ifdef SMC_DEBUG +#define PRINTK(args...) printk(args) +#else +#define PRINTK(args...) +#endif + + +/*------------------------------------------------------------------------ + . + . The internal workings of the driver. If you are changing anything + . here with the SMC stuff, you should have the datasheet and know + . what you are doing. + . + -------------------------------------------------------------------------*/ +#define CARDNAME "LAN91C111" + +// Memory sizing constant +#define LAN91C111_MEMORY_MULTIPLIER (1024*2) + +/* store this information for the driver.. */ +struct smc_local { + + // these are things that the kernel wants me to keep, so users + // can find out semi-useless statistics of how well the card is + // performing + struct net_device_stats stats; + + // If I have to wait until memory is available to send + // a packet, I will store the skbuff here, until I get the + // desired memory. Then, I'll send it out and free it. + struct sk_buff * saved_skb; + + // This keeps track of how many packets that I have + // sent out. When an TX_EMPTY interrupt comes, I know + // that all of these have been sent. + int packets_waiting; + + // Set to true during the auto-negotiation sequence + int autoneg_active; + + // Address of our PHY port + word phyaddr; + + // Type of PHY + word phytype; + + // Last contents of PHY Register 18 + word lastPhy18; + + // Contains the current active transmission mode + word tcr_cur_mode; + + // Contains the current active receive mode + word rcr_cur_mode; + + // Contains the current active receive/phy mode + word rpc_cur_mode; + + /* => Pramod, Odd Byte issue */ + // Contains the Current ChipID + unsigned short ChipID; + + //Contains the Current ChipRevision + unsigned short ChipRev; + /* <= Pramod, Odd Byte issue */ + + spinlock_t lock; + +#ifdef CONFIG_SYSCTL + + // Root directory /proc/sys/dev + // Second entry must be null to terminate the table + ctl_table root_table[2]; + + // Directory for this device /proc/sys/dev/ethX + // Again the second entry must be zero to terminate + ctl_table eth_table[2]; + + // This is the parameters (file) table + ctl_table param_table[CTL_SMC_LAST_ENTRY]; + + // Saves the sysctl header returned by register_sysctl_table() + // we send this to unregister_sysctl_table() + struct ctl_table_header *sysctl_header; + + // Parameter variables (files) go here + char ctl_info[1024]; + int ctl_swfdup; + int ctl_ephloop; + int ctl_miiop; + int ctl_autoneg; + int ctl_rfduplx; + int ctl_rspeed; + int ctl_afduplx; + int ctl_aspeed; + int ctl_lnkfail; + int ctl_forcol; + int ctl_filtcar; + int ctl_freemem; + int ctl_totmem; + int ctl_leda; + int ctl_ledb; + int ctl_chiprev; +#ifdef SMC_DEBUG + int ctl_reg_bsr; + int ctl_reg_tcr; + int ctl_reg_esr; + int ctl_reg_rcr; + int ctl_reg_ctrr; + int ctl_reg_mir; + int ctl_reg_rpcr; + int ctl_reg_cfgr; + int ctl_reg_bar; + int ctl_reg_iar0; + int ctl_reg_iar1; + int ctl_reg_iar2; + int ctl_reg_gpr; + int ctl_reg_ctlr; + int ctl_reg_mcr; + int ctl_reg_pnr; + int ctl_reg_fpr; + int ctl_reg_ptr; + int ctl_reg_dr; + int ctl_reg_isr; + int ctl_reg_mtr1; + int ctl_reg_mtr2; + int ctl_reg_mtr3; + int ctl_reg_mtr4; + int ctl_reg_miir; + int ctl_reg_revr; + int ctl_reg_ercvr; + int ctl_reg_extr; + int ctl_phy_ctrl; + int ctl_phy_stat; + int ctl_phy_id1; + int ctl_phy_id2; + int ctl_phy_adc; + int ctl_phy_remc; + int ctl_phy_cfg1; + int ctl_phy_cfg2; + int ctl_phy_int; + int ctl_phy_mask; +#endif // SMC_DEBUG + + +#endif // CONFIG_SYSCTL + +}; + + +/*----------------------------------------------------------------- + . + . The driver can be entered at any of the following entry points. + . + .------------------------------------------------------------------ */ + +/* + . This is called by register_netdev(). It is responsible for + . checking the portlist for the SMC9000 series chipset. If it finds + . one, then it will initialize the device, find the hardware information, + . and sets up the appropriate device parameters. + . NOTE: Interrupts are *OFF* when this procedure is called. + . + . NB:This shouldn't be static since it is referred to externally. +*/ +struct net_device *smc_init(int unit); + +/* + . This is called by unregister_netdev(). It is responsible for + . cleaning up before the driver is finally unregistered and discarded. +*/ +void smc_destructor(struct net_device *dev); + +/* + . The kernel calls this function when someone wants to use the net_device, + . typically 'ifconfig ethX up'. +*/ +static int smc_open(struct net_device *dev); + +/* + . This is called by the kernel to send a packet out into the net. it's + . responsible for doing a best-effort send, but if it's simply not possible + . to send it, the packet gets dropped. +*/ +static void smc_timeout (struct net_device *dev); +/* + . This is called by the kernel in response to 'ifconfig ethX down'. It + . is responsible for cleaning up everything that the open routine + . does, and maybe putting the card into a powerdown state. +*/ +static int smc_close(struct net_device *dev); + +/* + . This routine allows the proc file system to query the driver's + . statistics. +*/ +static struct net_device_stats * smc_query_statistics( struct net_device *dev); + +/* + . Finally, a call to set promiscuous mode ( for TCPDUMP and related + . programs ) and multicast modes. +*/ +static void smc_set_multicast_list(struct net_device *dev); + +/* + . Configures the PHY through the MII Management interface +*/ +static void smc_phy_configure(struct net_device* dev); + +/*--------------------------------------------------------------- + . + . Interrupt level calls.. + . + ----------------------------------------------------------------*/ + +/* + . Handles the actual interrupt +*/ +static irqreturn_t smc_interrupt(int irq, void *, struct pt_regs *regs); +/* + . This is a separate procedure to handle the receipt of a packet, to + . leave the interrupt code looking slightly cleaner +*/ +static inline void smc_rcv( struct net_device *dev ); +/* + . This handles a TX interrupt, which is only called when an error + . relating to a packet is sent. +*/ +static inline void smc_tx( struct net_device * dev ); + +/* + . This handles interrupts generated from PHY register 18 +*/ +static void smc_phy_interrupt(struct net_device* dev); + +/* + ------------------------------------------------------------ + . + . Internal routines + . + ------------------------------------------------------------ +*/ + +/* + . Test if a given location contains a chip, trying to cause as + . little damage as possible if it's not a SMC chip. +*/ +static int smc_probe(struct net_device *dev, int ioaddr); + +/* + . A rather simple routine to print out a packet for debugging purposes. +*/ +#if SMC_DEBUG > 2 +static void print_packet( byte *, int ); +#endif + +#define tx_done(dev) 1 + +/* this is called to actually send the packet to the chip */ +static void smc_hardware_send_packet( struct net_device * dev ); + +/* Since I am not sure if I will have enough room in the chip's ram + . to store the packet, I call this routine, which either sends it + . now, or generates an interrupt when the card is ready for the + . packet */ +static int smc_wait_to_send_packet( struct sk_buff * skb, struct net_device *dev ); + +/* this does a soft reset on the device */ +static void smc_reset( struct net_device* dev ); + +/* Enable Interrupts, Receive, and Transmit */ +static void smc_enable( struct net_device *dev ); + +/* this puts the device in an inactive state */ +static void smc_shutdown( int ioaddr ); + +#ifndef NO_AUTOPROBE +/* This routine will find the IRQ of the driver if one is not + . specified in the input to the device. */ +static int smc_findirq( int ioaddr ); +#endif + +/* + this routine will set the hardware multicast table to the specified + values given it by the higher level routines +*/ +static void smc_setmulticast( int ioaddr, int count, struct dev_mc_list * ); +static int crc32( char *, int ); + +/* Routines to Read and Write the PHY Registers across the + MII Management Interface +*/ + +static word smc_read_phy_register(int ioaddr, byte phyaddr, byte phyreg); +static void smc_write_phy_register(int ioaddr, byte phyaddr, byte phyreg, word phydata); + +/* Initilizes our device's sysctl proc filesystem */ + +#ifdef CONFIG_SYSCTL +static void smc_sysctl_register(struct net_device *); +static void smc_sysctl_unregister(struct net_device *); +#endif /* CONFIG_SYSCTL */ + +/* + . Function: smc_reset( struct device* dev ) + . Purpose: + . This sets the SMC91111 chip to its normal state, hopefully from whatever + . mess that any other DOS driver has put it in. + . + . Maybe I should reset more registers to defaults in here? SOFTRST should + . do that for me. + . + . Method: + . 1. send a SOFT RESET + . 2. wait for it to finish + . 3. enable autorelease mode + . 4. reset the memory management unit + . 5. clear all interrupts + . +*/ +static void smc_reset( struct net_device* dev ) +{ + //struct smc_local *lp = (struct smc_local *)dev->priv; + int ioaddr = dev->base_addr; + + PRINTK2("%s:smc_reset\n", dev->name); + + /* This resets the registers mostly to defaults, but doesn't + affect EEPROM. That seems unnecessary */ + SMC_SELECT_BANK( 0 ); + outw( RCR_SOFTRST, ioaddr + RCR_REG ); + + /* Setup the Configuration Register */ + /* This is necessary because the CONFIG_REG is not affected */ + /* by a soft reset */ + + SMC_SELECT_BANK( 1 ); + outw( CONFIG_DEFAULT, ioaddr + CONFIG_REG); + + /* Setup for fast accesses if requested */ + /* If the card/system can't handle it then there will */ + /* be no recovery except for a hard reset or power cycle */ + + if (dev->dma) + outw( inw( ioaddr + CONFIG_REG ) | CONFIG_NO_WAIT, + ioaddr + CONFIG_REG ); + +#ifdef POWER_DOWN + /* Release from possible power-down state */ + /* Configuration register is not affected by Soft Reset */ + SMC_SELECT_BANK( 1 ); + outw( inw( ioaddr + CONFIG_REG ) | CONFIG_EPH_POWER_EN, + ioaddr + CONFIG_REG ); +#endif + + SMC_SELECT_BANK( 0 ); + + /* this should pause enough for the chip to be happy */ + mdelay(10); + + /* Disable transmit and receive functionality */ + outw( RCR_CLEAR, ioaddr + RCR_REG ); + outw( TCR_CLEAR, ioaddr + TCR_REG ); + + /* set the control register to automatically + release successfully transmitted packets, to make the best + use out of our limited memory */ + SMC_SELECT_BANK( 1 ); +#if ! THROTTLE_TX_PKTS + outw( inw( ioaddr + CTL_REG ) | CTL_AUTO_RELEASE , ioaddr + CTL_REG ); +#else + outw( inw( ioaddr + CTL_REG ) & ~CTL_AUTO_RELEASE , ioaddr + CTL_REG ); +#endif + + /* Reset the MMU */ + SMC_SELECT_BANK( 2 ); + outw( MC_RESET, ioaddr + MMU_CMD_REG ); + + /* Note: It doesn't seem that waiting for the MMU busy is needed here, + but this is a place where future chipsets _COULD_ break. Be wary + of issuing another MMU command right after this */ + + /* Disable all interrupts */ + outb( 0, ioaddr + IM_REG ); +} + +/* + . Function: smc_enable + . Purpose: let the chip talk to the outside work + . Method: + . 1. Enable the transmitter + . 2. Enable the receiver + . 3. Enable interrupts +*/ +static void smc_enable( struct net_device *dev ) +{ + unsigned short ioaddr = dev->base_addr; + struct smc_local *lp = (struct smc_local *)dev->priv; + + PRINTK2("%s:smc_enable\n", dev->name); + + SMC_SELECT_BANK( 0 ); + /* see the header file for options in TCR/RCR DEFAULT*/ + outw( lp->tcr_cur_mode, ioaddr + TCR_REG ); + outw( lp->rcr_cur_mode, ioaddr + RCR_REG ); + + /* now, enable interrupts */ + SMC_SELECT_BANK( 2 ); + outb( SMC_INTERRUPT_MASK, ioaddr + IM_REG ); +} + +/* + . Function: smc_shutdown + . Purpose: closes down the SMC91xxx chip. + . Method: + . 1. zero the interrupt mask + . 2. clear the enable receive flag + . 3. clear the enable xmit flags + . + . TODO: + . (1) maybe utilize power down mode. + . Why not yet? Because while the chip will go into power down mode, + . the manual says that it will wake up in response to any I/O requests + . in the register space. Empirical results do not show this working. +*/ +static void smc_shutdown( int ioaddr ) +{ + PRINTK2("CARDNAME:smc_shutdown\n"); + + /* no more interrupts for me */ + SMC_SELECT_BANK( 2 ); + outb( 0, ioaddr + IM_REG ); + + /* and tell the card to stay away from that nasty outside world */ + SMC_SELECT_BANK( 0 ); + outb( RCR_CLEAR, ioaddr + RCR_REG ); + outb( TCR_CLEAR, ioaddr + TCR_REG ); + +#ifdef POWER_DOWN + /* finally, shut the chip down */ + SMC_SELECT_BANK( 1 ); + outw( inw( ioaddr + CONFIG_REG ) & ~CONFIG_EPH_POWER_EN, + ioaddr + CONFIG_REG ); +#endif +} + + +/* + . Function: smc_setmulticast( int ioaddr, int count, dev_mc_list * adds ) + . Purpose: + . This sets the internal hardware table to filter out unwanted multicast + . packets before they take up memory. + . + . The SMC chip uses a hash table where the high 6 bits of the CRC of + . address are the offset into the table. If that bit is 1, then the + . multicast packet is accepted. Otherwise, it's dropped silently. + . + . To use the 6 bits as an offset into the table, the high 3 bits are the + . number of the 8 bit register, while the low 3 bits are the bit within + . that register. + . + . This routine is based very heavily on the one provided by Peter Cammaert. +*/ + + +static void smc_setmulticast( int ioaddr, int count, struct dev_mc_list * addrs ) { + int i; + unsigned char multicast_table[ 8 ]; + struct dev_mc_list * cur_addr; + /* table for flipping the order of 3 bits */ + unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; + + PRINTK2("CARDNAME:smc_setmulticast\n"); + + /* start with a table of all zeros: reject all */ + memset( multicast_table, 0, sizeof( multicast_table ) ); + + cur_addr = addrs; + for ( i = 0; i < count ; i ++, cur_addr = cur_addr->next ) { + int position; + + /* do we have a pointer here? */ + if ( !cur_addr ) + break; + /* make sure this is a multicast address - shouldn't this + be a given if we have it here ? */ + if ( !( *cur_addr->dmi_addr & 1 ) ) + continue; + + /* only use the low order bits */ + position = crc32( cur_addr->dmi_addr, 6 ) & 0x3f; + + /* do some messy swapping to put the bit in the right spot */ + multicast_table[invert3[position&7]] |= + (1<>3)&7]); + + } + /* now, the table can be loaded into the chipset */ + SMC_SELECT_BANK( 3 ); + + for ( i = 0; i < 8 ; i++ ) { + outb( multicast_table[i], ioaddr + MCAST_REG1 + i ); + } +} + +/* + Finds the CRC32 of a set of bytes. + Again, from Peter Cammaert's code. +*/ +static int crc32( char * s, int length ) { + /* indices */ + int perByte; + int perBit; + /* crc polynomial for Ethernet */ + const unsigned long poly = 0xedb88320; + /* crc value - preinitialized to all 1's */ + unsigned long crc_value = 0xffffffff; + + for ( perByte = 0; perByte < length; perByte ++ ) { + unsigned char c; + + c = *(s++); + for ( perBit = 0; perBit < 8; perBit++ ) { + crc_value = (crc_value>>1)^ + (((crc_value^c)&0x01)?poly:0); + c >>= 1; + } + } + return crc_value; +} + + +/* + . Function: smc_wait_to_send_packet( struct sk_buff * skb, struct device * ) + . Purpose: + . Attempt to allocate memory for a packet, if chip-memory is not + . available, then tell the card to generate an interrupt when it + . is available. + . + . Algorithm: + . + . o if the saved_skb is not currently null, then drop this packet + . on the floor. This should never happen, because of TBUSY. + . o if the saved_skb is null, then replace it with the current packet, + . o See if I can sending it now. + . o (NO): Enable interrupts and let the interrupt handler deal with it. + . o (YES):Send it now. +*/ +static int smc_wait_to_send_packet( struct sk_buff * skb, struct net_device * dev ) +{ + struct smc_local *lp = (struct smc_local *)dev->priv; + unsigned short ioaddr = dev->base_addr; + word length; + unsigned short numPages; + word time_out; + word status; + unsigned long flags; + + PRINTK3("%s:smc_wait_to_send_packet\n", dev->name); + + spin_lock_irqsave(&lp->lock, flags); + + if ( lp->saved_skb) { + /* THIS SHOULD NEVER HAPPEN. */ + lp->stats.tx_aborted_errors++; + printk("%s: Bad Craziness - sent packet while busy.\n", + dev->name); + spin_unlock_irqrestore(&lp->lock, flags); + return 1; + } + lp->saved_skb = skb; + + length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + + /* + ** The MMU wants the number of pages to be the number of 256 bytes + ** 'pages', minus 1 ( since a packet can't ever have 0 pages :) ) + ** + ** The 91C111 ignores the size bits, but the code is left intact + ** for backwards and future compatibility. + ** + ** Pkt size for allocating is data length +6 (for additional status + ** words, length and ctl!) + ** + ** If odd size then last byte is included in this header. + */ + numPages = ((length & 0xfffe) + 6); + numPages >>= 8; // Divide by 256 + + if (numPages > 7 ) { + printk("%s: Far too big packet error. \n", dev->name); + /* freeing the packet is a good thing here... but should + . any packets of this size get down here? */ + dev_kfree_skb (skb); + lp->saved_skb = NULL; + /* this IS an error, but, i don't want the skb saved */ + netif_wake_queue(dev); + spin_unlock_irqrestore(&lp->lock, flags); + return 0; + } + /* either way, a packet is waiting now */ + lp->packets_waiting++; + + /* now, try to allocate the memory */ + SMC_SELECT_BANK( 2 ); + outw( MC_ALLOC | numPages, ioaddr + MMU_CMD_REG ); + /* + . Performance Hack + . + . wait a short amount of time.. if I can send a packet now, I send + . it now. Otherwise, I enable an interrupt and wait for one to be + . available. + . + . I could have handled this a slightly different way, by checking to + . see if any memory was available in the FREE MEMORY register. However, + . either way, I need to generate an allocation, and the allocation works + . no matter what, so I saw no point in checking free memory. + */ + time_out = MEMORY_WAIT_TIME; + do { + status = inb( ioaddr + INT_REG ); + if ( status & IM_ALLOC_INT ) { + /* acknowledge the interrupt */ + outb( IM_ALLOC_INT, ioaddr + INT_REG ); + break; + } + } while ( -- time_out ); + + if ( !time_out ) { +#if defined(__m32r__) +// netif_stop_queue(dev); // 2003-07-13 by takeo +#endif + /* oh well, wait until the chip finds memory later */ + SMC_ENABLE_INT( IM_ALLOC_INT ); + + /* Check the status bit one more time just in case */ + /* it snuk in between the time we last checked it */ + /* and when we set the interrupt bit */ + status = inb( ioaddr + INT_REG ); + if ( !(status & IM_ALLOC_INT) ) { + PRINTK2("%s: memory allocation deferred. \n", + dev->name); + /* it's deferred, but I'll handle it later */ + spin_unlock_irqrestore(&lp->lock, flags); + return 0; + } + + /* Looks like it did sneak in, so disable */ + /* the interrupt */ + SMC_DISABLE_INT( IM_ALLOC_INT ); + } + /* or YES! I can send the packet now.. */ +#if THROTTLE_TX_PKTS + netif_stop_queue(dev); +#endif + smc_hardware_send_packet(dev); +// netif_wake_queue(dev); + + spin_unlock_irqrestore(&lp->lock, flags); + + return 0; +} + +/* + . Function: smc_hardware_send_packet(struct device * ) + . Purpose: + . This sends the actual packet to the SMC9xxx chip. + . + . Algorithm: + . First, see if a saved_skb is available. + . ( this should NOT be called if there is no 'saved_skb' + . Now, find the packet number that the chip allocated + . Point the data pointers at it in memory + . Set the length word in the chip's memory + . Dump the packet to chip memory + . Check if a last byte is needed ( odd length packet ) + . if so, set the control flag right + . Tell the card to send it + . Enable the transmit interrupt, so I know if it failed + . Free the kernel data if I actually sent it. +*/ +static void smc_hardware_send_packet( struct net_device * dev ) +{ + struct smc_local *lp = (struct smc_local *)dev->priv; + byte packet_no; + struct sk_buff * skb = lp->saved_skb; + word length; + unsigned short ioaddr; + byte * buf; + + PRINTK3("%s:smc_hardware_send_packet\n", dev->name); + + ioaddr = dev->base_addr; + + if ( !skb ) { + PRINTK("%s: In XMIT with no packet to send \n", dev->name); + return; + } + length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + buf = skb->data; + + /* If I get here, I _know_ there is a packet slot waiting for me */ + packet_no = inb( ioaddr + AR_REG ); + if ( packet_no & AR_FAILED ) { + /* or isn't there? BAD CHIP! */ + printk(KERN_DEBUG "%s: Memory allocation failed. \n", + dev->name); + dev_kfree_skb_any (skb); + lp->saved_skb = NULL; + netif_wake_queue(dev); + return; + } + + /* we have a packet address, so tell the card to use it */ + outb( packet_no, ioaddr + PN_REG ); + + /* point to the beginning of the packet */ + outw( PTR_AUTOINC , ioaddr + PTR_REG ); + + PRINTK3("%s: Trying to xmit packet of length %x\n", + dev->name, length); + +#if SMC_DEBUG > 2 + printk("Transmitting Packet\n"); + print_packet( buf, length ); +#endif + + /* send the packet length ( +6 for status, length and ctl byte ) + and the status word ( set to zeros ) */ +#ifdef USE_32_BIT + outl( (length +6 ) << 16 , ioaddr + DATA_REG ); +#else + outw( 0, ioaddr + DATA_REG ); + /* send the packet length ( +6 for status words, length, and ctl*/ + outb( (length+6) & 0xFF,ioaddr + DATA_REG ); + outb( (length+6) >> 8 , ioaddr + DATA_REG ); +#endif + + /* send the actual data + . I _think_ it's faster to send the longs first, and then + . mop up by sending the last word. It depends heavily + . on alignment, at least on the 486. Maybe it would be + . a good idea to check which is optimal? But that could take + . almost as much time as is saved? + */ +#ifdef USE_32_BIT + outsl(ioaddr + DATA_REG, buf, length >> 2 ); + if ( length & 0x2 ) + outw(*((word *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_REG); +#else + outsw(ioaddr + DATA_REG , buf, (length ) >> 1); +#endif // USE_32_BIT + + /* Send the last byte, if there is one. */ + if ( (length & 1) == 0 ) { + outw( 0, ioaddr + DATA_REG ); + } else { + outb( buf[length -1 ], ioaddr + DATA_REG ); + outb( 0x20, ioaddr + DATA_REG); // Set odd bit in CONTROL BYTE + } + + /* and let the chipset deal with it */ + outw( MC_ENQUEUE , ioaddr + MMU_CMD_REG ); + + /* enable the interrupts */ + SMC_ENABLE_INT( (IM_TX_INT | IM_TX_EMPTY_INT) ); + + PRINTK2("%s: Sent packet of length %d \n", dev->name, length); +#if defined(__m32r__) + lp->stats.tx_bytes += length; +#endif + + lp->saved_skb = NULL; + dev_kfree_skb_any (skb); + + dev->trans_start = jiffies; + + /* we can send another packet */ +// netif_wake_queue(dev); + + return; +} + +/*------------------------------------------------------------------------- + | + | smc_init( int unit ) + | Input parameters: + | dev->base_addr == 0, try to find all possible locations + | dev->base_addr == 1, return failure code + | dev->base_addr == 2, always allocate space, and return success + | dev->base_addr == this is the address to check + | + | Output: + | pointer to net_device or ERR_PTR(error) + | + --------------------------------------------------------------------------- +*/ +static int io; +static int irq; +static int ifport; + +struct net_device * __init smc_init(int unit) +{ + struct net_device *dev = alloc_etherdev(sizeof(struct smc_local)); + unsigned int *port; + int err = 0; + + if (!dev) + return ERR_PTR(-ENODEV); + + if (unit >= 0) { + sprintf(dev->name, "eth%d", unit); + netdev_boot_setup_check(dev); + io = dev->base_addr; + irq = dev->irq; + } + +#ifdef MODULE + SET_MODULE_OWNER (dev); +#endif + if (io > 0x1ff) { /* Check a single specified location. */ + err = smc_probe(dev, io); + } else if (io != 0) { /* Don't probe at all. */ + err = -ENXIO; + } else { + for (port = smc_portlist; *port; port++) { + if (smc_probe(dev, *port) == 0) + break; + } + if (!*port) + err = -ENODEV; + } + if (err) + goto out; + err = register_netdev(dev); + if (err) + goto out1; + return dev; +out1: + free_irq(dev->irq, dev); + release_region(dev->base_addr, SMC_IO_EXTENT); +out: + free_netdev(dev); + return ERR_PTR(err); +} + + +/*------------------------------------------------------------------------- + | + | smc_destructor( struct device * dev ) + | Input parameters: + | dev, pointer to the device structure + | + | Output: + | None. + | + --------------------------------------------------------------------------- +*/ +void smc_destructor(struct net_device *dev) +{ + PRINTK2("CARDNAME:smc_destructor\n"); +} + + +#ifndef NO_AUTOPROBE +/*---------------------------------------------------------------------- + . smc_findirq + . + . This routine has a simple purpose -- make the SMC chip generate an + . interrupt, so an auto-detect routine can detect it, and find the IRQ, + ------------------------------------------------------------------------ +*/ +int __init smc_findirq( int ioaddr ) +{ + int timeout = 20; + unsigned long cookie; + + PRINTK2("CARDNAME:smc_findirq\n"); + + /* I have to do a STI() here, because this is called from + a routine that does an CLI during this process, making it + rather difficult to get interrupts for auto detection */ + sti(); + + cookie = probe_irq_on(); + + /* + * What I try to do here is trigger an ALLOC_INT. This is done + * by allocating a small chunk of memory, which will give an interrupt + * when done. + */ + + + SMC_SELECT_BANK(2); + /* enable ALLOCation interrupts ONLY */ + outb( IM_ALLOC_INT, ioaddr + IM_REG ); + + /* + . Allocate 512 bytes of memory. Note that the chip was just + . reset so all the memory is available + */ + outw( MC_ALLOC | 1, ioaddr + MMU_CMD_REG ); + + /* + . Wait until positive that the interrupt has been generated + */ + while ( timeout ) { + byte int_status; + + int_status = inb( ioaddr + INT_REG ); + + if ( int_status & IM_ALLOC_INT ) + break; /* got the interrupt */ + timeout--; + } + + /* there is really nothing that I can do here if timeout fails, + as autoirq_report will return a 0 anyway, which is what I + want in this case. Plus, the clean up is needed in both + cases. */ + + /* DELAY HERE! + On a fast machine, the status might change before the interrupt + is given to the processor. This means that the interrupt was + never detected, and autoirq_report fails to report anything. + This should fix autoirq_* problems. + */ + mdelay(10); + + /* and disable all interrupts again */ + outb( 0, ioaddr + IM_REG ); + + /* clear hardware interrupts again, because that's how it + was when I was called... */ + cli(); + + /* and return what I found */ + return probe_irq_off(cookie); +} +#endif + +/*---------------------------------------------------------------------- + . Function: smc_probe( int ioaddr ) + . + . Purpose: + . Tests to see if a given ioaddr points to an SMC91111 chip. + . Returns a 0 on success + . + . Algorithm: + . (1) see if the high byte of BANK_SELECT is 0x33 + . (2) compare the ioaddr with the base register's address + . (3) see if I recognize the chip ID in the appropriate register + . + .--------------------------------------------------------------------- + */ +/*--------------------------------------------------------------- + . Here I do typical initialization tasks. + . + . o Initialize the structure if needed + . o print out my vanity message if not done so already + . o print out what type of hardware is detected + . o print out the ethernet address + . o find the IRQ + . o set up my private data + . o configure the dev structure with my subroutines + . o actually GRAB the irq. + . o GRAB the region + .-----------------------------------------------------------------*/ + +static int __init smc_probe(struct net_device *dev, int ioaddr ) +{ + int i, memory, retval; + static unsigned version_printed = 0; + unsigned int bank; + + const char *version_string; + + /*registers */ + word revision_register; + word base_address_register; + word memory_info_register; + /*=> Pramod */ + struct smc_local *lp; + /*<= Pramod */ + + PRINTK2("CARDNAME:smc_probe\n"); + + /* Grab the region so that no one else tries to probe our ioports. */ + if (!request_region(ioaddr, SMC_IO_EXTENT, dev->name)) return -EBUSY; + + dev->irq = irq; + dev->if_port = ifport; + + /* First, see if the high byte is 0x33 */ + bank = inw( ioaddr + BANK_SELECT ); + if ( (bank & 0xFF00) != 0x3300 ) return -ENODEV; + + /* The above MIGHT indicate a device, but I need to write to further test this. */ + outw( 0x0, ioaddr + BANK_SELECT ); + bank = inw( ioaddr + BANK_SELECT ); + if ( (bank & 0xFF00 ) != 0x3300 ) + { + retval = -ENODEV; + goto err_out; + } + + /* well, we've already written once, so hopefully another time won't + hurt. This time, I need to switch the bank register to bank 1, + so I can access the base address register */ + SMC_SELECT_BANK(1); + base_address_register = inw( ioaddr + BASE_REG ); + if ( ioaddr != ( base_address_register >> 3 & 0x3E0 ) ) + { + printk("CARDNAME: IOADDR %x doesn't match configuration (%x)." + "Probably not a SMC chip\n", + ioaddr, base_address_register >> 3 & 0x3E0 ); + /* well, the base address register didn't match. Must not have + been a SMC chip after all. */ + retval = -ENODEV; + goto err_out; + } + + /* check if the revision register is something that I recognize. + These might need to be added to later, as future revisions + could be added. */ + SMC_SELECT_BANK(3); + revision_register = inw( ioaddr + REV_REG ); + if ( !chip_ids[ ( revision_register >> 4 ) & 0xF ] ) + { + /* I don't recognize this chip, so... */ + printk("CARDNAME: IO %x: Unrecognized revision register:" + " %x, Contact author. \n", + ioaddr, revision_register ); + retval = -ENODEV; + goto err_out; + } + + /* at this point I'll assume that the chip is an SMC9xxx. + It might be prudent to check a listing of MAC addresses + against the hardware address, or do some other tests. */ + + if (version_printed++ == 0) + printk("%s", version); + + /* fill in some of the fields */ + dev->base_addr = ioaddr; + + /* + . Get the MAC address ( bank 1, regs 4 - 9 ) + */ + SMC_SELECT_BANK( 1 ); + for ( i = 0; i < 6; i += 2 ) + { + word address; + + address = inw( ioaddr + ADDR0_REG + i ); + dev->dev_addr[ i + 1] = address >> 8; + dev->dev_addr[ i ] = address & 0xFF; + } + + /* get the memory information */ + + SMC_SELECT_BANK( 0 ); + memory_info_register = inw( ioaddr + MIR_REG ); + memory = memory_info_register & (word)0x00ff; + memory *= LAN91C111_MEMORY_MULTIPLIER; + + /* + Now, I want to find out more about the chip. This is sort of + redundant, but it's cleaner to have it in both, rather than having + one VERY long probe procedure. + */ + SMC_SELECT_BANK(3); + revision_register = inw( ioaddr + REV_REG ); + version_string = chip_ids[ ( revision_register >> 4 ) & 0xF ]; + if ( !version_string ) + { + /* I shouldn't get here because this call was done before.... */ + retval = -ENODEV; + goto err_out; + } + + /* now, reset the chip, and put it into a known state */ + smc_reset( dev ); + + /* + . If dev->irq is 0, then the device has to be banged on to see + . what the IRQ is. + . + . This banging doesn't always detect the IRQ, for unknown reasons. + . a workaround is to reset the chip and try again. + . + . Interestingly, the DOS packet driver *SETS* the IRQ on the card to + . be what is requested on the command line. I don't do that, mostly + . because the card that I have uses a non-standard method of accessing + . the IRQs, and because this _should_ work in most configurations. + . + . Specifying an IRQ is done with the assumption that the user knows + . what (s)he is doing. No checking is done!!!! + . + */ +#if defined(CONFIG_PLAT_M32700UT) && defined(NO_AUTOPROBE) + dev->irq = M32700UT_LAN_IRQ_LAN; +#endif +#if defined(CONFIG_PLAT_OPSPUT) && defined(NO_AUTOPROBE) + dev->irq = OPSPUT_LAN_IRQ_LAN; +#endif +#ifndef NO_AUTOPROBE // by takeo + if ( dev->irq < 2 ) { + int trials; + + trials = 3; + while ( trials-- ) { + dev->irq = smc_findirq( ioaddr ); + if ( dev->irq ) + break; + /* kick the card and try again */ + smc_reset( dev ); + } + } + + if (dev->irq == 0 ) { + printk("%s: Couldn't autodetect your IRQ. Use irq=xx.\n", + dev->name); + retval = -ENODEV; + goto err_out; + } + + if (dev->irq == 2) { + /* Fixup for users that don't know that IRQ 2 is really IRQ 9, + * or don't know which one to set. + */ + dev->irq = 9; + } +#endif + + /* now, print out the card info, in a short format.. */ + + printk("%s: %s(rev:%d) at %#3x IRQ:%d MEMSIZE:%db NOWAIT:%d ", + dev->name, + version_string, revision_register & 0xF, ioaddr, dev->irq, + memory, dev->dma); + /* + . Print the Ethernet address + */ + printk("ADDR: "); + for (i = 0; i < 5; i++) + printk("%2.2x:", dev->dev_addr[i] ); + printk("%2.2x \n", dev->dev_addr[5] ); + + /* set the private data to zero by default */ + memset(dev->priv, 0, sizeof(struct smc_local)); + + /* Grab the IRQ */ + retval = request_irq(dev->irq, &smc_interrupt, 0, dev->name, dev); + if (retval) { + printk("%s: unable to get IRQ %d (irqval=%d).\n", + dev->name, dev->irq, retval); + goto err_out; + } + + dev->open = smc_open; + dev->stop = smc_close; + dev->hard_start_xmit = smc_wait_to_send_packet; + dev->tx_timeout = smc_timeout; + dev->get_stats = smc_query_statistics; +#ifdef HAVE_MULTICAST + dev->set_multicast_list = &smc_set_multicast_list; +#endif + + /* => Store the ChipRevision and ChipID, to be used in resolving the Odd-Byte issue in RevB of LAN91C111; Pramod */ + SMC_SELECT_BANK(3); + revision_register = inw( ioaddr + REV_REG ); + lp = (struct smc_local *)dev->priv; + lp->ChipID = (revision_register >> 4) & 0xF; + lp->ChipRev = revision_register & 0xF; + + spin_lock_init(&lp->lock); + + return 0; + +err_out: + release_region (ioaddr, SMC_IO_EXTENT); + return retval; +} + +#if SMC_DEBUG > 2 +static void print_packet( byte * buf, int length ) +{ +#if 1 + int i; + int remainder; + int lines; + + printk("Packet of length %d \n", length ); + +#if SMC_DEBUG > 3 + lines = length / 16; + remainder = length % 16; + + for ( i = 0; i < lines ; i ++ ) { + int cur; + + for ( cur = 0; cur < 8; cur ++ ) { + byte a, b; + + a = *(buf ++ ); + b = *(buf ++ ); + printk("%02x%02x ", a, b ); + } + printk("\n"); + } + for ( i = 0; i < remainder/2 ; i++ ) { + byte a, b; + + a = *(buf ++ ); + b = *(buf ++ ); + printk("%02x%02x ", a, b ); + } + printk("\n"); +#endif +#endif +} +#endif + + +/* + * Open and Initialize the board + * + * Set up everything, reset the card, etc .. + * + */ +static int smc_open(struct net_device *dev) +{ + struct smc_local *lp = (struct smc_local *)dev->priv; + int ioaddr = dev->base_addr; + int i; /* used to set hw ethernet address */ + + PRINTK2("%s:smc_open\n", dev->name); + +#if 0 + /* clear out all the junk that was put here before... */ + memset(dev->priv, 0, sizeof(struct smc_local)); +#endif + + netif_start_queue(dev); +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + + // Setup the default Register Modes + lp->tcr_cur_mode = TCR_DEFAULT; + lp->rcr_cur_mode = RCR_DEFAULT; + lp->rpc_cur_mode = RPC_DEFAULT; + + // Set default parameters (files) + lp->ctl_swfdup = 0; + lp->ctl_ephloop = 0; + lp->ctl_miiop = 0; + lp->ctl_autoneg = 1; + lp->ctl_rfduplx = 1; + lp->ctl_rspeed = 100; + lp->ctl_afduplx = 1; + lp->ctl_aspeed = 100; + lp->ctl_lnkfail = 1; + lp->ctl_forcol = 0; + lp->ctl_filtcar = 0; + + /* reset the hardware */ + + smc_reset( dev ); + smc_enable( dev ); + + /* Configure the PHY */ + smc_phy_configure(dev); + + /* + According to Becker, I have to set the hardware address + at this point, because the (l)user can set it with an + ioctl. Easily done... + */ + SMC_SELECT_BANK( 1 ); + for ( i = 0; i < 6; i += 2 ) { + word address; + + address = dev->dev_addr[ i + 1 ] << 8 ; + address |= dev->dev_addr[ i ]; + outw( address, ioaddr + ADDR0_REG + i ); + } + +#ifdef CONFIG_SYSCTL + smc_sysctl_register(dev); +#endif /* CONFIG_SYSCTL */ + + netif_start_queue(dev); + return 0; +} + +/*-------------------------------------------------------- + . Called by the kernel to send a packet out into the void + . of the net. This routine is largely based on + . skeleton.c, from Becker. + .-------------------------------------------------------- +*/ +static void smc_timeout (struct net_device *dev) +{ + + struct smc_local *lp = (struct smc_local *)dev->priv; + unsigned long flags; + + /* + * Best would be to use synchronize_irq(); spin_lock() here + * lets make it work first.. + */ + + spin_lock_irqsave(&lp->lock, flags); + + PRINTK3("%s:smc_send_packet\n", dev->name); + + /* If we get here, some higher level has decided we are broken. + There should really be a "kick me" function call instead. */ + printk(KERN_WARNING "%s: transmit timed out, %s?\n",dev->name, tx_done(dev) ? "IRQ conflict" :"network cable problem"); + /* "kick" the adaptor */ + smc_reset( dev ); + smc_enable( dev ); + + /* Reconfigure the PHY */ + smc_phy_configure(dev); + + dev->trans_start = jiffies; + /* clear anything saved */ + ((struct smc_local *)dev->priv)->saved_skb = NULL; + + spin_unlock_irqrestore(&lp->lock, flags); + + netif_wake_queue(dev); +} + +/*-------------------------------------------------------------------- + . + . This is the main routine of the driver, to handle the net_device when + . it needs some attention. + . + . So: + . first, save state of the chipset + . branch off into routines to handle each case, and acknowledge + . each to the interrupt register + . and finally restore state. + . + ---------------------------------------------------------------------*/ +static irqreturn_t smc_interrupt(int irq, void * dev_id, struct pt_regs * regs) +{ + struct net_device *dev = dev_id; + int ioaddr = dev->base_addr; + struct smc_local *lp = (struct smc_local *)dev->priv; + + byte status; + word card_stats; + byte mask; + int timeout; + /* state registers */ + word saved_bank; + word saved_pointer; + int handled = 0; + + PRINTK3("%s: SMC interrupt started \n", dev->name); + + if (dev == NULL) { + printk(KERN_WARNING "%s: irq %d for unknown device.\n", + dev->name, irq); + return IRQ_RETVAL(handled); + } + +/* will Linux let this happen ?? If not, this costs some speed + if ( dev->interrupt ) { + printk(KERN_WARNING "%s: interrupt inside interrupt.\n", + dev->name); + return; + } + + dev->interrupt = 1; */ + + spin_lock(&lp->lock); + + saved_bank = inw( ioaddr + BANK_SELECT ); + + SMC_SELECT_BANK(2); + saved_pointer = inw( ioaddr + PTR_REG ); + + /* read the interrupt status register */ + mask = inb( ioaddr + IM_REG ); + + /* disable all interrupts */ + outb( 0, ioaddr + IM_REG ); + + + /* set a timeout value, so I don't stay here forever */ + timeout = 8; + + PRINTK2(KERN_WARNING "%s: MASK IS %x \n", dev->name, mask); + do { + /* read the status flag, and mask it */ + status = inb( ioaddr + INT_REG ) & mask; + if (!status ) + break; + + handled = 1; + + PRINTK3(KERN_WARNING "%s: Handling interrupt status %x \n", + dev->name, status); + + if (status & IM_RCV_INT) { + /* Got a packet(s). */ + PRINTK2(KERN_WARNING + "%s: Receive Interrupt\n", dev->name); + smc_rcv(dev); + } else if (status & IM_TX_INT ) { + PRINTK2(KERN_WARNING "%s: TX ERROR handled\n", + dev->name); + smc_tx(dev); + // Acknowledge the interrupt + outb(IM_TX_INT, ioaddr + INT_REG ); +#if THROTTLE_TX_PKTS + netif_wake_queue(dev); +#endif + } else if (status & IM_TX_EMPTY_INT ) { + /* update stats */ + SMC_SELECT_BANK( 0 ); + card_stats = inw( ioaddr + COUNTER_REG ); + /* single collisions */ + lp->stats.collisions += card_stats & 0xF; + card_stats >>= 4; + /* multiple collisions */ + lp->stats.collisions += card_stats & 0xF; + + /* these are for when linux supports these statistics */ +#if 0 + card_stats >>= 4; + /* deferred */ + card_stats >>= 4; + /* excess deferred */ +#endif + SMC_SELECT_BANK( 2 ); + PRINTK2(KERN_WARNING "%s: TX_BUFFER_EMPTY handled\n", + dev->name); + // Acknowledge the interrupt + outb( IM_TX_EMPTY_INT, ioaddr + INT_REG ); + mask &= ~IM_TX_EMPTY_INT; + lp->stats.tx_packets += lp->packets_waiting; + lp->packets_waiting = 0; + + } else if (status & IM_ALLOC_INT ) { + PRINTK2(KERN_DEBUG "%s: Allocation interrupt \n", + dev->name); + /* clear this interrupt so it doesn't happen again */ + mask &= ~IM_ALLOC_INT; + + smc_hardware_send_packet( dev ); + + /* enable xmit interrupts based on this */ + mask |= ( IM_TX_EMPTY_INT | IM_TX_INT ); + + /* and let the card send more packets to me */ +#if ! THROTTLE_TX_PKTS + netif_wake_queue(dev); +#endif + + PRINTK2("%s: Handoff done successfully.\n", + dev->name); + } else if (status & IM_RX_OVRN_INT ) { + PRINTK2("%s: IM_RX_OVRN_INT \n",dev->name); + lp->stats.rx_errors++; + lp->stats.rx_fifo_errors++; + // Acknowledge the interrupt + outb( IM_RX_OVRN_INT, ioaddr + INT_REG ); + } else if (status & IM_EPH_INT ) { + PRINTK("%s: UNSUPPORTED: EPH INTERRUPT \n", + dev->name); + } else if (status & IM_MDINT ) { + smc_phy_interrupt(dev); + // Acknowledge the interrupt + outb(IM_MDINT, ioaddr + INT_REG ); + } else if (status & IM_ERCV_INT ) { + PRINTK("%s: UNSUPPORTED: ERCV INTERRUPT \n", + dev->name); + // Acknowledge the interrupt + outb( IM_ERCV_INT, ioaddr + INT_REG ); + } + } while ( timeout -- ); + + + /* restore register states */ + + SMC_SELECT_BANK( 2 ); + + outb( mask, ioaddr + IM_REG ); + + PRINTK3( KERN_WARNING "%s: MASK is now %x \n", dev->name, mask); + outw( saved_pointer, ioaddr + PTR_REG ); + + SMC_SELECT_BANK( saved_bank ); + + //dev->interrupt = 0; + PRINTK3("%s: Interrupt done\n", dev->name); + + spin_unlock(&lp->lock); + + return IRQ_RETVAL(handled); +} + +/*------------------------------------------------------------- + . + . smc_rcv - receive a packet from the card + . + . There is ( at least ) a packet waiting to be read from + . chip-memory. + . + . o Read the status + . o If an error, record it + . o otherwise, read in the packet + -------------------------------------------------------------- +*/ +static inline void smc_rcv(struct net_device *dev) +{ + struct smc_local *lp = (struct smc_local *)dev->priv; + int ioaddr = dev->base_addr; + int packet_number; + word status; + word packet_length; + + PRINTK3("%s:smc_rcv\n", dev->name); + + /* assume bank 2 */ + + packet_number = inw( ioaddr + RXFIFO_REG ); + + if ( packet_number & RXFIFO_REMPTY ) { + + /* we got called , but nothing was on the FIFO */ + PRINTK("%s: WARNING: smc_rcv with nothing on FIFO. \n", + dev->name); + /* don't need to restore anything */ + return; + } + + /* start reading from the start of the packet */ + outw( PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + PTR_REG ); + + /* First two words are status and packet_length */ + status = inw( ioaddr + DATA_REG ); + packet_length = inw( ioaddr + DATA_REG ); + + packet_length &= 0x07ff; /* mask off top bits */ + + PRINTK2("RCV: STATUS %4x LENGTH %4x\n", status, packet_length ); + + if ( !(status & RS_ERRORS ) ){ + /* do stuff to make a new packet */ + struct sk_buff * skb; + byte * data; + + /* set multicast stats */ + if ( status & RS_MULTICAST ) + lp->stats.multicast++; + + // Allocate enough memory for entire receive frame, to be safe + skb = dev_alloc_skb( packet_length ); + + /* Adjust for having already read the first two words */ + packet_length -= 4; + + if ( skb == NULL ) { + printk(KERN_NOTICE "%s: Low memory, packet dropped.\n", + dev->name); + lp->stats.rx_dropped++; + goto done; + } + + /* + ! This should work without alignment, but it could be + ! in the worse case + */ + /* TODO: Should I use 32bit alignment here ? */ + skb_reserve( skb, 2 ); /* 16 bit alignment */ + + skb->dev = dev; + + /* => + ODD-BYTE ISSUE : The odd byte problem has been fixed in the LAN91C111 Rev B. + So we check if the Chip Revision, stored in smsc_local->ChipRev, is = 1. + If so then we increment the packet length only if RS_ODDFRAME is set. + If the Chip's revision is equal to 0, then we blindly increment the packet length + by 1, thus always assuming that the packet is odd length, leaving the higher layer + to decide the actual length. + -- Pramod + <= */ + if ((9 == lp->ChipID) && (1 == lp->ChipRev)) + { + if (status & RS_ODDFRAME) + data = skb_put( skb, packet_length + 1 ); + else + data = skb_put( skb, packet_length); + } + else + { + // set odd length for bug in LAN91C111, REV A + // which never sets RS_ODDFRAME + data = skb_put( skb, packet_length + 1 ); + } + +#ifdef USE_32_BIT + PRINTK3(" Reading %d dwords (and %d bytes) \n", + packet_length >> 2, packet_length & 3 ); + /* QUESTION: Like in the TX routine, do I want + to send the DWORDs or the bytes first, or some + mixture. A mixture might improve already slow PIO + performance */ + insl(ioaddr + DATA_REG , data, packet_length >> 2 ); + /* read the left over bytes */ + insb( ioaddr + DATA_REG, data + (packet_length & 0xFFFFFC), + packet_length & 0x3 ); +#else + PRINTK3(" Reading %d words and %d byte(s) \n", + (packet_length >> 1 ), packet_length & 1 ); + insw(ioaddr + DATA_REG , data, packet_length >> 1); + +#endif // USE_32_BIT + +#if SMC_DEBUG > 2 + printk("Receiving Packet\n"); + print_packet( data, packet_length ); +#endif + + skb->protocol = eth_type_trans(skb, dev ); + netif_rx(skb); + lp->stats.rx_packets++; +#if defined(__m32r__) + lp->stats.rx_bytes += packet_length; +#endif + } else { + /* error ... */ + lp->stats.rx_errors++; + + if ( status & RS_ALGNERR ) lp->stats.rx_frame_errors++; + if ( status & (RS_TOOSHORT | RS_TOOLONG ) ) + lp->stats.rx_length_errors++; + if ( status & RS_BADCRC) lp->stats.rx_crc_errors++; + } + + while ( inw( ioaddr + MMU_CMD_REG ) & MC_BUSY ) + udelay(1); // Wait until not busy +done: + /* error or good, tell the card to get rid of this packet */ + outw( MC_RELEASE, ioaddr + MMU_CMD_REG ); + + return; +} + + +/************************************************************************* + . smc_tx + . + . Purpose: Handle a transmit error message. This will only be called + . when an error, because of the AUTO_RELEASE mode. + . + . Algorithm: + . Save pointer and packet no + . Get the packet no from the top of the queue + . check if it's valid ( if not, is this an error??? ) + . read the status word + . record the error + . ( resend? Not really, since we don't want old packets around ) + . Restore saved values + ************************************************************************/ +static inline void smc_tx( struct net_device * dev ) +{ + int ioaddr = dev->base_addr; + struct smc_local *lp = (struct smc_local *)dev->priv; + byte saved_packet; + byte packet_no; + word tx_status; + + + PRINTK3("%s:smc_tx\n", dev->name); + + /* assume bank 2 */ + + saved_packet = inb( ioaddr + PN_REG ); + packet_no = inw( ioaddr + RXFIFO_REG ); +#if defined(__m32r__) + /* If the TX FIFO is empty then nothing to do */ + if ( packet_no & TXFIFO_TEMPTY ) + return; + + packet_no &= 0x7F; +#else + packet_no &= 0x7F; + + /* If the TX FIFO is empty then nothing to do */ + if ( packet_no & TXFIFO_TEMPTY ) + return; +#endif + + /* select this as the packet to read from */ + outb( packet_no, ioaddr + PN_REG ); + + /* read the first word (status word) from this packet */ + outw( PTR_AUTOINC | PTR_READ, ioaddr + PTR_REG ); + + tx_status = inw( ioaddr + DATA_REG ); + PRINTK3("%s: TX DONE STATUS: %4x \n", dev->name, tx_status); + + lp->stats.tx_errors++; + if ( tx_status & TS_LOSTCAR ) lp->stats.tx_carrier_errors++; + if ( tx_status & TS_LATCOL ) { + printk(KERN_DEBUG + "%s: Late collision occurred on last xmit.\n", + dev->name); + lp->stats.tx_window_errors++; + lp->ctl_forcol = 0; // Reset forced collsion + } +#if 0 + if ( tx_status & TS_16COL ) { ... } +#endif + + if ( tx_status & TS_SUCCESS ) { + // printk("%s: Successful packet caused interrupt \n", dev->name); + } + /* re-enable transmit */ + SMC_SELECT_BANK( 0 ); + outw( inw( ioaddr + TCR_REG ) | TCR_ENABLE, ioaddr + TCR_REG ); + + /* kill the packet */ + SMC_SELECT_BANK( 2 ); + outw( MC_FREEPKT, ioaddr + MMU_CMD_REG ); + + /* one less packet waiting for me */ + lp->packets_waiting--; + + /* Don't change Packet Number Reg until busy bit is cleared */ + /* Per LAN91C111 Spec, Page 50 */ + while ( inw( ioaddr + MMU_CMD_REG ) & MC_BUSY ); + + outb( saved_packet, ioaddr + PN_REG ); + return; +} + + +/*---------------------------------------------------- + . smc_close + . + . this makes the board clean up everything that it can + . and not talk to the outside world. Caused by + . an 'ifconfig ethX down' + . + -----------------------------------------------------*/ +static int smc_close(struct net_device *dev) +{ + netif_stop_queue(dev); + //dev->start = 0; + + PRINTK2("%s:smc_close\n", dev->name); + +#ifdef CONFIG_SYSCTL + smc_sysctl_unregister(dev); +#endif /* CONFIG_SYSCTL */ + + /* clear everything */ + smc_shutdown( dev->base_addr ); + + /* Update the statistics here. */ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + + return 0; +} + +/*------------------------------------------------------------ + . Get the current statistics. + . This may be called with the card open or closed. + .-------------------------------------------------------------*/ +static struct net_device_stats* smc_query_statistics(struct net_device *dev) { + struct smc_local *lp = (struct smc_local *)dev->priv; + + PRINTK2("%s:smc_query_statistics\n", dev->name); + + return &lp->stats; +} + +/*----------------------------------------------------------- + . smc_set_multicast_list + . + . This routine will, depending on the values passed to it, + . either make it accept multicast packets, go into + . promiscuous mode ( for TCPDUMP and cousins ) or accept + . a select set of multicast packets +*/ +static void smc_set_multicast_list(struct net_device *dev) +{ + short ioaddr = dev->base_addr; + + PRINTK2("%s:smc_set_multicast_list\n", dev->name); + + SMC_SELECT_BANK(0); + if ( dev->flags & IFF_PROMISC ) + { + PRINTK2("%s:smc_set_multicast_list:RCR_PRMS\n", dev->name); + outw( inw(ioaddr + RCR_REG ) | RCR_PRMS, ioaddr + RCR_REG ); + } + +/* BUG? I never disable promiscuous mode if multicasting was turned on. + Now, I turn off promiscuous mode, but I don't do anything to multicasting + when promiscuous mode is turned on. +*/ + + /* Here, I am setting this to accept all multicast packets. + I don't need to zero the multicast table, because the flag is + checked before the table is + */ + else if (dev->flags & IFF_ALLMULTI) + { + outw( inw(ioaddr + RCR_REG ) | RCR_ALMUL, ioaddr + RCR_REG ); + PRINTK2("%s:smc_set_multicast_list:RCR_ALMUL\n", dev->name); + } + + /* We just get all multicast packets even if we only want them + . from one source. This will be changed at some future + . point. */ + else if (dev->mc_count ) { + /* support hardware multicasting */ + + /* be sure I get rid of flags I might have set */ + outw( inw( ioaddr + RCR_REG ) & ~(RCR_PRMS | RCR_ALMUL), + ioaddr + RCR_REG ); + /* NOTE: this has to set the bank, so make sure it is the + last thing called. The bank is set to zero at the top */ + smc_setmulticast( ioaddr, dev->mc_count, dev->mc_list ); + } else { + PRINTK2("%s:smc_set_multicast_list:~(RCR_PRMS|RCR_ALMUL)\n", + dev->name); + outw( inw( ioaddr + RCR_REG ) & ~(RCR_PRMS | RCR_ALMUL), + ioaddr + RCR_REG ); + + /* + since I'm disabling all multicast entirely, I need to + clear the multicast list + */ + SMC_SELECT_BANK( 3 ); + outw( 0, ioaddr + MCAST_REG1 ); + outw( 0, ioaddr + MCAST_REG2 ); + outw( 0, ioaddr + MCAST_REG3 ); + outw( 0, ioaddr + MCAST_REG4 ); + } +} + +#ifdef MODULE + +static struct net_device *devSMC91111; +int nowait = 0; + +MODULE_PARM(io, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(nowait, "i"); + +/*------------------------------------------------------------ + . Module initialization function + .-------------------------------------------------------------*/ +int init_module(void) +{ + PRINTK2("CARDNAME:init_module\n"); + if (io == 0) + printk(KERN_WARNING + CARDNAME": You shouldn't use auto-probing with insmod!\n" ); + + /* copy the parameters from insmod into the device structure */ + devSMC91111 = smc_init(-1); + if (IS_ERR(devSMC91111)) + return PTR_ERR(devSMC91111); + + return 0; +} + +/*------------------------------------------------------------ + . Cleanup when module is removed with rmmod + .-------------------------------------------------------------*/ +void cleanup_module(void) +{ + unregister_netdev(devSMC91111); + free_irq(devSMC91111->irq, devSMC91111); + release_region(devSMC91111->base_addr, SMC_IO_EXTENT); + free_netdev(devSMC91111); +} + +#endif /* MODULE */ + + +#ifdef CONFIG_SYSCTL + + +/*------------------------------------------------------------ + . Modify a bit in the LAN91C111 register set + .-------------------------------------------------------------*/ +static word smc_modify_regbit(int bank, int ioaddr, int reg, + unsigned int bit, int val) +{ + word regval; + + SMC_SELECT_BANK( bank ); + + regval = inw( ioaddr+reg ); + if (val) + regval |= bit; + else + regval &= ~bit; + + outw( regval, ioaddr ); + return(regval); +} + + +/*------------------------------------------------------------ + . Retrieve a bit in the LAN91C111 register set + .-------------------------------------------------------------*/ +static int smc_get_regbit(int bank, int ioaddr, int reg, unsigned int bit) +{ + SMC_SELECT_BANK( bank ); + if ( inw( ioaddr+reg ) & bit) + return(1); + else + return(0); +} + + +/*------------------------------------------------------------ + . Modify a LAN91C111 register (word access only) + .-------------------------------------------------------------*/ +static void smc_modify_reg(int bank, int ioaddr, int reg, word val) +{ + SMC_SELECT_BANK( bank ); + outw( val, ioaddr+reg ); +} + + +/*------------------------------------------------------------ + . Retrieve a LAN91C111 register (word access only) + .-------------------------------------------------------------*/ +static int smc_get_reg(int bank, int ioaddr, int reg) +{ + SMC_SELECT_BANK( bank ); + return(inw( ioaddr+reg )); +} + + +static const char smc_info_string[] = +"\n" +"info Provides this information blurb\n" +"swver Prints the software version information of this driver\n" +"autoneg Auto-negotiate Mode = 1\n" +"rspeed Requested Speed, 100=100Mbps, 10=10Mpbs\n" +"rfduplx Requested Full Duplex Operation\n" +"aspeed Actual Speed, 100=100Mbps, 10=10Mpbs\n" +"afduplx Actual Full Duplex Operation\n" +"lnkfail PHY Link Failure when 1\n" +"miiop External MII when 1, Internal PHY when 0\n" +"swfdup Switched Full Duplex Mode (allowed only in MII operation)\n" +"ephloop EPH Block Loopback\n" +"forcol Force a collision\n" +"filtcar Filter leading edge of carrier sense for 12 bit times\n" +"freemem Free buffer memory in bytes\n" +"totmem Total buffer memory in bytes\n" +"leda Output of LED-A (green)\n" +"ledb Output of LED-B (yellow)\n" +"chiprev Revision ID of the LAN91C111 chip\n" +""; + +/*------------------------------------------------------------ + . Sysctl handler for all integer parameters + .-------------------------------------------------------------*/ +static int smc_sysctl_handler(ctl_table *ctl, int write, struct file * filp, + void *buffer, size_t *lenp, loff_t *ppos) +{ + struct net_device *dev = (struct net_device*)ctl->extra1; + struct smc_local *lp = (struct smc_local *)ctl->extra2; + int ioaddr = dev->base_addr; + int *valp = ctl->data; + int val; + int ret; + + // Update parameters from the real registers + switch (ctl->ctl_name) + { + case CTL_SMC_FORCOL: + *valp = smc_get_regbit(0, ioaddr, TCR_REG, TCR_FORCOL); + break; + + case CTL_SMC_FREEMEM: + *valp = ( (word)smc_get_reg(0, ioaddr, MIR_REG) >> 8 ) + * LAN91C111_MEMORY_MULTIPLIER; + break; + + + case CTL_SMC_TOTMEM: + *valp = ( smc_get_reg(0, ioaddr, MIR_REG) & (word)0x00ff ) + * LAN91C111_MEMORY_MULTIPLIER; + break; + + case CTL_SMC_CHIPREV: + *valp = smc_get_reg(3, ioaddr, REV_REG); + break; + + case CTL_SMC_AFDUPLX: + *valp = (lp->lastPhy18 & PHY_INT_DPLXDET) ? 1 : 0; + break; + + case CTL_SMC_ASPEED: + *valp = (lp->lastPhy18 & PHY_INT_SPDDET) ? 100 : 10; + break; + + case CTL_SMC_LNKFAIL: + *valp = (lp->lastPhy18 & PHY_INT_LNKFAIL) ? 1 : 0; + break; + + case CTL_SMC_LEDA: + *valp = (lp->rpc_cur_mode >> RPC_LSXA_SHFT) & (word)0x0007; + break; + + case CTL_SMC_LEDB: + *valp = (lp->rpc_cur_mode >> RPC_LSXB_SHFT) & (word)0x0007; + break; + + case CTL_SMC_MIIOP: + *valp = smc_get_regbit(1, ioaddr, CONFIG_REG, CONFIG_EXT_PHY); + break; + +#ifdef SMC_DEBUG + case CTL_SMC_REG_BSR: // Bank Select + *valp = smc_get_reg(0, ioaddr, BSR_REG); + break; + + case CTL_SMC_REG_TCR: // Transmit Control + *valp = smc_get_reg(0, ioaddr, TCR_REG); + break; + + case CTL_SMC_REG_ESR: // EPH Status + *valp = smc_get_reg(0, ioaddr, EPH_STATUS_REG); + break; + + case CTL_SMC_REG_RCR: // Receive Control + *valp = smc_get_reg(0, ioaddr, RCR_REG); + break; + + case CTL_SMC_REG_CTRR: // Counter + *valp = smc_get_reg(0, ioaddr, COUNTER_REG); + break; + + case CTL_SMC_REG_MIR: // Memory Information + *valp = smc_get_reg(0, ioaddr, MIR_REG); + break; + + case CTL_SMC_REG_RPCR: // Receive/Phy Control + *valp = smc_get_reg(0, ioaddr, RPC_REG); + break; + + case CTL_SMC_REG_CFGR: // Configuration + *valp = smc_get_reg(1, ioaddr, CONFIG_REG); + break; + + case CTL_SMC_REG_BAR: // Base Address + *valp = smc_get_reg(1, ioaddr, BASE_REG); + break; + + case CTL_SMC_REG_IAR0: // Individual Address + *valp = smc_get_reg(1, ioaddr, ADDR0_REG); + break; + + case CTL_SMC_REG_IAR1: // Individual Address + *valp = smc_get_reg(1, ioaddr, ADDR1_REG); + break; + + case CTL_SMC_REG_IAR2: // Individual Address + *valp = smc_get_reg(1, ioaddr, ADDR2_REG); + break; + + case CTL_SMC_REG_GPR: // General Purpose + *valp = smc_get_reg(1, ioaddr, GP_REG); + break; + + case CTL_SMC_REG_CTLR: // Control + *valp = smc_get_reg(1, ioaddr, CTL_REG); + break; + + case CTL_SMC_REG_MCR: // MMU Command + *valp = smc_get_reg(2, ioaddr, MMU_CMD_REG); + break; + + case CTL_SMC_REG_PNR: // Packet Number + *valp = smc_get_reg(2, ioaddr, PN_REG); + break; + + case CTL_SMC_REG_FPR: // Allocation Result/FIFO Ports + *valp = smc_get_reg(2, ioaddr, RXFIFO_REG); + break; + + case CTL_SMC_REG_PTR: // Pointer + *valp = smc_get_reg(2, ioaddr, PTR_REG); + break; + + case CTL_SMC_REG_DR: // Data + *valp = smc_get_reg(2, ioaddr, DATA_REG); + break; + + case CTL_SMC_REG_ISR: // Interrupt Status/Mask + *valp = smc_get_reg(2, ioaddr, INT_REG); + break; + + case CTL_SMC_REG_MTR1: // Multicast Table Entry 1 + *valp = smc_get_reg(3, ioaddr, MCAST_REG1); + break; + + case CTL_SMC_REG_MTR2: // Multicast Table Entry 2 + *valp = smc_get_reg(3, ioaddr, MCAST_REG2); + break; + + case CTL_SMC_REG_MTR3: // Multicast Table Entry 3 + *valp = smc_get_reg(3, ioaddr, MCAST_REG3); + break; + + case CTL_SMC_REG_MTR4: // Multicast Table Entry 4 + *valp = smc_get_reg(3, ioaddr, MCAST_REG4); + break; + + case CTL_SMC_REG_MIIR: // Management Interface + *valp = smc_get_reg(3, ioaddr, MII_REG); + break; + + case CTL_SMC_REG_REVR: // Revision + *valp = smc_get_reg(3, ioaddr, REV_REG); + break; + + case CTL_SMC_REG_ERCVR: // Early RCV + *valp = smc_get_reg(3, ioaddr, ERCV_REG); + break; + + case CTL_SMC_REG_EXTR: // External + *valp = smc_get_reg(7, ioaddr, EXT_REG); + break; + + case CTL_SMC_PHY_CTRL: + *valp = smc_read_phy_register(ioaddr, lp->phyaddr, + PHY_CNTL_REG); + break; + + case CTL_SMC_PHY_STAT: + *valp = smc_read_phy_register(ioaddr, lp->phyaddr, + PHY_STAT_REG); + break; + + case CTL_SMC_PHY_ID1: + *valp = smc_read_phy_register(ioaddr, lp->phyaddr, + PHY_ID1_REG); + break; + + case CTL_SMC_PHY_ID2: + *valp = smc_read_phy_register(ioaddr, lp->phyaddr, + PHY_ID2_REG); + break; + + case CTL_SMC_PHY_ADC: + *valp = smc_read_phy_register(ioaddr, lp->phyaddr, + PHY_AD_REG); + break; + + case CTL_SMC_PHY_REMC: + *valp = smc_read_phy_register(ioaddr, lp->phyaddr, + PHY_RMT_REG); + break; + + case CTL_SMC_PHY_CFG1: + *valp = smc_read_phy_register(ioaddr, lp->phyaddr, + PHY_CFG1_REG); + break; + + case CTL_SMC_PHY_CFG2: + *valp = smc_read_phy_register(ioaddr, lp->phyaddr, + PHY_CFG2_REG); + break; + + case CTL_SMC_PHY_INT: + *valp = smc_read_phy_register(ioaddr, lp->phyaddr, + PHY_INT_REG); + break; + + case CTL_SMC_PHY_MASK: + *valp = smc_read_phy_register(ioaddr, lp->phyaddr, + PHY_MASK_REG); + break; + +#endif // SMC_DEBUG + + default: + // Just ignore unsupported parameters + break; + } + + // Save old state + val = *valp; + + // Perform the generic integer operation + if ((ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos)) != 0) + return(ret); + + // Write changes out to the registers + if (write && *valp != val) { + + val = *valp; + switch (ctl->ctl_name) { + + case CTL_SMC_SWFDUP: + if (val) + lp->tcr_cur_mode |= TCR_SWFDUP; + else + lp->tcr_cur_mode &= ~TCR_SWFDUP; + + smc_modify_regbit(0, ioaddr, TCR_REG, TCR_SWFDUP, val); + break; + + case CTL_SMC_EPHLOOP: + if (val) + lp->tcr_cur_mode |= TCR_EPH_LOOP; + else + lp->tcr_cur_mode &= ~TCR_EPH_LOOP; + + smc_modify_regbit(0, ioaddr, TCR_REG, TCR_EPH_LOOP, val); + break; + + case CTL_SMC_FORCOL: + if (val) + lp->tcr_cur_mode |= TCR_FORCOL; + else + lp->tcr_cur_mode &= ~TCR_FORCOL; + + // Update the EPH block + smc_modify_regbit(0, ioaddr, TCR_REG, TCR_FORCOL, val); + break; + + case CTL_SMC_FILTCAR: + if (val) + lp->rcr_cur_mode |= RCR_FILT_CAR; + else + lp->rcr_cur_mode &= ~RCR_FILT_CAR; + + // Update the EPH block + smc_modify_regbit(0, ioaddr, RCR_REG, RCR_FILT_CAR, val); + break; + + case CTL_SMC_RFDUPLX: + // Disallow changes if in auto-negotiation mode + if (lp->ctl_autoneg) + break; + + if (val) + { + lp->rpc_cur_mode |= RPC_DPLX; + } + else + { + lp->rpc_cur_mode &= ~RPC_DPLX; + } + + // Reconfigure the PHY + smc_phy_configure(dev); + + break; + + case CTL_SMC_RSPEED: + // Disallow changes if in auto-negotiation mode + if (lp->ctl_autoneg) + break; + + if (val > 10) + lp->rpc_cur_mode |= RPC_SPEED; + else + lp->rpc_cur_mode &= ~RPC_SPEED; + + // Reconfigure the PHY + smc_phy_configure(dev); + + break; + + case CTL_SMC_AUTONEG: + if (val) + lp->rpc_cur_mode |= RPC_ANEG; + else + lp->rpc_cur_mode &= ~RPC_ANEG; + + // Reconfigure the PHY + smc_phy_configure(dev); + + break; + + case CTL_SMC_LEDA: + val &= 0x07; // Restrict to 3 ls bits + lp->rpc_cur_mode &= ~(word)(0x07<rpc_cur_mode |= (word)(val<rpc_cur_mode); + break; + + case CTL_SMC_LEDB: + val &= 0x07; // Restrict to 3 ls bits + lp->rpc_cur_mode &= ~(word)(0x07<rpc_cur_mode |= (word)(val<rpc_cur_mode); + break; + + case CTL_SMC_MIIOP: + // Update the Internal PHY block + smc_modify_regbit(1, ioaddr, CONFIG_REG, + CONFIG_EXT_PHY, val); + break; + +#ifdef SMC_DEBUG + case CTL_SMC_REG_BSR: // Bank Select + smc_modify_reg(0, ioaddr, BSR_REG, val); + break; + + case CTL_SMC_REG_TCR: // Transmit Control + smc_modify_reg(0, ioaddr, TCR_REG, val); + break; + + case CTL_SMC_REG_ESR: // EPH Status + smc_modify_reg(0, ioaddr, EPH_STATUS_REG, val); + break; + + case CTL_SMC_REG_RCR: // Receive Control + smc_modify_reg(0, ioaddr, RCR_REG, val); + break; + + case CTL_SMC_REG_CTRR: // Counter + smc_modify_reg(0, ioaddr, COUNTER_REG, val); + break; + + case CTL_SMC_REG_MIR: // Memory Information + smc_modify_reg(0, ioaddr, MIR_REG, val); + break; + + case CTL_SMC_REG_RPCR: // Receive/Phy Control + smc_modify_reg(0, ioaddr, RPC_REG, val); + break; + + case CTL_SMC_REG_CFGR: // Configuration + smc_modify_reg(1, ioaddr, CONFIG_REG, val); + break; + + case CTL_SMC_REG_BAR: // Base Address + smc_modify_reg(1, ioaddr, BASE_REG, val); + break; + + case CTL_SMC_REG_IAR0: // Individual Address + smc_modify_reg(1, ioaddr, ADDR0_REG, val); + break; + + case CTL_SMC_REG_IAR1: // Individual Address + smc_modify_reg(1, ioaddr, ADDR1_REG, val); + break; + + case CTL_SMC_REG_IAR2: // Individual Address + smc_modify_reg(1, ioaddr, ADDR2_REG, val); + break; + + case CTL_SMC_REG_GPR: // General Purpose + smc_modify_reg(1, ioaddr, GP_REG, val); + break; + + case CTL_SMC_REG_CTLR: // Control + smc_modify_reg(1, ioaddr, CTL_REG, val); + break; + + case CTL_SMC_REG_MCR: // MMU Command + smc_modify_reg(2, ioaddr, MMU_CMD_REG, val); + break; + + case CTL_SMC_REG_PNR: // Packet Number + smc_modify_reg(2, ioaddr, PN_REG, val); + break; + + case CTL_SMC_REG_FPR: // Allocation Result/FIFO Ports + smc_modify_reg(2, ioaddr, RXFIFO_REG, val); + break; + + case CTL_SMC_REG_PTR: // Pointer + smc_modify_reg(2, ioaddr, PTR_REG, val); + break; + + case CTL_SMC_REG_DR: // Data + smc_modify_reg(2, ioaddr, DATA_REG, val); + break; + + case CTL_SMC_REG_ISR: // Interrupt Status/Mask + smc_modify_reg(2, ioaddr, INT_REG, val); + break; + + case CTL_SMC_REG_MTR1: // Multicast Table Entry 1 + smc_modify_reg(3, ioaddr, MCAST_REG1, val); + break; + + case CTL_SMC_REG_MTR2: // Multicast Table Entry 2 + smc_modify_reg(3, ioaddr, MCAST_REG2, val); + break; + + case CTL_SMC_REG_MTR3: // Multicast Table Entry 3 + smc_modify_reg(3, ioaddr, MCAST_REG3, val); + break; + + case CTL_SMC_REG_MTR4: // Multicast Table Entry 4 + smc_modify_reg(3, ioaddr, MCAST_REG4, val); + break; + + case CTL_SMC_REG_MIIR: // Management Interface + smc_modify_reg(3, ioaddr, MII_REG, val); + break; + + case CTL_SMC_REG_REVR: // Revision + smc_modify_reg(3, ioaddr, REV_REG, val); + break; + + case CTL_SMC_REG_ERCVR: // Early RCV + smc_modify_reg(3, ioaddr, ERCV_REG, val); + break; + + case CTL_SMC_REG_EXTR: // External + smc_modify_reg(7, ioaddr, EXT_REG, val); + break; + + case CTL_SMC_PHY_CTRL: + smc_write_phy_register(ioaddr, lp->phyaddr, + PHY_CNTL_REG, val); + break; + + case CTL_SMC_PHY_STAT: + smc_write_phy_register(ioaddr, lp->phyaddr, + PHY_STAT_REG, val); + break; + + case CTL_SMC_PHY_ID1: + smc_write_phy_register(ioaddr, lp->phyaddr, + PHY_ID1_REG, val); + break; + + case CTL_SMC_PHY_ID2: + smc_write_phy_register(ioaddr, lp->phyaddr, + PHY_ID2_REG, val); + break; + + case CTL_SMC_PHY_ADC: + smc_write_phy_register(ioaddr, lp->phyaddr, + PHY_AD_REG, val); + break; + + case CTL_SMC_PHY_REMC: + smc_write_phy_register(ioaddr, lp->phyaddr, + PHY_RMT_REG, val); + break; + + case CTL_SMC_PHY_CFG1: + smc_write_phy_register(ioaddr, lp->phyaddr, + PHY_CFG1_REG, val); + break; + + case CTL_SMC_PHY_CFG2: + smc_write_phy_register(ioaddr, lp->phyaddr, + PHY_CFG2_REG, val); + break; + + case CTL_SMC_PHY_INT: + smc_write_phy_register(ioaddr, lp->phyaddr, + PHY_INT_REG, val); + break; + + case CTL_SMC_PHY_MASK: + smc_write_phy_register(ioaddr, lp->phyaddr, + PHY_MASK_REG, val); + break; + +#endif // SMC_DEBUG + + default: + // Just ignore unsupported parameters + break; + } // end switch + + } // end if + + return ret; +} + +/*------------------------------------------------------------ + . Sysctl registration function for all parameters (files) + .-------------------------------------------------------------*/ +static void smc_sysctl_register(struct net_device *dev) +{ + struct smc_local *lp = (struct smc_local *)dev->priv; + static int ctl_name = CTL_SMC; + ctl_table* ct; + int i; + + // Make sure the ctl_tables start out as all zeros + memset(lp->root_table, 0, sizeof lp->root_table); + memset(lp->eth_table, 0, sizeof lp->eth_table); + memset(lp->param_table, 0, sizeof lp->param_table); + + // Initialize the root table + ct = lp->root_table; + ct->ctl_name = CTL_DEV; + ct->procname = "dev"; + ct->maxlen = 0; + ct->mode = 0555; + ct->child = lp->eth_table; + // remaining fields are zero + + // Initialize the ethX table (this device's table) + ct = lp->eth_table; + ct->ctl_name = ctl_name++; // Must be unique + ct->procname = dev->name; + ct->maxlen = 0; + ct->mode = 0555; + ct->child = lp->param_table; + // remaining fields are zero + + // Initialize the parameter (files) table + // Make sure the last entry remains null + ct = lp->param_table; + for (i = 0; i < (CTL_SMC_LAST_ENTRY-1); ++i) + { + // Initialize fields common to all table entries + ct[i].proc_handler = smc_sysctl_handler; + ct[i].extra1 = (void*)dev; // Save our device pointer + ct[i].extra2 = (void*)lp; // Save our smc_local data pointer + } + + // INFO - this is our only string parameter + i = 0; + ct[i].proc_handler = proc_dostring; // use default handler + ct[i].ctl_name = CTL_SMC_INFO; + ct[i].procname = "info"; + ct[i].data = (void*)smc_info_string; + ct[i].maxlen = sizeof smc_info_string; + ct[i].mode = 0444; // Read only + + // SWVER + ++i; + ct[i].proc_handler = proc_dostring; // use default handler + ct[i].ctl_name = CTL_SMC_SWVER; + ct[i].procname = "swver"; + ct[i].data = (void*)version; + ct[i].maxlen = sizeof version; + ct[i].mode = 0444; // Read only + + // SWFDUP + ++i; + ct[i].ctl_name = CTL_SMC_SWFDUP; + ct[i].procname = "swfdup"; + ct[i].data = (void*)&(lp->ctl_swfdup); + ct[i].maxlen = sizeof lp->ctl_swfdup; + ct[i].mode = 0644; // Read by all, write by root + + // EPHLOOP + ++i; + ct[i].ctl_name = CTL_SMC_EPHLOOP; + ct[i].procname = "ephloop"; + ct[i].data = (void*)&(lp->ctl_ephloop); + ct[i].maxlen = sizeof lp->ctl_ephloop; + ct[i].mode = 0644; // Read by all, write by root + + // MIIOP + ++i; + ct[i].ctl_name = CTL_SMC_MIIOP; + ct[i].procname = "miiop"; + ct[i].data = (void*)&(lp->ctl_miiop); + ct[i].maxlen = sizeof lp->ctl_miiop; + ct[i].mode = 0644; // Read by all, write by root + + // AUTONEG + ++i; + ct[i].ctl_name = CTL_SMC_AUTONEG; + ct[i].procname = "autoneg"; + ct[i].data = (void*)&(lp->ctl_autoneg); + ct[i].maxlen = sizeof lp->ctl_autoneg; + ct[i].mode = 0644; // Read by all, write by root + + // RFDUPLX + ++i; + ct[i].ctl_name = CTL_SMC_RFDUPLX; + ct[i].procname = "rfduplx"; + ct[i].data = (void*)&(lp->ctl_rfduplx); + ct[i].maxlen = sizeof lp->ctl_rfduplx; + ct[i].mode = 0644; // Read by all, write by root + + // RSPEED + ++i; + ct[i].ctl_name = CTL_SMC_RSPEED; + ct[i].procname = "rspeed"; + ct[i].data = (void*)&(lp->ctl_rspeed); + ct[i].maxlen = sizeof lp->ctl_rspeed; + ct[i].mode = 0644; // Read by all, write by root + + // AFDUPLX + ++i; + ct[i].ctl_name = CTL_SMC_AFDUPLX; + ct[i].procname = "afduplx"; + ct[i].data = (void*)&(lp->ctl_afduplx); + ct[i].maxlen = sizeof lp->ctl_afduplx; + ct[i].mode = 0444; // Read only + + // ASPEED + ++i; + ct[i].ctl_name = CTL_SMC_ASPEED; + ct[i].procname = "aspeed"; + ct[i].data = (void*)&(lp->ctl_aspeed); + ct[i].maxlen = sizeof lp->ctl_aspeed; + ct[i].mode = 0444; // Read only + + // LNKFAIL + ++i; + ct[i].ctl_name = CTL_SMC_LNKFAIL; + ct[i].procname = "lnkfail"; + ct[i].data = (void*)&(lp->ctl_lnkfail); + ct[i].maxlen = sizeof lp->ctl_lnkfail; + ct[i].mode = 0444; // Read only + + // FORCOL + ++i; + ct[i].ctl_name = CTL_SMC_FORCOL; + ct[i].procname = "forcol"; + ct[i].data = (void*)&(lp->ctl_forcol); + ct[i].maxlen = sizeof lp->ctl_forcol; + ct[i].mode = 0644; // Read by all, write by root + + // FILTCAR + ++i; + ct[i].ctl_name = CTL_SMC_FILTCAR; + ct[i].procname = "filtcar"; + ct[i].data = (void*)&(lp->ctl_filtcar); + ct[i].maxlen = sizeof lp->ctl_filtcar; + ct[i].mode = 0644; // Read by all, write by root + + // FREEMEM + ++i; + ct[i].ctl_name = CTL_SMC_FREEMEM; + ct[i].procname = "freemem"; + ct[i].data = (void*)&(lp->ctl_freemem); + ct[i].maxlen = sizeof lp->ctl_freemem; + ct[i].mode = 0444; // Read only + + // TOTMEM + ++i; + ct[i].ctl_name = CTL_SMC_TOTMEM; + ct[i].procname = "totmem"; + ct[i].data = (void*)&(lp->ctl_totmem); + ct[i].maxlen = sizeof lp->ctl_totmem; + ct[i].mode = 0444; // Read only + + // LEDA + ++i; + ct[i].ctl_name = CTL_SMC_LEDA; + ct[i].procname = "leda"; + ct[i].data = (void*)&(lp->ctl_leda); + ct[i].maxlen = sizeof lp->ctl_leda; + ct[i].mode = 0644; // Read by all, write by root + + // LEDB + ++i; + ct[i].ctl_name = CTL_SMC_LEDB; + ct[i].procname = "ledb"; + ct[i].data = (void*)&(lp->ctl_ledb); + ct[i].maxlen = sizeof lp->ctl_ledb; + ct[i].mode = 0644; // Read by all, write by root + + // CHIPREV + ++i; + ct[i].ctl_name = CTL_SMC_CHIPREV; + ct[i].procname = "chiprev"; + ct[i].data = (void*)&(lp->ctl_chiprev); + ct[i].maxlen = sizeof lp->ctl_chiprev; + ct[i].mode = 0444; // Read only + +#ifdef SMC_DEBUG + // REG_BSR + ++i; + ct[i].ctl_name = CTL_SMC_REG_BSR; + ct[i].procname = "reg_bsr"; + ct[i].data = (void*)&(lp->ctl_reg_bsr); + ct[i].maxlen = sizeof lp->ctl_reg_bsr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_TCR + ++i; + ct[i].ctl_name = CTL_SMC_REG_TCR; + ct[i].procname = "reg_tcr"; + ct[i].data = (void*)&(lp->ctl_reg_tcr); + ct[i].maxlen = sizeof lp->ctl_reg_tcr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_ESR + ++i; + ct[i].ctl_name = CTL_SMC_REG_ESR; + ct[i].procname = "reg_esr"; + ct[i].data = (void*)&(lp->ctl_reg_esr); + ct[i].maxlen = sizeof lp->ctl_reg_esr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_RCR + ++i; + ct[i].ctl_name = CTL_SMC_REG_RCR; + ct[i].procname = "reg_rcr"; + ct[i].data = (void*)&(lp->ctl_reg_rcr); + ct[i].maxlen = sizeof lp->ctl_reg_rcr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_CTRR + ++i; + ct[i].ctl_name = CTL_SMC_REG_CTRR; + ct[i].procname = "reg_ctrr"; + ct[i].data = (void*)&(lp->ctl_reg_ctrr); + ct[i].maxlen = sizeof lp->ctl_reg_ctrr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_MIR + ++i; + ct[i].ctl_name = CTL_SMC_REG_MIR; + ct[i].procname = "reg_mir"; + ct[i].data = (void*)&(lp->ctl_reg_mir); + ct[i].maxlen = sizeof lp->ctl_reg_mir; + ct[i].mode = 0644; // Read by all, write by root + + // REG_RPCR + ++i; + ct[i].ctl_name = CTL_SMC_REG_RPCR; + ct[i].procname = "reg_rpcr"; + ct[i].data = (void*)&(lp->ctl_reg_rpcr); + ct[i].maxlen = sizeof lp->ctl_reg_rpcr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_CFGR + ++i; + ct[i].ctl_name = CTL_SMC_REG_CFGR; + ct[i].procname = "reg_cfgr"; + ct[i].data = (void*)&(lp->ctl_reg_cfgr); + ct[i].maxlen = sizeof lp->ctl_reg_cfgr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_BAR + ++i; + ct[i].ctl_name = CTL_SMC_REG_BAR; + ct[i].procname = "reg_bar"; + ct[i].data = (void*)&(lp->ctl_reg_bar); + ct[i].maxlen = sizeof lp->ctl_reg_bar; + ct[i].mode = 0644; // Read by all, write by root + + // REG_IAR0 + ++i; + ct[i].ctl_name = CTL_SMC_REG_IAR0; + ct[i].procname = "reg_iar0"; + ct[i].data = (void*)&(lp->ctl_reg_iar0); + ct[i].maxlen = sizeof lp->ctl_reg_iar0; + ct[i].mode = 0644; // Read by all, write by root + + // REG_IAR1 + ++i; + ct[i].ctl_name = CTL_SMC_REG_IAR1; + ct[i].procname = "reg_iar1"; + ct[i].data = (void*)&(lp->ctl_reg_iar1); + ct[i].maxlen = sizeof lp->ctl_reg_iar1; + ct[i].mode = 0644; // Read by all, write by root + + // REG_IAR2 + ++i; + ct[i].ctl_name = CTL_SMC_REG_IAR2; + ct[i].procname = "reg_iar2"; + ct[i].data = (void*)&(lp->ctl_reg_iar2); + ct[i].maxlen = sizeof lp->ctl_reg_iar2; + ct[i].mode = 0644; // Read by all, write by root + + // REG_GPR + ++i; + ct[i].ctl_name = CTL_SMC_REG_GPR; + ct[i].procname = "reg_gpr"; + ct[i].data = (void*)&(lp->ctl_reg_gpr); + ct[i].maxlen = sizeof lp->ctl_reg_gpr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_CTLR + ++i; + ct[i].ctl_name = CTL_SMC_REG_CTLR; + ct[i].procname = "reg_ctlr"; + ct[i].data = (void*)&(lp->ctl_reg_ctlr); + ct[i].maxlen = sizeof lp->ctl_reg_ctlr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_MCR + ++i; + ct[i].ctl_name = CTL_SMC_REG_MCR; + ct[i].procname = "reg_mcr"; + ct[i].data = (void*)&(lp->ctl_reg_mcr); + ct[i].maxlen = sizeof lp->ctl_reg_mcr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_PNR + ++i; + ct[i].ctl_name = CTL_SMC_REG_PNR; + ct[i].procname = "reg_pnr"; + ct[i].data = (void*)&(lp->ctl_reg_pnr); + ct[i].maxlen = sizeof lp->ctl_reg_pnr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_FPR + ++i; + ct[i].ctl_name = CTL_SMC_REG_FPR; + ct[i].procname = "reg_fpr"; + ct[i].data = (void*)&(lp->ctl_reg_fpr); + ct[i].maxlen = sizeof lp->ctl_reg_fpr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_PTR + ++i; + ct[i].ctl_name = CTL_SMC_REG_PTR; + ct[i].procname = "reg_ptr"; + ct[i].data = (void*)&(lp->ctl_reg_ptr); + ct[i].maxlen = sizeof lp->ctl_reg_ptr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_DR + ++i; + ct[i].ctl_name = CTL_SMC_REG_DR; + ct[i].procname = "reg_dr"; + ct[i].data = (void*)&(lp->ctl_reg_dr); + ct[i].maxlen = sizeof lp->ctl_reg_dr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_ISR + ++i; + ct[i].ctl_name = CTL_SMC_REG_ISR; + ct[i].procname = "reg_isr"; + ct[i].data = (void*)&(lp->ctl_reg_isr); + ct[i].maxlen = sizeof lp->ctl_reg_isr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_MTR1 + ++i; + ct[i].ctl_name = CTL_SMC_REG_MTR1; + ct[i].procname = "reg_mtr1"; + ct[i].data = (void*)&(lp->ctl_reg_mtr1); + ct[i].maxlen = sizeof lp->ctl_reg_mtr1; + ct[i].mode = 0644; // Read by all, write by root + + // REG_MTR2 + ++i; + ct[i].ctl_name = CTL_SMC_REG_MTR2; + ct[i].procname = "reg_mtr2"; + ct[i].data = (void*)&(lp->ctl_reg_mtr2); + ct[i].maxlen = sizeof lp->ctl_reg_mtr2; + ct[i].mode = 0644; // Read by all, write by root + + // REG_MTR3 + ++i; + ct[i].ctl_name = CTL_SMC_REG_MTR3; + ct[i].procname = "reg_mtr3"; + ct[i].data = (void*)&(lp->ctl_reg_mtr3); + ct[i].maxlen = sizeof lp->ctl_reg_mtr3; + ct[i].mode = 0644; // Read by all, write by root + + // REG_MTR4 + ++i; + ct[i].ctl_name = CTL_SMC_REG_MTR4; + ct[i].procname = "reg_mtr4"; + ct[i].data = (void*)&(lp->ctl_reg_mtr4); + ct[i].maxlen = sizeof lp->ctl_reg_mtr4; + ct[i].mode = 0644; // Read by all, write by root + + // REG_MIIR + ++i; + ct[i].ctl_name = CTL_SMC_REG_MIIR; + ct[i].procname = "reg_miir"; + ct[i].data = (void*)&(lp->ctl_reg_miir); + ct[i].maxlen = sizeof lp->ctl_reg_miir; + ct[i].mode = 0644; // Read by all, write by root + + // REG_REVR + ++i; + ct[i].ctl_name = CTL_SMC_REG_REVR; + ct[i].procname = "reg_revr"; + ct[i].data = (void*)&(lp->ctl_reg_revr); + ct[i].maxlen = sizeof lp->ctl_reg_revr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_ERCVR + ++i; + ct[i].ctl_name = CTL_SMC_REG_ERCVR; + ct[i].procname = "reg_ercvr"; + ct[i].data = (void*)&(lp->ctl_reg_ercvr); + ct[i].maxlen = sizeof lp->ctl_reg_ercvr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_EXTR + ++i; + ct[i].ctl_name = CTL_SMC_REG_EXTR; + ct[i].procname = "reg_extr"; + ct[i].data = (void*)&(lp->ctl_reg_extr); + ct[i].maxlen = sizeof lp->ctl_reg_extr; + ct[i].mode = 0644; // Read by all, write by root + + // PHY Control + ++i; + ct[i].ctl_name = CTL_SMC_PHY_CTRL; + ct[i].procname = "phy_ctrl"; + ct[i].data = (void*)&(lp->ctl_phy_ctrl); + ct[i].maxlen = sizeof lp->ctl_phy_ctrl; + ct[i].mode = 0644; // Read by all, write by root + + // PHY Status + ++i; + ct[i].ctl_name = CTL_SMC_PHY_STAT; + ct[i].procname = "phy_stat"; + ct[i].data = (void*)&(lp->ctl_phy_stat); + ct[i].maxlen = sizeof lp->ctl_phy_stat; + ct[i].mode = 0644; // Read by all, write by root + + // PHY ID1 + ++i; + ct[i].ctl_name = CTL_SMC_PHY_ID1; + ct[i].procname = "phy_id1"; + ct[i].data = (void*)&(lp->ctl_phy_id1); + ct[i].maxlen = sizeof lp->ctl_phy_id1; + ct[i].mode = 0644; // Read by all, write by root + + // PHY ID2 + ++i; + ct[i].ctl_name = CTL_SMC_PHY_ID2; + ct[i].procname = "phy_id2"; + ct[i].data = (void*)&(lp->ctl_phy_id2); + ct[i].maxlen = sizeof lp->ctl_phy_id2; + ct[i].mode = 0644; // Read by all, write by root + + // PHY Advertise Capabilities + ++i; + ct[i].ctl_name = CTL_SMC_PHY_ADC; + ct[i].procname = "phy_adc"; + ct[i].data = (void*)&(lp->ctl_phy_adc); + ct[i].maxlen = sizeof lp->ctl_phy_adc; + ct[i].mode = 0644; // Read by all, write by root + + // PHY Remote Capabilities + ++i; + ct[i].ctl_name = CTL_SMC_PHY_REMC; + ct[i].procname = "phy_remc"; + ct[i].data = (void*)&(lp->ctl_phy_remc); + ct[i].maxlen = sizeof lp->ctl_phy_remc; + ct[i].mode = 0644; // Read by all, write by root + + // PHY Configuration 1 + ++i; + ct[i].ctl_name = CTL_SMC_PHY_CFG1; + ct[i].procname = "phy_cfg1"; + ct[i].data = (void*)&(lp->ctl_phy_cfg1); + ct[i].maxlen = sizeof lp->ctl_phy_cfg1; + ct[i].mode = 0644; // Read by all, write by root + + // PHY Configuration 2 + ++i; + ct[i].ctl_name = CTL_SMC_PHY_CFG2; + ct[i].procname = "phy_cfg2"; + ct[i].data = (void*)&(lp->ctl_phy_cfg2); + ct[i].maxlen = sizeof lp->ctl_phy_cfg2; + ct[i].mode = 0644; // Read by all, write by root + + // PHY Interrupt/Status Output + ++i; + ct[i].ctl_name = CTL_SMC_PHY_INT; + ct[i].procname = "phy_int"; + ct[i].data = (void*)&(lp->ctl_phy_int); + ct[i].maxlen = sizeof lp->ctl_phy_int; + ct[i].mode = 0644; // Read by all, write by root + + // PHY Interrupt/Status Mask + ++i; + ct[i].ctl_name = CTL_SMC_PHY_MASK; + ct[i].procname = "phy_mask"; + ct[i].data = (void*)&(lp->ctl_phy_mask); + ct[i].maxlen = sizeof lp->ctl_phy_mask; + ct[i].mode = 0644; // Read by all, write by root + +#endif // SMC_DEBUG + + // Register /proc/sys/dev/ethX + lp->sysctl_header = register_sysctl_table(lp->root_table, 1); +} + + +/*------------------------------------------------------------ + . Sysctl unregistration when driver is closed + .-------------------------------------------------------------*/ +static void smc_sysctl_unregister(struct net_device *dev) +{ + struct smc_local *lp = (struct smc_local *)dev->priv; + + unregister_sysctl_table(lp->sysctl_header); +} + +#endif /* endif CONFIG_SYSCTL */ + + +//---PHY CONTROL AND CONFIGURATION----------------------------------------- + +#if (SMC_DEBUG > 2 ) + +/*------------------------------------------------------------ + . Debugging function for viewing MII Management serial bitstream + .-------------------------------------------------------------*/ +static void smc_dump_mii_stream(byte* bits, int size) +{ + int i; + + printk("BIT#:"); + for (i = 0; i < size; ++i) + { + printk("%d", i%10); + } + + printk("\nMDOE:"); + for (i = 0; i < size; ++i) + { + if (bits[i] & MII_MDOE) + printk("1"); + else + printk("0"); + } + + printk("\nMDO :"); + for (i = 0; i < size; ++i) + { + if (bits[i] & MII_MDO) + printk("1"); + else + printk("0"); + } + + printk("\nMDI :"); + for (i = 0; i < size; ++i) + { + if (bits[i] & MII_MDI) + printk("1"); + else + printk("0"); + } + + printk("\n"); +} +#endif + +/*------------------------------------------------------------ + . Reads a register from the MII Management serial interface + .-------------------------------------------------------------*/ +static word smc_read_phy_register(int ioaddr, byte phyaddr, byte phyreg) +{ + int oldBank; + int i; + byte mask; + word mii_reg; + byte bits[64]; + int clk_idx = 0; + int input_idx; + word phydata; + + // 32 consecutive ones on MDO to establish sync + for (i = 0; i < 32; ++i) + bits[clk_idx++] = MII_MDOE | MII_MDO; + + // Start code <01> + bits[clk_idx++] = MII_MDOE; + bits[clk_idx++] = MII_MDOE | MII_MDO; + + // Read command <10> + bits[clk_idx++] = MII_MDOE | MII_MDO; + bits[clk_idx++] = MII_MDOE; + + // Output the PHY address, msb first + mask = (byte)0x10; + for (i = 0; i < 5; ++i) + { + if (phyaddr & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + // Shift to next lowest bit + mask >>= 1; + } + + // Output the phy register number, msb first + mask = (byte)0x10; + for (i = 0; i < 5; ++i) + { + if (phyreg & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + // Shift to next lowest bit + mask >>= 1; + } + + // Tristate and turnaround (2 bit times) + bits[clk_idx++] = 0; + //bits[clk_idx++] = 0; + + // Input starts at this bit time + input_idx = clk_idx; + + // Will input 16 bits + for (i = 0; i < 16; ++i) + bits[clk_idx++] = 0; + + // Final clock bit + bits[clk_idx++] = 0; + + // Save the current bank + oldBank = inw( ioaddr+BANK_SELECT ); + + // Select bank 3 + SMC_SELECT_BANK( 3 ); + + // Get the current MII register value + mii_reg = inw( ioaddr+MII_REG ); + + // Turn off all MII Interface bits + mii_reg &= ~(MII_MDOE|MII_MCLK|MII_MDI|MII_MDO); + + // Clock all 64 cycles + for (i = 0; i < sizeof bits; ++i) + { + // Clock Low - output data + outw( mii_reg | bits[i], ioaddr+MII_REG ); + udelay(50); + + + // Clock Hi - input data + outw( mii_reg | bits[i] | MII_MCLK, ioaddr+MII_REG ); + udelay(50); + bits[i] |= inw( ioaddr+MII_REG ) & MII_MDI; + } + + // Return to idle state + // Set clock to low, data to low, and output tristated + outw( mii_reg, ioaddr+MII_REG ); + udelay(50); + + // Restore original bank select + SMC_SELECT_BANK( oldBank ); + + // Recover input data + phydata = 0; + for (i = 0; i < 16; ++i) + { + phydata <<= 1; + + if (bits[input_idx++] & MII_MDI) + phydata |= 0x0001; + } + +#if (SMC_DEBUG > 2 ) + printk("smc_read_phy_register(): phyaddr=%x,phyreg=%x,phydata=%x\n", + phyaddr, phyreg, phydata); + smc_dump_mii_stream(bits, sizeof bits); +#endif + + return(phydata); +} + + +/*------------------------------------------------------------ + . Writes a register to the MII Management serial interface + .-------------------------------------------------------------*/ +static void smc_write_phy_register(int ioaddr, + byte phyaddr, byte phyreg, word phydata) +{ + int oldBank; + int i; + word mask; + word mii_reg; + byte bits[65]; + int clk_idx = 0; + + // 32 consecutive ones on MDO to establish sync + for (i = 0; i < 32; ++i) + bits[clk_idx++] = MII_MDOE | MII_MDO; + + // Start code <01> + bits[clk_idx++] = MII_MDOE; + bits[clk_idx++] = MII_MDOE | MII_MDO; + + // Write command <01> + bits[clk_idx++] = MII_MDOE; + bits[clk_idx++] = MII_MDOE | MII_MDO; + + // Output the PHY address, msb first + mask = (byte)0x10; + for (i = 0; i < 5; ++i) + { + if (phyaddr & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + // Shift to next lowest bit + mask >>= 1; + } + + // Output the phy register number, msb first + mask = (byte)0x10; + for (i = 0; i < 5; ++i) + { + if (phyreg & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + // Shift to next lowest bit + mask >>= 1; + } + + // Tristate and turnaround (2 bit times) + bits[clk_idx++] = 0; + bits[clk_idx++] = 0; + + // Write out 16 bits of data, msb first + mask = 0x8000; + for (i = 0; i < 16; ++i) + { + if (phydata & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + // Shift to next lowest bit + mask >>= 1; + } + + // Final clock bit (tristate) + bits[clk_idx++] = 0; + + // Save the current bank + oldBank = inw( ioaddr+BANK_SELECT ); + + // Select bank 3 + SMC_SELECT_BANK( 3 ); + + // Get the current MII register value + mii_reg = inw( ioaddr+MII_REG ); + + // Turn off all MII Interface bits + mii_reg &= ~(MII_MDOE|MII_MCLK|MII_MDI|MII_MDO); + + // Clock all cycles + for (i = 0; i < sizeof bits; ++i) + { + // Clock Low - output data + outw( mii_reg | bits[i], ioaddr+MII_REG ); + udelay(50); + + + // Clock Hi - input data + outw( mii_reg | bits[i] | MII_MCLK, ioaddr+MII_REG ); + udelay(50); + bits[i] |= inw( ioaddr+MII_REG ) & MII_MDI; + } + + // Return to idle state + // Set clock to low, data to low, and output tristated + outw( mii_reg, ioaddr+MII_REG ); + udelay(50); + + // Restore original bank select + SMC_SELECT_BANK( oldBank ); + +#if (SMC_DEBUG > 2 ) + printk("smc_write_phy_register(): phyaddr=%x,phyreg=%x,phydata=%x\n", + phyaddr, phyreg, phydata); + smc_dump_mii_stream(bits, sizeof bits); +#endif +} + + +/*------------------------------------------------------------ + . Finds and reports the PHY address + .-------------------------------------------------------------*/ +static int smc_detect_phy(struct net_device* dev) +{ + struct smc_local *lp = (struct smc_local *)dev->priv; + int ioaddr = dev->base_addr; + word phy_id1 = 0; + word phy_id2 = 0; + int phyaddr; + int found = 0; + + PRINTK3("%s:smc_detect_phy()\n", dev->name); + + // Scan all 32 PHY addresses if necessary + for (phyaddr = 0; phyaddr < 32; ++phyaddr) + { + // Read the PHY identifiers + phy_id1 = smc_read_phy_register(ioaddr, phyaddr, PHY_ID1_REG); + phy_id2 = smc_read_phy_register(ioaddr, phyaddr, PHY_ID2_REG); + + PRINTK3("%s: phy_id1=%x, phy_id2=%x\n", + dev->name, phy_id1, phy_id2); + + // Make sure it is a valid identifier + if ((phy_id2 > 0x0000) && (phy_id2 < 0xffff) && + (phy_id1 > 0x0000) && (phy_id1 < 0xffff)) + { + if ((phy_id1 != 0x8000) && (phy_id2 != 0x8000)) + { + // Save the PHY's address + lp->phyaddr = phyaddr; + found = 1; + break; + } + } + } + + if (!found) + { + PRINTK("%s: No PHY found\n", dev->name); + return(0); + } + + // Set the PHY type + if ( (phy_id1 == 0x0016) && ((phy_id2 & 0xFFF0) == 0xF840 ) ) + { + lp->phytype = PHY_LAN83C183; + PRINTK("%s: PHY=LAN83C183 (LAN91C111 Internal)\n", dev->name); + } + + if ( (phy_id1 == 0x0282) && ((phy_id2 & 0xFFF0) == 0x1C50) ) + { + lp->phytype = PHY_LAN83C180; + PRINTK("%s: PHY=LAN83C180\n", dev->name); + } + + return(1); +} + +/*------------------------------------------------------------ + . Waits the specified number of milliseconds - kernel friendly + .-------------------------------------------------------------*/ +static void smc_wait_ms(unsigned int ms) +{ + + if (!in_interrupt()) + { + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(1 + ms * HZ / 1000); + } + else + { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1 + ms * HZ / 1000); + current->state = TASK_RUNNING; + } +} + +/*------------------------------------------------------------ + . Sets the PHY to a configuration as determined by the user + .-------------------------------------------------------------*/ +static int smc_phy_fixed(struct net_device* dev) +{ + int ioaddr = dev->base_addr; + struct smc_local *lp = (struct smc_local *)dev->priv; + byte phyaddr = lp->phyaddr; + word my_fixed_caps; + word cfg1; + + PRINTK3("%s:smc_phy_fixed()\n", dev->name); + + // Enter Link Disable state + cfg1 = smc_read_phy_register(ioaddr, phyaddr, PHY_CFG1_REG); + cfg1 |= PHY_CFG1_LNKDIS; + smc_write_phy_register(ioaddr, phyaddr, PHY_CFG1_REG, cfg1); + + // Set our fixed capabilities + // Disable auto-negotiation + my_fixed_caps = 0; + + if (lp->ctl_rfduplx) + my_fixed_caps |= PHY_CNTL_DPLX; + + if (lp->ctl_rspeed == 100) + my_fixed_caps |= PHY_CNTL_SPEED; + + // Write our capabilities to the phy control register + smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG, my_fixed_caps); + + // Re-Configure the Receive/Phy Control register + outw( lp->rpc_cur_mode, ioaddr + RPC_REG ); + + // Success + return(1); +} + + +/*------------------------------------------------------------ + . Configures the specified PHY using Autonegotiation. Calls + . smc_phy_fixed() if the user has requested a certain config. + .-------------------------------------------------------------*/ +static void smc_phy_configure(struct net_device* dev) +{ + int ioaddr = dev->base_addr; + struct smc_local *lp = (struct smc_local *)dev->priv; + int timeout; + byte phyaddr; + word my_phy_caps; // My PHY capabilities + word my_ad_caps; // My Advertised capabilities + word status; + int failed = 0; + + PRINTK3("%s:smc_program_phy()\n", dev->name); + + // Set the blocking flag + lp->autoneg_active = 1; + + // Find the address and type of our phy + if (!smc_detect_phy(dev)) + { + goto smc_phy_configure_exit; + } + + // Get the detected phy address + phyaddr = lp->phyaddr; + + // Reset the PHY, setting all other bits to zero + smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG, PHY_CNTL_RST); + + // Wait for the reset to complete, or time out + timeout = 6; // Wait up to 3 seconds + while (timeout--) + { + if (!(smc_read_phy_register(ioaddr, phyaddr, PHY_CNTL_REG) + & PHY_CNTL_RST)) + { + // reset complete + break; + } + + smc_wait_ms(500); // wait 500 millisecs + if (signal_pending(current)) // Exit anyway if signaled + { + PRINTK2("%s:PHY reset interrupted by signal\n", + dev->name); + timeout = 0; + break; + } + } + + if (timeout < 1) + { + PRINTK2("%s:PHY reset timed out\n", dev->name); + goto smc_phy_configure_exit; + } + + // Read PHY Register 18, Status Output + lp->lastPhy18 = smc_read_phy_register(ioaddr, phyaddr, PHY_INT_REG); + + // Enable PHY Interrupts (for register 18) + // Interrupts listed here are disabled + smc_write_phy_register(ioaddr, phyaddr, PHY_MASK_REG, + PHY_INT_LOSSSYNC | PHY_INT_CWRD | PHY_INT_SSD | + PHY_INT_ESD | PHY_INT_RPOL | PHY_INT_JAB | + PHY_INT_SPDDET | PHY_INT_DPLXDET); + + /* Configure the Receive/Phy Control register */ + SMC_SELECT_BANK( 0 ); + outw( lp->rpc_cur_mode, ioaddr + RPC_REG ); + + // Copy our capabilities from PHY_STAT_REG to PHY_AD_REG + my_phy_caps = smc_read_phy_register(ioaddr, phyaddr, PHY_STAT_REG); + my_ad_caps = PHY_AD_CSMA; // I am CSMA capable + + if (my_phy_caps & PHY_STAT_CAP_T4) + my_ad_caps |= PHY_AD_T4; + + if (my_phy_caps & PHY_STAT_CAP_TXF) + my_ad_caps |= PHY_AD_TX_FDX; + + if (my_phy_caps & PHY_STAT_CAP_TXH) + my_ad_caps |= PHY_AD_TX_HDX; + + if (my_phy_caps & PHY_STAT_CAP_TF) + my_ad_caps |= PHY_AD_10_FDX; + + if (my_phy_caps & PHY_STAT_CAP_TH) + my_ad_caps |= PHY_AD_10_HDX; + + // Disable capabilities not selected by our user + if (lp->ctl_rspeed != 100) + { + my_ad_caps &= ~(PHY_AD_T4|PHY_AD_TX_FDX|PHY_AD_TX_HDX); + } + + if (!lp->ctl_rfduplx) + { + my_ad_caps &= ~(PHY_AD_TX_FDX|PHY_AD_10_FDX); + } + + // Update our Auto-Neg Advertisement Register + smc_write_phy_register(ioaddr, phyaddr, PHY_AD_REG, my_ad_caps); + + PRINTK2("%s:phy caps=%x\n", dev->name, my_phy_caps); + PRINTK2("%s:phy advertised caps=%x\n", dev->name, my_ad_caps); + + // If the user requested no auto neg, then go set his request + if (!(lp->ctl_autoneg)) + { + smc_phy_fixed(dev); + goto smc_phy_configure_exit; + } + + // Restart auto-negotiation process in order to advertise my caps + smc_write_phy_register( ioaddr, phyaddr, PHY_CNTL_REG, + PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST ); + + // Wait for the auto-negotiation to complete. This may take from + // 2 to 3 seconds. + // Wait for the reset to complete, or time out + timeout = 20; // Wait up to 10 seconds + while (timeout--) + { + status = smc_read_phy_register(ioaddr, phyaddr, PHY_STAT_REG); + if (status & PHY_STAT_ANEG_ACK) + { + // auto-negotiate complete + break; + } + + smc_wait_ms(500); // wait 500 millisecs + if (signal_pending(current)) // Exit anyway if signaled + { + printk(KERN_DEBUG + "%s:PHY auto-negotiate interrupted by signal\n", + dev->name); + timeout = 0; + break; + } + PRINTK3("%s:phy phy_fixed\n", dev->name); + + + // Restart auto-negotiation if remote fault + if (status & PHY_STAT_REM_FLT) + { + PRINTK2("%s:PHY remote fault detected\n", dev->name); + + // Restart auto-negotiation + PRINTK2("%s:PHY restarting auto-negotiation\n", + dev->name); + smc_write_phy_register( ioaddr, phyaddr, PHY_CNTL_REG, + PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST | + PHY_CNTL_SPEED | PHY_CNTL_DPLX); + } + } + + if (timeout < 1) + { + printk(KERN_DEBUG "%s:PHY auto-negotiate timed out\n", + dev->name); + PRINTK2("%s:PHY auto-negotiate timed out\n", dev->name); + failed = 1; + } + + // Fail if we detected an auto-negotiate remote fault + if (status & PHY_STAT_REM_FLT) + { + printk(KERN_DEBUG "%s:PHY remote fault detected\n", dev->name); + PRINTK2("%s:PHY remote fault detected\n", dev->name); + failed = 1; + } + + // The smc_phy_interrupt() routine will be called to update lastPhy18 + + // Set our sysctl parameters to match auto-negotiation results + if ( lp->lastPhy18 & PHY_INT_SPDDET ) + { + PRINTK2("%s:PHY 100BaseT\n", dev->name); + lp->rpc_cur_mode |= RPC_SPEED; + } + else + { + PRINTK2("%s:PHY 10BaseT\n", dev->name); + lp->rpc_cur_mode &= ~RPC_SPEED; + } + + if ( lp->lastPhy18 & PHY_INT_DPLXDET ) + { + PRINTK2("%s:PHY Full Duplex\n", dev->name); + lp->rpc_cur_mode |= RPC_DPLX; + } + else + { + PRINTK2("%s:PHY Half Duplex\n", dev->name); + lp->rpc_cur_mode &= ~RPC_DPLX; + } + + // Re-Configure the Receive/Phy Control register + outw( lp->rpc_cur_mode, ioaddr + RPC_REG ); + + smc_phy_configure_exit: + + // Exit auto-negotiation + lp->autoneg_active = 0; +} + + + +/************************************************************************* + . smc_phy_interrupt + . + . Purpose: Handle interrupts relating to PHY register 18. This is + . called from the "hard" interrupt handler. + . + ************************************************************************/ +static void smc_phy_interrupt(struct net_device* dev) +{ + int ioaddr = dev->base_addr; + struct smc_local *lp = (struct smc_local *)dev->priv; + byte phyaddr = lp->phyaddr; + word phy18; + + PRINTK2("%s: smc_phy_interrupt\n", dev->name); + + while (1) { + // Read PHY Register 18, Status Output + phy18 = smc_read_phy_register(ioaddr, phyaddr, PHY_INT_REG); + + // Exit if not more changes + if (phy18 == lp->lastPhy18) + break; + +#if (SMC_DEBUG > 1 ) + + PRINTK2("%s: phy18=0x%x\n", dev->name, phy18); + PRINTK2("%s: lastPhy18=0x%x\n", dev->name, lp->lastPhy18); + + // Handle events + if ((phy18 & PHY_INT_LNKFAIL) != (lp->lastPhy18 & PHY_INT_LNKFAIL)) + { + PRINTK2("%s: PHY Link Fail=%x\n", dev->name, + phy18 & PHY_INT_LNKFAIL); + } + + if ((phy18 & PHY_INT_LOSSSYNC) != (lp->lastPhy18 & PHY_INT_LOSSSYNC)) + { + PRINTK2("%s: PHY LOSS SYNC=%x\n", dev->name, + phy18 & PHY_INT_LOSSSYNC); + } + + if ((phy18 & PHY_INT_CWRD) != (lp->lastPhy18 & PHY_INT_CWRD)) + { + PRINTK2("%s: PHY INVALID 4B5B code=%x\n", dev->name, + phy18 & PHY_INT_CWRD); + } + + if ((phy18 & PHY_INT_SSD) != (lp->lastPhy18 & PHY_INT_SSD)) + { + PRINTK2("%s: PHY No Start Of Stream=%x\n", dev->name, + phy18 & PHY_INT_SSD); + } + + if ((phy18 & PHY_INT_ESD) != (lp->lastPhy18 & PHY_INT_ESD)) + { + PRINTK2("%s: PHY No End Of Stream=%x\n", dev->name, + phy18 & PHY_INT_ESD); + } + + if ((phy18 & PHY_INT_RPOL) != (lp->lastPhy18 & PHY_INT_RPOL)) + { + PRINTK2("%s: PHY Reverse Polarity Detected=%x\n", dev->name, + phy18 & PHY_INT_RPOL); + } + + if ((phy18 & PHY_INT_JAB) != (lp->lastPhy18 & PHY_INT_JAB)) + { + PRINTK2("%s: PHY Jabber Detected=%x\n", dev->name, + phy18 & PHY_INT_JAB); + } + + if ((phy18 & PHY_INT_SPDDET) != (lp->lastPhy18 & PHY_INT_SPDDET)) + { + PRINTK2("%s: PHY Speed Detect=%x\n", dev->name, + phy18 & PHY_INT_SPDDET); + } + + if ((phy18 & PHY_INT_DPLXDET) != (lp->lastPhy18 & PHY_INT_DPLXDET)) + { + PRINTK2("%s: PHY Duplex Detect=%x\n", dev->name, + phy18 & PHY_INT_DPLXDET); + } +#endif + + // Update the last phy 18 variable + lp->lastPhy18 = phy18; + + } // end while +} + + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/drivers/smc91111.copying 2004-09-03 00:57:17.294410112 -0700 @@ -0,0 +1,352 @@ + + NOTE! This copyright does *not* cover user programs that use kernel + services by normal system calls - this is merely considered normal use + of the kernel, and does *not* fall under the heading of "derived work". + Also note that the GPL below is copyrighted by the Free Software + Foundation, but the instance of code that it refers to (the Linux + kernel) is copyrighted by me and others who actually wrote it. + + Linus Torvalds + +---------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/drivers/smc91111.h 2004-09-03 00:57:17.298409504 -0700 @@ -0,0 +1,570 @@ +/*------------------------------------------------------------------------ + . smc91111.h - macros for the LAN91C111 Ethernet Driver + . + . Copyright (C) 2001 Standard Microsystems Corporation (SMSC) + . Developed by Simple Network Magic Corporation (SNMC) + . Copyright (C) 1996 by Erik Stahlman (ES) + . + . This program is free software; you can redistribute it and/or modify + . it under the terms of the GNU General Public License as published by + . the Free Software Foundation; either version 2 of the License, or + . (at your option) any later version. + . + . This program is distributed in the hope that it will be useful, + . but WITHOUT ANY WARRANTY; without even the implied warranty of + . MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + . GNU General Public License for more details. + . + . You should have received a copy of the GNU General Public License + . along with this program; if not, write to the Free Software + . Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + . + . This file contains register information and access macros for + . the LAN91C111 single chip ethernet controller. It is a modified + . version of the smc9194.h file. + . + . Information contained in this file was obtained from the LAN91C111 + . manual from SMC. To get a copy, if you really want one, you can find + . information under www.smsc.com. + . + . Authors + . Erik Stahlman ( erik@vt.edu ) + . Daris A Nevil ( dnevil@snmc.com ) + . + . History + . 03/16/01 Daris A Nevil Modified for use with LAN91C111 device + . + ---------------------------------------------------------------------------*/ +#ifndef _SMC91111_H_ +#define _SMC91111_H_ + +/* I want some simple types */ + +typedef unsigned char byte; +typedef unsigned short word; +typedef unsigned long int dword; + + +/* Because of bank switching, the LAN91xxx uses only 16 I/O ports */ + +#define SMC_IO_EXTENT 16 + + +/*--------------------------------------------------------------- + . + . A description of the SMSC registers is probably in order here, + . although for details, the SMC datasheet is invaluable. + . + . Basically, the chip has 4 banks of registers ( 0 to 3 ), which + . are accessed by writing a number into the BANK_SELECT register + . ( I also use a SMC_SELECT_BANK macro for this ). + . + . The banks are configured so that for most purposes, bank 2 is all + . that is needed for simple run time tasks. + -----------------------------------------------------------------------*/ + +/* + . Bank Select Register: + . + . yyyy yyyy 0000 00xx + . xx = bank number + . yyyy yyyy = 0x33, for identification purposes. +*/ +#define BANK_SELECT 14 + +// Transmit Control Register +/* BANK 0 */ +#define TCR_REG 0x0000 // transmit control register +#define TCR_ENABLE 0x0001 // When 1 we can transmit +#define TCR_LOOP 0x0002 // Controls output pin LBK +#define TCR_FORCOL 0x0004 // When 1 will force a collision +#define TCR_PAD_EN 0x0080 // When 1 will pad tx frames < 64 bytes w/0 +#define TCR_NOCRC 0x0100 // When 1 will not append CRC to tx frames +#define TCR_MON_CSN 0x0400 // When 1 tx monitors carrier +#define TCR_FDUPLX 0x0800 // When 1 enables full duplex operation +#define TCR_STP_SQET 0x1000 // When 1 stops tx if Signal Quality Error +#define TCR_EPH_LOOP 0x2000 // When 1 enables EPH block loopback +#define TCR_SWFDUP 0x8000 // When 1 enables Switched Full Duplex mode + +#define TCR_CLEAR 0 /* do NOTHING */ +/* the default settings for the TCR register : */ +/* QUESTION: do I want to enable padding of short packets ? */ +#define TCR_DEFAULT TCR_ENABLE + + +// EPH Status Register +/* BANK 0 */ +#define EPH_STATUS_REG 0x0002 +#define ES_TX_SUC 0x0001 // Last TX was successful +#define ES_SNGL_COL 0x0002 // Single collision detected for last tx +#define ES_MUL_COL 0x0004 // Multiple collisions detected for last tx +#define ES_LTX_MULT 0x0008 // Last tx was a multicast +#define ES_16COL 0x0010 // 16 Collisions Reached +#define ES_SQET 0x0020 // Signal Quality Error Test +#define ES_LTXBRD 0x0040 // Last tx was a broadcast +#define ES_TXDEFR 0x0080 // Transmit Deferred +#define ES_LATCOL 0x0200 // Late collision detected on last tx +#define ES_LOSTCARR 0x0400 // Lost Carrier Sense +#define ES_EXC_DEF 0x0800 // Excessive Deferral +#define ES_CTR_ROL 0x1000 // Counter Roll Over indication +#define ES_LINK_OK 0x4000 // Driven by inverted value of nLNK pin +#define ES_TXUNRN 0x8000 // Tx Underrun + + +// Receive Control Register +/* BANK 0 */ +#define RCR_REG 0x0004 +#define RCR_RX_ABORT 0x0001 // Set if a rx frame was aborted +#define RCR_PRMS 0x0002 // Enable promiscuous mode +#define RCR_ALMUL 0x0004 // When set accepts all multicast frames +#define RCR_RXEN 0x0100 // IFF this is set, we can receive packets +#define RCR_STRIP_CRC 0x0200 // When set strips CRC from rx packets +#define RCR_ABORT_ENB 0x0200 // When set will abort rx on collision +#define RCR_FILT_CAR 0x0400 // When set filters leading 12 bit s of carrier +#define RCR_SOFTRST 0x8000 // resets the chip + +/* the normal settings for the RCR register : */ +#define RCR_DEFAULT (RCR_STRIP_CRC | RCR_RXEN) +#define RCR_CLEAR 0x0 // set it to a base state + +// Counter Register +/* BANK 0 */ +#define COUNTER_REG 0x0006 + +// Memory Information Register +/* BANK 0 */ +#define MIR_REG 0x0008 + +// Receive/Phy Control Register +/* BANK 0 */ +#define RPC_REG 0x000A +#define RPC_SPEED 0x2000 // When 1 PHY is in 100Mbps mode. +#define RPC_DPLX 0x1000 // When 1 PHY is in Full-Duplex Mode +#define RPC_ANEG 0x0800 // When 1 PHY is in Auto-Negotiate Mode +#define RPC_LSXA_SHFT 5 // Bits to shift LS2A,LS1A,LS0A to lsb +#define RPC_LSXB_SHFT 2 // Bits to get LS2B,LS1B,LS0B to lsb +#define RPC_LED_100_10 (0x00) // LED = 100Mbps OR's with 10Mbps link detect +#define RPC_LED_RES (0x01) // LED = Reserved +#define RPC_LED_10 (0x02) // LED = 10Mbps link detect +#define RPC_LED_FD (0x03) // LED = Full Duplex Mode +#define RPC_LED_TX_RX (0x04) // LED = TX or RX packet occurred +#define RPC_LED_100 (0x05) // LED = 100Mbps link dectect +#define RPC_LED_TX (0x06) // LED = TX packet occurred +#define RPC_LED_RX (0x07) // LED = RX packet occurred +#if defined(__m32r__) +#define RPC_DEFAULT (RPC_ANEG | (RPC_LED_TX_RX << RPC_LSXA_SHFT) | (RPC_LED_100_10 << RPC_LSXB_SHFT) | RPC_SPEED | RPC_DPLX) +#else +#define RPC_DEFAULT (RPC_ANEG | (RPC_LED_100 << RPC_LSXA_SHFT) | (RPC_LED_FD << RPC_LSXB_SHFT) | RPC_SPEED | RPC_DPLX) +#endif + +/* Bank 0 0x000C is reserved */ + +// Bank Select Register +/* All Banks */ +#define BSR_REG 0x000E + + +// Configuration Reg +/* BANK 1 */ +#define CONFIG_REG 0x0000 +#define CONFIG_EXT_PHY 0x0200 // 1=external MII, 0=internal Phy +#define CONFIG_GPCNTRL 0x0400 // Inverse value drives pin nCNTRL +#define CONFIG_NO_WAIT 0x1000 // When 1 no extra wait states on ISA bus +#define CONFIG_EPH_POWER_EN 0x8000 // When 0 EPH is placed into low power mode. + +// Default is powered-up, Internal Phy, Wait States, and pin nCNTRL=low +#define CONFIG_DEFAULT (CONFIG_EPH_POWER_EN) + + +// Base Address Register +/* BANK 1 */ +#define BASE_REG 0x0002 + + +// Individual Address Registers +/* BANK 1 */ +#define ADDR0_REG 0x0004 +#define ADDR1_REG 0x0006 +#define ADDR2_REG 0x0008 + + +// General Purpose Register +/* BANK 1 */ +#define GP_REG 0x000A + + +// Control Register +/* BANK 1 */ +#define CTL_REG 0x000C +#define CTL_RCV_BAD 0x4000 // When 1 bad CRC packets are received +#define CTL_AUTO_RELEASE 0x0800 // When 1 tx pages are released automatically +#define CTL_LE_ENABLE 0x0080 // When 1 enables Link Error interrupt +#define CTL_CR_ENABLE 0x0040 // When 1 enables Counter Rollover interrupt +#define CTL_TE_ENABLE 0x0020 // When 1 enables Transmit Error interrupt +#define CTL_EEPROM_SELECT 0x0004 // Controls EEPROM reload & store +#define CTL_RELOAD 0x0002 // When set reads EEPROM into registers +#define CTL_STORE 0x0001 // When set stores registers into EEPROM + + +// MMU Command Register +/* BANK 2 */ +#define MMU_CMD_REG 0x0000 +#define MC_BUSY 1 // When 1 the last release has not completed +#define MC_NOP (0<<5) // No Op +#define MC_ALLOC (1<<5) // OR with number of 256 byte packets +#define MC_RESET (2<<5) // Reset MMU to initial state +#define MC_REMOVE (3<<5) // Remove the current rx packet +#define MC_RELEASE (4<<5) // Remove and release the current rx packet +#define MC_FREEPKT (5<<5) // Release packet in PNR register +#define MC_ENQUEUE (6<<5) // Enqueue the packet for transmit +#define MC_RSTTXFIFO (7<<5) // Reset the TX FIFOs + + +// Packet Number Register +/* BANK 2 */ +#define PN_REG 0x0002 + + +// Allocation Result Register +/* BANK 2 */ +#define AR_REG 0x0003 +#define AR_FAILED 0x80 // Alocation Failed + + +// RX FIFO Ports Register +/* BANK 2 */ +#define RXFIFO_REG 0x0004 // Must be read as a word +#define RXFIFO_REMPTY 0x8000 // RX FIFO Empty + + +// TX FIFO Ports Register +/* BANK 2 */ +#define TXFIFO_REG RXFIFO_REG // Must be read as a word +#define TXFIFO_TEMPTY 0x80 // TX FIFO Empty + + +// Pointer Register +/* BANK 2 */ +#define PTR_REG 0x0006 +#define PTR_RCV 0x8000 // 1=Receive area, 0=Transmit area +#define PTR_AUTOINC 0x4000 // Auto increment the pointer on each access +#define PTR_READ 0x2000 // When 1 the operation is a read + + +// Data Register +/* BANK 2 */ +#define DATA_REG 0x0008 + + +// Interrupt Status/Acknowledge Register +/* BANK 2 */ +#define INT_REG 0x000C + + +// Interrupt Mask Register +/* BANK 2 */ +#define IM_REG 0x000D +#define IM_MDINT 0x80 // PHY MI Register 18 Interrupt +#define IM_ERCV_INT 0x40 // Early Receive Interrupt +#define IM_EPH_INT 0x20 // Set by Etheret Protocol Handler section +#define IM_RX_OVRN_INT 0x10 // Set by Receiver Overruns +#define IM_ALLOC_INT 0x08 // Set when allocation request is completed +#define IM_TX_EMPTY_INT 0x04 // Set if the TX FIFO goes empty +#define IM_TX_INT 0x02 // Transmit Interrrupt +#define IM_RCV_INT 0x01 // Receive Interrupt + + +// Multicast Table Registers +/* BANK 3 */ +#define MCAST_REG1 0x0000 +#define MCAST_REG2 0x0002 +#define MCAST_REG3 0x0004 +#define MCAST_REG4 0x0006 + + +// Management Interface Register (MII) +/* BANK 3 */ +#define MII_REG 0x0008 +#define MII_MSK_CRS100 0x4000 // Disables CRS100 detection during tx half dup +#define MII_MDOE 0x0008 // MII Output Enable +#define MII_MCLK 0x0004 // MII Clock, pin MDCLK +#define MII_MDI 0x0002 // MII Input, pin MDI +#define MII_MDO 0x0001 // MII Output, pin MDO + + +// Revision Register +/* BANK 3 */ +#define REV_REG 0x000A /* ( hi: chip id low: rev # ) */ + + +// Early RCV Register +/* BANK 3 */ +/* this is NOT on SMC9192 */ +#define ERCV_REG 0x000C +#define ERCV_RCV_DISCRD 0x0080 // When 1 discards a packet being received +#define ERCV_THRESHOLD 0x001F // ERCV Threshold Mask + +// External Register +/* BANK 7 */ +#define EXT_REG 0x0000 + + +#define CHIP_9192 3 +#define CHIP_9194 4 +#define CHIP_9195 5 +#define CHIP_9196 6 +#define CHIP_91100 7 +#define CHIP_91100FD 8 +#define CHIP_91111FD 9 + +static const char * chip_ids[ 15 ] = { + NULL, NULL, NULL, + /* 3 */ "SMC91C90/91C92", + /* 4 */ "SMC91C94", + /* 5 */ "SMC91C95", + /* 6 */ "SMC91C96", + /* 7 */ "SMC91C100", + /* 8 */ "SMC91C100FD", + /* 9 */ "SMC91C11xFD", + NULL, NULL, + NULL, NULL, NULL}; + +/* + . Transmit status bits +*/ +#define TS_SUCCESS 0x0001 +#define TS_LOSTCAR 0x0400 +#define TS_LATCOL 0x0200 +#define TS_16COL 0x0010 + +/* + . Receive status bits +*/ +#define RS_ALGNERR 0x8000 +#define RS_BRODCAST 0x4000 +#define RS_BADCRC 0x2000 +#define RS_ODDFRAME 0x1000 // bug: the LAN91C111 never sets this on receive +#define RS_TOOLONG 0x0800 +#define RS_TOOSHORT 0x0400 +#define RS_MULTICAST 0x0001 +#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT) + + +// PHY Types +enum { + PHY_LAN83C183 = 1, // LAN91C111 Internal PHY + PHY_LAN83C180 +}; + + +// PHY Register Addresses (LAN91C111 Internal PHY) + +// PHY Control Register +#define PHY_CNTL_REG 0x00 +#define PHY_CNTL_RST 0x8000 // 1=PHY Reset +#define PHY_CNTL_LPBK 0x4000 // 1=PHY Loopback +#define PHY_CNTL_SPEED 0x2000 // 1=100Mbps, 0=10Mpbs +#define PHY_CNTL_ANEG_EN 0x1000 // 1=Enable Auto negotiation +#define PHY_CNTL_PDN 0x0800 // 1=PHY Power Down mode +#define PHY_CNTL_MII_DIS 0x0400 // 1=MII 4 bit interface disabled +#define PHY_CNTL_ANEG_RST 0x0200 // 1=Reset Auto negotiate +#define PHY_CNTL_DPLX 0x0100 // 1=Full Duplex, 0=Half Duplex +#define PHY_CNTL_COLTST 0x0080 // 1= MII Colision Test + +// PHY Status Register +#define PHY_STAT_REG 0x01 +#define PHY_STAT_CAP_T4 0x8000 // 1=100Base-T4 capable +#define PHY_STAT_CAP_TXF 0x4000 // 1=100Base-X full duplex capable +#define PHY_STAT_CAP_TXH 0x2000 // 1=100Base-X half duplex capable +#define PHY_STAT_CAP_TF 0x1000 // 1=10Mbps full duplex capable +#define PHY_STAT_CAP_TH 0x0800 // 1=10Mbps half duplex capable +#define PHY_STAT_CAP_SUPR 0x0040 // 1=recv mgmt frames with not preamble +#define PHY_STAT_ANEG_ACK 0x0020 // 1=ANEG has completed +#define PHY_STAT_REM_FLT 0x0010 // 1=Remote Fault detected +#define PHY_STAT_CAP_ANEG 0x0008 // 1=Auto negotiate capable +#define PHY_STAT_LINK 0x0004 // 1=valid link +#define PHY_STAT_JAB 0x0002 // 1=10Mbps jabber condition +#define PHY_STAT_EXREG 0x0001 // 1=extended registers implemented + +// PHY Identifier Registers +#define PHY_ID1_REG 0x02 // PHY Identifier 1 +#define PHY_ID2_REG 0x03 // PHY Identifier 2 + +// PHY Auto-Negotiation Advertisement Register +#define PHY_AD_REG 0x04 +#define PHY_AD_NP 0x8000 // 1=PHY requests exchange of Next Page +#define PHY_AD_ACK 0x4000 // 1=got link code word from remote +#define PHY_AD_RF 0x2000 // 1=advertise remote fault +#define PHY_AD_T4 0x0200 // 1=PHY is capable of 100Base-T4 +#define PHY_AD_TX_FDX 0x0100 // 1=PHY is capable of 100Base-TX FDPLX +#define PHY_AD_TX_HDX 0x0080 // 1=PHY is capable of 100Base-TX HDPLX +#define PHY_AD_10_FDX 0x0040 // 1=PHY is capable of 10Base-T FDPLX +#define PHY_AD_10_HDX 0x0020 // 1=PHY is capable of 10Base-T HDPLX +#define PHY_AD_CSMA 0x0001 // 1=PHY is capable of 802.3 CMSA + +// PHY Auto-negotiation Remote End Capability Register +#define PHY_RMT_REG 0x05 +// Uses same bit definitions as PHY_AD_REG + +// PHY Configuration Register 1 +#define PHY_CFG1_REG 0x10 +#define PHY_CFG1_LNKDIS 0x8000 // 1=Rx Link Detect Function disabled +#define PHY_CFG1_XMTDIS 0x4000 // 1=TP Transmitter Disabled +#define PHY_CFG1_XMTPDN 0x2000 // 1=TP Transmitter Powered Down +#define PHY_CFG1_BYPSCR 0x0400 // 1=Bypass scrambler/descrambler +#define PHY_CFG1_UNSCDS 0x0200 // 1=Unscramble Idle Reception Disable +#define PHY_CFG1_EQLZR 0x0100 // 1=Rx Equalizer Disabled +#define PHY_CFG1_CABLE 0x0080 // 1=STP(150ohm), 0=UTP(100ohm) +#define PHY_CFG1_RLVL0 0x0040 // 1=Rx Squelch level reduced by 4.5db +#define PHY_CFG1_TLVL_SHIFT 2 // Transmit Output Level Adjust +#define PHY_CFG1_TLVL_MASK 0x003C +#define PHY_CFG1_TRF_MASK 0x0003 // Transmitter Rise/Fall time + + +// PHY Configuration Register 2 +#define PHY_CFG2_REG 0x11 +#define PHY_CFG2_APOLDIS 0x0020 // 1=Auto Polarity Correction disabled +#define PHY_CFG2_JABDIS 0x0010 // 1=Jabber disabled +#define PHY_CFG2_MREG 0x0008 // 1=Multiple register access (MII mgt) +#define PHY_CFG2_INTMDIO 0x0004 // 1=Interrupt signaled with MDIO pulseo + +// PHY Status Output (and Interrupt status) Register +#define PHY_INT_REG 0x12 // Status Output (Interrupt Status) +#define PHY_INT_INT 0x8000 // 1=bits have changed since last read +#define PHY_INT_LNKFAIL 0x4000 // 1=Link Not detected +#define PHY_INT_LOSSSYNC 0x2000 // 1=Descrambler has lost sync +#define PHY_INT_CWRD 0x1000 // 1=Invalid 4B5B code detected on rx +#define PHY_INT_SSD 0x0800 // 1=No Start Of Stream detected on rx +#define PHY_INT_ESD 0x0400 // 1=No End Of Stream detected on rx +#define PHY_INT_RPOL 0x0200 // 1=Reverse Polarity detected +#define PHY_INT_JAB 0x0100 // 1=Jabber detected +#define PHY_INT_SPDDET 0x0080 // 1=100Base-TX mode, 0=10Base-T mode +#define PHY_INT_DPLXDET 0x0040 // 1=Device in Full Duplex + +// PHY Interrupt/Status Mask Register +#define PHY_MASK_REG 0x13 // Interrupt Mask +// Uses the same bit definitions as PHY_INT_REG + + + +/*------------------------------------------------------------------------- + . I define some macros to make it easier to do somewhat common + . or slightly complicated, repeated tasks. + --------------------------------------------------------------------------*/ + +/* select a register bank, 0 to 3 */ + +#define SMC_SELECT_BANK(x) { outw( x, ioaddr + BANK_SELECT ); } + +/* this enables an interrupt in the interrupt mask register */ +#define SMC_ENABLE_INT(x) {\ + unsigned char mask;\ + SMC_SELECT_BANK(2);\ + mask = inb( ioaddr + IM_REG );\ + mask |= (x);\ + outb( mask, ioaddr + IM_REG ); \ +} + +/* this disables an interrupt from the interrupt mask register */ + +#define SMC_DISABLE_INT(x) {\ + unsigned char mask;\ + SMC_SELECT_BANK(2);\ + mask = inb( ioaddr + IM_REG );\ + mask &= ~(x);\ + outb( mask, ioaddr + IM_REG ); \ +} + +/*---------------------------------------------------------------------- + . Define the interrupts that I want to receive from the card + . + . I want: + . IM_EPH_INT, for nasty errors + . IM_RCV_INT, for happy received packets + . IM_RX_OVRN_INT, because I have to kick the receiver + . IM_MDINT, for PHY Register 18 Status Changes + --------------------------------------------------------------------------*/ +#define SMC_INTERRUPT_MASK (IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT | \ + IM_MDINT) + + +#ifdef CONFIG_SYSCTL + + +/* + * Declarations for the sysctl interface, which allows users the ability to + * control the finer aspects of the LAN91C111 chip. Since the smc + * module currently registers its sysctl table dynamically, the sysctl path + * for module FOO is /proc/sys/dev/ethX/FOO + */ +#define CTL_SMC (CTL_BUS+1389) // arbitrary and hopefully unused + +enum { + CTL_SMC_INFO = 1, // Sysctl files information + CTL_SMC_SWVER, // Driver Software Version Info + CTL_SMC_SWFDUP, // Switched Full Duplex Mode + CTL_SMC_EPHLOOP, // EPH Block Internal Loopback + CTL_SMC_MIIOP, // MII Operation + CTL_SMC_AUTONEG, // Auto-negotiate Mode + CTL_SMC_RFDUPLX, // Request Full Duplex Mode + CTL_SMC_RSPEED, // Request Speed Selection + CTL_SMC_AFDUPLX, // Actual Full Duplex Mode + CTL_SMC_ASPEED, // Actual Speed Selection + CTL_SMC_LNKFAIL, // Link Failed + CTL_SMC_FORCOL, // Force a Collision + CTL_SMC_FILTCAR, // Filter Carrier + CTL_SMC_FREEMEM, // Free Buffer Memory + CTL_SMC_TOTMEM, // Total Buffer Memory + CTL_SMC_LEDA, // Output of LED-A + CTL_SMC_LEDB, // Output of LED-B + CTL_SMC_CHIPREV, // LAN91C111 Chip Revision ID +#ifdef SMC_DEBUG + // Register access for debugging + CTL_SMC_REG_BSR, // Bank Select + CTL_SMC_REG_TCR, // Transmit Control + CTL_SMC_REG_ESR, // EPH Status + CTL_SMC_REG_RCR, // Receive Control + CTL_SMC_REG_CTRR, // Counter + CTL_SMC_REG_MIR, // Memory Information + CTL_SMC_REG_RPCR, // Receive/Phy Control + CTL_SMC_REG_CFGR, // Configuration + CTL_SMC_REG_BAR, // Base Address + CTL_SMC_REG_IAR0, // Individual Address 0 + CTL_SMC_REG_IAR1, // Individual Address 1 + CTL_SMC_REG_IAR2, // Individual Address 2 + CTL_SMC_REG_GPR, // General Purpose + CTL_SMC_REG_CTLR, // Control + CTL_SMC_REG_MCR, // MMU Command + CTL_SMC_REG_PNR, // Packet Number + CTL_SMC_REG_FPR, // FIFO Ports + CTL_SMC_REG_PTR, // Pointer + CTL_SMC_REG_DR, // Data + CTL_SMC_REG_ISR, // Interrupt Status + CTL_SMC_REG_MTR1, // Multicast Table Entry 1 + CTL_SMC_REG_MTR2, // Multicast Table Entry 2 + CTL_SMC_REG_MTR3, // Multicast Table Entry 3 + CTL_SMC_REG_MTR4, // Multicast Table Entry 4 + CTL_SMC_REG_MIIR, // Management Interface + CTL_SMC_REG_REVR, // Revision + CTL_SMC_REG_ERCVR, // Early RCV + CTL_SMC_REG_EXTR, // External + CTL_SMC_PHY_CTRL, // PHY Control + CTL_SMC_PHY_STAT, // PHY Status + CTL_SMC_PHY_ID1, // PHY ID1 + CTL_SMC_PHY_ID2, // PHY ID2 + CTL_SMC_PHY_ADC, // PHY Advertise Capability + CTL_SMC_PHY_REMC, // PHY Advertise Capability + CTL_SMC_PHY_CFG1, // PHY Configuration 1 + CTL_SMC_PHY_CFG2, // PHY Configuration 2 + CTL_SMC_PHY_INT, // PHY Interrupt/Status Output + CTL_SMC_PHY_MASK, // PHY Interrupt/Status Mask +#endif + // --------------------------------------------------- + CTL_SMC_LAST_ENTRY // Add new entries above the line +}; + +#endif // CONFIG_SYSCTL + +#endif /* _SMC_91111_H_ */ + + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/drivers/smc91111.readme.txt 2004-09-03 00:57:17.301409048 -0700 @@ -0,0 +1,561 @@ +SMSC LAN91C111 Driver +Revision 2.0 +9/24/01 +Copyright (C) 2001 Standard Microsystems Corporation (SMSC) +Copyright (C) 1996 by Erik Stahlman (ES) + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +This file contains the instructions, test methods, and caveats for the +smc91111.c driver. You may not be using the driver without reading this file. + +Driver Description: +=================== +This driver has been modified to work on kernel 2.4. And it also contains the +latest SMSC updates for the Odd Byte issue. Important thing to note about this +is that, this driver might not compile on kernel version older than 2.4. + Please visit the SMSC website (http://www.smsc.com) for latest updates on the +drivers. + + +Things to note about installation: +================================== + + 1. This is designed to be compiled and loaded as a module. While putting + it into your kernel should be possible, it has not yet been tested. + + 2. The driver will only load on recent kernels because of changes to + the network driver interface. I have compiled this under kernel 2.4. + + + 3. To compile, run 'make' . + + 4. Loading the driver: + + use: insmod smc91111.o + optional parameters: + io=0x### : your base address + irq=## : your irq + nowait=x : 0 for standard I/O access (IOCHRDY wait states) + 1 for fast access (no IOCHRDY wait states) + + 5. To allow automatic loading and intialization of the driver at boot-up + add the following line to /etc/modules.conf (in RedHat 7.0): + + alias eth0 smc91111 + options eth0 io=0x### irq=## + + See the man pages on module.conf. You must also be sure to copy the + smc91111.o file to the modules directory: + + su + cd /lib/modules/YOUR_CURRENT_KERNEL_VERSION/net + cp 'directory where the file resides'/smc91111.o . + chown root:root smc91111.o + chmod a+r smc91111.o + + Then you must update the module dependencies (as root): + + /sbin/depmod + + + +Advanced Features: +================== + +If your Linux Kernel was compiled with the configuration options CONFIG_PROC_FS +and CONFIG_SYSCTL enabled then the smc91111 driver provides several sysctl +files for accessing extended features. These files can be found in +/proc/sys/dev/ethX, where X is the interface number of the ethernet port. For +example, if your SMC91C111 card is the only one in the system then your sysctl +files will be located at /proc/sys/dev/eth0. + +The following lists the files supported by the driver. + + sysctl file Description +------------- -------------------------------------------------------------- + info Prints a list of supported files along with a terse + description of each one. + + swver Prints the software version information of the driver. + + autoneg When set to 1 enables auto negotiation mode. The LAN91C111 + supports auto negotiation per IEEE 802.3 Clause 28. When auto + negotiation is set the files "rspeed" and "rfduplx" (below) + limit what capabilities are broadcast to the remote end. + When "autoneg" is set to 0 the auto-negotiation feature + is disabled, and the speed and duplex can be controlled + directly and immediately with "rspeed" and "rfduplx". + You can change the value of this file by issuing the command + "echo 0 > autoneg", assuming that you are in the directory + /proc/sys/dev/ethX. + The default value of "autoneg" is 1. + + aspeed Reports the actual speed of the link. This file is read-only. + + afduplx Reports the actual duplex of the link. A value of 1 indicates + the link is currently operating in full duplex. This file + is read-only. + + rspeed Requested speed, controls the speed of the interface. The + behavior invoked by this file depends on the value of + the "autoneg" file. See the "autoneg" description above. A value + of 100 indicates 100Mpbs, and a speed of 10 indicates 10Mbps. + You cannot change the value of "rspeed" when "autoneg" is + set to 1. You can issue the following commands to restrict + the LAN91C111 to 10Mbps operation: + cd /proc/sys/dev/eth0 + echo 0 > autoneg + echo 10 > speed + echo 1 > autoneg + The default value of "speed" is 100. + + rfduplx Requested duplex, controls the duplex operation of the + interface. The behavior invoked by this file depends on the + value of the "autoneg" file. See the "autoneg" description + above. A value of 1 indicates full duplex, while a value of 0 + indicates half duplex operation. You cannot change the value + of "rfduplx" when "autoneg" is set to 1. The default value of + "rfduplx" is 1. + + lnkfail When 1 indicates the PHY link is in a failure state (i.e. + not connected to a valid LAN). A value of 0 indicates + normal operation. + + miiop When set to 1 selects an external PHY. This feature has not + yet been tested. The default value of "miiop" is 0. + + swfdup When set to 1 enables Switched Full Duplex Operation. This + is to be used only when miiop is set to 1 (according to the + LAN91C111 documentation). This feature has not yet been + tested. The default value of "swfdup" is 0. + + ephloop When set to 1 enables a loopback in the EPH block. This feature + has not yet been tested. The default value of "ephloop" is 0. + + forcol When set to 1 forces a collision. This feature has not yet been + tested. The default value of "forcol" is 0. + + filtcar When set to 1 the LAN91C111 filters the leading edge of carrier + sense for 12 bit times. This feature has not yet been tested. + The default value of "filtcar" is 0. + + freemem Reports the amount of buffer memory currently free, in bytes. + + totmem Reports the total amount of buffer memory contained in the + LAN91C111, in bytes. + + leda Reports and controls which line condition is reported on + LEDA. I tested this feature with the SMSC EVB111-ISA board. + LED A on this card is green and is labelled "100". This + file accepts values from 0 thru 7. + + Value Line Condition Reported + ----- ---------------------------------------------- + 0 Logical OR of 100Mbps and 10Mbps link detected + 1 Reserved + 2 10Mpbs link detected + 3 Full Duplex Mode Enabled + 4 Transmit or Receive packet occurred + 5 100Mbps Link Detected + 6 Transmit packet occurred + 7 Receive packet occurred + + The default value of "leda" is 5. + + ledb Reports and controls which line condition is reported on + LEDB. I tested this feature with the SMSC EVB111-ISA board. + LED B on this card is yellow and is labelled "FD". This + file accepts values from 0 thru 7 (see above). + The default value of "ledb" is 3. + + chiprev Reports the chip revision number of the LAN91C111. + + +Testing Methodology and Report: +=============================== + +I tested the smc91111.c driver using RedHat 7.1. The version I tested with +came with kernel version 2.4.2. I updated the gcc tools because of +known bugs in this version of the RedHat distribution. It should not have +made a difference though, as the module is actually compiled using the "kgcc" +compiler provided by RedHat 7.1 (you have to manually install the kgcc rpm +from the CDROM). + +I used two machines to perform tests. The first is an AST Bravo MS-T PRO 6200, +Model 2500C. It contains a PentiumPro w/integrated 256KB L2 cache, 64Mbytes of +EDO DRAM, and a 2.5GB EIDE hard drive. + +The second machine is a generic Pentium II running at 360MHz. It contains +64Mbytes of SDRAM, and a 30GB hard drive. I used a NetGear FA311 PCI Ethernet +card in this machine. I compiled and installed the fa311.c driver provided +with the card. + +I tested in two configurations. The first was in isolation, where the two +machines were connected to each other with a CAT5 crossover cable. The second +configuration connected both machines to a local area network running at +10Mbps. This lan containes three Windows machines and an Efficient Networks +SpeedStream router, connected to the Internet through an IDSL (today it is +served by Northpoint, tomorrow it is serviced by ????) + +Test Scenerio #1 +Testing in Isolated Network + +The PentiumPro machine with the EVB111-ISA card is named "LOCAL", with IP address 216.36.95.135, netmask 255.255.255.240. + +The PentiumII machine with the NetGear FA311 is named "REMOTE", with IP address of 216.36.95.134, netmask 255.255.255.240. + +1. Bring the interface up. + + The insmod and ifconfig programs must be executed as root. + + LOCAL: /sbin/insmod smc91111.o + Check /var/log/messages (at end) for results. You should see the I/O address + and IRQ assigned to the card by the auto probe function. + + LOCAL: /sbin/ifconfig eth0 216.36.95.135 netmask 255.255.255.240 + Verify that the interace is up by checking the results of ifconfig: + + LOCAL: /sbin/ifconfig + Verify that the Ethernet MAC address reported by ifconfig looks reasonable. + The card I tested with contained the address "00:80:0F:6A:00:00". Beware + of addresses with all zeros, all ones, or repeating bytes. + + Test Result: Passed + + +2. Ping Remote Side + + LOCAL: ping -c5 216.36.95.134 + Verify that 5 pings were returned. + + Test Result: Passed + + +3. Check sysctl variables + + LOCAL: cd /proc/sys/dev/eth0 + LOCAL: ls + Verify that the following files are shown: + afduplx autoneg ephloop forcol info ledb miiop rspeed swver + aspeed chiprev filtcar freemem leda lnkfail rfduplx swfdup totmem + + LOCAL: more autoneg + Verify that the value printed is 1. + + LOCAL: more rspeed + Verify that the value printed is 100. + + LOCAL: more rfduplx + Verify that the value printed is 1. + + LOCAL: more aspeed + Verify that the value printed is 100. + + LOCAL: more afduplx + Verify that the value printed is 1. + + LOCAL: more lnkfail + Verify that the value printed is 0. + + LOCAL: more totmem + Verify that the value printed is 8192. + + LOCAL: more freemem + Verify that the value printed is 8192. + + LOCAL: more leda + Verify that the value printed is 5 + + LOCAL: more ledb + Verify that the value printed is 3 + + Test Result: Passed + + +4. Check LEDs + + You may not be able to do this depending on the LEDs supported on your + card. + + Verify that the "100" LED is on, indicating a 100Mbps connection. + + Verify that the "FD" LED is on, indicating Full-Duplex connection. + + Test Result: Passed + + +5. Check Transport - use ftp to transport a large file across the ethernet + + Obtain a large file, preferably several megabytes in size. I used the + file XFree86-4.0.1-1.i386.rpm from Disk 1 of the RedHat 7.0 distribution + because it was 15Mbytes in size. + + REMOTE: ftp 216.36.95.135 + REMOTE: bin + REMOTE: put large_file + REMOTE: get large_file large_file_ftp + REMOTE: quit + REMOTE: diff large_file large_file_ftp + Verify that the transfer occurred correctly, and that the file was not + modified after transport. + + Test Result: Passed + + +6. Disable Auto-Negotiate, Force Half Duplex, 100Mbps + + The files in /proc/sys/dev/ethX can only be changed by root. + + LOCAL: echo 0 > autoneg + Wait 5 seconds. + LOCAL: echo 0 > rfduplx + Wait 5 seconds. + LOCAL: more rfduplx + Verify that the result printed is 0. + LOCAL: more afduplx + Verify that the result printed is 0. Verify that the "100" LED is on, + and the "FD" LED is off. + Perform Test #2 (Ping Remote Side) to verify connection. + Perform Test #5 (Check Transport). + + Test Result: Passed + + +7. Eanble Auto-Negotiate, Advertising Half Duplex, 100Mbps + + LOCAL: echo 1 > autoneg + Wait 5 seconds. + Verify that the "100" LED is on, and the "FD" LED is off. + LOCAL: more autoneg + Verify that the result printed is 1. + LOCAL: more afduplx + Verify that the result printed is 0. + LOCAL: more aspeed + Verify that the result printed is 100. + Perform Test #2 (Ping Remote Side) to verify connection. + LOCAL: more lnkfail + Verify that the result printed is 0. + Remove the ethernet cable from the RJ-45 connector. + LOCAL: more lnkfail + Verify that the result printed is 1. + Reinsert the ethernet cable into the RJ-45 connector. + LOCAL: more lnkfail + Verify that the result printed is 0. + LOCAL: more afduplx + Verify that the result printed is 0. + LOCAL: more aspeed + Verify that the result printed is 100. + Perform Test #2 (Ping Remote Side) to verify connection. + + Test Result: Passed + + +8. Force Half Duplex, 10Mbps + + LOCAL: echo 0 > autoneg + Wait 5 seconds. + LOCAL: echo 10 > rspeed + Wait 5 seconds. + LOCAL: more aspeed + Verify that the result printed is 10. Verify that the "100" LED is off, + and the "FD" LED is off. + LOCAL: more afduplx + Verify that the result printed is 0. + Perform Test #2 (Ping Remote Side) to verify connection. + Perform Test #5 (Check Transport). + + Test Result: Passed + + +9. Eanble Auto-Negotiate, Advertising Half Duplex, 10Mbps + + LOCAL: echo 1 > autoneg + Wait 5 seconds. + Verify that the "100" LED is off, and the "FD" LED is off. + LOCAL: more autoneg + Verify that the result printed is 1. + LOCAL: more afduplx + Verify that the result printed is 0. + LOCAL: more aspeed + Verify that the result printed is 10. + Perform Test #2 (Ping Remote Side) to verify connection. + LOCAL: more lnkfail + Verify that the result printed is 0. + Remove the ethernet cable from the RJ-45 connector. + LOCAL: more lnkfail + Verify that the result printed is 1. + Reinsert the ethernet cable into the RJ-45 connector. + LOCAL: more lnkfail + Verify that the result printed is 0. + LOCAL: more afduplx + Verify that the result printed is 0. + LOCAL: more aspeed + Verify that the result printed is 10. + Perform Test #2 (Ping Remote Side) to verify connection. + + Test Result: Passed + + +10. Force Full Duplex, 10Mbps + + LOCAL: echo 0 > autoneg + Wait 5 seconds. + LOCAL: echo 1 > rfduplx + Wait 5 seconds. + LOCAL: more afduplx + Verify that the result printed is 1. Verify that the "100" LED is off, + and the "FD" LED is on. + LOCAL: more aspeed + Verify that the result printed is 10. + Perform Test #2 (Ping Remote Side) to verify connection. + Perform Test #5 (Check Transport). + + Test Result: Passed + + +11. Eanble Auto-Negotiate, Advertising Full Duplex, 10Mbps + + LOCAL: echo 1 > autoneg + Wait 5 seconds. + Verify that the "100" LED is off, and the "FD" LED is on. + LOCAL: more autoneg + Verify that the result printed is 1. + LOCAL: more afduplx + Verify that the result printed is 1. + LOCAL: more aspeed + Verify that the result printed is 10. + Perform Test #2 (Ping Remote Side) to verify connection. + + Test Result: Passed + + + +12. Return to Full Duplex, 100Mbps + + LOCAL: echo 0 > autoneg + Wait 5 seconds. + LOCAL: echo 1 > rfduplx + Wait 5 seconds. + LOCAL: more afduplx + Verify that the result printed is 1. + LOCAL: echo 100 > rspeed + Wait 5 seconds. + LOCAL: more espeed + Verify that the result printed is 100. + Verify that the "100" LED is on, and the "FD" LED is on. + Perform Test #2 (Ping Remote Side) to verify connection. + LOCAL: echo 1 > autoneg + Wait 5 seconds. + Verify that the "100" LED is on, and the "FD" LED is on. + LOCAL: more autoneg + Verify that the result printed is 1. + LOCAL: more afduplx + Verify that the result printed is 1. + LOCAL: more aspeed + Verify that the result printed is 100. + Perform Test #2 (Ping Remote Side) to verify connection. + + Test Result: Passed + + + +Test Scenerio #2 +Testing in a Mixed-Mode 10Mbps LAN + +This section of testing was perform in a 10Mbps LAN with mixed 10Base2 and +10BaseT interfaces. The LAN was interconnected with a D-Link HUB, and a +connection to the Internet was secured through an Efficient Networks +SpeedStream router. The address of the router is 216.36.95.129. + + +13. Connect to LAN + + Connect both the LOCAL and REMOTE ethernet cards to the non-isolated LAN. + Verify that the "100" LED is off, and the "FD" LED is off. + LOCAL: more lnkfail + Verify that the result printed is 0. + LOCAL: more afduplx + Verify that the result printed is 0. + LOCAL: more aspeed + Verify that the result printed is 10. + Perform Test #2 (Ping Remote Side) to verify connection. + + Test Results: Passed + + +14. Test Promiscuous Mode + + LOCAL: /usr/sbin/tcpdump -p -i eth0 host 216.36.95.129 + REMOTE: ping -c5 216.36.95.129 + Verify that the LOCAL "tcpdump" program sees the pings to 216.36.95.129. + + Test Results: Passed + +15. Test Multicast Mode + + The "route" command must be executed as root. + LOCAL: route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0 + REMOTE: route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0 + LOCAL: /usr/sbin/tcpdump -p -i eth0 host 216.36.95.129 + REMOTE: ping 224.0.0.1 + Verify that the REMOTE machine gets at least one ICMP response from + 216.36.95.135. You will probably also see ICMP responses from other + Linux machines listening on the LAN. + Also, by inspecting the output of the LOCAL "tcpdump" you can see which + packets are Broadcast (B), Multicast (M), or Point-to-point (P). + + Test Results: Passed + +16. Test LED Control + + LOCAL: echo 6 > leda + LOCAL: echo 7 > ledb + LOCAL: more leda + Verify that the result printed is 6. + LOCAL: more ledb + Verify that the result printed is 7. + Perform Test #2 (Ping Remote Side) to verify connection. + Verify that the two LEDs toggle when packets are received and transmitted. + + Test Results: Passed + + + +How to obtain the latest version: +================================= + + http://www.smsc.com + + +Known bugs: +=========== + + 1. You cannot yet set the hardware address. + + 2. Hardware multicast filtering is not yet supported. + + 3. The driver does not support multiple cards in a system (yet) + + 4. One anomoly was observed during testing with an Athlon Thunderbird 1GHz system + during FTP uploads to a Pentim 4 1.3GHz system running Windows 2000 Adv Server, + where FTP transfer speed were observed to be 40-50% below normal. The cause + of the anomoly is unknown. + + +Contacting me: +============== + Pramod Bhardwaj, pramod.bhardwaj@smsc.com + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/Kconfig 2004-09-03 00:57:17.304408592 -0700 @@ -0,0 +1,621 @@ +# +# For a description of the syntax of this configuration file, +# see Documentation/kbuild/kconfig-language.txt. +# + +mainmenu "Linux Kernel Configuration" + +config M32R + bool + default y + +config SBUS + bool + +config UID16 + bool + default y + +config GENERIC_ISA_DMA + bool + default y + +source "init/Kconfig" + + +menu "Processor type and features" + +choice + prompt "Platform Type" + default PLAT_MAPPI + +config PLAT_MAPPI + bool "Mappi-I" + help + The Mappi-I is an FPGA board for SOC (System-On-a-Chip) prototyping. + You can operate a Linux system on this board by using an M32R + softmacro core, which is a fully-synthesizable functional model + described in Verilog-HDL. + + The Mappi-I board was the first platform, which had been used + to port and develop a Linux system for the M32R processor. + Currently, the Mappi-II, an heir to the Mappi-I, is available. + +config PLAT_USRV + bool "uServer" + +config PLAT_M32700UT + bool "M32700UT" + help + The M3T-M32700UT is an evaluation board based on uT-Engine + specification. This board has an M32700 (Chaos) evaluation chip. + You can say Y for SMP, because the M32700 is a single chip + multiprocessor. + +config PLAT_OPSPUT + bool "OPSPUT" + help + The OPSPUT is an evaluation board based on uT-Engine + specification. This board has a OPSP-REP chip. + +config PLAT_OAKS32R + bool "OAKS32R" + help + The OAKS32R is a tiny, inexpensive evaluation board. + Please note that if you say Y here and choose chip "M32102", + say N for MMU and select a no-MMU version kernel, otherwise + a kernel with MMU support will not work, because the M32102 + is a microcontroller for embedded systems and it has no MMU. + +config PLAT_MAPPI2 + bool "Mappi-II(M3A-ZA36/M3A-ZA52)" + +endchoice + +choice + prompt "Processor family" + default CHIP_M32700 + +config CHIP_M32700 + bool "M32700 (Chaos)" + +config CHIP_M32102 + bool "M32102" + +config CHIP_VDEC2 + bool "VDEC2" + +config CHIP_OPSP + bool "OPSP" + +endchoice + +config MMU + bool "Support for memory management hardware" + depends on CHIP_M32700 || CHIP_VDEC2 || CHIP_OPSP + default y + +config TLB_ENTRIES + int "TLB Entries" + depends on CHIP_M32700 || CHIP_VDEC2 || CHIP_OPSP + default 32 if CHIP_M32700 || CHIP_OPSP + default 16 if CHIP_VDEC2 + + +config ISA_M32R + bool + depends on CHIP_M32102 + default y + +config ISA_M32R2 + bool + depends on CHIP_M32700 || CHIP_VDEC2 || CHIP_OPSP + default y + +config ISA_DSP_LEVEL2 + bool + depends on CHIP_M32700 || CHIP_OPSP + default y + +config ISA_DUAL_ISSUE + bool + depends on CHIP_M32700 || CHIP_OPSP + default y + +config BUS_CLOCK + int "Bus Clock [Hz] (integer)" + default "70000000" if PLAT_MAPPI + default "25000000" if PLAT_USRV + default "50000000" if PLAT_M32700UT + default "50000000" if PLAT_OPSPUT + default "33333333" if PLAT_OAKS32R + default "20000000" if PLAT_MAPPI2 + +config TIMER_DIVIDE + int "Timer divider (integer)" + default "128" + +config CPU_LITTLE_ENDIAN + bool "Generate little endian code" + default n + +config MEMORY_START + hex "Physical memory start address (hex)" + default "08000000" if PLAT_MAPPI || PLAT_MAPPI2 + default "08000000" if PLAT_USRV + default "08000000" if PLAT_M32700UT + default "08000000" if PLAT_OPSPUT + default "01000000" if PLAT_OAKS32R + +config MEMORY_SIZE + hex "Physical memory size (hex)" + default "04000000" if PLAT_MAPPI || PLAT_MAPPI2 + default "02000000" if PLAT_USRV + default "01000000" if PLAT_M32700UT + default "01000000" if PLAT_OPSPUT + default "00800000" if PLAT_OAKS32R + +config NOHIGHMEM + bool + default y + +config DISCONTIGMEM + bool "Internal RAM Support" + depends on CHIP_M32700 || CHIP_M32102 || CHIP_VDEC2 || CHIP_OPSP + default y + +config IRAM_START + hex "Internal memory start address (hex)" + default "00f00000" + depends on (CHIP_M32700 || CHIP_M32102 || CHIP_VDEC2 || CHIP_OPSP) && DISCONTIGMEM + +config IRAM_SIZE + hex "Internal memory size (hex)" + depends on (CHIP_M32700 || CHIP_M32102 || CHIP_VDEC2 || CHIP_OPSP) && DISCONTIGMEM + default "00080000" if CHIP_M32700 + default "00010000" if CHIP_M32102 || CHIP_OPSP + default "00008000" if CHIP_VDEC2 + +# +# Define implied options from the CPU selection here +# + +config RWSEM_GENERIC_SPINLOCK + bool + depends on M32R + default y + +config RWSEM_XCHGADD_ALGORITHM + bool + default n + +config PREEMPT + bool "Preemptible Kernel" + help + This option reduces the latency of the kernel when reacting to + real-time or interactive events by allowing a low priority process to + be preempted even if it is in kernel mode executing a system call. + This allows applications to run more reliably even when the system is + under load. + + Say Y here if you are building a kernel for a desktop, embedded + or real-time system. Say N if you are unsure. + +config HAVE_DEC_LOCK + bool + depends on (SMP || PREEMPT) + default n + +config SMP + bool "Symmetric multi-processing support" + ---help--- + This enables support for systems with more than one CPU. If you have + a system with only one CPU, like most personal computers, say N. If + you have a system with more than one CPU, say Y. + + If you say N here, the kernel will run on single and multiprocessor + machines, but will use only one CPU of a multiprocessor machine. If + you say Y here, the kernel will run on many, but not all, + singleprocessor machines. On a singleprocessor machine, the kernel + will run faster if you say N here. + + Note that if you say Y here and choose architecture "586" or + "Pentium" under "Processor family", the kernel will not work on 486 + architectures. Similarly, multiprocessor kernels for the "PPro" + architecture may not work on all Pentium based boards. + + People using multiprocessor machines who say Y here should also say + Y to "Enhanced Real Time Clock Support", below. The "Advanced Power + Management" code will be disabled if you say Y here. + + See also the , + , , + and the SMP-HOWTO available at + . + + If you don't know what to do here, say N. + +config CHIP_M32700_TS1 + bool "Workaround code for the M32700 TS1 chip's bug" + depends on (CHIP_M32700 && SMP) + default n + +config NR_CPUS + int "Maximum number of CPUs (2-32)" + range 2 32 + depends on SMP + default "2" + help + This allows you to specify the maximum number of CPUs which this + kernel will support. The maximum supported value is 32 and the + minimum value which makes sense is 2. + + This is purely to save memory - each supported CPU adds + approximately eight kilobytes to the kernel image. + +# Common NUMA Features +config NUMA + bool "Numa Memory Allocation Support" + depends on SMP + default n + +source "arch/m32r/drivers/Kconfig" + +# turning this on wastes a bunch of space. +# Summit needs it only when NUMA is on +config BOOT_IOREMAP + bool + depends on NUMA + default n + +endmenu + + +menu "Power management options (ACPI, APM)" + +source kernel/power/Kconfig + +config APM + tristate "Advanced Power Management BIOS support" + depends on PM + ---help--- + APM is a BIOS specification for saving power using several different + techniques. This is mostly useful for battery powered laptops with + APM compliant BIOSes. If you say Y here, the system time will be + reset after a RESUME operation, the /proc/apm device will provide + battery status information, and user-space programs will receive + notification of APM "events" (e.g. battery status change). + + If you select "Y" here, you can disable actual use of the APM + BIOS by passing the "apm=off" option to the kernel at boot time. + + Note that the APM support is almost completely disabled for + machines with more than one CPU. + + In order to use APM, you will need supporting software. For location + and more information, read and the + Battery Powered Linux mini-HOWTO, available from + . + + This driver does not spin down disk drives (see the hdparm(8) + manpage ("man 8 hdparm") for that), and it doesn't turn off + VESA-compliant "green" monitors. + + This driver does not support the TI 4000M TravelMate and the ACER + 486/DX4/75 because they don't have compliant BIOSes. Many "green" + desktop machines also don't have compliant BIOSes, and this driver + may cause those machines to panic during the boot phase. + + Generally, if you don't have a battery in your machine, there isn't + much point in using this driver and you should say N. If you get + random kernel OOPSes or reboots that don't seem to be related to + anything, try disabling/enabling this option (or disabling/enabling + APM in your BIOS). + + Some other things you should try when experiencing seemingly random, + "weird" problems: + + 1) make sure that you have enough swap space and that it is + enabled. + 2) pass the "no-hlt" option to the kernel + 3) switch on floating point emulation in the kernel and pass + the "no387" option to the kernel + 4) pass the "floppy=nodma" option to the kernel + 5) pass the "mem=4M" option to the kernel (thereby disabling + all but the first 4 MB of RAM) + 6) make sure that the CPU is not over clocked. + 7) read the sig11 FAQ at + 8) disable the cache from your BIOS settings + 9) install a fan for the video card or exchange video RAM + 10) install a better fan for the CPU + 11) exchange RAM chips + 12) exchange the motherboard. + + To compile this driver as a module ( = code which can be inserted in + and removed from the running kernel whenever you want), say M here + and read . The module will be called + apm. + +config APM_IGNORE_USER_SUSPEND + bool "Ignore USER SUSPEND" + depends on APM + help + This option will ignore USER SUSPEND requests. On machines with a + compliant APM BIOS, you want to say N. However, on the NEC Versa M + series notebooks, it is necessary to say Y because of a BIOS bug. + +config APM_DO_ENABLE + bool "Enable PM at boot time" + depends on APM + ---help--- + Enable APM features at boot time. From page 36 of the APM BIOS + specification: "When disabled, the APM BIOS does not automatically + power manage devices, enter the Standby State, enter the Suspend + State, or take power saving steps in response to CPU Idle calls." + This driver will make CPU Idle calls when Linux is idle (unless this + feature is turned off -- see "Do CPU IDLE calls", below). This + should always save battery power, but more complicated APM features + will be dependent on your BIOS implementation. You may need to turn + this option off if your computer hangs at boot time when using APM + support, or if it beeps continuously instead of suspending. Turn + this off if you have a NEC UltraLite Versa 33/C or a Toshiba + T400CDT. This is off by default since most machines do fine without + this feature. + +config APM_CPU_IDLE + bool "Make CPU Idle calls when idle" + depends on APM + help + Enable calls to APM CPU Idle/CPU Busy inside the kernel's idle loop. + On some machines, this can activate improved power savings, such as + a slowed CPU clock rate, when the machine is idle. These idle calls + are made after the idle loop has run for some length of time (e.g., + 333 mS). On some machines, this will cause a hang at boot time or + whenever the CPU becomes idle. (On machines with more than one CPU, + this option does nothing.) + +config APM_DISPLAY_BLANK + bool "Enable console blanking using APM" + depends on APM + help + Enable console blanking using the APM. Some laptops can use this to + turn off the LCD backlight when the screen blanker of the Linux + virtual console blanks the screen. Note that this is only used by + the virtual console screen blanker, and won't turn off the backlight + when using the X Window system. This also doesn't have anything to + do with your VESA-compliant power-saving monitor. Further, this + option doesn't work for all laptops -- it might not turn off your + backlight at all, or it might print a lot of errors to the console, + especially if you are using gpm. + +config APM_RTC_IS_GMT + bool "RTC stores time in GMT" + depends on APM + help + Say Y here if your RTC (Real Time Clock a.k.a. hardware clock) + stores the time in GMT (Greenwich Mean Time). Say N if your RTC + stores localtime. + + It is in fact recommended to store GMT in your RTC, because then you + don't have to worry about daylight savings time changes. The only + reason not to use GMT in your RTC is if you also run a broken OS + that doesn't understand GMT. + +config APM_ALLOW_INTS + bool "Allow interrupts during APM BIOS calls" + depends on APM + help + Normally we disable external interrupts while we are making calls to + the APM BIOS as a measure to lessen the effects of a badly behaving + BIOS implementation. The BIOS should reenable interrupts if it + needs to. Unfortunately, some BIOSes do not -- especially those in + many of the newer IBM Thinkpads. If you experience hangs when you + suspend, try setting this to Y. Otherwise, say N. + +config APM_REAL_MODE_POWER_OFF + bool "Use real mode APM BIOS call to power off" + depends on APM + help + Use real mode APM BIOS calls to switch off the computer. This is + a work-around for a number of buggy BIOSes. Switch this option on if + your computer crashes instead of powering off properly. + +endmenu + + +menu "Bus options (PCI, PCMCIA, EISA, MCA, ISA)" + +config PCI + bool "PCI support" + default n + help + Find out whether you have a PCI motherboard. PCI is the name of a + bus system, i.e. the way the CPU talks to the other stuff inside + your box. Other bus systems are ISA, EISA, MicroChannel (MCA) or + VESA. If you have PCI, say Y, otherwise N. + + The PCI-HOWTO, available from + , contains valuable + information about which PCI hardware does work under Linux and which + doesn't. + +choice + prompt "PCI access mode" + depends on PCI + default PCI_GOANY + +config PCI_GOBIOS + bool "BIOS" + ---help--- + On PCI systems, the BIOS can be used to detect the PCI devices and + determine their configuration. However, some old PCI motherboards + have BIOS bugs and may crash if this is done. Also, some embedded + PCI-based systems don't have any BIOS at all. Linux can also try to + detect the PCI hardware directly without using the BIOS. + + With this option, you can specify how Linux should detect the PCI + devices. If you choose "BIOS", the BIOS will be used, if you choose + "Direct", the BIOS won't be used, and if you choose "Any", the + kernel will try the direct access method and falls back to the BIOS + if that doesn't work. If unsure, go with the default, which is + "Any". + +config PCI_GODIRECT + bool "Direct" + +config PCI_GOANY + bool "Any" + +endchoice + +config PCI_BIOS + bool + depends on PCI && (PCI_GOBIOS || PCI_GOANY) + default y + +config PCI_DIRECT + bool + depends on PCI && (PCI_GODIRECT || PCI_GOANY) + default y + +source "drivers/pci/Kconfig" + +config ISA + bool "ISA support" + help + Find out whether you have ISA slots on your motherboard. ISA is the + name of a bus system, i.e. the way the CPU talks to the other stuff + inside your box. Other bus systems are PCI, EISA, MicroChannel + (MCA) or VESA. ISA is an older system, now being displaced by PCI; + newer boards don't support it. If you have ISA, say Y, otherwise N. + +config EISA + bool "EISA support" + depends on ISA + ---help--- + The Extended Industry Standard Architecture (EISA) bus was + developed as an open alternative to the IBM MicroChannel bus. + + The EISA bus provided some of the features of the IBM MicroChannel + bus while maintaining backward compatibility with cards made for + the older ISA bus. The EISA bus saw limited use between 1988 and + 1995 when it was made obsolete by the PCI bus. + + Say Y here if you are building a kernel for an EISA-based machine. + + Otherwise, say N. + +source "drivers/eisa/Kconfig" + +source "drivers/pcmcia/Kconfig" + +source "drivers/pci/hotplug/Kconfig" + +endmenu + + +menu "Executable file formats" + +source "fs/Kconfig.binfmt" + +endmenu + +source "drivers/Kconfig" + +source "fs/Kconfig" + +source "arch/m32r/oprofile/Kconfig" + +menu "Kernel hacking" + +config DEBUG_KERNEL + bool "Kernel debugging" + help + Say Y here if you are developing drivers or trying to debug and + identify kernel problems. + +config DEBUG_STACKOVERFLOW + bool "Check for stack overflows" + depends on DEBUG_KERNEL + +config DEBUG_SLAB + bool "Debug memory allocations" + depends on DEBUG_KERNEL + help + Say Y here to have the kernel do limited verification on memory + allocation as well as poisoning memory on free to catch use of freed + memory. + +config DEBUG_IOVIRT + bool "Memory mapped I/O debugging" + depends on DEBUG_KERNEL + help + Say Y here to get warned whenever an attempt is made to do I/O on + obviously invalid addresses such as those generated when ioremap() + calls are forgotten. Memory mapped I/O will go through an extra + check to catch access to unmapped ISA addresses, an access method + that can still be used by old drivers that are being ported from + 2.0/2.2. + +config MAGIC_SYSRQ + bool "Magic SysRq key" + depends on DEBUG_KERNEL + help + If you say Y here, you will have some control over the system even + if the system crashes for example during kernel debugging (e.g., you + will be able to flush the buffer cache to disk, reboot the system + immediately or dump some status information). This is accomplished + by pressing various keys while holding SysRq (Alt+PrintScreen). It + also works on a serial console (on PC hardware at least), if you + send a BREAK and then within 5 seconds a command keypress. The + keys are documented in . Don't say Y + unless you really know what this hack does. + +config DEBUG_SPINLOCK + bool "Spinlock debugging" + depends on DEBUG_KERNEL + help + Say Y here and build SMP to catch missing spinlock initialization + and certain other kinds of spinlock errors commonly made. This is + best used in conjunction with the NMI watchdog so that spinlock + deadlocks are also debuggable. + +config DEBUG_PAGEALLOC + bool "Page alloc debugging" + depends on DEBUG_KERNEL + help + Unmap pages from the kernel linear mapping after free_pages(). + This results in a large slowdown, but helps to find certain types + of memory corruptions. + +config DEBUG_INFO + bool "Compile the kernel with debug info" + depends on DEBUG_KERNEL + help + If you say Y here the resulting kernel image will include + debugging info resulting in a larger kernel image. + Say Y here only if you plan to use gdb to debug the kernel. + If you don't debug the kernel, you can say N. + +config DEBUG_SPINLOCK_SLEEP + bool "Sleep-inside-spinlock checking" + help + If you say Y here, various routines which may sleep will become very + noisy if they are called with a spinlock held. + +config FRAME_POINTER + bool "Compile the kernel with frame pointers" + help + If you say Y here the resulting kernel image will be slightly larger + and slower, but it will give very useful debugging information. + If you don't debug the kernel, you can say N, but we may not be able + to solve problems without frame pointers. + +endmenu + +source "security/Kconfig" + +source "crypto/Kconfig" + +source "lib/Kconfig" + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/align.c 2004-09-03 00:57:17.306408288 -0700 @@ -0,0 +1,585 @@ +/* + * align.c - address exception handler for M32R + * + * Copyright (c) 2003 Hitoshi Yamamoto + */ + +#include +#include +#include + +static int get_reg(struct pt_regs *regs, int nr) +{ + int val; + + if (nr < 4) + val = *(unsigned long *)(®s->r0 + nr); + else if (nr < 7) + val = *(unsigned long *)(®s->r4 + (nr - 4)); + else if (nr < 13) + val = *(unsigned long *)(®s->r7 + (nr - 7)); + else + val = *(unsigned long *)(®s->fp + (nr - 13)); + + return val; +} + +static void set_reg(struct pt_regs *regs, int nr, int val) +{ + if (nr < 4) + *(unsigned long *)(®s->r0 + nr) = val; + else if (nr < 7) + *(unsigned long *)(®s->r4 + (nr - 4)) = val; + else if (nr < 13) + *(unsigned long *)(®s->r7 + (nr - 7)) = val; + else + *(unsigned long *)(®s->fp + (nr - 13)) = val; +} + +#define REG1(insn) (((insn) & 0x0f00) >> 8) +#define REG2(insn) ((insn) & 0x000f) +#define PSW_BC 0x100 + +/* O- instruction */ +#define ISA_LD1 0x20c0 /* ld Rdest, @Rsrc */ +#define ISA_LD2 0x20e0 /* ld Rdest, @Rsrc+ */ +#define ISA_LDH 0x20a0 /* ldh Rdest, @Rsrc */ +#define ISA_LDUH 0x20b0 /* lduh Rdest, @Rsrc */ +#define ISA_ST1 0x2040 /* st Rsrc1, @Rsrc2 */ +#define ISA_ST2 0x2060 /* st Rsrc1, @+Rsrc2 */ +#define ISA_ST3 0x2070 /* st Rsrc1, @-Rsrc2 */ +#define ISA_STH1 0x2020 /* sth Rsrc1, @Rsrc2 */ +#define ISA_STH2 0x2030 /* sth Rsrc1, @Rsrc2+ */ + +#ifdef CONFIG_ISA_DUAL_ISSUE + +/* OS instruction */ +#define ISA_ADD 0x00a0 /* add Rdest, Rsrc */ +#define ISA_ADDI 0x4000 /* addi Rdest, #imm8 */ +#define ISA_ADDX 0x0090 /* addx Rdest, Rsrc */ +#define ISA_AND 0x00c0 /* and Rdest, Rsrc */ +#define ISA_CMP 0x0040 /* cmp Rsrc1, Rsrc2 */ +#define ISA_CMPEQ 0x0060 /* cmpeq Rsrc1, Rsrc2 */ +#define ISA_CMPU 0x0050 /* cmpu Rsrc1, Rsrc2 */ +#define ISA_CMPZ 0x0070 /* cmpz Rsrc */ +#define ISA_LDI 0x6000 /* ldi Rdest, #imm8 */ +#define ISA_MV 0x1080 /* mv Rdest, Rsrc */ +#define ISA_NEG 0x0030 /* neg Rdest, Rsrc */ +#define ISA_NOP 0x7000 /* nop */ +#define ISA_NOT 0x00b0 /* not Rdest, Rsrc */ +#define ISA_OR 0x00e0 /* or Rdest, Rsrc */ +#define ISA_SUB 0x0020 /* sub Rdest, Rsrc */ +#define ISA_SUBX 0x0010 /* subx Rdest, Rsrc */ +#define ISA_XOR 0x00d0 /* xor Rdest, Rsrc */ + +/* -S instruction */ +#define ISA_MUL 0x1060 /* mul Rdest, Rsrc */ +#define ISA_MULLO_A0 0x3010 /* mullo Rsrc1, Rsrc2, A0 */ +#define ISA_MULLO_A1 0x3090 /* mullo Rsrc1, Rsrc2, A1 */ +#define ISA_MVFACMI_A0 0x50f2 /* mvfacmi Rdest, A0 */ +#define ISA_MVFACMI_A1 0x50f6 /* mvfacmi Rdest, A1 */ + +static int emu_addi(unsigned short insn, struct pt_regs *regs) +{ + char imm = (char)(insn & 0xff); + int dest = REG1(insn); + int val; + + val = get_reg(regs, dest); + val += imm; + set_reg(regs, dest, val); + + return 0; +} + +static int emu_ldi(unsigned short insn, struct pt_regs *regs) +{ + char imm = (char)(insn & 0xff); + + set_reg(regs, REG1(insn), (int)imm); + + return 0; +} + +static int emu_add(unsigned short insn, struct pt_regs *regs) +{ + int dest = REG1(insn); + int src = REG2(insn); + int val; + + val = get_reg(regs, dest); + val += get_reg(regs, src); + set_reg(regs, dest, val); + + return 0; +} + +static int emu_addx(unsigned short insn, struct pt_regs *regs) +{ + int dest = REG1(insn); + unsigned int val, tmp; + + val = regs->psw & PSW_BC ? 1 : 0; + tmp = get_reg(regs, dest); + val += tmp; + val += (unsigned int)get_reg(regs, REG2(insn)); + set_reg(regs, dest, val); + + /* C bit set */ + if (val < tmp) + regs->psw |= PSW_BC; + else + regs->psw &= ~(PSW_BC); + + return 0; +} + +static int emu_and(unsigned short insn, struct pt_regs *regs) +{ + int dest = REG1(insn); + int val; + + val = get_reg(regs, dest); + val &= get_reg(regs, REG2(insn)); + set_reg(regs, dest, val); + + return 0; +} + +static int emu_cmp(unsigned short insn, struct pt_regs *regs) +{ + if (get_reg(regs, REG1(insn)) < get_reg(regs, REG2(insn))) + regs->psw |= PSW_BC; + else + regs->psw &= ~(PSW_BC); + + return 0; +} + +static int emu_cmpeq(unsigned short insn, struct pt_regs *regs) +{ + if (get_reg(regs, REG1(insn)) == get_reg(regs, REG2(insn))) + regs->psw |= PSW_BC; + else + regs->psw &= ~(PSW_BC); + + return 0; +} + +static int emu_cmpu(unsigned short insn, struct pt_regs *regs) +{ + if ((unsigned int)get_reg(regs, REG1(insn)) + < (unsigned int)get_reg(regs, REG2(insn))) + regs->psw |= PSW_BC; + else + regs->psw &= ~(PSW_BC); + + return 0; +} + +static int emu_cmpz(unsigned short insn, struct pt_regs *regs) +{ + if (!get_reg(regs, REG2(insn))) + regs->psw |= PSW_BC; + else + regs->psw &= ~(PSW_BC); + + return 0; +} + +static int emu_mv(unsigned short insn, struct pt_regs *regs) +{ + int val; + + val = get_reg(regs, REG2(insn)); + set_reg(regs, REG1(insn), val); + + return 0; +} + +static int emu_neg(unsigned short insn, struct pt_regs *regs) +{ + int val; + + val = get_reg(regs, REG2(insn)); + set_reg(regs, REG1(insn), 0 - val); + + return 0; +} + +static int emu_not(unsigned short insn, struct pt_regs *regs) +{ + int val; + + val = get_reg(regs, REG2(insn)); + set_reg(regs, REG1(insn), ~val); + + return 0; +} + +static int emu_or(unsigned short insn, struct pt_regs *regs) +{ + int dest = REG1(insn); + int val; + + val = get_reg(regs, dest); + val |= get_reg(regs, REG2(insn)); + set_reg(regs, dest, val); + + return 0; +} + +static int emu_sub(unsigned short insn, struct pt_regs *regs) +{ + int dest = REG1(insn); + int val; + + val = get_reg(regs, dest); + val -= get_reg(regs, REG2(insn)); + set_reg(regs, dest, val); + + return 0; +} + +static int emu_subx(unsigned short insn, struct pt_regs *regs) +{ + int dest = REG1(insn); + unsigned int val, tmp; + + val = tmp = get_reg(regs, dest); + val -= (unsigned int)get_reg(regs, REG2(insn)); + val -= regs->psw & PSW_BC ? 1 : 0; + set_reg(regs, dest, val); + + /* C bit set */ + if (val > tmp) + regs->psw |= PSW_BC; + else + regs->psw &= ~(PSW_BC); + + return 0; +} + +static int emu_xor(unsigned short insn, struct pt_regs *regs) +{ + int dest = REG1(insn); + unsigned int val; + + val = (unsigned int)get_reg(regs, dest); + val ^= (unsigned int)get_reg(regs, REG2(insn)); + set_reg(regs, dest, val); + + return 0; +} + +static int emu_mul(unsigned short insn, struct pt_regs *regs) +{ + int dest = REG1(insn); + int reg1, reg2; + + reg1 = get_reg(regs, dest); + reg2 = get_reg(regs, REG2(insn)); + + __asm__ __volatile__ ( + "mul %0, %1; \n\t" + : "+r" (reg1) : "r" (reg2) + ); + + set_reg(regs, dest, reg1); + + return 0; +} + +static int emu_mullo_a0(unsigned short insn, struct pt_regs *regs) +{ + int reg1, reg2; + + reg1 = get_reg(regs, REG1(insn)); + reg2 = get_reg(regs, REG2(insn)); + + __asm__ __volatile__ ( + "mullo %0, %1, a0; \n\t" + "mvfachi %0, a0; \n\t" + "mvfaclo %1, a0; \n\t" + : "+r" (reg1), "+r" (reg2) + ); + + regs->acc0h = reg1; + regs->acc0l = reg2; + + return 0; +} + +static int emu_mullo_a1(unsigned short insn, struct pt_regs *regs) +{ + int reg1, reg2; + + reg1 = get_reg(regs, REG1(insn)); + reg2 = get_reg(regs, REG2(insn)); + + __asm__ __volatile__ ( + "mullo %0, %1, a0; \n\t" + "mvfachi %0, a0; \n\t" + "mvfaclo %1, a0; \n\t" + : "+r" (reg1), "+r" (reg2) + ); + + regs->acc1h = reg1; + regs->acc1l = reg2; + + return 0; +} + +static int emu_mvfacmi_a0(unsigned short insn, struct pt_regs *regs) +{ + unsigned long val; + + val = (regs->acc0h << 16) | (regs->acc0l >> 16); + set_reg(regs, REG1(insn), (int)val); + + return 0; +} + +static int emu_mvfacmi_a1(unsigned short insn, struct pt_regs *regs) +{ + unsigned long val; + + val = (regs->acc1h << 16) | (regs->acc1l >> 16); + set_reg(regs, REG1(insn), (int)val); + + return 0; +} + +static int emu_m32r2(unsigned short insn, struct pt_regs *regs) +{ + int res = -1; + + if ((insn & 0x7fff) == ISA_NOP) /* nop */ + return 0; + + switch(insn & 0x7000) { + case ISA_ADDI: /* addi Rdest, #imm8 */ + res = emu_addi(insn, regs); + break; + case ISA_LDI: /* ldi Rdest, #imm8 */ + res = emu_ldi(insn, regs); + break; + default: + break; + } + + if (!res) + return 0; + + switch(insn & 0x70f0) { + case ISA_ADD: /* add Rdest, Rsrc */ + res = emu_add(insn, regs); + break; + case ISA_ADDX: /* addx Rdest, Rsrc */ + res = emu_addx(insn, regs); + break; + case ISA_AND: /* and Rdest, Rsrc */ + res = emu_and(insn, regs); + break; + case ISA_CMP: /* cmp Rsrc1, Rsrc2 */ + res = emu_cmp(insn, regs); + break; + case ISA_CMPEQ: /* cmpeq Rsrc1, Rsrc2 */ + res = emu_cmpeq(insn, regs); + break; + case ISA_CMPU: /* cmpu Rsrc1, Rsrc2 */ + res = emu_cmpu(insn, regs); + break; + case ISA_CMPZ: /* cmpz Rsrc */ + res = emu_cmpz(insn, regs); + break; + case ISA_MV: /* mv Rdest, Rsrc */ + res = emu_mv(insn, regs); + break; + case ISA_NEG: /* neg Rdest, Rsrc */ + res = emu_neg(insn, regs); + break; + case ISA_NOT: /* not Rdest, Rsrc */ + res = emu_not(insn, regs); + break; + case ISA_OR: /* or Rdest, Rsrc */ + res = emu_or(insn, regs); + break; + case ISA_SUB: /* sub Rdest, Rsrc */ + res = emu_sub(insn, regs); + break; + case ISA_SUBX: /* subx Rdest, Rsrc */ + res = emu_subx(insn, regs); + break; + case ISA_XOR: /* xor Rdest, Rsrc */ + res = emu_xor(insn, regs); + break; + case ISA_MUL: /* mul Rdest, Rsrc */ + res = emu_mul(insn, regs); + break; + case ISA_MULLO_A0: /* mullo Rsrc1, Rsrc2 */ + res = emu_mullo_a0(insn, regs); + break; + case ISA_MULLO_A1: /* mullo Rsrc1, Rsrc2 */ + res = emu_mullo_a1(insn, regs); + break; + default: + break; + } + + if (!res) + return 0; + + switch(insn & 0x70ff) { + case ISA_MVFACMI_A0: /* mvfacmi Rdest */ + res = emu_mvfacmi_a0(insn, regs); + break; + case ISA_MVFACMI_A1: /* mvfacmi Rdest */ + res = emu_mvfacmi_a1(insn, regs); + break; + default: + break; + } + + return res; +} + +#endif /* CONFIG_ISA_DUAL_ISSUE */ + +/* + * ld : ?010 dest 1100 src + * 0010 dest 1110 src : ld Rdest, @Rsrc+ + * ldh : ?010 dest 1010 src + * lduh : ?010 dest 1011 src + * st : ?010 src1 0100 src2 + * 0010 src1 0110 src2 : st Rsrc1, @+Rsrc2 + * 0010 src1 0111 src2 : st Rsrc1, @-Rsrc2 + * sth : ?010 src1 0010 src2 + */ + +static int insn_check(unsigned long insn, struct pt_regs *regs, + unsigned char **ucp) +{ + int res = 0; + + /* + * 32bit insn + * ld Rdest, @(disp16, Rsrc) + * st Rdest, @(disp16, Rsrc) + */ + if (insn & 0x80000000) { /* 32bit insn */ + *ucp += (short)(insn & 0x0000ffff); + regs->bpc += 4; + } else { /* 16bit insn */ +#ifdef CONFIG_ISA_DUAL_ISSUE + /* parallel exec check */ + if (!(regs->bpc & 0x2) && insn & 0x8000) { + res = emu_m32r2((unsigned short)insn, regs); + regs->bpc += 4; + } else +#endif /* CONFIG_ISA_DUAL_ISSUE */ + regs->bpc += 2; + } + + return res; +} + +static int emu_ld(unsigned long insn32, struct pt_regs *regs) +{ + unsigned char *ucp; + unsigned long val; + unsigned short insn16; + int size, src; + + insn16 = insn32 >> 16; + src = REG2(insn16); + ucp = (unsigned char *)get_reg(regs, src); + + if (insn_check(insn32, regs, &ucp)) + return -1; + + size = insn16 & 0x0040 ? 4 : 2; + if (copy_from_user(&val, ucp, size)) + return -1; + + if (size == 2) + val >>= 16; + + /* ldh sign check */ + if ((insn16 & 0x00f0) == 0x00a0 && (val & 0x8000)) + val |= 0xffff0000; + + set_reg(regs, REG1(insn16), val); + + /* ld increment check */ + if ((insn16 & 0xf0f0) == ISA_LD2) /* ld Rdest, @Rsrc+ */ + set_reg(regs, src, (unsigned long)(ucp + 4)); + + return 0; +} + +static int emu_st(unsigned long insn32, struct pt_regs *regs) +{ + unsigned char *ucp; + unsigned long val; + unsigned short insn16; + int size, src2; + + insn16 = insn32 >> 16; + src2 = REG2(insn16); + + ucp = (unsigned char *)get_reg(regs, src2); + + if (insn_check(insn32, regs, &ucp)) + return -1; + + size = insn16 & 0x0040 ? 4 : 2; + val = get_reg(regs, REG1(insn16)); + if (size == 2) + val <<= 16; + + /* st inc/dec check */ + if ((insn16 & 0xf0e0) == 0x2060) { + if (insn16 & 0x0010) + ucp -= 4; + else + ucp += 4; + + set_reg(regs, src2, (unsigned long)ucp); + } + + if (copy_to_user(ucp, &val, size)) + return -1; + + /* sth inc check */ + if ((insn16 & 0xf0f0) == ISA_STH2) { + ucp += 2; + set_reg(regs, src2, (unsigned long)ucp); + } + + return 0; +} + +int handle_unaligned_access(unsigned long insn32, struct pt_regs *regs) +{ + unsigned short insn16; + int res; + + insn16 = insn32 >> 16; + + /* ld or st check */ + if ((insn16 & 0x7000) != 0x2000) + return -1; + + /* insn alignment check */ + if ((insn16 & 0x8000) && (regs->bpc & 3)) + return -1; + + if (insn16 & 0x0080) /* ld */ + res = emu_ld(insn32, regs); + else /* st */ + res = emu_st(insn32, regs); + + return res; +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/entry.S 2004-09-03 00:57:19.060141680 -0700 @@ -0,0 +1,997 @@ +/* + * linux/arch/m32r/kernel/entry.S + * + * Copyright (c) 2001, 2002 Hirokazu Takata, Hitoshi Yamamoto, H. Kondo + * Copyright (c) 2003 Hitoshi Yamamoto + * + * Taken from i386 version. + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* + * entry.S contains the system-call and fault low-level handling routines. + * This also contains the timer-interrupt handler, as well as all interrupts + * and faults that can result in a task-switch. + * + * NOTE: This code handles signal-recognition, which happens every time + * after a timer-interrupt and after each system call. + * + * Stack layout in 'ret_from_system_call': + * ptrace needs to have all regs on the stack. + * if the order here is changed, it needs to be + * updated in fork.c:copy_process, signal.c:do_signal, + * ptrace.c and ptrace.h + * + * M32Rx/M32R2 M32R + * @(sp) - r4 ditto + * @(0x04,sp) - r5 ditto + * @(0x08,sp) - r6 ditto + * @(0x0c,sp) - *pt_regs ditto + * @(0x10,sp) - r0 ditto + * @(0x14,sp) - r1 ditto + * @(0x18,sp) - r2 ditto + * @(0x1c,sp) - r3 ditto + * @(0x20,sp) - r7 ditto + * @(0x24,sp) - r8 ditto + * @(0x28,sp) - r9 ditto + * @(0x2c,sp) - r10 ditto + * @(0x30,sp) - r11 ditto + * @(0x34,sp) - r12 ditto + * @(0x38,sp) - syscall_nr ditto + * @(0x3c,sp) - acc0h @(0x3c,sp) - acch + * @(0x40,sp) - acc0l @(0x40,sp) - accl + * @(0x44,sp) - acc1h @(0x44,sp) - psw + * @(0x48,sp) - acc1l @(0x48,sp) - bpc + * @(0x4c,sp) - psw @(0x4c,sp) - bbpsw + * @(0x50,sp) - bpc @(0x50,sp) - bbpc + * @(0x54,sp) - bbpsw @(0x54,sp) - spu (cr3) + * @(0x58,sp) - bbpc @(0x58,sp) - fp (r13) + * @(0x5c,sp) - spu (cr3) @(0x5c,sp) - lr (r14) + * @(0x60,sp) - fp (r13) @(0x60,sp) - spi (cr12) + * @(0x64,sp) - lr (r14) @(0x64,sp) - orig_r0 + * @(0x68,sp) - spi (cr2) + * @(0x6c,sp) - orig_r0 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(CONFIG_MMU) +#define sys_madvise sys_ni_syscall +#define sys_readahead sys_ni_syscall +#define sys_mprotect sys_ni_syscall +#define sys_msync sys_ni_syscall +#define sys_mlock sys_ni_syscall +#define sys_munlock sys_ni_syscall +#define sys_mlockall sys_ni_syscall +#define sys_munlockall sys_ni_syscall +#define sys_mremap sys_ni_syscall +#define sys_mincore sys_ni_syscall +#endif /* CONFIG_MMU */ + +#define R4(reg) @reg +#define R5(reg) @(0x04,reg) +#define R6(reg) @(0x08,reg) +#define PTREGS(reg) @(0x0C,reg) +#define R0(reg) @(0x10,reg) +#define R1(reg) @(0x14,reg) +#define R2(reg) @(0x18,reg) +#define R3(reg) @(0x1C,reg) +#define R7(reg) @(0x20,reg) +#define R8(reg) @(0x24,reg) +#define R9(reg) @(0x28,reg) +#define R10(reg) @(0x2C,reg) +#define R11(reg) @(0x30,reg) +#define R12(reg) @(0x34,reg) +#define SYSCALL_NR(reg) @(0x38,reg) +#if defined(CONFIG_ISA_M32R2) && defined(CONFIG_ISA_DSP_LEVEL2) +#define ACC0H(reg) @(0x3C,reg) +#define ACC0L(reg) @(0x40,reg) +#define ACC1H(reg) @(0x44,reg) +#define ACC1L(reg) @(0x48,reg) +#define PSW(reg) @(0x4C,reg) +#define BPC(reg) @(0x50,reg) +#define BBPSW(reg) @(0x54,reg) +#define BBPC(reg) @(0x58,reg) +#define SPU(reg) @(0x5C,reg) +#define FP(reg) @(0x60,reg) /* FP = R13 */ +#define LR(reg) @(0x64,reg) +#define SP(reg) @(0x68,reg) +#define ORIG_R0(reg) @(0x6C,reg) +#elif defined(CONFIG_ISA_M32R2) || defined(CONFIG_ISA_M32R) +#define ACCH(reg) @(0x3C,reg) +#define ACCL(reg) @(0x40,reg) +#define PSW(reg) @(0x44,reg) +#define BPC(reg) @(0x48,reg) +#define BBPSW(reg) @(0x4C,reg) +#define BBPC(reg) @(0x50,reg) +#define SPU(reg) @(0x54,reg) +#define FP(reg) @(0x58,reg) /* FP = R13 */ +#define LR(reg) @(0x5C,reg) +#define SP(reg) @(0x60,reg) +#define ORIG_R0(reg) @(0x64,reg) +#else +#error unknown isa configuration +#endif + +CF_MASK = 0x00000001 +TF_MASK = 0x00000100 +IF_MASK = 0x00000200 +DF_MASK = 0x00000400 +NT_MASK = 0x00004000 +VM_MASK = 0x00020000 + +#ifdef CONFIG_PREEMPT +#define preempt_stop(x) CLI(x) +#else +#define preempt_stop(x) +#define resume_kernel restore_all +#endif + +ENTRY(ret_from_fork) + ld r0, @sp+ + bl schedule_tail + GET_THREAD_INFO(r8) + bra syscall_exit + +/* + * Return to user mode is not as complex as all this looks, + * but we want the default path for a system call return to + * go as quickly as possible which is why some of this is + * less clear than it otherwise should be. + */ + + ; userspace resumption stub bypassing syscall exit tracing + ALIGN +ret_from_exception: + preempt_stop(r4) +ret_from_intr: + ld r4, PSW(sp) +#ifdef CONFIG_ISA_M32R2 + and3 r4, r4, #0x8800 ; check BSM and BPM bits +#else + and3 r4, r4, #0x8000 ; check BSM bit +#endif + beqz r4, resume_kernel +ENTRY(resume_userspace) + CLI(r4) ; make sure we don't miss an interrupt + ; setting need_resched or sigpending + ; between sampling and the iret + GET_THREAD_INFO(r8) + ld r9, @(TI_FLAGS, r8) + and3 r4, r9, #_TIF_WORK_MASK ; is there any work to be done on + ; int/exception return? + bnez r4, work_pending + bra restore_all + +#ifdef CONFIG_PREEMPT +ENTRY(resume_kernel) + GET_THREAD_INFO(r8) + ld r9, @(TI_PRE_COUNT, r8) ; non-zero preempt_count ? + bnez r9, restore_all +need_resched: + ld r9, @(TI_FLAGS, r8) ; need_resched set ? + and3 r4, r9, #_TIF_NEED_RESCHED + beqz r4, restore_all + ld r4, PSW(sp) ; interrupts off (exception path) ? + and3 r4, r4, #0x4000 + beqz r4, restore_all + LDIMM (r4, PREEMPT_ACTIVE) + st r4, @(TI_PRE_COUNT, r8) + STI(r4) + bl schedule + ldi r4, #0 + st r4, @(TI_PRE_COUNT, r8) + CLI(r4) + bra need_resched +#endif + + ; system call handler stub +ENTRY(system_call) + SWITCH_TO_KERNEL_STACK + SAVE_ALL + STI(r4) ; Enable interrupt + st sp, PTREGS(sp) ; implicit pt_regs parameter + cmpui r7, #NR_syscalls + bnc syscall_badsys + st r7, SYSCALL_NR(sp) ; syscall_nr + ; system call tracing in operation + GET_THREAD_INFO(r8) + ld r9, @(TI_FLAGS, r8) + and3 r4, r9, #_TIF_SYSCALL_TRACE + bnez r4, syscall_trace_entry +syscall_call: + slli r7, #2 ; table jump for the system call + LDIMM (r4, sys_call_table) + add r7, r4 + ld r7, @r7 + jl r7 ; execute system call + st r0, R0(sp) ; save the return value +syscall_exit: + CLI(r4) ; make sure we don't miss an interrupt + ; setting need_resched or sigpending + ; between sampling and the iret + ld r9, @(TI_FLAGS, r8) + and3 r4, r9, #_TIF_ALLWORK_MASK ; current->work + bnez r4, syscall_exit_work +restore_all: + RESTORE_ALL + + # perform work that needs to be done immediately before resumption + # r9 : frags + ALIGN +work_pending: + and3 r4, r9, #_TIF_NEED_RESCHED + beqz r4, work_notifysig +work_resched: + bl schedule + CLI(r4) ; make sure we don't miss an interrupt + ; setting need_resched or sigpending + ; between sampling and the iret + ld r9, @(TI_FLAGS, r8) + and3 r4, r9, #_TIF_WORK_MASK ; is there any work to be done other + ; than syscall tracing? + beqz r4, restore_all + and3 r4, r4, #_TIF_NEED_RESCHED + bnez r4, work_resched + +work_notifysig: ; deal with pending signals and + ; notify-resume requests + mv r0, sp ; arg1 : struct pt_regs *regs + ldi r1, #0 ; arg2 : sigset_t *oldset + mv r2, r9 ; arg3 : __u32 thread_info_flags + bl do_notify_resume + bra restore_all + + ; perform syscall exit tracing + ALIGN +syscall_trace_entry: + ldi r4, #-ENOSYS + st r4, R0(sp) + bl do_syscall_trace + ld r0, ORIG_R0(sp) + ld r1, R1(sp) + ld r2, R2(sp) + ld r3, R3(sp) + ld r4, R4(sp) + ld r5, R5(sp) + ld r6, R6(sp) + ld r7, SYSCALL_NR(sp) + cmpui r7, #NR_syscalls + bc syscall_call + bra syscall_exit + + ; perform syscall exit tracing + ALIGN +syscall_exit_work: + ld r9, @(TI_FLAGS, r8) + and3 r4, r9, #_TIF_SYSCALL_TRACE + beqz r4, work_pending + STI(r4) ; could let do_syscall_trace() call + ; schedule() instead + bl do_syscall_trace + bra resume_userspace + + ALIGN +syscall_fault: + SAVE_ALL + GET_THREAD_INFO(r8) + ldi r4, #-EFAULT + st r4, R0(sp) + bra resume_userspace + + ALIGN +syscall_badsys: + ldi r4, #-ENOSYS + st r4, R0(sp) + bra resume_userspace + + .global eit_vector + + .equ ei_vec_table, eit_vector + 0x0200 + +/* + * EI handler routine + */ +ENTRY(ei_handler) +#if defined(CONFIG_CHIP_M32700) + SWITCH_TO_KERNEL_STACK + ; WORKAROUND: force to clear SM bit and use the kernel stack (SPI). +#endif + SAVE_ALL + mv r1, sp ; arg1(regs) +#if defined(CONFIG_CHIP_VDEC2) || defined(CONFIG_CHIP_XNUX2) \ + || defined(CONFIG_CHIP_M32700) || defined(CONFIG_CHIP_M32102) \ + || defined(CONFIG_CHIP_OPSP) + +; GET_ICU_STATUS; + seth r0, #shigh(M32R_ICU_ISTS_ADDR) + ld r0, @(low(M32R_ICU_ISTS_ADDR),r0) + st r0, @-sp +#if defined(CONFIG_SMP) + /* + * If IRQ == 0 --> Nothing to do, Not write IMASK + * If IRQ == IPI --> Do IPI handler, Not write IMASK + * If IRQ != 0, IPI --> Do do_IRQ(), Write IMASK + */ + slli r0, #4 + srli r0, #24 ; r0(irq_num<<2) + ;; IRQ exist check +#if defined(CONFIG_CHIP_M32700) + /* WORKAROUND: IMASK bug M32700-TS1, TS2 chip. */ + beqz r0, 3f ; if (!irq_num) goto exit +#else + beqz r0, 1f ; if (!irq_num) goto exit +#endif /* WORKAROUND */ + ;; IPI check + cmpi r0, #(M32R_IRQ_IPI0<<2) ; ISN < IPI0 check + bc 2f + cmpi r0, #((M32R_IRQ_IPI7+1)<<2) ; ISN > IPI7 check + bnc 2f + LDIMM (r2, ei_vec_table) + add r2, r0 + ld r2, @r2 + beqz r2, 1f ; if (no IPI handler) goto exit + mv r0, r1 ; arg0(regs) + jl r2 + .fillinsn +1: + addi sp, #4 + bra ret_to_intr +#if defined(CONFIG_CHIP_M32700) + /* WORKAROUND: IMASK bug M32700-TS1, TS2 chip. */ + .fillinsn +3: + ld24 r14, #0x00070000 + seth r0, #shigh(M32R_ICU_IMASK_ADDR) + st r14, @(low(M32R_ICU_IMASK_ADDR), r0) + addi sp, #4 + bra ret_to_intr +#endif /* WORKAROUND */ + ;; do_IRQ + .fillinsn +2: + srli r0, #2 +#if defined(CONFIG_PLAT_USRV) + add3 r2, r0, #-(M32R_IRQ_INT1) ; INT1# interrupt + bnez r2, 9f + ; read ICU status register of PLD + seth r0, #high(PLD_ICUISTS) + or3 r0, r0, #low(PLD_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + addi r0, #(M32700UT_PLD_IRQ_BASE) + .fillinsn +9: +#elif defined(CONFIG_PLAT_M32700UT) + add3 r2, r0, #-(M32R_IRQ_INT1) ; INT1# interrupt + bnez r2, check_int0 + ; read ICU status register of PLD + seth r0, #high(PLD_ICUISTS) + or3 r0, r0, #low(PLD_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + addi r0, #(M32700UT_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_int0: + add3 r2, r0, #-(M32R_IRQ_INT0) ; INT0# interrupt + bnez r2, check_int2 + ; read ICU status of LAN-board + seth r0, #high(M32700UT_LAN_ICUISTS) + or3 r0, r0, #low(M32700UT_LAN_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + add3 r0, r0, #(M32700UT_LAN_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_int2: + add3 r2, r0, #-(M32R_IRQ_INT2) ; INT2# interrupt + bnez r2, check_end + ; read ICU status of LCD-board + seth r0, #high(M32700UT_LCD_ICUISTS) + or3 r0, r0, #low(M32700UT_LCD_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + add3 r0, r0, #(M32700UT_LCD_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_end: +#elif defined(CONFIG_PLAT_OPSPUT) + add3 r2, r0, #-(M32R_IRQ_INT1) ; INT1# interrupt + bnez r2, check_int0 + ; read ICU status register of PLD + seth r0, #high(PLD_ICUISTS) + or3 r0, r0, #low(PLD_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + addi r0, #(OPSPUT_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_int0: + add3 r2, r0, #-(M32R_IRQ_INT0) ; INT0# interrupt + bnez r2, check_int2 + ; read ICU status of LAN-board + seth r0, #high(OPSPUT_LAN_ICUISTS) + or3 r0, r0, #low(OPSPUT_LAN_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + add3 r0, r0, #(OPSPUT_LAN_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_int2: + add3 r2, r0, #-(M32R_IRQ_INT2) ; INT2# interrupt + bnez r2, check_end + ; read ICU status of LCD-board + seth r0, #high(OPSPUT_LCD_ICUISTS) + or3 r0, r0, #low(OPSPUT_LCD_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + add3 r0, r0, #(OPSPUT_LCD_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_end: +#endif /* CONFIG_PLAT_OPSPUT */ + bl do_IRQ ; r0(irq), r1(regs) +#else /* not CONFIG_SMP */ + srli r0, #22 ; r0(irq) +#if defined(CONFIG_PLAT_USRV) + add3 r2, r0, #-(M32R_IRQ_INT1) ; INT1# interrupt + bnez r2, 1f + ; read ICU status register of PLD + seth r0, #high(PLD_ICUISTS) + or3 r0, r0, #low(PLD_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + addi r0, #(M32700UT_PLD_IRQ_BASE) + .fillinsn +1: +#elif defined(CONFIG_PLAT_M32700UT) + add3 r2, r0, #-(M32R_IRQ_INT1) ; INT1# interrupt + bnez r2, check_int0 + ; read ICU status register of PLD + seth r0, #high(PLD_ICUISTS) + or3 r0, r0, #low(PLD_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + addi r0, #(M32700UT_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_int0: + add3 r2, r0, #-(M32R_IRQ_INT0) ; INT0# interrupt + bnez r2, check_int2 + ; read ICU status of LAN-board + seth r0, #high(M32700UT_LAN_ICUISTS) + or3 r0, r0, #low(M32700UT_LAN_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + add3 r0, r0, #(M32700UT_LAN_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_int2: + add3 r2, r0, #-(M32R_IRQ_INT2) ; INT2# interrupt + bnez r2, check_end + ; read ICU status of LCD-board + seth r0, #high(M32700UT_LCD_ICUISTS) + or3 r0, r0, #low(M32700UT_LCD_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + add3 r0, r0, #(M32700UT_LCD_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_end: +#elif defined(CONFIG_PLAT_OPSPUT) + add3 r2, r0, #-(M32R_IRQ_INT1) ; INT1# interrupt + bnez r2, check_int0 + ; read ICU status register of PLD + seth r0, #high(PLD_ICUISTS) + or3 r0, r0, #low(PLD_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + addi r0, #(OPSPUT_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_int0: + add3 r2, r0, #-(M32R_IRQ_INT0) ; INT0# interrupt + bnez r2, check_int2 + ; read ICU status of LAN-board + seth r0, #high(OPSPUT_LAN_ICUISTS) + or3 r0, r0, #low(OPSPUT_LAN_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + add3 r0, r0, #(OPSPUT_LAN_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_int2: + add3 r2, r0, #-(M32R_IRQ_INT2) ; INT2# interrupt + bnez r2, check_end + ; read ICU status of LCD-board + seth r0, #high(OPSPUT_LCD_ICUISTS) + or3 r0, r0, #low(OPSPUT_LCD_ICUISTS) + lduh r0, @r0 + slli r0, #21 + srli r0, #27 ; ISN + add3 r0, r0, #(OPSPUT_LCD_PLD_IRQ_BASE) + bra check_end + .fillinsn +check_end: +#endif /* CONFIG_PLAT_OPSPUT */ + bl do_IRQ +#endif /* CONFIG_SMP */ + ld r14, @sp+ + seth r0, #shigh(M32R_ICU_IMASK_ADDR) + st r14, @(low(M32R_ICU_IMASK_ADDR),r0) +#else +#error no chip configuration +#endif +ret_to_intr: + bra ret_from_intr + +/* + * Default EIT handler + */ + ALIGN +int_msg: + .asciz "Unknown interrupt\n" + .byte 0 + +ENTRY(default_eit_handler) + push r0 + mvfc r0, psw + push r1 + push r2 + push r3 + push r0 + LDIMM (r0, __KERNEL_DS) + mv r0, r1 + mv r0, r2 + LDIMM (r0, int_msg) + bl printk + pop r0 + pop r3 + pop r2 + pop r1 + mvtc r0, psw + pop r0 +infinit: + bra infinit + +#ifdef CONFIG_MMU +/* + * Access Exception handler + */ +ENTRY(ace_handler) + SWITCH_TO_KERNEL_STACK + SAVE_ALL + + seth r2, #shigh(MMU_REG_BASE) /* Check status register */ + ld r4, @(low(MESTS_offset),r2) + st r4, @(low(MESTS_offset),r2) + srl3 r1, r4, #4 +#ifdef CONFIG_CHIP_M32700 + and3 r1, r1, #0x0000ffff + ; WORKAROUND: ignore TME bit for the M32700(TS1). +#endif /* CONFIG_CHIP_M32700 */ + beqz r1, inst +oprand: + ld r2, @(low(MDEVA_offset),r2) ; set address + srli r2, #12 + slli r2, #12 + srli r1, #1 + bra 1f +inst: + and3 r1, r4, #2 + srli r1, #1 + or3 r1, r1, #8 + mvfc r2, bpc ; set address + .fillinsn +1: + mvfc r3, psw + mv r0, sp + and3 r3, r3, 0x800 + srli r3, #9 + or r1, r3 + /* + * do_page_fault(): + * r0 : struct pt_regs *regs + * r1 : unsigned long error-code + * r2 : unsigned long address + * error-code: + * +------+------+------+------+ + * | bit3 | bit2 | bit1 | bit0 | + * +------+------+------+------+ + * bit 3 == 0:means data, 1:means instruction + * bit 2 == 0:means kernel, 1:means user-mode + * bit 1 == 0:means read, 1:means write + * bit 0 == 0:means no page found 1:means protection fault + * + */ + bl do_page_fault + bra ret_from_intr +#endif /* CONFIG_MMU */ + + +ENTRY(alignment_check) +/* void alignment_check(int error_code) */ + SWITCH_TO_KERNEL_STACK + SAVE_ALL + ldi r1, #0x30 ; error_code + mv r0, sp ; pt_regs + bl do_alignment_check +error_code: + bra ret_from_exception + +ENTRY(rie_handler) +/* void rie_handler(int error_code) */ + SWITCH_TO_KERNEL_STACK + SAVE_ALL + mvfc r0, bpc + ld r1, @r0 + seth r0, #0xa0f0 + st r1, @r0 + ldi r1, #0x20 ; error_code + mv r0, sp ; pt_regs + bl do_rie_handler + bra error_code + +ENTRY(pie_handler) +/* void pie_handler(int error_code) */ + SWITCH_TO_KERNEL_STACK + SAVE_ALL + ldi r1, #0 ; error_code ; FIXME + mv r0, sp ; pt_regs + bl do_pie_handler + bra error_code + +ENTRY(debug_trap) + .global withdraw_debug_trap + /* void debug_trap(void) */ + SWITCH_TO_KERNEL_STACK + SAVE_ALL + mv r0, sp ; pt_regs + bl withdraw_debug_trap + ldi r1, #0 ; error_code + mv r0, sp ; pt_regs + bl do_debug_trap + bra error_code + + +/* Cache flushing handler */ +ENTRY(cache_flushing_handler) + .global _flush_cache_all + /* void _flush_cache_all(void); */ + SWITCH_TO_KERNEL_STACK + push r0 + push r1 + push r2 + push r3 + push r4 + push r5 + push r6 + push r7 + push lr + bl _flush_cache_all + pop lr + pop r7 + pop r6 + pop r5 + pop r4 + pop r3 + pop r2 + pop r1 + pop r0 + rte + +.data +ENTRY(sys_call_table) + .long sys_restart_syscall /* 0 - old "setup()" system call*/ + .long sys_exit + .long sys_fork + .long sys_read + .long sys_write + .long sys_open /* 5 */ + .long sys_close + .long sys_waitpid + .long sys_creat + .long sys_link + .long sys_unlink /* 10 */ + .long sys_execve + .long sys_chdir + .long sys_time + .long sys_mknod + .long sys_chmod /* 15 */ + .long sys_lchown + .long sys_ni_syscall /* old break syscall holder */ + .long sys_stat + .long sys_lseek + .long sys_getpid /* 20 */ + .long sys_mount + .long sys_oldumount + .long sys_setuid + .long sys_getuid + .long sys_stime /* 25 */ + .long sys_ptrace + .long sys_alarm + .long sys_fstat + .long sys_pause + .long sys_utime /* 30 */ + .long sys_cacheflush /* for M32R */ /* old stty syscall holder */ + .long sys_cachectl /* for M32R */ /* old gtty syscall holder */ + .long sys_access + .long sys_nice + .long sys_ni_syscall /* 35 - old ftime syscall holder */ + .long sys_sync + .long sys_kill + .long sys_rename + .long sys_mkdir + .long sys_rmdir /* 40 */ + .long sys_dup + .long sys_pipe + .long sys_times + .long sys_ni_syscall /* old prof syscall holder */ + .long sys_brk /* 45 */ + .long sys_setgid + .long sys_getgid + .long sys_signal + .long sys_geteuid + .long sys_getegid /* 50 */ + .long sys_acct + .long sys_umount /* recycled never used phys() */ + .long sys_ni_syscall /* old lock syscall holder */ + .long sys_ioctl + .long sys_fcntl /* 55 */ + .long sys_ni_syscall /* old mpx syscall holder */ + .long sys_setpgid + .long sys_ni_syscall /* old ulimit syscall holder */ + .long sys_ni_syscall /* sys_olduname */ + .long sys_umask /* 60 */ + .long sys_chroot + .long sys_ustat + .long sys_dup2 + .long sys_getppid + .long sys_getpgrp /* 65 */ + .long sys_setsid + .long sys_sigaction + .long sys_sgetmask + .long sys_ssetmask + .long sys_setreuid /* 70 */ + .long sys_setregid + .long sys_sigsuspend + .long sys_sigpending + .long sys_sethostname + .long sys_setrlimit /* 75 */ + .long sys_getrlimit + .long sys_getrusage + .long sys_gettimeofday + .long sys_settimeofday + .long sys_getgroups /* 80 */ + .long sys_setgroups + .long sys_ni_syscall /* sys_oldselect */ + .long sys_symlink + .long sys_lstat + .long sys_readlink /* 85 */ + .long sys_uselib + .long sys_swapon + .long sys_reboot + .long old_readdir + .long old_mmap /* 90 */ + .long sys_munmap + .long sys_truncate + .long sys_ftruncate + .long sys_fchmod + .long sys_fchown /* 95 */ + .long sys_getpriority + .long sys_setpriority + .long sys_ni_syscall /* old profil syscall holder */ + .long sys_statfs + .long sys_fstatfs /* 100 */ + .long sys_ni_syscall /* ioperm */ + .long sys_socketcall + .long sys_syslog + .long sys_setitimer + .long sys_getitimer /* 105 */ + .long sys_newstat + .long sys_newlstat + .long sys_newfstat + .long sys_uname + .long sys_ni_syscall /* 110 - iopl */ + .long sys_vhangup + .long sys_ni_syscall /* for idle */ + .long sys_ni_syscall /* for vm86old */ + .long sys_wait4 + .long sys_swapoff /* 115 */ + .long sys_sysinfo + .long sys_ipc + .long sys_fsync + .long sys_sigreturn + .long sys_clone /* 120 */ + .long sys_setdomainname + .long sys_newuname + .long sys_ni_syscall /* sys_modify_ldt */ + .long sys_adjtimex + .long sys_mprotect /* 125 */ + .long sys_sigprocmask + .long sys_ni_syscall /* sys_create_module */ + .long sys_init_module + .long sys_delete_module + .long sys_ni_syscall /* 130 sys_get_kernel_syms */ + .long sys_quotactl + .long sys_getpgid + .long sys_fchdir + .long sys_bdflush + .long sys_sysfs /* 135 */ + .long sys_personality + .long sys_ni_syscall /* for afs_syscall */ + .long sys_setfsuid + .long sys_setfsgid + .long sys_llseek /* 140 */ + .long sys_getdents + .long sys_select + .long sys_flock + .long sys_msync + .long sys_readv /* 145 */ + .long sys_writev + .long sys_getsid + .long sys_fdatasync + .long sys_sysctl + .long sys_mlock /* 150 */ + .long sys_munlock + .long sys_mlockall + .long sys_munlockall + .long sys_sched_setparam + .long sys_sched_getparam /* 155 */ + .long sys_sched_setscheduler + .long sys_sched_getscheduler + .long sys_sched_yield + .long sys_sched_get_priority_max + .long sys_sched_get_priority_min /* 160 */ + .long sys_sched_rr_get_interval + .long sys_nanosleep + .long sys_mremap + .long sys_setresuid + .long sys_getresuid /* 165 */ + .long sys_tas /* vm86 */ + .long sys_ni_syscall /* sys_query_module */ + .long sys_poll + .long sys_nfsservctl + .long sys_setresgid /* 170 */ + .long sys_getresgid + .long sys_prctl + .long sys_rt_sigreturn + .long sys_rt_sigaction + .long sys_rt_sigprocmask /* 175 */ + .long sys_rt_sigpending + .long sys_rt_sigtimedwait + .long sys_rt_sigqueueinfo + .long sys_rt_sigsuspend + .long sys_pread64 /* 180 */ + .long sys_pwrite64 + .long sys_chown + .long sys_getcwd + .long sys_capget + .long sys_capset /* 185 */ + .long sys_sigaltstack + .long sys_sendfile + .long sys_ni_syscall /* streams1 */ + .long sys_ni_syscall /* streams2 */ + .long sys_vfork /* 190 */ + .long sys_getrlimit + .long sys_mmap2 + .long sys_truncate64 + .long sys_ftruncate64 + .long sys_stat64 /* 195 */ + .long sys_lstat64 + .long sys_fstat64 + .long sys_lchown + .long sys_getuid + .long sys_getgid /* 200 */ + .long sys_geteuid + .long sys_getegid + .long sys_setreuid + .long sys_setregid + .long sys_getgroups /* 205 */ + .long sys_setgroups + .long sys_fchown + .long sys_setresuid + .long sys_getresuid + .long sys_setresgid /* 210 */ + .long sys_getresgid + .long sys_chown + .long sys_setuid + .long sys_setgid + .long sys_setfsuid /* 215 */ + .long sys_setfsgid + .long sys_pivot_root + .long sys_mincore + .long sys_madvise + .long sys_getdents64 /* 220 */ + .long sys_fcntl64 + .long sys_ni_syscall /* reserved for TUX */ + .long sys_ni_syscall /* Reserved for Security */ + .long sys_gettid + .long sys_readahead /* 225 */ + .long sys_setxattr + .long sys_lsetxattr + .long sys_fsetxattr + .long sys_getxattr + .long sys_lgetxattr /* 230 */ + .long sys_fgetxattr + .long sys_listxattr + .long sys_llistxattr + .long sys_flistxattr + .long sys_removexattr /* 235 */ + .long sys_lremovexattr + .long sys_fremovexattr + .long sys_tkill + .long sys_sendfile64 + .long sys_futex /* 240 */ + .long sys_sched_setaffinity + .long sys_sched_getaffinity + .long sys_ni_syscall /* reserved for "set_thread_area" system call */ + .long sys_ni_syscall /* reserved for "get_thread_area" system call */ + .long sys_io_setup /* 245 */ + .long sys_io_destroy + .long sys_io_getevents + .long sys_io_submit + .long sys_io_cancel + .long sys_fadvise64 /* 250 */ + .long sys_ni_syscall + .long sys_exit_group + .long sys_lookup_dcookie + .long sys_epoll_create + .long sys_epoll_ctl /* 255 */ + .long sys_epoll_wait + .long sys_remap_file_pages + .long sys_set_tid_address + .long sys_timer_create + .long sys_timer_settime /* 260 */ + .long sys_timer_gettime + .long sys_timer_getoverrun + .long sys_timer_delete + .long sys_clock_settime + .long sys_clock_gettime /* 265 */ + .long sys_clock_getres + .long sys_clock_nanosleep + .long sys_statfs64 + .long sys_fstatfs64 + .long sys_tgkill /* 270 */ + .long sys_utimes + .long sys_fadvise64_64 + .long sys_ni_syscall /* Reserved for sys_vserver */ + .long sys_ni_syscall /* Reserved for sys_mbind */ + .long sys_ni_syscall /* Reserved for sys_get_mempolicy */ + .long sys_ni_syscall /* Reserved for sys_set_mempolicy */ + .long sys_mq_open + .long sys_mq_unlink + .long sys_mq_timedsend + .long sys_mq_timedreceive /* 280 */ + .long sys_mq_notify + .long sys_mq_getsetattr + .long sys_ni_syscall /* reserved for kexec */ + +syscall_table_size=(.-sys_call_table) + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/head.S 2004-09-03 00:57:19.061141528 -0700 @@ -0,0 +1,287 @@ +/* + * linux/arch/m32r/kernel/head.S + * + * M32R startup code. + * + * Copyright (c) 2001, 2002 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto + */ + +/* $Id$ */ + +#include +__INIT +__INITDATA + + .text +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * References to members of the boot_cpu_data structure. + */ + .text + .global start_kernel + .global __bss_start + .global _end +ENTRY(stext) +ENTRY(_stext) +ENTRY(startup_32) + /* Setup up the stack pointer */ + LDIMM (r0, spi_stack_top) + LDIMM (r1, spu_stack_top) + mvtc r0, spi + mvtc r1, spu + + /* Initilalize PSW */ + ldi r0, #0x0000 /* use SPI, disable EI */ + mvtc r0, psw + + /* Set up the stack pointer */ + LDIMM (r0, stack_start) + ld r0, @r0 + mvtc r0, spi + +/* + * Clear BSS first so that there are no surprises... + */ +#ifdef CONFIG_ISA_DUAL_ISSUE + + LDIMM (r2, __bss_start) + LDIMM (r3, _end) + sub r3, r2 ; BSS size in bytes + ; R4 = BSS size in longwords (rounded down) + mv r4, r3 || ldi r1, #0 + srli r4, #4 || addi r2, #-4 + beqz r4, .Lendloop1 +.Lloop1: +#ifndef CONFIG_CHIP_M32310 + ; Touch memory for the no-write-allocating cache. + ld r0, @(4,r2) +#endif + st r1, @+r2 || addi r4, #-1 + st r1, @+r2 + st r1, @+r2 + st r1, @+r2 || cmpeq r1, r4 ; R4 = 0? + bnc .Lloop1 +.Lendloop1: + and3 r4, r3, #15 + addi r2, #4 + beqz r4, .Lendloop2 +.Lloop2: + stb r1, @r2 || addi r4, #-1 + addi r2, #1 + bnez r4, .Lloop2 +.Lendloop2: + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + + LDIMM (r2, __bss_start) + LDIMM (r3, _end) + sub r3, r2 ; BSS size in bytes + mv r4, r3 + srli r4, #2 ; R4 = BSS size in longwords (rounded down) + ldi r1, #0 ; clear R1 for longwords store + addi r2, #-4 ; account for pre-inc store + beqz r4, .Lendloop1 ; any more to go? +.Lloop1: + st r1, @+r2 ; yep, zero out another longword + addi r4, #-1 ; decrement count + bnez r4, .Lloop1 ; go do some more +.Lendloop1: + and3 r4, r3, #3 ; get no. of remaining BSS bytes to clear + addi r2, #4 ; account for pre-inc store + beqz r4, .Lendloop2 ; any more to go? +.Lloop2: + stb r1, @r2 ; yep, zero out another byte + addi r2, #1 ; bump address + addi r4, #-1 ; decrement count + bnez r4, .Lloop2 ; go do some more +.Lendloop2: + +#endif /* not CONFIG_ISA_DUAL_ISSUE */ + +#if 0 /* M32R_FIXME */ +/* + * Copy data segment from ROM to RAM. + */ + .global ROM_D, TOP_DATA, END_DATA + + LDIMM (r1, ROM_D) + LDIMM (r2, TOP_DATA) + LDIMM (r3, END_DATA) + addi r2, #-4 + addi r3, #-4 +loop1: + ld r0, @r1+ + st r0, @+r2 + cmp r2, r3 + bc loop1 +#endif /* 0 */ + +/* Jump to kernel */ + LDIMM (r2, start_kernel) + jl r2 + .fillinsn +1: + bra 1b ; main should never return here, but + ; just in case, we know what happens. + +#ifdef CONFIG_SMP +/* + * AP startup routine + */ + .text + .global eit_vector +ENTRY(startup_AP) +;; setup EVB + LDIMM (r4, eit_vector) + mvtc r4, cr5 + +;; enable MMU + LDIMM (r2, init_tlb) + jl r2 + seth r4, #high(MATM) + or3 r4, r4, #low(MATM) + ldi r5, #0x01 + st r5, @r4 ; Set MATM Reg(T bit ON) + ld r6, @r4 ; MATM Check + LDIMM (r5, 1f) + jmp r5 ; enable MMU + nop + .fillinsn +1: +;; ISN check + ld r6, @r4 ; MATM Check + seth r4, #high(M32R_ICU_ISTS_ADDR) + or3 r4, r4, #low(M32R_ICU_ISTS_ADDR) + ld r5, @r4 ; Read ISTSi reg. + mv r6, r5 + slli r5, #13 ; PIML check + srli r5, #13 ; + seth r4, #high(M32R_ICU_IMASK_ADDR) + or3 r4, r4, #low(M32R_ICU_IMASK_ADDR) + st r5, @r4 ; Write IMASKi reg. + slli r6, #4 ; ISN check + srli r6, #26 ; + seth r4, #high(M32R_IRQ_IPI5) + or3 r4, r4, #low(M32R_IRQ_IPI5) + bne r4, r6, 2f ; if (ISN != CPU_BOOT_IPI) goto sleep; + +;; check cpu_bootout_map and set cpu_bootin_map + LDIMM (r4, cpu_bootout_map) + ld r4, @r4 + seth r5, #high(M32R_CPUID_PORTL) + or3 r5, r5, #low(M32R_CPUID_PORTL) + ld r5, @r5 + ldi r6, #1 + sll r6, r5 + and r4, r6 + beqz r4, 2f + LDIMM (r4, cpu_bootin_map) + ld r5, @r4 + or r5, r6 + st r6, @r4 + +;; clear PSW + ldi r4, #0 + mvtc r4, psw + +;; setup SPI + LDIMM (r4, stack_start) + ld r4, @r4 + mvtc r4, spi + +;; setup BPC (start_secondary) + LDIMM (r4, start_secondary) + mvtc r4, bpc + + rte ; goto startup_secondary + nop + nop + + .fillinsn +2: + ;; disable MMU + seth r4, #high(MATM) + or3 r4, r4, #low(MATM) + ldi r5, #0 + st r5, @r4 ; Set MATM Reg(T bit OFF) + ld r6, @r4 ; MATM Check + LDIMM (r4, 3f) + seth r5, #high(__PAGE_OFFSET) + or3 r5, r5, #low(__PAGE_OFFSET) + not r5, r5 + and r4, r5 + jmp r4 ; disable MMU + nop + .fillinsn +3: + ;; SLEEP and wait IPI + LDIMM (r4, AP_loop) + seth r5, #high(__PAGE_OFFSET) + or3 r5, r5, #low(__PAGE_OFFSET) + not r5, r5 + and r4, r5 + jmp r4 + nop + nop +#endif /* CONFIG_SMP */ + +ENTRY(stack_start) + .long init_thread_union+8192 + .long __KERNEL_DS + +/* + * This is initialized to create a identity-mapping at 0-4M (for bootup + * purposes) and another mapping of the 0-4M area at virtual address + * PAGE_OFFSET. + */ + .text + +#define MOUNT_ROOT_RDONLY 1 +#define RAMDISK_FLAGS 0 ; 1024KB +#define ORIG_ROOT_DEV 0x0100 ; /dev/ram0 (major:01, minor:00) +#define LOADER_TYPE 1 ; (??? - non-zero value seems + ; to be needed to boot from initrd) + +#define COMMAND_LINE "" + + .section .empty_zero_page, "aw" +ENTRY(empty_zero_page) + .long MOUNT_ROOT_RDONLY /* offset: +0x00 */ + .long RAMDISK_FLAGS + .long ORIG_ROOT_DEV + .long LOADER_TYPE + .long 0 /* INITRD_START */ /* +0x10 */ + .long 0 /* INITRD_SIZE */ + .long 0 /* CPU_CLOCK */ + .long 0 /* BUS_CLOCK */ + .long 0 /* TIMER_DIVIDE */ /* +0x20 */ + .balign 256,0 + .asciz COMMAND_LINE + .byte 0 + .balign 4096,0,4096 + +/*------------------------------------------------------------------------ + * Stack area + */ + .section .spi + ALIGN + .global spi_stack_top + .zero 1024 +spi_stack_top: + + .section .spu + ALIGN + .global spu_stack_top + .zero 1024 +spu_stack_top: + + .end --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/init_task.c 2004-09-03 00:57:18.718193664 -0700 @@ -0,0 +1,41 @@ +/* orig : i386 init_task.c */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS(init_signals); +static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); +struct mm_struct init_mm = INIT_MM(init_mm); + +EXPORT_SYMBOL(init_mm); + +/* + * Initial thread structure. + * + * We need to make sure that this is 8192-byte aligned due to the + * way process stacks are handled. This is done by having a special + * "init_task" linker map entry.. + */ +union thread_union init_thread_union + __attribute__((__section__(".data.init_task"))) = + { INIT_THREAD_INFO(init_task) }; + +/* + * Initial task structure. + * + * All other task structs will be allocated on slabs in fork.c + */ +struct task_struct init_task = INIT_TASK(init_task); + +EXPORT_SYMBOL(init_task); + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/io_m32102.c 2004-09-03 00:57:17.313407224 -0700 @@ -0,0 +1,277 @@ +/* + * Mitsubishi M32R 32102 group + * Typical I/O routines. + * + * Copyright (c) 2001 Hitoshi Yamamoto + */ + +/* $Id$ */ + +#include +#include + +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) +#include + +#define M32R_PCC_IOMAP_SIZE 0x1000 + +#define M32R_PCC_IOSTART0 0x1000 +#define M32R_PCC_IOEND0 (M32R_PCC_IOSTART0 + M32R_PCC_IOMAP_SIZE - 1) +#define M32R_PCC_IOSTART1 0x2000 +#define M32R_PCC_IOEND1 (M32R_PCC_IOSTART1 + M32R_PCC_IOMAP_SIZE - 1) + +extern void pcc_ioread(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_iowrite(int, unsigned long, void *, size_t, size_t, int); +#endif /* CONFIG_PCMCIA && CONFIG_M32RPCC */ + + +/* + * Function prototypes + */ +unsigned char ne_inb(unsigned long); +void ne_outb(unsigned char, unsigned long); +void ne_insb(unsigned int, void *, unsigned long); +void ne_insw(unsigned int, void *, unsigned long); +void ne_outsb(unsigned int, const void *, unsigned long); +void ne_outsw(unsigned int, const void *, unsigned long); + +#define PORT2ADDR(port) m32102_port2addr(port) + +static __inline__ unsigned long +m32102_port2addr(unsigned long port) +{ + unsigned long ul; + ul = port + PAGE_OFFSET + 0x20000000; + return (ul); +} + +unsigned char +m32102_inb(unsigned long port) +{ +#ifdef CONFIG_PLAT_MAPPI + if(port >= 0x300 && port < 0x320) + return ne_inb(port); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned char b; + pcc_ioread(0, port, &b, sizeof(b), 1, 0); + return b; + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + unsigned char b; + pcc_ioread(1, port, &b, sizeof(b), 1, 0); + return b; + } else +#endif +#endif /* CONFIG_PLAT_MAPPI */ + return *(unsigned char *)PORT2ADDR(port); +} + +unsigned short +m32102_inw(unsigned long port) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned short w; + pcc_ioread(0, port, &w, sizeof(w), 1, 0); + return w; + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + unsigned short w; + pcc_ioread(1, port, &w, sizeof(w), 1, 0); + return w; + } else +#endif + return *(unsigned short *)PORT2ADDR(port); +} + +unsigned long +m32102_inl(unsigned long port) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned long l; + pcc_ioread(0, port, &l, sizeof(l), 1, 0); + return l; + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + unsigned short l; + pcc_ioread(1, port, &l, sizeof(l), 1, 0); + return l; + } else +#endif + return *(unsigned long *)PORT2ADDR(port); +} + +void +m32102_outb(unsigned char b, unsigned long port) +{ +#ifdef CONFIG_PLAT_MAPPI + if(port >= 0x300 && port < 0x320) + ne_outb(b,port); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite(0, port, &b, sizeof(b), 1, 0); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_iowrite(1, port, &b, sizeof(b), 1, 0); + } else +#endif +#endif /* CONFIG_PLAT_MAPPI */ + *(unsigned volatile char *)PORT2ADDR(port) = b; +} + +void +m32102_outw(unsigned short w, unsigned long port) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite(0, port, &w, sizeof(w), 1, 0); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_iowrite(1, port, &w, sizeof(w), 1, 0); + } else +#endif +*(unsigned volatile short *)PORT2ADDR(port) = w; +} + +void +m32102_outl(unsigned long l, unsigned long port) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite(0, port, &l, sizeof(l), 1, 0); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_iowrite(1, port, &l, sizeof(l), 1, 0); + } else +#endif + *(unsigned volatile long *)PORT2ADDR(port) = l; +} + +void +m32102_insb(unsigned int port, void * addr, unsigned long count) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_ioread(0, port, (void *)addr, sizeof(unsigned char), count, 1); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_ioread(1, port, (void *)addr, sizeof(unsigned char), count, 1); + } else +#endif + while(count--){ + *(unsigned char *)addr = *(unsigned volatile char *)PORT2ADDR(port); + addr+=1; + } +} + +void +m32102_insw(unsigned int port, void * addr, unsigned long count) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_ioread(0, port, (void *)addr, sizeof(unsigned short), count, 1); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_ioread(1, port, (void *)addr, sizeof(unsigned short), count, 1); + } else +#endif +while(count--){ + *(unsigned short *)addr = *(unsigned volatile short *)PORT2ADDR(port); + addr+=2; + } +} + +void +m32102_outsb(unsigned int port, const void * addr, unsigned long count) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite(0, port, (void *)addr, sizeof(unsigned char), count, 1); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_iowrite(1, port, (void *)addr, sizeof(unsigned char), count, 1); + } else +#endif +while(count--){ + *(unsigned volatile char *)PORT2ADDR(port) = *(unsigned char *)addr; + addr+=1; + } +} +void +m32102_outsw(unsigned int port, const void * addr, unsigned long count) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite(0, port, (void *)addr, sizeof(unsigned short), count, 1); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_iowrite(1, port, (void *)addr, sizeof(unsigned short), count, 1); + } else +#endif +while(count--){ + *(unsigned volatile short *)PORT2ADDR(port) = *(unsigned short *)addr; + addr+=2; + } +} + +#ifdef CONFIG_PLAT_MAPPI +unsigned char +ne_inb(unsigned long port) +{ + unsigned short tmp; + port <<= 1; + port+= PAGE_OFFSET + 0x20000000 + 0x0c000000; + tmp = *(unsigned short *)port; + return (unsigned char)tmp; +} +void +ne_outb(unsigned char b, unsigned long port) +{ + port <<= 1; + port += PAGE_OFFSET + 0x20000000 + 0x0c000000; + *(unsigned volatile short *)port = (unsigned short)b; +} +void +ne_insb(unsigned int port, void * addr, unsigned long count) +{ + + unsigned short tmp; + port <<= 1; + port+= PAGE_OFFSET + 0x20000000 + 0x0c000000; + tmp = *(unsigned short *)port; + while(count--){ + *(unsigned char *)addr = *(unsigned volatile char *)port; + addr+=1; + } +} + +void +ne_insw(unsigned int port, void * addr, unsigned long count) { + unsigned short tmp; + port <<= 1; + port+= PAGE_OFFSET + 0x20000000 + 0x0c000000; + while(count--){ + tmp = *(unsigned volatile short *)port; + *(unsigned short *)addr = (tmp>>8) | (tmp <<8); + addr+=2; + } +} + +void +ne_outsb(unsigned int port, const void * addr, unsigned long count) +{ + port <<= 1; + port += PAGE_OFFSET + 0x20000000 + 0x0c000000; + while(count--){ + *(unsigned volatile short *)port = *(unsigned char *)addr; + addr+=1; + } +} +void +ne_outsw(unsigned int port, const void * addr, unsigned long count) +{ + unsigned short tmp; + port <<= 1; + port += PAGE_OFFSET + 0x20000000 + 0x0c000000; + while(count--){ + tmp = *(unsigned short *)addr; + *(unsigned volatile short *)port = (tmp>>8)|(tmp<<8); + addr+=2; + } +} + +#endif /* CONFIG_PLAT_MAPPI */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/io_m32700ut.c 2004-09-03 00:57:19.062141376 -0700 @@ -0,0 +1,455 @@ +/* + * linux/arch/m32r/kernel/io_mappi.c + * + * Typical I/O routines for M32700UT board. + * + * Copyright (c) 2001, 2002 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto, Takeo Takahashi + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + */ + +#include +#include +#include +#include +#include + +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) +#include + +#define M32R_PCC_IOMAP_SIZE 0x1000 + +#define M32R_PCC_IOSTART0 0x1000 +#define M32R_PCC_IOEND0 (M32R_PCC_IOSTART0 + M32R_PCC_IOMAP_SIZE - 1) + +extern void pcc_ioread_byte(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_ioread_word(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_iowrite_byte(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_iowrite_word(int, unsigned long, void *, size_t, size_t, int); +#endif /* CONFIG_PCMCIA && CONFIG_M32R_CFC */ + +#define PORT2ADDR(port) _port2addr(port) +#define PORT2ADDR_USB(port) _port2addr_usb(port) + +static __inline__ void *_port2addr(unsigned long port) +{ + return (void *)(port + NONCACHE_OFFSET); +} + +#if defined(CONFIG_IDE) +static __inline__ void *__port2addr_ata(unsigned long port) +{ + static int dummy_reg; + + switch (port) { + case 0x1f0: return (void *)0xac002000; + case 0x1f1: return (void *)0xac012800; + case 0x1f2: return (void *)0xac012002; + case 0x1f3: return (void *)0xac012802; + case 0x1f4: return (void *)0xac012004; + case 0x1f5: return (void *)0xac012804; + case 0x1f6: return (void *)0xac012006; + case 0x1f7: return (void *)0xac012806; + case 0x3f6: return (void *)0xac01200e; + default: return (void *)&dummy_reg; + } +} +#endif + +/* + * M32700UT-LAN is located in the extended bus space + * from 0x10000000 to 0x13ffffff on physical address. + * The base address of LAN controller(LAN91C111) is 0x300. + */ +#define LAN_IOSTART 0x300 +#define LAN_IOEND 0x320 +static __inline__ void *_port2addr_ne(unsigned long port) +{ + return (void *)(port + NONCACHE_OFFSET + 0x10000000); +} +static __inline__ void *_port2addr_usb(unsigned long port) +{ + return (void *)((port & 0x0f) + NONCACHE_OFFSET + 0x10303000); +} + +static __inline__ void delay(void) +{ + __asm__ __volatile__ ("push r0; \n\t pop r0;" : : :"memory"); +} + +/* + * NIC I/O function + */ + +#define PORT2ADDR_NE(port) _port2addr_ne(port) + +static __inline__ unsigned char _ne_inb(void *portp) +{ + return *(volatile unsigned char *)portp; +} + +static __inline__ unsigned short _ne_inw(void *portp) +{ + return (unsigned short)le16_to_cpu(*(volatile unsigned short *)portp); +} + +static __inline__ void _ne_insb(void *portp, void * addr, unsigned long count) +{ + unsigned char *buf = (unsigned char *)addr; + + while (count--) *buf++ = _ne_inb(portp); +} + +static __inline__ void _ne_outb(unsigned char b, void *portp) +{ + *(volatile unsigned char *)portp = b; +} + +static __inline__ void _ne_outw(unsigned short w, void *portp) +{ + *(volatile unsigned short *)portp = cpu_to_le16(w); +} + +unsigned char _inb(unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + return _ne_inb(PORT2ADDR_NE(port)); + +#if defined(CONFIG_IDE) + else if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + return *(volatile unsigned char *)__port2addr_ata(port); + } +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned char b; + pcc_ioread_byte(0, port, &b, sizeof(b), 1, 0); + return b; + } else +#endif + + return *(volatile unsigned char *)PORT2ADDR(port); +} + +unsigned short _inw(unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + return _ne_inw(PORT2ADDR_NE(port)); +#if defined(CONFIG_IDE) + else if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + return *(volatile unsigned short *)__port2addr_ata(port); + } +#endif +#if defined(CONFIG_USB) + else if(port >= 0x340 && port < 0x3a0) + return *(volatile unsigned short *)PORT2ADDR_USB(port); +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned short w; + pcc_ioread_word(0, port, &w, sizeof(w), 1, 0); + return w; + } else +#endif + return *(volatile unsigned short *)PORT2ADDR(port); +} + +unsigned long _inl(unsigned long port) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned long l; + pcc_ioread_word(0, port, &l, sizeof(l), 1, 0); + return l; + } else +#endif + return *(volatile unsigned long *)PORT2ADDR(port); +} + +unsigned char _inb_p(unsigned long port) +{ + unsigned char v; + + if (port >= LAN_IOSTART && port < LAN_IOEND) + v = _ne_inb(PORT2ADDR_NE(port)); + else +#if defined(CONFIG_IDE) + if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + return *(volatile unsigned char *)__port2addr_ata(port); + } else +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned char b; + pcc_ioread_byte(0, port, &b, sizeof(b), 1, 0); + return b; + } else +#endif + v = *(volatile unsigned char *)PORT2ADDR(port); + + delay(); + return (v); +} + +unsigned short _inw_p(unsigned long port) +{ + unsigned short v; + + if (port >= LAN_IOSTART && port < LAN_IOEND) + v = _ne_inw(PORT2ADDR_NE(port)); + else +#if defined(CONFIG_IDE) + if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + return *(volatile unsigned short *)__port2addr_ata(port); + } else +#endif +#if defined(CONFIG_USB) + if(port >= 0x340 && port < 0x3a0) + return *(volatile unsigned short *)PORT2ADDR_USB(port); + else +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned short w; + pcc_ioread_word(0, port, &w, sizeof(w), 1, 0); + return w; + } else +#endif + v = *(volatile unsigned short *)PORT2ADDR(port); + + delay(); + return (v); +} + +unsigned long _inl_p(unsigned long port) +{ + unsigned long v; + + v = *(volatile unsigned long *)PORT2ADDR(port); + delay(); + return (v); +} + +void _outb(unsigned char b, unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_outb(b, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_IDE) + if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + *(volatile unsigned char *)__port2addr_ata(port) = b; + } else +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_byte(0, port, &b, sizeof(b), 1, 0); + } else +#endif + *(volatile unsigned char *)PORT2ADDR(port) = b; +} + +void _outw(unsigned short w, unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_outw(w, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_IDE) + if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + *(volatile unsigned short *)__port2addr_ata(port) = w; + } else +#endif +#if defined(CONFIG_USB) + if(port >= 0x340 && port < 0x3a0) + *(volatile unsigned short *)PORT2ADDR_USB(port) = w; + else +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(0, port, &w, sizeof(w), 1, 0); + } else +#endif + *(volatile unsigned short *)PORT2ADDR(port) = w; +} + +void _outl(unsigned long l, unsigned long port) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(0, port, &l, sizeof(l), 1, 0); + } else +#endif + *(volatile unsigned long *)PORT2ADDR(port) = l; +} + +void _outb_p(unsigned char b, unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_outb(b, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_IDE) + if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + *(volatile unsigned char *)__port2addr_ata(port) = b; + } else +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_byte(0, port, &b, sizeof(b), 1, 0); + } else +#endif + *(volatile unsigned char *)PORT2ADDR(port) = b; + + delay(); +} + +void _outw_p(unsigned short w, unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_outw(w, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_IDE) + if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + *(volatile unsigned short *)__port2addr_ata(port) = w; + } else +#endif +#if defined(CONFIG_USB) + if(port >= 0x340 && port < 0x3a0) + *(volatile unsigned short *)PORT2ADDR_USB(port) = w; + else +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(0, port, &w, sizeof(w), 1, 0); + } else +#endif + *(volatile unsigned short *)PORT2ADDR(port) = w; + + delay(); +} + +void _outl_p(unsigned long l, unsigned long port) +{ + *(volatile unsigned long *)PORT2ADDR(port) = l; + delay(); +} + +void _insb(unsigned int port, void * addr, unsigned long count) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_insb(PORT2ADDR_NE(port), addr, count); +#if defined(CONFIG_IDE) + else if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + unsigned char *buf = addr; + unsigned char *portp = __port2addr_ata(port); + while(count--) *buf++ = *(volatile unsigned char *)portp; + } +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_ioread_byte(0, port, (void *)addr, sizeof(unsigned char), count, 1); + } +#endif + else { + unsigned char *buf = addr; + unsigned char *portp = PORT2ADDR(port); + while(count--) *buf++ = *(volatile unsigned char *)portp; + } +} + +void _insw(unsigned int port, void * addr, unsigned long count) +{ + unsigned short *buf = addr; + unsigned short *portp; + + if (port >= LAN_IOSTART && port < LAN_IOEND) { + /* + * This portion is only used by smc91111.c to read data + * from the DATA_REG. Do not swap the data. + */ + portp = PORT2ADDR_NE(port); + while (count--) *buf++ = *(volatile unsigned short *)portp; +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_ioread_word(9, port, (void *)addr, sizeof(unsigned short), count, 1); +#endif +#if defined(CONFIG_IDE) + } else if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + portp = __port2addr_ata(port); + while (count--) *buf++ = *(volatile unsigned short *)portp; +#endif + } else { + portp = PORT2ADDR(port); + while (count--) *buf++ = *(volatile unsigned short *)portp; + } +} + +void _insl(unsigned int port, void * addr, unsigned long count) +{ + unsigned long *buf = addr; + unsigned long *portp; + + portp = PORT2ADDR(port); + while (count--) *buf++ = *(volatile unsigned long *)portp; +} + +void _outsb(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned char *buf = addr; + unsigned char *portp; + + if (port >= LAN_IOSTART && port < LAN_IOEND) { + portp = PORT2ADDR_NE(port); + while (count--) _ne_outb(*buf++, portp); +#if defined(CONFIG_IDE) + } else if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + portp = __port2addr_ata(port); + while(count--) *(volatile unsigned char *)portp = *buf++; +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_byte(0, port, (void *)addr, sizeof(unsigned char), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned char *)portp = *buf++; + } +} + +void _outsw(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned short *buf = addr; + unsigned short *portp; + + if (port >= LAN_IOSTART && port < LAN_IOEND) { + /* + * This portion is only used by smc91111.c to write data + * into the DATA_REG. Do not swap the data. + */ + portp = PORT2ADDR_NE(port); + while(count--) *(volatile unsigned short *)portp = *buf++; +#if defined(CONFIG_IDE) + } else if ((port >= 0x1f0 && port <=0x1f7) || port == 0x3f6) { + portp = __port2addr_ata(port); + while(count--) *(volatile unsigned short *)portp = *buf++; +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(9, port, (void *)addr, sizeof(unsigned short), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned short *)portp = *buf++; + } +} + +void _outsl(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned long *buf = addr; + unsigned char *portp; + + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned long *)portp = *buf++; +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/io_mappi2.c 2004-09-03 00:57:17.317406616 -0700 @@ -0,0 +1,367 @@ +/* + * linux/arch/m32r/kernel/io_mappi2.c + * + * Typical I/O routines for Mappi2 board. + * + * Copyright (c) 2001-2003 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto, Mamoru Sakugawa + */ + +/* $Id:$ */ + +#include +#include +#include +#include + +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) +#include + +#define M32R_PCC_IOMAP_SIZE 0x1000 + +#define M32R_PCC_IOSTART0 0x1000 +#define M32R_PCC_IOEND0 (M32R_PCC_IOSTART0 + M32R_PCC_IOMAP_SIZE - 1) + +extern void pcc_ioread_byte(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_ioread_word(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_iowrite_byte(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_iowrite_word(int, unsigned long, void *, size_t, size_t, int); +#endif /* CONFIG_PCMCIA && CONFIG_MAPPI2_CFC */ + +#define PORT2ADDR(port) _port2addr(port) +#define PORT2ADDR_NE(port) _port2addr_ne(port) +#define PORT2ADDR_USB(port) _port2addr_usb(port) + +static __inline__ void *_port2addr(unsigned long port) +{ + return (void *)(port + NONCACHE_OFFSET); +} +#ifdef CONFIG_CHIP_OPSP +static __inline__ void *_port2addr_ne(unsigned long port) +{ + return (void *)(port + NONCACHE_OFFSET + 0x10000000); +} +#else +static __inline__ void *_port2addr_ne(unsigned long port) +{ + return (void *)(port + NONCACHE_OFFSET + 0x04000000); +} +#endif +static __inline__ void *_port2addr_usb(unsigned long port) +{ + return (void *)(port + NONCACHE_OFFSET + 0x14000000); +} +static __inline__ void delay(void) +{ + __asm__ __volatile__ ("push r0; \n\t pop r0;" : : :"memory"); +} + +/* + * NIC I/O function + */ + +static __inline__ unsigned char _ne_inb(void *portp) +{ + return (unsigned char) *(volatile unsigned char *)portp; +} + +static __inline__ unsigned short _ne_inw(void *portp) +{ +#if 1 /* byte swap */ + unsigned short tmp,tmp2; + tmp = *(volatile unsigned short *)portp; + tmp2 = (tmp>>8|tmp<<8); + return tmp2; +#else + return *(volatile unsigned short *)portp; +#endif +} + +static __inline__ void _ne_insb(void *portp, void * addr, unsigned long count) +{ + unsigned short tmp; + unsigned char *buf = addr; + + tmp = *(volatile unsigned char *)portp; + while (count--) *buf++ = *(volatile unsigned char *)portp; +} + +static __inline__ void _ne_outb(unsigned char b, void *portp) +{ + *(volatile unsigned char *)portp = (unsigned char)b; +} + +static __inline__ void _ne_outw(unsigned short w, void *portp) +{ + *(volatile unsigned short *)portp = (w>>8|w<<8); +} + +unsigned char _inb(unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + return _ne_inb(PORT2ADDR_NE(port)); +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned char b; + pcc_ioread_byte(0, port, &b, sizeof(b), 1, 0); + return b; + } else +#endif + + return *(volatile unsigned char *)PORT2ADDR(port); +} + +unsigned short _inw(unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + return _ne_inw(PORT2ADDR_NE(port)); +#if defined(CONFIG_USB) + else if (port >= 0x340 && port < 0x3a0) + return *(volatile unsigned short *)PORT2ADDR_USB(port); +#endif + +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned short w; + pcc_ioread_word(0, port, &w, sizeof(w), 1, 0); + return w; + } else +#endif + return *(volatile unsigned short *)PORT2ADDR(port); +} + +unsigned long _inl(unsigned long port) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned long l; + pcc_ioread_word(0, port, &l, sizeof(l), 1, 0); + return l; + } else +#endif + return *(volatile unsigned long *)PORT2ADDR(port); +} + +unsigned char _inb_p(unsigned long port) +{ + unsigned char v; + + if (port >= 0x300 && port < 0x320) + v = _ne_inb(PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned char b; + pcc_ioread_byte(0, port, &b, sizeof(b), 1, 0); + return b; + } else +#endif + v = *(volatile unsigned char *)PORT2ADDR(port); + + delay(); + return (v); +} + +unsigned short _inw_p(unsigned long port) +{ + unsigned short v; + + if (port >= 0x300 && port < 0x320) + v = _ne_inw(PORT2ADDR_NE(port)); + else +#if defined(CONFIG_USB) + if (port >= 0x340 && port < 0x3a0) + v = *(volatile unsigned short *)PORT2ADDR_USB(port); + else +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned short w; + pcc_ioread_word(0, port, &w, sizeof(w), 1, 0); + return w; + } else +#endif + v = *(volatile unsigned short *)PORT2ADDR(port); + + delay(); + return (v); +} + +unsigned long _inl_p(unsigned long port) +{ + unsigned long v; + + v = *(volatile unsigned long *)PORT2ADDR(port); + delay(); + return (v); +} + +void _outb(unsigned char b, unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + _ne_outb(b, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_byte(0, port, &b, sizeof(b), 1, 0); + } else +#endif + *(volatile unsigned char *)PORT2ADDR(port) = b; +} + +void _outw(unsigned short w, unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + _ne_outw(w, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_USB) + if (port >= 0x340 && port < 0x3a0) + *(volatile unsigned short *)PORT2ADDR_USB(port) = w; + else +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(0, port, &w, sizeof(w), 1, 0); + } else +#endif + *(volatile unsigned short *)PORT2ADDR(port) = w; +} + +void _outl(unsigned long l, unsigned long port) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(0, port, &l, sizeof(l), 1, 0); + } else +#endif + *(volatile unsigned long *)PORT2ADDR(port) = l; +} + +void _outb_p(unsigned char b, unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + _ne_outb(b, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_byte(0, port, &b, sizeof(b), 1, 0); + } else +#endif + *(volatile unsigned char *)PORT2ADDR(port) = b; + + delay(); +} + +void _outw_p(unsigned short w, unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + _ne_outw(w, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_USB) + if (port >= 0x340 && port < 0x3a0) + *(volatile unsigned short *)PORT2ADDR_USB(port) = w; + else +#endif +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(0, port, &w, sizeof(w), 1, 0); + } else +#endif + *(volatile unsigned short *)PORT2ADDR(port) = w; + + delay(); +} + +void _outl_p(unsigned long l, unsigned long port) +{ + *(volatile unsigned long *)PORT2ADDR(port) = l; + delay(); +} + +void _insb(unsigned int port, void * addr, unsigned long count) +{ + if (port >= 0x300 && port < 0x320) + _ne_insb(PORT2ADDR_NE(port), addr, count); +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_ioread_byte(0, port, (void *)addr, sizeof(unsigned char), count, 1); + } +#endif + else { + unsigned char *buf = addr; + unsigned char *portp = PORT2ADDR(port); + while(count--) *buf++ = *(volatile unsigned char *)portp; + } +} + +void _insw(unsigned int port, void * addr, unsigned long count) +{ + unsigned short *buf = addr; + unsigned short *portp; + + if (port >= 0x300 && port < 0x320) { + portp = PORT2ADDR_NE(port); + while (count--) *buf++ = *(volatile unsigned short *)portp; +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_ioread_word(9, port, (void *)addr, sizeof(unsigned short), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while (count--) *buf++ = *(volatile unsigned short *)portp; + } +} + +void _insl(unsigned int port, void * addr, unsigned long count) +{ + unsigned long *buf = addr; + unsigned long *portp; + + portp = PORT2ADDR(port); + while (count--) *buf++ = *(volatile unsigned long *)portp; +} + +void _outsb(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned char *buf = addr; + unsigned char *portp; + + if (port >= 0x300 && port < 0x320) { + portp = PORT2ADDR_NE(port); + while (count--) _ne_outb(*buf++, portp); +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_byte(0, port, (void *)addr, sizeof(unsigned char), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned char *)portp = *buf++; + } +} + +void _outsw(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned short *buf = addr; + unsigned short *portp; + + if (port >= 0x300 && port < 0x320) { + portp = PORT2ADDR_NE(port); + while (count--) *(volatile unsigned short *)portp = *buf++; +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(9, port, (void *)addr, sizeof(unsigned short), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned short *)portp = *buf++; + } +} + +void _outsl(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned long *buf = addr; + unsigned char *portp; + + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned long *)portp = *buf++; +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/io_mappi.c 2004-09-03 00:57:17.319406312 -0700 @@ -0,0 +1,368 @@ +/* + * linux/arch/m32r/kernel/io_mappi.c + * + * Typical I/O routines for Mappi board. + * + * Copyright (c) 2001, 2002 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto + */ + +/* $Id: io_mappi.c,v 1.9 2003/12/02 07:18:08 fujiwara Exp $ */ + +#include +#include +#include +#include +#include + +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) +#include + +#define M32R_PCC_IOMAP_SIZE 0x1000 + +#define M32R_PCC_IOSTART0 0x1000 +#define M32R_PCC_IOEND0 (M32R_PCC_IOSTART0 + M32R_PCC_IOMAP_SIZE - 1) +#define M32R_PCC_IOSTART1 0x2000 +#define M32R_PCC_IOEND1 (M32R_PCC_IOSTART1 + M32R_PCC_IOMAP_SIZE - 1) + +extern void pcc_ioread(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_iowrite(int, unsigned long, void *, size_t, size_t, int); +#endif /* CONFIG_PCMCIA && CONFIG_M32RPCC */ + +#define PORT2ADDR(port) _port2addr(port) + +static __inline__ void *_port2addr(unsigned long port) +{ + return (void *)(port + NONCACHE_OFFSET); +} + +static __inline__ void *_port2addr_ne(unsigned long port) +{ + return (void *)((port<<1) + NONCACHE_OFFSET + 0x0C000000); +} + +static __inline__ void delay(void) +{ + __asm__ __volatile__ ("push r0; \n\t pop r0;" : : :"memory"); +} + +/* + * NIC I/O function + */ + +#define PORT2ADDR_NE(port) _port2addr_ne(port) + +static __inline__ unsigned char _ne_inb(void *portp) +{ + return (unsigned char) *(volatile unsigned short *)portp; +} + +static __inline__ unsigned short _ne_inw(void *portp) +{ + unsigned short tmp; + + tmp = *(volatile unsigned short *)portp; + return le16_to_cpu(tmp); +} + +static __inline__ void _ne_outb(unsigned char b, void *portp) +{ + *(volatile unsigned short *)portp = (unsigned short)b; +} + +static __inline__ void _ne_outw(unsigned short w, void *portp) +{ + *(volatile unsigned short *)portp = cpu_to_le16(w); +} + +unsigned char _inb(unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + return _ne_inb(PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned char b; + pcc_ioread(0, port, &b, sizeof(b), 1, 0); + return b; + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + unsigned char b; + pcc_ioread(1, port, &b, sizeof(b), 1, 0); + return b; + } else +#endif + + return *(volatile unsigned char *)PORT2ADDR(port); +} + +unsigned short _inw(unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + return _ne_inw(PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned short w; + pcc_ioread(0, port, &w, sizeof(w), 1, 0); + return w; + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + unsigned short w; + pcc_ioread(1, port, &w, sizeof(w), 1, 0); + return w; + } else +#endif + return *(volatile unsigned short *)PORT2ADDR(port); +} + +unsigned long _inl(unsigned long port) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned long l; + pcc_ioread(0, port, &l, sizeof(l), 1, 0); + return l; + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + unsigned short l; + pcc_ioread(1, port, &l, sizeof(l), 1, 0); + return l; + } else +#endif + return *(volatile unsigned long *)PORT2ADDR(port); +} + +unsigned char _inb_p(unsigned long port) +{ + unsigned char v; + + if (port >= 0x300 && port < 0x320) + v = _ne_inb(PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned char b; + pcc_ioread(0, port, &b, sizeof(b), 1, 0); + return b; + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + unsigned char b; + pcc_ioread(1, port, &b, sizeof(b), 1, 0); + return b; + } else +#endif + v = *(volatile unsigned char *)PORT2ADDR(port); + + delay(); + return (v); +} + +unsigned short _inw_p(unsigned long port) +{ + unsigned short v; + + if (port >= 0x300 && port < 0x320) + v = _ne_inw(PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned short w; + pcc_ioread(0, port, &w, sizeof(w), 1, 0); + return w; + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + unsigned short w; + pcc_ioread(1, port, &w, sizeof(w), 1, 0); + return w; + } else +#endif + v = *(volatile unsigned short *)PORT2ADDR(port); + + delay(); + return (v); +} + +unsigned long _inl_p(unsigned long port) +{ + unsigned long v; + + v = *(volatile unsigned long *)PORT2ADDR(port); + delay(); + return (v); +} + +void _outb(unsigned char b, unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + _ne_outb(b, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite(0, port, &b, sizeof(b), 1, 0); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_iowrite(1, port, &b, sizeof(b), 1, 0); + } else +#endif + *(volatile unsigned char *)PORT2ADDR(port) = b; +} + +void _outw(unsigned short w, unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + _ne_outw(w, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite(0, port, &w, sizeof(w), 1, 0); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_iowrite(1, port, &w, sizeof(w), 1, 0); + } else +#endif + *(volatile unsigned short *)PORT2ADDR(port) = w; +} + +void _outl(unsigned long l, unsigned long port) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite(0, port, &l, sizeof(l), 1, 0); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_iowrite(1, port, &l, sizeof(l), 1, 0); + } else +#endif + *(volatile unsigned long *)PORT2ADDR(port) = l; +} + +void _outb_p(unsigned char b, unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + _ne_outb(b, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite(0, port, &b, sizeof(b), 1, 0); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_iowrite(1, port, &b, sizeof(b), 1, 0); + } else +#endif + *(volatile unsigned char *)PORT2ADDR(port) = b; + + delay(); +} + +void _outw_p(unsigned short w, unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + _ne_outw(w, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite(0, port, &w, sizeof(w), 1, 0); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_iowrite(1, port, &w, sizeof(w), 1, 0); + } else +#endif + *(volatile unsigned short *)PORT2ADDR(port) = w; + + delay(); +} + +void _outl_p(unsigned long l, unsigned long port) +{ + *(volatile unsigned long *)PORT2ADDR(port) = l; + delay(); +} + +void _insb(unsigned int port, void * addr, unsigned long count) +{ + unsigned short *buf = addr; + unsigned short *portp; + + if (port >= 0x300 && port < 0x320){ + portp = PORT2ADDR_NE(port); + while(count--) *buf++ = *(volatile unsigned char *)portp; +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_ioread(0, port, (void *)addr, sizeof(unsigned char), count, 1); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_ioread(1, port, (void *)addr, sizeof(unsigned char), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while(count--) *buf++ = *(volatile unsigned char *)portp; + } +} + +void _insw(unsigned int port, void * addr, unsigned long count) +{ + unsigned short *buf = addr; + unsigned short *portp; + + if (port >= 0x300 && port < 0x320) { + portp = PORT2ADDR_NE(port); + while (count--) *buf++ = _ne_inw(portp); +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_ioread(0, port, (void *)addr, sizeof(unsigned short), count, 1); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_ioread(1, port, (void *)addr, sizeof(unsigned short), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while (count--) *buf++ = *(volatile unsigned short *)portp; + } +} + +void _insl(unsigned int port, void * addr, unsigned long count) +{ + unsigned long *buf = addr; + unsigned long *portp; + + portp = PORT2ADDR(port); + while (count--) *buf++ = *(volatile unsigned long *)portp; +} + +void _outsb(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned char *buf = addr; + unsigned char *portp; + + if (port >= 0x300 && port < 0x320) { + portp = PORT2ADDR_NE(port); + while (count--) _ne_outb(*buf++, portp); +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite(0, port, (void *)addr, sizeof(unsigned char), count, 1); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_iowrite(1, port, (void *)addr, sizeof(unsigned char), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned char *)portp = *buf++; + } +} + +void _outsw(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned short *buf = addr; + unsigned short *portp; + + if (port >= 0x300 && port < 0x320) { + portp = PORT2ADDR_NE(port); + while (count--) _ne_outw(*buf++, portp); +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32RPCC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite(0, port, (void *)addr, sizeof(unsigned short), count, 1); + } else if (port >= M32R_PCC_IOSTART1 && port <= M32R_PCC_IOEND1) { + pcc_iowrite(1, port, (void *)addr, sizeof(unsigned short), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned short *)portp = *buf++; + } +} + +void _outsl(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned long *buf = addr; + unsigned char *portp; + + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned long *)portp = *buf++; +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/io_oaks32r.c 2004-09-03 00:57:17.320406160 -0700 @@ -0,0 +1,243 @@ +/* + * linux/arch/m32r/kernel/io_oaks32r.c + * + * Typical I/O routines for OAKS32R board. + * + * Copyright (c) 2001-2004 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto, Mamoru Sakugawa + */ + +/* $Id$ */ + +#include +#include +#include +#include + +#define PORT2ADDR(port) _port2addr(port) + +static __inline__ void *_port2addr(unsigned long port) +{ + return (void *)(port + NONCACHE_OFFSET); +} + +static __inline__ void *_port2addr_ne(unsigned long port) +{ + return (void *)((port<<1) + NONCACHE_OFFSET + 0x02000000); +} + +static __inline__ void delay(void) +{ + __asm__ __volatile__ ("push r0; \n\t pop r0;" : : :"memory"); +} + +/* + * NIC I/O function + */ + +#define PORT2ADDR_NE(port) _port2addr_ne(port) + +static __inline__ unsigned char _ne_inb(void *portp) +{ + return *(volatile unsigned char *)(portp+1); +} + +static __inline__ unsigned short _ne_inw(void *portp) +{ + unsigned short tmp; + + tmp = *(unsigned short *)(portp) & 0xff; + tmp |= *(unsigned short *)(portp+2) << 8; + return tmp; +} + +static __inline__ void _ne_insb(void *portp, void * addr, unsigned long count) +{ + unsigned char *buf = addr; + while (count--) *buf++ = *(volatile unsigned char *)(portp+1); +} + +static __inline__ void _ne_outb(unsigned char b, void *portp) +{ + *(volatile unsigned char *)(portp+1) = b; +} + +static __inline__ void _ne_outw(unsigned short w, void *portp) +{ + *(volatile unsigned short *)portp = (w >> 8); + *(volatile unsigned short *)(portp+2) = (w & 0xff); +} + +unsigned char _inb(unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + return _ne_inb(PORT2ADDR_NE(port)); + + return *(volatile unsigned char *)PORT2ADDR(port); +} + +unsigned short _inw(unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + return _ne_inw(PORT2ADDR_NE(port)); + + return *(volatile unsigned short *)PORT2ADDR(port); +} + +unsigned long _inl(unsigned long port) +{ + return *(volatile unsigned long *)PORT2ADDR(port); +} + +unsigned char _inb_p(unsigned long port) +{ + unsigned char v; + + if (port >= 0x300 && port < 0x320) + v = _ne_inb(PORT2ADDR_NE(port)); + else + v = *(volatile unsigned char *)PORT2ADDR(port); + + delay(); + return (v); +} + +unsigned short _inw_p(unsigned long port) +{ + unsigned short v; + + if (port >= 0x300 && port < 0x320) + v = _ne_inw(PORT2ADDR_NE(port)); + else + v = *(volatile unsigned short *)PORT2ADDR(port); + + delay(); + return (v); +} + +unsigned long _inl_p(unsigned long port) +{ + unsigned long v; + + v = *(volatile unsigned long *)PORT2ADDR(port); + delay(); + return (v); +} + +void _outb(unsigned char b, unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + _ne_outb(b, PORT2ADDR_NE(port)); + else + *(volatile unsigned char *)PORT2ADDR(port) = b; +} + +void _outw(unsigned short w, unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + _ne_outw(w, PORT2ADDR_NE(port)); + else + *(volatile unsigned short *)PORT2ADDR(port) = w; +} + +void _outl(unsigned long l, unsigned long port) +{ + *(volatile unsigned long *)PORT2ADDR(port) = l; +} + +void _outb_p(unsigned char b, unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + _ne_outb(b, PORT2ADDR_NE(port)); + else + *(volatile unsigned char *)PORT2ADDR(port) = b; + + delay(); +} + +void _outw_p(unsigned short w, unsigned long port) +{ + if (port >= 0x300 && port < 0x320) + _ne_outw(w, PORT2ADDR_NE(port)); + else + *(volatile unsigned short *)PORT2ADDR(port) = w; + + delay(); +} + +void _outl_p(unsigned long l, unsigned long port) +{ + *(volatile unsigned long *)PORT2ADDR(port) = l; + delay(); +} + +void _insb(unsigned int port, void * addr, unsigned long count) +{ + if (port >= 0x300 && port < 0x320) + _ne_insb(PORT2ADDR_NE(port), addr, count); + else { + unsigned char *buf = addr; + unsigned char *portp = PORT2ADDR(port); + while(count--) *buf++ = *(volatile unsigned char *)portp; + } +} + +void _insw(unsigned int port, void * addr, unsigned long count) +{ + unsigned short *buf = addr; + unsigned short *portp; + + if (port >= 0x300 && port < 0x320) { + portp = PORT2ADDR_NE(port); + while (count--) *buf++ = _ne_inw(portp); + } else { + portp = PORT2ADDR(port); + while (count--) *buf++ = *(volatile unsigned short *)portp; + } +} + +void _insl(unsigned int port, void * addr, unsigned long count) +{ + unsigned long *buf = addr; + unsigned long *portp; + + portp = PORT2ADDR(port); + while (count--) *buf++ = *(volatile unsigned long *)portp; +} + +void _outsb(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned char *buf = addr; + unsigned char *portp; + + if (port >= 0x300 && port < 0x320) { + portp = PORT2ADDR_NE(port); + while (count--) _ne_outb(*buf++, portp); + } else { + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned char *)portp = *buf++; + } +} + +void _outsw(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned short *buf = addr; + unsigned short *portp; + + if (port >= 0x300 && port < 0x320) { + portp = PORT2ADDR_NE(port); + while (count--) _ne_outw(*buf++, portp); + } else { + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned short *)portp = *buf++; + } +} + +void _outsl(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned long *buf = addr; + unsigned char *portp; + + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned long *)portp = *buf++; +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/io_opsput.c 2004-09-03 00:57:17.322405856 -0700 @@ -0,0 +1,377 @@ +/* + * linux/arch/m32r/kernel/io_mappi.c + * + * Typical I/O routines for OPSPUT board. + * + * Copyright (c) 2001, 2002 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto, Takeo Takahashi + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + */ + +#include +#include +#include +#include +#include + +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) +#include + +#define M32R_PCC_IOMAP_SIZE 0x1000 + +#define M32R_PCC_IOSTART0 0x1000 +#define M32R_PCC_IOEND0 (M32R_PCC_IOSTART0 + M32R_PCC_IOMAP_SIZE - 1) + +extern void pcc_ioread_byte(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_ioread_word(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_iowrite_byte(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_iowrite_word(int, unsigned long, void *, size_t, size_t, int); +#endif /* CONFIG_PCMCIA && CONFIG_M32R_CFC */ + +#define PORT2ADDR(port) _port2addr(port) +#define PORT2ADDR_USB(port) _port2addr_usb(port) + +static __inline__ void *_port2addr(unsigned long port) +{ + return (void *)(port + NONCACHE_OFFSET); +} + +/* + * OPSPUT-LAN is located in the extended bus space + * from 0x10000000 to 0x13ffffff on physical address. + * The base address of LAN controller(LAN91C111) is 0x300. + */ +#define LAN_IOSTART 0x300 +#define LAN_IOEND 0x320 +static __inline__ void *_port2addr_ne(unsigned long port) +{ + return (void *)(port + NONCACHE_OFFSET + 0x10000000); +} +static __inline__ void *_port2addr_usb(unsigned long port) +{ + return (void *)((port & 0x0f) + NONCACHE_OFFSET + 0x10303000); +} + +static __inline__ void delay(void) +{ + __asm__ __volatile__ ("push r0; \n\t pop r0;" : : :"memory"); +} + +/* + * NIC I/O function + */ + +#define PORT2ADDR_NE(port) _port2addr_ne(port) + +static __inline__ unsigned char _ne_inb(void *portp) +{ + return *(volatile unsigned char *)portp; +} + +static __inline__ unsigned short _ne_inw(void *portp) +{ + return (unsigned short)le16_to_cpu(*(volatile unsigned short *)portp); +} + +static __inline__ void _ne_insb(void *portp, void * addr, unsigned long count) +{ + unsigned char *buf = (unsigned char *)addr; + + while (count--) *buf++ = _ne_inb(portp); +} + +static __inline__ void _ne_outb(unsigned char b, void *portp) +{ + *(volatile unsigned char *)portp = b; +} + +static __inline__ void _ne_outw(unsigned short w, void *portp) +{ + *(volatile unsigned short *)portp = cpu_to_le16(w); +} + +unsigned char _inb(unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + return _ne_inb(PORT2ADDR_NE(port)); + +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned char b; + pcc_ioread_byte(0, port, &b, sizeof(b), 1, 0); + return b; + } else +#endif + + return *(volatile unsigned char *)PORT2ADDR(port); +} + +unsigned short _inw(unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + return _ne_inw(PORT2ADDR_NE(port)); +#if defined(CONFIG_USB) + else if(port >= 0x340 && port < 0x3a0) + return *(volatile unsigned short *)PORT2ADDR_USB(port); +#endif + +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned short w; + pcc_ioread_word(0, port, &w, sizeof(w), 1, 0); + return w; + } else +#endif + return *(volatile unsigned short *)PORT2ADDR(port); +} + +unsigned long _inl(unsigned long port) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned long l; + pcc_ioread_word(0, port, &l, sizeof(l), 1, 0); + return l; + } else +#endif + return *(volatile unsigned long *)PORT2ADDR(port); +} + +unsigned char _inb_p(unsigned long port) +{ + unsigned char v; + + if (port >= LAN_IOSTART && port < LAN_IOEND) + v = _ne_inb(PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned char b; + pcc_ioread_byte(0, port, &b, sizeof(b), 1, 0); + return b; + } else +#endif + v = *(volatile unsigned char *)PORT2ADDR(port); + + delay(); + return (v); +} + +unsigned short _inw_p(unsigned long port) +{ + unsigned short v; + + if (port >= LAN_IOSTART && port < LAN_IOEND) + v = _ne_inw(PORT2ADDR_NE(port)); + else +#if defined(CONFIG_USB) + if(port >= 0x340 && port < 0x3a0) + return *(volatile unsigned short *)PORT2ADDR_USB(port); + else +#endif + +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + unsigned short w; + pcc_ioread_word(0, port, &w, sizeof(w), 1, 0); + return w; + } else +#endif + v = *(volatile unsigned short *)PORT2ADDR(port); + + delay(); + return (v); +} + +unsigned long _inl_p(unsigned long port) +{ + unsigned long v; + + v = *(volatile unsigned long *)PORT2ADDR(port); + delay(); + return (v); +} + +void _outb(unsigned char b, unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_outb(b, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_byte(0, port, &b, sizeof(b), 1, 0); + } else +#endif + *(volatile unsigned char *)PORT2ADDR(port) = b; +} + +void _outw(unsigned short w, unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_outw(w, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_USB) + if(port >= 0x340 && port < 0x3a0) + *(volatile unsigned short *)PORT2ADDR_USB(port) = w; + else +#endif + +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(0, port, &w, sizeof(w), 1, 0); + } else +#endif + *(volatile unsigned short *)PORT2ADDR(port) = w; +} + +void _outl(unsigned long l, unsigned long port) +{ +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(0, port, &l, sizeof(l), 1, 0); + } else +#endif + *(volatile unsigned long *)PORT2ADDR(port) = l; +} + +void _outb_p(unsigned char b, unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_outb(b, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_byte(0, port, &b, sizeof(b), 1, 0); + } else +#endif + *(volatile unsigned char *)PORT2ADDR(port) = b; + + delay(); +} + +void _outw_p(unsigned short w, unsigned long port) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_outw(w, PORT2ADDR_NE(port)); + else +#if defined(CONFIG_USB) + if(port >= 0x340 && port < 0x3a0) + *(volatile unsigned short *)PORT2ADDR_USB(port) = w; + else +#endif + +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(0, port, &w, sizeof(w), 1, 0); + } else +#endif + *(volatile unsigned short *)PORT2ADDR(port) = w; + + delay(); +} + +void _outl_p(unsigned long l, unsigned long port) +{ + *(volatile unsigned long *)PORT2ADDR(port) = l; + delay(); +} + +void _insb(unsigned int port, void * addr, unsigned long count) +{ + if (port >= LAN_IOSTART && port < LAN_IOEND) + _ne_insb(PORT2ADDR_NE(port), addr, count); +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_ioread_byte(0, port, (void *)addr, sizeof(unsigned char), count, 1); + } +#endif + else { + unsigned char *buf = addr; + unsigned char *portp = PORT2ADDR(port); + while(count--) *buf++ = *(volatile unsigned char *)portp; + } +} + +void _insw(unsigned int port, void * addr, unsigned long count) +{ + unsigned short *buf = addr; + unsigned short *portp; + + if (port >= LAN_IOSTART && port < LAN_IOEND) { + /* + * This portion is only used by smc91111.c to read data + * from the DATA_REG. Do not swap the data. + */ + portp = PORT2ADDR_NE(port); + while (count--) *buf++ = *(volatile unsigned short *)portp; +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_ioread_word(9, port, (void *)addr, sizeof(unsigned short), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while (count--) *buf++ = *(volatile unsigned short *)portp; + } +} + +void _insl(unsigned int port, void * addr, unsigned long count) +{ + unsigned long *buf = addr; + unsigned long *portp; + + portp = PORT2ADDR(port); + while (count--) *buf++ = *(volatile unsigned long *)portp; +} + +void _outsb(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned char *buf = addr; + unsigned char *portp; + + if (port >= LAN_IOSTART && port < LAN_IOEND) { + portp = PORT2ADDR_NE(port); + while (count--) _ne_outb(*buf++, portp); +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_byte(0, port, (void *)addr, sizeof(unsigned char), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned char *)portp = *buf++; + } +} + +void _outsw(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned short *buf = addr; + unsigned short *portp; + + if (port >= LAN_IOSTART && port < LAN_IOEND) { + /* + * This portion is only used by smc91111.c to write data + * into the DATA_REG. Do not swap the data. + */ + portp = PORT2ADDR_NE(port); + while(count--) *(volatile unsigned short *)portp = *buf++; +#if defined(CONFIG_PCMCIA) && defined(CONFIG_M32R_CFC) + } else if (port >= M32R_PCC_IOSTART0 && port <= M32R_PCC_IOEND0) { + pcc_iowrite_word(9, port, (void *)addr, sizeof(unsigned short), count, 1); +#endif + } else { + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned short *)portp = *buf++; + } +} + +void _outsl(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned long *buf = addr; + unsigned char *portp; + + portp = PORT2ADDR(port); + while(count--) *(volatile unsigned long *)portp = *buf++; +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/io_usrv.c 2004-09-03 00:57:17.323405704 -0700 @@ -0,0 +1,247 @@ +/* + * linux/arch/m32r/kernel/io_usrv.c + * + * Typical I/O routines for uServer board. + * + * Copyright (c) 2001 - 2003 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto, Takeo Takahashi + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + */ + +#include +#include +#include +#include + +#include +#include "../drivers/m32r_cfc.h" + +extern void pcc_ioread_byte(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_ioread_word(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_iowrite_byte(int, unsigned long, void *, size_t, size_t, int); +extern void pcc_iowrite_word(int, unsigned long, void *, size_t, size_t, int); +#define CFC_IOSTART CFC_IOPORT_BASE +#define CFC_IOEND (CFC_IOSTART + (M32R_PCC_MAPSIZE * M32R_MAX_PCC) - 1) + +#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE) +#define UART0_REGSTART 0x04c20000 +#define UART1_REGSTART 0x04c20100 +#define UART_IOMAP_SIZE 8 +#define UART0_IOSTART 0x3f8 +#define UART0_IOEND (UART0_IOSTART + UART_IOMAP_SIZE - 1) +#define UART1_IOSTART 0x2f8 +#define UART1_IOEND (UART1_IOSTART + UART_IOMAP_SIZE - 1) +#endif /* CONFIG_SERIAL_8250 || CONFIG_SERIAL_8250_MODULE */ + +#define PORT2ADDR(port) _port2addr(port) + +static __inline__ void *_port2addr(unsigned long port) +{ +#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE) + if (port >= UART0_IOSTART && port <= UART0_IOEND) + port = ((port - UART0_IOSTART) << 1) + UART0_REGSTART; + else if (port >= UART1_IOSTART && port <= UART1_IOEND) + port = ((port - UART1_IOSTART) << 1) + UART1_REGSTART; +#endif /* CONFIG_SERIAL_8250 || CONFIG_SERIAL_8250_MODULE */ + return (void *)(port + NONCACHE_OFFSET); +} + +static __inline__ void delay(void) +{ + __asm__ __volatile__ ("push r0; \n\t pop r0;" : : :"memory"); +} + +unsigned char _inb(unsigned long port) +{ + if (port >= CFC_IOSTART && port <= CFC_IOEND) { + unsigned char b; + pcc_ioread_byte(0, port, &b, sizeof(b), 1, 0); + return b; + } else + return *(volatile unsigned char *)PORT2ADDR(port); +} + +unsigned short _inw(unsigned long port) +{ + if (port >= CFC_IOSTART && port <= CFC_IOEND) { + unsigned short w; + pcc_ioread_word(0, port, &w, sizeof(w), 1, 0); + return w; + } else + return *(volatile unsigned short *)PORT2ADDR(port); +} + +unsigned long _inl(unsigned long port) +{ + if (port >= CFC_IOSTART && port <= CFC_IOEND) { + unsigned long l; + pcc_ioread_word(0, port, &l, sizeof(l), 1, 0); + return l; + } else + return *(volatile unsigned long *)PORT2ADDR(port); +} + +unsigned char _inb_p(unsigned long port) +{ + unsigned char b; + + if (port >= CFC_IOSTART && port <= CFC_IOEND) { + pcc_ioread_byte(0, port, &b, sizeof(b), 1, 0); + return b; + } else { + b = *(volatile unsigned char *)PORT2ADDR(port); + delay(); + return b; + } +} + +unsigned short _inw_p(unsigned long port) +{ + unsigned short w; + + if (port >= CFC_IOSTART && port <= CFC_IOEND) { + pcc_ioread_word(0, port, &w, sizeof(w), 1, 0); + return w; + } else { + w = *(volatile unsigned short *)PORT2ADDR(port); + delay(); + return w; + } +} + +unsigned long _inl_p(unsigned long port) +{ + unsigned long v; + + v = *(volatile unsigned long *)PORT2ADDR(port); + delay(); + + return v; +} + +void _outb(unsigned char b, unsigned long port) +{ + if (port >= CFC_IOSTART && port <= CFC_IOEND) + pcc_iowrite_byte(0, port, &b, sizeof(b), 1, 0); + else + *(volatile unsigned char *)PORT2ADDR(port) = b; +} + +void _outw(unsigned short w, unsigned long port) +{ + if (port >= CFC_IOSTART && port <= CFC_IOEND) + pcc_iowrite_word(0, port, &w, sizeof(w), 1, 0); + else + *(volatile unsigned short *)PORT2ADDR(port) = w; +} + +void _outl(unsigned long l, unsigned long port) +{ + if (port >= CFC_IOSTART && port <= CFC_IOEND) + pcc_iowrite_word(0, port, &l, sizeof(l), 1, 0); + else + *(volatile unsigned long *)PORT2ADDR(port) = l; +} + +void _outb_p(unsigned char b, unsigned long port) +{ + if (port >= CFC_IOSTART && port <= CFC_IOEND) + pcc_iowrite_byte(0, port, &b, sizeof(b), 1, 0); + else + *(volatile unsigned char *)PORT2ADDR(port) = b; + delay(); +} + +void _outw_p(unsigned short w, unsigned long port) +{ + if (port >= CFC_IOSTART && port <= CFC_IOEND) + pcc_iowrite_word(0, port, &w, sizeof(w), 1, 0); + else + *(volatile unsigned short *)PORT2ADDR(port) = w; + delay(); +} + +void _outl_p(unsigned long l, unsigned long port) +{ + *(volatile unsigned long *)PORT2ADDR(port) = l; + delay(); +} + +void _insb(unsigned int port, void * addr, unsigned long count) +{ + if (port >= CFC_IOSTART && port <= CFC_IOEND) + pcc_ioread_byte(0, port, addr, sizeof(unsigned char), count, 1); + else { + unsigned char *buf = addr; + unsigned char *portp = PORT2ADDR(port); + while(count--) *buf++ = *(volatile unsigned char *)portp; + } +} + +void _insw(unsigned int port, void * addr, unsigned long count) +{ + unsigned short *buf = addr; + unsigned short *portp; + + if (port >= CFC_IOSTART && port <= CFC_IOEND) + pcc_ioread_word(0, port, addr, sizeof(unsigned short), count, + 1); + else { + portp = PORT2ADDR(port); + while (count--) *buf++ = *(volatile unsigned short *)portp; + } +} + +void _insl(unsigned int port, void * addr, unsigned long count) +{ + unsigned long *buf = addr; + unsigned long *portp; + + portp = PORT2ADDR(port); + while (count--) + *buf++ = *(volatile unsigned long *)portp; +} + +void _outsb(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned char *buf = addr; + unsigned char *portp; + + if (port >= CFC_IOSTART && port <= CFC_IOEND) + pcc_iowrite_byte(0, port, (void *)addr, sizeof(unsigned char), + count, 1); + else { + portp = PORT2ADDR(port); + while (count--) + *(volatile unsigned char *)portp = *buf++; + } +} + +void _outsw(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned short *buf = addr; + unsigned short *portp; + + if (port >= CFC_IOSTART && port <= CFC_IOEND) + pcc_iowrite_word(0, port, (void *)addr, sizeof(unsigned short), + count, 1); + else { + portp = PORT2ADDR(port); + while (count--) + *(volatile unsigned short *)portp = *buf++; + } +} + +void _outsl(unsigned int port, const void * addr, unsigned long count) +{ + const unsigned long *buf = addr; + unsigned char *portp; + + portp = PORT2ADDR(port); + while (count--) + *(volatile unsigned long *)portp = *buf++; +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/irq.c 2004-09-03 00:57:18.719193512 -0700 @@ -0,0 +1,1018 @@ +/* + * linux/arch/m32r/kernel/irq.c + * + * Copyright (c) 2003, 2004 Hitoshi Yamamoto + * + * Taken from i386 2.6.4 version. + */ + +/* + * linux/arch/i386/kernel/irq.c + * + * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar + * + * This file contains the code used by various IRQ handling routines: + * asking for different IRQ's should be done through these routines + * instead of just grabbing them. Thus setups with different IRQ numbers + * shouldn't result in any weird surprises, and installing new handlers + * should be easier. + */ + +/* + * (mostly architecture independent, will move to kernel/irq.c in 2.5.) + * + * IRQs are in fact implemented a bit like signal handlers for the kernel. + * Naturally it's not a 1:1 relation, but there are similarities. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Linux has a controller-independent x86 interrupt architecture. + * every controller has a 'controller-template', that is used + * by the main code to do the right thing. Each driver-visible + * interrupt source is transparently wired to the apropriate + * controller. Thus drivers need not be aware of the + * interrupt-controller. + * + * Various interrupt controllers we handle: 8259 PIC, SMP IO-APIC, + * PIIX4's internal 8259 PIC and SGI's Visual Workstation Cobalt (IO-)APIC. + * (IO-APICs assumed to be messaging to Pentium local-APICs) + * + * the code is designed to be easily extended with new/different + * interrupt controllers, without having to do assembly magic. + */ + +/* + * Controller mappings for all interrupt sources: + */ +irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = { + [0 ... NR_IRQS-1] = { + .handler = &no_irq_type, + .lock = SPIN_LOCK_UNLOCKED + } +}; + +static void register_irq_proc (unsigned int irq); + +/* + * Special irq handlers. + */ + +irqreturn_t no_action(int cpl, void *dev_id, struct pt_regs *regs) +{ return IRQ_NONE; } + +/* + * Generic no controller code + */ + +static void enable_none(unsigned int irq) { } +static unsigned int startup_none(unsigned int irq) { return 0; } +static void disable_none(unsigned int irq) { } +static void ack_none(unsigned int irq) +{ +/* + * 'what should we do if we get a hw irq event on an illegal vector'. + * each architecture has to answer this themselves, it doesn't deserve + * a generic callback i think. + */ + printk("unexpected IRQ trap at vector %02x\n", irq); +} + +/* startup is the same as "enable", shutdown is same as "disable" */ +#define shutdown_none disable_none +#define end_none enable_none + +struct hw_interrupt_type no_irq_type = { + "none", + startup_none, + shutdown_none, + enable_none, + disable_none, + ack_none, + end_none +}; + +atomic_t irq_err_count; +atomic_t irq_mis_count; + +/* + * Generic, controller-independent functions: + */ + +int show_interrupts(struct seq_file *p, void *v) +{ + int i = *(loff_t *) v, j; + struct irqaction * action; + unsigned long flags; + + if (i == 0) { + seq_printf(p, " "); + for (j=0; jtypename); + seq_printf(p, " %s", action->name); + + for (action=action->next; action; action = action->next) + seq_printf(p, ", %s", action->name); + + seq_putc(p, '\n'); +skip: + spin_unlock_irqrestore(&irq_desc[i].lock, flags); + } else if (i == NR_IRQS) { + seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count)); + seq_printf(p, "MIS: %10u\n", atomic_read(&irq_mis_count)); + } + return 0; +} + +#ifdef CONFIG_SMP +inline void synchronize_irq(unsigned int irq) +{ + while (irq_desc[irq].status & IRQ_INPROGRESS) + cpu_relax(); +} +#endif + +/* + * This should really return information about whether + * we should do bottom half handling etc. Right now we + * end up _always_ checking the bottom half, which is a + * waste of time and is not what some drivers would + * prefer. + */ +int handle_IRQ_event(unsigned int irq, + struct pt_regs *regs, struct irqaction *action) +{ + int status = 1; /* Force the "do bottom halves" bit */ + int retval = 0; + + if (!(action->flags & SA_INTERRUPT)) + local_irq_enable(); + + do { + status |= action->flags; + retval |= action->handler(irq, action->dev_id, regs); + action = action->next; + } while (action); + if (status & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + local_irq_disable(); + return retval; +} + +static void __report_bad_irq(int irq, irq_desc_t *desc, irqreturn_t action_ret) +{ + struct irqaction *action; + + if (action_ret != IRQ_HANDLED && action_ret != IRQ_NONE) { + printk(KERN_ERR "irq event %d: bogus return value %x\n", + irq, action_ret); + } else { + printk(KERN_ERR "irq %d: nobody cared!\n", irq); + } + dump_stack(); + printk(KERN_ERR "handlers:\n"); + action = desc->action; + do { + printk(KERN_ERR "[<%p>]", action->handler); + print_symbol(" (%s)", + (unsigned long)action->handler); + printk("\n"); + action = action->next; + } while (action); +} + +static void report_bad_irq(int irq, irq_desc_t *desc, irqreturn_t action_ret) +{ + static int count = 100; + + if (count) { + count--; + __report_bad_irq(irq, desc, action_ret); + } +} + +static int noirqdebug; + +static int __init noirqdebug_setup(char *str) +{ + noirqdebug = 1; + printk("IRQ lockup detection disabled\n"); + return 1; +} + +__setup("noirqdebug", noirqdebug_setup); + +/* + * If 99,900 of the previous 100,000 interrupts have not been handled then + * assume that the IRQ is stuck in some manner. Drop a diagnostic and try to + * turn the IRQ off. + * + * (The other 100-of-100,000 interrupts may have been a correctly-functioning + * device sharing an IRQ with the failing one) + * + * Called under desc->lock + */ +static void note_interrupt(int irq, irq_desc_t *desc, irqreturn_t action_ret) +{ + if (action_ret != IRQ_HANDLED) { + desc->irqs_unhandled++; + if (action_ret != IRQ_NONE) + report_bad_irq(irq, desc, action_ret); + } + + desc->irq_count++; + if (desc->irq_count < 100000) + return; + + desc->irq_count = 0; + if (desc->irqs_unhandled > 99900) { + /* + * The interrupt is stuck + */ + __report_bad_irq(irq, desc, action_ret); + /* + * Now kill the IRQ + */ + printk(KERN_EMERG "Disabling IRQ #%d\n", irq); + desc->status |= IRQ_DISABLED; + desc->handler->disable(irq); + } + desc->irqs_unhandled = 0; +} + +/* + * Generic enable/disable code: this just calls + * down into the PIC-specific version for the actual + * hardware disable after having gotten the irq + * controller lock. + */ + +/** + * disable_irq_nosync - disable an irq without waiting + * @irq: Interrupt to disable + * + * Disable the selected interrupt line. Disables and Enables are + * nested. + * Unlike disable_irq(), this function does not ensure existing + * instances of the IRQ handler have completed before returning. + * + * This function may be called from IRQ context. + */ + +inline void disable_irq_nosync(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + if (!desc->depth++) { + desc->status |= IRQ_DISABLED; + desc->handler->disable(irq); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +/** + * disable_irq - disable an irq and wait for completion + * @irq: Interrupt to disable + * + * Disable the selected interrupt line. Enables and Disables are + * nested. + * This function waits for any pending IRQ handlers for this interrupt + * to complete before returning. If you use this function while + * holding a resource the IRQ handler may need you will deadlock. + * + * This function may be called - with care - from IRQ context. + */ + +void disable_irq(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + disable_irq_nosync(irq); + if (desc->action) + synchronize_irq(irq); +} + +/** + * enable_irq - enable handling of an irq + * @irq: Interrupt to enable + * + * Undoes the effect of one call to disable_irq(). If this + * matches the last disable, processing of interrupts on this + * IRQ line is re-enabled. + * + * This function may be called from IRQ context. + */ + +void enable_irq(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + switch (desc->depth) { + case 1: { + unsigned int status = desc->status & ~IRQ_DISABLED; + desc->status = status; + if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) { + desc->status = status | IRQ_REPLAY; + hw_resend_irq(desc->handler,irq); + } + desc->handler->enable(irq); + /* fall-through */ + } + default: + desc->depth--; + break; + case 0: + printk("enable_irq(%u) unbalanced from %p\n", irq, + __builtin_return_address(0)); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +/* + * do_IRQ handles all normal device IRQ's (the special + * SMP cross-CPU interrupts have their own specific + * handlers). + */ +asmlinkage unsigned int do_IRQ(int irq, struct pt_regs *regs) +{ + /* + * We ack quickly, we don't want the irq controller + * thinking we're snobs just because some other CPU has + * disabled global interrupts (we have already done the + * INT_ACK cycles, it's too late to try to pretend to the + * controller that we aren't taking the interrupt). + * + * 0 return value means that this irq is already being + * handled by some other CPU. (or is disabled) + */ + irq_desc_t *desc = irq_desc + irq; + struct irqaction * action; + unsigned int status; + + irq_enter(); + +#ifdef CONFIG_DEBUG_STACKOVERFLOW + /* FIXME M32R */ +#endif + kstat_this_cpu.irqs[irq]++; + spin_lock(&desc->lock); + desc->handler->ack(irq); + /* + REPLAY is when Linux resends an IRQ that was dropped earlier + WAITING is used by probe to mark irqs that are being tested + */ + status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); + status |= IRQ_PENDING; /* we _want_ to handle it */ + + /* + * If the IRQ is disabled for whatever reason, we cannot + * use the action we have. + */ + action = NULL; + if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) { + action = desc->action; + status &= ~IRQ_PENDING; /* we commit to handling */ + status |= IRQ_INPROGRESS; /* we are handling it */ + } + desc->status = status; + + /* + * If there is no IRQ handler or it was disabled, exit early. + Since we set PENDING, if another processor is handling + a different instance of this same irq, the other processor + will take care of it. + */ + if (unlikely(!action)) + goto out; + + /* + * Edge triggered interrupts need to remember + * pending events. + * This applies to any hw interrupts that allow a second + * instance of the same irq to arrive while we are in do_IRQ + * or in the handler. But the code here only handles the _second_ + * instance of the irq, not the third or fourth. So it is mostly + * useful for irq hardware that does not mask cleanly in an + * SMP environment. + */ + for (;;) { + irqreturn_t action_ret; + + spin_unlock(&desc->lock); + action_ret = handle_IRQ_event(irq, regs, action); + spin_lock(&desc->lock); + if (!noirqdebug) + note_interrupt(irq, desc, action_ret); + if (likely(!(desc->status & IRQ_PENDING))) + break; + desc->status &= ~IRQ_PENDING; + } + desc->status &= ~IRQ_INPROGRESS; + +out: + /* + * The ->end() handler has to deal with interrupts which got + * disabled while the handler was running. + */ + desc->handler->end(irq); + spin_unlock(&desc->lock); + + irq_exit(); + +#if defined(CONFIG_SMP) + if (irq == M32R_IRQ_MFT2) + smp_send_timer(); +#endif /* CONFIG_SMP */ + + return 1; +} + +int can_request_irq(unsigned int irq, unsigned long irqflags) +{ + struct irqaction *action; + + if (irq >= NR_IRQS) + return 0; + action = irq_desc[irq].action; + if (action) { + if (irqflags & action->flags & SA_SHIRQ) + action = NULL; + } + return !action; +} + +/** + * request_irq - allocate an interrupt line + * @irq: Interrupt line to allocate + * @handler: Function to be called when the IRQ occurs + * @irqflags: Interrupt type flags + * @devname: An ascii name for the claiming device + * @dev_id: A cookie passed back to the handler function + * + * This call allocates interrupt resources and enables the + * interrupt line and IRQ handling. From the point this + * call is made your handler function may be invoked. Since + * your handler function must clear any interrupt the board + * raises, you must take care both to initialise your hardware + * and to set up the interrupt handler in the right order. + * + * Dev_id must be globally unique. Normally the address of the + * device data structure is used as the cookie. Since the handler + * receives this value it makes sense to use it. + * + * If your interrupt is shared you must pass a non NULL dev_id + * as this is required when freeing the interrupt. + * + * Flags: + * + * SA_SHIRQ Interrupt is shared + * + * SA_INTERRUPT Disable local interrupts while processing + * + * SA_SAMPLE_RANDOM The interrupt can be used for entropy + * + */ + +int request_irq(unsigned int irq, + irqreturn_t (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, + const char * devname, + void *dev_id) +{ + int retval; + struct irqaction * action; + +#if 1 + /* + * Sanity-check: shared interrupts should REALLY pass in + * a real dev-ID, otherwise we'll have trouble later trying + * to figure out which interrupt is which (messes up the + * interrupt freeing logic etc). + */ + if (irqflags & SA_SHIRQ) { + if (!dev_id) + printk("Bad boy: %s (at 0x%x) called us without a dev_id!\n", devname, (&irq)[-1]); + } +#endif + + if (irq >= NR_IRQS) + return -EINVAL; + if (!handler) + return -EINVAL; + + action = (struct irqaction *) + kmalloc(sizeof(struct irqaction), GFP_ATOMIC); + if (!action) + return -ENOMEM; + + action->handler = handler; + action->flags = irqflags; + cpus_clear(action->mask); + action->name = devname; + action->next = NULL; + action->dev_id = dev_id; + + retval = setup_irq(irq, action); + if (retval) + kfree(action); + return retval; +} + +EXPORT_SYMBOL(request_irq); + +/** + * free_irq - free an interrupt + * @irq: Interrupt line to free + * @dev_id: Device identity to free + * + * Remove an interrupt handler. The handler is removed and if the + * interrupt line is no longer in use by any driver it is disabled. + * On a shared IRQ the caller must ensure the interrupt is disabled + * on the card it drives before calling this function. The function + * does not return until any executing interrupts for this IRQ + * have completed. + * + * This function must not be called from interrupt context. + */ + +void free_irq(unsigned int irq, void *dev_id) +{ + irq_desc_t *desc; + struct irqaction **p; + unsigned long flags; + + if (irq >= NR_IRQS) + return; + + desc = irq_desc + irq; + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; + for (;;) { + struct irqaction * action = *p; + if (action) { + struct irqaction **pp = p; + p = &action->next; + if (action->dev_id != dev_id) + continue; + + /* Found it - now remove it from the list of entries */ + *pp = action->next; + if (!desc->action) { + desc->status |= IRQ_DISABLED; + desc->handler->shutdown(irq); + } + spin_unlock_irqrestore(&desc->lock,flags); + + /* Wait to make sure it's not being used on another CPU */ + synchronize_irq(irq); + kfree(action); + return; + } + printk("Trying to free free IRQ%d\n",irq); + spin_unlock_irqrestore(&desc->lock,flags); + return; + } +} + +EXPORT_SYMBOL(free_irq); + +/* + * IRQ autodetection code.. + * + * This depends on the fact that any interrupt that + * comes in on to an unassigned handler will get stuck + * with "IRQ_WAITING" cleared and the interrupt + * disabled. + */ + +static DECLARE_MUTEX(probe_sem); + +/** + * probe_irq_on - begin an interrupt autodetect + * + * Commence probing for an interrupt. The interrupts are scanned + * and a mask of potential interrupt lines is returned. + * + */ + +unsigned long probe_irq_on(void) +{ + unsigned int i; + irq_desc_t *desc; + unsigned long val; + unsigned long delay; + + down(&probe_sem); + /* + * something may have generated an irq long ago and we want to + * flush such a longstanding irq before considering it as spurious. + */ + for (i = NR_IRQS-1; i > 0; i--) { + desc = irq_desc + i; + + spin_lock_irq(&desc->lock); + if (!irq_desc[i].action) + irq_desc[i].handler->startup(i); + spin_unlock_irq(&desc->lock); + } + + /* Wait for longstanding interrupts to trigger. */ + for (delay = jiffies + HZ/50; time_after(delay, jiffies); ) + /* about 20ms delay */ barrier(); + + /* + * enable any unassigned irqs + * (we must startup again here because if a longstanding irq + * happened in the previous stage, it may have masked itself) + */ + for (i = NR_IRQS-1; i > 0; i--) { + desc = irq_desc + i; + + spin_lock_irq(&desc->lock); + if (!desc->action) { + desc->status |= IRQ_AUTODETECT | IRQ_WAITING; + if (desc->handler->startup(i)) + desc->status |= IRQ_PENDING; + } + spin_unlock_irq(&desc->lock); + } + + /* + * Wait for spurious interrupts to trigger + */ + for (delay = jiffies + HZ/10; time_after(delay, jiffies); ) + /* about 100ms delay */ barrier(); + + /* + * Now filter out any obviously spurious interrupts + */ + val = 0; + for (i = 0; i < NR_IRQS; i++) { + irq_desc_t *desc = irq_desc + i; + unsigned int status; + + spin_lock_irq(&desc->lock); + status = desc->status; + + if (status & IRQ_AUTODETECT) { + /* It triggered already - consider it spurious. */ + if (!(status & IRQ_WAITING)) { + desc->status = status & ~IRQ_AUTODETECT; + desc->handler->shutdown(i); + } else + if (i < 32) + val |= 1 << i; + } + spin_unlock_irq(&desc->lock); + } + + return val; +} + +EXPORT_SYMBOL(probe_irq_on); + +/* + * Return a mask of triggered interrupts (this + * can handle only legacy ISA interrupts). + */ + +/** + * probe_irq_mask - scan a bitmap of interrupt lines + * @val: mask of interrupts to consider + * + * Scan the ISA bus interrupt lines and return a bitmap of + * active interrupts. The interrupt probe logic state is then + * returned to its previous value. + * + * Note: we need to scan all the irq's even though we will + * only return ISA irq numbers - just so that we reset them + * all to a known state. + */ +unsigned int probe_irq_mask(unsigned long val) +{ + int i; + unsigned int mask; + + mask = 0; + for (i = 0; i < NR_IRQS; i++) { + irq_desc_t *desc = irq_desc + i; + unsigned int status; + + spin_lock_irq(&desc->lock); + status = desc->status; + + if (status & IRQ_AUTODETECT) { + if (i < 16 && !(status & IRQ_WAITING)) + mask |= 1 << i; + + desc->status = status & ~IRQ_AUTODETECT; + desc->handler->shutdown(i); + } + spin_unlock_irq(&desc->lock); + } + up(&probe_sem); + + return mask & val; +} + +/* + * Return the one interrupt that triggered (this can + * handle any interrupt source). + */ + +/** + * probe_irq_off - end an interrupt autodetect + * @val: mask of potential interrupts (unused) + * + * Scans the unused interrupt lines and returns the line which + * appears to have triggered the interrupt. If no interrupt was + * found then zero is returned. If more than one interrupt is + * found then minus the first candidate is returned to indicate + * their is doubt. + * + * The interrupt probe logic state is returned to its previous + * value. + * + * BUGS: When used in a module (which arguably shouldnt happen) + * nothing prevents two IRQ probe callers from overlapping. The + * results of this are non-optimal. + */ + +int probe_irq_off(unsigned long val) +{ + int i, irq_found, nr_irqs; + + nr_irqs = 0; + irq_found = 0; + for (i = 0; i < NR_IRQS; i++) { + irq_desc_t *desc = irq_desc + i; + unsigned int status; + + spin_lock_irq(&desc->lock); + status = desc->status; + + if (status & IRQ_AUTODETECT) { + if (!(status & IRQ_WAITING)) { + if (!nr_irqs) + irq_found = i; + nr_irqs++; + } + desc->status = status & ~IRQ_AUTODETECT; + desc->handler->shutdown(i); + } + spin_unlock_irq(&desc->lock); + } + up(&probe_sem); + + if (nr_irqs > 1) + irq_found = -irq_found; + return irq_found; +} + +EXPORT_SYMBOL(probe_irq_off); + +/* this was setup_x86_irq but it seems pretty generic */ +int setup_irq(unsigned int irq, struct irqaction * new) +{ + int shared = 0; + unsigned long flags; + struct irqaction *old, **p; + irq_desc_t *desc = irq_desc + irq; + + if (desc->handler == &no_irq_type) + return -ENOSYS; + /* + * Some drivers like serial.c use request_irq() heavily, + * so we have to be careful not to interfere with a + * running system. + */ + if (new->flags & SA_SAMPLE_RANDOM) { + /* + * This function might sleep, we want to call it first, + * outside of the atomic block. + * Yes, this might clear the entropy pool if the wrong + * driver is attempted to be loaded, without actually + * installing a new handler, but is this really a problem, + * only the sysadmin is able to do this. + */ + rand_initialize_irq(irq); + } + + /* + * The following block of code has to be executed atomically + */ + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; + if ((old = *p) != NULL) { + /* Can't share interrupts unless both agree to */ + if (!(old->flags & new->flags & SA_SHIRQ)) { + spin_unlock_irqrestore(&desc->lock,flags); + return -EBUSY; + } + + /* add new interrupt at end of irq queue */ + do { + p = &old->next; + old = *p; + } while (old); + shared = 1; + } + + *p = new; + + if (!shared) { + desc->depth = 0; + desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING | IRQ_INPROGRESS); + desc->handler->startup(irq); + } + spin_unlock_irqrestore(&desc->lock,flags); + + register_irq_proc(irq); + return 0; +} + +static struct proc_dir_entry * root_irq_dir; +static struct proc_dir_entry * irq_dir [NR_IRQS]; + +#ifdef CONFIG_SMP + +static struct proc_dir_entry *smp_affinity_entry[NR_IRQS]; + +cpumask_t irq_affinity[NR_IRQS] = { [0 ... NR_IRQS-1] = CPU_MASK_ALL }; + +static int irq_affinity_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = cpumask_scnprintf(page, count, irq_affinity[(long)data]); + if (count - len < 2) + return -EINVAL; + len += sprintf(page + len, "\n"); + return len; +} + +static int irq_affinity_write_proc(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + int irq = (long)data, full_count = count, err; + cpumask_t new_value, tmp; + + if (!irq_desc[irq].handler->set_affinity) + return -EIO; + + err = cpumask_parse(buffer, count, new_value); + if (err) + return err; + + /* + * Do not allow disabling IRQs completely - it's a too easy + * way to make the system unusable accidentally :-) At least + * one online CPU still has to be targeted. + */ + cpus_and(tmp, new_value, cpu_online_map); + if (cpus_empty(tmp)) + return -EINVAL; + + irq_affinity[irq] = new_value; + irq_desc[irq].handler->set_affinity(irq, + cpumask_of_cpu(first_cpu(new_value))); + + return full_count; +} + +#endif + +static int prof_cpu_mask_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = cpumask_scnprintf(page, count, *(cpumask_t *)data); + if (count - len < 2) + return -EINVAL; + len += sprintf(page + len, "\n"); + return len; +} + +static int prof_cpu_mask_write_proc (struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + cpumask_t *mask = (cpumask_t *)data; + unsigned long full_count = count, err; + cpumask_t new_value; + + err = cpumask_parse(buffer, count, new_value); + if (err) + return err; + + *mask = new_value; + return full_count; +} + +#define MAX_NAMELEN 10 + +static void register_irq_proc (unsigned int irq) +{ + char name [MAX_NAMELEN]; + + if (!root_irq_dir || (irq_desc[irq].handler == &no_irq_type) || + irq_dir[irq]) + return; + + memset(name, 0, MAX_NAMELEN); + sprintf(name, "%d", irq); + + /* create /proc/irq/1234 */ + irq_dir[irq] = proc_mkdir(name, root_irq_dir); + +#ifdef CONFIG_SMP + { + struct proc_dir_entry *entry; + + /* create /proc/irq/1234/smp_affinity */ + entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); + + if (entry) { + entry->nlink = 1; + entry->data = (void *)(long)irq; + entry->read_proc = irq_affinity_read_proc; + entry->write_proc = irq_affinity_write_proc; + } + + smp_affinity_entry[irq] = entry; + } +#endif +} + +unsigned long prof_cpu_mask = -1; + +void init_irq_proc (void) +{ + struct proc_dir_entry *entry; + int i; + + /* create /proc/irq */ + root_irq_dir = proc_mkdir("irq", NULL); + + /* create /proc/irq/prof_cpu_mask */ + entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); + + if (!entry) + return; + + entry->nlink = 1; + entry->data = (void *)&prof_cpu_mask; + entry->read_proc = prof_cpu_mask_read_proc; + entry->write_proc = prof_cpu_mask_write_proc; + + /* + * Create entries for all existing IRQs. + */ + for (i = 0; i < NR_IRQS; i++) + register_irq_proc(i); +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/m32r_ksyms.c 2004-09-03 00:57:17.328404944 -0700 @@ -0,0 +1,143 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern void dump_thread(struct pt_regs *, struct user *); + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_HD) || defined(CONFIG_BLK_DEV_IDE_MODULE) || defined(CONFIG_BLK_DEV_HD_MODULE) +extern struct drive_info_struct drive_info; +EXPORT_SYMBOL(drive_info); +#endif + +/* platform dependent support */ +EXPORT_SYMBOL(boot_cpu_data); +EXPORT_SYMBOL(dump_thread); +EXPORT_SYMBOL(dump_fpu); +EXPORT_SYMBOL(__ioremap); +EXPORT_SYMBOL(iounmap); +EXPORT_SYMBOL(enable_irq); +EXPORT_SYMBOL(disable_irq); +EXPORT_SYMBOL(disable_irq_nosync); +EXPORT_SYMBOL(kernel_thread); +EXPORT_SYMBOL(__down); +EXPORT_SYMBOL(__down_interruptible); +EXPORT_SYMBOL(__up); +EXPORT_SYMBOL(__down_trylock); + +/* Networking helper routines. */ +EXPORT_SYMBOL(csum_partial_copy); +/* Delay loops */ +EXPORT_SYMBOL(__udelay); +EXPORT_SYMBOL(__delay); +EXPORT_SYMBOL(__const_udelay); + +EXPORT_SYMBOL_NOVERS(__get_user_1); +EXPORT_SYMBOL_NOVERS(__get_user_2); +EXPORT_SYMBOL_NOVERS(__get_user_4); + +EXPORT_SYMBOL(strpbrk); +EXPORT_SYMBOL(strstr); + +EXPORT_SYMBOL(strncpy_from_user); +EXPORT_SYMBOL(__strncpy_from_user); +EXPORT_SYMBOL(clear_user); +EXPORT_SYMBOL(__clear_user); +EXPORT_SYMBOL(__generic_copy_from_user); +EXPORT_SYMBOL(__generic_copy_to_user); +EXPORT_SYMBOL(strnlen_user); + +#ifdef CONFIG_SMP +#ifdef CONFIG_CHIP_M32700_TS1 +extern void *dcache_dummy; +EXPORT_SYMBOL(dcache_dummy); +#endif +EXPORT_SYMBOL(cpu_data); +EXPORT_SYMBOL(cpu_online_map); +EXPORT_SYMBOL(cpu_callout_map); + +/* Global SMP stuff */ +EXPORT_SYMBOL(synchronize_irq); +EXPORT_SYMBOL(smp_call_function); + +/* TLB flushing */ +EXPORT_SYMBOL(smp_flush_tlb_page); +EXPORT_SYMBOL_GPL(smp_flush_tlb_all); +#endif + +/* compiler generated symbol */ +extern void __ashldi3(void); +extern void __ashrdi3(void); +extern void __lshldi3(void); +extern void __lshrdi3(void); +extern void __muldi3(void); +EXPORT_SYMBOL_NOVERS(__ashldi3); +EXPORT_SYMBOL_NOVERS(__ashrdi3); +EXPORT_SYMBOL_NOVERS(__lshldi3); +EXPORT_SYMBOL_NOVERS(__lshrdi3); +EXPORT_SYMBOL_NOVERS(__muldi3); + +/* memory and string operations */ +EXPORT_SYMBOL_NOVERS(memchr); +EXPORT_SYMBOL_NOVERS(memcpy); +/* EXPORT_SYMBOL_NOVERS(memcpy_fromio); // not implement yet */ +/* EXPORT_SYMBOL_NOVERS(memcpy_toio); // not implement yet */ +EXPORT_SYMBOL_NOVERS(memset); +/* EXPORT_SYMBOL_NOVERS(memset_io); // not implement yet */ +EXPORT_SYMBOL_NOVERS(memmove); +EXPORT_SYMBOL_NOVERS(memcmp); +EXPORT_SYMBOL_NOVERS(memscan); +EXPORT_SYMBOL_NOVERS(copy_page); +EXPORT_SYMBOL_NOVERS(clear_page); + +EXPORT_SYMBOL_NOVERS(strcat); +EXPORT_SYMBOL_NOVERS(strchr); +EXPORT_SYMBOL_NOVERS(strcmp); +EXPORT_SYMBOL_NOVERS(strcpy); +EXPORT_SYMBOL_NOVERS(strlen); +EXPORT_SYMBOL_NOVERS(strncat); +EXPORT_SYMBOL_NOVERS(strncmp); +EXPORT_SYMBOL_NOVERS(strnlen); +EXPORT_SYMBOL_NOVERS(strncpy); + +EXPORT_SYMBOL_NOVERS(_inb); +EXPORT_SYMBOL_NOVERS(_inw); +EXPORT_SYMBOL_NOVERS(_inl); +EXPORT_SYMBOL_NOVERS(_outb); +EXPORT_SYMBOL_NOVERS(_outw); +EXPORT_SYMBOL_NOVERS(_outl); +EXPORT_SYMBOL_NOVERS(_inb_p); +EXPORT_SYMBOL_NOVERS(_inw_p); +EXPORT_SYMBOL_NOVERS(_inl_p); +EXPORT_SYMBOL_NOVERS(_outb_p); +EXPORT_SYMBOL_NOVERS(_outw_p); +EXPORT_SYMBOL_NOVERS(_outl_p); +EXPORT_SYMBOL_NOVERS(_insb); +EXPORT_SYMBOL_NOVERS(_insw); +EXPORT_SYMBOL_NOVERS(_insl); +EXPORT_SYMBOL_NOVERS(_outsb); +EXPORT_SYMBOL_NOVERS(_outsw); +EXPORT_SYMBOL_NOVERS(_outsl); +EXPORT_SYMBOL_NOVERS(_readb); +EXPORT_SYMBOL_NOVERS(_readw); +EXPORT_SYMBOL_NOVERS(_readl); +EXPORT_SYMBOL_NOVERS(_writeb); +EXPORT_SYMBOL_NOVERS(_writew); +EXPORT_SYMBOL_NOVERS(_writel); + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/Makefile 2004-09-03 00:57:17.328404944 -0700 @@ -0,0 +1,20 @@ +# +# Makefile for the Linux/M32R kernel. +# + +extra-y := head.o init_task.o vmlinux.lds.s + +obj-y := process.o entry.o traps.o align.o irq.o setup.o time.o \ + m32r_ksyms.o sys_m32r.o semaphore.o signal.o ptrace.o + +obj-$(CONFIG_SMP) += smp.o smpboot.o +obj-$(CONFIG_PLAT_MAPPI) += setup_mappi.o io_mappi.o +obj-$(CONFIG_PLAT_MAPPI2) += setup_mappi2.o io_mappi2.o +obj-$(CONFIG_PLAT_USRV) += setup_usrv.o io_usrv.o +obj-$(CONFIG_PLAT_M32700UT) += setup_m32700ut.o io_m32700ut.o +obj-$(CONFIG_PLAT_OPSPUT) += setup_opsput.o io_opsput.o +obj-$(CONFIG_MODULES) += module.o +obj-$(CONFIG_PLAT_OAKS32R) += setup_oaks32r.o io_oaks32r.o + +EXTRA_AFLAGS := -traditional + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/module.c 2004-09-03 00:57:18.720193360 -0700 @@ -0,0 +1,253 @@ +/* Kernel module help for M32R. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include +#include +#include +#include +#include +#include + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(fmt...) +#endif + +void *module_alloc(unsigned long size) +{ + if (size == 0) + return NULL; + return vmalloc_exec(size); +} + + +/* Free memory returned from module_alloc */ +void module_free(struct module *mod, void *module_region) +{ + vfree(module_region); + /* FIXME: If module_region == mod->init_region, trim exception + table entries. */ +} + +/* We don't need anything special. */ +int module_frob_arch_sections(Elf_Ehdr *hdr, + Elf_Shdr *sechdrs, + char *secstrings, + struct module *mod) +{ + return 0; +} + +#define COPY_UNALIGNED_WORD(sw, tw, align) \ +{ \ + void *__s = &(sw), *__t = &(tw); \ + unsigned short *__s2 = __s, *__t2 =__t; \ + unsigned char *__s1 = __s, *__t1 =__t; \ + switch ((align)) \ + { \ + case 0: \ + *(unsigned long *) __t = *(unsigned long *) __s; \ + break; \ + case 2: \ + *__t2++ = *__s2++; \ + *__t2 = *__s2; \ + break; \ + default: \ + *__t1++ = *__s1++; \ + *__t1++ = *__s1++; \ + *__t1++ = *__s1++; \ + *__t1 = *__s1; \ + break; \ + } \ +} + +#define COPY_UNALIGNED_HWORD(sw, tw, align) \ + { \ + void *__s = &(sw), *__t = &(tw); \ + unsigned short *__s2 = __s, *__t2 =__t; \ + unsigned char *__s1 = __s, *__t1 =__t; \ + switch ((align)) \ + { \ + case 0: \ + *__t2 = *__s2; \ + break; \ + default: \ + *__t1++ = *__s1++; \ + *__t1 = *__s1; \ + break; \ + } \ + } + +int apply_relocate_add(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + unsigned int i; + Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr; + Elf32_Sym *sym; + Elf32_Addr relocation; + uint32_t *location; + uint32_t value; + unsigned short *hlocation; + unsigned short hvalue; + int svalue; + int align; + + DEBUGP("Applying relocate section %u to %u\n", relsec, + sechdrs[relsec].sh_info); + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { + /* This is where to make the change */ + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + + rel[i].r_offset; + /* This is the symbol it is referring to. Note that all + undefined symbols have been resolved. */ + sym = (Elf32_Sym *)sechdrs[symindex].sh_addr + + ELF32_R_SYM(rel[i].r_info); + relocation = sym->st_value + rel[i].r_addend; + align = (int)location & 3; + + switch (ELF32_R_TYPE(rel[i].r_info)) { + case R_M32R_32_RELA: + COPY_UNALIGNED_WORD (*location, value, align); + value += relocation; + COPY_UNALIGNED_WORD (value, *location, align); + break; + case R_M32R_HI16_ULO_RELA: + COPY_UNALIGNED_WORD (*location, value, align); + relocation = (relocation >>16) & 0xffff; + /* RELA must has 0 at relocation field. */ + value += relocation; + COPY_UNALIGNED_WORD (value, *location, align); + break; + case R_M32R_HI16_SLO_RELA: + COPY_UNALIGNED_WORD (*location, value, align); + if (relocation & 0x8000) relocation += 0x10000; + relocation = (relocation >>16) & 0xffff; + /* RELA must has 0 at relocation field. */ + value += relocation; + COPY_UNALIGNED_WORD (value, *location, align); + break; + case R_M32R_16_RELA: + hlocation = (unsigned short *)location; + relocation = relocation & 0xffff; + /* RELA must has 0 at relocation field. */ + hvalue = relocation; + COPY_UNALIGNED_WORD (hvalue, *hlocation, align); + break; + case R_M32R_SDA16_RELA: + case R_M32R_LO16_RELA: + COPY_UNALIGNED_WORD (*location, value, align); + relocation = relocation & 0xffff; + /* RELA must has 0 at relocation field. */ + value += relocation; + COPY_UNALIGNED_WORD (value, *location, align); + break; + case R_M32R_24_RELA: + COPY_UNALIGNED_WORD (*location, value, align); + relocation = relocation & 0xffffff; + /* RELA must has 0 at relocation field. */ + value += relocation; + COPY_UNALIGNED_WORD (value, *location, align); + break; + case R_M32R_18_PCREL_RELA: + relocation = (relocation - (Elf32_Addr) location); + if (relocation < -0x20000 || 0x1fffc < relocation) + { + printk(KERN_ERR "module %s: relocation overflow: %u\n", + me->name, relocation); + return -ENOEXEC; + } + COPY_UNALIGNED_WORD (*location, value, align); + if (value & 0xffff) + { + /* RELA must has 0 at relocation field. */ + printk(KERN_ERR "module %s: illegal relocation field: %u\n", + me->name, value); + return -ENOEXEC; + } + relocation = (relocation >> 2) & 0xffff; + value += relocation; + COPY_UNALIGNED_WORD (value, *location, align); + break; + case R_M32R_10_PCREL_RELA: + hlocation = (unsigned short *)location; + relocation = (relocation - (Elf32_Addr) location); + COPY_UNALIGNED_HWORD (*hlocation, hvalue, align); + svalue = (int)hvalue; + svalue = (signed char)svalue << 2; + relocation += svalue; + relocation = (relocation >> 2) & 0xff; + hvalue = hvalue & 0xff00; + hvalue += relocation; + COPY_UNALIGNED_HWORD (hvalue, *hlocation, align); + break; + case R_M32R_26_PCREL_RELA: + relocation = (relocation - (Elf32_Addr) location); + if (relocation < -0x2000000 || 0x1fffffc < relocation) + { + printk(KERN_ERR "module %s: relocation overflow: %u\n", + me->name, relocation); + return -ENOEXEC; + } + COPY_UNALIGNED_WORD (*location, value, align); + if (value & 0xffffff) + { + /* RELA must has 0 at relocation field. */ + printk(KERN_ERR "module %s: illegal relocation field: %u\n", + me->name, value); + return -ENOEXEC; + } + relocation = (relocation >> 2) & 0xffffff; + value += relocation; + COPY_UNALIGNED_WORD (value, *location, align); + break; + default: + printk(KERN_ERR "module %s: Unknown relocation: %u\n", + me->name, ELF32_R_TYPE(rel[i].r_info)); + return -ENOEXEC; + } + } + return 0; +} + +int apply_relocate(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ +#if 0 + printk(KERN_ERR "module %s: REL RELOCATION unsupported\n", + me->name); + return -ENOEXEC; +#endif + return 0; + +} + +int module_finalize(const Elf_Ehdr *hdr, + const Elf_Shdr *sechdrs, + struct module *me) +{ + return 0; +} + +void module_arch_cleanup(struct module *mod) +{ +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/process.c 2004-09-03 00:57:17.331404488 -0700 @@ -0,0 +1,357 @@ +/* + * linux/arch/m32r/kernel/process.c + * orig : sh + * + * Copyright (c) 2001, 2002 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto + * Taken from sh version. + * Copyright (C) 1995 Linus Torvalds + * SuperH version: Copyright (C) 1999, 2000 Niibe Yutaka & Kaz Kojima + */ + +#undef DEBUG_PROCESS +#ifdef DEBUG_PROCESS +#define DPRINTK(fmt, args...) printk("%s:%d:%s: " fmt, __FILE__, __LINE__, \ + __FUNCTION__, ##args) +#else +#define DPRINTK(fmt, args...) +#endif + +/* + * This file handles the architecture-dependent parts of process handling.. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +static int hlt_counter=0; + +/* + * Return saved PC of a blocked thread. + */ +unsigned long thread_saved_pc(struct task_struct *tsk) +{ + return tsk->thread.lr; +} + +/* + * Powermanagement idle function, if any.. + */ +void (*pm_idle)(void) = NULL; + +void disable_hlt(void) +{ + hlt_counter++; +} + +EXPORT_SYMBOL(disable_hlt); + +void enable_hlt(void) +{ + hlt_counter--; +} + +EXPORT_SYMBOL(enable_hlt); + +/* + * We use this is we don't have any better + * idle routine.. + */ +void default_idle(void) +{ + /* M32R_FIXME: Please use "cpu_sleep" mode. */ + cpu_relax(); +} + +/* + * On SMP it's slightly faster (but much more power-consuming!) + * to poll the ->work.need_resched flag instead of waiting for the + * cross-CPU IPI to arrive. Use this option with caution. + */ +static void poll_idle (void) +{ + /* M32R_FIXME */ + cpu_relax(); +} + +/* + * The idle thread. There's no useful work to be + * done, so just try to conserve power and have a + * low exit latency (ie sit in a loop waiting for + * somebody to say that they'd like to reschedule) + */ +void cpu_idle (void) +{ + /* endless idle loop with no priority at all */ + while (1) { + while (!need_resched()) { + void (*idle)(void) = pm_idle; + + if (!idle) + idle = default_idle; + + idle(); + } + schedule(); + } +} + +void machine_restart(char *__unused) +{ + printk("Please push reset button!\n"); + while (1) + cpu_relax(); +} + +EXPORT_SYMBOL(machine_restart); + +void machine_halt(void) +{ + printk("Please push reset button!\n"); + while (1) + cpu_relax(); +} + +EXPORT_SYMBOL(machine_halt); + +void machine_power_off(void) +{ + /* M32R_FIXME */ +} + +EXPORT_SYMBOL(machine_power_off); + +static int __init idle_setup (char *str) +{ + if (!strncmp(str, "poll", 4)) { + printk("using poll in idle threads.\n"); + pm_idle = poll_idle; + } else if (!strncmp(str, "sleep", 4)) { + printk("using sleep in idle threads.\n"); + pm_idle = default_idle; + } + + return 1; +} + +__setup("idle=", idle_setup); + +void show_regs(struct pt_regs * regs) +{ + printk("\n"); + printk("BPC[%08lx]:PSW[%08lx]:LR [%08lx]:FP [%08lx]\n", \ + regs->bpc, regs->psw, regs->lr, regs->fp); + printk("BBPC[%08lx]:BBPSW[%08lx]:SPU[%08lx]:SPI[%08lx]\n", \ + regs->bbpc, regs->bbpsw, regs->spu, regs->spi); + printk("R0 [%08lx]:R1 [%08lx]:R2 [%08lx]:R3 [%08lx]\n", \ + regs->r0, regs->r1, regs->r2, regs->r3); + printk("R4 [%08lx]:R5 [%08lx]:R6 [%08lx]:R7 [%08lx]\n", \ + regs->r4, regs->r5, regs->r6, regs->r7); + printk("R8 [%08lx]:R9 [%08lx]:R10[%08lx]:R11[%08lx]\n", \ + regs->r8, regs->r9, regs->r10, regs->r11); + printk("R12[%08lx]\n", \ + regs->r12); + +#if defined(CONFIG_ISA_M32R2) && defined(CONFIG_ISA_DSP_LEVEL2) + printk("ACC0H[%08lx]:ACC0L[%08lx]\n", \ + regs->acc0h, regs->acc0l); + printk("ACC1H[%08lx]:ACC1L[%08lx]\n", \ + regs->acc1h, regs->acc1l); +#elif defined(CONFIG_ISA_M32R2) || defined(CONFIG_ISA_M32R) + printk("ACCH[%08lx]:ACCL[%08lx]\n", \ + regs->acch, regs->accl); +#else +#error unknown isa configuration +#endif +} + +/* + * Create a kernel thread + */ + +/* + * This is the mechanism for creating a new kernel thread. + * + * NOTE! Only a kernel-only process(ie the swapper or direct descendants + * who haven't done an "execve()") should use this: it will work within + * a system call from a "real" process, but the process memory space will + * not be free'd until both the parent and the child have exited. + */ +static void kernel_thread_helper(void *nouse, int (*fn)(void *), void *arg) +{ + fn(arg); + do_exit(-1); +} + +int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) +{ + struct pt_regs regs; + + memset(®s, 0, sizeof (regs)); + regs.r1 = (unsigned long)fn; + regs.r2 = (unsigned long)arg; + + regs.bpc = (unsigned long)kernel_thread_helper; + + regs.psw = M32R_PSW_BIE; + + /* Ok, create the new process. */ + return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, + NULL); +} + +/* + * Free current thread data structures etc.. + */ +void exit_thread(void) +{ + /* Nothing to do. */ + DPRINTK("pid = %d\n", current->pid); +} + +void flush_thread(void) +{ + DPRINTK("pid = %d\n", current->pid); + memset(¤t->thread.debug_trap, 0, sizeof(struct debug_trap)); +} + +void release_thread(struct task_struct *dead_task) +{ + /* do nothing */ + DPRINTK("pid = %d\n", dead_task->pid); +} + +/* Fill in the fpu structure for a core dump.. */ +int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu) +{ + return 0; /* Task didn't use the fpu at all. */ +} + +int copy_thread(int nr, unsigned long clone_flags, unsigned long spu, + unsigned long unused, struct task_struct *tsk, struct pt_regs *regs) +{ + struct pt_regs *childregs; + unsigned long sp = (unsigned long)tsk->thread_info + THREAD_SIZE; + extern void ret_from_fork(void); + + tsk->set_child_tid = tsk->clear_child_tid = NULL; + + /* Copy registers */ + sp -= sizeof (struct pt_regs); + childregs = (struct pt_regs *)sp; + *childregs = *regs; + + childregs->spu = spu; + childregs->r0 = 0; /* Child gets zero as return value */ + regs->r0 = tsk->pid; + tsk->thread.sp = (unsigned long)childregs; + tsk->thread.lr = (unsigned long)ret_from_fork; + + return 0; +} + +/* + * fill in the user structure for a core dump.. + */ +void dump_thread(struct pt_regs * regs, struct user * dump) +{ + /* M32R_FIXME */ +} + +/* + * Capture the user space registers if the task is not running (in user space) + */ +int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs) +{ + /* M32R_FIXME */ + return 1; +} + +asmlinkage int sys_fork(unsigned long r0, unsigned long r1, unsigned long r2, + unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, + struct pt_regs regs) +{ +#ifdef CONFIG_MMU + return do_fork(SIGCHLD, regs.spu, ®s, 0, NULL, NULL); +#else + return -EINVAL; +#endif /* CONFIG_MMU */ +} + +asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, + unsigned long r2, unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, struct pt_regs regs) +{ + if (!newsp) + newsp = regs.spu; + + return do_fork(clone_flags & ~CLONE_IDLETASK, newsp, ®s, 0, NULL, + NULL); +} + +/* + * This is trivial, and on the face of it looks like it + * could equally well be done in user mode. + * + * Not so, for quite unobvious reasons - register pressure. + * In user mode vfork() cannot have a stack frame, and if + * done by calling the "clone()" system call directly, you + * do not have enough call-clobbered registers to hold all + * the information you need. + */ +asmlinkage int sys_vfork(unsigned long r0, unsigned long r1, unsigned long r2, + unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, + struct pt_regs regs) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.spu, ®s, 0, + NULL, NULL); +} + +/* + * sys_execve() executes a new program. + */ +asmlinkage int sys_execve(char __user *ufilename, char __user * __user *uargv, char __user * __user *uenvp, + unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, + struct pt_regs regs) +{ + int error; + char *filename; + + filename = getname(ufilename); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + + error = do_execve(filename, uargv, uenvp, ®s); + if (error == 0) + current->ptrace &= ~PT_DTRACE; + putname(filename); +out: + return error; +} + +/* + * These bracket the sleeping functions.. + */ +#define first_sched ((unsigned long) scheduling_functions_start_here) +#define last_sched ((unsigned long) scheduling_functions_end_here) + +unsigned long get_wchan(struct task_struct *p) +{ + /* M32R_FIXME */ + return (0); +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/ptrace.c 2004-09-03 00:57:17.335403880 -0700 @@ -0,0 +1,828 @@ +/* + * linux/arch/sh/kernel/ptrace.c + * + * Copyright (C) 2002 Hirokazu Takata, Takeo Takahashi + * + * Original x86 implementation: + * By Ross Biro 1/23/92 + * edited by Linus Torvalds + * + * Some code taken from sh version: + * Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka + * + */ + +/* $Id$ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG_PTRACE 0 + +/* + * does not yet catch signals sent when the child dies. + * in exit.c or in signal.c. + */ + +/* + * This routine will get a word off of the process kernel stack. + */ +static __inline__ unsigned long int get_stack_long(struct task_struct *task, + int offset) +{ + + unsigned char *stack; + + stack = (unsigned char *)(task->thread_info) + THREAD_SIZE + - sizeof(struct pt_regs); + stack += offset; + + return *((unsigned long *)stack); +} + +/* + * This routine will put a word on the process kernel stack. + */ +static __inline__ int put_stack_long(struct task_struct *task, int offset, + unsigned long data) +{ + unsigned char *stack; + + stack = (unsigned char *)(task->thread_info) + THREAD_SIZE + - sizeof(struct pt_regs); + stack += offset; + *((unsigned long *)stack) = data; + + return 0; +} + +static int reg_offset[] = { + (4 * PT_R0), (4 * PT_R1), (4 * PT_R2), (4 * PT_R3), + (4 * PT_R4), (4 * PT_R5), (4 * PT_R6), (4 * PT_R7), + (4 * PT_R8), (4 * PT_R9), (4 * PT_R10), (4 * PT_R11), + (4 * PT_R12), (4 * PT_FP), (4 * PT_LR), (4 * PT_SPU), +}; + +static __inline__ int +check_condition_bit(struct task_struct *child) +{ + return (int)((get_stack_long(child, (4 * PT_PSW)) >> 8) & 1); +} + +static int +check_condition_src(unsigned long op, unsigned long regno1, unsigned long regno2, struct task_struct *child) +{ + unsigned long reg1, reg2; + + reg2 = get_stack_long(child, reg_offset[regno2]); + + switch (op) { + case 0x0: /* BEQ */ + reg1 = get_stack_long(child, reg_offset[regno1]); + return reg1 == reg2; + case 0x1: /* BNE */ + reg1 = get_stack_long(child, reg_offset[regno1]); + return reg1 != reg2; + case 0x8: /* BEQZ */ + return reg2 == 0; + case 0x9: /* BNEZ */ + return reg2 != 0; + case 0xa: /* BLTZ */ + return (int)reg2 < 0; + case 0xb: /* BGEZ */ + return (int)reg2 >= 0; + case 0xc: /* BLEZ */ + return (int)reg2 <= 0; + case 0xd: /* BGTZ */ + return (int)reg2 > 0; + default: + /* never reached */ + return 0; + } +} + +static void +compute_next_pc_for_16bit_insn(unsigned long insn, unsigned long pc, + unsigned long *next_pc, struct task_struct *child) +{ + unsigned long op, op2, op3; + unsigned long disp; + unsigned long regno; + int parallel = 0; + + if (insn & 0x00008000) + parallel = 1; + if (pc & 3) + insn &= 0x7fff; /* right slot */ + else + insn >>= 16; /* left slot */ + + op = (insn >> 12) & 0xf; + op2 = (insn >> 8) & 0xf; + op3 = (insn >> 4) & 0xf; + + if (op == 0x7) { + switch (op2) { + case 0xd: /* BNC */ + case 0x9: /* BNCL */ + if (!check_condition_bit(child)) { + disp = (long)(insn << 24) >> 22; + *next_pc = (pc & ~0x3) + disp; + return; + } + break; + case 0x8: /* BCL */ + case 0xc: /* BC */ + if (check_condition_bit(child)) { + disp = (long)(insn << 24) >> 22; + *next_pc = (pc & ~0x3) + disp; + return; + } + break; + case 0xe: /* BL */ + case 0xf: /* BRA */ + disp = (long)(insn << 24) >> 22; + *next_pc = (pc & ~0x3) + disp; + return; + break; + } + } else if (op == 0x1) { + switch (op2) { + case 0x0: + if (op3 == 0xf) { /* TRAP */ +#if 1 + /* pass through */ +#else + /* kernel space is not allowed as next_pc */ + unsigned long evb; + unsigned long trapno; + trapno = insn & 0xf; + __asm__ __volatile__ ( + "mvfc %0, cr5\n" + :"=r"(evb) + : + ); + *next_pc = evb + (trapno << 2); + return; +#endif + } else if (op3 == 0xd) { /* RTE */ + *next_pc = get_stack_long(child, (4 * PT_BPC)); + return; + } + break; + case 0xc: /* JC */ + if (op3 == 0xc && check_condition_bit(child)) { + regno = insn & 0xf; + *next_pc = get_stack_long(child, reg_offset[regno]); + return; + } + break; + case 0xd: /* JNC */ + if (op3 == 0xc && !check_condition_bit(child)) { + regno = insn & 0xf; + *next_pc = get_stack_long(child, reg_offset[regno]); + return; + } + break; + case 0xe: /* JL */ + case 0xf: /* JMP */ + if (op3 == 0xc) { /* JMP */ + regno = insn & 0xf; + *next_pc = get_stack_long(child, reg_offset[regno]); + return; + } + break; + } + } + if (parallel) + *next_pc = pc + 4; + else + *next_pc = pc + 2; +} + +static void +compute_next_pc_for_32bit_insn(unsigned long insn, unsigned long pc, + unsigned long *next_pc, struct task_struct *child) +{ + unsigned long op; + unsigned long op2; + unsigned long disp; + unsigned long regno1, regno2; + + op = (insn >> 28) & 0xf; + if (op == 0xf) { /* branch 24-bit relative */ + op2 = (insn >> 24) & 0xf; + switch (op2) { + case 0xd: /* BNC */ + case 0x9: /* BNCL */ + if (!check_condition_bit(child)) { + disp = (long)(insn << 8) >> 6; + *next_pc = (pc & ~0x3) + disp; + return; + } + break; + case 0x8: /* BCL */ + case 0xc: /* BC */ + if (check_condition_bit(child)) { + disp = (long)(insn << 8) >> 6; + *next_pc = (pc & ~0x3) + disp; + return; + } + break; + case 0xe: /* BL */ + case 0xf: /* BRA */ + disp = (long)(insn << 8) >> 6; + *next_pc = (pc & ~0x3) + disp; + return; + } + } else if (op == 0xb) { /* branch 16-bit relative */ + op2 = (insn >> 20) & 0xf; + switch (op2) { + case 0x0: /* BEQ */ + case 0x1: /* BNE */ + case 0x8: /* BEQZ */ + case 0x9: /* BNEZ */ + case 0xa: /* BLTZ */ + case 0xb: /* BGEZ */ + case 0xc: /* BLEZ */ + case 0xd: /* BGTZ */ + regno1 = ((insn >> 24) & 0xf); + regno2 = ((insn >> 16) & 0xf); + if (check_condition_src(op2, regno1, regno2, child)) { + disp = (long)(insn << 16) >> 14; + *next_pc = (pc & ~0x3) + disp; + return; + } + break; + } + } + *next_pc = pc + 4; +} + +static __inline__ void +compute_next_pc(unsigned long insn, unsigned long pc, + unsigned long *next_pc, struct task_struct *child) +{ + if (insn & 0x80000000) + compute_next_pc_for_32bit_insn(insn, pc, next_pc, child); + else + compute_next_pc_for_16bit_insn(insn, pc, next_pc, child); +} + +static int +register_debug_trap(struct task_struct *child, unsigned long next_pc, + unsigned long next_insn, unsigned long *code) +{ + struct debug_trap *p = &child->thread.debug_trap; + unsigned long addr = next_pc & ~3; + + if (p->nr_trap != 0) { + printk("kernel BUG at %s %d: p->nr_trap = %d\n", + __FILE__, __LINE__, p->nr_trap); + return -1; + } + p->addr = addr; + p->insn = next_insn; + p->nr_trap++; + if (next_pc & 3) { + *code = (next_insn & 0xffff0000) | 0x10f1; + /* xxx --> TRAP1 */ + } else { + if ((next_insn & 0x80000000) || (next_insn & 0x8000)) { + *code = 0x10f17000; + /* TRAP1 --> NOP */ + } else { + *code = (next_insn & 0xffff) | 0x10f10000; + /* TRAP1 --> xxx */ + } + } + return 0; +} + +int withdraw_debug_trap_for_signal(struct task_struct *child) +{ + struct debug_trap *p = &child->thread.debug_trap; + int nr_trap = p->nr_trap; + + if (nr_trap) { + access_process_vm(child, p->addr, &p->insn, sizeof(p->insn), 1); + p->nr_trap = 0; + p->addr = 0; + p->insn = 0; + } + return nr_trap; +} + +static int +unregister_debug_trap(struct task_struct *child, unsigned long addr, unsigned long *code) +{ + struct debug_trap *p = &child->thread.debug_trap; + + if (p->nr_trap != 1 || p->addr != addr) { + /* The trap may be requested from debugger. + * ptrace should do nothing in this case. + */ + return 0; + } + *code = p->insn; + p->insn = 0; + p->addr = 0; + p->nr_trap--; + return 1; +} + +static void +unregister_all_debug_traps(struct task_struct *child) +{ + struct debug_trap *p = &child->thread.debug_trap; + + if (p->nr_trap) { + access_process_vm(child, p->addr, &p->insn, sizeof(p->insn), 1); + p->addr = 0; + p->insn = 0; + p->nr_trap = 0; + } +} + +static void +invalidate_cache(void) +{ +#if defined(CONFIG_CHIP_M32700) || defined(CONFIG_CHIP_OPSP) + + _flush_cache_copyback_all(); + +#else /* ! CONFIG_CHIP_M32700 */ + + /* Invalidate cache */ + __asm__ __volatile__ ( + "ldi r0, #-1 \n\t" + "ldi r1, #0 \n\t" + "stb r1, @r0 ; cache off \n\t" + "; \n\t" + "ldi r0, #-2 \n\t" + "ldi r1, #1 \n\t" + "stb r1, @r0 ; cache invalidate \n\t" + ".fillinsn \n" + "0: \n\t" + "ldb r1, @r0 ; invalidate check \n\t" + "bnez r1, 0b \n\t" + "; \n\t" + "ldi r0, #-1 \n\t" + "ldi r1, #1 \n\t" + "stb r1, @r0 ; cache on \n\t" + : : : "r0", "r1", "memory" + ); + /* FIXME: copying-back d-cache and invalidating i-cache are needed. + */ +#endif /* CONFIG_CHIP_M32700 */ +} + +/* Embed a debug trap (TRAP1) code */ +static int +embed_debug_trap(struct task_struct *child, unsigned long next_pc) +{ + unsigned long next_insn, code; + unsigned long addr = next_pc & ~3; + + if (access_process_vm(child, addr, &next_insn, sizeof(next_insn), 0) + != sizeof(next_insn)) { + return -1; /* error */ + } + + /* Set a trap code. */ + if (register_debug_trap(child, next_pc, next_insn, &code)) { + return -1; /* error */ + } + if (access_process_vm(child, addr, &code, sizeof(code), 1) + != sizeof(code)) { + return -1; /* error */ + } + return 0; /* success */ +} + +void +embed_debug_trap_for_signal(struct task_struct *child) +{ + unsigned long next_pc; + unsigned long pc, insn; + int ret; + + pc = get_stack_long(child, (4 * PT_BPC)); + ret = access_process_vm(child, pc&~3, &insn, sizeof(insn), 0); + if (ret != sizeof(insn)) { + printk("kernel BUG at %s %d: access_process_vm returns %d\n", + __FILE__, __LINE__, ret); + return; + } + compute_next_pc(insn, pc, &next_pc, child); + if (next_pc & 0x80000000) { + printk("kernel BUG at %s %d: next_pc = 0x%08x\n", + __FILE__, __LINE__, (int)next_pc); + return; + } + if (embed_debug_trap(child, next_pc)) { + printk("kernel BUG at %s %d: embed_debug_trap error\n", + __FILE__, __LINE__); + return; + } + invalidate_cache(); +} + +void +withdraw_debug_trap(struct pt_regs *regs) +{ + unsigned long addr; + unsigned long code; + + addr = (regs->bpc - 2) & ~3; + regs->bpc -= 2; + if (unregister_debug_trap(current, addr, &code)) { + access_process_vm(current, addr, &code, sizeof(code), 1); + invalidate_cache(); + } +} + + +/* + * Called by kernel/ptrace.c when detaching.. + * + * Make sure single step bits etc are not set. + */ +void ptrace_disable(struct task_struct *child) +{ + /* nothing to do.. */ +} + +static void +init_debug_traps(struct task_struct *child) +{ + struct debug_trap *p = &child->thread.debug_trap; + p->nr_trap = 0; + p->addr = 0; + p->insn = 0; +} + +asmlinkage int sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + int ret; +#ifndef NO_FPU + struct user * dummy = NULL; +#endif + + lock_kernel(); + ret = -EPERM; + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->ptrace & PT_PTRACED) + goto out; + /* set the ptrace bit in the process flags. */ + current->ptrace |= PT_PTRACED; + ret = 0; + goto out; + } + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); + if (!child) + goto out; + + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out; + + if (request == PTRACE_ATTACH) { +#if (DEBUG_PTRACE > 1) +printk("ptrace: PTRACE_ATTACH: child:%08lx\n", (unsigned long)child); +#endif + ret = ptrace_attach(child); + if (ret == 0) + init_debug_traps(child); + goto out_tsk; + } + + ret = ptrace_check_attach(child, request == PTRACE_KILL); + if (ret < 0) + goto out_tsk; + + switch (request) { + /* when I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: { + unsigned long tmp; + int copied; + +#if (DEBUG_PTRACE > 1) +printk("ptrace: PTRACE_PEEKTEXT/PEEKDATA: child:%08lx, addr:%08lx\n", + (unsigned long)child, addr); +#endif + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + ret = -EIO; + if (copied != sizeof(tmp)) + break; + ret = put_user(tmp,(unsigned long __user *) data); + break; + } + + /* read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: { + unsigned long tmp; + +#if DEBUG_PTRACE +printk("ptrace: PTRACE_PEEKUSER: child:%08lx, addr:%08lx\n", + (unsigned long)child, addr); +#endif + ret = -EIO; + if ((addr & 3) || addr < 0 || + addr > sizeof(struct user) - 3) + break; + + switch (addr) { + case (4 * PT_BPC): + addr = (4 * PT_BBPC); + break; + case (4 * PT_EVB): + __asm__ __volatile__ ( + "mvfc %0, cr5\n" + :"=r"(tmp) + : + ); + ret = put_user(tmp, (unsigned long __user *)data); + goto out_tsk; + break; + case (4 * PT_CBR): { + unsigned long psw; + psw = get_stack_long(child, (4 * PT_PSW)); + tmp = ((psw >> 8) & 1); + ret = put_user(tmp, (unsigned long __user *)data); + goto out_tsk; + } + break; + case (4 * PT_PSW): { + unsigned long psw, bbpsw; + psw = get_stack_long(child, (4 * PT_PSW)); + bbpsw = get_stack_long(child, (4 * PT_BBPSW)); + tmp = ((psw >> 8) & 0xff) | ((bbpsw & 0xff) << 8); + ret = put_user(tmp, (unsigned long __user *)data); + goto out_tsk; + } + break; + case (4 * PT_PC): { + unsigned long pc; + pc = get_stack_long(child, (4 * PT_BPC)); + ret = put_user(pc, (unsigned long __user *)data); + goto out_tsk; + } + break; + } + + if (addr < sizeof(struct pt_regs)) + tmp = get_stack_long(child, addr); +#ifndef NO_FPU + else if (addr >= (long) &dummy->fpu && + addr < (long) &dummy->u_fpvalid) { + if (!child->used_math) { + if (addr == (long)&dummy->fpu.fpscr) + tmp = FPSCR_INIT; + else + tmp = 0; + } else + tmp = ((long *)&child->thread.fpu) + [(addr - (long)&dummy->fpu) >> 2]; + } else if (addr == (long) &dummy->u_fpvalid) + tmp = child->used_math; +#endif /* not NO_FPU */ + else + tmp = 0; + ret = put_user(tmp, (unsigned long __user *)data); + break; + } + + /* when I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: +#if DEBUG_PTRACE +printk("ptrace: PTRACE_POKETEXT/POKEDATA: child:%08lx, addr:%08lx, data:%08lx\n", + (unsigned long)child, addr, data); +#endif + ret = -EIO; + if (access_process_vm(child, addr, &data, sizeof(data), 1) + != sizeof(data)) { + break; + } + ret = 0; + if (request == PTRACE_POKETEXT) { + invalidate_cache(); + } + break; + + case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ +#if DEBUG_PTRACE +printk("ptrace: PTRACE_POKEUSR: child:%08lx, addr:%08lx, data:%08lx\n", + (unsigned long)child, addr, data); +#endif + ret = -EIO; + if ((addr & 3) || addr < 0 || + addr > sizeof(struct user) - 3) + break; + + switch (addr) { + case (4 * PT_EVB): + case (4 * PT_BPC): + case (4 * PT_SPI): + /* We don't allow to modify evb. */ + ret = 0; + goto out_tsk; + break; + case (4 * PT_PSW): + case (4 * PT_CBR): { + /* We allow to modify only cbr in psw */ + unsigned long psw; + psw = get_stack_long(child, (4 * PT_PSW)); + psw = (psw & ~0x100) | ((data & 1) << 8); + ret = put_stack_long(child, (4 * PT_PSW), psw); + goto out_tsk; + } + break; + case (4 * PT_PC): + addr = (4 * PT_BPC); + data &= ~1; + break; + } + + if (addr < sizeof(struct pt_regs)) + ret = put_stack_long(child, addr, data); +#ifndef NO_FPU + else if (addr >= (long) &dummy->fpu && + addr < (long) &dummy->u_fpvalid) { + child->used_math = 1; + ((long *)&child->thread.fpu) + [(addr - (long)&dummy->fpu) >> 2] = data; + ret = 0; + } else if (addr == (long) &dummy->u_fpvalid) { + child->used_math = data?1:0; + ret = 0; + } +#endif /* not NO_FPU */ + break; + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: { /* restart after signal. */ +#if DEBUG_PTRACE +printk("ptrace: PTRACE_SYSCALL/CONT: child:%08lx, data:%08lx\n", + (unsigned long)child, data); +#endif + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + if (request == PTRACE_SYSCALL) + set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + else + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + child->exit_code = data; + wake_up_process(child); + ret = 0; + break; + } + +/* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: { +#if DEBUG_PTRACE +printk("ptrace: PTRACE_KILL: child:%08lx\n", (unsigned long)child); +#endif + ret = 0; + unregister_all_debug_traps(child); + invalidate_cache(); + if (child->state == TASK_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + wake_up_process(child); + break; + } + + case PTRACE_SINGLESTEP: { /* set the trap flag. */ + unsigned long next_pc; + unsigned long pc, insn; + + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + if ((child->ptrace & PT_DTRACE) == 0) { + /* Spurious delayed TF traps may occur */ + child->ptrace |= PT_DTRACE; + } + + /* Compute next pc. */ + pc = get_stack_long(child, (4 * PT_BPC)); + +#if DEBUG_PTRACE +printk("ptrace: PTRACE_SINGLESTEP: child:%08lx, pc:%08lx, ", + (unsigned long)child, pc); +#endif + if (access_process_vm(child, pc&~3, &insn, sizeof(insn), 0) != sizeof(insn)) + break; + +#if DEBUG_PTRACE +printk("(pc&~3):%08lx, insn:%08lx, ", (pc & ~3), insn); +#endif + + compute_next_pc(insn, pc, &next_pc, child); +#if DEBUG_PTRACE +printk("nextpc:%08lx\n", next_pc); +#endif + if (next_pc & 0x80000000) + break; + + if (embed_debug_trap(child, next_pc)) + break; + + invalidate_cache(); + child->exit_code = data; + /* give it a chance to run. */ + wake_up_process(child); + ret = 0; + break; + } + + case PTRACE_DETACH: /* detach a process that was attached. */ +#if DEBUG_PTRACE +printk("ptrace: PTRACE_DETACH: child:%08lx, data:%08lx\n", + (unsigned long)child, data); +#endif + ret = 0; + ret = ptrace_detach(child, data); + break; + + case PTRACE_SETOPTIONS: { +#if DEBUG_PTRACE +printk("ptrace: PTRACE_SETOPTIONS:\n"); +#endif + if (data & PTRACE_O_TRACESYSGOOD) + child->ptrace |= PT_TRACESYSGOOD; + else + child->ptrace &= ~PT_TRACESYSGOOD; + ret = 0; + break; + } + + default: + ret = -EIO; + break; + } +out_tsk: + put_task_struct(child); +out: + unlock_kernel(); + + return ret; +} + +/* notification of system call entry/exit + * - triggered by current->work.syscall_trace + */ +void do_syscall_trace(void) +{ + if (!test_thread_flag(TIF_SYSCALL_TRACE)) + return; + if (!(current->ptrace & PT_PTRACED)) + return; + /* the 0x80 provides a way for the tracing parent to distinguish + between a syscall stop and SIGTRAP delivery */ + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); + + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/semaphore.c 2004-09-03 00:57:17.336403728 -0700 @@ -0,0 +1,186 @@ +/* + * linux/arch/m32r/semaphore.c + * orig : i386 2.6.4 + * + * M32R semaphore implementation. + * + * Copyright (c) 2002 - 2004 Hitoshi Yamamoto + */ + +/* + * i386 semaphore implementation. + * + * (C) Copyright 1999 Linus Torvalds + * + * Portions Copyright 1999 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * rw semaphores implemented November 1999 by Benjamin LaHaise + */ +#include +#include +#include +#include +#include + +/* + * Semaphores are implemented using a two-way counter: + * The "count" variable is decremented for each process + * that tries to acquire the semaphore, while the "sleeping" + * variable is a count of such acquires. + * + * Notably, the inline "up()" and "down()" functions can + * efficiently test if they need to do any extra work (up + * needs to do something only if count was negative before + * the increment operation. + * + * "sleeping" and the contention routine ordering is protected + * by the spinlock in the semaphore's waitqueue head. + * + * Note that these functions are only called when there is + * contention on the lock, and as such all this is the + * "non-critical" part of the whole semaphore business. The + * critical part is the inline stuff in + * where we want to avoid any extra jumps and calls. + */ + +/* + * Logic: + * - only on a boundary condition do we need to care. When we go + * from a negative count to a non-negative, we wake people up. + * - when we go from a non-negative count to a negative do we + * (a) synchronize with the "sleeper" count and (b) make sure + * that we're on the wakeup list before we synchronize so that + * we cannot lose wakeup events. + */ + +asmlinkage void __up(struct semaphore *sem) +{ + wake_up(&sem->wait); +} + +asmlinkage void __sched __down(struct semaphore * sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + unsigned long flags; + + tsk->state = TASK_UNINTERRUPTIBLE; + spin_lock_irqsave(&sem->wait.lock, flags); + add_wait_queue_exclusive_locked(&sem->wait, &wait); + + sem->sleepers++; + for (;;) { + int sleepers = sem->sleepers; + + /* + * Add "everybody else" into it. They aren't + * playing, because we own the spinlock in + * the wait_queue_head. + */ + if (!atomic_add_negative(sleepers - 1, &sem->count)) { + sem->sleepers = 0; + break; + } + sem->sleepers = 1; /* us - see -1 above */ + spin_unlock_irqrestore(&sem->wait.lock, flags); + + schedule(); + + spin_lock_irqsave(&sem->wait.lock, flags); + tsk->state = TASK_UNINTERRUPTIBLE; + } + remove_wait_queue_locked(&sem->wait, &wait); + wake_up_locked(&sem->wait); + spin_unlock_irqrestore(&sem->wait.lock, flags); + tsk->state = TASK_RUNNING; +} + +asmlinkage int __sched __down_interruptible(struct semaphore * sem) +{ + int retval = 0; + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + unsigned long flags; + + tsk->state = TASK_INTERRUPTIBLE; + spin_lock_irqsave(&sem->wait.lock, flags); + add_wait_queue_exclusive_locked(&sem->wait, &wait); + + sem->sleepers++; + for (;;) { + int sleepers = sem->sleepers; + + /* + * With signals pending, this turns into + * the trylock failure case - we won't be + * sleeping, and we* can't get the lock as + * it has contention. Just correct the count + * and exit. + */ + if (signal_pending(current)) { + retval = -EINTR; + sem->sleepers = 0; + atomic_add(sleepers, &sem->count); + break; + } + + /* + * Add "everybody else" into it. They aren't + * playing, because we own the spinlock in + * wait_queue_head. The "-1" is because we're + * still hoping to get the semaphore. + */ + if (!atomic_add_negative(sleepers - 1, &sem->count)) { + sem->sleepers = 0; + break; + } + sem->sleepers = 1; /* us - see -1 above */ + spin_unlock_irqrestore(&sem->wait.lock, flags); + + schedule(); + + spin_lock_irqsave(&sem->wait.lock, flags); + tsk->state = TASK_INTERRUPTIBLE; + } + remove_wait_queue_locked(&sem->wait, &wait); + wake_up_locked(&sem->wait); + spin_unlock_irqrestore(&sem->wait.lock, flags); + + tsk->state = TASK_RUNNING; + return retval; +} + +/* + * Trylock failed - make sure we correct for + * having decremented the count. + * + * We could have done the trylock with a + * single "cmpxchg" without failure cases, + * but then it wouldn't work on a 386. + */ +asmlinkage int __down_trylock(struct semaphore * sem) +{ + int sleepers; + unsigned long flags; + + spin_lock_irqsave(&sem->wait.lock, flags); + sleepers = sem->sleepers + 1; + sem->sleepers = 0; + + /* + * Add "everybody else" and us into it. They aren't + * playing, because we own the spinlock in the + * wait_queue_head. + */ + if (!atomic_add_negative(sleepers, &sem->count)) { + wake_up_locked(&sem->wait); + } + + spin_unlock_irqrestore(&sem->wait.lock, flags); + return 1; +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/setup.c 2004-09-03 00:57:19.063141224 -0700 @@ -0,0 +1,403 @@ +/* + * linux/arch/m32r/kernel/setup.c + * + * Setup routines for MITSUBISHI M32R + * + * Copyright (c) 2001, 2002 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto + */ + +/* $Id$ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MMU +extern void init_mmu(void); +#endif + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_HD) \ + || defined(CONFIG_BLK_DEV_IDE_MODULE) \ + || defined(CONFIG_BLK_DEV_HD_MODULE) +struct drive_info_struct { char dummy[32]; } drive_info; +#endif + +extern char _end[]; + +/* + * Machine setup.. + */ +struct cpuinfo_m32r boot_cpu_data; + +#ifdef CONFIG_BLK_DEV_RAM +extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ +extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ +extern int rd_image_start; /* starting block # of image */ +#endif + +#if defined(CONFIG_VGA_CONSOLE) +struct screen_info screen_info = { + .orig_video_lines = 25, + .orig_video_cols = 80, + .orig_video_mode = 0, + .orig_video_ega_bx = 0, + .orig_video_isVGA = 1, + .orig_video_points = 8 +}; +#endif + +extern int root_mountflags; + +static char command_line[COMMAND_LINE_SIZE]; + +static struct resource data_resource = { + .name = "Kernel data", + .start = 0, + .end = 0, + .flags = IORESOURCE_BUSY | IORESOURCE_MEM +}; + +static struct resource code_resource = { + .name = "Kernel code", + .start = 0, + .end = 0, + .flags = IORESOURCE_BUSY | IORESOURCE_MEM +}; + +unsigned long memory_start; +unsigned long memory_end; + +void __init setup_arch(char **); +int get_cpuinfo(char *); + +static __inline__ void parse_mem_cmdline(char ** cmdline_p) +{ + char c = ' '; + char *to = command_line; + char *from = COMMAND_LINE; + int len = 0; + int usermem = 0; + + /* Save unparsed command line copy for /proc/cmdline */ + memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE); + saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; + + memory_start = (unsigned long)CONFIG_MEMORY_START+PAGE_OFFSET; + memory_end = memory_start+(unsigned long)CONFIG_MEMORY_SIZE; + + for ( ; ; ) { + if (c == ' ' && !memcmp(from, "mem=", 4)) { + if (to != command_line) + to--; + + { + unsigned long mem_size; + + usermem = 1; + mem_size = memparse(from+4, &from); + memory_end = memory_start + mem_size; + } + } + c = *(from++); + if (!c) + break; + + if (COMMAND_LINE_SIZE <= ++len) + break; + + *(to++) = c; + } + *to = '\0'; + *cmdline_p = command_line; + if (usermem) + printk(KERN_INFO "user-defined physical RAM map:\n"); +} + +#ifndef CONFIG_DISCONTIGMEM +static unsigned long __init setup_memory(void) +{ + unsigned long start_pfn, max_low_pfn, bootmap_size; + + start_pfn = PFN_UP( __pa(_end) ); + max_low_pfn = PFN_DOWN( __pa(memory_end) ); + + /* + * Initialize the boot-time allocator (with low memory only): + */ + bootmap_size = init_bootmem_node(NODE_DATA(0), start_pfn, + CONFIG_MEMORY_START>>PAGE_SHIFT, max_low_pfn); + + /* + * Register fully available low RAM pages with the bootmem allocator. + */ + { + unsigned long curr_pfn; + unsigned long last_pfn; + unsigned long pages; + + /* + * We are rounding up the start address of usable memory: + */ + curr_pfn = PFN_UP(__pa(memory_start)); + + /* + * ... and at the end of the usable range downwards: + */ + last_pfn = PFN_DOWN(__pa(memory_end)); + + if (last_pfn > max_low_pfn) + last_pfn = max_low_pfn; + + pages = last_pfn - curr_pfn; + free_bootmem(PFN_PHYS(curr_pfn), PFN_PHYS(pages)); + } + + /* + * Reserve the kernel text and + * Reserve the bootmem bitmap. We do this in two steps (first step + * was init_bootmem()), because this catches the (definitely buggy) + * case of us accidentally initializing the bootmem allocator with + * an invalid RAM area. + */ + reserve_bootmem(CONFIG_MEMORY_START + PAGE_SIZE, + (PFN_PHYS(start_pfn) + bootmap_size + PAGE_SIZE - 1) + - CONFIG_MEMORY_START); + + /* + * reserve physical page 0 - it's a special BIOS page on many boxes, + * enabling clean reboots, SMP operation, laptop functions. + */ + reserve_bootmem(CONFIG_MEMORY_START, PAGE_SIZE); + + /* + * reserve memory hole + */ +#ifdef CONFIG_MEMHOLE + reserve_bootmem(CONFIG_MEMHOLE_START, CONFIG_MEMHOLE_SIZE); +#endif + +#ifdef CONFIG_BLK_DEV_INITRD + if (LOADER_TYPE && INITRD_START) { + if (INITRD_START + INITRD_SIZE <= (max_low_pfn << PAGE_SHIFT)) { + reserve_bootmem(INITRD_START, INITRD_SIZE); + initrd_start = INITRD_START ? + INITRD_START + PAGE_OFFSET : 0; + + initrd_end = initrd_start + INITRD_SIZE; + printk("initrd:start[%08lx],size[%08lx]\n", + initrd_start, INITRD_SIZE); + } else { + printk("initrd extends beyond end of memory " + "(0x%08lx > 0x%08lx)\ndisabling initrd\n", + INITRD_START + INITRD_SIZE, + max_low_pfn << PAGE_SHIFT); + + initrd_start = 0; + } + } +#endif + + return max_low_pfn; +} +#else /* CONFIG_DISCONTIGMEM */ +extern unsigned long setup_memory(void); +#endif /* CONFIG_DISCONTIGMEM */ + +#define M32R_PCC_PCATCR 0x00ef7014 /* will move to m32r.h */ + +void __init setup_arch(char **cmdline_p) +{ + ROOT_DEV = old_decode_dev(ORIG_ROOT_DEV); + + boot_cpu_data.cpu_clock = M32R_CPUCLK; + boot_cpu_data.bus_clock = M32R_BUSCLK; + boot_cpu_data.timer_divide = M32R_TIMER_DIVIDE; + +#ifdef CONFIG_BLK_DEV_RAM + rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; + rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0); + rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0); +#endif + + if (!MOUNT_ROOT_RDONLY) + root_mountflags &= ~MS_RDONLY; + +#ifdef CONFIG_VT +#if defined(CONFIG_VGA_CONSOLE) + conswitchp = &vga_con; +#elif defined(CONFIG_DUMMY_CONSOLE) + conswitchp = &dummy_con; +#endif +#endif + +#ifdef CONFIG_DISCONTIGMEM + numnodes = 2; +#endif /* CONFIG_DISCONTIGMEM */ + + init_mm.start_code = (unsigned long) _text; + init_mm.end_code = (unsigned long) _etext; + init_mm.end_data = (unsigned long) _edata; + init_mm.brk = (unsigned long) _end; + + code_resource.start = virt_to_phys(_text); + code_resource.end = virt_to_phys(_etext)-1; + data_resource.start = virt_to_phys(_etext); + data_resource.end = virt_to_phys(_edata)-1; + + parse_mem_cmdline(cmdline_p); + + setup_memory(); + + paging_init(); +} + +#ifdef CONFIG_PROC_FS +/* + * Get CPU information for use by the procfs. + */ +static int show_cpuinfo(struct seq_file *m, void *v) +{ + struct cpuinfo_m32r *c = v; + unsigned long cpu = c - cpu_data; + +#ifdef CONFIG_SMP + if (!cpu_online(cpu)) + return 0; +#endif /* CONFIG_SMP */ + + seq_printf(m, "processor\t: %ld\n", cpu); + +#ifdef CONFIG_CHIP_VDEC2 + seq_printf(m, "cpu family\t: VDEC2\n" + "cache size\t: Unknown\n"); +#elif CONFIG_CHIP_M32700 + seq_printf(m,"cpu family\t: M32700\n" + "cache size\t: I-8KB/D-8KB\n"); +#elif CONFIG_CHIP_M32102 + seq_printf(m,"cpu family\t: M32102\n" + "cache size\t: I-8KB\n"); +#elif CONFIG_CHIP_OPSP + seq_printf(m,"cpu family\t: OPSP\n" + "cache size\t: I-8KB/D-8KB\n"); +#elif CONFIG_CHIP_MP + seq_printf(m, "cpu family\t: M32R-MP\n" + "cache size\t: I-xxKB/D-xxKB\n"); +#else + seq_printf(m, "cpu family\t: Unknown\n"); +#endif + seq_printf(m, "bogomips\t: %lu.%02lu\n", + c->loops_per_jiffy/(500000/HZ), + (c->loops_per_jiffy/(5000/HZ)) % 100); +#ifdef CONFIG_PLAT_MAPPI + seq_printf(m, "Machine\t\t: Mappi Evaluation board\n"); +#elif CONFIG_PLAT_MAPPI2 + seq_printf(m, "Machine\t\t: Mappi-II Evaluation board\n"); +#elif CONFIG_PLAT_M32700UT + seq_printf(m, "Machine\t\t: M32700UT Evaluation board\n"); +#elif CONFIG_PLAT_OPSPUT + seq_printf(m, "Machine\t\t: OPSPUT Evaluation board\n"); +#elif CONFIG_PLAT_USRV + seq_printf(m, "Machine\t\t: uServer\n"); +#elif CONFIG_PLAT_OAKS32R + seq_printf(m, "Machine\t\t: OAKS32R\n"); +#else + seq_printf(m, "Machine\t\t: Unknown\n"); +#endif + +#define PRINT_CLOCK(name, value) \ + seq_printf(m, name " clock\t: %d.%02dMHz\n", \ + ((value) / 1000000), ((value) % 1000000)/10000) + + PRINT_CLOCK("CPU", (int)c->cpu_clock); + PRINT_CLOCK("Bus", (int)c->bus_clock); + + seq_printf(m, "\n"); + + return 0; +} + +static void *c_start(struct seq_file *m, loff_t *pos) +{ + return *pos < NR_CPUS ? cpu_data + *pos : NULL; +} + +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return c_start(m, pos); +} + +static void c_stop(struct seq_file *m, void *v) +{ +} + +struct seq_operations cpuinfo_op = { + start: c_start, + next: c_next, + stop: c_stop, + show: show_cpuinfo, +}; +#endif /* CONFIG_PROC_FS */ + +unsigned long cpu_initialized __initdata = 0; + +/* + * cpu_init() initializes state that is per-CPU. Some data is already + * initialized (naturally) in the bootstrap process. + * We reload them nevertheless, this function acts as a + * 'CPU state barrier', nothing should get across. + */ +#if defined(CONFIG_CHIP_VDEC2) || defined(CONFIG_CHIP_XNUX2) \ + || defined(CONFIG_CHIP_M32700) || defined(CONFIG_CHIP_M32102) \ + || defined(CONFIG_CHIP_OPSP) +void __init cpu_init (void) +{ + int cpu_id = smp_processor_id(); + + if (test_and_set_bit(cpu_id, &cpu_initialized)) { + printk(KERN_WARNING "CPU#%d already initialized!\n", cpu_id); + for ( ; ; ) + local_irq_enable(); + } + printk(KERN_INFO "Initializing CPU#%d\n", cpu_id); + + /* Set up and load the per-CPU TSS and LDT */ + atomic_inc(&init_mm.mm_count); + current->active_mm = &init_mm; + if (current->mm) + BUG(); + + /* Force FPU initialization */ + current_thread_info()->status = 0; + current->used_math = 0; + +#ifdef CONFIG_MMU + /* Set up MMU */ + init_mmu(); +#endif + + /* Set up ICUIMASK */ + outl(0x00070000, M32R_ICU_IMASK_PORTL); /* imask=111 */ +} +#endif /* defined(CONFIG_CHIP_VDEC2) ... */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/setup_m32700ut.c 2004-09-03 00:57:19.063141224 -0700 @@ -0,0 +1,450 @@ +/* + * linux/arch/m32r/kernel/setup_m32700ut.c + * + * Setup routines for MITSUBISHI M32700UT Board + * + * Copyright (c) 2002 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto, Takeo Takahashi + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * $Id: setup_m32700ut.c,v 1.6 2003/11/27 10:18:49 takeo Exp $ + */ + +#include +#include +#include +#include + +#include +#include +#include + +/* + * M32700 Interrupt Control Unit (Level 1) + */ +#define irq2port(x) (M32R_ICU_CR1_PORTL + ((x - 1) * sizeof(unsigned long))) + +#ifndef CONFIG_SMP +typedef struct { + unsigned long icucr; /* ICU Control Register */ +} icu_data_t; +#endif /* CONFIG_SMP */ + +static icu_data_t icu_data[M32700UT_NUM_CPU_IRQ]; + +static void disable_m32700ut_irq(unsigned int irq) +{ + unsigned long port, data; + + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_ILEVEL7; + outl(data, port); +} + +static void enable_m32700ut_irq(unsigned int irq) +{ + unsigned long port, data; + + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_IEN|M32R_ICUCR_ILEVEL6; + outl(data, port); +} + +static void mask_and_ack_m32700ut(unsigned int irq) +{ + disable_m32700ut_irq(irq); +} + +static void end_m32700ut_irq(unsigned int irq) +{ + enable_m32700ut_irq(irq); +} + +static unsigned int startup_m32700ut_irq(unsigned int irq) +{ + enable_m32700ut_irq(irq); + return (0); +} + +static void shutdown_m32700ut_irq(unsigned int irq) +{ + unsigned long port; + + port = irq2port(irq); + outl(M32R_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type m32700ut_irq_type = +{ + "M32700UT-IRQ", + startup_m32700ut_irq, + shutdown_m32700ut_irq, + enable_m32700ut_irq, + disable_m32700ut_irq, + mask_and_ack_m32700ut, + end_m32700ut_irq +}; + +/* + * Interrupt Control Unit of PLD on M32700UT (Level 2) + */ +#define irq2pldirq(x) ((x) - M32700UT_PLD_IRQ_BASE) +#define pldirq2port(x) (unsigned long)((int)PLD_ICUCR1 + \ + (((x) - 1) * sizeof(unsigned short))) + +typedef struct { + unsigned short icucr; /* ICU Control Register */ +} pld_icu_data_t; + +static pld_icu_data_t pld_icu_data[M32700UT_NUM_PLD_IRQ]; + +static void disable_m32700ut_pld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2pldirq(irq); +// disable_m32700ut_irq(M32R_IRQ_INT1); + port = pldirq2port(pldirq); + data = pld_icu_data[pldirq].icucr|PLD_ICUCR_ILEVEL7; + outw(data, port); +} + +static void enable_m32700ut_pld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2pldirq(irq); +// enable_m32700ut_irq(M32R_IRQ_INT1); + port = pldirq2port(pldirq); + data = pld_icu_data[pldirq].icucr|PLD_ICUCR_IEN|PLD_ICUCR_ILEVEL6; + outw(data, port); +} + +static void mask_and_ack_m32700ut_pld(unsigned int irq) +{ + disable_m32700ut_pld_irq(irq); +// mask_and_ack_m32700ut(M32R_IRQ_INT1); +} + +static void end_m32700ut_pld_irq(unsigned int irq) +{ + enable_m32700ut_pld_irq(irq); + end_m32700ut_irq(M32R_IRQ_INT1); +} + +static unsigned int startup_m32700ut_pld_irq(unsigned int irq) +{ + enable_m32700ut_pld_irq(irq); + return (0); +} + +static void shutdown_m32700ut_pld_irq(unsigned int irq) +{ + unsigned long port; + unsigned int pldirq; + + pldirq = irq2pldirq(irq); +// shutdown_m32700ut_irq(M32R_IRQ_INT1); + port = pldirq2port(pldirq); + outw(PLD_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type m32700ut_pld_irq_type = +{ + "M32700UT-PLD-IRQ", + startup_m32700ut_pld_irq, + shutdown_m32700ut_pld_irq, + enable_m32700ut_pld_irq, + disable_m32700ut_pld_irq, + mask_and_ack_m32700ut_pld, + end_m32700ut_pld_irq +}; + +/* + * Interrupt Control Unit of PLD on M32700UT-LAN (Level 2) + */ +#define irq2lanpldirq(x) ((x) - M32700UT_LAN_PLD_IRQ_BASE) +#define lanpldirq2port(x) (unsigned long)((int)M32700UT_LAN_ICUCR1 + \ + (((x) - 1) * sizeof(unsigned short))) + +static pld_icu_data_t lanpld_icu_data[M32700UT_NUM_LAN_PLD_IRQ]; + +static void disable_m32700ut_lanpld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2lanpldirq(irq); + port = lanpldirq2port(pldirq); + data = lanpld_icu_data[pldirq].icucr|PLD_ICUCR_ILEVEL7; + outw(data, port); +} + +static void enable_m32700ut_lanpld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2lanpldirq(irq); + port = lanpldirq2port(pldirq); + data = lanpld_icu_data[pldirq].icucr|PLD_ICUCR_IEN|PLD_ICUCR_ILEVEL6; + outw(data, port); +} + +static void mask_and_ack_m32700ut_lanpld(unsigned int irq) +{ + disable_m32700ut_lanpld_irq(irq); +} + +static void end_m32700ut_lanpld_irq(unsigned int irq) +{ + enable_m32700ut_lanpld_irq(irq); + end_m32700ut_irq(M32R_IRQ_INT0); +} + +static unsigned int startup_m32700ut_lanpld_irq(unsigned int irq) +{ + enable_m32700ut_lanpld_irq(irq); + return (0); +} + +static void shutdown_m32700ut_lanpld_irq(unsigned int irq) +{ + unsigned long port; + unsigned int pldirq; + + pldirq = irq2lanpldirq(irq); + port = lanpldirq2port(pldirq); + outw(PLD_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type m32700ut_lanpld_irq_type = +{ + "M32700UT-PLD-LAN-IRQ", + startup_m32700ut_lanpld_irq, + shutdown_m32700ut_lanpld_irq, + enable_m32700ut_lanpld_irq, + disable_m32700ut_lanpld_irq, + mask_and_ack_m32700ut_lanpld, + end_m32700ut_lanpld_irq +}; + +/* + * Interrupt Control Unit of PLD on M32700UT-LCD (Level 2) + */ +#define irq2lcdpldirq(x) ((x) - M32700UT_LCD_PLD_IRQ_BASE) +#define lcdpldirq2port(x) (unsigned long)((int)M32700UT_LCD_ICUCR1 + \ + (((x) - 1) * sizeof(unsigned short))) + +static pld_icu_data_t lcdpld_icu_data[M32700UT_NUM_LCD_PLD_IRQ]; + +static void disable_m32700ut_lcdpld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2lcdpldirq(irq); + port = lcdpldirq2port(pldirq); + data = lcdpld_icu_data[pldirq].icucr|PLD_ICUCR_ILEVEL7; + outw(data, port); +} + +static void enable_m32700ut_lcdpld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2lcdpldirq(irq); + port = lcdpldirq2port(pldirq); + data = lcdpld_icu_data[pldirq].icucr|PLD_ICUCR_IEN|PLD_ICUCR_ILEVEL6; + outw(data, port); +} + +static void mask_and_ack_m32700ut_lcdpld(unsigned int irq) +{ + disable_m32700ut_lcdpld_irq(irq); +} + +static void end_m32700ut_lcdpld_irq(unsigned int irq) +{ + enable_m32700ut_lcdpld_irq(irq); + end_m32700ut_irq(M32R_IRQ_INT2); +} + +static unsigned int startup_m32700ut_lcdpld_irq(unsigned int irq) +{ + enable_m32700ut_lcdpld_irq(irq); + return (0); +} + +static void shutdown_m32700ut_lcdpld_irq(unsigned int irq) +{ + unsigned long port; + unsigned int pldirq; + + pldirq = irq2lcdpldirq(irq); + port = lcdpldirq2port(pldirq); + outw(PLD_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type m32700ut_lcdpld_irq_type = +{ + "M32700UT-PLD-LCD-IRQ", + startup_m32700ut_lcdpld_irq, + shutdown_m32700ut_lcdpld_irq, + enable_m32700ut_lcdpld_irq, + disable_m32700ut_lcdpld_irq, + mask_and_ack_m32700ut_lcdpld, + end_m32700ut_lcdpld_irq +}; + +void __init init_IRQ(void) +{ +#ifdef CONFIG_M32R_SMC91111 + /* INT#0: LAN controller on M32700UT-LAN (SMC91C111)*/ + irq_desc[M32700UT_LAN_IRQ_LAN].status = IRQ_DISABLED; + irq_desc[M32700UT_LAN_IRQ_LAN].handler = &m32700ut_lanpld_irq_type; + irq_desc[M32700UT_LAN_IRQ_LAN].action = 0; + irq_desc[M32700UT_LAN_IRQ_LAN].depth = 1; /* disable nested irq */ + lanpld_icu_data[irq2lanpldirq(M32700UT_LAN_IRQ_LAN)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD02; /* "H" edge sense */ + disable_m32700ut_lanpld_irq(M32700UT_LAN_IRQ_LAN); +#endif /* CONFIG_M32R_SMC91111 */ + + /* MFT2 : system timer */ + irq_desc[M32R_IRQ_MFT2].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_MFT2].handler = &m32700ut_irq_type; + irq_desc[M32R_IRQ_MFT2].action = 0; + irq_desc[M32R_IRQ_MFT2].depth = 1; + icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; + disable_m32700ut_irq(M32R_IRQ_MFT2); + + /* SIO0 : receive */ + irq_desc[M32R_IRQ_SIO0_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_R].handler = &m32700ut_irq_type; + irq_desc[M32R_IRQ_SIO0_R].action = 0; + irq_desc[M32R_IRQ_SIO0_R].depth = 1; + icu_data[M32R_IRQ_SIO0_R].icucr = 0; + disable_m32700ut_irq(M32R_IRQ_SIO0_R); + + /* SIO0 : send */ + irq_desc[M32R_IRQ_SIO0_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_S].handler = &m32700ut_irq_type; + irq_desc[M32R_IRQ_SIO0_S].action = 0; + irq_desc[M32R_IRQ_SIO0_S].depth = 1; + icu_data[M32R_IRQ_SIO0_S].icucr = 0; + disable_m32700ut_irq(M32R_IRQ_SIO0_S); + + /* SIO1 : receive */ + irq_desc[M32R_IRQ_SIO1_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_R].handler = &m32700ut_irq_type; + irq_desc[M32R_IRQ_SIO1_R].action = 0; + irq_desc[M32R_IRQ_SIO1_R].depth = 1; + icu_data[M32R_IRQ_SIO1_R].icucr = 0; + disable_m32700ut_irq(M32R_IRQ_SIO1_R); + + /* SIO1 : send */ + irq_desc[M32R_IRQ_SIO1_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_S].handler = &m32700ut_irq_type; + irq_desc[M32R_IRQ_SIO1_S].action = 0; + irq_desc[M32R_IRQ_SIO1_S].depth = 1; + icu_data[M32R_IRQ_SIO1_S].icucr = 0; + disable_m32700ut_irq(M32R_IRQ_SIO1_S); + + /* DMA1 : */ + irq_desc[M32R_IRQ_DMA1].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_DMA1].handler = &m32700ut_irq_type; + irq_desc[M32R_IRQ_DMA1].action = 0; + irq_desc[M32R_IRQ_DMA1].depth = 1; + icu_data[M32R_IRQ_DMA1].icucr = 0; + disable_m32700ut_irq(M32R_IRQ_DMA1); + +#ifdef CONFIG_SERIAL_M32R_PLDSIO + /* INT#1: SIO0 Receive on PLD */ + irq_desc[PLD_IRQ_SIO0_RCV].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_SIO0_RCV].handler = &m32700ut_pld_irq_type; + irq_desc[PLD_IRQ_SIO0_RCV].action = 0; + irq_desc[PLD_IRQ_SIO0_RCV].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_SIO0_RCV)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD03; + disable_m32700ut_pld_irq(PLD_IRQ_SIO0_RCV); + + /* INT#1: SIO0 Send on PLD */ + irq_desc[PLD_IRQ_SIO0_SND].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_SIO0_SND].handler = &m32700ut_pld_irq_type; + irq_desc[PLD_IRQ_SIO0_SND].action = 0; + irq_desc[PLD_IRQ_SIO0_SND].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_SIO0_SND)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD03; + disable_m32700ut_pld_irq(PLD_IRQ_SIO0_SND); +#endif /* CONFIG_SERIAL_M32R_PLDSIO */ + + /* INT#1: CFC IREQ on PLD */ + irq_desc[PLD_IRQ_CFIREQ].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_CFIREQ].handler = &m32700ut_pld_irq_type; + irq_desc[PLD_IRQ_CFIREQ].action = 0; + irq_desc[PLD_IRQ_CFIREQ].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_CFIREQ)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD01; /* 'L' level sense */ + disable_m32700ut_pld_irq(PLD_IRQ_CFIREQ); + + /* INT#1: CFC Insert on PLD */ + irq_desc[PLD_IRQ_CFC_INSERT].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_CFC_INSERT].handler = &m32700ut_pld_irq_type; + irq_desc[PLD_IRQ_CFC_INSERT].action = 0; + irq_desc[PLD_IRQ_CFC_INSERT].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_CFC_INSERT)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD00; /* 'L' edge sense */ + disable_m32700ut_pld_irq(PLD_IRQ_CFC_INSERT); + + /* INT#1: CFC Eject on PLD */ + irq_desc[PLD_IRQ_CFC_EJECT].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_CFC_EJECT].handler = &m32700ut_pld_irq_type; + irq_desc[PLD_IRQ_CFC_EJECT].action = 0; + irq_desc[PLD_IRQ_CFC_EJECT].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_CFC_EJECT)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD02; /* 'H' edge sense */ + disable_m32700ut_pld_irq(PLD_IRQ_CFC_EJECT); + + /* + * INT0# is used for LAN, DIO + * We enable it here. + */ + icu_data[M32R_IRQ_INT0].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD11; + enable_m32700ut_irq(M32R_IRQ_INT0); + + /* + * INT1# is used for UART, MMC, CF Controller in FPGA. + * We enable it here. + */ + icu_data[M32R_IRQ_INT1].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD11; + enable_m32700ut_irq(M32R_IRQ_INT1); + +#if defined(CONFIG_USB) + outw(USBCR_OTGS, USBCR); /* USBCR: non-OTG */ + + irq_desc[M32700UT_LCD_IRQ_USB_INT1].status = IRQ_DISABLED; + irq_desc[M32700UT_LCD_IRQ_USB_INT1].handler = &m32700ut_lcdpld_irq_type; + irq_desc[M32700UT_LCD_IRQ_USB_INT1].action = 0; + irq_desc[M32700UT_LCD_IRQ_USB_INT1].depth = 1; + lcdpld_icu_data[irq2lcdpldirq(M32700UT_LCD_IRQ_USB_INT1)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD01; /* "L" level sense */ + disable_m32700ut_lcdpld_irq(M32700UT_LCD_IRQ_USB_INT1); +#endif + /* + * INT2# is used for BAT, USB, AUDIO + * We enable it here. + */ + icu_data[M32R_IRQ_INT2].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD01; + enable_m32700ut_irq(M32R_IRQ_INT2); + +//#if defined(CONFIG_M32R_AR_VGA) + /* + * INT3# is used for AR + */ + irq_desc[M32R_IRQ_INT3].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_INT3].handler = &m32700ut_irq_type; + irq_desc[M32R_IRQ_INT3].action = 0; + irq_desc[M32R_IRQ_INT3].depth = 1; + icu_data[M32R_IRQ_INT3].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; + disable_m32700ut_irq(M32R_IRQ_INT3); +//#endif /* CONFIG_M32R_ARV */ +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/setup_mappi2.c 2004-09-03 00:57:17.341402968 -0700 @@ -0,0 +1,186 @@ +/* + * linux/arch/m32r/kernel/setup_mappi.c + * + * Setup routines for Renesas MAPPI-II(M3A-ZA36) Board + * + * Copyright (c) 2001, 2002 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto, Mamoru Sakugawa + */ + +static char *rcsid = +"$Id$"; +static void use_rcsid(void) {rcsid = rcsid; use_rcsid();} + +#include +#include +#include +#include + +#include +#include +#include + +#define irq2port(x) (M32R_ICU_CR1_PORTL + ((x - 1) * sizeof(unsigned long))) + +#ifndef CONFIG_SMP +typedef struct { + unsigned long icucr; /* ICU Control Register */ +} icu_data_t; +#endif /* CONFIG_SMP */ + +icu_data_t icu_data[NR_IRQS]; + +static void disable_mappi2_irq(unsigned int irq) +{ + unsigned long port, data; + + if ((irq == 0) ||(irq >= NR_IRQS)) { + printk("bad irq 0x%08x\n", irq); + return; + } + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_ILEVEL7; + outl(data, port); +} + +static void enable_mappi2_irq(unsigned int irq) +{ + unsigned long port, data; + + if ((irq == 0) ||(irq >= NR_IRQS)) { + printk("bad irq 0x%08x\n", irq); + return; + } + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_IEN|M32R_ICUCR_ILEVEL6; + outl(data, port); +} + +static void mask_and_ack_mappi2(unsigned int irq) +{ + disable_mappi2_irq(irq); +} + +static void end_mappi2_irq(unsigned int irq) +{ + enable_mappi2_irq(irq); +} + +static unsigned int startup_mappi2_irq(unsigned int irq) +{ + enable_mappi2_irq(irq); + return (0); +} + +static void shutdown_mappi2_irq(unsigned int irq) +{ + unsigned long port; + + port = irq2port(irq); + outl(M32R_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type mappi2_irq_type = +{ + "MAPPI2-IRQ", + startup_mappi2_irq, + shutdown_mappi2_irq, + enable_mappi2_irq, + disable_mappi2_irq, + mask_and_ack_mappi2, + end_mappi2_irq +}; + +void __init init_IRQ(void) +{ +#ifdef CONFIG_M32R_SMC91111 + /* INT0 : LAN controller (SMC91111) */ + irq_desc[M32R_IRQ_INT0].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_INT0].handler = &mappi2_irq_type; + irq_desc[M32R_IRQ_INT0].action = 0; + irq_desc[M32R_IRQ_INT0].depth = 1; + icu_data[M32R_IRQ_INT0].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; + disable_mappi2_irq(M32R_IRQ_INT0); +#endif /* CONFIG_MAPPI2_SMC9111 */ + + /* MFT2 : system timer */ + irq_desc[M32R_IRQ_MFT2].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_MFT2].handler = &mappi2_irq_type; + irq_desc[M32R_IRQ_MFT2].action = 0; + irq_desc[M32R_IRQ_MFT2].depth = 1; + icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; + disable_mappi2_irq(M32R_IRQ_MFT2); + +#ifdef CONFIG_SERIAL_M32R_SIO + /* SIO0_R : uart receive data */ + irq_desc[M32R_IRQ_SIO0_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_R].handler = &mappi2_irq_type; + irq_desc[M32R_IRQ_SIO0_R].action = 0; + irq_desc[M32R_IRQ_SIO0_R].depth = 1; + icu_data[M32R_IRQ_SIO0_R].icucr = 0; + disable_mappi2_irq(M32R_IRQ_SIO0_R); + + /* SIO0_S : uart send data */ + irq_desc[M32R_IRQ_SIO0_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_S].handler = &mappi2_irq_type; + irq_desc[M32R_IRQ_SIO0_S].action = 0; + irq_desc[M32R_IRQ_SIO0_S].depth = 1; + icu_data[M32R_IRQ_SIO0_S].icucr = 0; + disable_mappi2_irq(M32R_IRQ_SIO0_S); + /* SIO1_R : uart receive data */ + irq_desc[M32R_IRQ_SIO1_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_R].handler = &mappi2_irq_type; + irq_desc[M32R_IRQ_SIO1_R].action = 0; + irq_desc[M32R_IRQ_SIO1_R].depth = 1; + icu_data[M32R_IRQ_SIO1_R].icucr = 0; + disable_mappi2_irq(M32R_IRQ_SIO1_R); + + /* SIO1_S : uart send data */ + irq_desc[M32R_IRQ_SIO1_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_S].handler = &mappi2_irq_type; + irq_desc[M32R_IRQ_SIO1_S].action = 0; + irq_desc[M32R_IRQ_SIO1_S].depth = 1; + icu_data[M32R_IRQ_SIO1_S].icucr = 0; + disable_mappi2_irq(M32R_IRQ_SIO1_S); +#endif /* CONFIG_M32R_USE_DBG_CONSOLE */ + +#if defined(CONFIG_USB) + /* INT1 : USB Host controller interrupt */ + irq_desc[M32R_IRQ_INT1].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_INT1].handler = &mappi2_irq_type; + irq_desc[M32R_IRQ_INT1].action = 0; + irq_desc[M32R_IRQ_INT1].depth = 1; + icu_data[M32R_IRQ_INT1].icucr = M32R_ICUCR_ISMOD01; + disable_mappi2_irq(M32R_IRQ_INT1); +#endif /* CONFIG_USB */ + +#if defined(CONFIG_M32R_CFC) + /* ICUCR40: CFC IREQ */ + irq_desc[PLD_IRQ_CFIREQ].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_CFIREQ].handler = &mappi2_irq_type; + irq_desc[PLD_IRQ_CFIREQ].action = 0; + irq_desc[PLD_IRQ_CFIREQ].depth = 1; /* disable nested irq */ +// icu_data[PLD_IRQ_CFIREQ].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD00; + icu_data[PLD_IRQ_CFIREQ].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD01; + disable_mappi2_irq(PLD_IRQ_CFIREQ); + + /* ICUCR41: CFC Insert */ + irq_desc[PLD_IRQ_CFC_INSERT].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_CFC_INSERT].handler = &mappi2_irq_type; + irq_desc[PLD_IRQ_CFC_INSERT].action = 0; + irq_desc[PLD_IRQ_CFC_INSERT].depth = 1; /* disable nested irq */ + icu_data[PLD_IRQ_CFC_INSERT].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD00; +// icu_data[PLD_IRQ_CFC_INSERT].icucr = 0; + disable_mappi2_irq(PLD_IRQ_CFC_INSERT); + + /* ICUCR42: CFC Eject */ + irq_desc[PLD_IRQ_CFC_EJECT].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_CFC_EJECT].handler = &mappi2_irq_type; + irq_desc[PLD_IRQ_CFC_EJECT].action = 0; + irq_desc[PLD_IRQ_CFC_EJECT].depth = 1; /* disable nested irq */ + icu_data[PLD_IRQ_CFC_EJECT].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; +// icu_data[PLD_IRQ_CFC_EJECT].icucr = 0; + disable_mappi2_irq(PLD_IRQ_CFC_EJECT); + +#endif /* CONFIG_MAPPI2_CFC */ +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/setup_mappi.c 2004-09-03 00:57:17.342402816 -0700 @@ -0,0 +1,164 @@ +/* + * linux/arch/m32r/kernel/setup_mappi.c + * + * Setup routines for MITSUBISHI MAPPI Board + * + * Copyright (c) 2001, 2002 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto + */ + +static char *rcsid = +"$Id$"; +static void use_rcsid(void) {rcsid = rcsid; use_rcsid();} + +#include +#include +#include +#include + +#include +#include +#include + +#define irq2port(x) (M32R_ICU_CR1_PORTL + ((x - 1) * sizeof(unsigned long))) + +#ifndef CONFIG_SMP +typedef struct { + unsigned long icucr; /* ICU Control Register */ +} icu_data_t; +#endif /* CONFIG_SMP */ + +icu_data_t icu_data[NR_IRQS]; + +static void disable_mappi_irq(unsigned int irq) +{ + unsigned long port, data; + + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_ILEVEL7; + outl(data, port); +} + +static void enable_mappi_irq(unsigned int irq) +{ + unsigned long port, data; + + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_IEN|M32R_ICUCR_ILEVEL6; + outl(data, port); +} + +static void mask_and_ack_mappi(unsigned int irq) +{ + disable_mappi_irq(irq); +} + +static void end_mappi_irq(unsigned int irq) +{ + enable_mappi_irq(irq); +} + +static unsigned int startup_mappi_irq(unsigned int irq) +{ + enable_mappi_irq(irq); + return (0); +} + +static void shutdown_mappi_irq(unsigned int irq) +{ + unsigned long port; + + port = irq2port(irq); + outl(M32R_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type mappi_irq_type = +{ + "MAPPI-IRQ", + startup_mappi_irq, + shutdown_mappi_irq, + enable_mappi_irq, + disable_mappi_irq, + mask_and_ack_mappi, + end_mappi_irq +}; + +void __init init_IRQ(void) +{ + static int once = 0; + + if (once) + return; + else + once++; + +#ifdef CONFIG_M32R_NE2000 + /* INT0 : LAN controller (RTL8019AS) */ + irq_desc[M32R_IRQ_INT0].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_INT0].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_INT0].action = 0; + irq_desc[M32R_IRQ_INT0].depth = 1; + icu_data[M32R_IRQ_INT0].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; + disable_mappi_irq(M32R_IRQ_INT0); +#endif /* CONFIG_M32R_NE2000 */ + + /* MFT2 : system timer */ + irq_desc[M32R_IRQ_MFT2].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_MFT2].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_MFT2].action = 0; + irq_desc[M32R_IRQ_MFT2].depth = 1; + icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; + disable_mappi_irq(M32R_IRQ_MFT2); + +#ifdef CONFIG_SERIAL_M32R_SIO + /* SIO0_R : uart receive data */ + irq_desc[M32R_IRQ_SIO0_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_R].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_SIO0_R].action = 0; + irq_desc[M32R_IRQ_SIO0_R].depth = 1; + icu_data[M32R_IRQ_SIO0_R].icucr = 0; + disable_mappi_irq(M32R_IRQ_SIO0_R); + + /* SIO0_S : uart send data */ + irq_desc[M32R_IRQ_SIO0_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_S].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_SIO0_S].action = 0; + irq_desc[M32R_IRQ_SIO0_S].depth = 1; + icu_data[M32R_IRQ_SIO0_S].icucr = 0; + disable_mappi_irq(M32R_IRQ_SIO0_S); + + /* SIO1_R : uart receive data */ + irq_desc[M32R_IRQ_SIO1_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_R].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_SIO1_R].action = 0; + irq_desc[M32R_IRQ_SIO1_R].depth = 1; + icu_data[M32R_IRQ_SIO1_R].icucr = 0; + disable_mappi_irq(M32R_IRQ_SIO1_R); + + /* SIO1_S : uart send data */ + irq_desc[M32R_IRQ_SIO1_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_S].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_SIO1_S].action = 0; + irq_desc[M32R_IRQ_SIO1_S].depth = 1; + icu_data[M32R_IRQ_SIO1_S].icucr = 0; + disable_mappi_irq(M32R_IRQ_SIO1_S); +#endif /* CONFIG_SERIAL_M32R_SIO */ + +#if defined(CONFIG_M32RPCC) + /* INT1 : pccard0 interrupt */ + irq_desc[M32R_IRQ_INT1].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_INT1].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_INT1].action = 0; + irq_desc[M32R_IRQ_INT1].depth = 1; + icu_data[M32R_IRQ_INT1].icucr = M32R_ICUCR_IEN | M32R_ICUCR_ISMOD00; + disable_mappi_irq(M32R_IRQ_INT1); + + /* INT2 : pccard1 interrupt */ + irq_desc[M32R_IRQ_INT2].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_INT2].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_INT2].action = 0; + irq_desc[M32R_IRQ_INT2].depth = 1; + icu_data[M32R_IRQ_INT2].icucr = M32R_ICUCR_IEN | M32R_ICUCR_ISMOD00; + disable_mappi_irq(M32R_IRQ_INT2); +#endif /* CONFIG_M32RPCC */ +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/setup_oaks32r.c 2004-09-03 00:57:17.342402816 -0700 @@ -0,0 +1,147 @@ +/* + * linux/arch/m32r/kernel/setup_oaks32r.c + * + * Setup routines for OAKS32R Board + * + * Copyright (c) 2002-2004 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto, Mamoru Sakugawa + */ + +static char *rcsid = +"$Id: setup_oaks32r.c,v 1.1 2004/03/31 05:06:18 sakugawa Exp $"; +static void use_rcsid(void) {rcsid = rcsid; use_rcsid();} + +#include +#include +#include +#include + +#include +#include +#include + +#define irq2port(x) (M32R_ICU_CR1_PORTL + ((x - 1) * sizeof(unsigned long))) + +#ifndef CONFIG_SMP +typedef struct { + unsigned long icucr; /* ICU Control Register */ +} icu_data_t; +#endif /* CONFIG_SMP */ + +icu_data_t icu_data[NR_IRQS]; + +static void disable_oaks32r_irq(unsigned int irq) +{ + unsigned long port, data; + + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_ILEVEL7; + outl(data, port); +} + +static void enable_oaks32r_irq(unsigned int irq) +{ + unsigned long port, data; + + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_IEN|M32R_ICUCR_ILEVEL6; + outl(data, port); +} + +static void mask_and_ack_mappi(unsigned int irq) +{ + disable_oaks32r_irq(irq); +} + +static void end_oaks32r_irq(unsigned int irq) +{ + enable_oaks32r_irq(irq); +} + +static unsigned int startup_oaks32r_irq(unsigned int irq) +{ + enable_oaks32r_irq(irq); + return (0); +} + +static void shutdown_oaks32r_irq(unsigned int irq) +{ + unsigned long port; + + port = irq2port(irq); + outl(M32R_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type oaks32r_irq_type = +{ + "OAKS32R-IRQ", + startup_oaks32r_irq, + shutdown_oaks32r_irq, + enable_oaks32r_irq, + disable_oaks32r_irq, + mask_and_ack_mappi, + end_oaks32r_irq +}; + +void __init init_IRQ(void) +{ + static int once = 0; + + if (once) + return; + else + once++; + +#ifdef CONFIG_M32R_NE2000 + /* INT3 : LAN controller (RTL8019AS) */ + irq_desc[M32R_IRQ_INT3].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_INT3].handler = &oaks32r_irq_type; + irq_desc[M32R_IRQ_INT3].action = 0; + irq_desc[M32R_IRQ_INT3].depth = 1; + icu_data[M32R_IRQ_INT3].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; + disable_oaks32r_irq(M32R_IRQ_INT3); +#endif /* CONFIG_M32R_NE2000 */ + + /* MFT2 : system timer */ + irq_desc[M32R_IRQ_MFT2].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_MFT2].handler = &oaks32r_irq_type; + irq_desc[M32R_IRQ_MFT2].action = 0; + irq_desc[M32R_IRQ_MFT2].depth = 1; + icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; + disable_oaks32r_irq(M32R_IRQ_MFT2); + +#ifdef CONFIG_SERIAL_M32R_SIO + /* SIO0_R : uart receive data */ + irq_desc[M32R_IRQ_SIO0_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_R].handler = &oaks32r_irq_type; + irq_desc[M32R_IRQ_SIO0_R].action = 0; + irq_desc[M32R_IRQ_SIO0_R].depth = 1; + icu_data[M32R_IRQ_SIO0_R].icucr = 0; + disable_oaks32r_irq(M32R_IRQ_SIO0_R); + + /* SIO0_S : uart send data */ + irq_desc[M32R_IRQ_SIO0_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_S].handler = &oaks32r_irq_type; + irq_desc[M32R_IRQ_SIO0_S].action = 0; + irq_desc[M32R_IRQ_SIO0_S].depth = 1; + icu_data[M32R_IRQ_SIO0_S].icucr = 0; + disable_oaks32r_irq(M32R_IRQ_SIO0_S); + + /* SIO1_R : uart receive data */ + irq_desc[M32R_IRQ_SIO1_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_R].handler = &oaks32r_irq_type; + irq_desc[M32R_IRQ_SIO1_R].action = 0; + irq_desc[M32R_IRQ_SIO1_R].depth = 1; + icu_data[M32R_IRQ_SIO1_R].icucr = 0; + disable_oaks32r_irq(M32R_IRQ_SIO1_R); + + /* SIO1_S : uart send data */ + irq_desc[M32R_IRQ_SIO1_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_S].handler = &oaks32r_irq_type; + irq_desc[M32R_IRQ_SIO1_S].action = 0; + irq_desc[M32R_IRQ_SIO1_S].depth = 1; + icu_data[M32R_IRQ_SIO1_S].icucr = 0; + disable_oaks32r_irq(M32R_IRQ_SIO1_S); +#endif /* CONFIG_SERIAL_M32R_SIO */ + +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/setup_opsput.c 2004-09-03 00:57:17.344402512 -0700 @@ -0,0 +1,454 @@ +/* + * linux/arch/m32r/kernel/setup_opsput.c + * + * Setup routines for Renesas OPSPUT Board + * + * Copyright (c) 2002-2004 + * Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto, Takeo Takahashi, Mamoru Sakugawa + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * $Id: setup_opsput.c,v 1.1 2004/07/27 06:54:20 sakugawa Exp $ + */ + +#include +#include +#include +#include + +#include +#include +#include + +/* + * OPSP Interrupt Control Unit (Level 1) + */ +#define irq2port(x) (M32R_ICU_CR1_PORTL + ((x - 1) * sizeof(unsigned long))) + +#ifndef CONFIG_SMP +typedef struct { + unsigned long icucr; /* ICU Control Register */ +} icu_data_t; +#endif /* CONFIG_SMP */ + +static icu_data_t icu_data[OPSPUT_NUM_CPU_IRQ]; + +static void disable_opsput_irq(unsigned int irq) +{ + unsigned long port, data; + + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_ILEVEL7; + outl(data, port); +} + +static void enable_opsput_irq(unsigned int irq) +{ + unsigned long port, data; + + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_IEN|M32R_ICUCR_ILEVEL6; + outl(data, port); +} + +static void mask_and_ack_opsput(unsigned int irq) +{ + disable_opsput_irq(irq); +} + +static void end_opsput_irq(unsigned int irq) +{ + enable_opsput_irq(irq); +} + +static unsigned int startup_opsput_irq(unsigned int irq) +{ + enable_opsput_irq(irq); + return (0); +} + +static void shutdown_opsput_irq(unsigned int irq) +{ + unsigned long port; + + port = irq2port(irq); + outl(M32R_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type opsput_irq_type = +{ + "OPSPUT-IRQ", + startup_opsput_irq, + shutdown_opsput_irq, + enable_opsput_irq, + disable_opsput_irq, + mask_and_ack_opsput, + end_opsput_irq +}; + +/* + * Interrupt Control Unit of PLD on OPSPUT (Level 2) + */ +#define irq2pldirq(x) ((x) - OPSPUT_PLD_IRQ_BASE) +#define pldirq2port(x) (unsigned long)((int)PLD_ICUCR1 + \ + (((x) - 1) * sizeof(unsigned short))) + +typedef struct { + unsigned short icucr; /* ICU Control Register */ +} pld_icu_data_t; + +static pld_icu_data_t pld_icu_data[OPSPUT_NUM_PLD_IRQ]; + +static void disable_opsput_pld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2pldirq(irq); +// disable_opsput_irq(M32R_IRQ_INT1); + port = pldirq2port(pldirq); + data = pld_icu_data[pldirq].icucr|PLD_ICUCR_ILEVEL7; + outw(data, port); +} + +static void enable_opsput_pld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2pldirq(irq); +// enable_opsput_irq(M32R_IRQ_INT1); + port = pldirq2port(pldirq); + data = pld_icu_data[pldirq].icucr|PLD_ICUCR_IEN|PLD_ICUCR_ILEVEL6; + outw(data, port); +} + +static void mask_and_ack_opsput_pld(unsigned int irq) +{ + disable_opsput_pld_irq(irq); +// mask_and_ack_opsput(M32R_IRQ_INT1); +} + +static void end_opsput_pld_irq(unsigned int irq) +{ + enable_opsput_pld_irq(irq); + end_opsput_irq(M32R_IRQ_INT1); +} + +static unsigned int startup_opsput_pld_irq(unsigned int irq) +{ + enable_opsput_pld_irq(irq); + return (0); +} + +static void shutdown_opsput_pld_irq(unsigned int irq) +{ + unsigned long port; + unsigned int pldirq; + + pldirq = irq2pldirq(irq); +// shutdown_opsput_irq(M32R_IRQ_INT1); + port = pldirq2port(pldirq); + outw(PLD_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type opsput_pld_irq_type = +{ + "OPSPUT-PLD-IRQ", + startup_opsput_pld_irq, + shutdown_opsput_pld_irq, + enable_opsput_pld_irq, + disable_opsput_pld_irq, + mask_and_ack_opsput_pld, + end_opsput_pld_irq +}; + +/* + * Interrupt Control Unit of PLD on OPSPUT-LAN (Level 2) + */ +#define irq2lanpldirq(x) ((x) - OPSPUT_LAN_PLD_IRQ_BASE) +#define lanpldirq2port(x) (unsigned long)((int)OPSPUT_LAN_ICUCR1 + \ + (((x) - 1) * sizeof(unsigned short))) + +static pld_icu_data_t lanpld_icu_data[OPSPUT_NUM_LAN_PLD_IRQ]; + +static void disable_opsput_lanpld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2lanpldirq(irq); + port = lanpldirq2port(pldirq); + data = lanpld_icu_data[pldirq].icucr|PLD_ICUCR_ILEVEL7; + outw(data, port); +} + +static void enable_opsput_lanpld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2lanpldirq(irq); + port = lanpldirq2port(pldirq); + data = lanpld_icu_data[pldirq].icucr|PLD_ICUCR_IEN|PLD_ICUCR_ILEVEL6; + outw(data, port); +} + +static void mask_and_ack_opsput_lanpld(unsigned int irq) +{ + disable_opsput_lanpld_irq(irq); +} + +static void end_opsput_lanpld_irq(unsigned int irq) +{ + enable_opsput_lanpld_irq(irq); + end_opsput_irq(M32R_IRQ_INT0); +} + +static unsigned int startup_opsput_lanpld_irq(unsigned int irq) +{ + enable_opsput_lanpld_irq(irq); + return (0); +} + +static void shutdown_opsput_lanpld_irq(unsigned int irq) +{ + unsigned long port; + unsigned int pldirq; + + pldirq = irq2lanpldirq(irq); + port = lanpldirq2port(pldirq); + outw(PLD_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type opsput_lanpld_irq_type = +{ + "OPSPUT-PLD-LAN-IRQ", + startup_opsput_lanpld_irq, + shutdown_opsput_lanpld_irq, + enable_opsput_lanpld_irq, + disable_opsput_lanpld_irq, + mask_and_ack_opsput_lanpld, + end_opsput_lanpld_irq +}; + +/* + * Interrupt Control Unit of PLD on OPSPUT-LCD (Level 2) + */ +#define irq2lcdpldirq(x) ((x) - OPSPUT_LCD_PLD_IRQ_BASE) +#define lcdpldirq2port(x) (unsigned long)((int)OPSPUT_LCD_ICUCR1 + \ + (((x) - 1) * sizeof(unsigned short))) + +static pld_icu_data_t lcdpld_icu_data[OPSPUT_NUM_LCD_PLD_IRQ]; + +static void disable_opsput_lcdpld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2lcdpldirq(irq); + port = lcdpldirq2port(pldirq); + data = lcdpld_icu_data[pldirq].icucr|PLD_ICUCR_ILEVEL7; + outw(data, port); +} + +static void enable_opsput_lcdpld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2lcdpldirq(irq); + port = lcdpldirq2port(pldirq); + data = lcdpld_icu_data[pldirq].icucr|PLD_ICUCR_IEN|PLD_ICUCR_ILEVEL6; + outw(data, port); +} + +static void mask_and_ack_opsput_lcdpld(unsigned int irq) +{ + disable_opsput_lcdpld_irq(irq); +} + +static void end_opsput_lcdpld_irq(unsigned int irq) +{ + enable_opsput_lcdpld_irq(irq); + end_opsput_irq(M32R_IRQ_INT2); +} + +static unsigned int startup_opsput_lcdpld_irq(unsigned int irq) +{ + enable_opsput_lcdpld_irq(irq); + return (0); +} + +static void shutdown_opsput_lcdpld_irq(unsigned int irq) +{ + unsigned long port; + unsigned int pldirq; + + pldirq = irq2lcdpldirq(irq); + port = lcdpldirq2port(pldirq); + outw(PLD_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type opsput_lcdpld_irq_type = +{ + "OPSPUT-PLD-LCD-IRQ", + startup_opsput_lcdpld_irq, + shutdown_opsput_lcdpld_irq, + enable_opsput_lcdpld_irq, + disable_opsput_lcdpld_irq, + mask_and_ack_opsput_lcdpld, + end_opsput_lcdpld_irq +}; + +void __init init_IRQ(void) +{ +#ifdef CONFIG_M32R_SMC91111 + /* INT#0: LAN controller on OPSPUT-LAN (SMC91C111)*/ + irq_desc[OPSPUT_LAN_IRQ_LAN].status = IRQ_DISABLED; + irq_desc[OPSPUT_LAN_IRQ_LAN].handler = &opsput_lanpld_irq_type; + irq_desc[OPSPUT_LAN_IRQ_LAN].action = 0; + irq_desc[OPSPUT_LAN_IRQ_LAN].depth = 1; /* disable nested irq */ + lanpld_icu_data[irq2lanpldirq(OPSPUT_LAN_IRQ_LAN)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD02; /* "H" edge sense */ + disable_opsput_lanpld_irq(OPSPUT_LAN_IRQ_LAN); +#endif /* CONFIG_M32R_SMC91111 */ + + /* MFT2 : system timer */ + irq_desc[M32R_IRQ_MFT2].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_MFT2].handler = &opsput_irq_type; + irq_desc[M32R_IRQ_MFT2].action = 0; + irq_desc[M32R_IRQ_MFT2].depth = 1; + icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; + disable_opsput_irq(M32R_IRQ_MFT2); + + /* SIO0 : receive */ + irq_desc[M32R_IRQ_SIO0_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_R].handler = &opsput_irq_type; + irq_desc[M32R_IRQ_SIO0_R].action = 0; + irq_desc[M32R_IRQ_SIO0_R].depth = 1; + icu_data[M32R_IRQ_SIO0_R].icucr = 0; + disable_opsput_irq(M32R_IRQ_SIO0_R); + + /* SIO0 : send */ + irq_desc[M32R_IRQ_SIO0_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_S].handler = &opsput_irq_type; + irq_desc[M32R_IRQ_SIO0_S].action = 0; + irq_desc[M32R_IRQ_SIO0_S].depth = 1; + icu_data[M32R_IRQ_SIO0_S].icucr = 0; + disable_opsput_irq(M32R_IRQ_SIO0_S); + + /* SIO1 : receive */ + irq_desc[M32R_IRQ_SIO1_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_R].handler = &opsput_irq_type; + irq_desc[M32R_IRQ_SIO1_R].action = 0; + irq_desc[M32R_IRQ_SIO1_R].depth = 1; + icu_data[M32R_IRQ_SIO1_R].icucr = 0; + disable_opsput_irq(M32R_IRQ_SIO1_R); + + /* SIO1 : send */ + irq_desc[M32R_IRQ_SIO1_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_S].handler = &opsput_irq_type; + irq_desc[M32R_IRQ_SIO1_S].action = 0; + irq_desc[M32R_IRQ_SIO1_S].depth = 1; + icu_data[M32R_IRQ_SIO1_S].icucr = 0; + disable_opsput_irq(M32R_IRQ_SIO1_S); + + /* DMA1 : */ + irq_desc[M32R_IRQ_DMA1].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_DMA1].handler = &opsput_irq_type; + irq_desc[M32R_IRQ_DMA1].action = 0; + irq_desc[M32R_IRQ_DMA1].depth = 1; + icu_data[M32R_IRQ_DMA1].icucr = 0; + disable_opsput_irq(M32R_IRQ_DMA1); + +#ifdef CONFIG_SERIAL_M32R_PLDSIO + /* INT#1: SIO0 Receive on PLD */ + irq_desc[PLD_IRQ_SIO0_RCV].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_SIO0_RCV].handler = &opsput_pld_irq_type; + irq_desc[PLD_IRQ_SIO0_RCV].action = 0; + irq_desc[PLD_IRQ_SIO0_RCV].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_SIO0_RCV)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD03; + disable_opsput_pld_irq(PLD_IRQ_SIO0_RCV); + + /* INT#1: SIO0 Send on PLD */ + irq_desc[PLD_IRQ_SIO0_SND].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_SIO0_SND].handler = &opsput_pld_irq_type; + irq_desc[PLD_IRQ_SIO0_SND].action = 0; + irq_desc[PLD_IRQ_SIO0_SND].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_SIO0_SND)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD03; + disable_opsput_pld_irq(PLD_IRQ_SIO0_SND); +#endif /* CONFIG_SERIAL_M32R_PLDSIO */ + +#if defined(CONFIG_M32R_CFC) + /* INT#1: CFC IREQ on PLD */ + irq_desc[PLD_IRQ_CFIREQ].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_CFIREQ].handler = &opsput_pld_irq_type; + irq_desc[PLD_IRQ_CFIREQ].action = 0; + irq_desc[PLD_IRQ_CFIREQ].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_CFIREQ)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD01; /* 'L' level sense */ + disable_opsput_pld_irq(PLD_IRQ_CFIREQ); + + /* INT#1: CFC Insert on PLD */ + irq_desc[PLD_IRQ_CFC_INSERT].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_CFC_INSERT].handler = &opsput_pld_irq_type; + irq_desc[PLD_IRQ_CFC_INSERT].action = 0; + irq_desc[PLD_IRQ_CFC_INSERT].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_CFC_INSERT)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD00; /* 'L' edge sense */ + disable_opsput_pld_irq(PLD_IRQ_CFC_INSERT); + + /* INT#1: CFC Eject on PLD */ + irq_desc[PLD_IRQ_CFC_EJECT].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_CFC_EJECT].handler = &opsput_pld_irq_type; + irq_desc[PLD_IRQ_CFC_EJECT].action = 0; + irq_desc[PLD_IRQ_CFC_EJECT].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_CFC_EJECT)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD02; /* 'H' edge sense */ + disable_opsput_pld_irq(PLD_IRQ_CFC_EJECT); +#endif /* CONFIG_M32R_CFC */ + + + /* + * INT0# is used for LAN, DIO + * We enable it here. + */ + icu_data[M32R_IRQ_INT0].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD11; + enable_opsput_irq(M32R_IRQ_INT0); + + /* + * INT1# is used for UART, MMC, CF Controller in FPGA. + * We enable it here. + */ + icu_data[M32R_IRQ_INT1].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD11; + enable_opsput_irq(M32R_IRQ_INT1); + +#if defined(CONFIG_USB) + outw(USBCR_OTGS, USBCR); /* USBCR: non-OTG */ + + irq_desc[OPSPUT_LCD_IRQ_USB_INT1].status = IRQ_DISABLED; + irq_desc[OPSPUT_LCD_IRQ_USB_INT1].handler = &opsput_lcdpld_irq_type; + irq_desc[OPSPUT_LCD_IRQ_USB_INT1].action = 0; + irq_desc[OPSPUT_LCD_IRQ_USB_INT1].depth = 1; + lcdpld_icu_data[irq2lcdpldirq(OPSPUT_LCD_IRQ_USB_INT1)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD01; /* "L" level sense */ + disable_opsput_lcdpld_irq(OPSPUT_LCD_IRQ_USB_INT1); +#endif + /* + * INT2# is used for BAT, USB, AUDIO + * We enable it here. + */ + icu_data[M32R_IRQ_INT2].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD01; + enable_opsput_irq(M32R_IRQ_INT2); + +//#if defined(CONFIG_M32R_AR_VGA) + /* + * INT3# is used for AR + */ + irq_desc[M32R_IRQ_INT3].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_INT3].handler = &opsput_irq_type; + irq_desc[M32R_IRQ_INT3].action = 0; + irq_desc[M32R_IRQ_INT3].depth = 1; + icu_data[M32R_IRQ_INT3].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; + disable_opsput_irq(M32R_IRQ_INT3); +//#endif /* CONFIG_M32R_ARV */ +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/setup_usrv.c 2004-09-03 00:57:17.346402208 -0700 @@ -0,0 +1,260 @@ +/* + * linux/arch/m32r/kernel/setup_usrv.c + * + * Setup routines for MITSUBISHI uServer + * + * Copyright (c) 2001, 2002, 2003 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto + */ + +static char *rcsid = +"$Id$"; +static void use_rcsid(void) {rcsid = rcsid; use_rcsid();} + +#include +#include +#include +#include + +#include +#include +#include + +#define irq2port(x) (M32R_ICU_CR1_PORTL + ((x - 1) * sizeof(unsigned long))) + +#if !defined(CONFIG_SMP) +typedef struct { + unsigned long icucr; /* ICU Control Register */ +} icu_data_t; +#endif /* CONFIG_SMP */ + +icu_data_t icu_data[M32700UT_NUM_CPU_IRQ]; + +static void disable_mappi_irq(unsigned int irq) +{ + unsigned long port, data; + + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_ILEVEL7; + outl(data, port); +} + +static void enable_mappi_irq(unsigned int irq) +{ + unsigned long port, data; + + port = irq2port(irq); + data = icu_data[irq].icucr|M32R_ICUCR_IEN|M32R_ICUCR_ILEVEL6; + outl(data, port); +} + +static void mask_and_ack_mappi(unsigned int irq) +{ + disable_mappi_irq(irq); +} + +static void end_mappi_irq(unsigned int irq) +{ + enable_mappi_irq(irq); +} + +static unsigned int startup_mappi_irq(unsigned int irq) +{ + enable_mappi_irq(irq); + return 0; +} + +static void shutdown_mappi_irq(unsigned int irq) +{ + unsigned long port; + + port = irq2port(irq); + outl(M32R_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type mappi_irq_type = +{ + "M32700-IRQ", + startup_mappi_irq, + shutdown_mappi_irq, + enable_mappi_irq, + disable_mappi_irq, + mask_and_ack_mappi, + end_mappi_irq +}; + +/* + * Interrupt Control Unit of PLD on M32700UT (Level 2) + */ +#define irq2pldirq(x) ((x) - M32700UT_PLD_IRQ_BASE) +#define pldirq2port(x) (unsigned long)((int)PLD_ICUCR1 + \ + (((x) - 1) * sizeof(unsigned short))) + +typedef struct { + unsigned short icucr; /* ICU Control Register */ +} pld_icu_data_t; + +static pld_icu_data_t pld_icu_data[M32700UT_NUM_PLD_IRQ]; + +static void disable_m32700ut_pld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2pldirq(irq); + port = pldirq2port(pldirq); + data = pld_icu_data[pldirq].icucr|PLD_ICUCR_ILEVEL7; + outw(data, port); +} + +static void enable_m32700ut_pld_irq(unsigned int irq) +{ + unsigned long port, data; + unsigned int pldirq; + + pldirq = irq2pldirq(irq); + port = pldirq2port(pldirq); + data = pld_icu_data[pldirq].icucr|PLD_ICUCR_IEN|PLD_ICUCR_ILEVEL6; + outw(data, port); +} + +static void mask_and_ack_m32700ut_pld(unsigned int irq) +{ + disable_m32700ut_pld_irq(irq); +} + +static void end_m32700ut_pld_irq(unsigned int irq) +{ + enable_m32700ut_pld_irq(irq); + end_mappi_irq(M32R_IRQ_INT1); +} + +static unsigned int startup_m32700ut_pld_irq(unsigned int irq) +{ + enable_m32700ut_pld_irq(irq); + return 0; +} + +static void shutdown_m32700ut_pld_irq(unsigned int irq) +{ + unsigned long port; + unsigned int pldirq; + + pldirq = irq2pldirq(irq); + port = pldirq2port(pldirq); + outw(PLD_ICUCR_ILEVEL7, port); +} + +static struct hw_interrupt_type m32700ut_pld_irq_type = +{ + "USRV-PLD-IRQ", + startup_m32700ut_pld_irq, + shutdown_m32700ut_pld_irq, + enable_m32700ut_pld_irq, + disable_m32700ut_pld_irq, + mask_and_ack_m32700ut_pld, + end_m32700ut_pld_irq +}; + +void __init init_IRQ(void) +{ + static int once = 0; + int i; + + if (once) + return; + else + once++; + + /* MFT2 : system timer */ + irq_desc[M32R_IRQ_MFT2].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_MFT2].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_MFT2].action = 0; + irq_desc[M32R_IRQ_MFT2].depth = 1; + icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; + disable_mappi_irq(M32R_IRQ_MFT2); + +#if defined(CONFIG_SERIAL_M32R_SIO) + /* SIO0_R : uart receive data */ + irq_desc[M32R_IRQ_SIO0_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_R].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_SIO0_R].action = 0; + irq_desc[M32R_IRQ_SIO0_R].depth = 1; + icu_data[M32R_IRQ_SIO0_R].icucr = 0; + disable_mappi_irq(M32R_IRQ_SIO0_R); + + /* SIO0_S : uart send data */ + irq_desc[M32R_IRQ_SIO0_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO0_S].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_SIO0_S].action = 0; + irq_desc[M32R_IRQ_SIO0_S].depth = 1; + icu_data[M32R_IRQ_SIO0_S].icucr = 0; + disable_mappi_irq(M32R_IRQ_SIO0_S); + + /* SIO1_R : uart receive data */ + irq_desc[M32R_IRQ_SIO1_R].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_R].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_SIO1_R].action = 0; + irq_desc[M32R_IRQ_SIO1_R].depth = 1; + icu_data[M32R_IRQ_SIO1_R].icucr = 0; + disable_mappi_irq(M32R_IRQ_SIO1_R); + + /* SIO1_S : uart send data */ + irq_desc[M32R_IRQ_SIO1_S].status = IRQ_DISABLED; + irq_desc[M32R_IRQ_SIO1_S].handler = &mappi_irq_type; + irq_desc[M32R_IRQ_SIO1_S].action = 0; + irq_desc[M32R_IRQ_SIO1_S].depth = 1; + icu_data[M32R_IRQ_SIO1_S].icucr = 0; + disable_mappi_irq(M32R_IRQ_SIO1_S); +#endif /* CONFIG_SERIAL_M32R_SIO */ + + /* INT#67-#71: CFC#0 IREQ on PLD */ + for (i = 0 ; i < CONFIG_CFC_NUM ; i++ ) { + irq_desc[PLD_IRQ_CF0 + i].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_CF0 + i].handler = &m32700ut_pld_irq_type; + irq_desc[PLD_IRQ_CF0 + i].action = 0; + irq_desc[PLD_IRQ_CF0 + i].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_CF0 + i)].icucr + = PLD_ICUCR_ISMOD01; /* 'L' level sense */ + disable_m32700ut_pld_irq(PLD_IRQ_CF0 + i); + } + +#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE) + /* INT#76: 16552D#0 IREQ on PLD */ + irq_desc[PLD_IRQ_UART0].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_UART0].handler = &m32700ut_pld_irq_type; + irq_desc[PLD_IRQ_UART0].action = 0; + irq_desc[PLD_IRQ_UART0].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_UART0)].icucr + = PLD_ICUCR_ISMOD03; /* 'H' level sense */ + disable_m32700ut_pld_irq(PLD_IRQ_UART0); + + /* INT#77: 16552D#1 IREQ on PLD */ + irq_desc[PLD_IRQ_UART1].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_UART1].handler = &m32700ut_pld_irq_type; + irq_desc[PLD_IRQ_UART1].action = 0; + irq_desc[PLD_IRQ_UART1].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_UART1)].icucr + = PLD_ICUCR_ISMOD03; /* 'H' level sense */ + disable_m32700ut_pld_irq(PLD_IRQ_UART1); +#endif /* CONFIG_SERIAL_8250 || CONFIG_SERIAL_8250_MODULE */ + +#if defined(CONFIG_IDC_AK4524) || defined(CONFIG_IDC_AK4524_MODULE) + /* INT#80: AK4524 IREQ on PLD */ + irq_desc[PLD_IRQ_SNDINT].status = IRQ_DISABLED; + irq_desc[PLD_IRQ_SNDINT].handler = &m32700ut_pld_irq_type; + irq_desc[PLD_IRQ_SNDINT].action = 0; + irq_desc[PLD_IRQ_SNDINT].depth = 1; /* disable nested irq */ + pld_icu_data[irq2pldirq(PLD_IRQ_SNDINT)].icucr + = PLD_ICUCR_ISMOD01; /* 'L' level sense */ + disable_m32700ut_pld_irq(PLD_IRQ_SNDINT); +#endif /* CONFIG_IDC_AK4524 || CONFIG_IDC_AK4524_MODULE */ + + /* + * INT1# is used for UART, MMC, CF Controller in FPGA. + * We enable it here. + */ + icu_data[M32R_IRQ_INT1].icucr = M32R_ICUCR_ISMOD11; + enable_mappi_irq(M32R_IRQ_INT1); +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/signal.c 2004-09-03 00:57:17.349401752 -0700 @@ -0,0 +1,620 @@ +/* + * linux/arch/m32r/kernel/signal.c + * orig : i386 2.5.67 + * + * Copyright (c) 2003 Hitoshi Yamamoto + * + * Taken from i386 version. + * Copyright (C) 1991, 1992 Linus Torvalds + * + * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson + * 2000-06-20 Pentium III FXSR, SSE support by Gareth Hughes + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG_SIG 0 + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +int do_signal(struct pt_regs *, sigset_t *); + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ +asmlinkage int sys_sigsuspend(old_sigset_t mask, unsigned long r1, + unsigned long r2, unsigned long r3, unsigned long r4, + unsigned long r5, unsigned long r6, struct pt_regs regs) +{ + sigset_t saveset; + + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sighand->siglock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + regs.r0 = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(®s, &saveset)) + return regs.r0; + } +} + +asmlinkage int sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, + unsigned long r2, unsigned long r3, unsigned long r4, + unsigned long r5, unsigned long r6, struct pt_regs regs) +{ + sigset_t saveset, newset; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sighand->siglock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + regs.r0 = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(®s, &saveset)) + return regs.r0; + } +} + +asmlinkage int sys_sigaction(int sig, const struct old_sigaction __user *act, + struct old_sigaction __user *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) { + old_sigset_t mask; + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + return ret; +} + +asmlinkage int sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, + unsigned long r2, unsigned long r3, unsigned long r4, + unsigned long r5, unsigned long r6, struct pt_regs regs) +{ + return do_sigaltstack(uss, uoss, regs.spu); +} + + +/* + * Do a signal return; undo the signal stack. + */ + +struct sigframe +{ +// char *pretcode; + int sig; + struct sigcontext sc; +// struct _fpstate fpstate; + unsigned long extramask[_NSIG_WORDS-1]; + char retcode[4]; +}; + +struct rt_sigframe +{ +// char *pretcode; + int sig; + struct siginfo *pinfo; + void *puc; + struct siginfo info; + struct ucontext uc; +// struct _fpstate fpstate; + char retcode[8]; +}; + +static int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, + int *r0_p) +{ + unsigned int err = 0; + + /* Always make any pending restarted system calls return -EINTR */ + current_thread_info()->restart_block.fn = do_no_restart_syscall; + +#define COPY(x) err |= __get_user(regs->x, &sc->sc_##x) + COPY(r4); + COPY(r5); + COPY(r6); + COPY(pt_regs); + /* COPY(r0); Skip r0 */ + COPY(r1); + COPY(r2); + COPY(r3); + COPY(r7); + COPY(r8); + COPY(r9); + COPY(r10); + COPY(r11); + COPY(r12); +#if defined(CONFIG_ISA_M32R2) && defined(CONFIG_ISA_DSP_LEVEL2) + COPY(acc0h); + COPY(acc0l); + COPY(acc1h); + COPY(acc1l); +#elif defined(CONFIG_ISA_M32R2) || defined(CONFIG_ISA_M32R) + COPY(acch); + COPY(accl); +#else +#error unknown isa configuration +#endif + COPY(psw); + COPY(bpc); + COPY(bbpsw); + COPY(bbpc); + COPY(spu); + COPY(fp); + COPY(lr); + COPY(spi); +#undef COPY + + regs->syscall_nr = -1; /* disable syscall checks */ + err |= __get_user(*r0_p, &sc->sc_r0); + + return err; +} + +asmlinkage int sys_sigreturn(unsigned long r0, unsigned long r1, + unsigned long r2, unsigned long r3, unsigned long r4, + unsigned long r5, unsigned long r6, struct pt_regs regs) +{ + struct sigframe __user *frame = (struct sigframe __user *)regs.spu; + sigset_t set; + int result; + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__get_user(set.sig[0], &frame->sc.oldmask) + || (_NSIG_WORDS > 1 + && __copy_from_user(&set.sig[1], &frame->extramask, + sizeof(frame->extramask)))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext(®s, &frame->sc, &result)) + goto badframe; + return result; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +asmlinkage int sys_rt_sigreturn(unsigned long r0, unsigned long r1, + unsigned long r2, unsigned long r3, unsigned long r4, + unsigned long r5, unsigned long r6, struct pt_regs regs) +{ + struct rt_sigframe __user *frame = (struct rt_sigframe __user *)regs.spu; + sigset_t set; + stack_t st; + int result; + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext(®s, &frame->uc.uc_mcontext, &result)) + goto badframe; + + if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st))) + goto badframe; + /* It is more difficult to avoid calling this function than to + call it and ignore errors. */ + do_sigaltstack(&st, NULL, regs.spu); + + return result; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +/* + * Set up a signal frame. + */ + +static int setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, + unsigned long mask) +{ + int err = 0; + +#define COPY(x) err |= __put_user(regs->x, &sc->sc_##x) + COPY(r4); + COPY(r5); + COPY(r6); + COPY(pt_regs); + COPY(r0); + COPY(r1); + COPY(r2); + COPY(r3); + COPY(r7); + COPY(r8); + COPY(r9); + COPY(r10); + COPY(r11); + COPY(r12); +#if defined(CONFIG_ISA_M32R2) && defined(CONFIG_ISA_DSP_LEVEL2) + COPY(acc0h); + COPY(acc0l); + COPY(acc1h); + COPY(acc1l); +#elif defined(CONFIG_ISA_M32R2) || defined(CONFIG_ISA_M32R) + COPY(acch); + COPY(accl); +#else +#error unknown isa configuration +#endif + COPY(psw); + COPY(bpc); + COPY(bbpsw); + COPY(bbpc); + COPY(spu); + COPY(fp); + COPY(lr); + COPY(spi); +#undef COPY + + err |= __put_user(mask, &sc->oldmask); + + return err; +} + +/* + * Determine which stack to use.. + */ +static __inline__ void __user *get_sigframe(struct k_sigaction *ka, unsigned long sp, + size_t frame_size) +{ + /* This is the X/Open sanctioned signal stack switching. */ + if (ka->sa.sa_flags & SA_ONSTACK) { + if (sas_ss_flags(sp) == 0) + sp = current->sas_ss_sp + current->sas_ss_size; + } + + return (void __user *)((sp - frame_size) & -8ul); +} + +static void setup_frame(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs *regs) +{ + struct sigframe __user *frame; + int err = 0; + int signal; + + frame = get_sigframe(ka, regs->spu, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + signal = current_thread_info()->exec_domain + && current_thread_info()->exec_domain->signal_invmap + && sig < 32 + ? current_thread_info()->exec_domain->signal_invmap[sig] + : sig; + + err |= __put_user(signal, &frame->sig); + if (err) + goto give_sigsegv; + + err |= setup_sigcontext(&frame->sc, regs, set->sig[0]); + if (err) + goto give_sigsegv; + + if (_NSIG_WORDS > 1) { + err |= __copy_to_user(frame->extramask, &set->sig[1], + sizeof(frame->extramask)); + if (err) + goto give_sigsegv; + } + + if (ka->sa.sa_flags & SA_RESTORER) + regs->lr = (unsigned long)ka->sa.sa_restorer; + else { + /* This is : ldi r7, #__NR_sigreturn ; trap #2 */ + unsigned long code = 0x670010f2 | (__NR_sigreturn << 16); + + regs->lr = (unsigned long)frame->retcode; + err |= __put_user(code, (long __user *)(frame->retcode+0)); + if (err) + goto give_sigsegv; + flush_cache_sigtramp((unsigned long)frame->retcode); + } + + /* Set up registers for signal handler */ + regs->spu = (unsigned long)frame; + regs->r0 = signal; /* Arg for signal handler */ + regs->r1 = (unsigned long)&frame->sc; + regs->bpc = (unsigned long)ka->sa.sa_handler; + + set_fs(USER_DS); + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=%p pc=%p\n", + current->comm, current->pid, frame, regs->pc); +#endif + + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); +} + +static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs *regs) +{ + struct rt_sigframe __user *frame; + int err = 0; + int signal; + + frame = get_sigframe(ka, regs->spu, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + signal = current_thread_info()->exec_domain + && current_thread_info()->exec_domain->signal_invmap + && sig < 32 + ? current_thread_info()->exec_domain->signal_invmap[sig] + : sig; + + err |= __put_user(signal, &frame->sig); + if (err) + goto give_sigsegv; + + err |= __put_user(&frame->info, &frame->pinfo); + err |= __put_user(&frame->uc, &frame->puc); + err |= copy_siginfo_to_user(&frame->info, info); + if (err) + goto give_sigsegv; + + /* Create the ucontext. */ + err |= __put_user(0, &frame->uc.uc_flags); + err |= __put_user(0, &frame->uc.uc_link); + err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); + err |= __put_user(sas_ss_flags(regs->spu), + &frame->uc.uc_stack.ss_flags); + err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); + err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0]); + err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + if (err) + goto give_sigsegv; + + /* Set up to return from userspace. */ + if (ka->sa.sa_flags & SA_RESTORER) + regs->lr = (unsigned long)ka->sa.sa_restorer; + else { + /* This is : ldi r7, #__NR_rt_sigreturn ; trap #2 */ + unsigned long code1 = 0x97f00000 | (__NR_rt_sigreturn); + unsigned long code2 = 0x10f2f000; + + regs->lr = (unsigned long)frame->retcode; + err |= __put_user(code1, (long __user *)(frame->retcode+0)); + err |= __put_user(code2, (long __user *)(frame->retcode+4)); + if (err) + goto give_sigsegv; + flush_cache_sigtramp((unsigned long)frame->retcode); + } + + /* Set up registers for signal handler */ + regs->spu = (unsigned long)frame; + regs->r0 = signal; /* Arg for signal handler */ + regs->r1 = (unsigned long)&frame->info; + regs->r2 = (unsigned long)&frame->uc; + regs->bpc = (unsigned long)ka->sa.sa_handler; + + set_fs(USER_DS); + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=%p pc=%p\n", + current->comm, current->pid, frame, regs->pc); +#endif + + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); +} + +/* + * OK, we're invoking a handler + */ + +static void handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset, + struct pt_regs *regs) +{ + struct k_sigaction *ka = ¤t->sighand->action[sig-1]; + unsigned short inst; + + /* Are we from a system call? */ + if (regs->syscall_nr >= 0) { + /* If so, check system call restarting.. */ + switch (regs->r0) { + case -ERESTART_RESTARTBLOCK: + case -ERESTARTNOHAND: + regs->r0 = -EINTR; + break; + + case -ERESTARTSYS: + if (!(ka->sa.sa_flags & SA_RESTART)) { + regs->r0 = -EINTR; + break; + } + /* fallthrough */ + case -ERESTARTNOINTR: + regs->r0 = regs->orig_r0; + inst = *(unsigned short *)(regs->bpc - 2); + if ((inst & 0xfff0) == 0x10f0) /* trap ? */ + regs->bpc -= 2; + else + regs->bpc -= 4; + } + } + + /* Set up the stack frame */ + if (ka->sa.sa_flags & SA_SIGINFO) + setup_rt_frame(sig, ka, info, oldset, regs); + else + setup_frame(sig, ka, oldset, regs); + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + */ +int do_signal(struct pt_regs *regs, sigset_t *oldset) +{ + siginfo_t info; + int signr; + unsigned short inst; + + /* + * We want the common case to go fast, which + * is why we may in certain cases get here from + * kernel mode. Just return without doing anything + * if so. + */ + if (!user_mode(regs)) + return 1; + + if (current->flags & PF_FREEZE) { + refrigerator(0); + goto no_signal; + } + + if (!oldset) + oldset = ¤t->blocked; + + signr = get_signal_to_deliver(&info, regs, NULL); + if (signr > 0) { + /* Reenable any watchpoints before delivering the + * signal to user space. The processor register will + * have been cleared if the watchpoint triggered + * inside the kernel. + */ + + /* Whee! Actually deliver the signal. */ + handle_signal(signr, &info, oldset, regs); + return 1; + } + + no_signal: + /* Did we come from a system call? */ + if (regs->syscall_nr >= 0) { + /* Restart the system call - no handlers present */ + if (regs->r0 == -ERESTARTNOHAND || + regs->r0 == -ERESTARTSYS || + regs->r0 == -ERESTARTNOINTR) { + regs->r0 = regs->orig_r0; + inst = *(unsigned short *)(regs->bpc - 2); + if ((inst & 0xfff0) == 0x10f0) /* trap ? */ + regs->bpc -= 2; + else + regs->bpc -= 4; + } + if (regs->r0 == -ERESTART_RESTARTBLOCK){ + regs->r0 = regs->orig_r0; + regs->r7 = __NR_restart_syscall; + inst = *(unsigned short *)(regs->bpc - 2); + if ((inst & 0xfff0) == 0x10f0) /* trap ? */ + regs->bpc -= 2; + else + regs->bpc -= 4; + } + } + return 0; +} + +/* + * notification of userspace execution resumption + * - triggered by current->work.notify_resume + */ +void do_notify_resume(struct pt_regs *regs, sigset_t *oldset, + __u32 thread_info_flags) +{ + /* Pending single-step? */ + if (thread_info_flags & _TIF_SINGLESTEP) + clear_thread_flag(TIF_SINGLESTEP); + + /* deal with pending signal delivery */ + if (thread_info_flags & _TIF_SIGPENDING) + do_signal(regs,oldset); + + clear_thread_flag(TIF_IRET); +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/smpboot.c 2004-09-03 00:57:18.722193056 -0700 @@ -0,0 +1,659 @@ +/* + * linux/arch/m32r/kernel/smpboot.c + * orig : i386 2.4.10 + * + * MITSUBISHI M32R SMP booting functions + * + * Copyright (c) 2001, 2002, 2003 Hitoshi Yamamoto + * + * Taken from i386 version. + * (c) 1995 Alan Cox, Building #3 + * (c) 1998, 1999, 2000 Ingo Molnar + * + * Much of the core SMP work is based on previous work by Thomas Radke, to + * whom a great many thanks are extended. + * + * Thanks to Intel for making available several different Pentium, + * Pentium Pro and Pentium-II/Xeon MP machines. + * Original development of Linux SMP code supported by Caldera. + * + * This code is released under the GNU General Public License version 2 or + * later. + * + * Fixes + * Felix Koop : NR_CPUS used properly + * Jose Renau : Handle single CPU case. + * Alan Cox : By repeated request + * 8) - Total BogoMIP report. + * Greg Wright : Fix for kernel stacks panic. + * Erich Boleyn : MP v1.4 and additional changes. + * Matthias Sattler : Changes for 2.1 kernel map. + * Michel Lespinasse : Changes for 2.1 kernel map. + * Michael Chastain : Change trampoline.S to gnu as. + * Alan Cox : Dumb bug: 'B' step PPro's are fine + * Ingo Molnar : Added APIC timers, based on code + * from Jose Renau + * Ingo Molnar : various cleanups and rewrites + * Tigran Aivazian : fixed "0.00 in /proc/uptime on SMP" bug. + * Maciej W. Rozycki : Bits for genuine 82489DX APICs + * Martin J. Bligh : Added support for multi-quad systems + */ + +/* $Id$ */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define DEBUG_SMP +#ifdef DEBUG_SMP +#define Dprintk(x...) printk(x) +#else +#define Dprintk(x...) +#endif + +extern int cpu_idle(void); +extern cpumask_t cpu_initialized; + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Data structures and variables */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ + +/* Processor that is doing the boot up */ +static unsigned int bsp_phys_id = -1; + +/* Bitmask of physically existing CPUs */ +physid_mask_t phys_cpu_present_map; + +/* Bitmask of currently online CPUs */ +cpumask_t cpu_online_map; + +cpumask_t cpu_bootout_map; +cpumask_t cpu_bootin_map; +cpumask_t cpu_callout_map; +static cpumask_t cpu_callin_map; + +/* Per CPU bogomips and other parameters */ +struct cpuinfo_m32r cpu_data[NR_CPUS] __cacheline_aligned; + +/* Set when the idlers are all forked */ +int smp_threads_ready; + +static int cpucount; +static cpumask_t smp_commenced_mask; + +extern struct { + void * spi; + unsigned short ss; +} stack_start; + +/* which physical physical ID maps to which logical CPU number */ +static volatile int physid_2_cpu[NR_CPUS]; + +/* which logical CPU number maps to which physical ID */ +volatile int cpu_2_physid[NR_CPUS]; + +DEFINE_PER_CPU(int, prof_multiplier) = 1; +DEFINE_PER_CPU(int, prof_old_multiplier) = 1; +DEFINE_PER_CPU(int, prof_counter) = 1; + +spinlock_t ipi_lock[NR_IPIS]; + +static unsigned int calibration_result; + +unsigned long cache_decay_ticks = HZ / 100; + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Function Prototypes */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ + +void smp_prepare_boot_cpu(void); +void smp_prepare_cpus(unsigned int); +static struct task_struct *fork_by_hand(void); +static void smp_tune_scheduling(void); +static void init_ipi_lock(void); +static void do_boot_cpu(int); +int __cpu_up(unsigned int); +void smp_cpus_done(unsigned int); + +int start_secondary(void *); +static void smp_callin(void); +static void smp_online(void); + +static void show_mp_info(int); +static void smp_store_cpu_info(int); +static void show_cpu_info(int); +int setup_profiling_timer(unsigned int); +static void init_cpu_to_physid(void); +static void map_cpu_to_physid(int, int); +static void unmap_cpu_to_physid(int, int); + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Boot up APs Routins : BSP */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +void __devinit smp_prepare_boot_cpu(void) +{ + bsp_phys_id = hard_smp_processor_id(); + physid_set(bsp_phys_id, phys_cpu_present_map); + cpu_set(0, cpu_online_map); /* BSP's cpu_id == 0 */ + cpu_set(0, cpu_callout_map); + cpu_set(0, cpu_callin_map); + + /* + * Initialize the logical to physical CPU number mapping + */ + init_cpu_to_physid(); + map_cpu_to_physid(0, bsp_phys_id); + current_thread_info()->cpu = 0; +} + +/*==========================================================================* + * Name: smp_prepare_cpus (old smp_boot_cpus) + * + * Description: This routine boot up APs. + * + * Born on Date: 2002.02.05 + * + * Arguments: NONE + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * 2003-06-24 hy modify for linux-2.5.69 + * + *==========================================================================*/ +void __init smp_prepare_cpus(unsigned int max_cpus) +{ + int phys_id; + unsigned long nr_cpu; + + nr_cpu = inl(M32R_FPGA_NUM_OF_CPUS_PORTL); + if (nr_cpu > NR_CPUS) { + printk(KERN_INFO "NUM_OF_CPUS reg. value [%ld] > NR_CPU [%d]", + nr_cpu, NR_CPUS); + goto smp_done; + } + for (phys_id = 0 ; phys_id < nr_cpu ; phys_id++) + physid_set(phys_id, phys_cpu_present_map); + + show_mp_info(nr_cpu); + + init_ipi_lock(); + + /* + * Setup boot CPU information + */ + smp_store_cpu_info(0); /* Final full version of the data */ + smp_tune_scheduling(); + + /* + * If SMP should be disabled, then really disable it! + */ + if (!max_cpus) { + printk(KERN_INFO "SMP mode deactivated by commandline.\n"); + goto smp_done; + } + + /* + * Now scan the CPU present map and fire up the other CPUs. + */ + Dprintk("CPU present map : %lx\n", physids_coerce(phys_cpu_present_map)); + + for (phys_id = 0 ; phys_id < NR_CPUS ; phys_id++) { + /* + * Don't even attempt to start the boot CPU! + */ + if (phys_id == bsp_phys_id) + continue; + + if (!physid_isset(phys_id, phys_cpu_present_map)) + continue; + + if ((max_cpus >= 0) && (max_cpus <= cpucount + 1)) + continue; + + do_boot_cpu(phys_id); + + /* + * Make sure we unmap all failed CPUs + */ + if (physid_to_cpu(phys_id) == -1) { + physid_clear(phys_id, phys_cpu_present_map); + printk("phys CPU#%d not responding - " \ + "cannot use it.\n", phys_id); + } + } + +smp_done: + Dprintk("Boot done.\n"); +} + +/* + * fork_by_hand : This routin executes do_fork for APs idle process. + */ +static struct task_struct * __init fork_by_hand(void) +{ + struct pt_regs regs = { 0 }; + + /* + * don't care about the eip and regs settings since + * we'll never reschedule the forked task. + */ + return copy_process(CLONE_VM | CLONE_IDLETASK, 0, ®s, 0, NULL, NULL); +} + +static void __init smp_tune_scheduling(void) +{ + /* Nothing to do. */ +} + +/* + * init_ipi_lock : Initialize IPI locks. + */ +static void __init init_ipi_lock(void) +{ + int ipi; + + for (ipi = 0 ; ipi < NR_IPIS ; ipi++) + ipi_lock[ipi] = SPIN_LOCK_UNLOCKED; +} + +/*==========================================================================* + * Name: do_boot_cpu + * + * Description: This routine boot up one AP. + * + * Born on Date: 2002.02.05 + * + * Arguments: phys_id - Target CPU physical ID + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * 2003-06-24 hy modify for linux-2.5.69 + * + *==========================================================================*/ +static void __init do_boot_cpu(int phys_id) +{ + struct task_struct *idle; + unsigned long send_status, boot_status; + int timeout, cpu_id; + + cpu_id = ++cpucount; + + /* + * We can't use kernel_thread since we must avoid to + * reschedule the child. + */ + idle = fork_by_hand(); + if (IS_ERR(idle)) + panic("failed fork for CPU#%d.", cpu_id); + wake_up_forked_process(idle); + + /* + * We remove it from the pidhash and the runqueue + * once we got the process: + */ + init_idle(idle, cpu_id); + idle->thread.lr = (unsigned long)start_secondary; + unhash_process(idle); + + map_cpu_to_physid(cpu_id, phys_id); + + /* So we see what's up */ + printk("Booting processor %d/%d\n", phys_id, cpu_id); + stack_start.spi = (void *)idle->thread.sp; + idle->thread_info->cpu = cpu_id; + + /* + * Send Startup IPI + * 1.IPI received by CPU#(phys_id). + * 2.CPU#(phys_id) enter startup_AP (arch/m32r/kernel/head.S) + * 3.CPU#(phys_id) enter start_secondary() + */ + send_status = 0; + boot_status = 0; + + cpu_set(phys_id, cpu_bootout_map); + + /* Send Startup IPI */ + send_IPI_mask_phys(cpumask_of_cpu(phys_id), CPU_BOOT_IPI, 0); + + Dprintk("Waiting for send to finish...\n"); + timeout = 0; + + /* Wait 100[ms] */ + do { + Dprintk("+"); + udelay(1000); + send_status = !cpu_isset(phys_id, cpu_bootin_map); + } while (send_status && (timeout++ < 100)); + + Dprintk("After Startup.\n"); + + if (!send_status) { + /* + * allow APs to start initializing. + */ + Dprintk("Before Callout %d.\n", cpu_id); + cpu_set(cpu_id, cpu_callout_map); + Dprintk("After Callout %d.\n", cpu_id); + + /* + * Wait 5s total for a response + */ + for (timeout = 0; timeout < 5000; timeout++) { + if (cpu_isset(cpu_id, cpu_callin_map)) + break; /* It has booted */ + udelay(1000); + } + + if (cpu_isset(cpu_id, cpu_callin_map)) { + /* number CPUs logically, starting from 1 (BSP is 0) */ + Dprintk("OK.\n"); + } else { + boot_status = 1; + printk("Not responding.\n"); + } + } else + printk("IPI never delivered???\n"); + + if (send_status || boot_status) { + unmap_cpu_to_physid(cpu_id, phys_id); + cpu_clear(cpu_id, cpu_callout_map); + cpu_clear(cpu_id, cpu_callin_map); + cpu_clear(cpu_id, cpu_initialized); + cpucount--; + } +} + +int __devinit __cpu_up(unsigned int cpu_id) +{ + int timeout; + + cpu_set(cpu_id, smp_commenced_mask); + + /* + * Wait 5s total for a response + */ + for (timeout = 0; timeout < 5000; timeout++) { + if (cpu_isset(cpu_id, cpu_online_map)) + break; + udelay(1000); + } + if (!cpu_isset(cpu_id, cpu_online_map)) + BUG(); + + return 0; +} + +void __init smp_cpus_done(unsigned int max_cpus) +{ + int cpu_id, timeout; + unsigned long bogosum = 0; + + for (timeout = 0; timeout < 5000; timeout++) { + if (cpus_equal(cpu_callin_map, cpu_online_map)) + break; + udelay(1000); + } + if (!cpus_equal(cpu_callin_map, cpu_online_map)) + BUG(); + + for (cpu_id = 0 ; cpu_id < num_online_cpus() ; cpu_id++) + show_cpu_info(cpu_id); + + /* + * Allow the user to impress friends. + */ + Dprintk("Before bogomips.\n"); + if (cpucount) { + for_each_cpu_mask(cpu_id, cpu_online_map) + bogosum += cpu_data[cpu_id].loops_per_jiffy; + + printk(KERN_INFO "Total of %d processors activated " \ + "(%lu.%02lu BogoMIPS).\n", cpucount + 1, + bogosum / (500000 / HZ), + (bogosum / (5000 / HZ)) % 100); + Dprintk("Before bogocount - setting activated=1.\n"); + } +} + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Activate a secondary processor Routins */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ + +/*==========================================================================* + * Name: start_secondary + * + * Description: This routine activate a secondary processor. + * + * Born on Date: 2002.02.05 + * + * Arguments: *unused - currently unused. + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * 2003-06-24 hy modify for linux-2.5.69 + * + *==========================================================================*/ +int __init start_secondary(void *unused) +{ + cpu_init(); + smp_callin(); + while (!cpu_isset(smp_processor_id(), smp_commenced_mask)) + rep_nop(); + + smp_online(); + + /* + * low-memory mappings have been cleared, flush them from + * the local TLBs too. + */ + local_flush_tlb_all(); + + return cpu_idle(); +} + +/*==========================================================================* + * Name: smp_callin + * + * Description: This routine activate a secondary processor. + * + * Born on Date: 2002.02.05 + * + * Arguments: NONE + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * 2003-06-24 hy modify for linux-2.5.69 + * + *==========================================================================*/ +static void __init smp_callin(void) +{ + int phys_id = hard_smp_processor_id(); + int cpu_id = smp_processor_id(); + unsigned long timeout; + + if (cpu_isset(cpu_id, cpu_callin_map)) { + printk("huh, phys CPU#%d, CPU#%d already present??\n", + phys_id, cpu_id); + BUG(); + } + Dprintk("CPU#%d (phys ID: %d) waiting for CALLOUT\n", cpu_id, phys_id); + + /* Waiting 2s total for startup (udelay is not yet working) */ + timeout = jiffies + (2 * HZ); + while (time_before(jiffies, timeout)) { + /* Has the boot CPU finished it's STARTUP sequence ? */ + if (cpu_isset(cpu_id, cpu_callout_map)) + break; + rep_nop(); + } + + if (!time_before(jiffies, timeout)) { + printk("BUG: CPU#%d started up but did not get a callout!\n", + cpu_id); + BUG(); + } + + /* Allow the master to continue. */ + cpu_set(cpu_id, cpu_callin_map); +} + +static void __init smp_online(void) +{ + int cpu_id = smp_processor_id(); + + local_irq_enable(); + + /* Get our bogomips. */ + calibrate_delay(); + + /* Save our processor parameters */ + smp_store_cpu_info(cpu_id); + + cpu_set(cpu_id, cpu_online_map); +} + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Boot up CPUs common Routins */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +static void __init show_mp_info(int nr_cpu) +{ + int i; + char cpu_model0[17], cpu_model1[17], cpu_ver[9]; + + strncpy(cpu_model0, (char *)M32R_FPGA_CPU_NAME_ADDR, 16); + strncpy(cpu_model1, (char *)M32R_FPGA_MODEL_ID_ADDR, 16); + strncpy(cpu_ver, (char *)M32R_FPGA_VERSION_ADDR, 8); + + cpu_model0[16] = '\0'; + for (i = 15 ; i >= 0 ; i--) { + if (cpu_model0[i] != ' ') + break; + cpu_model0[i] = '\0'; + } + cpu_model1[16] = '\0'; + for (i = 15 ; i >= 0 ; i--) { + if (cpu_model1[i] != ' ') + break; + cpu_model1[i] = '\0'; + } + cpu_ver[8] = '\0'; + for (i = 7 ; i >= 0 ; i--) { + if (cpu_ver[i] != ' ') + break; + cpu_ver[i] = '\0'; + } + + printk(KERN_INFO "M32R-mp information\n"); + printk(KERN_INFO " On-chip CPUs : %d\n", nr_cpu); + printk(KERN_INFO " CPU model : %s/%s(%s)\n", cpu_model0, + cpu_model1, cpu_ver); +} + +/* + * The bootstrap kernel entry code has set these up. Save them for + * a given CPU + */ +static void __init smp_store_cpu_info(int cpu_id) +{ + struct cpuinfo_m32r *ci = cpu_data + cpu_id; + + *ci = boot_cpu_data; + ci->loops_per_jiffy = loops_per_jiffy; +} + +static void __init show_cpu_info(int cpu_id) +{ + struct cpuinfo_m32r *ci = &cpu_data[cpu_id]; + + printk("CPU#%d : ", cpu_id); + +#define PRINT_CLOCK(name, value) \ + printk(name " clock %d.%02dMHz", \ + ((value) / 1000000), ((value) % 1000000) / 10000) + + PRINT_CLOCK("CPU", (int)ci->cpu_clock); + PRINT_CLOCK(", Bus", (int)ci->bus_clock); + printk(", loops_per_jiffy[%ld]\n", ci->loops_per_jiffy); +} + +/* + * the frequency of the profiling timer can be changed + * by writing a multiplier value into /proc/profile. + */ +int setup_profiling_timer(unsigned int multiplier) +{ + int i; + + /* + * Sanity check. [at least 500 APIC cycles should be + * between APIC interrupts as a rule of thumb, to avoid + * irqs flooding us] + */ + if ( (!multiplier) || (calibration_result / multiplier < 500)) + return -EINVAL; + + /* + * Set the new multiplier for each CPU. CPUs don't start using the + * new values until the next timer interrupt in which they do process + * accounting. At that time they also adjust their APIC timers + * accordingly. + */ + for (i = 0; i < NR_CPUS; ++i) + per_cpu(prof_multiplier, i) = multiplier; + + return 0; +} + +/* Initialize all maps between cpu number and apicids */ +static void __init init_cpu_to_physid(void) +{ + int i; + + for (i = 0 ; i < NR_CPUS ; i++) { + cpu_2_physid[i] = -1; + physid_2_cpu[i] = -1; + } +} + +/* + * set up a mapping between cpu and apicid. Uses logical apicids for multiquad, + * else physical apic ids + */ +static void __init map_cpu_to_physid(int cpu_id, int phys_id) +{ + physid_2_cpu[phys_id] = cpu_id; + cpu_2_physid[cpu_id] = phys_id; +} + +/* + * undo a mapping between cpu and apicid. Uses logical apicids for multiquad, + * else physical apic ids + */ +static void __init unmap_cpu_to_physid(int cpu_id, int phys_id) +{ + physid_2_cpu[phys_id] = -1; + cpu_2_physid[cpu_id] = -1; +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/smp.c 2004-09-03 00:57:18.724192752 -0700 @@ -0,0 +1,957 @@ +/* + * linux/arch/m32r/kernel/smp.c + * orig : i386 2.4.10 + * + * MITSUBISHI M32R SMP support routines. + * + * Copyright (c) 2001, 2002 Hitoshi Yamamoto + * + * Taken from i386 version. + * (c) 1995 Alan Cox, Building #3 + * (c) 1998-99, 2000 Ingo Molnar + * + * This code is released under the GNU General Public License version 2 or + * later. + */ + +/* $Id$ */ + +#undef DEBUG_SMP + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Data structures and variables */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ + +/* + * 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; + atomic_t started; + atomic_t finished; + int wait; +} __attribute__ ((__aligned__(SMP_CACHE_BYTES))); + +static struct call_data_struct *call_data; + +/* + * For flush_cache_all() + */ +static spinlock_t flushcache_lock = SPIN_LOCK_UNLOCKED; +static volatile unsigned long flushcache_cpumask = 0; + +/* + * For flush_tlb_others() + */ +static volatile cpumask_t flush_cpumask; +static struct mm_struct *flush_mm; +static struct vm_area_struct *flush_vma; +static volatile unsigned long flush_va; +static spinlock_t tlbstate_lock = SPIN_LOCK_UNLOCKED; +#define FLUSH_ALL 0xffffffff + +DECLARE_PER_CPU(int, prof_multiplier); +DECLARE_PER_CPU(int, prof_old_multiplier); +DECLARE_PER_CPU(int, prof_counter); + +extern spinlock_t ipi_lock[]; + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Function Prototypes */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ + +void smp_send_reschedule(int); +void smp_reschedule_interrupt(void); + +void smp_flush_cache_all(void); +void smp_flush_cache_all_interrupt(void); + +void smp_flush_tlb_all(void); +static void flush_tlb_all_ipi(void *); + +void smp_flush_tlb_mm(struct mm_struct *); +void smp_flush_tlb_range(struct vm_area_struct *, unsigned long, \ + unsigned long); +void smp_flush_tlb_page(struct vm_area_struct *, unsigned long); +static void flush_tlb_others(cpumask_t, struct mm_struct *, + struct vm_area_struct *, unsigned long); +void smp_invalidate_interrupt(void); + +void smp_send_stop(void); +static void stop_this_cpu(void *); + +int smp_call_function(void (*) (void *), void *, int, int); +void smp_call_function_interrupt(void); + +void smp_send_timer(void); +void smp_ipi_timer_interrupt(struct pt_regs *); +void smp_local_timer_interrupt(struct pt_regs *); + +void send_IPI_allbutself(int, int); +static void send_IPI_mask(cpumask_t, int, int); +unsigned long send_IPI_mask_phys(cpumask_t, int, int); + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Rescheduling request Routins */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ + +/*==========================================================================* + * Name: smp_send_reschedule + * + * Description: This routine requests other CPU to execute rescheduling. + * 1.Send 'RESCHEDULE_IPI' to other CPU. + * Request other CPU to execute 'smp_reschedule_interrupt()'. + * + * Born on Date: 2002.02.05 + * + * Arguments: cpu_id - Target CPU ID + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_send_reschedule(int cpu_id) +{ + send_IPI_mask(cpumask_of_cpu(cpu_id), RESCHEDULE_IPI, 1); +} + +/*==========================================================================* + * Name: smp_reschedule_interrupt + * + * Description: This routine executes on CPU which received + * 'RESCHEDULE_IPI'. + * Rescheduling is processed at the exit of interrupt + * operation. + * + * Born on Date: 2002.02.05 + * + * Arguments: NONE + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_reschedule_interrupt(void) +{ + /* nothing to do */ +} + +/*==========================================================================* + * Name: smp_flush_cache_all + * + * Description: This routine sends a 'INVALIDATE_CACHE_IPI' to all other + * CPUs in the system. + * + * Born on Date: 2003-05-28 + * + * Arguments: NONE + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_flush_cache_all(void) +{ + cpumask_t cpumask; + unsigned long *mask; + + preempt_disable(); + cpumask = cpu_online_map; + cpu_clear(smp_processor_id(), cpumask); + spin_lock(&flushcache_lock); + mask=cpus_addr(cpumask); + atomic_set_mask(*mask, (atomic_t *)&flushcache_cpumask); + send_IPI_mask(cpumask, INVALIDATE_CACHE_IPI, 0); + _flush_cache_copyback_all(); + while (flushcache_cpumask) + mb(); + spin_unlock(&flushcache_lock); + preempt_enable(); +} + +void smp_flush_cache_all_interrupt(void) +{ + _flush_cache_copyback_all(); + clear_bit(smp_processor_id(), &flushcache_cpumask); +} + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* TLB flush request Routins */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ + +/*==========================================================================* + * Name: smp_flush_tlb_all + * + * Description: This routine flushes all processes TLBs. + * 1.Request other CPU to execute 'flush_tlb_all_ipi()'. + * 2.Execute 'do_flush_tlb_all_local()'. + * + * Born on Date: 2002.02.05 + * + * Arguments: NONE + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_flush_tlb_all(void) +{ + unsigned long flags; + + preempt_disable(); + local_irq_save(flags); + __flush_tlb_all(); + local_irq_restore(flags); + smp_call_function(flush_tlb_all_ipi, 0, 1, 1); + preempt_enable(); +} + +/*==========================================================================* + * Name: flush_tlb_all_ipi + * + * Description: This routine flushes all local TLBs. + * 1.Execute 'do_flush_tlb_all_local()'. + * + * Born on Date: 2002.02.05 + * + * Arguments: *info - not used + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +static void flush_tlb_all_ipi(void *info) +{ + __flush_tlb_all(); +} + +/*==========================================================================* + * Name: smp_flush_tlb_mm + * + * Description: This routine flushes the specified mm context TLB's. + * + * Born on Date: 2002.02.05 + * + * Arguments: *mm - a pointer to the mm struct for flush TLB + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_flush_tlb_mm(struct mm_struct *mm) +{ + int cpu_id = smp_processor_id(); + cpumask_t cpu_mask; + unsigned long *mmc = &mm->context[cpu_id]; + unsigned long flags; + + preempt_disable(); + cpu_mask = mm->cpu_vm_mask; + cpu_clear(cpu_id, cpu_mask); + + if (*mmc != NO_CONTEXT) { + local_irq_save(flags); + *mmc = NO_CONTEXT; + if (mm == current->mm) + activate_context(mm); + else + cpu_clear(cpu_id, mm->cpu_vm_mask); + local_irq_restore(flags); + } + if (!cpus_empty(cpu_mask)) + flush_tlb_others(cpu_mask, mm, NULL, FLUSH_ALL); + + preempt_enable(); +} + +/*==========================================================================* + * Name: smp_flush_tlb_range + * + * Description: This routine flushes a range of pages. + * + * Born on Date: 2002.02.05 + * + * Arguments: *mm - a pointer to the mm struct for flush TLB + * start - not used + * end - not used + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, + unsigned long end) +{ + smp_flush_tlb_mm(vma->vm_mm); +} + +/*==========================================================================* + * Name: smp_flush_tlb_page + * + * Description: This routine flushes one page. + * + * Born on Date: 2002.02.05 + * + * Arguments: *vma - a pointer to the vma struct include va + * va - virtual address for flush TLB + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_flush_tlb_page(struct vm_area_struct *vma, unsigned long va) +{ + struct mm_struct *mm = vma->vm_mm; + int cpu_id = smp_processor_id(); + cpumask_t cpu_mask; + unsigned long *mmc = &mm->context[cpu_id]; + unsigned long flags; + + preempt_disable(); + cpu_mask = mm->cpu_vm_mask; + cpu_clear(cpu_id, cpu_mask); + +#ifdef DEBUG_SMP + if (!mm) + BUG(); +#endif + + if (*mmc != NO_CONTEXT) { + local_irq_save(flags); + va &= PAGE_MASK; + va |= (*mmc & MMU_CONTEXT_ASID_MASK); + __flush_tlb_page(va); + local_irq_restore(flags); + } + if (!cpus_empty(cpu_mask)) + flush_tlb_others(cpu_mask, mm, vma, va); + + preempt_enable(); +} + +/*==========================================================================* + * Name: flush_tlb_others + * + * Description: This routine requests other CPU to execute flush TLB. + * 1.Setup parmeters. + * 2.Send 'INVALIDATE_TLB_IPI' to other CPU. + * Request other CPU to execute 'smp_invalidate_interrupt()'. + * 3.Wait for other CPUs operation finished. + * + * Born on Date: 2002.02.05 + * + * Arguments: cpumask - bitmap of target CPUs + * *mm - a pointer to the mm struct for flush TLB + * *vma - a pointer to the vma struct include va + * va - virtual address for flush TLB + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +static void flush_tlb_others(cpumask_t cpumask, struct mm_struct *mm, + struct vm_area_struct *vma, unsigned long va) +{ + cpumask_t tmp; + unsigned long *mask; +#ifdef DEBUG_SMP + unsigned long flags; + __save_flags(flags); + if (!(flags & 0x0040)) /* Interrupt Disable NONONO */ + BUG(); +#endif /* DEBUG_SMP */ + + /* + * A couple of (to be removed) sanity checks: + * + * - we do not send IPIs to not-yet booted CPUs. + * - current CPU must not be in mask + * - mask must exist :) + */ + BUG_ON(cpus_empty(cpumask)); + + cpus_and(tmp, cpumask, cpu_online_map); + BUG_ON(!cpus_equal(cpumask, tmp)); + BUG_ON(cpu_isset(smp_processor_id(), cpumask)); + BUG_ON(!mm); + + /* + * i'm not happy about this global shared spinlock in the + * MM hot path, but we'll see how contended it is. + * Temporarily this turns IRQs off, so that lockups are + * detected by the NMI watchdog. + */ + spin_lock(&tlbstate_lock); + + flush_mm = mm; + flush_vma = vma; + flush_va = va; + mask=cpus_addr(cpumask); + atomic_set_mask(*mask, (atomic_t *)&flush_cpumask); + + /* + * We have to send the IPI only to + * CPUs affected. + */ + send_IPI_mask(cpumask, INVALIDATE_TLB_IPI, 0); + + while (!cpus_empty(flush_cpumask)) + /* nothing. lockup detection does not belong here */ + mb(); + + flush_mm = NULL; + flush_vma = NULL; + flush_va = 0; + spin_unlock(&tlbstate_lock); +} + +/*==========================================================================* + * Name: smp_invalidate_interrupt + * + * Description: This routine executes on CPU which received + * 'INVALIDATE_TLB_IPI'. + * 1.Flush local TLB. + * 2.Report flush TLB process was finished. + * + * Born on Date: 2002.02.05 + * + * Arguments: NONE + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_invalidate_interrupt(void) +{ + int cpu_id = smp_processor_id(); + unsigned long *mmc = &flush_mm->context[cpu_id]; + + if (!cpu_isset(cpu_id, flush_cpumask)) + return; + + if (flush_va == FLUSH_ALL) { + *mmc = NO_CONTEXT; + if (flush_mm == current->active_mm) + activate_context(flush_mm); + else + cpu_clear(cpu_id, flush_mm->cpu_vm_mask); + } else { + unsigned long va = flush_va; + + if (*mmc != NO_CONTEXT) { + va &= PAGE_MASK; + va |= (*mmc & MMU_CONTEXT_ASID_MASK); + __flush_tlb_page(va); + } + } + cpu_clear(cpu_id, flush_cpumask); +} + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Stop CPU request Routins */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ + +/*==========================================================================* + * Name: smp_send_stop + * + * Description: This routine requests stop all CPUs. + * 1.Request other CPU to execute 'stop_this_cpu()'. + * + * Born on Date: 2002.02.05 + * + * Arguments: NONE + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_send_stop(void) +{ + smp_call_function(stop_this_cpu, NULL, 1, 0); +} + +/*==========================================================================* + * Name: stop_this_cpu + * + * Description: This routine halt CPU. + * + * Born on Date: 2002.02.05 + * + * Arguments: NONE + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +static void stop_this_cpu(void *dummy) +{ + int cpu_id = smp_processor_id(); + + /* + * Remove this CPU: + */ + cpu_clear(cpu_id, cpu_online_map); + + /* + * PSW IE = 1; + * IMASK = 0; + * goto SLEEP + */ + local_irq_disable(); + outl(0, M32R_ICU_IMASK_PORTL); + inl(M32R_ICU_IMASK_PORTL); /* dummy read */ + local_irq_enable(); + + for ( ; ; ); +} + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Call function Routins */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ + +/*==========================================================================* + * Name: smp_call_function + * + * Description: This routine sends a 'CALL_FUNCTION_IPI' to all other CPUs + * in the system. + * + * Born on Date: 2002.02.05 + * + * Arguments: *func - The function to run. This must be fast and + * non-blocking. + * *info - An arbitrary pointer to pass to the function. + * nonatomic - currently unused. + * wait - If true, wait (atomically) until function has + * completed on other CPUs. + * + * Returns: 0 on success, else a negative status code. Does not return + * until remote CPUs are nearly ready to execute <> or + * are or have executed. + * + * Cautions: You must not call this function with disabled interrupts or + * from a hardware interrupt handler, you may call it from a + * bottom half handler. + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +int smp_call_function(void (*func) (void *info), void *info, int nonatomic, + int wait) +{ + struct call_data_struct data; + int cpus = num_online_cpus() - 1; + +#ifdef DEBUG_SMP + unsigned long flags; + __save_flags(flags); + if (!(flags & 0x0040)) /* Interrupt Disable NONONO */ + BUG(); +#endif /* DEBUG_SMP */ + + if (!cpus) + return 0; + + /* Can deadlock when called with interrupts disabled */ + WARN_ON(irqs_disabled()); + + data.func = func; + data.info = info; + atomic_set(&data.started, 0); + data.wait = wait; + if (wait) + atomic_set(&data.finished, 0); + + spin_lock(&call_lock); + call_data = &data; + mb(); + + /* Send a message to all other CPUs and wait for them to respond */ + send_IPI_allbutself(CALL_FUNCTION_IPI, 0); + + /* Wait for response */ + while (atomic_read(&data.started) != cpus) + barrier(); + + if (wait) + while (atomic_read(&data.finished) != cpus) + barrier(); + spin_unlock(&call_lock); + + return 0; +} + +/*==========================================================================* + * Name: smp_call_function_interrupt + * + * Description: This routine executes on CPU which received + * 'CALL_FUNCTION_IPI'. + * + * Born on Date: 2002.02.05 + * + * Arguments: NONE + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_call_function_interrupt(void) +{ + void (*func) (void *info) = call_data->func; + void *info = call_data->info; + int wait = call_data->wait; + + /* + * Notify initiating CPU that I've grabbed the data and am + * about to execute the function + */ + mb(); + atomic_inc(&call_data->started); + /* + * At this point the info structure may be out of scope unless wait==1 + */ + irq_enter(); + (*func)(info); + irq_exit(); + + if (wait) { + mb(); + atomic_inc(&call_data->finished); + } +} + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Timer Routins */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ + +/*==========================================================================* + * Name: smp_send_timer + * + * Description: This routine sends a 'LOCAL_TIMER_IPI' to all other CPUs + * in the system. + * + * Born on Date: 2002.02.05 + * + * Arguments: NONE + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_send_timer(void) +{ + send_IPI_allbutself(LOCAL_TIMER_IPI, 1); +} + +/*==========================================================================* + * Name: smp_send_timer + * + * Description: This routine executes on CPU which received + * 'LOCAL_TIMER_IPI'. + * + * Born on Date: 2002.02.05 + * + * Arguments: *regs - a pointer to the saved regster info + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void smp_ipi_timer_interrupt(struct pt_regs *regs) +{ + irq_enter(); + smp_local_timer_interrupt(regs); + irq_exit(); +} + +/*==========================================================================* + * Name: smp_local_timer_interrupt + * + * Description: Local timer interrupt handler. It does both profiling and + * process statistics/rescheduling. + * We do profiling in every local tick, statistics/rescheduling + * happen only every 'profiling multiplier' ticks. The default + * multiplier is 1 and it can be changed by writing the new + * multiplier value into /proc/profile. + * + * Born on Date: 2002.02.05 + * + * Arguments: *regs - a pointer to the saved regster info + * + * Returns: void (cannot fail) + * + * Original: arch/i386/kernel/apic.c + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * 2003-06-24 hy use per_cpu structure. + *==========================================================================*/ +void smp_local_timer_interrupt(struct pt_regs *regs) +{ + int user = user_mode(regs); + int cpu_id = smp_processor_id(); + + /* + * The profiling function is SMP safe. (nothing can mess + * around with "current", and the profiling counters are + * updated with atomic operations). This is especially + * useful with a profiling multiplier != 1 + */ + + m32r_do_profile(regs); + + if (--per_cpu(prof_counter, cpu_id) <= 0) { + /* + * The multiplier may have changed since the last time we got + * to this point as a result of the user writing to + * /proc/profile. In this case we need to adjust the APIC + * timer accordingly. + * + * Interrupts are already masked off at this point. + */ + per_cpu(prof_counter, cpu_id) + = per_cpu(prof_multiplier, cpu_id); + if (per_cpu(prof_counter, cpu_id) + != per_cpu(prof_old_multiplier, cpu_id)) + { + per_cpu(prof_old_multiplier, cpu_id) + = per_cpu(prof_counter, cpu_id); + } + + update_process_times(user); + } +} + +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ +/* Send IPI Routins */ +/*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ + +/*==========================================================================* + * Name: send_IPI_allbutself + * + * Description: This routine sends a IPI to all other CPUs in the system. + * + * Born on Date: 2002.02.05 + * + * Arguments: ipi_num - Number of IPI + * try - 0 : Send IPI certainly. + * !0 : The following IPI is not sended when Target CPU + * has not received the before IPI. + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +void send_IPI_allbutself(int ipi_num, int try) +{ + cpumask_t cpumask; + + cpumask = cpu_online_map; + cpu_clear(smp_processor_id(), cpumask); + + send_IPI_mask(cpumask, ipi_num, try); +} + +/*==========================================================================* + * Name: send_IPI_mask + * + * Description: This routine sends a IPI to CPUs in the system. + * + * Born on Date: 2002.02.05 + * + * Arguments: cpu_mask - Bitmap of target CPUs logical ID + * ipi_num - Number of IPI + * try - 0 : Send IPI certainly. + * !0 : The following IPI is not sended when Target CPU + * has not received the before IPI. + * + * Returns: void (cannot fail) + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +static void send_IPI_mask(cpumask_t cpumask, int ipi_num, int try) +{ + cpumask_t physid_mask, tmp; + int cpu_id, phys_id; + int num_cpus = num_online_cpus(); + + if (num_cpus <= 1) /* NO MP */ + return; + + cpus_and(tmp, cpumask, cpu_online_map); + BUG_ON(!cpus_equal(cpumask, tmp)); + + physid_mask = CPU_MASK_NONE; + for_each_cpu_mask(cpu_id, cpumask){ + if ((phys_id = cpu_to_physid(cpu_id)) != -1) + cpu_set(phys_id, physid_mask); + } + + send_IPI_mask_phys(physid_mask, ipi_num, try); +} + +/*==========================================================================* + * Name: send_IPI_mask_phys + * + * Description: This routine sends a IPI to other CPUs in the system. + * + * Born on Date: 2002.02.05 + * + * Arguments: cpu_mask - Bitmap of target CPUs physical ID + * ipi_num - Number of IPI + * try - 0 : Send IPI certainly. + * !0 : The following IPI is not sended when Target CPU + * has not received the before IPI. + * + * Returns: IPICRi regster value. + * + * Modification log: + * Date Who Description + * ---------- --- -------------------------------------------------------- + * + *==========================================================================*/ +unsigned long send_IPI_mask_phys(cpumask_t physid_mask, int ipi_num, + int try) +{ + spinlock_t *ipilock; + unsigned long flags = 0; + volatile unsigned long *ipicr_addr; + unsigned long ipicr_val; + unsigned long my_physid_mask; + unsigned long mask = cpus_addr(physid_mask)[0]; + + + if (mask & ~physids_coerce(phys_cpu_present_map)) + BUG(); + if (ipi_num >= NR_IPIS) + BUG(); + + mask <<= IPI_SHIFT; + ipilock = &ipi_lock[ipi_num]; + ipicr_addr = (volatile unsigned long *)(M32R_ICU_IPICR_ADDR + + (ipi_num << 2)); + my_physid_mask = ~(1 << smp_processor_id()); + + /* + * lock ipi_lock[i] + * check IPICRi == 0 + * write IPICRi (send IPIi) + * unlock ipi_lock[i] + */ + __asm__ __volatile__ ( + ";; LOCK ipi_lock[i] \n\t" + ".fillinsn \n" + "1: \n\t" + "mvfc %1, psw \n\t" + "clrpsw #0x40 -> nop \n\t" + DCACHE_CLEAR("r4", "r5", "%2") + "lock r4, @%2 \n\t" + "addi r4, #-1 \n\t" + "unlock r4, @%2 \n\t" + "mvtc %1, psw \n\t" + "bnez r4, 2f \n\t" + LOCK_SECTION_START(".balign 4 \n\t") + ".fillinsn \n" + "2: \n\t" + "ld r4, @%2 \n\t" + "blez r4, 2b \n\t" + "bra 1b \n\t" + LOCK_SECTION_END + ";; CHECK IPICRi == 0 \n\t" + ".fillinsn \n" + "3: \n\t" + "ld %0, @%3 \n\t" + "and %0, %6 \n\t" + "beqz %0, 4f \n\t" + "bnez %5, 5f \n\t" + "bra 3b \n\t" + ";; WRITE IPICRi (send IPIi) \n\t" + ".fillinsn \n" + "4: \n\t" + "st %4, @%3 \n\t" + ";; UNLOCK ipi_lock[i] \n\t" + ".fillinsn \n" + "5: \n\t" + "ldi r4, #1 \n\t" + "st r4, @%2 \n\t" + : "=&r"(ipicr_val) + : "r"(flags), "r"(&ipilock->lock), "r"(ipicr_addr), + "r"(mask), "r"(try), "r"(my_physid_mask) + : "memory", "r4" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r5" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + + return ipicr_val; +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/sys_m32r.c 2004-09-03 00:57:18.724192752 -0700 @@ -0,0 +1,281 @@ +/* + * linux/arch/m32r/kernel/sys_m32r.c + * + * This file contains various random system calls that + * have a non-standard calling sequence on the Linux/M32R platform. + * + * Taken from i386 version. + */ + +/* $Id$ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * sys_tas() - test-and-set + * linuxthreads testing version + */ +#ifndef CONFIG_SMP +asmlinkage int sys_tas(int *addr) +{ + int oldval; + unsigned long flags; + + if (!access_ok(VERIFY_WRITE, addr, sizeof (int))) + return -EFAULT; + local_irq_save(flags); + oldval = *addr; + *addr = 1; + local_irq_restore(flags); + return oldval; +} +#else /* CONFIG_SMP */ +#include + +static spinlock_t tas_lock = SPIN_LOCK_UNLOCKED; + +asmlinkage int sys_tas(int *addr) +{ + int oldval; + + if (!access_ok(VERIFY_WRITE, addr, sizeof (int))) + return -EFAULT; + + spin_lock(&tas_lock); + oldval = *addr; + *addr = 1; + spin_unlock(&tas_lock); + + return oldval; +} +#endif /* CONFIG_SMP */ + +/* + * sys_pipe() is the normal C calling standard for creating + * a pipe. It's not the way Unix traditionally does this, though. + */ +asmlinkage int +sys_pipe(unsigned long r0, unsigned long r1, unsigned long r2, + unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, struct pt_regs regs) +{ + int fd[2]; + int error; + + error = do_pipe(fd); + if (!error) { + if (copy_to_user((void *)r0, (void *)fd, 2*sizeof(int))) + error = -EFAULT; + } + return error; +} + +static inline long do_mmap2( + unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + int fd, unsigned long pgoff) +{ + int error = -EBADF; + struct file *file = NULL; + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + if (!(flags & MAP_ANONYMOUS)) { + file = fget(fd); + if (!file) + goto out; + } + + down_write(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + up_write(¤t->mm->mmap_sem); + + if (file) + fput(file); +out: + return error; +} + +asmlinkage long sys_mmap2(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff) +{ + return do_mmap2(addr, len, prot, flags, fd, pgoff); +} + +/* + * Perform the select(nd, in, out, ex, tv) and mmap() system + * calls. Linux/M32R didn't use to be able to handle more than + * 4 system call parameters, so these system calls used a memory + * block for parameter passing.. + */ + +struct mmap_arg_struct { + unsigned long addr; + unsigned long len; + unsigned long prot; + unsigned long flags; + unsigned long fd; + unsigned long offset; +}; + +asmlinkage int old_mmap(struct mmap_arg_struct *arg) +{ + struct mmap_arg_struct a; + int err = -EFAULT; + + if (copy_from_user(&a, arg, sizeof(a))) + goto out; + + err = -EINVAL; + if (a.offset & ~PAGE_MASK) + goto out; + err = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, + a.offset>>PAGE_SHIFT); +out: + return err; +} + +struct sel_arg_struct { + unsigned long n; + fd_set __user *inp, *outp, *exp; + struct timeval __user *tvp; +}; + +asmlinkage int old_select(struct sel_arg_struct __user *arg) +{ + struct sel_arg_struct a; + + if (copy_from_user(&a, arg, sizeof(a))) + return -EFAULT; + /* sys_select() does the appropriate kernel locking */ + return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp); +} + +/* + * sys_ipc() is the de-multiplexer for the SysV IPC calls.. + * + * This is really horribly ugly. + */ +asmlinkage int sys_ipc(uint call, int first, int second, + int third, void __user *ptr, long fifth) +{ + int version, ret; + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + switch (call) { + case SEMOP: + return sys_semtimedop(first, (struct sembuf __user *)ptr, + second, NULL); + case SEMTIMEDOP: + return sys_semtimedop(first, (struct sembuf __user *)ptr, + second, (const struct timespec __user *)fifth); + case SEMGET: + return sys_semget (first, second, third); + case SEMCTL: { + union semun fourth; + if (!ptr) + return -EINVAL; + if (get_user(fourth.__pad, (void __user * __user *) ptr)) + return -EFAULT; + return sys_semctl (first, second, third, fourth); + } + + case MSGSND: + return sys_msgsnd (first, (struct msgbuf __user *) ptr, + second, third); + case MSGRCV: + switch (version) { + case 0: { + struct ipc_kludge tmp; + if (!ptr) + return -EINVAL; + + if (copy_from_user(&tmp, + (struct ipc_kludge __user *) ptr, + sizeof (tmp))) + return -EFAULT; + return sys_msgrcv (first, tmp.msgp, second, + tmp.msgtyp, third); + } + default: + return sys_msgrcv (first, + (struct msgbuf __user *) ptr, + second, fifth, third); + } + case MSGGET: + return sys_msgget ((key_t) first, second); + case MSGCTL: + return sys_msgctl (first, second, + (struct msqid_ds __user *) ptr); + case SHMAT: + switch (version) { + default: { + ulong raddr; + ret = do_shmat (first, (char __user *) ptr, + second, &raddr); + if (ret) + return ret; + return put_user (raddr, (ulong __user *) third); + } + case 1: /* iBCS2 emulator entry point */ + if (!segment_eq(get_fs(), get_ds())) + return -EINVAL; + return do_shmat (first, (char __user *) ptr, + second, (ulong *) third); + } + case SHMDT: + return sys_shmdt ((char __user *)ptr); + case SHMGET: + return sys_shmget (first, second, third); + case SHMCTL: + return sys_shmctl (first, second, + (struct shmid_ds __user *) ptr); + default: + return -ENOSYS; + } +} + +asmlinkage int sys_uname(struct old_utsname * name) +{ + int err; + if (!name) + return -EFAULT; + down_read(&uts_sem); + err=copy_to_user(name, &system_utsname, sizeof (*name)); + up_read(&uts_sem); + return err?-EFAULT:0; +} + +asmlinkage int sys_cacheflush(void *addr, int bytes, int cache) +{ + /* This should flush more selectivly ... */ + _flush_cache_all(); + return 0; +} + +asmlinkage int sys_cachectl(char *addr, int nbytes, int op) +{ + /* Not implemented yet. */ + return -ENOSYS; +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/time.c 2004-09-03 00:57:18.725192600 -0700 @@ -0,0 +1,318 @@ +/* + * linux/arch/m32r/kernel/time.c + * + * Copyright (c) 2001, 2002 Hiroyuki Kondo, Hirokazu Takata, + * Hitoshi Yamamoto + * Taken from i386 version. + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * Copyright (C) 1996, 1997, 1998 Ralf Baechle + * + * This file contains the time handling details for PC-style clocks as + * found in some MIPS systems. + * + * Some code taken from sh version. + * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka + * Copyright (C) 2000 Philipp Rumpf + */ + +/* $Id$ */ + +#undef DEBUG_TIMER + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#ifdef CONFIG_SMP +extern void send_IPI_allbutself(int, int); +extern void smp_local_timer_interrupt(struct pt_regs *); +#endif + +u64 jiffies_64 = INITIAL_JIFFIES; + +EXPORT_SYMBOL(jiffies_64); + +extern unsigned long wall_jiffies; +#define TICK_SIZE (tick_nsec / 1000) + +/* + * Change this if you have some constant time drift + */ + +/* This is for machines which generate the exact clock. */ +#define USECS_PER_JIFFY (1000000/HZ) + +static unsigned long latch; + +static unsigned long do_gettimeoffset(void) +{ + unsigned long elapsed_time = 0; /* [us] */ + +#if defined(CONFIG_CHIP_M32102) || defined(CONFIG_CHIP_XNUX2) \ + || defined(CONFIG_CHIP_VDEC2) || defined(CONFIG_CHIP_M32700) \ + || defined(CONFIG_CHIP_OPSP) +#ifndef CONFIG_SMP + + unsigned long count; + + /* timer count may underflow right here */ + count = inl(M32R_MFT2CUT_PORTL); + + if (inl(M32R_ICU_CR18_PORTL) & 0x00000100) /* underflow check */ + count = 0; + + count = (latch - count) * TICK_SIZE; + elapsed_time = (count + latch / 2) / latch; + /* NOTE: LATCH is equal to the "interval" value (= reload count). */ + +#else /* CONFIG_SMP */ + unsigned long count; + static unsigned long p_jiffies = -1; + static unsigned long p_count = 0; + + /* timer count may underflow right here */ + count = inl(M32R_MFT2CUT_PORTL); + + if (jiffies == p_jiffies && count > p_count) + count = 0; + + p_jiffies = jiffies; + p_count = count; + + count = (latch - count) * TICK_SIZE; + elapsed_time = (count + latch / 2) / latch; + /* NOTE: LATCH is equal to the "interval" value (= reload count). */ +#endif /* CONFIG_SMP */ +#elif defined(CONFIG_CHIP_M32310) +#warning do_gettimeoffse not implemented +#else +#error no chip configuration +#endif + + return elapsed_time; +} + +/* + * This version of gettimeofday has near microsecond resolution. + */ +void do_gettimeofday(struct timeval *tv) +{ + unsigned long seq; + unsigned long usec, sec; + unsigned long max_ntp_tick = tick_usec - tickadj; + + do { + unsigned long lost; + + seq = read_seqbegin(&xtime_lock); + + usec = do_gettimeoffset(); + lost = jiffies - wall_jiffies; + + /* + * If time_adjust is negative then NTP is slowing the clock + * so make sure not to go into next possible interval. + * Better to lose some accuracy than have time go backwards.. + */ + if (unlikely(time_adjust < 0)) { + usec = min(usec, max_ntp_tick); + if (lost) + usec += lost * max_ntp_tick; + } else if (unlikely(lost)) + usec += lost * tick_usec; + + sec = xtime.tv_sec; + usec += (xtime.tv_nsec / 1000); + } while (read_seqretry(&xtime_lock, seq)); + + while (usec >= 1000000) { + usec -= 1000000; + sec++; + } + + tv->tv_sec = sec; + tv->tv_usec = usec; +} + +EXPORT_SYMBOL(do_gettimeofday); + +int do_settimeofday(struct timespec *tv) +{ + time_t wtm_sec, sec = tv->tv_sec; + long wtm_nsec, nsec = tv->tv_nsec; + + if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) + return -EINVAL; + + write_seqlock_irq(&xtime_lock); + /* + * This is revolting. We need to set "xtime" correctly. However, the + * value in this location is the value at the most recent update of + * wall time. Discover what correction gettimeofday() would have + * made, and then undo it! + */ + nsec -= do_gettimeoffset() * NSEC_PER_USEC; + nsec -= (jiffies - wall_jiffies) * TICK_NSEC; + + wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); + wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); + + set_normalized_timespec(&xtime, sec, nsec); + set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); + + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; + write_sequnlock_irq(&xtime_lock); + clock_was_set(); + + return 0; +} + +EXPORT_SYMBOL(do_settimeofday); + +/* + * In order to set the CMOS clock precisely, set_rtc_mmss has to be + * called 500 ms after the second nowtime has started, because when + * nowtime is written into the registers of the CMOS clock, it will + * jump to the next second precisely 500 ms later. Check the Motorola + * MC146818A or Dallas DS12887 data sheet for details. + * + * BUG: This routine does not handle hour overflow properly; it just + * sets the minutes. Usually you won't notice until after reboot! + */ +static __inline__ int set_rtc_mmss(unsigned long nowtime) +{ + return 0; +} + +/* last time the cmos clock got updated */ +static long last_rtc_update = 0; + +/* + * timer_interrupt() needs to keep up the real-time clock, + * as well as call the "do_timer()" routine every clocktick + */ +static __inline__ void do_timer_interrupt(int irq, void *dev_id, + struct pt_regs * regs) +{ + do_timer(regs); + + /* + * If we have an externally synchronized Linux clock, then update + * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be + * called as close as possible to 500 ms before the new second starts. + */ + if ((time_status & STA_UNSYNC) == 0 + && xtime.tv_sec > last_rtc_update + 660 + && (xtime.tv_nsec / 1000) >= 500000 - ((unsigned)TICK_SIZE) / 2 + && (xtime.tv_nsec / 1000) <= 500000 + ((unsigned)TICK_SIZE) / 2) + { + if (set_rtc_mmss(xtime.tv_sec) == 0) + last_rtc_update = xtime.tv_sec; + else /* do it again in 60 s */ + last_rtc_update = xtime.tv_sec - 600; + } + /* As we return to user mode fire off the other CPU schedulers.. + this is basically because we don't yet share IRQ's around. + This message is rigged to be safe on the 386 - basically it's + a hack, so don't look closely for now.. */ + +#ifdef CONFIG_SMP + smp_local_timer_interrupt(regs); +#endif +} + +irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + write_seqlock(&xtime_lock); + do_timer_interrupt(irq, NULL, regs); + write_sequnlock(&xtime_lock); + +#ifndef CONFIG_SMP + m32r_do_profile(regs); +#endif + + return IRQ_HANDLED; +} + +struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, CPU_MASK_NONE, + "MFT2", NULL, NULL }; + +void __init time_init(void) +{ + unsigned int epoch, year, mon, day, hour, min, sec; + + sec = min = hour = day = mon = year = 0; + epoch = 0; + + year = 23; + mon = 4; + day = 17; + + /* Attempt to guess the epoch. This is the same heuristic as in rtc.c + so no stupid things will happen to timekeeping. Who knows, maybe + Ultrix also uses 1952 as epoch ... */ + if (year > 10 && year < 44) + epoch = 1980; + else if (year < 96) + epoch = 1952; + year += epoch; + + xtime.tv_sec = mktime(year, mon, day, hour, min, sec); + xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); + wall_to_monotonic.tv_sec = -xtime.tv_sec; + wall_to_monotonic.tv_nsec = -xtime.tv_nsec; + +#if defined(CONFIG_CHIP_M32102) || defined(CONFIG_CHIP_XNUX2) \ + || defined(CONFIG_CHIP_VDEC2) || defined(CONFIG_CHIP_M32700) \ + || defined(CONFIG_CHIP_OPSP) + + /* M32102 MFT setup */ + setup_irq(M32R_IRQ_MFT2, &irq0); + { + unsigned long bus_clock; + unsigned short divide; + + bus_clock = boot_cpu_data.bus_clock; + divide = boot_cpu_data.timer_divide; + latch = (bus_clock/divide + HZ / 2) / HZ; + + printk("Timer start : latch = %ld\n", latch); + + outl((M32R_MFTMOD_CC_MASK | M32R_MFTMOD_TCCR \ + |M32R_MFTMOD_CSSEL011), M32R_MFT2MOD_PORTL); + outl(latch, M32R_MFT2RLD_PORTL); + outl(latch, M32R_MFT2CUT_PORTL); + outl(0, M32R_MFT2CMPRLD_PORTL); + outl((M32R_MFTCR_MFT2MSK|M32R_MFTCR_MFT2EN), M32R_MFTCR_PORTL); + } + +#elif defined(CONFIG_CHIP_M32310) +#warning time_init not implemented +#else +#error no chip configuration +#endif +} + +/* + * Scheduler clock - returns current time in nanosec units. + */ +unsigned long long sched_clock(void) +{ + return (unsigned long long)jiffies * (1000000000 / HZ); +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/traps.c 2004-09-03 00:57:19.065140920 -0700 @@ -0,0 +1,330 @@ +/* + * linux/arch/m32r/kernel/traps.c + * + * Copyright (C) 2001, 2002 Hirokazu Takata, Hiroyuki Kondo, + * Hitoshi Yamamoto + */ + +/* $Id$ */ + +/* + * 'traps.c' handles hardware traps and faults after we have saved some + * state in 'entry.S'. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +asmlinkage void alignment_check(void); +asmlinkage void ei_handler(void); +asmlinkage void rie_handler(void); +asmlinkage void debug_trap(void); +asmlinkage void cache_flushing_handler(void); + +#ifdef CONFIG_SMP +extern void smp_reschedule_interrupt(void); +extern void smp_invalidate_interrupt(void); +extern void smp_call_function_interrupt(void); +extern void smp_ipi_timer_interrupt(void); +extern void smp_flush_cache_all_interrupt(void); + +/* + * for Boot AP function + */ +asm ( + " .section .eit_vector4,\"ax\" \n" + " .global _AP_RE \n" + " .global startup_AP \n" + "_AP_RE: \n" + " .fill 32, 4, 0 \n" + "_AP_EI: bra startup_AP \n" + " .previous \n" +); +#endif /* CONFIG_SMP */ + +extern unsigned long eit_vector[]; +#define BRA_INSN(func, entry) \ + ((unsigned long)func - (unsigned long)eit_vector - entry*4)/4 \ + + 0xff000000UL + +void set_eit_vector_entries(void) +{ + extern void default_eit_handler(void); + extern void system_call(void); + extern void pie_handler(void); + extern void ace_handler(void); + extern void tme_handler(void); + extern void _flush_cache_copyback_all(void); + + eit_vector[0] = 0xd0c00001; /* seth r0, 0x01 */ + eit_vector[1] = BRA_INSN(default_eit_handler, 1); + eit_vector[4] = 0xd0c00010; /* seth r0, 0x10 */ + eit_vector[5] = BRA_INSN(default_eit_handler, 5); + eit_vector[8] = BRA_INSN(rie_handler, 8); + eit_vector[12] = BRA_INSN(alignment_check, 12); + eit_vector[16] = 0xff000000UL; + eit_vector[17] = BRA_INSN(debug_trap, 17); + eit_vector[18] = BRA_INSN(system_call, 18); + eit_vector[19] = 0xff000000UL; + eit_vector[20] = 0xff000000UL; + eit_vector[21] = 0xff000000UL; + eit_vector[22] = 0xff000000UL; + eit_vector[23] = 0xff000000UL; + eit_vector[24] = 0xff000000UL; + eit_vector[25] = 0xff000000UL; + eit_vector[26] = 0xff000000UL; + eit_vector[27] = 0xff000000UL; + eit_vector[28] = BRA_INSN(cache_flushing_handler, 28); + eit_vector[29] = 0xff000000UL; + eit_vector[30] = 0xff000000UL; + eit_vector[31] = 0xff000000UL; + eit_vector[32] = BRA_INSN(ei_handler, 32); + eit_vector[64] = BRA_INSN(pie_handler, 64); + eit_vector[68] = BRA_INSN(ace_handler, 68); + eit_vector[72] = BRA_INSN(tme_handler, 72); +#ifdef CONFIG_SMP + eit_vector[184] = (unsigned long)smp_reschedule_interrupt; + eit_vector[185] = (unsigned long)smp_invalidate_interrupt; + eit_vector[186] = (unsigned long)smp_call_function_interrupt; + eit_vector[187] = (unsigned long)smp_ipi_timer_interrupt; + eit_vector[188] = (unsigned long)smp_flush_cache_all_interrupt; + eit_vector[189] = 0; + eit_vector[190] = 0; + eit_vector[191] = 0; +#endif + _flush_cache_copyback_all(); +} + +void __init trap_init(void) +{ + set_eit_vector_entries(); + + /* + * Should be a barrier for any external CPU state. + */ + cpu_init(); +} + +int kstack_depth_to_print = 24; + +void show_trace(struct task_struct *task, unsigned long *stack) +{ + unsigned long addr; + + if (!stack) + stack = (unsigned long*)&stack; + + printk("Call Trace: "); + while (!kstack_end(stack)) { + addr = *stack++; + if (__kernel_text_address(addr)) { + printk("[<%08lx>] ", addr); + print_symbol("%s\n", addr); + } + } + printk("\n"); +} + +void show_stack(struct task_struct *task, unsigned long *sp) +{ + unsigned long *stack; + int i; + + /* + * debugging aid: "show_stack(NULL);" prints the + * back trace for this cpu. + */ + + if(sp==NULL) { + if (task) + sp = (unsigned long *)task->thread.sp; + else + sp=(unsigned long*)&sp; + } + + stack = sp; + for(i=0; i < kstack_depth_to_print; i++) { + if (kstack_end(stack)) + break; + if (i && ((i % 4) == 0)) + printk("\n "); + printk("%08lx ", *stack++); + } + printk("\n"); + show_trace(task, sp); +} + +void dump_stack(void) +{ + unsigned long stack; + + show_trace(current, &stack); +} + +EXPORT_SYMBOL(dump_stack); + +static void show_registers(struct pt_regs *regs) +{ + int i = 0; + int in_kernel = 1; + unsigned long sp; + + printk("CPU: %d\n", smp_processor_id()); + show_regs(regs); + + sp = (unsigned long) (1+regs); + if (user_mode(regs)) { + in_kernel = 0; + sp = regs->spu; + printk("SPU: %08lx\n", sp); + } else { + printk("SPI: %08lx\n", sp); + } + printk("Process %s (pid: %d, process nr: %d, stackpage=%08lx)", + current->comm, current->pid, 0xffff & i, 4096+(unsigned long)current); + + /* + * When in-kernel, we also print out the stack and code at the + * time of the fault.. + */ + if (in_kernel) { + printk("\nStack: "); + show_stack(current, (unsigned long*) sp); + + printk("\nCode: "); + if (regs->bpc < PAGE_OFFSET) + goto bad; + + for(i=0;i<20;i++) { + unsigned char c; + if (__get_user(c, &((unsigned char*)regs->bpc)[i])) { +bad: + printk(" Bad PC value."); + break; + } + printk("%02x ", c); + } + } + printk("\n"); +} + +spinlock_t die_lock = SPIN_LOCK_UNLOCKED; + +void die(const char * str, struct pt_regs * regs, long err) +{ + console_verbose(); + spin_lock_irq(&die_lock); + bust_spinlocks(1); + printk("%s: %04lx\n", str, err & 0xffff); + show_registers(regs); + bust_spinlocks(0); + spin_unlock_irq(&die_lock); + do_exit(SIGSEGV); +} + +static __inline__ void die_if_kernel(const char * str, + struct pt_regs * regs, long err) +{ + if (!user_mode(regs)) + die(str, regs, err); +} + +static __inline__ void do_trap(int trapnr, int signr, const char * str, + struct pt_regs * regs, long error_code, siginfo_t *info) +{ + if (user_mode(regs)) { + /* trap_signal */ + struct task_struct *tsk = current; + tsk->thread.error_code = error_code; + tsk->thread.trap_no = trapnr; + if (info) + force_sig_info(signr, info, tsk); + else + force_sig(signr, tsk); + return; + } else { + /* kernel_trap */ + if (!fixup_exception(regs)) + die(str, regs, error_code); + return; + } +} + +#define DO_ERROR(trapnr, signr, str, name) \ +asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ +{ \ + do_trap(trapnr, signr, 0, regs, error_code, NULL); \ +} + +#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \ +asmlinkage void do_##name(struct pt_regs * regs, long error_code) \ +{ \ + siginfo_t info; \ + info.si_signo = signr; \ + info.si_errno = 0; \ + info.si_code = sicode; \ + info.si_addr = (void __user *)siaddr; \ + do_trap(trapnr, signr, str, regs, error_code, &info); \ +} + +DO_ERROR( 1, SIGTRAP, "debug trap", debug_trap) +DO_ERROR_INFO(0x20, SIGILL, "reserved instruction ", rie_handler, ILL_ILLOPC, regs->bpc) +DO_ERROR_INFO(0x100, SIGILL, "privilege instruction", pie_handler, ILL_PRVOPC, regs->bpc) + +extern int handle_unaligned_access(unsigned long, struct pt_regs *); + +/* This code taken from arch/sh/kernel/traps.c */ +asmlinkage void do_alignment_check(struct pt_regs *regs, long error_code) +{ + mm_segment_t oldfs; + unsigned long insn; + int tmp; + + oldfs = get_fs(); + + if (user_mode(regs)) { + local_irq_enable(); + current->thread.error_code = error_code; + current->thread.trap_no = 0x17; + + set_fs(USER_DS); + if (copy_from_user(&insn, (void *)regs->bpc, 4)) { + set_fs(oldfs); + goto uspace_segv; + } + tmp = handle_unaligned_access(insn, regs); + set_fs(oldfs); + + if (!tmp) + return; + + uspace_segv: + printk(KERN_NOTICE "Killing process \"%s\" due to unaligned " + "access\n", current->comm); + force_sig(SIGSEGV, current); + } else { + set_fs(KERNEL_DS); + if (copy_from_user(&insn, (void *)regs->bpc, 4)) { + set_fs(oldfs); + die("insn faulting in do_address_error", regs, 0); + } + handle_unaligned_access(insn, regs); + set_fs(oldfs); + } +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/kernel/vmlinux.lds.S 2004-09-03 00:57:19.065140920 -0700 @@ -0,0 +1,145 @@ +/* ld script to make M32R Linux kernel + */ + +#include +#include +#include +#include + +OUTPUT_ARCH(m32r) +ENTRY(startup_32) +#if defined(__LITTLE_ENDIAN__) + jiffies = jiffies_64; +#else + jiffies = jiffies_64 + 4; +#endif +SECTIONS +{ + . = CONFIG_MEMORY_START + __PAGE_OFFSET; + eit_vector = .; + + . = . + 0x1000; + .empty_zero_page : { *(.empty_zero_page) } = 0 + + /* read-only */ + _text = .; /* Text and read-only data */ + .boot : { *(.boot) } = 0 + .text : { + *(.text) + SCHED_TEXT + *(.fixup) + *(.gnu.warning) + } = 0x9090 +#ifdef CONFIG_SMP + . = ALIGN(65536); + .eit_vector4 : { *(.eit_vector4) } +#endif + _etext = .; /* End of text section */ + + . = ALIGN(16); /* Exception table */ + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + RODATA + + /* writeable */ + .data : { /* Data */ + *(.spu) + *(.spi) + *(.data) + CONSTRUCTORS + } + + . = ALIGN(4096); + __nosave_begin = .; + .data_nosave : { *(.data.nosave) } + . = ALIGN(4096); + __nosave_end = .; + + . = ALIGN(4096); + .data.page_aligned : { *(.data.idt) } + + . = ALIGN(32); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + + _edata = .; /* End of data section */ + + . = ALIGN(8192); /* init_task */ + .data.init_task : { *(.data.init_task) } + + /* will be freed after init */ + . = ALIGN(4096); /* Init code and data */ + __init_begin = .; + .init.text : { + _sinittext = .; + *(.init.text) + _einittext = .; + } + .init.data : { *(.init.data) } + . = ALIGN(16); + __setup_start = .; + .init.setup : { *(.init.setup) } + __setup_end = .; + __start___param = .; + __param : { *(__param) } + __stop___param = .; + __initcall_start = .; + .initcall.init : { + *(.initcall1.init) + *(.initcall2.init) + *(.initcall3.init) + *(.initcall4.init) + *(.initcall5.init) + *(.initcall6.init) + *(.initcall7.init) + } + __initcall_end = .; + __con_initcall_start = .; + .con_initcall.init : { *(.con_initcall.init) } + __con_initcall_end = .; + SECURITY_INIT + . = ALIGN(4); + __alt_instructions = .; + .altinstructions : { *(.altinstructions) } + __alt_instructions_end = .; + .altinstr_replacement : { *(.altinstr_replacement) } + /* .exit.text is discard at runtime, not link time, to deal with references + from .altinstructions and .eh_frame */ + .exit.text : { *(.exit.text) } + .exit.data : { *(.exit.data) } + . = ALIGN(4096); + __initramfs_start = .; + .init.ramfs : { *(.init.ramfs) } + __initramfs_end = .; + . = ALIGN(32); + __per_cpu_start = .; + .data.percpu : { *(.data.percpu) } + __per_cpu_end = .; + . = ALIGN(4096); + __init_end = .; + /* freed after init ends here */ + + __bss_start = .; /* BSS */ + .bss : { *(.bss) } + . = ALIGN(4); + __bss_stop = .; + + _end = . ; + + /* Sections to be discarded */ + /DISCARD/ : { + *(.exit.text) + *(.exit.data) + *(.exitcall.exit) + } + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/lib/ashxdi3.S 2004-09-03 00:57:17.363399624 -0700 @@ -0,0 +1,297 @@ +/* + * linux/arch/m32r/lib/ashxdi3.S + * + * Copyright (C) 2001,2002 Hiroyuki Kondo, and Hirokazu Takata + * + */ +/* $Id$ */ + +#include + +; +; input (r0,r1) src +; input r2 shift val +; r3 scratch +; output (r0,r1) +; + +#ifdef CONFIG_ISA_DUAL_ISSUE + +#ifndef __LITTLE_ENDIAN__ + + .text + .align 4 + .globl __ashrdi3 +__ashrdi3: + cmpz r2 || ldi r3, #32 + jc r14 || cmpu r2, r3 + bc 1f + ; case 32 =< shift + mv r1, r0 || srai r0, #31 + addi r2, #-32 + sra r1, r2 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r0 || srl r1, r2 + sra r0, r2 || neg r2, r2 + sll r3, r2 + or r1, r3 || jmp r14 + + .align 4 + .globl __ashldi3 + .globl __lshldi3 +__ashldi3: +__lshldi3: + cmpz r2 || ldi r3, #32 + jc r14 || cmpu r2, r3 + bc 1f + ; case 32 =< shift + mv r0, r1 || addi r2, #-32 + sll r0, r2 || ldi r1, #0 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r1 || sll r0, r2 + sll r1, r2 || neg r2, r2 + srl r3, r2 + or r0, r3 || jmp r14 + + .align 4 + .globl __lshrdi3 +__lshrdi3: + cmpz r2 || ldi r3, #32 + jc r14 || cmpu r2, r3 + bc 1f + ; case 32 =< shift + mv r1, r0 || addi r2, #-32 + ldi r0, #0 || srl r1, r2 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r0 || srl r1, r2 + srl r0, r2 || neg r2, r2 + sll r3, r2 + or r1, r3 || jmp r14 + +#else /* LITTLE_ENDIAN */ + + .text + .align 4 + .globl __ashrdi3 +__ashrdi3: + cmpz r2 || ldi r3, #32 + jc r14 || cmpu r2, r3 + bc 1f + ; case 32 =< shift + mv r0, r1 || srai r1, #31 + addi r2, #-32 + sra r0, r2 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r1 || srl r0, r2 + sra r1, r2 || neg r2, r2 + sll r3, r2 + or r0, r3 || jmp r14 + + .align 4 + .globl __ashldi3 + .globl __lshldi3 +__ashldi3: +__lshldi3: + cmpz r2 || ldi r3, #32 + jc r14 || cmpu r2, r3 + bc 1f + ; case 32 =< shift + mv r1, r0 || addi r2, #-32 + sll r1, r2 || ldi r0, #0 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r0 || sll r1, r2 + sll r0, r2 || neg r2, r2 + srl r3, r2 + or r1, r3 || jmp r14 + + .align 4 + .globl __lshrdi3 +__lshrdi3: + cmpz r2 || ldi r3, #32 + jc r14 || cmpu r2, r3 + bc 1f + ; case 32 =< shift + mv r0, r1 || addi r2, #-32 + ldi r1, #0 || srl r0, r2 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r1 || srl r0, r2 + srl r1, r2 || neg r2, r2 + sll r3, r2 + or r0, r3 || jmp r14 + +#endif + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + +#ifndef __LITTLE_ENDIAN__ + + .text + .align 4 + .globl __ashrdi3 +__ashrdi3: + beqz r2, 2f + cmpui r2, #32 + bc 1f + ; case 32 =< shift + mv r1, r0 + srai r0, #31 + addi r2, #-32 + sra r1, r2 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r0 + srl r1, r2 + sra r0, r2 + neg r2, r2 + sll r3, r2 + or r1, r3 + .fillinsn +2: + jmp r14 + + .align 4 + .globl __ashldi3 + .globl __lshldi3 +__ashldi3: +__lshldi3: + beqz r2, 2f + cmpui r2, #32 + bc 1f + ; case 32 =< shift + mv r0, r1 + addi r2, #-32 + sll r0, r2 + ldi r1, #0 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r1 + sll r0, r2 + sll r1, r2 + neg r2, r2 + srl r3, r2 + or r0, r3 + .fillinsn +2: + jmp r14 + + .align 4 + .globl __lshrdi3 +__lshrdi3: + beqz r2, 2f + cmpui r2, #32 + bc 1f + ; case 32 =< shift + mv r1, r0 + ldi r0, #0 + addi r2, #-32 + srl r1, r2 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r0 + srl r1, r2 + srl r0, r2 + neg r2, r2 + sll r3, r2 + or r1, r3 + .fillinsn +2: + jmp r14 + +#else + + .text + .align 4 + .globl __ashrdi3 +__ashrdi3: + beqz r2, 2f + cmpui r2, #32 + bc 1f + ; case 32 =< shift + mv r0, r1 + srai r1, #31 + addi r2, #-32 + sra r0, r2 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r1 + srl r0, r2 + sra r1, r2 + neg r2, r2 + sll r3, r2 + or r0, r3 + .fillinsn +2: + jmp r14 + + .align 4 + .globl __ashldi3 + .globl __lshldi3 +__ashldi3: +__lshldi3: + beqz r2, 2f + cmpui r2, #32 + bc 1f + ; case 32 =< shift + mv r1, r0 + addi r2, #-32 + sll r1, r2 + ldi r0, #0 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r0 + sll r1, r2 + sll r0, r2 + neg r2, r2 + srl r3, r2 + or r1, r3 + .fillinsn +2: + jmp r14 + + .align 4 + .globl __lshrdi3 +__lshrdi3: + beqz r2, 2f + cmpui r2, #32 + bc 1f + ; case 32 =< shift + mv r0, r1 + ldi r1, #0 + addi r2, #-32 + srl r0, r2 + jmp r14 + .fillinsn +1: ; case shift <32 + mv r3, r1 + srl r0, r2 + srl r1, r2 + neg r2, r2 + sll r3, r2 + or r0, r3 + .fillinsn +2: + jmp r14 + +#endif + +#endif /* not CONFIG_ISA_DUAL_ISSUE */ + + .end + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/lib/checksum.S 2004-09-03 00:57:17.364399472 -0700 @@ -0,0 +1,322 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * IP/TCP/UDP checksumming routines + * + * Authors: Jorge Cwik, + * Arnt Gulbrandsen, + * Tom May, + * Pentium Pro/II routines: + * Alexander Kjeldaas + * Finn Arne Gangstad + * Lots of code moved from tcp.c and ip.c; see those files + * for more names. + * + * Changes: Ingo Molnar, converted csum_partial_copy() to 2.1 exception + * handling. + * Andi Kleen, add zeroing on error + * converted to pure assembler + * Hirokazu Takata,Hiroyuki Kondo rewrite for the m32r architecture. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +/* $Id$ */ + + +#include +#include +#include +#include + +/* + * computes a partial checksum, e.g. for TCP/UDP fragments + */ + +/* +unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum) + */ + + +#ifdef CONFIG_ISA_DUAL_ISSUE + + /* + * Experiments with Ethernet and SLIP connections show that buff + * is aligned on either a 2-byte or 4-byte boundary. We get at + * least a twofold speedup on 486 and Pentium if it is 4-byte aligned. + * Fortunately, it is easy to convert 2-byte alignment to 4-byte + * alignment for the unrolled loop. + */ + + .text +ENTRY(csum_partial) + ; Function args + ; r0: unsigned char *buff + ; r1: int len + ; r2: unsigned int sum + + push r2 || ldi r2, #0 + and3 r7, r0, #1 ; Check alignment. + beqz r7, 1f ; Jump if alignment is ok. + ; 1-byte mis aligned + ldub r4, @r0 || addi r0, #1 + ; clear c-bit || Alignment uses up bytes. + cmp r0, r0 || addi r1, #-1 + ldi r3, #0 || addx r2, r4 + addx r2, r3 + .fillinsn +1: + and3 r4, r0, #2 ; Check alignment. + beqz r4, 2f ; Jump if alignment is ok. + ; clear c-bit || Alignment uses up two bytes. + cmp r0, r0 || addi r1, #-2 + bgtz r1, 1f ; Jump if we had at least two bytes. + bra 4f || addi r1, #2 + .fillinsn ; len(r1) was < 2. Deal with it. +1: + ; 2-byte aligned + lduh r4, @r0 || ldi r3, #0 + addx r2, r4 || addi r0, #2 + addx r2, r3 + .fillinsn +2: + ; 4-byte aligned + cmp r0, r0 ; clear c-bit + srl3 r6, r1, #5 + beqz r6, 2f + .fillinsn + +1: ld r3, @r0+ + ld r4, @r0+ ; +4 + ld r5, @r0+ ; +8 + ld r3, @r0+ || addx r2, r3 ; +12 + ld r4, @r0+ || addx r2, r4 ; +16 + ld r5, @r0+ || addx r2, r5 ; +20 + ld r3, @r0+ || addx r2, r3 ; +24 + ld r4, @r0+ || addx r2, r4 ; +28 + addx r2, r5 || addi r6, #-1 + addx r2, r3 + addx r2, r4 + bnez r6, 1b + + addx r2, r6 ; r6=0 + cmp r0, r0 ; This clears c-bit + .fillinsn +2: and3 r6, r1, #0x1c ; withdraw len + beqz r6, 4f + srli r6, #2 + .fillinsn + +3: ld r4, @r0+ || addi r6, #-1 + addx r2, r4 + bnez r6, 3b + + addx r2, r6 ; r6=0 + cmp r0, r0 ; This clears c-bit + .fillinsn +4: and3 r1, r1, #3 + beqz r1, 7f ; if len == 0 goto end + and3 r6, r1, #2 + beqz r6, 5f ; if len < 2 goto 5f(1byte) + lduh r4, @r0 || addi r0, #2 + addi r1, #-2 || slli r4, #16 + addx r2, r4 + beqz r1, 6f + .fillinsn +5: ldub r4, @r0 || ldi r1, #0 +#ifndef __LITTLE_ENDIAN__ + slli r4, #8 +#endif + addx r2, r4 + .fillinsn +6: addx r2, r1 + .fillinsn +7: + and3 r0, r2, #0xffff + srli r2, #16 + add r0, r2 + srl3 r2, r0, #16 + beqz r2, 1f + addi r0, #1 + and3 r0, r0, #0xffff + .fillinsn +1: + beqz r7, 1f ; swap the upper byte for the lower + and3 r2, r0, #0xff + srl3 r0, r0, #8 + slli r2, #8 + or r0, r2 + .fillinsn +1: + pop r2 || cmp r0, r0 + addx r0, r2 || ldi r2, #0 + addx r0, r2 + jmp r14 + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + + /* + * Experiments with Ethernet and SLIP connections show that buff + * is aligned on either a 2-byte or 4-byte boundary. We get at + * least a twofold speedup on 486 and Pentium if it is 4-byte aligned. + * Fortunately, it is easy to convert 2-byte alignment to 4-byte + * alignment for the unrolled loop. + */ + + .text +ENTRY(csum_partial) + ; Function args + ; r0: unsigned char *buff + ; r1: int len + ; r2: unsigned int sum + + push r2 + ldi r2, #0 + and3 r7, r0, #1 ; Check alignment. + beqz r7, 1f ; Jump if alignment is ok. + ; 1-byte mis aligned + ldub r4, @r0 + addi r0, #1 + addi r1, #-1 ; Alignment uses up bytes. + cmp r0, r0 ; clear c-bit + ldi r3, #0 + addx r2, r4 + addx r2, r3 + .fillinsn +1: + and3 r4, r0, #2 ; Check alignment. + beqz r4, 2f ; Jump if alignment is ok. + addi r1, #-2 ; Alignment uses up two bytes. + cmp r0, r0 ; clear c-bit + bgtz r1, 1f ; Jump if we had at least two bytes. + addi r1, #2 ; len(r1) was < 2. Deal with it. + bra 4f + .fillinsn +1: + ; 2-byte aligned + lduh r4, @r0 + addi r0, #2 + ldi r3, #0 + addx r2, r4 + addx r2, r3 + .fillinsn +2: + ; 4-byte aligned + cmp r0, r0 ; clear c-bit + srl3 r6, r1, #5 + beqz r6, 2f + .fillinsn + +1: ld r3, @r0+ + ld r4, @r0+ ; +4 + ld r5, @r0+ ; +8 + addx r2, r3 + addx r2, r4 + addx r2, r5 + ld r3, @r0+ ; +12 + ld r4, @r0+ ; +16 + ld r5, @r0+ ; +20 + addx r2, r3 + addx r2, r4 + addx r2, r5 + ld r3, @r0+ ; +24 + ld r4, @r0+ ; +28 + addi r6, #-1 + addx r2, r3 + addx r2, r4 + bnez r6, 1b + addx r2, r6 ; r6=0 + cmp r0, r0 ; This clears c-bit + .fillinsn + +2: and3 r6, r1, #0x1c ; withdraw len + beqz r6, 4f + srli r6, #2 + .fillinsn + +3: ld r4, @r0+ + addi r6, #-1 + addx r2, r4 + bnez r6, 3b + addx r2, r6 ; r6=0 + cmp r0, r0 ; This clears c-bit + .fillinsn + +4: and3 r1, r1, #3 + beqz r1, 7f ; if len == 0 goto end + and3 r6, r1, #2 + beqz r6, 5f ; if len < 2 goto 5f(1byte) + + lduh r4, @r0 + addi r0, #2 + addi r1, #-2 + slli r4, #16 + addx r2, r4 + beqz r1, 6f + .fillinsn +5: ldub r4, @r0 +#ifndef __LITTLE_ENDIAN__ + slli r4, #8 +#endif + addx r2, r4 + .fillinsn +6: ldi r5, #0 + addx r2, r5 + .fillinsn +7: + and3 r0, r2, #0xffff + srli r2, #16 + add r0, r2 + srl3 r2, r0, #16 + beqz r2, 1f + addi r0, #1 + and3 r0, r0, #0xffff + .fillinsn +1: + beqz r7, 1f + mv r2, r0 + srl3 r0, r2, #8 + and3 r2, r2, #0xff + slli r2, #8 + or r0, r2 + .fillinsn +1: + pop r2 + cmp r0, r0 + addx r0, r2 + ldi r2, #0 + addx r0, r2 + jmp r14 + +#endif /* not CONFIG_ISA_DUAL_ISSUE */ + +/* +unsigned int csum_partial_copy_generic (const char *src, char *dst, + int len, int sum, int *src_err_ptr, int *dst_err_ptr) + */ + +/* + * Copy from ds while checksumming, otherwise like csum_partial + * + * The macros SRC and DST specify the type of access for the instruction. + * thus we can call a custom exception handler for all access types. + * + * FIXME: could someone double-check whether I haven't mixed up some SRC and + * DST definitions? It's damn hard to trigger all cases. I hope I got + * them all but there's no guarantee. + */ + +ENTRY(csum_partial_copy_generic) + nop + nop + nop + nop + jmp r14 + nop + nop + nop + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/lib/csum_partial_copy.c 2004-09-03 00:57:17.364399472 -0700 @@ -0,0 +1,77 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * MIPS specific IP/TCP/UDP checksumming routines + * + * Authors: Ralf Baechle, + * Lots of code moved from tcp.c and ip.c; see those files + * for more names. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * $Id$ + */ +#include +#include +#include +#include +#include + +/* + * copy while checksumming, otherwise like csum_partial + */ +unsigned int csum_partial_copy(const char *src, char *dst, + int len, int sum) +{ + /* + * It's 2:30 am and I don't feel like doing it real ... + * This is lots slower than the real thing (tm) + */ + sum = csum_partial(src, len, sum); + memcpy(dst, src, len); + + return sum; +} + +/* + * Copy from userspace and compute checksum. If we catch an exception + * then zero the rest of the buffer. +unsigned int csum_partial_copy_from_user (const char *src, char *dst, + int len, unsigned int sum, + int *err_ptr) + */ +unsigned int csum_partial_copy_generic_from (const char *src, char *dst, + int len, unsigned int sum, + int *err_ptr) +{ + int missing; + + missing = copy_from_user(dst, src, len); + if (missing) { + memset(dst + len - missing, 0, missing); + *err_ptr = -EFAULT; + } + + return csum_partial(dst, len-missing, sum); +} +unsigned int csum_partial_copy_generic_to (const char *src, char *dst, + int len, unsigned int sum, + int *err_ptr) +{ + int missing; + + missing = copy_to_user(dst, src, len); + if (missing) { +/* + memset(dst + len - missing, 0, missing); +*/ + *err_ptr = -EFAULT; + } + + return csum_partial(src, len-missing, sum); +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/lib/delay.c 2004-09-03 00:57:18.726192448 -0700 @@ -0,0 +1,126 @@ +/* + * linux/arch/m32r/lib/delay.c + * + * Copyright (c) 2002 Hitoshi Yamamoto, Hirokazu Takata + * Copyright (c) 2004 Hirokazu Takata + */ + +/* $Id$ */ + +#include +#include +#ifdef CONFIG_SMP +#include +#include +#include +#endif /* CONFIG_SMP */ +#include + +void __delay(unsigned long loops) +{ +#ifdef CONFIG_ISA_DUAL_ISSUE + __asm__ __volatile__ ( + "beqz %0, 2f \n\t" + "addi %0, #-1 \n\t" + + " .fillinsn \n\t" + "1: \n\t" + "cmpz %0 || addi %0, #-1 \n\t" + "bc 2f || cmpz %0 \n\t" + "bc 2f || addi %0, #-1 \n\t" + "cmpz %0 || addi %0, #-1 \n\t" + "bc 2f || cmpz %0 \n\t" + "bnc 1b || addi %0, #-1 \n\t" + " .fillinsn \n\t" + "2: \n\t" + : "+r" (loops) + : "r" (0) + : "cbit" + ); +#else + __asm__ __volatile__ ( + "beqz %0, 2f \n\t" + " .fillinsn \n\t" + "1: \n\t" + "addi %0, #-1 \n\t" + "blez %0, 2f \n\t" + "addi %0, #-1 \n\t" + "blez %0, 2f \n\t" + "addi %0, #-1 \n\t" + "blez %0, 2f \n\t" + "addi %0, #-1 \n\t" + "bgtz %0, 1b \n\t" + " .fillinsn \n\t" + "2:i \n\t" + : "+r" (loops) + : "r" (0) + ); +#endif +} + +void __const_udelay(unsigned long xloops) +{ +#if defined(CONFIG_ISA_M32R2) && defined(CONFIG_ISA_DSP_LEVEL2) + /* + * loops [1] = (xloops >> 32) [sec] * loops_per_jiffy [1/jiffy] + * * HZ [jiffy/sec] + * = (xloops >> 32) [sec] * (loops_per_jiffy * HZ) [1/sec] + * = (((xloops * loops_per_jiffy) >> 32) * HZ) [1] + * + * NOTE: + * - '[]' depicts variable's dimension in the above equation. + * - "rac" instruction rounds the accumulator in word size. + */ + __asm__ __volatile__ ( + "srli %0, #1 \n\t" + "mulwhi %0, %1 ; a0 \n\t" + "mulwu1 %0, %1 ; a1 \n\t" + "sadd ; a0 += (a1 >> 16) \n\t" + "rac a0, a0, #1 \n\t" + "mvfacmi %0, a0 \n\t" + : "+r" (xloops) + : "r" (current_cpu_data.loops_per_jiffy) + : "a0", "a1" + ); +#elif defined(CONFIG_ISA_M32R2) || defined(CONFIG_ISA_M32R) + /* + * u64 ull; + * ull = (u64)xloops * (u64)current_cpu_data.loops_per_jiffy; + * xloops = (ull >> 32); + */ + __asm__ __volatile__ ( + "and3 r4, %0, #0xffff \n\t" + "and3 r5, %1, #0xffff \n\t" + "mul r4, r5 \n\t" + "srl3 r6, %0, #16 \n\t" + "srli r4, #16 \n\t" + "mul r5, r6 \n\t" + "add r4, r5 \n\t" + "and3 r5, %0, #0xffff \n\t" + "srl3 r6, %1, #16 \n\t" + "mul r5, r6 \n\t" + "add r4, r5 \n\t" + "srl3 r5, %0, #16 \n\t" + "srli r4, #16 \n\t" + "mul r5, r6 \n\t" + "add r4, r5 \n\t" + "mv %0, r4 \n\t" + : "+r" (xloops) + : "r" (current_cpu_data.loops_per_jiffy) + : "r4", "r5", "r6" + ); +#else +#error unknown isa configuration +#endif + __delay(xloops * HZ); +} + +void __udelay(unsigned long usecs) +{ + __const_udelay(usecs * 0x000010c7); /* 2**32 / 1000000 (rounded up) */ +} + +void __ndelay(unsigned long nsecs) +{ + __const_udelay(nsecs * 0x00005); /* 2**32 / 1000000000 (rounded up) */ +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/lib/getuser.S 2004-09-03 00:57:17.366399168 -0700 @@ -0,0 +1,88 @@ +/* + * __get_user functions. + * + * (C) Copyright 2001 Hirokazu Takata + * + * These functions have a non-standard call interface + * to make them more efficient, especially as they + * return an error value in addition to the "real" + * return value. + */ + +#include + +/* + * __get_user_X + * + * Inputs: r0 contains the address + * + * Outputs: r0 is error code (0 or -EFAULT) + * r1 contains zero-extended value + * + * These functions should not modify any other registers, + * as they get called from within inline assembly. + */ + +#ifdef CONFIG_ISA_DUAL_ISSUE + + .text + .balign 4 + .globl __get_user_1 +__get_user_1: +1: ldub r1, @r0 || ldi r0, #0 + jmp r14 + + .balign 4 + .globl __get_user_2 +__get_user_2: +2: lduh r1, @r0 || ldi r0, #0 + jmp r14 + + .balign 4 + .globl __get_user_4 +__get_user_4: +3: ld r1, @r0 || ldi r0, #0 + jmp r14 + +bad_get_user: + ldi r1, #0 || ldi r0, #-14 + jmp r14 + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + + .text + .balign 4 + .globl __get_user_1 +__get_user_1: +1: ldub r1, @r0 + ldi r0, #0 + jmp r14 + + .balign 4 + .globl __get_user_2 +__get_user_2: +2: lduh r1, @r0 + ldi r0, #0 + jmp r14 + + .balign 4 + .globl __get_user_4 +__get_user_4: +3: ld r1, @r0 + ldi r0, #0 + jmp r14 + +bad_get_user: + ldi r1, #0 + ldi r0, #-14 + jmp r14 + +#endif /* not CONFIG_ISA_DUAL_ISSUE */ + +.section __ex_table,"a" + .long 1b,bad_get_user + .long 2b,bad_get_user + .long 3b,bad_get_user +.previous + + .end --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/lib/Makefile 2004-09-03 00:57:17.366399168 -0700 @@ -0,0 +1,7 @@ +# +# Makefile for M32R-specific library files.. +# + +lib-y := checksum.o ashxdi3.o memset.o memcpy.o getuser.o \ + putuser.o delay.o strlen.o usercopy.o csum_partial_copy.o + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/lib/memcpy.S 2004-09-03 00:57:17.366399168 -0700 @@ -0,0 +1,95 @@ +/* + * linux/arch/m32r/lib/memcpy.S + * + * Copyright (C) 2001 Hiroyuki Kondo, and Hirokazu Takata + * Copyright (C) 2004 Hirokazu Takata + * + * void *memcopy(void *dst, const void *src, int n); + * + * dst: r0 + * src: r1 + * n : r2 + */ +/* $Id$ */ + + + .text +#include +#include +#include + +#ifdef CONFIG_ISA_DUAL_ISSUE + + .text +ENTRY(memcpy) +memcopy: + mv r4, r0 || mv r7, r0 + or r7, r1 || cmpz r2 + jc r14 || cmpeq r0, r1 ; return if r2=0 + jc r14 ; return if r0=r1 + + and3 r7, r7, #3 + bnez r7, byte_copy + srl3 r3, r2, #2 + and3 r2, r2, #3 + beqz r3, byte_copy + addi r4, #-4 +word_copy: + ld r7, @r1+ || addi r3, #-1 + st r7, @+r4 || cmpz r2 + bnez r3, word_copy + addi r4, #4 || jc r14 ; return if r2=0 +#if defined(CONFIG_ISA_M32R2) +byte_copy: + ldb r7, @r1 || addi r1, #1 + addi r2, #-1 || stb r7, @r4+ + bnez r2, byte_copy +#elif defined(CONFIG_ISA_M32R) +byte_copy: + ldb r7, @r1 || addi r1, #1 + addi r2, #-1 || stb r7, @r4 + addi r4, #1 + bnez r2, byte_copy +#else +#error unknown isa configuration +#endif +end_memcopy: + jmp r14 + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + + .text +ENTRY(memcpy) +memcopy: + mv r4, r0 + mv r7, r0 + or r7, r1 + beq r0, r1, end_memcopy + beqz r2, end_memcopy + + and3 r7, r7, #3 + bnez r7, byte_copy + srl3 r3, r2, #2 + and3 r2, r2, #3 + beqz r3, byte_copy + addi r4, #-4 +word_copy: + ld r7, @r1+ + addi r3, #-1 + st r7, @+r4 + bnez r3, word_copy + beqz r2, end_memcopy + addi r4, #4 +byte_copy: + ldb r7, @r1 + addi r1, #1 + addi r2, #-1 + stb r7, @r4 + addi r4, #1 + bnez r2, byte_copy +end_memcopy: + jmp r14 + +#endif /* not CONFIG_ISA_DUAL_ISSUE */ + + .end --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/lib/memset.S 2004-09-03 00:57:17.367399016 -0700 @@ -0,0 +1,178 @@ +/* + * linux/arch/m32r/lib/memset.S + * + * Copyright (C) 2001,2002 Hiroyuki Kondo, and Hirokazu Takata + * Copyright (C) 2004 Hirokazu Takata + * + * void *memset(void *dst, int val, int len); + * + * dst: r0 + * val: r1 + * len: r2 + * ret: r0 + * + */ +/* $Id$ */ + +#include + + .text + .global memset + +#ifdef CONFIG_ISA_DUAL_ISSUE + + .align 4 +memset: + mv r4, r0 || cmpz r2 + jc r14 + cmpui r2, #16 + bnc qword_align_check + cmpui r2, #4 + bc byte_set +word_align_check: /* len >= 4 */ + and3 r3, r4, #3 + beqz r3, word_set + addi r3, #-4 + neg r3, r3 /* r3 = -(r3 - 4) */ +align_word: + stb r1, @r4 || addi r4, #1 + addi r2, #-1 || addi r3, #-1 + bnez r3, align_word + cmpui r2, #4 + bc byte_set +word_set: + and3 r1, r1, #0x00ff /* r1: abababab <-- ??????ab */ + sll3 r3, r1, #8 + or r1, r3 || addi r4, #-4 + sll3 r3, r1, #16 + or r1, r3 || addi r2, #-4 +word_set_loop: + st r1, @+r4 || addi r2, #-4 + bgtz r2, word_set_loop + bnez r2, byte_set_wrap + st r1, @+r4 + jmp r14 + +qword_align_check: /* len >= 16 */ + and3 r3, r4, #15 + bnez r3, word_align_check +qword_set: + and3 r1, r1, #0x00ff /* r1: abababab <-- ??????ab */ + sll3 r3, r1, #8 + or r1, r3 || addi r4, #-4 + sll3 r3, r1, #16 + or r1, r3 || ldi r5, #16 +qword_set_loop: + ld r3, @(4,r4) /* cache line allocate */ + st r1, @+r4 || addi r2, #-16 + st r1, @+r4 || cmpu r2, r5 + st r1, @+r4 + st r1, @+r4 + bnc qword_set_loop || cmpz r2 + jc r14 +word_set_wrap: + cmpui r2, #4 + bc byte_set + addi r2, #-4 + bra word_set_loop + +byte_set_wrap: + addi r2, #4 + addi r4, #4 || cmpz r2 + jc r14 +#if defined(CONFIG_ISA_M32R2) +byte_set: + addi r2, #-1 || stb r1, @r4+ + bnez r2, byte_set +#elif defined(CONFIG_ISA_M32R) +byte_set: + addi r2, #-1 || stb r1, @r4 + addi r4, #1 + bnez r2, byte_set +#else +#error unknown isa configuration +#endif +end_memset: + jmp r14 + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + + .align 4 +memset: + mv r4, r0 + beqz r2, end_memset + cmpui r2, #16 + bnc qword_align_check + cmpui r2, #4 + bc byte_set +word_align_check: /* len >= 4 */ + and3 r3, r4, #3 + beqz r3, word_set + addi r3, #-4 + neg r3, r3 /* r3 = -(r3 - 4) */ +align_word: + stb r1, @r4 + addi r4, #1 + addi r2, #-1 + addi r3, #-1 + bnez r3, align_word + cmpui r2, #4 + bc byte_set +word_set: + and3 r1, r1, #0x00ff /* r1: abababab <-- ??????ab */ + sll3 r3, r1, #8 + or r1, r3 + sll3 r3, r1, #16 + or r1, r3 + addi r2, #-4 + addi r4, #-4 +word_set_loop: + st r1, @+r4 + addi r2, #-4 + bgtz r2, word_set_loop + bnez r2, byte_set_wrap + st r1, @+r4 + jmp r14 + +qword_align_check: /* len >= 16 */ + and3 r3, r4, #15 + bnez r3, word_align_check +qword_set: + and3 r1, r1, #0x00ff /* r1: abababab <-- ??????ab */ + sll3 r3, r1, #8 + or r1, r3 + sll3 r3, r1, #16 + or r1, r3 + addi r4, #-4 +qword_set_loop: + ld r3, @(4,r4) /* cache line allocate */ + addi r2, #-16 + st r1, @+r4 + st r1, @+r4 + cmpui r2, #16 + st r1, @+r4 + st r1, @+r4 + bnc qword_set_loop + bnez r2, word_set_wrap + jmp r14 +word_set_wrap: + cmpui r2, #4 + bc byte_set + addi r2, #-4 + bra word_set_loop + +byte_set_wrap: + addi r2, #4 + addi r4, #4 + beqz r2, end_memset +byte_set: + addi r2, #-1 + stb r1, @r4 + addi r4, #1 + bnez r2, byte_set +end_memset: + jmp r14 + +#endif /* not CONFIG_ISA_DUAL_ISSUE */ + + .end --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/lib/putuser.S 2004-09-03 00:57:17.367399016 -0700 @@ -0,0 +1,84 @@ +/* + * __put_user functions. + * + * (C) Copyright 1998 Linus Torvalds + * (C) Copyright 2001 Hirokazu Takata + * + * These functions have a non-standard call interface + * to make them more efficient. + */ + +#include + +/* + * __put_user_X + * + * Inputs: r0 contains the address + * r1 contains the value + * + * Outputs: r0 is error code (0 or -EFAULT) + * r1 is corrupted (will contain "current_task"). + * + * These functions should not modify any other registers, + * as they get called from within inline assembly. + */ + +#ifdef CONFIG_ISA_DUAL_ISSUE + + .text + .balign 4 + .globl __put_user_1 +__put_user_1: +1: stb r1, @r0 || ldi r0, #0 + jmp r14 + + .balign 4 + .globl __put_user_2 +__put_user_2: +2: sth r1, @r0 || ldi r0, #0 + jmp r14 + + .balign 4 + .globl __put_user_4 +__put_user_4: +3: st r1, @r0 || ldi r0, #0 + jmp r14 + +bad_put_user: + ldi r0, #-14 || jmp r14 + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + + .text + .balign 4 + .globl __put_user_1 +__put_user_1: +1: stb r1, @r0 + ldi r0, #0 + jmp r14 + + .balign 4 + .globl __put_user_2 +__put_user_2: +2: sth r1, @r0 + ldi r0, #0 + jmp r14 + + .balign 4 + .globl __put_user_4 +__put_user_4: +3: st r1, @r0 + ldi r0, #0 + jmp r14 + +bad_put_user: + ldi r0, #-14 + jmp r14 + +#endif /* not CONFIG_ISA_DUAL_ISSUE */ + +.section __ex_table,"a" + .long 1b,bad_put_user + .long 2b,bad_put_user + .long 3b,bad_put_user +.previous --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/lib/strlen.S 2004-09-03 00:57:17.368398864 -0700 @@ -0,0 +1,120 @@ +/* + * linux/arch/m32r/strlen.S -- strlen code. + * + * Copyright (C) 2001 Hirokazu Takata + * + * size_t strlen(const char *s); + * + */ +/* $Id$ */ + + +#include +#include +#include + +#ifdef CONFIG_ISA_DUAL_ISSUE + + .text +ENTRY(strlen) + mv r6, r0 || ldi r2, #0 + and3 r0, r0, #3 + bnez r0, strlen_byte +; +strlen_word: + ld r0, @r6+ +; + seth r5, #high(0x01010101) + or3 r5, r5, #low(0x01010101) + sll3 r7, r5, #7 +strlen_word_loop: + ld r1, @r6+ || not r4, r0 + sub r0, r5 || and r4, r7 + and r4, r0 + bnez r4, strlen_last_bytes + ld r0, @r6+ || not r4, r1 + sub r1, r5 || and r4, r7 + and r4, r1 || addi r2, #4 + bnez r4, strlen_last_bytes + addi r2, #4 || bra.s strlen_word_loop + + ; NOTE: If a null char. exists, return 0. + ; if ((x - 0x01010101) & ~x & 0x80808080) + ; return 0; +; +strlen_byte: + ldb r1, @r6 || addi r6, #1 + beqz r1, strlen_exit + addi r2, #1 || bra.s strlen_byte +; +strlen_last_bytes: + ldi r0, #4 || addi r6, #-8 +; +strlen_byte_loop: + ldb r1, @r6 || addi r6, #1 + addi r0, #-1 || cmpz r1 + bc.s strlen_exit || cmpz r0 + addi r2, #1 || bnc.s strlen_byte_loop +; +strlen_exit: + mv r0, r2 || jmp r14 + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + + .text +ENTRY(strlen) + mv r6, r0 + ldi r2, #0 + and3 r0, r0, #3 + bnez r0, strlen_byte +; +strlen_word: + ld r0, @r6+ +; + seth r5, #high(0x01010101) + or3 r5, r5, #low(0x01010101) + sll3 r7, r5, #7 +strlen_word_loop: + ld r1, @r6+ + not r4, r0 ; NOTE: If a null char. exists, return 0. + sub r0, r5 ; if ((x - 0x01010101) & ~x & 0x80808080) + and r4, r7 ; return 0; + and r4, r0 + bnez r4, strlen_last_bytes + addi r2, #4 +; + ld r0, @r6+ + not r4, r1 ; NOTE: If a null char. exists, return 0. + sub r1, r5 ; if ((x - 0x01010101) & ~x & 0x80808080) + and r4, r7 ; return 0; + and r4, r1 + bnez r4, strlen_last_bytes + addi r2, #4 + bra strlen_word_loop +; +strlen_byte: + ldb r1, @r6 + addi r6, #1 + beqz r1, strlen_exit + addi r2, #1 + bra strlen_byte +; +strlen_last_bytes: + ldi r0, #4 + addi r6, #-8 +; +strlen_byte_loop: + ldb r1, @r6 + addi r6, #1 + addi r0, #-1 + beqz r1, strlen_exit + addi r2, #1 + bnez r0, strlen_byte_loop +; +strlen_exit: + mv r0, r2 + jmp r14 + +#endif /* not CONFIG_ISA_DUAL_ISSUE */ + + .end --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/lib/usercopy.c 2004-09-03 00:57:17.370398560 -0700 @@ -0,0 +1,391 @@ +/* + * User address space access functions. + * The non inlined parts of asm-m32r/uaccess.h are here. + * + * Copyright 1997 Andi Kleen + * Copyright 1997 Linus Torvalds + * Copyright 2001, 2002, 2004 Hirokazu Takata + */ +#include +#include +#include +#include +#include + +unsigned long +__generic_copy_to_user(void *to, const void *from, unsigned long n) +{ + prefetch(from); + if (access_ok(VERIFY_WRITE, to, n)) + __copy_user(to,from,n); + return n; +} + +unsigned long +__generic_copy_from_user(void *to, const void *from, unsigned long n) +{ + prefetchw(to); + if (access_ok(VERIFY_READ, from, n)) + __copy_user_zeroing(to,from,n); + else + memset(to, 0, n); + return n; +} + + +/* + * Copy a null terminated string from userspace. + */ + +#ifdef CONFIG_ISA_DUAL_ISSUE + +#define __do_strncpy_from_user(dst,src,count,res) \ +do { \ + int __d0, __d1, __d2; \ + __asm__ __volatile__( \ + " beqz %1, 2f\n" \ + " .fillinsn\n" \ + "0: ldb r14, @%3 || addi %3, #1\n" \ + " stb r14, @%4 || addi %4, #1\n" \ + " beqz r14, 1f\n" \ + " addi %1, #-1\n" \ + " bnez %1, 0b\n" \ + " .fillinsn\n" \ + "1: sub %0, %1\n" \ + " .fillinsn\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + " .balign 4\n" \ + "3: seth r14, #high(2b)\n" \ + " or3 r14, r14, #low(2b)\n" \ + " jmp r14 || ldi %0, #%5\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 0b,3b\n" \ + ".previous" \ + : "=r"(res), "=r"(count), "=&r" (__d0), "=&r" (__d1), \ + "=&r" (__d2) \ + : "i"(-EFAULT), "0"(count), "1"(count), "3"(src), \ + "4"(dst) \ + : "r14", "cbit", "memory"); \ +} while (0) + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + +#define __do_strncpy_from_user(dst,src,count,res) \ +do { \ + int __d0, __d1, __d2; \ + __asm__ __volatile__( \ + " beqz %1, 2f\n" \ + " .fillinsn\n" \ + "0: ldb r14, @%3\n" \ + " stb r14, @%4\n" \ + " addi %3, #1\n" \ + " addi %4, #1\n" \ + " beqz r14, 1f\n" \ + " addi %1, #-1\n" \ + " bnez %1, 0b\n" \ + " .fillinsn\n" \ + "1: sub %0, %1\n" \ + " .fillinsn\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + " .balign 4\n" \ + "3: ldi %0, #%5\n" \ + " seth r14, #high(2b)\n" \ + " or3 r14, r14, #low(2b)\n" \ + " jmp r14\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 0b,3b\n" \ + ".previous" \ + : "=r"(res), "=r"(count), "=&r" (__d0), "=&r" (__d1), \ + "=&r" (__d2) \ + : "i"(-EFAULT), "0"(count), "1"(count), "3"(src), \ + "4"(dst) \ + : "r14", "cbit", "memory"); \ +} while (0) + +#endif /* CONFIG_ISA_DUAL_ISSUE */ + +long +__strncpy_from_user(char *dst, const char *src, long count) +{ + long res; + __do_strncpy_from_user(dst, src, count, res); + return res; +} + +long +strncpy_from_user(char *dst, const char *src, long count) +{ + long res = -EFAULT; + if (access_ok(VERIFY_READ, src, 1)) + __do_strncpy_from_user(dst, src, count, res); + return res; +} + + +/* + * Zero Userspace + */ + +#ifdef CONFIG_ISA_DUAL_ISSUE + +#define __do_clear_user(addr,size) \ +do { \ + int __dst, __c; \ + __asm__ __volatile__( \ + " beqz %1, 9f\n" \ + " and3 r14, %0, #3\n" \ + " bnez r14, 2f\n" \ + " and3 r14, %1, #3\n" \ + " bnez r14, 2f\n" \ + " and3 %1, %1, #3\n" \ + " beqz %2, 2f\n" \ + " addi %0, #-4\n" \ + " .fillinsn\n" \ + "0: ; word clear \n" \ + " st %6, @+%0 || addi %2, #-1\n" \ + " bnez %2, 0b\n" \ + " beqz %1, 9f\n" \ + " .fillinsn\n" \ + "2: ; byte clear \n" \ + " stb %6, @%0 || addi %1, #-1\n" \ + " addi %0, #1\n" \ + " bnez %1, 2b\n" \ + " .fillinsn\n" \ + "9:\n" \ + ".section .fixup,\"ax\"\n" \ + " .balign 4\n" \ + "4: slli %2, #2\n" \ + " seth r14, #high(9b)\n" \ + " or3 r14, r14, #low(9b)\n" \ + " jmp r14 || add %1, %2\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 0b,4b\n" \ + " .long 2b,9b\n" \ + ".previous\n" \ + : "=&r"(__dst), "=&r"(size), "=&r"(__c) \ + : "0"(addr), "1"(size), "2"(size / 4), "r"(0) \ + : "r14", "cbit", "memory"); \ +} while (0) + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + +#define __do_clear_user(addr,size) \ +do { \ + int __dst, __c; \ + __asm__ __volatile__( \ + " beqz %1, 9f\n" \ + " and3 r14, %0, #3\n" \ + " bnez r14, 2f\n" \ + " and3 r14, %1, #3\n" \ + " bnez r14, 2f\n" \ + " and3 %1, %1, #3\n" \ + " beqz %2, 2f\n" \ + " addi %0, #-4\n" \ + " .fillinsn\n" \ + "0: st %6, @+%0 ; word clear \n" \ + " addi %2, #-1\n" \ + " bnez %2, 0b\n" \ + " beqz %1, 9f\n" \ + " .fillinsn\n" \ + "2: stb %6, @%0 ; byte clear \n" \ + " addi %1, #-1\n" \ + " addi %0, #1\n" \ + " bnez %1, 2b\n" \ + " .fillinsn\n" \ + "9:\n" \ + ".section .fixup,\"ax\"\n" \ + " .balign 4\n" \ + "4: slli %2, #2\n" \ + " add %1, %2\n" \ + " seth r14, #high(9b)\n" \ + " or3 r14, r14, #low(9b)\n" \ + " jmp r14\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 0b,4b\n" \ + " .long 2b,9b\n" \ + ".previous\n" \ + : "=&r"(__dst), "=&r"(size), "=&r"(__c) \ + : "0"(addr), "1"(size), "2"(size / 4), "r"(0) \ + : "r14", "cbit", "memory"); \ +} while (0) + +#endif /* not CONFIG_ISA_DUAL_ISSUE */ + +unsigned long +clear_user(void *to, unsigned long n) +{ + if (access_ok(VERIFY_WRITE, to, n)) + __do_clear_user(to, n); + return n; +} + +unsigned long +__clear_user(void *to, unsigned long n) +{ + __do_clear_user(to, n); + return n; +} + +/* + * Return the size of a string (including the ending 0) + * + * Return 0 on exception, a value greater than N if too long + */ + +#ifdef CONFIG_ISA_DUAL_ISSUE + +long strnlen_user(const char *s, long n) +{ + unsigned long mask = -__addr_ok(s); + unsigned long res; + + __asm__ __volatile__( + " and %0, %5 || mv r1, %1\n" + " beqz %0, strnlen_exit\n" + " and3 r0, %1, #3\n" + " bnez r0, strnlen_byte_loop\n" + " cmpui %0, #4\n" + " bc strnlen_byte_loop\n" + "strnlen_word_loop:\n" + "0: ld r0, @%1+\n" + " pcmpbz r0\n" + " bc strnlen_last_bytes_fixup\n" + " addi %0, #-4\n" + " beqz %0, strnlen_exit\n" + " bgtz %0, strnlen_word_loop\n" + "strnlen_last_bytes:\n" + " mv %0, %4\n" + "strnlen_last_bytes_fixup:\n" + " addi %1, #-4\n" + "strnlen_byte_loop:\n" + "1: ldb r0, @%1 || addi %0, #-1\n" + " beqz r0, strnlen_exit\n" + " addi %1, #1\n" + " bnez %0, strnlen_byte_loop\n" + "strnlen_exit:\n" + " sub %1, r1\n" + " add3 %0, %1, #1\n" + " .fillinsn\n" + "9:\n" + ".section .fixup,\"ax\"\n" + " .balign 4\n" + "4: addi %1, #-4\n" + " .fillinsn\n" + "5: seth r1, #high(9b)\n" + " or3 r1, r1, #low(9b)\n" + " jmp r1 || ldi %0, #0\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .balign 4\n" + " .long 0b,4b\n" + " .long 1b,5b\n" + ".previous" + : "=&r" (res), "=r" (s) + : "0" (n), "1" (s), "r" (n & 3), "r" (mask), "r"(0x01010101) + : "r0", "r1", "cbit"); + + /* NOTE: strnlen_user() algorism: + * { + * char *p; + * for (p = s; n-- && *p != '\0'; ++p) + * ; + * return p - s + 1; + * } + */ + + /* NOTE: If a null char. exists, return 0. + * if ((x - 0x01010101) & ~x & 0x80808080)\n" + * return 0;\n" + */ + + return res & mask; +} + +#else /* not CONFIG_ISA_DUAL_ISSUE */ + +long strnlen_user(const char *s, long n) +{ + unsigned long mask = -__addr_ok(s); + unsigned long res; + + __asm__ __volatile__( + " and %0, %5\n" + " mv r1, %1\n" + " beqz %0, strnlen_exit\n" + " and3 r0, %1, #3\n" + " bnez r0, strnlen_byte_loop\n" + " cmpui %0, #4\n" + " bc strnlen_byte_loop\n" + " sll3 r3, %6, #7\n" + "strnlen_word_loop:\n" + "0: ld r0, @%1+\n" + " not r2, r0\n" + " sub r0, %6\n" + " and r2, r3\n" + " and r2, r0\n" + " bnez r2, strnlen_last_bytes_fixup\n" + " addi %0, #-4\n" + " beqz %0, strnlen_exit\n" + " bgtz %0, strnlen_word_loop\n" + "strnlen_last_bytes:\n" + " mv %0, %4\n" + "strnlen_last_bytes_fixup:\n" + " addi %1, #-4\n" + "strnlen_byte_loop:\n" + "1: ldb r0, @%1\n" + " addi %0, #-1\n" + " beqz r0, strnlen_exit\n" + " addi %1, #1\n" + " bnez %0, strnlen_byte_loop\n" + "strnlen_exit:\n" + " sub %1, r1\n" + " add3 %0, %1, #1\n" + " .fillinsn\n" + "9:\n" + ".section .fixup,\"ax\"\n" + " .balign 4\n" + "4: addi %1, #-4\n" + " .fillinsn\n" + "5: ldi %0, #0\n" + " seth r1, #high(9b)\n" + " or3 r1, r1, #low(9b)\n" + " jmp r1\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .balign 4\n" + " .long 0b,4b\n" + " .long 1b,5b\n" + ".previous" + : "=&r" (res), "=r" (s) + : "0" (n), "1" (s), "r" (n & 3), "r" (mask), "r"(0x01010101) + : "r0", "r1", "r2", "r3", "cbit"); + + /* NOTE: strnlen_user() algorism: + * { + * char *p; + * for (p = s; n-- && *p != '\0'; ++p) + * ; + * return p - s + 1; + * } + */ + + /* NOTE: If a null char. exists, return 0. + * if ((x - 0x01010101) & ~x & 0x80808080)\n" + * return 0;\n" + */ + + return res & mask; +} + +#endif /* CONFIG_ISA_DUAL_ISSUE */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/m32700ut/defconfig.m32700ut.smp 2004-09-03 00:57:18.727192296 -0700 @@ -0,0 +1,662 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_M32R=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y + +# +# General setup +# +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=15 +CONFIG_HOTPLUG=y +CONFIG_IKCONFIG=y +# CONFIG_IKCONFIG_PROC is not set +CONFIG_EMBEDDED=y +# CONFIG_KALLSYMS is not set +# CONFIG_FUTEX is not set +# CONFIG_EPOLL is not set +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y +CONFIG_STOP_MACHINE=y + +# +# Processor type and features +# +# CONFIG_PLAT_MAPPI is not set +# CONFIG_PLAT_USRV is not set +CONFIG_PLAT_M32700UT=y +# CONFIG_PLAT_OPSPUT is not set +# CONFIG_PLAT_OAKS32R is not set +# CONFIG_PLAT_MAPPI2 is not set +CONFIG_CHIP_M32700=y +# CONFIG_CHIP_M32102 is not set +# CONFIG_CHIP_VDEC2 is not set +# CONFIG_CHIP_OPSP is not set +CONFIG_MMU=y +CONFIG_TLB_ENTRIES=32 +CONFIG_ISA_M32R2=y +CONFIG_ISA_DSP_LEVEL2=y +CONFIG_ISA_DUAL_ISSUE=y +CONFIG_BUS_CLOCK=50000000 +CONFIG_TIMER_DIVIDE=128 +# CONFIG_CPU_LITTLE_ENDIAN is not set +CONFIG_MEMORY_START=0x08000000 +CONFIG_MEMORY_SIZE=0x01000000 +CONFIG_NOHIGHMEM=y +# CONFIG_DISCONTIGMEM is not set +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +CONFIG_PREEMPT=y +# CONFIG_HAVE_DEC_LOCK is not set +CONFIG_SMP=y +CONFIG_CHIP_M32700_TS1=y +CONFIG_NR_CPUS=2 +# CONFIG_NUMA is not set + +# +# M32R drivers +# +# CONFIG_M32RPCC is not set +CONFIG_M32R_CFC=y +CONFIG_M32700UT_CFC=y +CONFIG_CFC_NUM=1 +# CONFIG_MTD_M32R is not set +CONFIG_M32R_SMC91111=y +CONFIG_M32700UT_DS1302=y + +# +# Power management options (ACPI, APM) +# +# CONFIG_PM is not set + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +# CONFIG_PCI is not set +# CONFIG_ISA is not set + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=y +# CONFIG_PCMCIA_DEBUG is not set +# CONFIG_TCIC is not set + +# +# PCI Hotplug Support +# + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=y +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +CONFIG_BLK_DEV_IDECS=y +CONFIG_BLK_DEV_IDECD=m +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=y +# CONFIG_IDE_ARM is not set +# CONFIG_BLK_DEV_IDEDMA is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +CONFIG_SCSI=m +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=m +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=m +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_CHR_DEV_SG=m + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI Transport Attributes +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_DEBUG is not set + +# +# PCMCIA SCSI adapter support +# +# CONFIG_PCMCIA_AHA152X is not set +# CONFIG_PCMCIA_FDOMAIN is not set +# CONFIG_PCMCIA_NINJA_SCSI is not set +# CONFIG_PCMCIA_QLOGIC is not set +# CONFIG_PCMCIA_SYM53C500 is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_NETDEVICES is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_M32R_SIO is not set +CONFIG_SERIAL_M32R_PLDSIO=y +CONFIG_SERIAL_M32R_PLDSIO_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_QIC02_TAPE is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +CONFIG_VIDEO_DEV=y + +# +# Video For Linux +# + +# +# Video Adapters +# +# CONFIG_VIDEO_CPIA is not set +CONFIG_M32R_AR=y +CONFIG_M32R_AR_VGA=y + +# +# Radio Adapters +# +# CONFIG_RADIO_MAESTRO is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +CONFIG_FB=y +CONFIG_FB_EPSON_S1D13806=y +# CONFIG_FB_VIRTUAL is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +# CONFIG_MDA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y + +# +# Logo configuration +# +CONFIG_LOGO=y +CONFIG_LOGO_LINUX_MONO=y +CONFIG_LOGO_LINUX_VGA16=y +CONFIG_LOGO_LINUX_CLUT224=y +CONFIG_LOGO_M32R_CLUT224=y + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +CONFIG_EXT3_FS=m +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +CONFIG_JBD=m +CONFIG_JBD_DEBUG=y +CONFIG_FS_MBCACHE=y +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_REISERFS_FS_XATTR is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +# CONFIG_ZISOFS is not set +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=m +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_FRAME_POINTER is not set + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/m32700ut/defconfig.m32700ut.up 2004-09-03 00:57:18.728192144 -0700 @@ -0,0 +1,659 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_M32R=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_HOTPLUG=y +CONFIG_IKCONFIG=y +# CONFIG_IKCONFIG_PROC is not set +CONFIG_EMBEDDED=y +# CONFIG_KALLSYMS is not set +# CONFIG_FUTEX is not set +# CONFIG_EPOLL is not set +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# Processor type and features +# +# CONFIG_PLAT_MAPPI is not set +# CONFIG_PLAT_USRV is not set +CONFIG_PLAT_M32700UT=y +# CONFIG_PLAT_OPSPUT is not set +# CONFIG_PLAT_OAKS32R is not set +# CONFIG_PLAT_MAPPI2 is not set +CONFIG_CHIP_M32700=y +# CONFIG_CHIP_M32102 is not set +# CONFIG_CHIP_VDEC2 is not set +# CONFIG_CHIP_OPSP is not set +CONFIG_MMU=y +CONFIG_TLB_ENTRIES=32 +CONFIG_ISA_M32R2=y +CONFIG_ISA_DSP_LEVEL2=y +CONFIG_ISA_DUAL_ISSUE=y +CONFIG_BUS_CLOCK=50000000 +CONFIG_TIMER_DIVIDE=128 +# CONFIG_CPU_LITTLE_ENDIAN is not set +CONFIG_MEMORY_START=0x08000000 +CONFIG_MEMORY_SIZE=0x01000000 +CONFIG_NOHIGHMEM=y +# CONFIG_DISCONTIGMEM is not set +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +CONFIG_PREEMPT=y +# CONFIG_HAVE_DEC_LOCK is not set +# CONFIG_SMP is not set + +# +# M32R drivers +# +# CONFIG_M32RPCC is not set +CONFIG_M32R_CFC=y +CONFIG_M32700UT_CFC=y +CONFIG_CFC_NUM=1 +# CONFIG_MTD_M32R is not set +CONFIG_M32R_SMC91111=y +CONFIG_M32700UT_DS1302=y + +# +# Power management options (ACPI, APM) +# +# CONFIG_PM is not set + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +# CONFIG_PCI is not set +# CONFIG_ISA is not set + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=y +# CONFIG_PCMCIA_DEBUG is not set +# CONFIG_TCIC is not set + +# +# PCI Hotplug Support +# + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=y +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +CONFIG_BLK_DEV_IDECS=y +CONFIG_BLK_DEV_IDECD=m +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=y +# CONFIG_IDE_ARM is not set +# CONFIG_BLK_DEV_IDEDMA is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +CONFIG_SCSI=m +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=m +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=m +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_CHR_DEV_SG=m + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI Transport Attributes +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_DEBUG is not set + +# +# PCMCIA SCSI adapter support +# +# CONFIG_PCMCIA_AHA152X is not set +# CONFIG_PCMCIA_FDOMAIN is not set +# CONFIG_PCMCIA_NINJA_SCSI is not set +# CONFIG_PCMCIA_QLOGIC is not set +# CONFIG_PCMCIA_SYM53C500 is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_NETDEVICES is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_M32R_SIO is not set +CONFIG_SERIAL_M32R_PLDSIO=y +CONFIG_SERIAL_M32R_PLDSIO_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_QIC02_TAPE is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +CONFIG_VIDEO_DEV=y + +# +# Video For Linux +# + +# +# Video Adapters +# +# CONFIG_VIDEO_CPIA is not set +CONFIG_M32R_AR=y +CONFIG_M32R_AR_VGA=y + +# +# Radio Adapters +# +# CONFIG_RADIO_MAESTRO is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +CONFIG_FB=y +CONFIG_FB_EPSON_S1D13806=y +# CONFIG_FB_VIRTUAL is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +# CONFIG_MDA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y + +# +# Logo configuration +# +CONFIG_LOGO=y +CONFIG_LOGO_LINUX_MONO=y +CONFIG_LOGO_LINUX_VGA16=y +CONFIG_LOGO_LINUX_CLUT224=y +CONFIG_LOGO_M32R_CLUT224=y + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +CONFIG_EXT3_FS=m +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +CONFIG_JBD=m +CONFIG_JBD_DEBUG=y +CONFIG_FS_MBCACHE=y +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_REISERFS_FS_XATTR is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +# CONFIG_ZISOFS is not set +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=m +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_FRAME_POINTER is not set + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/m32700ut/dot.gdbinit_200MHz_16MB 2004-09-03 00:57:17.375397800 -0700 @@ -0,0 +1,249 @@ +# .gdbinit file +# $Id: dot.gdbinit_200MHz_16MB,v 1.1 2004/08/17 02:58:11 takata Exp $ +#----- +# NOTE: this file is generated by a script, "gen_gdbinit.pl". +# (Please type "gen_gdbinit.pl --help" and check the help message). +# $ Id: gen_gdbinit.pl,v 1.12 2004/07/26 09:56:10 takata Exp $ +#----- +# target platform: m32700ut + +# setting +set width 0d70 +set radix 0d16 + +debug_chaos + +# clk xin:cpu:bif:bus=25:200:50:50 +define clock_init + set *(unsigned long *)0x00ef4008 = 0x00000000 + set *(unsigned long *)0x00ef4004 = 0 + shell sleep 0.1 + # NOTE: Please change the master clock source from PLL-clock to Xin-clock + # and switch off PLL, before resetting the clock gear ratio. + + set *(unsigned long *)0x00ef4024 = 2 + set *(unsigned long *)0x00ef4020 = 2 + set *(unsigned long *)0x00ef4010 = 0 + set *(unsigned long *)0x00ef4014 = 0 + set *(unsigned long *)0x00ef4004 = 3 + shell sleep 0.1 + set *(unsigned long *)0x00ef4008 = 0x00000200 +end + +# Initialize SDRAM controller +define sdram_init + # SDIR0 + set *(unsigned long *)0x00ef6008 = 0x00000182 + # SDIR1 + set *(unsigned long *)0x00ef600c = 0x00000001 + # Initialize wait + shell sleep 0.1 + # Ch0-MOD + set *(unsigned long *)0x00ef602c = 0x00000020 + # Ch0-TR + set *(unsigned long *)0x00ef6028 = 0x00041302 + # Ch0-ADR (size:16MB) + set *(unsigned long *)0x00ef6020 = 0x08000002 + # AutoRef On + set *(unsigned long *)0x00ef6004 = 0x00010517 + # Access enable + set *(unsigned long *)0x00ef6024 = 0x00000001 +end +document sdram_init + SDRAM controller initialization + 0x08000000 - 0x08ffffff (16MB) +end + +# Initialize BSEL3 for UT-CFC +define cfc_init + set $sfrbase = 0xa0ef0000 +# too fast +# set *(unsigned long *)($sfrbase + 0x5300) = 0x0b0b8000 +# set *(unsigned long *)($sfrbase + 0x5304) = 0x00102204 +# set *(unsigned long *)($sfrbase + 0x5300) = 0x1f1f8000 +# set *(unsigned long *)($sfrbase + 0x5300) = 0x1f1f1fdf +# set *(unsigned long *)($sfrbase + 0x5304) = 0x0013220f +# set *(unsigned long *)($sfrbase + 0x5304) = 0x0013330f +end +document cfc_init + CF controller initialization +end + +# MMU enable +define mmu_enable + set $evb=0x88000000 + set *(unsigned long *)0xffff0024=1 +end + +# MMU disable +define mmu_disable + set $evb=0 + set *(unsigned long *)0xffff0024=0 +end + +# Show TLB entries +define show_tlb_entries + set $i = 0 + set $addr = $arg0 + set $nr_entries = $arg1 + use_mon_code + while ($i < $nr_entries) + set $tlb_tag = *(unsigned long*)$addr + set $tlb_data = *(unsigned long*)($addr + 4) + printf " [%2d] 0x%08lx : 0x%08lx - 0x%08lx\n", $i, $addr, $tlb_tag, $tlb_data + set $i = $i + 1 + set $addr = $addr + 8 + end + use_debug_dma +end +define itlb + set $itlb=0xfe000000 + show_tlb_entries $itlb 0d32 +end +define dtlb + set $dtlb=0xfe000800 + show_tlb_entries $dtlb 0d32 +end + +# Initialize TLB entries +define init_tlb_entries + set $i = 0 + set $addr = $arg0 + set $nr_entries = $arg1 + use_mon_code + while ($i < $nr_entries) + set *(unsigned long *)($addr + 0x4) = 0 + set $i = $i + 1 + set $addr = $addr + 8 + end + use_debug_dma +end +define tlb_init + set $itlb=0xfe000000 + init_tlb_entries $itlb 0d32 + set $dtlb=0xfe000800 + init_tlb_entries $dtlb 0d32 +end + +# Show current task structure +define show_current + set $current = $spi & 0xffffe000 + printf "$current=0x%08lX\n",$current + print *(struct task_struct *)$current +end + +# Show user assigned task structure +define show_task + set = $arg0 & 0xffffe000 + printf "$task=0x%08lX\n",$task + print *(struct task_struct *)$task +end +document show_task + Show user assigned task structure + arg0 : task structure address +end + +# Show M32R registers +define show_regs + printf " R0[0x%08lX] R1[0x%08lX] R2[0x%08lX] R3[0x%08lX]\n",$r0,$r1,$r2,$r3 + printf " R4[0x%08lX] R5[0x%08lX] R6[0x%08lX] R7[0x%08lX]\n",$r4,$r5,$r6,$r7 + printf " R8[0x%08lX] R9[0x%08lX] R10[0x%08lX] R11[0x%08lX]\n",$r8,$r9,$r10,$r11 + printf "R12[0x%08lX] FP[0x%08lX] LR[0x%08lX] SP[0x%08lX]\n",$r12,$fp,$lr,$sp + printf "PSW[0x%08lX] CBR[0x%08lX] SPI[0x%08lX] SPU[0x%08lX]\n",$psw,$cbr,$spi,$spu + printf "BPC[0x%08lX] PC[0x%08lX] ACCL[0x%08lX] ACCH[0x%08lX]\n",$bpc,$pc,$accl,$acch + printf "EVB[0x%08lX]\n",$evb +end + +# Setup all +define setup + use_mon_code + set *(unsigned int)0xfffffffc=0x60 + shell sleep 0.1 + clock_init + shell sleep 0.1 + # SDRAM: 16MB + set *(unsigned long *)0x00ef6020 = 0x08000002 + cfc_init + # USB + set *(unsigned short *)0xb0301000 = 0x100 + + set $evb=0x08000000 +end + +# Load modules +define load_modules + use_debug_dma + load +end + +# Set kernel parameters +define set_kernel_parameters + set $param = (void*)0x08002000 + # INITRD_START + set *(unsigned long *)($param + 0x0010) = 0x082a0000 + # INITRD_SIZE + set *(unsigned long *)($param + 0x0014) = 0x00000000 + # M32R_CPUCLK + set *(unsigned long *)($param + 0x0018) = 0d200000000 + # M32R_BUSCLK + set *(unsigned long *)($param + 0x001c) = 0d50000000 + + # M32R_TIMER_DIVIDE + set *(unsigned long *)($param + 0x0020) = 0d128 + + set {char[0x200]}($param + 0x100) = "console=ttyD0,115200n8x console=tty1 video=s1d13xxxfb:mode:240x320-16 root=/dev/nfsroot nfsroot=192.168.0.1:/project/m32r-linux/export/rootfs,rsize=1024,wsize=1024 nfsaddrs=192.168.0.101:192.168.0.1:192.168.0.1:255.255.255.0:mappi001 mem=16M \0" +end + +# Boot +define boot + set_kernel_parameters + set $fp = 0 + set $pc = 0x08001000 + set *(unsigned char *)0xffffffff = 0x03 + si + c +end + +# Set breakpoints +define set_breakpoints + b *0x08000030 +end + +# Restart +define restart + sdireset + sdireset + set $pc = 0 + b *0x04001000 + b *0x08001000 + b *0x08002000 + si + c + tlb_init + del + setup + load_modules + boot +end + +define si + stepi + x/i $pc + show_reg +end + +sdireset +sdireset +file vmlinux +target m32rsdi +set $pc = 0 +b *0x04001000 +b *0x08001000 +b *0x08002000 +c +tlb_init +del +setup +load_modules +boot + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/m32700ut/dot.gdbinit_300MHz_32MB 2004-09-03 00:57:17.376397648 -0700 @@ -0,0 +1,249 @@ +# .gdbinit file +# $Id: dot.gdbinit_300MHz_32MB,v 1.1 2004/08/17 02:58:11 takata Exp $ +#----- +# NOTE: this file is generated by a script, "gen_gdbinit.pl". +# (Please type "gen_gdbinit.pl --help" and check the help message). +# $ Id: gen_gdbinit.pl,v 1.12 2004/07/26 09:56:10 takata Exp $ +#----- +# target platform: m32700ut + +# setting +set width 0d70 +set radix 0d16 + +debug_chaos + +# clk xin:cpu:bif:bus=25:300:75:75 +define clock_init + set *(unsigned long *)0x00ef4008 = 0x00000000 + set *(unsigned long *)0x00ef4004 = 0 + shell sleep 0.1 + # NOTE: Please change the master clock source from PLL-clock to Xin-clock + # and switch off PLL, before resetting the clock gear ratio. + + set *(unsigned long *)0x00ef4024 = 2 + set *(unsigned long *)0x00ef4020 = 2 + set *(unsigned long *)0x00ef4010 = 0 + set *(unsigned long *)0x00ef4014 = 0 + set *(unsigned long *)0x00ef4004 = 5 + shell sleep 0.1 + set *(unsigned long *)0x00ef4008 = 0x00000200 +end + +# Initialize SDRAM controller +define sdram_init + # SDIR0 + set *(unsigned long *)0x00ef6008 = 0x00000182 + # SDIR1 + set *(unsigned long *)0x00ef600c = 0x00000001 + # Initialize wait + shell sleep 0.1 + # Ch0-MOD + set *(unsigned long *)0x00ef602c = 0x00000020 + # Ch0-TR + set *(unsigned long *)0x00ef6028 = 0x00051502 + # Ch0-ADR (size:16MB) + set *(unsigned long *)0x00ef6020 = 0x08000002 + # AutoRef On + set *(unsigned long *)0x00ef6004 = 0x00010e24 + # Access enable + set *(unsigned long *)0x00ef6024 = 0x00000001 +end +document sdram_init + SDRAM controller initialization + 0x08000000 - 0x08ffffff (16MB) +end + +# Initialize BSEL3 for UT-CFC +define cfc_init + set $sfrbase = 0xa0ef0000 +# too fast +# set *(unsigned long *)($sfrbase + 0x5300) = 0x0b0b8000 +# set *(unsigned long *)($sfrbase + 0x5304) = 0x00102204 +# set *(unsigned long *)($sfrbase + 0x5300) = 0x1f1f8000 +# set *(unsigned long *)($sfrbase + 0x5300) = 0x1f1f1fdf +# set *(unsigned long *)($sfrbase + 0x5304) = 0x0013220f +# set *(unsigned long *)($sfrbase + 0x5304) = 0x0013330f +end +document cfc_init + CF controller initialization +end + +# MMU enable +define mmu_enable + set $evb=0x88000000 + set *(unsigned long *)0xffff0024=1 +end + +# MMU disable +define mmu_disable + set $evb=0 + set *(unsigned long *)0xffff0024=0 +end + +# Show TLB entries +define show_tlb_entries + set $i = 0 + set $addr = $arg0 + set $nr_entries = $arg1 + use_mon_code + while ($i < $nr_entries) + set $tlb_tag = *(unsigned long*)$addr + set $tlb_data = *(unsigned long*)($addr + 4) + printf " [%2d] 0x%08lx : 0x%08lx - 0x%08lx\n", $i, $addr, $tlb_tag, $tlb_data + set $i = $i + 1 + set $addr = $addr + 8 + end + use_debug_dma +end +define itlb + set $itlb=0xfe000000 + show_tlb_entries $itlb 0d32 +end +define dtlb + set $dtlb=0xfe000800 + show_tlb_entries $dtlb 0d32 +end + +# Initialize TLB entries +define init_tlb_entries + set $i = 0 + set $addr = $arg0 + set $nr_entries = $arg1 + use_mon_code + while ($i < $nr_entries) + set *(unsigned long *)($addr + 0x4) = 0 + set $i = $i + 1 + set $addr = $addr + 8 + end + use_debug_dma +end +define tlb_init + set $itlb=0xfe000000 + init_tlb_entries $itlb 0d32 + set $dtlb=0xfe000800 + init_tlb_entries $dtlb 0d32 +end + +# Show current task structure +define show_current + set $current = $spi & 0xffffe000 + printf "$current=0x%08lX\n",$current + print *(struct task_struct *)$current +end + +# Show user assigned task structure +define show_task + set = $arg0 & 0xffffe000 + printf "$task=0x%08lX\n",$task + print *(struct task_struct *)$task +end +document show_task + Show user assigned task structure + arg0 : task structure address +end + +# Show M32R registers +define show_regs + printf " R0[0x%08lX] R1[0x%08lX] R2[0x%08lX] R3[0x%08lX]\n",$r0,$r1,$r2,$r3 + printf " R4[0x%08lX] R5[0x%08lX] R6[0x%08lX] R7[0x%08lX]\n",$r4,$r5,$r6,$r7 + printf " R8[0x%08lX] R9[0x%08lX] R10[0x%08lX] R11[0x%08lX]\n",$r8,$r9,$r10,$r11 + printf "R12[0x%08lX] FP[0x%08lX] LR[0x%08lX] SP[0x%08lX]\n",$r12,$fp,$lr,$sp + printf "PSW[0x%08lX] CBR[0x%08lX] SPI[0x%08lX] SPU[0x%08lX]\n",$psw,$cbr,$spi,$spu + printf "BPC[0x%08lX] PC[0x%08lX] ACCL[0x%08lX] ACCH[0x%08lX]\n",$bpc,$pc,$accl,$acch + printf "EVB[0x%08lX]\n",$evb +end + +# Setup all +define setup + use_mon_code + set *(unsigned int)0xfffffffc=0x60 + shell sleep 0.1 + clock_init + shell sleep 0.1 + # SDRAM: 16MB + set *(unsigned long *)0x00ef6020 = 0x08000002 + cfc_init + # USB + set *(unsigned short *)0xb0301000 = 0x100 + + set $evb=0x08000000 +end + +# Load modules +define load_modules + use_debug_dma + load +end + +# Set kernel parameters +define set_kernel_parameters + set $param = (void*)0x08002000 + # INITRD_START + set *(unsigned long *)($param + 0x0010) = 0x082a0000 + # INITRD_SIZE + set *(unsigned long *)($param + 0x0014) = 0x00000000 + # M32R_CPUCLK + set *(unsigned long *)($param + 0x0018) = 0d300000000 + # M32R_BUSCLK + set *(unsigned long *)($param + 0x001c) = 0d75000000 + + # M32R_TIMER_DIVIDE + set *(unsigned long *)($param + 0x0020) = 0d128 + + set {char[0x200]}($param + 0x100) = "console=ttyD0,115200n8x console=tty1 video=s1d13xxxfb:mode:240x320-16 root=/dev/nfsroot nfsroot=192.168.0.1:/project/m32r-linux/export/rootfs,rsize=1024,wsize=1024 nfsaddrs=192.168.0.101:192.168.0.1:192.168.0.1:255.255.255.0:mappi001 mem=16M \0" +end + +# Boot +define boot + set_kernel_parameters + set $fp = 0 + set $pc = 0x08001000 + set *(unsigned char *)0xffffffff = 0x03 + si + c +end + +# Set breakpoints +define set_breakpoints + b *0x08000030 +end + +# Restart +define restart + sdireset + sdireset + set $pc = 0 + b *0x04001000 + b *0x08001000 + b *0x08002000 + si + c + tlb_init + del + setup + load_modules + boot +end + +define si + stepi + x/i $pc + show_reg +end + +sdireset +sdireset +file vmlinux +target m32rsdi +set $pc = 0 +b *0x04001000 +b *0x08001000 +b *0x08002000 +c +tlb_init +del +setup +load_modules +boot + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/m32700ut/m32r-flash.c 2004-09-03 00:57:17.377397496 -0700 @@ -0,0 +1,227 @@ +/* + * Flash memory access on M32R based devices + * + * Copyright (C) 2003 Takeo Takahashi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * $Id$ + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define WINDOW_ADDR (0xa0000000) /* start of flash memory */ + +static __u8 m32r_read8(struct map_info *map, unsigned long ofs) +{ + return readb(map->map_priv_1 + ofs); +} + +static __u16 m32r_read16(struct map_info *map, unsigned long ofs) +{ + return readw(map->map_priv_1 + ofs); +} + +static __u32 m32r_read32(struct map_info *map, unsigned long ofs) +{ + return readl(map->map_priv_1 + ofs); +} + +static void m32r_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy(to, (void *)(map->map_priv_1 + from), len); +} + +static void m32r_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + writeb(d, map->map_priv_1 + adr); +} + +static void m32r_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + writew(d, map->map_priv_1 + adr); +} + +static void m32r_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + writel(d, map->map_priv_1 + adr); +} + +static void m32r_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy((void *)(map->map_priv_1 + to), from, len); +} + +static struct map_info m32r_map = { + name: "M32R flash", + read8: m32r_read8, + read16: m32r_read16, + read32: m32r_read32, + copy_from: m32r_copy_from, + write8: m32r_write8, + write16: m32r_write16, + write32: m32r_write32, + copy_to: m32r_copy_to, + + map_priv_1: WINDOW_ADDR, + map_priv_2: -1, +}; + +#ifdef CONFIG_PLAT_M32700UT +#define M32700UT_FLASH_SIZE 0x00400000 +static struct mtd_partition m32700ut_partitions[] = { + { + name: "M32700UT boot firmware", + size: 0x30000, /* 192KB */ + offset: 0, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "M32700UT kernel", + size: 0xd0000, /* 832KB */ + offset: MTDPART_OFS_APPEND, + }, { + name: "M32700UT root", + size: 0x2f0000, /* 3008KB */ + offset: MTDPART_OFS_APPEND, + }, { + name: "M32700UT params", + size: MTDPART_SIZ_FULL, /* 64KB */ + offset: MTDPART_OFS_APPEND, + } +}; +#endif + +extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); +extern int parse_bootldr_partitions(struct mtd_info *master, struct mtd_partition **pparts); + +static struct mtd_partition *parsed_parts; +static struct mtd_info *mymtd; + +int __init m32r_mtd_init(void) +{ + struct mtd_partition *parts; + int nb_parts = 0, ret; + int parsed_nr_parts = 0; + const char *part_type; + unsigned long base = -1UL; + + + /* Default flash buswidth */ + m32r_map.buswidth = 2; + + /* + * Static partition definition selection + */ + part_type = "static"; + +#ifdef CONFIG_PLAT_M32700UT + parts = m32700ut_partitions; + nb_parts = ARRAY_SIZE(m32700ut_partitions); + m32r_map.size = M32700UT_FLASH_SIZE; + m32r_map.buswidth = 2; +#endif + + /* + * For simple flash devices, use ioremap to map the flash. + */ + if (base != (unsigned long)-1) { + if (!request_mem_region(base, m32r_map.size, "flash")) + return -EBUSY; + m32r_map.map_priv_2 = base; + m32r_map.map_priv_1 = (unsigned long) + ioremap(base, m32r_map.size); + ret = -ENOMEM; + if (!m32r_map.map_priv_1) + goto out_err; + } + + /* + * Now let's probe for the actual flash. Do it here since + * specific machine settings might have been set above. + */ + printk(KERN_NOTICE "M32R flash: probing %d-bit flash bus\n", m32r_map.buswidth*8); + mymtd = do_map_probe("m5drv", &m32r_map); + ret = -ENXIO; + if (!mymtd) + goto out_err; + mymtd->module = THIS_MODULE; + + /* + * Dynamic partition selection stuff (might override the static ones) + */ +#ifdef CONFIG_MTD_REDBOOT_PARTS + if (parsed_nr_parts == 0) { + ret = parse_redboot_partitions(mymtd, &parsed_parts); + + if (ret > 0) { + part_type = "RedBoot"; + parsed_nr_parts = ret; + } + } +#endif +#ifdef CONFIG_MTD_BOOTLDR_PARTS + if (parsed_nr_parts == 0) { + ret = parse_bootldr_partitions(mymtd, &parsed_parts); + if (ret > 0) { + part_type = "Compaq bootldr"; + parsed_nr_parts = ret; + } + } +#endif + + if (parsed_nr_parts > 0) { + parts = parsed_parts; + nb_parts = parsed_nr_parts; + } + + if (nb_parts == 0) { + printk(KERN_NOTICE "M32R flash: no partition info available, registering whole flash at once\n"); + add_mtd_device(mymtd); + } else { + printk(KERN_NOTICE "Using %s partition definition\n", part_type); + add_mtd_partitions(mymtd, parts, nb_parts); + } + return 0; + + out_err: + if (m32r_map.map_priv_2 != -1) { + iounmap((void *)m32r_map.map_priv_1); + release_mem_region(m32r_map.map_priv_2, m32r_map.size); + } + return ret; +} + +static void __exit m32r_mtd_cleanup(void) +{ + if (mymtd) { + del_mtd_partitions(mymtd); + map_destroy(mymtd); + if (parsed_parts) + kfree(parsed_parts); + } + if (m32r_map.map_priv_2 != -1) { + iounmap((void *)m32r_map.map_priv_1); + release_mem_region(m32r_map.map_priv_2, m32r_map.size); + } +} + +module_init(m32r_mtd_init); +module_exit(m32r_mtd_cleanup); + +MODULE_AUTHOR("Takeo Takahashi"); +MODULE_DESCRIPTION("M32R Flash map driver"); +MODULE_LICENSE("GPL"); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/Makefile 2004-09-03 00:57:19.065140920 -0700 @@ -0,0 +1,56 @@ +# +# m32r/Makefile +# + +LDFLAGS := +OBJCOPYFLAGS := -O binary -R .note -R .comment -S +LDFLAGS_vmlinux := -e startup_32 +LDFLAGS_BLOB := --format binary --oformat elf32-m32r + +CFLAGS += -pipe -fno-schedule-insns +CFLAGS_KERNEL += -mmodel=medium +CFLAGS_MODULE += -mmodel=large + +ifdef CONFIG_CHIP_VDEC2 +cflags-$(CONFIG_ISA_M32R2) += -DNO_FPU -Wa,-bitinst +aflags-$(CONFIG_ISA_M32R2) += -DNO_FPU -Wa,-bitinst +else +cflags-$(CONFIG_ISA_M32R2) += -DNO_FPU -m32r2 +aflags-$(CONFIG_ISA_M32R2) += -DNO_FPU -m32r2 +endif + +cflags-$(CONFIG_ISA_M32R) += -DNO_FPU +aflags-$(CONFIG_ISA_M32R) += -DNO_FPU -Wa,-no-bitinst + +CFLAGS += $(cflags-y) +AFLAGS += $(aflags-y) + +CHECK := $(CHECK) -D__m32r__=1 + +head-y := arch/m32r/kernel/head.o arch/m32r/kernel/init_task.o + +LIBGCC := $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) + +libs-y += arch/m32r/lib/ $(LIBGCC) +core-y += arch/m32r/kernel/ \ + arch/m32r/mm/ \ + arch/m32r/boot/ + +drivers-y += arch/m32r/drivers/ +drivers-$(CONFIG_OPROFILE) += arch/m32r/oprofile/ + +boot := arch/m32r/boot + +.PHONY: zImage + +zImage: vmlinux + $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ + +compressed: zImage + +archclean: + $(Q)$(MAKE) $(clean)=$(boot) + +define archhelp + @echo ' zImage - Compressed kernel image (arch/m32r/boot/zImage)' +endef --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/mappi/defconfig.nommu 2004-09-03 00:57:18.729191992 -0700 @@ -0,0 +1,529 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_M32R=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_HOTPLUG=y +CONFIG_IKCONFIG=y +# CONFIG_IKCONFIG_PROC is not set +CONFIG_EMBEDDED=y +# CONFIG_KALLSYMS is not set +# CONFIG_FUTEX is not set +# CONFIG_EPOLL is not set +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# Processor type and features +# +CONFIG_PLAT_MAPPI=y +# CONFIG_PLAT_USRV is not set +# CONFIG_PLAT_M32700UT is not set +# CONFIG_PLAT_OPSPUT is not set +# CONFIG_PLAT_OAKS32R is not set +# CONFIG_PLAT_MAPPI2 is not set +CONFIG_CHIP_M32700=y +# CONFIG_CHIP_M32102 is not set +# CONFIG_CHIP_VDEC2 is not set +# CONFIG_CHIP_OPSP is not set +# CONFIG_MMU is not set +CONFIG_TLB_ENTRIES=32 +CONFIG_ISA_M32R2=y +CONFIG_ISA_DSP_LEVEL2=y +CONFIG_ISA_DUAL_ISSUE=y +CONFIG_BUS_CLOCK=50000000 +CONFIG_TIMER_DIVIDE=128 +# CONFIG_CPU_LITTLE_ENDIAN is not set +CONFIG_MEMORY_START=0x00000000 +CONFIG_MEMORY_SIZE=0x00E00000 +CONFIG_NOHIGHMEM=y +# CONFIG_DISCONTIGMEM is not set +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +CONFIG_PREEMPT=y +# CONFIG_HAVE_DEC_LOCK is not set +# CONFIG_SMP is not set + +# +# M32R drivers +# +# CONFIG_M32RPCC is not set +CONFIG_M32R_NE2000=y + +# +# Power management options (ACPI, APM) +# +# CONFIG_PM is not set + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +# CONFIG_PCI is not set +# CONFIG_ISA is not set + +# +# PCMCIA/CardBus support +# +# CONFIG_PCMCIA is not set + +# +# PCI Hotplug Support +# + +# +# Executable file formats +# +CONFIG_BINFMT_FLAT=y +# CONFIG_BINFMT_ZFLAT is not set +# CONFIG_BINFMT_SHARED_FLAT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set + +# +# ATA/ATAPI/MFM/RLL support +# +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_SCSI is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_NETDEVICES is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_M32R_SIO=y +CONFIG_SERIAL_M32R_SIO_CONSOLE=y +# CONFIG_SERIAL_M32R_PLDSIO is not set +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_QIC02_TAPE is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_AGP is not set +# CONFIG_DRM is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS_XATTR=y +CONFIG_DEVPTS_FS_SECURITY=y +# CONFIG_TMPFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_FRAME_POINTER is not set + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/mappi/defconfig.smp 2004-09-03 00:57:18.731191688 -0700 @@ -0,0 +1,646 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_M32R=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +# CONFIG_CLEAN_COMPILE is not set +CONFIG_BROKEN=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=15 +CONFIG_HOTPLUG=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_EMBEDDED=y +# CONFIG_KALLSYMS is not set +# CONFIG_FUTEX is not set +# CONFIG_EPOLL is not set +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y +CONFIG_STOP_MACHINE=y + +# +# Processor type and features +# +CONFIG_PLAT_MAPPI=y +# CONFIG_PLAT_USRV is not set +# CONFIG_PLAT_M32700UT is not set +# CONFIG_PLAT_OPSPUT is not set +# CONFIG_PLAT_OAKS32R is not set +# CONFIG_PLAT_MAPPI2 is not set +CONFIG_CHIP_M32700=y +# CONFIG_CHIP_M32102 is not set +# CONFIG_CHIP_VDEC2 is not set +# CONFIG_CHIP_OPSP is not set +CONFIG_MMU=y +CONFIG_TLB_ENTRIES=32 +CONFIG_ISA_M32R2=y +CONFIG_ISA_DSP_LEVEL2=y +CONFIG_ISA_DUAL_ISSUE=y +CONFIG_BUS_CLOCK=10000000 +CONFIG_TIMER_DIVIDE=128 +# CONFIG_CPU_LITTLE_ENDIAN is not set +CONFIG_MEMORY_START=0x08000000 +CONFIG_MEMORY_SIZE=0x04000000 +CONFIG_NOHIGHMEM=y +CONFIG_DISCONTIGMEM=y +CONFIG_IRAM_START=0x00f00000 +CONFIG_IRAM_SIZE=0x00080000 +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +CONFIG_PREEMPT=y +# CONFIG_HAVE_DEC_LOCK is not set +CONFIG_SMP=y +CONFIG_CHIP_M32700_TS1=y +CONFIG_NR_CPUS=2 +# CONFIG_NUMA is not set + +# +# M32R drivers +# +CONFIG_M32RPCC=y +CONFIG_M32R_NE2000=y + +# +# Power management options (ACPI, APM) +# +# CONFIG_PM is not set + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +# CONFIG_PCI is not set +# CONFIG_ISA is not set + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=y +# CONFIG_PCMCIA_DEBUG is not set +# CONFIG_TCIC is not set + +# +# PCI Hotplug Support +# + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +# CONFIG_STANDALONE is not set +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_REDBOOT_PARTS=y +# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set +# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set +# CONFIG_MTD_CMDLINE_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_OBSOLETE_CHIPS is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLKMTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set + +# +# NAND Flash Device Drivers +# +# CONFIG_MTD_NAND is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_NBD=m +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=m +CONFIG_BLK_DEV_IDE=m + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_IDEDISK=m +# CONFIG_IDEDISK_MULTI_MODE is not set +CONFIG_BLK_DEV_IDECS=m +CONFIG_BLK_DEV_IDECD=m +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=m +# CONFIG_IDE_ARM is not set +# CONFIG_BLK_DEV_IDEDMA is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +# CONFIG_SCSI is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_NETDEVICES is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +# CONFIG_SERIO_SERPORT is not set +# CONFIG_SERIO_CT82C710 is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_M32R_SIO=y +CONFIG_SERIAL_M32R_SIO_CONSOLE=y +# CONFIG_SERIAL_M32R_PLDSIO is not set +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_QIC02_TAPE is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +CONFIG_ROMFS_FS=y +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=m +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_HUGETLBFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_JFFS_FS=y +CONFIG_JFFS_FS_VERBOSE=0 +CONFIG_JFFS_PROC_FS=y +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_NAND is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_FRAME_POINTER is not set + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/mappi/defconfig.up 2004-09-03 00:57:18.732191536 -0700 @@ -0,0 +1,642 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_M32R=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +# CONFIG_CLEAN_COMPILE is not set +CONFIG_BROKEN=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_HOTPLUG=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_EMBEDDED=y +# CONFIG_KALLSYMS is not set +# CONFIG_FUTEX is not set +# CONFIG_EPOLL is not set +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# Processor type and features +# +CONFIG_PLAT_MAPPI=y +# CONFIG_PLAT_USRV is not set +# CONFIG_PLAT_M32700UT is not set +# CONFIG_PLAT_OPSPUT is not set +# CONFIG_PLAT_OAKS32R is not set +# CONFIG_PLAT_MAPPI2 is not set +CONFIG_CHIP_M32700=y +# CONFIG_CHIP_M32102 is not set +# CONFIG_CHIP_VDEC2 is not set +# CONFIG_CHIP_OPSP is not set +CONFIG_MMU=y +CONFIG_TLB_ENTRIES=32 +CONFIG_ISA_M32R2=y +CONFIG_ISA_DSP_LEVEL2=y +CONFIG_ISA_DUAL_ISSUE=y +CONFIG_BUS_CLOCK=10000000 +CONFIG_TIMER_DIVIDE=128 +# CONFIG_CPU_LITTLE_ENDIAN is not set +CONFIG_MEMORY_START=0x08000000 +CONFIG_MEMORY_SIZE=0x04000000 +CONFIG_NOHIGHMEM=y +CONFIG_DISCONTIGMEM=y +CONFIG_IRAM_START=0x00f00000 +CONFIG_IRAM_SIZE=0x00080000 +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +CONFIG_PREEMPT=y +# CONFIG_HAVE_DEC_LOCK is not set +# CONFIG_SMP is not set + +# +# M32R drivers +# +CONFIG_M32RPCC=y +CONFIG_M32R_NE2000=y + +# +# Power management options (ACPI, APM) +# +# CONFIG_PM is not set + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +# CONFIG_PCI is not set +# CONFIG_ISA is not set + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=y +# CONFIG_PCMCIA_DEBUG is not set +# CONFIG_TCIC is not set + +# +# PCI Hotplug Support +# + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +# CONFIG_STANDALONE is not set +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_REDBOOT_PARTS=y +# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set +# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set +# CONFIG_MTD_CMDLINE_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_OBSOLETE_CHIPS is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLKMTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set + +# +# NAND Flash Device Drivers +# +# CONFIG_MTD_NAND is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_NBD=m +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=m +CONFIG_BLK_DEV_IDE=m + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_IDEDISK=m +# CONFIG_IDEDISK_MULTI_MODE is not set +CONFIG_BLK_DEV_IDECS=m +CONFIG_BLK_DEV_IDECD=m +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=m +# CONFIG_IDE_ARM is not set +# CONFIG_BLK_DEV_IDEDMA is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +# CONFIG_SCSI is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_NETDEVICES is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +# CONFIG_SERIO_SERPORT is not set +# CONFIG_SERIO_CT82C710 is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_M32R_SIO=y +CONFIG_SERIAL_M32R_SIO_CONSOLE=y +# CONFIG_SERIAL_M32R_PLDSIO is not set +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_QIC02_TAPE is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +CONFIG_ROMFS_FS=y +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=m +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_HUGETLBFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_JFFS_FS=y +CONFIG_JFFS_FS_VERBOSE=0 +CONFIG_JFFS_PROC_FS=y +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_NAND is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_FRAME_POINTER is not set + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/mappi/dot.gdbinit 2004-09-03 00:57:17.385396280 -0700 @@ -0,0 +1,242 @@ +# .gdbinit file +# $Id$ +#----- +# NOTE: this file is generated by a script, "gen_gdbinit.pl". +# (Please type "gen_gdbinit.pl --help" and check the help message). +# $ Id: gen_gdbinit.pl,v 1.8 2004/02/27 07:08:32 takata Exp $ +#----- +# target platform: mappi + +# setting +set width 0d70 +set radix 0d16 +debug_chaos + +# clk xin:cpu:bif:bus=30:360:180:90 +define clock_init + set *(unsigned long *)0x00ef4024 = 2 + set *(unsigned long *)0x00ef4020 = 1 + set *(unsigned long *)0x00ef4010 = 0 + set *(unsigned long *)0x00ef4014 = 0 + set *(unsigned long *)0x00ef4004 = 5 + shell sleep 0.1 + set *(unsigned long *)0x00ef4008 = 0x00000200 +end + +# Initialize programmable ports +define port_init + set $sfrbase = 0x00ef0000 + set *(unsigned short *)0x00ef1060 = 0x5555 + set *(unsigned short *)0x00ef1062 = 0x5555 + set *(unsigned short *)0x00ef1064 = 0x5555 + set *(unsigned short *)0x00ef1066 = 0x5555 + set *(unsigned short *)0x00ef1068 = 0x5555 + set *(unsigned short *)0x00ef106a = 0x0000 + set *(unsigned short *)0x00ef106e = 0x5555 + set *(unsigned short *)0x00ef1070 = 0x5555 + # LED ON + set *(unsigned char *)($sfrbase + 0x1015) = 0xff + set *(unsigned char *)($sfrbase + 0x1085) = 0xff + shell sleep 0.1 + # LED OFF + set *(unsigned char *)($sfrbase + 0x1085) = 0x00 +end +document port_init + P5=LED(output), P6.b4=LAN_RESET(output) +end + +# Initialize SDRAM controller +define sdram_init + # SDIR0 + set *(unsigned long *)0x00ef6008 = 0x00000182 + # SDIR1 + set *(unsigned long *)0x00ef600c = 0x00000001 + # Initialize wait + shell sleep 0.1 + # Ch0-MOD + set *(unsigned long *)0x00ef602c = 0x00000020 + # Ch0-TR + set *(unsigned long *)0x00ef6028 = 0x00051502 + # Ch0-ADR (size:64MB) + set *(unsigned long *)0x00ef6020 = 0x08000004 + # AutoRef On + set *(unsigned long *)0x00ef6004 = 0x00010e2b + # Access enable + set *(unsigned long *)0x00ef6024 = 0x00000001 +end +document sdram_init + SDRAM controller initialization + 0x08000000 - 0x0bffffff (64MB) +end + +# Initialize LAN controller +define lanc_init + set $sfrbase = 0x00ef0000 + # Set BSEL3 (BSEL3 for the Chaos's bselc) + set *(unsigned long *)($sfrbase + 0x5300) = 0x0a0a8040 + set *(unsigned long *)($sfrbase + 0x5304) = 0x01120203 + set *(unsigned long *)($sfrbase + 0x5308) = 0x00000001 + # Reset (P5=LED,P6.b4=LAN_RESET) + set *(unsigned short *)($sfrbase + 0x106c) = 0x0000 + set *(unsigned char *)($sfrbase + 0x1016) = 0xff + set *(unsigned char *)($sfrbase + 0x1086) = 0xff + shell sleep 0.1 + # swivel: 0=normal, 4=reverse +# set *(unsigned char *)($sfrbase + 0x1086) = 0x00 + set *(unsigned char *)($sfrbase + 0x1086) = 0x04 + set *(unsigned long *)(0x0c000330) = 0xffffffff + # Set mac address + set $lanc = (void*)0x0c000300 + set *(unsigned long *)($lanc + 0x0000) = 0x00610010 + set *(unsigned long *)($lanc + 0x0004) = 0x00200030 + set *(unsigned long *)($lanc + 0x0008) = 0x00400050 + set *(unsigned long *)($lanc + 0x000c) = 0x00600007 +end +document lanc_init + LAN controller initialization + ex.) MAC address: 10 20 30 40 50 60 +end + +# LCD & CRT dual-head setting (8bpp) +define dispc_init + set $sfrbase = 0x00ef0000 + # BSEL4 Dispc + set *(unsigned long *)($sfrbase + 0x5400) = 0x0e0e8000 + set *(unsigned long *)($sfrbase + 0x5404) = 0x0012220a +end + +# MMU enable +define mmu_enable + set $evb=0x88000000 + set *(unsigned long *)0xffff0024=1 +end + +# MMU disable +define mmu_disable + set $evb=0 + set *(unsigned long *)0xffff0024=0 +end + +# Show TLB entries +define show_tlb_entries + set $i = 0 + set $addr = $arg0 + set $nr_entries = $arg1 + use_mon_code + while ($i < $nr_entries) + set $tlb_tag = *(unsigned long*)$addr + set $tlb_data = *(unsigned long*)($addr + 4) + printf " [%2d] 0x%08lx : 0x%08lx - 0x%08lx\n", $i, $addr, $tlb_tag, $tlb_data + set $i = $i + 1 + set $addr = $addr + 8 + end + use_debug_dma +end +define itlb + set $itlb=0xfe000000 + show_tlb_entries $itlb 0d32 +end +define dtlb + set $dtlb=0xfe000800 + show_tlb_entries $dtlb 0d32 +end + +# Show current task structure +define show_current + set $current = $spi & 0xffffe000 + printf "$current=0x%08lX\n",$current + print *(struct task_struct *)$current +end + +# Show user assigned task structure +define show_task + set = $arg0 & 0xffffe000 + printf "$task=0x%08lX\n",$task + print *(struct task_struct *)$task +end +document show_task + Show user assigned task structure + arg0 : task structure address +end + +# Show M32R registers +define show_regs + printf " R0[0x%08lX] R1[0x%08lX] R2[0x%08lX] R3[0x%08lX]\n",$r0,$r1,$r2,$r3 + printf " R4[0x%08lX] R5[0x%08lX] R6[0x%08lX] R7[0x%08lX]\n",$r4,$r5,$r6,$r7 + printf " R8[0x%08lX] R9[0x%08lX] R10[0x%08lX] R11[0x%08lX]\n",$r8,$r9,$r10,$r11 + printf "R12[0x%08lX] FP[0x%08lX] LR[0x%08lX] SP[0x%08lX]\n",$r12,$fp,$lr,$sp + printf "PSW[0x%08lX] CBR[0x%08lX] SPI[0x%08lX] SPU[0x%08lX]\n",$psw,$cbr,$spi,$spu + printf "BPC[0x%08lX] PC[0x%08lX] ACCL[0x%08lX] ACCH[0x%08lX]\n",$bpc,$pc,$accl,$acch + printf "EVB[0x%08lX]\n",$evb +end + +# Setup all +define setup + use_mon_code + set *(unsigned int)0xfffffffc=0x60 + shell sleep 0.1 + clock_init + shell sleep 0.1 + port_init + sdram_init + lanc_init + dispc_init + set $evb=0x08000000 +end + +# Load modules +define load_modules + use_debug_dma + load +end + +# Set kernel parameters +define set_kernel_parameters + set $param = (void*)0x08002000 + # INITRD_START + set *(unsigned long *)($param + 0x0010) = 0x082a0000 + # INITRD_SIZE + set *(unsigned long *)($param + 0x0014) = 0x00000000 + # M32R_CPUCLK + set *(unsigned long *)($param + 0x0018) = 0d360000000 + # M32R_BUSCLK + set *(unsigned long *)($param + 0x001c) = 0d90000000 + + # M32R_TIMER_DIVIDE + set *(unsigned long *)($param + 0x0020) = 0d128 + + set {char[0x200]}($param + 0x100) = "console=ttyD0,115200n8x root=/dev/nfsroot nfsroot=192.168.0.1:/project/m32r-linux/export/root.x nfsaddrs=192.168.0.101:192.168.0.1:192.168.0.1:255.255.255.0:mappi001 \0" +end + +# Boot +define boot + set_kernel_parameters + set $fp = 0 + set $pc=0x08001000 + si + c +end + +# Set breakpoints +define set_breakpoints + b *0x08000030 +end + +# Restart +define restart + sdireset + sdireset + setup + load_modules + boot +end + +sdireset +sdireset +file vmlinux +target m32rsdi +setup +#load_module +#set_breakpoints +#boot + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/mappi/dot.gdbinit.nommu 2004-09-03 00:57:17.386396128 -0700 @@ -0,0 +1,245 @@ +# .gdbinit file +# $Id$ +#----- +# NOTE: this file is generated by a script, "gen_gdbinit.pl". +# (Please type "gen_gdbinit.pl --help" and check the help message). +# $ Id: gen_gdbinit.pl,v 1.5 2004/01/23 08:23:25 takata Exp $ +#----- +# target platform: mappi + +# setting +set width 0d70 +set radix 0d16 +debug_chaos + +# clk xin:cpu:bif:bus=25:200:50:50 +define clock_init + set *(unsigned long *)0x00ef4024 = 2 + set *(unsigned long *)0x00ef4020 = 2 + set *(unsigned long *)0x00ef4010 = 0 + set *(unsigned long *)0x00ef4014 = 0 + set *(unsigned long *)0x00ef4004 = 3 + shell sleep 0.1 + set *(unsigned long *)0x00ef4008 = 0x00000200 +end + +# Initialize programmable ports +define port_init + set $sfrbase = 0x00ef0000 + set *(unsigned short *)0x00ef1060 = 0x5555 + set *(unsigned short *)0x00ef1062 = 0x5555 + set *(unsigned short *)0x00ef1064 = 0x5555 + set *(unsigned short *)0x00ef1066 = 0x5555 + set *(unsigned short *)0x00ef1068 = 0x5555 + set *(unsigned short *)0x00ef106a = 0x0000 + set *(unsigned short *)0x00ef106e = 0x5555 + set *(unsigned short *)0x00ef1070 = 0x5555 + # LED ON + set *(unsigned char *)($sfrbase + 0x1015) = 0xff + set *(unsigned char *)($sfrbase + 0x1085) = 0xff + shell sleep 0.1 + # LED OFF + set *(unsigned char *)($sfrbase + 0x1085) = 0x00 +end +document port_init + P5=LED(output), P6.b4=LAN_RESET(output) +end + +# Initialize SDRAM controller +define sdram_init + # SDIR0 + set *(unsigned long *)0x00ef6008 = 0x00000182 + # SDIR1 + set *(unsigned long *)0x00ef600c = 0x00000001 + # Initialize wait + shell sleep 0.1 + # Ch0-MOD + set *(unsigned long *)0x00ef602c = 0x00000020 + # Ch0-TR + set *(unsigned long *)0x00ef6028 = 0x00051502 + # Ch0-ADR (size:64MB) + set *(unsigned long *)0x00ef6020 = 0x00000004 + # AutoRef On + set *(unsigned long *)0x00ef6004 = 0x00010f05 + # Access enable + set *(unsigned long *)0x00ef6024 = 0x00000001 +end +document sdram_init + SDRAM controller initialization + 0x08000000 - 0x0bffffff (64MB) +end + +# Initialize LAN controller +define lanc_init + set $sfrbase = 0x00ef0000 + # Set BSEL3 (BSEL3 for the Chaos's bselc) + set *(unsigned long *)($sfrbase + 0x5300) = 0x07078040 + set *(unsigned long *)($sfrbase + 0x5304) = 0x01110102 + set *(unsigned long *)($sfrbase + 0x5308) = 0x00000001 + # Reset (P5=LED,P6.b4=LAN_RESET) + set *(unsigned short *)($sfrbase + 0x106c) = 0x0000 + set *(unsigned char *)($sfrbase + 0x1016) = 0xff + set *(unsigned char *)($sfrbase + 0x1086) = 0xff + shell sleep 0.1 + # swivel: 0=normal, 4=reverse +# set *(unsigned char *)($sfrbase + 0x1086) = 0x00 + set *(unsigned char *)($sfrbase + 0x1086) = 0x04 + set *(unsigned long *)(0x0c000330) = 0xffffffff + # Set mac address + set $lanc = (void*)0x0c000300 + set *(unsigned long *)($lanc + 0x0000) = 0x00610010 + set *(unsigned long *)($lanc + 0x0004) = 0x00200030 + set *(unsigned long *)($lanc + 0x0008) = 0x00400050 + set *(unsigned long *)($lanc + 0x000c) = 0x00600007 +end +document lanc_init + LAN controller initialization + ex.) MAC address: 10 20 30 40 50 60 +end + +# LCD & CRT dual-head setting (8bpp) +define dispc_init + set $sfrbase = 0x00ef0000 + # BSEL4 Dispc + set *(unsigned long *)($sfrbase + 0x5400) = 0x06078000 + set *(unsigned long *)($sfrbase + 0x5404) = 0x00101101 +end + +# MMU enable +define mmu_enable + set $evb=0x88000000 + set *(unsigned long *)0xffff0024=1 +end + +# MMU disable +define mmu_disable + set $evb=0 + set *(unsigned long *)0xffff0024=0 +end + +# Show TLB entries +define show_tlb_entries + set $i = 0 + set $addr = $arg0 + set $nr_entries = $arg1 + use_mon_code + while ($i < $nr_entries) + set $tlb_tag = *(unsigned long*)$addr + set $tlb_data = *(unsigned long*)($addr + 4) + printf " [%2d] 0x%08lx : 0x%08lx - 0x%08lx\n", $i, $addr, $tlb_tag, $tlb_data + set $i = $i + 1 + set $addr = $addr + 8 + end + use_debug_dma +end +define itlb + set $itlb=0xfe000000 + show_tlb_entries $itlb 0d32 +end +define dtlb + set $dtlb=0xfe000800 + show_tlb_entries $dtlb 0d32 +end + +# Show current task structure +define show_current + set $current = $spi & 0xffffe000 + printf "$current=0x%08lX\n",$current + print *(struct task_struct *)$current +end + +# Show user assigned task structure +define show_task + set = $arg0 & 0xffffe000 + printf "$task=0x%08lX\n",$task + print *(struct task_struct *)$task +end +document show_task + Show user assigned task structure + arg0 : task structure address +end + +# Show M32R registers +define show_regs + printf " R0[0x%08lX] R1[0x%08lX] R2[0x%08lX] R3[0x%08lX]\n",$r0,$r1,$r2,$r3 + printf " R4[0x%08lX] R5[0x%08lX] R6[0x%08lX] R7[0x%08lX]\n",$r4,$r5,$r6,$r7 + printf " R8[0x%08lX] R9[0x%08lX] R10[0x%08lX] R11[0x%08lX]\n",$r8,$r9,$r10,$r11 + printf "R12[0x%08lX] FP[0x%08lX] LR[0x%08lX] SP[0x%08lX]\n",$r12,$fp,$lr,$sp + printf "PSW[0x%08lX] CBR[0x%08lX] SPI[0x%08lX] SPU[0x%08lX]\n",$psw,$cbr,$spi,$spu + printf "BPC[0x%08lX] PC[0x%08lX] ACCL[0x%08lX] ACCH[0x%08lX]\n",$bpc,$pc,$accl,$acch + printf "EVB[0x%08lX]\n",$evb +end + +# Setup all +define setup + use_mon_code + set *(unsigned int)0xfffffffc=0x60 + shell sleep 0.1 + clock_init + shell sleep 0.1 + port_init + sdram_init + lanc_init + dispc_init + set $evb=0x00000000 +end + +# Load modules +define load_modules + use_debug_dma + load +end + +# Set kernel parameters +define set_kernel_parameters + set $param = (void*)0x00002000 + # INITRD_START + #set *(unsigned long *)($param + 0x0010) = 0x082a0000 + # INITRD_SIZE + #set *(unsigned long *)($param + 0x0014) = 0x00000000 + # M32R_CPUCLK + set *(unsigned long *)($param + 0x0018) = 0d200000000 + # M32R_BUSCLK + set *(unsigned long *)($param + 0x001c) = 0d50000000 + + # M32R_TIMER_DIVIDE + set *(unsigned long *)($param + 0x0020) = 0d128 + + set {char[0x200]}($param + 0x100) = "console=ttyD0,115200n8x root=/dev/nfsroot nfsroot=192.168.0.1:/project/m32r-linux/export/root.bbox-httpd nfsaddrs=192.168.0.101:192.168.0.1:192.168.0.1:255.255.255.0:mappi001 \0" +end + +# Boot +define boot + set_kernel_parameters + set $fp = 0 + set $pc=0x00001000 + set *(long *)0xfffffff4=0x8080 +# b load_flat_binary +# set *(unsigned char *)0x08001003=0x63 +# set *(unsigned char *)0x08001003=0x02 + si +# c +end + +# Set breakpoints +define set_breakpoints + b *0x08000030 +end + +# Restart +define restart + sdireset + sdireset + setup + load_modules + boot +end + +sdireset +sdireset +file vmlinux +target m32rsdi +setup +load_modules +boot + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/mappi/dot.gdbinit.smp 2004-09-03 00:57:17.388395824 -0700 @@ -0,0 +1,344 @@ +# .gdbinit file +# $Id$ + +# setting +set width 0d70 +set radix 0d16 +debug_chaos + +# clk xin:cpu:bif:bus=1:4:2:1 +define clock_init_on + set *(unsigned long *)0x00ef4024 = 2 + set *(unsigned long *)0x00ef4020 = 1 + set *(unsigned long *)0x00ef4010 = 0 + set *(unsigned long *)0x00ef4014 = 0 + set *(unsigned long *)0x00ef4004 = 0x1 + shell sleep 0.1 + set *(unsigned long *)0x00ef4008 = 0x0200 +# set *(unsigned long *)0x00ef4008 = 0x0201 +end + +# clk xin:cpu:bif:bus=1:4:1:1 +define clock_init_on_1411 + set *(unsigned long *)0x00ef4024 = 2 + set *(unsigned long *)0x00ef4020 = 2 + set *(unsigned long *)0x00ef4010 = 0 + set *(unsigned long *)0x00ef4014 = 0 + set *(unsigned long *)0x00ef4004 = 0x1 + shell sleep 0.1 + set *(unsigned long *)0x00ef4008 = 0x0200 +end + +# clk xin:cpu:bif:bus=1:4:2:1 +define clock_init_on_1421 + set *(unsigned long *)0x00ef4024 = 2 + set *(unsigned long *)0x00ef4020 = 1 + set *(unsigned long *)0x00ef4010 = 0 + set *(unsigned long *)0x00ef4014 = 0 + set *(unsigned long *)0x00ef4004 = 0x1 + shell sleep 0.1 + set *(unsigned long *)0x00ef4008 = 0x0200 +end + +# clk xin:cpu:bif:bus=1:8:2:1 +define clock_init_on_1821 + set *(unsigned long *)0x00ef4024 = 3 + set *(unsigned long *)0x00ef4020 = 2 + set *(unsigned long *)0x00ef4010 = 0 + set *(unsigned long *)0x00ef4014 = 0 + set *(unsigned long *)0x00ef4004 = 0x3 + shell sleep 0.1 + set *(unsigned long *)0x00ef4008 = 0x0200 +end + +# clk xin:cpu:bif:bus=1:8:4:1 +define clock_init_on_1841 + set *(unsigned long *)0x00ef4024 = 3 + set *(unsigned long *)0x00ef4020 = 1 + set *(unsigned long *)0x00ef4010 = 0 + set *(unsigned long *)0x00ef4014 = 0 + set *(unsigned long *)0x00ef4004 = 0x3 + shell sleep 0.1 + set *(unsigned long *)0x00ef4008 = 0x0200 +end + +# clk xin:cpu:bif:bus=1:16:8:1 +define clock_init_on_11681 + set *(unsigned long *)0x00ef4024 = 4 + set *(unsigned long *)0x00ef4020 = 2 + set *(unsigned long *)0x00ef4010 = 0 + set *(unsigned long *)0x00ef4014 = 0 + set *(unsigned long *)0x00ef4004 = 0x7 + shell sleep 0.1 + set *(unsigned long *)0x00ef4008 = 0x0200 +end + +# clk xin:cpu:bif:bus=1:1:1:1 +define clock_init_off + # CPU + set *(unsigned long *)0x00ef4010 = 0 + set *(unsigned long *)0x00ef4014 = 0 + # BIF + set *(unsigned long *)0x00ef4020 = 0 + # BUS + set *(unsigned long *)0x00ef4024 = 0 + # PLL + set *(unsigned long *)0x00ef4008 = 0x0000 +end + +# Initialize programmable ports +define port_init + set $sfrbase = 0x00ef0000 + set *(unsigned short *)0x00ef1060 = 0x5555 + set *(unsigned short *)0x00ef1062 = 0x5555 + set *(unsigned short *)0x00ef1064 = 0x5555 + set *(unsigned short *)0x00ef1066 = 0x5555 + set *(unsigned short *)0x00ef1068 = 0x5555 + set *(unsigned short *)0x00ef106a = 0x0000 + set *(unsigned short *)0x00ef106e = 0x5555 + set *(unsigned short *)0x00ef1070 = 0x5555 + # LED ON + set *(unsigned char *)($sfrbase + 0x1015) = 0xff + set *(unsigned char *)($sfrbase + 0x1085) = 0xff + shell sleep 0.1 + # LED OFF + set *(unsigned char *)($sfrbase + 0x1085) = 0x00 +end +document port_init + P5=LED(output), P6.b4=LAN_RESET(output) +end + +# Initialize SDRAM controller for Mappi +define sdram_init + # SDIR0 + set *(unsigned long *)0x00ef6008 = 0x00000182 + # SDIR1 + set *(unsigned long *)0x00ef600c = 0x00000001 + # Initialize wait + shell sleep 0.1 + # Ch0-MOD + set *(unsigned long *)0x00ef602c = 0x00000020 + # Ch0-TR + set *(unsigned long *)0x00ef6028 = 0x00010002 + # Ch0-ADR + set *(unsigned long *)0x00ef6020 = 0x08000004 + # AutoRef On + set *(unsigned long *)0x00ef6004 = 0x00010107 + # Access enable + set *(unsigned long *)0x00ef6024 = 0x00000001 +end +document sdram_init + Mappi SDRAM controller initialization + 0x08000000 - 0x0bffffff (64MB) +end + +# Initialize LAN controller for Mappi +define lanc_init + set $sfrbase = 0x00ef0000 + # Set BSEL3 (BSEL3 for the Chaos's bselc) +# set *(unsigned long *)($sfrbase + 0x5300) = 0x01018040 +# set *(unsigned long *)($sfrbase + 0x5304) = 0x01011101 + set *(unsigned long *)($sfrbase + 0x5300) = 0x04048000 + set *(unsigned long *)($sfrbase + 0x5304) = 0x01011103 + set *(unsigned long *)($sfrbase + 0x5308) = 0x00000001 + # Reset (P5=LED,P6.b4=LAN_RESET) + set *(unsigned short *)($sfrbase + 0x106c) = 0x0000 + set *(unsigned char *)($sfrbase + 0x1016) = 0xff + set *(unsigned char *)($sfrbase + 0x1086) = 0xff + shell sleep 0.1 +# set *(unsigned char *)($sfrbase + 0x1086) = 0x00 + set *(unsigned char *)($sfrbase + 0x1086) = 0x04 + set *(unsigned long *)(0x0c000330) = 0xffffffff + # Set mac address + set $lanc = (void*)0x0c000300 + set *(unsigned long *)($lanc + 0x0000) = 0x00610010 + set *(unsigned long *)($lanc + 0x0004) = 0x00200030 + set *(unsigned long *)($lanc + 0x0008) = 0x00400050 + set *(unsigned long *)($lanc + 0x000c) = 0x00600007 +end +document lanc_init + Mappi LAN controller initialization + ex.) MAC address: 10 20 30 40 50 60 +end + +# LCD & CRT dual-head setting (8bpp) +define dispc_init + set $sfrbase = 0x00ef0000 + # BSEL4 Dispc + # 20MHz +# set *(unsigned long *)($sfrbase + 0x5400) = 0x02028282 +# set *(unsigned long *)($sfrbase + 0x5404) = 0x00122202 + # 40MHz + set *(unsigned long *)($sfrbase + 0x5400) = 0x04048000 + set *(unsigned long *)($sfrbase + 0x5404) = 0x00101103 +end + +# MMU enable +define mmu_enable + set $evb=0x88000000 + set *(unsigned long *)0xffff0024=1 +end + +# MMU disable +define mmu_disable + set $evb=0 + set *(unsigned long *)0xffff0024=0 +end + +# Show TLB entries +define show_tlb_entries + set $i = 0 + set $addr = $arg0 + use_mon_code + while ($i < 0d32 ) + set $tlb_tag = *(unsigned long*)$addr + set $tlb_data = *(unsigned long*)($addr + 4) + printf " [%2d] 0x%08lx : 0x%08lx - 0x%08lx\n", $i, $addr, $tlb_tag, $tlb_data + set $i = $i + 1 + set $addr = $addr + 8 + end + use_debug_dma +end +define itlb + set $itlb=0xfe000000 + show_tlb_entries $itlb +end +define dtlb + set $dtlb=0xfe000800 + show_tlb_entries $dtlb +end + + +# Show current task structure +define show_current + set $current = $spi & 0xffffe000 + printf "$current=0x%08lX\n",$current + print *(struct task_struct *)$current +end + +# Show user assigned task structure +define show_task + set $task = $arg0 & 0xffffe000 + printf "$task=0x%08lX\n",$task + print *(struct task_struct *)$task +end +document show_task + Show user assigned task structure + arg0 : task structure address +end + +# Show M32R registers +define show_regs + printf " R0[0x%08lX] R1[0x%08lX] R2[0x%08lX] R3[0x%08lX]\n",$r0,$r1,$r2,$r3 + printf " R4[0x%08lX] R5[0x%08lX] R6[0x%08lX] R7[0x%08lX]\n",$r4,$r5,$r6,$r7 + printf " R8[0x%08lX] R9[0x%08lX] R10[0x%08lX] R11[0x%08lX]\n",$r8,$r9,$r10,$r11 + printf "R12[0x%08lX] FP[0x%08lX] LR[0x%08lX] SP[0x%08lX]\n",$r12,$fp,$lr,$fp + printf "PSW[0x%08lX] CBR[0x%08lX] SPI[0x%08lX] SPU[0x%08lX]\n",$psw,$cbr,$spi,$spu + printf "BPC[0x%08lX] PC[0x%08lX] ACCL[0x%08lX] ACCH[0x%08lX]\n",$bpc,$pc,$accl,$acch + printf "EVB[0x%08lX]\n",$evb +end + + +# Setup all +define setup + use_mon_code + set *(unsigned int)0xfffffffc=0x60 + shell sleep 0.1 +# clock_init_on_1411 + clock_init_on_1421 +# clock_init_on_1821 +# clock_init_on_1841 +# clock_init_on_11681 +# clock_init_off + shell sleep 0.1 + port_init + sdram_init + lanc_init + dispc_init + set $evb=0x08000000 +end + +# Load modules +define load_modules + use_debug_dma + load +# load ramdisk_082a0000.mot +# load romfs_082a0000.mot +# use_mon_code +end + +# Set kernel parameters +define set_kernel_parameters + set $param = (void*)0x08002000 + # INITRD_START +# set *(unsigned long *)($param + 0x0010) = 0x082a0000 + # INITRD_SIZE + set *(unsigned long *)($param + 0x0014) = 0x00000000 + # M32R_CPUCLK + set *(unsigned long *)($param + 0x0018) = 0d160000000 +# set *(unsigned long *)($param + 0x0018) = 0d80000000 +# set *(unsigned long *)($param + 0x0018) = 0d40000000 + # M32R_BUSCLK + set *(unsigned long *)($param + 0x001c) = 0d40000000 + + # M32R_TIMER_DIVIDE + set *(unsigned long *)($param + 0x0020) = 0d128 + + set {char[0x200]}($param + 0x100) = "console=tty1 console=ttyD0,115200n8x root=/dev/nfsroot nfsroot=192.168.0.1:/project/m32r-linux/export/root.x nfsaddrs=192.168.0.101:192.168.0.1:192.168.0.1:255.255.255.0:mappi001 \0" +# set {char[0x200]}($param + 0x100) = "console=tty1 root=/dev/nfsroot nfsroot=192.168.0.1:/project/m32r-linux/export/root.x nfsaddrs=192.168.0.101:192.168.0.1:192.168.0.1:255.255.255.0:mappi001 \0" +end + +# Boot +define boot + set_kernel_parameters + set $pc=0x08001000 + set *(unsigned char *)0x08001003=0x03 + si + c +end + +# Set breakpoints +define set_breakpoints + b *0x08000030 +end + +## Boot MP +define boot_mp + set_kernel_parameters + set *(unsigned long *)0x00f00000 = boot - 0x80000000 + set *(unsigned long *)0x00eff2f8 = 0x2 + x 0x00eff2f8 + + set $pc=0x08001000 + si + c +end +document boot_mp + Boot BSP +end + +## Boot UP +define boot_up + set_kernel_parameters + set $pc=0x08001000 + si + c +end +document boot_up + Boot BSP +end + +# Restart +define restart + sdireset + sdireset + setup + load_modules + boot_mp +end + +sdireset +sdireset +file vmlinux +target m32rsdi +setup --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/mm/cache.c 2004-09-03 00:57:17.388395824 -0700 @@ -0,0 +1,68 @@ +/* + * linux/arch/m32r/mm/cache.c + * + * Copyright (C) 2002 Hirokazu Takata + */ + +/* $Id$ */ + +#include +#include + +#undef MCCR + +#if defined(CONFIG_CHIP_XNUX2) || defined(CONFIG_CHIP_M32700) || defined(CONFIG_CHIP_VDEC2) || defined(CONFIG_CHIP_OPSP) +/* Cache Control Register */ +#define MCCR ((volatile unsigned long*)0xfffffffc) +#define MCCR_CC (1UL << 7) /* Cache mode modify bit */ +#define MCCR_IIV (1UL << 6) /* I-cache invalidate */ +#define MCCR_DIV (1UL << 5) /* D-cache invalidate */ +#define MCCR_DCB (1UL << 4) /* D-cache copy back */ +#define MCCR_ICM (1UL << 1) /* I-cache mode [0:off,1:on] */ +#define MCCR_DCM (1UL << 0) /* D-cache mode [0:off,1:on] */ +#define MCCR_ICACHE_INV (MCCR_CC|MCCR_IIV) +#define MCCR_DCACHE_CB (MCCR_CC|MCCR_DCB) +#define MCCR_DCACHE_CBINV (MCCR_CC|MCCR_DIV|MCCR_DCB) +#define CHECK_MCCR(mccr) (mccr = *MCCR) +#elif defined(CONFIG_CHIP_M32102) +#define MCCR ((volatile unsigned long*)0xfffffffc) +#define MCCR_IIV (1UL << 8) /* I-cache invalidate */ +#define MCCR_ICACHE_INV MCCR_IIV +#endif /* CONFIG_CHIP_XNUX2 || CONFIG_CHIP_M32700 */ + +#ifndef MCCR +#error Unknown cache type. +#endif + + +/* Copy back and invalidate D-cache and invalidate I-cache all */ +void _flush_cache_all(void) +{ +#if defined(CONFIG_CHIP_M32102) + *MCCR = MCCR_ICACHE_INV; +#else + unsigned long mccr; + + /* Copyback and invalidate D-cache */ + /* Invalidate I-cache */ + *MCCR = MCCR_ICACHE_INV | MCCR_DCACHE_CBINV; + while ((mccr = *MCCR) & MCCR_IIV); /* loop while invalidating... */ +#endif +} + +/* Copy back D-cache and invalidate I-cache all */ +void _flush_cache_copyback_all(void) +{ +#if defined(CONFIG_CHIP_M32102) + *MCCR = MCCR_ICACHE_INV; +#else + unsigned long mccr; + + /* Copyback D-cache */ + /* Invalidate I-cache */ + *MCCR = MCCR_ICACHE_INV | MCCR_DCACHE_CB; + while ((mccr = *MCCR) & MCCR_IIV); /* loop while invalidating... */ + +#endif +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/mm/discontig.c 2004-09-03 00:57:17.389395672 -0700 @@ -0,0 +1,170 @@ +/* + * linux/arch/m32r/mm/discontig.c + * + * Discontig memory support + * + * Copyright (c) 2003 Hitoshi Yamamoto + */ + +#include +#include +#include +#include +#include + +#include + +extern char _end[]; + +struct pglist_data *node_data[MAX_NUMNODES]; +static bootmem_data_t node_bdata[MAX_NUMNODES] __initdata; + +pg_data_t m32r_node_data[MAX_NUMNODES]; + +/* Memory profile */ +typedef struct { + unsigned long start_pfn; + unsigned long pages; + unsigned long holes; + unsigned long free_pfn; +} mem_prof_t; +static mem_prof_t mem_prof[MAX_NUMNODES]; + +static void __init mem_prof_init(void) +{ + unsigned long start_pfn, holes, free_pfn; + const unsigned long zone_alignment = 1UL << (MAX_ORDER - 1); + unsigned long ul; + mem_prof_t *mp; + + /* Node#0 SDRAM */ + mp = &mem_prof[0]; + mp->start_pfn = PFN_UP(CONFIG_MEMORY_START); + mp->pages = PFN_DOWN(CONFIG_MEMORY_SIZE); + mp->holes = 0; + mp->free_pfn = PFN_UP(__pa(_end)); + + /* Node#1 internal SRAM */ + mp = &mem_prof[1]; + start_pfn = free_pfn = PFN_UP(CONFIG_IRAM_START); + holes = 0; + if (start_pfn & (zone_alignment - 1)) { + ul = zone_alignment; + while (start_pfn >= ul) + ul += zone_alignment; + + start_pfn = ul - zone_alignment; + holes = free_pfn - start_pfn; + } + + mp->start_pfn = start_pfn; + mp->pages = PFN_DOWN(CONFIG_IRAM_SIZE) + holes; + mp->holes = holes; + mp->free_pfn = PFN_UP(CONFIG_IRAM_START); +} + +unsigned long __init setup_memory(void) +{ + unsigned long bootmap_size; + unsigned long min_pfn; + int nid; + mem_prof_t *mp; + + max_low_pfn = 0; + min_low_pfn = -1; + + mem_prof_init(); + + for (nid = 0 ; nid < numnodes ; nid++) { + mp = &mem_prof[nid]; + NODE_DATA(nid)=(pg_data_t *)&m32r_node_data[nid]; + NODE_DATA(nid)->bdata = &node_bdata[nid]; + min_pfn = mp->start_pfn; + max_pfn = mp->start_pfn + mp->pages; + bootmap_size = init_bootmem_node(NODE_DATA(nid), mp->free_pfn, + mp->start_pfn, max_pfn); + + free_bootmem_node(NODE_DATA(nid), PFN_PHYS(mp->start_pfn), + PFN_PHYS(mp->pages)); + + reserve_bootmem_node(NODE_DATA(nid), PFN_PHYS(mp->start_pfn), + PFN_PHYS(mp->free_pfn - mp->start_pfn) + bootmap_size); + + if (max_low_pfn < max_pfn) + max_low_pfn = max_pfn; + + if (min_low_pfn > min_pfn) + min_low_pfn = min_pfn; + } + +#ifdef CONFIG_BLK_DEV_INITRD + if (LOADER_TYPE && INITRD_START) { + if (INITRD_START + INITRD_SIZE <= PFN_PHYS(max_low_pfn)) { + reserve_bootmem_node(NODE_DATA(0), INITRD_START, + INITRD_SIZE); + initrd_start = INITRD_START ? + INITRD_START + PAGE_OFFSET : 0; + + initrd_end = initrd_start + INITRD_SIZE; + printk("initrd:start[%08lx],size[%08lx]\n", + initrd_start, INITRD_SIZE); + } else { + printk("initrd extends beyond end of memory " + "(0x%08lx > 0x%08lx)\ndisabling initrd\n", + INITRD_START + INITRD_SIZE, + PFN_PHYS(max_low_pfn)); + + initrd_start = 0; + } + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + return max_low_pfn; +} + +#define START_PFN(nid) \ + (NODE_DATA(nid)->bdata->node_boot_start >> PAGE_SHIFT) +#define MAX_LOW_PFN(nid) (NODE_DATA(nid)->bdata->node_low_pfn) + +unsigned long __init zone_sizes_init(void) +{ + unsigned long zones_size[MAX_NR_ZONES], zholes_size[MAX_NR_ZONES]; + unsigned long low, start_pfn; + unsigned long holes = 0; + int nid, i; + mem_prof_t *mp; + + pgdat_list = NULL; + for (nid = numnodes - 1 ; nid >= 0 ; nid--) { + NODE_DATA(nid)->pgdat_next = pgdat_list; + pgdat_list = NODE_DATA(nid); + } + + for (nid = 0 ; nid < numnodes ; nid++) { + mp = &mem_prof[nid]; + for (i = 0 ; i < MAX_NR_ZONES ; i++) { + zones_size[i] = 0; + zholes_size[i] = 0; + } + start_pfn = START_PFN(nid); + low = MAX_LOW_PFN(nid); + zones_size[ZONE_DMA] = low - start_pfn; + zholes_size[ZONE_DMA] = mp->holes; + holes += zholes_size[ZONE_DMA]; + + free_area_init_node(nid, NODE_DATA(nid), NULL, zones_size, + start_pfn, zholes_size); + } + + /* + * For test + * Use all area of internal RAM. + * see __alloc_pages() + */ + NODE_DATA(1)->node_zones->pages_min = 0; + NODE_DATA(1)->node_zones->pages_low = 0; + NODE_DATA(1)->node_zones->pages_high = 0; + + return holes; +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/mm/extable.c 2004-09-03 00:57:17.389395672 -0700 @@ -0,0 +1,22 @@ +/* + * linux/arch/i386/mm/extable.c + */ + +#include +#include +#include +#include + +int fixup_exception(struct pt_regs *regs) +{ + const struct exception_table_entry *fixup; + + fixup = search_exception_tables(regs->bpc); + if (fixup) { + regs->bpc = fixup->fixup; + return 1; + } + + return 0; +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/mm/fault.c 2004-09-03 00:57:18.733191384 -0700 @@ -0,0 +1,572 @@ +/* + * linux/arch/m32r/mm/fault.c + * + * Copyright (c) 2001, 2002 Hitoshi Yamamoto, and H. Kondo + * + * Some code taken from i386 version. + * Copyright (C) 1995 Linus Torvalds + */ + +/* $Id$ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* For unblank_screen() */ +#include +#include + +#include +#include +#include +#include +#include +#include + +extern void die(const char *, struct pt_regs *, long); + +#ifndef CONFIG_SMP +asmlinkage unsigned int tlb_entry_i_dat; +asmlinkage unsigned int tlb_entry_d_dat; +#define tlb_entry_i tlb_entry_i_dat +#define tlb_entry_d tlb_entry_d_dat +#else +unsigned int tlb_entry_i_dat[NR_CPUS]; +unsigned int tlb_entry_d_dat[NR_CPUS]; +#define tlb_entry_i tlb_entry_i_dat[smp_processor_id()] +#define tlb_entry_d tlb_entry_d_dat[smp_processor_id()] +#endif + +extern void init_tlb(void); + +/* + * Unlock any spinlocks which will prevent us from getting the + * message out + */ +void bust_spinlocks(int yes) +{ + int loglevel_save = console_loglevel; + + if (yes) { + oops_in_progress = 1; + return; + } +#ifdef CONFIG_VT + unblank_screen(); +#endif + oops_in_progress = 0; + /* + * OK, the message is on the console. Now we call printk() + * without oops_in_progress set so that printk will give klogd + * a poke. Hold onto your hats... + */ + console_loglevel = 15; /* NMI oopser may have shut the console up */ + printk(" "); + console_loglevel = loglevel_save; +} + +/*======================================================================* + * do_page_fault() + *======================================================================* + * This routine handles page faults. It determines the address, + * and the problem, and then passes it off to one of the appropriate + * routines. + * + * ARGUMENT: + * regs : M32R SP reg. + * error_code : See below + * address : M32R MMU MDEVA reg. (Operand ACE) + * : M32R BPC reg. (Instruction ACE) + * + * error_code : + * bit 0 == 0 means no page found, 1 means protection fault + * bit 1 == 0 means read, 1 means write + * bit 2 == 0 means kernel, 1 means user-mode + * bit 3 == 0 means data, 1 means instruction + *======================================================================*/ +asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code, + unsigned long address) +{ + struct task_struct *tsk; + struct mm_struct *mm; + struct vm_area_struct * vma; + unsigned long page, addr; + int write; + siginfo_t info; + + /* + * If BPSW IE bit enable --> set PSW IE bit + */ + if (regs->psw & M32R_PSW_BIE) + local_irq_enable(); + + tsk = current; + + info.si_code = SEGV_MAPERR; + + /* + * We fault-in kernel-space virtual memory on-demand. The + * 'reference' page table is init_mm.pgd. + * + * NOTE! We MUST NOT take any locks for this case. We may + * be in an interrupt or a critical region, and should + * only copy the information from the master page table, + * nothing more. + * + * This verifies that the fault happens in kernel space + * (error_code & 4) == 0, and that the fault was not a + * protection error (error_code & 1) == 0. + */ + if (address >= TASK_SIZE && !(error_code & 4)) + goto vmalloc_fault; + + mm = tsk->mm; + + /* + * If we're in an interrupt or have no user context or are running in an + * atomic region then we must not take the fault.. + */ + if (in_atomic() || !mm) + goto bad_area_nosemaphore; + + /* When running in the kernel we expect faults to occur only to + * addresses in user space. All other faults represent errors in the + * kernel and should generate an OOPS. Unfortunatly, in the case of an + * erroneous fault occuring in a code path which already holds mmap_sem + * we will deadlock attempting to validate the fault against the + * address space. Luckily the kernel only validly references user + * space from well defined areas of code, which are listed in the + * exceptions table. + * + * As the vast majority of faults will be valid we will only perform + * the source reference check when there is a possibilty of a deadlock. + * Attempt to lock the address space, if we cannot we then validate the + * source. If this is invalid we can skip the address space check, + * thus avoiding the deadlock. + */ + if (!down_read_trylock(&mm->mmap_sem)) { + if ((error_code & 4) == 0 && + !search_exception_tables(regs->psw)) + goto bad_area_nosemaphore; + down_read(&mm->mmap_sem); + } + + vma = find_vma(mm, address); + if (!vma) + goto bad_area; + if (vma->vm_start <= address) + goto good_area; + if (!(vma->vm_flags & VM_GROWSDOWN)) + goto bad_area; +#if 0 + if (error_code & 4) { + /* + * accessing the stack below "spu" is always a bug. + * The "+ 4" is there due to the push instruction + * doing pre-decrement on the stack and that + * doesn't show up until later.. + */ + if (address + 4 < regs->spu) + goto bad_area; + } +#endif + if (expand_stack(vma, address)) + goto bad_area; +/* + * Ok, we have a good vm_area for this memory access, so + * we can handle it.. + */ +good_area: + info.si_code = SEGV_ACCERR; + write = 0; + switch (error_code & 3) { + default: /* 3: write, present */ + /* fall through */ + case 2: /* write, not present */ + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + write++; + break; + case 1: /* read, present */ + case 0: /* read, not present */ + if (!(vma->vm_flags & (VM_READ | VM_EXEC))) + goto bad_area; + } + +survive: + /* + * If for any reason at all we couldn't handle the fault, + * make sure we exit gracefully rather than endlessly redo + * the fault. + */ + addr = (address & PAGE_MASK) | (error_code & 8); + switch (handle_mm_fault(mm, vma, addr, write)) { + case VM_FAULT_MINOR: + tsk->min_flt++; + break; + case VM_FAULT_MAJOR: + tsk->maj_flt++; + break; + case VM_FAULT_SIGBUS: + goto do_sigbus; + case VM_FAULT_OOM: + goto out_of_memory; + default: + BUG(); + } + + up_read(&mm->mmap_sem); + return; + +/* + * Something tried to access memory that isn't in our memory map.. + * Fix it, but check if it's kernel or user first.. + */ +bad_area: + up_read(&mm->mmap_sem); + +bad_area_nosemaphore: + /* User mode accesses just cause a SIGSEGV */ + if (error_code & 4) { + tsk->thread.address = address; + tsk->thread.error_code = error_code | (address >= TASK_SIZE); + tsk->thread.trap_no = 14; + info.si_signo = SIGSEGV; + info.si_errno = 0; + /* info.si_code has been set above */ + info.si_addr = (void __user *)address; + force_sig_info(SIGSEGV, &info, tsk); + return; + } + +no_context: + /* Are we prepared to handle this kernel fault? */ + if (fixup_exception(regs)) + return; + +/* + * Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. + */ + + bust_spinlocks(1); + + if (address < PAGE_SIZE) + printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); + else + printk(KERN_ALERT "Unable to handle kernel paging request"); + printk(" at virtual address %08lx\n",address); + printk(KERN_ALERT " printing bpc:\n"); + printk("%08lx\n", regs->bpc); + page = *(unsigned long *)MPTB; + page = ((unsigned long *) page)[address >> PGDIR_SHIFT]; + printk(KERN_ALERT "*pde = %08lx\n", page); + if (page & _PAGE_PRESENT) { + page &= PAGE_MASK; + address &= 0x003ff000; + page = ((unsigned long *) __va(page))[address >> PAGE_SHIFT]; + printk(KERN_ALERT "*pte = %08lx\n", page); + } + die("Oops", regs, error_code); + bust_spinlocks(0); + do_exit(SIGKILL); + +/* + * We ran out of memory, or some other thing happened to us that made + * us unable to handle the page fault gracefully. + */ +out_of_memory: + up_read(&mm->mmap_sem); + if (tsk->pid == 1) { + yield(); + down_read(&mm->mmap_sem); + goto survive; + } + printk("VM: killing process %s\n", tsk->comm); + if (error_code & 4) + do_exit(SIGKILL); + goto no_context; + +do_sigbus: + up_read(&mm->mmap_sem); + + /* Kernel mode? Handle exception or die */ + if (!(error_code & 4)) + goto no_context; + + tsk->thread.address = address; + tsk->thread.error_code = error_code; + tsk->thread.trap_no = 14; + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRERR; + info.si_addr = (void __user *)address; + force_sig_info(SIGBUS, &info, tsk); + return; + +vmalloc_fault: + { + /* + * Synchronize this task's top level page-table + * with the 'reference' page table. + * + * Do _not_ use "tsk" here. We might be inside + * an interrupt in the middle of a task switch.. + */ + int offset = pgd_index(address); + pgd_t *pgd, *pgd_k; + pmd_t *pmd, *pmd_k; + pte_t *pte_k; + + pgd = (pgd_t *)*(unsigned long *)MPTB; + pgd = offset + (pgd_t *)pgd; + pgd_k = init_mm.pgd + offset; + + if (!pgd_present(*pgd_k)) + goto no_context; + + /* + * set_pgd(pgd, *pgd_k); here would be useless on PAE + * and redundant with the set_pmd() on non-PAE. + */ + + pmd = pmd_offset(pgd, address); + pmd_k = pmd_offset(pgd_k, address); + if (!pmd_present(*pmd_k)) + goto no_context; + set_pmd(pmd, *pmd_k); + + pte_k = pte_offset_kernel(pmd_k, address); + if (!pte_present(*pte_k)) + goto no_context; + + addr = (address & PAGE_MASK) | (error_code & 8); + update_mmu_cache(NULL, addr, *pte_k); + return; + } +} + +/*======================================================================* + * update_mmu_cache() + *======================================================================*/ +#define TLB_MASK (NR_TLB_ENTRIES - 1) +#define ITLB_END (unsigned long *)(ITLB_BASE + (NR_TLB_ENTRIES * 8)) +#define DTLB_END (unsigned long *)(DTLB_BASE + (NR_TLB_ENTRIES * 8)) +void update_mmu_cache(struct vm_area_struct *vma, unsigned long vaddr, + pte_t pte) +{ + unsigned long *entry1, *entry2; + unsigned long pte_data, flags; + unsigned int *entry_dat; + int inst = vaddr & 8; + int i; + + /* Ptrace may call this routine. */ + if (vma && current->active_mm != vma->vm_mm) + return; + + local_irq_save(flags); + + vaddr = (vaddr & PAGE_MASK) | get_asid(); + +#ifdef CONFIG_CHIP_OPSP + entry1 = (unsigned long *)ITLB_BASE; + for(i = 0 ; i < NR_TLB_ENTRIES; i++) { + if(*entry1++ == vaddr) { + pte_data = pte_val(pte); + set_tlb_data(entry1, pte_data); + break; + } + entry1++; + } + entry2 = (unsigned long *)DTLB_BASE; + for(i = 0 ; i < NR_TLB_ENTRIES ; i++) { + if(*entry2++ == vaddr) { + pte_data = pte_val(pte); + set_tlb_data(entry2, pte_data); + break; + } + entry2++; + } + local_irq_restore(flags); + return; +#else + pte_data = pte_val(pte); + + /* + * Update TLB entries + * entry1: ITLB entry address + * entry2: DTLB entry address + */ + __asm__ __volatile__ ( + "seth %0, #high(%4) \n\t" + "st %2, @(%5, %0) \n\t" + "ldi %1, #1 \n\t" + "st %1, @(%6, %0) \n\t" + "add3 r4, %0, %7 \n\t" + ".fillinsn \n" + "1: \n\t" + "ld %1, @(%6, %0) \n\t" + "bnez %1, 1b \n\t" + "ld %0, @r4+ \n\t" + "ld %1, @r4 \n\t" + "st %3, @+%0 \n\t" + "st %3, @+%1 \n\t" + : "=&r" (entry1), "=&r" (entry2) + : "r" (vaddr), "r" (pte_data), "i" (MMU_REG_BASE), + "i" (MSVA_offset), "i" (MTOP_offset), "i" (MIDXI_offset) + : "r4", "memory" + ); + + if ((!inst && entry2 >= DTLB_END) || (inst && entry1 >= ITLB_END)) + goto notfound; + +found: + local_irq_restore(flags); + + return; + + /* Valid entry not found */ +notfound: + /* + * Update ITLB or DTLB entry + * entry1: TLB entry address + * entry2: TLB base address + */ + if (!inst) { + entry2 = (unsigned long *)DTLB_BASE; + entry_dat = &tlb_entry_d; + } else { + entry2 = (unsigned long *)ITLB_BASE; + entry_dat = &tlb_entry_i; + } + entry1 = entry2 + (((*entry_dat - 1) & TLB_MASK) << 1); + + for (i = 0 ; i < NR_TLB_ENTRIES ; i++) { + if (!(entry1[1] & 2)) /* Valid bit check */ + break; + + if (entry1 != entry2) + entry1 -= 2; + else + entry1 += TLB_MASK << 1; + } + + if (i >= NR_TLB_ENTRIES) { /* Empty entry not found */ + entry1 = entry2 + (*entry_dat << 1); + *entry_dat = (*entry_dat + 1) & TLB_MASK; + } + *entry1++ = vaddr; /* Set TLB tag */ + set_tlb_data(entry1, pte_data); + + goto found; +#endif +} + +/*======================================================================* + * flush_tlb_page() : flushes one page + *======================================================================*/ +void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) +{ + if (vma->vm_mm && mm_context(vma->vm_mm) != NO_CONTEXT) { + unsigned long flags; + + local_irq_save(flags); + page &= PAGE_MASK; + page |= (mm_context(vma->vm_mm) & MMU_CONTEXT_ASID_MASK); + __flush_tlb_page(page); + local_irq_restore(flags); + } +} + +/*======================================================================* + * flush_tlb_range() : flushes a range of pages + *======================================================================*/ +void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, + unsigned long end) +{ + struct mm_struct *mm; + + mm = vma->vm_mm; + if (mm_context(mm) != NO_CONTEXT) { + unsigned long flags; + int size; + + local_irq_save(flags); + size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + if (size > (NR_TLB_ENTRIES / 4)) { /* Too many TLB to flush */ + mm_context(mm) = NO_CONTEXT; + if (mm == current->mm) + activate_context(mm); + } else { + unsigned long asid; + + asid = mm_context(mm) & MMU_CONTEXT_ASID_MASK; + start &= PAGE_MASK; + end += (PAGE_SIZE - 1); + end &= PAGE_MASK; + + start |= asid; + end |= asid; + while (start < end) { + __flush_tlb_page(start); + start += PAGE_SIZE; + } + } + local_irq_restore(flags); + } +} + +/*======================================================================* + * flush_tlb_mm() : flushes the specified mm context TLB's + *======================================================================*/ +void local_flush_tlb_mm(struct mm_struct *mm) +{ + /* Invalidate all TLB of this process. */ + /* Instead of invalidating each TLB, we get new MMU context. */ + if (mm_context(mm) != NO_CONTEXT) { + unsigned long flags; + + local_irq_save(flags); + mm_context(mm) = NO_CONTEXT; + if (mm == current->mm) + activate_context(mm); + local_irq_restore(flags); + } +} + +/*======================================================================* + * flush_tlb_all() : flushes all processes TLBs + *======================================================================*/ +void local_flush_tlb_all(void) +{ + unsigned long flags; + + local_irq_save(flags); + __flush_tlb_all(); + local_irq_restore(flags); +} + +/*======================================================================* + * init_mmu() + *======================================================================*/ +void __init init_mmu(void) +{ + tlb_entry_i = 0; + tlb_entry_d = 0; + mmu_context_cache = MMU_CONTEXT_FIRST_VERSION; + set_asid(mmu_context_cache & MMU_CONTEXT_ASID_MASK); + *(volatile unsigned long *)MPTB = (unsigned long)swapper_pg_dir; +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/mm/fault-nommu.c 2004-09-03 00:57:17.393395064 -0700 @@ -0,0 +1,164 @@ +/* + * linux/arch/m32r/mm/fault.c + * + * Copyright (c) 2001, 2002 Hitoshi Yamamoto, and H. Kondo + * + * Some code taken from i386 version. + * Copyright (C) 1995 Linus Torvalds + */ + +/* $Id: fault-nommu.c,v 1.1 2004/03/30 06:40:59 sakugawa Exp $ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +extern void die(const char *, struct pt_regs *, long); + +#ifndef CONFIG_SMP +asmlinkage unsigned int tlb_entry_i_dat; +asmlinkage unsigned int tlb_entry_d_dat; +#define tlb_entry_i tlb_entry_i_dat +#define tlb_entry_d tlb_entry_d_dat +#else +unsigned int tlb_entry_i_dat[NR_CPUS]; +unsigned int tlb_entry_d_dat[NR_CPUS]; +#define tlb_entry_i tlb_entry_i_dat[smp_processor_id()] +#define tlb_entry_d tlb_entry_d_dat[smp_processor_id()] +#endif + +/* + * Unlock any spinlocks which will prevent us from getting the + * message out + */ +void bust_spinlocks(int yes) +{ + int loglevel_save = console_loglevel; + + if (yes) { + oops_in_progress = 1; + return; + } +#ifdef CONFIG_VT + unblank_screen(); +#endif + oops_in_progress = 0; + /* + * OK, the message is on the console. Now we call printk() + * without oops_in_progress set so that printk will give klogd + * a poke. Hold onto your hats... + */ + console_loglevel = 15; /* NMI oopser may have shut the console up */ + printk(" "); + console_loglevel = loglevel_save; +} + +void do_BUG(const char *file, int line) +{ + bust_spinlocks(1); + printk("kernel BUG at %s:%d!\n", file, line); +} + +/*======================================================================* + * do_page_fault() + *======================================================================* + * This routine handles page faults. It determines the address, + * and the problem, and then passes it off to one of the appropriate + * routines. + * + * ARGUMENT: + * regs : M32R SP reg. + * error_code : See below + * address : M32R MMU MDEVA reg. (Operand ACE) + * : M32R BPC reg. (Instruction ACE) + * + * error_code : + * bit 0 == 0 means no page found, 1 means protection fault + * bit 1 == 0 means read, 1 means write + * bit 2 == 0 means kernel, 1 means user-mode + *======================================================================*/ +asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code, + unsigned long address) +{ + +/* + * Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. + */ + + bust_spinlocks(1); + + if (address < PAGE_SIZE) + printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); + else + printk(KERN_ALERT "Unable to handle kernel paging request"); + printk(" at virtual address %08lx\n",address); + printk(" printing bpc:\n"); + printk(KERN_ALERT "bpc = %08lx\n", regs->bpc); + + die("Oops", regs, error_code); + bust_spinlocks(0); + do_exit(SIGKILL); +} + +/*======================================================================* + * update_mmu_cache() + *======================================================================*/ +void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, + pte_t pte) +{ + BUG(); +} + +/*======================================================================* + * flush_tlb_page() : flushes one page + *======================================================================*/ +void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) +{ + BUG(); +} + +/*======================================================================* + * flush_tlb_range() : flushes a range of pages + *======================================================================*/ +void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, + unsigned long end) +{ + BUG(); +} + +/*======================================================================* + * flush_tlb_mm() : flushes the specified mm context TLB's + *======================================================================*/ +void local_flush_tlb_mm(struct mm_struct *mm) +{ + BUG(); +} + +/*======================================================================* + * flush_tlb_all() : flushes all processes TLBs + *======================================================================*/ +void local_flush_tlb_all(void) +{ + BUG(); +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/mm/init.c 2004-09-03 00:57:18.733191384 -0700 @@ -0,0 +1,251 @@ +/* + * linux/arch/m32r/mm/init.c + * + * Copyright (c) 2001, 2002 Hitoshi Yamamoto + * + * Some code taken from sh version. + * Copyright (C) 1999 Niibe Yutaka + * Based on linux/arch/i386/mm/init.c: + * Copyright (C) 1995 Linus Torvalds + */ + +/* $Id$ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* References to section boundaries */ +extern char _text, _etext, _edata; +extern char __init_begin, __init_end; + +pgd_t swapper_pg_dir[1024]; + +DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); + +void show_mem(void) +{ + int total = 0, reserved = 0; + int shared = 0, cached = 0; + int highmem = 0; + struct page *page; + pg_data_t *pgdat; + unsigned long i; + + printk("Mem-info:\n"); + show_free_areas(); + printk("Free swap: %6ldkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); + for_each_pgdat(pgdat) { + for (i = 0; i < pgdat->node_spanned_pages; ++i) { + page = pgdat->node_mem_map + i; + total++; + if (PageHighMem(page)) + highmem++; + if (PageReserved(page)) + reserved++; + else if (PageSwapCache(page)) + cached++; + else if (page_count(page)) + shared += page_count(page) - 1; + } + } + printk("%d pages of RAM\n", total); + printk("%d pages of HIGHMEM\n",highmem); + printk("%d reserved pages\n",reserved); + printk("%d pages shared\n",shared); + printk("%d pages swap cached\n",cached); +} + +/* + * Cache of MMU context last used. + */ +#ifndef CONFIG_SMP +unsigned long mmu_context_cache_dat; +#else +unsigned long mmu_context_cache_dat[NR_CPUS]; +#endif +static unsigned long hole_pages; + +/* + * function prototype + */ +void __init paging_init(void); +void __init mem_init(void); +void free_initmem(void); +#ifdef CONFIG_BLK_DEV_INITRD +void free_initrd_mem(unsigned long, unsigned long); +#endif + +/* It'd be good if these lines were in the standard header file. */ +#define START_PFN(nid) \ + (NODE_DATA(nid)->bdata->node_boot_start >> PAGE_SHIFT) +#define MAX_LOW_PFN(nid) (NODE_DATA(nid)->bdata->node_low_pfn) + +#ifndef CONFIG_DISCONTIGMEM +unsigned long __init zone_sizes_init(void) +{ + unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0}; + unsigned long max_dma; + unsigned long low; + unsigned long start_pfn; + +#ifdef CONFIG_MMU + start_pfn = START_PFN(0); + max_dma = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT; + low = MAX_LOW_PFN(0); + + if (low < max_dma){ + zones_size[ZONE_DMA] = low - start_pfn; + zones_size[ZONE_NORMAL] = 0; + } else { + zones_size[ZONE_DMA] = low - start_pfn; + zones_size[ZONE_NORMAL] = low - max_dma; + } +#else + zones_size[ZONE_DMA] = 0 >> PAGE_SHIFT; + zones_size[ZONE_NORMAL] = __MEMORY_SIZE >> PAGE_SHIFT; + start_pfn = __MEMORY_START >> PAGE_SHIFT; +#endif /* CONFIG_MMU */ + + free_area_init_node(0, NODE_DATA(0), 0, zones_size, + start_pfn, 0); + + mem_map = contig_page_data.node_mem_map; + + return 0; +} +#else /* CONFIG_DISCONTIGMEM */ +extern unsigned long zone_sizes_init(void); +#endif /* CONFIG_DISCONTIGMEM */ + +/*======================================================================* + * paging_init() : sets up the page tables + *======================================================================*/ +void __init paging_init(void) +{ +#ifdef CONFIG_MMU + int i; + pgd_t *pg_dir; + + /* We don't need kernel mapping as hardware support that. */ + pg_dir = swapper_pg_dir; + + for (i = 0 ; i < USER_PTRS_PER_PGD * 2 ; i++) + pgd_val(pg_dir[i]) = 0; +#endif /* CONFIG_MMU */ + hole_pages = zone_sizes_init(); +} + +int __init reservedpages_count(void) +{ + int reservedpages, nid, i; + + reservedpages = 0; + for (nid = 0 ; nid < numnodes ; nid++) + for (i = 0 ; i < MAX_LOW_PFN(nid) - START_PFN(nid) ; i++) + if (PageReserved(NODE_DATA(nid)->node_mem_map + i)) + reservedpages++; + + return reservedpages; +} + +/*======================================================================* + * mem_init() : + * orig : arch/sh/mm/init.c + *======================================================================*/ +void __init mem_init(void) +{ + int codesize, reservedpages, datasize, initsize; + int nid; +#ifndef CONFIG_MMU + extern unsigned long memory_end; +#endif + + num_physpages = 0; + for (nid = 0 ; nid < numnodes ; nid++) + num_physpages += MAX_LOW_PFN(nid) - START_PFN(nid) + 1; + + num_physpages -= hole_pages; + +#ifndef CONFIG_DISCONTIGMEM + max_mapnr = num_physpages; +#endif /* CONFIG_DISCONTIGMEM */ + +#ifdef CONFIG_MMU + high_memory = (void *)__va(PFN_PHYS(MAX_LOW_PFN(0))); +#else + high_memory = (void *)(memory_end & PAGE_MASK); +#endif /* CONFIG_MMU */ + + /* clear the zero-page */ + memset(empty_zero_page, 0, PAGE_SIZE); + + /* this will put all low memory onto the freelists */ + for (nid = 0 ; nid < numnodes ; nid++) + totalram_pages += free_all_bootmem_node(NODE_DATA(nid)); + + reservedpages = reservedpages_count() - hole_pages; + codesize = (unsigned long) &_etext - (unsigned long)&_text; + datasize = (unsigned long) &_edata - (unsigned long)&_etext; + initsize = (unsigned long) &__init_end - (unsigned long)&__init_begin; + + printk(KERN_INFO "Memory: %luk/%luk available (%dk kernel code, " + "%dk reserved, %dk data, %dk init)\n", + (unsigned long) nr_free_pages() << (PAGE_SHIFT-10), + num_physpages << (PAGE_SHIFT-10), + codesize >> 10, + reservedpages << (PAGE_SHIFT-10), + datasize >> 10, + initsize >> 10); +} + +/*======================================================================* + * free_initmem() : + * orig : arch/sh/mm/init.c + *======================================================================*/ +void free_initmem(void) +{ + unsigned long addr; + + addr = (unsigned long)(&__init_begin); + for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { + ClearPageReserved(virt_to_page(addr)); + set_page_count(virt_to_page(addr), 1); + free_page(addr); + totalram_pages++; + } + printk (KERN_INFO "Freeing unused kernel memory: %dk freed\n", \ + (int)(&__init_end - &__init_begin) >> 10); +} + +#ifdef CONFIG_BLK_DEV_INITRD +/*======================================================================* + * free_initrd_mem() : + * orig : arch/sh/mm/init.c + *======================================================================*/ +void free_initrd_mem(unsigned long start, unsigned long end) +{ + unsigned long p; + for (p = start; p < end; p += PAGE_SIZE) { + ClearPageReserved(virt_to_page(p)); + set_page_count(virt_to_page(p), 1); + free_page(p); + totalram_pages++; + } + printk (KERN_INFO "Freeing initrd memory: %ldk freed\n", (end - start) >> 10); +} +#endif + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/mm/ioremap.c 2004-09-03 00:57:17.395394760 -0700 @@ -0,0 +1,188 @@ +/* + * linux/arch/m32r/mm/io_remap.c + * + * Copyright (c) 2001, 2002 Hiroyuki Kondo + * + * Taken from mips version. + * (C) Copyright 1995 1996 Linus Torvalds + * (C) Copyright 2001 Ralf Baechle + */ + +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, + unsigned long phys_addr, unsigned long flags) +{ + unsigned long end; + unsigned long pfn; + pgprot_t pgprot = __pgprot(_PAGE_GLOBAL | _PAGE_PRESENT | _PAGE_READ + | _PAGE_WRITE | flags); + + address &= ~PMD_MASK; + end = address + size; + if (end > PMD_SIZE) + end = PMD_SIZE; + if (address >= end) + BUG(); + pfn = phys_addr >> PAGE_SHIFT; + do { + if (!pte_none(*pte)) { + printk("remap_area_pte: page already exists\n"); + BUG(); + } + set_pte(pte, pfn_pte(pfn, pgprot)); + address += PAGE_SIZE; + pfn++; + pte++; + } while (address && (address < end)); +} + +static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, + unsigned long phys_addr, unsigned long flags) +{ + unsigned long end; + + address &= ~PGDIR_MASK; + end = address + size; + if (end > PGDIR_SIZE) + end = PGDIR_SIZE; + phys_addr -= address; + if (address >= end) + BUG(); + do { + pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address); + if (!pte) + return -ENOMEM; + remap_area_pte(pte, address, end - address, address + phys_addr, flags); + address = (address + PMD_SIZE) & PMD_MASK; + pmd++; + } while (address && (address < end)); + return 0; +} + +static int remap_area_pages(unsigned long address, unsigned long phys_addr, + unsigned long size, unsigned long flags) +{ + int error; + pgd_t * dir; + unsigned long end = address + size; + + phys_addr -= address; + dir = pgd_offset(&init_mm, address); + flush_cache_all(); + if (address >= end) + BUG(); + spin_lock(&init_mm.page_table_lock); + do { + pmd_t *pmd; + pmd = pmd_alloc(&init_mm, dir, address); + error = -ENOMEM; + if (!pmd) + break; + if (remap_area_pmd(pmd, address, end - address, + phys_addr + address, flags)) + break; + error = 0; + address = (address + PGDIR_SIZE) & PGDIR_MASK; + dir++; + } while (address && (address < end)); + spin_unlock(&init_mm.page_table_lock); + flush_tlb_all(); + return error; +} + +/* + * Generic mapping function (not visible outside): + */ + +/* + * Remap an arbitrary physical address space into the kernel virtual + * address space. Needed when the kernel wants to access high addresses + * directly. + * + * NOTE! We need to allow non-page-aligned mappings too: we will obviously + * have to convert them into an offset in a page-aligned mapping, but the + * caller shouldn't need to know that small detail. + */ + +#define IS_LOW512(addr) (!((unsigned long)(addr) & ~0x1fffffffUL)) + +void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) +{ + void * addr; + struct vm_struct * area; + unsigned long offset, last_addr; + + /* Don't allow wraparound or zero size */ + last_addr = phys_addr + size - 1; + if (!size || last_addr < phys_addr) + return NULL; + + /* + * Map objects in the low 512mb of address space using KSEG1, otherwise + * map using page tables. + */ + if (IS_LOW512(phys_addr) && IS_LOW512(phys_addr + size - 1)) + return (void *) KSEG1ADDR(phys_addr); + + /* + * Don't allow anybody to remap normal RAM that we're using.. + */ + if (phys_addr < virt_to_phys(high_memory)) { + char *t_addr, *t_end; + struct page *page; + + t_addr = __va(phys_addr); + t_end = t_addr + (size - 1); + + for(page = virt_to_page(t_addr); page <= virt_to_page(t_end); page++) + if(!PageReserved(page)) + return NULL; + } + + /* + * Mappings have to be page-aligned + */ + offset = phys_addr & ~PAGE_MASK; + phys_addr &= PAGE_MASK; + size = PAGE_ALIGN(last_addr + 1) - phys_addr; + + /* + * Ok, go for it.. + */ + area = get_vm_area(size, VM_IOREMAP); + if (!area) + return NULL; + area->phys_addr = phys_addr; + addr = area->addr; + if (remap_area_pages((unsigned long)addr, phys_addr, size, flags)) { + vunmap(addr); + return NULL; + } + + return (void *) (offset + (char *)addr); +} + +#define IS_KSEG1(addr) (((unsigned long)(addr) & ~0x1fffffffUL) == KSEG1) + +void iounmap(void *addr) +{ + if (!IS_KSEG1(addr)) + vfree((void *) (PAGE_MASK & (unsigned long) addr)); +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/mm/ioremap-nommu.c 2004-09-03 00:57:17.395394760 -0700 @@ -0,0 +1,51 @@ +/* + * linux/arch/m32r/mm/io_remap.c + * + * Copyright (c) 2001, 2002 Hiroyuki Kondo + * + * Taken from mips version. + * (C) Copyright 1995 1996 Linus Torvalds + * (C) Copyright 2001 Ralf Baechle + */ + +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + + +/* + * Remap an arbitrary physical address space into the kernel virtual + * address space. Needed when the kernel wants to access high addresses + * directly. + * + * NOTE! We need to allow non-page-aligned mappings too: we will obviously + * have to convert them into an offset in a page-aligned mapping, but the + * caller shouldn't need to know that small detail. + */ + +#define IS_LOW512(addr) (!((unsigned long)(addr) & ~0x1fffffffUL)) + +void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) +{ + return (void *)phys_addr; +} + +#define IS_KSEG1(addr) (((unsigned long)(addr) & ~0x1fffffffUL) == KSEG1) + +void iounmap(void *addr) +{ +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/mm/Makefile 2004-09-03 00:57:17.395394760 -0700 @@ -0,0 +1,12 @@ +# +# Makefile for the Linux M32R-specific parts of the memory manager. +# + +ifdef CONFIG_MMU +obj-y := init.o fault.o mmu.o extable.o ioremap.o cache.o page.o +else +obj-y := init.o fault-nommu.o mmu.o extable.o ioremap-nommu.o cache.o page.o +endif + +obj-$(CONFIG_DISCONTIGMEM) += discontig.o + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/mm/mmu.S 2004-09-03 00:57:17.397394456 -0700 @@ -0,0 +1,350 @@ +/* + * linux/arch/m32r/mm/mmu.S + * + * Copyright (C) 2001 by Hiroyuki Kondo + */ + +/* $Id: mmu.S,v 1.15 2004/03/16 02:56:27 takata Exp $ */ + +#include /* CONFIG_MMU */ +#include +#include +#include + + .text +#ifdef CONFIG_MMU + +#include +#include +#include +#include + +/* + * TLB Miss Exception handler + */ + .balign 16 +ENTRY(tme_handler) + .global tlb_entry_i_dat + .global tlb_entry_d_dat + + SWITCH_TO_KERNEL_STACK + +#if defined(CONFIG_ISA_M32R2) + st r0, @-sp + st r1, @-sp + st r2, @-sp + st r3, @-sp + + seth r3, #high(MMU_REG_BASE) + ld r1, @(MESTS_offset, r3) ; r1: status (MESTS reg.) + ld r0, @(MDEVP_offset, r3) ; r0: PFN + ASID (MDEVP reg.) + st r1, @(MESTS_offset, r3) ; clear status (MESTS reg.) + and3 r1, r1, #(MESTS_IT) + bnez r1, 1f ; instruction TLB miss? + +;; data TLB miss +;; input +;; r0: PFN + ASID (MDEVP reg.) +;; r1 - r3: free +;; output +;; r0: PFN + ASID +;; r1: TLB entry base address +;; r2: &tlb_entry_{i|d}_dat +;; r3: free + +#ifndef CONFIG_SMP + seth r2, #high(tlb_entry_d_dat) + or3 r2, r2, #low(tlb_entry_d_dat) +#else /* CONFIG_SMP */ + ldi r1, #-8192 + seth r2, #high(tlb_entry_d_dat) + or3 r2, r2, #low(tlb_entry_d_dat) + and r1, sp + ld r1, @(16, r1) ; current_thread_info->cpu + slli r1, #2 + add r2, r1 +#endif /* !CONFIG_SMP */ + seth r1, #high(DTLB_BASE) + or3 r1, r1, #low(DTLB_BASE) + bra 2f + + .balign 16 + .fillinsn +1: +;; instrucntion TLB miss +;; input +;; r0: MDEVP reg. (included ASID) +;; r1 - r3: free +;; output +;; r0: PFN + ASID +;; r1: TLB entry base address +;; r2: &tlb_entry_{i|d}_dat +;; r3: free + ldi r3, #-4096 + and3 r0, r0, #(MMU_CONTEXT_ASID_MASK) + mvfc r1, bpc + and r1, r3 + or r0, r1 ; r0: PFN + ASID +#ifndef CONFIG_SMP + seth r2, #high(tlb_entry_i_dat) + or3 r2, r2, #low(tlb_entry_i_dat) +#else /* CONFIG_SMP */ + ldi r1, #-8192 + seth r2, #high(tlb_entry_i_dat) + or3 r2, r2, #low(tlb_entry_i_dat) + and r1, sp + ld r1, @(16, r1) ; current_thread_info->cpu + slli r1, #2 + add r2, r1 +#endif /* !CONFIG_SMP */ + seth r1, #high(ITLB_BASE) + or3 r1, r1, #low(ITLB_BASE) + + .fillinsn +2: +;; select TLB entry +;; input +;; r0: PFN + ASID +;; r1: TLB entry base address +;; r2: &tlb_entry_{i|d}_dat +;; r3: free +;; output +;; r0: PFN + ASID +;; r1: TLB entry address +;; r2, r3: free +#ifdef CONFIG_ISA_DUAL_ISSUE + ld r3, @r2 || srli r1, #3 +#else + ld r3, @r2 + srli r1, #3 +#endif + add r1, r3 + ; tlb_entry_{d|i}_dat++; + addi r3, #1 + and3 r3, r3, #(NR_TLB_ENTRIES - 1) +#ifdef CONFIG_ISA_DUAL_ISSUE + st r3, @r2 || slli r1, #3 +#else + st r3, @r2 + slli r1, #3 +#endif + +;; load pte +;; input +;; r0: PFN + ASID +;; r1: TLB entry address +;; r2, r3: free +;; output +;; r0: PFN + ASID +;; r1: TLB entry address +;; r2: pte_data +;; r3: free + ; pgd = *(unsigned long *)MPTB; + ld24 r2, #(-MPTB - 1) + srl3 r3, r0, #22 +#ifdef CONFIG_ISA_DUAL_ISSUE + not r2, r2 || slli r3, #2 ; r3: pgd offset +#else + not r2, r2 + slli r3, #2 +#endif + ld r2, @r2 ; r2: pgd base addr (MPTB reg.) + or r3, r2 ; r3: pmd addr + + ; pmd = pmd_offset(pgd, address); + ld r3, @r3 ; r3: pmd data + ldi r2, #-4096 + beqz r3, 3f ; pmd_none(*pmd) ? + + ; pte = pte_offset(pmd, address); + and r2, r3 ; r2: pte base addr + srl3 r3, r0, #10 + and3 r3, r3, #0xffc ; r3: pte offset + or r3, r2 + seth r2, #0x8000 + or r3, r2 ; r3: pte addr + + ; pte_data = (unsigned long)pte_val(*pte); + ld r2, @r3 ; r2: pte data + or3 r2, r2, #2 ; _PAGE_PRESENT(=2) + + .fillinsn +5: +;; set tlb +;; input +;; r0: PFN + ASID +;; r1: TLB entry address +;; r2: pte_data +;; r3: free + st r0, @r1 ; set_tlb_tag(entry++, address); + st r2, @+r1 ; set_tlb_data(entry, pte_data); + + .fillinsn +6: + ld r3, @sp+ + ld r2, @sp+ + ld r1, @sp+ + ld r0, @sp+ + rte + + .fillinsn +3: +;; error +;; input +;; r0: PFN + ASID +;; r1: TLB entry address +;; r2, r3: free +;; output +;; r0: PFN + ASID +;; r1: TLB entry address +;; r2: pte_data +;; r3: free +#ifdef CONFIG_ISA_DUAL_ISSUE + bra 5b || ldi r2, #2 +#else + ldi r2, #2 ; r2: pte_data = 0 | _PAGE_PRESENT(=2) + bra 5b +#endif + +#elif defined (CONFIG_ISA_M32R) + + st sp, @-sp + st r0, @-sp + st r1, @-sp + st r2, @-sp + st r3, @-sp + st r4, @-sp + + seth r3, #high(MMU_REG_BASE) + ld r0, @(MDEVA_offset,r3) ; r0: address (MDEVA reg.) + mvfc r2, bpc ; r2: bpc + ld r1, @(MESTS_offset,r3) ; r1: status (MESTS reg.) + st r1, @(MESTS_offset,r3) ; clear status (MESTS reg.) + and3 r1, r1, #(MESTS_IT) + beqz r1, 1f ; data TLB miss? + +;; instrucntion TLB miss + mv r0, r2 ; address = bpc; + ; entry = (unsigned long *)ITLB_BASE+tlb_entry_i*2; + seth r3, #shigh(tlb_entry_i_dat) + ld r4, @(low(tlb_entry_i_dat),r3) + sll3 r2, r4, #3 + seth r1, #high(ITLB_BASE) + or3 r1, r1, #low(ITLB_BASE) + add r2, r1 ; r2: entry + addi r4, #1 ; tlb_entry_i++; + and3 r4, r4, #(NR_TLB_ENTRIES-1) + st r4, @(low(tlb_entry_i_dat),r3) + bra 2f + .fillinsn +1: +;; data TLB miss + ; entry = (unsigned long *)DTLB_BASE+tlb_entry_d*2; + seth r3, #shigh(tlb_entry_d_dat) + ld r4, @(low(tlb_entry_d_dat),r3) + sll3 r2, r4, #3 + seth r1, #high(DTLB_BASE) + or3 r1, r1, #low(DTLB_BASE) + add r2, r1 ; r2: entry + addi r4, #1 ; tlb_entry_d++; + and3 r4, r4, #(NR_TLB_ENTRIES-1) + st r4, @(low(tlb_entry_d_dat),r3) + .fillinsn +2: +;; load pte +; r0: address, r2: entry +; r1,r3,r4: (free) + ; pgd = *(unsigned long *)MPTB; + ld24 r1, #(-MPTB-1) + not r1, r1 + ld r1, @r1 + srl3 r4, r0, #22 + sll3 r3, r4, #2 + add r3, r1 ; r3: pgd + ; pmd = pmd_offset(pgd, address); + ld r1, @r3 ; r1: pmd + beqz r1, 3f ; pmd_none(*pmd) ? +; + and3 r1, r1, #0xeff + ldi r4, #611 ; _KERNPG_TABLE(=611) + beq r1, r4, 4f ; !pmd_bad(*pmd) ? + .fillinsn +3: + ldi r1, #0 ; r1: pte_data = 0 + bra 5f + .fillinsn +4: + ; pte = pte_offset(pmd, address); + ld r4, @r3 ; r4: pte + ldi r3, #-4096 + and r4, r3 + srl3 r3, r0, #10 + and3 r3, r3, #0xffc + add r4, r3 + seth r3, #0x8000 + add r4, r3 ; r4: pte + ; pte_data = (unsigned long)pte_val(*pte); + ld r1, @r4 ; r1: pte_data + .fillinsn + +;; set tlb +; r0: address, r1: pte_data, r2: entry +; r3,r4: (free) +5: + ldi r3, #-4096 ; set_tlb_tag(entry++, address); + and r3, r0 + seth r4, #shigh(MASID) + ld r4, @(low(MASID),r4) ; r4: MASID + and3 r4, r4, #(MMU_CONTEXT_ASID_MASK) + or r3, r4 + st r3, @r2 + or3 r4, r1, #2 ; _PAGE_PRESENT(=2) + st r4, @(4,r2) ; set_tlb_data(entry, pte_data); + + ld r4, @sp+ + ld r3, @sp+ + ld r2, @sp+ + ld r1, @sp+ + ld r0, @sp+ + ld sp, @sp+ + rte + +#else +#error unknown isa configuration +#endif + +ENTRY(init_tlb) +;; Set MMU Register + seth r0, #high(MMU_REG_BASE) ; Set MMU_REG_BASE higher + or3 r0, r0, #low(MMU_REG_BASE) ; Set MMU_REG_BASE lower + ldi r1, #0 + st r1, @(MPSZ_offset,r0) ; Set MPSZ Reg(Page size 4KB:0 16KB:1 64KB:2) + ldi r1, #0 + st r1, @(MASID_offset,r0) ; Set ASID Zero + +;; Set TLB + seth r0, #high(ITLB_BASE) ; Set ITLB_BASE higher + or3 r0, r0, #low(ITLB_BASE) ; Set ITLB_BASE lower + seth r1, #high(DTLB_BASE) ; Set DTLB_BASE higher + or3 r1, r1, #low(DTLB_BASE) ; Set DTLB_BASE lower + ldi r2, #0 + ldi r3, #NR_TLB_ENTRIES + addi r0, #-4 + addi r1, #-4 +clear_tlb: + st r2, @+r0 ; VPA <- 0 + st r2, @+r0 ; PPA <- 0 + st r2, @+r1 ; VPA <- 0 + st r2, @+r1 ; PPA <- 0 + addi r3, #-1 + bnez r3, clear_tlb +;; + jmp r14 + +ENTRY(m32r_itlb_entrys) +ENTRY(m32r_otlb_entrys) + +#endif /* CONFIG_MMU */ + +.end + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/mm/page.S 2004-09-03 00:57:17.397394456 -0700 @@ -0,0 +1,82 @@ +/* + * linux/arch/m32r/mm/page.S + * + * Clear/Copy page with CPU + * + * Copyright (C) 2004 The Free Software Initiative of Japan + * + * Written by Niibe Yutaka + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + */ + .text + .global copy_page + /* + * copy_page (to, from) + * + * PAGE_SIZE = 4096-byte + * Cache line = 16-byte + * 16 * 256 + */ + .align 4 +copy_page: + ldi r2, #255 + ld r3, @r0 /* cache line allocate */ + ld r4, @r1+ + ld r5, @r1+ + ld r6, @r1+ + ld r7, @r1+ + .fillinsn +0: + st r4, @r0 + st r5, @+r0 + st r6, @+r0 + st r7, @+r0 + ld r4, @r1+ + addi r0, #4 + ld r5, @r1+ + ld r6, @r1+ + ld r7, @r1+ + ld r3, @r0 /* cache line allocate */ + addi r2, #-1 + bnez r2, 0b + + st r4, @r0 + st r5, @+r0 + st r6, @+r0 + st r7, @+r0 + jmp r14 + + .text + .global clear_page + /* + * clear_page (to) + * + * PAGE_SIZE = 4096-byte + * Cache line = 16-byte + * 16 * 256 + */ + .align 4 +clear_page: + ldi r2, #255 + ldi r4, #0 + ld r3, @r0 /* cache line allocate */ + .fillinsn +0: + st r4, @r0 + st r4, @+r0 + st r4, @+r0 + st r4, @+r0 + addi r0, #4 + ld r3, @r0 /* cache line allocate */ + addi r2, #-1 + bnez r2, 0b + + st r4, @r0 + st r4, @+r0 + st r4, @+r0 + st r4, @+r0 + jmp r14 --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/oaks32r/defconfig.nommu 2004-09-03 00:57:18.734191232 -0700 @@ -0,0 +1,521 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_M32R=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_HOTPLUG=y +# CONFIG_IKCONFIG is not set +CONFIG_EMBEDDED=y +# CONFIG_KALLSYMS is not set +# CONFIG_FUTEX is not set +# CONFIG_EPOLL is not set +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# Processor type and features +# +# CONFIG_PLAT_MAPPI is not set +# CONFIG_PLAT_USRV is not set +# CONFIG_PLAT_M32700UT is not set +# CONFIG_PLAT_OPSPUT is not set +CONFIG_PLAT_OAKS32R=y +# CONFIG_PLAT_MAPPI2 is not set +# CONFIG_CHIP_M32700 is not set +CONFIG_CHIP_M32102=y +# CONFIG_CHIP_VDEC2 is not set +# CONFIG_CHIP_OPSP is not set +CONFIG_ISA_M32R=y +CONFIG_BUS_CLOCK=33333333 +CONFIG_TIMER_DIVIDE=128 +# CONFIG_CPU_LITTLE_ENDIAN is not set +CONFIG_MEMORY_START=0x01000000 +CONFIG_MEMORY_SIZE=0x00800000 +CONFIG_NOHIGHMEM=y +# CONFIG_DISCONTIGMEM is not set +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +CONFIG_PREEMPT=y +# CONFIG_HAVE_DEC_LOCK is not set +# CONFIG_SMP is not set + +# +# M32R drivers +# +CONFIG_M32R_NE2000=y + +# +# Power management options (ACPI, APM) +# +# CONFIG_PM is not set + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +# CONFIG_PCI is not set +# CONFIG_ISA is not set + +# +# PCMCIA/CardBus support +# +# CONFIG_PCMCIA is not set + +# +# PCI Hotplug Support +# + +# +# Executable file formats +# +CONFIG_BINFMT_FLAT=y +# CONFIG_BINFMT_ZFLAT is not set +# CONFIG_BINFMT_SHARED_FLAT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set + +# +# ATA/ATAPI/MFM/RLL support +# +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_SCSI is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_NETDEVICES is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_M32R_SIO=y +CONFIG_SERIAL_M32R_SIO_CONSOLE=y +# CONFIG_SERIAL_M32R_PLDSIO is not set +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_QIC02_TAPE is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_AGP is not set +# CONFIG_DRM is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +# CONFIG_DEVFS_FS is not set +CONFIG_DEVPTS_FS_XATTR=y +CONFIG_DEVPTS_FS_SECURITY=y +# CONFIG_TMPFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_FRAME_POINTER is not set + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/oaks32r/dot.gdbinit.nommu 2004-09-03 00:57:17.400394000 -0700 @@ -0,0 +1,155 @@ +# .gdbinit file +# $Id: dot.gdbinit.oaks32r,v 1.2 2004/04/15 02:33:14 takata Exp $ +#----- +# NOTE: this file is generated by a script, "gen_gdbinit.pl". +# (Please type "gen_gdbinit.pl --help" and check the help message). +# $ Id: gen_gdbinit.pl,v 1.10 2004/04/15 02:10:45 takata Exp $ +#----- +# target platform: oaks32r + +# setting +set width 0d70 +set radix 0d16 + +# clk xin:cpu:bus=16:66:33 +define clock_init + set *(unsigned long *)0x00ef4008 = 1 + shell sleep 0.1 + set *(unsigned long *)0x00ef4000 = 0x00020100 +end + +# Initialize programmable ports +define port_init + set *(unsigned long *)0x00ef1000 = 0x1 + set *(unsigned long *)0x00ef1060 = 0x01400001 + set *(unsigned long *)0x00ef1064 = 0x00015555 + set *(unsigned long *)0x00ef1068 = 0x55555050 + set *(unsigned long *)0x00ef106c = 0x05150040 +end + +# Initialize SDRAM controller +define sdram_init + set *(unsigned long *)0x00ef6008 = 0x00000182 + set *(unsigned long *)0x00ef600c = 0x00000001 + shell sleep 0.1 + set *(unsigned long *)0x00ef602c = 0x00000010 + set *(unsigned long *)0x00ef6028 = 0x00000300 + set *(unsigned long *)0x00ef6048 = 0x00000001 + set *(unsigned long *)0x00ef6020 = 0x01000041 + set *(unsigned long *)0x00ef6004 = 0x00010117 + set *(unsigned long *)0x00ef6010 = 0x00000001 + set *(unsigned long *)0x00ef6024 = 0x00000001 +end +document sdram_init + SDRAM controller initialization + 0x01000000 - 0x017fffff (8MB) +end + +# Initialize LAN controller +define lanc_init + set *(unsigned long *)0x00ef5008 = 0x03031303 + #RST DRV (P64) + set *(unsigned char *)0x00ef1046 = 0x08 + set *(unsigned char *)0x00ef1026 = 0xff + set *(unsigned char *)0x00ef1026 = 0x00 + set *(unsigned short *)0x02000630 = 0xffff +end + +# Show current task structure +define show_current + set $current = $spi & 0xffffe000 + printf "$current=0x%08lX\n",$current + print *(struct task_struct *)$current +end + +# Show user assigned task structure +define show_task + set = $arg0 & 0xffffe000 + printf "$task=0x%08lX\n",$task + print *(struct task_struct *)$task +end +document show_task + Show user assigned task structure + arg0 : task structure address +end + +# Show M32R registers +define show_regs + printf " R0[0x%08lX] R1[0x%08lX] R2[0x%08lX] R3[0x%08lX]\n",$r0,$r1,$r2,$r3 + printf " R4[0x%08lX] R5[0x%08lX] R6[0x%08lX] R7[0x%08lX]\n",$r4,$r5,$r6,$r7 + printf " R8[0x%08lX] R9[0x%08lX] R10[0x%08lX] R11[0x%08lX]\n",$r8,$r9,$r10,$r11 + printf "R12[0x%08lX] FP[0x%08lX] LR[0x%08lX] SP[0x%08lX]\n",$r12,$fp,$lr,$sp + printf "PSW[0x%08lX] CBR[0x%08lX] SPI[0x%08lX] SPU[0x%08lX]\n",$psw,$cbr,$spi,$spu + printf "BPC[0x%08lX] PC[0x%08lX] ACCL[0x%08lX] ACCH[0x%08lX]\n",$bpc,$pc,$accl,$acch +end + +# Setup all +define setup + use_mon_code + set *(unsigned int)0xfffffffc=0x60 + shell sleep 0.1 + clock_init + shell sleep 0.1 + port_init + sdram_init + lanc_init +end + +# Load modules +define load_modules + use_debug_dma + load +end + +# Set kernel parameters +define set_kernel_parameters + set $param = (void*)0x01002000 + # INITRD_START + set *(unsigned long *)($param + 0x0010) = 0x00000000 + # INITRD_SIZE + set *(unsigned long *)($param + 0x0014) = 0x00000000 + # M32R_CPUCLK + set *(unsigned long *)($param + 0x0018) = 0d66666667 + # M32R_BUSCLK + set *(unsigned long *)($param + 0x001c) = 0d33333333 + + # M32R_TIMER_DIVIDE + set *(unsigned long *)($param + 0x0020) = 0d128 + +# set {char[0x200]}($param + 0x100) = "console=ttyD0,115200n8x root=/dev/nfsroot nfsroot=192.168.0.1:/project/m32r-linux/export/rootfs nfsaddrs=192.168.0.101:192.168.0.1:192.168.0.1:255.255.255.0:mappi001 \0" + set {char[0x200]}($param + 0x100) = "console=ttyD0,115200n8x root=/dev/nfsroot nfsroot=192.168.0.1:/project/m32r-linux/export/root.busybox.flat nfsaddrs=192.168.0.101:192.168.0.1:192.168.0.1:255.255.255.0:mappi001 \0" +end + +# Boot +define boot + set_kernel_parameters + set $fp = 0 + set $pc = 0x01001000 + si + c +end + +# Set breakpoints +define set_breakpoints + b *0x00000020 + b *0x00000030 +end + +# Restart +define restart + sdireset + sdireset + setup + load_modules + boot +end + +sdireset +sdireset +file vmlinux +target m32rsdi +setup +#load_modules +#set_breakpoints +#boot + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/oprofile/init.c 2004-09-03 00:57:17.400394000 -0700 @@ -0,0 +1,25 @@ +/** + * @file init.c + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + */ + +#include +#include +#include +#include + +extern void timer_init(struct oprofile_operations ** ops); + +int __init oprofile_arch_init(struct oprofile_operations ** ops) +{ + return -ENODEV; +} + + +void oprofile_arch_exit(void) +{ +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/oprofile/Kconfig 2004-09-03 00:57:17.400394000 -0700 @@ -0,0 +1,23 @@ + +menu "Profiling support" + depends on EXPERIMENTAL + +config PROFILING + bool "Profiling support (EXPERIMENTAL)" + help + Say Y here to enable the extended profiling support mechanisms used + by profilers such as OProfile. + + +config OPROFILE + tristate "OProfile system profiling (EXPERIMENTAL)" + depends on PROFILING + help + OProfile is a profiling system capable of profiling the + whole system, include the kernel, kernel modules, libraries, + and applications. + + If unsure, say N. + +endmenu + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/oprofile/Makefile 2004-09-03 00:57:17.401393848 -0700 @@ -0,0 +1,9 @@ +obj-$(CONFIG_OPROFILE) += oprofile.o + +DRIVER_OBJS := $(addprefix ../../../drivers/oprofile/, \ + oprof.o cpu_buffer.o buffer_sync.o \ + event_buffer.o oprofile_files.o \ + oprofilefs.o oprofile_stats.o \ + timer_int.o ) + +oprofile-y := $(DRIVER_OBJS) init.o --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/opsput/defconfig.opsput 2004-09-03 00:57:18.735191080 -0700 @@ -0,0 +1,598 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_M32R=y +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_HOTPLUG=y +CONFIG_IKCONFIG=y +# CONFIG_IKCONFIG_PROC is not set +CONFIG_EMBEDDED=y +# CONFIG_KALLSYMS is not set +# CONFIG_FUTEX is not set +# CONFIG_EPOLL is not set +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# Processor type and features +# +# CONFIG_PLAT_MAPPI is not set +# CONFIG_PLAT_USRV is not set +# CONFIG_PLAT_M32700UT is not set +CONFIG_PLAT_OPSPUT=y +# CONFIG_PLAT_OAKS32R is not set +# CONFIG_PLAT_MAPPI2 is not set +# CONFIG_CHIP_M32700 is not set +# CONFIG_CHIP_M32102 is not set +# CONFIG_CHIP_VDEC2 is not set +CONFIG_CHIP_OPSP=y +CONFIG_MMU=y +CONFIG_TLB_ENTRIES=32 +CONFIG_ISA_M32R2=y +CONFIG_ISA_DSP_LEVEL2=y +CONFIG_ISA_DUAL_ISSUE=y +CONFIG_BUS_CLOCK=50000000 +CONFIG_TIMER_DIVIDE=128 +# CONFIG_CPU_LITTLE_ENDIAN is not set +CONFIG_MEMORY_START=0x08000000 +CONFIG_MEMORY_SIZE=0x01000000 +CONFIG_NOHIGHMEM=y +# CONFIG_DISCONTIGMEM is not set +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +# CONFIG_PREEMPT is not set +# CONFIG_SMP is not set + +# +# M32R drivers +# +# CONFIG_M32R_CFC is not set +CONFIG_M32R_SMC91111=y +CONFIG_M32700UT_DS1302=y + +# +# Power management options (ACPI, APM) +# +# CONFIG_PM is not set + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +# CONFIG_PCI is not set +# CONFIG_ISA is not set + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=y +# CONFIG_PCMCIA_DEBUG is not set +# CONFIG_TCIC is not set + +# +# PCI Hotplug Support +# + +# +# Executable file formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set +# CONFIG_DEBUG_DRIVER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_INITRD is not set + +# +# ATA/ATAPI/MFM/RLL support +# +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI=m +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=m +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=m +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_CHR_DEV_SG=m + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI Transport Attributes +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_DEBUG is not set + +# +# PCMCIA SCSI adapter support +# +# CONFIG_PCMCIA_AHA152X is not set +# CONFIG_PCMCIA_FDOMAIN is not set +# CONFIG_PCMCIA_NINJA_SCSI is not set +# CONFIG_PCMCIA_QLOGIC is not set +# CONFIG_PCMCIA_SYM53C500 is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_NETDEVICES is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Telephony Support +# +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_M32R_SIO is not set +CONFIG_SERIAL_M32R_PLDSIO=y +CONFIG_SERIAL_M32R_PLDSIO_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_QIC02_TAPE is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +CONFIG_EXT3_FS=m +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +CONFIG_JBD=m +CONFIG_JBD_DEBUG=y +CONFIG_FS_MBCACHE=y +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_REISERFS_FS_XATTR is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +# CONFIG_ZISOFS is not set +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=m +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_STACKOVERFLOW is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_IOVIRT is not set +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_FRAME_POINTER is not set + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/m32r/opsput/dot.gdbinit 2004-09-03 00:57:17.404393392 -0700 @@ -0,0 +1,180 @@ +# .gdbinit file +# $Id: dot.gdbinit,v 1.1 2004/07/27 06:54:20 sakugawa Exp $ + +# setting +set width 0d70 +set radix 0d16 +set height 0 +debug_chaos + +define tlb_init + set $tlbbase = 0xfe000000 + set *(unsigned long *)($tlbbase + 0x04) = 0x0 + set *(unsigned long *)($tlbbase + 0x0c) = 0x0 + set *(unsigned long *)($tlbbase + 0x14) = 0x0 + set *(unsigned long *)($tlbbase + 0x1c) = 0x0 + set *(unsigned long *)($tlbbase + 0x24) = 0x0 + set *(unsigned long *)($tlbbase + 0x2c) = 0x0 + set *(unsigned long *)($tlbbase + 0x34) = 0x0 + set *(unsigned long *)($tlbbase + 0x3c) = 0x0 + set *(unsigned long *)($tlbbase + 0x44) = 0x0 + set *(unsigned long *)($tlbbase + 0x4c) = 0x0 + set *(unsigned long *)($tlbbase + 0x54) = 0x0 + set *(unsigned long *)($tlbbase + 0x5c) = 0x0 + set *(unsigned long *)($tlbbase + 0x64) = 0x0 + set *(unsigned long *)($tlbbase + 0x6c) = 0x0 + set *(unsigned long *)($tlbbase + 0x74) = 0x0 + set *(unsigned long *)($tlbbase + 0x7c) = 0x0 + set *(unsigned long *)($tlbbase + 0x84) = 0x0 + set *(unsigned long *)($tlbbase + 0x8c) = 0x0 + set *(unsigned long *)($tlbbase + 0x94) = 0x0 + set *(unsigned long *)($tlbbase + 0x9c) = 0x0 + set *(unsigned long *)($tlbbase + 0xa4) = 0x0 + set *(unsigned long *)($tlbbase + 0xac) = 0x0 + set *(unsigned long *)($tlbbase + 0xb4) = 0x0 + set *(unsigned long *)($tlbbase + 0xbc) = 0x0 + set *(unsigned long *)($tlbbase + 0xc4) = 0x0 + set *(unsigned long *)($tlbbase + 0xcc) = 0x0 + set *(unsigned long *)($tlbbase + 0xd4) = 0x0 + set *(unsigned long *)($tlbbase + 0xdc) = 0x0 + set *(unsigned long *)($tlbbase + 0xe4) = 0x0 + set *(unsigned long *)($tlbbase + 0xec) = 0x0 + set *(unsigned long *)($tlbbase + 0xf4) = 0x0 + set *(unsigned long *)($tlbbase + 0xfc) = 0x0 + set $tlbbase = 0xfe000800 + set *(unsigned long *)($tlbbase + 0x04) = 0x0 + set *(unsigned long *)($tlbbase + 0x0c) = 0x0 + set *(unsigned long *)($tlbbase + 0x14) = 0x0 + set *(unsigned long *)($tlbbase + 0x1c) = 0x0 + set *(unsigned long *)($tlbbase + 0x24) = 0x0 + set *(unsigned long *)($tlbbase + 0x2c) = 0x0 + set *(unsigned long *)($tlbbase + 0x34) = 0x0 + set *(unsigned long *)($tlbbase + 0x3c) = 0x0 + set *(unsigned long *)($tlbbase + 0x44) = 0x0 + set *(unsigned long *)($tlbbase + 0x4c) = 0x0 + set *(unsigned long *)($tlbbase + 0x54) = 0x0 + set *(unsigned long *)($tlbbase + 0x5c) = 0x0 + set *(unsigned long *)($tlbbase + 0x64) = 0x0 + set *(unsigned long *)($tlbbase + 0x6c) = 0x0 + set *(unsigned long *)($tlbbase + 0x74) = 0x0 + set *(unsigned long *)($tlbbase + 0x7c) = 0x0 + set *(unsigned long *)($tlbbase + 0x84) = 0x0 + set *(unsigned long *)($tlbbase + 0x8c) = 0x0 + set *(unsigned long *)($tlbbase + 0x94) = 0x0 + set *(unsigned long *)($tlbbase + 0x9c) = 0x0 + set *(unsigned long *)($tlbbase + 0xa4) = 0x0 + set *(unsigned long *)($tlbbase + 0xac) = 0x0 + set *(unsigned long *)($tlbbase + 0xb4) = 0x0 + set *(unsigned long *)($tlbbase + 0xbc) = 0x0 + set *(unsigned long *)($tlbbase + 0xc4) = 0x0 + set *(unsigned long *)($tlbbase + 0xcc) = 0x0 + set *(unsigned long *)($tlbbase + 0xd4) = 0x0 + set *(unsigned long *)($tlbbase + 0xdc) = 0x0 + set *(unsigned long *)($tlbbase + 0xe4) = 0x0 + set *(unsigned long *)($tlbbase + 0xec) = 0x0 + set *(unsigned long *)($tlbbase + 0xf4) = 0x0 + set *(unsigned long *)($tlbbase + 0xfc) = 0x0 +end + +define load_modules + use_debug_dma + load +end + +# Set kernel parameters +define set_kernel_parameters + set $param = (void*)0x88002000 + # INITRD_START +# set *(unsigned long *)($param + 0x0010) = 0x08300000 + # INITRD_SIZE +# set *(unsigned long *)($param + 0x0014) = 0x00400000 + # M32R_CPUCLK + set *(unsigned long *)($param + 0x0018) = 0d200000000 + # M32R_BUSCLK + set *(unsigned long *)($param + 0x001c) = 0d50000000 +# set *(unsigned long *)($param + 0x001c) = 0d25000000 + + # M32R_TIMER_DIVIDE + set *(unsigned long *)($param + 0x0020) = 0d128 + + set {char[0x200]}($param + 0x100) = "console=ttyD0,115200n8x\ + root=/dev/nfsroot \ + nfsroot=192.168.0.1:/project/m32r-linux/export/root.2.6 \ + nfsaddrs=192.168.0.101:192.168.0.1:192.168.0.1:255.255.255.0:mappi001 \ + mem=16m \0" +end + +define boot + set_kernel_parameters + set $pc=0x88001000 + set $fp=0 + set $evb=0x88000000 + # I/D-Cache ON + +# IPI +# set *(long *)0x00eff2f8 = 0x2 + set $fp=0 +# set *(unsigned long *)0xa0ef4000 = 0x100 + si +end + +# Show TLB entries +define show_tlb_entries + set $i = 0 + set $addr = $arg0 + use_mon_code + while ($i < 0d32 ) + set $tlb_tag = *(unsigned long*)$addr + set $tlb_data = *(unsigned long*)($addr + 4) + printf " [%2d] 0x%08lx : 0x%08lx - 0x%08lx\n", $i, $addr, $tlb_tag, $tlb_data + set $i = $i + 1 + set $addr = $addr + 8 + end +# use_debug_dma +end +define itlb + set $itlb=0xfe000000 + show_tlb_entries $itlb +end +define dtlb + set $dtlb=0xfe000800 + show_tlb_entries $dtlb +end + +define show_regs + printf " R0[%08lx] R1[%08lx] R2[%08lx] R3[%08lx]\n",$r0,$r1,$r2,$r3 + printf " R4[%08lx] R5[%08lx] R6[%08lx] R7[%08lx]\n",$r4,$r5,$r6,$r7 + printf " R8[%08lx] R9[%08lx] R10[%08lx] R11[%08lx]\n",$r8,$r9,$r10,$r11 + printf "R12[%08lx] FP[%08lx] LR[%08lx] SP[%08lx]\n",$r12,$fp,$lr,$sp + printf "PSW[%08lx] CBR[%08lx] SPI[%08lx] SPU[%08lx]\n",$psw,$cbr,$spi,$spu + printf "BPC[%08lx] PC[%08lx] ACCL[%08lx] ACCH[%08lx]\n",$bpc,$pc,$accl,$acch + printf "EVB[%08lx]\n",$evb +end + +define setup + debug_chaos + set *(unsigned long *)0xa0ef6004 = 0x0001053f + set *(unsigned long *)0xa0ef6028 = 0x00031102 +# set *(unsigned long *)0xa0ef400c = 0x2 +end + +sdireset +sdireset +file vmlinux +target m32rsdi +set $pc=0x0 +b *0x30000 +c +setup +tlb_init +load_modules +#set *(long *)0xa0ef4000=0x101 +#set *(long *)0xa0ef400c=0x002 + +boot +#b tme_handler +b *0x88000020 + + + + --- linux-2.6.9-rc1/arch/m68k/kernel/m68k_ksyms.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/m68k/kernel/m68k_ksyms.c 2004-09-03 00:56:40.982930296 -0700 @@ -16,7 +16,6 @@ #include #include #include -#include asmlinkage long long __ashldi3 (long long, int); asmlinkage long long __ashrdi3 (long long, int); --- linux-2.6.9-rc1/arch/m68k/kernel/process.c 2004-08-24 00:03:49.000000000 -0700 +++ 25/arch/m68k/kernel/process.c 2004-09-02 20:51:51.000000000 -0700 @@ -232,7 +232,7 @@ asmlinkage int m68k_clone(struct pt_regs child_tidptr = (int *)regs->d4; if (!newsp) newsp = rdusp(); - return do_fork(clone_flags & ~CLONE_IDLETASK, newsp, regs, 0, + return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr); } --- linux-2.6.9-rc1/arch/m68k/kernel/ptrace.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/m68k/kernel/ptrace.c 2004-09-03 00:56:56.198617160 -0700 @@ -379,11 +379,8 @@ asmlinkage void syscall_trace(void) if (!current->thread.work.delayed_trace && !current->thread.work.syscall_trace) return; - current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0); - current->state = TASK_STOPPED; - notify_parent(current, SIGCHLD); - schedule(); + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); /* * this isn't the same as continuing with a signal, but it will do * for normal use. strace only continues with a signal if the --- linux-2.6.9-rc1/arch/m68k/kernel/signal.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/arch/m68k/kernel/signal.c 2004-09-02 20:51:51.000000000 -0700 @@ -842,9 +842,7 @@ adjust_stack: return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); goto adjust_stack; } @@ -925,9 +923,7 @@ adjust_stack: return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); goto adjust_stack; } --- linux-2.6.9-rc1/arch/m68k/kernel/time.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/m68k/kernel/time.c 2004-09-02 20:51:51.000000000 -0700 @@ -38,24 +38,6 @@ static inline int set_rtc_mmss(unsigned return -1; } -static inline void do_profile (unsigned long pc) -{ - if (prof_buffer && current->pid) { - extern int _stext; - pc -= (unsigned long) &_stext; - pc >>= prof_shift; - if (pc < prof_len) - ++prof_buffer[pc]; - else - /* - * Don't ignore out-of-bounds PC values silently, - * put them into the last histogram slot, so if - * present, they will show up as a sharp peak. - */ - ++prof_buffer[prof_len-1]; - } -} - /* * timer_interrupt() needs to keep up the real-time clock, * as well as call the "do_timer()" routine every clocktick @@ -63,9 +45,7 @@ static inline void do_profile (unsigned static irqreturn_t timer_interrupt(int irq, void *dummy, struct pt_regs * regs) { do_timer(regs); - - if (!user_mode(regs)) - do_profile(regs->pc); + profile_tick(CPU_PROFILING, regs); #ifdef CONFIG_HEARTBEAT /* use power LED as a heartbeat instead -- much more useful --- linux-2.6.9-rc1/arch/m68k/kernel/vmlinux-std.lds 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/m68k/kernel/vmlinux-std.lds 2004-09-02 20:51:51.000000000 -0700 @@ -51,9 +51,6 @@ SECTIONS __setup_start = .; .init.setup : { *(.init.setup) } __setup_end = .; - __start___param = .; - __param : { *(__param) } - __stop___param = .; __initcall_start = .; .initcall.init : { *(.initcall1.init) --- linux-2.6.9-rc1/arch/m68k/kernel/vmlinux-sun3.lds 2004-08-24 00:01:50.000000000 -0700 +++ 25/arch/m68k/kernel/vmlinux-sun3.lds 2004-09-02 20:51:51.000000000 -0700 @@ -45,9 +45,6 @@ __init_begin = .; __setup_start = .; .init.setup : { *(.init.setup) } __setup_end = .; - __start___param = .; - __param : { *(__param) } - __stop___param = .; __initcall_start = .; .initcall.init : { *(.initcall1.init) --- linux-2.6.9-rc1/arch/m68knommu/kernel/asm-offsets.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/m68knommu/kernel/asm-offsets.c 2004-09-03 00:56:40.983930144 -0700 @@ -12,9 +12,9 @@ #include #include #include +#include #include #include -#include #define DEFINE(sym, val) \ asm volatile("\n->" #sym " %0 " #val : : "i" (val)) --- linux-2.6.9-rc1/arch/m68knommu/kernel/m68k_ksyms.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/m68knommu/kernel/m68k_ksyms.c 2004-09-03 00:56:40.983930144 -0700 @@ -16,7 +16,6 @@ #include #include #include -#include #include extern void dump_thread(struct pt_regs *, struct user *); --- linux-2.6.9-rc1/arch/m68knommu/kernel/process.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/m68knommu/kernel/process.c 2004-09-02 20:51:51.000000000 -0700 @@ -188,7 +188,7 @@ asmlinkage int m68k_clone(struct pt_regs newsp = regs->d2; if (!newsp) newsp = rdusp(); - return do_fork(clone_flags & ~CLONE_IDLETASK, newsp, regs, 0, NULL, NULL); + return do_fork(clone_flags, newsp, regs, 0, NULL, NULL); } int copy_thread(int nr, unsigned long clone_flags, --- linux-2.6.9-rc1/arch/m68knommu/kernel/ptrace.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/m68knommu/kernel/ptrace.c 2004-09-03 00:56:56.199617008 -0700 @@ -133,13 +133,6 @@ asmlinkage int sys_ptrace(long request, ret = ptrace_attach(child); goto out_tsk; } - ret = -ESRCH; - if (!(child->ptrace & PT_PTRACED)) - goto out_tsk; - if (child->state != TASK_STOPPED) { - if (request != PTRACE_KILL) - goto out_tsk; - } ret = ptrace_check_attach(child, request == PTRACE_KILL); if (ret < 0) goto out_tsk; @@ -376,10 +369,8 @@ asmlinkage void syscall_trace(void) return; if (!(current->ptrace & PT_PTRACED)) return; - current->exit_code = SIGTRAP; - current->state = TASK_STOPPED; - notify_parent(current, SIGCHLD); - schedule(); + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); /* * this isn't the same as continuing with a signal, but it will do * for normal use. strace only continues with a signal if the --- linux-2.6.9-rc1/arch/m68knommu/kernel/signal.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/arch/m68knommu/kernel/signal.c 2004-09-02 20:51:51.000000000 -0700 @@ -616,9 +616,7 @@ adjust_stack: return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); goto adjust_stack; } @@ -685,9 +683,7 @@ adjust_stack: return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); goto adjust_stack; } --- linux-2.6.9-rc1/arch/m68knommu/kernel/time.c 2004-08-24 00:03:12.000000000 -0700 +++ 25/arch/m68knommu/kernel/time.c 2004-09-02 20:51:51.000000000 -0700 @@ -41,24 +41,6 @@ static inline int set_rtc_mmss(unsigned return -1; } -static inline void do_profile (unsigned long pc) -{ - if (prof_buffer && current->pid) { - extern int _stext; - pc -= (unsigned long) &_stext; - pc >>= prof_shift; - if (pc < prof_len) - ++prof_buffer[pc]; - else - /* - * Don't ignore out-of-bounds PC values silently, - * put them into the last histogram slot, so if - * present, they will show up as a sharp peak. - */ - ++prof_buffer[prof_len-1]; - } -} - /* * timer_interrupt() needs to keep up the real-time clock, * as well as call the "do_timer()" routine every clocktick @@ -75,9 +57,8 @@ static irqreturn_t timer_interrupt(int i write_seqlock(&xtime_lock); do_timer(regs); - - if (!user_mode(regs)) - do_profile(regs->pc); + if (current->pid) + profile_tick(CPU_PROFILING, regs); /* * If we have an externally synchronized Linux clock, then update --- linux-2.6.9-rc1/arch/m68knommu/kernel/vmlinux.lds.S 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/m68knommu/kernel/vmlinux.lds.S 2004-09-02 20:51:51.000000000 -0700 @@ -284,9 +284,6 @@ SECTIONS { __setup_start = .; *(.init.setup) __setup_end = .; - __start___param = .; - *(__param) - __stop___param = .; __initcall_start = .; *(.initcall1.init) *(.initcall2.init) --- linux-2.6.9-rc1/arch/m68knommu/platform/5307/timers.c 2004-08-24 00:03:49.000000000 -0700 +++ 25/arch/m68knommu/platform/5307/timers.c 2004-09-02 20:51:51.000000000 -0700 @@ -110,17 +110,8 @@ void coldfire_profile_tick(int irq, void { /* Reset ColdFire timer2 */ mcf_proftp->ter = MCFTIMER_TER_CAP | MCFTIMER_TER_REF; - - if (!user_mode(regs)) { - if (prof_buffer && current->pid) { - extern int _stext; - unsigned long ip = instruction_pointer(regs); - ip -= (unsigned long) &_stext; - ip >>= prof_shift; - if (ip < prof_len) - prof_buffer[ip]++; - } - } + if (current->pid) + profile_tick(CPU_PROFILING, regs); } /***************************************************************************/ --- linux-2.6.9-rc1/arch/m68k/q40/q40ints.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/m68k/q40/q40ints.c 2004-09-03 00:56:40.984929992 -0700 @@ -19,12 +19,12 @@ #include #include #include +#include #include #include #include #include -#include #include #include --- linux-2.6.9-rc1/arch/mips/au1000/common/time.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/arch/mips/au1000/common/time.c 2004-09-03 00:56:40.984929992 -0700 @@ -38,11 +38,11 @@ #include #include #include +#include #include #include #include -#include #include #include --- linux-2.6.9-rc1/arch/mips/baget/irq.c 2004-08-24 00:03:14.000000000 -0700 +++ 25/arch/mips/baget/irq.c 2004-09-02 20:51:51.000000000 -0700 @@ -180,7 +180,7 @@ skip: static void do_IRQ(int irq, struct pt_regs * regs) { struct irqaction *action; - int do_random, cpu; + int ret, do_random, cpu; cpu = smp_processor_id(); irq_enter(); @@ -194,8 +194,9 @@ static void do_IRQ(int irq, struct pt_re action = *(irq + irq_action); do_random = 0; do { - do_random |= action->flags; - action->handler(irq, action->dev_id, regs); + ret = action->handler(irq, action->dev_id, regs); + if (ret == IRQ_HANDLED) + do_random |= action->flags; action = action->next; } while (action); if (do_random & SA_SAMPLE_RANDOM) --- linux-2.6.9-rc1/arch/mips/kernel/irixsig.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/mips/kernel/irixsig.c 2004-09-02 20:51:51.000000000 -0700 @@ -121,9 +121,7 @@ static void setup_irix_frame(struct k_si return; segv_and_exit: - if (signr == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(signr, current); } static void inline --- linux-2.6.9-rc1/arch/mips/kernel/irq.c 2004-08-24 00:01:55.000000000 -0700 +++ 25/arch/mips/kernel/irq.c 2004-09-02 20:51:51.000000000 -0700 @@ -144,14 +144,16 @@ inline void synchronize_irq(unsigned int int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * action) { int status = 1; /* Force the "do bottom halves" bit */ - int retval = 0; + int ret, retval = 0; if (!(action->flags & SA_INTERRUPT)) local_irq_enable(); do { - status |= action->flags; - retval |= action->handler(irq, action->dev_id, regs); + ret = action->handler(irq, action->dev_id, regs); + if (ret == IRQ_HANDLED) + status |= action->flags; + retval |= ret; action = action->next; } while (action); if (status & SA_SAMPLE_RANDOM) @@ -872,30 +874,6 @@ static int irq_affinity_write_proc (stru #endif -static int prof_cpu_mask_read_proc (char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - int len = cpumask_scnprintf(page, count, *(cpumask_t *)data); - if (count - len < 2) - return -EINVAL; - len += sprintf(page + len, "\n"); - return len; -} - -static int prof_cpu_mask_write_proc (struct file *file, const char *buffer, - unsigned long count, void *data) -{ - cpumask_t *mask = (cpumask_t *)data, new_value; - unsigned long full_count = count, err; - - err = cpumask_parse(buffer, count, new_value); - if (err) - return err; - - *mask = new_value; - return full_count; -} - #define MAX_NAMELEN 10 static void register_irq_proc (unsigned int irq) @@ -931,26 +909,15 @@ static void register_irq_proc (unsigned #endif } -unsigned long prof_cpu_mask = -1; - void init_irq_proc (void) { - struct proc_dir_entry *entry; int i; /* create /proc/irq */ root_irq_dir = proc_mkdir("irq", 0); /* create /proc/irq/prof_cpu_mask */ - entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); - - if (!entry) - return; - - entry->nlink = 1; - entry->data = (void *)&prof_cpu_mask; - entry->read_proc = prof_cpu_mask_read_proc; - entry->write_proc = prof_cpu_mask_write_proc; + create_prof_cpu_mask(root_irq_dir); /* * Create entries for all existing IRQs. --- linux-2.6.9-rc1/arch/mips/kernel/signal32.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/mips/kernel/signal32.c 2004-09-02 20:51:51.000000000 -0700 @@ -574,9 +574,7 @@ static inline void setup_frame(struct k_ return; give_sigsegv: - if (signr == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(signr, current); } static inline void setup_rt_frame(struct k_sigaction * ka, @@ -647,9 +645,7 @@ static inline void setup_rt_frame(struct return; give_sigsegv: - if (signr == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(signr, current); } static inline void handle_signal(unsigned long sig, siginfo_t *info, --- linux-2.6.9-rc1/arch/mips/kernel/signal.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/mips/kernel/signal.c 2004-09-02 20:51:51.000000000 -0700 @@ -406,9 +406,7 @@ static void inline setup_frame(struct k_ return; give_sigsegv: - if (signr == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(signr, current); } #endif @@ -475,9 +473,7 @@ static void inline setup_rt_frame(struct return; give_sigsegv: - if (signr == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(signr, current); } extern void setup_rt_frame_n32(struct k_sigaction * ka, --- linux-2.6.9-rc1/arch/mips/kernel/signal_n32.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/mips/kernel/signal_n32.c 2004-09-02 20:51:51.000000000 -0700 @@ -208,7 +208,5 @@ void setup_rt_frame_n32(struct k_sigacti return; give_sigsegv: - if (signr == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(signr, current); } --- linux-2.6.9-rc1/arch/mips/kernel/smp.c 2004-08-24 00:02:20.000000000 -0700 +++ 25/arch/mips/kernel/smp.c 2004-09-03 00:56:40.985929840 -0700 @@ -35,7 +35,6 @@ #include #include #include -#include #include #include @@ -254,16 +253,6 @@ void __devinit smp_prepare_boot_cpu(void cpu_set(0, cpu_callin_map); } -static struct task_struct * __init fork_by_hand(void) -{ - struct pt_regs regs; - /* - * don't care about the eip and regs settings since - * we'll never reschedule the forked task. - */ - return copy_process(CLONE_VM|CLONE_IDLETASK, 0, ®s, 0, NULL, NULL); -} - /* * Startup the CPU with this logical number */ @@ -275,20 +264,10 @@ static int __init do_boot_cpu(int cpu) * The following code is purely to make sure * Linux can schedule processes on this slave. */ - idle = fork_by_hand(); + idle = fork_idle(cpu); if (IS_ERR(idle)) panic("failed fork for CPU %d\n", cpu); - wake_up_forked_process(idle); - - /* - * We remove it from the pidhash and the runqueue once we've - * got the process: - */ - init_idle(idle, cpu); - - unhash_process(idle); - prom_boot_secondary(cpu, idle); /* XXXKW timeout */ --- linux-2.6.9-rc1/arch/mips/kernel/syscall.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/mips/kernel/syscall.c 2004-09-02 20:51:51.000000000 -0700 @@ -180,7 +180,7 @@ static_unused int _sys_clone(nabi_no_reg newsp = regs.regs[29]; parent_tidptr = (int *) regs.regs[6]; child_tidptr = (int *) regs.regs[7]; - return do_fork(clone_flags & ~CLONE_IDLETASK, newsp, ®s, 0, + return do_fork(clone_flags, newsp, ®s, 0, parent_tidptr, child_tidptr); } --- linux-2.6.9-rc1/arch/mips/kernel/sysirix.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/mips/kernel/sysirix.c 2004-09-02 20:51:51.000000000 -0700 @@ -810,8 +810,8 @@ asmlinkage int irix_times(struct tms * t return err; err |= __put_user(current->utime, &tbuf->tms_utime); err |= __put_user(current->stime, &tbuf->tms_stime); - err |= __put_user(current->cutime, &tbuf->tms_cutime); - err |= __put_user(current->cstime, &tbuf->tms_cstime); + err |= __put_user(current->signal->cutime, &tbuf->tms_cutime); + err |= __put_user(current->signal->cstime, &tbuf->tms_cstime); } return err; --- linux-2.6.9-rc1/arch/mips/kernel/time.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/mips/kernel/time.c 2004-09-03 00:56:40.985929840 -0700 @@ -29,7 +29,6 @@ #include #include #include -#include #include #include @@ -417,23 +416,8 @@ static long last_rtc_update; */ void local_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - if (!user_mode(regs)) { - if (prof_buffer && current->pid) { - unsigned long pc = regs->cp0_epc; - - pc -= (unsigned long) _stext; - pc >>= prof_shift; - /* - * Dont ignore out-of-bounds pc values silently, - * put them into the last histogram slot, so if - * present, they will show up as a sharp peak. - */ - if (pc > prof_len - 1) - pc = prof_len - 1; - atomic_inc((atomic_t *)&prof_buffer[pc]); - } - } - + if (current->pid) + profile_tick(CPU_PROFILING, regs); #ifdef CONFIG_SMP /* in UP mode, update_process_times() is invoked by do_timer() */ update_process_times(user_mode(regs)); --- linux-2.6.9-rc1/arch/mips/kernel/vmlinux.lds.S 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/mips/kernel/vmlinux.lds.S 2004-09-02 20:51:51.000000000 -0700 @@ -96,9 +96,6 @@ SECTIONS __setup_start = .; .init.setup : { *(.init.setup) } __setup_end = .; - __start___param = .; - __param : { *(__param) } - __stop___param = .; .early_initcall.init : { __earlyinitcall_start = .; --- linux-2.6.9-rc1/arch/mips/mips-boards/generic/time.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/arch/mips/mips-boards/generic/time.c 2004-09-03 00:56:40.986929688 -0700 @@ -31,7 +31,6 @@ #include #include -#include #include #include #include --- linux-2.6.9-rc1/arch/mips/mm/fault.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/mips/mm/fault.c 2004-09-03 00:56:40.986929688 -0700 @@ -21,7 +21,6 @@ #include #include -#include #include #include #include --- linux-2.6.9-rc1/arch/mips/sgi-ip27/ip27-memory.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/mips/sgi-ip27/ip27-memory.c 2004-09-02 20:51:51.000000000 -0700 @@ -225,7 +225,7 @@ void __init paging_init(void) pfn_t end_pfn = node_getmaxclick(node) + 1; zones_size[ZONE_DMA] = end_pfn - start_pfn; - free_area_init_node(node, NODE_DATA(node), NULL, + free_area_init_node(node, NODE_DATA(node), zones_size, start_pfn, NULL); if (end_pfn > max_low_pfn) --- linux-2.6.9-rc1/arch/parisc/kernel/asm-offsets.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/parisc/kernel/asm-offsets.c 2004-09-03 00:56:40.987929536 -0700 @@ -32,11 +32,11 @@ #include #include #include -#include +#include +#include #include #include -#include #include #define DEFINE(sym, val) \ --- linux-2.6.9-rc1/arch/parisc/kernel/hardware.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/parisc/kernel/hardware.c 2004-09-02 20:51:51.000000000 -0700 @@ -7,7 +7,7 @@ * Reference Specification", March 7, 1999, version 0.96. This * is available at http://parisc-linux.org/documentation/ * - * Copyright 1999 by Alex deVries + * Copyright 1999 by Alex deVries * and copyright 1999 The Puffin Group Inc. * * This program is free software; you can redistribute it and/or modify --- linux-2.6.9-rc1/arch/parisc/kernel/process.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/parisc/kernel/process.c 2004-09-02 20:51:51.000000000 -0700 @@ -262,7 +262,7 @@ sys_clone(unsigned long clone_flags, uns if(usp == 0) usp = regs->gr[30]; - return do_fork(clone_flags & ~CLONE_IDLETASK, usp, regs, 0, user_tid, NULL); + return do_fork(clone_flags, usp, regs, 0, user_tid, NULL); } int --- linux-2.6.9-rc1/arch/parisc/kernel/ptrace.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/parisc/kernel/ptrace.c 2004-09-03 00:56:56.199617008 -0700 @@ -404,11 +404,8 @@ void syscall_trace(void) return; if (!(current->ptrace & PT_PTRACED)) return; - current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0); - current->state = TASK_STOPPED; - notify_parent(current, SIGCHLD); - schedule(); + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); /* * this isn't the same as continuing with a signal, but it will do * for normal use. strace only continues with a signal if the --- linux-2.6.9-rc1/arch/parisc/kernel/smp.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/parisc/kernel/smp.c 2004-09-02 20:51:51.000000000 -0700 @@ -486,24 +486,6 @@ void __init smp_callin(void) } /* - * Create the idle task for a new Slave CPU. DO NOT use kernel_thread() - * because that could end up calling schedule(). If it did, the new idle - * task could get scheduled before we had a chance to remove it from the - * run-queue... - */ -static struct task_struct *fork_by_hand(void) -{ - struct pt_regs regs; - - /* - * don't care about the regs settings since - * we'll never reschedule the forked task. - */ - return copy_process(CLONE_VM|CLONE_IDLETASK, 0, ®s, 0, NULL, NULL); -} - - -/* * Bring one cpu online. */ int __init smp_boot_one_cpu(int cpuid) @@ -521,13 +503,10 @@ int __init smp_boot_one_cpu(int cpuid) * Sheesh . . . */ - idle = fork_by_hand(); + idle = fork_idle(cpuid); if (IS_ERR(idle)) panic("SMP: fork failed for CPU:%d", cpuid); - wake_up_forked_process(idle); - init_idle(idle, cpuid); - unhash_process(idle); idle->thread_info->cpu = cpuid; /* Let _start know what logical CPU we're booting --- linux-2.6.9-rc1/arch/parisc/kernel/time.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/arch/parisc/kernel/time.c 2004-09-02 20:51:51.000000000 -0700 @@ -47,44 +47,6 @@ static long halftick; extern void smp_do_timer(struct pt_regs *regs); #endif -static inline void -parisc_do_profile(struct pt_regs *regs) -{ - unsigned long pc = regs->iaoq[0]; -#if 0 - extern unsigned long prof_cpu_mask; -#endif - extern char _stext; - - profile_hook(regs); - - if (user_mode(regs)) - return; - - if (!prof_buffer) - return; - -#if 0 - /* FIXME: when we have irq affinity to cpu, we need to - * only look at the cpus specified in this mask - */ - - if (!((1 << smp_processor_id()) & prof_cpu_mask)) - return; -#endif - - pc -= (unsigned long) &_stext; - pc >>= prof_shift; - /* - * Don't ignore out-of-bounds PC values silently, - * put them into the last histogram slot, so if - * present, they will show up as a sharp peak. - */ - if (pc > prof_len - 1) - pc = prof_len - 1; - atomic_inc((atomic_t *)&prof_buffer[pc]); -} - irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { long now; @@ -92,7 +54,7 @@ irqreturn_t timer_interrupt(int irq, voi int nticks; int cpu = smp_processor_id(); - parisc_do_profile(regs); + profile_tick(CPU_PROFILING, regs); now = mfctl(16); /* initialize next_tick to time at last clocktick */ --- linux-2.6.9-rc1/arch/parisc/kernel/vmlinux.lds.S 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/parisc/kernel/vmlinux.lds.S 2004-09-02 20:51:51.000000000 -0700 @@ -130,9 +130,6 @@ SECTIONS __setup_start = .; .init.setup : { *(.init.setup) } __setup_end = .; - __start___param = .; - __param : { *(__param) } - __stop___param = .; __initcall_start = .; .initcall.init : { *(.initcall1.init) --- linux-2.6.9-rc1/arch/parisc/lib/debuglocks.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/arch/parisc/lib/debuglocks.c 2004-09-03 00:56:40.987929536 -0700 @@ -25,8 +25,8 @@ #include #include #include +#include /* in_interrupt() */ #include -#include /* in_interrupt() */ #undef INIT_STUCK #define INIT_STUCK 1L << 30 --- linux-2.6.9-rc1/arch/parisc/mm/init.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/parisc/mm/init.c 2004-09-02 20:51:51.000000000 -0700 @@ -804,7 +804,7 @@ void __init paging_init(void) ZONE_DMA zone. */ zones_size[ZONE_DMA] = pmem_ranges[i].pages; - free_area_init_node(i,NODE_DATA(i),NULL,zones_size, + free_area_init_node(i, NODE_DATA(i), zones_size, pmem_ranges[i].start_pfn, 0); #ifdef CONFIG_DISCONTIGMEM --- linux-2.6.9-rc1/arch/ppc64/configs/iSeries_defconfig 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/ppc64/configs/iSeries_defconfig 2004-09-03 00:56:39.569145224 -0700 @@ -16,17 +16,17 @@ CONFIG_FORCE_MAX_ZONEORDER=13 # CONFIG_EXPERIMENTAL=y CONFIG_CLEAN_COMPILE=y -CONFIG_STANDALONE=y # # General setup # CONFIG_SWAP=y CONFIG_SYSVIPC=y -# CONFIG_POSIX_MQUEUE is not set +CONFIG_POSIX_MQUEUE=y # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_SYSCTL=y -# CONFIG_AUDIT is not set +CONFIG_AUDIT=y +CONFIG_AUDITSYSCALL=y CONFIG_LOG_BUF_SHIFT=17 CONFIG_HOTPLUG=y CONFIG_IKCONFIG=y @@ -34,6 +34,7 @@ CONFIG_IKCONFIG_PROC=y # CONFIG_EMBEDDED is not set CONFIG_KALLSYMS=y # CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set CONFIG_FUTEX=y CONFIG_EPOLL=y CONFIG_IOSCHED_NOOP=y @@ -41,6 +42,8 @@ CONFIG_IOSCHED_AS=y CONFIG_IOSCHED_DEADLINE=y CONFIG_IOSCHED_CFQ=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SHMEM=y +# CONFIG_TINY_SHMEM is not set # # Loadable module support @@ -62,10 +65,11 @@ CONFIG_PPC_ISERIES=y CONFIG_PPC=y CONFIG_PPC64=y # CONFIG_POWER4_ONLY is not set -# CONFIG_IOMMU_VMERGE is not set +CONFIG_IOMMU_VMERGE=y CONFIG_SMP=y CONFIG_NR_CPUS=32 # CONFIG_SCHED_SMT is not set +# CONFIG_PREEMPT is not set CONFIG_MSCHUNKS=y CONFIG_LPARCFG=y @@ -97,6 +101,8 @@ CONFIG_PCI_NAMES=y # # Generic Driver Options # +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y CONFIG_FW_LOADER=m # CONFIG_DEBUG_DRIVER is not set @@ -125,7 +131,7 @@ CONFIG_FW_LOADER=m CONFIG_BLK_DEV_LOOP=y # CONFIG_BLK_DEV_CRYPTOLOOP is not set CONFIG_BLK_DEV_NBD=m -# CONFIG_BLK_DEV_CARMEL is not set +# CONFIG_BLK_DEV_SX8 is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y @@ -168,22 +174,23 @@ CONFIG_SCSI_FC_ATTRS=y # SCSI low-level drivers # # CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_3W_9XXX is not set # CONFIG_SCSI_ACARD is not set # CONFIG_SCSI_AACRAID is not set # CONFIG_SCSI_AIC7XXX is not set # CONFIG_SCSI_AIC7XXX_OLD is not set # CONFIG_SCSI_AIC79XX is not set -# CONFIG_SCSI_ADVANSYS is not set -# CONFIG_SCSI_MEGARAID is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_LEGACY is not set # CONFIG_SCSI_SATA is not set # CONFIG_SCSI_BUSLOGIC is not set -# CONFIG_SCSI_CPQFCTS is not set # CONFIG_SCSI_DMX3191D is not set # CONFIG_SCSI_EATA is not set # CONFIG_SCSI_EATA_PIO is not set # CONFIG_SCSI_FUTURE_DOMAIN is not set # CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_IPS is not set +CONFIG_SCSI_IBMVSCSI=m # CONFIG_SCSI_INIA100 is not set # CONFIG_SCSI_SYM53C8XX_2 is not set # CONFIG_SCSI_IPR is not set @@ -209,11 +216,15 @@ CONFIG_BLK_DEV_MD=y CONFIG_MD_LINEAR=y CONFIG_MD_RAID0=y CONFIG_MD_RAID1=y +CONFIG_MD_RAID10=m CONFIG_MD_RAID5=y -CONFIG_MD_RAID6=y -# CONFIG_MD_MULTIPATH is not set +CONFIG_MD_RAID6=m +CONFIG_MD_MULTIPATH=m CONFIG_BLK_DEV_DM=y CONFIG_DM_CRYPT=m +CONFIG_DM_SNAPSHOT=m +CONFIG_DM_MIRROR=m +CONFIG_DM_ZERO=m # # Fusion MPT device support @@ -259,6 +270,7 @@ CONFIG_SYN_COOKIES=y CONFIG_INET_AH=m CONFIG_INET_ESP=m CONFIG_INET_IPCOMP=m +CONFIG_INET_TUNNEL=y # # IP: Virtual Server Configuration @@ -324,7 +336,13 @@ CONFIG_IP_NF_ARPFILTER=m CONFIG_IP_NF_ARP_MANGLE=m CONFIG_IP_NF_COMPAT_IPCHAINS=m CONFIG_IP_NF_COMPAT_IPFWADM=m -# CONFIG_IP_NF_RAW is not set +CONFIG_IP_NF_TARGET_NOTRACK=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_MATCH_ADDRTYPE=m +CONFIG_IP_NF_MATCH_REALM=m +# CONFIG_IP_NF_CT_ACCT is not set +CONFIG_IP_NF_MATCH_SCTP=m +CONFIG_IP_NF_CT_PROTO_SCTP=m CONFIG_XFRM=y CONFIG_XFRM_USER=m @@ -351,6 +369,7 @@ CONFIG_LLC=y # QoS and/or fair queueing # # CONFIG_NET_SCHED is not set +CONFIG_NET_CLS_ROUTE=y # # Network testing @@ -379,7 +398,6 @@ CONFIG_TUN=m # CONFIG_NET_ETHERNET=y CONFIG_MII=y -# CONFIG_OAKNET is not set # CONFIG_HAPPYMEAL is not set # CONFIG_SUNGEM is not set # CONFIG_NET_VENDOR_3COM is not set @@ -408,18 +426,32 @@ CONFIG_E100=y # CONFIG_EPIC100 is not set # CONFIG_SUNDANCE is not set # CONFIG_VIA_RHINE is not set +# CONFIG_VIA_VELOCITY is not set # -# Gigabit Ethernet (1000/10000 Mbit) +# Ethernet (1000 Mbit) # -# CONFIG_NET_GIGE is not set +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_E1000 is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_R8169 is not set +# CONFIG_SK98LIN is not set +# CONFIG_TIGON3 is not set + +# +# Ethernet (10000 Mbit) +# +# CONFIG_IXGB is not set +# CONFIG_S2IO is not set # # Token Ring devices # CONFIG_TR=y CONFIG_IBMOL=y -# CONFIG_IBMLS is not set # CONFIG_3C359 is not set # CONFIG_TMS380TR is not set @@ -508,10 +540,11 @@ CONFIG_SERIO_SERPORT=y # # Non-8250 serial port support # +CONFIG_SERIAL_CORE=m +CONFIG_SERIAL_ICOM=m CONFIG_UNIX98_PTYS=y CONFIG_LEGACY_PTYS=y CONFIG_LEGACY_PTY_COUNT=256 -# CONFIG_QIC02_TAPE is not set # # IPMI @@ -542,6 +575,11 @@ CONFIG_MAX_RAW_DEVS=256 # CONFIG_I2C is not set # +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# # Misc devices # @@ -592,8 +630,10 @@ CONFIG_FS_MBCACHE=y CONFIG_REISERFS_FS=y # CONFIG_REISERFS_CHECK is not set # CONFIG_REISERFS_PROC_INFO is not set -# CONFIG_REISERFS_FS_XATTR is not set -CONFIG_JFS_FS=y +CONFIG_REISERFS_FS_XATTR=y +CONFIG_REISERFS_FS_POSIX_ACL=y +# CONFIG_REISERFS_FS_SECURITY is not set +CONFIG_JFS_FS=m CONFIG_JFS_POSIX_ACL=y # CONFIG_JFS_DEBUG is not set # CONFIG_JFS_STATISTICS is not set @@ -616,6 +656,7 @@ CONFIG_ISO9660_FS=y # CONFIG_JOLIET is not set # CONFIG_ZISOFS is not set CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y # # DOS/FAT/NT Filesystems @@ -623,6 +664,8 @@ CONFIG_UDF_FS=m CONFIG_FAT_FS=y CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" # CONFIG_NTFS_FS is not set # @@ -663,19 +706,22 @@ CONFIG_NFS_FS=y CONFIG_NFS_V3=y CONFIG_NFS_V4=y # CONFIG_NFS_DIRECTIO is not set -CONFIG_NFSD=y +CONFIG_NFSD=m CONFIG_NFSD_V3=y CONFIG_NFSD_V4=y CONFIG_NFSD_TCP=y CONFIG_LOCKD=y CONFIG_LOCKD_V4=y -CONFIG_EXPORTFS=y +CONFIG_EXPORTFS=m CONFIG_SUNRPC=y CONFIG_SUNRPC_GSS=y CONFIG_RPCSEC_GSS_KRB5=y +CONFIG_RPCSEC_GSS_SPKM3=m # CONFIG_SMB_FS is not set CONFIG_CIFS=m # CONFIG_CIFS_STATS is not set +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y # CONFIG_NCP_FS is not set # CONFIG_CODA_FS is not set # CONFIG_AFS_FS is not set @@ -691,7 +737,7 @@ CONFIG_MSDOS_PARTITION=y # CONFIG_NLS=y CONFIG_NLS_DEFAULT="iso8859-1" -# CONFIG_NLS_CODEPAGE_437 is not set +CONFIG_NLS_CODEPAGE_437=y # CONFIG_NLS_CODEPAGE_737 is not set # CONFIG_NLS_CODEPAGE_775 is not set # CONFIG_NLS_CODEPAGE_850 is not set @@ -714,7 +760,8 @@ CONFIG_NLS_DEFAULT="iso8859-1" # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_CODEPAGE_1250 is not set # CONFIG_NLS_CODEPAGE_1251 is not set -# CONFIG_NLS_ISO8859_1 is not set +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y # CONFIG_NLS_ISO8859_2 is not set # CONFIG_NLS_ISO8859_3 is not set # CONFIG_NLS_ISO8859_4 is not set @@ -748,14 +795,16 @@ CONFIG_OPROFILE=y # Kernel hacking # CONFIG_DEBUG_KERNEL=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_INFO is not set CONFIG_DEBUG_STACKOVERFLOW=y CONFIG_DEBUG_STACK_USAGE=y -# CONFIG_DEBUG_SLAB is not set -CONFIG_MAGIC_SYSRQ=y # CONFIG_DEBUGGER is not set # CONFIG_PPCDBG is not set -# CONFIG_DEBUG_INFO is not set # CONFIG_IRQSTACKS is not set +# CONFIG_SCHEDSTATS is not set # # Security options @@ -773,6 +822,7 @@ CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_SHA1=m CONFIG_CRYPTO_SHA256=m CONFIG_CRYPTO_SHA512=m +CONFIG_CRYPTO_WHIRLPOOL=m CONFIG_CRYPTO_DES=y CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_TWOFISH=m @@ -780,16 +830,19 @@ CONFIG_CRYPTO_SERPENT=m CONFIG_CRYPTO_AES=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_TEA=m CONFIG_CRYPTO_ARC4=m +CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_DEFLATE=m -# CONFIG_CRYPTO_MICHAEL_MIC is not set -# CONFIG_CRYPTO_CRC32C is not set +CONFIG_CRYPTO_MICHAEL_MIC=m +CONFIG_CRYPTO_CRC32C=m CONFIG_CRYPTO_TEST=m # # Library routines # +CONFIG_CRC_CCITT=m CONFIG_CRC32=y -# CONFIG_LIBCRC32C is not set +CONFIG_LIBCRC32C=m CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=m --- linux-2.6.9-rc1/arch/ppc64/configs/pSeries_defconfig 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/ppc64/configs/pSeries_defconfig 2004-09-03 00:56:39.442164528 -0700 @@ -16,27 +16,34 @@ CONFIG_FORCE_MAX_ZONEORDER=13 # CONFIG_EXPERIMENTAL=y CONFIG_CLEAN_COMPILE=y -CONFIG_STANDALONE=y # # General setup # CONFIG_SWAP=y CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_SYSCTL=y +CONFIG_AUDIT=y +CONFIG_AUDITSYSCALL=y CONFIG_LOG_BUF_SHIFT=17 CONFIG_HOTPLUG=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y # CONFIG_EMBEDDED is not set CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_KALLSYMS_EXTRA_PASS is not set CONFIG_FUTEX=y CONFIG_EPOLL=y CONFIG_IOSCHED_NOOP=y CONFIG_IOSCHED_AS=y CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SHMEM=y +# CONFIG_TINY_SHMEM is not set # # Loadable module support @@ -48,6 +55,7 @@ CONFIG_OBSOLETE_MODPARM=y # CONFIG_MODVERSIONS is not set # CONFIG_KMOD is not set CONFIG_STOP_MACHINE=y +CONFIG_SYSVIPC_COMPAT=y # # Platform support @@ -59,14 +67,18 @@ CONFIG_PPC64=y CONFIG_PPC_OF=y CONFIG_ALTIVEC=y # CONFIG_PPC_PMAC is not set +CONFIG_PPC_SPLPAR=y # CONFIG_BOOTX_TEXT is not set # CONFIG_POWER4_ONLY is not set -# CONFIG_IOMMU_VMERGE is not set +CONFIG_IOMMU_VMERGE=y CONFIG_SMP=y CONFIG_IRQ_ALL_CPUS=y -CONFIG_NR_CPUS=32 +CONFIG_NR_CPUS=128 # CONFIG_HMT is not set -# CONFIG_DISCONTIGMEM is not set +CONFIG_DISCONTIGMEM=y +CONFIG_NUMA=y +CONFIG_SCHED_SMT=y +# CONFIG_PREEMPT is not set CONFIG_PPC_RTAS=y CONFIG_RTAS_FLASH=m CONFIG_SCANLOG=m @@ -81,6 +93,7 @@ CONFIG_BINFMT_ELF=y # CONFIG_BINFMT_MISC is not set CONFIG_PCI_LEGACY_PROC=y CONFIG_PCI_NAMES=y +CONFIG_HOTPLUG_CPU=y # # PCMCIA/CardBus support @@ -107,7 +120,9 @@ CONFIG_PROC_DEVICETREE=y # # Generic Driver Options # -CONFIG_FW_LOADER=m +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y # CONFIG_DEBUG_DRIVER is not set # @@ -127,7 +142,7 @@ CONFIG_FW_LOADER=m # # Block devices # -CONFIG_BLK_DEV_FD=y +CONFIG_BLK_DEV_FD=m # CONFIG_BLK_CPQ_DA is not set # CONFIG_BLK_CPQ_CISS_DA is not set # CONFIG_BLK_DEV_DAC960 is not set @@ -135,7 +150,8 @@ CONFIG_BLK_DEV_FD=y CONFIG_BLK_DEV_LOOP=y # CONFIG_BLK_DEV_CRYPTOLOOP is not set CONFIG_BLK_DEV_NBD=m -# CONFIG_BLK_DEV_CARMEL is not set +# CONFIG_BLK_DEV_SX8 is not set +# CONFIG_BLK_DEV_UB is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y @@ -149,9 +165,9 @@ CONFIG_BLK_DEV_IDE=y # # Please see Documentation/ide.txt for help/info on IDE drives # +# CONFIG_BLK_DEV_IDE_SATA is not set CONFIG_BLK_DEV_IDEDISK=y # CONFIG_IDEDISK_MULTI_MODE is not set -# CONFIG_IDEDISK_STROKE is not set CONFIG_BLK_DEV_IDECD=y # CONFIG_BLK_DEV_IDETAPE is not set # CONFIG_BLK_DEV_IDEFLOPPY is not set @@ -194,6 +210,7 @@ CONFIG_BLK_DEV_AMD74XX=y # CONFIG_BLK_DEV_SLC90E66 is not set # CONFIG_BLK_DEV_TRM290 is not set # CONFIG_BLK_DEV_VIA82CXXX is not set +# CONFIG_IDE_ARM is not set CONFIG_BLK_DEV_IDEDMA=y # CONFIG_IDEDMA_IVB is not set CONFIG_IDEDMA_AUTO=y @@ -219,7 +236,6 @@ CONFIG_CHR_DEV_SG=y # Some SCSI devices (e.g. CD jukebox) support multiple LUNs # CONFIG_SCSI_MULTI_LUN=y -CONFIG_SCSI_REPORT_LUNS=y CONFIG_SCSI_CONSTANTS=y # CONFIG_SCSI_LOGGING is not set @@ -233,28 +249,32 @@ CONFIG_SCSI_FC_ATTRS=y # SCSI low-level drivers # # CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_3W_9XXX is not set # CONFIG_SCSI_ACARD is not set # CONFIG_SCSI_AACRAID is not set # CONFIG_SCSI_AIC7XXX is not set # CONFIG_SCSI_AIC7XXX_OLD is not set # CONFIG_SCSI_AIC79XX is not set -# CONFIG_SCSI_ADVANSYS is not set -# CONFIG_SCSI_MEGARAID is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_LEGACY is not set # CONFIG_SCSI_SATA is not set # CONFIG_SCSI_BUSLOGIC is not set -# CONFIG_SCSI_CPQFCTS is not set # CONFIG_SCSI_DMX3191D is not set # CONFIG_SCSI_EATA is not set # CONFIG_SCSI_EATA_PIO is not set # CONFIG_SCSI_FUTURE_DOMAIN is not set # CONFIG_SCSI_GDTH is not set # CONFIG_SCSI_IPS is not set +CONFIG_SCSI_IBMVSCSI=m # CONFIG_SCSI_INIA100 is not set CONFIG_SCSI_SYM53C8XX_2=y CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0 CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 # CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set +CONFIG_SCSI_IPR=y +# CONFIG_SCSI_IPR_TRACE is not set +# CONFIG_SCSI_IPR_DUMP is not set # CONFIG_SCSI_QLOGIC_ISP is not set # CONFIG_SCSI_QLOGIC_FC is not set # CONFIG_SCSI_QLOGIC_1280 is not set @@ -277,11 +297,15 @@ CONFIG_BLK_DEV_MD=y CONFIG_MD_LINEAR=y CONFIG_MD_RAID0=y CONFIG_MD_RAID1=y +CONFIG_MD_RAID10=m CONFIG_MD_RAID5=y -CONFIG_MD_RAID6=y -# CONFIG_MD_MULTIPATH is not set +CONFIG_MD_RAID6=m +CONFIG_MD_MULTIPATH=m CONFIG_BLK_DEV_DM=y CONFIG_DM_CRYPT=m +CONFIG_DM_SNAPSHOT=m +CONFIG_DM_MIRROR=m +CONFIG_DM_ZERO=m # # Fusion MPT device support @@ -296,6 +320,7 @@ CONFIG_DM_CRYPT=m # # I2O device support # +# CONFIG_I2O is not set # # Macintosh device drivers @@ -322,19 +347,17 @@ CONFIG_NET_IPIP=y # CONFIG_NET_IPGRE is not set # CONFIG_IP_MROUTE is not set # CONFIG_ARPD is not set -CONFIG_INET_ECN=y CONFIG_SYN_COOKIES=y CONFIG_INET_AH=m CONFIG_INET_ESP=m CONFIG_INET_IPCOMP=m +CONFIG_INET_TUNNEL=y # # IP: Virtual Server Configuration # # CONFIG_IP_VS is not set # CONFIG_IPV6 is not set -# CONFIG_DECNET is not set -# CONFIG_BRIDGE is not set CONFIG_NETFILTER=y # CONFIG_NETFILTER_DEBUG is not set @@ -394,16 +417,24 @@ CONFIG_IP_NF_ARPFILTER=m CONFIG_IP_NF_ARP_MANGLE=m CONFIG_IP_NF_COMPAT_IPCHAINS=m CONFIG_IP_NF_COMPAT_IPFWADM=m +CONFIG_IP_NF_TARGET_NOTRACK=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_MATCH_ADDRTYPE=m +CONFIG_IP_NF_MATCH_REALM=m +# CONFIG_IP_NF_CT_ACCT is not set +CONFIG_IP_NF_MATCH_SCTP=m +CONFIG_IP_NF_CT_PROTO_SCTP=m CONFIG_XFRM=y CONFIG_XFRM_USER=m # # SCTP Configuration (EXPERIMENTAL) # -CONFIG_IPV6_SCTP__=y # CONFIG_IP_SCTP is not set # CONFIG_ATM is not set +# CONFIG_BRIDGE is not set # CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set CONFIG_LLC=y # CONFIG_LLC2 is not set # CONFIG_IPX is not set @@ -419,28 +450,35 @@ CONFIG_LLC=y # QoS and/or fair queueing # # CONFIG_NET_SCHED is not set +CONFIG_NET_CLS_ROUTE=y # # Network testing # # CONFIG_NET_PKTGEN is not set +CONFIG_NETPOLL=y +CONFIG_NETPOLL_RX=y +CONFIG_NETPOLL_TRAP=y +CONFIG_NET_POLL_CONTROLLER=y +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set CONFIG_NETDEVICES=y +CONFIG_DUMMY=m +CONFIG_BONDING=m +# CONFIG_EQUALIZER is not set +CONFIG_TUN=m # # ARCnet devices # # CONFIG_ARCNET is not set -CONFIG_DUMMY=m -CONFIG_BONDING=m -# CONFIG_EQUALIZER is not set -CONFIG_TUN=m # # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y CONFIG_MII=y -# CONFIG_OAKNET is not set # CONFIG_HAPPYMEAL is not set # CONFIG_SUNGEM is not set CONFIG_NET_VENDOR_3COM=y @@ -452,6 +490,7 @@ CONFIG_VORTEX=y # # CONFIG_NET_TULIP is not set # CONFIG_HP100 is not set +CONFIG_IBMVETH=m CONFIG_NET_PCI=y CONFIG_PCNET32=y # CONFIG_AMD8111_ETH is not set @@ -471,6 +510,7 @@ CONFIG_E100=y # CONFIG_EPIC100 is not set # CONFIG_SUNDANCE is not set # CONFIG_VIA_RHINE is not set +# CONFIG_VIA_VELOCITY is not set # # Ethernet (1000 Mbit) @@ -484,7 +524,6 @@ CONFIG_E1000=y # CONFIG_HAMACHI is not set # CONFIG_YELLOWFIN is not set # CONFIG_R8169 is not set -# CONFIG_SIS190 is not set # CONFIG_SK98LIN is not set CONFIG_TIGON3=y @@ -493,59 +532,40 @@ CONFIG_TIGON3=y # CONFIG_IXGB=m # CONFIG_IXGB_NAPI is not set -# CONFIG_FDDI is not set -# CONFIG_HIPPI is not set -CONFIG_IBMVETH=m -CONFIG_PPP=m -# CONFIG_PPP_MULTILINK is not set -# CONFIG_PPP_FILTER is not set -CONFIG_PPP_ASYNC=m -CONFIG_PPP_SYNC_TTY=m -CONFIG_PPP_DEFLATE=m -CONFIG_PPP_BSDCOMP=m -CONFIG_PPPOE=m -# CONFIG_SLIP is not set - -# -# Wireless LAN (non-hamradio) -# -# CONFIG_NET_RADIO is not set +CONFIG_S2IO=m +# CONFIG_S2IO_NAPI is not set # # Token Ring devices # CONFIG_TR=y CONFIG_IBMOL=y -# CONFIG_IBMLS is not set # CONFIG_3C359 is not set # CONFIG_TMS380TR is not set -# CONFIG_NET_FC is not set -# CONFIG_SHAPER is not set -CONFIG_NETCONSOLE=y - -# -# Wan interfaces -# -# CONFIG_WAN is not set - -# -# Amateur Radio support -# -# CONFIG_HAMRADIO is not set # -# IrDA (infrared) support +# Wireless LAN (non-hamradio) # -# CONFIG_IRDA is not set +# CONFIG_NET_RADIO is not set # -# Bluetooth support +# Wan interfaces # -# CONFIG_BT is not set -CONFIG_NETPOLL=y -CONFIG_NETPOLL_RX=y -CONFIG_NETPOLL_TRAP=y -CONFIG_NET_POLL_CONTROLLER=y +# CONFIG_WAN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +CONFIG_PPP=m +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPPOE=m +# CONFIG_SLIP is not set +# CONFIG_NET_FC is not set +# CONFIG_SHAPER is not set +CONFIG_NETCONSOLE=y # # ISDN subsystem @@ -591,15 +611,16 @@ CONFIG_SERIO_I8042=y CONFIG_INPUT_KEYBOARD=y CONFIG_KEYBOARD_ATKBD=y # CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set # CONFIG_KEYBOARD_XTKBD is not set # CONFIG_KEYBOARD_NEWTON is not set CONFIG_INPUT_MOUSE=y CONFIG_MOUSE_PS2=y # CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_VSXXXAA is not set # CONFIG_INPUT_JOYSTICK is not set # CONFIG_INPUT_TOUCHSCREEN is not set CONFIG_INPUT_MISC=y -CONFIG_INPUT_PCSPKR=y # CONFIG_INPUT_UINPUT is not set # @@ -624,16 +645,12 @@ CONFIG_SERIAL_8250_NR_UARTS=4 CONFIG_SERIAL_CORE=y CONFIG_SERIAL_CORE_CONSOLE=y # CONFIG_SERIAL_PMACZILOG is not set +CONFIG_SERIAL_ICOM=m CONFIG_UNIX98_PTYS=y CONFIG_LEGACY_PTYS=y CONFIG_LEGACY_PTY_COUNT=256 CONFIG_HVC_CONSOLE=y - -# -# Mice -# -# CONFIG_BUSMOUSE is not set -# CONFIG_QIC02_TAPE is not set +CONFIG_HVCS=m # # IPMI @@ -656,7 +673,7 @@ CONFIG_HVC_CONSOLE=y # CONFIG_AGP is not set # CONFIG_DRM is not set CONFIG_RAW_DRIVER=y -CONFIG_MAX_RAW_DEVS=256 +CONFIG_MAX_RAW_DEVS=1024 # # I2C support @@ -669,11 +686,13 @@ CONFIG_I2C=y # CONFIG_I2C_ALGOBIT=y # CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set # # I2C Hardware Bus support # # CONFIG_I2C_ALI1535 is not set +# CONFIG_I2C_ALI1563 is not set # CONFIG_I2C_ALI15X3 is not set # CONFIG_I2C_AMD756 is not set # CONFIG_I2C_AMD8111 is not set @@ -691,30 +710,52 @@ CONFIG_I2C_ALGOBIT=y # CONFIG_I2C_VIA is not set # CONFIG_I2C_VIAPRO is not set # CONFIG_I2C_VOODOO3 is not set +# CONFIG_I2C_PCA_ISA is not set # -# I2C Hardware Sensors Chip support +# Hardware Sensors Chip support # # CONFIG_I2C_SENSOR is not set # CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1031 is not set # CONFIG_SENSORS_ASB100 is not set -# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_DS1621 is not set # CONFIG_SENSORS_FSCHER is not set # CONFIG_SENSORS_GL518SM is not set # CONFIG_SENSORS_IT87 is not set # CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set # CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set # CONFIG_SENSORS_LM83 is not set # CONFIG_SENSORS_LM85 is not set # CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_SMSC47M1 is not set # CONFIG_SENSORS_VIA686A is not set # CONFIG_SENSORS_W83781D is not set # CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83627HF is not set + +# +# Other I2C Chip support +# +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_RTC8564 is not set # CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set # CONFIG_I2C_DEBUG_BUS is not set # CONFIG_I2C_DEBUG_CHIP is not set # +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# # Misc devices # @@ -732,12 +773,14 @@ CONFIG_I2C_ALGOBIT=y # Graphics support # CONFIG_FB=y +CONFIG_FB_MODE_HELPERS=y +# CONFIG_FB_CIRRUS is not set # CONFIG_FB_PM2 is not set # CONFIG_FB_CYBER2000 is not set CONFIG_FB_OF=y # CONFIG_FB_CT65550 is not set +# CONFIG_FB_ASILIANT is not set # CONFIG_FB_IMSTT is not set -# CONFIG_FB_S3TRIO is not set # CONFIG_FB_VGA16 is not set # CONFIG_FB_RIVA is not set CONFIG_FB_MATROX=y @@ -765,10 +808,8 @@ CONFIG_FB_RADEON_I2C=y # Console display driver support # # CONFIG_VGA_CONSOLE is not set -# CONFIG_MDA_CONSOLE is not set CONFIG_DUMMY_CONSOLE=y CONFIG_FRAMEBUFFER_CONSOLE=y -CONFIG_PCI_CONSOLE=y # CONFIG_FONTS is not set CONFIG_FONT_8x8=y CONFIG_FONT_8x16=y @@ -798,11 +839,14 @@ CONFIG_USB=y CONFIG_USB_DEVICEFS=y # CONFIG_USB_BANDWIDTH is not set # CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG is not set # # USB Host Controller Drivers # CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_SPLIT_ISO is not set +# CONFIG_USB_EHCI_ROOT_HUB_TT is not set CONFIG_USB_OHCI_HCD=y # CONFIG_USB_UHCI_HCD is not set @@ -814,6 +858,7 @@ CONFIG_USB_OHCI_HCD=y # CONFIG_USB_PRINTER is not set CONFIG_USB_STORAGE=y # CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_RW_DETECT is not set # CONFIG_USB_STORAGE_DATAFAB is not set # CONFIG_USB_STORAGE_FREECOM is not set # CONFIG_USB_STORAGE_ISD200 is not set @@ -834,7 +879,10 @@ CONFIG_USB_HIDDEV=y # CONFIG_USB_WACOM is not set # CONFIG_USB_KBTAB is not set # CONFIG_USB_POWERMATE is not set +# CONFIG_USB_MTOUCH is not set +# CONFIG_USB_EGALAX is not set # CONFIG_USB_XPAD is not set +# CONFIG_USB_ATI_REMOTE is not set # # USB Imaging devices @@ -881,6 +929,8 @@ CONFIG_USB_HIDDEV=y # CONFIG_USB_LEGOTOWER is not set # CONFIG_USB_LCD is not set # CONFIG_USB_LED is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_PHIDGETSERVO is not set # CONFIG_USB_TEST is not set # @@ -905,7 +955,10 @@ CONFIG_FS_MBCACHE=y CONFIG_REISERFS_FS=y # CONFIG_REISERFS_CHECK is not set # CONFIG_REISERFS_PROC_INFO is not set -CONFIG_JFS_FS=y +CONFIG_REISERFS_FS_XATTR=y +CONFIG_REISERFS_FS_POSIX_ACL=y +# CONFIG_REISERFS_FS_SECURITY is not set +CONFIG_JFS_FS=m CONFIG_JFS_POSIX_ACL=y # CONFIG_JFS_DEBUG is not set # CONFIG_JFS_STATISTICS is not set @@ -928,6 +981,7 @@ CONFIG_ISO9660_FS=y # CONFIG_JOLIET is not set # CONFIG_ZISOFS is not set CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y # # DOS/FAT/NT Filesystems @@ -935,6 +989,8 @@ CONFIG_UDF_FS=m CONFIG_FAT_FS=y CONFIG_MSDOS_FS=y CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" # CONFIG_NTFS_FS is not set # @@ -942,6 +998,7 @@ CONFIG_VFAT_FS=y # CONFIG_PROC_FS=y CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y # CONFIG_DEVFS_FS is not set CONFIG_DEVPTS_FS_XATTR=y # CONFIG_DEVPTS_FS_SECURITY is not set @@ -974,18 +1031,22 @@ CONFIG_NFS_FS=y CONFIG_NFS_V3=y CONFIG_NFS_V4=y # CONFIG_NFS_DIRECTIO is not set -CONFIG_NFSD=y +CONFIG_NFSD=m CONFIG_NFSD_V3=y CONFIG_NFSD_V4=y CONFIG_NFSD_TCP=y CONFIG_LOCKD=y CONFIG_LOCKD_V4=y -CONFIG_EXPORTFS=y +CONFIG_EXPORTFS=m CONFIG_SUNRPC=y -CONFIG_SUNRPC_GSS=m -CONFIG_RPCSEC_GSS_KRB5=m +CONFIG_SUNRPC_GSS=y +CONFIG_RPCSEC_GSS_KRB5=y +CONFIG_RPCSEC_GSS_SPKM3=m # CONFIG_SMB_FS is not set CONFIG_CIFS=m +# CONFIG_CIFS_STATS is not set +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y # CONFIG_NCP_FS is not set # CONFIG_CODA_FS is not set # CONFIG_AFS_FS is not set @@ -1001,7 +1062,7 @@ CONFIG_MSDOS_PARTITION=y # CONFIG_NLS=y CONFIG_NLS_DEFAULT="iso8859-1" -# CONFIG_NLS_CODEPAGE_437 is not set +CONFIG_NLS_CODEPAGE_437=y # CONFIG_NLS_CODEPAGE_737 is not set # CONFIG_NLS_CODEPAGE_775 is not set # CONFIG_NLS_CODEPAGE_850 is not set @@ -1024,7 +1085,8 @@ CONFIG_NLS_DEFAULT="iso8859-1" # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_CODEPAGE_1250 is not set # CONFIG_NLS_CODEPAGE_1251 is not set -# CONFIG_NLS_ISO8859_1 is not set +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y # CONFIG_NLS_ISO8859_2 is not set # CONFIG_NLS_ISO8859_3 is not set # CONFIG_NLS_ISO8859_4 is not set @@ -1049,15 +1111,18 @@ CONFIG_OPROFILE=y # Kernel hacking # CONFIG_DEBUG_KERNEL=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_INFO is not set CONFIG_DEBUG_STACKOVERFLOW=y CONFIG_DEBUG_STACK_USAGE=y -# CONFIG_DEBUG_SLAB is not set -CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUGGER=y CONFIG_XMON=y CONFIG_XMON_DEFAULT=y # CONFIG_PPCDBG is not set -# CONFIG_DEBUG_INFO is not set +CONFIG_IRQSTACKS=y +# CONFIG_SCHEDSTATS is not set # # Security options @@ -1071,24 +1136,31 @@ CONFIG_CRYPTO=y CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_MD4=m -CONFIG_CRYPTO_MD5=m +CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_SHA1=m CONFIG_CRYPTO_SHA256=m CONFIG_CRYPTO_SHA512=m -CONFIG_CRYPTO_DES=m +CONFIG_CRYPTO_WHIRLPOOL=m +CONFIG_CRYPTO_DES=y CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_TWOFISH=m CONFIG_CRYPTO_SERPENT=m CONFIG_CRYPTO_AES=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_TEA=m CONFIG_CRYPTO_ARC4=m +CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_DEFLATE=m +CONFIG_CRYPTO_MICHAEL_MIC=m +CONFIG_CRYPTO_CRC32C=m CONFIG_CRYPTO_TEST=m # # Library routines # +CONFIG_CRC_CCITT=m CONFIG_CRC32=y +CONFIG_LIBCRC32C=m CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=m --- linux-2.6.9-rc1/arch/ppc64/Kconfig.debug 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/ppc64/Kconfig.debug 2004-09-02 20:51:51.000000000 -0700 @@ -54,4 +54,16 @@ config SPINLINE If in doubt, say N. +config SCHEDSTATS + bool "Collect scheduler statistics" + depends on DEBUG_KERNEL && PROC_FS + help + If you say Y here, additional code will be inserted into the + scheduler and related routines to collect statistics about + scheduler behavior and provide them in /proc/schedstat. These + stats may be useful for both tuning and debugging the scheduler + If you aren't debugging the scheduler or trying to tune a specific + application, you can say N to avoid the very slight overhead + this adds. + endmenu --- linux-2.6.9-rc1/arch/ppc64/kernel/asm-offsets.c 2004-08-24 00:03:11.000000000 -0700 +++ 25/arch/ppc64/kernel/asm-offsets.c 2004-09-03 00:56:40.987929536 -0700 @@ -22,11 +22,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include @@ -58,6 +58,7 @@ int main(void) DEFINE(THREAD_FPR0, offsetof(struct thread_struct, fpr[0])); DEFINE(THREAD_FPSCR, offsetof(struct thread_struct, fpscr)); DEFINE(KSP, offsetof(struct thread_struct, ksp)); + DEFINE(KSP_VSID, offsetof(struct thread_struct, ksp_vsid)); #ifdef CONFIG_ALTIVEC DEFINE(THREAD_VR0, offsetof(struct thread_struct, vr[0])); @@ -98,11 +99,6 @@ int main(void) DEFINE(PACAHTLBSEGS, offsetof(struct paca_struct, context.htlb_segs)); #endif /* CONFIG_HUGETLB_PAGE */ DEFINE(PACADEFAULTDECR, offsetof(struct paca_struct, default_decr)); - DEFINE(PACAPROFENABLED, offsetof(struct paca_struct, prof_enabled)); - DEFINE(PACAPROFLEN, offsetof(struct paca_struct, prof_len)); - DEFINE(PACAPROFSHIFT, offsetof(struct paca_struct, prof_shift)); - DEFINE(PACAPROFBUFFER, offsetof(struct paca_struct, prof_buffer)); - DEFINE(PACAPROFSTEXT, offsetof(struct paca_struct, prof_stext)); DEFINE(PACA_EXGEN, offsetof(struct paca_struct, exgen)); DEFINE(PACA_EXMC, offsetof(struct paca_struct, exmc)); DEFINE(PACA_EXSLB, offsetof(struct paca_struct, exslb)); --- linux-2.6.9-rc1/arch/ppc64/kernel/chrp_setup.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/ppc64/kernel/chrp_setup.c 2004-09-03 00:56:38.896247520 -0700 @@ -229,10 +229,6 @@ void __init chrp_init(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { - struct device_node * dn; - char * hypertas; - unsigned int len; - ppc_md.setup_arch = chrp_setup_arch; ppc_md.get_cpuinfo = chrp_get_cpuinfo; if (naca->interrupt_controller == IC_OPEN_PIC) { @@ -261,10 +257,18 @@ chrp_init(unsigned long r3, unsigned lon ppc_md.progress = chrp_progress; - /* Build up the firmware_features bitmask field - * using contents of device-tree/ibm,hypertas-functions. - * Ultimately this functionality may be moved into prom.c prom_init(). - */ +} + +/* Build up the firmware_features bitmask field + * using contents of device-tree/ibm,hypertas-functions. + * Ultimately this functionality may be moved into prom.c prom_init(). + */ +void __init fw_feature_init(void) +{ + struct device_node * dn; + char * hypertas; + unsigned int len; + cur_cpu_spec->firmware_features = 0; dn = of_find_node_by_path("/rtas"); if (dn == NULL) { --- linux-2.6.9-rc1/arch/ppc64/kernel/eeh.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/arch/ppc64/kernel/eeh.c 2004-09-03 00:56:38.509306344 -0700 @@ -348,7 +348,7 @@ void __init pci_addr_cache_build(void) * ths routine does *not* convert I/O BAR addresses (which start * with 0xE...) to phys addresses! */ -static unsigned long eeh_token_to_phys(unsigned long token) +static inline unsigned long eeh_token_to_phys(unsigned long token) { pte_t *ptep; unsigned long pa, vaddr; @@ -365,24 +365,22 @@ static unsigned long eeh_token_to_phys(u } /** - * eeh_check_failure - check if all 1's data is due to EEH slot freeze - * @token i/o token, should be address in the form 0xA.... - * @val value, should be all 1's (XXX why do we need this arg??) + * eeh_dn_check_failure - check if all 1's data is due to EEH slot freeze + * @dn device node + * @dev pci device, if known + * + * Check for an EEH failure for the given device node. Call this + * routine if the result of a read was all 0xff's and you want to + * find out if this is due to an EEH slot freeze event. This routine + * will query firmware for the EEH status. * - * Check for an eeh failure at the given token address. - * The given value has been read and it should be 1's (0xff, 0xffff or - * 0xffffffff). + * Returns 0 if there has not been an EEH error; otherwise returns + * an error code. * - * Probe to determine if an error actually occurred. If not return val. - * Otherwise panic. - * - * Note this routine might be called in an interrupt context ... + * It is safe to call this routine in an interrupt context. */ -unsigned long eeh_check_failure(void *token, unsigned long val) +int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev) { - unsigned long addr; - struct pci_dev *dev; - struct device_node *dn; int ret; int rets[2]; unsigned long flags; @@ -390,30 +388,19 @@ unsigned long eeh_check_failure(void *to __get_cpu_var(total_mmio_ffs)++; if (!eeh_subsystem_enabled) - return val; + return 0; - /* Finding the phys addr + pci device; this is pretty quick. */ - addr = eeh_token_to_phys((unsigned long)token); - dev = pci_get_device_by_addr(addr); - if (!dev) - return val; - - dn = pci_device_to_OF_node(dev); - if (!dn) { - pci_dev_put(dev); - return val; - } + if (!dn) + return 0; /* Access to IO BARs might get this far and still not want checking. */ if (!(dn->eeh_mode & EEH_MODE_SUPPORTED) || dn->eeh_mode & EEH_MODE_NOCHECK) { - pci_dev_put(dev); - return val; + return 0; } if (!dn->eeh_config_addr) { - pci_dev_put(dev); - return val; + return 0; } /* @@ -439,7 +426,7 @@ unsigned long eeh_check_failure(void *to BUID_LO(dn->phb->buid), NULL, 0, virt_to_phys(slot_errbuf), eeh_error_buf_size, - 2 /* Permanent Error */); + 1 /* Temporary Error */); if (log_event == 0) log_error(slot_errbuf, ERR_TYPE_RTAS_LOG, @@ -447,6 +434,10 @@ unsigned long eeh_check_failure(void *to spin_unlock_irqrestore(&slot_errbuf_lock, flags); + printk(KERN_ERR "EEH: MMIO failure (%d) on device:%s %s\n", + rets[0], pci_name(dev), pci_pretty_name(dev)); + WARN_ON(1); + /* * XXX We should create a separate sysctl for this. * @@ -456,19 +447,53 @@ unsigned long eeh_check_failure(void *to */ if (panic_on_oops) { panic("EEH: MMIO failure (%d) on device:%s %s\n", - rets[0], pci_name(dev), pci_pretty_name(dev)); + rets[0], dn->name, dn->full_name); } else { __get_cpu_var(ignored_failures)++; printk(KERN_INFO "EEH: MMIO failure (%d) on device:%s %s\n", - rets[0], pci_name(dev), pci_pretty_name(dev)); + rets[0], dn->name, dn->full_name); } } else { __get_cpu_var(false_positives)++; } + return 0; +} + +EXPORT_SYMBOL(eeh_dn_check_failure); + +/** + * eeh_check_failure - check if all 1's data is due to EEH slot freeze + * @token i/o token, should be address in the form 0xA.... + * @val value, should be all 1's (XXX why do we need this arg??) + * + * Check for an eeh failure at the given token address. + * Check for an EEH failure at the given token address. Call this + * routine if the result of a read was all 0xff's and you want to + * find out if this is due to an EEH slot freeze event. This routine + * will query firmware for the EEH status. + * + * Note this routine is safe to call in an interrupt context. + */ +unsigned long eeh_check_failure(void *token, unsigned long val) +{ + unsigned long addr; + struct pci_dev *dev; + struct device_node *dn; + + /* Finding the phys addr + pci device; this is pretty quick. */ + addr = eeh_token_to_phys((unsigned long)token); + dev = pci_get_device_by_addr(addr); + if (!dev) + return val; + + dn = pci_device_to_OF_node(dev); + eeh_dn_check_failure (dn, dev); + pci_dev_put(dev); return val; } + EXPORT_SYMBOL(eeh_check_failure); struct eeh_early_enable_info { --- linux-2.6.9-rc1/arch/ppc64/kernel/entry.S 2004-08-24 00:01:51.000000000 -0700 +++ 25/arch/ppc64/kernel/entry.S 2004-09-03 00:56:38.090370032 -0700 @@ -393,9 +393,17 @@ BEGIN_FTR_SECTION cmpd cr1,r6,r9 /* or is new ESID the same as current ESID? */ cror eq,4*cr1+eq,eq beq 2f /* if yes, don't slbie it */ - oris r6,r6,0x0800 /* set C (class) bit */ - slbie r6 - slbie r6 /* Workaround POWER5 < DD2.1 issue */ + oris r0,r6,0x0800 /* set C (class) bit */ + + /* Bolt in the new stack SLB entry */ + ld r7,KSP_VSID(r4) /* Get new stack's VSID */ + oris r6,r6,(SLB_ESID_V)@h + ori r6,r6,(SLB_NUM_BOLTED-1)@l + slbie r0 + slbie r0 /* Workaround POWER5 < DD2.1 issue */ + slbmte r7,r6 + isync + 2: END_FTR_SECTION_IFSET(CPU_FTR_SLB) clrrdi r7,r8,THREAD_SHIFT /* base of new stack */ --- linux-2.6.9-rc1/arch/ppc64/kernel/head.S 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/ppc64/kernel/head.S 2004-09-02 20:51:51.000000000 -0700 @@ -322,36 +322,11 @@ label##_Iseries: \ HMT_MEDIUM; \ mtspr SPRG1,r13; /* save r13 */ \ EXCEPTION_PROLOG_ISERIES_1(PACA_EXGEN); \ - lbz r10,PACAPROFENABLED(r13); \ - cmpwi r10,0; \ - bne- label##_Iseries_profile; \ -label##_Iseries_prof_ret: \ lbz r10,PACAPROCENABLED(r13); \ cmpwi 0,r10,0; \ beq- label##_Iseries_masked; \ EXCEPTION_PROLOG_ISERIES_2; \ b label##_common; \ -label##_Iseries_profile: \ - ld r12,PACALPPACA+LPPACASRR1(r13); \ - andi. r12,r12,MSR_PR; /* Test if in kernel */ \ - bne label##_Iseries_prof_ret; \ - ld r11,PACALPPACA+LPPACASRR0(r13); \ - ld r12,PACAPROFSTEXT(r13); /* _stext */ \ - subf r11,r12,r11; /* offset into kernel */ \ - lwz r12,PACAPROFSHIFT(r13); \ - srd r11,r11,r12; \ - lwz r12,PACAPROFLEN(r13); /* profile table length - 1 */ \ - cmpd r11,r12; /* off end? */ \ - ble 1f; \ - mr r11,r12; /* force into last entry */ \ -1: sldi r11,r11,2; /* convert to offset */ \ - ld r12,PACAPROFBUFFER(r13);/* profile buffer */ \ - add r12,r12,r11; \ -2: lwarx r11,0,r12; /* atomically increment */ \ - addi r11,r11,1; \ - stwcx. r11,0,r12; \ - bne- 2b; \ - b label##_Iseries_prof_ret #ifdef DO_SOFT_DISABLE #define DISABLE_INTS \ @@ -1037,6 +1012,9 @@ END_FTR_SECTION_IFCLR(CPU_FTR_SLB) * interrupts if necessary. */ beq .ret_from_except_lite + /* For a hash failure, we don't bother re-enabling interrupts */ + ble- 12f + /* * hash_page couldn't handle it, set soft interrupt enable back * to what it was before the trap. Note that .local_irq_restore @@ -1047,6 +1025,8 @@ END_FTR_SECTION_IFCLR(CPU_FTR_SLB) b 11f #else beq fast_exception_return /* Return from exception on success */ + ble- 12f /* Failure return from hash_page */ + /* fall through */ #endif @@ -1066,6 +1046,15 @@ _GLOBAL(handle_page_fault) bl .bad_page_fault b .ret_from_except +/* We have a page fault that hash_page could handle but HV refused + * the PTE insertion + */ +12: bl .save_nvgprs + addi r3,r1,STACK_FRAME_OVERHEAD + lwz r4,_DAR(r1) + bl .low_hash_fault + b .ret_from_except + /* here we have a segment miss */ _GLOBAL(do_ste_alloc) bl .ste_allocate /* try to insert stab entry */ --- linux-2.6.9-rc1/arch/ppc64/kernel/hvconsole.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/ppc64/kernel/hvconsole.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,6 +1,10 @@ /* * hvconsole.c * Copyright (C) 2004 Hollis Blanchard, IBM Corporation + * Copyright (C) 2004 IBM Corporation + * + * Additional Author(s): + * Ryan S. Arnold * * LPAR console support. * @@ -22,14 +26,22 @@ #include #include #include -#include #include +#include -int hvc_get_chars(int index, char *buf, int count) +/** + * hvc_get_chars - retrieve characters from firmware for denoted vterm adatper + * @vtermno: The vtermno or unit_address of the adapter from which to fetch the + * data. + * @buf: The character buffer into which to put the character data fetched from + * firmware. + * @count: not used? + */ +int hvc_get_chars(uint32_t vtermno, char *buf, int count) { unsigned long got; - if (plpar_hcall(H_GET_TERM_CHAR, index, 0, 0, 0, &got, + if (plpar_hcall(H_GET_TERM_CHAR, vtermno, 0, 0, 0, &got, (unsigned long *)buf, (unsigned long *)buf+1) == H_Success) { /* * Work around a HV bug where it gives us a null @@ -53,40 +65,56 @@ int hvc_get_chars(int index, char *buf, EXPORT_SYMBOL(hvc_get_chars); -int hvc_put_chars(int index, const char *buf, int count) +/** + * hvc_put_chars: send characters to firmware for denoted vterm adapter + * @vtermno: The vtermno or unit_address of the adapter from which the data + * originated. + * @buf: The character buffer that contains the character data to send to + * firmware. + * @count: Send this number of characters. + */ +int hvc_put_chars(uint32_t vtermno, const char *buf, int count) { unsigned long *lbuf = (unsigned long *) buf; long ret; - ret = plpar_hcall_norets(H_PUT_TERM_CHAR, index, count, lbuf[0], + ret = plpar_hcall_norets(H_PUT_TERM_CHAR, vtermno, count, lbuf[0], lbuf[1]); if (ret == H_Success) return count; if (ret == H_Busy) return 0; - return -1; + return -EIO; } EXPORT_SYMBOL(hvc_put_chars); -/* return the number of client vterms present */ -/* XXX this requires an interface change to handle multiple discontiguous - * vterms */ -int hvc_count(int *start_termno) +/* + * We hope/assume that the first vty found corresponds to the first console + * device. + */ +int hvc_find_vtys(void) { struct device_node *vty; int num_found = 0; - /* consider only the first vty node. - * we should _always_ be able to find one. */ - vty = of_find_node_by_name(NULL, "vty"); - if (vty && device_is_compatible(vty, "hvterm1")) { - u32 *termno = (u32 *)get_property(vty, "reg", NULL); - - if (termno && start_termno) - *start_termno = *termno; - num_found = 1; - of_node_put(vty); + for (vty = of_find_node_by_name(NULL, "vty"); vty != NULL; + vty = of_find_node_by_name(vty, "vty")) { + uint32_t *vtermno; + + /* We have statically defined space for only a certain number of + * console adapters. */ + if (num_found >= MAX_NR_HVC_CONSOLES) + break; + + vtermno = (uint32_t *)get_property(vty, "reg", NULL); + if (!vtermno) + continue; + + if (device_is_compatible(vty, "hvterm1")) { + hvc_instantiate(*vtermno, num_found); + ++num_found; + } } return num_found; --- linux-2.6.9-rc1/arch/ppc64/kernel/irq.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/ppc64/kernel/irq.c 2004-09-02 20:51:51.000000000 -0700 @@ -363,14 +363,16 @@ skip: int handle_irq_event(int irq, struct pt_regs *regs, struct irqaction *action) { int status = 0; - int retval = 0; + int ret, retval = 0; if (!(action->flags & SA_INTERRUPT)) local_irq_enable(); do { - status |= action->flags; - retval |= action->handler(irq, action->dev_id, regs); + ret = action->handler(irq, action->dev_id, regs); + if (ret == IRQ_HANDLED) + status |= action->flags; + retval |= ret; action = action->next; } while (action); if (status & SA_SAMPLE_RANDOM) @@ -758,44 +760,6 @@ out: return ret; } -static int prof_cpu_mask_read_proc (char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - int len = cpumask_scnprintf(page, count, *(cpumask_t *)data); - if (count - len < 2) - return -EINVAL; - len += sprintf(page + len, "\n"); - return len; -} - -static int prof_cpu_mask_write_proc (struct file *file, const char __user *buffer, - unsigned long count, void *data) -{ - cpumask_t *mask = (cpumask_t *)data; - unsigned long full_count = count, err; - cpumask_t new_value; - - err = cpumask_parse(buffer, count, new_value); - if (err) - return err; - - *mask = new_value; - -#ifdef CONFIG_PPC_ISERIES - { - unsigned i; - for (i=0; inlink = 1; - entry->data = (void *)&prof_cpu_mask; - entry->read_proc = prof_cpu_mask_read_proc; - entry->write_proc = prof_cpu_mask_write_proc; + create_prof_cpu_mask(root_irq_dir); /* * Create entries for all existing IRQs. @@ -978,7 +931,7 @@ void irq_ctx_init(void) struct thread_info *tp; int i; - for (i = 0; i < NR_CPUS; i++) { + for_each_cpu(i) { memset((void *)softirq_ctx[i], 0, THREAD_SIZE); tp = softirq_ctx[i]; tp->cpu = i; --- linux-2.6.9-rc1/arch/ppc64/kernel/iSeries_setup.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/arch/ppc64/kernel/iSeries_setup.c 2004-09-02 20:51:51.000000000 -0700 @@ -36,6 +36,7 @@ #include #include #include +#include #include #include "iSeries_setup.h" @@ -54,17 +55,12 @@ #include /* Function Prototypes */ -extern void abort(void); extern void ppcdbg_initialize(void); -extern void iSeries_pcibios_init(void); extern void tce_init_iSeries(void); static void build_iSeries_Memory_Map(void); static void setup_iSeries_cache_sizes(void); static void iSeries_bolt_kernel(unsigned long saddr, unsigned long eaddr); -extern void build_valid_hpte(unsigned long vsid, unsigned long ea, unsigned long pa, - pte_t *ptep, unsigned hpteflags, unsigned bolted); -static void iSeries_setup_dprofile(void); extern void iSeries_setup_arch(void); extern void iSeries_pci_final_fixup(void); @@ -77,16 +73,10 @@ static unsigned long tbFreqHz; static unsigned long tbFreqMhz; static unsigned long tbFreqMhzHundreths; -unsigned long dprof_shift; -unsigned long dprof_len; -unsigned int *dprof_buffer; - int piranha_simulator; int boot_cpuid; -extern char _end[]; - extern int rd_size; /* Defined in drivers/block/rd.c */ extern unsigned long klimit; extern unsigned long embedded_sysmap_start; @@ -366,30 +356,6 @@ void __init iSeries_init(unsigned long r } *p = 0; - if (strstr(cmd_line, "dprofile=")) { - for (q = cmd_line; (p = strstr(q, "dprofile=")) != 0; ) { - unsigned long size, new_klimit; - - q = p + 9; - if ((p > cmd_line) && (p[-1] != ' ')) - continue; - dprof_shift = simple_strtoul(q, &q, 0); - dprof_len = (unsigned long)_etext - - (unsigned long)_stext; - dprof_len >>= dprof_shift; - size = ((dprof_len * sizeof(unsigned int)) + - (PAGE_SIZE-1)) & PAGE_MASK; - dprof_buffer = (unsigned int *)((klimit + - (PAGE_SIZE-1)) & PAGE_MASK); - new_klimit = ((unsigned long)dprof_buffer) + size; - lmb_reserve(__pa(klimit), (new_klimit-klimit)); - klimit = new_klimit; - memset(dprof_buffer, 0, size); - } - } - - iSeries_setup_dprofile(); - mf_init(); mf_initialized = 1; mb(); @@ -837,22 +803,6 @@ void iSeries_fixup_klimit(void) } } -static void iSeries_setup_dprofile(void) -{ - if (dprof_buffer) { - unsigned i; - - for (i = 0; i < NR_CPUS; ++i) { - paca[i].prof_shift = dprof_shift; - paca[i].prof_len = dprof_len - 1; - paca[i].prof_buffer = dprof_buffer; - paca[i].prof_stext = (unsigned *)_stext; - mb(); - paca[i].prof_enabled = 1; - } - } -} - int __init iSeries_src_init(void) { /* clear the progress line */ --- linux-2.6.9-rc1/arch/ppc64/kernel/lmb.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/ppc64/kernel/lmb.c 2004-09-03 00:56:40.093065576 -0700 @@ -20,7 +20,7 @@ #include #include -struct lmb lmb __initdata; +struct lmb lmb; static unsigned long __init lmb_addrs_overlap(unsigned long base1, unsigned long size1, --- linux-2.6.9-rc1/arch/ppc64/kernel/misc.S 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/ppc64/kernel/misc.S 2004-09-02 20:51:51.000000000 -0700 @@ -454,12 +454,6 @@ _GLOBAL(_outsl_ns) sync blr -_GLOBAL(abs) - cmpi 0,r3,0 - bge 10f - neg r3,r3 -10: blr - _GLOBAL(_get_PVR) mfspr r3,PVR blr --- linux-2.6.9-rc1/arch/ppc64/kernel/pacaData.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/ppc64/kernel/pacaData.c 2004-09-03 00:56:39.307185048 -0700 @@ -27,13 +27,6 @@ struct systemcfg *systemcfg; * field correctly */ extern unsigned long __toc_start; -/* Stack space used when we detect a bad kernel stack pointer, and - * early in SMP boots before relocation is enabled. - * - * ABI requires stack to be 128-byte aligned - */ -char emergency_stack[PAGE_SIZE * NR_CPUS] __attribute__((aligned(128))); - /* The Paca is an array with one entry per processor. Each contains an * ItLpPaca, which contains the information shared between the * hypervisor and Linux. Each also contains an ItLpRegSave area which @@ -55,7 +48,6 @@ char emergency_stack[PAGE_SIZE * NR_CPUS .kernel_toc = (unsigned long)(&__toc_start) + 0x8000UL, \ .stab_real = (asrr), /* Real pointer to segment table */ \ .stab_addr = (asrv), /* Virt pointer to segment table */ \ - .emergency_sp = &emergency_stack[((number)+1) * PAGE_SIZE], \ .cpu_start = (start), /* Processor start */ \ .lppaca = { \ .xDesc = 0xd397d781, /* "LpPa" */ \ --- linux-2.6.9-rc1/arch/ppc64/kernel/pci.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/ppc64/kernel/pci.c 2004-09-03 00:56:33.062134440 -0700 @@ -297,7 +297,7 @@ static int __init pcibios_init(void) ppc_md.pcibios_fixup(); /* Cache the location of the ISA bridge (if we have one) */ - ppc64_isabridge_dev = pci_find_class(PCI_CLASS_BRIDGE_ISA << 8, NULL); + ppc64_isabridge_dev = pci_get_class(PCI_CLASS_BRIDGE_ISA << 8, NULL); if (ppc64_isabridge_dev != NULL) printk("ISA bridge at %s\n", pci_name(ppc64_isabridge_dev)); --- linux-2.6.9-rc1/arch/ppc64/kernel/pmac_feature.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/arch/ppc64/kernel/pmac_feature.c 2004-09-03 00:57:15.929617592 -0700 @@ -611,7 +611,7 @@ int __init pmac_feature_late_init(void) device_initcall(pmac_feature_late_init); - +#if 0 static void dump_HT_speeds(char *name, u32 cfg, u32 frq) { int freqs[16] = { 200,300,400,500,600,800,1000,0,0,0,0,0,0,0,0,0 }; @@ -625,6 +625,7 @@ static void dump_HT_speeds(char *name, u name, freqs[freq], bits[(cfg >> 28) & 0x7], bits[(cfg >> 24) & 0x7]); } +#endif void __init pmac_check_ht_link(void) { --- linux-2.6.9-rc1/arch/ppc64/kernel/pmac_smp.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/arch/ppc64/kernel/pmac_smp.c 2004-09-03 00:56:40.988929384 -0700 @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include --- linux-2.6.9-rc1/arch/ppc64/kernel/pmac_time.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/arch/ppc64/kernel/pmac_time.c 2004-09-03 00:56:40.988929384 -0700 @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -25,7 +26,6 @@ #include #include #include -#include #include #include --- linux-2.6.9-rc1/arch/ppc64/kernel/process.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/ppc64/kernel/process.c 2004-09-03 00:56:40.989929232 -0700 @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -47,7 +48,6 @@ #include #include #include -#include #include #include #include @@ -356,6 +356,16 @@ copy_thread(int nr, unsigned long clone_ kregs = (struct pt_regs *) sp; sp -= STACK_FRAME_OVERHEAD; p->thread.ksp = sp; + if (cur_cpu_spec->cpu_features & CPU_FTR_SLB) { + unsigned long sp_vsid = get_kernel_vsid(sp); + + sp_vsid <<= SLB_VSID_SHIFT; + sp_vsid |= SLB_VSID_KERNEL; + if (cur_cpu_spec->cpu_features & CPU_FTR_16M_PAGE) + sp_vsid |= SLB_VSID_L; + + p->thread.ksp_vsid = sp_vsid; + } /* * The PPC64 ABI makes use of a TOC to contain function @@ -458,7 +468,7 @@ int sys_clone(unsigned long clone_flags, } } - return do_fork(clone_flags & ~CLONE_IDLETASK, p2, regs, 0, + return do_fork(clone_flags, p2, regs, 0, (int __user *)parent_tidptr, (int __user *)child_tidptr); } --- linux-2.6.9-rc1/arch/ppc64/kernel/prom.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/arch/ppc64/kernel/prom.c 2004-09-02 20:51:51.000000000 -0700 @@ -919,40 +919,11 @@ static void __init prom_hold_cpus(unsign unsigned long secondary_hold = virt_to_abs(*PTRRELOC((unsigned long *)__secondary_hold)); struct systemcfg *_systemcfg = RELOC(systemcfg); - struct paca_struct *lpaca = PTRRELOC(&paca[0]); struct prom_t *_prom = PTRRELOC(&prom); #ifdef CONFIG_SMP struct naca_struct *_naca = RELOC(naca); #endif - /* On pmac, we just fill out the various global bitmasks and - * arrays indicating our CPUs are here, they are actually started - * later on from pmac_smp - */ - if (_systemcfg->platform == PLATFORM_POWERMAC) { - for (node = 0; prom_next_node(&node); ) { - type[0] = 0; - prom_getprop(node, "device_type", type, sizeof(type)); - if (strcmp(type, RELOC("cpu")) != 0) - continue; - reg = -1; - prom_getprop(node, "reg", ®, sizeof(reg)); - lpaca[cpuid].hw_cpu_id = reg; - -#ifdef CONFIG_SMP - cpu_set(cpuid, RELOC(cpu_possible_map)); - cpu_set(cpuid, RELOC(cpu_present_map)); - if (reg == 0) - cpu_set(cpuid, RELOC(cpu_online_map)); -#endif /* CONFIG_SMP */ - cpuid++; - } - return; - } - - /* Initially, we must have one active CPU. */ - _systemcfg->processorCount = 1; - prom_debug("prom_hold_cpus: start...\n"); prom_debug(" 1) spinloop = 0x%x\n", (unsigned long)spinloop); prom_debug(" 1) *spinloop = 0x%x\n", *spinloop); @@ -996,7 +967,6 @@ static void __init prom_hold_cpus(unsign prom_debug("\ncpuid = 0x%x\n", cpuid); prom_debug("cpu hw idx = 0x%x\n", reg); - lpaca[cpuid].hw_cpu_id = reg; /* Init the acknowledge var which will be reset by * the secondary cpu when it awakens from its OF @@ -1038,23 +1008,13 @@ static void __init prom_hold_cpus(unsign * even if we never start it. */ if (cpuid >= NR_CPUS) goto next; -#ifdef CONFIG_SMP - /* Set the number of active processors. */ - _systemcfg->processorCount++; - cpu_set(cpuid, RELOC(cpu_possible_map)); - cpu_set(cpuid, RELOC(cpu_present_map)); -#endif } else { prom_printf("... failed: %x\n", *acknowledge); } } #ifdef CONFIG_SMP - else { + else prom_printf("%x : booting cpu %s\n", cpuid, path); - cpu_set(cpuid, RELOC(cpu_possible_map)); - cpu_set(cpuid, RELOC(cpu_online_map)); - cpu_set(cpuid, RELOC(cpu_present_map)); - } #endif next: #ifdef CONFIG_SMP @@ -1063,13 +1023,9 @@ next: cpuid++; if (cpuid >= NR_CPUS) continue; - lpaca[cpuid].hw_cpu_id = interrupt_server[i]; prom_printf("%x : preparing thread ... ", interrupt_server[i]); if (_naca->smt_state) { - cpu_set(cpuid, RELOC(cpu_present_map)); - cpu_set(cpuid, RELOC(cpu_possible_map)); - _systemcfg->processorCount++; prom_printf("available\n"); } else { prom_printf("not available\n"); @@ -1099,11 +1055,7 @@ next: pir & 0x3ff; } } -/* cpu_set(i+1, cpu_online_map); */ - cpu_set(i+1, RELOC(cpu_possible_map)); - cpu_set(i+1, RELOC(cpu_present_map)); } - _systemcfg->processorCount *= 2; } else { prom_printf("Processor is not HMT capable\n"); } --- linux-2.6.9-rc1/arch/ppc64/kernel/pSeries_lpar.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/ppc64/kernel/pSeries_lpar.c 2004-09-03 00:56:39.030227152 -0700 @@ -112,6 +112,22 @@ long plpar_tce_put(unsigned long liobn, return plpar_hcall_norets(H_PUT_TCE, liobn, ioba, tceval); } +long plpar_tce_put_indirect(unsigned long liobn, + unsigned long ioba, + unsigned long page, + unsigned long count) +{ + return plpar_hcall_norets(H_PUT_TCE_INDIRECT, liobn, ioba, page, count); +} + +long plpar_tce_stuff(unsigned long liobn, + unsigned long ioba, + unsigned long tceval, + unsigned long count) +{ + return plpar_hcall_norets(H_STUFF_TCE, liobn, ioba, tceval, count); +} + long plpar_get_term_char(unsigned long termno, unsigned long *len_ret, char *buf_ret) @@ -161,6 +177,71 @@ static void tce_build_pSeriesLP(struct i } } +DEFINE_PER_CPU(void *, tce_page) = NULL; + +static void tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, + long npages, unsigned long uaddr, + enum dma_data_direction direction) +{ + u64 rc; + union tce_entry tce, *tcep; + long l, limit; + + if (npages == 1) + return tce_build_pSeriesLP(tbl, tcenum, npages, uaddr, + direction); + + tcep = __get_cpu_var(tce_page); + + /* This is safe to do since interrupts are off when we're called + * from iommu_alloc{,_sg}() + */ + if (!tcep) { + tcep = (void *)__get_free_page(GFP_ATOMIC); + /* If allocation fails, fall back to the loop implementation */ + if (!tcep) + return tce_build_pSeriesLP(tbl, tcenum, npages, + uaddr, direction); + __get_cpu_var(tce_page) = tcep; + } + + tce.te_word = 0; + tce.te_rpn = (virt_to_abs(uaddr)) >> PAGE_SHIFT; + tce.te_rdwr = 1; + if (direction != DMA_TO_DEVICE) + tce.te_pciwr = 1; + + /* We can map max one pageful of TCEs at a time */ + do { + /* + * Set up the page with TCE data, looping through and setting + * the values. + */ + limit = min_t(long, npages, PAGE_SIZE/sizeof(union tce_entry)); + + for (l = 0; l < limit; l++) { + tcep[l] = tce; + tce.te_rpn++; + } + + rc = plpar_tce_put_indirect((u64)tbl->it_index, + (u64)tcenum << 12, + (u64)virt_to_abs(tcep), + limit); + + npages -= limit; + tcenum += limit; + } while (npages > 0 && !rc); + + if (rc && printk_ratelimit()) { + printk("tce_buildmulti_pSeriesLP: plpar_tce_put failed. rc=%ld\n", rc); + printk("\tindex = 0x%lx\n", (u64)tbl->it_index); + printk("\tnpages = 0x%lx\n", (u64)npages); + printk("\ttce[0] val = 0x%lx\n", tcep[0].te_word); + show_stack(current, (unsigned long *)__get_SP()); + } +} + static void tce_free_pSeriesLP(struct iommu_table *tbl, long tcenum, long npages) { u64 rc; @@ -169,23 +250,45 @@ static void tce_free_pSeriesLP(struct io tce.te_word = 0; while (npages--) { - rc = plpar_tce_put((u64)tbl->it_index, + rc = plpar_tce_put((u64)tbl->it_index, (u64)tcenum << 12, - tce.te_word ); - + tce.te_word); + if (rc && printk_ratelimit()) { - printk("tce_free_pSeriesLP: plpar_tce_put failed\n"); - printk("\trc = %ld\n", rc); + printk("tce_free_pSeriesLP: plpar_tce_put failed. rc=%ld\n", rc); printk("\tindex = 0x%lx\n", (u64)tbl->it_index); printk("\ttcenum = 0x%lx\n", (u64)tcenum); printk("\ttce val = 0x%lx\n", tce.te_word ); show_stack(current, (unsigned long *)__get_SP()); } - + tcenum++; } } + +static void tce_freemulti_pSeriesLP(struct iommu_table *tbl, long tcenum, long npages) +{ + u64 rc; + union tce_entry tce; + + tce.te_word = 0; + + rc = plpar_tce_stuff((u64)tbl->it_index, + (u64)tcenum << 12, + tce.te_word, + npages); + + if (rc && printk_ratelimit()) { + printk("tce_freemulti_pSeriesLP: plpar_tce_stuff failed\n"); + printk("\trc = %ld\n", rc); + printk("\tindex = 0x%lx\n", (u64)tbl->it_index); + printk("\tnpages = 0x%lx\n", (u64)npages); + printk("\ttce val = 0x%lx\n", tce.te_word ); + show_stack(current, (unsigned long *)__get_SP()); + } +} + int vtermno; /* virtual terminal# for udbg */ static void udbg_putcLP(unsigned char c) @@ -315,8 +418,13 @@ void pSeriesLP_init_early(void) tce_init_pSeries(); - ppc_md.tce_build = tce_build_pSeriesLP; - ppc_md.tce_free = tce_free_pSeriesLP; + if (cur_cpu_spec->firmware_features & FW_FEATURE_MULTITCE) { + ppc_md.tce_build = tce_buildmulti_pSeriesLP; + ppc_md.tce_free = tce_freemulti_pSeriesLP; + } else { + ppc_md.tce_build = tce_build_pSeriesLP; + ppc_md.tce_free = tce_free_pSeriesLP; + } pci_iommu_init(); @@ -377,7 +485,7 @@ long pSeries_lpar_hpte_insert(unsigned l lpar_rc = plpar_hcall(H_ENTER, flags, hpte_group, lhpte.dw0.dword0, lhpte.dw1.dword1, &slot, &dummy0, &dummy1); - if (lpar_rc == H_PTEG_Full) + if (unlikely(lpar_rc == H_PTEG_Full)) return -1; /* @@ -385,7 +493,7 @@ long pSeries_lpar_hpte_insert(unsigned l * will fail. However we must catch the failure in hash_page * or we will loop forever, so return -2 in this case. */ - if (lpar_rc != H_Success) + if (unlikely(lpar_rc != H_Success)) return -2; /* Because of iSeries, we have to pass down the secondary @@ -415,9 +523,7 @@ static long pSeries_lpar_hpte_remove(uns if (lpar_rc == H_Success) return i; - if (lpar_rc != H_Not_Found) - panic("Bad return code from pte remove rc = %lx\n", - lpar_rc); + BUG_ON(lpar_rc != H_Not_Found); slot_offset++; slot_offset &= 0x7; @@ -447,8 +553,7 @@ static long pSeries_lpar_hpte_updatepp(u if (lpar_rc == H_Not_Found) return -1; - if (lpar_rc != H_Success) - panic("bad return code from pte protect rc = %lx\n", lpar_rc); + BUG_ON(lpar_rc != H_Success); return 0; } @@ -464,11 +569,10 @@ static unsigned long pSeries_lpar_hpte_g /* Do not need RPN to logical page translation */ /* No cross CEC PFT access */ flags = 0; - + lpar_rc = plpar_pte_read(flags, slot, &dword0, &dummy_word1); - if (lpar_rc != H_Success) - panic("Error on pte read in get_hpte0 rc = %lx\n", lpar_rc); + BUG_ON(lpar_rc != H_Success); return dword0; } @@ -519,15 +623,12 @@ static void pSeries_lpar_hpte_updatebolt vpn = va >> PAGE_SHIFT; slot = pSeries_lpar_hpte_find(vpn); - if (slot == -1) - panic("updateboltedpp: Could not find page to bolt\n"); + BUG_ON(slot == -1); flags = newpp & 3; lpar_rc = plpar_pte_protect(flags, slot, 0); - if (lpar_rc != H_Success) - panic("Bad return code from pte bolted protect rc = %lx\n", - lpar_rc); + BUG_ON(lpar_rc != H_Success); } static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va, @@ -546,8 +647,7 @@ static void pSeries_lpar_hpte_invalidate if (lpar_rc == H_Not_Found) return; - if (lpar_rc != H_Success) - panic("Bad return code from invalidate rc = %lx\n", lpar_rc); + BUG_ON(lpar_rc != H_Success); } /* --- linux-2.6.9-rc1/arch/ppc64/kernel/pSeries_pci.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/arch/ppc64/kernel/pSeries_pci.c 2004-09-03 00:56:38.375326712 -0700 @@ -68,7 +68,9 @@ static int rtas_read_config(struct devic int ret; if (!dn) - return -2; + return PCIBIOS_DEVICE_NOT_FOUND; + if (where & (size - 1)) + return PCIBIOS_BAD_REGISTER_NUMBER; addr = (dn->busno << 16) | (dn->devfn << 8) | where; buid = dn->phb->buid; @@ -79,7 +81,15 @@ static int rtas_read_config(struct devic ret = rtas_call(read_pci_config, 2, 2, &returnval, addr, size); } *val = returnval; - return ret; + + if (ret) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (returnval == EEH_IO_ERROR_VALUE(size) + && eeh_dn_check_failure (dn, NULL)) + return PCIBIOS_DEVICE_NOT_FOUND; + + return PCIBIOS_SUCCESSFUL; } static int rtas_pci_read_config(struct pci_bus *bus, @@ -106,7 +116,9 @@ static int rtas_write_config(struct devi int ret; if (!dn) - return -2; + return PCIBIOS_DEVICE_NOT_FOUND; + if (where & (size - 1)) + return PCIBIOS_BAD_REGISTER_NUMBER; addr = (dn->busno << 16) | (dn->devfn << 8) | where; buid = dn->phb->buid; @@ -115,7 +127,11 @@ static int rtas_write_config(struct devi } else { ret = rtas_call(write_pci_config, 3, 1, NULL, addr, size, (ulong)val); } - return ret; + + if (ret) + return PCIBIOS_DEVICE_NOT_FOUND; + + return PCIBIOS_SUCCESSFUL; } static int rtas_pci_write_config(struct pci_bus *bus, --- linux-2.6.9-rc1/arch/ppc64/kernel/ras.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/ppc64/kernel/ras.c 2004-09-03 00:56:39.165206632 -0700 @@ -161,7 +161,8 @@ ras_epow_interrupt(int irq, void *dev_id RAS_VECTOR_OFFSET, virt_irq_to_real(irq_offset_down(irq)), RTAS_EPOW_WARNING | RTAS_POWERMGM_EVENTS, - critical, __pa(&ras_log_buf), RTAS_ERROR_LOG_MAX); + critical, __pa(&ras_log_buf), + rtas_get_error_log_max()); udbg_printf("EPOW <0x%lx 0x%x 0x%x>\n", *((unsigned long *)&ras_log_buf), status, state); @@ -196,7 +197,8 @@ ras_error_interrupt(int irq, void *dev_i RAS_VECTOR_OFFSET, virt_irq_to_real(irq_offset_down(irq)), RTAS_INTERNAL_ERROR, 1 /*Time Critical */, - __pa(&ras_log_buf), RTAS_ERROR_LOG_MAX); + __pa(&ras_log_buf), + rtas_get_error_log_max()); rtas_elog = (struct rtas_error_log *)ras_log_buf; --- linux-2.6.9-rc1/arch/ppc64/kernel/rtas.c 2004-08-24 00:01:55.000000000 -0700 +++ 25/arch/ppc64/kernel/rtas.c 2004-09-03 00:56:39.166206480 -0700 @@ -516,6 +516,26 @@ void rtas_stop_self(void) } #endif /* CONFIG_HOTPLUG_CPU */ +/* + * Return the firmware-specified size of the error log buffer + * for all rtas calls that require an error buffer argument. + * This includes 'check-exception' and 'rtas-last-error'. + */ +int rtas_get_error_log_max(void) +{ + static int rtas_error_log_max; + if (rtas_error_log_max) + return rtas_error_log_max; + + rtas_error_log_max = rtas_token ("rtas-error-log-max"); + if ((rtas_error_log_max == RTAS_UNKNOWN_SERVICE) || + (rtas_error_log_max > RTAS_ERROR_LOG_MAX)) { + printk (KERN_WARNING "RTAS: bad log buffer size %d\n", rtas_error_log_max); + rtas_error_log_max = RTAS_ERROR_LOG_MAX; + } + return rtas_error_log_max; +} + EXPORT_SYMBOL(rtas_firmware_flash_list); EXPORT_SYMBOL(rtas_token); EXPORT_SYMBOL(rtas_call); @@ -526,3 +546,4 @@ EXPORT_SYMBOL(rtas_get_sensor); EXPORT_SYMBOL(rtas_get_power_level); EXPORT_SYMBOL(rtas_set_power_level); EXPORT_SYMBOL(rtas_set_indicator); +EXPORT_SYMBOL(rtas_get_error_log_max); --- linux-2.6.9-rc1/arch/ppc64/kernel/rtasd.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/ppc64/kernel/rtasd.c 2004-09-03 00:56:39.166206480 -0700 @@ -373,21 +373,8 @@ static int get_eventscan_parms(void) rtas_event_scan_rate = *ip; DEBUG("rtas-event-scan-rate %d\n", rtas_event_scan_rate); - ip = (int *)get_property(node, "rtas-error-log-max", NULL); - if (ip == NULL) { - printk(KERN_ERR "rtasd: no rtas-error-log-max\n"); - of_node_put(node); - return -1; - } - rtas_error_log_max = *ip; - DEBUG("rtas-error-log-max %d\n", rtas_error_log_max); - - if (rtas_error_log_max > RTAS_ERROR_LOG_MAX) { - printk(KERN_ERR "rtasd: truncated error log from %d to %d bytes\n", rtas_error_log_max, RTAS_ERROR_LOG_MAX); - rtas_error_log_max = RTAS_ERROR_LOG_MAX; - } - /* Make room for the sequence number */ + rtas_error_log_max = rtas_get_error_log_max(); rtas_error_log_buffer_max = rtas_error_log_max + sizeof(int); of_node_put(node); --- linux-2.6.9-rc1/arch/ppc64/kernel/rtc.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/arch/ppc64/kernel/rtc.c 2004-09-03 00:56:40.989929232 -0700 @@ -34,8 +34,8 @@ #include #include #include +#include -#include #include #include #include --- linux-2.6.9-rc1/arch/ppc64/kernel/setup.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/ppc64/kernel/setup.c 2004-09-03 00:56:39.308184896 -0700 @@ -67,6 +67,7 @@ extern void pmac_init(unsigned long r3, unsigned long r6, unsigned long r7); +extern void fw_feature_init(void); extern void iSeries_init( void ); extern void iSeries_init_early( void ); extern void pSeries_init_early( void ); @@ -155,6 +156,100 @@ void __init disable_early_printk(void) early_console_initialized = 0; } +#if !defined(CONFIG_PPC_ISERIES) && defined(CONFIG_SMP) +/** + * setup_cpu_maps - initialize the following cpu maps: + * cpu_possible_map + * cpu_present_map + * cpu_sibling_map + * + * Having the possible map set up early allows us to restrict allocations + * of things like irqstacks to num_possible_cpus() rather than NR_CPUS. + * + * We do not initialize the online map here; cpus set their own bits in + * cpu_online_map as they come up. + * + * This function is valid only for Open Firmware systems. finish_device_tree + * must be called before using this. + * + * While we're here, we may as well set the "physical" cpu ids in the paca. + */ +static void __init setup_cpu_maps(void) +{ + struct device_node *dn = NULL; + int cpu = 0; + + while ((dn = of_find_node_by_type(dn, "cpu")) && cpu < NR_CPUS) { + u32 *intserv; + int j, len = sizeof(u32), nthreads; + + intserv = (u32 *)get_property(dn, "ibm,ppc-interrupt-server#s", + &len); + if (!intserv) + intserv = (u32 *)get_property(dn, "reg", NULL); + + nthreads = len / sizeof(u32); + + for (j = 0; j < nthreads && cpu < NR_CPUS; j++) { + cpu_set(cpu, cpu_possible_map); + cpu_set(cpu, cpu_present_map); + set_hard_smp_processor_id(cpu, intserv[j]); + cpu++; + } + } + + /* + * On pSeries LPAR, we need to know how many cpus + * could possibly be added to this partition. + */ + if (systemcfg->platform == PLATFORM_PSERIES_LPAR && + (dn = of_find_node_by_path("/rtas"))) { + int num_addr_cell, num_size_cell, maxcpus; + unsigned int *ireg; + + num_addr_cell = prom_n_addr_cells(dn); + num_size_cell = prom_n_size_cells(dn); + + ireg = (unsigned int *) + get_property(dn, "ibm,lrdr-capacity", NULL); + + if (!ireg) + goto out; + + maxcpus = ireg[num_addr_cell + num_size_cell]; + + /* Double maxcpus for processors which have SMT capability */ + if (cur_cpu_spec->cpu_features & CPU_FTR_SMT) + maxcpus *= 2; + + if (maxcpus > NR_CPUS) { + printk(KERN_WARNING + "Partition configured for %d cpus, " + "operating system maximum is %d.\n", + maxcpus, NR_CPUS); + maxcpus = NR_CPUS; + } else + printk(KERN_INFO "Partition configured for %d cpus.\n", + maxcpus); + + for (cpu = 0; cpu < maxcpus; cpu++) + cpu_set(cpu, cpu_possible_map); + out: + of_node_put(dn); + } + + /* + * Do the sibling map; assume only two threads per processor. + */ + for_each_cpu(cpu) { + cpu_set(cpu, cpu_sibling_map[cpu]); + if (cur_cpu_spec->cpu_features & CPU_FTR_SMT) + cpu_set(cpu ^ 0x1, cpu_sibling_map[cpu]); + } + + systemcfg->processorCount = num_present_cpus(); +} +#endif /* !defined(CONFIG_PPC_ISERIES) && defined(CONFIG_SMP) */ /* * Do some initial setup of the system. The parameters are those which * were passed in from the bootloader. @@ -185,11 +280,13 @@ void setup_system(unsigned long r3, unsi #ifdef CONFIG_PPC_PSERIES case PLATFORM_PSERIES: + fw_feature_init(); pSeries_init_early(); parse_bootinfo(); break; case PLATFORM_PSERIES_LPAR: + fw_feature_init(); pSeriesLP_init_early(); parse_bootinfo(); break; @@ -220,6 +317,13 @@ void setup_system(unsigned long r3, unsi } #endif /* CONFIG_BOOTX_TEXT */ +#ifdef CONFIG_PPC_PMAC + if (systemcfg->platform == PLATFORM_POWERMAC) { + finish_device_tree(); + pmac_init(r3, r4, r5, r6, r7); + } +#endif /* CONFIG_PPC_PMAC */ + #ifdef CONFIG_PPC_PSERIES if (systemcfg->platform & PLATFORM_PSERIES) { early_console_initialized = 1; @@ -227,31 +331,32 @@ void setup_system(unsigned long r3, unsi __irq_offset_value = NUM_ISA_INTERRUPTS; finish_device_tree(); chrp_init(r3, r4, r5, r6, r7); + } +#endif /* CONFIG_PPC_PSERIES */ #ifdef CONFIG_SMP - /* Start secondary threads on SMT systems; primary threads - * are already in the running state. - */ - for_each_present_cpu(i) { - if (query_cpu_stopped - (get_hard_smp_processor_id(i)) == 0) { - printk("%16.16x : starting thread\n", i); - rtas_call(rtas_token("start-cpu"), 3, 1, &ret, - get_hard_smp_processor_id(i), - (u32)*((unsigned long *)pseries_secondary_smp_init), - i); - } +#ifndef CONFIG_PPC_ISERIES + /* + * iSeries has already initialized the cpu maps at this point. + */ + setup_cpu_maps(); +#endif /* CONFIG_PPC_ISERIES */ + +#ifdef CONFIG_PPC_PSERIES + /* Start secondary threads on SMT systems; primary threads + * are already in the running state. + */ + for_each_present_cpu(i) { + if (query_cpu_stopped(get_hard_smp_processor_id(i)) == 0) { + printk("%16.16x : starting thread\n", i); + rtas_call(rtas_token("start-cpu"), 3, 1, &ret, + get_hard_smp_processor_id(i), + (u32)*((unsigned long *)pseries_secondary_smp_init), + i); } -#endif /* CONFIG_SMP */ } #endif /* CONFIG_PPC_PSERIES */ - -#ifdef CONFIG_PPC_PMAC - if (systemcfg->platform == PLATFORM_POWERMAC) { - finish_device_tree(); - pmac_init(r3, r4, r5, r6, r7); - } -#endif /* CONFIG_PPC_PMAC */ +#endif /* CONFIG_SMP */ #if defined(CONFIG_HOTPLUG_CPU) && !defined(CONFIG_PPC_PMAC) rtas_stop_self_args.token = rtas_token("stop-self"); @@ -596,10 +701,13 @@ extern void (*calibrate_delay)(void); #ifdef CONFIG_IRQSTACKS static void __init irqstack_early_init(void) { - int i; + unsigned int i; - /* interrupt stacks must be under 256MB, we cannot afford to take SLB misses on them */ - for (i = 0; i < NR_CPUS; i++) { + /* + * interrupt stacks must be under 256MB, we cannot afford to take + * SLB misses on them. + */ + for_each_cpu(i) { softirq_ctx[i] = (struct thread_info *)__va(lmb_alloc_base(THREAD_SIZE, THREAD_SIZE, 0x10000000)); hardirq_ctx[i] = (struct thread_info *)__va(lmb_alloc_base(THREAD_SIZE, @@ -611,6 +719,24 @@ static void __init irqstack_early_init(v #endif /* + * Stack space used when we detect a bad kernel stack pointer, and + * early in SMP boots before relocation is enabled. + */ +static void __init emergency_stack_init(void) +{ + unsigned int i; + + /* + * Emergency stacks must be under 256MB, we cannot afford to take + * SLB misses on them. The ABI also requires them to be 128-byte + * aligned. + */ + for_each_cpu(i) + paca[i].emergency_sp = __va(lmb_alloc_base(PAGE_SIZE, 128, + 0x10000000)) + PAGE_SIZE; +} + +/* * Called into from start_kernel, after lock_kernel has been called. * Initializes bootmem, which is unsed to manage page allocation until * mem_init is called. @@ -656,6 +782,7 @@ void __init setup_arch(char **cmdline_p) *cmdline_p = cmd_line; irqstack_early_init(); + emergency_stack_init(); /* set up the bootmem stuff with available memory */ do_init_bootmem(); --- linux-2.6.9-rc1/arch/ppc64/kernel/signal32.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/ppc64/kernel/signal32.c 2004-09-02 20:51:51.000000000 -0700 @@ -699,9 +699,7 @@ badframe: printk("badframe in handle_rt_signal, regs=%p frame=%p newsp=%lx\n", regs, frame, newsp); #endif - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } static long do_setcontext32(struct ucontext32 __user *ucp, struct pt_regs *regs, int sig) @@ -864,9 +862,7 @@ badframe: printk("badframe in handle_signal, regs=%p frame=%x newsp=%x\n", regs, frame, *newspp); #endif - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } /* @@ -928,18 +924,16 @@ badframe: int do_signal32(sigset_t *oldset, struct pt_regs *regs) { siginfo_t info; - struct k_sigaction *ka; unsigned int frame, newsp; int signr, ret; + struct k_sigaction ka; if (!oldset) oldset = ¤t->blocked; newsp = frame = 0; - signr = get_signal_to_deliver(&info, regs, NULL); - - ka = (signr == 0)? NULL: ¤t->sighand->action[signr-1]; + signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (TRAP(regs) == 0x0C00 /* System Call! */ && regs->ccr & 0x10000000 /* error signalled */ @@ -950,7 +944,7 @@ int do_signal32(sigset_t *oldset, struct if (signr > 0 && (ret == ERESTARTNOHAND || ret == ERESTART_RESTARTBLOCK || (ret == ERESTARTSYS - && !(ka->sa.sa_flags & SA_RESTART)))) { + && !(ka.sa.sa_flags & SA_RESTART)))) { /* make the system call return an EINTR error */ regs->result = -EINTR; regs->gpr[3] = EINTR; @@ -969,7 +963,7 @@ int do_signal32(sigset_t *oldset, struct if (signr == 0) return 0; /* no signals delivered */ - if ((ka->sa.sa_flags & SA_ONSTACK) && current->sas_ss_size + if ((ka.sa.sa_flags & SA_ONSTACK) && current->sas_ss_size && (!on_sig_stack(regs->gpr[1]))) newsp = (current->sas_ss_sp + current->sas_ss_size); else @@ -977,17 +971,15 @@ int do_signal32(sigset_t *oldset, struct newsp &= ~0xfUL; /* Whee! Actually deliver the signal. */ - if (ka->sa.sa_flags & SA_SIGINFO) - handle_rt_signal32(signr, ka, &info, oldset, regs, newsp); + if (ka.sa.sa_flags & SA_SIGINFO) + handle_rt_signal32(signr, &ka, &info, oldset, regs, newsp); else - handle_signal32(signr, ka, &info, oldset, regs, newsp); - - if (ka->sa.sa_flags & SA_ONESHOT) - ka->sa.sa_handler = SIG_DFL; + handle_signal32(signr, &ka, &info, oldset, regs, newsp); - if (!(ka->sa.sa_flags & SA_NODEFER)) { + if (!(ka.sa.sa_flags & SA_NODEFER)) { spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigorsets(¤t->blocked, ¤t->blocked, + &ka.sa.sa_mask); sigaddset(¤t->blocked, signr); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); --- linux-2.6.9-rc1/arch/ppc64/kernel/signal.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/arch/ppc64/kernel/signal.c 2004-09-02 20:51:51.000000000 -0700 @@ -459,17 +459,13 @@ static void handle_signal(unsigned long /* Set up Signal Frame */ setup_rt_frame(sig, ka, info, oldset, regs); - if (ka->sa.sa_flags & SA_ONESHOT) - ka->sa.sa_handler = SIG_DFL; - if (!(ka->sa.sa_flags & SA_NODEFER)) { spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigorsets(¤t->blocked, ¤t->blocked, &ka->sa.sa_mask); sigaddset(¤t->blocked,sig); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); } - return; } static inline void syscall_restart(struct pt_regs *regs, struct k_sigaction *ka) @@ -512,6 +508,7 @@ int do_signal(sigset_t *oldset, struct p { siginfo_t info; int signr; + struct k_sigaction ka; /* * If the current thread is 32 bit - invoke the @@ -523,14 +520,12 @@ int do_signal(sigset_t *oldset, struct p if (!oldset) oldset = ¤t->blocked; - signr = get_signal_to_deliver(&info, regs, NULL); + signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (signr > 0) { - struct k_sigaction *ka = ¤t->sighand->action[signr-1]; - /* Whee! Actually deliver the signal. */ if (TRAP(regs) == 0x0C00) - syscall_restart(regs, ka); - handle_signal(signr, ka, &info, oldset, regs); + syscall_restart(regs, &ka); + handle_signal(signr, &ka, &info, oldset, regs); return 1; } --- linux-2.6.9-rc1/arch/ppc64/kernel/smp.c 2004-08-24 00:03:23.000000000 -0700 +++ 25/arch/ppc64/kernel/smp.c 2004-09-03 00:56:52.901118456 -0700 @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include @@ -59,6 +58,7 @@ unsigned long cache_decay_ticks; cpumask_t cpu_possible_map = CPU_MASK_NONE; cpumask_t cpu_online_map = CPU_MASK_NONE; +cpumask_t cpu_sibling_map[NR_CPUS] = { [0 ... NR_CPUS-1] = CPU_MASK_NONE }; EXPORT_SYMBOL(cpu_online_map); EXPORT_SYMBOL(cpu_possible_map); @@ -253,11 +253,13 @@ int __cpu_disable(void) { /* FIXME: go put this in a header somewhere */ extern void xics_migrate_irqs_away(void); + int cpu = smp_processor_id(); + cpu_clear(cpu, cpu_online_map); systemcfg->processorCount--; /*fix boot_cpuid here*/ - if (smp_processor_id() == boot_cpuid) + if (cpu == boot_cpuid) boot_cpuid = any_online_cpu(cpu_online_map); /* FIXME: abstract this to not be platform specific later on */ @@ -400,56 +402,11 @@ static inline int __devinit smp_startup_ } return 1; } - -static inline void look_for_more_cpus(void) -{ - int num_addr_cell, num_size_cell, len, i, maxcpus; - struct device_node *np; - unsigned int *ireg; - - /* Find the property which will tell us about how many CPUs - * we're allowed to have. */ - if ((np = find_path_device("/rtas")) == NULL) { - printk(KERN_ERR "Could not find /rtas in device tree!"); - return; - } - num_addr_cell = prom_n_addr_cells(np); - num_size_cell = prom_n_size_cells(np); - - ireg = (unsigned int *)get_property(np, "ibm,lrdr-capacity", &len); - if (ireg == NULL) { - /* FIXME: make sure not marked as lrdr_capable() */ - return; - } - - maxcpus = ireg[num_addr_cell + num_size_cell]; - - /* Double maxcpus for processors which have SMT capability */ - if (cur_cpu_spec->cpu_features & CPU_FTR_SMT) - maxcpus *= 2; - - - if (maxcpus > NR_CPUS) { - printk(KERN_WARNING - "Partition configured for %d cpus, " - "operating system maximum is %d.\n", maxcpus, NR_CPUS); - maxcpus = NR_CPUS; - } else - printk(KERN_INFO "Partition configured for %d cpus.\n", - maxcpus); - - /* Make those cpus (which might appear later) possible too. */ - for (i = 0; i < maxcpus; i++) - cpu_set(i, cpu_possible_map); -} #else /* ... CONFIG_HOTPLUG_CPU */ static inline int __devinit smp_startup_cpu(unsigned int lcpu) { return 1; } -static inline void look_for_more_cpus(void) -{ -} #endif /* CONFIG_HOTPLUG_CPU */ static void smp_pSeries_kick_cpu(int nr) @@ -591,10 +548,7 @@ void __init smp_init_pSeries(void) void smp_local_timer_interrupt(struct pt_regs * regs) { - if (!--(get_paca()->prof_counter)) { - update_process_times(user_mode(regs)); - (get_paca()->prof_counter)=get_paca()->prof_multiplier; - } + update_process_times(user_mode(regs)); } void smp_message_recv(int msg, struct pt_regs *regs) @@ -800,21 +754,12 @@ static void __devinit smp_store_cpu_info static void __init smp_create_idle(unsigned int cpu) { - struct pt_regs regs; struct task_struct *p; /* create a process for the processor */ - /* only regs.msr is actually used, and 0 is OK for it */ - memset(®s, 0, sizeof(struct pt_regs)); - p = copy_process(CLONE_VM | CLONE_IDLETASK, - 0, ®s, 0, NULL, NULL); + p = fork_idle(cpu); if (IS_ERR(p)) panic("failed fork for CPU %u: %li", cpu, PTR_ERR(p)); - - wake_up_forked_process(p); - init_idle(p, cpu); - unhash_process(p); - paca[cpu].__current = p; current_set[cpu] = p->thread_info; } @@ -832,8 +777,6 @@ void __init smp_prepare_cpus(unsigned in /* Fixup boot cpu */ smp_store_cpu_info(boot_cpuid); cpu_callin_map[boot_cpuid] = 1; - paca[boot_cpuid].prof_counter = 1; - paca[boot_cpuid].prof_multiplier = 1; #ifndef CONFIG_PPC_ISERIES paca[boot_cpuid].next_jiffy_update_tb = tb_last_stamp = get_tb(); @@ -845,8 +788,6 @@ void __init smp_prepare_cpus(unsigned in */ do_gtod.tb_orig_stamp = tb_last_stamp; systemcfg->tb_orig_stamp = tb_last_stamp; - - look_for_more_cpus(); #endif max_cpus = smp_ops->probe(); @@ -865,7 +806,6 @@ void __devinit smp_prepare_boot_cpu(void { BUG_ON(smp_processor_id() != boot_cpuid); - /* cpu_possible is set up in prom.c */ cpu_set(boot_cpuid, cpu_online_map); paca[boot_cpuid].__current = current; @@ -880,8 +820,6 @@ int __devinit __cpu_up(unsigned int cpu) if (system_state == SYSTEM_BOOTING && !cpu_present(cpu)) return -ENOENT; - paca[cpu].prof_counter = 1; - paca[cpu].prof_multiplier = 1; paca[cpu].default_decr = tb_ticks_per_jiffy / decr_overclock; if (!(cur_cpu_spec->cpu_features & CPU_FTR_SLB)) { @@ -1007,218 +945,3 @@ void __init smp_cpus_done(unsigned int m set_cpus_allowed(current, old_mask); } - -#ifdef CONFIG_SCHED_SMT -#ifdef CONFIG_NUMA -static struct sched_group sched_group_cpus[NR_CPUS]; -static struct sched_group sched_group_phys[NR_CPUS]; -static struct sched_group sched_group_nodes[MAX_NUMNODES]; -static DEFINE_PER_CPU(struct sched_domain, cpu_domains); -static DEFINE_PER_CPU(struct sched_domain, phys_domains); -static DEFINE_PER_CPU(struct sched_domain, node_domains); -__init void arch_init_sched_domains(void) -{ - int i; - struct sched_group *first = NULL, *last = NULL; - - /* Set up domains */ - for_each_cpu(i) { - struct sched_domain *cpu_domain = &per_cpu(cpu_domains, i); - struct sched_domain *phys_domain = &per_cpu(phys_domains, i); - struct sched_domain *node_domain = &per_cpu(node_domains, i); - int node = cpu_to_node(i); - cpumask_t nodemask = node_to_cpumask(node); - cpumask_t my_cpumask = cpumask_of_cpu(i); - cpumask_t sibling_cpumask = cpumask_of_cpu(i ^ 0x1); - - *cpu_domain = SD_SIBLING_INIT; - if (cur_cpu_spec->cpu_features & CPU_FTR_SMT) - cpus_or(cpu_domain->span, my_cpumask, sibling_cpumask); - else - cpu_domain->span = my_cpumask; - cpu_domain->parent = phys_domain; - cpu_domain->groups = &sched_group_cpus[i]; - - *phys_domain = SD_CPU_INIT; - phys_domain->span = nodemask; - phys_domain->parent = node_domain; - phys_domain->groups = &sched_group_phys[first_cpu(cpu_domain->span)]; - - *node_domain = SD_NODE_INIT; - node_domain->span = cpu_possible_map; - node_domain->groups = &sched_group_nodes[node]; - } - - /* Set up CPU (sibling) groups */ - for_each_cpu(i) { - struct sched_domain *cpu_domain = &per_cpu(cpu_domains, i); - int j; - first = last = NULL; - - if (i != first_cpu(cpu_domain->span)) - continue; - - for_each_cpu_mask(j, cpu_domain->span) { - struct sched_group *cpu = &sched_group_cpus[j]; - - cpus_clear(cpu->cpumask); - cpu_set(j, cpu->cpumask); - cpu->cpu_power = SCHED_LOAD_SCALE; - - if (!first) - first = cpu; - if (last) - last->next = cpu; - last = cpu; - } - last->next = first; - } - - for (i = 0; i < MAX_NUMNODES; i++) { - int j; - cpumask_t nodemask; - struct sched_group *node = &sched_group_nodes[i]; - cpumask_t node_cpumask = node_to_cpumask(i); - cpus_and(nodemask, node_cpumask, cpu_possible_map); - - if (cpus_empty(nodemask)) - continue; - - first = last = NULL; - /* Set up physical groups */ - for_each_cpu_mask(j, nodemask) { - struct sched_domain *cpu_domain = &per_cpu(cpu_domains, j); - struct sched_group *cpu = &sched_group_phys[j]; - - if (j != first_cpu(cpu_domain->span)) - continue; - - cpu->cpumask = cpu_domain->span; - /* - * Make each extra sibling increase power by 10% of - * the basic CPU. This is very arbitrary. - */ - cpu->cpu_power = SCHED_LOAD_SCALE + SCHED_LOAD_SCALE*(cpus_weight(cpu->cpumask)-1) / 10; - node->cpu_power += cpu->cpu_power; - - if (!first) - first = cpu; - if (last) - last->next = cpu; - last = cpu; - } - last->next = first; - } - - /* Set up nodes */ - first = last = NULL; - for (i = 0; i < MAX_NUMNODES; i++) { - struct sched_group *cpu = &sched_group_nodes[i]; - cpumask_t nodemask; - cpumask_t node_cpumask = node_to_cpumask(i); - cpus_and(nodemask, node_cpumask, cpu_possible_map); - - if (cpus_empty(nodemask)) - continue; - - cpu->cpumask = nodemask; - /* ->cpu_power already setup */ - - if (!first) - first = cpu; - if (last) - last->next = cpu; - last = cpu; - } - last->next = first; - - mb(); - for_each_cpu(i) { - struct sched_domain *cpu_domain = &per_cpu(cpu_domains, i); - cpu_attach_domain(cpu_domain, i); - } -} -#else /* !CONFIG_NUMA */ -static struct sched_group sched_group_cpus[NR_CPUS]; -static struct sched_group sched_group_phys[NR_CPUS]; -static DEFINE_PER_CPU(struct sched_domain, cpu_domains); -static DEFINE_PER_CPU(struct sched_domain, phys_domains); -__init void arch_init_sched_domains(void) -{ - int i; - struct sched_group *first = NULL, *last = NULL; - - /* Set up domains */ - for_each_cpu(i) { - struct sched_domain *cpu_domain = &per_cpu(cpu_domains, i); - struct sched_domain *phys_domain = &per_cpu(phys_domains, i); - cpumask_t my_cpumask = cpumask_of_cpu(i); - cpumask_t sibling_cpumask = cpumask_of_cpu(i ^ 0x1); - - *cpu_domain = SD_SIBLING_INIT; - if (cur_cpu_spec->cpu_features & CPU_FTR_SMT) - cpus_or(cpu_domain->span, my_cpumask, sibling_cpumask); - else - cpu_domain->span = my_cpumask; - cpu_domain->parent = phys_domain; - cpu_domain->groups = &sched_group_cpus[i]; - - *phys_domain = SD_CPU_INIT; - phys_domain->span = cpu_possible_map; - phys_domain->groups = &sched_group_phys[first_cpu(cpu_domain->span)]; - } - - /* Set up CPU (sibling) groups */ - for_each_cpu(i) { - struct sched_domain *cpu_domain = &per_cpu(cpu_domains, i); - int j; - first = last = NULL; - - if (i != first_cpu(cpu_domain->span)) - continue; - - for_each_cpu_mask(j, cpu_domain->span) { - struct sched_group *cpu = &sched_group_cpus[j]; - - cpus_clear(cpu->cpumask); - cpu_set(j, cpu->cpumask); - cpu->cpu_power = SCHED_LOAD_SCALE; - - if (!first) - first = cpu; - if (last) - last->next = cpu; - last = cpu; - } - last->next = first; - } - - first = last = NULL; - /* Set up physical groups */ - for_each_cpu(i) { - struct sched_domain *cpu_domain = &per_cpu(cpu_domains, i); - struct sched_group *cpu = &sched_group_phys[i]; - - if (i != first_cpu(cpu_domain->span)) - continue; - - cpu->cpumask = cpu_domain->span; - /* See SMT+NUMA setup for comment */ - cpu->cpu_power = SCHED_LOAD_SCALE + SCHED_LOAD_SCALE*(cpus_weight(cpu->cpumask)-1) / 10; - - if (!first) - first = cpu; - if (last) - last->next = cpu; - last = cpu; - } - last->next = first; - - mb(); - for_each_cpu(i) { - struct sched_domain *cpu_domain = &per_cpu(cpu_domains, i); - cpu_attach_domain(cpu_domain, i); - } -} -#endif /* CONFIG_NUMA */ -#endif /* CONFIG_SCHED_SMT */ --- linux-2.6.9-rc1/arch/ppc64/kernel/sysfs.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/ppc64/kernel/sysfs.c 2004-09-03 00:56:51.387348584 -0700 @@ -6,6 +6,8 @@ #include #include #include +#include + #include #include #include --- linux-2.6.9-rc1/arch/ppc64/kernel/time.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/ppc64/kernel/time.c 2004-09-02 20:51:51.000000000 -0700 @@ -108,46 +108,6 @@ void ppc_adjtimex(void); static unsigned adjusting_time = 0; -/* - * The profiling function is SMP safe. (nothing can mess - * around with "current", and the profiling counters are - * updated with atomic operations). This is especially - * useful with a profiling multiplier != 1 - */ -static inline void ppc64_do_profile(struct pt_regs *regs) -{ - unsigned long nip; - extern unsigned long prof_cpu_mask; - - profile_hook(regs); - - if (user_mode(regs)) - return; - - if (!prof_buffer) - return; - - nip = instruction_pointer(regs); - - /* - * Only measure the CPUs specified by /proc/irq/prof_cpu_mask. - * (default is all CPUs.) - */ - if (!((1<>= prof_shift; - /* - * Don't ignore out-of-bounds EIP values silently, - * put them into the last histogram slot, so if - * present, they will show up as a sharp peak. - */ - if (nip > prof_len-1) - nip = prof_len-1; - atomic_inc((atomic_t *)&prof_buffer[nip]); -} - static __inline__ void timer_check_rtc(void) { /* @@ -278,7 +238,7 @@ int timer_interrupt(struct pt_regs * reg irq_enter(); #ifndef CONFIG_PPC_ISERIES - ppc64_do_profile(regs); + profile_tick(CPU_PROFILING, regs); #endif lpaca->lppaca.xIntDword.xFields.xDecrInt = 0; --- linux-2.6.9-rc1/arch/ppc64/kernel/viopath.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/ppc64/kernel/viopath.c 2004-09-03 00:56:40.991928928 -0700 @@ -38,8 +38,8 @@ #include #include #include +#include -#include #include #include #include --- linux-2.6.9-rc1/arch/ppc64/kernel/vmlinux.lds.S 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/ppc64/kernel/vmlinux.lds.S 2004-09-02 20:51:51.000000000 -0700 @@ -61,12 +61,6 @@ SECTIONS __setup_end = .; } - __param : { - __start___param = .; - *(__param) - __stop___param = .; - } - .initcall.init : { __initcall_start = .; *(.initcall1.init) --- linux-2.6.9-rc1/arch/ppc64/mm/hash_low.S 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/ppc64/mm/hash_low.S 2004-09-02 20:51:51.000000000 -0700 @@ -278,6 +278,10 @@ htab_wrong_access: b bail htab_pte_insert_failure: - b .htab_insert_failure + /* Bail out restoring old PTE */ + ld r6,STK_PARM(r6)(r1) + std r31,0(r6) + li r3,-1 + b bail --- linux-2.6.9-rc1/arch/ppc64/mm/hash_utils.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/ppc64/mm/hash_utils.c 2004-09-02 20:51:51.000000000 -0700 @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -236,14 +237,11 @@ unsigned int hash_page_do_lazy_icache(un return pp; } -/* - * Called by asm hashtable.S in case of critical insert failure +/* Result code is: + * 0 - handled + * 1 - normal page fault + * -1 - critical hash insertion error */ -void htab_insert_failure(void) -{ - panic("hash_page: pte_insert failed\n"); -} - int hash_page(unsigned long ea, unsigned long access, unsigned long trap) { void *pgdir; @@ -371,6 +369,25 @@ static inline void make_bl(unsigned int (unsigned long)insn_addr); } +/* + * low_hash_fault is called when we the low level hash code failed + * to instert a PTE due to an hypervisor error + */ +void low_hash_fault(struct pt_regs *regs, unsigned long address) +{ + if (user_mode(regs)) { + siginfo_t info; + + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRERR; + info.si_addr = (void *)address; + force_sig_info(SIGBUS, &info, current); + return; + } + bad_page_fault(regs, address, SIGBUS); +} + void __init htab_finish_init(void) { extern unsigned int *htab_call_hpte_insert1; --- linux-2.6.9-rc1/arch/ppc64/mm/hugetlbpage.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/ppc64/mm/hugetlbpage.c 2004-09-03 00:56:38.764267584 -0700 @@ -527,6 +527,108 @@ full_search: return -ENOMEM; } +/* + * This mmap-allocator allocates new areas top-down from below the + * stack's low limit (the base): + * + * Because we have an exclusive hugepage region which lies within the + * normal user address space, we have to take special measures to make + * non-huge mmap()s evade the hugepage reserved regions. + */ +unsigned long +arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0, + const unsigned long len, const unsigned long pgoff, + const unsigned long flags) +{ + struct vm_area_struct *vma, *prev_vma; + struct mm_struct *mm = current->mm; + unsigned long base = mm->mmap_base, addr = addr0; + int first_time = 1; + + /* requested length too big for entire address space */ + if (len > TASK_SIZE) + return -ENOMEM; + + /* dont allow allocations above current base */ + if (mm->free_area_cache > base) + mm->free_area_cache = base; + + /* requesting a specific address */ + if (addr) { + addr = PAGE_ALIGN(addr); + vma = find_vma(mm, addr); + if (TASK_SIZE - len >= addr && + (!vma || addr + len <= vma->vm_start) + && !is_hugepage_only_range(addr,len)) + return addr; + } + +try_again: + /* make sure it can fit in the remaining address space */ + if (mm->free_area_cache < len) + goto fail; + + /* either no address requested or cant fit in requested address hole */ + addr = (mm->free_area_cache - len) & PAGE_MASK; + do { +hugepage_recheck: + if (touches_hugepage_low_range(addr, len)) { + addr = (addr & ((~0) << SID_SHIFT)) - len; + goto hugepage_recheck; + } else if (touches_hugepage_high_range(addr, len)) { + addr = TASK_HPAGE_BASE - len; + } + + /* + * Lookup failure means no vma is above this address, + * i.e. return with success: + */ + if (!(vma = find_vma_prev(mm, addr, &prev_vma))) + return addr; + + /* + * new region fits between prev_vma->vm_end and + * vma->vm_start, use it: + */ + if (addr+len <= vma->vm_start && + (!prev_vma || (addr >= prev_vma->vm_end))) + /* remember the address as a hint for next time */ + return (mm->free_area_cache = addr); + else + /* pull free_area_cache down to the first hole */ + if (mm->free_area_cache == vma->vm_end) + mm->free_area_cache = vma->vm_start; + + /* try just below the current vma->vm_start */ + addr = vma->vm_start-len; + } while (len <= vma->vm_start); + +fail: + /* + * if hint left us with no space for the requested + * mapping then try again: + */ + if (first_time) { + mm->free_area_cache = base; + first_time = 0; + goto try_again; + } + /* + * A failed mmap() very likely causes application failure, + * so fall back to the bottom-up function here. This scenario + * can happen with large stack limits and large mmap() + * allocations. + */ + mm->free_area_cache = TASK_UNMAPPED_BASE; + addr = arch_get_unmapped_area(filp, addr0, len, pgoff, flags); + /* + * Restore the topdown base: + */ + mm->free_area_cache = base; + + return addr; +} + static unsigned long htlb_get_low_area(unsigned long len, u16 segmask) { unsigned long addr = 0; --- linux-2.6.9-rc1/arch/ppc64/mm/init.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/ppc64/mm/init.c 2004-09-03 00:56:40.094065424 -0700 @@ -85,7 +85,6 @@ unsigned long __max_memory; /* info on what we think the IO hole is */ unsigned long io_hole_start; unsigned long io_hole_size; -unsigned long top_of_ram; void show_mem(void) { @@ -498,16 +497,12 @@ void __init mm_init_ppc64(void) * So we need some rough way to tell where your big IO hole * is. On pmac, it's between 2G and 4G, on POWER3, it's around * that area as well, on POWER4 we don't have one, etc... - * We need that to implement something approx. decent for - * page_is_ram() so that /dev/mem doesn't map cacheable IO space - * when XFree resquest some IO regions witout using O_SYNC, we - * also need that as a "hint" when sizing the TCE table on POWER3 + * We need that as a "hint" when sizing the TCE table on POWER3 * So far, the simplest way that seem work well enough for us it * to just assume that the first discontinuity in our physical * RAM layout is the IO hole. That may not be correct in the future * (and isn't on iSeries but then we don't care ;) */ - top_of_ram = lmb_end_of_DRAM(); #ifndef CONFIG_PPC_ISERIES for (i = 1; i < lmb.memory.cnt; i++) { @@ -530,22 +525,32 @@ void __init mm_init_ppc64(void) ppc64_boot_msg(0x100, "MM Init Done"); } - /* * This is called by /dev/mem to know if a given address has to * be mapped non-cacheable or not */ -int page_is_ram(unsigned long physaddr) +int page_is_ram(unsigned long pfn) { -#ifdef CONFIG_PPC_ISERIES - return 1; + int i; + unsigned long paddr = (pfn << PAGE_SHIFT); + + for (i=0; i < lmb.memory.cnt; i++) { + unsigned long base; + +#ifdef CONFIG_MSCHUNKS + base = lmb.memory.region[i].physbase; +#else + base = lmb.memory.region[i].base; #endif - if (physaddr >= top_of_ram) - return 0; - return io_hole_start == 0 || physaddr < io_hole_start || - physaddr >= (io_hole_start + io_hole_size); -} + if ((paddr >= base) && + (paddr < (base + lmb.memory.region[i].size))) { + return 1; + } + } + return 0; +} +EXPORT_SYMBOL(page_is_ram); /* * Initialize the bootmem system and give it all the memory we @@ -599,6 +604,7 @@ void __init paging_init(void) unsigned long zones_size[MAX_NR_ZONES]; unsigned long zholes_size[MAX_NR_ZONES]; unsigned long total_ram = lmb_phys_mem_size(); + unsigned long top_of_ram = lmb_end_of_DRAM(); printk(KERN_INFO "Top of RAM: 0x%lx, Total RAM: 0x%lx\n", top_of_ram, total_ram); @@ -613,7 +619,7 @@ void __init paging_init(void) zones_size[ZONE_DMA] = top_of_ram >> PAGE_SHIFT; zholes_size[ZONE_DMA] = (top_of_ram - total_ram) >> PAGE_SHIFT; - free_area_init_node(0, &contig_page_data, NULL, zones_size, + free_area_init_node(0, &contig_page_data, zones_size, __pa(PAGE_OFFSET) >> PAGE_SHIFT, zholes_size); mem_map = contig_page_data.node_mem_map; } @@ -663,7 +669,7 @@ void __init mem_init(void) int nid; for (nid = 0; nid < numnodes; nid++) { - if (node_data[nid].node_spanned_pages != 0) { + if (NODE_DATA(nid)->node_spanned_pages != 0) { printk("freeing bootmem node %x\n", nid); totalram_pages += free_all_bootmem_node(NODE_DATA(nid)); --- linux-2.6.9-rc1/arch/ppc64/mm/Makefile 2004-08-24 00:01:51.000000000 -0700 +++ 25/arch/ppc64/mm/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -5,6 +5,6 @@ EXTRA_CFLAGS += -mno-minimal-toc obj-y := fault.o init.o imalloc.o hash_utils.o hash_low.o tlb.o \ - slb_low.o slb.o stab.o + slb_low.o slb.o stab.o mmap.o obj-$(CONFIG_DISCONTIGMEM) += numa.o obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/ppc64/mm/mmap.c 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,86 @@ +/* + * linux/arch/ppc64/mm/mmap.c + * + * flexible mmap layout support + * + * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Started by Ingo Molnar + */ + +#include +#include + +/* + * Top of mmap area (just below the process stack). + * + * Leave an at least ~128 MB hole. + */ +#define MIN_GAP (128*1024*1024) +#define MAX_GAP (TASK_SIZE/6*5) + +static inline unsigned long mmap_base(void) +{ + unsigned long gap = current->rlim[RLIMIT_STACK].rlim_cur; + + if (gap < MIN_GAP) + gap = MIN_GAP; + else if (gap > MAX_GAP) + gap = MAX_GAP; + + return TASK_SIZE - (gap & PAGE_MASK); +} + +static inline int mmap_is_legacy(void) +{ + /* + * Force standard allocation for 64 bit programs. + */ + if (!test_thread_flag(TIF_32BIT)) + return 1; + + if (current->personality & ADDR_COMPAT_LAYOUT) + return 1; + + if (current->rlim[RLIMIT_STACK].rlim_cur == RLIM_INFINITY) + return 1; + + return sysctl_legacy_va_layout; +} + +/* + * This function, called very early during the creation of a new + * process VM image, sets up which VM layout function to use: + */ +void arch_pick_mmap_layout(struct mm_struct *mm) +{ + /* + * Fall back to the standard layout if the personality + * bit is set, or if the expected stack growth is unlimited: + */ + if (mmap_is_legacy()) { + mm->mmap_base = TASK_UNMAPPED_BASE; + mm->get_unmapped_area = arch_get_unmapped_area; + mm->unmap_area = arch_unmap_area; + } else { + mm->mmap_base = mmap_base(); + mm->get_unmapped_area = arch_get_unmapped_area_topdown; + mm->unmap_area = arch_unmap_area_topdown; + } +} --- linux-2.6.9-rc1/arch/ppc64/mm/numa.c 2004-08-24 00:03:17.000000000 -0700 +++ 25/arch/ppc64/mm/numa.c 2004-09-03 00:56:51.388348432 -0700 @@ -14,15 +14,13 @@ #include #include #include +#include #include #include #include -#if 1 -#define dbg(args...) printk(KERN_INFO args) -#else -#define dbg(args...) -#endif +static int numa_debug; +#define dbg(args...) if (numa_debug) { printk(KERN_INFO args); } #ifdef DEBUG_NUMA #define ARRAY_INITIALISER -1 @@ -36,10 +34,19 @@ char *numa_memory_lookup_table; cpumask_t numa_cpumask_lookup_table[MAX_NUMNODES]; int nr_cpus_in_node[MAX_NUMNODES] = { [0 ... (MAX_NUMNODES -1)] = 0}; -struct pglist_data node_data[MAX_NUMNODES]; -bootmem_data_t plat_node_bdata[MAX_NUMNODES]; +struct pglist_data *node_data[MAX_NUMNODES]; +bootmem_data_t __initdata plat_node_bdata[MAX_NUMNODES]; static unsigned long node0_io_hole_size; +/* + * We need somewhere to store start/span for each node until we have + * allocated the real node_data structures. + */ +static struct { + unsigned long node_start_pfn; + unsigned long node_spanned_pages; +} init_node_data[MAX_NUMNODES] __initdata; + EXPORT_SYMBOL(node_data); EXPORT_SYMBOL(numa_cpu_lookup_table); EXPORT_SYMBOL(numa_memory_lookup_table); @@ -48,7 +55,6 @@ EXPORT_SYMBOL(nr_cpus_in_node); static inline void map_cpu_to_node(int cpu, int node) { - dbg("cpu %d maps to domain %d\n", cpu, node); numa_cpu_lookup_table[cpu] = node; if (!(cpu_isset(cpu, numa_cpumask_lookup_table[node]))) { cpu_set(cpu, numa_cpumask_lookup_table[node]); @@ -107,8 +113,8 @@ static int of_node_numa_domain(struct de if (tmp && (tmp[0] >= depth)) { numa_domain = tmp[depth]; } else { - printk(KERN_ERR "WARNING: no NUMA information for " - "%s\n", device->full_name); + dbg("WARNING: no NUMA information for %s\n", + device->full_name); numa_domain = 0; } return numa_domain; @@ -137,11 +143,8 @@ static int find_min_common_depth(void) rtas_root = of_find_node_by_path("/rtas"); - if (!rtas_root) { - printk(KERN_ERR "WARNING: %s() could not find rtas root\n", - __FUNCTION__); + if (!rtas_root) return -1; - } /* * this property is 2 32-bit integers, each representing a level of @@ -155,8 +158,8 @@ static int find_min_common_depth(void) if ((len >= 1) && ref_points) { depth = ref_points[1]; } else { - printk(KERN_ERR "WARNING: could not find NUMA " - "associativity reference point\n"); + dbg("WARNING: could not find NUMA " + "associativity reference point\n"); depth = -1; } of_node_put(rtas_root); @@ -187,6 +190,9 @@ static int __init parse_numa_properties( long entries = lmb_end_of_DRAM() >> MEMORY_INCREMENT_SHIFT; unsigned long i; + if (strstr(saved_command_line, "numa=debug")) + numa_debug = 1; + if (strstr(saved_command_line, "numa=off")) { printk(KERN_WARNING "NUMA disabled by user\n"); return -1; @@ -194,13 +200,14 @@ static int __init parse_numa_properties( numa_memory_lookup_table = (char *)abs_to_virt(lmb_alloc(entries * sizeof(char), 1)); + memset(numa_memory_lookup_table, 0, entries * sizeof(char)); for (i = 0; i < entries ; i++) numa_memory_lookup_table[i] = ARRAY_INITIALISER; depth = find_min_common_depth(); - printk(KERN_INFO "NUMA associativity depth for CPU/Memory: %d\n", depth); + dbg("NUMA associativity depth for CPU/Memory: %d\n", depth); if (depth < 0) return depth; @@ -225,8 +232,7 @@ static int __init parse_numa_properties( numa_domain = 0; } } else { - printk(KERN_ERR "WARNING: no NUMA information for " - "cpu %ld\n", i); + dbg("WARNING: no NUMA information for cpu %ld\n", i); numa_domain = 0; } @@ -281,22 +287,22 @@ new_range: * this simple case and complain if there is a gap in * memory */ - if (node_data[numa_domain].node_spanned_pages) { + if (init_node_data[numa_domain].node_spanned_pages) { unsigned long shouldstart = - node_data[numa_domain].node_start_pfn + - node_data[numa_domain].node_spanned_pages; + init_node_data[numa_domain].node_start_pfn + + init_node_data[numa_domain].node_spanned_pages; if (shouldstart != (start / PAGE_SIZE)) { - printk(KERN_ERR "Hole in node, disabling " - "region start %lx length %lx\n", - start, size); + printk(KERN_ERR "WARNING: Hole in node, " + "disabling region start %lx " + "length %lx\n", start, size); continue; } - node_data[numa_domain].node_spanned_pages += + init_node_data[numa_domain].node_spanned_pages += size / PAGE_SIZE; } else { - node_data[numa_domain].node_start_pfn = + init_node_data[numa_domain].node_start_pfn = start / PAGE_SIZE; - node_data[numa_domain].node_spanned_pages = + init_node_data[numa_domain].node_spanned_pages = size / PAGE_SIZE; } @@ -304,9 +310,6 @@ new_range: numa_memory_lookup_table[i >> MEMORY_INCREMENT_SHIFT] = numa_domain; - dbg("memory region %lx to %lx maps to domain %d\n", - start, start+size, numa_domain); - ranges--; if (ranges) goto new_range; @@ -332,6 +335,7 @@ static void __init setup_nonnuma(void) long entries = top_of_ram >> MEMORY_INCREMENT_SHIFT; numa_memory_lookup_table = (char *)abs_to_virt(lmb_alloc(entries * sizeof(char), 1)); + memset(numa_memory_lookup_table, 0, entries * sizeof(char)); for (i = 0; i < entries ; i++) numa_memory_lookup_table[i] = ARRAY_INITIALISER; } @@ -341,8 +345,8 @@ static void __init setup_nonnuma(void) node_set_online(0); - node_data[0].node_start_pfn = 0; - node_data[0].node_spanned_pages = lmb_end_of_DRAM() / PAGE_SIZE; + init_node_data[0].node_start_pfn = 0; + init_node_data[0].node_spanned_pages = lmb_end_of_DRAM() / PAGE_SIZE; for (i = 0 ; i < top_of_ram; i += MEMORY_INCREMENT) numa_memory_lookup_table[i >> MEMORY_INCREMENT_SHIFT] = 0; @@ -350,6 +354,108 @@ static void __init setup_nonnuma(void) node0_io_hole_size = top_of_ram - total_ram; } +static void __init dump_numa_topology(void) +{ + unsigned int node; + unsigned int cpu, count; + + for (node = 0; node < MAX_NUMNODES; node++) { + if (!node_online(node)) + continue; + + printk(KERN_INFO "Node %d CPUs:", node); + + count = 0; + /* + * If we used a CPU iterator here we would miss printing + * the holes in the cpumap. + */ + for (cpu = 0; cpu < NR_CPUS; cpu++) { + if (cpu_isset(cpu, numa_cpumask_lookup_table[node])) { + if (count == 0) + printk(" %u", cpu); + ++count; + } else { + if (count > 1) + printk("-%u", cpu - 1); + count = 0; + } + } + + if (count > 1) + printk("-%u", NR_CPUS - 1); + printk("\n"); + } + + for (node = 0; node < MAX_NUMNODES; node++) { + unsigned long i; + + if (!node_online(node)) + continue; + + printk(KERN_INFO "Node %d Memory:", node); + + count = 0; + + for (i = 0; i < lmb_end_of_DRAM(); i += MEMORY_INCREMENT) { + if (numa_memory_lookup_table[i >> MEMORY_INCREMENT_SHIFT] == node) { + if (count == 0) + printk(" 0x%lx", i); + ++count; + } else { + if (count > 0) + printk("-0x%lx", i); + count = 0; + } + } + + if (count > 0) + printk("-0x%lx", i); + printk("\n"); + } +} + +/* + * Allocate some memory, satisfying the lmb or bootmem allocator where + * required. nid is the preferred node and end is the physical address of + * the highest address in the node. + * + * Returns the physical address of the memory. + */ +static unsigned long careful_allocation(int nid, unsigned long size, + unsigned long align, unsigned long end) +{ + unsigned long ret = lmb_alloc_base(size, align, end); + + /* retry over all memory */ + if (!ret) + ret = lmb_alloc_base(size, align, lmb_end_of_DRAM()); + + if (!ret) + panic("numa.c: cannot allocate %lu bytes on node %d", + size, nid); + + /* + * If the memory came from a previously allocated node, we must + * retry with the bootmem allocator. + */ + if (pa_to_nid(ret) < nid) { + nid = pa_to_nid(ret); + ret = (unsigned long)__alloc_bootmem_node(NODE_DATA(nid), + size, align, 0); + + if (!ret) + panic("numa.c: cannot allocate %lu bytes on node %d", + size, nid); + + ret = virt_to_abs(ret); + + dbg("alloc_bootmem %lx %lx\n", ret, size); + } + + return ret; +} + void __init do_init_bootmem(void) { int nid; @@ -360,6 +466,8 @@ void __init do_init_bootmem(void) if (parse_numa_properties()) setup_nonnuma(); + else + dump_numa_topology(); for (nid = 0; nid < numnodes; nid++) { unsigned long start_paddr, end_paddr; @@ -367,24 +475,38 @@ void __init do_init_bootmem(void) unsigned long bootmem_paddr; unsigned long bootmap_pages; - if (node_data[nid].node_spanned_pages == 0) - continue; + start_paddr = init_node_data[nid].node_start_pfn * PAGE_SIZE; + end_paddr = start_paddr + (init_node_data[nid].node_spanned_pages * PAGE_SIZE); - start_paddr = node_data[nid].node_start_pfn * PAGE_SIZE; - end_paddr = start_paddr + - (node_data[nid].node_spanned_pages * PAGE_SIZE); - - dbg("node %d\n", nid); - dbg("start_paddr = %lx\n", start_paddr); - dbg("end_paddr = %lx\n", end_paddr); + /* Allocate the node structure node local if possible */ + NODE_DATA(nid) = (struct pglist_data *)careful_allocation(nid, + sizeof(struct pglist_data), + SMP_CACHE_BYTES, end_paddr); + NODE_DATA(nid) = abs_to_virt(NODE_DATA(nid)); + memset(NODE_DATA(nid), 0, sizeof(struct pglist_data)); + + dbg("node %d\n", nid); + dbg("NODE_DATA() = %p\n", NODE_DATA(nid)); NODE_DATA(nid)->bdata = &plat_node_bdata[nid]; + NODE_DATA(nid)->node_start_pfn = + init_node_data[nid].node_start_pfn; + NODE_DATA(nid)->node_spanned_pages = + init_node_data[nid].node_spanned_pages; + + if (init_node_data[nid].node_spanned_pages == 0) + continue; + + dbg("start_paddr = %lx\n", start_paddr); + dbg("end_paddr = %lx\n", end_paddr); bootmap_pages = bootmem_bootmap_pages((end_paddr - start_paddr) >> PAGE_SHIFT); - dbg("bootmap_pages = %lx\n", bootmap_pages); - bootmem_paddr = lmb_alloc_base(bootmap_pages << PAGE_SHIFT, + bootmem_paddr = careful_allocation(nid, + bootmap_pages << PAGE_SHIFT, PAGE_SIZE, end_paddr); + memset(abs_to_virt(bootmem_paddr), 0, + bootmap_pages << PAGE_SHIFT); dbg("bootmap_paddr = %lx\n", bootmem_paddr); init_bootmem_node(NODE_DATA(nid), bootmem_paddr >> PAGE_SHIFT, @@ -442,7 +564,6 @@ void __init paging_init(void) { unsigned long zones_size[MAX_NR_ZONES]; unsigned long zholes_size[MAX_NR_ZONES]; - struct page *node_mem_map; int nid; memset(zones_size, 0, sizeof(zones_size)); @@ -463,17 +584,7 @@ void __init paging_init(void) dbg("free_area_init node %d %lx %lx (hole: %lx)\n", nid, zones_size[ZONE_DMA], start_pfn, zholes_size[ZONE_DMA]); - /* - * Give this empty node a dummy struct page to avoid - * us from trying to allocate a node local mem_map - * in free_area_init_node (which will fail). - */ - if (!node_data[nid].node_spanned_pages) - node_mem_map = alloc_bootmem(sizeof(struct page)); - else - node_mem_map = NULL; - - free_area_init_node(nid, NODE_DATA(nid), node_mem_map, - zones_size, start_pfn, zholes_size); + free_area_init_node(nid, NODE_DATA(nid), zones_size, + start_pfn, zholes_size); } } --- linux-2.6.9-rc1/arch/ppc64/mm/slb.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/ppc64/mm/slb.c 2004-09-03 00:56:38.091369880 -0700 @@ -24,30 +24,55 @@ extern void slb_allocate(unsigned long ea); +static inline unsigned long mk_esid_data(unsigned long ea, unsigned long slot) +{ + return (ea & ESID_MASK) | SLB_ESID_V | slot; +} + +static inline unsigned long mk_vsid_data(unsigned long ea, unsigned long flags) +{ + return (get_kernel_vsid(ea) << SLB_VSID_SHIFT) | flags; +} + static inline void create_slbe(unsigned long ea, unsigned long vsid, unsigned long flags, unsigned long entry) { - ea = (ea & ESID_MASK) | SLB_ESID_V | entry; - vsid = (vsid << SLB_VSID_SHIFT) | flags; asm volatile("slbmte %0,%1" : - : "r" (vsid), "r" (ea) + : "r" (mk_vsid_data(ea, flags)), + "r" (mk_esid_data(ea, entry)) : "memory" ); } -static void slb_add_bolted(void) +static void slb_flush_and_rebolt(void) { - WARN_ON(!irqs_disabled()); - /* If you change this make sure you change SLB_NUM_BOLTED - * appropriately too */ + * appropriately too. */ + unsigned long ksp_flags = SLB_VSID_KERNEL; + unsigned long ksp_esid_data; - /* Slot 1 - first VMALLOC segment - * Since modules end up there it gets hit very heavily. - */ - create_slbe(VMALLOCBASE, get_kernel_vsid(VMALLOCBASE), - SLB_VSID_KERNEL, 1); + WARN_ON(!irqs_disabled()); - asm volatile("isync":::"memory"); + if (cur_cpu_spec->cpu_features & CPU_FTR_16M_PAGE) + ksp_flags |= SLB_VSID_L; + + ksp_esid_data = mk_esid_data(get_paca()->kstack, 2); + if ((ksp_esid_data & ESID_MASK) == KERNELBASE) + ksp_esid_data &= ~SLB_ESID_V; + + /* We need to do this all in asm, so we're sure we don't touch + * the stack between the slbia and rebolting it. */ + asm volatile("isync\n" + "slbia\n" + /* Slot 1 - first VMALLOC segment */ + "slbmte %0,%1\n" + /* Slot 2 - kernel stack */ + "slbmte %2,%3\n" + "isync" + :: "r"(mk_vsid_data(VMALLOCBASE, SLB_VSID_KERNEL)), + "r"(mk_esid_data(VMALLOCBASE, 1)), + "r"(mk_vsid_data(ksp_esid_data, ksp_flags)), + "r"(ksp_esid_data) + : "memory"); } /* Flush all user entries from the segment table of the current processor. */ @@ -69,8 +94,7 @@ void switch_slb(struct task_struct *tsk, } asm volatile("isync" : : : "memory"); } else { - asm volatile("isync; slbia; isync" : : : "memory"); - slb_add_bolted(); + slb_flush_and_rebolt(); } /* Workaround POWER5 < DD2.1 issue */ @@ -113,22 +137,27 @@ void switch_slb(struct task_struct *tsk, void slb_initialize(void) { -#ifdef CONFIG_PPC_ISERIES - asm volatile("isync; slbia; isync":::"memory"); -#else + /* On iSeries the bolted entries have already been set up by + * the hypervisor from the lparMap data in head.S */ +#ifndef CONFIG_PPC_ISERIES unsigned long flags = SLB_VSID_KERNEL; - /* Invalidate the entire SLB (even slot 0) & all the ERATS */ - if (cur_cpu_spec->cpu_features & CPU_FTR_16M_PAGE) - flags |= SLB_VSID_L; + /* Invalidate the entire SLB (even slot 0) & all the ERATS */ + if (cur_cpu_spec->cpu_features & CPU_FTR_16M_PAGE) + flags |= SLB_VSID_L; - asm volatile("isync":::"memory"); - asm volatile("slbmte %0,%0"::"r" (0) : "memory"); + asm volatile("isync":::"memory"); + asm volatile("slbmte %0,%0"::"r" (0) : "memory"); asm volatile("isync; slbia; isync":::"memory"); - create_slbe(KERNELBASE, get_kernel_vsid(KERNELBASE), - flags, 0); - + create_slbe(KERNELBASE, get_kernel_vsid(KERNELBASE), flags, 0); + create_slbe(VMALLOCBASE, get_kernel_vsid(KERNELBASE), + SLB_VSID_KERNEL, 1); + /* We don't bolt the stack for the time being - we're in boot, + * so the stack is in the bolted segment. By the time it goes + * elsewhere, we'll call _switch() which will bolt in the new + * one. */ + asm volatile("isync":::"memory"); #endif - slb_add_bolted(); + get_paca()->stab_rr = SLB_NUM_BOLTED; } --- linux-2.6.9-rc1/arch/ppc64/mm/slb_low.S 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/ppc64/mm/slb_low.S 2004-09-03 00:56:38.092369728 -0700 @@ -37,8 +37,21 @@ _GLOBAL(slb_allocate) * a free slot first but that took too long. Unfortunately we * dont have any LRU information to help us choose a slot. */ +#ifdef CONFIG_PPC_ISERIES + /* + * On iSeries, the "bolted" stack segment can be cast out on + * shared processor switch so we need to check for a miss on + * it and restore it to the right slot. + */ + ld r9,PACAKSAVE(r13) + clrrdi r9,r9,28 + clrrdi r11,r3,28 + li r10,SLB_NUM_BOLTED-1 /* Stack goes in last bolted slot */ + cmpld r9,r11 + beq 3f +#endif /* CONFIG_PPC_ISERIES */ + ld r10,PACASTABRR(r13) -3: addi r10,r10,1 /* use a cpu feature mask if we ever change our slb size */ cmpldi r10,SLB_NUM_ENTRIES @@ -46,36 +59,9 @@ _GLOBAL(slb_allocate) blt+ 4f li r10,SLB_NUM_BOLTED - /* - * Never cast out the segment for our kernel stack. Since we - * dont invalidate the ERAT we could have a valid translation - * for the kernel stack during the first part of exception exit - * which gets invalidated due to a tlbie from another cpu at a - * non recoverable point (after setting srr0/1) - Anton - */ -4: slbmfee r11,r10 - srdi r11,r11,27 - /* - * Use paca->ksave as the value of the kernel stack pointer, - * because this is valid at all times. - * The >> 27 (rather than >> 28) is so that the LSB is the - * valid bit - this way we check valid and ESID in one compare. - * In order to completely close the tiny race in the context - * switch (between updating r1 and updating paca->ksave), - * we check against both r1 and paca->ksave. - */ - srdi r9,r1,27 - ori r9,r9,1 /* mangle SP for later compare */ - cmpd r11,r9 - beq- 3b - ld r9,PACAKSAVE(r13) - srdi r9,r9,27 - ori r9,r9,1 - cmpd r11,r9 - beq- 3b - +4: std r10,PACASTABRR(r13) - +3: /* r3 = faulting address, r10 = entry */ srdi r9,r3,60 /* get region */ --- linux-2.6.9-rc1/arch/ppc64/mm/tlb.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/ppc64/mm/tlb.c 2004-09-03 00:56:40.991928928 -0700 @@ -26,10 +26,10 @@ #include #include #include +#include #include #include #include -#include #include DEFINE_PER_CPU(struct ppc64_tlb_batch, ppc64_tlb_batch); --- linux-2.6.9-rc1/arch/ppc/8xx_io/uart.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/arch/ppc/8xx_io/uart.c 2004-09-02 20:51:51.000000000 -0700 @@ -2592,7 +2592,7 @@ static int __init rs_8xx_init(void) state->icount.rx = state->icount.tx = 0; state->icount.frame = state->icount.parity = 0; state->icount.overrun = state->icount.brk = 0; - printk(KERN_INFO "ttyS%02d at 0x%04x is a %s\n", + printk(KERN_INFO "ttyS%d at 0x%04x is a %s\n", i, (unsigned int)(state->port), (state->smc_scc_num & NUM_IS_SCC) ? "SCC" : "SMC"); #ifdef CONFIG_SERIAL_CONSOLE --- linux-2.6.9-rc1/arch/ppc/amiga/config.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/ppc/amiga/config.c 2004-09-02 20:51:51.000000000 -0700 @@ -423,9 +423,6 @@ void __init config_amiga(void) mach_floppy_setup = amiga_floppy_setup; #endif mach_reset = amiga_reset; -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif #ifdef CONFIG_HEARTBEAT mach_heartbeat = amiga_heartbeat; #endif --- linux-2.6.9-rc1/arch/ppc/boot/common/misc-common.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/ppc/boot/common/misc-common.c 2004-09-02 20:51:51.000000000 -0700 @@ -526,6 +526,11 @@ _dump_buf(unsigned char *p, int s) * on others it's an offset from a given location. -- Tom */ +void ISA_init(unsigned long base) +{ + ISA_io = (unsigned char *)base; +} + void outb(int port, unsigned char val) { --- linux-2.6.9-rc1/arch/ppc/boot/common/serial_stub.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/ppc/boot/common/serial_stub.c 2004-09-02 20:51:51.000000000 -0700 @@ -11,11 +11,6 @@ * is" without any warranty of any kind, whether express or implied. */ -void __attribute__ ((weak)) -serial_fixups(void) -{ -} - unsigned long __attribute__ ((weak)) serial_init(int chan, void *ignored) { --- linux-2.6.9-rc1/arch/ppc/boot/include/nonstdio.h 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/ppc/boot/include/nonstdio.h 2004-09-02 20:51:51.000000000 -0700 @@ -30,3 +30,5 @@ extern void puthex(unsigned long val); extern void puts(const char *); extern void udelay(long delay); extern unsigned char inb(int port); +extern void board_isa_init(void); +extern void ISA_init(unsigned long base); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/ppc/boot/simple/chrpmap.c 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,12 @@ +/* + * 2004 (C) IBM. This file is licensed under the terms of the GNU General + * Public License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include + +void board_isa_init(void) +{ + ISA_init(0xFE000000); +} --- linux-2.6.9-rc1/arch/ppc/boot/simple/chrpmap.S 2004-08-24 00:03:30.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,19 +0,0 @@ -/* - * arch/ppc/boot/simple/chrpmap.S - * - * Author: Tom Rini - * - * This will go and setup ISA_io to 0xFE00000 and return. - */ - -#include - - .text - - .globl serial_fixups -serial_fixups: - lis r3,ISA_io@h /* Load ISA_io */ - ori r3,r3,ISA_io@l - lis r4,0xFE00 /* Load the value, 0xFE00000 */ - stw r4,0(r3) /* store */ - blr --- linux-2.6.9-rc1/arch/ppc/boot/simple/legacy.S 2004-08-24 00:02:33.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,19 +0,0 @@ -/* - * arch/ppc/boot/simple/legacy.S - * - * Author: Tom Rini - * - * This will go and setup ISA_io to 0x8000000 and return. - */ - -#include - - .text - - .globl serial_fixups -serial_fixups: - lis r3,ISA_io@h /* Load ISA_io */ - ori r3,r3,ISA_io@l - lis r4,0x8000 /* Load the value, 0x8000000 */ - stw r4,0(r3) /* store */ - blr --- linux-2.6.9-rc1/arch/ppc/boot/simple/Makefile 2004-08-24 00:01:51.000000000 -0700 +++ 25/arch/ppc/boot/simple/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -73,7 +73,7 @@ zimageinitrd-$(CONFIG_OCOTEA) := zImage zimageinitrd-$(CONFIG_GEMINI) := zImage.initrd-STRIPELF end-$(CONFIG_GEMINI) := gemini - extra.o-$(CONFIG_K2) := legacy.o + extra.o-$(CONFIG_K2) := prepmap.o end-$(CONFIG_K2) := k2 cacheflag-$(CONFIG_K2) := -include $(clear_L2_L3) @@ -89,7 +89,7 @@ zimageinitrd-$(motorola) := zImage.init end-$(motorola) := pplus # Overrides previous assingment - extra.o-$(CONFIG_PPLUS) := legacy.o + extra.o-$(CONFIG_PPLUS) := prepmap.o extra.o-$(CONFIG_LOPEC) := mpc10x_memory.o zimage-$(pcore) := zImage-STRIPELF @@ -100,7 +100,7 @@ zimageinitrd-$(pcore) := zImage.initrd zimage-$(CONFIG_PPC_PREP) := zImage-PPLUS zimageinitrd-$(CONFIG_PPC_PREP) := zImage.initrd-PPLUS - extra.o-$(CONFIG_PPC_PREP) := legacy.o + extra.o-$(CONFIG_PPC_PREP) := prepmap.o misc-$(CONFIG_PPC_PREP) += misc-prep.o mpc10x_memory.o end-$(CONFIG_PPC_PREP) := prep --- linux-2.6.9-rc1/arch/ppc/boot/simple/misc.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/ppc/boot/simple/misc.c 2004-09-02 20:51:51.000000000 -0700 @@ -97,7 +97,6 @@ decompress_kernel(unsigned long load_add struct bi_record *rec; unsigned long initrd_loc, TotalMemory = 0; - serial_fixups(); #ifdef CONFIG_SERIAL_8250_CONSOLE com_port = serial_init(0, NULL); #endif @@ -268,10 +267,16 @@ decompress_kernel(unsigned long load_add return rec; } +void __attribute__ ((weak)) +board_isa_init(void) +{ +} + /* Allow decompress_kernel to be hooked into. This is the default. */ void * __attribute__ ((weak)) load_kernel(unsigned long load_addr, int num_words, unsigned long cksum, void *ign1, void *ign2) { + board_isa_init(); return decompress_kernel(load_addr, num_words, cksum); } --- linux-2.6.9-rc1/arch/ppc/boot/simple/misc-embedded.c 2004-08-24 00:02:20.000000000 -0700 +++ 25/arch/ppc/boot/simple/misc-embedded.c 2004-09-02 20:51:51.000000000 -0700 @@ -72,6 +72,14 @@ extern void flush_instruction_cache(void extern void gunzip(void *, int, unsigned char *, int *); extern void embed_config(bd_t **bp); +/* Weak function for boards which don't need to build the + * board info struct because they are using PPCBoot/U-Boot. + */ +void __attribute__ ((weak)) +embed_config(bd_t **bdp) +{ +} + unsigned long load_kernel(unsigned long load_addr, int num_words, unsigned long cksum, bd_t *bp) { --- linux-2.6.9-rc1/arch/ppc/boot/simple/misc-prep.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/ppc/boot/simple/misc-prep.c 2004-09-02 20:51:51.000000000 -0700 @@ -88,6 +88,7 @@ load_kernel(unsigned long load_addr, int ofinit(OFW_interface); } + board_isa_init(); #if defined(CONFIG_VGA_CONSOLE) vga_init((unsigned char *)0xC0000000); #endif /* CONFIG_VGA_CONSOLE */ --- linux-2.6.9-rc1/arch/ppc/boot/simple/misc-spruce.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/arch/ppc/boot/simple/misc-spruce.c 2004-09-02 20:51:51.000000000 -0700 @@ -26,7 +26,6 @@ extern unsigned long decompress_kernel(u /* Define some important locations of the Spruce. */ #define SPRUCE_PCI_CONFIG_ADDR 0xfec00000 #define SPRUCE_PCI_CONFIG_DATA 0xfec00004 -#define SPRUCE_ISA_IO_BASE 0xf8000000 /* PCI configuration space access routines. */ unsigned int *pci_config_address = (unsigned int *)SPRUCE_PCI_CONFIG_ADDR; @@ -86,8 +85,6 @@ void cpc700_pcibios_write_config_dword(u out_le32((unsigned *)pci_config_data, val); } -unsigned long isa_io_base = SPRUCE_ISA_IO_BASE; - #define PCNET32_WIO_RDP 0x10 #define PCNET32_WIO_RAP 0x12 #define PCNET32_WIO_RESET 0x14 --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/ppc/boot/simple/prepmap.c 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,12 @@ +/* + * 2004 (C) IBM. This file is licensed under the terms of the GNU General + * Public License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include + +void board_isa_init(void) +{ + ISA_init(0x80000000); +} --- linux-2.6.9-rc1/arch/ppc/configs/mvme5100_defconfig 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/ppc/configs/mvme5100_defconfig 2004-09-02 20:51:51.000000000 -0700 @@ -4,23 +4,39 @@ CONFIG_MMU=y CONFIG_RWSEM_XCHGADD_ALGORITHM=y CONFIG_HAVE_DEC_LOCK=y +CONFIG_PPC=y +CONFIG_PPC32=y +CONFIG_GENERIC_NVRAM=y # # Code maturity level options # CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y # # General setup # CONFIG_SWAP=y CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_HOTPLUG is not set +# CONFIG_IKCONFIG is not set # CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_EXTRA_PASS is not set CONFIG_FUTEX=y CONFIG_EPOLL=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set # # Loadable module support @@ -33,24 +49,26 @@ CONFIG_OBSOLETE_MODPARM=y CONFIG_KMOD=y # -# Platform support +# Processor # -CONFIG_PPC=y -CONFIG_PPC32=y CONFIG_6xx=y # CONFIG_40x is not set +# CONFIG_44x is not set # CONFIG_POWER3 is not set +# CONFIG_POWER4 is not set # CONFIG_8xx is not set +# CONFIG_E500 is not set +CONFIG_ALTIVEC=y +# CONFIG_TAU is not set +# CONFIG_CPU_FREQ is not set +CONFIG_PPC_STD_MMU=y # -# IBM 4xx options +# Platform options # -# CONFIG_8260 is not set -CONFIG_GENERIC_ISA_DMA=y -CONFIG_PPC_STD_MMU=y # CONFIG_PPC_MULTIPLATFORM is not set # CONFIG_APUS is not set -# CONFIG_WILLOW_2 is not set +# CONFIG_WILLOW is not set # CONFIG_PCORE is not set # CONFIG_POWERPMC250 is not set # CONFIG_EV64260 is not set @@ -66,34 +84,31 @@ CONFIG_MVME5100=y # CONFIG_K2 is not set # CONFIG_PAL4 is not set # CONFIG_GEMINI is not set +# CONFIG_EST8260 is not set +# CONFIG_SBC82xx is not set +# CONFIG_SBS8260 is not set +# CONFIG_RPX8260 is not set +# CONFIG_TQM8260 is not set +# CONFIG_ADS8272 is not set +# CONFIG_LITE5200 is not set # CONFIG_MVME5100_IPMC761_PRESENT is not set # CONFIG_SMP is not set # CONFIG_PREEMPT is not set -CONFIG_ALTIVEC=y -# CONFIG_TAU is not set -# CONFIG_CPU_FREQ is not set +# CONFIG_HIGHMEM is not set +CONFIG_KERNEL_ELF=y +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="ip=on" # -# General setup +# Bus options # -# CONFIG_HIGHMEM is not set +CONFIG_GENERIC_ISA_DMA=y CONFIG_PCI=y CONFIG_PCI_DOMAINS=y -CONFIG_KCORE_ELF=y -CONFIG_BINFMT_ELF=y -CONFIG_KERNEL_ELF=y -# CONFIG_BINFMT_MISC is not set # CONFIG_PCI_LEGACY_PROC is not set # CONFIG_PCI_NAMES is not set -# CONFIG_HOTPLUG is not set - -# -# Parallel port support -# -# CONFIG_PARPORT is not set -# CONFIG_PPC601_SYNC_FIX is not set -CONFIG_CMDLINE_BOOL=y -CONFIG_CMDLINE="ip=on" # # Advanced setup @@ -110,14 +125,28 @@ CONFIG_TASK_SIZE=0x80000000 CONFIG_BOOT_LOAD=0x00800000 # +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y + +# # Memory Technology Devices (MTD) # # CONFIG_MTD is not set # +# Parallel port support +# +# CONFIG_PARPORT is not set + +# # Plug and Play support # -# CONFIG_PNP is not set # # Block devices @@ -129,46 +158,46 @@ CONFIG_BOOT_LOAD=0x00800000 # CONFIG_BLK_DEV_UMEM is not set # CONFIG_BLK_DEV_LOOP is not set # CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_SX8 is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y +# CONFIG_LBD is not set # -# Multi-device support (RAID and LVM) -# -# CONFIG_MD is not set - -# -# ATA/IDE/MFM/RLL support +# ATA/ATAPI/MFM/RLL support # CONFIG_IDE=y - -# -# IDE, ATA and ATAPI Block devices -# CONFIG_BLK_DEV_IDE=y # # Please see Documentation/ide.txt for help/info on IDE drives # -# CONFIG_BLK_DEV_HD is not set +# CONFIG_BLK_DEV_IDE_SATA is not set CONFIG_BLK_DEV_IDEDISK=y # CONFIG_IDEDISK_MULTI_MODE is not set -# CONFIG_IDEDISK_STROKE is not set # CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDETAPE is not set # CONFIG_BLK_DEV_IDEFLOPPY is not set # CONFIG_BLK_DEV_IDESCSI is not set # CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set # # IDE chipset support/bugfixes # +CONFIG_IDE_GENERIC=y # CONFIG_BLK_DEV_IDEPCI is not set +# CONFIG_IDE_ARM is not set +# CONFIG_BLK_DEV_IDEDMA is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_BLK_DEV_HD is not set # -# SCSI support +# SCSI device support # CONFIG_SCSI=y +CONFIG_SCSI_PROC_FS=y # # SCSI support type (disk, tape, CD-ROM) @@ -184,64 +213,69 @@ CONFIG_BLK_DEV_SR=y # Some SCSI devices (e.g. CD jukebox) support multiple LUNs # # CONFIG_SCSI_MULTI_LUN is not set -# CONFIG_SCSI_REPORT_LUNS is not set # CONFIG_SCSI_CONSTANTS is not set # CONFIG_SCSI_LOGGING is not set # +# SCSI Transport Attributes +# +CONFIG_SCSI_SPI_ATTRS=y +# CONFIG_SCSI_FC_ATTRS is not set + +# # SCSI low-level drivers # # CONFIG_BLK_DEV_3W_XXXX_RAID is not set +# CONFIG_SCSI_3W_9XXX is not set # CONFIG_SCSI_ACARD is not set # CONFIG_SCSI_AACRAID is not set # CONFIG_SCSI_AIC7XXX is not set # CONFIG_SCSI_AIC7XXX_OLD is not set # CONFIG_SCSI_AIC79XX is not set # CONFIG_SCSI_DPT_I2O is not set -# CONFIG_SCSI_ADVANSYS is not set -# CONFIG_SCSI_IN2000 is not set -# CONFIG_SCSI_AM53C974 is not set # CONFIG_SCSI_MEGARAID is not set +# CONFIG_SCSI_SATA is not set # CONFIG_SCSI_BUSLOGIC is not set -# CONFIG_SCSI_CPQFCTS is not set # CONFIG_SCSI_DMX3191D is not set # CONFIG_SCSI_EATA is not set # CONFIG_SCSI_EATA_PIO is not set # CONFIG_SCSI_FUTURE_DOMAIN is not set # CONFIG_SCSI_GDTH is not set -# CONFIG_SCSI_GENERIC_NCR5380 is not set -# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set -# CONFIG_SCSI_INITIO is not set +# CONFIG_SCSI_IPS is not set # CONFIG_SCSI_INIA100 is not set -# CONFIG_SCSI_NCR53C7xx is not set -# CONFIG_SCSI_SYM53C8XX_2 is not set -# CONFIG_SCSI_NCR53C8XX is not set -CONFIG_SCSI_SYM53C8XX=y -CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8 -CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32 -CONFIG_SCSI_NCR53C8XX_SYNC=20 -# CONFIG_SCSI_NCR53C8XX_PROFILE is not set -# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set -# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set -# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set -# CONFIG_SCSI_PCI2000 is not set -# CONFIG_SCSI_PCI2220I is not set +CONFIG_SCSI_SYM53C8XX_2=y +CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0 +CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=8 +CONFIG_SCSI_SYM53C8XX_MAX_TAGS=32 +# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set +# CONFIG_SCSI_IPR is not set # CONFIG_SCSI_QLOGIC_ISP is not set # CONFIG_SCSI_QLOGIC_FC is not set # CONFIG_SCSI_QLOGIC_1280 is not set +CONFIG_SCSI_QLA2XXX=y +# CONFIG_SCSI_QLA21XX is not set +# CONFIG_SCSI_QLA22XX is not set +# CONFIG_SCSI_QLA2300 is not set +# CONFIG_SCSI_QLA2322 is not set +# CONFIG_SCSI_QLA6312 is not set +# CONFIG_SCSI_QLA6322 is not set # CONFIG_SCSI_DC395x is not set # CONFIG_SCSI_DC390T is not set -# CONFIG_SCSI_U14_34F is not set # CONFIG_SCSI_NSP32 is not set # CONFIG_SCSI_DEBUG is not set # +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# # Fusion MPT device support # # CONFIG_FUSION is not set # -# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# IEEE 1394 (FireWire) support # # CONFIG_IEEE1394 is not set @@ -251,6 +285,10 @@ CONFIG_SCSI_NCR53C8XX_SYNC=20 # CONFIG_I2O is not set # +# Macintosh device drivers +# + +# # Networking support # CONFIG_NET=y @@ -261,28 +299,33 @@ CONFIG_NET=y CONFIG_PACKET=y # CONFIG_PACKET_MMAP is not set # CONFIG_NETLINK_DEV is not set -CONFIG_NETFILTER=y -# CONFIG_NETFILTER_DEBUG is not set CONFIG_UNIX=y # CONFIG_NET_KEY is not set CONFIG_INET=y CONFIG_IP_MULTICAST=y # CONFIG_IP_ADVANCED_ROUTER is not set CONFIG_IP_PNP=y -CONFIG_IP_PNP_DHCP=y -# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_DHCP is not set +CONFIG_IP_PNP_BOOTP=y # CONFIG_IP_PNP_RARP is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set # CONFIG_IP_MROUTE is not set # CONFIG_ARPD is not set -# CONFIG_INET_ECN is not set # CONFIG_SYN_COOKIES is not set # CONFIG_INET_AH is not set # CONFIG_INET_ESP is not set # CONFIG_INET_IPCOMP is not set # +# IP: Virtual Server Configuration +# +# CONFIG_IP_VS is not set +# CONFIG_IPV6 is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set + +# # IP: Netfilter Configuration # CONFIG_IP_NF_CONNTRACK=m @@ -293,11 +336,13 @@ CONFIG_IP_NF_IRC=m # CONFIG_IP_NF_QUEUE is not set CONFIG_IP_NF_IPTABLES=m CONFIG_IP_NF_MATCH_LIMIT=m +# CONFIG_IP_NF_MATCH_IPRANGE is not set CONFIG_IP_NF_MATCH_MAC=m CONFIG_IP_NF_MATCH_PKTTYPE=m CONFIG_IP_NF_MATCH_MARK=m CONFIG_IP_NF_MATCH_MULTIPORT=m CONFIG_IP_NF_MATCH_TOS=m +# CONFIG_IP_NF_MATCH_RECENT is not set CONFIG_IP_NF_MATCH_ECN=m CONFIG_IP_NF_MATCH_DSCP=m CONFIG_IP_NF_MATCH_AH_ESP=m @@ -307,15 +352,15 @@ CONFIG_IP_NF_MATCH_TCPMSS=m CONFIG_IP_NF_MATCH_HELPER=m CONFIG_IP_NF_MATCH_STATE=m CONFIG_IP_NF_MATCH_CONNTRACK=m -CONFIG_IP_NF_MATCH_UNCLEAN=m CONFIG_IP_NF_MATCH_OWNER=m CONFIG_IP_NF_FILTER=m CONFIG_IP_NF_TARGET_REJECT=m -CONFIG_IP_NF_TARGET_MIRROR=m CONFIG_IP_NF_NAT=m CONFIG_IP_NF_NAT_NEEDED=y CONFIG_IP_NF_TARGET_MASQUERADE=m CONFIG_IP_NF_TARGET_REDIRECT=m +# CONFIG_IP_NF_TARGET_NETMAP is not set +# CONFIG_IP_NF_TARGET_SAME is not set # CONFIG_IP_NF_NAT_LOCAL is not set # CONFIG_IP_NF_NAT_SNMP_BASIC is not set CONFIG_IP_NF_NAT_IRC=m @@ -326,21 +371,24 @@ CONFIG_IP_NF_TARGET_ULOG=m CONFIG_IP_NF_TARGET_TCPMSS=m CONFIG_IP_NF_ARPTABLES=m CONFIG_IP_NF_ARPFILTER=m +# CONFIG_IP_NF_ARP_MANGLE is not set CONFIG_IP_NF_COMPAT_IPCHAINS=m # CONFIG_IP_NF_COMPAT_IPFWADM is not set -# CONFIG_IPV6 is not set -# CONFIG_XFRM_USER is not set +# CONFIG_IP_NF_RAW is not set +# CONFIG_IP_NF_MATCH_ADDRTYPE is not set +# CONFIG_IP_NF_MATCH_REALM is not set # # SCTP Configuration (EXPERIMENTAL) # -CONFIG_IPV6_SCTP__=y # CONFIG_IP_SCTP is not set # CONFIG_ATM is not set +# CONFIG_BRIDGE is not set # CONFIG_VLAN_8021Q is not set -# CONFIG_LLC is not set # CONFIG_DECNET is not set -# CONFIG_BRIDGE is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set # CONFIG_X25 is not set # CONFIG_LAPB is not set # CONFIG_NET_DIVERT is not set @@ -352,29 +400,33 @@ CONFIG_IPV6_SCTP__=y # QoS and/or fair queueing # # CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set # # Network testing # # CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set # # ARCnet devices # # CONFIG_ARCNET is not set -# CONFIG_DUMMY is not set -# CONFIG_BONDING is not set -# CONFIG_EQUALIZER is not set -# CONFIG_TUN is not set -# CONFIG_ETHERTAP is not set # # Ethernet (10 or 100Mbit) # CONFIG_NET_ETHERNET=y CONFIG_MII=y -# CONFIG_OAKNET is not set # CONFIG_HAPPYMEAL is not set # CONFIG_SUNGEM is not set # CONFIG_NET_VENDOR_3COM is not set @@ -389,10 +441,11 @@ CONFIG_NET_PCI=y # CONFIG_AMD8111_ETH is not set # CONFIG_ADAPTEC_STARFIRE is not set # CONFIG_B44 is not set +# CONFIG_FORCEDETH is not set # CONFIG_DGRS is not set -CONFIG_EEPRO100=y -# CONFIG_EEPRO100_PIO is not set -# CONFIG_E100 is not set +# CONFIG_EEPRO100 is not set +CONFIG_E100=y +# CONFIG_E100_NAPI is not set # CONFIG_FEALNX is not set # CONFIG_NATSEMI is not set # CONFIG_NE2K_PCI is not set @@ -403,6 +456,7 @@ CONFIG_EEPRO100=y # CONFIG_SUNDANCE is not set # CONFIG_TLAN is not set # CONFIG_VIA_RHINE is not set +# CONFIG_VIA_VELOCITY is not set # # Ethernet (1000 Mbit) @@ -421,80 +475,91 @@ CONFIG_EEPRO100=y # Ethernet (10000 Mbit) # # CONFIG_IXGB is not set -# CONFIG_FDDI is not set -# CONFIG_HIPPI is not set -# CONFIG_PPP is not set -# CONFIG_SLIP is not set +# CONFIG_S2IO is not set # -# Wireless LAN (non-hamradio) +# Token Ring devices # -# CONFIG_NET_RADIO is not set +# CONFIG_TR is not set # -# Token Ring devices (depends on LLC=y) +# Wireless LAN (non-hamradio) # -# CONFIG_NET_FC is not set -# CONFIG_RCPCI is not set -# CONFIG_SHAPER is not set +# CONFIG_NET_RADIO is not set # # Wan interfaces # # CONFIG_WAN is not set - -# -# Amateur Radio support -# -# CONFIG_HAMRADIO is not set - -# -# IrDA (infrared) support -# -# CONFIG_IRDA is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_NET_FC is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set # # ISDN subsystem # -# CONFIG_ISDN_BOOL is not set - -# -# Graphics support -# -# CONFIG_FB is not set +# CONFIG_ISDN is not set # -# Old CD-ROM drivers (not SCSI, not IDE) +# Telephony Support # -# CONFIG_CD_NO_IDESCSI is not set +# CONFIG_PHONE is not set # # Input device support # -# CONFIG_INPUT is not set +CONFIG_INPUT=y # # Userland interfaces # +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set # # Input I/O drivers # # CONFIG_GAMEPORT is not set CONFIG_SOUND_GAMEPORT=y -# CONFIG_SERIO is not set +CONFIG_SERIO=y +CONFIG_SERIO_I8042=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_PCIPS2 is not set # # Input Device Drivers # - -# -# Macintosh device drivers -# +CONFIG_INPUT_KEYBOARD=y +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +CONFIG_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set # # Character devices # +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y # CONFIG_SERIAL_NONSTANDARD is not set # @@ -502,6 +567,7 @@ CONFIG_SOUND_GAMEPORT=y # CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=4 # CONFIG_SERIAL_8250_EXTENDED is not set # @@ -510,26 +576,8 @@ CONFIG_SERIAL_8250_CONSOLE=y CONFIG_SERIAL_CORE=y CONFIG_SERIAL_CORE_CONSOLE=y CONFIG_UNIX98_PTYS=y -CONFIG_UNIX98_PTY_COUNT=256 - -# -# I2C support -# -# CONFIG_I2C is not set - -# -# I2C Hardware Sensors Mainboard support -# - -# -# I2C Hardware Sensors Chip support -# -# CONFIG_I2C_SENSOR is not set - -# -# Mice -# -# CONFIG_BUSMOUSE is not set +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 # CONFIG_QIC02_TAPE is not set # @@ -551,11 +599,23 @@ CONFIG_GEN_RTC=y # # Ftape, the floppy tape device driver # -# CONFIG_FTAPE is not set # CONFIG_AGP is not set # CONFIG_DRM is not set # CONFIG_RAW_DRIVER is not set -# CONFIG_HANGCHECK_TIMER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# # # Multimedia devices @@ -568,6 +628,33 @@ CONFIG_GEN_RTC=y # CONFIG_DVB is not set # +# Graphics support +# +# CONFIG_FB is not set + +# +# Console display driver support +# +CONFIG_VGA_CONSOLE=y +# CONFIG_MDA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# # File systems # CONFIG_EXT2_FS=y @@ -592,17 +679,20 @@ CONFIG_EXT2_FS=y # # DOS/FAT/NT Filesystems # -# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set # CONFIG_NTFS_FS is not set # # Pseudo filesystems # CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y # CONFIG_DEVFS_FS is not set -CONFIG_DEVPTS_FS=y # CONFIG_DEVPTS_FS_XATTR is not set CONFIG_TMPFS=y +# CONFIG_HUGETLB_PAGE is not set CONFIG_RAMFS=y # @@ -611,6 +701,7 @@ CONFIG_RAMFS=y # CONFIG_ADFS_FS is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set # CONFIG_BEFS_FS is not set # CONFIG_BFS_FS is not set # CONFIG_EFS_FS is not set @@ -625,19 +716,20 @@ CONFIG_RAMFS=y # Network File Systems # CONFIG_NFS_FS=y -# CONFIG_NFS_V3 is not set +CONFIG_NFS_V3=y # CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set # CONFIG_NFSD is not set CONFIG_ROOT_NFS=y CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y # CONFIG_EXPORTFS is not set CONFIG_SUNRPC=y -# CONFIG_SUNRPC_GSS is not set +# CONFIG_RPCSEC_GSS_KRB5 is not set # CONFIG_SMB_FS is not set # CONFIG_CIFS is not set # CONFIG_NCP_FS is not set # CONFIG_CODA_FS is not set -# CONFIG_INTERMEZZO_FS is not set # CONFIG_AFS_FS is not set # @@ -647,31 +739,26 @@ CONFIG_SUNRPC=y CONFIG_MSDOS_PARTITION=y # -# Sound -# -# CONFIG_SOUND is not set - -# -# USB support +# Native Language Support # -# CONFIG_USB is not set -# CONFIG_USB_GADGET is not set +# CONFIG_NLS is not set # -# Bluetooth support +# Library routines # -# CONFIG_BT is not set +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC32 is not set +# CONFIG_LIBCRC32C is not set # -# Library routines +# Profiling support # -# CONFIG_CRC32 is not set +# CONFIG_PROFILING is not set # # Kernel hacking # # CONFIG_DEBUG_KERNEL is not set -# CONFIG_KALLSYMS is not set # # Security options --- linux-2.6.9-rc1/arch/ppc/Kconfig 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/ppc/Kconfig 2004-09-03 00:56:59.608098840 -0700 @@ -243,6 +243,28 @@ config NOT_COHERENT_CACHE depends on 4xx || 8xx default y +source "drivers/perfctr/Kconfig" + +config KEXEC + bool "kexec system call (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + kexec is a system call that implements the ability to shutdown your + current kernel, and to start another kernel. It is like a reboot + but it is indepedent of the system firmware. And like a reboot + you can start any kernel with it, not just Linux. + + The name comes from the similiarity to the exec system call. + + It is an ongoing process to be certain the hardware in a machine + is properly shutdown, so do not be surprised if this code does not + initially work for you. It may help to enable device hotplugging + support. As of this writing the exact hardware interface is + strongly in flux, so no good recommendation can be made. + + In the GameCube implementation, kexec allows you to load and + run DOL files, including kernel and homebrew DOLs. + endmenu menu "Platform options" @@ -685,7 +707,8 @@ config PPC_OF config PPC_GEN550 bool - depends on SANDPOINT || MCPN765 || SPRUCE || PPLUS || PCORE || PRPMC750 || K2 || PRPMC800 + depends on SANDPOINT || MCPN765 || SPRUCE || PPLUS || PCORE || \ + PRPMC750 || K2 || PRPMC800 || LOPEC default y config FORCE @@ -811,10 +834,6 @@ config PREEMPT config HIGHMEM bool "High memory support" -config KERNEL_ELF - bool - default y - source "fs/Kconfig.binfmt" config PROC_DEVICETREE @@ -832,10 +851,11 @@ config PREP_RESIDUAL Some PReP systems have residual data passed to the kernel by the firmware. This allows detection of memory size, devices present and other useful pieces of information. Sometimes this information is - not present or incorrect. + not present or incorrect, in which case it could lead to the machine + behaving incorrectly. If this happens, either disable PREP_RESIDUAL + or pass the 'noresidual' option to the kernel. - Unless you expect to boot on a PReP system, there is no need to - select Y. + If you are running a PReP system, say Y here, otherwise say N. config PROC_PREPRESIDUAL bool "Support for reading of PReP Residual Data in /proc" --- linux-2.6.9-rc1/arch/ppc/Kconfig.debug 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/ppc/Kconfig.debug 2004-09-02 20:51:51.000000000 -0700 @@ -53,6 +53,18 @@ config BDI_SWITCH Unless you are intending to debug the kernel with one of these machines, say N here. +config SCHEDSTATS + bool "Collect scheduler statistics" + depends on DEBUG_KERNEL && PROC_FS + help + If you say Y here, additional code will be inserted into the + scheduler and related routines to collect statistics about + scheduler behavior and provide them in /proc/schedstat. These + stats may be useful for both tuning and debugging the scheduler + If you aren't debugging the scheduler or trying to tune a specific + application, you can say N to avoid the very slight overhead + this adds. + config BOOTX_TEXT bool "Support for early boot text console (BootX or OpenFirmware only)" depends PPC_OF --- linux-2.6.9-rc1/arch/ppc/kernel/cpu_setup_6xx.S 2004-08-24 00:02:22.000000000 -0700 +++ 25/arch/ppc/kernel/cpu_setup_6xx.S 2004-09-02 20:51:51.000000000 -0700 @@ -218,10 +218,10 @@ setup_745x_specifics: /* All of the bits we have to set..... */ - ori r11,r11,HID0_SGE | HID0_FOLD | HID0_BHTE | HID0_LRSTK + ori r11,r11,HID0_SGE | HID0_FOLD | HID0_BHTE | HID0_LRSTK | HID0_BTIC BEGIN_FTR_SECTION - ori r11,r11,HID0_BTIC -END_FTR_SECTION_IFCLR(CPU_FTR_NO_BTIC) + xori r11,r11,HID0_BTIC +END_FTR_SECTION_IFSET(CPU_FTR_NO_BTIC) BEGIN_FTR_SECTION oris r11,r11,HID0_DPM@h /* enable dynamic power mgmt */ END_FTR_SECTION_IFCLR(CPU_FTR_NO_DPM) --- linux-2.6.9-rc1/arch/ppc/kernel/cputable.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/ppc/kernel/cputable.c 2004-09-02 20:51:51.000000000 -0700 @@ -55,7 +55,7 @@ extern void __setup_cpu_generic(unsigned #endif /* We need to mark all pages as being coherent if we're SMP or we - * have a 754x and an MPC107 host bridge. + * have a 74[45]x and an MPC107 host bridge. */ #if defined(CONFIG_SMP) || defined(CONFIG_MPC10X_BRIDGE) #define CPU_FTR_COMMON CPU_FTR_NEED_COHERENT --- linux-2.6.9-rc1/arch/ppc/kernel/dma-mapping.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/arch/ppc/kernel/dma-mapping.c 2004-09-03 00:56:40.992928776 -0700 @@ -41,11 +41,11 @@ #include #include #include +#include #include #include #include -#include #include #include #include --- linux-2.6.9-rc1/arch/ppc/kernel/head_44x.S 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/ppc/kernel/head_44x.S 2004-09-02 20:51:51.000000000 -0700 @@ -41,16 +41,9 @@ #include #include #include +#include "head_booke.h" -/* - * Macros - */ -#define SET_IVOR(vector_number, vector_label) \ - li r26,vector_label@l; \ - mtspr SPRN_IVOR##vector_number,r26; \ - sync - /* As with the other PowerPC ports, it is expected that when code * execution begins here, the following registers contain valid, yet * optional, information: @@ -302,234 +295,6 @@ skpinv: addi r4,r4,1 /* Increment */ * We align on a 32 byte cache line boundary for good measure. */ -#define NORMAL_EXCEPTION_PROLOG \ - mtspr SPRN_SPRG0,r10; /* save two registers to work with */\ - mtspr SPRN_SPRG1,r11; \ - mtspr SPRN_SPRG4W,r1; \ - mfcr r10; /* save CR in r10 for now */\ - mfspr r11,SPRN_SRR1; /* check whether user or kernel */\ - andi. r11,r11,MSR_PR; \ - beq 1f; \ - mfspr r1,SPRG3; /* if from user, start at top of */\ - lwz r1,THREAD_INFO-THREAD(r1); /* this thread's kernel stack */\ - addi r1,r1,THREAD_SIZE; \ -1: subi r1,r1,INT_FRAME_SIZE; /* Allocate an exception frame */\ - tophys(r11,r1); \ - stw r10,_CCR(r11); /* save various registers */\ - stw r12,GPR12(r11); \ - stw r9,GPR9(r11); \ - mfspr r10,SPRG0; \ - stw r10,GPR10(r11); \ - mfspr r12,SPRG1; \ - stw r12,GPR11(r11); \ - mflr r10; \ - stw r10,_LINK(r11); \ - mfspr r10,SPRG4R; \ - mfspr r12,SRR0; \ - stw r10,GPR1(r11); \ - mfspr r9,SRR1; \ - stw r10,0(r11); \ - rlwinm r9,r9,0,14,12; /* clear MSR_WE (necessary?) */\ - stw r0,GPR0(r11); \ - SAVE_4GPRS(3, r11); \ - SAVE_2GPRS(7, r11) - -/* - * Exception prolog for critical exceptions. This is a little different - * from the normal exception prolog above since a critical exception - * can potentially occur at any point during normal exception processing. - * Thus we cannot use the same SPRG registers as the normal prolog above. - * Instead we use a couple of words of memory at low physical addresses. - * This is OK since we don't support SMP on these processors. For Book E - * processors, we also have a reserved register (SPRG2) that is only used - * in critical exceptions so we can free up a GPR to use as the base for - * indirect access to the critical exception save area. This is necessary - * since the MMU is always on and the save area is offset from KERNELBASE. - */ -#define CRITICAL_EXCEPTION_PROLOG \ - mtspr SPRG2,r8; /* SPRG2 only used in criticals */ \ - lis r8,crit_save@ha; \ - stw r10,crit_r10@l(r8); \ - stw r11,crit_r11@l(r8); \ - mfspr r10,SPRG0; \ - stw r10,crit_sprg0@l(r8); \ - mfspr r10,SPRG1; \ - stw r10,crit_sprg1@l(r8); \ - mfspr r10,SPRG4R; \ - stw r10,crit_sprg4@l(r8); \ - mfspr r10,SPRG5R; \ - stw r10,crit_sprg5@l(r8); \ - mfspr r10,SPRG7R; \ - stw r10,crit_sprg7@l(r8); \ - mfspr r10,SPRN_PID; \ - stw r10,crit_pid@l(r8); \ - mfspr r10,SRR0; \ - stw r10,crit_srr0@l(r8); \ - mfspr r10,SRR1; \ - stw r10,crit_srr1@l(r8); \ - mfspr r8,SPRG2; /* SPRG2 only used in criticals */ \ - mfcr r10; /* save CR in r10 for now */\ - mfspr r11,SPRN_CSRR1; /* check whether user or kernel */\ - andi. r11,r11,MSR_PR; \ - lis r11,critical_stack_top@h; \ - ori r11,r11,critical_stack_top@l; \ - beq 1f; \ - /* COMING FROM USER MODE */ \ - mfspr r11,SPRG3; /* if from user, start at top of */\ - lwz r11,THREAD_INFO-THREAD(r11); /* this thread's kernel stack */\ - addi r11,r11,THREAD_SIZE; \ -1: subi r11,r11,INT_FRAME_SIZE; /* Allocate an exception frame */\ - stw r10,_CCR(r11); /* save various registers */\ - stw r12,GPR12(r11); \ - stw r9,GPR9(r11); \ - mflr r10; \ - stw r10,_LINK(r11); \ - mfspr r12,SPRN_DEAR; /* save DEAR and ESR in the frame */\ - stw r12,_DEAR(r11); /* since they may have had stuff */\ - mfspr r9,SPRN_ESR; /* in them at the point where the */\ - stw r9,_ESR(r11); /* exception was taken */\ - mfspr r12,CSRR0; \ - stw r1,GPR1(r11); \ - mfspr r9,CSRR1; \ - stw r1,0(r11); \ - tovirt(r1,r11); \ - rlwinm r9,r9,0,14,12; /* clear MSR_WE (necessary?) */\ - stw r0,GPR0(r11); \ - SAVE_4GPRS(3, r11); \ - SAVE_2GPRS(7, r11) - -/* - * Exception prolog for machine check exceptions. This is similar to - * the critical exception prolog, except that machine check exceptions - * have their own save area. For Book E processors, we also have a - * reserved register (SPRG6) that is only used in machine check exceptions - * so we can free up a GPR to use as the base for indirect access to the - * machine check exception save area. This is necessary since the MMU - * is always on and the save area is offset from KERNELBASE. - */ -#define MCHECK_EXCEPTION_PROLOG \ - mtspr SPRG6W,r8; /* SPRG6 used in machine checks */ \ - lis r8,mcheck_save@ha; \ - stw r10,mcheck_r10@l(r8); \ - stw r11,mcheck_r11@l(r8); \ - mfspr r10,SPRG0; \ - stw r10,mcheck_sprg0@l(r8); \ - mfspr r10,SPRG1; \ - stw r10,mcheck_sprg1@l(r8); \ - mfspr r10,SPRG4R; \ - stw r10,mcheck_sprg4@l(r8); \ - mfspr r10,SPRG5R; \ - stw r10,mcheck_sprg5@l(r8); \ - mfspr r10,SPRG7R; \ - stw r10,mcheck_sprg7@l(r8); \ - mfspr r10,SPRN_PID; \ - stw r10,mcheck_pid@l(r8); \ - mfspr r10,SRR0; \ - stw r10,mcheck_srr0@l(r8); \ - mfspr r10,SRR1; \ - stw r10,mcheck_srr1@l(r8); \ - mfspr r10,CSRR0; \ - stw r10,mcheck_csrr0@l(r8); \ - mfspr r10,CSRR1; \ - stw r10,mcheck_csrr1@l(r8); \ - mfspr r8,SPRG6R; /* SPRG6 used in machine checks */ \ - mfcr r10; /* save CR in r10 for now */\ - mfspr r11,SPRN_MCSRR1; /* check whether user or kernel */\ - andi. r11,r11,MSR_PR; \ - lis r11,mcheck_stack_top@h; \ - ori r11,r11,mcheck_stack_top@l; \ - beq 1f; \ - /* COMING FROM USER MODE */ \ - mfspr r11,SPRG3; /* if from user, start at top of */\ - lwz r11,THREAD_INFO-THREAD(r11); /* this thread's kernel stack */\ - addi r11,r11,THREAD_SIZE; \ -1: subi r11,r11,INT_FRAME_SIZE; /* Allocate an exception frame */\ - stw r10,_CCR(r11); /* save various registers */\ - stw r12,GPR12(r11); \ - stw r9,GPR9(r11); \ - mflr r10; \ - stw r10,_LINK(r11); \ - mfspr r12,SPRN_DEAR; /* save DEAR and ESR in the frame */\ - stw r12,_DEAR(r11); /* since they may have had stuff */\ - mfspr r9,SPRN_ESR; /* in them at the point where the */\ - stw r9,_ESR(r11); /* exception was taken */\ - mfspr r12,MCSRR0; \ - stw r1,GPR1(r11); \ - mfspr r9,MCSRR1; \ - stw r1,0(r11); \ - tovirt(r1,r11); \ - rlwinm r9,r9,0,14,12; /* clear MSR_WE (necessary?) */\ - stw r0,GPR0(r11); \ - SAVE_4GPRS(3, r11); \ - SAVE_2GPRS(7, r11) - -/* - * Exception vectors. - */ -#define START_EXCEPTION(label) \ - .align 5; \ -label: - -#define FINISH_EXCEPTION(func) \ - bl transfer_to_handler_full; \ - .long func; \ - .long ret_from_except_full - -#define EXCEPTION(n, label, hdlr, xfer) \ - START_EXCEPTION(label); \ - NORMAL_EXCEPTION_PROLOG; \ - addi r3,r1,STACK_FRAME_OVERHEAD; \ - xfer(n, hdlr) - -#define CRITICAL_EXCEPTION(n, label, hdlr) \ - START_EXCEPTION(label); \ - CRITICAL_EXCEPTION_PROLOG; \ - addi r3,r1,STACK_FRAME_OVERHEAD; \ - EXC_XFER_TEMPLATE(hdlr, n+2, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \ - NOCOPY, transfer_to_handler_full, \ - ret_from_except_full) - -#define MCHECK_EXCEPTION(n, label, hdlr) \ - START_EXCEPTION(label); \ - MCHECK_EXCEPTION_PROLOG; \ - lis r4,MCSR_MCS@h; \ - mtspr SPRN_MCSR,r4; \ - mfspr r5,SPRN_ESR; \ - stw r5,_ESR(r11); \ - addi r3,r1,STACK_FRAME_OVERHEAD; \ - EXC_XFER_TEMPLATE(hdlr, n+2, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \ - NOCOPY, mcheck_transfer_to_handler, \ - ret_from_mcheck_exc) - -#define EXC_XFER_TEMPLATE(hdlr, trap, msr, copyee, tfer, ret) \ - li r10,trap; \ - stw r10,TRAP(r11); \ - lis r10,msr@h; \ - ori r10,r10,msr@l; \ - copyee(r10, r9); \ - bl tfer; \ - .long hdlr; \ - .long ret - -#define COPY_EE(d, s) rlwimi d,s,0,16,16 -#define NOCOPY(d, s) - -#define EXC_XFER_STD(n, hdlr) \ - EXC_XFER_TEMPLATE(hdlr, n, MSR_KERNEL, NOCOPY, transfer_to_handler_full, \ - ret_from_except_full) - -#define EXC_XFER_LITE(n, hdlr) \ - EXC_XFER_TEMPLATE(hdlr, n+1, MSR_KERNEL, NOCOPY, transfer_to_handler, \ - ret_from_except) - -#define EXC_XFER_EE(n, hdlr) \ - EXC_XFER_TEMPLATE(hdlr, n, MSR_KERNEL, COPY_EE, transfer_to_handler_full, \ - ret_from_except_full) - -#define EXC_XFER_EE_LITE(n, hdlr) \ - EXC_XFER_TEMPLATE(hdlr, n+1, MSR_KERNEL, COPY_EE, transfer_to_handler, \ - ret_from_except) - interrupt_base: /* Critical Input Interrupt */ CRITICAL_EXCEPTION(0x0100, CriticalInput, UnknownException) --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/ppc/kernel/head_booke.h 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,240 @@ +#ifndef __HEAD_BOOKE_H__ +#define __HEAD_BOOKE_H__ + +/* + * Macros used for common Book-e exception handling + */ + +#define SET_IVOR(vector_number, vector_label) \ + li r26,vector_label@l; \ + mtspr SPRN_IVOR##vector_number,r26; \ + sync + +#define NORMAL_EXCEPTION_PROLOG \ + mtspr SPRN_SPRG0,r10; /* save two registers to work with */\ + mtspr SPRN_SPRG1,r11; \ + mtspr SPRN_SPRG4W,r1; \ + mfcr r10; /* save CR in r10 for now */\ + mfspr r11,SPRN_SRR1; /* check whether user or kernel */\ + andi. r11,r11,MSR_PR; \ + beq 1f; \ + mfspr r1,SPRG3; /* if from user, start at top of */\ + lwz r1,THREAD_INFO-THREAD(r1); /* this thread's kernel stack */\ + addi r1,r1,THREAD_SIZE; \ +1: subi r1,r1,INT_FRAME_SIZE; /* Allocate an exception frame */\ + tophys(r11,r1); \ + stw r10,_CCR(r11); /* save various registers */\ + stw r12,GPR12(r11); \ + stw r9,GPR9(r11); \ + mfspr r10,SPRG0; \ + stw r10,GPR10(r11); \ + mfspr r12,SPRG1; \ + stw r12,GPR11(r11); \ + mflr r10; \ + stw r10,_LINK(r11); \ + mfspr r10,SPRG4R; \ + mfspr r12,SRR0; \ + stw r10,GPR1(r11); \ + mfspr r9,SRR1; \ + stw r10,0(r11); \ + rlwinm r9,r9,0,14,12; /* clear MSR_WE (necessary?) */\ + stw r0,GPR0(r11); \ + SAVE_4GPRS(3, r11); \ + SAVE_2GPRS(7, r11) + +/* + * Exception prolog for critical exceptions. This is a little different + * from the normal exception prolog above since a critical exception + * can potentially occur at any point during normal exception processing. + * Thus we cannot use the same SPRG registers as the normal prolog above. + * Instead we use a couple of words of memory at low physical addresses. + * This is OK since we don't support SMP on these processors. For Book E + * processors, we also have a reserved register (SPRG2) that is only used + * in critical exceptions so we can free up a GPR to use as the base for + * indirect access to the critical exception save area. This is necessary + * since the MMU is always on and the save area is offset from KERNELBASE. + */ +#define CRITICAL_EXCEPTION_PROLOG \ + mtspr SPRG2,r8; /* SPRG2 only used in criticals */ \ + lis r8,crit_save@ha; \ + stw r10,crit_r10@l(r8); \ + stw r11,crit_r11@l(r8); \ + mfspr r10,SPRG0; \ + stw r10,crit_sprg0@l(r8); \ + mfspr r10,SPRG1; \ + stw r10,crit_sprg1@l(r8); \ + mfspr r10,SPRG4R; \ + stw r10,crit_sprg4@l(r8); \ + mfspr r10,SPRG5R; \ + stw r10,crit_sprg5@l(r8); \ + mfspr r10,SPRG7R; \ + stw r10,crit_sprg7@l(r8); \ + mfspr r10,SPRN_PID; \ + stw r10,crit_pid@l(r8); \ + mfspr r10,SRR0; \ + stw r10,crit_srr0@l(r8); \ + mfspr r10,SRR1; \ + stw r10,crit_srr1@l(r8); \ + mfspr r8,SPRG2; /* SPRG2 only used in criticals */ \ + mfcr r10; /* save CR in r10 for now */\ + mfspr r11,SPRN_CSRR1; /* check whether user or kernel */\ + andi. r11,r11,MSR_PR; \ + lis r11,critical_stack_top@h; \ + ori r11,r11,critical_stack_top@l; \ + beq 1f; \ + /* COMING FROM USER MODE */ \ + mfspr r11,SPRG3; /* if from user, start at top of */\ + lwz r11,THREAD_INFO-THREAD(r11); /* this thread's kernel stack */\ + addi r11,r11,THREAD_SIZE; \ +1: subi r11,r11,INT_FRAME_SIZE; /* Allocate an exception frame */\ + stw r10,_CCR(r11); /* save various registers */\ + stw r12,GPR12(r11); \ + stw r9,GPR9(r11); \ + mflr r10; \ + stw r10,_LINK(r11); \ + mfspr r12,SPRN_DEAR; /* save DEAR and ESR in the frame */\ + stw r12,_DEAR(r11); /* since they may have had stuff */\ + mfspr r9,SPRN_ESR; /* in them at the point where the */\ + stw r9,_ESR(r11); /* exception was taken */\ + mfspr r12,CSRR0; \ + stw r1,GPR1(r11); \ + mfspr r9,CSRR1; \ + stw r1,0(r11); \ + tovirt(r1,r11); \ + rlwinm r9,r9,0,14,12; /* clear MSR_WE (necessary?) */\ + stw r0,GPR0(r11); \ + SAVE_4GPRS(3, r11); \ + SAVE_2GPRS(7, r11) + +/* + * Exception prolog for machine check exceptions. This is similar to + * the critical exception prolog, except that machine check exceptions + * have their own save area. For Book E processors, we also have a + * reserved register (SPRG6) that is only used in machine check exceptions + * so we can free up a GPR to use as the base for indirect access to the + * machine check exception save area. This is necessary since the MMU + * is always on and the save area is offset from KERNELBASE. + */ +#define MCHECK_EXCEPTION_PROLOG \ + mtspr SPRG6W,r8; /* SPRG6 used in machine checks */ \ + lis r8,mcheck_save@ha; \ + stw r10,mcheck_r10@l(r8); \ + stw r11,mcheck_r11@l(r8); \ + mfspr r10,SPRG0; \ + stw r10,mcheck_sprg0@l(r8); \ + mfspr r10,SPRG1; \ + stw r10,mcheck_sprg1@l(r8); \ + mfspr r10,SPRG4R; \ + stw r10,mcheck_sprg4@l(r8); \ + mfspr r10,SPRG5R; \ + stw r10,mcheck_sprg5@l(r8); \ + mfspr r10,SPRG7R; \ + stw r10,mcheck_sprg7@l(r8); \ + mfspr r10,SPRN_PID; \ + stw r10,mcheck_pid@l(r8); \ + mfspr r10,SRR0; \ + stw r10,mcheck_srr0@l(r8); \ + mfspr r10,SRR1; \ + stw r10,mcheck_srr1@l(r8); \ + mfspr r10,CSRR0; \ + stw r10,mcheck_csrr0@l(r8); \ + mfspr r10,CSRR1; \ + stw r10,mcheck_csrr1@l(r8); \ + mfspr r8,SPRG6R; /* SPRG6 used in machine checks */ \ + mfcr r10; /* save CR in r10 for now */\ + mfspr r11,SPRN_MCSRR1; /* check whether user or kernel */\ + andi. r11,r11,MSR_PR; \ + lis r11,mcheck_stack_top@h; \ + ori r11,r11,mcheck_stack_top@l; \ + beq 1f; \ + /* COMING FROM USER MODE */ \ + mfspr r11,SPRG3; /* if from user, start at top of */\ + lwz r11,THREAD_INFO-THREAD(r11); /* this thread's kernel stack */\ + addi r11,r11,THREAD_SIZE; \ +1: subi r11,r11,INT_FRAME_SIZE; /* Allocate an exception frame */\ + stw r10,_CCR(r11); /* save various registers */\ + stw r12,GPR12(r11); \ + stw r9,GPR9(r11); \ + mflr r10; \ + stw r10,_LINK(r11); \ + mfspr r12,SPRN_DEAR; /* save DEAR and ESR in the frame */\ + stw r12,_DEAR(r11); /* since they may have had stuff */\ + mfspr r9,SPRN_ESR; /* in them at the point where the */\ + stw r9,_ESR(r11); /* exception was taken */\ + mfspr r12,MCSRR0; \ + stw r1,GPR1(r11); \ + mfspr r9,MCSRR1; \ + stw r1,0(r11); \ + tovirt(r1,r11); \ + rlwinm r9,r9,0,14,12; /* clear MSR_WE (necessary?) */\ + stw r0,GPR0(r11); \ + SAVE_4GPRS(3, r11); \ + SAVE_2GPRS(7, r11) + +/* + * Exception vectors. + */ +#define START_EXCEPTION(label) \ + .align 5; \ +label: + +#define FINISH_EXCEPTION(func) \ + bl transfer_to_handler_full; \ + .long func; \ + .long ret_from_except_full + +#define EXCEPTION(n, label, hdlr, xfer) \ + START_EXCEPTION(label); \ + NORMAL_EXCEPTION_PROLOG; \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + xfer(n, hdlr) + +#define CRITICAL_EXCEPTION(n, label, hdlr) \ + START_EXCEPTION(label); \ + CRITICAL_EXCEPTION_PROLOG; \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + EXC_XFER_TEMPLATE(hdlr, n+2, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \ + NOCOPY, transfer_to_handler_full, \ + ret_from_except_full) + +#define MCHECK_EXCEPTION(n, label, hdlr) \ + START_EXCEPTION(label); \ + MCHECK_EXCEPTION_PROLOG; \ + mfspr r5,SPRN_ESR; \ + stw r5,_ESR(r11); \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + EXC_XFER_TEMPLATE(hdlr, n+2, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \ + NOCOPY, mcheck_transfer_to_handler, \ + ret_from_mcheck_exc) + +#define EXC_XFER_TEMPLATE(hdlr, trap, msr, copyee, tfer, ret) \ + li r10,trap; \ + stw r10,TRAP(r11); \ + lis r10,msr@h; \ + ori r10,r10,msr@l; \ + copyee(r10, r9); \ + bl tfer; \ + .long hdlr; \ + .long ret + +#define COPY_EE(d, s) rlwimi d,s,0,16,16 +#define NOCOPY(d, s) + +#define EXC_XFER_STD(n, hdlr) \ + EXC_XFER_TEMPLATE(hdlr, n, MSR_KERNEL, NOCOPY, transfer_to_handler_full, \ + ret_from_except_full) + +#define EXC_XFER_LITE(n, hdlr) \ + EXC_XFER_TEMPLATE(hdlr, n+1, MSR_KERNEL, NOCOPY, transfer_to_handler, \ + ret_from_except) + +#define EXC_XFER_EE(n, hdlr) \ + EXC_XFER_TEMPLATE(hdlr, n, MSR_KERNEL, COPY_EE, transfer_to_handler_full, \ + ret_from_except_full) + +#define EXC_XFER_EE_LITE(n, hdlr) \ + EXC_XFER_TEMPLATE(hdlr, n+1, MSR_KERNEL, COPY_EE, transfer_to_handler, \ + ret_from_except) + + +#endif /* __HEAD_BOOKE_H__ */ --- linux-2.6.9-rc1/arch/ppc/kernel/head_e500.S 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/ppc/kernel/head_e500.S 2004-09-02 20:51:51.000000000 -0700 @@ -41,15 +41,7 @@ #include #include #include - -/* - * Macros - */ - -#define SET_IVOR(vector_number, vector_label) \ - li r26,vector_label@l; \ - mtspr SPRN_IVOR##vector_number,r26; \ - sync +#include "head_booke.h" /* As with the other PowerPC ports, it is expected that when code * execution begins here, the following registers contain valid, yet @@ -371,232 +363,6 @@ skpinv: addi r6,r6,1 /* Increment */ * We align on a 32 byte cache line boundary for good measure. */ -#define NORMAL_EXCEPTION_PROLOG \ - mtspr SPRN_SPRG0,r10; /* save two registers to work with */\ - mtspr SPRN_SPRG1,r11; \ - mtspr SPRN_SPRG4W,r1; \ - mfcr r10; /* save CR in r10 for now */\ - mfspr r11,SPRN_SRR1; /* check whether user or kernel */\ - andi. r11,r11,MSR_PR; \ - beq 1f; \ - mfspr r1,SPRG3; /* if from user, start at top of */\ - lwz r1,THREAD_INFO-THREAD(r1); /* this thread's kernel stack */\ - addi r1,r1,THREAD_SIZE; \ -1: subi r1,r1,INT_FRAME_SIZE; /* Allocate an exception frame */\ - tophys(r11,r1); \ - stw r10,_CCR(r11); /* save various registers */\ - stw r12,GPR12(r11); \ - stw r9,GPR9(r11); \ - mfspr r10,SPRG0; \ - stw r10,GPR10(r11); \ - mfspr r12,SPRG1; \ - stw r12,GPR11(r11); \ - mflr r10; \ - stw r10,_LINK(r11); \ - mfspr r10,SPRG4R; \ - mfspr r12,SRR0; \ - stw r10,GPR1(r11); \ - mfspr r9,SRR1; \ - stw r10,0(r11); \ - rlwinm r9,r9,0,14,12; /* clear MSR_WE (necessary?) */\ - stw r0,GPR0(r11); \ - SAVE_4GPRS(3, r11); \ - SAVE_2GPRS(7, r11) - -/* - * Exception prolog for critical exceptions. This is a little different - * from the normal exception prolog above since a critical exception - * can potentially occur at any point during normal exception processing. - * Thus we cannot use the same SPRG registers as the normal prolog above. - * Instead we use a couple of words of memory at low physical addresses. - * This is OK since we don't support SMP on these processors. For Book E - * processors, we also have a reserved register (SPRG2) that is only used - * in critical exceptions so we can free up a GPR to use as the base for - * indirect access to the critical exception save area. This is necessary - * since the MMU is always on and the save area is offset from KERNELBASE. - */ -#define CRITICAL_EXCEPTION_PROLOG \ - mtspr SPRG2,r8; /* SPRG2 only used in criticals */ \ - lis r8,crit_save@ha; \ - stw r10,crit_r10@l(r8); \ - stw r11,crit_r11@l(r8); \ - mfspr r10,SPRG0; \ - stw r10,crit_sprg0@l(r8); \ - mfspr r10,SPRG1; \ - stw r10,crit_sprg1@l(r8); \ - mfspr r10,SPRG4R; \ - stw r10,crit_sprg4@l(r8); \ - mfspr r10,SPRG5R; \ - stw r10,crit_sprg5@l(r8); \ - mfspr r10,SPRG7R; \ - stw r10,crit_sprg7@l(r8); \ - mfspr r10,SPRN_PID; \ - stw r10,crit_pid@l(r8); \ - mfspr r10,SRR0; \ - stw r10,crit_srr0@l(r8); \ - mfspr r10,SRR1; \ - stw r10,crit_srr1@l(r8); \ - mfspr r8,SPRG2; /* SPRG2 only used in criticals */ \ - mfcr r10; /* save CR in r10 for now */\ - mfspr r11,SPRN_CSRR1; /* check whether user or kernel */\ - andi. r11,r11,MSR_PR; \ - lis r11,critical_stack_top@h; \ - ori r11,r11,critical_stack_top@l; \ - beq 1f; \ - /* COMING FROM USER MODE */ \ - mfspr r11,SPRG3; /* if from user, start at top of */\ - lwz r11,THREAD_INFO-THREAD(r11); /* this thread's kernel stack */\ - addi r11,r11,THREAD_SIZE; \ -1: subi r11,r11,INT_FRAME_SIZE; /* Allocate an exception frame */\ - stw r10,_CCR(r11); /* save various registers */\ - stw r12,GPR12(r11); \ - stw r9,GPR9(r11); \ - mflr r10; \ - stw r10,_LINK(r11); \ - mfspr r12,SPRN_DEAR; /* save DEAR and ESR in the frame */\ - stw r12,_DEAR(r11); /* since they may have had stuff */\ - mfspr r9,SPRN_ESR; /* in them at the point where the */\ - stw r9,_ESR(r11); /* exception was taken */\ - mfspr r12,CSRR0; \ - stw r1,GPR1(r11); \ - mfspr r9,CSRR1; \ - stw r1,0(r11); \ - tovirt(r1,r11); \ - rlwinm r9,r9,0,14,12; /* clear MSR_WE (necessary?) */\ - stw r0,GPR0(r11); \ - SAVE_4GPRS(3, r11); \ - SAVE_2GPRS(7, r11) - -/* - * Exception prolog for machine check exceptions. This is similar to - * the critical exception prolog, except that machine check exceptions - * have their own save area. For Book E processors, we also have a - * reserved register (SPRG6) that is only used in machine check exceptions - * so we can free up a GPR to use as the base for indirect access to the - * machine check exception save area. This is necessary since the MMU - * is always on and the save area is offset from KERNELBASE. - */ -#define MCHECK_EXCEPTION_PROLOG \ - mtspr SPRG6W,r8; /* SPRG6 used in machine checks */ \ - lis r8,mcheck_save@ha; \ - stw r10,mcheck_r10@l(r8); \ - stw r11,mcheck_r11@l(r8); \ - mfspr r10,SPRG0; \ - stw r10,mcheck_sprg0@l(r8); \ - mfspr r10,SPRG1; \ - stw r10,mcheck_sprg1@l(r8); \ - mfspr r10,SPRG4R; \ - stw r10,mcheck_sprg4@l(r8); \ - mfspr r10,SPRG5R; \ - stw r10,mcheck_sprg5@l(r8); \ - mfspr r10,SPRG7R; \ - stw r10,mcheck_sprg7@l(r8); \ - mfspr r10,SPRN_PID; \ - stw r10,mcheck_pid@l(r8); \ - mfspr r10,SRR0; \ - stw r10,mcheck_srr0@l(r8); \ - mfspr r10,SRR1; \ - stw r10,mcheck_srr1@l(r8); \ - mfspr r10,CSRR0; \ - stw r10,mcheck_csrr0@l(r8); \ - mfspr r10,CSRR1; \ - stw r10,mcheck_csrr1@l(r8); \ - mfspr r8,SPRG6R; /* SPRG6 used in machine checks */ \ - mfcr r10; /* save CR in r10 for now */\ - mfspr r11,SPRN_MCSRR1; /* check whether user or kernel */\ - andi. r11,r11,MSR_PR; \ - lis r11,mcheck_stack_top@h; \ - ori r11,r11,mcheck_stack_top@l; \ - beq 1f; \ - /* COMING FROM USER MODE */ \ - mfspr r11,SPRG3; /* if from user, start at top of */\ - lwz r11,THREAD_INFO-THREAD(r11); /* this thread's kernel stack */\ - addi r11,r11,THREAD_SIZE; \ -1: subi r11,r11,INT_FRAME_SIZE; /* Allocate an exception frame */\ - stw r10,_CCR(r11); /* save various registers */\ - stw r12,GPR12(r11); \ - stw r9,GPR9(r11); \ - mflr r10; \ - stw r10,_LINK(r11); \ - mfspr r12,SPRN_DEAR; /* save DEAR and ESR in the frame */\ - stw r12,_DEAR(r11); /* since they may have had stuff */\ - mfspr r9,SPRN_ESR; /* in them at the point where the */\ - stw r9,_ESR(r11); /* exception was taken */\ - mfspr r12,MCSRR0; \ - stw r1,GPR1(r11); \ - mfspr r9,MCSRR1; \ - stw r1,0(r11); \ - tovirt(r1,r11); \ - rlwinm r9,r9,0,14,12; /* clear MSR_WE (necessary?) */\ - stw r0,GPR0(r11); \ - SAVE_4GPRS(3, r11); \ - SAVE_2GPRS(7, r11) - -/* - * Exception vectors. - */ -#define START_EXCEPTION(label) \ - .align 5; \ -label: - -#define FINISH_EXCEPTION(func) \ - bl transfer_to_handler_full; \ - .long func; \ - .long ret_from_except_full - -#define EXCEPTION(n, label, hdlr, xfer) \ - START_EXCEPTION(label); \ - NORMAL_EXCEPTION_PROLOG; \ - addi r3,r1,STACK_FRAME_OVERHEAD; \ - xfer(n, hdlr) - -#define CRITICAL_EXCEPTION(n, label, hdlr) \ - START_EXCEPTION(label); \ - CRITICAL_EXCEPTION_PROLOG; \ - addi r3,r1,STACK_FRAME_OVERHEAD; \ - EXC_XFER_TEMPLATE(hdlr, n+2, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \ - NOCOPY, transfer_to_handler_full, \ - ret_from_except_full) - -#define MCHECK_EXCEPTION(n, label, hdlr) \ - START_EXCEPTION(label); \ - MCHECK_EXCEPTION_PROLOG; \ - mfspr r5,SPRN_ESR; \ - stw r5,_ESR(r11); \ - addi r3,r1,STACK_FRAME_OVERHEAD; \ - EXC_XFER_TEMPLATE(hdlr, n+2, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \ - NOCOPY, mcheck_transfer_to_handler, \ - ret_from_mcheck_exc) - -#define EXC_XFER_TEMPLATE(hdlr, trap, msr, copyee, tfer, ret) \ - li r10,trap; \ - stw r10,TRAP(r11); \ - lis r10,msr@h; \ - ori r10,r10,msr@l; \ - copyee(r10, r9); \ - bl tfer; \ - .long hdlr; \ - .long ret - -#define COPY_EE(d, s) rlwimi d,s,0,16,16 -#define NOCOPY(d, s) - -#define EXC_XFER_STD(n, hdlr) \ - EXC_XFER_TEMPLATE(hdlr, n, MSR_KERNEL, NOCOPY, transfer_to_handler_full, \ - ret_from_except_full) - -#define EXC_XFER_LITE(n, hdlr) \ - EXC_XFER_TEMPLATE(hdlr, n+1, MSR_KERNEL, NOCOPY, transfer_to_handler, \ - ret_from_except) - -#define EXC_XFER_EE(n, hdlr) \ - EXC_XFER_TEMPLATE(hdlr, n, MSR_KERNEL, COPY_EE, transfer_to_handler_full, \ - ret_from_except_full) - -#define EXC_XFER_EE_LITE(n, hdlr) \ - EXC_XFER_TEMPLATE(hdlr, n+1, MSR_KERNEL, COPY_EE, transfer_to_handler, \ - ret_from_except) - interrupt_base: /* Critical Input Interrupt */ CRITICAL_EXCEPTION(0x0100, CriticalInput, UnknownException) --- linux-2.6.9-rc1/arch/ppc/kernel/idle.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/arch/ppc/kernel/idle.c 2004-09-02 20:51:51.000000000 -0700 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -65,3 +66,36 @@ int cpu_idle(void) default_idle(); return 0; } + +#if defined(CONFIG_SYSCTL) && defined(CONFIG_6xx) +/* + * Register the sysctl to set/clear powersave_nap. + */ +extern unsigned long powersave_nap; + +static ctl_table powersave_nap_ctl_table[]={ + { + .ctl_name = KERN_PPC_POWERSAVE_NAP, + .procname = "powersave-nap", + .data = &powersave_nap, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, + { 0, }, +}; +static ctl_table powersave_nap_sysctl_root[] = { + { 1, "kernel", NULL, 0, 0755, powersave_nap_ctl_table, }, + { 0,}, +}; + +static int __init +register_powersave_nap_sysctl(void) +{ + register_sysctl_table(powersave_nap_sysctl_root, 0); + + return 0; +} + +__initcall(register_powersave_nap_sysctl); +#endif --- linux-2.6.9-rc1/arch/ppc/kernel/irq.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/ppc/kernel/irq.c 2004-09-02 20:51:51.000000000 -0700 @@ -46,6 +46,7 @@ #include #include #include +#include #include #include @@ -414,13 +415,15 @@ static inline void handle_irq_event(int irq, struct pt_regs *regs, struct irqaction *action) { int status = 0; + int ret; if (!(action->flags & SA_INTERRUPT)) local_irq_enable(); do { - status |= action->flags; - action->handler(irq, action->dev_id, regs); + ret = action->handler(irq, action->dev_id, regs); + if (ret == IRQ_HANDLED) + status |= action->flags; action = action->next; } while (action); if (status & SA_SAMPLE_RANDOM) @@ -620,32 +623,6 @@ static int irq_affinity_write_proc (stru return full_count; } -static int prof_cpu_mask_read_proc (char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - int len = cpumask_scnprintf(page, count, *(cpumask_t *)data); - if (count - len < 2) - return -EINVAL; - len += sprintf(page + len, "\n"); - return len; -} - -static int prof_cpu_mask_write_proc (struct file *file, const char __user *buffer, - unsigned long count, void *data) -{ - int err; - int full_count = count; - cpumask_t *mask = (cpumask_t *)data; - cpumask_t new_value; - - err = cpumask_parse(buffer, count, new_value); - if (err) - return err; - - *mask = new_value; - return full_count; -} - #define MAX_NAMELEN 10 static void register_irq_proc (unsigned int irq) @@ -673,23 +650,14 @@ static void register_irq_proc (unsigned smp_affinity_entry[irq] = entry; } -unsigned long prof_cpu_mask = -1; - void init_irq_proc (void) { - struct proc_dir_entry *entry; int i; /* create /proc/irq */ root_irq_dir = proc_mkdir("irq", NULL); - /* create /proc/irq/prof_cpu_mask */ - entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); - - entry->nlink = 1; - entry->data = (void *)&prof_cpu_mask; - entry->read_proc = prof_cpu_mask_read_proc; - entry->write_proc = prof_cpu_mask_write_proc; + create_prof_cpu_mask(root_irq_dir); /* * Create entries for all existing IRQs. --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/ppc/kernel/machine_kexec.c 2004-09-03 00:56:59.457121792 -0700 @@ -0,0 +1,132 @@ +/* + * machine_kexec.c - handle transition of Linux booting another kernel + * Copyright (C) 2002-2003 Eric Biederman + * + * GAMECUBE/PPC32 port Copyright (C) 2004 Albert Herranz + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef void (*relocate_new_kernel_t)( + unsigned long indirection_page, unsigned long reboot_code_buffer, + unsigned long start_address); + +const extern unsigned char relocate_new_kernel[]; +const extern unsigned int relocate_new_kernel_size; +extern void use_mm(struct mm_struct *mm); + +static int identity_map_pages(struct page *pages, int order) +{ + struct mm_struct *mm; + struct vm_area_struct *vma; + int error; + + mm = &init_mm; + vma = NULL; + + down_write(&mm->mmap_sem); + error = -ENOMEM; + vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); + if (!vma) { + goto out; + } + + memset(vma, 0, sizeof(*vma)); + vma->vm_mm = mm; + vma->vm_start = page_to_pfn(pages) << PAGE_SHIFT; + vma->vm_end = vma->vm_start + (1 << (order + PAGE_SHIFT)); + vma->vm_ops = NULL; + vma->vm_flags = VM_SHARED \ + | VM_READ | VM_WRITE | VM_EXEC \ + | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC \ + | VM_DONTCOPY | VM_RESERVED; + vma->vm_page_prot = protection_map[vma->vm_flags & 0xf]; + vma->vm_file = NULL; + vma->vm_private_data = NULL; + insert_vm_struct(mm, vma); + + error = remap_page_range(vma, vma->vm_start, vma->vm_start, + vma->vm_end - vma->vm_start, vma->vm_page_prot); + if (error) { + goto out; + } + + error = 0; + out: + if (error && vma) { + kmem_cache_free(vm_area_cachep, vma); + vma = NULL; + } + up_write(&mm->mmap_sem); + + return error; +} + +/* + * Do what every setup is needed on image and the + * reboot code buffer to allow us to avoid allocations + * later. + */ +int machine_kexec_prepare(struct kimage *image) +{ + unsigned int order; + order = get_order(KEXEC_CONTROL_CODE_SIZE); + return identity_map_pages(image->control_code_page, order); +} + +void machine_kexec_cleanup(struct kimage *image) +{ + unsigned int order; + order = get_order(KEXEC_CONTROL_CODE_SIZE); + do_munmap(&init_mm, + page_to_pfn(image->control_code_page) << PAGE_SHIFT, + 1 << (order + PAGE_SHIFT)); +} + +void machine_shutdown(void) +{ +} + +/* + * Do not allocate memory (or fail in any way) in machine_kexec(). + * We are past the point of no return, committed to rebooting now. + */ +void machine_kexec(struct kimage *image) +{ + unsigned long indirection_page; + unsigned long reboot_code_buffer; + relocate_new_kernel_t rnk; + + /* switch to an mm where the reboot_code_buffer is identity mapped */ + use_mm(&init_mm); + + /* Interrupts aren't acceptable while we reboot */ + local_irq_disable(); + + reboot_code_buffer = page_to_pfn(image->control_code_page) <head & PAGE_MASK; + + /* copy it out */ + memcpy((void *)reboot_code_buffer, + relocate_new_kernel, relocate_new_kernel_size); + + flush_icache_range(reboot_code_buffer, + reboot_code_buffer + KEXEC_CONTROL_CODE_SIZE); + printk(KERN_INFO "Bye!\n"); + + /* now call it */ + rnk = (relocate_new_kernel_t) reboot_code_buffer; + (*rnk)(indirection_page, reboot_code_buffer, image->start); +} + --- linux-2.6.9-rc1/arch/ppc/kernel/Makefile 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/ppc/kernel/Makefile 2004-09-03 00:56:59.458121640 -0700 @@ -24,6 +24,7 @@ obj-$(CONFIG_KGDB) += ppc-stub.o obj-$(CONFIG_SMP) += smp.o smp-tbsync.o obj-$(CONFIG_TAU) += temp.o obj-$(CONFIG_ALTIVEC) += vecemu.o vector.o +obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o ifdef CONFIG_MATH_EMULATION obj-$(CONFIG_8xx) += softemu8xx.o --- linux-2.6.9-rc1/arch/ppc/kernel/misc.S 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/ppc/kernel/misc.S 2004-09-03 00:56:59.459121488 -0700 @@ -617,10 +617,9 @@ _GLOBAL(flush_instruction_cache) * flush_icache_range(unsigned long start, unsigned long stop) */ _GLOBAL(flush_icache_range) - mfspr r5,PVR - rlwinm r5,r5,16,16,31 - cmpi 0,r5,1 - beqlr /* for 601, do nothing */ +BEGIN_FTR_SECTION + blr /* for 601, do nothing */ +END_FTR_SECTION_IFSET(PPC_FEATURE_UNIFIED_CACHE) li r5,L1_CACHE_LINE_SIZE-1 andc r3,r3,r5 subf r4,r3,r4 @@ -735,10 +734,9 @@ _GLOBAL(flush_dcache_all) * void __flush_dcache_icache(void *page) */ _GLOBAL(__flush_dcache_icache) - mfspr r5,PVR - rlwinm r5,r5,16,16,31 - cmpi 0,r5,1 - beqlr /* for 601, do nothing */ +BEGIN_FTR_SECTION + blr /* for 601, do nothing */ +END_FTR_SECTION_IFSET(PPC_FEATURE_UNIFIED_CACHE) rlwinm r3,r3,0,0,19 /* Get page base address */ li r4,4096/L1_CACHE_LINE_SIZE /* Number of lines in a page */ mtctr r4 @@ -764,10 +762,9 @@ _GLOBAL(__flush_dcache_icache) * void __flush_dcache_icache_phys(unsigned long physaddr) */ _GLOBAL(__flush_dcache_icache_phys) - mfspr r5,PVR - rlwinm r5,r5,16,16,31 - cmpi 0,r5,1 - beqlr /* for 601, do nothing */ +BEGIN_FTR_SECTION + blr /* for 601, do nothing */ +END_FTR_SECTION_IFSET(PPC_FEATURE_UNIFIED_CACHE) mfmsr r10 rlwinm r0,r10,0,28,26 /* clear DR */ mtmsr r0 @@ -1449,4 +1446,10 @@ _GLOBAL(sys_call_table) .long sys_mq_timedreceive /* 265 */ .long sys_mq_notify .long sys_mq_getsetattr - .long sys_ni_syscall /* 268 reserved for sys_kexec_load */ + .long sys_kexec_load + .long sys_perfctr_info + .long sys_vperfctr_open /* 270 */ + .long sys_vperfctr_control + .long sys_vperfctr_unlink + .long sys_vperfctr_iresume + .long sys_vperfctr_read --- linux-2.6.9-rc1/arch/ppc/kernel/pci.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/ppc/kernel/pci.c 2004-09-02 20:51:51.000000000 -0700 @@ -144,8 +144,7 @@ pcibios_fixup_resources(struct pci_dev * } DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_resources); -void -pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, +void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, struct resource *res) { unsigned long offset = 0; @@ -158,6 +157,7 @@ pcibios_resource_to_bus(struct pci_dev * region->start = res->start - offset; region->end = res->end - offset; } +EXPORT_SYMBOL(pcibios_resource_to_bus); /* * We need to avoid collisions with `mirrored' VGA ports @@ -172,8 +172,7 @@ pcibios_resource_to_bus(struct pci_dev * * but we want to try to avoid allocating at 0x2900-0x2bff * which might have be mirrored at 0x0100-0x03ff.. */ -void -pcibios_align_resource(void *data, struct resource *res, unsigned long size, +void pcibios_align_resource(void *data, struct resource *res, unsigned long size, unsigned long align) { struct pci_dev *dev = data; @@ -193,7 +192,7 @@ pcibios_align_resource(void *data, struc } } } - +EXPORT_SYMBOL(pcibios_align_resource); /* * Handle resources of PCI devices. If the world were perfect, we could --- linux-2.6.9-rc1/arch/ppc/kernel/ppc_htab.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/arch/ppc/kernel/ppc_htab.c 2004-09-02 20:51:51.000000000 -0700 @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -34,9 +35,6 @@ static int ppc_htab_show(struct seq_file *m, void *v); static ssize_t ppc_htab_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos); -int proc_dol2crvec(ctl_table *table, int write, struct file *filp, - void __user *buffer, size_t *lenp, loff_t *ppos); - extern PTE *Hash, *Hash_end; extern unsigned long Hash_size, Hash_mask; extern unsigned long _SDR1; @@ -438,3 +436,32 @@ int proc_dol2crvec(ctl_table *table, int *ppos += *lenp; return 0; } + +#ifdef CONFIG_SYSCTL +/* + * Register our sysctl. + */ +static ctl_table htab_ctl_table[]={ + { + .ctl_name = KERN_PPC_L2CR, + .procname = "l2cr", + .mode = 0644, + .proc_handler = &proc_dol2crvec, + }, + { 0, }, +}; +static ctl_table htab_sysctl_root[] = { + { 1, "kernel", NULL, 0, 0755, htab_ctl_table, }, + { 0,}, +}; + +static int __init +register_ppc_htab_sysctl(void) +{ + register_sysctl_table(htab_sysctl_root, 0); + + return 0; +} + +__initcall(register_ppc_htab_sysctl); +#endif --- linux-2.6.9-rc1/arch/ppc/kernel/process.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/ppc/kernel/process.c 2004-09-03 00:56:46.655068000 -0700 @@ -35,7 +35,9 @@ #include #include #include +#include #include +#include #include #include @@ -44,7 +46,6 @@ #include #include #include -#include extern unsigned long _get_SP(void); @@ -301,7 +302,9 @@ struct task_struct *__switch_to(struct t #endif /* CONFIG_SPE */ new_thread = &new->thread; old_thread = ¤t->thread; + perfctr_suspend_thread(&prev->thread); last = _switch(old_thread, new_thread); + perfctr_resume_thread(¤t->thread); local_irq_restore(s); return last; } @@ -370,6 +373,7 @@ void exit_thread(void) last_task_used_math = NULL; if (last_task_used_altivec == current) last_task_used_altivec = NULL; + perfctr_exit_thread(¤t->thread); } void flush_thread(void) @@ -460,6 +464,8 @@ copy_thread(int nr, unsigned long clone_ p->thread.last_syscall = -1; + perfctr_copy_task(p, regs); + return 0; } @@ -555,8 +561,7 @@ int sys_clone(unsigned long clone_flags, CHECK_FULL_REGS(regs); if (usp == 0) usp = regs->gpr[1]; /* stack pointer for child */ - return do_fork(clone_flags & ~CLONE_IDLETASK, usp, regs, 0, - parent_tidp, child_tidp); + return do_fork(clone_flags, usp, regs, 0, parent_tidp, child_tidp); } int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6, @@ -662,7 +667,7 @@ void show_stack(struct task_struct *tsk, ++count; sp = *(unsigned long *)sp; } -#if !CONFIG_KALLSYMS +#ifndef CONFIG_KALLSYMS if (count > 0) printk("\n"); #endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/ppc/kernel/relocate_kernel.S 2004-09-03 00:56:59.460121336 -0700 @@ -0,0 +1,127 @@ +/* + * relocate_kernel.S - put the kernel image in place to boot + * Copyright (C) 2002-2003 Eric Biederman + * + * GAMECUBE/PPC32 port Copyright (C) 2004 Albert Herranz + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include +#include +#include + +#include + +#define PAGE_SIZE 4096 /* must be same value as in */ + +/* returns r3 = relocated address of sym */ +/* modifies r0 */ +#define RELOC_SYM(sym) \ + mflr r3; \ + bl 1f; \ +1: mflr r0; \ + mtlr r3; \ + lis r3, 1b@ha; \ + ori r3, r3, 1b@l; \ + subf r0, r3, r0; \ + lis r3, sym@ha; \ + ori r3, r3, sym@l; \ + add r3, r3, r0 + + /* + * Must be relocatable PIC code callable as a C function. + */ + .globl relocate_new_kernel +relocate_new_kernel: + /* r3 = indirection_page */ + /* r4 = reboot_code_buffer */ + /* r5 = start_address */ + + li r0, 0 + + /* Set Machine Status Register to a known status */ + mr r8, r0 + ori r8, r8, MSR_RI|MSR_ME + mtmsr r8 + isync + + /* from this point address translation is turned off */ + /* and interrupts are disabled */ + + /* set a new stack at the bottom of our page... */ + /* (not really needed now) */ + addi r1, r4, KEXEC_CONTROL_CODE_SIZE - 8 /* for LR Save+Back Chain */ + stw r0, 0(r1) + + /* Do the copies */ + li r6, 0 /* checksum */ + subi r3, r3, 4 + +0: /* top, read another word for the indirection page */ + lwzu r0, 4(r3) + + /* is it a destination page? (r8) */ + rlwinm. r7, r0, 0, 31, 31 /* IND_DESTINATION (1<<0) */ + beq 1f + + rlwinm r8, r0, 0, 0, 19 /* clear kexec flags, page align */ + b 0b + +1: /* is it an indirection page? (r3) */ + rlwinm. r7, r0, 0, 30, 30 /* IND_INDIRECTION (1<<1) */ + beq 1f + + rlwinm r3, r0, 0, 0, 19 /* clear kexec flags, page align */ + subi r3, r3, 4 + b 0b + +1: /* are we done? */ + rlwinm. r7, r0, 0, 29, 29 /* IND_DONE (1<<2) */ + beq 1f + b 2f + +1: /* is it a source page? (r9) */ + rlwinm. r7, r0, 0, 28, 28 /* IND_SOURCE (1<<3) */ + beq 0b + + rlwinm r9, r0, 0, 0, 19 /* clear kexec flags, page align */ + + li r7, PAGE_SIZE / 4 + mtctr r7 + subi r9, r9, 4 + subi r8, r8, 4 +9: + lwzu r0, 4(r9) /* do the copy */ + xor r6, r6, r0 + stwu r0, 4(r8) + dcbst 0, r8 + sync + icbi 0, r8 + bdnz 9b + + addi r9, r9, 4 + addi r8, r8, 4 + b 0b + +2: + + /* To be certain of avoiding problems with self-modifying code + * execute a serializing instruction here. + */ + isync + sync + + /* jump to the entry point, usually the setup routine */ + mtlr r5 + blrl + +1: b 1b + +relocate_new_kernel_end: + + .globl relocate_new_kernel_size +relocate_new_kernel_size: + .long relocate_new_kernel_end - relocate_new_kernel + --- linux-2.6.9-rc1/arch/ppc/kernel/setup.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/ppc/kernel/setup.c 2004-09-02 20:51:51.000000000 -0700 @@ -747,6 +747,10 @@ void __init setup_arch(char **cmdline_p) if ( ppc_md.progress ) ppc_md.progress("ocp: exit", 0x3eab); #endif +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif + ppc_md.setup_arch(); if ( ppc_md.progress ) ppc_md.progress("arch: exit", 0x3eab); --- linux-2.6.9-rc1/arch/ppc/kernel/signal.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/ppc/kernel/signal.c 2004-09-02 20:51:51.000000000 -0700 @@ -404,9 +404,7 @@ badframe: printk("badframe in handle_rt_signal, regs=%p frame=%p newsp=%lx\n", regs, frame, newsp); #endif - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } static int do_setcontext(struct ucontext __user *ucp, struct pt_regs *regs, int sig) @@ -556,9 +554,7 @@ badframe: printk("badframe in handle_signal, regs=%p frame=%p newsp=%lx\n", regs, frame, newsp); #endif - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } /* @@ -604,7 +600,7 @@ badframe: int do_signal(sigset_t *oldset, struct pt_regs *regs) { siginfo_t info; - struct k_sigaction *ka; + struct k_sigaction ka; unsigned long frame, newsp; int signr, ret; @@ -613,9 +609,7 @@ int do_signal(sigset_t *oldset, struct p newsp = frame = 0; - signr = get_signal_to_deliver(&info, regs, NULL); - - ka = (signr == 0)? NULL: ¤t->sighand->action[signr-1]; + signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (TRAP(regs) == 0x0C00 /* System Call! */ && regs->ccr & 0x10000000 /* error signalled */ @@ -626,7 +620,7 @@ int do_signal(sigset_t *oldset, struct p if (signr > 0 && (ret == ERESTARTNOHAND || ret == ERESTART_RESTARTBLOCK || (ret == ERESTARTSYS - && !(ka->sa.sa_flags & SA_RESTART)))) { + && !(ka.sa.sa_flags & SA_RESTART)))) { /* make the system call return an EINTR error */ regs->result = -EINTR; regs->gpr[3] = EINTR; @@ -645,7 +639,7 @@ int do_signal(sigset_t *oldset, struct p if (signr == 0) return 0; /* no signals delivered */ - if ((ka->sa.sa_flags & SA_ONSTACK) && current->sas_ss_size + if ((ka.sa.sa_flags & SA_ONSTACK) && current->sas_ss_size && !on_sig_stack(regs->gpr[1])) newsp = current->sas_ss_sp + current->sas_ss_size; else @@ -653,17 +647,14 @@ int do_signal(sigset_t *oldset, struct p newsp &= ~0xfUL; /* Whee! Actually deliver the signal. */ - if (ka->sa.sa_flags & SA_SIGINFO) - handle_rt_signal(signr, ka, &info, oldset, regs, newsp); + if (ka.sa.sa_flags & SA_SIGINFO) + handle_rt_signal(signr, &ka, &info, oldset, regs, newsp); else - handle_signal(signr, ka, &info, oldset, regs, newsp); - - if (ka->sa.sa_flags & SA_ONESHOT) - ka->sa.sa_handler = SIG_DFL; + handle_signal(signr, &ka, &info, oldset, regs, newsp); - if (!(ka->sa.sa_flags & SA_NODEFER)) { + if (!(ka.sa.sa_flags & SA_NODEFER)) { spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigorsets(¤t->blocked,¤t->blocked,&ka.sa.sa_mask); sigaddset(¤t->blocked, signr); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); --- linux-2.6.9-rc1/arch/ppc/kernel/smp.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/ppc/kernel/smp.c 2004-09-03 00:56:40.993928624 -0700 @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -364,22 +363,15 @@ int __devinit start_secondary(void *unus int __cpu_up(unsigned int cpu) { - struct pt_regs regs; struct task_struct *p; char buf[32]; int c; /* create a process for the processor */ /* only regs.msr is actually used, and 0 is OK for it */ - memset(®s, 0, sizeof(struct pt_regs)); - p = copy_process(CLONE_VM|CLONE_IDLETASK, 0, ®s, 0, NULL, NULL); + p = fork_idle(cpu); if (IS_ERR(p)) panic("failed fork for CPU %u: %li", cpu, PTR_ERR(p)); - wake_up_forked_process(p); - - init_idle(p, cpu); - unhash_process(p); - secondary_ti = p->thread_info; p->thread_info->cpu = cpu; --- linux-2.6.9-rc1/arch/ppc/kernel/time.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/ppc/kernel/time.c 2004-09-02 20:51:51.000000000 -0700 @@ -108,42 +108,6 @@ static inline int tb_delta(unsigned *jif return delta; } -extern char _stext; - -static inline void ppc_do_profile (struct pt_regs *regs) -{ - unsigned long nip; - extern unsigned long prof_cpu_mask; - - profile_hook(regs); - - if (user_mode(regs)) - return; - - if (!prof_buffer) - return; - - nip = instruction_pointer(regs); - - /* - * Only measure the CPUs specified by /proc/irq/prof_cpu_mask. - * (default is all CPUs.) - */ - if (!((1<>= prof_shift; - /* - * Don't ignore out-of-bounds EIP values silently, - * put them into the last histogram slot, so if - * present, they will show up as a sharp peak. - */ - if (nip > prof_len-1) - nip = prof_len-1; - atomic_inc((atomic_t *)&prof_buffer[nip]); -} - /* * timer_interrupt - gets called when the decrementer overflows, * with interrupts disabled. @@ -164,7 +128,7 @@ void timer_interrupt(struct pt_regs * re while ((next_dec = tb_ticks_per_jiffy - tb_delta(&jiffy_stamp)) <= 0) { jiffy_stamp += tb_ticks_per_jiffy; - ppc_do_profile(regs); + profile_tick(CPU_PROFILING, regs); if (smp_processor_id()) continue; --- linux-2.6.9-rc1/arch/ppc/kernel/vmlinux.lds.S 2004-08-24 00:03:50.000000000 -0700 +++ 25/arch/ppc/kernel/vmlinux.lds.S 2004-09-02 20:51:51.000000000 -0700 @@ -102,9 +102,6 @@ SECTIONS __setup_start = .; .init.setup : { *(.init.setup) } __setup_end = .; - __start___param = .; - __param : { *(__param) } - __stop___param = .; __initcall_start = .; .initcall.init : { *(.initcall1.init) --- linux-2.6.9-rc1/arch/ppc/Makefile 2004-08-24 00:01:53.000000000 -0700 +++ 25/arch/ppc/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -104,16 +104,15 @@ arch/$(ARCH)/kernel/asm-offsets.s: inclu include/asm-$(ARCH)/offsets.h: arch/$(ARCH)/kernel/asm-offsets.s $(call filechk,gen-asm-offsets) -ifdef CONFIG_6xx -# Ensure this is binutils 2.12.1 (or 2.12.90.0.7) or later -NEW_AS := $(shell echo dssall | $(AS) -many -o /dev/null >/dev/null 2>&1 ; echo $$?) -GOODVER := 2.12.1 -else -NEW_AS := 0 -endif +# Use the file '.tmp_gas_check' for binutils tests, as gas won't output +# to stdout and these checks are run even on install targets. +TOUT := .tmp_gas_check +# Ensure this is binutils 2.12.1 (or 2.12.90.0.7) or later for altivec +# instructions. +AS_ALTIVEC := $(shell echo dssall | $(AS) -many -o $(TOUT) >/dev/null 2>&1 ; echo $$?) # gcc-3.4 and binutils-2.14 are a fatal combination. GCC_VERSION := $(call cc-version) -BAD_GCC_AS := $(shell echo mftb 5 | $(AS) -mppc -many -o /dev/null >/dev/null 2>&1 && echo 0 || echo 1) +BAD_GCC_AS := $(shell echo mftb 5 | $(AS) -mppc -many -o $(TOUT) >/dev/null 2>&1 && echo 0 || echo 1) checkbin: ifeq ($(GCC_VERSION)$(BAD_GCC_AS),03041) @@ -122,10 +121,11 @@ ifeq ($(GCC_VERSION)$(BAD_GCC_AS),03041) @echo '*** Please upgrade your binutils or downgrade your gcc' @false endif -ifneq ($(NEW_AS),0) +ifneq ($(AS_ALTIVEC),0) + echo $(AS_ALTIVEC) @echo -n '*** ${VERSION}.${PATCHLEVEL} kernels no longer build ' @echo 'correctly with old versions of binutils.' - @echo '*** Please upgrade your binutils to ${GOODVER} or newer' + @echo '*** Please upgrade your binutils to 2.12.1 or newer' @false endif @true --- linux-2.6.9-rc1/arch/ppc/math-emu/op-common.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/ppc/math-emu/op-common.h 2004-09-02 20:51:51.000000000 -0700 @@ -82,7 +82,6 @@ do { \ if (X##_e <= _FP_WFRACBITS_##fs) \ { \ _FP_FRAC_SRS_##wc(X, X##_e, _FP_WFRACBITS_##fs); \ - __ret |= _FP_ROUND(wc, X); \ _FP_FRAC_SLL_##wc(X, 1); \ if (_FP_FRAC_OVERP_##wc(fs, X)) \ { \ --- linux-2.6.9-rc1/arch/ppc/platforms/4xx/ebony.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/ppc/platforms/4xx/ebony.c 2004-09-02 20:51:51.000000000 -0700 @@ -361,10 +361,6 @@ ebony_setup_arch(void) ROOT_DEV = Root_HDA1; #endif -#ifdef CONFIG_VT - conswitchp = &dummy_con; -#endif - ebony_early_serial_map(); ibm4xxPIC_InitSenses = ebony_IRQ_initsenses; --- linux-2.6.9-rc1/arch/ppc/platforms/4xx/ocotea.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/ppc/platforms/4xx/ocotea.c 2004-09-02 20:51:51.000000000 -0700 @@ -319,10 +319,6 @@ ocotea_setup_arch(void) ROOT_DEV = Root_HDA1; #endif -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif - ocotea_early_serial_map(&clocks); /* Identify the system */ --- linux-2.6.9-rc1/arch/ppc/platforms/85xx/mpc8540_ads.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/ppc/platforms/85xx/mpc8540_ads.c 2004-09-02 20:51:51.000000000 -0700 @@ -120,10 +120,6 @@ mpc8540ads_setup_arch(void) mpc85xx_setup_hose(); #endif -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif - #ifdef CONFIG_SERIAL_8250 mpc85xx_early_serial_map(); #endif --- linux-2.6.9-rc1/arch/ppc/platforms/85xx/mpc85xx_cds_common.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/ppc/platforms/85xx/mpc85xx_cds_common.c 2004-09-02 20:51:51.000000000 -0700 @@ -307,7 +307,7 @@ mpc85xx_exclude_device(u_char bus, u_cha { if (bus == 0 && PCI_SLOT(devfn) == 0) return PCIBIOS_DEVICE_NOT_FOUND; -#if CONFIG_85xx_PCI2 +#ifdef CONFIG_85xx_PCI2 /* With the current code we know PCI2 will be bus 2, however this may * not be guarnteed */ if (bus == 2 && PCI_SLOT(devfn) == 0) @@ -358,10 +358,6 @@ mpc85xx_cds_setup_arch(void) mpc85xx_setup_hose(); #endif -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif - #ifdef CONFIG_SERIAL_8250 mpc85xx_early_serial_map(); #endif --- linux-2.6.9-rc1/arch/ppc/platforms/85xx/sbc8560.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/ppc/platforms/85xx/sbc8560.c 2004-09-02 20:51:51.000000000 -0700 @@ -144,9 +144,6 @@ sbc8560_setup_arch(void) /* setup PCI host bridges */ mpc85xx_setup_hose(); #endif -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif #ifdef CONFIG_SERIAL_8250 sbc8560_early_serial_map(); #endif --- linux-2.6.9-rc1/arch/ppc/platforms/chrp_setup.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/ppc/platforms/chrp_setup.c 2004-09-02 20:51:51.000000000 -0700 @@ -250,13 +250,6 @@ chrp_setup_arch(void) */ sio_init(); - /* - * Setup the console operations - */ -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif - /* Get the event scan rate for the rtas so we know how * often it expects a heartbeat. -- Cort */ --- linux-2.6.9-rc1/arch/ppc/platforms/chrp_smp.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/arch/ppc/platforms/chrp_smp.c 2004-09-03 00:56:40.993928624 -0700 @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include --- linux-2.6.9-rc1/arch/ppc/platforms/est8260.h 2004-08-24 00:03:29.000000000 -0700 +++ 25/arch/ppc/platforms/est8260.h 2004-09-02 20:51:51.000000000 -0700 @@ -10,6 +10,9 @@ #define BOOTROM_RESTART_ADDR ((uint)0xff000104) +/* For our show_cpuinfo hooks. */ +#define CPUINFO_VENDOR "EST Corporation" +#define CPUINFO_MACHINE "SBC8260 PowerPC" /* A Board Information structure that is given to a program when * prom starts it up. --- linux-2.6.9-rc1/arch/ppc/platforms/est8260_setup.c 2004-08-24 00:01:53.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,65 +0,0 @@ -/* - * arch/ppc/platforms/est8260_setup.c - * - * EST8260 platform support - * - * Author: Allen Curtis - * Derived from: m8260_setup.c by Dan Malek, MVista - * - * Copyright 2002 Ones and Zeros, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include - -#include -#include - -static void (*callback_setup_arch)(void); - -extern unsigned char __res[sizeof(bd_t)]; - -extern void m8260_init(unsigned long r3, unsigned long r4, - unsigned long r5, unsigned long r6, unsigned long r7); - -static int -est8260_show_cpuinfo(struct seq_file *m) -{ - bd_t *binfo = (bd_t *)__res; - - seq_printf(m, "vendor\t\t: EST Corporation\n" - "machine\t\t: SBC8260 PowerPC\n" - "\n" - "mem size\t\t: 0x%08x\n" - "console baud\t\t: %d\n" - "\n", - binfo->bi_memsize, - binfo->bi_baudrate); - return 0; -} - -static void __init -est8260_setup_arch(void) -{ - printk("EST SBC8260 Port\n"); - callback_setup_arch(); -} - -void __init -platform_init(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7) -{ - /* Generic 8260 platform initialization */ - m8260_init(r3, r4, r5, r6, r7); - - /* Anything special for this platform */ - ppc_md.show_cpuinfo = est8260_show_cpuinfo; - - callback_setup_arch = ppc_md.setup_arch; - ppc_md.setup_arch = est8260_setup_arch; -} --- linux-2.6.9-rc1/arch/ppc/platforms/k2.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/arch/ppc/platforms/k2.c 2004-09-02 20:51:51.000000000 -0700 @@ -464,10 +464,6 @@ static void __init k2_setup_arch(void) ROOT_DEV = Root_HDC1; #endif -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif - /* Identify the system */ printk(KERN_INFO "System Identification: SBS K2 - PowerPC 750 @ " "%d Mhz\n", k2_get_cpu_speed() / 1000000); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/ppc/platforms/lopec.c 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,410 @@ +/* + * arch/ppc/platforms/lopec.c + * + * Setup routines for the Motorola LoPEC. + * + * Author: Dan Cox + * Maintainer: Tom Rini + * + * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Define all of the IRQ senses and polarities. Taken from the + * LoPEC Programmer's Reference Guide. + */ +static u_char lopec_openpic_initsenses[16] __initdata = { + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* IRQ 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* IRQ 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 3 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* IRQ 4 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* IRQ 5 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 6 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 7 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 8 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 9 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 10 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 11 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 12 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* IRQ 13 */ + (IRQ_SENSE_EDGE | IRQ_POLARITY_NEGATIVE), /* IRQ 14 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE) /* IRQ 15 */ +}; + +static inline int __init +lopec_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + int irq; + static char pci_irq_table[][4] = { + {16, 0, 0, 0}, /* ID 11 - Winbond */ + {22, 0, 0, 0}, /* ID 12 - SCSI */ + {0, 0, 0, 0}, /* ID 13 - nothing */ + {17, 0, 0, 0}, /* ID 14 - 82559 Ethernet */ + {27, 0, 0, 0}, /* ID 15 - USB */ + {23, 0, 0, 0}, /* ID 16 - PMC slot 1 */ + {24, 0, 0, 0}, /* ID 17 - PMC slot 2 */ + {25, 0, 0, 0}, /* ID 18 - PCI slot */ + {0, 0, 0, 0}, /* ID 19 - nothing */ + {0, 0, 0, 0}, /* ID 20 - nothing */ + {0, 0, 0, 0}, /* ID 21 - nothing */ + {0, 0, 0, 0}, /* ID 22 - nothing */ + {0, 0, 0, 0}, /* ID 23 - nothing */ + {0, 0, 0, 0}, /* ID 24 - PMC slot 1b */ + {0, 0, 0, 0}, /* ID 25 - nothing */ + {0, 0, 0, 0} /* ID 26 - PMC Slot 2b */ + }; + const long min_idsel = 11, max_idsel = 26, irqs_per_slot = 4; + + irq = PCI_IRQ_TABLE_LOOKUP; + if (!irq) + return 0; + + return irq; +} + +static void __init +lopec_setup_winbond_83553(struct pci_controller *hose) +{ + int devfn; + + devfn = PCI_DEVFN(11,0); + + /* IDE interrupt routing (primary 14, secondary 15) */ + early_write_config_byte(hose, 0, devfn, 0x43, 0xef); + /* PCI interrupt routing */ + early_write_config_word(hose, 0, devfn, 0x44, 0x0000); + + /* ISA-PCI address decoder */ + early_write_config_byte(hose, 0, devfn, 0x48, 0xf0); + + /* RTC, kb, not used in PPC */ + early_write_config_byte(hose, 0, devfn, 0x4d, 0x00); + early_write_config_byte(hose, 0, devfn, 0x4e, 0x04); + devfn = PCI_DEVFN(11, 1); + early_write_config_byte(hose, 0, devfn, 0x09, 0x8f); + early_write_config_dword(hose, 0, devfn, 0x40, 0x00ff0011); +} + +static void __init +lopec_find_bridges(void) +{ + struct pci_controller *hose; + + hose = pcibios_alloc_controller(); + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + + if (mpc10x_bridge_init(hose, MPC10X_MEM_MAP_B, MPC10X_MEM_MAP_B, + MPC10X_MAPB_EUMB_BASE) == 0) { + + hose->mem_resources[0].end = 0xffffffff; + lopec_setup_winbond_83553(hose); + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = lopec_map_irq; + } +} + +static int +lopec_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "machine\t\t: Motorola LoPEC\n"); + return 0; +} + +static u32 +lopec_irq_canonicalize(u32 irq) +{ + if (irq == 2) + return 9; + else + return irq; +} + +static void +lopec_restart(char *cmd) +{ +#define LOPEC_SYSSTAT1 0xffe00000 + /* force a hard reset, if possible */ + unsigned char reg = *((unsigned char *) LOPEC_SYSSTAT1); + reg |= 0x80; + *((unsigned char *) LOPEC_SYSSTAT1) = reg; + + local_irq_disable(); + while(1); +#undef LOPEC_SYSSTAT1 +} + +static void +lopec_halt(void) +{ + local_irq_disable(); + while(1); +} + +static void +lopec_power_off(void) +{ + lopec_halt(); +} + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) +int lopec_ide_ports_known = 0; +static unsigned long lopec_ide_regbase[MAX_HWIFS]; +static unsigned long lopec_ide_ctl_regbase[MAX_HWIFS]; +static unsigned long lopec_idedma_regbase; + +static void +lopec_ide_probe(void) +{ + struct pci_dev *dev = pci_find_device(PCI_VENDOR_ID_WINBOND, + PCI_DEVICE_ID_WINBOND_82C105, + NULL); + lopec_ide_ports_known = 1; + + if (dev) { + lopec_ide_regbase[0] = dev->resource[0].start; + lopec_ide_regbase[1] = dev->resource[2].start; + lopec_ide_ctl_regbase[0] = dev->resource[1].start; + lopec_ide_ctl_regbase[1] = dev->resource[3].start; + lopec_idedma_regbase = dev->resource[4].start; + } +} + +static int +lopec_ide_default_irq(unsigned long base) +{ + if (lopec_ide_ports_known == 0) + lopec_ide_probe(); + + if (base == lopec_ide_regbase[0]) + return 14; + else if (base == lopec_ide_regbase[1]) + return 15; + else + return 0; +} + +static unsigned long +lopec_ide_default_io_base(int index) +{ + if (lopec_ide_ports_known == 0) + lopec_ide_probe(); + return lopec_ide_regbase[index]; +} + +static void __init +lopec_ide_init_hwif_ports(hw_regs_t *hw, unsigned long data, + unsigned long ctl, int *irq) +{ + unsigned long reg = data; + uint alt_status_base; + int i; + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) + hw->io_ports[i] = reg++; + + if (data == lopec_ide_regbase[0]) { + alt_status_base = lopec_ide_ctl_regbase[0] + 2; + hw->irq = 14; + } else if (data == lopec_ide_regbase[1]) { + alt_status_base = lopec_ide_ctl_regbase[1] + 2; + hw->irq = 15; + } else { + alt_status_base = 0; + hw->irq = 0; + } + + if (ctl) + hw->io_ports[IDE_CONTROL_OFFSET] = ctl; + else + hw->io_ports[IDE_CONTROL_OFFSET] = alt_status_base; + + if (irq != NULL) + *irq = hw->irq; + +} +#endif /* BLK_DEV_IDE */ + +static void __init +lopec_init_IRQ(void) +{ + int i; + + /* + * Provide the open_pic code with the correct table of interrupts. + */ + OpenPIC_InitSenses = lopec_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(lopec_openpic_initsenses); + + mpc10x_set_openpic(); + + /* We have a cascade on OpenPIC IRQ 0, Linux IRQ 16 */ + openpic_hookup_cascade(NUM_8259_INTERRUPTS, "82c59 cascade", + &i8259_irq); + + /* Map i8259 interrupts */ + for(i = 0; i < NUM_8259_INTERRUPTS; i++) + irq_desc[i].handler = &i8259_pic; + + /* + * The EPIC allows for a read in the range of 0xFEF00000 -> + * 0xFEFFFFFF to generate a PCI interrupt-acknowledge transaction. + */ + i8259_init(0xfef00000); +} + +static int __init +lopec_request_io(void) +{ + outb(0x00, 0x4d0); + outb(0xc0, 0x4d1); + + request_region(0x00, 0x20, "dma1"); + request_region(0x20, 0x20, "pic1"); + request_region(0x40, 0x20, "timer"); + request_region(0x80, 0x10, "dma page reg"); + request_region(0xa0, 0x20, "pic2"); + request_region(0xc0, 0x20, "dma2"); + + return 0; +} + +device_initcall(lopec_request_io); + +static void __init +lopec_map_io(void) +{ + io_block_mapping(0xf0000000, 0xf0000000, 0x10000000, _PAGE_IO); + io_block_mapping(0xb0000000, 0xb0000000, 0x10000000, _PAGE_IO); +} + +/* + * Set BAT 3 to map 0xf8000000 to end of physical memory space 1-to-1. + */ +static __inline__ void +lopec_set_bat(void) +{ + mb(); + mtspr(DBAT1U, 0xf8000ffe); + mtspr(DBAT1L, 0xf800002a); + mb(); +} + +TODC_ALLOC(); + +static void __init +lopec_setup_arch(void) +{ + + TODC_INIT(TODC_TYPE_MK48T37, 0, 0, + ioremap(0xffe80000, 0x8000), 8); + + loops_per_jiffy = 100000000/HZ; + + lopec_find_bridges(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#elif defined(CONFIG_ROOT_NFS) + ROOT_DEV = Root_NFS; +#elif defined(CONFIG_BLK_DEV_IDEDISK) + ROOT_DEV = Root_HDA1; +#else + ROOT_DEV = Root_SDA1; +#endif + +#ifdef CONFIG_PPCBUG_NVRAM + /* Read in NVRAM data */ + init_prep_nvram(); + + /* if no bootargs, look in NVRAM */ + if ( cmd_line[0] == '\0' ) { + char *bootargs; + bootargs = prep_nvram_get_var("bootargs"); + if (bootargs != NULL) { + strcpy(cmd_line, bootargs); + /* again.. */ + strcpy(saved_command_line, cmd_line); + } + } +#endif +} + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + lopec_set_bat(); + + isa_io_base = MPC10X_MAPB_ISA_IO_BASE; + isa_mem_base = MPC10X_MAPB_ISA_MEM_BASE; + pci_dram_offset = MPC10X_MAPB_DRAM_OFFSET; + ISA_DMA_THRESHOLD = 0x00ffffff; + DMA_MODE_READ = 0x44; + DMA_MODE_WRITE = 0x48; + + ppc_md.setup_arch = lopec_setup_arch; + ppc_md.show_cpuinfo = lopec_show_cpuinfo; + ppc_md.irq_canonicalize = lopec_irq_canonicalize; + ppc_md.init_IRQ = lopec_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + + ppc_md.restart = lopec_restart; + ppc_md.power_off = lopec_power_off; + ppc_md.halt = lopec_halt; + + ppc_md.setup_io_mappings = lopec_map_io; + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.calibrate_decr = todc_calibrate_decr; + + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.default_irq = lopec_ide_default_irq; + ppc_ide_md.default_io_base = lopec_ide_default_io_base; + ppc_ide_md.ide_init_hwif = lopec_ide_init_hwif_ports; +#endif +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.progress = gen550_progress; +#endif +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/ppc/platforms/lopec.h 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,39 @@ +/* + * include/asm-ppc/lopec_serial.h + * + * Definitions for Motorola LoPEC board. + * + * Author: Dan Cox + * danc@mvista.com (or, alternately, source@mvista.com) + * + * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef __H_LOPEC_SERIAL +#define __H_LOPEC_SERIAL + +#define RS_TABLE_SIZE 3 + +#define BASE_BAUD (1843200 / 16) + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) +#endif + +#define SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, 0xffe10000, 29, STD_COM_FLAGS, \ + iomem_base: (u8 *) 0xffe10000, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, 0xffe11000, 20, STD_COM_FLAGS, \ + iomem_base: (u8 *) 0xffe11000, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, 0xffe12000, 21, STD_COM_FLAGS, \ + iomem_base: (u8 *) 0xffe12000, \ + io_type: SERIAL_IO_MEM } + +#endif --- linux-2.6.9-rc1/arch/ppc/platforms/lopec_pci.c 2004-08-24 00:02:47.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,99 +0,0 @@ -/* - * arch/ppc/platforms/lopec_pci.c - * - * PCI setup routines for the Motorola LoPEC. - * - * Author: Dan Cox - * danc@mvista.com (or, alternately, source@mvista.com) - * - * 2001-2002 (c) MontaVista, Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ - -#include -#include - -#include -#include -#include - -static inline int __init -lopec_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) -{ - int irq; - static char pci_irq_table[][4] = { - {16, 0, 0, 0}, /* ID 11 - Winbond */ - {22, 0, 0, 0}, /* ID 12 - SCSI */ - {0, 0, 0, 0}, /* ID 13 - nothing */ - {17, 0, 0, 0}, /* ID 14 - 82559 Ethernet */ - {27, 0, 0, 0}, /* ID 15 - USB */ - {23, 0, 0, 0}, /* ID 16 - PMC slot 1 */ - {24, 0, 0, 0}, /* ID 17 - PMC slot 2 */ - {25, 0, 0, 0}, /* ID 18 - PCI slot */ - {0, 0, 0, 0}, /* ID 19 - nothing */ - {0, 0, 0, 0}, /* ID 20 - nothing */ - {0, 0, 0, 0}, /* ID 21 - nothing */ - {0, 0, 0, 0}, /* ID 22 - nothing */ - {0, 0, 0, 0}, /* ID 23 - nothing */ - {0, 0, 0, 0}, /* ID 24 - PMC slot 1b */ - {0, 0, 0, 0}, /* ID 25 - nothing */ - {0, 0, 0, 0} /* ID 26 - PMC Slot 2b */ - }; - const long min_idsel = 11, max_idsel = 26, irqs_per_slot = 4; - - irq = PCI_IRQ_TABLE_LOOKUP; - if (!irq) - return 0; - - return irq; -} - -void __init -lopec_setup_winbond_83553(struct pci_controller *hose) -{ - int devfn; - - devfn = PCI_DEVFN(11,0); - - /* IDE interrupt routing (primary 14, secondary 15) */ - early_write_config_byte(hose, 0, devfn, 0x43, 0xef); - /* PCI interrupt routing */ - early_write_config_word(hose, 0, devfn, 0x44, 0x0000); - - /* ISA-PCI address decoder */ - early_write_config_byte(hose, 0, devfn, 0x48, 0xf0); - - /* RTC, kb, not used in PPC */ - early_write_config_byte(hose, 0, devfn, 0x4d, 0x00); - early_write_config_byte(hose, 0, devfn, 0x4e, 0x04); - devfn = PCI_DEVFN(11, 1); - early_write_config_byte(hose, 0, devfn, 0x09, 0x8f); - early_write_config_dword(hose, 0, devfn, 0x40, 0x00ff0011); -} - -void __init -lopec_find_bridges(void) -{ - struct pci_controller *hose; - - hose = pcibios_alloc_controller(); - if (!hose) - return; - - hose->first_busno = 0; - hose->last_busno = 0xff; - - if (mpc10x_bridge_init(hose, - MPC10X_MEM_MAP_B, - MPC10X_MEM_MAP_B, - MPC10X_MAPB_EUMB_BASE) == 0) { - - hose->mem_resources[0].end = 0xffffffff; - lopec_setup_winbond_83553(hose); - hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); - ppc_md.pci_swizzle = common_swizzle; - ppc_md.pci_map_irq = lopec_map_irq; - } -} --- linux-2.6.9-rc1/arch/ppc/platforms/lopec_serial.h 2004-08-24 00:03:31.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,39 +0,0 @@ -/* - * include/asm-ppc/lopec_serial.h - * - * Definitions for Motorola LoPEC board. - * - * Author: Dan Cox - * danc@mvista.com (or, alternately, source@mvista.com) - * - * 2001 (c) MontaVista, Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ - -#ifndef __H_LOPEC_SERIAL -#define __H_LOPEC_SERIAL - -#define RS_TABLE_SIZE 3 - -#define BASE_BAUD (1843200 / 16) - -#ifdef CONFIG_SERIAL_DETECT_IRQ -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) -#else -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) -#endif - -#define SERIAL_PORT_DFNS \ - { 0, BASE_BAUD, 0xffe10000, 29, STD_COM_FLAGS, \ - iomem_base: (u8 *) 0xffe10000, \ - io_type: SERIAL_IO_MEM }, \ - { 0, BASE_BAUD, 0xffe11000, 20, STD_COM_FLAGS, \ - iomem_base: (u8 *) 0xffe11000, \ - io_type: SERIAL_IO_MEM }, \ - { 0, BASE_BAUD, 0xffe12000, 21, STD_COM_FLAGS, \ - iomem_base: (u8 *) 0xffe12000, \ - io_type: SERIAL_IO_MEM } - -#endif --- linux-2.6.9-rc1/arch/ppc/platforms/lopec_setup.c 2004-08-24 00:02:48.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,378 +0,0 @@ -/* - * arch/ppc/platforms/lopec_setup.c - * - * Setup routines for the Motorola LoPEC. - * - * Author: Dan Cox - * danc@mvista.com - * - * 2001-2002 (c) MontaVista, Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -extern void lopec_find_bridges(void); - -/* - * Define all of the IRQ senses and polarities. Taken from the - * LoPEC Programmer's Reference Guide. - */ -static u_char lopec_openpic_initsenses[16] __initdata = { - (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* IRQ 0 */ - (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 1 */ - (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* IRQ 2 */ - (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 3 */ - (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* IRQ 4 */ - (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* IRQ 5 */ - (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 6 */ - (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 7 */ - (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 8 */ - (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 9 */ - (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 10 */ - (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 11 */ - (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* IRQ 12 */ - (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* IRQ 13 */ - (IRQ_SENSE_EDGE | IRQ_POLARITY_NEGATIVE), /* IRQ 14 */ - (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE) /* IRQ 15 */ -}; - -static int -lopec_show_cpuinfo(struct seq_file *m) -{ - seq_printf(m, "machine\t\t: Motorola LoPEC\n"); - return 0; -} - -static u32 -lopec_irq_canonicalize(u32 irq) -{ - if (irq == 2) - return 9; - else - return irq; -} - -static void -lopec_restart(char *cmd) -{ -#define LOPEC_SYSSTAT1 0xffe00000 - /* force a hard reset, if possible */ - unsigned char reg = *((unsigned char *) LOPEC_SYSSTAT1); - reg |= 0x80; - *((unsigned char *) LOPEC_SYSSTAT1) = reg; - - local_irq_disable(); - while(1); -#undef LOPEC_SYSSTAT1 -} - -static void -lopec_halt(void) -{ - local_irq_disable(); - while(1); -} - -static void -lopec_power_off(void) -{ - lopec_halt(); -} - -#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) -int lopec_ide_ports_known = 0; -static unsigned long lopec_ide_regbase[MAX_HWIFS]; -static unsigned long lopec_ide_ctl_regbase[MAX_HWIFS]; -static unsigned long lopec_idedma_regbase; - -static void -lopec_ide_probe(void) -{ - struct pci_dev *dev = pci_find_device(PCI_VENDOR_ID_WINBOND, - PCI_DEVICE_ID_WINBOND_82C105, - NULL); - lopec_ide_ports_known = 1; - - if (dev) { - lopec_ide_regbase[0] = dev->resource[0].start; - lopec_ide_regbase[1] = dev->resource[2].start; - lopec_ide_ctl_regbase[0] = dev->resource[1].start; - lopec_ide_ctl_regbase[1] = dev->resource[3].start; - lopec_idedma_regbase = dev->resource[4].start; - } -} - -static int -lopec_ide_default_irq(unsigned long base) -{ - if (lopec_ide_ports_known == 0) - lopec_ide_probe(); - - if (base == lopec_ide_regbase[0]) - return 14; - else if (base == lopec_ide_regbase[1]) - return 15; - else - return 0; -} - -static unsigned long -lopec_ide_default_io_base(int index) -{ - if (lopec_ide_ports_known == 0) - lopec_ide_probe(); - return lopec_ide_regbase[index]; -} - -static void __init -lopec_ide_init_hwif_ports(hw_regs_t *hw, unsigned long data, - unsigned long ctl, int *irq) -{ - unsigned long reg = data; - uint alt_status_base; - int i; - - for(i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) - hw->io_ports[i] = reg++; - - if (data == lopec_ide_regbase[0]) { - alt_status_base = lopec_ide_ctl_regbase[0] + 2; - hw->irq = 14; - } - else if (data == lopec_ide_regbase[1]) { - alt_status_base = lopec_ide_ctl_regbase[1] + 2; - hw->irq = 15; - } - else { - alt_status_base = 0; - hw->irq = 0; - } - - if (ctl) - hw->io_ports[IDE_CONTROL_OFFSET] = ctl; - else - hw->io_ports[IDE_CONTROL_OFFSET] = alt_status_base; - - if (irq != NULL) - *irq = hw->irq; - -} -#endif /* BLK_DEV_IDE */ - -static void __init -lopec_init_IRQ(void) -{ - int i; - - /* - * Provide the open_pic code with the correct table of interrupts. - */ - OpenPIC_InitSenses = lopec_openpic_initsenses; - OpenPIC_NumInitSenses = sizeof(lopec_openpic_initsenses); - - mpc10x_set_openpic(); - - /* We have a cascade on OpenPIC IRQ 0, Linux IRQ 16 */ - openpic_hookup_cascade(NUM_8259_INTERRUPTS, "82c59 cascade", - &i8259_irq); - - /* Map i8259 interrupts */ - for(i = 0; i < NUM_8259_INTERRUPTS; i++) - irq_desc[i].handler = &i8259_pic; - - /* - * The EPIC allows for a read in the range of 0xFEF00000 -> - * 0xFEFFFFFF to generate a PCI interrupt-acknowledge transaction. - */ - i8259_init(0xfef00000); -} - -static int __init -lopec_request_io(void) -{ - outb(0x00, 0x4d0); - outb(0xc0, 0x4d1); - - request_region(0x00, 0x20, "dma1"); - request_region(0x20, 0x20, "pic1"); - request_region(0x40, 0x20, "timer"); - request_region(0x80, 0x10, "dma page reg"); - request_region(0xa0, 0x20, "pic2"); - request_region(0xc0, 0x20, "dma2"); - - return 0; -} - -device_initcall(lopec_request_io); - -static void __init -lopec_map_io(void) -{ - io_block_mapping(0xf0000000, 0xf0000000, 0x10000000, _PAGE_IO); - io_block_mapping(0xb0000000, 0xb0000000, 0x10000000, _PAGE_IO); -} - -static void __init -lopec_set_bat(void) -{ - unsigned long batu, batl; - - __asm__ __volatile__( - "lis %0,0xf800\n \ - ori %1,%0,0x002a\n \ - ori %0,%0,0x0ffe\n \ - mtspr 0x21e,%0\n \ - mtspr 0x21f,%1\n \ - isync\n \ - sync " - : "=r" (batu), "=r" (batl)); -} - -#ifdef CONFIG_SERIAL_TEXT_DEBUG -#include -#include -#include -#include - -static struct serial_state rs_table[RS_TABLE_SIZE] = { - SERIAL_PORT_DFNS /* Defined in */ -}; - -volatile unsigned char *com_port; -volatile unsigned char *com_port_lsr; - -static void -serial_writechar(char c) -{ - while ((*com_port_lsr & UART_LSR_THRE) == 0) - ; - *com_port = c; -} - -void -lopec_progress(char *s, unsigned short hex) -{ - volatile char c; - - com_port = (volatile unsigned char *) rs_table[0].port; - com_port_lsr = com_port + UART_LSR; - - while ((c = *s++) != 0) - serial_writechar(c); - - /* Most messages don't have a newline in them */ - serial_writechar('\n'); - serial_writechar('\r'); -} -#endif /* CONFIG_SERIAL_TEXT_DEBUG */ - -TODC_ALLOC(); - -static void __init -lopec_setup_arch(void) -{ - - TODC_INIT(TODC_TYPE_MK48T37, 0, 0, - ioremap(0xffe80000, 0x8000), 8); - - loops_per_jiffy = 100000000/HZ; - - lopec_find_bridges(); - -#ifdef CONFIG_BLK_DEV_INITRD - if (initrd_start) - ROOT_DEV = Root_RAM0; - else -#elif defined(CONFIG_ROOT_NFS) - ROOT_DEV = Root_NFS; -#elif defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) - ROOT_DEV = Root_HDA1; -#else - ROOT_DEV = Root_SDA1; -#endif - -#ifdef CONFIG_VT - conswitchp = &dummy_con; -#endif -#ifdef CONFIG_PPCBUG_NVRAM - /* Read in NVRAM data */ - init_prep_nvram(); - - /* if no bootargs, look in NVRAM */ - if ( cmd_line[0] == '\0' ) { - char *bootargs; - bootargs = prep_nvram_get_var("bootargs"); - if (bootargs != NULL) { - strcpy(cmd_line, bootargs); - /* again.. */ - strcpy(saved_command_line, cmd_line); - } - } -#endif -} - -void __init -platform_init(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7) -{ - parse_bootinfo(find_bootinfo()); - lopec_set_bat(); - - isa_io_base = MPC10X_MAPB_ISA_IO_BASE; - isa_mem_base = MPC10X_MAPB_ISA_MEM_BASE; - pci_dram_offset = MPC10X_MAPB_DRAM_OFFSET; - ISA_DMA_THRESHOLD = 0x00ffffff; - DMA_MODE_READ = 0x44; - DMA_MODE_WRITE = 0x48; - - ppc_md.setup_arch = lopec_setup_arch; - ppc_md.show_cpuinfo = lopec_show_cpuinfo; - ppc_md.irq_canonicalize = lopec_irq_canonicalize; - ppc_md.init_IRQ = lopec_init_IRQ; - ppc_md.get_irq = openpic_get_irq; - - ppc_md.restart = lopec_restart; - ppc_md.power_off = lopec_power_off; - ppc_md.halt = lopec_halt; - - ppc_md.setup_io_mappings = lopec_map_io; - - ppc_md.time_init = todc_time_init; - ppc_md.set_rtc_time = todc_set_rtc_time; - ppc_md.get_rtc_time = todc_get_rtc_time; - ppc_md.calibrate_decr = todc_calibrate_decr; - - ppc_md.nvram_read_val = todc_direct_read_val; - ppc_md.nvram_write_val = todc_direct_write_val; - -#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) - ppc_ide_md.default_irq = lopec_ide_default_irq; - ppc_ide_md.default_io_base = lopec_ide_default_io_base; - ppc_ide_md.ide_init_hwif = lopec_ide_init_hwif_ports; -#endif -#ifdef CONFIG_SERIAL_TEXT_DEBUG - ppc_md.progress = lopec_progress; -#endif -} --- linux-2.6.9-rc1/arch/ppc/platforms/Makefile 2004-08-24 00:03:49.000000000 -0700 +++ 25/arch/ppc/platforms/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -21,23 +21,21 @@ endif obj-$(CONFIG_PMAC_BACKLIGHT) += pmac_backlight.o obj-$(CONFIG_PREP_RESIDUAL) += residual.o obj-$(CONFIG_ADIR) += adir_setup.o adir_pic.o adir_pci.o -obj-$(CONFIG_EST8260) += est8260_setup.o -obj-$(CONFIG_PQ2ADS) += pq2ads_setup.o +obj-$(CONFIG_PQ2ADS) += pq2ads.o obj-$(CONFIG_TQM8260) += tqm8260_setup.o obj-$(CONFIG_EV64260) += ev64260_setup.o obj-$(CONFIG_GEMINI) += gemini_pci.o gemini_setup.o gemini_prom.o obj-$(CONFIG_K2) += k2.o -obj-$(CONFIG_LOPEC) += lopec_setup.o lopec_pci.o +obj-$(CONFIG_LOPEC) += lopec.o obj-$(CONFIG_MCPN765) += mcpn765.o obj-$(CONFIG_MENF1) += menf1_setup.o menf1_pci.o -obj-$(CONFIG_MVME5100) += mvme5100_setup.o mvme5100_pci.o +obj-$(CONFIG_MVME5100) += mvme5100.o obj-$(CONFIG_PAL4) += pal4_setup.o pal4_pci.o obj-$(CONFIG_PCORE) += pcore.o obj-$(CONFIG_POWERPMC250) += powerpmc250.o obj-$(CONFIG_PPLUS) += pplus.o obj-$(CONFIG_PRPMC750) += prpmc750.o obj-$(CONFIG_PRPMC800) += prpmc800.o -obj-$(CONFIG_RPX8260) += rpx8260.o obj-$(CONFIG_SANDPOINT) += sandpoint.o obj-$(CONFIG_SBC82xx) += sbc82xx.o obj-$(CONFIG_SPRUCE) += spruce.o --- linux-2.6.9-rc1/arch/ppc/platforms/mcpn765.c 2004-08-24 00:03:17.000000000 -0700 +++ 25/arch/ppc/platforms/mcpn765.c 2004-09-02 20:51:51.000000000 -0700 @@ -58,8 +58,6 @@ #include #include "mcpn765.h" -#include "mcpn765_serial.h" - static u_char mcpn765_openpic_initsenses[] __initdata = { (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE),/* 16: i8259 cascade */ @@ -324,10 +322,6 @@ mcpn765_setup_arch(void) ROOT_DEV = Root_SDA2; #endif -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif - if ( ppc_md.progress ) ppc_md.progress("mcpn765_setup_arch: find_bridges", 0); --- linux-2.6.9-rc1/arch/ppc/platforms/mcpn765.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/ppc/platforms/mcpn765.h 2004-09-02 20:51:51.000000000 -0700 @@ -6,7 +6,7 @@ * Author: Mark A. Greer * mgreer@mvista.com * - * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under * the terms of the GNU General Public License version 2. This program * is licensed "as is" without any warranty of any kind, whether express * or implied. @@ -25,6 +25,7 @@ #ifndef __PPC_PLATFORMS_MCPN765_H #define __PPC_PLATFORMS_MCPN765_H +#include /* PCI Memory space mapping info */ #define MCPN765_PCI_MEM_SIZE 0x40000000U @@ -65,14 +66,57 @@ #define MCPN765_BOARD_EXT_FEATURE_REG 0xfef880f0U #define MCPN765_BOARD_LAST_RESET_REG 0xfef880f8U -/* UART base addresses are defined in */ +/* Defines for UART */ + +/* Define the UART base addresses */ +#define MCPN765_SERIAL_1 0xfef88000 +#define MCPN765_SERIAL_2 0xfef88200 +#define MCPN765_SERIAL_3 0xfef88400 +#define MCPN765_SERIAL_4 0xfef88600 + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define RS_TABLE_SIZE 64 +#else +#define RS_TABLE_SIZE 4 +#endif + +/* Rate for the 1.8432 Mhz clock for the onboard serial chip */ +#define BASE_BAUD ( 1843200 / 16 ) +#define UART_CLK 1843200 + +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) +#endif + +/* All UART IRQ's are wire-OR'd to IRQ 17 */ +#define STD_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, MCPN765_SERIAL_1, 17, STD_COM_FLAGS, /* ttyS0 */\ + iomem_base: (u8 *)MCPN765_SERIAL_1, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, MCPN765_SERIAL_2, 17, STD_COM_FLAGS, /* ttyS1 */\ + iomem_base: (u8 *)MCPN765_SERIAL_2, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, MCPN765_SERIAL_3, 17, STD_COM_FLAGS, /* ttyS2 */\ + iomem_base: (u8 *)MCPN765_SERIAL_3, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, MCPN765_SERIAL_4, 17, STD_COM_FLAGS, /* ttyS3 */\ + iomem_base: (u8 *)MCPN765_SERIAL_4, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DFNS /* Define the NVRAM/RTC address strobe & data registers */ #define MCPN765_PHYS_NVRAM_AS0 0xfef880c8U #define MCPN765_PHYS_NVRAM_AS1 0xfef880d0U #define MCPN765_PHYS_NVRAM_DATA 0xfef880d8U - extern void mcpn765_find_bridges(void); #endif /* __PPC_PLATFORMS_MCPN765_H */ --- linux-2.6.9-rc1/arch/ppc/platforms/mcpn765_serial.h 2004-08-24 00:03:30.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,64 +0,0 @@ -/* - * include/asm-ppc/mcpn765_serial.h - * - * Definitions for Motorola MCG MCPN765 cPCI board support - * - * Author: Mark A. Greer - * mgreer@mvista.com - * - * 2001 (c) MontaVista, Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ - -#ifndef __ASMPPC_MCPN765_SERIAL_H -#define __ASMPPC_MCPN765_SERIAL_H - -#include - -/* Define the UART base addresses */ -#define MCPN765_SERIAL_1 0xfef88000 -#define MCPN765_SERIAL_2 0xfef88200 -#define MCPN765_SERIAL_3 0xfef88400 -#define MCPN765_SERIAL_4 0xfef88600 - -#ifdef CONFIG_SERIAL_MANY_PORTS -#define RS_TABLE_SIZE 64 -#else -#define RS_TABLE_SIZE 4 -#endif - -/* Rate for the 1.8432 Mhz clock for the onboard serial chip */ -#define BASE_BAUD ( 1843200 / 16 ) -#define UART_CLK 1843200 - -#ifdef CONFIG_SERIAL_DETECT_IRQ -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) -#else -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) -#endif - -/* All UART IRQ's are wire-OR'd to IRQ 17 */ -#define STD_SERIAL_PORT_DFNS \ - { 0, BASE_BAUD, MCPN765_SERIAL_1, 17, STD_COM_FLAGS, /* ttyS0 */\ - iomem_base: (u8 *)MCPN765_SERIAL_1, \ - iomem_reg_shift: 4, \ - io_type: SERIAL_IO_MEM }, \ - { 0, BASE_BAUD, MCPN765_SERIAL_2, 17, STD_COM_FLAGS, /* ttyS1 */\ - iomem_base: (u8 *)MCPN765_SERIAL_2, \ - iomem_reg_shift: 4, \ - io_type: SERIAL_IO_MEM }, \ - { 0, BASE_BAUD, MCPN765_SERIAL_3, 17, STD_COM_FLAGS, /* ttyS2 */\ - iomem_base: (u8 *)MCPN765_SERIAL_3, \ - iomem_reg_shift: 4, \ - io_type: SERIAL_IO_MEM }, \ - { 0, BASE_BAUD, MCPN765_SERIAL_4, 17, STD_COM_FLAGS, /* ttyS3 */\ - iomem_base: (u8 *)MCPN765_SERIAL_4, \ - iomem_reg_shift: 4, \ - io_type: SERIAL_IO_MEM }, - -#define SERIAL_PORT_DFNS \ - STD_SERIAL_PORT_DFNS - -#endif /* __ASMPPC_MCPN765_SERIAL_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/ppc/platforms/mvme5100.c 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,349 @@ +/* + * arch/ppc/platforms/mvme5100.c + * + * Board setup routines for the Motorola MVME5100. + * + * Author: Matt Porter + * + * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static u_char mvme5100_openpic_initsenses[16] __initdata = { + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* i8259 cascade */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* TL16C550 UART 1,2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Enet1 front panel or P2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Hawk Watchdog 1,2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* DS1621 thermal alarm */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Universe II LINT0# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Universe II LINT1# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Universe II LINT2# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Universe II LINT3# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PMC1 INTA#, PMC2 INTB# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PMC1 INTB#, PMC2 INTC# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PMC1 INTC#, PMC2 INTD# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* PMC1 INTD#, PMC2 INTA# */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Enet 2 (front panel) */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* Abort Switch */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* RTC Alarm */ +}; + +static inline int +mvme5100_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + int irq; + + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { 0, 0, 0, 0 }, /* IDSEL 11 - Winbond */ + { 0, 0, 0, 0 }, /* IDSEL 12 - unused */ + { 21, 22, 23, 24 }, /* IDSEL 13 - Universe II */ + { 18, 0, 0, 0 }, /* IDSEL 14 - Enet 1 */ + { 0, 0, 0, 0 }, /* IDSEL 15 - unused */ + { 25, 26, 27, 28 }, /* IDSEL 16 - PMC Slot 1 */ + { 28, 25, 26, 27 }, /* IDSEL 17 - PMC Slot 2 */ + { 0, 0, 0, 0 }, /* IDSEL 18 - unused */ + { 29, 0, 0, 0 }, /* IDSEL 19 - Enet 2 */ + { 0, 0, 0, 0 }, /* IDSEL 20 - PMCSPAN */ + }; + + const long min_idsel = 11, max_idsel = 20, irqs_per_slot = 4; + irq = PCI_IRQ_TABLE_LOOKUP; + /* If lookup is zero, always return 0 */ + if (!irq) + return 0; + else +#ifdef CONFIG_MVME5100_IPMC761_PRESENT + /* If IPMC761 present, return table value */ + return irq; +#else + /* If IPMC761 not present, we don't have an i8259 so adjust */ + return (irq - NUM_8259_INTERRUPTS); +#endif +} + +static void +mvme5100_pcibios_fixup_resources(struct pci_dev *dev) +{ + int i; + + if ((dev->vendor == PCI_VENDOR_ID_MOTOROLA) && + (dev->device == PCI_DEVICE_ID_MOTOROLA_HAWK)) + for (i=0; iresource[i].start = 0; + dev->resource[i].end = 0; + } +} + +static void __init +mvme5100_setup_bridge(void) +{ + struct pci_controller* hose; + + hose = pcibios_alloc_controller(); + + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + hose->pci_mem_offset = MVME5100_PCI_MEM_OFFSET; + + pci_init_resource(&hose->io_resource, MVME5100_PCI_LOWER_IO, + MVME5100_PCI_UPPER_IO, IORESOURCE_IO, + "PCI host bridge"); + + pci_init_resource(&hose->mem_resources[0], MVME5100_PCI_LOWER_MEM, + MVME5100_PCI_UPPER_MEM, IORESOURCE_MEM, + "PCI host bridge"); + + hose->io_space.start = MVME5100_PCI_LOWER_IO; + hose->io_space.end = MVME5100_PCI_UPPER_IO; + hose->mem_space.start = MVME5100_PCI_LOWER_MEM; + hose->mem_space.end = MVME5100_PCI_UPPER_MEM; + hose->io_base_virt = (void *)MVME5100_ISA_IO_BASE; + + /* Use indirect method of Hawk */ + setup_indirect_pci(hose, MVME5100_PCI_CONFIG_ADDR, + MVME5100_PCI_CONFIG_DATA); + + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + ppc_md.pcibios_fixup_resources = mvme5100_pcibios_fixup_resources; + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = mvme5100_map_irq; +} + +static void __init +mvme5100_setup_arch(void) +{ + if ( ppc_md.progress ) + ppc_md.progress("mvme5100_setup_arch: enter", 0); + + loops_per_jiffy = 50000000 / HZ; + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = Root_NFS; +#else + ROOT_DEV = Root_SDA2; +#endif + + if ( ppc_md.progress ) + ppc_md.progress("mvme5100_setup_arch: find_bridges", 0); + + /* Setup PCI host bridge */ + mvme5100_setup_bridge(); + + /* Find and map our OpenPIC */ + hawk_mpic_init(MVME5100_PCI_MEM_OFFSET); + OpenPIC_InitSenses = mvme5100_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(mvme5100_openpic_initsenses); + + printk("MVME5100 port (C) 2001 MontaVista Software, Inc. (source@mvista.com)\n"); + + if ( ppc_md.progress ) + ppc_md.progress("mvme5100_setup_arch: exit", 0); + + return; +} + +static void __init +mvme5100_init2(void) +{ +#ifdef CONFIG_MVME5100_IPMC761_PRESENT + request_region(0x00,0x20,"dma1"); + request_region(0x20,0x20,"pic1"); + request_region(0x40,0x20,"timer"); + request_region(0x80,0x10,"dma page reg"); + request_region(0xa0,0x20,"pic2"); + request_region(0xc0,0x20,"dma2"); +#endif + return; +} + +/* + * Interrupt setup and service. + * Have MPIC on HAWK and cascaded 8259s on Winbond cascaded to MPIC. + */ +static void __init +mvme5100_init_IRQ(void) +{ +#ifdef CONFIG_MVME5100_IPMC761_PRESENT + int i; +#endif + + if ( ppc_md.progress ) + ppc_md.progress("init_irq: enter", 0); + + openpic_set_sources(0, 16, OpenPIC_Addr + 0x10000); +#ifdef CONFIG_MVME5100_IPMC761_PRESENT + openpic_init(NUM_8259_INTERRUPTS); + openpic_hookup_cascade(NUM_8259_INTERRUPTS, "82c59 cascade", + &i8259_irq); + + /* Map i8259 interrupts. */ + for (i = 0; i < NUM_8259_INTERRUPTS; i++) + irq_desc[i].handler = &i8259_pic; + + i8259_init(NULL); +#else + openpic_init(0); +#endif + + if ( ppc_md.progress ) + ppc_md.progress("init_irq: exit", 0); + + return; +} + +/* + * Set BAT 3 to map 0xf0000000 to end of physical memory space. + */ +static __inline__ void +mvme5100_set_bat(void) +{ + mb(); + mtspr(DBAT1U, 0xf0001ffe); + mtspr(DBAT1L, 0xf000002a); + mb(); +} + +static unsigned long __init +mvme5100_find_end_of_memory(void) +{ + return hawk_get_mem_size(MVME5100_HAWK_SMC_BASE); +} + +static void __init +mvme5100_map_io(void) +{ + io_block_mapping(0xfe000000, 0xfe000000, 0x02000000, _PAGE_IO); + ioremap_base = 0xfe000000; +} + +static void +mvme5100_reset_board(void) +{ + local_irq_disable(); + + /* Set exception prefix high - to the firmware */ + _nmask_and_or_msr(0, MSR_IP); + + out_8((u_char *)MVME5100_BOARD_MODRST_REG, 0x01); + + return; +} + +static void +mvme5100_restart(char *cmd) +{ + volatile ulong i = 10000000; + + mvme5100_reset_board(); + + while (i-- > 0); + panic("restart failed\n"); +} + +static void +mvme5100_halt(void) +{ + local_irq_disable(); + while (1); +} + +static void +mvme5100_power_off(void) +{ + mvme5100_halt(); +} + +static int +mvme5100_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "vendor\t\t: Motorola\n"); + seq_printf(m, "machine\t\t: MVME5100\n"); + + return 0; +} + +TODC_ALLOC(); + +void __init +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + parse_bootinfo(find_bootinfo()); + mvme5100_set_bat(); + + isa_io_base = MVME5100_ISA_IO_BASE; + isa_mem_base = MVME5100_ISA_MEM_BASE; + pci_dram_offset = MVME5100_PCI_DRAM_OFFSET; + + ppc_md.setup_arch = mvme5100_setup_arch; + ppc_md.show_cpuinfo = mvme5100_show_cpuinfo; + ppc_md.init_IRQ = mvme5100_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + ppc_md.init = mvme5100_init2; + + ppc_md.restart = mvme5100_restart; + ppc_md.power_off = mvme5100_power_off; + ppc_md.halt = mvme5100_halt; + + ppc_md.find_end_of_memory = mvme5100_find_end_of_memory; + ppc_md.setup_io_mappings = mvme5100_map_io; + + TODC_INIT(TODC_TYPE_MK48T37, MVME5100_NVRAM_AS0, MVME5100_NVRAM_AS1, + MVME5100_NVRAM_DATA, 8); + + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.calibrate_decr = todc_calibrate_decr; + + ppc_md.nvram_read_val = todc_m48txx_read_val; + ppc_md.nvram_write_val = todc_m48txx_write_val; +} --- linux-2.6.9-rc1/arch/ppc/platforms/mvme5100.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/ppc/platforms/mvme5100.h 2004-09-02 20:51:51.000000000 -0700 @@ -63,10 +63,29 @@ #define MVME5100_SERIAL_IRQ 1 #endif -#define MVME5100_WINBOND_DEVFN 0x58 -#define MVME5100_WINBOND_VIDDID 0x056510ad +#define RS_TABLE_SIZE 4 -extern void mvme5100_setup_bridge(void); +#define BASE_BAUD ( MVME5100_BASE_BAUD / 16 ) + +#define STD_COM_FLAGS ASYNC_BOOT_AUTOCONF + +/* All UART IRQ's are wire-OR'd to one MPIC IRQ */ +#define STD_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, MVME5100_SERIAL_1, \ + MVME5100_SERIAL_IRQ, \ + STD_COM_FLAGS, /* ttyS0 */ \ + iomem_base: (unsigned char *)MVME5100_SERIAL_1, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, \ + { 0, BASE_BAUD, MVME5100_SERIAL_2, \ + MVME5100_SERIAL_IRQ, \ + STD_COM_FLAGS, /* ttyS1 */ \ + iomem_base: (unsigned char *)MVME5100_SERIAL_2, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM }, + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DFNS #endif /* __ASM_MVME5100_H__ */ #endif /* __KERNEL__ */ --- linux-2.6.9-rc1/arch/ppc/platforms/mvme5100_pci.c 2004-08-24 00:01:54.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,121 +0,0 @@ -/* - * arch/ppc/platforms/mvme5100_pci.c - * - * PCI setup routines for the Motorola MVME5100. - * - * Author: Matt Porter - * - * 2001 (c) MontaVista, Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -static inline int -mvme5100_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) -{ - int irq; - - static char pci_irq_table[][4] = - /* - * PCI IDSEL/INTPIN->INTLINE - * A B C D - */ - { - { 0, 0, 0, 0 }, /* IDSEL 11 - Winbond */ - { 0, 0, 0, 0 }, /* IDSEL 12 - unused */ - { 21, 22, 23, 24 }, /* IDSEL 13 - Universe II */ - { 18, 0, 0, 0 }, /* IDSEL 14 - Enet 1 */ - { 0, 0, 0, 0 }, /* IDSEL 15 - unused */ - { 25, 26, 27, 28 }, /* IDSEL 16 - PMC Slot 1 */ - { 28, 25, 26, 27 }, /* IDSEL 17 - PMC Slot 2 */ - { 0, 0, 0, 0 }, /* IDSEL 18 - unused */ - { 29, 0, 0, 0 }, /* IDSEL 19 - Enet 2 */ - { 0, 0, 0, 0 }, /* IDSEL 20 - PMCSPAN */ - }; - - const long min_idsel = 11, max_idsel = 20, irqs_per_slot = 4; - irq = PCI_IRQ_TABLE_LOOKUP; - /* If lookup is zero, always return 0 */ - if (!irq) - return 0; - else -#ifdef CONFIG_MVME5100_IPMC761_PRESENT - /* If IPMC761 present, return table value */ - return irq; -#else - /* If IPMC761 not present, we don't have an i8259 so adjust */ - return (irq - NUM_8259_INTERRUPTS); -#endif -} - -static void -mvme5100_pcibios_fixup_resources(struct pci_dev *dev) -{ - int i; - - if ((dev->vendor == PCI_VENDOR_ID_MOTOROLA) && - (dev->device == PCI_DEVICE_ID_MOTOROLA_HAWK)) - for (i=0; iresource[i].start = 0; - dev->resource[i].end = 0; - } -} - -void __init -mvme5100_setup_bridge(void) -{ - struct pci_controller* hose; - - hose = pcibios_alloc_controller(); - - if (!hose) - return; - - hose->first_busno = 0; - hose->last_busno = 0xff; - hose->pci_mem_offset = MVME5100_PCI_MEM_OFFSET; - - pci_init_resource(&hose->io_resource, - MVME5100_PCI_LOWER_IO, - MVME5100_PCI_UPPER_IO, - IORESOURCE_IO, - "PCI host bridge"); - - pci_init_resource(&hose->mem_resources[0], - MVME5100_PCI_LOWER_MEM, - MVME5100_PCI_UPPER_MEM, - IORESOURCE_MEM, - "PCI host bridge"); - - hose->io_space.start = MVME5100_PCI_LOWER_IO; - hose->io_space.end = MVME5100_PCI_UPPER_IO; - hose->mem_space.start = MVME5100_PCI_LOWER_MEM; - hose->mem_space.end = MVME5100_PCI_UPPER_MEM; - hose->io_base_virt = (void *)MVME5100_ISA_IO_BASE; - - /* Use indirect method of Hawk */ - setup_indirect_pci(hose, - MVME5100_PCI_CONFIG_ADDR, - MVME5100_PCI_CONFIG_DATA); - - hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); - - ppc_md.pcibios_fixup_resources = mvme5100_pcibios_fixup_resources; - ppc_md.pci_swizzle = common_swizzle; - ppc_md.pci_map_irq = mvme5100_map_irq; -} --- linux-2.6.9-rc1/arch/ppc/platforms/mvme5100_serial.h 2004-08-24 00:02:48.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,54 +0,0 @@ -/* - * include/asm-ppc/mvme5100_serial.h - * - * Definitions for Motorola MVME5100 support - * - * Author: Matt Porter - * - * 2001 (c) MontaVista, Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ - -#ifdef __KERNEL__ -#ifndef __ASM_MVME5100_SERIAL_H__ -#define __ASM_MVME5100_SERIAL_H__ - -#include -#include - -#ifdef CONFIG_SERIAL_MANY_PORTS -#define RS_TABLE_SIZE 64 -#else -#define RS_TABLE_SIZE 4 -#endif - -#define BASE_BAUD ( MVME5100_BASE_BAUD / 16 ) - -#ifdef CONFIG_SERIAL_DETECT_IRQ -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) -#else -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) -#endif - -/* All UART IRQ's are wire-OR'd to one MPIC IRQ */ -#define STD_SERIAL_PORT_DFNS \ - { 0, BASE_BAUD, MVME5100_SERIAL_1, \ - MVME5100_SERIAL_IRQ, \ - STD_COM_FLAGS, /* ttyS0 */ \ - iomem_base: (unsigned char *)MVME5100_SERIAL_1, \ - iomem_reg_shift: 4, \ - io_type: SERIAL_IO_MEM }, \ - { 0, BASE_BAUD, MVME5100_SERIAL_2, \ - MVME5100_SERIAL_IRQ, \ - STD_COM_FLAGS, /* ttyS1 */ \ - iomem_base: (unsigned char *)MVME5100_SERIAL_2, \ - iomem_reg_shift: 4, \ - io_type: SERIAL_IO_MEM }, - -#define SERIAL_PORT_DFNS \ - STD_SERIAL_PORT_DFNS - -#endif /* __ASM_MVME5100_SERIAL_H__ */ -#endif /* __KERNEL__ */ --- linux-2.6.9-rc1/arch/ppc/platforms/mvme5100_setup.c 2004-08-24 00:02:25.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,284 +0,0 @@ -/* - * arch/ppc/platforms/mvme5100_setup.c - * - * Board setup routines for the Motorola MVME5100. - * - * Author: Matt Porter - * - * 2001 (c) MontaVista, Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern char cmd_line[]; - -static u_char mvme5100_openpic_initsenses[] __initdata = { - 0, /* 16: i8259 cascade (active high) */ - 1, /* 17: TL16C550 UART 1,2 */ - 1, /* 18: Enet 1 (front panel or P2) */ - 1, /* 19: Hawk Watchdog 1,2 */ - 1, /* 20: DS1621 thermal alarm */ - 1, /* 21: Universe II LINT0# */ - 1, /* 22: Universe II LINT1# */ - 1, /* 23: Universe II LINT2# */ - 1, /* 24: Universe II LINT3# */ - 1, /* 25: PMC1 INTA#, PMC2 INTB# */ - 1, /* 26: PMC1 INTB#, PMC2 INTC# */ - 1, /* 27: PMC1 INTC#, PMC2 INTD# */ - 1, /* 28: PMC1 INTD#, PMC2 INTA# */ - 1, /* 29: Enet 2 (front panel) */ - 1, /* 30: Abort Switch */ - 1, /* 31: RTC Alarm */ -}; - -static void __init -mvme5100_setup_arch(void) -{ - if ( ppc_md.progress ) - ppc_md.progress("mvme5100_setup_arch: enter", 0); - - loops_per_jiffy = 50000000 / HZ; - -#ifdef CONFIG_BLK_DEV_INITRD - if (initrd_start) - ROOT_DEV = Root_RAM0; - else -#endif -#ifdef CONFIG_ROOT_NFS - ROOT_DEV = Root_NFS; -#else - ROOT_DEV = Root_SDA2; -#endif - -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif - - if ( ppc_md.progress ) - ppc_md.progress("mvme5100_setup_arch: find_bridges", 0); - - /* Setup PCI host bridge */ - mvme5100_setup_bridge(); - - /* Find and map our OpenPIC */ - pplus_mpic_init(MVME5100_PCI_MEM_OFFSET); - OpenPIC_InitSenses = mvme5100_openpic_initsenses; - OpenPIC_NumInitSenses = sizeof(mvme5100_openpic_initsenses); - - printk("MVME5100 port (C) 2001 MontaVista Software, Inc. (source@mvista.com)\n"); - - if ( ppc_md.progress ) - ppc_md.progress("mvme5100_setup_arch: exit", 0); - - return; -} - -static void __init -mvme5100_init2(void) -{ -#ifdef CONFIG_MVME5100_IPMC761_PRESENT - request_region(0x00,0x20,"dma1"); - request_region(0x20,0x20,"pic1"); - request_region(0x40,0x20,"timer"); - request_region(0x80,0x10,"dma page reg"); - request_region(0xa0,0x20,"pic2"); - request_region(0xc0,0x20,"dma2"); -#endif - return; -} - -/* - * Interrupt setup and service. - * Have MPIC on HAWK and cascaded 8259s on Winbond cascaded to MPIC. - */ -static void __init -mvme5100_init_IRQ(void) -{ -#ifdef CONFIG_MVME5100_IPMC761_PRESENT - int i; -#endif - - if ( ppc_md.progress ) - ppc_md.progress("init_irq: enter", 0); - -#ifdef CONFIG_MVME5100_IPMC761_PRESENT - openpic_init(1, NUM_8259_INTERRUPTS, NULL, -1); - openpic_hookup_cascade(NUM_8259_INTERRUPTS,"82c59 cascade",&i8259_irq); - - for(i=0; i < NUM_8259_INTERRUPTS; i++) - irq_desc[i].handler = &i8259_pic; - - i8259_init(NULL); -#else - openpic_init(1, 0, NULL, -1); -#endif - - if ( ppc_md.progress ) - ppc_md.progress("init_irq: exit", 0); - - return; -} - -/* - * Set BAT 3 to map 0xf0000000 to end of physical memory space. - */ -static __inline__ void -mvme5100_set_bat(void) -{ - unsigned long bat3u, bat3l; - static int mapping_set = 0; - - if (!mapping_set) { - - __asm__ __volatile__( - " lis %0,0xf000\n \ - ori %1,%0,0x002a\n \ - ori %0,%0,0x1ffe\n \ - mtspr 0x21e,%0\n \ - mtspr 0x21f,%1\n \ - isync\n \ - sync " - : "=r" (bat3u), "=r" (bat3l)); - - mapping_set = 1; - } - - return; -} - -static unsigned long __init -mvme5100_find_end_of_memory(void) -{ - mvme5100_set_bat(); - return pplus_get_mem_size(MVME5100_HAWK_SMC_BASE); -} - -static void __init -mvme5100_map_io(void) -{ - io_block_mapping(0xfe000000, 0xfe000000, 0x02000000, _PAGE_IO); - ioremap_base = 0xfe000000; -} - -static void -mvme5100_reset_board(void) -{ - local_irq_disable(); - - /* Set exception prefix high - to the firmware */ - _nmask_and_or_msr(0, MSR_IP); - - out_8((u_char *)MVME5100_BOARD_MODRST_REG, 0x01); - - return; -} - -static void -mvme5100_restart(char *cmd) -{ - volatile ulong i = 10000000; - - mvme5100_reset_board(); - - while (i-- > 0); - panic("restart failed\n"); -} - -static void -mvme5100_halt(void) -{ - local_irq_disable(); - while (1); -} - -static void -mvme5100_power_off(void) -{ - mvme5100_halt(); -} - -static int -mvme5100_show_cpuinfo(struct seq_file *m) -{ - seq_printf(m, "vendor\t\t: Motorola\n"); - seq_printf(m, "machine\t\t: MVME5100\n"); - - return 0; -} - -TODC_ALLOC(); - -void __init -platform_init(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7) -{ - parse_bootinfo(find_bootinfo()); - - isa_io_base = MVME5100_ISA_IO_BASE; - isa_mem_base = MVME5100_ISA_MEM_BASE; - pci_dram_offset = MVME5100_PCI_DRAM_OFFSET; - - ppc_md.setup_arch = mvme5100_setup_arch; - ppc_md.show_cpuinfo = mvme5100_show_cpuinfo; - ppc_md.init_IRQ = mvme5100_init_IRQ; - ppc_md.get_irq = openpic_get_irq; - ppc_md.init = mvme5100_init2; - - ppc_md.restart = mvme5100_restart; - ppc_md.power_off = mvme5100_power_off; - ppc_md.halt = mvme5100_halt; - - ppc_md.find_end_of_memory = mvme5100_find_end_of_memory; - ppc_md.setup_io_mappings = mvme5100_map_io; - - TODC_INIT(TODC_TYPE_MK48T37, - MVME5100_NVRAM_AS0, - MVME5100_NVRAM_AS1, - MVME5100_NVRAM_DATA, - 8); - - ppc_md.time_init = todc_time_init; - ppc_md.set_rtc_time = todc_set_rtc_time; - ppc_md.get_rtc_time = todc_get_rtc_time; - ppc_md.calibrate_decr = todc_calibrate_decr; - - ppc_md.nvram_read_val = todc_m48txx_read_val; - ppc_md.nvram_write_val = todc_m48txx_write_val; - - ppc_md.progress = NULL; -} --- linux-2.6.9-rc1/arch/ppc/platforms/pcore.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/arch/ppc/platforms/pcore.c 2004-09-02 20:51:51.000000000 -0700 @@ -228,10 +228,6 @@ pcore_setup_arch(void) ROOT_DEV = Root_SDA2; #endif -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif - printk(KERN_INFO "Force PowerCore "); if (board_type == PCORE_TYPE_6750) printk("6750\n"); --- linux-2.6.9-rc1/arch/ppc/platforms/pmac_cpufreq.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/ppc/platforms/pmac_cpufreq.c 2004-09-03 00:56:40.994928472 -0700 @@ -24,10 +24,10 @@ #include #include #include +#include #include #include #include -#include #include #include #include --- linux-2.6.9-rc1/arch/ppc/platforms/pmac_setup.c 2004-08-24 00:03:12.000000000 -0700 +++ 25/arch/ppc/platforms/pmac_setup.c 2004-09-02 20:51:51.000000000 -0700 @@ -318,9 +318,6 @@ pmac_setup_arch(void) #ifdef CONFIG_NVRAM pmac_nvram_init(); #endif -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif #ifdef CONFIG_BLK_DEV_INITRD if (initrd_start) ROOT_DEV = Root_RAM0; --- linux-2.6.9-rc1/arch/ppc/platforms/pmac_smp.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/ppc/platforms/pmac_smp.c 2004-09-03 00:56:40.995928320 -0700 @@ -32,13 +32,13 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include #include --- linux-2.6.9-rc1/arch/ppc/platforms/pmac_time.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/ppc/platforms/pmac_time.c 2004-09-03 00:56:40.995928320 -0700 @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -26,7 +27,6 @@ #include #include #include -#include #include #include --- linux-2.6.9-rc1/arch/ppc/platforms/powerpmc250.h 2004-08-24 00:03:14.000000000 -0700 +++ 25/arch/ppc/platforms/powerpmc250.h 2004-09-02 20:51:51.000000000 -0700 @@ -7,7 +7,7 @@ * * Borrowed heavily from prpmc750.h by Matt Porter * - * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under * the terms of the GNU General Public License version 2. This program * is licensed "as is" without any warranty of any kind, whether express * or implied. @@ -16,8 +16,6 @@ #ifndef __ASMPPC_POWERPMC250_H #define __ASMPPC_POWERPMC250_H -#include - #define POWERPMC250_PCI_CONFIG_ADDR 0x80000cf8 #define POWERPMC250_PCI_CONFIG_DATA 0x80000cfc @@ -37,4 +35,18 @@ #define POWERPMC250_SERIAL 0xff000000 #define POWERPMC250_SERIAL_IRQ 20 +/* UART Defines. */ +#define RS_TABLE_SIZE 1 + +#define BASE_BAUD (POWERPMC250_BASE_BAUD / 16) + +#define STD_COM_FLAGS ASYNC_BOOT_AUTOCONF + +#define SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, POWERPMC250_SERIAL, POWERPMC250_SERIAL_IRQ, \ + STD_COM_FLAGS, /* ttyS0 */ \ + iomem_base: (u8 *)POWERPMC250_SERIAL, \ + iomem_reg_shift: 0, \ + io_type: SERIAL_IO_MEM } + #endif /* __ASMPPC_POWERPMC250_H */ --- linux-2.6.9-rc1/arch/ppc/platforms/powerpmc250_serial.h 2004-08-24 00:02:26.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,40 +0,0 @@ -/* - * include/asm-ppc/platforms/powerpmc250_serial.h - * - * Motorola PrPMC750 serial support - * - * Author: Troy Benjegerdes - * - * 2001 (c) MontaVista, Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ - -#ifdef __KERNEL__ -#ifndef __ASMPPC_POWERPMC250_SERIAL_H -#define __ASMPPC_POWERPMC250_SERIAL_H - -#include -#include - -#define RS_TABLE_SIZE 1 - -#define BASE_BAUD (POWERPMC250_BASE_BAUD / 16) - -#ifdef CONFIG_SERIAL_DETECT_IRQ -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) -#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_AUTO_IRQ) -#else -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) -#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF) -#endif - -#define SERIAL_PORT_DFNS \ -{ 0, BASE_BAUD, POWERPMC250_SERIAL, POWERPMC250_SERIAL_IRQ, STD_COM_FLAGS, /* ttyS0 */\ - iomem_base: (u8 *)POWERPMC250_SERIAL, \ - iomem_reg_shift: 0, \ - io_type: SERIAL_IO_MEM } - -#endif -#endif /* __KERNEL__ */ --- linux-2.6.9-rc1/arch/ppc/platforms/pplus.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/ppc/platforms/pplus.c 2004-09-02 20:51:51.000000000 -0700 @@ -583,8 +583,6 @@ static void __init pplus_setup_arch(void vgacon_remap_base = (unsigned long)ioremap(PPLUS_ISA_MEM_BASE, 0x08000000); conswitchp = &vga_con; -#elif defined(CONFIG_DUMMY_CONSOLE) - conswitchp = &dummy_con; #endif #ifdef CONFIG_PPCBUG_NVRAM /* Read in NVRAM data */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/ppc/platforms/pq2ads.c 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,26 @@ +/* + * arch/ppc/platforms/pq2ads.c + * + * PQ2ADS platform support + * + * Author: Kumar Gala + * Derived from: est8260_setup.c by Allen Curtis + * + * Copyright 2004 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include + +#include + +void __init +m82xx_board_init(void) +{ + /* Enable the 2nd UART port */ + *(volatile uint *)(BCSR_ADDR + 4) &= ~BCSR1_RS232_EN2; +} --- linux-2.6.9-rc1/arch/ppc/platforms/pq2ads.h 2004-08-24 00:02:48.000000000 -0700 +++ 25/arch/ppc/platforms/pq2ads.h 2004-09-02 20:51:51.000000000 -0700 @@ -23,6 +23,10 @@ #define BOOTROM_RESTART_ADDR ((uint)0xff000104) +/* For our show_cpuinfo hooks. */ +#define CPUINFO_VENDOR "Motorola" +#define CPUINFO_MACHINE "PQ2 ADS PowerPC" + /* The ADS8260 has 16, 32-bit wide control/status registers, accessed * only on word boundaries. * Not all are used (yet), or are interesting to us (yet). --- linux-2.6.9-rc1/arch/ppc/platforms/pq2ads_setup.c 2004-08-24 00:03:49.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,66 +0,0 @@ -/* - * arch/ppc/platforms/pq2ads_setup.c - * - * PQ2ADS platform support - * - * Author: Kumar Gala - * Derived from: est8260_setup.c by Allen Curtis - * - * Copyright 2004 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include - -#include -#include - -static void (*callback_setup_arch)(void); - -extern unsigned char __res[sizeof(bd_t)]; - -extern void m8260_init(unsigned long r3, unsigned long r4, - unsigned long r5, unsigned long r6, unsigned long r7); - -static int -pq2ads_show_cpuinfo(struct seq_file *m) -{ - bd_t *binfo = (bd_t *)__res; - - seq_printf(m, "vendor\t\t: Motorola\n" - "machine\t\t: PQ2 ADS PowerPC\n" - "\n" - "mem size\t\t: 0x%08lx\n" - "console baud\t\t: %ld\n" - "\n", - binfo->bi_memsize, - binfo->bi_baudrate); - return 0; -} - -static void __init -pq2ads_setup_arch(void) -{ - printk("PQ2 ADS Port\n"); - callback_setup_arch(); - *(volatile uint *)(BCSR_ADDR + 4) &= ~BCSR1_RS232_EN2; -} - -void __init -platform_init(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7) -{ - /* Generic 8260 platform initialization */ - m8260_init(r3, r4, r5, r6, r7); - - /* Anything special for this platform */ - ppc_md.show_cpuinfo = pq2ads_show_cpuinfo; - - callback_setup_arch = ppc_md.setup_arch; - ppc_md.setup_arch = pq2ads_setup_arch; -} --- linux-2.6.9-rc1/arch/ppc/platforms/prep_pci.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/ppc/platforms/prep_pci.c 2004-09-02 20:51:51.000000000 -0700 @@ -836,52 +836,59 @@ struct mot_info { void __init ibm_prep_init(void) { -#ifdef CONFIG_PREP_RESIDUAL - u32 addr, real_addr, len; - PPC_DEVICE *mpic; - PnP_TAG_PACKET *pkt; - - /* Use the PReP residual data to determine if an OpenPIC is - * present. If so, get the large vendor packet which will - * tell us the base address and length in memory. - * If we are successful, ioremap the memory area and set - * OpenPIC_Addr (this indicates that the OpenPIC was found). - */ - mpic = residual_find_device(-1, NULL, SystemPeripheral, - ProgrammableInterruptController, MPIC, 0); - if (!mpic) - return; + if (have_residual_data) { + u32 addr, real_addr, len, offset; + PPC_DEVICE *mpic; + PnP_TAG_PACKET *pkt; + + /* Use the PReP residual data to determine if an OpenPIC is + * present. If so, get the large vendor packet which will + * tell us the base address and length in memory. + * If we are successful, ioremap the memory area and set + * OpenPIC_Addr (this indicates that the OpenPIC was found). + */ + mpic = residual_find_device(-1, NULL, SystemPeripheral, + ProgrammableInterruptController, MPIC, 0); + if (!mpic) + return; - pkt = PnP_find_large_vendor_packet(res->DevicePnPHeap + - mpic->AllocatedOffset, 9, 0); + pkt = PnP_find_large_vendor_packet(res->DevicePnPHeap + + mpic->AllocatedOffset, 9, 0); - if (!pkt) - return; + if (!pkt) + return; #define p pkt->L4_Pack.L4_Data.L4_PPCPack - if (!((p.PPCData[0] == 2) && (p.PPCData[1] == 32))) - return; /* not a 32-bit memory address */ - - real_addr = ld_le32((unsigned int *) (p.PPCData + 4)); - if (real_addr == 0xffffffff) - return; - - /* Adjust address to be as seen by CPU */ - addr = real_addr + PREP_ISA_MEM_BASE; + if (p.PPCData[1] == 32) { + switch (p.PPCData[0]) { + case 1: offset = PREP_ISA_IO_BASE; break; + case 2: offset = PREP_ISA_MEM_BASE; break; + default: return; /* Not I/O or memory?? */ + } + } + else + return; /* Not a 32-bit address */ - len = ld_le32((unsigned int *) (p.PPCData + 12)); - if (!len) - return; + real_addr = ld_le32((unsigned int *) (p.PPCData + 4)); + if (real_addr == 0xffffffff) + return; + + /* Adjust address to be as seen by CPU */ + addr = real_addr + offset; + + len = ld_le32((unsigned int *) (p.PPCData + 12)); + if (!len) + return; #undef p - OpenPIC_Addr = ioremap(addr, len); - ppc_md.get_irq = openpic_get_irq; + OpenPIC_Addr = ioremap(addr, len); + ppc_md.get_irq = openpic_get_irq; - OpenPIC_InitSenses = prep_openpic_initsenses; - OpenPIC_NumInitSenses = sizeof(prep_openpic_initsenses); + OpenPIC_InitSenses = prep_openpic_initsenses; + OpenPIC_NumInitSenses = sizeof(prep_openpic_initsenses); - printk(KERN_INFO "MPIC at 0x%08x (0x%08x), length 0x%08x " - "mapped to 0x%p\n", addr, real_addr, len, OpenPIC_Addr); -#endif + printk(KERN_INFO "MPIC at 0x%08x (0x%08x), length 0x%08x " + "mapped to 0x%p\n", addr, real_addr, len, OpenPIC_Addr); + } } static void __init @@ -901,6 +908,17 @@ ibm43p_pci_map_non0(struct pci_dev *dev) } void __init +prep_residual_setup_pci(char *irq_edge_mask_lo, char *irq_edge_mask_hi) +{ + if (have_residual_data) { + Motherboard_map_name = res->VitalProductData.PrintableModel; + Motherboard_map = NULL; + Motherboard_routes = NULL; + residual_irq_mask(irq_edge_mask_lo, irq_edge_mask_hi); + } +} + +void __init prep_sandalfoot_setup_pci(char *irq_edge_mask_lo, char *irq_edge_mask_hi) { Motherboard_map_name = "IBM 6015/7020 (Sandalfoot/Sandalbow)"; @@ -1011,21 +1029,31 @@ prep_route_pci_interrupts(void) } } else if ( _prep_type == _PREP_IBM ) { unsigned char irq_edge_mask_lo, irq_edge_mask_hi; + unsigned short irq_edge_mask; + int i; setup_ibm_pci(&irq_edge_mask_lo, &irq_edge_mask_hi); outb(inb(0x04d0)|irq_edge_mask_lo, 0x4d0); /* primary 8259 */ outb(inb(0x04d1)|irq_edge_mask_hi, 0x4d1); /* cascaded 8259 */ + + irq_edge_mask = (irq_edge_mask_hi << 8) | irq_edge_mask_lo; + for (i = 0; i < 16; ++i, irq_edge_mask >>= 1) + if (irq_edge_mask & 1) + irq_desc[i].status |= IRQ_LEVEL; } else { printk("No known machine pci routing!\n"); return; } /* Set up mapping from slots */ - for (i = 1; i <= 4; i++) - ibc_pirq[i-1] = Motherboard_routes[i]; - /* Enable PCI interrupts */ - *ibc_pcicon |= 0x20; + if (Motherboard_routes) { + for (i = 1; i <= 4; i++) + ibc_pirq[i-1] = Motherboard_routes[i]; + + /* Enable PCI interrupts */ + *ibc_pcicon |= 0x20; + } } void __init @@ -1171,38 +1199,52 @@ void __init prep_pcibios_fixup(void) { struct pci_dev *dev = NULL; + int irq; + int have_openpic = (OpenPIC_Addr != NULL); prep_route_pci_interrupts(); printk("Setting PCI interrupts for a \"%s\"\n", Motherboard_map_name); - if (OpenPIC_Addr) { - /* PCI interrupts are controlled by the OpenPIC */ - while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { - if (dev->bus->number == 0) { - dev->irq = openpic_to_irq(Motherboard_map[PCI_SLOT(dev->devfn)]); - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); - } else { - if (Motherboard_non0 != NULL) - Motherboard_non0(dev); - } - } - - /* Setup the Winbond or Via PIB */ - prep_pib_init(); - - return; - } - dev = NULL; + /* Iterate through all the PCI devices, setting the IRQ */ while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { /* - * Use our old hard-coded kludge to figure out what - * irq this device uses. This is necessary on things - * without residual data. -- Cort + * If we have residual data, then this is easy: query the + * residual data for the IRQ line allocated to the device. + * This works the same whether we have an OpenPic or not. + */ + if (have_residual_data) { + irq = residual_pcidev_irq(dev); + dev->irq = have_openpic ? openpic_to_irq(irq) : irq; + } + /* + * If we don't have residual data, then we need to use + * tables to determine the IRQ. The table organisation + * is different depending on whether there is an OpenPIC + * or not. The tables are only used for bus 0, so check + * this first. */ - unsigned char d = PCI_SLOT(dev->devfn); - dev->irq = Motherboard_routes[Motherboard_map[d]]; + else if (dev->bus->number == 0) { + irq = Motherboard_map[PCI_SLOT(dev->devfn)]; + dev->irq = have_openpic ? openpic_to_irq(irq) + : Motherboard_routes[irq]; + } + /* + * Finally, if we don't have residual data and the bus is + * non-zero, use the callback (if provided) + */ + else { + if (Motherboard_non0 != NULL) + Motherboard_non0(dev); + + continue; + } + + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); } + + /* Setup the Winbond or Via PIB */ + prep_pib_init(); } static void __init @@ -1262,14 +1304,15 @@ prep_find_bridges(void) PREP_ISA_IO_BASE + 0xcfc); printk("PReP architecture\n"); -#ifdef CONFIG_PREP_RESIDUAL - { + + if (have_residual_data) { PPC_DEVICE *hostbridge; hostbridge = residual_find_device(PROCESSORDEVICE, NULL, BridgeController, PCIBridge, -1, 0); if (hostbridge && - hostbridge->DeviceId.Interface == PCIBridgeIndirect) { + ((hostbridge->DeviceId.Interface == PCIBridgeIndirect) || + (hostbridge->DeviceId.Interface == PCIBridgeRS6K))) { PnP_TAG_PACKET * pkt; pkt = PnP_find_large_vendor_packet( res->DevicePnPHeap+hostbridge->AllocatedOffset, @@ -1284,7 +1327,6 @@ prep_find_bridges(void) setup_indirect_pci(hose, 0x80000cf8, 0x80000cfc); } } -#endif /* CONFIG_PREP_RESIDUAL */ ppc_md.pcibios_fixup = prep_pcibios_fixup; ppc_md.pcibios_after_init = prep_pcibios_after_init; --- linux-2.6.9-rc1/arch/ppc/platforms/prep_setup.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/ppc/platforms/prep_setup.c 2004-09-02 20:51:51.000000000 -0700 @@ -79,6 +79,7 @@ extern void prep_find_bridges(void); int _prep_type; +extern void prep_residual_setup_pci(char *irq_edge_mask_lo, char *irq_edge_mask_hi); extern void prep_sandalfoot_setup_pci(char *irq_edge_mask_lo, char *irq_edge_mask_hi); extern void prep_thinkpad_setup_pci(char *irq_edge_mask_lo, char *irq_edge_mask_hi); extern void prep_carolina_setup_pci(char *irq_edge_mask_lo, char *irq_edge_mask_hi); @@ -193,9 +194,8 @@ prep_ibm_cpuinfo(struct seq_file *m) seq_printf(m, "bad"); seq_printf(m, "\n"); -#ifdef CONFIG_PREP_RESIDUAL /* print info about SIMMs */ - if (res->ResidualLength != 0) { + if (have_residual_data) { int i; seq_printf(m, "simms\t\t: "); for (i = 0; (res->ActualNumMemories) && (i < MAX_MEMS); i++) { @@ -207,7 +207,13 @@ prep_ibm_cpuinfo(struct seq_file *m) } seq_printf(m, "\n"); } -#endif +} + +static int __prep +prep_gen_cpuinfo(struct seq_file *m) +{ + prep_ibm_cpuinfo(m); + return 0; } static int __prep @@ -431,9 +437,8 @@ prep_mot_cpuinfo(struct seq_file *m) } no_l2: -#ifdef CONFIG_PREP_RESIDUAL /* print info about SIMMs */ - if (res->ResidualLength != 0) { + if (have_residual_data) { int i; seq_printf(m, "simms\t\t: "); for (i = 0; (res->ActualNumMemories) && (i < MAX_MEMS); i++) { @@ -445,18 +450,11 @@ no_l2: } seq_printf(m, "\n"); } -#endif return 0; } static void __prep -prep_tiger1_progress(char *msg, unsigned short code) -{ - outw(code, PREP_IBM_DISP); -} - -static void __prep prep_restart(char *cmd) { #define PREP_SP92 0x92 /* Special Port 92 */ @@ -561,14 +559,12 @@ prep_show_percpuinfo(struct seq_file *m, { /* PREP's without residual data will give incorrect values here */ seq_printf(m, "clock\t\t: "); -#ifdef CONFIG_PREP_RESIDUAL - if (res->ResidualLength) + if (have_residual_data) seq_printf(m, "%ldMHz\n", (res->VitalProductData.ProcessorHz > 1024) ? res->VitalProductData.ProcessorHz / 1000000 : res->VitalProductData.ProcessorHz); else -#endif /* CONFIG_PREP_RESIDUAL */ seq_printf(m, "???\n"); return 0; @@ -598,9 +594,10 @@ static void __init prep_init_sound(void) * Get the needed resource informations from residual data. * */ -#ifdef CONFIG_PREP_RESIDUAL - audiodevice = residual_find_device(~0, NULL, MultimediaController, - AudioController, -1, 0); + if (have_residual_data) + audiodevice = residual_find_device(~0, NULL, + MultimediaController, AudioController, -1, 0); + if (audiodevice != NULL) { PnP_TAG_PACKET *pkt; @@ -613,7 +610,6 @@ static void __init prep_init_sound(void) if (pkt != NULL) ppc_cs4232_dma2 = masktoint(pkt->S5_Pack.DMAMask); } -#endif /* * These are the PReP specs' defaults for the cs4231. We use these @@ -649,13 +645,14 @@ static void __init prep_init_sound(void) static void __init prep_init_vesa(void) { -#if defined(CONFIG_PREP_RESIDUAL) && \ - (defined(CONFIG_FB_VGA16) || defined(CONFIG_FB_VGA_16_MODULE) || \ +#if (defined(CONFIG_FB_VGA16) || defined(CONFIG_FB_VGA_16_MODULE) || \ defined(CONFIG_FB_VESA)) - PPC_DEVICE *vgadev; + PPC_DEVICE *vgadev = NULL; + + if (have_residual_data) + vgadev = residual_find_device(~0, NULL, DisplayController, + SVGAController, -1, 0); - vgadev = residual_find_device(~0, NULL, DisplayController, SVGAController, - -1, 0); if (vgadev != NULL) { PnP_TAG_PACKET *pkt; @@ -680,7 +677,112 @@ prep_init_vesa(void) } } } -#endif /* CONFIG_PREP_RESIDUAL */ +#endif +} + +/* + * Set DBAT 2 to access 0x80000000 so early progress messages will work + */ +static __inline__ void +prep_set_bat(void) +{ + /* wait for all outstanding memory access to complete */ + mb(); + + /* setup DBATs */ + mtspr(DBAT2U, 0x80001ffe); + mtspr(DBAT2L, 0x8000002a); + + /* wait for updates */ + mb(); +} + +/* + * IBM 3-digit status LED + */ +static unsigned int ibm_statusled_base __prepdata; + +static void __prep +ibm_statusled_progress(char *s, unsigned short hex); + +static int __prep +ibm_statusled_panic(struct notifier_block *dummy1, unsigned long dummy2, + void * dummy3) +{ + ibm_statusled_progress(NULL, 0x505); /* SOS */ + return NOTIFY_DONE; +} + +static struct notifier_block ibm_statusled_block __prepdata = { + ibm_statusled_panic, + NULL, + INT_MAX /* try to do it first */ +}; + +static void __prep +ibm_statusled_progress(char *s, unsigned short hex) +{ + static int notifier_installed; + /* + * Progress uses 4 digits and we have only 3. So, we map 0xffff to + * 0xfff for display switch off. Out of range values are mapped to + * 0xeff, as I'm told 0xf00 and above are reserved for hardware codes. + * Install the panic notifier when the display is first switched off. + */ + if (hex == 0xffff) { + hex = 0xfff; + if (!notifier_installed) { + ++notifier_installed; + notifier_chain_register(&panic_notifier_list, + &ibm_statusled_block); + } + } + else + if (hex > 0xfff) + hex = 0xeff; + + mb(); + outw(hex, ibm_statusled_base); +} + +static void __init +ibm_statusled_init(void) +{ + /* + * The IBM 3-digit LED display is specified in the residual data + * as an operator panel device, type "System Status LED". Find + * that device and determine its address. We validate all the + * other parameters on the off-chance another, similar device + * exists. + */ + if (have_residual_data) { + PPC_DEVICE *led; + PnP_TAG_PACKET *pkt; + + led = residual_find_device(~0, NULL, SystemPeripheral, + OperatorPanel, SystemStatusLED, 0); + if (!led) + return; + + pkt = PnP_find_packet((unsigned char *) + &res->DevicePnPHeap[led->AllocatedOffset], S8_Packet, 0); + if (!pkt) + return; + + if (pkt->S8_Pack.IOInfo != ISAAddr16bit) + return; + if (*(unsigned short *)pkt->S8_Pack.RangeMin != + *(unsigned short *)pkt->S8_Pack.RangeMax) + return; + if (pkt->S8_Pack.IOAlign != 2) + return; + if (pkt->S8_Pack.IONum != 2) + return; + + ibm_statusled_base = ld_le16((unsigned short *) + (pkt->S8_Pack.RangeMin)); + ppc_md.progress = ibm_statusled_progress; + } } static void __init @@ -706,7 +808,7 @@ prep_setup_arch(void) { case _PREP_IBM: reg = inb(PREP_IBM_PLANAR); - printk(KERN_INFO "IBM planar ID: %08x", reg); + printk(KERN_INFO "IBM planar ID: %02x", reg); switch (reg) { case PREP_IBM_SANDALFOOT: prep_gen_enable_l2(); @@ -721,7 +823,16 @@ prep_setup_arch(void) ppc_md.show_cpuinfo = prep_thinkpad_cpuinfo; break; default: - printk(" -- unknown! Assuming Carolina"); + if (have_residual_data) { + prep_gen_enable_l2(); + setup_ibm_pci = prep_residual_setup_pci; + ppc_md.power_off = prep_halt; + ppc_md.show_cpuinfo = prep_gen_cpuinfo; + break; + } + else + printk(" - unknown! Assuming Carolina"); + /* fall through */ case PREP_IBM_CAROLINA_IDE_0: case PREP_IBM_CAROLINA_IDE_1: case PREP_IBM_CAROLINA_IDE_2: @@ -745,7 +856,6 @@ prep_setup_arch(void) setup_ibm_pci = prep_tiger1_setup_pci; ppc_md.power_off = prep_sig750_poweroff; ppc_md.show_cpuinfo = prep_tiger1_cpuinfo; - ppc_md.progress = prep_tiger1_progress; break; } printk("\n"); @@ -808,8 +918,6 @@ prep_setup_arch(void) /* vgacon.c needs to know where we mapped IO memory in io_block_mapping() */ vgacon_remap_base = 0xf0000000; conswitchp = &vga_con; -#elif defined(CONFIG_DUMMY_CONSOLE) - conswitchp = &dummy_con; #endif } @@ -821,18 +929,19 @@ prep_setup_arch(void) static void __init prep_calibrate_decr(void) { -#ifdef CONFIG_PREP_RESIDUAL - unsigned long freq, divisor = 4; + if (have_residual_data) { + unsigned long freq, divisor = 4; - if ( res->VitalProductData.ProcessorBusHz ) { - freq = res->VitalProductData.ProcessorBusHz; - printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", - (freq/divisor)/1000000, - (freq/divisor)%1000000); - tb_to_us = mulhwu_scale_factor(freq/divisor, 1000000); - tb_ticks_per_jiffy = freq / HZ / divisor; - } else -#endif + if ( res->VitalProductData.ProcessorBusHz ) { + freq = res->VitalProductData.ProcessorBusHz; + printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", + (freq/divisor)/1000000, + (freq/divisor)%1000000); + tb_to_us = mulhwu_scale_factor(freq/divisor, 1000000); + tb_ticks_per_jiffy = freq / HZ / divisor; + } + } + else todc_calibrate_decr(); } @@ -863,6 +972,12 @@ prep_init_IRQ(void) } for ( i = 0 ; i < NUM_8259_INTERRUPTS ; i++ ) irq_desc[i].handler = &i8259_pic; + + if (have_residual_data) { + i8259_init(residual_isapic_addr()); + return; + } + /* If we have a Raven PCI bridge or a Hawk PCI bridge / Memory * controller, we poll (as they have a different int-ack address). */ early_read_config_dword(NULL, 0, 0, PCI_VENDOR_ID, &pci_viddid); @@ -1000,18 +1115,27 @@ prep_init(unsigned long r3, unsigned lon DMA_MODE_WRITE = 0x48; /* figure out what kind of prep workstation we are */ -#ifdef CONFIG_PREP_RESIDUAL - if ( res->ResidualLength != 0 ) { + if (have_residual_data) { if ( !strncmp(res->VitalProductData.PrintableModel,"IBM",3) ) _prep_type = _PREP_IBM; else _prep_type = _PREP_Motorola; - } else /* assume motorola if no residual (netboot?) */ -#endif - { + } + else { + /* assume motorola if no residual (netboot?) */ _prep_type = _PREP_Motorola; } +#ifdef CONFIG_PREP_PRESIDUAL + /* Switch off all residual data processing if the user requests it */ + if (strstr(cmd_line, "noresidual") != NULL) + res = NULL; +#endif + + /* Initialise progress early to get maximum benefit */ + prep_set_bat(); + ibm_statusled_init(); + ppc_md.setup_arch = prep_setup_arch; ppc_md.show_percpuinfo = prep_show_percpuinfo; ppc_md.show_cpuinfo = NULL; /* set in prep_setup_arch() */ --- linux-2.6.9-rc1/arch/ppc/platforms/prpmc750.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/ppc/platforms/prpmc750.c 2004-09-02 20:51:51.000000000 -0700 @@ -193,10 +193,6 @@ static void __init prpmc750_setup_arch(v ROOT_DEV = Root_SDA2; #endif -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif - OpenPIC_InitSenses = prpmc750_openpic_initsenses; OpenPIC_NumInitSenses = sizeof(prpmc750_openpic_initsenses); --- linux-2.6.9-rc1/arch/ppc/platforms/prpmc750.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/ppc/platforms/prpmc750.h 2004-09-02 20:51:51.000000000 -0700 @@ -5,20 +5,16 @@ * * Author: Matt Porter * - * Copyright 2001 MontaVista Software Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. + * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. */ #ifdef __KERNEL__ #ifndef __ASM_PRPMC750_H__ #define __ASM_PRPMC750_H__ -#include - /* * Due to limiations imposed by legacy hardware (primaryily IDE controllers), * the PrPMC750 carrier board operates using a PReP address map. @@ -81,5 +77,19 @@ #define PRPMC750_TBEN_REG 0xfef880c0 #define PRPMC750_TBEN_MASK 0x01 +/* UART Defines. */ +#define RS_TABLE_SIZE 4 + +/* Rate for the 1.8432 Mhz clock for the onboard serial chip */ +#define BASE_BAUD (PRPMC750_BASE_BAUD / 16) + +#define STD_COM_FLAGS ASYNC_BOOT_AUTOCONF + +#define SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, PRPMC750_SERIAL_0, 1, STD_COM_FLAGS, \ + iomem_base: (unsigned char *)PRPMC750_SERIAL_0, \ + iomem_reg_shift: 4, \ + io_type: SERIAL_IO_MEM } /* ttyS0 */ + #endif /* __ASM_PRPMC750_H__ */ #endif /* __KERNEL__ */ --- linux-2.6.9-rc1/arch/ppc/platforms/prpmc750_serial.h 2004-08-24 00:02:24.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,43 +0,0 @@ -/* - * include/asm-ppc/platforms/prpmc750_serial.h - * - * Motorola PrPMC750 serial support - * - * Author: Matt Porter - * - * 2001 (c) MontaVista, Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ - -#ifdef __KERNEL__ -#ifndef __ASM_PRPMC750_SERIAL_H__ -#define __ASM_PRPMC750_SERIAL_H__ - -#include -#include - -#define RS_TABLE_SIZE 4 - -/* Rate for the 1.8432 Mhz clock for the onboard serial chip */ -#define BASE_BAUD (PRPMC750_BASE_BAUD / 16) - -#ifndef SERIAL_MAGIC_KEY -#define kernel_debugger ppc_kernel_debug -#endif - -#ifdef CONFIG_SERIAL_DETECT_IRQ -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) -#else -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) -#endif - -#define SERIAL_PORT_DFNS \ - { 0, BASE_BAUD, PRPMC750_SERIAL_0, 1, STD_COM_FLAGS, \ - iomem_base: (unsigned char *)PRPMC750_SERIAL_0, \ - iomem_reg_shift: 4, \ - io_type: SERIAL_IO_MEM } /* ttyS0 */ - -#endif /* __ASM_PRPMC750_SERIAL_H__ */ -#endif /* __KERNEL__ */ --- linux-2.6.9-rc1/arch/ppc/platforms/prpmc800.c 2004-08-24 00:03:14.000000000 -0700 +++ 25/arch/ppc/platforms/prpmc800.c 2004-09-02 20:51:51.000000000 -0700 @@ -309,10 +309,6 @@ static void __init prpmc800_setup_arch(v ROOT_DEV = Root_SDA2; #endif -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif - printk(KERN_INFO "Port by MontaVista Software, Inc. " "(source@mvista.com)\n"); } --- linux-2.6.9-rc1/arch/ppc/platforms/prpmc800.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/ppc/platforms/prpmc800.h 2004-09-02 20:51:51.000000000 -0700 @@ -5,12 +5,10 @@ * * Author: Dale Farnsworth * - * Copyright 2001 MontaVista Software Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. + * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. */ /* * From Processor to PCI: @@ -63,4 +61,22 @@ #define PRPMC800_INT_IRQ 16 #define PRPMC800_INT_PRI 15 +/* UART Defines. */ +#define RS_TABLE_SIZE 4 + +/* Rate for the 1.8432 Mhz clock for the onboard serial chip */ +#define BASE_BAUD (PRPMC800_BASE_BAUD / 16) + +#define STD_COM_FLAGS ASYNC_BOOT_AUTOCONF + +/* UARTS are at IRQ 16 */ +#define STD_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, PRPMC800_SERIAL_1, 16, STD_COM_FLAGS, /* ttyS0 */\ + iomem_base: (unsigned char *)PRPMC800_SERIAL_1, \ + iomem_reg_shift: 0, \ + io_type: SERIAL_IO_MEM }, + +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DFNS + #endif /* __ASMPPC_PRPMC800_H */ --- linux-2.6.9-rc1/arch/ppc/platforms/prpmc800_serial.h 2004-08-24 00:03:31.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,49 +0,0 @@ -/* - * arch/ppc/platforms/prpmc800_serial.h - * - * Definitions for Motorola MCG PRPMC800 cPCI board support - * - * Author: Dale Farnsworth dale.farnsworth@mvista.com - * - * 2001 (c) MontaVista, Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ - -#ifndef __ASMPPC_PRPMC800_SERIAL_H -#define __ASMPPC_PRPMC800_SERIAL_H - -#include -#include - -#ifdef CONFIG_SERIAL_MANY_PORTS -#define RS_TABLE_SIZE 64 -#else -#define RS_TABLE_SIZE 4 -#endif - -/* Rate for the 1.8432 Mhz clock for the onboard serial chip */ -#define BASE_BAUD (PRPMC800_BASE_BAUD / 16) - -#ifndef SERIAL_MAGIC_KEY -#define kernel_debugger ppc_kernel_debug -#endif - -#ifdef CONFIG_SERIAL_DETECT_IRQ -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ) -#else -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST) -#endif - -/* UARTS are at IRQ 16 */ -#define STD_SERIAL_PORT_DFNS \ - { 0, BASE_BAUD, PRPMC800_SERIAL_1, 16, STD_COM_FLAGS, /* ttyS0 */\ - iomem_base: (unsigned char *)PRPMC800_SERIAL_1, \ - iomem_reg_shift: 0, \ - io_type: SERIAL_IO_MEM }, - -#define SERIAL_PORT_DFNS \ - STD_SERIAL_PORT_DFNS - -#endif /* __ASMPPC_PRPMC800_SERIAL_H */ --- linux-2.6.9-rc1/arch/ppc/platforms/residual.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/ppc/platforms/residual.c 2004-09-02 20:51:51.000000000 -0700 @@ -504,7 +504,7 @@ void __init print_residual_device_info(v #define did dev->DeviceId /* make sure we have residual data first */ - if ( res->ResidualLength == 0 ) + if (!have_residual_data) return; printk("Residual: %ld devices\n", res->ActualNumDevices); @@ -639,7 +639,7 @@ void print_residual_device_info(void) #define did dev->DeviceId /* make sure we have residual data first */ - if ( res->ResidualLength == 0 ) + if (!have_residual_data) return; printk("Residual: %ld devices\n", res->ActualNumDevices); for ( i = 0; @@ -790,7 +790,7 @@ PPC_DEVICE __init *residual_find_device( int n) { int i; - if ( !res->ResidualLength ) return NULL; + if (!have_residual_data) return NULL; for (i=0; iActualNumDevices; i++) { #define Dev res->Devices[i].DeviceId if ( (Dev.BusId&BusMask) && @@ -813,7 +813,7 @@ PPC_DEVICE __init *residual_find_device_ int n) { int i; - if ( !res->ResidualLength ) return NULL; + if (!have_residual_data) return NULL; for (i=0; iActualNumDevices; i++) { #define Dev res->Devices[i].DeviceId if ( (Dev.BusId&BusMask) && @@ -827,6 +827,129 @@ PPC_DEVICE __init *residual_find_device_ return NULL; } +static int __init +residual_scan_pcibridge(PnP_TAG_PACKET * pkt, struct pci_dev *dev) +{ + int irq = -1; + +#define data pkt->L4_Pack.L4_Data.L4_PPCPack.PPCData + if (dev->bus->number == data[16]) { + int i, size; + + size = 3 + ld_le16((u_short *) (&pkt->L4_Pack.Count0)); + for (i = 20; i < size - 4; i += 12) { + unsigned char pin; + int line_irq; + + if (dev->devfn != data[i + 1]) + continue; + + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (pin) { + line_irq = ld_le16((unsigned short *) + (&data[i + 4 + 2 * (pin - 1)])); + irq = (line_irq == 0xffff) ? 0 + : line_irq & 0x7fff; + } else + irq = 0; + + break; + } + } +#undef data + + return irq; +} + +int __init +residual_pcidev_irq(struct pci_dev *dev) +{ + int i = 0; + int irq = -1; + PPC_DEVICE *bridge; + + while ((bridge = residual_find_device + (-1, NULL, BridgeController, PCIBridge, -1, i++))) { + + PnP_TAG_PACKET *pkt; + if (bridge->AllocatedOffset) { + pkt = PnP_find_large_vendor_packet(res->DevicePnPHeap + + bridge->AllocatedOffset, 3, 0); + if (!pkt) + continue; + + irq = residual_scan_pcibridge(pkt, dev); + if (irq != -1) + break; + } + } + + return (irq < 0) ? 0 : irq; +} + +void __init residual_irq_mask(char *irq_edge_mask_lo, char *irq_edge_mask_hi) +{ + PPC_DEVICE *dev; + int i = 0; + unsigned short irq_mask = 0x000; /* default to edge */ + + while ((dev = residual_find_device(-1, NULL, -1, -1, -1, i++))) { + PnP_TAG_PACKET *pkt; + unsigned short mask; + int size; + int offset = dev->AllocatedOffset; + + if (!offset) + continue; + + pkt = PnP_find_packet(res->DevicePnPHeap + offset, + IRQFormat, 0); + if (!pkt) + continue; + + size = tag_small_count(pkt->S1_Pack.Tag) + 1; + mask = ld_le16((unsigned short *)pkt->S4_Pack.IRQMask); + if (size > 3 && (pkt->S4_Pack.IRQInfo & 0x0c)) + irq_mask |= mask; + } + + *irq_edge_mask_lo = irq_mask & 0xff; + *irq_edge_mask_hi = irq_mask >> 8; +} + +unsigned int __init residual_isapic_addr(void) +{ + PPC_DEVICE *isapic; + PnP_TAG_PACKET *pkt; + unsigned int addr; + + isapic = residual_find_device(~0, NULL, SystemPeripheral, + ProgrammableInterruptController, + ISA_PIC, 0); + if (!isapic) + goto unknown; + + pkt = PnP_find_large_vendor_packet(res->DevicePnPHeap + + isapic->AllocatedOffset, 9, 0); + if (!pkt) + goto unknown; + +#define p pkt->L4_Pack.L4_Data.L4_PPCPack + /* Must be 32-bit memory address */ + if (!((p.PPCData[0] == 2) && (p.PPCData[1] == 32))) + goto unknown; + + /* It doesn't seem to work where length != 1 (what can I say? :-/ ) */ + if (ld_le32((unsigned int *)(p.PPCData + 12)) != 1) + goto unknown; + + addr = ld_le32((unsigned int *) (p.PPCData + 4)); +#undef p + return addr; +unknown: + return 0; +} + PnP_TAG_PACKET *PnP_find_packet(unsigned char *p, unsigned packet_tag, int n) @@ -901,7 +1024,7 @@ static int proc_prep_residual_read(char int __init proc_prep_residual_init(void) { - if (res->ResidualLength) + if (have_residual_data) create_proc_read_entry("residual", S_IRUGO, NULL, proc_prep_residual_read, NULL); return 0; --- linux-2.6.9-rc1/arch/ppc/platforms/rpx8260.c 2004-08-24 00:02:47.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,65 +0,0 @@ -/* - * arch/ppc/platforms/rpx8260.c - * - * RPC EP8260 platform support - * - * Author: Dan Malek - * Derived from: pq2ads_setup.c by Kumar - * - * Copyright 2004 Embedded Edge, LLC - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include - -#include -#include - -static void (*callback_setup_arch)(void); - -extern unsigned char __res[sizeof(bd_t)]; - -extern void m8260_init(unsigned long r3, unsigned long r4, - unsigned long r5, unsigned long r6, unsigned long r7); - -static int -ep8260_show_cpuinfo(struct seq_file *m) -{ - bd_t *binfo = (bd_t *)__res; - - seq_printf(m, "vendor\t\t: RPC\n" - "machine\t\t: EP8260 PPC\n" - "\n" - "mem size\t\t: 0x%08x\n" - "console baud\t\t: %d\n" - "\n", - binfo->bi_memsize, - binfo->bi_baudrate); - return 0; -} - -static void __init -ep8260_setup_arch(void) -{ - printk("RPC EP8260 Port\n"); - callback_setup_arch(); -} - -void __init -platform_init(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7) -{ - /* Generic 8260 platform initialization */ - m8260_init(r3, r4, r5, r6, r7); - - /* Anything special for this platform */ - ppc_md.show_cpuinfo = ep8260_show_cpuinfo; - - callback_setup_arch = ppc_md.setup_arch; - ppc_md.setup_arch = ep8260_setup_arch; -} --- linux-2.6.9-rc1/arch/ppc/platforms/rpx8260.h 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/ppc/platforms/rpx8260.h 2004-09-02 20:51:51.000000000 -0700 @@ -70,5 +70,12 @@ extern volatile u_char *rpx6_csr_addr; #define PHY_INTERRUPT SIU_INT_IRQ7 +/* For our show_cpuinfo hooks. */ +#define CPUINFO_VENDOR "Embedded Planet" +#define CPUINFO_MACHINE "EP8260 PowerPC" + +/* Warm reset vector. */ +#define BOOTROM_RESTART_ADDR ((uint)0xfff00104) + #endif /* __ASM_PLATFORMS_RPX8260_H__ */ #endif /* __KERNEL__ */ --- linux-2.6.9-rc1/arch/ppc/platforms/sandpoint.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/ppc/platforms/sandpoint.c 2004-09-02 20:51:51.000000000 -0700 @@ -303,10 +303,6 @@ sandpoint_setup_arch(void) /* Lookup PCI host bridges */ sandpoint_find_bridges(); -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif - printk(KERN_INFO "Motorola SPS Sandpoint Test Platform\n"); printk(KERN_INFO "Port by MontaVista Software, Inc. (source@mvista.com)\n"); --- linux-2.6.9-rc1/arch/ppc/platforms/sbc82xx.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/ppc/platforms/sbc82xx.c 2004-09-02 20:51:51.000000000 -0700 @@ -16,10 +16,10 @@ */ #include -#include #include #include #include +#include #include #include @@ -29,39 +29,12 @@ #include #include -static void (*callback_setup_arch)(void); static void (*callback_init_IRQ)(void); extern unsigned char __res[sizeof(bd_t)]; -extern void m8260_init(unsigned long r3, unsigned long r4, - unsigned long r5, unsigned long r6, unsigned long r7); - extern void (*late_time_init)(void); -static int -sbc82xx_show_cpuinfo(struct seq_file *m) -{ - bd_t *binfo = (bd_t *)__res; - - seq_printf(m, "vendor\t\t: Wind River\n" - "machine\t\t: SBC PowerQUICC II\n" - "\n" - "mem size\t\t: 0x%08lx\n" - "console baud\t\t: %ld\n" - "\n", - binfo->bi_memsize, - binfo->bi_baudrate); - return 0; -} - -static void __init -sbc82xx_setup_arch(void) -{ - printk("SBC PowerQUICC II Port\n"); - callback_setup_arch(); -} - #ifdef CONFIG_GEN_RTC TODC_ALLOC(); @@ -237,7 +210,6 @@ static int sbc82xx_pci_map_irq(struct pc return PCI_IRQ_TABLE_LOOKUP; } - static void __devinit quirk_sbc8260_cardbus(struct pci_dev *pdev) { uint32_t ctrl; @@ -259,23 +231,15 @@ static void __devinit quirk_sbc8260_card DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1420, quirk_sbc8260_cardbus); void __init -platform_init(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7) +m82xx_board_init(void) { - /* Generic 8260 platform initialization */ - m8260_init(r3, r4, r5, r6, r7); - /* u-boot may be using one of the FCC Ethernet devices. Use the MAC address to the SCC. */ __res[offsetof(bd_t, bi_enetaddr[5])] &= ~3; /* Anything special for this platform */ - ppc_md.show_cpuinfo = sbc82xx_show_cpuinfo; - - callback_setup_arch = ppc_md.setup_arch; callback_init_IRQ = ppc_md.init_IRQ; - ppc_md.setup_arch = sbc82xx_setup_arch; ppc_md.init_IRQ = sbc82xx_init_IRQ; ppc_md.pci_map_irq = sbc82xx_pci_map_irq; #ifdef CONFIG_GEN_RTC --- linux-2.6.9-rc1/arch/ppc/platforms/sbc82xx.h 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/ppc/platforms/sbc82xx.h 2004-09-02 20:51:51.000000000 -0700 @@ -18,6 +18,10 @@ #define SBC82xx_MACADDR_NVRAM_FCC2 0x220000d5 /* JP7A */ #define SBC82xx_MACADDR_NVRAM_FCC3 0x220000db /* JP7B */ +/* For our show_cpuinfo hooks. */ +#define CPUINFO_VENDOR "Wind River" +#define CPUINFO_MACHINE "SBC PowerQUICC II" + #define BOOTROM_RESTART_ADDR ((uint)0x40000104) #define SBC82xx_PC_IRQA (NR_SIU_INTS+0) --- linux-2.6.9-rc1/arch/ppc/platforms/spruce.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/ppc/platforms/spruce.c 2004-09-02 20:51:51.000000000 -0700 @@ -228,11 +228,6 @@ spruce_setup_arch(void) ROOT_DEV = Root_SDA1; #endif -#ifdef CONFIG_VT - conswitchp = &dummy_con; -#endif - - /* Identify the system */ printk(KERN_INFO "System Identification: IBM Spruce\n"); printk(KERN_INFO "Port by MontaVista Software, Inc. (source@mvista.com)\n"); --- linux-2.6.9-rc1/arch/ppc/platforms/tqm8260.h 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/ppc/platforms/tqm8260.h 2004-09-02 20:51:51.000000000 -0700 @@ -14,6 +14,10 @@ #define CPM_MAP_ADDR ((uint)0xFFF00000) #define PHY_INTERRUPT 25 +/* For our show_cpuinfo hooks. */ +#define CPUINFO_VENDOR "IN2 Systems" +#define CPUINFO_MACHINE "TQM8260 PowerPC" + #define BOOTROM_RESTART_ADDR ((uint)0x40000104) #endif /* __TQM8260_PLATFORM */ --- linux-2.6.9-rc1/arch/ppc/platforms/tqm8260_setup.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/ppc/platforms/tqm8260_setup.c 2004-09-02 20:51:51.000000000 -0700 @@ -14,33 +14,12 @@ * option) any later version. */ -#include -#include +#include #include #include #include -static void (*callback_setup_arch)(void); - -extern unsigned char __res[sizeof(bd_t)]; - -extern void m8260_init(unsigned long r3, unsigned long r4, - unsigned long r5, unsigned long r6, unsigned long r7); - -static int -tqm8260_show_cpuinfo(struct seq_file *m) -{ - bd_t *binfo = (bd_t *)__res; - - seq_printf(m, "vendor\t\t: IN2 Systems\n" - "machine\t\t: TQM8260 PowerPC\n" - "mem size\t\t: 0x%08x\n" - "\n", - binfo->bi_memsize); - return 0; -} - static int tqm8260_set_rtc_time(unsigned long time) { @@ -56,24 +35,10 @@ tqm8260_get_rtc_time(void) return ((cpm2_map_t *)CPM_MAP_ADDR)->im_sit.sit_tmcnt; } -static void __init -tqm8260_setup_arch(void) -{ - printk("IN2 Systems TQM8260 port\n"); - callback_setup_arch(); -} - void __init -platform_init(unsigned long r3, unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7) +m82xx_board_init(void) { - /* Generic 8260 platform initialization */ - m8260_init(r3, r4, r5, r6, r7); - /* Anything special for this platform */ - ppc_md.show_cpuinfo = tqm8260_show_cpuinfo; ppc_md.set_rtc_time = tqm8260_set_rtc_time; ppc_md.get_rtc_time = tqm8260_get_rtc_time; - - callback_setup_arch = ppc_md.setup_arch; - ppc_md.setup_arch = tqm8260_setup_arch; +} --- linux-2.6.9-rc1/arch/ppc/syslib/cpm2_pic.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/arch/ppc/syslib/cpm2_pic.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,12 +1,3 @@ -#include -#include -#include -#include -#include -#include -#include -#include "cpm2_pic.h" - /* The CPM2 internal interrupt controller. It is usually * the only interrupt controller. * There are two 32-bit registers (high/low) for up to 64 @@ -18,6 +9,18 @@ * We create two tables, indexed by vector number, to indicate * which register to use and which bit in the register to use. */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "cpm2_pic.h" + static u_char irq_to_siureg[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -123,9 +126,5 @@ cpm2_get_irq(struct pt_regs *regs) if (irq == 0) return(-1); -#if 0 - irq += ppc8260_pic.irq_offset; -#endif return irq; } - --- linux-2.6.9-rc1/arch/ppc/syslib/cpm2_pic.h 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/ppc/syslib/cpm2_pic.h 2004-09-02 20:51:51.000000000 -0700 @@ -1,13 +1,7 @@ #ifndef _PPC_KERNEL_CPM2_H #define _PPC_KERNEL_CPM2_H -#include - extern struct hw_interrupt_type cpm2_pic; - -void cpm2_pic_init(void); -void cpm2_do_IRQ(struct pt_regs *regs, - int cpu); -int cpm2_get_irq(struct pt_regs *regs); +extern int cpm2_get_irq(struct pt_regs *regs); #endif /* _PPC_KERNEL_CPM2_H */ --- linux-2.6.9-rc1/arch/ppc/syslib/hawk_common.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/arch/ppc/syslib/hawk_common.c 2004-09-02 20:51:51.000000000 -0700 @@ -285,3 +285,35 @@ hawk_get_mem_size(uint smc_base) return total; } + +int __init +hawk_mpic_init(unsigned int pci_mem_offset) +{ + unsigned short devid; + unsigned int pci_membase; + + /* Check the first PCI device to see if it is a Raven or Hawk. */ + early_read_config_word(0, 0, 0, PCI_DEVICE_ID, &devid); + + switch (devid) { + case PCI_DEVICE_ID_MOTOROLA_RAVEN: + case PCI_DEVICE_ID_MOTOROLA_HAWK: + break; + default: + OpenPIC_Addr = NULL; + return 1; + } + + /* Read the memory base register. */ + early_read_config_dword(0, 0, 0, PCI_BASE_ADDRESS_1, &pci_membase); + + if (pci_membase == 0) { + OpenPIC_Addr = NULL; + return 1; + } + + /* Map the MPIC registers to virtual memory. */ + OpenPIC_Addr = ioremap(pci_membase + pci_mem_offset, 0x22000); + + return 0; +} --- linux-2.6.9-rc1/arch/ppc/syslib/m8260_pci.h 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/ppc/syslib/m8260_pci.h 2004-09-02 20:51:51.000000000 -0700 @@ -66,6 +66,7 @@ #endif #ifdef CONFIG_8260_PCI9 +struct pci_controller; extern void setup_m8260_indirect_pci(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data); #else --- linux-2.6.9-rc1/arch/ppc/syslib/m8260_setup.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/ppc/syslib/m8260_setup.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,5 +1,5 @@ /* - * arch/ppc/kernel/setup.c + * arch/ppc/syslib/m8260_setup.c * * Copyright (C) 1995 Linus Torvalds * Adapted from 'alpha' version by Gary Thomas @@ -8,36 +8,20 @@ * Further modified for generic 8xx and 8260 by Dan. */ -/* - * bootup setup stuff.. - */ - #include -#include #include #include #include #include -#include -#include #include -#include -#include -#include -#include -#include -#include #include #include -#include -#include #include +#include #include -#include #include #include -#include #include #include #include @@ -46,10 +30,6 @@ #include "cpm2_pic.h" -static int m8260_set_rtc_time(unsigned long time); -static unsigned long m8260_get_rtc_time(void); -static void m8260_calibrate_decr(void); - unsigned char __res[sizeof(bd_t)]; extern void cpm2_reset(void); @@ -59,8 +39,10 @@ extern void idma_pci9_init(void); static void __init m8260_setup_arch(void) { - /* Reset the Communication Processor Module. - */ + /* Print out Vendor and Machine info. */ + printk(KERN_INFO "%s %s port\n", CPUINFO_VENDOR, CPUINFO_MACHINE); + + /* Reset the Communication Processor Module. */ cpm2_reset(); #ifdef CONFIG_8260_PCI9 /* Initialise IDMA for PCI erratum workaround */ @@ -69,6 +51,10 @@ m8260_setup_arch(void) #ifdef CONFIG_PCI_8260 m8260_find_bridges(); #endif +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = Root_RAM0; +#endif } /* The decrementer counts at the system (internal) clock frequency @@ -77,7 +63,7 @@ m8260_setup_arch(void) static void __init m8260_calibrate_decr(void) { - bd_t *binfo = (bd_t *)__res; + bd_t *binfo = (bd_t *)__res; int freq, divisor; freq = binfo->bi_busfreq; @@ -145,18 +131,22 @@ m8260_power_off(void) } static int -m8260_show_percpuinfo(struct seq_file *m, int i) +m8260_show_cpuinfo(struct seq_file *m) { - bd_t *bp; - - bp = (bd_t *)__res; + bd_t *bp = (bd_t *)__res; - seq_printf(m, "core clock\t: %ld MHz\n" - "CPM clock\t: %ld MHz\n" - "bus clock\t: %ld MHz\n", - bp->bi_intfreq / 1000000, - bp->bi_cpmfreq / 1000000, - bp->bi_busfreq / 1000000); + seq_printf(m, "vendor\t\t: %s\n" + "machine\t\t: %s\n" + "\n" + "mem size\t\t: 0x%08x\n" + "console baud\t\t: %d\n" + "\n" + "core clock\t: %u MHz\n" + "CPM clock\t: %u MHz\n" + "bus clock\t: %u MHz\n", + CPUINFO_VENDOR, CPUINFO_MACHINE, bp->bi_memsize, + bp->bi_baudrate, bp->bi_intfreq / 1000000, + bp->bi_cpmfreq / 1000000, bp->bi_busfreq / 1000000); return 0; } @@ -170,7 +160,6 @@ static void __init m8260_init_IRQ(void) { int i; - void cpm_interrupt_init(void); for ( i = 0 ; i < NR_SIU_INTS ; i++ ) irq_desc[i].handler = &cpm2_pic; @@ -190,10 +179,7 @@ m8260_init_IRQ(void) static unsigned long __init m8260_find_end_of_memory(void) { - bd_t *binfo; - extern unsigned char __res[]; - - binfo = (bd_t *)__res; + bd_t *binfo = (bd_t *)__res; return binfo->bi_memsize; } @@ -216,6 +202,12 @@ m8260_map_io(void) io_block_mapping(IO_VIRT_ADDR, IO_PHYS_ADDR, 0x10000000, _PAGE_IO); } +/* Place-holder for board-specific init */ +void __attribute__ ((weak)) __init +m82xx_board_init(void) +{ +} + /* Inputs: * r3 - Optional pointer to a board information structure. * r4 - Optional pointer to the physical starting address of the init RAM @@ -228,7 +220,7 @@ m8260_map_io(void) * command-line parameters. */ void __init -m8260_init(unsigned long r3, unsigned long r4, unsigned long r5, +platform_init(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { parse_bootinfo(find_bootinfo()); @@ -249,18 +241,18 @@ m8260_init(unsigned long r3, unsigned lo strcpy(cmd_line, (char *)(r6+KERNELBASE)); } + /* Call back for board-specific settings. */ + m82xx_board_init(); + ppc_md.setup_arch = m8260_setup_arch; - ppc_md.show_percpuinfo = m8260_show_percpuinfo; - ppc_md.irq_canonicalize = NULL; + ppc_md.show_cpuinfo = m8260_show_cpuinfo; ppc_md.init_IRQ = m8260_init_IRQ; ppc_md.get_irq = cpm2_get_irq; - ppc_md.init = NULL; ppc_md.restart = m8260_restart; ppc_md.power_off = m8260_power_off; ppc_md.halt = m8260_halt; - ppc_md.time_init = NULL; ppc_md.set_rtc_time = m8260_set_rtc_time; ppc_md.get_rtc_time = m8260_get_rtc_time; ppc_md.calibrate_decr = m8260_calibrate_decr; --- linux-2.6.9-rc1/arch/ppc/syslib/m8xx_setup.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/ppc/syslib/m8xx_setup.c 2004-09-02 20:51:51.000000000 -0700 @@ -60,6 +60,11 @@ extern unsigned long find_available_memo extern void m8xx_cpm_reset(uint); extern void rpxfb_alloc_pages(void); +void __attribute__ ((weak)) +board_init(void) +{ +} + void __init m8xx_setup_arch(void) { @@ -102,6 +107,7 @@ m8xx_setup_arch(void) } #endif #endif + board_init(); } void --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/ppc/syslib/m8xx_wdt.c 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,99 @@ +/* + * m8xx_wdt.c - MPC8xx watchdog driver + * + * Author: Florian Schirmer + * + * 2002 (c) Florian Schirmer This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include + +static int wdt_timeout; + +void m8xx_wdt_reset(void) +{ + volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; + + imap->im_siu_conf.sc_swsr = 0x556c; /* write magic1 */ + imap->im_siu_conf.sc_swsr = 0xaa39; /* write magic2 */ +} + +static irqreturn_t m8xx_wdt_interrupt(int irq, void *dev, struct pt_regs *regs) +{ + volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; + + m8xx_wdt_reset(); + + imap->im_sit.sit_piscr |= PISCR_PS; /* clear irq */ + + return IRQ_HANDLED; +} + +void __init m8xx_wdt_handler_install(bd_t * binfo) +{ + volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; + u32 pitc; + u32 sypcr; + u32 pitrtclk; + + sypcr = imap->im_siu_conf.sc_sypcr; + + if (!(sypcr & 0x04)) { + printk(KERN_NOTICE "m8xx_wdt: wdt disabled (SYPCR: 0x%08X)\n", + sypcr); + return; + } + + m8xx_wdt_reset(); + + printk(KERN_NOTICE + "m8xx_wdt: active wdt found (SWTC: 0x%04X, SWP: 0x%01X)\n", + (sypcr >> 16), sypcr & 0x01); + + wdt_timeout = (sypcr >> 16) & 0xFFFF; + + if (!wdt_timeout) + wdt_timeout = 0xFFFF; + + if (sypcr & 0x01) + wdt_timeout *= 2048; + + /* + * Fire trigger if half of the wdt ticked down + */ + + if (imap->im_sit.sit_rtcsc & RTCSC_38K) + pitrtclk = 9600; + else + pitrtclk = 8192; + + if ((wdt_timeout) > (UINT_MAX / pitrtclk)) + pitc = wdt_timeout / binfo->bi_intfreq * pitrtclk / 2; + else + pitc = pitrtclk * wdt_timeout / binfo->bi_intfreq / 2; + + imap->im_sit.sit_pitc = pitc << 16; + imap->im_sit.sit_piscr = + (mk_int_int_mask(PIT_INTERRUPT) << 8) | PISCR_PIE | PISCR_PTE; + + if (request_irq(PIT_INTERRUPT, m8xx_wdt_interrupt, 0, "watchdog", NULL)) + panic("m8xx_wdt: could not allocate watchdog irq!"); + + printk(KERN_NOTICE + "m8xx_wdt: keep-alive trigger installed (PITC: 0x%04X)\n", pitc); + + wdt_timeout /= binfo->bi_intfreq; +} + +int m8xx_wdt_get_timeout(void) +{ + return wdt_timeout; +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/ppc/syslib/m8xx_wdt.h 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,16 @@ +/* + * Author: Florian Schirmer + * + * 2002 (c) Florian Schirmer This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#ifndef _PPC_SYSLIB_M8XX_WDT_H +#define _PPC_SYSLIB_M8XX_WDT_H + +extern void m8xx_wdt_handler_install(bd_t * binfo); +extern int m8xx_wdt_get_timeout(void); +extern void m8xx_wdt_reset(void); + +#endif /* _PPC_SYSLIB_M8XX_WDT_H */ --- linux-2.6.9-rc1/arch/ppc/syslib/Makefile 2004-08-24 00:01:53.000000000 -0700 +++ 25/arch/ppc/syslib/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -5,6 +5,8 @@ CFLAGS_prom_init.o += -fPIC CFLAGS_btext.o += -fPIC +wdt-mpc8xx-$(CONFIG_8xx_WDT) += m8xx_wdt.o + obj-$(CONFIG_PPCBUG_NVRAM) += prep_nvram.o obj-$(CONFIG_PPC_OCP) += ocp.o obj-$(CONFIG_IBM_OCP) += ibm_ocp.o @@ -22,7 +24,7 @@ obj-$(CONFIG_KGDB) += ppc4xx_kgdb.o obj-$(CONFIG_PCI) += indirect_pci.o pci_auto.o ppc405_pci.o endif endif -obj-$(CONFIG_8xx) += m8xx_setup.o ppc8xx_pic.o +obj-$(CONFIG_8xx) += m8xx_setup.o ppc8xx_pic.o $(wdt-mpc8xx-y) ifeq ($(CONFIG_8xx),y) obj-$(CONFIG_PCI) += qspan_pci.o i8259.o endif --- linux-2.6.9-rc1/arch/ppc/syslib/open_pic2.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/ppc/syslib/open_pic2.c 2004-09-03 00:56:40.996928168 -0700 @@ -28,7 +28,6 @@ #include #include #include -#include #include "open_pic_defs.h" --- linux-2.6.9-rc1/arch/ppc/syslib/open_pic.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/ppc/syslib/open_pic.c 2004-09-03 00:56:40.996928168 -0700 @@ -24,7 +24,6 @@ #include #include #include -#include #include "open_pic_defs.h" --- linux-2.6.9-rc1/arch/ppc/syslib/ppc4xx_setup.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/arch/ppc/syslib/ppc4xx_setup.c 2004-09-02 20:51:51.000000000 -0700 @@ -61,10 +61,6 @@ ppc4xx_setup_arch(void) #ifdef CONFIG_PCI ppc4xx_find_bridges(); #endif - -#if defined(CONFIG_FB) - conswitchp = &dummy_con; -#endif } /* --- linux-2.6.9-rc1/arch/ppc/syslib/ppc85xx_setup.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/ppc/syslib/ppc85xx_setup.c 2004-09-02 20:51:51.000000000 -0700 @@ -277,7 +277,7 @@ mpc85xx_setup_hose(void) hose_a->io_space.start = MPC85XX_PCI1_LOWER_IO; hose_a->io_space.end = MPC85XX_PCI1_UPPER_IO; hose_a->io_base_phys = MPC85XX_PCI1_IO_BASE; -#if CONFIG_85xx_PCI2 +#ifdef CONFIG_85xx_PCI2 isa_io_base = (unsigned long) ioremap(MPC85XX_PCI1_IO_BASE, MPC85XX_PCI1_IO_SIZE + @@ -304,7 +304,7 @@ mpc85xx_setup_hose(void) hose_a->last_busno = pciauto_bus_scan(hose_a, hose_a->first_busno); -#if CONFIG_85xx_PCI2 +#ifdef CONFIG_85xx_PCI2 hose_b = pcibios_alloc_controller(); if (!hose_b) --- linux-2.6.9-rc1/arch/s390/defconfig 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/s390/defconfig 2004-09-02 20:51:51.000000000 -0700 @@ -1,5 +1,7 @@ # # Automatically generated make config: don't edit +# Linux kernel version: 2.6.9-rc1 +# Mon Aug 30 19:03:48 2004 # CONFIG_MMU=y CONFIG_RWSEM_XCHGADD_ALGORITHM=y @@ -56,15 +58,23 @@ CONFIG_KMOD=y # CONFIG_ARCH_S390X is not set # CONFIG_64BIT is not set CONFIG_ARCH_S390_31=y -CONFIG_MARCH_G5=y -# CONFIG_MARCH_Z900 is not set -# CONFIG_MARCH_Z990 is not set CONFIG_SMP=y CONFIG_NR_CPUS=32 # CONFIG_HOTPLUG_CPU is not set CONFIG_MATHEMU=y # +# Code generation options +# +CONFIG_MARCH_G5=y +# CONFIG_MARCH_Z900 is not set +# CONFIG_MARCH_Z990 is not set +CONFIG_PACK_STACK=y +# CONFIG_SMALL_STACK is not set +# CONFIG_CHECK_STACK is not set +# CONFIG_WARN_STACK is not set + +# # I/O subsystem configuration # CONFIG_MACHCHK_WARNING=y @@ -130,9 +140,7 @@ CONFIG_SCSI_FC_ATTRS=y # # SCSI low-level drivers # -# CONFIG_SCSI_AIC7XXX_OLD is not set # CONFIG_SCSI_SATA is not set -# CONFIG_SCSI_EATA_PIO is not set # CONFIG_SCSI_DEBUG is not set CONFIG_ZFCP=y CONFIG_CCW=y @@ -168,6 +176,7 @@ CONFIG_BLK_DEV_MD=y CONFIG_MD_LINEAR=m CONFIG_MD_RAID0=m CONFIG_MD_RAID1=m +# CONFIG_MD_RAID10 is not set CONFIG_MD_RAID5=m # CONFIG_MD_RAID6 is not set CONFIG_MD_MULTIPATH=m @@ -236,11 +245,13 @@ CONFIG_IP_MULTICAST=y # CONFIG_INET_AH is not set # CONFIG_INET_ESP is not set # CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set CONFIG_IPV6=y # CONFIG_IPV6_PRIVACY is not set # CONFIG_INET6_AH is not set # CONFIG_INET6_ESP is not set # CONFIG_INET6_IPCOMP is not set +# CONFIG_INET6_TUNNEL is not set # CONFIG_IPV6_TUNNEL is not set # CONFIG_NETFILTER is not set CONFIG_XFRM=y @@ -419,7 +430,6 @@ CONFIG_RAMFS=y # CONFIG_BEFS_FS is not set # CONFIG_BFS_FS is not set # CONFIG_EFS_FS is not set -# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set # CONFIG_CRAMFS is not set # CONFIG_VXFS_FS is not set # CONFIG_HPFS_FS is not set @@ -443,6 +453,7 @@ CONFIG_LOCKD_V4=y CONFIG_EXPORTFS=y CONFIG_SUNRPC=y # CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set # CONFIG_SMB_FS is not set # CONFIG_CIFS is not set # CONFIG_NCP_FS is not set @@ -486,8 +497,8 @@ CONFIG_MSDOS_PARTITION=y CONFIG_DEBUG_KERNEL=y CONFIG_MAGIC_SYSRQ=y # CONFIG_DEBUG_SLAB is not set -# CONFIG_DEBUG_INFO is not set # CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_INFO is not set # # Security options @@ -506,16 +517,18 @@ CONFIG_CRYPTO=y # CONFIG_CRYPTO_SHA1_Z990 is not set # CONFIG_CRYPTO_SHA256 is not set # CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_WHIRLPOOL is not set # CONFIG_CRYPTO_DES is not set # CONFIG_CRYPTO_DES_Z990 is not set # CONFIG_CRYPTO_BLOWFISH is not set # CONFIG_CRYPTO_TWOFISH is not set # CONFIG_CRYPTO_SERPENT is not set -# CONFIG_CRYPTO_AES_GENERIC is not set +# CONFIG_CRYPTO_AES is not set # CONFIG_CRYPTO_CAST5 is not set # CONFIG_CRYPTO_CAST6 is not set # CONFIG_CRYPTO_TEA is not set # CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_KHAZAD is not set # CONFIG_CRYPTO_DEFLATE is not set # CONFIG_CRYPTO_MICHAEL_MIC is not set # CONFIG_CRYPTO_CRC32C is not set --- linux-2.6.9-rc1/arch/s390/Kconfig 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/s390/Kconfig 2004-09-02 20:51:51.000000000 -0700 @@ -48,33 +48,6 @@ config ARCH_S390_31 depends on ARCH_S390X = 'n' default y -choice - prompt "Processor type" - default MARCH_G5 - -config MARCH_G5 - bool "S/390 model G5 and G6" - depends on ARCH_S390_31 - help - Select this to build a 31 bit kernel that works - on all S/390 and zSeries machines. - -config MARCH_Z900 - bool "IBM eServer zSeries model z800 and z900" - help - Select this to optimize for zSeries machines. This - will enable some optimizations that are not available - on older 31 bit only CPUs. - -config MARCH_Z990 - bool "IBM eServer zSeries model z890 and z990" - help - Select this enable optimizations for model z890/z990. - This will be slightly faster but does not work on - older machines such as the z900. - -endchoice - config SMP bool "Symmetric multi-processing support" ---help--- @@ -149,6 +122,105 @@ config BINFMT_ELF32 This allows you to run 32-bit Linux/ELF binaries on your zSeries in 64 bit mode. Everybody wants this; say Y. +comment "Code generation options" + +choice + prompt "Processor type" + default MARCH_G5 + +config MARCH_G5 + bool "S/390 model G5 and G6" + depends on ARCH_S390_31 + help + Select this to build a 31 bit kernel that works + on all S/390 and zSeries machines. + +config MARCH_Z900 + bool "IBM eServer zSeries model z800 and z900" + help + Select this to optimize for zSeries machines. This + will enable some optimizations that are not available + on older 31 bit only CPUs. + +config MARCH_Z990 + bool "IBM eServer zSeries model z890 and z990" + help + Select this enable optimizations for model z890/z990. + This will be slightly faster but does not work on + older machines such as the z900. + +endchoice + +config PACK_STACK + bool "Pack kernel stack" + help + This option enables the compiler option -mkernel-backchain if it + is available. If the option is available the compiler supports + the new stack layout which dramatically reduces the minimum stack + frame size. With an old compiler a non-leaf function needs a + minimum of 96 bytes on 31 bit and 160 bytes on 64 bit. With + -mkernel-backchain the minimum size drops to 16 byte on 31 bit + and 24 byte on 64 bit. + + Say Y if you are unsure. + +config SMALL_STACK + bool "Use 4kb/8kb for kernel stack instead of 8kb/16kb" + depends on PACK_STACK + help + If you say Y here and the compiler supports the -mkernel-backchain + option the kernel will use a smaller kernel stack size. For 31 bit + the reduced size is 4kb instead of 8kb and for 64 bit it is 8kb + instead of 16kb. This allows to run more thread on a system and + reduces the pressure on the memory management for higher order + page allocations. + + Say N if you are unsure. + + +config CHECK_STACK + bool "Detect kernel stack overflow" + help + This option enables the compiler option -mstack-guard and + -mstack-size if they are available. If the compiler supports them + it will emit additional code to each function prolog to trigger + an illegal operation if the kernel stack is about to overflow. + + Say N if you are unsure. + +config STACK_GUARD + int "Size of the guard area (128-1024)" + range 128 1024 + depends on CHECK_STACK + default "256" + help + This allows you to specify the size of the guard area at the lower + end of the kernel stack. If the kernel stack points into the guard + area on function entry an illegal operation is triggered. The size + needs to be a power of 2. Please keep in mind that the size of an + interrupt frame is 184 bytes for 31 bit and 328 bytes on 64 bit. + The minimum size for the stack guard should be 256 for 31 bit and + 512 for 64 bit. + +config WARN_STACK + bool "Emit compiler warnings for function with broken stack usage" + help + This option enables the compiler options -mwarn-framesize and + -mwarn-dynamicstack. If the compiler supports these options it + will generate warnings for function which either use alloca or + create a stack frame bigger then CONFIG_WARN_STACK_SIZE. + + Say N if you are unsure. + +config WARN_STACK_SIZE + int "Maximum frame size considered safe (128-2048)" + range 128 2048 + depends on WARN_STACK + default "256" + help + This allows you to specify the maximum frame size a function may + have without the compiler complaining about it. + comment "I/O subsystem configuration" config MACHCHK_WARNING --- linux-2.6.9-rc1/arch/s390/kernel/asm-offsets.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/s390/kernel/asm-offsets.c 2004-09-02 20:51:51.000000000 -0700 @@ -39,5 +39,9 @@ int main(void) DEFINE(__PT_ILC, offsetof(struct pt_regs, ilc),); DEFINE(__PT_TRAP, offsetof(struct pt_regs, trap),); DEFINE(__PT_SIZE, sizeof(struct pt_regs),); + BLANK(); + DEFINE(__SF_BACKCHAIN, offsetof(struct stack_frame, back_chain),); + DEFINE(__SF_GPRS, offsetof(struct stack_frame, gprs),); + DEFINE(__SF_EMPTY, offsetof(struct stack_frame, empty1),); return 0; } --- linux-2.6.9-rc1/arch/s390/kernel/compat_exec.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/s390/kernel/compat_exec.c 2004-09-02 20:51:51.000000000 -0700 @@ -69,7 +69,7 @@ int setup_arg_pages32(struct linux_binpr mpnt->vm_page_prot = PAGE_COPY; mpnt->vm_flags = VM_STACK_FLAGS; insert_vm_struct(mm, mpnt); - mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT; + mm->stack_vm = mm->total_vm = vma_pages(mpnt); } for (i = 0 ; i < MAX_ARG_PAGES ; i++) { --- linux-2.6.9-rc1/arch/s390/kernel/compat_linux.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/s390/kernel/compat_linux.c 2004-09-02 20:51:51.000000000 -0700 @@ -725,8 +725,7 @@ sys32_rt_sigqueueinfo(int pid, int sig, int ret; mm_segment_t old_fs = get_fs(); - if (copy_from_user (&info, uinfo, 3*sizeof(int)) || - copy_from_user (info._sifields._pad, uinfo->_sifields._pad, SI_PAD_SIZE)) + if (copy_siginfo_from_user32(&info, uinfo)) return -EFAULT; set_fs (KERNEL_DS); ret = sys_rt_sigqueueinfo(pid, sig, &info); @@ -1219,7 +1218,7 @@ asmlinkage long sys32_clone(struct pt_re child_tidptr = (int *) (regs.gprs[5] & 0x7fffffffUL); if (!newsp) newsp = regs.gprs[15]; - return do_fork(clone_flags & ~CLONE_IDLETASK, newsp, ®s, 0, + return do_fork(clone_flags, newsp, ®s, 0, parent_tidptr, child_tidptr); } --- linux-2.6.9-rc1/arch/s390/kernel/compat_signal.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/s390/kernel/compat_signal.c 2004-09-02 20:51:51.000000000 -0700 @@ -218,14 +218,17 @@ sys32_sigaction(int sig, const struct ol struct old_sigaction32 __user *oact) { struct k_sigaction new_ka, old_ka; + unsigned long sa_handler, sa_restorer; int ret; if (act) { compat_old_sigset_t mask; if (verify_area(VERIFY_READ, act, sizeof(*act)) || - __get_user((unsigned long)new_ka.sa.sa_handler, &act->sa_handler) || - __get_user((unsigned long)new_ka.sa.sa_restorer, &act->sa_restorer)) + __get_user(sa_handler, &act->sa_handler) || + __get_user(sa_restorer, &act->sa_restorer)) return -EFAULT; + new_ka.sa.sa_handler = (__sighandler_t) sa_handler; + new_ka.sa.sa_restorer = (void (*)(void)) sa_restorer; __get_user(new_ka.sa.sa_flags, &act->sa_flags); __get_user(mask, &act->sa_mask); siginitset(&new_ka.sa.sa_mask, mask); @@ -234,9 +237,11 @@ sys32_sigaction(int sig, const struct ol ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); if (!ret && oact) { + sa_handler = (unsigned long) old_ka.sa.sa_handler; + sa_restorer = (unsigned long) old_ka.sa.sa_restorer; if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || - __put_user((unsigned long)old_ka.sa.sa_handler, &oact->sa_handler) || - __put_user((unsigned long)old_ka.sa.sa_restorer, &oact->sa_restorer)) + __put_user(sa_handler, &oact->sa_handler) || + __put_user(sa_restorer, &oact->sa_restorer)) return -EFAULT; __put_user(old_ka.sa.sa_flags, &oact->sa_flags); __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); @@ -253,6 +258,7 @@ sys32_rt_sigaction(int sig, const struct struct sigaction32 __user *oact, size_t sigsetsize) { struct k_sigaction new_ka, old_ka; + unsigned long sa_handler; int ret; compat_sigset_t set32; @@ -261,7 +267,7 @@ sys32_rt_sigaction(int sig, const struct return -EINVAL; if (act) { - ret = get_user((unsigned long)new_ka.sa.sa_handler, &act->sa_handler); + ret = get_user(sa_handler, &act->sa_handler); ret |= __copy_from_user(&set32, &act->sa_mask, sizeof(compat_sigset_t)); switch (_NSIG_WORDS) { @@ -278,6 +284,7 @@ sys32_rt_sigaction(int sig, const struct if (ret) return -EFAULT; + new_ka.sa.sa_handler = (__sighandler_t) sa_handler; } ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); @@ -311,17 +318,19 @@ sys32_sigaltstack(const stack_t32 __user struct pt_regs *regs) { stack_t kss, koss; + unsigned long ss_sp; int ret, err = 0; mm_segment_t old_fs = get_fs(); if (uss) { if (!access_ok(VERIFY_READ, uss, sizeof(*uss))) return -EFAULT; - err |= __get_user((unsigned long) kss.ss_sp, &uss->ss_sp); + err |= __get_user(ss_sp, &uss->ss_sp); err |= __get_user(kss.ss_size, &uss->ss_size); err |= __get_user(kss.ss_flags, &uss->ss_flags); if (err) return -EFAULT; + kss.ss_sp = (void *) ss_sp; } set_fs (KERNEL_DS); @@ -333,7 +342,8 @@ sys32_sigaltstack(const stack_t32 __user if (!ret && uoss) { if (!access_ok(VERIFY_WRITE, uoss, sizeof(*uoss))) return -EFAULT; - err |= __put_user((unsigned long) koss.ss_sp, &uoss->ss_sp); + ss_sp = (unsigned long) koss.ss_sp; + err |= __put_user(ss_sp, &uoss->ss_sp); err |= __put_user(koss.ss_size, &uoss->ss_size); err |= __put_user(koss.ss_flags, &uoss->ss_flags); if (err) @@ -552,9 +562,7 @@ static void setup_frame32(int sig, struc return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } static void setup_rt_frame32(int sig, struct k_sigaction *ka, siginfo_t *info, @@ -604,9 +612,7 @@ static void setup_rt_frame32(int sig, st return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } /* @@ -614,20 +620,15 @@ give_sigsegv: */ void -handle_signal32(unsigned long sig, siginfo_t *info, sigset_t *oldset, - struct pt_regs * regs) +handle_signal32(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs) { - struct k_sigaction *ka = ¤t->sighand->action[sig-1]; - /* Set up the stack frame */ if (ka->sa.sa_flags & SA_SIGINFO) setup_rt_frame32(sig, ka, info, oldset, regs); else setup_frame32(sig, ka, oldset, regs); - if (ka->sa.sa_flags & SA_ONESHOT) - ka->sa.sa_handler = SIG_DFL; - if (!(ka->sa.sa_flags & SA_NODEFER)) { spin_lock_irq(¤t->sighand->siglock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); --- linux-2.6.9-rc1/arch/s390/kernel/entry64.S 2004-08-24 00:02:22.000000000 -0700 +++ 25/arch/s390/kernel/entry64.S 2004-09-02 20:51:51.000000000 -0700 @@ -19,6 +19,7 @@ #include #include #include +#include /* * Stack layout for the system_call stack entry. @@ -48,6 +49,9 @@ SP_ILC = STACK_FRAME_OVERHEAD + _ SP_TRAP = STACK_FRAME_OVERHEAD + __PT_TRAP SP_SIZE = STACK_FRAME_OVERHEAD + __PT_SIZE +STACK_SHIFT = PAGE_SHIFT + THREAD_ORDER +STACK_SIZE = 1 << STACK_SHIFT + _TIF_WORK_SVC = (_TIF_SIGPENDING | _TIF_NEED_RESCHED | \ _TIF_RESTART_SVC | _TIF_SINGLE_STEP ) _TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_NEED_RESCHED) @@ -85,10 +89,16 @@ _TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_ jnz 1f 0: lg %r14,__LC_ASYNC_STACK # are we already on the async. stack ? slgr %r14,%r15 - srag %r14,%r14,14 + srag %r14,%r14,STACK_SHIFT jz 2f 1: lg %r15,__LC_ASYNC_STACK # load async stack .endif +#ifdef CONFIG_CHECK_STACK + j 3f +2: tml %r15,STACK_SIZE - CONFIG_STACK_GUARD + jz stack_overflow +3: +#endif 2: aghi %r15,-SP_SIZE # make room for registers & psw mvc SP_PSW(16,%r15),0(%r12) # move user PSW to stack la %r12,\psworg @@ -98,7 +108,7 @@ _TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_ st %r12,SP_ILC(%r15) mvc SP_R12(32,%r15),\savearea # move %r12-%r15 to stack la %r12,0 - stg %r12,0(%r15) + stg %r12,__SF_BACKCHAIN(%r15) .endm .macro RESTORE_ALL sync @@ -121,19 +131,19 @@ _TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_ __switch_to: tm __THREAD_per+4(%r3),0xe8 # is the new process using per ? jz __switch_to_noper # if not we're fine - stctg %c9,%c11,48(%r15) # We are using per stuff - clc __THREAD_per(24,%r3),48(%r15) + stctg %c9,%c11,__SF_EMPTY(%r15)# We are using per stuff + clc __THREAD_per(24,%r3),__SF_EMPTY(%r15) je __switch_to_noper # we got away without bashing TLB's lctlg %c9,%c11,__THREAD_per(%r3) # Nope we didn't __switch_to_noper: - stmg %r6,%r15,48(%r15) # store __switch_to registers of prev task + stmg %r6,%r15,__SF_GPRS(%r15)# store __switch_to registers of prev task stg %r15,__THREAD_ksp(%r2) # store kernel stack to prev->tss.ksp lg %r15,__THREAD_ksp(%r3) # load kernel stack from next->tss.ksp - lmg %r6,%r15,48(%r15) # load __switch_to registers of next task + lmg %r6,%r15,__SF_GPRS(%r15)# load __switch_to registers of next task stg %r3,__LC_CURRENT # __LC_CURRENT = current task struct lg %r3,__THREAD_info(%r3) # load thread_info from task struct stg %r3,__LC_THREAD_INFO - aghi %r3,16384 + aghi %r3,STACK_SIZE stg %r3,__LC_KERNEL_STACK # __LC_KERNEL_STACK = new kernel stack br %r14 @@ -143,19 +153,19 @@ __switch_to_noper: */ .global do_call_softirq do_call_softirq: - stnsm 48(%r15),0xfc - stmg %r12,%r15,56(%r15) + stnsm __SF_EMPTY(%r15),0xfc + stmg %r12,%r15,__SF_GPRS(%r15) lgr %r12,%r15 lg %r0,__LC_ASYNC_STACK slgr %r0,%r15 - srag %r0,%r0,14 + srag %r0,%r0,STACK_SHIFT je 0f lg %r15,__LC_ASYNC_STACK 0: aghi %r15,-STACK_FRAME_OVERHEAD - stg %r12,0(%r15) # store back chain + stg %r12,__SF_BACKCHAIN(%r15) # store back chain brasl %r14,do_softirq - lmg %r12,%r15,56(%r12) - ssm 48(%r15) + lmg %r12,%r15,__SF_GPRS(%r12) + ssm __SF_EMPTY(%r15) br %r14 __critical_start: @@ -304,7 +314,10 @@ sysc_tracenogo: ret_from_fork: lg %r13,__LC_SVC_NEW_PSW+8 lg %r9,__LC_THREAD_INFO # load pointer to thread_info struct - brasl %r14,schedule_tail + tm SP_PSW+1(%r15),0x01 # forking a kernel thread ? + jo 0f + stg %r15,SP_R15(%r15) # store stack pointer for new kthread +0: brasl %r14,schedule_tail stosm 24(%r15),0x03 # reenable interrupts j sysc_return @@ -504,7 +517,7 @@ pgm_svcper: mvc __THREAD_per+__PER_address(8,%r1),__LC_PER_ADDRESS mvc __THREAD_per+__PER_access_id(1,%r1),__LC_PER_ACCESS_ID oi __TI_flags+7(%r9),_TIF_SINGLE_STEP # set TIF_SINGLE_STEP - stosm 48(%r15),0x03 # reenable interrupts + stosm __SF_EMPTY(%r15),0x03 # reenable interrupts j sysc_do_svc /* @@ -539,16 +552,16 @@ io_preempt: lg %r1,SP_R15(%r15) aghi %r1,-SP_SIZE mvc SP_PTREGS(__PT_SIZE,%r1),SP_PTREGS(%r15) - xc 0(8,%r1),0(%r1) # clear back chain + xc __SF_BACKCHAIN(8,%r1),__SF_BACKCHAIN(%r1) # clear back chain lgr %r15,%r1 io_resume_loop: tm __TI_flags+7(%r9),_TIF_NEED_RESCHED jno io_leave larl %r1,.Lc_pactive mvc __TI_precount(4,%r9),0(%r1) - stosm 48(%r15),0x03 # reenable interrupts + stosm __SF_EMPTY(%r15),0x03 # reenable interrupts brasl %r14,schedule # call schedule - stnsm 48(%r15),0xfc # disable I/O and ext. interrupts + stnsm __SF_EMPTY(%r15),0xfc # disable I/O and ext. interrupts xc __TI_precount(4,%r9),__TI_precount(%r9) j io_resume_loop #endif @@ -560,7 +573,7 @@ io_work: lg %r1,__LC_KERNEL_STACK aghi %r1,-SP_SIZE mvc SP_PTREGS(__PT_SIZE,%r1),SP_PTREGS(%r15) - xc 0(8,%r1),0(%r1) # clear back chain + xc __SF_BACKCHAIN(8,%r1),__SF_BACKCHAIN(%r1) # clear back chain lgr %r15,%r1 # # One of the work bits is on. Find out which one. @@ -577,23 +590,23 @@ io_work_loop: # _TIF_NEED_RESCHED is set, call schedule # io_reschedule: - stosm 48(%r15),0x03 # reenable interrupts - brasl %r14,schedule # call scheduler - stnsm 48(%r15),0xfc # disable I/O and ext. interrupts + stosm __SF_EMPTY(%r15),0x03 # reenable interrupts + brasl %r14,schedule # call scheduler + stnsm __SF_EMPTY(%r15),0xfc # disable I/O and ext. interrupts tm __TI_flags+7(%r9),_TIF_WORK_INT - jz io_leave # there is no work to do + jz io_leave # there is no work to do j io_work_loop # # _TIF_SIGPENDING is set, call do_signal # io_sigpending: - stosm 48(%r15),0x03 # reenable interrupts - la %r2,SP_PTREGS(%r15) # load pt_regs - slgr %r3,%r3 # clear *oldset - brasl %r14,do_signal # call do_signal - stnsm 48(%r15),0xfc # disable I/O and ext. interrupts - j sysc_leave # out of here, do NOT recheck + stosm __SF_EMPTY(%r15),0x03 # reenable interrupts + la %r2,SP_PTREGS(%r15) # load pt_regs + slgr %r3,%r3 # clear *oldset + brasl %r14,do_signal # call do_signal + stnsm __SF_EMPTY(%r15),0xfc # disable I/O and ext. interrupts + j sysc_leave # out of here, do NOT recheck /* * External interrupt handler routine @@ -632,7 +645,7 @@ restart_int_handler: lghi %r10,__LC_AREGS_SAVE_AREA lam %a0,%a15,0(%r10) stosm 0(%r15),0x04 # now we can turn dat on - lmg %r6,%r15,48(%r15) # load registers from clone + lmg %r6,%r15,__SF_GPRS(%r15) # load registers from clone jg start_secondary #else /* @@ -649,6 +662,29 @@ restart_crash: restart_go: #endif +#ifdef CONFIG_CHECK_STACK +/* + * The synchronous or the asynchronous stack overflowed. We are dead. + * No need to properly save the registers, we are going to panic anyway. + * Setup a pt_regs so that show_trace can provide a good call trace. + */ +stack_overflow: + lg %r15,__LC_PANIC_STACK # change to panic stack + aghi %r1,-SP_SIZE + mvc SP_PSW(16,%r15),0(%r12) # move user PSW to stack + stmg %r0,%r11,SP_R0(%r15) # store gprs %r0-%r11 to kernel stack + la %r1,__LC_SAVE_AREA + chi %r12,__LC_SVC_OLD_PSW + je 0f + chi %r12,__LC_PGM_OLD_PSW + je 0f + la %r1,__LC_SAVE_AREA+16 +0: mvc SP_R12(32,%r15),0(%r1) # move %r12-%r15 to stack + xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) # clear back chain + la %r2,SP_PTREGS(%r15) # load pt_regs + jg kernel_stack_overflow +#endif + cleanup_table_system_call: .quad system_call, sysc_do_svc cleanup_table_sysc_return: --- linux-2.6.9-rc1/arch/s390/kernel/entry.S 2004-08-24 00:02:22.000000000 -0700 +++ 25/arch/s390/kernel/entry.S 2004-09-02 20:51:51.000000000 -0700 @@ -19,6 +19,7 @@ #include #include #include +#include /* * Stack layout for the system_call stack entry. @@ -52,6 +53,9 @@ _TIF_WORK_SVC = (_TIF_SIGPENDING | _TIF_ _TIF_RESTART_SVC | _TIF_SINGLE_STEP ) _TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_NEED_RESCHED) +STACK_SHIFT = PAGE_SHIFT + THREAD_ORDER +STACK_SIZE = 1 << STACK_SHIFT + #define BASED(name) name-system_call(%r13) /* @@ -86,10 +90,16 @@ _TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_ bnz BASED(1f) 0: l %r14,__LC_ASYNC_STACK # are we already on the async stack ? slr %r14,%r15 - sra %r14,13 + sra %r14,STACK_SHIFT be BASED(2f) 1: l %r15,__LC_ASYNC_STACK .endif +#ifdef CONFIG_CHECK_STACK + b BASED(3f) +2: tml %r15,STACK_SIZE - CONFIG_STACK_GUARD + bz BASED(stack_overflow) +3: +#endif 2: s %r15,BASED(.Lc_spsize) # make room for registers & psw mvc SP_PSW(8,%r15),0(%r12) # move user PSW to stack la %r12,\psworg @@ -99,7 +109,7 @@ _TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_ st %r12,SP_ILC(%r15) mvc SP_R12(16,%r15),\savearea # move %r12-%r15 to stack la %r12,0 - st %r12,0(%r15) # clear back chain + st %r12,__SF_BACKCHAIN(%r15) # clear back chain .endm .macro RESTORE_ALL sync @@ -124,19 +134,19 @@ __switch_to: __switch_to_base: tm __THREAD_per(%r3),0xe8 # new process is using per ? bz __switch_to_noper-__switch_to_base(%r1) # if not we're fine - stctl %c9,%c11,24(%r15) # We are using per stuff - clc __THREAD_per(12,%r3),24(%r15) + stctl %c9,%c11,__SF_EMPTY(%r15) # We are using per stuff + clc __THREAD_per(12,%r3),__SF_EMPTY(%r15) be __switch_to_noper-__switch_to_base(%r1) # we got away w/o bashing TLB's lctl %c9,%c11,__THREAD_per(%r3) # Nope we didn't __switch_to_noper: - stm %r6,%r15,24(%r15) # store __switch_to registers of prev task + stm %r6,%r15,__SF_GPRS(%r15)# store __switch_to registers of prev task st %r15,__THREAD_ksp(%r2) # store kernel stack to prev->tss.ksp l %r15,__THREAD_ksp(%r3) # load kernel stack from next->tss.ksp - lm %r6,%r15,24(%r15) # load __switch_to registers of next task + lm %r6,%r15,__SF_GPRS(%r15)# load __switch_to registers of next task st %r3,__LC_CURRENT # __LC_CURRENT = current task struct l %r3,__THREAD_info(%r3) # load thread_info from task struct st %r3,__LC_THREAD_INFO - ahi %r3,8192 + ahi %r3,STACK_SIZE st %r3,__LC_KERNEL_STACK # __LC_KERNEL_STACK = new kernel stack br %r14 @@ -146,24 +156,24 @@ __switch_to_noper: */ .global do_call_softirq do_call_softirq: - stnsm 24(%r15),0xfc - stm %r12,%r15,28(%r15) + stnsm __SF_EMPTY(%r15),0xfc + stm %r12,%r15,__SF_GPRS(%r15) lr %r12,%r15 basr %r13,0 do_call_base: l %r0,__LC_ASYNC_STACK slr %r0,%r15 - sra %r0,13 + sra %r0,STACK_SHIFT be 0f-do_call_base(%r13) l %r15,__LC_ASYNC_STACK 0: sl %r15,.Lc_overhead-do_call_base(%r13) - st %r12,0(%r15) # store backchain + st %r12,__SF_BACKCHAIN(%r15) # store backchain l %r1,.Ldo_softirq-do_call_base(%r13) basr %r14,%r1 - lm %r12,%r15,28(%r12) - ssm 24(%r15) + lm %r12,%r15,__SF_GPRS(%r12) + ssm __SF_EMPTY(%r15) br %r14 - + __critical_start: /* * SVC interrupt handler routine. System calls are synchronous events and @@ -304,9 +314,12 @@ sysc_tracenogo: ret_from_fork: l %r13,__LC_SVC_NEW_PSW+4 l %r9,__LC_THREAD_INFO # load pointer to thread_info struct - l %r1,BASED(.Lschedtail) + tm SP_PSW+1(%r15),0x01 # forking a kernel thread ? + bo BASED(0f) + st %r15,SP_R15(%r15) # store stack pointer for new kthread +0: l %r1,BASED(.Lschedtail) basr %r14,%r1 - stosm 24(%r15),0x03 # reenable interrupts + stosm __SF_EMPTY(%r15),0x03 # reenable interrupts b BASED(sysc_return) # @@ -457,7 +470,7 @@ pgm_svcper: mvc __THREAD_per+__PER_address(4,%r1),__LC_PER_ADDRESS mvc __THREAD_per+__PER_access_id(1,%r1),__LC_PER_ACCESS_ID oi __TI_flags+3(%r9),_TIF_SINGLE_STEP # set TIF_SINGLE_STEP - stosm 24(%r15),0x03 # reenable interrupts + stosm __SF_EMPTY(%r15),0x03 # reenable interrupts b BASED(sysc_do_svc) /* @@ -493,16 +506,16 @@ io_preempt: l %r1,SP_R15(%r15) s %r1,BASED(.Lc_spsize) mvc SP_PTREGS(__PT_SIZE,%r1),SP_PTREGS(%r15) - xc 0(4,%r1),0(%r1) # clear back chain + xc __SF_BACKCHAIN(4,%r1),__SF_BACKCHAIN(%r1) # clear back chain lr %r15,%r1 io_resume_loop: tm __TI_flags+3(%r9),_TIF_NEED_RESCHED bno BASED(io_leave) mvc __TI_precount(4,%r9),BASED(.Lc_pactive) - stosm 24(%r15),0x03 # reenable interrupts + stosm __SF_EMPTY(%r15),0x03 # reenable interrupts l %r1,BASED(.Lschedule) basr %r14,%r1 # call schedule - stnsm 24(%r15),0xfc # disable I/O and ext. interrupts + stnsm __SF_EMPTY(%r15),0xfc # disable I/O and ext. interrupts xc __TI_precount(4,%r9),__TI_precount(%r9) b BASED(io_resume_loop) #endif @@ -514,7 +527,7 @@ io_work: l %r1,__LC_KERNEL_STACK s %r1,BASED(.Lc_spsize) mvc SP_PTREGS(__PT_SIZE,%r1),SP_PTREGS(%r15) - xc 0(4,%r1),0(%r1) # clear back chain + xc __SF_BACKCHAIN(4,%r1),__SF_BACKCHAIN(%r1) # clear back chain lr %r15,%r1 # # One of the work bits is on. Find out which one. @@ -532,9 +545,9 @@ io_work_loop: # io_reschedule: l %r1,BASED(.Lschedule) - stosm 24(%r15),0x03 # reenable interrupts + stosm __SF_EMPTY(%r15),0x03 # reenable interrupts basr %r14,%r1 # call scheduler - stnsm 24(%r15),0xfc # disable I/O and ext. interrupts + stnsm __SF_EMPTY(%r15),0xfc # disable I/O and ext. interrupts tm __TI_flags+3(%r9),_TIF_WORK_INT bz BASED(io_leave) # there is no work to do b BASED(io_work_loop) @@ -543,12 +556,12 @@ io_reschedule: # _TIF_SIGPENDING is set, call do_signal # io_sigpending: - stosm 24(%r15),0x03 # reenable interrupts + stosm __SF_EMPTY(%r15),0x03 # reenable interrupts la %r2,SP_PTREGS(%r15) # load pt_regs sr %r3,%r3 # clear *oldset l %r1,BASED(.Ldo_signal) basr %r14,%r1 # call do_signal - stnsm 24(%r15),0xfc # disable I/O and ext. interrupts + stnsm __SF_EMPTY(%r15),0xfc # disable I/O and ext. interrupts b BASED(io_leave) # out of here, do NOT recheck /* @@ -590,7 +603,7 @@ restart_int_handler: lctl %c0,%c15,__LC_CREGS_SAVE_AREA # get new ctl regs lam %a0,%a15,__LC_AREGS_SAVE_AREA stosm 0(%r15),0x04 # now we can turn dat on - lm %r6,%r15,24(%r15) # load registers from clone + lm %r6,%r15,__SF_GPRS(%r15) # load registers from clone basr %r14,0 l %r14,restart_addr-.(%r14) br %r14 # branch to start_secondary @@ -611,6 +624,31 @@ restart_crash: restart_go: #endif +#ifdef CONFIG_CHECK_STACK +/* + * The synchronous or the asynchronous stack overflowed. We are dead. + * No need to properly save the registers, we are going to panic anyway. + * Setup a pt_regs so that show_trace can provide a good call trace. + */ +stack_overflow: + l %r15,__LC_PANIC_STACK # change to panic stack + sl %r15,BASED(.Lc_spsize) + mvc SP_PSW(8,%r15),0(%r12) # move user PSW to stack + stm %r0,%r11,SP_R0(%r15) # store gprs %r0-%r11 to kernel stack + la %r1,__LC_SAVE_AREA + ch %r12,BASED(.L0x020) # old psw addr == __LC_SVC_OLD_PSW ? + be BASED(0f) + ch %r12,BASED(.L0x028) # old psw addr == __LC_PGM_OLD_PSW ? + be BASED(0f) + la %r1,__LC_SAVE_AREA+16 +0: mvc SP_R12(16,%r15),0(%r1) # move %r12-%r15 to stack + xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15) # clear back chain + l %r1,BASED(1f) # branch to kernel_stack_overflow + la %r2,SP_PTREGS(%r15) # load pt_regs + br %r1 +1: .long kernel_stack_overflow +#endif + cleanup_table_system_call: .long system_call + 0x80000000, sysc_do_svc + 0x80000000 cleanup_table_sysc_return: --- linux-2.6.9-rc1/arch/s390/kernel/head64.S 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/s390/kernel/head64.S 2004-09-02 20:51:51.000000000 -0700 @@ -31,6 +31,8 @@ #include #include #include +#include +#include #ifndef CONFIG_IPL .org 0 @@ -741,10 +743,10 @@ _stext: basr %r13,0 larl %r15,init_thread_union lg %r14,__TI_task(%r15) # cache current in lowcore stg %r14,__LC_CURRENT - aghi %r15,16384 # init_task_union + 16384 + aghi %r15,1<<(PAGE_SHIFT+THREAD_ORDER) # init_task_union + THREAD_SIZE stg %r15,__LC_KERNEL_STACK # set end of kernel stack aghi %r15,-160 - xc 0(8,%r15),0(%r15) # set backchain to zero + xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15) # clear backchain # check control registers stctg %c0,%c15,0(%r15) --- linux-2.6.9-rc1/arch/s390/kernel/head.S 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/s390/kernel/head.S 2004-09-02 20:51:51.000000000 -0700 @@ -31,6 +31,8 @@ #include #include #include +#include +#include #ifndef CONFIG_IPL .org 0 @@ -741,10 +743,10 @@ _stext: basr %r13,0 # l %r15,.Linittu-.LPG2(%r13) mvc __LC_CURRENT(4),__TI_task(%r15) - ahi %r15,8192 # init_task_union + 8192 + ahi %r15,1<<(PAGE_SHIFT+THREAD_ORDER) # init_task_union + THREAD_SIZE st %r15,__LC_KERNEL_STACK # set end of kernel stack ahi %r15,-96 - xc 0(4,%r15),0(%r15) # set backchain to zero + xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15) # clear backchain # check control registers stctl %c0,%c15,0(%r15) --- linux-2.6.9-rc1/arch/s390/kernel/process.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/s390/kernel/process.c 2004-09-02 20:51:51.000000000 -0700 @@ -58,14 +58,11 @@ asmlinkage void ret_from_fork(void) __as */ unsigned long thread_saved_pc(struct task_struct *tsk) { - unsigned long bc; + struct stack_frame *sf; - bc = *((unsigned long *) tsk->thread.ksp); -#ifndef CONFIG_ARCH_S390X - return *((unsigned long *) (bc+56)); -#else - return *((unsigned long *) (bc+112)); -#endif + sf = (struct stack_frame *) tsk->thread.ksp; + sf = (struct stack_frame *) sf->back_chain; + return sf->gprs[8]; } /* @@ -186,41 +183,20 @@ void show_regs(struct pt_regs *regs) extern void kernel_thread_starter(void); -#ifndef CONFIG_ARCH_S390X - __asm__(".align 4\n" "kernel_thread_starter:\n" - " l 15,0(8)\n" - " sr 15,7\n" - " stosm 24(15),3\n" - " lr 2,10\n" + " la 2,0(10)\n" " basr 14,9\n" - " sr 2,2\n" + " la 2,0\n" " br 11\n"); -#else /* CONFIG_ARCH_S390X */ - -__asm__(".align 4\n" - "kernel_thread_starter:\n" - " lg 15,0(8)\n" - " sgr 15,7\n" - " stosm 48(15),3\n" - " lgr 2,10\n" - " basr 14,9\n" - " sgr 2,2\n" - " br 11\n"); - -#endif /* CONFIG_ARCH_S390X */ - int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) { struct pt_regs regs; memset(®s, 0, sizeof(regs)); - regs.psw.mask = PSW_KERNEL_BITS; + regs.psw.mask = PSW_KERNEL_BITS | PSW_MASK_IO | PSW_MASK_EXT; regs.psw.addr = (unsigned long) kernel_thread_starter | PSW_ADDR_AMODE; - regs.gprs[7] = STACK_FRAME_OVERHEAD + sizeof(struct pt_regs); - regs.gprs[8] = __LC_KERNEL_STACK; regs.gprs[9] = (unsigned long) fn; regs.gprs[10] = (unsigned long) arg; regs.gprs[11] = (unsigned long) do_exit; @@ -253,20 +229,13 @@ int copy_thread(int nr, unsigned long cl unsigned long unused, struct task_struct * p, struct pt_regs * regs) { - struct stack_frame + struct fake_frame { - unsigned long back_chain; - unsigned long eos; - unsigned long glue1; - unsigned long glue2; - unsigned long scratch[2]; - unsigned long gprs[10]; /* gprs 6 -15 */ - unsigned int fprs[4]; /* fpr 4 and 6 */ - unsigned int empty[4]; + struct stack_frame sf; struct pt_regs childregs; } *frame; - frame = ((struct stack_frame *) + frame = ((struct fake_frame *) (THREAD_SIZE + (unsigned long) p->thread_info)) - 1; p->thread.ksp = (unsigned long) frame; p->set_child_tid = p->clear_child_tid = NULL; @@ -274,13 +243,13 @@ int copy_thread(int nr, unsigned long cl frame->childregs = *regs; frame->childregs.gprs[2] = 0; /* child returns 0 on fork. */ frame->childregs.gprs[15] = new_stackp; - frame->back_chain = frame->eos = 0; + frame->sf.back_chain = 0; /* new return point is ret_from_fork */ - frame->gprs[8] = (unsigned long) ret_from_fork; + frame->sf.gprs[8] = (unsigned long) ret_from_fork; /* fake return stack for resume(), don't go back to schedule */ - frame->gprs[9] = (unsigned long) frame; + frame->sf.gprs[9] = (unsigned long) frame; /* Save access registers to new thread structure. */ save_access_regs(&p->thread.acrs[0]); @@ -336,7 +305,7 @@ asmlinkage long sys_clone(struct pt_regs child_tidptr = (int __user *) regs.gprs[5]; if (!newsp) newsp = regs.gprs[15]; - return do_fork(clone_flags & ~CLONE_IDLETASK, newsp, ®s, 0, + return do_fork(clone_flags, newsp, ®s, 0, parent_tidptr, child_tidptr); } @@ -423,30 +392,26 @@ void dump_thread(struct pt_regs * regs, unsigned long get_wchan(struct task_struct *p) { - unsigned long r14, r15, bc; - unsigned long stack_page; - int count = 0; - if (!p || p == current || p->state == TASK_RUNNING) + struct stack_frame *sf, *low, *high; + unsigned long return_address; + int count; + + if (!p || p == current || p->state == TASK_RUNNING || !p->thread_info) return 0; - stack_page = (unsigned long) p->thread_info; - r15 = p->thread.ksp; - if (!stack_page || r15 < stack_page || - r15 >= THREAD_SIZE - sizeof(unsigned long) + stack_page) + low = (struct stack_frame *) p->thread_info; + high = (struct stack_frame *) + ((unsigned long) p->thread_info + THREAD_SIZE) - 1; + sf = (struct stack_frame *) (p->thread.ksp & PSW_ADDR_INSN); + if (sf <= low || sf > high) return 0; - bc = (*(unsigned long *) r15) & PSW_ADDR_INSN; - do { - if (bc < stack_page || - bc >= THREAD_SIZE - sizeof(unsigned long) + stack_page) + for (count = 0; count < 16; count++) { + sf = (struct stack_frame *) (sf->back_chain & PSW_ADDR_INSN); + if (sf <= low || sf > high) return 0; -#ifndef CONFIG_ARCH_S390X - r14 = (*(unsigned long *) (bc+56)) & PSW_ADDR_INSN; -#else - r14 = *(unsigned long *) (bc+112); -#endif - if (!in_sched_functions(r14)) - return r14; - bc = (*(unsigned long *) bc) & PSW_ADDR_INSN; - } while (count++ < 16); + return_address = sf->gprs[8] & PSW_ADDR_INSN; + if (!in_sched_functions(return_address)) + return return_address; + } return 0; } --- linux-2.6.9-rc1/arch/s390/kernel/profile.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/arch/s390/kernel/profile.c 2004-09-02 20:51:51.000000000 -0700 @@ -6,52 +6,15 @@ * */ #include +#include static struct proc_dir_entry * root_irq_dir; -static int prof_cpu_mask_read_proc (char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - int len = cpumask_scnprintf(page, count, *(cpumask_t *)data); - if (count - len < 2) - return -EINVAL; - len += sprintf(page + len, "\n"); - return len; -} - -static int prof_cpu_mask_write_proc (struct file *file, - const char __user *buffer, - unsigned long count, void *data) -{ - cpumask_t *mask = (cpumask_t *)data; - unsigned long full_count = count, err; - cpumask_t new_value; - - err = cpumask_parse(buffer, count, new_value); - if (err) - return err; - - *mask = new_value; - return full_count; -} - -cpumask_t prof_cpu_mask = CPU_MASK_ALL; - void init_irq_proc(void) { - struct proc_dir_entry *entry; - /* create /proc/irq */ root_irq_dir = proc_mkdir("irq", 0); /* create /proc/irq/prof_cpu_mask */ - entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); - - if (!entry) - return; - - entry->nlink = 1; - entry->data = (void *)&prof_cpu_mask; - entry->read_proc = prof_cpu_mask_read_proc; - entry->write_proc = prof_cpu_mask_write_proc; + create_prof_cpu_mask(root_irq_dir); } --- linux-2.6.9-rc1/arch/s390/kernel/setup.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/s390/kernel/setup.c 2004-09-02 20:51:51.000000000 -0700 @@ -503,6 +503,10 @@ void __init setup_arch(char **cmdline_p) lc->kernel_stack = ((unsigned long) &init_thread_union) + THREAD_SIZE; lc->async_stack = (unsigned long) __alloc_bootmem(ASYNC_SIZE, ASYNC_SIZE, 0) + ASYNC_SIZE; +#ifdef CONFIG_CHECK_STACK + lc->panic_stack = (unsigned long) + __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, 0) + PAGE_SIZE; +#endif lc->current_task = (unsigned long) init_thread_union.thread_info.task; lc->thread_info = (unsigned long) &init_thread_union; #ifdef CONFIG_ARCH_S390X --- linux-2.6.9-rc1/arch/s390/kernel/signal.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/s390/kernel/signal.c 2004-09-02 20:51:51.000000000 -0700 @@ -358,9 +358,7 @@ static void setup_frame(int sig, struct return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, @@ -414,9 +412,7 @@ static void setup_rt_frame(int sig, stru return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } /* @@ -424,20 +420,15 @@ give_sigsegv: */ static void -handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset, - struct pt_regs * regs) +handle_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs) { - struct k_sigaction *ka = ¤t->sighand->action[sig-1]; - /* Set up the stack frame */ if (ka->sa.sa_flags & SA_SIGINFO) setup_rt_frame(sig, ka, info, oldset, regs); else setup_frame(sig, ka, oldset, regs); - if (ka->sa.sa_flags & SA_ONESHOT) - ka->sa.sa_handler = SIG_DFL; - if (!(ka->sa.sa_flags & SA_NODEFER)) { spin_lock_irq(¤t->sighand->siglock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); @@ -461,6 +452,7 @@ int do_signal(struct pt_regs *regs, sigs unsigned long retval = 0, continue_addr = 0, restart_addr = 0; siginfo_t info; int signr; + struct k_sigaction ka; /* * We want the common case to go fast, which @@ -494,7 +486,7 @@ int do_signal(struct pt_regs *regs, sigs /* Get signal to deliver. When running under ptrace, at this point the debugger may change all our registers ... */ - signr = get_signal_to_deliver(&info, regs, NULL); + signr = get_signal_to_deliver(&info, &ka, regs, NULL); /* Depending on the signal settings we may need to revert the decision to restart the system call. */ @@ -513,14 +505,15 @@ int do_signal(struct pt_regs *regs, sigs #ifdef CONFIG_S390_SUPPORT if (test_thread_flag(TIF_31BIT)) { extern void handle_signal32(unsigned long sig, + struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset, struct pt_regs *regs); - handle_signal32(signr, &info, oldset, regs); + handle_signal32(signr, &ka, &info, oldset, regs); return 1; } #endif - handle_signal(signr, &info, oldset, regs); + handle_signal(signr, &ka, &info, oldset, regs); return 1; } --- linux-2.6.9-rc1/arch/s390/kernel/smp.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/arch/s390/kernel/smp.c 2004-09-03 00:56:52.902118304 -0700 @@ -562,21 +562,15 @@ int __devinit start_secondary(void *cpuv static void __init smp_create_idle(unsigned int cpu) { - struct pt_regs regs; struct task_struct *p; /* * don't care about the psw and regs settings since we'll never * reschedule the forked task. */ - memset(®s, 0, sizeof(struct pt_regs)); - p = copy_process(CLONE_VM | CLONE_IDLETASK, 0, ®s, 0, NULL, NULL); + p = fork_idle(cpu); if (IS_ERR(p)) panic("failed fork for CPU %u: %li", cpu, PTR_ERR(p)); - - wake_up_forked_process(p); - init_idle(p, cpu); - unhash_process(p); current_set[cpu] = p; } @@ -688,13 +682,15 @@ __cpu_disable(void) { unsigned long flags; ec_creg_mask_parms cr_parms; + int cpu = smp_processor_id(); spin_lock_irqsave(&smp_reserve_lock, flags); - if (smp_cpu_reserved[smp_processor_id()] != 0) { + if (smp_cpu_reserved[cpu] != 0) { spin_unlock_irqrestore(&smp_reserve_lock, flags); return -EBUSY; } + cpu_clear(cpu, cpu_online_map); /* disable all external interrupts */ cr_parms.start_ctl = 0; @@ -747,7 +743,7 @@ cpu_die(void) void __init smp_prepare_cpus(unsigned int max_cpus) { - unsigned long async_stack; + unsigned long stack; unsigned int cpu; int i; @@ -767,12 +763,18 @@ void __init smp_prepare_cpus(unsigned in lowcore_ptr[i] = (struct _lowcore *) __get_free_pages(GFP_KERNEL|GFP_DMA, sizeof(void*) == 8 ? 1 : 0); - async_stack = __get_free_pages(GFP_KERNEL,ASYNC_ORDER); - if (lowcore_ptr[i] == NULL || async_stack == 0ULL) + stack = __get_free_pages(GFP_KERNEL,ASYNC_ORDER); + if (lowcore_ptr[i] == NULL || stack == 0ULL) panic("smp_boot_cpus failed to allocate memory\n"); *(lowcore_ptr[i]) = S390_lowcore; - lowcore_ptr[i]->async_stack = async_stack + (ASYNC_SIZE); + lowcore_ptr[i]->async_stack = stack + (ASYNC_SIZE); +#ifdef CONFIG_CHECK_STACK + stack = __get_free_pages(GFP_KERNEL,0); + if (stack == 0ULL) + panic("smp_boot_cpus failed to allocate memory\n"); + lowcore_ptr[i]->panic_stack = stack + (PAGE_SIZE); +#endif } set_prefix((u32)(unsigned long) lowcore_ptr[smp_processor_id()]); --- linux-2.6.9-rc1/arch/s390/kernel/time.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/arch/s390/kernel/time.c 2004-09-02 20:51:51.000000000 -0700 @@ -174,47 +174,7 @@ __calculate_ticks(__u64 elapsed) #ifdef CONFIG_PROFILING -extern char _stext, _etext; - -/* - * The profiling function is SMP safe. (nothing can mess - * around with "current", and the profiling counters are - * updated with atomic operations). This is especially - * useful with a profiling multiplier != 1 - */ -static inline void s390_do_profile(struct pt_regs * regs) -{ - unsigned long eip; - extern cpumask_t prof_cpu_mask; - - profile_hook(regs); - - if (user_mode(regs)) - return; - - if (!prof_buffer) - return; - - eip = instruction_pointer(regs); - - /* - * Only measure the CPUs specified by /proc/irq/prof_cpu_mask. - * (default is all CPUs.) - */ - if (!cpu_isset(smp_processor_id(), prof_cpu_mask)) - return; - - eip -= (unsigned long) &_stext; - eip >>= prof_shift; - /* - * Don't ignore out-of-bounds EIP values silently, - * put them into the last histogram slot, so if - * present, they will show up as a sharp peak. - */ - if (eip > prof_len-1) - eip = prof_len-1; - atomic_inc((atomic_t *)&prof_buffer[eip]); -} +#define s390_do_profile(regs) profile_tick(CPU_PROFILING, regs) #else #define s390_do_profile(regs) do { ; } while(0) #endif /* CONFIG_PROFILING */ --- linux-2.6.9-rc1/arch/s390/kernel/traps.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/s390/kernel/traps.c 2004-09-02 20:51:51.000000000 -0700 @@ -67,54 +67,93 @@ extern pgm_check_handler_t do_monitor_ca #define stack_pointer ({ void **sp; asm("la %0,0(15)" : "=&d" (sp)); sp; }) #ifndef CONFIG_ARCH_S390X -#define RET_ADDR 56 #define FOURLONG "%08lx %08lx %08lx %08lx\n" static int kstack_depth_to_print = 12; - #else /* CONFIG_ARCH_S390X */ -#define RET_ADDR 112 #define FOURLONG "%016lx %016lx %016lx %016lx\n" static int kstack_depth_to_print = 20; - #endif /* CONFIG_ARCH_S390X */ -void show_trace(struct task_struct *task, unsigned long * stack) +/* + * For show_trace we have tree different stack to consider: + * - the panic stack which is used if the kernel stack has overflown + * - the asynchronous interrupt stack (cpu related) + * - the synchronous kernel stack (process related) + * The stack trace can start at any of the three stack and can potentially + * touch all of them. The order is: panic stack, async stack, sync stack. + */ +static unsigned long +__show_trace(unsigned long sp, unsigned long low, unsigned long high) { - unsigned long backchain, low_addr, high_addr, ret_addr; + struct stack_frame *sf; + struct pt_regs *regs; - if (!stack) - stack = (task == NULL) ? *stack_pointer : &(task->thread.ksp); + while (1) { + sp = sp & PSW_ADDR_INSN; + if (sp < low || sp > high - sizeof(*sf)) + return sp; + sf = (struct stack_frame *) sp; + printk("([<%016lx>] ", sf->gprs[8] & PSW_ADDR_INSN); + print_symbol("%s)\n", sf->gprs[8] & PSW_ADDR_INSN); + /* Follow the backchain. */ + while (1) { + low = sp; + sp = sf->back_chain & PSW_ADDR_INSN; + if (!sp) + break; + if (sp <= low || sp > high - sizeof(*sf)) + return sp; + sf = (struct stack_frame *) sp; + printk(" [<%016lx>] ", sf->gprs[8] & PSW_ADDR_INSN); + print_symbol("%s\n", sf->gprs[8] & PSW_ADDR_INSN); + } + /* Zero backchain detected, check for interrupt frame. */ + sp = (unsigned long) (sf + 1); + if (sp <= low || sp > high - sizeof(*regs)) + return sp; + regs = (struct pt_regs *) sp; + printk(" [<%016lx>] ", regs->psw.addr & PSW_ADDR_INSN); + print_symbol("%s\n", regs->psw.addr & PSW_ADDR_INSN); + low = sp; + sp = regs->gprs[15]; + } +} +void show_trace(struct task_struct *task, unsigned long * stack) +{ + register unsigned long __r15 asm ("15"); + unsigned long sp; + + sp = (unsigned long) stack; + if (!sp) + sp = task ? task->thread.ksp : __r15; printk("Call Trace:\n"); - low_addr = ((unsigned long) stack) & PSW_ADDR_INSN; - high_addr = (low_addr & (-THREAD_SIZE)) + THREAD_SIZE; - /* Skip the first frame (biased stack) */ - backchain = *((unsigned long *) low_addr) & PSW_ADDR_INSN; - /* Print up to 8 lines */ - while (backchain > low_addr && backchain <= high_addr) { - ret_addr = *((unsigned long *) (backchain+RET_ADDR)) & PSW_ADDR_INSN; - printk(" [<%016lx>] ", ret_addr); - print_symbol("%s\n", ret_addr); - low_addr = backchain; - backchain = *((unsigned long *) backchain) & PSW_ADDR_INSN; - } +#ifdef CONFIG_CHECK_STACK + sp = __show_trace(sp, S390_lowcore.panic_stack - 4096, + S390_lowcore.panic_stack); +#endif + sp = __show_trace(sp, S390_lowcore.async_stack - ASYNC_SIZE, + S390_lowcore.async_stack); + if (task) + __show_trace(sp, (unsigned long) task->thread_info, + (unsigned long) task->thread_info + THREAD_SIZE); + else + __show_trace(sp, S390_lowcore.thread_info - THREAD_SIZE, + S390_lowcore.thread_info); printk("\n"); } void show_stack(struct task_struct *task, unsigned long *sp) { + register unsigned long * __r15 asm ("15"); unsigned long *stack; int i; // debugging aid: "show_stack(NULL);" prints the // back trace for this cpu. - if (!sp) { - if (task) - sp = (unsigned long *) task->thread.ksp; - else - sp = *stack_pointer; - } + if (!sp) + sp = task ? (unsigned long *) task->thread.ksp : __r15; stack = sp; for (i = 0; i < kstack_depth_to_print; i++) { @@ -591,6 +630,11 @@ asmlinkage void data_exception(struct pt } } +asmlinkage void kernel_stack_overflow(struct pt_regs * regs) +{ + die("Kernel stack overflow", regs, 0); + panic("Corrupt kernel stack, can't continue."); +} /* init is done in lowcore.S and head.S */ --- linux-2.6.9-rc1/arch/s390/kernel/vmlinux.lds.S 2004-08-24 00:02:26.000000000 -0700 +++ 25/arch/s390/kernel/vmlinux.lds.S 2004-09-02 20:51:51.000000000 -0700 @@ -78,9 +78,6 @@ SECTIONS __setup_start = .; .init.setup : { *(.init.setup) } __setup_end = .; - __start___param = .; - __param : { *(__param) } - __stop___param = .; __initcall_start = .; .initcall.init : { *(.initcall1.init) --- linux-2.6.9-rc1/arch/s390/kernel/vtime.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/s390/kernel/vtime.c 2004-09-02 20:51:51.000000000 -0700 @@ -21,7 +21,7 @@ #include #include -#define VTIMER_MAGIC (0x4b87ad6e + 1) +#define VTIMER_MAGIC (TIMER_MAGIC + 1) static ext_int_info_t ext_int_info_timer; DEFINE_PER_CPU(struct vtimer_queue, virt_cpu_timer); --- linux-2.6.9-rc1/arch/s390/Makefile 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/s390/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -18,6 +18,7 @@ LDFLAGS := -m elf_s390 CFLAGS += -m31 AFLAGS += -m31 UTS_MACHINE := s390 +STACK_SIZE := 8192 endif ifdef CONFIG_ARCH_S390X @@ -26,16 +27,37 @@ MODFLAGS += -fpic -D__PIC__ CFLAGS += -m64 AFLAGS += -m64 UTS_MACHINE := s390x +STACK_SIZE := 16384 endif cflags-$(CONFIG_MARCH_G5) += $(call cc-option,-march=g5) cflags-$(CONFIG_MARCH_Z900) += $(call cc-option,-march=z900) cflags-$(CONFIG_MARCH_Z990) += $(call cc-option,-march=z990) -CFLAGS += $(cflags-y) +ifeq ($(call cc-option-yn,-mkernel-backchain),y) +cflags-$(CONFIG_PACK_STACK) += -mkernel-backchain -D__PACK_STACK +aflags-$(CONFIG_PACK_STACK) += -D__PACK_STACK +cflags-$(CONFIG_SMALL_STACK) += -D__SMALL_STACK +aflags-$(CONFIG_SMALL_STACK) += -D__SMALL_STACK +ifdef CONFIG_SMALL_STACK +STACK_SIZE := $(shell echo $$(($(STACK_SIZE)/2)) ) +endif +endif + +ifeq ($(call cc-option-yn,-mstack-size=8192 -mstack-guard=128),y) +cflags-$(CONFIG_CHECK_STACK) += -mstack-size=$(STACK_SIZE) +cflags-$(CONFIG_CHECK_STACK) += -mstack-guard=$(CONFIG_STACK_GUARD) +endif + +ifeq ($(call cc-option-yn,-mwarn-dynamicstack),y) +cflags-$(CONFIG_WARN_STACK) += -mwarn-dynamicstack +cflags-$(CONFIG_WARN_STACK) += -mwarn-framesize=$(CONFIG_WARN_STACK_SIZE) +endif + +CFLAGS += -mbackchain $(cflags-y) CFLAGS += $(call cc-option,-finline-limit=10000) CFLAGS += -pipe -fno-strength-reduce -Wno-sign-compare -CFLAGS += -mbackchain +AFLAGS += $(aflags-y) OBJCOPYFLAGS := -O binary LDFLAGS_vmlinux := -e start --- linux-2.6.9-rc1/arch/s390/mm/fault.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/arch/s390/mm/fault.c 2004-09-03 00:56:40.997928016 -0700 @@ -25,11 +25,11 @@ #include #include #include +#include #include #include #include -#include #ifndef CONFIG_ARCH_S390X #define __FAIL_ADDR_MASK 0x7ffff000 @@ -126,8 +126,8 @@ static inline int check_user_space(struc * Send SIGSEGV to task. This is an external routine * to keep the stack usage of do_page_fault small. */ -static void force_sigsegv(struct pt_regs *regs, unsigned long error_code, - int si_code, unsigned long address) +static void do_sigsegv(struct pt_regs *regs, unsigned long error_code, + int si_code, unsigned long address) { struct siginfo si; @@ -282,7 +282,7 @@ bad_area: if (regs->psw.mask & PSW_MASK_PSTATE) { tsk->thread.prot_addr = address; tsk->thread.trap_no = error_code; - force_sigsegv(regs, error_code, si_code, address); + do_sigsegv(regs, error_code, si_code, address); return; } --- linux-2.6.9-rc1/arch/s390/mm/Makefile 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/s390/mm/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -2,6 +2,6 @@ # Makefile for the linux s390-specific parts of the memory manager. # -obj-y := init.o fault.o ioremap.o extmem.o +obj-y := init.o fault.o ioremap.o extmem.o mmap.o obj-$(CONFIG_CMM) += cmm.o --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/s390/mm/mmap.c 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,83 @@ +/* + * linux/arch/s390/mm/mmap.c + * + * flexible mmap layout support + * + * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Started by Ingo Molnar + */ + +#include +#include + +/* + * Top of mmap area (just below the process stack). + * + * Leave an at least ~128 MB hole. + */ +#define MIN_GAP (128*1024*1024) +#define MAX_GAP (TASK_SIZE/6*5) + +static inline unsigned long mmap_base(void) +{ + unsigned long gap = current->rlim[RLIMIT_STACK].rlim_cur; + + if (gap < MIN_GAP) + gap = MIN_GAP; + else if (gap > MAX_GAP) + gap = MAX_GAP; + + return TASK_SIZE - (gap & PAGE_MASK); +} + +static inline int mmap_is_legacy(void) +{ +#ifdef CONFIG_ARCH_S390X + /* + * Force standard allocation for 64 bit programs. + */ + if (!test_thread_flag(TIF_31BIT)) + return 1; +#endif + return sysctl_legacy_va_layout || + (current->personality & ADDR_COMPAT_LAYOUT) || + current->rlim[RLIMIT_STACK].rlim_cur == RLIM_INFINITY; +} + +/* + * This function, called very early during the creation of a new + * process VM image, sets up which VM layout function to use: + */ +void arch_pick_mmap_layout(struct mm_struct *mm) +{ + /* + * Fall back to the standard layout if the personality + * bit is set, or if the expected stack growth is unlimited: + */ + if (mmap_is_legacy()) { + mm->mmap_base = TASK_UNMAPPED_BASE; + mm->get_unmapped_area = arch_get_unmapped_area; + mm->unmap_area = arch_unmap_area; + } else { + mm->mmap_base = mmap_base(); + mm->get_unmapped_area = arch_get_unmapped_area_topdown; + mm->unmap_area = arch_unmap_area_topdown; + } +} --- linux-2.6.9-rc1/arch/sh64/kernel/irq.c 2004-08-24 00:02:20.000000000 -0700 +++ 25/arch/sh64/kernel/irq.c 2004-09-02 20:51:51.000000000 -0700 @@ -148,6 +148,7 @@ asmlinkage void do_NMI(unsigned long vec int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * action) { int status; + int ret; status = 1; /* Force the "do bottom halves" bit */ @@ -155,8 +156,9 @@ int handle_IRQ_event(unsigned int irq, s local_irq_enable(); do { - status |= action->flags; - action->handler(irq, action->dev_id, regs); + ret = action->handler(irq, action->dev_id, regs); + if (ret == IRQ_HANDLED) + status |= action->flags; action = action->next; } while (action); if (status & SA_SAMPLE_RANDOM) --- linux-2.6.9-rc1/arch/sh64/kernel/process.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/arch/sh64/kernel/process.c 2004-09-02 20:51:51.000000000 -0700 @@ -820,7 +820,7 @@ asmlinkage int sys_clone(unsigned long c { if (!newsp) newsp = pregs->regs[15]; - return do_fork(clone_flags & ~CLONE_IDLETASK, newsp, pregs, 0, 0, 0); + return do_fork(clone_flags, newsp, pregs, 0, 0, 0); } /* @@ -901,7 +901,7 @@ unsigned long get_wchan(struct task_stru */ pc = thread_saved_pc(p); -#if CONFIG_FRAME_POINTER +#ifdef CONFIG_FRAME_POINTER if (in_sh64_switch_to(pc)) { sh64_switch_to_fp = (long) p->thread.sp; /* r14 is saved at offset 4 in the sh64_switch_to frame */ --- linux-2.6.9-rc1/arch/sh64/kernel/ptrace.c 2004-08-24 00:03:17.000000000 -0700 +++ 25/arch/sh64/kernel/ptrace.c 2004-09-03 00:56:56.200616856 -0700 @@ -311,11 +311,8 @@ asmlinkage void syscall_trace(void) if (!(tsk->ptrace & PT_PTRACED)) return; - tsk->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0); - tsk->state = TASK_STOPPED; - notify_parent(tsk, SIGCHLD); - schedule(); + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); /* * this isn't the same as continuing with a signal, but it will do * for normal use. strace only continues with a signal if the --- linux-2.6.9-rc1/arch/sh64/kernel/sh_ksyms.c 2004-08-24 00:02:19.000000000 -0700 +++ 25/arch/sh64/kernel/sh_ksyms.c 2004-09-03 00:56:40.997928016 -0700 @@ -25,7 +25,6 @@ #include #include #include -#include #include #include --- linux-2.6.9-rc1/arch/sh64/kernel/signal.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/sh64/kernel/signal.c 2004-09-02 20:51:51.000000000 -0700 @@ -520,9 +520,7 @@ static void setup_frame(int sig, struct return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, @@ -628,9 +626,7 @@ static void setup_rt_frame(int sig, stru return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } /* --- linux-2.6.9-rc1/arch/sh64/kernel/time.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/arch/sh64/kernel/time.c 2004-09-02 20:51:51.000000000 -0700 @@ -298,37 +298,6 @@ static int set_rtc_time(unsigned long no /* last time the RTC clock got updated */ static long last_rtc_update = 0; -static inline void sh64_do_profile(struct pt_regs *regs) -{ - extern int _stext; - unsigned long pc; - - profile_hook(regs); - - if (user_mode(regs)) - return; - - /* Don't profile cpu_idle.. */ - if (!prof_buffer || !current->pid) - return; - - pc = instruction_pointer(regs); - pc -= (unsigned long) &_stext; - pc >>= prof_shift; - - /* - * Don't ignore out-of-bounds PC values silently, put them into the - * last histogram slot, so if present, they will show up as a sharp - * peak. - */ - if (pc > prof_len - 1) - pc = prof_len - 1; - - /* We could just be sloppy and not lock against a re-entry on this - increment, but the profiling code won't always be linked in anyway. */ - atomic_inc((atomic_t *)&prof_buffer[pc]); -} - /* * timer_interrupt() needs to keep up the real-time clock, * as well as call the "do_timer()" routine every clocktick @@ -340,8 +309,7 @@ static inline void do_timer_interrupt(in ctc_last_interrupt = (unsigned long) current_ctc; do_timer(regs); - - sh64_do_profile(regs); + profile_tick(CPU_PROFILING, regs); #ifdef CONFIG_HEARTBEAT { --- linux-2.6.9-rc1/arch/sh64/kernel/vmlinux.lds.S 2004-08-24 00:02:19.000000000 -0700 +++ 25/arch/sh64/kernel/vmlinux.lds.S 2004-09-02 20:51:51.000000000 -0700 @@ -106,9 +106,6 @@ SECTIONS __setup_start = .; .init.setup : C_PHYS(.init.setup) { *(.init.setup) } __setup_end = .; - __start___param = .; - __param : C_PHYS(__param) { *(__param) } - __stop___param = .; __initcall_start = .; .initcall.init : C_PHYS(.initcall.init) { *(.initcall1.init) --- linux-2.6.9-rc1/arch/sh64/mm/fault.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/sh64/mm/fault.c 2004-09-03 00:56:40.998927864 -0700 @@ -30,7 +30,6 @@ #include #include #include -#include #include #include /* required by inline asm statements */ --- linux-2.6.9-rc1/arch/sh64/mm/init.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/arch/sh64/mm/init.c 2004-09-02 20:51:51.000000000 -0700 @@ -122,8 +122,8 @@ void __init paging_init(void) * All memory is good as ZONE_NORMAL (fall-through) and ZONE_DMA. */ zones_size[ZONE_DMA] = MAX_LOW_PFN - START_PFN; - - free_area_init_node(0, NODE_DATA(0), 0, zones_size, __MEMORY_START >> PAGE_SHIFT, 0); + NODE_DATA(0)->node_mem_map = NULL; + free_area_init_node(0, NODE_DATA(0), zones_size, __MEMORY_START >> PAGE_SHIFT, 0); /* XXX: MRB-remove - this doesn't seem sane, should this be done somewhere else ?*/ mem_map = NODE_DATA(0)->node_mem_map; --- linux-2.6.9-rc1/arch/sh64/mm/tlbmiss.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/sh64/mm/tlbmiss.c 2004-09-03 00:56:40.998927864 -0700 @@ -40,7 +40,6 @@ #include #include #include -#include #include #include /* required by inline asm statements */ --- linux-2.6.9-rc1/arch/sh/kernel/irq.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/arch/sh/kernel/irq.c 2004-09-02 20:51:51.000000000 -0700 @@ -137,14 +137,16 @@ unlock: int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * action) { int status = 1; /* Force the "do bottom halves" bit */ - int retval = 0; + int ret, retval = 0; if (!(action->flags & SA_INTERRUPT)) local_irq_enable(); do { - status |= action->flags; - retval |= action->handler(irq, action->dev_id, regs); + ret = action->handler(irq, action->dev_id, regs); + if (ret == IRQ_HANDLED) + status |= action->flags; + retval |= ret; action = action->next; } while (action); --- linux-2.6.9-rc1/arch/sh/kernel/process.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/arch/sh/kernel/process.c 2004-09-02 20:51:51.000000000 -0700 @@ -440,7 +440,7 @@ asmlinkage int sys_clone(unsigned long c { if (!newsp) newsp = regs.regs[15]; - return do_fork(clone_flags & ~CLONE_IDLETASK, newsp, ®s, 0, + return do_fork(clone_flags, newsp, ®s, 0, (int __user *)parent_tidptr, (int __user *)child_tidptr); } --- linux-2.6.9-rc1/arch/sh/kernel/sh_ksyms.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/sh/kernel/sh_ksyms.c 2004-09-03 00:56:40.999927712 -0700 @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include --- linux-2.6.9-rc1/arch/sh/kernel/signal.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/arch/sh/kernel/signal.c 2004-09-02 20:51:51.000000000 -0700 @@ -413,9 +413,7 @@ static void setup_frame(int sig, struct return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, @@ -490,9 +488,7 @@ static void setup_rt_frame(int sig, stru return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } /* --- linux-2.6.9-rc1/arch/sh/kernel/smp.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/sh/kernel/smp.c 2004-09-03 00:56:40.999927712 -0700 @@ -26,7 +26,6 @@ #include #include #include -#include #include #include @@ -98,19 +97,12 @@ void __devinit smp_prepare_boot_cpu(void int __cpu_up(unsigned int cpu) { struct task_struct *tsk; - struct pt_regs regs; - memset(®s, 0, sizeof(struct pt_regs)); - tsk = copy_process(CLONE_VM | CLONE_IDLETASK, 0, ®s, 0, 0, 0); + tsk = fork_idle(cpu); if (IS_ERR(tsk)) panic("Failed forking idle task for cpu %d\n", cpu); - wake_up_forked_process(tsk); - - init_idle(tsk, cpu); - unhash_process(tsk); - tsk->thread_info->cpu = cpu; cpu_set(cpu, cpu_online_map); --- linux-2.6.9-rc1/arch/sh/kernel/time.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/arch/sh/kernel/time.c 2004-09-02 20:51:51.000000000 -0700 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -250,36 +251,6 @@ EXPORT_SYMBOL(do_settimeofday); /* last time the RTC clock got updated */ static long last_rtc_update; -/* Profiling definitions */ -extern unsigned long prof_cpu_mask; -extern unsigned int * prof_buffer; -extern unsigned long prof_len; -extern unsigned long prof_shift; -extern char _stext; - -static inline void sh_do_profile(unsigned long pc) -{ - /* Don't profile cpu_idle.. */ - if (!prof_buffer || !current->pid) - return; - - if (pc >= 0xa0000000UL && pc < 0xc0000000UL) - pc -= 0x20000000; - - pc -= (unsigned long)&_stext; - pc >>= prof_shift; - - /* - * Don't ignore out-of-bounds PC values silently, - * put them into the last histogram slot, so if - * present, they will show up as a sharp peak. - */ - if (pc > prof_len - 1) - pc = prof_len - 1; - - atomic_inc((atomic_t *)&prof_buffer[pc]); -} - /* * timer_interrupt() needs to keep up the real-time clock, * as well as call the "do_timer()" routine every clocktick @@ -287,9 +258,7 @@ static inline void sh_do_profile(unsigne static inline void do_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { do_timer(regs); - - if (!user_mode(regs)) - sh_do_profile(regs->pc); + profile_tick(CPU_PROFILING, regs); #ifdef CONFIG_HEARTBEAT if (sh_mv.mv_heartbeat != NULL) --- linux-2.6.9-rc1/arch/sh/kernel/vmlinux.lds.S 2004-08-24 00:02:48.000000000 -0700 +++ 25/arch/sh/kernel/vmlinux.lds.S 2004-09-02 20:51:51.000000000 -0700 @@ -75,9 +75,6 @@ SECTIONS __setup_start = .; .init.setup : { *(.init.setup) } __setup_end = .; - __start___param = .; - __param : { *(__param) } - __stop___param = .; __initcall_start = .; .initcall.init : { *(.initcall1.init) --- linux-2.6.9-rc1/arch/sh/mm/fault.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/sh/mm/fault.c 2004-09-03 00:56:41.000927560 -0700 @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include --- linux-2.6.9-rc1/arch/sh/mm/fault-nommu.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/arch/sh/mm/fault-nommu.c 2004-09-03 00:56:41.000927560 -0700 @@ -26,7 +26,6 @@ #include #include #include -#include #include #include --- linux-2.6.9-rc1/arch/sh/mm/init.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/sh/mm/init.c 2004-09-02 20:51:51.000000000 -0700 @@ -214,8 +214,8 @@ void __init paging_init(void) */ disable_mmu(); #endif - - free_area_init_node(0, NODE_DATA(0), 0, zones_size, __MEMORY_START >> PAGE_SHIFT, 0); + NODE_DATA(0)->node_mem_map = NULL; + free_area_init_node(0, NODE_DATA(0), zones_size, __MEMORY_START >> PAGE_SHIFT, 0); /* XXX: MRB-remove - this doesn't seem sane, should this be done somewhere else ?*/ mem_map = NODE_DATA(0)->node_mem_map; @@ -225,7 +225,7 @@ void __init paging_init(void) */ zones_size[ZONE_DMA] = __MEMORY_SIZE_2ND >> PAGE_SHIFT; zones_size[ZONE_NORMAL] = 0; - free_area_init_node(1, NODE_DATA(1), 0, zones_size, __MEMORY_START_2ND >> PAGE_SHIFT, 0); + free_area_init_node(1, NODE_DATA(1), zones_size, __MEMORY_START_2ND >> PAGE_SHIFT, 0); #endif } --- linux-2.6.9-rc1/arch/sh/mm/tlb-sh3.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/sh/mm/tlb-sh3.c 2004-09-03 00:56:41.000927560 -0700 @@ -25,7 +25,6 @@ #include #include #include -#include #include #include --- linux-2.6.9-rc1/arch/sh/mm/tlb-sh4.c 2004-08-24 00:03:14.000000000 -0700 +++ 25/arch/sh/mm/tlb-sh4.c 2004-09-03 00:56:41.000927560 -0700 @@ -25,7 +25,6 @@ #include #include #include -#include #include #include --- linux-2.6.9-rc1/arch/sparc64/defconfig 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/sparc64/defconfig 2004-09-02 20:51:51.000000000 -0700 @@ -1,5 +1,7 @@ # # Automatically generated make config: don't edit +# Linux kernel version: 2.6.9-rc1 +# Wed Sep 1 17:54:49 2004 # CONFIG_64BIT=y CONFIG_MMU=y @@ -33,6 +35,8 @@ CONFIG_IOSCHED_AS=y CONFIG_IOSCHED_DEADLINE=y CONFIG_IOSCHED_CFQ=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SHMEM=y +# CONFIG_TINY_SHMEM is not set # # Loadable module support @@ -46,7 +50,7 @@ CONFIG_KMOD=y CONFIG_STOP_MACHINE=y # -# General setup +# General machine setup # CONFIG_BBC_I2C=m CONFIG_VT=y @@ -65,6 +69,7 @@ CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE= CONFIG_CPU_FREQ_GOV_PERFORMANCE=y CONFIG_CPU_FREQ_GOV_POWERSAVE=m CONFIG_CPU_FREQ_GOV_USERSPACE=m +CONFIG_CPU_FREQ_GOV_ONDEMAND=m # CONFIG_CPU_FREQ_24_API is not set CONFIG_SPARC64=y CONFIG_RWSEM_XCHGADD_ALGORITHM=y @@ -118,6 +123,7 @@ CONFIG_FW_LOADER=m # Graphics support # CONFIG_FB=y +CONFIG_FB_MODE_HELPERS=y # CONFIG_FB_CIRRUS is not set CONFIG_FB_PM2=y # CONFIG_FB_PM2_FIFO_DISCONNECT is not set @@ -153,7 +159,6 @@ CONFIG_FB_FFB=y # # Console display driver support # -# CONFIG_MDA_CONSOLE is not set # CONFIG_PROM_CONSOLE is not set CONFIG_DUMMY_CONSOLE=y CONFIG_FRAMEBUFFER_CONSOLE=y @@ -217,6 +222,7 @@ CONFIG_BLK_DEV_LOOP=m CONFIG_BLK_DEV_CRYPTOLOOP=m CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_SX8=m +CONFIG_BLK_DEV_UB=m # CONFIG_BLK_DEV_RAM is not set # @@ -328,7 +334,8 @@ CONFIG_AIC79XX_RESET_DELAY_MS=15000 # CONFIG_AIC79XX_DEBUG_ENABLE is not set CONFIG_AIC79XX_DEBUG_MASK=0 # CONFIG_AIC79XX_REG_PRETTY_PRINT is not set -# CONFIG_SCSI_MEGARAID is not set +# CONFIG_MEGARAID_NEWGEN is not set +# CONFIG_MEGARAID_LEGACY is not set CONFIG_SCSI_SATA=y CONFIG_SCSI_SATA_SVW=m CONFIG_SCSI_ATA_PIIX=m @@ -353,9 +360,7 @@ CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MOD CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 # CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set -CONFIG_SCSI_IPR=m -# CONFIG_SCSI_IPR_TRACE is not set -# CONFIG_SCSI_IPR_DUMP is not set +# CONFIG_SCSI_IPR is not set CONFIG_SCSI_QLOGIC_ISP=m CONFIG_SCSI_QLOGIC_FC=y CONFIG_SCSI_QLOGIC_FC_FIRMWARE=y @@ -398,6 +403,7 @@ CONFIG_BLK_DEV_MD=m CONFIG_MD_LINEAR=m CONFIG_MD_RAID0=m CONFIG_MD_RAID1=m +CONFIG_MD_RAID10=m CONFIG_MD_RAID5=m CONFIG_MD_RAID6=m CONFIG_MD_MULTIPATH=m @@ -412,7 +418,6 @@ CONFIG_DM_ZERO=m # CONFIG_FUSION=m CONFIG_FUSION_MAX_SGE=40 -CONFIG_FUSION_ISENSE=m CONFIG_FUSION_CTL=m CONFIG_FUSION_LAN=m @@ -475,6 +480,7 @@ CONFIG_SYN_COOKIES=y CONFIG_INET_AH=y CONFIG_INET_ESP=y CONFIG_INET_IPCOMP=y +CONFIG_INET_TUNNEL=y # # IP: Virtual Server Configuration @@ -514,6 +520,7 @@ CONFIG_IPV6_PRIVACY=y CONFIG_INET6_AH=m CONFIG_INET6_ESP=m CONFIG_INET6_IPCOMP=m +CONFIG_INET6_TUNNEL=m CONFIG_IPV6_TUNNEL=m CONFIG_NETFILTER=y # CONFIG_NETFILTER_DEBUG is not set @@ -580,6 +587,9 @@ CONFIG_IP_NF_TARGET_NOTRACK=m CONFIG_IP_NF_RAW=m CONFIG_IP_NF_MATCH_ADDRTYPE=m CONFIG_IP_NF_MATCH_REALM=m +CONFIG_IP_NF_CT_ACCT=y +CONFIG_IP_NF_MATCH_SCTP=m +CONFIG_IP_NF_CT_PROTO_SCTP=m # # IPv6: Netfilter Configuration @@ -1096,6 +1106,7 @@ CONFIG_I2C_CHARDEV=m # CONFIG_I2C_ALGOBIT=y CONFIG_I2C_ALGOPCF=m +CONFIG_I2C_ALGOPCA=m # # I2C Hardware Bus support @@ -1120,6 +1131,7 @@ CONFIG_I2C_SIS96X=m CONFIG_I2C_VIA=m CONFIG_I2C_VIAPRO=m CONFIG_I2C_VOODOO3=m +CONFIG_I2C_PCA_ISA=m # # Hardware Sensors Chip support @@ -1141,6 +1153,7 @@ CONFIG_SENSORS_LM83=m CONFIG_SENSORS_LM85=m CONFIG_SENSORS_LM90=m CONFIG_SENSORS_MAX1619=m +CONFIG_SENSORS_SMSC47M1=m CONFIG_SENSORS_VIA686A=m CONFIG_SENSORS_W83781D=m CONFIG_SENSORS_W83L785TS=m @@ -1263,6 +1276,7 @@ CONFIG_EXPORTFS=m CONFIG_SUNRPC=m CONFIG_SUNRPC_GSS=m CONFIG_RPCSEC_GSS_KRB5=m +CONFIG_RPCSEC_GSS_SPKM3=m CONFIG_SMB_FS=m # CONFIG_SMB_NLS_DEFAULT is not set CONFIG_CIFS=m @@ -1474,6 +1488,7 @@ CONFIG_SND_VIRMIDI=m CONFIG_SND_AC97_CODEC=m CONFIG_SND_ALI5451=m CONFIG_SND_ATIIXP=m +CONFIG_SND_ATIIXP_MODEM=m CONFIG_SND_AU8810=m CONFIG_SND_AU8820=m CONFIG_SND_AU8830=m @@ -1513,6 +1528,7 @@ CONFIG_SND_VX222=m # ALSA USB devices # # CONFIG_SND_USB_AUDIO is not set +CONFIG_SND_USB_USX2Y=m # # ALSA Sparc devices @@ -1532,6 +1548,7 @@ CONFIG_USB=y CONFIG_USB_DEVICEFS=y # CONFIG_USB_BANDWIDTH is not set # CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG is not set # # USB Host Controller Drivers @@ -1597,7 +1614,6 @@ CONFIG_USB_HPUSBSCSI=m # CONFIG_USB_IBMCAM is not set # CONFIG_USB_KONICAWC is not set # CONFIG_USB_OV511 is not set -CONFIG_USB_PWC=m # CONFIG_USB_SE401 is not set CONFIG_USB_SN9C102=m # CONFIG_USB_STV680 is not set @@ -1734,14 +1750,15 @@ CONFIG_OPROFILE=m # Kernel hacking # CONFIG_DEBUG_KERNEL=y -# CONFIG_DEBUG_STACK_USAGE is not set -# CONFIG_DEBUG_SLAB is not set CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_SLAB is not set # CONFIG_DEBUG_SPINLOCK is not set # CONFIG_DEBUG_SPINLOCK_SLEEP is not set # CONFIG_DEBUG_BUGVERBOSE is not set -# CONFIG_DEBUG_DCFLUSH is not set # CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_STACK_USAGE is not set +CONFIG_KPROBES=y +# CONFIG_DEBUG_DCFLUSH is not set # CONFIG_STACK_DEBUG is not set # CONFIG_DEBUG_BOOTMEM is not set CONFIG_HAVE_DEC_LOCK=y @@ -1762,6 +1779,7 @@ CONFIG_CRYPTO_MD5=y CONFIG_CRYPTO_SHA1=y CONFIG_CRYPTO_SHA256=m CONFIG_CRYPTO_SHA512=m +CONFIG_CRYPTO_WHIRLPOOL=m CONFIG_CRYPTO_DES=y CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_TWOFISH=m --- linux-2.6.9-rc1/arch/sparc64/Kconfig.debug 2004-08-24 00:02:26.000000000 -0700 +++ 25/arch/sparc64/Kconfig.debug 2004-09-03 00:56:42.842647576 -0700 @@ -11,6 +11,16 @@ config DEBUG_STACK_USAGE This option will slow down process creation somewhat. +config KPROBES + bool "Kprobes" + depends on DEBUG_KERNEL + help + Kprobes allows you to trap at almost any kernel address and + execute a callback function. register_kprobe() establishes + a probepoint and specifies the callback. Kprobes is useful + for kernel debugging, non-intrusive instrumentation and testing. + If in doubt, say "N". + config DEBUG_DCFLUSH bool "D-cache flush debugging" depends on DEBUG_KERNEL @@ -23,12 +33,19 @@ config DEBUG_BOOTMEM depends on DEBUG_KERNEL bool "Debug BOOTMEM initialization" +config LOCKMETER + bool "Kernel lock metering" + depends on SMP && !PREEMPT + help + Say Y to enable kernel lock metering, which adds overhead to SMP locks, + but allows you to see various statistics using the lockstat command. + # We have a custom atomic_dec_and_lock() implementation but it's not # compatible with spinlock debugging so we need to fall back on # the generic version in that case. config HAVE_DEC_LOCK bool - depends on SMP && !DEBUG_SPINLOCK + depends on SMP && !DEBUG_SPINLOCK && !LOCKMETER default y config MCOUNT --- linux-2.6.9-rc1/arch/sparc64/kernel/irq.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/sparc64/kernel/irq.c 2004-09-03 00:56:41.001927408 -0700 @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -791,16 +790,24 @@ void handler_irq(int irq, struct pt_regs #endif if ((flags & IBF_MULTI) == 0) { struct irqaction *ap = bp->irq_info; - ap->handler(__irq(bp), ap->dev_id, regs); - random |= ap->flags & SA_SAMPLE_RANDOM; + int ret; + + ret = ap->handler(__irq(bp), ap->dev_id, regs); + if (ret == IRQ_HANDLED) + random |= ap->flags; } else { void **vector = (void **)bp->irq_info; int ent; for (ent = 0; ent < 4; ent++) { struct irqaction *ap = vector[ent]; if (ap != NULL) { - ap->handler(__irq(bp), ap->dev_id, regs); - random |= ap->flags & SA_SAMPLE_RANDOM; + int ret; + + ret = ap->handler(__irq(bp), + ap->dev_id, + regs); + if (ret == IRQ_HANDLED) + random |= ap->flags; } } } @@ -813,8 +820,9 @@ void handler_irq(int irq, struct pt_regs } #endif upa_writel(ICLR_IDLE, bp->iclr); + /* Test and add entropy */ - if (random) + if (random & SA_SAMPLE_RANDOM) add_interrupt_randomness(irq); } } else --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/sparc64/kernel/kprobes.c 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,276 @@ +/* arch/sparc64/kernel/kprobes.c + * + * Copyright (C) 2004 David S. Miller + */ + +#include +#include +#include + +#include +#include + +/* We do not have hardware single-stepping, so in order + * to implement post handlers correctly we use two breakpoint + * instructions. + * + * 1) ta 0x70 --> 0x91d02070 + * 2) ta 0x71 --> 0x91d02071 + * + * When these are hit, control is transferred to kprobe_trap() + * below. The arg 'level' tells us which of the two traps occurred. + * + * Initially, the instruction at p->addr gets set to "ta 0x70" + * by code in register_kprobe() by setting that memory address + * to BREAKPOINT_INSTRUCTION. When this breakpoint is hit + * the following happens: + * + * 1) We run the pre-handler + * 2) We replace p->addr with the original opcode + * 3) We set the instruction at "regs->npc" to "ta 0x71" + * 4) We mark that we are waiting for the second breakpoint + * to hit and return from the trap. + * + * At this point we wait for the second breakpoint to hit. + * When it does: + * + * 1) We run the post-handler + * 2) We re-install "ta 0x70" at p->addr + * 3) We restore the opcode at the "ta 0x71" breakpoint + * 4) We reset our "waiting for "ta 0x71" state + * 5) We return from the trap + * + * We could use the trick used by the i386 kprobe code but I + * think that scheme has problems with exception tables. On i386 + * they single-step over the original instruction stored at + * kprobe->insn. So they set the processor to single step, and + * set the program counter to kprobe->insn. + * + * But that explodes if the original opcode is a user space + * access instruction and that faults. It will go wrong because + * since the location of the instruction being executed is + * different from that recorded in the exception tables, the + * kernel will not find it and this will cause an erroneous + * kernel OOPS. + */ + +void arch_prepare_kprobe(struct kprobe *p) +{ + p->insn[0] = *p->addr; + p->insn[1] = 0xdeadbeef; +} + +static void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) +{ + u32 *insn2 = (u32 *) regs->tpc; + + p->insn[1] = *insn2; + + *insn2 = BREAKPOINT_INSTRUCTION_2; + flushi(insn2); +} + +static void undo_singlestep(struct kprobe *p, struct pt_regs *regs) +{ + u32 *insn2 = (u32 *) regs->tpc; + + BUG_ON(p->insn[1] == 0xdeadbeef); + + *insn2 = p->insn[1]; + flushi(insn2); + + p->insn[1] = 0xdeadbeef; +} + +/* kprobe_status settings */ +#define KPROBE_HIT_ACTIVE 0x00000001 +#define KPROBE_HIT_SS 0x00000002 + +static struct kprobe *current_kprobe; +static unsigned int kprobe_status; + +static int kprobe_handler(struct pt_regs *regs) +{ + struct kprobe *p; + void *addr = (void *) regs->tpc; + int ret = 0; + + preempt_disable(); + + if (kprobe_running()) { + p = get_kprobe(addr); + if (p) { + *p->addr = p->opcode; + flushi(p->addr); + ret = 1; + } else { + p = current_kprobe; + if (p->break_handler && p->break_handler(p, regs)) + goto ss_probe; + } + goto no_kprobe; + } + + lock_kprobes(); + p = get_kprobe(addr); + if (!p) { + unlock_kprobes(); + if (*(u32 *)addr != BREAKPOINT_INSTRUCTION) + ret = 1; + goto no_kprobe; + } + + kprobe_status = KPROBE_HIT_ACTIVE; + current_kprobe = p; + if (p->pre_handler(p, regs)) + return 1; + +ss_probe: + prepare_singlestep(p, regs); + kprobe_status = KPROBE_HIT_SS; + return 1; + +no_kprobe: + preempt_enable_no_resched(); + return ret; +} + +static int post_kprobe_handler(struct pt_regs *regs) +{ + u32 *insn_p = (u32 *) regs->tpc; + + if (!kprobe_running() || (*insn_p != BREAKPOINT_INSTRUCTION_2)) + return 0; + + if (current_kprobe->post_handler) + current_kprobe->post_handler(current_kprobe, regs, 0); + + undo_singlestep(current_kprobe, regs); + + unlock_kprobes(); + preempt_enable_no_resched(); + + return 1; +} + +/* Interrupts disabled, kprobe_lock held. */ +static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr) +{ + if (current_kprobe->fault_handler + && current_kprobe->fault_handler(current_kprobe, regs, trapnr)) + return 1; + + if (kprobe_status & KPROBE_HIT_SS) { + undo_singlestep(current_kprobe, regs); + + unlock_kprobes(); + preempt_enable_no_resched(); + } + return 0; +} + +/* + * Wrapper routine to for handling exceptions. + */ +int kprobe_exceptions_notify(struct notifier_block *self, unsigned long val, + void *data) +{ + struct die_args *args = (struct die_args *)data; + switch (val) { + case DIE_DEBUG: + if (kprobe_handler(args->regs)) + return NOTIFY_OK; + break; + case DIE_DEBUG_2: + if (post_kprobe_handler(args->regs)) + return NOTIFY_OK; + break; + case DIE_GPF: + if (kprobe_running() && + kprobe_fault_handler(args->regs, args->trapnr)) + return NOTIFY_OK; + break; + case DIE_PAGE_FAULT: + if (kprobe_running() && + kprobe_fault_handler(args->regs, args->trapnr)) + return NOTIFY_OK; + break; + default: + break; + } + return NOTIFY_BAD; +} + +asmlinkage void kprobe_trap(unsigned long trap_level, struct pt_regs *regs) +{ + BUG_ON(trap_level != 0x170 && trap_level != 0x171); + + if (user_mode(regs)) { + local_irq_enable(); + bad_trap(regs, trap_level); + return; + } + + /* trap_level == 0x170 --> ta 0x70 + * trap_level == 0x171 --> ta 0x71 + */ + if (notify_die((trap_level == 0x170) ? DIE_DEBUG : DIE_DEBUG_2, + (trap_level == 0x170) ? "debug" : "debug_2", + regs, 0, trap_level, SIGTRAP) != NOTIFY_OK) + bad_trap(regs, trap_level); +} + +/* Jprobes support. */ +static struct pt_regs jprobe_saved_regs; +static struct sparc_stackf jprobe_saved_stack; + +int setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct jprobe *jp = container_of(p, struct jprobe, kp); + + memcpy(&jprobe_saved_regs, regs, sizeof(*regs)); + + /* Save a whole stack frame, this gets arguments + * pushed onto the stack after using up all the + * arg registers. + */ + memcpy(&jprobe_saved_stack, + (char *) (regs->u_regs[UREG_FP] + STACK_BIAS), + sizeof(jprobe_saved_stack)); + + regs->tpc = (unsigned long) jp->entry; + regs->tnpc = ((unsigned long) jp->entry) + 0x4UL; + + return 1; +} + +void jprobe_return(void) +{ + preempt_enable_no_resched(); + __asm__ __volatile__( + ".globl jprobe_return_trap_instruction\n" +"jprobe_return_trap_instruction:\n\t" + "ta 0x70"); +} + +extern void jprobe_return_trap_instruction(void); + +int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) +{ + u32 *addr = (u32 *) regs->tpc; + + if (addr == (u32 *) jprobe_return_trap_instruction) { + /* Restore old register state. Do pt_regs + * first so that UREG_FP is the original one for + * the stack frame restore. + */ + memcpy(regs, &jprobe_saved_regs, sizeof(*regs)); + + memcpy((char *) (regs->u_regs[UREG_FP] + STACK_BIAS), + &jprobe_saved_stack, + sizeof(jprobe_saved_stack)); + + return 1; + } + return 0; +} --- linux-2.6.9-rc1/arch/sparc64/kernel/Makefile 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/sparc64/kernel/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -22,6 +22,7 @@ obj-$(CONFIG_BINFMT_AOUT32) += binfmt_ao obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_US3_FREQ) += us3_cpufreq.o obj-$(CONFIG_US2E_FREQ) += us2e_cpufreq.o +obj-$(CONFIG_KPROBES) += kprobes.o ifdef CONFIG_SUNOS_EMUL obj-y += sys_sunos32.o sunos_ioctl32.o --- linux-2.6.9-rc1/arch/sparc64/kernel/pci.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/arch/sparc64/kernel/pci.c 2004-09-02 20:51:51.000000000 -0700 @@ -41,7 +41,6 @@ asmlinkage int sys_pciconfig_write(unsig #else /* List of all PCI controllers found in the system. */ -spinlock_t pci_controller_lock = SPIN_LOCK_UNLOCKED; struct pci_controller_info *pci_controller_root = NULL; /* Each PCI controller found gets a unique index. */ @@ -298,12 +297,9 @@ static void __init pci_controller_probe( static void __init pci_scan_each_controller_bus(void) { struct pci_controller_info *p; - unsigned long flags; - spin_lock_irqsave(&pci_controller_lock, flags); for (p = pci_controller_root; p; p = p->next) p->scan_bus(p); - spin_unlock_irqrestore(&pci_controller_lock, flags); } /* Reorder the pci_dev chain, so that onboard devices come first --- linux-2.6.9-rc1/arch/sparc64/kernel/pci_common.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/sparc64/kernel/pci_common.c 2004-09-02 20:51:51.000000000 -0700 @@ -15,18 +15,13 @@ */ void __init pci_fixup_host_bridge_self(struct pci_bus *pbus) { - struct list_head *walk = &pbus->devices; - - walk = walk->next; - while (walk != &pbus->devices) { - struct pci_dev *pdev = pci_dev_b(walk); + struct pci_dev *pdev; + list_for_each_entry(pdev, &pbus->devices, bus_list) { if (pdev->class >> 8 == PCI_CLASS_BRIDGE_HOST) { pbus->self = pdev; return; } - - walk = walk->next; } prom_printf("PCI: Critical error, cannot find host bridge PDEV.\n"); @@ -217,31 +212,18 @@ void __init pci_fill_in_pbm_cookies(stru struct pci_pbm_info *pbm, int prom_node) { - struct list_head *walk = &pbus->devices; - - /* This loop is coded like this because the cookie - * fillin routine can delete devices from the tree. - */ - walk = walk->next; - while (walk != &pbus->devices) { - struct pci_dev *pdev = pci_dev_b(walk); - struct list_head *walk_next = walk->next; + struct pci_dev *pdev, *pdev_next; + struct pci_bus *this_pbus, *pbus_next; + /* This must be _safe because the cookie fillin + routine can delete devices from the tree. */ + list_for_each_entry_safe(pdev, pdev_next, &pbus->devices, bus_list) pdev_cookie_fillin(pbm, pdev, prom_node); - walk = walk_next; - } - - walk = &pbus->children; - walk = walk->next; - while (walk != &pbus->children) { - struct pci_bus *this_pbus = pci_bus_b(walk); + list_for_each_entry_safe(this_pbus, pbus_next, &pbus->children, node) { struct pcidev_cookie *pcp = this_pbus->self->sysdata; - struct list_head *walk_next = walk->next; pci_fill_in_pbm_cookies(this_pbus, pbm, pcp->prom_node); - - walk = walk_next; } } @@ -431,14 +413,14 @@ static void __init pdev_record_assignmen void __init pci_record_assignments(struct pci_pbm_info *pbm, struct pci_bus *pbus) { - struct list_head *walk = &pbus->devices; + struct pci_dev *dev; + struct pci_bus *bus; - for (walk = walk->next; walk != &pbus->devices; walk = walk->next) - pdev_record_assignments(pbm, pci_dev_b(walk)); + list_for_each_entry(dev, &pbus->devices, bus_list) + pdev_record_assignments(pbm, dev); - walk = &pbus->children; - for (walk = walk->next; walk != &pbus->children; walk = walk->next) - pci_record_assignments(pbm, pci_bus_b(walk)); + list_for_each_entry(bus, &pbus->children, node) + pci_record_assignments(pbm, bus); } /* Return non-zero if PDEV has implicit I/O resources even @@ -549,14 +531,14 @@ static void __init pdev_assign_unassigne void __init pci_assign_unassigned(struct pci_pbm_info *pbm, struct pci_bus *pbus) { - struct list_head *walk = &pbus->devices; + struct pci_dev *dev; + struct pci_bus *bus; - for (walk = walk->next; walk != &pbus->devices; walk = walk->next) - pdev_assign_unassigned(pbm, pci_dev_b(walk)); + list_for_each_entry(dev, &pbus->devices, bus_list) + pdev_assign_unassigned(pbm, dev); - walk = &pbus->children; - for (walk = walk->next; walk != &pbus->children; walk = walk->next) - pci_assign_unassigned(pbm, pci_bus_b(walk)); + list_for_each_entry(bus, &pbus->children, node) + pci_assign_unassigned(pbm, bus); } static int __init pci_intmap_match(struct pci_dev *pdev, unsigned int *interrupt) @@ -797,14 +779,14 @@ have_irq: void __init pci_fixup_irq(struct pci_pbm_info *pbm, struct pci_bus *pbus) { - struct list_head *walk = &pbus->devices; + struct pci_dev *dev; + struct pci_bus *bus; - for (walk = walk->next; walk != &pbus->devices; walk = walk->next) - pdev_fixup_irq(pci_dev_b(walk)); + list_for_each_entry(dev, &pbus->devices, bus_list) + pdev_fixup_irq(dev); - walk = &pbus->children; - for (walk = walk->next; walk != &pbus->children; walk = walk->next) - pci_fixup_irq(pbm, pci_bus_b(walk)); + list_for_each_entry(bus, &pbus->children, node) + pci_fixup_irq(pbm, bus); } static void pdev_setup_busmastering(struct pci_dev *pdev, int is_66mhz) @@ -897,7 +879,7 @@ static void pdev_setup_busmastering(stru void pci_determine_66mhz_disposition(struct pci_pbm_info *pbm, struct pci_bus *pbus) { - struct list_head *walk; + struct pci_dev *pdev; int all_are_66mhz; u16 status; @@ -906,11 +888,8 @@ void pci_determine_66mhz_disposition(str goto out; } - walk = &pbus->devices; all_are_66mhz = 1; - for (walk = walk->next; walk != &pbus->devices; walk = walk->next) { - struct pci_dev *pdev = pci_dev_b(walk); - + list_for_each_entry(pdev, &pbus->devices, bus_list) { pci_read_config_word(pdev, PCI_STATUS, &status); if (!(status & PCI_STATUS_66MHZ)) { all_are_66mhz = 0; @@ -929,17 +908,17 @@ out: void pci_setup_busmastering(struct pci_pbm_info *pbm, struct pci_bus *pbus) { - struct list_head *walk = &pbus->devices; + struct pci_dev *dev; + struct pci_bus *bus; int is_66mhz; is_66mhz = pbm->is_66mhz_capable && pbm->all_devs_66mhz; - for (walk = walk->next; walk != &pbus->devices; walk = walk->next) - pdev_setup_busmastering(pci_dev_b(walk), is_66mhz); + list_for_each_entry(dev, &pbus->devices, bus_list) + pdev_setup_busmastering(dev, is_66mhz); - walk = &pbus->children; - for (walk = walk->next; walk != &pbus->children; walk = walk->next) - pci_setup_busmastering(pbm, pci_bus_b(walk)); + list_for_each_entry(bus, &pbus->children, node) + pci_setup_busmastering(pbm, bus); } void pci_register_legacy_regions(struct resource *io_res, @@ -987,10 +966,10 @@ void pci_scan_for_target_abort(struct pc struct pci_pbm_info *pbm, struct pci_bus *pbus) { - struct list_head *walk = &pbus->devices; + struct pci_dev *pdev; + struct pci_bus *bus; - for (walk = walk->next; walk != &pbus->devices; walk = walk->next) { - struct pci_dev *pdev = pci_dev_b(walk); + list_for_each_entry(pdev, &pbus->devices, bus_list) { u16 status, error_bits; pci_read_config_word(pdev, PCI_STATUS, &status); @@ -1005,19 +984,18 @@ void pci_scan_for_target_abort(struct pc } } - walk = &pbus->children; - for (walk = walk->next; walk != &pbus->children; walk = walk->next) - pci_scan_for_target_abort(p, pbm, pci_bus_b(walk)); + list_for_each_entry(bus, &pbus->children, node) + pci_scan_for_target_abort(p, pbm, bus); } void pci_scan_for_master_abort(struct pci_controller_info *p, struct pci_pbm_info *pbm, struct pci_bus *pbus) { - struct list_head *walk = &pbus->devices; + struct pci_dev *pdev; + struct pci_bus *bus; - for (walk = walk->next; walk != &pbus->devices; walk = walk->next) { - struct pci_dev *pdev = pci_dev_b(walk); + list_for_each_entry(pdev, &pbus->devices, bus_list) { u16 status, error_bits; pci_read_config_word(pdev, PCI_STATUS, &status); @@ -1031,19 +1009,18 @@ void pci_scan_for_master_abort(struct pc } } - walk = &pbus->children; - for (walk = walk->next; walk != &pbus->children; walk = walk->next) - pci_scan_for_master_abort(p, pbm, pci_bus_b(walk)); + list_for_each_entry(bus, &pbus->children, node) + pci_scan_for_master_abort(p, pbm, bus); } void pci_scan_for_parity_error(struct pci_controller_info *p, struct pci_pbm_info *pbm, struct pci_bus *pbus) { - struct list_head *walk = &pbus->devices; + struct pci_dev *pdev; + struct pci_bus *bus; - for (walk = walk->next; walk != &pbus->devices; walk = walk->next) { - struct pci_dev *pdev = pci_dev_b(walk); + list_for_each_entry(pdev, &pbus->devices, bus_list) { u16 status, error_bits; pci_read_config_word(pdev, PCI_STATUS, &status); @@ -1058,7 +1035,6 @@ void pci_scan_for_parity_error(struct pc } } - walk = &pbus->children; - for (walk = walk->next; walk != &pbus->children; walk = walk->next) - pci_scan_for_parity_error(p, pbm, pci_bus_b(walk)); + list_for_each_entry(bus, &pbus->children, node) + pci_scan_for_parity_error(p, pbm, bus); } --- linux-2.6.9-rc1/arch/sparc64/kernel/pci_impl.h 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/sparc64/kernel/pci_impl.h 2004-09-02 20:51:51.000000000 -0700 @@ -11,7 +11,6 @@ #include #include -extern spinlock_t pci_controller_lock; extern struct pci_controller_info *pci_controller_root; extern int pci_num_controllers; --- linux-2.6.9-rc1/arch/sparc64/kernel/pci_psycho.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/sparc64/kernel/pci_psycho.c 2004-09-02 20:51:51.000000000 -0700 @@ -1487,22 +1487,18 @@ void __init psycho_init(int node, char * struct linux_prom64_registers pr_regs[3]; struct pci_controller_info *p; struct pci_iommu *iommu; - unsigned long flags; u32 upa_portid; int is_pbm_a, err; upa_portid = prom_getintdefault(node, "upa-portid", 0xff); - spin_lock_irqsave(&pci_controller_lock, flags); for(p = pci_controller_root; p; p = p->next) { if (p->pbm_A.portid == upa_portid) { - spin_unlock_irqrestore(&pci_controller_lock, flags); is_pbm_a = (p->pbm_A.prom_node == 0); psycho_pbm_init(p, node, is_pbm_a); return; } } - spin_unlock_irqrestore(&pci_controller_lock, flags); p = kmalloc(sizeof(struct pci_controller_info), GFP_ATOMIC); if (!p) { @@ -1518,10 +1514,8 @@ void __init psycho_init(int node, char * memset(iommu, 0, sizeof(*iommu)); p->pbm_A.iommu = p->pbm_B.iommu = iommu; - spin_lock_irqsave(&pci_controller_lock, flags); p->next = pci_controller_root; pci_controller_root = p; - spin_unlock_irqrestore(&pci_controller_lock, flags); p->pbm_A.portid = upa_portid; p->pbm_B.portid = upa_portid; --- linux-2.6.9-rc1/arch/sparc64/kernel/pci_sabre.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/sparc64/kernel/pci_sabre.c 2004-09-02 20:51:51.000000000 -0700 @@ -1113,10 +1113,9 @@ static void __init sabre_base_address_up static void __init apb_init(struct pci_controller_info *p, struct pci_bus *sabre_bus) { - struct list_head *walk = &sabre_bus->devices; + struct pci_dev *pdev; - for (walk = walk->next; walk != &sabre_bus->devices; walk = walk->next) { - struct pci_dev *pdev = pci_dev_b(walk); + list_for_each_entry(pdev, &sabre_bus->devices, bus_list) { if (pdev->vendor == PCI_VENDOR_ID_SUN && pdev->device == PCI_DEVICE_ID_SUN_SIMBA) { @@ -1178,10 +1177,9 @@ static struct pcidev_cookie *alloc_bridg static void __init sabre_scan_bus(struct pci_controller_info *p) { static int once; - struct pci_bus *sabre_bus; + struct pci_bus *sabre_bus, *pbus; struct pci_pbm_info *pbm; struct pcidev_cookie *cookie; - struct list_head *walk; int sabres_scanned; /* The APB bridge speaks to the Sabre host PCI bridge @@ -1217,9 +1215,7 @@ static void __init sabre_scan_bus(struct sabres_scanned = 0; - walk = &sabre_bus->children; - for (walk = walk->next; walk != &sabre_bus->children; walk = walk->next) { - struct pci_bus *pbus = pci_bus_b(walk); + list_for_each_entry(pbus, &sabre_bus->children, node) { if (pbus->number == p->pbm_A.pci_first_busno) { pbm = &p->pbm_A; @@ -1554,7 +1550,6 @@ void __init sabre_init(int pnode, char * struct linux_prom64_registers pr_regs[2]; struct pci_controller_info *p; struct pci_iommu *iommu; - unsigned long flags; int tsbsize, err; u32 busrange[2]; u32 vdma[2]; @@ -1602,10 +1597,8 @@ void __init sabre_init(int pnode, char * upa_portid = prom_getintdefault(pnode, "upa-portid", 0xff); - spin_lock_irqsave(&pci_controller_lock, flags); p->next = pci_controller_root; pci_controller_root = p; - spin_unlock_irqrestore(&pci_controller_lock, flags); p->pbm_A.portid = upa_portid; p->pbm_B.portid = upa_portid; --- linux-2.6.9-rc1/arch/sparc64/kernel/pci_schizo.c 2004-08-24 00:03:17.000000000 -0700 +++ 25/arch/sparc64/kernel/pci_schizo.c 2004-09-02 20:51:51.000000000 -0700 @@ -2081,13 +2081,11 @@ static void __init __schizo_init(int nod { struct pci_controller_info *p; struct pci_iommu *iommu; - unsigned long flags; int is_pbm_a; u32 portid; portid = prom_getintdefault(node, "portid", 0xff); - spin_lock_irqsave(&pci_controller_lock, flags); for(p = pci_controller_root; p; p = p->next) { struct pci_pbm_info *pbm; @@ -2099,13 +2097,11 @@ static void __init __schizo_init(int nod &p->pbm_B); if (portid_compare(pbm->portid, portid, chip_type)) { - spin_unlock_irqrestore(&pci_controller_lock, flags); is_pbm_a = (p->pbm_A.prom_node == 0); schizo_pbm_init(p, node, portid, chip_type); return; } } - spin_unlock_irqrestore(&pci_controller_lock, flags); p = kmalloc(sizeof(struct pci_controller_info), GFP_ATOMIC); if (!p) { @@ -2130,10 +2126,8 @@ static void __init __schizo_init(int nod memset(iommu, 0, sizeof(*iommu)); p->pbm_B.iommu = iommu; - spin_lock_irqsave(&pci_controller_lock, flags); p->next = pci_controller_root; pci_controller_root = p; - spin_unlock_irqrestore(&pci_controller_lock, flags); p->index = pci_num_controllers++; p->pbms_same_domain = 0; --- linux-2.6.9-rc1/arch/sparc64/kernel/process.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/sparc64/kernel/process.c 2004-09-02 20:51:51.000000000 -0700 @@ -588,8 +588,6 @@ asmlinkage long sparc_do_fork(unsigned l { int __user *parent_tid_ptr, *child_tid_ptr; - clone_flags &= ~CLONE_IDLETASK; - #ifdef CONFIG_COMPAT if (test_thread_flag(TIF_32BIT)) { parent_tid_ptr = compat_ptr(regs->u_regs[UREG_I2]); --- linux-2.6.9-rc1/arch/sparc64/kernel/ptrace.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/sparc64/kernel/ptrace.c 2004-09-03 00:56:56.200616856 -0700 @@ -627,11 +627,8 @@ asmlinkage void syscall_trace(void) return; if (!(current->ptrace & PT_PTRACED)) return; - current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0); - current->state = TASK_STOPPED; - notify_parent(current, SIGCHLD); - schedule(); + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); /* * this isn't the same as continuing with a signal, but it will do --- linux-2.6.9-rc1/arch/sparc64/kernel/setup.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/sparc64/kernel/setup.c 2004-09-03 00:56:41.002927256 -0700 @@ -43,7 +43,6 @@ #include #include #include -#include #include #include #include --- linux-2.6.9-rc1/arch/sparc64/kernel/signal32.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/arch/sparc64/kernel/signal32.c 2004-09-02 20:51:51.000000000 -0700 @@ -59,6 +59,16 @@ struct signal_sframe32 { unsigned int extramask[_COMPAT_NSIG_WORDS - 1]; }; +/* This magic should be in g_upper[0] for all upper parts + * to be valid. + */ +#define SIGINFO_EXTRA_V8PLUS_MAGIC 0x130e269 +typedef struct { + unsigned int g_upper[8]; + unsigned int o_upper[8]; + unsigned int asi; +} siginfo_extra_v8plus_t; + /* * And the new one, intended to be used for Linux applications only * (we have enough in there to work with clone). @@ -76,9 +86,62 @@ struct new_signal_frame32 { __siginfo_fpu_t fpu_state; }; +struct siginfo32 { + int si_signo; + int si_errno; + int si_code; + + union { + int _pad[SI_PAD_SIZE32]; + + /* kill() */ + struct { + compat_pid_t _pid; /* sender's pid */ + unsigned int _uid; /* sender's uid */ + } _kill; + + /* POSIX.1b timers */ + struct { + timer_t _tid; /* timer id */ + int _overrun; /* overrun count */ + sigval_t32 _sigval; /* same as below */ + int _sys_private; /* not to be passed to user */ + } _timer; + + /* POSIX.1b signals */ + struct { + compat_pid_t _pid; /* sender's pid */ + unsigned int _uid; /* sender's uid */ + sigval_t32 _sigval; + } _rt; + + /* SIGCHLD */ + struct { + compat_pid_t _pid; /* which child */ + unsigned int _uid; /* sender's uid */ + int _status; /* exit code */ + compat_clock_t _utime; + compat_clock_t _stime; + struct compat_rusage _rusage; + } _sigchld; + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGEMT */ + struct { + u32 _addr; /* faulting insn/memory ref. */ + int _trapno; + } _sigfault; + + /* SIGPOLL */ + struct { + int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ + int _fd; + } _sigpoll; + } _sifields; +}; + struct rt_signal_frame32 { struct sparc_stackf32 ss; - siginfo_t32 info; + struct siginfo32 info; struct pt_regs32 regs; compat_sigset_t mask; /* __siginfo_fpu32_t * */ u32 fpu_save; @@ -95,11 +158,11 @@ struct rt_signal_frame32 { #define NF_ALIGNEDSZ (((sizeof(struct new_signal_frame32) + 7) & (~7))) #define RT_ALIGNEDSZ (((sizeof(struct rt_signal_frame32) + 7) & (~7))) -int copy_siginfo_to_user32(siginfo_t32 __user *to, siginfo_t *from) +int copy_siginfo_to_user32(struct siginfo32 __user *to, siginfo_t *from) { int err; - if (!access_ok(VERIFY_WRITE, to, sizeof(siginfo_t32))) + if (!access_ok(VERIFY_WRITE, to, sizeof(struct siginfo32))) return -EFAULT; /* If you change siginfo_t structure, please be sure @@ -125,6 +188,8 @@ int copy_siginfo_to_user32(siginfo_t32 _ err |= __put_user(from->si_utime, &to->si_utime); err |= __put_user(from->si_stime, &to->si_stime); err |= __put_user(from->si_status, &to->si_status); + err |= put_compat_rusage(&from->si_rusage, + &to->si_rusage); default: err |= __put_user(from->si_pid, &to->si_pid); err |= __put_user(from->si_uid, &to->si_uid); @@ -145,6 +210,22 @@ int copy_siginfo_to_user32(siginfo_t32 _ return err; } +/* CAUTION: This is just a very minimalist implementation for the + * sake of compat_sys_rt_sigqueueinfo() + */ +int copy_siginfo_to_kernel32(siginfo_t *to, struct siginfo32 __user *from) +{ + if (!access_ok(VERIFY_WRITE, from, sizeof(struct siginfo32))) + return -EFAULT; + + if (copy_from_user(to, from, 3*sizeof(int)) || + copy_from_user(to->_sifields._pad, from->_sifields._pad, + SI_PAD_SIZE)) + return -EFAULT; + + return 0; +} + /* * atomically swap in the new signal mask, and wait for a signal. * This is really tricky on the Sparc, watch out... @@ -299,8 +380,13 @@ void do_new_sigreturn32(struct pt_regs * if ((psr & (PSR_VERS|PSR_IMPL)) == PSR_V8PLUS) { err |= __get_user(i, &sf->v8plus.g_upper[0]); if (i == SIGINFO_EXTRA_V8PLUS_MAGIC) { + unsigned long asi; + for (i = UREG_G1; i <= UREG_I7; i++) err |= __get_user(((u32 *)regs->u_regs)[2*i], &sf->v8plus.g_upper[i]); + err |= __get_user(asi, &sf->v8plus.asi); + regs->tstate &= ~TSTATE_ASI; + regs->tstate |= ((asi & 0xffUL) << 24UL); } } @@ -330,7 +416,7 @@ void do_new_sigreturn32(struct pt_regs * return; segv: - do_exit(SIGSEGV); + force_sig(SIGSEGV, current); } asmlinkage void do_sigreturn32(struct pt_regs *regs) @@ -400,7 +486,7 @@ asmlinkage void do_sigreturn32(struct pt return; segv: - do_exit(SIGSEGV); + force_sig(SIGSEGV, current); } asmlinkage void do_rt_sigreturn32(struct pt_regs *regs) @@ -447,8 +533,13 @@ asmlinkage void do_rt_sigreturn32(struct if ((psr & (PSR_VERS|PSR_IMPL)) == PSR_V8PLUS) { err |= __get_user(i, &sf->v8plus.g_upper[0]); if (i == SIGINFO_EXTRA_V8PLUS_MAGIC) { + unsigned long asi; + for (i = UREG_G1; i <= UREG_I7; i++) err |= __get_user(((u32 *)regs->u_regs)[2*i], &sf->v8plus.g_upper[i]); + err |= __get_user(asi, &sf->v8plus.asi); + regs->tstate &= ~TSTATE_ASI; + regs->tstate |= ((asi & 0xffUL) << 24UL); } } @@ -487,7 +578,7 @@ asmlinkage void do_rt_sigreturn32(struct spin_unlock_irq(¤t->sighand->siglock); return; segv: - do_exit(SIGSEGV); + force_sig(SIGSEGV, current); } /* Checks if the fp is valid */ @@ -648,7 +739,7 @@ setup_frame32(struct sigaction *sa, stru return; sigsegv: - do_exit(SIGSEGV); + force_sigsegv(signr, current); } @@ -715,7 +806,10 @@ static void new_setup_frame32(struct k_s err |= __put_user(sizeof(siginfo_extra_v8plus_t), &sf->extra_size); err |= __put_user(SIGINFO_EXTRA_V8PLUS_MAGIC, &sf->v8plus.g_upper[0]); for (i = 1; i < 16; i++) - err |= __put_user(((u32 *)regs->u_regs)[2*i], &sf->v8plus.g_upper[i]); + err |= __put_user(((u32 *)regs->u_regs)[2*i], + &sf->v8plus.g_upper[i]); + err |= __put_user((regs->tstate & TSTATE_ASI) >> 24UL, + &sf->v8plus.asi); if (psr & PSR_EF) { err |= save_fpu_state32(regs, &sf->fpu_state); @@ -796,7 +890,7 @@ static void new_setup_frame32(struct k_s sigill: do_exit(SIGILL); sigsegv: - do_exit(SIGSEGV); + force_sigsegv(signo, current); } /* Setup a Solaris stack frame */ @@ -920,7 +1014,7 @@ setup_svr4_frame32(struct sigaction *sa, return; sigsegv: - do_exit(SIGSEGV); + force_sigsegv(signr, current); } asmlinkage int @@ -1071,7 +1165,7 @@ asmlinkage int svr4_setcontext(svr4_ucon return -EINTR; sigsegv: - do_exit(SIGSEGV); + return -EFAULT; } static void setup_rt_frame32(struct k_sigaction *ka, struct pt_regs *regs, @@ -1120,6 +1214,8 @@ static void setup_rt_frame32(struct k_si for (i = 1; i < 16; i++) err |= __put_user(((u32 *)regs->u_regs)[2*i], &sf->v8plus.g_upper[i]); + err |= __put_user((regs->tstate & TSTATE_ASI) >> 24UL, + &sf->v8plus.asi); if (psr & PSR_EF) { err |= save_fpu_state32(regs, &sf->fpu_state); @@ -1208,7 +1304,7 @@ static void setup_rt_frame32(struct k_si sigill: do_exit(SIGILL); sigsegv: - do_exit(SIGSEGV); + force_sigsegv(signr, current); } static inline void handle_signal32(unsigned long signr, struct k_sigaction *ka, @@ -1227,8 +1323,6 @@ static inline void handle_signal32(unsig else setup_frame32(&ka->sa, regs, signr, oldset, info); } - if (ka->sa.sa_flags & SA_ONESHOT) - ka->sa.sa_handler = SIG_DFL; if (!(ka->sa.sa_flags & SA_NOMASK)) { spin_lock_irq(¤t->sighand->siglock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); @@ -1268,21 +1362,19 @@ int do_signal32(sigset_t *oldset, struct { siginfo_t info; struct signal_deliver_cookie cookie; + struct k_sigaction ka; int signr; int svr4_signal = current->personality == PER_SVR4; cookie.restart_syscall = restart_syscall; cookie.orig_i0 = orig_i0; - signr = get_signal_to_deliver(&info, regs, &cookie); + signr = get_signal_to_deliver(&info, &ka, regs, &cookie); if (signr > 0) { - struct k_sigaction *ka; - - ka = ¤t->sighand->action[signr-1]; - if (cookie.restart_syscall) - syscall_restart32(orig_i0, regs, &ka->sa); - handle_signal32(signr, ka, &info, oldset, regs, svr4_signal); + syscall_restart32(orig_i0, regs, &ka.sa); + handle_signal32(signr, &ka, &info, oldset, + regs, svr4_signal); return 1; } if (cookie.restart_syscall && --- linux-2.6.9-rc1/arch/sparc64/kernel/signal.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/sparc64/kernel/signal.c 2004-09-02 20:51:51.000000000 -0700 @@ -84,8 +84,8 @@ asmlinkage void sparc64_set_context(stru regs->tnpc = npc; err |= __get_user(regs->y, &((*grp)[MC_Y])); err |= __get_user(tstate, &((*grp)[MC_TSTATE])); - regs->tstate &= ~(TSTATE_ICC | TSTATE_XCC); - regs->tstate |= (tstate & (TSTATE_ICC | TSTATE_XCC)); + regs->tstate &= ~(TSTATE_ASI | TSTATE_ICC | TSTATE_XCC); + regs->tstate |= (tstate & (TSTATE_ASI | TSTATE_ICC | TSTATE_XCC)); err |= __get_user(regs->u_regs[UREG_G1], (&(*grp)[MC_G1])); err |= __get_user(regs->u_regs[UREG_G2], (&(*grp)[MC_G2])); err |= __get_user(regs->u_regs[UREG_G3], (&(*grp)[MC_G3])); @@ -135,7 +135,7 @@ asmlinkage void sparc64_set_context(stru return; do_sigsegv: - do_exit(SIGSEGV); + force_sig(SIGSEGV, current); } asmlinkage void sparc64_get_context(struct pt_regs *regs) @@ -226,7 +226,7 @@ asmlinkage void sparc64_get_context(stru return; do_sigsegv: - do_exit(SIGSEGV); + force_sig(SIGSEGV, current); } struct rt_signal_frame { @@ -408,9 +408,9 @@ void do_rt_sigreturn(struct pt_regs *reg err |= __get_user(tstate, &sf->regs.tstate); err |= copy_from_user(regs->u_regs, sf->regs.u_regs, sizeof(regs->u_regs)); - /* User can only change condition codes in %tstate. */ - regs->tstate &= ~(TSTATE_ICC | TSTATE_XCC); - regs->tstate |= (tstate & (TSTATE_ICC | TSTATE_XCC)); + /* User can only change condition codes and %asi in %tstate. */ + regs->tstate &= ~(TSTATE_ASI | TSTATE_ICC | TSTATE_XCC); + regs->tstate |= (tstate & (TSTATE_ASI | TSTATE_ICC | TSTATE_XCC)); err |= __get_user(fpu_save, &sf->fpu_save); if (fpu_save) @@ -439,7 +439,7 @@ void do_rt_sigreturn(struct pt_regs *reg spin_unlock_irq(¤t->sighand->siglock); return; segv: - send_sig(SIGSEGV, current, 1); + force_sig(SIGSEGV, current); } /* Checks if the fp is valid */ @@ -565,7 +565,7 @@ setup_rt_frame(struct k_sigaction *ka, s sigill: do_exit(SIGILL); sigsegv: - do_exit(SIGSEGV); + force_sigsegv(signo, current); } static inline void handle_signal(unsigned long signr, struct k_sigaction *ka, @@ -574,8 +574,6 @@ static inline void handle_signal(unsigne { setup_rt_frame(ka, regs, signr, oldset, (ka->sa.sa_flags & SA_SIGINFO) ? info : NULL); - if (ka->sa.sa_flags & SA_ONESHOT) - ka->sa.sa_handler = SIG_DFL; if (!(ka->sa.sa_flags & SA_NOMASK)) { spin_lock_irq(¤t->sighand->siglock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); @@ -615,6 +613,7 @@ static int do_signal(sigset_t *oldset, s { siginfo_t info; struct signal_deliver_cookie cookie; + struct k_sigaction ka; int signr; cookie.restart_syscall = restart_syscall; @@ -632,15 +631,11 @@ static int do_signal(sigset_t *oldset, s } #endif - signr = get_signal_to_deliver(&info, regs, &cookie); + signr = get_signal_to_deliver(&info, &ka, regs, &cookie); if (signr > 0) { - struct k_sigaction *ka; - - ka = ¤t->sighand->action[signr-1]; - if (cookie.restart_syscall) - syscall_restart(orig_i0, regs, &ka->sa); - handle_signal(signr, ka, &info, oldset, regs); + syscall_restart(orig_i0, regs, &ka.sa); + handle_signal(signr, &ka, &info, oldset, regs); return 1; } if (cookie.restart_syscall && --- linux-2.6.9-rc1/arch/sparc64/kernel/smp.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/arch/sparc64/kernel/smp.c 2004-09-03 00:56:41.003927104 -0700 @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -31,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -303,14 +303,7 @@ static int __devinit smp_boot_one_cpu(un struct task_struct *p; int timeout, ret, cpu_node; - kernel_thread(NULL, NULL, CLONE_IDLETASK); - - p = prev_task(&init_task); - - init_idle(p, cpu); - - unhash_process(p); - + p = fork_idle(cpu); callin_flag = 0; cpu_new_thread = p->thread_info; cpu_set(cpu, cpu_callout_map); @@ -981,8 +974,6 @@ void smp_promstop_others(void) smp_cross_call(&xcall_promstop, 0, 0, 0); } -extern void sparc64_do_profile(struct pt_regs *regs); - #define prof_multiplier(__cpu) cpu_data(__cpu).multiplier #define prof_counter(__cpu) cpu_data(__cpu).counter @@ -1008,7 +999,7 @@ void smp_percpu_timer_interrupt(struct p } do { - sparc64_do_profile(regs); + profile_tick(CPU_PROFILING, regs); if (!--prof_counter(cpu)) { irq_enter(); --- linux-2.6.9-rc1/arch/sparc64/kernel/sparc64_ksyms.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/sparc64/kernel/sparc64_ksyms.c 2004-09-03 00:56:41.004926952 -0700 @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include @@ -372,6 +371,12 @@ EXPORT_SYMBOL_NOVERS(memset); EXPORT_SYMBOL_NOVERS(memmove); EXPORT_SYMBOL_NOVERS(strncmp); +/* Delay routines. */ +EXPORT_SYMBOL(__udelay); +EXPORT_SYMBOL(__ndelay); +EXPORT_SYMBOL(__const_udelay); +EXPORT_SYMBOL(__delay); + void VISenter(void); /* RAID code needs this */ EXPORT_SYMBOL_NOVERS(VISenter); --- linux-2.6.9-rc1/arch/sparc64/kernel/sys32.S 2004-08-24 00:02:20.000000000 -0700 +++ 25/arch/sparc64/kernel/sys32.S 2004-09-02 20:51:51.000000000 -0700 @@ -120,7 +120,7 @@ SIGN1(sys32_setgroups, sys_setgroups, %o SIGN2(sys32_setpgid, sys_setpgid, %o0, %o1) SIGN3(sys32_setpriority, sys_setpriority, %o0, %o1, %o2) SIGN1(sys32_ssetmask, sys_ssetmask, %o0) -SIGN2(sys32_syslog, sys_syslog, %o0, %o1) +SIGN2(sys32_syslog, sys_syslog, %o0, %o2) SIGN1(sys32_umask, sys_umask, %o0) SIGN3(sys32_tgkill, sys_tgkill, %o0, %o1, %o2) SIGN1(sys32_sendto, sys_sendto, %o0) --- linux-2.6.9-rc1/arch/sparc64/kernel/sys_sparc32.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/sparc64/kernel/sys_sparc32.c 2004-09-02 20:51:51.000000000 -0700 @@ -1045,7 +1045,7 @@ asmlinkage long sys32_rt_sigpending(comp } asmlinkage long sys32_rt_sigtimedwait(compat_sigset_t __user *uthese, - siginfo_t32 __user *uinfo, + struct siginfo32 __user *uinfo, struct compat_timespec __user *uts, compat_size_t sigsetsize) { @@ -1130,15 +1130,15 @@ asmlinkage long sys32_rt_sigtimedwait(co } asmlinkage long compat_sys_rt_sigqueueinfo(int pid, int sig, - siginfo_t32 __user *uinfo) + struct siginfo32 __user *uinfo) { siginfo_t info; int ret; mm_segment_t old_fs = get_fs(); - if (copy_from_user (&info, uinfo, 3*sizeof(int)) || - copy_from_user (info._sifields._pad, uinfo->_sifields._pad, SI_PAD_SIZE)) + if (copy_siginfo_to_kernel32(&info, uinfo)) return -EFAULT; + set_fs (KERNEL_DS); ret = sys_rt_sigqueueinfo(pid, sig, (siginfo_t __user *) &info); set_fs (old_fs); @@ -1736,3 +1736,23 @@ sys32_timer_create(u32 clock, struct sig return err; } +asmlinkage long compat_sys_waitid(u32 which, u32 pid, + struct siginfo32 __user *uinfo, u32 options) +{ + siginfo_t info; + long ret; + mm_segment_t old_fs = get_fs(); + + memset(&info, 0, sizeof(info)); + + set_fs (KERNEL_DS); + ret = sys_waitid((int)which, (compat_pid_t) pid, + (siginfo_t __user *) &info, (int) options); + set_fs (old_fs); + + if (ret < 0 || info.si_signo == 0) + return ret; + BUG_ON(info.si_code & __SI_MASK); + info.si_code |= __SI_CHLD; + return copy_siginfo_to_user32(uinfo, &info); +} --- linux-2.6.9-rc1/arch/sparc64/kernel/systbls.S 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/sparc64/kernel/systbls.S 2004-09-02 20:51:51.000000000 -0700 @@ -75,7 +75,7 @@ sys_call_table32: /*260*/ .word compat_sys_sched_getaffinity, compat_sys_sched_setaffinity, sys32_timer_settime, compat_timer_gettime, sys_timer_getoverrun .word sys_timer_delete, sys32_timer_create, sys_ni_syscall, compat_sys_io_setup, sys_io_destroy /*270*/ .word sys32_io_submit, sys_io_cancel, compat_sys_io_getevents, sys32_mq_open, sys_mq_unlink - .word sys_mq_timedsend, sys_mq_timedreceive, compat_sys_mq_notify, compat_sys_mq_getsetattr, sys_ni_syscall + .word sys_mq_timedsend, sys_mq_timedreceive, compat_sys_mq_notify, compat_sys_mq_getsetattr, compat_sys_waitid /*280*/ .word sys_ni_syscall, sys_ni_syscall, sys_ni_syscall #endif /* CONFIG_COMPAT */ @@ -141,7 +141,7 @@ sys_call_table: /*260*/ .word sys_sched_getaffinity, sys_sched_setaffinity, sys_timer_settime, sys_timer_gettime, sys_timer_getoverrun .word sys_timer_delete, sys_timer_create, sys_ni_syscall, sys_io_setup, sys_io_destroy /*270*/ .word sys_io_submit, sys_io_cancel, sys_io_getevents, sys_mq_open, sys_mq_unlink - .word sys_mq_timedsend, sys_mq_timedreceive, sys_mq_notify, sys_mq_getsetattr, sys_ni_syscall + .word sys_mq_timedsend, sys_mq_timedreceive, sys_mq_notify, sys_mq_getsetattr, sys_waitid /*280*/ .word sys_ni_syscall, sys_ni_syscall, sys_ni_syscall #if defined(CONFIG_SUNOS_EMUL) || defined(CONFIG_SOLARIS_EMUL) || \ --- linux-2.6.9-rc1/arch/sparc64/kernel/time.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/sparc64/kernel/time.c 2004-09-02 20:51:51.000000000 -0700 @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -441,28 +442,6 @@ static inline void timer_check_rtc(void) } } -void sparc64_do_profile(struct pt_regs *regs) -{ - unsigned long pc; - - profile_hook(regs); - - if (user_mode(regs)) - return; - - if (!prof_buffer) - return; - - pc = regs->tpc; - - pc -= (unsigned long) _stext; - pc >>= prof_shift; - - if(pc >= prof_len) - pc = prof_len - 1; - atomic_inc((atomic_t *)&prof_buffer[pc]); -} - static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs * regs) { unsigned long ticks, pstate; @@ -471,7 +450,7 @@ static irqreturn_t timer_interrupt(int i do { #ifndef CONFIG_SMP - sparc64_do_profile(regs); + profile_tick(CPU_PROFILING, regs); #endif do_timer(regs); --- linux-2.6.9-rc1/arch/sparc64/kernel/traps.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/sparc64/kernel/traps.c 2004-09-02 20:51:51.000000000 -0700 @@ -36,10 +36,24 @@ #include #include #include +#include #ifdef CONFIG_KMOD #include #endif +struct notifier_block *sparc64die_chain; +static spinlock_t die_notifier_lock = SPIN_LOCK_UNLOCKED; + +int register_die_notifier(struct notifier_block *nb) +{ + int err = 0; + unsigned long flags; + spin_lock_irqsave(&die_notifier_lock, flags); + err = notifier_chain_register(&sparc64die_chain, nb); + spin_unlock_irqrestore(&die_notifier_lock, flags); + return err; +} + /* When an irrecoverable trap occurs at tl > 0, the trap entry * code logs the trap state registers at every level in the trap * stack. It is found at (pt_regs + sizeof(pt_regs)) and the layout @@ -71,11 +85,20 @@ static void dump_tl1_traplog(struct tl1_ } } -void bad_trap (struct pt_regs *regs, long lvl) +void do_call_debug(struct pt_regs *regs) +{ + notify_die(DIE_CALL, "debug call", regs, 0, 255, SIGINT); +} + +void bad_trap(struct pt_regs *regs, long lvl) { char buffer[32]; siginfo_t info; + if (notify_die(DIE_TRAP, "bad trap", regs, + 0, lvl, SIGTRAP) == NOTIFY_OK) + return; + if (lvl < 0x100) { sprintf(buffer, "Bad hw trap %lx at tl0\n", lvl); die_if_kernel(buffer, regs); @@ -84,7 +107,7 @@ void bad_trap (struct pt_regs *regs, lon lvl -= 0x100; if (regs->tstate & TSTATE_PRIV) { sprintf(buffer, "Kernel bad sw trap %lx", lvl); - die_if_kernel (buffer, regs); + die_if_kernel(buffer, regs); } if (test_thread_flag(TIF_32BIT)) { regs->tpc &= 0xffffffff; @@ -98,10 +121,14 @@ void bad_trap (struct pt_regs *regs, lon force_sig_info(SIGILL, &info, current); } -void bad_trap_tl1 (struct pt_regs *regs, long lvl) +void bad_trap_tl1(struct pt_regs *regs, long lvl) { char buffer[32]; + if (notify_die(DIE_TRAP_TL1, "bad trap tl1", regs, + 0, lvl, SIGTRAP) == NOTIFY_OK) + return; + dump_tl1_traplog((struct tl1_traplog *)(regs + 1)); sprintf (buffer, "Bad trap %lx at tl>0", lvl); @@ -121,6 +148,10 @@ void instruction_access_exception(struct { siginfo_t info; + if (notify_die(DIE_TRAP, "instruction access exception", regs, + 0, 0x8, SIGTRAP) == NOTIFY_OK) + return; + if (regs->tstate & TSTATE_PRIV) { printk("instruction_access_exception: SFSR[%016lx] SFAR[%016lx], going.\n", sfsr, sfar); @@ -141,15 +172,23 @@ void instruction_access_exception(struct void instruction_access_exception_tl1(struct pt_regs *regs, unsigned long sfsr, unsigned long sfar) { + if (notify_die(DIE_TRAP_TL1, "instruction access exception tl1", regs, + 0, 0x8, SIGTRAP) == NOTIFY_OK) + return; + dump_tl1_traplog((struct tl1_traplog *)(regs + 1)); instruction_access_exception(regs, sfsr, sfar); } -void data_access_exception (struct pt_regs *regs, - unsigned long sfsr, unsigned long sfar) +void data_access_exception(struct pt_regs *regs, + unsigned long sfsr, unsigned long sfar) { siginfo_t info; + if (notify_die(DIE_TRAP, "data access exception", regs, + 0, 0x30, SIGTRAP) == NOTIFY_OK) + return; + if (regs->tstate & TSTATE_PRIV) { /* Test if this comes from uaccess places. */ unsigned long fixup; @@ -220,6 +259,10 @@ void do_iae(struct pt_regs *regs) spitfire_clean_and_reenable_l1_caches(); + if (notify_die(DIE_TRAP, "instruction access exception", regs, + 0, 0x8, SIGTRAP) == NOTIFY_OK) + return; + info.si_signo = SIGBUS; info.si_errno = 0; info.si_code = BUS_OBJERR; @@ -230,6 +273,8 @@ void do_iae(struct pt_regs *regs) void do_dae(struct pt_regs *regs) { + siginfo_t info; + #ifdef CONFIG_PCI if (pci_poke_in_progress && pci_poke_cpu == smp_processor_id()) { spitfire_clean_and_reenable_l1_caches(); @@ -244,7 +289,18 @@ void do_dae(struct pt_regs *regs) return; } #endif - do_iae(regs); + spitfire_clean_and_reenable_l1_caches(); + + if (notify_die(DIE_TRAP, "data access exception", regs, + 0, 0x30, SIGTRAP) == NOTIFY_OK) + return; + + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_OBJERR; + info.si_addr = (void *)0; + info.si_trapno = 0; + force_sig_info(SIGBUS, &info, current); } static char ecc_syndrome_table[] = { @@ -1638,6 +1694,10 @@ void do_fpe_common(struct pt_regs *regs) void do_fpieee(struct pt_regs *regs) { + if (notify_die(DIE_TRAP, "fpu exception ieee", regs, + 0, 0x24, SIGFPE) == NOTIFY_OK) + return; + do_fpe_common(regs); } @@ -1648,6 +1708,10 @@ void do_fpother(struct pt_regs *regs) struct fpustate *f = FPUSTATE; int ret = 0; + if (notify_die(DIE_TRAP, "fpu exception other", regs, + 0, 0x25, SIGFPE) == NOTIFY_OK) + return; + switch ((current_thread_info()->xfsr[0] & 0x1c000)) { case (2 << 14): /* unfinished_FPop */ case (3 << 14): /* unimplemented_FPop */ @@ -1663,6 +1727,10 @@ void do_tof(struct pt_regs *regs) { siginfo_t info; + if (notify_die(DIE_TRAP, "tagged arithmetic overflow", regs, + 0, 0x26, SIGEMT) == NOTIFY_OK) + return; + if (regs->tstate & TSTATE_PRIV) die_if_kernel("Penguin overflow trap from kernel mode", regs); if (test_thread_flag(TIF_32BIT)) { @@ -1681,6 +1749,10 @@ void do_div0(struct pt_regs *regs) { siginfo_t info; + if (notify_die(DIE_TRAP, "integer division by zero", regs, + 0, 0x28, SIGFPE) == NOTIFY_OK) + return; + if (regs->tstate & TSTATE_PRIV) die_if_kernel("TL0: Kernel divide by zero.", regs); if (test_thread_flag(TIF_32BIT)) { @@ -1786,6 +1858,7 @@ void die_if_kernel(char *str, struct pt_ " \\__U_/\n"); printk("%s(%d): %s [#%d]\n", current->comm, current->pid, str, ++die_counter); + notify_die(DIE_OOPS, str, regs, 0, 255, SIGSEGV); __asm__ __volatile__("flushw"); __show_regs(regs); if (regs->tstate & TSTATE_PRIV) { @@ -1834,6 +1907,10 @@ void do_illegal_instruction(struct pt_re u32 insn; siginfo_t info; + if (notify_die(DIE_TRAP, "illegal instruction", regs, + 0, 0x10, SIGILL) == NOTIFY_OK) + return; + if (tstate & TSTATE_PRIV) die_if_kernel("Kernel illegal instruction", regs); if (test_thread_flag(TIF_32BIT)) @@ -1859,6 +1936,10 @@ void mem_address_unaligned(struct pt_reg { siginfo_t info; + if (notify_die(DIE_TRAP, "memory address unaligned", regs, + 0, 0x34, SIGSEGV) == NOTIFY_OK) + return; + if (regs->tstate & TSTATE_PRIV) { extern void kernel_unaligned_trap(struct pt_regs *regs, unsigned int insn, @@ -1881,6 +1962,10 @@ void do_privop(struct pt_regs *regs) { siginfo_t info; + if (notify_die(DIE_TRAP, "privileged operation", regs, + 0, 0x11, SIGILL) == NOTIFY_OK) + return; + if (test_thread_flag(TIF_32BIT)) { regs->tpc &= 0xffffffff; regs->tnpc &= 0xffffffff; --- linux-2.6.9-rc1/arch/sparc64/kernel/ttable.S 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/sparc64/kernel/ttable.S 2004-09-02 20:51:51.000000000 -0700 @@ -158,7 +158,7 @@ tl0_resv164: BTRAP(0x164) BTRAP(0x165) B tl0_resv169: BTRAP(0x169) BTRAP(0x16a) BTRAP(0x16b) BTRAP(0x16c) tl0_linux64: LINUX_64BIT_SYSCALL_TRAP tl0_gsctx: TRAP(sparc64_get_context) TRAP(sparc64_set_context) -tl0_resv170: BTRAP(0x170) BTRAP(0x171) BTRAP(0x172) +tl0_resv170: KPROBES_TRAP(0x170) KPROBES_TRAP(0x171) BTRAP(0x172) tl0_resv173: BTRAP(0x173) BTRAP(0x174) BTRAP(0x175) BTRAP(0x176) BTRAP(0x177) tl0_resv178: BTRAP(0x178) BTRAP(0x179) BTRAP(0x17a) BTRAP(0x17b) BTRAP(0x17c) tl0_resv17d: BTRAP(0x17d) BTRAP(0x17e) BTRAP(0x17f) --- linux-2.6.9-rc1/arch/sparc64/kernel/vmlinux.lds.S 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/sparc64/kernel/vmlinux.lds.S 2004-09-02 20:51:51.000000000 -0700 @@ -52,9 +52,6 @@ SECTIONS __setup_start = .; .init.setup : { *(.init.setup) } __setup_end = .; - __start___param = .; - __param : { *(__param) } - __stop___param = .; __initcall_start = .; .initcall.init : { *(.initcall1.init) --- linux-2.6.9-rc1/arch/sparc64/lib/atomic.S 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/sparc64/lib/atomic.S 2004-09-02 20:51:51.000000000 -0700 @@ -10,6 +10,7 @@ .align 64 .globl __atomic_add + .type __atomic_add,#function __atomic_add: /* %o0 = increment, %o1 = atomic_ptr */ lduw [%o1], %g5 add %g5, %o0, %g7 @@ -19,8 +20,10 @@ __atomic_add: /* %o0 = increment, %o1 = membar #StoreLoad | #StoreStore retl add %g7, %o0, %o0 + .size __atomic_add, .-__atomic_add .globl __atomic_sub + .type __atomic_sub,#function __atomic_sub: /* %o0 = increment, %o1 = atomic_ptr */ lduw [%o1], %g5 sub %g5, %o0, %g7 @@ -30,8 +33,10 @@ __atomic_sub: /* %o0 = increment, %o1 = membar #StoreLoad | #StoreStore retl sub %g7, %o0, %o0 + .size __atomic_sub, .-__atomic_sub .globl __atomic64_add + .type __atomic64_add,#function __atomic64_add: /* %o0 = increment, %o1 = atomic_ptr */ ldx [%o1], %g5 add %g5, %o0, %g7 @@ -41,8 +46,10 @@ __atomic64_add: /* %o0 = increment, %o1 membar #StoreLoad | #StoreStore retl add %g7, %o0, %o0 + .size __atomic64_add, .-__atomic64_add .globl __atomic64_sub + .type __atomic64_sub,#function __atomic64_sub: /* %o0 = increment, %o1 = atomic_ptr */ ldx [%o1], %g5 sub %g5, %o0, %g7 @@ -52,4 +59,4 @@ __atomic64_sub: /* %o0 = increment, %o1 membar #StoreLoad | #StoreStore retl sub %g7, %o0, %o0 - + .size __atomic64_sub, .-__atomic64_sub --- linux-2.6.9-rc1/arch/sparc64/lib/bitops.S 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/sparc64/lib/bitops.S 2004-09-02 20:51:51.000000000 -0700 @@ -9,6 +9,7 @@ .text .align 64 .globl ___test_and_set_bit + .type ___test_and_set_bit,#function ___test_and_set_bit: /* %o0=nr, %o1=addr */ srlx %o0, 6, %g1 mov 1, %g5 @@ -26,8 +27,10 @@ ___test_and_set_bit: /* %o0=nr, %o1=addr ldx [%o1], %g7 2: retl membar #StoreLoad | #StoreStore + .size ___test_and_set_bit, .-___test_and_set_bit .globl ___test_and_clear_bit + .type ___test_and_clear_bit,#function ___test_and_clear_bit: /* %o0=nr, %o1=addr */ srlx %o0, 6, %g1 mov 1, %g5 @@ -45,8 +48,10 @@ ___test_and_clear_bit: /* %o0=nr, %o1=ad ldx [%o1], %g7 2: retl membar #StoreLoad | #StoreStore + .size ___test_and_clear_bit, .-___test_and_clear_bit .globl ___test_and_change_bit + .type ___test_and_change_bit,#function ___test_and_change_bit: /* %o0=nr, %o1=addr */ srlx %o0, 6, %g1 mov 1, %g5 @@ -64,3 +69,4 @@ ___test_and_change_bit: /* %o0=nr, %o1=a 2: retl membar #StoreLoad | #StoreStore nop + .size ___test_and_change_bit, .-___test_and_change_bit --- linux-2.6.9-rc1/arch/sparc64/lib/copy_in_user.S 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/sparc64/lib/copy_in_user.S 2004-09-02 20:51:51.000000000 -0700 @@ -31,7 +31,8 @@ * to copy register windows around during thread cloning. */ - .globl ___copy_in_user + .globl ___copy_in_user + .type ___copy_in_user,#function ___copy_in_user: /* %o0=dst, %o1=src, %o2=len */ /* Writing to %asi is _expensive_ so we hardcode it. * Reading %asi to check for KERNEL_DS is comparatively @@ -99,11 +100,14 @@ ___copy_in_user: /* %o0=dst, %o1=src, %o retl clr %o0 + .size ___copy_in_user, .-___copy_in_user + /* Act like copy_{to,in}_user(), ie. return zero instead * of original destination pointer. This is invoked when * copy_{to,in}_user() finds that %asi is kernel space. */ .globl memcpy_user_stub + .type memcpy_user_stub,#function memcpy_user_stub: save %sp, -192, %sp mov %i0, %o0 @@ -112,3 +116,4 @@ memcpy_user_stub: mov %i2, %o2 ret restore %g0, %g0, %o0 + .size memcpy_user_stub, .-memcpy_user_stub --- linux-2.6.9-rc1/arch/sparc64/lib/copy_page.S 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/sparc64/lib/copy_page.S 2004-09-02 20:51:51.000000000 -0700 @@ -45,6 +45,7 @@ .align 32 .globl copy_user_page + .type copy_user_page,#function copy_user_page: /* %o0=dest, %o1=src, %o2=vaddr */ lduw [%g6 + TI_PRE_COUNT], %o4 sethi %uhi(PAGE_OFFSET), %g2 @@ -237,3 +238,5 @@ copy_user_page: /* %o0=dest, %o1=src, % retl stw %o4, [%g6 + TI_PRE_COUNT] + + .size copy_user_page, .-copy_user_page --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/sparc64/lib/delay.c 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,49 @@ +/* delay.c: Delay loops for sparc64 + * + * Copyright (C) 2004 David S. Miller + * + * Based heavily upon x86 variant which is: + * Copyright (C) 1993 Linus Torvalds + * Copyright (C) 1997 Martin Mares + */ + +#include + +void __delay(unsigned long loops) +{ + __asm__ __volatile__( +" b,pt %%xcc, 1f\n" +" cmp %0, 0\n" +" .align 32\n" +"1:\n" +" bne,pt %%xcc, 1b\n" +" subcc %0, 1, %0\n" + : "=&r" (loops) + : "0" (loops) + : "cc"); +} + +/* We used to multiply by HZ after shifting down by 32 bits + * but that runs into problems for higher values of HZ and + * slow cpus. + */ +void __const_udelay(unsigned long n) +{ + n *= 4; + + n *= (cpu_data(smp_processor_id()).udelay_val * (HZ/4)); + n >>= 32; + + __delay(n + 1); +} + +void __udelay(unsigned long n) +{ + __const_udelay(n * 0x10c7UL); +} + + +void __ndelay(unsigned long n) +{ + __const_udelay(n * 0x5UL); +} --- linux-2.6.9-rc1/arch/sparc64/lib/ipcsum.S 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/sparc64/lib/ipcsum.S 2004-09-02 20:51:51.000000000 -0700 @@ -1,6 +1,7 @@ .text - .align 32 - .globl ip_fast_csum + .align 32 + .globl ip_fast_csum + .type ip_fast_csum,#function ip_fast_csum: /* %o0 = iph, %o1 = ihl */ sub %o1, 4, %g7 lduw [%o0 + 0x00], %o2 @@ -30,3 +31,4 @@ ip_fast_csum: /* %o0 = iph, %o1 = ihl */ set 0xffff, %o1 retl and %o2, %o1, %o0 + .size ip_fast_csum, .-ip_fast_csum --- linux-2.6.9-rc1/arch/sparc64/lib/Makefile 2004-08-24 00:01:53.000000000 -0700 +++ 25/arch/sparc64/lib/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -12,7 +12,7 @@ lib-y := PeeCeeI.o copy_page.o clear_pag U1memcpy.o U1copy_from_user.o U1copy_to_user.o \ U3memcpy.o U3copy_from_user.o U3copy_to_user.o U3patch.o \ copy_in_user.o user_fixup.o memmove.o \ - mcount.o ipcsum.o rwsem.o xor.o splock.o find_bit.o + mcount.o ipcsum.o rwsem.o xor.o splock.o find_bit.o delay.o lib-$(CONFIG_DEBUG_SPINLOCK) += debuglocks.o lib-$(CONFIG_HAVE_DEC_LOCK) += dec_and_lock.o --- linux-2.6.9-rc1/arch/sparc64/lib/memmove.S 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/sparc64/lib/memmove.S 2004-09-02 20:51:51.000000000 -0700 @@ -5,8 +5,9 @@ */ .text - .align 32 - .globl memmove + .align 32 + .globl memmove + .type memmove,#function memmove: mov %o0, %g1 cmp %o0, %o1 @@ -29,3 +30,4 @@ memmove: retl mov %g1, %o0 + .size memmove, .-memmove --- linux-2.6.9-rc1/arch/sparc64/lib/rwlock.S 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/sparc64/lib/rwlock.S 2004-09-03 00:56:42.825650160 -0700 @@ -47,6 +47,20 @@ __write_unlock: /* %o0 = lock_ptr */ retl stw %g0, [%o0] + .globl __read_trylock +__read_trylock: /* %o0 = lock_ptr */ + ldsw [%o0], %g5 + brlz,pn %g5, 100f + add %g5, 1, %g7 + cas [%o0], %g5, %g7 + cmp %g5, %g7 + bne,pn %icc, __read_trylock + membar #StoreLoad | #StoreStore + retl + mov 1, %o0 +100: retl + mov 0, %o0 + .globl __write_lock __write_lock: /* %o0 = lock_ptr */ sethi %hi(0x80000000), %g2 --- linux-2.6.9-rc1/arch/sparc64/lib/splock.S 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/sparc64/lib/splock.S 2004-09-02 20:51:51.000000000 -0700 @@ -7,6 +7,7 @@ .align 64 .globl _raw_spin_lock + .type _raw_spin_lock,#function _raw_spin_lock: /* %o0 = lock_ptr */ 1: ldstub [%o0], %g7 brnz,pn %g7, 2f @@ -17,8 +18,10 @@ _raw_spin_lock: /* %o0 = lock_ptr */ brnz,pt %g7, 2b membar #LoadLoad ba,a,pt %xcc, 1b + .size _raw_spin_lock, .-_raw_spin_lock - .globl _raw_spin_lock_flags + .globl _raw_spin_lock_flags + .type _raw_spin_lock_flags,#function _raw_spin_lock_flags: /* %o0 = lock_ptr, %o1 = irq_flags */ 1: ldstub [%o0], %g7 brnz,pn %g7, 2f @@ -33,3 +36,4 @@ _raw_spin_lock_flags: /* %o0 = lock_ptr, membar #LoadLoad ba,pt %xcc, 1b ! Retry lock acquire wrpr %g2, %pil ! Restore PIL + .size _raw_spin_lock_flags, .-_raw_spin_lock_flags --- linux-2.6.9-rc1/arch/sparc64/lib/strlen.S 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/sparc64/lib/strlen.S 2004-09-02 20:51:51.000000000 -0700 @@ -9,7 +9,8 @@ #define HI_MAGIC 0x80808080 .align 32 - .global strlen + .globl strlen + .type strlen,#function strlen: mov %o0, %o1 andcc %o0, 3, %g0 @@ -75,3 +76,5 @@ strlen: 13: retl mov 2, %o0 + + .size strlen, .-strlen --- linux-2.6.9-rc1/arch/sparc64/lib/strncmp.S 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/sparc64/lib/strncmp.S 2004-09-02 20:51:51.000000000 -0700 @@ -7,8 +7,9 @@ #include .text - .align 4 - .global strncmp + .align 32 + .globl strncmp + .type strncmp,#function strncmp: brlez,pn %o2, 3f lduba [%o0] (ASI_PNF), %o3 @@ -28,3 +29,4 @@ strncmp: 3: retl clr %o0 + .size strncmp, .-strncmp --- linux-2.6.9-rc1/arch/sparc64/lib/strncpy_from_user.S 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/sparc64/lib/strncpy_from_user.S 2004-09-02 20:51:51.000000000 -0700 @@ -12,7 +12,7 @@ 0: .xword 0x0101010101010101 .text - .align 4 + .align 32 /* Must return: * @@ -31,6 +31,7 @@ */ .globl __strncpy_from_user + .type __strncpy_from_user,#function __strncpy_from_user: /* %o0=dest, %o1=src, %o2=count */ sethi %hi(0b), %o5 ! IEU0 Group @@ -122,6 +123,7 @@ __strncpy_from_user: mov %o2, %o0 2: retl add %o2, %o3, %o0 + .size __strncpy_from_user, .-__strncpy_from_user .section .fixup,#alloc,#execinstr .align 4 --- linux-2.6.9-rc1/arch/sparc64/lib/U1memcpy.S 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/sparc64/lib/U1memcpy.S 2004-09-02 20:51:51.000000000 -0700 @@ -117,6 +117,7 @@ .align 64 .globl FUNC_NAME + .type FUNC_NAME,#function FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */ PREAMBLE mov %o0, %g5 @@ -550,3 +551,5 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len add %o1, 1, %o1 retl mov EX_RETVAL(%g5), %o0 + + .size FUNC_NAME, .-FUNC_NAME --- linux-2.6.9-rc1/arch/sparc64/lib/U3memcpy.S 2004-08-24 00:03:49.000000000 -0700 +++ 25/arch/sparc64/lib/U3memcpy.S 2004-09-02 20:51:51.000000000 -0700 @@ -78,6 +78,7 @@ */ .globl FUNC_NAME + .type FUNC_NAME,#function FUNC_NAME: /* %o0=dst, %o1=src, %o2=len */ PREAMBLE mov %o0, %g5 @@ -412,3 +413,5 @@ FUNC_NAME: /* %o0=dst, %o1=src, %o2=len add %o1, 1, %o1 retl mov EX_RETVAL(%g5), %o0 + + .size FUNC_NAME, .-FUNC_NAME --- linux-2.6.9-rc1/arch/sparc64/lib/U3patch.S 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/sparc64/lib/U3patch.S 2004-09-02 20:51:51.000000000 -0700 @@ -22,9 +22,11 @@ flush %g2; .globl cheetah_patch_copyops + .type cheetah_patch_copyops,#function cheetah_patch_copyops: ULTRA3_DO_PATCH(memcpy, U3memcpy) ULTRA3_DO_PATCH(___copy_from_user, U3copy_from_user) ULTRA3_DO_PATCH(___copy_to_user, U3copy_to_user) retl nop + .size cheetah_patch_copyops,.-cheetah_patch_copyops --- linux-2.6.9-rc1/arch/sparc64/lib/xor.S 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/sparc64/lib/xor.S 2004-09-02 20:51:51.000000000 -0700 @@ -18,8 +18,9 @@ * !(len & 127) && len >= 256 */ .text - .globl xor_vis_2 - .type xor_vis_2,@function + .align 32 + .globl xor_vis_2 + .type xor_vis_2,#function xor_vis_2: rd %fprs, %o5 andcc %o5, FPRS_FEF|FPRS_DU, %g0 @@ -87,11 +88,10 @@ xor_vis_2: wr %g1, %g0, %asi retl wr %g0, 0, %fprs - .size xor_vis_2, .-xor_vis_2 + .size xor_vis_2, .-xor_vis_2 - - .globl xor_vis_3 - .type xor_vis_3,@function + .globl xor_vis_3 + .type xor_vis_3,#function xor_vis_3: rd %fprs, %o5 andcc %o5, FPRS_FEF|FPRS_DU, %g0 @@ -156,11 +156,10 @@ xor_vis_3: wr %g1, %g0, %asi retl wr %g0, 0, %fprs - .size xor_vis_3, .-xor_vis_3 - + .size xor_vis_3, .-xor_vis_3 - .globl xor_vis_4 - .type xor_vis_4,@function + .globl xor_vis_4 + .type xor_vis_4,#function xor_vis_4: rd %fprs, %o5 andcc %o5, FPRS_FEF|FPRS_DU, %g0 @@ -244,11 +243,10 @@ xor_vis_4: wr %g1, %g0, %asi retl wr %g0, 0, %fprs - .size xor_vis_4, .-xor_vis_4 - + .size xor_vis_4, .-xor_vis_4 - .globl xor_vis_5 - .type xor_vis_5,@function + .globl xor_vis_5 + .type xor_vis_5,#function xor_vis_5: mov %o5, %g5 rd %fprs, %o5 @@ -353,4 +351,4 @@ xor_vis_5: wr %g1, %g0, %asi retl wr %g0, 0, %fprs - .size xor_vis_5, .-xor_vis_5 + .size xor_vis_5, .-xor_vis_5 --- linux-2.6.9-rc1/arch/sparc64/mm/fault.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/sparc64/mm/fault.c 2004-09-02 20:51:51.000000000 -0700 @@ -27,6 +27,7 @@ #include #include #include +#include #define ELEMENTS(arr) (sizeof (arr)/sizeof (arr[0])) @@ -147,6 +148,9 @@ static void unhandled_fault(unsigned lon printk(KERN_ALERT "tsk->{mm,active_mm}->pgd = %016lx\n", (tsk->mm ? (unsigned long) tsk->mm->pgd : (unsigned long) tsk->active_mm->pgd)); + if (notify_die(DIE_GPF, "general protection fault", regs, + 0, 0, SIGSEGV) == NOTIFY_OK) + return; die_if_kernel("Oops", regs); } @@ -318,8 +322,13 @@ asmlinkage void do_sparc64_fault(struct int si_code, fault_code; unsigned long address; - si_code = SEGV_MAPERR; fault_code = get_thread_fault_code(); + + if (notify_die(DIE_PAGE_FAULT, "page_fault", regs, + fault_code, 0, SIGSEGV) == NOTIFY_OK) + return; + + si_code = SEGV_MAPERR; address = current_thread_info()->fault_address; if ((fault_code & FAULT_CODE_ITLB) && --- linux-2.6.9-rc1/arch/sparc64/mm/init.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/arch/sparc64/mm/init.c 2004-09-02 20:51:51.000000000 -0700 @@ -1502,7 +1502,7 @@ void __init paging_init(void) zones_size[ZONE_DMA] = npages; zholes_size[ZONE_DMA] = npages - pages_avail; - free_area_init_node(0, &contig_page_data, NULL, zones_size, + free_area_init_node(0, &contig_page_data, zones_size, phys_base >> PAGE_SHIFT, zholes_size); mem_map = contig_page_data.node_mem_map; } --- linux-2.6.9-rc1/arch/sparc64/solaris/misc.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/sparc64/solaris/misc.c 2004-09-02 20:51:51.000000000 -0700 @@ -137,21 +137,34 @@ asmlinkage int solaris_brk(u32 brk) return sunos_brk(brk); } -#define set_utsfield(to, from, dotchop, countfrom) { \ - char *p; \ - int i, len = (countfrom) ? \ - ((sizeof(to) > sizeof(from) ? \ - sizeof(from) : sizeof(to))) : sizeof(to); \ - if (copy_to_user(to, from, len)) \ - return -EFAULT; \ - if (dotchop) \ - for (p=from,i=0; *p && *p != '.' && --len; p++,i++); \ - else \ - i = len - 1; \ - if (__put_user('\0', (char __user *)((to)+i))) \ - return -EFAULT; \ +static int __set_utsfield(char __user *to, int to_size, + const char *from, int from_size, + int dotchop, int countfrom) +{ + int len = countfrom ? (to_size > from_size ? + from_size : to_size) : to_size; + int off; + + if (copy_to_user(to, from, len)) + return -EFAULT; + + if (dotchop) { + off = (strnchr(from, len, '.') - from); + } else{ + off = len - 1; + } + + if (__put_user('\0', to + off)) + return -EFAULT; + + return 0; } +#define set_utsfield(to, from, dotchop, countfrom) \ + __set_utsfield((to), sizeof(to), \ + (from), sizeof(from), \ + (dotchop), (countfrom)) + struct sol_uname { char sysname[9]; char nodename[9]; @@ -219,17 +232,20 @@ static char *serial(char *buffer) asmlinkage int solaris_utssys(u32 buf, u32 flags, int which, u32 buf2) { struct sol_uname __user *v = A(buf); + int err; + switch (which) { case 0: /* old uname */ /* Let's cheat */ - set_utsfield(v->sysname, "SunOS", 1, 0); + err = set_utsfield(v->sysname, "SunOS", 1, 0); down_read(&uts_sem); - set_utsfield(v->nodename, system_utsname.nodename, 1, 1); + err |= set_utsfield(v->nodename, system_utsname.nodename, + 1, 1); up_read(&uts_sem); - set_utsfield(v->release, "2.6", 0, 0); - set_utsfield(v->version, "Generic", 0, 0); - set_utsfield(v->machine, machine(), 0, 0); - return 0; + err |= set_utsfield(v->release, "2.6", 0, 0); + err |= set_utsfield(v->version, "Generic", 0, 0); + err |= set_utsfield(v->machine, machine(), 0, 0); + return (err ? -EFAULT : 0); case 2: /* ustat */ return -ENOSYS; case 3: /* fusers */ @@ -242,15 +258,18 @@ asmlinkage int solaris_utssys(u32 buf, u asmlinkage int solaris_utsname(u32 buf) { struct sol_utsname __user *v = A(buf); + int err; + /* Why should we not lie a bit? */ down_read(&uts_sem); - set_utsfield(v->sysname, "SunOS", 0, 0); - set_utsfield(v->nodename, system_utsname.nodename, 1, 1); - set_utsfield(v->release, "5.6", 0, 0); - set_utsfield(v->version, "Generic", 0, 0); - set_utsfield(v->machine, machine(), 0, 0); + err = set_utsfield(v->sysname, "SunOS", 0, 0); + err |= set_utsfield(v->nodename, system_utsname.nodename, 1, 1); + err |= set_utsfield(v->release, "5.6", 0, 0); + err |= set_utsfield(v->version, "Generic", 0, 0); + err |= set_utsfield(v->machine, machine(), 0, 0); up_read(&uts_sem); - return 0; + + return (err ? -EFAULT : 0); } #define SI_SYSNAME 1 /* return name of operating system */ --- linux-2.6.9-rc1/arch/sparc/Kconfig 2004-08-24 00:01:50.000000000 -0700 +++ 25/arch/sparc/Kconfig 2004-09-02 20:51:51.000000000 -0700 @@ -23,7 +23,7 @@ config GENERIC_ISA_DMA source "init/Kconfig" -menu "General setup" +menu "General machine setup" config VT bool --- linux-2.6.9-rc1/arch/sparc/kernel/irq.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/arch/sparc/kernel/irq.c 2004-09-03 00:56:41.004926952 -0700 @@ -45,7 +45,6 @@ #include #include #include -#include #include #include --- linux-2.6.9-rc1/arch/sparc/kernel/process.c 2004-08-24 00:01:55.000000000 -0700 +++ 25/arch/sparc/kernel/process.c 2004-09-02 20:51:51.000000000 -0700 @@ -435,8 +435,6 @@ asmlinkage int sparc_do_fork(unsigned lo { unsigned long parent_tid_ptr, child_tid_ptr; - clone_flags &= ~CLONE_IDLETASK; - parent_tid_ptr = regs->u_regs[UREG_I2]; child_tid_ptr = regs->u_regs[UREG_I4]; --- linux-2.6.9-rc1/arch/sparc/kernel/ptrace.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/sparc/kernel/ptrace.c 2004-09-03 00:56:56.201616704 -0700 @@ -614,12 +614,9 @@ asmlinkage void syscall_trace(void) return; if (!(current->ptrace & PT_PTRACED)) return; - current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0); - current->state = TASK_STOPPED; current->thread.flags ^= MAGIC_CONSTANT; - notify_parent(current, SIGCHLD); - schedule(); + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); /* * this isn't the same as continuing with a signal, but it will do * for normal use. strace only continues with a signal if the --- linux-2.6.9-rc1/arch/sparc/kernel/setup.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/arch/sparc/kernel/setup.c 2004-09-03 00:56:41.005926800 -0700 @@ -44,7 +44,6 @@ #include #include #include -#include #include #include #include --- linux-2.6.9-rc1/arch/sparc/kernel/signal.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/arch/sparc/kernel/signal.c 2004-09-02 20:51:51.000000000 -0700 @@ -274,7 +274,7 @@ static inline void do_new_sigreturn (str return; segv_and_exit: - do_exit(SIGSEGV); + force_sig(SIGSEGV, current); } asmlinkage void do_sigreturn(struct pt_regs *regs) @@ -341,7 +341,7 @@ asmlinkage void do_sigreturn(struct pt_r return; segv_and_exit: - send_sig(SIGSEGV, current, 1); + force_sig(SIGSEGV, current); } asmlinkage void do_rt_sigreturn(struct pt_regs *regs) @@ -401,7 +401,7 @@ asmlinkage void do_rt_sigreturn(struct p spin_unlock_irq(¤t->sighand->siglock); return; segv: - send_sig(SIGSEGV, current, 1); + force_sig(SIGSEGV, current); } /* Checks if the fp is valid */ @@ -549,7 +549,7 @@ setup_frame(struct sigaction *sa, struct sigill_and_return: do_exit(SIGILL); sigsegv: - do_exit(SIGSEGV); + force_sigsegv(signr, current); } @@ -663,7 +663,7 @@ new_setup_frame(struct k_sigaction *ka, sigill_and_return: do_exit(SIGILL); sigsegv: - do_exit(SIGSEGV); + force_sigsegv(signo, current); } static inline void @@ -746,7 +746,7 @@ new_setup_rt_frame(struct k_sigaction *k sigill: do_exit(SIGILL); sigsegv: - do_exit(SIGSEGV); + force_sigsegv(signo, current); } /* Setup a Solaris stack frame */ @@ -876,7 +876,7 @@ setup_svr4_frame(struct sigaction *sa, u sigill_and_return: do_exit(SIGILL); sigsegv: - do_exit(SIGSEGV); + force_sigsegv(signr, current); } asmlinkage int svr4_getcontext(svr4_ucontext_t __user *uc, struct pt_regs *regs) @@ -889,7 +889,7 @@ asmlinkage int svr4_getcontext(svr4_ucon synchronize_user_stack(); if (current_thread_info()->w_saved) - goto sigsegv_and_return; + return -EFAULT; err = clear_user(uc, sizeof(*uc)); if (err) @@ -930,9 +930,6 @@ asmlinkage int svr4_getcontext(svr4_ucon * we have already stuffed all of it with sync_user_stack */ return (err ? -EFAULT : 0); - -sigsegv_and_return: - do_exit(SIGSEGV); } /* Set the context for a svr4 application, this is Solaris way to sigreturn */ @@ -1018,7 +1015,7 @@ asmlinkage int svr4_setcontext(svr4_ucon return (err ? -EFAULT : 0); sigsegv_and_return: - do_exit(SIGSEGV); + force_sig(SIGSEGV, current); } static inline void @@ -1036,8 +1033,6 @@ handle_signal(unsigned long signr, struc else setup_frame(&ka->sa, regs, signr, oldset, info); } - if (ka->sa.sa_flags & SA_ONESHOT) - ka->sa.sa_handler = SIG_DFL; if (!(ka->sa.sa_flags & SA_NOMASK)) { spin_lock_irq(¤t->sighand->siglock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); @@ -1077,6 +1072,7 @@ asmlinkage int do_signal(sigset_t *oldse { siginfo_t info; struct sparc_deliver_cookie cookie; + struct k_sigaction ka; int signr; /* @@ -1096,15 +1092,12 @@ asmlinkage int do_signal(sigset_t *oldse if (!oldset) oldset = ¤t->blocked; - signr = get_signal_to_deliver(&info, regs, &cookie); + signr = get_signal_to_deliver(&info, &ka, regs, &cookie); if (signr > 0) { - struct k_sigaction *ka; - - ka = ¤t->sighand->action[signr-1]; - if (cookie.restart_syscall) - syscall_restart(cookie.orig_i0, regs, &ka->sa); - handle_signal(signr, ka, &info, oldset, regs, svr4_signal); + syscall_restart(cookie.orig_i0, regs, &ka.sa); + handle_signal(signr, &ka, &info, oldset, + regs, svr4_signal); return 1; } if (cookie.restart_syscall && --- linux-2.6.9-rc1/arch/sparc/kernel/smp.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/sparc/kernel/smp.c 2004-09-03 00:56:41.005926800 -0700 @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include --- linux-2.6.9-rc1/arch/sparc/kernel/sparc_ksyms.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/sparc/kernel/sparc_ksyms.c 2004-09-03 00:56:41.006926648 -0700 @@ -41,7 +41,6 @@ #include #include #include -#include #include #include #include @@ -214,6 +213,10 @@ EXPORT_SYMBOL(pci_dma_sync_single_for_cp EXPORT_SYMBOL(pci_dma_sync_single_for_device); EXPORT_SYMBOL(pci_dma_sync_sg_for_cpu); EXPORT_SYMBOL(pci_dma_sync_sg_for_device); +EXPORT_SYMBOL(pci_map_sg); +EXPORT_SYMBOL(pci_unmap_sg); +EXPORT_SYMBOL(pci_map_page); +EXPORT_SYMBOL(pci_unmap_page); /* Actually, ioremap/iounmap are not PCI specific. But it is ok for drivers. */ EXPORT_SYMBOL(ioremap); EXPORT_SYMBOL(iounmap); @@ -298,6 +301,9 @@ EXPORT_SYMBOL(csum_partial); /* Cache flushing. */ EXPORT_SYMBOL(sparc_flush_page_to_ram); +/* For when serial stuff is built as modules. */ +EXPORT_SYMBOL(sun_do_break); + EXPORT_SYMBOL(__ret_efault); EXPORT_SYMBOL(memcmp); --- linux-2.6.9-rc1/arch/sparc/kernel/sun4d_smp.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/sparc/kernel/sun4d_smp.c 2004-09-03 00:56:41.006926648 -0700 @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -29,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -201,18 +201,9 @@ void __init smp4d_boot_cpus(void) int no; /* Cook up an idler for this guy. */ - kernel_thread(start_secondary, NULL, CLONE_IDLETASK); - + p = fork_idle(i); cpucount++; - - p = prev_task(&init_task); - - init_idle(p, i); - current_set[i] = p->thread_info; - - unhash_process(p); - for (no = 0; !cpu_find_by_instance(no, NULL, &mid) && mid != i; no++) ; @@ -418,8 +409,6 @@ void smp4d_message_pass(int target, int panic("Bogon SMP message pass."); } -extern void sparc_do_profile(unsigned long pc, unsigned long o7); - void smp4d_percpu_timer_interrupt(struct pt_regs *regs) { int cpu = hard_smp4d_processor_id(); @@ -437,8 +426,7 @@ void smp4d_percpu_timer_interrupt(struct show_leds(cpu); } - if(!user_mode(regs)) - sparc_do_profile(regs->pc, regs->u_regs[UREG_RETPC]); + profile_tick(CPU_PROFILING, regs); if(!--prof_counter(cpu)) { int user = user_mode(regs); --- linux-2.6.9-rc1/arch/sparc/kernel/sun4m_smp.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/sparc/kernel/sun4m_smp.c 2004-09-03 00:56:41.007926496 -0700 @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -28,7 +29,6 @@ #include #include #include -#include #include #define IRQ_RESCHEDULE 13 @@ -173,18 +173,9 @@ void __init smp4m_boot_cpus(void) int timeout; /* Cook up an idler for this guy. */ - kernel_thread(start_secondary, NULL, CLONE_IDLETASK); - + p = fork_idle(i); cpucount++; - - p = prev_task(&init_task); - - init_idle(p, i); - current_set[i] = p->thread_info; - - unhash_process(p); - /* See trampoline.S for details... */ entry += ((i-1) * 3); @@ -400,16 +391,13 @@ void smp4m_cross_call_irq(void) ccall_info.processors_out[i] = 1; } -extern void sparc_do_profile(unsigned long pc, unsigned long o7); - void smp4m_percpu_timer_interrupt(struct pt_regs *regs) { int cpu = smp_processor_id(); clear_profile_irq(cpu); - if(!user_mode(regs)) - sparc_do_profile(regs->pc, regs->u_regs[UREG_RETPC]); + profile_tick(CPU_PROFILING, regs); if(!--prof_counter(cpu)) { int user = user_mode(regs); --- linux-2.6.9-rc1/arch/sparc/kernel/systbls.S 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/sparc/kernel/systbls.S 2004-09-02 20:51:51.000000000 -0700 @@ -74,7 +74,7 @@ sys_call_table: /*260*/ .long sys_sched_getaffinity, sys_sched_setaffinity, sys_timer_settime, sys_timer_gettime, sys_timer_getoverrun /*265*/ .long sys_timer_delete, sys_timer_create, sys_nis_syscall, sys_io_setup, sys_io_destroy /*270*/ .long sys_io_submit, sys_io_cancel, sys_io_getevents, sys_mq_open, sys_mq_unlink -/*275*/ .long sys_mq_timedsend, sys_mq_timedreceive, sys_mq_notify, sys_mq_getsetattr, sys_ni_syscall +/*275*/ .long sys_mq_timedsend, sys_mq_timedreceive, sys_mq_notify, sys_mq_getsetattr, sys_waitid /*280*/ .long sys_ni_syscall, sys_ni_syscall, sys_ni_syscall #ifdef CONFIG_SUNOS_EMUL --- linux-2.6.9-rc1/arch/sparc/kernel/time.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/sparc/kernel/time.c 2004-09-02 20:51:51.000000000 -0700 @@ -79,38 +79,24 @@ struct intersil *intersil_clock; #endif -static spinlock_t ticker_lock = SPIN_LOCK_UNLOCKED; - -/* 32-bit Sparc specific profiling function. */ -void sparc_do_profile(unsigned long pc, unsigned long o7) +unsigned long profile_pc(struct pt_regs *regs) { - if(prof_buffer && current->pid) { - extern int _stext; - extern int __copy_user_begin, __copy_user_end; - extern int __atomic_begin, __atomic_end; - extern int __bzero_begin, __bzero_end; - extern int __bitops_begin, __bitops_end; - - if ((pc >= (unsigned long) &__copy_user_begin && - pc < (unsigned long) &__copy_user_end) || - (pc >= (unsigned long) &__atomic_begin && - pc < (unsigned long) &__atomic_end) || - (pc >= (unsigned long) &__bzero_begin && - pc < (unsigned long) &__bzero_end) || - (pc >= (unsigned long) &__bitops_begin && - pc < (unsigned long) &__bitops_end)) - pc = o7; - - pc -= (unsigned long) &_stext; - pc >>= prof_shift; - - spin_lock(&ticker_lock); - if(pc < prof_len) - prof_buffer[pc]++; - else - prof_buffer[prof_len - 1]++; - spin_unlock(&ticker_lock); - } + extern int __copy_user_begin, __copy_user_end; + extern int __atomic_begin, __atomic_end; + extern int __bzero_begin, __bzero_end; + extern int __bitops_begin, __bitops_end; + unsigned long pc = regs->pc; + + if ((pc >= (unsigned long) &__copy_user_begin && + pc < (unsigned long) &__copy_user_end) || + (pc >= (unsigned long) &__atomic_begin && + pc < (unsigned long) &__atomic_end) || + (pc >= (unsigned long) &__bzero_begin && + pc < (unsigned long) &__bzero_end) || + (pc >= (unsigned long) &__bitops_begin && + pc < (unsigned long) &__bitops_end)) + pc = regs->u_regs[UREG_RETPC]; + return pc; } __volatile__ unsigned int *master_l10_counter; @@ -129,8 +115,7 @@ irqreturn_t timer_interrupt(int irq, voi static long last_rtc_update; #ifndef CONFIG_SMP - if(!user_mode(regs)) - sparc_do_profile(regs->pc, regs->u_regs[UREG_RETPC]); + profile_tick(CPU_PROFILING, regs); #endif /* Protect counter clear so that do_gettimeoffset works */ --- linux-2.6.9-rc1/arch/sparc/kernel/vmlinux.lds.S 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/sparc/kernel/vmlinux.lds.S 2004-09-02 20:51:51.000000000 -0700 @@ -46,9 +46,6 @@ SECTIONS __setup_start = .; .init.setup : { *(.init.setup) } __setup_end = .; - __start___param = .; - __param : { *(__param) } - __stop___param = .; __initcall_start = .; .initcall.init : { *(.initcall1.init) --- linux-2.6.9-rc1/arch/sparc/mm/srmmu.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/sparc/mm/srmmu.c 2004-09-02 20:51:51.000000000 -0700 @@ -1341,7 +1341,7 @@ void __init srmmu_paging_init(void) zones_size[ZONE_HIGHMEM] = npages; zholes_size[ZONE_HIGHMEM] = npages - calc_highpages(); - free_area_init_node(0, &contig_page_data, NULL, zones_size, + free_area_init_node(0, &contig_page_data, zones_size, pfn_base, zholes_size); mem_map = contig_page_data.node_mem_map; } --- linux-2.6.9-rc1/arch/sparc/mm/sun4c.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/sparc/mm/sun4c.c 2004-09-02 20:51:51.000000000 -0700 @@ -2114,7 +2114,7 @@ void __init sun4c_paging_init(void) zones_size[ZONE_HIGHMEM] = npages; zholes_size[ZONE_HIGHMEM] = npages - calc_highpages(); - free_area_init_node(0, &contig_page_data, NULL, zones_size, + free_area_init_node(0, &contig_page_data, zones_size, pfn_base, zholes_size); mem_map = contig_page_data.node_mem_map; } --- linux-2.6.9-rc1/arch/um/config.release 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/um/config.release 2004-09-02 20:51:51.000000000 -0700 @@ -227,7 +227,6 @@ CONFIG_ROMFS_FS=m CONFIG_EXT2_FS=y CONFIG_SYSV_FS=m CONFIG_UDF_FS=m -# CONFIG_UDF_RW is not set CONFIG_UFS_FS=m # CONFIG_UFS_FS_WRITE is not set --- linux-2.6.9-rc1/arch/um/defconfig 2004-08-24 00:02:59.000000000 -0700 +++ 25/arch/um/defconfig 2004-09-02 20:51:51.000000000 -0700 @@ -1,47 +1,77 @@ # # Automatically generated make config: don't edit +# Linux kernel version: 2.6.8.1-mm3 +# Fri Aug 20 13:03:03 2004 # CONFIG_USERMODE=y CONFIG_MMU=y -CONFIG_SWAP=y CONFIG_UID16=y CONFIG_RWSEM_GENERIC_SPINLOCK=y -CONFIG_CONFIG_LOG_BUF_SHIFT=14 - -# -# Code maturity level options -# -CONFIG_EXPERIMENTAL=y # -# General Setup +# UML-specific options # CONFIG_MODE_TT=y CONFIG_MODE_SKAS=y CONFIG_NET=y -CONFIG_SYSVIPC=y -CONFIG_BSD_PROCESS_ACCT=y -CONFIG_SYSCTL=y -CONFIG_BINFMT_AOUT=y CONFIG_BINFMT_ELF=y CONFIG_BINFMT_MISC=y CONFIG_HOSTFS=y +CONFIG_HPPFS=y CONFIG_MCONSOLE=y -CONFIG_MAGIC_SYSRQ=y # CONFIG_HOST_2G_2G is not set # CONFIG_UML_SMP is not set # CONFIG_SMP is not set CONFIG_NEST_LEVEL=0 CONFIG_KERNEL_HALF_GIGS=1 # CONFIG_HIGHMEM is not set -CONFIG_PROC_MM=y CONFIG_KERNEL_STACK_ORDER=2 +CONFIG_UML_REAL_TIME_CLOCK=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_HOTPLUG is not set +# CONFIG_IKCONFIG is not set +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_FUTEX=y +CONFIG_EPOLL=y +# CONFIG_CPUSETS is not set +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set # # Loadable module support # -CONFIG_MODULES=y -# CONFIG_KMOD is not set +# CONFIG_MODULES is not set + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_DEBUG_DRIVER is not set # # Character Devices @@ -58,7 +88,8 @@ CONFIG_CON_ZERO_CHAN="fd:0,fd:1" CONFIG_CON_CHAN="xterm" CONFIG_SSL_CHAN="pty" CONFIG_UNIX98_PTYS=y -CONFIG_UNIX98_PTY_COUNT=256 +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 # CONFIG_WATCHDOG is not set CONFIG_UML_SOUND=y CONFIG_SOUND=y @@ -69,6 +100,7 @@ CONFIG_HOSTAUDIO=y # CONFIG_BLK_DEV_UBD=y # CONFIG_BLK_DEV_UBD_SYNC is not set +CONFIG_BLK_DEV_COW_COMMON=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_NBD=y CONFIG_BLK_DEV_RAM=y @@ -78,7 +110,7 @@ CONFIG_BLK_DEV_INITRD=y CONFIG_NETDEVICES=y # -# Network Devices +# UML Network Devices # CONFIG_UML_NET=y CONFIG_UML_NET_ETHERTAP=y @@ -88,22 +120,6 @@ CONFIG_UML_NET_DAEMON=y CONFIG_UML_NET_MCAST=y # CONFIG_UML_NET_PCAP is not set CONFIG_UML_NET_SLIRP=y -CONFIG_DUMMY=y -# CONFIG_BONDING is not set -# CONFIG_EQUALIZER is not set -CONFIG_TUN=y -# CONFIG_ETHERTAP is not set -CONFIG_PPP=y -# CONFIG_PPP_MULTILINK is not set -# CONFIG_PPP_ASYNC is not set -# CONFIG_PPP_SYNC_TTY is not set -# CONFIG_PPP_DEFLATE is not set -# CONFIG_PPP_BSDCOMP is not set -# CONFIG_PPPOE is not set -CONFIG_SLIP=y -# CONFIG_SLIP_COMPRESSED is not set -# CONFIG_SLIP_SMART is not set -# CONFIG_SLIP_MODE_SLIP6 is not set # # Networking support @@ -115,8 +131,6 @@ CONFIG_SLIP=y CONFIG_PACKET=y CONFIG_PACKET_MMAP=y # CONFIG_NETLINK_DEV is not set -# CONFIG_NETFILTER is not set -# CONFIG_FILTER is not set CONFIG_UNIX=y # CONFIG_NET_KEY is not set CONFIG_INET=y @@ -126,23 +140,24 @@ CONFIG_INET=y # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set # CONFIG_ARPD is not set -# CONFIG_INET_ECN is not set # CONFIG_SYN_COOKIES is not set # CONFIG_INET_AH is not set # CONFIG_INET_ESP is not set -# CONFIG_XFRM_USER is not set +# CONFIG_INET_IPCOMP is not set # CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set # # SCTP Configuration (EXPERIMENTAL) # -CONFIG_IPV6_SCTP__=y # CONFIG_IP_SCTP is not set # CONFIG_ATM is not set +# CONFIG_BRIDGE is not set # CONFIG_VLAN_8021Q is not set -# CONFIG_LLC is not set # CONFIG_DECNET is not set -# CONFIG_BRIDGE is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set # CONFIG_X25 is not set # CONFIG_LAPB is not set # CONFIG_NET_DIVERT is not set @@ -154,11 +169,24 @@ CONFIG_IPV6_SCTP__=y # QoS and/or fair queueing # # CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set # # Network testing # # CONFIG_NET_PKTGEN is not set +# CONFIG_KGDBOE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NETPOLL_RX is not set +# CONFIG_NETPOLL_TRAP is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_DUMMY=y +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=y # # Ethernet (10 or 100Mbit) @@ -170,83 +198,125 @@ CONFIG_IPV6_SCTP__=y # # -# Wireless LAN (non-hamradio) +# Ethernet (10000 Mbit) # -# CONFIG_NET_RADIO is not set # -# Token Ring devices (depends on LLC=y) +# Token Ring devices # -# CONFIG_SHAPER is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set # # Wan interfaces # # CONFIG_WAN is not set +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +# CONFIG_PPP_ASYNC is not set +# CONFIG_PPP_SYNC_TTY is not set +# CONFIG_PPP_DEFLATE is not set +# CONFIG_PPP_BSDCOMP is not set +# CONFIG_PPPOE is not set +CONFIG_SLIP=y +# CONFIG_SLIP_COMPRESSED is not set +# CONFIG_SLIP_SMART is not set +# CONFIG_SLIP_MODE_SLIP6 is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set # # File systems # +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +CONFIG_REISER4_FS=y +CONFIG_REISER4_LARGE_KEY=y +# CONFIG_REISER4_CHECK is not set +CONFIG_REISERFS_FS=y +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_REISERFS_FS_XATTR is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +CONFIG_MINIX_FS=y +# CONFIG_ROMFS_FS is not set CONFIG_QUOTA=y # CONFIG_QFMT_V1 is not set # CONFIG_QFMT_V2 is not set CONFIG_QUOTACTL=y -CONFIG_AUTOFS_FS=m -CONFIG_AUTOFS4_FS=m -CONFIG_REISERFS_FS=m -# CONFIG_REISERFS_CHECK is not set -# CONFIG_REISERFS_PROC_INFO is not set +CONFIG_AUTOFS_FS=y +CONFIG_AUTOFS4_FS=y + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# # CONFIG_ADFS_FS is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set # CONFIG_BEFS_FS is not set # CONFIG_BFS_FS is not set -# CONFIG_EXT3_FS is not set -# CONFIG_JBD is not set -CONFIG_FAT_FS=m -CONFIG_MSDOS_FS=m -CONFIG_VFAT_FS=m # CONFIG_EFS_FS is not set CONFIG_JFFS_FS=y CONFIG_JFFS_FS_VERBOSE=0 -CONFIG_JFFS_PROC_FS=y +# CONFIG_JFFS_PROC_FS is not set # CONFIG_JFFS2_FS is not set # CONFIG_CRAMFS is not set -# CONFIG_TMPFS is not set -CONFIG_RAMFS=y -CONFIG_ISO9660_FS=m -# CONFIG_JOLIET is not set -# CONFIG_ZISOFS is not set -# CONFIG_JFS_FS is not set -CONFIG_MINIX_FS=m # CONFIG_VXFS_FS is not set -# CONFIG_NTFS_FS is not set # CONFIG_HPFS_FS is not set -CONFIG_PROC_FS=y -CONFIG_DEVFS_FS=y -CONFIG_DEVFS_MOUNT=y -# CONFIG_DEVFS_DEBUG is not set -CONFIG_DEVPTS_FS=y # CONFIG_QNX4FS_FS is not set -# CONFIG_ROMFS_FS is not set -CONFIG_EXT2_FS=y -# CONFIG_EXT2_FS_XATTR is not set # CONFIG_SYSV_FS is not set -# CONFIG_UDF_FS is not set # CONFIG_UFS_FS is not set -# CONFIG_XFS_FS is not set # # Network File Systems # -# CONFIG_CODA_FS is not set -# CONFIG_INTERMEZZO_FS is not set # CONFIG_NFS_FS is not set # CONFIG_NFSD is not set # CONFIG_EXPORTFS is not set -# CONFIG_CIFS is not set # CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set # CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set # CONFIG_AFS_FS is not set # @@ -254,11 +324,11 @@ CONFIG_EXT2_FS=y # # CONFIG_PARTITION_ADVANCED is not set CONFIG_MSDOS_PARTITION=y -CONFIG_NLS=y # # Native Language Support # +CONFIG_NLS=y CONFIG_NLS_DEFAULT="iso8859-1" # CONFIG_NLS_CODEPAGE_437 is not set # CONFIG_NLS_CODEPAGE_737 is not set @@ -283,6 +353,7 @@ CONFIG_NLS_DEFAULT="iso8859-1" # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_CODEPAGE_1250 is not set # CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set # CONFIG_NLS_ISO8859_1 is not set # CONFIG_NLS_ISO8859_2 is not set # CONFIG_NLS_ISO8859_3 is not set @@ -301,6 +372,7 @@ CONFIG_NLS_DEFAULT="iso8859-1" # # Security options # +# CONFIG_KEYS is not set # CONFIG_SECURITY is not set # @@ -311,33 +383,14 @@ CONFIG_NLS_DEFAULT="iso8859-1" # # Library routines # +# CONFIG_CRC_CCITT is not set # CONFIG_CRC32 is not set +# CONFIG_LIBCRC32C is not set # # SCSI support # -CONFIG_SCSI=y -CONFIG_GENERIC_ISA_DMA=y - -# -# SCSI support type (disk, tape, CD-ROM) -# -CONFIG_BLK_DEV_SD=y -CONFIG_SD_EXTRA_DEVS=40 -CONFIG_CHR_DEV_ST=y -CONFIG_BLK_DEV_SR=y -CONFIG_BLK_DEV_SR_VENDOR=y -CONFIG_SR_EXTRA_DEVS=2 -CONFIG_CHR_DEV_SG=y - -# -# Some SCSI devices (e.g. CD jukebox) support multiple LUNs -# -CONFIG_SCSI_DEBUG_QUEUES=y -CONFIG_SCSI_MULTI_LUN=y -CONFIG_SCSI_CONSTANTS=y -CONFIG_SCSI_LOGGING=y -CONFIG_SCSI_DEBUG=y +# CONFIG_SCSI is not set # # Multi-device support (RAID and LVM) @@ -359,34 +412,46 @@ CONFIG_MTD_CHAR=y CONFIG_MTD_BLOCK=y # CONFIG_FTL is not set # CONFIG_NFTL is not set +# CONFIG_INFTL is not set # # RAM/ROM/Flash chip drivers # # CONFIG_MTD_CFI is not set # CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set # CONFIG_MTD_RAM is not set # CONFIG_MTD_ROM is not set # CONFIG_MTD_ABSENT is not set -# CONFIG_MTD_OBSOLETE_CHIPS is not set # # Mapping drivers for chip access # +# CONFIG_MTD_COMPLEX_MAPPINGS is not set # # Self-contained MTD device drivers # # CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set # CONFIG_MTD_MTDRAM is not set -CONFIG_MTD_BLKMTD=m +CONFIG_MTD_BLKMTD=y # # Disk-On-Chip Device Drivers # -# CONFIG_MTD_DOC1000 is not set # CONFIG_MTD_DOC2000 is not set # CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set # # NAND Flash Device Drivers @@ -396,6 +461,7 @@ CONFIG_MTD_BLKMTD=m # # Kernel hacking # +CONFIG_DEBUG_KERNEL=y # CONFIG_DEBUG_SLAB is not set # CONFIG_DEBUG_SPINLOCK is not set CONFIG_DEBUG_INFO=y --- linux-2.6.9-rc1/arch/um/drivers/chan_kern.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/um/drivers/chan_kern.c 2004-09-02 20:51:51.000000000 -0700 @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include "chan_kern.h" @@ -16,6 +17,7 @@ #include "irq_user.h" #include "sigio.h" #include "line.h" +#include "os.h" static void *not_configged_init(char *str, int device, struct chan_opts *opts) { @@ -86,6 +88,52 @@ static struct chan_ops not_configged_ops .winch = 0, }; +void generic_close(int fd, void *unused) +{ + os_close_file(fd); +} + +int generic_read(int fd, char *c_out, void *unused) +{ + int n; + + n = os_read_file(fd, c_out, sizeof(*c_out)); + + if(n == -EAGAIN) + return(0); + else if(n == 0) + return(-EIO); + return(n); +} + +int generic_write(int fd, const char *buf, int n, void *unused) +{ + return(os_write_file(fd, buf, n)); +} + +int generic_window_size(int fd, void *unused, unsigned short *rows_out, + unsigned short *cols_out) +{ + int rows, cols; + int ret; + + ret = os_window_size(fd, &rows, &cols); + if(ret < 0) + return(ret); + + ret = ((*rows_out != rows) || (*cols_out != cols)); + + *rows_out = rows; + *cols_out = cols; + + return(ret); +} + +void generic_free(void *data) +{ + kfree(data); +} + static void tty_receive_char(struct tty_struct *tty, char ch) { if(tty == NULL) return; @@ -265,6 +313,11 @@ static int one_chan_config_string(struct { int n = 0; + if(chan == NULL){ + CONFIG_CHUNK(str, size, n, "none", 1); + return(n); + } + CONFIG_CHUNK(str, size, n, chan->ops->type, 0); if(chan->dev == NULL){ @@ -420,7 +473,8 @@ int parse_chan_pair(char *str, struct li INIT_LIST_HEAD(chans); } - if((out = strchr(str, ',')) != NULL){ + out = strchr(str, ','); + if(out != NULL){ in = str; *out = '\0'; out++; @@ -475,12 +529,15 @@ void chan_interrupt(struct list_head *ch goto out; } err = chan->ops->read(chan->fd, &c, chan->data); - if(err > 0) tty_receive_char(tty, c); + if(err > 0) + tty_receive_char(tty, c); } while(err > 0); + if(err == 0) reactivate_fd(chan->fd, irq); if(err == -EIO){ if(chan->primary){ - if(tty != NULL) tty_hangup(tty); + if(tty != NULL) + tty_hangup(tty); line_disable(dev, irq); close_chan(chans); free_chan(chans); --- linux-2.6.9-rc1/arch/um/drivers/chan_user.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/arch/um/drivers/chan_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) * Licensed under the GPL */ @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -22,33 +21,6 @@ #include "choose-mode.h" #include "mode.h" -void generic_close(int fd, void *unused) -{ - close(fd); -} - -int generic_read(int fd, char *c_out, void *unused) -{ - int n; - - n = read(fd, c_out, sizeof(*c_out)); - if(n < 0){ - if(errno == EAGAIN) return(0); - return(-errno); - } - else if(n == 0) return(-EIO); - return(1); -} - -int generic_write(int fd, const char *buf, int n, void *unused) -{ - int count; - - count = write(fd, buf, n); - if(count < 0) return(-errno); - return(count); -} - int generic_console_write(int fd, const char *buf, int n, void *unused) { struct termios save, new; @@ -65,26 +37,6 @@ int generic_console_write(int fd, const return(err); } -int generic_window_size(int fd, void *unused, unsigned short *rows_out, - unsigned short *cols_out) -{ - struct winsize size; - int ret = 0; - - if(ioctl(fd, TIOCGWINSZ, &size) == 0){ - ret = ((*rows_out != size.ws_row) || - (*cols_out != size.ws_col)); - *rows_out = size.ws_row; - *cols_out = size.ws_col; - } - return(ret); -} - -void generic_free(void *data) -{ - kfree(data); -} - static void winch_handler(int sig) { } @@ -100,14 +52,16 @@ static int winch_thread(void *arg) struct winch_data *data = arg; sigset_t sigs; int pty_fd, pipe_fd; + int count, err; char c = 1; - close(data->close_me); + os_close_file(data->close_me); pty_fd = data->pty_fd; pipe_fd = data->pipe_fd; - if(write(pipe_fd, &c, sizeof(c)) != sizeof(c)) + count = os_write_file(pipe_fd, &c, sizeof(c)); + if(count != sizeof(c)) printk("winch_thread : failed to write synchronization " - "byte, errno = %d\n", errno); + "byte, err = %d\n", -count); signal(SIGWINCH, winch_handler); sigfillset(&sigs); @@ -123,26 +77,24 @@ static int winch_thread(void *arg) exit(1); } - if(ioctl(pty_fd, TIOCSCTTY, 0) < 0){ - printk("winch_thread : TIOCSCTTY failed, errno = %d\n", errno); - exit(1); - } - if(tcsetpgrp(pty_fd, os_getpid()) < 0){ - printk("winch_thread : tcsetpgrp failed, errno = %d\n", errno); + err = os_new_tty_pgrp(pty_fd, os_getpid()); + if(err < 0){ + printk("winch_thread : new_tty_pgrp failed, err = %d\n", -err); exit(1); } - if(read(pipe_fd, &c, sizeof(c)) != sizeof(c)) + count = os_read_file(pipe_fd, &c, sizeof(c)); + if(count != sizeof(c)) printk("winch_thread : failed to read synchronization byte, " - "errno = %d\n", errno); + "err = %d\n", -count); while(1){ pause(); - if(write(pipe_fd, &c, sizeof(c)) != sizeof(c)){ - printk("winch_thread : write failed, errno = %d\n", - errno); - } + count = os_write_file(pipe_fd, &c, sizeof(c)); + if(count != sizeof(c)) + printk("winch_thread : write failed, err = %d\n", + -count); } } @@ -154,8 +106,8 @@ static int winch_tramp(int fd, void *dev char c; err = os_pipe(fds, 1, 1); - if(err){ - printk("winch_tramp : os_pipe failed, errno = %d\n", -err); + if(err < 0){ + printk("winch_tramp : os_pipe failed, err = %d\n", -err); return(err); } @@ -168,12 +120,12 @@ static int winch_tramp(int fd, void *dev return(pid); } - close(fds[1]); + os_close_file(fds[1]); *fd_out = fds[0]; - n = read(fds[0], &c, sizeof(c)); + n = os_read_file(fds[0], &c, sizeof(c)); if(n != sizeof(c)){ printk("winch_tramp : failed to read synchronization byte\n"); - printk("read returned %d, errno = %d\n", n, errno); + printk("read failed, err = %d\n", -n); printk("fd %d will not support SIGWINCH\n", fd); *fd_out = -1; } @@ -183,20 +135,24 @@ static int winch_tramp(int fd, void *dev void register_winch(int fd, void *device_data) { int pid, thread, thread_fd; + int count; char c = 1; - if(!isatty(fd)) return; + if(!isatty(fd)) + return; pid = tcgetpgrp(fd); - if(!CHOOSE_MODE(is_tracer_winch(pid, fd, device_data), 0) && - (pid == -1)){ + if(!CHOOSE_MODE_PROC(is_tracer_winch, is_skas_winch, pid, fd, + device_data) && (pid == -1)){ thread = winch_tramp(fd, device_data, &thread_fd); if(fd != -1){ register_winch_irq(thread_fd, fd, thread, device_data); - if(write(thread_fd, &c, sizeof(c)) != sizeof(c)) + count = os_write_file(thread_fd, &c, sizeof(c)); + if(count != sizeof(c)) printk("register_winch : failed to write " - "synchronization byte\n"); + "synchronization byte, err = %d\n", + -count); } } } --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/um/drivers/cow.h 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,41 @@ +#ifndef __COW_H__ +#define __COW_H__ + +#include + +#if __BYTE_ORDER == __BIG_ENDIAN +# define ntohll(x) (x) +# define htonll(x) (x) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +# define ntohll(x) bswap_64(x) +# define htonll(x) bswap_64(x) +#else +#error "__BYTE_ORDER not defined" +#endif + +extern int init_cow_file(int fd, char *cow_file, char *backing_file, + int sectorsize, int alignment, int *bitmap_offset_out, + unsigned long *bitmap_len_out, int *data_offset_out); + +extern int file_reader(__u64 offset, char *buf, int len, void *arg); +extern int read_cow_header(int (*reader)(__u64, char *, int, void *), + void *arg, __u32 *version_out, + char **backing_file_out, time_t *mtime_out, + __u64 *size_out, int *sectorsize_out, + __u32 *align_out, int *bitmap_offset_out); + +extern int write_cow_header(char *cow_file, int fd, char *backing_file, + int sectorsize, int alignment, long long *size); + +extern void cow_sizes(int version, __u64 size, int sectorsize, int align, + int bitmap_offset, unsigned long *bitmap_len_out, + int *data_offset_out); + +#endif + +/* + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/um/drivers/cow_sys.h 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,48 @@ +#ifndef __COW_SYS_H__ +#define __COW_SYS_H__ + +#include "kern_util.h" +#include "user_util.h" +#include "os.h" +#include "user.h" + +static inline void *cow_malloc(int size) +{ + return(um_kmalloc(size)); +} + +static inline void cow_free(void *ptr) +{ + kfree(ptr); +} + +#define cow_printf printk + +static inline char *cow_strdup(char *str) +{ + return(uml_strdup(str)); +} + +static inline int cow_seek_file(int fd, __u64 offset) +{ + return(os_seek_file(fd, offset)); +} + +static inline int cow_file_size(char *file, __u64 *size_out) +{ + return(os_file_size(file, size_out)); +} + +static inline int cow_write_file(int fd, char *buf, int size) +{ + return(os_write_file(fd, buf, size)); +} + +#endif + +/* + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/um/drivers/cow_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,375 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "os.h" + +#include "cow.h" +#include "cow_sys.h" + +#define PATH_LEN_V1 256 + +struct cow_header_v1 { + int magic; + int version; + char backing_file[PATH_LEN_V1]; + time_t mtime; + __u64 size; + int sectorsize; +}; + +#define PATH_LEN_V2 MAXPATHLEN + +struct cow_header_v2 { + unsigned long magic; + unsigned long version; + char backing_file[PATH_LEN_V2]; + time_t mtime; + __u64 size; + int sectorsize; +}; + +/* Define PATH_LEN_V3 as the usual value of MAXPATHLEN, just hard-code it in + * case other systems have different values for MAXPATHLEN + */ +#define PATH_LEN_V3 4096 + +/* Changes from V2 - + * PATH_LEN_V3 as described above + * Explicitly specify field bit lengths for systems with different + * lengths for the usual C types. Not sure whether char or + * time_t should be changed, this can be changed later without + * breaking compatibility + * Add alignment field so that different alignments can be used for the + * bitmap and data + * Add cow_format field to allow for the possibility of different ways + * of specifying the COW blocks. For now, the only value is 0, + * for the traditional COW bitmap. + * Move the backing_file field to the end of the header. This allows + * for the possibility of expanding it into the padding required + * by the bitmap alignment. + * The bitmap and data portions of the file will be aligned as specified + * by the alignment field. This is to allow COW files to be + * put on devices with restrictions on access alignments, such as + * /dev/raw, with a 512 byte alignment restriction. This also + * allows the data to be more aligned more strictly than on + * sector boundaries. This is needed for ubd-mmap, which needs + * the data to be page aligned. + * Fixed (finally!) the rounding bug + */ + +struct cow_header_v3 { + __u32 magic; + __u32 version; + time_t mtime; + __u64 size; + __u32 sectorsize; + __u32 alignment; + __u32 cow_format; + char backing_file[PATH_LEN_V3]; +}; + +/* COW format definitions - for now, we have only the usual COW bitmap */ +#define COW_BITMAP 0 + +union cow_header { + struct cow_header_v1 v1; + struct cow_header_v2 v2; + struct cow_header_v3 v3; +}; + +#define COW_MAGIC 0x4f4f4f4d /* MOOO */ +#define COW_VERSION 3 + +#define DIV_ROUND(x, len) (((x) + (len) - 1) / (len)) +#define ROUND_UP(x, align) DIV_ROUND(x, align) * (align) + +void cow_sizes(int version, __u64 size, int sectorsize, int align, + int bitmap_offset, unsigned long *bitmap_len_out, + int *data_offset_out) +{ + if(version < 3){ + *bitmap_len_out = (size + sectorsize - 1) / (8 * sectorsize); + + *data_offset_out = bitmap_offset + *bitmap_len_out; + *data_offset_out = (*data_offset_out + sectorsize - 1) / + sectorsize; + *data_offset_out *= sectorsize; + } + else { + *bitmap_len_out = DIV_ROUND(size, sectorsize); + *bitmap_len_out = DIV_ROUND(*bitmap_len_out, 8); + + *data_offset_out = bitmap_offset + *bitmap_len_out; + *data_offset_out = ROUND_UP(*data_offset_out, align); + } +} + +static int absolutize(char *to, int size, char *from) +{ + char save_cwd[256], *slash; + int remaining; + + if(getcwd(save_cwd, sizeof(save_cwd)) == NULL) { + cow_printf("absolutize : unable to get cwd - errno = %d\n", + errno); + return(-1); + } + slash = strrchr(from, '/'); + if(slash != NULL){ + *slash = '\0'; + if(chdir(from)){ + *slash = '/'; + cow_printf("absolutize : Can't cd to '%s' - " + "errno = %d\n", from, errno); + return(-1); + } + *slash = '/'; + if(getcwd(to, size) == NULL){ + cow_printf("absolutize : unable to get cwd of '%s' - " + "errno = %d\n", from, errno); + return(-1); + } + remaining = size - strlen(to); + if(strlen(slash) + 1 > remaining){ + cow_printf("absolutize : unable to fit '%s' into %d " + "chars\n", from, size); + return(-1); + } + strcat(to, slash); + } + else { + if(strlen(save_cwd) + 1 + strlen(from) + 1 > size){ + cow_printf("absolutize : unable to fit '%s' into %d " + "chars\n", from, size); + return(-1); + } + strcpy(to, save_cwd); + strcat(to, "/"); + strcat(to, from); + } + chdir(save_cwd); + return(0); +} + +int write_cow_header(char *cow_file, int fd, char *backing_file, + int sectorsize, int alignment, long long *size) +{ + struct cow_header_v3 *header; + unsigned long modtime; + int err; + + err = cow_seek_file(fd, 0); + if(err < 0){ + cow_printf("write_cow_header - lseek failed, err = %d\n", -err); + goto out; + } + + err = -ENOMEM; + header = cow_malloc(sizeof(*header)); + if(header == NULL){ + cow_printf("Failed to allocate COW V3 header\n"); + goto out; + } + header->magic = htonl(COW_MAGIC); + header->version = htonl(COW_VERSION); + + err = -EINVAL; + if(strlen(backing_file) > sizeof(header->backing_file) - 1){ + cow_printf("Backing file name \"%s\" is too long - names are " + "limited to %d characters\n", backing_file, + sizeof(header->backing_file) - 1); + goto out_free; + } + + if(absolutize(header->backing_file, sizeof(header->backing_file), + backing_file)) + goto out_free; + + err = os_file_modtime(header->backing_file, &modtime); + if(err < 0){ + cow_printf("Backing file '%s' mtime request failed, " + "err = %d\n", header->backing_file, -err); + goto out_free; + } + + err = cow_file_size(header->backing_file, size); + if(err < 0){ + cow_printf("Couldn't get size of backing file '%s', " + "err = %d\n", header->backing_file, -err); + goto out_free; + } + + header->mtime = htonl(modtime); + header->size = htonll(*size); + header->sectorsize = htonl(sectorsize); + header->alignment = htonl(alignment); + header->cow_format = COW_BITMAP; + + err = os_write_file(fd, header, sizeof(*header)); + if(err != sizeof(*header)){ + cow_printf("Write of header to new COW file '%s' failed, " + "err = %d\n", cow_file, -err); + goto out_free; + } + err = 0; + out_free: + cow_free(header); + out: + return(err); +} + +int file_reader(__u64 offset, char *buf, int len, void *arg) +{ + int fd = *((int *) arg); + + return(pread(fd, buf, len, offset)); +} + +/* XXX Need to sanity-check the values read from the header */ + +int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg, + __u32 *version_out, char **backing_file_out, + time_t *mtime_out, __u64 *size_out, + int *sectorsize_out, __u32 *align_out, + int *bitmap_offset_out) +{ + union cow_header *header; + char *file; + int err, n; + unsigned long version, magic; + + header = cow_malloc(sizeof(*header)); + if(header == NULL){ + cow_printf("read_cow_header - Failed to allocate header\n"); + return(-ENOMEM); + } + err = -EINVAL; + n = (*reader)(0, (char *) header, sizeof(*header), arg); + if(n < offsetof(typeof(header->v1), backing_file)){ + cow_printf("read_cow_header - short header\n"); + goto out; + } + + magic = header->v1.magic; + if(magic == COW_MAGIC) { + version = header->v1.version; + } + else if(magic == ntohl(COW_MAGIC)){ + version = ntohl(header->v1.version); + } + /* No error printed because the non-COW case comes through here */ + else goto out; + + *version_out = version; + + if(version == 1){ + if(n < sizeof(header->v1)){ + cow_printf("read_cow_header - failed to read V1 " + "header\n"); + goto out; + } + *mtime_out = header->v1.mtime; + *size_out = header->v1.size; + *sectorsize_out = header->v1.sectorsize; + *bitmap_offset_out = sizeof(header->v1); + *align_out = *sectorsize_out; + file = header->v1.backing_file; + } + else if(version == 2){ + if(n < sizeof(header->v2)){ + cow_printf("read_cow_header - failed to read V2 " + "header\n"); + goto out; + } + *mtime_out = ntohl(header->v2.mtime); + *size_out = ntohll(header->v2.size); + *sectorsize_out = ntohl(header->v2.sectorsize); + *bitmap_offset_out = sizeof(header->v2); + *align_out = *sectorsize_out; + file = header->v2.backing_file; + } + else if(version == 3){ + if(n < sizeof(header->v3)){ + cow_printf("read_cow_header - failed to read V2 " + "header\n"); + goto out; + } + *mtime_out = ntohl(header->v3.mtime); + *size_out = ntohll(header->v3.size); + *sectorsize_out = ntohl(header->v3.sectorsize); + *align_out = ntohl(header->v3.alignment); + *bitmap_offset_out = ROUND_UP(sizeof(header->v3), *align_out); + file = header->v3.backing_file; + } + else { + cow_printf("read_cow_header - invalid COW version\n"); + goto out; + } + err = -ENOMEM; + *backing_file_out = cow_strdup(file); + if(*backing_file_out == NULL){ + cow_printf("read_cow_header - failed to allocate backing " + "file\n"); + goto out; + } + err = 0; + out: + cow_free(header); + return(err); +} + +int init_cow_file(int fd, char *cow_file, char *backing_file, int sectorsize, + int alignment, int *bitmap_offset_out, + unsigned long *bitmap_len_out, int *data_offset_out) +{ + __u64 size, offset; + char zero = 0; + int err; + + err = write_cow_header(cow_file, fd, backing_file, sectorsize, + alignment, &size); + if(err) + goto out; + + *bitmap_offset_out = ROUND_UP(sizeof(struct cow_header_v3), alignment); + cow_sizes(COW_VERSION, size, sectorsize, alignment, *bitmap_offset_out, + bitmap_len_out, data_offset_out); + + offset = *data_offset_out + size - sizeof(zero); + err = cow_seek_file(fd, offset); + if(err < 0){ + cow_printf("cow bitmap lseek failed : err = %d\n", -err); + goto out; + } + + /* does not really matter how much we write it is just to set EOF + * this also sets the entire COW bitmap + * to zero without having to allocate it + */ + err = cow_write_file(fd, &zero, sizeof(zero)); + if(err != sizeof(zero)){ + cow_printf("Write of bitmap to new COW file '%s' failed, " + "err = %d\n", cow_file, -err); + err = -EINVAL; + goto out; + } + + return(0); + + out: + return(err); +} + +/* + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ --- linux-2.6.9-rc1/arch/um/drivers/daemon_user.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/um/drivers/daemon_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -53,7 +53,8 @@ static int connect_to_switch(struct daem struct request_v3 req; int fd, n, err; - if((pri->control = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){ + pri->control = socket(AF_UNIX, SOCK_STREAM, 0); + if(pri->control < 0){ printk("daemon_open : control socket failed, errno = %d\n", errno); return(-errno); @@ -67,7 +68,8 @@ static int connect_to_switch(struct daem goto out; } - if((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0){ + fd = socket(AF_UNIX, SOCK_DGRAM, 0); + if(fd < 0){ printk("daemon_open : data socket failed, errno = %d\n", errno); err = -errno; @@ -91,18 +93,18 @@ static int connect_to_switch(struct daem req.version = SWITCH_VERSION; req.type = REQ_NEW_CONTROL; req.sock = *local_addr; - n = write(pri->control, &req, sizeof(req)); + n = os_write_file(pri->control, &req, sizeof(req)); if(n != sizeof(req)){ - printk("daemon_open : control setup request returned %d, " - "errno = %d\n", n, errno); + printk("daemon_open : control setup request failed, err = %d\n", + -n); err = -ENOTCONN; goto out; } - n = read(pri->control, sun, sizeof(*sun)); + n = os_read_file(pri->control, sun, sizeof(*sun)); if(n != sizeof(*sun)){ - printk("daemon_open : read of data socket returned %d, " - "errno = %d\n", n, errno); + printk("daemon_open : read of data socket failed, err = %d\n", + -n); err = -ENOTCONN; goto out_close; } @@ -111,9 +113,9 @@ static int connect_to_switch(struct daem return(fd); out_close: - close(fd); + os_close_file(fd); out: - close(pri->control); + os_close_file(pri->control); return(err); } @@ -153,8 +155,8 @@ static void daemon_remove(void *data) { struct daemon_data *pri = data; - close(pri->fd); - close(pri->control); + os_close_file(pri->fd); + os_close_file(pri->control); if(pri->data_addr != NULL) kfree(pri->data_addr); if(pri->ctl_addr != NULL) kfree(pri->ctl_addr); if(pri->local_addr != NULL) kfree(pri->local_addr); --- linux-2.6.9-rc1/arch/um/drivers/fd.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/um/drivers/fd.c 2004-09-02 20:51:51.000000000 -0700 @@ -35,7 +35,8 @@ void *fd_init(char *str, int device, str printk("fd_init : couldn't parse file descriptor '%s'\n", str); return(NULL); } - if((data = um_kmalloc(sizeof(*data))) == NULL) return(NULL); + data = um_kmalloc(sizeof(*data)); + if(data == NULL) return(NULL); *data = ((struct fd_chan) { .fd = n, .raw = opts->raw }); return(data); --- linux-2.6.9-rc1/arch/um/drivers/harddog_user.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/um/drivers/harddog_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -27,10 +27,10 @@ static void pre_exec(void *d) dup2(data->stdin, 0); dup2(data->stdout, 1); dup2(data->stdout, 2); - close(data->stdin); - close(data->stdout); - close(data->close_me[0]); - close(data->close_me[1]); + os_close_file(data->stdin); + os_close_file(data->stdout); + os_close_file(data->close_me[0]); + os_close_file(data->close_me[1]); } int start_watchdog(int *in_fd_ret, int *out_fd_ret, char *sock) @@ -44,15 +44,15 @@ int start_watchdog(int *in_fd_ret, int * char **args = NULL; err = os_pipe(in_fds, 1, 0); - if(err){ - printk("harddog_open - os_pipe failed, errno = %d\n", -err); - return(err); + if(err < 0){ + printk("harddog_open - os_pipe failed, err = %d\n", -err); + goto out; } err = os_pipe(out_fds, 1, 0); - if(err){ - printk("harddog_open - os_pipe failed, errno = %d\n", -err); - return(err); + if(err < 0){ + printk("harddog_open - os_pipe failed, err = %d\n", -err); + goto out_close_in; } data.stdin = out_fds[0]; @@ -72,42 +72,47 @@ int start_watchdog(int *in_fd_ret, int * pid = run_helper(pre_exec, &data, args, NULL); - close(out_fds[0]); - close(in_fds[1]); + os_close_file(out_fds[0]); + os_close_file(in_fds[1]); if(pid < 0){ err = -pid; - printk("harddog_open - run_helper failed, errno = %d\n", err); - goto out; + printk("harddog_open - run_helper failed, errno = %d\n", -err); + goto out_close_out; } - n = read(in_fds[0], &c, sizeof(c)); + n = os_read_file(in_fds[0], &c, sizeof(c)); if(n == 0){ printk("harddog_open - EOF on watchdog pipe\n"); helper_wait(pid); err = -EIO; - goto out; + goto out_close_out; } else if(n < 0){ printk("harddog_open - read of watchdog pipe failed, " - "errno = %d\n", errno); + "err = %d\n", -n); helper_wait(pid); - err = -errno; - goto out; + err = n; + goto out_close_out; } *in_fd_ret = in_fds[0]; *out_fd_ret = out_fds[1]; return(0); + + out_close_in: + os_close_file(in_fds[0]); + os_close_file(in_fds[1]); + out_close_out: + os_close_file(out_fds[0]); + os_close_file(out_fds[1]); out: - close(out_fds[1]); - close(in_fds[0]); return(err); } void stop_watchdog(int in_fd, int out_fd) { - close(in_fd); - close(out_fd); + os_close_file(in_fd); + os_close_file(out_fd); } int ping_watchdog(int fd) @@ -115,11 +120,12 @@ int ping_watchdog(int fd) int n; char c = '\n'; - n = write(fd, &c, sizeof(c)); - if(n < sizeof(c)){ - printk("ping_watchdog - write failed, errno = %d\n", - errno); - return(-errno); + n = os_write_file(fd, &c, sizeof(c)); + if(n != sizeof(c)){ + printk("ping_watchdog - write failed, err = %d\n", -n); + if(n < 0) + return(n); + return(-EIO); } return 1; --- linux-2.6.9-rc1/arch/um/drivers/hostaudio_kern.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/um/drivers/hostaudio_kern.c 2004-09-02 20:51:51.000000000 -0700 @@ -5,44 +5,64 @@ #include "linux/config.h" #include "linux/module.h" -#include "linux/version.h" #include "linux/init.h" #include "linux/slab.h" #include "linux/fs.h" #include "linux/sound.h" #include "linux/soundcard.h" +#include "asm/uaccess.h" #include "kern_util.h" #include "init.h" -#include "hostaudio.h" +#include "os.h" + +struct hostaudio_state { + int fd; +}; + +struct hostmixer_state { + int fd; +}; + +#define HOSTAUDIO_DEV_DSP "/dev/sound/dsp" +#define HOSTAUDIO_DEV_MIXER "/dev/sound/mixer" /* Only changed from linux_main at boot time */ char *dsp = HOSTAUDIO_DEV_DSP; char *mixer = HOSTAUDIO_DEV_MIXER; +#define DSP_HELP \ +" This is used to specify the host dsp device to the hostaudio driver.\n" \ +" The default is \"" HOSTAUDIO_DEV_DSP "\".\n\n" + +#define MIXER_HELP \ +" This is used to specify the host mixer device to the hostaudio driver.\n" \ +" The default is \"" HOSTAUDIO_DEV_MIXER "\".\n\n" + #ifndef MODULE static int set_dsp(char *name, int *add) { - dsp = uml_strdup(name); + dsp = name; return(0); } -__uml_setup("dsp=", set_dsp, -"dsp=\n" -" This is used to specify the host dsp device to the hostaudio driver.\n" -" The default is \"" HOSTAUDIO_DEV_DSP "\".\n\n" -); +__uml_setup("dsp=", set_dsp, "dsp=\n" DSP_HELP); static int set_mixer(char *name, int *add) { - mixer = uml_strdup(name); + mixer = name; return(0); } -__uml_setup("mixer=", set_mixer, -"mixer=\n" -" This is used to specify the host mixer device to the hostaudio driver.\n" -" The default is \"" HOSTAUDIO_DEV_MIXER "\".\n\n" -); +__uml_setup("mixer=", set_mixer, "mixer=\n" MIXER_HELP); + +#else /*MODULE*/ + +MODULE_PARM(dsp, "s"); +MODULE_PARM_DESC(dsp, DSP_HELP); + +MODULE_PARM(mixer, "s"); +MODULE_PARM_DESC(mixer, MIXER_HELP); + #endif /* /dev/dsp file operations */ @@ -51,23 +71,55 @@ static ssize_t hostaudio_read(struct fil loff_t *ppos) { struct hostaudio_state *state = file->private_data; + void *kbuf; + int ret; #ifdef DEBUG printk("hostaudio: read called, count = %d\n", count); #endif - return(hostaudio_read_user(state, buffer, count, ppos)); + kbuf = kmalloc(count, GFP_KERNEL); + if(kbuf == NULL) + return(-ENOMEM); + + ret = os_read_file(state->fd, kbuf, count); + if(ret < 0) + goto out; + + if(copy_to_user(buffer, kbuf, ret)) + ret = -EFAULT; + + out: + kfree(kbuf); + return(ret); } static ssize_t hostaudio_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct hostaudio_state *state = file->private_data; + void *kbuf; + int ret; #ifdef DEBUG printk("hostaudio: write called, count = %d\n", count); #endif - return(hostaudio_write_user(state, buffer, count, ppos)); + + kbuf = kmalloc(count, GFP_KERNEL); + if(kbuf == NULL) + return(-ENOMEM); + + ret = -EFAULT; + if(copy_from_user(kbuf, buffer, count)) + goto out; + + ret = os_write_file(state->fd, kbuf, count); + if(ret < 0) + goto out; + + out: + kfree(kbuf); + return(ret); } static unsigned int hostaudio_poll(struct file *file, @@ -86,12 +138,43 @@ static int hostaudio_ioctl(struct inode unsigned int cmd, unsigned long arg) { struct hostaudio_state *state = file->private_data; + unsigned long data = 0; + int ret; #ifdef DEBUG printk("hostaudio: ioctl called, cmd = %u\n", cmd); #endif + switch(cmd){ + case SNDCTL_DSP_SPEED: + case SNDCTL_DSP_STEREO: + case SNDCTL_DSP_GETBLKSIZE: + case SNDCTL_DSP_CHANNELS: + case SNDCTL_DSP_SUBDIVIDE: + case SNDCTL_DSP_SETFRAGMENT: + if(get_user(data, (int *) arg)) + return(-EFAULT); + break; + default: + break; + } + + ret = os_ioctl_generic(state->fd, cmd, (unsigned long) &data); + + switch(cmd){ + case SNDCTL_DSP_SPEED: + case SNDCTL_DSP_STEREO: + case SNDCTL_DSP_GETBLKSIZE: + case SNDCTL_DSP_CHANNELS: + case SNDCTL_DSP_SUBDIVIDE: + case SNDCTL_DSP_SETFRAGMENT: + if(put_user(data, (int *) arg)) + return(-EFAULT); + break; + default: + break; + } - return(hostaudio_ioctl_user(state, cmd, arg)); + return(ret); } static int hostaudio_open(struct inode *inode, struct file *file) @@ -110,12 +193,17 @@ static int hostaudio_open(struct inode * if(file->f_mode & FMODE_READ) r = 1; if(file->f_mode & FMODE_WRITE) w = 1; - ret = hostaudio_open_user(state, r, w, dsp); + ret = os_open_file(dsp, of_set_rw(OPENFLAGS(), r, w), 0); + if(ret < 0){ + printk("hostaudio_open failed to open '%s', err = %d\n", + dsp, -ret); kfree(state); return(ret); } + state->fd = ret; + file->private_data = state; return(0); } @@ -123,16 +211,19 @@ static int hostaudio_open(struct inode * static int hostaudio_release(struct inode *inode, struct file *file) { struct hostaudio_state *state = file->private_data; - int ret; #ifdef DEBUG printk("hostaudio: release called\n"); #endif - ret = hostaudio_release_user(state); + if(state->fd >= 0){ + os_close_file(state->fd); + state->fd = -1; + } + kfree(state); - return(ret); + return(0); } /* /dev/mixer file operations */ @@ -146,7 +237,7 @@ static int hostmixer_ioctl_mixdev(struct printk("hostmixer: ioctl called\n"); #endif - return(hostmixer_ioctl_mixdev_user(state, cmd, arg)); + return(os_ioctl_generic(state->fd, cmd, arg)); } static int hostmixer_open_mixdev(struct inode *inode, struct file *file) @@ -165,13 +256,17 @@ static int hostmixer_open_mixdev(struct if(file->f_mode & FMODE_READ) r = 1; if(file->f_mode & FMODE_WRITE) w = 1; - ret = hostmixer_open_mixdev_user(state, r, w, mixer); + ret = os_open_file(mixer, of_set_rw(OPENFLAGS(), r, w), 0); if(ret < 0){ + printk("hostaudio_open_mixdev failed to open '%s', err = %d\n", + dsp, -ret); kfree(state); return(ret); } + state->fd = ret; + file->private_data = state; return(0); } @@ -179,16 +274,18 @@ static int hostmixer_open_mixdev(struct static int hostmixer_release(struct inode *inode, struct file *file) { struct hostmixer_state *state = file->private_data; - int ret; #ifdef DEBUG printk("hostmixer: release called\n"); #endif - ret = hostmixer_release_mixdev_user(state); + if(state->fd >= 0){ + os_close_file(state->fd); + state->fd = -1; + } kfree(state); - return(ret); + return(0); } @@ -225,7 +322,8 @@ MODULE_LICENSE("GPL"); static int __init hostaudio_init_module(void) { - printk(KERN_INFO "UML Audio Relay\n"); + printk(KERN_INFO "UML Audio Relay (host dsp = %s, host mixer = %s)\n", + dsp, mixer); module_data.dev_audio = register_sound_dsp(&hostaudio_fops, -1); if(module_data.dev_audio < 0){ --- linux-2.6.9-rc1/arch/um/drivers/hostaudio_user.c 2004-08-24 00:03:18.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2002 Steve Schmidtke - * Licensed under the GPL - */ - -#include -#include -#include -#include -#include -#include -#include "hostaudio.h" -#include "user_util.h" -#include "kern_util.h" -#include "user.h" -#include "os.h" - -/* /dev/dsp file operations */ - -ssize_t hostaudio_read_user(struct hostaudio_state *state, char *buffer, - size_t count, loff_t *ppos) -{ - ssize_t ret; - -#ifdef DEBUG - printk("hostaudio: read_user called, count = %d\n", count); -#endif - - ret = read(state->fd, buffer, count); - - if(ret < 0) return(-errno); - return(ret); -} - -ssize_t hostaudio_write_user(struct hostaudio_state *state, const char *buffer, - size_t count, loff_t *ppos) -{ - ssize_t ret; - -#ifdef DEBUG - printk("hostaudio: write_user called, count = %d\n", count); -#endif - - ret = write(state->fd, buffer, count); - - if(ret < 0) return(-errno); - return(ret); -} - -int hostaudio_ioctl_user(struct hostaudio_state *state, unsigned int cmd, - unsigned long arg) -{ - int ret; -#ifdef DEBUG - printk("hostaudio: ioctl_user called, cmd = %u\n", cmd); -#endif - - ret = ioctl(state->fd, cmd, arg); - - if(ret < 0) return(-errno); - return(ret); -} - -int hostaudio_open_user(struct hostaudio_state *state, int r, int w, char *dsp) -{ -#ifdef DEBUG - printk("hostaudio: open_user called\n"); -#endif - - state->fd = os_open_file(dsp, of_set_rw(OPENFLAGS(), r, w), 0); - - if(state->fd >= 0) return(0); - - printk("hostaudio_open_user failed to open '%s', errno = %d\n", - dsp, errno); - - return(-errno); -} - -int hostaudio_release_user(struct hostaudio_state *state) -{ -#ifdef DEBUG - printk("hostaudio: release called\n"); -#endif - if(state->fd >= 0){ - close(state->fd); - state->fd=-1; - } - - return(0); -} - -/* /dev/mixer file operations */ - -int hostmixer_ioctl_mixdev_user(struct hostmixer_state *state, - unsigned int cmd, unsigned long arg) -{ - int ret; -#ifdef DEBUG - printk("hostmixer: ioctl_user called cmd = %u\n",cmd); -#endif - - ret = ioctl(state->fd, cmd, arg); - if(ret < 0) - return(-errno); - return(ret); -} - -int hostmixer_open_mixdev_user(struct hostmixer_state *state, int r, int w, - char *mixer) -{ -#ifdef DEBUG - printk("hostmixer: open_user called\n"); -#endif - - state->fd = os_open_file(mixer, of_set_rw(OPENFLAGS(), r, w), 0); - - if(state->fd >= 0) return(0); - - printk("hostaudio_open_mixdev_user failed to open '%s', errno = %d\n", - mixer, errno); - - return(-errno); -} - -int hostmixer_release_mixdev_user(struct hostmixer_state *state) -{ -#ifdef DEBUG - printk("hostmixer: release_user called\n"); -#endif - - if(state->fd >= 0){ - close(state->fd); - state->fd = -1; - } - - return 0; -} - -/* - * 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: - */ --- linux-2.6.9-rc1/arch/um/drivers/line.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/um/drivers/line.c 2004-09-02 20:51:51.000000000 -0700 @@ -6,8 +6,8 @@ #include "linux/sched.h" #include "linux/slab.h" #include "linux/list.h" +#include "linux/interrupt.h" #include "linux/devfs_fs_kernel.h" -#include "asm/irq.h" #include "asm/uaccess.h" #include "chan_kern.h" #include "irq_user.h" @@ -16,38 +16,55 @@ #include "user_util.h" #include "kern_util.h" #include "os.h" +#include "irq_kern.h" #define LINE_BUFSIZE 4096 -void line_interrupt(int irq, void *data, struct pt_regs *unused) +static irqreturn_t line_interrupt(int irq, void *data, struct pt_regs *unused) { struct line *dev = data; if(dev->count > 0) chan_interrupt(&dev->chan_list, &dev->task, dev->tty, irq, dev); + return IRQ_HANDLED; } -void line_timer_cb(void *arg) +static void line_timer_cb(void *arg) { struct line *dev = arg; line_interrupt(dev->driver->read_irq, dev, NULL); } -static void buffer_data(struct line *line, const char *buf, int len) +static int write_room(struct line *dev) { - int end; + int n; + + if(dev->buffer == NULL) return(LINE_BUFSIZE - 1); + + n = dev->head - dev->tail; + if(n <= 0) n = LINE_BUFSIZE + n; + return(n - 1); +} + +static int buffer_data(struct line *line, const char *buf, int len) +{ + int end, room; if(line->buffer == NULL){ line->buffer = kmalloc(LINE_BUFSIZE, GFP_ATOMIC); if(line->buffer == NULL){ printk("buffer_data - atomic allocation failed\n"); - return; + return(0); } line->head = line->buffer; line->tail = line->buffer; } + + room = write_room(line); + len = (len > room) ? room : len; + end = line->buffer + LINE_BUFSIZE - line->tail; if(len < end){ memcpy(line->tail, buf, len); @@ -60,6 +77,8 @@ static void buffer_data(struct line *lin memcpy(line->buffer, buf, len); line->tail = line->buffer + len; } + + return(len); } static int flush_buffer(struct line *line) @@ -95,7 +114,7 @@ int line_write(struct line *lines, struc struct line *line; char *new; unsigned long flags; - int n, err, i; + int n, err, i, ret = 0; if(tty->stopped) return 0; @@ -104,9 +123,13 @@ int line_write(struct line *lines, struc if(new == NULL) return(0); n = copy_from_user(new, buf, len); - if(n == len) - return(-EFAULT); buf = new; + if(n == len){ + len = -EFAULT; + goto out_free; + } + + len -= n; } i = tty->index; @@ -115,41 +138,50 @@ int line_write(struct line *lines, struc down(&line->sem); if(line->head != line->tail){ local_irq_save(flags); - buffer_data(line, buf, len); + ret += buffer_data(line, buf, len); err = flush_buffer(line); local_irq_restore(flags); if(err <= 0) - goto out; + goto out_up; } else { n = write_chan(&line->chan_list, buf, len, line->driver->write_irq); if(n < 0){ - len = n; - goto out; + ret = n; + goto out_up; } - if(n < len) - buffer_data(line, buf + n, len - n); + + len -= n; + ret += n; + if(len > 0) + ret += buffer_data(line, buf + n, len); } - out: + out_up: up(&line->sem); - return(len); + out_free: + if(from_user) + kfree(buf); + return(ret); } -void line_write_interrupt(int irq, void *data, struct pt_regs *unused) +static irqreturn_t line_write_interrupt(int irq, void *data, + struct pt_regs *unused) { struct line *dev = data; struct tty_struct *tty = dev->tty; int err; err = flush_buffer(dev); - if(err == 0) return; + if(err == 0) + return(IRQ_NONE); else if(err < 0){ dev->head = dev->buffer; dev->tail = dev->buffer; } - if(tty == NULL) return; + if(tty == NULL) + return(IRQ_NONE); if(test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && (tty->ldisc.write_wakeup != NULL)) @@ -161,21 +193,9 @@ void line_write_interrupt(int irq, void * writes. */ - if (waitqueue_active(&tty->write_wait)) + if(waitqueue_active(&tty->write_wait)) wake_up_interruptible(&tty->write_wait); - -} - -int line_write_room(struct tty_struct *tty) -{ - struct line *dev = tty->driver_data; - int n; - - if(dev->buffer == NULL) return(LINE_BUFSIZE - 1); - - n = dev->head - dev->tail; - if(n <= 0) n = LINE_BUFSIZE + n; - return(n - 1); + return(IRQ_HANDLED); } int line_setup_irq(int fd, int input, int output, void *data) @@ -305,7 +325,7 @@ int line_setup(struct line *lines, int n if(*end != '='){ printk(KERN_ERR "line_setup failed to parse \"%s\"\n", init); - return(1); + return(0); } init = end; } @@ -313,12 +333,12 @@ int line_setup(struct line *lines, int n if((n >= 0) && (n >= num)){ printk("line_setup - %d out of range ((0 ... %d) allowed)\n", n, num); - return(1); + return(0); } else if(n >= 0){ if(lines[n].count > 0){ printk("line_setup - device %d is open\n", n); - return(1); + return(0); } if(lines[n].init_pri <= INIT_ONE){ lines[n].init_pri = INIT_ONE; @@ -332,7 +352,7 @@ int line_setup(struct line *lines, int n else if(!all_allowed){ printk("line_setup - can't configure all devices from " "mconsole\n"); - return(1); + return(0); } else { for(i = 0; i < num; i++){ @@ -346,7 +366,7 @@ int line_setup(struct line *lines, int n } } } - return(0); + return(1); } int line_config(struct line *lines, int num, char *str) @@ -357,7 +377,7 @@ int line_config(struct line *lines, int printk("line_config - uml_strdup failed\n"); return(-ENOMEM); } - return(line_setup(lines, num, new, 0)); + return(!line_setup(lines, num, new, 0)); } int line_get_config(char *name, struct line *lines, int num, char *str, @@ -369,7 +389,7 @@ int line_get_config(char *name, struct l dev = simple_strtoul(name, &end, 0); if((*end != '\0') || (end == name)){ - *error_out = "line_setup failed to parse device number"; + *error_out = "line_get_config failed to parse device number"; return(0); } @@ -379,15 +399,15 @@ int line_get_config(char *name, struct l } line = &lines[dev]; + down(&line->sem); - if(!line->valid) CONFIG_CHUNK(str, size, n, "none", 1); else if(line->count == 0) CONFIG_CHUNK(str, size, n, line->init_str, 1); else n = chan_config_string(&line->chan_list, str, size, error_out); - up(&line->sem); + return(n); } @@ -396,7 +416,14 @@ int line_remove(struct line *lines, int char config[sizeof("conxxxx=none\0")]; sprintf(config, "%s=none", str); - return(line_setup(lines, num, config, 0)); + return(!line_setup(lines, num, config, 0)); +} + +int line_write_room(struct tty_struct *tty) +{ + struct line *dev = tty->driver_data; + + return(write_room(dev)); } struct tty_driver *line_register_devfs(struct lines *set, @@ -412,7 +439,8 @@ struct tty_driver *line_register_devfs(s return NULL; driver->driver_name = line_driver->name; - driver->name = line_driver->devfs_name; + driver->name = line_driver->device_name; + driver->devfs_name = line_driver->devfs_name; driver->major = line_driver->major; driver->minor_start = line_driver->minor_start; driver->type = line_driver->type; @@ -432,7 +460,7 @@ struct tty_driver *line_register_devfs(s for(i = 0; i < nlines; i++){ if(!lines[i].valid) - tty_unregister_devfs(driver, i); + tty_unregister_device(driver, i); } mconsole_register_dev(&line_driver->mc); @@ -465,24 +493,25 @@ struct winch { struct line *line; }; -void winch_interrupt(int irq, void *data, struct pt_regs *unused) +irqreturn_t winch_interrupt(int irq, void *data, struct pt_regs *unused) { struct winch *winch = data; struct tty_struct *tty; int err; char c; - err = generic_read(winch->fd, &c, NULL); - if(err < 0){ - if(err != -EAGAIN){ - printk("winch_interrupt : read failed, errno = %d\n", - -err); - printk("fd %d is losing SIGWINCH support\n", - winch->tty_fd); - free_irq(irq, data); - return; + if(winch->fd != -1){ + err = generic_read(winch->fd, &c, NULL); + if(err < 0){ + if(err != -EAGAIN){ + printk("winch_interrupt : read failed, " + "errno = %d\n", -err); + printk("fd %d is losing SIGWINCH support\n", + winch->tty_fd); + return(IRQ_HANDLED); + } + goto out; } - goto out; } tty = winch->line->tty; if(tty != NULL){ @@ -492,7 +521,9 @@ void winch_interrupt(int irq, void *data kill_pg(tty->pgrp, SIGWINCH, 1); } out: - reactivate_fd(winch->fd, WINCH_IRQ); + if(winch->fd != -1) + reactivate_fd(winch->fd, WINCH_IRQ); + return(IRQ_HANDLED); } DECLARE_MUTEX(winch_handler_sem); @@ -529,7 +560,10 @@ static void winch_cleanup(void) list_for_each(ele, &winch_handlers){ winch = list_entry(ele, struct winch, list); - close(winch->fd); + if(winch->fd != -1){ + deactivate_fd(winch->fd, WINCH_IRQ); + os_close_file(winch->fd); + } if(winch->pid != -1) os_kill_process(winch->pid, 1); } --- linux-2.6.9-rc1/arch/um/drivers/Makefile 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/um/drivers/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -1,5 +1,5 @@ # -# Copyright (C) 2000, 2002 Jeff Dike (jdike@karaya.com) +# Copyright (C) 2000, 2002, 2003 Jeff Dike (jdike@karaya.com) # Licensed under the GPL # @@ -15,7 +15,7 @@ mcast-objs := mcast_kern.o mcast_user.o #pcap-objs := pcap_kern.o pcap_user.o $(PCAP) net-objs := net_kern.o net_user.o mconsole-objs := mconsole_kern.o mconsole_user.o -hostaudio-objs := hostaudio_kern.o hostaudio_user.o +hostaudio-objs := hostaudio_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 @@ -39,6 +39,7 @@ obj-$(CONFIG_PTY_CHAN) += pty.o obj-$(CONFIG_TTY_CHAN) += tty.o obj-$(CONFIG_XTERM_CHAN) += xterm.o xterm_kern.o obj-$(CONFIG_UML_WATCHDOG) += harddog.o +obj-$(CONFIG_BLK_DEV_COW_COMMON) += cow_user.o obj-y += stdio_console.o $(CHAN_OBJS) @@ -46,18 +47,7 @@ USER_SINGLE_OBJS = $(foreach f,$(patsubs USER_OBJS := $(filter %_user.o,$(obj-y) $(obj-m) $(USER_SINGLE_OBJS)) fd.o \ null.o pty.o tty.o xterm.o -USER_OBJS := $(foreach file,$(USER_OBJS),arch/um/drivers/$(file)) +USER_OBJS := $(foreach file,$(USER_OBJS),$(obj)/$(file)) $(USER_OBJS) : %.o: %.c $(CC) $(CFLAGS_$(notdir $@)) $(USER_CFLAGS) -c -o $@ $< - -clean: - -modules: - -fastdep: - -dep: - -archmrproper: clean - --- linux-2.6.9-rc1/arch/um/drivers/mcast_user.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/um/drivers/mcast_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -23,6 +23,7 @@ #include "kern_util.h" #include "user_util.h" #include "user.h" +#include "os.h" #define MAX_PACKET (ETH_MAX_PACKET + ETH_HEADER_OTHER) @@ -62,7 +63,8 @@ static int mcast_open(void *data) goto out; } - if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0){ printk("mcast_open : data socket failed, errno = %d\n", errno); fd = -ENOMEM; @@ -72,7 +74,7 @@ static int mcast_open(void *data) if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { printk("mcast_open: SO_REUSEADDR failed, errno = %d\n", errno); - close(fd); + os_close_file(fd); fd = -EINVAL; goto out; } @@ -82,7 +84,7 @@ static int mcast_open(void *data) sizeof(pri->ttl)) < 0) { printk("mcast_open: IP_MULTICAST_TTL failed, error = %d\n", errno); - close(fd); + os_close_file(fd); fd = -EINVAL; goto out; } @@ -91,7 +93,7 @@ static int mcast_open(void *data) if (setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) { printk("mcast_open: IP_MULTICAST_LOOP failed, error = %d\n", errno); - close(fd); + os_close_file(fd); fd = -EINVAL; goto out; } @@ -99,7 +101,7 @@ static int mcast_open(void *data) /* bind socket to mcast address */ if (bind(fd, (struct sockaddr *) sin, sizeof(*sin)) < 0) { printk("mcast_open : data bind failed, errno = %d\n", errno); - close(fd); + os_close_file(fd); fd = -EINVAL; goto out; } @@ -115,7 +117,7 @@ static int mcast_open(void *data) "interface on the host.\n"); printk("eth0 should be configured in order to use the " "multicast transport.\n"); - close(fd); + os_close_file(fd); fd = -EINVAL; } @@ -137,7 +139,7 @@ static void mcast_close(int fd, void *da errno); } - close(fd); + os_close_file(fd); } int mcast_user_write(int fd, void *buf, int len, struct mcast_data *pri) --- linux-2.6.9-rc1/arch/um/drivers/mconsole_kern.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/arch/um/drivers/mconsole_kern.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,6 +1,6 @@ /* * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) - * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2001 - 2003 Jeff Dike (jdike@addtoit.com) * Licensed under the GPL */ @@ -15,6 +15,9 @@ #include "linux/sysrq.h" #include "linux/workqueue.h" #include "linux/module.h" +#include "linux/file.h" +#include "linux/fs.h" +#include "linux/namei.h" #include "linux/proc_fs.h" #include "asm/irq.h" #include "asm/uaccess.h" @@ -27,6 +30,7 @@ #include "init.h" #include "os.h" #include "umid.h" +#include "irq_kern.h" static int do_unlink_socket(struct notifier_block *notifier, unsigned long what, void *data) @@ -67,7 +71,7 @@ void mc_work_proc(void *unused) DECLARE_WORK(mconsole_work, mc_work_proc, NULL); -void mconsole_interrupt(int irq, void *dev_id, struct pt_regs *regs) +irqreturn_t mconsole_interrupt(int irq, void *dev_id, struct pt_regs *regs) { int fd; struct mconsole_entry *new; @@ -75,9 +79,10 @@ void mconsole_interrupt(int irq, void *d fd = (int) dev_id; while (mconsole_get_request(fd, &req)){ - if(req.cmd->as_interrupt) (*req.cmd->handler)(&req); + if(req.cmd->context == MCONSOLE_INTR) + (*req.cmd->handler)(&req); else { - new = kmalloc(sizeof(req), GFP_ATOMIC); + new = kmalloc(sizeof(*new), GFP_ATOMIC); if(new == NULL) mconsole_reply(&req, "Out of memory", 1, 0); else { @@ -88,6 +93,7 @@ void mconsole_interrupt(int irq, void *d } if(!list_empty(&mc_requests)) schedule_work(&mconsole_work); reactivate_fd(fd, MCONSOLE_IRQ); + return(IRQ_HANDLED); } void mconsole_version(struct mc_request *req) @@ -100,20 +106,109 @@ void mconsole_version(struct mc_request mconsole_reply(req, version, 0, 0); } +void mconsole_log(struct mc_request *req) +{ + int len; + char *ptr = req->request.data; + + ptr += strlen("log "); + + len = req->len - (ptr - req->request.data); + printk("%.*s", len, ptr); + mconsole_reply(req, "", 0, 0); +} + +void mconsole_proc(struct mc_request *req) +{ + struct nameidata nd; + struct file_system_type *proc; + struct super_block *super; + struct file *file; + int n, err; + char *ptr = req->request.data, *buf; + + ptr += strlen("proc"); + while(isspace(*ptr)) ptr++; + + proc = get_fs_type("proc"); + if(proc == NULL){ + mconsole_reply(req, "procfs not registered", 1, 0); + goto out; + } + + super = (*proc->get_sb)(proc, 0, NULL, NULL); + put_filesystem(proc); + if(super == NULL){ + mconsole_reply(req, "Failed to get procfs superblock", 1, 0); + goto out; + } + up_write(&super->s_umount); + + nd.dentry = super->s_root; + nd.mnt = NULL; + nd.flags = O_RDONLY + 1; + nd.last_type = LAST_ROOT; + + err = link_path_walk(ptr, &nd); + if(err){ + mconsole_reply(req, "Failed to look up file", 1, 0); + goto out_kill; + } + + file = dentry_open(nd.dentry, nd.mnt, O_RDONLY); + if(IS_ERR(file)){ + mconsole_reply(req, "Failed to open file", 1, 0); + goto out_kill; + } + + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if(buf == NULL){ + mconsole_reply(req, "Failed to allocate buffer", 1, 0); + goto out_fput; + } + + if((file->f_op != NULL) && (file->f_op->read != NULL)){ + do { + n = (*file->f_op->read)(file, buf, PAGE_SIZE - 1, + &file->f_pos); + if(n >= 0){ + buf[n] = '\0'; + mconsole_reply(req, buf, 0, (n > 0)); + } + else { + mconsole_reply(req, "Read of file failed", + 1, 0); + goto out_free; + } + } while(n > 0); + } + else mconsole_reply(req, "", 0, 0); + + out_free: + kfree(buf); + out_fput: + fput(file); + out_kill: + deactivate_super(super); + out: ; +} + #define UML_MCONSOLE_HELPTEXT \ -"Commands: - version - Get kernel version - help - Print this message - halt - Halt UML - reboot - Reboot UML - config = - Add a new device to UML; - same syntax as command line - config - Query the configuration of a device - remove - Remove a device from UML - sysrq - Performs the SysRq action controlled by the letter - cad - invoke the Ctl-Alt-Del handler - stop - pause the UML; it will do nothing until it receives a 'go' - go - continue the UML after a 'stop' +"Commands: \n\ + version - Get kernel version \n\ + help - Print this message \n\ + halt - Halt UML \n\ + reboot - Reboot UML \n\ + config = - Add a new device to UML; \n\ + same syntax as command line \n\ + config - Query the configuration of a device \n\ + remove - Remove a device from UML \n\ + sysrq - Performs the SysRq action controlled by the letter \n\ + cad - invoke the Ctl-Alt-Del handler \n\ + stop - pause the UML; it will do nothing until it receives a 'go' \n\ + go - continue the UML after a 'stop' \n\ + log - make UML enter into the kernel log\n\ + proc - returns the contents of the UML's /proc/\n\ " void mconsole_help(struct mc_request *req) @@ -302,7 +397,7 @@ int mconsole_init(void) if(umid_file_name("mconsole", file, sizeof(file))) return(-1); snprintf(mconsole_socket_name, sizeof(file), "%s", file); - sock = create_unix_socket(file, sizeof(file)); + sock = os_create_unix_socket(file, sizeof(file), 1); if (sock < 0){ printk("Failed to initialize management console\n"); return(1); @@ -344,11 +439,16 @@ static int write_proc_mconsole(struct fi if(buf == NULL) return(-ENOMEM); - if(copy_from_user(buf, buffer, count)) - return(-EFAULT); + if(copy_from_user(buf, buffer, count)){ + count = -EFAULT; + goto out; + } + buf[count] = '\0'; mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count); + out: + kfree(buf); return(count); } --- linux-2.6.9-rc1/arch/um/drivers/mconsole_user.c 2004-08-24 00:02:20.000000000 -0700 +++ 25/arch/um/drivers/mconsole_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,6 +1,6 @@ /* * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) - * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2001 - 2003 Jeff Dike (jdike@addtoit.com) * Licensed under the GPL */ @@ -18,16 +18,18 @@ #include "umid.h" static struct mconsole_command commands[] = { - { "version", mconsole_version, 1 }, - { "halt", mconsole_halt, 0 }, - { "reboot", mconsole_reboot, 0 }, - { "config", mconsole_config, 0 }, - { "remove", mconsole_remove, 0 }, - { "sysrq", mconsole_sysrq, 1 }, - { "help", mconsole_help, 1 }, - { "cad", mconsole_cad, 1 }, - { "stop", mconsole_stop, 0 }, - { "go", mconsole_go, 1 }, + { "version", mconsole_version, MCONSOLE_INTR }, + { "halt", mconsole_halt, MCONSOLE_PROC }, + { "reboot", mconsole_reboot, MCONSOLE_PROC }, + { "config", mconsole_config, MCONSOLE_PROC }, + { "remove", mconsole_remove, MCONSOLE_PROC }, + { "sysrq", mconsole_sysrq, MCONSOLE_INTR }, + { "help", mconsole_help, MCONSOLE_INTR }, + { "cad", mconsole_cad, MCONSOLE_INTR }, + { "stop", mconsole_stop, MCONSOLE_PROC }, + { "go", mconsole_go, MCONSOLE_INTR }, + { "log", mconsole_log, MCONSOLE_INTR }, + { "proc", mconsole_proc, MCONSOLE_PROC }, }; /* Initialized in mconsole_init, which is an initcall */ @@ -139,6 +141,7 @@ int mconsole_reply(struct mc_request *re memcpy(reply.data, str, len); reply.data[len] = '\0'; total -= len; + str += len; reply.len = len + 1; len = sizeof(reply) + reply.len - sizeof(reply.data); --- linux-2.6.9-rc1/arch/um/drivers/mmapper_kern.c 2004-08-24 00:01:55.000000000 -0700 +++ 25/arch/um/drivers/mmapper_kern.c 2004-09-02 20:51:51.000000000 -0700 @@ -120,7 +120,10 @@ static int __init mmapper_init(void) printk(KERN_INFO "Mapper v0.1\n"); v_buf = (char *) find_iomem("mmapper", &mmapper_size); - if(mmapper_size == 0) return(0); + if(mmapper_size == 0){ + printk(KERN_ERR "mmapper_init - find_iomem failed\n"); + return(0); + } p_buf = __pa(v_buf); --- linux-2.6.9-rc1/arch/um/drivers/net_kern.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/um/drivers/net_kern.c 2004-09-02 20:51:51.000000000 -0700 @@ -26,6 +26,7 @@ #include "mconsole_kern.h" #include "init.h" #include "irq_user.h" +#include "irq_kern.h" static spinlock_t opened_lock = SPIN_LOCK_UNLOCKED; LIST_HEAD(opened); @@ -37,7 +38,8 @@ static int uml_net_rx(struct net_device struct sk_buff *skb; /* If we can't allocate memory, try again next round. */ - if ((skb = dev_alloc_skb(dev->mtu)) == NULL) { + skb = dev_alloc_skb(dev->mtu); + if (skb == NULL) { lp->stats.rx_dropped++; return 0; } @@ -61,14 +63,14 @@ static int uml_net_rx(struct net_device return pkt_len; } -void uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs) +irqreturn_t uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct net_device *dev = dev_id; struct uml_net_private *lp = dev->priv; int err; if(!netif_running(dev)) - return; + return(IRQ_NONE); spin_lock(&lp->lock); while((err = uml_net_rx(dev)) > 0) ; @@ -83,6 +85,7 @@ void uml_net_interrupt(int irq, void *de out: spin_unlock(&lp->lock); + return(IRQ_HANDLED); } static int uml_net_open(struct net_device *dev) @@ -250,37 +253,6 @@ void uml_net_user_timer_expire(unsigned #endif } -/* - * default do nothing hard header packet routines for struct net_device init. - * real ethernet transports will overwrite with real routines. - */ -static int uml_net_hard_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, void *daddr, void *saddr, unsigned len) -{ - return(0); /* no change */ -} - -static int uml_net_rebuild_header(struct sk_buff *skb) -{ - return(0); /* ignore */ -} - -static int uml_net_header_cache(struct neighbour *neigh, struct hh_cache *hh) -{ - return(-1); /* fail */ -} - -static void uml_net_header_cache_update(struct hh_cache *hh, - struct net_device *dev, unsigned char * haddr) -{ - /* ignore */ -} - -static int uml_net_header_parse(struct sk_buff *skb, unsigned char *haddr) -{ - return(0); /* nothing */ -} - static spinlock_t devices_lock = SPIN_LOCK_UNLOCKED; static struct list_head devices = LIST_HEAD_INIT(devices); @@ -290,7 +262,7 @@ static int eth_configure(int n, void *in struct uml_net *device; struct net_device *dev; struct uml_net_private *lp; - int err, size; + int save, err, size; size = transport->private_size + sizeof(struct uml_net_private) + sizeof(((struct uml_net_private *) 0)->user); @@ -332,12 +304,6 @@ static int eth_configure(int n, void *in snprintf(dev->name, sizeof(dev->name), "eth%d", n); device->dev = dev; - dev->hard_header = uml_net_hard_header; - dev->rebuild_header = uml_net_rebuild_header; - dev->hard_header_cache = uml_net_header_cache; - dev->header_cache_update= uml_net_header_cache_update; - dev->hard_header_parse = uml_net_header_parse; - (*transport->kern->init)(dev, init); dev->mtu = transport->user->max_packet; @@ -364,21 +330,29 @@ static int eth_configure(int n, void *in } lp = dev->priv; - INIT_LIST_HEAD(&lp->list); - spin_lock_init(&lp->lock); - lp->dev = dev; - lp->fd = -1; - lp->mac = { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0 }; - lp->have_mac = device->have_mac; - lp->protocol = transport->kern->protocol; - lp->open = transport->user->open; - lp->close = transport->user->close; - lp->remove = transport->user->remove; - lp->read = transport->kern->read; - lp->write = transport->kern->write; - lp->add_address = transport->user->add_address; - lp->delete_address = transport->user->delete_address; - lp->set_mtu = transport->user->set_mtu; + /* lp.user is the first four bytes of the transport data, which + * has already been initialized. This structure assignment will + * overwrite that, so we make sure that .user gets overwritten with + * what it already has. + */ + save = lp->user[0]; + *lp = ((struct uml_net_private) + { .list = LIST_HEAD_INIT(lp->list), + .lock = SPIN_LOCK_UNLOCKED, + .dev = dev, + .fd = -1, + .mac = { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0}, + .have_mac = device->have_mac, + .protocol = transport->kern->protocol, + .open = transport->user->open, + .close = transport->user->close, + .remove = transport->user->remove, + .read = transport->kern->read, + .write = transport->kern->write, + .add_address = transport->user->add_address, + .delete_address = transport->user->delete_address, + .set_mtu = transport->user->set_mtu, + .user = { save } }); init_timer(&lp->tl); lp->tl.function = uml_net_user_timer_expire; @@ -611,7 +585,8 @@ static int net_remove(char *str) unregister_netdev(dev); list_del(&device->list); - free_netdev(device); + kfree(device); + free_netdev(dev); return(0); } --- linux-2.6.9-rc1/arch/um/drivers/net_user.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/um/drivers/net_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -26,8 +26,7 @@ int tap_open_common(void *dev, char *gat if(gate_addr == NULL) return(0); if(sscanf(gate_addr, "%d.%d.%d.%d", &tap_addr[0], &tap_addr[1], &tap_addr[2], &tap_addr[3]) != 4){ - printk("Invalid tap IP address - '%s'\n", - gate_addr); + printk("Invalid tap IP address - '%s'\n", gate_addr); return(-EINVAL); } return(0); @@ -60,18 +59,18 @@ void read_output(int fd, char *output, i } *output = '\0'; - if(read(fd, &remain, sizeof(remain)) != sizeof(remain)){ - printk("read_output - read of length failed, errno = %d\n", - errno); + n = os_read_file(fd, &remain, sizeof(remain)); + if(n != sizeof(remain)){ + printk("read_output - read of length failed, err = %d\n", -n); return; } while(remain != 0){ n = (remain < len) ? remain : len; - actual = read(fd, output, n); + actual = os_read_file(fd, output, n); if(actual != n){ printk("read_output - read of data failed, " - "errno = %d\n", errno); + "err = %d\n", -actual); return; } remain -= actual; @@ -83,13 +82,12 @@ int net_read(int fd, void *buf, int len) { int n; - while(((n = read(fd, buf, len)) < 0) && (errno == EINTR)) ; + n = os_read_file(fd, buf, len); - if(n < 0){ - if(errno == EAGAIN) return(0); - return(-errno); - } - else if(n == 0) return(-ENOTCONN); + if(n == -EAGAIN) + return(0); + else if(n == 0) + return(-ENOTCONN); return(n); } @@ -112,13 +110,13 @@ int net_write(int fd, void *buf, int len { int n; - while(((n = write(fd, buf, len)) < 0) && (errno == EINTR)) ; - if(n < 0){ - if(errno == EAGAIN) return(0); - return(-errno); - } - else if(n == 0) return(-ENOTCONN); - return(n); + n = os_write_file(fd, buf, len); + + if(n == -EAGAIN) + return(0); + else if(n == 0) + return(-ENOTCONN); + return(n); } int net_send(int fd, void *buf, int len) @@ -157,7 +155,7 @@ static void change_pre_exec(void *arg) { struct change_pre_exec_data *data = arg; - close(data->close_me); + os_close_file(data->close_me); dup2(data->stdout, 1); } @@ -167,17 +165,18 @@ static int change_tramp(char **argv, cha struct change_pre_exec_data pe_data; err = os_pipe(fds, 1, 0); - if(err){ - printk("change_tramp - pipe failed, errno = %d\n", -err); + if(err < 0){ + printk("change_tramp - pipe failed, err = %d\n", -err); return(err); } pe_data.close_me = fds[0]; pe_data.stdout = fds[1]; pid = run_helper(change_pre_exec, &pe_data, argv, NULL); - close(fds[1]); + os_close_file(fds[1]); read_output(fds[0], output, output_len); - waitpid(pid, NULL, 0); + + CATCH_EINTR(err = waitpid(pid, NULL, 0)); return(pid); } --- linux-2.6.9-rc1/arch/um/drivers/null.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/um/drivers/null.c 2004-09-02 20:51:51.000000000 -0700 @@ -5,7 +5,6 @@ #include #include -#include #include "chan_user.h" #include "os.h" --- linux-2.6.9-rc1/arch/um/drivers/port_kern.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/arch/um/drivers/port_kern.c 2004-09-02 20:51:51.000000000 -0700 @@ -6,6 +6,7 @@ #include "linux/list.h" #include "linux/sched.h" #include "linux/slab.h" +#include "linux/interrupt.h" #include "linux/irq.h" #include "linux/spinlock.h" #include "linux/errno.h" @@ -14,6 +15,7 @@ #include "kern_util.h" #include "kern.h" #include "irq_user.h" +#include "irq_kern.h" #include "port.h" #include "init.h" #include "os.h" @@ -38,21 +40,21 @@ struct port_dev { struct connection { struct list_head list; int fd; - int helper_pid; + int helper_pid; int socket[2]; int telnetd_pid; struct port_list *port; }; -static void pipe_interrupt(int irq, void *data, struct pt_regs *regs) +static irqreturn_t pipe_interrupt(int irq, void *data, struct pt_regs *regs) { struct connection *conn = data; int fd; - fd = os_rcv_fd(conn->socket[0], &conn->helper_pid); + fd = os_rcv_fd(conn->socket[0], &conn->helper_pid); if(fd < 0){ if(fd == -EAGAIN) - return; + return(IRQ_NONE); printk(KERN_ERR "pipe_interrupt : os_rcv_fd returned %d\n", -fd); @@ -65,6 +67,7 @@ static void pipe_interrupt(int irq, void list_add(&conn->list, &conn->port->connections); up(&conn->port->sem); + return(IRQ_HANDLED); } static int port_accept(struct port_list *port) @@ -102,8 +105,7 @@ static int port_accept(struct port_list } list_add(&conn->list, &port->pending); - ret = 1; - goto out; + return(1); out_free: kfree(conn); @@ -138,12 +140,13 @@ void port_work_proc(void *unused) DECLARE_WORK(port_work, port_work_proc, NULL); -static void port_interrupt(int irq, void *data, struct pt_regs *regs) +static irqreturn_t port_interrupt(int irq, void *data, struct pt_regs *regs) { struct port_list *port = data; port->has_connection = 1; schedule_work(&port_work); + return(IRQ_HANDLED); } void *port_data(int port_num) --- linux-2.6.9-rc1/arch/um/drivers/port_user.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/um/drivers/port_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -47,10 +47,12 @@ void *port_init(char *str, int device, s return(NULL); } - if((kern_data = port_data(port)) == NULL) + kern_data = port_data(port); + if(kern_data == NULL) return(NULL); - if((data = um_kmalloc(sizeof(*data))) == NULL) + data = um_kmalloc(sizeof(*data)); + if(data == NULL) goto err; *data = ((struct port_chan) { .raw = opts->raw, @@ -90,7 +92,7 @@ void port_close(int fd, void *d) struct port_chan *data = d; port_remove_dev(data->kernel_data); - close(fd); + os_close_file(fd); } int port_console_write(int fd, const char *buf, int n, void *d) @@ -130,11 +132,15 @@ int port_listen_fd(int port) goto out; } - if((listen(fd, 1) < 0) || (os_set_fd_block(fd, 0))){ + if(listen(fd, 1) < 0){ err = -errno; goto out; } + err = os_set_fd_block(fd, 0); + if(err < 0) + goto out; + return(fd); out: os_close_file(fd); @@ -153,10 +159,10 @@ void port_pre_exec(void *arg) dup2(data->sock_fd, 0); dup2(data->sock_fd, 1); dup2(data->sock_fd, 2); - close(data->sock_fd); + os_close_file(data->sock_fd); dup2(data->pipe_fd, 3); os_shutdown_socket(3, 1, 0); - close(data->pipe_fd); + os_close_file(data->pipe_fd); } int port_connection(int fd, int *socket, int *pid_out) @@ -166,11 +172,12 @@ int port_connection(int fd, int *socket, "/usr/lib/uml/port-helper", NULL }; struct port_pre_exec_data data; - if((new = os_accept_connection(fd)) < 0) - return(-errno); + new = os_accept_connection(fd); + if(new < 0) + return(new); err = os_pipe(socket, 0, 0); - if(err) + if(err < 0) goto out_close; data = ((struct port_pre_exec_data) @@ -186,11 +193,11 @@ int port_connection(int fd, int *socket, out_shutdown: os_shutdown_socket(socket[0], 1, 1); - close(socket[0]); + os_close_file(socket[0]); os_shutdown_socket(socket[1], 1, 1); - close(socket[1]); + os_close_file(socket[1]); out_close: - close(new); + os_close_file(new); return(err); } --- linux-2.6.9-rc1/arch/um/drivers/pty.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/um/drivers/pty.c 2004-09-02 20:51:51.000000000 -0700 @@ -7,12 +7,12 @@ #include #include #include -#include #include #include "chan_user.h" #include "user.h" #include "user_util.h" #include "kern_util.h" +#include "os.h" struct pty_chan { void (*announce)(char *dev_name, int dev); @@ -26,7 +26,8 @@ void *pty_chan_init(char *str, int devic { struct pty_chan *data; - if((data = um_kmalloc(sizeof(*data))) == NULL) return(NULL); + data = um_kmalloc(sizeof(*data)); + if(data == NULL) return(NULL); *data = ((struct pty_chan) { .announce = opts->announce, .dev = device, .raw = opts->raw }); @@ -39,7 +40,8 @@ int pts_open(int input, int output, int char *dev; int fd; - if((fd = get_pty()) < 0){ + fd = get_pty(); + if(fd < 0){ printk("open_pts : Failed to open pts\n"); return(-errno); } @@ -57,29 +59,27 @@ int pts_open(int input, int output, int int getmaster(char *line) { - struct stat stb; char *pty, *bank, *cp; - int master; + int master, err; pty = &line[strlen("/dev/ptyp")]; for (bank = "pqrs"; *bank; bank++) { line[strlen("/dev/pty")] = *bank; *pty = '0'; - if (stat(line, &stb) < 0) + if (os_stat_file(line, NULL) < 0) break; for (cp = "0123456789abcdef"; *cp; cp++) { *pty = *cp; - master = open(line, O_RDWR); + master = os_open_file(line, of_rdwr(OPENFLAGS()), 0); if (master >= 0) { char *tp = &line[strlen("/dev/")]; - int ok; /* verify slave side is usable */ *tp = 't'; - ok = access(line, R_OK|W_OK) == 0; + err = os_access(line, OS_ACC_RW_OK); *tp = 'p'; - if (ok) return(master); - (void) close(master); + if(err == 0) return(master); + (void) os_close_file(master); } } } --- linux-2.6.9-rc1/arch/um/drivers/slip_user.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/um/drivers/slip_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -4,11 +4,9 @@ #include #include #include -#include -#include +#include #include #include -#include #include #include "user_util.h" #include "kern_util.h" @@ -65,9 +63,9 @@ static void slip_pre_exec(void *arg) { struct slip_pre_exec_data *data = arg; - if(data->stdin != -1) dup2(data->stdin, 0); + if(data->stdin >= 0) dup2(data->stdin, 0); dup2(data->stdout, 1); - if(data->close_me != -1) close(data->close_me); + if(data->close_me >= 0) os_close_file(data->close_me); } static int slip_tramp(char **argv, int fd) @@ -77,8 +75,8 @@ static int slip_tramp(char **argv, int f int status, pid, fds[2], err, output_len; err = os_pipe(fds, 1, 0); - if(err){ - printk("slip_tramp : pipe failed, errno = %d\n", -err); + if(err < 0){ + printk("slip_tramp : pipe failed, err = %d\n", -err); return(err); } @@ -96,16 +94,18 @@ static int slip_tramp(char **argv, int f printk("slip_tramp : failed to allocate output " "buffer\n"); - close(fds[1]); + os_close_file(fds[1]); read_output(fds[0], output, output_len); if(output != NULL){ printk("%s", output); kfree(output); } - if(waitpid(pid, &status, 0) < 0) err = errno; + CATCH_EINTR(err = waitpid(pid, &status, 0)); + if(err < 0) + err = errno; else if(!WIFEXITED(status) || (WEXITSTATUS(status) != 0)){ printk("'%s' didn't exit with status 0\n", argv[0]); - err = EINVAL; + err = -EINVAL; } } return(err); @@ -118,15 +118,17 @@ static int slip_open(void *data) char gate_buf[sizeof("nnn.nnn.nnn.nnn\0")]; char *argv[] = { "uml_net", version_buf, "slip", "up", gate_buf, NULL }; - int sfd, mfd, disc, sencap, err; + int sfd, mfd, err; - if((mfd = get_pty()) < 0){ - printk("umn : Failed to open pty\n"); - return(-1); + mfd = get_pty(); + if(mfd < 0){ + printk("umn : Failed to open pty, err = %d\n", -mfd); + return(mfd); } - if((sfd = os_open_file(ptsname(mfd), of_rdwr(OPENFLAGS()), 0)) < 0){ - printk("Couldn't open tty for slip line\n"); - return(-1); + sfd = os_open_file(ptsname(mfd), of_rdwr(OPENFLAGS()), 0); + if(sfd < 0){ + printk("Couldn't open tty for slip line, err = %d\n", -sfd); + return(sfd); } if(set_up_tty(sfd)) return(-1); pri->slave = sfd; @@ -138,28 +140,23 @@ static int slip_open(void *data) err = slip_tramp(argv, sfd); - if(err != 0){ - printk("slip_tramp failed - errno = %d\n", err); - return(-err); + if(err < 0){ + printk("slip_tramp failed - err = %d\n", -err); + return(err); } - if(ioctl(pri->slave, SIOCGIFNAME, pri->name) < 0){ - printk("SIOCGIFNAME failed, errno = %d\n", errno); - return(-errno); + err = os_get_ifname(pri->slave, pri->name); + if(err < 0){ + printk("get_ifname failed, err = %d\n", -err); + return(err); } iter_addresses(pri->dev, open_addr, pri->name); } else { - disc = N_SLIP; - if(ioctl(sfd, TIOCSETD, &disc) < 0){ - printk("Failed to set slip line discipline - " - "errno = %d\n", errno); - return(-errno); - } - sencap = 0; - if(ioctl(sfd, SIOCSIFENCAP, &sencap) < 0){ - printk("Failed to set slip encapsulation - " - "errno = %d\n", errno); - return(-errno); + err = os_set_slip(sfd); + if(err < 0){ + printk("Failed to set slip discipline encapsulation - " + "err = %d\n", -err); + return(err); } } return(mfd); @@ -181,9 +178,9 @@ static void slip_close(int fd, void *dat err = slip_tramp(argv, -1); if(err != 0) - printk("slip_tramp failed - errno = %d\n", err); - close(fd); - close(pri->slave); + printk("slip_tramp failed - errno = %d\n", -err); + os_close_file(fd); + os_close_file(pri->slave); pri->slave = -1; } @@ -243,7 +240,7 @@ static void slip_add_addr(unsigned char { struct slip_data *pri = data; - if(pri->slave == -1) return; + if(pri->slave < 0) return; open_addr(addr, netmask, pri->name); } @@ -252,7 +249,7 @@ static void slip_del_addr(unsigned char { struct slip_data *pri = data; - if(pri->slave == -1) return; + if(pri->slave < 0) return; close_addr(addr, netmask, pri->name); } --- linux-2.6.9-rc1/arch/um/drivers/slirp_user.c 2004-08-24 00:03:14.000000000 -0700 +++ 25/arch/um/drivers/slirp_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -4,8 +4,7 @@ #include #include #include -#include -#include +#include #include #include #include "user_util.h" @@ -48,15 +47,15 @@ static int slirp_tramp(char **argv, int return(pid); } - + +/* XXX This is just a trivial wrapper around os_pipe */ static int slirp_datachan(int *mfd, int *sfd) { int fds[2], err; err = os_pipe(fds, 1, 1); - if(err){ - printk("slirp_datachan: Failed to open pipe, errno = %d\n", - -err); + if(err < 0){ + printk("slirp_datachan: Failed to open pipe, err = %d\n", -err); return(err); } @@ -77,7 +76,7 @@ static int slirp_open(void *data) pid = slirp_tramp(pri->argw.argv, sfd); if(pid < 0){ - printk("slirp_tramp failed - errno = %d\n", pid); + printk("slirp_tramp failed - errno = %d\n", -pid); os_close_file(sfd); os_close_file(mfd); return(pid); @@ -97,8 +96,8 @@ static void slirp_close(int fd, void *da struct slirp_data *pri = data; int status,err; - close(fd); - close(pri->slave); + os_close_file(fd); + os_close_file(pri->slave); pri->slave = -1; @@ -114,13 +113,13 @@ static void slirp_close(int fd, void *da } #endif - err = waitpid(pri->pid, &status, WNOHANG); - if(err<0) { + CATCH_EINTR(err = waitpid(pri->pid, &status, WNOHANG)); + if(err < 0) { printk("slirp_close: waitpid returned %d\n", errno); return; } - if(err==0) { + if(err == 0) { printk("slirp_close: process %d has not exited\n"); return; } --- linux-2.6.9-rc1/arch/um/drivers/ssl.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/um/drivers/ssl.c 2004-09-02 20:51:51.000000000 -0700 @@ -10,6 +10,7 @@ #include "linux/major.h" #include "linux/mm.h" #include "linux/init.h" +#include "linux/console.h" #include "asm/termbits.h" #include "asm/irq.h" #include "line.h" @@ -53,8 +54,9 @@ static int ssl_remove(char *str); static struct line_driver driver = { .name = "UML serial line", - .devfs_name = "tts/%d", - .major = TTYAUX_MAJOR, + .device_name = "ttS", + .devfs_name = "tts/", + .major = TTY_MAJOR, .minor_start = 64, .type = TTY_DRIVER_TYPE_SERIAL, .subtype = 0, @@ -149,6 +151,9 @@ static int ssl_ioctl(struct tty_struct * case TCSETSW: case TCGETA: case TIOCMGET: + case TCSBRK: + case TCSBRKP: + case TIOCMSET: ret = -ENOIOCTLCMD; break; default: @@ -212,6 +217,37 @@ static struct tty_operations ssl_ops = { */ static int ssl_init_done = 0; +static void ssl_console_write(struct console *c, const char *string, + unsigned len) +{ + struct line *line = &serial_lines[c->index]; + if(ssl_init_done) + down(&line->sem); + console_write_chan(&line->chan_list, string, len); + if(ssl_init_done) + up(&line->sem); +} + +static struct tty_driver *ssl_console_device(struct console *c, int *index) +{ + *index = c->index; + return ssl_driver; +} + +static int ssl_console_setup(struct console *co, char *options) +{ + return(0); +} + +static struct console ssl_cons = { + name: "ttyS", + write: ssl_console_write, + device: ssl_console_device, + setup: ssl_console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; + int ssl_init(void) { char *new_title; @@ -227,17 +263,18 @@ int ssl_init(void) new_title = add_xterm_umid(opts.xterm_title); if(new_title != NULL) opts.xterm_title = new_title; + register_console(&ssl_cons); ssl_init_done = 1; return(0); } -__initcall(ssl_init); +late_initcall(ssl_init); static int ssl_chan_setup(char *str) { - line_setup(serial_lines, sizeof(serial_lines)/sizeof(serial_lines[0]), - str, 1); - return(1); + return(line_setup(serial_lines, + sizeof(serial_lines)/sizeof(serial_lines[0]), + str, 1)); } __setup("ssl", ssl_chan_setup); --- linux-2.6.9-rc1/arch/um/drivers/stdio_console.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/um/drivers/stdio_console.c 2004-09-03 00:56:41.007926496 -0700 @@ -17,8 +17,8 @@ #include "linux/init.h" #include "linux/interrupt.h" #include "linux/slab.h" +#include "linux/hardirq.h" #include "asm/current.h" -#include "asm/hardirq.h" #include "asm/irq.h" #include "stdio_console.h" #include "line.h" @@ -83,7 +83,8 @@ static int con_remove(char *str); static struct line_driver driver = { .name = "UML console", - .devfs_name = "vc/%d", + .device_name = "tty", + .devfs_name = "vc/", .major = TTY_MAJOR, .minor_start = 0, .type = TTY_DRIVER_TYPE_CONSOLE, @@ -159,6 +160,15 @@ static int chars_in_buffer(struct tty_st static int con_init_done = 0; +static struct tty_operations console_ops = { + .open = con_open, + .close = con_close, + .write = con_write, + .chars_in_buffer = chars_in_buffer, + .set_termios = set_termios, + .write_room = line_write_room, +}; + int stdio_init(void) { char *new_title; @@ -166,7 +176,8 @@ int stdio_init(void) printk(KERN_INFO "Initializing stdio console driver\n"); console_driver = line_register_devfs(&console_lines, &driver, - &console_ops, vts, sizeof(vts)/sizeof(vts[0])); + &console_ops, vts, + sizeof(vts)/sizeof(vts[0])); lines_init(vts, sizeof(vts)/sizeof(vts[0])); @@ -178,26 +189,21 @@ int stdio_init(void) return(0); } -__initcall(stdio_init); +late_initcall(stdio_init); static void console_write(struct console *console, const char *string, unsigned len) { - if(con_init_done) down(&vts[console->index].sem); - console_write_chan(&vts[console->index].chan_list, string, len); - if(con_init_done) up(&vts[console->index].sem); -} + struct line *line = &vts[console->index]; -static struct tty_operations console_ops = { - .open = con_open, - .close = con_close, - .write = con_write, - .chars_in_buffer = chars_in_buffer, - .set_termios = set_termios, - .write_room = line_write_room, -}; + if(con_init_done) + down(&line->sem); + console_write_chan(&line->chan_list, string, len); + if(con_init_done) + up(&line->sem); +} -static struct tty_driver *console_device(struct console *c, int *index) +static struct tty_driver *um_console_device(struct console *c, int *index) { *index = c->index; return console_driver; @@ -208,22 +214,28 @@ static int console_setup(struct console return(0); } -static struct console stdiocons = INIT_CONSOLE("tty", console_write, - console_device, console_setup, - CON_PRINTBUFFER); +static struct console stdiocons = { + name: "tty", + write: console_write, + device: um_console_device, + setup: console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; -static void __init stdio_console_init(void) +static int __init stdio_console_init(void) { INIT_LIST_HEAD(&vts[0].chan_list); list_add(&init_console_chan.list, &vts[0].chan_list); register_console(&stdiocons); + return(0); } + console_initcall(stdio_console_init); static int console_chan_setup(char *str) { - line_setup(vts, sizeof(vts)/sizeof(vts[0]), str, 1); - return(1); + return(line_setup(vts, sizeof(vts)/sizeof(vts[0]), str, 1)); } __setup("con", console_chan_setup); --- linux-2.6.9-rc1/arch/um/drivers/tty.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/arch/um/drivers/tty.c 2004-09-02 20:51:51.000000000 -0700 @@ -5,7 +5,6 @@ #include #include -#include #include #include #include "chan_user.h" @@ -30,7 +29,8 @@ void *tty_chan_init(char *str, int devic } str++; - if((data = um_kmalloc(sizeof(*data))) == NULL) + data = um_kmalloc(sizeof(*data)); + if(data == NULL) return(NULL); *data = ((struct tty_chan) { .dev = str, .raw = opts->raw }); --- linux-2.6.9-rc1/arch/um/drivers/ubd_kern.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/um/drivers/ubd_kern.c 2004-09-02 20:51:51.000000000 -0700 @@ -8,6 +8,13 @@ * old style ubd by setting UBD_SHIFT to 0 * 2002-09-27...2002-10-18 massive tinkering for 2.5 * partitions have changed in 2.5 + * 2003-01-29 more tinkering for 2.5.59-1 + * This should now address the sysfs problems and has + * the symlink for devfs to allow for booting with + * the common /dev/ubd/discX/... names rather than + * only /dev/ubdN/discN this version also has lots of + * clean ups preparing for ubd-many. + * James McMechan */ #define MAJOR_NR UBD_MAJOR @@ -40,9 +47,12 @@ #include "mconsole_kern.h" #include "init.h" #include "irq_user.h" +#include "irq_kern.h" #include "ubd_user.h" #include "2_5compat.h" #include "os.h" +#include "mem.h" +#include "mem_kern.h" static spinlock_t ubd_io_lock = SPIN_LOCK_UNLOCKED; static spinlock_t ubd_lock = SPIN_LOCK_UNLOCKED; @@ -56,6 +66,10 @@ static int ubd_ioctl(struct inode * inod #define MAX_DEV (8) +/* Changed in early boot */ +static int ubd_do_mmap = 0; +#define UBD_MMAP_BLOCK_SIZE PAGE_SIZE + static struct block_device_operations ubd_blops = { .owner = THIS_MODULE, .open = ubd_open, @@ -67,7 +81,7 @@ static struct block_device_operations ub static request_queue_t *ubd_queue; /* Protected by ubd_lock */ -static int fake_major = 0; +static int fake_major = MAJOR_NR; static struct gendisk *ubd_gendisk[MAX_DEV]; static struct gendisk *fake_gendisk[MAX_DEV]; @@ -96,13 +110,19 @@ struct cow { struct ubd { char *file; - int is_dir; int count; int fd; __u64 size; struct openflags boot_openflags; struct openflags openflags; + int no_cow; struct cow cow; + + int map_writes; + int map_reads; + int nomap_writes; + int nomap_reads; + int write_maps; }; #define DEFAULT_COW { \ @@ -115,21 +135,28 @@ struct ubd { #define DEFAULT_UBD { \ .file = NULL, \ - .is_dir = 0, \ .count = 0, \ .fd = -1, \ .size = -1, \ .boot_openflags = OPEN_FLAGS, \ .openflags = OPEN_FLAGS, \ + .no_cow = 0, \ .cow = DEFAULT_COW, \ + .map_writes = 0, \ + .map_reads = 0, \ + .nomap_writes = 0, \ + .nomap_reads = 0, \ + .write_maps = 0, \ } struct ubd ubd_dev[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = DEFAULT_UBD }; static int ubd0_init(void) { - if(ubd_dev[0].file == NULL) - ubd_dev[0].file = "root_fs"; + struct ubd *dev = &ubd_dev[0]; + + if(dev->file == NULL) + dev->file = "root_fs"; return(0); } @@ -196,19 +223,46 @@ __uml_help(fake_ide_setup, " Create ide0 entries that map onto ubd devices.\n\n" ); +static int parse_unit(char **ptr) +{ + char *str = *ptr, *end; + int n = -1; + + if(isdigit(*str)) { + n = simple_strtoul(str, &end, 0); + if(end == str) + return(-1); + *ptr = end; + } + else if (('a' <= *str) && (*str <= 'h')) { + n = *str - 'a'; + str++; + *ptr = str; + } + return(n); +} + static int ubd_setup_common(char *str, int *index_out) { + struct ubd *dev; struct openflags flags = global_openflags; char *backing_file; int n, err; if(index_out) *index_out = -1; - n = *str++; + n = *str; if(n == '='){ - static int fake_major_allowed = 1; char *end; int major; + str++; + if(!strcmp(str, "mmap")){ + CHOOSE_MODE(printk("mmap not supported by the ubd " + "driver in tt mode\n"), + ubd_do_mmap = 1); + return(0); + } + if(!strcmp(str, "sync")){ global_openflags.s = 1; return(0); @@ -220,20 +274,14 @@ static int ubd_setup_common(char *str, i return(1); } - if(!fake_major_allowed){ - printk(KERN_ERR "Can't assign a fake major twice\n"); - return(1); - } - err = 1; spin_lock(&ubd_lock); - if(!fake_major_allowed){ + if(fake_major != MAJOR_NR){ printk(KERN_ERR "Can't assign a fake major twice\n"); goto out1; } fake_major = major; - fake_major_allowed = 0; printk(KERN_INFO "Setting extra ubd major number to %d\n", major); @@ -243,25 +291,23 @@ static int ubd_setup_common(char *str, i return(err); } - if(n < '0'){ - printk(KERN_ERR "ubd_setup : index out of range\n"); } - - if((n >= '0') && (n <= '9')) n -= '0'; - else if((n >= 'a') && (n <= 'z')) n -= 'a'; - else { - printk(KERN_ERR "ubd_setup : device syntax invalid\n"); + n = parse_unit(&str); + if(n < 0){ + printk(KERN_ERR "ubd_setup : couldn't parse unit number " + "'%s'\n", str); return(1); } if(n >= MAX_DEV){ - printk(KERN_ERR "ubd_setup : index out of range " - "(%d devices)\n", MAX_DEV); + printk(KERN_ERR "ubd_setup : index %d out of range " + "(%d devices)\n", n, MAX_DEV); return(1); } err = 1; spin_lock(&ubd_lock); - if(ubd_dev[n].file != NULL){ + dev = &ubd_dev[n]; + if(dev->file != NULL){ printk(KERN_ERR "ubd_setup : device already configured\n"); goto out2; } @@ -276,6 +322,11 @@ static int ubd_setup_common(char *str, i flags.s = 1; str++; } + if (*str == 'd'){ + dev->no_cow = 1; + str++; + } + if(*str++ != '='){ printk(KERN_ERR "ubd_setup : Expected '='\n"); goto out2; @@ -284,14 +335,17 @@ static int ubd_setup_common(char *str, i err = 0; backing_file = strchr(str, ','); if(backing_file){ - *backing_file = '\0'; - backing_file++; + if(dev->no_cow) + printk(KERN_ERR "Can't specify both 'd' and a " + "cow file\n"); + else { + *backing_file = '\0'; + backing_file++; + } } - ubd_dev[n].file = str; - if(ubd_is_dir(ubd_dev[n].file)) - ubd_dev[n].is_dir = 1; - ubd_dev[n].cow.file = backing_file; - ubd_dev[n].boot_openflags = flags; + dev->file = str; + dev->cow.file = backing_file; + dev->boot_openflags = flags; out2: spin_unlock(&ubd_lock); return(err); @@ -321,8 +375,7 @@ __uml_help(ubd_setup, static int fakehd_set = 0; static int fakehd(char *str) { - printk(KERN_INFO - "fakehd : Changing ubd name to \"hd\".\n"); + printk(KERN_INFO "fakehd : Changing ubd name to \"hd\".\n"); fakehd_set = 1; return 1; } @@ -368,32 +421,42 @@ static void ubd_handler(void) { struct io_thread_req req; struct request *rq = elv_next_request(ubd_queue); - int n; + int n, err; do_ubd = NULL; intr_count++; n = read_ubd_fs(thread_fd, &req, sizeof(req)); if(n != sizeof(req)){ printk(KERN_ERR "Pid %d - spurious interrupt in ubd_handler, " - "errno = %d\n", os_getpid(), -n); + "err = %d\n", os_getpid(), -n); spin_lock(&ubd_io_lock); end_request(rq, 0); spin_unlock(&ubd_io_lock); return; } - if((req.offset != ((__u64) (rq->sector)) << 9) || - (req.length != (rq->current_nr_sectors) << 9)) + if((req.op != UBD_MMAP) && + ((req.offset != ((__u64) (rq->sector)) << 9) || + (req.length != (rq->current_nr_sectors) << 9))) panic("I/O op mismatch"); + if(req.map_fd != -1){ + err = physmem_subst_mapping(req.buffer, req.map_fd, + req.map_offset, 1); + if(err) + printk("ubd_handler - physmem_subst_mapping failed, " + "err = %d\n", -err); + } + ubd_finish(rq, req.error); reactivate_fd(thread_fd, UBD_IRQ); do_ubd_request(ubd_queue); } -static void ubd_intr(int irq, void *dev, struct pt_regs *unused) +static irqreturn_t ubd_intr(int irq, void *dev, struct pt_regs *unused) { ubd_handler(); + return(IRQ_HANDLED); } /* Only changed by ubd_init, which is an initcall. */ @@ -417,10 +480,14 @@ static int ubd_file_size(struct ubd *dev static void ubd_close(struct ubd *dev) { + if(ubd_do_mmap) + physmem_forget_descriptor(dev->fd); os_close_file(dev->fd); if(dev->cow.file == NULL) return; + if(ubd_do_mmap) + physmem_forget_descriptor(dev->cow.fd); os_close_file(dev->cow.fd); vfree(dev->cow.bitmap); dev->cow.bitmap = NULL; @@ -429,18 +496,20 @@ static void ubd_close(struct ubd *dev) static int ubd_open_dev(struct ubd *dev) { struct openflags flags; - int err, n, create_cow, *create_ptr; + char **back_ptr; + int err, create_cow, *create_ptr; + dev->openflags = dev->boot_openflags; create_cow = 0; create_ptr = (dev->cow.file != NULL) ? &create_cow : NULL; - dev->fd = open_ubd_file(dev->file, &dev->openflags, &dev->cow.file, + back_ptr = dev->no_cow ? NULL : &dev->cow.file; + dev->fd = open_ubd_file(dev->file, &dev->openflags, back_ptr, &dev->cow.bitmap_offset, &dev->cow.bitmap_len, &dev->cow.data_offset, create_ptr); if((dev->fd == -ENOENT) && create_cow){ - n = dev - ubd_dev; dev->fd = create_cow_file(dev->file, dev->cow.file, - dev->openflags, 1 << 9, + dev->openflags, 1 << 9, PAGE_SIZE, &dev->cow.bitmap_offset, &dev->cow.bitmap_len, &dev->cow.data_offset); @@ -455,13 +524,17 @@ static int ubd_open_dev(struct ubd *dev) if(dev->cow.file != NULL){ err = -ENOMEM; dev->cow.bitmap = (void *) vmalloc(dev->cow.bitmap_len); - if(dev->cow.bitmap == NULL) goto error; + if(dev->cow.bitmap == NULL){ + printk(KERN_ERR "Failed to vmalloc COW bitmap\n"); + goto error; + } flush_tlb_kernel_vm(); err = read_cow_bitmap(dev->fd, dev->cow.bitmap, dev->cow.bitmap_offset, dev->cow.bitmap_len); - if(err) goto error; + if(err < 0) + goto error; flags = dev->openflags; flags.w = 0; @@ -481,17 +554,31 @@ static int ubd_new_disk(int major, u64 s { struct gendisk *disk; + char from[sizeof("ubd/nnnnn\0")], to[sizeof("discnnnnn/disc\0")]; + int err; disk = alloc_disk(1 << UBD_SHIFT); - if (!disk) - return -ENOMEM; + if(disk == NULL) + return(-ENOMEM); disk->major = major; disk->first_minor = unit << UBD_SHIFT; disk->fops = &ubd_blops; set_capacity(disk, size / 512); - sprintf(disk->disk_name, "ubd"); - sprintf(disk->devfs_name, "ubd/disc%d", unit); + if(major == MAJOR_NR){ + sprintf(disk->disk_name, "ubd%c", 'a' + unit); + sprintf(disk->devfs_name, "ubd/disc%d", unit); + sprintf(from, "ubd/%d", unit); + sprintf(to, "disc%d/disc", unit); + err = devfs_mk_symlink(from, to); + if(err) + printk("ubd_new_disk failed to make link from %s to " + "%s, error = %d\n", from, to, err); + } + else { + sprintf(disk->disk_name, "ubd_fake%d", unit); + sprintf(disk->devfs_name, "ubd_fake/disc%d", unit); + } disk->private_data = &ubd_dev[unit]; disk->queue = ubd_queue; @@ -506,24 +593,21 @@ static int ubd_add(int n) struct ubd *dev = &ubd_dev[n]; int err; - if(dev->is_dir) - return(-EISDIR); - - if (!dev->file) + if(dev->file == NULL) return(-ENODEV); if (ubd_open_dev(dev)) return(-ENODEV); err = ubd_file_size(dev, &dev->size); - if(err) + if(err < 0) return(err); err = ubd_new_disk(MAJOR_NR, dev->size, n, &ubd_gendisk[n]); if(err) return(err); - if(fake_major) + if(fake_major != MAJOR_NR) ubd_new_disk(fake_major, dev->size, n, &fake_gendisk[n]); @@ -561,42 +645,42 @@ static int ubd_config(char *str) return(err); } -static int ubd_get_config(char *dev, char *str, int size, char **error_out) +static int ubd_get_config(char *name, char *str, int size, char **error_out) { - struct ubd *ubd; + struct ubd *dev; char *end; - int major, n = 0; + int n, len = 0; - major = simple_strtoul(dev, &end, 0); - if((*end != '\0') || (end == dev)){ - *error_out = "ubd_get_config : didn't parse major number"; + n = simple_strtoul(name, &end, 0); + if((*end != '\0') || (end == name)){ + *error_out = "ubd_get_config : didn't parse device number"; return(-1); } - if((major >= MAX_DEV) || (major < 0)){ - *error_out = "ubd_get_config : major number out of range"; + if((n >= MAX_DEV) || (n < 0)){ + *error_out = "ubd_get_config : device number out of range"; return(-1); } - ubd = &ubd_dev[major]; + dev = &ubd_dev[n]; spin_lock(&ubd_lock); - if(ubd->file == NULL){ - CONFIG_CHUNK(str, size, n, "", 1); + if(dev->file == NULL){ + CONFIG_CHUNK(str, size, len, "", 1); goto out; } - CONFIG_CHUNK(str, size, n, ubd->file, 0); + CONFIG_CHUNK(str, size, len, dev->file, 0); - if(ubd->cow.file != NULL){ - CONFIG_CHUNK(str, size, n, ",", 0); - CONFIG_CHUNK(str, size, n, ubd->cow.file, 1); + if(dev->cow.file != NULL){ + CONFIG_CHUNK(str, size, len, ",", 0); + CONFIG_CHUNK(str, size, len, dev->cow.file, 1); } - else CONFIG_CHUNK(str, size, n, "", 1); + else CONFIG_CHUNK(str, size, len, "", 1); out: spin_unlock(&ubd_lock); - return(n); + return(len); } static int ubd_remove(char *str) @@ -604,11 +688,9 @@ static int ubd_remove(char *str) struct ubd *dev; int n, err = -ENODEV; - if(!isdigit(*str)) - return(err); /* it should be a number 0-7/a-h */ + n = parse_unit(&str); - n = *str - '0'; - if(n >= MAX_DEV) + if((n < 0) || (n >= MAX_DEV)) return(err); dev = &ubd_dev[n]; @@ -669,7 +751,7 @@ int ubd_init(void) elevator_init(ubd_queue, &elevator_noop); - if (fake_major != 0) { + if (fake_major != MAJOR_NR) { char name[sizeof("ubd_nnn\0")]; snprintf(name, sizeof(name), "ubd_%d", fake_major); @@ -696,6 +778,7 @@ int ubd_driver_init(void){ io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *), &thread_fd); if(io_pid < 0){ + io_pid = -1; printk(KERN_ERR "ubd : Failed to start I/O thread (errno = %d) - " "falling back to synchronous I/O\n", -io_pid); @@ -703,8 +786,8 @@ int ubd_driver_init(void){ } 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); + if(err != 0) + printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err); return(err); } @@ -714,15 +797,9 @@ static int ubd_open(struct inode *inode, { struct gendisk *disk = inode->i_bdev->bd_disk; struct ubd *dev = disk->private_data; - int err = -EISDIR; + int err = 0; - if(dev->is_dir == 1) - goto out; - - err = 0; if(dev->count == 0){ - dev->openflags = dev->boot_openflags; - err = ubd_open_dev(dev); if(err){ printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n", @@ -749,62 +826,156 @@ static int ubd_release(struct inode * in return(0); } -void cowify_req(struct io_thread_req *req, struct ubd *dev) +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 = 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; + + update_bitmap = 1; + ubd_set_bit(sector + i, (unsigned char *) bitmap); + } + + if(!update_bitmap) + return; + + *cow_offset = sector / (sizeof(unsigned long) * 8); + + /* 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) { - int i, update_bitmap, sector = req->offset >> 9; + __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 *) - dev->cow.bitmap)){ + if(ubd_test_bit(sector + i, (unsigned char *) bitmap)) ubd_set_bit(i, (unsigned char *) &req->sector_mask); - } } - } - else { - update_bitmap = 0; - for(i = 0; i < req->length >> 9; i++){ - ubd_set_bit(i, (unsigned char *) - &req->sector_mask); - if(!ubd_test_bit(sector + i, (unsigned char *) - dev->cow.bitmap)) - update_bitmap = 1; - ubd_set_bit(sector + i, (unsigned char *) - dev->cow.bitmap); - } - if(update_bitmap){ - req->cow_offset = sector / (sizeof(unsigned long) * 8); - req->bitmap_words[0] = - dev->cow.bitmap[req->cow_offset]; - req->bitmap_words[1] = - dev->cow.bitmap[req->cow_offset + 1]; - req->cow_offset *= sizeof(unsigned long); - req->cow_offset += dev->cow.bitmap_offset; + } + else cowify_bitmap(req->offset, req->length, &req->sector_mask, + &req->cow_offset, bitmap, bitmap_offset, + req->bitmap_words, bitmap_len); +} + +static int mmap_fd(struct request *req, struct ubd *dev, __u64 offset) +{ + __u64 sector; + unsigned char *bitmap; + int bit, i; + + /* mmap must have been requested on the command line */ + if(!ubd_do_mmap) + return(-1); + + /* The buffer must be page aligned */ + if(((unsigned long) req->buffer % UBD_MMAP_BLOCK_SIZE) != 0) + return(-1); + + /* The request must be a page long */ + if((req->current_nr_sectors << 9) != PAGE_SIZE) + return(-1); + + if(dev->cow.file == NULL) + return(dev->fd); + + sector = offset >> 9; + bitmap = (unsigned char *) dev->cow.bitmap; + bit = ubd_test_bit(sector, bitmap); + + for(i = 1; i < req->current_nr_sectors; i++){ + if(ubd_test_bit(sector + i, bitmap) != bit) + return(-1); + } + + if(bit || (rq_data_dir(req) == WRITE)) + offset += dev->cow.data_offset; + + /* The data on disk must be page aligned */ + if((offset % UBD_MMAP_BLOCK_SIZE) != 0) + return(-1); + + return(bit ? dev->fd : dev->cow.fd); +} + +static int prepare_mmap_request(struct ubd *dev, int fd, __u64 offset, + struct request *req, + struct io_thread_req *io_req) +{ + int err; + + if(rq_data_dir(req) == WRITE){ + /* Writes are almost no-ops since the new data is already in the + * host page cache + */ + dev->map_writes++; + if(dev->cow.file != NULL) + cowify_bitmap(io_req->offset, io_req->length, + &io_req->sector_mask, &io_req->cow_offset, + dev->cow.bitmap, dev->cow.bitmap_offset, + io_req->bitmap_words, + dev->cow.bitmap_len); + } + else { + int w; + + if((dev->cow.file != NULL) && (fd == dev->cow.fd)) + w = 0; + else w = dev->openflags.w; + + if((dev->cow.file != NULL) && (fd == dev->fd)) + offset += dev->cow.data_offset; + + err = physmem_subst_mapping(req->buffer, fd, offset, w); + if(err){ + printk("physmem_subst_mapping failed, err = %d\n", + -err); + return(1); } + dev->map_reads++; } + io_req->op = UBD_MMAP; + io_req->buffer = req->buffer; + return(0); } 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 block; - int nsect; + __u64 offset; + int len, fd; if(req->rq_status == RQ_INACTIVE) return(1); - if(dev->is_dir){ - strcpy(req->buffer, "HOSTFS:"); - strcat(req->buffer, dev->file); - spin_lock(&ubd_io_lock); - end_request(req, 1); - spin_unlock(&ubd_io_lock); - return(1); - } - if((rq_data_dir(req) == WRITE) && !dev->openflags.w){ printk("Write attempted on readonly ubd device %s\n", disk->disk_name); @@ -814,23 +985,49 @@ static int prepare_request(struct reques return(1); } - block = req->sector; - nsect = req->current_nr_sectors; + offset = ((__u64) req->sector) << 9; + len = req->current_nr_sectors << 9; - io_req->op = rq_data_dir(req) == READ ? UBD_READ : UBD_WRITE; io_req->fds[0] = (dev->cow.file != NULL) ? dev->cow.fd : dev->fd; io_req->fds[1] = dev->fd; + io_req->map_fd = -1; + io_req->cow_offset = -1; + io_req->offset = offset; + io_req->length = len; + io_req->error = 0; + io_req->sector_mask = 0; + + fd = mmap_fd(req, dev, io_req->offset); + if(fd > 0){ + /* If mmapping is otherwise OK, but the first access to the + * page is a write, then it's not mapped in yet. So we have + * to write the data to disk first, then we can map the disk + * page in and continue normally from there. + */ + if((rq_data_dir(req) == WRITE) && !is_remapped(req->buffer)){ + io_req->map_fd = dev->fd; + io_req->map_offset = io_req->offset + + dev->cow.data_offset; + dev->write_maps++; + } + else return(prepare_mmap_request(dev, fd, io_req->offset, req, + io_req)); + } + + if(rq_data_dir(req) == READ) + dev->nomap_reads++; + else dev->nomap_writes++; + + 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->offset = ((__u64) block) << 9; - io_req->length = nsect << 9; io_req->buffer = req->buffer; io_req->sectorsize = 1 << 9; - io_req->sector_mask = 0; - io_req->cow_offset = -1; - io_req->error = 0; - if(dev->cow.file != NULL) cowify_req(io_req, dev); + if(dev->cow.file != NULL) + cowify_req(io_req, dev->cow.bitmap, dev->cow.bitmap_offset, + dev->cow.bitmap_len); + return(0); } @@ -841,7 +1038,7 @@ static void do_ubd_request(request_queue int err, n; if(thread_fd == -1){ - while(!list_empty(&q->queue_head)){ + while(!elv_queue_empty(q)){ req = elv_next_request(q); err = prepare_request(req, &io_req); if(!err){ @@ -851,7 +1048,8 @@ static void do_ubd_request(request_queue } } else { - if(do_ubd || list_empty(&q->queue_head)) return; + if(do_ubd || elv_queue_empty(q)) + return; req = elv_next_request(q); err = prepare_request(req, &io_req); if(!err){ @@ -885,7 +1083,7 @@ static int ubd_ioctl(struct inode * inod g.heads = 128; g.sectors = 32; g.cylinders = dev->size / (128 * 32 * 512); - g.start = 2; + g.start = get_start_sect(inode->i_bdev); return(copy_to_user(loc, &g, sizeof(g)) ? -EFAULT : 0); case HDIO_SET_UNMASKINTR: @@ -935,6 +1133,142 @@ static int ubd_ioctl(struct inode * inod return(-EINVAL); } +static int ubd_check_remapped(int fd, unsigned long address, int is_write, + __u64 offset) +{ + __u64 bitmap_offset; + unsigned long new_bitmap[2]; + int i, err, n; + + /* If it's not a write access, we can't do anything about it */ + if(!is_write) + return(0); + + /* We have a write */ + for(i = 0; i < sizeof(ubd_dev) / sizeof(ubd_dev[0]); i++){ + struct ubd *dev = &ubd_dev[i]; + + if((dev->fd != fd) && (dev->cow.fd != fd)) + continue; + + /* It's a write to a ubd device */ + + if(!dev->openflags.w){ + /* It's a write access on a read-only device - probably + * shouldn't happen. If the kernel is trying to change + * something with no intention of writing it back out, + * then this message will clue us in that this needs + * fixing + */ + printk("Write access to mapped page from readonly ubd " + "device %d\n", i); + return(0); + } + + /* It's a write to a writeable ubd device - it must be COWed + * because, otherwise, the page would have been mapped in + * writeable + */ + + if(!dev->cow.file) + panic("Write fault on writeable non-COW ubd device %d", + i); + + /* It should also be an access to the backing file since the + * COW pages should be mapped in read-write + */ + + if(fd == dev->fd) + panic("Write fault on a backing page of ubd " + "device %d\n", i); + + /* So, we do the write, copying the backing data to the COW + * file... + */ + + err = os_seek_file(dev->fd, offset + dev->cow.data_offset); + if(err < 0) + panic("Couldn't seek to %lld in COW file of ubd " + "device %d, err = %d", + offset + dev->cow.data_offset, i, -err); + + n = os_write_file(dev->fd, (void *) address, PAGE_SIZE); + if(n != PAGE_SIZE) + panic("Couldn't copy data to COW file of ubd " + "device %d, err = %d", i, -n); + + /* ... updating the COW bitmap... */ + + cowify_bitmap(offset, PAGE_SIZE, NULL, &bitmap_offset, + dev->cow.bitmap, dev->cow.bitmap_offset, + new_bitmap, dev->cow.bitmap_len); + + err = os_seek_file(dev->fd, bitmap_offset); + if(err < 0) + panic("Couldn't seek to %lld in COW file of ubd " + "device %d, err = %d", bitmap_offset, i, -err); + + n = os_write_file(dev->fd, new_bitmap, sizeof(new_bitmap)); + if(n != sizeof(new_bitmap)) + panic("Couldn't update bitmap of ubd device %d, " + "err = %d", i, -n); + + /* Maybe we can map the COW page in, and maybe we can't. If + * it is a pre-V3 COW file, we can't, since the alignment will + * be wrong. If it is a V3 or later COW file which has been + * moved to a system with a larger page size, then maybe we + * can't, depending on the exact location of the page. + */ + + offset += dev->cow.data_offset; + + /* Remove the remapping, putting the original anonymous page + * back. If the COW file can be mapped in, that is done. + * Otherwise, the COW page is read in. + */ + + if(!physmem_remove_mapping((void *) address)) + panic("Address 0x%lx not remapped by ubd device %d", + address, i); + if((offset % UBD_MMAP_BLOCK_SIZE) == 0) + physmem_subst_mapping((void *) address, dev->fd, + offset, 1); + else { + err = os_seek_file(dev->fd, offset); + if(err < 0) + panic("Couldn't seek to %lld in COW file of " + "ubd device %d, err = %d", offset, i, + -err); + + n = os_read_file(dev->fd, (void *) address, PAGE_SIZE); + if(n != PAGE_SIZE) + panic("Failed to read page from offset %llx of " + "COW file of ubd device %d, err = %d", + offset, i, -n); + } + + return(1); + } + + /* It's not a write on a ubd device */ + return(0); +} + +static struct remapper ubd_remapper = { + .list = LIST_HEAD_INIT(ubd_remapper.list), + .proc = ubd_check_remapped, +}; + +static int ubd_remapper_setup(void) +{ + if(ubd_do_mmap) + register_remapper(&ubd_remapper); + + return(0); +} + +__initcall(ubd_remapper_setup); + /* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically --- linux-2.6.9-rc1/arch/um/drivers/ubd_user.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/um/drivers/ubd_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -11,11 +11,8 @@ #include #include #include -#include #include -#include #include -#include #include #include #include "asm/types.h" @@ -24,146 +21,30 @@ #include "user.h" #include "ubd_user.h" #include "os.h" +#include "cow.h" #include #include -#if __BYTE_ORDER == __BIG_ENDIAN -# define ntohll(x) (x) -# define htonll(x) (x) -#elif __BYTE_ORDER == __LITTLE_ENDIAN -# define ntohll(x) bswap_64(x) -# define htonll(x) bswap_64(x) -#else -#error "__BYTE_ORDER not defined" -#endif - -#define PATH_LEN_V1 256 - -struct cow_header_v1 { - int magic; - int version; - char backing_file[PATH_LEN_V1]; - time_t mtime; - __u64 size; - int sectorsize; -}; - -#define PATH_LEN_V2 MAXPATHLEN - -struct cow_header_v2 { - unsigned long magic; - unsigned long version; - char backing_file[PATH_LEN_V2]; - time_t mtime; - __u64 size; - int sectorsize; -}; - -union cow_header { - struct cow_header_v1 v1; - struct cow_header_v2 v2; -}; - -#define COW_MAGIC 0x4f4f4f4d /* MOOO */ -#define COW_VERSION 2 - -static void sizes(__u64 size, int sectorsize, int bitmap_offset, - unsigned long *bitmap_len_out, int *data_offset_out) -{ - *bitmap_len_out = (size + sectorsize - 1) / (8 * sectorsize); - - *data_offset_out = bitmap_offset + *bitmap_len_out; - *data_offset_out = (*data_offset_out + sectorsize - 1) / sectorsize; - *data_offset_out *= sectorsize; -} - -static int read_cow_header(int fd, int *magic_out, char **backing_file_out, - time_t *mtime_out, __u64 *size_out, - int *sectorsize_out, int *bitmap_offset_out) -{ - union cow_header *header; - char *file; - int err, n; - unsigned long version, magic; - - header = um_kmalloc(sizeof(*header)); - if(header == NULL){ - printk("read_cow_header - Failed to allocate header\n"); - return(-ENOMEM); - } - err = -EINVAL; - n = read(fd, header, sizeof(*header)); - if(n < offsetof(typeof(header->v1), backing_file)){ - printk("read_cow_header - short header\n"); - goto out; - } - - magic = header->v1.magic; - if(magic == COW_MAGIC) { - version = header->v1.version; - } - else if(magic == ntohl(COW_MAGIC)){ - version = ntohl(header->v1.version); - } - else goto out; - - *magic_out = COW_MAGIC; - - if(version == 1){ - if(n < sizeof(header->v1)){ - printk("read_cow_header - failed to read V1 header\n"); - goto out; - } - *mtime_out = header->v1.mtime; - *size_out = header->v1.size; - *sectorsize_out = header->v1.sectorsize; - *bitmap_offset_out = sizeof(header->v1); - file = header->v1.backing_file; - } - else if(version == 2){ - if(n < sizeof(header->v2)){ - printk("read_cow_header - failed to read V2 header\n"); - goto out; - } - *mtime_out = ntohl(header->v2.mtime); - *size_out = ntohll(header->v2.size); - *sectorsize_out = ntohl(header->v2.sectorsize); - *bitmap_offset_out = sizeof(header->v2); - file = header->v2.backing_file; - } - else { - printk("read_cow_header - invalid COW version\n"); - goto out; - } - err = -ENOMEM; - *backing_file_out = uml_strdup(file); - if(*backing_file_out == NULL){ - printk("read_cow_header - failed to allocate backing file\n"); - goto out; - } - err = 0; - out: - kfree(header); - return(err); -} static int same_backing_files(char *from_cmdline, char *from_cow, char *cow) { - struct stat buf1, buf2; + struct uml_stat buf1, buf2; + int err; if(from_cmdline == NULL) return(1); if(!strcmp(from_cmdline, from_cow)) return(1); - if(stat(from_cmdline, &buf1) < 0){ - printk("Couldn't stat '%s', errno = %d\n", from_cmdline, - errno); + err = os_stat_file(from_cmdline, &buf1); + if(err < 0){ + printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err); return(1); } - if(stat(from_cow, &buf2) < 0){ - printk("Couldn't stat '%s', errno = %d\n", from_cow, errno); + err = os_stat_file(from_cow, &buf2); + if(err < 0){ + printk("Couldn't stat '%s', err = %d\n", from_cow, -err); return(1); } - if((buf1.st_dev == buf2.st_dev) && (buf1.st_ino == buf2.st_ino)) + if((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino)) return(1); printk("Backing file mismatch - \"%s\" requested,\n" @@ -174,20 +55,21 @@ static int same_backing_files(char *from static int backing_file_mismatch(char *file, __u64 size, time_t mtime) { - struct stat64 buf; + unsigned long modtime; long long actual; int err; - if(stat64(file, &buf) < 0){ - printk("Failed to stat backing file \"%s\", errno = %d\n", - file, errno); - return(-errno); + err = os_file_modtime(file, &modtime); + if(err < 0){ + printk("Failed to get modification time of backing file " + "\"%s\", err = %d\n", file, -err); + return(err); } err = os_file_size(file, &actual); - if(err){ + if(err < 0){ printk("Failed to get size of backing file \"%s\", " - "errno = %d\n", file, -err); + "err = %d\n", file, -err); return(err); } @@ -196,9 +78,9 @@ static int backing_file_mismatch(char *f "file\n", size, actual); return(-EINVAL); } - if(buf.st_mtime != mtime){ + if(modtime != mtime){ printk("mtime mismatch (%ld vs %ld) of COW header vs backing " - "file\n", mtime, buf.st_mtime); + "file\n", mtime, modtime); return(-EINVAL); } return(0); @@ -209,124 +91,16 @@ int read_cow_bitmap(int fd, void *buf, i int err; err = os_seek_file(fd, offset); - if(err != 0) return(-errno); - err = read(fd, buf, len); - if(err < 0) return(-errno); - return(0); -} + if(err < 0) + return(err); -static int absolutize(char *to, int size, char *from) -{ - char save_cwd[256], *slash; - int remaining; + err = os_read_file(fd, buf, len); + if(err < 0) + return(err); - if(getcwd(save_cwd, sizeof(save_cwd)) == NULL) { - printk("absolutize : unable to get cwd - errno = %d\n", errno); - return(-1); - } - slash = strrchr(from, '/'); - if(slash != NULL){ - *slash = '\0'; - if(chdir(from)){ - *slash = '/'; - printk("absolutize : Can't cd to '%s' - errno = %d\n", - from, errno); - return(-1); - } - *slash = '/'; - if(getcwd(to, size) == NULL){ - printk("absolutize : unable to get cwd of '%s' - " - "errno = %d\n", from, errno); - return(-1); - } - remaining = size - strlen(to); - if(strlen(slash) + 1 > remaining){ - printk("absolutize : unable to fit '%s' into %d " - "chars\n", from, size); - return(-1); - } - strcat(to, slash); - } - else { - if(strlen(save_cwd) + 1 + strlen(from) + 1 > size){ - printk("absolutize : unable to fit '%s' into %d " - "chars\n", from, size); - return(-1); - } - strcpy(to, save_cwd); - strcat(to, "/"); - strcat(to, from); - } - chdir(save_cwd); return(0); } -static int write_cow_header(char *cow_file, int fd, char *backing_file, - int sectorsize, long long *size) -{ - struct cow_header_v2 *header; - struct stat64 buf; - int err; - - err = os_seek_file(fd, 0); - if(err != 0){ - printk("write_cow_header - lseek failed, errno = %d\n", errno); - return(-errno); - } - - err = -ENOMEM; - header = um_kmalloc(sizeof(*header)); - if(header == NULL){ - printk("Failed to allocate COW V2 header\n"); - goto out; - } - header->magic = htonl(COW_MAGIC); - header->version = htonl(COW_VERSION); - - err = -EINVAL; - if(strlen(backing_file) > sizeof(header->backing_file) - 1){ - printk("Backing file name \"%s\" is too long - names are " - "limited to %d characters\n", backing_file, - sizeof(header->backing_file) - 1); - goto out_free; - } - - if(absolutize(header->backing_file, sizeof(header->backing_file), - backing_file)) - goto out_free; - - err = stat64(header->backing_file, &buf); - if(err < 0){ - printk("Stat of backing file '%s' failed, errno = %d\n", - header->backing_file, errno); - err = -errno; - goto out_free; - } - - err = os_file_size(header->backing_file, size); - if(err){ - printk("Couldn't get size of backing file '%s', errno = %d\n", - header->backing_file, -*size); - goto out_free; - } - - header->mtime = htonl(buf.st_mtime); - header->size = htonll(*size); - header->sectorsize = htonl(sectorsize); - - err = write(fd, header, sizeof(*header)); - if(err != sizeof(*header)){ - printk("Write of header to new COW file '%s' failed, " - "errno = %d\n", cow_file, errno); - goto out_free; - } - err = 0; - out_free: - kfree(header); - out: - return(err); -} - int open_ubd_file(char *file, struct openflags *openflags, char **backing_file_out, int *bitmap_offset_out, unsigned long *bitmap_len_out, int *data_offset_out, @@ -334,26 +108,36 @@ int open_ubd_file(char *file, struct ope { time_t mtime; __u64 size; + __u32 version, align; char *backing_file; - int fd, err, sectorsize, magic, same, mode = 0644; + int fd, err, sectorsize, same, mode = 0644; - if((fd = os_open_file(file, *openflags, mode)) < 0){ + fd = os_open_file(file, *openflags, mode); + if(fd < 0){ if((fd == -ENOENT) && (create_cow_out != NULL)) *create_cow_out = 1; if(!openflags->w || ((errno != EROFS) && (errno != EACCES))) return(-errno); openflags->w = 0; - if((fd = os_open_file(file, *openflags, mode)) < 0) + fd = os_open_file(file, *openflags, mode); + if(fd < 0) return(fd); } + + err = os_lock_file(fd, openflags->w); + if(err < 0){ + printk("Failed to lock '%s', err = %d\n", file, -err); + goto out_close; + } + if(backing_file_out == NULL) return(fd); - err = read_cow_header(fd, &magic, &backing_file, &mtime, &size, - §orsize, bitmap_offset_out); + err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime, + &size, §orsize, &align, bitmap_offset_out); if(err && (*backing_file_out != NULL)){ printk("Failed to read COW header from COW file \"%s\", " - "errno = %d\n", file, err); - goto error; + "errno = %d\n", file, -err); + goto out_close; } if(err) return(fd); @@ -363,36 +147,33 @@ int open_ubd_file(char *file, struct ope if(!same && !backing_file_mismatch(*backing_file_out, size, mtime)){ printk("Switching backing file to '%s'\n", *backing_file_out); - err = write_cow_header(file, fd, *backing_file_out, - sectorsize, &size); + err = write_cow_header(file, fd, *backing_file_out, + sectorsize, align, &size); if(err){ - printk("Switch failed, errno = %d\n", err); + printk("Switch failed, errno = %d\n", -err); return(err); } } else { *backing_file_out = backing_file; err = backing_file_mismatch(*backing_file_out, size, mtime); - if(err) goto error; + if(err) goto out_close; } - sizes(size, sectorsize, *bitmap_offset_out, bitmap_len_out, - data_offset_out); + cow_sizes(version, size, sectorsize, align, *bitmap_offset_out, + bitmap_len_out, data_offset_out); return(fd); - error: - close(fd); + out_close: + os_close_file(fd); return(err); } int create_cow_file(char *cow_file, char *backing_file, struct openflags flags, - int sectorsize, int *bitmap_offset_out, + int sectorsize, int alignment, int *bitmap_offset_out, unsigned long *bitmap_len_out, int *data_offset_out) { - __u64 blocks; - long zero; - int err, fd, i; - long long size; + int err, fd; flags.c = 1; fd = open_ubd_file(cow_file, &flags, NULL, NULL, NULL, NULL, NULL); @@ -403,57 +184,49 @@ int create_cow_file(char *cow_file, char goto out; } - err = write_cow_header(cow_file, fd, backing_file, sectorsize, &size); - if(err) goto out_close; - - blocks = (size + sectorsize - 1) / sectorsize; - blocks = (blocks + sizeof(long) * 8 - 1) / (sizeof(long) * 8); - zero = 0; - for(i = 0; i < blocks; i++){ - err = write(fd, &zero, sizeof(zero)); - if(err != sizeof(zero)){ - printk("Write of bitmap to new COW file '%s' failed, " - "errno = %d\n", cow_file, errno); - goto out_close; - } - } - - sizes(size, sectorsize, sizeof(struct cow_header_v2), - bitmap_len_out, data_offset_out); - *bitmap_offset_out = sizeof(struct cow_header_v2); - - return(fd); - - out_close: - close(fd); + err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment, + bitmap_offset_out, bitmap_len_out, + data_offset_out); + if(!err) + return(fd); + os_close_file(fd); out: return(err); } +/* XXX Just trivial wrappers around os_read_file and os_write_file */ int read_ubd_fs(int fd, void *buffer, int len) { - int n; - - n = read(fd, buffer, len); - if(n < 0) return(-errno); - else return(n); + return(os_read_file(fd, buffer, len)); } int write_ubd_fs(int fd, char *buffer, int len) { - int n; - - n = write(fd, buffer, len); - if(n < 0) return(-errno); - else return(n); + return(os_write_file(fd, buffer, len)); } -int ubd_is_dir(char *file) +static int update_bitmap(struct io_thread_req *req) { - struct stat64 buf; + int n; + + if(req->cow_offset == -1) + return(0); + + 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); + } + + 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); + } - if(stat64(file, &buf) < 0) return(0); - return(S_ISDIR(buf.st_mode)); + return(0); } void do_io(struct io_thread_req *req) @@ -461,8 +234,18 @@ void do_io(struct io_thread_req *req) char *buf; unsigned long len; int n, nsectors, start, end, bit; + int err; __u64 off; + if(req->op == UBD_MMAP){ + /* Touch the page to force the host to do any necessary IO to + * get it into memory + */ + n = *((volatile int *) req->buffer); + req->error = update_bitmap(req); + return; + } + nsectors = req->length / req->sectorsize; start = 0; do { @@ -473,15 +256,14 @@ void do_io(struct io_thread_req *req) &req->sector_mask) == bit)) end++; - if(end != nsectors) - printk("end != nsectors\n"); off = req->offset + req->offsets[bit] + start * req->sectorsize; len = (end - start) * req->sectorsize; buf = &req->buffer[start * req->sectorsize]; - if(os_seek_file(req->fds[bit], off) != 0){ - printk("do_io - lseek failed : errno = %d\n", errno); + err = os_seek_file(req->fds[bit], off); + if(err < 0){ + printk("do_io - lseek failed : err = %d\n", -err); req->error = 1; return; } @@ -490,11 +272,10 @@ void do_io(struct io_thread_req *req) do { buf = &buf[n]; len -= n; - n = read(req->fds[bit], buf, len); + n = os_read_file(req->fds[bit], buf, len); if (n < 0) { - printk("do_io - read returned %d : " - "errno = %d fd = %d\n", n, - errno, req->fds[bit]); + printk("do_io - read failed, err = %d " + "fd = %d\n", -n, req->fds[bit]); req->error = 1; return; } @@ -502,11 +283,10 @@ void do_io(struct io_thread_req *req) if (n < len) memset(&buf[n], 0, len - n); } else { - n = write(req->fds[bit], buf, len); + n = os_write_file(req->fds[bit], buf, len); if(n != len){ - printk("do_io - write returned %d : " - "errno = %d fd = %d\n", n, - errno, req->fds[bit]); + printk("do_io - write failed err = %d " + "fd = %d\n", -n, req->fds[bit]); req->error = 1; return; } @@ -515,24 +295,7 @@ void do_io(struct io_thread_req *req) start = end; } while(start < nsectors); - if(req->cow_offset != -1){ - if(os_seek_file(req->fds[1], req->cow_offset) != 0){ - printk("do_io - bitmap lseek failed : errno = %d\n", - errno); - req->error = 1; - return; - } - n = write(req->fds[1], &req->bitmap_words, - sizeof(req->bitmap_words)); - if(n != sizeof(req->bitmap_words)){ - printk("do_io - bitmap update returned %d : " - "errno = %d fd = %d\n", n, errno, req->fds[1]); - req->error = 1; - return; - } - } - req->error = 0; - return; + req->error = update_bitmap(req); } /* Changed in start_io_thread, which is serialized by being called only @@ -550,19 +313,23 @@ int io_thread(void *arg) signal(SIGWINCH, SIG_IGN); while(1){ - n = read(kernel_fd, &req, sizeof(req)); - if(n < 0) printk("io_thread - read returned %d, errno = %d\n", - n, errno); - else if(n < sizeof(req)){ - printk("io_thread - short read : length = %d\n", n); + 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 = write(kernel_fd, &req, sizeof(req)); + n = os_write_file(kernel_fd, &req, sizeof(req)); if(n != sizeof(req)) - printk("io_thread - write failed, errno = %d\n", - errno); + printk("io_thread - write failed, fd = %d, err = %d\n", + kernel_fd, -n); } } @@ -571,10 +338,11 @@ int start_io_thread(unsigned long sp, in int pid, fds[2], err; err = os_pipe(fds, 1, 1); - if(err){ - printk("start_io_thread - os_pipe failed, errno = %d\n", -err); - return(-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]; @@ -582,32 +350,19 @@ int start_io_thread(unsigned long sp, in NULL); if(pid < 0){ printk("start_io_thread - clone failed : errno = %d\n", errno); - return(-errno); + goto out_close; } - return(pid); -} - -#ifdef notdef -int start_io_thread(unsigned long sp, int *fd_out) -{ - int pid; - if((kernel_fd = get_pty()) < 0) return(-1); - raw(kernel_fd, 0); - if((*fd_out = open(ptsname(kernel_fd), O_RDWR)) < 0){ - printk("Couldn't open tty for IO\n"); - return(-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); - return(-errno); - } return(pid); + + out_close: + os_close_file(fds[0]); + os_close_file(fds[1]); + kernel_fd = -1; + *fd_out = -1; + out: + return(err); } -#endif /* * Overrides for Emacs so that we follow Linus's tabbing style. --- linux-2.6.9-rc1/arch/um/drivers/xterm.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/arch/um/drivers/xterm.c 2004-09-02 20:51:51.000000000 -0700 @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -36,7 +35,8 @@ void *xterm_init(char *str, int device, { struct xterm_chan *data; - if((data = malloc(sizeof(*data))) == NULL) return(NULL); + data = malloc(sizeof(*data)); + if(data == NULL) return(NULL); *data = ((struct xterm_chan) { .pid = -1, .helper_pid = -1, .device = device, @@ -93,7 +93,7 @@ int xterm_open(int input, int output, in "/usr/lib/uml/port-helper", "-uml-socket", file, NULL }; - if(access(argv[4], X_OK)) + if(os_access(argv[4], OS_ACC_X_OK) < 0) argv[4] = "port-helper"; fd = mkstemp(file); @@ -106,13 +106,13 @@ int xterm_open(int input, int output, in printk("xterm_open : unlink failed, errno = %d\n", errno); return(-errno); } - close(fd); + os_close_file(fd); - fd = create_unix_socket(file, sizeof(file)); + fd = os_create_unix_socket(file, sizeof(file), 1); if(fd < 0){ printk("xterm_open : create_unix_socket failed, errno = %d\n", -fd); - return(-fd); + return(fd); } sprintf(title, data->title, data->device); @@ -128,15 +128,16 @@ int xterm_open(int input, int output, in if(data->direct_rcv) new = os_rcv_fd(fd, &data->helper_pid); else { - if((err = os_set_fd_block(fd, 0)) != 0){ + err = os_set_fd_block(fd, 0); + if(err < 0){ printk("xterm_open : failed to set descriptor " - "non-blocking, errno = %d\n", err); + "non-blocking, err = %d\n", -err); return(err); } new = xterm_fd(fd, &data->helper_pid); } if(new < 0){ - printk("xterm_open : os_rcv_fd failed, errno = %d\n", -new); + printk("xterm_open : os_rcv_fd failed, err = %d\n", -new); goto out; } @@ -160,7 +161,7 @@ void xterm_close(int fd, void *d) if(data->helper_pid != -1) os_kill_process(data->helper_pid, 0); data->helper_pid = -1; - close(fd); + os_close_file(fd); } void xterm_free(void *d) --- linux-2.6.9-rc1/arch/um/drivers/xterm_kern.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/arch/um/drivers/xterm_kern.c 2004-09-02 20:51:51.000000000 -0700 @@ -5,9 +5,12 @@ #include "linux/errno.h" #include "linux/slab.h" +#include "linux/signal.h" +#include "linux/interrupt.h" #include "asm/semaphore.h" #include "asm/irq.h" #include "irq_user.h" +#include "irq_kern.h" #include "kern_util.h" #include "os.h" #include "xterm.h" @@ -19,17 +22,18 @@ struct xterm_wait { int new_fd; }; -static void xterm_interrupt(int irq, void *data, struct pt_regs *regs) +static irqreturn_t xterm_interrupt(int irq, void *data, struct pt_regs *regs) { struct xterm_wait *xterm = data; int fd; fd = os_rcv_fd(xterm->fd, &xterm->pid); if(fd == -EAGAIN) - return; + return(IRQ_NONE); xterm->new_fd = fd; up(&xterm->sem); + return(IRQ_HANDLED); } int xterm_fd(int socket, int *pid_out) @@ -54,7 +58,8 @@ int xterm_fd(int socket, int *pid_out) if(err){ printk(KERN_ERR "xterm_fd : failed to get IRQ for xterm, " "err = %d\n", err); - return(err); + ret = err; + goto out; } down(&data->sem); @@ -62,6 +67,7 @@ int xterm_fd(int socket, int *pid_out) ret = data->new_fd; *pid_out = data->pid; + out: kfree(data); return(ret); --- linux-2.6.9-rc1/arch/um/dyn.lds.S 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/um/dyn.lds.S 2004-09-02 20:51:51.000000000 -0700 @@ -1,3 +1,5 @@ +#include + OUTPUT_FORMAT(ELF_FORMAT) OUTPUT_ARCH(ELF_ARCH) ENTRY(_start) @@ -10,12 +12,15 @@ SECTIONS { . = START + SIZEOF_HEADERS; .interp : { *(.interp) } - . = ALIGN(4096); __binary_start = .; . = ALIGN(4096); /* Init code and data */ _stext = .; __init_begin = .; - .text.init : { *(.text.init) } + .init.text : { + _sinittext = .; + *(.init.text) + _einittext = .; + } . = ALIGN(4096); @@ -55,7 +60,9 @@ SECTIONS } =0x90909090 .plt : { *(.plt) } .text : { - *(.text .stub .text.* .gnu.linkonce.t.*) + *(.text) + SCHED_TEXT + *(.stub .text.* .gnu.linkonce.t.*) /* .gnu.warning sections are handled specially by elf32.em. */ *(.gnu.warning) } =0x90909090 @@ -67,7 +74,7 @@ SECTIONS #include "asm/common.lds.S" - .data.init : { *(.data.init) } + init.data : { *(.init.data) } /* Ensure the __preinit_array_start label is properly aligned. We could instead move the label definition inside the section, but --- linux-2.6.9-rc1/arch/um/include/2_5compat.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/um/include/2_5compat.h 2004-09-02 20:51:51.000000000 -0700 @@ -6,20 +6,6 @@ #ifndef __2_5_COMPAT_H__ #define __2_5_COMPAT_H__ -#include "linux/version.h" - -#define INIT_CONSOLE(dev_name, write_proc, device_proc, setup_proc, f) { \ - name : dev_name, \ - write : write_proc, \ - read : NULL, \ - device : device_proc, \ - setup : setup_proc, \ - flags : f, \ - index : -1, \ - cflag : 0, \ - next : NULL \ -} - #define INIT_HARDSECT(arr, maj, sizes) #define SET_PRI(task) do ; while(0) --- linux-2.6.9-rc1/arch/um/include/hostaudio.h 2004-08-24 00:02:48.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2002 Steve Schmidtke - * Licensed under the GPL - */ - -#ifndef HOSTAUDIO_H -#define HOSTAUDIO_H - -#define HOSTAUDIO_DEV_DSP "/dev/sound/dsp" -#define HOSTAUDIO_DEV_MIXER "/dev/sound/mixer" - -struct hostaudio_state { - int fd; -}; - -struct hostmixer_state { - int fd; -}; - -/* UML user-side protoypes */ -extern ssize_t hostaudio_read_user(struct hostaudio_state *state, char *buffer, - size_t count, loff_t *ppos); -extern ssize_t hostaudio_write_user(struct hostaudio_state *state, - const char *buffer, size_t count, - loff_t *ppos); -extern int hostaudio_ioctl_user(struct hostaudio_state *state, - unsigned int cmd, unsigned long arg); -extern int hostaudio_open_user(struct hostaudio_state *state, int r, int w, - char *dsp); -extern int hostaudio_release_user(struct hostaudio_state *state); -extern int hostmixer_ioctl_mixdev_user(struct hostmixer_state *state, - unsigned int cmd, unsigned long arg); -extern int hostmixer_open_mixdev_user(struct hostmixer_state *state, int r, - int w, char *mixer); -extern int hostmixer_release_mixdev_user(struct hostmixer_state *state); - -#endif /* HOSTAUDIO_H */ - -/* - * 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: - */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/um/include/irq_kern.h 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __IRQ_KERN_H__ +#define __IRQ_KERN_H__ + +#include "linux/interrupt.h" + +extern int um_request_irq(unsigned int irq, int fd, int type, + irqreturn_t (*handler)(int, void *, + struct pt_regs *), + unsigned long irqflags, const char * devname, + void *dev_id); + +#endif + +/* + * 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: + */ --- linux-2.6.9-rc1/arch/um/include/kern_util.h 2004-08-24 00:02:26.000000000 -0700 +++ 25/arch/um/include/kern_util.h 2004-09-02 20:51:51.000000000 -0700 @@ -60,12 +60,11 @@ extern void finish_fork(void); extern void paging_init(void); extern void init_flush_vm(void); extern void *syscall_sp(void *t); -extern void syscall_trace(void); +extern void syscall_trace(union uml_pt_regs *regs, int entryexit); extern int hz(void); -extern void idle_timer(void); +extern void uml_idle_timer(void); extern unsigned int do_IRQ(int irq, union uml_pt_regs *regs); extern int external_pid(void *t); -extern int pid_to_processor_id(int pid); extern void boot_timer_handler(int sig); extern void interrupt_end(void); extern void initial_thread_cb(void (*proc)(void *), void *arg); @@ -89,9 +88,7 @@ extern int remove_gdb(void); extern char *uml_strdup(char *string); extern void unprotect_kernel_mem(void); extern void protect_kernel_mem(void); -extern void set_kmem_end(unsigned long); extern void uml_cleanup(void); -extern int pid_to_processor_id(int pid); extern void set_current(void *t); extern void lock_signalled_task(void *t); extern void IPI_handler(int cpu); @@ -100,7 +97,9 @@ extern void *get_init_task(void); extern int clear_user_proc(void *buf, int size); extern int copy_to_user_proc(void *to, void *from, int size); extern int copy_from_user_proc(void *to, void *from, int size); +extern int strlen_user_proc(char *str); extern void bus_handler(int sig, union uml_pt_regs *regs); +extern void winch(int sig, union uml_pt_regs *regs); extern long execute_syscall(void *r); extern int smp_sigio_handler(void); extern void *get_current(void); @@ -111,6 +110,8 @@ extern void arch_switch(void); extern void free_irq(unsigned int, void *); extern int um_in_interrupt(void); extern int cpu(void); +extern unsigned long long time_stamp(void); + #endif /* --- linux-2.6.9-rc1/arch/um/include/line.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/um/include/line.h 2004-09-02 20:51:51.000000000 -0700 @@ -9,12 +9,14 @@ #include "linux/list.h" #include "linux/workqueue.h" #include "linux/tty.h" +#include "linux/interrupt.h" #include "asm/semaphore.h" #include "chan_user.h" #include "mconsole_kern.h" struct line_driver { char *name; + char *device_name; char *devfs_name; short major; short minor_start; @@ -67,8 +69,6 @@ struct lines { #define LINES_INIT(n) { num : n } -extern void line_interrupt(int irq, void *data, struct pt_regs *unused); -extern void line_write_interrupt(int irq, void *data, struct pt_regs *unused); extern void line_close(struct line *lines, struct tty_struct *tty); extern int line_open(struct line *lines, struct tty_struct *tty, struct chan_opts *opts); --- linux-2.6.9-rc1/arch/um/include/mconsole.h 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/um/include/mconsole.h 2004-09-02 20:51:51.000000000 -0700 @@ -41,11 +41,13 @@ struct mconsole_notify { struct mc_request; +enum mc_context { MCONSOLE_INTR, MCONSOLE_PROC }; + struct mconsole_command { char *command; void (*handler)(struct mc_request *req); - int as_interrupt; + enum mc_context context; }; struct mc_request @@ -77,6 +79,8 @@ extern void mconsole_sysrq(struct mc_req extern void mconsole_cad(struct mc_request *req); extern void mconsole_stop(struct mc_request *req); extern void mconsole_go(struct mc_request *req); +extern void mconsole_log(struct mc_request *req); +extern void mconsole_proc(struct mc_request *req); extern int mconsole_get_request(int fd, struct mc_request *req); extern int mconsole_notify(char *sock_name, int type, const void *data, --- linux-2.6.9-rc1/arch/um/include/mem.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/um/include/mem.h 2004-09-02 20:51:51.000000000 -0700 @@ -1,19 +1,18 @@ /* - * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2002, 2003 Jeff Dike (jdike@addtoit.com) * Licensed under the GPL */ #ifndef __MEM_H__ #define __MEM_H__ -struct vm_reserved { - struct list_head list; - unsigned long start; - unsigned long end; -}; +#include "linux/types.h" -extern void set_usable_vm(unsigned long start, unsigned long end); -extern void set_kmem_end(unsigned long new); +extern int phys_mapping(unsigned long phys, __u64 *offset_out); +extern int physmem_subst_mapping(void *virt, int fd, __u64 offset, int w); +extern int is_remapped(void *virt); +extern int physmem_remove_mapping(void *virt); +extern void physmem_forget_descriptor(int fd); #endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/um/include/mem_kern.h 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2003 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#ifndef __MEM_KERN_H__ +#define __MEM_KERN_H__ + +#include "linux/list.h" +#include "linux/types.h" + +struct remapper { + struct list_head list; + int (*proc)(int, unsigned long, int, __u64); +}; + +extern void register_remapper(struct remapper *info); + +#endif + +/* + * 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: + */ --- linux-2.6.9-rc1/arch/um/include/mem_user.h 2004-08-24 00:03:12.000000000 -0700 +++ 25/arch/um/include/mem_user.h 2004-09-02 20:51:51.000000000 -0700 @@ -32,43 +32,38 @@ #ifndef _MEM_USER_H #define _MEM_USER_H -struct mem_region { +struct iomem_region { + struct iomem_region *next; char *driver; - unsigned long start_pfn; - unsigned long start; - unsigned long len; - void *mem_map; int fd; + int size; + unsigned long phys; + unsigned long virt; }; -extern struct mem_region *regions[]; -extern struct mem_region physmem_region; +extern struct iomem_region *iomem_regions; +extern int iomem_size; #define ROUND_4M(n) ((((unsigned long) (n)) + (1 << 22)) & ~((1 << 22) - 1)) extern unsigned long host_task_size; extern unsigned long task_size; +extern void check_devanon(void); extern int init_mem_user(void); extern int create_mem_file(unsigned long len); -extern void setup_range(int fd, char *driver, unsigned long start, - unsigned long pfn, unsigned long total, int need_vm, - struct mem_region *region, void *reserved); extern void setup_memory(void *entry); extern unsigned long find_iomem(char *driver, unsigned long *len_out); -extern int init_maps(struct mem_region *region); -extern int nregions(void); -extern int reserve_vm(unsigned long start, unsigned long end, void *e); +extern int init_maps(unsigned long physmem, unsigned long iomem, + unsigned long highmem); extern unsigned long get_vm(unsigned long len); extern void setup_physmem(unsigned long start, unsigned long usable, - unsigned long len); -extern int setup_region(struct mem_region *region, void *entry); + unsigned long len, unsigned long highmem); extern void add_iomem(char *name, int fd, unsigned long size); -extern struct mem_region *phys_region(unsigned long phys); extern unsigned long phys_offset(unsigned long phys); extern void unmap_physmem(void); -extern int map_memory(unsigned long virt, unsigned long phys, - unsigned long len, int r, int w, int x); +extern void map_memory(unsigned long virt, unsigned long phys, + unsigned long len, int r, int w, int x); extern int protect_memory(unsigned long addr, unsigned long len, int r, int w, int x, int must_succeed); extern unsigned long get_kmem_end(void); --- linux-2.6.9-rc1/arch/um/include/os.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/um/include/os.h 2004-09-02 20:51:51.000000000 -0700 @@ -17,6 +17,32 @@ #define OS_TYPE_FIFO 6 #define OS_TYPE_SOCK 7 +/* os_access() flags */ +#define OS_ACC_F_OK 0 /* Test for existence. */ +#define OS_ACC_X_OK 1 /* Test for execute permission. */ +#define OS_ACC_W_OK 2 /* Test for write permission. */ +#define OS_ACC_R_OK 4 /* Test for read permission. */ +#define OS_ACC_RW_OK (OS_ACC_W_OK | OS_ACC_R_OK) /* Test for RW permission */ + +/* + * types taken from stat_file() in hostfs_user.c + * (if they are wrong here, they are wrong there...). + */ +struct uml_stat { + int ust_dev; /* device */ + unsigned long long ust_ino; /* inode */ + int ust_mode; /* protection */ + int ust_nlink; /* number of hard links */ + int ust_uid; /* user ID of owner */ + int ust_gid; /* group ID of owner */ + unsigned long long ust_size; /* total size, in bytes */ + int ust_blksize; /* blocksize for filesystem I/O */ + unsigned long long ust_blocks; /* number of blocks allocated */ + unsigned long ust_atime; /* time of last access */ + unsigned long ust_mtime; /* time of last modification */ + unsigned long ust_ctime; /* time of last change */ +}; + struct openflags { unsigned int r : 1; unsigned int w : 1; @@ -84,29 +110,47 @@ static inline struct openflags of_excl(s flags.e = 1; return(flags); } - + static inline struct openflags of_cloexec(struct openflags flags) { flags.cl = 1; return(flags); } +extern int os_stat_file(const char *file_name, struct uml_stat *buf); +extern int os_stat_fd(const int fd, struct uml_stat *buf); +extern int os_access(const char *file, int mode); +extern void os_print_error(int error, const char* str); +extern int os_get_exec_close(int fd, int *close_on_exec); +extern int os_set_exec_close(int fd, int close_on_exec); +extern int os_ioctl_generic(int fd, unsigned int cmd, unsigned long arg); +extern int os_window_size(int fd, int *rows, int *cols); +extern int os_new_tty_pgrp(int fd, int pid); +extern int os_get_ifname(int fd, char *namebuf); +extern int os_set_slip(int fd); +extern int os_set_owner(int fd, int pid); +extern int os_sigio_async(int master, int slave); +extern int os_mode_fd(int fd, int mode); + extern int os_seek_file(int fd, __u64 offset); extern int os_open_file(char *file, struct openflags flags, int mode); extern int os_read_file(int fd, void *buf, int len); -extern int os_write_file(int fd, void *buf, int count); +extern int os_write_file(int fd, const void *buf, int count); extern int os_file_size(char *file, long long *size_out); +extern int os_file_modtime(char *file, unsigned long *modtime); extern int os_pipe(int *fd, int stream, int close_on_exec); extern int os_set_fd_async(int fd, int owner); extern int os_set_fd_block(int fd, int blocking); extern int os_accept_connection(int fd); +extern int os_create_unix_socket(char *file, int len, int close_on_exec); extern int os_shutdown_socket(int fd, int r, int w); extern void os_close_file(int fd); extern int os_rcv_fd(int fd, int *helper_pid_out); -extern int create_unix_socket(char *file, int len); +extern int create_unix_socket(char *file, int len, int close_on_exec); extern int os_connect_socket(char *name); extern int os_file_type(char *file); extern int os_file_mode(char *file, struct openflags *mode_out); +extern int os_lock_file(int fd, int excl); extern unsigned long os_process_pc(int pid); extern int os_process_parent(int pid); @@ -115,11 +159,12 @@ extern void os_kill_process(int pid, int extern void os_usr1_process(int pid); extern int os_getpid(void); -extern int os_map_memory(void *virt, int fd, unsigned long off, +extern int os_map_memory(void *virt, int fd, unsigned long long off, unsigned long len, int r, int w, int x); extern int os_protect_memory(void *addr, unsigned long len, int r, int w, int x); extern int os_unmap_memory(void *addr, int len); +extern void os_flush_stdout(void); #endif --- linux-2.6.9-rc1/arch/um/include/signal_user.h 2004-08-24 00:01:52.000000000 -0700 +++ 25/arch/um/include/signal_user.h 2004-09-02 20:51:51.000000000 -0700 @@ -11,6 +11,8 @@ extern int signal_stack_size; extern int change_sig(int signal, int on); extern void set_sigstack(void *stack, int size); extern void set_handler(int sig, void (*handler)(int), int flags, ...); +extern int set_signals(int enable); +extern int get_signals(void); #endif --- linux-2.6.9-rc1/arch/um/include/skas_ptrace.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/um/include/skas_ptrace.h 2004-09-02 20:51:51.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) * Licensed under the GPL */ --- linux-2.6.9-rc1/arch/um/include/sysdep-i386/checksum.h 2004-08-24 00:03:50.000000000 -0700 +++ 25/arch/um/include/sysdep-i386/checksum.h 2004-09-02 20:51:51.000000000 -0700 @@ -5,6 +5,7 @@ #ifndef __UM_SYSDEP_CHECKSUM_H #define __UM_SYSDEP_CHECKSUM_H +#include "linux/in6.h" #include "linux/string.h" /* --- linux-2.6.9-rc1/arch/um/include/sysdep-i386/frame_user.h 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/um/include/sysdep-i386/frame_user.h 2004-09-02 20:51:51.000000000 -0700 @@ -56,26 +56,26 @@ static inline void setup_arch_frame(stru * it would have to be __builtin_frame_address(1). */ -static inline unsigned long frame_restorer(void) -{ - unsigned long *fp; - - fp = __builtin_frame_address(0); - return((unsigned long) (fp + 1)); -} +#define frame_restorer() \ +({ \ + unsigned long *fp; \ +\ + fp = __builtin_frame_address(0); \ + ((unsigned long) (fp + 1)); \ +}) /* Similarly, this returns the value of sp when the handler was first * entered. This is used to calculate the proper sp when delivering * signals. */ -static inline unsigned long frame_sp(void) -{ - unsigned long *fp; - - fp = __builtin_frame_address(0); - return((unsigned long) (fp + 1)); -} +#define frame_sp() \ +({ \ + unsigned long *fp; \ +\ + fp = __builtin_frame_address(0); \ + ((unsigned long) (fp + 1)); \ +}) #endif --- linux-2.6.9-rc1/arch/um/include/sysdep-i386/sigcontext.h 2004-08-24 00:03:18.000000000 -0700 +++ 25/arch/um/include/sysdep-i386/sigcontext.h 2004-09-02 20:51:51.000000000 -0700 @@ -28,8 +28,8 @@ */ #define SC_START_SYSCALL(sc) do SC_EAX(sc) = -ENOSYS; while(0) -/* These are General Protection and Page Fault */ -#define SEGV_IS_FIXABLE(trap) ((trap == 13) || (trap == 14)) +/* This is Page Fault */ +#define SEGV_IS_FIXABLE(trap) (trap == 14) #define SC_SEGV_IS_FIXABLE(sc) (SEGV_IS_FIXABLE(SC_TRAPNO(sc))) --- linux-2.6.9-rc1/arch/um/include/sysdep-i386/syscalls.h 2004-08-24 00:01:55.000000000 -0700 +++ 25/arch/um/include/sysdep-i386/syscalls.h 2004-09-02 20:51:51.000000000 -0700 @@ -11,39 +11,34 @@ typedef long syscall_handler_t(struct pt #define EXECUTE_SYSCALL(syscall, regs) \ ((long (*)(struct syscall_args)) (*sys_call_table[syscall]))(SYSCALL_ARGS(®s->regs)) -extern syscall_handler_t sys_modify_ldt; -extern syscall_handler_t old_mmap_i386; -extern syscall_handler_t old_select; -extern syscall_handler_t sys_ni_syscall; - #define ARCH_SYSCALLS \ - [ __NR_mmap ] = old_mmap_i386, \ - [ __NR_select ] = old_select, \ - [ __NR_vm86old ] = sys_ni_syscall, \ - [ __NR_modify_ldt ] = sys_modify_ldt, \ - [ __NR_lchown32 ] = sys_lchown, \ - [ __NR_getuid32 ] = sys_getuid, \ - [ __NR_getgid32 ] = sys_getgid, \ - [ __NR_geteuid32 ] = sys_geteuid, \ - [ __NR_getegid32 ] = sys_getegid, \ - [ __NR_setreuid32 ] = sys_setreuid, \ - [ __NR_setregid32 ] = sys_setregid, \ - [ __NR_getgroups32 ] = sys_getgroups, \ - [ __NR_setgroups32 ] = sys_setgroups, \ - [ __NR_fchown32 ] = sys_fchown, \ - [ __NR_setresuid32 ] = sys_setresuid, \ - [ __NR_getresuid32 ] = sys_getresuid, \ - [ __NR_setresgid32 ] = sys_setresgid, \ - [ __NR_getresgid32 ] = sys_getresgid, \ - [ __NR_chown32 ] = sys_chown, \ - [ __NR_setuid32 ] = sys_setuid, \ - [ __NR_setgid32 ] = sys_setgid, \ - [ __NR_setfsuid32 ] = sys_setfsuid, \ - [ __NR_setfsgid32 ] = sys_setfsgid, \ - [ __NR_pivot_root ] = sys_pivot_root, \ - [ __NR_mincore ] = sys_mincore, \ - [ __NR_madvise ] = sys_madvise, \ - [ 222 ] = sys_ni_syscall, + [ __NR_mmap ] = (syscall_handler_t *) old_mmap_i386, \ + [ __NR_select ] = (syscall_handler_t *) old_select, \ + [ __NR_vm86old ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_modify_ldt ] = (syscall_handler_t *) sys_modify_ldt, \ + [ __NR_lchown32 ] = (syscall_handler_t *) sys_lchown, \ + [ __NR_getuid32 ] = (syscall_handler_t *) sys_getuid, \ + [ __NR_getgid32 ] = (syscall_handler_t *) sys_getgid, \ + [ __NR_geteuid32 ] = (syscall_handler_t *) sys_geteuid, \ + [ __NR_getegid32 ] = (syscall_handler_t *) sys_getegid, \ + [ __NR_setreuid32 ] = (syscall_handler_t *) sys_setreuid, \ + [ __NR_setregid32 ] = (syscall_handler_t *) sys_setregid, \ + [ __NR_getgroups32 ] = (syscall_handler_t *) sys_getgroups, \ + [ __NR_setgroups32 ] = (syscall_handler_t *) sys_setgroups, \ + [ __NR_fchown32 ] = (syscall_handler_t *) sys_fchown, \ + [ __NR_setresuid32 ] = (syscall_handler_t *) sys_setresuid, \ + [ __NR_getresuid32 ] = (syscall_handler_t *) sys_getresuid, \ + [ __NR_setresgid32 ] = (syscall_handler_t *) sys_setresgid, \ + [ __NR_getresgid32 ] = (syscall_handler_t *) sys_getresgid, \ + [ __NR_chown32 ] = (syscall_handler_t *) sys_chown, \ + [ __NR_setuid32 ] = (syscall_handler_t *) sys_setuid, \ + [ __NR_setgid32 ] = (syscall_handler_t *) sys_setgid, \ + [ __NR_setfsuid32 ] = (syscall_handler_t *) sys_setfsuid, \ + [ __NR_setfsgid32 ] = (syscall_handler_t *) sys_setfsgid, \ + [ __NR_pivot_root ] = (syscall_handler_t *) sys_pivot_root, \ + [ __NR_mincore ] = (syscall_handler_t *) sys_mincore, \ + [ __NR_madvise ] = (syscall_handler_t *) sys_madvise, \ + [ 222 ] = (syscall_handler_t *) sys_ni_syscall, /* 222 doesn't yet have a name in include/asm-i386/unistd.h */ --- linux-2.6.9-rc1/arch/um/include/ubd_user.h 2004-08-24 00:02:48.000000000 -0700 +++ 25/arch/um/include/ubd_user.h 2004-09-02 20:51:51.000000000 -0700 @@ -9,7 +9,7 @@ #include "os.h" -enum ubd_req { UBD_READ, UBD_WRITE }; +enum ubd_req { UBD_READ, UBD_WRITE, UBD_MMAP }; struct io_thread_req { enum ubd_req op; @@ -20,8 +20,10 @@ struct io_thread_req { char *buffer; int sectorsize; unsigned long sector_mask; - unsigned long cow_offset; + unsigned long long cow_offset; unsigned long bitmap_words[2]; + int map_fd; + unsigned long long map_offset; int error; }; @@ -31,7 +33,7 @@ extern int open_ubd_file(char *file, str int *create_cow_out); extern int create_cow_file(char *cow_file, char *backing_file, struct openflags flags, int sectorsize, - int *bitmap_offset_out, + int alignment, int *bitmap_offset_out, unsigned long *bitmap_len_out, int *data_offset_out); extern int read_cow_bitmap(int fd, void *buf, int offset, int len); @@ -39,7 +41,6 @@ extern int read_ubd_fs(int fd, void *buf extern int write_ubd_fs(int fd, char *buffer, int len); extern int start_io_thread(unsigned long sp, int *fds_out); extern void do_io(struct io_thread_req *req); -extern int ubd_is_dir(char *file); static inline int ubd_test_bit(__u64 bit, unsigned char *data) { --- linux-2.6.9-rc1/arch/um/include/um_uaccess.h 2004-08-24 00:03:18.000000000 -0700 +++ 25/arch/um/include/um_uaccess.h 2004-09-02 20:51:51.000000000 -0700 @@ -38,22 +38,73 @@ static inline int copy_to_user(void *to, from, n)); } +/* + * strncpy_from_user: - Copy a NUL terminated string from userspace. + * @dst: Destination address, in kernel space. This buffer must be at + * least @count bytes long. + * @src: Source address, in user space. + * @count: Maximum number of bytes to copy, including the trailing NUL. + * + * Copies a NUL-terminated string from userspace to kernel space. + * + * On success, returns the length of the string (not including the trailing + * NUL). + * + * If access to userspace fails, returns -EFAULT (some data may have been + * copied). + * + * If @count is smaller than the length of the string, copies @count bytes + * and returns @count. + */ + static inline int strncpy_from_user(char *dst, const char *src, int count) { return(CHOOSE_MODE_PROC(strncpy_from_user_tt, strncpy_from_user_skas, dst, src, count)); } +/* + * __clear_user: - Zero a block of memory in user space, with less checking. + * @to: Destination address, in user space. + * @n: Number of bytes to zero. + * + * Zero a block of memory in user space. Caller must check + * the specified block with access_ok() before calling this function. + * + * Returns number of bytes that could not be cleared. + * On success, this will be zero. + */ static inline int __clear_user(void *mem, int len) { return(CHOOSE_MODE_PROC(__clear_user_tt, __clear_user_skas, mem, len)); } +/* + * clear_user: - Zero a block of memory in user space. + * @to: Destination address, in user space. + * @n: Number of bytes to zero. + * + * Zero a block of memory in user space. + * + * Returns number of bytes that could not be cleared. + * On success, this will be zero. + */ static inline int clear_user(void *mem, int len) { return(CHOOSE_MODE_PROC(clear_user_tt, clear_user_skas, mem, len)); } +/* + * strlen_user: - Get the size of a string in user space. + * @str: The string to measure. + * @n: The maximum valid length + * + * Get the size of a NUL-terminated string in user space. + * + * Returns the size of the string INCLUDING the terminating NUL. + * On exception, returns 0. + * If the string is too long, returns a value greater than @n. + */ static inline int strnlen_user(const void *str, int len) { return(CHOOSE_MODE_PROC(strnlen_user_tt, strnlen_user_skas, str, len)); --- linux-2.6.9-rc1/arch/um/include/user.h 2004-08-24 00:01:52.000000000 -0700 +++ 25/arch/um/include/user.h 2004-09-02 20:51:51.000000000 -0700 @@ -14,6 +14,9 @@ extern void *um_kmalloc_atomic(int size) extern void kfree(void *ptr); extern int in_aton(char *str); extern int open_gdb_chan(void); +extern int strlcpy(char *, const char *, int); +extern void *um_vmalloc(int size); +extern void vfree(void *ptr); #endif --- linux-2.6.9-rc1/arch/um/include/user_util.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/um/include/user_util.h 2004-09-02 20:51:51.000000000 -0700 @@ -14,8 +14,6 @@ extern int grantpt(int __fd); extern int unlockpt(int __fd); extern char *ptsname(int __fd); -enum { OP_NONE, OP_EXEC, OP_FORK, OP_TRACE_ON, OP_REBOOT, OP_HALT, OP_CB }; - struct cpu_task { int pid; void *task; @@ -59,13 +57,11 @@ extern int wait_for_stop(int pid, int si extern void *add_signal_handler(int sig, void (*handler)(int)); extern int start_fork_tramp(void *arg, unsigned long temp_stack, int clone_flags, int (*tramp)(void *)); -extern int clone_and_wait(int (*fn)(void *), void *arg, void *sp, int flags); extern int linux_main(int argc, char **argv); extern void set_cmdline(char *cmd); extern void input_cb(void (*proc)(void *), void *arg, int arg_len); extern int get_pty(void); extern void *um_kmalloc(int size); -extern int raw(int fd, int complain); extern int switcheroo(int fd, int prot, void *from, void *to, int size); extern void setup_machinename(char *machine_out); extern void setup_hostinfo(void); @@ -86,11 +82,17 @@ extern void check_sigio(void); extern int run_kernel_thread(int (*fn)(void *), void *arg, void **jmp_ptr); extern void write_sigio_workaround(void); extern void arch_check_bugs(void); +extern int cpu_feature(char *what, char *buf, int len); extern int arch_handle_signal(int sig, union uml_pt_regs *regs); extern int arch_fixup(unsigned long address, void *sc_ptr); extern void forward_pending_sigio(int target); extern int can_do_skas(void); - +extern void arch_init_thread(void); + +extern int __raw(int fd, int complain, int now); +#define raw(fd, complain) __raw((fd), (complain), 1) + +#define CATCH_EINTR(expr) while ( ((expr) < 0) && errno == EINTR) #endif /* --- linux-2.6.9-rc1/arch/um/Kconfig 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/um/Kconfig 2004-09-02 20:51:51.000000000 -0700 @@ -61,6 +61,20 @@ config MODE_SKAS config NET bool "Networking support" + help + Unless you really know what you are doing, you should say Y here. + The reason is that some programs need kernel networking support even + when running on a stand-alone machine that isn't connected to any + other computer. If you are upgrading from an older kernel, you + should consider updating your networking tools too because changes + in the kernel and the tools often go hand in hand. The tools are + contained in the package net-tools, the location and version number + of which are given in Documentation/Changes. + + For a general introduction to Linux networking, it is highly + recommended to read the NET-HOWTO, available from + . + source "fs/Kconfig.binfmt" @@ -85,6 +99,19 @@ config HOSTFS If you'd like to be able to work with files stored on the host, say Y or M here; otherwise say N. +config HPPFS + tristate "HoneyPot ProcFS" + help + hppfs (HoneyPot ProcFS) is a filesystem which allows UML /proc + entries to be overridden, removed, or fabricated from the host. + Its purpose is to allow a UML to appear to be a physical machine + by removing or changing anything in /proc which gives away the + identity of a UML. + + See http://user-mode-linux.sf.net/hppfs.html for more information. + + You only need this if you are setting up a UML honeypot. Otherwise, + it is safe to say 'N' here. config MCONSOLE bool "Management console" @@ -164,6 +191,17 @@ config KERNEL_STACK_ORDER be 1 << order pages. The default is OK unless you're running Valgrind on UML, in which case, set this to 3. +config UML_REAL_TIME_CLOCK + bool "Real-time Clock" + default y + help + This option makes UML time deltas match wall clock deltas. This should + normally be enabled. The exception would be if you are debugging with + UML and spend long times with UML stopped at a breakpoint. In this + case, when UML is restarted, it will call the timer enough times to make + up for the time spent at the breakpoint. This could result in a + noticable lag. If this is a problem, then disable this option. + endmenu source "init/Kconfig" --- linux-2.6.9-rc1/arch/um/Kconfig_block 2004-08-24 00:03:17.000000000 -0700 +++ 25/arch/um/Kconfig_block 2004-09-02 20:51:51.000000000 -0700 @@ -29,6 +29,10 @@ config BLK_DEV_UBD_SYNC wise choice too. In all other cases (for example, if you're just playing around with User-Mode Linux) you can choose N. +config BLK_DEV_COW_COMMON + bool + default BLK_DEV_UBD + config BLK_DEV_LOOP tristate "Loopback device support" --- linux-2.6.9-rc1/arch/um/Kconfig_char 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/um/Kconfig_char 2004-09-02 20:51:51.000000000 -0700 @@ -108,11 +108,60 @@ config SSL_CHAN config UNIX98_PTYS bool "Unix98 PTY support" - -config UNIX98_PTY_COUNT - int "Maximum number of Unix98 PTYs in use (0-2048)" - depends on UNIX98_PTYS + ---help--- + A pseudo terminal (PTY) is a software device consisting of two + halves: a master and a slave. The slave device behaves identical to + a physical terminal; the master device is used by a process to + read data from and write data to the slave, thereby emulating a + terminal. Typical programs for the master side are telnet servers + and xterms. + + Linux has traditionally used the BSD-like names /dev/ptyxx for + masters and /dev/ttyxx for slaves of pseudo terminals. This scheme + has a number of problems. The GNU C library glibc 2.1 and later, + however, supports the Unix98 naming standard: in order to acquire a + pseudo terminal, a process opens /dev/ptmx; the number of the pseudo + terminal is then made available to the process and the pseudo + terminal slave can be accessed as /dev/pts/. What was + traditionally /dev/ttyp2 will then be /dev/pts/2, for example. + + All modern Linux systems use the Unix98 ptys. Say Y unless + you're on an embedded system and want to conserve memory. + +config LEGACY_PTYS + bool "Legacy (BSD) PTY support" + default y + ---help--- + A pseudo terminal (PTY) is a software device consisting of two + halves: a master and a slave. The slave device behaves identical to + a physical terminal; the master device is used by a process to + read data from and write data to the slave, thereby emulating a + terminal. Typical programs for the master side are telnet servers + and xterms. + + Linux has traditionally used the BSD-like names /dev/ptyxx + for masters and /dev/ttyxx for slaves of pseudo + terminals. This scheme has a number of problems, including + security. This option enables these legacy devices; on most + systems, it is safe to say N. + + +config LEGACY_PTY_COUNT + int "Maximum number of legacy PTY in use" + depends on LEGACY_PTYS default "256" + ---help--- + The maximum number of legacy PTYs that can be used at any one time. + The default is 256, and should be more than enough. Embedded + systems may want to reduce this to save memory. + + When not in use, each legacy PTY occupies 12 bytes on 32-bit + architectures and 24 bytes on 64-bit architectures. + +#config UNIX98_PTY_COUNT +# int "Maximum number of Unix98 PTYs in use (0-2048)" +# depends on UNIX98_PTYS +# default "256" config WATCHDOG bool "Watchdog Timer Support" --- linux-2.6.9-rc1/arch/um/Kconfig.debug 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/um/Kconfig.debug 2004-09-02 20:51:51.000000000 -0700 @@ -9,6 +9,10 @@ config FRAME_POINTER config PT_PROXY bool "Enable ptrace proxy" depends on XTERM_CHAN && DEBUG_INFO + help + This option enables a debugging interface which allows gdb to debug + the kernel without needing to actually attach to kernel threads. + If you want to do kernel debugging, say Y here; otherwise say N. config GPROF bool "Enable gprof support" --- linux-2.6.9-rc1/arch/um/Kconfig_net 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/um/Kconfig_net 2004-09-02 20:51:51.000000000 -0700 @@ -1,5 +1,5 @@ -menu "Network Devices" +menu "UML Network Devices" depends on NET # UML virtual driver @@ -176,73 +176,5 @@ config UML_NET_SLIRP Startup example: "eth0=slirp,FE:FD:01:02:03:04,/usr/local/bin/slirp" - -# Below are hardware-independent drivers mirrored from -# drivers/net/Config.in. It would be nice if Linux -# had HW independent drivers separated from the other -# but it does not. Until then each non-ISA/PCI arch -# needs to provide it's own menu of network drivers -config DUMMY - tristate "Dummy net driver support" - -config BONDING - tristate "Bonding driver support" - -config EQUALIZER - tristate "EQL (serial line load balancing) support" - -config TUN - tristate "Universal TUN/TAP device driver support" - -config ETHERTAP - tristate "Ethertap network tap (OBSOLETE)" - depends on EXPERIMENTAL && NETLINK - -config PPP - tristate "PPP (point-to-point protocol) support" - -config PPP_MULTILINK - bool "PPP multilink support (EXPERIMENTAL)" - depends on PPP && EXPERIMENTAL - -config PPP_FILTER - bool "PPP filtering" - depends on PPP && FILTER - -config PPP_ASYNC - tristate "PPP support for async serial ports" - depends on PPP - -config PPP_SYNC_TTY - tristate "PPP support for sync tty ports" - depends on PPP - -config PPP_DEFLATE - tristate "PPP Deflate compression" - depends on PPP - -config PPP_BSDCOMP - tristate "PPP BSD-Compress compression" - depends on PPP - -config PPPOE - tristate "PPP over Ethernet (EXPERIMENTAL)" - depends on PPP && EXPERIMENTAL - -config SLIP - tristate "SLIP (serial line) support" - -config SLIP_COMPRESSED - bool "CSLIP compressed headers" - depends on SLIP=y - -config SLIP_SMART - bool "Keepalive and linefill" - depends on SLIP=y - -config SLIP_MODE_SLIP6 - bool "Six bit SLIP encapsulation" - depends on SLIP=y - endmenu --- linux-2.6.9-rc1/arch/um/kernel/config.c.in 2004-08-24 00:03:19.000000000 -0700 +++ 25/arch/um/kernel/config.c.in 2004-09-02 20:51:51.000000000 -0700 @@ -7,9 +7,7 @@ #include #include "init.h" -static __initdata char *config = " -CONFIG -"; +static __initdata char *config = "CONFIG"; static int __init print_config(char *line, int *add) { --- linux-2.6.9-rc1/arch/um/kernel/exec_kern.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/um/kernel/exec_kern.c 2004-09-02 20:51:51.000000000 -0700 @@ -32,10 +32,15 @@ void start_thread(struct pt_regs *regs, CHOOSE_MODE_PROC(start_thread_tt, start_thread_skas, regs, eip, esp); } +extern void log_exec(char **argv, void *tty); + static int execve1(char *file, char **argv, char **env) { int error; +#ifdef CONFIG_TTY_LOG + log_exec(argv, current->tty); +#endif error = do_execve(file, argv, env, ¤t->thread.regs); if (error == 0){ current->ptrace &= ~PT_DTRACE; --- linux-2.6.9-rc1/arch/um/kernel/frame.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/arch/um/kernel/frame.c 2004-09-02 20:51:51.000000000 -0700 @@ -21,6 +21,7 @@ #include "sysdep/sigcontext.h" #include "frame_user.h" #include "kern_util.h" +#include "user_util.h" #include "ptrace_user.h" #include "os.h" @@ -40,7 +41,7 @@ static int capture_stack(int (*child)(vo /* Wait for it to stop itself and continue it with a SIGUSR1 to force * it into the signal handler. */ - n = waitpid(pid, &status, WUNTRACED); + CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); if(n < 0){ printf("capture_stack : waitpid failed - errno = %d\n", errno); exit(1); @@ -60,7 +61,7 @@ static int capture_stack(int (*child)(vo * At this point, the handler has stuffed the addresses of * sig, sc, and SA_RESTORER in raw. */ - n = waitpid(pid, &status, WUNTRACED); + CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); if(n < 0){ printf("capture_stack : waitpid failed - errno = %d\n", errno); exit(1); @@ -82,7 +83,8 @@ static int capture_stack(int (*child)(vo errno); exit(1); } - if(waitpid(pid, &status, 0) < 0){ + CATCH_EINTR(n = waitpid(pid, &status, 0)); + if(n < 0){ printf("capture_stack : waitpid failed - errno = %d\n", errno); exit(1); } @@ -279,7 +281,7 @@ void capture_signal_stack(void) struct sc_frame_raw raw_sc; struct si_frame_raw raw_si; void *stack, *sigstack; - unsigned long top, sig_top, base; + unsigned long top, base; stack = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); @@ -292,7 +294,6 @@ void capture_signal_stack(void) } top = (unsigned long) stack + PAGE_SIZE - sizeof(void *); - sig_top = (unsigned long) sigstack + PAGE_SIZE; /* Get the sigcontext, no sigrestorer layout */ raw_sc.restorer = 0; --- linux-2.6.9-rc1/arch/um/kernel/frame_kern.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/arch/um/kernel/frame_kern.c 2004-09-02 20:51:51.000000000 -0700 @@ -6,7 +6,6 @@ #include "asm/ptrace.h" #include "asm/uaccess.h" #include "asm/signal.h" -#include "asm/uaccess.h" #include "asm/ucontext.h" #include "frame_kern.h" #include "sigcontext.h" @@ -29,12 +28,15 @@ static int copy_restorer(void (*restorer sizeof(restorer))); } +extern int userspace_pid[]; + static int copy_sc_to_user(void *to, void *fp, struct pt_regs *from, struct arch_frame_data *arch) { return(CHOOSE_MODE(copy_sc_to_user_tt(to, fp, UPT_SC(&from->regs), arch), - copy_sc_to_user_skas(to, fp, &from->regs, + copy_sc_to_user_skas(userspace_pid[0], to, fp, + &from->regs, current->thread.cr2, current->thread.err))); } --- linux-2.6.9-rc1/arch/um/kernel/helper.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/um/kernel/helper.c 2004-09-02 20:51:51.000000000 -0700 @@ -7,12 +7,12 @@ #include #include #include -#include #include #include #include #include "user.h" #include "kern_util.h" +#include "user_util.h" #include "os.h" struct helper_data { @@ -33,6 +33,7 @@ static int helper_child(void *arg) { struct helper_data *data = arg; char **argv = data->argv; + int errval; if(helper_pause){ signal(SIGHUP, helper_hup); @@ -41,8 +42,9 @@ static int helper_child(void *arg) if(data->pre_exec != NULL) (*data->pre_exec)(data->pre_data); execvp(argv[0], argv); + errval = errno; printk("execvp of '%s' failed - errno = %d\n", argv[0], errno); - write(data->fd, &errno, sizeof(errno)); + os_write_file(data->fd, &errval, sizeof(errval)); os_kill_process(os_getpid(), 0); return(0); } @@ -59,17 +61,20 @@ int run_helper(void (*pre_exec)(void *), if((stack_out != NULL) && (*stack_out != 0)) stack = *stack_out; else stack = alloc_stack(0, um_in_interrupt()); - if(stack == 0) return(-ENOMEM); + if(stack == 0) + return(-ENOMEM); err = os_pipe(fds, 1, 0); - if(err){ - printk("run_helper : pipe failed, errno = %d\n", -err); - return(err); + if(err < 0){ + printk("run_helper : pipe failed, err = %d\n", -err); + goto out_free; } - if(fcntl(fds[1], F_SETFD, 1) != 0){ - printk("run_helper : setting FD_CLOEXEC failed, errno = %d\n", - errno); - return(-errno); + + err = os_set_exec_close(fds[1], 1); + if(err < 0){ + printk("run_helper : setting FD_CLOEXEC failed, err = %d\n", + -err); + goto out_close; } sp = stack + page_size() - sizeof(void *); @@ -80,23 +85,34 @@ int run_helper(void (*pre_exec)(void *), pid = clone(helper_child, (void *) sp, CLONE_VM | SIGCHLD, &data); if(pid < 0){ printk("run_helper : clone failed, errno = %d\n", errno); - return(-errno); + err = -errno; + goto out_close; } - close(fds[1]); - n = read(fds[0], &err, sizeof(err)); + + os_close_file(fds[1]); + n = os_read_file(fds[0], &err, sizeof(err)); if(n < 0){ - printk("run_helper : read on pipe failed, errno = %d\n", - errno); - return(-errno); + printk("run_helper : read on pipe failed, err = %d\n", -n); + err = n; + goto out_kill; } else if(n != 0){ - waitpid(pid, NULL, 0); - pid = -err; + CATCH_EINTR(n = waitpid(pid, NULL, 0)); + pid = -errno; } if(stack_out == NULL) free_stack(stack, 0); else *stack_out = stack; return(pid); + + out_kill: + os_kill_process(pid, 1); + out_close: + os_close_file(fds[0]); + os_close_file(fds[1]); + out_free: + free_stack(stack, 0); + return(err); } int run_helper_thread(int (*proc)(void *), void *arg, unsigned int flags, @@ -117,9 +133,11 @@ int run_helper_thread(int (*proc)(void * } if(stack_out == NULL){ pid = waitpid(pid, &status, 0); - if(pid < 0) + if(pid < 0){ printk("run_helper_thread - wait failed, errno = %d\n", - pid); + errno); + pid = -errno; + } if(!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) printk("run_helper_thread - thread returned status " "0x%x\n", status); --- linux-2.6.9-rc1/arch/um/kernel/initrd_user.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/um/kernel/initrd_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -6,7 +6,6 @@ #include #include #include -#include #include #include "user_util.h" @@ -19,13 +18,15 @@ int load_initrd(char *filename, void *bu { int fd, n; - if((fd = os_open_file(filename, of_read(OPENFLAGS()), 0)) < 0){ - printk("Opening '%s' failed - errno = %d\n", filename, errno); + fd = os_open_file(filename, of_read(OPENFLAGS()), 0); + if(fd < 0){ + printk("Opening '%s' failed - err = %d\n", filename, -fd); return(-1); } - if((n = read(fd, buf, size)) != size){ - printk("Read of %d bytes from '%s' returned %d, errno = %d\n", - size, filename, n, errno); + n = os_read_file(fd, buf, size); + if(n != size){ + printk("Read of %d bytes from '%s' failed, err = %d\n", size, + filename, -n); return(-1); } return(0); --- linux-2.6.9-rc1/arch/um/kernel/init_task.c 2004-08-24 00:03:49.000000000 -0700 +++ 25/arch/um/kernel/init_task.c 2004-09-02 20:51:51.000000000 -0700 @@ -8,7 +8,6 @@ #include "linux/module.h" #include "linux/sched.h" #include "linux/init_task.h" -#include "linux/version.h" #include "linux/mqueue.h" #include "asm/uaccess.h" #include "asm/pgtable.h" @@ -19,7 +18,7 @@ static struct fs_struct init_fs = INIT_F struct mm_struct init_mm = INIT_MM(init_mm); static struct files_struct init_files = INIT_FILES; static struct signal_struct init_signals = INIT_SIGNALS(init_signals); - +static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); EXPORT_SYMBOL(init_mm); /* @@ -44,26 +43,12 @@ union thread_union init_thread_union __attribute__((__section__(".data.init_task"))) = { INIT_THREAD_INFO(init_task) }; -struct task_struct *alloc_task_struct(void) -{ - return((struct task_struct *) - __get_free_pages(GFP_KERNEL, CONFIG_KERNEL_STACK_ORDER)); -} - void unprotect_stack(unsigned long stack) { protect_memory(stack, (1 << CONFIG_KERNEL_STACK_ORDER) * PAGE_SIZE, 1, 1, 0, 1); } -void free_task_struct(struct task_struct *task) -{ - /* free_pages decrements the page counter and only actually frees - * the pages if they are now not accessed by anything. - */ - free_pages((unsigned long) task, CONFIG_KERNEL_STACK_ORDER); -} - /* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically --- linux-2.6.9-rc1/arch/um/kernel/irq.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/um/kernel/irq.c 2004-09-03 00:56:41.008926344 -0700 @@ -18,9 +18,10 @@ #include "linux/proc_fs.h" #include "linux/init.h" #include "linux/seq_file.h" +#include "linux/profile.h" +#include "linux/hardirq.h" #include "asm/irq.h" #include "asm/hw_irq.h" -#include "asm/hardirq.h" #include "asm/atomic.h" #include "asm/signal.h" #include "asm/system.h" @@ -29,6 +30,7 @@ #include "user_util.h" #include "kern_util.h" #include "irq_user.h" +#include "irq_kern.h" static void register_irq_proc (unsigned int irq); @@ -83,65 +85,55 @@ struct hw_interrupt_type no_irq_type = { end_none }; -/* Not changed */ -volatile unsigned long irq_err_count; - /* * Generic, controller-independent functions: */ -int get_irq_list(char *buf) +int show_interrupts(struct seq_file *p, void *v) { - int i, j; - unsigned long flags; + int i = *(loff_t *) v, j; struct irqaction * action; - char *p = buf; + unsigned long flags; - p += sprintf(p, " "); - for (j=0; jtypename); - p += sprintf(p, " %s", action->name); + seq_printf(p, " %14s", irq_desc[i].handler->typename); + seq_printf(p, " %s", action->name); for (action=action->next; action; action = action->next) - p += sprintf(p, ", %s", action->name); - *p++ = '\n'; - end: + seq_printf(p, ", %s", action->name); + + seq_putc(p, '\n'); +skip: spin_unlock_irqrestore(&irq_desc[i].lock, flags); + } else if (i == NR_IRQS) { + seq_printf(p, "NMI: "); + for (j = 0; j < NR_CPUS; j++) + if (cpu_online(j)) + seq_printf(p, "%10u ", nmi_count(j)); + seq_putc(p, '\n'); } - p += sprintf(p, "\n"); -#ifdef notdef -#ifdef CONFIG_SMP - p += sprintf(p, "LOC: "); - for (j = 0; j < num_online_cpus(); j++) - p += sprintf(p, "%10u ", - apic_timer_irqs[cpu_logical_map(j)]); - p += sprintf(p, "\n"); -#endif -#endif - p += sprintf(p, "ERR: %10lu\n", irq_err_count); - return p - buf; -} - -int show_interrupts(struct seq_file *p, void *v) -{ - return(0); + return 0; } /* @@ -155,13 +147,15 @@ int handle_IRQ_event(unsigned int irq, s struct irqaction * action) { int status = 1; /* Force the "do bottom halves" bit */ + int ret; if (!(action->flags & SA_INTERRUPT)) local_irq_enable(); do { - status |= action->flags; - action->handler(irq, action->dev_id, regs); + ret = action->handler(irq, action->dev_id, regs); + if (ret == IRQ_HANDLED) + status |= action->flags; action = action->next; } while (action); if (status & SA_SAMPLE_RANDOM) @@ -282,13 +276,12 @@ unsigned int do_IRQ(int irq, union uml_p * 0 return value means that this irq is already being * handled by some other CPU. (or is disabled) */ - int cpu = smp_processor_id(); irq_desc_t *desc = irq_desc + irq; struct irqaction * action; unsigned int status; irq_enter(); - kstat_cpu(cpu).irqs[irq]++; + kstat_this_cpu.irqs[irq]++; spin_lock(&desc->lock); desc->handler->ack(irq); /* @@ -385,7 +378,7 @@ out: */ int request_irq(unsigned int irq, - void (*handler)(int, void *, struct pt_regs *), + irqreturn_t (*handler)(int, void *, struct pt_regs *), unsigned long irqflags, const char * devname, void *dev_id) @@ -433,15 +426,19 @@ int request_irq(unsigned int irq, EXPORT_SYMBOL(request_irq); int um_request_irq(unsigned int irq, int fd, int type, - void (*handler)(int, void *, struct pt_regs *), + irqreturn_t (*handler)(int, void *, struct pt_regs *), unsigned long irqflags, const char * devname, void *dev_id) { - int retval; + int err; - retval = request_irq(irq, handler, irqflags, devname, dev_id); - if(retval) return(retval); - return(activate_fd(irq, fd, type, dev_id)); + err = request_irq(irq, handler, irqflags, devname, dev_id); + if(err) + return(err); + + if(fd != -1) + err = activate_fd(irq, fd, type, dev_id); + return(err); } /* this was setup_x86_irq but it seems pretty generic */ @@ -474,7 +471,8 @@ int setup_irq(unsigned int irq, struct i */ spin_lock_irqsave(&desc->lock,flags); p = &desc->action; - if ((old = *p) != NULL) { + old = *p; + if (old != NULL) { /* Can't share interrupts unless both agree to */ if (!(old->flags & new->flags & SA_SHIRQ)) { spin_unlock_irqrestore(&desc->lock,flags); @@ -586,12 +584,14 @@ static int irq_affinity_write_proc (stru unsigned long count, void *data) { int irq = (long) data, full_count = count, err; - cpumask_t new_value, tmp; + cpumask_t new_value; if (!irq_desc[irq].handler->set_affinity) return -EIO; err = cpumask_parse(buffer, count, new_value); + if(err) + return(err); #ifdef CONFIG_SMP /* @@ -599,9 +599,11 @@ static int irq_affinity_write_proc (stru * way to make the system unusable accidentally :-) At least * one online CPU still has to be targeted. */ - cpus_and(tmp, new_value, cpu_online_map); - if (cpus_empty(tmp)) - return -EINVAL; + { cpumask_t tmp; + cpus_and(tmp, new_value, cpu_online_map); + if (cpus_empty(tmp)) + return -EINVAL; + } #endif irq_affinity[irq] = new_value; @@ -610,30 +612,6 @@ static int irq_affinity_write_proc (stru return full_count; } -static int prof_cpu_mask_read_proc (char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - int len = cpumask_scnprintf(page, count, *(cpumask_t *)data); - if (count - len < 2) - return -EINVAL; - len += sprintf(page + len, "\n"); - return len; -} - -static int prof_cpu_mask_write_proc (struct file *file, const char *buffer, - unsigned long count, void *data) -{ - cpumask_t *mask = (cpumask_t *)data, new_value; - unsigned long full_count = count, err; - - err = cpumask_parse(buffer, count, new_value); - if (err) - return err; - - *mask = new_value; - return full_count; -} - #define MAX_NAMELEN 10 static void register_irq_proc (unsigned int irq) @@ -662,24 +640,15 @@ static void register_irq_proc (unsigned smp_affinity_entry[irq] = entry; } -/* Read and written as a long */ -cpumask_t prof_cpu_mask = CPU_MASK_ALL; - void __init init_irq_proc (void) { - struct proc_dir_entry *entry; int i; /* create /proc/irq */ root_irq_dir = proc_mkdir("irq", 0); /* create /proc/irq/prof_cpu_mask */ - entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); - - entry->nlink = 1; - entry->data = (void *)&prof_cpu_mask; - entry->read_proc = prof_cpu_mask_read_proc; - entry->write_proc = prof_cpu_mask_write_proc; + create_prof_cpu_mask(root_irq_dir); /* * Create entries for all existing IRQs. --- linux-2.6.9-rc1/arch/um/kernel/irq_user.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/um/kernel/irq_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -49,7 +48,8 @@ void sigio_handler(int sig, union uml_pt if(smp_sigio_handler()) return; while(1){ - if((n = poll(pollfds, pollfds_num, 0)) < 0){ + n = poll(pollfds, pollfds_num, 0); + if(n < 0){ if(errno == EINTR) continue; printk("sigio_handler : poll returned %d, " "errno = %d\n", n, errno); @@ -366,34 +366,31 @@ void deactivate_fd(int fd, int irqnum) void forward_ipi(int fd, int pid) { - if(fcntl(fd, F_SETOWN, pid) < 0){ - int save_errno = errno; - if(fcntl(fd, F_GETOWN, 0) != pid){ - printk("forward_ipi: F_SETOWN failed, fd = %d, " - "me = %d, target = %d, errno = %d\n", fd, - os_getpid(), pid, save_errno); - } - } + int err; + + err = os_set_owner(fd, pid); + if(err < 0) + printk("forward_ipi: set_owner failed, fd = %d, me = %d, " + "target = %d, err = %d\n", fd, os_getpid(), pid, -err); } void forward_interrupts(int pid) { struct irq_fd *irq; unsigned long flags; + int err; flags = irq_lock(); for(irq=active_fds;irq != NULL;irq = irq->next){ - if(fcntl(irq->fd, F_SETOWN, pid) < 0){ - int save_errno = errno; - if(fcntl(irq->fd, F_GETOWN, 0) != pid){ - /* XXX Just remove the irq rather than - * print out an infinite stream of these - */ - printk("Failed to forward %d to pid %d, " - "errno = %d\n", irq->fd, pid, - save_errno); - } + err = os_set_owner(irq->fd, pid); + if(err < 0){ + /* XXX Just remove the irq rather than + * print out an infinite stream of these + */ + printk("Failed to forward %d to pid %d, err = %d\n", + irq->fd, pid, -err); } + irq->pid = pid; } irq_unlock(flags); --- linux-2.6.9-rc1/arch/um/kernel/ksyms.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/arch/um/kernel/ksyms.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2001 - 2004 Jeff Dike (jdike@addtoit.com) * Licensed under the GPL */ @@ -8,7 +8,7 @@ #include "linux/string.h" #include "linux/smp_lock.h" #include "linux/spinlock.h" -#include +#include "linux/highmem.h" #include "asm/current.h" #include "asm/delay.h" #include "asm/processor.h" @@ -19,6 +19,7 @@ #include "asm/tlbflush.h" #include "kern_util.h" #include "user_util.h" +#include "mem_user.h" #include "os.h" #include "helper.h" @@ -34,34 +35,66 @@ EXPORT_SYMBOL(task_size); EXPORT_SYMBOL(flush_tlb_range); EXPORT_SYMBOL(host_task_size); EXPORT_SYMBOL(arch_validate); +EXPORT_SYMBOL(get_kmem_end); -EXPORT_SYMBOL(region_pa); -EXPORT_SYMBOL(region_va); -EXPORT_SYMBOL(phys_mem_map); -EXPORT_SYMBOL(page_mem_map); EXPORT_SYMBOL(page_to_phys); EXPORT_SYMBOL(phys_to_page); EXPORT_SYMBOL(high_physmem); EXPORT_SYMBOL(empty_zero_page); EXPORT_SYMBOL(um_virt_to_phys); +EXPORT_SYMBOL(__virt_to_page); +EXPORT_SYMBOL(to_phys); +EXPORT_SYMBOL(to_virt); EXPORT_SYMBOL(mode_tt); EXPORT_SYMBOL(handle_page_fault); +EXPORT_SYMBOL(find_iomem); +#ifdef CONFIG_MODE_TT +EXPORT_SYMBOL(strncpy_from_user_tt); +EXPORT_SYMBOL(copy_from_user_tt); +EXPORT_SYMBOL(copy_to_user_tt); +#endif + +#ifdef CONFIG_MODE_SKAS +EXPORT_SYMBOL(strncpy_from_user_skas); +EXPORT_SYMBOL(copy_to_user_skas); +EXPORT_SYMBOL(copy_from_user_skas); +#endif + +EXPORT_SYMBOL(os_stat_fd); +EXPORT_SYMBOL(os_stat_file); +EXPORT_SYMBOL(os_access); +EXPORT_SYMBOL(os_print_error); +EXPORT_SYMBOL(os_get_exec_close); +EXPORT_SYMBOL(os_set_exec_close); EXPORT_SYMBOL(os_getpid); EXPORT_SYMBOL(os_open_file); EXPORT_SYMBOL(os_read_file); EXPORT_SYMBOL(os_write_file); EXPORT_SYMBOL(os_seek_file); +EXPORT_SYMBOL(os_lock_file); +EXPORT_SYMBOL(os_ioctl_generic); EXPORT_SYMBOL(os_pipe); EXPORT_SYMBOL(os_file_type); +EXPORT_SYMBOL(os_file_mode); +EXPORT_SYMBOL(os_file_size); +EXPORT_SYMBOL(os_flush_stdout); EXPORT_SYMBOL(os_close_file); +EXPORT_SYMBOL(os_set_fd_async); +EXPORT_SYMBOL(os_set_fd_block); EXPORT_SYMBOL(helper_wait); EXPORT_SYMBOL(os_shutdown_socket); +EXPORT_SYMBOL(os_create_unix_socket); EXPORT_SYMBOL(os_connect_socket); +EXPORT_SYMBOL(os_accept_connection); +EXPORT_SYMBOL(os_rcv_fd); EXPORT_SYMBOL(run_helper); EXPORT_SYMBOL(start_thread); EXPORT_SYMBOL(dump_thread); +EXPORT_SYMBOL(do_gettimeofday); +EXPORT_SYMBOL(do_settimeofday); + /* This is here because UML expands open to sys_open, not to a system * call instruction. */ @@ -90,3 +123,13 @@ EXPORT_SYMBOL(kunmap_atomic); EXPORT_SYMBOL(kmap_atomic_to_page); #endif +/* + * 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: + */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/um/kernel/main.c 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "user_util.h" +#include "kern_util.h" +#include "mem_user.h" +#include "signal_user.h" +#include "user.h" +#include "init.h" +#include "mode.h" +#include "choose-mode.h" +#include "uml-config.h" + +/* Set in set_stklim, which is called from main and __wrap_malloc. + * __wrap_malloc only calls it if main hasn't started. + */ +unsigned long stacksizelim; + +/* Set in main */ +char *linux_prog; + +#define PGD_BOUND (4 * 1024 * 1024) +#define STACKSIZE (8 * 1024 * 1024) +#define THREAD_NAME_LEN (256) + +static void set_stklim(void) +{ + struct rlimit lim; + + if(getrlimit(RLIMIT_STACK, &lim) < 0){ + perror("getrlimit"); + exit(1); + } + if((lim.rlim_cur == RLIM_INFINITY) || (lim.rlim_cur > STACKSIZE)){ + lim.rlim_cur = STACKSIZE; + if(setrlimit(RLIMIT_STACK, &lim) < 0){ + perror("setrlimit"); + exit(1); + } + } + stacksizelim = (lim.rlim_cur + PGD_BOUND - 1) & ~(PGD_BOUND - 1); +} + +static __init void do_uml_initcalls(void) +{ + initcall_t *call; + + call = &__uml_initcall_start; + while (call < &__uml_initcall_end){; + (*call)(); + call++; + } +} + +static void last_ditch_exit(int sig) +{ + CHOOSE_MODE(kmalloc_ok = 0, (void) 0); + signal(SIGINT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + signal(SIGHUP, SIG_DFL); + uml_cleanup(); + exit(1); +} + +extern int uml_exitcode; + +int main(int argc, char **argv, char **envp) +{ + char **new_argv; + sigset_t mask; + int ret, i; + + /* Enable all signals except SIGIO - in some environments, we can + * enter with some signals blocked + */ + + sigemptyset(&mask); + sigaddset(&mask, SIGIO); + if(sigprocmask(SIG_SETMASK, &mask, NULL) < 0){ + perror("sigprocmask"); + exit(1); + } + +#ifdef UML_CONFIG_MODE_TT + /* Allocate memory for thread command lines */ + if(argc < 2 || strlen(argv[1]) < THREAD_NAME_LEN - 1){ + + char padding[THREAD_NAME_LEN] = { + [ 0 ... THREAD_NAME_LEN - 2] = ' ', '\0' + }; + + new_argv = malloc((argc + 2) * sizeof(char*)); + if(!new_argv) { + perror("Allocating extended argv"); + exit(1); + } + + new_argv[0] = argv[0]; + new_argv[1] = padding; + + for(i = 2; i <= argc; i++) + new_argv[i] = argv[i - 1]; + new_argv[argc + 1] = NULL; + + execvp(new_argv[0], new_argv); + perror("execing with extended args"); + exit(1); + } +#endif + + linux_prog = argv[0]; + + set_stklim(); + + new_argv = malloc((argc + 1) * sizeof(char *)); + if(new_argv == NULL){ + perror("Mallocing argv"); + exit(1); + } + for(i=0;i= uml_physmem) && (addr <= high_physmem)) + kfree(ptr); + else if((addr >= start_vm) && (addr <= end_vm)) + vfree(ptr); + else + __real_free(ptr); + } + else __real_free(ptr); +} + +/* + * 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: + */ --- linux-2.6.9-rc1/arch/um/kernel/Makefile 2004-08-24 00:03:17.000000000 -0700 +++ 25/arch/um/kernel/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -3,15 +3,15 @@ # Licensed under the GPL # -extra-y := vmlinux.lds +extra-y := vmlinux.lds uml.lds obj-y = checksum.o config.o exec_kern.o exitcode.o frame_kern.o frame.o \ - helper.o init_task.o irq.o irq_user.o ksyms.o mem.o mem_user.o \ - process.o process_kern.o ptrace.o reboot.o resource.o sigio_user.o \ - sigio_kern.o signal_kern.o signal_user.o smp.o syscall_kern.o \ - syscall_user.o sysrq.o sys_call_table.o tempfile.o time.o \ - time_kern.o tlb.o trap_kern.o trap_user.o uaccess_user.o um_arch.o \ - umid.o user_syms.o user_util.o + helper.o init_task.o irq.o irq_user.o ksyms.o main.o mem.o mem_user.o \ + physmem.o process.o process_kern.o ptrace.o reboot.o resource.o \ + sigio_user.o sigio_kern.o signal_kern.o signal_user.o smp.o \ + syscall_kern.o syscall_user.o sysrq.o sys_call_table.o tempfile.o \ + time.o time_kern.o tlb.o trap_kern.o trap_user.o uaccess_user.o \ + um_arch.o umid.o user_util.o obj-$(CONFIG_BLK_DEV_INITRD) += initrd_kern.o initrd_user.o obj-$(CONFIG_GPROF) += gprof_syms.o @@ -24,43 +24,27 @@ obj-$(CONFIG_MODE_SKAS) += skas/ user-objs-$(CONFIG_TTY_LOG) += tty_log.o USER_OBJS := $(filter %_user.o,$(obj-y)) $(user-objs-y) config.o helper.o \ - process.o tempfile.o time.o tty_log.o umid.o user_util.o user_syms.o + main.o process.o tempfile.o time.o tty_log.o umid.o user_util.o USER_OBJS := $(foreach file,$(USER_OBJS),$(obj)/$(file)) -DMODULES-$(CONFIG_MODULES) = -D__CONFIG_MODULES__ -DMODVERSIONS-$(CONFIG_MODVERSIONS) = -D__CONFIG_MODVERSIONS__ - - -CFLAGS_user_syms.o = -D__AUTOCONF_INCLUDED__ $(DMODULES-y) $(DMODVERSIONS-y) \ - -I/usr/include -I../include - CFLAGS_frame.o := $(patsubst -fomit-frame-pointer,,$(USER_CFLAGS)) -$(USER_OBJS) : %.o: %.c - $(CC) $(CFLAGS_$(notdir $@)) $(USER_CFLAGS) -c -o $@ $< - # This has to be separate because it needs be compiled with frame pointers # regardless of how the rest of the kernel is built. $(obj)/frame.o: $(src)/frame.c $(CC) $(CFLAGS_$(notdir $@)) -c -o $@ $< -QUOTE = 'my $$config=`cat $(TOPDIR)/.config`; $$config =~ s/"/\\"/g ; while() { $$_ =~ s/CONFIG/$$config/; print $$_ }' +$(USER_OBJS) : %.o: %.c + $(CC) $(CFLAGS_$(notdir $@)) $(USER_CFLAGS) -c -o $@ $< -$(obj)/config.c : $(src)/config.c.in $(TOPDIR)/.config - $(PERL) -e $(QUOTE) < $(src)/config.c.in > $@ +QUOTE = 'my $$config=`cat $(TOPDIR)/.config`; $$config =~ s/"/\\"/g ; $$config =~ s/\n/\\n"\n"/g ; while() { $$_ =~ s/CONFIG/$$config/; print $$_ }' $(obj)/config.o : $(obj)/config.c -clean: - rm -f config.c - for dir in $(subdir-y) ; do $(MAKE) -C $$dir clean; done - -modules: - -fastdep: - -dep: - -archmrproper: clean +quiet_cmd_quote = QUOTE $@ +cmd_quote = $(PERL) -e $(QUOTE) < $< > $@ +targets += config.c +$(obj)/config.c : $(src)/config.c.in $(TOPDIR)/.config FORCE + $(call if_changed,quote) --- linux-2.6.9-rc1/arch/um/kernel/mem.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/um/kernel/mem.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,74 +1,66 @@ /* - * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) * Licensed under the GPL */ -#include "linux/config.h" -#include "linux/module.h" -#include "linux/types.h" +#include "linux/stddef.h" +#include "linux/kernel.h" #include "linux/mm.h" -#include "linux/fs.h" -#include "linux/init.h" #include "linux/bootmem.h" #include "linux/swap.h" -#include "linux/slab.h" -#include "linux/vmalloc.h" #include "linux/highmem.h" +#include "linux/gfp.h" #include "asm/page.h" -#include "asm/pgtable.h" +#include "asm/fixmap.h" #include "asm/pgalloc.h" -#include "asm/bitops.h" -#include "asm/uaccess.h" -#include "asm/tlb.h" #include "user_util.h" #include "kern_util.h" -#include "mem_user.h" -#include "mem.h" #include "kern.h" -#include "init.h" -#include "os.h" -#include "mode_kern.h" +#include "mem_user.h" #include "uml_uaccess.h" +#include "os.h" + +extern char __binary_start; /* Changed during early boot */ -pgd_t swapper_pg_dir[1024]; -unsigned long high_physmem; -unsigned long vm_start; -unsigned long vm_end; -unsigned long highmem; unsigned long *empty_zero_page = NULL; unsigned long *empty_bad_page = NULL; - -/* Not modified */ -const char bad_pmd_string[] = "Bad pmd in pte_alloc: %08lx\n"; - -extern char __init_begin, __init_end; -extern long physmem_size; - -/* Not changed by UML */ -DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); - -/* Changed during early boot */ +pgd_t swapper_pg_dir[1024]; +unsigned long highmem; int kmalloc_ok = 0; -#define NREGIONS (phys_region_index(0xffffffff) - phys_region_index(0x0) + 1) -struct mem_region *regions[NREGIONS] = { [ 0 ... NREGIONS - 1 ] = NULL }; -#define REGION_SIZE ((0xffffffff & ~REGION_MASK) + 1) - -/* Changed during early boot */ static unsigned long brk_end; +void unmap_physmem(void) +{ + os_unmap_memory((void *) brk_end, uml_reserved - brk_end); +} + static void map_cb(void *unused) { map_memory(brk_end, __pa(brk_end), uml_reserved - brk_end, 1, 1, 0); } -void unmap_physmem(void) +#ifdef CONFIG_HIGHMEM +static void setup_highmem(unsigned long highmem_start, + unsigned long highmem_len) { - os_unmap_memory((void *) brk_end, uml_reserved - brk_end); -} + struct page *page; + unsigned long highmem_pfn; + int i; -extern char __binary_start; + highmem_start_page = virt_to_page(highmem_start); + + highmem_pfn = __pa(highmem_start) >> PAGE_SHIFT; + for(i = 0; i < highmem_len >> PAGE_SHIFT; i++){ + page = &mem_map[highmem_pfn + i]; + ClearPageReserved(page); + set_bit(PG_highmem, &page->flags); + set_page_count(page, 1); + __free_page(page); + } +} +#endif void mem_init(void) { @@ -103,50 +95,15 @@ void mem_init(void) totalhigh_pages = highmem >> PAGE_SHIFT; totalram_pages += totalhigh_pages; num_physpages = totalram_pages; - max_mapnr = totalram_pages; max_pfn = totalram_pages; printk(KERN_INFO "Memory: %luk available\n", (unsigned long) nr_free_pages() << (PAGE_SHIFT-10)); kmalloc_ok = 1; -} - -/* Changed during early boot */ -static unsigned long kmem_top = 0; - -unsigned long get_kmem_end(void) -{ - if(kmem_top == 0) - kmem_top = CHOOSE_MODE(kmem_end_tt, kmem_end_skas); - return(kmem_top); -} - -void set_kmem_end(unsigned long new) -{ - kmem_top = new; -} #ifdef CONFIG_HIGHMEM -/* Changed during early boot */ -pte_t *kmap_pte; -pgprot_t kmap_prot; - -EXPORT_SYMBOL(kmap_prot); -EXPORT_SYMBOL(kmap_pte); - -#define kmap_get_fixmap_pte(vaddr) \ - pte_offset_kernel(pmd_offset(pgd_offset_k(vaddr), (vaddr)), (vaddr)) - -void __init kmap_init(void) -{ - unsigned long kmap_vstart; - - /* cache the first kmap pte */ - kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN); - kmap_pte = kmap_get_fixmap_pte(kmap_vstart); - - kmap_prot = PAGE_KERNEL; + setup_highmem(end_iomem, highmem); +#endif } -#endif /* CONFIG_HIGHMEM */ static void __init fixrange_init(unsigned long start, unsigned long end, pgd_t *pgd_base) @@ -178,76 +135,24 @@ static void __init fixrange_init(unsigne } } -int init_maps(struct mem_region *region) -{ - struct page *p, *map; - int i, n, len; - - if(region == &physmem_region){ - region->mem_map = mem_map; - return(0); - } - else if(region->mem_map != NULL) return(0); - - n = region->len >> PAGE_SHIFT; - len = n * sizeof(struct page); - if(kmalloc_ok){ - map = kmalloc(len, GFP_KERNEL); - if(map == NULL) map = vmalloc(len); - } - else map = alloc_bootmem_low_pages(len); - - if(map == NULL) - return(-ENOMEM); - for(i = 0; i < n; i++){ - p = &map[i]; - set_page_count(p, 0); - SetPageReserved(p); - INIT_LIST_HEAD(&p->list); - } - region->mem_map = map; - return(0); -} +#if CONFIG_HIGHMEM +pte_t *kmap_pte; +pgprot_t kmap_prot; -DECLARE_MUTEX(regions_sem); +#define kmap_get_fixmap_pte(vaddr) \ + pte_offset(pmd_offset(pgd_offset_k(vaddr), (vaddr)), (vaddr)) -static int setup_one_range(int fd, char *driver, unsigned long start, - unsigned long pfn, int len, - struct mem_region *region) +void __init kmap_init(void) { - int i; - - down(®ions_sem); - for(i = 0; i < NREGIONS; i++){ - if(regions[i] == NULL) break; - } - if(i == NREGIONS){ - printk("setup_range : no free regions\n"); - i = -1; - goto out; - } - - if(fd == -1) - fd = create_mem_file(len); + unsigned long kmap_vstart; - if(region == NULL){ - region = alloc_bootmem_low_pages(sizeof(*region)); - if(region == NULL) - panic("Failed to allocating mem_region"); - } + /* cache the first kmap pte */ + kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN); + kmap_pte = kmap_get_fixmap_pte(kmap_vstart); - *region = ((struct mem_region) { .driver = driver, - .start_pfn = pfn, - .start = start, - .len = len, - .fd = fd } ); - regions[i] = region; - out: - up(®ions_sem); - return(i); + kmap_prot = PAGE_KERNEL; } -#ifdef CONFIG_HIGHMEM static void init_highmem(void) { pgd_t *pgd; @@ -268,63 +173,20 @@ static void init_highmem(void) kmap_init(); } - -void setup_highmem(unsigned long len) -{ - struct mem_region *region; - struct page *page, *map; - unsigned long phys; - int i, cur, index; - - phys = physmem_size; - do { - cur = min(len, (unsigned long) REGION_SIZE); - i = setup_one_range(-1, NULL, -1, phys >> PAGE_SHIFT, cur, - NULL); - if(i == -1){ - printk("setup_highmem - setup_one_range failed\n"); - return; - } - region = regions[i]; - index = phys / PAGE_SIZE; - region->mem_map = &mem_map[index]; - - map = region->mem_map; - for(i = 0; i < (cur >> PAGE_SHIFT); i++){ - page = &map[i]; - ClearPageReserved(page); - set_bit(PG_highmem, &page->flags); - set_page_count(page, 1); - __free_page(page); - } - phys += cur; - len -= cur; - } while(len > 0); -} -#endif +#endif /* CONFIG_HIGHMEM */ void paging_init(void) { - struct mem_region *region; - unsigned long zones_size[MAX_NR_ZONES], start, end, vaddr; - int i, index; + unsigned long zones_size[MAX_NR_ZONES], vaddr; + int i; empty_zero_page = (unsigned long *) alloc_bootmem_low_pages(PAGE_SIZE); empty_bad_page = (unsigned long *) alloc_bootmem_low_pages(PAGE_SIZE); for(i=0;i> PAGE_SHIFT) - - (uml_physmem >> PAGE_SHIFT); + zones_size[0] = (end_iomem >> PAGE_SHIFT) - (uml_physmem >> PAGE_SHIFT); zones_size[2] = highmem >> PAGE_SHIFT; free_area_init(zones_size); - start = phys_region_index(__pa(uml_physmem)); - end = phys_region_index(__pa(high_physmem - 1)); - for(i = start; i <= end; i++){ - region = regions[i]; - index = (region->start - uml_physmem) / PAGE_SIZE; - region->mem_map = &mem_map[index]; - if(i > start) free_bootmem(__pa(region->start), region->len); - } /* * Fixed mappings, only the page table structure has to be @@ -335,15 +197,33 @@ void paging_init(void) #ifdef CONFIG_HIGHMEM init_highmem(); - setup_highmem(highmem); #endif } -pte_t __bad_page(void) +struct page *arch_validate(struct page *page, int mask, int order) { - clear_page(empty_bad_page); - return pte_mkdirty(mk_pte((struct page *) empty_bad_page, - PAGE_SHARED)); + unsigned long addr, zero = 0; + int i; + + again: + if(page == NULL) return(page); + if(PageHighMem(page)) return(page); + + addr = (unsigned long) page_address(page); + for(i = 0; i < (1 << order); i++){ + current->thread.fault_addr = (void *) addr; + if(__do_copy_to_user((void *) addr, &zero, + sizeof(zero), + ¤t->thread.fault_addr, + ¤t->thread.fault_catcher)){ + if(!(mask & __GFP_WAIT)) return(NULL); + else break; + } + addr += PAGE_SIZE; + } + if(i == (1 << order)) return(page); + page = alloc_pages(mask, order); + goto again; } /* This can't do anything because nothing in the kernel image can be freed @@ -401,395 +281,6 @@ void show_mem(void) printk("%d pages swap cached\n", cached); } -static int __init uml_mem_setup(char *line, int *add) -{ - char *retptr; - physmem_size = memparse(line,&retptr); - return 0; -} -__uml_setup("mem=", uml_mem_setup, -"mem=\n" -" This controls how much \"physical\" memory the kernel allocates\n" -" for the system. The size is specified as a number followed by\n" -" one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n" -" This is not related to the amount of memory in the physical\n" -" machine. It can be more, and the excess, if it's ever used, will\n" -" just be swapped out.\n Example: mem=64M\n\n" -); - -struct page *arch_validate(struct page *page, int mask, int order) -{ - unsigned long addr, zero = 0; - int i; - - again: - if(page == NULL) return(page); - if(PageHighMem(page)) return(page); - - addr = (unsigned long) page_address(page); - for(i = 0; i < (1 << order); i++){ - current->thread.fault_addr = (void *) addr; - if(__do_copy_to_user((void *) addr, &zero, - sizeof(zero), - ¤t->thread.fault_addr, - ¤t->thread.fault_catcher)){ - if(!(mask & __GFP_WAIT)) return(NULL); - else break; - } - addr += PAGE_SIZE; - } - if(i == (1 << order)) return(page); - page = alloc_pages(mask, order); - goto again; -} - -DECLARE_MUTEX(vm_reserved_sem); -static struct list_head vm_reserved = LIST_HEAD_INIT(vm_reserved); - -/* Static structures, linked in to the list in early boot */ -static struct vm_reserved head = { - .list = LIST_HEAD_INIT(head.list), - .start = 0, - .end = 0xffffffff -}; - -static struct vm_reserved tail = { - .list = LIST_HEAD_INIT(tail.list), - .start = 0, - .end = 0xffffffff -}; - -void set_usable_vm(unsigned long start, unsigned long end) -{ - list_add(&head.list, &vm_reserved); - list_add(&tail.list, &head.list); - head.end = start; - tail.start = end; -} - -int reserve_vm(unsigned long start, unsigned long end, void *e) - -{ - struct vm_reserved *entry = e, *reserved, *prev; - struct list_head *ele; - int err; - - down(&vm_reserved_sem); - list_for_each(ele, &vm_reserved){ - reserved = list_entry(ele, struct vm_reserved, list); - if(reserved->start >= end) goto found; - } - panic("Reserved vm out of range"); - found: - prev = list_entry(ele->prev, struct vm_reserved, list); - if(prev->end > start) - panic("Can't reserve vm"); - if(entry == NULL) - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - if(entry == NULL){ - printk("reserve_vm : Failed to allocate entry\n"); - err = -ENOMEM; - goto out; - } - *entry = ((struct vm_reserved) - { .list = LIST_HEAD_INIT(entry->list), - .start = start, - .end = end }); - list_add(&entry->list, &prev->list); - err = 0; - out: - up(&vm_reserved_sem); - return(0); -} - -unsigned long get_vm(unsigned long len) -{ - struct vm_reserved *this, *next; - struct list_head *ele; - unsigned long start; - int err; - - down(&vm_reserved_sem); - list_for_each(ele, &vm_reserved){ - this = list_entry(ele, struct vm_reserved, list); - next = list_entry(ele->next, struct vm_reserved, list); - if((this->start < next->start) && - (this->end + len + PAGE_SIZE <= next->start)) - goto found; - } - up(&vm_reserved_sem); - return(0); - found: - up(&vm_reserved_sem); - start = (unsigned long) UML_ROUND_UP(this->end) + PAGE_SIZE; - err = reserve_vm(start, start + len, NULL); - if(err) return(0); - return(start); -} - -int nregions(void) -{ - return(NREGIONS); -} - -void setup_range(int fd, char *driver, unsigned long start, unsigned long pfn, - unsigned long len, int need_vm, struct mem_region *region, - void *reserved) -{ - int i, cur; - - do { - cur = min(len, (unsigned long) REGION_SIZE); - i = setup_one_range(fd, driver, start, pfn, cur, region); - region = regions[i]; - if(need_vm && setup_region(region, reserved)){ - kfree(region); - regions[i] = NULL; - return; - } - start += cur; - if(pfn != -1) pfn += cur; - len -= cur; - } while(len > 0); -} - -struct iomem { - char *name; - int fd; - unsigned long size; -}; - -/* iomem regions can only be added on the command line at the moment. - * Locking will be needed when they can be added via mconsole. - */ - -struct iomem iomem_regions[NREGIONS] = { [ 0 ... NREGIONS - 1 ] = - { .name = NULL, - .fd = -1, - .size = 0 } }; - -int num_iomem_regions = 0; - -void add_iomem(char *name, int fd, unsigned long size) -{ - if(num_iomem_regions == sizeof(iomem_regions)/sizeof(iomem_regions[0])) - return; - size = (size + PAGE_SIZE - 1) & PAGE_MASK; - iomem_regions[num_iomem_regions++] = - ((struct iomem) { .name = name, - .fd = fd, - .size = size } ); -} - -int setup_iomem(void) -{ - struct iomem *iomem; - int i; - - for(i = 0; i < num_iomem_regions; i++){ - iomem = &iomem_regions[i]; - setup_range(iomem->fd, iomem->name, -1, -1, iomem->size, 1, - NULL, NULL); - } - return(0); -} - -__initcall(setup_iomem); - -#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) -#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) - -/* Changed during early boot */ -static struct mem_region physmem_region; -static struct vm_reserved physmem_reserved; - -void setup_physmem(unsigned long start, unsigned long reserve_end, - unsigned long len) -{ - struct mem_region *region = &physmem_region; - struct vm_reserved *reserved = &physmem_reserved; - unsigned long cur, pfn = 0; - int do_free = 1, bootmap_size; - - do { - cur = min(len, (unsigned long) REGION_SIZE); - if(region == NULL) - region = alloc_bootmem_low_pages(sizeof(*region)); - if(reserved == NULL) - reserved = alloc_bootmem_low_pages(sizeof(*reserved)); - if((region == NULL) || (reserved == NULL)) - panic("Couldn't allocate physmem region or vm " - "reservation\n"); - setup_range(-1, NULL, start, pfn, cur, 1, region, reserved); - - if(do_free){ - unsigned long reserve = reserve_end - start; - int pfn = PFN_UP(__pa(reserve_end)); - int delta = (len - reserve) >> PAGE_SHIFT; - - bootmap_size = init_bootmem(pfn, pfn + delta); - free_bootmem(__pa(reserve_end) + bootmap_size, - cur - bootmap_size - reserve); - do_free = 0; - } - start += cur; - pfn += cur >> PAGE_SHIFT; - len -= cur; - region = NULL; - reserved = NULL; - } while(len > 0); -} - -struct mem_region *phys_region(unsigned long phys) -{ - unsigned int n = phys_region_index(phys); - - if(regions[n] == NULL) - panic("Physical address in uninitialized region"); - return(regions[n]); -} - -unsigned long phys_offset(unsigned long phys) -{ - return(phys_addr(phys)); -} - -struct page *phys_mem_map(unsigned long phys) -{ - return((struct page *) phys_region(phys)->mem_map); -} - -struct page *pte_mem_map(pte_t pte) -{ - return(phys_mem_map(pte_val(pte))); -} - -struct mem_region *page_region(struct page *page, int *index_out) -{ - int i; - struct mem_region *region; - struct page *map; - - for(i = 0; i < NREGIONS; i++){ - region = regions[i]; - if(region == NULL) continue; - map = region->mem_map; - if((page >= map) && (page < &map[region->len >> PAGE_SHIFT])){ - if(index_out != NULL) *index_out = i; - return(region); - } - } - panic("No region found for page"); - return(NULL); -} - -unsigned long page_to_pfn(struct page *page) -{ - struct mem_region *region = page_region(page, NULL); - - return(region->start_pfn + (page - (struct page *) region->mem_map)); -} - -struct mem_region *pfn_to_region(unsigned long pfn, int *index_out) -{ - struct mem_region *region; - int i; - - for(i = 0; i < NREGIONS; i++){ - region = regions[i]; - if(region == NULL) - continue; - - if((region->start_pfn <= pfn) && - (region->start_pfn + (region->len >> PAGE_SHIFT) > pfn)){ - if(index_out != NULL) - *index_out = i; - return(region); - } - } - return(NULL); -} - -struct page *pfn_to_page(unsigned long pfn) -{ - struct mem_region *region = pfn_to_region(pfn, NULL); - struct page *mem_map = (struct page *) region->mem_map; - - return(&mem_map[pfn - region->start_pfn]); -} - -unsigned long phys_to_pfn(unsigned long p) -{ - struct mem_region *region = regions[phys_region_index(p)]; - - return(region->start_pfn + (phys_addr(p) >> PAGE_SHIFT)); -} - -unsigned long pfn_to_phys(unsigned long pfn) -{ - int n; - struct mem_region *region = pfn_to_region(pfn, &n); - - return(mk_phys((pfn - region->start_pfn) << PAGE_SHIFT, n)); -} - -struct page *page_mem_map(struct page *page) -{ - return((struct page *) page_region(page, NULL)->mem_map); -} - -extern unsigned long region_pa(void *virt) -{ - struct mem_region *region; - unsigned long addr = (unsigned long) virt; - int i; - - for(i = 0; i < NREGIONS; i++){ - region = regions[i]; - if(region == NULL) continue; - if((region->start <= addr) && - (addr <= region->start + region->len)) - return(mk_phys(addr - region->start, i)); - } - panic("region_pa : no region for virtual address"); - return(0); -} - -extern void *region_va(unsigned long phys) -{ - return((void *) (phys_region(phys)->start + phys_addr(phys))); -} - -unsigned long page_to_phys(struct page *page) -{ - int n; - struct mem_region *region = page_region(page, &n); - struct page *map = region->mem_map; - return(mk_phys((page - map) << PAGE_SHIFT, n)); -} - -struct page *phys_to_page(unsigned long phys) -{ - struct page *mem_map; - - mem_map = phys_mem_map(phys); - return(mem_map + (phys_offset(phys) >> PAGE_SHIFT)); -} - -static int setup_mem_maps(void) -{ - struct mem_region *region; - int i; - - for(i = 0; i < NREGIONS; i++){ - region = regions[i]; - if((region != NULL) && (region->fd > 0)) init_maps(region); - } - return(0); -} - -__initcall(setup_mem_maps); - /* * Allocate and free page tables. */ --- linux-2.6.9-rc1/arch/um/kernel/mem_user.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/arch/um/kernel/mem_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -34,10 +34,9 @@ #include #include #include -#include #include #include -#include +#include #include #include #include "kern_util.h" @@ -47,105 +46,145 @@ #include "init.h" #include "os.h" #include "tempfile.h" +#include "kern_constants.h" extern struct mem_region physmem_region; #define TEMPNAME_TEMPLATE "vm_file-XXXXXX" -int create_mem_file(unsigned long len) +static int create_tmp_file(unsigned long len) { - int fd; + int fd, err; char zero; fd = make_tempfile(TEMPNAME_TEMPLATE, NULL, 1); - if (fchmod(fd, 0777) < 0){ - perror("fchmod"); + if(fd < 0) { + os_print_error(fd, "make_tempfile"); + exit(1); + } + + err = os_mode_fd(fd, 0777); + if(err < 0){ + os_print_error(err, "os_mode_fd"); exit(1); } - if(os_seek_file(fd, len) < 0){ - perror("lseek"); + err = os_seek_file(fd, len); + if(err < 0){ + os_print_error(err, "os_seek_file"); exit(1); } zero = 0; - if(write(fd, &zero, 1) != 1){ - perror("write"); + err = os_write_file(fd, &zero, 1); + if(err != 1){ + os_print_error(err, "os_write_file"); exit(1); } - if(fcntl(fd, F_SETFD, 1) != 0) - perror("Setting FD_CLOEXEC failed"); + return(fd); } -int setup_region(struct mem_region *region, void *entry) +static int have_devanon = 0; + +void check_devanon(void) +{ + int fd; + + printk("Checking for /dev/anon on the host..."); + fd = open("/dev/anon", O_RDWR); + if(fd < 0){ + printk("Not available (open failed with errno %d)\n", errno); + return; + } + + printk("OK\n"); + have_devanon = 1; +} + +static int create_anon_file(unsigned long len) { - void *loc, *start; - char *driver; - int err, offset; - - if(region->start != -1){ - err = reserve_vm(region->start, - region->start + region->len, entry); - if(err){ - printk("setup_region : failed to reserve " - "0x%x - 0x%x for driver '%s'\n", - region->start, - region->start + region->len, - region->driver); - return(-1); - } - } - else region->start = get_vm(region->len); - if(region->start == 0){ - if(region->driver == NULL) driver = "physmem"; - else driver = region->driver; - printk("setup_region : failed to find vm for " - "driver '%s' (length %d)\n", driver, region->len); - return(-1); - } - if(region->start == uml_physmem){ - start = (void *) uml_reserved; - offset = uml_reserved - uml_physmem; - } - else { - start = (void *) region->start; - offset = 0; - } - - loc = mmap(start, region->len - offset, PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_FIXED, region->fd, offset); - if(loc != start){ - perror("Mapping memory"); + void *addr; + int fd; + + fd = open("/dev/anon", O_RDWR); + if(fd < 0) { + os_print_error(fd, "opening /dev/anon"); exit(1); } - return(0); + + addr = mmap(NULL, len, PROT_READ | PROT_WRITE , MAP_PRIVATE, fd, 0); + if(addr == MAP_FAILED){ + os_print_error((int) addr, "mapping physmem file"); + exit(1); + } + munmap(addr, len); + + return(fd); +} + +int create_mem_file(unsigned long len) +{ + int err, fd; + + if(have_devanon) + fd = create_anon_file(len); + else fd = create_tmp_file(len); + + err = os_set_exec_close(fd, 1); + if(err < 0) + os_print_error(err, "exec_close"); + return(fd); } +struct iomem_region *iomem_regions = NULL; +int iomem_size = 0; + static int __init parse_iomem(char *str, int *add) { - struct stat buf; + struct iomem_region *new; + struct uml_stat buf; char *file, *driver; - int fd; + int fd, err; driver = str; file = strchr(str,','); if(file == NULL){ - printk("parse_iomem : failed to parse iomem\n"); - return(1); + printf("parse_iomem : failed to parse iomem\n"); + goto out; } *file = '\0'; file++; fd = os_open_file(file, of_rdwr(OPENFLAGS()), 0); if(fd < 0){ - printk("parse_iomem - Couldn't open io file, errno = %d\n", - errno); - return(1); - } - if(fstat(fd, &buf) < 0) { - printk("parse_iomem - cannot fstat file, errno = %d\n", errno); - return(1); + os_print_error(fd, "parse_iomem - Couldn't open io file"); + goto out; } - add_iomem(driver, fd, buf.st_size); + + err = os_stat_fd(fd, &buf); + if(err < 0){ + os_print_error(err, "parse_iomem - cannot stat_fd file"); + goto out_close; + } + + new = malloc(sizeof(*new)); + if(new == NULL){ + perror("Couldn't allocate iomem_region struct"); + goto out_close; + } + + *new = ((struct iomem_region) { .next = iomem_regions, + .driver = driver, + .fd = fd, + .size = buf.ust_size, + .phys = 0, + .virt = 0 }); + iomem_regions = new; + iomem_size += new->size + UM_KERN_PAGE_SIZE; + return(0); + out_close: + os_close_file(fd); + out: + return(1); } __uml_setup("iomem=", parse_iomem, @@ -153,73 +192,20 @@ __uml_setup("iomem=", parse_iomem, " Configure as an IO memory region named .\n\n" ); -#ifdef notdef -int logging = 0; -int logging_fd = -1; - -int logging_line = 0; -char logging_buf[256]; - -void log(char *fmt, ...) -{ - va_list ap; - struct timeval tv; - struct openflags flags; - - if(logging == 0) return; - if(logging_fd < 0){ - flags = of_create(of_trunc(of_rdrw(OPENFLAGS()))); - logging_fd = os_open_file("log", flags, 0644); - } - gettimeofday(&tv, NULL); - sprintf(logging_buf, "%d\t %u.%u ", logging_line++, tv.tv_sec, - tv.tv_usec); - va_start(ap, fmt); - vsprintf(&logging_buf[strlen(logging_buf)], fmt, ap); - va_end(ap); - write(logging_fd, logging_buf, strlen(logging_buf)); -} -#endif - -int map_memory(unsigned long virt, unsigned long phys, unsigned long len, - int r, int w, int x) -{ - struct mem_region *region = phys_region(phys); - - return(os_map_memory((void *) virt, region->fd, phys_offset(phys), len, - r, w, x)); -} - int protect_memory(unsigned long addr, unsigned long len, int r, int w, int x, int must_succeed) { - if(os_protect_memory((void *) addr, len, r, w, x) < 0){ + int err; + + err = os_protect_memory((void *) addr, len, r, w, x); + if(err < 0){ if(must_succeed) - panic("protect failed, errno = %d", errno); - else return(-errno); + panic("protect failed, err = %d", -err); + else return(err); } return(0); } -unsigned long find_iomem(char *driver, unsigned long *len_out) -{ - struct mem_region *region; - int i, n; - - n = nregions(); - for(i = 0; i < n; i++){ - region = regions[i]; - if(region == NULL) continue; - if((region->driver != NULL) && - !strcmp(region->driver, driver)){ - *len_out = region->len; - return(region->start); - } - } - *len_out = 0; - return 0; -} - /* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/um/kernel/physmem.c 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,468 @@ +/* + * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include "linux/mm.h" +#include "linux/ghash.h" +#include "linux/slab.h" +#include "linux/vmalloc.h" +#include "linux/bootmem.h" +#include "asm/types.h" +#include "asm/pgtable.h" +#include "kern_util.h" +#include "user_util.h" +#include "mode_kern.h" +#include "mem.h" +#include "mem_user.h" +#include "os.h" +#include "kern.h" +#include "init.h" + +#if 0 +static pgd_t physmem_pgd[PTRS_PER_PGD]; + +static struct phys_desc *lookup_mapping(void *addr) +{ + pgd = &physmem_pgd[pgd_index(addr)]; + if(pgd_none(pgd)) + return(NULL); + + pmd = pmd_offset(pgd, addr); + if(pmd_none(pmd)) + return(NULL); + + pte = pte_offset_kernel(pmd, addr); + return((struct phys_desc *) pte_val(pte)); +} + +static struct add_mapping(void *addr, struct phys_desc *new) +{ +} +#endif + +#define PHYS_HASHSIZE (8192) + +struct phys_desc; + +DEF_HASH_STRUCTS(virtmem, PHYS_HASHSIZE, struct phys_desc); + +struct phys_desc { + struct virtmem_ptrs virt_ptrs; + int fd; + __u64 offset; + void *virt; + unsigned long phys; + struct list_head list; +}; + +struct virtmem_table virtmem_hash; + +static int virt_cmp(void *virt1, void *virt2) +{ + return(virt1 != virt2); +} + +static int virt_hash(void *virt) +{ + unsigned long addr = ((unsigned long) virt) >> PAGE_SHIFT; + return(addr % PHYS_HASHSIZE); +} + +DEF_HASH(static, virtmem, struct phys_desc, virt_ptrs, void *, virt, virt_cmp, + virt_hash); + +LIST_HEAD(descriptor_mappings); + +struct desc_mapping { + int fd; + struct list_head list; + struct list_head pages; +}; + +static struct desc_mapping *find_mapping(int fd) +{ + struct desc_mapping *desc; + struct list_head *ele; + + list_for_each(ele, &descriptor_mappings){ + desc = list_entry(ele, struct desc_mapping, list); + if(desc->fd == fd) + return(desc); + } + + return(NULL); +} + +static struct desc_mapping *descriptor_mapping(int fd) +{ + struct desc_mapping *desc; + + desc = find_mapping(fd); + if(desc != NULL) + return(desc); + + desc = kmalloc(sizeof(*desc), GFP_ATOMIC); + if(desc == NULL) + return(NULL); + + *desc = ((struct desc_mapping) + { .fd = fd, + .list = LIST_HEAD_INIT(desc->list), + .pages = LIST_HEAD_INIT(desc->pages) }); + list_add(&desc->list, &descriptor_mappings); + + return(desc); +} + +int physmem_subst_mapping(void *virt, int fd, __u64 offset, int w) +{ + struct desc_mapping *fd_maps; + struct phys_desc *desc; + unsigned long phys; + int err; + + fd_maps = descriptor_mapping(fd); + if(fd_maps == NULL) + return(-ENOMEM); + + phys = __pa(virt); + if(find_virtmem_hash(&virtmem_hash, virt) != NULL) + panic("Address 0x%p is already substituted\n", virt); + + err = -ENOMEM; + desc = kmalloc(sizeof(*desc), GFP_ATOMIC); + if(desc == NULL) + goto out; + + *desc = ((struct phys_desc) + { .virt_ptrs = { NULL, NULL }, + .fd = fd, + .offset = offset, + .virt = virt, + .phys = __pa(virt), + .list = LIST_HEAD_INIT(desc->list) }); + insert_virtmem_hash(&virtmem_hash, desc); + + list_add(&desc->list, &fd_maps->pages); + + virt = (void *) ((unsigned long) virt & PAGE_MASK); + err = os_map_memory(virt, fd, offset, PAGE_SIZE, 1, w, 0); + if(!err) + goto out; + + remove_virtmem_hash(&virtmem_hash, desc); + kfree(desc); + out: + return(err); +} + +static int physmem_fd = -1; + +static void remove_mapping(struct phys_desc *desc) +{ + void *virt = desc->virt; + int err; + + remove_virtmem_hash(&virtmem_hash, desc); + list_del(&desc->list); + kfree(desc); + + err = os_map_memory(virt, physmem_fd, __pa(virt), PAGE_SIZE, 1, 1, 0); + if(err) + panic("Failed to unmap block device page from physical memory, " + "errno = %d", -err); +} + +int physmem_remove_mapping(void *virt) +{ + struct phys_desc *desc; + + virt = (void *) ((unsigned long) virt & PAGE_MASK); + desc = find_virtmem_hash(&virtmem_hash, virt); + if(desc == NULL) + return(0); + + remove_mapping(desc); + return(1); +} + +void physmem_forget_descriptor(int fd) +{ + struct desc_mapping *desc; + struct phys_desc *page; + struct list_head *ele, *next; + __u64 offset; + void *addr; + int err; + + desc = find_mapping(fd); + if(desc == NULL) + return; + + list_for_each_safe(ele, next, &desc->pages){ + page = list_entry(ele, struct phys_desc, list); + offset = page->offset; + addr = page->virt; + remove_mapping(page); + err = os_seek_file(fd, offset); + if(err) + panic("physmem_forget_descriptor - failed to seek " + "to %lld in fd %d, error = %d\n", + offset, fd, -err); + err = os_read_file(fd, addr, PAGE_SIZE); + if(err < 0) + panic("physmem_forget_descriptor - failed to read " + "from fd %d to 0x%p, error = %d\n", + fd, addr, -err); + } + + list_del(&desc->list); + kfree(desc); +} + +void arch_free_page(struct page *page, int order) +{ + void *virt; + int i; + + for(i = 0; i < (1 << order); i++){ + virt = __va(page_to_phys(page + i)); + physmem_remove_mapping(virt); + } +} + +int is_remapped(void *virt) +{ + return(find_virtmem_hash(&virtmem_hash, virt) != NULL); +} + +/* Changed during early boot */ +unsigned long high_physmem; + +extern unsigned long physmem_size; + +void *to_virt(unsigned long phys) +{ + return((void *) uml_physmem + phys); +} + +unsigned long to_phys(void *virt) +{ + return(((unsigned long) virt) - uml_physmem); +} + +int init_maps(unsigned long physmem, unsigned long iomem, unsigned long highmem) +{ + struct page *p, *map; + unsigned long phys_len, phys_pages, highmem_len, highmem_pages; + unsigned long iomem_len, iomem_pages, total_len, total_pages; + int i; + + phys_pages = physmem >> PAGE_SHIFT; + phys_len = phys_pages * sizeof(struct page); + + iomem_pages = iomem >> PAGE_SHIFT; + iomem_len = iomem_pages * sizeof(struct page); + + highmem_pages = highmem >> PAGE_SHIFT; + highmem_len = highmem_pages * sizeof(struct page); + + total_pages = phys_pages + iomem_pages + highmem_pages; + total_len = phys_len + iomem_pages + highmem_len; + + if(kmalloc_ok){ + map = kmalloc(total_len, GFP_KERNEL); + if(map == NULL) + map = vmalloc(total_len); + } + else map = alloc_bootmem_low_pages(total_len); + + if(map == NULL) + return(-ENOMEM); + + for(i = 0; i < total_pages; i++){ + p = &map[i]; + set_page_count(p, 0); + SetPageReserved(p); + INIT_LIST_HEAD(&p->lru); + } + + mem_map = map; + max_mapnr = total_pages; + return(0); +} + +struct page *phys_to_page(const unsigned long phys) +{ + return(&mem_map[phys >> PAGE_SHIFT]); +} + +struct page *__virt_to_page(const unsigned long virt) +{ + return(&mem_map[__pa(virt) >> PAGE_SHIFT]); +} + +unsigned long page_to_phys(struct page *page) +{ + return((page - mem_map) << PAGE_SHIFT); +} + +pte_t mk_pte(struct page *page, pgprot_t pgprot) +{ + pte_t pte; + + pte_val(pte) = page_to_phys(page) + pgprot_val(pgprot); + if(pte_present(pte)) pte_mknewprot(pte_mknewpage(pte)); + return(pte); +} + +/* Changed during early boot */ +static unsigned long kmem_top = 0; + +unsigned long get_kmem_end(void) +{ + if(kmem_top == 0) + kmem_top = CHOOSE_MODE(kmem_end_tt, kmem_end_skas); + return(kmem_top); +} + +void map_memory(unsigned long virt, unsigned long phys, unsigned long len, + int r, int w, int x) +{ + __u64 offset; + int fd, err; + + fd = phys_mapping(phys, &offset); + err = os_map_memory((void *) virt, fd, offset, len, r, w, x); + if(err) + panic("map_memory(0x%lx, %d, 0x%llx, %ld, %d, %d, %d) failed, " + "err = %d\n", virt, fd, offset, len, r, w, x, err); +} + +#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) + +void setup_physmem(unsigned long start, unsigned long reserve_end, + unsigned long len, unsigned long highmem) +{ + unsigned long reserve = reserve_end - start; + int pfn = PFN_UP(__pa(reserve_end)); + int delta = (len - reserve) >> PAGE_SHIFT; + int err, offset, bootmap_size; + + physmem_fd = create_mem_file(len + highmem); + + offset = uml_reserved - uml_physmem; + err = os_map_memory((void *) uml_reserved, physmem_fd, offset, + len - offset, 1, 1, 0); + if(err < 0){ + os_print_error(err, "Mapping memory"); + exit(1); + } + + bootmap_size = init_bootmem(pfn, pfn + delta); + free_bootmem(__pa(reserve_end) + bootmap_size, + len - bootmap_size - reserve); +} + +int phys_mapping(unsigned long phys, __u64 *offset_out) +{ + struct phys_desc *desc = find_virtmem_hash(&virtmem_hash, + __va(phys & PAGE_MASK)); + int fd = -1; + + if(desc != NULL){ + fd = desc->fd; + *offset_out = desc->offset; + } + else if(phys < physmem_size){ + fd = physmem_fd; + *offset_out = phys; + } + else if(phys < __pa(end_iomem)){ + struct iomem_region *region = iomem_regions; + + while(region != NULL){ + if((phys >= region->phys) && + (phys < region->phys + region->size)){ + fd = region->fd; + *offset_out = phys - region->phys; + break; + } + region = region->next; + } + } + else if(phys < __pa(end_iomem) + highmem){ + fd = physmem_fd; + *offset_out = phys - iomem_size; + } + + return(fd); +} + +static int __init uml_mem_setup(char *line, int *add) +{ + char *retptr; + physmem_size = memparse(line,&retptr); + return 0; +} +__uml_setup("mem=", uml_mem_setup, +"mem=\n" +" This controls how much \"physical\" memory the kernel allocates\n" +" for the system. The size is specified as a number followed by\n" +" one of 'k', 'K', 'm', 'M', which have the obvious meanings.\n" +" This is not related to the amount of memory in the host. It can\n" +" be more, and the excess, if it's ever used, will just be swapped out.\n" +" Example: mem=64M\n\n" +); + +unsigned long find_iomem(char *driver, unsigned long *len_out) +{ + struct iomem_region *region = iomem_regions; + + while(region != NULL){ + if(!strcmp(region->driver, driver)){ + *len_out = region->size; + return(region->virt); + } + } + + return(0); +} + +int setup_iomem(void) +{ + struct iomem_region *region = iomem_regions; + unsigned long iomem_start = high_physmem + PAGE_SIZE; + int err; + + while(region != NULL){ + err = os_map_memory((void *) iomem_start, region->fd, 0, + region->size, 1, 1, 0); + if(err) + printk("Mapping iomem region for driver '%s' failed, " + "errno = %d\n", region->driver, -err); + else { + region->virt = iomem_start; + region->phys = __pa(region->virt); + } + + iomem_start += region->size + PAGE_SIZE; + region = region->next; + } + + return(0); +} + +__initcall(setup_iomem); + +/* + * 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: + */ --- linux-2.6.9-rc1/arch/um/kernel/process.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/um/kernel/process.c 2004-09-02 20:51:51.000000000 -0700 @@ -9,18 +9,17 @@ #include #include #include -#include #include #include #include #include -#include #include #include #include #include #include #include +#include #include "user_util.h" #include "kern_util.h" #include "user.h" @@ -58,7 +57,11 @@ void init_new_thread_signals(int altstac { int flags = altstack ? SA_ONSTACK : 0; - set_handler(SIGSEGV, (__sighandler_t) sig_handler, flags, + /* NODEFER is set here because SEGV isn't turned back on when the + * handler is ready to receive signals. This causes any segfault + * during a copy_user to kill the process because the fault is blocked. + */ + set_handler(SIGSEGV, (__sighandler_t) sig_handler, flags | SA_NODEFER, SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); set_handler(SIGTRAP, (__sighandler_t) sig_handler, flags, SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); @@ -72,7 +75,6 @@ void init_new_thread_signals(int altstac SIGUSR1, SIGIO, SIGWINCH, SIGALRM, SIGVTALRM, -1); set_handler(SIGUSR2, (__sighandler_t) sig_handler, SA_NOMASK | flags, -1); - (void) CHOOSE_MODE(signal(SIGCHLD, SIG_IGN), (void *) 0); signal(SIGHUP, SIG_IGN); init_irq_signals(altstack); @@ -122,12 +124,17 @@ int start_fork_tramp(void *thread_arg, u /* Start the process and wait for it to kill itself */ new_pid = clone(outer_tramp, (void *) sp, clone_flags, &arg); - if(new_pid < 0) return(-errno); - while((err = waitpid(new_pid, &status, 0) < 0) && (errno == EINTR)) ; - if(err < 0) panic("Waiting for outer trampoline failed - errno = %d", - errno); + if(new_pid < 0) + return(new_pid); + + CATCH_EINTR(err = waitpid(new_pid, &status, 0)); + if(err < 0) + panic("Waiting for outer trampoline failed - errno = %d", + errno); + if(!WIFSIGNALED(status) || (WTERMSIG(status) != SIGKILL)) - panic("outer trampoline didn't exit with SIGKILL"); + panic("outer trampoline didn't exit with SIGKILL, " + "status = %d", status); return(arg.pid); } @@ -138,7 +145,7 @@ void suspend_new_thread(int fd) os_stop_process(os_getpid()); - if(read(fd, &c, sizeof(c)) != sizeof(c)) + if(os_read_file(fd, &c, sizeof(c)) != sizeof(c)) panic("read failed in suspend_new_thread"); } @@ -168,7 +175,7 @@ static int start_ptraced_child(void **st pid = clone(ptrace_child, (void *) sp, SIGCHLD, NULL); if(pid < 0) panic("check_ptrace : clone failed, errno = %d", errno); - n = waitpid(pid, &status, WUNTRACED); + CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); if(n < 0) panic("check_ptrace : wait failed, errno = %d", errno); if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGSTOP)) @@ -185,7 +192,7 @@ static void stop_ptraced_child(int pid, if(ptrace(PTRACE_CONT, pid, 0, 0) < 0) panic("check_ptrace : ptrace failed, errno = %d", errno); - n = waitpid(pid, &status, 0); + CATCH_EINTR(n = waitpid(pid, &status, 0)); if(!WIFEXITED(status) || (WEXITSTATUS(status) != exitcode)) panic("check_ptrace : child exited with status 0x%x", status); @@ -193,6 +200,66 @@ static void stop_ptraced_child(int pid, panic("check_ptrace : munmap failed, errno = %d", errno); } +static int force_sysemu_disabled = 0; + +static int __init nosysemu_cmd_param(char *str, int* add) +{ + force_sysemu_disabled = 1; + return 0; +} + +__uml_setup("nosysemu", nosysemu_cmd_param, + "nosysemu\n" + " Turns off syscall emulation patch for ptrace (SYSEMU) on.\n" + " SYSEMU is a performance-patch introduced by Laurent Vivier. It changes\n" + " behaviour of ptrace() and helps reducing host context switch rate.\n" + " To make it working, you need a kernel patch for your host, too.\n" + " See http://perso.wanadoo.fr/laurent.vivier/UML/ for further information.\n"); + +static void __init check_sysemu(void) +{ + void *stack; + int pid, n, status; + + if (mode_tt) + return; + + printk("Checking syscall emulation patch for ptrace..."); + sysemu_supported = 0; + pid = start_ptraced_child(&stack); + if(ptrace(PTRACE_SYSEMU, pid, 0, 0) >= 0) { + struct user_regs_struct regs; + + CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); + if (n < 0) + panic("check_ptrace : wait failed, errno = %d", errno); + if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP)) + panic("check_ptrace : expected SIGTRAP, " + "got status = %d", status); + + if (ptrace(PTRACE_GETREGS, pid, 0, ®s) < 0) + panic("check_ptrace : failed to read child " + "registers, errno = %d", errno); + regs.orig_eax = pid; + if (ptrace(PTRACE_SETREGS, pid, 0, ®s) < 0) + panic("check_ptrace : failed to modify child " + "registers, errno = %d", errno); + + stop_ptraced_child(pid, stack, 0); + + sysemu_supported = 1; + printk("found\n"); + } + else + { + stop_ptraced_child(pid, stack, 1); + sysemu_supported = 0; + printk("missing\n"); + } + + set_using_sysemu(!force_sysemu_disabled); +} + void __init check_ptrace(void) { void *stack; @@ -205,7 +272,7 @@ void __init check_ptrace(void) if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) panic("check_ptrace : ptrace failed, errno = %d", errno); - n = waitpid(pid, &status, WUNTRACED); + CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); if(n < 0) panic("check_ptrace : wait failed, errno = %d", errno); if(!WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP)) @@ -225,6 +292,7 @@ void __init check_ptrace(void) } stop_ptraced_child(pid, stack, 0); printk("OK\n"); + check_sysemu(); } int run_kernel_thread(int (*fn)(void *), void *arg, void **jmp_ptr) @@ -233,7 +301,7 @@ int run_kernel_thread(int (*fn)(void *), int n; *jmp_ptr = &buf; - n = setjmp(buf); + n = sigsetjmp(buf, 1); if(n != 0) return(n); (*fn)(arg); @@ -273,7 +341,7 @@ int can_do_skas(void) stop_ptraced_child(pid, stack, 1); printf("Checking for /proc/mm..."); - if(access("/proc/mm", W_OK)){ + if(os_access("/proc/mm", OS_ACC_W_OK) < 0){ printf("not found\n"); ret = 0; } --- linux-2.6.9-rc1/arch/um/kernel/process_kern.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/um/kernel/process_kern.c 2004-09-02 20:51:51.000000000 -0700 @@ -16,6 +16,8 @@ #include "linux/module.h" #include "linux/init.h" #include "linux/capability.h" +#include "linux/vmalloc.h" +#include "linux/spinlock.h" #include "asm/unistd.h" #include "asm/mman.h" #include "asm/segment.h" @@ -23,7 +25,6 @@ #include "asm/pgtable.h" #include "asm/processor.h" #include "asm/tlbflush.h" -#include "asm/spinlock.h" #include "asm/uaccess.h" #include "asm/user.h" #include "user_util.h" @@ -52,17 +53,12 @@ struct cpu_task cpu_tasks[NR_CPUS] = { [ struct task_struct *get_task(int pid, int require) { - struct task_struct *task, *ret; + struct task_struct *ret; - ret = NULL; read_lock(&tasklist_lock); - for_each_process(task){ - if(task->pid == pid){ - ret = task; - break; - } - } + ret = find_task_by_pid(pid); read_unlock(&tasklist_lock); + if(require && (ret == NULL)) panic("get_task couldn't find a task\n"); return(ret); } @@ -95,7 +91,8 @@ unsigned long alloc_stack(int order, int int flags = GFP_KERNEL; if(atomic) flags |= GFP_ATOMIC; - if((page = __get_free_pages(flags, order)) == 0) + page = __get_free_pages(flags, order); + if(page == 0) return(0); stack_protections(page); return(page); @@ -103,22 +100,25 @@ unsigned long alloc_stack(int order, int int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) { - struct task_struct *p; + int pid; current->thread.request.u.thread.proc = fn; current->thread.request.u.thread.arg = arg; - p = do_fork(CLONE_VM | flags, 0, NULL, 0, NULL, NULL); - if(IS_ERR(p)) panic("do_fork failed in kernel_thread"); - return(p->pid); + pid = do_fork(CLONE_VM | CLONE_UNTRACED | flags, 0, NULL, 0, NULL, + NULL); + if(pid < 0) + panic("do_fork failed in kernel_thread, errno = %d", pid); + return(pid); } void switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk) { - unsigned cpu = smp_processor_id(); + int cpu = smp_processor_id(); + if (prev != next) - clear_bit(cpu, &prev->cpu_vm_mask); - set_bit(cpu, &next->cpu_vm_mask); + cpu_clear(cpu, prev->cpu_vm_mask); + cpu_set(cpu, next->cpu_vm_mask); } void set_current(void *t) @@ -129,7 +129,7 @@ void set_current(void *t) { external_pid(task), task }); } -void *switch_to(void *prev, void *next, void *last) +void *_switch_to(void *prev, void *next, void *last) { return(CHOOSE_MODE(switch_to_tt(prev, next), switch_to_skas(prev, next))); @@ -149,7 +149,7 @@ void release_thread(struct task_struct * void exit_thread(void) { CHOOSE_MODE(exit_thread_tt(), exit_thread_skas()); - unprotect_stack((unsigned long) current->thread_info); + unprotect_stack((unsigned long) current_thread); } void *get_current(void) @@ -157,6 +157,10 @@ void *get_current(void) return(current); } +void prepare_to_copy(struct task_struct *tsk) +{ +} + int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, unsigned long stack_top, struct task_struct * p, struct pt_regs *regs) @@ -190,7 +194,7 @@ int current_pid(void) void default_idle(void) { - idle_timer(); + uml_idle_timer(); atomic_inc(&init_mm.mm_count); current->mm = &init_mm; @@ -299,6 +303,11 @@ void *um_kmalloc_atomic(int size) return(kmalloc(size, GFP_ATOMIC)); } +void *um_vmalloc(int size) +{ + return(vmalloc(size)); +} + unsigned long get_fault_addr(void) { return((unsigned long) current->thread.fault_addr); @@ -367,10 +376,15 @@ int clear_user_proc(void *buf, int size) return(clear_user(buf, size)); } +int strlen_user_proc(char *str) +{ + return(strlen_user(str)); +} + int smp_sigio_handler(void) { #ifdef CONFIG_SMP - int cpu = current->thread_info->cpu; + int cpu = current_thread->cpu; IPI_handler(cpu); if(cpu != 0) return(1); @@ -385,7 +399,7 @@ int um_in_interrupt(void) int cpu(void) { - return(current->thread_info->cpu); + return(current_thread->cpu); } /* --- linux-2.6.9-rc1/arch/um/kernel/ptrace.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/arch/um/kernel/ptrace.c 2004-09-02 20:51:51.000000000 -0700 @@ -24,11 +24,6 @@ void ptrace_disable(struct task_struct * { } -extern long do_mmap2(struct task_struct *task, unsigned long addr, - unsigned long len, unsigned long prot, - unsigned long flags, unsigned long fd, - unsigned long pgoff); - int sys_ptrace(long request, long pid, long addr, long data) { struct task_struct *child; @@ -302,8 +297,17 @@ int sys_ptrace(long request, long pid, l return ret; } -void syscall_trace(void) +void syscall_trace(union uml_pt_regs *regs, int entryexit) { + if (unlikely(current->audit_context)) { + if (!entryexit) + audit_syscall_entry(current, regs->orig_eax, + regs->ebx, regs->ecx, + regs->edx, regs->esi); + else + audit_syscall_exit(current, regs->eax); + } + if (!test_thread_flag(TIF_SYSCALL_TRACE)) return; if (!(current->ptrace & PT_PTRACED)) @@ -311,11 +315,8 @@ void syscall_trace(void) /* the 0x80 provides a way for the tracing parent to distinguish between a syscall stop and SIGTRAP delivery */ - current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0); - current->state = TASK_STOPPED; - notify_parent(current, SIGCHLD); - schedule(); + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); /* * this isn't the same as continuing with a signal, but it will do --- linux-2.6.9-rc1/arch/um/kernel/reboot.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/um/kernel/reboot.c 2004-09-02 20:51:51.000000000 -0700 @@ -15,6 +15,7 @@ #ifdef CONFIG_SMP static void kill_idlers(int me) { +#ifdef CONFIG_MODE_TT struct task_struct *p; int i; @@ -23,6 +24,7 @@ static void kill_idlers(int me) if((p != NULL) && (p->thread.mode.tt.extern_pid != me)) os_kill_process(p->thread.mode.tt.extern_pid, 0); } +#endif } #endif --- linux-2.6.9-rc1/arch/um/kernel/sigio_kern.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/um/kernel/sigio_kern.c 2004-09-02 20:51:51.000000000 -0700 @@ -6,18 +6,21 @@ #include "linux/kernel.h" #include "linux/list.h" #include "linux/slab.h" -#include "asm/irq.h" +#include "linux/signal.h" +#include "linux/interrupt.h" #include "init.h" #include "sigio.h" #include "irq_user.h" +#include "irq_kern.h" /* Protected by sigio_lock() called from write_sigio_workaround */ static int sigio_irq_fd = -1; -void sigio_interrupt(int irq, void *data, struct pt_regs *unused) +irqreturn_t sigio_interrupt(int irq, void *data, struct pt_regs *unused) { read_sigio_fd(sigio_irq_fd); reactivate_fd(sigio_irq_fd, SIGIO_WRITE_IRQ); + return(IRQ_HANDLED); } int write_sigio_irq(int fd) --- linux-2.6.9-rc1/arch/um/kernel/sigio_user.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/um/kernel/sigio_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -17,6 +16,7 @@ #include "init.h" #include "user.h" #include "kern_util.h" +#include "user_util.h" #include "sigio.h" #include "helper.h" #include "os.h" @@ -26,7 +26,7 @@ int pty_output_sigio = 0; int pty_close_sigio = 0; /* Used as a flag during SIGIO testing early in boot */ -static int got_sigio = 0; +static volatile int got_sigio = 0; void __init handler(int sig) { @@ -45,19 +45,18 @@ static void openpty_cb(void *arg) info->err = 0; if(openpty(&info->master, &info->slave, NULL, NULL, NULL)) - info->err = errno; + info->err = -errno; } void __init check_one_sigio(void (*proc)(int, int)) { struct sigaction old, new; - struct termios tt; struct openpty_arg pty = { .master = -1, .slave = -1 }; - int master, slave, flags; + int master, slave, err; initial_thread_cb(openpty_cb, &pty); if(pty.err){ - printk("openpty failed, errno = %d\n", pty.err); + printk("openpty failed, errno = %d\n", -pty.err); return; } @@ -69,23 +68,13 @@ void __init check_one_sigio(void (*proc) return; } - if(tcgetattr(master, &tt) < 0) - panic("check_sigio : tcgetattr failed, errno = %d\n", errno); - cfmakeraw(&tt); - if(tcsetattr(master, TCSADRAIN, &tt) < 0) - panic("check_sigio : tcsetattr failed, errno = %d\n", errno); - - if((flags = fcntl(master, F_GETFL)) < 0) - panic("tty_fds : fcntl F_GETFL failed, errno = %d\n", errno); - - if((fcntl(master, F_SETFL, flags | O_NONBLOCK | O_ASYNC) < 0) || - (fcntl(master, F_SETOWN, os_getpid()) < 0)) - panic("check_sigio : fcntl F_SETFL or F_SETOWN failed, " - "errno = %d\n", errno); - - if((fcntl(slave, F_SETFL, flags | O_NONBLOCK) < 0)) - panic("check_sigio : fcntl F_SETFL failed, errno = %d\n", - errno); + err = __raw(master, 1, 0); //Not now, but complain so we now where we failed. + if (err < 0) + panic("check_sigio : __raw failed, errno = %d\n", -err); + + err = os_sigio_async(master, slave); + if(err < 0) + panic("tty_fds : sigio_async failed, err = %d\n", -err); if(sigaction(SIGIO, NULL, &old) < 0) panic("check_sigio : sigaction 1 failed, errno = %d\n", errno); @@ -97,8 +86,8 @@ void __init check_one_sigio(void (*proc) got_sigio = 0; (*proc)(master, slave); - close(master); - close(slave); + os_close_file(master); + os_close_file(slave); if(sigaction(SIGIO, &old, NULL) < 0) panic("check_sigio : sigaction 3 failed, errno = %d\n", errno); @@ -112,25 +101,25 @@ static void tty_output(int master, int s printk("Checking that host ptys support output SIGIO..."); memset(buf, 0, sizeof(buf)); - while(write(master, buf, sizeof(buf)) > 0) ; + + while(os_write_file(master, buf, sizeof(buf)) > 0) ; if(errno != EAGAIN) panic("check_sigio : write failed, errno = %d\n", errno); - - while(((n = read(slave, buf, sizeof(buf))) > 0) && !got_sigio) ; + while(((n = os_read_file(slave, buf, sizeof(buf))) > 0) && !got_sigio) ; if(got_sigio){ printk("Yes\n"); pty_output_sigio = 1; } - else if(errno == EAGAIN) printk("No, enabling workaround\n"); - else panic("check_sigio : read failed, errno = %d\n", errno); + else if(n == -EAGAIN) printk("No, enabling workaround\n"); + else panic("check_sigio : read failed, err = %d\n", n); } static void tty_close(int master, int slave) { printk("Checking that host ptys support SIGIO on close..."); - close(slave); + os_close_file(slave); if(got_sigio){ printk("Yes\n"); pty_close_sigio = 1; @@ -140,7 +129,8 @@ static void tty_close(int master, int sl void __init check_sigio(void) { - if(access("/dev/ptmx", R_OK) && access("/dev/ptyp0", R_OK)){ + if((os_access("/dev/ptmx", OS_ACC_R_OK) < 0) && + (os_access("/dev/ptyp0", OS_ACC_R_OK) < 0)){ printk("No pseudo-terminals available - skipping pty SIGIO " "check\n"); return; @@ -201,11 +191,10 @@ static int write_sigio_thread(void *unus p = &fds->poll[i]; if(p->revents == 0) continue; if(p->fd == sigio_private[1]){ - n = read(sigio_private[1], &c, sizeof(c)); + n = os_read_file(sigio_private[1], &c, sizeof(c)); if(n != sizeof(c)) printk("write_sigio_thread : " - "read failed, errno = %d\n", - errno); + "read failed, err = %d\n", -n); tmp = current_poll; current_poll = next_poll; next_poll = tmp; @@ -218,10 +207,10 @@ static int write_sigio_thread(void *unus (fds->used - i) * sizeof(*fds->poll)); } - n = write(respond_fd, &c, sizeof(c)); + n = os_write_file(respond_fd, &c, sizeof(c)); if(n != sizeof(c)) printk("write_sigio_thread : write failed, " - "errno = %d\n", errno); + "err = %d\n", -n); } } } @@ -252,15 +241,15 @@ static void update_thread(void) char c; flags = set_signals(0); - n = write(sigio_private[0], &c, sizeof(c)); + n = os_write_file(sigio_private[0], &c, sizeof(c)); if(n != sizeof(c)){ - printk("update_thread : write failed, errno = %d\n", errno); + printk("update_thread : write failed, err = %d\n", -n); goto fail; } - n = read(sigio_private[0], &c, sizeof(c)); + n = os_read_file(sigio_private[0], &c, sizeof(c)); if(n != sizeof(c)){ - printk("update_thread : read failed, errno = %d\n", errno); + printk("update_thread : read failed, err = %d\n", -n); goto fail; } @@ -271,10 +260,10 @@ static void update_thread(void) if(write_sigio_pid != -1) os_kill_process(write_sigio_pid, 1); write_sigio_pid = -1; - close(sigio_private[0]); - close(sigio_private[1]); - close(write_sigio_fds[0]); - close(write_sigio_fds[1]); + os_close_file(sigio_private[0]); + os_close_file(sigio_private[1]); + os_close_file(write_sigio_fds[0]); + os_close_file(write_sigio_fds[1]); sigio_unlock(); set_signals(flags); } @@ -369,15 +358,15 @@ void write_sigio_workaround(void) goto out; err = os_pipe(write_sigio_fds, 1, 1); - if(err){ + if(err < 0){ printk("write_sigio_workaround - os_pipe 1 failed, " - "errno = %d\n", -err); + "err = %d\n", -err); goto out; } err = os_pipe(sigio_private, 1, 1); - if(err){ + if(err < 0){ printk("write_sigio_workaround - os_pipe 2 failed, " - "errno = %d\n", -err); + "err = %d\n", -err); goto out_close1; } if(setup_initial_poll(sigio_private[1])) @@ -399,11 +388,11 @@ void write_sigio_workaround(void) os_kill_process(write_sigio_pid, 1); write_sigio_pid = -1; out_close2: - close(sigio_private[0]); - close(sigio_private[1]); + os_close_file(sigio_private[0]); + os_close_file(sigio_private[1]); out_close1: - close(write_sigio_fds[0]); - close(write_sigio_fds[1]); + os_close_file(write_sigio_fds[0]); + os_close_file(write_sigio_fds[1]); sigio_unlock(); } @@ -412,10 +401,16 @@ int read_sigio_fd(int fd) int n; char c; - n = read(fd, &c, sizeof(c)); + n = os_read_file(fd, &c, sizeof(c)); if(n != sizeof(c)){ - printk("read_sigio_fd - read failed, errno = %d\n", errno); - return(-errno); + if(n < 0) { + printk("read_sigio_fd - read failed, err = %d\n", -n); + return(n); + } + else { + printk("read_sigio_fd - short read, bytes = %d\n", n); + return(-EIO); + } } return(n); } --- linux-2.6.9-rc1/arch/um/kernel/signal_kern.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/um/kernel/signal_kern.c 2004-09-02 20:51:51.000000000 -0700 @@ -31,17 +31,6 @@ EXPORT_SYMBOL(block_signals); EXPORT_SYMBOL(unblock_signals); -static void force_segv(int sig) -{ - if(sig == SIGSEGV){ - struct k_sigaction *ka; - - ka = ¤t->sig->action[SIGSEGV - 1]; - ka->sa.sa_handler = SIG_DFL; - } - force_sig(SIGSEGV, current); -} - #define _S(nr) (1<<((nr)-1)) #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) @@ -60,10 +49,10 @@ static int handle_signal(struct pt_regs int err, ret; ret = 0; + /* Always make any pending restarted system calls return -EINTR */ + current_thread_info()->restart_block.fn = do_no_restart_syscall; switch(error){ case -ERESTART_RESTARTBLOCK: - current_thread_info()->restart_block.fn = - do_no_restart_syscall; case -ERESTARTNOHAND: ret = -EINTR; break; @@ -124,27 +113,27 @@ static int handle_signal(struct pt_regs return(0); segv: - force_segv(signr); + force_sigsegv(signr, current); return(1); } static int kern_do_signal(struct pt_regs *regs, sigset_t *oldset, int error) { + struct k_sigaction ka_copy; siginfo_t info; - struct k_sigaction *ka; int err, sig; if (!oldset) oldset = ¤t->blocked; - sig = get_signal_to_deliver(&info, regs, NULL); + sig = get_signal_to_deliver(&info, &ka_copy, regs, NULL); if(sig == 0) return(0); /* Whee! Actually deliver the signal. */ - ka = ¤t->sig->action[sig -1 ]; - err = handle_signal(regs, sig, ka, &info, oldset, error); - if(!err) return(1); + err = handle_signal(regs, sig, &ka_copy, &info, oldset, error); + if(!err) + return(1); /* Did we come from a system call? */ if(PT_REGS_SYSCALL_NR(regs) >= 0){ @@ -201,7 +190,7 @@ int sys_sigsuspend(int history0, int his } } -int sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize) +int sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize) { sigset_t saveset, newset; @@ -227,20 +216,59 @@ int sys_rt_sigsuspend(sigset_t *unewset, } } +int sys_sigaction(int sig, const struct old_sigaction __user *act, + struct old_sigaction __user *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) { + old_sigset_t mask; + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + return ret; +} + +int sys_sigaltstack(const stack_t *uss, stack_t *uoss) +{ + return(do_sigaltstack(uss, uoss, PT_REGS_SP(¤t->thread.regs))); +} + +extern int userspace_pid[]; + static int copy_sc_from_user(struct pt_regs *to, void *from, struct arch_frame_data *arch) { int ret; ret = CHOOSE_MODE(copy_sc_from_user_tt(UPT_SC(&to->regs), from, arch), - copy_sc_from_user_skas(&to->regs, from)); + copy_sc_from_user_skas(userspace_pid[0], + &to->regs, from)); return(ret); } int sys_sigreturn(struct pt_regs regs) { - void *sc = sp_to_sc(PT_REGS_SP(¤t->thread.regs)); - void *mask = sp_to_mask(PT_REGS_SP(¤t->thread.regs)); + void __user *sc = sp_to_sc(PT_REGS_SP(¤t->thread.regs)); + void __user *mask = sp_to_mask(PT_REGS_SP(¤t->thread.regs)); int sig_size = (_NSIG_WORDS - 1) * sizeof(unsigned long); spin_lock_irq(¤t->sighand->siglock); @@ -257,8 +285,8 @@ int sys_sigreturn(struct pt_regs regs) int sys_rt_sigreturn(struct pt_regs regs) { - struct ucontext *uc = sp_to_uc(PT_REGS_SP(¤t->thread.regs)); - void *fp; + unsigned long sp = PT_REGS_SP(¤t->thread.regs); + struct ucontext __user *uc = sp_to_uc(sp); int sig_size = _NSIG_WORDS * sizeof(unsigned long); spin_lock_irq(¤t->sighand->siglock); @@ -266,7 +294,6 @@ int sys_rt_sigreturn(struct pt_regs regs sigdelsetmask(¤t->blocked, ~_BLOCKABLE); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - fp = (void *) (((unsigned long) uc) + sizeof(struct ucontext)); copy_sc_from_user(¤t->thread.regs, &uc->uc_mcontext, &signal_frame_si.common.arch); return(PT_REGS_SYSCALL_RET(¤t->thread.regs)); --- linux-2.6.9-rc1/arch/um/kernel/skas/exec_user.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/um/kernel/skas/exec_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -11,6 +11,7 @@ #include #include "user.h" #include "kern_util.h" +#include "user_util.h" #include "os.h" #include "time_user.h" @@ -26,7 +27,7 @@ static int user_thread_tramp(void *arg) int user_thread(unsigned long stack, int flags) { - int pid, status; + int pid, status, err; pid = clone(user_thread_tramp, (void *) stack_sp(stack), flags | CLONE_FILES | SIGCHLD, NULL); @@ -35,7 +36,8 @@ int user_thread(unsigned long stack, int return(pid); } - if(waitpid(pid, &status, WUNTRACED) < 0){ + CATCH_EINTR(err = waitpid(pid, &status, WUNTRACED)); + if(err < 0){ printk("user_thread - waitpid failed, errno = %d\n", errno); return(-errno); } --- linux-2.6.9-rc1/arch/um/kernel/skas/include/mode.h 2004-08-24 00:02:48.000000000 -0700 +++ 25/arch/um/kernel/skas/include/mode.h 2004-09-02 20:51:51.000000000 -0700 @@ -12,14 +12,16 @@ extern unsigned long exec_fpx_regs[]; extern int have_fpx_regs; extern void user_time_init_skas(void); -extern int copy_sc_from_user_skas(union uml_pt_regs *regs, void *from_ptr); -extern int copy_sc_to_user_skas(void *to_ptr, void *fp, +extern int copy_sc_from_user_skas(int pid, union uml_pt_regs *regs, + void *from_ptr); +extern int copy_sc_to_user_skas(int pid, void *to_ptr, void *fp, union uml_pt_regs *regs, unsigned long fault_addr, int fault_type); extern void sig_handler_common_skas(int sig, void *sc_ptr); extern void halt_skas(void); extern void reboot_skas(void); extern void kill_off_processes_skas(void); +extern int is_skas_winch(int pid, int fd, void *data); #endif --- linux-2.6.9-rc1/arch/um/kernel/skas/include/ptrace-skas.h 2004-08-24 00:01:55.000000000 -0700 +++ 25/arch/um/kernel/skas/include/ptrace-skas.h 2004-09-02 20:51:51.000000000 -0700 @@ -10,6 +10,16 @@ #ifdef UML_CONFIG_MODE_SKAS +/* syscall emulation path in ptrace */ + +#ifndef PTRACE_SYSEMU +#define PTRACE_SYSEMU 31 +#endif + +void set_using_sysemu(int value); +int get_using_sysemu(void); +extern int sysemu_supported; + #include "skas_ptregs.h" #define HOST_FRAME_SIZE 17 --- linux-2.6.9-rc1/arch/um/kernel/skas/include/skas.h 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/um/kernel/skas/include/skas.h 2004-09-02 20:51:51.000000000 -0700 @@ -8,7 +8,7 @@ #include "sysdep/ptrace.h" -extern int userspace_pid; +extern int userspace_pid[]; extern void switch_threads(void *me, void *next); extern void thread_wait(void *sw, void *fb); @@ -32,7 +32,7 @@ extern int singlestepping_skas(void); extern int new_mm(int from); extern void save_registers(union uml_pt_regs *regs); extern void restore_registers(union uml_pt_regs *regs); -extern void start_userspace(void); +extern void start_userspace(int cpu); extern void init_registers(int pid); #endif --- linux-2.6.9-rc1/arch/um/kernel/skas/include/uaccess.h 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/um/kernel/skas/include/uaccess.h 2004-09-02 20:51:51.000000000 -0700 @@ -6,20 +6,12 @@ #ifndef __SKAS_UACCESS_H #define __SKAS_UACCESS_H -#include "linux/string.h" -#include "linux/sched.h" -#include "linux/err.h" -#include "asm/processor.h" -#include "asm/pgtable.h" #include "asm/errno.h" -#include "asm/current.h" -#include "asm/a.out.h" -#include "kern_util.h" #define access_ok_skas(type, addr, size) \ ((segment_eq(get_fs(), KERNEL_DS)) || \ (((unsigned long) (addr) < TASK_SIZE) && \ - ((unsigned long) (addr) + (size) < TASK_SIZE))) + ((unsigned long) (addr) + (size) <= TASK_SIZE))) static inline int verify_area_skas(int type, const void * addr, unsigned long size) @@ -27,197 +19,12 @@ static inline int verify_area_skas(int t return(access_ok_skas(type, addr, size) ? 0 : -EFAULT); } -static inline unsigned long maybe_map(unsigned long virt, int is_write) -{ - pte_t pte; - - void *phys = um_virt_to_phys(current, virt, &pte); - int dummy_code; - - if(IS_ERR(phys) || (is_write && !pte_write(pte))){ - if(handle_page_fault(virt, 0, is_write, 0, &dummy_code)) - return(0); - phys = um_virt_to_phys(current, virt, NULL); - } - return((unsigned long) __va((unsigned long) phys)); -} - -static inline int buffer_op(unsigned long addr, int len, - int (*op)(unsigned long addr, int len, void *arg), - void *arg) -{ - int size = min(PAGE_ALIGN(addr) - addr, (unsigned long) len); - int remain = len, n; - - n = (*op)(addr, size, arg); - if(n != 0) - return(n < 0 ? remain : 0); - - addr += size; - remain -= size; - if(remain == 0) - return(0); - - while(addr < ((addr + remain) & PAGE_MASK)){ - n = (*op)(addr, PAGE_SIZE, arg); - if(n != 0) - return(n < 0 ? remain : 0); - - addr += PAGE_SIZE; - remain -= PAGE_SIZE; - } - if(remain == 0) - return(0); - - n = (*op)(addr, remain, arg); - if(n != 0) - return(n < 0 ? remain : 0); - return(0); -} - -static inline int copy_chunk_from_user(unsigned long from, int len, void *arg) -{ - unsigned long *to_ptr = arg, to = *to_ptr; - - from = maybe_map(from, 0); - if(from == 0) - return(-1); - - memcpy((void *) to, (void *) from, len); - *to_ptr += len; - return(0); -} - -static inline int copy_from_user_skas(void *to, const void *from, int n) -{ - if(segment_eq(get_fs(), KERNEL_DS)){ - memcpy(to, from, n); - return(0); - } - - return(access_ok_skas(VERIFY_READ, from, n) ? - buffer_op((unsigned long) from, n, copy_chunk_from_user, &to) : - n); -} - -static inline int copy_chunk_to_user(unsigned long to, int len, void *arg) -{ - unsigned long *from_ptr = arg, from = *from_ptr; - - to = maybe_map(to, 1); - if(to == 0) - return(-1); - - memcpy((void *) to, (void *) from, len); - *from_ptr += len; - return(0); -} - -static inline int copy_to_user_skas(void *to, const void *from, int n) -{ - if(segment_eq(get_fs(), KERNEL_DS)){ - memcpy(to, from, n); - return(0); - } - - return(access_ok_skas(VERIFY_WRITE, to, n) ? - buffer_op((unsigned long) to, n, copy_chunk_to_user, &from) : - n); -} - -static inline int strncpy_chunk_from_user(unsigned long from, int len, - void *arg) -{ - char **to_ptr = arg, *to = *to_ptr; - int n; - - from = maybe_map(from, 0); - if(from == 0) - return(-1); - - strncpy(to, (void *) from, len); - n = strnlen(to, len); - *to_ptr += n; - - if(n < len) - return(1); - return(0); -} - -static inline int strncpy_from_user_skas(char *dst, const char *src, int count) -{ - int n; - char *ptr = dst; - - if(segment_eq(get_fs(), KERNEL_DS)){ - strncpy(dst, src, count); - return(strnlen(dst, count)); - } - - if(!access_ok_skas(VERIFY_READ, src, 1)) - return(-EFAULT); - - n = buffer_op((unsigned long) src, count, strncpy_chunk_from_user, - &ptr); - if(n != 0) - return(-EFAULT); - return(strnlen(dst, count)); -} - -static inline int clear_chunk(unsigned long addr, int len, void *unused) -{ - addr = maybe_map(addr, 1); - if(addr == 0) - return(-1); - - memset((void *) addr, 0, len); - return(0); -} - -static inline int __clear_user_skas(void *mem, int len) -{ - return(buffer_op((unsigned long) mem, len, clear_chunk, NULL)); -} - -static inline int clear_user_skas(void *mem, int len) -{ - if(segment_eq(get_fs(), KERNEL_DS)){ - memset(mem, 0, len); - return(0); - } - - return(access_ok_skas(VERIFY_WRITE, mem, len) ? - buffer_op((unsigned long) mem, len, clear_chunk, NULL) : len); -} - -static inline int strnlen_chunk(unsigned long str, int len, void *arg) -{ - int *len_ptr = arg, n; - - str = maybe_map(str, 0); - if(str == 0) - return(-1); - - n = strnlen((void *) str, len); - *len_ptr += n; - - if(n < len) - return(1); - return(0); -} - -static inline int strnlen_user_skas(const void *str, int len) -{ - int count = 0, n; - - if(segment_eq(get_fs(), KERNEL_DS)) - return(strnlen(str, len) + 1); - - n = buffer_op((unsigned long) str, len, strnlen_chunk, &count); - if(n == 0) - return(count + 1); - return(-EFAULT); -} +extern int copy_from_user_skas(void *to, const void *from, int n); +extern int copy_to_user_skas(void *to, const void *from, int n); +extern int strncpy_from_user_skas(char *dst, const char *src, int count); +extern int __clear_user_skas(void *mem, int len); +extern int clear_user_skas(void *mem, int len); +extern int strnlen_user_skas(const void *str, int len); #endif --- linux-2.6.9-rc1/arch/um/kernel/skas/Makefile 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/um/kernel/skas/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -5,20 +5,24 @@ obj-y = exec_kern.o exec_user.o mem.o mem_user.o mmu.o process.o \ process_kern.o syscall_kern.o syscall_user.o time.o tlb.o trap_user.o \ - sys-$(SUBARCH)/ + uaccess.o sys-$(SUBARCH)/ + +hostprogs-y := util/mk_ptregs +clean-files := include/skas_ptregs.h USER_OBJS = $(filter %_user.o,$(obj-y)) process.o time.o USER_OBJS := $(foreach file,$(USER_OBJS),$(obj)/$(file)) -include/skas_ptregs.h : util/mk_ptregs - util/mk_ptregs > $@ - -util/mk_ptregs : - $(MAKE) -C util +$(TOPDIR)/arch/um/include/skas_ptregs.h : $(src)/util/mk_ptregs + @echo -n ' Generating $@' + @$< > $@.tmp + @if [ -r $@ ] && cmp -s $@ $@.tmp; then \ + echo ' (unchanged)'; \ + rm -f $@.tmp; \ + else \ + echo ' (updated)'; \ + mv -f $@.tmp $@; \ + fi $(USER_OBJS) : %.o: %.c $(CC) $(CFLAGS_$(notdir $@)) $(USER_CFLAGS) -c -o $@ $< - -clean : - $(MAKE) -C util clean - $(RM) -f include/skas_ptregs.h --- linux-2.6.9-rc1/arch/um/kernel/skas/mem_user.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/arch/um/kernel/skas/mem_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -7,6 +7,7 @@ #include #include #include "mem_user.h" +#include "mem.h" #include "user.h" #include "os.h" #include "proc_mm.h" @@ -15,12 +16,12 @@ void map(int fd, unsigned long virt, uns int r, int w, int x) { struct proc_mm_op map; - struct mem_region *region; - int prot, n; + __u64 offset; + int prot, n, phys_fd; prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) | (x ? PROT_EXEC : 0); - region = phys_region(phys); + phys_fd = phys_mapping(phys, &offset); map = ((struct proc_mm_op) { .op = MM_MMAP, .u = @@ -30,12 +31,12 @@ void map(int fd, unsigned long virt, uns .prot = prot, .flags = MAP_SHARED | MAP_FIXED, - .fd = region->fd, - .offset = phys_offset(phys) + .fd = phys_fd, + .offset = offset } } } ); n = os_write_file(fd, &map, sizeof(map)); if(n != sizeof(map)) - printk("map : /proc/mm map failed, errno = %d\n", errno); + printk("map : /proc/mm map failed, err = %d\n", -n); } int unmap(int fd, void *addr, int len) @@ -49,8 +50,13 @@ int unmap(int fd, void *addr, int len) { .addr = (unsigned long) addr, .len = len } } } ); n = os_write_file(fd, &unmap, sizeof(unmap)); - if((n != 0) && (n != sizeof(unmap))) - return(-errno); + if(n != sizeof(unmap)) { + if(n < 0) + return(n); + else if(n > 0) + return(-EIO); + } + return(0); } @@ -71,11 +77,15 @@ int protect(int fd, unsigned long addr, .prot = prot } } } ); n = os_write_file(fd, &protect, sizeof(protect)); - if((n != 0) && (n != sizeof(protect))){ + if(n != sizeof(protect)) { + if(n == 0) return(0); + if(must_succeed) - panic("protect failed, errno = %d", errno); - return(-errno); + panic("protect failed, err = %d", -n); + + return(-EIO); } + return(0); } --- linux-2.6.9-rc1/arch/um/kernel/skas/mmu.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/arch/um/kernel/skas/mmu.c 2004-09-02 20:51:51.000000000 -0700 @@ -22,9 +22,11 @@ int init_new_context_skas(struct task_st else from = -1; mm->context.skas.mm_fd = new_mm(from); - if(mm->context.skas.mm_fd < 0) - panic("init_new_context_skas - new_mm failed, errno = %d\n", - mm->context.skas.mm_fd); + if(mm->context.skas.mm_fd < 0){ + printk("init_new_context_skas - new_mm failed, errno = %d\n", + mm->context.skas.mm_fd); + return(mm->context.skas.mm_fd); + } return(0); } --- linux-2.6.9-rc1/arch/um/kernel/skas/process.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/um/kernel/skas/process.c 2004-09-02 20:51:51.000000000 -0700 @@ -4,6 +4,7 @@ */ #include +#include #include #include #include @@ -24,6 +25,19 @@ #include "os.h" #include "proc_mm.h" #include "skas_ptrace.h" +#include "chan_user.h" +#include "signal_user.h" + +int is_skas_winch(int pid, int fd, void *data) +{ + if(pid != getpid()) + return(0); + + register_winch_irq(-1, fd, -1, data); + return(1); +} + +/* These are set once at boot time and not changed thereafter */ unsigned long exec_regs[FRAME_SIZE]; unsigned long exec_fp_regs[HOST_FP_SIZE]; @@ -43,37 +57,39 @@ static void handle_segv(int pid) segv(fault.addr, 0, FAULT_WRITE(fault.is_write), 1, NULL); } -static void handle_trap(int pid, union uml_pt_regs *regs) +/*To use the same value of using_sysemu as the caller, ask it that value (in local_using_sysemu)*/ +static void handle_trap(int pid, union uml_pt_regs *regs, int local_using_sysemu) { int err, syscall_nr, status; syscall_nr = PT_SYSCALL_NR(regs->skas.regs); + UPT_SYSCALL_NR(regs) = syscall_nr; if(syscall_nr < 1){ relay_signal(SIGTRAP, regs); return; } - UPT_SYSCALL_NR(regs) = syscall_nr; - err = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_NR_OFFSET, __NR_getpid); - if(err < 0) - panic("handle_trap - nullifying syscall failed errno = %d\n", - errno); + if (!local_using_sysemu) + { + err = ptrace(PTRACE_POKEUSER, pid, PT_SYSCALL_NR_OFFSET, __NR_getpid); + if(err < 0) + panic("handle_trap - nullifying syscall failed errno = %d\n", + errno); + + err = ptrace(PTRACE_SYSCALL, pid, 0, 0); + if(err < 0) + panic("handle_trap - continuing to end of syscall failed, " + "errno = %d\n", errno); - err = ptrace(PTRACE_SYSCALL, pid, 0, 0); - if(err < 0) - panic("handle_trap - continuing to end of syscall failed, " - "errno = %d\n", errno); - - err = waitpid(pid, &status, WUNTRACED); - if((err < 0) || !WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP)) - panic("handle_trap - failed to wait at end of syscall, " - "errno = %d, status = %d\n", errno, status); + CATCH_EINTR(err = waitpid(pid, &status, WUNTRACED)); + if((err < 0) || !WIFSTOPPED(status) || (WSTOPSIG(status) != SIGTRAP)) + panic("handle_trap - failed to wait at end of syscall, " + "errno = %d, status = %d\n", errno, status); + } handle_syscall(regs); } -int userspace_pid; - static int userspace_tramp(void *arg) { init_new_thread_signals(0); @@ -83,7 +99,11 @@ static int userspace_tramp(void *arg) return(0); } -void start_userspace(void) +/* Each element set once, and only accessed by a single processor anyway */ +#define NR_CPUS 1 +int userspace_pid[NR_CPUS]; + +void start_userspace(int cpu) { void *stack; unsigned long sp; @@ -101,7 +121,7 @@ void start_userspace(void) panic("start_userspace : clone failed, errno = %d", errno); do { - n = waitpid(pid, &status, WUNTRACED); + CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); if(n < 0) panic("start_userspace : wait failed, errno = %d", errno); @@ -114,21 +134,27 @@ void start_userspace(void) if(munmap(stack, PAGE_SIZE) < 0) panic("start_userspace : munmap failed, errno = %d\n", errno); - userspace_pid = pid; + userspace_pid[cpu] = pid; } void userspace(union uml_pt_regs *regs) { - int err, status, op; + int err, status, op, pid = userspace_pid[0]; + int local_using_sysemu; /*To prevent races if using_sysemu changes under us.*/ restore_registers(regs); - err = ptrace(PTRACE_SYSCALL, userspace_pid, 0, 0); + local_using_sysemu = get_using_sysemu(); + + if (local_using_sysemu) + err = ptrace(PTRACE_SYSEMU, pid, 0, 0); + else + err = ptrace(PTRACE_SYSCALL, pid, 0, 0); if(err) - panic("userspace - PTRACE_SYSCALL failed, errno = %d\n", - errno); + panic("userspace - PTRACE_%s failed, errno = %d\n", + local_using_sysemu ? "SYSEMU" : "SYSCALL", errno); while(1){ - err = waitpid(userspace_pid, &status, WUNTRACED); + CATCH_EINTR(err = waitpid(pid, &status, WUNTRACED)); if(err < 0) panic("userspace - waitpid failed, errno = %d\n", errno); @@ -139,16 +165,17 @@ void userspace(union uml_pt_regs *regs) if(WIFSTOPPED(status)){ switch(WSTOPSIG(status)){ case SIGSEGV: - handle_segv(userspace_pid); + handle_segv(pid); break; case SIGTRAP: - handle_trap(userspace_pid, regs); + handle_trap(pid, regs, local_using_sysemu); break; case SIGIO: case SIGVTALRM: case SIGILL: case SIGBUS: case SIGFPE: + case SIGWINCH: user_signal(WSTOPSIG(status), regs); break; default: @@ -160,25 +187,46 @@ void userspace(union uml_pt_regs *regs) restore_registers(regs); - op = singlestepping_skas() ? PTRACE_SINGLESTEP : - PTRACE_SYSCALL; - err = ptrace(op, userspace_pid, 0, 0); + /*Now we ended the syscall, so re-read local_using_sysemu.*/ + local_using_sysemu = get_using_sysemu(); + + if (local_using_sysemu) + op = singlestepping_skas() ? PTRACE_SINGLESTEP : + PTRACE_SYSEMU; + else + op = singlestepping_skas() ? PTRACE_SINGLESTEP : + PTRACE_SYSCALL; + + err = ptrace(op, pid, 0, 0); if(err) - panic("userspace - PTRACE_SYSCALL failed, " - "errno = %d\n", errno); + panic("userspace - PTRACE_%s failed, " + "errno = %d\n", + local_using_sysemu ? "SYSEMU" : "SYSCALL", errno); } } void new_thread(void *stack, void **switch_buf_ptr, void **fork_buf_ptr, void (*handler)(int)) { + unsigned long flags; jmp_buf switch_buf, fork_buf; *switch_buf_ptr = &switch_buf; *fork_buf_ptr = &fork_buf; - if(setjmp(fork_buf) == 0) + /* Somewhat subtle - siglongjmp restores the signal mask before doing + * the longjmp. This means that when jumping from one stack to another + * when the target stack has interrupts enabled, an interrupt may occur + * on the source stack. This is bad when starting up a process because + * it's not supposed to get timer ticks until it has been scheduled. + * So, we disable interrupts around the sigsetjmp to ensure that + * they can't happen until we get back here where they are safe. + */ + flags = get_signals(); + block_signals(); + if(sigsetjmp(fork_buf, 1) == 0) new_thread_proc(stack, handler); + set_signals(flags); remove_sigstack(); } @@ -189,16 +237,16 @@ void thread_wait(void *sw, void *fb) *switch_buf = &buf; fork_buf = fb; - if(setjmp(buf) == 0) - longjmp(*fork_buf, 1); + if(sigsetjmp(buf, 1) == 0) + siglongjmp(*fork_buf, 1); } -static int move_registers(int int_op, int fp_op, union uml_pt_regs *regs, - unsigned long *fp_regs) +static int move_registers(int pid, int int_op, int fp_op, + union uml_pt_regs *regs, unsigned long *fp_regs) { - if(ptrace(int_op, userspace_pid, 0, regs->skas.regs) < 0) + if(ptrace(int_op, pid, 0, regs->skas.regs) < 0) return(-errno); - if(ptrace(fp_op, userspace_pid, 0, fp_regs) < 0) + if(ptrace(fp_op, pid, 0, fp_regs) < 0) return(-errno); return(0); } @@ -217,10 +265,11 @@ void save_registers(union uml_pt_regs *r fp_regs = regs->skas.fp; } - err = move_registers(PTRACE_GETREGS, fp_op, regs, fp_regs); + err = move_registers(userspace_pid[0], PTRACE_GETREGS, fp_op, regs, + fp_regs); if(err) panic("save_registers - saving registers failed, errno = %d\n", - err); + -err); } void restore_registers(union uml_pt_regs *regs) @@ -237,10 +286,11 @@ void restore_registers(union uml_pt_regs fp_regs = regs->skas.fp; } - err = move_registers(PTRACE_SETREGS, fp_op, regs, fp_regs); + err = move_registers(userspace_pid[0], PTRACE_SETREGS, fp_op, regs, + fp_regs); if(err) panic("restore_registers - saving registers failed, " - "errno = %d\n", err); + "errno = %d\n", -err); } void switch_threads(void *me, void *next) @@ -248,8 +298,8 @@ void switch_threads(void *me, void *next jmp_buf my_buf, **me_ptr = me, *next_buf = next; *me_ptr = &my_buf; - if(setjmp(my_buf) == 0) - longjmp(*next_buf, 1); + if(sigsetjmp(my_buf, 1) == 0) + siglongjmp(*next_buf, 1); } static jmp_buf initial_jmpbuf; @@ -265,14 +315,14 @@ int start_idle_thread(void *stack, void int n; *fork_buf_ptr = &initial_jmpbuf; - n = setjmp(initial_jmpbuf); + n = sigsetjmp(initial_jmpbuf, 1); if(n == 0) new_thread_proc((void *) stack, new_thread_handler); else if(n == 1) remove_sigstack(); else if(n == 2){ (*cb_proc)(cb_arg); - longjmp(*cb_back, 1); + siglongjmp(*cb_back, 1); } else if(n == 3){ kmalloc_ok = 0; @@ -282,7 +332,7 @@ int start_idle_thread(void *stack, void kmalloc_ok = 0; return(1); } - longjmp(**switch_buf, 1); + siglongjmp(**switch_buf, 1); } void remove_sigstack(void) @@ -304,8 +354,8 @@ void initial_thread_cb_skas(void (*proc) cb_back = &here; block_signals(); - if(setjmp(here) == 0) - longjmp(initial_jmpbuf, 2); + if(sigsetjmp(here, 1) == 0) + siglongjmp(initial_jmpbuf, 2); unblock_signals(); cb_proc = NULL; @@ -316,22 +366,23 @@ void initial_thread_cb_skas(void (*proc) void halt_skas(void) { block_signals(); - longjmp(initial_jmpbuf, 3); + siglongjmp(initial_jmpbuf, 3); } void reboot_skas(void) { block_signals(); - longjmp(initial_jmpbuf, 4); + siglongjmp(initial_jmpbuf, 4); } int new_mm(int from) { struct proc_mm_op copy; - int n, fd = os_open_file("/proc/mm", of_write(OPENFLAGS()), 0); + int n, fd = os_open_file("/proc/mm", + of_cloexec(of_write(OPENFLAGS())), 0); if(fd < 0) - return(-errno); + return(fd); if(from != -1){ copy = ((struct proc_mm_op) { .op = MM_COPY_SEGMENTS, @@ -340,8 +391,9 @@ int new_mm(int from) n = os_write_file(fd, ©, sizeof(copy)); if(n != sizeof(copy)) printk("new_mm : /proc/mm copy_segments failed, " - "errno = %d\n", errno); + "err = %d\n", -n); } + return(fd); } @@ -349,7 +401,8 @@ void switch_mm_skas(int mm_fd) { int err; - err = ptrace(PTRACE_SWITCH_MM, userspace_pid, 0, mm_fd); +#warning need cpu pid in switch_mm_skas + err = ptrace(PTRACE_SWITCH_MM, userspace_pid[0], 0, mm_fd); if(err) panic("switch_mm_skas - PTRACE_SWITCH_MM failed, errno = %d\n", errno); @@ -357,7 +410,8 @@ void switch_mm_skas(int mm_fd) void kill_off_processes_skas(void) { - os_kill_process(userspace_pid, 1); +#warning need to loop over userspace_pids in kill_off_processes_skas + os_kill_process(userspace_pid[0], 1); } void init_registers(int pid) --- linux-2.6.9-rc1/arch/um/kernel/skas/process_kern.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/arch/um/kernel/skas/process_kern.c 2004-09-02 20:51:51.000000000 -0700 @@ -6,6 +6,12 @@ #include "linux/sched.h" #include "linux/slab.h" #include "linux/ptrace.h" +#include "linux/proc_fs.h" +#include "linux/file.h" +#include "linux/errno.h" +#include "linux/init.h" +#include "asm/uaccess.h" +#include "asm/atomic.h" #include "kern_util.h" #include "time_user.h" #include "signal_user.h" @@ -17,6 +23,61 @@ #include "kern.h" #include "mode.h" +static atomic_t using_sysemu; +int sysemu_supported; + +void set_using_sysemu(int value) +{ + atomic_set(&using_sysemu, sysemu_supported && value); +} + +int get_using_sysemu(void) +{ + return atomic_read(&using_sysemu); +} + +int proc_read_sysemu(char *buf, char **start, off_t offset, int size,int *eof, void *data) +{ + if (snprintf(buf, size, "%d\n", get_using_sysemu()) < size) /*No overflow*/ + *eof = 1; + + return strlen(buf); +} + +int proc_write_sysemu(struct file *file,const char *buf, unsigned long count,void *data) +{ + char tmp[2]; + + if (copy_from_user(tmp, buf, 1)) + return -EFAULT; + + if (tmp[0] == '0' || tmp[0] == '1') + set_using_sysemu(tmp[0] - '0'); + return count; /*We use the first char, but pretend to write everything*/ +} + +int __init make_proc_sysemu(void) +{ + struct proc_dir_entry *ent; + if (mode_tt || !sysemu_supported) + return 0; + + ent = create_proc_entry("sysemu", 0600, &proc_root); + + if (ent == NULL) + { + printk("Failed to register /proc/sysemu\n"); + return(0); + } + + ent->read_proc = proc_read_sysemu; + ent->write_proc = proc_write_sysemu; + + return 0; +} + +late_initcall(make_proc_sysemu); + int singlestepping_skas(void) { int ret = current->ptrace & PT_DTRACE; @@ -61,11 +122,13 @@ void new_thread_handler(int sig) thread_wait(¤t->thread.mode.skas.switch_buf, current->thread.mode.skas.fork_buf); -#ifdef CONFIG_SMP - schedule_tail(NULL); -#endif + if(current->thread.prev_sched != NULL) + schedule_tail(current->thread.prev_sched); current->thread.prev_sched = NULL; + /* The return value is 1 if the kernel thread execs a process, + * 0 if it just exits + */ n = run_kernel_thread(fn, arg, ¤t->thread.exec_buf); if(n == 1) userspace(¤t->thread.regs.regs); @@ -93,11 +156,11 @@ void fork_handler(int sig) current->thread.mode.skas.fork_buf); force_flush_all(); -#ifdef CONFIG_SMP + if(current->thread.prev_sched == NULL) + panic("blech"); + schedule_tail(current->thread.prev_sched); -#endif current->thread.prev_sched = NULL; - unblock_signals(); userspace(¤t->thread.regs.regs); } @@ -136,7 +199,7 @@ int copy_thread_skas(int nr, unsigned lo void init_idle_skas(void) { - cpu_tasks[current->thread_info->cpu].pid = os_getpid(); + cpu_tasks[current_thread->cpu].pid = os_getpid(); default_idle(); } @@ -160,11 +223,11 @@ static int start_kernel_proc(void *unuse int start_uml_skas(void) { - start_userspace(); + start_userspace(0); capture_signal_stack(); + uml_idle_timer(); init_new_thread_signals(1); - idle_timer(); init_task.thread.request.u.thread.proc = start_kernel_proc; init_task.thread.request.u.thread.arg = NULL; @@ -175,12 +238,14 @@ int start_uml_skas(void) int external_pid_skas(struct task_struct *task) { - return(userspace_pid); +#warning Need to look up userspace_pid by cpu + return(userspace_pid[0]); } int thread_pid_skas(struct task_struct *task) { - return(userspace_pid); +#warning Need to look up userspace_pid by cpu + return(userspace_pid[0]); } /* --- linux-2.6.9-rc1/arch/um/kernel/skas/syscall_kern.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/um/kernel/skas/syscall_kern.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com) * Licensed under the GPL */ --- linux-2.6.9-rc1/arch/um/kernel/skas/syscall_user.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/um/kernel/skas/syscall_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -22,7 +22,7 @@ void handle_syscall(union uml_pt_regs *r index = record_syscall_start(UPT_SYSCALL_NR(regs)); - syscall_trace(); + syscall_trace(regs, 1); result = execute_syscall(regs); REGS_SET_SYSCALL_RETURN(regs->skas.regs, result); @@ -30,7 +30,7 @@ void handle_syscall(union uml_pt_regs *r (result == -ERESTARTNOINTR)) do_signal(result); - syscall_trace(); + syscall_trace(regs, 0); record_syscall_end(index, result); } --- linux-2.6.9-rc1/arch/um/kernel/skas/sys-i386/Makefile 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/um/kernel/skas/sys-i386/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -10,5 +10,3 @@ USER_OBJS := $(foreach file,$(USER_OBJS) $(USER_OBJS) : %.o: %.c $(CC) $(CFLAGS_$(notdir $@)) $(USER_CFLAGS) -c -o $@ $< - -clean : --- linux-2.6.9-rc1/arch/um/kernel/skas/sys-i386/sigcontext.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/arch/um/kernel/skas/sys-i386/sigcontext.c 2004-09-02 20:51:51.000000000 -0700 @@ -12,10 +12,9 @@ #include "kern_util.h" #include "user.h" #include "sigcontext.h" +#include "mode.h" -extern int userspace_pid; - -int copy_sc_from_user_skas(union uml_pt_regs *regs, void *from_ptr) +int copy_sc_from_user_skas(int pid, union uml_pt_regs *regs, void *from_ptr) { struct sigcontext sc, *from = from_ptr; unsigned long fpregs[FP_FRAME_SIZE]; @@ -41,13 +40,12 @@ int copy_sc_from_user_skas(union uml_pt_ regs->skas.regs[EIP] = sc.eip; regs->skas.regs[CS] = sc.cs; regs->skas.regs[EFL] = sc.eflags; - regs->skas.regs[UESP] = sc.esp_at_signal; regs->skas.regs[SS] = sc.ss; regs->skas.fault_addr = sc.cr2; regs->skas.fault_type = FAULT_WRITE(sc.err); regs->skas.trap_type = sc.trapno; - err = ptrace(PTRACE_SETFPREGS, userspace_pid, 0, fpregs); + err = ptrace(PTRACE_SETFPREGS, pid, 0, fpregs); if(err < 0){ printk("copy_sc_to_user - PTRACE_SETFPREGS failed, " "errno = %d\n", errno); @@ -57,8 +55,9 @@ int copy_sc_from_user_skas(union uml_pt_ return(0); } -int copy_sc_to_user_skas(void *to_ptr, void *fp, union uml_pt_regs *regs, - unsigned long fault_addr, int fault_type) +int copy_sc_to_user_skas(int pid, void *to_ptr, void *fp, + union uml_pt_regs *regs, unsigned long fault_addr, + int fault_type) { struct sigcontext sc, *to = to_ptr; struct _fpstate *to_fp; @@ -86,7 +85,7 @@ int copy_sc_to_user_skas(void *to_ptr, v sc.err = TO_SC_ERR(fault_type); sc.trapno = regs->skas.trap_type; - err = ptrace(PTRACE_GETFPREGS, userspace_pid, 0, fpregs); + err = ptrace(PTRACE_GETFPREGS, pid, 0, fpregs); if(err < 0){ printk("copy_sc_to_user - PTRACE_GETFPREGS failed, " "errno = %d\n", errno); --- linux-2.6.9-rc1/arch/um/kernel/skas/trap_user.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/um/kernel/skas/trap_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com) * Licensed under the GPL */ @@ -35,14 +35,10 @@ void sig_handler_common_skas(int sig, vo errno = save_errno; } -extern int missed_ticks[]; - void user_signal(int sig, union uml_pt_regs *regs) { struct signal_info *info; - if(sig == SIGVTALRM) - missed_ticks[cpu()]++; regs->skas.is_user = 1; regs->skas.fault_addr = 0; regs->skas.fault_type = 0; --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/um/kernel/skas/uaccess.c 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include "linux/stddef.h" +#include "linux/kernel.h" +#include "linux/string.h" +#include "linux/fs.h" +#include "linux/highmem.h" +#include "asm/page.h" +#include "asm/pgtable.h" +#include "asm/uaccess.h" +#include "kern_util.h" + +extern void *um_virt_to_phys(struct task_struct *task, unsigned long addr, + pte_t *pte_out); + +static unsigned long maybe_map(unsigned long virt, int is_write) +{ + pte_t pte; + int err; + + void *phys = um_virt_to_phys(current, virt, &pte); + int dummy_code; + + if(IS_ERR(phys) || (is_write && !pte_write(pte))){ + err = handle_page_fault(virt, 0, is_write, 1, &dummy_code); + if(err) + return(0); + phys = um_virt_to_phys(current, virt, NULL); + } + return((unsigned long) phys); +} + +static int do_op(unsigned long addr, int len, int is_write, + int (*op)(unsigned long addr, int len, void *arg), void *arg) +{ + struct page *page; + int n; + + addr = maybe_map(addr, is_write); + if(addr == -1) + return(-1); + + page = phys_to_page(addr); + addr = (unsigned long) kmap(page) + (addr & ~PAGE_MASK); + n = (*op)(addr, len, arg); + kunmap(page); + + return(n); +} + +static int buffer_op(unsigned long addr, int len, int is_write, + int (*op)(unsigned long addr, int len, void *arg), + void *arg) +{ + int size = min(PAGE_ALIGN(addr) - addr, (unsigned long) len); + int remain = len, n; + + n = do_op(addr, size, is_write, op, arg); + if(n != 0) + return(n < 0 ? remain : 0); + + addr += size; + remain -= size; + if(remain == 0) + return(0); + + while(addr < ((addr + remain) & PAGE_MASK)){ + n = do_op(addr, PAGE_SIZE, is_write, op, arg); + if(n != 0) + return(n < 0 ? remain : 0); + + addr += PAGE_SIZE; + remain -= PAGE_SIZE; + } + if(remain == 0) + return(0); + + n = do_op(addr, remain, is_write, op, arg); + if(n != 0) + return(n < 0 ? remain : 0); + return(0); +} + +static int copy_chunk_from_user(unsigned long from, int len, void *arg) +{ + unsigned long *to_ptr = arg, to = *to_ptr; + + memcpy((void *) to, (void *) from, len); + *to_ptr += len; + return(0); +} + +int copy_from_user_skas(void *to, const void *from, int n) +{ + if(segment_eq(get_fs(), KERNEL_DS)){ + memcpy(to, from, n); + return(0); + } + + return(access_ok_skas(VERIFY_READ, from, n) ? + buffer_op((unsigned long) from, n, 0, copy_chunk_from_user, &to): + n); +} + +static int copy_chunk_to_user(unsigned long to, int len, void *arg) +{ + unsigned long *from_ptr = arg, from = *from_ptr; + + memcpy((void *) to, (void *) from, len); + *from_ptr += len; + return(0); +} + +int copy_to_user_skas(void *to, const void *from, int n) +{ + if(segment_eq(get_fs(), KERNEL_DS)){ + memcpy(to, from, n); + return(0); + } + + return(access_ok_skas(VERIFY_WRITE, to, n) ? + buffer_op((unsigned long) to, n, 1, copy_chunk_to_user, &from) : + n); +} + +static int strncpy_chunk_from_user(unsigned long from, int len, void *arg) +{ + char **to_ptr = arg, *to = *to_ptr; + int n; + + strncpy(to, (void *) from, len); + n = strnlen(to, len); + *to_ptr += n; + + if(n < len) + return(1); + return(0); +} + +int strncpy_from_user_skas(char *dst, const char *src, int count) +{ + int n; + char *ptr = dst; + + if(segment_eq(get_fs(), KERNEL_DS)){ + strncpy(dst, src, count); + return(strnlen(dst, count)); + } + + if(!access_ok_skas(VERIFY_READ, src, 1)) + return(-EFAULT); + + n = buffer_op((unsigned long) src, count, 0, strncpy_chunk_from_user, + &ptr); + if(n != 0) + return(-EFAULT); + return(strnlen(dst, count)); +} + +static int clear_chunk(unsigned long addr, int len, void *unused) +{ + memset((void *) addr, 0, len); + return(0); +} + +int __clear_user_skas(void *mem, int len) +{ + return(buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL)); +} + +int clear_user_skas(void *mem, int len) +{ + if(segment_eq(get_fs(), KERNEL_DS)){ + memset(mem, 0, len); + return(0); + } + + return(access_ok_skas(VERIFY_WRITE, mem, len) ? + buffer_op((unsigned long) mem, len, 1, clear_chunk, NULL) : len); +} + +static int strnlen_chunk(unsigned long str, int len, void *arg) +{ + int *len_ptr = arg, n; + + n = strnlen((void *) str, len); + *len_ptr += n; + + if(n < len) + return(1); + return(0); +} + +int strnlen_user_skas(const void *str, int len) +{ + int count = 0, n; + + if(segment_eq(get_fs(), KERNEL_DS)) + return(strnlen(str, len) + 1); + + n = buffer_op((unsigned long) str, len, 0, strnlen_chunk, &count); + if(n == 0) + return(count + 1); + return(-EFAULT); +} + +/* + * 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: + */ --- linux-2.6.9-rc1/arch/um/kernel/skas/util/Makefile 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/um/kernel/skas/util/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -1,10 +1,9 @@ all: mk_ptregs mk_ptregs : mk_ptregs.o - $(CC) -o mk_ptregs mk_ptregs.o + $(HOSTCC) -o mk_ptregs mk_ptregs.o mk_ptregs.o : mk_ptregs.c - $(CC) -c $< + $(HOSTCC) -c $< -clean : - $(RM) -f mk_ptregs *.o *~ +clean-files := mk_ptregs *.o *~ --- linux-2.6.9-rc1/arch/um/kernel/skas/util/mk_ptregs.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/um/kernel/skas/util/mk_ptregs.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,3 +1,4 @@ +#include #include #include --- linux-2.6.9-rc1/arch/um/kernel/smp.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/um/kernel/smp.c 2004-09-03 00:56:41.008926344 -0700 @@ -1,9 +1,15 @@ /* - * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) * Licensed under the GPL */ #include "linux/config.h" +#include "linux/percpu.h" +#include "asm/pgalloc.h" +#include "asm/tlb.h" + +/* For some reason, mmu_gathers are referenced when CONFIG_SMP is off. */ +DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); #ifdef CONFIG_SMP @@ -12,10 +18,10 @@ #include "linux/threads.h" #include "linux/interrupt.h" #include "linux/err.h" +#include "linux/hardirq.h" #include "asm/smp.h" #include "asm/processor.h" #include "asm/spinlock.h" -#include "asm/hardirq.h" #include "user_util.h" #include "kern_util.h" #include "kern.h" @@ -23,7 +29,7 @@ #include "os.h" /* CPU online map, set by smp_boot_cpus */ -unsigned long cpu_online_map = cpumask_of_cpu(0); +unsigned long cpu_online_map = CPU_MASK_NONE; EXPORT_SYMBOL(cpu_online_map); @@ -33,14 +39,6 @@ EXPORT_SYMBOL(cpu_online_map); */ struct cpuinfo_um cpu_data[NR_CPUS]; -spinlock_t um_bh_lock = SPIN_LOCK_UNLOCKED; - -atomic_t global_bh_count; - -/* Not used by UML */ -unsigned char global_irq_holder = NO_PROC_ID; -unsigned volatile long global_irq_lock; - /* Set when the idlers are all forked */ int smp_threads_ready = 0; @@ -55,80 +53,44 @@ struct task_struct *idle_threads[NR_CPUS void smp_send_reschedule(int cpu) { - write(cpu_data[cpu].ipi_pipe[1], "R", 1); + os_write_file(cpu_data[cpu].ipi_pipe[1], "R", 1); num_reschedules_sent++; } -static void show(char * str) -{ - int cpu = smp_processor_id(); - - printk(KERN_INFO "\n%s, CPU %d:\n", str, cpu); -} - -#define MAXCOUNT 100000000 - -static inline void wait_on_bh(void) -{ - int count = MAXCOUNT; - do { - if (!--count) { - show("wait_on_bh"); - count = ~0; - } - /* nothing .. wait for the other bh's to go away */ - } while (atomic_read(&global_bh_count) != 0); -} - -/* - * This is called when we want to synchronize with - * bottom half handlers. We need to wait until - * no other CPU is executing any bottom half handler. - * - * Don't wait if we're already running in an interrupt - * context or are inside a bh handler. - */ -void synchronize_bh(void) -{ - if (atomic_read(&global_bh_count) && !in_interrupt()) - wait_on_bh(); -} - void smp_send_stop(void) { int i; printk(KERN_INFO "Stopping all CPUs..."); for(i = 0; i < num_online_cpus(); i++){ - if(i == current->thread_info->cpu) + if(i == current_thread->cpu) continue; - write(cpu_data[i].ipi_pipe[1], "S", 1); + os_write_file(cpu_data[i].ipi_pipe[1], "S", 1); } printk("done\n"); } -static cpumask_t smp_commenced_mask; -static cpumask_t smp_callin_map = CPU_MASK_NONE; +static cpumask_t smp_commenced_mask = CPU_MASK_NONE; +static cpumask_t cpu_callin_map = CPU_MASK_NONE; static int idle_proc(void *cpup) { int cpu = (int) cpup, err; err = os_pipe(cpu_data[cpu].ipi_pipe, 1, 1); - if(err) - panic("CPU#%d failed to create IPI pipe, errno = %d", cpu, - -err); + if(err < 0) + panic("CPU#%d failed to create IPI pipe, err = %d", cpu, -err); activate_ipi(cpu_data[cpu].ipi_pipe[0], current->thread.mode.tt.extern_pid); wmb(); - if (cpu_test_and_set(cpu, &smp_callin_map)) { + if (cpu_test_and_set(cpu, cpu_callin_map)) { printk("huh, CPU#%d already present??\n", cpu); BUG(); } - while (!cpu_isset(cpu, &smp_commenced_mask)) + while (!cpu_isset(cpu, smp_commenced_mask)) cpu_relax(); cpu_set(cpu, cpu_online_map); @@ -143,14 +105,16 @@ static struct task_struct *idle_thread(i current->thread.request.u.thread.proc = idle_proc; current->thread.request.u.thread.arg = (void *) cpu; - new_task = do_fork(CLONE_VM | CLONE_IDLETASK, 0, NULL, 0, NULL, NULL); - if(IS_ERR(new_task)) panic("do_fork failed in idle_thread"); + new_task = fork_idle(cpu); + if(IS_ERR(new_task)) + panic("copy_process failed in idle_thread, error = %ld", + PTR_ERR(new_task)); cpu_tasks[cpu] = ((struct cpu_task) { .pid = new_task->thread.mode.tt.extern_pid, .task = new_task } ); idle_threads[cpu] = new_task; - CHOOSE_MODE(write(new_task->thread.mode.tt.switch_pipe[1], &c, + CHOOSE_MODE(os_write_file(new_task->thread.mode.tt.switch_pipe[1], &c, sizeof(c)), ({ panic("skas mode doesn't support SMP"); })); return(new_task); @@ -160,15 +124,17 @@ void smp_prepare_cpus(unsigned int maxcp { struct task_struct *idle; unsigned long waittime; - int err, cpu; + int err, cpu, me = smp_processor_id(); - cpu_set(0, cpu_online_map); - cpu_set(0, smp_callin_map); + cpu_clear(me, cpu_online_map); + cpu_set(me, cpu_online_map); + cpu_set(me, cpu_callin_map); - err = os_pipe(cpu_data[0].ipi_pipe, 1, 1); - if(err) panic("CPU#0 failed to create IPI pipe, errno = %d", -err); + err = os_pipe(cpu_data[me].ipi_pipe, 1, 1); + if(err < 0) + panic("CPU#0 failed to create IPI pipe, errno = %d", -err); - activate_ipi(cpu_data[0].ipi_pipe[0], + activate_ipi(cpu_data[me].ipi_pipe[0], current->thread.mode.tt.extern_pid); for(cpu = 1; cpu < ncpus; cpu++){ @@ -180,10 +146,10 @@ void smp_prepare_cpus(unsigned int maxcp unhash_process(idle); waittime = 200000000; - while (waittime-- && !cpu_isset(cpu, smp_callin_map)) + while (waittime-- && !cpu_isset(cpu, cpu_callin_map)) cpu_relax(); - if (cpu_isset(cpu, smp_callin_map)) + if (cpu_isset(cpu, cpu_callin_map)) printk("done\n"); else printk("failed\n"); } @@ -216,7 +182,7 @@ void IPI_handler(int cpu) int fd; fd = cpu_data[cpu].ipi_pipe[0]; - while (read(fd, &c, 1) == 1) { + while (os_read_file(fd, &c, 1) == 1) { switch (c) { case 'C': smp_call_function_slave(cpu); @@ -276,9 +242,9 @@ int smp_call_function(void (*_func)(void info = _info; for (i=0;ithread_info->cpu) && + if((i != current_thread->cpu) && cpu_isset(i, cpu_online_map)) - write(cpu_data[i].ipi_pipe[1], "C", 1); + os_write_file(cpu_data[i].ipi_pipe[1], "C", 1); while (atomic_read(&scf_started) != cpus) barrier(); --- linux-2.6.9-rc1/arch/um/kernel/syscall_kern.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/arch/um/kernel/syscall_kern.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) * Licensed under the GPL */ @@ -36,32 +36,34 @@ long um_mount(char * dev_name, char * di long sys_fork(void) { - struct task_struct *p; + long ret; current->thread.forking = 1; - p = do_fork(SIGCHLD, 0, NULL, 0, NULL, NULL); + ret = do_fork(SIGCHLD, 0, NULL, 0, NULL, NULL); current->thread.forking = 0; - return(IS_ERR(p) ? PTR_ERR(p) : p->pid); + return(ret); } -long sys_clone(unsigned long clone_flags, unsigned long newsp) +long sys_clone(unsigned long clone_flags, unsigned long newsp, + int *parent_tid, int *child_tid) { - struct task_struct *p; + long ret; current->thread.forking = 1; - p = do_fork(clone_flags, newsp, NULL, 0, NULL, NULL); + ret = do_fork(clone_flags, newsp, NULL, 0, parent_tid, child_tid); current->thread.forking = 0; - return(IS_ERR(p) ? PTR_ERR(p) : p->pid); + return(ret); } long sys_vfork(void) { - struct task_struct *p; + long ret; current->thread.forking = 1; - p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, 0, NULL, 0, NULL, NULL); + ret = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, 0, NULL, 0, NULL, + NULL); current->thread.forking = 0; - return(IS_ERR(p) ? PTR_ERR(p) : p->pid); + return(ret); } /* common code for old and new mmaps */ @@ -136,43 +138,12 @@ int sys_pipe(unsigned long * fildes) error = do_pipe(fd); if (!error) { - if (copy_to_user(fildes, fd, 2*sizeof(int))) + if (copy_to_user(fildes, fd, sizeof(fd))) error = -EFAULT; } return error; } -int sys_sigaction(int sig, const struct old_sigaction *act, - struct old_sigaction *oact) -{ - struct k_sigaction new_ka, old_ka; - int ret; - - if (act) { - old_sigset_t mask; - if (verify_area(VERIFY_READ, act, sizeof(*act)) || - __get_user(new_ka.sa.sa_handler, &act->sa_handler) || - __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) - return -EFAULT; - __get_user(new_ka.sa.sa_flags, &act->sa_flags); - __get_user(mask, &act->sa_mask); - siginitset(&new_ka.sa.sa_mask, mask); - } - - ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); - - if (!ret && oact) { - if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || - __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || - __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) - return -EFAULT; - __put_user(old_ka.sa.sa_flags, &oact->sa_flags); - __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); - } - - return ret; -} - /* * sys_ipc() is the de-multiplexer for the SysV IPC calls.. * @@ -254,7 +225,7 @@ int sys_ipc (uint call, int first, int s return sys_shmctl (first, second, (struct shmid_ds *) ptr); default: - return -EINVAL; + return -ENOSYS; } } @@ -303,11 +274,6 @@ int sys_olduname(struct oldold_utsname * return error; } -int sys_sigaltstack(const stack_t *uss, stack_t *uoss) -{ - return(do_sigaltstack(uss, uoss, PT_REGS_SP(¤t->thread.regs))); -} - long execute_syscall(void *r) { return(CHOOSE_MODE_PROC(execute_syscall_tt, execute_syscall_skas, r)); --- linux-2.6.9-rc1/arch/um/kernel/sys_call_table.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/um/kernel/sys_call_table.c 2004-09-02 20:51:51.000000000 -0700 @@ -5,7 +5,6 @@ #include "linux/config.h" #include "linux/unistd.h" -#include "linux/version.h" #include "linux/sys.h" #include "linux/swap.h" #include "linux/syscalls.h" @@ -14,251 +13,50 @@ #include "sysdep/syscalls.h" #include "kern_util.h" -extern syscall_handler_t sys_restart_syscall; -extern syscall_handler_t sys_ni_syscall; -extern syscall_handler_t sys_exit; +#ifdef CONFIG_NFSD +#define NFSSERVCTL sys_nfsservctl +#else +#define NFSSERVCTL sys_ni_syscall +#endif + +#define LAST_GENERIC_SYSCALL __NR_vserver + +#if LAST_GENERIC_SYSCALL > LAST_ARCH_SYSCALL +#define LAST_SYSCALL LAST_GENERIC_SYSCALL +#else +#define LAST_SYSCALL LAST_ARCH_SYSCALL +#endif + extern syscall_handler_t sys_fork; -extern syscall_handler_t sys_creat; -extern syscall_handler_t sys_link; -extern syscall_handler_t sys_unlink; -extern syscall_handler_t sys_chdir; -extern syscall_handler_t sys_mknod; -extern syscall_handler_t sys_chmod; -extern syscall_handler_t sys_lchown16; -extern syscall_handler_t sys_ni_syscall; -extern syscall_handler_t sys_stat; -extern syscall_handler_t sys_getpid; -extern syscall_handler_t sys_oldumount; -extern syscall_handler_t sys_setuid16; -extern syscall_handler_t sys_getuid16; +extern syscall_handler_t sys_execve; +extern syscall_handler_t um_time; +extern syscall_handler_t um_mount; +extern syscall_handler_t um_stime; extern syscall_handler_t sys_ptrace; -extern syscall_handler_t sys_alarm; -extern syscall_handler_t sys_fstat; -extern syscall_handler_t sys_pause; -extern syscall_handler_t sys_utime; -extern syscall_handler_t sys_ni_syscall; -extern syscall_handler_t sys_ni_syscall; -extern syscall_handler_t sys_access; -extern syscall_handler_t sys_nice; -extern syscall_handler_t sys_ni_syscall; -extern syscall_handler_t sys_sync; -extern syscall_handler_t sys_kill; -extern syscall_handler_t sys_rename; -extern syscall_handler_t sys_mkdir; -extern syscall_handler_t sys_rmdir; extern syscall_handler_t sys_pipe; -extern syscall_handler_t sys_times; -extern syscall_handler_t sys_ni_syscall; -extern syscall_handler_t sys_brk; -extern syscall_handler_t sys_setgid16; -extern syscall_handler_t sys_getgid16; -extern syscall_handler_t sys_signal; -extern syscall_handler_t sys_geteuid16; -extern syscall_handler_t sys_getegid16; -extern syscall_handler_t sys_acct; -extern syscall_handler_t sys_umount; -extern syscall_handler_t sys_ni_syscall; -extern syscall_handler_t sys_ioctl; -extern syscall_handler_t sys_fcntl; -extern syscall_handler_t sys_ni_syscall; -extern syscall_handler_t sys_setpgid; -extern syscall_handler_t sys_ni_syscall; extern syscall_handler_t sys_olduname; -extern syscall_handler_t sys_umask; -extern syscall_handler_t sys_chroot; -extern syscall_handler_t sys_ustat; -extern syscall_handler_t sys_dup2; -extern syscall_handler_t sys_getppid; -extern syscall_handler_t sys_getpgrp; extern syscall_handler_t sys_sigaction; -extern syscall_handler_t sys_sgetmask; -extern syscall_handler_t sys_ssetmask; -extern syscall_handler_t sys_setreuid16; -extern syscall_handler_t sys_setregid16; extern syscall_handler_t sys_sigsuspend; -extern syscall_handler_t sys_sigpending; -extern syscall_handler_t sys_sethostname; -extern syscall_handler_t sys_setrlimit; -extern syscall_handler_t sys_old_getrlimit; -extern syscall_handler_t sys_getrusage; -extern syscall_handler_t sys_gettimeofday; -extern syscall_handler_t sys_settimeofday; -extern syscall_handler_t sys_getgroups16; -extern syscall_handler_t sys_setgroups16; -extern syscall_handler_t sys_symlink; -extern syscall_handler_t sys_lstat; -extern syscall_handler_t sys_readlink; -extern syscall_handler_t sys_swapon; -extern syscall_handler_t sys_uselib; -extern syscall_handler_t sys_reboot; extern syscall_handler_t old_readdir; -extern syscall_handler_t sys_munmap; -extern syscall_handler_t sys_truncate; -extern syscall_handler_t sys_ftruncate; -extern syscall_handler_t sys_fchmod; -extern syscall_handler_t sys_fchown16; -extern syscall_handler_t sys_getpriority; -extern syscall_handler_t sys_setpriority; -extern syscall_handler_t sys_ni_syscall; -extern syscall_handler_t sys_statfs; -extern syscall_handler_t sys_fstatfs; -extern syscall_handler_t sys_ni_syscall; -extern syscall_handler_t sys_socketcall; -extern syscall_handler_t sys_syslog; -extern syscall_handler_t sys_setitimer; -extern syscall_handler_t sys_getitimer; -extern syscall_handler_t sys_newstat; -extern syscall_handler_t sys_newlstat; -extern syscall_handler_t sys_newfstat; extern syscall_handler_t sys_uname; -extern syscall_handler_t sys_ni_syscall; -extern syscall_handler_t sys_vhangup; -extern syscall_handler_t sys_ni_syscall; -extern syscall_handler_t sys_ni_syscall; -extern syscall_handler_t sys_swapoff; -extern syscall_handler_t sys_sysinfo; extern syscall_handler_t sys_ipc; -extern syscall_handler_t sys_fsync; extern syscall_handler_t sys_sigreturn; -extern syscall_handler_t sys_rt_sigreturn; extern syscall_handler_t sys_clone; -extern syscall_handler_t sys_setdomainname; -extern syscall_handler_t sys_newuname; -extern syscall_handler_t sys_ni_syscall; -extern syscall_handler_t sys_adjtimex; -extern syscall_handler_t sys_mprotect; -extern syscall_handler_t sys_sigprocmask; -extern syscall_handler_t sys_init_module; -extern syscall_handler_t sys_delete_module; -extern syscall_handler_t sys_quotactl; -extern syscall_handler_t sys_getpgid; -extern syscall_handler_t sys_fchdir; -extern syscall_handler_t sys_bdflush; -extern syscall_handler_t sys_sysfs; -extern syscall_handler_t sys_personality; -extern syscall_handler_t sys_ni_syscall; -extern syscall_handler_t sys_setfsuid16; -extern syscall_handler_t sys_setfsgid16; -extern syscall_handler_t sys_llseek; -extern syscall_handler_t sys_getdents; -extern syscall_handler_t sys_flock; -extern syscall_handler_t sys_msync; -extern syscall_handler_t sys_readv; -extern syscall_handler_t sys_writev; -extern syscall_handler_t sys_getsid; -extern syscall_handler_t sys_fdatasync; -extern syscall_handler_t sys_mlock; -extern syscall_handler_t sys_munlock; -extern syscall_handler_t sys_mlockall; -extern syscall_handler_t sys_munlockall; -extern syscall_handler_t sys_sched_setparam; -extern syscall_handler_t sys_sched_getparam; -extern syscall_handler_t sys_sched_setscheduler; -extern syscall_handler_t sys_sched_getscheduler; -extern syscall_handler_t sys_sched_get_priority_max; -extern syscall_handler_t sys_sched_get_priority_min; -extern syscall_handler_t sys_sched_rr_get_interval; -extern syscall_handler_t sys_nanosleep; -extern syscall_handler_t sys_mremap; -extern syscall_handler_t sys_setresuid16; -extern syscall_handler_t sys_getresuid16; -extern syscall_handler_t sys_ni_syscall; -extern syscall_handler_t sys_poll; -extern syscall_handler_t sys_nfsservctl; -extern syscall_handler_t sys_setresgid16; -extern syscall_handler_t sys_getresgid16; -extern syscall_handler_t sys_prctl; -extern syscall_handler_t sys_ni_syscall; +extern syscall_handler_t sys_rt_sigreturn; extern syscall_handler_t sys_rt_sigaction; -extern syscall_handler_t sys_rt_sigprocmask; -extern syscall_handler_t sys_rt_sigpending; -extern syscall_handler_t sys_rt_sigtimedwait; -extern syscall_handler_t sys_rt_sigqueueinfo; -extern syscall_handler_t sys_rt_sigsuspend; -extern syscall_handler_t sys_pread64; -extern syscall_handler_t sys_pwrite64; -extern syscall_handler_t sys_chown16; -extern syscall_handler_t sys_getcwd; -extern syscall_handler_t sys_capget; -extern syscall_handler_t sys_capset; extern syscall_handler_t sys_sigaltstack; -extern syscall_handler_t sys_sendfile; -extern syscall_handler_t sys_ni_syscall; -extern syscall_handler_t sys_ni_syscall; extern syscall_handler_t sys_vfork; -extern syscall_handler_t sys_getrlimit; extern syscall_handler_t sys_mmap2; -extern syscall_handler_t sys_truncate64; -extern syscall_handler_t sys_ftruncate64; -extern syscall_handler_t sys_stat64; -extern syscall_handler_t sys_lstat64; -extern syscall_handler_t sys_fstat64; -extern syscall_handler_t sys_lchown; -extern syscall_handler_t sys_getuid; -extern syscall_handler_t sys_getgid; -extern syscall_handler_t sys_geteuid; -extern syscall_handler_t sys_getegid; -extern syscall_handler_t sys_setreuid; -extern syscall_handler_t sys_setregid; -extern syscall_handler_t sys_getgroups; -extern syscall_handler_t sys_setgroups; -extern syscall_handler_t sys_fchown; -extern syscall_handler_t sys_setresuid; -extern syscall_handler_t sys_getresuid; -extern syscall_handler_t sys_setresgid; -extern syscall_handler_t sys_getresgid; -extern syscall_handler_t sys_chown; -extern syscall_handler_t sys_setuid; -extern syscall_handler_t sys_setgid; -extern syscall_handler_t sys_setfsuid; -extern syscall_handler_t sys_setfsgid; -extern syscall_handler_t sys_pivot_root; -extern syscall_handler_t sys_mincore; -extern syscall_handler_t sys_madvise; -extern syscall_handler_t sys_fcntl64; -extern syscall_handler_t sys_getdents64; -extern syscall_handler_t sys_gettid; -extern syscall_handler_t sys_readahead; -extern syscall_handler_t sys_tkill; -extern syscall_handler_t sys_sendfile64; -extern syscall_handler_t sys_futex; -extern syscall_handler_t sys_sched_setaffinity; -extern syscall_handler_t sys_sched_getaffinity; -extern syscall_handler_t sys_io_setup; -extern syscall_handler_t sys_io_destroy; -extern syscall_handler_t sys_io_getevents; -extern syscall_handler_t sys_io_submit; -extern syscall_handler_t sys_io_cancel; -extern syscall_handler_t sys_exit_group; -extern syscall_handler_t sys_lookup_dcookie; -extern syscall_handler_t sys_epoll_create; -extern syscall_handler_t sys_epoll_ctl; -extern syscall_handler_t sys_epoll_wait; -extern syscall_handler_t sys_remap_file_pages; -extern syscall_handler_t sys_set_tid_address; - -#ifdef CONFIG_NFSD -#define NFSSERVCTL sys_nfsservctl -#else -#define NFSSERVCTL sys_ni_syscall -#endif - -extern syscall_handler_t um_mount; -extern syscall_handler_t um_time; -extern syscall_handler_t um_stime; - -#define LAST_GENERIC_SYSCALL __NR_set_tid_address - -#if LAST_GENERIC_SYSCALL > LAST_ARCH_SYSCALL -#define LAST_SYSCALL LAST_GENERIC_SYSCALL -#else -#define LAST_SYSCALL LAST_ARCH_SYSCALL -#endif +extern syscall_handler_t sys_timer_create; +extern syscall_handler_t old_mmap_i386; +extern syscall_handler_t old_select; +extern syscall_handler_t sys_modify_ldt; +extern syscall_handler_t sys_rt_sigsuspend; syscall_handler_t *sys_call_table[] = { - [ __NR_restart_syscall ] = sys_restart_syscall, - [ __NR_exit ] = sys_exit, - [ __NR_fork ] = sys_fork, + [ __NR_restart_syscall ] = (syscall_handler_t *) sys_restart_syscall, + [ __NR_exit ] (syscall_handler_t *) sys_exit, + [ __NR_fork ] (syscall_handler_t *) sys_fork, [ __NR_read ] = (syscall_handler_t *) sys_read, [ __NR_write ] = (syscall_handler_t *) sys_write, @@ -266,229 +64,249 @@ syscall_handler_t *sys_call_table[] = { [ __NR_open ] = (syscall_handler_t *) sys_open, [ __NR_close ] = (syscall_handler_t *) sys_close, [ __NR_waitpid ] = (syscall_handler_t *) sys_waitpid, - [ __NR_creat ] = sys_creat, - [ __NR_link ] = sys_link, - [ __NR_unlink ] = sys_unlink, + [ __NR_creat ] (syscall_handler_t *) sys_creat, + [ __NR_link ] (syscall_handler_t *) sys_link, + [ __NR_unlink ] (syscall_handler_t *) sys_unlink, [ __NR_execve ] = (syscall_handler_t *) sys_execve, /* declared differently in kern_util.h */ - [ __NR_chdir ] = sys_chdir, + [ __NR_chdir ] (syscall_handler_t *) sys_chdir, [ __NR_time ] = um_time, - [ __NR_mknod ] = sys_mknod, - [ __NR_chmod ] = sys_chmod, - [ __NR_lchown ] = sys_lchown16, - [ __NR_break ] = sys_ni_syscall, - [ __NR_oldstat ] = sys_stat, + [ __NR_mknod ] (syscall_handler_t *) sys_mknod, + [ __NR_chmod ] (syscall_handler_t *) sys_chmod, + [ __NR_lchown ] (syscall_handler_t *) sys_lchown16, + [ __NR_break ] (syscall_handler_t *) sys_ni_syscall, + [ __NR_oldstat ] (syscall_handler_t *) sys_stat, [ __NR_lseek ] = (syscall_handler_t *) sys_lseek, - [ __NR_getpid ] = sys_getpid, + [ __NR_getpid ] (syscall_handler_t *) sys_getpid, [ __NR_mount ] = um_mount, - [ __NR_umount ] = sys_oldumount, - [ __NR_setuid ] = sys_setuid16, - [ __NR_getuid ] = sys_getuid16, + [ __NR_umount ] (syscall_handler_t *) sys_oldumount, + [ __NR_setuid ] (syscall_handler_t *) sys_setuid16, + [ __NR_getuid ] (syscall_handler_t *) sys_getuid16, [ __NR_stime ] = um_stime, - [ __NR_ptrace ] = sys_ptrace, - [ __NR_alarm ] = sys_alarm, - [ __NR_oldfstat ] = sys_fstat, - [ __NR_pause ] = sys_pause, - [ __NR_utime ] = sys_utime, - [ __NR_stty ] = sys_ni_syscall, - [ __NR_gtty ] = sys_ni_syscall, - [ __NR_access ] = sys_access, - [ __NR_nice ] = sys_nice, - [ __NR_ftime ] = sys_ni_syscall, - [ __NR_sync ] = sys_sync, - [ __NR_kill ] = sys_kill, - [ __NR_rename ] = sys_rename, - [ __NR_mkdir ] = sys_mkdir, - [ __NR_rmdir ] = sys_rmdir, + [ __NR_ptrace ] (syscall_handler_t *) sys_ptrace, + [ __NR_alarm ] (syscall_handler_t *) sys_alarm, + [ __NR_oldfstat ] (syscall_handler_t *) sys_fstat, + [ __NR_pause ] (syscall_handler_t *) sys_pause, + [ __NR_utime ] (syscall_handler_t *) sys_utime, + [ __NR_stty ] (syscall_handler_t *) sys_ni_syscall, + [ __NR_gtty ] (syscall_handler_t *) sys_ni_syscall, + [ __NR_access ] (syscall_handler_t *) sys_access, + [ __NR_nice ] (syscall_handler_t *) sys_nice, + [ __NR_ftime ] (syscall_handler_t *) sys_ni_syscall, + [ __NR_sync ] (syscall_handler_t *) sys_sync, + [ __NR_kill ] (syscall_handler_t *) sys_kill, + [ __NR_rename ] (syscall_handler_t *) sys_rename, + [ __NR_mkdir ] (syscall_handler_t *) sys_mkdir, + [ __NR_rmdir ] (syscall_handler_t *) sys_rmdir, /* Declared differently in asm/unistd.h */ [ __NR_dup ] = (syscall_handler_t *) sys_dup, - [ __NR_pipe ] = sys_pipe, - [ __NR_times ] = sys_times, - [ __NR_prof ] = sys_ni_syscall, - [ __NR_brk ] = sys_brk, - [ __NR_setgid ] = sys_setgid16, - [ __NR_getgid ] = sys_getgid16, - [ __NR_signal ] = sys_signal, - [ __NR_geteuid ] = sys_geteuid16, - [ __NR_getegid ] = sys_getegid16, - [ __NR_acct ] = sys_acct, - [ __NR_umount2 ] = sys_umount, - [ __NR_lock ] = sys_ni_syscall, - [ __NR_ioctl ] = sys_ioctl, - [ __NR_fcntl ] = sys_fcntl, - [ __NR_mpx ] = sys_ni_syscall, - [ __NR_setpgid ] = sys_setpgid, - [ __NR_ulimit ] = sys_ni_syscall, - [ __NR_oldolduname ] = sys_olduname, - [ __NR_umask ] = sys_umask, - [ __NR_chroot ] = sys_chroot, - [ __NR_ustat ] = sys_ustat, - [ __NR_dup2 ] = sys_dup2, - [ __NR_getppid ] = sys_getppid, - [ __NR_getpgrp ] = sys_getpgrp, + [ __NR_pipe ] (syscall_handler_t *) sys_pipe, + [ __NR_times ] (syscall_handler_t *) sys_times, + [ __NR_prof ] (syscall_handler_t *) sys_ni_syscall, + [ __NR_brk ] (syscall_handler_t *) sys_brk, + [ __NR_setgid ] (syscall_handler_t *) sys_setgid16, + [ __NR_getgid ] (syscall_handler_t *) sys_getgid16, + [ __NR_signal ] (syscall_handler_t *) sys_signal, + [ __NR_geteuid ] (syscall_handler_t *) sys_geteuid16, + [ __NR_getegid ] (syscall_handler_t *) sys_getegid16, + [ __NR_acct ] (syscall_handler_t *) sys_acct, + [ __NR_umount2 ] (syscall_handler_t *) sys_umount, + [ __NR_lock ] (syscall_handler_t *) sys_ni_syscall, + [ __NR_ioctl ] (syscall_handler_t *) sys_ioctl, + [ __NR_fcntl ] (syscall_handler_t *) sys_fcntl, + [ __NR_mpx ] (syscall_handler_t *) sys_ni_syscall, + [ __NR_setpgid ] (syscall_handler_t *) sys_setpgid, + [ __NR_ulimit ] (syscall_handler_t *) sys_ni_syscall, + [ __NR_oldolduname ] (syscall_handler_t *) sys_olduname, + [ __NR_umask ] (syscall_handler_t *) sys_umask, + [ __NR_chroot ] (syscall_handler_t *) sys_chroot, + [ __NR_ustat ] (syscall_handler_t *) sys_ustat, + [ __NR_dup2 ] (syscall_handler_t *) sys_dup2, + [ __NR_getppid ] (syscall_handler_t *) sys_getppid, + [ __NR_getpgrp ] (syscall_handler_t *) sys_getpgrp, [ __NR_setsid ] = (syscall_handler_t *) sys_setsid, - [ __NR_sigaction ] = sys_sigaction, - [ __NR_sgetmask ] = sys_sgetmask, - [ __NR_ssetmask ] = sys_ssetmask, - [ __NR_setreuid ] = sys_setreuid16, - [ __NR_setregid ] = sys_setregid16, - [ __NR_sigsuspend ] = sys_sigsuspend, - [ __NR_sigpending ] = sys_sigpending, - [ __NR_sethostname ] = sys_sethostname, - [ __NR_setrlimit ] = sys_setrlimit, - [ __NR_getrlimit ] = sys_old_getrlimit, - [ __NR_getrusage ] = sys_getrusage, - [ __NR_gettimeofday ] = sys_gettimeofday, - [ __NR_settimeofday ] = sys_settimeofday, - [ __NR_getgroups ] = sys_getgroups16, - [ __NR_setgroups ] = sys_setgroups16, - [ __NR_symlink ] = sys_symlink, - [ __NR_oldlstat ] = sys_lstat, - [ __NR_readlink ] = sys_readlink, - [ __NR_uselib ] = sys_uselib, + [ __NR_sigaction ] (syscall_handler_t *) sys_sigaction, + [ __NR_sgetmask ] (syscall_handler_t *) sys_sgetmask, + [ __NR_ssetmask ] (syscall_handler_t *) sys_ssetmask, + [ __NR_setreuid ] (syscall_handler_t *) sys_setreuid16, + [ __NR_setregid ] (syscall_handler_t *) sys_setregid16, + [ __NR_sigsuspend ] (syscall_handler_t *) sys_sigsuspend, + [ __NR_sigpending ] (syscall_handler_t *) sys_sigpending, + [ __NR_sethostname ] (syscall_handler_t *) sys_sethostname, + [ __NR_setrlimit ] (syscall_handler_t *) sys_setrlimit, + [ __NR_getrlimit ] (syscall_handler_t *) sys_old_getrlimit, + [ __NR_getrusage ] (syscall_handler_t *) sys_getrusage, + [ __NR_gettimeofday ] (syscall_handler_t *) sys_gettimeofday, + [ __NR_settimeofday ] (syscall_handler_t *) sys_settimeofday, + [ __NR_getgroups ] (syscall_handler_t *) sys_getgroups16, + [ __NR_setgroups ] (syscall_handler_t *) sys_setgroups16, + [ __NR_symlink ] (syscall_handler_t *) sys_symlink, + [ __NR_oldlstat ] (syscall_handler_t *) sys_lstat, + [ __NR_readlink ] (syscall_handler_t *) sys_readlink, + [ __NR_uselib ] (syscall_handler_t *) sys_uselib, [ __NR_swapon ] = (syscall_handler_t *) sys_swapon, - [ __NR_reboot ] = sys_reboot, + [ __NR_reboot ] (syscall_handler_t *) sys_reboot, [ __NR_readdir ] = old_readdir, - [ __NR_munmap ] = sys_munmap, - [ __NR_truncate ] = sys_truncate, - [ __NR_ftruncate ] = sys_ftruncate, - [ __NR_fchmod ] = sys_fchmod, - [ __NR_fchown ] = sys_fchown16, - [ __NR_getpriority ] = sys_getpriority, - [ __NR_setpriority ] = sys_setpriority, - [ __NR_profil ] = sys_ni_syscall, - [ __NR_statfs ] = sys_statfs, - [ __NR_fstatfs ] = sys_fstatfs, - [ __NR_ioperm ] = sys_ni_syscall, - [ __NR_socketcall ] = sys_socketcall, - [ __NR_syslog ] = sys_syslog, - [ __NR_setitimer ] = sys_setitimer, - [ __NR_getitimer ] = sys_getitimer, - [ __NR_stat ] = sys_newstat, - [ __NR_lstat ] = sys_newlstat, - [ __NR_fstat ] = sys_newfstat, - [ __NR_olduname ] = sys_uname, - [ __NR_iopl ] = sys_ni_syscall, - [ __NR_vhangup ] = sys_vhangup, - [ __NR_idle ] = sys_ni_syscall, + [ __NR_munmap ] (syscall_handler_t *) sys_munmap, + [ __NR_truncate ] (syscall_handler_t *) sys_truncate, + [ __NR_ftruncate ] (syscall_handler_t *) sys_ftruncate, + [ __NR_fchmod ] (syscall_handler_t *) sys_fchmod, + [ __NR_fchown ] (syscall_handler_t *) sys_fchown16, + [ __NR_getpriority ] (syscall_handler_t *) sys_getpriority, + [ __NR_setpriority ] (syscall_handler_t *) sys_setpriority, + [ __NR_profil ] (syscall_handler_t *) sys_ni_syscall, + [ __NR_statfs ] (syscall_handler_t *) sys_statfs, + [ __NR_fstatfs ] (syscall_handler_t *) sys_fstatfs, + [ __NR_ioperm ] (syscall_handler_t *) sys_ni_syscall, + [ __NR_socketcall ] (syscall_handler_t *) sys_socketcall, + [ __NR_syslog ] (syscall_handler_t *) sys_syslog, + [ __NR_setitimer ] (syscall_handler_t *) sys_setitimer, + [ __NR_getitimer ] (syscall_handler_t *) sys_getitimer, + [ __NR_stat ] (syscall_handler_t *) sys_newstat, + [ __NR_lstat ] (syscall_handler_t *) sys_newlstat, + [ __NR_fstat ] (syscall_handler_t *) sys_newfstat, + [ __NR_olduname ] (syscall_handler_t *) sys_uname, + [ __NR_iopl ] (syscall_handler_t *) sys_ni_syscall, + [ __NR_vhangup ] (syscall_handler_t *) sys_vhangup, + [ __NR_idle ] (syscall_handler_t *) sys_ni_syscall, [ __NR_wait4 ] = (syscall_handler_t *) sys_wait4, [ __NR_swapoff ] = (syscall_handler_t *) sys_swapoff, - [ __NR_sysinfo ] = sys_sysinfo, - [ __NR_ipc ] = sys_ipc, - [ __NR_fsync ] = sys_fsync, - [ __NR_sigreturn ] = sys_sigreturn, - [ __NR_clone ] = sys_clone, - [ __NR_setdomainname ] = sys_setdomainname, - [ __NR_uname ] = sys_newuname, - [ __NR_adjtimex ] = sys_adjtimex, - [ __NR_mprotect ] = sys_mprotect, - [ __NR_sigprocmask ] = sys_sigprocmask, - [ __NR_create_module ] = sys_ni_syscall, - [ __NR_init_module ] = sys_init_module, - [ __NR_delete_module ] = sys_delete_module, - [ __NR_get_kernel_syms ] = sys_ni_syscall, - [ __NR_quotactl ] = sys_quotactl, - [ __NR_getpgid ] = sys_getpgid, - [ __NR_fchdir ] = sys_fchdir, - [ __NR_bdflush ] = sys_bdflush, - [ __NR_sysfs ] = sys_sysfs, - [ __NR_personality ] = sys_personality, - [ __NR_afs_syscall ] = sys_ni_syscall, - [ __NR_setfsuid ] = sys_setfsuid16, - [ __NR_setfsgid ] = sys_setfsgid16, - [ __NR__llseek ] = sys_llseek, - [ __NR_getdents ] = sys_getdents, + [ __NR_sysinfo ] (syscall_handler_t *) sys_sysinfo, + [ __NR_ipc ] (syscall_handler_t *) sys_ipc, + [ __NR_fsync ] (syscall_handler_t *) sys_fsync, + [ __NR_sigreturn ] (syscall_handler_t *) sys_sigreturn, + [ __NR_clone ] (syscall_handler_t *) sys_clone, + [ __NR_setdomainname ] (syscall_handler_t *) sys_setdomainname, + [ __NR_uname ] (syscall_handler_t *) sys_newuname, + [ __NR_adjtimex ] (syscall_handler_t *) sys_adjtimex, + [ __NR_mprotect ] (syscall_handler_t *) sys_mprotect, + [ __NR_sigprocmask ] (syscall_handler_t *) sys_sigprocmask, + [ __NR_create_module ] (syscall_handler_t *) sys_ni_syscall, + [ __NR_init_module ] (syscall_handler_t *) sys_init_module, + [ __NR_delete_module ] (syscall_handler_t *) sys_delete_module, + [ __NR_get_kernel_syms ] (syscall_handler_t *) sys_ni_syscall, + [ __NR_quotactl ] (syscall_handler_t *) sys_quotactl, + [ __NR_getpgid ] (syscall_handler_t *) sys_getpgid, + [ __NR_fchdir ] (syscall_handler_t *) sys_fchdir, + [ __NR_bdflush ] (syscall_handler_t *) sys_bdflush, + [ __NR_sysfs ] (syscall_handler_t *) sys_sysfs, + [ __NR_personality ] (syscall_handler_t *) sys_personality, + [ __NR_afs_syscall ] (syscall_handler_t *) sys_ni_syscall, + [ __NR_setfsuid ] (syscall_handler_t *) sys_setfsuid16, + [ __NR_setfsgid ] (syscall_handler_t *) sys_setfsgid16, + [ __NR__llseek ] (syscall_handler_t *) sys_llseek, + [ __NR_getdents ] (syscall_handler_t *) sys_getdents, [ __NR__newselect ] = (syscall_handler_t *) sys_select, - [ __NR_flock ] = sys_flock, - [ __NR_msync ] = sys_msync, - [ __NR_readv ] = sys_readv, - [ __NR_writev ] = sys_writev, - [ __NR_getsid ] = sys_getsid, - [ __NR_fdatasync ] = sys_fdatasync, + [ __NR_flock ] (syscall_handler_t *) sys_flock, + [ __NR_msync ] (syscall_handler_t *) sys_msync, + [ __NR_readv ] (syscall_handler_t *) sys_readv, + [ __NR_writev ] (syscall_handler_t *) sys_writev, + [ __NR_getsid ] (syscall_handler_t *) sys_getsid, + [ __NR_fdatasync ] (syscall_handler_t *) sys_fdatasync, [ __NR__sysctl ] = (syscall_handler_t *) sys_sysctl, - [ __NR_mlock ] = sys_mlock, - [ __NR_munlock ] = sys_munlock, - [ __NR_mlockall ] = sys_mlockall, - [ __NR_munlockall ] = sys_munlockall, - [ __NR_sched_setparam ] = sys_sched_setparam, - [ __NR_sched_getparam ] = sys_sched_getparam, - [ __NR_sched_setscheduler ] = sys_sched_setscheduler, - [ __NR_sched_getscheduler ] = sys_sched_getscheduler, + [ __NR_mlock ] (syscall_handler_t *) sys_mlock, + [ __NR_munlock ] (syscall_handler_t *) sys_munlock, + [ __NR_mlockall ] (syscall_handler_t *) sys_mlockall, + [ __NR_munlockall ] (syscall_handler_t *) sys_munlockall, + [ __NR_sched_setparam ] (syscall_handler_t *) sys_sched_setparam, + [ __NR_sched_getparam ] (syscall_handler_t *) sys_sched_getparam, + [ __NR_sched_setscheduler ] (syscall_handler_t *) sys_sched_setscheduler, + [ __NR_sched_getscheduler ] (syscall_handler_t *) sys_sched_getscheduler, [ __NR_sched_yield ] = (syscall_handler_t *) yield, - [ __NR_sched_get_priority_max ] = sys_sched_get_priority_max, - [ __NR_sched_get_priority_min ] = sys_sched_get_priority_min, - [ __NR_sched_rr_get_interval ] = sys_sched_rr_get_interval, - [ __NR_nanosleep ] = sys_nanosleep, - [ __NR_mremap ] = sys_mremap, - [ __NR_setresuid ] = sys_setresuid16, - [ __NR_getresuid ] = sys_getresuid16, - [ __NR_vm86 ] = sys_ni_syscall, - [ __NR_query_module ] = sys_ni_syscall, - [ __NR_poll ] = sys_poll, - [ __NR_nfsservctl ] = NFSSERVCTL, - [ __NR_setresgid ] = sys_setresgid16, - [ __NR_getresgid ] = sys_getresgid16, - [ __NR_prctl ] = sys_prctl, - [ __NR_rt_sigreturn ] = sys_rt_sigreturn, - [ __NR_rt_sigaction ] = sys_rt_sigaction, - [ __NR_rt_sigprocmask ] = sys_rt_sigprocmask, - [ __NR_rt_sigpending ] = sys_rt_sigpending, - [ __NR_rt_sigtimedwait ] = sys_rt_sigtimedwait, - [ __NR_rt_sigqueueinfo ] = sys_rt_sigqueueinfo, - [ __NR_rt_sigsuspend ] = sys_rt_sigsuspend, - [ __NR_pread64 ] = sys_pread64, - [ __NR_pwrite64 ] = sys_pwrite64, - [ __NR_chown ] = sys_chown16, - [ __NR_getcwd ] = sys_getcwd, - [ __NR_capget ] = sys_capget, - [ __NR_capset ] = sys_capset, - [ __NR_sigaltstack ] = sys_sigaltstack, - [ __NR_sendfile ] = sys_sendfile, - [ __NR_getpmsg ] = sys_ni_syscall, - [ __NR_putpmsg ] = sys_ni_syscall, - [ __NR_vfork ] = sys_vfork, - [ __NR_ugetrlimit ] = sys_getrlimit, - [ __NR_mmap2 ] = sys_mmap2, - [ __NR_truncate64 ] = sys_truncate64, - [ __NR_ftruncate64 ] = sys_ftruncate64, - [ __NR_stat64 ] = sys_stat64, - [ __NR_lstat64 ] = sys_lstat64, - [ __NR_fstat64 ] = sys_fstat64, - [ __NR_fcntl64 ] = sys_fcntl64, - [ __NR_getdents64 ] = sys_getdents64, - [ __NR_gettid ] = sys_gettid, - [ __NR_readahead ] = sys_readahead, - [ __NR_setxattr ] = sys_ni_syscall, - [ __NR_lsetxattr ] = sys_ni_syscall, - [ __NR_fsetxattr ] = sys_ni_syscall, - [ __NR_getxattr ] = sys_ni_syscall, - [ __NR_lgetxattr ] = sys_ni_syscall, - [ __NR_fgetxattr ] = sys_ni_syscall, - [ __NR_listxattr ] = sys_ni_syscall, - [ __NR_llistxattr ] = sys_ni_syscall, - [ __NR_flistxattr ] = sys_ni_syscall, - [ __NR_removexattr ] = sys_ni_syscall, - [ __NR_lremovexattr ] = sys_ni_syscall, - [ __NR_fremovexattr ] = sys_ni_syscall, - [ __NR_tkill ] = sys_tkill, - [ __NR_sendfile64 ] = sys_sendfile64, - [ __NR_futex ] = sys_futex, - [ __NR_sched_setaffinity ] = sys_sched_setaffinity, - [ __NR_sched_getaffinity ] = sys_sched_getaffinity, - [ __NR_io_setup ] = sys_io_setup, - [ __NR_io_destroy ] = sys_io_destroy, - [ __NR_io_getevents ] = sys_io_getevents, - [ __NR_io_submit ] = sys_io_submit, - [ __NR_io_cancel ] = sys_io_cancel, - [ __NR_exit_group ] = sys_exit_group, - [ __NR_lookup_dcookie ] = sys_lookup_dcookie, - [ __NR_epoll_create ] = sys_epoll_create, - [ __NR_epoll_ctl ] = sys_epoll_ctl, - [ __NR_epoll_wait ] = sys_epoll_wait, - [ __NR_remap_file_pages ] = sys_remap_file_pages, - [ __NR_set_tid_address ] = sys_set_tid_address, + [ __NR_sched_get_priority_max ] (syscall_handler_t *) sys_sched_get_priority_max, + [ __NR_sched_get_priority_min ] (syscall_handler_t *) sys_sched_get_priority_min, + [ __NR_sched_rr_get_interval ] (syscall_handler_t *) sys_sched_rr_get_interval, + [ __NR_nanosleep ] (syscall_handler_t *) sys_nanosleep, + [ __NR_mremap ] (syscall_handler_t *) sys_mremap, + [ __NR_setresuid ] (syscall_handler_t *) sys_setresuid16, + [ __NR_getresuid ] (syscall_handler_t *) sys_getresuid16, + [ __NR_vm86 ] (syscall_handler_t *) sys_ni_syscall, + [ __NR_query_module ] (syscall_handler_t *) sys_ni_syscall, + [ __NR_poll ] (syscall_handler_t *) sys_poll, + [ __NR_nfsservctl ] = (syscall_handler_t *) NFSSERVCTL, + [ __NR_setresgid ] (syscall_handler_t *) sys_setresgid16, + [ __NR_getresgid ] (syscall_handler_t *) sys_getresgid16, + [ __NR_prctl ] (syscall_handler_t *) sys_prctl, + [ __NR_rt_sigreturn ] (syscall_handler_t *) sys_rt_sigreturn, + [ __NR_rt_sigaction ] (syscall_handler_t *) sys_rt_sigaction, + [ __NR_rt_sigprocmask ] (syscall_handler_t *) sys_rt_sigprocmask, + [ __NR_rt_sigpending ] (syscall_handler_t *) sys_rt_sigpending, + [ __NR_rt_sigtimedwait ] (syscall_handler_t *) sys_rt_sigtimedwait, + [ __NR_rt_sigqueueinfo ] (syscall_handler_t *) sys_rt_sigqueueinfo, + [ __NR_rt_sigsuspend ] (syscall_handler_t *) sys_rt_sigsuspend, + [ __NR_pread64 ] (syscall_handler_t *) sys_pread64, + [ __NR_pwrite64 ] (syscall_handler_t *) sys_pwrite64, + [ __NR_chown ] (syscall_handler_t *) sys_chown16, + [ __NR_getcwd ] (syscall_handler_t *) sys_getcwd, + [ __NR_capget ] (syscall_handler_t *) sys_capget, + [ __NR_capset ] (syscall_handler_t *) sys_capset, + [ __NR_sigaltstack ] (syscall_handler_t *) sys_sigaltstack, + [ __NR_sendfile ] (syscall_handler_t *) sys_sendfile, + [ __NR_getpmsg ] (syscall_handler_t *) sys_ni_syscall, + [ __NR_putpmsg ] (syscall_handler_t *) sys_ni_syscall, + [ __NR_vfork ] (syscall_handler_t *) sys_vfork, + [ __NR_ugetrlimit ] (syscall_handler_t *) sys_getrlimit, + [ __NR_mmap2 ] (syscall_handler_t *) sys_mmap2, + [ __NR_truncate64 ] (syscall_handler_t *) sys_truncate64, + [ __NR_ftruncate64 ] (syscall_handler_t *) sys_ftruncate64, + [ __NR_stat64 ] (syscall_handler_t *) sys_stat64, + [ __NR_lstat64 ] (syscall_handler_t *) sys_lstat64, + [ __NR_fstat64 ] (syscall_handler_t *) sys_fstat64, + [ __NR_getdents64 ] (syscall_handler_t *) sys_getdents64, + [ __NR_fcntl64 ] (syscall_handler_t *) sys_fcntl64, + [ 223 ] (syscall_handler_t *) sys_ni_syscall, + [ __NR_gettid ] (syscall_handler_t *) sys_gettid, + [ __NR_readahead ] (syscall_handler_t *) sys_readahead, + [ __NR_setxattr ] (syscall_handler_t *) sys_setxattr, + [ __NR_lsetxattr ] (syscall_handler_t *) sys_lsetxattr, + [ __NR_fsetxattr ] (syscall_handler_t *) sys_fsetxattr, + [ __NR_getxattr ] (syscall_handler_t *) sys_getxattr, + [ __NR_lgetxattr ] (syscall_handler_t *) sys_lgetxattr, + [ __NR_fgetxattr ] (syscall_handler_t *) sys_fgetxattr, + [ __NR_listxattr ] (syscall_handler_t *) sys_listxattr, + [ __NR_llistxattr ] (syscall_handler_t *) sys_llistxattr, + [ __NR_flistxattr ] (syscall_handler_t *) sys_flistxattr, + [ __NR_removexattr ] (syscall_handler_t *) sys_removexattr, + [ __NR_lremovexattr ] (syscall_handler_t *) sys_lremovexattr, + [ __NR_fremovexattr ] (syscall_handler_t *) sys_fremovexattr, + [ __NR_tkill ] (syscall_handler_t *) sys_tkill, + [ __NR_sendfile64 ] (syscall_handler_t *) sys_sendfile64, + [ __NR_futex ] (syscall_handler_t *) sys_futex, + [ __NR_sched_setaffinity ] (syscall_handler_t *) sys_sched_setaffinity, + [ __NR_sched_getaffinity ] (syscall_handler_t *) sys_sched_getaffinity, + [ __NR_set_thread_area ] (syscall_handler_t *) sys_ni_syscall, + [ __NR_get_thread_area ] (syscall_handler_t *) sys_ni_syscall, + [ __NR_io_setup ] (syscall_handler_t *) sys_io_setup, + [ __NR_io_destroy ] (syscall_handler_t *) sys_io_destroy, + [ __NR_io_getevents ] (syscall_handler_t *) sys_io_getevents, + [ __NR_io_submit ] (syscall_handler_t *) sys_io_submit, + [ __NR_io_cancel ] (syscall_handler_t *) sys_io_cancel, + [ __NR_fadvise64 ] (syscall_handler_t *) sys_fadvise64, + [ 251 ] (syscall_handler_t *) sys_ni_syscall, + [ __NR_exit_group ] (syscall_handler_t *) sys_exit_group, + [ __NR_lookup_dcookie ] (syscall_handler_t *) sys_lookup_dcookie, + [ __NR_epoll_create ] (syscall_handler_t *) sys_epoll_create, + [ __NR_epoll_ctl ] (syscall_handler_t *) sys_epoll_ctl, + [ __NR_epoll_wait ] (syscall_handler_t *) sys_epoll_wait, + [ __NR_remap_file_pages ] (syscall_handler_t *) sys_remap_file_pages, + [ __NR_set_tid_address ] (syscall_handler_t *) sys_set_tid_address, + [ __NR_timer_create ] (syscall_handler_t *) sys_timer_create, + [ __NR_timer_settime ] (syscall_handler_t *) sys_timer_settime, + [ __NR_timer_gettime ] (syscall_handler_t *) sys_timer_gettime, + [ __NR_timer_getoverrun ] (syscall_handler_t *) sys_timer_getoverrun, + [ __NR_timer_delete ] (syscall_handler_t *) sys_timer_delete, + [ __NR_clock_settime ] (syscall_handler_t *) sys_clock_settime, + [ __NR_clock_gettime ] (syscall_handler_t *) sys_clock_gettime, + [ __NR_clock_getres ] (syscall_handler_t *) sys_clock_getres, + [ __NR_clock_nanosleep ] (syscall_handler_t *) sys_clock_nanosleep, + [ __NR_statfs64 ] (syscall_handler_t *) sys_statfs64, + [ __NR_fstatfs64 ] (syscall_handler_t *) sys_fstatfs64, + [ __NR_tgkill ] (syscall_handler_t *) sys_tgkill, + [ __NR_utimes ] (syscall_handler_t *) sys_utimes, + [ __NR_fadvise64_64 ] (syscall_handler_t *) sys_fadvise64_64, + [ __NR_vserver ] (syscall_handler_t *) sys_ni_syscall, ARCH_SYSCALLS [ LAST_SYSCALL + 1 ... NR_syscalls ] = --- linux-2.6.9-rc1/arch/um/kernel/sysrq.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/um/kernel/sysrq.c 2004-09-02 20:51:51.000000000 -0700 @@ -44,6 +44,11 @@ void dump_stack(void) } EXPORT_SYMBOL(dump_stack); +void show_stack(struct task_struct *task, unsigned long *sp) +{ + show_trace(sp); +} + /* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically --- linux-2.6.9-rc1/arch/um/kernel/tempfile.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/arch/um/kernel/tempfile.c 2004-09-02 20:51:51.000000000 -0700 @@ -28,6 +28,7 @@ static void __init find_tempdir(void) } if((dir == NULL) || (*dir == '\0')) dir = "/tmp"; + tempdir = malloc(strlen(dir) + 2); if(tempdir == NULL){ fprintf(stderr, "Failed to malloc tempdir, " @@ -49,7 +50,8 @@ int make_tempfile(const char *template, else *tempname = 0; strcat(tempname, template); - if((fd = mkstemp(tempname)) < 0){ + fd = mkstemp(tempname); + if(fd < 0){ fprintf(stderr, "open - cannot create %s: %s\n", tempname, strerror(errno)); return -1; @@ -59,7 +61,8 @@ int make_tempfile(const char *template, return -1; } if(out_tempname){ - if((*out_tempname = strdup(tempname)) == NULL){ + *out_tempname = strdup(tempname); + if(*out_tempname == NULL){ perror("strdup"); return -1; } --- linux-2.6.9-rc1/arch/um/kernel/time.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/um/kernel/time.c 2004-09-02 20:51:51.000000000 -0700 @@ -4,24 +4,34 @@ */ #include +#include #include #include #include #include #include -#include "linux/module.h" +#include #include "user_util.h" #include "kern_util.h" #include "user.h" #include "process.h" #include "signal_user.h" #include "time_user.h" +#include "kern_constants.h" + +/* XXX This really needs to be declared and initialized in a kernel file since + * it's in + */ +extern struct timespec wall_to_monotonic; extern struct timeval xtime; +struct timeval local_offset = { 0, 0 }; + void timer(void) { gettimeofday(&xtime, NULL); + timeradd(&xtime, &local_offset, &xtime); } void set_interval(int timer_type) @@ -66,7 +76,7 @@ void switch_timers(int to_real) errno); } -void idle_timer(void) +void uml_idle_timer(void) { if(signal(SIGVTALRM, SIG_IGN) == SIG_ERR) panic("Couldn't unset SIGVTALRM handler"); @@ -76,14 +86,60 @@ void idle_timer(void) set_interval(ITIMER_REAL); } +static unsigned long long get_host_hz(void) +{ + char mhzline[16], *end; + unsigned long long mhz; + int ret, mult, rest, len; + + ret = cpu_feature("cpu MHz", mhzline, + sizeof(mhzline) / sizeof(mhzline[0])); + if(!ret) + panic ("Could not get host MHZ"); + + mhz = strtoul(mhzline, &end, 10); + + /* This business is to parse a floating point number without using + * floating types. + */ + + rest = 0; + mult = 0; + if(*end == '.'){ + end++; + len = strlen(end); + if(len < 6) + mult = 6 - len; + else if(len > 6) + end[6] = '\0'; + rest = strtoul(end, NULL, 10); + while(mult-- > 0) + rest *= 10; + } + + return(1000000 * mhz + rest); +} + +unsigned long long host_hz = 0; + +extern int do_posix_clock_monotonic_gettime(struct timespec *tp); + void time_init(void) { + struct timespec now; + + host_hz = get_host_hz(); if(signal(SIGVTALRM, boot_timer_handler) == SIG_ERR) panic("Couldn't set SIGVTALRM handler"); set_interval(ITIMER_VIRTUAL); + + do_posix_clock_monotonic_gettime(&now); + wall_to_monotonic.tv_sec = -now.tv_sec; + wall_to_monotonic.tv_nsec = -now.tv_nsec; } -struct timeval local_offset = { 0, 0 }; +/* Declared in linux/time.h, which can't be included here */ +extern void clock_was_set(void); void do_gettimeofday(struct timeval *tv) { @@ -96,15 +152,13 @@ void do_gettimeofday(struct timeval *tv) clock_was_set(); } -EXPORT_SYMBOL(do_gettimeofday); - int do_settimeofday(struct timespec *tv) { struct timeval now; unsigned long flags; struct timeval tv_in; - if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) + if ((unsigned long) tv->tv_nsec >= UM_NSEC_PER_SEC) return -EINVAL; tv_in.tv_sec = tv->tv_sec; @@ -114,9 +168,9 @@ int do_settimeofday(struct timespec *tv) gettimeofday(&now, NULL); timersub(&tv_in, &now, &local_offset); time_unlock(flags); -} -EXPORT_SYMBOL(do_settimeofday); + return(0); +} void idle_sleep(int secs) { --- linux-2.6.9-rc1/arch/um/kernel/time_kern.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/um/kernel/time_kern.c 2004-09-02 20:51:51.000000000 -0700 @@ -30,22 +30,60 @@ int hz(void) return(HZ); } +/* + * Scheduler clock - returns current time in nanosec units. + */ +unsigned long long sched_clock(void) +{ + return (unsigned long long)jiffies_64 * (1000000000 / HZ); +} + /* Changed at early boot */ int timer_irq_inited = 0; -/* missed_ticks will be modified after kernel memory has been - * write-protected, so this puts it in a section which will be left - * write-enabled. - */ -int __attribute__ ((__section__ (".unprotected"))) missed_ticks[NR_CPUS]; +static int first_tick; +static unsigned long long prev_tsc; +#ifdef CONFIG_UML_REAL_TIME_CLOCK +static long long delta; /* Deviation per interval */ +#endif + +extern unsigned long long host_hz; void timer_irq(union uml_pt_regs *regs) { - int cpu = current->thread_info->cpu, ticks = missed_ticks[cpu]; + unsigned long long ticks = 0; + + if(!timer_irq_inited){ + /* This is to ensure that ticks don't pile up when + * the timer handler is suspended */ + first_tick = 0; + return; + } - if(!timer_irq_inited) return; - missed_ticks[cpu] = 0; - while(ticks--) do_IRQ(TIMER_IRQ, regs); + if(first_tick){ +#ifdef CONFIG_UML_REAL_TIME_CLOCK + unsigned long long tsc; + /* We've had 1 tick */ + tsc = time_stamp(); + + delta += tsc - prev_tsc; + prev_tsc = tsc; + + ticks += (delta * HZ) / host_hz; + delta -= (ticks * host_hz) / HZ; +#else + ticks = 1; +#endif + } + else { + prev_tsc = time_stamp(); + first_tick = 1; + } + + while(ticks > 0){ + do_IRQ(TIMER_IRQ, regs); + ticks--; + } } void boot_timer_handler(int sig) @@ -58,12 +96,15 @@ void boot_timer_handler(int sig) do_timer(®s); } -void um_timer(int irq, void *dev, struct pt_regs *regs) +irqreturn_t um_timer(int irq, void *dev, struct pt_regs *regs) { + unsigned long flags; + do_timer(regs); - write_seqlock(&xtime_lock); + write_seqlock_irqsave(&xtime_lock, flags); timer(); - write_sequnlock(&xtime_lock); + write_sequnlock_irqrestore(&xtime_lock, flags); + return(IRQ_HANDLED); } long um_time(int * tloc) @@ -81,12 +122,12 @@ long um_time(int * tloc) long um_stime(int * tptr) { int value; - struct timeval new; + struct timespec new; if (get_user(value, tptr)) return -EFAULT; new.tv_sec = value; - new.tv_usec = 0; + new.tv_nsec = 0; do_settimeofday(&new); return 0; } @@ -125,9 +166,11 @@ void __const_udelay(um_udelay_t usecs) void timer_handler(int sig, union uml_pt_regs *regs) { #ifdef CONFIG_SMP + local_irq_disable(); update_process_times(user_context(UPT_SP(regs))); + local_irq_enable(); #endif - if(current->thread_info->cpu == 0) + if(current_thread->cpu == 0) timer_irq(regs); } @@ -136,6 +179,7 @@ static spinlock_t timer_spinlock = SPIN_ unsigned long time_lock(void) { unsigned long flags; + spin_lock_irqsave(&timer_spinlock, flags); return(flags); } @@ -150,8 +194,8 @@ int __init timer_init(void) int err; CHOOSE_MODE(user_time_init_tt(), user_time_init_skas()); - if((err = request_irq(TIMER_IRQ, um_timer, SA_INTERRUPT, "timer", - NULL)) != 0) + err = request_irq(TIMER_IRQ, um_timer, SA_INTERRUPT, "timer", NULL); + if(err != 0) printk(KERN_ERR "timer_init : request_irq failed - " "errno = %d\n", -err); timer_irq_inited = 1; @@ -160,7 +204,6 @@ int __init timer_init(void) __initcall(timer_init); - /* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically --- linux-2.6.9-rc1/arch/um/kernel/trap_kern.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/arch/um/kernel/trap_kern.c 2004-09-02 20:51:51.000000000 -0700 @@ -16,12 +16,15 @@ #include "asm/tlbflush.h" #include "asm/a.out.h" #include "asm/current.h" +#include "asm/irq.h" #include "user_util.h" #include "kern_util.h" #include "kern.h" #include "chan_kern.h" #include "mconsole_kern.h" #include "2_5compat.h" +#include "mem.h" +#include "mem_kern.h" int handle_page_fault(unsigned long address, unsigned long ip, int is_write, int is_user, int *code_out) @@ -51,12 +54,10 @@ int handle_page_fault(unsigned long addr if(is_write && !(vma->vm_flags & VM_WRITE)) goto out; page = address & PAGE_MASK; - if(page == (unsigned long) current->thread_info + PAGE_SIZE) - panic("Kernel stack overflow"); pgd = pgd_offset(mm, page); pmd = pmd_offset(pgd, page); - survive: do { + survive: switch (handle_mm_fault(mm, vma, address, is_write)){ case VM_FAULT_MINOR: current->min_flt++; @@ -75,10 +76,10 @@ int handle_page_fault(unsigned long addr } pte = pte_offset_kernel(pmd, page); } while(!pte_present(*pte)); + err = 0; *pte = pte_mkyoung(*pte); if(pte_write(*pte)) *pte = pte_mkdirty(*pte); flush_tlb_page(vma, page); - err = 0; out: up_read(&mm->mmap_sem); return(err); @@ -94,10 +95,36 @@ out_of_memory: down_read(&mm->mmap_sem); goto survive; } - err = -ENOMEM; goto out; } +LIST_HEAD(physmem_remappers); + +void register_remapper(struct remapper *info) +{ + list_add(&info->list, &physmem_remappers); +} + +static int check_remapped_addr(unsigned long address, int is_write) +{ + struct remapper *remapper; + struct list_head *ele; + __u64 offset; + int fd; + + fd = phys_mapping(__pa(address), &offset); + if(fd == -1) + return(0); + + list_for_each(ele, &physmem_remappers){ + remapper = list_entry(ele, struct remapper, list); + if((*remapper->proc)(fd, address, is_write, offset)) + return(1); + } + + return(0); +} + unsigned long segv(unsigned long address, unsigned long ip, int is_write, int is_user, void *sc) { @@ -109,7 +136,9 @@ unsigned long segv(unsigned long address flush_tlb_kernel_vm(); return(0); } - if(current->mm == NULL) + else if(check_remapped_addr(address & PAGE_MASK, is_write)) + return(0); + else if(current->mm == NULL) panic("Segfault with no mm"); err = handle_page_fault(address, ip, is_write, is_user, &si.si_code); @@ -120,9 +149,8 @@ unsigned long segv(unsigned long address current->thread.fault_addr = (void *) address; do_longjmp(catcher, 1); } - else if(current->thread.fault_addr != NULL){ + else if(current->thread.fault_addr != NULL) panic("fault_addr set but no fault catcher"); - } else if(arch_fixup(ip, sc)) return(0); @@ -155,8 +183,6 @@ void bad_segv(unsigned long address, uns { struct siginfo si; - printk(KERN_ERR "Unfixable SEGV in '%s' (pid %d) at 0x%lx " - "(ip 0x%lx)\n", current->comm, current->pid, address, ip); si.si_signo = SIGSEGV; si.si_code = SEGV_ACCERR; si.si_addr = (void *) address; @@ -180,6 +206,11 @@ void bus_handler(int sig, union uml_pt_r else relay_signal(sig, regs); } +void winch(int sig, union uml_pt_regs *regs) +{ + do_IRQ(WINCH_IRQ, regs); +} + void trap_init(void) { } --- linux-2.6.9-rc1/arch/um/kernel/trap_user.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/um/kernel/trap_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -5,11 +5,9 @@ #include #include -#include #include #include #include -#include #include #include #include @@ -34,7 +32,14 @@ void kill_child_dead(int pid) { kill(pid, SIGKILL); kill(pid, SIGCONT); - while(waitpid(pid, NULL, 0) > 0) kill(pid, SIGCONT); + do { + int n; + CATCH_EINTR(n = waitpid(pid, NULL, 0)); + if (n > 0) + kill(pid, SIGCONT); + else + break; + } while(1); } /* Unlocked - don't care if this is a bit off */ @@ -82,6 +87,8 @@ struct signal_info sig_info[] = { .is_irq = 0 }, [ SIGILL ] { .handler = relay_signal, .is_irq = 0 }, + [ SIGWINCH ] { .handler = winch, + .is_irq = 1 }, [ SIGBUS ] { .handler = bus_handler, .is_irq = 0 }, [ SIGSEGV] { .handler = segv_handler, @@ -102,12 +109,11 @@ void sig_handler(int sig, struct sigcont sig, &sc); } -extern int timer_irq_inited, missed_ticks[]; +extern int timer_irq_inited; void alarm_handler(int sig, struct sigcontext sc) { if(!timer_irq_inited) return; - missed_ticks[cpu()]++; if(sig == SIGALRM) switch_timers(0); @@ -123,7 +129,7 @@ void do_longjmp(void *b, int val) { jmp_buf *buf = b; - longjmp(*buf, val); + siglongjmp(*buf, val); } /* --- linux-2.6.9-rc1/arch/um/kernel/tt/exec_kern.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/um/kernel/tt/exec_kern.c 2004-09-02 20:51:51.000000000 -0700 @@ -17,6 +17,7 @@ #include "mem_user.h" #include "os.h" #include "tlb.h" +#include "mode.h" static int exec_tramp(void *sig_stack) { @@ -47,17 +48,17 @@ void flush_thread_tt(void) do_exit(SIGKILL); } - if(current->thread_info->cpu == 0) + if(current_thread->cpu == 0) forward_interrupts(new_pid); current->thread.request.op = OP_EXEC; current->thread.request.u.exec.pid = new_pid; - unprotect_stack((unsigned long) current->thread_info); + unprotect_stack((unsigned long) current_thread); os_usr1_process(os_getpid()); enable_timer(); free_page(stack); protect_memory(uml_reserved, high_physmem - uml_reserved, 1, 1, 0, 1); - task_protections((unsigned long) current->thread_info); + task_protections((unsigned long) current_thread); force_flush_all(); unblock_signals(); } --- linux-2.6.9-rc1/arch/um/kernel/tt/exec_user.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/um/kernel/tt/exec_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -19,13 +19,18 @@ void do_exec(int old_pid, int new_pid) { unsigned long regs[FRAME_SIZE]; + int err; if((ptrace(PTRACE_ATTACH, new_pid, 0, 0) < 0) || - (ptrace(PTRACE_CONT, new_pid, 0, 0) < 0) || - (waitpid(new_pid, 0, WUNTRACED) < 0)) + (ptrace(PTRACE_CONT, new_pid, 0, 0) < 0)) tracer_panic("do_exec failed to attach proc - errno = %d", errno); + CATCH_EINTR(err = waitpid(new_pid, 0, WUNTRACED)); + if (err < 0) + tracer_panic("do_exec failed to attach proc in waitpid - errno = %d", + errno); + if(ptrace_getregs(old_pid, regs) < 0) tracer_panic("do_exec failed to get registers - errno = %d", errno); --- linux-2.6.9-rc1/arch/um/kernel/tt/include/mode.h 2004-08-24 00:02:59.000000000 -0700 +++ 25/arch/um/kernel/tt/include/mode.h 2004-09-02 20:51:51.000000000 -0700 @@ -8,6 +8,8 @@ #include "sysdep/ptrace.h" +enum { OP_NONE, OP_EXEC, OP_FORK, OP_TRACE_ON, OP_REBOOT, OP_HALT, OP_CB }; + extern int tracing_pid; extern int tracer(int (*init_proc)(void *), void *sp); --- linux-2.6.9-rc1/arch/um/kernel/tt/include/uaccess.h 2004-08-24 00:02:59.000000000 -0700 +++ 25/arch/um/kernel/tt/include/uaccess.h 2004-09-02 20:51:51.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) * Licensed under the GPL */ @@ -43,65 +43,19 @@ extern unsigned long get_fault_addr(void extern int __do_copy_from_user(void *to, const void *from, int n, void **fault_addr, void **fault_catcher); - -static inline int copy_from_user_tt(void *to, const void *from, int n) -{ - return(access_ok_tt(VERIFY_READ, from, n) ? - __do_copy_from_user(to, from, n, - ¤t->thread.fault_addr, - ¤t->thread.fault_catcher) : n); -} - -static inline int copy_to_user_tt(void *to, const void *from, int n) -{ - return(access_ok_tt(VERIFY_WRITE, to, n) ? - __do_copy_to_user(to, from, n, - ¤t->thread.fault_addr, - ¤t->thread.fault_catcher) : n); -} - extern int __do_strncpy_from_user(char *dst, const char *src, size_t n, void **fault_addr, void **fault_catcher); - -static inline int strncpy_from_user_tt(char *dst, const char *src, int count) -{ - int n; - - if(!access_ok_tt(VERIFY_READ, src, 1)) return(-EFAULT); - n = __do_strncpy_from_user(dst, src, count, - ¤t->thread.fault_addr, - ¤t->thread.fault_catcher); - if(n < 0) return(-EFAULT); - return(n); -} - extern int __do_clear_user(void *mem, size_t len, void **fault_addr, void **fault_catcher); - -static inline int __clear_user_tt(void *mem, int len) -{ - return(__do_clear_user(mem, len, - ¤t->thread.fault_addr, - ¤t->thread.fault_catcher)); -} - -static inline int clear_user_tt(void *mem, int len) -{ - return(access_ok_tt(VERIFY_WRITE, mem, len) ? - __do_clear_user(mem, len, - ¤t->thread.fault_addr, - ¤t->thread.fault_catcher) : len); -} - extern int __do_strnlen_user(const char *str, unsigned long n, void **fault_addr, void **fault_catcher); -static inline int strnlen_user_tt(const void *str, int len) -{ - return(__do_strnlen_user(str, len, - ¤t->thread.fault_addr, - ¤t->thread.fault_catcher)); -} +extern int copy_from_user_tt(void *to, const void *from, int n); +extern int copy_to_user_tt(void *to, const void *from, int n); +extern int strncpy_from_user_tt(char *dst, const char *src, int count); +extern int __clear_user_tt(void *mem, int len); +extern int clear_user_tt(void *mem, int len); +extern int strnlen_user_tt(const void *str, int len); #endif --- linux-2.6.9-rc1/arch/um/kernel/tt/Makefile 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/um/kernel/tt/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -1,5 +1,5 @@ # -# Copyright (C) 2002 Jeff Dike (jdike@karaya.com) +# Copyright (C) 2002 - 2003 Jeff Dike (jdike@addtoit.com) # Licensed under the GPL # @@ -7,7 +7,7 @@ extra-y := unmap_fin.o obj-y = exec_kern.o exec_user.o gdb.o ksyms.o mem.o mem_user.o process_kern.o \ syscall_kern.o syscall_user.o time.o tlb.o tracer.o trap_user.o \ - uaccess_user.o sys-$(SUBARCH)/ + uaccess.o uaccess_user.o sys-$(SUBARCH)/ obj-$(CONFIG_PT_PROXY) += gdb_kern.o ptproxy/ @@ -27,5 +27,3 @@ $(obj)/unmap.o: $(src)/unmap.c $(obj)/unmap_fin.o : $(src)/unmap.o ld -r -o $@ $< -lc -L/usr/lib - -clean : --- linux-2.6.9-rc1/arch/um/kernel/tt/mem.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/um/kernel/tt/mem.c 2004-09-02 20:51:51.000000000 -0700 @@ -18,7 +18,7 @@ void before_mem_tt(unsigned long brk_sta if(!jail || debug) remap_data(UML_ROUND_DOWN(&_stext), UML_ROUND_UP(&_etext), 1); remap_data(UML_ROUND_DOWN(&_sdata), UML_ROUND_UP(&_edata), 1); - remap_data(UML_ROUND_DOWN(&__bss_start), UML_ROUND_UP(brk_start), 1); + remap_data(UML_ROUND_DOWN(&__bss_start), UML_ROUND_UP(&_end), 1); } #ifdef CONFIG_HOST_2G_2G --- linux-2.6.9-rc1/arch/um/kernel/tt/mem_user.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/um/kernel/tt/mem_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -25,14 +25,13 @@ void remap_data(void *segment_start, voi size = (unsigned long) segment_end - (unsigned long) segment_start; data = create_mem_file(size); - if((addr = mmap(NULL, size, PROT_WRITE | PROT_READ, - MAP_SHARED, data, 0)) == MAP_FAILED){ + addr = mmap(NULL, size, PROT_WRITE | PROT_READ, MAP_SHARED, data, 0); + if(addr == MAP_FAILED){ perror("mapping new data segment"); exit(1); } memcpy(addr, segment_start, size); - if(switcheroo(data, prot, addr, segment_start, - size) < 0){ + if(switcheroo(data, prot, addr, segment_start, size) < 0){ printf("switcheroo failed\n"); exit(1); } --- linux-2.6.9-rc1/arch/um/kernel/tt/process_kern.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/um/kernel/tt/process_kern.c 2004-09-02 20:51:51.000000000 -0700 @@ -62,7 +62,7 @@ void *switch_to_tt(void *prev, void *nex reading = 0; err = os_write_file(to->thread.mode.tt.switch_pipe[1], &c, sizeof(c)); if(err != sizeof(c)) - panic("write of switch_pipe failed, errno = %d", -err); + panic("write of switch_pipe failed, err = %d", -err); reading = 1; if((from->state == TASK_ZOMBIE) || (from->state == TASK_DEAD)) @@ -104,48 +104,72 @@ void *switch_to_tt(void *prev, void *nex void release_thread_tt(struct task_struct *task) { - os_kill_process(task->thread.mode.tt.extern_pid, 0); + int pid = task->thread.mode.tt.extern_pid; + + if(os_getpid() != pid) + os_kill_process(pid, 0); } void exit_thread_tt(void) { - close(current->thread.mode.tt.switch_pipe[0]); - close(current->thread.mode.tt.switch_pipe[1]); + os_close_file(current->thread.mode.tt.switch_pipe[0]); + os_close_file(current->thread.mode.tt.switch_pipe[1]); } void schedule_tail(task_t *prev); static void new_thread_handler(int sig) { + unsigned long disable; int (*fn)(void *); void *arg; fn = current->thread.request.u.thread.proc; arg = current->thread.request.u.thread.arg; + UPT_SC(¤t->thread.regs.regs) = (void *) (&sig + 1); + disable = (1 << (SIGVTALRM - 1)) | (1 << (SIGALRM - 1)) | + (1 << (SIGIO - 1)) | (1 << (SIGPROF - 1)); + SC_SIGMASK(UPT_SC(¤t->thread.regs.regs)) &= ~disable; + suspend_new_thread(current->thread.mode.tt.switch_pipe[0]); - block_signals(); + force_flush_all(); + if(current->thread.prev_sched != NULL) + schedule_tail(current->thread.prev_sched); + current->thread.prev_sched = NULL; + init_new_thread_signals(1); -#ifdef CONFIG_SMP - schedule_tail(current->thread.prev_sched); -#endif enable_timer(); free_page(current->thread.temp_stack); set_cmdline("(kernel thread)"); - force_flush_all(); - current->thread.prev_sched = NULL; change_sig(SIGUSR1, 1); change_sig(SIGVTALRM, 1); change_sig(SIGPROF, 1); - unblock_signals(); + local_irq_enable(); if(!run_kernel_thread(fn, arg, ¤t->thread.exec_buf)) do_exit(0); } static int new_thread_proc(void *stack) { + /* local_irq_disable is needed to block out signals until this thread is + * properly scheduled. Otherwise, the tracing thread will get mighty + * upset about any signals that arrive before that. + * This has the complication that it sets the saved signal mask in + * the sigcontext to block signals. This gets restored when this + * thread (or a descendant, since they get a copy of this sigcontext) + * returns to userspace. + * So, this is compensated for elsewhere. + * XXX There is still a small window until local_irq_disable() actually + * finishes where signals are possible - shouldn't be a problem in + * practice since SIGIO hasn't been forwarded here yet, and the + * local_irq_disable should finish before a SIGVTALRM has time to be + * delivered. + */ + + local_irq_disable(); init_new_thread_stack(stack, new_thread_handler); os_usr1_process(os_getpid()); return(0); @@ -156,7 +180,7 @@ static int new_thread_proc(void *stack) * itself with a SIGUSR1. set_user_mode has to be run with SIGUSR1 off, * so it is blocked before it's called. They are re-enabled on sigreturn * despite the fact that they were blocked when the SIGUSR1 was issued because - * copy_thread copies the parent's signcontext, including the signal mask + * copy_thread copies the parent's sigcontext, including the signal mask * onto the signal frame. */ @@ -165,35 +189,32 @@ void finish_fork_handler(int sig) UPT_SC(¤t->thread.regs.regs) = (void *) (&sig + 1); suspend_new_thread(current->thread.mode.tt.switch_pipe[0]); -#ifdef CONFIG_SMP - schedule_tail(NULL); -#endif + force_flush_all(); + if(current->thread.prev_sched != NULL) + schedule_tail(current->thread.prev_sched); + current->thread.prev_sched = NULL; + enable_timer(); change_sig(SIGVTALRM, 1); local_irq_enable(); - force_flush_all(); if(current->mm != current->parent->mm) protect_memory(uml_reserved, high_physmem - uml_reserved, 1, 1, 0, 1); - task_protections((unsigned long) current->thread_info); - - current->thread.prev_sched = NULL; + task_protections((unsigned long) current_thread); free_page(current->thread.temp_stack); + local_irq_disable(); change_sig(SIGUSR1, 0); set_user_mode(current); } -static int sigusr1 = SIGUSR1; - int fork_tramp(void *stack) { - int sig = sigusr1; - local_irq_disable(); + arch_init_thread(); init_new_thread_stack(stack, finish_fork_handler); - kill(os_getpid(), sig); + os_usr1_process(os_getpid()); return(0); } @@ -213,8 +234,8 @@ int copy_thread_tt(int nr, unsigned long } err = os_pipe(p->thread.mode.tt.switch_pipe, 1, 1); - if(err){ - printk("copy_thread : pipe failed, errno = %d\n", -err); + if(err < 0){ + printk("copy_thread : pipe failed, err = %d\n", -err); return(err); } @@ -377,8 +398,8 @@ static void mprotect_kernel_mem(int w) pages = (1 << CONFIG_KERNEL_STACK_ORDER); - start = (unsigned long) current->thread_info + PAGE_SIZE; - end = (unsigned long) current + PAGE_SIZE * pages; + start = (unsigned long) current_thread + PAGE_SIZE; + end = (unsigned long) current_thread + PAGE_SIZE * pages; protect_memory(uml_reserved, start - uml_reserved, 1, w, 1, 1); protect_memory(end, high_physmem - end, 1, w, 1, 1); @@ -391,7 +412,7 @@ static void mprotect_kernel_mem(int w) protect_memory(start, end - start, 1, w, 1, 1); start = (unsigned long) UML_ROUND_DOWN(&__bss_start); - end = (unsigned long) UML_ROUND_UP(brk_start); + end = (unsigned long) UML_ROUND_UP(&_end); protect_memory(start, end - start, 1, w, 1, 1); mprotect_kernel_vm(w); @@ -454,8 +475,9 @@ void set_init_pid(int pid) init_task.thread.mode.tt.extern_pid = pid; err = os_pipe(init_task.thread.mode.tt.switch_pipe, 1, 1); - if(err) panic("Can't create switch pipe for init_task, errno = %d", - err); + if(err) + panic("Can't create switch pipe for init_task, errno = %d", + -err); } int singlestepping_tt(void *t) --- linux-2.6.9-rc1/arch/um/kernel/tt/ptproxy/Makefile 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/um/kernel/tt/ptproxy/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -9,5 +9,3 @@ USER_OBJS := $(foreach file,$(obj-y),$(s $(USER_OBJS) : %.o: %.c $(CC) $(CFLAGS_$(notdir $@)) $(USER_CFLAGS) -c -o $@ $< - -clean: --- linux-2.6.9-rc1/arch/um/kernel/tt/ptproxy/proxy.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/um/kernel/tt/ptproxy/proxy.c 2004-09-02 20:51:51.000000000 -0700 @@ -15,7 +15,6 @@ Jeff Dike (jdike@karaya.com) : Modified #include #include #include -#include #include #include #include @@ -273,7 +272,7 @@ void fake_child_exit(void) child_proxy(1, W_EXITCODE(0, 0)); while(debugger.waiting == 1){ - pid = waitpid(debugger.pid, &status, WUNTRACED); + CATCH_EINTR(pid = waitpid(debugger.pid, &status, WUNTRACED)); if(pid != debugger.pid){ printk("fake_child_exit - waitpid failed, " "errno = %d\n", errno); @@ -281,7 +280,7 @@ void fake_child_exit(void) } debugger_proxy(status, debugger.pid); } - pid = waitpid(debugger.pid, &status, WUNTRACED); + CATCH_EINTR(pid = waitpid(debugger.pid, &status, WUNTRACED)); if(pid != debugger.pid){ printk("fake_child_exit - waitpid failed, " "errno = %d\n", errno); @@ -293,10 +292,10 @@ void fake_child_exit(void) } char gdb_init_string[] = -"att 1 -b panic -b stop -handle SIGWINCH nostop noprint pass +"att 1 \n\ +b panic \n\ +b stop \n\ +handle SIGWINCH nostop noprint pass \n\ "; int start_debugger(char *prog, int startup, int stop, int *fd_out) @@ -304,7 +303,8 @@ int start_debugger(char *prog, int start int slave, child; slave = open_gdb_chan(); - if((child = fork()) == 0){ + child = fork(); + if(child == 0){ char *tempname = NULL; int fd; @@ -327,18 +327,19 @@ int start_debugger(char *prog, int start exit(1); #endif } - if((fd = make_tempfile("/tmp/gdb_init-XXXXXX", &tempname, 0)) < 0){ - printk("start_debugger : make_tempfile failed, errno = %d\n", - errno); + fd = make_tempfile("/tmp/gdb_init-XXXXXX", &tempname, 0); + if(fd < 0){ + printk("start_debugger : make_tempfile failed," + "err = %d\n", -fd); exit(1); } - write(fd, gdb_init_string, sizeof(gdb_init_string) - 1); + os_write_file(fd, gdb_init_string, sizeof(gdb_init_string) - 1); if(startup){ if(stop){ - write(fd, "b start_kernel\n", + os_write_file(fd, "b start_kernel\n", strlen("b start_kernel\n")); } - write(fd, "c\n", strlen("c\n")); + os_write_file(fd, "c\n", strlen("c\n")); } if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0){ printk("start_debugger : PTRACE_TRACEME failed, " --- linux-2.6.9-rc1/arch/um/kernel/tt/ptproxy/sysdep.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/um/kernel/tt/ptproxy/sysdep.c 2004-09-02 20:51:51.000000000 -0700 @@ -9,6 +9,7 @@ terms and conditions. #include #include #include +#include #include #include #include --- linux-2.6.9-rc1/arch/um/kernel/tt/ptproxy/wait.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/um/kernel/tt/ptproxy/wait.c 2004-09-02 20:51:51.000000000 -0700 @@ -56,21 +56,23 @@ int parent_wait_return(struct debugger * int real_wait_return(struct debugger *debugger) { unsigned long ip; - int err, pid; + int pid; pid = debugger->pid; + ip = ptrace(PTRACE_PEEKUSER, pid, PT_IP_OFFSET, 0); - ip = IP_RESTART_SYSCALL(ip); - err = ptrace(PTRACE_POKEUSER, pid, PT_IP_OFFSET, ip); + IP_RESTART_SYSCALL(ip); + if(ptrace(PTRACE_POKEUSER, pid, PT_IP_OFFSET, ip) < 0) tracer_panic("real_wait_return : Failed to restart system " - "call, errno = %d\n"); + "call, errno = %d\n", errno); + if((ptrace(PTRACE_SYSCALL, debugger->pid, 0, SIGCHLD) < 0) || (ptrace(PTRACE_SYSCALL, debugger->pid, 0, 0) < 0) || (ptrace(PTRACE_SYSCALL, debugger->pid, 0, 0) < 0) || debugger_normal_return(debugger, -1)) tracer_panic("real_wait_return : gdb failed to wait, " - "errno = %d\n"); + "errno = %d\n", errno); return(0); } --- linux-2.6.9-rc1/arch/um/kernel/tt/syscall_kern.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/arch/um/kernel/tt/syscall_kern.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) * Licensed under the GPL */ --- linux-2.6.9-rc1/arch/um/kernel/tt/syscall_user.c 2004-08-24 00:03:10.000000000 -0700 +++ 25/arch/um/kernel/tt/syscall_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -33,7 +33,7 @@ void syscall_handler_tt(int sig, union u SC_START_SYSCALL(sc); index = record_syscall_start(syscall); - syscall_trace(); + syscall_trace(regs, 1); result = execute_syscall(regs); /* regs->sc may have changed while the system call ran (there may @@ -46,7 +46,7 @@ void syscall_handler_tt(int sig, union u (result == -ERESTARTNOINTR)) do_signal(result); - syscall_trace(); + syscall_trace(regs, 0); record_syscall_end(index, result); } --- linux-2.6.9-rc1/arch/um/kernel/tt/sys-i386/Makefile 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/um/kernel/tt/sys-i386/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -10,5 +10,3 @@ USER_OBJS := $(foreach file,$(USER_OBJS) $(USER_OBJS) : %.o: %.c $(CC) $(CFLAGS_$(notdir $@)) $(USER_CFLAGS) -c -o $@ $< - -clean : --- linux-2.6.9-rc1/arch/um/kernel/tt/tlb.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/arch/um/kernel/tt/tlb.c 2004-09-02 20:51:51.000000000 -0700 @@ -10,6 +10,7 @@ #include "asm/page.h" #include "asm/pgtable.h" #include "asm/uaccess.h" +#include "asm/tlbflush.h" #include "user_util.h" #include "mem_user.h" #include "os.h" --- linux-2.6.9-rc1/arch/um/kernel/tt/tracer.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/arch/um/kernel/tt/tracer.c 2004-09-02 20:51:51.000000000 -0700 @@ -39,16 +39,17 @@ int is_tracer_winch(int pid, int fd, voi return(0); register_winch_irq(tracer_winch[0], fd, -1, data); - return(0); + return(1); } static void tracer_winch_handler(int sig) { + int n; char c = 1; - if(write(tracer_winch[1], &c, sizeof(c)) != sizeof(c)) - printk("tracer_winch_handler - write failed, errno = %d\n", - errno); + n = os_write_file(tracer_winch[1], &c, sizeof(c)); + if(n != sizeof(c)) + printk("tracer_winch_handler - write failed, err = %d\n", -n); } /* Called only by the tracing thread during initialization */ @@ -58,9 +59,8 @@ static void setup_tracer_winch(void) int err; err = os_pipe(tracer_winch, 1, 1); - if(err){ - printk("setup_tracer_winch : os_pipe failed, errno = %d\n", - -err); + if(err < 0){ + printk("setup_tracer_winch : os_pipe failed, err = %d\n", -err); return; } signal(SIGWINCH, tracer_winch_handler); @@ -130,8 +130,8 @@ static void sleeping_process_signal(int case SIGTSTP: if(ptrace(PTRACE_CONT, pid, 0, sig) < 0) tracer_panic("sleeping_process_signal : Failed to " - "continue pid %d, errno = %d\n", pid, - sig); + "continue pid %d, signal = %d, " + "errno = %d\n", pid, sig, errno); break; /* This happens when the debugger (e.g. strace) is doing system call @@ -145,7 +145,7 @@ static void sleeping_process_signal(int if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) tracer_panic("sleeping_process_signal : Failed to " "PTRACE_SYSCALL pid %d, errno = %d\n", - pid, sig); + pid, errno); break; case SIGSTOP: break; @@ -192,7 +192,7 @@ int tracer(int (*init_proc)(void *), voi printf("tracing thread pid = %d\n", tracing_pid); pid = clone(signal_tramp, sp, CLONE_FILES | SIGCHLD, init_proc); - n = waitpid(pid, &status, WUNTRACED); + CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED)); if(n < 0){ printf("waitpid on idle thread failed, errno = %d\n", errno); exit(1); @@ -218,7 +218,7 @@ int tracer(int (*init_proc)(void *), voi err = attach(debugger_parent); if(err){ printf("Failed to attach debugger parent %d, " - "errno = %d\n", debugger_parent, err); + "errno = %d\n", debugger_parent, -err); debugger_parent = -1; } else { @@ -233,7 +233,8 @@ int tracer(int (*init_proc)(void *), voi } set_cmdline("(tracing thread)"); while(1){ - if((pid = waitpid(-1, &status, WUNTRACED)) <= 0){ + CATCH_EINTR(pid = waitpid(-1, &status, WUNTRACED)); + if(pid <= 0){ if(errno != ECHILD){ printf("wait failed - errno = %d\n", errno); } @@ -401,7 +402,7 @@ static int __init uml_debug_setup(char * if(!strcmp(line, "go")) debug_stop = 0; else if(!strcmp(line, "parent")) debug_parent = 1; - else printk("Unknown debug option : '%s'\n", line); + else printf("Unknown debug option : '%s'\n", line); line = next; } --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/um/kernel/tt/uaccess.c 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "asm/uaccess.h" + +int copy_from_user_tt(void *to, const void *from, int n) +{ + if(!access_ok_tt(VERIFY_READ, from, n)) + return(n); + + return(__do_copy_from_user(to, from, n, ¤t->thread.fault_addr, + ¤t->thread.fault_catcher)); +} + +int copy_to_user_tt(void *to, const void *from, int n) +{ + if(!access_ok_tt(VERIFY_WRITE, to, n)) + return(n); + + return(__do_copy_to_user(to, from, n, ¤t->thread.fault_addr, + ¤t->thread.fault_catcher)); +} + +int strncpy_from_user_tt(char *dst, const char *src, int count) +{ + int n; + + if(!access_ok_tt(VERIFY_READ, src, 1)) + return(-EFAULT); + + n = __do_strncpy_from_user(dst, src, count, + ¤t->thread.fault_addr, + ¤t->thread.fault_catcher); + if(n < 0) return(-EFAULT); + return(n); +} + +int __clear_user_tt(void *mem, int len) +{ + return(__do_clear_user(mem, len, + ¤t->thread.fault_addr, + ¤t->thread.fault_catcher)); +} + +int clear_user_tt(void *mem, int len) +{ + if(!access_ok_tt(VERIFY_WRITE, mem, len)) + return(len); + + return(__do_clear_user(mem, len, ¤t->thread.fault_addr, + ¤t->thread.fault_catcher)); +} + +int strnlen_user_tt(const void *str, int len) +{ + return(__do_strnlen_user(str, len, + ¤t->thread.fault_addr, + ¤t->thread.fault_catcher)); +} + +/* + * 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: + */ --- linux-2.6.9-rc1/arch/um/kernel/tt/uaccess_user.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/um/kernel/tt/uaccess_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -8,15 +8,20 @@ #include #include "user_util.h" #include "uml_uaccess.h" +#include "task.h" +#include "kern_util.h" int __do_copy_from_user(void *to, const void *from, int n, void **fault_addr, void **fault_catcher) { + struct tt_regs save = TASK_REGS(get_current())->tt; unsigned long fault; int faulted; fault = __do_user_copy(to, from, n, fault_addr, fault_catcher, __do_copy, &faulted); + TASK_REGS(get_current())->tt = save; + if(!faulted) return(0); else return(n - (fault - (unsigned long) from)); } @@ -29,11 +34,14 @@ static void __do_strncpy(void *dst, cons int __do_strncpy_from_user(char *dst, const char *src, unsigned long count, void **fault_addr, void **fault_catcher) { + struct tt_regs save = TASK_REGS(get_current())->tt; unsigned long fault; int faulted; fault = __do_user_copy(dst, src, count, fault_addr, fault_catcher, __do_strncpy, &faulted); + TASK_REGS(get_current())->tt = save; + if(!faulted) return(strlen(dst)); else return(-1); } @@ -46,11 +54,14 @@ static void __do_clear(void *to, const v int __do_clear_user(void *mem, unsigned long len, void **fault_addr, void **fault_catcher) { + struct tt_regs save = TASK_REGS(get_current())->tt; unsigned long fault; int faulted; fault = __do_user_copy(mem, NULL, len, fault_addr, fault_catcher, __do_clear, &faulted); + TASK_REGS(get_current())->tt = save; + if(!faulted) return(0); else return(len - (fault - (unsigned long) mem)); } @@ -58,19 +69,20 @@ int __do_clear_user(void *mem, unsigned int __do_strnlen_user(const char *str, unsigned long n, void **fault_addr, void **fault_catcher) { + struct tt_regs save = TASK_REGS(get_current())->tt; int ret; unsigned long *faddrp = (unsigned long *)fault_addr; jmp_buf jbuf; *fault_catcher = &jbuf; - if(setjmp(jbuf) == 0){ + if(sigsetjmp(jbuf, 1) == 0) ret = strlen(str) + 1; - } - else { - ret = *faddrp - (unsigned long) str; - } + else ret = *faddrp - (unsigned long) str; + *fault_addr = NULL; *fault_catcher = NULL; + + TASK_REGS(get_current())->tt = save; return ret; } --- linux-2.6.9-rc1/arch/um/kernel/tt/unmap.c 2004-08-24 00:03:26.000000000 -0700 +++ 25/arch/um/kernel/tt/unmap.c 2004-09-02 20:51:51.000000000 -0700 @@ -3,10 +3,7 @@ * Licensed under the GPL */ -#include -#include #include -#include "user.h" int switcheroo(int fd, int prot, void *from, void *to, int size) { --- linux-2.6.9-rc1/arch/um/kernel/tty_log.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/um/kernel/tty_log.c 2004-09-02 20:51:51.000000000 -0700 @@ -9,10 +9,10 @@ #include #include #include -#include #include #include "init.h" #include "user.h" +#include "kern_util.h" #include "os.h" #define TTY_LOG_DIR "./" @@ -24,29 +24,40 @@ static int tty_log_fd = -1; #define TTY_LOG_OPEN 1 #define TTY_LOG_CLOSE 2 #define TTY_LOG_WRITE 3 +#define TTY_LOG_EXEC 4 + +#define TTY_READ 1 +#define TTY_WRITE 2 struct tty_log_buf { int what; unsigned long tty; int len; + int direction; + unsigned long sec; + unsigned long usec; }; -int open_tty_log(void *tty) +int open_tty_log(void *tty, void *current_tty) { struct timeval tv; struct tty_log_buf data; char buf[strlen(tty_log_dir) + sizeof("01234567890-01234567\0")]; int fd; + gettimeofday(&tv, NULL); if(tty_log_fd != -1){ - data = ((struct tty_log_buf) { what : TTY_LOG_OPEN, - tty : (unsigned long) tty, - len : 0 }); - write(tty_log_fd, &data, sizeof(data)); + data = ((struct tty_log_buf) { .what = TTY_LOG_OPEN, + .tty = (unsigned long) tty, + .len = sizeof(current_tty), + .direction = 0, + .sec = tv.tv_sec, + .usec = tv.tv_usec } ); + os_write_file(tty_log_fd, &data, sizeof(data)); + os_write_file(tty_log_fd, ¤t_tty, data.len); return(tty_log_fd); } - gettimeofday(&tv, NULL); sprintf(buf, "%s/%0u-%0u", tty_log_dir, (unsigned int) tv.tv_sec, (unsigned int) tv.tv_usec); @@ -62,30 +73,117 @@ int open_tty_log(void *tty) void close_tty_log(int fd, void *tty) { struct tty_log_buf data; + struct timeval tv; if(tty_log_fd != -1){ - data = ((struct tty_log_buf) { what : TTY_LOG_CLOSE, - tty : (unsigned long) tty, - len : 0 }); - write(tty_log_fd, &data, sizeof(data)); + gettimeofday(&tv, NULL); + data = ((struct tty_log_buf) { .what = TTY_LOG_CLOSE, + .tty = (unsigned long) tty, + .len = 0, + .direction = 0, + .sec = tv.tv_sec, + .usec = tv.tv_usec } ); + os_write_file(tty_log_fd, &data, sizeof(data)); return; } - close(fd); + os_close_file(fd); } -int write_tty_log(int fd, char *buf, int len, void *tty) +static int log_chunk(int fd, const char *buf, int len) { + int total = 0, try, missed, n; + char chunk[64]; + + while(len > 0){ + try = (len > sizeof(chunk)) ? sizeof(chunk) : len; + missed = copy_from_user_proc(chunk, (char *) buf, try); + try -= missed; + n = os_write_file(fd, chunk, try); + if(n != try) { + if(n < 0) + return(n); + return(-EIO); + } + if(missed != 0) + return(-EFAULT); + + len -= try; + total += try; + buf += try; + } + + return(total); +} + +int write_tty_log(int fd, const char *buf, int len, void *tty, int is_read) +{ + struct timeval tv; struct tty_log_buf data; + int direction; if(fd == tty_log_fd){ - data = ((struct tty_log_buf) { what : TTY_LOG_WRITE, - tty : (unsigned long) tty, - len : len }); - write(tty_log_fd, &data, sizeof(data)); + gettimeofday(&tv, NULL); + direction = is_read ? TTY_READ : TTY_WRITE; + data = ((struct tty_log_buf) { .what = TTY_LOG_WRITE, + .tty = (unsigned long) tty, + .len = len, + .direction = direction, + .sec = tv.tv_sec, + .usec = tv.tv_usec } ); + os_write_file(tty_log_fd, &data, sizeof(data)); + } + + return(log_chunk(fd, buf, len)); +} + +void log_exec(char **argv, void *tty) +{ + struct timeval tv; + struct tty_log_buf data; + char **ptr,*arg; + int len; + + if(tty_log_fd == -1) return; + + gettimeofday(&tv, NULL); + + len = 0; + for(ptr = argv; ; ptr++){ + if(copy_from_user_proc(&arg, ptr, sizeof(arg))) + return; + if(arg == NULL) break; + len += strlen_user_proc(arg); + } + + data = ((struct tty_log_buf) { .what = TTY_LOG_EXEC, + .tty = (unsigned long) tty, + .len = len, + .direction = 0, + .sec = tv.tv_sec, + .usec = tv.tv_usec } ); + os_write_file(tty_log_fd, &data, sizeof(data)); + + for(ptr = argv; ; ptr++){ + if(copy_from_user_proc(&arg, ptr, sizeof(arg))) + return; + if(arg == NULL) break; + log_chunk(tty_log_fd, arg, strlen_user_proc(arg)); } - return(write(fd, buf, len)); } +extern void register_tty_logger(int (*opener)(void *, void *), + int (*writer)(int, const char *, int, + void *, int), + void (*closer)(int, void *)); + +static int register_logger(void) +{ + register_tty_logger(open_tty_log, write_tty_log, close_tty_log); + return(0); +} + +__uml_initcall(register_logger); + static int __init set_tty_log_dir(char *name, int *add) { tty_log_dir = name; @@ -104,7 +202,7 @@ static int __init set_tty_log_fd(char *n tty_log_fd = strtoul(name, &end, 0); if((*end != '\0') || (end == name)){ - printk("set_tty_log_fd - strtoul failed on '%s'\n", name); + printf("set_tty_log_fd - strtoul failed on '%s'\n", name); tty_log_fd = -1; } return 0; --- linux-2.6.9-rc1/arch/um/kernel/uaccess_user.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/arch/um/kernel/uaccess_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -20,7 +20,7 @@ unsigned long __do_user_copy(void *to, c jmp_buf jbuf; *fault_catcher = &jbuf; - if(setjmp(jbuf) == 0){ + if(sigsetjmp(jbuf, 1) == 0){ (*op)(to, from, n); ret = 0; *faulted_out = 0; --- linux-2.6.9-rc1/arch/um/kernel/um_arch.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/um/kernel/um_arch.c 2004-09-02 20:51:51.000000000 -0700 @@ -38,13 +38,18 @@ #include "mode_kern.h" #include "mode.h" -#define DEFAULT_COMMAND_LINE "root=6200" +#define DEFAULT_COMMAND_LINE "root=98:0" struct cpuinfo_um boot_cpu_data = { .loops_per_jiffy = 0, .ipi_pipe = { -1, -1 } }; +/* Placeholder to make UML link until the vsyscall stuff is actually + * implemented + */ +void *__kernel_vsyscall; + unsigned long thread_saved_pc(struct task_struct *task) { return(os_process_pc(CHOOSE_MODE_PROC(thread_pid_tt, thread_pid_skas, @@ -53,18 +58,22 @@ unsigned long thread_saved_pc(struct tas static int show_cpuinfo(struct seq_file *m, void *v) { - int index; + int index = 0; - index = (struct cpuinfo_um *)v - cpu_data; #ifdef CONFIG_SMP + index = (struct cpuinfo_um *) v - cpu_data; if (!cpu_online(index)) return 0; #endif - seq_printf(m, "bogomips\t: %lu.%02lu\n", + seq_printf(m, "processor\t: %d\n", index); + seq_printf(m, "vendor_id\t: User Mode Linux\n"); + seq_printf(m, "model name\t: UML\n"); + seq_printf(m, "mode\t\t: %s\n", CHOOSE_MODE("tt", "skas")); + seq_printf(m, "host\t\t: %s\n", host_info); + seq_printf(m, "bogomips\t: %lu.%02lu\n\n", loops_per_jiffy/(500000/HZ), (loops_per_jiffy/(5000/HZ)) % 100); - seq_printf(m, "host\t\t: %s\n", host_info); return(0); } @@ -134,12 +143,12 @@ void set_cmdline(char *cmd) if(umid != NULL){ snprintf(argv1_begin, (argv1_end - argv1_begin) * sizeof(*ptr), - "(%s)", umid); + "(%s) ", umid); ptr = &argv1_begin[strlen(argv1_begin)]; } else ptr = argv1_begin; - snprintf(ptr, (argv1_end - ptr) * sizeof(*ptr), " [%s]", cmd); + snprintf(ptr, (argv1_end - ptr) * sizeof(*ptr), "[%s]", cmd); memset(argv1_begin + strlen(argv1_begin), '\0', argv1_end - argv1_begin - strlen(argv1_begin)); #endif @@ -179,7 +188,7 @@ __uml_setup("root=", uml_root_setup, static int __init uml_ncpus_setup(char *line, int *add) { if (!sscanf(line, "%d", &ncpus)) { - printk("Couldn't parse [%s]\n", line); + printf("Couldn't parse [%s]\n", line); return -1; } @@ -210,7 +219,7 @@ static int __init mode_tt_setup(char *li static int __init mode_tt_setup(char *line, int *add) { - printk("CONFIG_MODE_TT disabled - 'mode=tt' ignored\n"); + printf("CONFIG_MODE_TT disabled - 'mode=tt' ignored\n"); return(0); } @@ -221,7 +230,7 @@ static int __init mode_tt_setup(char *li static int __init mode_tt_setup(char *line, int *add) { - printk("CONFIG_MODE_SKAS disabled - 'mode=tt' redundant\n"); + printf("CONFIG_MODE_SKAS disabled - 'mode=tt' redundant\n"); return(0); } @@ -291,7 +300,7 @@ static void __init uml_postsetup(void) /* Set during early boot */ unsigned long brk_start; -static struct vm_reserved kernel_vm_reserved; +unsigned long end_iomem; #define MIN_VMALLOC (32 * 1024 * 1024) @@ -299,7 +308,7 @@ int linux_main(int argc, char **argv) { unsigned long avail; unsigned long virtmem_size, max_physmem; - unsigned int i, add, err; + unsigned int i, add; for (i = 1; i < argc; i++){ if((i == 1) && (argv[i][0] == ' ')) continue; @@ -328,12 +337,16 @@ int linux_main(int argc, char **argv) argv1_end = &argv[1][strlen(argv[1])]; #endif - set_usable_vm(uml_physmem, get_kmem_end()); - highmem = 0; - max_physmem = get_kmem_end() - uml_physmem - MIN_VMALLOC; - if(physmem_size > max_physmem){ - highmem = physmem_size - max_physmem; + iomem_size = (iomem_size + PAGE_SIZE - 1) & PAGE_MASK; + max_physmem = get_kmem_end() - uml_physmem - iomem_size - MIN_VMALLOC; + + /* Zones have to begin on a 1 << MAX_ORDER page boundary, + * so this makes sure that's true for highmem + */ + max_physmem &= ~((1 << (PAGE_SHIFT + MAX_ORDER)) - 1); + if(physmem_size + iomem_size > max_physmem){ + highmem = physmem_size + iomem_size - max_physmem; physmem_size -= highmem; #ifndef CONFIG_HIGHMEM highmem = 0; @@ -343,11 +356,19 @@ int linux_main(int argc, char **argv) } high_physmem = uml_physmem + physmem_size; - high_memory = (void *) high_physmem; + end_iomem = high_physmem + iomem_size; + high_memory = (void *) end_iomem; start_vm = VMALLOC_START; - setup_physmem(uml_physmem, uml_reserved, physmem_size); + setup_physmem(uml_physmem, uml_reserved, physmem_size, highmem); + if(init_maps(physmem_size, iomem_size, highmem)){ + printf("Failed to allocate mem_map for %ld bytes of physical " + "memory and %ld bytes of highmem\n", physmem_size, + highmem); + exit(1); + } + virtmem_size = physmem_size; avail = get_kmem_end() - start_vm; if(physmem_size > avail) virtmem_size = avail; @@ -357,28 +378,26 @@ int linux_main(int argc, char **argv) printf("Kernel virtual memory size shrunk to %ld bytes\n", virtmem_size); - err = reserve_vm(high_physmem, end_vm, &kernel_vm_reserved); - if(err){ - printf("Failed to reserve VM area for kernel VM\n"); - exit(1); - } - uml_postsetup(); init_task.thread.kernel_stack = (unsigned long) &init_thread_info + 2 * PAGE_SIZE; task_protections((unsigned long) &init_thread_info); + os_flush_stdout(); return(CHOOSE_MODE(start_uml_tt(), start_uml_skas())); } +extern int uml_exitcode; + static int panic_exit(struct notifier_block *self, unsigned long unused1, void *unused2) { #ifdef CONFIG_MAGIC_SYSRQ - handle_sysrq('p', ¤t->thread.regs, NULL, NULL); + handle_sysrq('p', ¤t->thread.regs, NULL); #endif + uml_exitcode = 1; machine_halt(); return(0); } @@ -403,6 +422,11 @@ void __init check_bugs(void) arch_check_bugs(); check_ptrace(); check_sigio(); + check_devanon(); +} + +void apply_alternatives(void *start, void *end) +{ } /* --- linux-2.6.9-rc1/arch/um/kernel/umid.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/um/kernel/umid.c 2004-09-02 20:51:51.000000000 -0700 @@ -5,7 +5,6 @@ #include #include -#include #include #include #include @@ -33,18 +32,19 @@ static char *uml_dir = UML_DIR; static int umid_is_random = 1; static int umid_inited = 0; -static int make_umid(void); +static int make_umid(int (*printer)(const char *fmt, ...)); -static int __init set_umid(char *name, int is_random) +static int __init set_umid(char *name, int is_random, + int (*printer)(const char *fmt, ...)) { if(umid_inited){ - printk("Unique machine name can't be set twice\n"); + (*printer)("Unique machine name can't be set twice\n"); return(-1); } if(strlen(name) > UMID_LEN - 1) - printk("Unique machine name is being truncated to %s " - "characters\n", UMID_LEN); + (*printer)("Unique machine name is being truncated to %s " + "characters\n", UMID_LEN); strlcpy(umid, name, sizeof(umid)); umid_is_random = is_random; @@ -54,7 +54,7 @@ static int __init set_umid(char *name, i static int __init set_umid_arg(char *name, int *add) { - return(set_umid(name, 0)); + return(set_umid(name, 0, printf)); } __uml_setup("umid=", set_umid_arg, @@ -67,7 +67,7 @@ int __init umid_file_name(char *name, ch { int n; - if(!umid_inited && make_umid()) return(-1); + if(!umid_inited && make_umid(printk)) return(-1); n = strlen(uml_dir) + strlen(umid) + strlen(name) + 1; if(n > len){ @@ -85,22 +85,23 @@ static int __init create_pid_file(void) { char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")]; char pid[sizeof("nnnnn\0")]; - int fd; + int fd, n; if(umid_file_name("pid", file, sizeof(file))) return 0; fd = os_open_file(file, of_create(of_excl(of_rdwr(OPENFLAGS()))), 0644); if(fd < 0){ - printk("Open of machine pid file \"%s\" failed - " - "errno = %d\n", file, -fd); + printf("Open of machine pid file \"%s\" failed - " + "err = %d\n", file, -fd); return 0; } sprintf(pid, "%d\n", os_getpid()); - if(write(fd, pid, strlen(pid)) != strlen(pid)) - printk("Write of pid file failed - errno = %d\n", errno); - close(fd); + n = os_write_file(fd, pid, strlen(pid)); + if(n != strlen(pid)) + printf("Write of pid file failed - err = %d\n", -n); + os_close_file(fd); return 0; } @@ -111,7 +112,8 @@ static int actually_do_remove(char *dir) int len; char file[256]; - if((directory = opendir(dir)) == NULL){ + directory = opendir(dir); + if(directory == NULL){ printk("actually_do_remove : couldn't open directory '%s', " "errno = %d\n", dir, errno); return(1); @@ -160,22 +162,24 @@ int not_dead_yet(char *dir) { char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")]; char pid[sizeof("nnnnn\0")], *end; - int dead, fd, p; + int dead, fd, p, n; sprintf(file, "%s/pid", dir); dead = 0; - if((fd = os_open_file(file, of_read(OPENFLAGS()), 0)) < 0){ + fd = os_open_file(file, of_read(OPENFLAGS()), 0); + if(fd < 0){ if(fd != -ENOENT){ printk("not_dead_yet : couldn't open pid file '%s', " - "errno = %d\n", file, -fd); + "err = %d\n", file, -fd); return(1); } dead = 1; } if(fd > 0){ - if(read(fd, pid, sizeof(pid)) < 0){ + n = os_read_file(fd, pid, sizeof(pid)); + if(n < 0){ printk("not_dead_yet : couldn't read pid file '%s', " - "errno = %d\n", file, errno); + "err = %d\n", file, -n); return(1); } p = strtoul(pid, &end, 0); @@ -197,7 +201,7 @@ static int __init set_uml_dir(char *name if((strlen(name) > 0) && (name[strlen(name) - 1] != '/')){ uml_dir = malloc(strlen(name) + 1); if(uml_dir == NULL){ - printk("Failed to malloc uml_dir - error = %d\n", + printf("Failed to malloc uml_dir - error = %d\n", errno); uml_dir = name; return(0); @@ -217,7 +221,7 @@ static int __init make_uml_dir(void) char *home = getenv("HOME"); if(home == NULL){ - printk("make_uml_dir : no value in environment for " + printf("make_uml_dir : no value in environment for " "$HOME\n"); exit(1); } @@ -232,57 +236,59 @@ static int __init make_uml_dir(void) dir[len + 1] = '\0'; } - if((uml_dir = malloc(strlen(dir) + 1)) == NULL){ + uml_dir = malloc(strlen(dir) + 1); + if(uml_dir == NULL){ printf("make_uml_dir : malloc failed, errno = %d\n", errno); exit(1); } strcpy(uml_dir, dir); if((mkdir(uml_dir, 0777) < 0) && (errno != EEXIST)){ - printk("Failed to mkdir %s - errno = %i\n", uml_dir, errno); + printf("Failed to mkdir %s - errno = %i\n", uml_dir, errno); return(-1); } return 0; } -static int __init make_umid(void) +static int __init make_umid(int (*printer)(const char *fmt, ...)) { int fd, err; char tmp[strlen(uml_dir) + UMID_LEN + 1]; strlcpy(tmp, uml_dir, sizeof(tmp)); - if(*umid == 0){ + if(!umid_inited){ strcat(tmp, "XXXXXX"); fd = mkstemp(tmp); if(fd < 0){ - printk("make_umid - mkstemp failed, errno = %d\n", - errno); + (*printer)("make_umid - mkstemp failed, errno = %d\n", + errno); return(1); } - close(fd); + os_close_file(fd); /* There's a nice tiny little race between this unlink and * the mkdir below. It'd be nice if there were a mkstemp * for directories. */ unlink(tmp); - set_umid(&tmp[strlen(uml_dir)], 1); + set_umid(&tmp[strlen(uml_dir)], 1, printer); } sprintf(tmp, "%s%s", uml_dir, umid); - if((err = mkdir(tmp, 0777)) < 0){ + err = mkdir(tmp, 0777); + if(err < 0){ if(errno == EEXIST){ if(not_dead_yet(tmp)){ - printk("umid '%s' is in use\n", umid); + (*printer)("umid '%s' is in use\n", umid); return(-1); } err = mkdir(tmp, 0777); } } if(err < 0){ - printk("Failed to create %s - errno = %d\n", umid, errno); + (*printer)("Failed to create %s - errno = %d\n", umid, errno); return(-1); } @@ -295,7 +301,13 @@ __uml_setup("uml_dir=", set_uml_dir, ); __uml_postsetup(make_uml_dir); -__uml_postsetup(make_umid); + +static int __init make_umid_setup(void) +{ + return(make_umid(printf)); +} + +__uml_postsetup(make_umid_setup); __uml_postsetup(create_pid_file); /* --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/um/kernel/uml.lds.S 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,96 @@ +#include + +OUTPUT_FORMAT(ELF_FORMAT) +OUTPUT_ARCH(ELF_ARCH) +ENTRY(_start) +jiffies = jiffies_64; + +SECTIONS +{ + . = START + SIZEOF_HEADERS; + + __binary_start = .; +#ifdef MODE_TT + .thread_private : { + __start_thread_private = .; + errno = .; + . += 4; + arch/um/kernel/tt/unmap_fin.o (.data) + __end_thread_private = .; + } + . = ALIGN(4096); + .remap : { arch/um/kernel/tt/unmap_fin.o (.text) } +#endif + + . = ALIGN(4096); /* Init code and data */ + _stext = .; + __init_begin = .; + .init.text : { + _sinittext = .; + *(.init.text) + _einittext = .; + } + . = ALIGN(4096); + .text : + { + *(.text) + SCHED_TEXT + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + *(.gnu.linkonce.t*) + } + + #include "asm/common.lds.S" + + init.data : { *(init.data) } + .data : + { + . = ALIGN(KERNEL_STACK_SIZE); /* init_task */ + *(.data.init_task) + *(.data) + *(.gnu.linkonce.d*) + CONSTRUCTORS + } + .data1 : { *(.data1) } + .ctors : + { + *(.ctors) + } + .dtors : + { + *(.dtors) + } + + .got : { *(.got.plt) *(.got) } + .dynamic : { *(.dynamic) } + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata : { *(.sdata) } + _edata = .; + PROVIDE (edata = .); + . = ALIGN(0x1000); + .sbss : + { + __bss_start = .; + PROVIDE(_bss_start = .); + *(.sbss) + *(.scommon) + } + .bss : + { + *(.dynbss) + *(.bss) + *(COMMON) + } + _end = . ; + PROVIDE (end = .); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } +} --- linux-2.6.9-rc1/arch/um/kernel/user_syms.c 2004-08-24 00:03:32.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,113 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "user_util.h" -#include "mem_user.h" -#include "uml-config.h" - -/* Had to steal this from linux/module.h because that file can't be included - * since this includes various user-level headers. - */ - -struct module_symbol -{ - unsigned long value; - const char *name; -}; - -/* Indirect stringification. */ - -#define __MODULE_STRING_1(x) #x -#define __MODULE_STRING(x) __MODULE_STRING_1(x) - -#if !defined(__AUTOCONF_INCLUDED__) - -#define __EXPORT_SYMBOL(sym,str) error config_must_be_included_before_module -#define EXPORT_SYMBOL(var) error config_must_be_included_before_module -#define EXPORT_SYMBOL_NOVERS(var) error config_must_be_included_before_module - -#elif !defined(UML_CONFIG_MODULES) - -#define __EXPORT_SYMBOL(sym,str) -#define EXPORT_SYMBOL(var) -#define EXPORT_SYMBOL_NOVERS(var) - -#else - -#define __EXPORT_SYMBOL(sym, str) \ -const char __kstrtab_##sym[] \ -__attribute__((section(".kstrtab"))) = str; \ -const struct module_symbol __ksymtab_##sym \ -__attribute__((section("__ksymtab"))) = \ -{ (unsigned long)&sym, __kstrtab_##sym } - -#if defined(__MODVERSIONS__) || !defined(UML_CONFIG_MODVERSIONS) -#define EXPORT_SYMBOL(var) __EXPORT_SYMBOL(var, __MODULE_STRING(var)) -#else -#define EXPORT_SYMBOL(var) __EXPORT_SYMBOL(var, __MODULE_STRING(__VERSIONED_SYMBOL(var))) -#endif - -#define EXPORT_SYMBOL_NOVERS(var) __EXPORT_SYMBOL(var, __MODULE_STRING(var)) - -#endif - -EXPORT_SYMBOL(__errno_location); - -EXPORT_SYMBOL(access); -EXPORT_SYMBOL(open); -EXPORT_SYMBOL(open64); -EXPORT_SYMBOL(close); -EXPORT_SYMBOL(read); -EXPORT_SYMBOL(write); -EXPORT_SYMBOL(dup2); -EXPORT_SYMBOL(__xstat); -EXPORT_SYMBOL(__lxstat); -EXPORT_SYMBOL(__lxstat64); -EXPORT_SYMBOL(lseek); -EXPORT_SYMBOL(lseek64); -EXPORT_SYMBOL(chown); -EXPORT_SYMBOL(truncate); -EXPORT_SYMBOL(utime); -EXPORT_SYMBOL(chmod); -EXPORT_SYMBOL(rename); -EXPORT_SYMBOL(__xmknod); - -EXPORT_SYMBOL(symlink); -EXPORT_SYMBOL(link); -EXPORT_SYMBOL(unlink); -EXPORT_SYMBOL(readlink); - -EXPORT_SYMBOL(mkdir); -EXPORT_SYMBOL(rmdir); -EXPORT_SYMBOL(opendir); -EXPORT_SYMBOL(readdir); -EXPORT_SYMBOL(closedir); -EXPORT_SYMBOL(seekdir); -EXPORT_SYMBOL(telldir); - -EXPORT_SYMBOL(ioctl); - -extern ssize_t pread64 (int __fd, void *__buf, size_t __nbytes, - __off64_t __offset); -extern ssize_t pwrite64 (int __fd, __const void *__buf, size_t __n, - __off64_t __offset); -EXPORT_SYMBOL(pread64); -EXPORT_SYMBOL(pwrite64); - -EXPORT_SYMBOL(statfs); -EXPORT_SYMBOL(statfs64); - -EXPORT_SYMBOL(memcpy); -EXPORT_SYMBOL(getuid); - -EXPORT_SYMBOL(memset); -EXPORT_SYMBOL(strstr); - -EXPORT_SYMBOL(find_iomem); --- linux-2.6.9-rc1/arch/um/kernel/user_util.c 2004-08-24 00:02:20.000000000 -0700 +++ 25/arch/um/kernel/user_util.c 2004-09-02 20:51:51.000000000 -0700 @@ -5,7 +5,6 @@ #include #include -#include #include #include #include @@ -81,10 +80,10 @@ int wait_for_stop(int pid, int sig, int int status, ret; while(1){ - if(((ret = waitpid(pid, &status, WUNTRACED)) < 0) || + CATCH_EINTR(ret = waitpid(pid, &status, WUNTRACED)); + if((ret < 0) || !WIFSTOPPED(status) || (WSTOPSIG(status) != sig)){ if(ret < 0){ - if(errno == EINTR) continue; printk("wait failed, errno = %d\n", errno); } @@ -118,29 +117,36 @@ int wait_for_stop(int pid, int sig, int } } -int clone_and_wait(int (*fn)(void *), void *arg, void *sp, int flags) -{ - int pid; - - pid = clone(fn, sp, flags, arg); - if(pid < 0) return(-1); - wait_for_stop(pid, SIGSTOP, PTRACE_CONT, NULL); - ptrace(PTRACE_CONT, pid, 0, 0); - return(pid); -} - -int raw(int fd, int complain) +int __raw(int fd, int complain, int now) { struct termios tt; int err; + int when; + + CATCH_EINTR(err = tcgetattr(fd, &tt)); + + if (err < 0) { + if (complain) + printk("tcgetattr failed, errno = %d\n", errno); + return(-errno); + } - tcgetattr(fd, &tt); cfmakeraw(&tt); - err = tcsetattr(fd, TCSANOW, &tt); - if((err < 0) && complain){ - printk("tcsetattr failed, errno = %d\n", errno); + + if (now) + when = TCSANOW; + else + when = TCSADRAIN; + + CATCH_EINTR(err = tcsetattr(fd, when, &tt)); + + if (err < 0) { + if (complain) + printk("tcsetattr failed, errno = %d\n", errno); return(-errno); } + /*XXX: tcsetattr could have applied only some changes + * (and cfmakeraw() is a set of changes) */ return(0); } --- linux-2.6.9-rc1/arch/um/main.c 2004-08-24 00:03:30.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,195 +0,0 @@ -/* - * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) - * Licensed under the GPL - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "user_util.h" -#include "kern_util.h" -#include "mem_user.h" -#include "signal_user.h" -#include "user.h" -#include "init.h" -#include "mode.h" -#include "choose-mode.h" -#include "uml-config.h" - -/* Set in set_stklim, which is called from main and __wrap_malloc. - * __wrap_malloc only calls it if main hasn't started. - */ -unsigned long stacksizelim; - -/* Set in main */ -char *linux_prog; - -#define PGD_BOUND (4 * 1024 * 1024) -#define STACKSIZE (8 * 1024 * 1024) -#define THREAD_NAME_LEN (256) - -static void set_stklim(void) -{ - struct rlimit lim; - - if(getrlimit(RLIMIT_STACK, &lim) < 0){ - perror("getrlimit"); - exit(1); - } - if((lim.rlim_cur == RLIM_INFINITY) || (lim.rlim_cur > STACKSIZE)){ - lim.rlim_cur = STACKSIZE; - if(setrlimit(RLIMIT_STACK, &lim) < 0){ - perror("setrlimit"); - exit(1); - } - } - stacksizelim = (lim.rlim_cur + PGD_BOUND - 1) & ~(PGD_BOUND - 1); -} - -static __init void do_uml_initcalls(void) -{ - initcall_t *call; - - call = &__uml_initcall_start; - while (call < &__uml_initcall_end){; - (*call)(); - call++; - } -} - -static void last_ditch_exit(int sig) -{ - CHOOSE_MODE(kmalloc_ok = 0, (void) 0); - signal(SIGINT, SIG_DFL); - signal(SIGTERM, SIG_DFL); - signal(SIGHUP, SIG_DFL); - uml_cleanup(); - exit(1); -} - -extern int uml_exitcode; - -int main(int argc, char **argv, char **envp) -{ - char **new_argv; - sigset_t mask; - int ret, i; - - /* Enable all signals except SIGIO - in some environments, we can - * enter with some signals blocked - */ - - sigemptyset(&mask); - sigaddset(&mask, SIGIO); - if(sigprocmask(SIG_SETMASK, &mask, NULL) < 0){ - perror("sigprocmask"); - exit(1); - } - -#ifdef UML_CONFIG_MODE_TT - /* Allocate memory for thread command lines */ - if(argc < 2 || strlen(argv[1]) < THREAD_NAME_LEN - 1){ - - char padding[THREAD_NAME_LEN] = { - [ 0 ... THREAD_NAME_LEN - 2] = ' ', '\0' - }; - - new_argv = malloc((argc + 2) * sizeof(char*)); - if(!new_argv) { - perror("Allocating extended argv"); - exit(1); - } - - new_argv[0] = argv[0]; - new_argv[1] = padding; - - for(i = 2; i <= argc; i++) - new_argv[i] = argv[i - 1]; - new_argv[argc + 1] = NULL; - - execvp(new_argv[0], new_argv); - perror("execing with extended args"); - exit(1); - } -#endif - - linux_prog = argv[0]; - - set_stklim(); - - if((new_argv = malloc((argc + 1) * sizeof(char *))) == NULL){ - perror("Mallocing argv"); - exit(1); - } - for(i=0;i $@ +# Generated files +define filechk_umlconfig + sed 's/ CONFIG/ UML_CONFIG/' +endef + +$(ARCH_DIR)/include/uml-config.h : $(TOPDIR)/include/linux/autoconf.h + $(call filechk,umlconfig) + +filechk_gen_header = $< $(ARCH_DIR)/include/task.h : $(ARCH_DIR)/util/mk_task - $< > $@ + $(call filechk,gen_header) $(ARCH_DIR)/include/kern_constants.h : $(ARCH_DIR)/util/mk_constants - $< > $@ + $(call filechk,gen_header) -$(ARCH_DIR)/util/mk_task : $(ARCH_DIR)/kernel/skas/include/skas_ptregs.h \ - $(ARCH_DIR)/util FORCE ; +$(ARCH_DIR)/util/mk_task $(ARCH_DIR)/util/mk_constants : $(ARCH_DIR)/util \ + sys_prepare FORCE ; $(ARCH_DIR)/util: FORCE - @$(call descend,$@,) + $(Q)$(MAKE) $(build)=$@ export SUBARCH USER_CFLAGS OS + +all: linux + +define archhelp + echo '* linux - Binary kernel image (./linux)' +endef --- linux-2.6.9-rc1/arch/um/Makefile-i386 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/um/Makefile-i386 2004-09-02 20:51:51.000000000 -0700 @@ -16,22 +16,27 @@ SYS_UTIL_DIR := $(ARCH_DIR)/sys-i386/uti SYS_HEADERS = $(SYS_DIR)/sc.h $(SYS_DIR)/thread.h +sys_prepare: $(SYS_DIR)/sc.h + prepare: $(SYS_HEADERS) +filechk_$(SYS_DIR)/sc.h := $(SYS_UTIL_DIR)/mk_sc + $(SYS_DIR)/sc.h: $(SYS_UTIL_DIR)/mk_sc - $< > $@ + $(call filechk,$@) + +filechk_$(SYS_DIR)/thread.h := $(SYS_UTIL_DIR)/mk_thread $(SYS_DIR)/thread.h: $(SYS_UTIL_DIR)/mk_thread - $< > $@ + $(call filechk,$@) -$(SYS_UTIL_DIR)/mk_sc: FORCE ; - @$(call descend,$(SYS_UTIL_DIR),$@) +$(SYS_UTIL_DIR)/mk_sc: scripts/basic/fixdep include/config/MARKER FORCE ; + $(Q)$(MAKE) $(build)=$(SYS_UTIL_DIR) $@ -$(SYS_UTIL_DIR)/mk_thread: $(ARCH_SYMLINKS) $(GEN_HEADERS) FORCE ; - @$(call descend,$(SYS_UTIL_DIR),$@) +$(SYS_UTIL_DIR)/mk_thread: $(ARCH_SYMLINKS) $(GEN_HEADERS) sys_prepare FORCE ; + $(Q)$(MAKE) $(build)=$(SYS_UTIL_DIR) $@ $(SYS_UTIL_DIR): include/asm FORCE - @$(call descend,$@,) + $(Q)$(MAKE) $(build)=$(SYS_UTIL_DIR) -sysclean : - rm -f $(SYS_HEADERS) +CLEAN_FILES += $(SYS_HEADERS) --- linux-2.6.9-rc1/arch/um/Makefile-skas 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/um/Makefile-skas 2004-09-02 20:51:51.000000000 -0700 @@ -14,7 +14,7 @@ MODE_INCLUDE += -I$(TOPDIR)/$(ARCH_DIR)/ LINK_SKAS = -Wl,-rpath,/lib LD_SCRIPT_SKAS = dyn.lds -GEN_HEADERS += $(ARCH_DIR)/kernel/skas/include/skas_ptregs.h +GEN_HEADERS += $(TOPDIR)/$(ARCH_DIR)/include/skas_ptregs.h -$(ARCH_DIR)/kernel/skas/include/skas_ptregs.h : - $(MAKE) -C $(ARCH_DIR)/kernel/skas include/skas_ptregs.h +$(TOPDIR)/$(ARCH_DIR)/include/skas_ptregs.h : + $(Q)$(MAKE) $(build)=$(ARCH_DIR)/kernel/skas $@ --- linux-2.6.9-rc1/arch/um/os-Linux/drivers/ethertap_kern.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/arch/um/os-Linux/drivers/ethertap_kern.c 2004-09-02 20:51:51.000000000 -0700 @@ -8,7 +8,6 @@ #include "linux/init.h" #include "linux/netdevice.h" #include "linux/etherdevice.h" -#include "linux/init.h" #include "net_kern.h" #include "net_user.h" #include "etap.h" --- linux-2.6.9-rc1/arch/um/os-Linux/drivers/ethertap_user.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/um/os-Linux/drivers/ethertap_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -17,6 +16,7 @@ #include #include "user.h" #include "kern_util.h" +#include "user_util.h" #include "net_user.h" #include "etap.h" #include "helper.h" @@ -42,13 +42,14 @@ static void etap_change(int op, unsigned { struct addr_change change; void *output; + int n; change.what = op; memcpy(change.addr, addr, sizeof(change.addr)); memcpy(change.netmask, netmask, sizeof(change.netmask)); - if(write(fd, &change, sizeof(change)) != sizeof(change)) - printk("etap_change - request failed, errno = %d\n", - errno); + n = os_write_file(fd, &change, sizeof(change)); + if(n != sizeof(change)) + printk("etap_change - request failed, err = %d\n", -n); output = um_kmalloc(page_size()); if(output == NULL) printk("etap_change : Failed to allocate output buffer\n"); @@ -82,15 +83,15 @@ static void etap_pre_exec(void *arg) struct etap_pre_exec_data *data = arg; dup2(data->control_remote, 1); - close(data->data_me); - close(data->control_me); + os_close_file(data->data_me); + os_close_file(data->control_me); } static int etap_tramp(char *dev, char *gate, int control_me, int control_remote, int data_me, int data_remote) { struct etap_pre_exec_data pe_data; - int pid, status, err; + int pid, status, err, n; char version_buf[sizeof("nnnnn\0")]; char data_fd_buf[sizeof("nnnnnn\0")]; char gate_buf[sizeof("nnn.nnn.nnn.nnn\0")]; @@ -114,21 +115,22 @@ static int etap_tramp(char *dev, char *g pe_data.data_me = data_me; pid = run_helper(etap_pre_exec, &pe_data, args, NULL); - if(pid < 0) err = errno; - close(data_remote); - close(control_remote); - if(read(control_me, &c, sizeof(c)) != sizeof(c)){ - printk("etap_tramp : read of status failed, errno = %d\n", - errno); - return(EINVAL); + if(pid < 0) err = pid; + os_close_file(data_remote); + os_close_file(control_remote); + n = os_read_file(control_me, &c, sizeof(c)); + if(n != sizeof(c)){ + printk("etap_tramp : read of status failed, err = %d\n", -n); + return(-EINVAL); } if(c != 1){ printk("etap_tramp : uml_net failed\n"); - err = EINVAL; - if(waitpid(pid, &status, 0) < 0) err = errno; - else if(!WIFEXITED(status) || (WEXITSTATUS(status) != 1)){ + err = -EINVAL; + CATCH_EINTR(n = waitpid(pid, &status, 0)); + if(n < 0) + err = -errno; + else if(!WIFEXITED(status) || (WEXITSTATUS(status) != 1)) printk("uml_net didn't exit with status 1\n"); - } } return(err); } @@ -143,14 +145,14 @@ static int etap_open(void *data) if(err) return(err); err = os_pipe(data_fds, 0, 0); - if(err){ - printk("data os_pipe failed - errno = %d\n", -err); + if(err < 0){ + printk("data os_pipe failed - err = %d\n", -err); return(err); } err = os_pipe(control_fds, 1, 0); - if(err){ - printk("control os_pipe failed - errno = %d\n", -err); + if(err < 0){ + printk("control os_pipe failed - err = %d\n", -err); return(err); } @@ -167,9 +169,9 @@ static int etap_open(void *data) kfree(output); } - if(err != 0){ - printk("etap_tramp failed - errno = %d\n", err); - return(-err); + if(err < 0){ + printk("etap_tramp failed - err = %d\n", -err); + return(err); } pri->data_fd = data_fds[0]; @@ -183,11 +185,11 @@ static void etap_close(int fd, void *dat struct ethertap_data *pri = data; iter_addresses(pri->dev, etap_close_addr, &pri->control_fd); - close(fd); + os_close_file(fd); os_shutdown_socket(pri->data_fd, 1, 1); - close(pri->data_fd); + os_close_file(pri->data_fd); pri->data_fd = -1; - close(pri->control_fd); + os_close_file(pri->control_fd); pri->control_fd = -1; } --- linux-2.6.9-rc1/arch/um/os-Linux/drivers/tuntap_user.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/um/os-Linux/drivers/tuntap_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -19,6 +18,7 @@ #include "net_user.h" #include "tuntap.h" #include "kern_util.h" +#include "user_util.h" #include "user.h" #include "helper.h" #include "os.h" @@ -61,7 +61,7 @@ static void tuntap_pre_exec(void *arg) struct tuntap_pre_exec_data *data = arg; dup2(data->stdout, 1); - close(data->close_me); + os_close_file(data->close_me); } static int tuntap_open_tramp(char *gate, int *fd_out, int me, int remote, @@ -86,7 +86,7 @@ static int tuntap_open_tramp(char *gate, if(pid < 0) return(-pid); - close(remote); + os_close_file(remote); msg.msg_name = NULL; msg.msg_namelen = 0; @@ -107,19 +107,19 @@ static int tuntap_open_tramp(char *gate, if(n < 0){ printk("tuntap_open_tramp : recvmsg failed - errno = %d\n", errno); - return(errno); + return(-errno); } - waitpid(pid, NULL, 0); + CATCH_EINTR(waitpid(pid, NULL, 0)); cmsg = CMSG_FIRSTHDR(&msg); if(cmsg == NULL){ printk("tuntap_open_tramp : didn't receive a message\n"); - return(EINVAL); + return(-EINVAL); } if((cmsg->cmsg_level != SOL_SOCKET) || (cmsg->cmsg_type != SCM_RIGHTS)){ printk("tuntap_open_tramp : didn't receive a descriptor\n"); - return(EINVAL); + return(-EINVAL); } *fd_out = ((int *) CMSG_DATA(cmsg))[0]; return(0); @@ -133,27 +133,29 @@ static int tuntap_open(void *data) int err, fds[2], len, used; err = tap_open_common(pri->dev, pri->gate_addr); - if(err) return(err); + if(err < 0) + return(err); if(pri->fixed_config){ - if((pri->fd = open("/dev/net/tun", O_RDWR)) < 0){ - printk("Failed to open /dev/net/tun, errno = %d\n", - errno); - return(-errno); + pri->fd = os_open_file("/dev/net/tun", of_rdwr(OPENFLAGS()), 0); + if(pri->fd < 0){ + printk("Failed to open /dev/net/tun, err = %d\n", + -pri->fd); + return(pri->fd); } memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_flags = IFF_TAP; + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; strlcpy(ifr.ifr_name, pri->dev_name, sizeof(ifr.ifr_name)); if(ioctl(pri->fd, TUNSETIFF, (void *) &ifr) < 0){ - printk("TUNSETIFF failed, errno = %d", errno); - close(pri->fd); + printk("TUNSETIFF failed, errno = %d\n", errno); + os_close_file(pri->fd); return(-errno); } } else { err = os_pipe(fds, 0, 0); - if(err){ - printk("tuntap_open : os_pipe failed - errno = %d\n", + if(err < 0){ + printk("tuntap_open : os_pipe failed - err = %d\n", -err); return(err); } @@ -166,19 +168,19 @@ static int tuntap_open(void *data) fds[1], buffer, len, &used); output = buffer; - if(err == 0){ - pri->dev_name = uml_strdup(buffer); - output += IFNAMSIZ; - printk(output); - free_output_buffer(buffer); - } - else { - printk(output); + if(err < 0) { + printk("%s", output); free_output_buffer(buffer); - printk("tuntap_open_tramp failed - errno = %d\n", err); - return(-err); + printk("tuntap_open_tramp failed - err = %d\n", -err); + return(err); } - close(fds[0]); + + pri->dev_name = uml_strdup(buffer); + output += IFNAMSIZ; + printk("%s", output); + free_output_buffer(buffer); + + os_close_file(fds[0]); iter_addresses(pri->dev, open_addr, pri->dev_name); } @@ -191,7 +193,7 @@ static void tuntap_close(int fd, void *d if(!pri->fixed_config) iter_addresses(pri->dev, close_addr, pri->dev_name); - close(fd); + os_close_file(fd); pri->fd = -1; } --- linux-2.6.9-rc1/arch/um/os-Linux/file.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/um/os-Linux/file.c 2004-09-02 20:51:51.000000000 -0700 @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include #include @@ -17,33 +19,235 @@ #include "user.h" #include "kern_util.h" -int os_file_type(char *file) +static void copy_stat(struct uml_stat *dst, struct stat64 *src) +{ + *dst = ((struct uml_stat) { + .ust_dev = src->st_dev, /* device */ + .ust_ino = src->st_ino, /* inode */ + .ust_mode = src->st_mode, /* protection */ + .ust_nlink = src->st_nlink, /* number of hard links */ + .ust_uid = src->st_uid, /* user ID of owner */ + .ust_gid = src->st_gid, /* group ID of owner */ + .ust_size = src->st_size, /* total size, in bytes */ + .ust_blksize = src->st_blksize, /* blocksize for filesys I/O */ + .ust_blocks = src->st_blocks, /* number of blocks allocated */ + .ust_atime = src->st_atime, /* time of last access */ + .ust_mtime = src->st_mtime, /* time of last modification */ + .ust_ctime = src->st_ctime, /* time of last change */ + }); +} + +int os_stat_fd(const int fd, struct uml_stat *ubuf) +{ + struct stat64 sbuf; + int err; + + do { + err = fstat64(fd, &sbuf); + } while((err < 0) && (errno == EINTR)) ; + + if(err < 0) + return(-errno); + + if(ubuf != NULL) + copy_stat(ubuf, &sbuf); + return(err); +} + +int os_stat_file(const char *file_name, struct uml_stat *ubuf) +{ + struct stat64 sbuf; + int err; + + do { + err = stat64(file_name, &sbuf); + } while((err < 0) && (errno == EINTR)) ; + + if(err < 0) + return(-errno); + + if(ubuf != NULL) + copy_stat(ubuf, &sbuf); + return(err); +} + +int os_access(const char* file, int mode) +{ + int amode, err; + + amode=(mode&OS_ACC_R_OK ? R_OK : 0) | (mode&OS_ACC_W_OK ? W_OK : 0) | + (mode&OS_ACC_X_OK ? X_OK : 0) | (mode&OS_ACC_F_OK ? F_OK : 0) ; + + err = access(file, amode); + if(err < 0) + return(-errno); + + return(0); +} + +void os_print_error(int error, const char* str) +{ + errno = error < 0 ? -error : error; + + perror(str); +} + +/* FIXME? required only by hostaudio (because it passes ioctls verbatim) */ +int os_ioctl_generic(int fd, unsigned int cmd, unsigned long arg) +{ + int err; + + err = ioctl(fd, cmd, arg); + if(err < 0) + return(-errno); + + return(err); +} + +int os_window_size(int fd, int *rows, int *cols) +{ + struct winsize size; + + if(ioctl(fd, TIOCGWINSZ, &size) < 0) + return(-errno); + + *rows = size.ws_row; + *cols = size.ws_col; + + return(0); +} + +int os_new_tty_pgrp(int fd, int pid) { - struct stat64 buf; + if(ioctl(fd, TIOCSCTTY, 0) < 0){ + printk("TIOCSCTTY failed, errno = %d\n", errno); + return(-errno); + } + + if(tcsetpgrp(fd, pid) < 0){ + printk("tcsetpgrp failed, errno = %d\n", errno); + return(-errno); + } + + return(0); +} + +/* FIXME: ensure namebuf in os_get_if_name is big enough */ +int os_get_ifname(int fd, char* namebuf) +{ + if(ioctl(fd, SIOCGIFNAME, namebuf) < 0) + return(-errno); + + return(0); +} + +int os_set_slip(int fd) +{ + int disc, sencap; + + disc = N_SLIP; + if(ioctl(fd, TIOCSETD, &disc) < 0){ + printk("Failed to set slip line discipline - " + "errno = %d\n", errno); + return(-errno); + } + + sencap = 0; + if(ioctl(fd, SIOCSIFENCAP, &sencap) < 0){ + printk("Failed to set slip encapsulation - " + "errno = %d\n", errno); + return(-errno); + } + + return(0); +} + +int os_set_owner(int fd, int pid) +{ + if(fcntl(fd, F_SETOWN, pid) < 0){ + int save_errno = errno; + + if(fcntl(fd, F_GETOWN, 0) != pid) + return(-save_errno); + } + + return(0); +} + +/* FIXME? moved wholesale from sigio_user.c to get fcntls out of that file */ +int os_sigio_async(int master, int slave) +{ + int flags; - if(stat64(file, &buf) == -1) + flags = fcntl(master, F_GETFL); + if(flags < 0) { + printk("fcntl F_GETFL failed, errno = %d\n", errno); return(-errno); + } + + if((fcntl(master, F_SETFL, flags | O_NONBLOCK | O_ASYNC) < 0) || + (fcntl(master, F_SETOWN, os_getpid()) < 0)){ + printk("fcntl F_SETFL or F_SETOWN failed, errno = %d\n", errno); + return(-errno); + } + + if((fcntl(slave, F_SETFL, flags | O_NONBLOCK) < 0)){ + printk("fcntl F_SETFL failed, errno = %d\n", errno); + return(-errno); + } - if(S_ISDIR(buf.st_mode)) return(OS_TYPE_DIR); - else if(S_ISLNK(buf.st_mode)) return(OS_TYPE_SYMLINK); - else if(S_ISCHR(buf.st_mode)) return(OS_TYPE_CHARDEV); - else if(S_ISBLK(buf.st_mode)) return(OS_TYPE_BLOCKDEV); - else if(S_ISFIFO(buf.st_mode)) return(OS_TYPE_FIFO); - else if(S_ISSOCK(buf.st_mode)) return(OS_TYPE_SOCK); + return(0); +} + +int os_mode_fd(int fd, int mode) +{ + int err; + + do { + err = fchmod(fd, mode); + } while((err < 0) && (errno==EINTR)) ; + + if(err < 0) + return(-errno); + + return(0); +} + +int os_file_type(char *file) +{ + struct uml_stat buf; + int err; + + err = os_stat_file(file, &buf); + if(err < 0) + return(err); + + if(S_ISDIR(buf.ust_mode)) return(OS_TYPE_DIR); + else if(S_ISLNK(buf.ust_mode)) return(OS_TYPE_SYMLINK); + else if(S_ISCHR(buf.ust_mode)) return(OS_TYPE_CHARDEV); + else if(S_ISBLK(buf.ust_mode)) return(OS_TYPE_BLOCKDEV); + else if(S_ISFIFO(buf.ust_mode)) return(OS_TYPE_FIFO); + else if(S_ISSOCK(buf.ust_mode)) return(OS_TYPE_SOCK); else return(OS_TYPE_FILE); } int os_file_mode(char *file, struct openflags *mode_out) { + int err; + *mode_out = OPENFLAGS(); - if(!access(file, W_OK)) *mode_out = of_write(*mode_out); - else if(errno != EACCES) - return(-errno); + err = os_access(file, OS_ACC_W_OK); + if((err < 0) && (err != -EACCES)) + return(err); - if(!access(file, R_OK)) *mode_out = of_read(*mode_out); - else if(errno != EACCES) - return(-errno); + *mode_out = of_write(*mode_out); + + err = os_access(file, OS_ACC_R_OK); + if((err < 0) && (err != -EACCES)) + return(err); + + *mode_out = of_read(*mode_out); return(0); } @@ -63,16 +267,14 @@ int os_open_file(char *file, struct open if(flags.e) f |= O_EXCL; fd = open64(file, f, mode); - if(fd < 0) return(-errno); - - if(flags.cl){ - if(fcntl(fd, F_SETFD, 1)){ - close(fd); - return(-errno); - } + if(fd < 0) + return(-errno); + + if(flags.cl && fcntl(fd, F_SETFD, 1)){ + os_close_file(fd); + return(-errno); } - return(fd); return(fd); } @@ -90,7 +292,7 @@ int os_connect_socket(char *name) err = connect(fd, (struct sockaddr *) &sock, sizeof(sock)); if(err) - return(err); + return(-errno); return(fd); } @@ -109,88 +311,162 @@ int os_seek_file(int fd, __u64 offset) return(0); } -int os_read_file(int fd, void *buf, int len) +static int fault_buffer(void *start, int len, + int (*copy_proc)(void *addr, void *buf, int len)) { - int n; + int page = getpagesize(), i; + char c; - /* Force buf into memory if it's not already. */ + for(i = 0; i < len; i += page){ + if((*copy_proc)(start + i, &c, sizeof(c))) + return(-EFAULT); + } + if((len % page) != 0){ + if((*copy_proc)(start + len - 1, &c, sizeof(c))) + return(-EFAULT); + } + return(0); +} - /* XXX This fails if buf is kernel memory */ -#ifdef notdef - if(copy_to_user_proc(buf, &c, sizeof(c))) - return(-EFAULT); -#endif +static int file_io(int fd, void *buf, int len, + int (*io_proc)(int fd, void *buf, int len), + int (*copy_user_proc)(void *addr, void *buf, int len)) +{ + int n, err; + + do { + n = (*io_proc)(fd, buf, len); + if((n < 0) && (errno == EFAULT)){ + err = fault_buffer(buf, len, copy_user_proc); + if(err) + return(err); + n = (*io_proc)(fd, buf, len); + } + } while((n < 0) && (errno == EINTR)); - n = read(fd, buf, len); if(n < 0) return(-errno); return(n); } -int os_write_file(int fd, void *buf, int count) +int os_read_file(int fd, void *buf, int len) { - int n; - - /* Force buf into memory if it's not already. */ - - /* XXX This fails if buf is kernel memory */ -#ifdef notdef - if(copy_to_user_proc(buf, buf, buf[0])) - return(-EFAULT); -#endif + return(file_io(fd, buf, len, (int (*)(int, void *, int)) read, + copy_from_user_proc)); +} - n = write(fd, buf, count); - if(n < 0) - return(-errno); - return(n); +int os_write_file(int fd, const void *buf, int len) +{ + return(file_io(fd, (void *) buf, len, + (int (*)(int, void *, int)) write, copy_to_user_proc)); } int os_file_size(char *file, long long *size_out) { - struct stat64 buf; + struct uml_stat buf; + int err; - if(stat64(file, &buf) == -1){ - printk("Couldn't stat \"%s\" : errno = %d\n", file, errno); - return(-errno); + err = os_stat_file(file, &buf); + if(err < 0){ + printk("Couldn't stat \"%s\" : err = %d\n", file, -err); + return(err); } - if(S_ISBLK(buf.st_mode)){ + + if(S_ISBLK(buf.ust_mode)){ int fd, blocks; - if((fd = open64(file, O_RDONLY)) < 0){ - printk("Couldn't open \"%s\", errno = %d\n", file, - errno); - return(-errno); + fd = os_open_file(file, of_read(OPENFLAGS()), 0); + if(fd < 0){ + printk("Couldn't open \"%s\", errno = %d\n", file, -fd); + return(fd); } if(ioctl(fd, BLKGETSIZE, &blocks) < 0){ printk("Couldn't get the block size of \"%s\", " "errno = %d\n", file, errno); - close(fd); - return(-errno); + err = -errno; + os_close_file(fd); + return(err); } *size_out = ((long long) blocks) * 512; - close(fd); + os_close_file(fd); return(0); } - *size_out = buf.st_size; + *size_out = buf.ust_size; + return(0); +} + +int os_file_modtime(char *file, unsigned long *modtime) +{ + struct uml_stat buf; + int err; + + err = os_stat_file(file, &buf); + if(err < 0){ + printk("Couldn't stat \"%s\" : err = %d\n", file, -err); + return(err); + } + + *modtime = buf.ust_mtime; return(0); } +int os_get_exec_close(int fd, int* close_on_exec) +{ + int ret; + + do { + ret = fcntl(fd, F_GETFD); + } while((ret < 0) && (errno == EINTR)) ; + + if(ret < 0) + return(-errno); + + *close_on_exec = (ret&FD_CLOEXEC) ? 1 : 0; + return(ret); +} + +int os_set_exec_close(int fd, int close_on_exec) +{ + int flag, err; + + if(close_on_exec) flag = FD_CLOEXEC; + else flag = 0; + + do { + err = fcntl(fd, F_SETFD, flag); + } while((err < 0) && (errno == EINTR)) ; + + if(err < 0) + return(-errno); + return(err); +} + int os_pipe(int *fds, int stream, int close_on_exec) { int err, type = stream ? SOCK_STREAM : SOCK_DGRAM; err = socketpair(AF_UNIX, type, 0, fds); - if(err) + if(err < 0) return(-errno); if(!close_on_exec) return(0); - if((fcntl(fds[0], F_SETFD, 1) < 0) || (fcntl(fds[1], F_SETFD, 1) < 0)) - printk("os_pipe : Setting FD_CLOEXEC failed, errno = %d", - errno); + err = os_set_exec_close(fds[0], 1); + if(err < 0) + goto error; + + err = os_set_exec_close(fds[1], 1); + if(err < 0) + goto error; return(0); + + error: + printk("os_pipe : Setting FD_CLOEXEC failed, err = %d\n", -err); + os_close_file(fds[1]); + os_close_file(fds[0]); + return(err); } int os_set_fd_async(int fd, int owner) @@ -270,7 +546,7 @@ int os_shutdown_socket(int fd, int r, in return(-EINVAL); } err = shutdown(fd, what); - if(err) + if(err < 0) return(-errno); return(0); } @@ -315,7 +591,7 @@ int os_rcv_fd(int fd, int *helper_pid_ou return(new); } -int create_unix_socket(char *file, int len) +int os_create_unix_socket(char *file, int len, int close_on_exec) { struct sockaddr_un addr; int sock, err; @@ -327,6 +603,13 @@ int create_unix_socket(char *file, int l return(-errno); } + if(close_on_exec) { + err = os_set_exec_close(sock, 1); + if(err < 0) + printk("create_unix_socket : close_on_exec failed, " + "err = %d", -err); + } + addr.sun_family = AF_UNIX; /* XXX Be more careful about overflow */ @@ -334,14 +617,45 @@ int create_unix_socket(char *file, int l err = bind(sock, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0){ - printk("create_listening_socket - bind failed, errno = %d\n", - errno); + printk("create_listening_socket at '%s' - bind failed, " + "errno = %d\n", file, errno); return(-errno); } return(sock); } +void os_flush_stdout(void) +{ + fflush(stdout); +} + +int os_lock_file(int fd, int excl) +{ + int type = excl ? F_WRLCK : F_RDLCK; + struct flock lock = ((struct flock) { .l_type = type, + .l_whence = SEEK_SET, + .l_start = 0, + .l_len = 0 } ); + int err, save; + + err = fcntl(fd, F_SETLK, &lock); + if(!err) + goto out; + + save = -errno; + err = fcntl(fd, F_GETLK, &lock); + if(err){ + err = -errno; + goto out; + } + + printk("F_SETLK failed, file already locked by pid %d\n", lock.l_pid); + err = save; + 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 --- linux-2.6.9-rc1/arch/um/os-Linux/Makefile 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/um/os-Linux/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -3,13 +3,9 @@ # Licensed under the GPL # -obj-y = file.o process.o tty.o drivers/ +obj-y = file.o process.o tty.o user_syms.o drivers/ USER_OBJS := $(foreach file,file.o process.o tty.o,$(obj)/$(file)) $(USER_OBJS) : %.o: %.c $(CC) $(CFLAGS_$(notdir $@)) $(USER_CFLAGS) -c -o $@ $< - -clean : - -archmrproper: --- linux-2.6.9-rc1/arch/um/os-Linux/process.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/um/os-Linux/process.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2002 Jeff Dike (jdike@addtoit.com) * Licensed under the GPL */ @@ -7,35 +7,44 @@ #include #include #include +#include #include #include #include "os.h" #include "user.h" +#include "user_util.h" + +#define ARBITRARY_ADDR -1 +#define FAILURE_PID -1 + +#define STAT_PATH_LEN sizeof("/proc/#######/stat\0") +#define COMM_SCANF "%*[^)])" unsigned long os_process_pc(int pid) { - char proc_stat[sizeof("/proc/#####/stat\0")], buf[256]; + char proc_stat[STAT_PATH_LEN], buf[256]; unsigned long pc; - int fd; + int fd, err; sprintf(proc_stat, "/proc/%d/stat", pid); fd = os_open_file(proc_stat, of_read(OPENFLAGS()), 0); if(fd < 0){ - printk("os_process_pc - couldn't open '%s', errno = %d\n", - proc_stat, errno); - return(-1); + printk("os_process_pc - couldn't open '%s', err = %d\n", + proc_stat, -fd); + return(ARBITRARY_ADDR); } - if(read(fd, buf, sizeof(buf)) < 0){ - printk("os_process_pc - couldn't read '%s', errno = %d\n", - proc_stat, errno); - close(fd); - return(-1); + err = os_read_file(fd, buf, sizeof(buf)); + if(err < 0){ + printk("os_process_pc - couldn't read '%s', err = %d\n", + proc_stat, -err); + os_close_file(fd); + return(ARBITRARY_ADDR); } - close(fd); - pc = -1; - if(sscanf(buf, "%*d %*s %*c %*d %*d %*d %*d %*d %*d %*d %*d " + os_close_file(fd); + pc = ARBITRARY_ADDR; + if(sscanf(buf, "%*d " COMM_SCANF " %*c %*d %*d %*d %*d %*d %*d %*d %*d " "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d " - "%*d %*d %*d %*d %ld", &pc) != 1){ + "%*d %*d %*d %*d %lu", &pc) != 1){ printk("os_process_pc - couldn't find pc in '%s'\n", buf); } return(pc); @@ -43,7 +52,7 @@ unsigned long os_process_pc(int pid) int os_process_parent(int pid) { - char stat[sizeof("/proc/nnnnn/stat\0")]; + char stat[STAT_PATH_LEN]; char data[256]; int parent, n, fd; @@ -52,22 +61,22 @@ int os_process_parent(int pid) snprintf(stat, sizeof(stat), "/proc/%d/stat", pid); fd = os_open_file(stat, of_read(OPENFLAGS()), 0); if(fd < 0){ - printk("Couldn't open '%s', errno = %d\n", stat, -fd); - return(-1); + printk("Couldn't open '%s', err = %d\n", stat, -fd); + return(FAILURE_PID); } - n = read(fd, data, sizeof(data)); - close(fd); + n = os_read_file(fd, data, sizeof(data)); + os_close_file(fd); if(n < 0){ - printk("Couldn't read '%s', errno = %d\n", stat); - return(-1); + printk("Couldn't read '%s', err = %d\n", stat, -n); + return(FAILURE_PID); } - parent = -1; - /* XXX This will break if there is a space in the command */ - n = sscanf(data, "%*d %*s %*c %d", &parent); - if(n != 1) printk("Failed to scan '%s'\n", data); + parent = FAILURE_PID; + n = sscanf(data, "%*d " COMM_SCANF " %*c %d", &parent); + if(n != 1) + printk("Failed to scan '%s'\n", data); return(parent); } @@ -81,13 +90,17 @@ void os_kill_process(int pid, int reap_c { kill(pid, SIGKILL); if(reap_child) - waitpid(pid, NULL, 0); + CATCH_EINTR(waitpid(pid, NULL, 0)); } void os_usr1_process(int pid) { +#ifdef __NR_tkill + syscall(__NR_tkill, pid, SIGUSR1); +#else kill(pid, SIGUSR1); +#endif } int os_getpid(void) @@ -95,7 +108,7 @@ int os_getpid(void) return(getpid()); } -int os_map_memory(void *virt, int fd, unsigned long off, unsigned long len, +int os_map_memory(void *virt, int fd, unsigned long long off, unsigned long len, int r, int w, int x) { void *loc; @@ -104,8 +117,8 @@ int os_map_memory(void *virt, int fd, un prot = (r ? PROT_READ : 0) | (w ? PROT_WRITE : 0) | (x ? PROT_EXEC : 0); - loc = mmap((void *) virt, len, prot, MAP_SHARED | MAP_FIXED, - fd, off); + loc = mmap64((void *) virt, len, prot, MAP_SHARED | MAP_FIXED, + fd, off); if(loc == MAP_FAILED) return(-errno); return(0); @@ -126,7 +139,8 @@ int os_unmap_memory(void *addr, int len) int err; err = munmap(addr, len); - if(err < 0) return(-errno); + if(err < 0) + return(-errno); return(0); } --- linux-2.6.9-rc1/arch/um/os-Linux/tty.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/um/os-Linux/tty.c 2004-09-02 20:51:51.000000000 -0700 @@ -28,10 +28,10 @@ int get_pty(void) struct grantpt_info info; int fd; - if((fd = os_open_file("/dev/ptmx", of_rdwr(OPENFLAGS()), 0)) < 0){ - printk("get_pty : Couldn't open /dev/ptmx - errno = %d\n", - errno); - return(-1); + fd = os_open_file("/dev/ptmx", of_rdwr(OPENFLAGS()), 0); + if(fd < 0){ + printk("get_pty : Couldn't open /dev/ptmx - err = %d\n", -fd); + return(fd); } info.fd = fd; @@ -39,7 +39,7 @@ int get_pty(void) if(info.res < 0){ printk("get_pty : Couldn't grant pty - errno = %d\n", - info.err); + -info.err); return(-1); } if(unlockpt(fd) < 0){ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/um/os-Linux/user_syms.c 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,88 @@ +#include "linux/types.h" +#include "linux/module.h" + +/* Some of this are builtin function (some are not but could in the future), + * so I *must* declare good prototypes for them and then EXPORT them. + * The kernel code uses the macro defined by include/linux/string.h, + * so I undef macros; the userspace code does not include that and I + * add an EXPORT for the glibc one.*/ + +#undef strlen +#undef strstr +#undef memcpy +#undef memset + +extern size_t strlen(const char *); +extern void *memcpy(void *, const void *, size_t); +extern void *memset(void *, int, size_t); +extern int printf(const char *, ...); + +EXPORT_SYMBOL(strlen); +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memset); +EXPORT_SYMBOL(printf); + +EXPORT_SYMBOL(strstr); + +/* Here, instead, I can provide a fake prototype. Yes, someone cares: genksyms. + * However, the modules will use the CRC defined *here*, no matter if it is + * good; so the versions of these symbols will always match + */ +#define EXPORT_SYMBOL_PROTO(sym) \ + int sym(void); \ + EXPORT_SYMBOL(sym); + +EXPORT_SYMBOL_PROTO(__errno_location); + +EXPORT_SYMBOL_PROTO(access); +EXPORT_SYMBOL_PROTO(open); +EXPORT_SYMBOL_PROTO(open64); +EXPORT_SYMBOL_PROTO(close); +EXPORT_SYMBOL_PROTO(read); +EXPORT_SYMBOL_PROTO(write); +EXPORT_SYMBOL_PROTO(dup2); +EXPORT_SYMBOL_PROTO(__xstat); +EXPORT_SYMBOL_PROTO(__lxstat); +EXPORT_SYMBOL_PROTO(__lxstat64); +EXPORT_SYMBOL_PROTO(lseek); +EXPORT_SYMBOL_PROTO(lseek64); +EXPORT_SYMBOL_PROTO(chown); +EXPORT_SYMBOL_PROTO(truncate); +EXPORT_SYMBOL_PROTO(utime); +EXPORT_SYMBOL_PROTO(chmod); +EXPORT_SYMBOL_PROTO(rename); +EXPORT_SYMBOL_PROTO(__xmknod); + +EXPORT_SYMBOL_PROTO(symlink); +EXPORT_SYMBOL_PROTO(link); +EXPORT_SYMBOL_PROTO(unlink); +EXPORT_SYMBOL_PROTO(readlink); + +EXPORT_SYMBOL_PROTO(mkdir); +EXPORT_SYMBOL_PROTO(rmdir); +EXPORT_SYMBOL_PROTO(opendir); +EXPORT_SYMBOL_PROTO(readdir); +EXPORT_SYMBOL_PROTO(closedir); +EXPORT_SYMBOL_PROTO(seekdir); +EXPORT_SYMBOL_PROTO(telldir); + +EXPORT_SYMBOL_PROTO(ioctl); + +EXPORT_SYMBOL_PROTO(pread64); +EXPORT_SYMBOL_PROTO(pwrite64); + +EXPORT_SYMBOL_PROTO(statfs); +EXPORT_SYMBOL_PROTO(statfs64); + +EXPORT_SYMBOL_PROTO(getuid); + +/* + * 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: + */ --- linux-2.6.9-rc1/arch/um/sys-i386/bugs.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/arch/um/sys-i386/bugs.c 2004-09-02 20:51:51.000000000 -0700 @@ -4,20 +4,21 @@ */ #include -#include #include #include #include +#include #include "kern_util.h" #include "user.h" #include "sysdep/ptrace.h" #include "task.h" +#include "os.h" #define MAXTOKEN 64 /* Set during early boot */ -int cpu_has_cmov = 1; -int cpu_has_xmm = 0; +int host_has_cmov = 1; +int host_has_xmm = 0; static char token(int fd, char *buf, int len, char stop) { @@ -27,13 +28,15 @@ static char token(int fd, char *buf, int ptr = buf; end = &buf[len]; do { - n = read(fd, ptr, sizeof(*ptr)); + n = os_read_file(fd, ptr, sizeof(*ptr)); c = *ptr++; - if(n == 0) return(0); - else if(n != sizeof(*ptr)){ - printk("Reading /proc/cpuinfo failed, " - "errno = %d\n", errno); - return(-errno); + if(n != sizeof(*ptr)){ + if(n == 0) return(0); + printk("Reading /proc/cpuinfo failed, err = %d\n", -n); + if(n < 0) + return(n); + else + return(-EIO); } } while((c != '\n') && (c != stop) && (ptr < end)); @@ -45,45 +48,79 @@ static char token(int fd, char *buf, int return(c); } -static int check_cpu_feature(char *feature, int *have_it) +static int find_cpuinfo_line(int fd, char *key, char *scratch, int len) { - char buf[MAXTOKEN], c; - int fd, len = sizeof(buf)/sizeof(buf[0]), n; - - printk("Checking for host processor %s support...", feature); - fd = open("/proc/cpuinfo", O_RDONLY); - if(fd < 0){ - printk("Couldn't open /proc/cpuinfo, errno = %d\n", errno); - return(0); - } + int n; + char c; - *have_it = 0; - buf[len - 1] = '\0'; + scratch[len - 1] = '\0'; while(1){ - c = token(fd, buf, len - 1, ':'); - if(c <= 0) goto out; + c = token(fd, scratch, len - 1, ':'); + if(c <= 0) + return(0); else if(c != ':'){ printk("Failed to find ':' in /proc/cpuinfo\n"); - goto out; + return(0); } - if(!strncmp(buf, "flags", strlen("flags"))) break; + if(!strncmp(scratch, key, strlen(key))) + return(1); do { - n = read(fd, &c, sizeof(c)); + n = os_read_file(fd, &c, sizeof(c)); if(n != sizeof(c)){ printk("Failed to find newline in " - "/proc/cpuinfo, n = %d, errno = %d\n", - n, errno); - goto out; + "/proc/cpuinfo, err = %d\n", -n); + return(0); } } while(c != '\n'); } + return(0); +} + +int cpu_feature(char *what, char *buf, int len) +{ + int fd, ret = 0; + + fd = os_open_file("/proc/cpuinfo", of_read(OPENFLAGS()), 0); + if(fd < 0){ + printk("Couldn't open /proc/cpuinfo, err = %d\n", -fd); + return(0); + } + + if(!find_cpuinfo_line(fd, what, buf, len)){ + printk("Couldn't find '%s' line in /proc/cpuinfo\n", what); + goto out_close; + } + + token(fd, buf, len, '\n'); + ret = 1; + + out_close: + os_close_file(fd); + return(ret); +} + +static int check_cpu_flag(char *feature, int *have_it) +{ + char buf[MAXTOKEN], c; + int fd, len = sizeof(buf)/sizeof(buf[0]); + + printk("Checking for host processor %s support...", feature); + fd = os_open_file("/proc/cpuinfo", of_read(OPENFLAGS()), 0); + if(fd < 0){ + printk("Couldn't open /proc/cpuinfo, err = %d\n", -fd); + return(0); + } + + *have_it = 0; + if(!find_cpuinfo_line(fd, "flags", buf, sizeof(buf) / sizeof(buf[0]))) + goto out; c = token(fd, buf, len - 1, ' '); if(c < 0) goto out; else if(c != ' '){ - printk("Failed to find ':' in /proc/cpuinfo\n"); + printk("Failed to find ' ' in /proc/cpuinfo\n"); goto out; } @@ -100,21 +137,48 @@ static int check_cpu_feature(char *featu out: if(*have_it == 0) printk("No\n"); else if(*have_it == 1) printk("Yes\n"); - close(fd); + os_close_file(fd); return(1); } +#if 0 /* This doesn't work in tt mode, plus it's causing compilation problems + * for some people. + */ +static void disable_lcall(void) +{ + struct modify_ldt_ldt_s ldt; + int err; + + bzero(&ldt, sizeof(ldt)); + ldt.entry_number = 7; + ldt.base_addr = 0; + ldt.limit = 0; + err = modify_ldt(1, &ldt, sizeof(ldt)); + if(err) + printk("Failed to disable lcall7 - errno = %d\n", errno); +} +#endif + +void arch_init_thread(void) +{ +#if 0 + disable_lcall(); +#endif +} + void arch_check_bugs(void) { int have_it; - if(access("/proc/cpuinfo", R_OK)){ + if(os_access("/proc/cpuinfo", OS_ACC_R_OK) < 0){ printk("/proc/cpuinfo not available - skipping CPU capability " "checks\n"); return; } - if(check_cpu_feature("cmov", &have_it)) cpu_has_cmov = have_it; - if(check_cpu_feature("xmm", &have_it)) cpu_has_xmm = have_it; + if(check_cpu_flag("cmov", &have_it)) + host_has_cmov = have_it; + if(check_cpu_flag("xmm", &have_it)) + host_has_xmm = have_it; } int arch_handle_signal(int sig, union uml_pt_regs *regs) @@ -130,18 +194,18 @@ int arch_handle_signal(int sig, union um if((*((char *) ip) != 0x0f) || ((*((char *) (ip + 1)) & 0xf0) != 0x40)) return(0); - if(cpu_has_cmov == 0) + if(host_has_cmov == 0) panic("SIGILL caused by cmov, which this processor doesn't " "implement, boot a filesystem compiled for older " "processors"); - else if(cpu_has_cmov == 1) + else if(host_has_cmov == 1) panic("SIGILL caused by cmov, which this processor claims to " "implement"); - else if(cpu_has_cmov == -1) + else if(host_has_cmov == -1) panic("SIGILL caused by cmov, couldn't tell if this processor " "implements it, boot a filesystem compiled for older " "processors"); - else panic("Bad value for cpu_has_cmov (%d)", cpu_has_cmov); + else panic("Bad value for host_has_cmov (%d)", host_has_cmov); return(0); } --- linux-2.6.9-rc1/arch/um/sys-i386/extable.c 2004-08-24 00:03:32.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,30 +0,0 @@ -/* - * linux/arch/i386/mm/extable.c - */ - -#include -#include -#include -#include - -/* Simple binary search */ -const struct exception_table_entry * -search_extable(const struct exception_table_entry *first, - const struct exception_table_entry *last, - unsigned long value) -{ - while (first <= last) { - const struct exception_table_entry *mid; - long diff; - - mid = (last - first) / 2 + first; - diff = mid->insn - value; - if (diff == 0) - return mid; - else if (diff < 0) - first = mid+1; - else - last = mid-1; - } - return NULL; -} --- linux-2.6.9-rc1/arch/um/sys-i386/fault.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/um/sys-i386/fault.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2002 - 2004 Jeff Dike (jdike@addtoit.com) * Licensed under the GPL */ @@ -7,16 +7,24 @@ #include "sysdep/ptrace.h" #include "sysdep/sigcontext.h" -extern unsigned long search_exception_table(unsigned long addr); +/* These two are from asm-um/uaccess.h and linux/module.h, check them. */ +struct exception_table_entry +{ + unsigned long insn; + unsigned long fixup; +}; +const struct exception_table_entry *search_exception_tables(unsigned long add); + +/* Compare this to arch/i386/mm/extable.c:fixup_exception() */ int arch_fixup(unsigned long address, void *sc_ptr) { struct sigcontext *sc = sc_ptr; - unsigned long fixup; + const struct exception_table_entry *fixup; fixup = search_exception_tables(address); if(fixup != 0){ - sc->eip = fixup; + sc->eip = fixup->fixup; return(1); } return(0); --- linux-2.6.9-rc1/arch/um/sys-i386/Makefile 2004-08-24 00:02:19.000000000 -0700 +++ 25/arch/um/sys-i386/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -1,14 +1,18 @@ -obj-y = bugs.o checksum.o extable.o fault.o ksyms.o ldt.o module.o \ - ptrace.o ptrace_user.o semaphore.o sigcontext.o syscalls.o sysrq.o +obj-y = bitops.o bugs.o checksum.o fault.o ksyms.o ldt.o ptrace.o \ + ptrace_user.o semaphore.o sigcontext.o syscalls.o sysrq.o time.o obj-$(CONFIG_HIGHMEM) += highmem.o +obj-$(CONFIG_MODULES) += module.o USER_OBJS := bugs.o ptrace_user.o sigcontext.o fault.o USER_OBJS := $(foreach file,$(USER_OBJS),$(obj)/$(file)) -SYMLINKS = semaphore.c highmem.c module.c +SYMLINKS = bitops.c semaphore.c highmem.c module.c SYMLINKS := $(foreach f,$(SYMLINKS),$(src)/$f) +clean-files := $(SYMLINKS) + +bitops.c-dir = lib semaphore.c-dir = kernel highmem.c-dir = mm module.c-dir = kernel @@ -24,19 +28,4 @@ $(USER_OBJS) : %.o: %.c $(SYMLINKS): $(call make_link,$@) -clean: - $(MAKE) -C util clean - -fastdep: - -dep: - -archmrproper: - rm -f $(SYMLINKS) - -archclean: - -archdep: - -modules: - +subdir- := util --- linux-2.6.9-rc1/arch/um/sys-i386/ptrace_user.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/arch/um/sys-i386/ptrace_user.c 2004-09-02 20:51:51.000000000 -0700 @@ -39,10 +39,10 @@ static void write_debugregs(int pid, uns nregs = sizeof(dummy->u_debugreg)/sizeof(dummy->u_debugreg[0]); for(i = 0; i < nregs; i++){ if((i == 4) || (i == 5)) continue; - if(ptrace(PTRACE_POKEUSR, pid, &dummy->u_debugreg[i], + if(ptrace(PTRACE_POKEUSER, pid, &dummy->u_debugreg[i], regs[i]) < 0) - printk("write_debugregs - ptrace failed, " - "errno = %d\n", errno); + printk("write_debugregs - ptrace failed on " + "register %d, errno = %d\n", errno); } } @@ -54,7 +54,7 @@ static void read_debugregs(int pid, unsi dummy = NULL; nregs = sizeof(dummy->u_debugreg)/sizeof(dummy->u_debugreg[0]); for(i = 0; i < nregs; i++){ - regs[i] = ptrace(PTRACE_PEEKUSR, pid, + regs[i] = ptrace(PTRACE_PEEKUSER, pid, &dummy->u_debugreg[i], 0); } } --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/um/sys-i386/time.c 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,24 @@ +/* + * sys-i386/time.c + * Created 25.9.2002 Sapan Bhatia + * + */ + +unsigned long long time_stamp(void) +{ + unsigned long low, high; + + asm("rdtsc" : "=a" (low), "=d" (high)); + return((((unsigned long long) high) << 32) + low); +} + +/* + * 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: + */ --- linux-2.6.9-rc1/arch/um/sys-i386/util/Makefile 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/um/sys-i386/util/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -1,15 +1,11 @@ -hostprogs-y := mk_sc -always := $(hostprogs-y) mk_thread -targets := mk_thread_kern.o mk_thread_user.o +hostprogs-y := mk_sc mk_thread +always := $(hostprogs-y) -mk_sc-objs := mk_sc.o +mk_thread-objs := mk_thread_kern.o mk_thread_user.o -$(obj)/mk_thread : $(obj)/mk_thread_kern.o $(obj)/mk_thread_user.o - $(CC) $(CFLAGS) -o $@ $^ - -$(obj)/mk_thread_user.o : $(src)/mk_thread_user.c - $(CC) $(USER_CFLAGS) -c -o $@ $< +HOSTCFLAGS_mk_thread_kern.o := $(CFLAGS) $(CPPFLAGS) +HOSTCFLAGS_mk_thread_user.o := $(USER_CFLAGS) clean : $(RM) -f $(build-targets) --- linux-2.6.9-rc1/arch/um/sys-i386/util/mk_sc.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/um/sys-i386/util/mk_sc.c 2004-09-02 20:51:51.000000000 -0700 @@ -38,6 +38,7 @@ int main(int argc, char **argv) SC_OFFSET("SC_ERR", err); SC_OFFSET("SC_CR2", cr2); SC_OFFSET("SC_FPSTATE", fpstate); + SC_OFFSET("SC_SIGMASK", oldmask); SC_FP_OFFSET("SC_FP_CW", cw); SC_FP_OFFSET("SC_FP_SW", sw); SC_FP_OFFSET("SC_FP_TAG", tag); --- linux-2.6.9-rc1/arch/um/sys-ia64/Makefile 2004-08-24 00:01:52.000000000 -0700 +++ 25/arch/um/sys-ia64/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -7,18 +7,5 @@ all: $(OBJ) $(OBJ): $(OBJS) rm -f $@ $(LD) $(LINKFLAGS) --start-group $^ --end-group -o $@ -clean: - rm -f $(OBJS) -fastdep: - -archmrproper: - -archclean: - rm -f link.ld - @$(MAKEBOOT) clean - -archdep: - @$(MAKEBOOT) dep - -modules: +clean-files := $(OBJS) link.ld --- linux-2.6.9-rc1/arch/um/sys-ppc/Makefile 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/um/sys-ppc/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -66,13 +66,4 @@ misc.o: misc.S ppc_defs.h $(CC) $(EXTRA_AFLAGS) $(AFLAGS) -D__ASSEMBLY__ -D__UM_PPC__ -c $< -o $*.o rm -f asm -clean: - rm -f $(OBJS) - rm -f ppc_defs.h - rm -f checksum.S semaphore.c mk_defs.c - -fastdep: - -dep: - -modules: +clean-files := $(OBJS) ppc_defs.h checksum.S semaphore.c mk_defs.c --- linux-2.6.9-rc1/arch/um/uml.lds.S 2004-08-24 00:02:33.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,92 +0,0 @@ -#include - -OUTPUT_FORMAT(ELF_FORMAT) -OUTPUT_ARCH(ELF_ARCH) -ENTRY(_start) -jiffies = jiffies_64; - -SECTIONS -{ - . = START + SIZEOF_HEADERS; - - . = ALIGN(4096); - __binary_start = .; -#ifdef MODE_TT - .thread_private : { - __start_thread_private = .; - errno = .; - . += 4; - arch/um/kernel/tt/unmap_fin.o (.data) - __end_thread_private = .; - } - . = ALIGN(4096); - .remap : { arch/um/kernel/tt/unmap_fin.o (.text) } -#endif - - . = ALIGN(4096); /* Init code and data */ - _stext = .; - __init_begin = .; - .text.init : { *(.text.init) } - . = ALIGN(4096); - .text : - { - *(.text) - /* .gnu.warning sections are handled specially by elf32.em. */ - *(.gnu.warning) - *(.gnu.linkonce.t*) - } - - #include "asm/common.lds.S" - - .data.init : { *(.data.init) } - .data : - { - . = ALIGN(KERNEL_STACK_SIZE); /* init_task */ - *(.data.init_task) - *(.data) - *(.gnu.linkonce.d*) - CONSTRUCTORS - } - .data1 : { *(.data1) } - .ctors : - { - *(.ctors) - } - .dtors : - { - *(.dtors) - } - - .got : { *(.got.plt) *(.got) } - .dynamic : { *(.dynamic) } - /* We want the small data sections together, so single-instruction offsets - can access them all, and initialized data all before uninitialized, so - we can shorten the on-disk segment size. */ - .sdata : { *(.sdata) } - _edata = .; - PROVIDE (edata = .); - . = ALIGN(0x1000); - .sbss : - { - __bss_start = .; - PROVIDE(_bss_start = .); - *(.sbss) - *(.scommon) - } - .bss : - { - *(.dynbss) - *(.bss) - *(COMMON) - } - _end = . ; - PROVIDE (end = .); - /* Stabs debugging sections. */ - .stab 0 : { *(.stab) } - .stabstr 0 : { *(.stabstr) } - .stab.excl 0 : { *(.stab.excl) } - .stab.exclstr 0 : { *(.stab.exclstr) } - .stab.index 0 : { *(.stab.index) } - .stab.indexstr 0 : { *(.stab.indexstr) } - .comment 0 : { *(.comment) } -} --- linux-2.6.9-rc1/arch/um/util/Makefile 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/um/util/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -1,23 +1,8 @@ -always := mk_task mk_constants -targets := mk_task_user.o mk_task_kern.o \ - mk_constants_user.o mk_constants_kern.o +hostprogs-y := mk_task mk_constants +always := $(hostprogs-y) -$(obj)/mk_task: $(obj)/mk_task_user.o $(obj)/mk_task_kern.o - $(CC) -o $@ $^ +mk_task-objs := mk_task_user.o mk_task_kern.o +mk_constants-objs := mk_constants_user.o mk_constants_kern.o -$(obj)/mk_task_user.o: $(src)/mk_task_user.c - $(CC) -o $@ -c $< - -$(obj)/mk_constants : $(obj)/mk_constants_user.o $(obj)/mk_constants_kern.o - $(CC) -o $@ $^ - -$(obj)/mk_constants_user.o : $(src)/mk_constants_user.c - $(CC) -c $< -o $@ - -$(obj)/mk_constants_kern.o : $(src)/mk_constants_kern.c - $(CC) $(CFLAGS) -c $< -o $@ - -clean: - $(RM) $(build-targets) - -archmrproper: +HOSTCFLAGS_mk_task_kern.o := $(CFLAGS) $(CPPFLAGS) +HOSTCFLAGS_mk_constants_kern.o := $(CFLAGS) $(CPPFLAGS) --- linux-2.6.9-rc1/arch/um/util/mk_constants_kern.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/arch/um/util/mk_constants_kern.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,5 +1,6 @@ #include "linux/kernel.h" #include "linux/stringify.h" +#include "linux/time.h" #include "asm/page.h" extern void print_head(void); @@ -11,6 +12,7 @@ int main(int argc, char **argv) { print_head(); print_constant_int("UM_KERN_PAGE_SIZE", PAGE_SIZE); + print_constant_str("UM_KERN_EMERG", KERN_EMERG); print_constant_str("UM_KERN_ALERT", KERN_ALERT); print_constant_str("UM_KERN_CRIT", KERN_CRIT); @@ -19,6 +21,8 @@ int main(int argc, char **argv) print_constant_str("UM_KERN_NOTICE", KERN_NOTICE); print_constant_str("UM_KERN_INFO", KERN_INFO); print_constant_str("UM_KERN_DEBUG", KERN_DEBUG); + + print_constant_int("UM_NSEC_PER_SEC", NSEC_PER_SEC); print_tail(); return(0); } --- linux-2.6.9-rc1/arch/v850/kernel/asm-consts.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/v850/kernel/asm-consts.c 2004-09-03 00:56:41.008926344 -0700 @@ -12,8 +12,8 @@ #include #include #include +#include #include -#include #include #define DEFINE(sym, val) \ --- linux-2.6.9-rc1/arch/v850/kernel/irq.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/v850/kernel/irq.c 2004-09-02 20:51:51.000000000 -0700 @@ -141,13 +141,15 @@ skip: int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * action) { int status = 1; /* Force the "do bottom halves" bit */ + int ret; if (!(action->flags & SA_INTERRUPT)) local_irq_enable(); do { - status |= action->flags; - action->handler(irq, action->dev_id, regs); + ret = action->handler(irq, action->dev_id, regs); + if (ret == IRQ_HANDLED) + status |= action->flags; action = action->next; } while (action); if (status & SA_SAMPLE_RANDOM) --- linux-2.6.9-rc1/arch/v850/kernel/ptrace.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/v850/kernel/ptrace.c 2004-09-03 00:56:56.202616552 -0700 @@ -147,14 +147,8 @@ int sys_ptrace(long request, long pid, l rval = ptrace_attach(child); goto out_tsk; } - rval = -ESRCH; - if (!(child->ptrace & PT_PTRACED)) - goto out_tsk; - if (child->state != TASK_STOPPED) { - if (request != PTRACE_KILL) - goto out_tsk; - } - if (child->parent != current) + ret = ptrace_check_attach(child, request == PTRACE_KILL); + if (ret < 0) goto out_tsk; switch (request) { @@ -269,11 +263,8 @@ asmlinkage void syscall_trace(void) return; /* The 0x80 provides a way for the tracing parent to distinguish between a syscall stop and SIGTRAP delivery */ - current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) - ? 0x80 : 0); - current->state = TASK_STOPPED; - notify_parent(current, SIGCHLD); - schedule(); + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); /* * this isn't the same as continuing with a signal, but it will do * for normal use. strace only continues with a signal if the --- linux-2.6.9-rc1/arch/v850/kernel/setup.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/v850/kernel/setup.c 2004-09-02 20:51:51.000000000 -0700 @@ -280,8 +280,8 @@ init_mem_alloc (unsigned long ram_start, #if ((PAGE_OFFSET >> PAGE_SHIFT) & ((1UL << (MAX_ORDER - 1)) - 1)) #error MAX_ORDER is too large for given PAGE_OFFSET (use CONFIG_FORCE_MAX_ZONEORDER to change it) #endif - - free_area_init_node (0, NODE_DATA(0), 0, zones_size, + NODE_DATA(0)->node_mem_map = NULL; + free_area_init_node (0, NODE_DATA(0), zones_size, ADDR_TO_PAGE (PAGE_OFFSET), 0); mem_map = NODE_DATA(0)->node_mem_map; } --- linux-2.6.9-rc1/arch/v850/kernel/signal.c 2004-08-24 00:03:49.000000000 -0700 +++ 25/arch/v850/kernel/signal.c 2004-09-02 20:51:51.000000000 -0700 @@ -344,9 +344,7 @@ static void setup_frame(int sig, struct return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, @@ -421,9 +419,7 @@ static void setup_rt_frame(int sig, stru return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); } /* --- linux-2.6.9-rc1/arch/v850/kernel/time.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/arch/v850/kernel/time.c 2004-09-02 20:51:51.000000000 -0700 @@ -40,24 +40,6 @@ unsigned long long sched_clock(void) return (unsigned long long)jiffies * (1000000000 / HZ); } -static inline void do_profile (unsigned long pc) -{ - if (prof_buffer && current->pid) { - extern int _stext; - pc -= (unsigned long) &_stext; - pc >>= prof_shift; - if (pc < prof_len) - ++prof_buffer[pc]; - else - /* - * Don't ignore out-of-bounds PC values silently, - * put them into the last histogram slot, so if - * present, they will show up as a sharp peak. - */ - ++prof_buffer[prof_len-1]; - } -} - /* * timer_interrupt() needs to keep up the real-time clock, * as well as call the "do_timer()" routine every clocktick @@ -74,10 +56,7 @@ static irqreturn_t timer_interrupt (int mach_tick (); do_timer (regs); - - if (! user_mode (regs)) - do_profile (regs->pc); - + profile_tick(CPU_PROFILING, regs); #if 0 /* * If we have an externally synchronized Linux clock, then update --- linux-2.6.9-rc1/arch/v850/kernel/v850_ksyms.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/arch/v850/kernel/v850_ksyms.c 2004-09-03 00:56:41.009926192 -0700 @@ -14,7 +14,6 @@ #include #include #include -#include #include --- linux-2.6.9-rc1/arch/v850/kernel/vmlinux.lds.S 2004-08-24 00:02:59.000000000 -0700 +++ 25/arch/v850/kernel/vmlinux.lds.S 2004-09-02 20:51:51.000000000 -0700 @@ -111,9 +111,6 @@ *(.init.setup) /* 2.5 convention */ \ *(.setup.init) /* 2.4 convention */ \ ___setup_end = . ; \ - ___start___param = . ; \ - *(__param) \ - ___stop___param = . ; \ ___initcall_start = . ; \ *(.initcall.init) \ *(.initcall1.init) \ --- linux-2.6.9-rc1/arch/x86_64/defconfig 2004-08-24 00:03:17.000000000 -0700 +++ 25/arch/x86_64/defconfig 2004-09-02 20:51:51.000000000 -0700 @@ -17,7 +17,6 @@ CONFIG_GENERIC_ISA_DMA=y # CONFIG_EXPERIMENTAL=y CONFIG_CLEAN_COMPILE=y -CONFIG_STANDALONE=y # # General setup @@ -29,12 +28,13 @@ CONFIG_POSIX_MQUEUE=y CONFIG_SYSCTL=y # CONFIG_AUDIT is not set CONFIG_LOG_BUF_SHIFT=18 -# CONFIG_HOTPLUG is not set +CONFIG_HOTPLUG=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y # CONFIG_EMBEDDED is not set CONFIG_KALLSYMS=y CONFIG_KALLSYMS_ALL=y +CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_FUTEX=y CONFIG_EPOLL=y CONFIG_IOSCHED_NOOP=y @@ -104,8 +104,8 @@ CONFIG_ACPI_FAN=y CONFIG_ACPI_PROCESSOR=y CONFIG_ACPI_THERMAL=y # CONFIG_ACPI_ASUS is not set -CONFIG_ACPI_TOSHIBA=y -CONFIG_ACPI_DEBUG=y +# CONFIG_ACPI_TOSHIBA is not set +# CONFIG_ACPI_DEBUG is not set CONFIG_ACPI_BUS=y CONFIG_ACPI_EC=y CONFIG_ACPI_POWER=y @@ -115,7 +115,23 @@ CONFIG_ACPI_SYSTEM=y # # CPU Frequency scaling # -# CONFIG_CPU_FREQ is not set +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_PROC_INTF=y +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +# CONFIG_CPU_FREQ_24_API is not set +CONFIG_CPU_FREQ_TABLE=y + +# +# CPUFreq processor drivers +# +CONFIG_X86_POWERNOW_K8=y +# CONFIG_X86_SPEEDSTEP_CENTRINO is not set +CONFIG_X86_ACPI_CPUFREQ=y +CONFIG_X86_ACPI_CPUFREQ_PROC_INTF=y # # Bus options (PCI etc.) @@ -123,10 +139,27 @@ CONFIG_ACPI_SYSTEM=y CONFIG_PCI=y CONFIG_PCI_DIRECT=y CONFIG_PCI_MMCONFIG=y +CONFIG_UNORDERED_IO=y +CONFIG_PCI_MSI=y # CONFIG_PCI_LEGACY_PROC is not set # CONFIG_PCI_NAMES is not set # +# PCMCIA/CardBus support +# +# CONFIG_PCMCIA is not set + +# +# PCI Hotplug Support +# +CONFIG_HOTPLUG_PCI=y +# CONFIG_HOTPLUG_PCI_FAKE is not set +# CONFIG_HOTPLUG_PCI_ACPI is not set +# CONFIG_HOTPLUG_PCI_CPCI is not set +# CONFIG_HOTPLUG_PCI_PCIE is not set +# CONFIG_HOTPLUG_PCI_SHPC is not set + +# # Executable file formats / Emulations # CONFIG_BINFMT_ELF=y @@ -144,6 +177,9 @@ CONFIG_UID16=y # # Generic Driver Options # +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set # CONFIG_DEBUG_DRIVER is not set # @@ -171,7 +207,7 @@ CONFIG_BLK_DEV_FD=y CONFIG_BLK_DEV_LOOP=y # CONFIG_BLK_DEV_CRYPTOLOOP is not set # CONFIG_BLK_DEV_NBD is not set -# CONFIG_BLK_DEV_CARMEL is not set +# CONFIG_BLK_DEV_SX8 is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y @@ -186,10 +222,10 @@ CONFIG_BLK_DEV_IDE=y # # Please see Documentation/ide.txt for help/info on IDE drives # +# CONFIG_BLK_DEV_IDE_SATA is not set # CONFIG_BLK_DEV_HD_IDE is not set CONFIG_BLK_DEV_IDEDISK=y CONFIG_IDEDISK_MULTI_MODE=y -# CONFIG_IDEDISK_STROKE is not set CONFIG_BLK_DEV_IDECD=y # CONFIG_BLK_DEV_IDETAPE is not set # CONFIG_BLK_DEV_IDEFLOPPY is not set @@ -234,7 +270,7 @@ CONFIG_BLK_DEV_PIIX=y # CONFIG_BLK_DEV_SIS5513 is not set # CONFIG_BLK_DEV_SLC90E66 is not set # CONFIG_BLK_DEV_TRM290 is not set -# CONFIG_BLK_DEV_VIA82CXXX is not set +CONFIG_BLK_DEV_VIA82CXXX=y # CONFIG_IDE_ARM is not set CONFIG_BLK_DEV_IDEDMA=y # CONFIG_IDEDMA_IVB is not set @@ -273,16 +309,24 @@ CONFIG_BLK_DEV_SD=y # SCSI low-level drivers # CONFIG_BLK_DEV_3W_XXXX_RAID=y +# CONFIG_SCSI_3W_9XXX is not set # CONFIG_SCSI_ACARD is not set # CONFIG_SCSI_AACRAID is not set # CONFIG_SCSI_AIC7XXX is not set # CONFIG_SCSI_AIC7XXX_OLD is not set -# CONFIG_SCSI_AIC79XX is not set -# CONFIG_SCSI_ADVANSYS is not set +CONFIG_SCSI_AIC79XX=y +CONFIG_AIC79XX_CMDS_PER_DEVICE=32 +CONFIG_AIC79XX_RESET_DELAY_MS=2000 +# CONFIG_AIC79XX_BUILD_FIRMWARE is not set +# CONFIG_AIC79XX_ENABLE_RD_STRM is not set +# CONFIG_AIC79XX_DEBUG_ENABLE is not set +CONFIG_AIC79XX_DEBUG_MASK=0 +CONFIG_AIC79XX_REG_PRETTY_PRINT=y # CONFIG_SCSI_MEGARAID is not set CONFIG_SCSI_SATA=y # CONFIG_SCSI_SATA_SVW is not set CONFIG_SCSI_ATA_PIIX=y +# CONFIG_SCSI_SATA_NV is not set # CONFIG_SCSI_SATA_PROMISE is not set # CONFIG_SCSI_SATA_SX4 is not set # CONFIG_SCSI_SATA_SIL is not set @@ -290,7 +334,6 @@ CONFIG_SCSI_ATA_PIIX=y CONFIG_SCSI_SATA_VIA=y # CONFIG_SCSI_SATA_VITESSE is not set # CONFIG_SCSI_BUSLOGIC is not set -# CONFIG_SCSI_CPQFCTS is not set # CONFIG_SCSI_DMX3191D is not set # CONFIG_SCSI_EATA is not set # CONFIG_SCSI_EATA_PIO is not set @@ -392,6 +435,7 @@ CONFIG_IPV6=y # QoS and/or fair queueing # # CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set # # Network testing @@ -431,8 +475,7 @@ CONFIG_MII=y # CONFIG_HP100 is not set CONFIG_NET_PCI=y # CONFIG_PCNET32 is not set -CONFIG_AMD8111_ETH=y -# CONFIG_AMD8111E_NAPI is not set +# CONFIG_AMD8111_ETH is not set # CONFIG_ADAPTEC_STARFIRE is not set # CONFIG_B44 is not set CONFIG_FORCEDETH=y @@ -442,16 +485,13 @@ CONFIG_FORCEDETH=y # CONFIG_FEALNX is not set # CONFIG_NATSEMI is not set # CONFIG_NE2K_PCI is not set -CONFIG_8139CP=m -CONFIG_8139TOO=m -# CONFIG_8139TOO_PIO is not set -# CONFIG_8139TOO_TUNE_TWISTER is not set -# CONFIG_8139TOO_8129 is not set -# CONFIG_8139_OLD_RX_RESET is not set +# CONFIG_8139CP is not set +# CONFIG_8139TOO is not set # CONFIG_SIS900 is not set # CONFIG_EPIC100 is not set # CONFIG_SUNDANCE is not set # CONFIG_VIA_RHINE is not set +# CONFIG_VIA_VELOCITY is not set # # Ethernet (1000 Mbit) @@ -602,6 +642,9 @@ CONFIG_AGP_AMD64=y # CONFIG_DRM is not set # CONFIG_MWAVE is not set CONFIG_RAW_DRIVER=y +CONFIG_HPET=y +# CONFIG_HPET_RTC_IRQ is not set +CONFIG_HPET_MMAP=y CONFIG_MAX_RAW_DEVS=256 CONFIG_HANGCHECK_TIMER=y @@ -611,6 +654,11 @@ CONFIG_HANGCHECK_TIMER=y # CONFIG_I2C is not set # +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# # Misc devices # # CONFIG_IBM_ASM is not set @@ -703,17 +751,8 @@ CONFIG_USB_OHCI_HCD=y # CONFIG_USB_BLUETOOTH_TTY is not set # CONFIG_USB_MIDI is not set # CONFIG_USB_ACM is not set -CONFIG_USB_PRINTER=y -CONFIG_USB_STORAGE=y -# CONFIG_USB_STORAGE_DEBUG is not set -# CONFIG_USB_STORAGE_DATAFAB is not set -# CONFIG_USB_STORAGE_FREECOM is not set -# CONFIG_USB_STORAGE_ISD200 is not set -# CONFIG_USB_STORAGE_DPCM is not set -# CONFIG_USB_STORAGE_HP8200e is not set -# CONFIG_USB_STORAGE_SDDR09 is not set -# CONFIG_USB_STORAGE_SDDR55 is not set -# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_STORAGE is not set # # USB Human Interface Devices (HID) @@ -830,7 +869,8 @@ CONFIG_ISO9660_FS=y # # DOS/FAT/NT Filesystems # -# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set # CONFIG_NTFS_FS is not set # @@ -856,6 +896,7 @@ CONFIG_RAMFS=y # CONFIG_BEFS_FS is not set # CONFIG_BFS_FS is not set # CONFIG_EFS_FS is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set # CONFIG_CRAMFS is not set # CONFIG_VXFS_FS is not set # CONFIG_HPFS_FS is not set @@ -909,10 +950,11 @@ CONFIG_DEBUG_KERNEL=y # CONFIG_DEBUG_SLAB is not set CONFIG_MAGIC_SYSRQ=y # CONFIG_DEBUG_SPINLOCK is not set -# CONFIG_INIT_DEBUG is not set +CONFIG_INIT_DEBUG=y # CONFIG_DEBUG_INFO is not set # CONFIG_FRAME_POINTER is not set -# CONFIG_IOMMU_DEBUG is not set +CONFIG_IOMMU_DEBUG=y +# CONFIG_IOMMU_LEAK is not set # # Security options @@ -927,5 +969,6 @@ CONFIG_MAGIC_SYSRQ=y # # Library routines # +# CONFIG_CRC_CCITT is not set CONFIG_CRC32=y # CONFIG_LIBCRC32C is not set --- linux-2.6.9-rc1/arch/x86_64/ia32/ia32_binfmt.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/arch/x86_64/ia32/ia32_binfmt.c 2004-09-02 20:51:51.000000000 -0700 @@ -301,6 +301,9 @@ MODULE_AUTHOR("Eric Youngdale, Andi Klee #define elf_addr_t __u32 +#undef TASK_SIZE +#define TASK_SIZE 0xffffffff + static void elf32_init(struct pt_regs *); #include "../../../fs/binfmt_elf.c" @@ -365,7 +368,7 @@ int setup_arg_pages(struct linux_binprm mpnt->vm_page_prot = (mpnt->vm_flags & VM_EXEC) ? PAGE_COPY_EXEC : PAGE_COPY; insert_vm_struct(mm, mpnt); - mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT; + mm->stack_vm = mm->total_vm = vma_pages(mpnt); } for (i = 0 ; i < MAX_ARG_PAGES ; i++) { --- linux-2.6.9-rc1/arch/x86_64/ia32/ia32entry.S 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/x86_64/ia32/ia32entry.S 2004-09-03 00:56:45.204288552 -0700 @@ -270,35 +270,32 @@ quiet_ni_syscall: ret CFI_ENDPROC - .macro PTREGSCALL label, func + .macro PTREGSCALL label, func, arg .globl \label \label: leaq \func(%rip),%rax + leaq -ARGOFFSET+8(%rsp),\arg /* 8 for return address */ jmp ia32_ptregs_common .endm - PTREGSCALL stub32_rt_sigreturn, sys32_rt_sigreturn - PTREGSCALL stub32_sigreturn, sys32_sigreturn - PTREGSCALL stub32_sigaltstack, sys32_sigaltstack - PTREGSCALL stub32_sigsuspend, sys32_sigsuspend - PTREGSCALL stub32_execve, sys32_execve - PTREGSCALL stub32_fork, sys_fork - PTREGSCALL stub32_clone, sys32_clone - PTREGSCALL stub32_vfork, sys_vfork - PTREGSCALL stub32_iopl, sys_iopl - PTREGSCALL stub32_rt_sigsuspend, sys_rt_sigsuspend + PTREGSCALL stub32_rt_sigreturn, sys32_rt_sigreturn, %rdi + PTREGSCALL stub32_sigreturn, sys32_sigreturn, %rdi + PTREGSCALL stub32_sigaltstack, sys32_sigaltstack, %rdx + PTREGSCALL stub32_sigsuspend, sys32_sigsuspend, %rcx + PTREGSCALL stub32_execve, sys32_execve, %rcx + PTREGSCALL stub32_fork, sys_fork, %rdi + PTREGSCALL stub32_clone, sys32_clone, %rdx + PTREGSCALL stub32_vfork, sys_vfork, %rdi + PTREGSCALL stub32_iopl, sys_iopl, %rsi + PTREGSCALL stub32_rt_sigsuspend, sys_rt_sigsuspend, %rdx ENTRY(ia32_ptregs_common) CFI_STARTPROC popq %r11 SAVE_REST - movq %r11, %r15 call *%rax - movq %r15, %r11 RESTORE_REST - leaq ia32_sysret(%rip),%r11 - pushq %r11 - ret + jmp ia32_sysret /* misbalances the return cache */ CFI_ENDPROC .data @@ -332,7 +329,7 @@ ia32_sys_call_table: .quad sys_getuid16 .quad sys_stime /* stime */ /* 25 */ .quad sys32_ptrace /* ptrace */ - .quad sys_alarm /* XXX sign extension??? */ + .quad sys_alarm .quad sys_fstat /* (old)fstat */ .quad sys_pause .quad compat_sys_utime /* 30 */ @@ -558,7 +555,7 @@ ia32_sys_call_table: .quad sys_fadvise64 /* 250 */ .quad quiet_ni_syscall /* free_huge_pages */ .quad sys_exit_group - .quad sys_lookup_dcookie + .quad sys32_lookup_dcookie .quad sys_epoll_create .quad sys_epoll_ctl /* 255 */ .quad sys_epoll_wait @@ -589,6 +586,13 @@ ia32_sys_call_table: .quad compat_sys_mq_notify .quad compat_sys_mq_getsetattr .quad quiet_ni_syscall /* reserved for kexec */ + .quad sys32_waitid + .quad sys_perfctr_info + .quad sys_vperfctr_open + .quad sys_vperfctr_control + .quad sys_vperfctr_unlink + .quad sys_vperfctr_iresume + .quad sys_vperfctr_read /* don't forget to change IA32_NR_syscalls */ ia32_syscall_end: .rept IA32_NR_syscalls-(ia32_syscall_end-ia32_sys_call_table)/8 --- linux-2.6.9-rc1/arch/x86_64/ia32/ia32_ioctl.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/x86_64/ia32/ia32_ioctl.c 2004-09-02 20:51:51.000000000 -0700 @@ -171,23 +171,8 @@ struct ioctl_trans ioctl_start[] = { COMPATIBLE_IOCTL(HDIO_SET_KEEPSETTINGS) COMPATIBLE_IOCTL(HDIO_SCAN_HWIF) COMPATIBLE_IOCTL(BLKRASET) -COMPATIBLE_IOCTL(BLKFRASET) COMPATIBLE_IOCTL(0x4B50) /* KDGHWCLK - not in the kernel, but don't complain */ COMPATIBLE_IOCTL(0x4B51) /* KDSHWCLK - not in the kernel, but don't complain */ -COMPATIBLE_IOCTL(RTC_AIE_ON) -COMPATIBLE_IOCTL(RTC_AIE_OFF) -COMPATIBLE_IOCTL(RTC_UIE_ON) -COMPATIBLE_IOCTL(RTC_UIE_OFF) -COMPATIBLE_IOCTL(RTC_PIE_ON) -COMPATIBLE_IOCTL(RTC_PIE_OFF) -COMPATIBLE_IOCTL(RTC_WIE_ON) -COMPATIBLE_IOCTL(RTC_WIE_OFF) -COMPATIBLE_IOCTL(RTC_ALM_SET) -COMPATIBLE_IOCTL(RTC_ALM_READ) -COMPATIBLE_IOCTL(RTC_RD_TIME) -COMPATIBLE_IOCTL(RTC_SET_TIME) -COMPATIBLE_IOCTL(RTC_WKALM_SET) -COMPATIBLE_IOCTL(RTC_WKALM_RD) COMPATIBLE_IOCTL(FIOQSIZE) /* And these ioctls need translation */ --- linux-2.6.9-rc1/arch/x86_64/ia32/ia32_signal.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/x86_64/ia32/ia32_signal.c 2004-09-03 00:57:12.706107640 -0700 @@ -74,6 +74,8 @@ int ia32_copy_siginfo_to_user(siginfo_t3 err |= __put_user(from->si_utime, &to->si_utime); err |= __put_user(from->si_stime, &to->si_stime); err |= __put_user(from->si_status, &to->si_status); + err |= put_compat_rusage(&from->si_rusage, + &to->si_rusage); default: case __SI_KILL >> 16: err |= __put_user(from->si_uid, &to->si_uid); @@ -115,7 +117,8 @@ int ia32_copy_siginfo_from_user(siginfo_ } asmlinkage long -sys32_sigsuspend(int history0, int history1, old_sigset_t mask, struct pt_regs regs) +sys32_sigsuspend(int history0, int history1, old_sigset_t mask, + struct pt_regs *regs) { sigset_t saveset; @@ -126,11 +129,11 @@ sys32_sigsuspend(int history0, int histo recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - regs.rax = -EINTR; + regs->rax = -EINTR; while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); - if (do_signal(®s, &saveset)) + if (do_signal(regs, &saveset)) return -EINTR; } } @@ -138,7 +141,7 @@ sys32_sigsuspend(int history0, int histo asmlinkage long sys32_sigaltstack(const stack_ia32_t __user *uss_ptr, stack_ia32_t __user *uoss_ptr, - struct pt_regs regs) + struct pt_regs *regs) { stack_t uss,uoss; int ret; @@ -155,7 +158,7 @@ sys32_sigaltstack(const stack_ia32_t __u } seg = get_fs(); set_fs(KERNEL_DS); - ret = do_sigaltstack(uss_ptr ? &uss : NULL, &uoss, regs.rsp); + ret = do_sigaltstack(uss_ptr ? &uss : NULL, &uoss, regs->rsp); set_fs(seg); if (ret >= 0 && uoss_ptr) { if (!access_ok(VERIFY_WRITE,uoss_ptr,sizeof(stack_ia32_t)) || @@ -274,9 +277,9 @@ badframe: return 1; } -asmlinkage long sys32_sigreturn(struct pt_regs regs) +asmlinkage long sys32_sigreturn(struct pt_regs *regs) { - struct sigframe __user *frame = (struct sigframe __user *)(regs.rsp-8); + struct sigframe __user *frame = (struct sigframe __user *)(regs->rsp-8); sigset_t set; unsigned int eax; @@ -294,20 +297,23 @@ asmlinkage long sys32_sigreturn(struct p recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - if (ia32_restore_sigcontext(®s, &frame->sc, &eax)) + if (ia32_restore_sigcontext(regs, &frame->sc, &eax)) goto badframe; return eax; badframe: - signal_fault(®s, frame, "32bit sigreturn"); + signal_fault(regs, frame, "32bit sigreturn"); return 0; } -asmlinkage long sys32_rt_sigreturn(struct pt_regs regs) +asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs) { - struct rt_sigframe __user *frame = (struct rt_sigframe __user *)(regs.rsp - 4); + struct rt_sigframe __user *frame; sigset_t set; unsigned int eax; + struct pt_regs tregs; + + frame = (struct rt_sigframe __user *)(regs->rsp - 4); if (verify_area(VERIFY_READ, frame, sizeof(*frame))) goto badframe; @@ -320,16 +326,17 @@ asmlinkage long sys32_rt_sigreturn(struc recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - if (ia32_restore_sigcontext(®s, &frame->uc.uc_mcontext, &eax)) + if (ia32_restore_sigcontext(regs, &frame->uc.uc_mcontext, &eax)) goto badframe; - if (sys32_sigaltstack(&frame->uc.uc_stack, NULL, regs) == -EFAULT) + tregs = *regs; + if (sys32_sigaltstack(&frame->uc.uc_stack, NULL, &tregs) == -EFAULT) goto badframe; return eax; badframe: - signal_fault(®s,frame,"32bit rt sigreturn"); + signal_fault(regs,frame,"32bit rt sigreturn"); return 0; } @@ -484,7 +491,13 @@ void ia32_setup_frame(int sig, struct k_ regs->ss = __USER32_DS; set_fs(USER_DS); - regs->eflags &= ~TF_MASK; + if (regs->eflags & TF_MASK) { + if (current->ptrace & PT_PTRACED) { + ptrace_notify(SIGTRAP); + } else { + regs->eflags &= ~TF_MASK; + } + } #if DEBUG_SIG printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", @@ -494,9 +507,7 @@ void ia32_setup_frame(int sig, struct k_ return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - signal_fault(regs,frame,"32bit signal deliver"); + force_sigsegv(sig, current); } void ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, @@ -580,7 +591,13 @@ void ia32_setup_rt_frame(int sig, struct regs->ss = __USER32_DS; set_fs(USER_DS); - regs->eflags &= ~TF_MASK; + if (regs->eflags & TF_MASK) { + if (current->ptrace & PT_PTRACED) { + ptrace_notify(SIGTRAP); + } else { + regs->eflags &= ~TF_MASK; + } + } #if DEBUG_SIG printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", @@ -590,8 +607,6 @@ void ia32_setup_rt_frame(int sig, struct return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - signal_fault(regs, frame, "32bit rt signal setup"); + force_sigsegv(sig, current); } --- linux-2.6.9-rc1/arch/x86_64/ia32/ptrace32.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/x86_64/ia32/ptrace32.c 2004-09-02 20:51:51.000000000 -0700 @@ -249,8 +249,8 @@ asmlinkage long sys32_ptrace(long reques case PTRACE_GETFPREGS: case PTRACE_SETFPXREGS: case PTRACE_GETFPXREGS: + case PTRACE_GETEVENTMSG: break; - } child = find_target(request, pid, &ret); @@ -363,6 +363,10 @@ asmlinkage long sys32_ptrace(long reques break; } + case PTRACE_GETEVENTMSG: + ret = put_user(child->ptrace_message,(unsigned int __user *)(u64)data); + break; + default: ret = -EINVAL; break; --- linux-2.6.9-rc1/arch/x86_64/ia32/sys_ia32.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/x86_64/ia32/sys_ia32.c 2004-09-02 20:51:51.000000000 -0700 @@ -1125,7 +1125,7 @@ long sys32_ustat(unsigned dev, struct us } asmlinkage long sys32_execve(char __user *name, compat_uptr_t __user *argv, - compat_uptr_t __user *envp, struct pt_regs regs) + compat_uptr_t __user *envp, struct pt_regs *regs) { long error; char * filename; @@ -1134,21 +1134,40 @@ asmlinkage long sys32_execve(char __user error = PTR_ERR(filename); if (IS_ERR(filename)) return error; - error = compat_do_execve(filename, argv, envp, ®s); + error = compat_do_execve(filename, argv, envp, regs); if (error == 0) current->ptrace &= ~PT_DTRACE; putname(filename); return error; } -asmlinkage long sys32_clone(unsigned int clone_flags, unsigned int newsp, struct pt_regs regs) +asmlinkage long sys32_clone(unsigned int clone_flags, unsigned int newsp, + struct pt_regs *regs) { - void __user *parent_tid = (void __user *)regs.rdx; - void __user *child_tid = (void __user *)regs.rdi; + void __user *parent_tid = (void __user *)regs->rdx; + void __user *child_tid = (void __user *)regs->rdi; if (!newsp) - newsp = regs.rsp; - return do_fork(clone_flags & ~CLONE_IDLETASK, newsp, ®s, 0, - parent_tid, child_tid); + newsp = regs->rsp; + return do_fork(clone_flags, newsp, regs, 0, parent_tid, child_tid); +} + +asmlinkage long sys32_waitid(int which, compat_pid_t pid, + siginfo_t32 __user *uinfo, int options) +{ + siginfo_t info; + long ret; + mm_segment_t old_fs = get_fs(); + + info.si_signo = 0; + set_fs (KERNEL_DS); + ret = sys_waitid(which, pid, (siginfo_t __user *) &info, options); + set_fs (old_fs); + + if (ret < 0 || info.si_signo == 0) + return ret; + BUG_ON(info.si_code & __SI_MASK); + info.si_code |= __SI_CHLD; + return ia32_copy_siginfo_to_user(uinfo, &info); } /* @@ -1337,6 +1356,12 @@ long sys32_quotactl(void) return -ENOSYS; } +long sys32_lookup_dcookie(u32 addr_low, u32 addr_high, + char __user * buf, size_t len) +{ + return sys_lookup_dcookie(((u64)addr_high << 32) | addr_low, buf, len); +} + cond_syscall(sys32_ipc) static int __init ia32_init (void) --- linux-2.6.9-rc1/arch/x86_64/Kconfig 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/x86_64/Kconfig 2004-09-03 00:56:58.894207368 -0700 @@ -165,7 +165,7 @@ config X86_HT bool depends on SMP && !MK8 default y - + config MATH_EMULATION bool @@ -318,6 +318,8 @@ config X86_MCE bool default y +source "drivers/perfctr/Kconfig" + endmenu @@ -347,6 +349,17 @@ config PCI_MMCONFIG depends on PCI select ACPI_BOOT +config UNORDERED_IO + bool "Unordered IO mapping access" + depends on EXPERIMENTAL + select UNORDERED_IO + help + Use unordered stores to access IO memory mappings in device drivers. + Still very experimental. When a driver works on IA64/ppc64/pa-risc it should + work with this option, but it makes the drivers behave differently + from i386. Requires that the driver writer used memory barriers + properly. + source "drivers/pci/Kconfig" source "drivers/pcmcia/Kconfig" @@ -388,6 +401,23 @@ config UID16 depends on IA32_EMULATION default y +config KEXEC + bool "kexec system call (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + kexec is a system call that implements the ability to shutdown your + current kernel, and to start another kernel. It is like a reboot + but it is indepedent of the system firmware. And like a reboot + you can start any kernel with it, not just Linux. + + The name comes from the similiarity to the exec system call. + + It is an ongoing process to be certain the hardware in a machine + is properly shutdown, so do not be surprised if this code does not + initially work for you. It may help to enable device hotplugging + support. As of this writing the exact hardware interface is + strongly in flux, so no good recommendation can be made. + endmenu source drivers/Kconfig --- linux-2.6.9-rc1/arch/x86_64/Kconfig.debug 2004-08-24 00:02:22.000000000 -0700 +++ 25/arch/x86_64/Kconfig.debug 2004-09-03 00:56:43.207592096 -0700 @@ -18,6 +18,18 @@ config INIT_DEBUG Fill __init and __initdata at the end of boot. This helps debugging illegal uses of __init and __initdata after initialization. +config SCHEDSTATS + bool "Collect scheduler statistics" + depends on DEBUG_KERNEL && PROC_FS + help + If you say Y here, additional code will be inserted into the + scheduler and related routines to collect statistics about + scheduler behavior and provide them in /proc/schedstat. These + stats may be useful for both tuning and debugging the scheduler + If you aren't debugging the scheduler or trying to tune a specific + application, you can say N to avoid the very slight overhead + this adds. + config FRAME_POINTER bool "Compile the kernel with frame pointers" help @@ -50,7 +62,13 @@ config IOMMU_LEAK Add a simple leak tracer to the IOMMU code. This is useful when you are debugging a buggy device driver that leaks IOMMU mappings. -#config X86_REMOTE_DEBUG -# bool "kgdb debugging stub" +config LOCKMETER + bool "Kernel lock metering" + depends on SMP + help + Say Y to enable kernel lock metering, which adds overhead to SMP locks, + but allows you to see various statistics using the lockstat command. + +source "arch/x86_64/Kconfig.kgdb" endmenu --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/x86_64/Kconfig.kgdb 2004-09-03 00:56:36.962541488 -0700 @@ -0,0 +1,176 @@ +config KGDB + bool "Include kgdb kernel debugger" + depends on DEBUG_KERNEL + select DEBUG_INFO + help + If you say Y here, the system will be compiled with the debug + option (-g) and a debugging stub will be included in the + kernel. This stub communicates with gdb on another (host) + computer via a serial port. The host computer should have + access to the kernel binary file (vmlinux) and a serial port + that is connected to the target machine. Gdb can be made to + configure the serial port or you can use stty and setserial to + do this. See the 'target' command in gdb. This option also + configures in the ability to request a breakpoint early in the + boot process. To request the breakpoint just include 'kgdb' + as a boot option when booting the target machine. The system + will then break as soon as it looks at the boot options. This + option also installs a breakpoint in panic and sends any + kernel faults to the debugger. For more information see the + Documentation/i386/kgdb.txt file. + +choice + depends on KGDB + prompt "Debug serial port BAUD" + default KGDB_115200BAUD + help + Gdb and the kernel stub need to agree on the baud rate to be + used. Some systems (x86 family at this writing) allow this to + be configured. + +config KGDB_9600BAUD + bool "9600" + +config KGDB_19200BAUD + bool "19200" + +config KGDB_38400BAUD + bool "38400" + +config KGDB_57600BAUD + bool "57600" + +config KGDB_115200BAUD + bool "115200" +endchoice + +config KGDB_PORT + hex "hex I/O port address of the debug serial port" + depends on KGDB + default 3f8 + help + Some systems (x86 family at this writing) allow the port + address to be configured. The number entered is assumed to be + hex, don't put 0x in front of it. The standard address are: + COM1 3f8 , irq 4 and COM2 2f8 irq 3. Setserial /dev/ttySx + will tell you what you have. It is good to test the serial + connection with a live system before trying to debug. + +config KGDB_IRQ + int "IRQ of the debug serial port" + depends on KGDB + default 4 + help + This is the irq for the debug port. If everything is working + correctly and the kernel has interrupts on a control C to the + port should cause a break into the kernel debug stub. + +config DEBUG_INFO + bool + depends on KGDB + default y + +config KGDB_MORE + bool "Add any additional compile options" + depends on KGDB + default n + help + Saying yes here turns on the ability to enter additional + compile options. + + +config KGDB_OPTIONS + depends on KGDB_MORE + string "Additional compile arguments" + default "-O1" + help + This option allows you enter additional compile options for + the whole kernel compile. Each platform will have a default + that seems right for it. For example on PPC "-ggdb -O1", and + for i386 "-O1". Note that by configuring KGDB "-g" is already + turned on. In addition, on i386 platforms + "-fomit-frame-pointer" is deleted from the standard compile + options. + +config NO_KGDB_CPUS + int "Number of CPUs" + depends on KGDB && SMP + default NR_CPUS + help + + This option sets the number of cpus for kgdb ONLY. It is used + to prune some internal structures so they look "nice" when + displayed with gdb. This is to overcome possibly larger + numbers that may have been entered above. Enter the real + number to get nice clean kgdb_info displays. + +config KGDB_TS + bool "Enable kgdb time stamp macros?" + depends on KGDB + default n + help + Kgdb event macros allow you to instrument your code with calls + to the kgdb event recording function. The event log may be + examined with gdb at a break point. Turning on this + capability also allows you to choose how many events to + keep. Kgdb always keeps the lastest events. + +choice + depends on KGDB_TS + prompt "Max number of time stamps to save?" + default KGDB_TS_128 + +config KGDB_TS_64 + bool "64" + +config KGDB_TS_128 + bool "128" + +config KGDB_TS_256 + bool "256" + +config KGDB_TS_512 + bool "512" + +config KGDB_TS_1024 + bool "1024" + +endchoice + +config STACK_OVERFLOW_TEST + bool "Turn on kernel stack overflow testing?" + depends on KGDB + default n + help + This option enables code in the front line interrupt handlers + to check for kernel stack overflow on interrupts and system + calls. This is part of the kgdb code on x86 systems. + +config KGDB_CONSOLE + bool "Enable serial console thru kgdb port" + depends on KGDB + default n + help + This option enables the command line "console=kgdb" option. + When the system is booted with this option in the command line + all kernel printk output is sent to gdb (as well as to other + consoles). For this to work gdb must be connected. For this + reason, this command line option will generate a breakpoint if + gdb has not yet connected. After the gdb continue command is + given all pent up console output will be printed by gdb on the + host machine. Neither this option, nor KGDB require the + serial driver to be configured. + +config KGDB_SYSRQ + bool "Turn on SysRq 'G' command to do a break?" + depends on KGDB + default y + help + This option includes an option in the SysRq code that allows + you to enter SysRq G which generates a breakpoint to the KGDB + stub. This will work if the keyboard is alive and can + interrupt the system. Because of constraints on when the + serial port interrupt can be enabled, this code may allow you + to interrupt the system before the serial port control C is + available. Just say yes here. + --- linux-2.6.9-rc1/arch/x86_64/kernel/aperture.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/arch/x86_64/kernel/aperture.c 2004-09-02 20:51:51.000000000 -0700 @@ -31,6 +31,8 @@ int iommu_aperture_allowed __initdata = int fallback_aper_order __initdata = 1; /* 64MB */ int fallback_aper_force __initdata = 0; +int fix_aperture __initdata = 1; + /* This code runs before the PCI subsystem is initialized, so just access the northbridge directly. */ @@ -202,7 +204,7 @@ void __init iommu_hole_init(void) u64 aper_base; int valid_agp = 0; - if (iommu_aperture_disabled) + if (iommu_aperture_disabled || !fix_aperture) return; printk("Checking aperture...\n"); @@ -241,20 +243,15 @@ void __init iommu_hole_init(void) /* Got the aperture from the AGP bridge */ } else if ((!no_iommu && end_pfn >= 0xffffffff>>PAGE_SHIFT) || force_iommu || - valid_agp || + valid_agp || fallback_aper_force) { - /* When there is a AGP bridge in the system assume the - user wants to use the AGP driver too and needs an - aperture. However this case (AGP but no good - aperture) should only happen with a more broken than - usual BIOS, because it would even break Windows. */ - - printk("Your BIOS doesn't leave a aperture memory hole\n"); - printk("Please enable the IOMMU option in the BIOS setup\n"); - printk("This costs you %d MB of RAM\n", 32 << fallback_aper_order); - + printk("Your BIOS doesn't leave a aperture memory hole\n"); + printk("Please enable the IOMMU option in the BIOS setup\n"); + printk("This costs you %d MB of RAM\n", + 32 << fallback_aper_order); + aper_order = fallback_aper_order; - aper_alloc = allocate_aperture(); + aper_alloc = allocate_aperture(); if (!aper_alloc) { /* Could disable AGP and IOMMU here, but it's probably not worth it. But the later users cannot deal with --- linux-2.6.9-rc1/arch/x86_64/kernel/apic.c 2004-08-24 00:01:55.000000000 -0700 +++ 25/arch/x86_64/kernel/apic.c 2004-09-03 00:56:58.102327752 -0700 @@ -142,6 +142,36 @@ void disconnect_bsp_APIC(void) outb(0x70, 0x22); outb(0x00, 0x23); } + else { + /* Go back to Virtual Wire compatibility mode */ + unsigned long value; + + /* For the spurious interrupt use vector F, and enable it */ + value = apic_read(APIC_SPIV); + value &= ~APIC_VECTOR_MASK; + value |= APIC_SPIV_APIC_ENABLED; + value |= 0xf; + apic_write_around(APIC_SPIV, value); + + /* For LVT0 make it edge triggered, active high, external and enabled */ + value = apic_read(APIC_LVT0); + value &= ~(APIC_MODE_MASK | APIC_SEND_PENDING | + APIC_INPUT_POLARITY | APIC_LVT_REMOTE_IRR | + APIC_LVT_LEVEL_TRIGGER | APIC_LVT_MASKED ); + value |= APIC_LVT_REMOTE_IRR | APIC_SEND_PENDING; + value = SET_APIC_DELIVERY_MODE(value, APIC_MODE_EXINT); + apic_write_around(APIC_LVT0, value); + + /* For LVT1 make it edge triggered, active high, nmi and enabled */ + value = apic_read(APIC_LVT1); + value &= ~( + APIC_MODE_MASK | APIC_SEND_PENDING | + APIC_INPUT_POLARITY | APIC_LVT_REMOTE_IRR | + APIC_LVT_LEVEL_TRIGGER | APIC_LVT_MASKED); + value |= APIC_LVT_REMOTE_IRR | APIC_SEND_PENDING; + value = SET_APIC_DELIVERY_MODE(value, APIC_MODE_NMI); + apic_write_around(APIC_LVT1, value); + } } void disable_local_APIC(void) @@ -836,8 +866,7 @@ void smp_local_timer_interrupt(struct pt { int cpu = smp_processor_id(); - x86_do_profile(regs); - + profile_tick(CPU_PROFILING, regs); if (--per_cpu(prof_counter, cpu) <= 0) { /* * The multiplier may have changed since the last time we got --- linux-2.6.9-rc1/arch/x86_64/kernel/asm-offsets.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/arch/x86_64/kernel/asm-offsets.c 2004-09-03 00:56:41.009926192 -0700 @@ -7,8 +7,8 @@ #include #include #include +#include #include -#include #include #include #include --- linux-2.6.9-rc1/arch/x86_64/kernel/cpufreq/Kconfig 2004-08-24 00:03:38.000000000 -0700 +++ 25/arch/x86_64/kernel/cpufreq/Kconfig 2004-09-02 20:51:51.000000000 -0700 @@ -46,4 +46,53 @@ config X86_POWERNOW_K8_ACPI depends on ((X86_POWERNOW_K8 = "m" && ACPI_PROCESSOR) || (X86_POWERNOW_K8 = "y" && ACPI_PROCESSOR = "y")) default y +config X86_SPEEDSTEP_CENTRINO + tristate "Intel Enhanced SpeedStep" + depends on CPU_FREQ_TABLE + help + This adds the CPUFreq driver for Enhanced SpeedStep enabled + mobile CPUs. This means Intel Pentium M (Centrino) CPUs + or 64bit enabled Intel Xeons. + + For details, take a look at . + + If in doubt, say N. + +config X86_SPEEDSTEP_CENTRINO_TABLE + bool + depends on X86_SPEEDSTEP_CENTRINO + default y + +config X86_SPEEDSTEP_CENTRINO_ACPI + bool "Use ACPI tables to decode valid frequency/voltage pairs (EXPERIMENTAL)" + depends on EXPERIMENTAL + depends on ((X86_SPEEDSTEP_CENTRINO = "m" && ACPI_PROCESSOR) || (X86_SPEEDSTEP_CENTRINO = "y" && ACPI_PROCESSOR = "y")) + help + Use primarily the information provided in the BIOS ACPI tables + to determine valid CPU frequency and voltage pairings. + + If in doubt, say Y. + +config X86_ACPI_CPUFREQ + tristate "ACPI Processor P-States driver" + depends on CPU_FREQ_TABLE && ACPI_PROCESSOR + help + This driver adds a CPUFreq driver which utilizes the ACPI + Processor Performance States. + + For details, take a look at . + + If in doubt, say N. + +config X86_ACPI_CPUFREQ_PROC_INTF + bool "/proc/acpi/processor/../performance interface (deprecated)" + depends on X86_ACPI_CPUFREQ && PROC_FS + help + This enables the deprecated /proc/acpi/processor/../performance + interface. While it is helpful for debugging, the generic, + cross-architecture cpufreq interfaces should be used. + + If in doubt, say N. + endmenu + --- linux-2.6.9-rc1/arch/x86_64/kernel/cpufreq/Makefile 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/x86_64/kernel/cpufreq/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -2,6 +2,12 @@ # Reuse the i386 cpufreq drivers # +SRCDIR := ../../../i386/kernel/cpu/cpufreq + obj-$(CONFIG_X86_POWERNOW_K8) += powernow-k8.o +obj-$(CONFIG_X86_SPEEDSTEP_CENTRINO) += speedstep-centrino.o +obj-$(CONFIG_X86_ACPI_CPUFREQ) += acpi.o -powernow-k8-objs := ../../../i386/kernel/cpu/cpufreq/powernow-k8.o +powernow-k8-objs := ${SRCDIR}/powernow-k8.o +speedstep-centrino-objs := ${SRCDIR}/speedstep-centrino.o +acpi-objs := ${SRCDIR}/acpi.o --- linux-2.6.9-rc1/arch/x86_64/kernel/domain.c 2004-08-24 00:02:25.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,93 +0,0 @@ -#include -#include - -/* Don't do any NUMA setup on Opteron right now. They seem to be - better off with flat scheduling. This is just for SMT. */ - -#ifdef CONFIG_SCHED_SMT - -static struct sched_group sched_group_cpus[NR_CPUS]; -static struct sched_group sched_group_phys[NR_CPUS]; -static DEFINE_PER_CPU(struct sched_domain, cpu_domains); -static DEFINE_PER_CPU(struct sched_domain, phys_domains); -__init void arch_init_sched_domains(void) -{ - int i; - struct sched_group *first = NULL, *last = NULL; - - /* Set up domains */ - for_each_cpu(i) { - struct sched_domain *cpu_domain = &per_cpu(cpu_domains, i); - struct sched_domain *phys_domain = &per_cpu(phys_domains, i); - - *cpu_domain = SD_SIBLING_INIT; - /* Disable SMT NICE for CMP */ - /* RED-PEN use a generic flag */ - if (cpu_data[i].x86_vendor == X86_VENDOR_AMD) - cpu_domain->flags &= ~SD_SHARE_CPUPOWER; - cpu_domain->span = cpu_sibling_map[i]; - cpu_domain->parent = phys_domain; - cpu_domain->groups = &sched_group_cpus[i]; - - *phys_domain = SD_CPU_INIT; - phys_domain->span = cpu_possible_map; - phys_domain->groups = &sched_group_phys[first_cpu(cpu_domain->span)]; - } - - /* Set up CPU (sibling) groups */ - for_each_cpu(i) { - struct sched_domain *cpu_domain = &per_cpu(cpu_domains, i); - int j; - first = last = NULL; - - if (i != first_cpu(cpu_domain->span)) - continue; - - for_each_cpu_mask(j, cpu_domain->span) { - struct sched_group *cpu = &sched_group_cpus[j]; - - cpus_clear(cpu->cpumask); - cpu_set(j, cpu->cpumask); - cpu->cpu_power = SCHED_LOAD_SCALE; - - if (!first) - first = cpu; - if (last) - last->next = cpu; - last = cpu; - } - last->next = first; - } - - first = last = NULL; - /* Set up physical groups */ - for_each_cpu(i) { - struct sched_domain *cpu_domain = &per_cpu(cpu_domains, i); - struct sched_group *cpu = &sched_group_phys[i]; - - if (i != first_cpu(cpu_domain->span)) - continue; - - cpu->cpumask = cpu_domain->span; - /* - * Make each extra sibling increase power by 10% of - * the basic CPU. This is very arbitrary. - */ - cpu->cpu_power = SCHED_LOAD_SCALE + SCHED_LOAD_SCALE*(cpus_weight(cpu->cpumask)-1) / 10; - - if (!first) - first = cpu; - if (last) - last->next = cpu; - last = cpu; - } - last->next = first; - - mb(); - for_each_cpu(i) { - struct sched_domain *cpu_domain = &per_cpu(cpu_domains, i); - cpu_attach_domain(cpu_domain, i); - } -} - -#endif --- linux-2.6.9-rc1/arch/x86_64/kernel/e820.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/arch/x86_64/kernel/e820.c 2004-09-03 00:56:58.480270296 -0700 @@ -185,8 +185,6 @@ void __init e820_reserve_resources(void) int i; for (i = 0; i < e820.nr_map; i++) { struct resource *res; - if (e820.map[i].addr + e820.map[i].size > 0x100000000ULL) - continue; res = alloc_bootmem_low(sizeof(struct resource)); switch (e820.map[i].type) { case E820_RAM: res->name = "System RAM"; break; --- linux-2.6.9-rc1/arch/x86_64/kernel/early_printk.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/arch/x86_64/kernel/early_printk.c 2004-09-02 20:51:51.000000000 -0700 @@ -8,7 +8,7 @@ /* Simple VGA output */ #ifdef __i386__ -#define VGABASE __pa(__PAGE_OFFSET + 0xb8000UL) +#define VGABASE __pa((void *)(__PAGE_OFFSET + 0xb8000UL)) #else #define VGABASE 0xffffffff800b8000UL #endif @@ -99,18 +99,17 @@ static void early_serial_write(struct co #define DEFAULT_BAUD 9600 -static __init void early_serial_init(char *opt) +static __init void early_serial_init(char *s) { unsigned char c; unsigned divisor; unsigned baud = DEFAULT_BAUD; - char *s, *e; + char *e; - if (*opt == ',') - ++opt; + if (*s == ',') + ++s; - s = strsep(&opt, ","); - if (s != NULL) { + if (*s) { unsigned port; if (!strncmp(s,"0x",2)) { early_serial_base = simple_strtoul(s, &e, 16); @@ -124,6 +123,9 @@ static __init void early_serial_init(cha port = 0; early_serial_base = bases[port]; } + s += strcspn(s, ","); + if (*s == ',') + s++; } outb(0x3, early_serial_base + LCR); /* 8n1 */ @@ -131,8 +133,7 @@ static __init void early_serial_init(cha outb(0, early_serial_base + FCR); /* no fifo */ outb(0x3, early_serial_base + MCR); /* DTR + RTS */ - s = strsep(&opt, ","); - if (s != NULL) { + if (*s) { baud = simple_strtoul(s, &e, 0); if (baud == 0 || s == e) baud = DEFAULT_BAUD; --- linux-2.6.9-rc1/arch/x86_64/kernel/entry.S 2004-08-24 00:02:26.000000000 -0700 +++ 25/arch/x86_64/kernel/entry.S 2004-09-03 00:56:45.205288400 -0700 @@ -324,19 +324,20 @@ int_restore_rest: * Certain special system calls that need to save a complete full stack frame. */ - .macro PTREGSCALL label,func + .macro PTREGSCALL label,func,arg .globl \label \label: leaq \func(%rip),%rax + leaq -ARGOFFSET+8(%rsp),\arg /* 8 for return address */ jmp ptregscall_common .endm - PTREGSCALL stub_clone, sys_clone - PTREGSCALL stub_fork, sys_fork - PTREGSCALL stub_vfork, sys_vfork - PTREGSCALL stub_rt_sigsuspend, sys_rt_sigsuspend - PTREGSCALL stub_sigaltstack, sys_sigaltstack - PTREGSCALL stub_iopl, sys_iopl + PTREGSCALL stub_clone, sys_clone, %r8 + PTREGSCALL stub_fork, sys_fork, %rdi + PTREGSCALL stub_vfork, sys_vfork, %rdi + PTREGSCALL stub_rt_sigsuspend, sys_rt_sigsuspend, %rdx + PTREGSCALL stub_sigaltstack, sys_sigaltstack, %rdx + PTREGSCALL stub_iopl, sys_iopl, %rsi ENTRY(ptregscall_common) CFI_STARTPROC @@ -386,6 +387,7 @@ ENTRY(stub_rt_sigreturn) CFI_STARTPROC addq $8, %rsp SAVE_REST + movq %rsp,%rdi FIXUP_TOP_OF_STACK %r11 call sys_rt_sigreturn movq %rax,RAX(%rsp) # fixme, this could be done at the higher layer @@ -558,6 +560,11 @@ ENTRY(spurious_interrupt) apicinterrupt SPURIOUS_APIC_VECTOR,smp_spurious_interrupt #endif +#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_PERFCTR) +ENTRY(perfctr_interrupt) + apicinterrupt LOCAL_PERFCTR_VECTOR,smp_perfctr_interrupt +#endif + /* * Exception entry points. */ --- linux-2.6.9-rc1/arch/x86_64/kernel/i8259.c 2004-08-24 00:02:19.000000000 -0700 +++ 25/arch/x86_64/kernel/i8259.c 2004-09-03 00:56:57.843367120 -0700 @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -415,10 +416,24 @@ static int i8259A_suspend(struct sys_dev return 0; } + + +static int i8259A_shutdown(struct sys_device *dev) +{ + /* Put the i8259A into a quiescent state that + * the kernel initialization code can get it + * out of. + */ + outb(0xff, 0x21); /* mask all of 8259A-1 */ + outb(0xff, 0xA1); /* mask all of 8259A-1 */ + return 0; +} + static struct sysdev_class i8259_sysdev_class = { set_kset_name("i8259"), .suspend = i8259A_suspend, .resume = i8259A_resume, + .shutdown = i8259A_shutdown, }; static struct sys_device device_i8259A = { @@ -560,6 +575,8 @@ void __init init_IRQ(void) set_intr_gate(ERROR_APIC_VECTOR, error_interrupt); #endif + perfctr_vector_init(); + /* * Set the clock to HZ Hz, we already have a valid * vector now: --- linux-2.6.9-rc1/arch/x86_64/kernel/io_apic.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/x86_64/kernel/io_apic.c 2004-09-03 00:56:58.358288840 -0700 @@ -80,7 +80,7 @@ int vector_irq[NR_VECTORS] = { [0 ... NR * shared ISA-space IRQs, so we have to support them. We are super * fast in the common case, and fast for shared ISA-space IRQs. */ -static void __init add_pin_to_irq(unsigned int irq, int apic, int pin) +static void add_pin_to_irq(unsigned int irq, int apic, int pin) { static int first_free_entry = NR_IRQS; struct irq_pin_list *entry = irq_2_pin + irq; @@ -329,7 +329,7 @@ static int __init find_irq_entry(int api /* * Find the pin to which IRQ[irq] (ISA) is connected */ -static int __init find_isa_irq_pin(int irq, int type) +static int find_isa_irq_pin(int irq, int type) { int i; @@ -656,11 +656,7 @@ static inline int IO_APIC_irq_trigger(in /* irq_vectors is indexed by the sum of all RTEs in all I/O APICs. */ u8 irq_vector[NR_IRQ_VECTORS] = { FIRST_DEVICE_VECTOR , 0 }; -#ifdef CONFIG_PCI_MSI int assign_irq_vector(int irq) -#else -int __init assign_irq_vector(int irq) -#endif { static int current_vector = FIRST_DEVICE_VECTOR, offset = 0; @@ -1123,11 +1119,43 @@ static void __init enable_IO_APIC(void) */ void disable_IO_APIC(void) { + int pin; /* * Clear the IO-APIC before rebooting: */ clear_IO_APIC(); + /* + * If the i82559 is routed through an IOAPIC + * Put that IOAPIC in virtual wire mode + * so legacy interrups can be delivered. + */ + pin = find_isa_irq_pin(0, mp_ExtINT); + if (pin != -1) { + struct IO_APIC_route_entry entry; + unsigned long flags; + + memset(&entry, 0, sizeof(entry)); + entry.mask = 0; /* Enabled */ + entry.trigger = 0; /* Edge */ + entry.irr = 0; + entry.polarity = 0; /* High */ + entry.delivery_status = 0; + entry.dest_mode = 0; /* Physical */ + entry.delivery_mode = 7; /* ExtInt */ + entry.vector = 0; + entry.dest.physical.physical_dest = 0; + + + /* + * Add it to the IO-APIC irq-routing table: + */ + spin_lock_irqsave(&ioapic_lock, flags); + io_apic_write(0, 0x11+2*pin, *(((int *)&entry)+1)); + io_apic_write(0, 0x10+2*pin, *(((int *)&entry)+0)); + spin_unlock_irqrestore(&ioapic_lock, flags); + } + disconnect_bsp_APIC(); } @@ -1926,15 +1954,7 @@ int io_apic_set_pci_routing (int ioapic, mp_ioapics[ioapic].mpc_apicid, pin, entry.vector, irq, edge_level, active_high_low); - if (use_pci_vector() && !platform_legacy_irq(irq)) - irq = IO_APIC_VECTOR(irq); - if (edge_level) { - irq_desc[irq].handler = &ioapic_level_type; - } else { - irq_desc[irq].handler = &ioapic_edge_type; - } - - set_intr_gate(entry.vector, interrupt[irq]); + ioapic_register_intr(irq, entry.vector, edge_level); if (!ioapic && (irq < 16)) disable_8259A_irq(irq); --- linux-2.6.9-rc1/arch/x86_64/kernel/ioport.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/x86_64/kernel/ioport.c 2004-09-02 20:51:51.000000000 -0700 @@ -83,9 +83,9 @@ asmlinkage long sys_ioperm(unsigned long * code. */ -asmlinkage long sys_iopl(unsigned int level, struct pt_regs regs) +asmlinkage long sys_iopl(unsigned int level, struct pt_regs *regs) { - unsigned int old = (regs.eflags >> 12) & 3; + unsigned int old = (regs->eflags >> 12) & 3; if (level > 3) return -EINVAL; @@ -94,6 +94,6 @@ asmlinkage long sys_iopl(unsigned int le if (!capable(CAP_SYS_RAWIO)) return -EPERM; } - regs.eflags = (regs.eflags &~ 0x3000UL) | (level << 12); + regs->eflags = (regs->eflags &~ 0x3000UL) | (level << 12); return 0; } --- linux-2.6.9-rc1/arch/x86_64/kernel/irq.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/x86_64/kernel/irq.c 2004-09-03 00:56:36.963541336 -0700 @@ -213,13 +213,15 @@ inline void synchronize_irq(unsigned int int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * action) { int status = 1; /* Force the "do bottom halves" bit */ + int ret; if (!(action->flags & SA_INTERRUPT)) local_irq_enable(); do { - status |= action->flags; - action->handler(irq, action->dev_id, regs); + ret = action->handler(irq, action->dev_id, regs); + if (ret == IRQ_HANDLED) + status |= action->flags; action = action->next; } while (action); if (status & SA_SAMPLE_RANDOM) @@ -405,6 +407,9 @@ out: spin_unlock(&desc->lock); irq_exit(); + + kgdb_process_breakpoint(); + return 1; } @@ -862,31 +867,6 @@ static int irq_affinity_write_proc (stru #endif -static int prof_cpu_mask_read_proc (char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - int len = cpumask_scnprintf(page, count, *(cpumask_t *)data); - if (count - len < 2) - return -EINVAL; - len += sprintf(page + len, "\n"); - return len; -} - -static int prof_cpu_mask_write_proc (struct file *file, - const char __user *buffer, - unsigned long count, void *data) -{ - unsigned long full_count = count, err; - cpumask_t new_value, *mask = (cpumask_t *)data; - - err = cpumask_parse(buffer, count, new_value); - if (err) - return err; - - *mask = new_value; - return full_count; -} - #define MAX_NAMELEN 10 static void register_irq_proc (unsigned int irq) @@ -922,26 +902,15 @@ static void register_irq_proc (unsigned #endif } -unsigned long prof_cpu_mask = -1; - void init_irq_proc (void) { - struct proc_dir_entry *entry; int i; /* create /proc/irq */ root_irq_dir = proc_mkdir("irq", NULL); /* create /proc/irq/prof_cpu_mask */ - entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); - - if (!entry) - return; - - entry->nlink = 1; - entry->data = (void *)&prof_cpu_mask; - entry->read_proc = prof_cpu_mask_read_proc; - entry->write_proc = prof_cpu_mask_write_proc; + create_prof_cpu_mask(root_irq_dir); /* * Create entries for all existing IRQs. @@ -949,4 +918,3 @@ void init_irq_proc (void) for (i = 0; i < NR_IRQS; i++) register_irq_proc(i); } - --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/x86_64/kernel/kgdb_stub.c 2004-09-03 00:56:36.974539664 -0700 @@ -0,0 +1,2591 @@ +/* + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +/* + * Copyright (c) 2000 VERITAS Software Corporation. + * + */ +/**************************************************************************** + * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ + * + * Module name: remcom.c $ + * Revision: 1.34 $ + * Date: 91/03/09 12:29:49 $ + * Contributor: Lake Stevens Instrument Division$ + * + * Description: low level support for gdb debugger. $ + * + * Considerations: only works on target hardware $ + * + * Written by: Glenn Engel $ + * Updated by: David Grothe + * Updated by: Robert Walsh + * Updated by: wangdi + * ModuleState: Experimental $ + * + * NOTES: See Below $ + * + * Modified for 386 by Jim Kingdon, Cygnus Support. + * Compatibility with 2.1.xx kernel by David Grothe + * + * Changes to allow auto initilization. All that is needed is that it + * be linked with the kernel and a break point (int 3) be executed. + * The header file defines BREAKPOINT to allow one to do + * this. It should also be possible, once the interrupt system is up, to + * call putDebugChar("+"). Once this is done, the remote debugger should + * get our attention by sending a ^C in a packet. George Anzinger + * + * Integrated into 2.2.5 kernel by Tigran Aivazian + * Added thread support, support for multiple processors, + * support for ia-32(x86) hardware debugging. + * Amit S. Kale ( akale@veritas.com ) + * + * Modified to support debugging over ethernet by Robert Walsh + * and wangdi , based on + * code by San Mehat. + * + * X86_64 changes from Andi Kleen's patch merged by Jim Houston + * (jim.houston@ccur.com). If it works thank Andi if its broken + * blame me. + * + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing an int 3. + * + ************* + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $#. + * + * where + * :: + * :: < two hex digits computed as modulo 256 sum of > + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + ****************************************************************************/ +#define KGDB_VERSION "<20030915.1651.33>" +#include +#include +#include /* for strcpy */ +#include +#include +#include +#include /* for linux pt_regs struct */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define Dearly_printk(x...) +int kgdb_enabled = 0; + +/************************************************************************ + * + * external low-level support routines + */ +typedef void (*Function) (void); /* pointer to a function */ + +/* Thread reference */ +typedef unsigned char threadref[8]; + +extern int tty_putDebugChar(int); /* write a single character */ +extern int tty_getDebugChar(void); /* read and return a single char */ +extern void tty_flushDebugChar(void); /* flush pending characters */ +extern int eth_putDebugChar(int); /* write a single character */ +extern int eth_getDebugChar(void); /* read and return a single char */ +extern void eth_flushDebugChar(void); /* flush pending characters */ + +/************************************************************************/ +/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/ +/* at least NUMREGBYTES*2 are needed for register packets */ +/* Longer buffer is needed to list all threads */ +#define BUFMAX 400 + +char *kgdb_version = KGDB_VERSION; + +/* debug > 0 prints ill-formed commands in valid packets & checksum errors */ +int debug_regs = 0; /* set to non-zero to print registers */ + +/* filled in by an external module */ +char *gdb_module_offsets; + +static const char hexchars[] = "0123456789abcdef"; + +/* Number of bytes of registers. */ +#define NUMREGBYTES (NUMREGS * sizeof(unsigned long)) +/* + * Note that this register image is in a different order than + * the register image that Linux produces at interrupt time. + * + * Linux's register image is defined by struct pt_regs in ptrace.h. + * Just why GDB uses a different order is a historical mystery. + * + * Could add XMM and segment registers here. + */ +enum regnames {_RAX, + _RBX, + _RCX, + _RDX, + _RSI, + _RDI, + _RBP, + _RSP, + _R8, + _R9, + _R10, + _R11, + _R12, + _R13, + _R14, + _R15, + _PC, + _PS, + NUMREGS }; + + +/*************************** ASSEMBLY CODE MACROS *************************/ +/* + * Put the error code here just in case the user cares. + * Likewise, the vector number here (since GDB only gets the signal + * number through the usual means, and that's not very specific). + * The called_from is the return address so he can tell how we entered kgdb. + * This will allow him to seperate out the various possible entries. + */ +#define REMOTE_DEBUG 0 /* set != to turn on printing (also available in info) */ + +#define PID_MAX PID_MAX_DEFAULT + +#ifdef CONFIG_SMP +void smp_send_nmi_allbutself(void); +#define IF_SMP(x) x +#undef MAX_NO_CPUS +#ifndef CONFIG_NO_KGDB_CPUS +#define CONFIG_NO_KGDB_CPUS 2 +#endif +#if CONFIG_NO_KGDB_CPUS > NR_CPUS +#define MAX_NO_CPUS NR_CPUS +#else +#define MAX_NO_CPUS CONFIG_NO_KGDB_CPUS +#endif +#define hold_init hold_on_sstep: 1, +#define MAX_CPU_MASK (unsigned long)((1LL << MAX_NO_CPUS) - 1LL) +#define NUM_CPUS num_online_cpus() +#else +#define IF_SMP(x) +#define hold_init +#undef MAX_NO_CPUS +#define MAX_NO_CPUS 1 +#define NUM_CPUS 1 +#endif +#define NOCPU (struct task_struct *)0xbad1fbad +/* *INDENT-OFF* */ +struct kgdb_info { + int used_malloc; + void *called_from; + long long entry_tsc; + int errcode; + int vector; + int print_debug_info; +#ifdef CONFIG_SMP + int hold_on_sstep; + struct { + volatile struct task_struct *task; + int pid; + int hold; + struct pt_regs *regs; + } cpus_waiting[MAX_NO_CPUS]; +#endif +} kgdb_info = {hold_init print_debug_info:REMOTE_DEBUG, vector:-1}; + +/* *INDENT-ON* */ + +#define used_m kgdb_info.used_malloc +/* + * This is little area we set aside to contain the stack we + * need to build to allow gdb to call functions. We use one + * per cpu to avoid locking issues. We will do all this work + * with interrupts off so that should take care of the protection + * issues. + */ +#define LOOKASIDE_SIZE 200 /* should be more than enough */ +#define MALLOC_MAX 200 /* Max malloc size */ +struct { + unsigned long rsp; + unsigned long array[LOOKASIDE_SIZE]; +} fn_call_lookaside[MAX_NO_CPUS]; + +static int trap_cpu; +static unsigned long OLD_esp; + +#define END_OF_LOOKASIDE &fn_call_lookaside[trap_cpu].array[LOOKASIDE_SIZE] +#define IF_BIT 0x200 +#define TF_BIT 0x100 + +#define MALLOC_ROUND 8-1 + +static char malloc_array[MALLOC_MAX]; +IF_SMP(static void to_gdb(const char *mess)); +void * +malloc(int size) +{ + + if (size <= (MALLOC_MAX - used_m)) { + int old_used = used_m; + used_m += ((size + MALLOC_ROUND) & (~MALLOC_ROUND)); + return &malloc_array[old_used]; + } else { + return NULL; + } +} + +/* + * I/O dispatch functions... + * Based upon kgdboe, either call the ethernet + * handler or the serial one.. + */ +void +putDebugChar(int c) +{ + if (!kgdboe) { + tty_putDebugChar(c); + } else { + eth_putDebugChar(c); + } +} + +int +getDebugChar(void) +{ + if (!kgdboe) { + return tty_getDebugChar(); + } else { + return eth_getDebugChar(); + } +} + +void +flushDebugChar(void) +{ + if (!kgdboe) { + tty_flushDebugChar(); + } else { + eth_flushDebugChar(); + } +} + +/* + * Gdb calls functions by pushing agruments, including a return address + * on the stack and the adjusting EIP to point to the function. The + * whole assumption in GDB is that we are on a different stack than the + * one the "user" i.e. code that hit the break point, is on. This, of + * course is not true in the kernel. Thus various dodges are needed to + * do the call without directly messing with EIP (which we can not change + * as it is just a location and not a register. To adjust it would then + * require that we move every thing below EIP up or down as needed. This + * will not work as we may well have stack relative pointer on the stack + * (such as the pointer to regs, for example). + + * So here is what we do: + * We detect gdb attempting to store into the stack area and instead, store + * into the fn_call_lookaside.array at the same relative location as if it + * were the area ESP pointed at. We also trap ESP modifications + * and uses these to adjust fn_call_lookaside.esp. On entry + * fn_call_lookaside.esp will be set to point at the last entry in + * fn_call_lookaside.array. This allows us to check if it has changed, and + * if so, on exit, we add the registers we will use to do the move and a + * trap/ interrupt return exit sequence. We then adjust the eflags in the + * regs array (remember we now have a copy in the fn_call_lookaside.array) to + * kill the interrupt bit, AND we change EIP to point at our set up stub. + * As part of the register set up we preset the registers to point at the + * begining and end of the fn_call_lookaside.array, so all the stub needs to + * do is move words from the array to the stack until ESP= the desired value + * then do the rti. This will then transfer to the desired function with + * all the correct registers. Nifty huh? + */ +extern asmlinkage void fn_call_stub(void); +extern asmlinkage void fn_rtn_stub(void); +/* *INDENT-OFF* */ +__asm__("fn_rtn_stub:\n\t" + "movq %rax,%rsp\n\t" + "fn_call_stub:\n\t" + "1:\n\t" + "addq $-8,%rbx\n\t" + "movq (%rbx), %rax\n\t" + "pushq %rax\n\t" + "cmpq %rsp,%rcx\n\t" + "jne 1b\n\t" + "popq %rax\n\t" + "popq %rbx\n\t" + "popq %rcx\n\t" + "iret \n\t"); +/* *INDENT-ON* */ +#define gdb_i386vector kgdb_info.vector +#define gdb_i386errcode kgdb_info.errcode +#define waiting_cpus kgdb_info.cpus_waiting +#define remote_debug kgdb_info.print_debug_info +#define hold_cpu(cpu) kgdb_info.cpus_waiting[cpu].hold +/* gdb locks */ + +#ifdef CONFIG_SMP +static int in_kgdb_called; +static spinlock_t waitlocks[MAX_NO_CPUS] = + {[0 ... MAX_NO_CPUS - 1] = SPIN_LOCK_UNLOCKED }; +/* + * The following array has the thread pointer of each of the "other" + * cpus. We make it global so it can be seen by gdb. + */ +volatile int in_kgdb_entry_log[MAX_NO_CPUS]; +volatile struct pt_regs *in_kgdb_here_log[MAX_NO_CPUS]; +/* +static spinlock_t continuelocks[MAX_NO_CPUS]; +*/ +spinlock_t kgdb_spinlock = SPIN_LOCK_UNLOCKED; +/* waiters on our spinlock plus us */ +static atomic_t spinlock_waiters = ATOMIC_INIT(1); +static int spinlock_count = 0; +static int spinlock_cpu = 0; +/* + * Note we use nested spin locks to account for the case where a break + * point is encountered when calling a function by user direction from + * kgdb. Also there is the memory exception recursion to account for. + * Well, yes, but this lets other cpus thru too. Lets add a + * cpu id to the lock. + */ +#define KGDB_SPIN_LOCK(x) if( spinlock_count == 0 || \ + spinlock_cpu != smp_processor_id()){\ + atomic_inc(&spinlock_waiters); \ + while (! spin_trylock(x)) {\ + in_kgdb(®s);\ + }\ + atomic_dec(&spinlock_waiters); \ + spinlock_count = 1; \ + spinlock_cpu = smp_processor_id(); \ + }else{ \ + spinlock_count++; \ + } +#define KGDB_SPIN_UNLOCK(x) if( --spinlock_count == 0) spin_unlock(x) +#else +unsigned kgdb_spinlock = 0; +#define KGDB_SPIN_LOCK(x) --*x +#define KGDB_SPIN_UNLOCK(x) ++*x +#endif + +int +hex(char ch) +{ + if ((ch >= 'a') && (ch <= 'f')) + return (ch - 'a' + 10); + if ((ch >= '0') && (ch <= '9')) + return (ch - '0'); + if ((ch >= 'A') && (ch <= 'F')) + return (ch - 'A' + 10); + return (-1); +} + +/* scan for the sequence $# */ +void +getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + char ch; + + do { + /* wait around for the start character, ignore all other characters */ + while ((ch = (getDebugChar() & 0x7f)) != '$') ; + checksum = 0; + xmitcsum = -1; + + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX) { + ch = getDebugChar() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum += hex(getDebugChar() & 0x7f); + if ((remote_debug) && (checksum != xmitcsum)) { + printk + ("bad checksum. My count = 0x%x, sent=0x%x. buf=%s\n", + checksum, xmitcsum, buffer); + } + + if (checksum != xmitcsum) + putDebugChar('-'); /* failed checksum */ + else { + putDebugChar('+'); /* successful transfer */ + /* if a sequence char is present, reply the sequence ID */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + /* remove sequence chars from buffer */ + count = strlen(buffer); + for (i = 3; i <= count; i++) + buffer[i - 3] = buffer[i]; + } + } + } + } while (checksum != xmitcsum); + + if (remote_debug) + printk("R:%s\n", buffer); + flushDebugChar(); +} + +/* send the packet in buffer. */ + +void +putpacket(char *buffer) +{ + unsigned char checksum; + int count; + char ch; + + /* $#. */ + + if (!kgdboe) { + do { + if (remote_debug) + printk("T:%s\n", buffer); + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count])) { + putDebugChar(ch); + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum % 16]); + flushDebugChar(); + + } while ((getDebugChar() & 0x7f) != '+'); + } else { + /* + * For udp, we can not transfer too much bytes once. + * We only transfer MAX_SEND_COUNT size bytes each time + */ + +#define MAX_SEND_COUNT 30 + + int send_count = 0, i = 0; + char send_buf[MAX_SEND_COUNT]; + + do { + if (remote_debug) + printk("T:%s\n", buffer); + putDebugChar('$'); + checksum = 0; + count = 0; + send_count = 0; + while ((ch = buffer[count])) { + if (send_count >= MAX_SEND_COUNT) { + for(i = 0; i < MAX_SEND_COUNT; i++) { + putDebugChar(send_buf[i]); + } + flushDebugChar(); + send_count = 0; + } else { + send_buf[send_count] = ch; + checksum += ch; + count ++; + send_count++; + } + } + for(i = 0; i < send_count; i++) + putDebugChar(send_buf[i]); + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum % 16]); + flushDebugChar(); + } while ((getDebugChar() & 0x7f) != '+'); + } +} + +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; +static char lbuf[BUFMAX]; +static short error; + +void +debug_error(char *format, char *parm) +{ + if (remote_debug) + printk(format, parm); +} + +static void +print_regs(struct pt_regs *regs) +{ + printk("RAX=%016lx RBX=%016lx RCX=%016lx\n", + regs->rax, regs->rbx, regs->rcx); + printk("RDX=%016lx RSI=%016lx RDI=%016lx\n", + regs->rdx, regs->rsi, regs->rdi); + printk("RBP=%016lx PS=%016lx PC=%016lx\n", + regs->rbp, regs->eflags, regs->rip); + printk("R8=%016lx R9=%016lx R10=%016lx\n", + regs->r8, regs->r9, regs->r10); + printk("R11=%016lx R12=%016lx R13=%016lx\n", + regs->r11, regs->r12, regs->r13); + printk("R14=%016lx R15=%016lx RSP=%016lx\n", + regs->r14, regs->r15, regs->rsp); +} + +#define NEW_esp fn_call_lookaside[trap_cpu].rsp + +static void +regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + gdb_regs[_RAX] = regs->rax; + gdb_regs[_RBX] = regs->rbx; + gdb_regs[_RCX] = regs->rcx; + gdb_regs[_RDX] = regs->rdx; + gdb_regs[_RSI] = regs->rsi; + gdb_regs[_RDI] = regs->rdi; + gdb_regs[_RBP] = regs->rbp; + gdb_regs[ _PS] = regs->eflags; + gdb_regs[ _PC] = regs->rip; + gdb_regs[ _R8] = regs->r8; + gdb_regs[ _R9] = regs->r9; + gdb_regs[_R10] = regs->r10; + gdb_regs[_R11] = regs->r11; + gdb_regs[_R12] = regs->r12; + gdb_regs[_R13] = regs->r13; + gdb_regs[_R14] = regs->r14; + gdb_regs[_R15] = regs->r15; + gdb_regs[_RSP] = regs->rsp; + + /* Note, as we are a debugging the kernel, we will always + * trap in kernel code, this means no priviledge change, + * and so the pt_regs structure is not completely valid. In a non + * privilege change trap, only EFLAGS, CS and EIP are put on the stack, + * SS and ESP are not stacked, this means that the last 2 elements of + * pt_regs is not valid (they would normally refer to the user stack) + * also, using regs+1 is no good because you end up will a value that is + * 2 longs (8) too high. This used to cause stepping over functions + * to fail, so my fix is to use the address of regs->esp, which + * should point at the end of the stack frame. Note I have ignored + * completely exceptions that cause an error code to be stacked, such + * as double fault. Stuart Hughes, Zentropix. + * original code: gdb_regs[_ESP] = (int) (regs + 1) ; + + * this is now done on entry and moved to OLD_esp (as well as NEW_esp). + */ +} + +static void +gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs) +{ + regs->rax = gdb_regs[_RAX] ; + regs->rbx = gdb_regs[_RBX] ; + regs->rcx = gdb_regs[_RCX] ; + regs->rdx = gdb_regs[_RDX] ; + regs->rsi = gdb_regs[_RSI] ; + regs->rdi = gdb_regs[_RDI] ; + regs->rbp = gdb_regs[_RBP] ; + regs->eflags = gdb_regs[ _PS] ; + regs->rip = gdb_regs[ _PC] ; + regs->r8 = gdb_regs[ _R8] ; + regs->r9 = gdb_regs[ _R9] ; + regs->r10 = gdb_regs[ _R10] ; + regs->r11 = gdb_regs[ _R11] ; + regs->r12 = gdb_regs[ _R12] ; + regs->r13 = gdb_regs[ _R13] ; + regs->r14 = gdb_regs[ _R14] ; + regs->r15 = gdb_regs[ _R15] ; + #if 0 /* can't change these */ + regs->rsp = gdb_regs[_RSP] ; + regs->ss = gdb_regs[ _SS] ; + regs->fs = gdb_regs[_FS]; + regs->gs = gdb_regs[_GS]; +#endif +} /* gdb_regs_to_regs */ + +int thread_list = 0; +extern void thread_return(void); + +void +get_gdb_regs(struct task_struct *p, struct pt_regs *regs, unsigned long *gdb_regs) +{ + unsigned long **rbp, *rsp, *rsp0, pc; + int count = 0; + IF_SMP(int i); + if (!p || p == current) { + regs_to_gdb_regs(gdb_regs, regs); + return; + } +#ifdef CONFIG_SMP + for (i = 0; i < MAX_NO_CPUS; i++) { + if (p == kgdb_info.cpus_waiting[i].task) { + regs_to_gdb_regs(gdb_regs, + kgdb_info.cpus_waiting[i].regs); + gdb_regs[_RSP] = + (unsigned long)&kgdb_info.cpus_waiting[i].regs->rsp; + + return; + } + } +#endif + memset(gdb_regs, 0, NUMREGBYTES); + rsp = (unsigned long *)p->thread.rsp; + rbp = (unsigned long **)rsp[0]; + rsp += 2; + gdb_regs[_PC] = (unsigned long)thread_return; + gdb_regs[_RBP] = (unsigned long)rbp; + gdb_regs[_RSP] = (unsigned long)rsp; + +/* + * This code is to give a more informative notion of where a process + * is waiting. It is used only when the user asks for a thread info + * list. If he then switches to the thread, s/he will find the task + * is in schedule, but a back trace should show the same info we come + * up with. This code was shamelessly purloined from process.c. It was + * then enhanced to provide more registers than simply the program + * counter. + */ + + if (!thread_list) { + return; + } + + if (p->state == TASK_RUNNING) + return; + rsp0 = (unsigned long *)p->thread.rsp0; + if (rsp < (unsigned long *) p->thread_info || rsp > rsp0) + return; + /* include/asm-i386/system.h:switch_to() pushes ebp last. */ + do { + if (*rbp < rsp || *rbp > rsp0) + break; + rbp = (unsigned long **)*rbp; + rsp = (unsigned long *)rbp; + pc = rsp[1]; + + if (!in_sched_functions(pc)) + break; + gdb_regs[_PC] = (unsigned long)pc; + gdb_regs[_RSP] = (unsigned long)rsp; + gdb_regs[_RBP] = (unsigned long)rbp; + } while (count++ < 16); + return; +} + +/* convert the memory pointed to by mem into hex, placing result in buf */ +/* returns nonzero if any memory access fails. */ +int mem2hex( char* mem, char* buf, int count) +{ + int i; + unsigned char ch; + int ret = 0; + + for (i=0;i> 4]; + *buf++ = hexchars[ch % 16]; + } + *buf = 0; + if (ret) { + Dearly_printk("mem2hex: fault at accessing %p\n", mem); + } + return(ret); +} + +/* convert the hex array pointed to by buf into binary to be placed in mem */ +/* return nonzero if any memory access fails. */ +int hex2mem( char* buf, char* mem, int count) +{ + int i; + unsigned char ch; + int ret = 0; + + for (i=0;i (OLD_esp - (unsigned int) LOOKASIDE_SIZE))) { + addr = (char *) END_OF_LOOKASIDE - ((char *) OLD_esp - addr); + } + *addr = val; +} + +/* convert the memory pointed to by mem into hex, placing result in buf */ +/* return a pointer to the last char put in buf (null) */ +/* If MAY_FAULT is non-zero, then we should set mem_err in response to + a fault; if zero treat a fault like any other fault in the stub. */ +char * +mem2hex(char *mem, char *buf, int count, int may_fault) +{ + int i; + unsigned char ch; + + if (may_fault) { + mem_err_expected = 1; + mem_err = 0; + } + for (i = 0; i < count; i++) { + /* printk("%lx = ", mem) ; */ + + ch = get_char(mem++); + + /* printk("%02x\n", ch & 0xFF) ; */ + if (may_fault && mem_err) { + if (remote_debug) + printk("Mem fault fetching from addr %lx\n", + (long) (mem - 1)); + *buf = 0; /* truncate buffer */ + return (buf); + } + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch % 16]; + } + *buf = 0; + if (may_fault) + mem_err_expected = 0; + return (buf); +} + +/* convert the hex array pointed to by buf into binary to be placed in mem */ +/* return a pointer to the character AFTER the last byte written */ +/* NOTE: We use the may fault flag to also indicate if the write is to + * the registers (0) or "other" memory (!=0) + */ +char * +hex2mem(char *buf, char *mem, int count, int may_fault) +{ + int i; + unsigned char ch; + + if (may_fault) { + mem_err_expected = 1; + mem_err = 0; + } + for (i = 0; i < count; i++) { + ch = hex(*buf++) << 4; + ch = ch + hex(*buf++); + set_char(mem++, ch, may_fault); + + if (may_fault && mem_err) { + if (remote_debug) + printk("Mem fault storing to addr %lx\n", + (long) (mem - 1)); + return (mem); + } + } + if (may_fault) + mem_err_expected = 0; + return (mem); +} +#endif + +/**********************************************/ +/* WHILE WE FIND NICE HEX CHARS, BUILD AN INT */ +/* RETURN NUMBER OF CHARS PROCESSED */ +/**********************************************/ +int +hexToLong(char **ptr, unsigned long *value) +{ + int numChars = 0; + int hexValue; + + *value = 0; + + while (**ptr) { + hexValue = hex(**ptr); + if (hexValue >= 0) { + *value = (*value << 4) | hexValue; + numChars++; + } else + break; + + (*ptr)++; + } + + return (numChars); +} + +#define stubhex(h) hex(h) +#ifdef old_thread_list + +static int +stub_unpack_int(char *buff, int fieldlength) +{ + int nibble; + int retval = 0; + + while (fieldlength) { + nibble = stubhex(*buff++); + retval |= nibble; + fieldlength--; + if (fieldlength) + retval = retval << 4; + } + return retval; +} +#endif +static char * +pack_hex_byte(char *pkt, int byte) +{ + *pkt++ = hexchars[(byte >> 4) & 0xf]; + *pkt++ = hexchars[(byte & 0xf)]; + return pkt; +} + +#define BUF_THREAD_ID_SIZE 16 + +static char * +pack_threadid(char *pkt, threadref * id) +{ + char *limit; + unsigned char *altid; + + altid = (unsigned char *) id; + limit = pkt + BUF_THREAD_ID_SIZE; + while (pkt < limit) + pkt = pack_hex_byte(pkt, *altid++); + return pkt; +} + +#ifdef old_thread_list +static char * +unpack_byte(char *buf, int *value) +{ + *value = stub_unpack_int(buf, 2); + return buf + 2; +} + +static char * +unpack_threadid(char *inbuf, threadref * id) +{ + char *altref; + char *limit = inbuf + BUF_THREAD_ID_SIZE; + int x, y; + + altref = (char *) id; + + while (inbuf < limit) { + x = stubhex(*inbuf++); + y = stubhex(*inbuf++); + *altref++ = (x << 4) | y; + } + return inbuf; +} +#endif +void +int_to_threadref(threadref * id, int value) +{ + unsigned char *scan; + + scan = (unsigned char *) id; + { + int i = 4; + while (i--) + *scan++ = 0; + } + *scan++ = (value >> 24) & 0xff; + *scan++ = (value >> 16) & 0xff; + *scan++ = (value >> 8) & 0xff; + *scan++ = (value & 0xff); +} +int +int_to_hex_v(unsigned char * id, int value) +{ + unsigned char *start = id; + int shift; + int ch; + + for (shift = 28; shift >= 0; shift -= 4) { + if ((ch = (value >> shift) & 0xf) || (id != start)) { + *id = hexchars[ch]; + id++; + } + } + if (id == start) + *id++ = '0'; + return id - start; +} +#ifdef old_thread_list + +static int +threadref_to_int(threadref * ref) +{ + int i, value = 0; + unsigned char *scan; + + scan = (char *) ref; + scan += 4; + i = 4; + while (i-- > 0) + value = (value << 8) | ((*scan++) & 0xff); + return value; +} +#endif +static int +cmp_str(char *s1, char *s2, int count) +{ + while (count--) { + if (*s1++ != *s2++) + return 0; + } + return 1; +} + +#if 1 /* this is a hold over from 2.4 where O(1) was "sometimes" */ +extern struct task_struct *kgdb_get_idle(int cpu); +#define idle_task(cpu) kgdb_get_idle(cpu) +#else +#define idle_task(cpu) init_tasks[cpu] +#endif + +extern int kgdb_pid_init_done; + +struct task_struct * +getthread(int pid) +{ + struct task_struct *thread; + if (pid >= PID_MAX && pid <= (PID_MAX + MAX_NO_CPUS)) { + if (!cpu_online(pid - PID_MAX)) + return NULL; + + return idle_task(pid - PID_MAX); + } else { + /* + * find_task_by_pid is relatively safe all the time + * Other pid functions require lock downs which imply + * that we may be interrupting them (as we get here + * in the middle of most any lock down). + * Still we don't want to call until the table exists! + */ + if (kgdb_pid_init_done){ + thread = find_task_by_pid(pid); + if (thread) { + return thread; + } + } + } + return NULL; +} +/* *INDENT-OFF* */ +struct hw_breakpoint { + unsigned enabled; + unsigned type; + unsigned len; + unsigned long addr; +} breakinfo[4] = { {enabled:0}, + {enabled:0}, + {enabled:0}, + {enabled:0}}; +/* *INDENT-ON* */ +unsigned long hw_breakpoint_status; +void +correct_hw_break(void) +{ + int breakno; + int correctit; + int breakbit; + unsigned long dr7; + + asm volatile ("movq %%db7, %0\n":"=r" (dr7) + :); + /* *INDENT-OFF* */ + do { + unsigned long addr0, addr1, addr2, addr3; + asm volatile ("movq %%db0, %0\n" + "movq %%db1, %1\n" + "movq %%db2, %2\n" + "movq %%db3, %3\n" + :"=r" (addr0), "=r"(addr1), + "=r"(addr2), "=r"(addr3) + :); + } while (0); + /* *INDENT-ON* */ + correctit = 0; + for (breakno = 0; breakno < 3; breakno++) { + breakbit = 2 << (breakno << 1); + if (!(dr7 & breakbit) && breakinfo[breakno].enabled) { + correctit = 1; + dr7 |= breakbit; + dr7 &= ~(0xf0000 << (breakno << 2)); + dr7 |= (((breakinfo[breakno].len << 2) | + breakinfo[breakno].type) << 16) << + (breakno << 2); + switch (breakno) { + case 0: + asm volatile ("movq %0, %%dr0\n"::"r" + (breakinfo[breakno].addr)); + break; + + case 1: + asm volatile ("movq %0, %%dr1\n"::"r" + (breakinfo[breakno].addr)); + break; + + case 2: + asm volatile ("movq %0, %%dr2\n"::"r" + (breakinfo[breakno].addr)); + break; + + case 3: + asm volatile ("movq %0, %%dr3\n"::"r" + (breakinfo[breakno].addr)); + break; + } + } else if ((dr7 & breakbit) && !breakinfo[breakno].enabled) { + correctit = 1; + dr7 &= ~breakbit; + dr7 &= ~(0xf0000 << (breakno << 2)); + } + } + if (correctit) { + asm volatile ("movq %0, %%db7\n"::"r" (dr7)); + } +} + +int +remove_hw_break(unsigned breakno) +{ + if (!breakinfo[breakno].enabled) { + return -1; + } + breakinfo[breakno].enabled = 0; + return 0; +} + +int +set_hw_break(unsigned breakno, unsigned type, unsigned len, unsigned addr) +{ + if (breakinfo[breakno].enabled) { + return -1; + } + breakinfo[breakno].enabled = 1; + breakinfo[breakno].type = type; + breakinfo[breakno].len = len; + breakinfo[breakno].addr = addr; + return 0; +} + +#ifdef CONFIG_SMP +static int in_kgdb_console = 0; + +int +in_kgdb(struct pt_regs *regs) +{ + unsigned long flags; + int cpu; + if (!kgdb_enabled) + return 0; + cpu = smp_processor_id(); + in_kgdb_called = 1; + if (!spin_is_locked(&kgdb_spinlock)) { + if (in_kgdb_here_log[cpu] || /* we are holding this cpu */ + in_kgdb_console) { /* or we are doing slow i/o */ + return 1; + } + return 0; + } + + /* As I see it the only reason not to let all cpus spin on + * the same spin_lock is to allow selected ones to proceed. + * This would be a good thing, so we leave it this way. + * Maybe someday.... Done ! + + * in_kgdb() is called from an NMI so we don't pretend + * to have any resources, like printk() for example. + */ + + local_irq_save(flags); /* only local here, to avoid hanging */ + /* + * log arival of this cpu + * The NMI keeps on ticking. Protect against recurring more + * than once, and ignor the cpu that has the kgdb lock + */ + in_kgdb_entry_log[cpu]++; + in_kgdb_here_log[cpu] = regs; + if (cpu == spinlock_cpu || waiting_cpus[cpu].task) + goto exit_in_kgdb; + + /* + * For protection of the initilization of the spin locks by kgdb + * it locks the kgdb spinlock before it gets the wait locks set + * up. We wait here for the wait lock to be taken. If the + * kgdb lock goes away first?? Well, it could be a slow exit + * sequence where the wait lock is removed prior to the kgdb lock + * so if kgdb gets unlocked, we just exit. + */ + + while (spin_is_locked(&kgdb_spinlock) && + !spin_is_locked(waitlocks + cpu)) ; + if (!spin_is_locked(&kgdb_spinlock)) + goto exit_in_kgdb; + + waiting_cpus[cpu].task = current; + waiting_cpus[cpu].pid = (current->pid) ? : (PID_MAX + cpu); + waiting_cpus[cpu].regs = regs; + + spin_unlock_wait(waitlocks + cpu); + + /* + * log departure of this cpu + */ + waiting_cpus[cpu].task = 0; + waiting_cpus[cpu].pid = 0; + waiting_cpus[cpu].regs = 0; + correct_hw_break(); + exit_in_kgdb: + in_kgdb_here_log[cpu] = 0; + local_irq_restore(flags); + return 1; + /* + spin_unlock(continuelocks + smp_processor_id()); + */ +} + +void +smp__in_kgdb(struct pt_regs regs) +{ + ack_APIC_irq(); + in_kgdb(®s); +} +#else +int +in_kgdb(struct pt_regs *regs) +{ + return (kgdb_spinlock); +} +#endif + +void +printexceptioninfo(int exceptionNo, int errorcode, char *buffer) +{ + unsigned long dr6; + int i; + switch (exceptionNo) { + case 1: /* debug exception */ + break; + case 3: /* breakpoint */ + sprintf(buffer, "Software breakpoint"); + return; + default: + sprintf(buffer, "Details not available"); + return; + } + asm volatile ("movq %%db6, %0\n":"=r" (dr6) + :); + if (dr6 & 0x4000) { + sprintf(buffer, "Single step"); + return; + } + for (i = 0; i < 4; ++i) { + if (dr6 & (1 << i)) { + sprintf(buffer, "Hardware breakpoint %d", i); + return; + } + } + sprintf(buffer, "Unknown trap"); + return; +} + +/* + * The ThreadExtraInfo query allows us to pass an arbitrary string + * for display with the "info threads" command. + */ + +void +print_extra_info(task_t *p, char *buf) +{ + if (!p) { + sprintf(buf, "Invalid thread"); + return; + } + sprintf(buf, "0x%p %8d %4d %c %s", + (void *)p, p->parent->pid, + task_cpu(p), + (p->state == 0) ? (task_curr(p)?'R':'r') : + (p->state < 0) ? 'U' : + (p->state & TASK_UNINTERRUPTIBLE) ? 'D' : + (p->state & TASK_STOPPED || p->ptrace & PT_PTRACED) ? 'T' : + (p->state & (TASK_ZOMBIE | TASK_DEAD)) ? 'Z' : + (p->state & TASK_INTERRUPTIBLE) ? 'S' : '?', + p->comm); +} + +/* + * This function does all command procesing for interfacing to gdb. + * + * NOTE: The INT nn instruction leaves the state of the interrupt + * enable flag UNCHANGED. That means that when this routine + * is entered via a breakpoint (INT 3) instruction from code + * that has interrupts enabled, then interrupts will STILL BE + * enabled when this routine is entered. The first thing that + * we do here is disable interrupts so as to prevent recursive + * entries and bothersome serial interrupts while we are + * trying to run the serial port in polled mode. + * + * For kernel version 2.1.xx the kgdb_cli() actually gets a spin lock so + * it is always necessary to do a restore_flags before returning + * so as to let go of that lock. + */ +int +kgdb_handle_exception(int exceptionVector, + int signo, int err_code, struct pt_regs *linux_regs) +{ + struct task_struct *usethread = NULL; + struct task_struct *thread_list_start = 0, *thread = NULL; + struct task_struct *p; + unsigned long addr, length; + unsigned long breakno, breaktype; + char *ptr; + unsigned long newPC; + threadref thref; + unsigned long threadid, tmpid; + int thread_min = PID_MAX + MAX_NO_CPUS; +#ifdef old_thread_list + int maxthreads; +#endif + int nothreads; + unsigned long flags; + unsigned long gdb_regs[NUMREGS]; + unsigned long dr6; + IF_SMP(int entry_state = 0); /* 0, ok, 1, no nmi, 2 sync failed */ +#define NO_NMI 1 +#define NO_SYNC 2 +#define regs (*linux_regs) + /* + * If the entry is not from the kernel then return to the Linux + * trap handler and let it process the interrupt normally. + */ + if ((linux_regs->eflags & VM_MASK) || (3 & linux_regs->cs)) { + printk("ignoring non-kernel exception\n"); + print_regs(®s); + return (0); + } + /* + * If we're using eth mode, set the 'mode' in the netdevice. + */ + + if (kgdboe) + netpoll_set_trap(1); + + local_irq_save(flags); + + /* Get kgdb spinlock */ + + KGDB_SPIN_LOCK(&kgdb_spinlock); + rdtscll(kgdb_info.entry_tsc); + /* + * We depend on this spinlock and the NMI watch dog to control the + * other cpus. They will arrive at "in_kgdb()" as a result of the + * NMI and will wait there for the following spin locks to be + * released. + */ +#ifdef CONFIG_SMP + +#if 0 + if (cpu_callout_map & ~MAX_CPU_MASK) { + printk("kgdb : too many cpus, possibly not mapped" + " in contiguous space, change MAX_NO_CPUS" + " in kgdb_stub and make new kernel.\n" + " cpu_callout_map is %lx\n", cpu_callout_map); + goto exit_just_unlock; + } +#endif + if (spinlock_count == 1) { + int time, end_time, dum; + int i; + int cpu_logged_in[MAX_NO_CPUS] = {[0 ... MAX_NO_CPUS - 1] = (0) + }; + if (remote_debug) { + printk("kgdb : cpu %d entry, syncing others\n", + smp_processor_id()); + } + for (i = 0; i < MAX_NO_CPUS; i++) { + /* + * Use trylock as we may already hold the lock if + * we are holding the cpu. Net result is all + * locked. + */ + spin_trylock(&waitlocks[i]); + } + for (i = 0; i < MAX_NO_CPUS; i++) + cpu_logged_in[i] = 0; + /* + * Wait for their arrival. We know the watch dog is active if + * in_kgdb() has ever been called, as it is always called on a + * watchdog tick. + */ + rdtsc(dum, time); + end_time = time + 2; /* Note: we use the High order bits! */ + i = 1; + if (num_online_cpus() > 1) { + int me_in_kgdb = in_kgdb_entry_log[smp_processor_id()]; + smp_send_nmi_allbutself(); + + while (i < num_online_cpus() && time != end_time) { + int j; + for (j = 0; j < MAX_NO_CPUS; j++) { + if (waiting_cpus[j].task && + waiting_cpus[j].task != NOCPU && + !cpu_logged_in[j]) { + i++; + cpu_logged_in[j] = 1; + if (remote_debug) { + printk + ("kgdb : cpu %d arrived at kgdb\n", + j); + } + break; + } else if (!waiting_cpus[j].task && + !cpu_online(j)) { + waiting_cpus[j].task = NOCPU; + cpu_logged_in[j] = 1; + waiting_cpus[j].hold = 1; + break; + } + if (!waiting_cpus[j].task && + in_kgdb_here_log[j]) { + + int wait = 100000; + while (wait--) ; + if (!waiting_cpus[j].task && + in_kgdb_here_log[j]) { + printk + ("kgdb : cpu %d stall" + " in in_kgdb\n", + j); + i++; + cpu_logged_in[j] = 1; + waiting_cpus[j].task = + (struct task_struct + *) 1; + } + } + } + + if (in_kgdb_entry_log[smp_processor_id()] > + (me_in_kgdb + 10)) { + break; + } + + rdtsc(dum, time); + } + if (i < num_online_cpus()) { + printk + ("kgdb : time out, proceeding without sync\n"); +#if 0 + printk("kgdb : Waiting_cpus: 0 = %d, 1 = %d\n", + waiting_cpus[0].task != 0, + waiting_cpus[1].task != 0); + printk("kgdb : Cpu_logged in: 0 = %d, 1 = %d\n", + cpu_logged_in[0], cpu_logged_in[1]); + printk + ("kgdb : in_kgdb_here_log in: 0 = %d, 1 = %d\n", + in_kgdb_here_log[0] != 0, + in_kgdb_here_log[1] != 0); +#endif + entry_state = NO_SYNC; + } else { +#if 0 + int ent = + in_kgdb_entry_log[smp_processor_id()] - + me_in_kgdb; + printk("kgdb : sync after %d entries\n", ent); +#endif + } + } else { + if (remote_debug) { + printk + ("kgdb : %d cpus, but watchdog not active\n" + "proceeding without locking down other cpus\n", + (int)num_online_cpus()); + entry_state = NO_NMI; + } + } + } +#endif + + if (remote_debug) { + unsigned long *lp = (unsigned long *) &linux_regs; + + printk("handle_exception(exceptionVector=%d, " + "signo=%d, err_code=%d, linux_regs=%p)\n", + exceptionVector, signo, err_code, linux_regs); + if (debug_regs) { + print_regs(®s); + printk("Stk: %8lx %8lx %8lx %8lx" + " %8lx %8lx %8lx %8lx\n", + lp[0], lp[1], lp[2], lp[3], + lp[4], lp[5], lp[6], lp[7]); + printk(" %8lx %8lx %8lx %8lx" + " %8lx %8lx %8lx %8lx\n", + lp[8], lp[9], lp[10], lp[11], + lp[12], lp[13], lp[14], lp[15]); + printk(" %8lx %8lx %8lx %8lx " + "%8lx %8lx %8lx %8lx\n", + lp[16], lp[17], lp[18], lp[19], + lp[20], lp[21], lp[22], lp[23]); + printk(" %8lx %8lx %8lx %8lx " + "%8lx %8lx %8lx %8lx\n", + lp[24], lp[25], lp[26], lp[27], + lp[28], lp[29], lp[30], lp[31]); + } + } + + /* Disable hardware debugging while we are in kgdb */ + /* Get the debug register status register */ +/* *INDENT-OFF* */ + __asm__("movq %0,%%db7" + : /* no output */ + :"r"(0UL)); + + asm volatile ("movq %%db6, %0\n" + :"=r" (hw_breakpoint_status) + :); + +#if 0 +/* *INDENT-ON* */ + switch (exceptionVector) { + case 0: /* divide error */ + case 1: /* debug exception */ + case 2: /* NMI */ + case 3: /* breakpoint */ + case 4: /* overflow */ + case 5: /* bounds check */ + case 6: /* invalid opcode */ + case 7: /* device not available */ + case 8: /* double fault (errcode) */ + case 10: /* invalid TSS (errcode) */ + case 12: /* stack fault (errcode) */ + case 16: /* floating point error */ + case 17: /* alignment check (errcode) */ + default: /* any undocumented */ + break; + case 11: /* segment not present (errcode) */ + case 13: /* general protection (errcode) */ + case 14: /* page fault (special errcode) */ + case 19: /* cache flush denied */ + if (mem_err_expected) { + /* + * This fault occured because of the + * get_char or set_char routines. These + * two routines use either eax of edx to + * indirectly reference the location in + * memory that they are working with. + * For a page fault, when we return the + * instruction will be retried, so we + * have to make sure that these + * registers point to valid memory. + */ + mem_err = 1; /* set mem error flag */ + mem_err_expected = 0; + mem_err_cnt++; /* helps in debugging */ + /* make valid address */ + regs.eax = (long) &garbage_loc; + /* make valid address */ + regs.edx = (long) &garbage_loc; + if (remote_debug) + printk("Return after memory error: " + "mem_err_cnt=%d\n", mem_err_cnt); + if (debug_regs) + print_regs(®s); + goto exit_kgdb; + } + break; + } +#endif + if (remote_debug) + printk("kgdb : entered kgdb on cpu %d\n", smp_processor_id()); + + gdb_i386vector = exceptionVector; + gdb_i386errcode = err_code; + kgdb_info.called_from = __builtin_return_address(0); +#ifdef CONFIG_SMP + /* + * OK, we can now communicate, lets tell gdb about the sync. + * but only if we had a problem. + */ + switch (entry_state) { + case NO_NMI: + to_gdb("NMI not active, other cpus not stopped\n"); + break; + case NO_SYNC: + to_gdb("Some cpus not stopped, see 'kgdb_info' for details\n"); + default:; + } + +#endif +/* + * Set up the gdb function call area. + */ + trap_cpu = smp_processor_id(); + OLD_esp = NEW_esp = (unsigned long) (&linux_regs->rsp); + + IF_SMP(once_again:) + /* reply to host that an exception has occurred */ + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[signo >> 4]; + remcomOutBuffer[2] = hexchars[signo % 16]; + remcomOutBuffer[3] = 0; + + putpacket(remcomOutBuffer); + + while (1 == 1) { + error = 0; + remcomOutBuffer[0] = 0; + getpacket(remcomInBuffer); + switch (remcomInBuffer[0]) { + case '?': + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[signo >> 4]; + remcomOutBuffer[2] = hexchars[signo % 16]; + remcomOutBuffer[3] = 0; + break; + case 'd': + remote_debug = !(remote_debug); /* toggle debug flag */ + printk("Remote debug %s\n", + remote_debug ? "on" : "off"); + break; + case 'g': /* return the value of the CPU registers */ + get_gdb_regs(usethread, ®s, gdb_regs); + mem2hex((char *) gdb_regs, + remcomOutBuffer, NUMREGBYTES); + break; + case 'G': /* set the value of the CPU registers - return OK */ + hex2mem(&remcomInBuffer[1], + (char *) gdb_regs, NUMREGBYTES); + if (!usethread || usethread == current) { + gdb_regs_to_regs(gdb_regs, ®s); + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "E00"); + } + break; + + case 'P':{ /* set the value of a single CPU register - + return OK */ + /* + * For some reason, gdb wants to talk about psudo + * registers (greater than 15). + */ + unsigned long regno; + + ptr = &remcomInBuffer[1]; + regs_to_gdb_regs(gdb_regs, ®s); + if ((!usethread || usethread == current) && + hexToLong(&ptr, ®no) && + *ptr++ == '=' && (regno >= 0)) { + if (regno >= NUMREGS) + break; + hex2mem(ptr, (char *) &gdb_regs[regno], + 8); + gdb_regs_to_regs(gdb_regs, ®s); + strcpy(remcomOutBuffer, "OK"); + break; + } + strcpy(remcomOutBuffer, "E01"); + break; + } + + /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + case 'm': + /* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */ + ptr = &remcomInBuffer[1]; + if (hexToLong(&ptr, &addr) && + (*(ptr++) == ',') && (hexToLong(&ptr, &length))) { + ptr = 0; + /* + * hex doubles the byte count + */ + if (length > (BUFMAX / 2)) + length = BUFMAX / 2; + if (mem2hex((char *) addr, + remcomOutBuffer, length)) { + strcpy(remcomOutBuffer, "E03"); + debug_error("memory fault\n", NULL); + } + } + + if (ptr) { + strcpy(remcomOutBuffer, "E01"); + debug_error + ("malformed read memory command: %s\n", + remcomInBuffer); + } + break; + + /* MAA..AA,LLLL: + Write LLLL bytes at address AA.AA return OK */ + case 'M': + /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */ + ptr = &remcomInBuffer[1]; + if (hexToLong(&ptr, &addr) && + (*(ptr++) == ',') && + (hexToLong(&ptr, &length)) && (*(ptr++) == ':')) { + if (hex2mem(ptr, (char *) addr, length)) { + strcpy(remcomOutBuffer, "E03"); + debug_error("memory fault\n", NULL); + } else { + strcpy(remcomOutBuffer, "OK"); + } + + ptr = 0; + } + if (ptr) { + strcpy(remcomOutBuffer, "E02"); + debug_error + ("malformed write memory command: %s\n", + remcomInBuffer); + } + break; + case 'S': + remcomInBuffer[0] = 's'; + case 'C': + /* Csig;AA..AA where ;AA..AA is optional + * continue with signal + * Since signals are meaning less to us, delete that + * part and then fall into the 'c' code. + */ + ptr = &remcomInBuffer[1]; + length = 2; + while (*ptr && *ptr != ';') { + length++; + ptr++; + } + if (*ptr) { + do { + ptr++; + *(ptr - length++) = *ptr; + } while (*ptr); + } else { + remcomInBuffer[1] = 0; + } + + /* cAA..AA Continue at address AA..AA(optional) */ + /* sAA..AA Step one instruction from AA..AA(optional) */ + /* D detach, reply OK and then continue */ + case 'c': + case 's': + case 'D': + + /* try to read optional parameter, + pc unchanged if no parm */ + ptr = &remcomInBuffer[1]; + if (hexToLong(&ptr, &addr)) { + if (remote_debug) + printk("Changing EIP to 0x%lx\n", addr); + + regs.rip = addr; + } + + newPC = regs.rip; + + /* clear the trace bit */ + regs.eflags &= 0xfffffeff; + + /* set the trace bit if we're stepping */ + if (remcomInBuffer[0] == 's') + regs.eflags |= 0x100; + + /* detach is a friendly version of continue. Note that + debugging is still enabled (e.g hit control C) + */ + if (remcomInBuffer[0] == 'D') { + strcpy(remcomOutBuffer, "OK"); + putpacket(remcomOutBuffer); + } + + if (remote_debug) { + printk("Resuming execution\n"); + print_regs(®s); + } + asm volatile ("movq %%db6, %0\n":"=r" (dr6) + :); + if (!(dr6 & 0x4000)) { + for (breakno = 0; breakno < 4; ++breakno) { + if (dr6 & (1 << breakno) && + (breakinfo[breakno].type == 0)) { + /* Set restore flag */ + regs.eflags |= 0x10000; + break; + } + } + } + + if (kgdboe) + netpoll_set_trap(0); + + correct_hw_break(); + asm volatile ("movq %0, %%db6\n"::"r" (0UL)); + goto exit_kgdb; + + /* kill the program */ + case 'k': /* do nothing */ + break; + + /* query */ + case 'q': + nothreads = 0; + switch (remcomInBuffer[1]) { + case 'f': + threadid = 1; + thread_list = 2; + thread_list_start = (usethread ? : current); + case 's': + if (!cmp_str(&remcomInBuffer[2], + "ThreadInfo", 10)) + break; + + remcomOutBuffer[nothreads++] = 'm'; + for (; threadid < PID_MAX + MAX_NO_CPUS; + threadid++) { + thread = getthread(threadid); + if (thread) { + nothreads += int_to_hex_v( + &remcomOutBuffer[ + nothreads], + threadid); + if (thread_min > threadid) + thread_min = threadid; + remcomOutBuffer[ + nothreads] = ','; + nothreads++; + if (nothreads > BUFMAX - 10) + break; + } + } + if (remcomOutBuffer[nothreads - 1] == 'm') { + remcomOutBuffer[nothreads - 1] = 'l'; + } else { + nothreads--; + } + remcomOutBuffer[nothreads] = 0; + break; + +#ifdef old_thread_list /* Old thread info request */ + case 'L': + /* List threads */ + thread_list = 2; + thread_list_start = (usethread ? : current); + unpack_byte(remcomInBuffer + 3, &maxthreads); + unpack_threadid(remcomInBuffer + 5, &thref); + do { + int buf_thread_limit = + (BUFMAX - 22) / BUF_THREAD_ID_SIZE; + if (maxthreads > buf_thread_limit) { + maxthreads = buf_thread_limit; + } + } while (0); + remcomOutBuffer[0] = 'q'; + remcomOutBuffer[1] = 'M'; + remcomOutBuffer[4] = '0'; + pack_threadid(remcomOutBuffer + 5, &thref); + + /* If start flag set start at 0. */ + if (remcomInBuffer[2] == '1') + threadid = 0; + else + threadid = threadref_to_int(&thref); + for (nothreads = 0; + nothreads < maxthreads && + threadid < PID_MAX + MAX_NO_CPUS; + threadid++) { + thread = getthread(threadid); + if (thread) { + int_to_threadref(&thref, + threadid); + pack_threadid(remcomOutBuffer + + 21 + + nothreads * 16, + &thref); + nothreads++; + if (thread_min > threadid) + thread_min = threadid; + } + } + + if (threadid == PID_MAX + MAX_NO_CPUS) { + remcomOutBuffer[4] = '1'; + } + pack_hex_byte(remcomOutBuffer + 2, nothreads); + remcomOutBuffer[21 + nothreads * 16] = '\0'; + break; +#endif + case 'C': + /* Current thread id */ + remcomOutBuffer[0] = 'Q'; + remcomOutBuffer[1] = 'C'; + threadid = current->pid; + if (!threadid) { + /* + * idle thread + */ + for (threadid = PID_MAX; + threadid < PID_MAX + MAX_NO_CPUS; + threadid++) { + if (current == + idle_task(threadid - + PID_MAX)) + break; + } + } + int_to_threadref(&thref, threadid); + pack_threadid(remcomOutBuffer + 2, &thref); + remcomOutBuffer[18] = '\0'; + break; + + case 'E': + /* Print exception info */ + printexceptioninfo(exceptionVector, + err_code, remcomOutBuffer); + break; + case 'T': + ptr = &remcomInBuffer[0]; + if (strncmp(ptr, "qThreadExtraInfo,", + strlen("qThreadExtraInfo,")) == 0) { + ptr += strlen("qThreadExtraInfo,"); + hexToLong(&ptr, &tmpid); + p = getthread(tmpid); + print_extra_info(p, lbuf); + mem2hex(lbuf, remcomOutBuffer, + strlen(lbuf)); + } + break; +#if 0 + case 'T':{ + char * nptr; + /* Thread extra info */ + if (!cmp_str(&remcomInBuffer[2], + "hreadExtraInfo,", 15)) { + break; + } + ptr = &remcomInBuffer[17]; + hexToLong(&ptr, &threadid); + thread = getthread(threadid); + nptr = &thread->comm[0]; + length = 0; + ptr = &remcomOutBuffer[0]; + do { + length++; + ptr = pack_hex_byte(ptr, *nptr++); + } while (*nptr && length < 16); + /* + * would like that 16 to be the size of + * task_struct.comm but don't know the + * syntax.. + */ + *ptr = 0; + } +#endif + } + break; + + /* task related */ + case 'H': + switch (remcomInBuffer[1]) { + case 'g': + ptr = &remcomInBuffer[2]; + hexToLong(&ptr, &threadid); + thread = getthread(threadid); + if (!thread) { + remcomOutBuffer[0] = 'E'; + remcomOutBuffer[1] = '\0'; + break; + } + /* + * Just in case I forget what this is all about, + * the "thread info" command to gdb causes it + * to ask for a thread list. It then switches + * to each thread and asks for the registers. + * For this (and only this) usage, we want to + * fudge the registers of tasks not on the run + * list (i.e. waiting) to show the routine that + * called schedule. Also, gdb, is a minimalist + * in that if the current thread is the last + * it will not re-read the info when done. + * This means that in this case we must show + * the real registers. So here is how we do it: + * Each entry we keep track of the min + * thread in the list (the last that gdb will) + * get info for. We also keep track of the + * starting thread. + * "thread_list" is cleared when switching back + * to the min thread if it is was current, or + * if it was not current, thread_list is set + * to 1. When the switch to current comes, + * if thread_list is 1, clear it, else do + * nothing. + */ + usethread = thread; + if ((thread_list == 1) && + (thread == thread_list_start)) { + thread_list = 0; + } + if (thread_list && (threadid == thread_min)) { + if (thread == thread_list_start) { + thread_list = 0; + } else { + thread_list = 1; + } + } + /* follow through */ + case 'c': + remcomOutBuffer[0] = 'O'; + remcomOutBuffer[1] = 'K'; + remcomOutBuffer[2] = '\0'; + break; + } + break; + + /* Query thread status */ + case 'T': + ptr = &remcomInBuffer[1]; + hexToLong(&ptr, &threadid); + thread = getthread(threadid); + if (thread) { + remcomOutBuffer[0] = 'O'; + remcomOutBuffer[1] = 'K'; + remcomOutBuffer[2] = '\0'; + if (thread_min > threadid) + thread_min = threadid; + } else { + remcomOutBuffer[0] = 'E'; + remcomOutBuffer[1] = '\0'; + } + break; + + case 'Y': /* set up a hardware breakpoint */ + ptr = &remcomInBuffer[1]; + hexToLong(&ptr, &breakno); + ptr++; + hexToLong(&ptr, &breaktype); + ptr++; + hexToLong(&ptr, &length); + ptr++; + hexToLong(&ptr, &addr); + if (set_hw_break(breakno & 0x3, + breaktype & 0x3, + length & 0x3, addr) == 0) { + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "ERROR"); + } + break; + + /* Remove hardware breakpoint */ + case 'y': + ptr = &remcomInBuffer[1]; + hexToLong(&ptr, &breakno); + if (remove_hw_break(breakno & 0x3) == 0) { + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "ERROR"); + } + break; + + case 'r': /* reboot */ + strcpy(remcomOutBuffer, "OK"); + putpacket(remcomOutBuffer); + /*to_gdb("Rebooting\n"); */ + /* triplefault no return from here */ + { + static long no_idt[2]; + __asm__ __volatile__("lidt %0"::"m"(no_idt[0])); + BREAKPOINT; + } + + } /* switch */ + + /* reply to the request */ + putpacket(remcomOutBuffer); + } /* while(1==1) */ + /* + * reached by goto only. + */ + exit_kgdb: + /* + * Here is where we set up to trap a gdb function call. NEW_esp + * will be changed if we are trying to do this. We handle both + * adding and subtracting, thus allowing gdb to put grung on + * the stack which it removes later. + */ + if (NEW_esp != OLD_esp) { + unsigned long *ptr = END_OF_LOOKASIDE; + if (NEW_esp < OLD_esp) + ptr -= (OLD_esp - NEW_esp) / sizeof (unsigned long); + *--ptr = linux_regs->eflags; + *--ptr = linux_regs->cs; + *--ptr = linux_regs->rip; + *--ptr = linux_regs->rcx; + *--ptr = linux_regs->rbx; + *--ptr = linux_regs->rax; + linux_regs->rcx = NEW_esp - (sizeof (unsigned long) * 6); + linux_regs->rbx = (unsigned long) END_OF_LOOKASIDE; + if (NEW_esp < OLD_esp) { + linux_regs->rip = (unsigned long) fn_call_stub; + } else { + linux_regs->rip = (unsigned long) fn_rtn_stub; + linux_regs->rax = NEW_esp; + } + linux_regs->eflags &= ~(IF_BIT | TF_BIT); + } +#ifdef CONFIG_SMP + /* + * Release gdb wait locks + * Sanity check time. Must have at least one cpu to run. Also single + * step must not be done if the current cpu is on hold. + */ + if (spinlock_count == 1) { + int ss_hold = (regs.eflags & 0x100) && kgdb_info.hold_on_sstep; + int cpu_avail = 0; + int i; + + for (i = 0; i < MAX_NO_CPUS; i++) { + if (!cpu_online(i)) + break; + if (!hold_cpu(i)) { + cpu_avail = 1; + } + } + /* + * Early in the bring up there will be NO cpus on line... + */ + if (!cpu_avail && !cpus_empty(cpu_online_map)) { + to_gdb("No cpus unblocked, see 'kgdb_info.hold_cpu'\n"); + goto once_again; + } + if (hold_cpu(smp_processor_id()) && (regs.eflags & 0x100)) { + to_gdb + ("Current cpu must be unblocked to single step\n"); + goto once_again; + } + if (!(ss_hold)) { + int i; + for (i = 0; i < MAX_NO_CPUS; i++) { + if (!hold_cpu(i)) { + spin_unlock(&waitlocks[i]); + } + } + } else { + spin_unlock(&waitlocks[smp_processor_id()]); + } + /* Release kgdb spinlock */ + KGDB_SPIN_UNLOCK(&kgdb_spinlock); + /* + * If this cpu is on hold, this is where we + * do it. Note, the NMI will pull us out of here, + * but will return as the above lock is not held. + * We will stay here till another cpu releases the lock for us. + */ + spin_unlock_wait(waitlocks + smp_processor_id()); + local_irq_restore(flags); + return (1); + } +#if 0 +exit_just_unlock: +#endif +#endif + /* Release kgdb spinlock */ + KGDB_SPIN_UNLOCK(&kgdb_spinlock); + local_irq_restore(flags); + return (1); +} + +#undef regs +static int kgdb_notify(struct notifier_block *self, unsigned long cmd, void *ptr) +{ + struct die_args *d = ptr; + + if (!kgdb_enabled || (cmd == DIE_DEBUG && user_mode(d->regs))) + return NOTIFY_DONE; + if (cmd == DIE_NMI_IPI) { + if (in_kgdb(d->regs)) + return NOTIFY_BAD; + } else if (kgdb_handle_exception(d->trapnr, d->signr, d->err, d->regs)) + return NOTIFY_BAD; /* skip */ + + return NOTIFY_DONE; +} + +static struct notifier_block kgdb_notifier = { + .notifier_call = kgdb_notify, + .priority = 0, +}; + +void set_debug_traps(void) +{ + static int initialized = 0; + + if (!initialized) { + initialized = 1; + notifier_chain_register(&die_chain, &kgdb_notifier); + } +} + +/* + * Provide the command line "gdb" initial break + */ +int __init kgdb_initial_break(char * str) +{ + if (*str == '\0'){ + breakpoint(); + return 1; + } + return 0; +} +__setup("gdb",kgdb_initial_break); + +/* This function will generate a breakpoint exception. It is used at the + beginning of a program to sync up with a debugger and can be used + otherwise as a quick means to stop program execution and "break" into + the debugger. */ +/* But really, just use the BREAKPOINT macro. We will handle the int stuff + */ + +void breakpoint(void) +{ + + set_debug_traps(); + kgdb_enabled = 1; +#if 0 + /* + * These calls were not enough to allow breakpoint to be + * called before trap_init(). I moved the argument parsing + * after trap_init() and it seems to work. + */ + set_intr_usr_gate(3,&int3); /* disable ints on trap */ + set_intr_gate(1,&debug); + set_intr_gate(14,&page_fault); +#endif + + BREAKPOINT; +} + +#ifdef later +/* + * possibly we should not go thru the traps.c code at all? Someday. + */ +void +do_kgdb_int3(struct pt_regs *regs, long error_code) +{ + kgdb_handle_exception(3, 5, error_code, regs); + return; +} +#endif +#undef regs +#ifdef CONFIG_TRAP_BAD_SYSCALL_EXITS +asmlinkage void +bad_sys_call_exit(int stuff) +{ + struct pt_regs *regs = (struct pt_regs *) &stuff; + printk("Sys call %d return with %x preempt_count\n", + (int) regs->orig_eax, preempt_count()); +} +#endif +#ifdef CONFIG_STACK_OVERFLOW_TEST +#include +asmlinkage void +stack_overflow(void) +{ +#ifdef BREAKPOINT + BREAKPOINT; +#else + printk("Kernel stack overflow, looping forever\n"); +#endif + while (1) { + } +} +#endif + +#if defined(CONFIG_SMP) || defined(CONFIG_KGDB_CONSOLE) +char gdbconbuf[BUFMAX]; + +static void +kgdb_gdb_message(const char *s, unsigned count) +{ + int i; + int wcount; + char *bufptr; + /* + * This takes care of NMI while spining out chars to gdb + */ + IF_SMP(in_kgdb_console = 1); + gdbconbuf[0] = 'O'; + bufptr = gdbconbuf + 1; + while (count > 0) { + if ((count << 1) > (BUFMAX - 2)) { + wcount = (BUFMAX - 2) >> 1; + } else { + wcount = count; + } + count -= wcount; + for (i = 0; i < wcount; i++) { + bufptr = pack_hex_byte(bufptr, s[i]); + } + *bufptr = '\0'; + s += wcount; + + putpacket(gdbconbuf); + + } + IF_SMP(in_kgdb_console = 0); +} +#endif +#ifdef CONFIG_SMP +static void +to_gdb(const char *s) +{ + int count = 0; + while (s[count] && (count++ < BUFMAX)) ; + kgdb_gdb_message(s, count); +} +#endif +#ifdef CONFIG_KGDB_CONSOLE +#include +#include +#include +#include + +void +kgdb_console_write(struct console *co, const char *s, unsigned count) +{ + + if (gdb_i386vector == -1) { + /* + * We have not yet talked to gdb. What to do... + * lets break, on continue we can do the write. + * But first tell him whats up. Uh, well no can do, + * as this IS the console. Oh well... + * We do need to wait or the messages will be lost. + * Other option would be to tell the above code to + * ignore this breakpoint and do an auto return, + * but that might confuse gdb. Also this happens + * early enough in boot up that we don't have the traps + * set up yet, so... + */ + breakpoint(); + } + kgdb_gdb_message(s, count); +} + +/* + * ------------------------------------------------------------ + * Serial KGDB driver + * ------------------------------------------------------------ + */ + +static struct console kgdbcons = { + name:"kgdb", + write:kgdb_console_write, +#ifdef CONFIG_KGDB_USER_CONSOLE + device:kgdb_console_device, +#endif + flags:CON_PRINTBUFFER | CON_ENABLED, + index:-1, +}; + +/* + * The trick here is that this file gets linked before printk.o + * That means we get to peer at the console info in the command + * line before it does. If we are up, we register, otherwise, + * do nothing. By returning 0, we allow printk to look also. + */ +static int kgdb_console_enabled; + +int __init +kgdb_console_init(char *str) +{ + if ((strncmp(str, "kgdb", 4) == 0) || (strncmp(str, "gdb", 3) == 0)) { + register_console(&kgdbcons); + kgdb_console_enabled = 1; + } + return 0; /* let others look at the string */ +} + +__setup("console=", kgdb_console_init); + +#ifdef CONFIG_KGDB_USER_CONSOLE +static kdev_t kgdb_console_device(struct console *c); +/* This stuff sort of works, but it knocks out telnet devices + * we are leaving it here in case we (or you) find time to figure it out + * better.. + */ + +/* + * We need a real char device as well for when the console is opened for user + * space activities. + */ + +static int +kgdb_consdev_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t +kgdb_consdev_write(struct file *file, const char *buf, + size_t count, loff_t * ppos) +{ + int size, ret = 0; + static char kbuf[128]; + static DECLARE_MUTEX(sem); + + /* We are not reentrant... */ + if (down_interruptible(&sem)) + return -ERESTARTSYS; + + while (count > 0) { + /* need to copy the data from user space */ + size = count; + if (size > sizeof (kbuf)) + size = sizeof (kbuf); + if (copy_from_user(kbuf, buf, size)) { + ret = -EFAULT; + break;; + } + kgdb_console_write(&kgdbcons, kbuf, size); + count -= size; + ret += size; + buf += size; + } + + up(&sem); + + return ret; +} + +struct file_operations kgdb_consdev_fops = { + open:kgdb_consdev_open, + write:kgdb_consdev_write +}; +static kdev_t +kgdb_console_device(struct console *c) +{ + return MKDEV(TTYAUX_MAJOR, 1); +} + +/* + * This routine gets called from the serial stub in the i386/lib + * This is so it is done late in bring up (just before the console open). + */ +void +kgdb_console_finit(void) +{ + if (kgdb_console_enabled) { + char *cptr = cdevname(MKDEV(TTYAUX_MAJOR, 1)); + char *cp = cptr; + while (*cptr && *cptr != '(') + cptr++; + *cptr = 0; + unregister_chrdev(TTYAUX_MAJOR, cp); + register_chrdev(TTYAUX_MAJOR, "kgdb", &kgdb_consdev_fops); + } +} +#endif +#endif +#ifdef CONFIG_KGDB_TS +#include /* time stamp code */ +#include /* in_interrupt */ +#ifdef CONFIG_KGDB_TS_64 +#define DATA_POINTS 64 +#endif +#ifdef CONFIG_KGDB_TS_128 +#define DATA_POINTS 128 +#endif +#ifdef CONFIG_KGDB_TS_256 +#define DATA_POINTS 256 +#endif +#ifdef CONFIG_KGDB_TS_512 +#define DATA_POINTS 512 +#endif +#ifdef CONFIG_KGDB_TS_1024 +#define DATA_POINTS 1024 +#endif +#ifndef DATA_POINTS +#define DATA_POINTS 128 /* must be a power of two */ +#endif +#define INDEX_MASK (DATA_POINTS - 1) +#if (INDEX_MASK & DATA_POINTS) +#error "CONFIG_KGDB_TS_COUNT must be a power of 2" +#endif +struct kgdb_and_then_struct { +#ifdef CONFIG_SMP + int on_cpu; +#endif + struct task_struct *task; + long long at_time; + int from_ln; + char *in_src; + void *from; + int *with_shpf; + int data0; + int data1; +}; +struct kgdb_and_then_struct2 { +#ifdef CONFIG_SMP + int on_cpu; +#endif + struct task_struct *task; + long long at_time; + int from_ln; + char *in_src; + void *from; + int *with_shpf; + struct task_struct *t1; + struct task_struct *t2; +}; +struct kgdb_and_then_struct kgdb_data[DATA_POINTS]; + +struct kgdb_and_then_struct *kgdb_and_then = &kgdb_data[0]; +int kgdb_and_then_count; + +void +kgdb_tstamp(int line, char *source, int data0, int data1) +{ + static spinlock_t ts_spin = SPIN_LOCK_UNLOCKED; + unsigned long flags; + + local_irq_save(flags); + spin_lock(&ts_spin); + rdtscll(kgdb_and_then->at_time); +#ifdef CONFIG_SMP + kgdb_and_then->on_cpu = smp_processor_id(); +#endif + kgdb_and_then->task = current; + kgdb_and_then->from_ln = line; + kgdb_and_then->in_src = source; + kgdb_and_then->from = __builtin_return_address(0); + kgdb_and_then->with_shpf = (int *)(long)(((flags & IF_BIT) >> 9) | + (preempt_count() << 8)); + kgdb_and_then->data0 = data0; + kgdb_and_then->data1 = data1; + kgdb_and_then = &kgdb_data[++kgdb_and_then_count & INDEX_MASK]; + spin_unlock(&ts_spin); + local_irq_restore(flags); +#ifdef CONFIG_PREEMPT + +#endif + return; +} +#endif +typedef int gdb_debug_hook(int exceptionVector, + int signo, int err_code, struct pt_regs *linux_regs); +gdb_debug_hook *linux_debug_hook = &kgdb_handle_exception; /* histerical reasons... */ + +static int kgdb_need_breakpoint[NR_CPUS]; + +void kgdb_schedule_breakpoint(void) +{ + kgdb_need_breakpoint[smp_processor_id()] = 1; +} + +void kgdb_process_breakpoint(void) +{ + /* + * Handle a breakpoint queued from inside network driver code + * to avoid reentrancy issues + */ + if (kgdb_need_breakpoint[smp_processor_id()]) { + kgdb_need_breakpoint[smp_processor_id()] = 0; + kgdb_enabled = 1; + BREAKPOINT; + } +} + --- linux-2.6.9-rc1/arch/x86_64/kernel/ldt.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/x86_64/kernel/ldt.c 2004-09-03 00:57:12.570128312 -0700 @@ -135,6 +135,7 @@ static int read_ldt(void __user * ptr, u return 0; if (bytecount > LDT_ENTRY_SIZE*LDT_ENTRIES) bytecount = LDT_ENTRY_SIZE*LDT_ENTRIES; + down(&mm->context.sem); size = mm->context.size*LDT_ENTRY_SIZE; if (size > bytecount) @@ -145,12 +146,17 @@ static int read_ldt(void __user * ptr, u err = -EFAULT; up(&mm->context.sem); if (err < 0) - return err; + goto error_return; if (size != bytecount) { /* zero-fill the rest */ - clear_user(ptr+size, bytecount-size); + if (clear_user(ptr+size, bytecount-size) != 0) { + err = -EFAULT; + goto error_return; + } } return bytecount; +error_return: + return err; } static int read_default_ldt(void __user * ptr, unsigned long bytecount) --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/x86_64/kernel/machine_kexec.c 2004-09-03 00:56:58.895207216 -0700 @@ -0,0 +1,246 @@ +/* + * machine_kexec.c - handle transition of Linux booting another kernel + * Copyright (C) 2002-2004 Eric Biederman + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LEVEL0_SIZE (1UL << 12UL) +#define LEVEL1_SIZE (1UL << 21UL) +#define LEVEL2_SIZE (1UL << 30UL) +#define LEVEL3_SIZE (1UL << 39UL) +#define LEVEL4_SIZE (1UL << 48UL) + +#define L0_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) +#define L1_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_PSE) +#define L2_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) +#define L3_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) + +static void init_level2_page( + uint64_t *level2p, unsigned long addr) +{ + unsigned long end_addr; + addr &= PAGE_MASK; + end_addr = addr + LEVEL2_SIZE; + while(addr < end_addr) { + *(level2p++) = addr | L1_ATTR; + addr += LEVEL1_SIZE; + } +} + +static int init_level3_page(struct kimage *image, + uint64_t *level3p, unsigned long addr, unsigned long last_addr) +{ + unsigned long end_addr; + int result; + result = 0; + addr &= PAGE_MASK; + end_addr = addr + LEVEL3_SIZE; + while((addr < last_addr) && (addr < end_addr)) { + struct page *page; + uint64_t *level2p; + page = kimage_alloc_control_pages(image, 0); + if (!page) { + result = -ENOMEM; + goto out; + } + level2p = (uint64_t *)page_address(page); + init_level2_page(level2p, addr); + *(level3p++) = __pa(level2p) | L2_ATTR; + addr += LEVEL2_SIZE; + } + /* clear the unused entries */ + while(addr < end_addr) { + *(level3p++) = 0; + addr += LEVEL2_SIZE; + } +out: + return result; +} + + +static int init_level4_page(struct kimage *image, + uint64_t *level4p, unsigned long addr, unsigned long last_addr) +{ + unsigned long end_addr; + int result; + result = 0; + addr &= PAGE_MASK; + end_addr = addr + LEVEL4_SIZE; + while((addr < last_addr) && (addr < end_addr)) { + struct page *page; + uint64_t *level3p; + page = kimage_alloc_control_pages(image, 0); + if (!page) { + result = -ENOMEM; + goto out; + } + level3p = (uint64_t *)page_address(page); + result = init_level3_page(image, level3p, addr, last_addr); + if (result) { + goto out; + } + *(level4p++) = __pa(level3p) | L3_ATTR; + addr += LEVEL3_SIZE; + } + /* clear the unused entries */ + while(addr < end_addr) { + *(level4p++) = 0; + addr += LEVEL3_SIZE; + } + out: + return result; +} + + +static int init_pgtable(struct kimage *image, unsigned long start_pgtable) +{ + uint64_t *level4p; + level4p = (uint64_t *)__va(start_pgtable); + return init_level4_page(image, level4p, 0, end_pfn << PAGE_SHIFT); +} + +static void set_idt(void *newidt, __u16 limit) +{ + unsigned char curidt[10]; + + /* x86-64 supports unaliged loads & stores */ + (*(__u16 *)(curidt)) = limit; + (*(__u64 *)(curidt +2)) = (unsigned long)(newidt); + + __asm__ __volatile__ ( + "lidt %0\n" + : "=m" (curidt) + ); +}; + + +static void set_gdt(void *newgdt, __u16 limit) +{ + unsigned char curgdt[10]; + + /* x86-64 supports unaligned loads & stores */ + (*(__u16 *)(curgdt)) = limit; + (*(__u64 *)(curgdt +2)) = (unsigned long)(newgdt); + + __asm__ __volatile__ ( + "lgdt %0\n" + : "=m" (curgdt) + ); +}; + +static void load_segments(void) +{ + __asm__ __volatile__ ( + "\tmovl $"STR(__KERNEL_DS)",%eax\n" + "\tmovl %eax,%ds\n" + "\tmovl %eax,%es\n" + "\tmovl %eax,%ss\n" + "\tmovl %eax,%fs\n" + "\tmovl %eax,%gs\n" + ); +#undef STR +#undef __STR +} + +typedef void (*relocate_new_kernel_t)( + unsigned long indirection_page, unsigned long control_code_buffer, + unsigned long start_address, unsigned long pgtable); + +const extern unsigned char relocate_new_kernel[]; +extern void relocate_new_kernel_end(void); +const extern unsigned long relocate_new_kernel_size; + +int machine_kexec_prepare(struct kimage *image) +{ + unsigned long start_pgtable, control_code_buffer; + int result; + + /* Calculate the offsets */ + start_pgtable = page_to_pfn(image->control_code_page) << PAGE_SHIFT; + control_code_buffer = start_pgtable + 4096UL; + + /* Setup the identity mapped 64bit page table */ + result = init_pgtable(image, start_pgtable); + if (result) { + return result; + } + + /* Place the code in the reboot code buffer */ + memcpy(__va(control_code_buffer), relocate_new_kernel, relocate_new_kernel_size); + + return 0; +} + +void machine_kexec_cleanup(struct kimage *image) +{ + return; +} + +/* + * Do not allocate memory (or fail in any way) in machine_kexec(). + * We are past the point of no return, committed to rebooting now. + */ +void machine_kexec(struct kimage *image) +{ + unsigned long indirection_page; + unsigned long control_code_buffer; + unsigned long start_pgtable; + relocate_new_kernel_t rnk; + + /* Interrupts aren't acceptable while we reboot */ + local_irq_disable(); + + /* Calculate the offsets */ + indirection_page = image->head & PAGE_MASK; + start_pgtable = page_to_pfn(image->control_code_page) << PAGE_SHIFT; + control_code_buffer = start_pgtable + 4096UL; + + /* Set the low half of the page table to my identity mapped + * page table for kexec. Leave the high half pointing at the + * kernel pages. Don't bother to flush the global pages + * as that will happen when I fully switch to my identity mapped + * page table anyway. + */ + memcpy((void *)read_pda(level4_pgt), __va(start_pgtable), PAGE_SIZE/2); + __flush_tlb(); + + + /* The segment registers are funny things, they are + * automatically loaded from a table, in memory wherever you + * set them to a specific selector, but this table is never + * accessed again unless you set the segment to a different selector. + * + * The more common model are caches where the behide + * the scenes work is done, but is also dropped at arbitrary + * times. + * + * I take advantage of this here by force loading the + * segments, before I zap the gdt with an invalid value. + */ + load_segments(); + /* The gdt & idt are now invalid. + * If you want to load them you must set up your own idt & gdt. + */ + set_gdt(phys_to_virt(0),0); + set_idt(phys_to_virt(0),0); + /* now call it */ + rnk = (relocate_new_kernel_t) control_code_buffer; + (*rnk)(indirection_page, control_code_buffer, image->start, start_pgtable); +} --- linux-2.6.9-rc1/arch/x86_64/kernel/Makefile 2004-08-24 00:03:25.000000000 -0700 +++ 25/arch/x86_64/kernel/Makefile 2004-09-03 00:56:58.896207064 -0700 @@ -18,6 +18,7 @@ obj-$(CONFIG_X86_CPUID) += cpuid.o obj-$(CONFIG_SMP) += smp.o smpboot.o trampoline.o obj-$(CONFIG_X86_LOCAL_APIC) += apic.o nmi.o obj-$(CONFIG_X86_IO_APIC) += io_apic.o mpparse.o +obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o obj-$(CONFIG_PM) += suspend.o obj-$(CONFIG_SOFTWARE_SUSPEND) += suspend_asm.o obj-$(CONFIG_CPU_FREQ) += cpufreq/ @@ -25,9 +26,9 @@ obj-$(CONFIG_EARLY_PRINTK) += early_prin obj-$(CONFIG_GART_IOMMU) += pci-gart.o aperture.o obj-$(CONFIG_DUMMY_IOMMU) += pci-nommu.o pci-dma.o obj-$(CONFIG_SWIOTLB) += swiotlb.o -obj-$(CONFIG_SCHED_SMT) += domain.o obj-$(CONFIG_MODULES) += module.o +obj-$(CONFIG_KGDB) += kgdb_stub.o obj-y += topology.o --- linux-2.6.9-rc1/arch/x86_64/kernel/Makefile-HEAD 2004-08-24 00:03:30.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,38 +0,0 @@ -# -# Makefile for the linux kernel. -# - -extra-y := head.o head64.o init_task.o vmlinux.lds -EXTRA_AFLAGS := -traditional -obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o \ - ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_x86_64.o \ - x8664_ksyms.o i387.o syscall.o vsyscall.o \ - setup64.o bootflag.o e820.o reboot.o warmreboot.o -obj-y += mce.o - -obj-$(CONFIG_MTRR) += ../../i386/kernel/cpu/mtrr/ -obj-$(CONFIG_ACPI_BOOT) += acpi/ -obj-$(CONFIG_X86_MSR) += msr.o -obj-$(CONFIG_MICROCODE) += microcode.o -obj-$(CONFIG_X86_CPUID) += cpuid.o -obj-$(CONFIG_SMP) += smp.o smpboot.o trampoline.o -obj-$(CONFIG_X86_LOCAL_APIC) += apic.o nmi.o -obj-$(CONFIG_X86_IO_APIC) += io_apic.o mpparse.o -obj-$(CONFIG_PM) += suspend.o -obj-$(CONFIG_SOFTWARE_SUSPEND) += suspend_asm.o -obj-$(CONFIG_CPU_FREQ) += cpufreq/ -obj-$(CONFIG_EARLY_PRINTK) += early_printk.o -obj-$(CONFIG_GART_IOMMU) += pci-gart.o aperture.o -obj-$(CONFIG_DUMMY_IOMMU) += pci-nommu.o pci-dma.o -obj-$(CONFIG_SWIOTLB) += swiotlb.o -obj-$(CONFIG_SCHED_SMT) += domain.o - -obj-$(CONFIG_MODULES) += module.o - -obj-y += topology.o - -bootflag-y += ../../i386/kernel/bootflag.o -cpuid-$(subst m,y,$(CONFIG_X86_CPUID)) += ../../i386/kernel/cpuid.o -topology-y += ../../i386/mach-default/topology.o -swiotlb-$(CONFIG_SWIOTLB) += ../../ia64/lib/swiotlb.o -microcode-$(subst m,y,$(CONFIG_MICROCODE)) += ../../i386/kernel/microcode.o --- linux-2.6.9-rc1/arch/x86_64/kernel/mce.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/x86_64/kernel/mce.c 2004-09-02 20:51:51.000000000 -0700 @@ -24,7 +24,8 @@ #define MISC_MCELOG_MINOR 227 #define NR_BANKS 5 -static int mce_disabled __initdata; +static int mce_dont_init; + /* 0: always panic, 1: panic if deadlock possible, 2: try to avoid panic, 3: never panic or exit (for testing only) */ static int tolerant = 1; @@ -113,9 +114,8 @@ static void mce_panic(char *msg, struct static int mce_available(struct cpuinfo_x86 *c) { - return !mce_disabled && - test_bit(X86_FEATURE_MCE, &c->x86_capability) && - test_bit(X86_FEATURE_MCA, &c->x86_capability); + return test_bit(X86_FEATURE_MCE, &c->x86_capability) && + test_bit(X86_FEATURE_MCA, &c->x86_capability); } /* @@ -127,8 +127,9 @@ void do_machine_check(struct pt_regs * r struct mce m, panicm; int nowayout = (tolerant < 1); int kill_it = 0; - u64 mcestart; + u64 mcestart = 0; int i; + int panicm_found = 0; if (regs) notify_die(DIE_NMI, "machine check", regs, error_code, 255, SIGKILL); @@ -138,17 +139,11 @@ void do_machine_check(struct pt_regs * r memset(&m, 0, sizeof(struct mce)); m.cpu = hard_smp_processor_id(); rdmsrl(MSR_IA32_MCG_STATUS, m.mcgstatus); - if (!regs && (m.mcgstatus & MCG_STATUS_MCIP)) - return; if (!(m.mcgstatus & MCG_STATUS_RIPV)) kill_it = 1; - if (regs) { - m.rip = regs->rip; - m.cs = regs->cs; - } rdtscll(mcestart); - mb(); + barrier(); for (i = 0; i < banks; i++) { if (!bank[i]) @@ -156,52 +151,62 @@ void do_machine_check(struct pt_regs * r m.misc = 0; m.addr = 0; + m.bank = i; + m.tsc = 0; rdmsrl(MSR_IA32_MC0_STATUS + i*4, m.status); if ((m.status & MCI_STATUS_VAL) == 0) continue; - /* Should be implied by the banks check above, but - check it anyways */ - if ((m.status & MCI_STATUS_EN) == 0) - continue; - /* Did this bank cause the exception? */ - /* Assume that the bank with uncorrectable errors did it, - and that there is only a single one. */ - if (m.status & MCI_STATUS_UC) { - panicm = m; - } else { - m.rip = 0; - m.cs = 0; + if (m.status & MCI_STATUS_EN) { + /* In theory _OVER could be a nowayout too, but + assume any overflowed errors were no fatal. */ + nowayout |= !!(m.status & MCI_STATUS_PCC); + kill_it |= !!(m.status & MCI_STATUS_UC); } - /* In theory _OVER could be a nowayout too, but - assume any overflowed errors were no fatal. */ - nowayout |= !!(m.status & MCI_STATUS_PCC); - kill_it |= !!(m.status & MCI_STATUS_UC); - m.bank = i; - if (m.status & MCI_STATUS_MISCV) rdmsrl(MSR_IA32_MC0_MISC + i*4, m.misc); if (m.status & MCI_STATUS_ADDRV) rdmsrl(MSR_IA32_MC0_ADDR + i*4, m.addr); - rdtscll(m.tsc); + if (regs && (m.mcgstatus & MCG_STATUS_RIPV)) { + m.rip = regs->rip; + m.cs = regs->cs; + } else { + m.rip = 0; + m.cs = 0; + } + + if (error_code != -1) + rdtscll(m.tsc); wrmsrl(MSR_IA32_MC0_STATUS + i*4, 0); mce_log(&m); + + /* Did this bank cause the exception? */ + /* Assume that the bank with uncorrectable errors did it, + and that there is only a single one. */ + if ((m.status & MCI_STATUS_UC) && (m.status & MCI_STATUS_EN)) { + panicm = m; + panicm_found = 1; + } } - wrmsrl(MSR_IA32_MCG_STATUS, 0); /* Never do anything final in the polling timer */ if (!regs) - return; + goto out; + + /* If we didn't find an uncorrectable error, pick + the last one (shouldn't happen, just being safe). */ + if (!panicm_found) + panicm = m; if (nowayout) - mce_panic("Machine check", &m, mcestart); + mce_panic("Machine check", &panicm, mcestart); if (kill_it) { int user_space = 0; if (m.mcgstatus & MCG_STATUS_RIPV) - user_space = m.rip && (m.cs & 3); + user_space = panicm.rip && (panicm.cs & 3); /* When the machine was in user space and the CPU didn't get confused it's normally not necessary to panic, unless you @@ -214,18 +219,15 @@ void do_machine_check(struct pt_regs * r (unsigned)current->pid <= 1) mce_panic("Uncorrected machine check", &panicm, mcestart); - /* do_exit takes an awful lot of locks and has as slight risk - of deadlocking. If you don't want that don't set tolerant >= 2 */ + /* do_exit takes an awful lot of locks and has as + slight risk of deadlocking. If you don't want that + don't set tolerant >= 2 */ if (tolerant < 3) do_exit(SIGBUS); } -} -static void mce_clear_all(void) -{ - int i; - for (i = 0; i < banks; i++) - wrmsrl(MSR_IA32_MC0_STATUS + i*4, 0); + out: + /* Last thing done in the machine check exception to clear state. */ wrmsrl(MSR_IA32_MCG_STATUS, 0); } @@ -268,22 +270,25 @@ static void mce_init(void *dummy) int i; rdmsrl(MSR_IA32_MCG_CAP, cap); - if (cap & MCG_CTL_P) - wrmsr(MSR_IA32_MCG_CTL, 0xffffffff, 0xffffffff); - banks = cap & 0xff; if (banks > NR_BANKS) { printk(KERN_INFO "MCE: warning: using only %d banks\n", banks); banks = NR_BANKS; } - mce_clear_all(); + /* Log the machine checks left over from the previous reset. + This also clears all registers */ + do_machine_check(NULL, -1); + + set_in_cr4(X86_CR4_MCE); + + if (cap & MCG_CTL_P) + wrmsr(MSR_IA32_MCG_CTL, 0xffffffff, 0xffffffff); + for (i = 0; i < banks; i++) { wrmsrl(MSR_IA32_MC0_CTL+4*i, bank[i]); wrmsrl(MSR_IA32_MC0_STATUS+4*i, 0); } - - set_in_cr4(X86_CR4_MCE); } /* Add per CPU specific workarounds here */ @@ -307,7 +312,9 @@ void __init mcheck_init(struct cpuinfo_x mce_cpu_quirks(c); - if (test_and_set_bit(smp_processor_id(), &mce_cpus) || !mce_available(c)) + if (mce_dont_init || + test_and_set_bit(smp_processor_id(), &mce_cpus) || + !mce_available(c)) return; mce_init(NULL); @@ -410,15 +417,16 @@ static struct miscdevice mce_log_device static int __init mcheck_disable(char *str) { - mce_disabled = 1; + mce_dont_init = 1; return 0; } -/* mce=off disable machine check */ +/* mce=off disables machine check. Note you can reenable it later + using sysfs */ static int __init mcheck_enable(char *str) { if (!strcmp(str, "off")) - mce_disabled = 1; + mce_dont_init = 1; else printk("mce= argument %s ignored. Please use /sys", str); return 0; @@ -434,7 +442,6 @@ __setup("mce", mcheck_enable); /* On resume clear all MCE state. Don't want to see leftovers from the BIOS. */ static int mce_resume(struct sys_device *dev) { - mce_clear_all(); on_each_cpu(mce_init, NULL, 1, 1); return 0; } @@ -492,7 +499,7 @@ static __init int mce_init_device(void) if (!err) err = sysdev_register(&device_mce); if (!err) { - /* could create per CPU objects, but is not worth it. */ + /* could create per CPU objects, but it is not worth it. */ sysdev_create_file(&device_mce, &attr_bank0ctl); sysdev_create_file(&device_mce, &attr_bank1ctl); sysdev_create_file(&device_mce, &attr_bank2ctl); --- linux-2.6.9-rc1/arch/x86_64/kernel/msr.c 2004-08-24 00:02:20.000000000 -0700 +++ 25/arch/x86_64/kernel/msr.c 2004-09-02 20:51:51.000000000 -0700 @@ -46,234 +46,229 @@ static inline int wrmsr_eio(u32 reg, u32 eax, u32 edx) { - int err; + int err; - asm volatile( - "1: wrmsr\n" - "2:\n" - ".section .fixup,\"ax\"\n" - "3: movl %4,%0\n" - " jmp 2b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 8\n" - " .quad 1b,3b\n" - ".previous" - : "=&bDS" (err) - : "a" (eax), "d" (edx), "c" (reg), "i" (-EIO), "0" (0)); + asm volatile ("1: wrmsr\n" + "2:\n" + ".section .fixup,\"ax\"\n" + "3: movl %4,%0\n" + " jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 8\n" " .quad 1b,3b\n" ".previous":"=&bDS" (err) + :"a"(eax), "d"(edx), "c"(reg), "i"(-EIO), "0"(0)); - return err; + return err; } static inline int rdmsr_eio(u32 reg, u32 *eax, u32 *edx) { - int err; + int err; - asm volatile( - "1: rdmsr\n" - "2:\n" - ".section .fixup,\"ax\"\n" - "3: movl %4,%0\n" - " jmp 2b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 8\n" - " .quad 1b,3b\n" - ".previous" - : "=&bDS" (err), "=a" (*eax), "=d" (*edx) - : "c" (reg), "i" (-EIO), "0" (0)); + asm volatile ("1: rdmsr\n" + "2:\n" + ".section .fixup,\"ax\"\n" + "3: movl %4,%0\n" + " jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .align 8\n" + " .quad 1b,3b\n" + ".previous":"=&bDS" (err), "=a"(*eax), "=d"(*edx) + :"c"(reg), "i"(-EIO), "0"(0)); - return err; + return err; } #ifdef CONFIG_SMP struct msr_command { - int cpu; - int err; - u32 reg; - u32 data[2]; + int cpu; + int err; + u32 reg; + u32 data[2]; }; static void msr_smp_wrmsr(void *cmd_block) { - struct msr_command *cmd = (struct msr_command *) cmd_block; - - if ( cmd->cpu == smp_processor_id() ) - cmd->err = wrmsr_eio(cmd->reg, cmd->data[0], cmd->data[1]); + struct msr_command *cmd = (struct msr_command *)cmd_block; + + if (cmd->cpu == smp_processor_id()) + cmd->err = wrmsr_eio(cmd->reg, cmd->data[0], cmd->data[1]); } static void msr_smp_rdmsr(void *cmd_block) { - struct msr_command *cmd = (struct msr_command *) cmd_block; - - if ( cmd->cpu == smp_processor_id() ) - cmd->err = rdmsr_eio(cmd->reg, &cmd->data[0], &cmd->data[1]); + struct msr_command *cmd = (struct msr_command *)cmd_block; + + if (cmd->cpu == smp_processor_id()) + cmd->err = rdmsr_eio(cmd->reg, &cmd->data[0], &cmd->data[1]); } static inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx) { - struct msr_command cmd; - int ret; + struct msr_command cmd; + int ret; + + preempt_disable(); + if (cpu == smp_processor_id()) { + ret = wrmsr_eio(reg, eax, edx); + } else { + cmd.cpu = cpu; + cmd.reg = reg; + cmd.data[0] = eax; + cmd.data[1] = edx; - preempt_disable(); - if ( cpu == smp_processor_id() ) { - ret = wrmsr_eio(reg, eax, edx); - } else { - cmd.cpu = cpu; - cmd.reg = reg; - cmd.data[0] = eax; - cmd.data[1] = edx; - - smp_call_function(msr_smp_wrmsr, &cmd, 1, 1); - ret = cmd.err; - } - preempt_enable(); - return ret; + smp_call_function(msr_smp_wrmsr, &cmd, 1, 1); + ret = cmd.err; + } + preempt_enable(); + return ret; } -static inline int do_rdmsr(int cpu, u32 reg, u32 *eax, u32 *edx) +static inline int do_rdmsr(int cpu, u32 reg, u32 * eax, u32 * edx) { - struct msr_command cmd; - int ret; + struct msr_command cmd; + int ret; + + preempt_disable(); + if (cpu == smp_processor_id()) { + ret = rdmsr_eio(reg, eax, edx); + } else { + cmd.cpu = cpu; + cmd.reg = reg; + + smp_call_function(msr_smp_rdmsr, &cmd, 1, 1); - preempt_disable(); - if ( cpu == smp_processor_id() ) { - ret = rdmsr_eio(reg, eax, edx); - } else { - cmd.cpu = cpu; - cmd.reg = reg; - - smp_call_function(msr_smp_rdmsr, &cmd, 1, 1); - - *eax = cmd.data[0]; - *edx = cmd.data[1]; - - ret = cmd.err; - } - preempt_enable(); - return ret; + *eax = cmd.data[0]; + *edx = cmd.data[1]; + + ret = cmd.err; + } + preempt_enable(); + return ret; } -#else /* ! CONFIG_SMP */ +#else /* ! CONFIG_SMP */ static inline int do_wrmsr(int cpu, u32 reg, u32 eax, u32 edx) { - return wrmsr_eio(reg, eax, edx); + return wrmsr_eio(reg, eax, edx); } static inline int do_rdmsr(int cpu, u32 reg, u32 *eax, u32 *edx) { - return rdmsr_eio(reg, eax, edx); + return rdmsr_eio(reg, eax, edx); } -#endif /* ! CONFIG_SMP */ +#endif /* ! CONFIG_SMP */ static loff_t msr_seek(struct file *file, loff_t offset, int orig) { - loff_t ret = -EINVAL; - lock_kernel(); - switch (orig) { - case 0: - file->f_pos = offset; - ret = file->f_pos; - break; - case 1: - file->f_pos += offset; - ret = file->f_pos; - } - unlock_kernel(); - return ret; -} - -static ssize_t msr_read(struct file * file, char __user * buf, - size_t count, loff_t *ppos) -{ - char __user *tmp = buf; - u32 data[2]; - size_t rv; - u32 reg = *ppos; - int cpu = iminor(file->f_dentry->d_inode); - int err; - - if ( count % 8 ) - return -EINVAL; /* Invalid chunk size */ - - for ( rv = 0 ; count ; count -= 8 ) { - err = do_rdmsr(cpu, reg, &data[0], &data[1]); - if ( err ) - return err; - if ( copy_to_user(tmp,&data,8) ) - return -EFAULT; - tmp += 8; - } + loff_t ret = -EINVAL; + + lock_kernel(); + switch (orig) { + case 0: + file->f_pos = offset; + ret = file->f_pos; + break; + case 1: + file->f_pos += offset; + ret = file->f_pos; + } + unlock_kernel(); + return ret; +} + +static ssize_t msr_read(struct file *file, char __user * buf, + size_t count, loff_t * ppos) +{ + u32 __user *tmp = (u32 __user *) buf; + u32 data[2]; + size_t rv; + u32 reg = *ppos; + int cpu = iminor(file->f_dentry->d_inode); + int err; + + if (count % 8) + return -EINVAL; /* Invalid chunk size */ + + for (rv = 0; count; count -= 8) { + err = do_rdmsr(cpu, reg, &data[0], &data[1]); + if (err) + return err; + if (copy_to_user(tmp, &data, 8)) + return -EFAULT; + tmp += 2; + } - return tmp - buf; + return ((char __user *)tmp) - buf; } -static ssize_t msr_write(struct file * file, const char __user * buf, +static ssize_t msr_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - const char __user *tmp = buf; - u32 data[2]; - size_t rv; - u32 reg = *ppos; - int cpu = iminor(file->f_dentry->d_inode); - int err; - - if ( count % 8 ) - return -EINVAL; /* Invalid chunk size */ - - for ( rv = 0 ; count ; count -= 8 ) { - if ( copy_from_user(&data,tmp,8) ) - return -EFAULT; - err = do_wrmsr(cpu, reg, data[0], data[1]); - if ( err ) - return err; - tmp += 8; - } + const u32 __user *tmp = (const u32 __user *)buf; + u32 data[2]; + size_t rv; + u32 reg = *ppos; + int cpu = iminor(file->f_dentry->d_inode); + int err; + + if (count % 8) + return -EINVAL; /* Invalid chunk size */ + + for (rv = 0; count; count -= 8) { + if (copy_from_user(&data, tmp, 8)) + return -EFAULT; + err = do_wrmsr(cpu, reg, data[0], data[1]); + if (err) + return err; + tmp += 2; + } - return tmp - buf; + return ((char __user *)tmp) - buf; } static int msr_open(struct inode *inode, struct file *file) { - int cpu = iminor(file->f_dentry->d_inode); - struct cpuinfo_x86 *c = &(cpu_data)[cpu]; - - if (cpu >= NR_CPUS || !cpu_online(cpu)) - return -ENXIO; /* No such CPU */ - if ( !cpu_has(c, X86_FEATURE_MSR) ) - return -EIO; /* MSR not supported */ - - return 0; + unsigned int cpu = iminor(file->f_dentry->d_inode); + struct cpuinfo_x86 *c = &(cpu_data)[cpu]; + + if (cpu >= NR_CPUS || !cpu_online(cpu)) + return -ENXIO; /* No such CPU */ + if (!cpu_has(c, X86_FEATURE_MSR)) + return -EIO; /* MSR not supported */ + + return 0; } /* * File operations we support */ static struct file_operations msr_fops = { - .owner = THIS_MODULE, - .llseek = msr_seek, - .read = msr_read, - .write = msr_write, - .open = msr_open, + .owner = THIS_MODULE, + .llseek = msr_seek, + .read = msr_read, + .write = msr_write, + .open = msr_open, }; int __init msr_init(void) { - if (register_chrdev(MSR_MAJOR, "cpu/msr", &msr_fops)) { - printk(KERN_ERR "msr: unable to get major %d for msr\n", - MSR_MAJOR); - return -EBUSY; - } - - return 0; + if (register_chrdev(MSR_MAJOR, "cpu/msr", &msr_fops)) { + printk(KERN_ERR "msr: unable to get major %d for msr\n", + MSR_MAJOR); + return -EBUSY; + } + + return 0; } void __exit msr_exit(void) { - unregister_chrdev(MSR_MAJOR, "cpu/msr"); + unregister_chrdev(MSR_MAJOR, "cpu/msr"); } module_init(msr_init); --- linux-2.6.9-rc1/arch/x86_64/kernel/pci-dma.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/x86_64/kernel/pci-dma.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Dynamic DMA mapping support. Common code + * Dynamic DMA mapping support. */ #include @@ -24,38 +24,37 @@ * Device ownership issues as mentioned above for pci_map_single are * the same here. */ -int pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, - int nents, int direction) +int dma_map_sg(struct device *hwdev, struct scatterlist *sg, + int nents, int direction) { int i; - BUG_ON(direction == PCI_DMA_NONE); + BUG_ON(direction == DMA_NONE); for (i = 0; i < nents; i++ ) { struct scatterlist *s = &sg[i]; BUG_ON(!s->page); - s->dma_address = pci_map_page(hwdev, s->page, s->offset, - s->length, direction); + s->dma_address = virt_to_bus(page_address(s->page) +s->offset); s->dma_length = s->length; } return nents; } -EXPORT_SYMBOL(pci_map_sg); +EXPORT_SYMBOL(dma_map_sg); /* Unmap a set of streaming mode DMA translations. * Again, cpu read rules concerning calls here are the same as for * pci_unmap_single() above. */ -void pci_unmap_sg(struct pci_dev *dev, struct scatterlist *sg, - int nents, int dir) +void dma_unmap_sg(struct device *dev, struct scatterlist *sg, + int nents, int dir) { int i; for (i = 0; i < nents; i++) { struct scatterlist *s = &sg[i]; BUG_ON(s->page == NULL); BUG_ON(s->dma_address == 0); - pci_unmap_single(dev, s->dma_address, s->dma_length, dir); + dma_unmap_single(dev, s->dma_address, s->dma_length, dir); } } -EXPORT_SYMBOL(pci_unmap_sg); +EXPORT_SYMBOL(dma_unmap_sg); --- linux-2.6.9-rc1/arch/x86_64/kernel/pci-gart.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/arch/x86_64/kernel/pci-gart.c 2004-09-02 20:51:51.000000000 -0700 @@ -31,12 +31,6 @@ #include #include -#ifdef CONFIG_PREEMPT -#define preempt_atomic() in_atomic() -#else -#define preempt_atomic() 1 -#endif - dma_addr_t bad_dma_address; unsigned long iommu_bus_base; /* GART remapping area (physical) */ @@ -54,7 +48,7 @@ int force_iommu = 1; int panic_on_overflow = 0; int force_iommu = 0; #endif -int iommu_merge = 0; +int iommu_merge = 1; int iommu_sac_force = 0; /* If this is disabled the IOMMU will use an optimized flushing strategy @@ -64,6 +58,10 @@ int iommu_sac_force = 0; also seen with Qlogic at least). */ int iommu_fullflush = 1; +/* This tells the BIO block layer to assume merging. Default to off + because we cannot guarantee merging later. */ +int iommu_bio_merge = 0; + #define MAX_NB 8 /* Allocation bitmap for the remapping area */ @@ -104,8 +102,16 @@ AGPEXTERN __u32 *agp_gatt_table; static unsigned long next_bit; /* protected by iommu_bitmap_lock */ static int need_flush; /* global flush state. set for each gart wrap */ -static dma_addr_t pci_map_area(struct pci_dev *dev, unsigned long phys_mem, - size_t size, int dir); +static dma_addr_t dma_map_area(struct device *dev, unsigned long phys_mem, + size_t size, int dir, int do_panic); + +/* Dummy device used for NULL arguments (normally ISA). Better would + be probably a smaller DMA mask, but this is bug-to-bug compatible to i386. */ +static struct device fallback_dev = { + .bus_id = "fallback device", + .coherent_dma_mask = 0xffffffff, + .dma_mask = &fallback_dev.coherent_dma_mask, +}; static unsigned long alloc_iommu(int size) { @@ -146,25 +152,31 @@ static void free_iommu(unsigned long off /* * Use global flush state to avoid races with multiple flushers. */ -static void flush_gart(struct pci_dev *dev) +static void flush_gart(struct device *dev) { unsigned long flags; int flushed = 0; - int i; + int i, max; spin_lock_irqsave(&iommu_bitmap_lock, flags); if (need_flush) { + max = 0; for (i = 0; i < MAX_NB; i++) { - u32 w; if (!northbridges[i]) continue; pci_write_config_dword(northbridges[i], 0x9c, northbridge_flush_word[i] | 1); + flushed++; + max = i; + } + for (i = 0; i <= max; i++) { + u32 w; + if (!northbridges[i]) + continue; /* Make sure the hardware actually executed the flush. */ do { pci_read_config_dword(northbridges[i], 0x9c, &w); } while (w & 1); - flushed++; } if (!flushed) printk("nothing to flush?\n"); @@ -173,31 +185,47 @@ static void flush_gart(struct pci_dev *d spin_unlock_irqrestore(&iommu_bitmap_lock, flags); } +/* Allocate DMA memory on node near device */ +noinline +static void *dma_alloc_pages(struct device *dev, unsigned gfp, unsigned order) +{ + struct page *page; + int node; + if (dev->bus == &pci_bus_type) { + cpumask_t mask; + mask = pcibus_to_cpumask(to_pci_dev(dev)->bus->number); + node = cpu_to_node(first_cpu(mask)); + } else + node = numa_node_id(); + page = alloc_pages_node(node, gfp, order); + return page ? page_address(page) : NULL; +} + /* - * Allocate memory for a consistent mapping. + * Allocate memory for a coherent mapping. */ -void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, - dma_addr_t *dma_handle) +void * +dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, + unsigned gfp) { void *memory; - int gfp = preempt_atomic() ? GFP_ATOMIC : GFP_KERNEL; unsigned long dma_mask = 0; u64 bus; - if (hwdev) - dma_mask = hwdev->dev.coherent_dma_mask; + if (!dev) + dev = &fallback_dev; + dma_mask = dev->coherent_dma_mask; if (dma_mask == 0) dma_mask = 0xffffffff; /* Kludge to make it bug-to-bug compatible with i386. i386 - uses the normal dma_mask for alloc_consistent. */ - if (hwdev) - dma_mask &= hwdev->dma_mask; + uses the normal dma_mask for alloc_coherent. */ + dma_mask &= *dev->dma_mask; again: - memory = (void *)__get_free_pages(gfp, get_order(size)); + memory = dma_alloc_pages(dev, gfp, get_order(size)); if (memory == NULL) - return NULL; + return NULL; { int high, mmu; @@ -223,28 +251,29 @@ void *pci_alloc_consistent(struct pci_de } } - *dma_handle = pci_map_area(hwdev, bus, size, PCI_DMA_BIDIRECTIONAL); + *dma_handle = dma_map_area(dev, bus, size, PCI_DMA_BIDIRECTIONAL, 0); if (*dma_handle == bad_dma_address) goto error; - flush_gart(hwdev); + flush_gart(dev); return memory; error: if (panic_on_overflow) - panic("pci_alloc_consistent: overflow %lu bytes\n", size); + panic("dma_alloc_coherent: IOMMU overflow by %lu bytes\n", size); free: free_pages((unsigned long)memory, get_order(size)); + /* XXX Could use the swiotlb pool here too */ return NULL; } /* - * Unmap consistent memory. + * Unmap coherent memory. * The caller must ensure that the device has finished accessing the mapping. */ -void pci_free_consistent(struct pci_dev *hwdev, size_t size, +void dma_free_coherent(struct device *dev, size_t size, void *vaddr, dma_addr_t bus) { - pci_unmap_single(hwdev, bus, size, 0); + dma_unmap_single(dev, bus, size, 0); free_pages((unsigned long)vaddr, get_order(size)); } @@ -280,7 +309,7 @@ void dump_leak(void) #define CLEAR_LEAK(x) #endif -static void iommu_full(struct pci_dev *dev, size_t size, int dir) +static void iommu_full(struct device *dev, size_t size, int dir, int do_panic) { /* * Ran out of IOMMU space for this operation. This is very bad. @@ -293,14 +322,14 @@ static void iommu_full(struct pci_dev *d */ printk(KERN_ERR - "PCI-DMA: Out of IOMMU space for %lu bytes at device %s[%s]\n", - size, dev ? pci_pretty_name(dev) : "", dev ? dev->slot_name : "?"); + "PCI-DMA: Out of IOMMU space for %lu bytes at device %s\n", + size, dev->bus_id); - if (size > PAGE_SIZE*EMERGENCY_PAGES) { + if (size > PAGE_SIZE*EMERGENCY_PAGES && do_panic) { if (dir == PCI_DMA_FROMDEVICE || dir == PCI_DMA_BIDIRECTIONAL) - panic("PCI-DMA: Memory will be corrupted\n"); + panic("PCI-DMA: Memory would be corrupted\n"); if (dir == PCI_DMA_TODEVICE || dir == PCI_DMA_BIDIRECTIONAL) - panic("PCI-DMA: Random memory will be DMAed\n"); + panic("PCI-DMA: Random memory would be DMAed\n"); } #ifdef CONFIG_IOMMU_LEAK @@ -308,9 +337,9 @@ static void iommu_full(struct pci_dev *d #endif } -static inline int need_iommu(struct pci_dev *dev, unsigned long addr, size_t size) +static inline int need_iommu(struct device *dev, unsigned long addr, size_t size) { - u64 mask = dev ? dev->dma_mask : 0xffffffff; + u64 mask = *dev->dma_mask; int high = addr + size >= mask; int mmu = high; if (force_iommu) @@ -323,9 +352,9 @@ static inline int need_iommu(struct pci_ return mmu; } -static inline int nonforced_iommu(struct pci_dev *dev, unsigned long addr, size_t size) +static inline int nonforced_iommu(struct device *dev, unsigned long addr, size_t size) { - u64 mask = dev ? dev->dma_mask : 0xffffffff; + u64 mask = *dev->dma_mask; int high = addr + size >= mask; int mmu = high; if (no_iommu) { @@ -339,8 +368,8 @@ static inline int nonforced_iommu(struct /* Map a single continuous physical area into the IOMMU. * Caller needs to check if the iommu is needed and flush. */ -static dma_addr_t pci_map_area(struct pci_dev *dev, unsigned long phys_mem, - size_t size, int dir) +static dma_addr_t dma_map_area(struct device *dev, unsigned long phys_mem, + size_t size, int dir, int do_panic) { unsigned long npages = to_pages(phys_mem, size); unsigned long iommu_page = alloc_iommu(npages); @@ -349,8 +378,8 @@ static dma_addr_t pci_map_area(struct pc if (!nonforced_iommu(dev, phys_mem, size)) return phys_mem; if (panic_on_overflow) - panic("pci_map_area overflow %lu bytes\n", size); - iommu_full(dev, size, dir); + panic("dma_map_area overflow %lu bytes\n", size); + iommu_full(dev, size, dir, do_panic); return bad_dma_address; } @@ -363,44 +392,44 @@ static dma_addr_t pci_map_area(struct pc } /* Map a single area into the IOMMU */ -dma_addr_t pci_map_single(struct pci_dev *dev, void *addr, size_t size, int dir) -{ +dma_addr_t dma_map_single(struct device *dev, void *addr, size_t size, int dir) +{ unsigned long phys_mem, bus; - BUG_ON(dir == PCI_DMA_NONE); + BUG_ON(dir == DMA_NONE); -#ifdef CONFIG_SWIOTLB if (swiotlb) - return swiotlb_map_single(&dev->dev,addr,size,dir); -#endif + return swiotlb_map_single(dev,addr,size,dir); + if (!dev) + dev = &fallback_dev; phys_mem = virt_to_phys(addr); if (!need_iommu(dev, phys_mem, size)) return phys_mem; - bus = pci_map_area(dev, phys_mem, size, dir); + bus = dma_map_area(dev, phys_mem, size, dir, 1); flush_gart(dev); return bus; } -/* Fallback for pci_map_sg in case of overflow */ -static int pci_map_sg_nonforce(struct pci_dev *dev, struct scatterlist *sg, +/* Fallback for dma_map_sg in case of overflow */ +static int dma_map_sg_nonforce(struct device *dev, struct scatterlist *sg, int nents, int dir) { int i; #ifdef CONFIG_IOMMU_DEBUG - printk(KERN_DEBUG "pci_map_sg overflow\n"); + printk(KERN_DEBUG "dma_map_sg overflow\n"); #endif for (i = 0; i < nents; i++ ) { struct scatterlist *s = &sg[i]; unsigned long addr = page_to_phys(s->page) + s->offset; if (nonforced_iommu(dev, addr, s->length)) { - addr = pci_map_area(dev, addr, s->length, dir); + addr = dma_map_area(dev, addr, s->length, dir, 0); if (addr == bad_dma_address) { if (i > 0) - pci_unmap_sg(dev, sg, i, dir); + dma_unmap_sg(dev, sg, i, dir); nents = 0; sg[0].dma_length = 0; break; @@ -414,7 +443,7 @@ static int pci_map_sg_nonforce(struct pc } /* Map multiple scatterlist entries continuous into the first. */ -static int __pci_map_cont(struct scatterlist *sg, int start, int stopat, +static int __dma_map_cont(struct scatterlist *sg, int start, int stopat, struct scatterlist *sout, unsigned long pages) { unsigned long iommu_start = alloc_iommu(pages); @@ -452,7 +481,7 @@ static int __pci_map_cont(struct scatter return 0; } -static inline int pci_map_cont(struct scatterlist *sg, int start, int stopat, +static inline int dma_map_cont(struct scatterlist *sg, int start, int stopat, struct scatterlist *sout, unsigned long pages, int need) { @@ -462,14 +491,14 @@ static inline int pci_map_cont(struct sc sout->dma_length = sg[start].length; return 0; } - return __pci_map_cont(sg, start, stopat, sout, pages); + return __dma_map_cont(sg, start, stopat, sout, pages); } /* * DMA map all entries in a scatterlist. * Merge chunks that have page aligned sizes into a continuous mapping. - */ -int pci_map_sg(struct pci_dev *dev, struct scatterlist *sg, int nents, int dir) + */ +int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, int dir) { int i; int out; @@ -477,19 +506,14 @@ int pci_map_sg(struct pci_dev *dev, stru unsigned long pages = 0; int need = 0, nextneed; -#ifdef CONFIG_SWIOTLB - if (swiotlb) - return swiotlb_map_sg(&dev->dev,sg,nents,dir); -#endif - - BUG_ON(dir == PCI_DMA_NONE); + BUG_ON(dir == DMA_NONE); if (nents == 0) return 0; -#ifdef CONFIG_SWIOTLB if (swiotlb) - return swiotlb_map_sg(&dev->dev,sg,nents,dir); -#endif + return swiotlb_map_sg(dev,sg,nents,dir); + if (!dev) + dev = &fallback_dev; out = 0; start = 0; @@ -508,19 +532,19 @@ int pci_map_sg(struct pci_dev *dev, stru boundary and the new one doesn't have an offset. */ if (!iommu_merge || !nextneed || !need || s->offset || (ps->offset + ps->length) % PAGE_SIZE) { - if (pci_map_cont(sg, start, i, sg+out, pages, + if (dma_map_cont(sg, start, i, sg+out, pages, need) < 0) goto error; out++; pages = 0; start = i; } - } + } need = nextneed; pages += to_pages(s->offset, s->length); } - if (pci_map_cont(sg, start, i, sg+out, pages, need) < 0) + if (dma_map_cont(sg, start, i, sg+out, pages, need) < 0) goto error; out++; flush_gart(dev); @@ -530,34 +554,32 @@ int pci_map_sg(struct pci_dev *dev, stru error: flush_gart(NULL); - pci_unmap_sg(dev, sg, nents, dir); + dma_unmap_sg(dev, sg, nents, dir); /* When it was forced try again unforced */ if (force_iommu) - return pci_map_sg_nonforce(dev, sg, nents, dir); + return dma_map_sg_nonforce(dev, sg, nents, dir); if (panic_on_overflow) - panic("pci_map_sg: overflow on %lu pages\n", pages); - iommu_full(dev, pages << PAGE_SHIFT, dir); + panic("dma_map_sg: overflow on %lu pages\n", pages); + iommu_full(dev, pages << PAGE_SHIFT, dir, 0); for (i = 0; i < nents; i++) sg[i].dma_address = bad_dma_address; return 0; } /* - * Free a PCI mapping. + * Free a DMA mapping. */ -void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, +void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, int direction) { unsigned long iommu_page; int npages; int i; -#ifdef CONFIG_SWIOTLB if (swiotlb) { - swiotlb_unmap_single(&hwdev->dev,dma_addr,size,direction); + swiotlb_unmap_single(dev,dma_addr,size,direction); return; } -#endif if (dma_addr < iommu_bus_base + EMERGENCY_PAGES*PAGE_SIZE || dma_addr >= iommu_bus_base + iommu_size) @@ -574,22 +596,25 @@ void pci_unmap_single(struct pci_dev *hw /* * Wrapper for pci_unmap_single working with scatterlists. */ -void pci_unmap_sg(struct pci_dev *dev, struct scatterlist *sg, int nents, - int dir) +void dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, int dir) { int i; + if (swiotlb) { + swiotlb_unmap_sg(dev,sg,nents,dir); + return; + } for (i = 0; i < nents; i++) { struct scatterlist *s = &sg[i]; if (!s->dma_length || !s->length) break; - pci_unmap_single(dev, s->dma_address, s->dma_length, dir); + dma_unmap_single(dev, s->dma_address, s->dma_length, dir); } } -int pci_dma_supported(struct pci_dev *dev, u64 mask) +int dma_supported(struct device *dev, u64 mask) { /* Copied from i386. Doesn't make much sense, because it will - only work for pci_alloc_consistent. + only work for pci_alloc_coherent. The caller just has to use GFP_DMA in this case. */ if (mask < 0x00ffffff) return 0; @@ -605,22 +630,31 @@ int pci_dma_supported(struct pci_dev *de Assume all masks <= 40 bits are of this type. Normally this doesn't make any difference, but gives more gentle handling of IOMMU overflow. */ if (iommu_sac_force && (mask >= 0xffffffffffULL)) { - printk(KERN_INFO "%s: Force SAC with mask %Lx\n", dev->slot_name,mask); + printk(KERN_INFO "%s: Force SAC with mask %Lx\n", dev->bus_id,mask); return 0; } return 1; } -EXPORT_SYMBOL(pci_unmap_sg); -EXPORT_SYMBOL(pci_map_sg); -EXPORT_SYMBOL(pci_map_single); -EXPORT_SYMBOL(pci_unmap_single); -EXPORT_SYMBOL(pci_dma_supported); +int dma_get_cache_alignment(void) +{ + return boot_cpu_data.x86_clflush_size; +} + +EXPORT_SYMBOL(dma_unmap_sg); +EXPORT_SYMBOL(dma_map_sg); +EXPORT_SYMBOL(dma_map_single); +EXPORT_SYMBOL(dma_unmap_single); +EXPORT_SYMBOL(dma_supported); EXPORT_SYMBOL(no_iommu); EXPORT_SYMBOL(force_iommu); EXPORT_SYMBOL(bad_dma_address); -EXPORT_SYMBOL(iommu_merge); +EXPORT_SYMBOL(iommu_bio_merge); +EXPORT_SYMBOL(iommu_sac_force); +EXPORT_SYMBOL(dma_get_cache_alignment); +EXPORT_SYMBOL(dma_alloc_coherent); +EXPORT_SYMBOL(dma_free_coherent); static __init unsigned long check_iommu_size(unsigned long aper, u64 aper_size) { @@ -747,7 +781,7 @@ static int __init pci_iommu_init(void) if (swiotlb) { no_iommu = 1; - printk(KERN_INFO "PCI-DMA: Using software bounce buffering for IO (SWIOTLB)\n"); + printk(KERN_INFO "PCI-DMA: Using software bounce buffering for IO (SWIOTLB)\n"); return -1; } @@ -851,7 +885,7 @@ static int __init pci_iommu_init(void) fs_initcall(pci_iommu_init); /* iommu=[size][,noagp][,off][,force][,noforce][,leak][,memaper[=order]][,merge] - [,forcesac][,fullflush][,nomerge] + [,forcesac][,fullflush][,nomerge][,biomerge] size set size of iommu (in bytes) noagp don't initialize the AGP driver and use full aperture. off don't use the IOMMU @@ -859,60 +893,73 @@ fs_initcall(pci_iommu_init); memaper[=order] allocate an own aperture over RAM with size 32MB^order. noforce don't force IOMMU usage. Default. force Force IOMMU. - merge Do SG merging. Implies force (experimental) + merge Do lazy merging. This may improve performance on some block devices. + Implies force (experimental) + biomerge Do merging at the BIO layer. This is more efficient than merge, + but should be only done with very big IOMMUs. Implies merge,force. nomerge Don't do SG merging. forcesac For SAC mode for masks <40bits (experimental) fullflush Flush IOMMU on each allocation (default) nofullflush Don't use IOMMU fullflush allowed overwrite iommu off workarounds for specific chipsets. soft Use software bounce buffering (default for Intel machines) + noaperture Don't touch the aperture for AGP. */ -__init int iommu_setup(char *opt) +__init int iommu_setup(char *p) { int arg; - char *p = opt; - - for (;;) { - if (!memcmp(p,"noagp", 5)) + + while (*p) { + if (!strncmp(p,"noagp",5)) no_agp = 1; - if (!memcmp(p,"off", 3)) + if (!strncmp(p,"off",3)) no_iommu = 1; - if (!memcmp(p,"force", 5)) { + if (!strncmp(p,"force",5)) { force_iommu = 1; iommu_aperture_allowed = 1; } - if (!memcmp(p,"allowed",7)) + if (!strncmp(p,"allowed",7)) iommu_aperture_allowed = 1; - if (!memcmp(p,"noforce", 7)) { + if (!strncmp(p,"noforce",7)) { iommu_merge = 0; force_iommu = 0; } - if (!memcmp(p, "memaper", 7)) { + if (!strncmp(p, "memaper", 7)) { fallback_aper_force = 1; p += 7; - if (*p == '=' && get_option(&p, &arg)) - fallback_aper_order = arg; + if (*p == '=') { + ++p; + if (get_option(&p, &arg)) + fallback_aper_order = arg; + } } - if (!memcmp(p, "panic", 5)) + if (!strncmp(p, "biomerge",8)) { + iommu_bio_merge = 4096; + iommu_merge = 1; + force_iommu = 1; + } + if (!strncmp(p, "panic",5)) panic_on_overflow = 1; - if (!memcmp(p, "nopanic", 7)) + if (!strncmp(p, "nopanic",7)) panic_on_overflow = 0; - if (!memcmp(p, "merge", 5)) { + if (!strncmp(p, "merge",5)) { iommu_merge = 1; force_iommu = 1; } - if (!memcmp(p, "nomerge", 7)) + if (!strncmp(p, "nomerge",7)) iommu_merge = 0; - if (!memcmp(p, "forcesac", 8)) + if (!strncmp(p, "forcesac",8)) iommu_sac_force = 1; - if (!memcmp(p, "fullflush", 9)) + if (!strncmp(p, "fullflush",8)) iommu_fullflush = 1; - if (!memcmp(p, "nofullflush", 11)) + if (!strncmp(p, "nofullflush",11)) iommu_fullflush = 0; - if (!memcmp(p, "soft", 4)) + if (!strncmp(p, "soft",4)) swiotlb = 1; + if (!strncmp(p, "noaperture",10)) + fix_aperture = 0; #ifdef CONFIG_IOMMU_LEAK - if (!memcmp(p,"leak", 4)) { + if (!strncmp(p,"leak",4)) { leak_trace = 1; p += 4; if (*p == '=') ++p; @@ -922,10 +969,9 @@ __init int iommu_setup(char *opt) #endif if (isdigit(*p) && get_option(&p, &arg)) iommu_size = arg; - do { - if (*p == ' ' || *p == 0) - return 0; - } while (*p++ != ','); + p += strcspn(p, ","); + if (*p == ',') + ++p; } return 1; } --- linux-2.6.9-rc1/arch/x86_64/kernel/pci-nommu.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/arch/x86_64/kernel/pci-nommu.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,8 +1,11 @@ +/* Fallback functions when the main IOMMU code is not compiled in. This + code is roughly equivalent to i386. */ #include #include #include #include #include +#include int iommu_merge = 0; EXPORT_SYMBOL(iommu_merge); @@ -10,57 +13,80 @@ EXPORT_SYMBOL(iommu_merge); dma_addr_t bad_dma_address; EXPORT_SYMBOL(bad_dma_address); +int iommu_bio_merge = 0; +EXPORT_SYMBOL(iommu_bio_merge); + +int iommu_sac_force = 0; +EXPORT_SYMBOL(iommu_sac_force); + /* * Dummy IO MMU functions */ -void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, - dma_addr_t *dma_handle) +void *dma_alloc_coherent(struct device *hwdev, size_t size, + dma_addr_t *dma_handle, unsigned gfp) { void *ret; - int gfp = GFP_ATOMIC; - - if (hwdev == NULL || - end_pfn > (hwdev->dma_mask>>PAGE_SHIFT) || /* XXX */ - (u32)hwdev->dma_mask < 0xffffffff) - gfp |= GFP_DMA; - ret = (void *)__get_free_pages(gfp, get_order(size)); + u64 mask; + int order = get_order(size); - if (ret != NULL) { - memset(ret, 0, size); + if (hwdev) + mask = hwdev->coherent_dma_mask & *hwdev->dma_mask; + else + mask = 0xffffffff; + for (;;) { + ret = (void *)__get_free_pages(gfp, order); + if (ret == NULL) + return NULL; *dma_handle = virt_to_bus(ret); + if ((*dma_handle & ~mask) == 0) + break; + free_pages((unsigned long)ret, order); + if (gfp & GFP_DMA) + return NULL; + gfp |= GFP_DMA; } + + memset(ret, 0, size); return ret; } +EXPORT_SYMBOL(dma_alloc_coherent); -void pci_free_consistent(struct pci_dev *hwdev, size_t size, +void dma_free_coherent(struct device *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle) { free_pages((unsigned long)vaddr, get_order(size)); } +EXPORT_SYMBOL(dma_free_coherent); -int pci_dma_supported(struct pci_dev *hwdev, u64 mask) +int dma_supported(struct device *hwdev, u64 mask) { /* * we fall back to GFP_DMA when the mask isn't all 1s, * so we can't guarantee allocations that must be * within a tighter range than GFP_DMA.. - * RED-PEN this won't work for pci_map_single. Caller has to - * use GFP_DMA in the first place. + * RED-PEN this won't work for pci_map_single. Caller has to + * use GFP_DMA in the first place. */ if (mask < 0x00ffffff) return 0; return 1; } +EXPORT_SYMBOL(dma_supported); -EXPORT_SYMBOL(pci_dma_supported); +int dma_get_cache_alignment(void) +{ + return boot_cpu_data.x86_clflush_size; +} +EXPORT_SYMBOL(dma_get_cache_alignment); static int __init check_ram(void) { if (end_pfn >= 0xffffffff>>PAGE_SHIFT) { - printk(KERN_ERR "WARNING more than 4GB of memory but no IOMMU.\n" - KERN_ERR "WARNING 32bit PCI may malfunction.\n"); + printk( + KERN_ERR "WARNING more than 4GB of memory but IOMMU not compiled in.\n" + KERN_ERR "WARNING 32bit PCI may malfunction.\n"); } return 0; } --- linux-2.6.9-rc1/arch/x86_64/kernel/process.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/arch/x86_64/kernel/process.c 2004-09-03 00:56:46.655068000 -0700 @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -168,9 +169,7 @@ void __init select_idle_routine(const st if (cpu_has(c, X86_FEATURE_MWAIT)) { /* * Skip, if setup has overridden idle. - * Also, take care of system with asymmetric CPUs. - * Use, mwait_idle only if all cpus support it. - * If not, we fallback to default_idle() + * One CPU supports mwait => All CPUs supports mwait */ if (!pm_idle) { if (!printed) { @@ -179,10 +178,7 @@ void __init select_idle_routine(const st } pm_idle = mwait_idle; } - return; } - pm_idle = default_idle; - return; } static int __init idle_setup (char *str) @@ -262,6 +258,7 @@ void exit_thread(void) tss->io_bitmap_base = INVALID_IO_BITMAP_OFFSET; put_cpu(); } + perfctr_exit_thread(&me->thread); } void flush_thread(void) @@ -365,6 +362,8 @@ int copy_thread(int nr, unsigned long cl asm("movl %%es,%0" : "=m" (p->thread.es)); asm("movl %%ds,%0" : "=m" (p->thread.ds)); + perfctr_copy_task(p, regs); + if (unlikely(me->thread.io_bitmap_ptr != NULL)) { p->thread.io_bitmap_ptr = kmalloc(IO_BITMAP_BYTES, GFP_KERNEL); if (!p->thread.io_bitmap_ptr) @@ -411,6 +410,8 @@ struct task_struct *__switch_to(struct t int cpu = smp_processor_id(); struct tss_struct *tss = init_tss + cpu; + perfctr_suspend_thread(prev); + unlazy_fpu(prev_p); /* @@ -514,6 +515,8 @@ struct task_struct *__switch_to(struct t } } + perfctr_resume_thread(next); + return prev_p; } @@ -546,17 +549,16 @@ void set_personality_64bit(void) clear_thread_flag(TIF_IA32); } -asmlinkage long sys_fork(struct pt_regs regs) +asmlinkage long sys_fork(struct pt_regs *regs) { - return do_fork(SIGCHLD, regs.rsp, ®s, 0, NULL, NULL); + return do_fork(SIGCHLD, regs->rsp, regs, 0, NULL, NULL); } -asmlinkage long sys_clone(unsigned long clone_flags, unsigned long newsp, void __user *parent_tid, void __user *child_tid, struct pt_regs regs) +asmlinkage long sys_clone(unsigned long clone_flags, unsigned long newsp, void __user *parent_tid, void __user *child_tid, struct pt_regs *regs) { if (!newsp) - newsp = regs.rsp; - return do_fork(clone_flags & ~CLONE_IDLETASK, newsp, ®s, 0, - parent_tid, child_tid); + newsp = regs->rsp; + return do_fork(clone_flags, newsp, regs, 0, parent_tid, child_tid); } /* @@ -569,9 +571,9 @@ asmlinkage long sys_clone(unsigned long * do not have enough call-clobbered registers to hold all * the information you need. */ -asmlinkage long sys_vfork(struct pt_regs regs) +asmlinkage long sys_vfork(struct pt_regs *regs) { - return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.rsp, ®s, 0, + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->rsp, regs, 0, NULL, NULL); } --- linux-2.6.9-rc1/arch/x86_64/kernel/ptrace.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/x86_64/kernel/ptrace.c 2004-09-02 20:51:51.000000000 -0700 @@ -433,30 +433,32 @@ asmlinkage long sys_ptrace(long request, break; case PTRACE_GETREGS: { /* Get all gp regs from the child. */ - if (!access_ok(VERIFY_WRITE, (unsigned __user *)data, FRAME_SIZE)) { + if (!access_ok(VERIFY_WRITE, (unsigned __user *)data, + sizeof(struct user_regs_struct))) { ret = -EIO; break; } + ret = 0; for (ui = 0; ui < sizeof(struct user_regs_struct); ui += sizeof(long)) { - __put_user(getreg(child, ui),(unsigned long __user *) data); + ret |= __put_user(getreg(child, ui),(unsigned long __user *) data); data += sizeof(long); } - ret = 0; break; } case PTRACE_SETREGS: { /* Set all gp regs in the child. */ unsigned long tmp; - if (!access_ok(VERIFY_READ, (unsigned __user *)data, FRAME_SIZE)) { + if (!access_ok(VERIFY_READ, (unsigned __user *)data, + sizeof(struct user_regs_struct))) { ret = -EIO; break; } + ret = 0; for (ui = 0; ui < sizeof(struct user_regs_struct); ui += sizeof(long)) { - __get_user(tmp, (unsigned long __user *) data); + ret |= __get_user(tmp, (unsigned long __user *) data); putreg(child, ui, tmp); data += sizeof(long); } - ret = 0; break; } --- linux-2.6.9-rc1/arch/x86_64/kernel/reboot.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/x86_64/kernel/reboot.c 2004-09-03 00:56:58.766226824 -0700 @@ -91,31 +91,6 @@ static void reboot_warm(void) [target] "b" (WARMBOOT_TRAMP)); } -#ifdef CONFIG_SMP -static void smp_halt(void) -{ - int cpuid = safe_smp_processor_id(); - static int first_entry = 1; - - if (first_entry) { - first_entry = 0; - smp_call_function((void *)machine_restart, NULL, 1, 0); - } - - smp_stop_cpu(); - - /* AP calling this. Just halt */ - if (cpuid != boot_cpu_id) { - for (;;) - asm("hlt"); - } - - /* Wait for all other CPUs to have run smp_stop_cpu */ - while (!cpus_empty(cpu_online_map)) - rep_nop(); -} -#endif - static inline void kb_wait(void) { int i; @@ -125,23 +100,45 @@ static inline void kb_wait(void) break; } -void machine_restart(char * __unused) +void machine_shutdown(void) { - int i; - + /* Stop the cpus and apics */ #ifdef CONFIG_SMP - smp_halt(); + int reboot_cpu_id; + + /* The boot cpu is always logical cpu 0 */ + reboot_cpu_id = 0; + + /* Make certain the cpu I'm about to reboot on is online */ + if (!cpu_isset(reboot_cpu_id, cpu_online_map)) { + reboot_cpu_id = smp_processor_id(); + } + + /* Make certain I only run on the appropriate processor */ + set_cpus_allowed(current, cpumask_of_cpu(reboot_cpu_id)); + + /* O.K Now that I'm on the appropriate processor, + * stop all of the others. + */ + smp_send_stop(); #endif local_irq_disable(); - + #ifndef CONFIG_SMP disable_local_APIC(); #endif disable_IO_APIC(); - + local_irq_enable(); +} + +void machine_restart(char * __unused) +{ + int i; + + machine_shutdown(); /* Tell the BIOS if we want cold or warm reboot */ *((unsigned short *)__va(0x472)) = reboot_mode; --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/x86_64/kernel/relocate_kernel.S 2004-09-03 00:56:58.897206912 -0700 @@ -0,0 +1,141 @@ +/* + * relocate_kernel.S - put the kernel image in place to boot + * Copyright (C) 2002-2004 Eric Biederman + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include + + /* + * Must be relocatable PIC code callable as a C function, that once + * it starts can not use the previous processes stack. + */ + .globl relocate_new_kernel + .code64 +relocate_new_kernel: + /* %rdi indirection_page + * %rsi reboot_code_buffer + * %rdx start address + * %rcx page_table + * %r8 arg5 + * %r9 arg6 + */ + + /* zero out flags, and disable interrupts */ + pushq $0 + popfq + + /* set a new stack at the bottom of our page... */ + lea 4096(%rsi), %rsp + + /* store the parameters back on the stack */ + pushq %rdx /* store the start address */ + + /* Set cr0 to a known state: + * 31 1 == Paging enabled + * 18 0 == Alignment check disabled + * 16 0 == Write protect disabled + * 3 0 == No task switch + * 2 0 == Don't do FP software emulation. + * 0 1 == Proctected mode enabled + */ + movq %cr0, %rax + andq $~((1<<18)|(1<<16)|(1<<3)|(1<<2)), %rax + orl $((1<<31)|(1<<0)), %eax + movq %rax, %cr0 + + /* Set cr4 to a known state: + * 10 0 == xmm exceptions disabled + * 9 0 == xmm registers instructions disabled + * 8 0 == performance monitoring counter disabled + * 7 0 == page global disabled + * 6 0 == machine check exceptions disabled + * 5 1 == physical address extension enabled + * 4 0 == page size extensions disabled + * 3 0 == Debug extensions disabled + * 2 0 == Time stamp disable (disabled) + * 1 0 == Protected mode virtual interrupts disabled + * 0 0 == VME disabled + */ + + movq $((1<<5)), %rax + movq %rax, %cr4 + + jmp 1f +1: + + /* Switch to the identity mapped page tables, + * and flush the TLB. + */ + movq %rcx, %cr3 + + /* Do the copies */ + movq %rdi, %rbx /* Put the indirection page in %rbx */ + xorq %rdi, %rdi + xorq %rsi, %rsi + +0: /* top, read another word for the indirection page */ + + movq (%rbx), %rcx + addq $8, %rbx + testq $0x1, %rcx /* is it a destination page? */ + jz 1f + movq %rcx, %rdi + andq $0xfffffffffffff000, %rdi + jmp 0b +1: + testq $0x2, %rcx /* is it an indirection page? */ + jz 1f + movq %rcx, %rbx + andq $0xfffffffffffff000, %rbx + jmp 0b +1: + testq $0x4, %rcx /* is it the done indicator? */ + jz 1f + jmp 2f +1: + testq $0x8, %rcx /* is it the source indicator? */ + jz 0b /* Ignore it otherwise */ + movq %rcx, %rsi /* For ever source page do a copy */ + andq $0xfffffffffffff000, %rsi + + movq $512, %rcx + rep ; movsq + jmp 0b +2: + + /* To be certain of avoiding problems with self-modifying code + * I need to execute a serializing instruction here. + * So I flush the TLB by reloading %cr3 here, it's handy, + * and not processor dependent. + */ + movq %cr3, %rax + movq %rax, %cr3 + + /* set all of the registers to known values */ + /* leave %rsp alone */ + + xorq %rax, %rax + xorq %rbx, %rbx + xorq %rcx, %rcx + xorq %rdx, %rdx + xorq %rsi, %rsi + xorq %rdi, %rdi + xorq %rbp, %rbp + xorq %r8, %r8 + xorq %r9, %r9 + xorq %r10, %r9 + xorq %r11, %r11 + xorq %r12, %r12 + xorq %r13, %r13 + xorq %r14, %r14 + xorq %r15, %r15 + + ret +relocate_new_kernel_end: + + .globl relocate_new_kernel_size +relocate_new_kernel_size: + .quad relocate_new_kernel_end - relocate_new_kernel --- linux-2.6.9-rc1/arch/x86_64/kernel/setup64.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/arch/x86_64/kernel/setup64.c 2004-09-02 20:51:51.000000000 -0700 @@ -60,12 +60,12 @@ noforce (default) Don't enable by defaul */ static int __init nonx_setup(char *str) { - if (!strncmp(str, "on",3)) { + if (!strcmp(str, "on")) { __supported_pte_mask |= _PAGE_NX; do_not_nx = 0; vm_data_default_flags &= ~VM_EXEC; vm_stack_flags &= ~VM_EXEC; - } else if (!strncmp(str, "noforce",7) || !strncmp(str,"off",3)) { + } else if (!strcmp(str, "noforce") || !strcmp(str, "off")) { do_not_nx = (str[0] == 'o'); if (do_not_nx) __supported_pte_mask &= ~_PAGE_NX; @@ -91,26 +91,28 @@ Valid options: compat (default) Imply PROT_EXEC for PROT_READ */ - static int __init nonx32_setup(char *str) + static int __init nonx32_setup(char *s) { - char *s; - while ((s = strsep(&str, ",")) != NULL) { - if (!strcmp(s, "all") || !strcmp(s,"on")) { + while (*s) { + if (!strncmp(s, "all", 3) || !strncmp(s,"on",2)) { vm_data_default_flags32 &= ~VM_EXEC; vm_stack_flags32 &= ~VM_EXEC; - } else if (!strcmp(s, "off")) { + } else if (!strncmp(s, "off",3)) { vm_data_default_flags32 |= VM_EXEC; vm_stack_flags32 |= VM_EXEC; - } else if (!strcmp(s, "stack")) { + } else if (!strncmp(s, "stack", 5)) { vm_data_default_flags32 |= VM_EXEC; vm_stack_flags32 &= ~VM_EXEC; - } else if (!strcmp(s, "force")) { + } else if (!strncmp(s, "force",5)) { vm_force_exec32 = 0; - } else if (!strcmp(s, "compat")) { + } else if (!strncmp(s, "compat",5)) { vm_force_exec32 = PROT_EXEC; } - } - return 1; + s += strcspn(s, ","); + if (*s == ',') + ++s; + } + return 1; } __setup("noexec32=", nonx32_setup); --- linux-2.6.9-rc1/arch/x86_64/kernel/setup.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/arch/x86_64/kernel/setup.c 2004-09-03 00:56:40.252041408 -0700 @@ -71,7 +71,7 @@ EXPORT_SYMBOL(acpi_disabled); #ifdef CONFIG_ACPI_BOOT extern int __initdata acpi_ht; extern acpi_interrupt_flags acpi_sci_flags; -/* int __initdata acpi_force = 0; */ +int __initdata acpi_force = 0; #endif /* For PCI or other memory-mapped resources */ @@ -79,8 +79,10 @@ unsigned long pci_mem_start = 0x10000000 unsigned long saved_video_mode; +#ifdef CONFIG_SWIOTLB int swiotlb; EXPORT_SYMBOL(swiotlb); +#endif /* * Setup options @@ -248,14 +250,14 @@ static __init void parse_cmdline_early ( if (!memcmp(from, "acpi=force", 10)) { /* add later when we do DMI horrors: */ - /* acpi_force = 1; */ + acpi_force = 1; acpi_disabled = 0; } /* acpi=ht just means: do ACPI MADT parsing at bootup, but don't enable the full ACPI interpreter */ if (!memcmp(from, "acpi=ht", 7)) { - /* if (!acpi_force) */ + if (!acpi_force) disable_acpi(); acpi_ht = 1; } @@ -679,6 +681,26 @@ static int __init init_amd(struct cpuinf } } display_cacheinfo(c); + + if (c->cpuid_level >= 0x80000008) { + c->x86_num_cores = (cpuid_ecx(0x80000008) & 0xff) + 1; + if (c->x86_num_cores & (c->x86_num_cores - 1)) + c->x86_num_cores = 1; + +#ifdef CONFIG_NUMA + /* On a dual core setup the lower bits of apic id + distingush the cores. Fix up the CPU<->node mappings + here based on that. + Assumes number of cores is a power of two. */ + if (c->x86_num_cores > 1) { + int cpu = c->x86_apicid; + cpu_to_node[cpu] = cpu >> hweight32(c->x86_num_cores - 1); + printk(KERN_INFO "CPU %d -> Node %d\n", + cpu, cpu_to_node[cpu]); + } +#endif + } + return r; } @@ -765,6 +787,7 @@ static struct _cache_table cache_table[] { 0x43, LVL_2, 512 }, { 0x44, LVL_2, 1024 }, { 0x45, LVL_2, 2048 }, + { 0x60, LVL_1_DATA, 16 }, { 0x66, LVL_1_DATA, 8 }, { 0x67, LVL_1_DATA, 16 }, { 0x68, LVL_1_DATA, 32 }, @@ -902,6 +925,8 @@ void __init early_identify_cpu(struct cp c->x86_model_id[0] = '\0'; /* Unset */ c->x86_clflush_size = 64; c->x86_cache_alignment = c->x86_clflush_size; + c->x86_num_cores = 1; + c->x86_apicid = c == &boot_cpu_data ? 0 : c - cpu_data; memset(&c->x86_capability, 0, sizeof c->x86_capability); /* Get vendor name */ @@ -929,6 +954,7 @@ void __init early_identify_cpu(struct cp } if (c->x86_capability[0] & (1<<19)) c->x86_clflush_size = ((misc >> 8) & 0xff) * 8; + c->x86_apicid = misc >> 24; } else { /* Have CPUID level 0 only - unheard of */ c->x86 = 4; @@ -948,8 +974,10 @@ void __init identify_cpu(struct cpuinfo_ /* AMD-defined flags: level 0x80000001 */ xlvl = cpuid_eax(0x80000000); if ( (xlvl & 0xffff0000) == 0x80000000 ) { - if ( xlvl >= 0x80000001 ) + if ( xlvl >= 0x80000001 ) { c->x86_capability[1] = cpuid_edx(0x80000001); + c->x86_capability[5] = cpuid_ecx(0x80000001); + } if ( xlvl >= 0x80000004 ) get_model_name(c); /* Default name */ } @@ -1059,8 +1087,8 @@ static int show_cpuinfo(struct seq_file NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* Intel-defined (#2) */ - "pni", NULL, NULL, "monitor", "ds_cpl", NULL, NULL, "tm2", - "est", NULL, "cid", NULL, NULL, "cmpxchg16b", NULL, NULL, + "pni", NULL, NULL, "monitor", "ds_cpl", NULL, NULL, "est", + "tm2", NULL, "cid", NULL, NULL, "cx16", "xtpr", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }; @@ -1149,6 +1177,9 @@ static int show_cpuinfo(struct seq_file } } + if (c->x86_num_cores > 1) + seq_printf(m, "cpu cores\t: %d\n", c->x86_num_cores); + seq_printf(m, "\n\n"); return 0; --- linux-2.6.9-rc1/arch/x86_64/kernel/signal.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/arch/x86_64/kernel/signal.c 2004-09-03 00:57:12.707107488 -0700 @@ -40,7 +40,7 @@ void ia32_setup_frame(int sig, struct k_ sigset_t *set, struct pt_regs * regs); asmlinkage long -sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize, struct pt_regs regs) +sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize, struct pt_regs *regs) { sigset_t saveset, newset; @@ -59,21 +59,22 @@ sys_rt_sigsuspend(sigset_t __user *unews spin_unlock_irq(¤t->sighand->siglock); #ifdef DEBUG_SIG printk("rt_sigsuspend savset(%lx) newset(%lx) regs(%p) rip(%lx)\n", - saveset, newset, ®s, regs.rip); + saveset, newset, regs, regs->rip); #endif - regs.rax = -EINTR; + regs->rax = -EINTR; while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); - if (do_signal(®s, &saveset)) + if (do_signal(regs, &saveset)) return -EINTR; } } asmlinkage long -sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, struct pt_regs regs) +sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, + struct pt_regs *regs) { - return do_sigaltstack(uss, uoss, regs.rsp); + return do_sigaltstack(uss, uoss, regs->rsp); } @@ -134,13 +135,13 @@ badframe: return 1; } -asmlinkage long sys_rt_sigreturn(struct pt_regs regs) +asmlinkage long sys_rt_sigreturn(struct pt_regs *regs) { struct rt_sigframe __user *frame; sigset_t set; long eax; - frame = (struct rt_sigframe __user *)(regs.rsp - 8); + frame = (struct rt_sigframe __user *)(regs->rsp - 8); if (verify_area(VERIFY_READ, frame, sizeof(*frame))) { goto badframe; } @@ -154,7 +155,7 @@ asmlinkage long sys_rt_sigreturn(struct recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - if (restore_sigcontext(®s, &frame->uc.uc_mcontext, &eax)) { + if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &eax)) { goto badframe; } @@ -162,13 +163,13 @@ asmlinkage long sys_rt_sigreturn(struct printk("%d sigreturn rip:%lx rsp:%lx frame:%p rax:%lx\n",current->pid,regs.rip,regs.rsp,frame,eax); #endif - if (do_sigaltstack(&frame->uc.uc_stack, NULL, regs.rsp) == -EFAULT) + if (do_sigaltstack(&frame->uc.uc_stack, NULL, regs->rsp) == -EFAULT) goto badframe; return eax; badframe: - signal_fault(®s,frame,"sigreturn"); + signal_fault(regs,frame,"sigreturn"); return 0; } @@ -287,7 +288,7 @@ static void setup_rt_frame(int sig, stru if (ka->sa.sa_flags & SA_RESTORER) { err |= __put_user(ka->sa.sa_restorer, &frame->pretcode); } else { - printk("%s forgot to set SA_RESTORER for signal %d.\n", me->comm, sig); + /* could use a vstub here */ goto give_sigsegv; } @@ -318,7 +319,13 @@ static void setup_rt_frame(int sig, stru regs->rsp = (unsigned long)frame; set_fs(USER_DS); - regs->eflags &= ~TF_MASK; + if (regs->eflags & TF_MASK) { + if (current->ptrace & PT_PTRACED) { + ptrace_notify(SIGTRAP); + } else { + regs->eflags &= ~TF_MASK; + } + } #ifdef DEBUG_SIG printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", @@ -328,9 +335,7 @@ static void setup_rt_frame(int sig, stru return; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - signal_fault(regs,frame,"signal deliver"); + force_sigsegv(sig, current); } /* @@ -338,11 +343,9 @@ give_sigsegv: */ static void -handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset, - struct pt_regs * regs) +handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, + sigset_t *oldset, struct pt_regs *regs) { - struct k_sigaction *ka = ¤t->sighand->action[sig-1]; - #ifdef DEBUG_SIG printk("handle_signal pid:%d sig:%lu rip:%lx rsp:%lx regs=%p\n", current->pid, sig, regs->rip, regs->rsp, regs); @@ -379,9 +382,6 @@ handle_signal(unsigned long sig, siginfo #endif setup_rt_frame(sig, ka, info, oldset, regs); - if (ka->sa.sa_flags & SA_ONESHOT) - ka->sa.sa_handler = SIG_DFL; - if (!(ka->sa.sa_flags & SA_NODEFER)) { spin_lock_irq(¤t->sighand->siglock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); @@ -398,6 +398,7 @@ handle_signal(unsigned long sig, siginfo */ int do_signal(struct pt_regs *regs, sigset_t *oldset) { + struct k_sigaction ka; siginfo_t info; int signr; @@ -419,7 +420,7 @@ int do_signal(struct pt_regs *regs, sigs if (!oldset) oldset = ¤t->blocked; - signr = get_signal_to_deliver(&info, regs, NULL); + signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (signr > 0) { /* Reenable any watchpoints before delivering the * signal to user space. The processor register will @@ -430,7 +431,7 @@ int do_signal(struct pt_regs *regs, sigs asm volatile("movq %0,%%db7" : : "r" (current->thread.debugreg7)); /* Whee! Actually deliver the signal. */ - handle_signal(signr, &info, oldset, regs); + handle_signal(signr, &info, &ka, oldset, regs); return 1; } --- linux-2.6.9-rc1/arch/x86_64/kernel/smpboot.c 2004-08-24 00:03:49.000000000 -0700 +++ 25/arch/x86_64/kernel/smpboot.c 2004-09-02 20:51:51.000000000 -0700 @@ -392,16 +392,6 @@ void __init start_secondary(void) extern volatile unsigned long init_rsp; extern void (*initial_code)(void); -static struct task_struct * __init fork_by_hand(void) -{ - struct pt_regs regs; - /* - * don't care about the eip and regs settings since - * we'll never reschedule the forked task. - */ - return copy_process(CLONE_VM|CLONE_IDLETASK, 0, ®s, 0, NULL, NULL); -} - #if APIC_DEBUG static inline void inquire_remote_apic(int apicid) { @@ -575,20 +565,11 @@ static void __init do_boot_cpu (int apic * We can't use kernel_thread since we must avoid to * reschedule the child. */ - idle = fork_by_hand(); + idle = fork_idle(cpu); if (IS_ERR(idle)) panic("failed fork for CPU %d", cpu); - wake_up_forked_process(idle); x86_cpu_to_apicid[cpu] = apicid; - /* - * We remove it from the pidhash and the runqueue - * once we got the process: - */ - init_idle(idle,cpu); - - unhash_process(idle); - cpu_pda[cpu].pcurrent = idle; start_rip = setup_trampoline(); --- linux-2.6.9-rc1/arch/x86_64/kernel/smp.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/arch/x86_64/kernel/smp.c 2004-09-03 00:56:36.975539512 -0700 @@ -362,6 +362,18 @@ void smp_send_reschedule(int cpu) send_IPI_mask(cpumask_of_cpu(cpu), RESCHEDULE_VECTOR); } +#ifdef CONFIG_KGDB +/* + * By using the NMI code instead of a vector we just sneak thru the + * word generator coming out with just what we want. AND it does + * not matter if clustered_apic_mode is set or not. + */ +void smp_send_nmi_allbutself(void) +{ + send_IPI_allbutself(APIC_DM_NMI); +} +#endif + /* * Structure and data for smp_call_function(). This is designed to minimise * static memory requirements. It also looks cleaner. --- linux-2.6.9-rc1/arch/x86_64/kernel/suspend_asm.S 2004-08-24 00:03:32.000000000 -0700 +++ 25/arch/x86_64/kernel/suspend_asm.S 2004-09-03 00:57:09.518592216 -0700 @@ -1,22 +1,18 @@ -/* originally gcc generated, but now changed. don't overwrite. */ +/* Originally gcc generated, modified by hand + * + * This may not use any stack, nor any variable that is not "NoSave": + * + * Its rewriting one kernel image with another. What is stack in "old" + * image could very well be data page in "new" image, and overwriting + * your own stack under you is bad idea. + */ .text #include #include #include -/* Input: - * rdi resume flag - */ - -ENTRY(do_magic) -.LFB5: - subq $8, %rsp -.LCFI2: - testl %edi, %edi - jne .L90 - call do_magic_suspend_1 - call save_processor_state +ENTRY(swsusp_arch_suspend) movq %rsp, saved_context_esp(%rip) movq %rax, saved_context_eax(%rip) @@ -36,9 +32,10 @@ ENTRY(do_magic) movq %r15, saved_context_r15(%rip) pushfq ; popq saved_context_eflags(%rip) - addq $8, %rsp - jmp do_magic_suspend_2 -.L90: + call swsusp_save + ret + +ENTRY(swsusp_arch_resume) /* set up cr3 */ leaq init_level4_pgt(%rip),%rax subq $__START_KERNEL_map,%rax @@ -53,7 +50,6 @@ ENTRY(do_magic) movq %rcx, %cr3; movq %rax, %cr4; # turn PGE back on - call do_magic_resume_1 movl nr_copy_pages(%rip), %eax xorl %ecx, %ecx movq $0, loop(%rip) @@ -113,11 +109,11 @@ ENTRY(do_magic) movq saved_context_r14(%rip), %r14 movq saved_context_r15(%rip), %r15 pushq saved_context_eflags(%rip) ; popfq - call restore_processor_state - addq $8, %rsp - jmp do_magic_resume_2 + call swsusp_restore + ret - .section .data.nosave + .section .data.nosave, "aw" + .align 8 loop: .quad 0 loop2: --- linux-2.6.9-rc1/arch/x86_64/kernel/time.c 2004-08-24 00:03:17.000000000 -0700 +++ 25/arch/x86_64/kernel/time.c 2004-09-02 20:51:51.000000000 -0700 @@ -11,7 +11,6 @@ * Copyright (c) 2002 Vojtech Pavlik * Copyright (c) 2003 Andi Kleen * RTC support code taken from arch/i386/kernel/timers/time_hpet.c - * */ #include @@ -42,6 +41,10 @@ u64 jiffies_64 = INITIAL_JIFFIES; EXPORT_SYMBOL(jiffies_64); +#ifdef CONFIG_CPU_FREQ +static void cpufreq_delayed_get(void); +#endif + extern int using_apic_timer; spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; @@ -82,7 +85,7 @@ static inline void rdtscll_sync(unsigned * timer interrupt has happened already, but vxtime.trigger wasn't updated yet. * This is not a problem, because jiffies hasn't updated either. They are bound * together by xtime_lock. - */ + */ static inline unsigned int do_gettimeoffset_tsc(void) { @@ -119,7 +122,7 @@ void do_gettimeofday(struct timeval *tv) usec = xtime.tv_nsec / 1000; /* i386 does some correction here to keep the clock - monotonus even when ntpd is fixing drift. + monotonous even when ntpd is fixing drift. But they didn't work for me, there is a non monotonic clock anyways with ntp. I dropped all corrections now until a real solution can @@ -214,7 +217,7 @@ static void set_rtc_mmss(unsigned long n * overflow. This avoids messing with unknown time zones but requires your RTC * not to be off by more than 15 minutes. Since we're calling it only when * our clock is externally synchronized using NTP, this shouldn't be a problem. - */ + */ real_seconds = nowtime % 60; real_minutes = nowtime / 60; @@ -297,15 +300,15 @@ EXPORT_SYMBOL(monotonic_clock); static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { static unsigned long rtc_update = 0; - unsigned long tsc, lost = 0; - int delay, offset = 0; + unsigned long tsc; + int delay, offset = 0, lost = 0; /* * Here we are in the timer irq handler. We have irqs locally disabled (so we * don't need spin_lock_irqsave()) but we don't know if the timer_bh is running * on the other CPU, so we need a lock. We also need to lock the vsyscall * variables, because both do_timer() and us change them -arca+vojtech - */ + */ write_seqlock(&xtime_lock); @@ -354,12 +357,29 @@ static irqreturn_t timer_interrupt(int i (((long) offset << 32) / vxtime.tsc_quot) - 1; } - if (lost) { + if (lost > 0) { + static long lost_count; + if (report_lost_ticks) { - printk(KERN_WARNING "time.c: Lost %ld timer " + printk(KERN_WARNING "time.c: Lost %d timer " "tick(s)! ", lost); print_symbol("rip %s)\n", regs->rip); } + + if (lost_count == 100) { + printk(KERN_WARNING + "warning: many lost ticks.\n" + KERN_WARNING "Your time source seems to be instable or some driver is hogging interupts\n"); + print_symbol("rip %s\n", regs->rip); + lost_count = 0; + } else + lost_count++; + + if ((lost_count % 25) == 0) { +#ifdef CONFIG_CPU_FREQ + cpufreq_delayed_get(); +#endif + } jiffies += lost; } @@ -376,7 +396,7 @@ static irqreturn_t timer_interrupt(int i */ #ifndef CONFIG_X86_LOCAL_APIC - x86_do_profile(regs); + profile_tick(CPU_PROFILING, regs); #else if (!using_apic_timer) smp_local_timer_interrupt(regs); @@ -509,6 +529,34 @@ unsigned long get_cmos_time(void) Should fix up last_tsc too. Currently gettimeofday in the first tick after the change will be slightly wrong. */ +#include + +static unsigned int cpufreq_delayed_issched = 0; +static unsigned int cpufreq_init = 0; +static struct work_struct cpufreq_delayed_get_work; + +static void handle_cpufreq_delayed_get(void *v) +{ + unsigned int cpu; + for_each_online_cpu(cpu) { + cpufreq_get(cpu); + } + cpufreq_delayed_issched = 0; +} + +/* if we notice lost ticks, schedule a call to cpufreq_get() as it tries + * to verify the CPU frequency the timing core thinks the CPU is running + * at is still correct. + */ +static void cpufreq_delayed_get(void) +{ + if (cpufreq_init && !cpufreq_delayed_issched) { + cpufreq_delayed_issched = 1; + printk(KERN_DEBUG "Losing some ticks... checking if CPU frequency changed.\n"); + schedule_work(&cpufreq_delayed_get_work); + } +} + static unsigned int ref_freq = 0; static unsigned long loops_per_jiffy_ref = 0; @@ -518,14 +566,18 @@ static int time_cpufreq_notifier(struct void *data) { struct cpufreq_freqs *freq = data; - unsigned long *lpj; + unsigned long *lpj, dummy; + lpj = &dummy; + if (!(freq->flags & CPUFREQ_CONST_LOOPS)) #ifdef CONFIG_SMP lpj = &cpu_data[freq->cpu].loops_per_jiffy; #else lpj = &boot_cpu_data.loops_per_jiffy; #endif + + if (!ref_freq) { ref_freq = freq->old; loops_per_jiffy_ref = *lpj; @@ -538,7 +590,8 @@ static int time_cpufreq_notifier(struct cpufreq_scale(loops_per_jiffy_ref, ref_freq, freq->new); cpu_khz = cpufreq_scale(cpu_khz_ref, ref_freq, freq->new); - vxtime.tsc_quot = (1000L << 32) / cpu_khz; + if (!(freq->flags & CPUFREQ_CONST_LOOPS)) + vxtime.tsc_quot = (1000L << 32) / cpu_khz; } set_cyc2ns_scale(cpu_khz_ref / 1000); @@ -549,6 +602,18 @@ static int time_cpufreq_notifier(struct static struct notifier_block time_cpufreq_notifier_block = { .notifier_call = time_cpufreq_notifier }; + +static int __init cpufreq_tsc(void) +{ + INIT_WORK(&cpufreq_delayed_get_work, handle_cpufreq_delayed_get, NULL); + if (!cpufreq_register_notifier(&time_cpufreq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER)) + cpufreq_init = 1; + return 0; +} + +core_initcall(cpufreq_tsc); + #endif /* @@ -725,8 +790,8 @@ void __init time_init(void) cpu_khz = hpet_calibrate_tsc(); timename = "HPET"; } else { - pit_init(); - cpu_khz = pit_calibrate_tsc(); + pit_init(); + cpu_khz = pit_calibrate_tsc(); timename = "PIT"; } @@ -742,11 +807,6 @@ void __init time_init(void) setup_irq(0, &irq0); set_cyc2ns_scale(cpu_khz / 1000); - -#ifdef CONFIG_CPU_FREQ - cpufreq_register_notifier(&time_cpufreq_notifier_block, - CPUFREQ_TRANSITION_NOTIFIER); -#endif } void __init time_init_smp(void) @@ -1038,6 +1098,8 @@ irqreturn_t hpet_rtc_interrupt(int irq, } #endif + + static int __init nohpet_setup(char *s) { nohpet = 1; --- linux-2.6.9-rc1/arch/x86_64/kernel/traps.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/arch/x86_64/kernel/traps.c 2004-09-03 00:56:36.976539360 -0700 @@ -45,6 +45,9 @@ #include #include +#ifdef CONFIG_KGDB +#include +#endif extern struct gate_struct idt_table[256]; @@ -352,7 +355,7 @@ void __die(const char * str, struct pt_r #ifdef CONFIG_DEBUG_PAGEALLOC printk("DEBUG_PAGEALLOC"); #endif - printk("\n"); + printk("\n"); notify_die(DIE_OOPS, (char *)str, regs, err, 255, SIGSEGV); show_registers(regs); /* Executive summary in case the oops scrolled away */ @@ -513,7 +516,7 @@ asmlinkage void do_general_protection(st tsk->thread.error_code = error_code; tsk->thread.trap_no = 13; force_sig(SIGSEGV, tsk); - return; + return; } /* kernel gp */ @@ -670,7 +673,6 @@ clear_dr7: return regs; clear_TF_reenable: - printk("clear_tf_reenable\n"); set_tsk_thread_flag(tsk, TIF_SINGLESTEP); clear_TF: @@ -681,16 +683,43 @@ clear_TF: return regs; } +static int kernel_math_error(struct pt_regs *regs, char *str) +{ + const struct exception_table_entry *fixup; + fixup = search_exception_tables(regs->rip); + if (fixup) { + regs->rip = fixup->fixup; + return 1; + } + notify_die(DIE_GPF, str, regs, 0, 16, SIGFPE); +#if 0 + /* This should be a die, but warn only for now */ + die(str, regs, 0); +#else + printk(KERN_DEBUG "%s: %s at ", current->comm, str); + printk_address(regs->rip); + printk("\n"); +#endif + return 0; +} + /* * Note that we play around with the 'TS' bit in an attempt to get * the correct behaviour even in the presence of the asynchronous * IRQ13 behaviour */ -void math_error(void __user *rip) +asmlinkage void do_coprocessor_error(struct pt_regs *regs) { + void __user *rip = (void __user *)(regs->rip); struct task_struct * task; siginfo_t info; unsigned short cwd, swd; + + conditional_sti(regs); + if ((regs->cs & 3) == 0 && + kernel_math_error(regs, "kernel x87 math error")) + return; + /* * Save the info for the exception handler and clear the error. */ @@ -740,23 +769,23 @@ void math_error(void __user *rip) force_sig_info(SIGFPE, &info, task); } -asmlinkage void do_coprocessor_error(struct pt_regs * regs) -{ - conditional_sti(regs); - math_error((void __user *)regs->rip); -} - asmlinkage void bad_intr(void) { printk("bad interrupt"); } -static inline void simd_math_error(void __user *rip) +asmlinkage void do_simd_coprocessor_error(struct pt_regs *regs) { + void __user *rip = (void __user *)(regs->rip); struct task_struct * task; siginfo_t info; unsigned short mxcsr; + conditional_sti(regs); + if ((regs->cs & 3) == 0 && + kernel_math_error(regs, "simd math error")) + return; + /* * Save the info for the exception handler and clear the error. */ @@ -799,12 +828,6 @@ static inline void simd_math_error(void force_sig_info(SIGFPE, &info, task); } -asmlinkage void do_simd_coprocessor_error(struct pt_regs * regs) -{ - conditional_sti(regs); - simd_math_error((void __user *)regs->rip); -} - asmlinkage void do_spurious_interrupt_bug(struct pt_regs * regs) { } --- linux-2.6.9-rc1/arch/x86_64/kernel/vmlinux.lds.S 2004-08-24 00:01:53.000000000 -0700 +++ 25/arch/x86_64/kernel/vmlinux.lds.S 2004-09-02 20:51:51.000000000 -0700 @@ -91,9 +91,6 @@ SECTIONS __setup_start = .; .init.setup : { *(.init.setup) } __setup_end = .; - __start___param = .; - __param : { *(__param) } - __stop___param = .; __initcall_start = .; .initcall.init : { *(.initcall1.init) --- linux-2.6.9-rc1/arch/x86_64/kernel/x8664_ksyms.c 2004-08-24 00:02:19.000000000 -0700 +++ 25/arch/x86_64/kernel/x8664_ksyms.c 2004-09-03 00:56:41.009926192 -0700 @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -108,11 +107,8 @@ EXPORT_SYMBOL(pcibios_penalize_isa_irq); EXPORT_SYMBOL(pci_mem_start); #endif -#ifdef CONFIG_X86_USE_3DNOW -EXPORT_SYMBOL(_mmx_memcpy); -EXPORT_SYMBOL(mmx_clear_page); -EXPORT_SYMBOL(mmx_copy_page); -#endif +EXPORT_SYMBOL(copy_page); +EXPORT_SYMBOL(clear_page); EXPORT_SYMBOL(cpu_pda); #ifdef CONFIG_SMP @@ -182,10 +178,17 @@ EXPORT_SYMBOL_NOVERS(memcpy); EXPORT_SYMBOL_NOVERS(__memcpy); EXPORT_SYMBOL_NOVERS(memcmp); -/* syscall export needed for misdesigned sound drivers. */ -EXPORT_SYMBOL(sys_read); -EXPORT_SYMBOL(sys_lseek); -EXPORT_SYMBOL(sys_open); +#ifdef CONFIG_RWSEM_XCHGADD_ALGORITHM +/* prototypes are wrong, these are assembly with custom calling functions */ +extern void rwsem_down_read_failed_thunk(void); +extern void rwsem_wake_thunk(void); +extern void rwsem_downgrade_thunk(void); +extern void rwsem_down_write_failed_thunk(void); +EXPORT_SYMBOL(rwsem_down_read_failed_thunk); +EXPORT_SYMBOL(rwsem_wake_thunk); +EXPORT_SYMBOL(rwsem_downgrade_thunk); +EXPORT_SYMBOL(rwsem_down_write_failed_thunk); +#endif EXPORT_SYMBOL(empty_zero_page); @@ -211,10 +214,9 @@ EXPORT_SYMBOL(init_level4_pgt); extern unsigned long __supported_pte_mask; EXPORT_SYMBOL(__supported_pte_mask); -EXPORT_SYMBOL(clear_page); - #ifdef CONFIG_SMP EXPORT_SYMBOL(flush_tlb_page); EXPORT_SYMBOL_GPL(flush_tlb_all); #endif +EXPORT_SYMBOL(cpu_khz); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/x86_64/lib/bitops.c 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,140 @@ +#include +#include + +#undef find_first_zero_bit +#undef find_next_zero_bit +#undef find_first_bit +#undef find_next_bit + +/** + * find_first_zero_bit - find the first zero bit in a memory region + * @addr: The address to start the search at + * @size: The maximum size to search + * + * Returns the bit-number of the first zero bit, not the number of the byte + * containing a bit. + */ +inline long find_first_zero_bit(const unsigned long * addr, unsigned long size) +{ + long d0, d1, d2; + long res; + + if (!size) + return 0; + asm volatile( + " repe; scasq\n" + " je 1f\n" + " xorq -8(%%rdi),%%rax\n" + " subq $8,%%rdi\n" + " bsfq %%rax,%%rdx\n" + "1: subq %[addr],%%rdi\n" + " shlq $3,%%rdi\n" + " addq %%rdi,%%rdx" + :"=d" (res), "=&c" (d0), "=&D" (d1), "=&a" (d2) + :"0" (0ULL), "1" ((size + 63) >> 6), "2" (addr), "3" (-1ULL), + [addr] "r" (addr) : "memory"); + return res; +} + +/** + * find_next_zero_bit - find the first zero bit in a memory region + * @addr: The address to base the search on + * @offset: The bitnumber to start searching at + * @size: The maximum size to search + */ +long find_next_zero_bit (const unsigned long * addr, long size, long offset) +{ + unsigned long * p = ((unsigned long *) addr) + (offset >> 6); + unsigned long set = 0; + unsigned long res, bit = offset&63; + + if (bit) { + /* + * Look for zero in first word + */ + asm("bsfq %1,%0\n\t" + "cmoveq %2,%0" + : "=r" (set) + : "r" (~(*p >> bit)), "r"(64L)); + if (set < (64 - bit)) + return set + offset; + set = 64 - bit; + p++; + } + /* + * No zero yet, search remaining full words for a zero + */ + res = find_first_zero_bit ((const unsigned long *)p, + size - 64 * (p - (unsigned long *) addr)); + return (offset + set + res); +} + +static inline long +__find_first_bit(const unsigned long * addr, unsigned long size) +{ + long d0, d1; + long res; + + asm volatile( + " repe; scasq\n" + " jz 1f\n" + " subq $8,%%rdi\n" + " bsfq (%%rdi),%%rax\n" + "1: subq %[addr],%%rdi\n" + " shlq $3,%%rdi\n" + " addq %%rdi,%%rax" + :"=a" (res), "=&c" (d0), "=&D" (d1) + :"0" (0ULL), + "1" ((size + 63) >> 6), "2" (addr), + [addr] "r" (addr) : "memory"); + return res; +} + +/** + * find_first_bit - find the first set bit in a memory region + * @addr: The address to start the search at + * @size: The maximum size to search + * + * Returns the bit-number of the first set bit, not the number of the byte + * containing a bit. + */ +long find_first_bit(const unsigned long * addr, unsigned long size) +{ + return __find_first_bit(addr,size); +} + +/** + * find_next_bit - find the first set bit in a memory region + * @addr: The address to base the search on + * @offset: The bitnumber to start searching at + * @size: The maximum size to search + */ +long find_next_bit(const unsigned long * addr, long size, long offset) +{ + const unsigned long * p = addr + (offset >> 6); + unsigned long set = 0, bit = offset & 63, res; + + if (bit) { + /* + * Look for nonzero in the first 64 bits: + */ + asm("bsfq %1,%0\n\t" + "cmoveq %2,%0\n\t" + : "=r" (set) + : "r" (*p >> bit), "r" (64L)); + if (set < (64 - bit)) + return set + offset; + set = 64 - bit; + p++; + } + /* + * No set bit yet, search remaining full words for a bit + */ + res = __find_first_bit (p, size - 64 * (p - addr)); + return (offset + set + res); +} + +EXPORT_SYMBOL(find_next_bit); +EXPORT_SYMBOL(find_first_bit); +EXPORT_SYMBOL(find_first_zero_bit); +EXPORT_SYMBOL(find_next_zero_bit); --- linux-2.6.9-rc1/arch/x86_64/lib/bitstr.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/arch/x86_64/lib/bitstr.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,3 +1,4 @@ +#include #include /* Find string of zero bits in a bitmap */ @@ -23,3 +24,5 @@ find_next_zero_string(unsigned long *bit } return n; } + +EXPORT_SYMBOL(find_next_zero_string); --- linux-2.6.9-rc1/arch/x86_64/lib/dec_and_lock.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/arch/x86_64/lib/dec_and_lock.c 2004-09-03 00:56:43.207592096 -0700 @@ -10,6 +10,7 @@ #include #include +#ifndef ATOMIC_DEC_AND_LOCK int atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock) { int counter; @@ -38,3 +39,4 @@ slow_path: spin_unlock(lock); return 0; } +#endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/arch/x86_64/lib/kgdb_serial.c 2004-09-03 00:56:36.978539056 -0700 @@ -0,0 +1,490 @@ +/* + * Serial interface GDB stub + * + * Written (hacked together) by David Grothe (dave@gcom.com) + * Modified to allow invokation early in boot see also + * kgdb.h for instructions by George Anzinger(george@mvista.com) + * Modified to handle debugging over ethernet by Robert Walsh + * and wangdi , based on + * code by San Mehat. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_KGDB_USER_CONSOLE +extern void kgdb_console_finit(void); +#endif +#define PRNT_off +#define TEST_EXISTANCE +#ifdef PRNT +#define dbprintk(s) printk s +#else +#define dbprintk(s) +#endif +#define TEST_INTERRUPT_off +#ifdef TEST_INTERRUPT +#define intprintk(s) printk s +#else +#define intprintk(s) +#endif + +#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT) + +#define GDB_BUF_SIZE 512 /* power of 2, please */ + +static char gdb_buf[GDB_BUF_SIZE]; +static int gdb_buf_in_inx; +static atomic_t gdb_buf_in_cnt; +static int gdb_buf_out_inx; + +struct async_struct *gdb_async_info; +static int gdb_async_irq; + +#define outb_px(a,b) outb_p(b,a) + +static void program_uart(struct async_struct *info); +static void write_char(struct async_struct *info, int chr); +/* + * Get a byte from the hardware data buffer and return it + */ +static int +read_data_bfr(struct async_struct *info) +{ + char it = inb_p(info->port + UART_LSR); + + if (it & UART_LSR_DR) + return (inb_p(info->port + UART_RX)); + /* + * If we have a framing error assume somebody messed with + * our uart. Reprogram it and send '-' both ways... + */ + if (it & 0xc) { + program_uart(info); + write_char(info, '-'); + return ('-'); + } + return (-1); + +} /* read_data_bfr */ + +/* + * Get a char if available, return -1 if nothing available. + * Empty the receive buffer first, then look at the interface hardware. + + * Locking here is a bit of a problem. We MUST not lock out communication + * if we are trying to talk to gdb about a kgdb entry. ON the other hand + * we can loose chars in the console pass thru if we don't lock. It is also + * possible that we could hold the lock or be waiting for it when kgdb + * NEEDS to talk. Since kgdb locks down the world, it does not need locks. + * We do, of course have possible issues with interrupting a uart operation, + * but we will just depend on the uart status to help keep that straight. + + */ +static spinlock_t uart_interrupt_lock = SPIN_LOCK_UNLOCKED; +#ifdef CONFIG_SMP +extern spinlock_t kgdb_spinlock; +#endif + +static int +read_char(struct async_struct *info) +{ + int chr; + unsigned long flags; + local_irq_save(flags); +#ifdef CONFIG_SMP + if (!spin_is_locked(&kgdb_spinlock)) { + spin_lock(&uart_interrupt_lock); + } +#endif + if (atomic_read(&gdb_buf_in_cnt) != 0) { /* intr routine has q'd chars */ + chr = gdb_buf[gdb_buf_out_inx++]; + gdb_buf_out_inx &= (GDB_BUF_SIZE - 1); + atomic_dec(&gdb_buf_in_cnt); + } else { + chr = read_data_bfr(info); + } +#ifdef CONFIG_SMP + if (!spin_is_locked(&kgdb_spinlock)) { + spin_unlock(&uart_interrupt_lock); + } +#endif + local_irq_restore(flags); + return (chr); +} + +/* + * Wait until the interface can accept a char, then write it. + */ +static void +write_char(struct async_struct *info, int chr) +{ + while (!(inb_p(info->port + UART_LSR) & UART_LSR_THRE)) ; + + outb_p(chr, info->port + UART_TX); + +} /* write_char */ + +/* + * Mostly we don't need a spinlock, but since the console goes + * thru here with interrutps on, well, we need to catch those + * chars. + */ +/* + * This is the receiver interrupt routine for the GDB stub. + * It will receive a limited number of characters of input + * from the gdb host machine and save them up in a buffer. + * + * When the gdb stub routine tty_getDebugChar() is called it + * draws characters out of the buffer until it is empty and + * then reads directly from the serial port. + * + * We do not attempt to write chars from the interrupt routine + * since the stubs do all of that via tty_putDebugChar() which + * writes one byte after waiting for the interface to become + * ready. + * + * The debug stubs like to run with interrupts disabled since, + * after all, they run as a consequence of a breakpoint in + * the kernel. + * + * Perhaps someone who knows more about the tty driver than I + * care to learn can make this work for any low level serial + * driver. + */ +static irqreturn_t +gdb_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct async_struct *info; + unsigned long flags; + + info = gdb_async_info; + if (!info || !info->tty || irq != gdb_async_irq) + return IRQ_NONE; + + local_irq_save(flags); + spin_lock(&uart_interrupt_lock); + do { + int chr = read_data_bfr(info); + intprintk(("Debug char on int: %x hex\n", chr)); + if (chr < 0) + continue; + + if (chr == 3) { /* Ctrl-C means remote interrupt */ + BREAKPOINT; + continue; + } + + if (atomic_read(&gdb_buf_in_cnt) >= GDB_BUF_SIZE) { + /* buffer overflow tosses early char */ + read_char(info); + } + gdb_buf[gdb_buf_in_inx++] = chr; + gdb_buf_in_inx &= (GDB_BUF_SIZE - 1); + } while (inb_p(info->port + UART_IIR) & UART_IIR_RDI); + spin_unlock(&uart_interrupt_lock); + local_irq_restore(flags); + return IRQ_HANDLED; +} /* gdb_interrupt */ + +/* + * Just a NULL routine for testing. + */ +void +gdb_null(void) +{ +} /* gdb_null */ + +/* These structure are filled in with values defined in asm/kgdb_local.h + */ +static struct serial_state state = SB_STATE; +static struct async_struct local_info = SB_INFO; +static int ok_to_enable_ints = 0; +static void kgdb_enable_ints_now(void); + +extern char *kgdb_version; +/* + * Hook an IRQ for KGDB. + * + * This routine is called from tty_putDebugChar, below. + */ +static int ints_disabled = 1; +int +gdb_hook_interrupt(struct async_struct *info, int verb) +{ + struct serial_state *state = info->state; + unsigned long flags; + int port; +#ifdef TEST_EXISTANCE + int scratch, scratch2; +#endif + + /* The above fails if memory managment is not set up yet. + * Rather than fail the set up, just keep track of the fact + * and pick up the interrupt thing later. + */ + gdb_async_info = info; + port = gdb_async_info->port; + gdb_async_irq = state->irq; + if (verb) { + printk("kgdb %s : port =%x, IRQ=%d, divisor =%d\n", + kgdb_version, + port, + gdb_async_irq, gdb_async_info->state->custom_divisor); + } + local_irq_save(flags); +#ifdef TEST_EXISTANCE + /* Existance test */ + /* Should not need all this, but just in case.... */ + + scratch = inb_p(port + UART_IER); + outb_px(port + UART_IER, 0); + outb_px(0xff, 0x080); + scratch2 = inb_p(port + UART_IER); + outb_px(port + UART_IER, scratch); + if (scratch2) { + printk + ("gdb_hook_interrupt: Could not clear IER, not a UART!\n"); + local_irq_restore(flags); + return 1; /* We failed; there's nothing here */ + } + scratch2 = inb_p(port + UART_LCR); + outb_px(port + UART_LCR, 0xBF); /* set up for StarTech test */ + outb_px(port + UART_EFR, 0); /* EFR is the same as FCR */ + outb_px(port + UART_LCR, 0); + outb_px(port + UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = inb_p(port + UART_IIR) >> 6; + if (scratch == 1) { + printk("gdb_hook_interrupt: Undefined UART type!" + " Not a UART! \n"); + local_irq_restore(flags); + return 1; + } else { + dbprintk(("gdb_hook_interrupt: UART type " + "is %d where 0=16450, 2=16550 3=16550A\n", scratch)); + } + scratch = inb_p(port + UART_MCR); + outb_px(port + UART_MCR, UART_MCR_LOOP | scratch); + outb_px(port + UART_MCR, UART_MCR_LOOP | 0x0A); + scratch2 = inb_p(port + UART_MSR) & 0xF0; + outb_px(port + UART_MCR, scratch); + if (scratch2 != 0x90) { + printk("gdb_hook_interrupt: " + "Loop back test failed! Not a UART!\n"); + local_irq_restore(flags); + return scratch2 + 1000; /* force 0 to fail */ + } +#endif /* test existance */ + program_uart(info); + local_irq_restore(flags); + + return (0); + +} /* gdb_hook_interrupt */ + +static void +program_uart(struct async_struct *info) +{ + int port = info->port; + + (void) inb_p(port + UART_RX); + outb_px(port + UART_IER, 0); + + (void) inb_p(port + UART_RX); /* serial driver comments say */ + (void) inb_p(port + UART_IIR); /* this clears the interrupt regs */ + (void) inb_p(port + UART_MSR); + outb_px(port + UART_LCR, UART_LCR_WLEN8 | UART_LCR_DLAB); + outb_px(port + UART_DLL, info->state->custom_divisor & 0xff); /* LS */ + outb_px(port + UART_DLM, info->state->custom_divisor >> 8); /* MS */ + outb_px(port + UART_MCR, info->MCR); + + outb_px(port + UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1 | UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR); /* set fcr */ + outb_px(port + UART_LCR, UART_LCR_WLEN8); /* reset DLAB */ + outb_px(port + UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1); /* set fcr */ + if (!ints_disabled) { + intprintk(("KGDB: Sending %d to port %x offset %d\n", + gdb_async_info->IER, + (int) gdb_async_info->port, UART_IER)); + outb_px(gdb_async_info->port + UART_IER, gdb_async_info->IER); + } + return; +} + +/* + * tty_getDebugChar + * + * This is a GDB stub routine. It waits for a character from the + * serial interface and then returns it. If there is no serial + * interface connection then it returns a bogus value which will + * almost certainly cause the system to hang. In the + */ +int kgdb_in_isr = 0; +int kgdb_in_lsr = 0; +extern spinlock_t kgdb_spinlock; + +/* Caller takes needed protections */ + +int +tty_getDebugChar(void) +{ + volatile int chr, dum, time, end_time; + + dbprintk(("tty_getDebugChar(port %x): ", gdb_async_info->port)); + + if (gdb_async_info == NULL) { + gdb_hook_interrupt(&local_info, 0); + } + /* + * This trick says if we wait a very long time and get + * no char, return the -1 and let the upper level deal + * with it. + */ + rdtsc(dum, time); + end_time = time + 2; + while (((chr = read_char(gdb_async_info)) == -1) && + (end_time - time) > 0) { + rdtsc(dum, time); + }; + /* + * This covers our butts if some other code messes with + * our uart, hay, it happens :o) + */ + if (chr == -1) + program_uart(gdb_async_info); + + dbprintk(("%c\n", chr > ' ' && chr < 0x7F ? chr : ' ')); + return (chr); + +} /* tty_getDebugChar */ + +static int count = 3; +static spinlock_t one_at_atime = SPIN_LOCK_UNLOCKED; + +static int __init +kgdb_enable_ints(void) +{ + set_debug_traps(); + if (kgdboe) { + return 0; + } + if (gdb_async_info == NULL) { + gdb_hook_interrupt(&local_info, 1); + } + ok_to_enable_ints = 1; + kgdb_enable_ints_now(); +#ifdef CONFIG_KGDB_USER_CONSOLE + kgdb_console_finit(); +#endif + return 0; +} + +#ifdef CONFIG_SERIAL_8250 +void shutdown_for_kgdb(struct async_struct *gdb_async_info); +#endif + +#define kgdb_mem_init_done() (1) + +static void +kgdb_enable_ints_now(void) +{ + if (!spin_trylock(&one_at_atime)) + return; + if (!ints_disabled) + goto exit; + if (kgdb_mem_init_done() && + ints_disabled) { /* don't try till mem init */ +#ifdef CONFIG_SERIAL_8250 + /* + * The ifdef here allows the system to be configured + * without the serial driver. + * Don't make it a module, however, it will steal the port + */ + shutdown_for_kgdb(gdb_async_info); +#endif + ints_disabled = request_irq(gdb_async_info->state->irq, + gdb_interrupt, + IRQ_T(gdb_async_info), + "KGDB-stub", NULL); + intprintk(("KGDB: request_irq returned %d\n", ints_disabled)); + } + if (!ints_disabled) { + intprintk(("KGDB: Sending %d to port %x offset %d\n", + gdb_async_info->IER, + (int) gdb_async_info->port, UART_IER)); + outb_px(gdb_async_info->port + UART_IER, gdb_async_info->IER); + } + exit: + spin_unlock(&one_at_atime); +} + +/* + * tty_putDebugChar + * + * This is a GDB stub routine. It waits until the interface is ready + * to transmit a char and then sends it. If there is no serial + * interface connection then it simply returns to its caller, having + * pretended to send the char. Caller takes needed protections. + */ +void +tty_putDebugChar(int chr) +{ + dbprintk(("tty_putDebugChar(port %x): chr=%02x '%c', ints_on=%d\n", + gdb_async_info->port, + chr, + chr > ' ' && chr < 0x7F ? chr : ' ', ints_disabled ? 0 : 1)); + + if (gdb_async_info == NULL) { + gdb_hook_interrupt(&local_info, 0); + } + + write_char(gdb_async_info, chr); /* this routine will wait */ + count = (chr == '#') ? 0 : count + 1; + if ((count == 2)) { /* try to enable after */ + if (ints_disabled & ok_to_enable_ints) + kgdb_enable_ints_now(); /* try to enable after */ + + /* We do this a lot because, well we really want to get these + * interrupts. The serial driver will clear these bits when it + * initializes the chip. Every thing else it does is ok, + * but this. + */ + if (!ints_disabled) { + outb_px(gdb_async_info->port + UART_IER, + gdb_async_info->IER); + } + } + +} /* tty_putDebugChar */ + +/* + * This does nothing for the serial port, since it doesn't buffer. + */ + +void tty_flushDebugChar(void) +{ +} + +module_init(kgdb_enable_ints); --- linux-2.6.9-rc1/arch/x86_64/lib/Makefile 2004-08-24 00:02:20.000000000 -0700 +++ 25/arch/x86_64/lib/Makefile 2004-09-03 00:56:36.978539056 -0700 @@ -8,7 +8,8 @@ obj-y := io.o lib-y := csum-partial.o csum-copy.o csum-wrappers.o delay.o \ usercopy.o getuser.o putuser.o \ - thunk.o clear_page.o copy_page.o bitstr.o + thunk.o clear_page.o copy_page.o bitstr.o bitops.o lib-y += memcpy.o memmove.o memset.o copy_user.o lib-$(CONFIG_HAVE_DEC_LOCK) += dec_and_lock.o +lib-$(CONFIG_KGDB) += kgdb_serial.o --- linux-2.6.9-rc1/arch/x86_64/lib/memmove.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/arch/x86_64/lib/memmove.c 2004-09-02 20:51:51.000000000 -0700 @@ -10,18 +10,10 @@ void *memmove(void * dest,const void *sr if (dest < src) { __inline_memcpy(dest,src,count); } else { - /* Could be more clever and move longs */ - unsigned long d0, d1, d2; - __asm__ __volatile__( - "std\n\t" - "rep\n\t" - "movsb\n\t" - "cld" - : "=&c" (d0), "=&S" (d1), "=&D" (d2) - :"0" (count), - "1" (count-1+(const char *)src), - "2" (count-1+(char *)dest) - :"memory"); + char *p = (char *) dest + count; + char *s = (char *) src + count; + while (count--) + *--p = *--s; } return dest; } --- linux-2.6.9-rc1/arch/x86_64/Makefile 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/x86_64/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -77,6 +77,7 @@ boot := arch/x86_64/boot all: bzImage BOOTIMAGE := arch/x86_64/boot/bzImage +KBUILD_IMAGE := $(BOOTIMAGE) bzImage: vmlinux $(Q)$(MAKE) $(build)=$(boot) $(BOOTIMAGE) --- linux-2.6.9-rc1/arch/x86_64/mm/fault.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/arch/x86_64/mm/fault.c 2004-09-03 00:56:41.010926040 -0700 @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -58,16 +57,17 @@ void bust_spinlocks(int yes) /* Sometimes the CPU reports invalid exceptions on prefetch. Check that here and ignore. Opcode checker based on code by Richard Brunner */ -static int is_prefetch(struct pt_regs *regs, unsigned long addr) +static noinline int is_prefetch(struct pt_regs *regs, unsigned long addr, + unsigned long error_code) { unsigned char *instr = (unsigned char *)(regs->rip); int scan_more = 1; int prefetch = 0; unsigned char *max_instr = instr + 15; - /* Avoid recursive faults for this common case */ - if (regs->rip == addr) - return 0; + /* If it was a exec fault ignore */ + if (error_code & (1<<4)) + return 0; /* Code segments in LDT could have a non zero base. Don't check when that's possible */ @@ -218,6 +218,18 @@ int unhandled_signal(struct task_struct (tsk->sighand->action[sig-1].sa.sa_handler == SIG_DFL); } +static noinline void pgtable_bad(unsigned long address, struct pt_regs *regs, + unsigned long error_code) +{ + oops_begin(); + printk(KERN_ALERT "%s: Corrupted page table at address %lx\n", + current->comm, address); + dump_pagetable(address); + __die("Bad pagetable", regs, error_code); + oops_end(); + do_exit(SIGKILL); +} + int page_fault_trace; int exception_trace = 1; @@ -268,11 +280,32 @@ asmlinkage void do_page_fault(struct pt_ mm = tsk->mm; info.si_code = SEGV_MAPERR; - /* 5 => page not present and from supervisor mode */ - if (unlikely(!(error_code & 5) && - ((address >= VMALLOC_START && address <= VMALLOC_END) || - (address >= MODULES_VADDR && address <= MODULES_END)))) - goto vmalloc_fault; + + /* + * We fault-in kernel-space virtual memory on-demand. The + * 'reference' page table is init_mm.pgd. + * + * NOTE! We MUST NOT take any locks for this case. We may + * be in an interrupt or a critical region, and should + * only copy the information from the master page table, + * nothing more. + * + * This verifies that the fault happens in kernel space + * (error_code & 4) == 0, and that the fault was not a + * protection error (error_code & 1) == 0. + */ + if (unlikely(address >= TASK_SIZE)) { + if (!(error_code & 5)) + goto vmalloc_fault; + /* + * Don't take the mm semaphore here. If we fixup a prefetch + * fault we could otherwise deadlock. + */ + goto bad_area_nosemaphore; + } + + if (unlikely(error_code & (1 << 3))) + goto page_table_corruption; /* * If we're in an interrupt or have no user @@ -351,18 +384,18 @@ bad_area: bad_area_nosemaphore: #ifdef CONFIG_IA32_EMULATION - /* 32bit vsyscall. map on demand. */ - if (test_thread_flag(TIF_IA32) && + /* 32bit vsyscall. map on demand. */ + if (test_thread_flag(TIF_IA32) && address >= 0xffffe000 && address < 0xffffe000 + PAGE_SIZE) { - if (map_syscall32(mm, address) < 0) - goto out_of_memory2; - return; - } + if (map_syscall32(mm, address) < 0) + goto out_of_memory2; + return; + } #endif /* User mode accesses just cause a SIGSEGV */ if (error_code & 4) { - if (is_prefetch(regs, address)) + if (is_prefetch(regs, address, error_code)) return; /* Work around K8 erratum #100 K8 in compat mode @@ -376,7 +409,7 @@ bad_area_nosemaphore: return; if (exception_trace && unhandled_signal(tsk, SIGSEGV)) { - printk(KERN_INFO + printk(KERN_INFO "%s[%d]: segfault at %016lx rip %016lx rsp %016lx error %lx\n", tsk->comm, tsk->pid, address, regs->rip, regs->rsp, error_code); @@ -407,7 +440,7 @@ no_context: * Hall of shame of CPU/BIOS bugs. */ - if (is_prefetch(regs, address)) + if (is_prefetch(regs, address, error_code)) return; if (is_errata93(regs, address)) @@ -481,10 +514,8 @@ vmalloc_fault: * is really there and when yes flush the local TLB. */ pgd = pgd_offset_k(address); - if (pgd != current_pgd_offset_k(address)) - BUG(); if (!pgd_present(*pgd)) - goto bad_area_nosemaphore; + goto bad_area_nosemaphore; pmd = pmd_offset(pgd, address); if (!pmd_present(*pmd)) goto bad_area_nosemaphore; @@ -495,4 +526,7 @@ vmalloc_fault: __flush_tlb_all(); return; } + +page_table_corruption: + pgtable_bad(address, regs, error_code); } --- linux-2.6.9-rc1/arch/x86_64/mm/init.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/arch/x86_64/mm/init.c 2004-09-03 00:56:41.909789392 -0700 @@ -41,6 +41,10 @@ #define Dprintk(x...) #endif +#ifdef CONFIG_GART_IOMMU +extern int swiotlb; +#endif + extern char _stext[]; DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); @@ -396,6 +400,28 @@ static inline int page_is_ram (unsigned return 0; } +extern int swiotlb_force; + +/* + * devmem_is_allowed() checks to see if /dev/mem access to a certain address is + * valid. The argument is a physical page number. + * + * + * On x86-64, access has to be given to the first megabyte of ram because that area + * contains bios code and data regions used by X and dosemu and similar apps. + * Access has to be given to non-kernel-ram areas as well, these contain the PCI + * mmio resources as well as potential bios/acpi data regions. + */ +int devmem_is_allowed(unsigned long pagenr) +{ + if (pagenr <= 256) + return 1; + if (!page_is_ram(pagenr)) + return 1; + return 0; +} + + static struct kcore_list kcore_mem, kcore_vmalloc, kcore_kernel, kcore_modules, kcore_vsyscall; @@ -405,7 +431,10 @@ void __init mem_init(void) int tmp; #ifdef CONFIG_SWIOTLB - if (!iommu_aperture && end_pfn >= 0xffffffff>>PAGE_SHIFT) + if (swiotlb_force) + swiotlb = 1; + if (!iommu_aperture && + (end_pfn >= 0xffffffff>>PAGE_SHIFT || force_iommu)) swiotlb = 1; if (swiotlb) swiotlb_init(); @@ -596,7 +625,16 @@ static struct vm_area_struct gate32_vma struct vm_area_struct *get_gate_vma(struct task_struct *tsk) { - return test_tsk_thread_flag(tsk, TIF_IA32) ? &gate32_vma : &gate_vma; +#ifdef CONFIG_IA32_EMULATION + if (test_tsk_thread_flag(tsk, TIF_IA32)) { + /* lookup code assumes the pages are present. set them up + now */ + if (map_syscall32(tsk->mm, 0xfffe000) < 0) + return NULL; + return &gate32_vma; + } +#endif + return &gate_vma; } int in_gate_area(struct task_struct *task, unsigned long addr) --- linux-2.6.9-rc1/arch/x86_64/mm/k8topology.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/arch/x86_64/mm/k8topology.c 2004-09-03 00:56:51.388348432 -0700 @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include --- linux-2.6.9-rc1/arch/x86_64/mm/numa.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/arch/x86_64/mm/numa.c 2004-09-03 00:56:51.388348432 -0700 @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -135,7 +136,7 @@ void __init setup_node_zones(int nodeid) zones[ZONE_NORMAL] = end_pfn - start_pfn; } - free_area_init_node(nodeid, NODE_DATA(nodeid), NULL, zones, + free_area_init_node(nodeid, NODE_DATA(nodeid), zones, start_pfn, NULL); } @@ -151,9 +152,9 @@ void __init numa_init_array(void) for (i = 0; i < MAXNODE; i++) { if (node_online(i)) continue; - rr = find_next_bit(node_online_map, MAX_NUMNODES, rr); + rr = next_node(rr, node_online_map); if (rr == MAX_NUMNODES) - rr = find_first_bit(node_online_map, MAX_NUMNODES); + rr = first_node(node_online_map); node_data[i] = node_data[rr]; cpu_to_node[i] = rr; rr++; @@ -162,10 +163,58 @@ void __init numa_init_array(void) set_bit(0, &node_to_cpumask[cpu_to_node(0)]); } +int numa_fake __initdata = 0; + +/* Numa emulation */ +static int numa_emulation(unsigned long start_pfn, unsigned long end_pfn) +{ + int i; + struct node nodes[MAXNODE]; + unsigned long sz = ((end_pfn - start_pfn)< 1) { + unsigned long x = 1; + while ((x << 1) < sz) + x <<= 1; + if (x < sz/2) + printk("Numa emulation unbalanced. Complain to maintainer\n"); + sz = x; + } + + memset(&nodes,0,sizeof(nodes)); + for (i = 0; i < numa_fake; i++) { + nodes[i].start = (start_pfn<> 20); + } + numnodes = numa_fake; + memnode_shift = compute_hash_shift(nodes); + if (memnode_shift < 0) { + memnode_shift = 0; + printk(KERN_ERR "No NUMA hash function found. Emulation disabled.\n"); + return -1; + } + for (i = 0; i < numa_fake; i++) + setup_node_bootmem(i, nodes[i].start, nodes[i].end); + numa_init_array(); + return 0; +} + void __init numa_initmem_init(unsigned long start_pfn, unsigned long end_pfn) { int i; + if (numa_fake && !numa_emulation(start_pfn, end_pfn)) + return; + #ifdef CONFIG_K8_NUMA if (!numa_off && !k8_scan_nodes(start_pfn<2.0.29) @@ -735,6 +748,12 @@ S: Schimmelsrain 1 S: D-69231 Rauenberg S: Germany +N: Jean Delvare +E: khali@linux-fr.org +W: http://khali.linux-fr.org/ +D: Several hardware monitoring drivers +S: France + N: Peter Denison E: peterd@pnd-pc.demon.co.uk W: http://www.pnd-pc.demon.co.uk/promise/ @@ -754,7 +773,7 @@ W: http://luxik.cdi.cz/~devik/qos/ D: HTB qdisc and random networking hacks N: Alex deVries -E: adevries@thepuffingroup.com +E: alex@onefishtwo.ca D: Various SGI parts, bits of HAL2 and Newport, PA-RISC Linux. S: 41.5 William Street S: Ottawa, Ontario @@ -905,6 +924,10 @@ S: Brevia 1043 S: S-114 79 Stockholm S: Sweden +N: David Engebretsen +E: engebret@us.ibm.com +D: Linux port to 64-bit PowerPC architecture + N: Michael Engel E: engel@unix-ag.org D: DECstation framebuffer drivers @@ -1049,6 +1072,12 @@ S: R. Tocantins, 89 - Cristo Rei S: 80050-430 - Curitiba - Paraná S: Brazil +N: Kumar Gala +E: kumar.gala@freescale.com +D: Embedded PowerPC 6xx/7xx/74xx/82xx/85xx support +S: Austin, Texas 78729 +S: USA + N: Nigel Gamble E: nigel@nrg.org D: Interrupt-driven printer driver @@ -1060,12 +1089,6 @@ S: USA N: Jeff Garzik E: jgarzik@pobox.com -N: Kumar Gala -E: kumar.gala@freescale.com -D: Embedded PowerPC 6xx/7xx/74xx/82xx/85xx support -S: Austin, Texas 78729 -S: USA - N: Jacques Gelinas E: jacques@solucorp.qc.ca D: Author of the Umsdos file system @@ -1567,6 +1590,13 @@ N: Ani Joshi E: ajoshi@shell.unixbox.com D: fbdev hacking +N: Jesper Juhl +E: juhl-lkml@dif.dk +D: Various small janitor fixes, cleanups etc. +S: Lemnosvej 1, 3.tv +S: 2300 Copenhagen S +S: Denmark + N: Bernhard Kaindl E: bkaindl@netway.at E: edv@bartelt.via.at @@ -1818,11 +1848,6 @@ S: Sindlovy Dvory 117 S: 370 01 Ceske Budejovice S: Czech Republic -N: Adam Belay -E: ambx1@neo.rr.com -D: Linux Plug and Play Support -S: USA - N: Bas Laarhoven E: sjml@xs4all.nl D: Loadable modules and ftape driver @@ -1954,12 +1979,6 @@ S: 8786 Niwot Road S: Niwot, Colorado 80503 S: USA -N: Pete Popov -E: pete_popov@yahoo.com -D: Linux/MIPS AMD/Alchemy Port and mips hacking and debugging -S: San Jose, CA 95134 -S: USA - N: Robert M. Love E: rml@tech9.net E: rml@novell.com @@ -2214,14 +2233,13 @@ S: 7546 JA Enschede S: Netherlands N: David S. Miller -E: davem@redhat.com +E: davem@davemloft.net D: Sparc and blue box hacker D: Vger Linux mailing list co-maintainer D: Linux Emacs elf/qmagic support + other libc/gcc things D: Yee bore de yee bore! ;-) -S: 750 N. Shoreline Blvd. -S: Apt. #111 -S: Mountain View, California 94043 +S: 575 Harrison St. #103 +S: San Francisco, CA 94105 S: USA N: Rick Miller @@ -2538,6 +2556,7 @@ N: Mikael Pettersson E: mikpe@csd.uu.se W: http://www.csd.uu.se/~mikpe/ D: Miscellaneous fixes +D: Performance-monitoring counters driver N: Reed H. Petty E: rhp@draper.net @@ -2582,6 +2601,12 @@ P: 1024D/EDBB6147 7B36 0E07 04BC 11DC A7 D: sonypi, meye drivers, mct_u232 usb serial hacks S: Paris, France +N: Pete Popov +E: pete_popov@yahoo.com +D: Linux/MIPS AMD/Alchemy Port and mips hacking and debugging +S: San Jose, CA 95134 +S: USA + N: Matt Porter E: mporter@kernel.crashing.org D: Motorola PowerPC PReP support @@ -2821,7 +2846,6 @@ S: I/3 Walter St S: Wellington S: New Zealand - N: Sampo Saaristo E: sambo@cs.tut.fi D: Co-author of Multi-Protocol Over ATM (MPOA) @@ -2838,6 +2862,9 @@ S: Markusstrasse 18 S: 8006 Zuerich S: Switzerland +N: Manuel Estrada Sainz +D: Firmware loader (request_firmware) + N: Wayne Salamon E: wsalamon@tislabs.com E: wsalamon@nai.com @@ -3289,14 +3316,6 @@ S: Chudenicka 8 S: 10200 Prague 10, Hostivar S: Czech Republic -N: James R. Van Zandt -E: jrv@vanzandt.mv.com -P: 1024/E298966D F0 37 4F FD E5 7E C5 E6 F1 A0 1E 22 6F 46 DA 0C -D: Author and maintainer of the Double Talk speech synthesizer driver -S: 27 Spencer Drive -S: Nashua, New Hampshire 03062 -S: USA - N: Heikki Vatiainen E: hessu@cs.tut.fi D: Co-author of Multi-Protocol Over ATM (MPOA), some LANE hacks @@ -3578,6 +3597,14 @@ S: ARCO Tower 1-8-1 Shimomeguro Meguro-k S: Tokyo 153 S: Japan +N: James R. Van Zandt +E: jrv@vanzandt.mv.com +P: 1024/E298966D F0 37 4F FD E5 7E C5 E6 F1 A0 1E 22 6F 46 DA 0C +D: Author and maintainer of the Double Talk speech synthesizer driver +S: 27 Spencer Drive +S: Nashua, New Hampshire 03062 +S: USA + N: Orest Zborowski E: orestz@eskimo.com D: XFree86 and kernel development @@ -3619,13 +3646,6 @@ D: MD driver D: EISA/sysfs subsystem S: France -N: Luiz Fernando N. Capitulino -E: lcapitulino@terra.com.br -E: lcapitulino@prefeitura.sp.gov.br -W: http://www.telecentros.sp.gov.br -D: Little fixes and a lot of janitorial work -S: E-GOV Telecentros SP -S: Brazil # Don't add your name here, unless you really _are_ after Marc # alphabetically. Leonard used to be very proud of being the --- linux-2.6.9-rc1/crypto/internal.h 2004-08-24 00:01:51.000000000 -0700 +++ 25/crypto/internal.h 2004-09-03 00:56:41.010926040 -0700 @@ -17,7 +17,6 @@ #include #include #include -#include #include extern enum km_type crypto_km_types[]; --- linux-2.6.9-rc1/crypto/Kconfig 2004-08-24 00:03:32.000000000 -0700 +++ 25/crypto/Kconfig 2004-09-02 20:51:51.000000000 -0700 @@ -67,6 +67,21 @@ config CRYPTO_SHA512 This code also includes SHA-384, a 384 bit hash with 192 bits of security against collision attacks. +config CRYPTO_WHIRLPOOL + tristate "Whirlpool digest algorithm" + depends on CRYPTO + help + Whirlpool hash algorithm. + + Whirlpool is part of the NESSIE cryptographic primtives. + Whirlpool works on messages shorter than 2^256 bits and + produces a 512 bit hash. + + Whirlpool will be part of the ISO/IEC 10118-3:2003(E) standard + + See also: + http://planeta.terra.com.br/informatica/paulobarreto/WhirlpoolPage.html + config CRYPTO_DES tristate "DES and Triple DES EDE cipher algorithms" depends on CRYPTO --- linux-2.6.9-rc1/crypto/Makefile 2004-08-24 00:03:38.000000000 -0700 +++ 25/crypto/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -14,6 +14,7 @@ obj-$(CONFIG_CRYPTO_MD5) += md5.o obj-$(CONFIG_CRYPTO_SHA1) += sha1.o obj-$(CONFIG_CRYPTO_SHA256) += sha256.o obj-$(CONFIG_CRYPTO_SHA512) += sha512.o +obj-$(CONFIG_CRYPTO_WHIRLPOOL) += whirlpool.o obj-$(CONFIG_CRYPTO_DES) += des.o obj-$(CONFIG_CRYPTO_BLOWFISH) += blowfish.o obj-$(CONFIG_CRYPTO_TWOFISH) += twofish.o --- linux-2.6.9-rc1/crypto/tcrypt.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/crypto/tcrypt.c 2004-09-02 20:51:51.000000000 -0700 @@ -24,6 +24,7 @@ #include #include #include +#include #include "tcrypt.h" /* @@ -61,7 +62,8 @@ static char *tvmem; static char *check[] = { "des", "md5", "des3_ede", "rot13", "sha1", "sha256", "blowfish", "twofish", "serpent", "sha384", "sha512", "md4", "aes", "cast6", - "arc4", "michael_mic", "deflate", "crc32c", "tea", "xtea", NULL + "arc4", "michael_mic", "deflate", "crc32c", "tea", "xtea", + "whirlpool", NULL }; static void @@ -680,6 +682,7 @@ do_test(void) test_hash("sha384", sha384_tv_template, SHA384_TEST_VECTORS); test_hash("sha512", sha512_tv_template, SHA512_TEST_VECTORS); + test_hash("whirlpool", whirlpool_tv_template, WHIRLPOOL_TEST_VECTORS); test_deflate(); test_crc32c(); #ifdef CONFIG_CRYPTO_HMAC @@ -791,6 +794,11 @@ do_test(void) test_cipher ("khazad", MODE_ECB, DECRYPT, khazad_dec_tv_template, KHAZAD_DEC_TEST_VECTORS); break; + case 22: + test_hash("whirlpool", whirlpool_tv_template, WHIRLPOOL_TEST_VECTORS); + break; + + #ifdef CONFIG_CRYPTO_HMAC case 100: test_hmac("md5", hmac_md5_tv_template, HMAC_MD5_TEST_VECTORS); @@ -846,7 +854,7 @@ static void __exit fini(void) { } module_init(init); module_exit(fini); -MODULE_PARM(mode, "i"); +module_param(mode, int, 0); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Quick & dirty crypto testing module"); --- linux-2.6.9-rc1/crypto/tcrypt.h 2004-08-24 00:03:32.000000000 -0700 +++ 25/crypto/tcrypt.h 2004-09-02 20:51:51.000000000 -0700 @@ -301,6 +301,110 @@ struct hash_testvec sha512_tv_template[] }, }; + +/* + * WHIRLPOOL test vectors from Whirlpool package + * by Vincent Rijmen and Paulo S. L. M. Barreto as part of the NESSIE + * submission + */ +#define WHIRLPOOL_TEST_VECTORS 8 + +struct hash_testvec whirlpool_tv_template[] = { + { + .plaintext = "", + .psize = 0, + .digest = { 0x19, 0xFA, 0x61, 0xD7, 0x55, 0x22, 0xA4, 0x66, + 0x9B, 0x44, 0xE3, 0x9C, 0x1D, 0x2E, 0x17, 0x26, + 0xC5, 0x30, 0x23, 0x21, 0x30, 0xD4, 0x07, 0xF8, + 0x9A, 0xFE, 0xE0, 0x96, 0x49, 0x97, 0xF7, 0xA7, + 0x3E, 0x83, 0xBE, 0x69, 0x8B, 0x28, 0x8F, 0xEB, + 0xCF, 0x88, 0xE3, 0xE0, 0x3C, 0x4F, 0x07, 0x57, + 0xEA, 0x89, 0x64, 0xE5, 0x9B, 0x63, 0xD9, 0x37, + 0x08, 0xB1, 0x38, 0xCC, 0x42, 0xA6, 0x6E, 0xB3 }, + + + }, { + .plaintext = "a", + .psize = 1, + .digest = { 0x8A, 0xCA, 0x26, 0x02, 0x79, 0x2A, 0xEC, 0x6F, + 0x11, 0xA6, 0x72, 0x06, 0x53, 0x1F, 0xB7, 0xD7, + 0xF0, 0xDF, 0xF5, 0x94, 0x13, 0x14, 0x5E, 0x69, + 0x73, 0xC4, 0x50, 0x01, 0xD0, 0x08, 0x7B, 0x42, + 0xD1, 0x1B, 0xC6, 0x45, 0x41, 0x3A, 0xEF, 0xF6, + 0x3A, 0x42, 0x39, 0x1A, 0x39, 0x14, 0x5A, 0x59, + 0x1A, 0x92, 0x20, 0x0D, 0x56, 0x01, 0x95, 0xE5, + 0x3B, 0x47, 0x85, 0x84, 0xFD, 0xAE, 0x23, 0x1A }, + }, { + .plaintext = "abc", + .psize = 3, + .digest = { 0x4E, 0x24, 0x48, 0xA4, 0xC6, 0xF4, 0x86, 0xBB, + 0x16, 0xB6, 0x56, 0x2C, 0x73, 0xB4, 0x02, 0x0B, + 0xF3, 0x04, 0x3E, 0x3A, 0x73, 0x1B, 0xCE, 0x72, + 0x1A, 0xE1, 0xB3, 0x03, 0xD9, 0x7E, 0x6D, 0x4C, + 0x71, 0x81, 0xEE, 0xBD, 0xB6, 0xC5, 0x7E, 0x27, + 0x7D, 0x0E, 0x34, 0x95, 0x71, 0x14, 0xCB, 0xD6, + 0xC7, 0x97, 0xFC, 0x9D, 0x95, 0xD8, 0xB5, 0x82, + 0xD2, 0x25, 0x29, 0x20, 0x76, 0xD4, 0xEE, 0xF5 }, + }, { + .plaintext = "message digest", + .psize = 14, + .digest = { 0x37, 0x8C, 0x84, 0xA4, 0x12, 0x6E, 0x2D, 0xC6, + 0xE5, 0x6D, 0xCC, 0x74, 0x58, 0x37, 0x7A, 0xAC, + 0x83, 0x8D, 0x00, 0x03, 0x22, 0x30, 0xF5, 0x3C, + 0xE1, 0xF5, 0x70, 0x0C, 0x0F, 0xFB, 0x4D, 0x3B, + 0x84, 0x21, 0x55, 0x76, 0x59, 0xEF, 0x55, 0xC1, + 0x06, 0xB4, 0xB5, 0x2A, 0xC5, 0xA4, 0xAA, 0xA6, + 0x92, 0xED, 0x92, 0x00, 0x52, 0x83, 0x8F, 0x33, + 0x62, 0xE8, 0x6D, 0xBD, 0x37, 0xA8, 0x90, 0x3E }, + }, { + .plaintext = "abcdefghijklmnopqrstuvwxyz", + .psize = 26, + .digest = { 0xF1, 0xD7, 0x54, 0x66, 0x26, 0x36, 0xFF, 0xE9, + 0x2C, 0x82, 0xEB, 0xB9, 0x21, 0x2A, 0x48, 0x4A, + 0x8D, 0x38, 0x63, 0x1E, 0xAD, 0x42, 0x38, 0xF5, + 0x44, 0x2E, 0xE1, 0x3B, 0x80, 0x54, 0xE4, 0x1B, + 0x08, 0xBF, 0x2A, 0x92, 0x51, 0xC3, 0x0B, 0x6A, + 0x0B, 0x8A, 0xAE, 0x86, 0x17, 0x7A, 0xB4, 0xA6, + 0xF6, 0x8F, 0x67, 0x3E, 0x72, 0x07, 0x86, 0x5D, + 0x5D, 0x98, 0x19, 0xA3, 0xDB, 0xA4, 0xEB, 0x3B }, + }, { + .plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz0123456789", + .psize = 62, + .digest = { 0xDC, 0x37, 0xE0, 0x08, 0xCF, 0x9E, 0xE6, 0x9B, + 0xF1, 0x1F, 0x00, 0xED, 0x9A, 0xBA, 0x26, 0x90, + 0x1D, 0xD7, 0xC2, 0x8C, 0xDE, 0xC0, 0x66, 0xCC, + 0x6A, 0xF4, 0x2E, 0x40, 0xF8, 0x2F, 0x3A, 0x1E, + 0x08, 0xEB, 0xA2, 0x66, 0x29, 0x12, 0x9D, 0x8F, + 0xB7, 0xCB, 0x57, 0x21, 0x1B, 0x92, 0x81, 0xA6, + 0x55, 0x17, 0xCC, 0x87, 0x9D, 0x7B, 0x96, 0x21, + 0x42, 0xC6, 0x5F, 0x5A, 0x7A, 0xF0, 0x14, 0x67 }, + }, { + .plaintext = "1234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890", + .psize = 80, + .digest = { 0x46, 0x6E, 0xF1, 0x8B, 0xAB, 0xB0, 0x15, 0x4D, + 0x25, 0xB9, 0xD3, 0x8A, 0x64, 0x14, 0xF5, 0xC0, + 0x87, 0x84, 0x37, 0x2B, 0xCC, 0xB2, 0x04, 0xD6, + 0x54, 0x9C, 0x4A, 0xFA, 0xDB, 0x60, 0x14, 0x29, + 0x4D, 0x5B, 0xD8, 0xDF, 0x2A, 0x6C, 0x44, 0xE5, + 0x38, 0xCD, 0x04, 0x7B, 0x26, 0x81, 0xA5, 0x1A, + 0x2C, 0x60, 0x48, 0x1E, 0x88, 0xC5, 0xA2, 0x0B, + 0x2C, 0x2A, 0x80, 0xCF, 0x3A, 0x9A, 0x08, 0x3B }, + }, { + .plaintext = "abcdbcdecdefdefgefghfghighijhijk", + .psize = 32, + .digest = { 0x2A, 0x98, 0x7E, 0xA4, 0x0F, 0x91, 0x70, 0x61, + 0xF5, 0xD6, 0xF0, 0xA0, 0xE4, 0x64, 0x4F, 0x48, + 0x8A, 0x7A, 0x5A, 0x52, 0xDE, 0xEE, 0x65, 0x62, + 0x07, 0xC5, 0x62, 0xF9, 0x88, 0xE9, 0x5C, 0x69, + 0x16, 0xBD, 0xC8, 0x03, 0x1B, 0xC5, 0xBE, 0x1B, + 0x7B, 0x94, 0x76, 0x39, 0xFE, 0x05, 0x0B, 0x56, + 0x93, 0x9B, 0xAA, 0xA0, 0xAD, 0xFF, 0x9A, 0xE6, + 0x74, 0x5B, 0x7B, 0x18, 0x1C, 0x3B, 0xE3, 0xFD }, + }, +}; + #ifdef CONFIG_CRYPTO_HMAC /* * HMAC-MD5 test vectors from RFC2202 --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/crypto/whirlpool.c 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,1131 @@ +/* + * Cryptographic API. + * + * Whirlpool hashing Algorithm + * + * The Whirlpool algorithm was developed by Paulo S. L. M. Barreto and + * Vincent Rijmen. It has been selected as one of cryptographic + * primitives by the NESSIE project http://www.cryptonessie.org/ + * + * The original authors have disclaimed all copyright interest in this + * code and thus put it in the public domain. The subsequent authors + * have put this under the GNU General Public License. + * + * By Aaron Grothe ajgrothe@yahoo.com, August 23, 2004 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ +#include +#include +#include +#include +#include + +#define WHIRLPOOL_DIGEST_SIZE 64 +#define WHIRLPOOL_BLOCK_SIZE 64 +#define WHIRLPOOL_LENGTHBYTES 32 + +#define WHIRLPOOL_ROUNDS 10 + +struct whirlpool_ctx { + u8 bitLength[WHIRLPOOL_LENGTHBYTES]; + u8 buffer[WHIRLPOOL_BLOCK_SIZE]; + int bufferBits; + int bufferPos; + u64 hash[WHIRLPOOL_DIGEST_SIZE/8]; +}; + +/* + * Though Whirlpool is endianness-neutral, the encryption tables are listed + * in BIG-ENDIAN format, which is adopted throughout this implementation + * (but little-endian notation would be equally suitable if consistently + * employed). + */ + +static const u64 C0[256] = { + 0x18186018c07830d8ULL, 0x23238c2305af4626ULL, 0xc6c63fc67ef991b8ULL, + 0xe8e887e8136fcdfbULL, 0x878726874ca113cbULL, 0xb8b8dab8a9626d11ULL, + 0x0101040108050209ULL, 0x4f4f214f426e9e0dULL, 0x3636d836adee6c9bULL, + 0xa6a6a2a6590451ffULL, 0xd2d26fd2debdb90cULL, 0xf5f5f3f5fb06f70eULL, + 0x7979f979ef80f296ULL, 0x6f6fa16f5fcede30ULL, 0x91917e91fcef3f6dULL, + 0x52525552aa07a4f8ULL, 0x60609d6027fdc047ULL, 0xbcbccabc89766535ULL, + 0x9b9b569baccd2b37ULL, 0x8e8e028e048c018aULL, 0xa3a3b6a371155bd2ULL, + 0x0c0c300c603c186cULL, 0x7b7bf17bff8af684ULL, 0x3535d435b5e16a80ULL, + 0x1d1d741de8693af5ULL, 0xe0e0a7e05347ddb3ULL, 0xd7d77bd7f6acb321ULL, + 0xc2c22fc25eed999cULL, 0x2e2eb82e6d965c43ULL, 0x4b4b314b627a9629ULL, + 0xfefedffea321e15dULL, 0x575741578216aed5ULL, 0x15155415a8412abdULL, + 0x7777c1779fb6eee8ULL, 0x3737dc37a5eb6e92ULL, 0xe5e5b3e57b56d79eULL, + 0x9f9f469f8cd92313ULL, 0xf0f0e7f0d317fd23ULL, 0x4a4a354a6a7f9420ULL, + 0xdada4fda9e95a944ULL, 0x58587d58fa25b0a2ULL, 0xc9c903c906ca8fcfULL, + 0x2929a429558d527cULL, 0x0a0a280a5022145aULL, 0xb1b1feb1e14f7f50ULL, + 0xa0a0baa0691a5dc9ULL, 0x6b6bb16b7fdad614ULL, 0x85852e855cab17d9ULL, + 0xbdbdcebd8173673cULL, 0x5d5d695dd234ba8fULL, 0x1010401080502090ULL, + 0xf4f4f7f4f303f507ULL, 0xcbcb0bcb16c08bddULL, 0x3e3ef83eedc67cd3ULL, + 0x0505140528110a2dULL, 0x676781671fe6ce78ULL, 0xe4e4b7e47353d597ULL, + 0x27279c2725bb4e02ULL, 0x4141194132588273ULL, 0x8b8b168b2c9d0ba7ULL, + 0xa7a7a6a7510153f6ULL, 0x7d7de97dcf94fab2ULL, 0x95956e95dcfb3749ULL, + 0xd8d847d88e9fad56ULL, 0xfbfbcbfb8b30eb70ULL, 0xeeee9fee2371c1cdULL, + 0x7c7ced7cc791f8bbULL, 0x6666856617e3cc71ULL, 0xdddd53dda68ea77bULL, + 0x17175c17b84b2eafULL, 0x4747014702468e45ULL, 0x9e9e429e84dc211aULL, + 0xcaca0fca1ec589d4ULL, 0x2d2db42d75995a58ULL, 0xbfbfc6bf9179632eULL, + 0x07071c07381b0e3fULL, 0xadad8ead012347acULL, 0x5a5a755aea2fb4b0ULL, + 0x838336836cb51befULL, 0x3333cc3385ff66b6ULL, 0x636391633ff2c65cULL, + 0x02020802100a0412ULL, 0xaaaa92aa39384993ULL, 0x7171d971afa8e2deULL, + 0xc8c807c80ecf8dc6ULL, 0x19196419c87d32d1ULL, 0x494939497270923bULL, + 0xd9d943d9869aaf5fULL, 0xf2f2eff2c31df931ULL, 0xe3e3abe34b48dba8ULL, + 0x5b5b715be22ab6b9ULL, 0x88881a8834920dbcULL, 0x9a9a529aa4c8293eULL, + 0x262698262dbe4c0bULL, 0x3232c8328dfa64bfULL, 0xb0b0fab0e94a7d59ULL, + 0xe9e983e91b6acff2ULL, 0x0f0f3c0f78331e77ULL, 0xd5d573d5e6a6b733ULL, + 0x80803a8074ba1df4ULL, 0xbebec2be997c6127ULL, 0xcdcd13cd26de87ebULL, + 0x3434d034bde46889ULL, 0x48483d487a759032ULL, 0xffffdbffab24e354ULL, + 0x7a7af57af78ff48dULL, 0x90907a90f4ea3d64ULL, 0x5f5f615fc23ebe9dULL, + 0x202080201da0403dULL, 0x6868bd6867d5d00fULL, 0x1a1a681ad07234caULL, + 0xaeae82ae192c41b7ULL, 0xb4b4eab4c95e757dULL, 0x54544d549a19a8ceULL, + 0x93937693ece53b7fULL, 0x222288220daa442fULL, 0x64648d6407e9c863ULL, + 0xf1f1e3f1db12ff2aULL, 0x7373d173bfa2e6ccULL, 0x12124812905a2482ULL, + 0x40401d403a5d807aULL, 0x0808200840281048ULL, 0xc3c32bc356e89b95ULL, + 0xecec97ec337bc5dfULL, 0xdbdb4bdb9690ab4dULL, 0xa1a1bea1611f5fc0ULL, + 0x8d8d0e8d1c830791ULL, 0x3d3df43df5c97ac8ULL, 0x97976697ccf1335bULL, + 0x0000000000000000ULL, 0xcfcf1bcf36d483f9ULL, 0x2b2bac2b4587566eULL, + 0x7676c57697b3ece1ULL, 0x8282328264b019e6ULL, 0xd6d67fd6fea9b128ULL, + 0x1b1b6c1bd87736c3ULL, 0xb5b5eeb5c15b7774ULL, 0xafaf86af112943beULL, + 0x6a6ab56a77dfd41dULL, 0x50505d50ba0da0eaULL, 0x45450945124c8a57ULL, + 0xf3f3ebf3cb18fb38ULL, 0x3030c0309df060adULL, 0xefef9bef2b74c3c4ULL, + 0x3f3ffc3fe5c37edaULL, 0x55554955921caac7ULL, 0xa2a2b2a2791059dbULL, + 0xeaea8fea0365c9e9ULL, 0x656589650fecca6aULL, 0xbabad2bab9686903ULL, + 0x2f2fbc2f65935e4aULL, 0xc0c027c04ee79d8eULL, 0xdede5fdebe81a160ULL, + 0x1c1c701ce06c38fcULL, 0xfdfdd3fdbb2ee746ULL, 0x4d4d294d52649a1fULL, + 0x92927292e4e03976ULL, 0x7575c9758fbceafaULL, 0x06061806301e0c36ULL, + 0x8a8a128a249809aeULL, 0xb2b2f2b2f940794bULL, 0xe6e6bfe66359d185ULL, + 0x0e0e380e70361c7eULL, 0x1f1f7c1ff8633ee7ULL, 0x6262956237f7c455ULL, + 0xd4d477d4eea3b53aULL, 0xa8a89aa829324d81ULL, 0x96966296c4f43152ULL, + 0xf9f9c3f99b3aef62ULL, 0xc5c533c566f697a3ULL, 0x2525942535b14a10ULL, + 0x59597959f220b2abULL, 0x84842a8454ae15d0ULL, 0x7272d572b7a7e4c5ULL, + 0x3939e439d5dd72ecULL, 0x4c4c2d4c5a619816ULL, 0x5e5e655eca3bbc94ULL, + 0x7878fd78e785f09fULL, 0x3838e038ddd870e5ULL, 0x8c8c0a8c14860598ULL, + 0xd1d163d1c6b2bf17ULL, 0xa5a5aea5410b57e4ULL, 0xe2e2afe2434dd9a1ULL, + 0x616199612ff8c24eULL, 0xb3b3f6b3f1457b42ULL, 0x2121842115a54234ULL, + 0x9c9c4a9c94d62508ULL, 0x1e1e781ef0663ceeULL, 0x4343114322528661ULL, + 0xc7c73bc776fc93b1ULL, 0xfcfcd7fcb32be54fULL, 0x0404100420140824ULL, + 0x51515951b208a2e3ULL, 0x99995e99bcc72f25ULL, 0x6d6da96d4fc4da22ULL, + 0x0d0d340d68391a65ULL, 0xfafacffa8335e979ULL, 0xdfdf5bdfb684a369ULL, + 0x7e7ee57ed79bfca9ULL, 0x242490243db44819ULL, 0x3b3bec3bc5d776feULL, + 0xabab96ab313d4b9aULL, 0xcece1fce3ed181f0ULL, 0x1111441188552299ULL, + 0x8f8f068f0c890383ULL, 0x4e4e254e4a6b9c04ULL, 0xb7b7e6b7d1517366ULL, + 0xebeb8beb0b60cbe0ULL, 0x3c3cf03cfdcc78c1ULL, 0x81813e817cbf1ffdULL, + 0x94946a94d4fe3540ULL, 0xf7f7fbf7eb0cf31cULL, 0xb9b9deb9a1676f18ULL, + 0x13134c13985f268bULL, 0x2c2cb02c7d9c5851ULL, 0xd3d36bd3d6b8bb05ULL, + 0xe7e7bbe76b5cd38cULL, 0x6e6ea56e57cbdc39ULL, 0xc4c437c46ef395aaULL, + 0x03030c03180f061bULL, 0x565645568a13acdcULL, 0x44440d441a49885eULL, + 0x7f7fe17fdf9efea0ULL, 0xa9a99ea921374f88ULL, 0x2a2aa82a4d825467ULL, + 0xbbbbd6bbb16d6b0aULL, 0xc1c123c146e29f87ULL, 0x53535153a202a6f1ULL, + 0xdcdc57dcae8ba572ULL, 0x0b0b2c0b58271653ULL, 0x9d9d4e9d9cd32701ULL, + 0x6c6cad6c47c1d82bULL, 0x3131c43195f562a4ULL, 0x7474cd7487b9e8f3ULL, + 0xf6f6fff6e309f115ULL, 0x464605460a438c4cULL, 0xacac8aac092645a5ULL, + 0x89891e893c970fb5ULL, 0x14145014a04428b4ULL, 0xe1e1a3e15b42dfbaULL, + 0x16165816b04e2ca6ULL, 0x3a3ae83acdd274f7ULL, 0x6969b9696fd0d206ULL, + 0x09092409482d1241ULL, 0x7070dd70a7ade0d7ULL, 0xb6b6e2b6d954716fULL, + 0xd0d067d0ceb7bd1eULL, 0xeded93ed3b7ec7d6ULL, 0xcccc17cc2edb85e2ULL, + 0x424215422a578468ULL, 0x98985a98b4c22d2cULL, 0xa4a4aaa4490e55edULL, + 0x2828a0285d885075ULL, 0x5c5c6d5cda31b886ULL, 0xf8f8c7f8933fed6bULL, + 0x8686228644a411c2ULL, +}; + +static const u64 C1[256] = { + 0xd818186018c07830ULL, 0x2623238c2305af46ULL, 0xb8c6c63fc67ef991ULL, + 0xfbe8e887e8136fcdULL, 0xcb878726874ca113ULL, 0x11b8b8dab8a9626dULL, + 0x0901010401080502ULL, 0x0d4f4f214f426e9eULL, 0x9b3636d836adee6cULL, + 0xffa6a6a2a6590451ULL, 0x0cd2d26fd2debdb9ULL, 0x0ef5f5f3f5fb06f7ULL, + 0x967979f979ef80f2ULL, 0x306f6fa16f5fcedeULL, 0x6d91917e91fcef3fULL, + 0xf852525552aa07a4ULL, 0x4760609d6027fdc0ULL, 0x35bcbccabc897665ULL, + 0x379b9b569baccd2bULL, 0x8a8e8e028e048c01ULL, 0xd2a3a3b6a371155bULL, + 0x6c0c0c300c603c18ULL, 0x847b7bf17bff8af6ULL, 0x803535d435b5e16aULL, + 0xf51d1d741de8693aULL, 0xb3e0e0a7e05347ddULL, 0x21d7d77bd7f6acb3ULL, + 0x9cc2c22fc25eed99ULL, 0x432e2eb82e6d965cULL, 0x294b4b314b627a96ULL, + 0x5dfefedffea321e1ULL, 0xd5575741578216aeULL, 0xbd15155415a8412aULL, + 0xe87777c1779fb6eeULL, 0x923737dc37a5eb6eULL, 0x9ee5e5b3e57b56d7ULL, + 0x139f9f469f8cd923ULL, 0x23f0f0e7f0d317fdULL, 0x204a4a354a6a7f94ULL, + 0x44dada4fda9e95a9ULL, 0xa258587d58fa25b0ULL, 0xcfc9c903c906ca8fULL, + 0x7c2929a429558d52ULL, 0x5a0a0a280a502214ULL, 0x50b1b1feb1e14f7fULL, + 0xc9a0a0baa0691a5dULL, 0x146b6bb16b7fdad6ULL, 0xd985852e855cab17ULL, + 0x3cbdbdcebd817367ULL, 0x8f5d5d695dd234baULL, 0x9010104010805020ULL, + 0x07f4f4f7f4f303f5ULL, 0xddcbcb0bcb16c08bULL, 0xd33e3ef83eedc67cULL, + 0x2d0505140528110aULL, 0x78676781671fe6ceULL, 0x97e4e4b7e47353d5ULL, + 0x0227279c2725bb4eULL, 0x7341411941325882ULL, 0xa78b8b168b2c9d0bULL, + 0xf6a7a7a6a7510153ULL, 0xb27d7de97dcf94faULL, 0x4995956e95dcfb37ULL, + 0x56d8d847d88e9fadULL, 0x70fbfbcbfb8b30ebULL, 0xcdeeee9fee2371c1ULL, + 0xbb7c7ced7cc791f8ULL, 0x716666856617e3ccULL, 0x7bdddd53dda68ea7ULL, + 0xaf17175c17b84b2eULL, 0x454747014702468eULL, 0x1a9e9e429e84dc21ULL, + 0xd4caca0fca1ec589ULL, 0x582d2db42d75995aULL, 0x2ebfbfc6bf917963ULL, + 0x3f07071c07381b0eULL, 0xacadad8ead012347ULL, 0xb05a5a755aea2fb4ULL, + 0xef838336836cb51bULL, 0xb63333cc3385ff66ULL, 0x5c636391633ff2c6ULL, + 0x1202020802100a04ULL, 0x93aaaa92aa393849ULL, 0xde7171d971afa8e2ULL, + 0xc6c8c807c80ecf8dULL, 0xd119196419c87d32ULL, 0x3b49493949727092ULL, + 0x5fd9d943d9869aafULL, 0x31f2f2eff2c31df9ULL, 0xa8e3e3abe34b48dbULL, + 0xb95b5b715be22ab6ULL, 0xbc88881a8834920dULL, 0x3e9a9a529aa4c829ULL, + 0x0b262698262dbe4cULL, 0xbf3232c8328dfa64ULL, 0x59b0b0fab0e94a7dULL, + 0xf2e9e983e91b6acfULL, 0x770f0f3c0f78331eULL, 0x33d5d573d5e6a6b7ULL, + 0xf480803a8074ba1dULL, 0x27bebec2be997c61ULL, 0xebcdcd13cd26de87ULL, + 0x893434d034bde468ULL, 0x3248483d487a7590ULL, 0x54ffffdbffab24e3ULL, + 0x8d7a7af57af78ff4ULL, 0x6490907a90f4ea3dULL, 0x9d5f5f615fc23ebeULL, + 0x3d202080201da040ULL, 0x0f6868bd6867d5d0ULL, 0xca1a1a681ad07234ULL, + 0xb7aeae82ae192c41ULL, 0x7db4b4eab4c95e75ULL, 0xce54544d549a19a8ULL, + 0x7f93937693ece53bULL, 0x2f222288220daa44ULL, 0x6364648d6407e9c8ULL, + 0x2af1f1e3f1db12ffULL, 0xcc7373d173bfa2e6ULL, 0x8212124812905a24ULL, + 0x7a40401d403a5d80ULL, 0x4808082008402810ULL, 0x95c3c32bc356e89bULL, + 0xdfecec97ec337bc5ULL, 0x4ddbdb4bdb9690abULL, 0xc0a1a1bea1611f5fULL, + 0x918d8d0e8d1c8307ULL, 0xc83d3df43df5c97aULL, 0x5b97976697ccf133ULL, + 0x0000000000000000ULL, 0xf9cfcf1bcf36d483ULL, 0x6e2b2bac2b458756ULL, + 0xe17676c57697b3ecULL, 0xe68282328264b019ULL, 0x28d6d67fd6fea9b1ULL, + 0xc31b1b6c1bd87736ULL, 0x74b5b5eeb5c15b77ULL, 0xbeafaf86af112943ULL, + 0x1d6a6ab56a77dfd4ULL, 0xea50505d50ba0da0ULL, 0x5745450945124c8aULL, + 0x38f3f3ebf3cb18fbULL, 0xad3030c0309df060ULL, 0xc4efef9bef2b74c3ULL, + 0xda3f3ffc3fe5c37eULL, 0xc755554955921caaULL, 0xdba2a2b2a2791059ULL, + 0xe9eaea8fea0365c9ULL, 0x6a656589650feccaULL, 0x03babad2bab96869ULL, + 0x4a2f2fbc2f65935eULL, 0x8ec0c027c04ee79dULL, 0x60dede5fdebe81a1ULL, + 0xfc1c1c701ce06c38ULL, 0x46fdfdd3fdbb2ee7ULL, 0x1f4d4d294d52649aULL, + 0x7692927292e4e039ULL, 0xfa7575c9758fbceaULL, 0x3606061806301e0cULL, + 0xae8a8a128a249809ULL, 0x4bb2b2f2b2f94079ULL, 0x85e6e6bfe66359d1ULL, + 0x7e0e0e380e70361cULL, 0xe71f1f7c1ff8633eULL, 0x556262956237f7c4ULL, + 0x3ad4d477d4eea3b5ULL, 0x81a8a89aa829324dULL, 0x5296966296c4f431ULL, + 0x62f9f9c3f99b3aefULL, 0xa3c5c533c566f697ULL, 0x102525942535b14aULL, + 0xab59597959f220b2ULL, 0xd084842a8454ae15ULL, 0xc57272d572b7a7e4ULL, + 0xec3939e439d5dd72ULL, 0x164c4c2d4c5a6198ULL, 0x945e5e655eca3bbcULL, + 0x9f7878fd78e785f0ULL, 0xe53838e038ddd870ULL, 0x988c8c0a8c148605ULL, + 0x17d1d163d1c6b2bfULL, 0xe4a5a5aea5410b57ULL, 0xa1e2e2afe2434dd9ULL, + 0x4e616199612ff8c2ULL, 0x42b3b3f6b3f1457bULL, 0x342121842115a542ULL, + 0x089c9c4a9c94d625ULL, 0xee1e1e781ef0663cULL, 0x6143431143225286ULL, + 0xb1c7c73bc776fc93ULL, 0x4ffcfcd7fcb32be5ULL, 0x2404041004201408ULL, + 0xe351515951b208a2ULL, 0x2599995e99bcc72fULL, 0x226d6da96d4fc4daULL, + 0x650d0d340d68391aULL, 0x79fafacffa8335e9ULL, 0x69dfdf5bdfb684a3ULL, + 0xa97e7ee57ed79bfcULL, 0x19242490243db448ULL, 0xfe3b3bec3bc5d776ULL, + 0x9aabab96ab313d4bULL, 0xf0cece1fce3ed181ULL, 0x9911114411885522ULL, + 0x838f8f068f0c8903ULL, 0x044e4e254e4a6b9cULL, 0x66b7b7e6b7d15173ULL, + 0xe0ebeb8beb0b60cbULL, 0xc13c3cf03cfdcc78ULL, 0xfd81813e817cbf1fULL, + 0x4094946a94d4fe35ULL, 0x1cf7f7fbf7eb0cf3ULL, 0x18b9b9deb9a1676fULL, + 0x8b13134c13985f26ULL, 0x512c2cb02c7d9c58ULL, 0x05d3d36bd3d6b8bbULL, + 0x8ce7e7bbe76b5cd3ULL, 0x396e6ea56e57cbdcULL, 0xaac4c437c46ef395ULL, + 0x1b03030c03180f06ULL, 0xdc565645568a13acULL, 0x5e44440d441a4988ULL, + 0xa07f7fe17fdf9efeULL, 0x88a9a99ea921374fULL, 0x672a2aa82a4d8254ULL, + 0x0abbbbd6bbb16d6bULL, 0x87c1c123c146e29fULL, 0xf153535153a202a6ULL, + 0x72dcdc57dcae8ba5ULL, 0x530b0b2c0b582716ULL, 0x019d9d4e9d9cd327ULL, + 0x2b6c6cad6c47c1d8ULL, 0xa43131c43195f562ULL, 0xf37474cd7487b9e8ULL, + 0x15f6f6fff6e309f1ULL, 0x4c464605460a438cULL, 0xa5acac8aac092645ULL, + 0xb589891e893c970fULL, 0xb414145014a04428ULL, 0xbae1e1a3e15b42dfULL, + 0xa616165816b04e2cULL, 0xf73a3ae83acdd274ULL, 0x066969b9696fd0d2ULL, + 0x4109092409482d12ULL, 0xd77070dd70a7ade0ULL, 0x6fb6b6e2b6d95471ULL, + 0x1ed0d067d0ceb7bdULL, 0xd6eded93ed3b7ec7ULL, 0xe2cccc17cc2edb85ULL, + 0x68424215422a5784ULL, 0x2c98985a98b4c22dULL, 0xeda4a4aaa4490e55ULL, + 0x752828a0285d8850ULL, 0x865c5c6d5cda31b8ULL, 0x6bf8f8c7f8933fedULL, + 0xc28686228644a411ULL, +}; + +static const u64 C2[256] = { + 0x30d818186018c078ULL, 0x462623238c2305afULL, 0x91b8c6c63fc67ef9ULL, + 0xcdfbe8e887e8136fULL, 0x13cb878726874ca1ULL, 0x6d11b8b8dab8a962ULL, + 0x0209010104010805ULL, 0x9e0d4f4f214f426eULL, 0x6c9b3636d836adeeULL, + 0x51ffa6a6a2a65904ULL, 0xb90cd2d26fd2debdULL, 0xf70ef5f5f3f5fb06ULL, + 0xf2967979f979ef80ULL, 0xde306f6fa16f5fceULL, 0x3f6d91917e91fcefULL, + 0xa4f852525552aa07ULL, 0xc04760609d6027fdULL, 0x6535bcbccabc8976ULL, + 0x2b379b9b569baccdULL, 0x018a8e8e028e048cULL, 0x5bd2a3a3b6a37115ULL, + 0x186c0c0c300c603cULL, 0xf6847b7bf17bff8aULL, 0x6a803535d435b5e1ULL, + 0x3af51d1d741de869ULL, 0xddb3e0e0a7e05347ULL, 0xb321d7d77bd7f6acULL, + 0x999cc2c22fc25eedULL, 0x5c432e2eb82e6d96ULL, 0x96294b4b314b627aULL, + 0xe15dfefedffea321ULL, 0xaed5575741578216ULL, 0x2abd15155415a841ULL, + 0xeee87777c1779fb6ULL, 0x6e923737dc37a5ebULL, 0xd79ee5e5b3e57b56ULL, + 0x23139f9f469f8cd9ULL, 0xfd23f0f0e7f0d317ULL, 0x94204a4a354a6a7fULL, + 0xa944dada4fda9e95ULL, 0xb0a258587d58fa25ULL, 0x8fcfc9c903c906caULL, + 0x527c2929a429558dULL, 0x145a0a0a280a5022ULL, 0x7f50b1b1feb1e14fULL, + 0x5dc9a0a0baa0691aULL, 0xd6146b6bb16b7fdaULL, 0x17d985852e855cabULL, + 0x673cbdbdcebd8173ULL, 0xba8f5d5d695dd234ULL, 0x2090101040108050ULL, + 0xf507f4f4f7f4f303ULL, 0x8bddcbcb0bcb16c0ULL, 0x7cd33e3ef83eedc6ULL, + 0x0a2d050514052811ULL, 0xce78676781671fe6ULL, 0xd597e4e4b7e47353ULL, + 0x4e0227279c2725bbULL, 0x8273414119413258ULL, 0x0ba78b8b168b2c9dULL, + 0x53f6a7a7a6a75101ULL, 0xfab27d7de97dcf94ULL, 0x374995956e95dcfbULL, + 0xad56d8d847d88e9fULL, 0xeb70fbfbcbfb8b30ULL, 0xc1cdeeee9fee2371ULL, + 0xf8bb7c7ced7cc791ULL, 0xcc716666856617e3ULL, 0xa77bdddd53dda68eULL, + 0x2eaf17175c17b84bULL, 0x8e45474701470246ULL, 0x211a9e9e429e84dcULL, + 0x89d4caca0fca1ec5ULL, 0x5a582d2db42d7599ULL, 0x632ebfbfc6bf9179ULL, + 0x0e3f07071c07381bULL, 0x47acadad8ead0123ULL, 0xb4b05a5a755aea2fULL, + 0x1bef838336836cb5ULL, 0x66b63333cc3385ffULL, 0xc65c636391633ff2ULL, + 0x041202020802100aULL, 0x4993aaaa92aa3938ULL, 0xe2de7171d971afa8ULL, + 0x8dc6c8c807c80ecfULL, 0x32d119196419c87dULL, 0x923b494939497270ULL, + 0xaf5fd9d943d9869aULL, 0xf931f2f2eff2c31dULL, 0xdba8e3e3abe34b48ULL, + 0xb6b95b5b715be22aULL, 0x0dbc88881a883492ULL, 0x293e9a9a529aa4c8ULL, + 0x4c0b262698262dbeULL, 0x64bf3232c8328dfaULL, 0x7d59b0b0fab0e94aULL, + 0xcff2e9e983e91b6aULL, 0x1e770f0f3c0f7833ULL, 0xb733d5d573d5e6a6ULL, + 0x1df480803a8074baULL, 0x6127bebec2be997cULL, 0x87ebcdcd13cd26deULL, + 0x68893434d034bde4ULL, 0x903248483d487a75ULL, 0xe354ffffdbffab24ULL, + 0xf48d7a7af57af78fULL, 0x3d6490907a90f4eaULL, 0xbe9d5f5f615fc23eULL, + 0x403d202080201da0ULL, 0xd00f6868bd6867d5ULL, 0x34ca1a1a681ad072ULL, + 0x41b7aeae82ae192cULL, 0x757db4b4eab4c95eULL, 0xa8ce54544d549a19ULL, + 0x3b7f93937693ece5ULL, 0x442f222288220daaULL, 0xc86364648d6407e9ULL, + 0xff2af1f1e3f1db12ULL, 0xe6cc7373d173bfa2ULL, 0x248212124812905aULL, + 0x807a40401d403a5dULL, 0x1048080820084028ULL, 0x9b95c3c32bc356e8ULL, + 0xc5dfecec97ec337bULL, 0xab4ddbdb4bdb9690ULL, 0x5fc0a1a1bea1611fULL, + 0x07918d8d0e8d1c83ULL, 0x7ac83d3df43df5c9ULL, 0x335b97976697ccf1ULL, + 0x0000000000000000ULL, 0x83f9cfcf1bcf36d4ULL, 0x566e2b2bac2b4587ULL, + 0xece17676c57697b3ULL, 0x19e68282328264b0ULL, 0xb128d6d67fd6fea9ULL, + 0x36c31b1b6c1bd877ULL, 0x7774b5b5eeb5c15bULL, 0x43beafaf86af1129ULL, + 0xd41d6a6ab56a77dfULL, 0xa0ea50505d50ba0dULL, 0x8a5745450945124cULL, + 0xfb38f3f3ebf3cb18ULL, 0x60ad3030c0309df0ULL, 0xc3c4efef9bef2b74ULL, + 0x7eda3f3ffc3fe5c3ULL, 0xaac755554955921cULL, 0x59dba2a2b2a27910ULL, + 0xc9e9eaea8fea0365ULL, 0xca6a656589650fecULL, 0x6903babad2bab968ULL, + 0x5e4a2f2fbc2f6593ULL, 0x9d8ec0c027c04ee7ULL, 0xa160dede5fdebe81ULL, + 0x38fc1c1c701ce06cULL, 0xe746fdfdd3fdbb2eULL, 0x9a1f4d4d294d5264ULL, + 0x397692927292e4e0ULL, 0xeafa7575c9758fbcULL, 0x0c3606061806301eULL, + 0x09ae8a8a128a2498ULL, 0x794bb2b2f2b2f940ULL, 0xd185e6e6bfe66359ULL, + 0x1c7e0e0e380e7036ULL, 0x3ee71f1f7c1ff863ULL, 0xc4556262956237f7ULL, + 0xb53ad4d477d4eea3ULL, 0x4d81a8a89aa82932ULL, 0x315296966296c4f4ULL, + 0xef62f9f9c3f99b3aULL, 0x97a3c5c533c566f6ULL, 0x4a102525942535b1ULL, + 0xb2ab59597959f220ULL, 0x15d084842a8454aeULL, 0xe4c57272d572b7a7ULL, + 0x72ec3939e439d5ddULL, 0x98164c4c2d4c5a61ULL, 0xbc945e5e655eca3bULL, + 0xf09f7878fd78e785ULL, 0x70e53838e038ddd8ULL, 0x05988c8c0a8c1486ULL, + 0xbf17d1d163d1c6b2ULL, 0x57e4a5a5aea5410bULL, 0xd9a1e2e2afe2434dULL, + 0xc24e616199612ff8ULL, 0x7b42b3b3f6b3f145ULL, 0x42342121842115a5ULL, + 0x25089c9c4a9c94d6ULL, 0x3cee1e1e781ef066ULL, 0x8661434311432252ULL, + 0x93b1c7c73bc776fcULL, 0xe54ffcfcd7fcb32bULL, 0x0824040410042014ULL, + 0xa2e351515951b208ULL, 0x2f2599995e99bcc7ULL, 0xda226d6da96d4fc4ULL, + 0x1a650d0d340d6839ULL, 0xe979fafacffa8335ULL, 0xa369dfdf5bdfb684ULL, + 0xfca97e7ee57ed79bULL, 0x4819242490243db4ULL, 0x76fe3b3bec3bc5d7ULL, + 0x4b9aabab96ab313dULL, 0x81f0cece1fce3ed1ULL, 0x2299111144118855ULL, + 0x03838f8f068f0c89ULL, 0x9c044e4e254e4a6bULL, 0x7366b7b7e6b7d151ULL, + 0xcbe0ebeb8beb0b60ULL, 0x78c13c3cf03cfdccULL, 0x1ffd81813e817cbfULL, + 0x354094946a94d4feULL, 0xf31cf7f7fbf7eb0cULL, 0x6f18b9b9deb9a167ULL, + 0x268b13134c13985fULL, 0x58512c2cb02c7d9cULL, 0xbb05d3d36bd3d6b8ULL, + 0xd38ce7e7bbe76b5cULL, 0xdc396e6ea56e57cbULL, 0x95aac4c437c46ef3ULL, + 0x061b03030c03180fULL, 0xacdc565645568a13ULL, 0x885e44440d441a49ULL, + 0xfea07f7fe17fdf9eULL, 0x4f88a9a99ea92137ULL, 0x54672a2aa82a4d82ULL, + 0x6b0abbbbd6bbb16dULL, 0x9f87c1c123c146e2ULL, 0xa6f153535153a202ULL, + 0xa572dcdc57dcae8bULL, 0x16530b0b2c0b5827ULL, 0x27019d9d4e9d9cd3ULL, + 0xd82b6c6cad6c47c1ULL, 0x62a43131c43195f5ULL, 0xe8f37474cd7487b9ULL, + 0xf115f6f6fff6e309ULL, 0x8c4c464605460a43ULL, 0x45a5acac8aac0926ULL, + 0x0fb589891e893c97ULL, 0x28b414145014a044ULL, 0xdfbae1e1a3e15b42ULL, + 0x2ca616165816b04eULL, 0x74f73a3ae83acdd2ULL, 0xd2066969b9696fd0ULL, + 0x124109092409482dULL, 0xe0d77070dd70a7adULL, 0x716fb6b6e2b6d954ULL, + 0xbd1ed0d067d0ceb7ULL, 0xc7d6eded93ed3b7eULL, 0x85e2cccc17cc2edbULL, + 0x8468424215422a57ULL, 0x2d2c98985a98b4c2ULL, 0x55eda4a4aaa4490eULL, + 0x50752828a0285d88ULL, 0xb8865c5c6d5cda31ULL, 0xed6bf8f8c7f8933fULL, + 0x11c28686228644a4ULL, +}; + +static const u64 C3[256] = { + 0x7830d818186018c0ULL, 0xaf462623238c2305ULL, 0xf991b8c6c63fc67eULL, + 0x6fcdfbe8e887e813ULL, 0xa113cb878726874cULL, 0x626d11b8b8dab8a9ULL, + 0x0502090101040108ULL, 0x6e9e0d4f4f214f42ULL, 0xee6c9b3636d836adULL, + 0x0451ffa6a6a2a659ULL, 0xbdb90cd2d26fd2deULL, 0x06f70ef5f5f3f5fbULL, + 0x80f2967979f979efULL, 0xcede306f6fa16f5fULL, 0xef3f6d91917e91fcULL, + 0x07a4f852525552aaULL, 0xfdc04760609d6027ULL, 0x766535bcbccabc89ULL, + 0xcd2b379b9b569bacULL, 0x8c018a8e8e028e04ULL, 0x155bd2a3a3b6a371ULL, + 0x3c186c0c0c300c60ULL, 0x8af6847b7bf17bffULL, 0xe16a803535d435b5ULL, + 0x693af51d1d741de8ULL, 0x47ddb3e0e0a7e053ULL, 0xacb321d7d77bd7f6ULL, + 0xed999cc2c22fc25eULL, 0x965c432e2eb82e6dULL, 0x7a96294b4b314b62ULL, + 0x21e15dfefedffea3ULL, 0x16aed55757415782ULL, 0x412abd15155415a8ULL, + 0xb6eee87777c1779fULL, 0xeb6e923737dc37a5ULL, 0x56d79ee5e5b3e57bULL, + 0xd923139f9f469f8cULL, 0x17fd23f0f0e7f0d3ULL, 0x7f94204a4a354a6aULL, + 0x95a944dada4fda9eULL, 0x25b0a258587d58faULL, 0xca8fcfc9c903c906ULL, + 0x8d527c2929a42955ULL, 0x22145a0a0a280a50ULL, 0x4f7f50b1b1feb1e1ULL, + 0x1a5dc9a0a0baa069ULL, 0xdad6146b6bb16b7fULL, 0xab17d985852e855cULL, + 0x73673cbdbdcebd81ULL, 0x34ba8f5d5d695dd2ULL, 0x5020901010401080ULL, + 0x03f507f4f4f7f4f3ULL, 0xc08bddcbcb0bcb16ULL, 0xc67cd33e3ef83eedULL, + 0x110a2d0505140528ULL, 0xe6ce78676781671fULL, 0x53d597e4e4b7e473ULL, + 0xbb4e0227279c2725ULL, 0x5882734141194132ULL, 0x9d0ba78b8b168b2cULL, + 0x0153f6a7a7a6a751ULL, 0x94fab27d7de97dcfULL, 0xfb374995956e95dcULL, + 0x9fad56d8d847d88eULL, 0x30eb70fbfbcbfb8bULL, 0x71c1cdeeee9fee23ULL, + 0x91f8bb7c7ced7cc7ULL, 0xe3cc716666856617ULL, 0x8ea77bdddd53dda6ULL, + 0x4b2eaf17175c17b8ULL, 0x468e454747014702ULL, 0xdc211a9e9e429e84ULL, + 0xc589d4caca0fca1eULL, 0x995a582d2db42d75ULL, 0x79632ebfbfc6bf91ULL, + 0x1b0e3f07071c0738ULL, 0x2347acadad8ead01ULL, 0x2fb4b05a5a755aeaULL, + 0xb51bef838336836cULL, 0xff66b63333cc3385ULL, 0xf2c65c636391633fULL, + 0x0a04120202080210ULL, 0x384993aaaa92aa39ULL, 0xa8e2de7171d971afULL, + 0xcf8dc6c8c807c80eULL, 0x7d32d119196419c8ULL, 0x70923b4949394972ULL, + 0x9aaf5fd9d943d986ULL, 0x1df931f2f2eff2c3ULL, 0x48dba8e3e3abe34bULL, + 0x2ab6b95b5b715be2ULL, 0x920dbc88881a8834ULL, 0xc8293e9a9a529aa4ULL, + 0xbe4c0b262698262dULL, 0xfa64bf3232c8328dULL, 0x4a7d59b0b0fab0e9ULL, + 0x6acff2e9e983e91bULL, 0x331e770f0f3c0f78ULL, 0xa6b733d5d573d5e6ULL, + 0xba1df480803a8074ULL, 0x7c6127bebec2be99ULL, 0xde87ebcdcd13cd26ULL, + 0xe468893434d034bdULL, 0x75903248483d487aULL, 0x24e354ffffdbffabULL, + 0x8ff48d7a7af57af7ULL, 0xea3d6490907a90f4ULL, 0x3ebe9d5f5f615fc2ULL, + 0xa0403d202080201dULL, 0xd5d00f6868bd6867ULL, 0x7234ca1a1a681ad0ULL, + 0x2c41b7aeae82ae19ULL, 0x5e757db4b4eab4c9ULL, 0x19a8ce54544d549aULL, + 0xe53b7f93937693ecULL, 0xaa442f222288220dULL, 0xe9c86364648d6407ULL, + 0x12ff2af1f1e3f1dbULL, 0xa2e6cc7373d173bfULL, 0x5a24821212481290ULL, + 0x5d807a40401d403aULL, 0x2810480808200840ULL, 0xe89b95c3c32bc356ULL, + 0x7bc5dfecec97ec33ULL, 0x90ab4ddbdb4bdb96ULL, 0x1f5fc0a1a1bea161ULL, + 0x8307918d8d0e8d1cULL, 0xc97ac83d3df43df5ULL, 0xf1335b97976697ccULL, + 0x0000000000000000ULL, 0xd483f9cfcf1bcf36ULL, 0x87566e2b2bac2b45ULL, + 0xb3ece17676c57697ULL, 0xb019e68282328264ULL, 0xa9b128d6d67fd6feULL, + 0x7736c31b1b6c1bd8ULL, 0x5b7774b5b5eeb5c1ULL, 0x2943beafaf86af11ULL, + 0xdfd41d6a6ab56a77ULL, 0x0da0ea50505d50baULL, 0x4c8a574545094512ULL, + 0x18fb38f3f3ebf3cbULL, 0xf060ad3030c0309dULL, 0x74c3c4efef9bef2bULL, + 0xc37eda3f3ffc3fe5ULL, 0x1caac75555495592ULL, 0x1059dba2a2b2a279ULL, + 0x65c9e9eaea8fea03ULL, 0xecca6a656589650fULL, 0x686903babad2bab9ULL, + 0x935e4a2f2fbc2f65ULL, 0xe79d8ec0c027c04eULL, 0x81a160dede5fdebeULL, + 0x6c38fc1c1c701ce0ULL, 0x2ee746fdfdd3fdbbULL, 0x649a1f4d4d294d52ULL, + 0xe0397692927292e4ULL, 0xbceafa7575c9758fULL, 0x1e0c360606180630ULL, + 0x9809ae8a8a128a24ULL, 0x40794bb2b2f2b2f9ULL, 0x59d185e6e6bfe663ULL, + 0x361c7e0e0e380e70ULL, 0x633ee71f1f7c1ff8ULL, 0xf7c4556262956237ULL, + 0xa3b53ad4d477d4eeULL, 0x324d81a8a89aa829ULL, 0xf4315296966296c4ULL, + 0x3aef62f9f9c3f99bULL, 0xf697a3c5c533c566ULL, 0xb14a102525942535ULL, + 0x20b2ab59597959f2ULL, 0xae15d084842a8454ULL, 0xa7e4c57272d572b7ULL, + 0xdd72ec3939e439d5ULL, 0x6198164c4c2d4c5aULL, 0x3bbc945e5e655ecaULL, + 0x85f09f7878fd78e7ULL, 0xd870e53838e038ddULL, 0x8605988c8c0a8c14ULL, + 0xb2bf17d1d163d1c6ULL, 0x0b57e4a5a5aea541ULL, 0x4dd9a1e2e2afe243ULL, + 0xf8c24e616199612fULL, 0x457b42b3b3f6b3f1ULL, 0xa542342121842115ULL, + 0xd625089c9c4a9c94ULL, 0x663cee1e1e781ef0ULL, 0x5286614343114322ULL, + 0xfc93b1c7c73bc776ULL, 0x2be54ffcfcd7fcb3ULL, 0x1408240404100420ULL, + 0x08a2e351515951b2ULL, 0xc72f2599995e99bcULL, 0xc4da226d6da96d4fULL, + 0x391a650d0d340d68ULL, 0x35e979fafacffa83ULL, 0x84a369dfdf5bdfb6ULL, + 0x9bfca97e7ee57ed7ULL, 0xb44819242490243dULL, 0xd776fe3b3bec3bc5ULL, + 0x3d4b9aabab96ab31ULL, 0xd181f0cece1fce3eULL, 0x5522991111441188ULL, + 0x8903838f8f068f0cULL, 0x6b9c044e4e254e4aULL, 0x517366b7b7e6b7d1ULL, + 0x60cbe0ebeb8beb0bULL, 0xcc78c13c3cf03cfdULL, 0xbf1ffd81813e817cULL, + 0xfe354094946a94d4ULL, 0x0cf31cf7f7fbf7ebULL, 0x676f18b9b9deb9a1ULL, + 0x5f268b13134c1398ULL, 0x9c58512c2cb02c7dULL, 0xb8bb05d3d36bd3d6ULL, + 0x5cd38ce7e7bbe76bULL, 0xcbdc396e6ea56e57ULL, 0xf395aac4c437c46eULL, + 0x0f061b03030c0318ULL, 0x13acdc565645568aULL, 0x49885e44440d441aULL, + 0x9efea07f7fe17fdfULL, 0x374f88a9a99ea921ULL, 0x8254672a2aa82a4dULL, + 0x6d6b0abbbbd6bbb1ULL, 0xe29f87c1c123c146ULL, 0x02a6f153535153a2ULL, + 0x8ba572dcdc57dcaeULL, 0x2716530b0b2c0b58ULL, 0xd327019d9d4e9d9cULL, + 0xc1d82b6c6cad6c47ULL, 0xf562a43131c43195ULL, 0xb9e8f37474cd7487ULL, + 0x09f115f6f6fff6e3ULL, 0x438c4c464605460aULL, 0x2645a5acac8aac09ULL, + 0x970fb589891e893cULL, 0x4428b414145014a0ULL, 0x42dfbae1e1a3e15bULL, + 0x4e2ca616165816b0ULL, 0xd274f73a3ae83acdULL, 0xd0d2066969b9696fULL, + 0x2d12410909240948ULL, 0xade0d77070dd70a7ULL, 0x54716fb6b6e2b6d9ULL, + 0xb7bd1ed0d067d0ceULL, 0x7ec7d6eded93ed3bULL, 0xdb85e2cccc17cc2eULL, + 0x578468424215422aULL, 0xc22d2c98985a98b4ULL, 0x0e55eda4a4aaa449ULL, + 0x8850752828a0285dULL, 0x31b8865c5c6d5cdaULL, 0x3fed6bf8f8c7f893ULL, + 0xa411c28686228644ULL, +}; + +static const u64 C4[256] = { + 0xc07830d818186018ULL, 0x05af462623238c23ULL, 0x7ef991b8c6c63fc6ULL, + 0x136fcdfbe8e887e8ULL, 0x4ca113cb87872687ULL, 0xa9626d11b8b8dab8ULL, + 0x0805020901010401ULL, 0x426e9e0d4f4f214fULL, 0xadee6c9b3636d836ULL, + 0x590451ffa6a6a2a6ULL, 0xdebdb90cd2d26fd2ULL, 0xfb06f70ef5f5f3f5ULL, + 0xef80f2967979f979ULL, 0x5fcede306f6fa16fULL, 0xfcef3f6d91917e91ULL, + 0xaa07a4f852525552ULL, 0x27fdc04760609d60ULL, 0x89766535bcbccabcULL, + 0xaccd2b379b9b569bULL, 0x048c018a8e8e028eULL, 0x71155bd2a3a3b6a3ULL, + 0x603c186c0c0c300cULL, 0xff8af6847b7bf17bULL, 0xb5e16a803535d435ULL, + 0xe8693af51d1d741dULL, 0x5347ddb3e0e0a7e0ULL, 0xf6acb321d7d77bd7ULL, + 0x5eed999cc2c22fc2ULL, 0x6d965c432e2eb82eULL, 0x627a96294b4b314bULL, + 0xa321e15dfefedffeULL, 0x8216aed557574157ULL, 0xa8412abd15155415ULL, + 0x9fb6eee87777c177ULL, 0xa5eb6e923737dc37ULL, 0x7b56d79ee5e5b3e5ULL, + 0x8cd923139f9f469fULL, 0xd317fd23f0f0e7f0ULL, 0x6a7f94204a4a354aULL, + 0x9e95a944dada4fdaULL, 0xfa25b0a258587d58ULL, 0x06ca8fcfc9c903c9ULL, + 0x558d527c2929a429ULL, 0x5022145a0a0a280aULL, 0xe14f7f50b1b1feb1ULL, + 0x691a5dc9a0a0baa0ULL, 0x7fdad6146b6bb16bULL, 0x5cab17d985852e85ULL, + 0x8173673cbdbdcebdULL, 0xd234ba8f5d5d695dULL, 0x8050209010104010ULL, + 0xf303f507f4f4f7f4ULL, 0x16c08bddcbcb0bcbULL, 0xedc67cd33e3ef83eULL, + 0x28110a2d05051405ULL, 0x1fe6ce7867678167ULL, 0x7353d597e4e4b7e4ULL, + 0x25bb4e0227279c27ULL, 0x3258827341411941ULL, 0x2c9d0ba78b8b168bULL, + 0x510153f6a7a7a6a7ULL, 0xcf94fab27d7de97dULL, 0xdcfb374995956e95ULL, + 0x8e9fad56d8d847d8ULL, 0x8b30eb70fbfbcbfbULL, 0x2371c1cdeeee9feeULL, + 0xc791f8bb7c7ced7cULL, 0x17e3cc7166668566ULL, 0xa68ea77bdddd53ddULL, + 0xb84b2eaf17175c17ULL, 0x02468e4547470147ULL, 0x84dc211a9e9e429eULL, + 0x1ec589d4caca0fcaULL, 0x75995a582d2db42dULL, 0x9179632ebfbfc6bfULL, + 0x381b0e3f07071c07ULL, 0x012347acadad8eadULL, 0xea2fb4b05a5a755aULL, + 0x6cb51bef83833683ULL, 0x85ff66b63333cc33ULL, 0x3ff2c65c63639163ULL, + 0x100a041202020802ULL, 0x39384993aaaa92aaULL, 0xafa8e2de7171d971ULL, + 0x0ecf8dc6c8c807c8ULL, 0xc87d32d119196419ULL, 0x7270923b49493949ULL, + 0x869aaf5fd9d943d9ULL, 0xc31df931f2f2eff2ULL, 0x4b48dba8e3e3abe3ULL, + 0xe22ab6b95b5b715bULL, 0x34920dbc88881a88ULL, 0xa4c8293e9a9a529aULL, + 0x2dbe4c0b26269826ULL, 0x8dfa64bf3232c832ULL, 0xe94a7d59b0b0fab0ULL, + 0x1b6acff2e9e983e9ULL, 0x78331e770f0f3c0fULL, 0xe6a6b733d5d573d5ULL, + 0x74ba1df480803a80ULL, 0x997c6127bebec2beULL, 0x26de87ebcdcd13cdULL, + 0xbde468893434d034ULL, 0x7a75903248483d48ULL, 0xab24e354ffffdbffULL, + 0xf78ff48d7a7af57aULL, 0xf4ea3d6490907a90ULL, 0xc23ebe9d5f5f615fULL, + 0x1da0403d20208020ULL, 0x67d5d00f6868bd68ULL, 0xd07234ca1a1a681aULL, + 0x192c41b7aeae82aeULL, 0xc95e757db4b4eab4ULL, 0x9a19a8ce54544d54ULL, + 0xece53b7f93937693ULL, 0x0daa442f22228822ULL, 0x07e9c86364648d64ULL, + 0xdb12ff2af1f1e3f1ULL, 0xbfa2e6cc7373d173ULL, 0x905a248212124812ULL, + 0x3a5d807a40401d40ULL, 0x4028104808082008ULL, 0x56e89b95c3c32bc3ULL, + 0x337bc5dfecec97ecULL, 0x9690ab4ddbdb4bdbULL, 0x611f5fc0a1a1bea1ULL, + 0x1c8307918d8d0e8dULL, 0xf5c97ac83d3df43dULL, 0xccf1335b97976697ULL, + 0x0000000000000000ULL, 0x36d483f9cfcf1bcfULL, 0x4587566e2b2bac2bULL, + 0x97b3ece17676c576ULL, 0x64b019e682823282ULL, 0xfea9b128d6d67fd6ULL, + 0xd87736c31b1b6c1bULL, 0xc15b7774b5b5eeb5ULL, 0x112943beafaf86afULL, + 0x77dfd41d6a6ab56aULL, 0xba0da0ea50505d50ULL, 0x124c8a5745450945ULL, + 0xcb18fb38f3f3ebf3ULL, 0x9df060ad3030c030ULL, 0x2b74c3c4efef9befULL, + 0xe5c37eda3f3ffc3fULL, 0x921caac755554955ULL, 0x791059dba2a2b2a2ULL, + 0x0365c9e9eaea8feaULL, 0x0fecca6a65658965ULL, 0xb9686903babad2baULL, + 0x65935e4a2f2fbc2fULL, 0x4ee79d8ec0c027c0ULL, 0xbe81a160dede5fdeULL, + 0xe06c38fc1c1c701cULL, 0xbb2ee746fdfdd3fdULL, 0x52649a1f4d4d294dULL, + 0xe4e0397692927292ULL, 0x8fbceafa7575c975ULL, 0x301e0c3606061806ULL, + 0x249809ae8a8a128aULL, 0xf940794bb2b2f2b2ULL, 0x6359d185e6e6bfe6ULL, + 0x70361c7e0e0e380eULL, 0xf8633ee71f1f7c1fULL, 0x37f7c45562629562ULL, + 0xeea3b53ad4d477d4ULL, 0x29324d81a8a89aa8ULL, 0xc4f4315296966296ULL, + 0x9b3aef62f9f9c3f9ULL, 0x66f697a3c5c533c5ULL, 0x35b14a1025259425ULL, + 0xf220b2ab59597959ULL, 0x54ae15d084842a84ULL, 0xb7a7e4c57272d572ULL, + 0xd5dd72ec3939e439ULL, 0x5a6198164c4c2d4cULL, 0xca3bbc945e5e655eULL, + 0xe785f09f7878fd78ULL, 0xddd870e53838e038ULL, 0x148605988c8c0a8cULL, + 0xc6b2bf17d1d163d1ULL, 0x410b57e4a5a5aea5ULL, 0x434dd9a1e2e2afe2ULL, + 0x2ff8c24e61619961ULL, 0xf1457b42b3b3f6b3ULL, 0x15a5423421218421ULL, + 0x94d625089c9c4a9cULL, 0xf0663cee1e1e781eULL, 0x2252866143431143ULL, + 0x76fc93b1c7c73bc7ULL, 0xb32be54ffcfcd7fcULL, 0x2014082404041004ULL, + 0xb208a2e351515951ULL, 0xbcc72f2599995e99ULL, 0x4fc4da226d6da96dULL, + 0x68391a650d0d340dULL, 0x8335e979fafacffaULL, 0xb684a369dfdf5bdfULL, + 0xd79bfca97e7ee57eULL, 0x3db4481924249024ULL, 0xc5d776fe3b3bec3bULL, + 0x313d4b9aabab96abULL, 0x3ed181f0cece1fceULL, 0x8855229911114411ULL, + 0x0c8903838f8f068fULL, 0x4a6b9c044e4e254eULL, 0xd1517366b7b7e6b7ULL, + 0x0b60cbe0ebeb8bebULL, 0xfdcc78c13c3cf03cULL, 0x7cbf1ffd81813e81ULL, + 0xd4fe354094946a94ULL, 0xeb0cf31cf7f7fbf7ULL, 0xa1676f18b9b9deb9ULL, + 0x985f268b13134c13ULL, 0x7d9c58512c2cb02cULL, 0xd6b8bb05d3d36bd3ULL, + 0x6b5cd38ce7e7bbe7ULL, 0x57cbdc396e6ea56eULL, 0x6ef395aac4c437c4ULL, + 0x180f061b03030c03ULL, 0x8a13acdc56564556ULL, 0x1a49885e44440d44ULL, + 0xdf9efea07f7fe17fULL, 0x21374f88a9a99ea9ULL, 0x4d8254672a2aa82aULL, + 0xb16d6b0abbbbd6bbULL, 0x46e29f87c1c123c1ULL, 0xa202a6f153535153ULL, + 0xae8ba572dcdc57dcULL, 0x582716530b0b2c0bULL, 0x9cd327019d9d4e9dULL, + 0x47c1d82b6c6cad6cULL, 0x95f562a43131c431ULL, 0x87b9e8f37474cd74ULL, + 0xe309f115f6f6fff6ULL, 0x0a438c4c46460546ULL, 0x092645a5acac8aacULL, + 0x3c970fb589891e89ULL, 0xa04428b414145014ULL, 0x5b42dfbae1e1a3e1ULL, + 0xb04e2ca616165816ULL, 0xcdd274f73a3ae83aULL, 0x6fd0d2066969b969ULL, + 0x482d124109092409ULL, 0xa7ade0d77070dd70ULL, 0xd954716fb6b6e2b6ULL, + 0xceb7bd1ed0d067d0ULL, 0x3b7ec7d6eded93edULL, 0x2edb85e2cccc17ccULL, + 0x2a57846842421542ULL, 0xb4c22d2c98985a98ULL, 0x490e55eda4a4aaa4ULL, + 0x5d8850752828a028ULL, 0xda31b8865c5c6d5cULL, 0x933fed6bf8f8c7f8ULL, + 0x44a411c286862286ULL, +}; + +static const u64 C5[256] = { + 0x18c07830d8181860ULL, 0x2305af462623238cULL, 0xc67ef991b8c6c63fULL, + 0xe8136fcdfbe8e887ULL, 0x874ca113cb878726ULL, 0xb8a9626d11b8b8daULL, + 0x0108050209010104ULL, 0x4f426e9e0d4f4f21ULL, 0x36adee6c9b3636d8ULL, + 0xa6590451ffa6a6a2ULL, 0xd2debdb90cd2d26fULL, 0xf5fb06f70ef5f5f3ULL, + 0x79ef80f2967979f9ULL, 0x6f5fcede306f6fa1ULL, 0x91fcef3f6d91917eULL, + 0x52aa07a4f8525255ULL, 0x6027fdc04760609dULL, 0xbc89766535bcbccaULL, + 0x9baccd2b379b9b56ULL, 0x8e048c018a8e8e02ULL, 0xa371155bd2a3a3b6ULL, + 0x0c603c186c0c0c30ULL, 0x7bff8af6847b7bf1ULL, 0x35b5e16a803535d4ULL, + 0x1de8693af51d1d74ULL, 0xe05347ddb3e0e0a7ULL, 0xd7f6acb321d7d77bULL, + 0xc25eed999cc2c22fULL, 0x2e6d965c432e2eb8ULL, 0x4b627a96294b4b31ULL, + 0xfea321e15dfefedfULL, 0x578216aed5575741ULL, 0x15a8412abd151554ULL, + 0x779fb6eee87777c1ULL, 0x37a5eb6e923737dcULL, 0xe57b56d79ee5e5b3ULL, + 0x9f8cd923139f9f46ULL, 0xf0d317fd23f0f0e7ULL, 0x4a6a7f94204a4a35ULL, + 0xda9e95a944dada4fULL, 0x58fa25b0a258587dULL, 0xc906ca8fcfc9c903ULL, + 0x29558d527c2929a4ULL, 0x0a5022145a0a0a28ULL, 0xb1e14f7f50b1b1feULL, + 0xa0691a5dc9a0a0baULL, 0x6b7fdad6146b6bb1ULL, 0x855cab17d985852eULL, + 0xbd8173673cbdbdceULL, 0x5dd234ba8f5d5d69ULL, 0x1080502090101040ULL, + 0xf4f303f507f4f4f7ULL, 0xcb16c08bddcbcb0bULL, 0x3eedc67cd33e3ef8ULL, + 0x0528110a2d050514ULL, 0x671fe6ce78676781ULL, 0xe47353d597e4e4b7ULL, + 0x2725bb4e0227279cULL, 0x4132588273414119ULL, 0x8b2c9d0ba78b8b16ULL, + 0xa7510153f6a7a7a6ULL, 0x7dcf94fab27d7de9ULL, 0x95dcfb374995956eULL, + 0xd88e9fad56d8d847ULL, 0xfb8b30eb70fbfbcbULL, 0xee2371c1cdeeee9fULL, + 0x7cc791f8bb7c7cedULL, 0x6617e3cc71666685ULL, 0xdda68ea77bdddd53ULL, + 0x17b84b2eaf17175cULL, 0x4702468e45474701ULL, 0x9e84dc211a9e9e42ULL, + 0xca1ec589d4caca0fULL, 0x2d75995a582d2db4ULL, 0xbf9179632ebfbfc6ULL, + 0x07381b0e3f07071cULL, 0xad012347acadad8eULL, 0x5aea2fb4b05a5a75ULL, + 0x836cb51bef838336ULL, 0x3385ff66b63333ccULL, 0x633ff2c65c636391ULL, + 0x02100a0412020208ULL, 0xaa39384993aaaa92ULL, 0x71afa8e2de7171d9ULL, + 0xc80ecf8dc6c8c807ULL, 0x19c87d32d1191964ULL, 0x497270923b494939ULL, + 0xd9869aaf5fd9d943ULL, 0xf2c31df931f2f2efULL, 0xe34b48dba8e3e3abULL, + 0x5be22ab6b95b5b71ULL, 0x8834920dbc88881aULL, 0x9aa4c8293e9a9a52ULL, + 0x262dbe4c0b262698ULL, 0x328dfa64bf3232c8ULL, 0xb0e94a7d59b0b0faULL, + 0xe91b6acff2e9e983ULL, 0x0f78331e770f0f3cULL, 0xd5e6a6b733d5d573ULL, + 0x8074ba1df480803aULL, 0xbe997c6127bebec2ULL, 0xcd26de87ebcdcd13ULL, + 0x34bde468893434d0ULL, 0x487a75903248483dULL, 0xffab24e354ffffdbULL, + 0x7af78ff48d7a7af5ULL, 0x90f4ea3d6490907aULL, 0x5fc23ebe9d5f5f61ULL, + 0x201da0403d202080ULL, 0x6867d5d00f6868bdULL, 0x1ad07234ca1a1a68ULL, + 0xae192c41b7aeae82ULL, 0xb4c95e757db4b4eaULL, 0x549a19a8ce54544dULL, + 0x93ece53b7f939376ULL, 0x220daa442f222288ULL, 0x6407e9c86364648dULL, + 0xf1db12ff2af1f1e3ULL, 0x73bfa2e6cc7373d1ULL, 0x12905a2482121248ULL, + 0x403a5d807a40401dULL, 0x0840281048080820ULL, 0xc356e89b95c3c32bULL, + 0xec337bc5dfecec97ULL, 0xdb9690ab4ddbdb4bULL, 0xa1611f5fc0a1a1beULL, + 0x8d1c8307918d8d0eULL, 0x3df5c97ac83d3df4ULL, 0x97ccf1335b979766ULL, + 0x0000000000000000ULL, 0xcf36d483f9cfcf1bULL, 0x2b4587566e2b2bacULL, + 0x7697b3ece17676c5ULL, 0x8264b019e6828232ULL, 0xd6fea9b128d6d67fULL, + 0x1bd87736c31b1b6cULL, 0xb5c15b7774b5b5eeULL, 0xaf112943beafaf86ULL, + 0x6a77dfd41d6a6ab5ULL, 0x50ba0da0ea50505dULL, 0x45124c8a57454509ULL, + 0xf3cb18fb38f3f3ebULL, 0x309df060ad3030c0ULL, 0xef2b74c3c4efef9bULL, + 0x3fe5c37eda3f3ffcULL, 0x55921caac7555549ULL, 0xa2791059dba2a2b2ULL, + 0xea0365c9e9eaea8fULL, 0x650fecca6a656589ULL, 0xbab9686903babad2ULL, + 0x2f65935e4a2f2fbcULL, 0xc04ee79d8ec0c027ULL, 0xdebe81a160dede5fULL, + 0x1ce06c38fc1c1c70ULL, 0xfdbb2ee746fdfdd3ULL, 0x4d52649a1f4d4d29ULL, + 0x92e4e03976929272ULL, 0x758fbceafa7575c9ULL, 0x06301e0c36060618ULL, + 0x8a249809ae8a8a12ULL, 0xb2f940794bb2b2f2ULL, 0xe66359d185e6e6bfULL, + 0x0e70361c7e0e0e38ULL, 0x1ff8633ee71f1f7cULL, 0x6237f7c455626295ULL, + 0xd4eea3b53ad4d477ULL, 0xa829324d81a8a89aULL, 0x96c4f43152969662ULL, + 0xf99b3aef62f9f9c3ULL, 0xc566f697a3c5c533ULL, 0x2535b14a10252594ULL, + 0x59f220b2ab595979ULL, 0x8454ae15d084842aULL, 0x72b7a7e4c57272d5ULL, + 0x39d5dd72ec3939e4ULL, 0x4c5a6198164c4c2dULL, 0x5eca3bbc945e5e65ULL, + 0x78e785f09f7878fdULL, 0x38ddd870e53838e0ULL, 0x8c148605988c8c0aULL, + 0xd1c6b2bf17d1d163ULL, 0xa5410b57e4a5a5aeULL, 0xe2434dd9a1e2e2afULL, + 0x612ff8c24e616199ULL, 0xb3f1457b42b3b3f6ULL, 0x2115a54234212184ULL, + 0x9c94d625089c9c4aULL, 0x1ef0663cee1e1e78ULL, 0x4322528661434311ULL, + 0xc776fc93b1c7c73bULL, 0xfcb32be54ffcfcd7ULL, 0x0420140824040410ULL, + 0x51b208a2e3515159ULL, 0x99bcc72f2599995eULL, 0x6d4fc4da226d6da9ULL, + 0x0d68391a650d0d34ULL, 0xfa8335e979fafacfULL, 0xdfb684a369dfdf5bULL, + 0x7ed79bfca97e7ee5ULL, 0x243db44819242490ULL, 0x3bc5d776fe3b3becULL, + 0xab313d4b9aabab96ULL, 0xce3ed181f0cece1fULL, 0x1188552299111144ULL, + 0x8f0c8903838f8f06ULL, 0x4e4a6b9c044e4e25ULL, 0xb7d1517366b7b7e6ULL, + 0xeb0b60cbe0ebeb8bULL, 0x3cfdcc78c13c3cf0ULL, 0x817cbf1ffd81813eULL, + 0x94d4fe354094946aULL, 0xf7eb0cf31cf7f7fbULL, 0xb9a1676f18b9b9deULL, + 0x13985f268b13134cULL, 0x2c7d9c58512c2cb0ULL, 0xd3d6b8bb05d3d36bULL, + 0xe76b5cd38ce7e7bbULL, 0x6e57cbdc396e6ea5ULL, 0xc46ef395aac4c437ULL, + 0x03180f061b03030cULL, 0x568a13acdc565645ULL, 0x441a49885e44440dULL, + 0x7fdf9efea07f7fe1ULL, 0xa921374f88a9a99eULL, 0x2a4d8254672a2aa8ULL, + 0xbbb16d6b0abbbbd6ULL, 0xc146e29f87c1c123ULL, 0x53a202a6f1535351ULL, + 0xdcae8ba572dcdc57ULL, 0x0b582716530b0b2cULL, 0x9d9cd327019d9d4eULL, + 0x6c47c1d82b6c6cadULL, 0x3195f562a43131c4ULL, 0x7487b9e8f37474cdULL, + 0xf6e309f115f6f6ffULL, 0x460a438c4c464605ULL, 0xac092645a5acac8aULL, + 0x893c970fb589891eULL, 0x14a04428b4141450ULL, 0xe15b42dfbae1e1a3ULL, + 0x16b04e2ca6161658ULL, 0x3acdd274f73a3ae8ULL, 0x696fd0d2066969b9ULL, + 0x09482d1241090924ULL, 0x70a7ade0d77070ddULL, 0xb6d954716fb6b6e2ULL, + 0xd0ceb7bd1ed0d067ULL, 0xed3b7ec7d6eded93ULL, 0xcc2edb85e2cccc17ULL, + 0x422a578468424215ULL, 0x98b4c22d2c98985aULL, 0xa4490e55eda4a4aaULL, + 0x285d8850752828a0ULL, 0x5cda31b8865c5c6dULL, 0xf8933fed6bf8f8c7ULL, + 0x8644a411c2868622ULL, +}; + +static const u64 C6[256] = { + 0x6018c07830d81818ULL, 0x8c2305af46262323ULL, 0x3fc67ef991b8c6c6ULL, + 0x87e8136fcdfbe8e8ULL, 0x26874ca113cb8787ULL, 0xdab8a9626d11b8b8ULL, + 0x0401080502090101ULL, 0x214f426e9e0d4f4fULL, 0xd836adee6c9b3636ULL, + 0xa2a6590451ffa6a6ULL, 0x6fd2debdb90cd2d2ULL, 0xf3f5fb06f70ef5f5ULL, + 0xf979ef80f2967979ULL, 0xa16f5fcede306f6fULL, 0x7e91fcef3f6d9191ULL, + 0x5552aa07a4f85252ULL, 0x9d6027fdc0476060ULL, 0xcabc89766535bcbcULL, + 0x569baccd2b379b9bULL, 0x028e048c018a8e8eULL, 0xb6a371155bd2a3a3ULL, + 0x300c603c186c0c0cULL, 0xf17bff8af6847b7bULL, 0xd435b5e16a803535ULL, + 0x741de8693af51d1dULL, 0xa7e05347ddb3e0e0ULL, 0x7bd7f6acb321d7d7ULL, + 0x2fc25eed999cc2c2ULL, 0xb82e6d965c432e2eULL, 0x314b627a96294b4bULL, + 0xdffea321e15dfefeULL, 0x41578216aed55757ULL, 0x5415a8412abd1515ULL, + 0xc1779fb6eee87777ULL, 0xdc37a5eb6e923737ULL, 0xb3e57b56d79ee5e5ULL, + 0x469f8cd923139f9fULL, 0xe7f0d317fd23f0f0ULL, 0x354a6a7f94204a4aULL, + 0x4fda9e95a944dadaULL, 0x7d58fa25b0a25858ULL, 0x03c906ca8fcfc9c9ULL, + 0xa429558d527c2929ULL, 0x280a5022145a0a0aULL, 0xfeb1e14f7f50b1b1ULL, + 0xbaa0691a5dc9a0a0ULL, 0xb16b7fdad6146b6bULL, 0x2e855cab17d98585ULL, + 0xcebd8173673cbdbdULL, 0x695dd234ba8f5d5dULL, 0x4010805020901010ULL, + 0xf7f4f303f507f4f4ULL, 0x0bcb16c08bddcbcbULL, 0xf83eedc67cd33e3eULL, + 0x140528110a2d0505ULL, 0x81671fe6ce786767ULL, 0xb7e47353d597e4e4ULL, + 0x9c2725bb4e022727ULL, 0x1941325882734141ULL, 0x168b2c9d0ba78b8bULL, + 0xa6a7510153f6a7a7ULL, 0xe97dcf94fab27d7dULL, 0x6e95dcfb37499595ULL, + 0x47d88e9fad56d8d8ULL, 0xcbfb8b30eb70fbfbULL, 0x9fee2371c1cdeeeeULL, + 0xed7cc791f8bb7c7cULL, 0x856617e3cc716666ULL, 0x53dda68ea77bddddULL, + 0x5c17b84b2eaf1717ULL, 0x014702468e454747ULL, 0x429e84dc211a9e9eULL, + 0x0fca1ec589d4cacaULL, 0xb42d75995a582d2dULL, 0xc6bf9179632ebfbfULL, + 0x1c07381b0e3f0707ULL, 0x8ead012347acadadULL, 0x755aea2fb4b05a5aULL, + 0x36836cb51bef8383ULL, 0xcc3385ff66b63333ULL, 0x91633ff2c65c6363ULL, + 0x0802100a04120202ULL, 0x92aa39384993aaaaULL, 0xd971afa8e2de7171ULL, + 0x07c80ecf8dc6c8c8ULL, 0x6419c87d32d11919ULL, 0x39497270923b4949ULL, + 0x43d9869aaf5fd9d9ULL, 0xeff2c31df931f2f2ULL, 0xabe34b48dba8e3e3ULL, + 0x715be22ab6b95b5bULL, 0x1a8834920dbc8888ULL, 0x529aa4c8293e9a9aULL, + 0x98262dbe4c0b2626ULL, 0xc8328dfa64bf3232ULL, 0xfab0e94a7d59b0b0ULL, + 0x83e91b6acff2e9e9ULL, 0x3c0f78331e770f0fULL, 0x73d5e6a6b733d5d5ULL, + 0x3a8074ba1df48080ULL, 0xc2be997c6127bebeULL, 0x13cd26de87ebcdcdULL, + 0xd034bde468893434ULL, 0x3d487a7590324848ULL, 0xdbffab24e354ffffULL, + 0xf57af78ff48d7a7aULL, 0x7a90f4ea3d649090ULL, 0x615fc23ebe9d5f5fULL, + 0x80201da0403d2020ULL, 0xbd6867d5d00f6868ULL, 0x681ad07234ca1a1aULL, + 0x82ae192c41b7aeaeULL, 0xeab4c95e757db4b4ULL, 0x4d549a19a8ce5454ULL, + 0x7693ece53b7f9393ULL, 0x88220daa442f2222ULL, 0x8d6407e9c8636464ULL, + 0xe3f1db12ff2af1f1ULL, 0xd173bfa2e6cc7373ULL, 0x4812905a24821212ULL, + 0x1d403a5d807a4040ULL, 0x2008402810480808ULL, 0x2bc356e89b95c3c3ULL, + 0x97ec337bc5dfececULL, 0x4bdb9690ab4ddbdbULL, 0xbea1611f5fc0a1a1ULL, + 0x0e8d1c8307918d8dULL, 0xf43df5c97ac83d3dULL, 0x6697ccf1335b9797ULL, + 0x0000000000000000ULL, 0x1bcf36d483f9cfcfULL, 0xac2b4587566e2b2bULL, + 0xc57697b3ece17676ULL, 0x328264b019e68282ULL, 0x7fd6fea9b128d6d6ULL, + 0x6c1bd87736c31b1bULL, 0xeeb5c15b7774b5b5ULL, 0x86af112943beafafULL, + 0xb56a77dfd41d6a6aULL, 0x5d50ba0da0ea5050ULL, 0x0945124c8a574545ULL, + 0xebf3cb18fb38f3f3ULL, 0xc0309df060ad3030ULL, 0x9bef2b74c3c4efefULL, + 0xfc3fe5c37eda3f3fULL, 0x4955921caac75555ULL, 0xb2a2791059dba2a2ULL, + 0x8fea0365c9e9eaeaULL, 0x89650fecca6a6565ULL, 0xd2bab9686903babaULL, + 0xbc2f65935e4a2f2fULL, 0x27c04ee79d8ec0c0ULL, 0x5fdebe81a160dedeULL, + 0x701ce06c38fc1c1cULL, 0xd3fdbb2ee746fdfdULL, 0x294d52649a1f4d4dULL, + 0x7292e4e039769292ULL, 0xc9758fbceafa7575ULL, 0x1806301e0c360606ULL, + 0x128a249809ae8a8aULL, 0xf2b2f940794bb2b2ULL, 0xbfe66359d185e6e6ULL, + 0x380e70361c7e0e0eULL, 0x7c1ff8633ee71f1fULL, 0x956237f7c4556262ULL, + 0x77d4eea3b53ad4d4ULL, 0x9aa829324d81a8a8ULL, 0x6296c4f431529696ULL, + 0xc3f99b3aef62f9f9ULL, 0x33c566f697a3c5c5ULL, 0x942535b14a102525ULL, + 0x7959f220b2ab5959ULL, 0x2a8454ae15d08484ULL, 0xd572b7a7e4c57272ULL, + 0xe439d5dd72ec3939ULL, 0x2d4c5a6198164c4cULL, 0x655eca3bbc945e5eULL, + 0xfd78e785f09f7878ULL, 0xe038ddd870e53838ULL, 0x0a8c148605988c8cULL, + 0x63d1c6b2bf17d1d1ULL, 0xaea5410b57e4a5a5ULL, 0xafe2434dd9a1e2e2ULL, + 0x99612ff8c24e6161ULL, 0xf6b3f1457b42b3b3ULL, 0x842115a542342121ULL, + 0x4a9c94d625089c9cULL, 0x781ef0663cee1e1eULL, 0x1143225286614343ULL, + 0x3bc776fc93b1c7c7ULL, 0xd7fcb32be54ffcfcULL, 0x1004201408240404ULL, + 0x5951b208a2e35151ULL, 0x5e99bcc72f259999ULL, 0xa96d4fc4da226d6dULL, + 0x340d68391a650d0dULL, 0xcffa8335e979fafaULL, 0x5bdfb684a369dfdfULL, + 0xe57ed79bfca97e7eULL, 0x90243db448192424ULL, 0xec3bc5d776fe3b3bULL, + 0x96ab313d4b9aababULL, 0x1fce3ed181f0ceceULL, 0x4411885522991111ULL, + 0x068f0c8903838f8fULL, 0x254e4a6b9c044e4eULL, 0xe6b7d1517366b7b7ULL, + 0x8beb0b60cbe0ebebULL, 0xf03cfdcc78c13c3cULL, 0x3e817cbf1ffd8181ULL, + 0x6a94d4fe35409494ULL, 0xfbf7eb0cf31cf7f7ULL, 0xdeb9a1676f18b9b9ULL, + 0x4c13985f268b1313ULL, 0xb02c7d9c58512c2cULL, 0x6bd3d6b8bb05d3d3ULL, + 0xbbe76b5cd38ce7e7ULL, 0xa56e57cbdc396e6eULL, 0x37c46ef395aac4c4ULL, + 0x0c03180f061b0303ULL, 0x45568a13acdc5656ULL, 0x0d441a49885e4444ULL, + 0xe17fdf9efea07f7fULL, 0x9ea921374f88a9a9ULL, 0xa82a4d8254672a2aULL, + 0xd6bbb16d6b0abbbbULL, 0x23c146e29f87c1c1ULL, 0x5153a202a6f15353ULL, + 0x57dcae8ba572dcdcULL, 0x2c0b582716530b0bULL, 0x4e9d9cd327019d9dULL, + 0xad6c47c1d82b6c6cULL, 0xc43195f562a43131ULL, 0xcd7487b9e8f37474ULL, + 0xfff6e309f115f6f6ULL, 0x05460a438c4c4646ULL, 0x8aac092645a5acacULL, + 0x1e893c970fb58989ULL, 0x5014a04428b41414ULL, 0xa3e15b42dfbae1e1ULL, + 0x5816b04e2ca61616ULL, 0xe83acdd274f73a3aULL, 0xb9696fd0d2066969ULL, + 0x2409482d12410909ULL, 0xdd70a7ade0d77070ULL, 0xe2b6d954716fb6b6ULL, + 0x67d0ceb7bd1ed0d0ULL, 0x93ed3b7ec7d6ededULL, 0x17cc2edb85e2ccccULL, + 0x15422a5784684242ULL, 0x5a98b4c22d2c9898ULL, 0xaaa4490e55eda4a4ULL, + 0xa0285d8850752828ULL, 0x6d5cda31b8865c5cULL, 0xc7f8933fed6bf8f8ULL, + 0x228644a411c28686ULL, +}; + +static const u64 C7[256] = { + 0x186018c07830d818ULL, 0x238c2305af462623ULL, 0xc63fc67ef991b8c6ULL, + 0xe887e8136fcdfbe8ULL, 0x8726874ca113cb87ULL, 0xb8dab8a9626d11b8ULL, + 0x0104010805020901ULL, 0x4f214f426e9e0d4fULL, 0x36d836adee6c9b36ULL, + 0xa6a2a6590451ffa6ULL, 0xd26fd2debdb90cd2ULL, 0xf5f3f5fb06f70ef5ULL, + 0x79f979ef80f29679ULL, 0x6fa16f5fcede306fULL, 0x917e91fcef3f6d91ULL, + 0x525552aa07a4f852ULL, 0x609d6027fdc04760ULL, 0xbccabc89766535bcULL, + 0x9b569baccd2b379bULL, 0x8e028e048c018a8eULL, 0xa3b6a371155bd2a3ULL, + 0x0c300c603c186c0cULL, 0x7bf17bff8af6847bULL, 0x35d435b5e16a8035ULL, + 0x1d741de8693af51dULL, 0xe0a7e05347ddb3e0ULL, 0xd77bd7f6acb321d7ULL, + 0xc22fc25eed999cc2ULL, 0x2eb82e6d965c432eULL, 0x4b314b627a96294bULL, + 0xfedffea321e15dfeULL, 0x5741578216aed557ULL, 0x155415a8412abd15ULL, + 0x77c1779fb6eee877ULL, 0x37dc37a5eb6e9237ULL, 0xe5b3e57b56d79ee5ULL, + 0x9f469f8cd923139fULL, 0xf0e7f0d317fd23f0ULL, 0x4a354a6a7f94204aULL, + 0xda4fda9e95a944daULL, 0x587d58fa25b0a258ULL, 0xc903c906ca8fcfc9ULL, + 0x29a429558d527c29ULL, 0x0a280a5022145a0aULL, 0xb1feb1e14f7f50b1ULL, + 0xa0baa0691a5dc9a0ULL, 0x6bb16b7fdad6146bULL, 0x852e855cab17d985ULL, + 0xbdcebd8173673cbdULL, 0x5d695dd234ba8f5dULL, 0x1040108050209010ULL, + 0xf4f7f4f303f507f4ULL, 0xcb0bcb16c08bddcbULL, 0x3ef83eedc67cd33eULL, + 0x05140528110a2d05ULL, 0x6781671fe6ce7867ULL, 0xe4b7e47353d597e4ULL, + 0x279c2725bb4e0227ULL, 0x4119413258827341ULL, 0x8b168b2c9d0ba78bULL, + 0xa7a6a7510153f6a7ULL, 0x7de97dcf94fab27dULL, 0x956e95dcfb374995ULL, + 0xd847d88e9fad56d8ULL, 0xfbcbfb8b30eb70fbULL, 0xee9fee2371c1cdeeULL, + 0x7ced7cc791f8bb7cULL, 0x66856617e3cc7166ULL, 0xdd53dda68ea77bddULL, + 0x175c17b84b2eaf17ULL, 0x47014702468e4547ULL, 0x9e429e84dc211a9eULL, + 0xca0fca1ec589d4caULL, 0x2db42d75995a582dULL, 0xbfc6bf9179632ebfULL, + 0x071c07381b0e3f07ULL, 0xad8ead012347acadULL, 0x5a755aea2fb4b05aULL, + 0x8336836cb51bef83ULL, 0x33cc3385ff66b633ULL, 0x6391633ff2c65c63ULL, + 0x020802100a041202ULL, 0xaa92aa39384993aaULL, 0x71d971afa8e2de71ULL, + 0xc807c80ecf8dc6c8ULL, 0x196419c87d32d119ULL, 0x4939497270923b49ULL, + 0xd943d9869aaf5fd9ULL, 0xf2eff2c31df931f2ULL, 0xe3abe34b48dba8e3ULL, + 0x5b715be22ab6b95bULL, 0x881a8834920dbc88ULL, 0x9a529aa4c8293e9aULL, + 0x2698262dbe4c0b26ULL, 0x32c8328dfa64bf32ULL, 0xb0fab0e94a7d59b0ULL, + 0xe983e91b6acff2e9ULL, 0x0f3c0f78331e770fULL, 0xd573d5e6a6b733d5ULL, + 0x803a8074ba1df480ULL, 0xbec2be997c6127beULL, 0xcd13cd26de87ebcdULL, + 0x34d034bde4688934ULL, 0x483d487a75903248ULL, 0xffdbffab24e354ffULL, + 0x7af57af78ff48d7aULL, 0x907a90f4ea3d6490ULL, 0x5f615fc23ebe9d5fULL, + 0x2080201da0403d20ULL, 0x68bd6867d5d00f68ULL, 0x1a681ad07234ca1aULL, + 0xae82ae192c41b7aeULL, 0xb4eab4c95e757db4ULL, 0x544d549a19a8ce54ULL, + 0x937693ece53b7f93ULL, 0x2288220daa442f22ULL, 0x648d6407e9c86364ULL, + 0xf1e3f1db12ff2af1ULL, 0x73d173bfa2e6cc73ULL, 0x124812905a248212ULL, + 0x401d403a5d807a40ULL, 0x0820084028104808ULL, 0xc32bc356e89b95c3ULL, + 0xec97ec337bc5dfecULL, 0xdb4bdb9690ab4ddbULL, 0xa1bea1611f5fc0a1ULL, + 0x8d0e8d1c8307918dULL, 0x3df43df5c97ac83dULL, 0x976697ccf1335b97ULL, + 0x0000000000000000ULL, 0xcf1bcf36d483f9cfULL, 0x2bac2b4587566e2bULL, + 0x76c57697b3ece176ULL, 0x82328264b019e682ULL, 0xd67fd6fea9b128d6ULL, + 0x1b6c1bd87736c31bULL, 0xb5eeb5c15b7774b5ULL, 0xaf86af112943beafULL, + 0x6ab56a77dfd41d6aULL, 0x505d50ba0da0ea50ULL, 0x450945124c8a5745ULL, + 0xf3ebf3cb18fb38f3ULL, 0x30c0309df060ad30ULL, 0xef9bef2b74c3c4efULL, + 0x3ffc3fe5c37eda3fULL, 0x554955921caac755ULL, 0xa2b2a2791059dba2ULL, + 0xea8fea0365c9e9eaULL, 0x6589650fecca6a65ULL, 0xbad2bab9686903baULL, + 0x2fbc2f65935e4a2fULL, 0xc027c04ee79d8ec0ULL, 0xde5fdebe81a160deULL, + 0x1c701ce06c38fc1cULL, 0xfdd3fdbb2ee746fdULL, 0x4d294d52649a1f4dULL, + 0x927292e4e0397692ULL, 0x75c9758fbceafa75ULL, 0x061806301e0c3606ULL, + 0x8a128a249809ae8aULL, 0xb2f2b2f940794bb2ULL, 0xe6bfe66359d185e6ULL, + 0x0e380e70361c7e0eULL, 0x1f7c1ff8633ee71fULL, 0x62956237f7c45562ULL, + 0xd477d4eea3b53ad4ULL, 0xa89aa829324d81a8ULL, 0x966296c4f4315296ULL, + 0xf9c3f99b3aef62f9ULL, 0xc533c566f697a3c5ULL, 0x25942535b14a1025ULL, + 0x597959f220b2ab59ULL, 0x842a8454ae15d084ULL, 0x72d572b7a7e4c572ULL, + 0x39e439d5dd72ec39ULL, 0x4c2d4c5a6198164cULL, 0x5e655eca3bbc945eULL, + 0x78fd78e785f09f78ULL, 0x38e038ddd870e538ULL, 0x8c0a8c148605988cULL, + 0xd163d1c6b2bf17d1ULL, 0xa5aea5410b57e4a5ULL, 0xe2afe2434dd9a1e2ULL, + 0x6199612ff8c24e61ULL, 0xb3f6b3f1457b42b3ULL, 0x21842115a5423421ULL, + 0x9c4a9c94d625089cULL, 0x1e781ef0663cee1eULL, 0x4311432252866143ULL, + 0xc73bc776fc93b1c7ULL, 0xfcd7fcb32be54ffcULL, 0x0410042014082404ULL, + 0x515951b208a2e351ULL, 0x995e99bcc72f2599ULL, 0x6da96d4fc4da226dULL, + 0x0d340d68391a650dULL, 0xfacffa8335e979faULL, 0xdf5bdfb684a369dfULL, + 0x7ee57ed79bfca97eULL, 0x2490243db4481924ULL, 0x3bec3bc5d776fe3bULL, + 0xab96ab313d4b9aabULL, 0xce1fce3ed181f0ceULL, 0x1144118855229911ULL, + 0x8f068f0c8903838fULL, 0x4e254e4a6b9c044eULL, 0xb7e6b7d1517366b7ULL, + 0xeb8beb0b60cbe0ebULL, 0x3cf03cfdcc78c13cULL, 0x813e817cbf1ffd81ULL, + 0x946a94d4fe354094ULL, 0xf7fbf7eb0cf31cf7ULL, 0xb9deb9a1676f18b9ULL, + 0x134c13985f268b13ULL, 0x2cb02c7d9c58512cULL, 0xd36bd3d6b8bb05d3ULL, + 0xe7bbe76b5cd38ce7ULL, 0x6ea56e57cbdc396eULL, 0xc437c46ef395aac4ULL, + 0x030c03180f061b03ULL, 0x5645568a13acdc56ULL, 0x440d441a49885e44ULL, + 0x7fe17fdf9efea07fULL, 0xa99ea921374f88a9ULL, 0x2aa82a4d8254672aULL, + 0xbbd6bbb16d6b0abbULL, 0xc123c146e29f87c1ULL, 0x535153a202a6f153ULL, + 0xdc57dcae8ba572dcULL, 0x0b2c0b582716530bULL, 0x9d4e9d9cd327019dULL, + 0x6cad6c47c1d82b6cULL, 0x31c43195f562a431ULL, 0x74cd7487b9e8f374ULL, + 0xf6fff6e309f115f6ULL, 0x4605460a438c4c46ULL, 0xac8aac092645a5acULL, + 0x891e893c970fb589ULL, 0x145014a04428b414ULL, 0xe1a3e15b42dfbae1ULL, + 0x165816b04e2ca616ULL, 0x3ae83acdd274f73aULL, 0x69b9696fd0d20669ULL, + 0x092409482d124109ULL, 0x70dd70a7ade0d770ULL, 0xb6e2b6d954716fb6ULL, + 0xd067d0ceb7bd1ed0ULL, 0xed93ed3b7ec7d6edULL, 0xcc17cc2edb85e2ccULL, + 0x4215422a57846842ULL, 0x985a98b4c22d2c98ULL, 0xa4aaa4490e55eda4ULL, + 0x28a0285d88507528ULL, 0x5c6d5cda31b8865cULL, 0xf8c7f8933fed6bf8ULL, + 0x86228644a411c286ULL, +}; + +static const u64 rc[WHIRLPOOL_ROUNDS + 1] = { + 0x0000000000000000ULL, 0x1823c6e887b8014fULL, 0x36a6d2f5796f9152ULL, + 0x60bc9b8ea30c7b35ULL, 0x1de0d7c22e4bfe57ULL, 0x157737e59ff04adaULL, + 0x58c9290ab1a06b85ULL, 0xbd5d10f4cb3e0567ULL, 0xe427418ba77d95d8ULL, + 0xfbee7c66dd17479eULL, 0xca2dbf07ad5a8333ULL, +}; + +/** + * The core Whirlpool transform. + */ + +static void whirlpool_process_buffer(struct whirlpool_ctx *wctx) { + int i, r; + u64 K[8]; /* the round key */ + u64 block[8]; /* mu(buffer) */ + u64 state[8]; /* the cipher state */ + u64 L[8]; + u8 *buffer = wctx->buffer; + + for (i = 0; i < 8; i++, buffer += 8) { + block[i] = + (((u64)buffer[0] ) << 56) ^ + (((u64)buffer[1] & 0xffL) << 48) ^ + (((u64)buffer[2] & 0xffL) << 40) ^ + (((u64)buffer[3] & 0xffL) << 32) ^ + (((u64)buffer[4] & 0xffL) << 24) ^ + (((u64)buffer[5] & 0xffL) << 16) ^ + (((u64)buffer[6] & 0xffL) << 8) ^ + (((u64)buffer[7] & 0xffL) ); + } + + state[0] = block[0] ^ (K[0] = wctx->hash[0]); + state[1] = block[1] ^ (K[1] = wctx->hash[1]); + state[2] = block[2] ^ (K[2] = wctx->hash[2]); + state[3] = block[3] ^ (K[3] = wctx->hash[3]); + state[4] = block[4] ^ (K[4] = wctx->hash[4]); + state[5] = block[5] ^ (K[5] = wctx->hash[5]); + state[6] = block[6] ^ (K[6] = wctx->hash[6]); + state[7] = block[7] ^ (K[7] = wctx->hash[7]); + + for (r = 1; r <= WHIRLPOOL_ROUNDS; r++) { + + L[0] = C0[(int)(K[0] >> 56) ] ^ + C1[(int)(K[7] >> 48) & 0xff] ^ + C2[(int)(K[6] >> 40) & 0xff] ^ + C3[(int)(K[5] >> 32) & 0xff] ^ + C4[(int)(K[4] >> 24) & 0xff] ^ + C5[(int)(K[3] >> 16) & 0xff] ^ + C6[(int)(K[2] >> 8) & 0xff] ^ + C7[(int)(K[1] ) & 0xff] ^ + rc[r]; + + L[1] = C0[(int)(K[1] >> 56) ] ^ + C1[(int)(K[0] >> 48) & 0xff] ^ + C2[(int)(K[7] >> 40) & 0xff] ^ + C3[(int)(K[6] >> 32) & 0xff] ^ + C4[(int)(K[5] >> 24) & 0xff] ^ + C5[(int)(K[4] >> 16) & 0xff] ^ + C6[(int)(K[3] >> 8) & 0xff] ^ + C7[(int)(K[2] ) & 0xff]; + + L[2] = C0[(int)(K[2] >> 56) ] ^ + C1[(int)(K[1] >> 48) & 0xff] ^ + C2[(int)(K[0] >> 40) & 0xff] ^ + C3[(int)(K[7] >> 32) & 0xff] ^ + C4[(int)(K[6] >> 24) & 0xff] ^ + C5[(int)(K[5] >> 16) & 0xff] ^ + C6[(int)(K[4] >> 8) & 0xff] ^ + C7[(int)(K[3] ) & 0xff]; + + L[3] = C0[(int)(K[3] >> 56) ] ^ + C1[(int)(K[2] >> 48) & 0xff] ^ + C2[(int)(K[1] >> 40) & 0xff] ^ + C3[(int)(K[0] >> 32) & 0xff] ^ + C4[(int)(K[7] >> 24) & 0xff] ^ + C5[(int)(K[6] >> 16) & 0xff] ^ + C6[(int)(K[5] >> 8) & 0xff] ^ + C7[(int)(K[4] ) & 0xff]; + + L[4] = C0[(int)(K[4] >> 56) ] ^ + C1[(int)(K[3] >> 48) & 0xff] ^ + C2[(int)(K[2] >> 40) & 0xff] ^ + C3[(int)(K[1] >> 32) & 0xff] ^ + C4[(int)(K[0] >> 24) & 0xff] ^ + C5[(int)(K[7] >> 16) & 0xff] ^ + C6[(int)(K[6] >> 8) & 0xff] ^ + C7[(int)(K[5] ) & 0xff]; + + L[5] = C0[(int)(K[5] >> 56) ] ^ + C1[(int)(K[4] >> 48) & 0xff] ^ + C2[(int)(K[3] >> 40) & 0xff] ^ + C3[(int)(K[2] >> 32) & 0xff] ^ + C4[(int)(K[1] >> 24) & 0xff] ^ + C5[(int)(K[0] >> 16) & 0xff] ^ + C6[(int)(K[7] >> 8) & 0xff] ^ + C7[(int)(K[6] ) & 0xff]; + + L[6] = C0[(int)(K[6] >> 56) ] ^ + C1[(int)(K[5] >> 48) & 0xff] ^ + C2[(int)(K[4] >> 40) & 0xff] ^ + C3[(int)(K[3] >> 32) & 0xff] ^ + C4[(int)(K[2] >> 24) & 0xff] ^ + C5[(int)(K[1] >> 16) & 0xff] ^ + C6[(int)(K[0] >> 8) & 0xff] ^ + C7[(int)(K[7] ) & 0xff]; + + L[7] = C0[(int)(K[7] >> 56) ] ^ + C1[(int)(K[6] >> 48) & 0xff] ^ + C2[(int)(K[5] >> 40) & 0xff] ^ + C3[(int)(K[4] >> 32) & 0xff] ^ + C4[(int)(K[3] >> 24) & 0xff] ^ + C5[(int)(K[2] >> 16) & 0xff] ^ + C6[(int)(K[1] >> 8) & 0xff] ^ + C7[(int)(K[0] ) & 0xff]; + + K[0] = L[0]; + K[1] = L[1]; + K[2] = L[2]; + K[3] = L[3]; + K[4] = L[4]; + K[5] = L[5]; + K[6] = L[6]; + K[7] = L[7]; + + L[0] = C0[(int)(state[0] >> 56) ] ^ + C1[(int)(state[7] >> 48) & 0xff] ^ + C2[(int)(state[6] >> 40) & 0xff] ^ + C3[(int)(state[5] >> 32) & 0xff] ^ + C4[(int)(state[4] >> 24) & 0xff] ^ + C5[(int)(state[3] >> 16) & 0xff] ^ + C6[(int)(state[2] >> 8) & 0xff] ^ + C7[(int)(state[1] ) & 0xff] ^ + K[0]; + + L[1] = C0[(int)(state[1] >> 56) ] ^ + C1[(int)(state[0] >> 48) & 0xff] ^ + C2[(int)(state[7] >> 40) & 0xff] ^ + C3[(int)(state[6] >> 32) & 0xff] ^ + C4[(int)(state[5] >> 24) & 0xff] ^ + C5[(int)(state[4] >> 16) & 0xff] ^ + C6[(int)(state[3] >> 8) & 0xff] ^ + C7[(int)(state[2] ) & 0xff] ^ + K[1]; + + L[2] = C0[(int)(state[2] >> 56) ] ^ + C1[(int)(state[1] >> 48) & 0xff] ^ + C2[(int)(state[0] >> 40) & 0xff] ^ + C3[(int)(state[7] >> 32) & 0xff] ^ + C4[(int)(state[6] >> 24) & 0xff] ^ + C5[(int)(state[5] >> 16) & 0xff] ^ + C6[(int)(state[4] >> 8) & 0xff] ^ + C7[(int)(state[3] ) & 0xff] ^ + K[2]; + + L[3] = C0[(int)(state[3] >> 56) ] ^ + C1[(int)(state[2] >> 48) & 0xff] ^ + C2[(int)(state[1] >> 40) & 0xff] ^ + C3[(int)(state[0] >> 32) & 0xff] ^ + C4[(int)(state[7] >> 24) & 0xff] ^ + C5[(int)(state[6] >> 16) & 0xff] ^ + C6[(int)(state[5] >> 8) & 0xff] ^ + C7[(int)(state[4] ) & 0xff] ^ + K[3]; + + L[4] = C0[(int)(state[4] >> 56) ] ^ + C1[(int)(state[3] >> 48) & 0xff] ^ + C2[(int)(state[2] >> 40) & 0xff] ^ + C3[(int)(state[1] >> 32) & 0xff] ^ + C4[(int)(state[0] >> 24) & 0xff] ^ + C5[(int)(state[7] >> 16) & 0xff] ^ + C6[(int)(state[6] >> 8) & 0xff] ^ + C7[(int)(state[5] ) & 0xff] ^ + K[4]; + + L[5] = C0[(int)(state[5] >> 56) ] ^ + C1[(int)(state[4] >> 48) & 0xff] ^ + C2[(int)(state[3] >> 40) & 0xff] ^ + C3[(int)(state[2] >> 32) & 0xff] ^ + C4[(int)(state[1] >> 24) & 0xff] ^ + C5[(int)(state[0] >> 16) & 0xff] ^ + C6[(int)(state[7] >> 8) & 0xff] ^ + C7[(int)(state[6] ) & 0xff] ^ + K[5]; + + L[6] = C0[(int)(state[6] >> 56) ] ^ + C1[(int)(state[5] >> 48) & 0xff] ^ + C2[(int)(state[4] >> 40) & 0xff] ^ + C3[(int)(state[3] >> 32) & 0xff] ^ + C4[(int)(state[2] >> 24) & 0xff] ^ + C5[(int)(state[1] >> 16) & 0xff] ^ + C6[(int)(state[0] >> 8) & 0xff] ^ + C7[(int)(state[7] ) & 0xff] ^ + K[6]; + + L[7] = C0[(int)(state[7] >> 56) ] ^ + C1[(int)(state[6] >> 48) & 0xff] ^ + C2[(int)(state[5] >> 40) & 0xff] ^ + C3[(int)(state[4] >> 32) & 0xff] ^ + C4[(int)(state[3] >> 24) & 0xff] ^ + C5[(int)(state[2] >> 16) & 0xff] ^ + C6[(int)(state[1] >> 8) & 0xff] ^ + C7[(int)(state[0] ) & 0xff] ^ + K[7]; + + state[0] = L[0]; + state[1] = L[1]; + state[2] = L[2]; + state[3] = L[3]; + state[4] = L[4]; + state[5] = L[5]; + state[6] = L[6]; + state[7] = L[7]; + } + /* + * apply the Miyaguchi-Preneel compression function: + */ + wctx->hash[0] ^= state[0] ^ block[0]; + wctx->hash[1] ^= state[1] ^ block[1]; + wctx->hash[2] ^= state[2] ^ block[2]; + wctx->hash[3] ^= state[3] ^ block[3]; + wctx->hash[4] ^= state[4] ^ block[4]; + wctx->hash[5] ^= state[5] ^ block[5]; + wctx->hash[6] ^= state[6] ^ block[6]; + wctx->hash[7] ^= state[7] ^ block[7]; + +} + +static void whirlpool_init (void *ctx) { + int i; + struct whirlpool_ctx *wctx = ctx; + + memset(wctx->bitLength, 0, 32); + wctx->bufferBits = wctx->bufferPos = 0; + wctx->buffer[0] = 0; + for (i = 0; i < 8; i++) { + wctx->hash[i] = 0L; + } +} + +static void whirlpool_update(void *ctx, const u8 *source, unsigned int len) +{ + + struct whirlpool_ctx *wctx = ctx; + int sourcePos = 0; + unsigned int bits_len = len * 8; // convert to number of bits + int sourceGap = (8 - ((int)bits_len & 7)) & 7; + int bufferRem = wctx->bufferBits & 7; + int i; + u32 b, carry; + u8 *buffer = wctx->buffer; + u8 *bitLength = wctx->bitLength; + int bufferBits = wctx->bufferBits; + int bufferPos = wctx->bufferPos; + + u64 value = bits_len; + for (i = 31, carry = 0; i >= 0 && (carry != 0 || value != 0ULL); i--) { + carry += bitLength[i] + ((u32)value & 0xff); + bitLength[i] = (u8)carry; + carry >>= 8; + value >>= 8; + } + while (bits_len > 8) { + b = ((source[sourcePos] << sourceGap) & 0xff) | + ((source[sourcePos + 1] & 0xff) >> (8 - sourceGap)); + buffer[bufferPos++] |= (u8)(b >> bufferRem); + bufferBits += 8 - bufferRem; + if (bufferBits == WHIRLPOOL_DIGEST_SIZE * 8) { + whirlpool_process_buffer(wctx); + bufferBits = bufferPos = 0; + } + buffer[bufferPos] = b << (8 - bufferRem); + bufferBits += bufferRem; + bits_len -= 8; + sourcePos++; + } + if (bits_len > 0) { + b = (source[sourcePos] << sourceGap) & 0xff; + buffer[bufferPos] |= b >> bufferRem; + } else { + b = 0; + } + if (bufferRem + bits_len < 8) { + bufferBits += bits_len; + } else { + bufferPos++; + bufferBits += 8 - bufferRem; + bits_len -= 8 - bufferRem; + if (bufferBits == WHIRLPOOL_DIGEST_SIZE * 8) { + whirlpool_process_buffer(wctx); + bufferBits = bufferPos = 0; + } + buffer[bufferPos] = b << (8 - bufferRem); + bufferBits += (int)bits_len; + } + + wctx->bufferBits = bufferBits; + wctx->bufferPos = bufferPos; + +} + +static void whirlpool_final(void *ctx, u8 *out) +{ + struct whirlpool_ctx *wctx = ctx; + int i; + u8 *buffer = wctx->buffer; + u8 *bitLength = wctx->bitLength; + int bufferBits = wctx->bufferBits; + int bufferPos = wctx->bufferPos; + u8 *digest = out; + + buffer[bufferPos] |= 0x80U >> (bufferBits & 7); + bufferPos++; + if (bufferPos > WHIRLPOOL_BLOCK_SIZE - WHIRLPOOL_LENGTHBYTES) { + if (bufferPos < WHIRLPOOL_BLOCK_SIZE) { + memset(&buffer[bufferPos], 0, WHIRLPOOL_BLOCK_SIZE - bufferPos); + } + whirlpool_process_buffer(wctx); + bufferPos = 0; + } + if (bufferPos < WHIRLPOOL_BLOCK_SIZE - WHIRLPOOL_LENGTHBYTES) { + memset(&buffer[bufferPos], 0, + (WHIRLPOOL_BLOCK_SIZE - WHIRLPOOL_LENGTHBYTES) - bufferPos); + } + bufferPos = WHIRLPOOL_BLOCK_SIZE - WHIRLPOOL_LENGTHBYTES; + memcpy(&buffer[WHIRLPOOL_BLOCK_SIZE - WHIRLPOOL_LENGTHBYTES], + bitLength, WHIRLPOOL_LENGTHBYTES); + whirlpool_process_buffer(wctx); + for (i = 0; i < WHIRLPOOL_DIGEST_SIZE/8; i++) { + digest[0] = (u8)(wctx->hash[i] >> 56); + digest[1] = (u8)(wctx->hash[i] >> 48); + digest[2] = (u8)(wctx->hash[i] >> 40); + digest[3] = (u8)(wctx->hash[i] >> 32); + digest[4] = (u8)(wctx->hash[i] >> 24); + digest[5] = (u8)(wctx->hash[i] >> 16); + digest[6] = (u8)(wctx->hash[i] >> 8); + digest[7] = (u8)(wctx->hash[i] ); + digest += 8; + } + wctx->bufferBits = bufferBits; + wctx->bufferPos = bufferPos; +} + +static struct crypto_alg alg = { + .cra_name = "whirlpool", + .cra_flags = CRYPTO_ALG_TYPE_DIGEST, + .cra_blocksize = WHIRLPOOL_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct whirlpool_ctx), + .cra_module = THIS_MODULE, + .cra_list = LIST_HEAD_INIT(alg.cra_list), + .cra_u = { .digest = { + .dia_digestsize = WHIRLPOOL_DIGEST_SIZE, + .dia_init = whirlpool_init, + .dia_update = whirlpool_update, + .dia_final = whirlpool_final } } +}; + +static int __init init(void) +{ + return crypto_register_alg(&alg); +} + +static void __exit fini(void) +{ + crypto_unregister_alg(&alg); +} + +module_init(init); +module_exit(fini); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Whirlpool Message Digest Algorithm"); --- linux-2.6.9-rc1/Documentation/cdrom/00-INDEX 2004-08-24 00:02:47.000000000 -0700 +++ 25/Documentation/cdrom/00-INDEX 2004-09-03 00:56:49.221677816 -0700 @@ -22,6 +22,8 @@ mcdx - info on improved Mitsumi CD-ROM driver. optcd - info on the Optics Storage 8000 AT CD-ROM driver +packet-writing.txt + - Info on the CDRW packet writing module sbpcd - info on the SoundBlaster/Panasonic CD-ROM interface driver. sjcd --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/Documentation/cdrom/packet-writing.txt 2004-09-03 00:56:49.697605464 -0700 @@ -0,0 +1,86 @@ +Getting started quick +--------------------- + +- Select packet support in the block device section and UDF support in + the file system section. + +- Compile and install kernel and modules, reboot. + +- You need the udftools package (pktsetup, mkudffs, cdrwtool). + Download from http://sourceforge.net/projects/linux-udf/ + +- Grab a new CD-RW disc and format it (assuming CD-RW is hdc, substitute + as appropriate): + # cdrwtool -d /dev/hdc -q + +- Setup your writer + # pktsetup dev_name /dev/hdc + +- Now you can mount /dev/pktcdvd/dev_name and copy files to it. Enjoy! + # mount /dev/pktcdvd/dev_name /cdrom -t udf -o rw,noatime + + +Packet writing for DVD-RW media +------------------------------- + +DVD-RW discs can be written to much like CD-RW discs if they are in +the so called "restricted overwrite" mode. To put a disc in restricted +overwrite mode, run: + + # dvd+rw-format /dev/hdc + +You can then use the disc the same way you would use a CD-RW disc: + + # pktsetup dev_name /dev/hdc + # mount /dev/pktcdvd/dev_name /cdrom -t udf -o rw,noatime + + +Packet writing for DVD+RW media +------------------------------- + +According to the DVD+RW specification, a drive supporting DVD+RW discs +shall implement "true random writes with 2KB granularity", which means +that it should be possible to put any filesystem with a block size >= +2KB on such a disc. For example, it should be possible to do: + + # mkudffs /dev/hdc + # mount /dev/hdc /cdrom -t udf -o rw,noatime + +However, some drives don't follow the specification and expect the +host to perform aligned writes at 32KB boundaries. Other drives do +follow the specification, but suffer bad performance problems if the +writes are not 32KB aligned. + +Both problems can be solved by using the pktcdvd driver, which always +generates aligned writes. + + # pktsetup dev_name /dev/hdc + # mkudffs /dev/pktcdvd/dev_name + # mount /dev/pktcdvd/dev_name /cdrom -t udf -o rw,noatime + + +Notes +----- + +- CD-RW media can usually not be overwritten more than about 1000 + times, so to avoid unnecessary wear on the media, you should always + use the noatime mount option. + +- Defect management (ie automatic remapping of bad sectors) has not + been implemented yet, so you are likely to get at least some + filesystem corruption if the disc wears out. + +- Since the pktcdvd driver makes the disc appear as a regular block + device with a 2KB block size, you can put any filesystem you like on + the disc. For example, run: + + # /sbin/mke2fs /dev/pktcdvd/dev_name + + to create an ext2 filesystem on the disc. + + +Links +----- + +See http://fy.chalmers.se/~appro/linux/DVD+RW/ for more information +about DVD writing. --- linux-2.6.9-rc1/Documentation/Changes 2004-08-24 00:03:32.000000000 -0700 +++ 25/Documentation/Changes 2004-09-03 00:57:04.263391128 -0700 @@ -56,6 +56,7 @@ o module-init-tools 0.9.10 o e2fsprogs 1.29 # tune2fs o jfsutils 1.1.3 # fsck.jfs -V o reiserfsprogs 3.6.3 # reiserfsck -V 2>&1|grep reiserfsprogs +o reiser4progs 1.0.0 # fsck.reiser4 -V o xfsprogs 2.6.0 # xfs_db -V o pcmcia-cs 3.1.21 # cardmgr -V o quota-tools 3.09 # quota -V @@ -177,6 +178,13 @@ The reiserfsprogs package should be used versions of mkreiserfs, resize_reiserfs, debugreiserfs and reiserfsck. These utils work on both i386 and alpha platforms. +Reiser4progs +------------ + +The reiser4progs package contains utilities for the reiser4 file system. +Detailed instructions are provided in the README file located at: +. + Xfsprogs -------- @@ -340,6 +348,10 @@ Reiserfsprogs ------------- o +Reiser4progs +------------ +o + Xfsprogs -------- o --- linux-2.6.9-rc1/Documentation/CodingStyle 2004-08-24 00:02:25.000000000 -0700 +++ 25/Documentation/CodingStyle 2004-09-02 20:51:51.000000000 -0700 @@ -356,10 +356,10 @@ Generally, inline functions are preferab Macros with multiple statements should be enclosed in a do - while block: -#define macrofun(a,b,c) \ +#define macrofun(a, b, c) \ do { \ if (a == 5) \ - do_this(b,c); \ + do_this(b, c); \ } while (0) Things to avoid when using macros: --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/Documentation/cpu-freq/cpufreq-nforce2.txt 2004-09-03 00:56:50.673457112 -0700 @@ -0,0 +1,22 @@ + +The cpufreq-nforce2 driver changes the FSB on nVidia nForce2 plattforms. + +This works better than on other plattforms, because the FSB of the CPU +can be controlled independently from the PCI/AGP clock. + +The module has three options: + + fid: multiplier * 10 (for example 8.5 = 85) + min_fsb: minimum FSB + max_fsb: maximum FSB + +If not set, fid is calculated with the current CPU speed and the FSB, +max_fsb is set to the current FSB and min_fsb to current FSB - 50. + +IMPORTANT: The available range is limited up- _and_ downwards! + You should not set max_fsb higher than the FSB at boot- + time. + Also the minimum available FSB can differ, for systems + booting with 200 MHz, 150 should always work. + + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/Documentation/cpusets.txt 2004-09-03 00:56:59.923050960 -0700 @@ -0,0 +1,387 @@ + CPUSETS + ------- + +Copyright (C) 2004 BULL SA. +Written by Simon.Derr@bull.net + +Portions Copyright (c) 2004 Silicon Graphics, Inc. +Modified by Paul Jackson + +CONTENTS: +========= + +1. Cpusets + 1.1 What are cpusets ? + 1.2 Why are cpusets needed ? + 1.3 How are cpusets implemented ? + 1.4 How do I use cpusets ? +2. Usage Examples and Syntax + 2.1 Basic Usage + 2.2 Adding/removing cpus + 2.3 Setting flags + 2.4 Attaching processes +3. Questions +4. Contact + +1. Cpusets +========== + +1.1 What are cpusets ? +---------------------- + +Cpusets provide a mechanism for assigning a set of CPUs and Memory +Nodes to a set of tasks. + +Cpusets constrain the CPU and Memory placement of tasks to only +the resources within a tasks current cpuset. They form a nested +hierarchy visible in a virtual file system. These are the essential +hooks, beyond what is already present, required to manage dynamic +job placement on large systems. + +Each task has a pointer to a cpuset. Multiple tasks may reference +the same cpuset. Requests by a task, using the sched_setaffinity(2) +system call to include CPUs in its CPU affinity mask, and using the +mbind(2) and set_mempolicy(2) system calls to include Memory Nodes +in its memory policy, are both filtered through that tasks cpuset, +filtering out any CPUs or Memory Nodes not in that cpuset. The +scheduler will not schedule a task on a CPU that is not allowed in +its cpus_allowed vector, and the kernel page allocator will not +allocate a page on a node that is not allowed in the requesting tasks +mems_allowed vector. + +If a cpuset is cpu or mem exclusive, no other cpuset, other than a direct +ancestor or descendent, may share any of the same CPUs or Memory Nodes. + +User level code may create and destroy cpusets by name in the cpuset +virtual file system, manage the attributes and permissions of these +cpusets and which CPUs and Memory Nodes are assigned to each cpuset, +specify and query to which cpuset a task is assigned, and list the +task pids assigned to a cpuset. + + +1.2 Why are cpusets needed ? +---------------------------- + +The management of large computer systems, with many processors (CPUs), +complex memory cache hierarchies and multiple Memory Nodes having +non-uniform access times (NUMA) presents additional challenges for +the efficient scheduling and memory placement of processes. + +Frequently more modest sized systems can be operated with adequate +efficiency just by letting the operating system automatically share +the available CPU and Memory resources amongst the requesting tasks. + +But larger systems, which benefit more from careful processor and +memory placement to reduce memory access times and contention, +and which typically represent a larger investment for the customer, +can benefit from explictly placing jobs on properly sized subsets of +the system. + +This can be especially valuable on: + + * Web Servers running multiple instances of the same web application, + * Servers running different applications (for instance, a web server + and a database), or + * NUMA systems running large HPC applications with demanding + performance characteristics. + +These subsets, or "soft partitions" must be able to be dynamically +adjusted, as the job mix changes, without impacting other concurrently +executing jobs. + +The kernel cpuset patch provides the minimum essential kernel +mechanisms required to efficiently implement such subsets. It +leverages existing CPU and Memory Placement facilities in the Linux +kernel to avoid any additional impact on the critical scheduler or +memory allocator code. + + +1.3 How are cpusets implemented ? +--------------------------------- + +Cpusets provide a Linux kernel (2.6.7 and above) mechanism to constrain +which CPUs and Memory Nodes are used by a process or set of processes. + +The Linux kernel already has a pair of mechanisms to specify on which +CPUs a task may be scheduled (sched_setaffinity) and on which Memory +Nodes it may obtain memory (mbind, set_mempolicy). + +Cpusets extends these two mechanisms as follows: + + - Cpusets are sets of allowed CPUs and Memory Nodes, known to the + kernel. + - Each task in the system is attached to a cpuset, via a pointer + in the task structure to a reference counted cpuset structure. + - Calls to sched_setaffinity are filtered to just those CPUs + allowed in that tasks cpuset. + - Calls to mbind and set_mempolicy are filtered to just + those Memory Nodes allowed in that tasks cpuset. + - The root cpuset contains all the systems CPUs and Memory + Nodes. + - For any cpuset, one can define child cpusets containing a subset + of the parents CPU and Memory Node resources. + - The hierarchy of cpusets can be mounted at /dev/cpuset, for + browsing and manipulation from user space. + - A cpuset may be marked exclusive, which ensures that no other + cpuset (except direct ancestors and descendents) may contain + any overlapping CPUs or Memory Nodes. + - You can list all the tasks (by pid) attached to any cpuset. + +The implementation of cpusets requires a few, simple hooks +into the rest of the kernel, none in performance critical paths: + + - in main/init.c, to initialize the root cpuset at system boot. + - in fork and exit, to attach and detach a task from its cpuset. + - in sched_setaffinity, to mask the requested CPUs by what's + allowed in that tasks cpuset. + - in sched.c migrate_all_tasks(), to keep migrating tasks within + the CPUs allowed by their cpuset, if possible. + - in the mbind and set_mempolicy system calls, to mask the requested + Memory Nodes by what's allowed in that tasks cpuset. + - in page_alloc, to restrict memory to allowed nodes. + - in vmscan.c, to restrict page recovery to the current cpuset. + +In addition a new file system, of type "cpuset" may be mounted, +typically at /dev/cpuset, to enable browsing and modifying the cpusets +presently known to the kernel. No new system calls are added for +cpusets - all support for querying and modifying cpusets is via +this cpuset file system. + +Each task under /proc has an added file named 'cpuset', displaying +the cpuset name, as the path relative to the root of the cpuset file +system. + +Each cpuset is represented by a directory in the cpuset file system +containing the following files describing that cpuset: + + - cpus: list of CPUs in that cpuset + - mems: list of Memory Nodes in that cpuset + - cpu_exclusive flag: is cpu placement exclusive? + - mem_exclusive flag: is memory placement exclusive? + - tasks: list of tasks (by pid) attached to that cpuset + +New cpusets are created using the mkdir system call or shell +command. The properties of a cpuset, such as its flags, allowed +CPUs and Memory Nodes, and attached tasks, are modified by writing +to the appropriate file in that cpusets directory, as listed above. + +The named hierarchical structure of nested cpusets allows partitioning +a large system into nested, dynamically changeable, "soft-partitions". + +The attachment of each task, automatically inherited at fork by any +children of that task, to a cpuset allows organizing the work load +on a system into related sets of tasks such that each set is constrained +to using the CPUs and Memory Nodes of a particular cpuset. A task +may be re-attached to any other cpuset, if allowed by the permissions +on the necessary cpuset file system directories. + +Such management of a system "in the large" integrates smoothly with +the detailed placement done on individual tasks and memory regions +using the sched_setaffinity, mbind and set_mempolicy system calls. + +The following rules apply to each cpuset: + + - Its CPUs and Memory Nodes must be a subset of its parents. + - It can only be marked exclusive if its parent is. + - If its cpu or memory is exclusive, they may not overlap any sibling. + +These rules, and the natural hierarchy of cpusets, enable efficient +enforcement of the exclusive guarantee, without having to scan all +cpusets every time any of them change to ensure nothing overlaps a +exclusive cpuset. Also, the use of a Linux virtual file system (vfs) +to represent the cpuset hierarchy provides for a familiar permission +and name space for cpusets, with a minimum of additional kernel code. + +1.4 How do I use cpusets ? +-------------------------- + +In order to minimize the impact of cpusets on critical kernel +code, such as the scheduler, and due to the fact that the kernel +does not support one task updating the memory placement of another +task directly, the impact on a task of changing its cpuset CPU +or Memory Node placement, or of changing to which cpuset a task +is attached, is subtle. + +If a cpuset has its Memory Nodes modified, then for each task attached +to that cpuset, the next time that the kernel attempts to allocate +a page of memory for that task, the kernel will notice the change +in the tasks cpuset, and update its per-task memory placement to +remain within the new cpusets memory placement. If the task was using +mempolicy MPOL_BIND, and the nodes to which it was bound overlap with +its new cpuset, then the task will continue to use whatever subset +of MPOL_BIND nodes are still allowed in the new cpuset. If the task +was using MPOL_BIND and now none of its MPOL_BIND nodes are allowed +in the new cpuset, then the task will be essentially treated as if it +was MPOL_BIND bound to the new cpuset (even though its numa placement, +as queried by get_mempolicy(), doesn't change). If a task is moved +from one cpuset to another, then the kernel will adjust the tasks +memory placement, as above, the next time that the kernel attempts +to allocate a page of memory for that task. + +If a cpuset has its CPUs modified, then each task using that +cpuset does _not_ change its behavior automatically. In order to +minimize the impact on the critical scheduling code in the kernel, +tasks will continue to use their prior CPU placement until they +are rebound to their cpuset, by rewriting their pid to the 'tasks' +file of their cpuset. If a task had been bound to some subset of its +cpuset using the sched_setaffinity() call, and if any of that subset +is still allowed in its new cpuset settings, then the task will be +restricted to the intersection of the CPUs it was allowed on before, +and its new cpuset CPU placement. If, on the other hand, there is +no overlap between a tasks prior placement and its new cpuset CPU +placement, then the task will be allowed to run on any CPU allowed +in its new cpuset. If a task is moved from one cpuset to another, +its CPU placement is updated in the same way as if the tasks pid is +rewritten to the 'tasks' file of its current cpuset. + +In summary, the memory placement of a task whose cpuset is changed is +updated by the kernel, on the next allocation of a page for that task, +but the processor placement is not updated, until that tasks pid is +rewritten to the 'tasks' file of its cpuset. This is done to avoid +impacting the scheduler code in the kernel with a check for changes +in a tasks processor placement. + +To start a new job that is to be contained within a cpuset, the steps are: + + 1) mkdir /dev/cpuset + 2) mount -t cpuset none /dev/cpuset + 3) Create the new cpuset by doing mkdir's and write's (or echo's) in + the /dev/cpuset virtual file system. + 4) Start a task that will be the "founding father" of the new job. + 5) Attach that task to the new cpuset by writing its pid to the + /dev/cpuset tasks file for that cpuset. + 6) fork, exec or clone the job tasks from this founding father task. + +For example, the following sequence of commands will setup a cpuset +named "Charlie", containing just CPUs 2 and 3, and Memory Node 1, +and then start a subshell 'sh' in that cpuset: + + mount -t cpuset none /dev/cpuset + cd /dev/cpuset + mkdir Charlie + cd Charlie + /bin/echo 2-3 > cpus + /bin/echo 1 > mems + /bin/echo $$ > tasks + sh + # The subshell 'sh' is now running in cpuset Charlie + # The next line should display '/Charlie' + cat /proc/self/cpuset + +In the case that a change of cpuset includes wanting to move already +allocated memory pages, consider further the work of IWAMOTO +Toshihiro for page remapping and memory +hotremoval, which can be found at: + + http://people.valinux.co.jp/~iwamoto/mh.html + +The integration of cpusets with such memory migration is not yet +available. + +In the future, a C library interface to cpusets will likely be +available. For now, the only way to query or modify cpusets is +via the cpuset file system, using the various cd, mkdir, echo, cat, +rmdir commands from the shell, or their equivalent from C. + +The sched_setaffinity calls can also be done at the shell prompt using +SGI's runon or Robert Love's taskset. The mbind and set_mempolicy +calls can be done at the shell prompt using the numactl command +(part of Andi Kleen's numa package). + +2. Usage Examples and Syntax +============================ + +2.1 Basic Usage +--------------- + +Creating, modifying, using the cpusets can be done through the cpuset +virtual filesystem. + +To mount it, type: +# mount -t cpuset none /dev/cpuset + +Then under /dev/cpuset you can find a tree that corresponds to the +tree of the cpusets in the system. For instance, /dev/cpuset +is the cpuset that holds the whole system. + +If you want to create a new cpuset under /dev/cpuset: +# cd /dev/cpuset +# mkdir my_cpuset + +Now you want to do something with this cpuset. +# cd my_cpuset + +In this directory you can find several files: +# ls +cpus cpu_exclusive mems mem_exclusive tasks + +Reading them will give you information about the state of this cpuset: +the CPUs and Memory Nodes it can use, the processes that are using +it, its properties. By writing to these files you can manipulate +the cpuset. + +Set some flags: +# /bin/echo 1 > cpu_exclusive + +Add some cpus: +# /bin/echo 0-7 > cpus + +Now attach your shell to this cpuset: +# /bin/echo $$ > tasks + +You can also create cpusets inside your cpuset by using mkdir in this +directory. +# mkdir my_sub_cs + +To remove a cpuset, just use rmdir: +# rmdir my_sub_cs +This will fail if the cpuset is in use (has cpusets inside, or has +processes attached). + +2.2 Adding/removing cpus +------------------------ + +This is the syntax to use when writing in the cpus or mems files +in cpuset directories: + +# /bin/echo 1-4 > cpus -> set cpus list to cpus 1,2,3,4 +# /bin/echo 1,2,3,4 > cpus -> set cpus list to cpus 1,2,3,4 + +2.3 Setting flags +----------------- + +The syntax is very simple: + +# /bin/echo 1 > cpu_exclusive -> set flag 'cpu_exclusive' +# /bin/echo 0 > cpu_exclusive -> unset flag 'cpu_exclusive' + +2.4 Attaching processes +----------------------- + +# /bin/echo PID > tasks + +Note that it is PID, not PIDs. You can only attach ONE task at a time. +If you have several tasks to attach, you have to do it one after another: + +# /bin/echo PID1 > tasks +# /bin/echo PID2 > tasks + ... +# /bin/echo PIDn > tasks + + +3. Questions +============ + +Q: what's up with this '/bin/echo' ? +A: bash's builtin 'echo' command does not check calls to write() against + errors. If you use it in the cpuset file system, you won't be + able to tell whether a command succeeded or failed. + +Q: When I attach processes, only the first of the line gets really attached ! +A: We can only return one error code per call to write(). So you should also + put only ONE pid. + +4. Contact +========== + +Web: http://www.bullopensource.org/cpuset --- linux-2.6.9-rc1/Documentation/crypto/api-intro.txt 2004-08-24 00:03:18.000000000 -0700 +++ 25/Documentation/crypto/api-intro.txt 2004-09-02 20:51:51.000000000 -0700 @@ -227,6 +227,9 @@ TEA/XTEA algorithm contributors: Khazad algorithm contributors: Aaron Grothe +Whirlpool algorithm contributors: + Aaron Grothe + Generic scatterwalk code by Adam J. Richter Please send any credits updates or corrections to: --- linux-2.6.9-rc1/Documentation/devices.txt 2004-08-24 00:02:32.000000000 -0700 +++ 25/Documentation/devices.txt 2004-09-02 20:51:51.000000000 -0700 @@ -1,9 +1,9 @@ LINUX ALLOCATED DEVICES - Maintained by John Cagle + Maintained by Torben Mathiasen - Last revised: 15 March 2004 + Last revised: 04 August 2004 This list is the Linux Device List, the official registry of allocated device numbers and /dev directory nodes for the Linux operating @@ -410,6 +410,7 @@ Your cooperation is appreciated. 199 = /dev/scanners/cuecat :CueCat barcode scanner 200 = /dev/net/tun TAP/TUN network device 201 = /dev/button/gulpb Transmeta GULP-B buttons + 202 = /dev/emd/ctl Enhanced Metadisk RAID (EMD) control 204 = /dev/video/em8300 EM8300 DVD decoder control 205 = /dev/video/em8300_mv EM8300 DVD decoder video 206 = /dev/video/em8300_ma EM8300 DVD decoder audio @@ -1796,14 +1797,15 @@ Your cooperation is appreciated. partitions is 15. 103 char Arla network file system - 0 = /dev/xfs0 Arla XFS + 0 = /dev/nnpfs0 First NNPFS device + 1 = /dev/nnpfs1 Second NNPFS device Arla is a free clone of the Andrew File System, AFS. - Any resemblance with the Swedish milk producer is - coincidental. For more information about the project, - write to or subscribe - to the arla announce mailing list by sending a mail to - . + The NNPFS device gives user mode filesystem + implementations a kernel presence for caching and easy + mounting. For more information about the project, + write to or see + http://www.stacken.kth.se/project/arla/ 103 block Audit device 0 = /dev/audit Audit device @@ -2060,12 +2062,10 @@ Your cooperation is appreciated. device names. For example, /dev/hda5 would become /dev/evms/hda5. -118 char Solidum ??? - 0 = /dev/solnp0 - 1 = /dev/solnp1 - ... - 128 = /dev/solnpctl0 - 129 = /dev/solnpctl1 +118 char IBM Cryptographic Accelerator + 0 = /dev/ica Virtual interface to all IBM Crypto Accelerators + 1 = /dev/ica0 IBMCA Device 0 + 2 = /dev/ica1 IBMCA Device 1 ... 119 char VMware virtual network control @@ -2310,6 +2310,11 @@ Your cooperation is appreciated. 1 = /dev/aureal1 Second Aureal Vortex ... +147 block Distributed Replicated Block Device (DRBD) + 0 = /dev/drbd0 First DRBD device + 1 = /dev/drbd1 Second DRBD device + ... + 148 char Technology Concepts serial card 0 = /dev/ttyT0 First TCL port 1 = /dev/ttyT1 Second TCL port @@ -2346,6 +2351,22 @@ Your cooperation is appreciated. ... 15 = /dev/spi15 Sixteenth SPI device on the bus +153 block Enhanced Metadisk RAID (EMD) storage units + 0 = /dev/emd/0 First unit + 1 = /dev/emd/0p1 Partition 1 on First unit + 2 = /dev/emd/0p2 Partition 2 on First unit + ... + 15 = /dev/emd/0p15 Partition 15 on First unit + + 16 = /dev/emd/1 Second unit + 32 = /dev/emd/2 Third unit + ... + 240 = /dev/emd/15 Sixteenth unit + + Partitions are handled in the same way as for IDE + disks (see major number 3) except that the limit on + partitions is 15. + 154 char Specialix RIO serial card 0 = /dev/ttySR0 First RIO port ... @@ -2371,23 +2392,24 @@ Your cooperation is appreciated. 1 = /dev/gfax1 GammaLink channel 1 ... -159 RESERVED +159 char RESERVED +159 block RESERVED 160 char General Purpose Instrument Bus (GPIB) 0 = /dev/gpib0 First GPIB bus 1 = /dev/gpib1 Second GPIB bus ... -160 block Promise SX8 8-port SATA Disks on First Controller - 0 = /dev/sx8/0 SATA disk 0 whole disk - 1 = /dev/sx8/0p1 SATA disk 0 partition 1 +160 block Carmel 8-port SATA Disks on First Controller + 0 = /dev/carmel/0 SATA disk 0 whole disk + 1 = /dev/carmel/0p1 SATA disk 0 partition 1 ... - 31 = /dev/sx8/0p31 SATA disk 0 partition 31 + 31 = /dev/carmel/0p31 SATA disk 0 partition 31 - 32 = /dev/sx8/1 SATA disk 1 whole disk - 64 = /dev/sx8/2 SATA disk 2 whole disk + 32 = /dev/carmel/1 SATA disk 1 whole disk + 64 = /dev/carmel/2 SATA disk 2 whole disk ... - 224 = /dev/sx8/7 SATA disk 7 whole disk + 224 = /dev/carmel/7 SATA disk 7 whole disk Partitions are handled in the same way as for IDE disks (see major number 3) except that the limit on @@ -2401,16 +2423,16 @@ Your cooperation is appreciated. 17 = /dev/irlpt1 Second IrLPT device ... -161 block Promise SX8 8-port SATA Disks on Second Controller - 0 = /dev/sx8/8 SATA disk 8 whole disk - 1 = /dev/sx8/8p1 SATA disk 8 partition 1 +161 block Carmel 8-port SATA Disks on Second Controller + 0 = /dev/carmel/8 SATA disk 8 whole disk + 1 = /dev/carmel/8p1 SATA disk 8 partition 1 ... - 31 = /dev/sx8/8p31 SATA disk 8 partition 31 + 31 = /dev/carmel/8p31 SATA disk 8 partition 31 - 32 = /dev/sx8/9 SATA disk 9 whole disk - 64 = /dev/sx8/10 SATA disk 10 whole disk + 32 = /dev/carmel/9 SATA disk 9 whole disk + 64 = /dev/carmel/10 SATA disk 10 whole disk ... - 224 = /dev/sx8/15 SATA disk 15 whole disk + 224 = /dev/carmel/15 SATA disk 15 whole disk Partitions are handled in the same way as for IDE disks (see major number 3) except that the limit on @@ -2720,6 +2742,9 @@ Your cooperation is appreciated. ... 39 = /dev/ttyDB7 DataBooster serial port 7 40 = /dev/ttySG0 SGI Altix console port + 41 = /dev/ttySMX0 Motorola i.MX - port 0 + 42 = /dev/ttySMX1 Motorola i.MX - port 1 + 43 = /dev/ttySMX2 Motorola i.MX - port 2 205 char Low-density serial ports (alternate device) 0 = /dev/culu0 Callout device for ttyLU0 @@ -2745,6 +2770,9 @@ Your cooperation is appreciated. ... 39 = /dev/cudb7 Callout device for ttyDB7 40 = /dev/cusg0 Callout device for ttySG0 + 41 = /dev/ttySMX0 Callout device for ttySMX0 + 42 = /dev/ttySMX1 Callout device for ttySMX1 + 43 = /dev/ttySMX2 Callout device for ttySMX2 206 char OnStream SC-x0 tape devices 0 = /dev/osst0 First OnStream SCSI tape, mode 0 --- linux-2.6.9-rc1/Documentation/DMA-API.txt 2004-08-24 00:02:48.000000000 -0700 +++ 25/Documentation/DMA-API.txt 2004-09-02 20:51:51.000000000 -0700 @@ -444,4 +444,83 @@ dma_alloc_noncoherent(), starting at vir continuing on for size. Again, you *must* observe the cache line boundaries when doing this. +int +dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, + dma_addr_t device_addr, size_t size, int + flags) + + +Declare region of memory to be handed out by dma_alloc_coherent when +it's asked for coherent memory for this device. + +bus_addr is the physical address to which the memory is currently +assigned in the bus responding region (this will be used by the +platform to perform the mapping) + +device_addr is the physical address the device needs to be programmed +with actually to address this memory (this will be handed out as the +dma_addr_t in dma_alloc_coherent()) + +size is the size of the area (must be multiples of PAGE_SIZE). + +flags can be or'd together and are + +DMA_MEMORY_MAP - request that the memory returned from +dma_alloc_coherent() be directly writeable. + +DMA_MEMORY_IO - request that the memory returned from +dma_alloc_coherent() be addressable using read/write/memcpy_toio etc. + +One or both of these flags must be present + +DMA_MEMORY_INCLUDES_CHILDREN - make the declared memory be allocated by +dma_alloc_coherent of any child devices of this one (for memory residing +on a bridge). + +DMA_MEMORY_EXCLUSIVE - only allocate memory from the declared regions. +Do not allow dma_alloc_coherent() to fall back to system memory when +it's out of memory in the declared region. + +The return value will be either DMA_MEMORY_MAP or DMA_MEMORY_IO and +must correspond to a passed in flag (i.e. no returning DMA_MEMORY_IO +if only DMA_MEMORY_MAP were passed in) for success or zero for +failure. + +Note, for DMA_MEMORY_IO returns, all subsequent memory returned by +dma_alloc_coherent() may no longer be accessed directly, but instead +must be accessed using the correct bus functions. If your driver +isn't prepared to handle this contingency, it should not specify +DMA_MEMORY_IO in the input flags. + +As a simplification for the platforms, only *one* such region of +memory may be declared per device. + +For reasons of efficiency, most platforms choose to track the declared +region only at the granularity of a page. For smaller allocations, +you should use the dma_pool() API. + +void +dma_release_declared_memory(struct device *dev) + +Remove the memory region previously declared from the system. This +API performs *no* in-use checking for this region and will return +unconditionally having removed all the required structures. It is the +drivers job to ensure that no parts of this memory region are +currently in use. + +void * +dma_mark_declared_memory_occupied(struct device *dev, + dma_addr_t device_addr, size_t size) + +This is used to occupy specific regions of the declared space +(dma_alloc_coherent() will hand out the first free region it finds). + +device_addr is the *device* address of the region requested + +size is the size (and should be a page sized multiple). + +The return value will be either a pointer to the processor virtual +address of the memory, or an error (via PTR_ERR()) if any part of the +region is occupied. + --- linux-2.6.9-rc1/Documentation/DocBook/gadget.tmpl 2004-08-24 00:02:47.000000000 -0700 +++ 25/Documentation/DocBook/gadget.tmpl 2004-09-02 20:51:51.000000000 -0700 @@ -2,8 +2,8 @@ USB Gadget API for Linux - 02 June 2003 - 02 June 2003 + 20 August 2004 + 20 August 2004 @@ -34,7 +34,7 @@ - 2003 + 2003-2004 David Brownell @@ -73,9 +73,13 @@ a number of important problems, includin composite devices, and alternate interface settings. + USB "On-The-Go" (OTG) support, in conjunction + with updates to the Linux-USB host side. + Sharing data structures and API models with the - Linux-USB host side API. This looks forward to USB "On-The-Go" - (OTG) and similar more-symmetric frameworks. + Linux-USB host side API. This helps the OTG support, and + looks forward to more-symmetric frameworks (where the same + I/O model is used by both host and device side drivers). Minimalist, so it's easier to support new device controller hardware. I/O processing doesn't imply large @@ -153,6 +157,7 @@ with the lowest level (which directly ha the SA-11x0 or PXA-25x UDC (found within many PDAs), and a variety of other products. + @@ -162,10 +167,14 @@ with the lowest level (which directly ha The lower boundary of this driver implements hardware-neutral USB functions, using calls to the controller driver. Because such hardware varies widely in capabilities and restrictions, - the gadget driver is normally configured at compile time + and is used in embedded environments where space is at a premium, + the gadget driver is often configured at compile time to work with endpoints supported by one particular controller. Gadget drivers may be portable to several different controllers, using conditional compilation. + (Recent kernels substantially simplify the work involved in + supporting new hardware, by autoconfiguring + endpoints automatically for many bulk-oriented drivers.) Gadget driver responsibilities include: @@ -178,8 +187,9 @@ with the lowest level (which directly ha altsettings, including enabling and configuring endpoints handling life cycle events, such as managing - bindings - to hardware, and disconnection from the USB host. + bindings to hardware, + USB suspend/resume, remote wakeup, + and disconnection from the USB host. managing IN and OUT transfers on all currently enabled endpoints @@ -244,15 +254,38 @@ with the lowest level (which directly ha -Over time, reusable utilities should evolve to help make some -gadget driver tasks simpler. An example of particular interest +OTG-capable systems will also need to include a standard Linux-USB +host side stack, +with usbcore, +one or more Host Controller Drivers (HCDs), +USB Device Drivers to support +the OTG "Targeted Peripheral List", +and so forth. +There will also be an OTG Controller Driver, +which is visible to gadget and device driver developers only indirectly. +That helps the host and device side USB controllers implement the +two new OTG protocols (HNP and SRP). +Roles switch (host to peripheral, or vice versa) using HNP +during USB suspend processing, and SRP can be viewed as a +more battery-friendly kind of device wakeup protocol. + + +Over time, reusable utilities are evolving to help make some +gadget driver tasks simpler. +For example, building configuration descriptors from vectors of +descriptors for the configurations interfaces and endpoints is +now automated, and many drivers now use autoconfiguration to +choose hardware endpoints and initialize their descriptors. + +A potential example of particular interest is code implementing standard USB-IF protocols for HID, networking, storage, or audio classes. Some developers are interested in KDB or KGDB hooks, to let target hardware be remotely debugged. Most such USB protocol code doesn't need to be hardware-specific, any more than network protocols like X11, HTTP, or NFS are. -Such interface drivers might be combined, to support composite devices. +Such gadget-side interface drivers should eventually be combined, +to implement composite devices. @@ -284,7 +317,7 @@ data types and functions are described h However, docproc does not understand all the C constructs that are used, so some relevant information is likely omitted from what you are reading. -One example of such information is several per-request flags. +One example of such information is endpoint autoconfiguration. You'll have to read the header file, and use example source code (such as that for "Gadget Zero"), to fully understand the API. @@ -292,7 +325,7 @@ code (such as that for "Gadget Zero"), t The part of the API implementing some basic driver capabilities is specific to the version of the Linux kernel that's in use. -The 2.5 kernel includes a driver model +The 2.6 kernel includes a driver model framework that has no analogue on earlier kernels; so those parts of the gadget API are not fully portable. (They are implemented on 2.4 kernels, but in a different way.) @@ -308,17 +341,21 @@ There are significant hardware features, that would be added using hardware-specific APIs. -This API expects drivers to use conditional compilation to handle -endpoint capabilities of different hardware. -Those tend to have arbitrary restrictions, relating to +This API allows drivers to use conditional compilation to handle +endpoint capabilities of different hardware, but doesn't require that. +Hardware tends to have arbitrary restrictions, relating to transfer types, addressing, packet sizes, buffering, and availability. As a rule, such differences only matter for "endpoint zero" logic that handles device configuration and management. -The API only supports limited run-time +The API supports limited run-time detection of capabilities, through naming conventions for endpoints. -Although a gadget driver could scan the endpoints available to it and -choose to map those capabilities onto driver functionality in some way, -few drivers will want to reconfigure themselves at run-time. +Many drivers will be able to at least partially autoconfigure +themselves. +In particular, driver init sections will often have endpoint +autoconfiguration logic that scans the hardware's list of endpoints +to find ones matching the driver requirements +(relying on those conventions), to eliminate some of the most +common reasons for conditional compilation. Like the Linux-USB host side API, this API exposes @@ -355,10 +392,14 @@ and so on. At this point the device is logically in the USB ch9 initial state ("attached"), drawing no power and not usable (since it does not yet support enumeration). +Any host should not see the device, since it's not +activated the data line pullup used by the host to +detect a device, even if VBUS power is available. Register a gadget driver that implements some higher level -device function. That will then bind() to a usb_gadget. +device function. That will then bind() to a usb_gadget, which +activates the data line pullup sometime after detecting VBUS. The hardware driver can now start enumerating. @@ -373,6 +414,8 @@ based both on what the bus interface har functionality being implemented. That can involve alternate settings or configurations, unless the hardware prevents such operation. +For OTG devices, each configuration descriptor includes +an OTG descriptor. The gadget driver handles the last step of enumeration, @@ -381,13 +424,18 @@ It enables all endpoints used in that co with all interfaces in their default settings. That involves using a list of the hardware's endpoints, enabling each endpoint according to its descriptor. +It may also involve using usb_gadget_vbus_draw +to let more power be drawn from VBUS, as allowed by that configuration. +For OTG devices, setting a configuration may also involve reporting +HNP capabilities through a user interface. Do real work and perform data transfers, possibly involving changes to interface settings or switching to new configurations, until the device is disconnect()ed from the host. Queue any number of transfer requests to each endpoint. -The drivers then go back to step 3 (above). +It may be suspended and resumed several times before being disconnected. +On disconnect, the drivers go back to step 3 (above). When the gadget driver module is being unloaded, @@ -399,7 +447,9 @@ driver be unloaded. Drivers will normally be arranged so that just loading the gadget driver module (or statically linking it into a Linux kernel) -allows the peripheral device to be enumerated. +allows the peripheral device to be enumerated, but some drivers +will defer enumeration until some higher level component (like +a user mode daemon) enables it. Note that at this lowest level there are no policies about how ep0 configuration logic is implemented, except that it should obey USB specifications. @@ -410,6 +460,18 @@ or understanding that composite devices be built by integrating reusable components. +Note that the lifecycle above can be slightly different +for OTG devices. +Other than providing an additional OTG descriptor in each +configuration, only the HNP-related differences are particularly +visible to driver code. +They involve reporting requirements during the SET_CONFIGURATION +request, and the option to invoke HNP during some suspend callbacks. +Also, SRP changes the semantics of +usb_gadget_wakeup +slightly. + + USB 2.0 Chapter 9 Types and Constants @@ -418,9 +480,9 @@ be built by integrating reusable compone rely on common USB structures and constants defined in the <linux/usb_ch9.h> -header file, which is standard in Linux 2.5 kernels. +header file, which is standard in Linux 2.6 kernels. These are the same types and constants used by host -side drivers. +side drivers (and usbcore). !Iinclude/linux/usb_ch9.h @@ -451,26 +513,38 @@ USB peripheral controller drivers. The core API is sufficient for writing a USB Gadget Driver, but some optional utilities are provided to simplify common tasks. +These utilities include endpoint autoconfiguration. !Edrivers/usb/gadget/usbstring.c !Edrivers/usb/gadget/config.c + Peripheral Controller Drivers -The first hardware supporting this API is the NetChip 2280 +The first hardware supporting this API was the NetChip 2280 controller, which supports USB 2.0 high speed and is based on PCI. This is the net2280 driver module. -The driver supports Linux kernel versions 2.4 and 2.5; +The driver supports Linux kernel versions 2.4 and 2.6; contact NetChip Technologies for development boards and product information. -For users of Intel's PXA 2xx series processors, -a pxa2xx_udc driver is available. +Other hardware working in the "gadget" framework includes: +Intel's PXA 25x and IXP42x series processors +(pxa2xx_udc), +Toshiba TC86c001 "Goku-S" (goku_udc), +Renesas SH7705/7727 (sh_udc), +MediaQ 11xx (mq11xx_udc), +Hynix HMS30C7202 (h7202_udc), +National 9303/4 (n9604_udc), +Texas Instruments OMAP (omap_udc), +Sharp LH7A40x (lh7a40x_udc), +and more. +Most of those are full speed controllers. At this writing, there are people at work on drivers in @@ -526,6 +600,15 @@ subset of CDC Ethernet. to avoid creating problems.) +Support for Microsoft's RNDIS +protocol has been contributed by Pengutronix and Auerswald GmbH. +This is like CDC Ethernet, but it runs on more slightly USB hardware +(but less than the CDC subset). +However, its main claim to fame is being able to connect directly to +recent versions of Windows, using drivers that Microsoft bundles +and supports, making it much simpler to network with Windows. + + There is also support for user mode gadget drivers, using gadgetfs. This provides a User Mode API that presents @@ -535,6 +618,10 @@ Familiar tools like GDB and pthreads can develop and debug user mode drivers, so that once a robust controller driver is available many applications for it won't require new kernel mode software. +Linux 2.6 Async I/O (AIO) +support is available, so that user mode software +can stream data with only slightly more overhead +than a kernel driver. There's a USB Mass Storage class driver, which provides @@ -548,6 +635,16 @@ storage class specification, using trans to access the data from the backing store. +There's a "serial line" driver, useful for TTY style +operation over USB. +The latest version of that driver supports CDC ACM style +operation, like a USB modem, and so on most hardware it can +interoperate easily with MS-Windows. +One interesting use of that driver is in boot firmware (like a BIOS), +which can sometimes use that model with very small systems without +real serial lines. + + Support for other kinds of gadget is expected to be developed and contributed over time, as this driver framework evolves. @@ -555,6 +652,96 @@ over time, as this driver framework evol +USB On-The-GO (OTG) + +USB OTG support on Linux 2.6 was initially developed +by Texas Instruments for +OMAP 16xx and 17xx +series processors. +Other OTG systems should work in similar ways, but the +hardware level details could be very different. + + +Systems need specialized hardware support to implement OTG, +notably including a special Mini-AB jack +and associated transciever to support Dual-Role +operation: +they can act either as a host, using the standard +Linux-USB host side driver stack, +or as a peripheral, using this "gadget" framework. +To do that, the system software relies on small additions +to those programming interfaces, +and on a new internal component (here called an "OTG Controller") +affecting which driver stack connects to the OTG port. +In each role, the system can re-use the existing pool of +hardware-neutral drivers, layered on top of the controller +driver interfaces (usb_bus or +usb_gadget). +Such drivers need at most minor changes, and most of the calls +added to support OTG can also benefit non-OTG products. + + + + Gadget drivers test the is_otg + flag, and use it to determine whether or not to include + an OTG descriptor in each of their configurations. + + Gadget drivers may need changes to support the + two new OTG protocols, exposed in new gadget attributes + such as b_hnp_enable flag. + HNP support should be reported through a user interface + (two LEDs could suffice), and is triggered in some cases + when the host suspends the peripheral. + SRP support can be user-initiated just like remote wakeup, + probably by pressing the same button. + + On the host side, USB device drivers need + to be taught to trigger HNP at appropriate moments, using + usb_suspend_device(). + That also conserves battery power, which is useful even + for non-OTG configurations. + + Also on the host side, a driver must support the + OTG "Targeted Peripheral List". That's just a whitelist, + used to reject peripherals not supported with a given + Linux OTG host. + This whitelist is product-specific; + each product must modify otg_whitelist.h + to match its interoperability specification. + + + Non-OTG Linux hosts, like PCs and workstations, + normally have some solution for adding drivers, so that + peripherals that aren't recognized can eventually be supported. + That approach is unreasonable for consumer products that may + never have their firmware upgraded, and where it's usually + unrealistic to expect traditional PC/workstation/server kinds + of support model to work. + For example, it's often impractical to change device firmware + once the product has been distributed, so driver bugs can't + normally be fixed if they're found after shipment. + + + + +Additional changes are needed below those hardware-neutral +usb_bus and usb_gadget +driver interfaces; those aren't discussed here in any detail. +Those affect the hardware-specific code for each USB Host or Peripheral +controller, and how the HCD initializes (since OTG can be active only +on a single port). +They also involve what may be called an OTG Controller +Driver, managing the OTG transceiver and the OTG state +machine logic as well as much of the root hub behavior for the +OTG port. +The OTG controller driver needs to activate and deactivate USB +controllers depending on the relevant device role. +Some related changes were needed inside usbcore, so that it +can identify OTG-capable devices and respond appropriately +to HNP or SRP protocols. + + + %s/%s\n", state_name(isp), tag); +} + +/* called from irq handlers */ +static void b_idle(struct isp1301 *isp, const char *tag) +{ + if (isp->otg.state == OTG_STATE_B_IDLE) + return; + + isp->otg.default_a = 0; + if (isp->otg.host) { + isp->otg.host->is_b_host = 1; + host_suspend(isp); + } + if (isp->otg.gadget) { + isp->otg.gadget->is_a_peripheral = 0; + gadget_suspend(isp); + } + isp->otg.state = OTG_STATE_B_IDLE; + isp->last_otg_ctrl = OTG_CTRL_REG = OTG_CTRL_REG & OTG_XCEIV_OUTPUTS; + pr_debug(" --> %s/%s\n", state_name(isp), tag); +} + +static void +dump_regs(struct isp1301 *isp, const char *label) +{ +#ifdef DEBUG + u8 ctrl = isp1301_get_u8(isp, ISP1301_OTG_CONTROL_1); + u8 status = isp1301_get_u8(isp, ISP1301_OTG_STATUS); + u8 src = isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE); + + pr_debug("otg: %06x, %s %s, otg/%02x stat/%02x.%02x\n", + OTG_CTRL_REG, label, state_name(isp), + ctrl, status, src); + /* mode control and irq enables don't change much */ +#endif +} + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_USB_OTG + +/* + * The OMAP OTG controller handles most of the OTG state transitions. + * + * We translate isp1301 outputs (mostly voltage comparator status) into + * OTG inputs; OTG outputs (mostly pullup/pulldown controls) and HNP state + * flags into isp1301 inputs ... and infer state transitions. + */ + +#ifdef VERBOSE + +static void check_state(struct isp1301 *isp, const char *tag) +{ + enum usb_otg_state state = OTG_STATE_UNDEFINED; + u8 fsm = OTG_TEST_REG & 0x0ff; + unsigned extra = 0; + + switch (fsm) { + + /* default-b */ + case 0x0: + state = OTG_STATE_B_IDLE; + break; + case 0x3: + case 0x7: + extra = 1; + case 0x1: + state = OTG_STATE_B_PERIPHERAL; + break; + case 0x11: + state = OTG_STATE_B_SRP_INIT; + break; + + /* extra dual-role default-b states */ + case 0x12: + case 0x13: + case 0x16: + extra = 1; + case 0x17: + state = OTG_STATE_B_WAIT_ACON; + break; + case 0x34: + state = OTG_STATE_B_HOST; + break; + + /* default-a */ + case 0x36: + state = OTG_STATE_A_IDLE; + break; + case 0x3c: + state = OTG_STATE_A_WAIT_VFALL; + break; + case 0x7d: + state = OTG_STATE_A_VBUS_ERR; + break; + case 0x9e: + case 0x9f: + extra = 1; + case 0x89: + state = OTG_STATE_A_PERIPHERAL; + break; + case 0xb7: + state = OTG_STATE_A_WAIT_VRISE; + break; + case 0xb8: + state = OTG_STATE_A_WAIT_BCON; + break; + case 0xb9: + state = OTG_STATE_A_HOST; + break; + case 0xba: + state = OTG_STATE_A_SUSPEND; + break; + default: + break; + } + if (isp->otg.state == state && !extra) + return; + pr_debug("otg: %s FSM %s/%02x, %s, %06x\n", tag, + state_string(state), fsm, state_name(isp), OTG_CTRL_REG); +} + +#else + +static inline void check_state(struct isp1301 *isp, const char *tag) { } + +#endif + +/* outputs from ISP1301_INTERRUPT_SOURCE */ +static void update_otg1(struct isp1301 *isp, u8 int_src) +{ + u32 otg_ctrl; + + otg_ctrl = OTG_CTRL_REG + & OTG_CTRL_MASK + & ~OTG_XCEIV_INPUTS + & ~(OTG_ID|OTG_ASESSVLD|OTG_VBUSVLD); + if (int_src & INTR_SESS_VLD) + otg_ctrl |= OTG_ASESSVLD; + else if (isp->otg.state == OTG_STATE_A_WAIT_VFALL) { + a_idle(isp, "vfall"); + otg_ctrl &= ~OTG_CTRL_BITS; + } + if (int_src & INTR_VBUS_VLD) + otg_ctrl |= OTG_VBUSVLD; + if (int_src & INTR_ID_GND) { /* default-A */ + if (isp->otg.state == OTG_STATE_B_IDLE + || isp->otg.state == OTG_STATE_UNDEFINED) { + a_idle(isp, "init"); + return; + } + } else { /* default-B */ + otg_ctrl |= OTG_ID; + if (isp->otg.state == OTG_STATE_A_IDLE + || isp->otg.state == OTG_STATE_UNDEFINED) { + b_idle(isp, "init"); + return; + } + } + OTG_CTRL_REG = otg_ctrl; +} + +/* outputs from ISP1301_OTG_STATUS */ +static void update_otg2(struct isp1301 *isp, u8 otg_status) +{ + u32 otg_ctrl; + + otg_ctrl = OTG_CTRL_REG + & OTG_CTRL_MASK + & ~OTG_XCEIV_INPUTS + & ~(OTG_BSESSVLD|OTG_BSESSEND); + if (otg_status & OTG_B_SESS_VLD) + otg_ctrl |= OTG_BSESSVLD; + else if (otg_status & OTG_B_SESS_END) + otg_ctrl |= OTG_BSESSEND; + OTG_CTRL_REG = otg_ctrl; +} + +/* inputs going to ISP1301 */ +static void otg_update_isp(struct isp1301 *isp) +{ + u32 otg_ctrl, otg_change; + u8 set = OTG1_DM_PULLDOWN, clr = OTG1_DM_PULLUP; + + otg_ctrl = OTG_CTRL_REG; + otg_change = otg_ctrl ^ isp->last_otg_ctrl; + isp->last_otg_ctrl = otg_ctrl; + otg_ctrl = otg_ctrl & OTG_XCEIV_INPUTS; + + switch (isp->otg.state) { + case OTG_STATE_B_IDLE: + case OTG_STATE_B_PERIPHERAL: + case OTG_STATE_B_SRP_INIT: + if (!(otg_ctrl & OTG_PULLUP)) { + // if (otg_ctrl & OTG_B_HNPEN) { + if (isp->otg.gadget->b_hnp_enable) { + isp->otg.state = OTG_STATE_B_WAIT_ACON; + pr_debug(" --> b_wait_acon\n"); + } + goto pulldown; + } +pullup: + set |= OTG1_DP_PULLUP; + clr |= OTG1_DP_PULLDOWN; + break; + case OTG_STATE_A_SUSPEND: + case OTG_STATE_A_PERIPHERAL: + if (otg_ctrl & OTG_PULLUP) + goto pullup; + /* FALLTHROUGH */ + // case OTG_STATE_B_WAIT_ACON: + default: +pulldown: + set |= OTG1_DP_PULLDOWN; + clr |= OTG1_DP_PULLUP; + break; + } + +# define toggle(OTG,ISP) do { \ + if (otg_ctrl & OTG) set |= ISP; \ + else clr |= ISP; \ + } while (0) + + if (!(isp->otg.host)) + otg_ctrl &= ~OTG_DRV_VBUS; + + switch (isp->otg.state) { + case OTG_STATE_A_SUSPEND: + if (otg_ctrl & OTG_DRV_VBUS) { + set |= OTG1_VBUS_DRV; + break; + } + /* HNP failed for some reason (A_AIDL_BDIS timeout) */ + notresponding(isp); + + /* FALLTHROUGH */ + case OTG_STATE_A_VBUS_ERR: + isp->otg.state = OTG_STATE_A_WAIT_VFALL; + pr_debug(" --> a_wait_vfall\n"); + /* FALLTHROUGH */ + case OTG_STATE_A_WAIT_VFALL: + /* FIXME usbcore thinks port power is still on ... */ + clr |= OTG1_VBUS_DRV; + break; + case OTG_STATE_A_IDLE: + if (otg_ctrl & OTG_DRV_VBUS) { + isp->otg.state = OTG_STATE_A_WAIT_VRISE; + pr_debug(" --> a_wait_vrise\n"); + } + /* FALLTHROUGH */ + default: + toggle(OTG_DRV_VBUS, OTG1_VBUS_DRV); + } + + toggle(OTG_PU_VBUS, OTG1_VBUS_CHRG); + toggle(OTG_PD_VBUS, OTG1_VBUS_DISCHRG); + +# undef toggle + + isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, set); + isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, clr); + + /* HNP switch to host or peripheral; and SRP */ + if (otg_change & OTG_PULLUP) { + switch (isp->otg.state) { + case OTG_STATE_B_IDLE: + if (clr & OTG1_DP_PULLUP) + break; + isp->otg.state = OTG_STATE_B_PERIPHERAL; + pr_debug(" --> b_peripheral\n"); + break; + case OTG_STATE_A_SUSPEND: + if (clr & OTG1_DP_PULLUP) + break; + isp->otg.state = OTG_STATE_A_PERIPHERAL; + pr_debug(" --> a_peripheral\n"); + break; + default: + break; + } + OTG_CTRL_REG |= OTG_PULLUP; + } + + check_state(isp, __FUNCTION__); + dump_regs(isp, "otg->isp1301"); +} + +static irqreturn_t omap_otg_irq(int irq, void *_isp, struct pt_regs *regs) +{ + u16 otg_irq = OTG_IRQ_SRC_REG; + u32 otg_ctrl; + int ret = IRQ_NONE; + struct isp1301 *isp = _isp; + + /* update ISP1301 transciever from OTG controller */ + if (otg_irq & OPRT_CHG) { + OTG_IRQ_SRC_REG = OPRT_CHG; + isp1301_defer_work(isp, WORK_UPDATE_ISP); + ret = IRQ_HANDLED; + + /* SRP to become b_peripheral failed */ + } else if (otg_irq & B_SRP_TMROUT) { + pr_debug("otg: B_SRP_TIMEOUT, %06x\n", OTG_CTRL_REG); + notresponding(isp); + + /* gadget drivers that care should monitor all kinds of + * remote wakeup (SRP, normal) using their own timer + * to give "check cable and A-device" messages. + */ + if (isp->otg.state == OTG_STATE_B_SRP_INIT) + b_idle(isp, "srp_timeout"); + + OTG_IRQ_SRC_REG = B_SRP_TMROUT; + ret = IRQ_HANDLED; + + /* HNP to become b_host failed */ + } else if (otg_irq & B_HNP_FAIL) { + pr_debug("otg: %s B_HNP_FAIL, %06x\n", + state_name(isp), OTG_CTRL_REG); + notresponding(isp); + + otg_ctrl = OTG_CTRL_REG; + otg_ctrl |= OTG_BUSDROP; + otg_ctrl &= OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; + OTG_CTRL_REG = otg_ctrl; + + /* subset of b_peripheral()... */ + isp->otg.state = OTG_STATE_B_PERIPHERAL; + pr_debug(" --> b_peripheral\n"); + + OTG_IRQ_SRC_REG = B_HNP_FAIL; + ret = IRQ_HANDLED; + + /* detect SRP from B-device ... */ + } else if (otg_irq & A_SRP_DETECT) { + pr_debug("otg: %s SRP_DETECT, %06x\n", + state_name(isp), OTG_CTRL_REG); + + isp1301_defer_work(isp, WORK_UPDATE_OTG); + switch (isp->otg.state) { + case OTG_STATE_A_IDLE: + if (!isp->otg.host) + break; + isp1301_defer_work(isp, WORK_HOST_RESUME); + otg_ctrl = OTG_CTRL_REG; + otg_ctrl |= OTG_A_BUSREQ; + otg_ctrl &= ~(OTG_BUSDROP|OTG_B_BUSREQ) + & ~OTG_XCEIV_INPUTS + & OTG_CTRL_MASK; + OTG_CTRL_REG = otg_ctrl; + break; + default: + break; + } + + OTG_IRQ_SRC_REG = A_SRP_DETECT; + ret = IRQ_HANDLED; + + /* timer expired: T(a_wait_bcon) and maybe T(a_wait_vrise) + * we don't track them separately + */ + } else if (otg_irq & A_REQ_TMROUT) { + otg_ctrl = OTG_CTRL_REG; + pr_info("otg: BCON_TMOUT from %s, %06x\n", + state_name(isp), otg_ctrl); + notresponding(isp); + + otg_ctrl |= OTG_BUSDROP; + otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; + OTG_CTRL_REG = otg_ctrl; + isp->otg.state = OTG_STATE_A_WAIT_VFALL; + + OTG_IRQ_SRC_REG = A_REQ_TMROUT; + ret = IRQ_HANDLED; + + /* A-supplied voltage fell too low; overcurrent */ + } else if (otg_irq & A_VBUS_ERR) { + otg_ctrl = OTG_CTRL_REG; + printk(KERN_ERR "otg: %s, VBUS_ERR %04x ctrl %06x\n", + state_name(isp), otg_irq, otg_ctrl); + + otg_ctrl |= OTG_BUSDROP; + otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; + OTG_CTRL_REG = otg_ctrl; + isp->otg.state = OTG_STATE_A_VBUS_ERR; + + OTG_IRQ_SRC_REG = A_VBUS_ERR; + ret = IRQ_HANDLED; + + /* switch driver; the transciever code activates it, + * ungating the udc clock or resuming OHCI. + */ + } else if (otg_irq & DRIVER_SWITCH) { + int kick = 0; + + otg_ctrl = OTG_CTRL_REG; + printk(KERN_NOTICE "otg: %s, SWITCH to %s, ctrl %06x\n", + state_name(isp), + (otg_ctrl & OTG_DRIVER_SEL) + ? "gadget" : "host", + otg_ctrl); + isp1301_defer_work(isp, WORK_UPDATE_ISP); + + /* role is peripheral */ + if (otg_ctrl & OTG_DRIVER_SEL) { + switch (isp->otg.state) { + case OTG_STATE_A_IDLE: + b_idle(isp, __FUNCTION__); + break; + default: + break; + } + isp1301_defer_work(isp, WORK_UPDATE_ISP); + + /* role is host */ + } else { + if (!(otg_ctrl & OTG_ID)) { + otg_ctrl &= OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS; + OTG_CTRL_REG = otg_ctrl | OTG_A_BUSREQ; + } + + if (isp->otg.host) { + switch (isp->otg.state) { + case OTG_STATE_B_WAIT_ACON: + isp->otg.state = OTG_STATE_B_HOST; + pr_debug(" --> b_host\n"); + kick = 1; + break; + case OTG_STATE_A_WAIT_BCON: + isp->otg.state = OTG_STATE_A_HOST; + pr_debug(" --> a_host\n"); + break; + case OTG_STATE_A_PERIPHERAL: + isp->otg.state = OTG_STATE_A_WAIT_BCON; + pr_debug(" --> a_wait_bcon\n"); + break; + default: + break; + } + isp1301_defer_work(isp, WORK_HOST_RESUME); + } + } + + OTG_IRQ_SRC_REG = DRIVER_SWITCH; + ret = IRQ_HANDLED; + + if (kick) + usb_bus_start_enum(isp->otg.host, + isp->otg.host->otg_port); + } + + check_state(isp, __FUNCTION__); + return ret; +} + +static struct platform_device *otg_dev; + +static int otg_init(struct isp1301 *isp) +{ + if (!otg_dev) + return -ENODEV; + + dump_regs(isp, __FUNCTION__); + /* some of these values are board-specific... */ + OTG_SYSCON_2_REG |= OTG_EN + /* for B-device: */ + | SRP_GPDATA /* 9msec Bdev D+ pulse */ + | SRP_GPDVBUS /* discharge after VBUS pulse */ + // | (3 << 24) /* 2msec VBUS pulse */ + /* for A-device: */ + | (0 << 20) /* 200ms nominal A_WAIT_VRISE timer */ + | SRP_DPW /* detect 167+ns SRP pulses */ + | SRP_DATA | SRP_VBUS /* accept both kinds of SRP pulse */ + ; + + update_otg1(isp, isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE)); + update_otg2(isp, isp1301_get_u8(isp, ISP1301_OTG_STATUS)); + + check_state(isp, __FUNCTION__); + pr_debug("otg: %s, %s %06x\n", + state_name(isp), __FUNCTION__, OTG_CTRL_REG); + + OTG_IRQ_EN_REG = DRIVER_SWITCH | OPRT_CHG + | B_SRP_TMROUT | B_HNP_FAIL + | A_VBUS_ERR | A_SRP_DETECT | A_REQ_TMROUT; + OTG_SYSCON_2_REG |= OTG_EN; + + return 0; +} + +static int otg_probe(struct device *dev) +{ + // struct omap_usb_config *config = dev->platform_data; + + otg_dev = to_platform_device(dev); + return 0; +} + +static int otg_remove(struct device *dev) +{ + otg_dev = 0; + return 0; +} + +struct device_driver omap_otg_driver = { + .name = "omap_otg", + .bus = &platform_bus_type, + .probe = otg_probe, + .remove = otg_remove, +}; + +static int otg_bind(struct isp1301 *isp) +{ + int status; + + if (otg_dev) + return -EBUSY; + + status = driver_register(&omap_otg_driver); + if (status < 0) + return status; + + if (otg_dev) + status = request_irq(otg_dev->resource[1].start, omap_otg_irq, + SA_INTERRUPT, DRIVER_NAME, isp); + else + status = -ENODEV; + + if (status < 0) + driver_unregister(&omap_otg_driver); + return status; +} + +static void otg_unbind(struct isp1301 *isp) +{ + if (!otg_dev) + return; + free_irq(otg_dev->resource[1].start, isp); +} + +#else + +/* OTG controller isn't clocked */ + +#endif /* CONFIG_USB_OTG */ + +/*-------------------------------------------------------------------------*/ + +static void b_peripheral(struct isp1301 *isp) +{ + enable_vbus_draw(isp, 8); + OTG_CTRL_REG = OTG_CTRL_REG & OTG_XCEIV_OUTPUTS; + usb_gadget_vbus_connect(isp->otg.gadget); + +#ifdef CONFIG_USB_OTG + otg_update_isp(isp); +#else + /* UDC driver just set OTG_BSESSVLD */ + isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_DP_PULLUP); + isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_DP_PULLDOWN); + isp->otg.state = OTG_STATE_B_PERIPHERAL; + pr_debug(" --> b_peripheral\n"); + dump_regs(isp, "2periph"); +#endif +} + +static int isp_update_otg(struct isp1301 *isp, u8 stat) +{ + u8 isp_stat, isp_bstat; + enum usb_otg_state state = isp->otg.state; + + if (stat & INTR_BDIS_ACON) + pr_debug("OTG: BDIS_ACON, %s\n", state_name(isp)); + + /* start certain state transitions right away */ + isp_stat = isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE); + if (isp_stat & INTR_ID_GND) { + if (isp->otg.default_a) { + switch (state) { + case OTG_STATE_B_IDLE: + a_idle(isp, "idle"); + /* FALLTHROUGH */ + case OTG_STATE_A_IDLE: + enable_vbus_source(isp); + /* FALLTHROUGH */ + case OTG_STATE_A_WAIT_VRISE: + /* we skip over OTG_STATE_A_WAIT_BCON, since + * the HC will transition to A_HOST (or + * A_SUSPEND!) without our noticing except + * when HNP is used. + */ + if (isp_stat & INTR_VBUS_VLD) + isp->otg.state = OTG_STATE_A_HOST; + break; + case OTG_STATE_A_WAIT_VFALL: + if (!(isp_stat & INTR_SESS_VLD)) + a_idle(isp, "vfell"); + break; + default: + if (!(isp_stat & INTR_VBUS_VLD)) + isp->otg.state = OTG_STATE_A_VBUS_ERR; + break; + } + isp_bstat = isp1301_get_u8(isp, ISP1301_OTG_STATUS); + } else { + switch (state) { + case OTG_STATE_B_PERIPHERAL: + case OTG_STATE_B_HOST: + case OTG_STATE_B_WAIT_ACON: + usb_gadget_vbus_disconnect(isp->otg.gadget); + break; + default: + break; + } + if (state != OTG_STATE_A_IDLE) + a_idle(isp, "id"); + if (isp->otg.host && state == OTG_STATE_A_IDLE) + isp1301_defer_work(isp, WORK_HOST_RESUME); + isp_bstat = 0; + } + } else { + /* if user unplugged mini-A end of cable, + * don't bypass A_WAIT_VFALL. + */ + if (isp->otg.default_a) { + switch (state) { + default: + isp->otg.state = OTG_STATE_A_WAIT_VFALL; + break; + case OTG_STATE_A_WAIT_VFALL: + state = OTG_STATE_A_IDLE; + /* khubd may take a while to notice and + * handle this disconnect, so don't go + * to B_IDLE quite yet. + */ + break; + case OTG_STATE_A_IDLE: + host_suspend(isp); + isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, + MC1_BDIS_ACON_EN); + isp->otg.state = OTG_STATE_B_IDLE; + OTG_CTRL_REG &= OTG_CTRL_REG & OTG_CTRL_MASK + & ~OTG_CTRL_BITS; + break; + case OTG_STATE_B_IDLE: + break; + } + } + isp_bstat = isp1301_get_u8(isp, ISP1301_OTG_STATUS); + + switch (isp->otg.state) { + case OTG_STATE_B_PERIPHERAL: + case OTG_STATE_B_WAIT_ACON: + case OTG_STATE_B_HOST: + if (likely(isp_bstat & OTG_B_SESS_VLD)) + break; + enable_vbus_draw(isp, 0); +#ifndef CONFIG_USB_OTG + /* UDC driver will clear OTG_BSESSVLD */ + isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, + OTG1_DP_PULLDOWN); + isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, + OTG1_DP_PULLUP); + dump_regs(isp, __FUNCTION__); +#endif + /* FALLTHROUGH */ + case OTG_STATE_B_SRP_INIT: + b_idle(isp, __FUNCTION__); + OTG_CTRL_REG &= OTG_CTRL_REG & OTG_XCEIV_OUTPUTS; + /* FALLTHROUGH */ + case OTG_STATE_B_IDLE: + if (isp->otg.gadget && (isp_bstat & OTG_B_SESS_VLD)) { +#ifdef CONFIG_USB_OTG + update_otg1(isp, isp_stat); + update_otg2(isp, isp_bstat); +#endif + b_peripheral(isp); + } else if (!(isp_stat & (INTR_VBUS_VLD|INTR_SESS_VLD))) + isp_bstat |= OTG_B_SESS_END; + break; + case OTG_STATE_A_WAIT_VFALL: + break; + default: + pr_debug("otg: unsupported b-device %s\n", + state_name(isp)); + break; + } + } + + if (state != isp->otg.state) + pr_debug(" isp, %s -> %s\n", + state_string(state), state_name(isp)); + +#ifdef CONFIG_USB_OTG + /* update the OTG controller state to match the isp1301; may + * trigger OPRT_CHG irqs for changes going to the isp1301. + */ + update_otg1(isp, isp_stat); + update_otg2(isp, isp_bstat); + check_state(isp, __FUNCTION__); +#endif + + dump_regs(isp, "isp1301->otg"); +} + +/*-------------------------------------------------------------------------*/ + +static u8 isp1301_clear_latch(struct isp1301 *isp) +{ + u8 latch = isp1301_get_u8(isp, ISP1301_INTERRUPT_LATCH); + isp1301_clear_bits(isp, ISP1301_INTERRUPT_LATCH, latch); + return latch; +} + +static void +isp1301_work(void *data) +{ + struct isp1301 *isp = data; + int stop; + + /* implicit lock: we're the only task using this device */ + isp->working = 1; + do { + stop = test_bit(WORK_STOP, &isp->todo); + +#ifdef CONFIG_USB_OTG + /* transfer state from otg engine to isp1301 */ + if (test_and_clear_bit(WORK_UPDATE_ISP, &isp->todo)) { + otg_update_isp(isp); + put_device(&isp->client.dev); + } +#endif + /* transfer state from isp1301 to otg engine */ + if (test_and_clear_bit(WORK_UPDATE_OTG, &isp->todo)) { + u8 stat = isp1301_clear_latch(isp); + + isp_update_otg(isp, stat); + put_device(&isp->client.dev); + } + + if (test_and_clear_bit(WORK_HOST_RESUME, &isp->todo)) { + u32 otg_ctrl; + + /* + * skip A_WAIT_VRISE; hc transitions invisibly + * skip A_WAIT_BCON; same. + */ + switch (isp->otg.state) { + case OTG_STATE_A_WAIT_BCON: + case OTG_STATE_A_WAIT_VRISE: + isp->otg.state = OTG_STATE_A_HOST; + pr_debug(" --> a_host\n"); + otg_ctrl = OTG_CTRL_REG; + otg_ctrl |= OTG_A_BUSREQ; + otg_ctrl &= ~(OTG_BUSDROP|OTG_B_BUSREQ) + & OTG_CTRL_MASK; + OTG_CTRL_REG = otg_ctrl; + break; + case OTG_STATE_B_WAIT_ACON: + isp->otg.state = OTG_STATE_B_HOST; + pr_debug(" --> b_host (acon)\n"); + break; + case OTG_STATE_B_HOST: + case OTG_STATE_B_IDLE: + case OTG_STATE_A_IDLE: + break; + default: + pr_debug(" host resume in %s\n", + state_name(isp)); + } + host_resume(isp); + // mdelay(10); + put_device(&isp->client.dev); + } + + if (test_and_clear_bit(WORK_TIMER, &isp->todo)) { +#ifdef VERBOSE + dump_regs(isp, "timer"); + if (!stop) + mod_timer(&isp->timer, jiffies + TIMER_JIFFIES); +#endif + put_device(&isp->client.dev); + } + + if (isp->todo) + dev_vdbg(&isp->client.dev, + "work done, todo = 0x%lx\n", + isp->todo); + if (stop) { + dev_dbg(&isp->client.dev, "stop\n"); + break; + } + } while (isp->todo); + isp->working = 0; +} + +static irqreturn_t isp1301_irq(int irq, void *isp, struct pt_regs *regs) +{ + isp1301_defer_work(isp, WORK_UPDATE_OTG); + return IRQ_HANDLED; +} + +static void isp1301_timer(unsigned long _isp) +{ + isp1301_defer_work((void *)_isp, WORK_TIMER); +} + +/*-------------------------------------------------------------------------*/ + +static void isp1301_release(struct device *dev) +{ + struct isp1301 *isp; + + isp = container_of(dev, struct isp1301, client.dev); + + /* ugly -- i2c hijacks our memory hook to wait_for_completion() */ + if (isp->i2c_release) + isp->i2c_release(dev); + kfree (isp); +} + +static struct isp1301 *the_transceiver; + +static int isp1301_detach_client(struct i2c_client *i2c) +{ + struct isp1301 *isp; + + isp = container_of(i2c, struct isp1301, client); + + isp1301_clear_bits(isp, ISP1301_INTERRUPT_FALLING, ~0); + isp1301_clear_bits(isp, ISP1301_INTERRUPT_RISING, ~0); + free_irq(isp->irq, isp); +#ifdef CONFIG_USB_OTG + otg_unbind(isp); +#endif + if (machine_is_omap_h2()) + omap_free_gpio(2); + + isp->timer.data = 0; + set_bit(WORK_STOP, &isp->todo); + del_timer_sync(&isp->timer); + flush_scheduled_work(); + + put_device(&i2c->dev); + the_transceiver = 0; + + return i2c_detach_client(i2c); +} + +/*-------------------------------------------------------------------------*/ + +/* NOTE: three modes are possible here, only one of which + * will be standards-conformant on any given system: + * + * - OTG mode (dual-role), required if there's a Mini-AB connector + * - HOST mode, for when there's one or more A (host) connectors + * - DEVICE mode, for when there's a B/Mini-B (device) connector + * + * As a rule, you won't have an isp1301 chip unless it's there to + * support the OTG mode. Other modes help testing USB controllers + * in isolation from (full) OTG support, or maybe so later board + * revisions can help to support those feature. + */ + +#ifdef CONFIG_USB_OTG + +static int isp1301_otg_enable(struct isp1301 *isp) +{ + power_up(isp); + otg_init(isp); + + /* NOTE: since we don't change this, this provides + * a few more interrupts than are strictly needed. + */ + isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING, + INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND); + isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING, + INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND); + + dev_info(&isp->client.dev, "ready for dual-role USB ...\n"); + + return 0; +} + +#endif + +/* add or disable the host device+driver */ +static int +isp1301_set_host(struct otg_transceiver *otg, struct usb_bus *host) +{ + struct isp1301 *isp = container_of(otg, struct isp1301, otg); + + if (!otg || isp != the_transceiver) + return -ENODEV; + + if (!host) { + OTG_IRQ_EN_REG = 0; + power_down(isp); + isp->otg.host = 0; + return 0; + } + +#ifdef CONFIG_USB_OTG + isp->otg.host = host; + dev_dbg(&isp->client.dev, "registered host\n"); + host_suspend(isp); + if (isp->otg.gadget) + return isp1301_otg_enable(isp); + return 0; + +#elif !defined(CONFIG_USB_GADGET_OMAP) + // FIXME update its refcount + isp->otg.host = host; + + power_up(isp); + + if (machine_is_omap_h2()) + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); + + dev_info(&isp->client.dev, "A-Host sessions ok\n"); + isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING, + INTR_ID_GND); + isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING, + INTR_ID_GND); + + /* If this has a Mini-AB connector, this mode is highly + * nonstandard ... but can be handy for testing, especially with + * the Mini-A end of an OTG cable. (Or something nonstandard + * like MiniB-to-StandardB, maybe built with a gender mender.) + */ + isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_VBUS_DRV); + + dump_regs(isp, __FUNCTION__); + + return 0; + +#else + dev_dbg(&isp->client.dev, "host sessions not allowed\n"); + return -EINVAL; +#endif + +} + +static int +isp1301_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *gadget) +{ + struct isp1301 *isp = container_of(otg, struct isp1301, otg); + + if (!otg || isp != the_transceiver) + return -ENODEV; + + if (!gadget) { + OTG_IRQ_EN_REG = 0; + if (!isp->otg.default_a) + enable_vbus_draw(isp, 0); + usb_gadget_vbus_disconnect(isp->otg.gadget); + isp->otg.gadget = 0; + power_down(isp); + return 0; + } + +#ifdef CONFIG_USB_OTG + isp->otg.gadget = gadget; + dev_dbg(&isp->client.dev, "registered gadget\n"); + /* gadget driver may be suspended until vbus_connect () */ + if (isp->otg.host) + return isp1301_otg_enable(isp); + return 0; + +#elif !defined(CONFIG_USB_OHCI_HCD) && !defined(CONFIG_USB_OHCI_HCD_MODULE) + isp->otg.gadget = gadget; + // FIXME update its refcount + + OTG_CTRL_REG = (OTG_CTRL_REG & OTG_CTRL_MASK + & ~(OTG_XCEIV_OUTPUTS|OTG_CTRL_BITS)) + | OTG_ID; + power_up(isp); + isp->otg.state = OTG_STATE_B_IDLE; + + if (machine_is_omap_h2()) + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); + + isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING, + INTR_SESS_VLD); + isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING, + INTR_VBUS_VLD); + dev_info(&isp->client.dev, "B-Peripheral sessions ok\n"); + dump_regs(isp, __FUNCTION__); + + /* If this has a Mini-AB connector, this mode is highly + * nonstandard ... but can be handy for testing, so long + * as you don't plug a Mini-A cable into the jack. + */ + if (isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE) & INTR_VBUS_VLD) + b_peripheral(isp); + + return 0; + +#else + dev_dbg(&isp->client.dev, "peripheral sessions not allowed\n"); + return -EINVAL; +#endif +} + + +/*-------------------------------------------------------------------------*/ + +static int +isp1301_set_power(struct otg_transceiver *dev, unsigned mA) +{ + if (!the_transceiver) + return -ENODEV; + if (dev->state == OTG_STATE_B_PERIPHERAL) + enable_vbus_draw(the_transceiver, mA); + return 0; +} + +static int +isp1301_start_srp(struct otg_transceiver *dev) +{ + struct isp1301 *isp = container_of(dev, struct isp1301, otg); + u32 otg_ctrl; + + if (!dev || isp != the_transceiver + || isp->otg.state != OTG_STATE_B_IDLE) + return -ENODEV; + + otg_ctrl = OTG_CTRL_REG; + if (!(otg_ctrl & OTG_BSESSEND)) + return -EINVAL; + + otg_ctrl |= OTG_B_BUSREQ; + otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK; + OTG_CTRL_REG = otg_ctrl; + isp->otg.state = OTG_STATE_B_SRP_INIT; + + pr_debug("otg: SRP, %s ... %06x\n", state_name(isp), OTG_CTRL_REG); +#ifdef CONFIG_USB_OTG + check_state(isp, __FUNCTION__); +#endif + return 0; +} + +static int +isp1301_start_hnp(struct otg_transceiver *dev) +{ +#ifdef CONFIG_USB_OTG + struct isp1301 *isp = container_of(dev, struct isp1301, otg); + + if (!dev || isp != the_transceiver) + return -ENODEV; + if (isp->otg.default_a && (isp->otg.host == NULL + || !isp->otg.host->b_hnp_enable)) + return -ENOTCONN; + if (!isp->otg.default_a && (isp->otg.gadget == NULL + || !isp->otg.gadget->b_hnp_enable)) + return -ENOTCONN; + + /* We want hardware to manage most HNP protocol timings. + * So do this part as early as possible... + */ + switch (isp->otg.state) { + case OTG_STATE_B_HOST: + isp->otg.state = OTG_STATE_B_PERIPHERAL; + /* caller will suspend next */ + break; + case OTG_STATE_A_HOST: +#if 0 + /* autoconnect mode avoids irq latency bugs */ + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, + MC1_BDIS_ACON_EN); +#endif + /* caller must suspend then clear A_BUSREQ */ + usb_gadget_vbus_connect(isp->otg.gadget); + OTG_CTRL_REG |= OTG_A_SETB_HNPEN; + + break; + case OTG_STATE_A_PERIPHERAL: + /* initiated by B-Host suspend */ + break; + default: + return -EILSEQ; + } + pr_debug("otg: HNP %s, %06x ...\n", + state_name(isp), OTG_CTRL_REG); + check_state(isp, __FUNCTION__); + return 0; +#else + /* srp-only */ + return -EINVAL; +#endif +} + +/*-------------------------------------------------------------------------*/ + +/* no error returns, they'd just make bus scanning stop */ +static int isp1301_probe(struct i2c_adapter *bus, int address, int kind) +{ + int status; + struct isp1301 *isp; + struct i2c_client *i2c; + + if (the_transceiver) + return 0; + + isp = kmalloc(sizeof *isp, GFP_KERNEL); + if (!isp) + return 0; + + memset(isp, 0, sizeof *isp); + + INIT_WORK(&isp->work, isp1301_work, isp); + init_timer(&isp->timer); + isp->timer.function = isp1301_timer; + isp->timer.data = (unsigned long) isp; + + isp->irq = -1; + isp->client.addr = address; + i2c_set_clientdata(&isp->client, isp); + isp->client.adapter = bus; + isp->client.id = 1301; + isp->client.driver = &isp1301_driver; + strlcpy(isp->client.name, DRIVER_NAME, I2C_NAME_SIZE); + i2c = &isp->client; + + /* if this is a true probe, verify the chip ... */ + if (kind < 0) { + status = isp1301_get_u16(isp, ISP1301_VENDOR_ID); + if (status != I2C_VENDOR_ID_PHILIPS) { + dev_dbg(&bus->dev, "addr %d not philips id: %d\n", + address, status); + goto fail1; + } + status = isp1301_get_u16(isp, ISP1301_PRODUCT_ID); + if (status != I2C_PRODUCT_ID_PHILIPS_1301) { + dev_dbg(&bus->dev, "%d not isp1301, %d\n", + address, status); + goto fail1; + } + } + + status = i2c_attach_client(i2c); + if (status < 0) { + dev_dbg(&bus->dev, "can't attach %s to device %d, err %d\n", + DRIVER_NAME, address, status); +fail1: + kfree(isp); + return 0; + } + isp->i2c_release = i2c->dev.release; + i2c->dev.release = isp1301_release; + + /* initial development used chiprev 2.00 */ + status = i2c_smbus_read_word_data(i2c, ISP1301_BCD_DEVICE); + dev_info(&i2c->dev, "chiprev %x.%02x, driver " DRIVER_VERSION "\n", + status >> 8, status & 0xff); + + /* make like power-on reset */ + isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_MASK); + + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_BI_DI); + isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_2, ~MC2_BI_DI); + + isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, + OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN); + isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, + ~(OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN)); + + isp1301_clear_bits(isp, ISP1301_INTERRUPT_LATCH, ~0); + isp1301_clear_bits(isp, ISP1301_INTERRUPT_FALLING, ~0); + isp1301_clear_bits(isp, ISP1301_INTERRUPT_RISING, ~0); + +#ifdef CONFIG_USB_OTG + status = otg_bind(isp); + if (status < 0) { + dev_dbg(&i2c->dev, "can't bind OTG\n"); + goto fail2; + } +#endif + + if (machine_is_omap_h2()) { + /* full speed signaling by default */ + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, + MC1_SPEED_REG); + isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, + MC2_SPD_SUSP_CTRL); + + /* IRQ wired at M14 */ + omap_cfg_reg(M14_1510_GPIO2); + isp->irq = OMAP_GPIO_IRQ(2); + omap_request_gpio(2); + omap_set_gpio_direction(2, 1); + omap_set_gpio_edge_ctrl(2, OMAP_GPIO_FALLING_EDGE); + } + + status = request_irq(isp->irq, isp1301_irq, + SA_SAMPLE_RANDOM, DRIVER_NAME, isp); + if (status < 0) { + dev_dbg(&i2c->dev, "can't get IRQ %d, err %d\n", + isp->irq, status); +#ifdef CONFIG_USB_OTG +fail2: +#endif + i2c_detach_client(i2c); + goto fail1; + } + + isp->otg.dev = &isp->client.dev; + isp->otg.label = DRIVER_NAME; + + isp->otg.set_host = isp1301_set_host, + isp->otg.set_peripheral = isp1301_set_peripheral, + isp->otg.set_power = isp1301_set_power, + isp->otg.start_srp = isp1301_start_srp, + isp->otg.start_hnp = isp1301_start_hnp, + + enable_vbus_draw(isp, 0); + power_down(isp); + the_transceiver = isp; + +#ifdef CONFIG_USB_OTG + update_otg1(isp, isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE)); + update_otg2(isp, isp1301_get_u8(isp, ISP1301_OTG_STATUS)); +#endif + + dump_regs(isp, __FUNCTION__); + +#ifdef VERBOSE + mod_timer(&isp->timer, jiffies + TIMER_JIFFIES); + dev_dbg(&i2c->dev, "scheduled timer, %d min\n", TIMER_MINUTES); +#endif + + status = otg_set_transceiver(&isp->otg); + if (status < 0) + dev_err(&i2c->dev, "can't register transceiver, %d\n", + status); + + return 0; +} + +static int isp1301_scan_bus(struct i2c_adapter *bus) +{ + if (!i2c_check_functionality(bus, I2C_FUNC_SMBUS_BYTE_DATA + | I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -EINVAL; + return i2c_probe(bus, &addr_data, isp1301_probe); +} + +static struct i2c_driver isp1301_driver = { + .owner = THIS_MODULE, + .name = "isp1301_omap", + .id = 1301, /* FIXME "official", i2c-ids.h */ + .class = I2C_CLASS_HWMON, + .flags = I2C_DF_NOTIFY, + .attach_adapter = isp1301_scan_bus, + .detach_client = isp1301_detach_client, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init isp_init(void) +{ + return i2c_add_driver(&isp1301_driver); +} +module_init(isp_init); + +static void __exit isp_exit(void) +{ + if (the_transceiver) + otg_set_transceiver(0); + i2c_del_driver(&isp1301_driver); +} +module_exit(isp_exit); + --- linux-2.6.9-rc1/drivers/i2c/chips/it87.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/i2c/chips/it87.c 2004-09-02 20:51:51.000000000 -0700 @@ -571,9 +571,9 @@ show_vid_reg(struct device *dev, char *b struct it87_data *data = it87_update_device(dev); return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); } -static DEVICE_ATTR(in0_ref, S_IRUGO, show_vid_reg, NULL); +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); #define device_create_file_vid(client) \ -device_create_file(&client->dev, &dev_attr_in0_ref) +device_create_file(&client->dev, &dev_attr_cpu0_vid) /* This function is called when: * it87_driver is inserted (when this module is loaded), for each --- linux-2.6.9-rc1/drivers/i2c/chips/Kconfig 2004-08-24 00:02:48.000000000 -0700 +++ 25/drivers/i2c/chips/Kconfig 2004-09-02 20:51:51.000000000 -0700 @@ -299,4 +299,16 @@ config SENSORS_RTC8564 This driver can also be built as a module. If so, the module will be called i2c-rtc8564. +config ISP1301_OMAP + tristate "Philips ISP1301 with OMAP OTG" + depends on I2C && ARCH_OMAP_OTG + help + If you say yes here you get support for the Philips ISP1301 + USB-On-The-Go transceiver working with the OMAP OTG controller. + The ISP1301 is used in products including H2 and H3 development + boards for Texas Instruments OMAP processors. + + This driver can also be built as a module. If so, the module + will be called isp1301_omap. + endmenu --- linux-2.6.9-rc1/drivers/i2c/chips/lm78.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/i2c/chips/lm78.c 2004-09-02 20:51:51.000000000 -0700 @@ -423,7 +423,7 @@ static ssize_t show_vid(struct device *d struct lm78_data *data = lm78_update_device(dev); return sprintf(buf, "%d\n", VID_FROM_REG(data->vid)); } -static DEVICE_ATTR(in0_ref, S_IRUGO, show_vid, NULL); +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); /* Alarms */ static ssize_t show_alarms(struct device *dev, char *buf) @@ -615,7 +615,7 @@ int lm78_detect(struct i2c_adapter *adap device_create_file(&new_client->dev, &dev_attr_fan3_min); device_create_file(&new_client->dev, &dev_attr_fan3_div); device_create_file(&new_client->dev, &dev_attr_alarms); - device_create_file(&new_client->dev, &dev_attr_in0_ref); + device_create_file(&new_client->dev, &dev_attr_cpu0_vid); return 0; --- linux-2.6.9-rc1/drivers/i2c/chips/lm85.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/drivers/i2c/chips/lm85.c 2004-09-02 20:51:51.000000000 -0700 @@ -465,7 +465,7 @@ static ssize_t show_vid_reg(struct devic return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); } -static DEVICE_ATTR(in0_ref, S_IRUGO, show_vid_reg, NULL); +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); static ssize_t show_vrm_reg(struct device *dev, char *buf) { @@ -874,7 +874,7 @@ int lm85_detect(struct i2c_adapter *adap device_create_file(&new_client->dev, &dev_attr_temp2_max); device_create_file(&new_client->dev, &dev_attr_temp3_max); device_create_file(&new_client->dev, &dev_attr_vrm); - device_create_file(&new_client->dev, &dev_attr_in0_ref); + device_create_file(&new_client->dev, &dev_attr_cpu0_vid); device_create_file(&new_client->dev, &dev_attr_alarms); return 0; --- linux-2.6.9-rc1/drivers/i2c/chips/Makefile 2004-08-24 00:03:50.000000000 -0700 +++ 25/drivers/i2c/chips/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -29,7 +29,9 @@ obj-$(CONFIG_SENSORS_RTC8564) += rtc8564 obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o obj-$(CONFIG_SENSORS_VIA686A) += via686a.o obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o +obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) EXTRA_CFLAGS += -DDEBUG endif + --- linux-2.6.9-rc1/drivers/i2c/chips/w83627hf.c 2004-08-24 00:03:28.000000000 -0700 +++ 25/drivers/i2c/chips/w83627hf.c 2004-09-02 20:51:51.000000000 -0700 @@ -635,9 +635,9 @@ show_vid_reg(struct device *dev, char *b struct w83627hf_data *data = w83627hf_update_device(dev); return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); } -static DEVICE_ATTR(in0_ref, S_IRUGO, show_vid_reg, NULL); +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); #define device_create_file_vid(client) \ -device_create_file(&client->dev, &dev_attr_in0_ref) +device_create_file(&client->dev, &dev_attr_cpu0_vid) static ssize_t show_vrm_reg(struct device *dev, char *buf) --- linux-2.6.9-rc1/drivers/i2c/chips/w83781d.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/drivers/i2c/chips/w83781d.c 2004-09-02 20:51:51.000000000 -0700 @@ -503,9 +503,9 @@ show_vid_reg(struct device *dev, char *b } static -DEVICE_ATTR(in0_ref, S_IRUGO, show_vid_reg, NULL); +DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); #define device_create_file_vid(client) \ -device_create_file(&client->dev, &dev_attr_in0_ref); +device_create_file(&client->dev, &dev_attr_cpu0_vid); static ssize_t show_vrm_reg(struct device *dev, char *buf) { --- linux-2.6.9-rc1/drivers/ide/ide.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/drivers/ide/ide.c 2004-09-03 00:56:53.514025280 -0700 @@ -1303,18 +1303,28 @@ static int set_io_32bit(ide_drive_t *dri static int set_using_dma (ide_drive_t *drive, int arg) { #ifdef CONFIG_BLK_DEV_IDEDMA + int ret = -EPERM; + + ide_pin_hwgroup(drive); + if (!drive->id || !(drive->id->capability & 1)) - return -EPERM; + goto out; if (HWIF(drive)->ide_dma_check == NULL) - return -EPERM; + goto out; + ret = -EIO; if (arg) { - if (HWIF(drive)->ide_dma_check(drive)) return -EIO; - if (HWIF(drive)->ide_dma_on(drive)) return -EIO; + if (HWIF(drive)->ide_dma_check(drive)) + goto out; + if (HWIF(drive)->ide_dma_on(drive)) + goto out; } else { if (__ide_dma_off(drive)) - return -EIO; + goto out; } - return 0; + ret = 0; +out: + ide_unpin_hwgroup(drive); + return ret; #else return -EPERM; #endif @@ -1348,23 +1358,6 @@ static int set_xfer_rate (ide_drive_t *d return err; } -int ide_atapi_to_scsi (ide_drive_t *drive, int arg) -{ - if (drive->media == ide_disk) { - drive->scsi = 0; - return 0; - } - - if (DRIVER(drive)->cleanup(drive)) { - drive->scsi = 0; - return 0; - } - - drive->scsi = (u8) arg; - ata_attach(drive); - return 0; -} - void ide_add_generic_settings (ide_drive_t *drive) { /* @@ -1379,8 +1372,6 @@ void ide_add_generic_settings (ide_drive ide_add_setting(drive, "init_speed", SETTING_RW, -1, -1, TYPE_BYTE, 0, 70, 1, 1, &drive->init_speed, NULL); ide_add_setting(drive, "current_speed", SETTING_RW, -1, -1, TYPE_BYTE, 0, 70, 1, 1, &drive->current_speed, set_xfer_rate); ide_add_setting(drive, "number", SETTING_RW, -1, -1, TYPE_BYTE, 0, 3, 1, 1, &drive->dn, NULL); - if (drive->media != ide_disk) - ide_add_setting(drive, "ide-scsi", SETTING_RW, -1, HDIO_SET_IDE_SCSI, TYPE_BYTE, 0, 1, 1, 1, &drive->scsi, ide_atapi_to_scsi); } int system_bus_clock (void) @@ -2029,7 +2020,7 @@ done: return 1; } -extern void pnpide_init(void); +extern int pnpide_init(void); extern void h8300_ide_init(void); /* --- linux-2.6.9-rc1/drivers/ide/ide-cd.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/drivers/ide/ide-cd.c 2004-09-03 00:56:49.236675536 -0700 @@ -1932,6 +1932,8 @@ static ide_startstop_t cdrom_start_write info->dma = drive->using_dma ? 1 : 0; info->cmd = WRITE; + info->devinfo.media_written = 1; + /* Start sending the write request to the drive. */ return cdrom_start_packet_command(drive, 32768, cdrom_start_write_cont); } @@ -1999,7 +2001,7 @@ ide_do_rw_cdrom (ide_drive_t *drive, str } CDROM_CONFIG_FLAGS(drive)->seeking = 0; } - if (IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap) { + if ((rq_data_dir(rq) == READ) && IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap) { action = cdrom_start_seek(drive, block); } else { if (rq_data_dir(rq) == READ) @@ -2960,8 +2962,10 @@ int ide_cdrom_probe_capabilities (ide_dr CDROM_CONFIG_FLAGS(drive)->no_eject = 0; if (cap.cd_r_write) CDROM_CONFIG_FLAGS(drive)->cd_r = 1; - if (cap.cd_rw_write) + if (cap.cd_rw_write) { CDROM_CONFIG_FLAGS(drive)->cd_rw = 1; + CDROM_CONFIG_FLAGS(drive)->ram = 1; + } if (cap.test_write) CDROM_CONFIG_FLAGS(drive)->test_write = 1; if (cap.dvd_ram_read || cap.dvd_r_read || cap.dvd_rom) --- linux-2.6.9-rc1/drivers/ide/ide-disk.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/drivers/ide/ide-disk.c 2004-09-03 00:57:15.253720344 -0700 @@ -1298,7 +1298,7 @@ static int set_nowerr(ide_drive_t *drive return 0; } -static int write_cache (ide_drive_t *drive, int arg) +static int write_cache(ide_drive_t *drive, int arg) { ide_task_t args; int err; @@ -1406,7 +1406,7 @@ static void idedisk_complete_power_step { switch (rq->pm->pm_step) { case idedisk_pm_flush_cache: /* Suspend step 1 (flush cache) complete */ - if (rq->pm->pm_state == 4) + if (system_state == SYSTEM_SNAPSHOT) rq->pm->pm_step = ide_pm_state_completed; else rq->pm->pm_step = idedisk_pm_standby; @@ -1508,7 +1508,7 @@ static void idedisk_setup (ide_drive_t * blk_queue_max_sectors(drive->queue, max_s); } - printk("%s: max request size: %dKiB\n", drive->name, drive->queue->max_sectors / 2); + printk(KERN_INFO "%s: max request size: %dKiB\n", drive->name, drive->queue->max_sectors / 2); /* Extract geometry if we did not already have one for the drive */ if (!drive->cyl || !drive->head || !drive->sect) { @@ -1537,7 +1537,7 @@ static void idedisk_setup (ide_drive_t * /* limit drive capacity to 137GB if LBA48 cannot be used */ if (drive->addressing == 0 && drive->capacity64 > 1ULL << 28) { - printk("%s: cannot use LBA48 - full capacity " + printk(KERN_WARNING "%s: cannot use LBA48 - full capacity " "%llu sectors (%llu MB)\n", drive->name, (unsigned long long)drive->capacity64, sectors_to_MB(drive->capacity64)); @@ -1607,23 +1607,31 @@ static void idedisk_setup (ide_drive_t * if ((id->csfo & 1) || (id->cfs_enable_1 & (1 << 5))) drive->wcache = 1; - write_cache(drive, 1); - /* - * decide if we can sanely support flushes and barriers on - * this drive. unfortunately not all drives advertise FLUSH_CACHE - * support even if they support it. So assume FLUSH_CACHE is there - * always. LBA48 drives are newer, so expect it to flag support - * properly. We can safely support FLUSH_CACHE on lba48, if capacity - * doesn't exceed lba28 + * We must avoid issuing commands a drive does not understand + * or we may crash it. We check flush cache is supported. We also + * check we have the LBA48 flush cache if the drive capacity is + * too large. By this time we have trimmed the drive capacity if + * LBA48 is not available so we don't need to recheck that. */ - barrier = 1; + barrier = 0; + if (ide_id_has_flush_cache(id)) + barrier = 1; if (drive->addressing == 1) { + /* Can't issue the correct flush ? */ if (capacity > (1ULL << 28) && !ide_id_has_flush_cache_ext(id)) barrier = 0; } + /* Now we have barrier awareness we can be properly conservative + by default with other drives. We turn off write caching when + barrier is not available. Users can adjust this at runtime if + they need unsafe but fast filesystems. This will reduce the + performance of non cache flush supporting disks but it means + you get the data order guarantees the journalling fs's require */ + + write_cache(drive, barrier); - printk("%s: cache flushes %ssupported\n", + printk(KERN_DEBUG "%s: cache flushes %ssupported\n", drive->name, barrier ? "" : "not "); if (barrier) { blk_queue_ordered(drive->queue, 1); @@ -1809,11 +1817,9 @@ static int idedisk_attach(ide_drive_t *d if ((!drive->head || drive->head > 16) && !drive->select.b.lba) { printk(KERN_ERR "%s: INVALID GEOMETRY: %d PHYSICAL HEADS?\n", drive->name, drive->head); - ide_cacheflush_p(drive); - ide_unregister_subdriver(drive); - DRIVER(drive)->busy--; - goto failed; - } + drive->attach = 0; + } else + drive->attach = 1; DRIVER(drive)->busy--; g->minors = 1 << PARTN_BITS; strcpy(g->devfs_name, drive->devfs_name); @@ -1821,7 +1827,6 @@ static int idedisk_attach(ide_drive_t *d g->flags = drive->removable ? GENHD_FL_REMOVABLE : 0; set_capacity(g, current_capacity(drive)); g->fops = &idedisk_ops; - drive->attach = 1; add_disk(g); return 0; failed: --- linux-2.6.9-rc1/drivers/ide/ide-io.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/ide/ide-io.c 2004-09-03 00:56:53.515025128 -0700 @@ -883,6 +883,46 @@ void ide_stall_queue (ide_drive_t *drive drive->sleep = timeout + jiffies; } +void ide_unpin_hwgroup(ide_drive_t *drive) +{ + ide_hwgroup_t *hwgroup = HWGROUP(drive); + + if (hwgroup) { + spin_lock_irq(&ide_lock); + HWGROUP(drive)->busy = 0; + drive->blocked = 0; + do_ide_request(drive->queue); + spin_unlock_irq(&ide_lock); + } +} + +void ide_pin_hwgroup(ide_drive_t *drive) +{ + ide_hwgroup_t *hwgroup = HWGROUP(drive); + + /* + * should only happen very early, so not a problem + */ + if (!hwgroup) + return; + + spin_lock_irq(&ide_lock); + do { + if (!hwgroup->busy && !drive->blocked && !drive->doing_barrier) + break; + spin_unlock_irq(&ide_lock); + schedule_timeout(HZ/100); + spin_lock_irq(&ide_lock); + } while (hwgroup->busy || drive->blocked || drive->doing_barrier); + + /* + * we've now secured exclusive access to this hwgroup + */ + hwgroup->busy = 1; + drive->blocked = 1; + spin_unlock_irq(&ide_lock); +} + EXPORT_SYMBOL(ide_stall_queue); #define WAKEUP(drive) ((drive)->service_start + 2 * (drive)->service_time) --- linux-2.6.9-rc1/drivers/ide/ide-lib.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/drivers/ide/ide-lib.c 2004-09-03 00:56:53.516024976 -0700 @@ -436,13 +436,17 @@ EXPORT_SYMBOL(ide_toggle_bounce); int ide_set_xfer_rate(ide_drive_t *drive, u8 rate) { + int ret; #ifndef CONFIG_BLK_DEV_IDEDMA rate = min(rate, (u8) XFER_PIO_4); #endif - if(HWIF(drive)->speedproc) - return HWIF(drive)->speedproc(drive, rate); + ide_pin_hwgroup(drive); + if (HWIF(drive)->speedproc) + ret = HWIF(drive)->speedproc(drive, rate); else - return -1; + ret = -1; + ide_unpin_hwgroup(drive); + return ret; } EXPORT_SYMBOL_GPL(ide_set_xfer_rate); --- linux-2.6.9-rc1/drivers/ide/ide-pnp.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/ide/ide-pnp.c 2004-09-03 00:56:50.828433552 -0700 @@ -69,7 +69,21 @@ static struct pnp_driver idepnp_driver = .remove = idepnp_remove, }; -void __init pnpide_init(void) +int __init pnpide_init(void) { - pnp_register_driver(&idepnp_driver); + return pnp_register_driver(&idepnp_driver); } + +#ifdef MODULE +static void __exit pnpide_exit(void) +{ + pnp_unregister_driver(&idepnp_driver); +} + +module_init(pnpide_init); +module_exit(pnpide_exit); +#endif + +MODULE_AUTHOR("Andrey Panin"); +MODULE_DESCRIPTION("Enabler for ISAPNP IDE devices"); +MODULE_LICENSE("GPL"); --- linux-2.6.9-rc1/drivers/ide/ide-probe.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/ide/ide-probe.c 2004-09-02 20:51:51.000000000 -0700 @@ -635,12 +635,11 @@ static void hwif_register (ide_hwif_t *h device_register(&hwif->gendev); } -#ifdef CONFIG_PPC static int wait_hwif_ready(ide_hwif_t *hwif) { int rc; - printk(KERN_INFO "Probing IDE interface %s...\n", hwif->name); + printk(KERN_DEBUG "Probing IDE interface %s...\n", hwif->name); /* Let HW settle down a bit from whatever init state we * come from */ @@ -671,7 +670,6 @@ static int wait_hwif_ready(ide_hwif_t *h return rc; } -#endif /* * This routine only knows how to look for drive units 0 and 1 @@ -717,7 +715,6 @@ static void probe_hwif(ide_hwif_t *hwif) local_irq_set(flags); -#ifdef CONFIG_PPC /* This is needed on some PPCs and a bunch of BIOS-less embedded * platforms. Typical cases are: * @@ -738,8 +735,7 @@ static void probe_hwif(ide_hwif_t *hwif) * BenH. */ if (wait_hwif_ready(hwif)) - printk(KERN_WARNING "%s: Wait for ready failed before probe !\n", hwif->name); -#endif /* CONFIG_PPC */ + printk(KERN_DEBUG "%s: Wait for ready failed before probe !\n", hwif->name); /* * Second drive should only exist if first drive was found, @@ -749,6 +745,18 @@ static void probe_hwif(ide_hwif_t *hwif) ide_drive_t *drive = &hwif->drives[unit]; drive->dn = (hwif->channel ? 2 : 0) + unit; (void) probe_for_drive(drive); + if (drive->present && hwif->present && unit == 1) { + if (strcmp(hwif->drives[0].id->model, drive->id->model) == 0 && + /* Don't do this for noprobe or non ATA */ + strcmp(drive->id->model, "UNKNOWN") && + /* And beware of confused Maxtor drives that go "M0000000000" + "The SN# is garbage in the ID block..." [Eric] */ + strncmp(drive->id->serial_no, "M0000000000000000000", 20) && + strncmp(hwif->drives[0].id->serial_no, drive->id->serial_no, 20) == 0) { + printk(KERN_WARNING "ide-probe: ignoring undecoded slave\n"); + drive->present = 0; + } + } if (drive->present && !hwif->present) { hwif->present = 1; if (hwif->chipset != ide_4drives || --- linux-2.6.9-rc1/drivers/ide/ide-tape.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/drivers/ide/ide-tape.c 2004-09-02 20:51:51.000000000 -0700 @@ -530,7 +530,6 @@ typedef struct os_dat_s { */ #define IDETAPE_DEBUG_INFO 0 #define IDETAPE_DEBUG_LOG 0 -#define IDETAPE_DEBUG_LOG_VERBOSE 0 #define IDETAPE_DEBUG_BUGS 1 /* @@ -1260,70 +1259,6 @@ typedef struct { */ static idetape_chrdev_t idetape_chrdevs[MAX_HWIFS * MAX_DRIVES]; -#if IDETAPE_DEBUG_LOG_VERBOSE - -/* - * DO NOT REMOVE, BUILDING A VERBOSE DEBUG SCHEME FOR ATAPI - */ - -char *idetape_sense_key_verbose(u8 idetape_sense_key) -{ - switch (idetape_sense_key) { - default: { - char buf[22]; - sprintf(buf, "IDETAPE_SENSE (0x%02x)", idetape_sense_key); - return(buf); - } - - } -} - -char *idetape_command_key_verbose(u8 idetape_command_key) -{ - switch (idetape_command_key) { - case IDETAPE_TEST_UNIT_READY_CMD: - return("TEST_UNIT_READY_CMD"); - case IDETAPE_REWIND_CMD: - return("REWIND_CMD"); - case IDETAPE_REQUEST_SENSE_CMD: - return("REQUEST_SENSE_CMD"); - case IDETAPE_READ_CMD: - return("READ_CMD"); - case IDETAPE_WRITE_CMD: - return("WRITE_CMD"); - case IDETAPE_WRITE_FILEMARK_CMD: - return("WRITE_FILEMARK_CMD"); - case IDETAPE_SPACE_CMD: - return("SPACE_CMD"); - case IDETAPE_INQUIRY_CMD: - return("INQUIRY_CMD"); - case IDETAPE_ERASE_CMD: - return("ERASE_CMD"); - case IDETAPE_MODE_SENSE_CMD: - return("MODE_SENSE_CMD"); - case IDETAPE_MODE_SELECT_CMD: - return("MODE_SELECT_CMD"); - case IDETAPE_LOAD_UNLOAD_CMD: - return("LOAD_UNLOAD_CMD"); - case IDETAPE_PREVENT_CMD: - return("PREVENT_CMD"); - case IDETAPE_LOCATE_CMD: - return("LOCATE_CMD"); - case IDETAPE_READ_POSITION_CMD: - return("READ_POSITION_CMD"); - case IDETAPE_READ_BUFFER_CMD: - return("READ_BUFFER_CMD"); - case IDETAPE_SET_SPEED_CMD: - return("SET_SPEED_CMD"); - default: { - char buf[20]; - sprintf(buf, "CMD (0x%02x)", idetape_command_key); - return(buf); - } - } -} -#endif /* IDETAPE_DEBUG_LOG_VERBOSE */ - /* * Function declarations * @@ -1507,15 +1442,6 @@ static void idetape_analyze_error (ide_d "asc = %x, ascq = %x\n", pc->c[0], result->sense_key, result->asc, result->ascq); -#if IDETAPE_DEBUG_LOG_VERBOSE - if (tape->debug_level >= 1) - printk(KERN_INFO "ide-tape: pc = %s, sense key = %x, " - "asc = %x, ascq = %x\n", - idetape_command_key_verbose((byte) pc->c[0]), - result->sense_key, - result->asc, - result->ascq); -#endif /* IDETAPE_DEBUG_LOG_VERBOSE */ #endif /* IDETAPE_DEBUG_LOG */ /* --- linux-2.6.9-rc1/drivers/ide/Kconfig 2004-08-24 00:02:33.000000000 -0700 +++ 25/drivers/ide/Kconfig 2004-09-03 00:56:52.408193392 -0700 @@ -325,7 +325,7 @@ config BLK_DEV_CMD640_ENHANCED Otherwise say N. config BLK_DEV_IDEPNP - bool "PNP EIDE support" + tristate "PNP EIDE support" depends on PNP help If you have a PnP (Plug and Play) compatible EIDE card and @@ -621,6 +621,12 @@ config BLK_DEV_IT8172 ; picture of the board at . +config BLK_DEV_IT8212 + tristate "IT8212 IDE support (Experimental)" + help + This driver adds support for the ITE 8212 IDE RAID controller in + both RAID and pass-through mode. + config BLK_DEV_NS87415 tristate "NS87415 chipset support" help --- linux-2.6.9-rc1/drivers/ide/legacy/Makefile 2004-08-24 00:01:55.000000000 -0700 +++ 25/drivers/ide/legacy/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -10,6 +10,5 @@ obj-$(CONFIG_BLK_DEV_IDECS) += ide-cs.o # Last of all obj-$(CONFIG_BLK_DEV_HD) += hd.o -obj-$(CONFIG_BLK_DEV_HD98) += hd98.o EXTRA_CFLAGS := -Idrivers/ide --- linux-2.6.9-rc1/drivers/ide/legacy/pdc4030.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/ide/legacy/pdc4030.c 2004-09-02 20:51:51.000000000 -0700 @@ -354,15 +354,6 @@ read_next: (unsigned long)rq->nr_sectors - nsect); #endif /* DEBUG_READ */ -#ifdef CONFIG_IDE_TASKFILE_IO - task_bio_sectors(drive, rq, nsect, IDE_PIO_IN); - - /* FIXME: can we check status after transfer on pdc4030? */ - /* Complete previously submitted bios. */ - while (rq->bio != rq->cbio) - if (!DRIVER(drive)->end_request(drive, 1, bio_sectors(rq->bio))) - return ide_stopped; -#else /* CONFIG_IDE_TASKFILE_IO */ HWIF(drive)->ata_input_data(drive, rq->buffer, nsect * SECTOR_WORDS); rq->buffer += nsect<<9; rq->sector += nsect; @@ -370,7 +361,6 @@ read_next: rq->nr_sectors -= nsect; if (!rq->current_nr_sectors) DRIVER(drive)->end_request(drive, 1, 0); -#endif /* CONFIG_IDE_TASKFILE_IO */ /* * Now the data has been read in, do the following: @@ -421,12 +411,8 @@ read_next: static ide_startstop_t promise_complete_pollfunc(ide_drive_t *drive) { ide_hwgroup_t *hwgroup = HWGROUP(drive); -#ifdef CONFIG_IDE_TASKFILE_IO - struct request *rq = hwgroup->rq; -#else struct request *rq = &hwgroup->wrq; struct bio *bio = rq->bio; -#endif if ((HWIF(drive)->INB(IDE_STATUS_REG)) & BUSY_STAT) { if (time_before(jiffies, hwgroup->poll_timeout)) { @@ -450,15 +436,10 @@ static ide_startstop_t promise_complete_ printk(KERN_DEBUG "%s: Write complete - end_request\n", drive->name); #endif /* DEBUG_WRITE */ -#ifdef CONFIG_IDE_TASKFILE_IO - /* Complete previously submitted bios. */ - while (rq->bio != rq->cbio) - (void) DRIVER(drive)->end_request(drive, 1, bio_sectors(rq->bio)); -#else bio->bi_idx = bio->bi_vcnt - rq->nr_cbio_segments; rq = hwgroup->rq; DRIVER(drive)->end_request(drive, 1, rq->hard_nr_sectors); -#endif + return ide_stopped; } @@ -466,27 +447,6 @@ static ide_startstop_t promise_complete_ * promise_multwrite() transfers a block of up to mcount sectors of data * to a drive as part of a disk multiple-sector write operation. */ -#ifdef CONFIG_IDE_TASKFILE_IO -static void promise_multwrite (ide_drive_t *drive, unsigned int msect) -{ - struct request* rq = HWGROUP(drive)->rq; - unsigned int nsect; - - rq->errors = 0; - do { - nsect = rq->current_nr_sectors; - if (nsect > msect) - nsect = msect; - - task_bio_sectors(drive, rq, nsect, IDE_PIO_OUT); - - if (!rq->nr_sectors) - msect = 0; - else - msect -= nsect; - } while (msect); -} -#else /* CONFIG_IDE_TASKFILE_IO */ static void promise_multwrite (ide_drive_t *drive, unsigned int mcount) { ide_hwgroup_t *hwgroup = HWGROUP(drive); @@ -537,7 +497,6 @@ static void promise_multwrite (ide_drive taskfile_output_data(drive, buffer, nsect<<7); } while (mcount); } -#endif /* * promise_write_pollfunc() is the handler for disk write completion polling. @@ -545,12 +504,8 @@ static void promise_multwrite (ide_drive static ide_startstop_t promise_write_pollfunc (ide_drive_t *drive) { ide_hwgroup_t *hwgroup = HWGROUP(drive); -#ifdef CONFIG_IDE_TASKFILE_IO - struct request *rq = hwgroup->rq; -#else struct request *rq = &hwgroup->wrq; struct bio *bio = rq->bio; -#endif if (HWIF(drive)->INB(IDE_NSECTOR_REG) != 0) { if (time_before(jiffies, hwgroup->poll_timeout)) { @@ -564,19 +519,11 @@ static ide_startstop_t promise_write_pol } hwgroup->poll_timeout = 0; printk(KERN_ERR "%s: write timed-out!\n",drive->name); -#ifndef CONFIG_IDE_TASKFILE_IO bio->bi_idx = bio->bi_vcnt - rq->nr_cbio_segments; -#endif return DRIVER(drive)->error(drive, "write timeout", HWIF(drive)->INB(IDE_STATUS_REG)); } -#ifdef CONFIG_IDE_TASKFILE_IO - /* Complete previously submitted bios. */ - while (rq->bio != rq->cbio) - (void) DRIVER(drive)->end_request(drive, 1, bio_sectors(rq->bio)); -#endif - /* * Now write out last 4 sectors and poll for not BUSY */ @@ -602,11 +549,7 @@ static ide_startstop_t promise_write_pol static ide_startstop_t promise_write (ide_drive_t *drive) { ide_hwgroup_t *hwgroup = HWGROUP(drive); -#ifdef CONFIG_IDE_TASKFILE_IO - struct request *rq = hwgroup->rq; -#else struct request *rq = &hwgroup->wrq; -#endif #ifdef DEBUG_WRITE printk(KERN_DEBUG "%s: %s: sectors(%lu-%lu)\n", @@ -654,39 +597,14 @@ static ide_startstop_t promise_write (id * already set up. It issues a READ or WRITE command to the Promise * controller, assuming LBA has been used to set up the block number. */ -#ifndef CONFIG_IDE_TASKFILE_IO ide_startstop_t do_pdc4030_io (ide_drive_t *drive, struct request *rq) { ide_startstop_t startstop; unsigned long timeout; u8 stat = 0; -#else -static ide_startstop_t do_pdc4030_io (ide_drive_t *drive, ide_task_t *task) -{ - struct request *rq = HWGROUP(drive)->rq; - task_struct_t *taskfile = (task_struct_t *) task->tfRegister; - ide_startstop_t startstop; - unsigned long timeout; - u8 stat = 0; - - if (IDE_CONTROL_REG) - HWIF(drive)->OUTB(drive->ctl, IDE_CONTROL_REG); /* clear nIEN */ - SELECT_MASK(drive, 0); - HWIF(drive)->OUTB(taskfile->feature, IDE_FEATURE_REG); - HWIF(drive)->OUTB(taskfile->sector_count, IDE_NSECTOR_REG); - /* refers to number of sectors to transfer */ - HWIF(drive)->OUTB(taskfile->sector_number, IDE_SECTOR_REG); - /* refers to sector offset or start sector */ - HWIF(drive)->OUTB(taskfile->low_cylinder, IDE_LCYL_REG); - HWIF(drive)->OUTB(taskfile->high_cylinder, IDE_HCYL_REG); - HWIF(drive)->OUTB(taskfile->device_head, IDE_SELECT_REG); - HWIF(drive)->OUTB(taskfile->command, IDE_COMMAND_REG); -#endif if (rq_data_dir(rq) == READ) { -#ifndef CONFIG_IDE_TASKFILE_IO HWIF(drive)->OUTB(PROMISE_READ, IDE_COMMAND_REG); -#endif /* * The card's behaviour is odd at this point. If the data is * available, DRQ will be true, and no interrupt will be @@ -722,9 +640,7 @@ static ide_startstop_t do_pdc4030_io (id "waiting - Odd!\n", drive->name); return ide_stopped; } else { -#ifndef CONFIG_IDE_TASKFILE_IO HWIF(drive)->OUTB(PROMISE_WRITE, IDE_COMMAND_REG); -#endif if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { printk(KERN_ERR "%s: no DRQ after issuing " @@ -733,9 +649,7 @@ static ide_startstop_t do_pdc4030_io (id } if (!drive->unmask) local_irq_disable(); -#ifndef CONFIG_IDE_TASKFILE_IO HWGROUP(drive)->wrq = *rq; /* scratchpad */ -#endif return promise_write(drive); } } @@ -749,14 +663,9 @@ static ide_startstop_t promise_rw_disk ( */ ide_hwif_t *hwif = HWIF(drive); int drive_number = (hwif->channel << 1) + drive->select.b.unit; -#ifdef CONFIG_IDE_TASKFILE_IO - struct hd_drive_task_hdr taskfile; - ide_task_t args; -#endif BUG_ON(rq->nr_sectors > 127); -#ifndef CONFIG_IDE_TASKFILE_IO if (IDE_CONTROL_REG) hwif->OUTB(drive->ctl, IDE_CONTROL_REG); hwif->OUTB(drive_number, IDE_FEATURE_REG); @@ -767,27 +676,4 @@ static ide_startstop_t promise_rw_disk ( hwif->OUTB(((block>>8)&0x0f)|drive->select.all,IDE_SELECT_REG); return do_pdc4030_io(drive, rq); -#else /* !CONFIG_IDE_TASKFILE_IO */ - memset(&taskfile, 0, sizeof(struct hd_drive_task_hdr)); - - taskfile.feature = drive_number; - taskfile.sector_count = rq->nr_sectors; - taskfile.sector_number = block; - taskfile.low_cylinder = (block>>=8); - taskfile.high_cylinder = (block>>=8); - taskfile.device_head = ((block>>8)&0x0f)|drive->select.all; - taskfile.command = (rq->cmd==READ)?PROMISE_READ:PROMISE_WRITE; - - memcpy(args.tfRegister, &taskfile, sizeof(struct hd_drive_task_hdr)); - memset(args.hobRegister, 0, sizeof(struct hd_drive_hob_hdr)); - /* - * Setup the bits of args that we do need. - * Note that we don't use the generic interrupt handlers. - */ - args.handler = NULL; - args.rq = (struct request *) rq; - rq->special = (ide_task_t *)&args; - - return do_pdc4030_io(drive, &args); -#endif /* !CONFIG_IDE_TASKFILE_IO */ } --- linux-2.6.9-rc1/drivers/ide/legacy/qd65xx.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/ide/legacy/qd65xx.h 2004-09-02 20:51:51.000000000 -0700 @@ -47,10 +47,10 @@ /* Drive specific timing taken from DOS driver v3.7 */ struct qd65xx_timing_s { - char offset; /* ofset from the beginning of Model Number" */ + s8 offset; /* ofset from the beginning of Model Number" */ char model[4]; /* 4 chars from Model number, no conversion */ - short active; /* active time */ - short recovery; /* recovery time */ + s16 active; /* active time */ + s16 recovery; /* recovery time */ } qd65xx_timing [] = { { 30, "2040", 110, 225 }, /* Conner CP30204 */ { 30, "2045", 135, 225 }, /* Conner CP30254 */ --- linux-2.6.9-rc1/drivers/ide/Makefile 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/ide/Makefile 2004-09-03 00:56:50.829433400 -0700 @@ -23,7 +23,6 @@ ide-core-$(CONFIG_BLK_DEV_IDEPCI) += set ide-core-$(CONFIG_BLK_DEV_IDEDMA) += ide-dma.o ide-core-$(CONFIG_BLK_DEV_IDE_TCQ) += ide-tcq.o ide-core-$(CONFIG_PROC_FS) += ide-proc.o -ide-core-$(CONFIG_BLK_DEV_IDEPNP) += ide-pnp.o # built-in only drivers from arm/ ide-core-$(CONFIG_IDE_ARM) += arm/ide_arm.o @@ -44,6 +43,7 @@ ide-core-$(CONFIG_H8300) += h8300/ide-h obj-$(CONFIG_BLK_DEV_IDE) += ide-core.o obj-$(CONFIG_IDE_GENERIC) += ide-generic.o +obj-$(CONFIG_BLK_DEV_IDEPNP) += ide-pnp.o obj-$(CONFIG_BLK_DEV_IDEDISK) += ide-disk.o obj-$(CONFIG_BLK_DEV_IDECD) += ide-cd.o --- linux-2.6.9-rc1/drivers/ide/pci/cs5520.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/ide/pci/cs5520.c 2004-09-02 20:51:51.000000000 -0700 @@ -290,7 +290,10 @@ static int __devinit cs5520_init_one(str return 1; } pci_set_master(dev); - pci_set_dma_mask(dev, 0xFFFFFFFF); + if (pci_set_dma_mask(dev, 0xFFFFFFFF)) { + printk(KERN_WARNING "cs5520: No suitable DMA available.\n"); + return -ENODEV; + } init_chipset_cs5520(dev, d->name); index.all = 0xf0f0; --- linux-2.6.9-rc1/drivers/ide/pci/hpt366.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/ide/pci/hpt366.c 2004-09-03 00:57:09.642573368 -0700 @@ -881,7 +881,7 @@ static int __devinit init_hpt37x(struct /* interrupt force enable */ pci_write_config_byte(dev, 0x5a, (reg5ah & ~0x10)); - if(dmabase) + if (dev->device == PCI_DEVICE_ID_TTI_HPT372N && dmabase) { did = inb(dmabase + 0x22); rid = inb(dmabase + 0x28); @@ -1132,7 +1132,7 @@ static void __devinit init_hwif_hpt366(i unsigned long dmabase = hwif->dma_base; int is_372n = 0; - if(dmabase) + if (dev->device == PCI_DEVICE_ID_TTI_HPT372N && dmabase) { did = inb(dmabase + 0x22); rid = inb(dmabase + 0x28); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/ide/pci/it8212.c 2004-09-03 00:56:52.411192936 -0700 @@ -0,0 +1,664 @@ +/* + * linux/drivers/ide/pci/it8212.c Version 0.01 July 2004 + * + * Copyright (C) 2004 Red Hat + * + * May be copied or modified under the terms of the GNU General Public License + * + * Documentation available from + * http://www.ite.com.tw/pc/IT8212F_V04.pdf + * + * The ITE8212 isn't exactly a standard IDE controller. It has two + * modes. In pass through mode then it is an IDE controller. In its smart + * mode its actually quite a capable hardware raid controller disguised + * as an IDE controller. + * + * Errata: + * Rev 0x10 also requires master/slave use the same UDMA timing and + * cannot do ATAPI DMA, while the other revisions can do ATAPI UDMA + * but not MWDMA. + * + * This has a few impacts on the driver + * - In pass through mode we do all the work you would expect + * - In smart mode the clocking set up is done by the controller generally + * - There are a few extra vendor commands that actually talk to the + * controller but only work PIO with no IRQ. + * + * Vendor areas of the identify block in smart mode are used for the + * timing and policy set up. Each HDD in raid mode also has a serial + * block on the disk. The hardware extra commands are get/set chip status, + * rebuild, get rebuild status. + * + * In Linux the driver supports pass through mode as if the device was + * just another IDE controller. If the smart mode is running then + * volumes are managed by the controller firmware and each IDE "disk" + * is a raid volume. Even more cute - the controller can do automated + * hotplug and rebuild. + * + * The pass through controller itself is a little demented. It has a + * flaw that it has a single set of PIO/MWDMA timings per channel so + * non UDMA devices restrict each others performance. It also has a + * single clock source per channel so mixed UDMA100/133 performance + * isn't perfect and we have to pick a clock. Thankfully none of this + * matters in smart mode. ATAPI DMA is not supported. + * + * TODO + * - Rev 0x10 in pass through mode needs UDMA clock whacking + * to work around h/w issues + * - Is rev 0x10 out anywhere - test it if so + * - More testing + * - Find an Innovision 8401D or R board somewhere to test that + * - See if we can kick the cheapo board into smart mode + * ourselves 8) + * - ATAPI UDMA is ok but not MWDMA it seems + * - Better clock strategy + * - RAID configuration ioctls + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct it8212_dev +{ + int smart:1, /* Are we in smart raid mode */ + timing10:1; /* Rev 0x10 */ + u8 clock_mode; /* 0, 50 or 66 */ + u8 want[2][2]; /* Mode/Pri log for master slave */ + + /* We need these for switching the clock when DMA goes on/off */ + u8 pio[2]; /* Cached PIO values */ + u8 mwdma[2]; /* Cached MWDMA values */ +}; + +/** + * it8212_clock_strategy + * @hwif: hardware interface + * + * Select between the 50 and 66Mhz base clocks to get the best + * results for this interface. Right now our approach is stupidity + * first until we have the driver stable + */ + +static void it8212_clock_strategy(ide_drive_t *drive) +{ + u8 v; + int sel = 0; + ide_hwif_t *hwif = HWIF(drive); + int clock; + struct it8212_dev *itdev = ide_get_hwifdata(hwif); + /* FIXME: Just to simplify we don't switch clocks once set */ + if(itdev->clock_mode == 0) + { + if(itdev->want[0][0] > itdev->want[1][0]) + clock = itdev->want[0][1]; + else + clock = itdev->want[1][1]; + /* Load this into the controller ? */ + if(clock == 66) + itdev->clock_mode = 66; + else + { + itdev->clock_mode = 50; + sel = 1; + } + pci_read_config_byte(hwif->pci_dev, 0x50, &v); + v &= ~(1 << (1 + hwif->channel)); + v |= sel << (1 + hwif->channel); + pci_write_config_byte(hwif->pci_dev, 0x50, v); + + printk(KERN_INFO "it8212: selected %dMHz clock.\n", itdev->clock_mode); + } +} + +/** + * it8212_ratemask - Compute available modes + * @drive: IDE drive + * + * Compute the available speeds for the devices on the interface. This + * is all modes to ATA133 clipped by drive cable setup. + */ + +static byte it8212_ratemask (ide_drive_t *drive) +{ + u8 mode = 4; + if (!eighty_ninty_three(drive)) + mode = min(mode, (u8)1); + return mode; +} + +/** + * it8212_tuneproc - tune a drive + * @drive: drive to tune + * @mode_wanted: the target operating mode + * + * Load the timing settings for this device mode into the + * controller. By the time we are called the mode has been + * modified as neccessary to handle the absence of seperate + * master/slave timers for MWDMA/PIO. + * + * This code is only used in pass through mode. + */ + +static void it8212_tuneproc (ide_drive_t *drive, byte mode_wanted) +{ + ide_hwif_t *hwif = HWIF(drive); + struct it8212_dev *itdev = ide_get_hwifdata(hwif); + int unit = drive->select.b.unit; + int channel = hwif->channel; + u8 conf; + + /* Spec says 89 ref driver uses 88 */ + static u8 pio_50[] = { 0x88, 0x82, 0x81, 0x32, 0x21 }; + static u8 pio_66[] = { 0xAA, 0xA3, 0xA1, 0x33, 0x31 }; + + static u8 pio_want[] = { 66, 66, 66, 66, 0 }; + + /* We prefer 66Mhz clock for PIO 0-3, don't care for PIO4 */ + itdev->want[unit][0] = pio_want[mode_wanted]; + itdev->want[unit][1] = 1; /* PIO is lowest priority */ + + it8212_clock_strategy(drive); + + /* Program PIO/MWDMA timing bits */ + pci_read_config_byte(hwif->pci_dev, 0x54 + 4 * channel, &conf); + if(itdev->clock_mode == 66) + conf = pio_66[mode_wanted]; + else + conf = pio_50[mode_wanted]; + pci_write_config_byte(hwif->pci_dev, 0x54 + 4 * channel, conf); + itdev->pio[unit] = conf ; +} + +/** + * it8212_tune_mwdma - tune a channel for MWDMA + * @drive: drive to set up + * @mode_wanted: the target operating mode + * + * Load the timing settings for this device mode into the + * controller when doing MWDMA in pass through mode. The caller + * must manage the whole lack of per device MWDMA/PIO timings and + * the shared MWDMA/PIO timing register. + * + * FIXME: before we can enable MWDMA we need to cache PIO/MWDMA + * timings and reload them on ide_dma_on/ide_dma_off_* because they + * occupy the same register. + */ + +static void it8212_tune_mwdma (ide_drive_t *drive, byte mode_wanted) +{ + ide_hwif_t *hwif = HWIF(drive); + struct it8212_dev *itdev = (void *)ide_get_hwifdata(hwif); + int unit = drive->select.b.unit; + int channel = hwif->channel; + u8 conf; + + static u8 dma_50[] = { 0x66, 0x22, 0x21 }; + static u8 dma_66[] = { 0x88, 0x32, 0x31 }; + + static u8 mwdma_want[] = { 0, 66, 0 }; + + itdev->want[unit][0] = mwdma_want[mode_wanted]; + itdev->want[unit][1] = 2; /* MWDMA is low priority */ + + it8212_clock_strategy(drive); + + /* UDMA bits off */ + pci_read_config_byte(hwif->pci_dev, 0x50, &conf); + conf |= 1 << (3 + 2 * channel + unit); + pci_write_config_byte(hwif->pci_dev, 0x50, conf); + + /* Program PIO/MWDMA timing bits */ + pci_read_config_byte(hwif->pci_dev, 0x54 + 4 * channel, &conf); + if(itdev->clock_mode == 66) + conf = dma_66[mode_wanted]; + else + conf = dma_50[mode_wanted]; + pci_write_config_byte(hwif->pci_dev, 0x54 + 4 * channel, conf); + itdev->mwdma[unit] = conf; +} + +/** + * it8212_tune_udma - tune a channel for UDMA + * @drive: drive to set up + * @mode_wanted: the target operating mode + * + * Load the timing settings for this device mode into the + * controller when doing UDMA modes in pass through. + */ + +static void it8212_tune_udma (ide_drive_t *drive, byte mode_wanted) +{ + ide_hwif_t *hwif = HWIF(drive); + struct it8212_dev *itdev = ide_get_hwifdata(hwif); + int unit = drive->select.b.unit; + int channel = hwif->channel; + u8 conf; + + static u8 udma_50[]= { 0x33, 0x31, 0x21, 0x21, 0x11, 0x11, 0x11 }; + static u8 udma_66[]= { 0x44, 0x42, 0x31, 0x21, 0x11, 0x22, 0x11 }; + + static u8 udma_want[] = { 0, 50, 0, 66, 66, 50, 66 }; + + itdev->want[unit][0] = udma_want[mode_wanted]; + itdev->want[unit][1] = 3; /* UDMA is high priority */ + + it8212_clock_strategy(drive); + + /* UDMA on */ + pci_read_config_byte(hwif->pci_dev, 0x50, &conf); + conf &= ~ (1 << (3 + 2 * channel + unit)); + pci_write_config_byte(hwif->pci_dev, 0x50, conf); + + /* Program UDMA timing bits */ + pci_read_config_byte(hwif->pci_dev, 0x56 + 4 * channel + unit, &conf); + if(itdev->clock_mode == 66) + conf = udma_66[mode_wanted]; + else + conf = udma_50[mode_wanted]; + + if(mode_wanted >= 5) + conf |= 0x80; /* UDMA 5/6 select on */ + + pci_write_config_byte(hwif->pci_dev, 0x56 + 4 * channel + unit, conf); + itdev->mwdma[unit] = 0; +} + +/** + * config_it8212_chipset_for_pio - set drive timings + * @drive: drive to tune + * @speed we want + * + * Compute the best pio mode we can for a given device. We must + * pick a speed that does not cause problems with the other device + * on the cable. + */ + +static void config_it8212_chipset_for_pio (ide_drive_t *drive, byte set_speed) +{ + u8 unit = drive->select.b.unit; + ide_hwif_t *hwif = HWIF(drive); + ide_drive_t *pair = &hwif->drives[1-unit]; + u8 speed = 0, set_pio = ide_get_best_pio_mode(drive, 4, 5, NULL); + u8 pair_pio; + + /* We have to deal with this mess in pairs */ + if(pair != NULL) + { + pair_pio = ide_get_best_pio_mode(pair, 4, 5, NULL); + /* Trim PIO to the slowest of the master/slave */ + if(pair_pio < set_pio) + set_pio = pair_pio; + } + it8212_tuneproc(drive, set_pio); + speed = XFER_PIO_0 + set_pio; + /* XXX - We trim to the lowest of the pair so the other drive + will always be fine at this point until we do hotplug passthru */ + + if (set_speed) + (void) ide_config_drive_speed(drive, speed); +} + +static void config_chipset_for_pio (ide_drive_t *drive, byte set_speed) +{ + config_it8212_chipset_for_pio(drive, set_speed); +} + +/** + * it8212_dma_read - DMA hook + * @drive: drive for DMA + * + * The IT8212 has a single timing register for MWDMA and for PIO + * operations. As we flip back and forth we have to reload the + * clock. + * + * FIXME: for 0x10 model we should reprogram UDMA here + */ + +static int it8212_dma_begin(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct it8212_dev *itdev = ide_get_hwifdata(hwif); + int unit = drive->select.b.unit; + if(itdev->mwdma[unit]) + pci_write_config_byte(hwif->pci_dev, 0x54 + 4 * hwif->channel, itdev->mwdma[drive->select.b.unit]); + return __ide_dma_begin(drive); +} + +/** + * it8212_dma_write - DMA hook + * @drive: drive for DMA stop + * + * The IT8212 has a single timing register for MWDMA and for PIO + * operations. As we flip back and forth we have to reload the + * clock. + * + * FIXME: for 0x10 model we should reprogram UDMA here + */ + +static int it8212_dma_end(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + int unit = drive->select.b.unit; + struct it8212_dev *itdev = ide_get_hwifdata(hwif); + int ret = __ide_dma_end(drive); + + if(itdev->mwdma[unit]) + pci_write_config_byte(hwif->pci_dev, 0x54 + 4 * hwif->channel, itdev->pio[drive->select.b.unit]); + return ret; +} + + +/** + * it8212_tune_chipset - set controller timings + * @drive: Drive to set up + * @xferspeed: speed we want to achieve + * + * Tune the ITE chipset for the desired mode. If we can't achieve + * the desired mode then tune for a lower one, but ultimately + * make the thing work. + */ + +static int it8212_tune_chipset (ide_drive_t *drive, byte xferspeed) +{ + + ide_hwif_t *hwif = HWIF(drive); + struct it8212_dev *itdev = ide_get_hwifdata(hwif); + u8 speed = ide_rate_filter(it8212_ratemask(drive), xferspeed); + + switch(speed) { + case XFER_PIO_4: + case XFER_PIO_3: + case XFER_PIO_2: + case XFER_PIO_1: + case XFER_PIO_0: + if(!itdev->smart) + it8212_tuneproc(drive, (speed - XFER_PIO_0)); + break; + /* MWDMA tuning is really hard because our MWDMA and PIO + timings are kept in the same place. We can switch in the + host dma on/off callbacks */ + case XFER_MW_DMA_2: + case XFER_MW_DMA_1: + case XFER_MW_DMA_0: + if(!itdev->smart) + it8212_tune_mwdma(drive, (speed - XFER_MW_DMA_0)); + break; + case XFER_UDMA_6: + case XFER_UDMA_5: + case XFER_UDMA_4: + case XFER_UDMA_3: + case XFER_UDMA_2: + case XFER_UDMA_1: + case XFER_UDMA_0: + if(!itdev->smart) + it8212_tune_udma(drive, (speed - XFER_UDMA_0)); + break; + default: + return 1; + } + /* + * In smart mode the clocking is done by the host controller + * snooping the mode we picked. The rest of it is not our problem + */ + return (ide_config_drive_speed(drive, speed)); +} + +/** + * config_chipset_for_dma - configure for DMA + * @drive: drive to configure + * + * Called by the IDE layer when it wants the timings set up. + */ + +static int config_chipset_for_dma (ide_drive_t *drive) +{ + u8 speed = ide_dma_speed(drive, it8212_ratemask(drive)); + + config_chipset_for_pio(drive, !speed); + + if (!speed) + return 0; + + if (ide_set_xfer_rate(drive, speed)) + return 0; + + if (!drive->init_speed) + drive->init_speed = speed; + + return ide_dma_enable(drive); +} + +/** + * it8212_configure_drive_for_dma - set up for DMA transfers + * @drive: drive we are going to set up + * + * Set up the drive for DMA, tune the controller and drive as + * required. If the drive isn't suitable for DMA or we hit + * other problems then we will drop down to PIO and set up + * PIO appropriately + */ + +static int it8212_config_drive_for_dma (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct hd_driveid *id = drive->id; + + if ((id->capability & 1) != 0 && drive->autodma) { + /* Consult the list of known "bad" drives */ + if (__ide_dma_bad_drive(drive)) + goto fast_ata_pio; + + if ((id->field_valid & 4) && it8212_ratemask(drive)) { + if (id->dma_ultra & hwif->ultra_mask) { + /* Force if Capable UltraDMA */ + int dma = config_chipset_for_dma(drive); + if ((id->field_valid & 2) && !dma) + goto try_dma_modes; + } + } else if (id->field_valid & 2) { +try_dma_modes: + if ((id->dma_mword & hwif->mwdma_mask) || + (id->dma_1word & hwif->swdma_mask)) { + /* Force if Capable regular DMA modes */ + if (!config_chipset_for_dma(drive)) + goto no_dma_set; + } + } else if (__ide_dma_good_drive(drive) && + (id->eide_dma_time < 150)) { + /* Consult the list of known "good" drives */ + if (!config_chipset_for_dma(drive)) + goto no_dma_set; + } else { + goto fast_ata_pio; + } + return hwif->ide_dma_on(drive); + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: +no_dma_set: + config_chipset_for_pio(drive, 1); + return hwif->ide_dma_off_quietly(drive); + } + /* IORDY not supported */ + return 0; +} + +/** + * init_chipset_it8212 - set up an ITE device + * @dev: PCI device + * @name: device name + * + */ + +static unsigned int __devinit init_chipset_it8212(struct pci_dev *dev, const char *name) +{ + return 0; +} + +/** + * ata66_it8212 - check for 80 pin cable + * @hwif: interface to check + * + * Check for the presence of an ATA66 capable cable on the + * interface. Note that the firmware writes bits 4-7 having done + * the drive side sampling. This may impact hotplug. + */ + +static unsigned int __devinit ata66_it8212(ide_hwif_t *hwif) +{ + u16 iocfg; + + pci_read_config_word(hwif->pci_dev, 0x40, &iocfg); + if(iocfg & (1 << (4 + 2*hwif->channel))) + return 1; + return 0; +} + +/** + * init_hwif_it8212 - set up hwif structs + * @hwif: interface to set up + * + * We do the basic set up of the interface structure. The IT8212 + * requires several custom handlers so we override the default + * ide DMA handlers appropriately + */ + +static void __devinit init_hwif_it8212(ide_hwif_t *hwif) +{ + struct it8212_dev *idev = kmalloc(sizeof(struct it8212_dev), GFP_KERNEL); + u8 conf; + + if(idev == NULL) + { + printk(KERN_ERR "it8212: out of memory, falling back to legacy behaviour.\n"); + goto fallback; + } + memset(idev, 0, sizeof(*idev)); + ide_set_hwifdata(hwif, idev); + + pci_read_config_byte(hwif->pci_dev, 0x50, &conf); + if(conf & 1) + { + if(hwif->channel == 0) + printk(KERN_INFO "it8212: controller in RAID mode.\n"); + idev->smart = 1; + } + else if(hwif->channel == 1) + printk(KERN_INFO "it8212: controller in pass through mode.\n"); + + /* + * Not in the docs but according to the reference driver + * this is neccessary. + */ + pci_read_config_byte(hwif->pci_dev, 0x08, &conf); + if(conf == 0x10) + idev->timing10 = 1; +#if 0 /* Needs more research */ + else + hwif->atapi_dma = 1; +#endif + + if(idev->timing10 && !idev->smart) + { + printk(KERN_WARNING "it8212: DMA is not currently supported on revision 0x10 in pass through.\n"); + goto fallback; + } + + hwif->speedproc = &it8212_tune_chipset; + hwif->tuneproc = &it8212_tuneproc; + + /* MWDMA/PIO clock switching for pass through mode */ + if(!idev->smart) + { + hwif->ide_dma_begin = &it8212_dma_begin; + hwif->ide_dma_end = &it8212_dma_end; + } + + if (!hwif->dma_base) + goto fallback; + + hwif->ultra_mask = 0x7f; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x07; + + + hwif->ide_dma_check = &it8212_config_drive_for_dma; + if (!(hwif->udma_four)) + hwif->udma_four = ata66_it8212(hwif); + + /* + * The BIOS often doesn't set up DMA on this controller + * so we always do it. + */ + + hwif->autodma = 1; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; + return; + +fallback: + hwif->autodma = 0; + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + return; +} + +#define DECLARE_ITE_DEV(name_str) \ + { \ + .name = name_str, \ + .init_chipset = init_chipset_it8212, \ + .init_hwif = init_hwif_it8212, \ + .channels = 2, \ + .autodma = AUTODMA, \ + .bootable = ON_BOARD, \ + } + +static ide_pci_device_t it8212_chipsets[] __devinitdata = { + /* 0 */ DECLARE_ITE_DEV("IT8212"), +}; + +/** + * it8212_init_one - pci layer discovery entry + * @dev: PCI device + * @id: ident table entry + * + * Called by the PCI code when it finds an ITE8212 controller. + * We then use the IDE PCI generic helper to do most of the work. + */ + +static int __devinit it8212_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + ide_setup_pci_device(dev, &it8212_chipsets[id->driver_data]); + return 0; +} + +static struct pci_device_id it8212_pci_tbl[] = { + { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8212, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, }, +}; + +MODULE_DEVICE_TABLE(pci, it8212_pci_tbl); + +static struct pci_driver driver = { + .name = "ITE8212 IDE", + .id_table = it8212_pci_tbl, + .probe = it8212_init_one, +}; + +static int it8212_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(it8212_ide_init); + +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("PCI driver module for the ITE 8212"); +MODULE_LICENSE("GPL"); --- linux-2.6.9-rc1/drivers/ide/pci/Makefile 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/ide/pci/Makefile 2004-09-03 00:56:52.412192784 -0700 @@ -13,6 +13,7 @@ obj-$(CONFIG_BLK_DEV_HPT34X) += hpt34x. obj-$(CONFIG_BLK_DEV_HPT366) += hpt366.o #obj-$(CONFIG_BLK_DEV_HPT37X) += hpt37x.o obj-$(CONFIG_BLK_DEV_IT8172) += it8172.o +obj-$(CONFIG_BLK_DEV_IT8212) += it8212.o obj-$(CONFIG_BLK_DEV_NS87415) += ns87415.o obj-$(CONFIG_BLK_DEV_OPTI621) += opti621.o obj-$(CONFIG_BLK_DEV_PDC202XX_OLD) += pdc202xx_old.o --- linux-2.6.9-rc1/drivers/ieee1394/csr1212.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/drivers/ieee1394/csr1212.c 2004-09-03 00:56:31.185419744 -0700 @@ -87,7 +87,8 @@ static const u_int8_t csr1212_key_id_typ static inline void free_keyval(struct csr1212_keyval *kv) { - if (kv->key.type == CSR1212_KV_TYPE_LEAF) + if ((kv->key.type == CSR1212_KV_TYPE_LEAF) && + (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM)) CSR1212_FREE(kv->value.leaf.data); CSR1212_FREE(kv); @@ -155,7 +156,7 @@ static inline struct csr1212_keyval *csr { struct csr1212_keyval *kv; - for (kv = kv_list; kv != NULL; kv = kv->next) { + for (kv = kv_list->next; kv && (kv != kv_list); kv = kv->next) { if (kv->offset == offset) return kv; } @@ -181,9 +182,9 @@ struct csr1212_csr *csr1212_create_csr(s return NULL; } - /* The keyval key id is not used for the root node, but a valid key id - * that can be used for a directory needs to be passed to - * csr1212_new_directory(). */ + /* The keyval key id is not used for the root node, but a valid key id + * that can be used for a directory needs to be passed to + * csr1212_new_directory(). */ csr->root_kv = csr1212_new_directory(CSR1212_KV_ID_VENDOR); if (!csr->root_kv) { CSR1212_FREE(csr->cache_head); @@ -709,7 +710,7 @@ void _csr1212_destroy_keyval(struct csr1 tail->next = k->value.directory.dentries_head; k->value.directory.dentries_head->prev = tail; tail = k->value.directory.dentries_tail; - } + } } free_keyval(k); k = a; @@ -796,7 +797,8 @@ static int csr1212_append_new_cache(stru return CSR1212_ENOMEM; } cache->ext_rom->offset = csr_addr - CSR1212_REGISTER_SPACE_BASE; - cache->ext_rom->value.leaf.len = 0; + cache->ext_rom->value.leaf.len = -1; + cache->ext_rom->value.leaf.data = cache->data; /* Add cache to tail of cache list */ cache->prev = csr->cache_tail; @@ -864,20 +866,20 @@ static int csr1212_generate_layout_subdi default: case CSR1212_KV_TYPE_IMMEDIATE: case CSR1212_KV_TYPE_CSR_OFFSET: - continue; + break; case CSR1212_KV_TYPE_LEAF: case CSR1212_KV_TYPE_DIRECTORY: /* Remove from list */ - if (dkv->prev) + if (dkv->prev && (dkv->prev->next == dkv)) dkv->prev->next = dkv->next; - if (dkv->next) + if (dkv->next && (dkv->next->prev == dkv)) dkv->next->prev = dkv->prev; - if (dkv == *layout_tail) - *layout_tail = dkv->prev; + //if (dkv == *layout_tail) + // *layout_tail = dkv->prev; /* Special case: Extended ROM leafs */ if (dkv->key.id == CSR1212_KV_ID_EXTENDED_ROM) { - dkv->value.leaf.len = 0; /* initialize to zero */ + dkv->value.leaf.len = -1; /* Don't add Extended ROM leafs in the layout list, * they are handled differently. */ break; @@ -919,8 +921,8 @@ size_t csr1212_generate_layout_order(str } struct csr1212_keyval *csr1212_generate_positions(struct csr1212_csr_rom_cache *cache, - struct csr1212_keyval *start_kv, - int start_pos) + struct csr1212_keyval *start_kv, + int start_pos) { struct csr1212_keyval *kv = start_kv; struct csr1212_keyval *okv = start_kv; @@ -930,7 +932,10 @@ struct csr1212_keyval *csr1212_generate_ cache->layout_head = kv; while(kv && pos < cache->size) { - kv->offset = cache->offset + pos; + /* Special case: Extended ROM leafs */ + if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) { + kv->offset = cache->offset + pos; + } switch(kv->key.type) { case CSR1212_KV_TYPE_LEAF: @@ -1090,6 +1095,9 @@ int csr1212_generate_csr_image(struct cs bi->crc_length = bi->length; bi->crc = csr1212_crc16(bi->data, bi->crc_length); + csr->root_kv->next = NULL; + csr->root_kv->prev = NULL; + agg_size = csr1212_generate_layout_order(csr->root_kv); init_offset = csr->bus_info_len; @@ -1158,6 +1166,17 @@ int csr1212_generate_csr_image(struct cs /* Copy the data into the cache buffer */ csr1212_fill_cache(cache); + + if (cache != csr->cache_head) { + /* Set the length and CRC of the extended ROM. */ + struct csr1212_keyval_img *kvi = + (struct csr1212_keyval_img*)cache->data; + + kvi->length = CSR1212_CPU_TO_BE16(bytes_to_quads(cache->len) - 1); + kvi->crc = csr1212_crc16(kvi->data, + bytes_to_quads(cache->len) - 1); + + } } return CSR1212_SUCCESS; @@ -1174,11 +1193,6 @@ int csr1212_read(struct csr1212_csr *csr &cache->data[bytes_to_quads(offset - cache->offset)], len); return CSR1212_SUCCESS; - } else if (((offset < cache->offset) && - ((offset + len) >= cache->offset)) || - ((offset >= cache->offset) && - ((offset + len) > (cache->offset + cache->size)))) { - return CSR1212_EINVAL; } } return CSR1212_ENOENT; @@ -1227,8 +1241,8 @@ static int csr1212_parse_bus_info_block( return CSR1212_EINVAL; #if 0 - /* Apparently there are too many differnt wrong implementations of the - * CRC algorithm that verifying them is moot. */ + /* Apparently there are too many differnt wrong implementations of the + * CRC algorithm that verifying them is moot. */ if ((csr1212_crc16(bi->data, bi->crc_length) != bi->crc) && (csr1212_msft_crc16(bi->data, bi->crc_length) != bi->crc)) return CSR1212_EINVAL; @@ -1249,10 +1263,9 @@ static int csr1212_parse_bus_info_block( return CSR1212_SUCCESS; } -static inline int csr1212_parse_dir_entry(struct csr1212_keyval *dir, - csr1212_quad_t ki, - u_int32_t kv_pos, - struct csr1212_csr_rom_cache *cache) +static int csr1212_parse_dir_entry(struct csr1212_keyval *dir, + csr1212_quad_t ki, + u_int32_t kv_pos) { int ret = CSR1212_SUCCESS; struct csr1212_keyval *k = NULL; @@ -1291,7 +1304,7 @@ static inline int csr1212_parse_dir_entr goto fail; } - k = csr1212_find_keyval_offset(cache->layout_head, offset); + k = csr1212_find_keyval_offset(dir, offset); if (k) break; /* Found it. */ @@ -1309,11 +1322,10 @@ static inline int csr1212_parse_dir_entr k->valid = 0; /* Contents not read yet so it's not valid. */ k->offset = offset; - k->prev = cache->layout_tail; - k->next = NULL; - if (cache->layout_tail) - cache->layout_tail->next = k; - cache->layout_tail = k; + k->prev = dir; + k->next = dir->next; + dir->next->prev = k; + dir->next = k; } ret = csr1212_attach_keyval_to_directory(dir, k); @@ -1325,6 +1337,7 @@ fail: return ret; } + int csr1212_parse_keyval(struct csr1212_keyval *kv, struct csr1212_csr_rom_cache *cache) { @@ -1338,8 +1351,8 @@ int csr1212_parse_keyval(struct csr1212_ kvi_len = CSR1212_BE16_TO_CPU(kvi->length); #if 0 - /* Apparently there are too many differnt wrong implementations of the - * CRC algorithm that verifying them is moot. */ + /* Apparently there are too many differnt wrong implementations of the + * CRC algorithm that verifying them is moot. */ if ((csr1212_crc16(kvi->data, kvi_len) != kvi->crc) && (csr1212_msft_crc16(kvi->data, kvi_len) != kvi->crc)) { ret = CSR1212_EINVAL; @@ -1353,22 +1366,19 @@ int csr1212_parse_keyval(struct csr1212_ csr1212_quad_t ki = kvi->data[i]; /* Some devices put null entries in their unit - * directories. If we come across such and entry, + * directories. If we come across such an entry, * then skip it. */ if (ki == 0x0) continue; ret = csr1212_parse_dir_entry(kv, ki, (kv->offset + - quads_to_bytes(i + 1)), - cache); + quads_to_bytes(i + 1))); } kv->value.directory.len = kvi_len; break; case CSR1212_KV_TYPE_LEAF: - if (kv->key.id == CSR1212_KV_ID_EXTENDED_ROM) { - kv->value.leaf.data = cache->data; - } else { + if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) { kv->value.leaf.data = CSR1212_MALLOC(quads_to_bytes(kvi_len)); if (!kv->value.leaf.data) { @@ -1399,7 +1409,6 @@ int _csr1212_read_keyval(struct csr1212_ u_int32_t *cache_ptr; u_int16_t kv_len = 0; - if (!csr || !kv) return CSR1212_EINVAL; @@ -1413,7 +1422,7 @@ int _csr1212_read_keyval(struct csr1212_ if (!cache) { csr1212_quad_t q; - struct csr1212_csr_rom_cache *nc; + u_int32_t cache_size; /* Only create a new cache for Extended ROM leaves. */ if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) @@ -1425,12 +1434,20 @@ int _csr1212_read_keyval(struct csr1212_ return CSR1212_EIO; } - kv->value.leaf.len = quads_to_bytes(CSR1212_BE32_TO_CPU(q)>>16); + kv->value.leaf.len = CSR1212_BE32_TO_CPU(q) >> 16; + + cache_size = (quads_to_bytes(kv->value.leaf.len + 1) + + (csr->max_rom - 1)) & ~(csr->max_rom - 1); - nc = csr1212_rom_cache_malloc(kv->offset, kv->value.leaf.len); - cache->next = nc; - nc->prev = cache; - csr->cache_tail = nc; + cache = csr1212_rom_cache_malloc(kv->offset, cache_size); + if (!cache) + return CSR1212_ENOMEM; + + kv->value.leaf.data = &cache->data[1]; + csr->cache_tail->next = cache; + cache->prev = csr->cache_tail; + cache->next = NULL; + csr->cache_tail = cache; cache->filled_head = CSR1212_MALLOC(sizeof(struct csr1212_cache_region)); if (!cache->filled_head) { @@ -1443,6 +1460,10 @@ int _csr1212_read_keyval(struct csr1212_ cache->filled_head->next = NULL; cache->filled_head->prev = NULL; cache->data[0] = q; + + /* Don't read the entire extended ROM now. Pieces of it will + * be read when entries inside it are read. */ + return csr1212_parse_keyval(kv, cache); } cache_index = kv->offset - cache->offset; @@ -1548,6 +1569,7 @@ int _csr1212_read_keyval(struct csr1212_ int csr1212_parse_csr(struct csr1212_csr *csr) { static const int mr_map[] = { 4, 64, 1024, 0 }; + struct csr1212_dentry *dentry; int ret; if (!csr || !csr->ops->bus_read) @@ -1570,7 +1592,21 @@ int csr1212_parse_csr(struct csr1212_csr csr->bus_info_len; csr->root_kv->valid = 0; + csr->root_kv->next = csr->root_kv; + csr->root_kv->prev = csr->root_kv; csr1212_get_keyval(csr, csr->root_kv); + /* Scan through the Root directory finding all extended ROM regions + * and make cache regions for them */ + for (dentry = csr->root_kv->value.directory.dentries_head; + dentry; dentry = dentry->next) { + if (dentry->kv->key.id == CSR1212_KV_ID_EXTENDED_ROM) { + csr1212_get_keyval(csr, dentry->kv); + + if (ret != CSR1212_SUCCESS) + return ret; + } + } + return CSR1212_SUCCESS; } --- linux-2.6.9-rc1/drivers/ieee1394/eth1394.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/ieee1394/eth1394.c 2004-09-03 00:56:32.521216672 -0700 @@ -89,7 +89,7 @@ #define TRACE() printk(KERN_ERR "%s:%s[%d] ---- TRACE\n", driver_name, __FUNCTION__, __LINE__) static char version[] __devinitdata = - "$Rev: 1224 $ Ben Collins "; + "$Rev: 1231 $ Ben Collins "; struct fragment_info { struct list_head list; @@ -190,8 +190,7 @@ static inline void purge_partial_datagra static int ether1394_tx(struct sk_buff *skb, struct net_device *dev); static void ether1394_iso(struct hpsb_iso *iso); -static int ether1394_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); -static int ether1394_ethtool_ioctl(struct net_device *dev, void __user *useraddr); +static struct ethtool_ops ethtool_ops; static int ether1394_write(struct hpsb_host *host, int srcid, int destid, quadlet_t *data, u64 addr, size_t len, u16 flags); @@ -216,7 +215,7 @@ static struct hpsb_highlevel eth1394_hig /* This is called after an "ifup" */ static int ether1394_open (struct net_device *dev) { - struct eth1394_priv *priv = dev->priv; + struct eth1394_priv *priv = netdev_priv(dev); int ret = 0; /* Something bad happened, don't even try */ @@ -261,7 +260,7 @@ static int ether1394_stop (struct net_de /* Return statistics to the caller */ static struct net_device_stats *ether1394_stats (struct net_device *dev) { - return &(((struct eth1394_priv *)dev->priv)->stats); + return &(((struct eth1394_priv *)netdev_priv(dev))->stats); } /* What to do if we timeout. I think a host reset is probably in order, so @@ -269,16 +268,16 @@ static struct net_device_stats *ether139 static void ether1394_tx_timeout (struct net_device *dev) { ETH1394_PRINT (KERN_ERR, dev->name, "Timeout, resetting host %s\n", - ((struct eth1394_priv *)(dev->priv))->host->driver->name); + ((struct eth1394_priv *)netdev_priv(dev))->host->driver->name); - highlevel_host_reset (((struct eth1394_priv *)(dev->priv))->host); + highlevel_host_reset (((struct eth1394_priv *)netdev_priv(dev))->host); netif_wake_queue (dev); } static int ether1394_change_mtu(struct net_device *dev, int new_mtu) { - struct eth1394_priv *priv = dev->priv; + struct eth1394_priv *priv = netdev_priv(dev); if ((new_mtu < 68) || (new_mtu > min(ETH1394_DATA_LEN, @@ -379,7 +378,7 @@ static int eth1394_probe(struct device * ud->device.driver_data = node_info; new_node->ud = ud; - priv = (struct eth1394_priv *)hi->dev->priv; + priv = netdev_priv(hi->dev); list_add_tail(&new_node->list, &priv->ip_node_list); return 0; @@ -400,7 +399,7 @@ static int eth1394_remove(struct device if (!hi) return -ENOENT; - priv = (struct eth1394_priv *)hi->dev->priv; + priv = netdev_priv(hi->dev); old_node = eth1394_find_node(&priv->ip_node_list, ud); @@ -435,7 +434,7 @@ static int eth1394_update(struct unit_di if (!hi) return -ENOENT; - priv = (struct eth1394_priv *)hi->dev->priv; + priv = netdev_priv(hi->dev); node = eth1394_find_node(&priv->ip_node_list, ud); @@ -459,7 +458,7 @@ static int eth1394_update(struct unit_di ud->device.driver_data = node_info; node->ud = ud; - priv = (struct eth1394_priv *)hi->dev->priv; + priv = netdev_priv(hi->dev); list_add_tail(&node->list, &priv->ip_node_list); } @@ -496,7 +495,7 @@ static void ether1394_reset_priv (struct { unsigned long flags; int i; - struct eth1394_priv *priv = dev->priv; + struct eth1394_priv *priv = netdev_priv(dev); struct hpsb_host *host = priv->host; u64 guid = *((u64*)&(host->csr.rom->bus_info_data[3])); u16 maxpayload = 1 << (host->csr.max_rec + 1); @@ -547,7 +546,7 @@ static void ether1394_init_dev (struct n dev->header_cache_update= ether1394_header_cache_update; dev->hard_header_parse = ether1394_header_parse; dev->set_mac_address = ether1394_mac_addr; - dev->do_ioctl = ether1394_do_ioctl; + SET_ETHTOOL_OPS(dev, ðtool_ops); /* Some constants */ dev->watchdog_timeo = ETHER1394_TIMEOUT; @@ -602,7 +601,7 @@ static void ether1394_add_host (struct h SET_MODULE_OWNER(dev); - priv = (struct eth1394_priv *)dev->priv; + priv = netdev_priv(dev); INIT_LIST_HEAD(&priv->ip_node_list); @@ -672,7 +671,7 @@ static void ether1394_remove_host (struc hi = hpsb_get_hostinfo(ð1394_highlevel, host); if (hi != NULL) { - struct eth1394_priv *priv = (struct eth1394_priv *)hi->dev->priv; + struct eth1394_priv *priv = netdev_priv(hi->dev); hpsb_unregister_addrspace(ð1394_highlevel, host, priv->local_fifo); @@ -707,7 +706,7 @@ static void ether1394_host_reset (struct return; dev = hi->dev; - priv = (struct eth1394_priv *)dev->priv; + priv = netdev_priv(dev); /* Reset our private host data, but not our mtu */ netif_stop_queue (dev); @@ -882,7 +881,7 @@ static inline u16 ether1394_parse_encap( nodeid_t srcid, nodeid_t destid, u16 ether_type) { - struct eth1394_priv *priv = dev->priv; + struct eth1394_priv *priv = netdev_priv(dev); u64 dest_hw; unsigned short ret = 0; @@ -1112,7 +1111,7 @@ static int ether1394_data_handler(struct { struct sk_buff *skb; unsigned long flags; - struct eth1394_priv *priv = (struct eth1394_priv *)dev->priv; + struct eth1394_priv *priv = netdev_priv(dev); union eth1394_hdr *hdr = (union eth1394_hdr *)buf; u16 ether_type = 0; /* initialized to clear warning */ int hdr_len; @@ -1350,7 +1349,7 @@ static void ether1394_iso(struct hpsb_is ((be32_to_cpu(data[1]) & 0xff000000) >> 24)); source_id = be32_to_cpu(data[0]) >> 16; - priv = (struct eth1394_priv *)dev->priv; + priv = netdev_priv(dev); if (info->channel != (iso->host->csr.broadcast_channel & 0x3f) || specifier_id != ETHER1394_GASP_SPECIFIER_ID) { @@ -1384,7 +1383,7 @@ static void ether1394_iso(struct hpsb_is static inline void ether1394_arp_to_1394arp(struct sk_buff *skb, struct net_device *dev) { - struct eth1394_priv *priv = (struct eth1394_priv *)(dev->priv); + struct eth1394_priv *priv = netdev_priv(dev); struct arphdr *arp = (struct arphdr *)skb->data; unsigned char *arp_ptr = (unsigned char *)(arp + 1); @@ -1582,7 +1581,7 @@ static inline void ether1394_dg_complete { struct sk_buff *skb = ptask->skb; struct net_device *dev = skb->dev; - struct eth1394_priv *priv = dev->priv; + struct eth1394_priv *priv = netdev_priv(dev); unsigned long flags; /* Statistics */ @@ -1635,7 +1634,7 @@ static int ether1394_tx (struct sk_buff { int kmflags = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL; struct eth1394hdr *eth; - struct eth1394_priv *priv = dev->priv; + struct eth1394_priv *priv = netdev_priv(dev); int proto; unsigned long flags; nodeid_t dest_node; @@ -1768,53 +1767,17 @@ fail: return 0; /* returning non-zero causes serious problems */ } -static int ether1394_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +static void ether1394_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - switch(cmd) { - case SIOCETHTOOL: - return ether1394_ethtool_ioctl(dev, ifr->ifr_data); - - case SIOCGMIIPHY: /* Get address of MII PHY in use. */ - case SIOCGMIIREG: /* Read MII PHY register. */ - case SIOCSMIIREG: /* Write MII PHY register. */ - default: - return -EOPNOTSUPP; - } - - return 0; -} - -static int ether1394_ethtool_ioctl(struct net_device *dev, void __user *useraddr) -{ - u32 ethcmd; - - if (get_user(ethcmd, (u32 __user *)useraddr)) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, driver_name); - strcpy (info.version, "$Rev: 1224 $"); - /* FIXME XXX provide sane businfo */ - strcpy (info.bus_info, "ieee1394"); - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - break; - } - case ETHTOOL_GSET: - case ETHTOOL_SSET: - case ETHTOOL_NWAY_RST: - case ETHTOOL_GLINK: - case ETHTOOL_GMSGLVL: - case ETHTOOL_SMSGLVL: - default: - return -EOPNOTSUPP; - } - - return 0; + strcpy (info->driver, driver_name); + strcpy (info->version, "$Rev: 1224 $"); + /* FIXME XXX provide sane businfo */ + strcpy (info->bus_info, "ieee1394"); } +static struct ethtool_ops ethtool_ops = { + .get_drvinfo = ether1394_get_drvinfo +}; static int __init ether1394_init_module (void) { --- linux-2.6.9-rc1/drivers/ieee1394/ohci1394.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/ieee1394/ohci1394.c 2004-09-03 00:56:31.189419136 -0700 @@ -162,7 +162,7 @@ printk(level "%s: " fmt "\n" , OHCI1394_ printk(level "%s: fw-host%d: " fmt "\n" , OHCI1394_DRIVER_NAME, ohci->host->id , ## args) static char version[] __devinitdata = - "$Rev: 1223 $ Ben Collins "; + "$Rev: 1226 $ Ben Collins "; /* Module Parameters */ static int phys_dma = 1; @@ -2545,6 +2545,10 @@ static void insert_dma_buffer(struct dma idx = (idx + d->num_desc - 1 ) % d->num_desc; d->prg_cpu[idx]->branchAddress |= le32_to_cpu(0x00000001); + /* To avoid a race, ensure 1394 interface hardware sees the inserted + * context program descriptors before it sees the wakeup bit set. */ + wmb(); + /* wake up the dma context if necessary */ if (!(reg_read(ohci, d->ctrlSet) & 0x400)) { PRINT(KERN_INFO, --- linux-2.6.9-rc1/drivers/ieee1394/pcilynx.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/drivers/ieee1394/pcilynx.c 2004-09-03 00:56:31.191418832 -0700 @@ -500,7 +500,7 @@ static void send_next(struct ti_lynx *ly pcl.async_error_next = PCL_NEXT_INVALID; pcl.pcl_status = 0; pcl.buffer[0].control = packet->speed_code << 14 | packet->header_size; -#ifdef __BIG_ENDIAN +#ifndef __BIG_ENDIAN pcl.buffer[0].control |= PCL_BIGENDIAN; #endif pcl.buffer[0].pointer = d->header_dma; @@ -1697,7 +1697,7 @@ static int __devinit add_card(struct pci pcl.async_error_next = PCL_NEXT_INVALID; pcl.buffer[0].control = PCL_CMD_RCV | 16; -#ifdef __BIG_ENDIAN +#ifndef __BIG_ENDIAN pcl.buffer[0].control |= PCL_BIGENDIAN; #endif pcl.buffer[1].control = PCL_LAST_BUFF | 4080; --- linux-2.6.9-rc1/drivers/ieee1394/sbp2.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/ieee1394/sbp2.c 2004-09-03 00:56:31.193418528 -0700 @@ -78,7 +78,7 @@ #include "sbp2.h" static char version[] __devinitdata = - "$Rev: 1219 $ Ben Collins "; + "$Rev: 1231 $ Ben Collins "; /* * Module load parameter definitions --- linux-2.6.9-rc1/drivers/input/gameport/emu10k1-gp.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/input/gameport/emu10k1-gp.c 2004-09-03 00:56:31.534366696 -0700 @@ -50,8 +50,11 @@ struct emu { }; static struct pci_device_id emu_tbl[] = { + { 0x1102, 0x7002, PCI_ANY_ID, PCI_ANY_ID }, /* SB Live gameport */ { 0x1102, 0x7003, PCI_ANY_ID, PCI_ANY_ID }, /* Audigy gameport */ + { 0x1102, 0x7004, PCI_ANY_ID, PCI_ANY_ID }, /* Dell SB Live */ + { 0x1102, 0x7005, PCI_ANY_ID, PCI_ANY_ID }, /* Audigy LS gameport */ { 0, } }; --- linux-2.6.9-rc1/drivers/input/joydev.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/input/joydev.c 2004-09-03 00:56:31.534366696 -0700 @@ -232,8 +232,10 @@ static ssize_t joydev_read(struct file * && list->head == list->tail && (file->f_flags & O_NONBLOCK)) return -EAGAIN; - retval = wait_event_interruptible(list->joydev->wait, list->joydev->exist - && (list->startup < joydev->nabs + joydev->nkey || list->head != list->tail)); + retval = wait_event_interruptible(list->joydev->wait, + !list->joydev->exist || + list->startup < joydev->nabs + joydev->nkey || + list->head != list->tail); if (retval) return retval; --- linux-2.6.9-rc1/drivers/input/joystick/gamecon.c 2004-08-24 00:03:40.000000000 -0700 +++ 25/drivers/input/joystick/gamecon.c 2004-09-03 00:56:31.537366240 -0700 @@ -1,7 +1,8 @@ /* - * $Id: gamecon.c,v 1.22 2002/07/01 15:42:25 vojtech Exp $ + * NES, SNES, N64, MultiSystem, PSX gamepad driver for Linux * - * Copyright (c) 1999-2001 Vojtech Pavlik + * Copyright (c) 1999-2004 Vojtech Pavlik + * Copyright (c) 2004 Peter Nelson * * Based on the work of: * Andree Borrmann John Dahlstrom @@ -9,10 +10,6 @@ */ /* - * NES, SNES, N64, MultiSystem, PSX gamepad driver for Linux - */ - -/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -72,8 +69,9 @@ __obsolete_setup("gc_3="); #define GC_MULTI2 5 #define GC_N64 6 #define GC_PSX 7 +#define GC_DDR 8 -#define GC_MAX 7 +#define GC_MAX 8 #define GC_REFRESH_TIME HZ/100 @@ -91,7 +89,8 @@ static struct gc *gc_base[3]; static int gc_status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 }; static char *gc_names[] = { NULL, "SNES pad", "NES pad", "NES FourPort", "Multisystem joystick", - "Multisystem 2-button joystick", "N64 controller", "PSX controller" }; + "Multisystem 2-button joystick", "N64 controller", "PSX controller" + "PSX DDR controller" }; /* * N64 support. */ @@ -237,7 +236,7 @@ static void gc_multi_read_packet(struct #define GC_PSX_RUMBLE 7 /* Rumble in Red mode */ #define GC_PSX_CLOCK 0x04 /* Pin 4 */ -#define GC_PSX_COMMAND 0x01 /* Pin 1 */ +#define GC_PSX_COMMAND 0x01 /* Pin 2 */ #define GC_PSX_POWER 0xf8 /* Pins 5-9 */ #define GC_PSX_SELECT 0x02 /* Pin 3 */ @@ -253,25 +252,29 @@ __obsolete_setup("gc_psx_delay="); static short gc_psx_abs[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_HAT0X, ABS_HAT0Y }; static short gc_psx_btn[] = { BTN_TL, BTN_TR, BTN_TL2, BTN_TR2, BTN_A, BTN_B, BTN_X, BTN_Y, BTN_START, BTN_SELECT, BTN_THUMBL, BTN_THUMBR }; +static short gc_psx_ddr_btn[] = { BTN_0, BTN_1, BTN_2, BTN_3 }; /* * gc_psx_command() writes 8bit command and reads 8bit data from * the psx pad. */ -static int gc_psx_command(struct gc *gc, int b) +static void gc_psx_command(struct gc *gc, int b, unsigned char data[GC_PSX_LENGTH]) { - int i, cmd, data = 0; + int i, j, cmd, read; + for (i = 0; i < 5; i++) + data[i] = 0; for (i = 0; i < 8; i++, b >>= 1) { cmd = (b & 1) ? GC_PSX_COMMAND : 0; parport_write_data(gc->pd->port, cmd | GC_PSX_POWER); udelay(gc_psx_delay); - data |= ((parport_read_status(gc->pd->port) ^ 0x80) & gc->pads[GC_PSX]) ? (1 << i) : 0; + read = parport_read_status(gc->pd->port) ^ 0x80; + for (j = 0; j < 5; j++) + data[j] |= (read & gc_status_bit[j] & gc->pads[GC_PSX]) ? (1 << i) : 0; parport_write_data(gc->pd->port, cmd | GC_PSX_CLOCK | GC_PSX_POWER); udelay(gc_psx_delay); } - return data; } /* @@ -279,30 +282,39 @@ static int gc_psx_command(struct gc *gc, * device identifier code. */ -static int gc_psx_read_packet(struct gc *gc, unsigned char *data) +static void gc_psx_read_packet(struct gc *gc, unsigned char data[5][GC_PSX_LENGTH], unsigned char id[5]) { - int i, id; + int i, j, max_len = 0; unsigned long flags; + unsigned char data2[5]; parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_SELECT | GC_PSX_POWER); /* Select pad */ - udelay(gc_psx_delay * 2); + udelay(gc_psx_delay); parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_POWER); /* Deselect, begin command */ - udelay(gc_psx_delay * 2); + udelay(gc_psx_delay); local_irq_save(flags); - gc_psx_command(gc, 0x01); /* Access pad */ - id = gc_psx_command(gc, 0x42); /* Get device id */ - if (gc_psx_command(gc, 0) == 0x5a) { /* Okay? */ - for (i = 0; i < GC_PSX_LEN(id) * 2; i++) - data[i] = gc_psx_command(gc, 0); - } else id = 0; + gc_psx_command(gc, 0x01, data2); /* Access pad */ + gc_psx_command(gc, 0x42, id); /* Get device ids */ + gc_psx_command(gc, 0, data2); /* Dump status */ + + for (i =0; i < 5; i++) /* Find the longest pad */ + if((gc_status_bit[i] & gc->pads[GC_PSX]) && (GC_PSX_LEN(id[i]) > max_len)) + max_len = GC_PSX_LEN(id[i]); + + for (i = 0; i < max_len * 2; i++) { /* Read in all the data */ + gc_psx_command(gc, 0, data2); + for (j = 0; j < 5; j++) + data[j][i] = data2[j]; + } local_irq_restore(flags); parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_SELECT | GC_PSX_POWER); - return GC_PSX_ID(id); + for(i = 0; i < 5; i++) /* Set id's to the real value */ + id[i] = GC_PSX_ID(id[i]); } /* @@ -316,6 +328,7 @@ static void gc_timer(unsigned long priva struct gc *gc = (void *) private; struct input_dev *dev = gc->dev; unsigned char data[GC_MAX_LENGTH]; + unsigned char data_psx[5][GC_PSX_LENGTH]; int i, j, s; /* @@ -412,53 +425,72 @@ static void gc_timer(unsigned long priva * PSX controllers */ - if (gc->pads[GC_PSX]) { + if (gc->pads[GC_PSX] || gc->pads[GC_DDR]) { - for (i = 0; i < 5; i++) - if (gc->pads[GC_PSX] & gc_status_bit[i]) - break; + gc_psx_read_packet(gc, data_psx, data); - switch (gc_psx_read_packet(gc, data)) { + for (i = 0; i < 5; i++) { + switch (data[i]) { - case GC_PSX_RUMBLE: + case GC_PSX_RUMBLE: - input_report_key(dev + i, BTN_THUMBL, ~data[0] & 0x04); - input_report_key(dev + i, BTN_THUMBR, ~data[0] & 0x02); - input_sync(dev + i); + input_report_key(dev + i, BTN_THUMBL, ~data_psx[i][0] & 0x04); + input_report_key(dev + i, BTN_THUMBR, ~data_psx[i][0] & 0x02); - case GC_PSX_NEGCON: - case GC_PSX_ANALOG: + case GC_PSX_NEGCON: + case GC_PSX_ANALOG: - for (j = 0; j < 4; j++) - input_report_abs(dev + i, gc_psx_abs[j], data[j + 2]); + if(gc->pads[GC_DDR] & gc_status_bit[i]) { + for(j = 0; j < 4; j++) + input_report_key(dev + i, gc_psx_ddr_btn[j], ~data_psx[i][0] & (0x10 << j)); + } else { + for (j = 0; j < 4; j++) + input_report_abs(dev + i, gc_psx_abs[j+2], data_psx[i][j + 2]); - input_report_abs(dev + i, ABS_HAT0X, !(data[0] & 0x20) - !(data[0] & 0x80)); - input_report_abs(dev + i, ABS_HAT0Y, !(data[0] & 0x40) - !(data[0] & 0x10)); + input_report_abs(dev + i, ABS_X, 128 + !(data_psx[i][0] & 0x20) * 127 - !(data_psx[i][0] & 0x80) * 128); + input_report_abs(dev + i, ABS_Y, 128 + !(data_psx[i][0] & 0x40) * 127 - !(data_psx[i][0] & 0x10) * 128); + } - for (j = 0; j < 8; j++) - input_report_key(dev + i, gc_psx_btn[j], ~data[1] & (1 << j)); + for (j = 0; j < 8; j++) + input_report_key(dev + i, gc_psx_btn[j], ~data_psx[i][1] & (1 << j)); - input_report_key(dev + i, BTN_START, ~data[0] & 0x08); - input_report_key(dev + i, BTN_SELECT, ~data[0] & 0x01); + input_report_key(dev + i, BTN_START, ~data_psx[i][0] & 0x08); + input_report_key(dev + i, BTN_SELECT, ~data_psx[i][0] & 0x01); - input_sync(dev + i); + input_sync(dev + i); - break; + break; - case GC_PSX_NORMAL: + case GC_PSX_NORMAL: + if(gc->pads[GC_DDR] & gc_status_bit[i]) { + for(j = 0; j < 4; j++) + input_report_key(dev + i, gc_psx_ddr_btn[j], ~data_psx[i][0] & (0x10 << j)); + } else { + input_report_abs(dev + i, ABS_X, 128 + !(data_psx[i][0] & 0x20) * 127 - !(data_psx[i][0] & 0x80) * 128); + input_report_abs(dev + i, ABS_Y, 128 + !(data_psx[i][0] & 0x40) * 127 - !(data_psx[i][0] & 0x10) * 128); - input_report_abs(dev + i, ABS_X, 128 + !(data[0] & 0x20) * 127 - !(data[0] & 0x80) * 128); - input_report_abs(dev + i, ABS_Y, 128 + !(data[0] & 0x40) * 127 - !(data[0] & 0x10) * 128); + /* for some reason if the extra axes are left unset they drift */ + /* for (j = 0; j < 4; j++) + input_report_abs(dev + i, gc_psx_abs[j+2], 128); + * This needs to be debugged properly, + * maybe fuzz processing needs to be done in input_sync() + * --vojtech + */ + } - for (j = 0; j < 8; j++) - input_report_key(dev + i, gc_psx_btn[j], ~data[1] & (1 << j)); + for (j = 0; j < 8; j++) + input_report_key(dev + i, gc_psx_btn[j], ~data_psx[i][1] & (1 << j)); - input_report_key(dev + i, BTN_START, ~data[0] & 0x08); - input_report_key(dev + i, BTN_SELECT, ~data[0] & 0x01); + input_report_key(dev + i, BTN_START, ~data_psx[i][0] & 0x08); + input_report_key(dev + i, BTN_SELECT, ~data_psx[i][0] & 0x01); - input_sync(dev + i); + input_sync(dev + i); - break; + break; + + case 0: /* not a pad, ignore */ + break; + } } } @@ -490,8 +522,7 @@ static struct gc __init *gc_probe(int *c { struct gc *gc; struct parport *pp; - int i, j, psx; - unsigned char data[32]; + int i, j; if (config[0] < 0) return NULL; @@ -588,43 +619,22 @@ static struct gc __init *gc_probe(int *c break; case GC_PSX: + case GC_DDR: + if(config[i + 1] == GC_DDR) { + for (j = 0; j < 4; j++) + set_bit(gc_psx_ddr_btn[j], gc->dev[i].keybit); + } else { + for (j = 0; j < 6; j++) { + set_bit(gc_psx_abs[j], gc->dev[i].absbit); + gc->dev[i].absmin[gc_psx_abs[j]] = 4; + gc->dev[i].absmax[gc_psx_abs[j]] = 252; + gc->dev[i].absflat[gc_psx_abs[j]] = 2; + } + } - psx = gc_psx_read_packet(gc, data); + for (j = 0; j < 12; j++) + set_bit(gc_psx_btn[j], gc->dev[i].keybit); - switch(psx) { - case GC_PSX_NEGCON: - case GC_PSX_NORMAL: - case GC_PSX_ANALOG: - case GC_PSX_RUMBLE: - - for (j = 0; j < 6; j++) { - psx = gc_psx_abs[j]; - set_bit(psx, gc->dev[i].absbit); - if (j < 4) { - gc->dev[i].absmin[psx] = 4; - gc->dev[i].absmax[psx] = 252; - gc->dev[i].absflat[psx] = 2; - } else { - gc->dev[i].absmin[psx] = -1; - gc->dev[i].absmax[psx] = 1; - } - } - - for (j = 0; j < 12; j++) - set_bit(gc_psx_btn[j], gc->dev[i].keybit); - - break; - - case 0: - gc->pads[GC_PSX] &= ~gc_status_bit[i]; - printk(KERN_ERR "gamecon.c: No PSX controller found.\n"); - break; - - default: - gc->pads[GC_PSX] &= ~gc_status_bit[i]; - printk(KERN_WARNING "gamecon.c: Unsupported PSX controller %#x," - " please report to .\n", psx); - } break; } --- linux-2.6.9-rc1/drivers/input/joystick/iforce/iforce.h 2004-08-24 00:03:50.000000000 -0700 +++ 25/drivers/input/joystick/iforce/iforce.h 2004-09-03 00:56:31.538366088 -0700 @@ -187,5 +187,5 @@ int iforce_upload_constant(struct iforce int iforce_upload_condition(struct iforce*, struct ff_effect*, int is_update); /* Public variables */ -extern struct serio_dev iforce_serio_dev; +extern struct serio_driver iforce_serio_drv; extern struct usb_driver iforce_usb_driver; --- linux-2.6.9-rc1/drivers/input/joystick/iforce/iforce-main.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/drivers/input/joystick/iforce/iforce-main.c 2004-09-03 00:56:31.537366240 -0700 @@ -524,7 +524,7 @@ static int __init iforce_init(void) usb_register(&iforce_usb_driver); #endif #ifdef CONFIG_JOYSTICK_IFORCE_232 - serio_register_device(&iforce_serio_dev); + serio_register_driver(&iforce_serio_drv); #endif return 0; } @@ -535,7 +535,7 @@ static void __exit iforce_exit(void) usb_deregister(&iforce_usb_driver); #endif #ifdef CONFIG_JOYSTICK_IFORCE_232 - serio_unregister_device(&iforce_serio_dev); + serio_unregister_driver(&iforce_serio_drv); #endif } --- linux-2.6.9-rc1/drivers/input/joystick/iforce/iforce-serio.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/drivers/input/joystick/iforce/iforce-serio.c 2004-09-03 00:56:31.538366088 -0700 @@ -124,7 +124,7 @@ out: return IRQ_HANDLED; } -static void iforce_serio_connect(struct serio *serio, struct serio_dev *dev) +static void iforce_serio_connect(struct serio *serio, struct serio_driver *drv) { struct iforce *iforce; if (serio->type != (SERIO_RS232 | SERIO_IFORCE)) @@ -137,7 +137,7 @@ static void iforce_serio_connect(struct iforce->serio = serio; serio->private = iforce; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(iforce); return; } @@ -158,9 +158,13 @@ static void iforce_serio_disconnect(stru kfree(iforce); } -struct serio_dev iforce_serio_dev = { - .write_wakeup = iforce_serio_write_wakeup, - .interrupt = iforce_serio_irq, - .connect = iforce_serio_connect, - .disconnect = iforce_serio_disconnect, +struct serio_driver iforce_serio_drv = { + .driver = { + .name = "iforce", + }, + .description = "RS232 I-Force joysticks and wheels driver", + .write_wakeup = iforce_serio_write_wakeup, + .interrupt = iforce_serio_irq, + .connect = iforce_serio_connect, + .disconnect = iforce_serio_disconnect, }; --- linux-2.6.9-rc1/drivers/input/joystick/Kconfig 2004-08-24 00:01:51.000000000 -0700 +++ 25/drivers/input/joystick/Kconfig 2004-09-03 00:56:31.535366544 -0700 @@ -247,7 +247,7 @@ config JOYSTICK_AMIGA To compile this driver as a module, choose M here: the module will be called amijoy. -config INPUT_JOYDUMP +config JOYSTICK_JOYDUMP tristate "Gameport data dumper" depends on INPUT && INPUT_JOYSTICK help --- linux-2.6.9-rc1/drivers/input/joystick/magellan.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/drivers/input/joystick/magellan.c 2004-09-03 00:56:31.539365936 -0700 @@ -35,8 +35,10 @@ #include #include +#define DRIVER_DESC "Magellan and SpaceMouse 6dof controller driver" + MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("Magellan and SpaceMouse 6dof controller driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); /* @@ -146,7 +148,7 @@ static void magellan_disconnect(struct s * it as an input device. */ -static void magellan_connect(struct serio *serio, struct serio_dev *dev) +static void magellan_connect(struct serio *serio, struct serio_driver *drv) { struct magellan *magellan; int i, t; @@ -184,7 +186,7 @@ static void magellan_connect(struct seri serio->private = magellan; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(magellan); return; } @@ -199,10 +201,14 @@ static void magellan_connect(struct seri * The serio device structure. */ -static struct serio_dev magellan_dev = { - .interrupt = magellan_interrupt, - .connect = magellan_connect, - .disconnect = magellan_disconnect, +static struct serio_driver magellan_drv = { + .driver = { + .name = "magellan", + }, + .description = DRIVER_DESC, + .interrupt = magellan_interrupt, + .connect = magellan_connect, + .disconnect = magellan_disconnect, }; /* @@ -211,13 +217,13 @@ static struct serio_dev magellan_dev = { int __init magellan_init(void) { - serio_register_device(&magellan_dev); + serio_register_driver(&magellan_drv); return 0; } void __exit magellan_exit(void) { - serio_unregister_device(&magellan_dev); + serio_unregister_driver(&magellan_drv); } module_init(magellan_init); --- linux-2.6.9-rc1/drivers/input/joystick/spaceball.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/drivers/input/joystick/spaceball.c 2004-09-03 00:56:31.540365784 -0700 @@ -39,8 +39,10 @@ #include #include +#define DRIVER_DESC "SpaceTec SpaceBall 2003/3003/4000 FLX driver" + MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("SpaceTec SpaceBall 2003/3003/4000 FLX driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); /* @@ -201,7 +203,7 @@ static void spaceball_disconnect(struct * it as an input device. */ -static void spaceball_connect(struct serio *serio, struct serio_dev *dev) +static void spaceball_connect(struct serio *serio, struct serio_driver *drv) { struct spaceball *spaceball; int i, t, id; @@ -254,7 +256,7 @@ static void spaceball_connect(struct ser serio->private = spaceball; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(spaceball); return; } @@ -269,10 +271,14 @@ static void spaceball_connect(struct ser * The serio device structure. */ -static struct serio_dev spaceball_dev = { - .interrupt = spaceball_interrupt, - .connect = spaceball_connect, - .disconnect = spaceball_disconnect, +static struct serio_driver spaceball_drv = { + .driver = { + .name = "spaceball", + }, + .description = DRIVER_DESC, + .interrupt = spaceball_interrupt, + .connect = spaceball_connect, + .disconnect = spaceball_disconnect, }; /* @@ -281,13 +287,13 @@ static struct serio_dev spaceball_dev = int __init spaceball_init(void) { - serio_register_device(&spaceball_dev); + serio_register_driver(&spaceball_drv); return 0; } void __exit spaceball_exit(void) { - serio_unregister_device(&spaceball_dev); + serio_unregister_driver(&spaceball_drv); } module_init(spaceball_init); --- linux-2.6.9-rc1/drivers/input/joystick/spaceorb.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/drivers/input/joystick/spaceorb.c 2004-09-03 00:56:31.540365784 -0700 @@ -38,8 +38,10 @@ #include #include +#define DRIVER_DESC "SpaceTec SpaceOrb 360 and Avenger 6dof controller driver" + MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("SpaceTec SpaceOrb 360 and Avenger 6dof controller driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); /* @@ -162,7 +164,7 @@ static void spaceorb_disconnect(struct s * it as an input device. */ -static void spaceorb_connect(struct serio *serio, struct serio_dev *dev) +static void spaceorb_connect(struct serio *serio, struct serio_driver *drv) { struct spaceorb *spaceorb; int i, t; @@ -201,7 +203,7 @@ static void spaceorb_connect(struct seri serio->private = spaceorb; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(spaceorb); return; } @@ -213,10 +215,14 @@ static void spaceorb_connect(struct seri * The serio device structure. */ -static struct serio_dev spaceorb_dev = { - .interrupt = spaceorb_interrupt, - .connect = spaceorb_connect, - .disconnect = spaceorb_disconnect, +static struct serio_driver spaceorb_drv = { + .driver = { + .name = "spaceorb", + }, + .description = DRIVER_DESC, + .interrupt = spaceorb_interrupt, + .connect = spaceorb_connect, + .disconnect = spaceorb_disconnect, }; /* @@ -225,13 +231,13 @@ static struct serio_dev spaceorb_dev = { int __init spaceorb_init(void) { - serio_register_device(&spaceorb_dev); + serio_register_driver(&spaceorb_drv); return 0; } void __exit spaceorb_exit(void) { - serio_unregister_device(&spaceorb_dev); + serio_unregister_driver(&spaceorb_drv); } module_init(spaceorb_init); --- linux-2.6.9-rc1/drivers/input/joystick/stinger.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/drivers/input/joystick/stinger.c 2004-09-03 00:56:31.541365632 -0700 @@ -36,8 +36,10 @@ #include #include +#define DRIVER_DESC "Gravis Stinger gamepad driver" + MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("Gravis Stinger gamepad driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); /* @@ -134,7 +136,7 @@ static void stinger_disconnect(struct se * it as an input device. */ -static void stinger_connect(struct serio *serio, struct serio_dev *dev) +static void stinger_connect(struct serio *serio, struct serio_driver *drv) { struct stinger *stinger; int i; @@ -172,7 +174,7 @@ static void stinger_connect(struct serio stinger->dev.private = stinger; serio->private = stinger; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(stinger); return; } @@ -187,10 +189,14 @@ static void stinger_connect(struct serio * The serio device structure. */ -static struct serio_dev stinger_dev = { - .interrupt = stinger_interrupt, - .connect = stinger_connect, - .disconnect = stinger_disconnect, +static struct serio_driver stinger_drv = { + .driver = { + .name = "stinger", + }, + .description = DRIVER_DESC, + .interrupt = stinger_interrupt, + .connect = stinger_connect, + .disconnect = stinger_disconnect, }; /* @@ -199,13 +205,13 @@ static struct serio_dev stinger_dev = { int __init stinger_init(void) { - serio_register_device(&stinger_dev); + serio_register_driver(&stinger_drv); return 0; } void __exit stinger_exit(void) { - serio_unregister_device(&stinger_dev); + serio_unregister_driver(&stinger_drv); } module_init(stinger_init); --- linux-2.6.9-rc1/drivers/input/joystick/tmdc.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/drivers/input/joystick/tmdc.c 2004-09-03 00:56:31.541365632 -0700 @@ -322,7 +322,7 @@ static void tmdc_connect(struct gameport tmdc->dev[j].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); for (i = 0; i < models[m].abs && i < TMDC_ABS; i++) { - if (tmdc->abs[i] < 0) continue; + if (tmdc->abs[j][i] < 0) continue; set_bit(tmdc->abs[j][i], tmdc->dev[j].absbit); tmdc->dev[j].absmin[tmdc->abs[j][i]] = 8; tmdc->dev[j].absmax[tmdc->abs[j][i]] = 248; --- linux-2.6.9-rc1/drivers/input/joystick/twidjoy.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/input/joystick/twidjoy.c 2004-09-03 00:56:31.542365480 -0700 @@ -187,7 +187,7 @@ static void twidjoy_disconnect(struct se * it as an input device. */ -static void twidjoy_connect(struct serio *serio, struct serio_dev *dev) +static void twidjoy_connect(struct serio *serio, struct serio_driver *drv) { struct twidjoy_button_spec *bp; struct twidjoy *twidjoy; @@ -232,7 +232,7 @@ static void twidjoy_connect(struct serio twidjoy->dev.private = twidjoy; serio->private = twidjoy; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(twidjoy); return; } @@ -246,10 +246,14 @@ static void twidjoy_connect(struct serio * The serio device structure. */ -static struct serio_dev twidjoy_dev = { - .interrupt = twidjoy_interrupt, - .connect = twidjoy_connect, - .disconnect = twidjoy_disconnect, +static struct serio_driver twidjoy_drv = { + .driver = { + .name = "twidjoy", + }, + .description = DRIVER_DESC, + .interrupt = twidjoy_interrupt, + .connect = twidjoy_connect, + .disconnect = twidjoy_disconnect, }; /* @@ -258,13 +262,13 @@ static struct serio_dev twidjoy_dev = { int __init twidjoy_init(void) { - serio_register_device(&twidjoy_dev); + serio_register_driver(&twidjoy_drv); return 0; } void __exit twidjoy_exit(void) { - serio_unregister_device(&twidjoy_dev); + serio_unregister_driver(&twidjoy_drv); } module_init(twidjoy_init); --- linux-2.6.9-rc1/drivers/input/joystick/warrior.c 2004-08-24 00:03:17.000000000 -0700 +++ 25/drivers/input/joystick/warrior.c 2004-09-03 00:56:31.542365480 -0700 @@ -35,8 +35,10 @@ #include #include +#define DRIVER_DESC "Logitech WingMan Warrior joystick driver" + MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("Logitech WingMan Warrior joystick driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); /* @@ -139,7 +141,7 @@ static void warrior_disconnect(struct se * it as an input device. */ -static void warrior_connect(struct serio *serio, struct serio_dev *dev) +static void warrior_connect(struct serio *serio, struct serio_driver *drv) { struct warrior *warrior; int i; @@ -185,7 +187,7 @@ static void warrior_connect(struct serio serio->private = warrior; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(warrior); return; } @@ -199,10 +201,14 @@ static void warrior_connect(struct serio * The serio device structure. */ -static struct serio_dev warrior_dev = { - .interrupt = warrior_interrupt, - .connect = warrior_connect, - .disconnect = warrior_disconnect, +static struct serio_driver warrior_drv = { + .driver = { + .name = "warrior", + }, + .description = DRIVER_DESC, + .interrupt = warrior_interrupt, + .connect = warrior_connect, + .disconnect = warrior_disconnect, }; /* @@ -211,13 +217,13 @@ static struct serio_dev warrior_dev = { int __init warrior_init(void) { - serio_register_device(&warrior_dev); + serio_register_driver(&warrior_drv); return 0; } void __exit warrior_exit(void) { - serio_unregister_device(&warrior_dev); + serio_unregister_driver(&warrior_drv); } module_init(warrior_init); --- linux-2.6.9-rc1/drivers/input/keyboard/atkbd.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/input/keyboard/atkbd.c 2004-09-03 00:56:54.239914928 -0700 @@ -27,8 +27,10 @@ #include #include +#define DRIVER_DESC "AT and PS/2 keyboard driver" + MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("AT and PS/2 keyboard driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); static int atkbd_set = 2; @@ -47,6 +49,10 @@ static int atkbd_softrepeat; module_param_named(softrepeat, atkbd_softrepeat, bool, 0); MODULE_PARM_DESC(softrepeat, "Use software keyboard repeat"); +static int atkbd_softraw = 1; +module_param_named(softraw, atkbd_softraw, bool, 0); +MODULE_PARM_DESC(softraw, "Use software generated rawmode"); + static int atkbd_scroll; module_param_named(scroll, atkbd_scroll, bool, 0); MODULE_PARM_DESC(scroll, "Enable scroll-wheel on MS Office and similar keyboards"); @@ -164,36 +170,64 @@ static unsigned char atkbd_scroll_keys[5 { ATKBD_SCR_CLICK, 0x60 }, }; +#define ATKBD_FLAG_ACK 0 /* Waiting for ACK/NAK */ +#define ATKBD_FLAG_CMD 1 /* Waiting for command to finish */ +#define ATKBD_FLAG_CMD1 2 /* First byte of command response */ +#define ATKBD_FLAG_ENABLED 3 /* Waining for init to finish */ + /* * The atkbd control structure */ struct atkbd { - unsigned char keycode[512]; - struct input_dev dev; - struct serio *serio; + /* Written only during init */ char name[64]; char phys[32]; - unsigned short id; + struct serio *serio; + struct input_dev dev; + unsigned char set; - unsigned int translated:1; - unsigned int extra:1; - unsigned int write:1; + unsigned short id; + unsigned char keycode[512]; + unsigned char translated; + unsigned char extra; + unsigned char write; + + /* Protected by FLAG_ACK */ + unsigned char nak; + /* Protected by FLAG_CMD */ unsigned char cmdbuf[4]; unsigned char cmdcnt; - volatile signed char ack; - unsigned char emul; - unsigned int resend:1; - unsigned int release:1; - unsigned int bat_xl:1; - unsigned int enabled:1; + /* Accessed only from interrupt */ + unsigned char emul; + unsigned char resend; + unsigned char release; + unsigned char bat_xl; unsigned int last; unsigned long time; + + /* Ensures that only one command is executing at a time */ + struct semaphore cmd_sem; + + /* Used to signal completion from interrupt handler */ + wait_queue_head_t wait; + + /* Flags */ + unsigned long flags; }; +/* Work structure to schedule execution of a command */ +struct atkbd_work { + struct work_struct work; + struct atkbd *atkbd; + int command; + unsigned char param[0]; +}; + + static void atkbd_report_key(struct input_dev *dev, struct pt_regs *regs, int code, int value) { input_regs(dev, regs); @@ -224,7 +258,7 @@ static irqreturn_t atkbd_interrupt(struc #if !defined(__i386__) && !defined (__x86_64__) if ((flags & (SERIO_FRAME | SERIO_PARITY)) && (~flags & SERIO_TIMEOUT) && !atkbd->resend && atkbd->write) { - printk("atkbd.c: frame/parity error: %02x\n", flags); + printk(KERN_WARNING "atkbd.c: frame/parity error: %02x\n", flags); serio_write(serio, ATKBD_CMD_RESEND); atkbd->resend = 1; goto out; @@ -234,24 +268,46 @@ static irqreturn_t atkbd_interrupt(struc atkbd->resend = 0; #endif - if (!atkbd->ack) + if (test_bit(ATKBD_FLAG_ACK, &atkbd->flags)) { switch (code) { case ATKBD_RET_ACK: - atkbd->ack = 1; - goto out; + atkbd->nak = 0; + if (atkbd->cmdcnt) { + set_bit(ATKBD_FLAG_CMD, &atkbd->flags); + set_bit(ATKBD_FLAG_CMD1, &atkbd->flags); + } + clear_bit(ATKBD_FLAG_ACK, &atkbd->flags); + wake_up_interruptible(&atkbd->wait); + break; case ATKBD_RET_NAK: - atkbd->ack = -1; - goto out; + atkbd->nak = 1; + clear_bit(ATKBD_FLAG_ACK, &atkbd->flags); + wake_up_interruptible(&atkbd->wait); + break; } + goto out; + } + + if (test_bit(ATKBD_FLAG_CMD, &atkbd->flags)) { + + if (atkbd->cmdcnt) + atkbd->cmdbuf[--atkbd->cmdcnt] = code; - if (atkbd->cmdcnt) { - atkbd->cmdbuf[--atkbd->cmdcnt] = code; + if (test_and_clear_bit(ATKBD_FLAG_CMD1, &atkbd->flags) && atkbd->cmdcnt) + wake_up_interruptible(&atkbd->wait); + + if (!atkbd->cmdcnt) { + clear_bit(ATKBD_FLAG_CMD, &atkbd->flags); + wake_up_interruptible(&atkbd->wait); + } goto out; } - if (!atkbd->enabled) + if (!test_bit(ATKBD_FLAG_ENABLED, &atkbd->flags)) goto out; + input_event(&atkbd->dev, EV_MSC, MSC_RAW, code); + if (atkbd->translated) { if (atkbd->emul || @@ -270,6 +326,7 @@ static irqreturn_t atkbd_interrupt(struc switch (code) { case ATKBD_RET_BAT: + clear_bit(ATKBD_FLAG_ENABLED, &atkbd->flags); serio_rescan(atkbd->serio); goto out; case ATKBD_RET_EMUL0: @@ -288,7 +345,10 @@ static irqreturn_t atkbd_interrupt(struc atkbd_report_key(&atkbd->dev, regs, KEY_HANJA, 3); goto out; case ATKBD_RET_ERR: - printk(KERN_DEBUG "atkbd.c: Keyboard on %s reports too many keys pressed.\n", serio->phys); + /* + * printk(KERN_DEBUG "atkbd.c: Keyboard on %s reports " + * "too many keys pressed.\n", serio->phys); + */ goto out; } @@ -300,6 +360,9 @@ static irqreturn_t atkbd_interrupt(struc code |= (atkbd->set != 3) ? 0x80 : 0x100; } + if (atkbd->keycode[code] != ATKBD_KEY_NULL) + input_event(&atkbd->dev, EV_MSC, MSC_SCAN, code); + switch (atkbd->keycode[code]) { case ATKBD_KEY_NULL: break; @@ -372,83 +435,147 @@ out: * acknowledge. It doesn't handle resends according to the keyboard * protocol specs, because if these are needed, the keyboard needs * replacement anyway, and they only make a mess in the protocol. + * + * atkbd_sendbyte() can only be called from a process context */ static int atkbd_sendbyte(struct atkbd *atkbd, unsigned char byte) { - int timeout = 20000; /* 200 msec */ - atkbd->ack = 0; - #ifdef ATKBD_DEBUG printk(KERN_DEBUG "atkbd.c: Sent: %02x\n", byte); #endif - if (serio_write(atkbd->serio, byte)) - return -1; + atkbd->nak = 1; + set_bit(ATKBD_FLAG_ACK, &atkbd->flags); - while (!atkbd->ack && timeout--) udelay(10); + if (serio_write(atkbd->serio, byte) == 0) + wait_event_interruptible_timeout(atkbd->wait, + !test_bit(ATKBD_FLAG_ACK, &atkbd->flags), + msecs_to_jiffies(200)); - return -(atkbd->ack <= 0); + clear_bit(ATKBD_FLAG_ACK, &atkbd->flags); + return -atkbd->nak; } /* * atkbd_command() sends a command, and its parameters to the keyboard, * then waits for the response and puts it in the param array. + * + * atkbd_command() can only be called from a process context */ static int atkbd_command(struct atkbd *atkbd, unsigned char *param, int command) { - int timeout = 500000; /* 500 msec */ + int timeout; int send = (command >> 12) & 0xf; int receive = (command >> 8) & 0xf; + int rc = -1; int i; - atkbd->cmdcnt = receive; + timeout = msecs_to_jiffies(command == ATKBD_CMD_RESET_BAT ? 4000 : 500); - if (command == ATKBD_CMD_RESET_BAT) - timeout = 2000000; /* 2 sec */ + down(&atkbd->cmd_sem); + clear_bit(ATKBD_FLAG_CMD, &atkbd->flags); if (receive && param) for (i = 0; i < receive; i++) atkbd->cmdbuf[(receive - 1) - i] = param[i]; + atkbd->cmdcnt = receive; + if (command & 0xff) if (atkbd_sendbyte(atkbd, command & 0xff)) - return (atkbd->cmdcnt = 0) - 1; + goto out; for (i = 0; i < send; i++) if (atkbd_sendbyte(atkbd, param[i])) - return (atkbd->cmdcnt = 0) - 1; - - while (atkbd->cmdcnt && timeout--) { + goto out; - if (atkbd->cmdcnt == 1 && - command == ATKBD_CMD_RESET_BAT && timeout > 100000) - timeout = 100000; + timeout = wait_event_interruptible_timeout(atkbd->wait, + !test_bit(ATKBD_FLAG_CMD1, &atkbd->flags), timeout); - if (atkbd->cmdcnt == 1 && command == ATKBD_CMD_GETID && - atkbd->cmdbuf[1] != 0xab && atkbd->cmdbuf[1] != 0xac) { + if (atkbd->cmdcnt && timeout > 0) { + if (command == ATKBD_CMD_RESET_BAT && jiffies_to_msecs(timeout) > 100) + timeout = msecs_to_jiffies(100); + + if (command == ATKBD_CMD_GETID && + atkbd->cmdbuf[receive - 1] != 0xab && atkbd->cmdbuf[receive - 1] != 0xac) { + /* + * Device behind the port is not a keyboard + * so we don't need to wait for the 2nd byte + * of ID response. + */ + clear_bit(ATKBD_FLAG_CMD, &atkbd->flags); atkbd->cmdcnt = 0; - break; } - udelay(1); + wait_event_interruptible_timeout(atkbd->wait, + !test_bit(ATKBD_FLAG_CMD, &atkbd->flags), timeout); } if (param) for (i = 0; i < receive; i++) param[i] = atkbd->cmdbuf[(receive - 1) - i]; - if (command == ATKBD_CMD_RESET_BAT && atkbd->cmdcnt == 1) - atkbd->cmdcnt = 0; + if (atkbd->cmdcnt && (command != ATKBD_CMD_RESET_BAT || atkbd->cmdcnt != 1)) + goto out; + + rc = 0; + +out: + clear_bit(ATKBD_FLAG_CMD, &atkbd->flags); + clear_bit(ATKBD_FLAG_CMD1, &atkbd->flags); + up(&atkbd->cmd_sem); + + return rc; +} + +/* + * atkbd_execute_scheduled_command() sends a command, previously scheduled by + * atkbd_schedule_command(), to the keyboard. + */ + +static void atkbd_execute_scheduled_command(void *data) +{ + struct atkbd_work *atkbd_work = data; + + atkbd_command(atkbd_work->atkbd, atkbd_work->param, atkbd_work->command); + + kfree(atkbd_work); +} + +/* + * atkbd_schedule_command() allows to schedule delayed execution of a keyboard + * command and can be used to issue a command from an interrupt or softirq + * context. + */ + +static int atkbd_schedule_command(struct atkbd *atkbd, unsigned char *param, int command) +{ + struct atkbd_work *atkbd_work; + int send = (command >> 12) & 0xf; + int receive = (command >> 8) & 0xf; + + if (!test_bit(ATKBD_FLAG_ENABLED, &atkbd->flags)) + return -1; - if (atkbd->cmdcnt) { - atkbd->cmdcnt = 0; + if (!(atkbd_work = kmalloc(sizeof(struct atkbd_work) + max(send, receive), GFP_ATOMIC))) + return -1; + + memset(atkbd_work, 0, sizeof(struct atkbd_work)); + atkbd_work->atkbd = atkbd; + atkbd_work->command = command; + memcpy(atkbd_work->param, param, send); + INIT_WORK(&atkbd_work->work, atkbd_execute_scheduled_command, atkbd_work); + + if (!schedule_work(&atkbd_work->work)) { + kfree(atkbd_work); return -1; } return 0; } + /* * Event callback from the input module. Events that change the state of * the hardware are processed here. @@ -475,7 +602,7 @@ static int atkbd_event(struct input_dev param[0] = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0) | (test_bit(LED_NUML, dev->led) ? 2 : 0) | (test_bit(LED_CAPSL, dev->led) ? 4 : 0); - atkbd_command(atkbd, param, ATKBD_CMD_SETLEDS); + atkbd_schedule_command(atkbd, param, ATKBD_CMD_SETLEDS); if (atkbd->extra) { param[0] = 0; @@ -484,7 +611,7 @@ static int atkbd_event(struct input_dev | (test_bit(LED_SUSPEND, dev->led) ? 0x04 : 0) | (test_bit(LED_MISC, dev->led) ? 0x10 : 0) | (test_bit(LED_MUTE, dev->led) ? 0x20 : 0); - atkbd_command(atkbd, param, ATKBD_CMD_EX_SETLEDS); + atkbd_schedule_command(atkbd, param, ATKBD_CMD_EX_SETLEDS); } return 0; @@ -500,7 +627,7 @@ static int atkbd_event(struct input_dev dev->rep[REP_PERIOD] = period[i]; dev->rep[REP_DELAY] = delay[j]; param[0] = i | (j << 5); - atkbd_command(atkbd, param, ATKBD_CMD_SETREP); + atkbd_schedule_command(atkbd, param, ATKBD_CMD_SETREP); return 0; } @@ -672,6 +799,11 @@ static void atkbd_cleanup(struct serio * static void atkbd_disconnect(struct serio *serio) { struct atkbd *atkbd = serio->private; + + clear_bit(ATKBD_FLAG_ENABLED, &atkbd->flags); + synchronize_kernel(); + flush_scheduled_work(); + input_unregister_device(&atkbd->dev); serio_close(serio); kfree(atkbd); @@ -684,7 +816,7 @@ static void atkbd_disconnect(struct seri * to the input module. */ -static void atkbd_connect(struct serio *serio, struct serio_dev *dev) +static void atkbd_connect(struct serio *serio, struct serio_driver *drv) { struct atkbd *atkbd; int i; @@ -693,6 +825,9 @@ static void atkbd_connect(struct serio * return; memset(atkbd, 0, sizeof(struct atkbd)); + init_MUTEX(&atkbd->cmd_sem); + init_waitqueue_head(&atkbd->wait); + switch (serio->type & SERIO_TYPE) { case SERIO_8042_XL: @@ -709,17 +844,22 @@ static void atkbd_connect(struct serio * return; } + if (!atkbd->write) + atkbd_softrepeat = 1; + if (atkbd_softrepeat) + atkbd_softraw = 1; + if (atkbd->write) { - atkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP); + atkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP) | BIT(EV_MSC); atkbd->dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL); - } else atkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP); + } else atkbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_MSC); + atkbd->dev.mscbit[0] = atkbd_softraw ? BIT(MSC_SCAN) : BIT(MSC_RAW) | BIT(MSC_SCAN); if (!atkbd_softrepeat) { atkbd->dev.rep[REP_DELAY] = 250; atkbd->dev.rep[REP_PERIOD] = 33; - } + } else atkbd_softraw = 1; - atkbd->ack = 1; atkbd->serio = serio; init_input_dev(&atkbd->dev); @@ -732,7 +872,7 @@ static void atkbd_connect(struct serio * serio->private = atkbd; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(atkbd); return; } @@ -754,8 +894,6 @@ static void atkbd_connect(struct serio * atkbd->id = 0xab00; } - atkbd->enabled = 1; - if (atkbd->extra) { atkbd->dev.ledbit[0] |= BIT(LED_COMPOSE) | BIT(LED_SUSPEND) | BIT(LED_SLEEP) | BIT(LED_MUTE) | BIT(LED_MISC); sprintf(atkbd->name, "AT Set 2 Extra keyboard"); @@ -797,6 +935,8 @@ static void atkbd_connect(struct serio * input_register_device(&atkbd->dev); + set_bit(ATKBD_FLAG_ENABLED, &atkbd->flags); + printk(KERN_INFO "input: %s on %s\n", atkbd->name, serio->phys); } @@ -808,10 +948,10 @@ static void atkbd_connect(struct serio * static int atkbd_reconnect(struct serio *serio) { struct atkbd *atkbd = serio->private; - struct serio_dev *dev = serio->dev; + struct serio_driver *drv = serio->drv; unsigned char param[1]; - if (!dev) { + if (!drv) { printk(KERN_DEBUG "atkbd: reconnect request, but serio is disconnected, ignoring...\n"); return -1; } @@ -832,26 +972,32 @@ static int atkbd_reconnect(struct serio return -1; } + set_bit(ATKBD_FLAG_ENABLED, &atkbd->flags); + return 0; } -static struct serio_dev atkbd_dev = { - .interrupt = atkbd_interrupt, - .connect = atkbd_connect, - .reconnect = atkbd_reconnect, - .disconnect = atkbd_disconnect, - .cleanup = atkbd_cleanup, +static struct serio_driver atkbd_drv = { + .driver = { + .name = "atkbd", + }, + .description = DRIVER_DESC, + .interrupt = atkbd_interrupt, + .connect = atkbd_connect, + .reconnect = atkbd_reconnect, + .disconnect = atkbd_disconnect, + .cleanup = atkbd_cleanup, }; int __init atkbd_init(void) { - serio_register_device(&atkbd_dev); + serio_register_driver(&atkbd_drv); return 0; } void __exit atkbd_exit(void) { - serio_unregister_device(&atkbd_dev); + serio_unregister_driver(&atkbd_drv); } module_init(atkbd_init); --- linux-2.6.9-rc1/drivers/input/keyboard/lkkbd.c 2004-08-24 00:02:20.000000000 -0700 +++ 25/drivers/input/keyboard/lkkbd.c 2004-09-03 00:56:31.546364872 -0700 @@ -76,8 +76,10 @@ #include #include +#define DRIVER_DESC "LK keyboard driver" + MODULE_AUTHOR ("Jan-Benedict Glaw "); -MODULE_DESCRIPTION ("LK keyboard driver"); +MODULE_DESCRIPTION (DRIVER_DESC); MODULE_LICENSE ("GPL"); /* @@ -622,7 +624,7 @@ lkkbd_reinit (void *data) * lkkbd_connect() probes for a LK keyboard and fills the necessary structures. */ static void -lkkbd_connect (struct serio *serio, struct serio_dev *dev) +lkkbd_connect (struct serio *serio, struct serio_driver *drv) { struct lkkbd *lk; int i; @@ -665,7 +667,7 @@ lkkbd_connect (struct serio *serio, stru serio->private = lk; - if (serio_open (serio, dev)) { + if (serio_open (serio, drv)) { kfree (lk); return; } @@ -703,10 +705,14 @@ lkkbd_disconnect (struct serio *serio) kfree (lk); } -static struct serio_dev lkkbd_dev = { - .connect = lkkbd_connect, - .disconnect = lkkbd_disconnect, - .interrupt = lkkbd_interrupt, +static struct serio_driver lkkbd_drv = { + .driver = { + .name = "lkkbd", + }, + .description = DRIVER_DESC, + .connect = lkkbd_connect, + .disconnect = lkkbd_disconnect, + .interrupt = lkkbd_interrupt, }; /* @@ -715,14 +721,14 @@ static struct serio_dev lkkbd_dev = { int __init lkkbd_init (void) { - serio_register_device (&lkkbd_dev); + serio_register_driver(&lkkbd_drv); return 0; } void __exit lkkbd_exit (void) { - serio_unregister_device (&lkkbd_dev); + serio_unregister_driver(&lkkbd_drv); } module_init (lkkbd_init); --- linux-2.6.9-rc1/drivers/input/keyboard/newtonkbd.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/drivers/input/keyboard/newtonkbd.c 2004-09-03 00:56:31.547364720 -0700 @@ -32,8 +32,10 @@ #include #include +#define DRIVER_DESC "Newton keyboard driver" + MODULE_AUTHOR("Justin Cormack "); -MODULE_DESCRIPTION("Newton keyboard driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); #define NKBD_KEY 0x7f @@ -82,7 +84,7 @@ irqreturn_t nkbd_interrupt(struct serio } -void nkbd_connect(struct serio *serio, struct serio_dev *dev) +void nkbd_connect(struct serio *serio, struct serio_driver *drv) { struct nkbd *nkbd; int i; @@ -106,7 +108,7 @@ void nkbd_connect(struct serio *serio, s nkbd->dev.private = nkbd; serio->private = nkbd; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(nkbd); return; } @@ -138,21 +140,25 @@ void nkbd_disconnect(struct serio *serio kfree(nkbd); } -struct serio_dev nkbd_dev = { - .interrupt = nkbd_interrupt, - .connect = nkbd_connect, - .disconnect = nkbd_disconnect +struct serio_driver nkbd_drv = { + .driver = { + .name = "newtonkbd", + }, + .description = DRIVER_DESC, + .interrupt = nkbd_interrupt, + .connect = nkbd_connect, + .disconnect = nkbd_disconnect, }; int __init nkbd_init(void) { - serio_register_device(&nkbd_dev); + serio_register_driver(&nkbd_drv); return 0; } void __exit nkbd_exit(void) { - serio_unregister_device(&nkbd_dev); + serio_unregister_driver(&nkbd_drv); } module_init(nkbd_init); --- linux-2.6.9-rc1/drivers/input/keyboard/sunkbd.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/drivers/input/keyboard/sunkbd.c 2004-09-03 00:56:31.548364568 -0700 @@ -37,8 +37,10 @@ #include #include +#define DRIVER_DESC "Sun keyboard driver" + MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("Sun keyboard driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); static unsigned char sunkbd_keycode[128] = { @@ -221,7 +223,7 @@ static void sunkbd_reinit(void *data) * sunkbd_connect() probes for a Sun keyboard and fills the necessary structures. */ -static void sunkbd_connect(struct serio *serio, struct serio_dev *dev) +static void sunkbd_connect(struct serio *serio, struct serio_driver *drv) { struct sunkbd *sunkbd; int i; @@ -257,7 +259,7 @@ static void sunkbd_connect(struct serio serio->private = sunkbd; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(sunkbd); return; } @@ -301,10 +303,14 @@ static void sunkbd_disconnect(struct ser kfree(sunkbd); } -static struct serio_dev sunkbd_dev = { - .interrupt = sunkbd_interrupt, - .connect = sunkbd_connect, - .disconnect = sunkbd_disconnect +static struct serio_driver sunkbd_drv = { + .driver = { + .name = "sunkbd", + }, + .description = DRIVER_DESC, + .interrupt = sunkbd_interrupt, + .connect = sunkbd_connect, + .disconnect = sunkbd_disconnect, }; /* @@ -313,13 +319,13 @@ static struct serio_dev sunkbd_dev = { int __init sunkbd_init(void) { - serio_register_device(&sunkbd_dev); + serio_register_driver(&sunkbd_drv); return 0; } void __exit sunkbd_exit(void) { - serio_unregister_device(&sunkbd_dev); + serio_unregister_driver(&sunkbd_drv); } module_init(sunkbd_init); --- linux-2.6.9-rc1/drivers/input/keyboard/xtkbd.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/drivers/input/keyboard/xtkbd.c 2004-09-03 00:56:31.548364568 -0700 @@ -34,8 +34,10 @@ #include #include +#define DRIVER_DESC "XT keyboard driver" + MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("XT keyboard driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); #define XTKBD_EMUL0 0xe0 @@ -86,7 +88,7 @@ irqreturn_t xtkbd_interrupt(struct serio return IRQ_HANDLED; } -void xtkbd_connect(struct serio *serio, struct serio_dev *dev) +void xtkbd_connect(struct serio *serio, struct serio_driver *drv) { struct xtkbd *xtkbd; int i; @@ -111,7 +113,7 @@ void xtkbd_connect(struct serio *serio, serio->private = xtkbd; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(xtkbd); return; } @@ -143,21 +145,25 @@ void xtkbd_disconnect(struct serio *seri kfree(xtkbd); } -struct serio_dev xtkbd_dev = { - .interrupt = xtkbd_interrupt, - .connect = xtkbd_connect, - .disconnect = xtkbd_disconnect +struct serio_driver xtkbd_drv = { + .driver = { + .name = "xtkbd", + }, + .description = DRIVER_DESC, + .interrupt = xtkbd_interrupt, + .connect = xtkbd_connect, + .disconnect = xtkbd_disconnect, }; int __init xtkbd_init(void) { - serio_register_device(&xtkbd_dev); + serio_register_driver(&xtkbd_drv); return 0; } void __exit xtkbd_exit(void) { - serio_unregister_device(&xtkbd_dev); + serio_unregister_driver(&xtkbd_drv); } module_init(xtkbd_init); --- linux-2.6.9-rc1/drivers/input/misc/Kconfig 2004-08-24 00:02:32.000000000 -0700 +++ 25/drivers/input/misc/Kconfig 2004-09-03 00:56:31.548364568 -0700 @@ -14,7 +14,7 @@ config INPUT_MISC config INPUT_PCSPKR tristate "PC Speaker support" - depends on (ALPHA || X86 || X86_64 || MIPS) && INPUT && INPUT_MISC + depends on (ALPHA || X86 || X86_64 || MIPS || PPC_PREP || PPC_CHRP || PPC_PSERIES) && INPUT && INPUT_MISC help Say Y here if you want the standard PC Speaker to be used for bells and whistles. --- linux-2.6.9-rc1/drivers/input/misc/uinput.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/input/misc/uinput.c 2004-09-03 00:56:31.549364416 -0700 @@ -279,6 +279,9 @@ static unsigned int uinput_poll(struct f { struct uinput_device *udev = file->private_data; + if (!test_bit(UIST_CREATED, &(udev->state))) + return 0; + poll_wait(file, &udev->waitq, wait); if (udev->head != udev->tail) --- linux-2.6.9-rc1/drivers/input/mousedev.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/drivers/input/mousedev.c 2004-09-03 00:56:31.558363048 -0700 @@ -48,8 +48,15 @@ static int yres = CONFIG_INPUT_MOUSEDEV_ module_param(yres, uint, 0); MODULE_PARM_DESC(yres, "Vertical screen resolution"); -struct mousedev_motion { +static unsigned tap_time = 200; +module_param(tap_time, uint, 0); +MODULE_PARM_DESC(tap_time, "Tap time for touchpads in absolute mode (msecs)"); + +struct mousedev_hw_data { int dx, dy, dz; + int x, y; + int abs_event; + unsigned long buttons; }; struct mousedev { @@ -61,22 +68,38 @@ struct mousedev { struct list_head list; struct input_handle handle; - struct mousedev_motion packet; - unsigned long buttons; + struct mousedev_hw_data packet; unsigned int pkt_count; int old_x[4], old_y[4]; - unsigned int touch; + unsigned long touch; }; +enum mousedev_emul { + MOUSEDEV_EMUL_PS2, + MOUSEDEV_EMUL_IMPS, + MOUSEDEV_EMUL_EXPS +}; + +struct mousedev_motion { + int dx, dy, dz; + unsigned long buttons; +}; + +#define PACKET_QUEUE_LEN 16 struct mousedev_list { struct fasync_struct *fasync; struct mousedev *mousedev; struct list_head node; - int dx, dy, dz; - unsigned long buttons; + + struct mousedev_motion packets[PACKET_QUEUE_LEN]; + unsigned int head, tail; + spinlock_t packet_lock; + int pos_x, pos_y; + signed char ps2[6]; unsigned char ready, buffer, bufsiz; - unsigned char mode, imexseq, impsseq; + unsigned char imexseq, impsseq; + enum mousedev_emul mode; }; #define MOUSEDEV_SEQ_LEN 6 @@ -119,15 +142,19 @@ static void mousedev_abs_event(struct in case ABS_X: size = dev->absmax[ABS_X] - dev->absmin[ABS_X]; if (size == 0) size = xres; - mousedev->packet.dx = (value * xres - mousedev->old_x[0]) / size; - mousedev->old_x[0] = mousedev->packet.dx * size; + if (value > dev->absmax[ABS_X]) value = dev->absmax[ABS_X]; + if (value < dev->absmin[ABS_X]) value = dev->absmin[ABS_X]; + mousedev->packet.x = ((value - dev->absmin[ABS_X]) * xres) / size; + mousedev->packet.abs_event = 1; break; case ABS_Y: size = dev->absmax[ABS_Y] - dev->absmin[ABS_Y]; if (size == 0) size = yres; - mousedev->packet.dy = (value * yres - mousedev->old_y[0]) / size; - mousedev->old_y[0] = mousedev->packet.dy * size; + if (value > dev->absmax[ABS_Y]) value = dev->absmax[ABS_Y]; + if (value < dev->absmin[ABS_Y]) value = dev->absmin[ABS_Y]; + mousedev->packet.y = yres - ((value - dev->absmin[ABS_Y]) * yres) / size; + mousedev->packet.abs_event = 1; break; } } @@ -165,30 +192,82 @@ static void mousedev_key_event(struct mo } if (value) { - set_bit(index, &mousedev->buttons); - set_bit(index, &mousedev_mix.buttons); + set_bit(index, &mousedev->packet.buttons); + set_bit(index, &mousedev_mix.packet.buttons); } else { - clear_bit(index, &mousedev->buttons); - clear_bit(index, &mousedev_mix.buttons); + clear_bit(index, &mousedev->packet.buttons); + clear_bit(index, &mousedev_mix.packet.buttons); } } -static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_motion *packet) +static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_hw_data *packet) { struct mousedev_list *list; + struct mousedev_motion *p; + unsigned long flags; list_for_each_entry(list, &mousedev->list, node) { - list->dx += packet->dx; - list->dy += packet->dy; - list->dz += packet->dz; - list->buttons = mousedev->buttons; + spin_lock_irqsave(&list->packet_lock, flags); + + p = &list->packets[list->head]; + if (list->ready && p->buttons != packet->buttons) { + unsigned int new_head = (list->head + 1) % PACKET_QUEUE_LEN; + if (new_head != list->tail) { + p = &list->packets[list->head = new_head]; + memset(p, 0, sizeof(struct mousedev_motion)); + } + } + + if (packet->abs_event) { + p->dx += packet->x - list->pos_x; + p->dy += packet->y - list->pos_y; + list->pos_x = packet->x; + list->pos_y = packet->y; + } + + list->pos_x += packet->dx; + list->pos_x = list->pos_x < 0 ? 0 : (list->pos_x >= xres ? xres : list->pos_x); + list->pos_y += packet->dy; + list->pos_y = list->pos_y < 0 ? 0 : (list->pos_y >= yres ? yres : list->pos_y); + + p->dx += packet->dx; + p->dy += packet->dy; + p->dz += packet->dz; + p->buttons = mousedev->packet.buttons; + list->ready = 1; + + spin_unlock_irqrestore(&list->packet_lock, flags); kill_fasync(&list->fasync, SIGIO, POLL_IN); } wake_up_interruptible(&mousedev->wait); } +static void mousedev_touchpad_touch(struct mousedev *mousedev, int value) +{ + if (!value) { + if (mousedev->touch && + time_before(jiffies, mousedev->touch + msecs_to_jiffies(tap_time))) { + /* + * Toggle left button to emulate tap. + * We rely on the fact that mousedev_mix always has 0 + * motion packet so we won't mess current position. + */ + set_bit(0, &mousedev->packet.buttons); + set_bit(0, &mousedev_mix.packet.buttons); + mousedev_notify_readers(mousedev, &mousedev_mix.packet); + mousedev_notify_readers(&mousedev_mix, &mousedev_mix.packet); + clear_bit(0, &mousedev->packet.buttons); + clear_bit(0, &mousedev_mix.packet.buttons); + } + mousedev->touch = mousedev->pkt_count = 0; + } + else + if (!mousedev->touch) + mousedev->touch = jiffies; +} + static void mousedev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { struct mousedev *mousedev = handle->private; @@ -212,12 +291,8 @@ static void mousedev_event(struct input_ case EV_KEY: if (value != 2) { - if (code == BTN_TOUCH && test_bit(BTN_TOOL_FINGER, handle->dev->keybit)) { - /* Handle touchpad data */ - mousedev->touch = value; - if (!mousedev->touch) - mousedev->pkt_count = 0; - } + if (code == BTN_TOUCH && test_bit(BTN_TOOL_FINGER, handle->dev->keybit)) + mousedev_touchpad_touch(mousedev, value); else mousedev_key_event(mousedev, code, value); } @@ -237,7 +312,8 @@ static void mousedev_event(struct input_ mousedev_notify_readers(mousedev, &mousedev->packet); mousedev_notify_readers(&mousedev_mix, &mousedev->packet); - memset(&mousedev->packet, 0, sizeof(struct mousedev_motion)); + mousedev->packet.dx = mousedev->packet.dy = mousedev->packet.dz = 0; + mousedev->packet.abs_event = 0; } break; } @@ -322,6 +398,9 @@ static int mousedev_open(struct inode * return -ENOMEM; memset(list, 0, sizeof(struct mousedev_list)); + spin_lock_init(&list->packet_lock); + list->pos_x = xres / 2; + list->pos_y = yres / 2; list->mousedev = mousedev_table[i]; list_add_tail(&list->node, &mousedev_table[i]->list); file->private_data = list; @@ -341,32 +420,56 @@ static int mousedev_open(struct inode * return 0; } -static void mousedev_packet(struct mousedev_list *list, unsigned char off) +static inline int mousedev_limit_delta(int delta, int limit) { - list->ps2[off] = 0x08 | ((list->dx < 0) << 4) | ((list->dy < 0) << 5) | (list->buttons & 0x07); - list->ps2[off + 1] = (list->dx > 127 ? 127 : (list->dx < -127 ? -127 : list->dx)); - list->ps2[off + 2] = (list->dy > 127 ? 127 : (list->dy < -127 ? -127 : list->dy)); - list->dx -= list->ps2[off + 1]; - list->dy -= list->ps2[off + 2]; - list->bufsiz = off + 3; - - if (list->mode == 2) { - list->ps2[off + 3] = (list->dz > 7 ? 7 : (list->dz < -7 ? -7 : list->dz)); - list->dz -= list->ps2[off + 3]; - list->ps2[off + 3] = (list->ps2[off + 3] & 0x0f) | ((list->buttons & 0x18) << 1); - list->bufsiz++; - } else { - list->ps2[off] |= ((list->buttons & 0x10) >> 3) | ((list->buttons & 0x08) >> 1); + return delta > limit ? limit : (delta < -limit ? -limit : delta); +} + +static void mousedev_packet(struct mousedev_list *list, signed char *ps2_data) +{ + struct mousedev_motion *p; + unsigned long flags; + + spin_lock_irqsave(&list->packet_lock, flags); + p = &list->packets[list->tail]; + + ps2_data[0] = 0x08 | ((p->dx < 0) << 4) | ((p->dy < 0) << 5) | (p->buttons & 0x07); + ps2_data[1] = mousedev_limit_delta(p->dx, 127); + ps2_data[2] = mousedev_limit_delta(p->dy, 127); + p->dx -= ps2_data[1]; + p->dy -= ps2_data[2]; + + switch (list->mode) { + case MOUSEDEV_EMUL_EXPS: + ps2_data[3] = mousedev_limit_delta(p->dz, 127); + p->dz -= ps2_data[3]; + ps2_data[3] = (ps2_data[3] & 0x0f) | ((p->buttons & 0x18) << 1); + list->bufsiz = 4; + break; + + case MOUSEDEV_EMUL_IMPS: + ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1); + ps2_data[3] = mousedev_limit_delta(p->dz, 127); + p->dz -= ps2_data[3]; + list->bufsiz = 4; + break; + + case MOUSEDEV_EMUL_PS2: + default: + ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1); + p->dz = 0; + list->bufsiz = 3; + break; } - if (list->mode == 1) { - list->ps2[off + 3] = (list->dz > 127 ? 127 : (list->dz < -127 ? -127 : list->dz)); - list->dz -= list->ps2[off + 3]; - list->bufsiz++; + if (!p->dx && !p->dy && !p->dz) { + if (list->tail != list->head) + list->tail = (list->tail + 1) % PACKET_QUEUE_LEN; + if (list->tail == list->head) + list->ready = 0; } - if (!list->dx && !list->dy && (!list->mode || !list->dz)) list->ready = 0; - list->buffer = list->bufsiz; + spin_unlock_irqrestore(&list->packet_lock, flags); } @@ -384,31 +487,31 @@ static ssize_t mousedev_write(struct fil if (c == mousedev_imex_seq[list->imexseq]) { if (++list->imexseq == MOUSEDEV_SEQ_LEN) { list->imexseq = 0; - list->mode = 2; + list->mode = MOUSEDEV_EMUL_EXPS; } } else list->imexseq = 0; if (c == mousedev_imps_seq[list->impsseq]) { if (++list->impsseq == MOUSEDEV_SEQ_LEN) { list->impsseq = 0; - list->mode = 1; + list->mode = MOUSEDEV_EMUL_IMPS; } } else list->impsseq = 0; list->ps2[0] = 0xfa; - list->bufsiz = 1; switch (c) { case 0xeb: /* Poll */ - mousedev_packet(list, 1); + mousedev_packet(list, &list->ps2[1]); + list->bufsiz++; /* account for leading ACK */ break; case 0xf2: /* Get ID */ switch (list->mode) { - case 0: list->ps2[1] = 0; break; - case 1: list->ps2[1] = 3; break; - case 2: list->ps2[1] = 4; break; + case MOUSEDEV_EMUL_PS2: list->ps2[1] = 0; break; + case MOUSEDEV_EMUL_IMPS: list->ps2[1] = 3; break; + case MOUSEDEV_EMUL_EXPS: list->ps2[1] = 4; break; } list->bufsiz = 2; break; @@ -419,13 +522,15 @@ static ssize_t mousedev_write(struct fil break; case 0xff: /* Reset */ - list->impsseq = 0; - list->imexseq = 0; - list->mode = 0; - list->ps2[1] = 0xaa; - list->ps2[2] = 0x00; + list->impsseq = list->imexseq = 0; + list->mode = MOUSEDEV_EMUL_PS2; + list->ps2[1] = 0xaa; list->ps2[2] = 0x00; list->bufsiz = 3; break; + + default: + list->bufsiz = 1; + break; } list->buffer = list->bufsiz; @@ -446,13 +551,19 @@ static ssize_t mousedev_read(struct file if (!list->ready && !list->buffer && (file->f_flags & O_NONBLOCK)) return -EAGAIN; - retval = wait_event_interruptible(list->mousedev->wait, list->ready || list->buffer); + retval = wait_event_interruptible(list->mousedev->wait, + !list->mousedev->exist || list->ready || list->buffer); if (retval) return retval; - if (!list->buffer && list->ready) - mousedev_packet(list, 0); + if (!list->mousedev->exist) + return -ENODEV; + + if (!list->buffer && list->ready) { + mousedev_packet(list, list->ps2); + list->buffer = list->bufsiz; + } if (count > list->buffer) count = list->buffer; --- linux-2.6.9-rc1/drivers/input/mouse/Kconfig 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/input/mouse/Kconfig 2004-09-03 00:56:31.549364416 -0700 @@ -30,8 +30,6 @@ config MOUSE_PS2 and a new verion of GPM at: http://www.geocities.com/dt_or/gpm/gpm.html to take advantage of the advanced features of the touchpad. - If you do not want install specialized drivers but want tapping - working please use option psmouse.proto=imps. If unsure, say Y. --- linux-2.6.9-rc1/drivers/input/mouse/logips2pp.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/drivers/input/mouse/logips2pp.c 2004-09-03 00:56:31.550364264 -0700 @@ -277,7 +277,7 @@ int ps2pp_init(struct psmouse *psmouse, protocol = PSMOUSE_PS2TPP; } - } else if (get_model_info(model) != NULL) { + } else if (model_info != NULL) { param[0] = param[1] = param[2] = 0; ps2pp_cmd(psmouse, param, 0x39); /* Magic knock */ --- linux-2.6.9-rc1/drivers/input/mouse/psmouse-base.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/input/mouse/psmouse-base.c 2004-09-03 00:56:31.552363960 -0700 @@ -22,8 +22,10 @@ #include "synaptics.h" #include "logips2pp.h" +#define DRIVER_DESC "PS/2 mouse driver" + MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("PS/2 mouse driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); static char *psmouse_proto; @@ -142,37 +144,67 @@ static irqreturn_t psmouse_interrupt(str printk(KERN_WARNING "psmouse.c: bad data from KBC -%s%s\n", flags & SERIO_TIMEOUT ? " timeout" : "", flags & SERIO_PARITY ? " bad parity" : ""); - if (psmouse->acking) { - psmouse->ack = -1; - psmouse->acking = 0; - } - psmouse->pktcnt = 0; + psmouse->nak = 1; + clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags); + clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags); + wake_up_interruptible(&psmouse->wait); goto out; } - if (psmouse->acking) { + if (test_bit(PSMOUSE_FLAG_ACK, &psmouse->flags)) { switch (data) { case PSMOUSE_RET_ACK: - psmouse->ack = 1; + psmouse->nak = 0; break; + case PSMOUSE_RET_NAK: - psmouse->ack = -1; + psmouse->nak = 1; break; + + /* + * Workaround for mice which don't ACK the Get ID command. + * These are valid mouse IDs that we recognize. + */ + case 0x00: + case 0x03: + case 0x04: + if (test_bit(PSMOUSE_FLAG_WAITID, &psmouse->flags)) { + psmouse->nak = 0; + break; + } + /* Fall through */ default: - psmouse->ack = 1; /* Workaround for mice which don't ACK the Get ID command */ - if (psmouse->cmdcnt) - psmouse->cmdbuf[--psmouse->cmdcnt] = data; - break; + goto out; } - psmouse->acking = 0; - goto out; + + if (!psmouse->nak && psmouse->cmdcnt) { + set_bit(PSMOUSE_FLAG_CMD, &psmouse->flags); + set_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags); + } + clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags); + wake_up_interruptible(&psmouse->wait); + + if (data == PSMOUSE_RET_ACK || data == PSMOUSE_RET_NAK) + goto out; } - if (psmouse->cmdcnt) { - psmouse->cmdbuf[--psmouse->cmdcnt] = data; + if (test_bit(PSMOUSE_FLAG_CMD, &psmouse->flags)) { + if (psmouse->cmdcnt) + psmouse->cmdbuf[--psmouse->cmdcnt] = data; + + if (test_and_clear_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags) && psmouse->cmdcnt) + wake_up_interruptible(&psmouse->wait); + + if (!psmouse->cmdcnt) { + clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags); + wake_up_interruptible(&psmouse->wait); + } goto out; } + if (psmouse->state == PSMOUSE_INITIALIZING) + goto out; + if (psmouse->state == PSMOUSE_ACTIVATED && psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) { printk(KERN_WARNING "psmouse.c: %s at %s lost synchronization, throwing %d bytes away.\n", @@ -238,78 +270,96 @@ out: * psmouse_sendbyte() sends a byte to the mouse, and waits for acknowledge. * It doesn't handle retransmission, though it could - because when there would * be need for retransmissions, the mouse has to be replaced anyway. + * + * psmouse_sendbyte() can only be called from a process context */ static int psmouse_sendbyte(struct psmouse *psmouse, unsigned char byte) { - int timeout = 10000; /* 100 msec */ - psmouse->ack = 0; - psmouse->acking = 1; - - if (serio_write(psmouse->serio, byte)) { - psmouse->acking = 0; - return -1; - } + psmouse->nak = 1; + set_bit(PSMOUSE_FLAG_ACK, &psmouse->flags); - while (!psmouse->ack && timeout--) udelay(10); + if (serio_write(psmouse->serio, byte) == 0) + wait_event_interruptible_timeout(psmouse->wait, + !test_bit(PSMOUSE_FLAG_ACK, &psmouse->flags), + msecs_to_jiffies(200)); - return -(psmouse->ack <= 0); + clear_bit(PSMOUSE_FLAG_ACK, &psmouse->flags); + return -psmouse->nak; } /* * psmouse_command() sends a command and its parameters to the mouse, * then waits for the response and puts it in the param array. + * + * psmouse_command() can only be called from a process context */ int psmouse_command(struct psmouse *psmouse, unsigned char *param, int command) { - int timeout = 500000; /* 500 msec */ + int timeout; int send = (command >> 12) & 0xf; int receive = (command >> 8) & 0xf; + int rc = -1; int i; - psmouse->cmdcnt = receive; + timeout = msecs_to_jiffies(command == PSMOUSE_CMD_RESET_BAT ? 4000 : 500); - if (command == PSMOUSE_CMD_RESET_BAT) - timeout = 4000000; /* 4 sec */ + clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags); + if (command == PSMOUSE_CMD_GETID) + set_bit(PSMOUSE_FLAG_WAITID, &psmouse->flags); + + if (receive && param) + for (i = 0; i < receive; i++) + psmouse->cmdbuf[(receive - 1) - i] = param[i]; - /* initialize cmdbuf with preset values from param */ - if (receive) - for (i = 0; i < receive; i++) - psmouse->cmdbuf[(receive - 1) - i] = param[i]; + psmouse->cmdcnt = receive; if (command & 0xff) if (psmouse_sendbyte(psmouse, command & 0xff)) - return (psmouse->cmdcnt = 0) - 1; + goto out; for (i = 0; i < send; i++) if (psmouse_sendbyte(psmouse, param[i])) - return (psmouse->cmdcnt = 0) - 1; - - while (psmouse->cmdcnt && timeout--) { + goto out; - if (psmouse->cmdcnt == 1 && command == PSMOUSE_CMD_RESET_BAT && - timeout > 100000) /* do not run in a endless loop */ - timeout = 100000; /* 1 sec */ + timeout = wait_event_interruptible_timeout(psmouse->wait, + !test_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags), timeout); - if (psmouse->cmdcnt == 1 && command == PSMOUSE_CMD_GETID && - psmouse->cmdbuf[1] != 0xab && psmouse->cmdbuf[1] != 0xac) { + if (psmouse->cmdcnt && timeout > 0) { + if (command == PSMOUSE_CMD_RESET_BAT && jiffies_to_msecs(timeout) > 100) + timeout = msecs_to_jiffies(100); + + if (command == PSMOUSE_CMD_GETID && + psmouse->cmdbuf[receive - 1] != 0xab && psmouse->cmdbuf[receive - 1] != 0xac) { + /* + * Device behind the port is not a keyboard + * so we don't need to wait for the 2nd byte + * of ID response. + */ + clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags); psmouse->cmdcnt = 0; - break; } - udelay(1); + wait_event_interruptible_timeout(psmouse->wait, + !test_bit(PSMOUSE_FLAG_CMD, &psmouse->flags), timeout); } - for (i = 0; i < receive; i++) - param[i] = psmouse->cmdbuf[(receive - 1) - i]; + if (param) + for (i = 0; i < receive; i++) + param[i] = psmouse->cmdbuf[(receive - 1) - i]; - if (psmouse->cmdcnt) - return (psmouse->cmdcnt = 0) - 1; + if (psmouse->cmdcnt && (command != PSMOUSE_CMD_RESET_BAT || psmouse->cmdcnt != 1)) + goto out; - return 0; -} + rc = 0; +out: + clear_bit(PSMOUSE_FLAG_CMD, &psmouse->flags); + clear_bit(PSMOUSE_FLAG_CMD1, &psmouse->flags); + clear_bit(PSMOUSE_FLAG_WAITID, &psmouse->flags); + return rc; +} /* * psmouse_sliced_command() sends an extended PS/2 command to the mouse @@ -394,6 +444,8 @@ static int im_explorer_detect(struct psm { unsigned char param[2]; + intellimouse_detect(psmouse); + param[0] = 200; psmouse_command(psmouse, param, PSMOUSE_CMD_SETRATE); param[0] = 200; @@ -598,6 +650,21 @@ static void psmouse_initialize(struct ps } /* + * psmouse_set_state() sets new psmouse state and resets all flags and + * counters while holding serio lock so fighting with interrupt handler + * is not a concern. + */ + +static void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) +{ + serio_pause_rx(psmouse->serio); + psmouse->state = new_state; + psmouse->pktcnt = psmouse->cmdcnt = psmouse->out_of_sync = 0; + psmouse->flags = 0; + serio_continue_rx(psmouse->serio); +} + +/* * psmouse_activate() enables the mouse so that we get motion reports from it. */ @@ -606,9 +673,24 @@ static void psmouse_activate(struct psmo if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_ENABLE)) printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n", psmouse->serio->phys); - psmouse->state = PSMOUSE_ACTIVATED; + psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); } + +/* + * psmouse_deactivate() puts the mouse into poll mode so that we don't get motion + * reports from it unless we explicitely request it. + */ + +static void psmouse_deactivate(struct psmouse *psmouse) +{ + if (psmouse_command(psmouse, NULL, PSMOUSE_CMD_DISABLE)) + printk(KERN_WARNING "psmouse.c: Failed to deactivate mouse on %s\n", psmouse->serio->phys); + + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); +} + + /* * psmouse_cleanup() resets the mouse into power-on state. */ @@ -626,22 +708,21 @@ static void psmouse_cleanup(struct serio static void psmouse_disconnect(struct serio *serio) { - struct psmouse *psmouse = serio->private; + struct psmouse *psmouse, *parent; - psmouse->state = PSMOUSE_CMD_MODE; + psmouse = serio->private; + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); - if (psmouse->ptport) { - if (psmouse->ptport->deactivate) - psmouse->ptport->deactivate(psmouse); - __serio_unregister_port(&psmouse->ptport->serio); /* we have serio_sem */ - kfree(psmouse->ptport); - psmouse->ptport = NULL; + if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU) { + parent = serio->parent->private; + if (parent->pt_deactivate) + parent->pt_deactivate(parent); } if (psmouse->disconnect) psmouse->disconnect(psmouse); - psmouse->state = PSMOUSE_IGNORE; + psmouse_set_state(psmouse, PSMOUSE_IGNORE); input_unregister_device(&psmouse->dev); serio_close(serio); @@ -652,39 +733,49 @@ static void psmouse_disconnect(struct se * psmouse_connect() is a callback from the serio module when * an unhandled serio port is found. */ -static void psmouse_connect(struct serio *serio, struct serio_dev *dev) +static void psmouse_connect(struct serio *serio, struct serio_driver *drv) { - struct psmouse *psmouse; + struct psmouse *psmouse, *parent = NULL; if ((serio->type & SERIO_TYPE) != SERIO_8042 && (serio->type & SERIO_TYPE) != SERIO_PS_PSTHRU) return; + /* + * If this is a pass-through port deactivate parent so the device + * connected to this port can be successfully identified + */ + if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU) { + parent = serio->parent->private; + psmouse_deactivate(parent); + } + if (!(psmouse = kmalloc(sizeof(struct psmouse), GFP_KERNEL))) - return; + goto out; memset(psmouse, 0, sizeof(struct psmouse)); + init_waitqueue_head(&psmouse->wait); init_input_dev(&psmouse->dev); psmouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); psmouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); psmouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y); - psmouse->state = PSMOUSE_CMD_MODE; psmouse->serio = serio; psmouse->dev.private = psmouse; + psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); serio->private = psmouse; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(psmouse); serio->private = NULL; - return; + goto out; } if (psmouse_probe(psmouse) < 0) { serio_close(serio); kfree(psmouse); serio->private = NULL; - return; + goto out; } psmouse->type = psmouse_extensions(psmouse, psmouse_max_proto, 1); @@ -711,63 +802,88 @@ static void psmouse_connect(struct serio printk(KERN_INFO "input: %s on %s\n", psmouse->devname, serio->phys); + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + psmouse_initialize(psmouse); - if (psmouse->ptport) { - printk(KERN_INFO "serio: %s port at %s\n", psmouse->ptport->serio.name, psmouse->phys); - __serio_register_port(&psmouse->ptport->serio); /* we have serio_sem */ - if (psmouse->ptport->activate) - psmouse->ptport->activate(psmouse); + if (parent && parent->pt_activate) + parent->pt_activate(parent); + + if (serio->child) { + /* + * Nothing to be done here, serio core will detect that + * the driver set serio->child and will register it for us. + */ + printk(KERN_INFO "serio: %s port at %s\n", serio->child->name, psmouse->phys); } psmouse_activate(psmouse); + +out: + /* If this is a pass-through port the parent awaits to be activated */ + if (parent) + psmouse_activate(parent); } static int psmouse_reconnect(struct serio *serio) { struct psmouse *psmouse = serio->private; - struct serio_dev *dev = serio->dev; + struct psmouse *parent = NULL; + struct serio_driver *drv = serio->drv; + int rc = -1; - if (!dev || !psmouse) { + if (!drv || !psmouse) { printk(KERN_DEBUG "psmouse: reconnect request, but serio is disconnected, ignoring...\n"); return -1; } - psmouse->state = PSMOUSE_CMD_MODE; - psmouse->acking = psmouse->cmdcnt = psmouse->pktcnt = psmouse->out_of_sync = 0; + if (serio->parent && (serio->type & SERIO_TYPE) == SERIO_PS_PSTHRU) { + parent = serio->parent->private; + psmouse_deactivate(parent); + } + + psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); + if (psmouse->reconnect) { if (psmouse->reconnect(psmouse)) - return -1; + goto out; } else if (psmouse_probe(psmouse) < 0 || psmouse->type != psmouse_extensions(psmouse, psmouse_max_proto, 0)) - return -1; + goto out; /* ok, the device type (and capabilities) match the old one, * we can continue using it, complete intialization */ + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + psmouse_initialize(psmouse); - if (psmouse->ptport) { - if (psmouse_reconnect(&psmouse->ptport->serio)) { - __serio_unregister_port(&psmouse->ptport->serio); - __serio_register_port(&psmouse->ptport->serio); - if (psmouse->ptport->activate) - psmouse->ptport->activate(psmouse); - } - } + if (parent && parent->pt_activate) + parent->pt_activate(parent); psmouse_activate(psmouse); - return 0; + rc = 0; + +out: + /* If this is a pass-through port the parent waits to be activated */ + if (parent) + psmouse_activate(parent); + + return rc; } -static struct serio_dev psmouse_dev = { - .interrupt = psmouse_interrupt, - .connect = psmouse_connect, - .reconnect = psmouse_reconnect, - .disconnect = psmouse_disconnect, - .cleanup = psmouse_cleanup, +static struct serio_driver psmouse_drv = { + .driver = { + .name = "psmouse", + }, + .description = DRIVER_DESC, + .interrupt = psmouse_interrupt, + .connect = psmouse_connect, + .reconnect = psmouse_reconnect, + .disconnect = psmouse_disconnect, + .cleanup = psmouse_cleanup, }; static inline void psmouse_parse_proto(void) @@ -787,13 +903,13 @@ static inline void psmouse_parse_proto(v int __init psmouse_init(void) { psmouse_parse_proto(); - serio_register_device(&psmouse_dev); + serio_register_driver(&psmouse_drv); return 0; } void __exit psmouse_exit(void) { - serio_unregister_device(&psmouse_dev); + serio_unregister_driver(&psmouse_drv); } module_init(psmouse_init); --- linux-2.6.9-rc1/drivers/input/mouse/psmouse.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/input/mouse/psmouse.h 2004-09-03 00:56:31.553363808 -0700 @@ -9,6 +9,7 @@ #define PSMOUSE_CMD_GETID 0x02f2 #define PSMOUSE_CMD_SETRATE 0x10f3 #define PSMOUSE_CMD_ENABLE 0x00f4 +#define PSMOUSE_CMD_DISABLE 0x00f5 #define PSMOUSE_CMD_RESET_DIS 0x00f6 #define PSMOUSE_CMD_RESET_BAT 0x02ff @@ -17,10 +18,17 @@ #define PSMOUSE_RET_ACK 0xfa #define PSMOUSE_RET_NAK 0xfe -/* psmouse states */ -#define PSMOUSE_CMD_MODE 0 -#define PSMOUSE_ACTIVATED 1 -#define PSMOUSE_IGNORE 2 +#define PSMOUSE_FLAG_ACK 0 /* Waiting for ACK/NAK */ +#define PSMOUSE_FLAG_CMD 1 /* Waiting for command to finish */ +#define PSMOUSE_FLAG_CMD1 2 /* Waiting for the first byte of command response */ +#define PSMOUSE_FLAG_WAITID 3 /* Command execiting is GET ID */ + +enum psmouse_state { + PSMOUSE_IGNORE, + PSMOUSE_INITIALIZING, + PSMOUSE_CMD_MODE, + PSMOUSE_ACTIVATED, +}; /* psmouse protocol handler return codes */ typedef enum { @@ -29,20 +37,10 @@ typedef enum { PSMOUSE_FULL_PACKET } psmouse_ret_t; -struct psmouse; - -struct psmouse_ptport { - struct serio serio; - - void (*activate)(struct psmouse *parent); - void (*deactivate)(struct psmouse *parent); -}; - struct psmouse { void *private; struct input_dev dev; struct serio *serio; - struct psmouse_ptport *ptport; char *vendor; char *name; unsigned char cmdbuf[8]; @@ -53,16 +51,22 @@ struct psmouse { unsigned char model; unsigned long last; unsigned long out_of_sync; - unsigned char state; - char acking; - volatile char ack; + enum psmouse_state state; + unsigned char nak; char error; char devname[64]; char phys[32]; + unsigned long flags; + + /* Used to signal completion from interrupt handler */ + wait_queue_head_t wait; - psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse, struct pt_regs *regs); + psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse, struct pt_regs *regs); int (*reconnect)(struct psmouse *psmouse); void (*disconnect)(struct psmouse *psmouse); + + void (*pt_activate)(struct psmouse *psmouse); + void (*pt_deactivate)(struct psmouse *psmouse); }; #define PSMOUSE_PS2 1 --- linux-2.6.9-rc1/drivers/input/mouse/sermouse.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/drivers/input/mouse/sermouse.c 2004-09-03 00:56:31.554363656 -0700 @@ -37,8 +37,10 @@ #include #include +#define DRIVER_DESC "Serial mouse driver" + MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("Serial mouse driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); static char *sermouse_protocols[] = { "None", "Mouse Systems Mouse", "Sun Mouse", "Microsoft Mouse", @@ -237,7 +239,7 @@ static void sermouse_disconnect(struct s * an unhandled serio port is found. */ -static void sermouse_connect(struct serio *serio, struct serio_dev *dev) +static void sermouse_connect(struct serio *serio, struct serio_driver *drv) { struct sermouse *sermouse; unsigned char c; @@ -279,7 +281,7 @@ static void sermouse_connect(struct seri sermouse->dev.id.product = c; sermouse->dev.id.version = 0x0100; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(sermouse); return; } @@ -289,21 +291,25 @@ static void sermouse_connect(struct seri printk(KERN_INFO "input: %s on %s\n", sermouse_protocols[sermouse->type], serio->phys); } -static struct serio_dev sermouse_dev = { - .interrupt = sermouse_interrupt, - .connect = sermouse_connect, - .disconnect = sermouse_disconnect +static struct serio_driver sermouse_drv = { + .driver = { + .name = "sermouse", + }, + .description = DRIVER_DESC, + .interrupt = sermouse_interrupt, + .connect = sermouse_connect, + .disconnect = sermouse_disconnect, }; int __init sermouse_init(void) { - serio_register_device(&sermouse_dev); + serio_register_driver(&sermouse_drv); return 0; } void __exit sermouse_exit(void) { - serio_unregister_device(&sermouse_dev); + serio_unregister_driver(&sermouse_drv); } module_init(sermouse_init); --- linux-2.6.9-rc1/drivers/input/mouse/synaptics.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/drivers/input/mouse/synaptics.c 2004-09-03 00:56:31.555363504 -0700 @@ -212,9 +212,9 @@ static int synaptics_set_mode(struct psm /***************************************************************************** * Synaptics pass-through PS/2 port support ****************************************************************************/ -static int synaptics_pt_write(struct serio *port, unsigned char c) +static int synaptics_pt_write(struct serio *serio, unsigned char c) { - struct psmouse *parent = port->driver; + struct psmouse *parent = serio->parent->private; char rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */ if (psmouse_sliced_command(parent, c)) @@ -233,22 +233,19 @@ static void synaptics_pass_pt_packet(str { struct psmouse *child = ptport->private; - if (child) { - if (child->state == PSMOUSE_ACTIVATED) { - serio_interrupt(ptport, packet[1], 0, NULL); - serio_interrupt(ptport, packet[4], 0, NULL); - serio_interrupt(ptport, packet[5], 0, NULL); - if (child->type >= PSMOUSE_GENPS) - serio_interrupt(ptport, packet[2], 0, NULL); - } else if (child->state != PSMOUSE_IGNORE) { - serio_interrupt(ptport, packet[1], 0, NULL); - } - } + if (child && child->state == PSMOUSE_ACTIVATED) { + serio_interrupt(ptport, packet[1], 0, NULL); + serio_interrupt(ptport, packet[4], 0, NULL); + serio_interrupt(ptport, packet[5], 0, NULL); + if (child->type >= PSMOUSE_GENPS) + serio_interrupt(ptport, packet[2], 0, NULL); + } else + serio_interrupt(ptport, packet[1], 0, NULL); } static void synaptics_pt_activate(struct psmouse *psmouse) { - struct psmouse *child = psmouse->ptport->serio.private; + struct psmouse *child = psmouse->serio->child->private; /* adjust the touchpad to child's choice of protocol */ if (child && child->type >= PSMOUSE_GENPS) { @@ -259,23 +256,25 @@ static void synaptics_pt_activate(struct static void synaptics_pt_create(struct psmouse *psmouse) { - struct psmouse_ptport *port; + struct serio *serio; - psmouse->ptport = port = kmalloc(sizeof(struct psmouse_ptport), GFP_KERNEL); - if (!port) { + serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (!serio) { printk(KERN_ERR "synaptics: not enough memory to allocate pass-through port\n"); return; } - memset(port, 0, sizeof(struct psmouse_ptport)); + memset(serio, 0, sizeof(struct serio)); + + serio->type = SERIO_PS_PSTHRU; + strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name)); + strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name)); + serio->write = synaptics_pt_write; + serio->parent = psmouse->serio; - port->serio.type = SERIO_PS_PSTHRU; - port->serio.name = "Synaptics pass-through"; - port->serio.phys = "synaptics-pt/serio0"; - port->serio.write = synaptics_pt_write; - port->serio.driver = psmouse; + psmouse->pt_activate = synaptics_pt_activate; - port->activate = synaptics_pt_activate; + psmouse->serio->child = serio; } /***************************************************************************** @@ -470,9 +469,10 @@ static psmouse_ret_t synaptics_process_b if (unlikely(priv->pkt_type == SYN_NEWABS)) priv->pkt_type = synaptics_detect_pkt_type(psmouse); - if (psmouse->ptport && psmouse->ptport->serio.dev && synaptics_is_pt_packet(psmouse->packet)) - synaptics_pass_pt_packet(&psmouse->ptport->serio, psmouse->packet); - else + if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) { + if (psmouse->serio->child) + synaptics_pass_pt_packet(psmouse->serio->child, psmouse->packet); + } else synaptics_process_packet(psmouse); return PSMOUSE_FULL_PACKET; --- linux-2.6.9-rc1/drivers/input/mouse/vsxxxaa.c 2004-08-24 00:03:49.000000000 -0700 +++ 25/drivers/input/mouse/vsxxxaa.c 2004-09-03 00:57:08.972675208 -0700 @@ -1,11 +1,14 @@ /* - * DEC VSXXX-AA and VSXXX-GA mouse driver. + * Driver for DEC VSXXX-AA mouse (hockey-puck mouse, ball or two rollers) + * DEC VSXXX-GA mouse (rectangular mouse, with ball) + * DEC VSXXX-AB tablet (digitizer with hair cross or stylus) * * Copyright (C) 2003-2004 by Jan-Benedict Glaw * - * The packet format was taken from a patch to GPM which is (C) 2001 + * The packet format was initially taken from a patch to GPM which is (C) 2001 * by Karsten Merker * and Maciej W. Rozycki + * Later on, I had access to the device's documentation (referenced below). */ /* @@ -25,7 +28,7 @@ */ /* - * Building an adaptor to DB9 / DB25 RS232 + * Building an adaptor to DE9 / DB25 RS232 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * DISCLAIMER: Use this description AT YOUR OWN RISK! I'll not pay for @@ -43,7 +46,7 @@ * \ 2 1 / * ------- * - * DEC socket DB9 DB25 Note + * DEC socket DE9 DB25 Note * 1 (GND) 5 7 - * 2 (RxD) 2 3 - * 3 (TxD) 3 2 - @@ -82,8 +85,10 @@ #include #include +#define DRIVER_DESC "Driver for DEC VSXXX-AA and -GA mice and VSXXX-AB tablet" + MODULE_AUTHOR ("Jan-Benedict Glaw "); -MODULE_DESCRIPTION ("Serial DEC VSXXX-AA/GA mouse / DEC tablet driver"); +MODULE_DESCRIPTION (DRIVER_DESC); MODULE_LICENSE ("GPL"); #undef VSXXXAA_DEBUG @@ -102,7 +107,7 @@ MODULE_LICENSE ("GPL"); #define VSXXXAA_PACKET_REL 0x80 #define VSXXXAA_PACKET_ABS 0xc0 #define VSXXXAA_PACKET_POR 0xa0 -#define MATCH_PACKET_TYPE(data, type) (((data) & VSXXXAA_PACKET_MASK) == type) +#define MATCH_PACKET_TYPE(data, type) (((data) & VSXXXAA_PACKET_MASK) == (type)) @@ -148,7 +153,7 @@ vsxxxaa_detection_done (struct vsxxxaa * { switch (mouse->type) { case 0x02: - sprintf (mouse->name, "DEC VSXXX-AA/GA mouse"); + sprintf (mouse->name, "DEC VSXXX-AA/-GA mouse"); break; case 0x04: @@ -156,7 +161,8 @@ vsxxxaa_detection_done (struct vsxxxaa * break; default: - sprintf (mouse->name, "unknown DEC pointer device"); + sprintf (mouse->name, "unknown DEC pointer device " + "(type = 0x%02x)", mouse->type); break; } @@ -334,13 +340,10 @@ vsxxxaa_handle_POR_packet (struct vsxxxa * * M: manufacturer location code * R: revision code - * E: Error code. I'm not sure about these, but gpm's sources, - * which support this mouse, too, tell about them: - * E = [0x00 .. 0x1f]: no error, byte #3 is button state - * E = 0x3d: button error, byte #3 tells which one. - * E = : other error + * E: Error code. If it's in the range of 0x00..0x1f, only some + * minor problem occured. Errors >= 0x20 are considered bad + * and the device may not work properly... * D: <0010> == mouse, <0100> == tablet - * */ mouse->version = buf[0] & 0x0f; @@ -361,28 +364,32 @@ vsxxxaa_handle_POR_packet (struct vsxxxa vsxxxaa_detection_done (mouse); if (error <= 0x1f) { - /* No error. Report buttons */ + /* No (serious) error. Report buttons */ input_regs (dev, regs); input_report_key (dev, BTN_LEFT, left); input_report_key (dev, BTN_MIDDLE, middle); input_report_key (dev, BTN_RIGHT, right); input_report_key (dev, BTN_TOUCH, 0); input_sync (dev); - } else { - printk (KERN_ERR "Your %s on %s reports an undefined error, " - "please check it...\n", mouse->name, - mouse->phys); + + if (error != 0) + printk (KERN_INFO "Your %s on %s reports error=0x%02x\n", + mouse->name, mouse->phys, error); + } /* * If the mouse was hot-plugged, we need to force differential mode * now... However, give it a second to recover from it's reset. */ - printk (KERN_NOTICE "%s on %s: Forceing standard packet format and " - "streaming mode\n", mouse->name, mouse->phys); - mouse->serio->write (mouse->serio, 'S'); + printk (KERN_NOTICE "%s on %s: Forceing standard packet format, " + "incremental streaming mode and 72 samples/sec\n", + mouse->name, mouse->phys); + mouse->serio->write (mouse->serio, 'S'); /* Standard format */ + mdelay (50); + mouse->serio->write (mouse->serio, 'R'); /* Incremental */ mdelay (50); - mouse->serio->write (mouse->serio, 'R'); + mouse->serio->write (mouse->serio, 'L'); /* 72 samples/sec */ } static void @@ -482,7 +489,7 @@ vsxxxaa_disconnect (struct serio *serio) } static void -vsxxxaa_connect (struct serio *serio, struct serio_dev *dev) +vsxxxaa_connect (struct serio *serio, struct serio_driver *drv) { struct vsxxxaa *mouse; @@ -517,14 +524,14 @@ vsxxxaa_connect (struct serio *serio, st mouse->dev.private = mouse; serio->private = mouse; - sprintf (mouse->name, "DEC VSXXX-AA/GA mouse or VSXXX-AB digitizer"); + sprintf (mouse->name, "DEC VSXXX-AA/-GA mouse or VSXXX-AB digitizer"); sprintf (mouse->phys, "%s/input0", serio->phys); mouse->dev.name = mouse->name; mouse->dev.phys = mouse->phys; mouse->dev.id.bustype = BUS_RS232; mouse->serio = serio; - if (serio_open (serio, dev)) { + if (serio_open (serio, drv)) { kfree (mouse); return; } @@ -540,23 +547,27 @@ vsxxxaa_connect (struct serio *serio, st printk (KERN_INFO "input: %s on %s\n", mouse->name, mouse->phys); } -static struct serio_dev vsxxxaa_dev = { - .connect = vsxxxaa_connect, - .interrupt = vsxxxaa_interrupt, - .disconnect = vsxxxaa_disconnect, +static struct serio_driver vsxxxaa_drv = { + .driver = { + .name = "vsxxxaa", + }, + .description = DRIVER_DESC, + .connect = vsxxxaa_connect, + .interrupt = vsxxxaa_interrupt, + .disconnect = vsxxxaa_disconnect, }; int __init vsxxxaa_init (void) { - serio_register_device (&vsxxxaa_dev); + serio_register_driver(&vsxxxaa_drv); return 0; } void __exit vsxxxaa_exit (void) { - serio_unregister_device (&vsxxxaa_dev); + serio_unregister_driver(&vsxxxaa_drv); } module_init (vsxxxaa_init); --- linux-2.6.9-rc1/drivers/input/serio/ambakmi.c 2004-08-24 00:03:12.000000000 -0700 +++ 25/drivers/input/serio/ambakmi.c 2004-09-03 00:56:31.560362744 -0700 @@ -29,7 +29,7 @@ #define KMI_BASE (kmi->base) struct amba_kmi_port { - struct serio io; + struct serio *io; struct clk *clk; unsigned char *base; unsigned int irq; @@ -44,7 +44,7 @@ static irqreturn_t amba_kmi_int(int irq, int handled = IRQ_NONE; while (status & KMIIR_RXINTR) { - serio_interrupt(&kmi->io, readb(KMIDATA), 0, regs); + serio_interrupt(kmi->io, readb(KMIDATA), 0, regs); status = readb(KMIIR); handled = IRQ_HANDLED; } @@ -54,7 +54,7 @@ static irqreturn_t amba_kmi_int(int irq, static int amba_kmi_write(struct serio *io, unsigned char val) { - struct amba_kmi_port *kmi = io->driver; + struct amba_kmi_port *kmi = io->port_data; unsigned int timeleft = 10000; /* timeout in 100ms */ while ((readb(KMISTAT) & KMISTAT_TXEMPTY) == 0 && timeleft--) @@ -68,7 +68,7 @@ static int amba_kmi_write(struct serio * static int amba_kmi_open(struct serio *io) { - struct amba_kmi_port *kmi = io->driver; + struct amba_kmi_port *kmi = io->port_data; unsigned int divisor; int ret; @@ -105,7 +105,7 @@ static int amba_kmi_open(struct serio *i static void amba_kmi_close(struct serio *io) { - struct amba_kmi_port *kmi = io->driver; + struct amba_kmi_port *kmi = io->port_data; writeb(0, KMICR); @@ -117,6 +117,7 @@ static void amba_kmi_close(struct serio static int amba_kmi_probe(struct amba_device *dev, void *id) { struct amba_kmi_port *kmi; + struct serio *io; int ret; ret = amba_request_regions(dev, NULL); @@ -124,21 +125,25 @@ static int amba_kmi_probe(struct amba_de return ret; kmi = kmalloc(sizeof(struct amba_kmi_port), GFP_KERNEL); - if (!kmi) { + io = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (!kmi || !io) { ret = -ENOMEM; goto out; } memset(kmi, 0, sizeof(struct amba_kmi_port)); + memset(io, 0, sizeof(struct serio)); - kmi->io.type = SERIO_8042; - kmi->io.write = amba_kmi_write; - kmi->io.open = amba_kmi_open; - kmi->io.close = amba_kmi_close; - kmi->io.name = dev->dev.bus_id; - kmi->io.phys = dev->dev.bus_id; - kmi->io.driver = kmi; + io->type = SERIO_8042; + io->write = amba_kmi_write; + io->open = amba_kmi_open; + io->close = amba_kmi_close; + strlcpy(io->name, dev->dev.bus_id, sizeof(io->name)); + strlcpy(io->phys, dev->dev.bus_id, sizeof(io->phys)); + io->port_data = kmi; + io->dev.parent = &dev->dev; + kmi->io = io; kmi->base = ioremap(dev->res.start, KMI_SIZE); if (!kmi->base) { ret = -ENOMEM; @@ -154,13 +159,14 @@ static int amba_kmi_probe(struct amba_de kmi->irq = dev->irq[0]; amba_set_drvdata(dev, kmi); - serio_register_port(&kmi->io); + serio_register_port(kmi->io); return 0; unmap: iounmap(kmi->base); out: kfree(kmi); + kfree(io); amba_release_regions(dev); return ret; } @@ -171,7 +177,7 @@ static int amba_kmi_remove(struct amba_d amba_set_drvdata(dev, NULL); - serio_unregister_port(&kmi->io); + serio_unregister_port(kmi->io); clk_put(kmi->clk); iounmap(kmi->base); kfree(kmi); @@ -184,7 +190,7 @@ static int amba_kmi_resume(struct amba_d struct amba_kmi_port *kmi = amba_get_drvdata(dev); /* kick the serio layer to rescan this port */ - serio_rescan(&kmi->io); + serio_reconnect(kmi->io); return 0; } @@ -214,7 +220,7 @@ static int __init amba_kmi_init(void) static void __exit amba_kmi_exit(void) { - return amba_driver_unregister(&ambakmi_driver); + amba_driver_unregister(&ambakmi_driver); } module_init(amba_kmi_init); --- linux-2.6.9-rc1/drivers/input/serio/ct82c710.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/drivers/input/serio/ct82c710.c 2004-09-03 00:56:31.561362592 -0700 @@ -36,6 +36,7 @@ #include #include #include +#include #include @@ -43,9 +44,6 @@ MODULE_AUTHOR("Vojtech Pavlik type = SERIO_8042; + serio->open = ct82c710_open; + serio->close = ct82c710_close; + serio->write = ct82c710_write; + serio->dev.parent = &ct82c710_device->dev; + strlcpy(serio->name, "C&T 82c710 mouse port", sizeof(serio->name)); + snprintf(serio->phys, sizeof(serio->phys), "isa%04lx/serio0", CT82C710_DATA); + } + + return serio; +} + int __init ct82c710_init(void) { if (ct82c710_probe()) return -ENODEV; - if (request_region(ct82c710_data, 2, "ct82c710")) - return -EBUSY; - - sprintf(ct82c710_phys, "isa%04x/serio0", ct82c710_data); + ct82c710_device = platform_device_register_simple("ct82c710", -1, &ct82c710_iores, 1); + if (IS_ERR(ct82c710_device)) + return PTR_ERR(ct82c710_device); + + if (!(ct82c710_port = ct82c710_allocate_port())) { + platform_device_unregister(ct82c710_device); + return -ENOMEM; + } - serio_register_port(&ct82c710_port); + serio_register_port(ct82c710_port); - printk(KERN_INFO "serio: C&T 82c710 mouse port at %#x irq %d\n", - ct82c710_data, CT82C710_IRQ); + printk(KERN_INFO "serio: C&T 82c710 mouse port at %#lx irq %d\n", + CT82C710_DATA, CT82C710_IRQ); return 0; } void __exit ct82c710_exit(void) { - serio_unregister_port(&ct82c710_port); - release_region(ct82c710_data, 2); + serio_unregister_port(ct82c710_port); + platform_device_unregister(ct82c710_device); } module_init(ct82c710_init); --- linux-2.6.9-rc1/drivers/input/serio/gscps2.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/input/serio/gscps2.c 2004-09-03 00:56:31.562362440 -0700 @@ -6,7 +6,7 @@ * Copyright (c) 2002 Thibaut Varene * * Pieces of code based on linux-2.4's hp_mouse.c & hp_keyb.c - * Copyright (c) 1999 Alex deVries + * Copyright (c) 1999 Alex deVries * Copyright (c) 1999-2000 Philipp Rumpf * Copyright (c) 2000 Xavier Debacker * Copyright (c) 2000-2001 Thomas Marteau @@ -91,7 +91,7 @@ static irqreturn_t gscps2_interrupt(int struct gscps2port { struct list_head node; struct parisc_device *padev; - struct serio port; + struct serio *port; spinlock_t lock; char *addr; u8 act, append; /* position in buffer[] */ @@ -100,7 +100,6 @@ struct gscps2port { u8 str; } buffer[BUFFER_SIZE+1]; int id; - char name[32]; }; /* @@ -272,7 +271,7 @@ static irqreturn_t gscps2_interrupt(int rxflags = ((status & GSC_STAT_TERR) ? SERIO_TIMEOUT : 0 ) | ((status & GSC_STAT_PERR) ? SERIO_PARITY : 0 ); - serio_interrupt(&ps2port->port, data, rxflags, regs); + serio_interrupt(ps2port->port, data, rxflags, regs); } /* while() */ @@ -288,7 +287,7 @@ static irqreturn_t gscps2_interrupt(int static int gscps2_write(struct serio *port, unsigned char data) { - struct gscps2port *ps2port = port->driver; + struct gscps2port *ps2port = port->port_data; if (!gscps2_writeb_output(ps2port, data)) { printk(KERN_DEBUG PFX "sending byte %#x failed.\n", data); @@ -304,7 +303,7 @@ static int gscps2_write(struct serio *po static int gscps2_open(struct serio *port) { - struct gscps2port *ps2port = port->driver; + struct gscps2port *ps2port = port->port_data; gscps2_reset(ps2port); @@ -319,7 +318,7 @@ static int gscps2_open(struct serio *por static void gscps2_close(struct serio *port) { - struct gscps2port *ps2port = port->driver; + struct gscps2port *ps2port = port->port_data; gscps2_enable(ps2port, DISABLE); } @@ -343,7 +342,8 @@ static struct serio gscps2_serio_port = static int __init gscps2_probe(struct parisc_device *dev) { - struct gscps2port *ps2port; + struct gscps2port *ps2port; + struct serio *serio; unsigned long hpa = dev->hpa; int ret; @@ -355,34 +355,45 @@ static int __init gscps2_probe(struct pa hpa += GSC_DINO_OFFSET; ps2port = kmalloc(sizeof(struct gscps2port), GFP_KERNEL); - if (!ps2port) - return -ENOMEM; + serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (!ps2port || !serio) { + ret = -ENOMEM; + goto fail_nomem; + } dev_set_drvdata(&dev->dev, ps2port); memset(ps2port, 0, sizeof(struct gscps2port)); + memset(serio, 0, sizeof(struct serio)); + ps2port->port = serio; ps2port->padev = dev; ps2port->addr = ioremap(hpa, GSC_STATUS + 4); spin_lock_init(&ps2port->lock); gscps2_reset(ps2port); - ps2port->id = readb(ps2port->addr+GSC_ID) & 0x0f; - snprintf(ps2port->name, sizeof(ps2port->name)-1, "%s %s", - gscps2_serio_port.name, - (ps2port->id == GSC_ID_KEYBOARD) ? "keyboard" : "mouse" ); - - memcpy(&ps2port->port, &gscps2_serio_port, sizeof(gscps2_serio_port)); - ps2port->port.driver = ps2port; - ps2port->port.name = ps2port->name; - ps2port->port.phys = dev->dev.bus_id; + ps2port->id = readb(ps2port->addr + GSC_ID) & 0x0f; + + snprintf(serio->name, sizeof(serio->name), "GSC PS/2 %s", + (ps2port->id == GSC_ID_KEYBOARD) ? "keyboard" : "mouse"); + strlcpy(serio->phys, dev->dev.bus_id, sizeof(serio->phys)); + serio->idbus = BUS_GSC; + serio->idvendor = PCI_VENDOR_ID_HP; + serio->idproduct = 0x0001; + serio->idversion = 0x0010; + serio->type = SERIO_8042; + serio->write = gscps2_write; + serio->open = gscps2_open; + serio->close = gscps2_close; + serio->port_data = ps2port; + serio->dev.parent = &dev->dev; list_add_tail(&ps2port->node, &ps2port_list); ret = -EBUSY; - if (request_irq(dev->irq, gscps2_interrupt, SA_SHIRQ, ps2port->name, ps2port)) + if (request_irq(dev->irq, gscps2_interrupt, SA_SHIRQ, ps2port->port->name, ps2port)) goto fail_miserably; - if ( (ps2port->id != GSC_ID_KEYBOARD) && (ps2port->id != GSC_ID_MOUSE) ) { + if (ps2port->id != GSC_ID_KEYBOARD && ps2port->id != GSC_ID_MOUSE) { printk(KERN_WARNING PFX "Unsupported PS/2 port at 0x%08lx (id=%d) ignored\n", hpa, ps2port->id); ret = -ENODEV; @@ -395,12 +406,12 @@ static int __init gscps2_probe(struct pa #endif printk(KERN_INFO "serio: %s port at 0x%p irq %d @ %s\n", - ps2port->name, + ps2port->port->name, ps2port->addr, ps2port->padev->irq, - ps2port->port.phys); + ps2port->port->phys); - serio_register_port(&ps2port->port); + serio_register_port(ps2port->port); return 0; @@ -411,7 +422,10 @@ fail_miserably: list_del(&ps2port->node); iounmap(ps2port->addr); release_mem_region(dev->hpa, GSC_STATUS + 4); + +fail_nomem: kfree(ps2port); + kfree(serio); return ret; } @@ -424,7 +438,7 @@ static int __devexit gscps2_remove(struc { struct gscps2port *ps2port = dev_get_drvdata(&dev->dev); - serio_unregister_port(&ps2port->port); + serio_unregister_port(ps2port->port); free_irq(dev->irq, ps2port); gscps2_flush(ps2port); list_del(&ps2port->node); --- linux-2.6.9-rc1/drivers/input/serio/i8042.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/input/serio/i8042.c 2004-09-03 00:56:54.110934536 -0700 @@ -1,7 +1,7 @@ /* * i8042 keyboard and mouse controller driver for Linux * - * Copyright (c) 1999-2002 Vojtech Pavlik + * Copyright (c) 1999-2004 Vojtech Pavlik */ /* @@ -21,6 +21,17 @@ #include #include #include +#include +#include + +#ifdef CONFIG_ACPI +#include +#include + +static int no_acpi; +module_param(no_acpi, uint, 0); +MODULE_PARM_DESC(no_acpi, "Disable ACPI keyboard/aux controller detection"); +#endif #include @@ -52,6 +63,10 @@ static unsigned int i8042_dumbkbd; module_param_named(dumbkbd, i8042_dumbkbd, bool, 0); MODULE_PARM_DESC(dumbkbd, "Pretend that controller can only read data from keyboard"); +static unsigned int i8042_noloop; +module_param_named(noloop, i8042_noloop, bool, 0); +MODULE_PARM_DESC(dumbkbd, "Disable the AUX Loopback command while probing for the AUX port"); + __obsolete_setup("i8042_noaux"); __obsolete_setup("i8042_nomux"); __obsolete_setup("i8042_unlock"); @@ -70,19 +85,35 @@ struct i8042_values { unsigned char irqen; unsigned char exists; signed char mux; - unsigned char *name; - unsigned char *phys; + char name[8]; +}; + +static struct i8042_values i8042_kbd_values = { + .disable = I8042_CTR_KBDDIS, + .irqen = I8042_CTR_KBDINT, + .mux = -1, + .name = "KBD", +}; + +static struct i8042_values i8042_aux_values = { + .disable = I8042_CTR_AUXDIS, + .irqen = I8042_CTR_AUXINT, + .mux = -1, + .name = "AUX", }; -static struct serio i8042_kbd_port; -static struct serio i8042_aux_port; +static struct i8042_values i8042_mux_values[I8042_NUM_MUX_PORTS]; + +static struct serio *i8042_kbd_port; +static struct serio *i8042_aux_port; +static struct serio *i8042_mux_port[I8042_NUM_MUX_PORTS]; static unsigned char i8042_initial_ctr; static unsigned char i8042_ctr; static unsigned char i8042_mux_open; static unsigned char i8042_mux_present; -static unsigned char i8042_sysdev_initialized; static struct pm_dev *i8042_pm_dev; -struct timer_list i8042_timer; +static struct timer_list i8042_timer; +static struct platform_device *i8042_platform_device; /* * Shared IRQ's require a device pointer, but this driver doesn't support @@ -95,6 +126,7 @@ static irqreturn_t i8042_interrupt(int i /* * The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to * be ready for reading values from it / writing values to it. + * Called always with i8042_lock held. */ static int i8042_wait_read(void) @@ -131,6 +163,7 @@ static int i8042_flush(void) spin_lock_irqsave(&i8042_lock, flags); while ((i8042_read_status() & I8042_STR_OBF) && (i++ < I8042_BUFFER_SIZE)) { + udelay(50); data = i8042_read_data(); dbg("%02x <- i8042 (flush, %s)", data, i8042_read_status() & I8042_STR_AUXDATA ? "aux" : "kbd"); @@ -154,6 +187,9 @@ static int i8042_command(unsigned char * unsigned long flags; int retval = 0, i = 0; + if (i8042_noloop && command == I8042_CMD_AUX_LOOP) + return -1; + spin_lock_irqsave(&i8042_lock, flags); retval = i8042_wait_write(); @@ -214,7 +250,7 @@ static int i8042_kbd_write(struct serio static int i8042_aux_write(struct serio *port, unsigned char c) { - struct i8042_values *values = port->driver; + struct i8042_values *values = port->port_data; int retval; /* @@ -242,7 +278,7 @@ static int i8042_aux_write(struct serio static int i8042_activate_port(struct serio *port) { - struct i8042_values *values = port->driver; + struct i8042_values *values = port->port_data; i8042_flush(); @@ -270,7 +306,7 @@ static int i8042_activate_port(struct se static int i8042_open(struct serio *port) { - struct i8042_values *values = port->driver; + struct i8042_values *values = port->port_data; if (values->mux != -1) if (i8042_mux_open++) @@ -309,7 +345,7 @@ irq_fail: static void i8042_close(struct serio *port) { - struct i8042_values *values = port->driver; + struct i8042_values *values = port->port_data; if (values->mux != -1) if (--i8042_mux_open) @@ -328,52 +364,6 @@ static void i8042_close(struct serio *po } /* - * Structures for registering the devices in the serio.c module. - */ - -static struct i8042_values i8042_kbd_values = { - .irqen = I8042_CTR_KBDINT, - .disable = I8042_CTR_KBDDIS, - .name = "KBD", - .mux = -1, -}; - -static struct serio i8042_kbd_port = -{ - .type = SERIO_8042_XL, - .write = i8042_kbd_write, - .open = i8042_open, - .close = i8042_close, - .driver = &i8042_kbd_values, - .name = "i8042 Kbd Port", - .phys = I8042_KBD_PHYS_DESC, -}; - -static struct i8042_values i8042_aux_values = { - .irqen = I8042_CTR_AUXINT, - .disable = I8042_CTR_AUXDIS, - .name = "AUX", - .mux = -1, -}; - -static struct serio i8042_aux_port = -{ - .type = SERIO_8042, - .write = i8042_aux_write, - .open = i8042_open, - .close = i8042_close, - .driver = &i8042_aux_values, - .name = "i8042 Aux Port", - .phys = I8042_AUX_PHYS_DESC, -}; - -static struct i8042_values i8042_mux_values[4]; -static struct serio i8042_mux_port[4]; -static char i8042_mux_names[4][32]; -static char i8042_mux_short[4][16]; -static char i8042_mux_phys[4][32]; - -/* * i8042_interrupt() is the most important function in this driver - * it handles the interrupts from the i8042, and sends incoming bytes * to the upper layers. @@ -384,6 +374,7 @@ static irqreturn_t i8042_interrupt(int i unsigned long flags; unsigned char str, data = 0; unsigned int dfl; + unsigned int aux_idx; int ret; mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD); @@ -400,44 +391,67 @@ static irqreturn_t i8042_interrupt(int i goto out; } - dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) | - ((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0); - - if (i8042_mux_values[0].exists && (str & I8042_STR_AUXDATA)) { + if (i8042_mux_present && (str & I8042_STR_AUXDATA)) { + static unsigned long last_transmit; + static unsigned char last_str; + dfl = 0; if (str & I8042_STR_MUXERR) { + dbg("MUX error, status is %02x, data is %02x", str, data); switch (data) { + default: +/* + * When MUXERR condition is signalled the data register can only contain + * 0xfd, 0xfe or 0xff if implementation follows the spec. Unfortunately + * it is not always the case. Some KBC just get confused which port the + * data came from and signal error leaving the data intact. They _do not_ + * revert to legacy mode (actually I've never seen KBC reverting to legacy + * mode yet, when we see one we'll add proper handling). + * Anyway, we will assume that the data came from the same serio last byte + * was transmitted (if transmission happened not too long ago). + */ + if (time_before(jiffies, last_transmit + HZ/10)) { + str = last_str; + break; + } + /* fall through - report timeout */ case 0xfd: - case 0xfe: dfl = SERIO_TIMEOUT; break; - case 0xff: dfl = SERIO_PARITY; break; + case 0xfe: dfl = SERIO_TIMEOUT; data = 0xfe; break; + case 0xff: dfl = SERIO_PARITY; data = 0xfe; break; } - data = 0xfe; - } else dfl = 0; + } + + aux_idx = (str >> 6) & 3; dbg("%02x <- i8042 (interrupt, aux%d, %d%s%s)", - data, (str >> 6), irq, + data, aux_idx, irq, dfl & SERIO_PARITY ? ", bad parity" : "", dfl & SERIO_TIMEOUT ? ", timeout" : ""); - serio_interrupt(i8042_mux_port + ((str >> 6) & 3), data, dfl, regs); + if (likely(i8042_mux_values[aux_idx].exists)) + serio_interrupt(i8042_mux_port[aux_idx], data, dfl, regs); + last_str = str; + last_transmit = jiffies; goto irq_ret; } + dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) | + ((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0); + dbg("%02x <- i8042 (interrupt, %s, %d%s%s)", data, (str & I8042_STR_AUXDATA) ? "aux" : "kbd", irq, dfl & SERIO_PARITY ? ", bad parity" : "", dfl & SERIO_TIMEOUT ? ", timeout" : ""); - if (i8042_aux_values.exists && (str & I8042_STR_AUXDATA)) { - serio_interrupt(&i8042_aux_port, data, dfl, regs); - goto irq_ret; - } - - if (!i8042_kbd_values.exists) - goto irq_ret; - serio_interrupt(&i8042_kbd_port, data, dfl, regs); + if (str & I8042_STR_AUXDATA) { + if (likely(i8042_aux_values.exists)) + serio_interrupt(i8042_aux_port, data, dfl, regs); + } else { + if (likely(i8042_kbd_values.exists)) + serio_interrupt(i8042_kbd_port, data, dfl, regs); + } irq_ret: ret = 1; @@ -474,17 +488,8 @@ static int i8042_enable_mux_mode(struct if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != 0xa9) return -1; param = 0xa4; - if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param == 0x5b) { - -/* - * Do another loop test with the 0x5a value. Doing anything else upsets - * Profusion/ServerWorks OSB4 chipsets. - */ - - param = 0x5a; - i8042_command(¶m, I8042_CMD_AUX_LOOP); + if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param == 0x5b) return -1; - } if (mux_version) *mux_version = ~param; @@ -639,8 +644,10 @@ static int __init i8042_check_aux(struct * registers it, and reports to the user. */ -static int __init i8042_port_register(struct i8042_values *values, struct serio *port) +static int __init i8042_port_register(struct serio *port) { + struct i8042_values *values = port->port_data; + values->exists = 1; i8042_ctr &= ~values->disable; @@ -669,6 +676,70 @@ static void i8042_timer_func(unsigned lo } +static int i8042_spank_usb(void) +{ + struct pci_dev *usb = NULL; + int found = 0; + u16 word; + unsigned long addr; + unsigned long len; + int i; + + while ((usb = pci_get_class((PCI_CLASS_SERIAL_USB << 8), usb)) != NULL) + { + /* UHCI controller not in legacy ? */ + + pci_read_config_word(usb, 0xC0, &word); + if(word & 0x2000) + continue; + + /* + * Check it is enabled. If the port is active in legacy mode + * then this will be mapped already + */ + + for (i = 0; i < PCI_ROM_RESOURCE; i++) { + if (!(pci_resource_flags (usb, i) & IORESOURCE_IO)) + continue; + } + if (i == PCI_ROM_RESOURCE) + continue; + + /* + * Retrieve the bits + */ + + addr = pci_resource_start(usb, i); + len = pci_resource_len (usb, i); + + /* + * Check its configured and not in use + */ + if (addr == 0) + continue; + if (request_region(addr, len, "usb-whackamole")) + continue; + + /* + * Kick the problem controller out of legacy mode + * so things like the E750x don't break + */ + + outw(0, addr + 4); /* IRQ Mask */ + outw(4, addr); /* Reset */ + msleep(20); + outw(0, addr); + + msleep(20); + /* Now take if off the BIOS */ + pci_write_config_word(usb, 0xC0, 0x2000); + release_region(addr, len); + + found = 1; + } + return found; +} + /* * i8042_controller init initializes the i8042 controller, and, * most importantly, sets it into non-xlated mode if that's @@ -677,6 +748,8 @@ static void i8042_timer_func(unsigned lo static int i8042_controller_init(void) { + unsigned long flags; + int tries = 0; /* * Test the i8042. We need to know if it thinks it's working correctly @@ -705,9 +778,16 @@ static int i8042_controller_init(void) * Save the CTR for restoral on unload / reboot. */ - if (i8042_command(&i8042_ctr, I8042_CMD_CTL_RCTR)) { - printk(KERN_ERR "i8042.c: Can't read CTR while initializing i8042.\n"); - return -1; + while (i8042_command(&i8042_ctr, I8042_CMD_CTL_RCTR)) { + if (tries > 3 || !i8042_spank_usb()) { + printk(KERN_ERR "i8042.c: Can't read CTR while " + "initializing i8042.\n"); + return -1; + } + printk(KERN_WARNING "i8042.c: Can't read CTR, disabling USB " + "legacy and retrying.\n"); + i8042_flush(); + tries++; } i8042_initial_ctr = i8042_ctr; @@ -723,12 +803,14 @@ static int i8042_controller_init(void) * Handle keylock. */ + spin_lock_irqsave(&i8042_lock, flags); if (~i8042_read_status() & I8042_STR_KEYLOCK) { if (i8042_unlock) i8042_ctr |= I8042_CTR_IGNKEYLOCK; else printk(KERN_WARNING "i8042.c: Warning: Keylock active.\n"); } + spin_unlock_irqrestore(&i8042_lock, flags); /* * If the chip is configured into nontranslated mode by the BIOS, don't @@ -745,10 +827,8 @@ static int i8042_controller_init(void) * BIOSes. */ - if (i8042_direct) { + if (i8042_direct) i8042_ctr &= ~I8042_CTR_XLATE; - i8042_kbd_port.type = SERIO_8042; - } /* * Write CTR back. @@ -802,14 +882,14 @@ void i8042_controller_cleanup(void) */ if (i8042_kbd_values.exists) - serio_cleanup(&i8042_kbd_port); + serio_cleanup(i8042_kbd_port); if (i8042_aux_values.exists) - serio_cleanup(&i8042_aux_port); + serio_cleanup(i8042_aux_port); - for (i = 0; i < 4; i++) + for (i = 0; i < I8042_NUM_MUX_PORTS; i++) if (i8042_mux_values[i].exists) - serio_cleanup(i8042_mux_port + i); + serio_cleanup(i8042_mux_port[i]); i8042_controller_reset(); } @@ -851,15 +931,15 @@ static int i8042_controller_resume(void) * Reconnect anything that was connected to the ports. */ - if (i8042_kbd_values.exists && i8042_activate_port(&i8042_kbd_port) == 0) - serio_reconnect(&i8042_kbd_port); + if (i8042_kbd_values.exists && i8042_activate_port(i8042_kbd_port) == 0) + serio_reconnect(i8042_kbd_port); - if (i8042_aux_values.exists && i8042_activate_port(&i8042_aux_port) == 0) - serio_reconnect(&i8042_aux_port); + if (i8042_aux_values.exists && i8042_activate_port(i8042_aux_port) == 0) + serio_reconnect(i8042_aux_port); - for (i = 0; i < 4; i++) - if (i8042_mux_values[i].exists && i8042_activate_port(i8042_mux_port + i) == 0) - serio_reconnect(i8042_mux_port + i); + for (i = 0; i < I8042_NUM_MUX_PORTS; i++) + if (i8042_mux_values[i].exists && i8042_activate_port(i8042_mux_port[i]) == 0) + serio_reconnect(i8042_mux_port[i]); /* * Restart timer (for polling "stuck" data) */ @@ -882,7 +962,7 @@ static int i8042_notify_sys(struct notif return NOTIFY_DONE; } -static struct notifier_block i8042_notifier= +static struct notifier_block i8042_notifier = { i8042_notify_sys, NULL, @@ -892,25 +972,27 @@ static struct notifier_block i8042_notif /* * Suspend/resume handlers for the new PM scheme (driver model) */ -static int i8042_suspend(struct sys_device *dev, u32 state) +static int i8042_suspend(struct device *dev, u32 state, u32 level) { - return i8042_controller_suspend(); + return level == SUSPEND_DISABLE ? i8042_controller_suspend() : 0; } -static int i8042_resume(struct sys_device *dev) +static int i8042_resume(struct device *dev, u32 level) { - return i8042_controller_resume(); + return level == RESUME_ENABLE ? i8042_controller_resume() : 0; } -static struct sysdev_class kbc_sysclass = { - set_kset_name("i8042"), - .suspend = i8042_suspend, - .resume = i8042_resume, -}; +static void i8042_shutdown(struct device *dev) +{ + i8042_controller_cleanup(); +} -static struct sys_device device_i8042 = { - .id = 0, - .cls = &kbc_sysclass, +static struct device_driver i8042_driver = { + .name = "i8042", + .bus = &platform_bus_type, + .suspend = i8042_suspend, + .resume = i8042_resume, + .shutdown = i8042_shutdown, }; /* @@ -929,23 +1011,211 @@ static int i8042_pm_callback(struct pm_d return 0; } -static void __init i8042_init_mux_values(struct i8042_values *values, struct serio *port, int index) +static struct serio * __init i8042_allocate_kbd_port(void) +{ + struct serio *serio; + + serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (serio) { + memset(serio, 0, sizeof(struct serio)); + serio->type = i8042_direct ? SERIO_8042 : SERIO_8042_XL, + serio->write = i8042_dumbkbd ? NULL : i8042_kbd_write, + serio->open = i8042_open, + serio->close = i8042_close, + serio->port_data = &i8042_kbd_values, + serio->dev.parent = &i8042_platform_device->dev; + strlcpy(serio->name, "i8042 Kbd Port", sizeof(serio->name)); + strlcpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys)); + } + + return serio; +} + +static struct serio * __init i8042_allocate_aux_port(void) +{ + struct serio *serio; + + serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (serio) { + memset(serio, 0, sizeof(struct serio)); + serio->type = SERIO_8042; + serio->write = i8042_aux_write; + serio->open = i8042_open; + serio->close = i8042_close; + serio->port_data = &i8042_aux_values, + serio->dev.parent = &i8042_platform_device->dev; + strlcpy(serio->name, "i8042 Aux Port", sizeof(serio->name)); + strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys)); + } + + return serio; +} + +static struct serio * __init i8042_allocate_mux_port(int index) +{ + struct serio *serio; + struct i8042_values *values = &i8042_mux_values[index]; + + serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (serio) { + *values = i8042_aux_values; + snprintf(values->name, sizeof(values->name), "AUX%d", index); + values->mux = index; + + memset(serio, 0, sizeof(struct serio)); + serio->type = SERIO_8042; + serio->write = i8042_aux_write; + serio->open = i8042_open; + serio->close = i8042_close; + serio->port_data = values; + serio->dev.parent = &i8042_platform_device->dev; + snprintf(serio->name, sizeof(serio->name), "i8042 Aux-%d Port", index); + snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, index + 1); + } + + return serio; +} + +#ifdef CONFIG_ACPI +static int acpi_kbd_registered; +static int acpi_aux_registered; + +struct i8042_resources { + unsigned int port1; + unsigned int port2; + unsigned int irq; +}; + +static acpi_status acpi_i8042_resource(struct acpi_resource *res, void *data) +{ + struct i8042_resources *i8042 = data; + struct acpi_resource_io *io; + struct acpi_resource_irq *irq; + struct acpi_resource_ext_irq *ext_irq; + + if (res->id == ACPI_RSTYPE_IO) { + io = &res->data.io; + if (io->range_length) { + if (!i8042->port1) + i8042->port1 = io->min_base_address; + else + i8042->port2 = io->min_base_address; + } + } else if (res->id == ACPI_RSTYPE_IRQ) { + irq = &res->data.irq; + if (irq->number_of_interrupts > 0) + i8042->irq = acpi_register_gsi(irq->interrupts[0], + irq->edge_level, irq->active_high_low); + } else if (res->id == ACPI_RSTYPE_EXT_IRQ) { + ext_irq = &res->data.extended_irq; + if (ext_irq->number_of_interrupts > 0) + i8042->irq = acpi_register_gsi(ext_irq->interrupts[0], + ext_irq->edge_level, ext_irq->active_high_low); + } + return AE_OK; +} + +static int acpi_i8042_kbd_add(struct acpi_device *device) +{ + struct i8042_resources i8042; + acpi_status status; + + memset(&i8042, 0, sizeof(i8042)); + status = acpi_walk_resources(device->handle, METHOD_NAME__CRS, + acpi_i8042_resource, &i8042); + if (ACPI_FAILURE(status)) + return -ENODEV; + + printk("i8042: ACPI %s [%s] at I/O 0x%x, 0x%x, irq %d\n", + acpi_device_name(device), acpi_device_bid(device), + i8042.port1, i8042.port2, i8042.irq); + + i8042_data_reg = i8042.port1; + i8042_command_reg = i8042.port2; + i8042_status_reg = i8042.port2; + i8042_kbd_values.irq = i8042.irq; + + return 0; +} + +static int acpi_i8042_aux_add(struct acpi_device *device) { - memcpy(port, &i8042_aux_port, sizeof(struct serio)); - memcpy(values, &i8042_aux_values, sizeof(struct i8042_values)); - sprintf(i8042_mux_names[index], "i8042 Aux-%d Port", index); - sprintf(i8042_mux_phys[index], I8042_MUX_PHYS_DESC, index + 1); - sprintf(i8042_mux_short[index], "AUX%d", index); - port->name = i8042_mux_names[index]; - port->phys = i8042_mux_phys[index]; - port->driver = values; - values->name = i8042_mux_short[index]; - values->mux = index; + struct i8042_resources i8042; + acpi_status status; + + memset(&i8042, 0, sizeof(i8042)); + status = acpi_walk_resources(device->handle, METHOD_NAME__CRS, + acpi_i8042_resource, &i8042); + if (ACPI_FAILURE(status)) + return -ENODEV; + + printk("i8042: ACPI %s [%s] at irq %d\n", + acpi_device_name(device), acpi_device_bid(device), i8042.irq); + + i8042_aux_values.irq = i8042.irq; + + return 0; } +static struct acpi_driver acpi_i8042_kbd_driver = { + .name = "i8042", + .ids = "PNP0303", + .ops = { + .add = acpi_i8042_kbd_add, + }, +}; + +static struct acpi_driver acpi_i8042_aux_driver = { + .name = "i8042", + .ids = "PNP0F13", + .ops = { + .add = acpi_i8042_aux_add, + }, +}; + +static int acpi_i8042_init(void) +{ + int err; + + if (no_acpi) { + printk("i8042: ACPI detection disabled\n"); + return -ENODEV; + } + + err = acpi_bus_register_driver(&acpi_i8042_kbd_driver); + if (err >= 0) + acpi_kbd_registered = 1; + if (err == 0) + return 0; + + err = acpi_bus_register_driver(&acpi_i8042_aux_driver); + if (err >= 0) + acpi_aux_registered = 1; + if (err == 0) + i8042_noaux = 1; + return 1; +} + +static void acpi_i8042_exit(void) +{ + if (acpi_kbd_registered) { + acpi_bus_unregister_driver(&acpi_i8042_kbd_driver); + acpi_kbd_registered = 0; + } + if (acpi_aux_registered) { + acpi_bus_unregister_driver(&acpi_i8042_aux_driver); + acpi_aux_registered = 0; + } +} +#else +static inline int acpi_i8042_init(void) { return -ENODEV; } +static inline void acpi_i8042_exit(void) { } +#endif + int __init i8042_init(void) { int i; + int err; dbg_init(); @@ -958,33 +1228,42 @@ int __init i8042_init(void) i8042_aux_values.irq = I8042_AUX_IRQ; i8042_kbd_values.irq = I8042_KBD_IRQ; + if (acpi_i8042_init() == 0) + return -ENODEV; + if (i8042_controller_init()) return -ENODEV; - if (i8042_dumbkbd) - i8042_kbd_port.write = NULL; + err = driver_register(&i8042_driver); + if (err) + return err; + + i8042_platform_device = platform_device_register_simple("i8042", -1, NULL, 0); + if (IS_ERR(i8042_platform_device)) { + driver_unregister(&i8042_driver); + return PTR_ERR(i8042_platform_device); + } if (!i8042_noaux && !i8042_check_aux(&i8042_aux_values)) { if (!i8042_nomux && !i8042_check_mux(&i8042_aux_values)) - for (i = 0; i < 4; i++) { - i8042_init_mux_values(i8042_mux_values + i, i8042_mux_port + i, i); - i8042_port_register(i8042_mux_values + i, i8042_mux_port + i); + for (i = 0; i < I8042_NUM_MUX_PORTS; i++) { + i8042_mux_port[i] = i8042_allocate_mux_port(i); + if (i8042_mux_port[i]) + i8042_port_register(i8042_mux_port[i]); } - else - i8042_port_register(&i8042_aux_values, &i8042_aux_port); + else { + i8042_aux_port = i8042_allocate_aux_port(); + if (i8042_aux_port) + i8042_port_register(i8042_aux_port); + } } - i8042_port_register(&i8042_kbd_values, &i8042_kbd_port); + i8042_kbd_port = i8042_allocate_kbd_port(); + if (i8042_kbd_port) + i8042_port_register(i8042_kbd_port); mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD); - if (sysdev_class_register(&kbc_sysclass) == 0) { - if (sysdev_register(&device_i8042) == 0) - i8042_sysdev_initialized = 1; - else - sysdev_class_unregister(&kbc_sysclass); - } - i8042_pm_dev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, i8042_pm_callback); register_reboot_notifier(&i8042_notifier); @@ -1001,24 +1280,25 @@ void __exit i8042_exit(void) if (i8042_pm_dev) pm_unregister(i8042_pm_dev); - if (i8042_sysdev_initialized) { - sysdev_unregister(&device_i8042); - sysdev_class_unregister(&kbc_sysclass); - } - i8042_controller_cleanup(); if (i8042_kbd_values.exists) - serio_unregister_port(&i8042_kbd_port); + serio_unregister_port(i8042_kbd_port); if (i8042_aux_values.exists) - serio_unregister_port(&i8042_aux_port); + serio_unregister_port(i8042_aux_port); - for (i = 0; i < 4; i++) + for (i = 0; i < I8042_NUM_MUX_PORTS; i++) if (i8042_mux_values[i].exists) - serio_unregister_port(i8042_mux_port + i); + serio_unregister_port(i8042_mux_port[i]); + del_timer_sync(&i8042_timer); + acpi_i8042_exit(); + + platform_device_unregister(i8042_platform_device); + driver_unregister(&i8042_driver); + i8042_platform_exit(); } --- linux-2.6.9-rc1/drivers/input/serio/i8042.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/input/serio/i8042.h 2004-09-03 00:56:31.566361832 -0700 @@ -104,6 +104,13 @@ #define I8042_BUFFER_SIZE 32 /* + * Number of AUX ports on controllers supporting active multiplexing + * specification + */ + +#define I8042_NUM_MUX_PORTS 4 + +/* * Debug. */ --- linux-2.6.9-rc1/drivers/input/serio/i8042-io.h 2004-08-24 00:01:51.000000000 -0700 +++ 25/drivers/input/serio/i8042-io.h 2004-09-03 00:56:31.562362440 -0700 @@ -65,6 +65,31 @@ static inline void i8042_write_command(i return; } +#if defined(__i386__) + +#include + +static struct dmi_system_id __initdata i8042_dmi_table[] = { + { + .ident = "Compaq Proliant 8500", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Compaq"), + DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"), + DMI_MATCH(DMI_PRODUCT_VERSION, "8500"), + }, + }, + { + .ident = "Compaq Proliant DL760", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Compaq"), + DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"), + DMI_MATCH(DMI_PRODUCT_VERSION, "DL760"), + }, + }, + { } +}; +#endif + static inline int i8042_platform_init(void) { /* @@ -79,6 +104,12 @@ static inline int i8042_platform_init(vo #if !defined(__i386__) && !defined(__x86_64__) i8042_reset = 1; #endif + +#if defined(__i386__) + if (dmi_check_system(i8042_dmi_table)) + i8042_noloop = 1; +#endif + return 0; } --- linux-2.6.9-rc1/drivers/input/serio/Kconfig 2004-08-24 00:02:48.000000000 -0700 +++ 25/drivers/input/serio/Kconfig 2004-09-03 00:56:31.558363048 -0700 @@ -20,7 +20,7 @@ config SERIO_I8042 tristate "i8042 PC Keyboard controller" if EMBEDDED || !X86 default y select SERIO - depends on !PARISC + depends on !PARISC && (!ARM || ARCH_SHARK || ARCH_EBSA285) ---help--- i8042 is the chip over which the standard AT keyboard and PS/2 mouse are connected to the computer. If you use these devices, @@ -130,3 +130,19 @@ config SERIO_MACEPS2 To compile this driver as a module, choose M here: the module will be called maceps2. + +config SERIO_RAW + tristate "Raw access to serio ports" + depends on SERIO + help + Say Y here if you want to have raw access to serio ports, such as + AUX ports on i8042 keyboard controller. Each serio port that is + bound to this driver will be accessible via a char device with + major 10 and dynamically allocated minor. The driver will try + allocating minor 1 (that historically corresponds to /dev/psaux) + first. To bind this driver to a serio port use sysfs interface: + + echo -n "serio_raw" > /sys/bus/serio/devices/serioX/driver + + To compile this driver as a module, choose M here: the + module will be called serio_raw. --- linux-2.6.9-rc1/drivers/input/serio/maceps2.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/drivers/input/serio/maceps2.c 2004-09-03 00:56:31.567361680 -0700 @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -46,15 +47,18 @@ MODULE_LICENSE("GPL"); #define PS2_CONTROL_RX_CLOCK_ENABLE BIT(4) /* pause reception if set to 0 */ #define PS2_CONTROL_RESET BIT(5) /* reset */ - struct maceps2_data { struct mace_ps2port *port; int irq; }; +static struct maceps2_data port_data[2]; +static struct serio *maceps2_port[2]; +static struct platform_device *maceps2_device; + static int maceps2_write(struct serio *dev, unsigned char val) { - struct mace_ps2port *port = ((struct maceps2_data *)dev->driver)->port; + struct mace_ps2port *port = ((struct maceps2_data *)dev->port_data)->port; unsigned int timeout = MACE_PS2_TIMEOUT; do { @@ -68,11 +72,10 @@ static int maceps2_write(struct serio *d return -1; } -static irqreturn_t maceps2_interrupt(int irq, void *dev_id, - struct pt_regs *regs) +static irqreturn_t maceps2_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct serio *dev = dev_id; - struct mace_ps2port *port = ((struct maceps2_data *)dev->driver)->port; + struct mace_ps2port *port = ((struct maceps2_data *)dev->port_data)->port; unsigned int byte; if (mace_read(port->status) & PS2_STATUS_RX_FULL) { @@ -85,7 +88,7 @@ static irqreturn_t maceps2_interrupt(int static int maceps2_open(struct serio *dev) { - struct maceps2_data *data = (struct maceps2_data *)dev->driver; + struct maceps2_data *data = (struct maceps2_data *)dev->port_data; if (request_irq(data->irq, maceps2_interrupt, 0, "PS/2 port", dev)) { printk(KERN_ERR "Could not allocate PS/2 IRQ\n"); @@ -106,7 +109,7 @@ static int maceps2_open(struct serio *de static void maceps2_close(struct serio *dev) { - struct maceps2_data *data = (struct maceps2_data *)dev->driver; + struct maceps2_data *data = (struct maceps2_data *)dev->port_data; mace_write(PS2_CONTROL_TX_CLOCK_DISABLE | PS2_CONTROL_RESET, data->port->control); @@ -114,46 +117,59 @@ static void maceps2_close(struct serio * free_irq(data->irq, dev); } -static struct maceps2_data port0_data, port1_data; -static struct serio maceps2_port0 = +static struct serio * __init maceps2_allocate_port(int idx) { - .type = SERIO_8042, - .open = maceps2_open, - .close = maceps2_close, - .write = maceps2_write, - .name = "MACE PS/2 port0", - .phys = "mace/serio0", - .driver = &port0_data, -}; + struct serio *serio; + + serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (serio) { + memset(serio, 0, sizeof(struct serio)); + serio->type = SERIO_8042; + serio->write = maceps2_write; + serio->open = maceps2_open; + serio->close = maceps2_close; + snprintf(serio->name, sizeof(serio->name), "MACE PS/2 port%d", idx); + snprintf(serio->phys, sizeof(serio->phys), "mace/serio%d", idx); + serio->port_data = &port_data[idx]; + serio->dev.parent = &maceps2_device->dev; + } + + return serio; +} -static struct serio maceps2_port1 = -{ - .type = SERIO_8042, - .open = maceps2_open, - .close = maceps2_close, - .write = maceps2_write, - .name = "MACE PS/2 port1", - .phys = "mace/serio1", - .driver = &port1_data, -}; static int __init maceps2_init(void) { - port0_data.port = &mace->perif.ps2.keyb; - port0_data.irq = MACEISA_KEYB_IRQ; - port1_data.port = &mace->perif.ps2.mouse; - port1_data.irq = MACEISA_MOUSE_IRQ; - serio_register_port(&maceps2_port0); - serio_register_port(&maceps2_port1); + maceps2_device = platform_device_register_simple("maceps2", -1, NULL, 0); + if (IS_ERR(maceps2_device)) + return PTR_ERR(maceps2_device); + + port_data[0].port = &mace->perif.ps2.keyb; + port_data[0].irq = MACEISA_KEYB_IRQ; + port_data[1].port = &mace->perif.ps2.mouse; + port_data[1].irq = MACEISA_MOUSE_IRQ; + + maceps2_port[0] = maceps2_allocate_port(0); + maceps2_port[1] = maceps2_allocate_port(1); + if (!maceps2_port[0] || !maceps2_port[1]) { + kfree(maceps2_port[0]); + kfree(maceps2_port[1]); + platform_device_unregister(maceps2_device); + return -ENOMEM; + } + + serio_register_port(maceps2_port[0]); + serio_register_port(maceps2_port[1]); return 0; } static void __exit maceps2_exit(void) { - serio_unregister_port(&maceps2_port0); - serio_unregister_port(&maceps2_port1); + serio_unregister_port(maceps2_port[0]); + serio_unregister_port(maceps2_port[1]); + platform_device_unregister(maceps2_device); } module_init(maceps2_init); --- linux-2.6.9-rc1/drivers/input/serio/Makefile 2004-08-24 00:01:53.000000000 -0700 +++ 25/drivers/input/serio/Makefile 2004-09-03 00:56:31.559362896 -0700 @@ -17,3 +17,4 @@ obj-$(CONFIG_SERIO_98KBD) += 98kbd-io.o obj-$(CONFIG_SERIO_GSCPS2) += gscps2.o obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o +obj-$(CONFIG_SERIO_RAW) += serio_raw.o --- linux-2.6.9-rc1/drivers/input/serio/parkbd.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/drivers/input/serio/parkbd.c 2004-09-03 00:56:31.568361528 -0700 @@ -53,9 +53,7 @@ static int parkbd_writing; static unsigned long parkbd_start; static struct pardevice *parkbd_dev; - -static char parkbd_name[] = "PARKBD AT/XT keyboard adapter"; -static char parkbd_phys[32]; +static struct serio *parkbd_port; static int parkbd_readlines(void) { @@ -86,13 +84,6 @@ static int parkbd_write(struct serio *po return 0; } -static struct serio parkbd_port = -{ - .write = parkbd_write, - .name = parkbd_name, - .phys = parkbd_phys, -}; - static void parkbd_interrupt(int irq, void *dev_id, struct pt_regs *regs) { @@ -125,7 +116,7 @@ static void parkbd_interrupt(int irq, vo parkbd_buffer |= (parkbd_readlines() >> 1) << parkbd_counter++; if (parkbd_counter == parkbd_mode + 10) - serio_interrupt(&parkbd_port, (parkbd_buffer >> (2 - parkbd_mode)) & 0xff, 0, regs); + serio_interrupt(parkbd_port, (parkbd_buffer >> (2 - parkbd_mode)) & 0xff, 0, regs); } parkbd_last = jiffies; @@ -163,16 +154,38 @@ static int parkbd_getport(void) return 0; } +static struct serio * __init parkbd_allocate_serio(void) +{ + struct serio *serio; + + serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (serio) { + serio->type = parkbd_mode; + serio->write = parkbd_write, + strlcpy(serio->name, "PARKBD AT/XT keyboard adapter", sizeof(serio->name)); + snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", parkbd_dev->port->name); + } + + return serio; +} int __init parkbd_init(void) { - if (parkbd_getport()) return -1; - parkbd_writelines(3); - parkbd_port.type = parkbd_mode; + int err; - sprintf(parkbd_phys, "%s/serio0", parkbd_dev->port->name); + err = parkbd_getport(); + if (err) + return err; + + parkbd_port = parkbd_allocate_serio(); + if (!parkbd_port) { + parport_release(parkbd_dev); + return -ENOMEM; + } + + parkbd_writelines(3); - serio_register_port(&parkbd_port); + serio_register_port(parkbd_port); printk(KERN_INFO "serio: PARKBD %s adapter on %s\n", parkbd_mode ? "AT" : "XT", parkbd_dev->port->name); @@ -183,7 +196,7 @@ int __init parkbd_init(void) void __exit parkbd_exit(void) { parport_release(parkbd_dev); - serio_unregister_port(&parkbd_port); + serio_unregister_port(parkbd_port); parport_unregister_device(parkbd_dev); } --- linux-2.6.9-rc1/drivers/input/serio/pcips2.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/input/serio/pcips2.c 2004-09-03 00:56:31.568361528 -0700 @@ -38,14 +38,14 @@ #define PS2_STAT_TXEMPTY (1<<7) struct pcips2_data { - struct serio io; + struct serio *io; unsigned int base; struct pci_dev *dev; }; static int pcips2_write(struct serio *io, unsigned char val) { - struct pcips2_data *ps2if = io->driver; + struct pcips2_data *ps2if = io->port_data; unsigned int stat; do { @@ -80,7 +80,7 @@ static irqreturn_t pcips2_interrupt(int if (hweight8(scancode) & 1) flag ^= SERIO_PARITY; - serio_interrupt(&ps2if->io, scancode, flag, regs); + serio_interrupt(ps2if->io, scancode, flag, regs); } while (1); return IRQ_RETVAL(handled); } @@ -101,7 +101,7 @@ static void pcips2_flush_input(struct pc static int pcips2_open(struct serio *io) { - struct pcips2_data *ps2if = io->driver; + struct pcips2_data *ps2if = io->port_data; int ret, val = 0; outb(PS2_CTRL_ENABLE, ps2if->base); @@ -119,7 +119,7 @@ static int pcips2_open(struct serio *io) static void pcips2_close(struct serio *io) { - struct pcips2_data *ps2if = io->driver; + struct pcips2_data *ps2if = io->port_data; outb(0, ps2if->base); @@ -129,46 +129,51 @@ static void pcips2_close(struct serio *i static int __devinit pcips2_probe(struct pci_dev *dev, const struct pci_device_id *id) { struct pcips2_data *ps2if; + struct serio *serio; int ret; ret = pci_enable_device(dev); if (ret) - return ret; + goto out; - if (!request_region(pci_resource_start(dev, 0), - pci_resource_len(dev, 0), "pcips2")) { - ret = -EBUSY; + ret = pci_request_regions(dev, "pcips2"); + if (ret) goto disable; - } ps2if = kmalloc(sizeof(struct pcips2_data), GFP_KERNEL); - if (!ps2if) { + serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (!ps2if || !serio) { ret = -ENOMEM; goto release; } memset(ps2if, 0, sizeof(struct pcips2_data)); + memset(serio, 0, sizeof(struct serio)); - ps2if->io.type = SERIO_8042; - ps2if->io.write = pcips2_write; - ps2if->io.open = pcips2_open; - ps2if->io.close = pcips2_close; - ps2if->io.name = pci_name(dev); - ps2if->io.phys = dev->dev.bus_id; - ps2if->io.driver = ps2if; + serio->type = SERIO_8042; + serio->write = pcips2_write; + serio->open = pcips2_open; + serio->close = pcips2_close; + strlcpy(serio->name, pci_name(dev), sizeof(serio->name)); + strlcpy(serio->phys, dev->dev.bus_id, sizeof(serio->phys)); + serio->port_data = ps2if; + serio->dev.parent = &dev->dev; + ps2if->io = serio; ps2if->dev = dev; ps2if->base = pci_resource_start(dev, 0); pci_set_drvdata(dev, ps2if); - serio_register_port(&ps2if->io); + serio_register_port(ps2if->io); return 0; release: - release_region(pci_resource_start(dev, 0), - pci_resource_len(dev, 0)); + kfree(ps2if); + kfree(serio); + pci_release_regions(dev); disable: pci_disable_device(dev); + out: return ret; } @@ -176,11 +181,10 @@ static void __devexit pcips2_remove(stru { struct pcips2_data *ps2if = pci_get_drvdata(dev); - serio_unregister_port(&ps2if->io); - release_region(pci_resource_start(dev, 0), - pci_resource_len(dev, 0)); + serio_unregister_port(ps2if->io); pci_set_drvdata(dev, NULL); kfree(ps2if); + pci_release_regions(dev); pci_disable_device(dev); } --- linux-2.6.9-rc1/drivers/input/serio/q40kbd.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/drivers/input/serio/q40kbd.c 2004-09-03 00:56:31.569361376 -0700 @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -47,43 +48,106 @@ MODULE_AUTHOR("Vojtech Pavlik type = SERIO_8042; + serio->open = q40kbd_open; + serio->close = q40kbd_close; + serio->dev.parent = &q40kbd_device->dev; + strlcpy(serio->name, "Q40 Kbd Port", sizeof(serio->name)); + strlcpy(serio->phys, "Q40", sizeof(serio->phys)); + } + + return serio; +} + +static int __init q40kbd_init(void) +{ if (!MACH_IS_Q40) return -EIO; - /* allocate the IRQ */ - request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0, "q40kbd", NULL); - - /* flush any pending input */ - while (maxread-- && (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG))) - master_inb(KEYCODE_REG); - - /* off we go */ - master_outb(-1,KEYBOARD_UNLOCK_REG); - master_outb(1,KEY_IRQ_ENABLE_REG); + q40kbd_device = platform_device_register_simple("q40kbd", -1, NULL, 0); + if (IS_ERR(q40kbd_device)) + return PTR_ERR(q40kbd_device); + + if (!(q40kbd_port = q40kbd_allocate_port())) { + platform_device_unregister(q40kbd_device); + return -ENOMEM; + } - serio_register_port(&q40kbd_port); + serio_register_port(q40kbd_port); printk(KERN_INFO "serio: Q40 kbd registered\n"); return 0; @@ -91,11 +155,8 @@ static int __init q40kbd_init(void) static void __exit q40kbd_exit(void) { - master_outb(0,KEY_IRQ_ENABLE_REG); - master_outb(-1,KEYBOARD_UNLOCK_REG); - - serio_unregister_port(&q40kbd_port); - free_irq(Q40_IRQ_KEYBOARD, NULL); + serio_unregister_port(q40kbd_port); + platform_device_unregister(q40kbd_device); } module_init(q40kbd_init); --- linux-2.6.9-rc1/drivers/input/serio/rpckbd.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/drivers/input/serio/rpckbd.c 2004-09-03 00:56:31.570361224 -0700 @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -44,6 +45,9 @@ MODULE_AUTHOR("Vojtech Pavlik, Russell K MODULE_DESCRIPTION("Acorn RiscPC PS/2 keyboard controller driver"); MODULE_LICENSE("GPL"); +static struct serio *rpckbd_port; +static struct platform_device *rpckbd_device; + static int rpckbd_write(struct serio *port, unsigned char val) { while (!(iomd_readb(IOMD_KCTRL) & (1 << 7))) @@ -101,25 +105,49 @@ static void rpckbd_close(struct serio *p free_irq(IRQ_KEYBOARDTX, port); } -static struct serio rpckbd_port = -{ - .type = SERIO_8042, - .open = rpckbd_open, - .close = rpckbd_close, - .write = rpckbd_write, - .name = "RiscPC PS/2 kbd port", - .phys = "rpckbd/serio0", -}; +/* + * Allocate and initialize serio structure for subsequent registration + * with serio core. + */ + +static struct serio * __init rpckbd_allocate_port(void) +{ + struct serio *serio; + + serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (serio) { + memset(serio, 0, sizeof(struct serio)); + serio->type = SERIO_8042; + serio->write = rpckbd_write; + serio->open = rpckbd_open; + serio->close = rpckbd_close; + serio->dev.parent = &rpckbd_device->dev; + strlcpy(serio->name, "RiscPC PS/2 kbd port", sizeof(serio->name)); + strlcpy(serio->phys, "rpckbd/serio0", sizeof(serio->phys)); + } + + return serio; +} static int __init rpckbd_init(void) { - serio_register_port(&rpckbd_port); + rpckbd_device = platform_device_register_simple("rpckbd", -1, NULL, 0); + if (IS_ERR(rpckbd_device)) + return PTR_ERR(rpckbd_device); + + if (!(rpckbd_port = rpckbd_allocate_port())) { + platform_device_unregister(rpckbd_device); + return -ENOMEM; + } + + serio_register_port(rpckbd_port); return 0; } static void __exit rpckbd_exit(void) { - serio_unregister_port(&rpckbd_port); + serio_unregister_port(rpckbd_port); + platform_device_unregister(rpckbd_device); } module_init(rpckbd_init); --- linux-2.6.9-rc1/drivers/input/serio/sa1111ps2.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/input/serio/sa1111ps2.c 2004-09-03 00:56:31.571361072 -0700 @@ -26,7 +26,7 @@ #include struct ps2if { - struct serio io; + struct serio *io; struct sa1111_dev *dev; unsigned long base; unsigned int open; @@ -59,7 +59,7 @@ static irqreturn_t ps2_rxint(int irq, vo if (hweight8(scancode) & 1) flag ^= SERIO_PARITY; - serio_interrupt(&ps2if->io, scancode, flag, regs); + serio_interrupt(ps2if->io, scancode, flag, regs); status = sa1111_readl(ps2if->base + SA1111_PS2STAT); } @@ -95,7 +95,7 @@ static irqreturn_t ps2_txint(int irq, vo */ static int ps2_write(struct serio *io, unsigned char val) { - struct ps2if *ps2if = io->driver; + struct ps2if *ps2if = io->port_data; unsigned long flags; unsigned int head; @@ -122,7 +122,7 @@ static int ps2_write(struct serio *io, u static int ps2_open(struct serio *io) { - struct ps2if *ps2if = io->driver; + struct ps2if *ps2if = io->port_data; int ret; sa1111_enable_device(ps2if->dev); @@ -154,7 +154,7 @@ static int ps2_open(struct serio *io) static void ps2_close(struct serio *io) { - struct ps2if *ps2if = io->driver; + struct ps2if *ps2if = io->port_data; sa1111_writel(0, ps2if->base + SA1111_PS2CR); @@ -232,22 +232,28 @@ static int __init ps2_test(struct ps2if static int ps2_probe(struct sa1111_dev *dev) { struct ps2if *ps2if; + struct serio *serio; int ret; ps2if = kmalloc(sizeof(struct ps2if), GFP_KERNEL); - if (!ps2if) { - return -ENOMEM; + serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (!ps2if || !serio) { + ret = -ENOMEM; + goto free; } memset(ps2if, 0, sizeof(struct ps2if)); + memset(serio, 0, sizeof(struct serio)); - ps2if->io.type = SERIO_8042; - ps2if->io.write = ps2_write; - ps2if->io.open = ps2_open; - ps2if->io.close = ps2_close; - ps2if->io.name = dev->dev.bus_id; - ps2if->io.phys = dev->dev.bus_id; - ps2if->io.driver = ps2if; + serio->type = SERIO_8042; + serio->write = ps2_write; + serio->open = ps2_open; + serio->close = ps2_close; + strlcpy(serio->name, dev->dev.bus_id, sizeof(serio->name)); + strlcpy(serio->phys, dev->dev.bus_id, sizeof(serio->phys)); + serio->port_data = ps2if; + serio->dev.parent = &dev->dev; + ps2if->io = serio; ps2if->dev = dev; sa1111_set_drvdata(dev, ps2if); @@ -292,7 +298,7 @@ static int ps2_probe(struct sa1111_dev * ps2_clear_input(ps2if); sa1111_disable_device(ps2if->dev); - serio_register_port(&ps2if->io); + serio_register_port(ps2if->io); return 0; out: @@ -302,6 +308,7 @@ static int ps2_probe(struct sa1111_dev * free: sa1111_set_drvdata(dev, NULL); kfree(ps2if); + kfree(serio); return ret; } @@ -312,7 +319,7 @@ static int ps2_remove(struct sa1111_dev { struct ps2if *ps2if = sa1111_get_drvdata(dev); - serio_unregister_port(&ps2if->io); + serio_unregister_port(ps2if->io); release_mem_region(dev->res.start, dev->res.end - dev->res.start + 1); sa1111_set_drvdata(dev, NULL); --- linux-2.6.9-rc1/drivers/input/serio/serio.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/drivers/input/serio/serio.c 2004-09-03 00:56:31.574360616 -0700 @@ -1,11 +1,9 @@ /* - * $Id: serio.c,v 1.15 2002/01/22 21:12:03 vojtech Exp $ - * - * Copyright (c) 1999-2001 Vojtech Pavlik - */ - -/* * The Serio abstraction module + * + * Copyright (c) 1999-2004 Vojtech Pavlik + * Copyright (c) 2004 Dmitry Torokhov + * Copyright (c) 2003 Daniele Bellucci */ /* @@ -26,10 +24,6 @@ * Should you need to contact me, the author, you can do so either by * e-mail - mail your message to , or by paper mail: * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic - * - * Changes: - * 20 Jul. 2003 Daniele Bellucci - * Minor cleanups. */ #include @@ -50,100 +44,178 @@ MODULE_LICENSE("GPL"); EXPORT_SYMBOL(serio_interrupt); EXPORT_SYMBOL(serio_register_port); EXPORT_SYMBOL(serio_register_port_delayed); -EXPORT_SYMBOL(__serio_register_port); EXPORT_SYMBOL(serio_unregister_port); EXPORT_SYMBOL(serio_unregister_port_delayed); -EXPORT_SYMBOL(__serio_unregister_port); -EXPORT_SYMBOL(serio_register_device); -EXPORT_SYMBOL(serio_unregister_device); +EXPORT_SYMBOL(serio_register_driver); +EXPORT_SYMBOL(serio_unregister_driver); EXPORT_SYMBOL(serio_open); EXPORT_SYMBOL(serio_close); EXPORT_SYMBOL(serio_rescan); EXPORT_SYMBOL(serio_reconnect); +static DECLARE_MUTEX(serio_sem); /* protects serio_list and serio_diriver_list */ +static LIST_HEAD(serio_list); +static LIST_HEAD(serio_driver_list); +static unsigned int serio_no; + +struct bus_type serio_bus = { + .name = "serio", +}; + +static void serio_find_driver(struct serio *serio); +static void serio_create_port(struct serio *serio); +static void serio_destroy_port(struct serio *serio); +static void serio_connect_port(struct serio *serio, struct serio_driver *drv); +static void serio_reconnect_port(struct serio *serio); +static void serio_disconnect_port(struct serio *serio); + +static int serio_bind_driver(struct serio *serio, struct serio_driver *drv) +{ + get_driver(&drv->driver); + + drv->connect(serio, drv); + if (serio->drv) { + down_write(&serio_bus.subsys.rwsem); + serio->dev.driver = &drv->driver; + device_bind_driver(&serio->dev); + up_write(&serio_bus.subsys.rwsem); + return 1; + } + + put_driver(&drv->driver); + return 0; +} + +/* serio_find_driver() must be called with serio_sem down. */ +static void serio_find_driver(struct serio *serio) +{ + struct serio_driver *drv; + + list_for_each_entry(drv, &serio_driver_list, node) + if (!drv->manual_bind) + if (serio_bind_driver(serio, drv)) + break; +} + +/* + * Serio event processing. + */ + struct serio_event { int type; struct serio *serio; struct list_head node; }; -static DECLARE_MUTEX(serio_sem); -static LIST_HEAD(serio_list); -static LIST_HEAD(serio_dev_list); +enum serio_event_type { + SERIO_RESCAN, + SERIO_RECONNECT, + SERIO_REGISTER_PORT, + SERIO_UNREGISTER_PORT, +}; + +static spinlock_t serio_event_lock = SPIN_LOCK_UNLOCKED; /* protects serio_event_list */ static LIST_HEAD(serio_event_list); +static DECLARE_WAIT_QUEUE_HEAD(serio_wait); +static DECLARE_COMPLETION(serio_exited); static int serio_pid; -static void serio_find_dev(struct serio *serio) +static void serio_queue_event(struct serio *serio, int event_type) { - struct serio_dev *dev; + unsigned long flags; + struct serio_event *event; - list_for_each_entry(dev, &serio_dev_list, node) { - if (serio->dev) - break; - if (dev->connect) - dev->connect(serio, dev); - } -} + spin_lock_irqsave(&serio_event_lock, flags); -#define SERIO_RESCAN 1 -#define SERIO_RECONNECT 2 -#define SERIO_REGISTER_PORT 3 -#define SERIO_UNREGISTER_PORT 4 + if ((event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC))) { + event->type = event_type; + event->serio = serio; -static DECLARE_WAIT_QUEUE_HEAD(serio_wait); -static DECLARE_COMPLETION(serio_exited); + list_add_tail(&event->node, &serio_event_list); + wake_up(&serio_wait); + } + + spin_unlock_irqrestore(&serio_event_lock, flags); +} -static void serio_invalidate_pending_events(struct serio *serio) +static struct serio_event *serio_get_event(void) { struct serio_event *event; + struct list_head *node; + unsigned long flags; + + spin_lock_irqsave(&serio_event_lock, flags); + + if (list_empty(&serio_event_list)) { + spin_unlock_irqrestore(&serio_event_lock, flags); + return NULL; + } + + node = serio_event_list.next; + event = container_of(node, struct serio_event, node); + list_del_init(node); + + spin_unlock_irqrestore(&serio_event_lock, flags); - list_for_each_entry(event, &serio_event_list, node) - if (event->serio == serio) - event->serio = NULL; + return event; } -void serio_handle_events(void) +static void serio_handle_events(void) { - struct list_head *node, *next; struct serio_event *event; - list_for_each_safe(node, next, &serio_event_list) { - event = container_of(node, struct serio_event, node); + while ((event = serio_get_event())) { down(&serio_sem); - if (event->serio == NULL) - goto event_done; switch (event->type) { case SERIO_REGISTER_PORT : - __serio_register_port(event->serio); + serio_create_port(event->serio); + serio_connect_port(event->serio, NULL); break; case SERIO_UNREGISTER_PORT : - __serio_unregister_port(event->serio); + serio_disconnect_port(event->serio); + serio_destroy_port(event->serio); break; case SERIO_RECONNECT : - if (event->serio->dev && event->serio->dev->reconnect) - if (event->serio->dev->reconnect(event->serio) == 0) - break; - /* reconnect failed - fall through to rescan */ + serio_reconnect_port(event->serio); + break; case SERIO_RESCAN : - if (event->serio->dev && event->serio->dev->disconnect) - event->serio->dev->disconnect(event->serio); - serio_find_dev(event->serio); + serio_disconnect_port(event->serio); + serio_connect_port(event->serio, NULL); break; default: break; } -event_done: + up(&serio_sem); - list_del_init(node); kfree(event); } } +static void serio_remove_pending_events(struct serio *serio) +{ + struct list_head *node, *next; + struct serio_event *event; + unsigned long flags; + + spin_lock_irqsave(&serio_event_lock, flags); + + list_for_each_safe(node, next, &serio_event_list) { + event = container_of(node, struct serio_event, node); + if (event->serio == serio) { + list_del_init(node); + kfree(event); + } + } + + spin_unlock_irqrestore(&serio_event_lock, flags); +} + + static int serio_thread(void *nothing) { lock_kernel(); @@ -163,52 +235,239 @@ static int serio_thread(void *nothing) complete_and_exit(&serio_exited, 0); } -static void serio_queue_event(struct serio *serio, int event_type) + +/* + * Serio port operations + */ + +static ssize_t serio_show_description(struct device *dev, char *buf) { - struct serio_event *event; + struct serio *serio = to_serio_port(dev); + return sprintf(buf, "%s\n", serio->name); +} - if ((event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC))) { - event->type = event_type; - event->serio = serio; +static ssize_t serio_show_driver(struct device *dev, char *buf) +{ + return sprintf(buf, "%s\n", dev->driver ? dev->driver->name : "(none)"); +} - list_add_tail(&event->node, &serio_event_list); - wake_up(&serio_wait); +static ssize_t serio_rebind_driver(struct device *dev, const char *buf, size_t count) +{ + struct serio *serio = to_serio_port(dev); + struct device_driver *drv; + int retval; + + retval = down_interruptible(&serio_sem); + if (retval) + return retval; + + retval = count; + if (!strncmp(buf, "none", count)) { + serio_disconnect_port(serio); + } else if (!strncmp(buf, "reconnect", count)) { + serio_reconnect_port(serio); + } else if (!strncmp(buf, "rescan", count)) { + serio_disconnect_port(serio); + serio_connect_port(serio, NULL); + } else if ((drv = driver_find(buf, &serio_bus)) != NULL) { + serio_disconnect_port(serio); + serio_connect_port(serio, to_serio_driver(drv)); + put_driver(drv); + } else { + retval = -EINVAL; } + + up(&serio_sem); + + return retval; } -void serio_rescan(struct serio *serio) +static ssize_t serio_show_bind_mode(struct device *dev, char *buf) { - serio_queue_event(serio, SERIO_RESCAN); + struct serio *serio = to_serio_port(dev); + return sprintf(buf, "%s\n", serio->manual_bind ? "manual" : "auto"); } -void serio_reconnect(struct serio *serio) +static ssize_t serio_set_bind_mode(struct device *dev, const char *buf, size_t count) { - serio_queue_event(serio, SERIO_RECONNECT); + struct serio *serio = to_serio_port(dev); + int retval; + + retval = count; + if (!strncmp(buf, "manual", count)) { + serio->manual_bind = 1; + } else if (!strncmp(buf, "auto", count)) { + serio->manual_bind = 0; + } else { + retval = -EINVAL; + } + + return retval; } -irqreturn_t serio_interrupt(struct serio *serio, - unsigned char data, unsigned int flags, struct pt_regs *regs) +static struct device_attribute serio_device_attrs[] = { + __ATTR(description, S_IRUGO, serio_show_description, NULL), + __ATTR(driver, S_IWUSR | S_IRUGO, serio_show_driver, serio_rebind_driver), + __ATTR(bind_mode, S_IWUSR | S_IRUGO, serio_show_bind_mode, serio_set_bind_mode), + __ATTR_NULL +}; + + +static void serio_release_port(struct device *dev) { - irqreturn_t ret = IRQ_NONE; + struct serio *serio = to_serio_port(dev); - if (serio->dev && serio->dev->interrupt) { - ret = serio->dev->interrupt(serio, data, flags, regs); - } else { - if (!flags) { - if ((serio->type == SERIO_8042 || - serio->type == SERIO_8042_XL) && (data != 0xaa)) - return ret; - serio_rescan(serio); - ret = IRQ_HANDLED; + kfree(serio); + module_put(THIS_MODULE); +} + +static void serio_create_port(struct serio *serio) +{ + try_module_get(THIS_MODULE); + + spin_lock_init(&serio->lock); + list_add_tail(&serio->node, &serio_list); + snprintf(serio->dev.bus_id, sizeof(serio->dev.bus_id), "serio%d", serio_no++); + serio->dev.bus = &serio_bus; + serio->dev.release = serio_release_port; + if (serio->parent) + serio->dev.parent = &serio->parent->dev; + device_register(&serio->dev); +} + +/* + * serio_destroy_port() completes deregistration process and removes + * port from the system + */ +static void serio_destroy_port(struct serio *serio) +{ + struct serio_driver *drv = serio->drv; + unsigned long flags; + + serio_remove_pending_events(serio); + list_del_init(&serio->node); + + if (drv) { + drv->disconnect(serio); + down_write(&serio_bus.subsys.rwsem); + device_release_driver(&serio->dev); + up_write(&serio_bus.subsys.rwsem); + put_driver(&drv->driver); + } + + if (serio->parent) { + spin_lock_irqsave(&serio->parent->lock, flags); + serio->parent->child = NULL; + spin_unlock_irqrestore(&serio->parent->lock, flags); + } + + device_unregister(&serio->dev); +} + +/* + * serio_connect_port() tries to bind the port and possible all its + * children to appropriate drivers. If driver passed in the function will not + * try otehr drivers when binding parent port. + */ +static void serio_connect_port(struct serio *serio, struct serio_driver *drv) +{ + WARN_ON(serio->drv); + WARN_ON(serio->child); + + if (drv) + serio_bind_driver(serio, drv); + else if (!serio->manual_bind) + serio_find_driver(serio); + + /* Ok, now bind children, if any */ + while (serio->child) { + serio = serio->child; + + WARN_ON(serio->drv); + WARN_ON(serio->child); + + serio_create_port(serio); + + if (!serio->manual_bind) { + /* + * With children we just _prefer_ passed in driver, + * but we will try other options in case preferred + * is not the one + */ + if (!drv || !serio_bind_driver(serio, drv)) + serio_find_driver(serio); } } - return ret; +} + +/* + * + */ +static void serio_reconnect_port(struct serio *serio) +{ + do { + if (!serio->drv || !serio->drv->reconnect || serio->drv->reconnect(serio)) { + serio_disconnect_port(serio); + serio_connect_port(serio, NULL); + /* Ok, old children are now gone, we are done */ + break; + } + serio = serio->child; + } while (serio); +} + +/* + * serio_disconnect_port() unbinds a port from its driver. As a side effect + * all child ports are unbound and destroyed. + */ +static void serio_disconnect_port(struct serio *serio) +{ + struct serio_driver *drv = serio->drv; + struct serio *s; + + if (serio->child) { + /* + * Children ports should be disconnected and destroyed + * first, staring with the leaf one, since we don't want + * to do recursion + */ + do { + s = serio->child; + } while (s->child); + + while (s != serio) { + s = s->parent; + serio_destroy_port(s->child); + } + } + + /* + * Ok, no children left, now disconnect this port + */ + if (drv) { + drv->disconnect(serio); + down_write(&serio_bus.subsys.rwsem); + device_release_driver(&serio->dev); + up_write(&serio_bus.subsys.rwsem); + put_driver(&drv->driver); + } +} + +void serio_rescan(struct serio *serio) +{ + serio_queue_event(serio, SERIO_RESCAN); +} + +void serio_reconnect(struct serio *serio) +{ + serio_queue_event(serio, SERIO_RECONNECT); } void serio_register_port(struct serio *serio) { down(&serio_sem); - __serio_register_port(serio); + serio_create_port(serio); + serio_connect_port(serio, NULL); up(&serio_sem); } @@ -222,21 +481,11 @@ void serio_register_port_delayed(struct serio_queue_event(serio, SERIO_REGISTER_PORT); } -/* - * Should only be called directly if serio_sem has already been taken, - * for example when unregistering a serio from other input device's - * connect() function. - */ -void __serio_register_port(struct serio *serio) -{ - list_add_tail(&serio->node, &serio_list); - serio_find_dev(serio); -} - void serio_unregister_port(struct serio *serio) { down(&serio_sem); - __serio_unregister_port(serio); + serio_disconnect_port(serio); + serio_destroy_port(serio); up(&serio_sem); } @@ -250,82 +499,171 @@ void serio_unregister_port_delayed(struc serio_queue_event(serio, SERIO_UNREGISTER_PORT); } + /* - * Should only be called directly if serio_sem has already been taken, - * for example when unregistering a serio from other input device's - * disconnect() function. + * Serio driver operations */ -void __serio_unregister_port(struct serio *serio) + +static ssize_t serio_driver_show_description(struct device_driver *drv, char *buf) { - serio_invalidate_pending_events(serio); - list_del_init(&serio->node); - if (serio->dev && serio->dev->disconnect) - serio->dev->disconnect(serio); + struct serio_driver *driver = to_serio_driver(drv); + return sprintf(buf, "%s\n", driver->description ? driver->description : "(none)"); +} + +static ssize_t serio_driver_show_bind_mode(struct device_driver *drv, char *buf) +{ + struct serio_driver *serio_drv = to_serio_driver(drv); + return sprintf(buf, "%s\n", serio_drv->manual_bind ? "manual" : "auto"); } -void serio_register_device(struct serio_dev *dev) +static ssize_t serio_driver_set_bind_mode(struct device_driver *drv, const char *buf, size_t count) +{ + struct serio_driver *serio_drv = to_serio_driver(drv); + int retval; + + retval = count; + if (!strncmp(buf, "manual", count)) { + serio_drv->manual_bind = 1; + } else if (!strncmp(buf, "auto", count)) { + serio_drv->manual_bind = 0; + } else { + retval = -EINVAL; + } + + return retval; +} + + +static struct driver_attribute serio_driver_attrs[] = { + __ATTR(description, S_IRUGO, serio_driver_show_description, NULL), + __ATTR(bind_mode, S_IWUSR | S_IRUGO, + serio_driver_show_bind_mode, serio_driver_set_bind_mode), + __ATTR_NULL +}; + +void serio_register_driver(struct serio_driver *drv) { struct serio *serio; + down(&serio_sem); - list_add_tail(&dev->node, &serio_dev_list); - list_for_each_entry(serio, &serio_list, node) - if (!serio->dev && dev->connect) - dev->connect(serio, dev); + + list_add_tail(&drv->node, &serio_driver_list); + + drv->driver.bus = &serio_bus; + driver_register(&drv->driver); + + if (drv->manual_bind) + goto out; + +start_over: + list_for_each_entry(serio, &serio_list, node) { + if (!serio->drv) { + serio_connect_port(serio, drv); + /* + * if new child appeared then the list is changed, + * we need to start over + */ + if (serio->child) + goto start_over; + } + } + +out: up(&serio_sem); } -void serio_unregister_device(struct serio_dev *dev) +void serio_unregister_driver(struct serio_driver *drv) { struct serio *serio; down(&serio_sem); - list_del_init(&dev->node); + list_del_init(&drv->node); + +start_over: list_for_each_entry(serio, &serio_list, node) { - if (serio->dev == dev && dev->disconnect) - dev->disconnect(serio); - serio_find_dev(serio); + if (serio->drv == drv) { + serio_disconnect_port(serio); + serio_connect_port(serio, NULL); + /* we could've deleted some ports, restart */ + goto start_over; + } } + + driver_unregister(&drv->driver); + up(&serio_sem); } -/* called from serio_dev->connect/disconnect methods under serio_sem */ -int serio_open(struct serio *serio, struct serio_dev *dev) +/* called from serio_driver->connect/disconnect methods under serio_sem */ +int serio_open(struct serio *serio, struct serio_driver *drv) { - serio->dev = dev; + serio_pause_rx(serio); + serio->drv = drv; + serio_continue_rx(serio); + if (serio->open && serio->open(serio)) { - serio->dev = NULL; + serio_pause_rx(serio); + serio->drv = NULL; + serio_continue_rx(serio); return -1; } return 0; } -/* called from serio_dev->connect/disconnect methods under serio_sem */ +/* called from serio_driver->connect/disconnect methods under serio_sem */ void serio_close(struct serio *serio) { if (serio->close) serio->close(serio); - serio->dev = NULL; + + serio_pause_rx(serio); + serio->drv = NULL; + serio_continue_rx(serio); } -static int __init serio_init(void) +irqreturn_t serio_interrupt(struct serio *serio, + unsigned char data, unsigned int dfl, struct pt_regs *regs) { - int pid; + unsigned long flags; + irqreturn_t ret = IRQ_NONE; + + spin_lock_irqsave(&serio->lock, flags); + + if (likely(serio->drv)) { + ret = serio->drv->interrupt(serio, data, dfl, regs); + } else { + if (!dfl) { + if ((serio->type != SERIO_8042 && + serio->type != SERIO_8042_XL) || (data == 0xaa)) { + serio_rescan(serio); + ret = IRQ_HANDLED; + } + } + } + + spin_unlock_irqrestore(&serio->lock, flags); - pid = kernel_thread(serio_thread, NULL, CLONE_KERNEL); + return ret; +} - if (!pid) { +static int __init serio_init(void) +{ + if (!(serio_pid = kernel_thread(serio_thread, NULL, CLONE_KERNEL))) { printk(KERN_WARNING "serio: Failed to start kseriod\n"); return -1; } - serio_pid = pid; + serio_bus.dev_attrs = serio_device_attrs; + serio_bus.drv_attrs = serio_driver_attrs; + bus_register(&serio_bus); return 0; } static void __exit serio_exit(void) { + bus_unregister(&serio_bus); kill_proc(serio_pid, SIGTERM, 1); wait_for_completion(&serio_exited); } --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/input/serio/serio_raw.c 2004-09-03 00:56:31.576360312 -0700 @@ -0,0 +1,390 @@ +/* + * Raw serio device providing access to a raw byte stream from underlying + * serio port. Closely emulates behavior of pre-2.6 /dev/psaux device + * + * Copyright (c) 2004 Dmitry Torokhov + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Raw serio driver" + +MODULE_AUTHOR("Dmitry Torokhov "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#define SERIO_RAW_QUEUE_LEN 64 +struct serio_raw { + unsigned char queue[SERIO_RAW_QUEUE_LEN]; + unsigned int tail, head; + + char name[16]; + unsigned int refcnt; + struct serio *serio; + struct miscdevice dev; + wait_queue_head_t wait; + struct list_head list; + struct list_head node; +}; + +struct serio_raw_list { + struct fasync_struct *fasync; + struct serio_raw *serio_raw; + struct list_head node; +}; + +static DECLARE_MUTEX(serio_raw_sem); +static LIST_HEAD(serio_raw_list); +static unsigned int serio_raw_no; + +/********************************************************************* + * Interface with userspace (file operations) * + *********************************************************************/ + +static int serio_raw_fasync(int fd, struct file *file, int on) +{ + struct serio_raw_list *list = file->private_data; + int retval; + + retval = fasync_helper(fd, file, on, &list->fasync); + return retval < 0 ? retval : 0; +} + +static struct serio_raw *serio_raw_locate(int minor) +{ + struct serio_raw *serio_raw; + + list_for_each_entry(serio_raw, &serio_raw_list, node) { + if (serio_raw->dev.minor == minor) + return serio_raw; + } + + return NULL; +} + +static int serio_raw_open(struct inode *inode, struct file *file) +{ + struct serio_raw *serio_raw; + struct serio_raw_list *list; + int retval = 0; + + retval = down_interruptible(&serio_raw_sem); + if (retval) + return retval; + + if (!(serio_raw = serio_raw_locate(iminor(inode)))) { + retval = -ENODEV; + goto out; + } + + if (!serio_raw->serio) { + retval = -ENODEV; + goto out; + } + + if (!(list = kmalloc(sizeof(struct serio_raw_list), GFP_KERNEL))) { + retval = -ENOMEM; + goto out; + } + + memset(list, 0, sizeof(struct serio_raw_list)); + list->serio_raw = serio_raw; + file->private_data = list; + + serio_raw->refcnt++; + list_add_tail(&list->node, &serio_raw->list); + +out: + up(&serio_raw_sem); + return retval; +} + +static int serio_raw_cleanup(struct serio_raw *serio_raw) +{ + if (--serio_raw->refcnt == 0) { + misc_deregister(&serio_raw->dev); + list_del_init(&serio_raw->node); + kfree(serio_raw); + + return 1; + } + + return 0; +} + +static int serio_raw_release(struct inode *inode, struct file *file) +{ + struct serio_raw_list *list = file->private_data; + struct serio_raw *serio_raw = list->serio_raw; + + down(&serio_raw_sem); + + serio_raw_fasync(-1, file, 0); + serio_raw_cleanup(serio_raw); + + up(&serio_raw_sem); + return 0; +} + +static int serio_raw_fetch_byte(struct serio_raw *serio_raw, char *c) +{ + unsigned long flags; + int empty; + + spin_lock_irqsave(&serio_raw->serio->lock, flags); + + empty = serio_raw->head == serio_raw->tail; + if (!empty) { + *c = serio_raw->queue[serio_raw->tail]; + serio_raw->tail = (serio_raw->tail + 1) % SERIO_RAW_QUEUE_LEN; + } + + spin_unlock_irqrestore(&serio_raw->serio->lock, flags); + + return !empty; +} + +static ssize_t serio_raw_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct serio_raw_list *list = file->private_data; + struct serio_raw *serio_raw = list->serio_raw; + char c; + ssize_t retval = 0; + + if (!serio_raw->serio) + return -ENODEV; + + if (serio_raw->head == serio_raw->tail && (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + + retval = wait_event_interruptible(list->serio_raw->wait, + serio_raw->head != serio_raw->tail || !serio_raw->serio); + if (retval) + return retval; + + if (!serio_raw->serio) + return -ENODEV; + + while (retval < count && serio_raw_fetch_byte(serio_raw, &c)) { + if (put_user(c, buffer++)) + return -EFAULT; + retval++; + } + + return retval; +} + +static ssize_t serio_raw_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct serio_raw_list *list = file->private_data; + ssize_t written = 0; + int retval; + unsigned char c; + + retval = down_interruptible(&serio_raw_sem); + if (retval) + return retval; + + if (!list->serio_raw->serio) { + retval = -ENODEV; + goto out; + } + + if (count > 32) + count = 32; + + while (count--) { + if (get_user(c, buffer++)) { + retval = -EFAULT; + goto out; + } + if (serio_write(list->serio_raw->serio, c)) { + retval = -EIO; + goto out; + } + written++; + }; + +out: + up(&serio_raw_sem); + return written; +} + +static unsigned int serio_raw_poll(struct file *file, poll_table *wait) +{ + struct serio_raw_list *list = file->private_data; + + poll_wait(file, &list->serio_raw->wait, wait); + + if (list->serio_raw->head != list->serio_raw->tail) + return POLLIN | POLLRDNORM; + + return 0; +} + +struct file_operations serio_raw_fops = { + .owner = THIS_MODULE, + .open = serio_raw_open, + .release = serio_raw_release, + .read = serio_raw_read, + .write = serio_raw_write, + .poll = serio_raw_poll, + .fasync = serio_raw_fasync, +}; + + +/********************************************************************* + * Interface with serio port * + *********************************************************************/ + +static irqreturn_t serio_raw_interrupt(struct serio *serio, unsigned char data, + unsigned int dfl, struct pt_regs *regs) +{ + struct serio_raw *serio_raw = serio->private; + struct serio_raw_list *list; + unsigned int head = serio_raw->head; + + /* we are holding serio->lock here so we are prootected */ + serio_raw->queue[head] = data; + head = (head + 1) % SERIO_RAW_QUEUE_LEN; + if (likely(head != serio_raw->tail)) { + serio_raw->head = head; + list_for_each_entry(list, &serio_raw->list, node) + kill_fasync(&list->fasync, SIGIO, POLL_IN); + wake_up_interruptible(&serio_raw->wait); + } + + return IRQ_HANDLED; +} + +static void serio_raw_connect(struct serio *serio, struct serio_driver *drv) +{ + struct serio_raw *serio_raw; + int err; + + if ((serio->type & SERIO_TYPE) != SERIO_8042) + return; + + if (!(serio_raw = kmalloc(sizeof(struct serio_raw), GFP_KERNEL))) { + printk(KERN_ERR "serio_raw.c: can't allocate memory for a device\n"); + return; + } + + down(&serio_raw_sem); + + memset(serio_raw, 0, sizeof(struct serio_raw)); + snprintf(serio_raw->name, sizeof(serio_raw->name), "serio_raw%d", serio_raw_no++); + serio_raw->refcnt = 1; + serio_raw->serio = serio; + INIT_LIST_HEAD(&serio_raw->list); + init_waitqueue_head(&serio_raw->wait); + + serio->private = serio_raw; + if (serio_open(serio, drv)) + goto out_free; + + list_add_tail(&serio_raw->node, &serio_raw_list); + + serio_raw->dev.minor = PSMOUSE_MINOR; + serio_raw->dev.name = serio_raw->name; + serio_raw->dev.fops = &serio_raw_fops; + + err = misc_register(&serio_raw->dev); + if (err) { + serio_raw->dev.minor = MISC_DYNAMIC_MINOR; + err = misc_register(&serio_raw->dev); + } + + if (err) { + printk(KERN_INFO "serio_raw: failed to register raw access device for %s\n", + serio->phys); + goto out_close; + } + + printk(KERN_INFO "serio_raw: raw access enabled on %s (%s, minor %d)\n", + serio->phys, serio_raw->name, serio_raw->dev.minor); + goto out; + +out_close: + serio_close(serio); + list_del_init(&serio_raw->node); +out_free: + serio->private = NULL; + kfree(serio_raw); +out: + up(&serio_raw_sem); +} + +static int serio_raw_reconnect(struct serio *serio) +{ + struct serio_raw *serio_raw = serio->private; + struct serio_driver *drv = serio->drv; + + if (!drv || !serio_raw) { + printk(KERN_DEBUG "serio_raw: reconnect request, but serio is disconnected, ignoring...\n"); + return -1; + } + + /* + * Nothing needs to be done here, we just need this method to + * keep the same device. + */ + return 0; +} + +static void serio_raw_disconnect(struct serio *serio) +{ + struct serio_raw *serio_raw; + + down(&serio_raw_sem); + + serio_raw = serio->private; + + serio_close(serio); + serio->private = NULL; + + serio_raw->serio = NULL; + if (!serio_raw_cleanup(serio_raw)) + wake_up_interruptible(&serio_raw->wait); + + up(&serio_raw_sem); +} + +static struct serio_driver serio_raw_drv = { + .driver = { + .name = "serio_raw", + }, + .description = DRIVER_DESC, + .interrupt = serio_raw_interrupt, + .connect = serio_raw_connect, + .reconnect = serio_raw_reconnect, + .disconnect = serio_raw_disconnect, + .manual_bind = 1, +}; + +int __init serio_raw_init(void) +{ + serio_register_driver(&serio_raw_drv); + return 0; +} + +void __exit serio_raw_exit(void) +{ + serio_unregister_driver(&serio_raw_drv); +} + +module_init(serio_raw_init); +module_exit(serio_raw_exit); --- linux-2.6.9-rc1/drivers/input/serio/serport.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/input/serio/serport.c 2004-09-03 00:56:31.576360312 -0700 @@ -31,28 +31,25 @@ MODULE_ALIAS_LDISC(N_MOUSE); struct serport { struct tty_struct *tty; wait_queue_head_t wait; - struct serio serio; + struct serio *serio; unsigned long flags; - char phys[32]; }; -char serport_name[] = "Serial port"; - /* * Callback functions from the serio code. */ static int serport_serio_write(struct serio *serio, unsigned char data) { - struct serport *serport = serio->driver; + struct serport *serport = serio->port_data; return -(serport->tty->driver->write(serport->tty, 0, &data, 1) != 1); } static void serport_serio_close(struct serio *serio) { - struct serport *serport = serio->driver; + struct serport *serport = serio->port_data; - serport->serio.type = 0; + serport->serio->type = 0; wake_up_interruptible(&serport->wait); } @@ -64,26 +61,30 @@ static void serport_serio_close(struct s static int serport_ldisc_open(struct tty_struct *tty) { struct serport *serport; + struct serio *serio; char name[64]; serport = kmalloc(sizeof(struct serport), GFP_KERNEL); - if (unlikely(!serport)) + serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (unlikely(!serport || !serio)) { + kfree(serport); + kfree(serio); return -ENOMEM; - memset(serport, 0, sizeof(struct serport)); + } + memset(serport, 0, sizeof(struct serport)); + serport->serio = serio; set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); serport->tty = tty; tty->disc_data = serport; - snprintf(serport->phys, sizeof(serport->phys), "%s/serio0", tty_name(tty, name)); - - serport->serio.name = serport_name; - serport->serio.phys = serport->phys; - - serport->serio.type = SERIO_RS232; - serport->serio.write = serport_serio_write; - serport->serio.close = serport_serio_close; - serport->serio.driver = serport; + memset(serio, 0, sizeof(struct serio)); + strlcpy(serio->name, "Serial port", sizeof(serio->name)); + snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", tty_name(tty, name)); + serio->type = SERIO_RS232; + serio->write = serport_serio_write; + serio->close = serport_serio_close; + serio->port_data = serport; init_waitqueue_head(&serport->wait); @@ -114,7 +115,7 @@ static void serport_ldisc_receive(struct struct serport *serport = (struct serport*) tty->disc_data; int i; for (i = 0; i < count; i++) - serio_interrupt(&serport->serio, cp[i], 0, NULL); + serio_interrupt(serport->serio, cp[i], 0, NULL); } /* @@ -142,10 +143,10 @@ static ssize_t serport_ldisc_read(struct if (test_and_set_bit(SERPORT_BUSY, &serport->flags)) return -EBUSY; - serio_register_port(&serport->serio); + serio_register_port(serport->serio); printk(KERN_INFO "serio: Serial port %s\n", tty_name(tty, name)); - wait_event_interruptible(serport->wait, !serport->serio.type); - serio_unregister_port(&serport->serio); + wait_event_interruptible(serport->wait, !serport->serio->type); + serio_unregister_port(serport->serio); clear_bit(SERPORT_BUSY, &serport->flags); @@ -161,7 +162,7 @@ static int serport_ldisc_ioctl(struct tt struct serport *serport = (struct serport*) tty->disc_data; if (cmd == SPIOCSTYPE) - return get_user(serport->serio.type, (unsigned long __user *) arg); + return get_user(serport->serio->type, (unsigned long __user *) arg); return -EINVAL; } @@ -170,7 +171,7 @@ static void serport_ldisc_write_wakeup(s { struct serport *sp = (struct serport *) tty->disc_data; - serio_dev_write_wakeup(&sp->serio); + serio_drv_write_wakeup(sp->serio); } /* --- linux-2.6.9-rc1/drivers/input/touchscreen/gunze.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/drivers/input/touchscreen/gunze.c 2004-09-03 00:56:31.577360160 -0700 @@ -36,8 +36,10 @@ #include #include +#define DRIVER_DESC "Gunze AHL-51S touchscreen driver" + MODULE_AUTHOR("Vojtech Pavlik "); -MODULE_DESCRIPTION("Gunze AHL-51S touchscreen driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); /* @@ -111,7 +113,7 @@ static void gunze_disconnect(struct seri * and if yes, registers it as an input device. */ -static void gunze_connect(struct serio *serio, struct serio_dev *dev) +static void gunze_connect(struct serio *serio, struct serio_driver *drv) { struct gunze *gunze; @@ -142,7 +144,7 @@ static void gunze_connect(struct serio * gunze->dev.id.product = 0x0051; gunze->dev.id.version = 0x0100; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { kfree(gunze); return; } @@ -156,10 +158,14 @@ static void gunze_connect(struct serio * * The serio device structure. */ -static struct serio_dev gunze_dev = { - .interrupt = gunze_interrupt, - .connect = gunze_connect, - .disconnect = gunze_disconnect, +static struct serio_driver gunze_drv = { + .driver = { + .name = "gunze", + }, + .description = DRIVER_DESC, + .interrupt = gunze_interrupt, + .connect = gunze_connect, + .disconnect = gunze_disconnect, }; /* @@ -168,13 +174,13 @@ static struct serio_dev gunze_dev = { int __init gunze_init(void) { - serio_register_device(&gunze_dev); + serio_register_driver(&gunze_drv); return 0; } void __exit gunze_exit(void) { - serio_unregister_device(&gunze_dev); + serio_unregister_driver(&gunze_drv); } module_init(gunze_init); --- linux-2.6.9-rc1/drivers/input/touchscreen/h3600_ts_input.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/input/touchscreen/h3600_ts_input.c 2004-09-03 00:56:31.578360008 -0700 @@ -45,8 +45,10 @@ #include #include +#define DRIVER_DESC "H3600 touchscreen driver" + MODULE_AUTHOR("James Simmons "); -MODULE_DESCRIPTION("H3600 touchscreen driver"); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); /* @@ -373,7 +375,7 @@ static irqreturn_t h3600ts_interrupt(str * new serio device. It looks whether it was registered as a H3600 touchscreen * and if yes, registers it as an input device. */ -static void h3600ts_connect(struct serio *serio, struct serio_dev *dev) +static void h3600ts_connect(struct serio *serio, struct serio_driver *drv) { struct h3600_dev *ts; @@ -441,7 +443,7 @@ static void h3600ts_connect(struct serio ts->dev.id.product = 0x0666; /* FIXME !!! We can ask the hardware */ ts->dev.id.version = 0x0100; - if (serio_open(serio, dev)) { + if (serio_open(serio, drv)) { free_irq(IRQ_GPIO_BITSY_ACTION_BUTTON, ts); free_irq(IRQ_GPIO_BITSY_NPOWER_BUTTON, ts); kfree(ts); @@ -478,10 +480,14 @@ static void h3600ts_disconnect(struct se * The serio device structure. */ -static struct serio_dev h3600ts_dev = { - .interrupt = h3600ts_interrupt, - .connect = h3600ts_connect, - .disconnect = h3600ts_disconnect, +static struct serio_driver h3600ts_drv = { + .driver = { + .name = "h3600ts", + }, + .description = DRIVER_DESC, + .interrupt = h3600ts_interrupt, + .connect = h3600ts_connect, + .disconnect = h3600ts_disconnect, }; /* @@ -490,13 +496,13 @@ static struct serio_dev h3600ts_dev = { static int __init h3600ts_init(void) { - serio_register_device(&h3600ts_dev); + serio_register_driver(&h3600ts_drv); return 0; } static void __exit h3600ts_exit(void) { - serio_unregister_device(&h3600ts_dev); + serio_unregister_driver(&h3600ts_drv); } module_init(h3600ts_init); --- linux-2.6.9-rc1/drivers/input/tsdev.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/drivers/input/tsdev.c 2004-09-03 00:56:31.580359704 -0700 @@ -3,32 +3,42 @@ * * Copyright (c) 2001 "Crazy" james Simmons * - * Input driver to Touchscreen device driver module. + * Compaq touchscreen protocol driver. The protocol emulated by this driver + * is obsolete; for new programs use the tslib library which can read directly + * from evdev and perform dejittering, variance filtering and calibration - + * all in user space, not at kernel level. The meaning of this driver is + * to allow usage of newer input drivers with old applications that use the + * old /dev/h3600_ts and /dev/h3600_tsraw devices. * - * Sponsored by Transvirtual Technology + * 09-Apr-2004: Andrew Zabolotny + * Fixed to actually work, not just output random numbers. + * Added support for both h3600_ts and h3600_tsraw protocol + * emulation. */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to . + * e-mail - mail your message to . */ #define TSDEV_MINOR_BASE 128 #define TSDEV_MINORS 32 +/* First 16 devices are h3600_ts compatible; second 16 are h3600_tsraw */ +#define TSDEV_MINOR_MASK 15 #define TSDEV_BUFFER_SIZE 64 #include @@ -52,48 +62,84 @@ #define CONFIG_INPUT_TSDEV_SCREEN_Y 320 #endif +/* This driver emulates both protocols of the old h3600_ts and h3600_tsraw + * devices. The first one must output X/Y data in 'cooked' format, e.g. + * filtered, dejittered and calibrated. Second device just outputs raw + * data received from the hardware. + * + * This driver doesn't support filtering and dejittering; it supports only + * calibration. Filtering and dejittering must be done in the low-level + * driver, if needed, because it may gain additional benefits from knowing + * the low-level details, the nature of noise and so on. + * + * The driver precomputes a calibration matrix given the initial xres and + * yres values (quite innacurate for most touchscreens) that will result + * in a more or less expected range of output values. The driver supports + * the TS_SET_CAL ioctl, which will replace the calibration matrix with a + * new one, supposedly generated from the values taken from the raw device. + */ + MODULE_AUTHOR("James Simmons "); MODULE_DESCRIPTION("Input driver to touchscreen converter"); MODULE_LICENSE("GPL"); static int xres = CONFIG_INPUT_TSDEV_SCREEN_X; module_param(xres, uint, 0); -MODULE_PARM_DESC(xres, "Horizontal screen resolution"); +MODULE_PARM_DESC(xres, "Horizontal screen resolution (can be negative for X-mirror)"); static int yres = CONFIG_INPUT_TSDEV_SCREEN_Y; module_param(yres, uint, 0); -MODULE_PARM_DESC(yres, "Vertical screen resolution"); +MODULE_PARM_DESC(yres, "Vertical screen resolution (can be negative for Y-mirror)"); + +/* From Compaq's Touch Screen Specification version 0.2 (draft) */ +struct ts_event { + short pressure; + short x; + short y; + short millisecs; +}; + +struct ts_calibration { + int xscale; + int xtrans; + int yscale; + int ytrans; + int xyswap; +}; struct tsdev { int exist; int open; int minor; - char name[16]; + char name[8]; wait_queue_head_t wait; struct list_head list; struct input_handle handle; + int x, y, pressure; + struct ts_calibration cal; }; -/* From Compaq's Touch Screen Specification version 0.2 (draft) */ -typedef struct { - short pressure; - short x; - short y; - short millisecs; -} TS_EVENT; - struct tsdev_list { struct fasync_struct *fasync; struct list_head node; struct tsdev *tsdev; int head, tail; - int oldx, oldy, pendown; - TS_EVENT event[TSDEV_BUFFER_SIZE]; + struct ts_event event[TSDEV_BUFFER_SIZE]; + int raw; }; +/* The following ioctl codes are defined ONLY for backward compatibility. + * Don't use tsdev for new developement; use the tslib library instead. + * Touchscreen calibration is a fully userspace task. + */ +/* Use 'f' as magic number */ +#define IOC_H3600_TS_MAGIC 'f' +#define TS_GET_CAL _IOR(IOC_H3600_TS_MAGIC, 10, struct ts_calibration) +#define TS_SET_CAL _IOW(IOC_H3600_TS_MAGIC, 11, struct ts_calibration) + static struct input_handler tsdev_handler; -static struct tsdev *tsdev_table[TSDEV_MINORS]; +static struct tsdev *tsdev_table[TSDEV_MINORS/2]; static int tsdev_fasync(int fd, struct file *file, int on) { @@ -109,13 +155,16 @@ static int tsdev_open(struct inode *inod int i = iminor(inode) - TSDEV_MINOR_BASE; struct tsdev_list *list; - if (i >= TSDEV_MINORS || !tsdev_table[i]) + if (i >= TSDEV_MINORS || !tsdev_table[i & TSDEV_MINOR_MASK]) return -ENODEV; if (!(list = kmalloc(sizeof(struct tsdev_list), GFP_KERNEL))) return -ENOMEM; memset(list, 0, sizeof(struct tsdev_list)); + list->raw = (i >= TSDEV_MINORS/2) ? 1 : 0; + + i &= TSDEV_MINOR_MASK; list->tsdev = tsdev_table[i]; list_add_tail(&list->node, &tsdev_table[i]->list); file->private_data = list; @@ -161,7 +210,7 @@ static ssize_t tsdev_read(struct file *f return -EAGAIN; retval = wait_event_interruptible(list->tsdev->wait, - (list->head != list->tail) && list->tsdev->exist); + list->head != list->tail || !list->tsdev->exist); if (retval) return retval; @@ -169,11 +218,13 @@ static ssize_t tsdev_read(struct file *f if (!list->tsdev->exist) return -ENODEV; - while (list->head != list->tail && retval + sizeof(TS_EVENT) <= count) { - if (copy_to_user (buffer + retval, list->event + list->tail, sizeof(TS_EVENT))) + while (list->head != list->tail && + retval + sizeof (struct ts_event) <= count) { + if (copy_to_user (buffer + retval, list->event + list->tail, + sizeof (struct ts_event))) return -EFAULT; list->tail = (list->tail + 1) & (TSDEV_BUFFER_SIZE - 1); - retval += sizeof(TS_EVENT); + retval += sizeof (struct ts_event); } return retval; @@ -193,22 +244,27 @@ static unsigned int tsdev_poll(struct fi static int tsdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { -/* struct tsdev_list *list = file->private_data; - struct tsdev *evdev = list->tsdev; - struct input_dev *dev = tsdev->handle.dev; - int retval; - + struct tsdev *tsdev = list->tsdev; + int retval = 0; + switch (cmd) { - case HHEHE: - return 0; - case hjff: - return 0; - default: - return 0; + case TS_GET_CAL: + if (copy_to_user ((void *)arg, &tsdev->cal, + sizeof (struct ts_calibration))) + retval = -EFAULT; + break; + case TS_SET_CAL: + if (copy_from_user (&tsdev->cal, (void *)arg, + sizeof (struct ts_calibration))) + retval = -EFAULT; + break; + default: + retval = -EINVAL; + break; } -*/ - return -EINVAL; + + return retval; } struct file_operations tsdev_fops = { @@ -227,82 +283,85 @@ static void tsdev_event(struct input_han struct tsdev *tsdev = handle->private; struct tsdev_list *list; struct timeval time; - int size; - list_for_each_entry(list, &tsdev->list, node) { - switch (type) { - case EV_ABS: - switch (code) { - case ABS_X: - if (!list->pendown) - return; - size = handle->dev->absmax[ABS_X] - handle->dev->absmin[ABS_X]; - if (size > 0) - list->oldx = ((value - handle->dev->absmin[ABS_X]) * xres / size); - else - list->oldx = ((value - handle->dev->absmin[ABS_X])); - break; - case ABS_Y: - if (!list->pendown) - return; - size = handle->dev->absmax[ABS_Y] - handle->dev->absmin[ABS_Y]; - if (size > 0) - list->oldy = ((value - handle->dev->absmin[ABS_Y]) * yres / size); - else - list->oldy = ((value - handle->dev->absmin[ABS_Y])); - break; - case ABS_PRESSURE: - list->pendown = ((value > handle->dev-> absmin[ABS_PRESSURE])) ? - value - handle->dev->absmin[ABS_PRESSURE] : 0; - break; - } + switch (type) { + case EV_ABS: + switch (code) { + case ABS_X: + tsdev->x = value; + break; + case ABS_Y: + tsdev->y = value; break; + case ABS_PRESSURE: + if (value > handle->dev->absmax[ABS_PRESSURE]) + value = handle->dev->absmax[ABS_PRESSURE]; + value -= handle->dev->absmin[ABS_PRESSURE]; + if (value < 0) + value = 0; + tsdev->pressure = value; + break; + } + break; - case EV_REL: - switch (code) { - case REL_X: - if (!list->pendown) - return; - list->oldx += value; - if (list->oldx < 0) - list->oldx = 0; - else if (list->oldx > xres) - list->oldx = xres; + case EV_REL: + switch (code) { + case REL_X: + tsdev->x += value; + if (tsdev->x < 0) + tsdev->x = 0; + else if (tsdev->x > xres) + tsdev->x = xres; + break; + case REL_Y: + tsdev->y += value; + if (tsdev->y < 0) + tsdev->y = 0; + else if (tsdev->y > yres) + tsdev->y = yres; + break; + } + break; + + case EV_KEY: + if (code == BTN_TOUCH || code == BTN_MOUSE) { + switch (value) { + case 0: + tsdev->pressure = 0; break; - case REL_Y: - if (!list->pendown) - return; - list->oldy += value; - if (list->oldy < 0) - list->oldy = 0; - else if (list->oldy > xres) - list->oldy = xres; + case 1: + if (!tsdev->pressure) + tsdev->pressure = 1; break; } - break; - - case EV_KEY: - if (code == BTN_TOUCH || code == BTN_MOUSE) { - switch (value) { - case 0: - list->pendown = 0; - break; - case 1: - if (!list->pendown) - list->pendown = 1; - break; - case 2: - return; - } - } else - return; - break; } + break; + } + + if (type != EV_SYN || code != SYN_REPORT) + return; + + list_for_each_entry(list, &tsdev->list, node) { + int x, y, tmp; + do_gettimeofday(&time); list->event[list->head].millisecs = time.tv_usec / 100; - list->event[list->head].pressure = list->pendown; - list->event[list->head].x = list->oldx; - list->event[list->head].y = list->oldy; + list->event[list->head].pressure = tsdev->pressure; + + x = tsdev->x; + y = tsdev->y; + + /* Calibration */ + if (!list->raw) { + x = ((x * tsdev->cal.xscale) >> 8) + tsdev->cal.xtrans; + y = ((y * tsdev->cal.yscale) >> 8) + tsdev->cal.ytrans; + if (tsdev->cal.xyswap) { + tmp = x; x = y; y = tmp; + } + } + + list->event[list->head].x = x; + list->event[list->head].y = y; list->head = (list->head + 1) & (TSDEV_BUFFER_SIZE - 1); kill_fasync(&list->fasync, SIGIO, POLL_IN); } @@ -314,11 +373,11 @@ static struct input_handle *tsdev_connec struct input_device_id *id) { struct tsdev *tsdev; - int minor; + int minor, delta; - for (minor = 0; minor < TSDEV_MINORS && tsdev_table[minor]; + for (minor = 0; minor < TSDEV_MINORS/2 && tsdev_table[minor]; minor++); - if (minor == TSDEV_MINORS) { + if (minor >= TSDEV_MINORS/2) { printk(KERN_ERR "tsdev: You have way too many touchscreens\n"); return NULL; @@ -340,10 +399,25 @@ static struct input_handle *tsdev_connec tsdev->handle.handler = handler; tsdev->handle.private = tsdev; + /* Precompute the rough calibration matrix */ + delta = dev->absmax [ABS_X] - dev->absmin [ABS_X] + 1; + if (delta == 0) + delta = 1; + tsdev->cal.xscale = (xres << 8) / delta; + tsdev->cal.xtrans = - ((dev->absmin [ABS_X] * tsdev->cal.xscale) >> 8); + + delta = dev->absmax [ABS_Y] - dev->absmin [ABS_Y] + 1; + if (delta == 0) + delta = 1; + tsdev->cal.yscale = (yres << 8) / delta; + tsdev->cal.ytrans = - ((dev->absmin [ABS_Y] * tsdev->cal.yscale) >> 8); + tsdev_table[minor] = tsdev; - + devfs_mk_cdev(MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor), S_IFCHR|S_IRUGO|S_IWUSR, "input/ts%d", minor); + devfs_mk_cdev(MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor + TSDEV_MINORS/2), + S_IFCHR|S_IRUGO|S_IWUSR, "input/tsraw%d", minor); class_simple_device_add(input_class, MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor), dev->dev, "ts%d", minor); @@ -362,6 +436,7 @@ static void tsdev_disconnect(struct inpu wake_up_interruptible(&tsdev->wait); } else tsdev_free(tsdev); + devfs_remove("input/tsraw%d", tsdev->minor); } static struct input_device_id tsdev_ids[] = { @@ -379,6 +454,12 @@ static struct input_device_id tsdev_ids[ .absbit = { BIT(ABS_X) | BIT(ABS_Y) }, },/* A tablet like device, at least touch detection, two absolute axes */ + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT, + .evbit = { BIT(EV_ABS) }, + .absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) }, + },/* A tablet like device with several gradations of pressure */ + {},/* Terminating entry */ }; --- linux-2.6.9-rc1/drivers/isdn/capi/capidrv.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/drivers/isdn/capi/capidrv.c 2004-09-02 20:51:51.000000000 -0700 @@ -512,7 +512,8 @@ static void send_message(capidrv_contr * len = CAPIMSG_LEN(cmsg->buf); skb = alloc_skb(len, GFP_ATOMIC); memcpy(skb_put(skb, len), cmsg->buf, len); - capi20_put_message(&global.ap, skb); + if (capi20_put_message(&global.ap, skb) != CAPI_NOERROR) + kfree_skb(skb); } /* -------- state machine -------------------------------------------- */ --- linux-2.6.9-rc1/drivers/isdn/hardware/avm/b1.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/isdn/hardware/avm/b1.c 2004-09-02 20:51:51.000000000 -0700 @@ -389,7 +389,7 @@ u16 b1_send_message(struct capi_ctr *ctr CAPIMSG_NCCI(skb->data), CAPIMSG_MSGID(skb->data)); if (retval != CAPI_NOERROR) - goto out; + return retval; dlen = CAPIMSG_DATALEN(skb->data); @@ -399,16 +399,14 @@ u16 b1_send_message(struct capi_ctr *ctr b1_put_slice(port, skb->data + len, dlen); spin_unlock_irqrestore(&card->lock, flags); } else { - retval = CAPI_NOERROR; - spin_lock_irqsave(&card->lock, flags); b1_put_byte(port, SEND_MESSAGE); b1_put_slice(port, skb->data, len); spin_unlock_irqrestore(&card->lock, flags); } - out: + dev_kfree_skb_any(skb); - return retval; + return CAPI_NOERROR; } /* ------------------------------------------------------------- */ --- linux-2.6.9-rc1/drivers/isdn/hardware/avm/b1dma.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/drivers/isdn/hardware/avm/b1dma.c 2004-09-02 20:51:51.000000000 -0700 @@ -839,8 +839,6 @@ u16 b1dma_send_message(struct capi_ctr * } if (retval == CAPI_NOERROR) b1dma_queue_tx(card, skb); - else - dev_kfree_skb_any(skb); return retval; } --- linux-2.6.9-rc1/drivers/isdn/hardware/avm/c4.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/drivers/isdn/hardware/avm/c4.c 2004-09-02 20:51:51.000000000 -0700 @@ -1029,8 +1029,6 @@ static u16 c4_send_message(struct capi_c spin_lock_irqsave(&card->lock, flags); c4_dispatch_tx(card); spin_unlock_irqrestore(&card->lock, flags); - } else { - dev_kfree_skb_any(skb); } return retval; } --- linux-2.6.9-rc1/drivers/isdn/hardware/avm/t1isa.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/isdn/hardware/avm/t1isa.c 2004-09-02 20:51:51.000000000 -0700 @@ -472,7 +472,7 @@ static u16 t1isa_send_message(struct cap CAPIMSG_NCCI(skb->data), CAPIMSG_MSGID(skb->data)); if (retval != CAPI_NOERROR) - goto out; + return retval; dlen = CAPIMSG_DATALEN(skb->data); @@ -482,16 +482,15 @@ static u16 t1isa_send_message(struct cap t1_put_slice(port, skb->data + len, dlen); spin_unlock_irqrestore(&card->lock, flags); } else { - retval = CAPI_NOERROR; spin_lock_irqsave(&card->lock, flags); b1_put_byte(port, SEND_MESSAGE); t1_put_slice(port, skb->data, len); spin_unlock_irqrestore(&card->lock, flags); } - out: + dev_kfree_skb_any(skb); - return retval; + return CAPI_NOERROR; } /* ------------------------------------------------------------- */ --- linux-2.6.9-rc1/drivers/isdn/hardware/eicon/capifunc.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/isdn/hardware/eicon/capifunc.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: capifunc.c,v 1.61.4.2 2004/05/05 16:09:25 armin Exp $ +/* $Id: capifunc.c,v 1.61.4.5 2004/08/27 20:10:12 armin Exp $ * * ISDN interface module for Eicon active cards DIVA. * CAPI Interface common functions @@ -998,7 +998,8 @@ static u16 diva_send_message(struct capi write_end: diva_os_leave_spin_lock(&api_lock, &old_irql, "send message"); - diva_os_free_message_buffer(dmb); + if (retval == CAPI_NOERROR) + diva_os_free_message_buffer(dmb); return retval; } --- linux-2.6.9-rc1/drivers/isdn/hardware/eicon/capifunc.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/isdn/hardware/eicon/capifunc.h 2004-09-02 20:51:51.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: capifunc.h,v 1.11 2004/03/20 17:19:58 armin Exp $ +/* $Id: capifunc.h,v 1.11.4.1 2004/08/28 20:03:53 armin Exp $ * * ISDN interface module for Eicon active cards DIVA. * CAPI Interface common functions @@ -13,8 +13,6 @@ #ifndef __CAPIFUNC_H__ #define __CAPIFUNC_H__ -#define MAX_DESCRIPTORS 32 - #define DRRELMAJOR 2 #define DRRELMINOR 0 #define DRRELEXTRA "" --- linux-2.6.9-rc1/drivers/isdn/hardware/eicon/debug.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/drivers/isdn/hardware/eicon/debug.c 2004-09-02 20:51:51.000000000 -0700 @@ -5,6 +5,7 @@ #include "divasync.h" #include "kst_ifc.h" #include "maintidi.h" +#include "man_defs.h" /* LOCALS @@ -13,17 +14,23 @@ static void DI_register (void *arg); static void DI_deregister (pDbgHandle hDbg); -static void DI_format (int do_lock, word id, int type, char *format, va_list ap); +static void DI_format (int do_lock, word id, int type, char *format, va_list argument_list); static void DI_format_locked (word id, int type, char *format, va_list argument_list); static void DI_format_old (word id, char *format, va_list ap) { } -static void DiProcessEventLog (word id, dword msgID, va_list ap) { } +static void DiProcessEventLog (unsigned short id, unsigned long msgID, va_list ap) { } static void single_p (byte * P, word * PLength, byte Id); static void diva_maint_xdi_cb (ENTITY* e); static word SuperTraceCreateReadReq (byte* P, const char* path); +static int diva_mnt_cmp_nmbr (const char* nmbr); +static void diva_free_dma_descriptor (IDI_CALL request, int nr); +static int diva_get_dma_descriptor (IDI_CALL request, dword *dma_magic); void diva_mnt_internal_dprintf (dword drv_id, dword type, char* p, ...); static dword MaxDumpSize = 256 ; static dword MaxXlogSize = 2 + 128 ; +static char TraceFilter[DIVA_MAX_SELECTIVE_FILTER_LENGTH+1]; +static int TraceFilterIdent = -1; +static int TraceFilterChannel = -1; typedef struct _diva_maint_client { dword sec; @@ -40,9 +47,10 @@ typedef struct _diva_maint_client { BUFFERS XData; char xbuffer[2048+512]; byte* pmem; - int request_pending; + int request_pending; + int dma_handle; } diva_maint_client_t; -static diva_maint_client_t clients[64]; +static diva_maint_client_t clients[MAX_DESCRIPTORS]; static void diva_change_management_debug_mask (diva_maint_client_t* pC, dword old_mask); @@ -201,6 +209,10 @@ int diva_maint_init (byte* base, unsigne return (-1); } + TraceFilter[0] = 0; + TraceFilterIdent = -1; + TraceFilterChannel = -1; + dbg_base = base; diva_os_get_time (&start_sec, &start_usec); @@ -249,7 +261,6 @@ int diva_maint_init (byte* base, unsigne return (-1); } - return (0); } @@ -302,16 +313,16 @@ diva_dbg_entry_head_t* diva_maint_get_me diva_os_spin_lock_magic_t* old_irql) { diva_dbg_entry_head_t* pmsg = NULL; - diva_os_enter_spin_lock_hard (&dbg_q_lock, old_irql, "read"); + diva_os_enter_spin_lock (&dbg_q_lock, old_irql, "read"); if (dbg_q_busy) { - diva_os_leave_spin_lock_hard (&dbg_q_lock, old_irql, "read_busy"); + diva_os_leave_spin_lock (&dbg_q_lock, old_irql, "read_busy"); return NULL; } dbg_q_busy = 1; if (!(pmsg = (diva_dbg_entry_head_t*)queuePeekMsg (dbg_queue, size))) { dbg_q_busy = 0; - diva_os_leave_spin_lock_hard (&dbg_q_lock, old_irql, "read_empty"); + diva_os_leave_spin_lock (&dbg_q_lock, old_irql, "read_empty"); } return (pmsg); @@ -330,7 +341,7 @@ void diva_maint_ack_message (int do_rele queueFreeMsg (dbg_queue); } dbg_q_busy = 0; - diva_os_leave_spin_lock_hard (&dbg_q_lock, old_irql, "read_ack"); + diva_os_leave_spin_lock (&dbg_q_lock, old_irql, "read_ack"); } @@ -378,14 +389,14 @@ static void DI_register (void *arg) { return ; } - diva_os_enter_spin_lock_hard (&dbg_q_lock, &old_irql, "register"); + diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "register"); for (id = 1; id < (sizeof(clients)/sizeof(clients[0])); id++) { if (clients[id].hDbg == hDbg) { /* driver already registered */ - diva_os_leave_spin_lock_hard (&dbg_q_lock, &old_irql, "register"); + diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "register"); return; } if (clients[id].hDbg) { /* slot is busy */ @@ -468,7 +479,7 @@ static void DI_register (void *arg) { } } - diva_os_leave_spin_lock_hard (&dbg_q_lock, &old_irql, "register"); + diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "register"); } static void DI_deregister (pDbgHandle hDbg) { @@ -480,8 +491,8 @@ static void DI_deregister (pDbgHandle hD diva_os_get_time (&sec, &usec); - diva_os_enter_spin_lock_hard (&dbg_adapter_lock, &old_irql1, "read"); - diva_os_enter_spin_lock_hard (&dbg_q_lock, &old_irql, "read"); + diva_os_enter_spin_lock (&dbg_adapter_lock, &old_irql1, "read"); + diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "read"); for (i = 1; i < (sizeof(clients)/sizeof(clients[0])); i++) { if (clients[i].hDbg == hDbg) { @@ -543,8 +554,8 @@ static void DI_deregister (pDbgHandle hD } } - diva_os_leave_spin_lock_hard (&dbg_q_lock, &old_irql, "read_ack"); - diva_os_leave_spin_lock_hard (&dbg_adapter_lock, &old_irql1, "read_ack"); + diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "read_ack"); + diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "read_ack"); if (pmem) { diva_os_free (0, pmem); @@ -572,13 +583,22 @@ static void DI_format (int do_lock, char *data; unsigned short code; - if (!format) + if (diva_os_in_irq()) { + dbg_sequence++; return; + } + + if ((!format) || + ((TraceFilter[0] != 0) && ((TraceFilterIdent < 0) || (TraceFilterChannel < 0)))) { + return; + } + + diva_os_get_time (&sec, &usec); if (do_lock) { - diva_os_enter_spin_lock_hard (&dbg_q_lock, &old_irql, "format"); + diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "format"); } switch (type) { @@ -703,7 +723,7 @@ static void DI_format (int do_lock, } if (do_lock) { - diva_os_leave_spin_lock_hard (&dbg_q_lock, &old_irql, "format"); + diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "format"); } } @@ -720,7 +740,7 @@ int diva_get_driver_info (dword id, byte return (-1); } - diva_os_enter_spin_lock_hard (&dbg_q_lock, &old_irql, "driver info"); + diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "driver info"); if (clients[id].hDbg) { *p++ = 1; @@ -757,7 +777,7 @@ int diva_get_driver_info (dword id, byte } *p++ = 0; - diva_os_leave_spin_lock_hard (&dbg_q_lock, &old_irql, "driver info"); + diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "driver info"); return (p - data); } @@ -769,7 +789,7 @@ int diva_get_driver_dbg_mask (dword id, if (!data || !id || (id >= (sizeof(clients)/sizeof(clients[0])))) { return (-1); } - diva_os_enter_spin_lock_hard (&dbg_q_lock, &old_irql, "driver info"); + diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "driver info"); if (clients[id].hDbg) { ret = 4; @@ -779,7 +799,7 @@ int diva_get_driver_dbg_mask (dword id, *data++= (byte)(clients[id].hDbg->dbgMask >> 24); } - diva_os_leave_spin_lock_hard (&dbg_q_lock, &old_irql, "driver info"); + diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "driver info"); return (ret); } @@ -793,8 +813,8 @@ int diva_set_driver_dbg_mask (dword id, return (-1); } - diva_os_enter_spin_lock_hard (&dbg_adapter_lock, &old_irql1, "dbg mask"); - diva_os_enter_spin_lock_hard (&dbg_q_lock, &old_irql, "dbg mask"); + diva_os_enter_spin_lock (&dbg_adapter_lock, &old_irql1, "dbg mask"); + diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "dbg mask"); if (clients[id].hDbg) { dword old_mask = clients[id].hDbg->dbgMask; @@ -806,14 +826,14 @@ int diva_set_driver_dbg_mask (dword id, } - diva_os_leave_spin_lock_hard (&dbg_q_lock, &old_irql, "dbg mask"); + diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "dbg mask"); if (clients[id].request_pending) { clients[id].request_pending = 0; (*(clients[id].request))((ENTITY*)(*(clients[id].pIdiLib->DivaSTraceGetHandle))(clients[id].pIdiLib->hLib)); } - diva_os_leave_spin_lock_hard (&dbg_adapter_lock, &old_irql1, "dbg mask"); + diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "dbg mask"); return (ret); } @@ -851,12 +871,12 @@ void diva_mnt_add_xdi_adapter (const DES diva_os_get_time (&sec, &usec); diva_get_idi_adapter_info (d->request, &serial, &logical); if (serial & 0xff000000) { - sprintf (tmp, "ADAPTER:%d SN:%d-%d", + sprintf (tmp, "ADAPTER:%d SN:%u-%d", (int)logical, serial & 0x00ffffff, (byte)(((serial & 0xff000000) >> 24) + 1)); } else { - sprintf (tmp, "ADAPTER:%d SN:%d", (int)logical, serial); + sprintf (tmp, "ADAPTER:%d SN:%u", (int)logical, serial); } if (!(pmem = diva_os_malloc (0, DivaSTraceGetMemotyRequirement (d->channels)))) { @@ -864,13 +884,14 @@ void diva_mnt_add_xdi_adapter (const DES } memset (pmem, 0x00, DivaSTraceGetMemotyRequirement (d->channels)); - diva_os_enter_spin_lock_hard (&dbg_adapter_lock, &old_irql1, "register"); - diva_os_enter_spin_lock_hard (&dbg_q_lock, &old_irql, "register"); + diva_os_enter_spin_lock (&dbg_adapter_lock, &old_irql1, "register"); + diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "register"); for (id = 1; id < (sizeof(clients)/sizeof(clients[0])); id++) { if (clients[id].hDbg && (clients[id].request == d->request)) { - diva_os_leave_spin_lock_hard (&dbg_q_lock, &old_irql, "register"); - diva_os_leave_spin_lock_hard (&dbg_adapter_lock, &old_irql1, "register"); + diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "register"); + diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "register"); + diva_os_free(0, pmem); return; } if (clients[id].hDbg) { /* slot is busy */ @@ -891,8 +912,8 @@ void diva_mnt_add_xdi_adapter (const DES } if (free_id < 0) { - diva_os_leave_spin_lock_hard (&dbg_q_lock, &old_irql, "register"); - diva_os_leave_spin_lock_hard (&dbg_adapter_lock, &old_irql1, "register"); + diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "register"); + diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "register"); diva_os_free (0, pmem); return; } @@ -908,6 +929,7 @@ void diva_mnt_add_xdi_adapter (const DES clients[id].Dbg.drvTag[0] = 0; clients[id].logical = (int)logical; clients[id].channels = (int)d->channels; + clients[id].dma_handle = -1; clients[id].Dbg.dbgMask = 0; clients[id].dbgMask = clients[id].Dbg.dbgMask; @@ -949,8 +971,8 @@ void diva_mnt_add_xdi_adapter (const DES clients[id].request = NULL; clients[id].request_pending = 0; clients[id].hDbg = NULL; - diva_os_leave_spin_lock_hard (&dbg_q_lock, &old_irql, "register"); - diva_os_leave_spin_lock_hard (&dbg_adapter_lock, &old_irql1, "register"); + diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "register"); + diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "register"); diva_os_free (0, pmem); return; } @@ -988,14 +1010,14 @@ void diva_mnt_add_xdi_adapter (const DES org_mask = clients[id].Dbg.dbgMask; clients[id].Dbg.dbgMask = 0; - diva_os_leave_spin_lock_hard (&dbg_q_lock, &old_irql, "register"); + diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "register"); if (clients[id].request_pending) { clients[id].request_pending = 0; (*(clients[id].request))((ENTITY*)(*(clients[id].pIdiLib->DivaSTraceGetHandle))(clients[id].pIdiLib->hLib)); } - diva_os_leave_spin_lock_hard (&dbg_adapter_lock, &old_irql1, "register"); + diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "register"); diva_set_driver_dbg_mask (id, org_mask); } @@ -1012,8 +1034,8 @@ void diva_mnt_remove_xdi_adapter (const diva_os_get_time (&sec, &usec); - diva_os_enter_spin_lock_hard (&dbg_adapter_lock, &old_irql1, "read"); - diva_os_enter_spin_lock_hard (&dbg_q_lock, &old_irql, "read"); + diva_os_enter_spin_lock (&dbg_adapter_lock, &old_irql1, "read"); + diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "read"); for (i = 1; i < (sizeof(clients)/sizeof(clients[0])); i++) { if (clients[i].hDbg && (clients[i].request == d->request)) { @@ -1030,8 +1052,15 @@ void diva_mnt_remove_xdi_adapter (const } clients[i].hDbg = NULL; - clients[i].request = NULL; clients[i].request_pending = 0; + if (clients[i].dma_handle >= 0) { + /* + Free DMA handle + */ + diva_free_dma_descriptor (clients[i].request, clients[i].dma_handle); + clients[i].dma_handle = -1; + } + clients[i].request = NULL; /* Log driver register, MAINT driver ID is '0' @@ -1069,8 +1098,8 @@ void diva_mnt_remove_xdi_adapter (const } } - diva_os_leave_spin_lock_hard (&dbg_q_lock, &old_irql, "read_ack"); - diva_os_leave_spin_lock_hard (&dbg_adapter_lock, &old_irql1, "read_ack"); + diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "read_ack"); + diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "read_ack"); if (pmem) { diva_os_free (0, pmem); @@ -1142,6 +1171,42 @@ int SuperTraceASSIGN (void* AdapterHandl if (pC && pC->pIdiLib && pC->request) { ENTITY* e = (ENTITY*)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib); + IDI_SYNC_REQ* preq; + char buffer[((sizeof(preq->xdi_extended_features)+4) > sizeof(ENTITY)) ? (sizeof(preq->xdi_extended_features)+4) : sizeof(ENTITY)]; + char features[4]; + word assign_data_length = 1; + + features[0] = 0; + pC->xbuffer[0] = 0; + preq = (IDI_SYNC_REQ*)&buffer[0]; + preq->xdi_extended_features.Req = 0; + preq->xdi_extended_features.Rc = IDI_SYNC_REQ_XDI_GET_EXTENDED_FEATURES; + preq->xdi_extended_features.info.buffer_length_in_bytes = sizeof(features); + preq->xdi_extended_features.info.features = &features[0]; + + (*(pC->request))((ENTITY*)preq); + + if ((features[0] & DIVA_XDI_EXTENDED_FEATURES_VALID) && + (features[0] & DIVA_XDI_EXTENDED_FEATURE_MANAGEMENT_DMA)) { + dword rx_dma_magic; + if ((pC->dma_handle = diva_get_dma_descriptor (pC->request, &rx_dma_magic)) >= 0) { + pC->xbuffer[0] = LLI; + pC->xbuffer[1] = 8; + pC->xbuffer[2] = 0x40; + pC->xbuffer[3] = (byte)pC->dma_handle; + pC->xbuffer[4] = (byte)rx_dma_magic; + pC->xbuffer[5] = (byte)(rx_dma_magic >> 8); + pC->xbuffer[6] = (byte)(rx_dma_magic >> 16); + pC->xbuffer[7] = (byte)(rx_dma_magic >> 24); + pC->xbuffer[8] = (byte)DIVA_MAX_MANAGEMENT_TRANSFER_SIZE; + pC->xbuffer[9] = (byte)(DIVA_MAX_MANAGEMENT_TRANSFER_SIZE >> 8); + pC->xbuffer[10] = 0; + + assign_data_length = 11; + } + } else { + pC->dma_handle = -1; + } e->Id = MAN_ID; e->callback = diva_maint_xdi_cb; @@ -1149,9 +1214,8 @@ int SuperTraceASSIGN (void* AdapterHandl e->X = &pC->XData; e->Req = ASSIGN; e->ReqCh = 0; - e->X->PLength = 1; + e->X->PLength = assign_data_length; e->X->P = (byte*)&pC->xbuffer[0]; - pC->xbuffer[0] = 0; pC->request_pending = 1; @@ -1300,16 +1364,25 @@ static void diva_maint_xdi_cb (ENTITY* e diva_os_spin_lock_magic_t old_irql, old_irql1; - diva_os_enter_spin_lock_hard (&dbg_adapter_lock, &old_irql1, "xdi_cb"); - diva_os_enter_spin_lock_hard (&dbg_q_lock, &old_irql, "xdi_cb"); + diva_os_enter_spin_lock (&dbg_adapter_lock, &old_irql1, "xdi_cb"); + diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "xdi_cb"); pC = (diva_maint_client_t*)pLib->hAdapter; - if ((*(pLib->instance.DivaSTraceMessageInput))(&pLib->instance)) { - diva_mnt_internal_dprintf (0, DLI_ERR, "Trace internal library error"); + if ((e->complete == 255) || (pC->dma_handle < 0)) { + if ((*(pLib->instance.DivaSTraceMessageInput))(&pLib->instance)) { + diva_mnt_internal_dprintf (0, DLI_ERR, "Trace internal library error"); + } + } else { + /* + Process combined management interface indication + */ + if ((*(pLib->instance.DivaSTraceMessageInput))(&pLib->instance)) { + diva_mnt_internal_dprintf (0, DLI_ERR, "Trace internal library error (DMA mode)"); + } } - diva_os_leave_spin_lock_hard (&dbg_q_lock, &old_irql, "xdi_cb"); + diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "xdi_cb"); if (pC->request_pending) { @@ -1317,7 +1390,7 @@ static void diva_maint_xdi_cb (ENTITY* e (*(pC->request))(e); } - diva_os_leave_spin_lock_hard (&dbg_adapter_lock, &old_irql1, "xdi_cb"); + diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "xdi_cb"); } @@ -1365,8 +1438,42 @@ static void diva_maint_state_change_noti } switch (notify_subject) { - case DIVA_SUPER_TRACE_NOTIFY_LINE_CHANGE: - if (pC->hDbg->dbgMask & DIVA_MGT_DBG_LINE_EVENTS) { + case DIVA_SUPER_TRACE_NOTIFY_LINE_CHANGE: { + int view = (TraceFilter[0] == 0); + /* + Process selective Trace + */ + if (channel->Line[0] == 'I' && channel->Line[1] == 'd' && + channel->Line[2] == 'l' && channel->Line[3] == 'e') { + if ((TraceFilterIdent == pC->hDbg->id) && (TraceFilterChannel == (int)channel->ChannelNumber)) { + (*(hLib->DivaSTraceSetBChannel))(hLib, (int)channel->ChannelNumber, 0); + (*(hLib->DivaSTraceSetAudioTap))(hLib, (int)channel->ChannelNumber, 0); + diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG, "Selective Trace OFF for Ch=%d", + (int)channel->ChannelNumber); + TraceFilterIdent = -1; + TraceFilterChannel = -1; + view = 1; + } + } else if (TraceFilter[0] && (TraceFilterIdent < 0) && !(diva_mnt_cmp_nmbr (&channel->RemoteAddress[0]) && + diva_mnt_cmp_nmbr (&channel->LocalAddress[0]))) { + + if ((pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_BCHANNEL) != 0) { /* Activate B-channel trace */ + (*(hLib->DivaSTraceSetBChannel))(hLib, (int)channel->ChannelNumber, 1); + } + if ((pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_AUDIO) != 0) { /* Activate AudioTap Trace */ + (*(hLib->DivaSTraceSetAudioTap))(hLib, (int)channel->ChannelNumber, 1); + } + + TraceFilterIdent = pC->hDbg->id; + TraceFilterChannel = (int)channel->ChannelNumber; + + if (TraceFilterIdent >= 0) { + diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG, "Selective Trace ON for Ch=%d", + (int)channel->ChannelNumber); + view = 1; + } + } + if (view && (pC->hDbg->dbgMask & DIVA_MGT_DBG_LINE_EVENTS)) { diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L Ch = %d", (int)channel->ChannelNumber); diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L Status = <%s>", &channel->Line[0]); @@ -1392,10 +1499,26 @@ static void diva_maint_state_change_noti channel->LastDisconnecCause); diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L Owner = <%s>", &channel->UserID[0]); } - break; + + } break; case DIVA_SUPER_TRACE_NOTIFY_MODEM_CHANGE: if (pC->hDbg->dbgMask & DIVA_MGT_DBG_MDM_PROGRESS) { + { + int ch = TraceFilterChannel; + int id = TraceFilterIdent; + + if ((id >= 0) && (ch >= 0) && (id < sizeof(clients)/sizeof(clients[0])) && + (clients[id].Dbg.id == (byte)id) && (clients[id].pIdiLib == hLib)) { + if (ch != (int)modem->ChannelNumber) { + break; + } + } else if (TraceFilter[0] != 0) { + break; + } + } + + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Ch = %lu", (int)modem->ChannelNumber); diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Event = %lu", modem->Event); @@ -1428,6 +1551,20 @@ static void diva_maint_state_change_noti case DIVA_SUPER_TRACE_NOTIFY_FAX_CHANGE: if (pC->hDbg->dbgMask & DIVA_MGT_DBG_FAX_PROGRESS) { + { + int ch = TraceFilterChannel; + int id = TraceFilterIdent; + + if ((id >= 0) && (ch >= 0) && (id < sizeof(clients)/sizeof(clients[0])) && + (clients[id].Dbg.id == (byte)id) && (clients[id].pIdiLib == hLib)) { + if (ch != (int)fax->ChannelNumber) { + break; + } + } else if (TraceFilter[0] != 0) { + break; + } + } + diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Ch = %lu",(int)fax->ChannelNumber); diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Event = %lu", fax->Event); diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Pages = %lu", fax->Page_Counter); @@ -1660,6 +1797,52 @@ static void diva_maint_trace_notify (voi diva_dbg_entry_head_t* pmsg; word size; dword sec, usec; + int ch = TraceFilterChannel; + int id = TraceFilterIdent; + + /* + Selective trace + */ + if ((id >= 0) && (ch >= 0) && (id < sizeof(clients)/sizeof(clients[0])) && + (clients[id].Dbg.id == (byte)id) && (clients[id].pIdiLib == hLib)) { + const char* p = NULL; + int ch_value = -1; + MI_XLOG_HDR *TrcData = (MI_XLOG_HDR *)xlog_buffer; + + if (Adapter != clients[id].logical) { + return; /* Ignore all trace messages from other adapters */ + } + + if (TrcData->code == 24) { + p = (char*)&TrcData->code; + p += 2; + } + + /* + All L1 messages start as [dsp,ch], so we can filter this information + and filter out all messages that use different channel + */ + if (p && p[0] == '[') { + if (p[2] == ',') { + p += 3; + ch_value = *p - '0'; + } else if (p[3] == ',') { + p += 4; + ch_value = *p - '0'; + } + if (ch_value >= 0) { + if (p[2] == ']') { + ch_value = ch_value * 10 + p[1] - '0'; + } + if (ch_value != ch) { + return; /* Ignore other channels */ + } + } + } + + } else if (TraceFilter[0] != 0) { + return; /* Ignore trace if trace filter is activated, but idle */ + } diva_os_get_time (&sec, &usec); @@ -1705,18 +1888,20 @@ static void diva_change_management_debug (*(pC->pIdiLib->DivaSTraceSetDChannel))(pC->pIdiLib, (pC->hDbg->dbgMask & DIVA_MGT_DBG_DCHAN) != 0); } - if (changed & DIVA_MGT_DBG_IFC_BCHANNEL) { - int i, state = ((pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_BCHANNEL) != 0); + if (!TraceFilter[0]) { + if (changed & DIVA_MGT_DBG_IFC_BCHANNEL) { + int i, state = ((pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_BCHANNEL) != 0); - for (i = 0; i < pC->channels; i++) { - (*(pC->pIdiLib->DivaSTraceSetBChannel))(pC->pIdiLib, i+1, state); + for (i = 0; i < pC->channels; i++) { + (*(pC->pIdiLib->DivaSTraceSetBChannel))(pC->pIdiLib, i+1, state); + } } - } - if (changed & DIVA_MGT_DBG_IFC_AUDIO) { - int i, state = ((pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_AUDIO) != 0); + if (changed & DIVA_MGT_DBG_IFC_AUDIO) { + int i, state = ((pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_AUDIO) != 0); - for (i = 0; i < pC->channels; i++) { - (*(pC->pIdiLib->DivaSTraceSetAudioTap))(pC->pIdiLib, i+1, state); + for (i = 0; i < pC->channels; i++) { + (*(pC->pIdiLib->DivaSTraceSetAudioTap))(pC->pIdiLib, i+1, state); + } } } } @@ -1743,8 +1928,8 @@ int diva_mnt_shutdown_xdi_adapters (void for (i = 1; i < (sizeof(clients)/sizeof(clients[0])); i++) { pmem = NULL; - diva_os_enter_spin_lock_hard (&dbg_adapter_lock, &old_irql1, "unload"); - diva_os_enter_spin_lock_hard (&dbg_q_lock, &old_irql, "unload"); + diva_os_enter_spin_lock (&dbg_adapter_lock, &old_irql1, "unload"); + diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "unload"); if (clients[i].hDbg && clients[i].pIdiLib && clients[i].request) { if ((*(clients[i].pIdiLib->DivaSTraceLibraryStop))(clients[i].pIdiLib) == 1) { @@ -1759,19 +1944,31 @@ int diva_mnt_shutdown_xdi_adapters (void clients[i].pmem = NULL; } clients[i].hDbg = NULL; - clients[i].request = NULL; clients[i].request_pending = 0; + + if (clients[i].dma_handle >= 0) { + /* + Free DMA handle + */ + diva_free_dma_descriptor (clients[i].request, clients[i].dma_handle); + clients[i].dma_handle = -1; + } + clients[i].request = NULL; } else { fret = -1; } } - diva_os_leave_spin_lock_hard (&dbg_q_lock, &old_irql, "unload"); + diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "unload"); if (clients[i].hDbg && clients[i].pIdiLib && clients[i].request && clients[i].request_pending) { clients[i].request_pending = 0; (*(clients[i].request))((ENTITY*)(*(clients[i].pIdiLib->DivaSTraceGetHandle))(clients[i].pIdiLib->hLib)); + if (clients[i].dma_handle >= 0) { + diva_free_dma_descriptor (clients[i].request, clients[i].dma_handle); + clients[i].dma_handle = -1; + } } - diva_os_leave_spin_lock_hard (&dbg_adapter_lock, &old_irql1, "unload"); + diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "unload"); if (pmem) { diva_os_free (0, pmem); @@ -1781,3 +1978,156 @@ int diva_mnt_shutdown_xdi_adapters (void return (fret); } +/* + Set/Read the trace filter used for selective tracing. + Affects B- and Audio Tap trace mask at run time + */ +int diva_set_trace_filter (int filter_length, const char* filter) { + diva_os_spin_lock_magic_t old_irql, old_irql1; + int i, ch, on, client_b_on, client_atap_on; + + diva_os_enter_spin_lock (&dbg_adapter_lock, &old_irql1, "dbg mask"); + diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "write_filter"); + + if (filter_length <= DIVA_MAX_SELECTIVE_FILTER_LENGTH) { + memcpy (&TraceFilter[0], filter, filter_length); + if (TraceFilter[filter_length]) { + TraceFilter[filter_length] = 0; + } + if (TraceFilter[0] == '*') { + TraceFilter[0] = 0; + } + } else { + filter_length = -1; + } + + TraceFilterIdent = -1; + TraceFilterChannel = -1; + + on = (TraceFilter[0] == 0); + + for (i = 1; i < (sizeof(clients)/sizeof(clients[0])); i++) { + if (clients[i].hDbg && clients[i].pIdiLib && clients[i].request) { + client_b_on = on && ((clients[i].hDbg->dbgMask & DIVA_MGT_DBG_IFC_BCHANNEL) != 0); + client_atap_on = on && ((clients[i].hDbg->dbgMask & DIVA_MGT_DBG_IFC_AUDIO) != 0); + for (ch = 0; ch < clients[i].channels; ch++) { + (*(clients[i].pIdiLib->DivaSTraceSetBChannel))(clients[i].pIdiLib->hLib, ch+1, client_b_on); + (*(clients[i].pIdiLib->DivaSTraceSetAudioTap))(clients[i].pIdiLib->hLib, ch+1, client_atap_on); + } + } + } + + for (i = 1; i < (sizeof(clients)/sizeof(clients[0])); i++) { + if (clients[i].hDbg && clients[i].pIdiLib && clients[i].request && clients[i].request_pending) { + diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "write_filter"); + clients[i].request_pending = 0; + (*(clients[i].request))((ENTITY*)(*(clients[i].pIdiLib->DivaSTraceGetHandle))(clients[i].pIdiLib->hLib)); + diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "write_filter"); + } + } + + diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "write_filter"); + diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "dbg mask"); + + return (filter_length); +} + +int diva_get_trace_filter (int max_length, char* filter) { + diva_os_spin_lock_magic_t old_irql; + int len; + + diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "read_filter"); + len = strlen (&TraceFilter[0]) + 1; + if (max_length >= len) { + memcpy (filter, &TraceFilter[0], len); + } + diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "read_filter"); + + return (len); +} + +static int diva_dbg_cmp_key (const char* ref, const char* key) { + while (*key && (*ref++ == *key++)); + return (!*key && !*ref); +} + +/* + In case trace filter starts with "C" character then + all following characters are interpreted as command. + Followings commands are available: + - single, trace single call at time, independent from CPN/CiPN + */ +static int diva_mnt_cmp_nmbr (const char* nmbr) { + const char* ref = &TraceFilter[0]; + int ref_len = strlen(&TraceFilter[0]), nmbr_len = strlen(nmbr); + + if (ref[0] == 'C') { + if (diva_dbg_cmp_key (&ref[1], "single")) { + return (0); + } + return (-1); + } + + if (!ref_len || (ref_len > nmbr_len)) { + return (-1); + } + + nmbr = nmbr + nmbr_len - 1; + ref = ref + ref_len - 1; + + while (ref_len--) { + if (*nmbr-- != *ref--) { + return (-1); + } + } + + return (0); +} + +static int diva_get_dma_descriptor (IDI_CALL request, dword *dma_magic) { + ENTITY e; + IDI_SYNC_REQ* pReq = (IDI_SYNC_REQ*)&e; + + if (!request) { + return (-1); + } + + pReq->xdi_dma_descriptor_operation.Req = 0; + pReq->xdi_dma_descriptor_operation.Rc = IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION; + + pReq->xdi_dma_descriptor_operation.info.operation = IDI_SYNC_REQ_DMA_DESCRIPTOR_ALLOC; + pReq->xdi_dma_descriptor_operation.info.descriptor_number = -1; + pReq->xdi_dma_descriptor_operation.info.descriptor_address = NULL; + pReq->xdi_dma_descriptor_operation.info.descriptor_magic = 0; + + (*request)((ENTITY*)pReq); + + if (!pReq->xdi_dma_descriptor_operation.info.operation && + (pReq->xdi_dma_descriptor_operation.info.descriptor_number >= 0) && + pReq->xdi_dma_descriptor_operation.info.descriptor_magic) { + *dma_magic = pReq->xdi_dma_descriptor_operation.info.descriptor_magic; + return (pReq->xdi_dma_descriptor_operation.info.descriptor_number); + } else { + return (-1); + } +} + +static void diva_free_dma_descriptor (IDI_CALL request, int nr) { + ENTITY e; + IDI_SYNC_REQ* pReq = (IDI_SYNC_REQ*)&e; + + if (!request || (nr < 0)) { + return; + } + + pReq->xdi_dma_descriptor_operation.Req = 0; + pReq->xdi_dma_descriptor_operation.Rc = IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION; + + pReq->xdi_dma_descriptor_operation.info.operation = IDI_SYNC_REQ_DMA_DESCRIPTOR_FREE; + pReq->xdi_dma_descriptor_operation.info.descriptor_number = nr; + pReq->xdi_dma_descriptor_operation.info.descriptor_address = NULL; + pReq->xdi_dma_descriptor_operation.info.descriptor_magic = 0; + + (*request)((ENTITY*)pReq); +} + --- linux-2.6.9-rc1/drivers/isdn/hardware/eicon/debug_if.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/isdn/hardware/eicon/debug_if.h 2004-09-02 20:51:51.000000000 -0700 @@ -57,12 +57,18 @@ void diva_mnt_remove_xdi_adapter (const void diva_mnt_add_xdi_adapter (const DESCRIPTOR* d); int diva_mnt_shutdown_xdi_adapters (void); +#define DIVA_MAX_SELECTIVE_FILTER_LENGTH 127 +int diva_set_trace_filter (int filter_length, const char* filter); +int diva_get_trace_filter (int max_length, char* filter); + #define DITRACE_CMD_GET_DRIVER_INFO 1 #define DITRACE_READ_DRIVER_DBG_MASK 2 #define DITRACE_WRITE_DRIVER_DBG_MASK 3 #define DITRACE_READ_TRACE_ENTRY 4 #define DITRACE_READ_TRACE_ENTRYS 5 +#define DITRACE_WRITE_SELECTIVE_TRACE_FILTER 6 +#define DITRACE_READ_SELECTIVE_TRACE_FILTER 7 /* Trace lavels for debug via management interface --- linux-2.6.9-rc1/drivers/isdn/hardware/eicon/debuglib.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/isdn/hardware/eicon/debuglib.h 2004-09-02 20:51:51.000000000 -0700 @@ -232,7 +232,7 @@ typedef struct _DbgHandle_ *pDbgHandle ; typedef void ( * DbgEnd) (pDbgHandle) ; typedef void ( * DbgLog) (unsigned short, int, char *, va_list) ; typedef void ( * DbgOld) (unsigned short, char *, va_list) ; -typedef void ( * DbgEv) (unsigned short, unsigned int, va_list) ; +typedef void ( * DbgEv) (unsigned short, unsigned long, va_list) ; typedef void ( * DbgIrq) (unsigned short, int, char *, va_list) ; typedef struct _DbgHandle_ { char Registered ; /* driver successfull registered */ @@ -259,7 +259,7 @@ typedef struct _DbgHandle_ void *pReserved3 ; } _DbgHandle_ ; extern _DbgHandle_ myDriverDebugHandle ; -typedef struct +typedef struct _OldDbgHandle_ { struct _OldDbgHandle_ *next ; void *pIrp ; long regTime[2] ; @@ -310,7 +310,7 @@ typedef struct unsigned long B_ChannelMask; unsigned long LogBufferSize; } CardTrace; - } u1; + }Data; } _DbgExtendedInfo_; #ifndef DIVA_NO_DEBUGLIB /* ------------------------------------------------------------- --- linux-2.6.9-rc1/drivers/isdn/hardware/eicon/diddfunc.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/isdn/hardware/eicon/diddfunc.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: diddfunc.c,v 1.14 2003/08/25 10:06:37 schindler Exp $ +/* $Id: diddfunc.c,v 1.14.6.2 2004/08/28 20:03:53 armin Exp $ * * DIDD Interface module for Eicon active cards. * @@ -16,8 +16,6 @@ #include "dadapter.h" #include "divasync.h" -#define MAX_DESCRIPTORS 32 - #define DBG_MINIMUM (DL_LOG + DL_FTL + DL_ERR) #define DBG_DEFAULT (DBG_MINIMUM + DL_XLOG + DL_REG) --- linux-2.6.9-rc1/drivers/isdn/hardware/eicon/diva_didd.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/isdn/hardware/eicon/diva_didd.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: diva_didd.c,v 1.13 2003/08/27 10:11:21 schindler Exp $ +/* $Id: diva_didd.c,v 1.13.6.1 2004/08/28 20:03:53 armin Exp $ * * DIDD Interface module for Eicon active cards. * @@ -23,7 +23,7 @@ #include "divasync.h" #include "did_vers.h" -static char *main_revision = "$Revision: 1.13 $"; +static char *main_revision = "$Revision: 1.13.6.1 $"; static char *DRIVERNAME = "Eicon DIVA - DIDD table (http://www.melware.net)"; @@ -37,8 +37,6 @@ MODULE_AUTHOR("Cytronics & Melware, Eico MODULE_SUPPORTED_DEVICE("Eicon diva drivers"); MODULE_LICENSE("GPL"); -#define MAX_DESCRIPTORS 32 - #define DBG_MINIMUM (DL_LOG + DL_FTL + DL_ERR) #define DBG_DEFAULT (DBG_MINIMUM + DL_XLOG + DL_REG) --- linux-2.6.9-rc1/drivers/isdn/hardware/eicon/divamnt.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/isdn/hardware/eicon/divamnt.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: divamnt.c,v 1.32 2004/01/15 09:48:13 armin Exp $ +/* $Id: divamnt.c,v 1.32.6.5 2004/08/28 20:03:53 armin Exp $ * * Driver for Eicon DIVA Server ISDN cards. * Maint module @@ -17,8 +17,6 @@ #include #include #include -#include -#include #include #include "platform.h" @@ -26,7 +24,7 @@ #include "divasync.h" #include "debug_if.h" -static char *main_revision = "$Revision: 1.32 $"; +static char *main_revision = "$Revision: 1.32.6.5 $"; static int major; @@ -47,8 +45,7 @@ static char *DEVNAME = "DivasMAINT"; char *DRIVERRELEASE_MNT = "2.0"; static wait_queue_head_t msgwaitq; -static DECLARE_MUTEX(opened_sem); -static int opened; +static unsigned long opened; static struct timeval start_time; extern int mntfunc_init(int *, void **, unsigned long); @@ -74,20 +71,6 @@ static char *getrev(const char *revision } /* - * buffer alloc - */ -void *diva_os_malloc_tbuffer(unsigned long flags, unsigned long size) -{ - return (kmalloc(size, GFP_KERNEL)); -} -void diva_os_free_tbuffer(unsigned long flags, void *ptr) -{ - if (ptr) { - kfree(ptr); - } -} - -/* * kernel/user space copy functions */ int diva_os_copy_to_user(void *os_handle, void __user *dst, const void *src, @@ -131,154 +114,8 @@ void diva_os_get_time(dword * sec, dword } /* - * /proc entries + * device node operations */ - -extern struct proc_dir_entry *proc_net_eicon; -static struct proc_dir_entry *maint_proc_entry = NULL; - -/* - Read function is provided for compatibility reason - this allows - to read unstructured traces, formated as ascii string only - */ -static ssize_t -maint_read(struct file *file, char __user *buf, size_t count, loff_t * off) -{ - diva_dbg_entry_head_t *pmsg = NULL; - diva_os_spin_lock_magic_t old_irql; - word size; - char *pstr, *dli_label = "UNK"; - int str_length; - int *str_msg; - - if (!file->private_data) { - for (;;) { - while ( - (pmsg = - diva_maint_get_message(&size, - &old_irql))) { - if (!(pmsg->facility == MSG_TYPE_STRING)) { - diva_maint_ack_message(1, - &old_irql); - } else { - break; - } - } - - if (!pmsg) { - if (file->f_flags & O_NONBLOCK) { - return (-EAGAIN); - } - interruptible_sleep_on(&msgwaitq); - if (signal_pending(current)) { - return (-ERESTARTSYS); - } - } else { - break; - } - } - /* - The length of message that shoule be read is: - pmsg->data_length + label(25) + DrvID(2) + byte CR + trailing zero - */ - if (! - (str_msg = - (int *) diva_os_malloc_tbuffer(0, - pmsg->data_length + - 29 + 2 * sizeof(int)))) { - diva_maint_ack_message(0, &old_irql); - return (-ENOMEM); - } - pstr = (char *) &str_msg[2]; - - switch (pmsg->dli) { - case DLI_LOG: - dli_label = "LOG"; - break; - case DLI_FTL: - dli_label = "FTL"; - break; - case DLI_ERR: - dli_label = "ERR"; - break; - case DLI_TRC: - dli_label = "TRC"; - break; - case DLI_REG: - dli_label = "REG"; - break; - case DLI_MEM: - dli_label = "MEM"; - break; - case DLI_SPL: - dli_label = "SPL"; - break; - case DLI_IRP: - dli_label = "IRP"; - break; - case DLI_TIM: - dli_label = "TIM"; - break; - case DLI_TAPI: - dli_label = "TAPI"; - break; - case DLI_NDIS: - dli_label = "NDIS"; - break; - case DLI_CONN: - dli_label = "CONN"; - break; - case DLI_STAT: - dli_label = "STAT"; - break; - case DLI_PRV0: - dli_label = "PRV0"; - break; - case DLI_PRV1: - dli_label = "PRV1"; - break; - case DLI_PRV2: - dli_label = "PRV2"; - break; - case DLI_PRV3: - dli_label = "PRV3"; - break; - } - str_length = sprintf(pstr, "%s %02x %s\n", - dli_label, (byte) pmsg->drv_id, - (char *) &pmsg[1]); - str_msg[0] = str_length; - str_msg[1] = 0; - file->private_data = str_msg; - diva_maint_ack_message(1, &old_irql); - } else { - str_msg = (int *) file->private_data; - pstr = (char *) &str_msg[2]; - pstr += str_msg[1]; /* head + offset */ - str_length = str_msg[0] - str_msg[1]; /* length - offset */ - } - str_length = MIN(str_length, count); - - if (diva_os_copy_to_user(NULL, buf, pstr, str_length)) { - diva_os_free_tbuffer(0, str_msg); - file->private_data = NULL; - return (-EFAULT); - } - str_msg[1] += str_length; - if ((str_msg[0] - str_msg[1]) <= 0) { - diva_os_free_tbuffer(0, str_msg); - file->private_data = NULL; - } - - return (str_length); -} - -static ssize_t -maint_write(struct file *file, const char __user *buf, size_t count, loff_t * off) -{ - return (-ENODEV); -} - static unsigned int maint_poll(struct file *file, poll_table * wait) { unsigned int mask = 0; @@ -293,13 +130,10 @@ static unsigned int maint_poll(struct fi static int maint_open(struct inode *ino, struct file *filep) { - down(&opened_sem); - if (opened) { - up(&opened_sem); + /* only one open is allowed, so we test + it atomically */ + if (test_and_set_bit(0, &opened)) return (-EBUSY); - } - opened++; - up(&opened_sem); filep->private_data = NULL; @@ -309,54 +143,16 @@ static int maint_open(struct inode *ino, static int maint_close(struct inode *ino, struct file *filep) { if (filep->private_data) { - diva_os_free_tbuffer(0, filep->private_data); + diva_os_free(0, filep->private_data); filep->private_data = NULL; } - down(&opened_sem); - opened--; - up(&opened_sem); + /* clear 'used' flag */ + clear_bit(0, &opened); + return (0); } -/* - * fops - */ -static struct file_operations maint_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = maint_read, - .write = maint_write, - .poll = maint_poll, - .open = maint_open, - .release = maint_close -}; - -static int DIVA_INIT_FUNCTION create_maint_proc(void) -{ - maint_proc_entry = - create_proc_entry("maint", S_IFREG | S_IRUGO | S_IWUSR, - proc_net_eicon); - if (!maint_proc_entry) - return (0); - - maint_proc_entry->proc_fops = &maint_fops; - maint_proc_entry->owner = THIS_MODULE; - - return (1); -} - -static void remove_maint_proc(void) -{ - if (maint_proc_entry) { - remove_proc_entry("maint", proc_net_eicon); - maint_proc_entry = NULL; - } -} - -/* - * device node operations - */ static ssize_t divas_maint_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) { @@ -427,18 +223,10 @@ static int DIVA_INIT_FUNCTION maint_init ret = -EIO; goto out; } - if (!create_maint_proc()) { - printk(KERN_ERR "%s: failed to create proc entry.\n", - DRIVERLNAME); - divas_maint_unregister_chrdev(); - ret = -EIO; - goto out; - } if (!(mntfunc_init(&buffer_length, &buffer, diva_dbg_mem))) { printk(KERN_ERR "%s: failed to connect to DIDD.\n", DRIVERLNAME); - remove_maint_proc(); divas_maint_unregister_chrdev(); ret = -EIO; goto out; @@ -457,7 +245,6 @@ static int DIVA_INIT_FUNCTION maint_init */ static void DIVA_EXIT_FUNCTION maint_exit(void) { - remove_maint_proc(); divas_maint_unregister_chrdev(); mntfunc_finit(); @@ -466,3 +253,4 @@ static void DIVA_EXIT_FUNCTION maint_exi module_init(maint_init); module_exit(maint_exit); + --- linux-2.6.9-rc1/drivers/isdn/hardware/eicon/divasfunc.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/isdn/hardware/eicon/divasfunc.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: divasfunc.c,v 1.23 2004/04/08 01:17:57 armin Exp $ +/* $Id: divasfunc.c,v 1.23.4.2 2004/08/28 20:03:53 armin Exp $ * * Low level driver for Eicon DIVA Server ISDN cards. * @@ -27,8 +27,6 @@ extern void DIVA_DIDD_Read(void *, int); extern PISDN_ADAPTER IoAdapters[MAX_ADAPTER]; -#define MAX_DESCRIPTORS 32 - extern char *DRIVERRELEASE_DIVAS; static dword notify_handle; @@ -76,10 +74,10 @@ void diva_xdi_didd_register_adapter(int d.features = IoAdapters[card - 1]->Properties.Features; DBG_TRC(("DIDD register A(%d) channels=%d", card, d.channels)) - /* workaround for different Name in structure */ - strlcpy(IoAdapters[card - 1]->Name, - IoAdapters[card - 1]->Properties.Name, - sizeof(IoAdapters[card - 1]->Name)); + /* workaround for different Name in structure */ + strlcpy(IoAdapters[card - 1]->Name, + IoAdapters[card - 1]->Properties.Name, + sizeof(IoAdapters[card - 1]->Name)); req.didd_remove_adapter.e.Req = 0; req.didd_add_adapter.e.Rc = IDI_SYNC_REQ_DIDD_ADD_ADAPTER; req.didd_add_adapter.info.descriptor = (void *) &d; --- linux-2.6.9-rc1/drivers/isdn/hardware/eicon/divasync.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/isdn/hardware/eicon/divasync.h 2004-09-02 20:51:51.000000000 -0700 @@ -31,33 +31,31 @@ #define IDI_SYNC_REQ_SET_POSTCALL 0x03 #define IDI_SYNC_REQ_GET_XLOG 0x04 #define IDI_SYNC_REQ_GET_FEATURES 0x05 -/* Added for DIVA USB support */ #define IDI_SYNC_REQ_USB_REGISTER 0x06 #define IDI_SYNC_REQ_USB_RELEASE 0x07 #define IDI_SYNC_REQ_USB_ADD_DEVICE 0x08 #define IDI_SYNC_REQ_USB_START_DEVICE 0x09 #define IDI_SYNC_REQ_USB_STOP_DEVICE 0x0A #define IDI_SYNC_REQ_USB_REMOVE_DEVICE 0x0B -/* Added for Diva Server Monitor */ #define IDI_SYNC_REQ_GET_CARDTYPE 0x0C #define IDI_SYNC_REQ_GET_DBG_XLOG 0x0D -#define IDI_SYNC_REQ_GET_LINE_IDX 0x0E #define DIVA_USB #define DIVA_USB_REQ 0xAC #define DIVA_USB_TEST 0xAB #define DIVA_USB_ADD_ADAPTER 0xAC #define DIVA_USB_REMOVE_ADAPTER 0xAD -/******************************************************************************/ #define IDI_SYNC_REQ_SERIAL_HOOK 0x80 #define IDI_SYNC_REQ_XCHANGE_STATUS 0x81 #define IDI_SYNC_REQ_USB_HOOK 0x82 #define IDI_SYNC_REQ_PORTDRV_HOOK 0x83 -#define IDI_SYNC_REQ_SLI (0x84) /* SLI request from 3signal modem drivers */ +#define IDI_SYNC_REQ_SLI 0x84 /* SLI request from 3signal modem drivers */ #define IDI_SYNC_REQ_RECONFIGURE 0x85 #define IDI_SYNC_REQ_RESET 0x86 +#define IDI_SYNC_REQ_GET_85X_DEVICE_DATA 0x87 #define IDI_SYNC_REQ_LOCK_85X 0x88 +#define IDI_SYNC_REQ_DIVA_85X_USB_DATA_EXCHANGE 0x99 +#define IDI_SYNC_REQ_DIPORT_EXCHANGE_REQ 0x98 #define IDI_SYNC_REQ_GET_85X_EXT_PORT_TYPE 0xA0 -#define IDI_SYNC_REQ_DIPORT_GET_85X_TX_CTRL_FN 0x98 /******************************************************************************/ #define IDI_SYNC_REQ_XDI_GET_EXTENDED_FEATURES 0x92 /* @@ -87,6 +85,8 @@ typedef struct _diva_xdi_get_extended_xd #define DIVA_XDI_EXTENDED_FEATURE_CAPI_PRMS 0x08 #define DIVA_XDI_EXTENDED_FEATURE_NO_CANCEL_RC 0x10 #define DIVA_XDI_EXTENDED_FEATURE_RX_DMA 0x20 +#define DIVA_XDI_EXTENDED_FEATURE_MANAGEMENT_DMA 0x40 +#define DIVA_XDI_EXTENDED_FEATURE_WIDE_ID 0x80 #define DIVA_XDI_EXTENDED_FEATURES_MAX_SZ 1 /******************************************************************************/ #define IDI_SYNC_REQ_XDI_GET_ADAPTER_SDRAM_BAR 0x93 @@ -115,6 +115,7 @@ typedef struct _diva_xdi_get_capi_parame typedef struct _diva_xdi_get_logical_adapter_number { dword logical_adapter_number; dword controller; + dword total_controllers; } diva_xdi_get_logical_adapter_number_s_t; /******************************************************************************/ #define IDI_SYNC_REQ_UP1DM_OPERATION 0x96 @@ -134,6 +135,7 @@ typedef struct _diva_xdi_dma_descriptor_ #define IDI_SYNC_REQ_DIDD_ADD_ADAPTER 0x03 #define IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER 0x04 #define IDI_SYNC_REQ_DIDD_READ_ADAPTER_ARRAY 0x05 +#define IDI_SYNC_REQ_DIDD_GET_CFG_LIB_IFC 0x10 typedef struct _diva_didd_adapter_notify { dword handle; /* Notification handle */ void * callback; @@ -149,6 +151,9 @@ typedef struct _diva_didd_read_adapter_a void * buffer; dword length; } diva_didd_read_adapter_array_t; +typedef struct _diva_didd_get_cfg_lib_ifc { + void* ifc; +} diva_didd_get_cfg_lib_ifc_t; /******************************************************************************/ #define IDI_SYNC_REQ_XDI_GET_STREAM 0x91 #define DIVA_XDI_SYNCHRONOUS_SERVICE 0x01 @@ -466,6 +471,10 @@ typedef union ENTITY e; diva_didd_read_adapter_array_t info; } didd_read_adapter_array; + struct { + ENTITY e; + diva_didd_get_cfg_lib_ifc_t info; + } didd_get_cfg_lib_ifc; struct { unsigned char Req; unsigned char Rc; --- linux-2.6.9-rc1/drivers/isdn/hardware/eicon/idifunc.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/drivers/isdn/hardware/eicon/idifunc.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: idifunc.c,v 1.14.4.2 2004/05/09 16:42:20 armin Exp $ +/* $Id: idifunc.c,v 1.14.4.4 2004/08/28 20:03:53 armin Exp $ * * Driver for Eicon DIVA Server ISDN cards. * User Mode IDI Interface @@ -25,8 +25,6 @@ extern void DIVA_DIDD_Read(void *, int); extern int diva_user_mode_idi_create_adapter(const DESCRIPTOR *, int); extern void diva_user_mode_idi_remove_adapter(int); -#define MAX_DESCRIPTORS 32 - static dword notify_handle; static DESCRIPTOR DAdapter; static DESCRIPTOR MAdapter; --- linux-2.6.9-rc1/drivers/isdn/hardware/eicon/io.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/drivers/isdn/hardware/eicon/io.c 2004-09-02 20:51:51.000000000 -0700 @@ -77,6 +77,7 @@ static byte extended_xdi_features[DIVA_X #if defined(DIVA_IDI_RX_DMA) DIVA_XDI_EXTENDED_FEATURE_CMA | DIVA_XDI_EXTENDED_FEATURE_RX_DMA | + DIVA_XDI_EXTENDED_FEATURE_MANAGEMENT_DMA | #endif DIVA_XDI_EXTENDED_FEATURE_NO_CANCEL_RC), 0 @@ -226,8 +227,10 @@ void request(PISDN_ADAPTER IoAdapter, EN if (pI->descriptor_number >= 0) { dword dma_magic; void* local_addr; +#if 0 DBG_TRC(("A(%d) dma_alloc(%d)", IoAdapter->ANum, pI->descriptor_number)) +#endif diva_get_dma_map_entry (\ (struct _diva_dma_map_entry*)IoAdapter->dma_map, pI->descriptor_number, @@ -240,7 +243,9 @@ void request(PISDN_ADAPTER IoAdapter, EN } } else if ((pI->operation == IDI_SYNC_REQ_DMA_DESCRIPTOR_FREE) && (pI->descriptor_number >= 0)) { +#if 0 DBG_TRC(("A(%d) dma_free(%d)", IoAdapter->ANum, pI->descriptor_number)) +#endif diva_free_dma_map_entry((struct _diva_dma_map_entry*)IoAdapter->dma_map, pI->descriptor_number); pI->descriptor_number = -1; @@ -257,6 +262,7 @@ void request(PISDN_ADAPTER IoAdapter, EN &syncReq->xdi_logical_adapter_number.info; pI->logical_adapter_number = IoAdapter->ANum; pI->controller = IoAdapter->ControllerNumber; + pI->total_controllers = IoAdapter->Properties.Adapters; } return; case IDI_SYNC_REQ_XDI_GET_CAPI_PARAMS: { diva_xdi_get_capi_parameters_t prms, *pI = &syncReq->xdi_capi_prms.info; @@ -318,6 +324,16 @@ void request(PISDN_ADAPTER IoAdapter, EN } syncReq->GetSerial.serial = 0 ; break ; + case IDI_SYNC_REQ_GET_CARDTYPE: + if ( IoAdapter ) + { + syncReq->GetCardType.cardtype = IoAdapter->cardType ; + DBG_TRC(("xdi: Adapter %d / CardType %ld", + IoAdapter->ANum, IoAdapter->cardType)) + return ; + } + syncReq->GetCardType.cardtype = 0 ; + break ; case IDI_SYNC_REQ_GET_XLOG: if ( IoAdapter ) { @@ -326,6 +342,14 @@ void request(PISDN_ADAPTER IoAdapter, EN } e->Ind = 0 ; break ; + case IDI_SYNC_REQ_GET_DBG_XLOG: + if ( IoAdapter ) + { + pcm_req (IoAdapter, e) ; + return ; + } + e->Ind = 0 ; + break ; case IDI_SYNC_REQ_GET_FEATURES: if ( IoAdapter ) { @@ -345,7 +369,9 @@ void request(PISDN_ADAPTER IoAdapter, EN } if ( IoAdapter ) { +#if 0 DBG_FTL(("xdi: unknown Req 0 / Rc %d !", e->Rc)) +#endif return ; } } @@ -496,7 +522,7 @@ pcm_req (PISDN_ADAPTER IoAdapter, ENTITY diva_os_enter_spin_lock (&IoAdapter->data_spin_lock, &OldIrql, "data_pcm_1"); - IoAdapter->pcm_data = (unsigned long)pcm; + IoAdapter->pcm_data = (void *)pcm; IoAdapter->pcm_pending = 1; diva_os_schedule_soft_isr (&IoAdapter->req_soft_isr); diva_os_leave_spin_lock (&IoAdapter->data_spin_lock, @@ -510,7 +536,7 @@ pcm_req (PISDN_ADAPTER IoAdapter, ENTITY &OldIrql, "data_pcm_3"); IoAdapter->pcm_pending = 0; - IoAdapter->pcm_data = 0; + IoAdapter->pcm_data = NULL ; diva_os_leave_spin_lock (&IoAdapter->data_spin_lock, &OldIrql, "data_pcm_3"); @@ -528,7 +554,7 @@ pcm_req (PISDN_ADAPTER IoAdapter, ENTITY &OldIrql, "data_pcm_4"); IoAdapter->pcm_pending = 0; - IoAdapter->pcm_data = 0; + IoAdapter->pcm_data = NULL ; diva_os_leave_spin_lock (&IoAdapter->data_spin_lock, &OldIrql, "data_pcm_4"); @@ -668,7 +694,7 @@ word io_inw(ADAPTER * a, void * adr) void io_in_buffer(ADAPTER * a, void * adr, void * buffer, word len) { byte *Port = (byte*)DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io); - byte* P = (byte*)buffer; + byte* P = (byte*)buffer; if ((long)adr & 1) { outppw(Port+4, (word)(unsigned long)adr); *P = inpp(Port); @@ -678,7 +704,7 @@ void io_in_buffer(ADAPTER * a, void * ad if (!len) { DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port); return; - } + } } outppw(Port+4, (word)(unsigned long)adr); inppw_buffer (Port, P, len+1); @@ -710,7 +736,7 @@ void io_outw(ADAPTER * a, void * adr, wo void io_out_buffer(ADAPTER * a, void * adr, void * buffer, word len) { byte *Port = (byte*)DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io); - byte* P = (byte*)buffer; + byte* P = (byte*)buffer; if ((long)adr & 1) { outppw(Port+4, (word)(unsigned long)adr); outpp(Port, *P); @@ -839,21 +865,21 @@ void CALLBACK(ADAPTER * a, ENTITY * e) /* -------------------------------------------------------------------------- routines for aligned reading and writing on RISC -------------------------------------------------------------------------- */ -void outp_words_from_buffer (word* adr, byte* P, word len) +void outp_words_from_buffer (word* adr, byte* P, dword len) { - word i = 0; + dword i = 0; word w; - while (i < (len & 0xfffe)) { + while (i < (len & 0xfffffffe)) { w = P[i++]; w += (P[i++])<<8; outppw (adr, w); } } -void inp_words_to_buffer (word* adr, byte* P, word len) +void inp_words_to_buffer (word* adr, byte* P, dword len) { - word i = 0; + dword i = 0; word w; - while (i < (len & 0xfffe)) { + while (i < (len & 0xfffffffe)) { w = inppw (adr); P[i++] = (byte)(w); P[i++] = (byte)(w>>8); --- linux-2.6.9-rc1/drivers/isdn/hardware/eicon/io.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/isdn/hardware/eicon/io.h 2004-09-02 20:51:51.000000000 -0700 @@ -40,6 +40,14 @@ typedef struct { PISDN_ADAPTER QuadroAdapter[4] ; } ADAPTER_LIST_ENTRY, *PADAPTER_LIST_ENTRY ; /* -------------------------------------------------------------------------- + Special OS memory support structures + -------------------------------------------------------------------------- */ +#define MAX_MAPPED_ENTRIES 8 +typedef struct { + void * Address; + dword Length; +} ADAPTER_MEMORY ; +/* -------------------------------------------------------------------------- Configuration of XDI clients carried by XDI -------------------------------------------------------------------------- */ #define DIVA_XDI_CAPI_CFG_1_DYNAMIC_L1_ON 0x01 @@ -52,6 +60,7 @@ typedef struct _diva_xdi_capi_cfg { -------------------------------------------------------------------------- */ struct _ISDN_ADAPTER { void (* DIRequest)(PISDN_ADAPTER, ENTITY *) ; + int State ; /* from NT4 1.srv, a good idea, but a poor achievment */ int Initialized ; int RegisteredWithDidd ; int Unavailable ; /* callback function possible? */ @@ -63,6 +72,7 @@ struct _ISDN_ADAPTER { /* remember mapped memory areas */ + ADAPTER_MEMORY MappedMemory[MAX_MAPPED_ENTRIES] ; CARD_PROPERTIES Properties ; dword cardType ; dword protocol_id ; /* configured protocol identifier */ @@ -87,15 +97,15 @@ struct _ISDN_ADAPTER { dword downloadAddrTable[4] ; /* add. for MultiMaster */ dword MemoryBase ; dword MemorySize ; - byte *Address ; + byte *Address ; byte *Config ; byte *Control ; - byte *reset ; - byte *port ; - byte *ram ; - byte *cfg ; - byte *prom ; - byte *ctlReg ; + byte *reset ; + byte *port ; + byte *ram ; + byte *cfg ; + byte *prom ; + byte *ctlReg ; struct pc_maint *pcm ; diva_os_dependent_devica_name_t os_name; byte Name[32] ; @@ -105,6 +115,7 @@ struct _ISDN_ADAPTER { char *ProtocolSuffix ; /* internal protocolfile table */ char Archive[32] ; char Protocol[32] ; + char AddDownload[32] ; /* Dsp- or other additional download files */ char Oad1[ISDN_MAX_NUM_LEN] ; char Osa1[ISDN_MAX_NUM_LEN] ; char Oad2[ISDN_MAX_NUM_LEN] ; @@ -153,8 +164,26 @@ struct _ISDN_ADAPTER { byte ModemCarrierWaitTimeSec; byte ModemCarrierLossWaitTimeTenthSec; byte PiafsLinkTurnaroundInFrames; + byte DiscAfterProgress; + byte AniDniLimiter[3]; + byte TxAttenuation; /* PRI/E1 only: attenuate TX signal */ word QsigFeatures; dword GenerateRingtone ; + dword SupplementaryServicesFeatures; + dword R2Dialect; + dword R2CasOptions; + dword FaxV34Options; + dword DisabledDspMask; + dword AdapterTestMask; + dword DspImageLength; + word AlertToIn20mSecTicks; + word ModemEyeSetup; + byte R2CtryLength; + byte CCBSRelTimer; + byte *PcCfgBufferFile;/* flexible parameter via file */ + byte *PcCfgBuffer ; /* flexible parameter via multistring */ + diva_os_dump_file_t dump_file; /* dump memory to file at lowest irq level */ + diva_os_board_trace_t board_trace ; /* traces from the board */ diva_os_spin_lock_t isr_spin_lock; diva_os_spin_lock_t data_spin_lock; diva_os_soft_isr_t req_soft_isr; @@ -180,15 +209,21 @@ struct _ISDN_ADAPTER { void (* stop)(PISDN_ADAPTER) ; void (* rstFnc)(PISDN_ADAPTER) ; void (* trapFnc)(PISDN_ADAPTER) ; + dword (* DetectDsps)(PISDN_ADAPTER) ; void (* os_trap_nfy_Fnc)(PISDN_ADAPTER, dword) ; diva_os_isr_callback_t diva_isr_handler; - dword sdram_bar; + dword sdram_bar; /* must be 32 bit */ dword fpga_features; volatile int pcm_pending; - volatile unsigned long pcm_data; + volatile void * pcm_data; diva_xdi_capi_cfg_t capi_cfg; dword tasks; - void* dma_map; + void *dma_map; + int (*DivaAdapterTestProc)(PISDN_ADAPTER); + void *AdapterTestMemoryStart; + dword AdapterTestMemoryLength; + const byte* cfg_lib_memory_init; + dword cfg_lib_memory_init_length; }; /* --------------------------------------------------------------------- Entity table @@ -219,8 +254,8 @@ struct s_load { /* --------------------------------------------------------------------- Functions for port io --------------------------------------------------------------------- */ -void outp_words_from_buffer (word* adr, byte* P, word len); -void inp_words_to_buffer (word* adr, byte* P, word len); +void outp_words_from_buffer (word* adr, byte* P, dword len); +void inp_words_to_buffer (word* adr, byte* P, dword len); /* --------------------------------------------------------------------- platform specific conversions --------------------------------------------------------------------- */ @@ -240,6 +275,10 @@ void io_out(ADAPTER * a, void * adr, byt void io_outw(ADAPTER * a, void * adr, word data); void io_out_buffer(ADAPTER * a, void * adr, void * P, word length); void io_inc(ADAPTER * a, void * adr); +void bri_in_buffer (PISDN_ADAPTER IoAdapter, dword Pos, + void *Buf, dword Len); +int bri_out_buffer (PISDN_ADAPTER IoAdapter, dword Pos, + void *Buf, dword Len, int Verify); /* --------------------------------------------------------------------- ram access functions for memory mapped cards --------------------------------------------------------------------- */ --- linux-2.6.9-rc1/drivers/isdn/hardware/eicon/maintidi.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/drivers/isdn/hardware/eicon/maintidi.c 2004-09-02 20:51:51.000000000 -0700 @@ -115,7 +115,7 @@ diva_strace_library_interface_t* DivaSTr return NULL; } - pmem += sizeof(*pLib); + pmem += sizeof(*pLib); memset(pLib, 0x00, sizeof(*pLib)); pLib->Adapter = Adapter; @@ -337,13 +337,60 @@ static int SuperTraceMessageInput (void* pLib->e.RNum = 1; pLib->e.R->P = (byte*)&pLib->buffer[0]; pLib->e.R->PLength = (word)(sizeof(pLib->buffer) - 1); + } else { /* Indication reception complete, process it now */ byte* p = (byte*)&pLib->buffer[0]; pLib->buffer[pLib->e.R->PLength] = 0; /* terminate I.E. with zero */ + switch (Ind) { + case MAN_COMBI_IND: { + int total_length = pLib->e.R->PLength; + word this_ind_length; + + while (total_length > 3 && *p) { + Ind = *p++; + this_ind_length = (word)p[0] | ((word)p[1] << 8); + p += 2; + + switch (Ind) { + case MAN_INFO_IND: + if (process_idi_info (pLib, (diva_man_var_header_t*)p)) { + return (-1); + } + break; + case MAN_EVENT_IND: + if (process_idi_event (pLib, (diva_man_var_header_t*)p)) { + return (-1); + } + break; + case MAN_TRACE_IND: + if (pLib->trace_on == 1) { + /* + Ignore first trace event that is result of + EVENT_ON operation + */ + pLib->trace_on++; + } else { + /* + Delivery XLOG buffer to application + */ + if (pLib->user_proc_table.trace_proc) { + (*(pLib->user_proc_table.trace_proc))(pLib->user_proc_table.user_context, + &pLib->instance, pLib->Adapter, + p, this_ind_length); + } + } + break; + default: + diva_mnt_internal_dprintf (0, DLI_ERR, "Unknon IDI Ind (DMA mode): %02x", Ind); + } + p += (this_ind_length+1); + total_length -= (4 + this_ind_length); + } + } break; case MAN_INFO_IND: if (process_idi_info (pLib, (diva_man_var_header_t*)p)) { return (-1); @@ -806,7 +853,7 @@ static int ScheduleNextTraceRequest (div } static int process_idi_event (diva_strace_context_t* pLib, - diva_man_var_header_t* pVar) { + diva_man_var_header_t* pVar) { const char* path = (char*)&pVar->path_length+1; char name[64]; int i; --- linux-2.6.9-rc1/drivers/isdn/hardware/eicon/maintidi.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/drivers/isdn/hardware/eicon/maintidi.h 2004-09-02 20:51:51.000000000 -0700 @@ -49,6 +49,8 @@ typedef struct _diva_strace_path2action void* variable; /* Variable that will receive value */ } diva_strace_path2action_t; +#define DIVA_MAX_MANAGEMENT_TRANSFER_SIZE 4096 + typedef struct _diva_strace_context { diva_strace_library_interface_t instance; @@ -62,7 +64,7 @@ typedef struct _diva_strace_context { IDI_CALL request; BUFFERS XData; BUFFERS RData; - byte buffer[2048+512+1]; + byte buffer[DIVA_MAX_MANAGEMENT_TRANSFER_SIZE + 1]; int removal_state; int general_b_ch_event; int general_fax_event; --- linux-2.6.9-rc1/drivers/isdn/hardware/eicon/mntfunc.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/drivers/isdn/hardware/eicon/mntfunc.c 2004-09-02 20:51:51.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: mntfunc.c,v 1.19 2004/01/09 21:22:03 armin Exp $ +/* $Id: mntfunc.c,v 1.19.6.2 2004/08/28 20:03:53 armin Exp $ * * Driver for Eicon DIVA Server ISDN cards. * Maint module @@ -23,17 +23,12 @@ extern char *DRIVERRELEASE_MNT; extern void DIVA_DIDD_Read(void *, int); -#define MAX_DESCRIPTORS 32 - static dword notify_handle; static DESCRIPTOR DAdapter; static DESCRIPTOR MAdapter; static DESCRIPTOR MaintDescriptor = { IDI_DIMAINT, 0, 0, (IDI_CALL) diva_maint_prtComp }; -extern void *diva_os_malloc_tbuffer(unsigned long flags, - unsigned long size); -extern void diva_os_free_tbuffer(unsigned long flags, void *ptr); extern int diva_os_copy_to_user(void *os_handle, void __user *dst, const void *src, int length); extern int diva_os_copy_from_user(void *os_handle, void *dst, @@ -47,16 +42,6 @@ static void no_printf(unsigned char *x, #include "debuglib.c" /* - * stop debug - */ -static void stop_dbg(void) -{ - DbgDeregister(); - memset(&MAdapter, 0, sizeof(MAdapter)); - dprintf = no_printf; -} - -/* * DIDD callback function */ static void *didd_callback(void *context, DESCRIPTOR * adapter, @@ -66,7 +51,9 @@ static void *didd_callback(void *context DBG_ERR(("cb: Change in DAdapter ? Oops ?.")); } else if (adapter->type == IDI_DIMAINT) { if (removal) { - stop_dbg(); + DbgDeregister(); + memset(&MAdapter, 0, sizeof(MAdapter)); + dprintf = no_printf; } else { memcpy(&MAdapter, adapter, sizeof(MAdapter)); dprintf = (DIVA_DI_PRINTF) MAdapter.request; @@ -131,8 +118,6 @@ static void DIVA_EXIT_FUNCTION disconnec { IDI_SYNC_REQ req; - stop_dbg(); - req.didd_notify.e.Req = 0; req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY; req.didd_notify.info.handle = notify_handle; @@ -193,34 +178,63 @@ int maint_read_write(void __user *buf, i } break; + /* + Filter commands will ignore the ID due to fact that filtering affects + the B- channel and Audio Tap trace levels only. Also MAINT driver will + select the right trace ID by itself + */ + case DITRACE_WRITE_SELECTIVE_TRACE_FILTER: + if (!mask) { + ret = diva_set_trace_filter (1, "*"); + } else if (mask < sizeof(data)) { + if (copy_from_user(data, (char __user *)buf+12, mask)) { + ret = -EFAULT; + } else { + ret = diva_set_trace_filter ((int)mask, data); + } + } else { + ret = -EINVAL; + } + break; + + case DITRACE_READ_SELECTIVE_TRACE_FILTER: + if ((ret = diva_get_trace_filter (sizeof(data), data)) > 0) { + if (copy_to_user (buf, data, ret)) + ret = -EFAULT; + } else { + ret = -ENODEV; + } + break; + case DITRACE_READ_TRACE_ENTRY:{ diva_os_spin_lock_magic_t old_irql; word size; diva_dbg_entry_head_t *pmsg; byte *pbuf; - if ((pmsg = diva_maint_get_message(&size, &old_irql))) { + if (!(pbuf = diva_os_malloc(0, mask))) { + return (-ENOMEM); + } + + for(;;) { + if (!(pmsg = + diva_maint_get_message(&size, &old_irql))) { + break; + } if (size > mask) { diva_maint_ack_message(0, &old_irql); ret = -EINVAL; - } else { - if (!(pbuf = diva_os_malloc_tbuffer(0, size))) - { - diva_maint_ack_message(0, &old_irql); - ret = -ENOMEM; - } else { - ret = size; - memcpy(pbuf, pmsg, size); - diva_maint_ack_message(1, &old_irql); - if ((count < size) || diva_os_copy_to_user (NULL, buf, - (void *) pbuf, size)) - ret = -EFAULT; - diva_os_free_tbuffer(0, pbuf); - } + break; } - } else { - ret = 0; + ret = size; + memcpy(pbuf, pmsg, size); + diva_maint_ack_message(1, &old_irql); + if ((count < size) || + diva_os_copy_to_user (NULL, buf, (void *) pbuf, size)) + ret = -EFAULT; + break; } + diva_os_free(0, pbuf); } break; @@ -235,7 +249,7 @@ int maint_read_write(void __user *buf, i ret = -EINVAL; break; } - if (!(pbuf = diva_os_malloc_tbuffer(0, mask))) { + if (!(pbuf = diva_os_malloc(0, mask))) { return (-ENOMEM); } @@ -273,7 +287,7 @@ int maint_read_write(void __user *buf, i } else { ret = written; } - diva_os_free_tbuffer(0, pbuf); + diva_os_free(0, pbuf); } break; @@ -302,7 +316,7 @@ int DIVA_INIT_FUNCTION mntfunc_init(int } else { while ((*buffer_length >= (64 * 1024)) && - (!(*buffer = diva_os_malloc_tbuffer(0, *buffer_length)))) { + (!(*buffer = diva_os_malloc (0, *buffer_length)))) { *buffer_length -= 1024; } @@ -314,7 +328,7 @@ int DIVA_INIT_FUNCTION mntfunc_init(int if (diva_maint_init(*buffer, *buffer_length, (diva_dbg_mem == 0))) { if (!diva_dbg_mem) { - diva_os_free_tbuffer(0, *buffer); + diva_os_free (0, *buffer); } DBG_ERR(("init: maint init failed")); return (0); @@ -324,7 +338,7 @@ int DIVA_INIT_FUNCTION mntfunc_init(int DBG_ERR(("init: failed to connect to DIDD.")); diva_maint_finit(); if (!diva_dbg_mem) { - diva_os_free_tbuffer(0, *buffer); + diva_os_free (0, *buffer); } return (0); } @@ -339,6 +353,8 @@ void DIVA_EXIT_FUNCTION mntfunc_finit(vo void *buffer; int i = 100; + DbgDeregister(); + while (diva_mnt_shutdown_xdi_adapters() && i--) { diva_os_sleep(10); } @@ -346,6 +362,9 @@ void DIVA_EXIT_FUNCTION mntfunc_finit(vo disconnect_didd(); if ((buffer = diva_maint_finit())) { - diva_os_free_tbuffer(0, buffer); + diva_os_free (0, buffer); } + + memset(&MAdapter, 0, sizeof(MAdapter)); + dprintf = no_printf; } --- linux-2.6.9-rc1/drivers/isdn/hardware/eicon/pc.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/drivers/isdn/hardware/eicon/pc.h 2004-09-02 20:51:51.000000000 -0700 @@ -143,6 +143,7 @@ struct dual #define N_DATA_ACK 12 /* data ack ind for D-bit procedure */ #define N_EDATA_ACK 13 /* data ack ind for INTERRUPT */ #define N_XON 15 /* clear RNR state */ +#define N_COMBI_IND N_XON /* combined indication */ #define N_Q_BIT 0x10 /* Q-bit for req/ind */ #define N_M_BIT 0x20 /* M-bit for req/ind */ #define N_D_BIT 0x40 /* D-bit for req/ind */ @@ -228,6 +229,10 @@ struct dual #define VSWITCH_IND 66 /* capifunctions for D-CH-switching */ #define MWI_POLL 67 /* Message Waiting Status Request fkt */ #define CALL_PEND_NOTIFY 68 /* notify capi to set new listen */ +#define DO_NOTHING 69 /* dont do somethin if you get this */ +#define INT_CT_REJ 70 /* ECT rejected internal command */ +#define CALL_HOLD_COMPLETE 71 /* In NT Mode indicate hold complete */ +#define CALL_RETRIEVE_COMPLETE 72 /* In NT Mode indicate retrieve complete */ /*------------------------------------------------------------------*/ /* management service primitives */ /*------------------------------------------------------------------*/ @@ -241,6 +246,7 @@ struct dual #define MAN_INFO_IND 2 #define MAN_EVENT_IND 3 #define MAN_TRACE_IND 4 +#define MAN_COMBI_IND 9 #define MAN_ESC 0x80 /*------------------------------------------------------------------*/ /* return code coding */ @@ -265,6 +271,7 @@ struct dual /*------------------------------------------------------------------*/ #define SHIFT 0x90 /* codeset shift */ #define MORE 0xa0 /* more data */ +#define SDNCMPL 0xa1 /* sending complete */ #define CL 0xb0 /* congestion level */ /* codeset 0 */ #define SMSG 0x00 /* segmented message */ @@ -288,6 +295,8 @@ struct dual #define RDX 0x73 /* redirecting number extended */ #define RDN 0x74 /* redirecting number */ #define RIN 0x76 /* redirection number */ +#define IUP 0x76 /* VN6 rerouter->PCS (codeset 6) */ +#define IPU 0x77 /* VN6 PCS->rerouter (codeset 6) */ #define RI 0x79 /* restart indicator */ #define MIE 0x7a /* management info element */ #define LLC 0x7c /* low layer compatibility */ @@ -296,6 +305,8 @@ struct dual #define ESC 0x7f /* escape extension */ #define DLC 0x20 /* data link layer configuration */ #define NLC 0x21 /* network layer configuration */ +#define REDIRECT_IE 0x22 /* redirection request/indication data */ +#define REDIRECT_NET_IE 0x23 /* redirection network override data */ /* codeset 6 */ #define SIN 0x01 /* service indicator */ #define CIF 0x02 /* charging information */ @@ -306,6 +317,7 @@ struct dual /*------------------------------------------------------------------*/ #define MSGTYPEIE 0x7a /* Messagetype info element */ #define CRIE 0x7b /* INFO info element */ +#define CODESET6IE 0xec /* Tunnel for Codeset 6 IEs */ #define VSWITCHIE 0xed /* VSwitch info element */ #define SSEXTIE 0xee /* Supplem. Service info element */ #define PROFILEIE 0xef /* Profile info element */ @@ -344,6 +356,13 @@ struct dual #define CCBS_REQUEST 0x32 #define CCBS_DEACTIVATE 0x33 #define CCBS_INTERROGATE 0x34 +#define CCBS_STATUS 0x35 +#define CCBS_ERASE 0x36 +#define CCBS_B_FREE 0x37 +#define CCNR_INFO_RETAIN 0x38 +#define CCBS_REMOTE_USER_FREE 0x39 +#define CCNR_REQUEST 0x3a +#define CCNR_INTERROGATE 0x3b #define GET_SUPPORTED_SERVICES 0xff #define DIVERSION_PROCEDURE_CFU 0x70 #define DIVERSION_PROCEDURE_CFB 0x71 @@ -362,6 +381,7 @@ struct dual #define SMASK_3PTY 0x00000008 #define SMASK_CALL_FORWARDING 0x00000010 #define SMASK_CALL_DEFLECTION 0x00000020 +#define SMASK_MCID 0x00000040 #define SMASK_CCBS 0x00000080 #define SMASK_MWI 0x00000100 #define SMASK_CCNR 0x00000200 @@ -406,6 +426,8 @@ struct dual #define RTPL2_IN 13 /* RTP layer-2 protocol, incomming */ #define RTPL2 14 /* RTP layer-2 protocol */ #define V120_V42BIS 15 /* V.120 asynchronous mode supporting V.42bis compression */ +#define LISTENER 27 /* Layer 2 to listen line */ +#define MTP2 28 /* MTP2 Layer 2 */ #define PIAFS_CRC 29 /* PIAFS Layer 2 with CRC calculation at L2 */ /* ------------------------------------------------------ PIAFS DLC DEFINITIONS @@ -506,6 +528,22 @@ Byte | 8 7 6 5 4 3 2 1 | | | data transfer. | +---------------------+------+-----------------------------------------+ */ +/* ------------------------------------------------------ + LISTENER DLC DEFINITIONS + ------------------------------------------------------ */ +#define LISTENER_FEATURE_MASK_CUMMULATIVE 0x0001 +/* ------------------------------------------------------ + LISTENER META-FRAME CODE/PRIMITIVE DEFINITIONS + ------------------------------------------------------ */ +#define META_CODE_LL_UDATA_RX 0x01 +#define META_CODE_LL_UDATA_TX 0x02 +#define META_CODE_LL_DATA_RX 0x03 +#define META_CODE_LL_DATA_TX 0x04 +#define META_CODE_LL_MDATA_RX 0x05 +#define META_CODE_LL_MDATA_TX 0x06 +#define META_CODE_EMPTY 0x10 +#define META_CODE_LOST_FRAMES 0x11 +#define META_FLAG_TRUNCATED 0x0001 /*------------------------------------------------------------------*/ /* CAPI-like profile to indicate features on LAW_REQ */ /*------------------------------------------------------------------*/ @@ -577,6 +615,14 @@ Byte | 8 7 6 5 4 3 2 1 #define MANUFACTURER_FEATURE_DMACONNECT 0x04000000L #define MANUFACTURER_FEATURE_AUDIO_TAP 0x08000000L #define MANUFACTURER_FEATURE_FAX_NONSTANDARD 0x10000000L +#define MANUFACTURER_FEATURE_SS7 0x20000000L +#define MANUFACTURER_FEATURE_MADAPTER 0x40000000L +#define MANUFACTURER_FEATURE_MEASURE 0x80000000L +#define MANUFACTURER_FEATURE2_LISTENING 0x00000001L +#define MANUFACTURER_FEATURE2_SS_DIFFCONTPOSSIBLE 0x00000002L +#define MANUFACTURER_FEATURE2_GENERIC_TONE 0x00000004L +#define MANUFACTURER_FEATURE2_COLOR_FAX 0x00000008L +#define MANUFACTURER_FEATURE2_SS_ECT_DIFFCONTPOSSIBLE 0x00000010L #define RTP_PRIM_PAYLOAD_PCMU_8000 0 #define RTP_PRIM_PAYLOAD_1016_8000 1 #define RTP_PRIM_PAYLOAD_G726_32_8000 2 @@ -624,6 +670,15 @@ Byte | 8 7 6 5 4 3 2 1 #define VSINVOKEID 4 #define VSCLMRKS 5 #define VSTBCTIDENT 6 +#define VSETSILINKID 7 +#define VSSAMECONTROLLER 8 +/* Errorcodes for VSETSILINKID begin */ +#define VSETSILINKIDRRWC 1 +#define VSETSILINKIDREJECT 2 +#define VSETSILINKIDTIMEOUT 3 +#define VSETSILINKIDFAILCOUNT 4 +#define VSETSILINKIDERROR 5 +/* Errorcodes for VSETSILINKID end */ /* -----------------------------------------------------------** ** The PROTOCOL_FEATURE_STRING in feature.h (included ** ** in prstart.sx and astart.sx) defines capabilities and ** @@ -647,5 +702,37 @@ Byte | 8 7 6 5 4 3 2 1 #define PROTCAP_FREE13 0x2000 /* not used */ #define PROTCAP_FREE14 0x4000 /* not used */ #define PROTCAP_EXTENSION 0x8000 /* used for future extentions */ +/* -----------------------------------------------------------* */ +/* Onhook data transmission ETS30065901 */ +/* Message Type */ +/*#define RESERVED4 0x4*/ +#define CALL_SETUP 0x80 +#define MESSAGE_WAITING_INDICATOR 0x82 +/*#define RESERVED84 0x84*/ +/*#define RESERVED85 0x85*/ +#define ADVICE_OF_CHARGE 0x86 +/*1111 0001 +to +1111 1111 +F1H - Reserved for network operator use +to +FFH*/ +/* Parameter Types */ +#define DATE_AND_TIME 1 +#define CLI_PARAMETER_TYPE 2 +#define CALLED_DIRECTORY_NUMBER_PARAMETER_TYPE 3 +#define REASON_FOR_ABSENCE_OF_CLI_PARAMETER_TYPE 4 +#define NAME_PARAMETER_TYPE 7 +#define REASON_FOR_ABSENCE_OF_CALLING_PARTY_NAME_PARAMETER_TYPE 8 +#define VISUAL_INDICATOR_PARAMETER_TYPE 0xb +#define COMPLEMENTARY_CLI_PARAMETER_TYPE 0x10 +#define CALL_TYPE_PARAMETER_TYPE 0x11 +#define FIRST_CALLED_LINE_DIRECTORY_NUMBER_PARAMETER_TYPE 0x12 +#define NETWORK_MESSAGE_SYSTEM_STATUS_PARAMETER_TYPE 0x13 +#define FORWARDED_CALL_TYPE_PARAMETER_TYPE 0x15 +#define TYPE_OF_CALLING_USER_PARAMETER_TYPE 0x16 +#define REDIRECTING_NUMBER_PARAMETER_TYPE 0x1a +#define EXTENSION_FOR_NETWORK_OPERATOR_USE_PARAMETER_TYPE 0xe0 +/* -----------------------------------------------------------* */ #else #endif /* PC_H_INCLUDED } */ --- linux-2.6.9-rc1/drivers/isdn/hardware/eicon/platform.h 2004-08-24 00:03:28.000000000 -0700 +++ 25/drivers/isdn/hardware/eicon/platform.h 2004-09-02 20:51:51.000000000 -0700 @@ -1,4 +1,4 @@ -/* $Id: platform.h,v 1.37.4.1 2004/07/28 14:47:21 armin Exp $ +/* $Id: platform.h,v 1.37.4.2 2004/08/28 20:03:53 armin Exp $ * * platform.h * @@ -269,20 +269,6 @@ static __inline__ void diva_os_leave_spi diva_os_spin_lock_magic_t* old_irql, \ void* dbg) { spin_unlock_bh(a); } -static __inline__ void diva_os_enter_spin_lock_hard (diva_os_spin_lock_t* a, \ - diva_os_spin_lock_magic_t* old_irql, \ - void* dbg) { \ - unsigned long flags; \ - spin_lock_irqsave (a, flags); \ - *old_irql = (diva_os_spin_lock_magic_t)flags; \ -} -static __inline__ void diva_os_leave_spin_lock_hard (diva_os_spin_lock_t* a, \ - diva_os_spin_lock_magic_t* old_irql, \ - void* dbg) { \ - unsigned long flags = (unsigned long)*old_irql; \ - spin_unlock_irqrestore (a, flags); \ -} - #define diva_os_destroy_spin_lock(a,b) do { } while(0) /* @@ -347,12 +333,18 @@ diva_os_atomic_decrement(diva_os_atomic_ #define DIVA_IDI_RX_DMA 1 +/* +** endian macros +*/ #define READ_WORD(addr) readw(addr) #define READ_DWORD(addr) readl(addr) #define WRITE_WORD(addr,v) writew(v,addr) #define WRITE_DWORD(addr,v) writel(v,addr) +/* +** 32/64 bit macors +*/ #ifdef BITS_PER_LONG #if BITS_PER_LONG > 32 #define PLATFORM_GT_32BIT @@ -360,8 +352,23 @@ diva_os_atomic_decrement(diva_os_atomic_ #endif #endif +/* +** undef os definitions of macros we use +*/ #undef ID_MASK #undef N_DATA #undef ADDR +/* +** dump file +*/ +#define diva_os_dump_file_t char +#define diva_os_board_trace_t char +#define diva_os_dump_file(__x__) do { } while(0) + +/* +** size of internal arrays +*/ +#define MAX_DESCRIPTORS 64 + #endif /* __PLATFORM_H__ */ --- linux-2.6.9-rc1/drivers/isdn/hisax/avm_pci.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/drivers/isdn/hisax/avm_pci.c 2004-09-02 20:51:51.000000000 -0700 @@ -752,70 +752,70 @@ setup_avm_pcipnp(struct IsdnCard *card) cs->hw.avm.cfg_reg = card->para[1]; cs->irq = card->para[0]; cs->subtyp = AVM_FRITZ_PNP; - } else { + goto ready; + } #ifdef __ISAPNP__ - if (isapnp_present()) { - struct pnp_dev *pnp_avm_d = NULL; - if ((pnp_avm_c = pnp_find_card( + if (isapnp_present()) { + struct pnp_dev *pnp_avm_d = NULL; + if ((pnp_avm_c = pnp_find_card( + ISAPNP_VENDOR('A', 'V', 'M'), + ISAPNP_FUNCTION(0x0900), pnp_avm_c))) { + if ((pnp_avm_d = pnp_find_dev(pnp_avm_c, ISAPNP_VENDOR('A', 'V', 'M'), - ISAPNP_FUNCTION(0x0900), pnp_avm_c))) { - if ((pnp_avm_d = pnp_find_dev(pnp_avm_c, - ISAPNP_VENDOR('A', 'V', 'M'), - ISAPNP_FUNCTION(0x0900), pnp_avm_d))) { - int err; + ISAPNP_FUNCTION(0x0900), pnp_avm_d))) { + int err; - pnp_disable_dev(pnp_avm_d); - err = pnp_activate_dev(pnp_avm_d); - if (err<0) { - printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n", - __FUNCTION__, err); - return(0); - } - cs->hw.avm.cfg_reg = - pnp_port_start(pnp_avm_d, 0); - cs->irq = pnp_irq(pnp_avm_d, 0); - if (!cs->irq) { - printk(KERN_ERR "FritzPnP:No IRQ\n"); - return(0); - } - if (!cs->hw.avm.cfg_reg) { - printk(KERN_ERR "FritzPnP:No IO address\n"); - return(0); - } - cs->subtyp = AVM_FRITZ_PNP; - goto ready; + pnp_disable_dev(pnp_avm_d); + err = pnp_activate_dev(pnp_avm_d); + if (err<0) { + printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n", + __FUNCTION__, err); + return(0); + } + cs->hw.avm.cfg_reg = + pnp_port_start(pnp_avm_d, 0); + cs->irq = pnp_irq(pnp_avm_d, 0); + if (!cs->irq) { + printk(KERN_ERR "FritzPnP:No IRQ\n"); + return(0); + } + if (!cs->hw.avm.cfg_reg) { + printk(KERN_ERR "FritzPnP:No IO address\n"); + return(0); } + cs->subtyp = AVM_FRITZ_PNP; + goto ready; } - } else { - printk(KERN_INFO "FritzPnP: no ISA PnP present\n"); } + } else { + printk(KERN_INFO "FritzPnP: no ISA PnP present\n"); + } #endif #ifdef CONFIG_PCI - if ((dev_avm = pci_find_device(PCI_VENDOR_ID_AVM, - PCI_DEVICE_ID_AVM_A1, dev_avm))) { - cs->irq = dev_avm->irq; - if (!cs->irq) { - printk(KERN_ERR "FritzPCI: No IRQ for PCI card found\n"); - return(0); - } - if (pci_enable_device(dev_avm)) - return(0); - cs->hw.avm.cfg_reg = pci_resource_start(dev_avm, 1); - if (!cs->hw.avm.cfg_reg) { - printk(KERN_ERR "FritzPCI: No IO-Adr for PCI card found\n"); - return(0); - } - cs->subtyp = AVM_FRITZ_PCI; - } else { - printk(KERN_WARNING "FritzPCI: No PCI card found\n"); + if ((dev_avm = pci_find_device(PCI_VENDOR_ID_AVM, + PCI_DEVICE_ID_AVM_A1, dev_avm))) { + cs->irq = dev_avm->irq; + if (!cs->irq) { + printk(KERN_ERR "FritzPCI: No IRQ for PCI card found\n"); return(0); } - cs->irq_flags |= SA_SHIRQ; + if (pci_enable_device(dev_avm)) + return(0); + cs->hw.avm.cfg_reg = pci_resource_start(dev_avm, 1); + if (!cs->hw.avm.cfg_reg) { + printk(KERN_ERR "FritzPCI: No IO-Adr for PCI card found\n"); + return(0); + } + cs->subtyp = AVM_FRITZ_PCI; + } else { + printk(KERN_WARNING "FritzPCI: No PCI card found\n"); + return(0); + } + cs->irq_flags |= SA_SHIRQ; #else - printk(KERN_WARNING "FritzPCI: NO_PCI_BIOS\n"); - return (0); + printk(KERN_WARNING "FritzPCI: NO_PCI_BIOS\n"); + return (0); #endif /* CONFIG_PCI */ - } ready: cs->hw.avm.isac = cs->hw.avm.cfg_reg + 0x10; if (!request_region(cs->hw.avm.cfg_reg, 32, --- linux-2.6.9-rc1/drivers/isdn/hisax/bkm_a4t.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/drivers/isdn/hisax/bkm_a4t.c 2004-09-02 20:51:51.000000000 -0700 @@ -265,7 +265,7 @@ setup_bkm_a4t(struct IsdnCard *card) char tmp[64]; u_int pci_memaddr = 0, found = 0; I20_REGISTER_FILE *pI20_Regs; -#if CONFIG_PCI +#ifdef CONFIG_PCI #endif strcpy(tmp, bkm_a4t_revision); @@ -275,7 +275,7 @@ setup_bkm_a4t(struct IsdnCard *card) } else return (0); -#if CONFIG_PCI +#ifdef CONFIG_PCI while ((dev_a4t = pci_find_device(PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36120, dev_a4t))) { u16 sub_sys; --- linux-2.6.9-rc1/drivers/isdn/hisax/bkm_a8.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/isdn/hisax/bkm_a8.c 2004-09-02 20:51:51.000000000 -0700 @@ -21,7 +21,7 @@ #include #include "bkm_ax.h" -#if CONFIG_PCI +#ifdef CONFIG_PCI #define ATTEMPT_PCI_REMAPPING /* Required for PLX rev 1 */ @@ -285,7 +285,7 @@ static u_char pci_irq __initdata = 0; int __init setup_sct_quadro(struct IsdnCard *card) { -#if CONFIG_PCI +#ifdef CONFIG_PCI struct IsdnCardState *cs = card->cs; char tmp[64]; u_char pci_rev_id; --- linux-2.6.9-rc1/drivers/isdn/hisax/diva.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/drivers/isdn/hisax/diva.c 2004-09-02 20:51:51.000000000 -0700 @@ -1027,7 +1027,7 @@ setup_diva(struct IsdnCard *card) } } #endif -#if CONFIG_PCI +#ifdef CONFIG_PCI cs->subtyp = 0; if ((dev_diva = pci_find_device(PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA20, dev_diva))) { --- linux-2.6.9-rc1/drivers/isdn/hisax/elsa.c 2004-08-24 00:01:55.000000000 -0700 +++ 25/drivers/isdn/hisax/elsa.c 2004-09-02 20:51:51.000000000 -0700 @@ -1022,7 +1022,7 @@ setup_elsa(struct IsdnCard *card) cs->hw.elsa.base, cs->irq); } else if (cs->typ == ISDN_CTYPE_ELSA_PCI) { -#if CONFIG_PCI +#ifdef CONFIG_PCI cs->subtyp = 0; if ((dev_qs1000 = pci_find_device(PCI_VENDOR_ID_ELSA, PCI_DEVICE_ID_ELSA_MICROLINK, dev_qs1000))) { --- linux-2.6.9-rc1/drivers/isdn/hisax/enternow_pci.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/isdn/hisax/enternow_pci.c 2004-09-02 20:51:51.000000000 -0700 @@ -299,7 +299,7 @@ setup_enternow_pci(struct IsdnCard *card struct IsdnCardState *cs = card->cs; char tmp[64]; -#if CONFIG_PCI +#ifdef CONFIG_PCI #ifdef __BIG_ENDIAN #error "not running on big endian machines now" #endif --- linux-2.6.9-rc1/drivers/isdn/hisax/gazel.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/drivers/isdn/hisax/gazel.c 2004-09-02 20:51:51.000000000 -0700 @@ -634,7 +634,7 @@ setup_gazel(struct IsdnCard *card) return (0); } else { -#if CONFIG_PCI +#ifdef CONFIG_PCI if (setup_gazelpci(cs)) return (0); #else --- linux-2.6.9-rc1/drivers/isdn/hisax/hfc_pci.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/drivers/isdn/hisax/hfc_pci.c 2004-09-02 20:51:51.000000000 -0700 @@ -65,7 +65,7 @@ static const PCI_ENTRY id_list[] = }; -#if CONFIG_PCI +#ifdef CONFIG_PCI /******************************************/ /* free hardware resources used by driver */ @@ -1655,7 +1655,7 @@ setup_hfcpci(struct IsdnCard *card) #endif strcpy(tmp, hfcpci_revision); printk(KERN_INFO "HiSax: HFC-PCI driver Rev. %s\n", HiSax_getrev(tmp)); -#if CONFIG_PCI +#ifdef CONFIG_PCI cs->hw.hfcpci.int_s1 = 0; cs->dc.hfcpci.ph_state = 0; cs->hw.hfcpci.fifo = 255; --- linux-2.6.9-rc1/drivers/isdn/hisax/niccy.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/drivers/isdn/hisax/niccy.c 2004-09-02 20:51:51.000000000 -0700 @@ -309,7 +309,7 @@ setup_niccy(struct IsdnCard *card) return (0); } } else { -#if CONFIG_PCI +#ifdef CONFIG_PCI u_int pci_ioaddr; cs->subtyp = 0; if ((niccy_dev = pci_find_device(PCI_VENDOR_ID_SATSAGEM, --- linux-2.6.9-rc1/drivers/isdn/hisax/nj_s.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/drivers/isdn/hisax/nj_s.c 2004-09-02 20:51:51.000000000 -0700 @@ -167,7 +167,7 @@ setup_netjet_s(struct IsdnCard *card) return(0); test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); -#if CONFIG_PCI +#ifdef CONFIG_PCI for ( ;; ) { --- linux-2.6.9-rc1/drivers/isdn/hisax/nj_u.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/drivers/isdn/hisax/nj_u.c 2004-09-02 20:51:51.000000000 -0700 @@ -137,7 +137,7 @@ setup_netjet_u(struct IsdnCard *card) int bytecnt; struct IsdnCardState *cs = card->cs; char tmp[64]; -#if CONFIG_PCI +#ifdef CONFIG_PCI #endif #ifdef __BIG_ENDIAN #error "not running on big endian machines now" @@ -148,7 +148,7 @@ setup_netjet_u(struct IsdnCard *card) return(0); test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); -#if CONFIG_PCI +#ifdef CONFIG_PCI for ( ;; ) { --- linux-2.6.9-rc1/drivers/isdn/hisax/sedlbauer.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/isdn/hisax/sedlbauer.c 2004-09-02 20:51:51.000000000 -0700 @@ -618,7 +618,7 @@ setup_sedlbauer(struct IsdnCard *card) } #endif /* Probe for Sedlbauer speed pci */ -#if CONFIG_PCI +#ifdef CONFIG_PCI if ((dev_sedl = pci_find_device(PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, dev_sedl))) { if (pci_enable_device(dev_sedl)) --- linux-2.6.9-rc1/drivers/isdn/hisax/telespci.c 2004-08-24 00:03:14.000000000 -0700 +++ 25/drivers/isdn/hisax/telespci.c 2004-09-02 20:51:51.000000000 -0700 @@ -300,7 +300,7 @@ setup_telespci(struct IsdnCard *card) printk(KERN_INFO "HiSax: Teles/PCI driver Rev. %s\n", HiSax_getrev(tmp)); if (cs->typ != ISDN_CTYPE_TELESPCI) return (0); -#if CONFIG_PCI +#ifdef CONFIG_PCI if ((dev_tel = pci_find_device (PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36120, dev_tel))) { if (pci_enable_device(dev_tel)) return(0); --- linux-2.6.9-rc1/drivers/isdn/hisax/w6692.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/isdn/hisax/w6692.c 2004-09-02 20:51:51.000000000 -0700 @@ -1012,7 +1012,7 @@ setup_w6692(struct IsdnCard *card) printk(KERN_INFO "HiSax: W6692 driver Rev. %s\n", HiSax_getrev(tmp)); if (cs->typ != ISDN_CTYPE_W6692) return (0); -#if CONFIG_PCI +#ifdef CONFIG_PCI while (id_list[id_idx].vendor_id) { dev_w6692 = pci_find_device(id_list[id_idx].vendor_id, id_list[id_idx].device_id, --- linux-2.6.9-rc1/drivers/isdn/tpam/tpam_main.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/drivers/isdn/tpam/tpam_main.c 2004-09-03 00:56:53.961957184 -0700 @@ -23,7 +23,7 @@ /* Local functions prototypes */ static int __devinit tpam_probe(struct pci_dev *, const struct pci_device_id *); -static void __devexit tpam_unregister_card(tpam_card *); +static void __devexit tpam_unregister_card(struct pci_dev *, tpam_card *); static void __devexit tpam_remove(struct pci_dev *); static int __init tpam_init(void); static void __exit tpam_exit(void); @@ -86,13 +86,20 @@ u32 tpam_findchannel(tpam_card *card, u3 */ static int __devinit tpam_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) { tpam_card *card, *c; - int i; + int i, err; + + if ((err = pci_enable_device(dev))) { + printk(KERN_ERR "TurboPAM: can't enable PCI device at %s\n", + pci_name(dev)); + return err; + } /* allocate memory for the board structure */ if (!(card = (tpam_card *)kmalloc(sizeof(tpam_card), GFP_KERNEL))) { printk(KERN_ERR "TurboPAM: tpam_register_card: " "kmalloc failed!\n"); - return -ENOMEM; + err = -ENOMEM; + goto err_out_disable_dev; } memset((char *)card, 0, sizeof(tpam_card)); @@ -106,8 +113,8 @@ static int __devinit tpam_probe(struct p card->interface.id, card)) { printk(KERN_ERR "TurboPAM: tpam_register_card: " "could not request irq %d\n", card->irq); - kfree(card); - return -EIO; + err = -EIO; + goto err_out_free_card; } /* remap board memory */ @@ -115,9 +122,8 @@ static int __devinit tpam_probe(struct p 0x800000))) { printk(KERN_ERR "TurboPAM: tpam_register_card: " "unable to remap bar0\n"); - free_irq(card->irq, card); - kfree(card); - return -EIO; + err = -EIO; + goto err_out_free_irq; } /* reset the board */ @@ -150,10 +156,8 @@ static int __devinit tpam_probe(struct p if (!register_isdn(&card->interface)) { printk(KERN_ERR "TurboPAM: tpam_register_card: " "unable to register %s\n", card->interface.id); - free_irq(card->irq, card); - iounmap((void *)card->bar0); - kfree(card); - return -EIO; + err = -EIO; + goto err_out_iounmap; } card->id = card->interface.channels; @@ -195,6 +199,19 @@ static int __devinit tpam_probe(struct p pci_set_drvdata(dev, card); return 0; + +err_out_iounmap: + iounmap((void *)card->bar0); + +err_out_free_irq: + free_irq(card->irq, card); + +err_out_free_card: + kfree(card); + +err_out_disable_dev: + pci_disable_device(dev); + return err; } /* @@ -202,7 +219,7 @@ static int __devinit tpam_probe(struct p * * card: the board. */ -static void __devexit tpam_unregister_card(tpam_card *card) { +static void __devexit tpam_unregister_card(struct pci_dev *pcidev, tpam_card *card) { isdn_ctrl cmd; /* prevent the ISDN link layer that the driver will be unloaded */ @@ -215,6 +232,8 @@ static void __devexit tpam_unregister_ca /* release mapped memory */ iounmap((void *)card->bar0); + + pci_disable_device(pcidev); } /* @@ -235,7 +254,7 @@ static void __devexit tpam_remove(struct } /* unregister each board */ - tpam_unregister_card(card); + tpam_unregister_card(pcidev, card); /* and free the board structure itself */ kfree(card); --- linux-2.6.9-rc1/drivers/macintosh/adbhid.c 2004-08-24 00:03:49.000000000 -0700 +++ 25/drivers/macintosh/adbhid.c 2004-09-02 20:51:51.000000000 -0700 @@ -326,7 +326,7 @@ adbhid_mouse_input(unsigned char *data, input_report_key(&adbhid[id]->input, BTN_LEFT, !((data[1] >> 7) & 1)); input_report_key(&adbhid[id]->input, BTN_MIDDLE, !((data[2] >> 7) & 1)); - if (nb >= 4) + if (nb >= 4 && adbhid[id]->mouse_kind != ADBMOUSE_TRACKPAD) input_report_key(&adbhid[id]->input, BTN_RIGHT, !((data[3] >> 7) & 1)); input_report_rel(&adbhid[id]->input, REL_X, --- linux-2.6.9-rc1/drivers/macintosh/via-pmu.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/drivers/macintosh/via-pmu.c 2004-09-03 00:57:16.057598136 -0700 @@ -52,7 +52,6 @@ #include #include #include -#include #include #include #include @@ -138,7 +137,6 @@ static int data_len; static volatile int adb_int_pending; static volatile int disable_poll; static struct adb_request bright_req_1, bright_req_2; -static unsigned long async_req_locks; static struct device_node *vias; static int pmu_kind = PMU_UNKNOWN; static int pmu_fully_inited = 0; @@ -155,6 +153,7 @@ static int drop_interrupts; static int option_lid_wakeup = 1; static int sleep_in_progress; static int can_sleep; +static unsigned long async_req_locks; #endif /* CONFIG_PMAC_PBOOK */ static unsigned int pmu_irq_stats[11]; @@ -494,12 +493,9 @@ static int __init via_pmu_dev_init(void) /* Create /proc/pmu */ proc_pmu_root = proc_mkdir("pmu", NULL); if (proc_pmu_root) { - int i; - proc_pmu_info = create_proc_read_entry("info", 0, proc_pmu_root, - proc_get_info, NULL); - proc_pmu_irqstats = create_proc_read_entry("interrupts", 0, proc_pmu_root, - proc_get_irqstats, NULL); #ifdef CONFIG_PMAC_PBOOK + int i; + for (i=0; inlink = 1; --- linux-2.6.9-rc1/drivers/Makefile 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/Makefile 2004-09-03 00:56:44.651372608 -0700 @@ -15,6 +15,9 @@ obj-$(CONFIG_PNP) += pnp/ # char/ comes before serial/ etc so that the VT console is the boot-time # default. obj-y += char/ +# we also need input/serio early so serio bus is initialized by the time +# serial drivers start registering their serio ports +obj-$(CONFIG_SERIO) += input/serio/ obj-y += serial/ obj-$(CONFIG_PARPORT) += parport/ obj-y += base/ block/ misc/ net/ media/ @@ -39,7 +42,6 @@ obj-$(CONFIG_USB) += usb/ obj-$(CONFIG_USB_GADGET) += usb/gadget/ obj-$(CONFIG_INPUT) += input/ obj-$(CONFIG_GAMEPORT) += input/gameport/ -obj-$(CONFIG_SERIO) += input/serio/ obj-$(CONFIG_I2O) += message/ obj-$(CONFIG_I2C) += i2c/ obj-$(CONFIG_W1) += w1/ @@ -51,4 +53,5 @@ obj-$(CONFIG_MCA) += mca/ obj-$(CONFIG_EISA) += eisa/ obj-$(CONFIG_CPU_FREQ) += cpufreq/ obj-$(CONFIG_MMC) += mmc/ +obj-$(CONFIG_PERFCTR) += perfctr/ obj-y += firmware/ --- linux-2.6.9-rc1/drivers/md/Kconfig 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/md/Kconfig 2004-09-02 20:51:51.000000000 -0700 @@ -85,6 +85,24 @@ config MD_RAID1 If unsure, say Y. +config MD_RAID10 + tristate "RAID-10 (mirrored striping) mode (EXPERIMENTAL)" + depends on BLK_DEV_MD && EXPERIMENTAL + ---help--- + RAID-10 provides a combination of striping (RAID-0) and + mirroring (RAID-1) with easier configuration and more flexable + layout. + Unlike RAID-0, but like RAID-1, RAID-10 requires all devices to + be the same size (or atleast, only as much as the smallest device + will be used). + RAID-10 provides a variety of layouts that provide different levels + of redundancy and performance. + + RAID-10 requires mdadm-1.7.0 or later, available at: + + ftp://ftp.kernel.org/pub/linux/utils/raid/mdadm/ + + config MD_RAID5 tristate "RAID-4/RAID-5 mode" depends on BLK_DEV_MD --- linux-2.6.9-rc1/drivers/md/Makefile 2004-08-24 00:02:48.000000000 -0700 +++ 25/drivers/md/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -20,6 +20,7 @@ hostprogs-y := mktables obj-$(CONFIG_MD_LINEAR) += linear.o obj-$(CONFIG_MD_RAID0) += raid0.o obj-$(CONFIG_MD_RAID1) += raid1.o +obj-$(CONFIG_MD_RAID10) += raid10.o obj-$(CONFIG_MD_RAID5) += raid5.o xor.o obj-$(CONFIG_MD_RAID6) += raid6.o xor.o obj-$(CONFIG_MD_MULTIPATH) += multipath.o --- linux-2.6.9-rc1/drivers/md/md.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/drivers/md/md.c 2004-09-03 00:57:25.800117048 -0700 @@ -37,6 +37,7 @@ #include #include /* for invalidate_bdev */ #include +#include #include @@ -124,6 +125,48 @@ static ctl_table raid_root_table[] = { static struct block_device_operations md_fops; + +/* + * We have a system wide 'event count' that is incremented + * on any 'interesting' event, and readers of /proc/mdstat + * can use 'poll' or 'select' to find out when the event + * count increases + * + * Events are: + * start array, stop array, error, add device, remove device, + * start build, activate spare + */ +DECLARE_WAIT_QUEUE_HEAD(md_event_waiters); +EXPORT_SYMBOL(md_event_waiters); +static atomic_t md_event_count; +int md_new_event(void) +{ + atomic_inc(&md_event_count); + wake_up(&md_event_waiters); + return atomic_read(&md_event_count); +} +EXPORT_SYMBOL(md_new_event); +struct mdstat_info { + struct list_head list; /* all active files linked together */ + unsigned long seen, reading, acknowledged; +}; +static LIST_HEAD(readers); +static spinlock_t readers_lock = SPIN_LOCK_UNLOCKED; + +int md_event_reached(unsigned long ev) +{ + /* returns true when all readers have acknowledged event 'ev' */ + struct mdstat_info *mi; + int rv = 1; + spin_lock(&readers_lock); + list_for_each_entry(mi, &readers, list) + if (mi->reading > 0 && mi->acknowledged < ev) + rv = 0; + spin_unlock(&readers_lock); + return rv; +} +EXPORT_SYMBOL(md_event_reached); + /* * Enables to iterate over all existing md arrays * all_mddevs_lock protects this list. @@ -406,7 +449,7 @@ static int read_disk_sb(mdk_rdev_t * rde return 0; fail: - printk(KERN_ERR "md: disabled device %s, could not read superblock.\n", + printk(KERN_WARNING "md: disabled device %s, could not read superblock.\n", bdevname(rdev->bdev,b)); return -EINVAL; } @@ -472,6 +515,31 @@ static unsigned int calc_sb_csum(mdp_sup return csum; } +/* csum_partial is not consistent between different architectures. + * Some (i386) do a 32bit csum. Some (alpha) do 16 bit. + * This makes it hard for user-space to know what to do. + * So we use calc_sb_csum to set the checksum to allow working + * with older kernels, but allow calc_sb_csum_common to + * be used when checking if a checksum is correct, to + * make life easier for user-space tools that might write + * a superblock. + */ +static unsigned int calc_sb_csum_common(mdp_super_t *super) +{ + unsigned int disk_csum = super->sb_csum; + unsigned long long newcsum = 0; + unsigned int csum; + int i; + unsigned int *superc = (int*) super; + super->sb_csum = 0; + + for (i=0; i>32); + super->sb_csum = disk_csum; + return csum; +} + /* * Handle superblock details. * We want to be able to handle multiple superblock formats @@ -554,7 +622,8 @@ static int super_90_load(mdk_rdev_t *rde if (sb->raid_disks <= 0) goto abort; - if (calc_sb_csum(sb) != sb->sb_csum) { + if (calc_sb_csum(sb) != sb->sb_csum && + calc_sb_csum_common(sb) != sb->sb_csum) { printk(KERN_WARNING "md: invalid superblock checksum on %s\n", b); goto abort; @@ -778,11 +847,21 @@ static void super_90_sync(mddev_t *mddev static unsigned int calc_sb_1_csum(struct mdp_superblock_1 * sb) { unsigned int disk_csum, csum; + unsigned long long newcsum; int size = 256 + sb->max_dev*2; + unsigned int *isuper = (unsigned int*)sb; + int i; disk_csum = sb->sb_csum; sb->sb_csum = 0; - csum = csum_partial((void *)sb, size, 0); + newcsum = 0; + for (i=0; size>=4; size -= 4 ) + newcsum += le32_to_cpu(*isuper++); + + if (size == 2) + newcsum += le16_to_cpu(*(unsigned short*) isuper); + + csum = (newcsum & 0xffffffff) + (newcsum >> 32); sb->sb_csum = disk_csum; return csum; } @@ -1075,20 +1154,24 @@ static void unbind_rdev_from_array(mdk_r /* * prevent the device from being mounted, repartitioned or * otherwise reused by a RAID array (or any other kernel - * subsystem), by opening the device. [simply getting an - * inode is not enough, the SCSI module usage code needs - * an explicit open() on the device] + * subsystem), by bd_claiming the device. */ static int lock_rdev(mdk_rdev_t *rdev, dev_t dev) { int err = 0; struct block_device *bdev; + char b[BDEVNAME_SIZE]; bdev = open_by_devnum(dev, FMODE_READ|FMODE_WRITE); - if (IS_ERR(bdev)) + if (IS_ERR(bdev)) { + printk(KERN_ERR "md: could not open %s.\n", + __bdevname(dev, b)); return PTR_ERR(bdev); + } err = bd_claim(bdev, rdev); if (err) { + printk(KERN_ERR "md: could not bd_claim %s.\n", + bdevname(bdev, b)); blkdev_put(bdev); return err; } @@ -1150,10 +1233,7 @@ static void export_array(mddev_t *mddev) static void print_desc(mdp_disk_t *desc) { - char b[BDEVNAME_SIZE]; - - printk(" DISK\n", desc->number, - __bdevname(MKDEV(desc->major, desc->minor), b), + printk(" DISK\n", desc->number, desc->major,desc->minor,desc->raid_disk,desc->state); } @@ -1345,8 +1425,7 @@ static mdk_rdev_t *md_import_device(dev_ rdev = (mdk_rdev_t *) kmalloc(sizeof(*rdev), GFP_KERNEL); if (!rdev) { - printk(KERN_ERR "md: could not alloc mem for %s!\n", - __bdevname(newdev, b)); + printk(KERN_ERR "md: could not alloc mem for new device!\n"); return ERR_PTR(-ENOMEM); } memset(rdev, 0, sizeof(*rdev)); @@ -1355,11 +1434,9 @@ static mdk_rdev_t *md_import_device(dev_ goto abort_free; err = lock_rdev(rdev, newdev); - if (err) { - printk(KERN_ERR "md: could not lock %s.\n", - __bdevname(newdev, b)); + if (err) goto abort_free; - } + rdev->desc_nr = -1; rdev->faulty = 0; rdev->in_sync = 0; @@ -1648,6 +1725,8 @@ static int do_md_run(mddev_t * mddev) mddev->pers = pers[pnum]; spin_unlock(&pers_lock); + mddev->resync_max_sectors = mddev->size << 1; /* may be over-ridden by personality */ + err = mddev->pers->run(mddev); if (err) { printk(KERN_ERR "md: pers->run() failed ...\n"); @@ -1681,6 +1760,7 @@ static int do_md_run(mddev_t * mddev) mddev->queue->issue_flush_fn = md_flush_all; mddev->changed = 1; + md_new_event(); return 0; } @@ -1785,6 +1865,7 @@ static int do_md_stop(mddev_t * mddev, i printk(KERN_INFO "md: %s switched to read-only mode.\n", mdname(mddev)); err = 0; + md_new_event(); out: return err; } @@ -1915,11 +1996,9 @@ static int autostart_array(dev_t startde mdk_rdev_t *start_rdev = NULL, *rdev; start_rdev = md_import_device(startdev, 0, 0); - if (IS_ERR(start_rdev)) { - printk(KERN_WARNING "md: could not import %s!\n", - __bdevname(startdev, b)); + if (IS_ERR(start_rdev)) return err; - } + /* NOTE: this can only work for 0.90.0 superblocks */ sb = (mdp_super_t*)page_address(start_rdev->sb_page); @@ -1950,12 +2029,9 @@ static int autostart_array(dev_t startde if (MAJOR(dev) != desc->major || MINOR(dev) != desc->minor) continue; rdev = md_import_device(dev, 0, 0); - if (IS_ERR(rdev)) { - printk(KERN_WARNING "md: could not import %s," - " trying to run array nevertheless.\n", - __bdevname(dev, b)); + if (IS_ERR(rdev)) continue; - } + list_add(&rdev->same_set, &pending_raid_disks); } @@ -2187,42 +2263,6 @@ static int add_new_disk(mddev_t * mddev, return 0; } -static int hot_generate_error(mddev_t * mddev, dev_t dev) -{ - char b[BDEVNAME_SIZE]; - struct request_queue *q; - mdk_rdev_t *rdev; - - if (!mddev->pers) - return -ENODEV; - - printk(KERN_INFO "md: trying to generate %s error in %s ... \n", - __bdevname(dev, b), mdname(mddev)); - - rdev = find_rdev(mddev, dev); - if (!rdev) { - /* MD_BUG(); */ /* like hell - it's not a driver bug */ - return -ENXIO; - } - - if (rdev->desc_nr == -1) { - MD_BUG(); - return -EINVAL; - } - if (!rdev->in_sync) - return -ENODEV; - - q = bdev_get_queue(rdev->bdev); - if (!q) { - MD_BUG(); - return -ENODEV; - } - printk(KERN_INFO "md: okay, generating error!\n"); -// q->oneshot_error = 1; // disabled for now - - return 0; -} - static int hot_remove_disk(mddev_t * mddev, dev_t dev) { char b[BDEVNAME_SIZE]; @@ -2231,9 +2271,6 @@ static int hot_remove_disk(mddev_t * mdd if (!mddev->pers) return -ENODEV; - printk(KERN_INFO "md: trying to remove %s from %s ... \n", - __bdevname(dev, b), mdname(mddev)); - rdev = find_rdev(mddev, dev); if (!rdev) return -ENXIO; @@ -2243,6 +2280,7 @@ static int hot_remove_disk(mddev_t * mdd kick_rdev_from_array(rdev); md_update_sb(mddev); + md_new_event(); return 0; busy: @@ -2261,9 +2299,6 @@ static int hot_add_disk(mddev_t * mddev, if (!mddev->pers) return -ENODEV; - printk(KERN_INFO "md: trying to hot-add %s to %s ... \n", - __bdevname(dev, b), mdname(mddev)); - if (mddev->major_version != 0) { printk(KERN_WARNING "%s: HOT_ADD may only be used with" " version-0 superblocks.\n", @@ -2336,7 +2371,7 @@ static int hot_add_disk(mddev_t * mddev, */ set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); md_wakeup_thread(mddev->thread); - + md_new_event(); return 0; abort_unbind_export: @@ -2523,7 +2558,6 @@ static int set_disk_faulty(mddev_t *mdde static int md_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - char b[BDEVNAME_SIZE]; int err = 0; void __user *argp = (void __user *)arg; struct hd_geometry __user *loc = argp; @@ -2582,8 +2616,7 @@ static int md_ioctl(struct inode *inode, } err = autostart_array(new_decode_dev(arg)); if (err) { - printk(KERN_WARNING "md: autostart %s failed!\n", - __bdevname(arg, b)); + printk(KERN_WARNING "md: autostart failed!\n"); goto abort; } goto done; @@ -2724,9 +2757,7 @@ static int md_ioctl(struct inode *inode, err = add_new_disk(mddev, &info); goto done_unlock; } - case HOT_GENERATE_ERROR: - err = hot_generate_error(mddev, new_decode_dev(arg)); - goto done_unlock; + case HOT_REMOVE_DISK: err = hot_remove_disk(mddev, new_decode_dev(arg)); goto done_unlock; @@ -2953,8 +2984,10 @@ void md_error(mddev_t *mddev, mdk_rdev_t if (!mddev->pers->error_handler) return; mddev->pers->error_handler(mddev,rdev); + set_bit(MD_RECOVERY_INTR, &mddev->recovery); set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); md_wakeup_thread(mddev->thread); + md_new_event(); } /* seq_file implementation /proc/mdstat */ @@ -2985,7 +3018,11 @@ static void status_resync(struct seq_fil unsigned long max_blocks, resync, res, dt, db, rt; resync = (mddev->curr_resync - atomic_read(&mddev->recovery_active))/2; - max_blocks = mddev->size; + + if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) + max_blocks = mddev->resync_max_sectors >> 1; + else + max_blocks = mddev->size; /* * Should not happen. @@ -3097,6 +3134,7 @@ static int md_seq_show(struct seq_file * sector_t size; struct list_head *tmp2; mdk_rdev_t *rdev; + struct mdstat_info *mi = seq->private; int i; if (v == (void*)1) { @@ -3107,11 +3145,13 @@ static int md_seq_show(struct seq_file * seq_printf(seq, "[%s] ", pers[i]->name); spin_unlock(&pers_lock); - seq_printf(seq, "\n"); + mi->reading = atomic_read(&md_event_count); + seq_printf(seq, "\nEvent: %-20ld\n", mi->reading); return 0; } if (v == (void*)2) { status_unused(seq); + mi->seen = mi->reading; return 0; } @@ -3170,19 +3210,74 @@ static struct seq_operations md_seq_ops .show = md_seq_show, }; + static int md_seq_open(struct inode *inode, struct file *file) { int error; + struct mdstat_info *mi = kmalloc(sizeof(*mi), GFP_KERNEL); + if (mi == NULL) + return -ENOMEM; error = seq_open(file, &md_seq_ops); + if (error) + kfree(mi); + else { + struct seq_file *p = file->private_data; + p->private = mi; + mi->acknowledged = mi->seen = mi->reading = 0; + if (file->f_mode & FMODE_WRITE) { + spin_lock(&readers_lock); + list_add(&mi->list, &readers); + spin_unlock(&readers_lock); + } else + INIT_LIST_HEAD(&mi->list); + } return error; } +static int md_seq_release(struct inode *inode, struct file *file) +{ + struct seq_file *m = (struct seq_file*)file->private_data; + struct mdstat_info *mi = m->private; + + m->private = NULL; + if (!list_empty(&mi->list)) { + spin_lock(&readers_lock); + list_del(&mi->list); + spin_unlock(&readers_lock); + } + kfree(mi); + wake_up(&md_event_waiters); + return seq_release(inode, file); +} + +static unsigned int +mdstat_poll(struct file *filp, poll_table *wait) +{ + struct seq_file *m = (struct seq_file*)filp->private_data; + struct mdstat_info*mi = m->private; + int mask; + + if (mi->acknowledged != mi->seen) { + mi->acknowledged = mi->seen; + wake_up(&md_event_waiters); + } + poll_wait(filp, &md_event_waiters, wait); + + /* always allow read */ + mask = POLLIN | POLLRDNORM; + + if (mi->seen < atomic_read(&md_event_count)) + mask |= POLLPRI; + return mask; +} + static struct file_operations md_seq_fops = { .open = md_seq_open, .read = seq_read, .llseek = seq_lseek, - .release = seq_release, + .release = md_seq_release, + .poll = mdstat_poll, }; int register_md_personality(int pnum, mdk_personality_t *p) @@ -3221,11 +3316,6 @@ int unregister_md_personality(int pnum) return 0; } -void md_sync_acct(mdk_rdev_t *rdev, unsigned long nr_sectors) -{ - rdev->bdev->bd_contains->bd_disk->sync_io += nr_sectors; -} - static int is_mddev_idle(mddev_t *mddev) { mdk_rdev_t * rdev; @@ -3238,8 +3328,12 @@ static int is_mddev_idle(mddev_t *mddev) struct gendisk *disk = rdev->bdev->bd_contains->bd_disk; curr_events = disk_stat_read(disk, read_sectors) + disk_stat_read(disk, write_sectors) - - disk->sync_io; - if ((curr_events - rdev->last_events) > 32) { + atomic_read(&disk->sync_io); + /* Allow some slack between valud of curr_events and last_events, + * as there are some uninteresting races. + * Note: the following is an unsigned comparison. + */ + if ((curr_events - rdev->last_events + 32) > 64) { rdev->last_events = curr_events; idle = 0; } @@ -3373,7 +3467,14 @@ static void md_do_sync(mddev_t *mddev) } } while (mddev->curr_resync < 2); - max_sectors = mddev->size << 1; + if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) + /* resync follows the size requested by the personality, + * which default to physical size, but can be virtual size + */ + max_sectors = mddev->resync_max_sectors; + else + /* recovery follows the physical size of devices */ + max_sectors = mddev->size << 1; printk(KERN_INFO "md: syncing RAID array %s\n", mdname(mddev)); printk(KERN_INFO "md: minimum _guaranteed_ reconstruction speed:" @@ -3422,6 +3523,11 @@ static void md_do_sync(mddev_t *mddev) atomic_add(sectors, &mddev->recovery_active); j += sectors; if (j>1) mddev->curr_resync = j; + if (last_check==0) + /* This is the earliest that rebuild will be visible + * in /proc/mdstat + */ + md_new_event(); if (last_check + window > j || j == max_sectors) continue; @@ -3573,6 +3679,7 @@ void md_check_recovery(mddev_t *mddev) /* flag recovery needed just to double check */ set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); wake_up(&resync_wait); + md_new_event(); goto unlock; } if (mddev->recovery) { @@ -3599,9 +3706,10 @@ void md_check_recovery(mddev_t *mddev) ITERATE_RDEV(mddev,rdev,rtmp) if (rdev->raid_disk < 0 && !rdev->faulty) { - if (mddev->pers->hot_add_disk(mddev,rdev)) + if (mddev->pers->hot_add_disk(mddev,rdev)) { spares++; - else + md_new_event(); + } else break; } } @@ -3626,6 +3734,7 @@ void md_check_recovery(mddev_t *mddev) } else { md_wakeup_thread(mddev->sync_thread); } + md_new_event(); } unlock: mddev_unlock(mddev); @@ -3668,7 +3777,7 @@ static void md_geninit(void) dprintk("md: sizeof(mdp_super_t) = %d\n", (int)sizeof(mdp_super_t)); - p = create_proc_entry("mdstat", S_IRUGO, NULL); + p = create_proc_entry("mdstat", S_IRUGO|S_IWUSR, NULL); if (p) p->proc_fops = &md_seq_fops; } @@ -3731,7 +3840,6 @@ void md_autodetect_dev(dev_t dev) static void autostart_arrays(int part) { - char b[BDEVNAME_SIZE]; mdk_rdev_t *rdev; int i; @@ -3741,11 +3849,9 @@ static void autostart_arrays(int part) dev_t dev = detected_devices[i]; rdev = md_import_device(dev,0, 0); - if (IS_ERR(rdev)) { - printk(KERN_ALERT "md: could not import %s!\n", - __bdevname(dev, b)); + if (IS_ERR(rdev)) continue; - } + if (rdev->faulty) { MD_BUG(); continue; @@ -3796,7 +3902,6 @@ module_exit(md_exit) EXPORT_SYMBOL(register_md_personality); EXPORT_SYMBOL(unregister_md_personality); EXPORT_SYMBOL(md_error); -EXPORT_SYMBOL(md_sync_acct); EXPORT_SYMBOL(md_done_sync); EXPORT_SYMBOL(md_write_start); EXPORT_SYMBOL(md_write_end); --- linux-2.6.9-rc1/drivers/md/multipath.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/drivers/md/multipath.c 2004-09-03 00:57:25.802116744 -0700 @@ -54,15 +54,20 @@ static void mp_pool_free(void *mpb, void kfree(mpb); } -static int multipath_map (multipath_conf_t *conf) +static int multipath_map (multipath_conf_t *conf, int retry) { int i, disks = conf->raid_disks; + int failed = -1; /* - * Later we do read balancing on the read side - * now we use the first available disk. + * We cannot tell the difference between a bad device and a bad block. + * So, while we always prefer a path that hasn't seen a failure, + * if there is no other option we accept a device which has seen + * a failure, but only on the first attempt, never on a retry. + * Normally, devices which have failed are removed from the array, + * but this does not happen for the last device. */ - + retry: spin_lock_irq(&conf->device_lock); for (i = 0; i < disks; i++) { mdk_rdev_t *rdev = conf->multipaths[i].rdev; @@ -71,9 +76,23 @@ static int multipath_map (multipath_conf spin_unlock_irq(&conf->device_lock); return i; } + if (rdev) failed = i; + } + if (!retry && failed >= 0) { + mdk_rdev_t *rdev = conf->multipaths[failed].rdev; + atomic_inc(&rdev->nr_pending); + spin_unlock_irq(&conf->device_lock); + return failed; } spin_unlock_irq(&conf->device_lock); + /* sync with any monitoring daemon */ + wait_event(md_event_waiters, + md_event_reached(conf->last_fail_event) || + conf->working_disks >= 1); + if (conf->working_disks) + goto retry; + printk(KERN_ERR "multipath_map(): no more operational IO paths?\n"); return (-1); } @@ -186,7 +205,7 @@ static int multipath_make_request (reque disk_stat_add(mddev->gendisk, read_sectors, bio_sectors(bio)); } - mp_bh->path = multipath_map(conf); + mp_bh->path = multipath_map(conf, 0); if (mp_bh->path < 0) { bio_endio(bio, bio->bi_size, -EIO); mempool_free(mp_bh, conf->pool); @@ -250,32 +269,23 @@ static void multipath_error (mddev_t *md { multipath_conf_t *conf = mddev_to_conf(mddev); - if (conf->working_disks <= 1) { - /* - * Uh oh, we can do nothing if this is our last path, but - * first check if this is a queued request for a device - * which has just failed. - */ - printk(KERN_ALERT - "multipath: only one IO path left and IO error.\n"); - /* leave it active... it's all we have */ - } else { - /* - * Mark disk as unusable - */ - if (!rdev->faulty) { - char b[BDEVNAME_SIZE]; - rdev->in_sync = 0; - rdev->faulty = 1; - mddev->sb_dirty = 1; - conf->working_disks--; - printk(KERN_ALERT "multipath: IO failure on %s," - " disabling IO path. \n Operation continuing" - " on %d IO paths.\n", - bdevname (rdev->bdev,b), - conf->working_disks); - } + conf->last_fail_event = md_new_event(); + /* + * Mark disk as unusable + */ + if (!rdev->faulty) { + char b[BDEVNAME_SIZE]; + rdev->in_sync = 0; + rdev->faulty = 1; + mddev->sb_dirty = 1; + conf->working_disks--; + printk(KERN_ALERT "multipath: IO failure on %s," + " disabling IO path. \n Operation continuing" + " on %d IO paths.\n", + bdevname (rdev->bdev,b), + conf->working_disks); } + } static void print_multipath_conf (multipath_conf_t *conf) @@ -347,10 +357,17 @@ static int multipath_remove_disk(mddev_t print_multipath_conf(conf); spin_lock_irq(&conf->device_lock); + if (conf->working_disks == 0) + /* don't remove devices when none seem to be working. + * We want to have somewhere to keep trying incase it + * is really a device error + */ + goto abort; if (p->rdev) { if (p->rdev->in_sync || atomic_read(&p->rdev->nr_pending)) { - printk(KERN_ERR "hot-remove-disk, slot %d is identified" " but is still operational!\n", number); + printk(KERN_ERR "hot-remove-disk, slot %d is identified" + " but is still operational!\n", number); err = -EBUSY; goto abort; } @@ -397,7 +414,7 @@ static void multipathd (mddev_t *mddev) bio = &mp_bh->bio; bio->bi_sector = mp_bh->master_bio->bi_sector; - if ((mp_bh->path = multipath_map (conf))<0) { + if ((mp_bh->path = multipath_map(conf, 1))<0) { printk(KERN_ALERT "multipath: %s: unrecoverable IO read" " error for block %llu\n", bdevname(bio->bi_bdev,b), --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/md/raid10.c 2004-09-02 20:51:51.000000000 -0700 @@ -0,0 +1,1780 @@ +/* + * raid10.c : Multiple Devices driver for Linux + * + * Copyright (C) 2000-2004 Neil Brown + * + * RAID-10 support for md. + * + * Base on code in raid1.c. See raid1.c for futher copyright information. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example /usr/src/linux/COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +/* + * RAID10 provides a combination of RAID0 and RAID1 functionality. + * The layout of data is defined by + * chunk_size + * raid_disks + * near_copies (stored in low byte of layout) + * far_copies (stored in second byte of layout) + * + * The data to be stored is divided into chunks using chunksize. + * Each device is divided into far_copies sections. + * In each section, chunks are laid out in a style similar to raid0, but + * near_copies copies of each chunk is stored (each on a different drive). + * The starting device for each section is offset near_copies from the starting + * device of the previous section. + * Thus there are (near_copies*far_copies) of each chunk, and each is on a different + * drive. + * near_copies and far_copies must be at least one, and there product is at most + * raid_disks. + */ + +/* + * Number of guaranteed r10bios in case of extreme VM load: + */ +#define NR_RAID10_BIOS 256 + +static void unplug_slaves(mddev_t *mddev); + +static void * r10bio_pool_alloc(int gfp_flags, void *data) +{ + conf_t *conf = data; + r10bio_t *r10_bio; + int size = offsetof(struct r10bio_s, devs[conf->copies]); + + /* allocate a r10bio with room for raid_disks entries in the bios array */ + r10_bio = kmalloc(size, gfp_flags); + if (r10_bio) + memset(r10_bio, 0, size); + else + unplug_slaves(conf->mddev); + + return r10_bio; +} + +static void r10bio_pool_free(void *r10_bio, void *data) +{ + kfree(r10_bio); +} + +#define RESYNC_BLOCK_SIZE (64*1024) +//#define RESYNC_BLOCK_SIZE PAGE_SIZE +#define RESYNC_SECTORS (RESYNC_BLOCK_SIZE >> 9) +#define RESYNC_PAGES ((RESYNC_BLOCK_SIZE + PAGE_SIZE-1) / PAGE_SIZE) +#define RESYNC_WINDOW (2048*1024) + +/* + * When performing a resync, we need to read and compare, so + * we need as many pages are there are copies. + * When performing a recovery, we need 2 bios, one for read, + * one for write (we recover only one drive per r10buf) + * + */ +static void * r10buf_pool_alloc(int gfp_flags, void *data) +{ + conf_t *conf = data; + struct page *page; + r10bio_t *r10_bio; + struct bio *bio; + int i, j; + int nalloc; + + r10_bio = r10bio_pool_alloc(gfp_flags, conf); + if (!r10_bio) { + unplug_slaves(conf->mddev); + return NULL; + } + + if (test_bit(MD_RECOVERY_SYNC, &conf->mddev->recovery)) + nalloc = conf->copies; /* resync */ + else + nalloc = 2; /* recovery */ + + /* + * Allocate bios. + */ + for (j = nalloc ; j-- ; ) { + bio = bio_alloc(gfp_flags, RESYNC_PAGES); + if (!bio) + goto out_free_bio; + r10_bio->devs[j].bio = bio; + } + /* + * Allocate RESYNC_PAGES data pages and attach them + * where needed. + */ + for (j = 0 ; j < nalloc; j++) { + bio = r10_bio->devs[j].bio; + for (i = 0; i < RESYNC_PAGES; i++) { + page = alloc_page(gfp_flags); + if (unlikely(!page)) + goto out_free_pages; + + bio->bi_io_vec[i].bv_page = page; + } + } + + return r10_bio; + +out_free_pages: + for ( ; i > 0 ; i--) + __free_page(bio->bi_io_vec[i-1].bv_page); + while (j--) + for (i = 0; i < RESYNC_PAGES ; i++) + __free_page(r10_bio->devs[j].bio->bi_io_vec[i].bv_page); + j = -1; +out_free_bio: + while ( ++j < nalloc ) + bio_put(r10_bio->devs[j].bio); + r10bio_pool_free(r10_bio, conf); + return NULL; +} + +static void r10buf_pool_free(void *__r10_bio, void *data) +{ + int i; + conf_t *conf = data; + r10bio_t *r10bio = __r10_bio; + int j; + + for (j=0; j < conf->copies; j++) { + struct bio *bio = r10bio->devs[j].bio; + if (bio) { + for (i = 0; i < RESYNC_PAGES; i++) { + __free_page(bio->bi_io_vec[i].bv_page); + bio->bi_io_vec[i].bv_page = NULL; + } + bio_put(bio); + } + } + r10bio_pool_free(r10bio, conf); +} + +static void put_all_bios(conf_t *conf, r10bio_t *r10_bio) +{ + int i; + + for (i = 0; i < conf->copies; i++) { + struct bio **bio = & r10_bio->devs[i].bio; + if (*bio) + bio_put(*bio); + *bio = NULL; + } +} + +static inline void free_r10bio(r10bio_t *r10_bio) +{ + unsigned long flags; + + conf_t *conf = mddev_to_conf(r10_bio->mddev); + + /* + * Wake up any possible resync thread that waits for the device + * to go idle. + */ + spin_lock_irqsave(&conf->resync_lock, flags); + if (!--conf->nr_pending) { + wake_up(&conf->wait_idle); + wake_up(&conf->wait_resume); + } + spin_unlock_irqrestore(&conf->resync_lock, flags); + + put_all_bios(conf, r10_bio); + mempool_free(r10_bio, conf->r10bio_pool); +} + +static inline void put_buf(r10bio_t *r10_bio) +{ + conf_t *conf = mddev_to_conf(r10_bio->mddev); + unsigned long flags; + + mempool_free(r10_bio, conf->r10buf_pool); + + spin_lock_irqsave(&conf->resync_lock, flags); + if (!conf->barrier) + BUG(); + --conf->barrier; + wake_up(&conf->wait_resume); + wake_up(&conf->wait_idle); + + if (!--conf->nr_pending) { + wake_up(&conf->wait_idle); + wake_up(&conf->wait_resume); + } + spin_unlock_irqrestore(&conf->resync_lock, flags); +} + +static void reschedule_retry(r10bio_t *r10_bio) +{ + unsigned long flags; + mddev_t *mddev = r10_bio->mddev; + conf_t *conf = mddev_to_conf(mddev); + + spin_lock_irqsave(&conf->device_lock, flags); + list_add(&r10_bio->retry_list, &conf->retry_list); + spin_unlock_irqrestore(&conf->device_lock, flags); + + md_wakeup_thread(mddev->thread); +} + +/* + * raid_end_bio_io() is called when we have finished servicing a mirrored + * operation and are ready to return a success/failure code to the buffer + * cache layer. + */ +static void raid_end_bio_io(r10bio_t *r10_bio) +{ + struct bio *bio = r10_bio->master_bio; + + bio_endio(bio, bio->bi_size, + test_bit(R10BIO_Uptodate, &r10_bio->state) ? 0 : -EIO); + free_r10bio(r10_bio); +} + +/* + * Update disk head position estimator based on IRQ completion info. + */ +static inline void update_head_pos(int slot, r10bio_t *r10_bio) +{ + conf_t *conf = mddev_to_conf(r10_bio->mddev); + + conf->mirrors[r10_bio->devs[slot].devnum].head_position = + r10_bio->devs[slot].addr + (r10_bio->sectors); +} + +static int raid10_end_read_request(struct bio *bio, unsigned int bytes_done, int error) +{ + int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); + r10bio_t * r10_bio = (r10bio_t *)(bio->bi_private); + int slot, dev; + conf_t *conf = mddev_to_conf(r10_bio->mddev); + + if (bio->bi_size) + return 1; + + slot = r10_bio->read_slot; + dev = r10_bio->devs[slot].devnum; + /* + * this branch is our 'one mirror IO has finished' event handler: + */ + if (!uptodate) + md_error(r10_bio->mddev, conf->mirrors[dev].rdev); + else + /* + * Set R10BIO_Uptodate in our master bio, so that + * we will return a good error code to the higher + * levels even if IO on some other mirrored buffer fails. + * + * The 'master' represents the composite IO operation to + * user-side. So if something waits for IO, then it will + * wait for the 'master' bio. + */ + set_bit(R10BIO_Uptodate, &r10_bio->state); + + update_head_pos(slot, r10_bio); + + /* + * we have only one bio on the read side + */ + if (uptodate) + raid_end_bio_io(r10_bio); + else { + /* + * oops, read error: + */ + char b[BDEVNAME_SIZE]; + if (printk_ratelimit()) + printk(KERN_ERR "raid10: %s: rescheduling sector %llu\n", + bdevname(conf->mirrors[dev].rdev->bdev,b), (unsigned long long)r10_bio->sector); + reschedule_retry(r10_bio); + } + + rdev_dec_pending(conf->mirrors[dev].rdev, conf->mddev); + return 0; +} + +static int raid10_end_write_request(struct bio *bio, unsigned int bytes_done, int error) +{ + int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); + r10bio_t * r10_bio = (r10bio_t *)(bio->bi_private); + int slot, dev; + conf_t *conf = mddev_to_conf(r10_bio->mddev); + + if (bio->bi_size) + return 1; + + for (slot = 0; slot < conf->copies; slot++) + if (r10_bio->devs[slot].bio == bio) + break; + dev = r10_bio->devs[slot].devnum; + + /* + * this branch is our 'one mirror IO has finished' event handler: + */ + if (!uptodate) + md_error(r10_bio->mddev, conf->mirrors[dev].rdev); + else + /* + * Set R10BIO_Uptodate in our master bio, so that + * we will return a good error code for to the higher + * levels even if IO on some other mirrored buffer fails. + * + * The 'master' represents the composite IO operation to + * user-side. So if something waits for IO, then it will + * wait for the 'master' bio. + */ + set_bit(R10BIO_Uptodate, &r10_bio->state); + + update_head_pos(slot, r10_bio); + + /* + * + * Let's see if all mirrored write operations have finished + * already. + */ + if (atomic_dec_and_test(&r10_bio->remaining)) { + md_write_end(r10_bio->mddev); + raid_end_bio_io(r10_bio); + } + + rdev_dec_pending(conf->mirrors[dev].rdev, conf->mddev); + return 0; +} + + +/* + * RAID10 layout manager + * Aswell as the chunksize and raid_disks count, there are two + * parameters: near_copies and far_copies. + * near_copies * far_copies must be <= raid_disks. + * Normally one of these will be 1. + * If both are 1, we get raid0. + * If near_copies == raid_disks, we get raid1. + * + * Chunks are layed out in raid0 style with near_copies copies of the + * first chunk, followed by near_copies copies of the next chunk and + * so on. + * If far_copies > 1, then after 1/far_copies of the array has been assigned + * as described above, we start again with a device offset of near_copies. + * So we effectively have another copy of the whole array further down all + * the drives, but with blocks on different drives. + * With this layout, and block is never stored twice on the one device. + * + * raid10_find_phys finds the sector offset of a given virtual sector + * on each device that it is on. If a block isn't on a device, + * that entry in the array is set to MaxSector. + * + * raid10_find_virt does the reverse mapping, from a device and a + * sector offset to a virtual address + */ + +static void raid10_find_phys(conf_t *conf, r10bio_t *r10bio) +{ + int n,f; + sector_t sector; + sector_t chunk; + sector_t stripe; + int dev; + + int slot = 0; + + /* now calculate first sector/dev */ + chunk = r10bio->sector >> conf->chunk_shift; + sector = r10bio->sector & conf->chunk_mask; + + chunk *= conf->near_copies; + stripe = chunk; + dev = sector_div(stripe, conf->raid_disks); + + sector += stripe << conf->chunk_shift; + + /* and calculate all the others */ + for (n=0; n < conf->near_copies; n++) { + int d = dev; + sector_t s = sector; + r10bio->devs[slot].addr = sector; + r10bio->devs[slot].devnum = d; + slot++; + + for (f = 1; f < conf->far_copies; f++) { + d += conf->near_copies; + if (d >= conf->raid_disks) + d -= conf->raid_disks; + s += conf->stride; + r10bio->devs[slot].devnum = d; + r10bio->devs[slot].addr = s; + slot++; + } + dev++; + if (dev >= conf->raid_disks) { + dev = 0; + sector += (conf->chunk_mask + 1); + } + } + BUG_ON(slot != conf->copies); +} + +static sector_t raid10_find_virt(conf_t *conf, sector_t sector, int dev) +{ + sector_t offset, chunk, vchunk; + + while (sector > conf->stride) { + sector -= conf->stride; + if (dev < conf->near_copies) + dev += conf->raid_disks - conf->near_copies; + else + dev -= conf->near_copies; + } + + offset = sector & conf->chunk_mask; + chunk = sector >> conf->chunk_shift; + vchunk = chunk * conf->raid_disks + dev; + sector_div(vchunk, conf->near_copies); + return (vchunk << conf->chunk_shift) + offset; +} + +/** + * raid10_mergeable_bvec -- tell bio layer if a two requests can be merged + * @q: request queue + * @bio: the buffer head that's been built up so far + * @biovec: the request that could be merged to it. + * + * Return amount of bytes we can accept at this offset + * If near_copies == raid_disk, there are no striping issues, + * but in that case, the function isn't called at all. + */ +static int raid10_mergeable_bvec(request_queue_t *q, struct bio *bio, + struct bio_vec *bio_vec) +{ + mddev_t *mddev = q->queuedata; + sector_t sector = bio->bi_sector + get_start_sect(bio->bi_bdev); + int max; + unsigned int chunk_sectors = mddev->chunk_size >> 9; + unsigned int bio_sectors = bio->bi_size >> 9; + + max = (chunk_sectors - ((sector & (chunk_sectors - 1)) + bio_sectors)) << 9; + if (max < 0) max = 0; /* bio_add cannot handle a negative return */ + if (max <= bio_vec->bv_len && bio_sectors == 0) + return bio_vec->bv_len; + else + return max; +} + +/* + * This routine returns the disk from which the requested read should + * be done. There is a per-array 'next expected sequential IO' sector + * number - if this matches on the next IO then we use the last disk. + * There is also a per-disk 'last know head position' sector that is + * maintained from IRQ contexts, both the normal and the resync IO + * completion handlers update this position correctly. If there is no + * perfect sequential match then we pick the disk whose head is closest. + * + * If there are 2 mirrors in the same 2 devices, performance degrades + * because position is mirror, not device based. + * + * The rdev for the device selected will have nr_pending incremented. + */ + +/* + * FIXME: possibly should rethink readbalancing and do it differently + * depending on near_copies / far_copies geometry. + */ +static int read_balance(conf_t *conf, r10bio_t *r10_bio) +{ + const unsigned long this_sector = r10_bio->sector; + int disk, slot, nslot; + const int sectors = r10_bio->sectors; + sector_t new_distance, current_distance; + + raid10_find_phys(conf, r10_bio); + spin_lock_irq(&conf->device_lock); + /* + * Check if we can balance. We can balance on the whole + * device if no resync is going on, or below the resync window. + * We take the first readable disk when above the resync window. + */ + if (conf->mddev->recovery_cp < MaxSector + && (this_sector + sectors >= conf->next_resync)) { + /* make sure that disk is operational */ + slot = 0; + disk = r10_bio->devs[slot].devnum; + + while (!conf->mirrors[disk].rdev || + !conf->mirrors[disk].rdev->in_sync) { + slot++; + if (slot == conf->copies) { + slot = 0; + disk = -1; + break; + } + disk = r10_bio->devs[slot].devnum; + } + goto rb_out; + } + + + /* make sure the disk is operational */ + slot = 0; + disk = r10_bio->devs[slot].devnum; + while (!conf->mirrors[disk].rdev || + !conf->mirrors[disk].rdev->in_sync) { + slot ++; + if (slot == conf->copies) { + disk = -1; + goto rb_out; + } + disk = r10_bio->devs[slot].devnum; + } + + + current_distance = abs(this_sector - conf->mirrors[disk].head_position); + + /* Find the disk whose head is closest */ + + for (nslot = slot; nslot < conf->copies; nslot++) { + int ndisk = r10_bio->devs[nslot].devnum; + + + if (!conf->mirrors[ndisk].rdev || + !conf->mirrors[ndisk].rdev->in_sync) + continue; + + if (!atomic_read(&conf->mirrors[ndisk].rdev->nr_pending)) { + disk = ndisk; + slot = nslot; + break; + } + new_distance = abs(r10_bio->devs[nslot].addr - + conf->mirrors[ndisk].head_position); + if (new_distance < current_distance) { + current_distance = new_distance; + disk = ndisk; + slot = nslot; + } + } + +rb_out: + r10_bio->read_slot = slot; +/* conf->next_seq_sect = this_sector + sectors;*/ + + if (disk >= 0 && conf->mirrors[disk].rdev) + atomic_inc(&conf->mirrors[disk].rdev->nr_pending); + spin_unlock_irq(&conf->device_lock); + + return disk; +} + +static void unplug_slaves(mddev_t *mddev) +{ + conf_t *conf = mddev_to_conf(mddev); + int i; + unsigned long flags; + + spin_lock_irqsave(&conf->device_lock, flags); + for (i=0; iraid_disks; i++) { + mdk_rdev_t *rdev = conf->mirrors[i].rdev; + if (rdev && atomic_read(&rdev->nr_pending)) { + request_queue_t *r_queue = bdev_get_queue(rdev->bdev); + + atomic_inc(&rdev->nr_pending); + spin_unlock_irqrestore(&conf->device_lock, flags); + + if (r_queue->unplug_fn) + r_queue->unplug_fn(r_queue); + + spin_lock_irqsave(&conf->device_lock, flags); + atomic_dec(&rdev->nr_pending); + } + } + spin_unlock_irqrestore(&conf->device_lock, flags); +} +static void raid10_unplug(request_queue_t *q) +{ + unplug_slaves(q->queuedata); +} + +static int raid10_issue_flush(request_queue_t *q, struct gendisk *disk, + sector_t *error_sector) +{ + mddev_t *mddev = q->queuedata; + conf_t *conf = mddev_to_conf(mddev); + unsigned long flags; + int i, ret = 0; + + spin_lock_irqsave(&conf->device_lock, flags); + for (i=0; iraid_disks; i++) { + mdk_rdev_t *rdev = conf->mirrors[i].rdev; + if (rdev && !rdev->faulty) { + struct block_device *bdev = rdev->bdev; + request_queue_t *r_queue = bdev_get_queue(bdev); + + if (r_queue->issue_flush_fn) { + ret = r_queue->issue_flush_fn(r_queue, bdev->bd_disk, error_sector); + if (ret) + break; + } + } + } + spin_unlock_irqrestore(&conf->device_lock, flags); + return ret; +} + +/* + * Throttle resync depth, so that we can both get proper overlapping of + * requests, but are still able to handle normal requests quickly. + */ +#define RESYNC_DEPTH 32 + +static void device_barrier(conf_t *conf, sector_t sect) +{ + spin_lock_irq(&conf->resync_lock); + wait_event_lock_irq(conf->wait_idle, !waitqueue_active(&conf->wait_resume), + conf->resync_lock, unplug_slaves(conf->mddev)); + + if (!conf->barrier++) { + wait_event_lock_irq(conf->wait_idle, !conf->nr_pending, + conf->resync_lock, unplug_slaves(conf->mddev)); + if (conf->nr_pending) + BUG(); + } + wait_event_lock_irq(conf->wait_resume, conf->barrier < RESYNC_DEPTH, + conf->resync_lock, unplug_slaves(conf->mddev)); + conf->next_resync = sect; + spin_unlock_irq(&conf->resync_lock); +} + +static int make_request(request_queue_t *q, struct bio * bio) +{ + mddev_t *mddev = q->queuedata; + conf_t *conf = mddev_to_conf(mddev); + mirror_info_t *mirror; + r10bio_t *r10_bio; + struct bio *read_bio; + int i; + int chunk_sects = conf->chunk_mask + 1; + + /* If this request crosses a chunk boundary, we need to + * split it. This will only happen for 1 PAGE (or less) requests. + */ + if (unlikely( (bio->bi_sector & conf->chunk_mask) + (bio->bi_size >> 9) + > chunk_sects && + conf->near_copies < conf->raid_disks)) { + struct bio_pair *bp; + /* Sanity check -- queue functions should prevent this happening */ + if (bio->bi_vcnt != 1 || + bio->bi_idx != 0) + goto bad_map; + /* This is a one page bio that upper layers + * refuse to split for us, so we need to split it. + */ + bp = bio_split(bio, bio_split_pool, + chunk_sects - (bio->bi_sector & (chunk_sects - 1)) ); + if (make_request(q, &bp->bio1)) + generic_make_request(&bp->bio1); + if (make_request(q, &bp->bio2)) + generic_make_request(&bp->bio2); + + bio_pair_release(bp); + return 0; + bad_map: + printk("raid10_make_request bug: can't convert block across chunks" + " or bigger than %dk %llu %d\n", chunk_sects/2, + (unsigned long long)bio->bi_sector, bio->bi_size >> 10); + + bio_io_error(bio, bio->bi_size); + return 0; + } + + /* + * Register the new request and wait if the reconstruction + * thread has put up a bar for new requests. + * Continue immediately if no resync is active currently. + */ + spin_lock_irq(&conf->resync_lock); + wait_event_lock_irq(conf->wait_resume, !conf->barrier, conf->resync_lock, ); + conf->nr_pending++; + spin_unlock_irq(&conf->resync_lock); + + if (bio_data_dir(bio)==WRITE) { + disk_stat_inc(mddev->gendisk, writes); + disk_stat_add(mddev->gendisk, write_sectors, bio_sectors(bio)); + } else { + disk_stat_inc(mddev->gendisk, reads); + disk_stat_add(mddev->gendisk, read_sectors, bio_sectors(bio)); + } + + r10_bio = mempool_alloc(conf->r10bio_pool, GFP_NOIO); + + r10_bio->master_bio = bio; + r10_bio->sectors = bio->bi_size >> 9; + + r10_bio->mddev = mddev; + r10_bio->sector = bio->bi_sector; + + if (bio_data_dir(bio) == READ) { + /* + * read balancing logic: + */ + int disk = read_balance(conf, r10_bio); + int slot = r10_bio->read_slot; + if (disk < 0) { + raid_end_bio_io(r10_bio); + return 0; + } + mirror = conf->mirrors + disk; + + read_bio = bio_clone(bio, GFP_NOIO); + + r10_bio->devs[slot].bio = read_bio; + + read_bio->bi_sector = r10_bio->devs[slot].addr + + mirror->rdev->data_offset; + read_bio->bi_bdev = mirror->rdev->bdev; + read_bio->bi_end_io = raid10_end_read_request; + read_bio->bi_rw = READ; + read_bio->bi_private = r10_bio; + + generic_make_request(read_bio); + return 0; + } + + /* + * WRITE: + */ + /* first select target devices under spinlock and + * inc refcount on their rdev. Record them by setting + * bios[x] to bio + */ + raid10_find_phys(conf, r10_bio); + spin_lock_irq(&conf->device_lock); + for (i = 0; i < conf->copies; i++) { + int d = r10_bio->devs[i].devnum; + if (conf->mirrors[d].rdev && + !conf->mirrors[d].rdev->faulty) { + atomic_inc(&conf->mirrors[d].rdev->nr_pending); + r10_bio->devs[i].bio = bio; + } else + r10_bio->devs[i].bio = NULL; + } + spin_unlock_irq(&conf->device_lock); + + atomic_set(&r10_bio->remaining, 1); + md_write_start(mddev); + for (i = 0; i < conf->copies; i++) { + struct bio *mbio; + int d = r10_bio->devs[i].devnum; + if (!r10_bio->devs[i].bio) + continue; + + mbio = bio_clone(bio, GFP_NOIO); + r10_bio->devs[i].bio = mbio; + + mbio->bi_sector = r10_bio->devs[i].addr+ + conf->mirrors[d].rdev->data_offset; + mbio->bi_bdev = conf->mirrors[d].rdev->bdev; + mbio->bi_end_io = raid10_end_write_request; + mbio->bi_rw = WRITE; + mbio->bi_private = r10_bio; + + atomic_inc(&r10_bio->remaining); + generic_make_request(mbio); + } + + if (atomic_dec_and_test(&r10_bio->remaining)) { + md_write_end(mddev); + raid_end_bio_io(r10_bio); + } + + return 0; +} + +static void status(struct seq_file *seq, mddev_t *mddev) +{ + conf_t *conf = mddev_to_conf(mddev); + int i; + + if (conf->near_copies < conf->raid_disks) + seq_printf(seq, " %dK chunks", mddev->chunk_size/1024); + if (conf->near_copies > 1) + seq_printf(seq, " %d near-copies", conf->near_copies); + if (conf->far_copies > 1) + seq_printf(seq, " %d far-copies", conf->far_copies); + + seq_printf(seq, " [%d/%d] [", conf->raid_disks, + conf->working_disks); + for (i = 0; i < conf->raid_disks; i++) + seq_printf(seq, "%s", + conf->mirrors[i].rdev && + conf->mirrors[i].rdev->in_sync ? "U" : "_"); + seq_printf(seq, "]"); +} + +static void error(mddev_t *mddev, mdk_rdev_t *rdev) +{ + char b[BDEVNAME_SIZE]; + conf_t *conf = mddev_to_conf(mddev); + + /* + * If it is not operational, then we have already marked it as dead + * else if it is the last working disks, ignore the error, let the + * next level up know. + * else mark the drive as failed + */ + if (rdev->in_sync + && conf->working_disks == 1) + /* + * Don't fail the drive, just return an IO error. + * The test should really be more sophisticated than + * "working_disks == 1", but it isn't critical, and + * can wait until we do more sophisticated "is the drive + * really dead" tests... + */ + return; + if (rdev->in_sync) { + mddev->degraded++; + conf->working_disks--; + /* + * if recovery is running, make sure it aborts. + */ + set_bit(MD_RECOVERY_ERR, &mddev->recovery); + } + rdev->in_sync = 0; + rdev->faulty = 1; + mddev->sb_dirty = 1; + printk(KERN_ALERT "raid10: Disk failure on %s, disabling device. \n" + " Operation continuing on %d devices\n", + bdevname(rdev->bdev,b), conf->working_disks); +} + +static void print_conf(conf_t *conf) +{ + int i; + mirror_info_t *tmp; + + printk("RAID10 conf printout:\n"); + if (!conf) { + printk("(!conf)\n"); + return; + } + printk(" --- wd:%d rd:%d\n", conf->working_disks, + conf->raid_disks); + + for (i = 0; i < conf->raid_disks; i++) { + char b[BDEVNAME_SIZE]; + tmp = conf->mirrors + i; + if (tmp->rdev) + printk(" disk %d, wo:%d, o:%d, dev:%s\n", + i, !tmp->rdev->in_sync, !tmp->rdev->faulty, + bdevname(tmp->rdev->bdev,b)); + } +} + +static void close_sync(conf_t *conf) +{ + spin_lock_irq(&conf->resync_lock); + wait_event_lock_irq(conf->wait_resume, !conf->barrier, + conf->resync_lock, unplug_slaves(conf->mddev)); + spin_unlock_irq(&conf->resync_lock); + + if (conf->barrier) BUG(); + if (waitqueue_active(&conf->wait_idle)) BUG(); + + mempool_destroy(conf->r10buf_pool); + conf->r10buf_pool = NULL; +} + +static int raid10_spare_active(mddev_t *mddev) +{ + int i; + conf_t *conf = mddev->private; + mirror_info_t *tmp; + + spin_lock_irq(&conf->device_lock); + /* + * Find all non-in_sync disks within the RAID10 configuration + * and mark them in_sync + */ + for (i = 0; i < conf->raid_disks; i++) { + tmp = conf->mirrors + i; + if (tmp->rdev + && !tmp->rdev->faulty + && !tmp->rdev->in_sync) { + conf->working_disks++; + mddev->degraded--; + tmp->rdev->in_sync = 1; + } + } + spin_unlock_irq(&conf->device_lock); + + print_conf(conf); + return 0; +} + + +static int raid10_add_disk(mddev_t *mddev, mdk_rdev_t *rdev) +{ + conf_t *conf = mddev->private; + int found = 0; + int mirror; + mirror_info_t *p; + + if (mddev->recovery_cp < MaxSector) + /* only hot-add to in-sync arrays, as recovery is + * very different from resync + */ + return 0; + spin_lock_irq(&conf->device_lock); + for (mirror=0; mirror < mddev->raid_disks; mirror++) + if ( !(p=conf->mirrors+mirror)->rdev) { + p->rdev = rdev; + + blk_queue_stack_limits(mddev->queue, + rdev->bdev->bd_disk->queue); + /* as we don't honour merge_bvec_fn, we must never risk + * violating it, so limit ->max_sector to one PAGE, as + * a one page request is never in violation. + */ + if (rdev->bdev->bd_disk->queue->merge_bvec_fn && + mddev->queue->max_sectors > (PAGE_SIZE>>9)) + mddev->queue->max_sectors = (PAGE_SIZE>>9); + + p->head_position = 0; + rdev->raid_disk = mirror; + found = 1; + break; + } + spin_unlock_irq(&conf->device_lock); + + print_conf(conf); + return found; +} + +static int raid10_remove_disk(mddev_t *mddev, int number) +{ + conf_t *conf = mddev->private; + int err = 1; + mirror_info_t *p = conf->mirrors+ number; + + print_conf(conf); + spin_lock_irq(&conf->device_lock); + if (p->rdev) { + if (p->rdev->in_sync || + atomic_read(&p->rdev->nr_pending)) { + err = -EBUSY; + goto abort; + } + p->rdev = NULL; + err = 0; + } + if (err) + MD_BUG(); +abort: + spin_unlock_irq(&conf->device_lock); + + print_conf(conf); + return err; +} + + +static int end_sync_read(struct bio *bio, unsigned int bytes_done, int error) +{ + int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); + r10bio_t * r10_bio = (r10bio_t *)(bio->bi_private); + conf_t *conf = mddev_to_conf(r10_bio->mddev); + int i,d; + + if (bio->bi_size) + return 1; + + for (i=0; icopies; i++) + if (r10_bio->devs[i].bio == bio) + break; + if (i == conf->copies) + BUG(); + update_head_pos(i, r10_bio); + d = r10_bio->devs[i].devnum; + if (!uptodate) + md_error(r10_bio->mddev, + conf->mirrors[d].rdev); + + /* for reconstruct, we always reschedule after a read. + * for resync, only after all reads + */ + if (test_bit(R10BIO_IsRecover, &r10_bio->state) || + atomic_dec_and_test(&r10_bio->remaining)) { + /* we have read all the blocks, + * do the comparison in process context in raid10d + */ + reschedule_retry(r10_bio); + } + rdev_dec_pending(conf->mirrors[d].rdev, conf->mddev); + return 0; +} + +static int end_sync_write(struct bio *bio, unsigned int bytes_done, int error) +{ + int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); + r10bio_t * r10_bio = (r10bio_t *)(bio->bi_private); + mddev_t *mddev = r10_bio->mddev; + conf_t *conf = mddev_to_conf(mddev); + int i,d; + + if (bio->bi_size) + return 1; + + for (i = 0; i < conf->copies; i++) + if (r10_bio->devs[i].bio == bio) + break; + d = r10_bio->devs[i].devnum; + + if (!uptodate) + md_error(mddev, conf->mirrors[d].rdev); + update_head_pos(i, r10_bio); + + while (atomic_dec_and_test(&r10_bio->remaining)) { + if (r10_bio->master_bio == NULL) { + /* the primary of several recovery bios */ + md_done_sync(mddev, r10_bio->sectors, 1); + put_buf(r10_bio); + break; + } else { + r10bio_t *r10_bio2 = (r10bio_t *)r10_bio->master_bio; + put_buf(r10_bio); + r10_bio = r10_bio2; + } + } + rdev_dec_pending(conf->mirrors[d].rdev, mddev); + return 0; +} + +/* + * Note: sync and recover and handled very differently for raid10 + * This code is for resync. + * For resync, we read through virtual addresses and read all blocks. + * If there is any error, we schedule a write. The lowest numbered + * drive is authoritative. + * However requests come for physical address, so we need to map. + * For every physical address there are raid_disks/copies virtual addresses, + * which is always are least one, but is not necessarly an integer. + * This means that a physical address can span multiple chunks, so we may + * have to submit multiple io requests for a single sync request. + */ +/* + * We check if all blocks are in-sync and only write to blocks that + * aren't in sync + */ +static void sync_request_write(mddev_t *mddev, r10bio_t *r10_bio) +{ + conf_t *conf = mddev_to_conf(mddev); + int i, first; + struct bio *tbio, *fbio; + + atomic_set(&r10_bio->remaining, 1); + + /* find the first device with a block */ + for (i=0; icopies; i++) + if (test_bit(BIO_UPTODATE, &r10_bio->devs[i].bio->bi_flags)) + break; + + if (i == conf->copies) + goto done; + + first = i; + fbio = r10_bio->devs[i].bio; + + /* now find blocks with errors */ + for (i=first+1 ; i < conf->copies ; i++) { + int vcnt, j, d; + + if (!test_bit(BIO_UPTODATE, &r10_bio->devs[i].bio->bi_flags)) + continue; + /* We know that the bi_io_vec layout is the same for + * both 'first' and 'i', so we just compare them. + * All vec entries are PAGE_SIZE; + */ + tbio = r10_bio->devs[i].bio; + vcnt = r10_bio->sectors >> (PAGE_SHIFT-9); + for (j = 0; j < vcnt; j++) + if (memcmp(page_address(fbio->bi_io_vec[j].bv_page), + page_address(tbio->bi_io_vec[j].bv_page), + PAGE_SIZE)) + break; + if (j == vcnt) + continue; + /* Ok, we need to write this bio + * First we need to fixup bv_offset, bv_len and + * bi_vecs, as the read request might have corrupted these + */ + tbio->bi_vcnt = vcnt; + tbio->bi_size = r10_bio->sectors << 9; + tbio->bi_idx = 0; + tbio->bi_phys_segments = 0; + tbio->bi_hw_segments = 0; + tbio->bi_hw_front_size = 0; + tbio->bi_hw_back_size = 0; + tbio->bi_flags &= ~(BIO_POOL_MASK - 1); + tbio->bi_flags |= 1 << BIO_UPTODATE; + tbio->bi_next = NULL; + tbio->bi_rw = WRITE; + tbio->bi_private = r10_bio; + tbio->bi_sector = r10_bio->devs[i].addr; + + for (j=0; j < vcnt ; j++) { + tbio->bi_io_vec[j].bv_offset = 0; + tbio->bi_io_vec[j].bv_len = PAGE_SIZE; + + memcpy(page_address(tbio->bi_io_vec[j].bv_page), + page_address(fbio->bi_io_vec[j].bv_page), + PAGE_SIZE); + } + tbio->bi_end_io = end_sync_write; + + d = r10_bio->devs[i].devnum; + atomic_inc(&conf->mirrors[d].rdev->nr_pending); + atomic_inc(&r10_bio->remaining); + md_sync_acct(conf->mirrors[d].rdev->bdev, tbio->bi_size >> 9); + + generic_make_request(tbio); + } + +done: + if (atomic_dec_and_test(&r10_bio->remaining)) { + md_done_sync(mddev, r10_bio->sectors, 1); + put_buf(r10_bio); + } +} + +/* + * Now for the recovery code. + * Recovery happens across physical sectors. + * We recover all non-is_sync drives by finding the virtual address of + * each, and then choose a working drive that also has that virt address. + * There is a separate r10_bio for each non-in_sync drive. + * Only the first two slots are in use. The first for reading, + * The second for writing. + * + */ + +static void recovery_request_write(mddev_t *mddev, r10bio_t *r10_bio) +{ + conf_t *conf = mddev_to_conf(mddev); + int i, d; + struct bio *bio, *wbio; + + + /* move the pages across to the second bio + * and submit the write request + */ + bio = r10_bio->devs[0].bio; + wbio = r10_bio->devs[1].bio; + for (i=0; i < wbio->bi_vcnt; i++) { + struct page *p = bio->bi_io_vec[i].bv_page; + bio->bi_io_vec[i].bv_page = wbio->bi_io_vec[i].bv_page; + wbio->bi_io_vec[i].bv_page = p; + } + d = r10_bio->devs[1].devnum; + + atomic_inc(&conf->mirrors[d].rdev->nr_pending); + md_sync_acct(conf->mirrors[d].rdev->bdev, wbio->bi_size >> 9); + generic_make_request(wbio); +} + + +/* + * This is a kernel thread which: + * + * 1. Retries failed read operations on working mirrors. + * 2. Updates the raid superblock when problems encounter. + * 3. Performs writes following reads for array syncronising. + */ + +static void raid10d(mddev_t *mddev) +{ + r10bio_t *r10_bio; + struct bio *bio; + unsigned long flags; + conf_t *conf = mddev_to_conf(mddev); + struct list_head *head = &conf->retry_list; + int unplug=0; + mdk_rdev_t *rdev; + + md_check_recovery(mddev); + md_handle_safemode(mddev); + + for (;;) { + char b[BDEVNAME_SIZE]; + spin_lock_irqsave(&conf->device_lock, flags); + if (list_empty(head)) + break; + r10_bio = list_entry(head->prev, r10bio_t, retry_list); + list_del(head->prev); + spin_unlock_irqrestore(&conf->device_lock, flags); + + mddev = r10_bio->mddev; + conf = mddev_to_conf(mddev); + if (test_bit(R10BIO_IsSync, &r10_bio->state)) { + sync_request_write(mddev, r10_bio); + unplug = 1; + } else if (test_bit(R10BIO_IsRecover, &r10_bio->state)) { + recovery_request_write(mddev, r10_bio); + unplug = 1; + } else { + int mirror; + bio = r10_bio->devs[r10_bio->read_slot].bio; + r10_bio->devs[r10_bio->read_slot].bio = NULL; + mirror = read_balance(conf, r10_bio); + r10_bio->devs[r10_bio->read_slot].bio = bio; + if (mirror == -1) { + printk(KERN_ALERT "raid10: %s: unrecoverable I/O" + " read error for block %llu\n", + bdevname(bio->bi_bdev,b), + (unsigned long long)r10_bio->sector); + raid_end_bio_io(r10_bio); + } else { + rdev = conf->mirrors[mirror].rdev; + if (printk_ratelimit()) + printk(KERN_ERR "raid10: %s: redirecting sector %llu to" + " another mirror\n", + bdevname(rdev->bdev,b), + (unsigned long long)r10_bio->sector); + bio->bi_bdev = rdev->bdev; + bio->bi_sector = r10_bio->devs[r10_bio->read_slot].addr + + rdev->data_offset; + bio->bi_next = NULL; + bio->bi_flags &= (1<bi_flags |= 1 << BIO_UPTODATE; + bio->bi_idx = 0; + bio->bi_size = r10_bio->sectors << 9; + bio->bi_rw = READ; + unplug = 1; + generic_make_request(bio); + } + } + } + spin_unlock_irqrestore(&conf->device_lock, flags); + if (unplug) + unplug_slaves(mddev); +} + + +static int init_resync(conf_t *conf) +{ + int buffs; + + buffs = RESYNC_WINDOW / RESYNC_BLOCK_SIZE; + if (conf->r10buf_pool) + BUG(); + conf->r10buf_pool = mempool_create(buffs, r10buf_pool_alloc, r10buf_pool_free, conf); + if (!conf->r10buf_pool) + return -ENOMEM; + conf->next_resync = 0; + return 0; +} + +/* + * perform a "sync" on one "block" + * + * We need to make sure that no normal I/O request - particularly write + * requests - conflict with active sync requests. + * + * This is achieved by tracking pending requests and a 'barrier' concept + * that can be installed to exclude normal IO requests. + * + * Resync and recovery are handled very differently. + * We differentiate by looking at MD_RECOVERY_SYNC in mddev->recovery. + * + * For resync, we iterate over virtual addresses, read all copies, + * and update if there are differences. If only one copy is live, + * skip it. + * For recovery, we iterate over physical addresses, read a good + * value for each non-in_sync drive, and over-write. + * + * So, for recovery we may have several outstanding complex requests for a + * given address, one for each out-of-sync device. We model this by allocating + * a number of r10_bio structures, one for each out-of-sync device. + * As we setup these structures, we collect all bio's together into a list + * which we then process collectively to add pages, and then process again + * to pass to generic_make_request. + * + * The r10_bio structures are linked using a borrowed master_bio pointer. + * This link is counted in ->remaining. When the r10_bio that points to NULL + * has its remaining count decremented to 0, the whole complex operation + * is complete. + * + */ + +static int sync_request(mddev_t *mddev, sector_t sector_nr, int go_faster) +{ + conf_t *conf = mddev_to_conf(mddev); + r10bio_t *r10_bio; + struct bio *biolist = NULL, *bio; + sector_t max_sector, nr_sectors; + int disk; + int i; + + sector_t sectors_skipped = 0; + int chunks_skipped = 0; + + if (!conf->r10buf_pool) + if (init_resync(conf)) + return -ENOMEM; + + skipped: + max_sector = mddev->size << 1; + if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) + max_sector = mddev->resync_max_sectors; + if (sector_nr >= max_sector) { + close_sync(conf); + return sectors_skipped; + } + if (chunks_skipped >= conf->raid_disks) { + /* if there has been nothing to do on any drive, + * then there is nothing to do at all.. + */ + sector_t sec = max_sector - sector_nr; + md_done_sync(mddev, sec, 1); + return sec + sectors_skipped; + } + + /* make sure whole request will fit in a chunk - if chunks + * are meaningful + */ + if (conf->near_copies < conf->raid_disks && + max_sector > (sector_nr | conf->chunk_mask)) + max_sector = (sector_nr | conf->chunk_mask) + 1; + /* + * If there is non-resync activity waiting for us then + * put in a delay to throttle resync. + */ + if (!go_faster && waitqueue_active(&conf->wait_resume)) + schedule_timeout(HZ); + device_barrier(conf, sector_nr + RESYNC_SECTORS); + + /* Again, very different code for resync and recovery. + * Both must result in an r10bio with a list of bios that + * have bi_end_io, bi_sector, bi_bdev set, + * and bi_private set to the r10bio. + * For recovery, we may actually create several r10bios + * with 2 bios in each, that correspond to the bios in the main one. + * In this case, the subordinate r10bios link back through a + * borrowed master_bio pointer, and the counter in the master + * includes a ref from each subordinate. + */ + /* First, we decide what to do and set ->bi_end_io + * To end_sync_read if we want to read, and + * end_sync_write if we will want to write. + */ + + if (!test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) { + /* recovery... the complicated one */ + int i, j, k; + r10_bio = NULL; + + for (i=0 ; iraid_disks; i++) + if (conf->mirrors[i].rdev && + !conf->mirrors[i].rdev->in_sync) { + /* want to reconstruct this device */ + r10bio_t *rb2 = r10_bio; + + r10_bio = mempool_alloc(conf->r10buf_pool, GFP_NOIO); + spin_lock_irq(&conf->resync_lock); + conf->nr_pending++; + if (rb2) conf->barrier++; + spin_unlock_irq(&conf->resync_lock); + atomic_set(&r10_bio->remaining, 0); + + r10_bio->master_bio = (struct bio*)rb2; + if (rb2) + atomic_inc(&rb2->remaining); + r10_bio->mddev = mddev; + set_bit(R10BIO_IsRecover, &r10_bio->state); + r10_bio->sector = raid10_find_virt(conf, sector_nr, i); + raid10_find_phys(conf, r10_bio); + for (j=0; jcopies;j++) { + int d = r10_bio->devs[j].devnum; + if (conf->mirrors[d].rdev && + conf->mirrors[d].rdev->in_sync) { + /* This is where we read from */ + bio = r10_bio->devs[0].bio; + bio->bi_next = biolist; + biolist = bio; + bio->bi_private = r10_bio; + bio->bi_end_io = end_sync_read; + bio->bi_rw = 0; + bio->bi_sector = r10_bio->devs[j].addr + + conf->mirrors[d].rdev->data_offset; + bio->bi_bdev = conf->mirrors[d].rdev->bdev; + atomic_inc(&conf->mirrors[d].rdev->nr_pending); + atomic_inc(&r10_bio->remaining); + /* and we write to 'i' */ + + for (k=0; kcopies; k++) + if (r10_bio->devs[k].devnum == i) + break; + bio = r10_bio->devs[1].bio; + bio->bi_next = biolist; + biolist = bio; + bio->bi_private = r10_bio; + bio->bi_end_io = end_sync_write; + bio->bi_rw = 1; + bio->bi_sector = r10_bio->devs[k].addr + + conf->mirrors[i].rdev->data_offset; + bio->bi_bdev = conf->mirrors[i].rdev->bdev; + + r10_bio->devs[0].devnum = d; + r10_bio->devs[1].devnum = i; + + break; + } + } + if (j == conf->copies) { + BUG(); + } + } + if (biolist == NULL) { + while (r10_bio) { + r10bio_t *rb2 = r10_bio; + r10_bio = (r10bio_t*) rb2->master_bio; + rb2->master_bio = NULL; + put_buf(rb2); + } + goto giveup; + } + } else { + /* resync. Schedule a read for every block at this virt offset */ + int count = 0; + r10_bio = mempool_alloc(conf->r10buf_pool, GFP_NOIO); + + spin_lock_irq(&conf->resync_lock); + conf->nr_pending++; + spin_unlock_irq(&conf->resync_lock); + + r10_bio->mddev = mddev; + atomic_set(&r10_bio->remaining, 0); + + r10_bio->master_bio = NULL; + r10_bio->sector = sector_nr; + set_bit(R10BIO_IsSync, &r10_bio->state); + raid10_find_phys(conf, r10_bio); + r10_bio->sectors = (sector_nr | conf->chunk_mask) - sector_nr +1; + spin_lock_irq(&conf->device_lock); + for (i=0; icopies; i++) { + int d = r10_bio->devs[i].devnum; + bio = r10_bio->devs[i].bio; + bio->bi_end_io = NULL; + if (conf->mirrors[d].rdev == NULL || + conf->mirrors[d].rdev->faulty) + continue; + atomic_inc(&conf->mirrors[d].rdev->nr_pending); + atomic_inc(&r10_bio->remaining); + bio->bi_next = biolist; + biolist = bio; + bio->bi_private = r10_bio; + bio->bi_end_io = end_sync_read; + bio->bi_rw = 0; + bio->bi_sector = r10_bio->devs[i].addr + + conf->mirrors[d].rdev->data_offset; + bio->bi_bdev = conf->mirrors[d].rdev->bdev; + count++; + } + spin_unlock_irq(&conf->device_lock); + if (count < 2) { + for (i=0; icopies; i++) { + int d = r10_bio->devs[i].devnum; + if (r10_bio->devs[i].bio->bi_end_io) + atomic_dec(&conf->mirrors[d].rdev->nr_pending); + } + put_buf(r10_bio); + goto giveup; + } + } + + for (bio = biolist; bio ; bio=bio->bi_next) { + + bio->bi_flags &= ~(BIO_POOL_MASK - 1); + if (bio->bi_end_io) + bio->bi_flags |= 1 << BIO_UPTODATE; + bio->bi_vcnt = 0; + bio->bi_idx = 0; + bio->bi_phys_segments = 0; + bio->bi_hw_segments = 0; + bio->bi_size = 0; + } + + nr_sectors = 0; + do { + struct page *page; + int len = PAGE_SIZE; + disk = 0; + if (sector_nr + (len>>9) > max_sector) + len = (max_sector - sector_nr) << 9; + if (len == 0) + break; + for (bio= biolist ; bio ; bio=bio->bi_next) { + page = bio->bi_io_vec[bio->bi_vcnt].bv_page; + if (bio_add_page(bio, page, len, 0) == 0) { + /* stop here */ + struct bio *bio2; + bio->bi_io_vec[bio->bi_vcnt].bv_page = page; + for (bio2 = biolist; bio2 && bio2 != bio; bio2 = bio2->bi_next) { + /* remove last page from this bio */ + bio2->bi_vcnt--; + bio2->bi_size -= len; + bio2->bi_flags &= ~(1<< BIO_SEG_VALID); + } + goto bio_full; + } + disk = i; + } + nr_sectors += len>>9; + sector_nr += len>>9; + } while (biolist->bi_vcnt < RESYNC_PAGES); + bio_full: + r10_bio->sectors = nr_sectors; + + while (biolist) { + bio = biolist; + biolist = biolist->bi_next; + + bio->bi_next = NULL; + r10_bio = bio->bi_private; + r10_bio->sectors = nr_sectors; + + if (bio->bi_end_io == end_sync_read) { + md_sync_acct(bio->bi_bdev, nr_sectors); + generic_make_request(bio); + } + } + + return nr_sectors; + giveup: + /* There is nowhere to write, so all non-sync + * drives must be failed, so try the next chunk... + */ + { + int sec = max_sector - sector_nr; + sectors_skipped += sec; + chunks_skipped ++; + sector_nr = max_sector; + md_done_sync(mddev, sec, 1); + goto skipped; + } +} + +static int run(mddev_t *mddev) +{ + conf_t *conf; + int i, disk_idx; + mirror_info_t *disk; + mdk_rdev_t *rdev; + struct list_head *tmp; + int nc, fc; + sector_t stride, size; + + if (mddev->level != 10) { + printk(KERN_ERR "raid10: %s: raid level not set correctly... (%d)\n", + mdname(mddev), mddev->level); + goto out; + } + nc = mddev->layout & 255; + fc = (mddev->layout >> 8) & 255; + if ((nc*fc) <2 || (nc*fc) > mddev->raid_disks || + (mddev->layout >> 16)) { + printk(KERN_ERR "raid10: %s: unsupported raid10 layout: 0x%8x\n", + mdname(mddev), mddev->layout); + goto out; + } + /* + * copy the already verified devices into our private RAID10 + * bookkeeping area. [whatever we allocate in run(), + * should be freed in stop()] + */ + conf = kmalloc(sizeof(conf_t), GFP_KERNEL); + mddev->private = conf; + if (!conf) { + printk(KERN_ERR "raid10: couldn't allocate memory for %s\n", + mdname(mddev)); + goto out; + } + memset(conf, 0, sizeof(*conf)); + conf->mirrors = kmalloc(sizeof(struct mirror_info)*mddev->raid_disks, + GFP_KERNEL); + if (!conf->mirrors) { + printk(KERN_ERR "raid10: couldn't allocate memory for %s\n", + mdname(mddev)); + goto out_free_conf; + } + memset(conf->mirrors, 0, sizeof(struct mirror_info)*mddev->raid_disks); + + conf->near_copies = nc; + conf->far_copies = fc; + conf->copies = nc*fc; + conf->chunk_mask = (sector_t)(mddev->chunk_size>>9)-1; + conf->chunk_shift = ffz(~mddev->chunk_size) - 9; + stride = mddev->size >> (conf->chunk_shift-1); + sector_div(stride, fc); + conf->stride = stride << conf->chunk_shift; + + conf->r10bio_pool = mempool_create(NR_RAID10_BIOS, r10bio_pool_alloc, + r10bio_pool_free, conf); + if (!conf->r10bio_pool) { + printk(KERN_ERR "raid10: couldn't allocate memory for %s\n", + mdname(mddev)); + goto out_free_conf; + } + mddev->queue->unplug_fn = raid10_unplug; + + mddev->queue->issue_flush_fn = raid10_issue_flush; + + ITERATE_RDEV(mddev, rdev, tmp) { + disk_idx = rdev->raid_disk; + if (disk_idx >= mddev->raid_disks + || disk_idx < 0) + continue; + disk = conf->mirrors + disk_idx; + + disk->rdev = rdev; + + blk_queue_stack_limits(mddev->queue, + rdev->bdev->bd_disk->queue); + /* as we don't honour merge_bvec_fn, we must never risk + * violating it, so limit ->max_sector to one PAGE, as + * a one page request is never in violation. + */ + if (rdev->bdev->bd_disk->queue->merge_bvec_fn && + mddev->queue->max_sectors > (PAGE_SIZE>>9)) + mddev->queue->max_sectors = (PAGE_SIZE>>9); + + disk->head_position = 0; + if (!rdev->faulty && rdev->in_sync) + conf->working_disks++; + } + conf->raid_disks = mddev->raid_disks; + conf->mddev = mddev; + conf->device_lock = SPIN_LOCK_UNLOCKED; + INIT_LIST_HEAD(&conf->retry_list); + + conf->resync_lock = SPIN_LOCK_UNLOCKED; + init_waitqueue_head(&conf->wait_idle); + init_waitqueue_head(&conf->wait_resume); + + if (!conf->working_disks) { + printk(KERN_ERR "raid10: no operational mirrors for %s\n", + mdname(mddev)); + goto out_free_conf; + } + + mddev->degraded = 0; + for (i = 0; i < conf->raid_disks; i++) { + + disk = conf->mirrors + i; + + if (!disk->rdev) { + disk->head_position = 0; + mddev->degraded++; + } + } + + + mddev->thread = md_register_thread(raid10d, mddev, "%s_raid10"); + if (!mddev->thread) { + printk(KERN_ERR + "raid10: couldn't allocate thread for %s\n", + mdname(mddev)); + goto out_free_conf; + } + + printk(KERN_INFO + "raid10: raid set %s active with %d out of %d devices\n", + mdname(mddev), mddev->raid_disks - mddev->degraded, + mddev->raid_disks); + /* + * Ok, everything is just fine now + */ + size = conf->stride * conf->raid_disks; + sector_div(size, conf->near_copies); + mddev->array_size = size/2; + mddev->resync_max_sectors = size; + + /* Calculate max read-ahead size. + * We need to readahead at least twice a whole stripe.... + * maybe... + */ + { + int stripe = conf->raid_disks * mddev->chunk_size / PAGE_CACHE_SIZE; + stripe /= conf->near_copies; + if (mddev->queue->backing_dev_info.ra_pages < 2* stripe) + mddev->queue->backing_dev_info.ra_pages = 2* stripe; + } + + if (conf->near_copies < mddev->raid_disks) + blk_queue_merge_bvec(mddev->queue, raid10_mergeable_bvec); + return 0; + +out_free_conf: + if (conf->r10bio_pool) + mempool_destroy(conf->r10bio_pool); + if (conf->mirrors) + kfree(conf->mirrors); + kfree(conf); + mddev->private = NULL; +out: + return -EIO; +} + +static int stop(mddev_t *mddev) +{ + conf_t *conf = mddev_to_conf(mddev); + + md_unregister_thread(mddev->thread); + mddev->thread = NULL; + if (conf->r10bio_pool) + mempool_destroy(conf->r10bio_pool); + if (conf->mirrors) + kfree(conf->mirrors); + kfree(conf); + mddev->private = NULL; + return 0; +} + + +static mdk_personality_t raid10_personality = +{ + .name = "raid10", + .owner = THIS_MODULE, + .make_request = make_request, + .run = run, + .stop = stop, + .status = status, + .error_handler = error, + .hot_add_disk = raid10_add_disk, + .hot_remove_disk= raid10_remove_disk, + .spare_active = raid10_spare_active, + .sync_request = sync_request, +}; + +static int __init raid_init(void) +{ + return register_md_personality(RAID10, &raid10_personality); +} + +static void raid_exit(void) +{ + unregister_md_personality(RAID10); +} + +module_init(raid_init); +module_exit(raid_exit); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("md-personality-9"); /* RAID10 */ --- linux-2.6.9-rc1/drivers/md/raid1.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/drivers/md/raid1.c 2004-09-02 20:51:51.000000000 -0700 @@ -24,10 +24,6 @@ #include -#define MAJOR_NR MD_MAJOR -#define MD_DRIVER -#define MD_PERSONALITY - /* * Number of guaranteed r1bios in case of extreme VM load: */ @@ -44,13 +40,12 @@ static void * r1bio_pool_alloc(int gfp_f { struct pool_info *pi = data; r1bio_t *r1_bio; + int size = offsetof(r1bio_t, bios[pi->raid_disks]); /* allocate a r1bio with room for raid_disks entries in the bios array */ - r1_bio = kmalloc(sizeof(r1bio_t) + sizeof(struct bio*)*pi->raid_disks, - gfp_flags); + r1_bio = kmalloc(size, gfp_flags); if (r1_bio) - memset(r1_bio, 0, sizeof(*r1_bio) + - sizeof(struct bio*) * pi->raid_disks); + memset(r1_bio, 0, size); else unplug_slaves(pi->mddev); @@ -104,7 +99,7 @@ static void * r1buf_pool_alloc(int gfp_f bio->bi_io_vec[i].bv_page = page; } - r1_bio->master_bio = bio; + r1_bio->master_bio = NULL; return r1_bio; @@ -189,32 +184,6 @@ static inline void put_buf(r1bio_t *r1_b spin_unlock_irqrestore(&conf->resync_lock, flags); } -static int map(mddev_t *mddev, mdk_rdev_t **rdevp) -{ - conf_t *conf = mddev_to_conf(mddev); - int i, disks = conf->raid_disks; - - /* - * Later we do read balancing on the read side - * now we use the first available disk. - */ - - spin_lock_irq(&conf->device_lock); - for (i = 0; i < disks; i++) { - mdk_rdev_t *rdev = conf->mirrors[i].rdev; - if (rdev && rdev->in_sync) { - *rdevp = rdev; - atomic_inc(&rdev->nr_pending); - spin_unlock_irq(&conf->device_lock); - return i; - } - } - spin_unlock_irq(&conf->device_lock); - - printk(KERN_ERR "raid1_map(): huh, no more operational devices?\n"); - return -1; -} - static void reschedule_retry(r1bio_t *r1_bio) { unsigned long flags; @@ -292,8 +261,9 @@ static int raid1_end_read_request(struct * oops, read error: */ char b[BDEVNAME_SIZE]; - printk(KERN_ERR "raid1: %s: rescheduling sector %llu\n", - bdevname(conf->mirrors[mirror].rdev->bdev,b), (unsigned long long)r1_bio->sector); + if (printk_ratelimit()) + printk(KERN_ERR "raid1: %s: rescheduling sector %llu\n", + bdevname(conf->mirrors[mirror].rdev->bdev,b), (unsigned long long)r1_bio->sector); reschedule_retry(r1_bio); } @@ -363,11 +333,11 @@ static int raid1_end_write_request(struc * * The rdev for the device selected will have nr_pending incremented. */ -static int read_balance(conf_t *conf, struct bio *bio, r1bio_t *r1_bio) +static int read_balance(conf_t *conf, r1bio_t *r1_bio) { const unsigned long this_sector = r1_bio->sector; int new_disk = conf->last_used, disk = new_disk; - const int sectors = bio->bi_size >> 9; + const int sectors = r1_bio->sectors; sector_t new_distance, current_distance; spin_lock_irq(&conf->device_lock); @@ -378,14 +348,14 @@ static int read_balance(conf_t *conf, st */ if (conf->mddev->recovery_cp < MaxSector && (this_sector + sectors >= conf->next_resync)) { - /* make sure that disk is operational */ + /* Choose the first operation device, for consistancy */ new_disk = 0; while (!conf->mirrors[new_disk].rdev || !conf->mirrors[new_disk].rdev->in_sync) { new_disk++; if (new_disk == conf->raid_disks) { - new_disk = 0; + new_disk = -1; break; } } @@ -400,7 +370,7 @@ static int read_balance(conf_t *conf, st new_disk = conf->raid_disks; new_disk--; if (new_disk == disk) { - new_disk = conf->last_used; + new_disk = -1; goto rb_out; } } @@ -440,13 +410,13 @@ static int read_balance(conf_t *conf, st } while (disk != conf->last_used); rb_out: - r1_bio->read_disk = new_disk; - conf->next_seq_sect = this_sector + sectors; - conf->last_used = new_disk; - if (conf->mirrors[new_disk].rdev) + if (new_disk >= 0) { + conf->next_seq_sect = this_sector + sectors; + conf->last_used = new_disk; atomic_inc(&conf->mirrors[new_disk].rdev->nr_pending); + } spin_unlock_irq(&conf->device_lock); return new_disk; @@ -571,15 +541,26 @@ static int make_request(request_queue_t r1_bio->mddev = mddev; r1_bio->sector = bio->bi_sector; + r1_bio->state = 0; + if (bio_data_dir(bio) == READ) { /* * read balancing logic: */ - mirror = conf->mirrors + read_balance(conf, bio, r1_bio); + int rdisk = read_balance(conf, r1_bio); + + if (rdisk < 0) { + /* couldn't find anywhere to read from */ + raid_end_bio_io(r1_bio); + return 0; + } + mirror = conf->mirrors + rdisk; + + r1_bio->read_disk = rdisk; read_bio = bio_clone(bio, GFP_NOIO); - r1_bio->bios[r1_bio->read_disk] = read_bio; + r1_bio->bios[rdisk] = read_bio; read_bio->bi_sector = r1_bio->sector + mirror->rdev->data_offset; read_bio->bi_bdev = mirror->rdev->bdev; @@ -903,7 +884,7 @@ static void sync_request_write(mddev_t * atomic_inc(&conf->mirrors[i].rdev->nr_pending); atomic_inc(&r1_bio->remaining); - md_sync_acct(conf->mirrors[i].rdev, wbio->bi_size >> 9); + md_sync_acct(conf->mirrors[i].rdev->bdev, wbio->bi_size >> 9); generic_make_request(wbio); } @@ -951,7 +932,7 @@ static void raid1d(mddev_t *mddev) } else { int disk; bio = r1_bio->bios[r1_bio->read_disk]; - if ((disk=map(mddev, &rdev)) == -1) { + if ((disk=read_balance(conf, r1_bio)) == -1) { printk(KERN_ALERT "raid1: %s: unrecoverable I/O" " read error for block %llu\n", bdevname(bio->bi_bdev,b), @@ -961,10 +942,12 @@ static void raid1d(mddev_t *mddev) r1_bio->bios[r1_bio->read_disk] = NULL; r1_bio->read_disk = disk; r1_bio->bios[r1_bio->read_disk] = bio; - printk(KERN_ERR "raid1: %s: redirecting sector %llu to" - " another mirror\n", - bdevname(rdev->bdev,b), - (unsigned long long)r1_bio->sector); + rdev = conf->mirrors[disk].rdev; + if (printk_ratelimit()) + printk(KERN_ERR "raid1: %s: redirecting sector %llu to" + " another mirror\n", + bdevname(rdev->bdev,b), + (unsigned long long)r1_bio->sector); bio->bi_bdev = rdev->bdev; bio->bi_sector = r1_bio->sector + rdev->data_offset; bio->bi_rw = READ; @@ -1143,7 +1126,7 @@ static int sync_request(mddev_t *mddev, bio = r1_bio->bios[disk]; r1_bio->sectors = nr_sectors; - md_sync_acct(mirror->rdev, nr_sectors); + md_sync_acct(mirror->rdev->bdev, nr_sectors); generic_make_request(bio); --- linux-2.6.9-rc1/drivers/md/raid5.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/md/raid5.c 2004-09-03 00:57:25.946094856 -0700 @@ -477,8 +477,8 @@ static void error(mddev_t *mddev, mdk_rd if (!rdev->faulty) { mddev->sb_dirty = 1; - conf->working_disks--; if (rdev->in_sync) { + conf->working_disks--; mddev->degraded++; conf->failed_disks++; rdev->in_sync = 0; @@ -1071,7 +1071,8 @@ static void handle_stripe(struct stripe_ PRINTK("Reading block %d (sync=%d)\n", i, syncing); if (syncing) - md_sync_acct(conf->disks[i].rdev, STRIPE_SECTORS); + md_sync_acct(conf->disks[i].rdev->bdev, + STRIPE_SECTORS); } } } @@ -1256,7 +1257,7 @@ static void handle_stripe(struct stripe_ if (rdev) { if (test_bit(R5_Syncio, &sh->dev[i].flags)) - md_sync_acct(rdev, STRIPE_SECTORS); + md_sync_acct(rdev->bdev, STRIPE_SECTORS); bi->bi_bdev = rdev->bdev; PRINTK("for %llu schedule op %ld on disc %d\n", --- linux-2.6.9-rc1/drivers/md/raid6main.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/drivers/md/raid6main.c 2004-09-03 00:57:25.948094552 -0700 @@ -498,8 +498,8 @@ static void error(mddev_t *mddev, mdk_rd if (!rdev->faulty) { mddev->sb_dirty = 1; - conf->working_disks--; if (rdev->in_sync) { + conf->working_disks--; mddev->degraded++; conf->failed_disks++; rdev->in_sync = 0; @@ -1208,7 +1208,8 @@ static void handle_stripe(struct stripe_ PRINTK("Reading block %d (sync=%d)\n", i, syncing); if (syncing) - md_sync_acct(conf->disks[i].rdev, STRIPE_SECTORS); + md_sync_acct(conf->disks[i].rdev->bdev, + STRIPE_SECTORS); } } } @@ -1418,7 +1419,7 @@ static void handle_stripe(struct stripe_ if (rdev) { if (test_bit(R5_Syncio, &sh->dev[i].flags)) - md_sync_acct(rdev, STRIPE_SECTORS); + md_sync_acct(rdev->bdev, STRIPE_SECTORS); bi->bi_bdev = rdev->bdev; PRINTK("for %llu schedule op %ld on disc %d\n", --- linux-2.6.9-rc1/drivers/media/common/saa7146_i2c.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/drivers/media/common/saa7146_i2c.c 2004-09-03 00:57:24.259351280 -0700 @@ -1,13 +1,6 @@ #include #include -/* helper function */ -static void my_wait(struct saa7146_dev *dev, long ms) -{ - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout((((ms+10)/10)*HZ)/1000); -} - u32 saa7146_i2c_func(struct i2c_adapter *adapter) { //fm DEB_I2C(("'%s'.\n", adapter->name)); @@ -136,12 +129,12 @@ static int saa7146_i2c_reset(struct saa7 /* set "ABORT-OPERATION"-bit (bit 7)*/ saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07)); saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - my_wait(dev,SAA7146_I2C_DELAY); + msleep(SAA7146_I2C_DELAY); /* clear all error-bits pending; this is needed because p.123, note 1 */ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - my_wait(dev,SAA7146_I2C_DELAY); + msleep(SAA7146_I2C_DELAY); } /* check if any error is (still) present. (this can be necessary because p.123, note 1) */ @@ -155,18 +148,18 @@ static int saa7146_i2c_reset(struct saa7 after serious protocol errors caused by e.g. the SAA7740 */ saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07)); saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - my_wait(dev,SAA7146_I2C_DELAY); + msleep(SAA7146_I2C_DELAY); /* clear all error-bits pending */ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - my_wait(dev,SAA7146_I2C_DELAY); + msleep(SAA7146_I2C_DELAY); /* the data sheet says it might be necessary to clear the status twice after an abort */ saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - my_wait(dev,SAA7146_I2C_DELAY); + msleep(SAA7146_I2C_DELAY); } /* if any error is still present, a fatal error has occured ... */ @@ -243,7 +236,7 @@ static int saa7146_i2c_writeout(struct s if ((++trial < 20) && short_delay) udelay(10); else - my_wait(dev,1); + msleep(1); } } @@ -345,7 +338,7 @@ int saa7146_i2c_transfer(struct saa7146_ } /* delay a bit before retrying */ - my_wait(dev, 10); + msleep(10); } while (err != num && retries--); --- linux-2.6.9-rc1/drivers/media/dvb/bt8xx/bt878.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/media/dvb/bt8xx/bt878.c 2004-09-02 20:51:51.000000000 -0700 @@ -417,6 +417,8 @@ static int __devinit bt878_probe(struct printk(KERN_INFO "bt878: Bt878 AUDIO function found (%d).\n", bt878_num); + if (pci_enable_device(dev)) + return -EIO; bt = &bt878[bt878_num]; bt->dev = dev; @@ -426,11 +428,10 @@ static int __devinit bt878_probe(struct bt->id = dev->device; bt->irq = dev->irq; bt->bt878_adr = pci_resource_start(dev, 0); - if (pci_enable_device(dev)) - return -EIO; if (!request_mem_region(pci_resource_start(dev, 0), pci_resource_len(dev, 0), "bt878")) { - return -EBUSY; + result = -EBUSY; + goto fail0; } pci_read_config_byte(dev, PCI_CLASS_REVISION, &bt->revision); @@ -501,6 +502,8 @@ static int __devinit bt878_probe(struct fail1: release_mem_region(pci_resource_start(bt->dev, 0), pci_resource_len(bt->dev, 0)); + fail0: + pci_disable_device(dev); return result; } @@ -517,7 +520,7 @@ static void __devexit bt878_remove(struc /* first disable interrupts before unmapping the memory! */ btwrite(0, BT878_AINT_MASK); - btwrite(~0x0UL, BT878_AINT_STAT); + btwrite(~0U, BT878_AINT_STAT); /* disable PCI bus-mastering */ pci_read_config_byte(bt->dev, PCI_COMMAND, &command); @@ -540,6 +543,7 @@ static void __devexit bt878_remove(struc bt878_mem_free(bt); pci_set_drvdata(pci_dev, NULL); + pci_disable_device(pci_dev); return; } --- linux-2.6.9-rc1/drivers/media/dvb/dvb-core/dvb_ca_en50221.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/drivers/media/dvb/dvb-core/dvb_ca_en50221.c 2004-09-02 20:51:51.000000000 -0700 @@ -1196,7 +1196,7 @@ static ssize_t dvb_ca_en50221_io_write(s int status; char fragbuf[HOST_LINK_BUF_SIZE]; int fragpos = 0; - int fraglen; + size_t fraglen; unsigned long timeout; int written; @@ -1257,7 +1257,7 @@ static int dvb_ca_en50221_io_read_condit int slot; int slot_count = 0; int idx; - int fraglen; + size_t fraglen; int connection_id = -1; int found = 0; u8 hdr[2]; --- linux-2.6.9-rc1/drivers/media/dvb/dvb-core/dvb_net.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/media/dvb/dvb-core/dvb_net.c 2004-09-03 00:56:32.522216520 -0700 @@ -563,7 +563,7 @@ static int dvb_net_ts_callback(const u8 if (buffer2 != 0) printk(KERN_WARNING "buffer2 not 0: %p.\n", buffer2); if (buffer1_len > 32768) - printk(KERN_WARNING "length > 32k: %u.\n", buffer1_len); + printk(KERN_WARNING "length > 32k: %zu.\n", buffer1_len); /* printk("TS callback: %u bytes, %u TS cells @ %p.\n", buffer1_len, buffer1_len / TS_SZ, buffer1); */ dvb_net_ule(dev, buffer1, buffer1_len); @@ -919,14 +919,6 @@ static void dvb_net_set_multicast_list ( } -static int dvb_net_set_config(struct net_device *dev, struct ifmap *map) -{ - if (netif_running(dev)) - return -EBUSY; - return 0; -} - - static void wq_restart_net_feed (void *data) { struct net_device *dev = data; @@ -985,7 +977,6 @@ static void dvb_net_setup(struct net_dev dev->hard_start_xmit = dvb_net_tx; dev->get_stats = dvb_net_get_stats; dev->set_multicast_list = dvb_net_set_multicast_list; - dev->set_config = dvb_net_set_config; dev->set_mac_address = dvb_net_set_mac; dev->mtu = 4096; dev->mc_count = 0; --- linux-2.6.9-rc1/drivers/media/dvb/frontends/dst.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/media/dvb/frontends/dst.c 2004-09-02 20:51:51.000000000 -0700 @@ -1145,8 +1145,8 @@ static int dst_attach (struct dvb_i2c_bu } dst_init (dst); - dprintk("%s: register dst %8.8x bt %8.8x i2c %8.8x\n", __FUNCTION__, - (u32)dst, (u32)(dst->bt), (u32)(dst->i2c)); + dprintk("%s: register dst %p bt %p i2c %p\n", __FUNCTION__, + dst, dst->bt, dst->i2c); info = &dst_info_sat; if (dst->dst_type == DST_TYPE_IS_TERR) @@ -1162,7 +1162,7 @@ static int dst_attach (struct dvb_i2c_bu static void dst_detach (struct dvb_i2c_bus *i2c, void *data) { dvb_unregister_frontend (dst_ioctl, i2c); - dprintk("%s: unregister dst %8.8x\n", __FUNCTION__, (u32)(data)); + dprintk("%s: unregister dst %p\n", __FUNCTION__, data); if (data) kfree(data); } --- linux-2.6.9-rc1/drivers/media/dvb/frontends/stv0299.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/media/dvb/frontends/stv0299.c 2004-09-02 20:51:51.000000000 -0700 @@ -1271,7 +1271,7 @@ static long probe_tuner (struct dvb_i2c_ printk ("%s: try to attach to %s\n", __FUNCTION__, adapter->name); - if ( strcmp(adapter->name, "Technisat SkyStar2 driver") == 0 ) + if ( strcmp(adapter->name, "SkyStar2") == 0 ) { printk ("%s: setup for tuner Samsung TBMU24112IMB\n", __FILE__); --- linux-2.6.9-rc1/drivers/media/dvb/ttpci/Kconfig 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/media/dvb/ttpci/Kconfig 2004-09-02 20:51:51.000000000 -0700 @@ -1,6 +1,6 @@ config DVB_AV7110 tristate "AV7110 cards" - depends on DVB_CORE + depends on DVB_CORE && PCI select FW_LOADER select VIDEO_DEV select VIDEO_SAA7146_VV @@ -45,7 +45,7 @@ config DVB_AV7110_OSD config DVB_BUDGET tristate "Budget cards" - depends on DVB_CORE + depends on DVB_CORE && PCI select VIDEO_SAA7146 help Support for simple SAA7146 based DVB cards @@ -59,7 +59,7 @@ config DVB_BUDGET config DVB_BUDGET_CI tristate "Budget cards with onboard CI connector" - depends on DVB_CORE + depends on DVB_CORE && PCI select VIDEO_SAA7146 help Support for simple SAA7146 based DVB cards @@ -76,7 +76,7 @@ config DVB_BUDGET_CI config DVB_BUDGET_AV tristate "Budget cards with analog video inputs" - depends on DVB_CORE + depends on DVB_CORE && PCI select VIDEO_DEV select VIDEO_SAA7146_VV help --- linux-2.6.9-rc1/drivers/media/dvb/ttusb-dec/ttusb_dec.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/drivers/media/dvb/ttusb-dec/ttusb_dec.c 2004-09-02 20:51:51.000000000 -0700 @@ -1181,7 +1181,7 @@ static int ttusb_dec_boot_dsp(struct ttu firmware_size = fw_entry->size; if (firmware_size < 60) { - printk("%s: firmware size too small for DSP code (%u < 60).\n", + printk("%s: firmware size too small for DSP code (%zu < 60).\n", __FUNCTION__, firmware_size); return -1; } --- linux-2.6.9-rc1/drivers/media/radio/miropcm20-rds.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/media/radio/miropcm20-rds.c 2004-09-03 00:57:24.012388824 -0700 @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "miropcm20-rds-core.h" @@ -60,8 +61,7 @@ static ssize_t rds_f_read(struct file *f char c; char bits[8]; - current->state=TASK_UNINTERRUPTIBLE; - schedule_timeout(2*HZ); + msleep(2000); aci_rds_cmd(RDS_STATUS, &c, 1); print_matrix(&c, bits); if (copy_to_user(buffer, bits, 8)) --- linux-2.6.9-rc1/drivers/media/radio/radio-aimslab.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/drivers/media/radio/radio-aimslab.c 2004-09-03 00:57:23.889407520 -0700 @@ -63,12 +63,7 @@ static void sleep_delay(long n) if(!d) udelay(n); else - { - /* Yield CPU time */ - unsigned long x=jiffies; - while((jiffies-x)<=d) - schedule(); - } + msleep(jiffies_to_msecs(d)); } static void rt_decvol(void) --- linux-2.6.9-rc1/drivers/media/radio/radio-cadet.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/drivers/media/radio/radio-cadet.c 2004-09-03 00:57:23.764426520 -0700 @@ -69,8 +69,7 @@ static int cadet_getrds(void) outb(inb(io+1)&0x7f,io+1); /* Reset RDS detection */ spin_unlock(&cadet_io_lock); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ/10); + msleep(100); spin_lock(&cadet_io_lock); outb(3,io); /* Select Decoder Control/Status */ @@ -243,8 +242,7 @@ static void cadet_setfreq(unsigned freq) outb(curvol,io+1); spin_unlock(&cadet_io_lock); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ/10); + msleep(100); cadet_gettune(); if((tunestat & 0x40) == 0) { /* Tuned */ --- linux-2.6.9-rc1/drivers/media/radio/radio-maestro.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/media/radio/radio-maestro.c 2004-09-03 00:57:23.641445216 -0700 @@ -93,27 +93,6 @@ static struct radio_device struct semaphore lock; } radio_unit = {0, 0, 0, 0, }; -static void sleep_125ms(void) -{ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(HZ >> 3); -} - -static void udelay2(void) -{ - udelay(2); -} - -static void udelay4(void) -{ - udelay(4); -} - -static void udelay16(void) -{ - udelay(16); -} - static __u32 radio_bits_get(struct radio_device *dev) { register __u16 io=dev->io, l, rdata; @@ -122,14 +101,15 @@ static __u32 radio_bits_get(struct radio omask = inw(io + IO_MASK); outw(~(STR_CLK | STR_WREN), io + IO_MASK); outw(0, io); - udelay16(); + udelay(16); + for (l=24;l--;) { outw(STR_CLK, io); /* HI state */ - udelay2(); + udelay(2); if(!l) dev->tuned = inw(io) & STR_MOST ? 0 : 0xffff; outw(0, io); /* LO state */ - udelay2(); + udelay(2); data <<= 1; /* shift data */ rdata = inw(io); if(!l) @@ -138,11 +118,11 @@ static __u32 radio_bits_get(struct radio else if(rdata & STR_DATA) data++; - udelay2(); + udelay(2); } if(dev->muted) outw(STR_WREN, io); - udelay4(); + udelay(4); outw(omask, io + IO_MASK); return data & 0x3ffe; } @@ -155,23 +135,23 @@ static void radio_bits_set(struct radio_ odir = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN); outw(odir | STR_DATA, io + IO_DIR); outw(~(STR_DATA | STR_CLK | STR_WREN), io + IO_MASK); - udelay16(); + udelay(16); for (l=25;l;l--) { bits = ((data >> 18) & STR_DATA) | STR_WREN ; data <<= 1; /* shift data */ outw(bits, io); /* start strobe */ - udelay2(); + udelay(2); outw(bits | STR_CLK, io); /* HI level */ - udelay2(); + udelay(2); outw(bits, io); /* LO level */ - udelay4(); + udelay(4); } if(!dev->muted) outw(0, io); - udelay4(); + udelay(4); outw(omask, io + IO_MASK); outw(odir, io + IO_DIR); - sleep_125ms(); + msleep(125); } inline static int radio_function(struct inode *inode, struct file *file, @@ -238,9 +218,9 @@ inline static int radio_function(struct outw(~STR_WREN, io + IO_MASK); outw((card->muted = v->flags & VIDEO_AUDIO_MUTE) ? STR_WREN : 0, io); - udelay4(); + udelay(4); outw(omask, io + IO_MASK); - sleep_125ms(); + msleep(125); return 0; } } @@ -315,7 +295,7 @@ inline static __u16 radio_power_on(struc outw(odir, io + IO_DIR); outw(~(STR_WREN | STR_CLK), io + IO_MASK); outw(dev->muted ? 0 : STR_WREN, io); - udelay16(); + udelay(16); outw(omask, io + IO_MASK); ofreq = radio_bits_get(dev); if((ofreqFREQ2BITS(FREQ_HI))) --- linux-2.6.9-rc1/drivers/media/radio/radio-maxiradio.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/drivers/media/radio/radio-maxiradio.c 2004-09-03 00:57:24.137369824 -0700 @@ -104,13 +104,6 @@ static struct radio_device } radio_unit = {0, 0, 0, 0, }; -static void sleep_125ms(void) -{ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(HZ >> 3); -} - - static void outbit(unsigned long bit, __u16 io) { if(bit != 0) @@ -228,7 +221,7 @@ inline static int radio_function(struct return -EINVAL; card->freq = *freq; set_freq(card->io, FREQ2BITS(card->freq)); - sleep_125ms(); + msleep(125); return 0; } case VIDIOCGAUDIO: { --- linux-2.6.9-rc1/drivers/media/radio/radio-sf16fmi.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/drivers/media/radio/radio-sf16fmi.c 2004-09-03 00:57:24.382332584 -0700 @@ -89,8 +89,7 @@ static inline int fmi_setfreq(struct fmi outbits(16, RSF16_ENCODE(freq), myport); outbits(8, 0xC0, myport); - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(HZ/7); + msleep(143); /* was schedule_timeout(HZ/7) */ up(&lock); if (dev->curvol) fmi_unmute(myport); return 0; @@ -107,8 +106,7 @@ static inline int fmi_getsigstr(struct f val = dev->curvol ? 0x08 : 0x00; /* unmute/mute */ outb(val, myport); outb(val | 0x10, myport); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ/7); + msleep(143); /* was schedule_timeout(HZ/7) */ res = (int)inb(myport+1); outb(val, myport); --- linux-2.6.9-rc1/drivers/media/radio/radio-sf16fmr2.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/drivers/media/radio/radio-sf16fmr2.c 2004-09-03 00:57:24.507313584 -0700 @@ -55,19 +55,6 @@ static int radio_nr = -1; #define RSF16_MINFREQ 87*16000 #define RSF16_MAXFREQ 108*16000 -/* from radio-aimslab */ -static void sleep_delay(unsigned long n) -{ - unsigned d=n/(1000000U/HZ); - if (!d) - udelay(n); - else - { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(d); - } -} - static inline void wait(int n,int port) { for (;n;--n) inb(port); @@ -153,7 +140,7 @@ static int fmr2_setfreq(struct fmr2_devi fmr2_unmute(port); /* wait 0.11 sec */ - sleep_delay(110000LU); + msleep(110); /* NOTE if mute this stop radio you must set freq on unmute */ --- linux-2.6.9-rc1/drivers/media/video/adv7175.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/drivers/media/video/adv7175.c 2004-09-02 20:51:51.000000000 -0700 @@ -156,6 +156,22 @@ adv7175_write_block (struct i2c_client * return ret; } +static void +set_subcarrier_freq (struct i2c_client *client, + int pass_through) +{ + /* for some reason pass_through NTSC needs + * a different sub-carrier freq to remain stable. */ + if(pass_through) + adv7175_write(client, 0x02, 0x00); + else + adv7175_write(client, 0x02, 0x55); + + adv7175_write(client, 0x03, 0x55); + adv7175_write(client, 0x04, 0x55); + adv7175_write(client, 0x05, 0x25); +} + #ifdef ENCODER_DUMP static void dump (struct i2c_client *client) @@ -322,6 +338,10 @@ adv7175_command (struct i2c_client *clie case 0: adv7175_write(client, 0x01, 0x00); + + if (encoder->norm == VIDEO_MODE_NTSC) + set_subcarrier_freq(client, 1); + adv7175_write(client, 0x0c, TR1CAPT); /* TR1 */ if (encoder->norm == VIDEO_MODE_SECAM) adv7175_write(client, 0x0d, 0x49); // Disable genlock @@ -334,6 +354,10 @@ adv7175_command (struct i2c_client *clie case 1: adv7175_write(client, 0x01, 0x00); + + if (encoder->norm == VIDEO_MODE_NTSC) + set_subcarrier_freq(client, 0); + adv7175_write(client, 0x0c, TR1PLAY); /* TR1 */ adv7175_write(client, 0x0d, 0x49); adv7175_write(client, 0x07, TR0MODE | TR0RST); @@ -343,6 +367,10 @@ adv7175_command (struct i2c_client *clie case 2: adv7175_write(client, 0x01, 0x80); + + if (encoder->norm == VIDEO_MODE_NTSC) + set_subcarrier_freq(client, 0); + adv7175_write(client, 0x0d, 0x49); adv7175_write(client, 0x07, TR0MODE | TR0RST); adv7175_write(client, 0x07, TR0MODE); --- linux-2.6.9-rc1/drivers/media/video/bttv-cards.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/drivers/media/video/bttv-cards.c 2004-09-03 00:57:13.942919616 -0700 @@ -31,7 +31,7 @@ #include #include #include -#ifdef CONFIG_FW_LOADER +#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE) # include #endif @@ -74,6 +74,9 @@ static void PXC200_muxsel(struct bttv *b static void picolo_tetra_muxsel(struct bttv *btv, unsigned int input); static void picolo_tetra_init(struct bttv *btv); +static void sigmaSLC_muxsel(struct bttv *btv, unsigned int input); +static void sigmaSQ_muxsel(struct bttv *btv, unsigned int input); + static int terratec_active_radio_upgrade(struct bttv *btv); static int tea5757_read(struct bttv *btv); static int tea5757_write(struct bttv *btv, int value); @@ -170,6 +173,7 @@ static struct CARD { { 0x6606107d, BTTV_WINFAST2000, "Leadtek WinFast TV 2000" }, { 0x6607107d, BTTV_WINFASTVC100, "Leadtek WinFast VC 100" }, + { 0x6609107d, BTTV_WINFAST2000, "Leadtek TV 2000 XP" }, { 0x263610b4, BTTV_STB2, "STB TV PCI FM, Gateway P/N 6000704" }, { 0x264510b4, BTTV_STB2, "STB TV PCI FM, Gateway P/N 6000704" }, { 0x402010fc, BTTV_GVBCTV3PCI, "I-O Data Co. GV-BCTV3/PCI" }, @@ -224,6 +228,7 @@ static struct CARD { { 0x1431aa00, BTTV_PV143, "Provideo PV143B" }, { 0x1432aa00, BTTV_PV143, "Provideo PV143C" }, { 0x1433aa00, BTTV_PV143, "Provideo PV143D" }, + { 0x1433aa03, BTTV_PV143, "Security Eyes" }, { 0x1460aa00, BTTV_PV150, "Provideo PV150A-1" }, { 0x1461aa01, BTTV_PV150, "Provideo PV150A-2" }, @@ -265,6 +270,7 @@ static struct CARD { { 0x01020304, BTTV_XGUARD, "Grandtec Grand X-Guard" }, { 0x18501851, BTTV_CHRONOS_VS2, "FlyVideo 98 (LR50)/ Chronos Video Shuttle II" }, + { 0xa0501851, BTTV_CHRONOS_VS2, "FlyVideo 98 (LR50)/ Chronos Video Shuttle II" }, { 0x18511851, BTTV_FLYVIDEO98EZ, "FlyVideo 98EZ (LR51)/ CyberMail AV" }, { 0x18521852, BTTV_TYPHOON_TVIEW, "FlyVideo 98FM (LR50)/ Typhoon TView TV/FM Tuner" }, { 0x41a0a051, BTTV_FLYVIDEO_98FM, "Lifeview FlyVideo 98 LR50 Rev Q" }, @@ -297,7 +303,7 @@ static struct CARD { // DVB cards (using pci function .1 for mpeg data xfer) { 0x01010071, BTTV_NEBULA_DIGITV, "Nebula Electronics DigiTV" }, - { 0x07611461, BTTV_NEBULA_DIGITV, "AverMedia AverTV DVB-T" }, + { 0x07611461, BTTV_AVDVBT_761, "AverMedia AverTV DVB-T" }, { 0x002611bd, BTTV_TWINHAN_DST, "Pinnacle PCTV SAT CI" }, { 0x00011822, BTTV_TWINHAN_DST, "Twinhan VisionPlus DVB-T" }, { 0xfc00270f, BTTV_TWINHAN_DST, "ChainTech digitop DST-1000 DVB-S" }, @@ -2078,6 +2084,69 @@ struct tvcard bttv_tvcards[] = { #if 0 /* untested */ .has_remote = 1, #endif +},{ + /* ---- card 0x7c ---------------------------------- */ + /* Matt Jesson */ + /* Based on the Nebula card data - added remote and new card number - BTTV_AVDVBT_761, see also ir-kbd-gpio.c */ + .name = "AverMedia AverTV DVB-T 761", + .video_inputs = 1, + .tuner = -1, + .svhs = -1, + .muxsel = { 2, 3, 1, 0}, + .no_msp34xx = 1, + .no_tda9875 = 1, + .no_tda7432 = 1, + .pll = PLL_28, + .tuner_type = -1, + .has_dvb = 1, + .no_gpioirq = 1, + .has_remote = 1, +},{ + /* andre.schwarz@matrix-vision.de */ + .name = "MATRIX Vision Sigma-SQ", + .video_inputs = 16, + .audio_inputs = 0, + .tuner = -1, + .svhs = -1, + .gpiomask = 0x0, + .muxsel = { 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3 }, + .muxsel_hook = sigmaSQ_muxsel, + .audiomux = { 0 }, + .no_msp34xx = 1, + .pll = PLL_28, + .tuner_type = -1, +},{ + /* andre.schwarz@matrix-vision.de */ + .name = "MATRIX Vision Sigma-SLC", + .video_inputs = 4, + .audio_inputs = 0, + .tuner = -1, + .svhs = -1, + .gpiomask = 0x0, + .muxsel = { 2, 2, 2, 2 }, + .muxsel_hook = sigmaSLC_muxsel, + .audiomux = { 0 }, + .no_msp34xx = 1, + .pll = PLL_28, + .tuner_type = -1, +},{ + /* BTTV_APAC_VIEWCOMP */ + /* Attila Kondoros */ + /* bt878 TV + FM 0x00000000 subsystem ID */ + .name = "APAC Viewcomp 878(AMAX)", + .video_inputs = 2, + .audio_inputs = 1, + .tuner = 0, + .svhs = -1, + .gpiomask = 0xFF, + .muxsel = { 2, 3, 1, 1}, + .audiomux = { 2, 0, 0, 0, 10}, + .needs_tvaudio = 0, + .pll = PLL_28, + .tuner_type = TUNER_PHILIPS_PAL, + .has_remote = 1, /* miniremote works, see ir-kbd-gpio.c */ + .has_radio = 1, /* not every card has radio */ }}; const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards); @@ -2405,6 +2474,19 @@ static void init_lmlbt4x(struct bttv *bt gpio_write(0x000000); } +static void sigmaSQ_muxsel(struct bttv *btv, unsigned int input) +{ + unsigned int inmux = input % 8; + gpio_inout( 0xf, 0xf ); + gpio_bits( 0xf, inmux ); +} + +static void sigmaSLC_muxsel(struct bttv *btv, unsigned int input) +{ + unsigned int inmux = input % 4; + gpio_inout( 3<<9, 3<<9 ); + gpio_bits( 3<<9, inmux<<9 ); +} /* ----------------------------------------------------------------------- */ @@ -2859,7 +2941,7 @@ static int __devinit pvr_altera_load(str return 0; } -#ifndef CONFIG_FW_LOADER +#if !defined(CONFIG_FW_LOADER) && !defined(CONFIG_FW_LOADER_MODULE) /* old 2.4.x way -- via soundcore's mod_firmware_load */ static char *firm_altera = "/usr/lib/video4linux/hcwamc.rbf"; @@ -3963,7 +4045,7 @@ void __devinit bttv_check_chipset(void) #if 0 /* print which chipset we have */ - while ((dev = pci_find_class(PCI_CLASS_BRIDGE_HOST << 8,dev))) + while ((dev = pci_get_class(PCI_CLASS_BRIDGE_HOST << 8,dev))) printk(KERN_INFO "bttv: Host bridge is %s\n",pci_name(dev)); #endif @@ -3982,8 +4064,8 @@ void __devinit bttv_check_chipset(void) if (UNSET != latency) printk(KERN_INFO "bttv: pci latency fixup [%d]\n",latency); - while ((dev = pci_find_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_82441, dev))) { + while ((dev = pci_get_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82441, dev))) { unsigned char b; pci_read_config_byte(dev, 0x53, &b); if (bttv_debug) @@ -4063,7 +4145,7 @@ static void PXC200_muxsel(struct bttv *b return; } - rc=bttv_I2CRead(btv,(PX_I2C_PIC<<1),NULL); + rc=bttv_I2CRead(btv,(PX_I2C_PIC<<1),0); if (!(rc & PX_CFG_PXC200F)) { printk(KERN_DEBUG "bttv%d: PXC200_muxsel: not PXC200F rc:%d \n", btv->c.nr,rc); return; --- linux-2.6.9-rc1/drivers/media/video/bttv-driver.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/media/video/bttv-driver.c 2004-09-03 00:57:13.945919160 -0700 @@ -1861,6 +1861,8 @@ static int setup_window(struct bttv_fh * if (NULL == fh->ovfmt) return -EINVAL; + if (!(fh->ovfmt->flags & FORMAT_FLAGS_PACKED)) + return -EINVAL; retval = verify_window(&bttv_tvnorms[btv->tvnorm],win,fixup); if (0 != retval) return retval; @@ -2052,6 +2054,7 @@ static int bttv_try_fmt(struct bttv_fh * f->fmt.pix.width = maxw; if (f->fmt.pix.height > maxh) f->fmt.pix.height = maxh; + f->fmt.pix.width &= ~0x03; f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = --- linux-2.6.9-rc1/drivers/media/video/bttv-gpio.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/media/video/bttv-gpio.c 2004-09-03 00:57:13.945919160 -0700 @@ -106,6 +106,20 @@ void bttv_gpio_irq(struct bttv_core *cor } } +void bttv_i2c_info(struct bttv_core *core, struct i2c_client *client, int attach) +{ + struct bttv_sub_driver *drv; + struct bttv_sub_device *dev; + struct list_head *item; + + list_for_each(item,&core->subs) { + dev = list_entry(item,struct bttv_sub_device,list); + drv = to_bttv_sub_drv(dev->dev.driver); + if (drv && drv->i2c_info) + drv->i2c_info(dev,client,attach); + } +} + /* ----------------------------------------------------------------------- */ /* external: sub-driver register/unregister */ --- linux-2.6.9-rc1/drivers/media/video/bttv.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/media/video/bttv.h 2004-09-03 00:57:13.946919008 -0700 @@ -126,6 +126,10 @@ #define BTTV_LMLBT4 0x76 #define BTTV_PICOLO_TETRA_CHIP 0x79 #define BTTV_AVDVBT_771 0x7b +#define BTTV_AVDVBT_761 0x7c +#define BTTV_MATRIX_VISIONSQ 0x7d +#define BTTV_MATRIX_VISIONSLC 0x7e +#define BTTV_APAC_VIEWCOMP 0x7f /* i2c address list */ #define I2C_TSA5522 0xc2 @@ -298,6 +302,8 @@ struct bttv_sub_driver { struct device_driver drv; char wanted[BUS_ID_SIZE]; void (*gpio_irq)(struct bttv_sub_device *sub); + void (*i2c_info)(struct bttv_sub_device *sub, + struct i2c_client *client, int attach); }; #define to_bttv_sub_drv(x) container_of((x), struct bttv_sub_driver, drv) --- linux-2.6.9-rc1/drivers/media/video/bttv-i2c.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/drivers/media/video/bttv-i2c.c 2004-09-03 00:57:13.947918856 -0700 @@ -40,6 +40,7 @@ static void bttv_inc_use(struct i2c_adap static void bttv_dec_use(struct i2c_adapter *adap); #endif static int attach_inform(struct i2c_client *client); +static int detach_inform(struct i2c_client *client); static int i2c_debug = 0; static int i2c_hw = 0; @@ -114,6 +115,7 @@ static struct i2c_adapter bttv_i2c_adap_ I2C_DEVNAME("bt848"), .id = I2C_HW_B_BT848, .client_register = attach_inform, + .client_unregister = detach_inform, }; /* ----------------------------------------------------------------------- */ @@ -298,6 +300,7 @@ static struct i2c_adapter bttv_i2c_adap_ .id = I2C_ALGO_BIT | I2C_HW_B_BT848 /* FIXME */, .algo = &bttv_algo, .client_register = attach_inform, + .client_unregister = detach_inform, }; /* ----------------------------------------------------------------------- */ @@ -324,6 +327,7 @@ static int attach_inform(struct i2c_clie if (btv->pinnacle_id != UNSET) bttv_call_i2c_clients(btv,AUDC_CONFIG_PINNACLE, &btv->pinnacle_id); + bttv_i2c_info(&btv->c, client, 1); if (bttv_debug) printk("bttv%d: i2c attach [client=%s]\n", @@ -331,6 +335,14 @@ static int attach_inform(struct i2c_clie return 0; } +static int detach_inform(struct i2c_client *client) +{ + struct bttv *btv = i2c_get_adapdata(client->adapter); + + bttv_i2c_info(&btv->c, client, 0); + return 0; +} + void bttv_call_i2c_clients(struct bttv *btv, unsigned int cmd, void *arg) { if (0 != btv->i2c_rc) --- linux-2.6.9-rc1/drivers/media/video/bttvp.h 2004-08-24 00:02:26.000000000 -0700 +++ 25/drivers/media/video/bttvp.h 2004-09-03 00:57:13.947918856 -0700 @@ -225,6 +225,7 @@ extern struct bus_type bttv_sub_bus_type int bttv_sub_add_device(struct bttv_core *core, char *name); int bttv_sub_del_devices(struct bttv_core *core); void bttv_gpio_irq(struct bttv_core *core); +void bttv_i2c_info(struct bttv_core *core, struct i2c_client *client, int attach); /* ---------------------------------------------------------- */ --- linux-2.6.9-rc1/drivers/media/video/bttv-risc.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/media/video/bttv-risc.c 2004-09-03 00:57:13.948918704 -0700 @@ -55,6 +55,8 @@ bttv_risc_packed(struct bttv *btv, struc instructions += 2; if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions*8)) < 0) return rc; + dprintk("bttv%d: risc packed: bpl %d lines %d instr %d size %d ptr %p\n", + btv->c.nr, bpl, lines, instructions, risc->size, risc->cpu); /* sync instruction */ rp = risc->cpu; @@ -99,8 +101,10 @@ bttv_risc_packed(struct bttv *btv, struc offset += todo; } offset += padding; + dprintk("bttv%d: risc packed: line %d ptr %p\n", + btv->c.nr, line, rp); } - dprintk("bttv%d: risc planar: %d sglist elems\n", btv->c.nr, (int)(sg-sglist)); + dprintk("bttv%d: risc packed: %d sglist elems\n", btv->c.nr, (int)(sg-sglist)); /* save pointer to jmp instruction address */ risc->jmp = rp; @@ -145,11 +149,26 @@ bttv_risc_planar(struct bttv *btv, struc (line >= (ylines - VCR_HACK_LINES))) continue; switch (vshift) { - case 0: chroma = 1; break; - case 1: chroma = !(line & 1); break; - case 2: chroma = !(line & 3); break; - default: chroma = 0; + case 0: + chroma = 1; + break; + case 1: + if (!yoffset) + chroma = (line & 1) == 0; + else + chroma = (line & 1) == 1; + break; + case 2: + if (!yoffset) + chroma = (line & 3) == 0; + else + chroma = (line & 3) == 2; + break; + default: + chroma = 0; + break; } + for (todo = ybpl; todo > 0; todo -= ylen) { /* go to next sg entry if needed */ while (yoffset && yoffset >= sg_dma_len(ysg)) { --- linux-2.6.9-rc1/drivers/media/video/cpia.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/drivers/media/video/cpia.c 2004-09-03 00:57:15.381700888 -0700 @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -62,6 +63,15 @@ MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("video"); #endif +static unsigned short colorspace_conv = 0; +module_param(colorspace_conv, ushort, 0444); +MODULE_PARM_DESC(colorspace_conv, + "\n Colorspace conversion:" + "\n0 = disable" + "\n1 = enable" + "\nDefault value is 0" + "\n"); + #define ABOUT "V4L-Driver for Vision CPiA based cameras" #ifndef VID_HARDWARE_CPIA @@ -1428,14 +1438,19 @@ static void __exit proc_cpia_destroy(voi /* supported frame palettes and depths */ static inline int valid_mode(u16 palette, u16 depth) { - return (palette == VIDEO_PALETTE_GREY && depth == 8) || - (palette == VIDEO_PALETTE_RGB555 && depth == 16) || - (palette == VIDEO_PALETTE_RGB565 && depth == 16) || - (palette == VIDEO_PALETTE_RGB24 && depth == 24) || - (palette == VIDEO_PALETTE_RGB32 && depth == 32) || - (palette == VIDEO_PALETTE_YUV422 && depth == 16) || - (palette == VIDEO_PALETTE_YUYV && depth == 16) || - (palette == VIDEO_PALETTE_UYVY && depth == 16); + if ((palette == VIDEO_PALETTE_YUV422 && depth == 16) || + (palette == VIDEO_PALETTE_YUYV && depth == 16)) + return 1; + + if (colorspace_conv) + return (palette == VIDEO_PALETTE_GREY && depth == 8) || + (palette == VIDEO_PALETTE_RGB555 && depth == 16) || + (palette == VIDEO_PALETTE_RGB565 && depth == 16) || + (palette == VIDEO_PALETTE_RGB24 && depth == 24) || + (palette == VIDEO_PALETTE_RGB32 && depth == 32) || + (palette == VIDEO_PALETTE_UYVY && depth == 16); + + return 0; } static int match_videosize( int width, int height ) @@ -4040,6 +4055,13 @@ static int __init cpia_init(void) { printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT, CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); + + printk(KERN_WARNING "Since in-kernel colorspace conversion is not " + "allowed, it is disabled by default now. Users should fix the " + "applications in case they don't work without conversion " + "reenabled by setting the 'colorspace_conv' module " + "parameter to 1"); + #ifdef CONFIG_PROC_FS proc_cpia_create(); #endif --- linux-2.6.9-rc1/drivers/media/video/Kconfig 2004-08-24 00:03:32.000000000 -0700 +++ 25/drivers/media/video/Kconfig 2004-09-02 20:51:51.000000000 -0700 @@ -155,7 +155,7 @@ config VIDEO_STRADIS config VIDEO_ZORAN tristate "Zoran ZR36057/36067 Video For Linux" - depends on VIDEO_DEV && PCI && I2C + depends on VIDEO_DEV && PCI && I2C_ALGOBIT help Say Y for support for MJPEG capture cards based on the Zoran 36057/36067 PCI controller chipset. This includes the Iomega --- linux-2.6.9-rc1/drivers/media/video/meye.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/media/video/meye.c 2004-09-02 20:51:51.000000000 -0700 @@ -58,7 +58,7 @@ static int video_nr = -1; static inline void meye_initq(struct meye_queue *queue) { queue->head = queue->tail = 0; queue->len = 0; - queue->s_lock = (spinlock_t)SPIN_LOCK_UNLOCKED; + queue->s_lock = SPIN_LOCK_UNLOCKED; init_waitqueue_head(&queue->proc_list); } --- linux-2.6.9-rc1/drivers/media/video/msp3400.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/media/video/msp3400.c 2004-09-03 00:57:13.630967040 -0700 @@ -1552,13 +1552,12 @@ static int msp_command(struct i2c_client /* ----------------------------------------------------------------------- */ -static int msp3400_init_module(void) +static int __init msp3400_init_module(void) { - i2c_add_driver(&driver); - return 0; + return i2c_add_driver(&driver); } -static void msp3400_cleanup_module(void) +static void __exit msp3400_cleanup_module(void) { i2c_del_driver(&driver); } --- linux-2.6.9-rc1/drivers/media/video/saa7134/saa6752hs.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/media/video/saa7134/saa6752hs.c 2004-09-03 00:57:14.122892256 -0700 @@ -387,13 +387,12 @@ static struct i2c_client client_template .driver = &driver, }; -static int saa6752hs_init_module(void) +static int __init saa6752hs_init_module(void) { - i2c_add_driver(&driver); - return 0; + return i2c_add_driver(&driver); } -static void saa6752hs_cleanup_module(void) +static void __exit saa6752hs_cleanup_module(void) { i2c_del_driver(&driver); } --- linux-2.6.9-rc1/drivers/media/video/saa7134/saa7134-cards.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/media/video/saa7134/saa7134-cards.c 2004-09-03 00:57:14.125891800 -0700 @@ -154,6 +154,26 @@ struct saa7134_board saa7134_boards[] = .gpio = 0x8000, }, }, + [SAA7134_BOARD_FLYTVPLATINUM] = { + /* "Arnaud Quette" */ + .name = "LifeView FlyTV Platinum", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_SECAM, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + }, [SAA7134_BOARD_EMPRESS] = { /* "Gert Vervoort" */ .name = "EMPRESS", @@ -177,7 +197,6 @@ struct saa7134_board saa7134_boards[] = .name = name_radio, .amux = LINE2, }, - .i2s_rate = 48000, .has_ts = 1, .video_out = CCIR656, }, @@ -243,7 +262,7 @@ struct saa7134_board saa7134_boards[] = .name = "KNC One TV-Station RDS / Typhoon TV Tuner RDS", .audio_clock = 0x00200000, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, - .need_tda9887 = 1, + .tda9887_conf = TDA9887_PRESENT, .inputs = {{ .name = name_tv, .vmux = 1, @@ -278,7 +297,7 @@ struct saa7134_board saa7134_boards[] = .name = "KNC One TV-Station DVR", .audio_clock = 0x00200000, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, - .need_tda9887 = 1, + .tda9887_conf = TDA9887_PRESENT, .gpiomask = 0x820000, .inputs = {{ .name = name_tv, @@ -302,7 +321,6 @@ struct saa7134_board saa7134_boards[] = .amux = LINE2, .gpio = 0x20000, }, - .i2s_rate = 48000, .has_ts = 1, .video_out = CCIR656, }, @@ -333,7 +351,7 @@ struct saa7134_board saa7134_boards[] = .name = "Medion 5044", .audio_clock = 0x00187de7, // was: 0x00200000, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, - .need_tda9887 = 1, + .tda9887_conf = TDA9887_PRESENT, .inputs = {{ .name = name_tv, .vmux = 1, @@ -414,7 +432,7 @@ struct saa7134_board saa7134_boards[] = //.audio_clock = 0x00200000, .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, - .need_tda9887 = 1, + .tda9887_conf = TDA9887_PRESENT, .inputs = {{ .name = name_tv, .vmux = 1, @@ -440,7 +458,7 @@ struct saa7134_board saa7134_boards[] = .name = "Typhoon TV+Radio 90031", .audio_clock = 0x00200000, .tuner_type = TUNER_PHILIPS_PAL, - .need_tda9887 = 1, + .tda9887_conf = TDA9887_PRESENT, .inputs = {{ .name = name_tv, .vmux = 1, @@ -503,7 +521,7 @@ struct saa7134_board saa7134_boards[] = .name = "ASUS TV-FM 7134", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, - .need_tda9887 = 1, + .tda9887_conf = TDA9887_PRESENT, .inputs = {{ .name = name_tv, .vmux = 1, @@ -607,7 +625,6 @@ struct saa7134_board saa7134_boards[] = .vmux = 8, .amux = LINE1, }}, - .i2s_rate = 48000, .has_ts = 1, .video_out = CCIR656, }, @@ -666,7 +683,7 @@ struct saa7134_board saa7134_boards[] = .name = "AverMedia M156 / Medion 2819", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, - .need_tda9887 = 1, + .tda9887_conf = TDA9887_PRESENT, .inputs = {{ .name = name_tv, .vmux = 1, @@ -709,7 +726,6 @@ struct saa7134_board saa7134_boards[] = .amux = LINE2, .tv = 1, }}, - .i2s_rate = 48000, .has_ts = 1, .video_out = CCIR656, }, @@ -719,7 +735,7 @@ struct saa7134_board saa7134_boards[] = // probably wrong, the 7133 one is the NTSC version ... // .tuner_type = TUNER_PHILIPS_FM1236_MK3 .tuner_type = TUNER_LG_NTSC_NEW_TAPC, - .need_tda9887 = 1, + .tda9887_conf = TDA9887_PRESENT, .inputs = {{ .name = name_tv, .vmux = 1, @@ -743,7 +759,7 @@ struct saa7134_board saa7134_boards[] = .name = "Pinnacle PCTV Stereo (saa7134)", .audio_clock = 0x00187de7, .tuner_type = TUNER_MT2032, - .need_tda9887 = 1, + .tda9887_conf = TDA9887_PRESENT, .inputs = {{ .name = name_tv, .vmux = 3, @@ -957,7 +973,7 @@ struct saa7134_board saa7134_boards[] = .name = "AverMedia 305", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, - .need_tda9887 = 1, + .tda9887_conf = TDA9887_PRESENT, .inputs = {{ .name = name_tv, .vmux = 1, @@ -989,7 +1005,7 @@ struct saa7134_board saa7134_boards[] = .name = "UPMOST PURPLE TV", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1236_MK3, - .need_tda9887 = 1, + .tda9887_conf = TDA9887_PRESENT, .inputs = {{ .name = name_tv, .vmux = 7, @@ -1000,7 +1016,7 @@ struct saa7134_board saa7134_boards[] = .vmux = 7, .amux = LINE1, }}, - }, + }, [SAA7134_BOARD_ITEMS_MTV005] = { /* Norman Jonas */ .name = "Items MuchTV Plus / IT-005", @@ -1025,6 +1041,56 @@ struct saa7134_board saa7134_boards[] = .amux = LINE2, }, }, + [SAA7134_BOARD_CINERGY200] = { + .name = "Terratec Cinergy 200 TV", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_PAL, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .gpio = 0x0000, + .tv = 1, + }}, + .mute = { + .name = name_mute, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_VIDEOMATE_TV_PVR] = { + /* Alain St-Denis */ + .name = "Compro VideoMate TV PVR/FM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .gpiomask = 0x808c0080, + .inputs = {{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0x00080 + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x00080 + },{ + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .tv = 1, + .gpio = 0x00080 + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x80000 + }, + .mute = { + .name = name_mute, + .amux = LINE2, + .gpio = 0x40000, + }, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -1088,6 +1154,12 @@ struct pci_device_id saa7134_pci_tbl[] = .driver_data = SAA7134_BOARD_FLYVIDEO2000, },{ .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7135, + .subvendor = 0x5168, + .subdevice = 0x0212, + .driver_data = SAA7134_BOARD_FLYTVPLATINUM, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, .subvendor = 0x16be, .subdevice = 0x0003, @@ -1219,6 +1291,19 @@ struct pci_device_id saa7134_pci_tbl[] = .subvendor = 0x12ab, .subdevice = 0x0800, .driver_data = SAA7133_BOARD_UPMOST_PURPLE_TV, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x153B, + .subdevice = 0x1152, + .driver_data = SAA7134_BOARD_CINERGY200, + + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x185b, + .subdevice = 0xc100, + .driver_data = SAA7134_BOARD_VIDEOMATE_TV_PVR, },{ /* --- boards without eeprom + subsystem ID --- */ @@ -1297,6 +1382,7 @@ static struct { static void board_flyvideo(struct saa7134_dev *dev) { #if 0 + /* non-working attempt to detect the correct tuner type ... */ u32 value; int index; @@ -1307,6 +1393,10 @@ static void board_flyvideo(struct saa713 fly_list[index].tuner_type); dev->tuner_type = fly_list[index].tuner_type; #endif + printk("%s: there are different flyvideo cards with different tuners\n" + "%s: out there, you might have to use the tuner= insmod\n" + "%s: option to override the default value.\n", + dev->name, dev->name, dev->name); } /* ----------------------------------------------------------- */ @@ -1321,8 +1411,10 @@ int saa7134_board_init(struct saa7134_de switch (dev->board) { case SAA7134_BOARD_FLYVIDEO2000: case SAA7134_BOARD_FLYVIDEO3000: - board_flyvideo(dev); dev->has_remote = 1; + /* fall throuth */ + case SAA7134_BOARD_FLYTVPLATINUM: + board_flyvideo(dev); break; case SAA7134_BOARD_CINERGY400: case SAA7134_BOARD_CINERGY600: @@ -1333,6 +1425,12 @@ int saa7134_board_init(struct saa7134_de case SAA7134_BOARD_AVACSSMARTTV: dev->has_remote = 1; break; + case SAA7134_BOARD_MD5044: + printk("%s: seems there are two different versions of the MD5044\n" + "%s: (with the same ID) out there. If sound doesn't work for\n" + "%s: you try the audio_clock_override=0x200000 insmod option.\n", + dev->name,dev->name,dev->name); + break; } return 0; } --- linux-2.6.9-rc1/drivers/media/video/saa7134/saa7134-core.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/drivers/media/video/saa7134/saa7134-core.c 2004-09-03 00:57:14.126891648 -0700 @@ -868,7 +868,8 @@ static int __devinit saa7134_initdev(str must_configure_manually(); dev->board = SAA7134_BOARD_UNKNOWN; } - dev->tuner_type = saa7134_boards[dev->board].tuner_type; + dev->tuner_type = saa7134_boards[dev->board].tuner_type; + dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf; if (UNSET != tuner[saa7134_devcount]) dev->tuner_type = tuner[saa7134_devcount]; printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", @@ -919,7 +920,7 @@ static int __devinit saa7134_initdev(str /* load i2c helpers */ if (TUNER_ABSENT != dev->tuner_type) request_module("tuner"); - if (saa7134_boards[dev->board].need_tda9887) + if (dev->tda9887_conf) request_module("tda9887"); if (card_has_ts(dev)) request_module("saa6752hs"); @@ -949,7 +950,7 @@ static int __devinit saa7134_initdev(str dev->name); goto fail4; } - printk(KERN_INFO "%s: registered device video%d [ts]\n", + printk(KERN_INFO "%s: registered device video%d [mpeg]\n", dev->name,dev->ts_dev->minor & 0x1f); } --- linux-2.6.9-rc1/drivers/media/video/saa7134/saa7134.h 2004-08-24 00:03:19.000000000 -0700 +++ 25/drivers/media/video/saa7134/saa7134.h 2004-09-03 00:57:14.127891496 -0700 @@ -156,6 +156,9 @@ struct saa7134_format { #define SAA7134_BOARD_AVERMEDIA_305 35 #define SAA7133_BOARD_UPMOST_PURPLE_TV 36 #define SAA7134_BOARD_ITEMS_MTV005 37 +#define SAA7134_BOARD_CINERGY200 38 +#define SAA7134_BOARD_FLYTVPLATINUM 39 +#define SAA7134_BOARD_VIDEOMATE_TV_PVR 40 #define SAA7134_INPUT_MAX 8 @@ -178,13 +181,12 @@ struct saa7134_board { struct saa7134_input mute; /* peripheral I/O */ - unsigned int i2s_rate; unsigned int has_ts; enum saa7134_video_out video_out; /* i2c chip info */ unsigned int tuner_type; - unsigned int need_tda9887:1; + unsigned int tda9887_conf; }; #define card_has_radio(dev) (NULL != saa7134_boards[dev->board].radio.name) @@ -362,6 +364,7 @@ struct saa7134_dev { /* config info */ unsigned int board; unsigned int tuner_type; + unsigned int tda9887_conf; unsigned int gpio_value; /* i2c i/o */ @@ -397,6 +400,7 @@ struct saa7134_dev { int ctl_mirror; int ctl_y_odd; int ctl_y_even; + int ctl_automute; /* crop */ struct v4l2_rect crop_bounds; --- linux-2.6.9-rc1/drivers/media/video/saa7134/saa7134-i2c.c 2004-08-24 00:02:16.000000000 -0700 +++ 25/drivers/media/video/saa7134/saa7134-i2c.c 2004-09-03 00:57:14.128891344 -0700 @@ -327,8 +327,10 @@ static int attach_inform(struct i2c_clie { struct saa7134_dev *dev = client->adapter->algo_data; int tuner = dev->tuner_type; + int conf = dev->tda9887_conf; saa7134_i2c_call_clients(dev,TUNER_SET_TYPE,&tuner); + saa7134_i2c_call_clients(dev,TDA9887_SET_CONFIG,&conf); return 0; } --- linux-2.6.9-rc1/drivers/media/video/saa7134/saa7134-oss.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/media/video/saa7134/saa7134-oss.c 2004-09-03 00:57:14.129891192 -0700 @@ -776,8 +776,6 @@ int saa7134_oss_init1(struct saa7134_dev dev->oss.rate = 32000; if (oss_rate) dev->oss.rate = oss_rate; - if (saa7134_boards[dev->board].i2s_rate) - dev->oss.rate = saa7134_boards[dev->board].i2s_rate; dev->oss.rate = (dev->oss.rate > 40000) ? 48000 : 32000; /* mixer */ --- linux-2.6.9-rc1/drivers/media/video/saa7134/saa7134-tvaudio.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/drivers/media/video/saa7134/saa7134-tvaudio.c 2004-09-03 00:57:14.130891040 -0700 @@ -504,7 +504,8 @@ static int tvaudio_thread(void *data) dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1); dev->tvaudio = NULL; tvaudio_init(dev); - dev->automute = 1; + if (dev->ctl_automute) + dev->automute = 1; mute_input_7134(dev); /* give the tuner some time */ @@ -924,8 +925,9 @@ int saa7134_tvaudio_init2(struct saa7134 int (*my_thread)(void *data) = NULL; /* enable I2S audio output */ - if (saa7134_boards[dev->board].i2s_rate) { - int i2sform = (32000 == saa7134_boards[dev->board].i2s_rate) ? 0x00 : 0x01; + if (saa7134_boards[dev->board].has_ts) { + int i2sform = (48000 == dev->oss.rate) + ? 0x01 : 0x00; /* enable I2S output */ saa_writeb(SAA7134_I2S_OUTPUT_SELECT, 0x80); --- linux-2.6.9-rc1/drivers/media/video/saa7134/saa7134-video.c 2004-08-24 00:02:20.000000000 -0700 +++ 25/drivers/media/video/saa7134/saa7134-video.c 2004-09-03 00:57:14.132890736 -0700 @@ -272,7 +272,8 @@ static struct saa7134_tvnorm tvnorms[] = #define V4L2_CID_PRIVATE_INVERT (V4L2_CID_PRIVATE_BASE + 0) #define V4L2_CID_PRIVATE_Y_ODD (V4L2_CID_PRIVATE_BASE + 1) #define V4L2_CID_PRIVATE_Y_EVEN (V4L2_CID_PRIVATE_BASE + 2) -#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 3) +#define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_PRIVATE_BASE + 3) +#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 4) static const struct v4l2_queryctrl no_ctrl = { .name = "42", @@ -356,6 +357,13 @@ static const struct v4l2_queryctrl video .maximum = 128, .default_value = 0, .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_PRIVATE_AUTOMUTE, + .name = "automute", + .minimum = 0, + .maximum = 1, + .default_value = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, } }; static const unsigned int CTRLS = ARRAY_SIZE(video_ctrls); @@ -433,10 +441,11 @@ void res_free(struct saa7134_dev *dev, s static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm) { - int luma_control,sync_control,mux; + int luma_control,sync_control,mux,nosignal; dprintk("set tv norm = %s\n",norm->name); dev->tvnorm = norm; + nosignal = (0 == (saa_readb(SAA7134_STATUS_VIDEO1) & 0x03)); mux = card_in(dev,dev->ctl_input).vmux; luma_control = norm->luma_control; @@ -444,7 +453,7 @@ static void set_tvnorm(struct saa7134_de if (mux > 5) luma_control |= 0x80; /* svideo */ - if (noninterlaced) + if (noninterlaced || nosignal) sync_control |= 0x20; /* setup cropping */ @@ -1043,6 +1052,9 @@ static int get_control(struct saa7134_de case V4L2_CID_PRIVATE_Y_ODD: c->value = dev->ctl_y_odd; break; + case V4L2_CID_PRIVATE_AUTOMUTE: + c->value = dev->ctl_automute; + break; default: return -EINVAL; } @@ -1118,6 +1130,17 @@ static int set_control(struct saa7134_de dev->ctl_y_odd = c->value; restart_overlay = 1; break; + case V4L2_CID_PRIVATE_AUTOMUTE: + dev->ctl_automute = c->value; + if (dev->tda9887_conf) { + if (dev->ctl_automute) + dev->tda9887_conf |= TDA9887_AUTOMUTE; + else + dev->tda9887_conf &= ~TDA9887_AUTOMUTE; + saa7134_i2c_call_clients(dev, TDA9887_SET_CONFIG, + &dev->tda9887_conf); + } + break; default: return -EINVAL; } @@ -1820,7 +1843,7 @@ static int video_do_ioctl(struct inode * struct v4l2_frequency *f = arg; memset(f,0,sizeof(*f)); - f->type = V4L2_TUNER_ANALOG_TV; + f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; f->frequency = dev->ctl_freq; return 0; } @@ -1830,7 +1853,9 @@ static int video_do_ioctl(struct inode * if (0 != f->tuner) return -EINVAL; - if (V4L2_TUNER_ANALOG_TV != f->type) + if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type) + return -EINVAL; + if (1 == fh->radio && V4L2_TUNER_RADIO != f->type) return -EINVAL; down(&dev->lock); dev->ctl_freq = f->frequency; @@ -2244,9 +2269,12 @@ int saa7134_video_init1(struct saa7134_d dev->ctl_hue = ctrl_by_id(V4L2_CID_HUE)->default_value; dev->ctl_saturation = ctrl_by_id(V4L2_CID_SATURATION)->default_value; dev->ctl_volume = ctrl_by_id(V4L2_CID_AUDIO_VOLUME)->default_value; + dev->ctl_mute = ctrl_by_id(V4L2_CID_AUDIO_MUTE)->default_value; + dev->ctl_invert = ctrl_by_id(V4L2_CID_PRIVATE_INVERT)->default_value; + dev->ctl_automute = ctrl_by_id(V4L2_CID_PRIVATE_AUTOMUTE)->default_value; - dev->ctl_invert = 0; - dev->ctl_mute = 1; + if (dev->tda9887_conf && dev->ctl_automute) + dev->tda9887_conf |= TDA9887_AUTOMUTE; dev->automute = 0; INIT_LIST_HEAD(&dev->video_q.queue); @@ -2300,10 +2328,14 @@ void saa7134_irq_video_intl(struct saa71 if (0 != norm) { /* wake up tvaudio audio carrier scan thread */ saa7134_tvaudio_do_scan(dev); + if (!noninterlaced) + saa_clearb(SAA7134_SYNC_CTRL, 0x20); } else { /* no video signal -> mute audio */ - dev->automute = 1; + if (dev->ctl_automute) + dev->automute = 1; saa7134_tvaudio_setmute(dev); + saa_setb(SAA7134_SYNC_CTRL, 0x20); } } --- linux-2.6.9-rc1/drivers/media/video/tda7432.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/drivers/media/video/tda7432.c 2004-09-03 00:57:13.631966888 -0700 @@ -532,17 +532,17 @@ static struct i2c_client client_template .driver = &driver, }; -static int tda7432_init(void) +static int __init tda7432_init(void) { if ( (loudness < 0) || (loudness > 15) ) { printk(KERN_ERR "tda7432: loudness parameter must be between 0 and 15\n"); return -EINVAL; } - i2c_add_driver(&driver); - return 0; + + return i2c_add_driver(&driver); } -static void tda7432_fini(void) +static void __exit tda7432_fini(void) { i2c_del_driver(&driver); } --- linux-2.6.9-rc1/drivers/media/video/tda9875.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/drivers/media/video/tda9875.c 2004-09-03 00:57:13.632966736 -0700 @@ -403,13 +403,12 @@ static struct i2c_client client_template .driver = &driver, }; -static int tda9875_init(void) +static int __init tda9875_init(void) { - i2c_add_driver(&driver); - return 0; + return i2c_add_driver(&driver); } -static void tda9875_fini(void) +static void __exit tda9875_fini(void) { i2c_del_driver(&driver); } --- linux-2.6.9-rc1/drivers/media/video/tda9887.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/drivers/media/video/tda9887.c 2004-09-03 00:57:13.786943328 -0700 @@ -8,6 +8,7 @@ #include #include +#include #include /* Chips: @@ -47,6 +48,7 @@ struct tda9887 { struct i2c_client client; v4l2_std_id std; unsigned int radio; + unsigned int config; unsigned int pinnacle_id; unsigned int using_v4l2; }; @@ -397,6 +399,36 @@ static int tda9887_set_insmod(struct tda return 0; } +static int tda9887_set_config(struct tda9887 *t, char *buf) +{ + if (t->config & TDA9887_PORT1) + buf[1] |= cOutputPort1Inactive; + if (t->config & TDA9887_PORT2) + buf[1] |= cOutputPort2Inactive; + if (t->config & TDA9887_QSS) + buf[1] |= cQSS; + if (t->config & TDA9887_INTERCARRIER) + buf[1] &= ~cQSS; + + if (t->config & TDA9887_AUTOMUTE) + buf[1] |= cAutoMuteFmActive; + if (t->config & TDA9887_DEEMPHASIS_MASK) { + buf[2] &= ~0x60; + switch (t->config & TDA9887_DEEMPHASIS_MASK) { + case TDA9887_DEEMPHASIS_NONE: + buf[2] |= cDeemphasisOFF; + break; + case TDA9887_DEEMPHASIS_50: + buf[2] |= cDeemphasisON | cDeemphasis50; + break; + case TDA9887_DEEMPHASIS_75: + buf[2] |= cDeemphasisON | cDeemphasis75; + break; + } + } + return 0; +} + /* ---------------------------------------------------------------------- */ static int tda9887_set_pinnacle(struct tda9887 *t, char *buf) @@ -499,6 +531,7 @@ static int tda9887_configure(struct tda9 if (UNSET != t->pinnacle_id) { tda9887_set_pinnacle(t,buf); } + tda9887_set_config(t,buf); tda9887_set_insmod(t,buf); dprintk(PREFIX "writing: b=0x%02x c=0x%02x e=0x%02x\n", @@ -594,6 +627,14 @@ tda9887_command(struct i2c_client *clien tda9887_configure(t); break; } + case TDA9887_SET_CONFIG: + { + int *i = arg; + + t->config = *i; + tda9887_configure(t); + break; + } /* --- v4l ioctls --- */ /* take care: bttv does userspace copying, we'll get a kernel pointer here... */ @@ -644,6 +685,25 @@ tda9887_command(struct i2c_client *clien t->radio = 1; } tda9887_configure(t); + break; + } + case VIDIOC_G_TUNER: + { + static int AFC_BITS_2_kHz[] = { + -12500, -37500, -62500, -97500, + -112500, -137500, -162500, -187500, + 187500, 162500, 137500, 112500, + 97500 , 62500, 37500 , 12500 + }; + struct v4l2_tuner* tuner = arg; + + if (t->radio) { + __u8 reg = 0; + tuner->afc=0; + if (1 == i2c_master_recv(&t->client,®,1)) + tuner->afc = AFC_BITS_2_kHz[(reg>>1)&0x0f]; + } + break; } default: /* nothing */ @@ -670,13 +730,12 @@ static struct i2c_client client_template .driver = &driver, }; -static int tda9887_init_module(void) +static int __init tda9887_init_module(void) { - i2c_add_driver(&driver); - return 0; + return i2c_add_driver(&driver); } -static void tda9887_cleanup_module(void) +static void __exit tda9887_cleanup_module(void) { i2c_del_driver(&driver); } --- linux-2.6.9-rc1/drivers/media/video/tuner.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/media/video/tuner.c 2004-09-03 00:57:13.788943024 -0700 @@ -208,7 +208,7 @@ static struct tunertype tuners[] = { { "Temic PAL* auto + FM (4009 FN5)", TEMIC, PAL, 16*141.00, 16*464.00, 0xa0,0x90,0x30,0x8e,623}, { "SHARP NTSC_JP (2U5JF5540)", SHARP, NTSC, /* 940=16*58.75 NTSC@Japan */ - 16*137.25,16*317.25,0x01,0x02,0x08,0x8e,732 }, // Corrected to NTSC=732 (was:940) + 16*137.25,16*317.25,0x01,0x02,0x08,0x8e,940 }, { "Samsung PAL TCPM9091PD27", Samsung, PAL, /* from sourceforge v3tv */ 16*169,16*464,0xA0,0x90,0x30,0x8e,623}, @@ -229,7 +229,7 @@ static struct tunertype tuners[] = { 16*170.00, 16*450.00, 0x01,0x02,0x08,0x8e,732}, { "HITACHI V7-J180AT", HITACHI, NTSC, - 16*170.00, 16*450.00, 0x01,0x02,0x00,0x8e,940 }, + 16*170.00, 16*450.00, 0x01,0x02,0x08,0x8e,940 }, { "Philips PAL_MK (FI1216 MK)", Philips, PAL, 16*140.25,16*463.25,0x01,0xc2,0xcf,0x8e,623}, { "Philips 1236D ATSC/NTSC daul in",Philips,ATSC, @@ -241,6 +241,13 @@ static struct tunertype tuners[] = { 16*160.00,16*442.00,0x01,0x02,0x04,0x8e,732}, { "Microtune 4049 FM5",Microtune,PAL, 16*141.00,16*464.00,0xa0,0x90,0x30,0x8e,623}, + { "Panasonic VP27s/ENGE4324D", Panasonic, NTSC, + 16*160.00,16*454.00,0x01,0x02,0x08,0xce,940}, + { "LG NTSC (TAPE series)", LGINNOTEK, NTSC, + 16*170.00, 16*450.00, 0x01,0x02,0x04,0x8e,732 }, + + { "Tenna TNF 8831 BGFF)", Philips, PAL, + 16*161.25,16*463.25,0xa0,0x90,0x30,0x8e,623}, }; #define TUNERS ARRAY_SIZE(tuners) @@ -934,6 +941,9 @@ static void default_set_radio_freq(struc case TUNER_PHILIPS_FM1236_MK3: buffer[3] = 0x19; break; + case TUNER_LG_PAL_FM: + buffer[3] = 0xa5; + break; default: buffer[3] = 0xa4; break; @@ -1300,13 +1310,12 @@ static struct i2c_client client_template .driver = &driver, }; -static int tuner_init_module(void) +static int __init tuner_init_module(void) { - i2c_add_driver(&driver); - return 0; + return i2c_add_driver(&driver); } -static void tuner_cleanup_module(void) +static void __exit tuner_cleanup_module(void) { i2c_del_driver(&driver); } --- linux-2.6.9-rc1/drivers/media/video/tvaudio.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/drivers/media/video/tvaudio.c 2004-09-03 00:57:13.633966584 -0700 @@ -1651,7 +1651,7 @@ static struct i2c_client client_template .driver = &driver, }; -static int audiochip_init_module(void) +static int __init audiochip_init_module(void) { struct CHIPDESC *desc; printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n"); @@ -1659,11 +1659,11 @@ static int audiochip_init_module(void) for (desc = chiplist; desc->name != NULL; desc++) printk("%s%s", (desc == chiplist) ? "" : ",",desc->name); printk("\n"); - i2c_add_driver(&driver); - return 0; + + return i2c_add_driver(&driver); } -static void audiochip_cleanup_module(void) +static void __exit audiochip_cleanup_module(void) { i2c_del_driver(&driver); } --- linux-2.6.9-rc1/drivers/media/video/tvmixer.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/media/video/tvmixer.c 2004-09-03 00:57:13.634966432 -0700 @@ -330,17 +330,17 @@ static int tvmixer_clients(struct i2c_cl /* ----------------------------------------------------------------------- */ -static int tvmixer_init_module(void) +static int __init tvmixer_init_module(void) { int i; for (i = 0; i < DEV_MAX; i++) devices[i].minor = -1; - i2c_add_driver(&driver); - return 0; + + return i2c_add_driver(&driver); } -static void tvmixer_cleanup_module(void) +static void __exit tvmixer_cleanup_module(void) { int i; --- linux-2.6.9-rc1/drivers/media/video/zoran_device.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/drivers/media/video/zoran_device.c 2004-09-02 20:51:51.000000000 -0700 @@ -1105,8 +1105,7 @@ zr36057_enable_jpg (struct zoran ZR36057_ISR); btand(~ZR36057_JMC_Go_en, ZR36057_JMC); // \Go_en - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(HZ / 20); + msleep(50); set_videobus_dir(zr, 0); set_frame(zr, 1); // /FRAME --- linux-2.6.9-rc1/drivers/media/video/zoran_driver.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/media/video/zoran_driver.c 2004-09-02 20:51:51.000000000 -0700 @@ -2265,8 +2265,8 @@ zoran_do_ioctl (struct inode *inode, dprintk(3, KERN_DEBUG - "%s: VIDIOCSFBUF - base=0x%x, w=%d, h=%d, depth=%d, bpl=%d\n", - ZR_DEVNAME(zr), (u32) vbuf->base, vbuf->width, + "%s: VIDIOCSFBUF - base=%p, w=%d, h=%d, depth=%d, bpl=%d\n", + ZR_DEVNAME(zr), vbuf->base, vbuf->width, vbuf->height, vbuf->depth, vbuf->bytesperline); for (i = 0; i < zoran_num_formats; i++) --- linux-2.6.9-rc1/drivers/message/fusion/ascq_tbl.c 2004-08-24 00:03:38.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,2416 +0,0 @@ -#ifndef SCSI_ASCQ_TBL_C_INCLUDED -#define SCSI_ASCQ_TBL_C_INCLUDED - -/* AuToMaGiCaLlY generated from: "t10.org/asc-num.txt" - ******************************************************************************* - * File: ASC-NUM.TXT - * - * SCSI ASC/ASCQ Assignments - * Numeric Sorted Listing - * as of 5/18/00 - * - * D - DIRECT ACCESS DEVICE (SBC-2) device column key - * .T - SEQUENTIAL ACCESS DEVICE (SSC) ------------------- - * . L - PRINTER DEVICE (SSC) blank = reserved - * . P - PROCESSOR DEVICE (SPC) not blank = allowed - * . .W - WRITE ONCE READ MULTIPLE DEVICE (SBC-2) - * . . R - CD DEVICE (MMC) - * . . S - SCANNER DEVICE (SCSI-2) - * . . .O - OPTICAL MEMORY DEVICE (SBC-2) - * . . . M - MEDIA CHANGER DEVICE (SMC) - * . . . C - COMMUNICATION DEVICE (SCSI-2) - * . . . .A - STORAGE ARRAY DEVICE (SCC) - * . . . . E - ENCLOSURE SERVICES DEVICE (SES) - * . . . . B - SIMPLIFIED DIRECT-ACCESS DEVICE (RBC) - * . . . . .K - OPTICAL CARD READER/WRITER DEVICE (OCRW) - * ASC/ASCQ DTLPWRSOMCAEBK Description - * ------- -------------- ---------------------------------------------------- - */ - -static char SenseDevTypes001[] = "DTLPWRSOMCAEBK"; -static char SenseDevTypes002[] = ".T............"; -static char SenseDevTypes003[] = ".T....S......."; -static char SenseDevTypes004[] = ".TL...S......."; -static char SenseDevTypes005[] = ".....R........"; -static char SenseDevTypes006[] = "DTL.WRSOM.AEBK"; -static char SenseDevTypes007[] = "D...W..O....BK"; -static char SenseDevTypes008[] = "D...WR.OM...BK"; -static char SenseDevTypes009[] = "DTL.W.SO....BK"; -static char SenseDevTypes010[] = "DTL..R.O....B."; -static char SenseDevTypes011[] = "DT..W..OMCA.BK"; -static char SenseDevTypes012[] = ".............."; -static char SenseDevTypes013[] = "DTL.WRSOMCAEBK"; -static char SenseDevTypes014[] = "DTL.WRSOM...BK"; -static char SenseDevTypes015[] = "DT...R.OM...BK"; -static char SenseDevTypes016[] = "DTLPWRSO.C...K"; -static char SenseDevTypes017[] = "DT..WR.O....B."; -static char SenseDevTypes018[] = "....WR.O.....K"; -static char SenseDevTypes019[] = "....WR.O......"; -static char SenseDevTypes020[] = ".T...RS......."; -static char SenseDevTypes021[] = ".............K"; -static char SenseDevTypes022[] = "DT..W..O....B."; -static char SenseDevTypes023[] = "DT..WRSO....BK"; -static char SenseDevTypes024[] = "DT..W.SO....BK"; -static char SenseDevTypes025[] = "....WR.O....B."; -static char SenseDevTypes026[] = "....W..O....B."; -static char SenseDevTypes027[] = "DT.....O....BK"; -static char SenseDevTypes028[] = "DTL.WRSO....BK"; -static char SenseDevTypes029[] = "DT..WR.O....BK"; -static char SenseDevTypes030[] = "DT..W..O....BK"; -static char SenseDevTypes031[] = "D...WR.O....BK"; -static char SenseDevTypes032[] = "D......O.....K"; -static char SenseDevTypes033[] = "D......O....BK"; -static char SenseDevTypes034[] = "DT..WR.OM...BK"; -static char SenseDevTypes035[] = "D............."; -static char SenseDevTypes036[] = "DTLPWRSOMCAE.K"; -static char SenseDevTypes037[] = "DTLPWRSOMCA.BK"; -static char SenseDevTypes038[] = ".T...R........"; -static char SenseDevTypes039[] = "DT..WR.OM...B."; -static char SenseDevTypes040[] = "DTL.WRSOMCAE.K"; -static char SenseDevTypes041[] = "DTLPWRSOMCAE.."; -static char SenseDevTypes042[] = "......S......."; -static char SenseDevTypes043[] = "............B."; -static char SenseDevTypes044[] = "DTLPWRSO.CA..K"; -static char SenseDevTypes045[] = "DT...R.......K"; -static char SenseDevTypes046[] = "D.L..R.O....B."; -static char SenseDevTypes047[] = "..L..........."; -static char SenseDevTypes048[] = ".TL..........."; -static char SenseDevTypes049[] = "DTLPWRSOMC..BK"; -static char SenseDevTypes050[] = "DT..WR.OMCAEBK"; -static char SenseDevTypes051[] = "DT..WR.OMCAEB."; -static char SenseDevTypes052[] = ".T...R.O......"; -static char SenseDevTypes053[] = "...P.........."; -static char SenseDevTypes054[] = "DTLPWRSOM.AE.K"; -static char SenseDevTypes055[] = "DTLPWRSOM.AE.."; -static char SenseDevTypes056[] = ".......O......"; -static char SenseDevTypes057[] = "DTLPWRSOM...BK"; -static char SenseDevTypes058[] = "DT..WR.O..A.BK"; -static char SenseDevTypes059[] = "DTLPWRSOM....K"; -static char SenseDevTypes060[] = "D......O......"; -static char SenseDevTypes061[] = ".....R......B."; -static char SenseDevTypes062[] = "D...........B."; -static char SenseDevTypes063[] = "............BK"; -static char SenseDevTypes064[] = "..........A..."; - -static ASCQ_Table_t ASCQ_Table[] = { - { - 0x00, 0x00, - SenseDevTypes001, - "NO ADDITIONAL SENSE INFORMATION" - }, - { - 0x00, 0x01, - SenseDevTypes002, - "FILEMARK DETECTED" - }, - { - 0x00, 0x02, - SenseDevTypes003, - "END-OF-PARTITION/MEDIUM DETECTED" - }, - { - 0x00, 0x03, - SenseDevTypes002, - "SETMARK DETECTED" - }, - { - 0x00, 0x04, - SenseDevTypes003, - "BEGINNING-OF-PARTITION/MEDIUM DETECTED" - }, - { - 0x00, 0x05, - SenseDevTypes004, - "END-OF-DATA DETECTED" - }, - { - 0x00, 0x06, - SenseDevTypes001, - "I/O PROCESS TERMINATED" - }, - { - 0x00, 0x11, - SenseDevTypes005, - "AUDIO PLAY OPERATION IN PROGRESS" - }, - { - 0x00, 0x12, - SenseDevTypes005, - "AUDIO PLAY OPERATION PAUSED" - }, - { - 0x00, 0x13, - SenseDevTypes005, - "AUDIO PLAY OPERATION SUCCESSFULLY COMPLETED" - }, - { - 0x00, 0x14, - SenseDevTypes005, - "AUDIO PLAY OPERATION STOPPED DUE TO ERROR" - }, - { - 0x00, 0x15, - SenseDevTypes005, - "NO CURRENT AUDIO STATUS TO RETURN" - }, - { - 0x00, 0x16, - SenseDevTypes001, - "OPERATION IN PROGRESS" - }, - { - 0x00, 0x17, - SenseDevTypes006, - "CLEANING REQUESTED" - }, - { - 0x01, 0x00, - SenseDevTypes007, - "NO INDEX/SECTOR SIGNAL" - }, - { - 0x02, 0x00, - SenseDevTypes008, - "NO SEEK COMPLETE" - }, - { - 0x03, 0x00, - SenseDevTypes009, - "PERIPHERAL DEVICE WRITE FAULT" - }, - { - 0x03, 0x01, - SenseDevTypes002, - "NO WRITE CURRENT" - }, - { - 0x03, 0x02, - SenseDevTypes002, - "EXCESSIVE WRITE ERRORS" - }, - { - 0x04, 0x00, - SenseDevTypes001, - "LOGICAL UNIT NOT READY, CAUSE NOT REPORTABLE" - }, - { - 0x04, 0x01, - SenseDevTypes001, - "LOGICAL UNIT IS IN PROCESS OF BECOMING READY" - }, - { - 0x04, 0x02, - SenseDevTypes001, - "LOGICAL UNIT NOT READY, INITIALIZING CMD. REQUIRED" - }, - { - 0x04, 0x03, - SenseDevTypes001, - "LOGICAL UNIT NOT READY, MANUAL INTERVENTION REQUIRED" - }, - { - 0x04, 0x04, - SenseDevTypes010, - "LOGICAL UNIT NOT READY, FORMAT IN PROGRESS" - }, - { - 0x04, 0x05, - SenseDevTypes011, - "LOGICAL UNIT NOT READY, REBUILD IN PROGRESS" - }, - { - 0x04, 0x06, - SenseDevTypes011, - "LOGICAL UNIT NOT READY, RECALCULATION IN PROGRESS" - }, - { - 0x04, 0x07, - SenseDevTypes001, - "LOGICAL UNIT NOT READY, OPERATION IN PROGRESS" - }, - { - 0x04, 0x08, - SenseDevTypes005, - "LOGICAL UNIT NOT READY, LONG WRITE IN PROGRESS" - }, - { - 0x04, 0x09, - SenseDevTypes001, - "LOGICAL UNIT NOT READY, SELF-TEST IN PROGRESS" - }, - { - 0x04, 0x10, - SenseDevTypes012, - "auxiliary memory code 2 (99-148) [proposed]" - }, - { - 0x05, 0x00, - SenseDevTypes013, - "LOGICAL UNIT DOES NOT RESPOND TO SELECTION" - }, - { - 0x06, 0x00, - SenseDevTypes008, - "NO REFERENCE POSITION FOUND" - }, - { - 0x07, 0x00, - SenseDevTypes014, - "MULTIPLE PERIPHERAL DEVICES SELECTED" - }, - { - 0x08, 0x00, - SenseDevTypes013, - "LOGICAL UNIT COMMUNICATION FAILURE" - }, - { - 0x08, 0x01, - SenseDevTypes013, - "LOGICAL UNIT COMMUNICATION TIME-OUT" - }, - { - 0x08, 0x02, - SenseDevTypes013, - "LOGICAL UNIT COMMUNICATION PARITY ERROR" - }, - { - 0x08, 0x03, - SenseDevTypes015, - "LOGICAL UNIT COMMUNICATION CRC ERROR (ULTRA-DMA/32)" - }, - { - 0x08, 0x04, - SenseDevTypes016, - "UNREACHABLE COPY TARGET" - }, - { - 0x09, 0x00, - SenseDevTypes017, - "TRACK FOLLOWING ERROR" - }, - { - 0x09, 0x01, - SenseDevTypes018, - "TRACKING SERVO FAILURE" - }, - { - 0x09, 0x02, - SenseDevTypes018, - "FOCUS SERVO FAILURE" - }, - { - 0x09, 0x03, - SenseDevTypes019, - "SPINDLE SERVO FAILURE" - }, - { - 0x09, 0x04, - SenseDevTypes017, - "HEAD SELECT FAULT" - }, - { - 0x0A, 0x00, - SenseDevTypes001, - "ERROR LOG OVERFLOW" - }, - { - 0x0B, 0x00, - SenseDevTypes001, - "WARNING" - }, - { - 0x0B, 0x01, - SenseDevTypes001, - "WARNING - SPECIFIED TEMPERATURE EXCEEDED" - }, - { - 0x0B, 0x02, - SenseDevTypes001, - "WARNING - ENCLOSURE DEGRADED" - }, - { - 0x0C, 0x00, - SenseDevTypes020, - "WRITE ERROR" - }, - { - 0x0C, 0x01, - SenseDevTypes021, - "WRITE ERROR - RECOVERED WITH AUTO REALLOCATION" - }, - { - 0x0C, 0x02, - SenseDevTypes007, - "WRITE ERROR - AUTO REALLOCATION FAILED" - }, - { - 0x0C, 0x03, - SenseDevTypes007, - "WRITE ERROR - RECOMMEND REASSIGNMENT" - }, - { - 0x0C, 0x04, - SenseDevTypes022, - "COMPRESSION CHECK MISCOMPARE ERROR" - }, - { - 0x0C, 0x05, - SenseDevTypes022, - "DATA EXPANSION OCCURRED DURING COMPRESSION" - }, - { - 0x0C, 0x06, - SenseDevTypes022, - "BLOCK NOT COMPRESSIBLE" - }, - { - 0x0C, 0x07, - SenseDevTypes005, - "WRITE ERROR - RECOVERY NEEDED" - }, - { - 0x0C, 0x08, - SenseDevTypes005, - "WRITE ERROR - RECOVERY FAILED" - }, - { - 0x0C, 0x09, - SenseDevTypes005, - "WRITE ERROR - LOSS OF STREAMING" - }, - { - 0x0C, 0x0A, - SenseDevTypes005, - "WRITE ERROR - PADDING BLOCKS ADDED" - }, - { - 0x0C, 0x0B, - SenseDevTypes012, - "auxiliary memory code 4 (99-148) [proposed]" - }, - { - 0x10, 0x00, - SenseDevTypes007, - "ID CRC OR ECC ERROR" - }, - { - 0x11, 0x00, - SenseDevTypes023, - "UNRECOVERED READ ERROR" - }, - { - 0x11, 0x01, - SenseDevTypes023, - "READ RETRIES EXHAUSTED" - }, - { - 0x11, 0x02, - SenseDevTypes023, - "ERROR TOO LONG TO CORRECT" - }, - { - 0x11, 0x03, - SenseDevTypes024, - "MULTIPLE READ ERRORS" - }, - { - 0x11, 0x04, - SenseDevTypes007, - "UNRECOVERED READ ERROR - AUTO REALLOCATE FAILED" - }, - { - 0x11, 0x05, - SenseDevTypes025, - "L-EC UNCORRECTABLE ERROR" - }, - { - 0x11, 0x06, - SenseDevTypes025, - "CIRC UNRECOVERED ERROR" - }, - { - 0x11, 0x07, - SenseDevTypes026, - "DATA RE-SYNCHRONIZATION ERROR" - }, - { - 0x11, 0x08, - SenseDevTypes002, - "INCOMPLETE BLOCK READ" - }, - { - 0x11, 0x09, - SenseDevTypes002, - "NO GAP FOUND" - }, - { - 0x11, 0x0A, - SenseDevTypes027, - "MISCORRECTED ERROR" - }, - { - 0x11, 0x0B, - SenseDevTypes007, - "UNRECOVERED READ ERROR - RECOMMEND REASSIGNMENT" - }, - { - 0x11, 0x0C, - SenseDevTypes007, - "UNRECOVERED READ ERROR - RECOMMEND REWRITE THE DATA" - }, - { - 0x11, 0x0D, - SenseDevTypes017, - "DE-COMPRESSION CRC ERROR" - }, - { - 0x11, 0x0E, - SenseDevTypes017, - "CANNOT DECOMPRESS USING DECLARED ALGORITHM" - }, - { - 0x11, 0x0F, - SenseDevTypes005, - "ERROR READING UPC/EAN NUMBER" - }, - { - 0x11, 0x10, - SenseDevTypes005, - "ERROR READING ISRC NUMBER" - }, - { - 0x11, 0x11, - SenseDevTypes005, - "READ ERROR - LOSS OF STREAMING" - }, - { - 0x11, 0x12, - SenseDevTypes012, - "auxiliary memory code 3 (99-148) [proposed]" - }, - { - 0x12, 0x00, - SenseDevTypes007, - "ADDRESS MARK NOT FOUND FOR ID FIELD" - }, - { - 0x13, 0x00, - SenseDevTypes007, - "ADDRESS MARK NOT FOUND FOR DATA FIELD" - }, - { - 0x14, 0x00, - SenseDevTypes028, - "RECORDED ENTITY NOT FOUND" - }, - { - 0x14, 0x01, - SenseDevTypes029, - "RECORD NOT FOUND" - }, - { - 0x14, 0x02, - SenseDevTypes002, - "FILEMARK OR SETMARK NOT FOUND" - }, - { - 0x14, 0x03, - SenseDevTypes002, - "END-OF-DATA NOT FOUND" - }, - { - 0x14, 0x04, - SenseDevTypes002, - "BLOCK SEQUENCE ERROR" - }, - { - 0x14, 0x05, - SenseDevTypes030, - "RECORD NOT FOUND - RECOMMEND REASSIGNMENT" - }, - { - 0x14, 0x06, - SenseDevTypes030, - "RECORD NOT FOUND - DATA AUTO-REALLOCATED" - }, - { - 0x15, 0x00, - SenseDevTypes014, - "RANDOM POSITIONING ERROR" - }, - { - 0x15, 0x01, - SenseDevTypes014, - "MECHANICAL POSITIONING ERROR" - }, - { - 0x15, 0x02, - SenseDevTypes029, - "POSITIONING ERROR DETECTED BY READ OF MEDIUM" - }, - { - 0x16, 0x00, - SenseDevTypes007, - "DATA SYNCHRONIZATION MARK ERROR" - }, - { - 0x16, 0x01, - SenseDevTypes007, - "DATA SYNC ERROR - DATA REWRITTEN" - }, - { - 0x16, 0x02, - SenseDevTypes007, - "DATA SYNC ERROR - RECOMMEND REWRITE" - }, - { - 0x16, 0x03, - SenseDevTypes007, - "DATA SYNC ERROR - DATA AUTO-REALLOCATED" - }, - { - 0x16, 0x04, - SenseDevTypes007, - "DATA SYNC ERROR - RECOMMEND REASSIGNMENT" - }, - { - 0x17, 0x00, - SenseDevTypes023, - "RECOVERED DATA WITH NO ERROR CORRECTION APPLIED" - }, - { - 0x17, 0x01, - SenseDevTypes023, - "RECOVERED DATA WITH RETRIES" - }, - { - 0x17, 0x02, - SenseDevTypes029, - "RECOVERED DATA WITH POSITIVE HEAD OFFSET" - }, - { - 0x17, 0x03, - SenseDevTypes029, - "RECOVERED DATA WITH NEGATIVE HEAD OFFSET" - }, - { - 0x17, 0x04, - SenseDevTypes025, - "RECOVERED DATA WITH RETRIES AND/OR CIRC APPLIED" - }, - { - 0x17, 0x05, - SenseDevTypes031, - "RECOVERED DATA USING PREVIOUS SECTOR ID" - }, - { - 0x17, 0x06, - SenseDevTypes007, - "RECOVERED DATA WITHOUT ECC - DATA AUTO-REALLOCATED" - }, - { - 0x17, 0x07, - SenseDevTypes031, - "RECOVERED DATA WITHOUT ECC - RECOMMEND REASSIGNMENT" - }, - { - 0x17, 0x08, - SenseDevTypes031, - "RECOVERED DATA WITHOUT ECC - RECOMMEND REWRITE" - }, - { - 0x17, 0x09, - SenseDevTypes031, - "RECOVERED DATA WITHOUT ECC - DATA REWRITTEN" - }, - { - 0x18, 0x00, - SenseDevTypes029, - "RECOVERED DATA WITH ERROR CORRECTION APPLIED" - }, - { - 0x18, 0x01, - SenseDevTypes031, - "RECOVERED DATA WITH ERROR CORR. & RETRIES APPLIED" - }, - { - 0x18, 0x02, - SenseDevTypes031, - "RECOVERED DATA - DATA AUTO-REALLOCATED" - }, - { - 0x18, 0x03, - SenseDevTypes005, - "RECOVERED DATA WITH CIRC" - }, - { - 0x18, 0x04, - SenseDevTypes005, - "RECOVERED DATA WITH L-EC" - }, - { - 0x18, 0x05, - SenseDevTypes031, - "RECOVERED DATA - RECOMMEND REASSIGNMENT" - }, - { - 0x18, 0x06, - SenseDevTypes031, - "RECOVERED DATA - RECOMMEND REWRITE" - }, - { - 0x18, 0x07, - SenseDevTypes007, - "RECOVERED DATA WITH ECC - DATA REWRITTEN" - }, - { - 0x19, 0x00, - SenseDevTypes032, - "DEFECT LIST ERROR" - }, - { - 0x19, 0x01, - SenseDevTypes032, - "DEFECT LIST NOT AVAILABLE" - }, - { - 0x19, 0x02, - SenseDevTypes032, - "DEFECT LIST ERROR IN PRIMARY LIST" - }, - { - 0x19, 0x03, - SenseDevTypes032, - "DEFECT LIST ERROR IN GROWN LIST" - }, - { - 0x1A, 0x00, - SenseDevTypes001, - "PARAMETER LIST LENGTH ERROR" - }, - { - 0x1B, 0x00, - SenseDevTypes001, - "SYNCHRONOUS DATA TRANSFER ERROR" - }, - { - 0x1C, 0x00, - SenseDevTypes033, - "DEFECT LIST NOT FOUND" - }, - { - 0x1C, 0x01, - SenseDevTypes033, - "PRIMARY DEFECT LIST NOT FOUND" - }, - { - 0x1C, 0x02, - SenseDevTypes033, - "GROWN DEFECT LIST NOT FOUND" - }, - { - 0x1D, 0x00, - SenseDevTypes029, - "MISCOMPARE DURING VERIFY OPERATION" - }, - { - 0x1E, 0x00, - SenseDevTypes007, - "RECOVERED ID WITH ECC CORRECTION" - }, - { - 0x1F, 0x00, - SenseDevTypes032, - "PARTIAL DEFECT LIST TRANSFER" - }, - { - 0x20, 0x00, - SenseDevTypes001, - "INVALID COMMAND OPERATION CODE" - }, - { - 0x20, 0x01, - SenseDevTypes012, - "access controls code 1 (99-314) [proposed]" - }, - { - 0x20, 0x02, - SenseDevTypes012, - "access controls code 2 (99-314) [proposed]" - }, - { - 0x20, 0x03, - SenseDevTypes012, - "access controls code 3 (99-314) [proposed]" - }, - { - 0x21, 0x00, - SenseDevTypes034, - "LOGICAL BLOCK ADDRESS OUT OF RANGE" - }, - { - 0x21, 0x01, - SenseDevTypes034, - "INVALID ELEMENT ADDRESS" - }, - { - 0x22, 0x00, - SenseDevTypes035, - "ILLEGAL FUNCTION (USE 20 00, 24 00, OR 26 00)" - }, - { - 0x24, 0x00, - SenseDevTypes001, - "INVALID FIELD IN CDB" - }, - { - 0x24, 0x01, - SenseDevTypes001, - "CDB DECRYPTION ERROR" - }, - { - 0x25, 0x00, - SenseDevTypes001, - "LOGICAL UNIT NOT SUPPORTED" - }, - { - 0x26, 0x00, - SenseDevTypes001, - "INVALID FIELD IN PARAMETER LIST" - }, - { - 0x26, 0x01, - SenseDevTypes001, - "PARAMETER NOT SUPPORTED" - }, - { - 0x26, 0x02, - SenseDevTypes001, - "PARAMETER VALUE INVALID" - }, - { - 0x26, 0x03, - SenseDevTypes036, - "THRESHOLD PARAMETERS NOT SUPPORTED" - }, - { - 0x26, 0x04, - SenseDevTypes001, - "INVALID RELEASE OF PERSISTENT RESERVATION" - }, - { - 0x26, 0x05, - SenseDevTypes037, - "DATA DECRYPTION ERROR" - }, - { - 0x26, 0x06, - SenseDevTypes016, - "TOO MANY TARGET DESCRIPTORS" - }, - { - 0x26, 0x07, - SenseDevTypes016, - "UNSUPPORTED TARGET DESCRIPTOR TYPE CODE" - }, - { - 0x26, 0x08, - SenseDevTypes016, - "TOO MANY SEGMENT DESCRIPTORS" - }, - { - 0x26, 0x09, - SenseDevTypes016, - "UNSUPPORTED SEGMENT DESCRIPTOR TYPE CODE" - }, - { - 0x26, 0x0A, - SenseDevTypes016, - "UNEXPECTED INEXACT SEGMENT" - }, - { - 0x26, 0x0B, - SenseDevTypes016, - "INLINE DATA LENGTH EXCEEDED" - }, - { - 0x26, 0x0C, - SenseDevTypes016, - "INVALID OPERATION FOR COPY SOURCE OR DESTINATION" - }, - { - 0x26, 0x0D, - SenseDevTypes016, - "COPY SEGMENT GRANULARITY VIOLATION" - }, - { - 0x27, 0x00, - SenseDevTypes029, - "WRITE PROTECTED" - }, - { - 0x27, 0x01, - SenseDevTypes029, - "HARDWARE WRITE PROTECTED" - }, - { - 0x27, 0x02, - SenseDevTypes029, - "LOGICAL UNIT SOFTWARE WRITE PROTECTED" - }, - { - 0x27, 0x03, - SenseDevTypes038, - "ASSOCIATED WRITE PROTECT" - }, - { - 0x27, 0x04, - SenseDevTypes038, - "PERSISTENT WRITE PROTECT" - }, - { - 0x27, 0x05, - SenseDevTypes038, - "PERMANENT WRITE PROTECT" - }, - { - 0x28, 0x00, - SenseDevTypes001, - "NOT READY TO READY CHANGE, MEDIUM MAY HAVE CHANGED" - }, - { - 0x28, 0x01, - SenseDevTypes039, - "IMPORT OR EXPORT ELEMENT ACCESSED" - }, - { - 0x29, 0x00, - SenseDevTypes001, - "POWER ON, RESET, OR BUS DEVICE RESET OCCURRED" - }, - { - 0x29, 0x01, - SenseDevTypes001, - "POWER ON OCCURRED" - }, - { - 0x29, 0x02, - SenseDevTypes001, - "SCSI BUS RESET OCCURRED" - }, - { - 0x29, 0x03, - SenseDevTypes001, - "BUS DEVICE RESET FUNCTION OCCURRED" - }, - { - 0x29, 0x04, - SenseDevTypes001, - "DEVICE INTERNAL RESET" - }, - { - 0x29, 0x05, - SenseDevTypes001, - "TRANSCEIVER MODE CHANGED TO SINGLE-ENDED" - }, - { - 0x29, 0x06, - SenseDevTypes001, - "TRANSCEIVER MODE CHANGED TO LVD" - }, - { - 0x2A, 0x00, - SenseDevTypes013, - "PARAMETERS CHANGED" - }, - { - 0x2A, 0x01, - SenseDevTypes013, - "MODE PARAMETERS CHANGED" - }, - { - 0x2A, 0x02, - SenseDevTypes040, - "LOG PARAMETERS CHANGED" - }, - { - 0x2A, 0x03, - SenseDevTypes036, - "RESERVATIONS PREEMPTED" - }, - { - 0x2A, 0x04, - SenseDevTypes041, - "RESERVATIONS RELEASED" - }, - { - 0x2A, 0x05, - SenseDevTypes041, - "REGISTRATIONS PREEMPTED" - }, - { - 0x2B, 0x00, - SenseDevTypes016, - "COPY CANNOT EXECUTE SINCE HOST CANNOT DISCONNECT" - }, - { - 0x2C, 0x00, - SenseDevTypes001, - "COMMAND SEQUENCE ERROR" - }, - { - 0x2C, 0x01, - SenseDevTypes042, - "TOO MANY WINDOWS SPECIFIED" - }, - { - 0x2C, 0x02, - SenseDevTypes042, - "INVALID COMBINATION OF WINDOWS SPECIFIED" - }, - { - 0x2C, 0x03, - SenseDevTypes005, - "CURRENT PROGRAM AREA IS NOT EMPTY" - }, - { - 0x2C, 0x04, - SenseDevTypes005, - "CURRENT PROGRAM AREA IS EMPTY" - }, - { - 0x2C, 0x05, - SenseDevTypes043, - "ILLEGAL POWER CONDITION REQUEST" - }, - { - 0x2D, 0x00, - SenseDevTypes002, - "OVERWRITE ERROR ON UPDATE IN PLACE" - }, - { - 0x2E, 0x00, - SenseDevTypes044, - "ERROR DETECTED BY THIRD PARTY TEMPORARY INITIATOR" - }, - { - 0x2E, 0x01, - SenseDevTypes044, - "THIRD PARTY DEVICE FAILURE" - }, - { - 0x2E, 0x02, - SenseDevTypes044, - "COPY TARGET DEVICE NOT REACHABLE" - }, - { - 0x2E, 0x03, - SenseDevTypes044, - "INCORRECT COPY TARGET DEVICE TYPE" - }, - { - 0x2E, 0x04, - SenseDevTypes044, - "COPY TARGET DEVICE DATA UNDERRUN" - }, - { - 0x2E, 0x05, - SenseDevTypes044, - "COPY TARGET DEVICE DATA OVERRUN" - }, - { - 0x2F, 0x00, - SenseDevTypes001, - "COMMANDS CLEARED BY ANOTHER INITIATOR" - }, - { - 0x30, 0x00, - SenseDevTypes034, - "INCOMPATIBLE MEDIUM INSTALLED" - }, - { - 0x30, 0x01, - SenseDevTypes029, - "CANNOT READ MEDIUM - UNKNOWN FORMAT" - }, - { - 0x30, 0x02, - SenseDevTypes029, - "CANNOT READ MEDIUM - INCOMPATIBLE FORMAT" - }, - { - 0x30, 0x03, - SenseDevTypes045, - "CLEANING CARTRIDGE INSTALLED" - }, - { - 0x30, 0x04, - SenseDevTypes029, - "CANNOT WRITE MEDIUM - UNKNOWN FORMAT" - }, - { - 0x30, 0x05, - SenseDevTypes029, - "CANNOT WRITE MEDIUM - INCOMPATIBLE FORMAT" - }, - { - 0x30, 0x06, - SenseDevTypes017, - "CANNOT FORMAT MEDIUM - INCOMPATIBLE MEDIUM" - }, - { - 0x30, 0x07, - SenseDevTypes006, - "CLEANING FAILURE" - }, - { - 0x30, 0x08, - SenseDevTypes005, - "CANNOT WRITE - APPLICATION CODE MISMATCH" - }, - { - 0x30, 0x09, - SenseDevTypes005, - "CURRENT SESSION NOT FIXATED FOR APPEND" - }, - { - 0x31, 0x00, - SenseDevTypes029, - "MEDIUM FORMAT CORRUPTED" - }, - { - 0x31, 0x01, - SenseDevTypes046, - "FORMAT COMMAND FAILED" - }, - { - 0x32, 0x00, - SenseDevTypes007, - "NO DEFECT SPARE LOCATION AVAILABLE" - }, - { - 0x32, 0x01, - SenseDevTypes007, - "DEFECT LIST UPDATE FAILURE" - }, - { - 0x33, 0x00, - SenseDevTypes002, - "TAPE LENGTH ERROR" - }, - { - 0x34, 0x00, - SenseDevTypes001, - "ENCLOSURE FAILURE" - }, - { - 0x35, 0x00, - SenseDevTypes001, - "ENCLOSURE SERVICES FAILURE" - }, - { - 0x35, 0x01, - SenseDevTypes001, - "UNSUPPORTED ENCLOSURE FUNCTION" - }, - { - 0x35, 0x02, - SenseDevTypes001, - "ENCLOSURE SERVICES UNAVAILABLE" - }, - { - 0x35, 0x03, - SenseDevTypes001, - "ENCLOSURE SERVICES TRANSFER FAILURE" - }, - { - 0x35, 0x04, - SenseDevTypes001, - "ENCLOSURE SERVICES TRANSFER REFUSED" - }, - { - 0x36, 0x00, - SenseDevTypes047, - "RIBBON, INK, OR TONER FAILURE" - }, - { - 0x37, 0x00, - SenseDevTypes013, - "ROUNDED PARAMETER" - }, - { - 0x38, 0x00, - SenseDevTypes043, - "EVENT STATUS NOTIFICATION" - }, - { - 0x38, 0x02, - SenseDevTypes043, - "ESN - POWER MANAGEMENT CLASS EVENT" - }, - { - 0x38, 0x04, - SenseDevTypes043, - "ESN - MEDIA CLASS EVENT" - }, - { - 0x38, 0x06, - SenseDevTypes043, - "ESN - DEVICE BUSY CLASS EVENT" - }, - { - 0x39, 0x00, - SenseDevTypes040, - "SAVING PARAMETERS NOT SUPPORTED" - }, - { - 0x3A, 0x00, - SenseDevTypes014, - "MEDIUM NOT PRESENT" - }, - { - 0x3A, 0x01, - SenseDevTypes034, - "MEDIUM NOT PRESENT - TRAY CLOSED" - }, - { - 0x3A, 0x02, - SenseDevTypes034, - "MEDIUM NOT PRESENT - TRAY OPEN" - }, - { - 0x3A, 0x03, - SenseDevTypes039, - "MEDIUM NOT PRESENT - LOADABLE" - }, - { - 0x3A, 0x04, - SenseDevTypes039, - "MEDIUM NOT PRESENT - MEDIUM AUXILIARY MEMORY ACCESSIBLE" - }, - { - 0x3B, 0x00, - SenseDevTypes048, - "SEQUENTIAL POSITIONING ERROR" - }, - { - 0x3B, 0x01, - SenseDevTypes002, - "TAPE POSITION ERROR AT BEGINNING-OF-MEDIUM" - }, - { - 0x3B, 0x02, - SenseDevTypes002, - "TAPE POSITION ERROR AT END-OF-MEDIUM" - }, - { - 0x3B, 0x03, - SenseDevTypes047, - "TAPE OR ELECTRONIC VERTICAL FORMS UNIT NOT READY" - }, - { - 0x3B, 0x04, - SenseDevTypes047, - "SLEW FAILURE" - }, - { - 0x3B, 0x05, - SenseDevTypes047, - "PAPER JAM" - }, - { - 0x3B, 0x06, - SenseDevTypes047, - "FAILED TO SENSE TOP-OF-FORM" - }, - { - 0x3B, 0x07, - SenseDevTypes047, - "FAILED TO SENSE BOTTOM-OF-FORM" - }, - { - 0x3B, 0x08, - SenseDevTypes002, - "REPOSITION ERROR" - }, - { - 0x3B, 0x09, - SenseDevTypes042, - "READ PAST END OF MEDIUM" - }, - { - 0x3B, 0x0A, - SenseDevTypes042, - "READ PAST BEGINNING OF MEDIUM" - }, - { - 0x3B, 0x0B, - SenseDevTypes042, - "POSITION PAST END OF MEDIUM" - }, - { - 0x3B, 0x0C, - SenseDevTypes003, - "POSITION PAST BEGINNING OF MEDIUM" - }, - { - 0x3B, 0x0D, - SenseDevTypes034, - "MEDIUM DESTINATION ELEMENT FULL" - }, - { - 0x3B, 0x0E, - SenseDevTypes034, - "MEDIUM SOURCE ELEMENT EMPTY" - }, - { - 0x3B, 0x0F, - SenseDevTypes005, - "END OF MEDIUM REACHED" - }, - { - 0x3B, 0x11, - SenseDevTypes034, - "MEDIUM MAGAZINE NOT ACCESSIBLE" - }, - { - 0x3B, 0x12, - SenseDevTypes034, - "MEDIUM MAGAZINE REMOVED" - }, - { - 0x3B, 0x13, - SenseDevTypes034, - "MEDIUM MAGAZINE INSERTED" - }, - { - 0x3B, 0x14, - SenseDevTypes034, - "MEDIUM MAGAZINE LOCKED" - }, - { - 0x3B, 0x15, - SenseDevTypes034, - "MEDIUM MAGAZINE UNLOCKED" - }, - { - 0x3B, 0x16, - SenseDevTypes005, - "MECHANICAL POSITIONING OR CHANGER ERROR" - }, - { - 0x3D, 0x00, - SenseDevTypes036, - "INVALID BITS IN IDENTIFY MESSAGE" - }, - { - 0x3E, 0x00, - SenseDevTypes001, - "LOGICAL UNIT HAS NOT SELF-CONFIGURED YET" - }, - { - 0x3E, 0x01, - SenseDevTypes001, - "LOGICAL UNIT FAILURE" - }, - { - 0x3E, 0x02, - SenseDevTypes001, - "TIMEOUT ON LOGICAL UNIT" - }, - { - 0x3E, 0x03, - SenseDevTypes001, - "LOGICAL UNIT FAILED SELF-TEST" - }, - { - 0x3E, 0x04, - SenseDevTypes001, - "LOGICAL UNIT UNABLE TO UPDATE SELF-TEST LOG" - }, - { - 0x3F, 0x00, - SenseDevTypes001, - "TARGET OPERATING CONDITIONS HAVE CHANGED" - }, - { - 0x3F, 0x01, - SenseDevTypes001, - "MICROCODE HAS BEEN CHANGED" - }, - { - 0x3F, 0x02, - SenseDevTypes049, - "CHANGED OPERATING DEFINITION" - }, - { - 0x3F, 0x03, - SenseDevTypes001, - "INQUIRY DATA HAS CHANGED" - }, - { - 0x3F, 0x04, - SenseDevTypes050, - "COMPONENT DEVICE ATTACHED" - }, - { - 0x3F, 0x05, - SenseDevTypes050, - "DEVICE IDENTIFIER CHANGED" - }, - { - 0x3F, 0x06, - SenseDevTypes051, - "REDUNDANCY GROUP CREATED OR MODIFIED" - }, - { - 0x3F, 0x07, - SenseDevTypes051, - "REDUNDANCY GROUP DELETED" - }, - { - 0x3F, 0x08, - SenseDevTypes051, - "SPARE CREATED OR MODIFIED" - }, - { - 0x3F, 0x09, - SenseDevTypes051, - "SPARE DELETED" - }, - { - 0x3F, 0x0A, - SenseDevTypes050, - "VOLUME SET CREATED OR MODIFIED" - }, - { - 0x3F, 0x0B, - SenseDevTypes050, - "VOLUME SET DELETED" - }, - { - 0x3F, 0x0C, - SenseDevTypes050, - "VOLUME SET DEASSIGNED" - }, - { - 0x3F, 0x0D, - SenseDevTypes050, - "VOLUME SET REASSIGNED" - }, - { - 0x3F, 0x0E, - SenseDevTypes041, - "REPORTED LUNS DATA HAS CHANGED" - }, - { - 0x3F, 0x0F, - SenseDevTypes001, - "ECHO BUFFER OVERWRITTEN" - }, - { - 0x3F, 0x10, - SenseDevTypes039, - "MEDIUM LOADABLE" - }, - { - 0x3F, 0x11, - SenseDevTypes039, - "MEDIUM AUXILIARY MEMORY ACCESSIBLE" - }, - { - 0x40, 0x00, - SenseDevTypes035, - "RAM FAILURE (SHOULD USE 40 NN)" - }, - { - 0x40, 0xFF, - SenseDevTypes001, - "DIAGNOSTIC FAILURE ON COMPONENT NN (80H-FFH)" - }, - { - 0x41, 0x00, - SenseDevTypes035, - "DATA PATH FAILURE (SHOULD USE 40 NN)" - }, - { - 0x42, 0x00, - SenseDevTypes035, - "POWER-ON OR SELF-TEST FAILURE (SHOULD USE 40 NN)" - }, - { - 0x43, 0x00, - SenseDevTypes001, - "MESSAGE ERROR" - }, - { - 0x44, 0x00, - SenseDevTypes001, - "INTERNAL TARGET FAILURE" - }, - { - 0x45, 0x00, - SenseDevTypes001, - "SELECT OR RESELECT FAILURE" - }, - { - 0x46, 0x00, - SenseDevTypes049, - "UNSUCCESSFUL SOFT RESET" - }, - { - 0x47, 0x00, - SenseDevTypes001, - "SCSI PARITY ERROR" - }, - { - 0x47, 0x01, - SenseDevTypes001, - "DATA PHASE CRC ERROR DETECTED" - }, - { - 0x47, 0x02, - SenseDevTypes001, - "SCSI PARITY ERROR DETECTED DURING ST DATA PHASE" - }, - { - 0x47, 0x03, - SenseDevTypes001, - "INFORMATION UNIT CRC ERROR DETECTED" - }, - { - 0x47, 0x04, - SenseDevTypes001, - "ASYNCHRONOUS INFORMATION PROTECTION ERROR DETECTED" - }, - { - 0x48, 0x00, - SenseDevTypes001, - "INITIATOR DETECTED ERROR MESSAGE RECEIVED" - }, - { - 0x49, 0x00, - SenseDevTypes001, - "INVALID MESSAGE ERROR" - }, - { - 0x4A, 0x00, - SenseDevTypes001, - "COMMAND PHASE ERROR" - }, - { - 0x4B, 0x00, - SenseDevTypes001, - "DATA PHASE ERROR" - }, - { - 0x4C, 0x00, - SenseDevTypes001, - "LOGICAL UNIT FAILED SELF-CONFIGURATION" - }, - { - 0x4D, 0xFF, - SenseDevTypes001, - "TAGGED OVERLAPPED COMMANDS (NN = QUEUE TAG)" - }, - { - 0x4E, 0x00, - SenseDevTypes001, - "OVERLAPPED COMMANDS ATTEMPTED" - }, - { - 0x50, 0x00, - SenseDevTypes002, - "WRITE APPEND ERROR" - }, - { - 0x50, 0x01, - SenseDevTypes002, - "WRITE APPEND POSITION ERROR" - }, - { - 0x50, 0x02, - SenseDevTypes002, - "POSITION ERROR RELATED TO TIMING" - }, - { - 0x51, 0x00, - SenseDevTypes052, - "ERASE FAILURE" - }, - { - 0x52, 0x00, - SenseDevTypes002, - "CARTRIDGE FAULT" - }, - { - 0x53, 0x00, - SenseDevTypes014, - "MEDIA LOAD OR EJECT FAILED" - }, - { - 0x53, 0x01, - SenseDevTypes002, - "UNLOAD TAPE FAILURE" - }, - { - 0x53, 0x02, - SenseDevTypes034, - "MEDIUM REMOVAL PREVENTED" - }, - { - 0x54, 0x00, - SenseDevTypes053, - "SCSI TO HOST SYSTEM INTERFACE FAILURE" - }, - { - 0x55, 0x00, - SenseDevTypes053, - "SYSTEM RESOURCE FAILURE" - }, - { - 0x55, 0x01, - SenseDevTypes033, - "SYSTEM BUFFER FULL" - }, - { - 0x55, 0x02, - SenseDevTypes054, - "INSUFFICIENT RESERVATION RESOURCES" - }, - { - 0x55, 0x03, - SenseDevTypes041, - "INSUFFICIENT RESOURCES" - }, - { - 0x55, 0x04, - SenseDevTypes055, - "INSUFFICIENT REGISTRATION RESOURCES" - }, - { - 0x55, 0x05, - SenseDevTypes012, - "access controls code 4 (99-314) [proposed]" - }, - { - 0x55, 0x06, - SenseDevTypes012, - "auxiliary memory code 1 (99-148) [proposed]" - }, - { - 0x57, 0x00, - SenseDevTypes005, - "UNABLE TO RECOVER TABLE-OF-CONTENTS" - }, - { - 0x58, 0x00, - SenseDevTypes056, - "GENERATION DOES NOT EXIST" - }, - { - 0x59, 0x00, - SenseDevTypes056, - "UPDATED BLOCK READ" - }, - { - 0x5A, 0x00, - SenseDevTypes057, - "OPERATOR REQUEST OR STATE CHANGE INPUT" - }, - { - 0x5A, 0x01, - SenseDevTypes034, - "OPERATOR MEDIUM REMOVAL REQUEST" - }, - { - 0x5A, 0x02, - SenseDevTypes058, - "OPERATOR SELECTED WRITE PROTECT" - }, - { - 0x5A, 0x03, - SenseDevTypes058, - "OPERATOR SELECTED WRITE PERMIT" - }, - { - 0x5B, 0x00, - SenseDevTypes059, - "LOG EXCEPTION" - }, - { - 0x5B, 0x01, - SenseDevTypes059, - "THRESHOLD CONDITION MET" - }, - { - 0x5B, 0x02, - SenseDevTypes059, - "LOG COUNTER AT MAXIMUM" - }, - { - 0x5B, 0x03, - SenseDevTypes059, - "LOG LIST CODES EXHAUSTED" - }, - { - 0x5C, 0x00, - SenseDevTypes060, - "RPL STATUS CHANGE" - }, - { - 0x5C, 0x01, - SenseDevTypes060, - "SPINDLES SYNCHRONIZED" - }, - { - 0x5C, 0x02, - SenseDevTypes060, - "SPINDLES NOT SYNCHRONIZED" - }, - { - 0x5D, 0x00, - SenseDevTypes001, - "FAILURE PREDICTION THRESHOLD EXCEEDED" - }, - { - 0x5D, 0x01, - SenseDevTypes061, - "MEDIA FAILURE PREDICTION THRESHOLD EXCEEDED" - }, - { - 0x5D, 0x02, - SenseDevTypes005, - "LOGICAL UNIT FAILURE PREDICTION THRESHOLD EXCEEDED" - }, - { - 0x5D, 0x10, - SenseDevTypes062, - "HARDWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE" - }, - { - 0x5D, 0x11, - SenseDevTypes062, - "HARDWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH" - }, - { - 0x5D, 0x12, - SenseDevTypes062, - "HARDWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH" - }, - { - 0x5D, 0x13, - SenseDevTypes062, - "HARDWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH" - }, - { - 0x5D, 0x14, - SenseDevTypes062, - "HARDWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS" - }, - { - 0x5D, 0x15, - SenseDevTypes062, - "HARDWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH" - }, - { - 0x5D, 0x16, - SenseDevTypes062, - "HARDWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH" - }, - { - 0x5D, 0x17, - SenseDevTypes062, - "HARDWARE IMPENDING FAILURE CHANNEL PARAMETRICS" - }, - { - 0x5D, 0x18, - SenseDevTypes062, - "HARDWARE IMPENDING FAILURE CONTROLLER DETECTED" - }, - { - 0x5D, 0x19, - SenseDevTypes062, - "HARDWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE" - }, - { - 0x5D, 0x1A, - SenseDevTypes062, - "HARDWARE IMPENDING FAILURE SEEK TIME PERFORMANCE" - }, - { - 0x5D, 0x1B, - SenseDevTypes062, - "HARDWARE IMPENDING FAILURE SPIN-UP RETRY COUNT" - }, - { - 0x5D, 0x1C, - SenseDevTypes062, - "HARDWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT" - }, - { - 0x5D, 0x20, - SenseDevTypes062, - "CONTROLLER IMPENDING FAILURE GENERAL HARD DRIVE FAILURE" - }, - { - 0x5D, 0x21, - SenseDevTypes062, - "CONTROLLER IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH" - }, - { - 0x5D, 0x22, - SenseDevTypes062, - "CONTROLLER IMPENDING FAILURE DATA ERROR RATE TOO HIGH" - }, - { - 0x5D, 0x23, - SenseDevTypes062, - "CONTROLLER IMPENDING FAILURE SEEK ERROR RATE TOO HIGH" - }, - { - 0x5D, 0x24, - SenseDevTypes062, - "CONTROLLER IMPENDING FAILURE TOO MANY BLOCK REASSIGNS" - }, - { - 0x5D, 0x25, - SenseDevTypes062, - "CONTROLLER IMPENDING FAILURE ACCESS TIMES TOO HIGH" - }, - { - 0x5D, 0x26, - SenseDevTypes062, - "CONTROLLER IMPENDING FAILURE START UNIT TIMES TOO HIGH" - }, - { - 0x5D, 0x27, - SenseDevTypes062, - "CONTROLLER IMPENDING FAILURE CHANNEL PARAMETRICS" - }, - { - 0x5D, 0x28, - SenseDevTypes062, - "CONTROLLER IMPENDING FAILURE CONTROLLER DETECTED" - }, - { - 0x5D, 0x29, - SenseDevTypes062, - "CONTROLLER IMPENDING FAILURE THROUGHPUT PERFORMANCE" - }, - { - 0x5D, 0x2A, - SenseDevTypes062, - "CONTROLLER IMPENDING FAILURE SEEK TIME PERFORMANCE" - }, - { - 0x5D, 0x2B, - SenseDevTypes062, - "CONTROLLER IMPENDING FAILURE SPIN-UP RETRY COUNT" - }, - { - 0x5D, 0x2C, - SenseDevTypes062, - "CONTROLLER IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT" - }, - { - 0x5D, 0x30, - SenseDevTypes062, - "DATA CHANNEL IMPENDING FAILURE GENERAL HARD DRIVE FAILURE" - }, - { - 0x5D, 0x31, - SenseDevTypes062, - "DATA CHANNEL IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH" - }, - { - 0x5D, 0x32, - SenseDevTypes062, - "DATA CHANNEL IMPENDING FAILURE DATA ERROR RATE TOO HIGH" - }, - { - 0x5D, 0x33, - SenseDevTypes062, - "DATA CHANNEL IMPENDING FAILURE SEEK ERROR RATE TOO HIGH" - }, - { - 0x5D, 0x34, - SenseDevTypes062, - "DATA CHANNEL IMPENDING FAILURE TOO MANY BLOCK REASSIGNS" - }, - { - 0x5D, 0x35, - SenseDevTypes062, - "DATA CHANNEL IMPENDING FAILURE ACCESS TIMES TOO HIGH" - }, - { - 0x5D, 0x36, - SenseDevTypes062, - "DATA CHANNEL IMPENDING FAILURE START UNIT TIMES TOO HIGH" - }, - { - 0x5D, 0x37, - SenseDevTypes062, - "DATA CHANNEL IMPENDING FAILURE CHANNEL PARAMETRICS" - }, - { - 0x5D, 0x38, - SenseDevTypes062, - "DATA CHANNEL IMPENDING FAILURE CONTROLLER DETECTED" - }, - { - 0x5D, 0x39, - SenseDevTypes062, - "DATA CHANNEL IMPENDING FAILURE THROUGHPUT PERFORMANCE" - }, - { - 0x5D, 0x3A, - SenseDevTypes062, - "DATA CHANNEL IMPENDING FAILURE SEEK TIME PERFORMANCE" - }, - { - 0x5D, 0x3B, - SenseDevTypes062, - "DATA CHANNEL IMPENDING FAILURE SPIN-UP RETRY COUNT" - }, - { - 0x5D, 0x3C, - SenseDevTypes062, - "DATA CHANNEL IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT" - }, - { - 0x5D, 0x40, - SenseDevTypes062, - "SERVO IMPENDING FAILURE GENERAL HARD DRIVE FAILURE" - }, - { - 0x5D, 0x41, - SenseDevTypes062, - "SERVO IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH" - }, - { - 0x5D, 0x42, - SenseDevTypes062, - "SERVO IMPENDING FAILURE DATA ERROR RATE TOO HIGH" - }, - { - 0x5D, 0x43, - SenseDevTypes062, - "SERVO IMPENDING FAILURE SEEK ERROR RATE TOO HIGH" - }, - { - 0x5D, 0x44, - SenseDevTypes062, - "SERVO IMPENDING FAILURE TOO MANY BLOCK REASSIGNS" - }, - { - 0x5D, 0x45, - SenseDevTypes062, - "SERVO IMPENDING FAILURE ACCESS TIMES TOO HIGH" - }, - { - 0x5D, 0x46, - SenseDevTypes062, - "SERVO IMPENDING FAILURE START UNIT TIMES TOO HIGH" - }, - { - 0x5D, 0x47, - SenseDevTypes062, - "SERVO IMPENDING FAILURE CHANNEL PARAMETRICS" - }, - { - 0x5D, 0x48, - SenseDevTypes062, - "SERVO IMPENDING FAILURE CONTROLLER DETECTED" - }, - { - 0x5D, 0x49, - SenseDevTypes062, - "SERVO IMPENDING FAILURE THROUGHPUT PERFORMANCE" - }, - { - 0x5D, 0x4A, - SenseDevTypes062, - "SERVO IMPENDING FAILURE SEEK TIME PERFORMANCE" - }, - { - 0x5D, 0x4B, - SenseDevTypes062, - "SERVO IMPENDING FAILURE SPIN-UP RETRY COUNT" - }, - { - 0x5D, 0x4C, - SenseDevTypes062, - "SERVO IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT" - }, - { - 0x5D, 0x50, - SenseDevTypes062, - "SPINDLE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE" - }, - { - 0x5D, 0x51, - SenseDevTypes062, - "SPINDLE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH" - }, - { - 0x5D, 0x52, - SenseDevTypes062, - "SPINDLE IMPENDING FAILURE DATA ERROR RATE TOO HIGH" - }, - { - 0x5D, 0x53, - SenseDevTypes062, - "SPINDLE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH" - }, - { - 0x5D, 0x54, - SenseDevTypes062, - "SPINDLE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS" - }, - { - 0x5D, 0x55, - SenseDevTypes062, - "SPINDLE IMPENDING FAILURE ACCESS TIMES TOO HIGH" - }, - { - 0x5D, 0x56, - SenseDevTypes062, - "SPINDLE IMPENDING FAILURE START UNIT TIMES TOO HIGH" - }, - { - 0x5D, 0x57, - SenseDevTypes062, - "SPINDLE IMPENDING FAILURE CHANNEL PARAMETRICS" - }, - { - 0x5D, 0x58, - SenseDevTypes062, - "SPINDLE IMPENDING FAILURE CONTROLLER DETECTED" - }, - { - 0x5D, 0x59, - SenseDevTypes062, - "SPINDLE IMPENDING FAILURE THROUGHPUT PERFORMANCE" - }, - { - 0x5D, 0x5A, - SenseDevTypes062, - "SPINDLE IMPENDING FAILURE SEEK TIME PERFORMANCE" - }, - { - 0x5D, 0x5B, - SenseDevTypes062, - "SPINDLE IMPENDING FAILURE SPIN-UP RETRY COUNT" - }, - { - 0x5D, 0x5C, - SenseDevTypes062, - "SPINDLE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT" - }, - { - 0x5D, 0x60, - SenseDevTypes062, - "FIRMWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE" - }, - { - 0x5D, 0x61, - SenseDevTypes062, - "FIRMWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH" - }, - { - 0x5D, 0x62, - SenseDevTypes062, - "FIRMWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH" - }, - { - 0x5D, 0x63, - SenseDevTypes062, - "FIRMWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH" - }, - { - 0x5D, 0x64, - SenseDevTypes062, - "FIRMWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS" - }, - { - 0x5D, 0x65, - SenseDevTypes062, - "FIRMWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH" - }, - { - 0x5D, 0x66, - SenseDevTypes062, - "FIRMWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH" - }, - { - 0x5D, 0x67, - SenseDevTypes062, - "FIRMWARE IMPENDING FAILURE CHANNEL PARAMETRICS" - }, - { - 0x5D, 0x68, - SenseDevTypes062, - "FIRMWARE IMPENDING FAILURE CONTROLLER DETECTED" - }, - { - 0x5D, 0x69, - SenseDevTypes062, - "FIRMWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE" - }, - { - 0x5D, 0x6A, - SenseDevTypes062, - "FIRMWARE IMPENDING FAILURE SEEK TIME PERFORMANCE" - }, - { - 0x5D, 0x6B, - SenseDevTypes062, - "FIRMWARE IMPENDING FAILURE SPIN-UP RETRY COUNT" - }, - { - 0x5D, 0x6C, - SenseDevTypes062, - "FIRMWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT" - }, - { - 0x5D, 0xFF, - SenseDevTypes001, - "FAILURE PREDICTION THRESHOLD EXCEEDED (FALSE)" - }, - { - 0x5E, 0x00, - SenseDevTypes044, - "LOW POWER CONDITION ON" - }, - { - 0x5E, 0x01, - SenseDevTypes044, - "IDLE CONDITION ACTIVATED BY TIMER" - }, - { - 0x5E, 0x02, - SenseDevTypes044, - "STANDBY CONDITION ACTIVATED BY TIMER" - }, - { - 0x5E, 0x03, - SenseDevTypes044, - "IDLE CONDITION ACTIVATED BY COMMAND" - }, - { - 0x5E, 0x04, - SenseDevTypes044, - "STANDBY CONDITION ACTIVATED BY COMMAND" - }, - { - 0x5E, 0x41, - SenseDevTypes043, - "POWER STATE CHANGE TO ACTIVE" - }, - { - 0x5E, 0x42, - SenseDevTypes043, - "POWER STATE CHANGE TO IDLE" - }, - { - 0x5E, 0x43, - SenseDevTypes043, - "POWER STATE CHANGE TO STANDBY" - }, - { - 0x5E, 0x45, - SenseDevTypes043, - "POWER STATE CHANGE TO SLEEP" - }, - { - 0x5E, 0x47, - SenseDevTypes063, - "POWER STATE CHANGE TO DEVICE CONTROL" - }, - { - 0x60, 0x00, - SenseDevTypes042, - "LAMP FAILURE" - }, - { - 0x61, 0x00, - SenseDevTypes042, - "VIDEO ACQUISITION ERROR" - }, - { - 0x61, 0x01, - SenseDevTypes042, - "UNABLE TO ACQUIRE VIDEO" - }, - { - 0x61, 0x02, - SenseDevTypes042, - "OUT OF FOCUS" - }, - { - 0x62, 0x00, - SenseDevTypes042, - "SCAN HEAD POSITIONING ERROR" - }, - { - 0x63, 0x00, - SenseDevTypes005, - "END OF USER AREA ENCOUNTERED ON THIS TRACK" - }, - { - 0x63, 0x01, - SenseDevTypes005, - "PACKET DOES NOT FIT IN AVAILABLE SPACE" - }, - { - 0x64, 0x00, - SenseDevTypes005, - "ILLEGAL MODE FOR THIS TRACK" - }, - { - 0x64, 0x01, - SenseDevTypes005, - "INVALID PACKET SIZE" - }, - { - 0x65, 0x00, - SenseDevTypes001, - "VOLTAGE FAULT" - }, - { - 0x66, 0x00, - SenseDevTypes042, - "AUTOMATIC DOCUMENT FEEDER COVER UP" - }, - { - 0x66, 0x01, - SenseDevTypes042, - "AUTOMATIC DOCUMENT FEEDER LIFT UP" - }, - { - 0x66, 0x02, - SenseDevTypes042, - "DOCUMENT JAM IN AUTOMATIC DOCUMENT FEEDER" - }, - { - 0x66, 0x03, - SenseDevTypes042, - "DOCUMENT MISS FEED AUTOMATIC IN DOCUMENT FEEDER" - }, - { - 0x67, 0x00, - SenseDevTypes064, - "CONFIGURATION FAILURE" - }, - { - 0x67, 0x01, - SenseDevTypes064, - "CONFIGURATION OF INCAPABLE LOGICAL UNITS FAILED" - }, - { - 0x67, 0x02, - SenseDevTypes064, - "ADD LOGICAL UNIT FAILED" - }, - { - 0x67, 0x03, - SenseDevTypes064, - "MODIFICATION OF LOGICAL UNIT FAILED" - }, - { - 0x67, 0x04, - SenseDevTypes064, - "EXCHANGE OF LOGICAL UNIT FAILED" - }, - { - 0x67, 0x05, - SenseDevTypes064, - "REMOVE OF LOGICAL UNIT FAILED" - }, - { - 0x67, 0x06, - SenseDevTypes064, - "ATTACHMENT OF LOGICAL UNIT FAILED" - }, - { - 0x67, 0x07, - SenseDevTypes064, - "CREATION OF LOGICAL UNIT FAILED" - }, - { - 0x67, 0x08, - SenseDevTypes064, - "ASSIGN FAILURE OCCURRED" - }, - { - 0x67, 0x09, - SenseDevTypes064, - "MULTIPLY ASSIGNED LOGICAL UNIT" - }, - { - 0x68, 0x00, - SenseDevTypes064, - "LOGICAL UNIT NOT CONFIGURED" - }, - { - 0x69, 0x00, - SenseDevTypes064, - "DATA LOSS ON LOGICAL UNIT" - }, - { - 0x69, 0x01, - SenseDevTypes064, - "MULTIPLE LOGICAL UNIT FAILURES" - }, - { - 0x69, 0x02, - SenseDevTypes064, - "PARITY/DATA MISMATCH" - }, - { - 0x6A, 0x00, - SenseDevTypes064, - "INFORMATIONAL, REFER TO LOG" - }, - { - 0x6B, 0x00, - SenseDevTypes064, - "STATE CHANGE HAS OCCURRED" - }, - { - 0x6B, 0x01, - SenseDevTypes064, - "REDUNDANCY LEVEL GOT BETTER" - }, - { - 0x6B, 0x02, - SenseDevTypes064, - "REDUNDANCY LEVEL GOT WORSE" - }, - { - 0x6C, 0x00, - SenseDevTypes064, - "REBUILD FAILURE OCCURRED" - }, - { - 0x6D, 0x00, - SenseDevTypes064, - "RECALCULATE FAILURE OCCURRED" - }, - { - 0x6E, 0x00, - SenseDevTypes064, - "COMMAND TO LOGICAL UNIT FAILED" - }, - { - 0x6F, 0x00, - SenseDevTypes005, - "COPY PROTECTION KEY EXCHANGE FAILURE - AUTHENTICATION FAILURE" - }, - { - 0x6F, 0x01, - SenseDevTypes005, - "COPY PROTECTION KEY EXCHANGE FAILURE - KEY NOT PRESENT" - }, - { - 0x6F, 0x02, - SenseDevTypes005, - "COPY PROTECTION KEY EXCHANGE FAILURE - KEY NOT ESTABLISHED" - }, - { - 0x6F, 0x03, - SenseDevTypes005, - "READ OF SCRAMBLED SECTOR WITHOUT AUTHENTICATION" - }, - { - 0x6F, 0x04, - SenseDevTypes005, - "MEDIA REGION CODE IS MISMATCHED TO LOGICAL UNIT REGION" - }, - { - 0x6F, 0x05, - SenseDevTypes005, - "DRIVE REGION MUST BE PERMANENT/REGION RESET COUNT ERROR" - }, - { - 0x70, 0xFF, - SenseDevTypes002, - "DECOMPRESSION EXCEPTION SHORT ALGORITHM ID OF NN" - }, - { - 0x71, 0x00, - SenseDevTypes002, - "DECOMPRESSION EXCEPTION LONG ALGORITHM ID" - }, - { - 0x72, 0x00, - SenseDevTypes005, - "SESSION FIXATION ERROR" - }, - { - 0x72, 0x01, - SenseDevTypes005, - "SESSION FIXATION ERROR WRITING LEAD-IN" - }, - { - 0x72, 0x02, - SenseDevTypes005, - "SESSION FIXATION ERROR WRITING LEAD-OUT" - }, - { - 0x72, 0x03, - SenseDevTypes005, - "SESSION FIXATION ERROR - INCOMPLETE TRACK IN SESSION" - }, - { - 0x72, 0x04, - SenseDevTypes005, - "EMPTY OR PARTIALLY WRITTEN RESERVED TRACK" - }, - { - 0x72, 0x05, - SenseDevTypes005, - "NO MORE TRACK RESERVATIONS ALLOWED" - }, - { - 0x73, 0x00, - SenseDevTypes005, - "CD CONTROL ERROR" - }, - { - 0x73, 0x01, - SenseDevTypes005, - "POWER CALIBRATION AREA ALMOST FULL" - }, - { - 0x73, 0x02, - SenseDevTypes005, - "POWER CALIBRATION AREA IS FULL" - }, - { - 0x73, 0x03, - SenseDevTypes005, - "POWER CALIBRATION AREA ERROR" - }, - { - 0x73, 0x04, - SenseDevTypes005, - "PROGRAM MEMORY AREA UPDATE FAILURE" - }, - { - 0x73, 0x05, - SenseDevTypes005, - "PROGRAM MEMORY AREA IS FULL" - }, - { - 0x73, 0x06, - SenseDevTypes005, - "RMA/PMA IS FULL" - }, -}; - -static int ASCQ_TableSize = 463; - - -#endif --- linux-2.6.9-rc1/drivers/message/fusion/ascq_tbl.sh 2004-08-24 00:02:48.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,109 +0,0 @@ -#!/bin/sh -# -# ascq_tbl.sh - Translate SCSI t10.org's "asc-num.txt" file of -# SCSI Additional Sense Code & Qualifiers (ASC/ASCQ's) -# into something useful in C, creating "ascq_tbl.c" file. -# -#*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*# - -PREF_INFILE="t10.org/asc-num.txt" # From SCSI t10.org -PREF_OUTFILE="ascq_tbl.c" - -#*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*# - -xlate_ascq() { - cat | awk ' - BEGIN { - DQ = "\042"; - OUTFILE = "'"${PREF_OUTFILE}"'"; - TRUE = 1; - FALSE = 0; - #debug = TRUE; - - # read and discard all lines up to and including the one that begins - # with the "magic token" of "------- -------------- ---"... - headers_gone = FALSE; - while (!headers_gone) { - if (getline <= 0) - exit 1; - header_line[++hdrs] = $0; - if (debug) - printf("header_line[%d] = :%s:\n", ++hdrs, $0); - if ($0 ~ /^------- -------------- ---/) { - headers_gone = TRUE; - } - } - outcount = 0; - } - - (NF > 1) { - ++outcount; - if (debug) - printf( "DBG: %s\n", $0 ); - ASC[outcount] = substr($0,1,2); - ASCQ[outcount] = substr($0,5,2); - devtypes = substr($0,10,14); - gsub(/ /, ".", devtypes); - DESCRIP[outcount] = substr($0,26); - - if (!(devtypes in DevTypesVoodoo)) { - DevTypesVoodoo[devtypes] = ++voodoo; - DevTypesIdx[voodoo] = devtypes; - } - DEVTYPES[outcount] = DevTypesVoodoo[devtypes]; - - # Handle 0xNN exception stuff... - if (ASCQ[outcount] == "NN" || ASCQ[outcount] == "nn") - ASCQ[outcount] = "FF"; - } - - END { - printf("#ifndef SCSI_ASCQ_TBL_C_INCLUDED\n") > OUTFILE; - printf("#define SCSI_ASCQ_TBL_C_INCLUDED\n") >> OUTFILE; - - printf("\n/* AuToMaGiCaLlY generated from: %s'"${FIN}"'%s\n", DQ, DQ) >> OUTFILE; - printf(" *******************************************************************************\n") >> OUTFILE; - for (i=1; i<=hdrs; i++) { - printf(" * %s\n", header_line[i]) >> OUTFILE; - } - printf(" */\n") >> OUTFILE; - - printf("\n") >> OUTFILE; - for (i=1; i<=voodoo; i++) { - printf("static char SenseDevTypes%03d[] = %s%s%s;\n", i, DQ, DevTypesIdx[i], DQ) >> OUTFILE; - } - - printf("\nstatic ASCQ_Table_t ASCQ_Table[] = {\n") >> OUTFILE; - for (i=1; i<=outcount; i++) { - printf(" {\n") >> OUTFILE; - printf(" 0x%s, 0x%s,\n", ASC[i], ASCQ[i]) >> OUTFILE; - printf(" SenseDevTypes%03d,\n", DEVTYPES[i]) >> OUTFILE; - printf(" %s%s%s\n", DQ, DESCRIP[i], DQ) >> OUTFILE; - printf(" },\n") >> OUTFILE; - } - printf( "};\n\n" ) >> OUTFILE; - - printf( "static int ASCQ_TableSize = %d;\n\n", outcount ) >> OUTFILE; - printf( "Total of %d ASC/ASCQ records generated\n", outcount ); - printf("\n#endif\n") >> OUTFILE; - close(OUTFILE); - }' - return -} - -#*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*# - -# main() -if [ $# -lt 1 ]; then - echo "INFO: No input filename supplied - using: $PREF_INFILE" >&2 - FIN=$PREF_INFILE -else - FIN="$1" - if [ "$FIN" != "$PREF_INFILE" ]; then - echo "INFO: Ok, I'll try chewing on '$FIN' for SCSI ASC/ASCQ combos..." >&2 - fi - shift -fi - -cat $FIN | xlate_ascq -exit 0 --- linux-2.6.9-rc1/drivers/message/fusion/isense.c 2004-08-24 00:02:47.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,119 +0,0 @@ -/* - * linux/drivers/message/fusion/isense.c - * Little linux driver / shim that interfaces with the Fusion MPT - * Linux base driver to provide english readable strings in SCSI - * Error Report logging output. This module implements SCSI-3 - * Opcode lookup and a sorted table of SCSI-3 ASC/ASCQ strings. - * - * Copyright (c) 1991-2004 Steven J. Ralston - * Written By: Steven J. Ralston - * (yes I wrote some of the orig. code back in 1991!) - * (mailto:sjralston1@netscape.net) - * (mailto:mpt_linux_developer@lsil.com) - * - * $Id: isense.c,v 1.33 2002/02/27 18:44:19 sralston Exp $ - */ -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - NO WARRANTY - THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT - LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, - MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is - solely responsible for determining the appropriateness of using and - distributing the Program and assumes all risks associated with its - exercise of rights under this Agreement, including but not limited to - the risks and costs of program errors, damage to or loss of data, - programs or equipment, and unavailability or interruption of operations. - - DISCLAIMER OF LIABILITY - NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED - HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ - -#include -#include -#include -#include -#include -#include - -#define MODULEAUTHOR "Steven J. Ralston" -#define COPYRIGHT "Copyright (c) 2001-2004 " MODULEAUTHOR -#include "mptbase.h" - -#include "isense.h" - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* - * Private data... - */ - -/* - * YIKES! I don't usually #include C source files, but.. - * The following #include's pulls in our needed ASCQ_Table[] array, - * ASCQ_TableSz integer, and ScsiOpcodeString[] array! - */ -#include "ascq_tbl.c" -#include "scsiops.c" - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -#define my_NAME "SCSI-3 Opcodes & ASC/ASCQ Strings" -#define my_VERSION MPT_LINUX_VERSION_COMMON -#define MYNAM "isense" - -MODULE_AUTHOR(MODULEAUTHOR); -MODULE_DESCRIPTION(my_NAME); -MODULE_LICENSE("GPL"); - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -int __init isense_init(void) -{ - show_mptmod_ver(my_NAME, my_VERSION); - - /* - * Install our handler - */ - if (mpt_register_ascqops_strings(&ASCQ_Table[0], ASCQ_TableSize, ScsiOpcodeString) != 1) - { - printk(KERN_ERR MYNAM ": ERROR: Can't register with Fusion MPT base driver!\n"); - return -EBUSY; - } - printk(KERN_INFO MYNAM ": Registered SCSI-3 Opcodes & ASC/ASCQ Strings\n"); - return 0; -} - - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -static void isense_exit(void) -{ -#ifdef MODULE - mpt_deregister_ascqops_strings(); -#endif - printk(KERN_INFO MYNAM ": Deregistered SCSI-3 Opcodes & ASC/ASCQ Strings\n"); -} - -module_init(isense_init); -module_exit(isense_exit); - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ - --- linux-2.6.9-rc1/drivers/message/fusion/isense.h 2004-08-24 00:02:58.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,95 +0,0 @@ -#ifndef ISENSE_H_INCLUDED -#define ISENSE_H_INCLUDED -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ - -#ifdef __KERNEL__ -#include /* needed for u8, etc. */ -#include /* needed for strcat */ -#include /* needed for sprintf */ -#else - #ifndef U_STUFF_DEFINED - #define U_STUFF_DEFINED - typedef unsigned char u8; - typedef unsigned short u16; - typedef unsigned int u32; - #endif -#endif - -#include "scsi3.h" /* needed for all things SCSI */ - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* - * Defines and typedefs... - */ - -#ifdef __KERNEL__ -#define PrintF(x) printk x -#else -#define PrintF(x) printf x -#endif - -#ifndef TRUE -#define TRUE 1 -#define FALSE 0 -#endif - -#define RETRY_STATUS ((int) 1) -#define PUT_STATUS ((int) 0) - -/* - * A generic structure to hold info about IO request that caused - * a Request Sense to be performed, and the resulting Sense Data. - */ -typedef struct IO_Info -{ - char *DevIDStr; /* String of chars which identifies the device. */ - u8 *cdbPtr; /* Pointer (Virtual/Logical addr) to CDB bytes of - IO request that caused ContAllegianceCond. */ - u8 *sensePtr; /* Pointer (Virtual/Logical addr) to Sense Data - returned by Request Sense operation. */ - u8 *dataPtr; /* Pointer (Virtual/Logical addr) to Data buffer - of IO request caused ContAllegianceCondition. */ - u8 *inqPtr; /* Pointer (Virtual/Logical addr) to Inquiry Data for - IO *Device* that caused ContAllegianceCondition. */ - u8 SCSIStatus; /* SCSI status byte of IO request that caused - Contingent Allegiance Condition. */ - u8 DoDisplay; /* Shall we display any messages? */ - u16 rsvd_align1; - u32 ComplCode; /* Four-byte OS-dependent completion code. */ - u32 NotifyL; /* Four-byte OS-dependent notification field. */ -} IO_Info_t; - -/* - * SCSI Additional Sense Code and Additional Sense Code Qualifier table. - */ -typedef struct ASCQ_Table -{ - u8 ASC; - u8 ASCQ; - char *DevTypes; - char *Description; -} ASCQ_Table_t; - -#if 0 -/* - * SCSI Opcodes table. - */ -typedef struct SCSI_OPS_Table -{ - u8 OpCode; - char *DevTypes; - char *ScsiCmndStr; -} SCSI_OPS_Table_t; -#endif - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* - * Public entry point prototypes - */ - -/* in scsiherr.c, needed by mptscsih.c */ -extern int mpt_ScsiHost_ErrorReport(IO_Info_t *ioop); - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -#endif - --- linux-2.6.9-rc1/drivers/message/fusion/Kconfig 2004-08-24 00:03:49.000000000 -0700 +++ 25/drivers/message/fusion/Kconfig 2004-09-02 20:51:51.000000000 -0700 @@ -27,39 +27,6 @@ config FUSION_MAX_SGE necessary (or recommended) unless the user will be running large I/O's via the raw interface. -config FUSION_ISENSE - tristate "Enhanced SCSI error reporting" - depends on MODULES && FUSION && m - ---help--- - The isense module (roughly stands for Interpret SENSE data) is - completely optional. It simply provides extra English readable - strings in SCSI Error Report(s) that might be generated from the - Fusion MPT SCSI Host driver, for example when a target device - returns a SCSI check condition on a I/O. Without this module - loaded you might see: - - SCSI Error Report =-=-= (ioc0,scsi5:0) - SCSI_Status=02h (CHECK_CONDITION) - Original_CDB[]: 2A 00 00 00 00 41 00 00 02 00 - SenseData[12h]: 70 00 02 00 00 00 00 0A 00 00 00 00 04 02 02 00 00 00 - SenseKey=2h (NOT READY); FRU=02h - ASC/ASCQ=29h/00h - - Where otherwise, if this module had been loaded, you would see: - - SCSI Error Report =-=-= (ioc0,scsi5:0) - SCSI_Status=02h (CHECK_CONDITION) - Original_CDB[]: 2A 00 00 00 00 41 00 00 02 00 - "WRITE(10)" - SenseData[12h]: 70 00 02 00 00 00 00 0A 00 00 00 00 04 02 02 00 00 00 - SenseKey=2h (NOT READY); FRU=02h - ASC/ASCQ=29h/00h "LOGICAL UNIT NOT READY, INITIALIZING CMD. REQUIRED" - - Say M for "Enhanced SCSI error reporting" to compile this optional module, - creating a driver named: isense. - - NOTE: Support for building this feature into the kernel is not - available, due to kernel size considerations. - config FUSION_CTL tristate "Fusion MPT misc device (ioctl) driver" depends on MODULES && FUSION && m --- linux-2.6.9-rc1/drivers/message/fusion/Makefile 2004-08-24 00:03:12.000000000 -0700 +++ 25/drivers/message/fusion/Makefile 2004-09-02 20:51:51.000000000 -0700 @@ -2,7 +2,7 @@ # Makefile for the LSI Logic Fusion MPT (Message Passing Technology) drivers. # # Note! If you want to turn on various debug defines for an extended period of -# time but don't want them lingering around in the Makefile when you pass it on +# time but don't want them lingering around in the Makefile when you pass it on # to someone else, use the MPT_CFLAGS env variable (thanks Steve). -nromer #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-{ LSI_LOGIC @@ -48,6 +48,5 @@ EXTRA_CFLAGS += ${MPT_CFLAGS} #=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-} LSI_LOGIC obj-$(CONFIG_FUSION) += mptbase.o mptscsih.o -obj-$(CONFIG_FUSION_ISENSE) += isense.o obj-$(CONFIG_FUSION_CTL) += mptctl.o obj-$(CONFIG_FUSION_LAN) += mptlan.o --- linux-2.6.9-rc1/drivers/message/fusion/mptbase.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/message/fusion/mptbase.c 2004-09-02 20:51:51.000000000 -0700 @@ -139,11 +139,6 @@ struct proc_dir_entry *mpt_proc_root_dir DmpServices_t *DmpService; -void *mpt_v_ASCQ_TablePtr; -const char **mpt_ScsiOpcodesPtr; -int mpt_ASCQ_TableSz; - - #define WHOINIT_UNKNOWN 0xAA /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -165,7 +160,6 @@ static struct mpt_pci_driver *MptDevice static int FusionInitCalled = 0; static int mpt_base_index = -1; static int last_drv_idx = -1; -static int isense_idx = -1; static DECLARE_WAIT_QUEUE_HEAD(mpt_waitq); @@ -177,8 +171,8 @@ static irqreturn_t mpt_interrupt(int irq static int mpt_base_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply); static int mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag); -static void mpt_detect_bound_ports(MPT_ADAPTER *this, struct pci_dev *pdev); -static void mpt_adapter_disable(MPT_ADAPTER *ioc, int freeup); +static void mpt_detect_bound_ports(MPT_ADAPTER *ioc, struct pci_dev *pdev); +static void mpt_adapter_disable(MPT_ADAPTER *ioc); static void mpt_adapter_dispose(MPT_ADAPTER *ioc); static void MptDisplayIocCapabilities(MPT_ADAPTER *ioc); @@ -291,7 +285,7 @@ mpt_interrupt(int irq, void *bus_id, str int type; int freeme; - ioc = bus_id; + ioc = (MPT_ADAPTER *)bus_id; /* * Drain the reply FIFO! @@ -333,8 +327,8 @@ mpt_interrupt(int irq, void *bus_id, str cb_idx = mr->u.frame.hwhdr.msgctxu.fld.cb_idx; mf = MPT_INDEX_2_MFPTR(ioc, req_idx); - dprintk((MYIOC_s_INFO_FMT "Got non-TURBO reply=%p\n", - ioc->name, mr)); + dmfprintk((MYIOC_s_INFO_FMT "Got non-TURBO reply=%p req_idx=%x\n", + ioc->name, mr, req_idx)); DBG_DUMP_REPLY_FRAME(mr) /* NEW! 20010301 -sralston @@ -358,7 +352,7 @@ mpt_interrupt(int irq, void *bus_id, str /* * Process turbo (context) reply... */ - dirqprintk((MYIOC_s_INFO_FMT "Got TURBO reply(=%08x)\n", ioc->name, pa)); + dmfprintk((MYIOC_s_INFO_FMT "Got TURBO reply req_idx=%08x\n", ioc->name, pa)); type = (pa >> MPI_CONTEXT_REPLY_TYPE_SHIFT); if (type == MPI_CONTEXT_REPLY_TYPE_SCSI_TARGET) { cb_idx = mpt_stm_index; @@ -506,7 +500,7 @@ mpt_base_reply(MPT_ADAPTER *ioc, MPT_FRA results = ProcessEventNotification(ioc, pEvReply, &evHandlers); if (results != evHandlers) { /* CHECKME! Any special handling needed here? */ - dprintk((MYIOC_s_WARN_FMT "Called %d event handlers, sum results = %d\n", + devtprintk((MYIOC_s_WARN_FMT "Called %d event handlers, sum results = %d\n", ioc->name, evHandlers, results)); } @@ -659,8 +653,6 @@ mpt_deregister(int cb_idx) MptEvHandlers[cb_idx] = NULL; last_drv_idx++; - if (isense_idx != -1 && isense_idx <= cb_idx) - isense_idx++; } } @@ -803,56 +795,60 @@ mpt_device_driver_deregister(int cb_idx) * mpt_get_msg_frame - Obtain a MPT request frame from the pool (of 1024) * allocated per MPT adapter. * @handle: Handle of registered MPT protocol driver - * @iocid: IOC unique identifier (integer) + * @ioc: Pointer to MPT adapter structure * * Returns pointer to a MPT request frame or %NULL if none are available * or IOC is not active. */ MPT_FRAME_HDR* -mpt_get_msg_frame(int handle, MPT_ADAPTER *iocp) +mpt_get_msg_frame(int handle, MPT_ADAPTER *ioc) { MPT_FRAME_HDR *mf; unsigned long flags; + u16 req_idx; /* Request index */ + + /* validate handle and ioc identifier */ #ifdef MFCNT - if (!iocp->active) + if (!ioc->active) printk(KERN_WARNING "IOC Not Active! mpt_get_msg_frame returning NULL!\n"); #endif /* If interrupts are not attached, do not return a request frame */ - if (!iocp->active) + if (!ioc->active) return NULL; - spin_lock_irqsave(&iocp->FreeQlock, flags); - if (! Q_IS_EMPTY(&iocp->FreeQ)) { + spin_lock_irqsave(&ioc->FreeQlock, flags); + if (! Q_IS_EMPTY(&ioc->FreeQ)) { int req_offset; - mf = iocp->FreeQ.head; + mf = ioc->FreeQ.head; Q_DEL_ITEM(&mf->u.frame.linkage); mf->u.frame.hwhdr.msgctxu.fld.cb_idx = handle; /* byte */ - req_offset = (u8 *)mf - (u8 *)iocp->req_frames; + req_offset = (u8 *)mf - (u8 *)ioc->req_frames; /* u16! */ - mf->u.frame.hwhdr.msgctxu.fld.req_idx = - cpu_to_le16(req_offset / iocp->req_sz); + req_idx = cpu_to_le16(req_offset / ioc->req_sz); + mf->u.frame.hwhdr.msgctxu.fld.req_idx = req_idx; mf->u.frame.hwhdr.msgctxu.fld.rsvd = 0; + ioc->RequestNB[req_idx] = ioc->NB_for_64_byte_frame; /* Default, will be changed if necessary in SG generation */ #ifdef MFCNT - iocp->mfcnt++; + ioc->mfcnt++; #endif } else mf = NULL; - spin_unlock_irqrestore(&iocp->FreeQlock, flags); + spin_unlock_irqrestore(&ioc->FreeQlock, flags); #ifdef MFCNT if (mf == NULL) - printk(KERN_WARNING "IOC Active. No free Msg Frames! Count 0x%x Max 0x%x\n", iocp->mfcnt, iocp->req_depth); + printk(KERN_WARNING "IOC Active. No free Msg Frames! Count 0x%x Max 0x%x\n", ioc->mfcnt, ioc->req_depth); mfcounter++; if (mfcounter == PRINT_MF_COUNT) - printk(KERN_INFO "MF Count 0x%x Max 0x%x \n", iocp->mfcnt, iocp->req_depth); + printk(KERN_INFO "MF Count 0x%x Max 0x%x \n", ioc->mfcnt, ioc->req_depth); #endif dmfprintk((KERN_INFO MYNAM ": %s: mpt_get_msg_frame(%d,%d), got mf=%p\n", - iocp->name, handle, iocid, mf)); + ioc->name, handle, ioc->id, mf)); return mf; } @@ -861,23 +857,25 @@ mpt_get_msg_frame(int handle, MPT_ADAPTE * mpt_put_msg_frame - Send a protocol specific MPT request frame * to a IOC. * @handle: Handle of registered MPT protocol driver - * @iocid: IOC unique identifier (integer) + * @ioc: Pointer to MPT adapter structure * @mf: Pointer to MPT request frame * * This routine posts a MPT request frame to the request post FIFO of a * specific MPT adapter. */ void -mpt_put_msg_frame(int handle, MPT_ADAPTER *iocp, MPT_FRAME_HDR *mf) +mpt_put_msg_frame(int handle, MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf) { u32 mf_dma_addr; int req_offset; + u16 req_idx; /* Request index */ /* ensure values are reset properly! */ mf->u.frame.hwhdr.msgctxu.fld.cb_idx = handle; /* byte */ - req_offset = (u8 *)mf - (u8 *)iocp->req_frames; - /* u16! */ - mf->u.frame.hwhdr.msgctxu.fld.req_idx = cpu_to_le16(req_offset / iocp->req_sz); + req_offset = (u8 *)mf - (u8 *)ioc->req_frames; + /* u16! */ + req_idx = cpu_to_le16(req_offset / ioc->req_sz); + mf->u.frame.hwhdr.msgctxu.fld.req_idx = req_idx; mf->u.frame.hwhdr.msgctxu.fld.rsvd = 0; #ifdef MPT_DEBUG_MSG_FRAME @@ -886,8 +884,8 @@ mpt_put_msg_frame(int handle, MPT_ADAPTE int ii, n; printk(KERN_INFO MYNAM ": %s: About to Put msg frame @ %p:\n" KERN_INFO " ", - iocp->name, m); - n = iocp->req_sz/4 - 1; + ioc->name, m); + n = ioc->req_sz/4 - 1; while (m[n] == 0) n--; for (ii=0; ii<=n; ii++) { @@ -899,32 +897,33 @@ mpt_put_msg_frame(int handle, MPT_ADAPTE } #endif - mf_dma_addr = iocp->req_frames_low_dma + req_offset; - CHIPREG_WRITE32(&iocp->chip->RequestFifo, mf_dma_addr); + mf_dma_addr = (ioc->req_frames_low_dma + req_offset) | ioc->RequestNB[req_idx]; + dsgprintk((MYIOC_s_INFO_FMT "mf_dma_addr=%x req_idx=%d RequestNB=%x\n", ioc->name, mf_dma_addr, req_idx, ioc->RequestNB[req_idx])); + CHIPREG_WRITE32(&ioc->chip->RequestFifo, mf_dma_addr); } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /** * mpt_free_msg_frame - Place MPT request frame back on FreeQ. * @handle: Handle of registered MPT protocol driver - * @iocid: IOC unique identifier (integer) + * @ioc: Pointer to MPT adapter structure * @mf: Pointer to MPT request frame * * This routine places a MPT request frame back on the MPT adapter's * FreeQ. */ void -mpt_free_msg_frame(int handle, MPT_ADAPTER *iocp, MPT_FRAME_HDR *mf) +mpt_free_msg_frame(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf) { unsigned long flags; /* Put Request back on FreeQ! */ - spin_lock_irqsave(&iocp->FreeQlock, flags); - Q_ADD_TAIL(&iocp->FreeQ, &mf->u.frame.linkage, MPT_FRAME_HDR); + spin_lock_irqsave(&ioc->FreeQlock, flags); + Q_ADD_TAIL(&ioc->FreeQ, &mf->u.frame.linkage, MPT_FRAME_HDR); #ifdef MFCNT - iocp->mfcnt--; + ioc->mfcnt--; #endif - spin_unlock_irqrestore(&iocp->FreeQlock, flags); + spin_unlock_irqrestore(&ioc->FreeQlock, flags); } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -996,7 +995,7 @@ mpt_add_chain(char *pAddr, u8 next, u16 * mpt_send_handshake_request - Send MPT request via doorbell * handshake method. * @handle: Handle of registered MPT protocol driver - * @iocid: IOC unique identifier (integer) + * @ioc: Pointer to MPT adapter structure * @reqBytes: Size of the request in bytes * @req: Pointer to MPT request frame * @sleepFlag: Use schedule if CAN_SLEEP else use udelay. @@ -1010,7 +1009,7 @@ mpt_add_chain(char *pAddr, u8 next, u16 * Returns 0 for success, non-zero for failure. */ int -mpt_send_handshake_request(int handle, MPT_ADAPTER *iocp, int reqBytes, u32 *req, int sleepFlag) +mpt_send_handshake_request(int handle, MPT_ADAPTER *ioc, int reqBytes, u32 *req, int sleepFlag) { int r = 0; u8 *req_as_bytes; @@ -1026,37 +1025,38 @@ mpt_send_handshake_request(int handle, M * setting cb_idx/req_idx. But ONLY if this request * is in proper (pre-alloc'd) request buffer range... */ - ii = MFPTR_2_MPT_INDEX(iocp,(MPT_FRAME_HDR*)req); - if (reqBytes >= 12 && ii >= 0 && ii < iocp->req_depth) { + ii = MFPTR_2_MPT_INDEX(ioc,(MPT_FRAME_HDR*)req); + if (reqBytes >= 12 && ii >= 0 && ii < ioc->req_depth) { MPT_FRAME_HDR *mf = (MPT_FRAME_HDR*)req; mf->u.frame.hwhdr.msgctxu.fld.req_idx = cpu_to_le16(ii); mf->u.frame.hwhdr.msgctxu.fld.cb_idx = handle; } /* Make sure there are no doorbells */ - CHIPREG_WRITE32(&iocp->chip->IntStatus, 0); - CHIPREG_WRITE32(&iocp->chip->Doorbell, + CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); + + CHIPREG_WRITE32(&ioc->chip->Doorbell, ((MPI_FUNCTION_HANDSHAKE<chip->Doorbell) & MPI_DOORBELL_ACTIVE)) + if (!(CHIPREG_READ32(&ioc->chip->Doorbell) & MPI_DOORBELL_ACTIVE)) return -5; dhsprintk((KERN_INFO MYNAM ": %s: mpt_send_handshake_request start, WaitCnt=%d\n", - iocp->name, ii)); + ioc->name, ii)); - CHIPREG_WRITE32(&iocp->chip->IntStatus, 0); + CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); - r = WaitForDoorbellAck(iocp, 5, sleepFlag); - if (r < 0) + if ((r = WaitForDoorbellAck(ioc, 5, sleepFlag)) < 0) { return -2; - + } + /* Send request via doorbell handshake */ req_as_bytes = (u8 *) req; for (ii = 0; ii < reqBytes/4; ii++) { @@ -1066,21 +1066,21 @@ mpt_send_handshake_request(int handle, M (req_as_bytes[(ii*4) + 1] << 8) | (req_as_bytes[(ii*4) + 2] << 16) | (req_as_bytes[(ii*4) + 3] << 24)); - CHIPREG_WRITE32(&iocp->chip->Doorbell, word); - r = WaitForDoorbellAck(iocp, 5, sleepFlag); - if (r < 0) { + CHIPREG_WRITE32(&ioc->chip->Doorbell, word); + if ((r = WaitForDoorbellAck(ioc, 5, sleepFlag)) < 0) { r = -3; break; } } - if (r >= 0 && WaitForDoorbellInt(iocp, 10, sleepFlag) >= 0) + if (r >= 0 && WaitForDoorbellInt(ioc, 10, sleepFlag) >= 0) r = 0; else r = -4; /* Make sure there are no doorbells */ - CHIPREG_WRITE32(&iocp->chip->IntStatus, 0); + CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); + return r; } @@ -1141,11 +1141,15 @@ mptbase_probe(struct pci_dev *pdev, cons u8 revision; u8 pcixcmd; static int mpt_ids = 0; +#ifdef CONFIG_PROC_FS struct proc_dir_entry *dent, *ent; +#endif if (pci_enable_device(pdev)) return r; + dinitprintk((KERN_WARNING MYNAM ": mpt_adapter_install\n")); + if (!pci_set_dma_mask(pdev, mask)) { dprintk((KERN_INFO MYNAM ": 64 BIT PCI BUS DMA ADDRESSING SUPPORTED\n")); @@ -1170,9 +1174,8 @@ mptbase_probe(struct pci_dev *pdev, cons ioc->alloc_total = sizeof(MPT_ADAPTER); ioc->req_sz = MPT_DEFAULT_FRAME_SIZE; /* avoid div by zero! */ ioc->reply_sz = MPT_REPLY_FRAME_SIZE; - + ioc->pcidev = pdev; - ioc->diagPending = 0; spin_lock_init(&ioc->diagLock); @@ -1223,8 +1226,8 @@ mptbase_probe(struct pci_dev *pdev, cons return -EINVAL; } - dprintk((KERN_INFO MYNAM ": MPT adapter @ %lx, msize=%dd bytes\n", mem_phys, msize)); - dprintk((KERN_INFO MYNAM ": (port i/o @ %lx, psize=%dd bytes)\n", port, psize)); + dinitprintk((KERN_INFO MYNAM ": MPT adapter @ %lx, msize=%dd bytes\n", mem_phys, msize)); + dinitprintk((KERN_INFO MYNAM ": (port i/o @ %lx, psize=%dd bytes)\n", port, psize)); mem = NULL; /* Get logical ptr for PciMem0 space */ @@ -1236,15 +1239,15 @@ mptbase_probe(struct pci_dev *pdev, cons return -EINVAL; } ioc->memmap = mem; - dprintk((KERN_INFO MYNAM ": mem = %p, mem_phys = %lx\n", mem, mem_phys)); + dinitprintk((KERN_INFO MYNAM ": mem = %p, mem_phys = %lx\n", mem, mem_phys)); - dprintk((KERN_INFO MYNAM ": facts @ %p, pfacts[0] @ %p\n", + dinitprintk((KERN_INFO MYNAM ": facts @ %p, pfacts[0] @ %p\n", &ioc->facts, &ioc->pfacts[0])); ioc->mem_phys = mem_phys; ioc->chip = (SYSIF_REGS*)mem; - /* Save Port IO values incase we need to do downloadboot */ + /* Save Port IO values in case we need to do downloadboot */ { u8 *pmem = (u8*)port; ioc->pio_mem_phys = port; @@ -1314,7 +1317,6 @@ mptbase_probe(struct pci_dev *pdev, cons sprintf(ioc->name, "ioc%d", ioc->id); - Q_INIT(&ioc->FreeQ, MPT_FRAME_HDR); spin_lock_init(&ioc->FreeQlock); /* Disable all! */ @@ -1337,7 +1339,6 @@ mptbase_probe(struct pci_dev *pdev, cons printk(MYIOC_s_ERR_FMT "Unable to allocate interrupt %s!\n", ioc->name, __irq_itoa(pdev->irq)); #endif - Q_DEL_ITEM(ioc); list_del(&ioc->list); iounmap(mem); kfree(ioc); @@ -1369,7 +1370,6 @@ mptbase_probe(struct pci_dev *pdev, cons ": WARNING - %s did not initialize properly! (%d)\n", ioc->name, r); - Q_DEL_ITEM(ioc); list_del(&ioc->list); free_irq(ioc->pci_irq, ioc); iounmap(mem); @@ -1386,6 +1386,7 @@ mptbase_probe(struct pci_dev *pdev, cons } } +#ifdef CONFIG_PROC_FS /* * Create "/proc/mpt/iocN" subdirectory entry for each MPT adapter. */ @@ -1402,6 +1403,7 @@ mptbase_probe(struct pci_dev *pdev, cons ent->data = ioc; } } +#endif return 0; } @@ -1625,7 +1627,7 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u3 int hard_reset_done = 0; int alt_ioc_ready = 0; int hard; - int r; + int rc=0; int ii; int handlers; int ret = 0; @@ -1675,39 +1677,37 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u3 * and 1 if a hard reset was performed. */ if (hard_reset_done && reset_alt_ioc_active && ioc->alt_ioc) { - if ((r = MakeIocReady(ioc->alt_ioc, 0, sleepFlag)) == 0) + if ((rc = MakeIocReady(ioc->alt_ioc, 0, sleepFlag)) == 0) alt_ioc_ready = 1; else printk(KERN_WARNING MYNAM - ": alt-%s: (%d) Not ready WARNING!\n", - ioc->alt_ioc->name, r); + ": alt-%s: Not ready WARNING!\n", + ioc->alt_ioc->name); } for (ii=0; ii<5; ii++) { - /* Get IOC facts! Allow 1 retry */ - if ((r = GetIocFacts(ioc, sleepFlag, reason)) != 0) { - dinitprintk((MYIOC_s_INFO_FMT - "ii=%d IocFacts failed r=%x\n", ioc->name, ii, r)); - } else + /* Get IOC facts! Allow 5 retries */ + if ((rc = GetIocFacts(ioc, sleepFlag, reason)) == 0) break; } + - if (r) { - dinitprintk((MYIOC_s_INFO_FMT "Retry IocFacts failed r=%x\n", ioc->name, r)); + if (ii == 5) { + dinitprintk((MYIOC_s_INFO_FMT "Retry IocFacts failed rc=%x\n", ioc->name, rc)); ret = -2; } else if (reason == MPT_HOSTEVENT_IOC_BRINGUP) { MptDisplayIocCapabilities(ioc); } - + if (alt_ioc_ready) { - if ((r = GetIocFacts(ioc->alt_ioc, sleepFlag, reason)) != 0) { - dinitprintk((MYIOC_s_INFO_FMT "Initial Alt IocFacts failed r=%x\n", ioc->name, r)); + if ((rc = GetIocFacts(ioc->alt_ioc, sleepFlag, reason)) != 0) { + dinitprintk((MYIOC_s_INFO_FMT "Initial Alt IocFacts failed rc=%x\n", ioc->name, rc)); /* Retry - alt IOC was initialized once */ - r = GetIocFacts(ioc->alt_ioc, sleepFlag, reason); + rc = GetIocFacts(ioc->alt_ioc, sleepFlag, reason); } - if (r) { - dinitprintk((MYIOC_s_INFO_FMT "Retry Alt IocFacts failed r=%x\n", ioc->name, r)); + if (rc) { + dinitprintk((MYIOC_s_INFO_FMT "Retry Alt IocFacts failed rc=%x\n", ioc->name, rc)); alt_ioc_ready = 0; reset_alt_ioc_active = 0; } else if (reason == MPT_HOSTEVENT_IOC_BRINGUP) { @@ -1720,29 +1720,29 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u3 * init as upper addresses are needed for init. * If fails, continue with alt-ioc processing */ - if ((ret == 0) && ((r = PrimeIocFifos(ioc)) != 0)) + if ((ret == 0) && ((rc = PrimeIocFifos(ioc)) != 0)) ret = -3; /* May need to check/upload firmware & data here! * If fails, continue with alt-ioc processing */ - if ((ret == 0) && ((r = SendIocInit(ioc, sleepFlag)) != 0)) + if ((ret == 0) && ((rc = SendIocInit(ioc, sleepFlag)) != 0)) ret = -4; // NEW! - if (alt_ioc_ready && ((r = PrimeIocFifos(ioc->alt_ioc)) != 0)) { + if (alt_ioc_ready && ((rc = PrimeIocFifos(ioc->alt_ioc)) != 0)) { printk(KERN_WARNING MYNAM ": alt-%s: (%d) FIFO mgmt alloc WARNING!\n", - ioc->alt_ioc->name, r); + ioc->alt_ioc->name, rc); alt_ioc_ready = 0; reset_alt_ioc_active = 0; } if (alt_ioc_ready) { - if ((r = SendIocInit(ioc->alt_ioc, sleepFlag)) != 0) { + if ((rc = SendIocInit(ioc->alt_ioc, sleepFlag)) != 0) { alt_ioc_ready = 0; reset_alt_ioc_active = 0; printk(KERN_WARNING MYNAM ": alt-%s: (%d) init failure WARNING!\n", - ioc->alt_ioc->name, r); + ioc->alt_ioc->name, rc); } } @@ -1754,18 +1754,8 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u3 /* Controller is not operational, cannot do upload */ if (ret == 0) { - r = mpt_do_upload(ioc, sleepFlag); - if (r != 0) - printk(KERN_WARNING MYNAM ": firmware upload failure!\n"); - } - - /* Handle the alt IOC too */ - if ((alt_ioc_ready) && (ioc->alt_ioc->upload_fw)){ - ddlprintk((MYIOC_s_INFO_FMT - "Alt-ioc firmware upload required!\n", - ioc->name)); - r = mpt_do_upload(ioc->alt_ioc, sleepFlag); - if (r != 0) + rc = mpt_do_upload(ioc, sleepFlag); + if (rc != 0) printk(KERN_WARNING MYNAM ": firmware upload failure!\n"); } } @@ -1859,19 +1849,19 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u3 * MptResetHandlers[] registered yet. */ if (hard_reset_done) { - r = handlers = 0; + rc = handlers = 0; for (ii=MPT_MAX_PROTOCOL_DRIVERS-1; ii; ii--) { if ((ret == 0) && MptResetHandlers[ii]) { dprintk((MYIOC_s_INFO_FMT "Calling IOC post_reset handler #%d\n", ioc->name, ii)); - r += (*(MptResetHandlers[ii]))(ioc, MPT_IOC_POST_RESET); + rc += (*(MptResetHandlers[ii]))(ioc, MPT_IOC_POST_RESET); handlers++; } if (alt_ioc_ready && MptResetHandlers[ii]) { dprintk((MYIOC_s_INFO_FMT "Calling alt-%s post_reset handler #%d\n", ioc->name, ioc->alt_ioc->name, ii)); - r += (*(MptResetHandlers[ii]))(ioc->alt_ioc, MPT_IOC_POST_RESET); + rc += (*(MptResetHandlers[ii]))(ioc->alt_ioc, MPT_IOC_POST_RESET); handlers++; } } @@ -1932,84 +1922,90 @@ mpt_detect_bound_ports(MPT_ADAPTER *ioc, /* * mpt_adapter_disable - Disable misbehaving MPT adapter. * @this: Pointer to MPT adapter structure - * @free: Free up alloc'd reply, request, etc. */ static void -mpt_adapter_disable(MPT_ADAPTER *this, int freeup) +mpt_adapter_disable(MPT_ADAPTER *ioc) { - if (this != NULL) { - int sz=0; - int ret; - - if (this->cached_fw != NULL) { - ddlprintk((KERN_INFO MYNAM ": Pushing FW onto adapter\n")); + int sz; + int ret; - if ((ret = mpt_downloadboot(this, NO_SLEEP)) < 0) { - printk(KERN_WARNING MYNAM - ": firmware downloadboot failure (%d)!\n", ret); - } + if (ioc->cached_fw != NULL) { + ddlprintk((KERN_INFO MYNAM ": mpt_adapter_disable: Pushing FW onto adapter\n")); + if ((ret = mpt_downloadboot(ioc, NO_SLEEP)) < 0) { + printk(KERN_WARNING MYNAM + ": firmware downloadboot failure (%d)!\n", ret); } + } - /* Disable adapter interrupts! */ - CHIPREG_WRITE32(&this->chip->IntMask, 0xFFFFFFFF); - this->active = 0; - /* Clear any lingering interrupt */ - CHIPREG_WRITE32(&this->chip->IntStatus, 0); - - if (freeup && this->fifo_pool != NULL) { - pci_free_consistent(this->pcidev, - this->fifo_pool_sz, - this->fifo_pool, this->fifo_pool_dma); - this->reply_frames = NULL; - this->reply_alloc = NULL; - this->req_frames = NULL; - this->req_alloc = NULL; - this->chain_alloc = NULL; - this->fifo_pool = NULL; - this->alloc_total -= this->fifo_pool_sz; - } - if (freeup && this->sense_buf_pool != NULL) { - sz = (this->req_depth * MPT_SENSE_BUFFER_ALLOC); - pci_free_consistent(this->pcidev, sz, - this->sense_buf_pool, this->sense_buf_pool_dma); - this->sense_buf_pool = NULL; - this->alloc_total -= sz; - } - - if (freeup && this->events != NULL){ - sz = MPTCTL_EVENT_LOG_SIZE * sizeof(MPT_IOCTL_EVENTS); - kfree(this->events); - this->events = NULL; - this->alloc_total -= sz; - } - - if (freeup && this->cached_fw != NULL) { - - sz = this->facts.FWImageSize; - pci_free_consistent(this->pcidev, sz, - this->cached_fw, this->cached_fw_dma); - this->cached_fw = NULL; - this->alloc_total -= sz; - } - - if (freeup && this->spi_data.nvram != NULL) { - kfree(this->spi_data.nvram); - this->spi_data.nvram = NULL; - } - - if (freeup && this->spi_data.pIocPg3 != NULL) { - kfree(this->spi_data.pIocPg3); - this->spi_data.pIocPg3 = NULL; - } - - if (freeup && this->spi_data.pIocPg4 != NULL) { - sz = this->spi_data.IocPg4Sz; - pci_free_consistent(this->pcidev, sz, - this->spi_data.pIocPg4, - this->spi_data.IocPg4_dma); - this->spi_data.pIocPg4 = NULL; - this->alloc_total -= sz; - } + /* Disable adapter interrupts! */ + CHIPREG_WRITE32(&ioc->chip->IntMask, 0xFFFFFFFF); + ioc->active = 0; + /* Clear any lingering interrupt */ + CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); + + if (ioc->alloc != NULL) { + sz = ioc->alloc_sz; + dexitprintk((KERN_INFO MYNAM ": %s.free @ %p, sz=%d bytes\n", + ioc->name, ioc->alloc, ioc->alloc_sz)); + pci_free_consistent(ioc->pcidev, sz, + ioc->alloc, ioc->alloc_dma); + ioc->reply_frames = NULL; + ioc->req_frames = NULL; + ioc->alloc = NULL; + ioc->alloc_total -= sz; + } + + if (ioc->sense_buf_pool != NULL) { + sz = (ioc->req_depth * MPT_SENSE_BUFFER_ALLOC); + pci_free_consistent(ioc->pcidev, sz, + ioc->sense_buf_pool, ioc->sense_buf_pool_dma); + ioc->sense_buf_pool = NULL; + ioc->alloc_total -= sz; + } + + if (ioc->events != NULL){ + sz = MPTCTL_EVENT_LOG_SIZE * sizeof(MPT_IOCTL_EVENTS); + kfree(ioc->events); + ioc->events = NULL; + ioc->alloc_total -= sz; + } + + if (ioc->cached_fw != NULL) { + sz = ioc->facts.FWImageSize; + pci_free_consistent(ioc->pcidev, sz, + ioc->cached_fw, ioc->cached_fw_dma); + ioc->cached_fw = NULL; + ioc->alloc_total -= sz; + } + + if (ioc->spi_data.nvram != NULL) { + kfree(ioc->spi_data.nvram); + ioc->spi_data.nvram = NULL; + } + + if (ioc->spi_data.pIocPg3 != NULL) { + kfree(ioc->spi_data.pIocPg3); + ioc->spi_data.pIocPg3 = NULL; + } + + if (ioc->spi_data.pIocPg4 != NULL) { + sz = ioc->spi_data.IocPg4Sz; + pci_free_consistent(ioc->pcidev, sz, + ioc->spi_data.pIocPg4, + ioc->spi_data.IocPg4_dma); + ioc->spi_data.pIocPg4 = NULL; + ioc->alloc_total -= sz; + } + + if (ioc->ReqToChain != NULL) { + kfree(ioc->ReqToChain); + kfree(ioc->RequestNB); + ioc->ReqToChain = NULL; + } + + if (ioc->ChainToChain != NULL) { + kfree(ioc->ChainToChain); + ioc->ChainToChain = NULL; } } @@ -2017,43 +2013,43 @@ mpt_adapter_disable(MPT_ADAPTER *this, i /* * mpt_adapter_dispose - Free all resources associated with a MPT * adapter. - * @this: Pointer to MPT adapter structure + * @ioc: Pointer to MPT adapter structure * * This routine unregisters h/w resources and frees all alloc'd memory * associated with a MPT adapter structure. */ static void -mpt_adapter_dispose(MPT_ADAPTER *this) +mpt_adapter_dispose(MPT_ADAPTER *ioc) { - if (this != NULL) { + if (ioc != NULL) { int sz_first, sz_last; - sz_first = this->alloc_total; + sz_first = ioc->alloc_total; - mpt_adapter_disable(this, 1); + mpt_adapter_disable(ioc); - if (this->pci_irq != -1) { - free_irq(this->pci_irq, this); - this->pci_irq = -1; + if (ioc->pci_irq != -1) { + free_irq(ioc->pci_irq, ioc); + ioc->pci_irq = -1; } - if (this->memmap != NULL) - iounmap((u8 *) this->memmap); + if (ioc->memmap != NULL) + iounmap((u8 *) ioc->memmap); #if defined(CONFIG_MTRR) && 0 - if (this->mtrr_reg > 0) { - mtrr_del(this->mtrr_reg, 0, 0); - dprintk((KERN_INFO MYNAM ": %s: MTRR region de-registered\n", this->name)); + if (ioc->mtrr_reg > 0) { + mtrr_del(ioc->mtrr_reg, 0, 0); + dprintk((KERN_INFO MYNAM ": %s: MTRR region de-registered\n", ioc->name)); } #endif /* Zap the adapter lookup ptr! */ - list_del(&this->list); + list_del(&ioc->list); - sz_last = this->alloc_total; + sz_last = ioc->alloc_total; dprintk((KERN_INFO MYNAM ": %s: free'd %d of %d bytes\n", - this->name, sz_first-sz_last+(int)sizeof(*this), sz_first)); - kfree(this); + ioc->name, sz_first-sz_last+(int)sizeof(*ioc), sz_first)); + kfree(ioc); } } @@ -2228,13 +2224,13 @@ MakeIocReady(MPT_ADAPTER *ioc, int force ii++; cntdn--; if (!cntdn) { printk(MYIOC_s_ERR_FMT "Wait IOC_READY state timeout(%d)!\n", - ioc->name, (ii+5)/HZ); + ioc->name, (int)((ii+5)/HZ)); return -ETIME; } if (sleepFlag == CAN_SLEEP) { set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(1); + schedule_timeout(1 * HZ / 1000); } else { mdelay (1); /* 1 msec delay */ } @@ -2292,7 +2288,9 @@ GetIocFacts(MPT_ADAPTER *ioc, int sleepF int r; int req_sz; int reply_sz; - u32 status; + int sz; + u32 status, vv; + u8 shiftFactor=1; /* IOC *must* NOT be in RESET state! */ if (ioc->last_state == MPI_IOC_STATE_RESET) { @@ -2315,7 +2313,9 @@ GetIocFacts(MPT_ADAPTER *ioc, int sleepF get_facts.Function = MPI_FUNCTION_IOC_FACTS; /* Assert: All other get_facts fields are zero! */ - dinitprintk((MYIOC_s_INFO_FMT "Sending get IocFacts request\n", ioc->name)); + dinitprintk((MYIOC_s_INFO_FMT + "Sending get IocFacts request req_sz=%d reply_sz=%d\n", + ioc->name, req_sz, reply_sz)); /* No non-zero fields in the get_facts request are greater than * 1 byte in size, so we can just fire it off as is. @@ -2388,6 +2388,13 @@ GetIocFacts(MPT_ADAPTER *ioc, int sleepF facts->FWImageSize = le32_to_cpu(facts->FWImageSize); } + sz = facts->FWImageSize; + if ( sz & 0x01 ) + sz += 1; + if ( sz & 0x02 ) + sz += 2; + facts->FWImageSize = sz; + if (!facts->RequestFrameSize) { /* Something is wrong! */ printk(MYIOC_s_ERR_FMT "IOC reported invalid 0 request size!\n", @@ -2395,6 +2402,18 @@ GetIocFacts(MPT_ADAPTER *ioc, int sleepF return -55; } + r = sz = le32_to_cpu(facts->BlockSize); + vv = ((63 / (sz * 4)) + 1) & 0x03; + ioc->NB_for_64_byte_frame = vv; + while ( sz ) + { + shiftFactor++; + sz = sz >> 1; + } + ioc->NBShiftFactor = shiftFactor; + dinitprintk((MYIOC_s_INFO_FMT "NB_for_64_byte_frame=%x NBShiftFactor=%x BlockSize=%x\n", + ioc->name, vv, shiftFactor, r)); + if (reason == MPT_HOSTEVENT_IOC_BRINGUP) { /* * Set values for this IOC's request & reply frame sizes, @@ -2405,9 +2424,9 @@ GetIocFacts(MPT_ADAPTER *ioc, int sleepF ioc->reply_sz = MPT_REPLY_FRAME_SIZE; ioc->reply_depth = min_t(int, MPT_DEFAULT_REPLY_DEPTH, facts->ReplyQueueDepth); - dprintk((MYIOC_s_INFO_FMT "reply_sz=%3d, reply_depth=%4d\n", + dinitprintk((MYIOC_s_INFO_FMT "reply_sz=%3d, reply_depth=%4d\n", ioc->name, ioc->reply_sz, ioc->reply_depth)); - dprintk((MYIOC_s_INFO_FMT "req_sz =%3d, req_depth =%4d\n", + dinitprintk((MYIOC_s_INFO_FMT "req_sz =%3d, req_depth =%4d\n", ioc->name, ioc->req_sz, ioc->req_depth)); /* Get port facts! */ @@ -2416,7 +2435,7 @@ GetIocFacts(MPT_ADAPTER *ioc, int sleepF } } else { printk(MYIOC_s_ERR_FMT - "Invalid IOC facts reply, msgLength=%d offsetof=%d!\n", + "Invalid IOC facts reply, msgLength=%d offsetof=%zd!\n", ioc->name, facts->MsgLength, (offsetof(IOCFactsReply_t, RequestFrameSize)/sizeof(u32))); return -66; @@ -2465,7 +2484,7 @@ GetPortFacts(MPT_ADAPTER *ioc, int portn get_pfacts.PortNumber = portnum; /* Assert: All other get_pfacts fields are zero! */ - dprintk((MYIOC_s_INFO_FMT "Sending get PortFacts(%d) request\n", + dinitprintk((MYIOC_s_INFO_FMT "Sending get PortFacts(%d) request\n", ioc->name, portnum)); /* No non-zero fields in the get_pfacts request are greater than @@ -2516,24 +2535,18 @@ SendIocInit(MPT_ADAPTER *ioc, int sleepF memset(&init_reply, 0, sizeof(init_reply)); ioc_init.WhoInit = MPI_WHOINIT_HOST_DRIVER; -/* ioc_init.ChainOffset = 0; */ ioc_init.Function = MPI_FUNCTION_IOC_INIT; -/* ioc_init.Flags = 0; */ /* If we are in a recovery mode and we uploaded the FW image, * then this pointer is not NULL. Skip the upload a second time. * Set this flag if cached_fw set for either IOC. */ - ioc->upload_fw = 0; - ioc_init.Flags = 0; - if (ioc->facts.Flags & MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT) { - if ((ioc->cached_fw) || (ioc->alt_ioc && ioc->alt_ioc->cached_fw)) - ioc_init.Flags = MPI_IOCINIT_FLAGS_DISCARD_FW_IMAGE; - else - ioc->upload_fw = 1; - } - ddlprintk((MYIOC_s_INFO_FMT "flags %d, upload_fw %d \n", - ioc->name, ioc_init.Flags, ioc->upload_fw)); + if (ioc->facts.Flags & MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT) + ioc->upload_fw = 1; + else + ioc->upload_fw = 0; + ddlprintk((MYIOC_s_INFO_FMT "upload_fw %d facts.Flags=%x\n", + ioc->name, ioc->upload_fw, ioc->facts.Flags)); if ((int)ioc->chip_type <= (int)FC929) { ioc_init.MaxDevices = MPT_MAX_FC_DEVICES; @@ -2542,17 +2555,13 @@ SendIocInit(MPT_ADAPTER *ioc, int sleepF } ioc_init.MaxBuses = MPT_MAX_BUS; -/* ioc_init.MsgFlags = 0; */ -/* ioc_init.MsgContext = cpu_to_le32(0x00000000); */ ioc_init.ReplyFrameSize = cpu_to_le16(ioc->reply_sz); /* in BYTES */ - - ioc->facts.RequestFrameSize = ioc_init.ReplyFrameSize; if (sizeof(dma_addr_t) == sizeof(u64)) { /* Save the upper 32-bits of the request * (reply) and sense buffers. */ - ioc_init.HostMfaHighAddr = cpu_to_le32((u32)((u64)ioc->req_frames_dma >> 32)); + ioc_init.HostMfaHighAddr = cpu_to_le32((u32)((u64)ioc->alloc_dma >> 32)); ioc_init.SenseBufferHighAddr = cpu_to_le32((u32)((u64)ioc->sense_buf_pool_dma >> 32)); } else { /* Force 32-bit addressing */ @@ -2591,14 +2600,14 @@ SendIocInit(MPT_ADAPTER *ioc, int sleepF while (state != MPI_IOC_STATE_OPERATIONAL && --cntdn) { if (sleepFlag == CAN_SLEEP) { set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(1); + schedule_timeout(1 * HZ / 1000); } else { mdelay(1); } if (!cntdn) { printk(MYIOC_s_ERR_FMT "Wait IOC_OP state timeout(%d)!\n", - ioc->name, (count+5)/HZ); + ioc->name, (int)((count+5)/HZ)); return -9; } @@ -2644,7 +2653,7 @@ SendPortEnable(MPT_ADAPTER *ioc, int por /* port_enable.MsgFlags = 0; */ /* port_enable.MsgContext = 0; */ - dprintk((MYIOC_s_INFO_FMT "Sending Port(%d)Enable (req @ %p)\n", + dinitprintk((MYIOC_s_INFO_FMT "Sending Port(%d)Enable (req @ %p)\n", ioc->name, portnum, &port_enable)); /* RAID FW may take a long time to enable @@ -2668,20 +2677,22 @@ SendPortEnable(MPT_ADAPTER *ioc, int por } /* - * Inputs: size - total FW bytes - * Outputs: frags - number of fragments needed - * Return NULL if failed. + * ioc: Pointer to MPT_ADAPTER structure + * size - total FW bytes */ void mpt_alloc_fw_memory(MPT_ADAPTER *ioc, int size) { - /* cached_fw - */ - - if ( (ioc->cached_fw = pci_alloc_consistent(ioc->pcidev, size, &ioc->cached_fw_dma) ) ) - ioc->alloc_total += size; + if (ioc->cached_fw) + return; /* use already allocated memory */ + if (ioc->alt_ioc && ioc->alt_ioc->cached_fw) { + ioc->cached_fw = ioc->alt_ioc->cached_fw; /* use alt_ioc's memory */ + ioc->cached_fw_dma = ioc->alt_ioc->cached_fw_dma; + } else { + if ( (ioc->cached_fw = pci_alloc_consistent(ioc->pcidev, size, &ioc->cached_fw_dma) ) ) + ioc->alloc_total += size; + } } - /* * If alt_img is NULL, delete from ioc structure. * Else, delete a secondary image in same format. @@ -2692,6 +2703,8 @@ mpt_free_fw_memory(MPT_ADAPTER *ioc) int sz; sz = ioc->facts.FWImageSize; + dinitprintk((KERN_WARNING MYNAM "free_fw_memory: FW Image @ %p[%p], sz=%d[%x] bytes\n", + ioc->cached_fw, (void *)(ulong)ioc->cached_fw_dma, sz, sz)); pci_free_consistent(ioc->pcidev, sz, ioc->cached_fw, ioc->cached_fw_dma); ioc->cached_fw = NULL; @@ -2725,30 +2738,24 @@ mpt_do_upload(MPT_ADAPTER *ioc, int slee int sgeoffset; u32 flagsLength; int ii, sz, reply_sz; - int cmdStatus, freeMem = 0; + int cmdStatus; - /* If the image size is 0 or if the pointer is - * not NULL (error), we are done. + /* If the image size is 0, we are done. */ - if (((sz = ioc->facts.FWImageSize) == 0) || ioc->cached_fw) + if ((sz = ioc->facts.FWImageSize) == 0) return 0; - if ( sz & 0x01 ) - sz += 1; - if ( sz & 0x02 ) - sz += 2; - mpt_alloc_fw_memory(ioc, sz); + dinitprintk((KERN_WARNING MYNAM ": FW Image @ %p[%p], sz=%d[%x] bytes\n", + ioc->cached_fw, (void *)(ulong)ioc->cached_fw_dma, sz, sz)); + if (ioc->cached_fw == NULL) { /* Major Failure. */ return -ENOMEM; } - dinitprintk((KERN_WARNING MYNAM ": FW Image @ %p[%p], sz=%d[%x] bytes\n", - ioc->cached_fw, (void *)(ulong)ioc->cached_fw_dma, sz, sz)); - prequest = (FWUpload_t *)&request; preply = (FWUploadReply_t *)&reply; @@ -2797,23 +2804,11 @@ mpt_do_upload(MPT_ADAPTER *ioc, int slee dinitprintk((MYIOC_s_INFO_FMT ": do_upload status %d \n", ioc->name, cmdStatus)); - /* Check to see if we have a copy of this image in - * host memory already. - */ - if (cmdStatus == 0) { - ioc->upload_fw = 0; - if (ioc->alt_ioc && ioc->alt_ioc->cached_fw) - freeMem = 1; - } - - /* We already have a copy of this image or - * we had some type of an error - either the handshake - * failed (i != 0) or the command did not complete successfully. - */ - if (cmdStatus || freeMem) { + + if (cmdStatus) { - ddlprintk((MYIOC_s_INFO_FMT ": do_upload freeing %s image \n", - ioc->name, cmdStatus ? "incomplete" : "duplicate")); + ddlprintk((MYIOC_s_INFO_FMT ": fw upload failed, freeing image \n", + ioc->name)); mpt_free_fw_memory(ioc); } @@ -2841,28 +2836,26 @@ mpt_downloadboot(MPT_ADAPTER *ioc, int s MpiExtImageHeader_t *pExtImage; u32 fwSize; u32 diag0val; -#ifdef MPT_DEBUG - u32 diag1val = 0; -#endif - int count = 0; + int count; u32 *ptrFw; u32 diagRwData; u32 nextImage; u32 load_addr; - u32 ioc_state; + u32 ioc_state=0; ddlprintk((MYIOC_s_INFO_FMT "downloadboot: fw size 0x%x, ioc FW Ptr %p\n", ioc->name, ioc->facts.FWImageSize, ioc->cached_fw)); - /* Get dma_addr and data transfer size. - */ if ( ioc->facts.FWImageSize == 0 ) return -1; - /* Get the DMA from ioc or ioc->alt_ioc */ if (ioc->cached_fw == NULL) return -2; + /* prevent a second downloadboot and memory free with alt_ioc */ + if (ioc->alt_ioc && ioc->alt_ioc->cached_fw) + ioc->alt_ioc->cached_fw = NULL; + CHIPREG_WRITE32(&ioc->chip->WriteSequence, 0xFF); CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_1ST_KEY_VALUE); CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_2ND_KEY_VALUE); @@ -2870,18 +2863,17 @@ mpt_downloadboot(MPT_ADAPTER *ioc, int s CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_4TH_KEY_VALUE); CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_5TH_KEY_VALUE); - diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); - diag0val |= (MPI_DIAG_PREVENT_IOC_BOOT | MPI_DIAG_DISABLE_ARM); - CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val); + CHIPREG_WRITE32(&ioc->chip->Diagnostic, (MPI_DIAG_PREVENT_IOC_BOOT | MPI_DIAG_DISABLE_ARM)); - /* wait 100 msec */ + /* wait 1 msec */ if (sleepFlag == CAN_SLEEP) { set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(100 * HZ / 1000); + schedule_timeout(1 * HZ / 1000); } else { - mdelay (100); + mdelay (1); } + diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val | MPI_DIAG_RESET_ADAPTER); for (count = 0; count < 30; count ++) { @@ -2894,7 +2886,7 @@ mpt_downloadboot(MPT_ADAPTER *ioc, int s /* wait 1 sec */ if (sleepFlag == CAN_SLEEP) { set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ); + schedule_timeout(1000 * HZ / 1000); } else { mdelay (1000); } @@ -2914,8 +2906,7 @@ mpt_downloadboot(MPT_ADAPTER *ioc, int s CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_5TH_KEY_VALUE); /* Set the DiagRwEn and Disable ARM bits */ - diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); - CHIPREG_WRITE32(&ioc->chip->Diagnostic, (diag0val | MPI_DIAG_RW_ENABLE | MPI_DIAG_DISABLE_ARM)); + CHIPREG_WRITE32(&ioc->chip->Diagnostic, (MPI_DIAG_RW_ENABLE | MPI_DIAG_DISABLE_ARM)); pFwHeader = (MpiFwHeader_t *) ioc->cached_fw; fwSize = (pFwHeader->ImageSize + 3)/4; @@ -2961,15 +2952,6 @@ mpt_downloadboot(MPT_ADAPTER *ioc, int s ddlprintk((MYIOC_s_INFO_FMT "Write IopResetVector Value=%x! \n", ioc->name, pFwHeader->IopResetVectorValue)); CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwData, pFwHeader->IopResetVectorValue); - /* clear the PREVENT_IOC_BOOT bit */ - diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); - ddlprintk((MYIOC_s_INFO_FMT "downloadboot diag0val=%x, turning off PREVENT_IOC_BOOT\n", - ioc->name, diag0val)); - diag0val &= ~(MPI_DIAG_PREVENT_IOC_BOOT); - ddlprintk((MYIOC_s_INFO_FMT "downloadboot now diag0val=%x\n", - ioc->name, diag0val)); - CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val); - /* Clear the internal flash bad bit - autoincrementing register, * so must do two writes. */ @@ -2980,28 +2962,13 @@ mpt_downloadboot(MPT_ADAPTER *ioc, int s CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwData, diagRwData); diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); - ddlprintk((MYIOC_s_INFO_FMT "downloadboot diag0val=%x, turning off DISABLE_ARM, RW_ENABLE, RESET_HISTORY\n", + ddlprintk((MYIOC_s_INFO_FMT "downloadboot diag0val=%x, turning off PREVENT_IOC_BOOT, DISABLE_ARM\n", ioc->name, diag0val)); - diag0val &= ~(MPI_DIAG_DISABLE_ARM | MPI_DIAG_RW_ENABLE | MPI_DIAG_RESET_HISTORY); + diag0val &= ~(MPI_DIAG_PREVENT_IOC_BOOT | MPI_DIAG_DISABLE_ARM); ddlprintk((MYIOC_s_INFO_FMT "downloadboot now diag0val=%x\n", ioc->name, diag0val)); CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val); - /* wait 100 msec */ - if (sleepFlag == CAN_SLEEP) { - ddlprintk((MYIOC_s_INFO_FMT "CAN_SLEEP 100 msec before reset the sequencer\n", ioc->name)); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(100 * HZ / 1000); - } else { - ddlprintk((MYIOC_s_INFO_FMT "mdelay 100 msec before reset the sequencer\n", ioc->name)); - mdelay (100); - } - - diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); - if ( diag0val & (MPI_DIAG_FLASH_BAD_SIG | MPI_DIAG_DISABLE_ARM) ) { - ddlprintk((MYIOC_s_INFO_FMT "downloadboot failed, diag0val=%x FLASH_BAD_SIG | DISABLE_ARM on\n ", - ioc->name, diag0val)); - } /* Write 0xFF to reset the sequencer */ CHIPREG_WRITE32(&ioc->chip->WriteSequence, 0xFF); @@ -3009,26 +2976,18 @@ mpt_downloadboot(MPT_ADAPTER *ioc, int s if ((ioc_state = mpt_GetIocState(ioc, 0)) & MPI_IOC_STATE_READY) { ddlprintk((MYIOC_s_INFO_FMT "downloadboot successful! (count=%d) IocState=%x\n", ioc->name, count, ioc_state)); -/* if ((r = GetIocFacts(ioc, sleepFlag, MPT_HOSTEVENT_IOC_BRINGUP)) != 0) { - if ((r = GetIocFacts(ioc, sleepFlag, MPT_HOSTEVENT_IOC_BRINGUP)) != 0) { - ddlprintk((MYIOC_s_INFO_FMT "GetIocFacts failed\n", - ioc->name)); - return -EFAULT; - } - } */ - /* wait 2 sec */ -/* if (sleepFlag == CAN_SLEEP) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(5000 * HZ / 1000); - } else { - mdelay (5000); - } */ - + if ((SendIocInit(ioc, sleepFlag)) != 0) { + ddlprintk((MYIOC_s_INFO_FMT "downloadboot: SendIocInit failed\n", + ioc->name)); + return -EFAULT; + } + ddlprintk((MYIOC_s_INFO_FMT "downloadboot: SendIocInit successful\n", + ioc->name)); return 0; } if (sleepFlag == CAN_SLEEP) { set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(1); + schedule_timeout(10 * HZ / 1000); } else { mdelay (10); } @@ -3068,8 +3027,8 @@ static int KickStart(MPT_ADAPTER *ioc, int force, int sleepFlag) { int hard_reset_done = 0; - u32 ioc_state; - int cntdn, cnt = 0; + u32 ioc_state=0; + int cnt,cntdn; dinitprintk((KERN_WARNING MYNAM ": KickStarting %s!\n", ioc->name)); if ((int)ioc->chip_type > (int)FC929) { @@ -3080,7 +3039,7 @@ KickStart(MPT_ADAPTER *ioc, int force, i if (sleepFlag == CAN_SLEEP) { set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ); + schedule_timeout(1000 * HZ / 1000); } else { mdelay (1000); } @@ -3090,26 +3049,27 @@ KickStart(MPT_ADAPTER *ioc, int force, i if (hard_reset_done < 0) return hard_reset_done; - dprintk((MYIOC_s_INFO_FMT "Diagnostic reset successful!\n", + dinitprintk((MYIOC_s_INFO_FMT "Diagnostic reset successful!\n", ioc->name)); - cntdn = ((sleepFlag == CAN_SLEEP) ? HZ : 1000) * 20; /* 20 seconds */ + cntdn = ((sleepFlag == CAN_SLEEP) ? HZ : 1000) * 2; /* 2 seconds */ for (cnt=0; cntname, cnt)); + ioc_state = mpt_GetIocState(ioc, 1); + if ((ioc_state == MPI_IOC_STATE_READY) || (ioc_state == MPI_IOC_STATE_OPERATIONAL)) { + dinitprintk((MYIOC_s_INFO_FMT "KickStart successful! (cnt=%d)\n", + ioc->name, cnt)); return hard_reset_done; } if (sleepFlag == CAN_SLEEP) { set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(1); + schedule_timeout(10 * HZ / 1000); } else { mdelay (10); } } - printk(MYIOC_s_ERR_FMT "Failed to come READY after reset!\n", - ioc->name); + printk(MYIOC_s_ERR_FMT "Failed to come READY after reset! IocState=%x\n", + ioc->name, ioc_state); return -1; } @@ -3199,12 +3159,6 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ign dprintk((MYIOC_s_INFO_FMT "DbG2: diag0=%08x, diag1=%08x\n", ioc->name, diag0val, diag1val)); #endif - /* Write the PreventIocBoot bit */ - if ((ioc->cached_fw) || (ioc->alt_ioc && ioc->alt_ioc->cached_fw)) { - diag0val |= MPI_DIAG_PREVENT_IOC_BOOT; - CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val); - } - /* * Disable the ARM (Bug fix) * @@ -3246,20 +3200,13 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ign /* FIXME? Examine results here? */ } - if ((ioc->cached_fw) || (ioc->alt_ioc && ioc->alt_ioc->cached_fw)) { + if (ioc->cached_fw) { /* If the DownloadBoot operation fails, the * IOC will be left unusable. This is a fatal error * case. _diag_reset will return < 0 */ for (count = 0; count < 30; count ++) { diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); -#ifdef MPT_DEBUG - if (ioc->alt_ioc) - diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic); - dprintk((MYIOC_s_INFO_FMT - "DbG2b: diag0=%08x, diag1=%08x\n", - ioc->name, diag0val, diag1val)); -#endif if (!(diag0val & MPI_DIAG_RESET_ADAPTER)) { break; } @@ -3267,7 +3214,7 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ign /* wait 1 sec */ if (sleepFlag == CAN_SLEEP) { set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ); + schedule_timeout(1000 * HZ / 1000); } else { mdelay (1000); } @@ -3295,7 +3242,7 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ign /* wait 1 sec */ if (sleepFlag == CAN_SLEEP) { set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ); + schedule_timeout(1000 * HZ / 1000); } else { mdelay (1000); } @@ -3419,13 +3366,13 @@ SendIocReset(MPT_ADAPTER *ioc, u8 reset_ count *= 10; printk(KERN_ERR MYNAM ": %s: ERROR - Wait IOC_READY state timeout(%d)!\n", - ioc->name, (count+5)/HZ); + ioc->name, (int)((count+5)/HZ)); return -ETIME; } if (sleepFlag == CAN_SLEEP) { set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(1); + schedule_timeout(1 * HZ / 1000); } else { mdelay (1); /* 1 msec delay */ } @@ -3443,35 +3390,45 @@ SendIocReset(MPT_ADAPTER *ioc, u8 reset_ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* - * PrimeIocFifos - Initialize IOC request and reply FIFOs. - * @ioc: Pointer to MPT_ADAPTER structure - * - * This routine allocates memory for the MPT reply and request frame - * pools (if necessary), and primes the IOC reply FIFO with - * reply frames. - * - * Returns 0 for success, non-zero for failure. + * initChainBuffers - Allocate memory for and initialize + * chain buffers, chain buffer control arrays and spinlock. + * @hd: Pointer to MPT_SCSI_HOST structure + * @init: If set, initialize the spin lock. */ static int -PrimeIocFifos(MPT_ADAPTER *ioc) +initChainBuffers(MPT_ADAPTER *ioc) { - MPT_FRAME_HDR *mf; - unsigned long b; - unsigned long flags; - dma_addr_t aligned_mem_dma; - u8 *aligned_mem; - int i, sz; - int chain_buffer_sz, reply_buffer_sz, request_buffer_sz; - int scale, num_sge, num_chain; - - /* request buffer size, rounding UP to nearest 4-kB boundary */ - request_buffer_sz = (ioc->req_sz * ioc->req_depth) + 128; - request_buffer_sz = ((request_buffer_sz + 0x1000UL - 1UL) / 0x1000) * 0x1000; + u8 *mem; + int sz, ii, num_chain; + int scale, num_sge, numSGE; + + /* ReqToChain size must equal the req_depth + * index = req_idx + */ + if (ioc->ReqToChain == NULL) { + sz = ioc->req_depth * sizeof(int); + mem = kmalloc(sz, GFP_ATOMIC); + if (mem == NULL) + return -1; + + ioc->ReqToChain = (int *) mem; + dinitprintk((KERN_INFO MYNAM ": %s ReqToChain alloc @ %p, sz=%d bytes\n", + ioc->name, mem, sz)); + mem = kmalloc(sz, GFP_ATOMIC); + if (mem == NULL) + return -1; - /* reply buffer size */ - reply_buffer_sz = (ioc->reply_sz * ioc->reply_depth) + 128; + ioc->RequestNB = (int *) mem; + dinitprintk((KERN_INFO MYNAM ": %s RequestNB alloc @ %p, sz=%d bytes\n", + ioc->name, mem, sz)); + } + for (ii = 0; ii < ioc->req_depth; ii++) { + ioc->ReqToChain[ii] = MPT_HOST_NO_CHAIN; + } - /* chain buffer size, copied from from mptscsih_initChainBuffers() + /* ChainToChain size must equal the total number + * of chain buffers to be allocated. + * index = chain_idx * * Calculate the number of chain buffers needed(plus 1) per I/O * then multiply the the maximum number of simultaneous cmds @@ -3479,79 +3436,134 @@ PrimeIocFifos(MPT_ADAPTER *ioc) * num_sge = num sge in request frame + last chain buffer * scale = num sge per chain buffer if no chain element */ - scale = ioc->req_sz/(sizeof(dma_addr_t) + sizeof(u32)); if (sizeof(dma_addr_t) == sizeof(u64)) num_sge = scale + (ioc->req_sz - 60) / (sizeof(dma_addr_t) + sizeof(u32)); else - num_sge = 1 + scale + (ioc->req_sz - 64) / (sizeof(dma_addr_t) + sizeof(u32)); + num_sge = 1+ scale + (ioc->req_sz - 64) / (sizeof(dma_addr_t) + sizeof(u32)); + + if (sizeof(dma_addr_t) == sizeof(u64)) { + numSGE = (scale - 1) * (ioc->facts.MaxChainDepth-1) + scale + + (ioc->req_sz - 60) / (sizeof(dma_addr_t) + sizeof(u32)); + } else { + numSGE = 1 + (scale - 1) * (ioc->facts.MaxChainDepth-1) + scale + + (ioc->req_sz - 64) / (sizeof(dma_addr_t) + sizeof(u32)); + } + dinitprintk((KERN_INFO MYNAM ": %s num_sge=%d numSGE=%d\n", + ioc->name, num_sge, numSGE)); + + if ( numSGE > MPT_SCSI_SG_DEPTH ) + numSGE = MPT_SCSI_SG_DEPTH; num_chain = 1; - while (MPT_SCSI_SG_DEPTH - num_sge > 0) { + while (numSGE - num_sge > 0) { num_chain++; num_sge += (scale - 1); } num_chain++; - if ((int)ioc->chip_type > (int) FC929) + dinitprintk((KERN_INFO MYNAM ": %s Now numSGE=%d num_sge=%d num_chain=%d\n", + ioc->name, numSGE, num_sge, num_chain)); + + if ((int) ioc->chip_type > (int) FC929) num_chain *= MPT_SCSI_CAN_QUEUE; else num_chain *= MPT_FC_CAN_QUEUE; - chain_buffer_sz = num_chain * ioc->req_sz; + ioc->num_chain = num_chain; - if(ioc->fifo_pool == NULL) { + sz = num_chain * sizeof(int); + if (ioc->ChainToChain == NULL) { + mem = kmalloc(sz, GFP_ATOMIC); + if (mem == NULL) + return -1; - ioc->fifo_pool_sz = request_buffer_sz + - reply_buffer_sz + chain_buffer_sz; + ioc->ChainToChain = (int *) mem; + dinitprintk((KERN_INFO MYNAM ": %s ChainToChain alloc @ %p, sz=%d bytes\n", + ioc->name, mem, sz)); + } else { + mem = (u8 *) ioc->ChainToChain; + } + memset(mem, 0xFF, sz); + return num_chain; +} - ioc->fifo_pool = pci_alloc_consistent(ioc->pcidev, - ioc->fifo_pool_sz, &ioc->fifo_pool_dma); +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * PrimeIocFifos - Initialize IOC request and reply FIFOs. + * @ioc: Pointer to MPT_ADAPTER structure + * + * This routine allocates memory for the MPT reply and request frame + * pools (if necessary), and primes the IOC reply FIFO with + * reply frames. + * + * Returns 0 for success, non-zero for failure. + */ +static int +PrimeIocFifos(MPT_ADAPTER *ioc) +{ + MPT_FRAME_HDR *mf; + unsigned long flags; + dma_addr_t alloc_dma; + u8 *mem; + int i, reply_sz, sz, total_size, num_chain; - if( ioc->fifo_pool == NULL) + /* Prime reply FIFO... */ + + if (ioc->reply_frames == NULL) { + if ( (num_chain = initChainBuffers(ioc)) < 0) + return -1; + + total_size = reply_sz = (ioc->reply_sz * ioc->reply_depth); + dinitprintk((KERN_INFO MYNAM ": %s.ReplyBuffer sz=%d bytes, ReplyDepth=%d\n", + ioc->name, ioc->reply_sz, ioc->reply_depth)); + dinitprintk((KERN_INFO MYNAM ": %s.ReplyBuffer sz=%d[%x] bytes\n", + ioc->name, reply_sz, reply_sz)); + + sz = (ioc->req_sz * ioc->req_depth); + dinitprintk((KERN_INFO MYNAM ": %s.RequestBuffer sz=%d bytes, RequestDepth=%d\n", + ioc->name, ioc->req_sz, ioc->req_depth)); + dinitprintk((KERN_INFO MYNAM ": %s.RequestBuffer sz=%d[%x] bytes\n", + ioc->name, sz, sz)); + total_size += sz; + + sz = num_chain * ioc->req_sz; /* chain buffer pool size */ + dinitprintk((KERN_INFO MYNAM ": %s.ChainBuffer sz=%d bytes, ChainDepth=%d\n", + ioc->name, ioc->req_sz, num_chain)); + dinitprintk((KERN_INFO MYNAM ": %s.ChainBuffer sz=%d[%x] bytes num_chain=%d\n", + ioc->name, sz, sz, num_chain)); + + total_size += sz; + mem = pci_alloc_consistent(ioc->pcidev, total_size, &alloc_dma); + if (mem == NULL) { + printk(MYIOC_s_ERR_FMT "Unable to allocate Reply, Request, Chain Buffers!\n", + ioc->name); goto out_fail; + } - ioc->alloc_total += ioc->fifo_pool_sz; - memset(ioc->fifo_pool, 0, ioc->fifo_pool_sz); + dinitprintk((KERN_INFO MYNAM ": %s.Total alloc @ %p[%p], sz=%d[%x] bytes\n", + ioc->name, mem, (void *)(ulong)alloc_dma, total_size, total_size)); - /* reply fifo pointers */ - ioc->reply_alloc = ioc->fifo_pool; - ioc->reply_alloc_dma = ioc->fifo_pool_dma; - /* request fifo pointers */ - ioc->req_alloc = ioc->reply_alloc+reply_buffer_sz; - ioc->req_alloc_dma = ioc->reply_alloc_dma+reply_buffer_sz; - /* chain buffer pointers */ - ioc->chain_alloc = ioc->req_alloc+request_buffer_sz; - ioc->chain_alloc_dma = ioc->req_alloc_dma+request_buffer_sz; - ioc->chain_alloc_sz = chain_buffer_sz; - - /* Prime reply FIFO... */ - dprintk((KERN_INFO MYNAM ": %s.reply_alloc @ %p[%p], sz=%d bytes\n", - ioc->name, ioc->reply_alloc, - (void *)(ulong)ioc->reply_alloc_dma, reply_buffer_sz)); - - b = (unsigned long) ioc->reply_alloc; - b = (b + (0x80UL - 1UL)) & ~(0x80UL - 1UL); /* round up to 128-byte boundary */ - aligned_mem = (u8 *) b; - ioc->reply_frames = (MPT_FRAME_HDR *) aligned_mem; - ioc->reply_frames_dma = - (ioc->reply_alloc_dma + (aligned_mem - ioc->reply_alloc)); + memset(mem, 0, total_size); + ioc->alloc_total += total_size; + ioc->alloc = mem; + ioc->alloc_dma = alloc_dma; + ioc->alloc_sz = total_size; + ioc->reply_frames = (MPT_FRAME_HDR *) mem; + ioc->reply_frames_low_dma = (u32) (alloc_dma & 0xFFFFFFFF); + + alloc_dma += reply_sz; + mem += reply_sz; - ioc->reply_frames_low_dma = (u32) (ioc->reply_frames_dma & 0xFFFFFFFF); - /* Request FIFO - WE manage this! */ - dprintk((KERN_INFO MYNAM ": %s.req_alloc @ %p[%p], sz=%d bytes\n", - ioc->name, ioc->req_alloc, - (void *)(ulong)ioc->req_alloc_dma, request_buffer_sz)); - - b = (unsigned long) ioc->req_alloc; - b = (b + (0x80UL - 1UL)) & ~(0x80UL - 1UL); /* round up to 128-byte boundary */ - aligned_mem = (u8 *) b; - ioc->req_frames = (MPT_FRAME_HDR *) aligned_mem; - ioc->req_frames_dma = - (ioc->req_alloc_dma + (aligned_mem - ioc->req_alloc)); - ioc->req_frames_low_dma = (u32) (ioc->req_frames_dma & 0xFFFFFFFF); + ioc->req_frames = (MPT_FRAME_HDR *) mem; + ioc->req_frames_dma = alloc_dma; + + dinitprintk((KERN_INFO MYNAM ": %s.RequestBuffers @ %p[%p]\n", + ioc->name, mem, (void *)(ulong)alloc_dma)); + + ioc->req_frames_low_dma = (u32) (alloc_dma & 0xFFFFFFFF); #if defined(CONFIG_MTRR) && 0 /* @@ -3559,79 +3571,93 @@ PrimeIocFifos(MPT_ADAPTER *ioc) * (at least as much as we can; "size and base must be * multiples of 4 kiB" */ - ioc->mtrr_reg = mtrr_add(ioc->fifo_pool, - ioc->fifo_pool_sz, + ioc->mtrr_reg = mtrr_add(ioc->req_frames_dma, + sz, MTRR_TYPE_WRCOMB, 1); dprintk((MYIOC_s_INFO_FMT "MTRR region registered (base:size=%08x:%x)\n", - ioc->name, ioc->fifo_pool, ioc->fifo_pool_sz)); + ioc->name, ioc->req_frames_dma, sz)); #endif - } /* ioc->fifo_pool == NULL */ - - /* Post Reply frames to FIFO - */ - aligned_mem_dma = ioc->reply_frames_dma; - dprintk((KERN_INFO MYNAM ": %s.reply_frames @ %p[%p]\n", - ioc->name, ioc->reply_frames, (void *)(ulong)aligned_mem_dma)); + for (i = 0; i < ioc->req_depth; i++) { + alloc_dma += ioc->req_sz; + mem += ioc->req_sz; + } - for (i = 0; i < ioc->reply_depth; i++) { - /* Write each address to the IOC! */ - CHIPREG_WRITE32(&ioc->chip->ReplyFifo, aligned_mem_dma); - aligned_mem_dma += ioc->reply_sz; - } + ioc->ChainBuffer = mem; + ioc->ChainBufferDMA = alloc_dma; + dinitprintk((KERN_INFO MYNAM " :%s.ChainBuffers @ %p(%p)\n", + ioc->name, ioc->ChainBuffer, (void *)(ulong)ioc->ChainBufferDMA)); - /* Initialize Request frames linked list - */ - aligned_mem_dma = ioc->req_frames_dma; - aligned_mem = (u8 *) ioc->req_frames; - dprintk((KERN_INFO MYNAM ": %s.req_frames @ %p[%p]\n", - ioc->name, aligned_mem, (void *)(ulong)aligned_mem_dma)); + /* Initialize the free chain Q. + */ - spin_lock_irqsave(&ioc->FreeQlock, flags); - Q_INIT(&ioc->FreeQ, MPT_FRAME_HDR); - for (i = 0; i < ioc->req_depth; i++) { - mf = (MPT_FRAME_HDR *) aligned_mem; - - /* Queue REQUESTs *internally*! */ - Q_ADD_TAIL(&ioc->FreeQ.head, &mf->u.frame.linkage, MPT_FRAME_HDR); - aligned_mem += ioc->req_sz; - } - spin_unlock_irqrestore(&ioc->FreeQlock, flags); + Q_INIT(&ioc->FreeChainQ, MPT_FRAME_HDR); + /* Post the chain buffers to the FreeChainQ. + */ + mem = (u8 *)ioc->ChainBuffer; + for (i=0; i < num_chain; i++) { + mf = (MPT_FRAME_HDR *) mem; + Q_ADD_TAIL(&ioc->FreeChainQ.head, &mf->u.frame.linkage, MPT_FRAME_HDR); + mem += ioc->req_sz; + } + + /* Initialize Request frames linked list + */ + alloc_dma = ioc->req_frames_dma; + mem = (u8 *) ioc->req_frames; + + spin_lock_irqsave(&ioc->FreeQlock, flags); + Q_INIT(&ioc->FreeQ, MPT_FRAME_HDR); + for (i = 0; i < ioc->req_depth; i++) { + mf = (MPT_FRAME_HDR *) mem; + + /* Queue REQUESTs *internally*! */ + Q_ADD_TAIL(&ioc->FreeQ.head, &mf->u.frame.linkage, MPT_FRAME_HDR); + mem += ioc->req_sz; + } + spin_unlock_irqrestore(&ioc->FreeQlock, flags); - if (ioc->sense_buf_pool == NULL) { sz = (ioc->req_depth * MPT_SENSE_BUFFER_ALLOC); ioc->sense_buf_pool = - pci_alloc_consistent(ioc->pcidev, sz, &ioc->sense_buf_pool_dma); - if (ioc->sense_buf_pool == NULL) + pci_alloc_consistent(ioc->pcidev, sz, &ioc->sense_buf_pool_dma); + if (ioc->sense_buf_pool == NULL) { + printk(MYIOC_s_ERR_FMT "Unable to allocate Sense Buffers!\n", + ioc->name); goto out_fail; + } ioc->sense_buf_low_dma = (u32) (ioc->sense_buf_pool_dma & 0xFFFFFFFF); ioc->alloc_total += sz; + dinitprintk((KERN_INFO MYNAM ": %s.SenseBuffers @ %p[%p]\n", + ioc->name, ioc->sense_buf_pool, (void *)(ulong)ioc->sense_buf_pool_dma)); + + } + + /* Post Reply frames to FIFO + */ + alloc_dma = ioc->alloc_dma; + dinitprintk((KERN_INFO MYNAM ": %s.ReplyBuffers @ %p[%p]\n", + ioc->name, ioc->reply_frames, (void *)(ulong)alloc_dma)); + + for (i = 0; i < ioc->reply_depth; i++) { + /* Write each address to the IOC! */ + CHIPREG_WRITE32(&ioc->chip->ReplyFifo, alloc_dma); + alloc_dma += ioc->reply_sz; } return 0; out_fail: - if (ioc->fifo_pool != NULL) { + if (ioc->alloc != NULL) { + sz = ioc->alloc_sz; pci_free_consistent(ioc->pcidev, - ioc->fifo_pool_sz, - ioc->fifo_pool, ioc->fifo_pool_dma); + sz, + ioc->alloc, ioc->alloc_dma); ioc->reply_frames = NULL; - ioc->reply_alloc = NULL; ioc->req_frames = NULL; - ioc->req_alloc = NULL; - ioc->chain_alloc = NULL; - ioc->fifo_pool = NULL; - ioc->alloc_total -= ioc->fifo_pool_sz; -#if defined(CONFIG_MTRR) && 0 - if (ioc->mtrr_reg > 0) { - mtrr_del(ioc->mtrr_reg, 0, 0); - dprintk((MYIOC_s_INFO_FMT "MTRR region de-registered\n", - ioc->name)); - } -#endif + ioc->alloc_total -= sz; } if (ioc->sense_buf_pool != NULL) { sz = (ioc->req_depth * MPT_SENSE_BUFFER_ALLOC); @@ -3693,8 +3719,8 @@ mpt_handshake_req_reply_wait(MPT_ADAPTER if ((t = WaitForDoorbellInt(ioc, 5, sleepFlag)) < 0) failcnt++; - dhsprintk((MYIOC_s_INFO_FMT "HandShake request start, WaitCnt=%d%s\n", - ioc->name, t, failcnt ? " - MISSING DOORBELL HANDSHAKE!" : "")); + dhsprintk((MYIOC_s_INFO_FMT "HandShake request start reqBytes=%d, WaitCnt=%d%s\n", + ioc->name, reqBytes, t, failcnt ? " - MISSING DOORBELL HANDSHAKE!" : "")); /* Read doorbell and check for active bit */ if (!(CHIPREG_READ32(&ioc->chip->Doorbell) & MPI_DOORBELL_ACTIVE)) @@ -3728,7 +3754,7 @@ mpt_handshake_req_reply_wait(MPT_ADAPTER failcnt++; } - dmfprintk((KERN_INFO MYNAM ": Handshake request frame (@%p) header\n", req)); + dhsprintk((KERN_INFO MYNAM ": Handshake request frame (@%p) header\n", req)); DBG_DUMP_REQUEST_FRAME_HDR(req) dhsprintk((MYIOC_s_INFO_FMT "HandShake request post done, WaitCnt=%d%s\n", @@ -3783,7 +3809,7 @@ WaitForDoorbellAck(MPT_ADAPTER *ioc, int if (! (intstat & MPI_HIS_IOP_DOORBELL_STATUS)) break; set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(1); + schedule_timeout(1 * HZ / 1000); count++; } } else { @@ -3833,7 +3859,7 @@ WaitForDoorbellInt(MPT_ADAPTER *ioc, int if (intstat & MPI_HIS_DOORBELL_INTERRUPT) break; set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(1); + schedule_timeout(1 * HZ / 1000); count++; } } else { @@ -3935,7 +3961,7 @@ WaitForDoorbellReply(MPT_ADAPTER *ioc, i } #endif - dmfprintk((MYIOC_s_INFO_FMT "Got Handshake reply:\n", ioc->name)); + dhsprintk((MYIOC_s_INFO_FMT "Got Handshake reply:\n", ioc->name)); DBG_DUMP_REPLY_FRAME(mptReply) dhsprintk((MYIOC_s_INFO_FMT "WaitForDoorbell REPLY WaitCnt=%d (sz=%d)\n", @@ -4282,9 +4308,11 @@ mpt_GetScsiPortSettings(MPT_ADAPTER *ioc pPP0->Capabilities = le32_to_cpu(pPP0->Capabilities); pPP0->PhysicalInterface = le32_to_cpu(pPP0->PhysicalInterface); - if ( (pPP0->Capabilities & MPI_SCSIPORTPAGE0_CAP_QAS) == 0 ) + if ( (pPP0->Capabilities & MPI_SCSIPORTPAGE0_CAP_QAS) == 0 ) { ioc->spi_data.noQas |= MPT_TARGET_NO_NEGO_QAS; - + dinitprintk((KERN_INFO MYNAM " :%s noQas due to Capabilities=%x\n", + ioc->name, pPP0->Capabilities)); + } ioc->spi_data.maxBusWidth = pPP0->Capabilities & MPI_SCSIPORTPAGE0_CAP_WIDE ? 1 : 0; data = pPP0->Capabilities & MPI_SCSIPORTPAGE0_CAP_MAX_SYNC_OFFSET_MASK; if (data) { @@ -4907,8 +4935,8 @@ int mpt_toolbox(MPT_ADAPTER *ioc, CONFIGPARMS *pCfg) { ToolboxIstwiReadWriteRequest_t *pReq; - struct pci_dev *pdev; MPT_FRAME_HDR *mf; + struct pci_dev *pdev; unsigned long flags; int rc; u32 flagsLength; @@ -5226,12 +5254,6 @@ procmpt_version_read(char *buf, char **s if (drvname) len += sprintf(buf+len, " Fusion MPT %s driver\n", drvname); - /* - * Handle isense special case, because it - * doesn't do a formal mpt_register call. - */ - if (isense_idx == ii) - len += sprintf(buf+len, " Fusion MPT isense driver\n"); } } @@ -5286,7 +5308,7 @@ procmpt_iocinfo_read(char *buf, char **s len += sprintf(buf+len, " MinBlockSize = 0x%02x bytes\n", 4*ioc->facts.BlockSize); len += sprintf(buf+len, " RequestFrames @ 0x%p (Dma @ 0x%p)\n", - (void *)ioc->req_alloc, (void *)(ulong)ioc->req_alloc_dma); + (void *)ioc->req_frames, (void *)(ulong)ioc->req_frames_dma); /* * Rounding UP to nearest 4-kB boundary here... */ @@ -5298,8 +5320,8 @@ procmpt_iocinfo_read(char *buf, char **s 4*ioc->facts.RequestFrameSize, ioc->facts.GlobalCredits); - len += sprintf(buf+len, " ReplyFrames @ 0x%p (Dma @ 0x%p)\n", - (void *)ioc->reply_alloc, (void *)(ulong)ioc->reply_alloc_dma); + len += sprintf(buf+len, " Frames @ 0x%p (Dma @ 0x%p)\n", + (void *)ioc->alloc, (void *)(ulong)ioc->alloc_dma); sz = (ioc->reply_sz * ioc->reply_depth) + 128; len += sprintf(buf+len, " {CurRepSz=%d} x {CurRepDepth=%d} = %d bytes ^= 0x%x\n", ioc->reply_sz, ioc->reply_depth, ioc->reply_sz*ioc->reply_depth, sz); @@ -5592,7 +5614,7 @@ ProcessEventNotification(MPT_ADAPTER *io } evStr = EventDescriptionStr(event, evData0); - dprintk((MYIOC_s_INFO_FMT "MPT event (%s=%02Xh) detected!\n", + devtprintk((MYIOC_s_INFO_FMT "MPT event (%s=%02Xh) detected!\n", ioc->name, evStr, event)); @@ -5664,7 +5686,7 @@ ProcessEventNotification(MPT_ADAPTER *io */ for (ii=MPT_MAX_PROTOCOL_DRIVERS-1; ii; ii--) { if (MptEvHandlers[ii]) { - dprintk((MYIOC_s_INFO_FMT "Routing Event to event handler #%d\n", + devtprintk((MYIOC_s_INFO_FMT "Routing Event to event handler #%d\n", ioc->name, ii)); r += (*(MptEvHandlers[ii]))(ioc, pEventReply); handlers++; @@ -5677,8 +5699,8 @@ ProcessEventNotification(MPT_ADAPTER *io */ if (pEventReply->AckRequired == MPI_EVENT_NOTIFICATION_ACK_REQUIRED) { if ((ii = SendEventAck(ioc, pEventReply)) != 0) { - printk(MYIOC_s_WARN_FMT "SendEventAck returned %d\n", - ioc->name, ii); + devtprintk((MYIOC_s_WARN_FMT "SendEventAck returned %d\n", + ioc->name, ii)); } } @@ -5702,9 +5724,8 @@ mpt_fc_log_info(MPT_ADAPTER *ioc, u32 lo "FC Link", "Context Manager", "Invalid Field Offset", "State Change Info" }; u8 subcl = (log_info >> 24) & 0x7; -// u32 SubCl = log_info & 0x27000000; - printk(MYIOC_s_INFO_FMT "LogInfo(0x%08x): SubCl={%s}", + printk(MYIOC_s_INFO_FMT "LogInfo(0x%08x): SubCl={%s}\n", ioc->name, log_info, subcl_str[subcl]); } @@ -5907,50 +5928,6 @@ mpt_sp_ioc_info(MPT_ADAPTER *ioc, u32 io } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/** - * mpt_register_ascqops_strings - Register SCSI ASC/ASCQ and SCSI - * OpCode strings from the (optional) isense module. - * @ascqTable: Pointer to ASCQ_Table_t structure - * @ascqtbl_sz: Number of entries in ASCQ_Table - * @opsTable: Pointer to array of SCSI OpCode strings (char pointers) - * - * Specialized driver registration routine for the isense driver. - */ -int -mpt_register_ascqops_strings(void *ascqTable, int ascqtbl_sz, const char **opsTable) -{ - int r = 0; - - if (ascqTable && ascqtbl_sz && opsTable) { - mpt_v_ASCQ_TablePtr = ascqTable; - mpt_ASCQ_TableSz = ascqtbl_sz; - mpt_ScsiOpcodesPtr = opsTable; - printk(KERN_INFO MYNAM ": English readable SCSI-3 strings enabled:-)\n"); - isense_idx = last_drv_idx; - r = 1; - } - return r; -} - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/** - * mpt_deregister_ascqops_strings - Deregister SCSI ASC/ASCQ and SCSI - * OpCode strings from the isense driver. - * - * Specialized driver deregistration routine for the isense driver. - */ -void -mpt_deregister_ascqops_strings(void) -{ - mpt_v_ASCQ_TablePtr = NULL; - mpt_ASCQ_TableSz = 0; - mpt_ScsiOpcodesPtr = NULL; - printk(KERN_INFO MYNAM ": English readable SCSI-3 strings disabled)-:\n"); - isense_idx = -1; -} - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ - EXPORT_SYMBOL(ioc_list); EXPORT_SYMBOL(mpt_proc_root_dir); EXPORT_SYMBOL(DmpService); @@ -5982,13 +5959,6 @@ EXPORT_SYMBOL(mpt_read_ioc_pg_3); EXPORT_SYMBOL(mpt_alloc_fw_memory); EXPORT_SYMBOL(mpt_free_fw_memory); -EXPORT_SYMBOL(mpt_register_ascqops_strings); -EXPORT_SYMBOL(mpt_deregister_ascqops_strings); -EXPORT_SYMBOL(mpt_v_ASCQ_TablePtr); -EXPORT_SYMBOL(mpt_ASCQ_TableSz); -EXPORT_SYMBOL(mpt_ScsiOpcodesPtr); - - static struct pci_driver mptbase_driver = { .name = "mptbase", .id_table = mptbase_pci_table, --- linux-2.6.9-rc1/drivers/message/fusion/mptbase.h 2004-08-24 00:02:20.000000000 -0700 +++ 25/drivers/message/fusion/mptbase.h 2004-09-02 20:51:51.000000000 -0700 @@ -60,8 +60,6 @@ #include #include -#include "scsi3.h" /* SCSI defines */ - #include "lsi/mpi_type.h" #include "lsi/mpi.h" /* Fusion MPI(nterface) basic defs */ #include "lsi/mpi_ioc.h" /* Fusion MPT IOC(ontroller) defs */ @@ -85,8 +83,8 @@ #define COPYRIGHT "Copyright (c) 1999-2004 " MODULEAUTHOR #endif -#define MPT_LINUX_VERSION_COMMON "3.01.09" -#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.01.09" +#define MPT_LINUX_VERSION_COMMON "3.01.16" +#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.01.16" #define WHAT_MAGIC_STRING "@" "(" "#" ")" #define show_mptmod_ver(s,ver) \ @@ -320,17 +318,6 @@ typedef struct _Q_TRACKER { struct _Q_ITEM *tail; } Q_TRACKER; -typedef struct _MPT_DONE_Q { - struct _MPT_DONE_Q *forw; - struct _MPT_DONE_Q *back; - void *argp; -} MPT_DONE_Q; - -typedef struct _DONE_Q_TRACKER { - MPT_DONE_Q *head; - MPT_DONE_Q *tail; -} DONE_Q_TRACKER; - /* * Chip-specific stuff... FC929 delineates break between * FC and Parallel SCSI parts. Do NOT re-order. @@ -403,6 +390,11 @@ typedef struct _ScsiCmndTracker { void *tail; } ScsiCmndTracker; +/* VirtDevice negoFlags field */ +#define MPT_TARGET_NO_NEGO_WIDE 0x01 +#define MPT_TARGET_NO_NEGO_SYNC 0x02 +#define MPT_TARGET_NO_NEGO_QAS 0x04 +#define MPT_TAPE_NEGO_IDP 0x08 /* * VirtDevice - FC LUN device or SCSI target device @@ -420,8 +412,8 @@ typedef struct _VirtDevice { u8 bus_id; u8 minSyncFactor; /* 0xFF is async */ u8 maxOffset; /* 0 if async */ - u8 maxWidth; /* 0 if narrow, 1 if wide*/ - u8 negoFlags; /* bit field, 0 if WDTR/SDTR/QAS allowed */ + u8 maxWidth; /* 0 if narrow, 1 if wide */ + u8 negoFlags; /* bit field, see above */ u8 raidVolume; /* set, if RAID Volume */ u8 type; /* byte 0 of Inquiry data */ u8 cflags; /* controller flags */ @@ -460,10 +452,6 @@ typedef struct _VirtDevice { #define MPT_TARGET_FLAGS_VALID_56 0x10 #define MPT_TARGET_FLAGS_SAF_TE_ISSUED 0x20 -#define MPT_TARGET_NO_NEGO_WIDE 0x01 -#define MPT_TARGET_NO_NEGO_SYNC 0x02 -#define MPT_TARGET_NO_NEGO_QAS 0x04 - typedef struct _VirtDevTracker { struct _VirtDevice *head; struct _VirtDevice *tail; @@ -523,6 +511,7 @@ typedef struct _MPT_IOCTL { u8 target; /* target for reset */ void *tmPtr; struct timer_list TMtimer; /* timer function for this adapter */ + struct semaphore sem_ioc; } MPT_IOCTL; /* @@ -600,25 +589,30 @@ typedef struct _MPT_ADAPTER int alloc_total; u32 last_state; int active; - u8 *fifo_pool; /* dma pool for fifo's */ - dma_addr_t fifo_pool_dma; - int fifo_pool_sz; /* allocated size */ - u8 *chain_alloc; /* chain buffer alloc ptr */ - dma_addr_t chain_alloc_dma; - int chain_alloc_sz; - u8 *reply_alloc; /* Reply frames alloc ptr */ - dma_addr_t reply_alloc_dma; + u8 *alloc; /* frames alloc ptr */ + dma_addr_t alloc_dma; + u32 alloc_sz; MPT_FRAME_HDR *reply_frames; /* Reply msg frames - rounded up! */ - dma_addr_t reply_frames_dma; u32 reply_frames_low_dma; int reply_depth; /* Num Allocated reply frames */ int reply_sz; /* Reply frame size */ + int num_chain; /* Number of chain buffers */ + /* Pool of buffers for chaining. ReqToChain + * and ChainToChain track index of chain buffers. + * ChainBuffer (DMA) virt/phys addresses. + * FreeChainQ (lock) locking mechanisms. + */ + int *ReqToChain; + int *RequestNB; + int *ChainToChain; + u8 *ChainBuffer; + dma_addr_t ChainBufferDMA; + MPT_Q_TRACKER FreeChainQ; + spinlock_t FreeChainQlock; CHIP_TYPE chip_type; /* We (host driver) get to manage our own RequestQueue! */ - u8 *req_alloc; /* Request frames alloc ptr */ - dma_addr_t req_alloc_dma; - MPT_FRAME_HDR *req_frames; /* Request msg frames - rounded up! */ dma_addr_t req_frames_dma; + MPT_FRAME_HDR *req_frames; /* Request msg frames - rounded up! */ u32 req_frames_low_dma; int req_depth; /* Number of request frames */ int req_sz; /* Request frame size (bytes) */ @@ -661,6 +655,7 @@ typedef struct _MPT_ADAPTER #else u32 mfcnt; #endif + u32 NB_for_64_byte_frame; u32 hs_req[MPT_MAX_FRAME_SIZE/sizeof(u32)]; u16 hs_reply[MPT_MAX_FRAME_SIZE/sizeof(u16)]; IOCFactsReply_t facts; @@ -674,8 +669,10 @@ typedef struct _MPT_ADAPTER u8 FirstWhoInit; u8 upload_fw; /* If set, do a fw upload */ u8 reload_fw; /* Force a FW Reload on next reset */ - u8 pad1[5]; + u8 NBShiftFactor; /* NB Shift Factor based on Block Size (Facts) */ + u8 pad1[4]; struct list_head list; + struct net_device *netdev; } MPT_ADAPTER; @@ -757,10 +754,10 @@ typedef struct _mpt_sge { #define dexitprintk(x) #endif -#ifdef MPT_DEBUG_RESET -#define drsprintk(x) printk x +#if defined MPT_DEBUG_FAIL || defined (MPT_DEBUG_SG) +#define dfailprintk(x) printk x #else -#define drsprintk(x) +#define dfailprintk(x) #endif #ifdef MPT_DEBUG_HANDSHAKE @@ -769,11 +766,34 @@ typedef struct _mpt_sge { #define dhsprintk(x) #endif +#ifdef MPT_DEBUG_EVENTS +#define devtprintk(x) printk x +#else +#define devtprintk(x) +#endif + +#ifdef MPT_DEBUG_RESET +#define drsprintk(x) printk x +#else +#define drsprintk(x) +#endif + //#if defined(MPT_DEBUG) || defined(MPT_DEBUG_MSG_FRAME) #if defined(MPT_DEBUG_MSG_FRAME) #define dmfprintk(x) printk x +#define DBG_DUMP_REQUEST_FRAME(mfp) \ + { int i, n = 24; \ + u32 *m = (u32 *)(mfp); \ + for (i=0; i> 16; \ + printk("TM_REPLY MessageLength=%d:\n", n); \ + for (i=0; iid])) + if (down_trylock(&ioc->ioctl->sem_ioc)) rc = -EAGAIN; } else { - if (down_interruptible(&mptctl_syscall_sem_ioc[ioc->id])) + if (down_interruptible(&ioc->ioctl->sem_ioc)) rc = -ERESTARTSYS; } dctlprintk((KERN_INFO MYNAM "::mptctl_syscall_down return %d\n", rc)); @@ -445,7 +447,7 @@ static int mptctl_bus_reset(MPT_IOCTL *i mptctl_free_tm_flags(ioctl->ioc); del_timer(&ioctl->TMtimer); - mpt_free_msg_frame(mptctl_id, ioctl->ioc, mf); + mpt_free_msg_frame(ioctl->ioc, mf); ioctl->tmPtr = NULL; } @@ -482,7 +484,7 @@ mptctl_free_tm_flags(MPT_ADAPTER *ioc) spin_lock_irqsave(&ioc->FreeQlock, flags); - hd->tmState = TM_STATE_ERROR; + hd->tmState = TM_STATE_NONE; hd->tmPending = 0; spin_unlock_irqrestore(&ioc->FreeQlock, flags); @@ -520,7 +522,7 @@ mptctl_ioc_reset(MPT_ADAPTER *ioc, int r if (ioctl && (ioctl->status & MPT_IOCTL_STATUS_TMTIMER_ACTIVE)){ ioctl->status &= ~MPT_IOCTL_STATUS_TMTIMER_ACTIVE; del_timer(&ioctl->TMtimer); - mpt_free_msg_frame(mptctl_id, ioc, ioctl->tmPtr); + mpt_free_msg_frame(ioc, ioctl->tmPtr); } } else { @@ -630,8 +632,7 @@ mptctl_ioctl(struct inode *inode, struct else ret = -EINVAL; - - up(&mptctl_syscall_sem_ioc[iocp->id]); + up(&iocp->ioctl->sem_ioc); return ret; } @@ -1807,7 +1808,6 @@ mptctl_do_mpt_command (struct mpt_ioctl_ struct buflist bufOut; /* data Out buffer */ dma_addr_t dma_addr_in; dma_addr_t dma_addr_out; - int dir; /* PCI data direction */ int sgSize = 0; /* Num SG elements */ int iocnum, flagsLength; int sz, rc = 0; @@ -2117,9 +2117,9 @@ mptctl_do_mpt_command (struct mpt_ioctl_ /* Set up the dataOut memory allocation */ if (karg.dataOutSize > 0) { - dir = PCI_DMA_TODEVICE; if (karg.dataInSize > 0) { flagsLength = ( MPI_SGE_FLAGS_SIMPLE_ELEMENT | + MPI_SGE_FLAGS_END_OF_BUFFER | MPI_SGE_FLAGS_DIRECTION | mpt_addr_size() ) << MPI_SGE_FLAGS_SHIFT; @@ -2158,7 +2158,6 @@ mptctl_do_mpt_command (struct mpt_ioctl_ } if (karg.dataInSize > 0) { - dir = PCI_DMA_FROMDEVICE; flagsLength = MPT_SGE_FLAGS_SSIMPLE_READ; flagsLength |= karg.dataInSize; @@ -2196,6 +2195,7 @@ mptctl_do_mpt_command (struct mpt_ioctl_ add_timer(&ioc->ioctl->timer); if (hdr->Function == MPI_FUNCTION_SCSI_TASK_MGMT) { + DBG_DUMP_TM_REQUEST_FRAME((u32 *)mf); rc = mpt_send_handshake_request(mptctl_id, ioc, sizeof(SCSITaskMgmt_t), (u32*)mf, CAN_SLEEP); if (rc == 0) { @@ -2206,7 +2206,7 @@ mptctl_do_mpt_command (struct mpt_ioctl_ del_timer(&ioc->ioctl->timer); ioc->ioctl->status &= ~MPT_IOCTL_STATUS_TIMER_ACTIVE; ioc->ioctl->status |= MPT_IOCTL_STATUS_TM_FAILED; - mpt_free_msg_frame(mptctl_id, ioc, mf); + mpt_free_msg_frame(ioc, mf); } } else { mpt_put_msg_frame(mptctl_id, ioc, mf); @@ -2324,7 +2324,7 @@ done_free_mem: * otherwise, failure occured after mf acquired. */ if (mf) - mpt_free_msg_frame(mptctl_id, ioc, mf); + mpt_free_msg_frame(ioc, mf); return rc; } @@ -2738,7 +2738,7 @@ compat_mptfwxfer_ioctl(unsigned int fd, ret = mptctl_do_fw_download(kfw.iocnum, kfw.bufp, kfw.fwlen); - up(&mptctl_syscall_sem_ioc[iocp->id]); + up(&iocp->ioctl->sem_ioc); return ret; } @@ -2792,55 +2792,91 @@ compat_mpt_command(unsigned int fd, unsi */ ret = mptctl_do_mpt_command (karg, &uarg->MF); - up(&mptctl_syscall_sem_ioc[iocp->id]); + up(&iocp->ioctl->sem_ioc); return ret; } #endif + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -int __init mptctl_init(void) +/* + * mptctl_probe - Installs ioctl devices per bus. + * @pdev: Pointer to pci_dev structure + * + * Returns 0 for success, non-zero for failure. + * + */ + +static int +mptctl_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int err; - int i; - int where = 1; int sz; u8 *mem; - MPT_ADAPTER *ioc = NULL; - int iocnum; + MPT_ADAPTER *ioc = pci_get_drvdata(pdev); - show_mptmod_ver(my_NAME, my_VERSION); + /* + * Allocate and inite a MPT_IOCTL structure + */ + sz = sizeof (MPT_IOCTL); + mem = kmalloc(sz, GFP_KERNEL); + if (mem == NULL) { + err = -ENOMEM; + goto out_fail; + } - for (i=0; iioctl = (MPT_IOCTL *) mem; + ioc->ioctl->ioc = ioc; + init_timer (&ioc->ioctl->timer); + ioc->ioctl->timer.data = (unsigned long) ioc->ioctl; + ioc->ioctl->timer.function = mptctl_timer_expired; + init_timer (&ioc->ioctl->TMtimer); + ioc->ioctl->TMtimer.data = (unsigned long) ioc->ioctl; + ioc->ioctl->TMtimer.function = mptctl_timer_expired; + sema_init(&ioc->ioctl->sem_ioc, 1); + return 0; - ioc = NULL; - if (((iocnum = mpt_verify_adapter(i, &ioc)) < 0) || - (ioc == NULL)) { - continue; - } - else { - /* This adapter instance is found. - * Allocate and inite a MPT_IOCTL structure - */ - sz = sizeof (MPT_IOCTL); - mem = kmalloc(sz, GFP_KERNEL); - if (mem == NULL) { - err = -ENOMEM; - goto out_fail; - } - - memset(mem, 0, sz); - ioc->ioctl = (MPT_IOCTL *) mem; - ioc->ioctl->ioc = ioc; - init_timer (&ioc->ioctl->timer); - ioc->ioctl->timer.data = (unsigned long) ioc->ioctl; - ioc->ioctl->timer.function = mptctl_timer_expired; - init_timer (&ioc->ioctl->TMtimer); - ioc->ioctl->TMtimer.data = (unsigned long) ioc->ioctl; - ioc->ioctl->TMtimer.function = mptctl_timer_expired; - } +out_fail: + + mptctl_remove(pdev); + return err; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mptctl_remove - Removed ioctl devices + * @pdev: Pointer to pci_dev structure + * + * + */ +static void +mptctl_remove(struct pci_dev *pdev) +{ + MPT_ADAPTER *ioc = pci_get_drvdata(pdev); + + kfree ( ioc->ioctl ); +} + +static struct mpt_pci_driver mptctl_driver = { + .probe = mptctl_probe, + .remove = mptctl_remove, +}; + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int __init mptctl_init(void) +{ + int err; + int where = 1; + + show_mptmod_ver(my_NAME, my_VERSION); + + if(mpt_device_driver_register(&mptctl_driver, + MPTCTL_DRIVER) != 0 ) { + dprintk((KERN_INFO MYNAM + ": failed to register dd callbacks\n")); } #ifdef CONFIG_COMPAT @@ -2922,29 +2958,14 @@ out_fail: unregister_ioctl32_conversion(HP_GETTARGETINFO); #endif - for (i=0; iioctl) { - kfree ( ioc->ioctl ); - ioc->ioctl = NULL; - } - } - } + mpt_device_driver_deregister(MPTCTL_DRIVER); + return err; } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -void mptctl_exit(void) +static void mptctl_exit(void) { - int i; - MPT_ADAPTER *ioc; - int iocnum; - misc_deregister(&mptctl_miscdev); printk(KERN_INFO MYNAM ": Deregistered /dev/%s @ (major,minor=%d,%d)\n", mptctl_miscdev.name, MISC_MAJOR, mptctl_miscdev.minor); @@ -2957,6 +2978,8 @@ void mptctl_exit(void) mpt_deregister(mptctl_id); printk(KERN_INFO MYNAM ": Deregistered from Fusion MPT base driver\n"); + mpt_device_driver_deregister(MPTCTL_DRIVER); + #ifdef CONFIG_COMPAT unregister_ioctl32_conversion(MPTIOCINFO); unregister_ioctl32_conversion(MPTIOCINFO1); @@ -2973,20 +2996,6 @@ void mptctl_exit(void) unregister_ioctl32_conversion(HP_GETTARGETINFO); #endif - /* Free allocated memory */ - for (i=0; iioctl) { - kfree ( ioc->ioctl ); - ioc->ioctl = NULL; - } - } - } } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ --- linux-2.6.9-rc1/drivers/message/fusion/mptlan.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/message/fusion/mptlan.c 2004-09-02 20:51:51.000000000 -0700 @@ -177,11 +177,9 @@ static int LanCtx = -1; static u32 max_buckets_out = 127; static u32 tx_max_out_p = 127 - 16; -static struct net_device *mpt_landev[MPT_MAX_ADAPTERS+1]; - #ifdef QLOGIC_NAA_WORKAROUND static struct NAA_Hosed *mpt_bad_naa = NULL; -rwlock_t bad_naa_lock; +rwlock_t bad_naa_lock = RW_LOCK_UNLOCKED; #endif /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -203,7 +201,7 @@ extern int mpt_lan_index; static int lan_reply (MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *reply) { - struct net_device *dev = mpt_landev[ioc->id]; + struct net_device *dev = ioc->netdev; int FreeReqFrame = 0; dioprintk((KERN_INFO MYNAM ": %s/%s: Got reply.\n", @@ -336,7 +334,7 @@ lan_reply (MPT_ADAPTER *ioc, MPT_FRAME_H static int mpt_lan_ioc_reset(MPT_ADAPTER *ioc, int reset_phase) { - struct net_device *dev = mpt_landev[ioc->id]; + struct net_device *dev = ioc->netdev; struct mpt_lan_priv *priv = netdev_priv(dev); dlprintk((KERN_INFO MYNAM ": IOC %s_reset routed to LAN driver!\n", @@ -1334,7 +1332,7 @@ mpt_lan_post_receive_buckets(void *dev_i if (pSimple == NULL) { /**/ printk (KERN_WARNING MYNAM "/%s: No buckets posted\n", /**/ __FUNCTION__); - mpt_free_msg_frame(LanCtx, mpt_dev, mf); + mpt_free_msg_frame(mpt_dev, mf); goto out; } @@ -1451,20 +1449,74 @@ mpt_register_lan_device (MPT_ADAPTER *mp return dev; } -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -static int __init mpt_lan_init (void) +static int +mptlan_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - struct net_device *dev; - MPT_ADAPTER *p; - int i, j; + MPT_ADAPTER *ioc = pci_get_drvdata(pdev); + struct net_device *dev; + int i; + + for (i = 0; i < ioc->facts.NumberOfPorts; i++) { + printk(KERN_INFO MYNAM ": %s: PortNum=%x, " + "ProtocolFlags=%02Xh (%c%c%c%c)\n", + ioc->name, ioc->pfacts[i].PortNumber, + ioc->pfacts[i].ProtocolFlags, + MPT_PROTOCOL_FLAGS_c_c_c_c( + ioc->pfacts[i].ProtocolFlags)); + + if (!(ioc->pfacts[i].ProtocolFlags & + MPI_PORTFACTS_PROTOCOL_LAN)) { + printk(KERN_INFO MYNAM ": %s: Hmmm... LAN protocol " + "seems to be disabled on this adapter port!\n", + ioc->name); + continue; + } - show_mptmod_ver(LANAME, LANVER); + dev = mpt_register_lan_device(ioc, i); + if (!dev) { + printk(KERN_ERR MYNAM ": %s: Unable to register " + "port%d as a LAN device\n", ioc->name, + ioc->pfacts[i].PortNumber); + continue; + } + + printk(KERN_INFO MYNAM ": %s: Fusion MPT LAN device " + "registered as '%s'\n", ioc->name, dev->name); + printk(KERN_INFO MYNAM ": %s/%s: " + "LanAddr = %02X:%02X:%02X:%02X:%02X:%02X\n", + IOC_AND_NETDEV_NAMES_s_s(dev), + dev->dev_addr[0], dev->dev_addr[1], + dev->dev_addr[2], dev->dev_addr[3], + dev->dev_addr[4], dev->dev_addr[5]); + + ioc->netdev = dev; -#ifdef QLOGIC_NAA_WORKAROUND - /* Init the global r/w lock for the bad_naa list. We want to do this - before any boards are initialized and may be used. */ - rwlock_init(&bad_naa_lock); -#endif + return 0; + } + + return -ENODEV; +} + +static void +mptlan_remove(struct pci_dev *pdev) +{ + MPT_ADAPTER *ioc = pci_get_drvdata(pdev); + struct net_device *dev = ioc->netdev; + + if(dev != NULL) { + unregister_netdev(dev); + free_netdev(dev); + } +} + +static struct mpt_pci_driver mptlan_driver = { + .probe = mptlan_probe, + .remove = mptlan_remove, +}; + +static int __init mpt_lan_init (void) +{ + show_mptmod_ver(LANAME, LANVER); if ((LanCtx = mpt_register(lan_reply, MPTLAN_DRIVER)) <= 0) { printk (KERN_ERR MYNAM ": Failed to register with MPT base driver\n"); @@ -1476,88 +1528,32 @@ static int __init mpt_lan_init (void) dlprintk((KERN_INFO MYNAM ": assigned context of %d\n", LanCtx)); - if (mpt_reset_register(LanCtx, mpt_lan_ioc_reset) == 0) { - dlprintk((KERN_INFO MYNAM ": Registered for IOC reset notifications\n")); - } else { + if (mpt_reset_register(LanCtx, mpt_lan_ioc_reset)) { printk(KERN_ERR MYNAM ": Eieee! unable to register a reset " "handler with mptbase! The world is at an end! " "Everything is fading to black! Goodbye.\n"); return -EBUSY; } - for (j = 0; j < MPT_MAX_ADAPTERS; j++) { - mpt_landev[j] = NULL; - } - - list_for_each_entry(p, &ioc_list, list) { - for (i = 0; i < p->facts.NumberOfPorts; i++) { - printk (KERN_INFO MYNAM ": %s: PortNum=%x, ProtocolFlags=%02Xh (%c%c%c%c)\n", - p->name, - p->pfacts[i].PortNumber, - p->pfacts[i].ProtocolFlags, - MPT_PROTOCOL_FLAGS_c_c_c_c(p->pfacts[i].ProtocolFlags)); - - if (!(p->pfacts[i].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN)) { - printk (KERN_INFO MYNAM ": %s: Hmmm... LAN protocol seems to be disabled on this adapter port!\n", - p->name); - continue; - } - - dev = mpt_register_lan_device (p, i); - if (!dev) { - printk (KERN_ERR MYNAM ": %s: Unable to register port%d as a LAN device\n", - p->name, - p->pfacts[i].PortNumber); - } - printk (KERN_INFO MYNAM ": %s: Fusion MPT LAN device registered as '%s'\n", - p->name, dev->name); - printk (KERN_INFO MYNAM ": %s/%s: LanAddr = %02X:%02X:%02X:%02X:%02X:%02X\n", - IOC_AND_NETDEV_NAMES_s_s(dev), - dev->dev_addr[0], dev->dev_addr[1], - dev->dev_addr[2], dev->dev_addr[3], - dev->dev_addr[4], dev->dev_addr[5]); -// printk (KERN_INFO MYNAM ": %s/%s: Max_TX_outstanding = %d\n", -// IOC_AND_NETDEV_NAMES_s_s(dev), -// NETDEV_TO_LANPRIV_PTR(dev)->tx_max_out); - j = p->id; - mpt_landev[j] = dev; - dlprintk((KERN_INFO MYNAM "/init: dev_addr=%p, mpt_landev[%d]=%p\n", - dev, j, mpt_landev[j])); - - } - } - + dlprintk((KERN_INFO MYNAM ": Registered for IOC reset notifications\n")); + + if (mpt_device_driver_register(&mptlan_driver, MPTLAN_DRIVER)) + dprintk((KERN_INFO MYNAM ": failed to register dd callbacks\n")); return 0; } -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ static void __exit mpt_lan_exit(void) { - int i; - + mpt_device_driver_deregister(MPTLAN_DRIVER); mpt_reset_deregister(LanCtx); - for (i = 0; mpt_landev[i] != NULL; i++) { - struct net_device *dev = mpt_landev[i]; - - printk (KERN_INFO ": %s/%s: Fusion MPT LAN device unregistered\n", - IOC_AND_NETDEV_NAMES_s_s(dev)); - unregister_netdev(dev); - free_netdev(dev); - mpt_landev[i] = NULL; - } - if (LanCtx >= 0) { mpt_deregister(LanCtx); LanCtx = -1; mpt_lan_index = 0; } - - /* deregister any send/receive handler structs. I2Oism? */ } -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ - module_init(mpt_lan_init); module_exit(mpt_lan_exit); --- linux-2.6.9-rc1/drivers/message/fusion/mptscsih.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/drivers/message/fusion/mptscsih.c 2004-09-03 00:57:24.639293520 -0700 @@ -86,7 +86,6 @@ #include "mptbase.h" #include "mptscsih.h" -#include "isense.h" /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ #define my_NAME "Fusion MPT SCSI Host driver" @@ -100,6 +99,7 @@ MODULE_LICENSE("GPL"); /* Set string for command line args from insmod */ #ifdef MODULE char *mptscsih = NULL; +MODULE_PARM(mptscsih, "s"); #endif /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -114,6 +114,7 @@ typedef struct _BIG_SENSE_BUF { #define MPT_SCANDV_SOME_ERROR (0x00000004) #define MPT_SCANDV_SELECTION_TIMEOUT (0x00000008) #define MPT_SCANDV_ISSUE_SENSE (0x00000010) +#define MPT_SCANDV_FALLBACK (0x00000020) #define MPT_SCANDV_MAX_RETRIES (10) @@ -161,15 +162,12 @@ static int mptscsih_io_done(MPT_ADAPTER static void mptscsih_report_queue_full(struct scsi_cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq); static int mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r); -static int mptscsih_AddSGE(MPT_SCSI_HOST *hd, struct scsi_cmnd *SCpnt, +static int mptscsih_AddSGE(MPT_ADAPTER *ioc, struct scsi_cmnd *SCpnt, SCSIIORequest_t *pReq, int req_idx); -static void mptscsih_freeChainBuffers(MPT_SCSI_HOST *hd, int req_idx); -static int mptscsih_initChainBuffers (MPT_SCSI_HOST *hd, int init); +static void mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx); static void copy_sense_data(struct scsi_cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply); static int mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd); static u32 SCPNT_TO_LOOKUP_IDX(struct scsi_cmnd *sc); -static MPT_FRAME_HDR *mptscsih_search_pendingQ(MPT_SCSI_HOST *hd, int scpnt_idx); -static void post_pendingQ_commands(MPT_SCSI_HOST *hd); static int mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout, int sleepFlag); static int mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, ulong timeout, int sleepFlag); @@ -178,7 +176,7 @@ static int mptscsih_ioc_reset(MPT_ADAPTE static int mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply); static void mptscsih_initTarget(MPT_SCSI_HOST *hd, int bus_id, int target_id, u8 lun, char *data, int dlen); -void mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtDevice *target, char byte56); +static void mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtDevice *target, char byte56); static void mptscsih_set_dvflags(MPT_SCSI_HOST *hd, SCSIIORequest_t *pReq); static void mptscsih_setDevicePage1Flags (u8 width, u8 factor, u8 offset, int *requestedPtr, int *configurationPtr, u8 flags); static void mptscsih_no_negotiate(MPT_SCSI_HOST *hd, int target_id); @@ -246,6 +244,7 @@ static struct work_struct mptscsih_dvTas static DECLARE_WAIT_QUEUE_HEAD (scandv_waitq); static int scandv_wait_done = 1; + /* Driver default setup */ static struct mptscsih_driver_setup @@ -323,44 +322,47 @@ mptscsih_add_chain(char *pAddr, u8 next, /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* - * mptscsih_getFreeChainBuffes - Function to get a free chain + * mptscsih_getFreeChainBuffer - Function to get a free chain * from the MPT_SCSI_HOST FreeChainQ. - * @hd: Pointer to the MPT_SCSI_HOST instance + * @ioc: Pointer to MPT_ADAPTER structure * @req_idx: Index of the SCSI IO request frame. (output) * * return SUCCESS or FAILED */ static inline int -mptscsih_getFreeChainBuffer(MPT_SCSI_HOST *hd, int *retIndex) +mptscsih_getFreeChainBuffer(MPT_ADAPTER *ioc, int *retIndex) { MPT_FRAME_HDR *chainBuf; unsigned long flags; int rc; int chain_idx; - spin_lock_irqsave(&hd->ioc->FreeQlock, flags); - if (!Q_IS_EMPTY(&hd->FreeChainQ)) { + dsgprintk((MYIOC_s_INFO_FMT "getFreeChainBuffer called\n", + ioc->name)); + spin_lock_irqsave(&ioc->FreeQlock, flags); + if (!Q_IS_EMPTY(&ioc->FreeChainQ)) { int offset; - chainBuf = hd->FreeChainQ.head; + chainBuf = ioc->FreeChainQ.head; Q_DEL_ITEM(&chainBuf->u.frame.linkage); - offset = (u8 *)chainBuf - (u8 *)hd->ChainBuffer; - chain_idx = offset / hd->ioc->req_sz; + offset = (u8 *)chainBuf - (u8 *)ioc->ChainBuffer; + chain_idx = offset / ioc->req_sz; rc = SUCCESS; + dsgprintk((MYIOC_s_INFO_FMT "getFreeChainBuffer (index %d), got buf=%p\n", + ioc->name, *retIndex, chainBuf)); } else { rc = FAILED; chain_idx = MPT_HOST_NO_CHAIN; + dfailprintk((MYIOC_s_ERR_FMT "getFreeChainBuffer failed\n", + ioc->name)); } - spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); + spin_unlock_irqrestore(&ioc->FreeQlock, flags); *retIndex = chain_idx; - dsgprintk((MYIOC_s_INFO_FMT "getFreeChainBuffer (index %d), got buf=%p\n", - hd->ioc->name, *retIndex, chainBuf)); - return rc; } /* mptscsih_getFreeChainBuffer() */ @@ -368,14 +370,14 @@ mptscsih_getFreeChainBuffer(MPT_SCSI_HOS /* * mptscsih_AddSGE - Add a SGE (plus chain buffers) to the * SCSIIORequest_t Message Frame. - * @hd: Pointer to MPT_SCSI_HOST structure + * @ioc: Pointer to MPT_ADAPTER structure * @SCpnt: Pointer to scsi_cmnd structure * @pReq: Pointer to SCSIIORequest_t structure * * Returns ... */ static int -mptscsih_AddSGE(MPT_SCSI_HOST *hd, struct scsi_cmnd *SCpnt, +mptscsih_AddSGE(MPT_ADAPTER *ioc, struct scsi_cmnd *SCpnt, SCSIIORequest_t *pReq, int req_idx) { char *psge; @@ -391,6 +393,7 @@ mptscsih_AddSGE(MPT_SCSI_HOST *hd, struc int newIndex; int ii; dma_addr_t v2; + u32 RequestNB; sgdir = le32_to_cpu(pReq->Control) & MPI_SCSIIO_CONTROL_DATADIRECTION_MASK; if (sgdir == MPI_SCSIIO_CONTROL_WRITE) { @@ -400,25 +403,25 @@ mptscsih_AddSGE(MPT_SCSI_HOST *hd, struc } psge = (char *) &pReq->SGL; - frm_sz = hd->ioc->req_sz; + frm_sz = ioc->req_sz; /* Map the data portion, if any. * sges_left = 0 if no data transfer. */ if ( (sges_left = SCpnt->use_sg) ) { - sges_left = pci_map_sg(hd->ioc->pcidev, + sges_left = pci_map_sg(ioc->pcidev, (struct scatterlist *) SCpnt->request_buffer, SCpnt->use_sg, SCpnt->sc_data_direction); if (sges_left == 0) return FAILED; } else if (SCpnt->request_bufflen) { - SCpnt->SCp.dma_handle = pci_map_single(hd->ioc->pcidev, + SCpnt->SCp.dma_handle = pci_map_single(ioc->pcidev, SCpnt->request_buffer, SCpnt->request_bufflen, SCpnt->sc_data_direction); dsgprintk((MYIOC_s_INFO_FMT "SG: non-SG for %p, len=%d\n", - hd->ioc->name, SCpnt, SCpnt->request_bufflen)); + ioc->name, SCpnt, SCpnt->request_bufflen)); mptscsih_add_sge((char *) &pReq->SGL, 0xD1000000|MPT_SGE_FLAGS_ADDRESSING|sgdir|SCpnt->request_bufflen, SCpnt->SCp.dma_handle); @@ -493,12 +496,16 @@ nextSGEset: * Update the chain element * Offset and Length fields. */ - mptscsih_add_chain((char *)chainSge, 0, sgeOffset, hd->ChainBufferDMA + chain_dma_off); + mptscsih_add_chain((char *)chainSge, 0, sgeOffset, ioc->ChainBufferDMA + chain_dma_off); } else { /* The current buffer is the original MF * and there is no Chain buffer. */ pReq->ChainOffset = 0; + RequestNB = (((sgeOffset - 1) >> ioc->NBShiftFactor) + 1) & 0x03; + dsgprintk((MYIOC_s_ERR_FMT + "Single Buffer RequestNB=%x, sgeOffset=%d\n", ioc->name, RequestNB, sgeOffset)); + ioc->RequestNB[req_idx] = RequestNB; } } else { /* At least one chain buffer is needed. @@ -513,7 +520,7 @@ nextSGEset: */ dsgprintk((MYIOC_s_INFO_FMT "SG: Chain Required! sg done %d\n", - hd->ioc->name, sg_done)); + ioc->name, sg_done)); /* Set LAST_ELEMENT flag for last non-chain element * in the buffer. Since psge points at the NEXT @@ -537,13 +544,16 @@ nextSGEset: */ u8 nextChain = (u8) (sgeOffset >> 2); sgeOffset += (sizeof(u32) + sizeof(dma_addr_t)); - mptscsih_add_chain((char *)chainSge, nextChain, sgeOffset, hd->ChainBufferDMA + chain_dma_off); + mptscsih_add_chain((char *)chainSge, nextChain, sgeOffset, ioc->ChainBufferDMA + chain_dma_off); } else { /* The original MF buffer requires a chain buffer - * set the offset. * Last element in this MF is a chain element. */ pReq->ChainOffset = (u8) (sgeOffset >> 2); + RequestNB = (((sgeOffset - 1) >> ioc->NBShiftFactor) + 1) & 0x03; + dsgprintk((MYIOC_s_ERR_FMT "Chain Buffer Needed, RequestNB=%x sgeOffset=%d\n", ioc->name, RequestNB, sgeOffset)); + ioc->RequestNB[req_idx] = RequestNB; } sges_left -= sg_done; @@ -552,19 +562,22 @@ nextSGEset: /* NOTE: psge points to the beginning of the chain element * in current buffer. Get a chain buffer. */ - if ((mptscsih_getFreeChainBuffer(hd, &newIndex)) == FAILED) + dsgprintk((MYIOC_s_INFO_FMT + "calling getFreeChainBuffer SCSI cmd=%02x (%p)\n", + ioc->name, pReq->CDB[0], SCpnt)); + if ((mptscsih_getFreeChainBuffer(ioc, &newIndex)) == FAILED) return FAILED; /* Update the tracking arrays. * If chainSge == NULL, update ReqToChain, else ChainToChain */ if (chainSge) { - hd->ChainToChain[chain_idx] = newIndex; + ioc->ChainToChain[chain_idx] = newIndex; } else { - hd->ReqToChain[req_idx] = newIndex; + ioc->ReqToChain[req_idx] = newIndex; } chain_idx = newIndex; - chain_dma_off = hd->ioc->req_sz * chain_idx; + chain_dma_off = ioc->req_sz * chain_idx; /* Populate the chainSGE for the current buffer. * - Set chain buffer pointer to psge and fill @@ -576,7 +589,7 @@ nextSGEset: /* Start the SGE for the next buffer */ - psge = (char *) (hd->ChainBuffer + chain_dma_off); + psge = (char *) (ioc->ChainBuffer + chain_dma_off); sgeOffset = 0; sg_done = 0; @@ -631,7 +644,7 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_F printk(MYIOC_s_ERR_FMT "NULL ScsiCmd ptr!\n", ioc->name); - mptscsih_freeChainBuffers(hd, req_idx); + mptscsih_freeChainBuffers(ioc, req_idx); return 1; } @@ -674,14 +687,15 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_F status = le16_to_cpu(pScsiReply->IOCStatus) & MPI_IOCSTATUS_MASK; scsi_state = pScsiReply->SCSIState; + scsi_status = pScsiReply->SCSIStatus; + xfer_cnt = le32_to_cpu(pScsiReply->TransferCount); - dprintk((KERN_NOTICE " Uh-Oh! (%d:%d:%d) mf=%p, mr=%p, sc=%p\n", + dreplyprintk((KERN_NOTICE " Reply (%d:%d:%d) mf=%p, mr=%p, sc=%p\n", ioc->id, pScsiReq->TargetID, pScsiReq->LUN[1], mf, mr, sc)); - dprintk((KERN_NOTICE " IOCStatus=%04xh, SCSIState=%02xh" - ", SCSIStatus=%02xh, IOCLogInfo=%08xh\n", - status, scsi_state, pScsiReply->SCSIStatus, - le32_to_cpu(pScsiReply->IOCLogInfo))); + dreplyprintk((KERN_NOTICE "IOCStatus=%04xh SCSIState=%02xh" + " SCSIStatus=%02xh xfer_cnt=%08xh\n", + status, scsi_state, scsi_status, xfer_cnt)); if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) copy_sense_data(sc, hd, mf, pScsiReply); @@ -701,7 +715,7 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_F * But not: DID_BUS_BUSY lest one risk * killing interrupt handler:-( */ - sc->result = STS_BUSY; + sc->result = SAM_STAT_BUSY; break; case MPI_IOCSTATUS_SCSI_INVALID_BUS: /* 0x0041 */ @@ -731,13 +745,22 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_F break; case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: /* 0x0049 */ - sc->result = (DRIVER_SENSE << 24) | (DID_OK << 16) | + sc->resid = sc->request_bufflen - xfer_cnt; + if ( xfer_cnt >= sc->underflow ) { + /* Sufficient data transfer occurred */ + sc->result = (DID_OK << 16) | scsi_status; + } else if ( xfer_cnt == 0 ) { + /* A CRC Error causes this condition; retry */ + sc->result = (DRIVER_SENSE << 24) | (DID_OK << 16) | (CHECK_CONDITION << 1); - sc->sense_buffer[0] = 0x70; - sc->sense_buffer[2] = NO_SENSE; - sc->sense_buffer[12] = 0; - sc->sense_buffer[13] = 0; - dprintk((KERN_NOTICE "RESIDUAL_MISMATCH: result=%x on id=%d\n", sc->result, sc->target)); + sc->sense_buffer[0] = 0x70; + sc->sense_buffer[2] = NO_SENSE; + sc->sense_buffer[12] = 0; + sc->sense_buffer[13] = 0; + } else { + sc->result = DID_SOFT_ERROR << 16; + } + dreplyprintk((KERN_NOTICE "RESIDUAL_MISMATCH: result=%x on id=%d\n", sc->result, sc->target)); break; case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */ @@ -745,9 +768,7 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_F * Do upfront check for valid SenseData and give it * precedence! */ - scsi_status = pScsiReply->SCSIStatus; sc->result = (DID_OK << 16) | scsi_status; - xfer_cnt = le32_to_cpu(pScsiReply->TransferCount); if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) { /* Have already saved the status and sense data */ @@ -769,12 +790,12 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_F /* Give report and update residual count. */ - dprintk((KERN_NOTICE " sc->underflow={report ERR if < %02xh bytes xfer'd}\n", + dreplyprintk((KERN_NOTICE " sc->underflow={report ERR if < %02xh bytes xfer'd}\n", sc->underflow)); - dprintk((KERN_NOTICE " ActBytesXferd=%02xh\n", xfer_cnt)); + dreplyprintk((KERN_NOTICE " ActBytesXferd=%02xh\n", xfer_cnt)); sc->resid = sc->request_bufflen - xfer_cnt; - dprintk((KERN_NOTICE " SET sc->resid=%02xh\n", sc->resid)); + dreplyprintk((KERN_NOTICE " SET sc->resid=%02xh\n", sc->resid)); /* Report Queue Full */ @@ -785,7 +806,8 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_F case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: /* 0x0040 */ case MPI_IOCSTATUS_SUCCESS: /* 0x0000 */ - sc->result = (DID_OK << 16) | pScsiReply->SCSIStatus; + scsi_status = pScsiReply->SCSIStatus; + sc->result = (DID_OK << 16) | scsi_status; if (scsi_state == 0) { ; } else if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) { @@ -851,7 +873,7 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_F } /* switch(status) */ - dprintk((KERN_NOTICE " sc->result set to %08xh\n", sc->result)); + dreplyprintk((KERN_NOTICE " sc->result is %08xh\n", sc->result)); } /* end of address reply case */ /* Unmap the DMA buffers, if any. */ @@ -868,88 +890,10 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_F sc->scsi_done(sc); /* Issue the command callback */ /* Free Chain buffers */ - mptscsih_freeChainBuffers(hd, req_idx); + mptscsih_freeChainBuffers(ioc, req_idx); return 1; } -/* - * Flush all commands on the doneQ. - * Lock Q when deleting/adding members - * Lock io_request_lock for OS callback. - */ -static void -flush_doneQ(MPT_SCSI_HOST *hd) -{ - MPT_DONE_Q *buffer; - struct scsi_cmnd *SCpnt; - unsigned long flags; - - /* Flush the doneQ. - */ - dtmprintk((KERN_INFO MYNAM ": flush_doneQ called\n")); - while (1) { - spin_lock_irqsave(&hd->freedoneQlock, flags); - if (Q_IS_EMPTY(&hd->doneQ)) { - spin_unlock_irqrestore(&hd->freedoneQlock, flags); - break; - } - - buffer = hd->doneQ.head; - /* Delete from Q - */ - Q_DEL_ITEM(buffer); - - /* Set the struct scsi_cmnd pointer - */ - SCpnt = (struct scsi_cmnd *) buffer->argp; - buffer->argp = NULL; - - /* Add to the freeQ - */ - Q_ADD_TAIL(&hd->freeQ.head, buffer, MPT_DONE_Q); - spin_unlock_irqrestore(&hd->freedoneQlock, flags); - - /* Do the OS callback. - */ - SCpnt->scsi_done(SCpnt); - } - - return; -} - -/* - * Search the doneQ for a specific command. If found, delete from Q. - * Calling function will finish processing. - */ -static void -search_doneQ_for_cmd(MPT_SCSI_HOST *hd, struct scsi_cmnd *SCpnt) -{ - unsigned long flags; - MPT_DONE_Q *buffer; - - spin_lock_irqsave(&hd->freedoneQlock, flags); - if (!Q_IS_EMPTY(&hd->doneQ)) { - buffer = hd->doneQ.head; - do { - struct scsi_cmnd *sc = (struct scsi_cmnd *) buffer->argp; - if (SCpnt == sc) { - Q_DEL_ITEM(buffer); - SCpnt->result = sc->result; - - /* Set the struct scsi_cmnd pointer - */ - buffer->argp = NULL; - - /* Add to the freeQ - */ - Q_ADD_TAIL(&hd->freeQ.head, buffer, MPT_DONE_Q); - break; - } - } while ((buffer = buffer->forw) != (MPT_DONE_Q *) &hd->doneQ); - } - spin_unlock_irqrestore(&hd->freedoneQlock, flags); - return; -} /* * mptscsih_flush_running_cmds - For each command found, search @@ -964,12 +908,11 @@ search_doneQ_for_cmd(MPT_SCSI_HOST *hd, static void mptscsih_flush_running_cmds(MPT_SCSI_HOST *hd) { + MPT_ADAPTER *ioc = hd->ioc; struct scsi_cmnd *SCpnt; MPT_FRAME_HDR *mf; - MPT_DONE_Q *buffer; int ii; - int max = hd->ioc->req_depth; - unsigned long flags; + int max = ioc->req_depth; dprintk((KERN_INFO MYNAM ": flush_ScsiLookup called\n")); for (ii= 0; ii < max; ii++) { @@ -978,16 +921,11 @@ mptscsih_flush_running_cmds(MPT_SCSI_HOS /* Command found. */ - /* Search pendingQ, if found, - * delete from Q. - */ - mptscsih_search_pendingQ(hd, ii); - /* Null ScsiLookup index */ hd->ScsiLookup[ii] = NULL; - mf = MPT_INDEX_2_MFPTR(hd->ioc, ii); + mf = MPT_INDEX_2_MFPTR(ioc, ii); dmfprintk(( "flush: ScsiDone (mf=%p,sc=%p)\n", mf, SCpnt)); @@ -997,12 +935,12 @@ mptscsih_flush_running_cmds(MPT_SCSI_HOS */ if (scsi_device_online(SCpnt->device)) { if (SCpnt->use_sg) { - pci_unmap_sg(hd->ioc->pcidev, + pci_unmap_sg(ioc->pcidev, (struct scatterlist *) SCpnt->request_buffer, SCpnt->use_sg, SCpnt->sc_data_direction); } else if (SCpnt->request_bufflen) { - pci_unmap_single(hd->ioc->pcidev, + pci_unmap_single(ioc->pcidev, SCpnt->SCp.dma_handle, SCpnt->request_bufflen, SCpnt->sc_data_direction); @@ -1012,37 +950,12 @@ mptscsih_flush_running_cmds(MPT_SCSI_HOS SCpnt->host_scribble = NULL; /* Free Chain buffers */ - mptscsih_freeChainBuffers(hd, ii); + mptscsih_freeChainBuffers(ioc, ii); /* Free Message frames */ - mpt_free_msg_frame(ScsiDoneCtx, hd->ioc, mf); - -#if 1 - /* Post to doneQ, do not reply until POST phase - * of reset handler....prevents new commands from - * being queued. - */ - spin_lock_irqsave(&hd->freedoneQlock, flags); - if (!Q_IS_EMPTY(&hd->freeQ)) { - buffer = hd->freeQ.head; - Q_DEL_ITEM(buffer); - - /* Set the struct scsi_cmnd pointer - */ - buffer->argp = (void *)SCpnt; + mpt_free_msg_frame(ioc, mf); - /* Add to the doneQ - */ - Q_ADD_TAIL(&hd->doneQ.head, buffer, MPT_DONE_Q); - spin_unlock_irqrestore(&hd->freedoneQlock, flags); - } else { - spin_unlock_irqrestore(&hd->freedoneQlock, flags); - SCpnt->scsi_done(SCpnt); - } -#else SCpnt->scsi_done(SCpnt); /* Issue the command callback */ -#endif - } } @@ -1087,8 +1000,8 @@ mptscsih_search_running_cmds(MPT_SCSI_HO /* Cleanup */ hd->ScsiLookup[ii] = NULL; - mptscsih_freeChainBuffers(hd, ii); - mpt_free_msg_frame(ScsiDoneCtx, hd->ioc, (MPT_FRAME_HDR *)mf); + mptscsih_freeChainBuffers(hd->ioc, ii); + mpt_free_msg_frame(hd->ioc, (MPT_FRAME_HDR *)mf); } } @@ -1097,111 +1010,6 @@ mptscsih_search_running_cmds(MPT_SCSI_HO /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* - * mptscsih_initChainBuffers - Allocate memory for and initialize - * chain buffers, chain buffer control arrays and spinlock. - * @hd: Pointer to MPT_SCSI_HOST structure - * @init: If set, initialize the spin lock. - */ -static int -mptscsih_initChainBuffers (MPT_SCSI_HOST *hd, int init) -{ - MPT_FRAME_HDR *chain; - u8 *mem; - unsigned long flags; - int sz, ii, num_chain; - int scale, num_sge; - - /* chain buffer allocation done from PrimeIocFifos */ - if (hd->ioc->fifo_pool == NULL) - return -1; - - hd->ChainBuffer = hd->ioc->chain_alloc; - hd->ChainBufferDMA = hd->ioc->chain_alloc_dma; - - dprintk((KERN_INFO " ChainBuffer @ %p(%p), sz=%d\n", - hd->ChainBuffer, (void *)(ulong)hd->ChainBufferDMA, hd->ioc->chain_alloc_sz)); - - /* ReqToChain size must equal the req_depth - * index = req_idx - */ - if (hd->ReqToChain == NULL) { - sz = hd->ioc->req_depth * sizeof(int); - mem = kmalloc(sz, GFP_ATOMIC); - if (mem == NULL) - return -1; - - hd->ReqToChain = (int *) mem; - } - for (ii = 0; ii < hd->ioc->req_depth; ii++) - hd->ReqToChain[ii] = MPT_HOST_NO_CHAIN; - - /* ChainToChain size must equal the total number - * of chain buffers to be allocated. - * index = chain_idx - * - * Calculate the number of chain buffers needed(plus 1) per I/O - * then multiply the the maximum number of simultaneous cmds - * - * num_sge = num sge in request frame + last chain buffer - * scale = num sge per chain buffer if no chain element - */ - scale = hd->ioc->req_sz/(sizeof(dma_addr_t) + sizeof(u32)); - if (sizeof(dma_addr_t) == sizeof(u64)) - num_sge = scale + (hd->ioc->req_sz - 60) / (sizeof(dma_addr_t) + sizeof(u32)); - else - num_sge = 1+ scale + (hd->ioc->req_sz - 64) / (sizeof(dma_addr_t) + sizeof(u32)); - - num_chain = 1; - while (hd->max_sge - num_sge > 0) { - num_chain++; - num_sge += (scale - 1); - } - num_chain++; - - if ((int) hd->ioc->chip_type > (int) FC929) - num_chain *= MPT_SCSI_CAN_QUEUE; - else - num_chain *= MPT_FC_CAN_QUEUE; - - hd->num_chain = num_chain; - - sz = num_chain * sizeof(int); - if (hd->ChainToChain == NULL) { - mem = kmalloc(sz, GFP_ATOMIC); - if (mem == NULL) - return -1; - - hd->ChainToChain = (int *) mem; - } else { - mem = (u8 *) hd->ChainToChain; - } - memset(mem, 0xFF, sz); - - - /* Initialize the free chain Q. - */ - if (init) { - spin_lock_init(&hd->FreeChainQlock); - } - - spin_lock_irqsave (&hd->FreeChainQlock, flags); - Q_INIT(&hd->FreeChainQ, MPT_FRAME_HDR); - - /* Post the chain buffers to the FreeChainQ. - */ - mem = (u8 *)hd->ChainBuffer; - for (ii=0; ii < num_chain; ii++) { - chain = (MPT_FRAME_HDR *) mem; - Q_ADD_TAIL(&hd->FreeChainQ.head, &chain->u.frame.linkage, MPT_FRAME_HDR); - mem += hd->ioc->req_sz; - } - spin_unlock_irqrestore(&hd->FreeChainQlock, flags); - - return 0; -} - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* * Hack! It might be nice to report if a device is returning QUEUE_FULL * but maybe not each and every time... */ @@ -1254,7 +1062,6 @@ mptscsih_probe(struct pci_dev *pdev, con struct Scsi_Host *sh; MPT_SCSI_HOST *hd; MPT_ADAPTER *ioc = pci_get_drvdata(pdev); - MPT_DONE_Q *freedoneQ; unsigned long flags; int sz, ii; int numSGE = 0; @@ -1386,7 +1193,6 @@ mptscsih_probe(struct pci_dev *pdev, con hd = (MPT_SCSI_HOST *) sh->hostdata; hd->ioc = ioc; - hd->max_sge = sh->sg_tablesize; if ((int)ioc->chip_type > (int)FC929) hd->is_spi = 1; @@ -1399,7 +1205,7 @@ mptscsih_probe(struct pci_dev *pdev, con /* SCSI needs scsi_cmnd lookup table! * (with size equal to req_depth*PtrSz!) */ - sz = hd->ioc->req_depth * sizeof(void *); + sz = ioc->req_depth * sizeof(void *); mem = kmalloc(sz, GFP_ATOMIC); if (mem == NULL) { error = -ENOMEM; @@ -1412,37 +1218,6 @@ mptscsih_probe(struct pci_dev *pdev, con dprintk((MYIOC_s_INFO_FMT "ScsiLookup @ %p, sz=%d\n", ioc->name, hd->ScsiLookup, sz)); - if (mptscsih_initChainBuffers(hd, 1) < 0) { - error = -EINVAL; - goto mptscsih_probe_failed; - } - - /* Allocate memory for free and doneQ's - */ - sz = sh->can_queue * sizeof(MPT_DONE_Q); - mem = kmalloc(sz, GFP_ATOMIC); - if (mem == NULL) { - error = -ENOMEM; - goto mptscsih_probe_failed; - } - - memset(mem, 0xFF, sz); - hd->memQ = mem; - - /* Initialize the free, done and pending Qs. - */ - Q_INIT(&hd->freeQ, MPT_DONE_Q); - Q_INIT(&hd->doneQ, MPT_DONE_Q); - Q_INIT(&hd->pendingQ, MPT_DONE_Q); - spin_lock_init(&hd->freedoneQlock); - - mem = hd->memQ; - for (ii=0; ii < sh->can_queue; ii++) { - freedoneQ = (MPT_DONE_Q *) mem; - Q_ADD_TAIL(&hd->freeQ.head, freedoneQ, MPT_DONE_Q); - mem += sizeof(MPT_DONE_Q); - } - /* Initialize this Scsi_Host * internal task Q. */ @@ -1474,8 +1249,7 @@ mptscsih_probe(struct pci_dev *pdev, con hd->resetPending = 0; hd->abortSCpnt = NULL; hd->tmPtr = NULL; - hd->numTMrequests = 0; - + /* Clear the pointer used to store * single-threaded commands, i.e., those * issued during a bus scan, dv and @@ -1500,10 +1274,10 @@ mptscsih_probe(struct pci_dev *pdev, con /* ioc->sh = sh; */ #ifdef MPTSCSIH_DBG_TIMEOUT - hd->ioc->timeout_hard = 0; - hd->ioc->timeout_delta = 30 * HZ; - hd->ioc->timeout_maxcnt = 0; - hd->ioc->timeout_cnt = 0; + ioc->timeout_hard = 0; + ioc->timeout_delta = 30 * HZ; + ioc->timeout_maxcnt = 0; + ioc->timeout_cnt = 0; for (ii=0; ii < 8; ii++) foo_to[ii] = NULL; #endif @@ -1511,47 +1285,41 @@ mptscsih_probe(struct pci_dev *pdev, con /* Update with the driver setup * values. */ - if (hd->ioc->spi_data.maxBusWidth > + if (ioc->spi_data.maxBusWidth > driver_setup.max_width) { - hd->ioc->spi_data.maxBusWidth = + ioc->spi_data.maxBusWidth = driver_setup.max_width; } - if (hd->ioc->spi_data.minSyncFactor < + if (ioc->spi_data.minSyncFactor < driver_setup.min_sync_fac) { - hd->ioc->spi_data.minSyncFactor = + ioc->spi_data.minSyncFactor = driver_setup.min_sync_fac; } - if (hd->ioc->spi_data.minSyncFactor == MPT_ASYNC) { - hd->ioc->spi_data.maxSyncOffset = 0; + if (ioc->spi_data.minSyncFactor == MPT_ASYNC) { + ioc->spi_data.maxSyncOffset = 0; } - hd->ioc->spi_data.Saf_Te = driver_setup.saf_te; + ioc->spi_data.Saf_Te = driver_setup.saf_te; hd->negoNvram = 0; #ifndef MPTSCSIH_ENABLE_DOMAIN_VALIDATION hd->negoNvram = MPT_SCSICFG_USE_NVRAM; #endif - if (driver_setup.dv == 0) { - hd->negoNvram = MPT_SCSICFG_USE_NVRAM; - } - - hd->ioc->spi_data.forceDv = 0; + ioc->spi_data.forceDv = 0; for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) { - hd->ioc->spi_data.dvStatus[ii] = + ioc->spi_data.dvStatus[ii] = MPT_SCSICFG_NEGOTIATE; } - if (hd->negoNvram == 0) { - for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) - hd->ioc->spi_data.dvStatus[ii] |= - MPT_SCSICFG_DV_NOT_DONE; - } + for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) + ioc->spi_data.dvStatus[ii] |= + MPT_SCSICFG_DV_NOT_DONE; ddvprintk((MYIOC_s_INFO_FMT "dv %x width %x factor %x saf_te %x\n", - hd->ioc->name, driver_setup.dv, + ioc->name, driver_setup.dv, driver_setup.max_width, driver_setup.min_sync_fac, driver_setup.saf_te)); @@ -1620,14 +1388,11 @@ mptscsih_remove(struct pci_dev *pdev) hd = (MPT_SCSI_HOST *)host->hostdata; if (hd != NULL) { - int sz1, sz2, sz3, sztarget=0; - int szr2chain = 0; - int szc2chain = 0; - int szQ = 0; + int sz1, sz3, sztarget=0; mptscsih_shutdown(&pdev->dev); - sz1 = sz2 = sz3 = 0; + sz1 = sz3 = 0; if (hd->ScsiLookup != NULL) { sz1 = hd->ioc->req_depth * sizeof(void *); @@ -1635,24 +1400,6 @@ mptscsih_remove(struct pci_dev *pdev) hd->ScsiLookup = NULL; } - if (hd->ReqToChain != NULL) { - szr2chain = hd->ioc->req_depth * sizeof(int); - kfree(hd->ReqToChain); - hd->ReqToChain = NULL; - } - - if (hd->ChainToChain != NULL) { - szc2chain = hd->num_chain * sizeof(int); - kfree(hd->ChainToChain); - hd->ChainToChain = NULL; - } - - if (hd->memQ != NULL) { - szQ = host->can_queue * sizeof(MPT_DONE_Q); - kfree(hd->memQ); - hd->memQ = NULL; - } - if (hd->Targets != NULL) { int max, ii; @@ -1680,9 +1427,9 @@ mptscsih_remove(struct pci_dev *pdev) hd->Targets = NULL; } - dprintk((MYIOC_s_INFO_FMT - "Free'd ScsiLookup (%d) Target (%d+%d) memory\n", - hd->ioc->name, sz1, sz3, sztarget)); + dprintk((MYIOC_s_INFO_FMT + "Free'd ScsiLookup (%d) Target (%d+%d) memory\n", + hd->ioc->name, sz1, sz3, sztarget)); dprintk(("Free'd done and free Q (%d) memory\n", szQ)); /* NULL the Scsi_Host pointer @@ -1802,7 +1549,7 @@ mptscsih_init(void) ScsiScanDvCtx = mpt_register(mptscsih_scandv_complete, MPTSCSIH_DRIVER); if (mpt_event_register(ScsiDoneCtx, mptscsih_event_process) == 0) { - dprintk((KERN_INFO MYNAM + devtprintk((KERN_INFO MYNAM ": Registered for IOC event notifications\n")); } @@ -1864,7 +1611,7 @@ mptscsih_exit(void) * * Returns pointer to buffer where information was written. */ -const char * +static const char * mptscsih_info(struct Scsi_Host *SChost) { MPT_SCSI_HOST *h; @@ -2079,7 +1826,8 @@ static int mptscsih_user_command(MPT_ADA * hostno: scsi host number * func: if write = 1; if read = 0 */ -int mptscsih_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset, +static int +mptscsih_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset, int length, int func) { MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)host->hostdata; @@ -2114,15 +1862,13 @@ int mptscsih_proc_info(struct Scsi_Host * * Returns 0. (rtn value discarded by linux scsi mid-layer) */ -int +static int mptscsih_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) { MPT_SCSI_HOST *hd; MPT_FRAME_HDR *mf; SCSIIORequest_t *pScsiReq; VirtDevice *pTarget; - MPT_DONE_Q *buffer; - unsigned long flags; int target; int lun; u32 datalen; @@ -2147,16 +1893,9 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, v (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt, done)); if (hd->resetPending) { - /* Prevent new commands from being issued - * while reloading the FW. Reset timer to 60 seconds, - * as the FW can take some time to come ready. - * For New EH, cmds on doneQ posted to FW. - */ - did_errcode = 1; - mod_timer(&SCpnt->eh_timeout, jiffies + (HZ * 60)); dtmprintk((MYIOC_s_WARN_FMT "qcmd: SCpnt=%p timeout + 60HZ\n", (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt)); - goto did_error; + return SCSI_MLQUEUE_HOST_BUSY; } /* @@ -2165,8 +1904,7 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, v if ((mf = mpt_get_msg_frame(ScsiDoneCtx, hd->ioc)) == NULL) { dprintk((MYIOC_s_WARN_FMT "QueueCmd, no msg frames!!\n", hd->ioc->name)); - did_errcode = 2; - goto did_error; + return SCSI_MLQUEUE_HOST_BUSY; } pScsiReq = (SCSIIORequest_t *) mf; @@ -2249,7 +1987,7 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, v (dma_addr_t) -1); } else { /* Add a 32 or 64 bit SGE */ - rc = mptscsih_AddSGE(hd, SCpnt, pScsiReq, my_idx); + rc = mptscsih_AddSGE(hd->ioc, SCpnt, pScsiReq, my_idx); } @@ -2283,7 +2021,7 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, v } /* Trying to do DV to this target, extend timeout. - * Wait to issue intil flag is clear + * Wait to issue until flag is clear */ if (dvStatus & MPT_SCSICFG_DV_PENDING) { mod_timer(&SCpnt->eh_timeout, jiffies + 40 * HZ); @@ -2314,64 +2052,18 @@ mptscsih_qcmd(struct scsi_cmnd *SCpnt, v mpt_put_msg_frame(ScsiDoneCtx, hd->ioc, mf); dmfprintk((MYIOC_s_INFO_FMT "Issued SCSI cmd (%p) mf=%p idx=%d\n", hd->ioc->name, SCpnt, mf, my_idx)); - } else { - ddvtprintk((MYIOC_s_INFO_FMT "Pending cmd=%p idx %d\n", - hd->ioc->name, SCpnt, my_idx)); - /* Place this command on the pendingQ if possible */ - spin_lock_irqsave(&hd->freedoneQlock, flags); - if (!Q_IS_EMPTY(&hd->freeQ)) { - buffer = hd->freeQ.head; - Q_DEL_ITEM(buffer); - - /* Save the mf pointer - */ - buffer->argp = (void *)mf; - - /* Add to the pendingQ - */ - Q_ADD_TAIL(&hd->pendingQ.head, buffer, MPT_DONE_Q); - spin_unlock_irqrestore(&hd->freedoneQlock, flags); - } else { - spin_unlock_irqrestore(&hd->freedoneQlock, flags); - SCpnt->result = (DID_BUS_BUSY << 16); - SCpnt->scsi_done(SCpnt); - } - } - } else { - mptscsih_freeChainBuffers(hd, my_idx); - mpt_free_msg_frame(ScsiDoneCtx, hd->ioc, mf); - did_errcode = 3; - goto did_error; - } + DBG_DUMP_REQUEST_FRAME(mf) + } else + goto fail; + } else + goto fail; return 0; -did_error: - dprintk((MYIOC_s_WARN_FMT "_qcmd did_errcode=%d (sc=%p)\n", - hd->ioc->name, did_errcode, SCpnt)); - /* Just wish OS to issue a retry */ - SCpnt->result = (DID_BUS_BUSY << 16); - spin_lock_irqsave(&hd->freedoneQlock, flags); - if (!Q_IS_EMPTY(&hd->freeQ)) { - dtmprintk((MYIOC_s_WARN_FMT "SCpnt=%p to doneQ\n", - (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt)); - buffer = hd->freeQ.head; - Q_DEL_ITEM(buffer); - - /* Set the scsi_cmnd pointer - */ - buffer->argp = (void *)SCpnt; - - /* Add to the doneQ - */ - Q_ADD_TAIL(&hd->doneQ.head, buffer, MPT_DONE_Q); - spin_unlock_irqrestore(&hd->freedoneQlock, flags); - } else { - spin_unlock_irqrestore(&hd->freedoneQlock, flags); - SCpnt->scsi_done(SCpnt); - } - - return 0; + fail: + mptscsih_freeChainBuffers(hd->ioc, my_idx); + mpt_free_msg_frame(hd->ioc, mf); + return SCSI_MLQUEUE_HOST_BUSY; } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -2385,7 +2077,7 @@ did_error: * No return. */ static void -mptscsih_freeChainBuffers(MPT_SCSI_HOST *hd, int req_idx) +mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx) { MPT_FRAME_HDR *chain; unsigned long flags; @@ -2395,28 +2087,28 @@ mptscsih_freeChainBuffers(MPT_SCSI_HOST /* Get the first chain index and reset * tracker state. */ - chain_idx = hd->ReqToChain[req_idx]; - hd->ReqToChain[req_idx] = MPT_HOST_NO_CHAIN; + chain_idx = ioc->ReqToChain[req_idx]; + ioc->ReqToChain[req_idx] = MPT_HOST_NO_CHAIN; while (chain_idx != MPT_HOST_NO_CHAIN) { /* Save the next chain buffer index */ - next = hd->ChainToChain[chain_idx]; + next = ioc->ChainToChain[chain_idx]; /* Free this chain buffer and reset * tracker */ - hd->ChainToChain[chain_idx] = MPT_HOST_NO_CHAIN; + ioc->ChainToChain[chain_idx] = MPT_HOST_NO_CHAIN; - chain = (MPT_FRAME_HDR *) (hd->ChainBuffer - + (chain_idx * hd->ioc->req_sz)); - spin_lock_irqsave(&hd->ioc->FreeQlock, flags); - Q_ADD_TAIL(&hd->FreeChainQ.head, + chain = (MPT_FRAME_HDR *) (ioc->ChainBuffer + + (chain_idx * ioc->req_sz)); + spin_lock_irqsave(&ioc->FreeQlock, flags); + Q_ADD_TAIL(&ioc->FreeChainQ.head, &chain->u.frame.linkage, MPT_FRAME_HDR); - spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); + spin_unlock_irqrestore(&ioc->FreeQlock, flags); dmfprintk((MYIOC_s_INFO_FMT "FreeChainBuffers (index %d)\n", - hd->ioc->name, chain_idx)); + ioc->name, chain_idx)); /* handle next */ chain_idx = next; @@ -2480,12 +2172,6 @@ mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 } spin_unlock_irqrestore(&ioc->diagLock, flags); - /* Do not do a Task Management if there are - * too many failed TMs on this adapter. - */ - if (hd->numTMrequests > MPT_HOST_TOO_MANY_TM) - doTask = 0; - /* Wait a fixed amount of time for the TM pending flag to be cleared. * If we time out and not bus reset, then we return a FAILED status to the caller. * The call to mptscsih_tm_pending_wait() will set the pending flag if we are @@ -2593,7 +2279,7 @@ mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd /* Return Fail to calling function if no message frames available. */ if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc)) == NULL) { - dtmprintk((MYIOC_s_WARN_FMT "IssueTaskMgmt, no msg frames!!\n", + dfailprintk((MYIOC_s_ERR_FMT "IssueTaskMgmt, no msg frames!!\n", hd->ioc->name)); //return FAILED; return -999; @@ -2624,29 +2310,30 @@ mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd pScsiTm->Reserved2[ii] = 0; pScsiTm->TaskMsgContext = ctx2abort; - dtmprintk((MYIOC_s_INFO_FMT "IssueTaskMgmt, ctx2abort (0x%08x), type (%d)\n", - hd->ioc->name, ctx2abort, type)); /* MPI v0.10 requires SCSITaskMgmt requests be sent via Doorbell/handshake mpt_put_msg_frame(hd->ioc->id, mf); * Save the MF pointer in case the request times out. */ hd->tmPtr = mf; - hd->numTMrequests++; hd->TMtimer.expires = jiffies + timeout; add_timer(&hd->TMtimer); + dtmprintk((MYIOC_s_INFO_FMT "IssueTaskMgmt: ctx2abort (0x%08x) type=%d\n", + hd->ioc->name, ctx2abort, type)); + + DBG_DUMP_TM_REQUEST_FRAME((u32 *)pScsiTm); + if ((retval = mpt_send_handshake_request(ScsiTaskCtx, hd->ioc, sizeof(SCSITaskMgmt_t), (u32*)pScsiTm, sleepFlag)) != 0) { - dtmprintk((MYIOC_s_WARN_FMT "_send_handshake FAILED!" + dfailprintk((MYIOC_s_ERR_FMT "_send_handshake FAILED!" " (hd %p, ioc %p, mf %p) \n", hd->ioc->name, hd, hd->ioc, mf)); - hd->numTMrequests--; hd->tmPtr = NULL; del_timer(&hd->TMtimer); - mpt_free_msg_frame(ScsiTaskCtx, hd->ioc, mf); + mpt_free_msg_frame(hd->ioc, mf); } - + return retval; } @@ -2659,10 +2346,11 @@ mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd * * Returns SUCCESS or FAILED. */ -int +static int mptscsih_abort(struct scsi_cmnd * SCpnt) { MPT_SCSI_HOST *hd; + MPT_ADAPTER *ioc; MPT_FRAME_HDR *mf; u32 ctx2abort; int scpnt_idx; @@ -2673,12 +2361,13 @@ mptscsih_abort(struct scsi_cmnd * SCpnt) if ((hd = (MPT_SCSI_HOST *) SCpnt->device->host->hostdata) == NULL) { SCpnt->result = DID_RESET << 16; SCpnt->scsi_done(SCpnt); - dtmprintk((KERN_WARNING MYNAM ": mptscsih_abort: " + dfailprintk((KERN_WARNING MYNAM ": mptscsih_abort: " "Can't locate host! (sc=%p)\n", SCpnt)); return FAILED; } + ioc = hd->ioc; if (hd->resetPending) return FAILED; @@ -2691,11 +2380,9 @@ mptscsih_abort(struct scsi_cmnd * SCpnt) /* Find this command */ if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(SCpnt)) < 0) { - /* Cmd not found in ScsiLookup. If found in - * doneQ, delete from Q. Do OS callback. + /* Cmd not found in ScsiLookup. + * Do OS callback. */ - search_doneQ_for_cmd(hd, SCpnt); - SCpnt->result = DID_RESET << 16; dtmprintk((KERN_WARNING MYNAM ": %s: mptscsih_abort: " "Command not in the active list! (sc=%p)\n", @@ -2703,18 +2390,6 @@ mptscsih_abort(struct scsi_cmnd * SCpnt) return SUCCESS; } - /* If this command is pended, then timeout/hang occurred - * during DV. Post command and flush pending Q - * and then following up with the reset request. - */ - if ((mf = mptscsih_search_pendingQ(hd, scpnt_idx)) != NULL) { - mpt_put_msg_frame(ScsiDoneCtx, hd->ioc, mf); - post_pendingQ_commands(hd); - dtmprintk((KERN_WARNING MYNAM ": %s: mptscsih_abort: " - "Posting pended cmd! (sc=%p)\n", - hd->ioc->name, SCpnt)); - } - /* Most important! Set TaskMsgContext to SCpnt's MsgContext! * (the IO to be ABORT'd) * @@ -2724,7 +2399,7 @@ mptscsih_abort(struct scsi_cmnd * SCpnt) */ mf = MPT_INDEX_2_MFPTR(hd->ioc, scpnt_idx); ctx2abort = mf->u.frame.hwhdr.msgctxu.MsgContext; - + hd->abortSCpnt = SCpnt; spin_unlock_irq(host_lock); @@ -2743,12 +2418,26 @@ mptscsih_abort(struct scsi_cmnd * SCpnt) */ hd->tmPending = 0; hd->tmState = TM_STATE_NONE; - + spin_lock_irq(host_lock); + + /* Unmap the DMA buffers, if any. */ + if (SCpnt->use_sg) { + pci_unmap_sg(ioc->pcidev, (struct scatterlist *) SCpnt->request_buffer, + SCpnt->use_sg, SCpnt->sc_data_direction); + } else if (SCpnt->request_bufflen) { + pci_unmap_single(ioc->pcidev, SCpnt->SCp.dma_handle, + SCpnt->request_bufflen, SCpnt->sc_data_direction); + } + hd->ScsiLookup[scpnt_idx] = NULL; + SCpnt->result = DID_RESET << 16; + SCpnt->scsi_done(SCpnt); /* Issue the command callback */ + mptscsih_freeChainBuffers(ioc, scpnt_idx); + mpt_free_msg_frame(ioc, mf); return FAILED; } spin_lock_irq(host_lock); - return FAILED; + return SUCCESS; } @@ -2761,7 +2450,7 @@ mptscsih_abort(struct scsi_cmnd * SCpnt) * * Returns SUCCESS or FAILED. */ -int +static int mptscsih_dev_reset(struct scsi_cmnd * SCpnt) { MPT_SCSI_HOST *hd; @@ -2816,7 +2505,7 @@ mptscsih_dev_reset(struct scsi_cmnd * SC * * Returns SUCCESS or FAILED. */ -int +static int mptscsih_bus_reset(struct scsi_cmnd * SCpnt) { MPT_SCSI_HOST *hd; @@ -2839,9 +2528,6 @@ mptscsih_bus_reset(struct scsi_cmnd * SC /* We are now ready to execute the task management request. */ spin_unlock_irq(host_lock); -// printk("testing start : mptscsih_schedule_reset\n"); -// mptscsih_schedule_reset(hd); -// printk("testing end: mptscsih_schedule_reset\n"); if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS, SCpnt->device->channel, 0, 0, 0, (HZ*5) /* 5 second timeout */, CAN_SLEEP) < 0){ @@ -2871,7 +2557,7 @@ mptscsih_bus_reset(struct scsi_cmnd * SC * * Returns SUCCESS or FAILED. */ -int +static int mptscsih_host_reset(struct scsi_cmnd *SCpnt) { MPT_SCSI_HOST * hd; @@ -2937,8 +2623,7 @@ mptscsih_tm_pending_wait(MPT_SCSI_HOST * break; } spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/4); + msleep(250); } while (--loop_count); return status; @@ -2965,9 +2650,10 @@ mptscsih_taskmgmt_complete(MPT_ADAPTER * SCSITaskMgmt_t *pScsiTmReq; MPT_SCSI_HOST *hd; unsigned long flags; - u8 tmType = 0; + u16 iocstatus; + u8 tmType; - dtmprintk((MYIOC_s_INFO_FMT "SCSI TaskMgmt completed (mf=%p,r=%p)\n", + dtmprintk((MYIOC_s_WARN_FMT "TaskMgmt completed (mf=%p,mr=%p)\n", ioc->name, mf, mr)); if (ioc->sh) { /* Depending on the thread, a timer is activated for @@ -2978,7 +2664,7 @@ mptscsih_taskmgmt_complete(MPT_ADAPTER * if (hd->tmPtr) { del_timer(&hd->TMtimer); } - dtmprintk((MYIOC_s_INFO_FMT "taskQcnt (%d)\n", + dtmprintk((MYIOC_s_WARN_FMT "taskQcnt (%d)\n", ioc->name, hd->taskQcnt)); } else { dtmprintk((MYIOC_s_WARN_FMT "TaskMgmt Complete: NULL Scsi Host Ptr\n", @@ -2997,18 +2683,15 @@ mptscsih_taskmgmt_complete(MPT_ADAPTER * /* Figure out if this was ABORT_TASK, TARGET_RESET, or BUS_RESET! */ tmType = pScsiTmReq->TaskType; - dtmprintk((KERN_INFO " TaskType = %d, TerminationCount=%d\n", - tmType, le32_to_cpu(pScsiTmReply->TerminationCount))); - + dtmprintk((MYIOC_s_WARN_FMT " TaskType = %d, TerminationCount=%d\n", + ioc->name, tmType, le32_to_cpu(pScsiTmReply->TerminationCount))); + DBG_DUMP_TM_REPLY_FRAME((u32 *)pScsiTmReply); + + iocstatus = le16_to_cpu(pScsiTmReply->IOCStatus) & MPI_IOCSTATUS_MASK; + dtmprintk((MYIOC_s_WARN_FMT " SCSI TaskMgmt (%d) IOCStatus=%04x IOCLogInfo=%08x\n", + ioc->name, tmType, iocstatus, le32_to_cpu(pScsiTmReply->IOCLogInfo))); /* Error? (anything non-zero?) */ - if (*(u32 *)&pScsiTmReply->Reserved2[0]) { - u16 iocstatus; - - iocstatus = le16_to_cpu(pScsiTmReply->IOCStatus) & MPI_IOCSTATUS_MASK; - dtmprintk((KERN_INFO " SCSI TaskMgmt (%d) - Oops!\n", tmType)); - dtmprintk((KERN_INFO " IOCStatus = %04xh\n", iocstatus)); - dtmprintk((KERN_INFO " IOCLogInfo = %08xh\n", - le32_to_cpu(pScsiTmReply->IOCLogInfo))); + if (iocstatus) { /* clear flags and continue. */ @@ -3029,11 +2712,9 @@ mptscsih_taskmgmt_complete(MPT_ADAPTER * } } } else { - dtmprintk((KERN_INFO " SCSI TaskMgmt SUCCESS!\n")); + dtmprintk((MYIOC_s_WARN_FMT " TaskMgmt SUCCESS\n", ioc->name)); - hd->numTMrequests--; hd->abortSCpnt = NULL; - flush_doneQ(hd); } } @@ -3051,26 +2732,21 @@ mptscsih_taskmgmt_complete(MPT_ADAPTER * /* * This is anyones guess quite frankly. */ -int +static int mptscsih_bios_param(struct scsi_device * sdev, struct block_device *bdev, sector_t capacity, int geom[]) { int heads; int sectors; sector_t cylinders; -#ifdef CONFIG_LBD ulong dummy; -#endif heads = 64; sectors = 32; -#ifdef CONFIG_LBD + dummy = heads * sectors; cylinders = capacity; sector_div(cylinders,dummy); -#else - cylinders = (ulong)capacity / (heads * sectors); -#endif /* * Handle extended translation size for logical drives @@ -3079,13 +2755,9 @@ mptscsih_bios_param(struct scsi_device * if ((ulong)capacity >= 0x200000) { heads = 255; sectors = 63; -#ifdef CONFIG_LBD dummy = heads * sectors; cylinders = capacity; sector_div(cylinders,dummy); -#else - cylinders = (ulong)capacity / (heads * sectors); -#endif } /* return result */ @@ -3107,7 +2779,7 @@ mptscsih_bios_param(struct scsi_device * * Return non-zero if allocation fails. * Init memory once per id (not LUN). */ -int +static int mptscsih_slave_alloc(struct scsi_device *device) { struct Scsi_Host *host = device->host; @@ -3130,12 +2802,21 @@ mptscsih_slave_alloc(struct scsi_device Q_INIT(&vdev->WaitQ, void); Q_INIT(&vdev->SentQ, void); Q_INIT(&vdev->DoneQ, void); - vdev->tflags = 0; + vdev->tflags = MPT_TARGET_FLAGS_Q_YES; vdev->ioc_id = hd->ioc->id; vdev->target_id = device->id; - vdev->bus_id = hd->port; - + vdev->bus_id = device->channel; + vdev->raidVolume = 0; hd->Targets[device->id] = vdev; + if (hd->is_spi) { + if (hd->ioc->spi_data.isRaid & (1 << device->id)) { + vdev->raidVolume = 1; + ddvtprintk((KERN_INFO + "RAID Volume @ id %d\n", device->id)); + } + } else { + vdev->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY; + } } } vdev->num_luns++; @@ -3147,7 +2828,7 @@ mptscsih_slave_alloc(struct scsi_device * OS entry point to allow for host driver to free allocated memory * Called if no device present or device being unloaded */ -void +static void mptscsih_slave_destroy(struct scsi_device *device) { struct Scsi_Host *host = device->host; @@ -3176,15 +2857,15 @@ mptscsih_slave_destroy(struct scsi_devic kfree(hd->Targets[device->id]); hd->Targets[device->id] = NULL; - if (!hd->is_spi) + if (!hd->is_spi) return; if((hd->ioc->spi_data.isRaid) && (hd->ioc->spi_data.pIocPg3)) { int i; for(i=0;iioc->spi_data.pIocPg3->NumPhysDisks && raid_volume==0;i++) - - if(device->id == + + if(device->id == hd->ioc->spi_data.pIocPg3->PhysDisk[i].PhysDiskID) { raid_volume=1; hd->ioc->spi_data.forceDv |= @@ -3202,7 +2883,7 @@ mptscsih_slave_destroy(struct scsi_devic } } } - + return; } @@ -3212,7 +2893,7 @@ mptscsih_slave_destroy(struct scsi_devic * member to 1 if a device does not support Q tags. * Return non-zero if fails. */ -int +static int mptscsih_slave_configure(struct scsi_device *device) { struct Scsi_Host *sh = device->host; @@ -3240,8 +2921,16 @@ mptscsih_slave_configure(struct scsi_dev pTarget = hd->Targets[device->id]; if (pTarget == NULL) { - /* error case - don't know about this device */ - scsi_adjust_queue_depth(device, 0, 1); + /* Driver doesn't know about this device. + * Kernel may generate a "Dummy Lun 0" which + * may become a real Lun if a + * "scsi add-single-device" command is executed + * while the driver is active (hot-plug a + * device). LSI Raid controllers need + * queue_depth set to DEV_HIGH for this reason. + */ + scsi_adjust_queue_depth(device, MSG_SIMPLE_TAG, + MPT_SCSI_CMD_PER_DEV_HIGH); goto slave_configure_exit; } @@ -3302,8 +2991,6 @@ copy_sense_data(struct scsi_cmnd *sc, MP SCSIIORequest_t *pReq; u32 sense_count = le32_to_cpu(pScsiReply->SenseCount); int index; - char devFoo[96]; - IO_Info_t thisIo; /* Get target structure */ @@ -3342,35 +3029,10 @@ copy_sense_data(struct scsi_cmnd *sc, MP ioc->eventContext++; } } - - /* Print an error report for the user. - */ - thisIo.cdbPtr = sc->cmnd; - thisIo.sensePtr = sc->sense_buffer; - thisIo.SCSIStatus = pScsiReply->SCSIStatus; - thisIo.DoDisplay = 1; - if (hd->is_multipath) - sprintf(devFoo, "%d:%d:%d", - hd->ioc->id, - pReq->TargetID, - pReq->LUN[1]); - else - sprintf(devFoo, "%d:%d:%d", hd->ioc->id, sc->device->id, sc->device->lun); - thisIo.DevIDStr = devFoo; -/* fubar */ - thisIo.dataPtr = NULL; - thisIo.inqPtr = NULL; - if (sc->device) { - thisIo.inqPtr = sc->device->vendor-8; /* FIXME!!! */ - } - (void) mpt_ScsiHost_ErrorReport(&thisIo); - } else { dprintk((MYIOC_s_INFO_FMT "Hmmm... SenseData len=0! (?)\n", hd->ioc->name)); } - - return; } static u32 @@ -3390,102 +3052,6 @@ SCPNT_TO_LOOKUP_IDX(struct scsi_cmnd *sc return -1; } - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* Search the pendingQ for a command with specific index. - * If found, delete and return mf pointer - * If not found, return NULL - */ -static MPT_FRAME_HDR * -mptscsih_search_pendingQ(MPT_SCSI_HOST *hd, int scpnt_idx) -{ - unsigned long flags; - MPT_DONE_Q *buffer; - MPT_FRAME_HDR *mf = NULL; - MPT_FRAME_HDR *cmdMfPtr; - - ddvtprintk((MYIOC_s_INFO_FMT ": search_pendingQ ...", hd->ioc->name)); - cmdMfPtr = MPT_INDEX_2_MFPTR(hd->ioc, scpnt_idx); - spin_lock_irqsave(&hd->freedoneQlock, flags); - if (!Q_IS_EMPTY(&hd->pendingQ)) { - buffer = hd->pendingQ.head; - do { - mf = (MPT_FRAME_HDR *) buffer->argp; - if (mf == cmdMfPtr) { - Q_DEL_ITEM(buffer); - - /* clear the arg pointer - */ - buffer->argp = NULL; - - /* Add to the freeQ - */ - Q_ADD_TAIL(&hd->freeQ.head, buffer, MPT_DONE_Q); - break; - } - mf = NULL; - } while ((buffer = buffer->forw) != (MPT_DONE_Q *) &hd->pendingQ); - } - spin_unlock_irqrestore(&hd->freedoneQlock, flags); - ddvtprintk((" ...return %p\n", mf)); - return mf; -} - -/* Post all commands on the pendingQ to the FW. - * Lock Q when deleting/adding members - * Lock io_request_lock for OS callback. - */ -static void -post_pendingQ_commands(MPT_SCSI_HOST *hd) -{ - MPT_FRAME_HDR *mf; - MPT_DONE_Q *buffer; - unsigned long flags; - - /* Flush the pendingQ. - */ - ddvtprintk((MYIOC_s_INFO_FMT ": post_pendingQ_commands\n", hd->ioc->name)); - while (1) { - spin_lock_irqsave(&hd->freedoneQlock, flags); - if (Q_IS_EMPTY(&hd->pendingQ)) { - spin_unlock_irqrestore(&hd->freedoneQlock, flags); - break; - } - - buffer = hd->pendingQ.head; - /* Delete from Q - */ - Q_DEL_ITEM(buffer); - - mf = (MPT_FRAME_HDR *) buffer->argp; - buffer->argp = NULL; - - /* Add to the freeQ - */ - Q_ADD_TAIL(&hd->freeQ.head, buffer, MPT_DONE_Q); - spin_unlock_irqrestore(&hd->freedoneQlock, flags); - - if (!mf) { - /* This should never happen */ - printk(MYIOC_s_WARN_FMT "post_pendingQ_commands: mf %p\n", hd->ioc->name, (void *) mf); - continue; - } - - mpt_put_msg_frame(ScsiDoneCtx, hd->ioc, mf); - -#if defined(MPT_DEBUG_DV) || defined(MPT_DEBUG_DV_TINY) - { - u16 req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); - struct scsi_cmnd *sc = hd->ScsiLookup[req_idx]; - printk(MYIOC_s_INFO_FMT "Issued SCSI cmd (sc=%p) idx=%d (mf=%p)\n", - hd->ioc->name, sc, req_idx, mf); - } -#endif - } - - return; -} - /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ static int mptscsih_ioc_reset(MPT_ADAPTER *ioc, int reset_phase) @@ -3520,14 +3086,10 @@ mptscsih_ioc_reset(MPT_ADAPTER *ioc, int dtmprintk((MYIOC_s_WARN_FMT "Pre-Diag Reset\n", ioc->name)); /* 2. Flush running commands - * Clean drop test code - if compiled * Clean ScsiLookup (and associated memory) * AND clean mytaskQ */ - /* 2a. Drop Test Command. - */ - /* 2b. Reply to OS all known outstanding I/O commands. */ mptscsih_flush_running_cmds(hd); @@ -3538,7 +3100,7 @@ mptscsih_ioc_reset(MPT_ADAPTER *ioc, int */ if (hd->cmdPtr) { del_timer(&hd->timer); - mpt_free_msg_frame(ScsiScanDvCtx, ioc, hd->cmdPtr); + mpt_free_msg_frame(ioc, hd->cmdPtr); } /* 2d. If a task management has not completed, @@ -3546,7 +3108,7 @@ mptscsih_ioc_reset(MPT_ADAPTER *ioc, int */ if (hd->tmPtr) { del_timer(&hd->TMtimer); - mpt_free_msg_frame(ScsiTaskCtx, ioc, hd->tmPtr); + mpt_free_msg_frame(ioc, hd->tmPtr); } #ifdef MPTSCSIH_DBG_TIMEOUT @@ -3573,7 +3135,6 @@ mptscsih_ioc_reset(MPT_ADAPTER *ioc, int /* 2. Chain Buffer initialization */ - mptscsih_initChainBuffers(hd, 0); /* 3. tmPtr clear */ @@ -3583,8 +3144,10 @@ mptscsih_ioc_reset(MPT_ADAPTER *ioc, int /* 4. Renegotiate to all devices, if SCSI */ - if (hd->is_spi) + if (hd->is_spi) { + dnegoprintk(("writeSDP1: ALL_IDS USE_NVRAM\n")); mptscsih_writeSDP1(hd, 0, 0, MPT_SCSICFG_ALL_IDS | MPT_SCSICFG_USE_NVRAM); + } /* 5. Enable new commands to be posted */ @@ -3592,7 +3155,6 @@ mptscsih_ioc_reset(MPT_ADAPTER *ioc, int hd->tmPending = 0; spin_unlock_irqrestore(&ioc->FreeQlock, flags); hd->resetPending = 0; - hd->numTMrequests = 0; hd->tmState = TM_STATE_NONE; /* 6. If there was an internal command, @@ -3609,11 +3171,7 @@ mptscsih_ioc_reset(MPT_ADAPTER *ioc, int hd->cmdPtr = NULL; } - /* 7. Flush doneQ - */ - flush_doneQ(hd); - - /* 8. Set flag to force DV and re-read IOC Page 3 + /* 7. Set flag to force DV and re-read IOC Page 3 */ if (hd->is_spi) { ioc->spi_data.forceDv = MPT_SCSICFG_NEED_DV | MPT_SCSICFG_RELOAD_IOC_PG3; @@ -3634,7 +3192,7 @@ mptscsih_event_process(MPT_ADAPTER *ioc, MPT_SCSI_HOST *hd; u8 event = le32_to_cpu(pEvReply->Event) & 0xFF; - dprintk((MYIOC_s_INFO_FMT "MPT event (=%02Xh) routed to SCSI host driver!\n", + devtprintk((MYIOC_s_INFO_FMT "MPT event (=%02Xh) routed to SCSI host driver!\n", ioc->name, event)); switch (event) { @@ -3766,394 +3324,6 @@ static struct scsi_host_template driver_ .use_clustering = ENABLE_CLUSTERING, }; - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* - * Private data... - */ -static ASCQ_Table_t *mptscsih_ASCQ_TablePtr; - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* old symsense.c stuff... */ -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* - * Private data... - * To protect ourselves against those that would pass us bogus pointers - */ -static u8 dummyInqData[SCSI_STD_INQUIRY_BYTES] - = { 0x1F, 0x00, 0x00, 0x00, - 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -static u8 dummySenseData[SCSI_STD_SENSE_BYTES] - = { 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00 }; -static u8 dummyCDB[16] - = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -static u8 dummyScsiData[16] - = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - -static char *ScsiStatusString[] = { - "GOOD", /* 00h */ - NULL, /* 01h */ - "CHECK CONDITION", /* 02h */ - NULL, /* 03h */ - "CONDITION MET", /* 04h */ - NULL, /* 05h */ - NULL, /* 06h */ - NULL, /* 07h */ - "BUSY", /* 08h */ - NULL, /* 09h */ - NULL, /* 0Ah */ - NULL, /* 0Bh */ - NULL, /* 0Ch */ - NULL, /* 0Dh */ - NULL, /* 0Eh */ - NULL, /* 0Fh */ - "INTERMEDIATE", /* 10h */ - NULL, /* 11h */ - NULL, /* 12h */ - NULL, /* 13h */ - "INTERMEDIATE-CONDITION MET", /* 14h */ - NULL, /* 15h */ - NULL, /* 16h */ - NULL, /* 17h */ - "RESERVATION CONFLICT", /* 18h */ - NULL, /* 19h */ - NULL, /* 1Ah */ - NULL, /* 1Bh */ - NULL, /* 1Ch */ - NULL, /* 1Dh */ - NULL, /* 1Eh */ - NULL, /* 1Fh */ - NULL, /* 20h */ - NULL, /* 21h */ - "COMMAND TERMINATED", /* 22h */ - NULL, /* 23h */ - NULL, /* 24h */ - NULL, /* 25h */ - NULL, /* 26h */ - NULL, /* 27h */ - "TASK SET FULL", /* 28h */ - NULL, /* 29h */ - NULL, /* 2Ah */ - NULL, /* 2Bh */ - NULL, /* 2Ch */ - NULL, /* 2Dh */ - NULL, /* 2Eh */ - NULL, /* 2Fh */ - "ACA ACTIVE", /* 30h */ - NULL -}; - -static const char *ScsiCommonOpString[] = { - "TEST UNIT READY", /* 00h */ - "REZERO UNIT (REWIND)", /* 01h */ - NULL, /* 02h */ - "REQUEST_SENSE", /* 03h */ - "FORMAT UNIT (MEDIUM)", /* 04h */ - "READ BLOCK LIMITS", /* 05h */ - NULL, /* 06h */ - "REASSIGN BLOCKS", /* 07h */ - "READ(6)", /* 08h */ - NULL, /* 09h */ - "WRITE(6)", /* 0Ah */ - "SEEK(6)", /* 0Bh */ - NULL, /* 0Ch */ - NULL, /* 0Dh */ - NULL, /* 0Eh */ - "READ REVERSE", /* 0Fh */ - "WRITE_FILEMARKS", /* 10h */ - "SPACE(6)", /* 11h */ - "INQUIRY", /* 12h */ - NULL -}; - -static const char *SenseKeyString[] = { - "NO SENSE", /* 0h */ - "RECOVERED ERROR", /* 1h */ - "NOT READY", /* 2h */ - "MEDIUM ERROR", /* 3h */ - "HARDWARE ERROR", /* 4h */ - "ILLEGAL REQUEST", /* 5h */ - "UNIT ATTENTION", /* 6h */ - "DATA PROTECT", /* 7h */ - "BLANK CHECK", /* 8h */ - "VENDOR-SPECIFIC", /* 9h */ - "ABORTED COPY", /* Ah */ - "ABORTED COMMAND", /* Bh */ - "EQUAL (obsolete)", /* Ch */ - "VOLUME OVERFLOW", /* Dh */ - "MISCOMPARE", /* Eh */ - "RESERVED", /* Fh */ - NULL -}; - -#define SPECIAL_ASCQ(c,q) \ - (((c) == 0x40 && (q) != 0x00) || ((c) == 0x4D) || ((c) == 0x70)) - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -static int dump_cdb(char *foo, unsigned char *cdb) -{ - int i, grpCode, cdbLen; - int l = 0; - - grpCode = cdb[0] >> 5; - if (grpCode < 1) - cdbLen = 6; - else if (grpCode < 3) - cdbLen = 10; - else if (grpCode == 5) - cdbLen = 12; - else - cdbLen = 16; - - for (i=0; i < cdbLen; i++) - l += sprintf(foo+l, " %02X", cdb[i]); - - return l; -} - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* Do ASC/ASCQ lookup/grindage to English readable string(s) */ -static const char * ascq_set_strings_4max( - u8 ASC, u8 ASCQ, - const char **s1, const char **s2, const char **s3, const char **s4) -{ - static const char *asc_04_part1_string = "LOGICAL UNIT "; - static const char *asc_04_part2a_string = "NOT READY, "; - static const char *asc_04_part2b_string = "IS "; - static const char *asc_04_ascq_NN_part3_strings[] = { /* ASC ASCQ (hex) */ - "CAUSE NOT REPORTABLE", /* 04 00 */ - "IN PROCESS OF BECOMING READY", /* 04 01 */ - "INITIALIZING CMD. REQUIRED", /* 04 02 */ - "MANUAL INTERVENTION REQUIRED", /* 04 03 */ - /* Add " IN PROGRESS" to all the following... */ - "FORMAT", /* 04 04 */ - "REBUILD", /* 04 05 */ - "RECALCULATION", /* 04 06 */ - "OPERATION", /* 04 07 */ - "LONG WRITE", /* 04 08 */ - "SELF-TEST", /* 04 09 */ - NULL - }; - static char *asc_04_part4_string = " IN PROGRESS"; - - static char *asc_29_ascq_NN_strings[] = { /* ASC ASCQ (hex) */ - "POWER ON, RESET, OR BUS DEVICE RESET OCCURRED", /* 29 00 */ - "POWER ON OCCURRED", /* 29 01 */ - "SCSI BUS RESET OCCURRED", /* 29 02 */ - "BUS DEVICE RESET FUNCTION OCCURRED", /* 29 03 */ - "DEVICE INTERNAL RESET", /* 29 04 */ - "TRANSCEIVER MODE CHANGED TO SINGLE-ENDED", /* 29 05 */ - "TRANSCEIVER MODE CHANGED TO LVD", /* 29 06 */ - NULL - }; - static char *ascq_vendor_uniq = "(Vendor Unique)"; - static char *ascq_noone = "(no matching ASC/ASCQ description found)"; - int idx; - - *s1 = *s2 = *s3 = *s4 = ""; /* set'em all to the empty "" string */ - - /* CHECKME! Need lock/sem? - * Update and examine for isense module presense. - */ - mptscsih_ASCQ_TablePtr = (ASCQ_Table_t *)mpt_v_ASCQ_TablePtr; - - if (mptscsih_ASCQ_TablePtr == NULL) { - /* 2nd chances... */ - if (ASC == 0x04 && (ASCQ < sizeof(asc_04_ascq_NN_part3_strings)/sizeof(char*)-1)) { - *s1 = asc_04_part1_string; - *s2 = (ASCQ == 0x01) ? asc_04_part2b_string : asc_04_part2a_string; - *s3 = asc_04_ascq_NN_part3_strings[ASCQ]; - /* check for " IN PROGRESS" ones */ - if (ASCQ >= 0x04) - *s4 = asc_04_part4_string; - } else if (ASC == 0x29 && (ASCQ < sizeof(asc_29_ascq_NN_strings)/sizeof(char*)-1)) - *s1 = asc_29_ascq_NN_strings[ASCQ]; - /* - * Else { leave all *s[1-4] values pointing to the empty "" string } - */ - return *s1; - } - - /* - * Need to check ASC here; if it is "special," then - * the ASCQ is variable, and indicates failed component number. - * We must treat the ASCQ as a "don't care" while searching the - * mptscsih_ASCQ_Table[] by masking it off, and then restoring it later - * on when we actually need to identify the failed component. - */ - if (SPECIAL_ASCQ(ASC,ASCQ)) - ASCQ = 0xFF; - - /* OK, now search mptscsih_ASCQ_Table[] for a matching entry */ - for (idx = 0; mptscsih_ASCQ_TablePtr && idx < mpt_ASCQ_TableSz; idx++) - if ((ASC == mptscsih_ASCQ_TablePtr[idx].ASC) && (ASCQ == mptscsih_ASCQ_TablePtr[idx].ASCQ)) { - *s1 = mptscsih_ASCQ_TablePtr[idx].Description; - return *s1; - } - - if ((ASC >= 0x80) || (ASCQ >= 0x80)) - *s1 = ascq_vendor_uniq; - else - *s1 = ascq_noone; - - return *s1; -} - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* - * SCSI Information Report; desired output format... - *--- -SCSI Error: (iocnum:target_id:LUN) Status=02h (CHECK CONDITION) - Key=6h (UNIT ATTENTION); FRU=03h - ASC/ASCQ=29h/00h, "POWER ON, RESET, OR BUS DEVICE RESET OCCURRED" - CDB: 00 00 00 00 00 00 - TestUnitReady - *--- - */ -/* - * SCSI Error Report; desired output format... - *--- -SCSI Error Report =-=-=-=-=-=-=-=-=-=-=-=-=-= (ioc0,scsi0:0) - SCSI_Status=02h (CHECK CONDITION) - Original_CDB[]: 00 00 00 00 00 00 - TestUnitReady - SenseData[12h]: 70 00 06 00 00 00 00 0A 00 00 00 00 29 00 03 00 00 00 - SenseKey=6h (UNIT ATTENTION); FRU=03h - ASC/ASCQ=29h/00h, "POWER ON, RESET, OR BUS DEVICE RESET OCCURRED" - *--- - */ - -int mpt_ScsiHost_ErrorReport(IO_Info_t *ioop) -{ - char foo[512]; - char buf2[32]; - char *statstr; - const char *opstr; - int sk = SD_Sense_Key(ioop->sensePtr); - const char *skstr = SenseKeyString[sk]; - unsigned char asc = SD_ASC(ioop->sensePtr); - unsigned char ascq = SD_ASCQ(ioop->sensePtr); - int l; - - /* Change the error logging to only report errors on - * read and write commands. Ignore errors on other commands. - * Should this be configurable via proc? - */ - switch (ioop->cdbPtr[0]) { - case READ_6: - case WRITE_6: - case READ_10: - case WRITE_10: - case READ_12: - case WRITE_12: - case READ_16: - case WRITE_16: - break; - default: - return 0; - } - - /* - * More quiet mode. - * Filter out common, repetitive, warning-type errors... like: - * POWER ON (06,29/00 or 06,29/01), - * SPINNING UP (02,04/01), - * LOGICAL UNIT NOT SUPPORTED (05,25/00), etc. - */ - if (sk == SK_NO_SENSE) { - return 0; - } - - if ( (sk==SK_UNIT_ATTENTION && asc==0x29 && (ascq==0x00 || ascq==0x01)) - || (sk==SK_NOT_READY && asc==0x04 && (ascq==0x01 || ascq==0x02)) - || (sk==SK_ILLEGAL_REQUEST && asc==0x25 && ascq==0x00) - ) - { - /* Do nothing! */ - return 0; - } - - /* Prevent the system from continually writing to the log - * if a medium is not found: 02 3A 00 - * Changer issues: TUR, Read Capacity, Table of Contents continually - */ - if (sk==SK_NOT_READY && asc==0x3A) { - if (ioop->cdbPtr == NULL) { - return 0; - } else if ((ioop->cdbPtr[0] == CMD_TestUnitReady) || - (ioop->cdbPtr[0] == CMD_ReadCapacity) || - (ioop->cdbPtr[0] == 0x43)) { - return 0; - } - } - if (sk==SK_UNIT_ATTENTION) { - if (ioop->cdbPtr == NULL) - return 0; - else if (ioop->cdbPtr[0] == CMD_TestUnitReady) - return 0; - } - - /* - * Protect ourselves... - */ - if (ioop->cdbPtr == NULL) - ioop->cdbPtr = dummyCDB; - if (ioop->sensePtr == NULL) - ioop->sensePtr = dummySenseData; - if (ioop->inqPtr == NULL) - ioop->inqPtr = dummyInqData; - if (ioop->dataPtr == NULL) - ioop->dataPtr = dummyScsiData; - - statstr = NULL; - if ((ioop->SCSIStatus >= sizeof(ScsiStatusString)/sizeof(char*)-1) || - ((statstr = (char*)ScsiStatusString[ioop->SCSIStatus]) == NULL)) { - (void) sprintf(buf2, "Bad-Reserved-%02Xh", ioop->SCSIStatus); - statstr = buf2; - } - - opstr = NULL; - if (1+ioop->cdbPtr[0] <= sizeof(ScsiCommonOpString)/sizeof(char*)) - opstr = ScsiCommonOpString[ioop->cdbPtr[0]]; - else if (mpt_ScsiOpcodesPtr) - opstr = mpt_ScsiOpcodesPtr[ioop->cdbPtr[0]]; - - l = sprintf(foo, "SCSI Error: (%s) Status=%02Xh (%s)\n", - ioop->DevIDStr, - ioop->SCSIStatus, - statstr); - l += sprintf(foo+l, " Key=%Xh (%s); FRU=%02Xh\n ASC/ASCQ=%02Xh/%02Xh", - sk, skstr, SD_FRU(ioop->sensePtr), asc, ascq ); - { - const char *x1, *x2, *x3, *x4; - x1 = x2 = x3 = x4 = ""; - x1 = ascq_set_strings_4max(asc, ascq, &x1, &x2, &x3, &x4); - if (x1 != NULL) { - if (x1[0] != '(') - l += sprintf(foo+l, " \"%s%s%s%s\"", x1,x2,x3,x4); - else - l += sprintf(foo+l, " %s%s%s%s", x1,x2,x3,x4); - } - } - l += sprintf(foo+l, "\n CDB:"); - l += dump_cdb(foo+l, ioop->cdbPtr); - if (opstr) - l += sprintf(foo+l, " - \"%s\"", opstr); - l += sprintf(foo+l, "\n"); - - PrintF(("%s\n", foo)); - - return l; -} - -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ - /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* * mptscsih_initTarget - Target, LUN alloc/free functionality. @@ -4176,48 +3346,33 @@ mptscsih_initTarget(MPT_SCSI_HOST *hd, i { int indexed_lun, lun_index; VirtDevice *vdev; + ScsiCfgData *pSpi; char data_56; - dprintk((MYIOC_s_INFO_FMT "initTarget bus=%d id=%d lun=%d hd=%p\n", + dinitprintk((MYIOC_s_INFO_FMT "initTarget bus=%d id=%d lun=%d hd=%p\n", hd->ioc->name, bus_id, target_id, lun, hd)); - /* Is LUN supported? If so, upper 3 bits will be 0 + /* Is LUN supported? If so, upper 2 bits will be 0 * in first byte of inquiry data. */ if (data[0] & 0xe0) return; - vdev = hd->Targets[target_id]; + if ((vdev = hd->Targets[target_id]) == NULL) { + return; + } lun_index = (lun >> 5); /* 32 luns per lun_index */ indexed_lun = (lun % 32); vdev->luns[lun_index] |= (1 << indexed_lun); - vdev->raidVolume = 0; if (hd->is_spi) { - if (hd->ioc->spi_data.isRaid & (1 << target_id)) { - vdev->raidVolume = 1; - ddvtprintk((KERN_INFO "RAID Volume @ id %d\n", target_id)); - } - } - - if (!(vdev->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY)) { - if ( dlen > 8 ) { - memcpy (vdev->inq_data, data, 8); - } else { - memcpy (vdev->inq_data, data, dlen); - } - vdev->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY; - - /* If LUN 0, tape and have not done DV, set the DV flag. - */ - if (hd->is_spi && (lun == 0) && (data[0] == SCSI_TYPE_TAPE)) { - ScsiCfgData *pSpi = &hd->ioc->spi_data; - if (pSpi->dvStatus[target_id] & MPT_SCSICFG_DV_NOT_DONE) - pSpi->dvStatus[target_id] |= MPT_SCSICFG_NEED_DV; - } - - if ( (data[0] == SCSI_TYPE_PROC) && + if ((data[0] == TYPE_PROCESSOR) && (hd->ioc->spi_data.Saf_Te)) { + /* Treat all Processors as SAF-TE if + * command line option is set */ + vdev->tflags |= MPT_TARGET_FLAGS_SAF_TE_ISSUED; + mptscsih_writeIOCPage4(hd, target_id, bus_id); + }else if ((data[0] == TYPE_PROCESSOR) && !(vdev->tflags & MPT_TARGET_FLAGS_SAF_TE_ISSUED )) { if ( dlen > 49 ) { vdev->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY; @@ -4230,30 +3385,51 @@ mptscsih_initTarget(MPT_SCSI_HOST *hd, i vdev->tflags |= MPT_TARGET_FLAGS_SAF_TE_ISSUED; mptscsih_writeIOCPage4(hd, target_id, bus_id); } - } else { - /* Treat all Processors as SAF-TE if - * command line option is set */ - if ( hd->ioc->spi_data.Saf_Te ) { - vdev->tflags |= MPT_TARGET_FLAGS_SAF_TE_ISSUED; - mptscsih_writeIOCPage4(hd, target_id, bus_id); - } } } + if (!(vdev->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY)) { + if ( dlen > 8 ) { + memcpy (vdev->inq_data, data, 8); + } else { + memcpy (vdev->inq_data, data, dlen); + } - data_56 = 0x0F; /* Default to full capabilities if Inq data length is < 57 */ - if (dlen > 56) { - if ( (!(vdev->tflags & MPT_TARGET_FLAGS_VALID_56))) { - /* Update the target capabilities + /* If have not done DV, set the DV flag. */ - data_56 = data[56]; - vdev->tflags |= MPT_TARGET_FLAGS_VALID_56; + pSpi = &hd->ioc->spi_data; + if ((data[0] == TYPE_TAPE) || (data[0] == TYPE_PROCESSOR)) { + if (pSpi->dvStatus[target_id] & MPT_SCSICFG_DV_NOT_DONE) + pSpi->dvStatus[target_id] |= MPT_SCSICFG_NEED_DV; + } + + vdev->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY; + + + data_56 = 0x0F; /* Default to full capabilities if Inq data length is < 57 */ + if (dlen > 56) { + if ( (!(vdev->tflags & MPT_TARGET_FLAGS_VALID_56))) { + /* Update the target capabilities + */ + data_56 = data[56]; + vdev->tflags |= MPT_TARGET_FLAGS_VALID_56; + } + } + mptscsih_setTargetNegoParms(hd, vdev, data_56); + } else { + /* Initial Inquiry may not request enough data bytes to + * obtain byte 57. DV will; if target doesn't return + * at least 57 bytes, data[56] will be zero. */ + if (dlen > 56) { + if ( (!(vdev->tflags & MPT_TARGET_FLAGS_VALID_56))) { + /* Update the target capabilities + */ + data_56 = data[56]; + vdev->tflags |= MPT_TARGET_FLAGS_VALID_56; + mptscsih_setTargetNegoParms(hd, vdev, data_56); + } } } - mptscsih_setTargetNegoParms(hd, vdev, data_56); } - - dprintk((KERN_INFO " target = %p\n", vdev)); - return; } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -4262,12 +3438,12 @@ mptscsih_initTarget(MPT_SCSI_HOST *hd, i * the Inquiry data, adapter capabilities, and NVRAM settings. * */ -void mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtDevice *target, char byte56) +static void +mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtDevice *target, char byte56) { ScsiCfgData *pspi_data = &hd->ioc->spi_data; int id = (int) target->target_id; int nvram; - char canQ = 0; VirtDevice *vdev; int ii; u8 width = MPT_NARROW; @@ -4276,14 +3452,6 @@ void mptscsih_setTargetNegoParms(MPT_SCS u8 version, nfactor; u8 noQas = 1; - if (!hd->is_spi) { - if (target->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY) { - if (target->inq_data[7] & 0x02) - target->tflags |= MPT_TARGET_FLAGS_Q_YES; - } - return; - } - target->negoFlags = pspi_data->noQas; /* noQas == 0 => device supports QAS. Need byte 56 of Inq to determine @@ -4293,137 +3461,152 @@ void mptscsih_setTargetNegoParms(MPT_SCS /* Set flags based on Inquiry data */ - if (target->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY) { - version = target->inq_data[2] & 0x07; - if (version < 2) { - width = 0; - factor = MPT_ULTRA2; - offset = pspi_data->maxSyncOffset; - } else { - if (target->inq_data[7] & 0x20) { - width = 1; - } + version = target->inq_data[2] & 0x07; + if (version < 2) { + width = 0; + factor = MPT_ULTRA2; + offset = pspi_data->maxSyncOffset; + target->tflags &= ~MPT_TARGET_FLAGS_Q_YES; + } else { + if (target->inq_data[7] & 0x20) { + width = 1; + } - if (target->inq_data[7] & 0x10) { - /* bits 2 & 3 show Clocking support - */ + if (target->inq_data[7] & 0x10) { + factor = pspi_data->minSyncFactor; + if (target->tflags & MPT_TARGET_FLAGS_VALID_56) { + /* bits 2 & 3 show Clocking support */ if ((byte56 & 0x0C) == 0) factor = MPT_ULTRA2; else { if ((byte56 & 0x03) == 0) factor = MPT_ULTRA160; - else + else { factor = MPT_ULTRA320; - } - offset = pspi_data->maxSyncOffset; - - /* If RAID, never disable QAS - * else if non RAID, do not disable - * QAS if bit 1 is set - * bit 1 QAS support, non-raid only - * bit 0 IU support - */ - if ((target->raidVolume == 1) || (byte56 & 0x02)) { - noQas = 0; + if (byte56 & 0x02) + { + ddvtprintk((KERN_INFO "Enabling QAS due to byte56=%02x on id=%d!\n", byte56, id)); + noQas = 0; + } + if (target->inq_data[0] == TYPE_TAPE) { + if (byte56 & 0x01) + target->negoFlags |= MPT_TAPE_NEGO_IDP; + } + } } } else { - factor = MPT_ASYNC; - offset = 0; + ddvtprintk((KERN_INFO "Enabling QAS on id=%d due to ~TARGET_FLAGS_VALID_56!\n", id)); + noQas = 0; } - } + + offset = pspi_data->maxSyncOffset; - if (target->inq_data[7] & 0x02) { - canQ = 1; + /* If RAID, never disable QAS + * else if non RAID, do not disable + * QAS if bit 1 is set + * bit 1 QAS support, non-raid only + * bit 0 IU support + */ + if (target->raidVolume == 1) { + noQas = 0; + } + } else { + factor = MPT_ASYNC; + offset = 0; } + } - /* Update tflags based on NVRAM settings. (SCSI only) - */ - if (pspi_data->nvram && (pspi_data->nvram[id] != MPT_HOST_NVRAM_INVALID)) { - nvram = pspi_data->nvram[id]; - nfactor = (nvram & MPT_NVRAM_SYNC_MASK) >> 8; + if ( (target->inq_data[7] & 0x02) == 0) { + target->tflags &= ~MPT_TARGET_FLAGS_Q_YES; + } - if (width) - width = nvram & MPT_NVRAM_WIDE_DISABLE ? 0 : 1; + /* Update tflags based on NVRAM settings. (SCSI only) + */ + if (pspi_data->nvram && (pspi_data->nvram[id] != MPT_HOST_NVRAM_INVALID)) { + nvram = pspi_data->nvram[id]; + nfactor = (nvram & MPT_NVRAM_SYNC_MASK) >> 8; - if (offset > 0) { - /* Ensure factor is set to the - * maximum of: adapter, nvram, inquiry - */ - if (nfactor) { - if (nfactor < pspi_data->minSyncFactor ) - nfactor = pspi_data->minSyncFactor; + if (width) + width = nvram & MPT_NVRAM_WIDE_DISABLE ? 0 : 1; - factor = max(factor, nfactor); - if (factor == MPT_ASYNC) - offset = 0; - } else { + if (offset > 0) { + /* Ensure factor is set to the + * maximum of: adapter, nvram, inquiry + */ + if (nfactor) { + if (nfactor < pspi_data->minSyncFactor ) + nfactor = pspi_data->minSyncFactor; + + factor = max(factor, nfactor); + if (factor == MPT_ASYNC) offset = 0; - factor = MPT_ASYNC; - } } else { + offset = 0; factor = MPT_ASYNC; - } } - - /* Make sure data is consistent - */ - if ((!width) && (factor < MPT_ULTRA2)) { - factor = MPT_ULTRA2; + } else { + factor = MPT_ASYNC; } + } - /* Save the data to the target structure. - */ - target->minSyncFactor = factor; - target->maxOffset = offset; - target->maxWidth = width; - if (canQ) { - target->tflags |= MPT_TARGET_FLAGS_Q_YES; - } + /* Make sure data is consistent + */ + if ((!width) && (factor < MPT_ULTRA2)) { + factor = MPT_ULTRA2; + } - target->tflags |= MPT_TARGET_FLAGS_VALID_NEGO; + /* Save the data to the target structure. + */ + target->minSyncFactor = factor; + target->maxOffset = offset; + target->maxWidth = width; - /* Disable unused features. - */ - if (!width) - target->negoFlags |= MPT_TARGET_NO_NEGO_WIDE; + target->tflags |= MPT_TARGET_FLAGS_VALID_NEGO; - if (!offset) - target->negoFlags |= MPT_TARGET_NO_NEGO_SYNC; + /* Disable unused features. + */ + if (!width) + target->negoFlags |= MPT_TARGET_NO_NEGO_WIDE; - /* GEM, processor WORKAROUND - */ - if (((target->inq_data[0] & 0x1F) == 0x03) - || ((target->inq_data[0] & 0x1F) > 0x08)) { - target->negoFlags |= (MPT_TARGET_NO_NEGO_WIDE | MPT_TARGET_NO_NEGO_SYNC); - pspi_data->dvStatus[id] |= MPT_SCSICFG_BLK_NEGO; - } else { - if (noQas && (pspi_data->noQas == 0)) { - pspi_data->noQas |= MPT_TARGET_NO_NEGO_QAS; - target->negoFlags |= MPT_TARGET_NO_NEGO_QAS; - - /* Disable QAS in a mixed configuration case - */ - - ddvtprintk((KERN_INFO "Disabling QAS due to noQas=%02x on id=%d!\n", noQas, id)); - for (ii = 0; ii < id; ii++) { - if ( (vdev = hd->Targets[ii]) ) { - vdev->negoFlags |= MPT_TARGET_NO_NEGO_QAS; - } - } + if (!offset) + target->negoFlags |= MPT_TARGET_NO_NEGO_SYNC; + + if ( factor > MPT_ULTRA320 ) + noQas = 0; + + /* GEM, processor WORKAROUND + */ + if ((target->inq_data[0] == TYPE_PROCESSOR) || (target->inq_data[0] > 0x08)) { + target->negoFlags |= (MPT_TARGET_NO_NEGO_WIDE | MPT_TARGET_NO_NEGO_SYNC); + pspi_data->dvStatus[id] |= MPT_SCSICFG_BLK_NEGO; + } else { + if (noQas && (pspi_data->noQas == 0)) { + pspi_data->noQas |= MPT_TARGET_NO_NEGO_QAS; + target->negoFlags |= MPT_TARGET_NO_NEGO_QAS; + + /* Disable QAS in a mixed configuration case + */ + + ddvtprintk((KERN_INFO "Disabling QAS due to noQas=%02x on id=%d!\n", noQas, id)); + for (ii = 0; ii < id; ii++) { + if ( (vdev = hd->Targets[ii]) ) { + vdev->negoFlags |= MPT_TARGET_NO_NEGO_QAS; + mptscsih_writeSDP1(hd, 0, ii, vdev->negoFlags); + } } } - - /* Write SDP1 on this I/O to this target */ - if (pspi_data->dvStatus[id] & MPT_SCSICFG_NEGOTIATE) { - mptscsih_writeSDP1(hd, 0, id, hd->negoNvram); - pspi_data->dvStatus[id] &= ~MPT_SCSICFG_NEGOTIATE; - } else if (pspi_data->dvStatus[id] & MPT_SCSICFG_BLK_NEGO) { - mptscsih_writeSDP1(hd, 0, id, MPT_SCSICFG_BLK_NEGO); - pspi_data->dvStatus[id] &= ~MPT_SCSICFG_BLK_NEGO; - } } - return; + /* Write SDP1 on this I/O to this target */ + if (pspi_data->dvStatus[id] & MPT_SCSICFG_NEGOTIATE) { + ddvtprintk((KERN_INFO "MPT_SCSICFG_NEGOTIATE on id=%d!\n", id)); + mptscsih_writeSDP1(hd, 0, id, hd->negoNvram); + pspi_data->dvStatus[id] &= ~MPT_SCSICFG_NEGOTIATE; + } else if (pspi_data->dvStatus[id] & MPT_SCSICFG_BLK_NEGO) { + ddvtprintk((KERN_INFO "MPT_SCSICFG_BLK_NEGO on id=%d!\n", id)); + mptscsih_writeSDP1(hd, 0, id, MPT_SCSICFG_BLK_NEGO); + pspi_data->dvStatus[id] &= ~MPT_SCSICFG_BLK_NEGO; + } } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -4437,14 +3620,18 @@ void mptscsih_setTargetNegoParms(MPT_SCS static void mptscsih_set_dvflags(MPT_SCSI_HOST *hd, SCSIIORequest_t *pReq) { u8 cmd; + ScsiCfgData *pSpi; + ddvtprintk((" set_dvflags: id=%d lun=%d negoNvram=%x cmd=%x\n", + pReq->TargetID, pReq->LUN[1], hd->negoNvram, pReq->CDB[0])); + if ((pReq->LUN[1] != 0) || (hd->negoNvram != 0)) return; cmd = pReq->CDB[0]; if ((cmd == READ_CAPACITY) || (cmd == MODE_SENSE)) { - ScsiCfgData *pSpi = &hd->ioc->spi_data; + pSpi = &hd->ioc->spi_data; if ((pSpi->isRaid & (1 << pReq->TargetID)) && pSpi->pIocPg3) { /* Set NEED_DV for all hidden disks */ @@ -4510,6 +3697,8 @@ mptscsih_setDevicePage1Flags (u8 width, *requestedPtr |= (MPI_SCSIDEVPAGE1_RP_IU + MPI_SCSIDEVPAGE1_RP_DT); if ((flags & MPT_TARGET_NO_NEGO_QAS) == 0) *requestedPtr |= MPI_SCSIDEVPAGE1_RP_QAS; + if (flags & MPT_TAPE_NEGO_IDP) + *requestedPtr |= 0x08000000; } else if (factor < MPT_ULTRA2) { *requestedPtr |= MPI_SCSIDEVPAGE1_RP_DT; } @@ -4624,6 +3813,16 @@ mptscsih_writeSDP1(MPT_SCSI_HOST *hd, in //negoFlags = MPT_TARGET_NO_NEGO_SYNC; } + /* If id is not a raid volume, get the updated + * transmission settings from the target structure. + */ + if (hd->Targets && (pTarget = hd->Targets[id]) && !pTarget->raidVolume) { + width = pTarget->maxWidth; + factor = pTarget->minSyncFactor; + offset = pTarget->maxOffset; + negoFlags = pTarget->negoFlags; + } + #ifdef MPTSCSIH_ENABLE_DOMAIN_VALIDATION /* Force to async and narrow if DV has not been executed * for this ID @@ -4635,21 +3834,13 @@ mptscsih_writeSDP1(MPT_SCSI_HOST *hd, in } #endif - /* If id is not a raid volume, get the updated - * transmission settings from the target structure. - */ - if (hd->Targets && (pTarget = hd->Targets[id]) && !pTarget->raidVolume) { - width = pTarget->maxWidth; - factor = pTarget->minSyncFactor; - offset = pTarget->maxOffset; - negoFlags = pTarget->negoFlags; - } - if (flags & MPT_SCSICFG_BLK_NEGO) negoFlags = MPT_TARGET_NO_NEGO_WIDE | MPT_TARGET_NO_NEGO_SYNC; mptscsih_setDevicePage1Flags(width, factor, offset, &requested, &configuration, negoFlags); + dnegoprintk(("writeSDP1: id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x config=%x\n", + target_id, width, factor, offset, negoFlags, requested, configuration)); /* Get a MF for this command. */ @@ -4754,9 +3945,6 @@ mptscsih_writeIOCPage4(MPT_SCSI_HOST *hd return -EAGAIN; } - ddvprintk((MYIOC_s_INFO_FMT "writeIOCPage4 (mf=%p, id=%d)\n", - ioc->name, mf, target_id)); - /* Set the request and the data pointers. * Place data at end of MF. */ @@ -4793,9 +3981,9 @@ mptscsih_writeIOCPage4(MPT_SCSI_HOST *hd mpt_add_sge((char *)&pReq->PageBufferSGE, flagsLength, dataDma); - dsprintk((MYIOC_s_INFO_FMT - "writeIOCPage4: pgaddr 0x%x\n", - ioc->name, (target_id | (bus<<8)))); + dinitprintk((MYIOC_s_INFO_FMT + "writeIOCPage4: MaxSEP=%d ActiveSEP=%d id=%d bus=%d\n", + ioc->name, IOCPage4Ptr->MaxSEP, IOCPage4Ptr->ActiveSEP, target_id, bus)); mpt_put_msg_frame(ScsiDoneCtx, ioc, mf); @@ -4922,13 +4110,15 @@ mptscsih_scandv_complete(MPT_ADAPTER *io } else { SCSIIOReply_t *pReply; u16 status; + u8 scsi_status; pReply = (SCSIIOReply_t *) mr; status = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK; + scsi_status = pReply->SCSIStatus; ddvtprintk((KERN_NOTICE " IOCStatus=%04xh, SCSIState=%02xh, SCSIStatus=%02xh, IOCLogInfo=%08xh\n", - status, pReply->SCSIState, pReply->SCSIStatus, + status, pReply->SCSIState, scsi_status, le32_to_cpu(pReply->IOCLogInfo))); switch(status) { @@ -4973,7 +4163,7 @@ mptscsih_scandv_complete(MPT_ADAPTER *io /* save sense data in global structure */ completionCode = MPT_SCANDV_SENSE; - hd->pLocal->scsiStatus = pReply->SCSIStatus; + hd->pLocal->scsiStatus = scsi_status; sense_data = ((u8 *)hd->ioc->sense_buf_pool + (req_idx * MPT_SENSE_BUFFER_ALLOC)); @@ -4984,7 +4174,7 @@ mptscsih_scandv_complete(MPT_ADAPTER *io ddvprintk((KERN_NOTICE " Check Condition, sense ptr %p\n", sense_data)); } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_FAILED) { - if (pReq->CDB[0] == CMD_Inquiry) + if (pReq->CDB[0] == INQUIRY) completionCode = MPT_SCANDV_ISSUE_SENSE; else completionCode = MPT_SCANDV_DID_RESET; @@ -4994,11 +4184,8 @@ mptscsih_scandv_complete(MPT_ADAPTER *io else if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED) completionCode = MPT_SCANDV_DID_RESET; else { - /* If no error, this will be equivalent - * to MPT_SCANDV_GOOD - */ completionCode = MPT_SCANDV_GOOD; - hd->pLocal->scsiStatus = pReply->SCSIStatus; + hd->pLocal->scsiStatus = scsi_status; } break; @@ -5025,7 +4212,7 @@ mptscsih_scandv_complete(MPT_ADAPTER *io */ wakeup: /* Free Chain buffers (will never chain) in scan or dv */ - //mptscsih_freeChainBuffers(hd, req_idx); + //mptscsih_freeChainBuffers(ioc, req_idx); /* * Wake up the original calling thread @@ -5197,7 +4384,7 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTER /* Set command specific information */ switch (cmd) { - case CMD_Inquiry: + case INQUIRY: cmdLen = 6; dir = MPI_SCSIIO_CONTROL_READ; CDB[0] = cmd; @@ -5205,13 +4392,13 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTER cmdTimeout = 10; break; - case CMD_TestUnitReady: + case TEST_UNIT_READY: cmdLen = 6; dir = MPI_SCSIIO_CONTROL_READ; cmdTimeout = 10; break; - case CMD_StartStopUnit: + case START_STOP: cmdLen = 6; dir = MPI_SCSIIO_CONTROL_READ; CDB[0] = cmd; @@ -5219,7 +4406,7 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTER cmdTimeout = 15; break; - case CMD_RequestSense: + case REQUEST_SENSE: cmdLen = 6; CDB[0] = cmd; CDB[4] = io->size; @@ -5227,7 +4414,7 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTER cmdTimeout = 10; break; - case CMD_ReadBuffer: + case READ_BUFFER: cmdLen = 10; dir = MPI_SCSIIO_CONTROL_READ; CDB[0] = cmd; @@ -5246,7 +4433,7 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTER cmdTimeout = 10; break; - case CMD_WriteBuffer: + case WRITE_BUFFER: cmdLen = 10; dir = MPI_SCSIIO_CONTROL_WRITE; CDB[0] = cmd; @@ -5261,21 +4448,21 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTER cmdTimeout = 10; break; - case CMD_Reserve6: + case RESERVE: cmdLen = 6; dir = MPI_SCSIIO_CONTROL_READ; CDB[0] = cmd; cmdTimeout = 10; break; - case CMD_Release6: + case RELEASE: cmdLen = 6; dir = MPI_SCSIIO_CONTROL_READ; CDB[0] = cmd; cmdTimeout = 10; break; - case CMD_SynchronizeCache: + case SYNCHRONIZE_CACHE: cmdLen = 10; dir = MPI_SCSIIO_CONTROL_READ; CDB[0] = cmd; @@ -5331,7 +4518,7 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTER else pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_UNTAGGED); - if (cmd == CMD_RequestSense) { + if (cmd == REQUEST_SENSE) { pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_UNTAGGED); ddvprintk((MYIOC_s_INFO_FMT "Untagged! 0x%2x\n", hd->ioc->name, cmd)); @@ -5439,7 +4626,7 @@ mptscsih_synchronize_cache(MPT_SCSI_HOST /* Following parameters will not change * in this routine. */ - iocmd.cmd = CMD_SynchronizeCache; + iocmd.cmd = SYNCHRONIZE_CACHE; iocmd.flags = 0; iocmd.physDiskNum = -1; iocmd.data = NULL; @@ -5509,6 +4696,9 @@ mptscsih_synchronize_cache(MPT_SCSI_HOST /* Force to async, narrow */ mptscsih_setDevicePage1Flags(0, MPT_ASYNC, 0, &requested, &configuration, flags); + dnegoprintk(("syncronize cache: id=%d width=0 factor=MPT_ASYNC " + "offset=0 negoFlags=%x request=%x config=%x\n", + id, flags, requested, configuration)); pcfg1Data->RequestedParameters = le32_to_cpu(requested); pcfg1Data->Reserved = 0; pcfg1Data->Configuration = le32_to_cpu(configuration); @@ -5597,8 +4787,7 @@ mptscsih_domainValidation(void *arg) } spin_unlock_irqrestore(&dvtaskQ_lock, flags); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/4); + msleep(250); /* DV only to SCSI adapters */ if ((int)ioc->chip_type <= (int)FC929) @@ -5646,8 +4835,7 @@ mptscsih_domainValidation(void *arg) hd->ioc->spi_data.dvStatus[id] |= MPT_SCSICFG_DV_PENDING; hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_NEED_DV; - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/4); + msleep(250); /* If hidden phys disk, block IO's to all * raid volumes @@ -5681,11 +4869,6 @@ mptscsih_domainValidation(void *arg) } } - /* Post OS IOs that were pended while - * DV running. - */ - post_pendingQ_commands(hd); - if (hd->ioc->spi_data.noQas) mptscsih_qas_check(hd, id); } @@ -5741,11 +4924,14 @@ static void mptscsih_qas_check(MPT_SCSI_ if ((pTarget != NULL) && (!pTarget->raidVolume)) { if ((pTarget->negoFlags & hd->ioc->spi_data.noQas) == 0) { pTarget->negoFlags |= hd->ioc->spi_data.noQas; + dnegoprintk(("writeSDP1: id=%d flags=0\n", id)); mptscsih_writeSDP1(hd, 0, ii, 0); } } else { - if (mptscsih_is_phys_disk(hd->ioc, ii) == 1) + if (mptscsih_is_phys_disk(hd->ioc, ii) == 1) { + dnegoprintk(("writeSDP1: id=%d SCSICFG_USE_NVRAM\n", id)); mptscsih_writeSDP1(hd, 0, ii, MPT_SCSICFG_USE_NVRAM); + } } } return; @@ -5824,7 +5010,7 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus lun = 0; bus = (u8) bus_number; ddvtprintk((MYIOC_s_NOTE_FMT - "DV started: bus=%d, id %d dv @ %p\n", + "DV started: bus=%d, id=%d dv @ %p\n", ioc->name, bus, id, &dv)); /* Prep DV structure @@ -5838,8 +5024,6 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus */ dv.cmd = MPT_GET_NVRAM_VALS; mptscsih_dv_parms(hd, &dv, NULL); - if ((!dv.max.width) && (!dv.max.offset)) - return 0; /* Prep SCSI IO structure */ @@ -5851,15 +5035,6 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus iocmd.rsvd = iocmd.rsvd2 = 0; pTarget = hd->Targets[id]; - if (pTarget && (pTarget->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY)) { - /* Another GEM workaround. Check peripheral device type, - * if PROCESSOR, quit DV. - */ - if (((pTarget->inq_data[0] & 0x1F) == 0x03) || ((pTarget->inq_data[0] & 0x1F) > 0x08)) { - pTarget->negoFlags |= (MPT_TARGET_NO_NEGO_WIDE | MPT_TARGET_NO_NEGO_SYNC); - return 0; - } - } /* Use tagged commands if possible. */ @@ -5959,7 +5134,7 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus /* Finish iocmd inititialization - hidden or visible disk? */ if (ioc->spi_data.pIocPg3) { - /* Searc IOC page 3 for matching id + /* Search IOC page 3 for matching id */ Ioc3PhysDisk_t *pPDisk = ioc->spi_data.pIocPg3->PhysDisk; int numPDisk = ioc->spi_data.pIocPg3->NumPhysDisks; @@ -6001,7 +5176,7 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus */ hd->pLocal = NULL; readPage0 = 0; - sz = SCSI_STD_INQUIRY_BYTES; + sz = SCSI_MAX_INQUIRY_BYTES; rc = MPT_SCANDV_GOOD; while (1) { ddvprintk((MYIOC_s_NOTE_FMT "DV: Start Basic test on id=%d\n", ioc->name, id)); @@ -6026,7 +5201,7 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus ((hd->ioc->facts.FWVersion.Word >= 0x01010000) && (hd->ioc->facts.FWVersion.Word < 0x01010B00)) ) { - iocmd.cmd = CMD_RequestSense; + iocmd.cmd = REQUEST_SENSE; iocmd.data_dma = buf1_dma; iocmd.data = pbuf1; iocmd.size = 0x12; @@ -6046,10 +5221,11 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus goto target_done; } - iocmd.cmd = CMD_Inquiry; + iocmd.cmd = INQUIRY; iocmd.data_dma = buf1_dma; iocmd.data = pbuf1; iocmd.size = sz; + memset(pbuf1, 0x00, sz); if (mptscsih_do_cmd(hd, &iocmd) < 0) goto target_done; else { @@ -6057,7 +5233,7 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus goto target_done; rc = hd->pLocal->completion; if (rc == MPT_SCANDV_GOOD) { - if (hd->pLocal->scsiStatus == STS_BUSY) { + if (hd->pLocal->scsiStatus == SAM_STAT_BUSY) { if ((iocmd.flags & MPT_ICFLAG_TAGGED_CMD) == 0) retcode = 1; else @@ -6087,7 +5263,17 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus /* Another GEM workaround. Check peripheral device type, * if PROCESSOR, quit DV. */ - if (((pbuf1[0] & 0x1F) == 0x03) || ((pbuf1[0] & 0x1F) > 0x08)) + if (inq0 == TYPE_PROCESSOR) { + mptscsih_initTarget(hd, + bus, + id, + lun, + pbuf1, + sz); + goto target_done; + } + + if (inq0 > 0x08) goto target_done; if (mptscsih_do_cmd(hd, &iocmd) < 0) @@ -6111,6 +5297,9 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus if ((pbuf1[56] & 0x02) == 0) { pTarget->negoFlags |= MPT_TARGET_NO_NEGO_QAS; hd->ioc->spi_data.noQas = MPT_TARGET_NO_NEGO_QAS; + ddvprintk((MYIOC_s_NOTE_FMT + "DV: Start Basic noQas on id=%d due to pbuf1[56]=%x\n", + ioc->name, id, pbuf1[56])); } } } @@ -6127,10 +5316,11 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus if ((!dv.now.width) && (!dv.now.offset)) goto target_done; - iocmd.cmd = CMD_Inquiry; + iocmd.cmd = INQUIRY; iocmd.data_dma = buf2_dma; iocmd.data = pbuf2; iocmd.size = sz; + memset(pbuf2, 0x00, sz); if (mptscsih_do_cmd(hd, &iocmd) < 0) goto target_done; else if (hd->pLocal == NULL) @@ -6183,14 +5373,26 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus if (memcmp(pbuf1, pbuf2, sz) != 0) { if (!firstPass) doFallback = 1; - } else + } else { + ddvprintk((MYIOC_s_NOTE_FMT + "DV:Inquiry compared id=%d, calling initTarget\n", ioc->name, id)); + hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_DV_NOT_DONE; + mptscsih_initTarget(hd, + bus, + id, + lun, + pbuf1, + sz); break; /* test complete */ + } } } else if (rc == MPT_SCANDV_ISSUE_SENSE) doFallback = 1; /* set fallback flag */ - else if ((rc == MPT_SCANDV_DID_RESET) || (rc == MPT_SCANDV_SENSE)) + else if ((rc == MPT_SCANDV_DID_RESET) || + (rc == MPT_SCANDV_SENSE) || + (rc == MPT_SCANDV_FALLBACK)) doFallback = 1; /* set fallback flag */ else goto target_done; @@ -6199,6 +5401,10 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus } } ddvprintk((MYIOC_s_NOTE_FMT "DV: Basic test on id=%d completed OK.\n", ioc->name, id)); + + if (driver_setup.dv == 0) + goto target_done; + inq0 = (*pbuf1) & 0x1F; /* Continue only for disks @@ -6222,7 +5428,7 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT; cfg.dir = 1; - iocmd.cmd = CMD_TestUnitReady; + iocmd.cmd = TEST_UNIT_READY; iocmd.data_dma = -1; iocmd.data = NULL; iocmd.size = 0; @@ -6245,14 +5451,14 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", ioc->name, skey, asc, ascq)); - if (skey == SK_UNIT_ATTENTION) + if (skey == UNIT_ATTENTION) notDone++; /* repeat */ - else if ((skey == SK_NOT_READY) && + else if ((skey == NOT_READY) && (asc == 0x04)&&(ascq == 0x01)) { /* wait then repeat */ mdelay (2000); notDone++; - } else if ((skey == SK_NOT_READY) && (asc == 0x3A)) { + } else if ((skey == NOT_READY) && (asc == 0x3A)) { /* no medium, try read test anyway */ notDone = 0; } else { @@ -6266,7 +5472,7 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus goto target_done; } - iocmd.cmd = CMD_ReadBuffer; + iocmd.cmd = READ_BUFFER; iocmd.data_dma = buf1_dma; iocmd.data = pbuf1; iocmd.size = 4; @@ -6314,11 +5520,11 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus ddvprintk((MYIOC_s_INFO_FMT "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", ioc->name, skey, asc, ascq)); - if (skey == SK_ILLEGAL_REQUEST) { + if (skey == ILLEGAL_REQUEST) { notDone = 0; - } else if (skey == SK_UNIT_ATTENTION) { + } else if (skey == UNIT_ATTENTION) { notDone++; /* repeat */ - } else if ((skey == SK_NOT_READY) && + } else if ((skey == NOT_READY) && (asc == 0x04)&&(ascq == 0x01)) { /* wait then repeat */ mdelay (2000); @@ -6373,14 +5579,14 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus for (patt = 0; patt < 4; patt++) { ddvprintk(("Pattern %d\n", patt)); if ((iocmd.flags & MPT_ICFLAG_RESERVED) && (iocmd.flags & MPT_ICFLAG_DID_RESET)) { - iocmd.cmd = CMD_TestUnitReady; + iocmd.cmd = TEST_UNIT_READY; iocmd.data_dma = -1; iocmd.data = NULL; iocmd.size = 0; if (mptscsih_do_cmd(hd, &iocmd) < 0) goto target_done; - iocmd.cmd = CMD_Release6; + iocmd.cmd = RELEASE; iocmd.data_dma = -1; iocmd.data = NULL; iocmd.size = 0; @@ -6402,7 +5608,7 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus repeat = 5; while (repeat && (!(iocmd.flags & MPT_ICFLAG_RESERVED))) { - iocmd.cmd = CMD_Reserve6; + iocmd.cmd = RESERVE; iocmd.data_dma = -1; iocmd.data = NULL; iocmd.size = 0; @@ -6425,7 +5631,7 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus ddvprintk(("SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", skey, asc, ascq)); - if ((skey == SK_NOT_READY) && (asc == 0x04)&& + if ((skey == NOT_READY) && (asc == 0x04)&& (ascq == 0x01)) { /* wait then repeat */ mdelay (2000); @@ -6444,7 +5650,7 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus } mptscsih_fillbuf(pbuf1, sz, patt, 1); - iocmd.cmd = CMD_WriteBuffer; + iocmd.cmd = WRITE_BUFFER; iocmd.data_dma = buf1_dma; iocmd.data = pbuf1; iocmd.size = sz; @@ -6485,10 +5691,10 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus ddvprintk((MYIOC_s_INFO_FMT "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", ioc->name, skey, hd->pLocal->sense[12], hd->pLocal->sense[13])); - if (skey == SK_UNIT_ATTENTION) { + if (skey == UNIT_ATTENTION) { patt = -1; continue; - } else if (skey == SK_ILLEGAL_REQUEST) { + } else if (skey == ILLEGAL_REQUEST) { if (iocmd.flags & MPT_ICFLAG_ECHO) { if (dataBufSize >= bufsize) { iocmd.flags &= ~MPT_ICFLAG_ECHO; @@ -6506,7 +5712,7 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus } } - iocmd.cmd = CMD_ReadBuffer; + iocmd.cmd = READ_BUFFER; iocmd.data_dma = buf2_dma; iocmd.data = pbuf2; iocmd.size = sz; @@ -6581,7 +5787,7 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus ddvprintk((MYIOC_s_INFO_FMT "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", ioc->name, skey, hd->pLocal->sense[12], hd->pLocal->sense[13])); - if (skey == SK_UNIT_ATTENTION) { + if (skey == UNIT_ATTENTION) { patt = -1; continue; } @@ -6597,7 +5803,7 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int bus target_done: if (iocmd.flags & MPT_ICFLAG_RESERVED) { - iocmd.cmd = CMD_Release6; + iocmd.cmd = RELEASE; iocmd.data_dma = -1; iocmd.data = NULL; iocmd.size = 0; @@ -6619,8 +5825,11 @@ target_done: if ((cfg.hdr != NULL) && (retcode == 0)){ /* If disk, not U320, disable QAS */ - if ((inq0 == 0) && (dv.now.factor > MPT_ULTRA320)) + if ((inq0 == 0) && (dv.now.factor > MPT_ULTRA320)) { hd->ioc->spi_data.noQas = MPT_TARGET_NO_NEGO_QAS; + ddvprintk((MYIOC_s_NOTE_FMT + "noQas set due to id=%d has factor=%x\n", ioc->name, id, dv.now.factor)); + } dv.cmd = MPT_SAVE; mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data); @@ -6649,8 +5858,8 @@ target_done: if (pDvBuf) pci_free_consistent(ioc->pcidev, dv_alloc, pDvBuf, dvbuf_dma); - ddvtprintk((MYIOC_s_INFO_FMT "DV Done.\n", - ioc->name)); + ddvtprintk((MYIOC_s_INFO_FMT "DV Done id=%d\n", + ioc->name, id)); return retcode; } @@ -6730,8 +5939,8 @@ mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVP dv->max.offset = offset; dv->max.factor = factor; dv->max.flags = negoFlags; - ddvprintk((" width %d, factor %x, offset %x flags %x\n", - width, factor, offset, negoFlags)); + ddvprintk((" id=%d width=%d factor=%x offset=%x flags=%x\n", + id, width, factor, offset, negoFlags)); break; case MPT_UPDATE_MAX: @@ -6749,8 +5958,8 @@ mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVP dv->now.width = dv->max.width; dv->now.offset = dv->max.offset; dv->now.factor = dv->max.factor; - ddvprintk(("width %d, factor %x, offset %x, flags %x\n", - dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags)); + ddvprintk(("id=%d width=%d factor=%x offset=%x flags=%x\n", + id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags)); break; case MPT_SET_MAX: @@ -6766,14 +5975,15 @@ mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVP if (pPage1) { mptscsih_setDevicePage1Flags (dv->now.width, dv->now.factor, dv->now.offset, &val, &configuration, dv->now.flags); + dnegoprintk(("Setting Max: id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x config=%x\n", + id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags, val, configuration)); pPage1->RequestedParameters = le32_to_cpu(val); pPage1->Reserved = 0; pPage1->Configuration = le32_to_cpu(configuration); - } - ddvprintk(("width %d, factor %x, offset %x request %x, config %x\n", - dv->now.width, dv->now.factor, dv->now.offset, val, configuration)); + ddvprintk(("id=%d width=%d factor=%x offset=%x flags=%x request=%x configuration=%x\n", + id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags, val, configuration)); break; case MPT_SET_MIN: @@ -6790,12 +6000,14 @@ mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVP if (pPage1) { mptscsih_setDevicePage1Flags (width, factor, offset, &val, &configuration, negoFlags); + dnegoprintk(("Setting Min: id=%d width=%d factor=%x offset=%x negoFlags=%x request=%x config=%x\n", + id, width, factor, offset, negoFlags, val, configuration)); pPage1->RequestedParameters = le32_to_cpu(val); pPage1->Reserved = 0; pPage1->Configuration = le32_to_cpu(configuration); } - ddvprintk(("width %d, factor %x, offset %x request %x config %x\n", - width, factor, offset, val, configuration)); + ddvprintk(("id=%d width=%d factor=%x offset=%x request=%x config=%x negoFlags=%x\n", + id, width, factor, offset, val, configuration, negoFlags)); break; case MPT_FALLBACK: @@ -6855,6 +6067,7 @@ mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVP factor = MPT_ASYNC; } dv->max.flags |= MPT_TARGET_NO_NEGO_QAS; + dv->max.flags &= ~MPT_TAPE_NEGO_IDP; dv->now.width = width; dv->now.offset = offset; @@ -6865,21 +6078,23 @@ mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVP if (pPage1) { mptscsih_setDevicePage1Flags (width, factor, offset, &val, &configuration, dv->now.flags); + dnegoprintk(("Finish: id=%d width=%d offset=%d factor=%x flags=%x request=%x config=%x\n", + id, width, offset, factor, dv->now.flags, val, configuration)); pPage1->RequestedParameters = le32_to_cpu(val); pPage1->Reserved = 0; pPage1->Configuration = le32_to_cpu(configuration); } - ddvprintk(("Finish: offset %d, factor %x, width %d, request %x config %x\n", - dv->now.offset, dv->now.factor, dv->now.width, val, configuration)); + ddvprintk(("Finish: id=%d offset=%d factor=%x width=%d request=%x config=%x\n", + id, dv->now.offset, dv->now.factor, dv->now.width, val, configuration)); break; case MPT_SAVE: ddvprintk((MYIOC_s_NOTE_FMT "Saving to Target structure: ", hd->ioc->name)); - ddvprintk(("offset %d, factor %x, width %d \n", - dv->now.offset, dv->now.factor, dv->now.width)); + ddvprintk(("id=%d width=%x factor=%x offset=%d flags=%x\n", + id, dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags)); /* Save these values to target structures * or overwrite nvram (phys disks only). --- linux-2.6.9-rc1/drivers/message/fusion/scsi3.h 2004-08-24 00:02:32.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,707 +0,0 @@ -/* - * linux/drivers/message/fusion/scsi3.h - * SCSI-3 definitions and macros. - * (Ultimately) SCSI-3 definitions; for now, inheriting - * SCSI-2 definitions. - * - * Copyright (c) 1996-2004 Steven J. Ralston - * Written By: Steven J. Ralston (19960517) - * (mailto:sjralston1@netscape.net) - * (mailto:mpt_linux_developer@lsil.com) - * - * $Id: scsi3.h,v 1.9 2002/02/27 18:45:02 sralston Exp $ - */ - -#ifndef SCSI3_H_INCLUDED -#define SCSI3_H_INCLUDED -/***************************************************************************/ - -/**************************************************************************** - * - * Includes - */ -#ifdef __KERNEL__ -#include -#else - #ifndef U_STUFF_DEFINED - #define U_STUFF_DEFINED - typedef unsigned char u8; - typedef unsigned short u16; - typedef unsigned int u32; - #endif -#endif - -/**************************************************************************** - * - * Defines - */ - -/* - * SCSI Commands - */ -#define CMD_TestUnitReady 0x00 -#define CMD_RezeroUnit 0x01 /* direct-access devices */ -#define CMD_Rewind 0x01 /* sequential-access devices */ -#define CMD_RequestSense 0x03 -#define CMD_FormatUnit 0x04 -#define CMD_ReassignBlock 0x07 -#define CMD_Read6 0x08 -#define CMD_Write6 0x0A -#define CMD_WriteFilemark 0x10 -#define CMD_Space 0x11 -#define CMD_Inquiry 0x12 -#define CMD_ModeSelect6 0x15 -#define CMD_ModeSense6 0x1A -#define CMD_Reserve6 0x16 -#define CMD_Release6 0x17 -#define CMD_Erase 0x19 -#define CMD_StartStopUnit 0x1b /* direct-access devices */ -#define CMD_LoadUnload 0x1b /* sequential-access devices */ -#define CMD_ReceiveDiagnostic 0x1C -#define CMD_SendDiagnostic 0x1D -#define CMD_ReadCapacity 0x25 -#define CMD_Read10 0x28 -#define CMD_Write10 0x2A -#define CMD_WriteVerify 0x2E -#define CMD_Verify 0x2F -#define CMD_SynchronizeCache 0x35 -#define CMD_ReadDefectData 0x37 -#define CMD_WriteBuffer 0x3B -#define CMD_ReadBuffer 0x3C -#define CMD_ReadLong 0x3E -#define CMD_LogSelect 0x4C -#define CMD_LogSense 0x4D -#define CMD_ModeSelect10 0x55 -#define CMD_Reserve10 0x56 -#define CMD_Release10 0x57 -#define CMD_ModeSense10 0x5A -#define CMD_PersistReserveIn 0x5E -#define CMD_PersistReserveOut 0x5F -#define CMD_ReportLuns 0xA0 - -/* - * Control byte field - */ -#define CONTROL_BYTE_NACA_BIT 0x04 -#define CONTROL_BYTE_Flag_BIT 0x02 -#define CONTROL_BYTE_Link_BIT 0x01 - -/* - * SCSI Messages - */ -#define MSG_COMPLETE 0x00 -#define MSG_EXTENDED 0x01 -#define MSG_SAVE_POINTERS 0x02 -#define MSG_RESTORE_POINTERS 0x03 -#define MSG_DISCONNECT 0x04 -#define MSG_IDERROR 0x05 -#define MSG_ABORT 0x06 -#define MSG_REJECT 0x07 -#define MSG_NOP 0x08 -#define MSG_PARITY_ERROR 0x09 -#define MSG_LINKED_CMD_COMPLETE 0x0a -#define MSG_LCMD_COMPLETE_W_FLG 0x0b -#define MSG_BUS_DEVICE_RESET 0x0c -#define MSG_ABORT_TAG 0x0d -#define MSG_CLEAR_QUEUE 0x0e -#define MSG_INITIATE_RECOVERY 0x0f - -#define MSG_RELEASE_RECOVRY 0x10 -#define MSG_TERMINATE_IO 0x11 - -#define MSG_SIMPLE_QUEUE 0x20 -#define MSG_HEAD_OF_QUEUE 0x21 -#define MSG_ORDERED_QUEUE 0x22 -#define MSG_IGNORE_WIDE_RESIDUE 0x23 - -#define MSG_IDENTIFY 0x80 -#define MSG_IDENTIFY_W_DISC 0xc0 - -/* - * SCSI Phases - */ -#define PHS_DATA_OUT 0x00 -#define PHS_DATA_IN 0x01 -#define PHS_COMMAND 0x02 -#define PHS_STATUS 0x03 -#define PHS_MSG_OUT 0x06 -#define PHS_MSG_IN 0x07 - -/* - * Statuses - */ -#define STS_GOOD 0x00 -#define STS_CHECK_CONDITION 0x02 -#define STS_CONDITION_MET 0x04 -#define STS_BUSY 0x08 -#define STS_INTERMEDIATE 0x10 -#define STS_INTERMEDIATE_CONDITION_MET 0x14 -#define STS_RESERVATION_CONFLICT 0x18 -#define STS_COMMAND_TERMINATED 0x22 -#define STS_TASK_SET_FULL 0x28 -#define STS_QUEUE_FULL 0x28 -#define STS_ACA_ACTIVE 0x30 - -#define STS_VALID_MASK 0x3e - -#define SCSI_STATUS(x) ((x) & STS_VALID_MASK) - -/* - * SCSI QTag Types - */ -#define QTAG_SIMPLE 0x20 -#define QTAG_HEAD_OF_Q 0x21 -#define QTAG_ORDERED 0x22 - -/* - * SCSI Sense Key Definitons - */ -#define SK_NO_SENSE 0x00 -#define SK_RECOVERED_ERROR 0x01 -#define SK_NOT_READY 0x02 -#define SK_MEDIUM_ERROR 0x03 -#define SK_HARDWARE_ERROR 0x04 -#define SK_ILLEGAL_REQUEST 0x05 -#define SK_UNIT_ATTENTION 0x06 -#define SK_DATA_PROTECT 0x07 -#define SK_BLANK_CHECK 0x08 -#define SK_VENDOR_SPECIFIC 0x09 -#define SK_COPY_ABORTED 0x0a -#define SK_ABORTED_COMMAND 0x0b -#define SK_EQUAL 0x0c -#define SK_VOLUME_OVERFLOW 0x0d -#define SK_MISCOMPARE 0x0e -#define SK_RESERVED 0x0f - - - -#define SCSI_MAX_INQUIRY_BYTES 96 -#define SCSI_STD_INQUIRY_BYTES 36 - -#undef USE_SCSI_COMPLETE_INQDATA -/* - * Structure definition for SCSI Inquiry Data - * - * NOTE: The following structure is 96 bytes in size - * iff USE_SCSI_COMPLETE_INQDATA IS defined above (i.e. w/ "#define"). - * If USE_SCSI_COMPLETE_INQDATA is NOT defined above (i.e. w/ "#undef") - * then the following structure is only 36 bytes in size. - * THE CHOICE IS YOURS! - */ -typedef struct SCSI_Inquiry_Data -{ -#ifdef USE_SCSI_COMPLETE_INQDATA - u8 InqByte[SCSI_MAX_INQUIRY_BYTES]; -#else - u8 InqByte[SCSI_STD_INQUIRY_BYTES]; -#endif - -/* - * the following structure works only for little-endian (Intel, - * LSB first (1234) byte order) systems with 4-byte ints. - * - u32 Periph_Device_Type : 5, - Periph_Qualifier : 3, - Device_Type_Modifier : 7, - Removable_Media : 1, - ANSI_Version : 3, - ECMA_Version : 3, - ISO_Version : 2, - Response_Data_Format : 4, - reserved_0 : 3, - AERC : 1 ; - u32 Additional_Length : 8, - reserved_1 :16, - SftReset : 1, - CmdQue : 1, - reserved_2 : 1, - Linked : 1, - Sync : 1, - WBus16 : 1, - WBus32 : 1, - RelAdr : 1 ; - u8 Vendor_ID[8]; - u8 Product_ID[16]; - u8 Revision_Level [4]; -#ifdef USE_SCSI_COMPLETE_INQDATA - u8 Vendor_Specific[20]; - u8 reserved_3[40]; -#endif - * - */ - -} SCSI_Inquiry_Data_t; - -#define INQ_PERIPHINFO_BYTE 0 -#define INQ_Periph_Qualifier_MASK 0xe0 -#define INQ_Periph_Device_Type_MASK 0x1f - -#define INQ_Peripheral_Qualifier(inqp) \ - (int)((*((u8*)(inqp)+INQ_PERIPHINFO_BYTE) & INQ_Periph_Qualifier_MASK) >> 5) -#define INQ_Peripheral_Device_Type(inqp) \ - (int)(*((u8*)(inqp)+INQ_PERIPHINFO_BYTE) & INQ_Periph_Device_Type_MASK) - - -#define INQ_DEVTYPEMOD_BYTE 1 -#define INQ_RMB_BIT 0x80 -#define INQ_Device_Type_Modifier_MASK 0x7f - -#define INQ_Removable_Medium(inqp) \ - (int)(*((u8*)(inqp)+INQ_DEVTYPEMOD_BYTE) & INQ_RMB_BIT) -#define INQ_Device_Type_Modifier(inqp) \ - (int)(*((u8*)(inqp)+INQ_DEVTYPEMOD_BYTE) & INQ_Device_Type_Modifier_MASK) - - -#define INQ_VERSIONINFO_BYTE 2 -#define INQ_ISO_Version_MASK 0xc0 -#define INQ_ECMA_Version_MASK 0x38 -#define INQ_ANSI_Version_MASK 0x07 - -#define INQ_ISO_Version(inqp) \ - (int)(*((u8*)(inqp)+INQ_VERSIONINFO_BYTE) & INQ_ISO_Version_MASK) -#define INQ_ECMA_Version(inqp) \ - (int)(*((u8*)(inqp)+INQ_VERSIONINFO_BYTE) & INQ_ECMA_Version_MASK) -#define INQ_ANSI_Version(inqp) \ - (int)(*((u8*)(inqp)+INQ_VERSIONINFO_BYTE) & INQ_ANSI_Version_MASK) - - -#define INQ_BYTE3 3 -#define INQ_AERC_BIT 0x80 -#define INQ_TrmTsk_BIT 0x40 -#define INQ_NormACA_BIT 0x20 -#define INQ_RDF_MASK 0x0F - -#define INQ_AER_Capable(inqp) \ - (int)(*((u8*)(inqp)+INQ_BYTE3) & INQ_AERC_BIT) -#define INQ_TrmTsk(inqp) \ - (int)(*((u8*)(inqp)+INQ_BYTE3) & INQ_TrmTsk_BIT) -#define INQ_NormACA(inqp) \ - (int)(*((u8*)(inqp)+INQ_BYTE3) & INQ_NormACA_BIT) -#define INQ_Response_Data_Format(inqp) \ - (int)(*((u8*)(inqp)+INQ_BYTE3) & INQ_RDF_MASK) - - -#define INQ_CAPABILITY_BYTE 7 -#define INQ_RelAdr_BIT 0x80 -#define INQ_WBus32_BIT 0x40 -#define INQ_WBus16_BIT 0x20 -#define INQ_Sync_BIT 0x10 -#define INQ_Linked_BIT 0x08 - /* INQ_Reserved BIT 0x40 */ -#define INQ_CmdQue_BIT 0x02 -#define INQ_SftRe_BIT 0x01 - -#define IS_RelAdr_DEV(inqp) \ - (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_RelAdr_BIT) -#define IS_WBus32_DEV(inqp) \ - (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_WBus32_BIT) -#define IS_WBus16_DEV(inqp) \ - (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_WBus16_BIT) -#define IS_Sync_DEV(inqp) \ - (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_Sync_BIT) -#define IS_Linked_DEV(inqp) \ - (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_Linked_BIT) -#define IS_CmdQue_DEV(inqp) \ - (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_CmdQue_BIT) -#define IS_SftRe_DEV(inqp) \ - (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_SftRe_BIT) - -#define INQ_Width_BITS \ - (INQ_WBus32_BIT | INQ_WBus16_BIT) -#define IS_Wide_DEV(inqp) \ - (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_Width_BITS) - - -/* - * SCSI peripheral device types - */ -#define SCSI_TYPE_DAD 0x00 /* Direct Access Device */ -#define SCSI_TYPE_SAD 0x01 /* Sequential Access Device */ -#define SCSI_TYPE_TAPE SCSI_TYPE_SAD -#define SCSI_TYPE_PRT 0x02 /* Printer */ -#define SCSI_TYPE_PROC 0x03 /* Processor */ -#define SCSI_TYPE_WORM 0x04 -#define SCSI_TYPE_CDROM 0x05 -#define SCSI_TYPE_SCAN 0x06 /* Scanner */ -#define SCSI_TYPE_OPTICAL 0x07 /* Magneto/Optical */ -#define SCSI_TYPE_CHANGER 0x08 -#define SCSI_TYPE_COMM 0x09 /* Communications device */ -#define SCSI_TYPE_UNKNOWN 0x1f -#define SCSI_TYPE_UNCONFIGURED_LUN 0x7f - -#define SCSI_TYPE_MAX_KNOWN SCSI_TYPE_COMM - -/* - * Peripheral Qualifiers - */ -#define DEVICE_PRESENT 0x00 -#define LUN_NOT_PRESENT 0x01 -#define LUN_NOT_SUPPORTED 0x03 - -/* - * ANSI Versions - */ -#ifndef SCSI_1 -#define SCSI_1 0x01 -#endif -#ifndef SCSI_2 -#define SCSI_2 0x02 -#endif -#ifndef SCSI_3 -#define SCSI_3 0x03 -#endif - - -#define SCSI_MAX_SENSE_BYTES 255 -#define SCSI_STD_SENSE_BYTES 18 -#define SCSI_PAD_SENSE_BYTES (SCSI_MAX_SENSE_BYTES - SCSI_STD_SENSE_BYTES) - -#undef USE_SCSI_COMPLETE_SENSE -/* - * Structure definition for SCSI Sense Data - * - * NOTE: The following structure is 255 bytes in size - * iiff USE_SCSI_COMPLETE_SENSE IS defined above (i.e. w/ "#define"). - * If USE_SCSI_COMPLETE_SENSE is NOT defined above (i.e. w/ "#undef") - * then the following structure is only 19 bytes in size. - * THE CHOICE IS YOURS! - * - */ -typedef struct SCSI_Sense_Data -{ -#ifdef USE_SCSI_COMPLETE_SENSE - u8 SenseByte[SCSI_MAX_SENSE_BYTES]; -#else - u8 SenseByte[SCSI_STD_SENSE_BYTES]; -#endif - -/* - * the following structure works only for little-endian (Intel, - * LSB first (1234) byte order) systems with 4-byte ints. - * - u8 Error_Code :4, // 0x00 - Error_Class :3, - Valid :1 - ; - u8 Segment_Number // 0x01 - ; - u8 Sense_Key :4, // 0x02 - Reserved :1, - Incorrect_Length_Indicator:1, - End_Of_Media :1, - Filemark :1 - ; - u8 Information_MSB; // 0x03 - u8 Information_Byte2; // 0x04 - u8 Information_Byte1; // 0x05 - u8 Information_LSB; // 0x06 - u8 Additional_Length; // 0x07 - - u32 Command_Specific_Information; // 0x08 - 0x0b - - u8 Additional_Sense_Code; // 0x0c - u8 Additional_Sense_Code_Qualifier; // 0x0d - u8 Field_Replaceable_Unit_Code; // 0x0e - u8 Illegal_Req_Bit_Pointer :3, // 0x0f - Illegal_Req_Bit_Valid :1, - Illegal_Req_Reserved :2, - Illegal_Req_Cmd_Data :1, - Sense_Key_Specific_Valid :1 - ; - u16 Sense_Key_Specific_Data; // 0x10 - 0x11 - -#ifdef USE_SCSI_COMPLETE_SENSE - u8 Additional_Sense_Data[SCSI_PAD_SENSE_BYTES]; -#else - u8 Additional_Sense_Data[1]; -#endif - * - */ - -} SCSI_Sense_Data_t; - - -#define SD_ERRCODE_BYTE 0 -#define SD_Valid_BIT 0x80 -#define SD_Error_Code_MASK 0x7f -#define SD_Valid(sdp) \ - (int)(*((u8*)(sdp)+SD_ERRCODE_BYTE) & SD_Valid_BIT) -#define SD_Error_Code(sdp) \ - (int)(*((u8*)(sdp)+SD_ERRCODE_BYTE) & SD_Error_Code_MASK) - - -#define SD_SEGNUM_BYTE 1 -#define SD_Segment_Number(sdp) (int)(*((u8*)(sdp)+SD_SEGNUM_BYTE)) - - -#define SD_SENSEKEY_BYTE 2 -#define SD_Filemark_BIT 0x80 -#define SD_EOM_BIT 0x40 -#define SD_ILI_BIT 0x20 -#define SD_Sense_Key_MASK 0x0f -#define SD_Filemark(sdp) \ - (int)(*((u8*)(sdp)+SD_SENSEKEY_BYTE) & SD_Filemark_BIT) -#define SD_EOM(sdp) \ - (int)(*((u8*)(sdp)+SD_SENSEKEY_BYTE) & SD_EOM_BIT) -#define SD_ILI(sdp) \ - (int)(*((u8*)(sdp)+SD_SENSEKEY_BYTE) & SD_ILI_BIT) -#define SD_Sense_Key(sdp) \ - (int)(*((u8*)(sdp)+SD_SENSEKEY_BYTE) & SD_Sense_Key_MASK) - - -#define SD_INFO3_BYTE 3 -#define SD_INFO2_BYTE 4 -#define SD_INFO1_BYTE 5 -#define SD_INFO0_BYTE 6 -#define SD_Information3(sdp) (int)(*((u8*)(sdp)+SD_INFO3_BYTE)) -#define SD_Information2(sdp) (int)(*((u8*)(sdp)+SD_INFO2_BYTE)) -#define SD_Information1(sdp) (int)(*((u8*)(sdp)+SD_INFO1_BYTE)) -#define SD_Information0(sdp) (int)(*((u8*)(sdp)+SD_INFO0_BYTE)) - - -#define SD_ADDL_LEN_BYTE 7 -#define SD_Additional_Sense_Length(sdp) \ - (int)(*((u8*)(sdp)+SD_ADDL_LEN_BYTE)) -#define SD_Addl_Sense_Len SD_Additional_Sense_Length - - -#define SD_CMD_SPECIFIC3_BYTE 8 -#define SD_CMD_SPECIFIC2_BYTE 9 -#define SD_CMD_SPECIFIC1_BYTE 10 -#define SD_CMD_SPECIFIC0_BYTE 11 -#define SD_Cmd_Specific_Info3(sdp) (int)(*((u8*)(sdp)+SD_CMD_SPECIFIC3_BYTE)) -#define SD_Cmd_Specific_Info2(sdp) (int)(*((u8*)(sdp)+SD_CMD_SPECIFIC2_BYTE)) -#define SD_Cmd_Specific_Info1(sdp) (int)(*((u8*)(sdp)+SD_CMD_SPECIFIC1_BYTE)) -#define SD_Cmd_Specific_Info0(sdp) (int)(*((u8*)(sdp)+SD_CMD_SPECIFIC0_BYTE)) - - -#define SD_ADDL_SENSE_CODE_BYTE 12 -#define SD_Additional_Sense_Code(sdp) \ - (int)(*((u8*)(sdp)+SD_ADDL_SENSE_CODE_BYTE)) -#define SD_Addl_Sense_Code SD_Additional_Sense_Code -#define SD_ASC SD_Additional_Sense_Code - - -#define SD_ADDL_SENSE_CODE_QUAL_BYTE 13 -#define SD_Additional_Sense_Code_Qualifier(sdp) \ - (int)(*((u8*)(sdp)+SD_ADDL_SENSE_CODE_QUAL_BYTE)) -#define SD_Addl_Sense_Code_Qual SD_Additional_Sense_Code_Qualifier -#define SD_ASCQ SD_Additional_Sense_Code_Qualifier - - -#define SD_FIELD_REPL_UNIT_CODE_BYTE 14 -#define SD_Field_Replaceable_Unit_Code(sdp) \ - (int)(*((u8*)(sdp)+SD_FIELD_REPL_UNIT_CODE_BYTE)) -#define SD_Field_Repl_Unit_Code SD_Field_Replaceable_Unit_Code -#define SD_FRUC SD_Field_Replaceable_Unit_Code -#define SD_FRU SD_Field_Replaceable_Unit_Code - - -/* - * Sense-Key Specific offsets and macros. - */ -#define SD_SKS2_BYTE 15 -#define SD_SKS_Valid_BIT 0x80 -#define SD_SKS_Cmd_Data_BIT 0x40 -#define SD_SKS_Bit_Ptr_Valid_BIT 0x08 -#define SD_SKS_Bit_Ptr_MASK 0x07 -#define SD_SKS1_BYTE 16 -#define SD_SKS0_BYTE 17 -#define SD_Sense_Key_Specific_Valid(sdp) \ - (int)(*((u8*)(sdp)+SD_SKS2_BYTE) & SD_SKS_Valid_BIT) -#define SD_SKS_Valid SD_Sense_Key_Specific_Valid -#define SD_SKS_CDB_Error(sdp) \ - (int)(*((u8*)(sdp)+SD_SKS2_BYTE) & SD_SKS_Cmd_Data_BIT) -#define SD_Was_Illegal_Request SD_SKS_CDB_Error -#define SD_SKS_Bit_Pointer_Valid(sdp) \ - (int)(*((u8*)(sdp)+SD_SKS2_BYTE) & SD_SKS_Bit_Ptr_Valid_BIT) -#define SD_SKS_Bit_Pointer(sdp) \ - (int)(*((u8*)(sdp)+SD_SKS2_BYTE) & SD_SKS_Bit_Ptr_MASK) -#define SD_Field_Pointer(sdp) \ - (int)( ((u16)(*((u8*)(sdp)+SD_SKS1_BYTE)) << 8) \ - + *((u8*)(sdp)+SD_SKS0_BYTE) ) -#define SD_Bad_Byte SD_Field_Pointer -#define SD_Actual_Retry_Count SD_Field_Pointer -#define SD_Progress_Indication SD_Field_Pointer - -/* - * Mode Sense Write Protect Mask - */ -#define WRITE_PROTECT_MASK 0X80 - -/* - * Medium Type Codes - */ -#define OPTICAL_DEFAULT 0x00 -#define OPTICAL_READ_ONLY_MEDIUM 0x01 -#define OPTICAL_WRITE_ONCE_MEDIUM 0x02 -#define OPTICAL_READ_WRITABLE_MEDIUM 0x03 -#define OPTICAL_RO_OR_WO_MEDIUM 0x04 -#define OPTICAL_RO_OR_RW_MEDIUM 0x05 -#define OPTICAL_WO_OR_RW_MEDIUM 0x06 - - - -/* - * Structure definition for READ6, WRITE6 (6-byte CDB) - */ -typedef struct SCSI_RW6_CDB -{ - u32 OpCode :8, - LBA_HI :5, /* 5 MSBit's of the LBA */ - Lun :3, - LBA_MID :8, /* NOTE: total of 21 bits in LBA */ - LBA_LO :8 ; /* Max LBA = 0x001fffff */ - u8 BlockCount; - u8 Control; -} SCSI_RW6_t; - -#define MAX_RW6_LBA ((u32)0x001fffff) - -/* - * Structure definition for READ10, WRITE10 (10-byte CDB) - * - * NOTE: ParityCheck bit is applicable only for VERIFY and WRITE VERIFY for - * the ADP-92 DAC only. In the SCSI2 spec. this same bit is defined as a - * FUA (forced unit access) bit for READs and WRITEs. Since this driver - * does not use the FUA, this bit is defined as it is used by the ADP-92. - * Also, for READ CAPACITY, only the OpCode field is used. - */ -typedef struct SCSI_RW10_CDB -{ - u8 OpCode; - u8 Reserved1; - u32 LBA; - u8 Reserved2; - u16 BlockCount; - u8 Control; -} SCSI_RW10_t; - -#define PARITY_CHECK 0x08 /* parity check bit - byte[1], bit 3 */ - - /* - * Structure definition for data returned by READ CAPACITY cmd; - * READ CAPACITY data - */ - typedef struct READ_CAP_DATA - { - u32 MaxLBA; - u32 BlockBytes; - } SCSI_READ_CAP_DATA_t, *pSCSI_READ_CAP_DATA_t; - - -/* - * Structure definition for FORMAT UNIT CDB (6-byte CDB) - */ -typedef struct _SCSI_FORMAT_UNIT -{ - u8 OpCode; - u8 Reserved1; - u8 VendorSpecific; - u16 Interleave; - u8 Control; -} SCSI_FORMAT_UNIT_t; - -/* - * Structure definition for REQUEST SENSE (6-byte CDB) - */ -typedef struct _SCSI_REQUEST_SENSE -{ - u8 OpCode; - u8 Reserved1; - u8 Reserved2; - u8 Reserved3; - u8 AllocLength; - u8 Control; -} SCSI_REQ_SENSE_t; - -/* - * Structure definition for REPORT LUNS (12-byte CDB) - */ -typedef struct _SCSI_REPORT_LUNS -{ - u8 OpCode; - u8 Reserved1[5]; - u32 AllocationLength; - u8 Reserved2; - u8 Control; -} SCSI_REPORT_LUNS_t, *pSCSI_REPORT_LUNS_t; - - /* - * (per-level) LUN information bytes - */ -/* - * Following doesn't work on ARMCC compiler - * [apparently] because it pads every struct - * to be multiple of 4 bytes! - * So SCSI_LUN_LEVELS_t winds up being 16 - * bytes instead of 8! - * - typedef struct LUN_INFO - { - u8 AddrMethod_plus_LunOrBusNumber; - u8 LunOrTarget; - } SCSI_LUN_INFO_t, *pSCSI_LUN_INFO_t; - - typedef struct LUN_LEVELS - { - SCSI_LUN_INFO_t LUN_0; - SCSI_LUN_INFO_t LUN_1; - SCSI_LUN_INFO_t LUN_2; - SCSI_LUN_INFO_t LUN_3; - } SCSI_LUN_LEVELS_t, *pSCSI_LUN_LEVELS_t; -*/ - /* - * All 4 levels (8 bytes) of LUN information - */ - typedef struct LUN_LEVELS - { - u8 LVL1_AddrMethod_plus_LunOrBusNumber; - u8 LVL1_LunOrTarget; - u8 LVL2_AddrMethod_plus_LunOrBusNumber; - u8 LVL2_LunOrTarget; - u8 LVL3_AddrMethod_plus_LunOrBusNumber; - u8 LVL3_LunOrTarget; - u8 LVL4_AddrMethod_plus_LunOrBusNumber; - u8 LVL4_LunOrTarget; - } SCSI_LUN_LEVELS_t, *pSCSI_LUN_LEVELS_t; - - /* - * Structure definition for data returned by REPORT LUNS cmd; - * LUN reporting parameter list format - */ - typedef struct LUN_REPORT - { - u32 LunListLength; - u32 Reserved; - SCSI_LUN_LEVELS_t LunInfo[1]; - } SCSI_LUN_REPORT_t, *pSCSI_LUN_REPORT_t; - -/**************************************************************************** - * - * Externals - */ - -/**************************************************************************** - * - * Public Typedefs & Related Defines - */ - -/**************************************************************************** - * - * Macros (embedded, above) - */ - -/**************************************************************************** - * - * Public Variables - */ - -/**************************************************************************** - * - * Public Prototypes (module entry points) - */ - - -/***************************************************************************/ -#endif --- linux-2.6.9-rc1/drivers/message/fusion/scsiops.c 2004-08-24 00:01:52.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,309 +0,0 @@ - -static const char *ScsiOpcodeString[256] = { - "TEST UNIT READY\0\01", /* 00h */ - "REWIND\0\002" - "\001REZERO UNIT", /* 01h */ - "\0\0", /* 02h */ - "REQUEST SENSE\0\01", /* 03h */ - "FORMAT UNIT\0\03" - "\001FORMAT MEDIUM\0" - "\002FORMAT", /* 04h */ - "READ BLOCK LIMITS\0\1", /* 05h */ - "\0\0", /* 06h */ - "REASSIGN BLOCKS\0\02" - "\010INITIALIZE ELEMENT STATUS", /* 07h */ - "READ(06)\0\04" - "\001READ\0" - "\003RECEIVE\0" - "\011GET MESSAGE(06)", /* 08h */ - "\0\0", /* 09h */ - "WRITE(06)\0\05" - "\001WRITE\0" - "\002PRINT\0" - "\003SEND(6)\0" - "\011SEND MESSAGE(06)", /* 0Ah */ - "SEEK(06)\0\02" - "\003SLEW AND PRINT", /* 0Bh */ - "\0\0", /* 0Ch */ - "\0\0", /* 0Dh */ - "\0\0", /* 0Eh */ - "READ REVERSE\0\01", /* 0Fh */ - "WRITE FILEMARKS\0\02" - "\003SYNCRONIZE BUFFER", /* 10h */ - "SPACE(6)\0\01", /* 11h */ - "INQUIRY\0\01", /* 12h */ - "VERIFY\0\01", /* 13h */ - "RECOVER BUFFERED DATA\0\01", /* 14h */ - "MODE SELECT(06)\0\01", /* 15h */ - "RESERVE(06)\0\02" - "\010RESERVE ELEMENT(06)", /* 16h */ - "RELEASE(06)\0\02" - "\010RELEASE ELEMENT(06)", /* 17h */ - "COPY\0\01", /* 18h */ - "ERASE\0\01", /* 19h */ - "MODE SENSE(06)\0\01", /* 1Ah */ - "STOP START UNIT\0\04" - "\001LOAD UNLOAD\0" - "\002STOP PRINT\0" - "\006SCAN\0\002", /* 1Bh */ - "RECEIVE DIAGNOSTIC RESULTS\0\01", /* 1Ch */ - "SEND DIAGNOSTIC\0\01", /* 1Dh */ - "PREVENT ALLOW MEDIUM REMOVAL\0\01", /* 1Eh */ - "\0\0", /* 1Fh */ - "\0\0", /* 20h */ - "\0\0", /* 21h */ - "\0\0", /* 22h */ - "READ FORMAT CAPACITIES\0\01", /* 23h */ - "SET WINDOW\0\01", /* 24h */ - "READ CAPACITY\0\03" - "\006GET WINDOW\0" - "\037FREAD CARD CAPACITY", /* 25h */ - "\0\0", /* 26h */ - "\0\0", /* 27h */ - "READ(10)\0\02" - "\011GET MESSAGE(10)", /* 28h */ - "READ GENERATION\0\01", /* 29h */ - "WRITE(10)\0\03" - "\011SEND(10)\0" - "\011SEND MESSAGE(10)", /* 2Ah */ - "SEEK(10)\0\03" - "LOCATE(10)\0" - "POSITION TO ELEMENT", /* 2Bh */ - "ERASE(10)\0\01", /* 2Ch */ - "READ UPDATED BLOCK\0\01", /* 2Dh */ - "WRITE AND VERIFY(10)\0\01", /* 2Eh */ - "VERIFY(10)\0\01", /* 2Fh */ - "SEARCH DATA HIGH(10)\0\01", /* 30h */ - "SEARCH DATA EQUAL(10)\0\02" - "OBJECT POSITION", /* 31h */ - "SEARCH DATA LOW(10)\0\01", /* 32h */ - "SET LIMITS(10)\0\01", /* 33h */ - "PRE-FETCH(10)\0\03" - "READ POSITION\0" - "GET DATA BUFFER STATUS", /* 34h */ - "SYNCHRONIZE CACHE(10)\0\01", /* 35h */ - "LOCK UNLOCK CACHE(10)\0\01", /* 36h */ - "READ DEFECT DATA(10)\0\01", /* 37h */ - "MEDIUM SCAN\0\01", /* 38h */ - "COMPARE\0\01", /* 39h */ - "COPY AND VERIFY\0\01", /* 3Ah */ - "WRITE BUFFER\0\01", /* 3Bh */ - "READ BUFFER\0\01", /* 3Ch */ - "UPDATE BLOCK\0\01", /* 3Dh */ - "READ LONG\0\01", /* 3Eh */ - "WRITE LONG\0\01", /* 3Fh */ - "CHANGE DEFINITION\0\01", /* 40h */ - "WRITE SAME(10)\0\01", /* 41h */ - "READ SUB-CHANNEL\0\01", /* 42h */ - "READ TOC/PMA/ATIP\0\01", /* 43h */ - "REPORT DENSITY SUPPORT\0\01", /* 44h */ - "READ HEADER\0\01", /* 44h */ - "PLAY AUDIO(10)\0\01", /* 45h */ - "GET CONFIGURATION\0\01", /* 46h */ - "PLAY AUDIO MSF\0\01", /* 47h */ - "PLAY AUDIO TRACK INDEX\0\01", /* 48h */ - "PLAY TRACK RELATIVE(10)\0\01", /* 49h */ - "GET EVENT STATUS NOTIFICATION\0\01", /* 4Ah */ - "PAUSE/RESUME\0\01", /* 4Bh */ - "LOG SELECT\0\01", /* 4Ch */ - "LOG SENSE\0\01", /* 4Dh */ - "STOP PLAY/SCAN\0\01", /* 4Eh */ - "\0\0", /* 4Fh */ - "XDWRITE(10)\0\01", /* 50h */ - "XPWRITE(10)\0\02" - "READ DISC INFORMATION", /* 51h */ - "XDREAD(10)\0\01" - "READ TRACK INFORMATION", /* 52h */ - "RESERVE TRACK\0\01", /* 53h */ - "SEND OPC INFORMATION\0\01", /* 54h */ - "MODE SELECT(10)\0\01", /* 55h */ - "RESERVE(10)\0\02" - "RESERVE ELEMENT(10)", /* 56h */ - "RELEASE(10)\0\02" - "RELEASE ELEMENT(10)", /* 57h */ - "REPAIR TRACK\0\01", /* 58h */ - "READ MASTER CUE\0\01", /* 59h */ - "MODE SENSE(10)\0\01", /* 5Ah */ - "CLOSE TRACK/SESSION\0\01", /* 5Bh */ - "READ BUFFER CAPACITY\0\01", /* 5Ch */ - "SEND CUE SHEET\0\01", /* 5Dh */ - "PERSISTENT RESERVE IN\0\01", /* 5Eh */ - "PERSISTENT RESERVE OUT\0\01", /* 5Fh */ - "\0\0", /* 60h */ - "\0\0", /* 61h */ - "\0\0", /* 62h */ - "\0\0", /* 63h */ - "\0\0", /* 64h */ - "\0\0", /* 65h */ - "\0\0", /* 66h */ - "\0\0", /* 67h */ - "\0\0", /* 68h */ - "\0\0", /* 69h */ - "\0\0", /* 6Ah */ - "\0\0", /* 6Bh */ - "\0\0", /* 6Ch */ - "\0\0", /* 6Dh */ - "\0\0", /* 6Eh */ - "\0\0", /* 6Fh */ - "\0\0", /* 70h */ - "\0\0", /* 71h */ - "\0\0", /* 72h */ - "\0\0", /* 73h */ - "\0\0", /* 74h */ - "\0\0", /* 75h */ - "\0\0", /* 76h */ - "\0\0", /* 77h */ - "\0\0", /* 78h */ - "\0\0", /* 79h */ - "\0\0", /* 7Ah */ - "\0\0", /* 7Bh */ - "\0\0", /* 7Ch */ - "\0\0", /* 7Eh */ - "\0\0", /* 7Eh */ - "\0\0", /* 7Fh */ - "XDWRITE EXTENDED(16)\0\01", /* 80h */ - "REBUILD(16)\0\01", /* 81h */ - "REGENERATE(16)\0\01", /* 82h */ - "EXTENDED COPY\0\01", /* 83h */ - "RECEIVE COPY RESULTS\0\01", /* 84h */ - "ACCESS CONTROL IN [proposed]\0\01", /* 86h */ - "ACCESS CONTROL OUT [proposed]\0\01", /* 87h */ - "READ(16)\0\01", /* 88h */ - "DEVICE LOCKS [proposed]\0\01", /* 89h */ - "WRITE(16)\0\01", /* 8Ah */ - "\0\0", /* 8Bh */ - "READ ATTRIBUTES [proposed]\0\01", /* 8Ch */ - "WRITE ATTRIBUTES [proposed]\0\01", /* 8Dh */ - "WRITE AND VERIFY(16)\0\01", /* 8Eh */ - "VERIFY(16)\0\01", /* 8Fh */ - "PRE-FETCH(16)\0\01", /* 90h */ - "SYNCHRONIZE CACHE(16)\0\02" - "SPACE(16) [1]", /* 91h */ - "LOCK UNLOCK CACHE(16)\0\02" - "LOCATE(16) [1]", /* 92h */ - "WRITE SAME(16)\0\01", /* 93h */ - "[usage proposed by SCSI Socket Services project]\0\01", /* 94h */ - "[usage proposed by SCSI Socket Services project]\0\01", /* 95h */ - "[usage proposed by SCSI Socket Services project]\0\01", /* 96h */ - "[usage proposed by SCSI Socket Services project]\0\01", /* 97h */ - "MARGIN CONTROL [proposed]\0\01", /* 98h */ - "\0\0", /* 99h */ - "\0\0", /* 9Ah */ - "\0\0", /* 9Bh */ - "\0\0", /* 9Ch */ - "\0\0", /* 9Dh */ - "SERVICE ACTION IN [proposed]\0\01", /* 9Eh */ - "SERVICE ACTION OUT [proposed]\0\01", /* 9Fh */ - "REPORT LUNS\0\01", /* A0h */ - "BLANK\0\01", /* A1h */ - "SEND EVENT\0\01", /* A2h */ - "MAINTENANCE (IN)\0\02" - "SEND KEY", /* A3h */ - "MAINTENANCE (OUT)\0\02" - "REPORT KEY", /* A4h */ - "MOVE MEDIUM\0\02" - "PLAY AUDIO(12)", /* A5h */ - "EXCHANGE MEDIUM\0\02" - "LOAD/UNLOAD C/DVD", /* A6h */ - "MOVE MEDIUM ATTACHED\0\02" - "SET READ AHEAD\0\01", /* A7h */ - "READ(12)\0\02" - "GET MESSAGE(12)", /* A8h */ - "PLAY TRACK RELATIVE(12)\0\01", /* A9h */ - "WRITE(12)\0\02" - "SEND MESSAGE(12)", /* AAh */ - "\0\0", /* ABh */ - "ERASE(12)\0\02" - "GET PERFORMANCE", /* ACh */ - "READ DVD STRUCTURE\0\01", /* ADh */ - "WRITE AND VERIFY(12)\0\01", /* AEh */ - "VERIFY(12)\0\01", /* AFh */ - "SEARCH DATA HIGH(12)\0\01", /* B0h */ - "SEARCH DATA EQUAL(12)\0\01", /* B1h */ - "SEARCH DATA LOW(12)\0\01", /* B2h */ - "SET LIMITS(12)\0\01", /* B3h */ - "READ ELEMENT STATUS ATTACHED\0\01", /* B4h */ - "REQUEST VOLUME ELEMENT ADDRESS\0\01", /* B5h */ - "SEND VOLUME TAG\0\02" - "SET STREAMING", /* B6h */ - "READ DEFECT DATA(12)\0\01", /* B7h */ - "READ ELEMENT STATUS\0\01", /* B8h */ - "READ CD MSF\0\01", /* B9h */ - "REDUNDANCY GROUP (IN)\0\02" - "SCAN", /* BAh */ - "REDUNDANCY GROUP (OUT)\0\02" - "SET CD-ROM SPEED", /* BBh */ - "SPARE (IN)\0\02" - "PLAY CD", /* BCh */ - "SPARE (OUT)\0\02" - "MECHANISM STATUS", /* BDh */ - "VOLUME SET (IN)\0\02" - "READ CD", /* BEh */ - "VOLUME SET (OUT)\0\0\02" - "SEND DVD STRUCTURE", /* BFh */ - "\0\0", /* C0h */ - "\0\0", /* C1h */ - "\0\0", /* C2h */ - "\0\0", /* C3h */ - "\0\0", /* C4h */ - "\0\0", /* C5h */ - "\0\0", /* C6h */ - "\0\0", /* C7h */ - "\0\0", /* C8h */ - "\0\0", /* C9h */ - "\0\0", /* CAh */ - "\0\0", /* CBh */ - "\0\0", /* CCh */ - "\0\0", /* CDh */ - "\0\0", /* CEh */ - "\0\0", /* CFh */ - "\0\0", /* D0h */ - "\0\0", /* D1h */ - "\0\0", /* D2h */ - "\0\0", /* D3h */ - "\0\0", /* D4h */ - "\0\0", /* D5h */ - "\0\0", /* D6h */ - "\0\0", /* D7h */ - "\0\0", /* D8h */ - "\0\0", /* D9h */ - "\0\0", /* DAh */ - "\0\0", /* DBh */ - "\0\0", /* DCh */ - "\0\0", /* DEh */ - "\0\0", /* DEh */ - "\0\0", /* DFh */ - "\0\0", /* E0h */ - "\0\0", /* E1h */ - "\0\0", /* E2h */ - "\0\0", /* E3h */ - "\0\0", /* E4h */ - "\0\0", /* E5h */ - "\0\0", /* E6h */ - "\0\0", /* E7h */ - "\0\0", /* E8h */ - "\0\0", /* E9h */ - "\0\0", /* EAh */ - "\0\0", /* EBh */ - "\0\0", /* ECh */ - "\0\0", /* EDh */ - "\0\0", /* EEh */ - "\0\0", /* EFh */ - "\0\0", /* F0h */ - "\0\0", /* F1h */ - "\0\0", /* F2h */ - "\0\0", /* F3h */ - "\0\0", /* F4h */ - "\0\0", /* F5h */ - "\0\0", /* F6h */ - "\0\0", /* F7h */ - "\0\0", /* F8h */ - "\0\0", /* F9h */ - "\0\0", /* FAh */ - "\0\0", /* FBh */ - "\0\0", /* FEh */ - "\0\0", /* FEh */ - "\0\0", /* FEh */ - "\0\0" /* FFh */ -}; - --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/message/i2o/debug.c 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,571 @@ +#include +#include +#include +#include +#include + +static int verbose; +extern struct i2o_driver **i2o_drivers; +extern unsigned int i2o_max_drivers; +static void i2o_report_util_cmd(u8 cmd); +static void i2o_report_exec_cmd(u8 cmd); +void i2o_report_fail_status(u8 req_status, u32 * msg); +void i2o_report_common_status(u8 req_status); +static void i2o_report_common_dsc(u16 detailed_status); + +void i2o_dump_status_block(i2o_status_block * sb) +{ + pr_debug("Organization ID: %d\n", sb->org_id); + pr_debug("IOP ID: %d\n", sb->iop_id); + pr_debug("Host Unit ID: %d\n", sb->host_unit_id); + pr_debug("Segment Number: %d\n", sb->segment_number); + pr_debug("I2O Version: %d\n", sb->i2o_version); + pr_debug("IOP State: %d\n", sb->iop_state); + pr_debug("Messanger Type: %d\n", sb->msg_type); + pr_debug("Inbound Frame Size: %d\n", sb->inbound_frame_size); + pr_debug("Init Code: %d\n", sb->init_code); + pr_debug("Max Inbound MFrames: %d\n", sb->max_inbound_frames); + pr_debug("Current Inbound MFrames: %d\n", sb->cur_inbound_frames); + pr_debug("Max Outbound MFrames: %d\n", sb->max_outbound_frames); + pr_debug("Product ID String: %s\n", sb->product_id); + pr_debug("Expected LCT Size: %d\n", sb->expected_lct_size); + pr_debug("IOP Capabilities: %d\n", sb->iop_capabilities); + pr_debug("Desired Private MemSize: %d\n", sb->desired_mem_size); + pr_debug("Current Private MemSize: %d\n", sb->current_mem_size); + pr_debug("Current Private MemBase: %d\n", sb->current_mem_base); + pr_debug("Desired Private IO Size: %d\n", sb->desired_io_size); + pr_debug("Current Private IO Size: %d\n", sb->current_io_size); + pr_debug("Current Private IO Base: %d\n", sb->current_io_base); +}; + +/* + * Used for error reporting/debugging purposes. + * Report Cmd name, Request status, Detailed Status. + */ +void i2o_report_status(const char *severity, const char *str, + struct i2o_message *m) +{ + u32 *msg = (u32 *) m; + u8 cmd = (msg[1] >> 24) & 0xFF; + u8 req_status = (msg[4] >> 24) & 0xFF; + u16 detailed_status = msg[4] & 0xFFFF; + //struct i2o_driver *h = i2o_drivers[msg[2] & (i2o_max_drivers-1)]; + + if (cmd == I2O_CMD_UTIL_EVT_REGISTER) + return; // No status in this reply + + printk("%s%s: ", severity, str); + + if (cmd < 0x1F) // Utility cmd + i2o_report_util_cmd(cmd); + + else if (cmd >= 0xA0 && cmd <= 0xEF) // Executive cmd + i2o_report_exec_cmd(cmd); + else + printk("Cmd = %0#2x, ", cmd); // Other cmds + + if (msg[0] & MSG_FAIL) { + i2o_report_fail_status(req_status, msg); + return; + } + + i2o_report_common_status(req_status); + + if (cmd < 0x1F || (cmd >= 0xA0 && cmd <= 0xEF)) + i2o_report_common_dsc(detailed_status); + else + printk(" / DetailedStatus = %0#4x.\n", detailed_status); +} + +/* Used to dump a message to syslog during debugging */ +void i2o_dump_message(struct i2o_message *m) +{ +#ifdef DEBUG + u32 *msg = (u32 *) m; + int i; + printk(KERN_INFO "Dumping I2O message size %d @ %p\n", + msg[0] >> 16 & 0xffff, msg); + for (i = 0; i < ((msg[0] >> 16) & 0xffff); i++) + printk(KERN_INFO " msg[%d] = %0#10x\n", i, msg[i]); +#endif +} + +/** + * i2o_report_controller_unit - print information about a tid + * @c: controller + * @d: device + * + * Dump an information block associated with a given unit (TID). The + * tables are read and a block of text is output to printk that is + * formatted intended for the user. + */ + +void i2o_report_controller_unit(struct i2o_controller *c, struct i2o_device *d) +{ + char buf[64]; + char str[22]; + int ret; + + if (verbose == 0) + return; + + printk(KERN_INFO "Target ID %03x.\n", d->lct_data.tid); + if ((ret = i2o_parm_field_get(d, 0xF100, 3, buf, 16)) >= 0) { + buf[16] = 0; + printk(KERN_INFO " Vendor: %s\n", buf); + } + if ((ret = i2o_parm_field_get(d, 0xF100, 4, buf, 16)) >= 0) { + buf[16] = 0; + printk(KERN_INFO " Device: %s\n", buf); + } + if (i2o_parm_field_get(d, 0xF100, 5, buf, 16) >= 0) { + buf[16] = 0; + printk(KERN_INFO " Description: %s\n", buf); + } + if ((ret = i2o_parm_field_get(d, 0xF100, 6, buf, 8)) >= 0) { + buf[8] = 0; + printk(KERN_INFO " Rev: %s\n", buf); + } + + printk(KERN_INFO " Class: "); + //sprintf(str, "%-21s", i2o_get_class_name(d->lct_data.class_id)); + printk("%s\n", str); + + printk(KERN_INFO " Subclass: 0x%04X\n", d->lct_data.sub_class); + printk(KERN_INFO " Flags: "); + + if (d->lct_data.device_flags & (1 << 0)) + printk("C"); // ConfigDialog requested + if (d->lct_data.device_flags & (1 << 1)) + printk("U"); // Multi-user capable + if (!(d->lct_data.device_flags & (1 << 4))) + printk("P"); // Peer service enabled! + if (!(d->lct_data.device_flags & (1 << 5))) + printk("M"); // Mgmt service enabled! + printk("\n"); +} + +/* +MODULE_PARM(verbose, "i"); +MODULE_PARM_DESC(verbose, "Verbose diagnostics"); +*/ +/* + * Used for error reporting/debugging purposes. + * Following fail status are common to all classes. + * The preserved message must be handled in the reply handler. + */ +void i2o_report_fail_status(u8 req_status, u32 * msg) +{ + static char *FAIL_STATUS[] = { + "0x80", /* not used */ + "SERVICE_SUSPENDED", /* 0x81 */ + "SERVICE_TERMINATED", /* 0x82 */ + "CONGESTION", + "FAILURE", + "STATE_ERROR", + "TIME_OUT", + "ROUTING_FAILURE", + "INVALID_VERSION", + "INVALID_OFFSET", + "INVALID_MSG_FLAGS", + "FRAME_TOO_SMALL", + "FRAME_TOO_LARGE", + "INVALID_TARGET_ID", + "INVALID_INITIATOR_ID", + "INVALID_INITIATOR_CONTEX", /* 0x8F */ + "UNKNOWN_FAILURE" /* 0xFF */ + }; + + if (req_status == I2O_FSC_TRANSPORT_UNKNOWN_FAILURE) + printk("TRANSPORT_UNKNOWN_FAILURE (%0#2x)\n.", req_status); + else + printk("TRANSPORT_%s.\n", FAIL_STATUS[req_status & 0x0F]); + + /* Dump some details */ + + printk(KERN_ERR " InitiatorId = %d, TargetId = %d\n", + (msg[1] >> 12) & 0xFFF, msg[1] & 0xFFF); + printk(KERN_ERR " LowestVersion = 0x%02X, HighestVersion = 0x%02X\n", + (msg[4] >> 8) & 0xFF, msg[4] & 0xFF); + printk(KERN_ERR " FailingHostUnit = 0x%04X, FailingIOP = 0x%03X\n", + msg[5] >> 16, msg[5] & 0xFFF); + + printk(KERN_ERR " Severity: 0x%02X ", (msg[4] >> 16) & 0xFF); + if (msg[4] & (1 << 16)) + printk("(FormatError), " + "this msg can never be delivered/processed.\n"); + if (msg[4] & (1 << 17)) + printk("(PathError), " + "this msg can no longer be delivered/processed.\n"); + if (msg[4] & (1 << 18)) + printk("(PathState), " + "the system state does not allow delivery.\n"); + if (msg[4] & (1 << 19)) + printk("(Congestion), resources temporarily not available;" + "do not retry immediately.\n"); +} + +/* + * Used for error reporting/debugging purposes. + * Following reply status are common to all classes. + */ +void i2o_report_common_status(u8 req_status) +{ + static char *REPLY_STATUS[] = { + "SUCCESS", + "ABORT_DIRTY", + "ABORT_NO_DATA_TRANSFER", + "ABORT_PARTIAL_TRANSFER", + "ERROR_DIRTY", + "ERROR_NO_DATA_TRANSFER", + "ERROR_PARTIAL_TRANSFER", + "PROCESS_ABORT_DIRTY", + "PROCESS_ABORT_NO_DATA_TRANSFER", + "PROCESS_ABORT_PARTIAL_TRANSFER", + "TRANSACTION_ERROR", + "PROGRESS_REPORT" + }; + + if (req_status >= ARRAY_SIZE(REPLY_STATUS)) + printk("RequestStatus = %0#2x", req_status); + else + printk("%s", REPLY_STATUS[req_status]); +} + +/* + * Used for error reporting/debugging purposes. + * Following detailed status are valid for executive class, + * utility class, DDM class and for transaction error replies. + */ +static void i2o_report_common_dsc(u16 detailed_status) +{ + static char *COMMON_DSC[] = { + "SUCCESS", + "0x01", // not used + "BAD_KEY", + "TCL_ERROR", + "REPLY_BUFFER_FULL", + "NO_SUCH_PAGE", + "INSUFFICIENT_RESOURCE_SOFT", + "INSUFFICIENT_RESOURCE_HARD", + "0x08", // not used + "CHAIN_BUFFER_TOO_LARGE", + "UNSUPPORTED_FUNCTION", + "DEVICE_LOCKED", + "DEVICE_RESET", + "INAPPROPRIATE_FUNCTION", + "INVALID_INITIATOR_ADDRESS", + "INVALID_MESSAGE_FLAGS", + "INVALID_OFFSET", + "INVALID_PARAMETER", + "INVALID_REQUEST", + "INVALID_TARGET_ADDRESS", + "MESSAGE_TOO_LARGE", + "MESSAGE_TOO_SMALL", + "MISSING_PARAMETER", + "TIMEOUT", + "UNKNOWN_ERROR", + "UNKNOWN_FUNCTION", + "UNSUPPORTED_VERSION", + "DEVICE_BUSY", + "DEVICE_NOT_AVAILABLE" + }; + + if (detailed_status > I2O_DSC_DEVICE_NOT_AVAILABLE) + printk(" / DetailedStatus = %0#4x.\n", detailed_status); + else + printk(" / %s.\n", COMMON_DSC[detailed_status]); +} + +/* + * Used for error reporting/debugging purposes + */ +static void i2o_report_util_cmd(u8 cmd) +{ + switch (cmd) { + case I2O_CMD_UTIL_NOP: + printk("UTIL_NOP, "); + break; + case I2O_CMD_UTIL_ABORT: + printk("UTIL_ABORT, "); + break; + case I2O_CMD_UTIL_CLAIM: + printk("UTIL_CLAIM, "); + break; + case I2O_CMD_UTIL_RELEASE: + printk("UTIL_CLAIM_RELEASE, "); + break; + case I2O_CMD_UTIL_CONFIG_DIALOG: + printk("UTIL_CONFIG_DIALOG, "); + break; + case I2O_CMD_UTIL_DEVICE_RESERVE: + printk("UTIL_DEVICE_RESERVE, "); + break; + case I2O_CMD_UTIL_DEVICE_RELEASE: + printk("UTIL_DEVICE_RELEASE, "); + break; + case I2O_CMD_UTIL_EVT_ACK: + printk("UTIL_EVENT_ACKNOWLEDGE, "); + break; + case I2O_CMD_UTIL_EVT_REGISTER: + printk("UTIL_EVENT_REGISTER, "); + break; + case I2O_CMD_UTIL_LOCK: + printk("UTIL_LOCK, "); + break; + case I2O_CMD_UTIL_LOCK_RELEASE: + printk("UTIL_LOCK_RELEASE, "); + break; + case I2O_CMD_UTIL_PARAMS_GET: + printk("UTIL_PARAMS_GET, "); + break; + case I2O_CMD_UTIL_PARAMS_SET: + printk("UTIL_PARAMS_SET, "); + break; + case I2O_CMD_UTIL_REPLY_FAULT_NOTIFY: + printk("UTIL_REPLY_FAULT_NOTIFY, "); + break; + default: + printk("Cmd = %0#2x, ", cmd); + } +} + +/* + * Used for error reporting/debugging purposes + */ +static void i2o_report_exec_cmd(u8 cmd) +{ + switch (cmd) { + case I2O_CMD_ADAPTER_ASSIGN: + printk("EXEC_ADAPTER_ASSIGN, "); + break; + case I2O_CMD_ADAPTER_READ: + printk("EXEC_ADAPTER_READ, "); + break; + case I2O_CMD_ADAPTER_RELEASE: + printk("EXEC_ADAPTER_RELEASE, "); + break; + case I2O_CMD_BIOS_INFO_SET: + printk("EXEC_BIOS_INFO_SET, "); + break; + case I2O_CMD_BOOT_DEVICE_SET: + printk("EXEC_BOOT_DEVICE_SET, "); + break; + case I2O_CMD_CONFIG_VALIDATE: + printk("EXEC_CONFIG_VALIDATE, "); + break; + case I2O_CMD_CONN_SETUP: + printk("EXEC_CONN_SETUP, "); + break; + case I2O_CMD_DDM_DESTROY: + printk("EXEC_DDM_DESTROY, "); + break; + case I2O_CMD_DDM_ENABLE: + printk("EXEC_DDM_ENABLE, "); + break; + case I2O_CMD_DDM_QUIESCE: + printk("EXEC_DDM_QUIESCE, "); + break; + case I2O_CMD_DDM_RESET: + printk("EXEC_DDM_RESET, "); + break; + case I2O_CMD_DDM_SUSPEND: + printk("EXEC_DDM_SUSPEND, "); + break; + case I2O_CMD_DEVICE_ASSIGN: + printk("EXEC_DEVICE_ASSIGN, "); + break; + case I2O_CMD_DEVICE_RELEASE: + printk("EXEC_DEVICE_RELEASE, "); + break; + case I2O_CMD_HRT_GET: + printk("EXEC_HRT_GET, "); + break; + case I2O_CMD_ADAPTER_CLEAR: + printk("EXEC_IOP_CLEAR, "); + break; + case I2O_CMD_ADAPTER_CONNECT: + printk("EXEC_IOP_CONNECT, "); + break; + case I2O_CMD_ADAPTER_RESET: + printk("EXEC_IOP_RESET, "); + break; + case I2O_CMD_LCT_NOTIFY: + printk("EXEC_LCT_NOTIFY, "); + break; + case I2O_CMD_OUTBOUND_INIT: + printk("EXEC_OUTBOUND_INIT, "); + break; + case I2O_CMD_PATH_ENABLE: + printk("EXEC_PATH_ENABLE, "); + break; + case I2O_CMD_PATH_QUIESCE: + printk("EXEC_PATH_QUIESCE, "); + break; + case I2O_CMD_PATH_RESET: + printk("EXEC_PATH_RESET, "); + break; + case I2O_CMD_STATIC_MF_CREATE: + printk("EXEC_STATIC_MF_CREATE, "); + break; + case I2O_CMD_STATIC_MF_RELEASE: + printk("EXEC_STATIC_MF_RELEASE, "); + break; + case I2O_CMD_STATUS_GET: + printk("EXEC_STATUS_GET, "); + break; + case I2O_CMD_SW_DOWNLOAD: + printk("EXEC_SW_DOWNLOAD, "); + break; + case I2O_CMD_SW_UPLOAD: + printk("EXEC_SW_UPLOAD, "); + break; + case I2O_CMD_SW_REMOVE: + printk("EXEC_SW_REMOVE, "); + break; + case I2O_CMD_SYS_ENABLE: + printk("EXEC_SYS_ENABLE, "); + break; + case I2O_CMD_SYS_MODIFY: + printk("EXEC_SYS_MODIFY, "); + break; + case I2O_CMD_SYS_QUIESCE: + printk("EXEC_SYS_QUIESCE, "); + break; + case I2O_CMD_SYS_TAB_SET: + printk("EXEC_SYS_TAB_SET, "); + break; + default: + printk("Cmd = %#02x, ", cmd); + } +} + +void i2o_debug_state(struct i2o_controller *c) +{ + printk(KERN_INFO "%s: State = ", c->name); + switch (((i2o_status_block *) c->status_block.virt)->iop_state) { + case 0x01: + printk("INIT\n"); + break; + case 0x02: + printk("RESET\n"); + break; + case 0x04: + printk("HOLD\n"); + break; + case 0x05: + printk("READY\n"); + break; + case 0x08: + printk("OPERATIONAL\n"); + break; + case 0x10: + printk("FAILED\n"); + break; + case 0x11: + printk("FAULTED\n"); + break; + default: + printk("%x (unknown !!)\n", + ((i2o_status_block *) c->status_block.virt)->iop_state); + } +}; + +void i2o_systab_debug(struct i2o_sys_tbl *sys_tbl) +{ + u32 *table; + int count; + u32 size; + + table = (u32 *) sys_tbl; + size = sizeof(struct i2o_sys_tbl) + sys_tbl->num_entries + * sizeof(struct i2o_sys_tbl_entry); + + for (count = 0; count < (size >> 2); count++) + printk(KERN_INFO "sys_tbl[%d] = %0#10x\n", count, table[count]); +} + +void i2o_dump_hrt(struct i2o_controller *c) +{ + u32 *rows = (u32 *) c->hrt.virt; + u8 *p = (u8 *) c->hrt.virt; + u8 *d; + int count; + int length; + int i; + int state; + + if (p[3] != 0) { + printk(KERN_ERR + "%s: HRT table for controller is too new a version.\n", + c->name); + return; + } + + count = p[0] | (p[1] << 8); + length = p[2]; + + printk(KERN_INFO "%s: HRT has %d entries of %d bytes each.\n", + c->name, count, length << 2); + + rows += 2; + + for (i = 0; i < count; i++) { + printk(KERN_INFO "Adapter %08X: ", rows[0]); + p = (u8 *) (rows + 1); + d = (u8 *) (rows + 2); + state = p[1] << 8 | p[0]; + + printk("TID %04X:[", state & 0xFFF); + state >>= 12; + if (state & (1 << 0)) + printk("H"); /* Hidden */ + if (state & (1 << 2)) { + printk("P"); /* Present */ + if (state & (1 << 1)) + printk("C"); /* Controlled */ + } + if (state > 9) + printk("*"); /* Hard */ + + printk("]:"); + + switch (p[3] & 0xFFFF) { + case 0: + /* Adapter private bus - easy */ + printk("Local bus %d: I/O at 0x%04X Mem 0x%08X", + p[2], d[1] << 8 | d[0], *(u32 *) (d + 4)); + break; + case 1: + /* ISA bus */ + printk("ISA %d: CSN %d I/O at 0x%04X Mem 0x%08X", + p[2], d[2], d[1] << 8 | d[0], *(u32 *) (d + 4)); + break; + + case 2: /* EISA bus */ + printk("EISA %d: Slot %d I/O at 0x%04X Mem 0x%08X", + p[2], d[3], d[1] << 8 | d[0], *(u32 *) (d + 4)); + break; + + case 3: /* MCA bus */ + printk("MCA %d: Slot %d I/O at 0x%04X Mem 0x%08X", + p[2], d[3], d[1] << 8 | d[0], *(u32 *) (d + 4)); + break; + + case 4: /* PCI bus */ + printk("PCI %d: Bus %d Device %d Function %d", + p[2], d[2], d[1], d[0]); + break; + + case 0x80: /* Other */ + default: + printk("Unsupported bus type."); + break; + } + printk("\n"); + rows += length; + } +} + +EXPORT_SYMBOL(i2o_dump_status_block); +EXPORT_SYMBOL(i2o_dump_message); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/message/i2o/device.c 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,674 @@ +/* + * Functions to handle I2O devices + * + * Copyright (C) 2004 Markus Lidel + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Fixes/additions: + * Markus Lidel + * initial version. + */ + +#include +#include + +/* Exec OSM functions */ +extern struct bus_type i2o_bus_type; + +/** + * i2o_device_issue_claim - claim or release a device + * @dev: I2O device to claim or release + * @cmd: claim or release command + * @type: type of claim + * + * Issue I2O UTIL_CLAIM or UTIL_RELEASE messages. The message to be sent + * is set by cmd. dev is the I2O device which should be claim or + * released and the type is the claim type (see the I2O spec). + * + * Returs 0 on success or negative error code on failure. + */ +static inline int i2o_device_issue_claim(struct i2o_device *dev, u32 cmd, + u32 type) +{ + struct i2o_message *msg; + u32 m; + + m = i2o_msg_get_wait(dev->iop, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(cmd << 24 | HOST_TID << 12 | dev->lct_data.tid, &msg->u.head[1]); + writel(type, &msg->body[0]); + + return i2o_msg_post_wait(dev->iop, m, 60); +}; + +/** + * i2o_device_claim - claim a device for use by an OSM + * @dev: I2O device to claim + * @drv: I2O driver which wants to claim the device + * + * Do the leg work to assign a device to a given OSM. If the claim succeed + * the owner of the rimary. If the attempt fails a negative errno code + * is returned. On success zero is returned. + */ +int i2o_device_claim(struct i2o_device *dev) +{ + int rc = 0; + + down(&dev->lock); + + rc = i2o_device_issue_claim(dev, I2O_CMD_UTIL_CLAIM, I2O_CLAIM_PRIMARY); + if (!rc) + pr_debug("claim of device %d succeded\n", dev->lct_data.tid); + else + pr_debug("claim of device %d failed %d\n", dev->lct_data.tid, + rc); + + up(&dev->lock); + + return rc; +}; + +/** + * i2o_device_claim_release - release a device that the OSM is using + * @dev: device to release + * @drv: driver which claimed the device + * + * Drop a claim by an OSM on a given I2O device. + * + * AC - some devices seem to want to refuse an unclaim until they have + * finished internal processing. It makes sense since you don't want a + * new device to go reconfiguring the entire system until you are done. + * Thus we are prepared to wait briefly. + * + * Returns 0 on success or negative error code on failure. + */ +int i2o_device_claim_release(struct i2o_device *dev) +{ + int tries; + int rc = 0; + + down(&dev->lock); + + /* + * If the controller takes a nonblocking approach to + * releases we have to sleep/poll for a few times. + */ + for (tries = 0; tries < 10; tries++) { + rc = i2o_device_issue_claim(dev, I2O_CMD_UTIL_RELEASE, + I2O_CLAIM_PRIMARY); + if (!rc) + break; + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ); + } + + if (!rc) + pr_debug("claim release of device %d succeded\n", + dev->lct_data.tid); + else + pr_debug("claim release of device %d failed %d\n", + dev->lct_data.tid, rc); + + up(&dev->lock); + + return rc; +}; + +/** + * i2o_device_release - release the memory for a I2O device + * @dev: I2O device which should be released + * + * Release the allocated memory. This function is called if refcount of + * device reaches 0 automatically. + */ +static void i2o_device_release(struct device *dev) +{ + struct i2o_device *i2o_dev = to_i2o_device(dev); + + pr_debug("Release I2O device %s\n", dev->bus_id); + + kfree(i2o_dev); +}; + +/** + * i2o_device_class_release - Remove I2O device attributes + * @cd: I2O class device which is added to the I2O device class + * + * Removes attributes from the I2O device again. Also search each device + * on the controller for I2O devices which refert to this device as parent + * or user and remove this links also. + */ +static void i2o_device_class_release(struct class_device *cd) +{ + struct i2o_device *i2o_dev, *tmp; + struct i2o_controller *c; + + i2o_dev = to_i2o_device(cd->dev); + c = i2o_dev->iop; + + sysfs_remove_link(&i2o_dev->device.kobj, "parent"); + sysfs_remove_link(&i2o_dev->device.kobj, "user"); + + list_for_each_entry(tmp, &c->devices, list) { + if (tmp->lct_data.parent_tid == i2o_dev->lct_data.tid) + sysfs_remove_link(&tmp->device.kobj, "parent"); + if (tmp->lct_data.user_tid == i2o_dev->lct_data.tid) + sysfs_remove_link(&tmp->device.kobj, "user"); + } +}; + +/* I2O device class */ +static struct class i2o_device_class = { + .name = "i2o_device", + .release = i2o_device_class_release +}; + +/** + * i2o_device_alloc - Allocate a I2O device and initialize it + * + * Allocate the memory for a I2O device and initialize locks and lists + * + * Returns the allocated I2O device or a negative error code if the device + * could not be allocated. + */ +static struct i2o_device *i2o_device_alloc(void) +{ + struct i2o_device *dev; + + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return ERR_PTR(-ENOMEM); + + memset(dev, 0, sizeof(*dev)); + + INIT_LIST_HEAD(&dev->list); + init_MUTEX(&dev->lock); + + dev->device.bus = &i2o_bus_type; + dev->device.release = &i2o_device_release; + dev->classdev.class = &i2o_device_class; + dev->classdev.dev = &dev->device; + + return dev; +}; + +/** + * i2o_device_add - allocate a new I2O device and add it to the IOP + * @iop: I2O controller where the device is on + * @entry: LCT entry of the I2O device + * + * Allocate a new I2O device and initialize it with the LCT entry. The + * device is appended to the device list of the controller. + * + * Returns a pointer to the I2O device on success or negative error code + * on failure. + */ +struct i2o_device *i2o_device_add(struct i2o_controller *c, + i2o_lct_entry * entry) +{ + struct i2o_device *dev; + + dev = i2o_device_alloc(); + if (IS_ERR(dev)) { + printk(KERN_ERR "i2o: unable to allocate i2o device\n"); + return dev; + } + + dev->lct_data = *entry; + + snprintf(dev->device.bus_id, BUS_ID_SIZE, "%d:%03x", c->unit, + dev->lct_data.tid); + + snprintf(dev->classdev.class_id, BUS_ID_SIZE, "%d:%03x", c->unit, + dev->lct_data.tid); + + dev->iop = c; + dev->device.parent = &c->device; + + device_register(&dev->device); + + list_add_tail(&dev->list, &c->devices); + + class_device_register(&dev->classdev); + + i2o_driver_notify_device_add_all(dev); + + pr_debug("I2O device %s added\n", dev->device.bus_id); + + return dev; +}; + +/** + * i2o_device_remove - remove an I2O device from the I2O core + * @dev: I2O device which should be released + * + * Is used on I2O controller removal or LCT modification, when the device + * is removed from the system. Note that the device could still hang + * around until the refcount reaches 0. + */ +void i2o_device_remove(struct i2o_device *i2o_dev) +{ + i2o_driver_notify_device_remove_all(i2o_dev); + class_device_unregister(&i2o_dev->classdev); + list_del(&i2o_dev->list); + device_unregister(&i2o_dev->device); +}; + +/** + * i2o_device_parse_lct - Parse a previously fetched LCT and create devices + * @c: I2O controller from which the LCT should be parsed. + * + * The Logical Configuration Table tells us what we can talk to on the + * board. For every entry we create an I2O device, which is registered in + * the I2O core. + * + * Returns 0 on success or negative error code on failure. + */ +int i2o_device_parse_lct(struct i2o_controller *c) +{ + struct i2o_device *dev, *tmp; + i2o_lct *lct; + int i; + int max; + + down(&c->lct_lock); + + if (c->lct) + kfree(c->lct); + + lct = c->dlct.virt; + + c->lct = kmalloc(lct->table_size * 4, GFP_KERNEL); + if (!c->lct) { + up(&c->lct_lock); + return -ENOMEM; + } + + if (lct->table_size * 4 > c->dlct.len) { + memcpy_fromio(c->lct, c->dlct.virt, c->dlct.len); + up(&c->lct_lock); + return -EAGAIN; + } + + memcpy_fromio(c->lct, c->dlct.virt, lct->table_size * 4); + + lct = c->lct; + + max = (lct->table_size - 3) / 9; + + pr_debug("LCT has %d entries (LCT size: %d)\n", max, lct->table_size); + + /* remove devices, which are not in the LCT anymore */ + list_for_each_entry_safe(dev, tmp, &c->devices, list) { + int found = 0; + + for (i = 0; i < max; i++) { + if (lct->lct_entry[i].tid == dev->lct_data.tid) { + found = 1; + break; + } + } + + if (!found) + i2o_device_remove(dev); + } + + /* add new devices, which are new in the LCT */ + for (i = 0; i < max; i++) { + int found = 0; + + list_for_each_entry_safe(dev, tmp, &c->devices, list) { + if (lct->lct_entry[i].tid == dev->lct_data.tid) { + found = 1; + break; + } + } + + if (!found) + i2o_device_add(c, &lct->lct_entry[i]); + } + up(&c->lct_lock); + + return 0; +}; + +/** + * i2o_device_class_show_class_id - Displays class id of I2O device + * @cd: class device of which the class id should be displayed + * @buf: buffer into which the class id should be printed + * + * Returns the number of bytes which are printed into the buffer. + */ +static ssize_t i2o_device_class_show_class_id(struct class_device *cd, + char *buf) +{ + struct i2o_device *dev = to_i2o_device(cd->dev); + + sprintf(buf, "%03x\n", dev->lct_data.class_id); + return strlen(buf) + 1; +}; + +/** + * i2o_device_class_show_tid - Displays TID of I2O device + * @cd: class device of which the TID should be displayed + * @buf: buffer into which the class id should be printed + * + * Returns the number of bytes which are printed into the buffer. + */ +static ssize_t i2o_device_class_show_tid(struct class_device *cd, char *buf) +{ + struct i2o_device *dev = to_i2o_device(cd->dev); + + sprintf(buf, "%03x\n", dev->lct_data.tid); + return strlen(buf) + 1; +}; + +/* I2O device class attributes */ +static CLASS_DEVICE_ATTR(class_id, S_IRUGO, i2o_device_class_show_class_id, + NULL); +static CLASS_DEVICE_ATTR(tid, S_IRUGO, i2o_device_class_show_tid, NULL); + +/** + * i2o_device_class_add - Adds attributes to the I2O device + * @cd: I2O class device which is added to the I2O device class + * + * This function get called when a I2O device is added to the class. It + * creates the attributes for each device and creates user/parent symlink + * if necessary. + * + * Returns 0 on success or negative error code on failure. + */ +static int i2o_device_class_add(struct class_device *cd) +{ + struct i2o_device *i2o_dev, *tmp; + struct i2o_controller *c; + + i2o_dev = to_i2o_device(cd->dev); + c = i2o_dev->iop; + + class_device_create_file(cd, &class_device_attr_class_id); + class_device_create_file(cd, &class_device_attr_tid); + + /* create user entries for this device */ + tmp = i2o_iop_find_device(i2o_dev->iop, i2o_dev->lct_data.user_tid); + if (tmp) + sysfs_create_link(&i2o_dev->device.kobj, &tmp->device.kobj, + "user"); + + /* create user entries refering to this device */ + list_for_each_entry(tmp, &c->devices, list) + if (tmp->lct_data.user_tid == i2o_dev->lct_data.tid) + sysfs_create_link(&tmp->device.kobj, + &i2o_dev->device.kobj, "user"); + + /* create parent entries for this device */ + tmp = i2o_iop_find_device(i2o_dev->iop, i2o_dev->lct_data.parent_tid); + if (tmp) + sysfs_create_link(&i2o_dev->device.kobj, &tmp->device.kobj, + "parent"); + + /* create parent entries refering to this device */ + list_for_each_entry(tmp, &c->devices, list) + if (tmp->lct_data.parent_tid == i2o_dev->lct_data.tid) + sysfs_create_link(&tmp->device.kobj, + &i2o_dev->device.kobj, "parent"); + + return 0; +}; + +/* I2O device class interface */ +static struct class_interface i2o_device_class_interface = { + .class = &i2o_device_class, + .add = i2o_device_class_add +}; + +/* + * Run time support routines + */ + +/* Issue UTIL_PARAMS_GET or UTIL_PARAMS_SET + * + * This function can be used for all UtilParamsGet/Set operations. + * The OperationList is given in oplist-buffer, + * and results are returned in reslist-buffer. + * Note that the minimum sized reslist is 8 bytes and contains + * ResultCount, ErrorInfoSize, BlockStatus and BlockSize. + */ + +int i2o_parm_issue(struct i2o_device *i2o_dev, int cmd, void *oplist, + int oplen, void *reslist, int reslen) +{ + struct i2o_message *msg; + u32 m; + u32 *res32 = (u32 *) reslist; + u32 *restmp = (u32 *) reslist; + int len = 0; + int i = 0; + int rc; + struct i2o_dma res; + struct i2o_controller *c = i2o_dev->iop; + struct device *dev = &c->pdev->dev; + + res.virt = NULL; + + if (i2o_dma_alloc(dev, &res, reslen, GFP_KERNEL)) + return -ENOMEM; + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) { + i2o_dma_free(dev, &res); + return -ETIMEDOUT; + } + + i = 0; + writel(cmd << 24 | HOST_TID << 12 | i2o_dev->lct_data.tid, + &msg->u.head[1]); + writel(0, &msg->body[i++]); + writel(0x4C000000 | oplen, &msg->body[i++]); /* OperationList */ + memcpy_toio(&msg->body[i], oplist, oplen); + i += (oplen / 4 + (oplen % 4 ? 1 : 0)); + writel(0xD0000000 | res.len, &msg->body[i++]); /* ResultList */ + writel(res.phys, &msg->body[i++]); + + writel(I2O_MESSAGE_SIZE(i + sizeof(struct i2o_message) / 4) | + SGL_OFFSET_5, &msg->u.head[0]); + + rc = i2o_msg_post_wait_mem(c, m, 10, &res); + + /* This only looks like a memory leak - don't "fix" it. */ + if (rc == -ETIMEDOUT) + return rc; + + memcpy_fromio(reslist, res.virt, res.len); + i2o_dma_free(dev, &res); + + /* Query failed */ + if (rc) + return rc; + /* + * Calculate number of bytes of Result LIST + * We need to loop through each Result BLOCK and grab the length + */ + restmp = res32 + 1; + len = 1; + for (i = 0; i < (res32[0] & 0X0000FFFF); i++) { + if (restmp[0] & 0x00FF0000) { /* BlockStatus != SUCCESS */ + printk(KERN_WARNING + "%s - Error:\n ErrorInfoSize = 0x%02x, " + "BlockStatus = 0x%02x, BlockSize = 0x%04x\n", + (cmd == + I2O_CMD_UTIL_PARAMS_SET) ? "PARAMS_SET" : + "PARAMS_GET", res32[1] >> 24, + (res32[1] >> 16) & 0xFF, res32[1] & 0xFFFF); + + /* + * If this is the only request,than we return an error + */ + if ((res32[0] & 0x0000FFFF) == 1) { + return -((res32[1] >> 16) & 0xFF); /* -BlockStatus */ + } + } + len += restmp[0] & 0x0000FFFF; /* Length of res BLOCK */ + restmp += restmp[0] & 0x0000FFFF; /* Skip to next BLOCK */ + } + return (len << 2); /* bytes used by result list */ +} + +/* + * Query one field group value or a whole scalar group. + */ +int i2o_parm_field_get(struct i2o_device *i2o_dev, int group, int field, + void *buf, int buflen) +{ + u16 opblk[] = { 1, 0, I2O_PARAMS_FIELD_GET, group, 1, field }; + u8 resblk[8 + buflen]; /* 8 bytes for header */ + int size; + + if (field == -1) /* whole group */ + opblk[4] = -1; + + size = i2o_parm_issue(i2o_dev, I2O_CMD_UTIL_PARAMS_GET, opblk, + sizeof(opblk), resblk, sizeof(resblk)); + + memcpy(buf, resblk + 8, buflen); /* cut off header */ + + if (size > buflen) + return buflen; + + return size; +} + +/* + * Set a scalar group value or a whole group. + */ +int i2o_parm_field_set(struct i2o_device *i2o_dev, int group, int field, + void *buf, int buflen) +{ + u16 *opblk; + u8 resblk[8 + buflen]; /* 8 bytes for header */ + int size; + + opblk = kmalloc(buflen + 64, GFP_KERNEL); + if (opblk == NULL) { + printk(KERN_ERR "i2o: no memory for operation buffer.\n"); + return -ENOMEM; + } + + opblk[0] = 1; /* operation count */ + opblk[1] = 0; /* pad */ + opblk[2] = I2O_PARAMS_FIELD_SET; + opblk[3] = group; + + if (field == -1) { /* whole group */ + opblk[4] = -1; + memcpy(opblk + 5, buf, buflen); + } else { /* single field */ + + opblk[4] = 1; + opblk[5] = field; + memcpy(opblk + 6, buf, buflen); + } + + size = i2o_parm_issue(i2o_dev, I2O_CMD_UTIL_PARAMS_SET, opblk, + 12 + buflen, resblk, sizeof(resblk)); + + kfree(opblk); + if (size > buflen) + return buflen; + + return size; +} + +/* + * if oper == I2O_PARAMS_TABLE_GET, get from all rows + * if fieldcount == -1 return all fields + * ibuf and ibuflen are unused (use NULL, 0) + * else return specific fields + * ibuf contains fieldindexes + * + * if oper == I2O_PARAMS_LIST_GET, get from specific rows + * if fieldcount == -1 return all fields + * ibuf contains rowcount, keyvalues + * else return specific fields + * fieldcount is # of fieldindexes + * ibuf contains fieldindexes, rowcount, keyvalues + * + * You could also use directly function i2o_issue_params(). + */ +int i2o_parm_table_get(struct i2o_device *dev, int oper, int group, + int fieldcount, void *ibuf, int ibuflen, void *resblk, + int reslen) +{ + u16 *opblk; + int size; + + size = 10 + ibuflen; + if (size % 4) + size += 4 - size % 4; + + opblk = kmalloc(size, GFP_KERNEL); + if (opblk == NULL) { + printk(KERN_ERR "i2o: no memory for query buffer.\n"); + return -ENOMEM; + } + + opblk[0] = 1; /* operation count */ + opblk[1] = 0; /* pad */ + opblk[2] = oper; + opblk[3] = group; + opblk[4] = fieldcount; + memcpy(opblk + 5, ibuf, ibuflen); /* other params */ + + size = i2o_parm_issue(dev, I2O_CMD_UTIL_PARAMS_GET, opblk, + size, resblk, reslen); + + kfree(opblk); + if (size > reslen) + return reslen; + + return size; +} + +/** + * i2o_device_init - Initialize I2O devices + * + * Registers the I2O device class. + * + * Returns 0 on success or negative error code on failure. + */ +int i2o_device_init(void) +{ + int rc; + + rc = class_register(&i2o_device_class); + if (rc) + return rc; + + return class_interface_register(&i2o_device_class_interface); +}; + +/** + * i2o_device_exit - I2O devices exit function + * + * Unregisters the I2O device class. + */ +void i2o_device_exit(void) +{ + class_interface_register(&i2o_device_class_interface); + class_unregister(&i2o_device_class); +}; + +EXPORT_SYMBOL(i2o_device_claim); +EXPORT_SYMBOL(i2o_device_claim_release); +EXPORT_SYMBOL(i2o_parm_field_get); +EXPORT_SYMBOL(i2o_parm_field_set); +EXPORT_SYMBOL(i2o_parm_table_get); +EXPORT_SYMBOL(i2o_parm_issue); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/message/i2o/driver.c 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,367 @@ +/* + * Functions to handle I2O drivers (OSMs) and I2O bus type for sysfs + * + * Copyright (C) 2004 Markus Lidel + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Fixes/additions: + * Markus Lidel + * initial version. + */ + +#include +#include +#include +#include + + +/* max_drivers - Maximum I2O drivers (OSMs) which could be registered */ +unsigned int i2o_max_drivers = I2O_MAX_DRIVERS; +module_param_named(max_drivers, i2o_max_drivers, uint, 0); +MODULE_PARM_DESC(max_drivers, "maximum number of OSM's to support"); + +/* I2O drivers lock and array */ +static spinlock_t i2o_drivers_lock = SPIN_LOCK_UNLOCKED; +static struct i2o_driver **i2o_drivers; + +/** + * i2o_bus_match - Tell if a I2O device class id match the class ids of + * the I2O driver (OSM) + * + * @dev: device which should be verified + * @drv: the driver to match against + * + * Used by the bus to check if the driver wants to handle the device. + * + * Returns 1 if the class ids of the driver match the class id of the + * device, otherwise 0. + */ +static int i2o_bus_match(struct device *dev, struct device_driver *drv) +{ + struct i2o_device *i2o_dev = to_i2o_device(dev); + struct i2o_driver *i2o_drv = to_i2o_driver(drv); + struct i2o_class_id *ids = i2o_drv->classes; + + if (ids) + while (ids->class_id != I2O_CLASS_END) { + if (ids->class_id == i2o_dev->lct_data.class_id) + return 1; + ids++; + } + return 0; +}; + +/* I2O bus type */ +struct bus_type i2o_bus_type = { + .name = "i2o", + .match = i2o_bus_match, +}; + +/** + * i2o_driver_register - Register a I2O driver (OSM) in the I2O core + * @drv: I2O driver which should be registered + * + * Registers the OSM drv in the I2O core and creates an event queues if + * necessary. + * + * Returns 0 on success or negative error code on failure. + */ +int i2o_driver_register(struct i2o_driver *drv) +{ + struct i2o_controller *c; + int i; + int rc = 0; + unsigned long flags; + + pr_debug("Register driver %s\n", drv->name); + + if (drv->event) { + drv->event_queue = create_workqueue(drv->name); + if (!drv->event_queue) { + printk(KERN_ERR "i2o: Could not initialize event queue " + "for driver %s\n", drv->name); + return -EFAULT; + } + pr_debug("Event queue initialized for driver %s\n", drv->name); + } else + drv->event_queue = NULL; + + drv->driver.name = drv->name; + drv->driver.bus = &i2o_bus_type; + + spin_lock_irqsave(&i2o_drivers_lock, flags); + + for (i = 0; i2o_drivers[i]; i++) + if (i >= i2o_max_drivers) { + printk(KERN_ERR "i2o: too many drivers registered, " + "increase max_drivers\n"); + spin_unlock_irqrestore(&i2o_drivers_lock, flags); + return -EFAULT; + } + + drv->context = i; + i2o_drivers[i] = drv; + + spin_unlock_irqrestore(&i2o_drivers_lock, flags); + + pr_debug("driver %s gets context id %d\n", drv->name, drv->context); + + list_for_each_entry(c, &i2o_controllers, list) { + struct i2o_device *i2o_dev; + + i2o_driver_notify_controller_add(drv, c); + list_for_each_entry(i2o_dev, &c->devices, list) + i2o_driver_notify_device_add(drv, i2o_dev); + } + + + rc = driver_register(&drv->driver); + if (rc) + destroy_workqueue(drv->event_queue); + + return rc; +}; + +/** + * i2o_driver_unregister - Unregister a I2O driver (OSM) from the I2O core + * @drv: I2O driver which should be unregistered + * + * Unregisters the OSM drv from the I2O core and cleanup event queues if + * necessary. + */ +void i2o_driver_unregister(struct i2o_driver *drv) +{ + struct i2o_controller *c; + unsigned long flags; + + pr_debug("unregister driver %s\n", drv->name); + + driver_unregister(&drv->driver); + + list_for_each_entry(c, &i2o_controllers, list) { + struct i2o_device *i2o_dev; + + list_for_each_entry(i2o_dev, &c->devices, list) + i2o_driver_notify_device_remove(drv, i2o_dev); + + i2o_driver_notify_controller_remove(drv, c); + } + + spin_lock_irqsave(&i2o_drivers_lock, flags); + i2o_drivers[drv->context] = NULL; + spin_unlock_irqrestore(&i2o_drivers_lock, flags); + + if (drv->event_queue) { + destroy_workqueue(drv->event_queue); + drv->event_queue = NULL; + pr_debug("event queue removed for %s\n", drv->name); + } +}; + +/** + * i2o_driver_dispatch - dispatch an I2O reply message + * @c: I2O controller of the message + * @m: I2O message number + * @msg: I2O message to be delivered + * + * The reply is delivered to the driver from which the original message + * was. This function is only called from interrupt context. + * + * Returns 0 on success and the message should not be flushed. Returns > 0 + * on success and if the message should be flushed afterwords. Returns + * negative error code on failure (the message will be flushed too). + */ +int i2o_driver_dispatch(struct i2o_controller *c, u32 m, + struct i2o_message *msg) +{ + struct i2o_driver *drv; + u32 context = readl(&msg->u.s.icntxt); + + if (likely(context < i2o_max_drivers)) { + spin_lock(&i2o_drivers_lock); + drv = i2o_drivers[context]; + spin_unlock(&i2o_drivers_lock); + + if (unlikely(!drv)) { + printk(KERN_WARNING "i2o: Spurious reply to unknown " + "driver %d\n", context); + return -EIO; + } + + if ((readl(&msg->u.head[1]) >> 24) == I2O_CMD_UTIL_EVT_REGISTER) { + struct i2o_device *dev, *tmp; + struct i2o_event *evt; + u16 size; + u16 tid; + + tid = readl(&msg->u.head[1]) & 0x1fff; + + pr_debug("%s: event received from device %d\n", c->name, + tid); + + /* cut of header from message size (in 32-bit words) */ + size = (readl(&msg->u.head[0]) >> 16) - 5; + + evt = kmalloc(size * 4 + sizeof(*evt), GFP_ATOMIC); + if (!evt) + return -ENOMEM; + memset(evt, 0, size * 4 + sizeof(*evt)); + + evt->size = size; + memcpy_fromio(&evt->tcntxt, &msg->u.s.tcntxt, + (size + 2) * 4); + + list_for_each_entry_safe(dev, tmp, &c->devices, list) + if (dev->lct_data.tid == tid) { + evt->i2o_dev = dev; + break; + } + + INIT_WORK(&evt->work, (void (*)(void *))drv->event, + evt); + queue_work(drv->event_queue, &evt->work); + return 1; + } + + if (likely(drv->reply)) + return drv->reply(c, m, msg); + else + pr_debug("%s: Reply to driver %s, but no reply function" + " defined!\n", c->name, drv->name); + return -EIO; + } else + printk(KERN_WARNING "i2o: Spurious reply to unknown driver " + "%d\n", readl(&msg->u.s.icntxt)); + return -EIO; +} + +/** + * i2o_driver_notify_controller_add_all - Send notify of added controller + * to all I2O drivers + * + * Send notifications to all registered drivers that a new controller was + * added. + */ +void i2o_driver_notify_controller_add_all(struct i2o_controller *c) { + int i; + struct i2o_driver *drv; + + for(i = 0; i < I2O_MAX_DRIVERS; i ++) { + drv = i2o_drivers[i]; + + if(drv) + i2o_driver_notify_controller_add(drv, c); + } +} + +/** + * i2o_driver_notify_controller_remove_all - Send notify of removed + * controller to all I2O drivers + * + * Send notifications to all registered drivers that a controller was + * removed. + */ +void i2o_driver_notify_controller_remove_all(struct i2o_controller *c) { + int i; + struct i2o_driver *drv; + + for(i = 0; i < I2O_MAX_DRIVERS; i ++) { + drv = i2o_drivers[i]; + + if(drv) + i2o_driver_notify_controller_remove(drv, c); + } +} + +/** + * i2o_driver_notify_device_add_all - Send notify of added device to all + * I2O drivers + * + * Send notifications to all registered drivers that a device was added. + */ +void i2o_driver_notify_device_add_all(struct i2o_device *i2o_dev) { + int i; + struct i2o_driver *drv; + + for(i = 0; i < I2O_MAX_DRIVERS; i ++) { + drv = i2o_drivers[i]; + + if(drv) + i2o_driver_notify_device_add(drv, i2o_dev); + } +} + +/** + * i2o_driver_notify_device_remove_all - Send notify of removed device to + * all I2O drivers + * + * Send notifications to all registered drivers that a device was removed. + */ +void i2o_driver_notify_device_remove_all(struct i2o_device *i2o_dev) { + int i; + struct i2o_driver *drv; + + for(i = 0; i < I2O_MAX_DRIVERS; i ++) { + drv = i2o_drivers[i]; + + if(drv) + i2o_driver_notify_device_remove(drv, i2o_dev); + } +} + +/** + * i2o_driver_init - initialize I2O drivers (OSMs) + * + * Registers the I2O bus and allocate memory for the array of OSMs. + * + * Returns 0 on success or negative error code on failure. + */ +int __init i2o_driver_init(void) +{ + int rc = 0; + + if ((i2o_max_drivers < 2) || (i2o_max_drivers > 64) || + ((i2o_max_drivers ^ (i2o_max_drivers - 1)) != + (2 * i2o_max_drivers - 1))) { + printk(KERN_WARNING "i2o: max_drivers set to %d, but must be " + ">=2 and <= 64 and a power of 2\n", i2o_max_drivers); + i2o_max_drivers = I2O_MAX_DRIVERS; + } + printk(KERN_INFO "i2o: max_drivers=%d\n", i2o_max_drivers); + + i2o_drivers = + kmalloc(i2o_max_drivers * sizeof(*i2o_drivers), GFP_KERNEL); + if (!i2o_drivers) + return -ENOMEM; + + memset(i2o_drivers, 0, i2o_max_drivers * sizeof(*i2o_drivers)); + + rc = bus_register(&i2o_bus_type); + + if (rc < 0) + kfree(i2o_drivers); + + return rc; +}; + +/** + * i2o_driver_exit - clean up I2O drivers (OSMs) + * + * Unregisters the I2O bus and free driver array. + */ +void __exit i2o_driver_exit(void) +{ + bus_unregister(&i2o_bus_type); + kfree(i2o_drivers); +}; + +EXPORT_SYMBOL(i2o_driver_register); +EXPORT_SYMBOL(i2o_driver_unregister); +EXPORT_SYMBOL(i2o_driver_notify_controller_add_all); +EXPORT_SYMBOL(i2o_driver_notify_controller_remove_all); +EXPORT_SYMBOL(i2o_driver_notify_device_add_all); +EXPORT_SYMBOL(i2o_driver_notify_device_remove_all); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/message/i2o/exec-osm.c 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,505 @@ +/* + * Executive OSM + * + * Copyright (C) 1999-2002 Red Hat Software + * + * Written by Alan Cox, Building Number Three Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * A lot of the I2O message side code from this is taken from the Red + * Creek RCPCI45 adapter driver by Red Creek Communications + * + * Fixes/additions: + * Philipp Rumpf + * Juha Sievänen + * Auvo Häkkinen + * Deepak Saxena + * Boji T Kannanthanam + * Alan Cox : + * Ported to Linux 2.5. + * Markus Lidel : + * Minor fixes for 2.6. + * Markus Lidel : + * Support for sysfs included. + */ + +#include +#include + +struct i2o_driver i2o_exec_driver; + +/* Module internal functions from other sources */ +extern int i2o_device_parse_lct(struct i2o_controller *); + +/* global wait list for POST WAIT */ +static LIST_HEAD(i2o_exec_wait_list); + +/* Wait struct needed for POST WAIT */ +struct i2o_exec_wait { + wait_queue_head_t *wq; /* Pointer to Wait queue */ + struct i2o_dma dma; /* DMA buffers to free on failure */ + u32 tcntxt; /* transaction context from reply */ + int complete; /* 1 if reply received otherwise 0 */ + u32 m; /* message id */ + struct i2o_message *msg; /* pointer to the reply message */ + struct list_head list; /* node in global wait list */ +}; + +/* Exec OSM class handling definition */ +static struct i2o_class_id i2o_exec_class_id[] = { + {I2O_CLASS_EXECUTIVE}, + {I2O_CLASS_END} +}; + +/** + * i2o_exec_wait_alloc - Allocate a i2o_exec_wait struct an initialize it + * + * Allocate the i2o_exec_wait struct and initialize the wait. + * + * Returns i2o_exec_wait pointer on success or negative error code on + * failure. + */ +static struct i2o_exec_wait *i2o_exec_wait_alloc(void) +{ + struct i2o_exec_wait *wait; + + wait = kmalloc(sizeof(*wait), GFP_KERNEL); + if (!wait) + return ERR_PTR(-ENOMEM); + + memset(wait, 0, sizeof(*wait)); + + INIT_LIST_HEAD(&wait->list); + + return wait; +}; + +/** + * i2o_exec_wait_free - Free a i2o_exec_wait struct + * @i2o_exec_wait: I2O wait data which should be cleaned up + */ +static void i2o_exec_wait_free(struct i2o_exec_wait *wait) +{ + kfree(wait); +}; + +/** + * i2o_msg_post_wait_mem - Post and wait a message with DMA buffers + * @c: controller + * @m: message to post + * @timeout: time in seconds to wait + * @dma: i2o_dma struct of the DMA buffer to free on failure + * + * This API allows an OSM to post a message and then be told whether or + * not the system received a successful reply. If the message times out + * then the value '-ETIMEDOUT' is returned. This is a special case. In + * this situation the message may (should) complete at an indefinite time + * in the future. When it completes it will use the memory buffer + * attached to the request. If -ETIMEDOUT is returned then the memory + * buffer must not be freed. Instead the event completion will free them + * for you. In all other cases the buffer are your problem. + * + * Returns 0 on success or negative error code on failure. + */ +int i2o_msg_post_wait_mem(struct i2o_controller *c, u32 m, unsigned long + timeout, struct i2o_dma *dma) +{ + DECLARE_WAIT_QUEUE_HEAD(wq); + DEFINE_WAIT(wait); + struct i2o_exec_wait *iwait; + static u32 tcntxt = 0x80000000; + struct i2o_message *msg = c->in_queue.virt + m; + int rc = 0; + + iwait = i2o_exec_wait_alloc(); + if (!iwait) + return -ENOMEM; + + if (tcntxt == 0xffffffff) + tcntxt = 0x80000000; + + if (dma) + iwait->dma = *dma; + + /* + * Fill in the message initiator context and transaction context. + * We will only use transaction contexts >= 0x80000000 for POST WAIT, + * so we could find a POST WAIT reply easier in the reply handler. + */ + writel(i2o_exec_driver.context, &msg->u.s.icntxt); + iwait->tcntxt = tcntxt++; + writel(iwait->tcntxt, &msg->u.s.tcntxt); + + /* + * Post the message to the controller. At some point later it will + * return. If we time out before it returns then complete will be zero. + */ + i2o_msg_post(c, m); + + if (!iwait->complete) { + iwait->wq = &wq; + /* + * we add elements add the head, because if a entry in the list + * will never be removed, we have to iterate over it every time + */ + list_add(&iwait->list, &i2o_exec_wait_list); + + prepare_to_wait(&wq, &wait, TASK_INTERRUPTIBLE); + + if (!iwait->complete) + schedule_timeout(timeout * HZ); + + finish_wait(&wq, &wait); + + iwait->wq = NULL; + } + + barrier(); + + if (iwait->complete) { + if (readl(&iwait->msg->body[0]) >> 24) + rc = readl(&iwait->msg->body[0]) & 0xff; + i2o_flush_reply(c, iwait->m); + i2o_exec_wait_free(iwait); + } else { + /* + * We cannot remove it now. This is important. When it does + * terminate (which it must do if the controller has not + * died...) then it will otherwise scribble on stuff. + * + * FIXME: try abort message + */ + if (dma) + dma->virt = NULL; + + rc = -ETIMEDOUT; + } + + return rc; +}; + +/** + * i2o_msg_post_wait_complete - Reply to a i2o_msg_post request from IOP + * @c: I2O controller which answers + * @m: message id + * @msg: pointer to the I2O reply message + * + * This function is called in interrupt context only. If the reply reached + * before the timeout, the i2o_exec_wait struct is filled with the message + * and the task will be waked up. The task is now responsible for returning + * the message m back to the controller! If the message reaches us after + * the timeout clean up the i2o_exec_wait struct (including allocated + * DMA buffer). + * + * Return 0 on success and if the message m should not be given back to the + * I2O controller, or >0 on success and if the message should be given back + * afterwords. Returns negative error code on failure. In this case the + * message must also be given back to the controller. + */ +static int i2o_msg_post_wait_complete(struct i2o_controller *c, u32 m, + struct i2o_message *msg) +{ + struct i2o_exec_wait *wait, *tmp; + static spinlock_t lock = SPIN_LOCK_UNLOCKED; + int rc = 1; + u32 context; + + context = readl(&msg->u.s.tcntxt); + + /* + * We need to search through the i2o_exec_wait_list to see if the given + * message is still outstanding. If not, it means that the IOP took + * longer to respond to the message than we had allowed and timer has + * already expired. Not much we can do about that except log it for + * debug purposes, increase timeout, and recompile. + */ + spin_lock(&lock); + list_for_each_entry_safe(wait, tmp, &i2o_exec_wait_list, list) { + if (wait->tcntxt == context) { + list_del(&wait->list); + + wait->m = m; + wait->msg = msg; + wait->complete = 1; + + barrier(); + + if (wait->wq) { + wake_up_interruptible(wait->wq); + rc = 0; + } else { + struct device *dev; + + dev = &c->pdev->dev; + + pr_debug("timedout reply received!\n"); + i2o_dma_free(dev, &wait->dma); + i2o_exec_wait_free(wait); + rc = -1; + } + + spin_unlock(&lock); + + return rc; + } + } + + spin_unlock(&lock); + + pr_debug("i2o: Bogus reply in POST WAIT (tr-context: %08x)!\n", + context); + + return -1; +}; + +/** + * i2o_exec_probe - Called if a new I2O device (executive class) appears + * @dev: I2O device which should be probed + * + * Registers event notification for every event from Executive device. The + * return is always 0, because we want all devices of class Executive. + * + * Returns 0 on success. + */ +static int i2o_exec_probe(struct device *dev) +{ + struct i2o_device *i2o_dev = to_i2o_device(dev); + + i2o_event_register(i2o_dev, &i2o_exec_driver, 0, 0xffffffff); + + i2o_dev->iop->exec = i2o_dev; + + return 0; +}; + +/** + * i2o_exec_remove - Called on I2O device removal + * @dev: I2O device which was removed + * + * Unregisters event notification from Executive I2O device. + * + * Returns 0 on success. + */ +static int i2o_exec_remove(struct device *dev) +{ + i2o_event_register(to_i2o_device(dev), &i2o_exec_driver, 0, 0); + + return 0; +}; + +/** + * i2o_exec_lct_modified - Called on LCT NOTIFY reply + * @c: I2O controller on which the LCT has modified + * + * This function handles asynchronus LCT NOTIFY replies. It parses the + * new LCT and if the buffer for the LCT was to small sends a LCT NOTIFY + * again. + */ +static void i2o_exec_lct_modified(struct i2o_controller *c) +{ + if (i2o_device_parse_lct(c) == -EAGAIN) + i2o_exec_lct_notify(c, 0); +}; + +/** + * i2o_exec_reply - I2O Executive reply handler + * @c: I2O controller from which the reply comes + * @m: message id + * @msg: pointer to the I2O reply message + * + * This function is always called from interrupt context. If a POST WAIT + * reply was received, pass it to the complete function. If a LCT NOTIFY + * reply was received, a new event is created to handle the update. + * + * Returns 0 on success and if the reply should not be flushed or > 0 + * on success and if the reply should be flushed. Returns negative error + * code on failure and if the reply should be flushed. + */ +static int i2o_exec_reply(struct i2o_controller *c, u32 m, + struct i2o_message *msg) +{ + if (readl(&msg->u.head[0]) & MSG_FAIL) { // Fail bit is set + struct i2o_message *pmsg; /* preserved message */ + u32 pm; + + pm = readl(&msg->body[3]); + + pmsg = c->in_queue.virt + pm; + + i2o_report_status(KERN_INFO, "i2o_core", msg); + + /* Release the preserved msg by resubmitting it as a NOP */ + i2o_msg_nop(c, pm); + + /* If reply to i2o_post_wait failed, return causes a timeout */ + return -1; + } + + if (readl(&msg->u.s.tcntxt) & 0x80000000) + return i2o_msg_post_wait_complete(c, m, msg); + + if ((readl(&msg->u.head[1]) >> 24) == I2O_CMD_LCT_NOTIFY) { + struct work_struct *work; + + pr_debug("%s: LCT notify received\n", c->name); + + work = kmalloc(sizeof(*work), GFP_ATOMIC); + if (!work) + return -ENOMEM; + + INIT_WORK(work, (void (*)(void *))i2o_exec_lct_modified, c); + queue_work(i2o_exec_driver.event_queue, work); + return 1; + } + + /* + * If this happens, we want to dump the message to the syslog so + * it can be sent back to the card manufacturer by the end user + * to aid in debugging. + * + */ + printk(KERN_WARNING "%s: Unsolicited message reply sent to core!" + "Message dumped to syslog\n", c->name); + i2o_dump_message(msg); + + return -EFAULT; +} + +/** + * i2o_exec_event - Event handling function + * @evt: Event which occurs + * + * Handles events send by the Executive device. At the moment does not do + * anything useful. + */ +static void i2o_exec_event(struct i2o_event *evt) +{ + printk(KERN_INFO "Event received from device: %d\n", + evt->i2o_dev->lct_data.tid); + kfree(evt); +}; + +/** + * i2o_exec_lct_get - Get the IOP's Logical Configuration Table + * @c: I2O controller from which the LCT should be fetched + * + * Send a LCT NOTIFY request to the controller, and wait + * I2O_TIMEOUT_LCT_GET seconds until arrival of response. If the LCT is + * to large, retry it. + * + * Returns 0 on success or negative error code on failure. + */ +int i2o_exec_lct_get(struct i2o_controller *c) +{ + struct i2o_message *msg; + u32 m; + int i = 0; + int rc = -EAGAIN; + + for (i = 1; i <= I2O_LCT_GET_TRIES; i++) { + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(EIGHT_WORD_MSG_SIZE | SGL_OFFSET_6, &msg->u.head[0]); + writel(I2O_CMD_LCT_NOTIFY << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + writel(0xffffffff, &msg->body[0]); + writel(0x00000000, &msg->body[1]); + writel(0xd0000000 | c->dlct.len, &msg->body[2]); + writel(c->dlct.phys, &msg->body[3]); + + rc = i2o_msg_post_wait(c, m, I2O_TIMEOUT_LCT_GET); + if (rc < 0) + break; + + rc = i2o_device_parse_lct(c); + if (rc != -EAGAIN) + break; + } + + return rc; +} + +/** + * i2o_exec_lct_notify - Send a asynchronus LCT NOTIFY request + * @c: I2O controller to which the request should be send + * @change_ind: change indicator + * + * This function sends a LCT NOTIFY request to the I2O controller with + * the change indicator change_ind. If the change_ind == 0 the controller + * replies immediately after the request. If change_ind > 0 the reply is + * send after change indicator of the LCT is > change_ind. + */ +int i2o_exec_lct_notify(struct i2o_controller *c, u32 change_ind) +{ + i2o_status_block *sb = c->status_block.virt; + struct device *dev; + struct i2o_message *msg; + u32 m; + + dev = &c->pdev->dev; + + if (i2o_dma_realloc(dev, &c->dlct, sb->expected_lct_size, GFP_KERNEL)) + return -ENOMEM; + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(EIGHT_WORD_MSG_SIZE | SGL_OFFSET_6, &msg->u.head[0]); + writel(I2O_CMD_LCT_NOTIFY << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + writel(i2o_exec_driver.context, &msg->u.s.icntxt); + writel(0, &msg->u.s.tcntxt); /* FIXME */ + writel(0xffffffff, &msg->body[0]); + writel(change_ind, &msg->body[1]); + writel(0xd0000000 | c->dlct.len, &msg->body[2]); + writel(c->dlct.phys, &msg->body[3]); + + i2o_msg_post(c, m); + + return 0; +}; + +/* Exec OSM driver struct */ +struct i2o_driver i2o_exec_driver = { + .name = "exec-osm", + .reply = i2o_exec_reply, + .event = i2o_exec_event, + .classes = i2o_exec_class_id, + .driver = { + .probe = i2o_exec_probe, + .remove = i2o_exec_remove, + }, +}; + +/** + * i2o_exec_init - Registers the Exec OSM + * + * Registers the Exec OSM in the I2O core. + * + * Returns 0 on success or negative error code on failure. + */ +int __init i2o_exec_init(void) +{ + return i2o_driver_register(&i2o_exec_driver); +}; + +/** + * i2o_exec_exit - Removes the Exec OSM + * + * Unregisters the Exec OSM from the I2O core. + */ +void __exit i2o_exec_exit(void) +{ + i2o_driver_unregister(&i2o_exec_driver); +}; + +EXPORT_SYMBOL(i2o_msg_post_wait_mem); +EXPORT_SYMBOL(i2o_exec_lct_get); +EXPORT_SYMBOL(i2o_exec_lct_notify); --- linux-2.6.9-rc1/drivers/message/i2o/i2o_block.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/drivers/message/i2o/i2o_block.c 2004-09-02 20:51:52.000000000 -0700 @@ -1,463 +1,426 @@ /* - * I2O Random Block Storage Class OSM + * Block OSM * - * (C) Copyright 1999-2002 Red Hat - * - * Written by Alan Cox, Building Number Three Ltd - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * For the purpose of avoiding doubt the preferred form of the work - * for making modifications shall be a standards compliant form such - * gzipped tar and not one requiring a proprietary or patent encumbered - * tool to unpack. - * - * This is a beta test release. Most of the good code was taken - * from the nbd driver by Pavel Machek, who in turn took some of it - * from loop.c. Isn't free software great for reusability 8) - * - * Fixes/additions: - * Steve Ralston: - * Multiple device handling error fixes, - * Added a queue depth. - * Alan Cox: - * FC920 has an rmw bug. Dont or in the end marker. - * Removed queue walk, fixed for 64bitness. - * Rewrote much of the code over time - * Added indirect block lists - * Handle 64K limits on many controllers - * Don't use indirects on the Promise (breaks) - * Heavily chop down the queue depths - * Deepak Saxena: - * Independent queues per IOP - * Support for dynamic device creation/deletion - * Code cleanup - * Support for larger I/Os through merge* functions - * (taken from DAC960 driver) - * Boji T Kannanthanam: - * Set the I2O Block devices to be detected in increasing - * order of TIDs during boot. - * Search and set the I2O block device that we boot off from as - * the first device to be claimed (as /dev/i2o/hda) - * Properly attach/detach I2O gendisk structure from the system - * gendisk list. The I2O block devices now appear in - * /proc/partitions. - * Markus Lidel : - * Minor bugfixes for 2.6. + * Copyright (C) 1999-2002 Red Hat Software * - * To do: - * Serial number scanning to find duplicates for FC multipathing + * Written by Alan Cox, Building Number Three Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * For the purpose of avoiding doubt the preferred form of the work + * for making modifications shall be a standards compliant form such + * gzipped tar and not one requiring a proprietary or patent encumbered + * tool to unpack. + * + * Fixes/additions: + * Steve Ralston: + * Multiple device handling error fixes, + * Added a queue depth. + * Alan Cox: + * FC920 has an rmw bug. Dont or in the end marker. + * Removed queue walk, fixed for 64bitness. + * Rewrote much of the code over time + * Added indirect block lists + * Handle 64K limits on many controllers + * Don't use indirects on the Promise (breaks) + * Heavily chop down the queue depths + * Deepak Saxena: + * Independent queues per IOP + * Support for dynamic device creation/deletion + * Code cleanup + * Support for larger I/Os through merge* functions + * (taken from DAC960 driver) + * Boji T Kannanthanam: + * Set the I2O Block devices to be detected in increasing + * order of TIDs during boot. + * Search and set the I2O block device that we boot off + * from as the first device to be claimed (as /dev/i2o/hda) + * Properly attach/detach I2O gendisk structure from the + * system gendisk list. The I2O block devices now appear in + * /proc/partitions. + * Markus Lidel : + * Minor bugfixes for 2.6. */ -#include - #include -#include -#include -#include -#include -#include -#include -#include -#include #include + +#include + +#include #include -#include -#include #include -#include -#include -#include -#include +#include "i2o_block.h" -#include -#include -#include -#include -#include -#include - -#define MAJOR_NR I2O_MAJOR - -#define MAX_I2OB 16 - -#define MAX_I2OB_DEPTH 8 -#define MAX_I2OB_RETRIES 4 - -//#define DRIVERDEBUG -#ifdef DRIVERDEBUG -#define DEBUG( s ) printk( s ) -#else -#define DEBUG( s ) -#endif +static struct i2o_driver i2o_block_driver; -/* - * Events that this OSM is interested in - */ -#define I2OB_EVENT_MASK (I2O_EVT_IND_BSA_VOLUME_LOAD | \ - I2O_EVT_IND_BSA_VOLUME_UNLOAD | \ - I2O_EVT_IND_BSA_VOLUME_UNLOAD_REQ | \ - I2O_EVT_IND_BSA_CAPACITY_CHANGE | \ - I2O_EVT_IND_BSA_SCSI_SMART ) +/* global Block OSM request mempool */ +static struct i2o_block_mempool i2o_blk_req_pool; +/* Block OSM class handling definition */ +static struct i2o_class_id i2o_block_class_id[] = { + {I2O_CLASS_RANDOM_BLOCK_STORAGE}, + {I2O_CLASS_END} +}; -/* - * Some of these can be made smaller later +/** + * i2o_block_device_free - free the memory of the I2O Block device + * @dev: I2O Block device, which should be cleaned up + * + * Frees the request queue, gendisk and the i2o_block_device structure. */ +static void i2o_block_device_free(struct i2o_block_device *dev) +{ + blk_cleanup_queue(dev->gd->queue); -static int i2ob_context; -static struct block_device_operations i2ob_fops; + put_disk(dev->gd); -/* - * I2O Block device descriptor + kfree(dev); +}; + +/** + * i2o_block_remove - remove the I2O Block device from the system again + * @dev: I2O Block device which should be removed + * + * Remove gendisk from system and free all allocated memory. + * + * Always returns 0. */ -struct i2ob_device +static int i2o_block_remove(struct device *dev) { - struct i2o_controller *controller; - struct i2o_device *i2odev; - int unit; - int tid; - int flags; - int refcnt; - struct request *head, *tail; - request_queue_t *req_queue; - int max_segments; - int max_direct; /* Not yet used properly */ - int done_flag; - int depth; - int rcache; - int wcache; - int power; - int index; - int media_change_flag; - u32 max_sectors; - struct gendisk *gd; + struct i2o_device *i2o_dev = to_i2o_device(dev); + struct i2o_block_device *i2o_blk_dev = dev_get_drvdata(dev); + + printk(KERN_INFO "block-osm: Device removed %s\n", + i2o_blk_dev->gd->disk_name); + + i2o_event_register(i2o_dev, &i2o_block_driver, 0, 0); + + del_gendisk(i2o_blk_dev->gd); + + dev_set_drvdata(dev, NULL); + + i2o_device_claim_release(i2o_dev); + + i2o_block_device_free(i2o_blk_dev); + + return 0; }; -/* - * FIXME: - * We should cache align these to avoid ping-ponging lines on SMP - * boxes under heavy I/O load... +/** + * i2o_block_device flush - Flush all dirty data of I2O device dev + * @dev: I2O device which should be flushed + * + * Flushes all dirty data on device dev. + * + * Returns 0 on success or negative error code on failure. */ - -struct i2ob_request +static int i2o_block_device_flush(struct i2o_device *dev) { - struct i2ob_request *next; - struct request *req; - int num; - int sg_dma_direction; - int sg_nents; - struct scatterlist sg_table[16]; + struct i2o_message *msg; + u32 m; + + m = i2o_msg_get_wait(dev->iop, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_BLOCK_CFLUSH << 24 | HOST_TID << 12 | dev->lct_data.tid, + &msg->u.head[1]); + writel(60 << 16, &msg->body[0]); + pr_debug("Flushing...\n"); + + return i2o_msg_post_wait(dev->iop, m, 60); }; -/* - * Per IOP request queue information +/** + * i2o_block_device_mount - Mount (load) the media of device dev + * @dev: I2O device which should receive the mount request + * @media_id: Media Identifier * - * We have a separate request_queue_t per IOP so that a heavilly - * loaded I2O block device on an IOP does not starve block devices - * across all I2O controllers. - * - */ -struct i2ob_iop_queue -{ - unsigned int queue_depth; - struct i2ob_request request_queue[MAX_I2OB_DEPTH]; - struct i2ob_request *i2ob_qhead; - request_queue_t *req_queue; - spinlock_t lock; + * Load a media into drive. Identifier should be set to -1, because the + * spec does not support any other value. + * + * Returns 0 on success or negative error code on failure. + */ +static int i2o_block_device_mount(struct i2o_device *dev, u32 media_id) +{ + struct i2o_message *msg; + u32 m; + + m = i2o_msg_get_wait(dev->iop, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_BLOCK_MMOUNT << 24 | HOST_TID << 12 | dev->lct_data.tid, + &msg->u.head[1]); + writel(-1, &msg->body[0]); + writel(0, &msg->body[1]); + pr_debug("Mounting...\n"); + + return i2o_msg_post_wait(dev->iop, m, 2); }; -static struct i2ob_iop_queue *i2ob_queues[MAX_I2O_CONTROLLERS]; -/* - * Each I2O disk is one of these. +/** + * i2o_block_device_lock - Locks the media of device dev + * @dev: I2O device which should receive the lock request + * @media_id: Media Identifier + * + * Lock media of device dev to prevent removal. The media identifier + * should be set to -1, because the spec does not support any other value. + * + * Returns 0 on success or negative error code on failure. */ +static int i2o_block_device_lock(struct i2o_device *dev, u32 media_id) +{ + struct i2o_message *msg; + u32 m; -static struct i2ob_device i2ob_dev[MAX_I2OB]; -static int i2ob_dev_count = 0; + m = i2o_msg_get_wait(dev->iop, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_BLOCK_MLOCK << 24 | HOST_TID << 12 | dev->lct_data.tid, + &msg->u.head[1]); + writel(-1, &msg->body[0]); + pr_debug("Locking...\n"); -/* - * Mutex and spin lock for event handling synchronization - * evt_msg contains the last event. + return i2o_msg_post_wait(dev->iop, m, 2); +}; + +/** + * i2o_block_device_unlock - Unlocks the media of device dev + * @dev: I2O device which should receive the unlocked request + * @media_id: Media Identifier + * + * Unlocks the media in device dev. The media identifier should be set to + * -1, because the spec does not support any other value. + * + * Returns 0 on success or negative error code on failure. */ -static DECLARE_MUTEX_LOCKED(i2ob_evt_sem); -static DECLARE_COMPLETION(i2ob_thread_dead); -static spinlock_t i2ob_evt_lock = SPIN_LOCK_UNLOCKED; -static u32 evt_msg[MSG_FRAME_SIZE]; - -static void i2o_block_reply(struct i2o_handler *, struct i2o_controller *, - struct i2o_message *); -static void i2ob_new_device(struct i2o_controller *, struct i2o_device *); -static void i2ob_del_device(struct i2o_controller *, struct i2o_device *); -static void i2ob_reboot_event(void); -static int i2ob_install_device(struct i2o_controller *, struct i2o_device *, int); -static void i2ob_end_request(struct request *); -static void i2ob_request(request_queue_t *); -static int i2ob_init_iop(unsigned int); -static int i2ob_query_device(struct i2ob_device *, int, int, void*, int); -static int i2ob_evt(void *); - -static int evt_pid = 0; -static int evt_running = 0; -static int scan_unit = 0; +static int i2o_block_device_unlock(struct i2o_device *dev, u32 media_id) +{ + struct i2o_message *msg; + u32 m; -/* - * I2O OSM registration structure...keeps getting bigger and bigger :) + m = i2o_msg_get_wait(dev->iop, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_BLOCK_MUNLOCK << 24 | HOST_TID << 12 | dev->lct_data.tid, + &msg->u.head[1]); + writel(media_id, &msg->body[0]); + pr_debug("Unlocking...\n"); + + return i2o_msg_post_wait(dev->iop, m, 2); +}; + +/** + * i2o_block_device_power - Power management for device dev + * @dev: I2O device which should receive the power management request + * @operation: Operation which should be send + * + * Send a power management request to the device dev. + * + * Returns 0 on success or negative error code on failure. */ -static struct i2o_handler i2o_block_handler = +static int i2o_block_device_power(struct i2o_block_device *dev, u8 op) { - i2o_block_reply, - i2ob_new_device, - i2ob_del_device, - i2ob_reboot_event, - "I2O Block OSM", - 0, - I2O_CLASS_RANDOM_BLOCK_STORAGE + struct i2o_device *i2o_dev = dev->i2o_dev; + struct i2o_controller *c = i2o_dev->iop; + struct i2o_message *msg; + u32 m; + int rc; + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_BLOCK_POWER << 24 | HOST_TID << 12 | i2o_dev->lct_data. + tid, &msg->u.head[1]); + writel(op << 24, &msg->body[0]); + pr_debug("Power...\n"); + + rc = i2o_msg_post_wait(c, m, 60); + if (!rc) + dev->power = op; + + return rc; }; /** - * i2ob_get - Get an I2O message - * @dev: I2O block device + * i2o_block_request_alloc - Allocate an I2O block request struct * - * Get a message from the FIFO used for this block device. The message is returned - * or the I2O 'no message' value of 0xFFFFFFFF if nothing is available. + * Allocates an I2O block request struct and initialize the list. + * + * Returns a i2o_block_request pointer on success or negative error code + * on failure. */ +static inline struct i2o_block_request *i2o_block_request_alloc(void) +{ + struct i2o_block_request *ireq; + + ireq = mempool_alloc(i2o_blk_req_pool.pool, GFP_ATOMIC); + if (!ireq) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&ireq->queue); -static u32 i2ob_get(struct i2ob_device *dev) + return ireq; +}; + +/** + * i2o_block_request_free - Frees a I2O block request + * @ireq: I2O block request which should be freed + * + * Fres the allocated memory (give it back to the request mempool). + */ +static inline void i2o_block_request_free(struct i2o_block_request *ireq) { - struct i2o_controller *c=dev->controller; - return I2O_POST_READ32(c); -} + mempool_free(ireq, i2o_blk_req_pool.pool); +}; -static int i2ob_build_sglist(struct i2ob_device *dev, struct i2ob_request *ireq) +/** + * i2o_block_sglist_alloc - Allocate the SG list and map it + * @ireq: I2O block request + * + * Builds the SG list and map it into to be accessable by the controller. + * + * Returns the number of elements in the SG list or 0 on failure. + */ +static inline int i2o_block_sglist_alloc(struct i2o_block_request *ireq) { - struct scatterlist *sg = ireq->sg_table; + struct device *dev = &ireq->i2o_blk_dev->i2o_dev->iop->pdev->dev; int nents; - nents = blk_rq_map_sg(dev->req_queue, ireq->req, ireq->sg_table); - + nents = blk_rq_map_sg(ireq->req->q, ireq->req, ireq->sg_table); + if (rq_data_dir(ireq->req) == READ) ireq->sg_dma_direction = PCI_DMA_FROMDEVICE; else ireq->sg_dma_direction = PCI_DMA_TODEVICE; - ireq->sg_nents = pci_map_sg(dev->controller->pdev, sg, nents, ireq->sg_dma_direction); + ireq->sg_nents = dma_map_sg(dev, ireq->sg_table, nents, + ireq->sg_dma_direction); + return ireq->sg_nents; -} +}; -void i2ob_free_sglist(struct i2ob_device *dev, struct i2ob_request *ireq) -{ - struct pci_dev *pdev = dev->controller->pdev; - struct scatterlist *sg = ireq->sg_table; - int nents = ireq->sg_nents; - pci_unmap_sg(pdev, sg, nents, ireq->sg_dma_direction); -} - /** - * i2ob_send - Turn a request into a message and send it - * @m: Message offset - * @dev: I2O device - * @ireq: Request structure - * @unit: Device identity - * - * Generate an I2O BSAREAD request. This interface function is called for devices that - * appear to explode when they are fed indirect chain pointers (notably right now this - * appears to afflict Promise hardwre, so be careful what you feed the hardware - * - * No cleanup is done by this interface. It is done on the interrupt side when the - * reply arrives - */ - -static int i2ob_send(u32 m, struct i2ob_device *dev, struct i2ob_request *ireq, int unit) -{ - struct i2o_controller *c = dev->controller; - int tid = dev->tid; - void *msg; - void *mptr; - u64 offset; - struct request *req = ireq->req; - int count = req->nr_sectors<<9; - struct scatterlist *sg; - int sgnum; - int i; + * i2o_block_sglist_free - Frees the SG list + * @ireq: I2O block request from which the SG should be freed + * + * Frees the SG list from the I2O block request. + */ +static inline void i2o_block_sglist_free(struct i2o_block_request *ireq) +{ + struct device *dev = &ireq->i2o_blk_dev->i2o_dev->iop->pdev->dev; - // printk(KERN_INFO "i2ob_send called\n"); - /* Map the message to a virtual address */ - msg = c->msg_virt + m; - - sgnum = i2ob_build_sglist(dev, ireq); - - /* FIXME: if we have no resources how should we get out of this */ - if(sgnum == 0) - BUG(); - - /* - * Build the message based on the request. - */ - i2o_raw_writel(i2ob_context|(unit<<8), msg+8); - i2o_raw_writel(ireq->num, msg+12); - i2o_raw_writel(req->nr_sectors << 9, msg+20); + dma_unmap_sg(dev, ireq->sg_table, ireq->sg_nents, + ireq->sg_dma_direction); +}; - /* - * Mask out partitions from now on - */ - - /* This can be optimised later - just want to be sure its right for - starters */ - offset = ((u64)req->sector) << 9; - i2o_raw_writel( offset & 0xFFFFFFFF, msg+24); - i2o_raw_writel(offset>>32, msg+28); - mptr=msg+32; - - sg = ireq->sg_table; - if(rq_data_dir(req) == READ) - { - DEBUG("READ\n"); - i2o_raw_writel(I2O_CMD_BLOCK_READ<<24|HOST_TID<<12|tid, msg+4); - for(i = sgnum; i > 0; i--) - { - if(i != 1) - i2o_raw_writel(0x10000000|sg_dma_len(sg), mptr); - else - i2o_raw_writel(0xD0000000|sg_dma_len(sg), mptr); - i2o_raw_writel(sg_dma_address(sg), mptr+4); - mptr += 8; - count -= sg_dma_len(sg); - sg++; - } - switch(dev->rcache) - { - case CACHE_NULL: - i2o_raw_writel(0, msg+16);break; - case CACHE_PREFETCH: - i2o_raw_writel(0x201F0008, msg+16);break; - case CACHE_SMARTFETCH: - if(req->nr_sectors > 16) - i2o_raw_writel(0x201F0008, msg+16); - else - i2o_raw_writel(0x001F0000, msg+16); - break; - } - -// printk("Reading %d entries %d bytes.\n", -// mptr-msg-8, req->nr_sectors<<9); - } - else if(rq_data_dir(req) == WRITE) - { - DEBUG("WRITE\n"); - i2o_raw_writel(I2O_CMD_BLOCK_WRITE<<24|HOST_TID<<12|tid, msg+4); - for(i = sgnum; i > 0; i--) - { - if(i != 1) - i2o_raw_writel(0x14000000|sg_dma_len(sg), mptr); - else - i2o_raw_writel(0xD4000000|sg_dma_len(sg), mptr); - i2o_raw_writel(sg_dma_address(sg), mptr+4); - mptr += 8; - count -= sg_dma_len(sg); - sg++; - } +/** + * i2o_block_prep_req_fn - Allocates I2O block device specific struct + * @q: request queue for the request + * @req: the request to prepare + * + * Allocate the necessary i2o_block_request struct and connect it to + * the request. This is needed that we not loose the SG list later on. + * + * Returns BLKPREP_OK on success or BLKPREP_DEFER on failure. + */ +static int i2o_block_prep_req_fn(struct request_queue *q, struct request *req) +{ + struct i2o_block_device *i2o_blk_dev = q->queuedata; + struct i2o_block_request *ireq; - switch(dev->wcache) - { - case CACHE_NULL: - i2o_raw_writel(0, msg+16);break; - case CACHE_WRITETHROUGH: - i2o_raw_writel(0x001F0008, msg+16);break; - case CACHE_WRITEBACK: - i2o_raw_writel(0x001F0010, msg+16);break; - case CACHE_SMARTBACK: - if(req->nr_sectors > 16) - i2o_raw_writel(0x001F0004, msg+16); - else - i2o_raw_writel(0x001F0010, msg+16); - break; - case CACHE_SMARTTHROUGH: - if(req->nr_sectors > 16) - i2o_raw_writel(0x001F0004, msg+16); - else - i2o_raw_writel(0x001F0010, msg+16); + /* request is already processed by us, so return */ + if (req->flags & REQ_SPECIAL) { + pr_debug("REQ_SPECIAL already set!\n"); + req->flags |= REQ_DONTPREP; + return BLKPREP_OK; + } + + /* connect the i2o_block_request to the request */ + if (!req->special) { + ireq = i2o_block_request_alloc(); + if (unlikely(IS_ERR(ireq))) { + pr_debug("unable to allocate i2o_block_request!\n"); + return BLKPREP_DEFER; } - -// printk("Writing %d entries %d bytes.\n", -// mptr-msg-8, req->nr_sectors<<9); - } - i2o_raw_writel(I2O_MESSAGE_SIZE(mptr-msg)>>2 | SGL_OFFSET_8, msg); - - if(count != 0) - { - printk(KERN_ERR "Request count botched by %d.\n", count); - } - i2o_post_message(c,m); - i2ob_queues[c->unit]->queue_depth ++; + ireq->i2o_blk_dev = i2o_blk_dev; + req->special = ireq; + ireq->req = req; + } else + ireq = req->special; - return 0; -} + /* do not come back here */ + req->flags |= REQ_DONTPREP | REQ_SPECIAL; -/* - * Remove a request from the _locked_ request list. We update both the - * list chain and if this is the last item the tail pointer. Caller - * must hold the lock. - */ - -static inline void i2ob_unhook_request(struct i2ob_request *ireq, - unsigned int iop) -{ - ireq->next = i2ob_queues[iop]->i2ob_qhead; - i2ob_queues[iop]->i2ob_qhead = ireq; -} + return BLKPREP_OK; +}; -/* - * Request completion handler +/** + * i2o_block_delayed_request_fn - delayed request queue function + * delayed_request: the delayed request with the queue to start + * + * If the request queue is stopped for a disk, and there is no open + * request, a new event is created, which calls this function to start + * the queue after I2O_BLOCK_REQUEST_TIME. Otherwise the queue will never + * be started again. */ - -static inline void i2ob_end_request(struct request *req) +static void i2o_block_delayed_request_fn(void *delayed_request) { - /* FIXME - pci unmap the request */ - - /* - * Loop until all of the buffers that are linked - * to this request have been marked updated and - * unlocked. - */ - - while (end_that_request_first( req, !req->errors, req->hard_cur_sectors )); + struct i2o_block_delayed_request *dreq = delayed_request; + struct request_queue *q = dreq->queue; + unsigned long flags; - /* - * It is now ok to complete the request. - */ - end_that_request_last( req ); - DEBUG("IO COMPLETED\n"); -} + spin_lock_irqsave(q->queue_lock, flags); + blk_start_queue(q); + spin_unlock_irqrestore(q->queue_lock, flags); + kfree(dreq); +}; -/* - * OSM reply handler. This gets all the message replies +/** + * i2o_block_reply - Block OSM reply handler. + * @c: I2O controller from which the message arrives + * @m: message id of reply + * qmsg: the actuall I2O message reply + * + * This function gets all the message replies. + * */ - -static void i2o_block_reply(struct i2o_handler *h, struct i2o_controller *c, struct i2o_message *msg) +static int i2o_block_reply(struct i2o_controller *c, u32 m, + struct i2o_message *msg) { - unsigned long flags; - struct i2ob_request *ireq = NULL; + struct i2o_block_request *ireq; + struct request *req; + struct i2o_block_device *dev; + struct request_queue *q; u8 st; - u32 *m = (u32 *)msg; - u8 unit = m[2]>>8; - struct i2ob_device *dev = &i2ob_dev[unit]; + unsigned long flags; - /* - * FAILed message - */ - if(m[0] & (1<<13)) - { - DEBUG("FAIL"); + /* FAILed message */ + if (unlikely(readl(&msg->u.head[0]) & (1 << 13))) { + struct i2o_message *pmsg; + u32 pm; + + printk(KERN_WARNING "FAIL"); /* * FAILed message from controller * We increment the error count and abort it @@ -468,65 +431,85 @@ static void i2o_block_reply(struct i2o_h * better be on the safe side since no one really follows * the spec to the book :) */ - ireq=&i2ob_queues[c->unit]->request_queue[m[3]]; - ireq->req->errors++; + pm = readl(&msg->body[3]); + pmsg = c->in_queue.virt + pm; + + req = i2o_cntxt_list_get(c, readl(&pmsg->u.s.tcntxt)); + if (unlikely(!req)) { + printk(KERN_ERR "block-osm: NULL reply received!\n"); + return -1; + } + + ireq = req->special; + dev = ireq->i2o_blk_dev; + q = dev->gd->queue; + + req->errors++; + + spin_lock_irqsave(q->queue_lock, flags); + + while (end_that_request_chunk(req, !req->errors, + readl(&pmsg->body[1]))) ; + end_that_request_last(req); + + dev->open_queue_depth--; + list_del(&ireq->queue); + blk_start_queue(q); + + spin_unlock_irqrestore(q->queue_lock, flags); - spin_lock_irqsave(dev->req_queue->queue_lock, flags); - i2ob_unhook_request(ireq, c->unit); - i2ob_end_request(ireq->req); - spin_unlock_irqrestore(dev->req_queue->queue_lock, flags); - /* Now flush the message by making it a NOP */ - m[0]&=0x00FFFFFF; - m[0]|=(I2O_CMD_UTIL_NOP)<<24; - i2o_post_message(c, (unsigned long) m - (unsigned long) c->msg_virt); + i2o_msg_nop(c, pm); - return; + return -1; } - if(msg->function == I2O_CMD_UTIL_EVT_REGISTER) - { - spin_lock(&i2ob_evt_lock); - memcpy(evt_msg, msg, (m[0]>>16)<<2); - spin_unlock(&i2ob_evt_lock); - up(&i2ob_evt_sem); - return; + req = i2o_cntxt_list_get(c, readl(&msg->u.s.tcntxt)); + if (unlikely(!req)) { + printk(KERN_ERR "block-osm: NULL reply received!\n"); + return -1; } - if(!dev->i2odev) - { + ireq = req->special; + dev = ireq->i2o_blk_dev; + q = dev->gd->queue; + + if (unlikely(!dev->i2o_dev)) { /* * This is HACK, but Intel Integrated RAID allows user - * to delete a volume that is claimed, locked, and in use + * to delete a volume that is claimed, locked, and in use * by the OS. We have to check for a reply from a - * non-existent device and flag it as an error or the system + * non-existent device and flag it as an error or the system * goes kaput... */ - ireq=&i2ob_queues[c->unit]->request_queue[m[3]]; - ireq->req->errors++; - printk(KERN_WARNING "I2O Block: Data transfer to deleted device!\n"); - spin_lock_irqsave(dev->req_queue->queue_lock, flags); - i2ob_unhook_request(ireq, c->unit); - i2ob_end_request(ireq->req); - spin_unlock_irqrestore(dev->req_queue->queue_lock, flags); - return; - } + req->errors++; + printk(KERN_WARNING + "I2O Block: Data transfer to deleted device!\n"); + spin_lock_irqsave(q->queue_lock, flags); + while (end_that_request_chunk + (req, !req->errors, readl(&msg->body[1]))) ; + end_that_request_last(req); + + dev->open_queue_depth--; + list_del(&ireq->queue); + blk_start_queue(q); + + spin_unlock_irqrestore(q->queue_lock, flags); + return -1; + } /* - * Lets see what is cooking. We stuffed the - * request in the context. + * Lets see what is cooking. We stuffed the + * request in the context. */ - - ireq=&i2ob_queues[c->unit]->request_queue[m[3]]; - st=m[4]>>24; - if(st!=0) - { + st = readl(&msg->body[0]) >> 24; + + if (st != 0) { int err; - char *bsa_errors[] = - { - "Success", - "Media Error", + char *bsa_errors[] = { + "Success", + "Media Error", "Failure communicating to device", "Device Failure", "Device is not ready", @@ -540,61 +523,62 @@ static void i2o_block_reply(struct i2o_h "Device has reset", "Volume has changed, waiting for acknowledgement" }; - - err = m[4]&0xFFFF; - + + err = readl(&msg->body[0]) & 0xffff; + /* - * Device not ready means two things. One is that the - * the thing went offline (but not a removal media) + * Device not ready means two things. One is that the + * the thing went offline (but not a removal media) * - * The second is that you have a SuperTrak 100 and the - * firmware got constipated. Unlike standard i2o card - * setups the supertrak returns an error rather than - * blocking for the timeout in these cases. + * The second is that you have a SuperTrak 100 and the + * firmware got constipated. Unlike standard i2o card + * setups the supertrak returns an error rather than + * blocking for the timeout in these cases. * - * Don't stick a supertrak100 into cache aggressive modes + * Don't stick a supertrak100 into cache aggressive modes */ - - - printk(KERN_ERR "\n/dev/%s error: %s", dev->i2odev->dev_name, - bsa_errors[m[4]&0XFFFF]); - if(m[4]&0x00FF0000) - printk(" - DDM attempted %d retries", (m[4]>>16)&0x00FF ); + + printk(KERN_ERR "\n/dev/%s error: %s", dev->gd->disk_name, + bsa_errors[readl(&msg->body[0]) & 0xffff]); + if (readl(&msg->body[0]) & 0x00ff0000) + printk(" - DDM attempted %d retries", + (readl(&msg->body[0]) >> 16) & 0x00ff); printk(".\n"); - ireq->req->errors++; - } - else - ireq->req->errors = 0; + req->errors++; + } else + req->errors = 0; - /* - * Dequeue the request. We use irqsave locks as one day we - * may be running polled controllers from a BH... - */ - - i2ob_free_sglist(dev, ireq); - spin_lock_irqsave(dev->req_queue->queue_lock, flags); - i2ob_unhook_request(ireq, c->unit); - i2ob_end_request(ireq->req); - i2ob_queues[c->unit]->queue_depth --; - - /* - * We may be able to do more I/O - */ - - i2ob_request(dev->gd->queue); - spin_unlock_irqrestore(dev->req_queue->queue_lock, flags); -} + if (!end_that_request_chunk(req, !req->errors, readl(&msg->body[1]))) { + add_disk_randomness(req->rq_disk); + spin_lock_irqsave(q->queue_lock, flags); -/* - * Event handler. Needs to be a separate thread b/c we may have - * to do things like scan a partition table, or query parameters - * which cannot be done from an interrupt or from a bottom half. - */ -static int i2ob_evt(void *dummy) + end_that_request_last(req); + + dev->open_queue_depth--; + list_del(&ireq->queue); + blk_start_queue(q); + + spin_unlock_irqrestore(q->queue_lock, flags); + + i2o_block_sglist_free(ireq); + i2o_block_request_free(ireq); + } else + printk(KERN_ERR "still remaining chunks\n"); + + return 1; +}; + +static void i2o_block_event(struct i2o_event *evt) +{ + printk(KERN_INFO "block-osm: event received\n"); +}; + +#if 0 +static int i2o_block_event(void *dummy) { unsigned int evt; unsigned long flags; - struct i2ob_device *dev; + struct i2o_block_device *dev; int unit; //The only event that has data is the SCSI_SMART event. struct i2o_reply { @@ -604,24 +588,22 @@ static int i2ob_evt(void *dummy) u8 ASCQ; u16 pad; u8 data[16]; - } *evt_local; + } *evt_local; daemonize("i2oblock"); allow_signal(SIGKILL); evt_running = 1; - while(1) - { - if(down_interruptible(&i2ob_evt_sem)) - { + while (1) { + if (down_interruptible(&i2ob_evt_sem)) { evt_running = 0; printk("exiting..."); break; } /* - * Keep another CPU/interrupt from overwriting the + * Keep another CPU/interrupt from overwriting the * message while we're reading it * * We stuffed the unit in the TxContext and grab the event mask @@ -634,20 +616,19 @@ static int i2ob_evt(void *dummy) unit = le32_to_cpu(evt_local->header[3]); evt = le32_to_cpu(evt_local->evt_indicator); - dev = &i2ob_dev[unit]; - switch(evt) - { + dev = &i2o_blk_dev[unit]; + switch (evt) { /* * New volume loaded on same TID, so we just re-install. * The TID/controller don't change as it is the same * I2O device. It's just new media that we have to * rescan. */ - case I2O_EVT_IND_BSA_VOLUME_LOAD: + case I2O_EVT_IND_BSA_VOLUME_LOAD: { - i2ob_install_device(dev->i2odev->controller, - dev->i2odev, unit); - add_disk(dev->gd); + i2ob_install_device(dev->i2o_device->iop, + dev->i2o_device, unit); + add_disk(dev->gendisk); break; } @@ -657,144 +638,108 @@ static int i2ob_evt(void *dummy) * have media, so we don't want to clear the controller or * device pointer. */ - case I2O_EVT_IND_BSA_VOLUME_UNLOAD: + case I2O_EVT_IND_BSA_VOLUME_UNLOAD: { - struct gendisk *p = dev->gd; - blk_queue_max_sectors(dev->gd->queue, 0); + struct gendisk *p = dev->gendisk; + blk_queue_max_sectors(dev->gendisk->queue, 0); del_gendisk(p); put_disk(p); - dev->gd = NULL; + dev->gendisk = NULL; dev->media_change_flag = 1; break; } - case I2O_EVT_IND_BSA_VOLUME_UNLOAD_REQ: - printk(KERN_WARNING "%s: Attempt to eject locked media\n", - dev->i2odev->dev_name); - break; + case I2O_EVT_IND_BSA_VOLUME_UNLOAD_REQ: + printk(KERN_WARNING + "%s: Attempt to eject locked media\n", + dev->i2o_device->dev_name); + break; /* * The capacity has changed and we are going to be - * updating the max_sectors and other information + * updating the max_sectors and other information * about this disk. We try a revalidate first. If * the block device is in use, we don't want to * do that as there may be I/Os bound for the disk - * at the moment. In that case we read the size + * at the moment. In that case we read the size * from the device and update the information ourselves * and the user can later force a partition table * update through an ioctl. */ - case I2O_EVT_IND_BSA_CAPACITY_CHANGE: + case I2O_EVT_IND_BSA_CAPACITY_CHANGE: { u64 size; - if(i2ob_query_device(dev, 0x0004, 0, &size, 8) !=0 ) - i2ob_query_device(dev, 0x0000, 4, &size, 8); - - spin_lock_irqsave(dev->req_queue->queue_lock, flags); - set_capacity(dev->gd, size>>9); - spin_unlock_irqrestore(dev->req_queue->queue_lock, flags); + if (i2ob_query_device(dev, 0x0004, 0, &size, 8) + != 0) + i2ob_query_device(dev, 0x0000, 4, &size, + 8); + + spin_lock_irqsave(dev->req_queue->queue_lock, + flags); + set_capacity(dev->gendisk, size >> 9); + spin_unlock_irqrestore(dev->req_queue-> + queue_lock, flags); break; } - /* + /* * We got a SCSI SMART event, we just log the relevant * information and let the user decide what they want * to do with the information. */ - case I2O_EVT_IND_BSA_SCSI_SMART: + case I2O_EVT_IND_BSA_SCSI_SMART: { char buf[16]; - printk(KERN_INFO "I2O Block: %s received a SCSI SMART Event\n",dev->i2odev->dev_name); - evt_local->data[16]='\0'; - sprintf(buf,"%s",&evt_local->data[0]); - printk(KERN_INFO " Disk Serial#:%s\n",buf); - printk(KERN_INFO " ASC 0x%02x \n",evt_local->ASC); - printk(KERN_INFO " ASCQ 0x%02x \n",evt_local->ASCQ); + printk(KERN_INFO + "I2O Block: %s received a SCSI SMART Event\n", + dev->i2o_device->dev_name); + evt_local->data[16] = '\0'; + sprintf(buf, "%s", &evt_local->data[0]); + printk(KERN_INFO " Disk Serial#:%s\n", + buf); + printk(KERN_INFO " ASC 0x%02x \n", + evt_local->ASC); + printk(KERN_INFO " ASCQ 0x%02x \n", + evt_local->ASCQ); break; } - + /* - * Non event + * Non event */ - - case 0: - break; - + + case 0: + break; + /* * An event we didn't ask for. Call the card manufacturer * and tell them to fix their firmware :) */ - - case 0x20: - /* - * If a promise card reports 0x20 event then the brown stuff - * hit the fan big time. The card seems to recover but loses - * the pending writes. Deeply ungood except for testing fsck - */ - if(dev->i2odev->controller->promise) - panic("I2O controller firmware failed. Reboot and force a filesystem check.\n"); - default: - printk(KERN_INFO "%s: Received event 0x%X we didn't register for\n" - KERN_INFO " Blame the I2O card manufacturer 8)\n", - dev->i2odev->dev_name, evt); - break; - } - }; - - complete_and_exit(&i2ob_thread_dead,0); - return 0; -} - -/* - * The I2O block driver is listed as one of those that pulls the - * front entry off the queue before processing it. This is important - * to remember here. If we drop the io lock then CURRENT will change - * on us. We must unlink CURRENT in this routine before we return, if - * we use it. - */ - -static void i2ob_request(request_queue_t *q) -{ - struct request *req; - struct i2ob_request *ireq; - struct i2ob_device *dev; - u32 m; - - while ((req = elv_next_request(q)) != NULL) { - dev = req->rq_disk->private_data; - - /* - * Queue depths probably belong with some kind of - * generic IOP commit control. Certainly it's not right - * its global! - */ - if(i2ob_queues[dev->unit]->queue_depth >= dev->depth) - break; - - /* Get a message */ - m = i2ob_get(dev); - if(m==0xFFFFFFFF) - { - if(i2ob_queues[dev->unit]->queue_depth == 0) - printk(KERN_ERR "i2o_block: message queue and request queue empty!!\n"); + case 0x20: + /* + * If a promise card reports 0x20 event then the brown stuff + * hit the fan big time. The card seems to recover but loses + * the pending writes. Deeply ungood except for testing fsck + */ + if (dev->i2o_device->iop->promise) + panic + ("I2O controller firmware failed. Reboot and force a filesystem check.\n"); + default: + printk(KERN_INFO + "%s: Received event 0x%X we didn't register for\n" + KERN_INFO + " Blame the I2O card manufacturer 8)\n", + dev->i2o_device->dev_name, evt); break; } - /* - * Everything ok, so pull from kernel queue onto our queue - */ - req->errors = 0; - blkdev_dequeue_request(req); - - ireq = i2ob_queues[dev->unit]->i2ob_qhead; - i2ob_queues[dev->unit]->i2ob_qhead = ireq->next; - ireq->req = req; + }; - i2ob_send(m, dev, ireq, dev->index); - } + complete_and_exit(&i2ob_thread_dead, 0); + return 0; } - +#endif /* * SCSI-CAM for ioctl geometry mapping @@ -803,8 +748,8 @@ static void i2ob_request(request_queue_t * * LBA -> CHS mapping table taken from: * - * "Incorporating the I2O Architecture into BIOS for Intel Architecture - * Platforms" + * "Incorporating the I2O Architecture into BIOS for Intel Architecture + * Platforms" * * This is an I2O document that is only available to I2O members, * not developers. @@ -825,865 +770,647 @@ static void i2ob_request(request_queue_t #define BLOCK_SIZE_42G 8806400 #define BLOCK_SIZE_84G 17612800 -static void i2o_block_biosparam( - unsigned long capacity, - unsigned short *cyls, - unsigned char *hds, - unsigned char *secs) -{ - unsigned long heads, sectors, cylinders; +static void i2o_block_biosparam(unsigned long capacity, unsigned short *cyls, + unsigned char *hds, unsigned char *secs) +{ + unsigned long heads, sectors, cylinders; - sectors = 63L; /* Maximize sectors per track */ - if(capacity <= BLOCK_SIZE_528M) + sectors = 63L; /* Maximize sectors per track */ + if (capacity <= BLOCK_SIZE_528M) heads = 16; - else if(capacity <= BLOCK_SIZE_1G) + else if (capacity <= BLOCK_SIZE_1G) heads = 32; - else if(capacity <= BLOCK_SIZE_21G) + else if (capacity <= BLOCK_SIZE_21G) heads = 64; - else if(capacity <= BLOCK_SIZE_42G) + else if (capacity <= BLOCK_SIZE_42G) heads = 128; else heads = 255; cylinders = (unsigned long)capacity / (heads * sectors); - *cyls = (unsigned short) cylinders; /* Stuff return values */ - *secs = (unsigned char) sectors; - *hds = (unsigned char) heads; + *cyls = (unsigned short)cylinders; /* Stuff return values */ + *secs = (unsigned char)sectors; + *hds = (unsigned char)heads; } -/* - * Issue device specific ioctl calls. +/** + * i2o_block_open - Open the block device + * + * Power up the device, mount and lock the media. This function is called, + * if the block device is opened for access. + * + * Returns 0 on success or negative error code on failure. */ - -static int i2ob_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int i2o_block_open(struct inode *inode, struct file *file) { - struct gendisk *disk = inode->i_bdev->bd_disk; - struct i2ob_device *dev = disk->private_data; - void __user *argp = (void __user *)arg; + struct i2o_block_device *dev = inode->i_bdev->bd_disk->private_data; - /* Anyone capable of this syscall can do *real bad* things */ + if (!dev->i2o_dev) + return -ENODEV; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - switch (cmd) { - case HDIO_GETGEO: - { - struct hd_geometry g; - i2o_block_biosparam(get_capacity(disk), - &g.cylinders, &g.heads, &g.sectors); - g.start = get_start_sect(inode->i_bdev); - return copy_to_user(argp, &g, sizeof(g))?-EFAULT:0; - } - - case BLKI2OGRSTRAT: - return put_user(dev->rcache, (int __user *)argp); - case BLKI2OGWSTRAT: - return put_user(dev->wcache, (int __user *)argp); - case BLKI2OSRSTRAT: - if(arg<0||arg>CACHE_SMARTFETCH) - return -EINVAL; - dev->rcache = arg; - break; - case BLKI2OSWSTRAT: - if(arg!=0 && (argCACHE_SMARTBACK)) - return -EINVAL; - dev->wcache = arg; - break; - } - return -ENOTTY; -} + if (dev->power > 0x1f) + i2o_block_device_power(dev, 0x02); -/* - * Close the block device down - */ - -static int i2ob_release(struct inode *inode, struct file *file) -{ - struct gendisk *disk = inode->i_bdev->bd_disk; - struct i2ob_device *dev = disk->private_data; + i2o_block_device_mount(dev->i2o_dev, -1); - /* - * This is to deail with the case of an application - * opening a device and then the device dissapears while - * it's in use, and then the application tries to release - * it. ex: Unmounting a deleted RAID volume at reboot. + i2o_block_device_lock(dev->i2o_dev, -1); + + pr_debug("Ready.\n"); + + return 0; +}; + +/** + * i2o_block_release - Release the I2O block device + * + * Unlock and unmount the media, and power down the device. Gets called if + * the block device is closed. + * + * Returns 0 on success or negative error code on failure. + */ +static int i2o_block_release(struct inode *inode, struct file *file) +{ + struct gendisk *disk = inode->i_bdev->bd_disk; + struct i2o_block_device *dev = disk->private_data; + u8 operation; + + /* + * This is to deail with the case of an application + * opening a device and then the device dissapears while + * it's in use, and then the application tries to release + * it. ex: Unmounting a deleted RAID volume at reboot. * If we send messages, it will just cause FAILs since * the TID no longer exists. */ - if(!dev->i2odev) + if (!dev->i2o_dev) return 0; - if (dev->refcnt <= 0) - printk(KERN_ALERT "i2ob_release: refcount(%d) <= 0\n", dev->refcnt); - dev->refcnt--; - if(dev->refcnt==0) - { - /* - * Flush the onboard cache on unmount - */ - u32 msg[5]; - int *query_done = &dev->done_flag; - msg[0] = (FIVE_WORD_MSG_SIZE|SGL_OFFSET_0); - msg[1] = I2O_CMD_BLOCK_CFLUSH<<24|HOST_TID<<12|dev->tid; - msg[2] = i2ob_context|0x40000000; - msg[3] = (u32)query_done; - msg[4] = 60<<16; - DEBUG("Flushing..."); - i2o_post_wait(dev->controller, msg, 20, 60); + i2o_block_device_flush(dev->i2o_dev); - /* - * Unlock the media - */ - msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_BLOCK_MUNLOCK<<24|HOST_TID<<12|dev->tid; - msg[2] = i2ob_context|0x40000000; - msg[3] = (u32)query_done; - msg[4] = -1; - DEBUG("Unlocking..."); - i2o_post_wait(dev->controller, msg, 20, 2); - DEBUG("Unlocked.\n"); - - msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_BLOCK_POWER<<24 | HOST_TID << 12 | dev->tid; - if(dev->flags & (1<<3|1<<4)) /* Removable */ - msg[4] = 0x21 << 24; - else - msg[4] = 0x24 << 24; + i2o_block_device_unlock(dev->i2o_dev, -1); - if(i2o_post_wait(dev->controller, msg, 20, 60)==0) - dev->power = 0x24; + if (dev->flags & (1 << 3 | 1 << 4)) /* Removable */ + operation = 0x21; + else + operation = 0x24; - /* - * Now unclaim the device. - */ + i2o_block_device_power(dev, operation); - if (i2o_release_device(dev->i2odev, &i2o_block_handler)) - printk(KERN_ERR "i2ob_release: controller rejected unclaim.\n"); - - DEBUG("Unclaim\n"); - } return 0; } -/* - * Open the block device. +/** + * i2o_block_ioctl - Issue device specific ioctl calls. + * @cmd: ioctl command + * @arg: arg + * + * Handles ioctl request for the block device. + * + * Return 0 on success or negative error on failure. */ - -static int i2ob_open(struct inode *inode, struct file *file) +static int i2o_block_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) { struct gendisk *disk = inode->i_bdev->bd_disk; - struct i2ob_device *dev = disk->private_data; + struct i2o_block_device *dev = disk->private_data; + void __user *argp = (void __user *)arg; - if(!dev->i2odev) - return -ENODEV; - - if(dev->refcnt++==0) - { - u32 msg[6]; - - DEBUG("Claim "); - if(i2o_claim_device(dev->i2odev, &i2o_block_handler)) - { - dev->refcnt--; - printk(KERN_INFO "I2O Block: Could not open device\n"); - return -EBUSY; - } - DEBUG("Claimed "); - /* - * Power up if needed - */ + /* Anyone capable of this syscall can do *real bad* things */ - if(dev->power > 0x1f) + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + switch (cmd) { + case HDIO_GETGEO: { - msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_BLOCK_POWER<<24 | HOST_TID << 12 | dev->tid; - msg[4] = 0x02 << 24; - if(i2o_post_wait(dev->controller, msg, 20, 60) == 0) - dev->power = 0x02; + struct hd_geometry g; + i2o_block_biosparam(get_capacity(disk), + &g.cylinders, &g.heads, &g.sectors); + g.start = get_start_sect(inode->i_bdev); + return copy_to_user(argp, &g, sizeof(g)) ? -EFAULT : 0; } - /* - * Mount the media if needed. Note that we don't use - * the lock bit. Since we have to issue a lock if it - * refuses a mount (quite possible) then we might as - * well just send two messages out. - */ - msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_BLOCK_MMOUNT<<24|HOST_TID<<12|dev->tid; - msg[4] = -1; - msg[5] = 0; - DEBUG("Mount "); - i2o_post_wait(dev->controller, msg, 24, 2); - - /* - * Lock the media - */ - msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_BLOCK_MLOCK<<24|HOST_TID<<12|dev->tid; - msg[4] = -1; - DEBUG("Lock "); - i2o_post_wait(dev->controller, msg, 20, 2); - DEBUG("Ready.\n"); - } - return 0; -} + case BLKI2OGRSTRAT: + return put_user(dev->rcache, (int __user *)arg); + case BLKI2OGWSTRAT: + return put_user(dev->wcache, (int __user *)arg); + case BLKI2OSRSTRAT: + if (arg < 0 || arg > CACHE_SMARTFETCH) + return -EINVAL; + dev->rcache = arg; + break; + case BLKI2OSWSTRAT: + if (arg != 0 + && (arg < CACHE_WRITETHROUGH || arg > CACHE_SMARTBACK)) + return -EINVAL; + dev->wcache = arg; + break; + } + return -ENOTTY; +}; -/* - * Issue a device query +/** + * i2o_block_media_changed - Have we seen a media change? + * @disk: gendisk which should be verified + * + * Verifies if the media has changed. + * + * Returns 1 if the media was changed or 0 otherwise. */ - -static int i2ob_query_device(struct i2ob_device *dev, int table, - int field, void *buf, int buflen) +static int i2o_block_media_changed(struct gendisk *disk) { - return i2o_query_scalar(dev->controller, dev->tid, - table, field, buf, buflen); -} + struct i2o_block_device *p = disk->private_data; + if (p->media_change_flag) { + p->media_change_flag = 0; + return 1; + } + return 0; +} -/* - * Install the I2O block device we found. +/** + * i2o_block_transfer - Transfer a request to/from the I2O controller + * @req: the request which should be transfered + * + * This function converts the request into a I2O message. The necessary + * DMA buffers are allocated and after everything is setup post the message + * to the I2O controller. No cleanup is done by this function. It is done + * on the interrupt side when the reply arrives. + * + * Return 0 on success or negative error code on failure. */ - -static int i2ob_install_device(struct i2o_controller *c, struct i2o_device *d, int unit) +static int i2o_block_transfer(struct request *req) { - u64 size; - u32 blocksize; - u8 type; - u16 power; - u32 flags, status; - struct i2ob_device *dev=&i2ob_dev[unit]; - struct gendisk *disk; - request_queue_t *q; - int segments; + struct i2o_block_device *dev = req->rq_disk->private_data; + struct i2o_controller *c = dev->i2o_dev->iop; + int tid = dev->i2o_dev->lct_data.tid; + struct i2o_message *msg; + void *mptr; + struct i2o_block_request *ireq = req->special; + struct scatterlist *sg; + int sgnum; + int i; + u32 m; + u32 tcntxt; + u32 sg_flags; + int rc; + m = i2o_msg_get(c, &msg); + if (m == I2O_QUEUE_EMPTY) { + rc = -EBUSY; + goto exit; + } - /* - * For logging purposes... - */ - printk(KERN_INFO "i2ob: Installing tid %d device at unit %d\n", - d->lct_data.tid, unit); + tcntxt = i2o_cntxt_list_add(c, req); + if (!tcntxt) { + rc = -ENOMEM; + goto nop_msg; + } - /* - * If this is the first I2O block device found on this IOP, - * we need to initialize all the queue data structures - * before any I/O can be performed. If it fails, this - * device is useless. - */ - if(!i2ob_queues[c->unit]) { - if(i2ob_init_iop(c->unit)) - return 1; + if ((sgnum = i2o_block_sglist_alloc(ireq)) <= 0) { + rc = -ENOMEM; + goto context_remove; } - q = i2ob_queues[c->unit]->req_queue; + /* Build the message based on the request. */ + writel(i2o_block_driver.context, &msg->u.s.icntxt); + writel(tcntxt, &msg->u.s.tcntxt); + writel(req->nr_sectors << 9, &msg->body[1]); - /* - * This will save one level of lookup/indirection in critical - * code so that we can directly get the queue ptr from the - * device instead of having to go the IOP data structure. - */ - dev->req_queue = q; + writel((((u64) req->sector) << 9) & 0xffffffff, &msg->body[2]); + writel(req->sector >> 23, &msg->body[3]); - /* - * Allocate a gendisk structure and initialize it - */ - disk = alloc_disk(16); - if (!disk) - return 1; + mptr = &msg->body[4]; - dev->gd = disk; - /* initialize gendik structure */ - disk->major = MAJOR_NR; - disk->first_minor = unit<<4; - disk->queue = q; - disk->fops = &i2ob_fops; - sprintf(disk->disk_name, "i2o/hd%c", 'a' + unit); - disk->private_data = dev; + sg = ireq->sg_table; - /* - * Ask for the current media data. If that isn't supported - * then we ask for the device capacity data - */ - if(i2ob_query_device(dev, 0x0004, 1, &blocksize, 4) != 0 - || i2ob_query_device(dev, 0x0004, 0, &size, 8) !=0 ) - { - i2ob_query_device(dev, 0x0000, 3, &blocksize, 4); - i2ob_query_device(dev, 0x0000, 4, &size, 8); + if (rq_data_dir(req) == READ) { + writel(I2O_CMD_BLOCK_READ << 24 | HOST_TID << 12 | tid, + &msg->u.head[1]); + sg_flags = 0x10000000; + switch (dev->rcache) { + case CACHE_NULL: + writel(0, &msg->body[0]); + break; + case CACHE_PREFETCH: + writel(0x201F0008, &msg->body[0]); + break; + case CACHE_SMARTFETCH: + if (req->nr_sectors > 16) + writel(0x201F0008, &msg->body[0]); + else + writel(0x001F0000, &msg->body[0]); + break; + } + } else { + writel(I2O_CMD_BLOCK_WRITE << 24 | HOST_TID << 12 | tid, + &msg->u.head[1]); + sg_flags = 0x14000000; + switch (dev->wcache) { + case CACHE_NULL: + writel(0, &msg->body[0]); + break; + case CACHE_WRITETHROUGH: + writel(0x001F0008, &msg->body[0]); + break; + case CACHE_WRITEBACK: + writel(0x001F0010, &msg->body[0]); + break; + case CACHE_SMARTBACK: + if (req->nr_sectors > 16) + writel(0x001F0004, &msg->body[0]); + else + writel(0x001F0010, &msg->body[0]); + break; + case CACHE_SMARTTHROUGH: + if (req->nr_sectors > 16) + writel(0x001F0004, &msg->body[0]); + else + writel(0x001F0010, &msg->body[0]); + } } - - if(i2ob_query_device(dev, 0x0000, 2, &power, 2)!=0) - power = 0; - i2ob_query_device(dev, 0x0000, 5, &flags, 4); - i2ob_query_device(dev, 0x0000, 6, &status, 4); - set_capacity(disk, size>>9); - /* - * Max number of Scatter-Gather Elements - */ + for (i = sgnum; i > 0; i--) { + if (i == 1) + sg_flags |= 0x80000000; + writel(sg_flags | sg_dma_len(sg), mptr); + writel(sg_dma_address(sg), mptr + 4); + mptr += 8; + sg++; + } + + writel(I2O_MESSAGE_SIZE + (((unsigned long)mptr - + (unsigned long)&msg->u.head[0]) >> 2) | SGL_OFFSET_8, + &msg->u.head[0]); - dev->power = power; /* Save power state in device proper */ - dev->flags = flags; + i2o_msg_post(c, m); - segments = (d->controller->status_block->inbound_frame_size - 7) / 2; + list_add_tail(&ireq->queue, &dev->open_queue); + dev->open_queue_depth++; - if(segments > 16) - segments = 16; - - dev->power = power; /* Save power state */ - dev->flags = flags; /* Keep the type info */ - - blk_queue_max_sectors(q, 96); /* 256 might be nicer but many controllers - explode on 65536 or higher */ - blk_queue_max_phys_segments(q, segments); - blk_queue_max_hw_segments(q, segments); - - dev->rcache = CACHE_SMARTFETCH; - dev->wcache = CACHE_WRITETHROUGH; - - if(d->controller->battery == 0) - dev->wcache = CACHE_WRITETHROUGH; - - if(d->controller->promise) - dev->wcache = CACHE_WRITETHROUGH; - - if(d->controller->short_req) - { - blk_queue_max_sectors(q, 8); - blk_queue_max_phys_segments(q, 8); - blk_queue_max_hw_segments(q, 8); - } - - strcpy(d->dev_name, disk->disk_name); - strcpy(disk->devfs_name, disk->disk_name); - - printk(KERN_INFO "%s: Max segments %d, queue depth %d, byte limit %d.\n", - d->dev_name, dev->max_segments, dev->depth, dev->max_sectors<<9); - - i2ob_query_device(dev, 0x0000, 0, &type, 1); - - printk(KERN_INFO "%s: ", d->dev_name); - switch(type) - { - case 0: printk("Disk Storage");break; - case 4: printk("WORM");break; - case 5: printk("CD-ROM");break; - case 7: printk("Optical device");break; - default: - printk("Type %d", type); - } - if(status&(1<<10)) - printk("(RAID)"); + return 0; - if((flags^status)&(1<<4|1<<3)) /* Missing media or device */ - { - printk(KERN_INFO " Not loaded.\n"); - /* Device missing ? */ - if((flags^status)&(1<<4)) - return 1; - } - else - { - printk(": %dMB, %d byte sectors", - (int)(size>>20), blocksize); - } - if(status&(1<<0)) - { - u32 cachesize; - i2ob_query_device(dev, 0x0003, 0, &cachesize, 4); - cachesize>>=10; - if(cachesize>4095) - printk(", %dMb cache", cachesize>>10); - else - printk(", %dKb cache", cachesize); - } - printk(".\n"); - printk(KERN_INFO "%s: Maximum sectors/read set to %d.\n", - d->dev_name, dev->max_sectors); + context_remove: + i2o_cntxt_list_remove(c, req); - /* - * Register for the events we're interested in and that the - * device actually supports. - */ + nop_msg: + i2o_msg_nop(c, m); - i2o_event_register(c, d->lct_data.tid, i2ob_context, unit, - (I2OB_EVENT_MASK & d->lct_data.event_capabilities)); - return 0; -} + exit: + return rc; +}; -/* - * Initialize IOP specific queue structures. This is called - * once for each IOP that has a block device sitting behind it. +/** + * i2o_block_request_fn - request queue handling function + * q: request queue from which the request could be fetched + * + * Takes the next request from the queue, transfers it and if no error + * occurs dequeue it from the queue. On arrival of the reply the message + * will be processed further. If an error occurs requeue the request. */ -static int i2ob_init_iop(unsigned int unit) +static void i2o_block_request_fn(struct request_queue *q) { - int i; + struct request *req; - i2ob_queues[unit] = (struct i2ob_iop_queue *) kmalloc(sizeof(struct i2ob_iop_queue), GFP_ATOMIC); - if(!i2ob_queues[unit]) - { - printk(KERN_WARNING "Could not allocate request queue for I2O block device!\n"); - return -1; - } + while (!blk_queue_plugged(q)) { + req = elv_next_request(q); + if (!req) + break; - for(i = 0; i< MAX_I2OB_DEPTH; i++) - { - i2ob_queues[unit]->request_queue[i].next = &i2ob_queues[unit]->request_queue[i+1]; - i2ob_queues[unit]->request_queue[i].num = i; - } - - /* Queue is MAX_I2OB + 1... */ - i2ob_queues[unit]->request_queue[i].next = NULL; - i2ob_queues[unit]->i2ob_qhead = &i2ob_queues[unit]->request_queue[0]; - i2ob_queues[unit]->queue_depth = 0; - - i2ob_queues[unit]->lock = SPIN_LOCK_UNLOCKED; - i2ob_queues[unit]->req_queue = blk_init_queue(i2ob_request, &i2ob_queues[unit]->lock); - if (!i2ob_queues[unit]->req_queue) { - kfree(i2ob_queues[unit]); - return -1; - } + if (blk_fs_request(req)) { + struct i2o_block_delayed_request *dreq; + struct i2o_block_request *ireq = req->special; + unsigned int queue_depth; + + queue_depth = ireq->i2o_blk_dev->open_queue_depth; + + if (queue_depth < I2O_BLOCK_MAX_OPEN_REQUESTS) + if (!i2o_block_transfer(req)) { + blkdev_dequeue_request(req); + continue; + } - i2ob_queues[unit]->req_queue->queuedata = &i2ob_queues[unit]; + if (queue_depth) + break; - return 0; -} + /* stop the queue and retry later */ + dreq = kmalloc(sizeof(*dreq), GFP_ATOMIC); + if (!dreq) + continue; -/* - * Probe the I2O subsytem for block class devices + dreq->queue = q; + INIT_WORK(&dreq->work, i2o_block_delayed_request_fn, + dreq); + + printk(KERN_INFO "block-osm: transfer error\n"); + if (!queue_delayed_work(i2o_block_driver.event_queue, + &dreq->work, + I2O_BLOCK_RETRY_TIME)) + kfree(dreq); + else { + blk_stop_queue(q); + break; + } + } else + end_request(req, 0); + } +}; + +/* I2O Block device operations definition */ +static struct block_device_operations i2o_block_fops = { + .owner = THIS_MODULE, + .open = i2o_block_open, + .release = i2o_block_release, + .ioctl = i2o_block_ioctl, + .media_changed = i2o_block_media_changed +}; + +/** + * i2o_block_device_alloc - Allocate memory for a I2O Block device + * + * Allocate memory for the i2o_block_device struct, gendisk and request + * queue and initialize them as far as no additional information is needed. + * + * Returns a pointer to the allocated I2O Block device on succes or a + * negative error code on failure. */ -static void i2ob_scan(int bios) +static struct i2o_block_device *i2o_block_device_alloc(void) { - int i; - int warned = 0; + struct i2o_block_device *dev; + struct gendisk *gd; + struct request_queue *queue; + int rc; - struct i2o_device *d, *b=NULL; - struct i2o_controller *c; - - for(i=0; i< MAX_I2O_CONTROLLERS; i++) - { - c=i2o_find_controller(i); - - if(c==NULL) - continue; + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + printk(KERN_ERR "block-osm: Insufficient memory to allocate " + "I2O Block disk.\n"); + rc = -ENOMEM; + goto exit; + } + memset(dev, 0, sizeof(*dev)); - /* - * The device list connected to the I2O Controller is doubly linked - * Here we traverse the end of the list , and start claiming devices - * from that end. This assures that within an I2O controller atleast - * the newly created volumes get claimed after the older ones, thus - * mapping to same major/minor (and hence device file name) after - * every reboot. - * The exception being: - * 1. If there was a TID reuse. - * 2. There was more than one I2O controller. - */ + INIT_LIST_HEAD(&dev->open_queue); + spin_lock_init(&dev->lock); + dev->rcache = CACHE_PREFETCH; + dev->wcache = CACHE_WRITEBACK; - if(!bios) - { - for (d=c->devices;d!=NULL;d=d->next) - if(d->next == NULL) - b = d; - } - else - b = c->devices; + /* allocate a gendisk with 16 partitions */ + gd = alloc_disk(16); + if (!gd) { + printk(KERN_ERR "block-osm: Insufficient memory to allocate " + "gendisk.\n"); + rc = -ENOMEM; + goto cleanup_dev; + } - while(b != NULL) - { - d=b; - if(bios) - b = b->next; - else - b = b->prev; + /* initialize the request queue */ + queue = blk_init_queue(i2o_block_request_fn, &dev->lock); + if (!queue) { + printk(KERN_ERR "block-osm: Insufficient memory to allocate " + "request queue.\n"); + rc = -ENOMEM; + goto cleanup_queue; + } - if(d->lct_data.class_id!=I2O_CLASS_RANDOM_BLOCK_STORAGE) - continue; + blk_queue_prep_rq(queue, i2o_block_prep_req_fn); - if(d->lct_data.user_tid != 0xFFF) - continue; + gd->major = I2O_MAJOR; + gd->queue = queue; + gd->fops = &i2o_block_fops; + gd->private_data = dev; - if(bios) - { - if(d->lct_data.bios_info != 0x80) - continue; - printk(KERN_INFO "Claiming as Boot device: Controller %d, TID %d\n", c->unit, d->lct_data.tid); - } - else - { - if(d->lct_data.bios_info == 0x80) - continue; /*Already claimed on pass 1 */ - } + dev->gd = gd; - if(scan_unitiop; + struct gendisk *gd; + struct request_queue *queue; + static int unit = 0; + int rc; + u64 size; + u32 blocksize; + u16 power; + u32 flags, status; + int segments; -/* - * New device notification handler. Called whenever a new - * I2O block storage device is added to the system. - * - * Should we spin lock around this to keep multiple devs from - * getting updated at the same time? - * - */ -void i2ob_new_device(struct i2o_controller *c, struct i2o_device *d) -{ - struct i2ob_device *dev; - int unit = 0; - - printk(KERN_INFO "i2o_block: New device detected\n"); - printk(KERN_INFO " Controller %d Tid %d\n",c->unit, d->lct_data.tid); - - /* Check for available space */ - if(i2ob_dev_count>=MAX_I2OB) - { - printk(KERN_ERR "i2o_block: No more devices allowed!\n"); - return; - } - for(unit = 0; unit < MAX_I2OB; unit ++) - { - if(!i2ob_dev[unit].i2odev) - break; + /* skip devices which are used by IOP */ + if (i2o_dev->lct_data.user_tid != 0xfff) { + pr_debug("skipping used device %03x\n", i2o_dev->lct_data.tid); + return -ENODEV; } - if(i2o_claim_device(d, &i2o_block_handler)) - { - printk(KERN_INFO "i2o_block: Unable to claim device. Installation aborted\n"); - return; - } - - dev = &i2ob_dev[unit]; - dev->i2odev = d; - dev->controller = c; - dev->tid = d->lct_data.tid; - dev->unit = c->unit; - - if(i2ob_install_device(c,d,unit)) { - i2o_release_device(d, &i2o_block_handler); - printk(KERN_ERR "i2o_block: Could not install new device\n"); - } - else - { - i2o_release_device(d, &i2o_block_handler); - add_disk(dev->gd); - i2ob_dev_count++; - i2o_device_notify_on(d, &i2o_block_handler); + printk(KERN_INFO "block-osm: New device detected (TID: %03x)\n", + i2o_dev->lct_data.tid); + + if (i2o_device_claim(i2o_dev)) { + printk(KERN_WARNING "block-osm: Unable to claim device. " + "Installation aborted\n"); + rc = -EFAULT; + goto exit; } - return; -} + i2o_blk_dev = i2o_block_device_alloc(); + if (IS_ERR(i2o_blk_dev)) { + printk(KERN_ERR "block-osm: could not alloc a new I2O block" + "device"); + rc = PTR_ERR(i2o_blk_dev); + goto claim_release; + } -/* - * Deleted device notification handler. Called when a device we - * are talking to has been deleted by the user or some other - * mysterious fource outside the kernel. - */ -void i2ob_del_device(struct i2o_controller *c, struct i2o_device *d) -{ - int unit = 0; - unsigned long flags; - struct i2ob_device *dev; + i2o_blk_dev->i2o_dev = i2o_dev; + dev_set_drvdata(dev, i2o_blk_dev); - for(unit = 0; unit < MAX_I2OB; unit ++) - { - dev = &i2ob_dev[unit]; - if(dev->i2odev == d) - { - printk(KERN_INFO " /dev/%s: Controller %d Tid %d\n", - d->dev_name, c->unit, d->lct_data.tid); - break; - } - } + /* setup gendisk */ + gd = i2o_blk_dev->gd; + gd->first_minor = unit << 4; + sprintf(gd->disk_name, "i2o/hd%c", 'a' + unit); + sprintf(gd->devfs_name, "i2o/hd%c", 'a' + unit); + gd->driverfs_dev = &i2o_dev->device; + + /* setup request queue */ + queue = gd->queue; + queue->queuedata = i2o_blk_dev; + + blk_queue_max_phys_segments(queue, I2O_MAX_SEGMENTS); + blk_queue_max_sectors(queue, I2O_MAX_SECTORS); - printk(KERN_INFO "I2O Block Device Deleted\n"); + if (c->short_req) + segments = 8; + else { + i2o_status_block *sb; - if(unit >= MAX_I2OB) - { - printk(KERN_ERR "i2ob_del_device called, but not in dev table!\n"); - return; + sb = c->status_block.virt; + + segments = (sb->inbound_frame_size - + sizeof(struct i2o_message) / 4 - 4) / 2; } - spin_lock_irqsave(dev->req_queue->queue_lock, flags); + blk_queue_max_hw_segments(queue, segments); + + pr_debug("max sectors: %d\n", I2O_MAX_SECTORS); + pr_debug("phys segments: %d\n", I2O_MAX_SEGMENTS); + pr_debug("hw segments: %d\n", segments); /* - * Need to do this...we somtimes get two events from the IRTOS - * in a row and that causes lots of problems. + * Ask for the current media data. If that isn't supported + * then we ask for the device capacity data */ - i2o_device_notify_off(d, &i2o_block_handler); + if (i2o_parm_field_get(i2o_dev, 0x0004, 1, &blocksize, 4) != 0 + || i2o_parm_field_get(i2o_dev, 0x0004, 0, &size, 8) != 0) { + i2o_parm_field_get(i2o_dev, 0x0000, 3, &blocksize, 4); + i2o_parm_field_get(i2o_dev, 0x0000, 4, &size, 8); + } + pr_debug("blocksize: %d\n", blocksize); - /* - * This will force errors when i2ob_get_queue() is called - * by the kenrel. - */ - if(dev->gd) { - struct gendisk *gd = dev->gd; - gd->queue = NULL; - del_gendisk(gd); - put_disk(gd); - dev->gd = NULL; - } - spin_unlock_irqrestore(dev->req_queue->queue_lock, flags); - dev->req_queue = NULL; - dev->i2odev = NULL; - dev->refcnt = 0; - dev->tid = 0; - - /* - * Do we need this? - * The media didn't really change...the device is just gone - */ - dev->media_change_flag = 1; + if (i2o_parm_field_get(i2o_dev, 0x0000, 2, &power, 2)) + power = 0; + i2o_parm_field_get(i2o_dev, 0x0000, 5, &flags, 4); + i2o_parm_field_get(i2o_dev, 0x0000, 6, &status, 4); - i2ob_dev_count--; -} + set_capacity(gd, size >> 9); -/* - * Have we seen a media change ? - */ -static int i2ob_media_change(struct gendisk *disk) -{ - struct i2ob_device *p = disk->private_data; - if(p->media_change_flag) - { - p->media_change_flag=0; - return 1; - } - return 0; -} + i2o_event_register(i2o_dev, &i2o_block_driver, 0, 0xffffffff); -static int i2ob_revalidate(struct gendisk *disk) -{ - struct i2ob_device *p = disk->private_data; - return i2ob_install_device(p->controller, p->i2odev, p->index); -} + add_disk(gd); -/* - * Reboot notifier. This is called by i2o_core when the system - * shuts down. - */ -static void i2ob_reboot_event(void) -{ - int i; - - for(i=0;irefcnt!=0) - { - /* - * Flush the onboard cache - */ - u32 msg[5]; - int *query_done = &dev->done_flag; - msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_BLOCK_CFLUSH<<24|HOST_TID<<12|dev->tid; - msg[2] = i2ob_context|0x40000000; - msg[3] = (u32)query_done; - msg[4] = 60<<16; - - DEBUG("Flushing..."); - i2o_post_wait(dev->controller, msg, 20, 60); + unit++; - DEBUG("Unlocking..."); - /* - * Unlock the media - */ - msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_BLOCK_MUNLOCK<<24|HOST_TID<<12|dev->tid; - msg[2] = i2ob_context|0x40000000; - msg[3] = (u32)query_done; - msg[4] = -1; - i2o_post_wait(dev->controller, msg, 20, 2); - - DEBUG("Unlocked.\n"); - } - } -} + return 0; -static struct block_device_operations i2ob_fops = -{ - .owner = THIS_MODULE, - .open = i2ob_open, - .release = i2ob_release, - .ioctl = i2ob_ioctl, - .media_changed = i2ob_media_change, - .revalidate_disk= i2ob_revalidate, + claim_release: + i2o_device_claim_release(i2o_dev); + + exit: + return rc; }; -/* - * And here should be modules and kernel interface - * (Just smiley confuses emacs :-) - */ +/* Block OSM driver struct */ +static struct i2o_driver i2o_block_driver = { + .name = "block-osm", + .event = i2o_block_event, + .reply = i2o_block_reply, + .classes = i2o_block_class_id, + .driver = { + .probe = i2o_block_probe, + .remove = i2o_block_remove, + }, +}; -static int i2o_block_init(void) +/** + * i2o_block_init - Block OSM initialization function + * + * Allocate the slab and mempool for request structs, registers i2o_block + * block device and finally register the Block OSM in the I2O core. + * + * Returns 0 on success or negative error code on failure. + */ +static int __init i2o_block_init(void) { - int i; + int rc; + int size; printk(KERN_INFO "I2O Block Storage OSM v0.9\n"); printk(KERN_INFO " (c) Copyright 1999-2001 Red Hat Software.\n"); - - /* - * Register the block device interfaces - */ - if (register_blkdev(MAJOR_NR, "i2o_block")) - return -EIO; + /* Allocate request mempool and slab */ + size = sizeof(struct i2o_block_request); + i2o_blk_req_pool.slab = kmem_cache_create("i2o_block_req", size, 0, + SLAB_HWCACHE_ALIGN, NULL, + NULL); + if (!i2o_blk_req_pool.slab) { + printk(KERN_ERR "block-osm: can't init request slab\n"); + rc = -ENOMEM; + goto exit; + } + + i2o_blk_req_pool.pool = mempool_create(I2O_REQ_MEMPOOL_SIZE, + mempool_alloc_slab, + mempool_free_slab, + i2o_blk_req_pool.slab); + if (!i2o_blk_req_pool.pool) { + printk(KERN_ERR "block-osm: can't init request mempool\n"); + rc = -ENOMEM; + goto free_slab; + } + + /* Register the block device interfaces */ + rc = register_blkdev(I2O_MAJOR, "i2o_block"); + if (rc) { + printk(KERN_ERR "block-osm: unable to register block device\n"); + goto free_mempool; + } #ifdef MODULE - printk(KERN_INFO "i2o_block: registered device at major %d\n", MAJOR_NR); + printk(KERN_INFO "block-osm: registered device at major %d\n", + I2O_MAJOR); #endif - /* - * Set up the queue - */ - for(i = 0; i < MAX_I2O_CONTROLLERS; i++) - i2ob_queues[i] = NULL; - - /* - * Now fill in the boiler plate - */ - - for (i = 0; i < MAX_I2OB; i++) { - struct i2ob_device *dev = &i2ob_dev[i]; - dev->index = i; - dev->refcnt = 0; - dev->flags = 0; - dev->controller = NULL; - dev->i2odev = NULL; - dev->tid = 0; - dev->head = NULL; - dev->tail = NULL; - dev->depth = MAX_I2OB_DEPTH; - dev->max_sectors = 2; - dev->gd = NULL; - } - - /* - * Register the OSM handler as we will need this to probe for - * drives, geometry and other goodies. - */ - - if(i2o_install_handler(&i2o_block_handler)<0) - { - unregister_blkdev(MAJOR_NR, "i2o_block"); - printk(KERN_ERR "i2o_block: unable to register OSM.\n"); - return -EINVAL; - } - i2ob_context = i2o_block_handler.context; - - /* - * Initialize event handling thread - */ - init_MUTEX_LOCKED(&i2ob_evt_sem); - evt_pid = kernel_thread(i2ob_evt, NULL, CLONE_SIGHAND); - if(evt_pid < 0) - { - printk(KERN_ERR "i2o_block: Could not initialize event thread. Aborting\n"); - i2o_remove_handler(&i2o_block_handler); - return 0; + /* Register Block OSM into I2O core */ + rc = i2o_driver_register(&i2o_block_driver); + if (rc) { + printk(KERN_ERR "block-osm: Could not register Block driver\n"); + goto unregister_blkdev; } - i2ob_probe(); - return 0; - unregister_blkdev(MAJOR_NR, "i2o_block"); - return -ENOMEM; -} + unregister_blkdev: + unregister_blkdev(I2O_MAJOR, "i2o_block"); + free_mempool: + mempool_destroy(i2o_blk_req_pool.pool); -static void i2o_block_exit(void) -{ - int i; - - if(evt_running) { - printk(KERN_INFO "Killing I2O block threads..."); - i = kill_proc(evt_pid, SIGKILL, 1); - if(!i) { - printk("waiting...\n"); - } - /* Be sure it died */ - wait_for_completion(&i2ob_thread_dead); - printk("done.\n"); - } + free_slab: + kmem_cache_destroy(i2o_blk_req_pool.slab); - /* - * Unregister for updates from any devices..otherwise we still - * get them and the core jumps to random memory :O - */ - if(i2ob_dev_count) { - struct i2o_device *d; - for(i = 0; i < MAX_I2OB; i++) - if((d = i2ob_dev[i].i2odev)) - i2ob_del_device(d->controller, d); - } - - /* - * We may get further callbacks for ourself. The i2o_core - * code handles this case reasonably sanely. The problem here - * is we shouldn't get them .. but a couple of cards feel - * obliged to tell us stuff we don't care about. - * - * This isnt ideal at all but will do for now. - */ - - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ); - - /* - * Flush the OSM - */ + exit: + return rc; +}; - i2o_remove_handler(&i2o_block_handler); +/** + * i2o_block_exit - Block OSM exit function + * + * Unregisters Block OSM from I2O core, unregisters i2o_block block device + * and frees the mempool and slab. + */ +static void __exit i2o_block_exit(void) +{ + /* Unregister I2O Block OSM from I2O core */ + i2o_driver_unregister(&i2o_block_driver); - /* - * Return the block device - */ - if (unregister_blkdev(MAJOR_NR, "i2o_block") != 0) - printk("i2o_block: cleanup_module failed\n"); + /* Unregister block device */ + unregister_blkdev(I2O_MAJOR, "i2o_block"); - /* - * release request queue - */ - for (i = 0; i < MAX_I2O_CONTROLLERS; i ++) - if(i2ob_queues[i]) { - blk_cleanup_queue(i2ob_queues[i]->req_queue); - kfree(i2ob_queues[i]); - } -} + /* Free request mempool and slab */ + mempool_destroy(i2o_blk_req_pool.pool); + kmem_cache_destroy(i2o_blk_req_pool.slab); +}; MODULE_AUTHOR("Red Hat"); MODULE_DESCRIPTION("I2O Block Device OSM"); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/message/i2o/i2o_block.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,99 @@ +/* + * Block OSM structures/API + * + * Copyright (C) 1999-2002 Red Hat Software + * + * Written by Alan Cox, Building Number Three Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * For the purpose of avoiding doubt the preferred form of the work + * for making modifications shall be a standards compliant form such + * gzipped tar and not one requiring a proprietary or patent encumbered + * tool to unpack. + * + * Fixes/additions: + * Steve Ralston: + * Multiple device handling error fixes, + * Added a queue depth. + * Alan Cox: + * FC920 has an rmw bug. Dont or in the end marker. + * Removed queue walk, fixed for 64bitness. + * Rewrote much of the code over time + * Added indirect block lists + * Handle 64K limits on many controllers + * Don't use indirects on the Promise (breaks) + * Heavily chop down the queue depths + * Deepak Saxena: + * Independent queues per IOP + * Support for dynamic device creation/deletion + * Code cleanup + * Support for larger I/Os through merge* functions + * (taken from DAC960 driver) + * Boji T Kannanthanam: + * Set the I2O Block devices to be detected in increasing + * order of TIDs during boot. + * Search and set the I2O block device that we boot off + * from as the first device to be claimed (as /dev/i2o/hda) + * Properly attach/detach I2O gendisk structure from the + * system gendisk list. The I2O block devices now appear in + * /proc/partitions. + * Markus Lidel : + * Minor bugfixes for 2.6. + */ + +#ifndef I2O_BLOCK_OSM_H +#define I2O_BLOCK_OSM_H + +#define I2O_BLOCK_RETRY_TIME HZ/4 +#define I2O_BLOCK_MAX_OPEN_REQUESTS 50 + +/* I2O Block OSM mempool struct */ +struct i2o_block_mempool { + kmem_cache_t *slab; + mempool_t *pool; +}; + +/* I2O Block device descriptor */ +struct i2o_block_device { + struct i2o_device *i2o_dev; /* pointer to I2O device */ + struct gendisk *gd; + spinlock_t lock; /* queue lock */ + struct list_head open_queue; /* list of transfered, but unfinished + requests */ + unsigned int open_queue_depth; /* number of requests in the queue */ + + int rcache; /* read cache flags */ + int wcache; /* write cache flags */ + int flags; + int power; /* power state */ + int media_change_flag; /* media changed flag */ +}; + +/* I2O Block device request */ +struct i2o_block_request +{ + struct list_head queue; + struct request *req; /* corresponding request */ + struct i2o_block_device *i2o_blk_dev; /* I2O block device */ + int sg_dma_direction; /* direction of DMA buffer read/write */ + int sg_nents; /* number of SG elements */ + struct scatterlist sg_table[I2O_MAX_SEGMENTS]; /* SG table */ +}; + +/* I2O Block device delayed request */ +struct i2o_block_delayed_request +{ + struct work_struct work; + struct request_queue *queue; +}; + +#endif --- linux-2.6.9-rc1/drivers/message/i2o/i2o_config.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/drivers/message/i2o/i2o_config.c 2004-09-02 20:51:52.000000000 -0700 @@ -2,7 +2,7 @@ * I2O Configuration Interface Driver * * (C) Copyright 1999-2002 Red Hat - * + * * Written by Alan Cox, Building Number Three Ltd * * Fixes/additions: @@ -41,63 +41,53 @@ #include #include #include +#include +#include +#include #include #include -static int i2o_cfg_context = -1; -static void *page_buf; +extern int i2o_parm_issue(struct i2o_device *, int, void *, int, void *, int); + static spinlock_t i2o_config_lock = SPIN_LOCK_UNLOCKED; struct wait_queue *i2o_wait_queue; #define MODINC(x,y) ((x) = ((x) + 1) % (y)) struct sg_simple_element { - u32 flag_count; + u32 flag_count; u32 addr_bus; }; -struct i2o_cfg_info -{ - struct file* fp; +struct i2o_cfg_info { + struct file *fp; struct fasync_struct *fasync; struct i2o_evt_info event_q[I2O_EVT_Q_LEN]; - u16 q_in; // Queue head index - u16 q_out; // Queue tail index - u16 q_len; // Queue length - u16 q_lost; // Number of lost events - u32 q_id; // Event queue ID...used as tx_context - struct i2o_cfg_info *next; + u16 q_in; // Queue head index + u16 q_out; // Queue tail index + u16 q_len; // Queue length + u16 q_lost; // Number of lost events + ulong q_id; // Event queue ID...used as tx_context + struct i2o_cfg_info *next; }; static struct i2o_cfg_info *open_files = NULL; -static int i2o_cfg_info_id = 0; - -static int ioctl_getiops(unsigned long); -static int ioctl_gethrt(unsigned long); -static int ioctl_getlct(unsigned long); -static int ioctl_parms(unsigned long, unsigned int); -static int ioctl_html(unsigned long); -static int ioctl_swdl(unsigned long); -static int ioctl_swul(unsigned long); -static int ioctl_swdel(unsigned long); -static int ioctl_validate(unsigned long); -static int ioctl_evt_reg(unsigned long, struct file *); -static int ioctl_evt_get(unsigned long, struct file *); -static int ioctl_passthru(unsigned long); -static int cfg_fasync(int, struct file*, int); +static ulong i2o_cfg_info_id = 0; +#if 0 /* * This is the callback for any message we have posted. The message itself * will be returned to the message pool when we return from the IRQ * * This runs in irq context so be short and sweet. */ -static void i2o_cfg_reply(struct i2o_handler *h, struct i2o_controller *c, struct i2o_message *m) +static void i2o_cfg_reply(struct i2o_handler *h, struct i2o_controller *c, + struct i2o_message *m) { - u32 *msg = (u32 *)m; + u32 *msg = (u32 *) m; if (msg[0] & MSG_FAIL) { - u32 *preserved_msg = (u32*)(c->msg_virt + msg[7]); + u32 *preserved_msg = (u32 *) (c->msg_virt + msg[7]); printk(KERN_ERR "i2o_config: IOP failed to process the msg.\n"); @@ -109,26 +99,25 @@ static void i2o_cfg_reply(struct i2o_han i2o_post_message(c, msg[7]); } - if (msg[4] >> 24) // ReqStatus != SUCCESS - i2o_report_status(KERN_INFO,"i2o_config", msg); + if (msg[4] >> 24) // ReqStatus != SUCCESS + i2o_report_status(KERN_INFO, "i2o_config", msg); - if(m->function == I2O_CMD_UTIL_EVT_REGISTER) - { + if (m->function == I2O_CMD_UTIL_EVT_REGISTER) { struct i2o_cfg_info *inf; - for(inf = open_files; inf; inf = inf->next) - if(inf->q_id == msg[3]) + for (inf = open_files; inf; inf = inf->next) + if (inf->q_id == i2o_cntxt_list_get(c, msg[3])) break; // // If this is the case, it means that we're getting // events for a file descriptor that's been close()'d // w/o the user unregistering for events first. - // The code currently assumes that the user will + // The code currently assumes that the user will // take care of unregistering for events before closing // a file. - // - // TODO: + // + // TODO: // Should we track event registartion and deregister // for events when a file is close()'d so this doesn't // happen? That would get rid of the search through @@ -137,8 +126,8 @@ static void i2o_cfg_reply(struct i2o_han // it would mean having all sorts of tables to track // what each file is registered for...I think the // current method is simpler. - DS - // - if(!inf) + // + if (!inf) return; inf->event_q[inf->q_in].id.iop = c->unit; @@ -149,278 +138,167 @@ static void i2o_cfg_reply(struct i2o_han // Data size = msg size - reply header // inf->event_q[inf->q_in].data_size = (m->size - 5) * 4; - if(inf->event_q[inf->q_in].data_size) - memcpy(inf->event_q[inf->q_in].evt_data, - (unsigned char *)(msg + 5), - inf->event_q[inf->q_in].data_size); + if (inf->event_q[inf->q_in].data_size) + memcpy(inf->event_q[inf->q_in].evt_data, + (unsigned char *)(msg + 5), + inf->event_q[inf->q_in].data_size); spin_lock(&i2o_config_lock); MODINC(inf->q_in, I2O_EVT_Q_LEN); - if(inf->q_len == I2O_EVT_Q_LEN) - { + if (inf->q_len == I2O_EVT_Q_LEN) { MODINC(inf->q_out, I2O_EVT_Q_LEN); inf->q_lost++; - } - else - { + } else { // Keep I2OEVTGET on another CPU from touching this inf->q_len++; } spin_unlock(&i2o_config_lock); - -// printk(KERN_INFO "File %p w/id %d has %d events\n", -// inf->fp, inf->q_id, inf->q_len); +// printk(KERN_INFO "File %p w/id %d has %d events\n", +// inf->fp, inf->q_id, inf->q_len); kill_fasync(&inf->fasync, SIGIO, POLL_IN); } return; } +#endif /* * Each of these describes an i2o message handler. They are * multiplexed by the i2o_core code */ - -struct i2o_handler cfg_handler= -{ - i2o_cfg_reply, - NULL, - NULL, - NULL, - "Configuration", - 0, - 0xffffffff // All classes -}; - -static ssize_t cfg_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) -{ - printk(KERN_INFO "i2o_config write not yet supported\n"); - return 0; -} - - -static ssize_t cfg_read(struct file *file, char __user *buf, size_t count, loff_t *ptr) -{ - return 0; -} +struct i2o_driver i2o_config_driver = { + .name = "Config-OSM" +}; -/* - * IOCTL Handler - */ -static int cfg_ioctl(struct inode *inode, struct file *fp, unsigned int cmd, - unsigned long arg) +static int i2o_cfg_getiops(unsigned long arg) { - int ret; - - switch(cmd) - { - case I2OGETIOPS: - ret = ioctl_getiops(arg); - break; - - case I2OHRTGET: - ret = ioctl_gethrt(arg); - break; - - case I2OLCTGET: - ret = ioctl_getlct(arg); - break; - - case I2OPARMSET: - ret = ioctl_parms(arg, I2OPARMSET); - break; - - case I2OPARMGET: - ret = ioctl_parms(arg, I2OPARMGET); - break; - - case I2OSWDL: - ret = ioctl_swdl(arg); - break; - - case I2OSWUL: - ret = ioctl_swul(arg); - break; - - case I2OSWDEL: - ret = ioctl_swdel(arg); - break; - - case I2OVALIDATE: - ret = ioctl_validate(arg); - break; - - case I2OHTML: - ret = ioctl_html(arg); - break; - - case I2OEVTREG: - ret = ioctl_evt_reg(arg, fp); - break; + struct i2o_controller *c; + u8 __user *user_iop_table = (void __user *)arg; + u8 tmp[MAX_I2O_CONTROLLERS]; - case I2OEVTGET: - ret = ioctl_evt_get(arg, fp); - break; + memset(tmp, 0, MAX_I2O_CONTROLLERS); - case I2OPASSTHRU: - ret = ioctl_passthru(arg); - break; + if (!access_ok(VERIFY_WRITE, user_iop_table, MAX_I2O_CONTROLLERS)) + return -EFAULT; - default: - ret = -EINVAL; - } + list_for_each_entry(c, &i2o_controllers, list) + tmp[c->unit] = 1; - return ret; -} + __copy_to_user(user_iop_table, tmp, MAX_I2O_CONTROLLERS); -int ioctl_getiops(unsigned long arg) -{ - u8 __user *user_iop_table = (void __user *)arg; - struct i2o_controller *c = NULL; - int i; - u8 foo[MAX_I2O_CONTROLLERS]; - - if(!access_ok(VERIFY_WRITE, user_iop_table, MAX_I2O_CONTROLLERS)) - return -EFAULT; - - for(i = 0; i < MAX_I2O_CONTROLLERS; i++) - { - c = i2o_find_controller(i); - if(c) - { - foo[i] = 1; - if(pci_set_dma_mask(c->pdev, 0xffffffff)) - { - printk(KERN_WARNING "i2o_config : No suitable DMA available on controller %d\n", i); - i2o_unlock_controller(c); - continue; - } - - i2o_unlock_controller(c); - } - else - { - foo[i] = 0; - } - } - - __copy_to_user(user_iop_table, foo, MAX_I2O_CONTROLLERS); return 0; -} +}; -int ioctl_gethrt(unsigned long arg) +static int i2o_cfg_gethrt(unsigned long arg) { struct i2o_controller *c; - struct i2o_cmd_hrtlct __user *cmd = (void __user *)arg; + struct i2o_cmd_hrtlct __user *cmd = (struct i2o_cmd_hrtlct __user *)arg; struct i2o_cmd_hrtlct kcmd; i2o_hrt *hrt; int len; u32 reslen; int ret = 0; - if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_hrtlct))) + if (copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_hrtlct))) return -EFAULT; - if(get_user(reslen, kcmd.reslen) < 0) + if (get_user(reslen, kcmd.reslen) < 0) return -EFAULT; - if(kcmd.resbuf == NULL) + if (kcmd.resbuf == NULL) return -EFAULT; - c = i2o_find_controller(kcmd.iop); - if(!c) + c = i2o_find_iop(kcmd.iop); + if (!c) return -ENXIO; - - hrt = (i2o_hrt *)c->hrt; - i2o_unlock_controller(c); + hrt = (i2o_hrt *) c->hrt.virt; len = 8 + ((hrt->entry_len * hrt->num_entries) << 2); - + /* We did a get user...so assuming mem is ok...is this bad? */ put_user(len, kcmd.reslen); - if(len > reslen) - ret = -ENOBUFS; - if(copy_to_user(kcmd.resbuf, (void*)hrt, len)) + if (len > reslen) + ret = -ENOBUFS; + if (copy_to_user(kcmd.resbuf, (void *)hrt, len)) ret = -EFAULT; return ret; -} +}; -int ioctl_getlct(unsigned long arg) +static int i2o_cfg_getlct(unsigned long arg) { struct i2o_controller *c; - struct i2o_cmd_hrtlct __user *cmd = (void __user *)arg; + struct i2o_cmd_hrtlct __user *cmd = (struct i2o_cmd_hrtlct __user *)arg; struct i2o_cmd_hrtlct kcmd; i2o_lct *lct; int len; int ret = 0; u32 reslen; - if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_hrtlct))) + if (copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_hrtlct))) return -EFAULT; - if(get_user(reslen, kcmd.reslen) < 0) + if (get_user(reslen, kcmd.reslen) < 0) return -EFAULT; - if(kcmd.resbuf == NULL) + if (kcmd.resbuf == NULL) return -EFAULT; - c = i2o_find_controller(kcmd.iop); - if(!c) + c = i2o_find_iop(kcmd.iop); + if (!c) return -ENXIO; - lct = (i2o_lct *)c->lct; - i2o_unlock_controller(c); + lct = (i2o_lct *) c->lct; len = (unsigned int)lct->table_size << 2; put_user(len, kcmd.reslen); - if(len > reslen) - ret = -ENOBUFS; - else if(copy_to_user(kcmd.resbuf, (void*)lct, len)) + if (len > reslen) + ret = -ENOBUFS; + else if (copy_to_user(kcmd.resbuf, lct, len)) ret = -EFAULT; return ret; -} +}; -static int ioctl_parms(unsigned long arg, unsigned int type) +static int i2o_cfg_parms(unsigned long arg, unsigned int type) { int ret = 0; struct i2o_controller *c; - struct i2o_cmd_psetget __user *cmd = (void __user *)arg; + struct i2o_device *dev; + struct i2o_cmd_psetget __user *cmd = + (struct i2o_cmd_psetget __user *)arg; struct i2o_cmd_psetget kcmd; u32 reslen; u8 *ops; u8 *res; - int len; + int len = 0; - u32 i2o_cmd = (type == I2OPARMGET ? - I2O_CMD_UTIL_PARAMS_GET : - I2O_CMD_UTIL_PARAMS_SET); + u32 i2o_cmd = (type == I2OPARMGET ? + I2O_CMD_UTIL_PARAMS_GET : I2O_CMD_UTIL_PARAMS_SET); - if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_psetget))) + if (copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_psetget))) return -EFAULT; - if(get_user(reslen, kcmd.reslen)) + if (get_user(reslen, kcmd.reslen)) return -EFAULT; - c = i2o_find_controller(kcmd.iop); - if(!c) + c = i2o_find_iop(kcmd.iop); + if (!c) return -ENXIO; - ops = (u8*)kmalloc(kcmd.oplen, GFP_KERNEL); - if(!ops) - { - i2o_unlock_controller(c); + dev = i2o_iop_find_device(c, kcmd.tid); + if (!dev) + return -ENXIO; + + ops = (u8 *) kmalloc(kcmd.oplen, GFP_KERNEL); + if (!ops) return -ENOMEM; - } - if(copy_from_user(ops, kcmd.opbuf, kcmd.oplen)) - { - i2o_unlock_controller(c); + if (copy_from_user(ops, kcmd.opbuf, kcmd.oplen)) { kfree(ops); return -EFAULT; } @@ -429,404 +307,309 @@ static int ioctl_parms(unsigned long arg * It's possible to have a _very_ large table * and that the user asks for all of it at once... */ - res = (u8*)kmalloc(65536, GFP_KERNEL); - if(!res) - { - i2o_unlock_controller(c); + res = (u8 *) kmalloc(65536, GFP_KERNEL); + if (!res) { kfree(ops); return -ENOMEM; } - len = i2o_issue_params(i2o_cmd, c, kcmd.tid, - ops, kcmd.oplen, res, 65536); - i2o_unlock_controller(c); + len = i2o_parm_issue(dev, i2o_cmd, ops, kcmd.oplen, res, 65536); kfree(ops); - + if (len < 0) { kfree(res); return -EAGAIN; } put_user(len, kcmd.reslen); - if(len > reslen) + if (len > reslen) ret = -ENOBUFS; - else if(copy_to_user(kcmd.resbuf, res, len)) + else if (copy_to_user(kcmd.resbuf, res, len)) ret = -EFAULT; kfree(res); return ret; -} +}; -int ioctl_html(unsigned long arg) +static int i2o_cfg_swdl(unsigned long arg) { - struct i2o_html __user *cmd = (void __user *)arg; - struct i2o_html kcmd; + struct i2o_sw_xfer kxfer; + struct i2o_sw_xfer __user *pxfer = (struct i2o_sw_xfer __user *)arg; + unsigned char maxfrag = 0, curfrag = 1; + struct i2o_dma buffer; + struct i2o_message *msg; + u32 m; + unsigned int status = 0, swlen = 0, fragsize = 8192; struct i2o_controller *c; - u8 *res = NULL; - void *query = NULL; - dma_addr_t query_phys, res_phys; - int ret = 0; - int token; - u32 len; - u32 reslen; - u32 msg[MSG_FRAME_SIZE]; - if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_html))) - { - printk(KERN_INFO "i2o_config: can't copy html cmd\n"); + if (copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer))) return -EFAULT; - } - if(get_user(reslen, kcmd.reslen) < 0) - { - printk(KERN_INFO "i2o_config: can't copy html reslen\n"); + if (get_user(swlen, kxfer.swlen) < 0) return -EFAULT; - } - if(!kcmd.resbuf) - { - printk(KERN_INFO "i2o_config: NULL html buffer\n"); + if (get_user(maxfrag, kxfer.maxfrag) < 0) return -EFAULT; - } - c = i2o_find_controller(kcmd.iop); - if(!c) + if (get_user(curfrag, kxfer.curfrag) < 0) + return -EFAULT; + + if (curfrag == maxfrag) + fragsize = swlen - (maxfrag - 1) * 8192; + + if (!kxfer.buf || !access_ok(VERIFY_READ, kxfer.buf, fragsize)) + return -EFAULT; + + c = i2o_find_iop(kxfer.iop); + if (!c) return -ENXIO; - if(kcmd.qlen) /* Check for post data */ - { - query = pci_alloc_consistent(c->pdev, kcmd.qlen, &query_phys); - if(!query) - { - i2o_unlock_controller(c); - return -ENOMEM; - } - if(copy_from_user(query, kcmd.qbuf, kcmd.qlen)) - { - i2o_unlock_controller(c); - printk(KERN_INFO "i2o_config: could not get query\n"); - pci_free_consistent(c->pdev, kcmd.qlen, query, query_phys); - return -EFAULT; - } - } + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -EBUSY; - res = pci_alloc_consistent(c->pdev, 65536, &res_phys); - if(!res) - { - i2o_unlock_controller(c); - pci_free_consistent(c->pdev, kcmd.qlen, query, query_phys); + if (i2o_dma_alloc(&c->pdev->dev, &buffer, fragsize, GFP_KERNEL)) { + i2o_msg_nop(c, m); return -ENOMEM; } - msg[1] = (I2O_CMD_UTIL_CONFIG_DIALOG << 24)|HOST_TID<<12|kcmd.tid; - msg[2] = i2o_cfg_context; - msg[3] = 0; - msg[4] = kcmd.page; - msg[5] = 0xD0000000|65536; - msg[6] = res_phys; - if(!kcmd.qlen) /* Check for post data */ - msg[0] = SEVEN_WORD_MSG_SIZE|SGL_OFFSET_5; - else - { - msg[0] = NINE_WORD_MSG_SIZE|SGL_OFFSET_5; - msg[5] = 0x50000000|65536; - msg[7] = 0xD4000000|(kcmd.qlen); - msg[8] = query_phys; - } - /* - Wait for a considerable time till the Controller - does its job before timing out. The controller might - take more time to process this request if there are - many devices connected to it. - */ - token = i2o_post_wait_mem(c, msg, 9*4, 400, query, res, query_phys, res_phys, kcmd.qlen, 65536); - if(token < 0) - { - printk(KERN_DEBUG "token = %#10x\n", token); - i2o_unlock_controller(c); - - if(token != -ETIMEDOUT) - { - pci_free_consistent(c->pdev, 65536, res, res_phys); - if(kcmd.qlen) - pci_free_consistent(c->pdev, kcmd.qlen, query, query_phys); - } - return token; - } - i2o_unlock_controller(c); + __copy_from_user(buffer.virt, kxfer.buf, fragsize); - len = strnlen(res, 65536); - put_user(len, kcmd.reslen); - if(len > reslen) - ret = -ENOMEM; - if(copy_to_user(kcmd.resbuf, res, len)) - ret = -EFAULT; + writel(NINE_WORD_MSG_SIZE | SGL_OFFSET_7, &msg->u.head[0]); + writel(I2O_CMD_SW_DOWNLOAD << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + writel(i2o_config_driver.context, &msg->u.head[2]); + writel(0, &msg->u.head[3]); + writel((((u32) kxfer.flags) << 24) | (((u32) kxfer.sw_type) << 16) | + (((u32) maxfrag) << 8) | (((u32) curfrag)), &msg->body[0]); + writel(swlen, &msg->body[1]); + writel(kxfer.sw_id, &msg->body[2]); + writel(0xD0000000 | fragsize, &msg->body[3]); + writel(buffer.phys, &msg->body[4]); - pci_free_consistent(c->pdev, 65536, res, res_phys); - if(kcmd.qlen) - pci_free_consistent(c->pdev, kcmd.qlen, query, query_phys); +// printk("i2o_config: swdl frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize); + status = i2o_msg_post_wait_mem(c, m, 60, &buffer); - return ret; -} - -int ioctl_swdl(unsigned long arg) + if (status != -ETIMEDOUT) + i2o_dma_free(&c->pdev->dev, &buffer); + + if (status != I2O_POST_WAIT_OK) { + // it fails if you try and send frags out of order + // and for some yet unknown reasons too + printk(KERN_INFO + "i2o_config: swdl failed, DetailedStatus = %d\n", + status); + return status; + } + + return 0; +}; + +static int i2o_cfg_swul(unsigned long arg) { struct i2o_sw_xfer kxfer; - struct i2o_sw_xfer __user *pxfer = (void __user *)arg; + struct i2o_sw_xfer __user *pxfer = (struct i2o_sw_xfer __user *)arg; unsigned char maxfrag = 0, curfrag = 1; - unsigned char *buffer; - u32 msg[9]; + struct i2o_dma buffer; + struct i2o_message *msg; + u32 m; unsigned int status = 0, swlen = 0, fragsize = 8192; struct i2o_controller *c; - dma_addr_t buffer_phys; - if(copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer))) + if (copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer))) return -EFAULT; - if(get_user(swlen, kxfer.swlen) < 0) + if (get_user(swlen, kxfer.swlen) < 0) return -EFAULT; - if(get_user(maxfrag, kxfer.maxfrag) < 0) + if (get_user(maxfrag, kxfer.maxfrag) < 0) return -EFAULT; - if(get_user(curfrag, kxfer.curfrag) < 0) + if (get_user(curfrag, kxfer.curfrag) < 0) return -EFAULT; - if(curfrag==maxfrag) fragsize = swlen-(maxfrag-1)*8192; + if (curfrag == maxfrag) + fragsize = swlen - (maxfrag - 1) * 8192; - if(!kxfer.buf || !access_ok(VERIFY_READ, kxfer.buf, fragsize)) + if (!kxfer.buf || !access_ok(VERIFY_WRITE, kxfer.buf, fragsize)) return -EFAULT; - - c = i2o_find_controller(kxfer.iop); - if(!c) + + c = i2o_find_iop(kxfer.iop); + if (!c) return -ENXIO; - buffer=pci_alloc_consistent(c->pdev, fragsize, &buffer_phys); - if (buffer==NULL) - { - i2o_unlock_controller(c); + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -EBUSY; + + if (i2o_dma_alloc(&c->pdev->dev, &buffer, fragsize, GFP_KERNEL)) { + i2o_msg_nop(c, m); return -ENOMEM; } - __copy_from_user(buffer, kxfer.buf, fragsize); - msg[0]= NINE_WORD_MSG_SIZE | SGL_OFFSET_7; - msg[1]= I2O_CMD_SW_DOWNLOAD<<24 | HOST_TID<<12 | ADAPTER_TID; - msg[2]= (u32)cfg_handler.context; - msg[3]= 0; - msg[4]= (((u32)kxfer.flags)<<24) | (((u32)kxfer.sw_type)<<16) | - (((u32)maxfrag)<<8) | (((u32)curfrag)); - msg[5]= swlen; - msg[6]= kxfer.sw_id; - msg[7]= (0xD0000000 | fragsize); - msg[8]= buffer_phys; - -// printk("i2o_config: swdl frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize); - status = i2o_post_wait_mem(c, msg, sizeof(msg), 60, buffer, NULL, buffer_phys, 0, fragsize, 0); - - i2o_unlock_controller(c); - if(status != -ETIMEDOUT) - pci_free_consistent(c->pdev, fragsize, buffer, buffer_phys); - - if (status != I2O_POST_WAIT_OK) - { - // it fails if you try and send frags out of order - // and for some yet unknown reasons too - printk(KERN_INFO "i2o_config: swdl failed, DetailedStatus = %d\n", status); + writel(NINE_WORD_MSG_SIZE | SGL_OFFSET_7, &msg->u.head[0]); + writel(I2O_CMD_SW_UPLOAD << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + writel(i2o_config_driver.context, &msg->u.head[2]); + writel(0, &msg->u.head[3]); + writel((u32) kxfer.flags << 24 | (u32) kxfer. + sw_type << 16 | (u32) maxfrag << 8 | (u32) curfrag, + &msg->body[0]); + writel(swlen, &msg->body[1]); + writel(kxfer.sw_id, &msg->body[2]); + writel(0xD0000000 | fragsize, &msg->body[3]); + writel(buffer.phys, &msg->body[4]); + +// printk("i2o_config: swul frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize); + status = i2o_msg_post_wait_mem(c, m, 60, &buffer); + + if (status != I2O_POST_WAIT_OK) { + if (status != -ETIMEDOUT) + i2o_dma_free(&c->pdev->dev, &buffer); + + printk(KERN_INFO + "i2o_config: swul failed, DetailedStatus = %d\n", + status); return status; } - return 0; -} + __copy_to_user(kxfer.buf, buffer.virt, fragsize); + i2o_dma_free(&c->pdev->dev, &buffer); -int ioctl_swul(unsigned long arg) -{ - struct i2o_sw_xfer kxfer; - struct i2o_sw_xfer __user *pxfer = (void __user *)arg; - unsigned char maxfrag = 0, curfrag = 1; - unsigned char *buffer; - u32 msg[9]; - unsigned int status = 0, swlen = 0, fragsize = 8192; - struct i2o_controller *c; - dma_addr_t buffer_phys; - - if(copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer))) - return -EFAULT; - - if(get_user(swlen, kxfer.swlen) < 0) - return -EFAULT; - - if(get_user(maxfrag, kxfer.maxfrag) < 0) - return -EFAULT; - - if(get_user(curfrag, kxfer.curfrag) < 0) - return -EFAULT; - - if(curfrag==maxfrag) fragsize = swlen-(maxfrag-1)*8192; - - if(!kxfer.buf || !access_ok(VERIFY_WRITE, kxfer.buf, fragsize)) - return -EFAULT; - - c = i2o_find_controller(kxfer.iop); - if(!c) - return -ENXIO; - - buffer=pci_alloc_consistent(c->pdev, fragsize, &buffer_phys); - if (buffer==NULL) - { - i2o_unlock_controller(c); - return -ENOMEM; - } - - msg[0]= NINE_WORD_MSG_SIZE | SGL_OFFSET_7; - msg[1]= I2O_CMD_SW_UPLOAD<<24 | HOST_TID<<12 | ADAPTER_TID; - msg[2]= (u32)cfg_handler.context; - msg[3]= 0; - msg[4]= (u32)kxfer.flags<<24|(u32)kxfer.sw_type<<16|(u32)maxfrag<<8|(u32)curfrag; - msg[5]= swlen; - msg[6]= kxfer.sw_id; - msg[7]= (0xD0000000 | fragsize); - msg[8]= buffer_phys; - -// printk("i2o_config: swul frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize); - status = i2o_post_wait_mem(c, msg, sizeof(msg), 60, buffer, NULL, buffer_phys, 0, fragsize, 0); - i2o_unlock_controller(c); - - if (status != I2O_POST_WAIT_OK) - { - if(status != -ETIMEDOUT) - pci_free_consistent(c->pdev, fragsize, buffer, buffer_phys); - printk(KERN_INFO "i2o_config: swul failed, DetailedStatus = %d\n", status); - return status; - } - - __copy_to_user(kxfer.buf, buffer, fragsize); - pci_free_consistent(c->pdev, fragsize, buffer, buffer_phys); - return 0; -} +}; -int ioctl_swdel(unsigned long arg) +static int i2o_cfg_swdel(unsigned long arg) { struct i2o_controller *c; struct i2o_sw_xfer kxfer; - struct i2o_sw_xfer __user *pxfer = (void __user *)arg; - u32 msg[7]; + struct i2o_sw_xfer __user *pxfer = (struct i2o_sw_xfer __user *)arg; + struct i2o_message *msg; + u32 m; unsigned int swlen; int token; - + if (copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer))) return -EFAULT; - + if (get_user(swlen, kxfer.swlen) < 0) return -EFAULT; - - c = i2o_find_controller(kxfer.iop); + + c = i2o_find_iop(kxfer.iop); if (!c) return -ENXIO; - msg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0; - msg[1] = I2O_CMD_SW_REMOVE<<24 | HOST_TID<<12 | ADAPTER_TID; - msg[2] = (u32)i2o_cfg_context; - msg[3] = 0; - msg[4] = (u32)kxfer.flags<<24 | (u32)kxfer.sw_type<<16; - msg[5] = swlen; - msg[6] = kxfer.sw_id; - - token = i2o_post_wait(c, msg, sizeof(msg), 10); - i2o_unlock_controller(c); - - if (token != I2O_POST_WAIT_OK) - { - printk(KERN_INFO "i2o_config: swdel failed, DetailedStatus = %d\n", token); + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -EBUSY; + + writel(SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_SW_REMOVE << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + writel(i2o_config_driver.context, &msg->u.head[2]); + writel(0, &msg->u.head[3]); + writel((u32) kxfer.flags << 24 | (u32) kxfer.sw_type << 16, + &msg->body[0]); + writel(swlen, &msg->body[1]); + writel(kxfer.sw_id, &msg->body[2]); + + token = i2o_msg_post_wait(c, m, 10); + + if (token != I2O_POST_WAIT_OK) { + printk(KERN_INFO + "i2o_config: swdel failed, DetailedStatus = %d\n", + token); return -ETIMEDOUT; } - + return 0; -} +}; -int ioctl_validate(unsigned long arg) +static int i2o_cfg_validate(unsigned long arg) { - int token; - int iop = (int)arg; - u32 msg[4]; - struct i2o_controller *c; - - c=i2o_find_controller(iop); - if (!c) - return -ENXIO; - - msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_CONFIG_VALIDATE<<24 | HOST_TID<<12 | iop; - msg[2] = (u32)i2o_cfg_context; - msg[3] = 0; - - token = i2o_post_wait(c, msg, sizeof(msg), 10); - i2o_unlock_controller(c); - - if (token != I2O_POST_WAIT_OK) - { - printk(KERN_INFO "Can't validate configuration, ErrorStatus = %d\n", - token); - return -ETIMEDOUT; - } + int token; + int iop = (int)arg; + struct i2o_message *msg; + u32 m; + struct i2o_controller *c; + + c = i2o_find_iop(iop); + if (!c) + return -ENXIO; + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -EBUSY; - return 0; -} + writel(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_CONFIG_VALIDATE << 24 | HOST_TID << 12 | iop, + &msg->u.head[1]); + writel(i2o_config_driver.context, &msg->u.head[2]); + writel(0, &msg->u.head[3]); + + token = i2o_msg_post_wait(c, m, 10); + + if (token != I2O_POST_WAIT_OK) { + printk(KERN_INFO "Can't validate configuration, ErrorStatus = " + "%d\n", token); + return -ETIMEDOUT; + } -static int ioctl_evt_reg(unsigned long arg, struct file *fp) + return 0; +}; + +static int i2o_cfg_evt_reg(unsigned long arg, struct file *fp) { - u32 msg[5]; - struct i2o_evt_id __user *pdesc = (void __user *)arg; + struct i2o_message *msg; + u32 m; + struct i2o_evt_id __user *pdesc = (struct i2o_evt_id __user *)arg; struct i2o_evt_id kdesc; - struct i2o_controller *iop; + struct i2o_controller *c; struct i2o_device *d; if (copy_from_user(&kdesc, pdesc, sizeof(struct i2o_evt_id))) return -EFAULT; /* IOP exists? */ - iop = i2o_find_controller(kdesc.iop); - if(!iop) + c = i2o_find_iop(kdesc.iop); + if (!c) return -ENXIO; - i2o_unlock_controller(iop); /* Device exists? */ - for(d = iop->devices; d; d = d->next) - if(d->lct_data.tid == kdesc.tid) - break; - - if(!d) + d = i2o_iop_find_device(c, kdesc.tid); + if (!d) return -ENODEV; - msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_UTIL_EVT_REGISTER<<24 | HOST_TID<<12 | kdesc.tid; - msg[2] = (u32)i2o_cfg_context; - msg[3] = (u32)fp->private_data; - msg[4] = kdesc.evt_mask; + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -EBUSY; + + writel(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_UTIL_EVT_REGISTER << 24 | HOST_TID << 12 | kdesc.tid, + &msg->u.head[1]); + writel(i2o_config_driver.context, &msg->u.head[2]); + writel(i2o_cntxt_list_add(c, fp->private_data), &msg->u.head[3]); + writel(kdesc.evt_mask, &msg->body[0]); - i2o_post_this(iop, msg, 20); + i2o_msg_post(c, m); return 0; -} +} -static int ioctl_evt_get(unsigned long arg, struct file *fp) +static int i2o_cfg_evt_get(unsigned long arg, struct file *fp) { - u32 id = (u32)fp->private_data; struct i2o_cfg_info *p = NULL; - struct i2o_evt_get __user *uget = (void __user *)arg; + struct i2o_evt_get __user *uget = (struct i2o_evt_get __user *)arg; struct i2o_evt_get kget; unsigned long flags; - for(p = open_files; p; p = p->next) - if(p->q_id == id) + for (p = open_files; p; p = p->next) + if (p->q_id == (ulong) fp->private_data) break; - if(!p->q_len) - { + if (!p->q_len) return -ENOENT; - return 0; - } memcpy(&kget.info, &p->event_q[p->q_out], sizeof(struct i2o_evt_info)); MODINC(p->q_out, I2O_EVT_Q_LEN); @@ -836,16 +619,236 @@ static int ioctl_evt_get(unsigned long a kget.lost = p->q_lost; spin_unlock_irqrestore(&i2o_config_lock, flags); - if(copy_to_user(uget, &kget, sizeof(struct i2o_evt_get))) + if (copy_to_user(uget, &kget, sizeof(struct i2o_evt_get))) return -EFAULT; return 0; } -static int ioctl_passthru(unsigned long arg) +#if BITS_PER_LONG == 64 +static int i2o_cfg_passthru32(unsigned fd, unsigned cmnd, unsigned long arg, + struct file *file) { - struct i2o_cmd_passthru __user *cmd = (void __user *) arg; + struct i2o_cmd_passthru32 __user *cmd; + struct i2o_controller *c; + u32 *user_msg; + u32 *reply = NULL; + u32 *user_reply = NULL; + u32 size = 0; + u32 reply_size = 0; + u32 rcode = 0; + struct i2o_dma sg_list[SG_TABLESIZE]; + u32 sg_offset = 0; + u32 sg_count = 0; + u32 i = 0; + i2o_status_block *sb; + struct i2o_message *msg; + u32 m; + unsigned int iop; + + cmd = (struct i2o_cmd_passthru32 __user *)arg; + + if (get_user(iop, &cmd->iop) || get_user(i, &cmd->msg)) + return -EFAULT; + + user_msg = compat_ptr(i); + + c = i2o_find_iop(iop); + if (!c) { + pr_debug("controller %d not found\n", iop); + return -ENXIO; + } + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + + sb = c->status_block.virt; + + if (get_user(size, &user_msg[0])) { + printk(KERN_WARNING "unable to get size!\n"); + return -EFAULT; + } + size = size >> 16; + + if (size > sb->inbound_frame_size) { + pr_debug("size of message > inbound_frame_size"); + return -EFAULT; + } + + user_reply = &user_msg[size]; + + size <<= 2; // Convert to bytes + + /* Copy in the user's I2O command */ + if (copy_from_user(msg, user_msg, size)) { + printk(KERN_WARNING "unable to copy user message\n"); + return -EFAULT; + } + i2o_dump_message(msg); + + if (get_user(reply_size, &user_reply[0]) < 0) + return -EFAULT; + + reply_size >>= 16; + reply_size <<= 2; + + reply = kmalloc(reply_size, GFP_KERNEL); + if (!reply) { + printk(KERN_WARNING "%s: Could not allocate reply buffer\n", + c->name); + return -ENOMEM; + } + memset(reply, 0, reply_size); + + sg_offset = (msg->u.head[0] >> 4) & 0x0f; + + writel(i2o_config_driver.context, &msg->u.s.icntxt); + writel(i2o_cntxt_list_add(c, reply), &msg->u.s.tcntxt); + + memset(sg_list, 0, sizeof(sg_list[0]) * SG_TABLESIZE); + if (sg_offset) { + struct sg_simple_element *sg; + + if (sg_offset * 4 >= size) { + rcode = -EFAULT; + goto cleanup; + } + // TODO 64bit fix + sg = (struct sg_simple_element *)((&msg->u.head[0]) + + sg_offset); + sg_count = + (size - sg_offset * 4) / sizeof(struct sg_simple_element); + if (sg_count > SG_TABLESIZE) { + printk(KERN_DEBUG "%s:IOCTL SG List too large (%u)\n", + c->name, sg_count); + kfree(reply); + return -EINVAL; + } + + for (i = 0; i < sg_count; i++) { + int sg_size; + struct i2o_dma *p; + + if (!(sg[i].flag_count & 0x10000000 + /*I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT */ )) { + printk(KERN_DEBUG + "%s:Bad SG element %d - not simple (%x)\n", + c->name, i, sg[i].flag_count); + rcode = -EINVAL; + goto cleanup; + } + sg_size = sg[i].flag_count & 0xffffff; + p = &(sg_list[i]); + /* Allocate memory for the transfer */ + if (i2o_dma_alloc + (&c->pdev->dev, p, sg_size, + PCI_DMA_BIDIRECTIONAL)) { + printk(KERN_DEBUG + "%s: Could not allocate SG buffer - size = %d buffer number %d of %d\n", + c->name, sg_size, i, sg_count); + rcode = -ENOMEM; + goto cleanup; + } + /* Copy in the user's SG buffer if necessary */ + if (sg[i]. + flag_count & 0x04000000 /*I2O_SGL_FLAGS_DIR */ ) { + // TODO 64bit fix + if (copy_from_user + (p->virt, (void *)(u64) sg[i].addr_bus, + sg_size)) { + printk(KERN_DEBUG + "%s: Could not copy SG buf %d FROM user\n", + c->name, i); + rcode = -EFAULT; + goto cleanup; + } + } + //TODO 64bit fix + sg[i].addr_bus = (u32) p->phys; + } + } + + rcode = i2o_msg_post_wait(c, m, 60); + if (rcode) + goto cleanup; + + if (sg_offset) { + u32 msg[128]; + /* Copy back the Scatter Gather buffers back to user space */ + u32 j; + // TODO 64bit fix + struct sg_simple_element *sg; + int sg_size; + printk(KERN_INFO "sg_offset\n"); + + // re-acquire the original message to handle correctly the sg copy operation + memset(&msg, 0, MSG_FRAME_SIZE * 4); + // get user msg size in u32s + if (get_user(size, &user_msg[0])) { + rcode = -EFAULT; + goto cleanup; + } + size = size >> 16; + size *= 4; + /* Copy in the user's I2O command */ + if (copy_from_user(msg, user_msg, size)) { + rcode = -EFAULT; + goto cleanup; + } + sg_count = + (size - sg_offset * 4) / sizeof(struct sg_simple_element); + + // TODO 64bit fix + sg = (struct sg_simple_element *)(msg + sg_offset); + for (j = 0; j < sg_count; j++) { + /* Copy out the SG list to user's buffer if necessary */ + if (! + (sg[j]. + flag_count & 0x4000000 /*I2O_SGL_FLAGS_DIR */ )) { + sg_size = sg[j].flag_count & 0xffffff; + // TODO 64bit fix + if (copy_to_user + ((void __user *)(u64) sg[j].addr_bus, + sg_list[j].virt, sg_size)) { + printk(KERN_WARNING + "%s: Could not copy %p TO user %x\n", + c->name, sg_list[j].virt, + sg[j].addr_bus); + rcode = -EFAULT; + goto cleanup; + } + } + } + } + + /* Copy back the reply to user space */ + if (reply_size) { + // we wrote our own values for context - now restore the user supplied ones + printk(KERN_INFO "reply_size\n"); + if (copy_from_user(reply + 2, user_msg + 2, sizeof(u32) * 2)) { + printk(KERN_WARNING + "%s: Could not copy message context FROM user\n", + c->name); + rcode = -EFAULT; + } + if (copy_to_user(user_reply, reply, reply_size)) { + printk(KERN_WARNING + "%s: Could not copy reply TO user\n", c->name); + rcode = -EFAULT; + } + } + + cleanup: + kfree(reply); + printk(KERN_INFO "rcode: %d\n", rcode); + return rcode; +} + +#else + +static int i2o_cfg_passthru(unsigned long arg) +{ + struct i2o_cmd_passthru __user *cmd = + (struct i2o_cmd_passthru __user *)arg; struct i2o_controller *c; - u32 msg[MSG_FRAME_SIZE]; u32 __user *user_msg; u32 *reply = NULL; u32 __user *user_reply = NULL; @@ -858,64 +861,88 @@ static int ioctl_passthru(unsigned long int sg_index = 0; u32 i = 0; void *p = NULL; + i2o_status_block *sb; + struct i2o_message *msg; + u32 m; unsigned int iop; if (get_user(iop, &cmd->iop) || get_user(user_msg, &cmd->msg)) return -EFAULT; - c = i2o_find_controller(iop); - if (!c) - return -ENXIO; + c = i2o_find_iop(iop); + if (!c) { + pr_debug("controller %d not found\n", iop); + return -ENXIO; + } + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + + sb = c->status_block.virt; - memset(&msg, 0, MSG_FRAME_SIZE*4); - if(get_user(size, &user_msg[0])) + if (get_user(size, &user_msg[0])) return -EFAULT; - size = size>>16; + size = size >> 16; - user_reply = &user_msg[size]; - if(size > MSG_FRAME_SIZE) + if (size > sb->inbound_frame_size) { + pr_debug("size of message > inbound_frame_size"); return -EFAULT; - size *= 4; // Convert to bytes + } + + user_reply = &user_msg[size]; + + size <<= 2; // Convert to bytes /* Copy in the user's I2O command */ - if(copy_from_user(msg, user_msg, size)) + if (copy_from_user(msg, user_msg, size)) return -EFAULT; - if(get_user(reply_size, &user_reply[0]) < 0) + + if (get_user(reply_size, &user_reply[0]) < 0) return -EFAULT; - reply_size = reply_size>>16; - reply = kmalloc(REPLY_FRAME_SIZE*4, GFP_KERNEL); - if(!reply) { - printk(KERN_WARNING"%s: Could not allocate reply buffer\n",c->name); + reply_size >>= 16; + reply_size <<= 2; + + reply = kmalloc(reply_size, GFP_KERNEL); + if (!reply) { + printk(KERN_WARNING "%s: Could not allocate reply buffer\n", + c->name); return -ENOMEM; } - memset(reply, 0, REPLY_FRAME_SIZE*4); - sg_offset = (msg[0]>>4)&0x0f; - msg[2] = (u32)i2o_cfg_context; - msg[3] = (u32)reply; + memset(reply, 0, reply_size); + + sg_offset = (msg->u.head[0] >> 4) & 0x0f; - memset(sg_list,0, sizeof(sg_list[0])*SG_TABLESIZE); - if(sg_offset) { + writel(i2o_config_driver.context, &msg->u.s.icntxt); + writel(i2o_cntxt_list_add(c, reply), &msg->u.s.tcntxt); + + memset(sg_list, 0, sizeof(sg_list[0]) * SG_TABLESIZE); + if (sg_offset) { struct sg_simple_element *sg; - if(sg_offset * 4 >= size) { + if (sg_offset * 4 >= size) { rcode = -EFAULT; goto cleanup; } // TODO 64bit fix - sg = (struct sg_simple_element*) (msg+sg_offset); - sg_count = (size - sg_offset*4) / sizeof(struct sg_simple_element); + sg = (struct sg_simple_element *)((&msg->u.head[0]) + + sg_offset); + sg_count = + (size - sg_offset * 4) / sizeof(struct sg_simple_element); if (sg_count > SG_TABLESIZE) { - printk(KERN_DEBUG"%s:IOCTL SG List too large (%u)\n", c->name,sg_count); - kfree (reply); + printk(KERN_DEBUG "%s:IOCTL SG List too large (%u)\n", + c->name, sg_count); + kfree(reply); return -EINVAL; } - for(i = 0; i < sg_count; i++) { + for (i = 0; i < sg_count; i++) { int sg_size; - if (!(sg[i].flag_count & 0x10000000 /*I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT*/)) { - printk(KERN_DEBUG"%s:Bad SG element %d - not simple (%x)\n",c->name,i, sg[i].flag_count); + if (!(sg[i].flag_count & 0x10000000 + /*I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT */ )) { + printk(KERN_DEBUG + "%s:Bad SG element %d - not simple (%x)\n", + c->name, i, sg[i].flag_count); rcode = -EINVAL; goto cleanup; } @@ -923,61 +950,78 @@ static int ioctl_passthru(unsigned long /* Allocate memory for the transfer */ p = kmalloc(sg_size, GFP_KERNEL); if (!p) { - printk(KERN_DEBUG"%s: Could not allocate SG buffer - size = %d buffer number %d of %d\n", c->name,sg_size,i,sg_count); + printk(KERN_DEBUG + "%s: Could not allocate SG buffer - size = %d buffer number %d of %d\n", + c->name, sg_size, i, sg_count); rcode = -ENOMEM; goto cleanup; } - sg_list[sg_index++] = p; // sglist indexed with input frame, not our internal frame. + sg_list[sg_index++] = p; // sglist indexed with input frame, not our internal frame. /* Copy in the user's SG buffer if necessary */ - if(sg[i].flag_count & 0x04000000 /*I2O_SGL_FLAGS_DIR*/) { + if (sg[i]. + flag_count & 0x04000000 /*I2O_SGL_FLAGS_DIR */ ) { // TODO 64bit fix - if (copy_from_user(p,(void __user *)sg[i].addr_bus, sg_size)) { - printk(KERN_DEBUG"%s: Could not copy SG buf %d FROM user\n",c->name,i); + if (copy_from_user + (p, (void __user *)sg[i].addr_bus, + sg_size)) { + printk(KERN_DEBUG + "%s: Could not copy SG buf %d FROM user\n", + c->name, i); rcode = -EFAULT; goto cleanup; } } //TODO 64bit fix - sg[i].addr_bus = (u32)virt_to_bus(p); + sg[i].addr_bus = virt_to_bus(p); } } - rcode = i2o_post_wait(c, msg, size, 60); - if(rcode) + rcode = i2o_msg_post_wait(c, m, 60); + if (rcode) goto cleanup; - if(sg_offset) { + if (sg_offset) { + u32 msg[128]; /* Copy back the Scatter Gather buffers back to user space */ u32 j; // TODO 64bit fix - struct sg_simple_element* sg; + struct sg_simple_element *sg; int sg_size; + printk(KERN_INFO "sg_offset\n"); // re-acquire the original message to handle correctly the sg copy operation - memset(&msg, 0, MSG_FRAME_SIZE*4); + memset(&msg, 0, MSG_FRAME_SIZE * 4); // get user msg size in u32s if (get_user(size, &user_msg[0])) { rcode = -EFAULT; goto cleanup; } - size = size>>16; + size = size >> 16; size *= 4; /* Copy in the user's I2O command */ - if (copy_from_user (msg, user_msg, size)) { + if (copy_from_user(msg, user_msg, size)) { rcode = -EFAULT; goto cleanup; } - sg_count = (size - sg_offset*4) / sizeof(struct sg_simple_element); + sg_count = + (size - sg_offset * 4) / sizeof(struct sg_simple_element); - // TODO 64bit fix - sg = (struct sg_simple_element*)(msg + sg_offset); + // TODO 64bit fix + sg = (struct sg_simple_element *)(msg + sg_offset); for (j = 0; j < sg_count; j++) { /* Copy out the SG list to user's buffer if necessary */ - if (!(sg[j].flag_count & 0x4000000 /*I2O_SGL_FLAGS_DIR*/)) { + if (! + (sg[j]. + flag_count & 0x4000000 /*I2O_SGL_FLAGS_DIR */ )) { sg_size = sg[j].flag_count & 0xffffff; // TODO 64bit fix - if (copy_to_user((void __user *)sg[j].addr_bus,sg_list[j], sg_size)) { - printk(KERN_WARNING"%s: Could not copy %p TO user %x\n",c->name, sg_list[j], sg[j].addr_bus); + if (copy_to_user + ((void __user *)sg[j].addr_bus, sg_list[j], + sg_size)) { + printk(KERN_WARNING + "%s: Could not copy %p TO user %x\n", + c->name, sg_list[j], + sg[j].addr_bus); rcode = -EFAULT; goto cleanup; } @@ -986,37 +1030,109 @@ static int ioctl_passthru(unsigned long } /* Copy back the reply to user space */ - if (reply_size) { + if (reply_size) { // we wrote our own values for context - now restore the user supplied ones - if(copy_from_user(reply+2, user_msg+2, sizeof(u32)*2)) { - printk(KERN_WARNING"%s: Could not copy message context FROM user\n",c->name); + printk(KERN_INFO "reply_size\n"); + if (copy_from_user(reply + 2, user_msg + 2, sizeof(u32) * 2)) { + printk(KERN_WARNING + "%s: Could not copy message context FROM user\n", + c->name); rcode = -EFAULT; } - if(copy_to_user(user_reply, reply, reply_size)) { - printk(KERN_WARNING"%s: Could not copy reply TO user\n",c->name); + if (copy_to_user(user_reply, reply, reply_size)) { + printk(KERN_WARNING + "%s: Could not copy reply TO user\n", c->name); rcode = -EFAULT; } } -cleanup: + cleanup: kfree(reply); - i2o_unlock_controller(c); return rcode; } +#endif + +/* + * IOCTL Handler + */ +static int i2o_cfg_ioctl(struct inode *inode, struct file *fp, unsigned int cmd, + unsigned long arg) +{ + int ret; + + switch (cmd) { + case I2OGETIOPS: + ret = i2o_cfg_getiops(arg); + break; + + case I2OHRTGET: + ret = i2o_cfg_gethrt(arg); + break; + + case I2OLCTGET: + ret = i2o_cfg_getlct(arg); + break; + + case I2OPARMSET: + ret = i2o_cfg_parms(arg, I2OPARMSET); + break; + + case I2OPARMGET: + ret = i2o_cfg_parms(arg, I2OPARMGET); + break; + + case I2OSWDL: + ret = i2o_cfg_swdl(arg); + break; + + case I2OSWUL: + ret = i2o_cfg_swul(arg); + break; + + case I2OSWDEL: + ret = i2o_cfg_swdel(arg); + break; + + case I2OVALIDATE: + ret = i2o_cfg_validate(arg); + break; + + case I2OEVTREG: + ret = i2o_cfg_evt_reg(arg, fp); + break; + + case I2OEVTGET: + ret = i2o_cfg_evt_get(arg, fp); + break; + +#if BITS_PER_LONG != 64 + case I2OPASSTHRU: + ret = i2o_cfg_passthru(arg); + break; +#endif + + default: + pr_debug("i2o_config: unknown ioctl called!\n"); + ret = -EINVAL; + } + + return ret; +} static int cfg_open(struct inode *inode, struct file *file) { - struct i2o_cfg_info *tmp = - (struct i2o_cfg_info *)kmalloc(sizeof(struct i2o_cfg_info), GFP_KERNEL); + struct i2o_cfg_info *tmp = + (struct i2o_cfg_info *)kmalloc(sizeof(struct i2o_cfg_info), + GFP_KERNEL); unsigned long flags; - if(!tmp) + if (!tmp) return -ENOMEM; - file->private_data = (void*)(i2o_cfg_info_id++); + file->private_data = (void *)(i2o_cfg_info_id++); tmp->fp = file; tmp->fasync = NULL; - tmp->q_id = (u32)file->private_data; + tmp->q_id = (ulong) file->private_data; tmp->q_len = 0; tmp->q_in = 0; tmp->q_out = 0; @@ -1026,13 +1142,28 @@ static int cfg_open(struct inode *inode, spin_lock_irqsave(&i2o_config_lock, flags); open_files = tmp; spin_unlock_irqrestore(&i2o_config_lock, flags); - + return 0; } +static int cfg_fasync(int fd, struct file *fp, int on) +{ + ulong id = (ulong) fp->private_data; + struct i2o_cfg_info *p; + + for (p = open_files; p; p = p->next) + if (p->q_id == id) + break; + + if (!p) + return -EBADF; + + return fasync_helper(fd, fp, on, &p->fasync); +} + static int cfg_release(struct inode *inode, struct file *file) { - u32 id = (u32)file->private_data; + ulong id = (ulong) file->private_data; struct i2o_cfg_info *p1, *p2; unsigned long flags; @@ -1040,14 +1171,12 @@ static int cfg_release(struct inode *ino p1 = p2 = NULL; spin_lock_irqsave(&i2o_config_lock, flags); - for(p1 = open_files; p1; ) - { - if(p1->q_id == id) - { + for (p1 = open_files; p1;) { + if (p1->q_id == id) { - if(p1->fasync) + if (p1->fasync) cfg_fasync(-1, file, 0); - if(p2) + if (p2) p2->next = p1->next; else open_files = p1->next; @@ -1064,83 +1193,55 @@ static int cfg_release(struct inode *ino return 0; } -static int cfg_fasync(int fd, struct file *fp, int on) -{ - u32 id = (u32)fp->private_data; - struct i2o_cfg_info *p; - - for(p = open_files; p; p = p->next) - if(p->q_id == id) - break; - - if(!p) - return -EBADF; - - return fasync_helper(fd, fp, on, &p->fasync); -} - -static struct file_operations config_fops = -{ - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = cfg_read, - .write = cfg_write, - .ioctl = cfg_ioctl, - .open = cfg_open, - .release = cfg_release, - .fasync = cfg_fasync, +static struct file_operations config_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = i2o_cfg_ioctl, + .open = cfg_open, + .release = cfg_release, + .fasync = cfg_fasync, }; static struct miscdevice i2o_miscdev = { I2O_MINOR, "i2octl", &config_fops -}; +}; static int __init i2o_config_init(void) { printk(KERN_INFO "I2O configuration manager v 0.04.\n"); printk(KERN_INFO " (C) Copyright 1999 Red Hat Software\n"); - - if((page_buf = kmalloc(4096, GFP_KERNEL))==NULL) - { - printk(KERN_ERR "i2o_config: no memory for page buffer.\n"); - return -ENOBUFS; - } - if(misc_register(&i2o_miscdev) < 0) - { + + if (misc_register(&i2o_miscdev) < 0) { printk(KERN_ERR "i2o_config: can't register device.\n"); - kfree(page_buf); return -EBUSY; } /* - * Install our handler + * Install our handler */ - if(i2o_install_handler(&cfg_handler)<0) - { - kfree(page_buf); + if (i2o_driver_register(&i2o_config_driver)) { printk(KERN_ERR "i2o_config: handler register failed.\n"); misc_deregister(&i2o_miscdev); return -EBUSY; } - /* - * The low 16bits of the transaction context must match this - * for everything we post. Otherwise someone else gets our mail - */ - i2o_cfg_context = cfg_handler.context; +#if BITS_PER_LONG ==64 + register_ioctl32_conversion(I2OPASSTHRU32, i2o_cfg_passthru32); + register_ioctl32_conversion(I2OGETIOPS, (void *)sys_ioctl); +#endif return 0; } static void i2o_config_exit(void) { +#if BITS_PER_LONG ==64 + unregister_ioctl32_conversion(I2OPASSTHRU32); + unregister_ioctl32_conversion(I2OGETIOPS); +#endif misc_deregister(&i2o_miscdev); - - if(page_buf) - kfree(page_buf); - if(i2o_cfg_context != -1) - i2o_remove_handler(&cfg_handler); + i2o_driver_unregister(&i2o_config_driver); } - + MODULE_AUTHOR("Red Hat Software"); MODULE_DESCRIPTION("I2O Configuration"); MODULE_LICENSE("GPL"); --- linux-2.6.9-rc1/drivers/message/i2o/i2o_core.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/message/i2o/i2o_core.c 2004-09-03 00:57:24.766274216 -0700 @@ -892,8 +892,7 @@ int i2o_release_device(struct i2o_device if((err=i2o_issue_claim(I2O_CMD_UTIL_RELEASE, d->controller, d->lct_data.tid, I2O_CLAIM_PRIMARY)) ) { err = -ENXIO; - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(HZ); + msleep(1000); } else { --- linux-2.6.9-rc1/drivers/message/i2o/i2o_proc.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/message/i2o/i2o_proc.c 2004-09-02 20:51:52.000000000 -0700 @@ -1,39 +1,33 @@ /* - * procfs handler for Linux I2O subsystem + * procfs handler for Linux I2O subsystem * - * (c) Copyright 1999 Deepak Saxena - * - * Originally written by Deepak Saxena(deepak@plexity.net) - * - * This program is free software. You can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * This is an initial test release. The code is based on the design - * of the ide procfs system (drivers/block/ide-proc.c). Some code - * taken from i2o-core module by Alan Cox. - * - * DISCLAIMER: This code is still under development/test and may cause - * your system to behave unpredictably. Use at your own discretion. - * - * LAN entries by Juha Sievänen (Juha.Sievanen@cs.Helsinki.FI), - * Auvo Häkkinen (Auvo.Hakkinen@cs.Helsinki.FI) - * University of Helsinki, Department of Computer Science - */ - -/* - * set tabstop=3 - */ - -/* - * TODO List + * (c) Copyright 1999 Deepak Saxena + * + * Originally written by Deepak Saxena(deepak@plexity.net) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. * - * - Add support for any version 2.0 spec changes once 2.0 IRTOS is - * is available to test with - * - Clean up code to use official structure definitions + * This is an initial test release. The code is based on the design of the + * ide procfs system (drivers/block/ide-proc.c). Some code taken from + * i2o-core module by Alan Cox. + * + * DISCLAIMER: This code is still under development/test and may cause + * your system to behave unpredictably. Use at your own discretion. + * + * + * Fixes/additions: + * Juha Sievänen (Juha.Sievanen@cs.Helsinki.FI), + * Auvo Häkkinen (Auvo.Hakkinen@cs.Helsinki.FI) + * University of Helsinki, Department of Computer Science + * LAN entries + * Markus Lidel + * Changes for new I2O API */ +#define I2O_MAX_MODULES 4 // FIXME! #define FMT_U64_HEX "0x%08x%08x" #define U64_VAL(pu64) *((u32*)(pu64)+1), *((u32*)(pu64)) @@ -54,188 +48,198 @@ #include #include -#include "i2o_lan.h" - -/* - * Structure used to define /proc entries - */ -typedef struct _i2o_proc_entry_t -{ - char *name; /* entry name */ - mode_t mode; /* mode */ - read_proc_t *read_proc; /* read func */ - write_proc_t *write_proc; /* write func */ - struct file_operations *fops_proc; /* file operations func */ +/* Structure used to define /proc entries */ +typedef struct _i2o_proc_entry_t { + char *name; /* entry name */ + mode_t mode; /* mode */ + struct file_operations *fops; /* open function */ } i2o_proc_entry; -// #define DRIVERDEBUG - -static int i2o_seq_show_lct(struct seq_file *, void *); -static int i2o_seq_show_hrt(struct seq_file *, void *); -static int i2o_seq_show_status(struct seq_file *, void *); - -static int i2o_proc_read_hw(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_ddm_table(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_driver_store(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_drivers_stored(char *, char **, off_t, int, int *, void *); - -static int i2o_proc_read_groups(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_phys_device(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_claimed(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_users(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_priv_msgs(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_authorized_users(char *, char **, off_t, int, int *, void *); - -static int i2o_proc_read_dev_name(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_dev_identity(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_ddm_identity(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_uinfo(char *, char **, off_t, int, int *, void *); -static int i2o_proc_read_sgl_limits(char *, char **, off_t, int, int *, void *); - -static int i2o_proc_read_sensors(char *, char **, off_t, int, int *, void *); - -static int print_serial_number(char *, int, u8 *, int); - -static int i2o_proc_create_entries(void *, i2o_proc_entry *, - struct proc_dir_entry *); -static void i2o_proc_remove_entries(i2o_proc_entry *, struct proc_dir_entry *); -static int i2o_proc_add_controller(struct i2o_controller *, - struct proc_dir_entry * ); -static void i2o_proc_remove_controller(struct i2o_controller *, - struct proc_dir_entry * ); -static void i2o_proc_add_device(struct i2o_device *, struct proc_dir_entry *); -static void i2o_proc_remove_device(struct i2o_device *); -static int create_i2o_procfs(void); -static int destroy_i2o_procfs(void); -static void i2o_proc_new_dev(struct i2o_controller *, struct i2o_device *); -static void i2o_proc_dev_del(struct i2o_controller *, struct i2o_device *); - -static int i2o_proc_read_lan_dev_info(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_mac_addr(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_mcast_addr(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_batch_control(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_operation(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_media_operation(char *, char **, off_t, int, - int *, void *); -static int i2o_proc_read_lan_alt_addr(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_tx_info(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_rx_info(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_hist_stats(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_eth_stats(char *, char **, off_t, int, - int *, void *); -static int i2o_proc_read_lan_tr_stats(char *, char **, off_t, int, int *, - void *); -static int i2o_proc_read_lan_fddi_stats(char *, char **, off_t, int, int *, - void *); - +/* global I2O /proc/i2o entry */ static struct proc_dir_entry *i2o_proc_dir_root; -/* - * I2O OSM descriptor - */ -static struct i2o_handler i2o_proc_handler = -{ - NULL, - i2o_proc_new_dev, - i2o_proc_dev_del, - NULL, - "I2O procfs Layer", - 0, - 0xffffffff // All classes +/* proc OSM driver struct */ +static struct i2o_driver i2o_proc_driver = { + .name = "proc-osm", }; -static int i2o_seq_open_hrt(struct inode *inode, struct file *file) +static int print_serial_number(struct seq_file *seq, u8 * serialno, int max_len) { - return single_open(file, i2o_seq_show_hrt, PDE(inode)->data); -}; + int i; -struct file_operations i2o_seq_fops_hrt = { - .open = i2o_seq_open_hrt, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release -}; + /* 19990419 -sralston + * The I2O v1.5 (and v2.0 so far) "official specification" + * got serial numbers WRONG! + * Apparently, and despite what Section 3.4.4 says and + * Figure 3-35 shows (pg 3-39 in the pdf doc), + * the convention / consensus seems to be: + * + First byte is SNFormat + * + Second byte is SNLen (but only if SNFormat==7 (?)) + * + (v2.0) SCSI+BS may use IEEE Registered (64 or 128 bit) format + */ + switch (serialno[0]) { + case I2O_SNFORMAT_BINARY: /* Binary */ + seq_printf(seq, "0x"); + for (i = 0; i < serialno[1]; i++) { + seq_printf(seq, "%02X", serialno[2 + i]); + } + break; -static int i2o_seq_open_lct(struct inode *inode, struct file *file) -{ - return single_open(file, i2o_seq_show_lct, PDE(inode)->data); -}; + case I2O_SNFORMAT_ASCII: /* ASCII */ + if (serialno[1] < ' ') { /* printable or SNLen? */ + /* sanity */ + max_len = + (max_len < serialno[1]) ? max_len : serialno[1]; + serialno[1 + max_len] = '\0'; + + /* just print it */ + seq_printf(seq, "%s", &serialno[2]); + } else { + /* print chars for specified length */ + for (i = 0; i < serialno[1]; i++) { + seq_printf(seq, "%c", serialno[2 + i]); + } + } + break; -struct file_operations i2o_seq_fops_lct = { - .open = i2o_seq_open_lct, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release -}; + case I2O_SNFORMAT_UNICODE: /* UNICODE */ + seq_printf(seq, "UNICODE Format. Can't Display\n"); + break; -static int i2o_seq_open_status(struct inode *inode, struct file *file) -{ - return single_open(file, i2o_seq_show_status, PDE(inode)->data); -}; + case I2O_SNFORMAT_LAN48_MAC: /* LAN-48 MAC Address */ + seq_printf(seq, + "LAN-48 MAC address @ %02X:%02X:%02X:%02X:%02X:%02X", + serialno[2], serialno[3], + serialno[4], serialno[5], serialno[6], serialno[7]); + break; -struct file_operations i2o_seq_fops_status = { - .open = i2o_seq_open_status, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release -}; + case I2O_SNFORMAT_WAN: /* WAN MAC Address */ + /* FIXME: Figure out what a WAN access address looks like?? */ + seq_printf(seq, "WAN Access Address"); + break; -/* - * IOP specific entries...write field just in case someone - * ever wants one. - */ -static i2o_proc_entry generic_iop_entries[] = -{ - {"hrt", S_IFREG|S_IRUGO, NULL, NULL, &i2o_seq_fops_hrt}, - {"lct", S_IFREG|S_IRUGO, NULL, NULL, &i2o_seq_fops_lct}, - {"status", S_IFREG|S_IRUGO, NULL, NULL, &i2o_seq_fops_status}, - {"hw", S_IFREG|S_IRUGO, i2o_proc_read_hw, NULL, NULL}, - {"ddm_table", S_IFREG|S_IRUGO, i2o_proc_read_ddm_table, NULL, NULL}, - {"driver_store", S_IFREG|S_IRUGO, i2o_proc_read_driver_store, NULL, NULL}, - {"drivers_stored", S_IFREG|S_IRUGO, i2o_proc_read_drivers_stored, NULL, NULL}, - {NULL, 0, NULL, NULL, NULL} -}; +/* plus new in v2.0 */ + case I2O_SNFORMAT_LAN64_MAC: /* LAN-64 MAC Address */ + /* FIXME: Figure out what a LAN-64 address really looks like?? */ + seq_printf(seq, + "LAN-64 MAC address @ [?:%02X:%02X:?] %02X:%02X:%02X:%02X:%02X:%02X", + serialno[8], serialno[9], + serialno[2], serialno[3], + serialno[4], serialno[5], serialno[6], serialno[7]); + break; + + case I2O_SNFORMAT_DDM: /* I2O DDM */ + seq_printf(seq, + "DDM: Tid=%03Xh, Rsvd=%04Xh, OrgId=%04Xh", + *(u16 *) & serialno[2], + *(u16 *) & serialno[4], *(u16 *) & serialno[6]); + break; + + case I2O_SNFORMAT_IEEE_REG64: /* IEEE Registered (64-bit) */ + case I2O_SNFORMAT_IEEE_REG128: /* IEEE Registered (128-bit) */ + /* FIXME: Figure if this is even close?? */ + seq_printf(seq, + "IEEE NodeName(hi,lo)=(%08Xh:%08Xh), PortName(hi,lo)=(%08Xh:%08Xh)\n", + *(u32 *) & serialno[2], + *(u32 *) & serialno[6], + *(u32 *) & serialno[10], *(u32 *) & serialno[14]); + break; -/* - * Device specific entries - */ -static i2o_proc_entry generic_dev_entries[] = -{ - {"groups", S_IFREG|S_IRUGO, i2o_proc_read_groups, NULL, NULL}, - {"phys_dev", S_IFREG|S_IRUGO, i2o_proc_read_phys_device, NULL, NULL}, - {"claimed", S_IFREG|S_IRUGO, i2o_proc_read_claimed, NULL, NULL}, - {"users", S_IFREG|S_IRUGO, i2o_proc_read_users, NULL, NULL}, - {"priv_msgs", S_IFREG|S_IRUGO, i2o_proc_read_priv_msgs, NULL, NULL}, - {"authorized_users", S_IFREG|S_IRUGO, i2o_proc_read_authorized_users, NULL, NULL}, - {"dev_identity", S_IFREG|S_IRUGO, i2o_proc_read_dev_identity, NULL, NULL}, - {"ddm_identity", S_IFREG|S_IRUGO, i2o_proc_read_ddm_identity, NULL, NULL}, - {"user_info", S_IFREG|S_IRUGO, i2o_proc_read_uinfo, NULL, NULL}, - {"sgl_limits", S_IFREG|S_IRUGO, i2o_proc_read_sgl_limits, NULL, NULL}, - {"sensors", S_IFREG|S_IRUGO, i2o_proc_read_sensors, NULL, NULL}, - {NULL, 0, NULL, NULL, NULL} -}; + case I2O_SNFORMAT_UNKNOWN: /* Unknown 0 */ + case I2O_SNFORMAT_UNKNOWN2: /* Unknown 0xff */ + default: + seq_printf(seq, "Unknown data format (0x%02x)", serialno[0]); + break; + } -/* - * Storage unit specific entries (SCSI Periph, BS) with device names + return 0; +} + +/** + * i2o_get_class_name - do i2o class name lookup + * @class: class number + * + * Return a descriptive string for an i2o class */ -static i2o_proc_entry rbs_dev_entries[] = +static const char *i2o_get_class_name(int class) { - {"dev_name", S_IFREG|S_IRUGO, i2o_proc_read_dev_name, NULL, NULL}, - {NULL, 0, NULL, NULL} -}; + int idx = 16; + static char *i2o_class_name[] = { + "Executive", + "Device Driver Module", + "Block Device", + "Tape Device", + "LAN Interface", + "WAN Interface", + "Fibre Channel Port", + "Fibre Channel Device", + "SCSI Device", + "ATE Port", + "ATE Device", + "Floppy Controller", + "Floppy Device", + "Secondary Bus Port", + "Peer Transport Agent", + "Peer Transport", + "Unknown" + }; + + switch (class & 0xfff) { + case I2O_CLASS_EXECUTIVE: + idx = 0; + break; + case I2O_CLASS_DDM: + idx = 1; + break; + case I2O_CLASS_RANDOM_BLOCK_STORAGE: + idx = 2; + break; + case I2O_CLASS_SEQUENTIAL_STORAGE: + idx = 3; + break; + case I2O_CLASS_LAN: + idx = 4; + break; + case I2O_CLASS_WAN: + idx = 5; + break; + case I2O_CLASS_FIBRE_CHANNEL_PORT: + idx = 6; + break; + case I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL: + idx = 7; + break; + case I2O_CLASS_SCSI_PERIPHERAL: + idx = 8; + break; + case I2O_CLASS_ATE_PORT: + idx = 9; + break; + case I2O_CLASS_ATE_PERIPHERAL: + idx = 10; + break; + case I2O_CLASS_FLOPPY_CONTROLLER: + idx = 11; + break; + case I2O_CLASS_FLOPPY_DEVICE: + idx = 12; + break; + case I2O_CLASS_BUS_ADAPTER_PORT: + idx = 13; + break; + case I2O_CLASS_PEER_TRANSPORT_AGENT: + idx = 14; + break; + case I2O_CLASS_PEER_TRANSPORT: + idx = 15; + break; + } + + return i2o_class_name[idx]; +} #define SCSI_TABLE_SIZE 13 -static char *scsi_devices[] = -{ +static char *scsi_devices[] = { "Direct-Access Read/Write", "Sequential-Access Storage", "Printer", @@ -251,307 +255,267 @@ static char *scsi_devices[] = "Array Controller Device" }; -/* private */ - -/* - * Generic LAN specific entries - * - * Should groups with r/w entries have their own subdirectory? - * - */ -static i2o_proc_entry lan_entries[] = -{ - {"lan_dev_info", S_IFREG|S_IRUGO, i2o_proc_read_lan_dev_info, NULL, NULL}, - {"lan_mac_addr", S_IFREG|S_IRUGO, i2o_proc_read_lan_mac_addr, NULL, NULL}, - {"lan_mcast_addr", S_IFREG|S_IRUGO|S_IWUSR, - i2o_proc_read_lan_mcast_addr, NULL, NULL}, - {"lan_batch_ctrl", S_IFREG|S_IRUGO|S_IWUSR, - i2o_proc_read_lan_batch_control, NULL, NULL}, - {"lan_operation", S_IFREG|S_IRUGO, i2o_proc_read_lan_operation, NULL, NULL}, - {"lan_media_operation", S_IFREG|S_IRUGO, - i2o_proc_read_lan_media_operation, NULL, NULL}, - {"lan_alt_addr", S_IFREG|S_IRUGO, i2o_proc_read_lan_alt_addr, NULL, NULL}, - {"lan_tx_info", S_IFREG|S_IRUGO, i2o_proc_read_lan_tx_info, NULL, NULL}, - {"lan_rx_info", S_IFREG|S_IRUGO, i2o_proc_read_lan_rx_info, NULL, NULL}, - - {"lan_hist_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_hist_stats, NULL, NULL}, - {NULL, 0, NULL, NULL, NULL} -}; - -/* - * Port specific LAN entries - * - */ -static i2o_proc_entry lan_eth_entries[] = -{ - {"lan_eth_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_eth_stats, NULL, NULL}, - {NULL, 0, NULL, NULL, NULL} -}; - -static i2o_proc_entry lan_tr_entries[] = -{ - {"lan_tr_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_tr_stats, NULL, NULL}, - {NULL, 0, NULL, NULL, NULL} -}; - -static i2o_proc_entry lan_fddi_entries[] = -{ - {"lan_fddi_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_fddi_stats, NULL, NULL}, - {NULL, 0, NULL, NULL, NULL} -}; - - -static char *chtostr(u8 *chars, int n) +static char *chtostr(u8 * chars, int n) { char tmp[256]; tmp[0] = 0; - return strncat(tmp, (char *)chars, n); + return strncat(tmp, (char *)chars, n); } -static int i2o_report_query_status(char *buf, int block_status, char *group) +static int i2o_report_query_status(struct seq_file *seq, int block_status, + char *group) { - switch (block_status) - { + switch (block_status) { case -ETIMEDOUT: - return sprintf(buf, "Timeout reading group %s.\n",group); + return seq_printf(seq, "Timeout reading group %s.\n", group); case -ENOMEM: - return sprintf(buf, "No free memory to read the table.\n"); + return seq_printf(seq, "No free memory to read the table.\n"); case -I2O_PARAMS_STATUS_INVALID_GROUP_ID: - return sprintf(buf, "Group %s not supported.\n", group); + return seq_printf(seq, "Group %s not supported.\n", group); default: - return sprintf(buf, "Error reading group %s. BlockStatus 0x%02X\n", - group, -block_status); + return seq_printf(seq, + "Error reading group %s. BlockStatus 0x%02X\n", + group, -block_status); } } -static char* bus_strings[] = -{ - "Local Bus", - "ISA", - "EISA", - "MCA", +static char *bus_strings[] = { + "Local Bus", + "ISA", + "EISA", + "MCA", "PCI", - "PCMCIA", - "NUBUS", + "PCMCIA", + "NUBUS", "CARDBUS" }; -static spinlock_t i2o_proc_lock = SPIN_LOCK_UNLOCKED; - int i2o_seq_show_hrt(struct seq_file *seq, void *v) { struct i2o_controller *c = (struct i2o_controller *)seq->private; - i2o_hrt *hrt = (i2o_hrt *)c->hrt; + i2o_hrt *hrt = (i2o_hrt *) c->hrt.virt; u32 bus; int i; - if(hrt->hrt_version) - { - seq_printf(seq, "HRT table for controller is too new a version.\n"); + if (hrt->hrt_version) { + seq_printf(seq, + "HRT table for controller is too new a version.\n"); return 0; } seq_printf(seq, "HRT has %d entries of %d bytes each.\n", - hrt->num_entries, hrt->entry_len << 2); + hrt->num_entries, hrt->entry_len << 2); - for(i = 0; i < hrt->num_entries; i++) - { + for (i = 0; i < hrt->num_entries; i++) { seq_printf(seq, "Entry %d:\n", i); seq_printf(seq, " Adapter ID: %0#10x\n", - hrt->hrt_entry[i].adapter_id); + hrt->hrt_entry[i].adapter_id); seq_printf(seq, " Controlling tid: %0#6x\n", - hrt->hrt_entry[i].parent_tid); + hrt->hrt_entry[i].parent_tid); - if(hrt->hrt_entry[i].bus_type != 0x80) - { + if (hrt->hrt_entry[i].bus_type != 0x80) { bus = hrt->hrt_entry[i].bus_type; - seq_printf(seq, " %s Information\n", bus_strings[bus]); + seq_printf(seq, " %s Information\n", + bus_strings[bus]); + + switch (bus) { + case I2O_BUS_LOCAL: + seq_printf(seq, " IOBase: %0#6x,", + hrt->hrt_entry[i].bus.local_bus. + LbBaseIOPort); + seq_printf(seq, " MemoryBase: %0#10x\n", + hrt->hrt_entry[i].bus.local_bus. + LbBaseMemoryAddress); + break; + + case I2O_BUS_ISA: + seq_printf(seq, " IOBase: %0#6x,", + hrt->hrt_entry[i].bus.isa_bus. + IsaBaseIOPort); + seq_printf(seq, " MemoryBase: %0#10x,", + hrt->hrt_entry[i].bus.isa_bus. + IsaBaseMemoryAddress); + seq_printf(seq, " CSN: %0#4x,", + hrt->hrt_entry[i].bus.isa_bus.CSN); + break; + + case I2O_BUS_EISA: + seq_printf(seq, " IOBase: %0#6x,", + hrt->hrt_entry[i].bus.eisa_bus. + EisaBaseIOPort); + seq_printf(seq, " MemoryBase: %0#10x,", + hrt->hrt_entry[i].bus.eisa_bus. + EisaBaseMemoryAddress); + seq_printf(seq, " Slot: %0#4x,", + hrt->hrt_entry[i].bus.eisa_bus. + EisaSlotNumber); + break; - switch(bus) - { - case I2O_BUS_LOCAL: - seq_printf(seq, " IOBase: %0#6x,", - hrt->hrt_entry[i].bus.local_bus.LbBaseIOPort); - seq_printf(seq, " MemoryBase: %0#10x\n", - hrt->hrt_entry[i].bus.local_bus.LbBaseMemoryAddress); - break; - - case I2O_BUS_ISA: - seq_printf(seq, " IOBase: %0#6x,", - hrt->hrt_entry[i].bus.isa_bus.IsaBaseIOPort); - seq_printf(seq, " MemoryBase: %0#10x,", - hrt->hrt_entry[i].bus.isa_bus.IsaBaseMemoryAddress); - seq_printf(seq, " CSN: %0#4x,", - hrt->hrt_entry[i].bus.isa_bus.CSN); - break; - - case I2O_BUS_EISA: - seq_printf(seq, " IOBase: %0#6x,", - hrt->hrt_entry[i].bus.eisa_bus.EisaBaseIOPort); - seq_printf(seq, " MemoryBase: %0#10x,", - hrt->hrt_entry[i].bus.eisa_bus.EisaBaseMemoryAddress); - seq_printf(seq, " Slot: %0#4x,", - hrt->hrt_entry[i].bus.eisa_bus.EisaSlotNumber); - break; - - case I2O_BUS_MCA: - seq_printf(seq, " IOBase: %0#6x,", - hrt->hrt_entry[i].bus.mca_bus.McaBaseIOPort); - seq_printf(seq, " MemoryBase: %0#10x,", - hrt->hrt_entry[i].bus.mca_bus.McaBaseMemoryAddress); - seq_printf(seq, " Slot: %0#4x,", - hrt->hrt_entry[i].bus.mca_bus.McaSlotNumber); - break; - - case I2O_BUS_PCI: - seq_printf(seq, " Bus: %0#4x", - hrt->hrt_entry[i].bus.pci_bus.PciBusNumber); - seq_printf(seq, " Dev: %0#4x", - hrt->hrt_entry[i].bus.pci_bus.PciDeviceNumber); - seq_printf(seq, " Func: %0#4x", - hrt->hrt_entry[i].bus.pci_bus.PciFunctionNumber); - seq_printf(seq, " Vendor: %0#6x", - hrt->hrt_entry[i].bus.pci_bus.PciVendorID); - seq_printf(seq, " Device: %0#6x\n", - hrt->hrt_entry[i].bus.pci_bus.PciDeviceID); - break; + case I2O_BUS_MCA: + seq_printf(seq, " IOBase: %0#6x,", + hrt->hrt_entry[i].bus.mca_bus. + McaBaseIOPort); + seq_printf(seq, " MemoryBase: %0#10x,", + hrt->hrt_entry[i].bus.mca_bus. + McaBaseMemoryAddress); + seq_printf(seq, " Slot: %0#4x,", + hrt->hrt_entry[i].bus.mca_bus. + McaSlotNumber); + break; + + case I2O_BUS_PCI: + seq_printf(seq, " Bus: %0#4x", + hrt->hrt_entry[i].bus.pci_bus. + PciBusNumber); + seq_printf(seq, " Dev: %0#4x", + hrt->hrt_entry[i].bus.pci_bus. + PciDeviceNumber); + seq_printf(seq, " Func: %0#4x", + hrt->hrt_entry[i].bus.pci_bus. + PciFunctionNumber); + seq_printf(seq, " Vendor: %0#6x", + hrt->hrt_entry[i].bus.pci_bus. + PciVendorID); + seq_printf(seq, " Device: %0#6x\n", + hrt->hrt_entry[i].bus.pci_bus. + PciDeviceID); + break; - default: - seq_printf(seq, " Unsupported Bus Type\n"); + default: + seq_printf(seq, " Unsupported Bus Type\n"); } - } - else + } else seq_printf(seq, " Unknown Bus Type\n"); } - + return 0; } int i2o_seq_show_lct(struct seq_file *seq, void *v) { - struct i2o_controller *c = (struct i2o_controller*)seq->private; - i2o_lct *lct = (i2o_lct *)c->lct; + struct i2o_controller *c = (struct i2o_controller *)seq->private; + i2o_lct *lct = (i2o_lct *) c->lct; int entries; int i; #define BUS_TABLE_SIZE 3 - static char *bus_ports[] = - { + static char *bus_ports[] = { "Generic Bus", "SCSI Bus", "Fibre Channel Bus" }; - entries = (lct->table_size - 3)/9; + entries = (lct->table_size - 3) / 9; seq_printf(seq, "LCT contains %d %s\n", entries, - entries == 1 ? "entry" : "entries"); - if(lct->boot_tid) + entries == 1 ? "entry" : "entries"); + if (lct->boot_tid) seq_printf(seq, "Boot Device @ ID %d\n", lct->boot_tid); seq_printf(seq, "Current Change Indicator: %#10x\n", lct->change_ind); - for(i = 0; i < entries; i++) - { + for (i = 0; i < entries; i++) { seq_printf(seq, "Entry %d\n", i); - seq_printf(seq, " Class, SubClass : %s", i2o_get_class_name(lct->lct_entry[i].class_id)); - + seq_printf(seq, " Class, SubClass : %s", + i2o_get_class_name(lct->lct_entry[i].class_id)); + /* - * Classes which we'll print subclass info for + * Classes which we'll print subclass info for */ - switch(lct->lct_entry[i].class_id & 0xFFF) - { - case I2O_CLASS_RANDOM_BLOCK_STORAGE: - switch(lct->lct_entry[i].sub_class) - { - case 0x00: - seq_printf(seq, ", Direct-Access Read/Write"); - break; - - case 0x04: - seq_printf(seq, ", WORM Drive"); - break; - - case 0x05: - seq_printf(seq, ", CD-ROM Drive"); - break; - - case 0x07: - seq_printf(seq, ", Optical Memory Device"); - break; - - default: - seq_printf(seq, ", Unknown (0x%02x)", - lct->lct_entry[i].sub_class); - break; - } - break; - - case I2O_CLASS_LAN: - switch(lct->lct_entry[i].sub_class & 0xFF) - { - case 0x30: - seq_printf(seq, ", Ethernet"); - break; - - case 0x40: - seq_printf(seq, ", 100base VG"); - break; - - case 0x50: - seq_printf(seq, ", IEEE 802.5/Token-Ring"); - break; - - case 0x60: - seq_printf(seq, ", ANSI X3T9.5 FDDI"); - break; - - case 0x70: - seq_printf(seq, ", Fibre Channel"); - break; - - default: - seq_printf(seq, ", Unknown Sub-Class (0x%02x)", - lct->lct_entry[i].sub_class & 0xFF); - break; - } - break; - - case I2O_CLASS_SCSI_PERIPHERAL: - if(lct->lct_entry[i].sub_class < SCSI_TABLE_SIZE) - seq_printf(seq, ", %s", - scsi_devices[lct->lct_entry[i].sub_class]); - else - seq_printf(seq, ", Unknown Device Type"); - break; - - case I2O_CLASS_BUS_ADAPTER_PORT: - if(lct->lct_entry[i].sub_class < BUS_TABLE_SIZE) - seq_printf(seq, ", %s", - bus_ports[lct->lct_entry[i].sub_class]); - else - seq_printf(seq, ", Unknown Bus Type"); + switch (lct->lct_entry[i].class_id & 0xFFF) { + case I2O_CLASS_RANDOM_BLOCK_STORAGE: + switch (lct->lct_entry[i].sub_class) { + case 0x00: + seq_printf(seq, ", Direct-Access Read/Write"); + break; + + case 0x04: + seq_printf(seq, ", WORM Drive"); + break; + + case 0x05: + seq_printf(seq, ", CD-ROM Drive"); + break; + + case 0x07: + seq_printf(seq, ", Optical Memory Device"); + break; + + default: + seq_printf(seq, ", Unknown (0x%02x)", + lct->lct_entry[i].sub_class); + break; + } + break; + + case I2O_CLASS_LAN: + switch (lct->lct_entry[i].sub_class & 0xFF) { + case 0x30: + seq_printf(seq, ", Ethernet"); + break; + + case 0x40: + seq_printf(seq, ", 100base VG"); + break; + + case 0x50: + seq_printf(seq, ", IEEE 802.5/Token-Ring"); + break; + + case 0x60: + seq_printf(seq, ", ANSI X3T9.5 FDDI"); + break; + + case 0x70: + seq_printf(seq, ", Fibre Channel"); + break; + + default: + seq_printf(seq, ", Unknown Sub-Class (0x%02x)", + lct->lct_entry[i].sub_class & 0xFF); break; + } + break; + + case I2O_CLASS_SCSI_PERIPHERAL: + if (lct->lct_entry[i].sub_class < SCSI_TABLE_SIZE) + seq_printf(seq, ", %s", + scsi_devices[lct->lct_entry[i]. + sub_class]); + else + seq_printf(seq, ", Unknown Device Type"); + break; + + case I2O_CLASS_BUS_ADAPTER_PORT: + if (lct->lct_entry[i].sub_class < BUS_TABLE_SIZE) + seq_printf(seq, ", %s", + bus_ports[lct->lct_entry[i]. + sub_class]); + else + seq_printf(seq, ", Unknown Bus Type"); + break; } seq_printf(seq, "\n"); - - seq_printf(seq, " Local TID : 0x%03x\n", lct->lct_entry[i].tid); - seq_printf(seq, " User TID : 0x%03x\n", lct->lct_entry[i].user_tid); + + seq_printf(seq, " Local TID : 0x%03x\n", + lct->lct_entry[i].tid); + seq_printf(seq, " User TID : 0x%03x\n", + lct->lct_entry[i].user_tid); seq_printf(seq, " Parent TID : 0x%03x\n", - lct->lct_entry[i].parent_tid); + lct->lct_entry[i].parent_tid); seq_printf(seq, " Identity Tag : 0x%x%x%x%x%x%x%x%x\n", - lct->lct_entry[i].identity_tag[0], - lct->lct_entry[i].identity_tag[1], - lct->lct_entry[i].identity_tag[2], - lct->lct_entry[i].identity_tag[3], - lct->lct_entry[i].identity_tag[4], - lct->lct_entry[i].identity_tag[5], - lct->lct_entry[i].identity_tag[6], - lct->lct_entry[i].identity_tag[7]); + lct->lct_entry[i].identity_tag[0], + lct->lct_entry[i].identity_tag[1], + lct->lct_entry[i].identity_tag[2], + lct->lct_entry[i].identity_tag[3], + lct->lct_entry[i].identity_tag[4], + lct->lct_entry[i].identity_tag[5], + lct->lct_entry[i].identity_tag[6], + lct->lct_entry[i].identity_tag[7]); seq_printf(seq, " Change Indicator : %0#10x\n", - lct->lct_entry[i].change_ind); + lct->lct_entry[i].change_ind); seq_printf(seq, " Event Capab Mask : %0#10x\n", - lct->lct_entry[i].device_flags); + lct->lct_entry[i].device_flags); } return 0; @@ -559,17 +523,17 @@ int i2o_seq_show_lct(struct seq_file *se int i2o_seq_show_status(struct seq_file *seq, void *v) { - struct i2o_controller *c = (struct i2o_controller*)seq->private; + struct i2o_controller *c = (struct i2o_controller *)seq->private; char prodstr[25]; int version; - - i2o_status_get(c); // reread the status block + i2o_status_block *sb = c->status_block.virt; + + i2o_status_get(c); // reread the status block + + seq_printf(seq, "Organization ID : %0#6x\n", sb->org_id); - seq_printf(seq, "Organization ID : %0#6x\n", - c->status_block->org_id); + version = sb->i2o_version; - version = c->status_block->i2o_version; - /* FIXME for Spec 2.0 if (version == 0x02) { seq_printf(seq, "Lowest I2O version supported: "); @@ -599,170 +563,171 @@ int i2o_seq_show_status(struct seq_file } } */ - seq_printf(seq, "IOP ID : %0#5x\n", - c->status_block->iop_id); - seq_printf(seq, "Host Unit ID : %0#6x\n", - c->status_block->host_unit_id); - seq_printf(seq, "Segment Number : %0#5x\n", - c->status_block->segment_number); + seq_printf(seq, "IOP ID : %0#5x\n", sb->iop_id); + seq_printf(seq, "Host Unit ID : %0#6x\n", sb->host_unit_id); + seq_printf(seq, "Segment Number : %0#5x\n", sb->segment_number); seq_printf(seq, "I2O version : "); switch (version) { - case 0x00: - seq_printf(seq, "1.0\n"); - break; - case 0x01: - seq_printf(seq, "1.5\n"); - break; - case 0x02: - seq_printf(seq, "2.0\n"); - break; - default: - seq_printf(seq, "Unknown version\n"); + case 0x00: + seq_printf(seq, "1.0\n"); + break; + case 0x01: + seq_printf(seq, "1.5\n"); + break; + case 0x02: + seq_printf(seq, "2.0\n"); + break; + default: + seq_printf(seq, "Unknown version\n"); } seq_printf(seq, "IOP State : "); - switch (c->status_block->iop_state) { - case 0x01: - seq_printf(seq, "INIT\n"); - break; + switch (sb->iop_state) { + case 0x01: + seq_printf(seq, "INIT\n"); + break; - case 0x02: - seq_printf(seq, "RESET\n"); - break; + case 0x02: + seq_printf(seq, "RESET\n"); + break; - case 0x04: - seq_printf(seq, "HOLD\n"); - break; + case 0x04: + seq_printf(seq, "HOLD\n"); + break; - case 0x05: - seq_printf(seq, "READY\n"); - break; + case 0x05: + seq_printf(seq, "READY\n"); + break; - case 0x08: - seq_printf(seq, "OPERATIONAL\n"); - break; + case 0x08: + seq_printf(seq, "OPERATIONAL\n"); + break; - case 0x10: - seq_printf(seq, "FAILED\n"); - break; + case 0x10: + seq_printf(seq, "FAILED\n"); + break; - case 0x11: - seq_printf(seq, "FAULTED\n"); - break; + case 0x11: + seq_printf(seq, "FAULTED\n"); + break; - default: - seq_printf(seq, "Unknown\n"); - break; + default: + seq_printf(seq, "Unknown\n"); + break; } seq_printf(seq, "Messenger Type : "); - switch (c->status_block->msg_type) { - case 0x00: - seq_printf(seq, "Memory mapped\n"); - break; - case 0x01: - seq_printf(seq, "Memory mapped only\n"); - break; - case 0x02: - seq_printf(seq,"Remote only\n"); - break; - case 0x03: - seq_printf(seq, "Memory mapped and remote\n"); - break; - default: - seq_printf(seq, "Unknown\n"); + switch (sb->msg_type) { + case 0x00: + seq_printf(seq, "Memory mapped\n"); + break; + case 0x01: + seq_printf(seq, "Memory mapped only\n"); + break; + case 0x02: + seq_printf(seq, "Remote only\n"); + break; + case 0x03: + seq_printf(seq, "Memory mapped and remote\n"); + break; + default: + seq_printf(seq, "Unknown\n"); } seq_printf(seq, "Inbound Frame Size : %d bytes\n", - c->status_block->inbound_frame_size<<2); + sb->inbound_frame_size << 2); seq_printf(seq, "Max Inbound Frames : %d\n", - c->status_block->max_inbound_frames); + sb->max_inbound_frames); seq_printf(seq, "Current Inbound Frames : %d\n", - c->status_block->cur_inbound_frames); + sb->cur_inbound_frames); seq_printf(seq, "Max Outbound Frames : %d\n", - c->status_block->max_outbound_frames); + sb->max_outbound_frames); /* Spec doesn't say if NULL terminated or not... */ - memcpy(prodstr, c->status_block->product_id, 24); + memcpy(prodstr, sb->product_id, 24); prodstr[24] = '\0'; seq_printf(seq, "Product ID : %s\n", prodstr); seq_printf(seq, "Expected LCT Size : %d bytes\n", - c->status_block->expected_lct_size); + sb->expected_lct_size); seq_printf(seq, "IOP Capabilities\n"); seq_printf(seq, " Context Field Size Support : "); - switch (c->status_block->iop_capabilities & 0x0000003) { - case 0: - seq_printf(seq, "Supports only 32-bit context fields\n"); - break; - case 1: - seq_printf(seq, "Supports only 64-bit context fields\n"); - break; - case 2: - seq_printf(seq, "Supports 32-bit and 64-bit context fields, " - "but not concurrently\n"); - break; - case 3: - seq_printf(seq, "Supports 32-bit and 64-bit context fields " - "concurrently\n"); - break; - default: - seq_printf(seq, "0x%08x\n",c->status_block->iop_capabilities); + switch (sb->iop_capabilities & 0x0000003) { + case 0: + seq_printf(seq, "Supports only 32-bit context fields\n"); + break; + case 1: + seq_printf(seq, "Supports only 64-bit context fields\n"); + break; + case 2: + seq_printf(seq, "Supports 32-bit and 64-bit context fields, " + "but not concurrently\n"); + break; + case 3: + seq_printf(seq, "Supports 32-bit and 64-bit context fields " + "concurrently\n"); + break; + default: + seq_printf(seq, "0x%08x\n", sb->iop_capabilities); } seq_printf(seq, " Current Context Field Size : "); - switch (c->status_block->iop_capabilities & 0x0000000C) { - case 0: - seq_printf(seq, "not configured\n"); - break; - case 4: - seq_printf(seq, "Supports only 32-bit context fields\n"); - break; - case 8: - seq_printf(seq, "Supports only 64-bit context fields\n"); - break; - case 12: - seq_printf(seq, "Supports both 32-bit or 64-bit context fields " - "concurrently\n"); - break; - default: - seq_printf(seq, "\n"); + switch (sb->iop_capabilities & 0x0000000C) { + case 0: + seq_printf(seq, "not configured\n"); + break; + case 4: + seq_printf(seq, "Supports only 32-bit context fields\n"); + break; + case 8: + seq_printf(seq, "Supports only 64-bit context fields\n"); + break; + case 12: + seq_printf(seq, "Supports both 32-bit or 64-bit context fields " + "concurrently\n"); + break; + default: + seq_printf(seq, "\n"); } seq_printf(seq, " Inbound Peer Support : %s\n", - (c->status_block->iop_capabilities & 0x00000010) ? "Supported" : "Not supported"); + (sb-> + iop_capabilities & 0x00000010) ? "Supported" : + "Not supported"); seq_printf(seq, " Outbound Peer Support : %s\n", - (c->status_block->iop_capabilities & 0x00000020) ? "Supported" : "Not supported"); + (sb-> + iop_capabilities & 0x00000020) ? "Supported" : + "Not supported"); seq_printf(seq, " Peer to Peer Support : %s\n", - (c->status_block->iop_capabilities & 0x00000040) ? "Supported" : "Not supported"); + (sb-> + iop_capabilities & 0x00000040) ? "Supported" : + "Not supported"); seq_printf(seq, "Desired private memory size : %d kB\n", - c->status_block->desired_mem_size>>10); + sb->desired_mem_size >> 10); seq_printf(seq, "Allocated private memory size : %d kB\n", - c->status_block->current_mem_size>>10); + sb->current_mem_size >> 10); seq_printf(seq, "Private memory base address : %0#10x\n", - c->status_block->current_mem_base); + sb->current_mem_base); seq_printf(seq, "Desired private I/O size : %d kB\n", - c->status_block->desired_io_size>>10); + sb->desired_io_size >> 10); seq_printf(seq, "Allocated private I/O size : %d kB\n", - c->status_block->current_io_size>>10); + sb->current_io_size >> 10); seq_printf(seq, "Private I/O base address : %0#10x\n", - c->status_block->current_io_base); + sb->current_io_base); return 0; } -int i2o_proc_read_hw(char *buf, char **start, off_t offset, int len, - int *eof, void *data) +int i2o_seq_show_hw(struct seq_file *seq, void *v) { - struct i2o_controller *c = (struct i2o_controller*)data; + struct i2o_controller *c = (struct i2o_controller *)seq->private; static u32 work32[5]; - static u8 *work8 = (u8*)work32; - static u16 *work16 = (u16*)work32; + static u8 *work8 = (u8 *) work32; + static u16 *work16 = (u16 *) work32; int token; u32 hwcap; - static char *cpu_table[] = - { + static char *cpu_table[] = { "Intel 80960 series", "AMD2900 series", "Motorola 68000 series", @@ -773,397 +738,350 @@ int i2o_proc_read_hw(char *buf, char **s "Intel x86 series" }; - spin_lock(&i2o_proc_lock); - - len = 0; - - token = i2o_query_scalar(c, ADAPTER_TID, 0x0000, -1, &work32, sizeof(work32)); + token = + i2o_parm_field_get(c->exec, 0x0000, -1, &work32, sizeof(work32)); if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0000 IOP Hardware"); - spin_unlock(&i2o_proc_lock); - return len; + i2o_report_query_status(seq, token, "0x0000 IOP Hardware"); + return 0; } - len += sprintf(buf+len, "I2O Vendor ID : %0#6x\n", work16[0]); - len += sprintf(buf+len, "Product ID : %0#6x\n", work16[1]); - len += sprintf(buf+len, "CPU : "); - if(work8[16] > 8) - len += sprintf(buf+len, "Unknown\n"); + seq_printf(seq, "I2O Vendor ID : %0#6x\n", work16[0]); + seq_printf(seq, "Product ID : %0#6x\n", work16[1]); + seq_printf(seq, "CPU : "); + if (work8[16] > 8) + seq_printf(seq, "Unknown\n"); else - len += sprintf(buf+len, "%s\n", cpu_table[work8[16]]); + seq_printf(seq, "%s\n", cpu_table[work8[16]]); /* Anyone using ProcessorVersion? */ - - len += sprintf(buf+len, "RAM : %dkB\n", work32[1]>>10); - len += sprintf(buf+len, "Non-Volatile Mem : %dkB\n", work32[2]>>10); - hwcap = work32[3]; - len += sprintf(buf+len, "Capabilities : 0x%08x\n", hwcap); - len += sprintf(buf+len, " [%s] Self booting\n", - (hwcap&0x00000001) ? "+" : "-"); - len += sprintf(buf+len, " [%s] Upgradable IRTOS\n", - (hwcap&0x00000002) ? "+" : "-"); - len += sprintf(buf+len, " [%s] Supports downloading DDMs\n", - (hwcap&0x00000004) ? "+" : "-"); - len += sprintf(buf+len, " [%s] Supports installing DDMs\n", - (hwcap&0x00000008) ? "+" : "-"); - len += sprintf(buf+len, " [%s] Battery-backed RAM\n", - (hwcap&0x00000010) ? "+" : "-"); + seq_printf(seq, "RAM : %dkB\n", work32[1] >> 10); + seq_printf(seq, "Non-Volatile Mem : %dkB\n", work32[2] >> 10); - spin_unlock(&i2o_proc_lock); + hwcap = work32[3]; + seq_printf(seq, "Capabilities : 0x%08x\n", hwcap); + seq_printf(seq, " [%s] Self booting\n", + (hwcap & 0x00000001) ? "+" : "-"); + seq_printf(seq, " [%s] Upgradable IRTOS\n", + (hwcap & 0x00000002) ? "+" : "-"); + seq_printf(seq, " [%s] Supports downloading DDMs\n", + (hwcap & 0x00000004) ? "+" : "-"); + seq_printf(seq, " [%s] Supports installing DDMs\n", + (hwcap & 0x00000008) ? "+" : "-"); + seq_printf(seq, " [%s] Battery-backed RAM\n", + (hwcap & 0x00000010) ? "+" : "-"); - return len; + return 0; } - /* Executive group 0003h - Executing DDM List (table) */ -int i2o_proc_read_ddm_table(char *buf, char **start, off_t offset, int len, - int *eof, void *data) +int i2o_seq_show_ddm_table(struct seq_file *seq, void *v) { - struct i2o_controller *c = (struct i2o_controller*)data; + struct i2o_controller *c = (struct i2o_controller *)seq->private; int token; int i; typedef struct _i2o_exec_execute_ddm_table { u16 ddm_tid; - u8 module_type; - u8 reserved; + u8 module_type; + u8 reserved; u16 i2o_vendor_id; u16 module_id; - u8 module_name_version[28]; + u8 module_name_version[28]; u32 data_size; u32 code_size; } i2o_exec_execute_ddm_table; - struct - { + struct { u16 result_count; u16 pad; u16 block_size; - u8 block_status; - u8 error_info_size; + u8 block_status; + u8 error_info_size; u16 row_count; u16 more_flag; - i2o_exec_execute_ddm_table ddm_table[MAX_I2O_MODULES]; + i2o_exec_execute_ddm_table ddm_table[I2O_MAX_MODULES]; } *result; i2o_exec_execute_ddm_table ddm_table; result = kmalloc(sizeof(*result), GFP_KERNEL); - if(!result) + if (!result) return -ENOMEM; - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - c, ADAPTER_TID, - 0x0003, -1, - NULL, 0, - result, sizeof(*result)); + token = i2o_parm_table_get(c->exec, I2O_PARAMS_TABLE_GET, 0x0003, -1, + NULL, 0, result, sizeof(*result)); if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0003 Executing DDM List"); + i2o_report_query_status(seq, token, + "0x0003 Executing DDM List"); goto out; } - len += sprintf(buf+len, "Tid Module_type Vendor Mod_id Module_name Vrs Data_size Code_size\n"); - ddm_table=result->ddm_table[0]; + seq_printf(seq, + "Tid Module_type Vendor Mod_id Module_name Vrs Data_size Code_size\n"); + ddm_table = result->ddm_table[0]; - for(i=0; i < result->row_count; ddm_table=result->ddm_table[++i]) - { - len += sprintf(buf+len, "0x%03x ", ddm_table.ddm_tid & 0xFFF); + for (i = 0; i < result->row_count; ddm_table = result->ddm_table[++i]) { + seq_printf(seq, "0x%03x ", ddm_table.ddm_tid & 0xFFF); - switch(ddm_table.module_type) - { + switch (ddm_table.module_type) { case 0x01: - len += sprintf(buf+len, "Downloaded DDM "); - break; + seq_printf(seq, "Downloaded DDM "); + break; case 0x22: - len += sprintf(buf+len, "Embedded DDM "); + seq_printf(seq, "Embedded DDM "); break; default: - len += sprintf(buf+len, " "); + seq_printf(seq, " "); } - len += sprintf(buf+len, "%-#7x", ddm_table.i2o_vendor_id); - len += sprintf(buf+len, "%-#8x", ddm_table.module_id); - len += sprintf(buf+len, "%-29s", chtostr(ddm_table.module_name_version, 28)); - len += sprintf(buf+len, "%9d ", ddm_table.data_size); - len += sprintf(buf+len, "%8d", ddm_table.code_size); + seq_printf(seq, "%-#7x", ddm_table.i2o_vendor_id); + seq_printf(seq, "%-#8x", ddm_table.module_id); + seq_printf(seq, "%-29s", + chtostr(ddm_table.module_name_version, 28)); + seq_printf(seq, "%9d ", ddm_table.data_size); + seq_printf(seq, "%8d", ddm_table.code_size); - len += sprintf(buf+len, "\n"); + seq_printf(seq, "\n"); } -out: - spin_unlock(&i2o_proc_lock); + out: kfree(result); - return len; + return 0; } - /* Executive group 0004h - Driver Store (scalar) */ -int i2o_proc_read_driver_store(char *buf, char **start, off_t offset, int len, - int *eof, void *data) +int i2o_seq_show_driver_store(struct seq_file *seq, void *v) { - struct i2o_controller *c = (struct i2o_controller*)data; + struct i2o_controller *c = (struct i2o_controller *)seq->private; u32 work32[8]; int token; - spin_lock(&i2o_proc_lock); - - len = 0; - - token = i2o_query_scalar(c, ADAPTER_TID, 0x0004, -1, &work32, sizeof(work32)); + token = + i2o_parm_field_get(c->exec, 0x0004, -1, &work32, sizeof(work32)); if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0004 Driver Store"); - spin_unlock(&i2o_proc_lock); - return len; + i2o_report_query_status(seq, token, "0x0004 Driver Store"); + return 0; } - len += sprintf(buf+len, "Module limit : %d\n" - "Module count : %d\n" - "Current space : %d kB\n" - "Free space : %d kB\n", - work32[0], work32[1], work32[2]>>10, work32[3]>>10); - - spin_unlock(&i2o_proc_lock); + seq_printf(seq, "Module limit : %d\n" + "Module count : %d\n" + "Current space : %d kB\n" + "Free space : %d kB\n", + work32[0], work32[1], work32[2] >> 10, work32[3] >> 10); - return len; + return 0; } - /* Executive group 0005h - Driver Store Table (table) */ -int i2o_proc_read_drivers_stored(char *buf, char **start, off_t offset, - int len, int *eof, void *data) +int i2o_seq_show_drivers_stored(struct seq_file *seq, void *v) { typedef struct _i2o_driver_store { u16 stored_ddm_index; - u8 module_type; - u8 reserved; + u8 module_type; + u8 reserved; u16 i2o_vendor_id; u16 module_id; - u8 module_name_version[28]; - u8 date[8]; + u8 module_name_version[28]; + u8 date[8]; u32 module_size; u32 mpb_size; u32 module_flags; } i2o_driver_store_table; - struct i2o_controller *c = (struct i2o_controller*)data; + struct i2o_controller *c = (struct i2o_controller *)seq->private; int token; int i; - typedef struct - { + typedef struct { u16 result_count; u16 pad; u16 block_size; - u8 block_status; - u8 error_info_size; + u8 block_status; + u8 error_info_size; u16 row_count; u16 more_flag; - i2o_driver_store_table dst[MAX_I2O_MODULES]; + i2o_driver_store_table dst[I2O_MAX_MODULES]; } i2o_driver_result_table; - + i2o_driver_result_table *result; i2o_driver_store_table *dst; - - len = 0; - result = kmalloc(sizeof(i2o_driver_result_table), GFP_KERNEL); - if(result == NULL) + if (result == NULL) return -ENOMEM; - spin_lock(&i2o_proc_lock); - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - c, ADAPTER_TID, 0x0005, -1, NULL, 0, - result, sizeof(*result)); + token = i2o_parm_table_get(c->exec, I2O_PARAMS_TABLE_GET, 0x0005, -1, + NULL, 0, result, sizeof(*result)); if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0005 DRIVER STORE TABLE"); - spin_unlock(&i2o_proc_lock); + i2o_report_query_status(seq, token, + "0x0005 DRIVER STORE TABLE"); kfree(result); - return len; + return 0; } - len += sprintf(buf+len, "# Module_type Vendor Mod_id Module_name Vrs" - "Date Mod_size Par_size Flags\n"); - for(i=0, dst=&result->dst[0]; i < result->row_count; dst=&result->dst[++i]) - { - len += sprintf(buf+len, "%-3d", dst->stored_ddm_index); - switch(dst->module_type) - { + seq_printf(seq, + "# Module_type Vendor Mod_id Module_name Vrs" + "Date Mod_size Par_size Flags\n"); + for (i = 0, dst = &result->dst[0]; i < result->row_count; + dst = &result->dst[++i]) { + seq_printf(seq, "%-3d", dst->stored_ddm_index); + switch (dst->module_type) { case 0x01: - len += sprintf(buf+len, "Downloaded DDM "); - break; + seq_printf(seq, "Downloaded DDM "); + break; case 0x22: - len += sprintf(buf+len, "Embedded DDM "); + seq_printf(seq, "Embedded DDM "); break; default: - len += sprintf(buf+len, " "); + seq_printf(seq, " "); } #if 0 - if(c->i2oversion == 0x02) - len += sprintf(buf+len, "%-d", dst->module_state); + if (c->i2oversion == 0x02) + seq_printf(seq, "%-d", dst->module_state); #endif - len += sprintf(buf+len, "%-#7x", dst->i2o_vendor_id); - len += sprintf(buf+len, "%-#8x", dst->module_id); - len += sprintf(buf+len, "%-29s", chtostr(dst->module_name_version,28)); - len += sprintf(buf+len, "%-9s", chtostr(dst->date,8)); - len += sprintf(buf+len, "%8d ", dst->module_size); - len += sprintf(buf+len, "%8d ", dst->mpb_size); - len += sprintf(buf+len, "0x%04x", dst->module_flags); + seq_printf(seq, "%-#7x", dst->i2o_vendor_id); + seq_printf(seq, "%-#8x", dst->module_id); + seq_printf(seq, "%-29s", chtostr(dst->module_name_version, 28)); + seq_printf(seq, "%-9s", chtostr(dst->date, 8)); + seq_printf(seq, "%8d ", dst->module_size); + seq_printf(seq, "%8d ", dst->mpb_size); + seq_printf(seq, "0x%04x", dst->module_flags); #if 0 - if(c->i2oversion == 0x02) - len += sprintf(buf+len, "%d", - dst->notification_level); + if (c->i2oversion == 0x02) + seq_printf(seq, "%d", dst->notification_level); #endif - len += sprintf(buf+len, "\n"); + seq_printf(seq, "\n"); } - spin_unlock(&i2o_proc_lock); kfree(result); - return len; + return 0; } - /* Generic group F000h - Params Descriptor (table) */ -int i2o_proc_read_groups(char *buf, char **start, off_t offset, int len, - int *eof, void *data) +int i2o_seq_show_groups(struct seq_file *seq, void *v) { - struct i2o_device *d = (struct i2o_device*)data; + struct i2o_device *d = (struct i2o_device *)seq->private; int token; int i; u8 properties; - typedef struct _i2o_group_info - { + typedef struct _i2o_group_info { u16 group_number; u16 field_count; u16 row_count; - u8 properties; - u8 reserved; + u8 properties; + u8 reserved; } i2o_group_info; - struct - { + struct { u16 result_count; u16 pad; u16 block_size; - u8 block_status; - u8 error_info_size; + u8 block_status; + u8 error_info_size; u16 row_count; u16 more_flag; i2o_group_info group[256]; } *result; result = kmalloc(sizeof(*result), GFP_KERNEL); - if(!result) + if (!result) return -ENOMEM; - spin_lock(&i2o_proc_lock); - - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, 0xF000, -1, NULL, 0, - result, sizeof(*result)); + token = i2o_parm_table_get(d, I2O_PARAMS_TABLE_GET, 0xF000, -1, NULL, 0, + result, sizeof(*result)); if (token < 0) { - len = i2o_report_query_status(buf+len, token, "0xF000 Params Descriptor"); + i2o_report_query_status(seq, token, "0xF000 Params Descriptor"); goto out; } - len += sprintf(buf+len, "# Group FieldCount RowCount Type Add Del Clear\n"); + seq_printf(seq, + "# Group FieldCount RowCount Type Add Del Clear\n"); - for (i=0; i < result->row_count; i++) - { - len += sprintf(buf+len, "%-3d", i); - len += sprintf(buf+len, "0x%04X ", result->group[i].group_number); - len += sprintf(buf+len, "%10d ", result->group[i].field_count); - len += sprintf(buf+len, "%8d ", result->group[i].row_count); + for (i = 0; i < result->row_count; i++) { + seq_printf(seq, "%-3d", i); + seq_printf(seq, "0x%04X ", result->group[i].group_number); + seq_printf(seq, "%10d ", result->group[i].field_count); + seq_printf(seq, "%8d ", result->group[i].row_count); properties = result->group[i].properties; - if (properties & 0x1) len += sprintf(buf+len, "Table "); - else len += sprintf(buf+len, "Scalar "); - if (properties & 0x2) len += sprintf(buf+len, " + "); - else len += sprintf(buf+len, " - "); - if (properties & 0x4) len += sprintf(buf+len, " + "); - else len += sprintf(buf+len, " - "); - if (properties & 0x8) len += sprintf(buf+len, " + "); - else len += sprintf(buf+len, " - "); + if (properties & 0x1) + seq_printf(seq, "Table "); + else + seq_printf(seq, "Scalar "); + if (properties & 0x2) + seq_printf(seq, " + "); + else + seq_printf(seq, " - "); + if (properties & 0x4) + seq_printf(seq, " + "); + else + seq_printf(seq, " - "); + if (properties & 0x8) + seq_printf(seq, " + "); + else + seq_printf(seq, " - "); - len += sprintf(buf+len, "\n"); + seq_printf(seq, "\n"); } if (result->more_flag) - len += sprintf(buf+len, "There is more...\n"); -out: - spin_unlock(&i2o_proc_lock); + seq_printf(seq, "There is more...\n"); + out: kfree(result); - return len; + return 0; } - /* Generic group F001h - Physical Device Table (table) */ -int i2o_proc_read_phys_device(char *buf, char **start, off_t offset, int len, - int *eof, void *data) +int i2o_seq_show_phys_device(struct seq_file *seq, void *v) { - struct i2o_device *d = (struct i2o_device*)data; + struct i2o_device *d = (struct i2o_device *)seq->private; int token; int i; - struct - { + struct { u16 result_count; u16 pad; u16 block_size; - u8 block_status; - u8 error_info_size; + u8 block_status; + u8 error_info_size; u16 row_count; u16 more_flag; u32 adapter_id[64]; } result; - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, - 0xF001, -1, NULL, 0, - &result, sizeof(result)); + token = i2o_parm_table_get(d, I2O_PARAMS_TABLE_GET, 0xF001, -1, NULL, 0, + &result, sizeof(result)); if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF001 Physical Device Table"); - spin_unlock(&i2o_proc_lock); - return len; + i2o_report_query_status(seq, token, + "0xF001 Physical Device Table"); + return 0; } if (result.row_count) - len += sprintf(buf+len, "# AdapterId\n"); + seq_printf(seq, "# AdapterId\n"); - for (i=0; i < result.row_count; i++) - { - len += sprintf(buf+len, "%-2d", i); - len += sprintf(buf+len, "%#7x\n", result.adapter_id[i]); + for (i = 0; i < result.row_count; i++) { + seq_printf(seq, "%-2d", i); + seq_printf(seq, "%#7x\n", result.adapter_id[i]); } if (result.more_flag) - len += sprintf(buf+len, "There is more...\n"); + seq_printf(seq, "There is more...\n"); - spin_unlock(&i2o_proc_lock); - return len; + return 0; } /* Generic group F002h - Claimed Table (table) */ -int i2o_proc_read_claimed(char *buf, char **start, off_t offset, int len, - int *eof, void *data) +int i2o_seq_show_claimed(struct seq_file *seq, void *v) { - struct i2o_device *d = (struct i2o_device*)data; + struct i2o_device *d = (struct i2o_device *)seq->private; int token; int i; @@ -1171,434 +1089,356 @@ int i2o_proc_read_claimed(char *buf, cha u16 result_count; u16 pad; u16 block_size; - u8 block_status; - u8 error_info_size; + u8 block_status; + u8 error_info_size; u16 row_count; u16 more_flag; u16 claimed_tid[64]; } result; - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, - 0xF002, -1, NULL, 0, - &result, sizeof(result)); + token = i2o_parm_table_get(d, I2O_PARAMS_TABLE_GET, 0xF002, -1, NULL, 0, + &result, sizeof(result)); if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF002 Claimed Table"); - spin_unlock(&i2o_proc_lock); - return len; + i2o_report_query_status(seq, token, "0xF002 Claimed Table"); + return 0; } if (result.row_count) - len += sprintf(buf+len, "# ClaimedTid\n"); + seq_printf(seq, "# ClaimedTid\n"); - for (i=0; i < result.row_count; i++) - { - len += sprintf(buf+len, "%-2d", i); - len += sprintf(buf+len, "%#7x\n", result.claimed_tid[i]); + for (i = 0; i < result.row_count; i++) { + seq_printf(seq, "%-2d", i); + seq_printf(seq, "%#7x\n", result.claimed_tid[i]); } if (result.more_flag) - len += sprintf(buf+len, "There is more...\n"); + seq_printf(seq, "There is more...\n"); - spin_unlock(&i2o_proc_lock); - return len; + return 0; } /* Generic group F003h - User Table (table) */ -int i2o_proc_read_users(char *buf, char **start, off_t offset, int len, - int *eof, void *data) +int i2o_seq_show_users(struct seq_file *seq, void *v) { - struct i2o_device *d = (struct i2o_device*)data; + struct i2o_device *d = (struct i2o_device *)seq->private; int token; int i; - typedef struct _i2o_user_table - { + typedef struct _i2o_user_table { u16 instance; u16 user_tid; u8 claim_type; - u8 reserved1; - u16 reserved2; + u8 reserved1; + u16 reserved2; } i2o_user_table; - struct - { + struct { u16 result_count; u16 pad; u16 block_size; - u8 block_status; - u8 error_info_size; + u8 block_status; + u8 error_info_size; u16 row_count; u16 more_flag; i2o_user_table user[64]; } *result; result = kmalloc(sizeof(*result), GFP_KERNEL); - if(!result) + if (!result) return -ENOMEM; - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, - 0xF003, -1, NULL, 0, - result, sizeof(*result)); + token = i2o_parm_table_get(d, I2O_PARAMS_TABLE_GET, 0xF003, -1, NULL, 0, + result, sizeof(*result)); if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF003 User Table"); + i2o_report_query_status(seq, token, "0xF003 User Table"); goto out; } - len += sprintf(buf+len, "# Instance UserTid ClaimType\n"); + seq_printf(seq, "# Instance UserTid ClaimType\n"); - for(i=0; i < result->row_count; i++) - { - len += sprintf(buf+len, "%-3d", i); - len += sprintf(buf+len, "%#8x ", result->user[i].instance); - len += sprintf(buf+len, "%#7x ", result->user[i].user_tid); - len += sprintf(buf+len, "%#9x\n", result->user[i].claim_type); + for (i = 0; i < result->row_count; i++) { + seq_printf(seq, "%-3d", i); + seq_printf(seq, "%#8x ", result->user[i].instance); + seq_printf(seq, "%#7x ", result->user[i].user_tid); + seq_printf(seq, "%#9x\n", result->user[i].claim_type); } if (result->more_flag) - len += sprintf(buf+len, "There is more...\n"); -out: - spin_unlock(&i2o_proc_lock); + seq_printf(seq, "There is more...\n"); + out: kfree(result); - return len; + return 0; } /* Generic group F005h - Private message extensions (table) (optional) */ -int i2o_proc_read_priv_msgs(char *buf, char **start, off_t offset, int len, - int *eof, void *data) +int i2o_seq_show_priv_msgs(struct seq_file *seq, void *v) { - struct i2o_device *d = (struct i2o_device*)data; + struct i2o_device *d = (struct i2o_device *)seq->private; int token; int i; - typedef struct _i2o_private - { + typedef struct _i2o_private { u16 ext_instance; u16 organization_id; u16 x_function_code; } i2o_private; - struct - { + struct { u16 result_count; u16 pad; u16 block_size; - u8 block_status; - u8 error_info_size; + u8 block_status; + u8 error_info_size; u16 row_count; u16 more_flag; i2o_private extension[64]; } result; - spin_lock(&i2o_proc_lock); - - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, - 0xF000, -1, - NULL, 0, - &result, sizeof(result)); + token = i2o_parm_table_get(d, I2O_PARAMS_TABLE_GET, 0xF000, -1, NULL, 0, + &result, sizeof(result)); if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF005 Private Message Extensions (optional)"); - spin_unlock(&i2o_proc_lock); - return len; + i2o_report_query_status(seq, token, + "0xF005 Private Message Extensions (optional)"); + return 0; } - - len += sprintf(buf+len, "Instance# OrgId FunctionCode\n"); - for(i=0; i < result.row_count; i++) - { - len += sprintf(buf+len, "%0#9x ", result.extension[i].ext_instance); - len += sprintf(buf+len, "%0#6x ", result.extension[i].organization_id); - len += sprintf(buf+len, "%0#6x", result.extension[i].x_function_code); + seq_printf(seq, "Instance# OrgId FunctionCode\n"); - len += sprintf(buf+len, "\n"); - } + for (i = 0; i < result.row_count; i++) { + seq_printf(seq, "%0#9x ", result.extension[i].ext_instance); + seq_printf(seq, "%0#6x ", result.extension[i].organization_id); + seq_printf(seq, "%0#6x", result.extension[i].x_function_code); - if(result.more_flag) - len += sprintf(buf+len, "There is more...\n"); + seq_printf(seq, "\n"); + } - spin_unlock(&i2o_proc_lock); + if (result.more_flag) + seq_printf(seq, "There is more...\n"); - return len; + return 0; } - /* Generic group F006h - Authorized User Table (table) */ -int i2o_proc_read_authorized_users(char *buf, char **start, off_t offset, int len, - int *eof, void *data) +int i2o_seq_show_authorized_users(struct seq_file *seq, void *v) { - struct i2o_device *d = (struct i2o_device*)data; + struct i2o_device *d = (struct i2o_device *)seq->private; int token; int i; - struct - { + struct { u16 result_count; u16 pad; u16 block_size; - u8 block_status; - u8 error_info_size; + u8 block_status; + u8 error_info_size; u16 row_count; u16 more_flag; u32 alternate_tid[64]; } result; - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, - 0xF006, -1, - NULL, 0, - &result, sizeof(result)); + token = i2o_parm_table_get(d, I2O_PARAMS_TABLE_GET, 0xF006, -1, NULL, 0, + &result, sizeof(result)); if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF006 Autohorized User Table"); - spin_unlock(&i2o_proc_lock); - return len; + i2o_report_query_status(seq, token, + "0xF006 Autohorized User Table"); + return 0; } if (result.row_count) - len += sprintf(buf+len, "# AlternateTid\n"); + seq_printf(seq, "# AlternateTid\n"); - for(i=0; i < result.row_count; i++) - { - len += sprintf(buf+len, "%-2d", i); - len += sprintf(buf+len, "%#7x ", result.alternate_tid[i]); + for (i = 0; i < result.row_count; i++) { + seq_printf(seq, "%-2d", i); + seq_printf(seq, "%#7x ", result.alternate_tid[i]); } if (result.more_flag) - len += sprintf(buf+len, "There is more...\n"); + seq_printf(seq, "There is more...\n"); - spin_unlock(&i2o_proc_lock); - return len; + return 0; } - /* Generic group F100h - Device Identity (scalar) */ -int i2o_proc_read_dev_identity(char *buf, char **start, off_t offset, int len, - int *eof, void *data) +int i2o_seq_show_dev_identity(struct seq_file *seq, void *v) { - struct i2o_device *d = (struct i2o_device*)data; - static u32 work32[128]; // allow for "stuff" + up to 256 byte (max) serial number - // == (allow) 512d bytes (max) - static u16 *work16 = (u16*)work32; + struct i2o_device *d = (struct i2o_device *)seq->private; + static u32 work32[128]; // allow for "stuff" + up to 256 byte (max) serial number + // == (allow) 512d bytes (max) + static u16 *work16 = (u16 *) work32; int token; - spin_lock(&i2o_proc_lock); - - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0xF100, -1, - &work32, sizeof(work32)); + token = i2o_parm_field_get(d, 0xF100, -1, &work32, sizeof(work32)); if (token < 0) { - len += i2o_report_query_status(buf+len, token ,"0xF100 Device Identity"); - spin_unlock(&i2o_proc_lock); - return len; + i2o_report_query_status(seq, token, "0xF100 Device Identity"); + return 0; } - - len += sprintf(buf, "Device Class : %s\n", i2o_get_class_name(work16[0])); - len += sprintf(buf+len, "Owner TID : %0#5x\n", work16[2]); - len += sprintf(buf+len, "Parent TID : %0#5x\n", work16[3]); - len += sprintf(buf+len, "Vendor info : %s\n", chtostr((u8 *)(work32+2), 16)); - len += sprintf(buf+len, "Product info : %s\n", chtostr((u8 *)(work32+6), 16)); - len += sprintf(buf+len, "Description : %s\n", chtostr((u8 *)(work32+10), 16)); - len += sprintf(buf+len, "Product rev. : %s\n", chtostr((u8 *)(work32+14), 8)); - - len += sprintf(buf+len, "Serial number : "); - len = print_serial_number(buf, len, - (u8*)(work32+16), - /* allow for SNLen plus - * possible trailing '\0' - */ - sizeof(work32)-(16*sizeof(u32))-2 - ); - len += sprintf(buf+len, "\n"); - spin_unlock(&i2o_proc_lock); + seq_printf(seq, "Device Class : %s\n", i2o_get_class_name(work16[0])); + seq_printf(seq, "Owner TID : %0#5x\n", work16[2]); + seq_printf(seq, "Parent TID : %0#5x\n", work16[3]); + seq_printf(seq, "Vendor info : %s\n", + chtostr((u8 *) (work32 + 2), 16)); + seq_printf(seq, "Product info : %s\n", + chtostr((u8 *) (work32 + 6), 16)); + seq_printf(seq, "Description : %s\n", + chtostr((u8 *) (work32 + 10), 16)); + seq_printf(seq, "Product rev. : %s\n", + chtostr((u8 *) (work32 + 14), 8)); + + seq_printf(seq, "Serial number : "); + print_serial_number(seq, (u8 *) (work32 + 16), + /* allow for SNLen plus + * possible trailing '\0' + */ + sizeof(work32) - (16 * sizeof(u32)) - 2); + seq_printf(seq, "\n"); - return len; + return 0; } - -int i2o_proc_read_dev_name(char *buf, char **start, off_t offset, int len, - int *eof, void *data) +int i2o_seq_show_dev_name(struct seq_file *seq, void *v) { - struct i2o_device *d = (struct i2o_device*)data; - - if ( d->dev_name[0] == '\0' ) - return 0; + struct i2o_device *d = (struct i2o_device *)seq->private; - len = sprintf(buf, "%s\n", d->dev_name); + seq_printf(seq, "%s\n", d->device.bus_id); - return len; + return 0; } - /* Generic group F101h - DDM Identity (scalar) */ -int i2o_proc_read_ddm_identity(char *buf, char **start, off_t offset, int len, - int *eof, void *data) +int i2o_seq_show_ddm_identity(struct seq_file *seq, void *v) { - struct i2o_device *d = (struct i2o_device*)data; + struct i2o_device *d = (struct i2o_device *)seq->private; int token; - struct - { + struct { u16 ddm_tid; u8 module_name[24]; u8 module_rev[8]; u8 sn_format; u8 serial_number[12]; - u8 pad[256]; // allow up to 256 byte (max) serial number - } result; + u8 pad[256]; // allow up to 256 byte (max) serial number + } result; - spin_lock(&i2o_proc_lock); - - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0xF101, -1, - &result, sizeof(result)); + token = i2o_parm_field_get(d, 0xF101, -1, &result, sizeof(result)); if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF101 DDM Identity"); - spin_unlock(&i2o_proc_lock); - return len; + i2o_report_query_status(seq, token, "0xF101 DDM Identity"); + return 0; } - len += sprintf(buf, "Registering DDM TID : 0x%03x\n", result.ddm_tid); - len += sprintf(buf+len, "Module name : %s\n", chtostr(result.module_name, 24)); - len += sprintf(buf+len, "Module revision : %s\n", chtostr(result.module_rev, 8)); - - len += sprintf(buf+len, "Serial number : "); - len = print_serial_number(buf, len, result.serial_number, sizeof(result)-36); - /* allow for SNLen plus possible trailing '\0' */ - - len += sprintf(buf+len, "\n"); + seq_printf(seq, "Registering DDM TID : 0x%03x\n", result.ddm_tid); + seq_printf(seq, "Module name : %s\n", + chtostr(result.module_name, 24)); + seq_printf(seq, "Module revision : %s\n", + chtostr(result.module_rev, 8)); + + seq_printf(seq, "Serial number : "); + print_serial_number(seq, result.serial_number, sizeof(result) - 36); + /* allow for SNLen plus possible trailing '\0' */ - spin_unlock(&i2o_proc_lock); + seq_printf(seq, "\n"); - return len; + return 0; } /* Generic group F102h - User Information (scalar) */ -int i2o_proc_read_uinfo(char *buf, char **start, off_t offset, int len, - int *eof, void *data) +int i2o_seq_show_uinfo(struct seq_file *seq, void *v) { - struct i2o_device *d = (struct i2o_device*)data; + struct i2o_device *d = (struct i2o_device *)seq->private; int token; - struct - { + struct { u8 device_name[64]; u8 service_name[64]; u8 physical_location[64]; u8 instance_number[4]; } result; - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0xF102, -1, - &result, sizeof(result)); + token = i2o_parm_field_get(d, 0xF102, -1, &result, sizeof(result)); if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF102 User Information"); - spin_unlock(&i2o_proc_lock); - return len; + i2o_report_query_status(seq, token, "0xF102 User Information"); + return 0; } - len += sprintf(buf, "Device name : %s\n", chtostr(result.device_name, 64)); - len += sprintf(buf+len, "Service name : %s\n", chtostr(result.service_name, 64)); - len += sprintf(buf+len, "Physical name : %s\n", chtostr(result.physical_location, 64)); - len += sprintf(buf+len, "Instance number : %s\n", chtostr(result.instance_number, 4)); + seq_printf(seq, "Device name : %s\n", + chtostr(result.device_name, 64)); + seq_printf(seq, "Service name : %s\n", + chtostr(result.service_name, 64)); + seq_printf(seq, "Physical name : %s\n", + chtostr(result.physical_location, 64)); + seq_printf(seq, "Instance number : %s\n", + chtostr(result.instance_number, 4)); - spin_unlock(&i2o_proc_lock); - return len; + return 0; } /* Generic group F103h - SGL Operating Limits (scalar) */ -int i2o_proc_read_sgl_limits(char *buf, char **start, off_t offset, int len, - int *eof, void *data) +int i2o_seq_show_sgl_limits(struct seq_file *seq, void *v) { - struct i2o_device *d = (struct i2o_device*)data; + struct i2o_device *d = (struct i2o_device *)seq->private; static u32 work32[12]; - static u16 *work16 = (u16 *)work32; - static u8 *work8 = (u8 *)work32; + static u16 *work16 = (u16 *) work32; + static u8 *work8 = (u8 *) work32; int token; - spin_lock(&i2o_proc_lock); - - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0xF103, -1, - &work32, sizeof(work32)); + token = i2o_parm_field_get(d, 0xF103, -1, &work32, sizeof(work32)); if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF103 SGL Operating Limits"); - spin_unlock(&i2o_proc_lock); - return len; + i2o_report_query_status(seq, token, + "0xF103 SGL Operating Limits"); + return 0; } - len += sprintf(buf, "SGL chain size : %d\n", work32[0]); - len += sprintf(buf+len, "Max SGL chain size : %d\n", work32[1]); - len += sprintf(buf+len, "SGL chain size target : %d\n", work32[2]); - len += sprintf(buf+len, "SGL frag count : %d\n", work16[6]); - len += sprintf(buf+len, "Max SGL frag count : %d\n", work16[7]); - len += sprintf(buf+len, "SGL frag count target : %d\n", work16[8]); + seq_printf(seq, "SGL chain size : %d\n", work32[0]); + seq_printf(seq, "Max SGL chain size : %d\n", work32[1]); + seq_printf(seq, "SGL chain size target : %d\n", work32[2]); + seq_printf(seq, "SGL frag count : %d\n", work16[6]); + seq_printf(seq, "Max SGL frag count : %d\n", work16[7]); + seq_printf(seq, "SGL frag count target : %d\n", work16[8]); +/* FIXME if (d->i2oversion == 0x02) { - len += sprintf(buf+len, "SGL data alignment : %d\n", work16[8]); - len += sprintf(buf+len, "SGL addr limit : %d\n", work8[20]); - len += sprintf(buf+len, "SGL addr sizes supported : "); - if (work8[21] & 0x01) - len += sprintf(buf+len, "32 bit "); - if (work8[21] & 0x02) - len += sprintf(buf+len, "64 bit "); - if (work8[21] & 0x04) - len += sprintf(buf+len, "96 bit "); - if (work8[21] & 0x08) - len += sprintf(buf+len, "128 bit "); - len += sprintf(buf+len, "\n"); +*/ + seq_printf(seq, "SGL data alignment : %d\n", work16[8]); + seq_printf(seq, "SGL addr limit : %d\n", work8[20]); + seq_printf(seq, "SGL addr sizes supported : "); + if (work8[21] & 0x01) + seq_printf(seq, "32 bit "); + if (work8[21] & 0x02) + seq_printf(seq, "64 bit "); + if (work8[21] & 0x04) + seq_printf(seq, "96 bit "); + if (work8[21] & 0x08) + seq_printf(seq, "128 bit "); + seq_printf(seq, "\n"); +/* } +*/ - spin_unlock(&i2o_proc_lock); - - return len; + return 0; } /* Generic group F200h - Sensors (scalar) */ -int i2o_proc_read_sensors(char *buf, char **start, off_t offset, int len, - int *eof, void *data) +int i2o_seq_show_sensors(struct seq_file *seq, void *v) { - struct i2o_device *d = (struct i2o_device*)data; + struct i2o_device *d = (struct i2o_device *)seq->private; int token; - struct - { + struct { u16 sensor_instance; - u8 component; + u8 component; u16 component_instance; - u8 sensor_class; - u8 sensor_type; - u8 scaling_exponent; + u8 sensor_class; + u8 sensor_type; + u8 scaling_exponent; u32 actual_reading; u32 minimum_reading; u32 low2lowcat_treshold; @@ -1615,1795 +1455,663 @@ int i2o_proc_read_sensors(char *buf, cha u32 hicat2high_treshold; u32 hi2hicat_treshold; u32 maximum_reading; - u8 sensor_state; + u8 sensor_state; u16 event_enable; } result; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0xF200, -1, - &result, sizeof(result)); + + token = i2o_parm_field_get(d, 0xF200, -1, &result, sizeof(result)); if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0xF200 Sensors (optional)"); - spin_unlock(&i2o_proc_lock); - return len; + i2o_report_query_status(seq, token, + "0xF200 Sensors (optional)"); + return 0; } - - len += sprintf(buf+len, "Sensor instance : %d\n", result.sensor_instance); - - len += sprintf(buf+len, "Component : %d = ", result.component); - switch (result.component) - { - case 0: len += sprintf(buf+len, "Other"); - break; - case 1: len += sprintf(buf+len, "Planar logic Board"); - break; - case 2: len += sprintf(buf+len, "CPU"); - break; - case 3: len += sprintf(buf+len, "Chassis"); - break; - case 4: len += sprintf(buf+len, "Power Supply"); - break; - case 5: len += sprintf(buf+len, "Storage"); - break; - case 6: len += sprintf(buf+len, "External"); - break; - } - len += sprintf(buf+len,"\n"); - - len += sprintf(buf+len, "Component instance : %d\n", result.component_instance); - len += sprintf(buf+len, "Sensor class : %s\n", - result.sensor_class ? "Analog" : "Digital"); - - len += sprintf(buf+len, "Sensor type : %d = ",result.sensor_type); - switch (result.sensor_type) - { - case 0: len += sprintf(buf+len, "Other\n"); - break; - case 1: len += sprintf(buf+len, "Thermal\n"); - break; - case 2: len += sprintf(buf+len, "DC voltage (DC volts)\n"); - break; - case 3: len += sprintf(buf+len, "AC voltage (AC volts)\n"); - break; - case 4: len += sprintf(buf+len, "DC current (DC amps)\n"); - break; - case 5: len += sprintf(buf+len, "AC current (AC volts)\n"); - break; - case 6: len += sprintf(buf+len, "Door open\n"); - break; - case 7: len += sprintf(buf+len, "Fan operational\n"); - break; - } - len += sprintf(buf+len, "Scaling exponent : %d\n", result.scaling_exponent); - len += sprintf(buf+len, "Actual reading : %d\n", result.actual_reading); - len += sprintf(buf+len, "Minimum reading : %d\n", result.minimum_reading); - len += sprintf(buf+len, "Low2LowCat treshold : %d\n", result.low2lowcat_treshold); - len += sprintf(buf+len, "LowCat2Low treshold : %d\n", result.lowcat2low_treshold); - len += sprintf(buf+len, "LowWarn2Low treshold : %d\n", result.lowwarn2low_treshold); - len += sprintf(buf+len, "Low2LowWarn treshold : %d\n", result.low2lowwarn_treshold); - len += sprintf(buf+len, "Norm2LowWarn treshold : %d\n", result.norm2lowwarn_treshold); - len += sprintf(buf+len, "LowWarn2Norm treshold : %d\n", result.lowwarn2norm_treshold); - len += sprintf(buf+len, "Nominal reading : %d\n", result.nominal_reading); - len += sprintf(buf+len, "HiWarn2Norm treshold : %d\n", result.hiwarn2norm_treshold); - len += sprintf(buf+len, "Norm2HiWarn treshold : %d\n", result.norm2hiwarn_treshold); - len += sprintf(buf+len, "High2HiWarn treshold : %d\n", result.high2hiwarn_treshold); - len += sprintf(buf+len, "HiWarn2High treshold : %d\n", result.hiwarn2high_treshold); - len += sprintf(buf+len, "HiCat2High treshold : %d\n", result.hicat2high_treshold); - len += sprintf(buf+len, "High2HiCat treshold : %d\n", result.hi2hicat_treshold); - len += sprintf(buf+len, "Maximum reading : %d\n", result.maximum_reading); + seq_printf(seq, "Sensor instance : %d\n", result.sensor_instance); - len += sprintf(buf+len, "Sensor state : %d = ", result.sensor_state); - switch (result.sensor_state) - { - case 0: len += sprintf(buf+len, "Normal\n"); - break; - case 1: len += sprintf(buf+len, "Abnormal\n"); - break; - case 2: len += sprintf(buf+len, "Unknown\n"); - break; - case 3: len += sprintf(buf+len, "Low Catastrophic (LoCat)\n"); - break; - case 4: len += sprintf(buf+len, "Low (Low)\n"); - break; - case 5: len += sprintf(buf+len, "Low Warning (LoWarn)\n"); - break; - case 6: len += sprintf(buf+len, "High Warning (HiWarn)\n"); - break; - case 7: len += sprintf(buf+len, "High (High)\n"); - break; - case 8: len += sprintf(buf+len, "High Catastrophic (HiCat)\n"); - break; - } - - len += sprintf(buf+len, "Event_enable : 0x%02X\n", result.event_enable); - len += sprintf(buf+len, " [%s] Operational state change. \n", - (result.event_enable & 0x01) ? "+" : "-" ); - len += sprintf(buf+len, " [%s] Low catastrophic. \n", - (result.event_enable & 0x02) ? "+" : "-" ); - len += sprintf(buf+len, " [%s] Low reading. \n", - (result.event_enable & 0x04) ? "+" : "-" ); - len += sprintf(buf+len, " [%s] Low warning. \n", - (result.event_enable & 0x08) ? "+" : "-" ); - len += sprintf(buf+len, " [%s] Change back to normal from out of range state. \n", - (result.event_enable & 0x10) ? "+" : "-" ); - len += sprintf(buf+len, " [%s] High warning. \n", - (result.event_enable & 0x20) ? "+" : "-" ); - len += sprintf(buf+len, " [%s] High reading. \n", - (result.event_enable & 0x40) ? "+" : "-" ); - len += sprintf(buf+len, " [%s] High catastrophic. \n", - (result.event_enable & 0x80) ? "+" : "-" ); + seq_printf(seq, "Component : %d = ", result.component); + switch (result.component) { + case 0: + seq_printf(seq, "Other"); + break; + case 1: + seq_printf(seq, "Planar logic Board"); + break; + case 2: + seq_printf(seq, "CPU"); + break; + case 3: + seq_printf(seq, "Chassis"); + break; + case 4: + seq_printf(seq, "Power Supply"); + break; + case 5: + seq_printf(seq, "Storage"); + break; + case 6: + seq_printf(seq, "External"); + break; + } + seq_printf(seq, "\n"); + + seq_printf(seq, "Component instance : %d\n", + result.component_instance); + seq_printf(seq, "Sensor class : %s\n", + result.sensor_class ? "Analog" : "Digital"); + + seq_printf(seq, "Sensor type : %d = ", result.sensor_type); + switch (result.sensor_type) { + case 0: + seq_printf(seq, "Other\n"); + break; + case 1: + seq_printf(seq, "Thermal\n"); + break; + case 2: + seq_printf(seq, "DC voltage (DC volts)\n"); + break; + case 3: + seq_printf(seq, "AC voltage (AC volts)\n"); + break; + case 4: + seq_printf(seq, "DC current (DC amps)\n"); + break; + case 5: + seq_printf(seq, "AC current (AC volts)\n"); + break; + case 6: + seq_printf(seq, "Door open\n"); + break; + case 7: + seq_printf(seq, "Fan operational\n"); + break; + } + + seq_printf(seq, "Scaling exponent : %d\n", + result.scaling_exponent); + seq_printf(seq, "Actual reading : %d\n", result.actual_reading); + seq_printf(seq, "Minimum reading : %d\n", result.minimum_reading); + seq_printf(seq, "Low2LowCat treshold : %d\n", + result.low2lowcat_treshold); + seq_printf(seq, "LowCat2Low treshold : %d\n", + result.lowcat2low_treshold); + seq_printf(seq, "LowWarn2Low treshold : %d\n", + result.lowwarn2low_treshold); + seq_printf(seq, "Low2LowWarn treshold : %d\n", + result.low2lowwarn_treshold); + seq_printf(seq, "Norm2LowWarn treshold : %d\n", + result.norm2lowwarn_treshold); + seq_printf(seq, "LowWarn2Norm treshold : %d\n", + result.lowwarn2norm_treshold); + seq_printf(seq, "Nominal reading : %d\n", result.nominal_reading); + seq_printf(seq, "HiWarn2Norm treshold : %d\n", + result.hiwarn2norm_treshold); + seq_printf(seq, "Norm2HiWarn treshold : %d\n", + result.norm2hiwarn_treshold); + seq_printf(seq, "High2HiWarn treshold : %d\n", + result.high2hiwarn_treshold); + seq_printf(seq, "HiWarn2High treshold : %d\n", + result.hiwarn2high_treshold); + seq_printf(seq, "HiCat2High treshold : %d\n", + result.hicat2high_treshold); + seq_printf(seq, "High2HiCat treshold : %d\n", + result.hi2hicat_treshold); + seq_printf(seq, "Maximum reading : %d\n", result.maximum_reading); + + seq_printf(seq, "Sensor state : %d = ", result.sensor_state); + switch (result.sensor_state) { + case 0: + seq_printf(seq, "Normal\n"); + break; + case 1: + seq_printf(seq, "Abnormal\n"); + break; + case 2: + seq_printf(seq, "Unknown\n"); + break; + case 3: + seq_printf(seq, "Low Catastrophic (LoCat)\n"); + break; + case 4: + seq_printf(seq, "Low (Low)\n"); + break; + case 5: + seq_printf(seq, "Low Warning (LoWarn)\n"); + break; + case 6: + seq_printf(seq, "High Warning (HiWarn)\n"); + break; + case 7: + seq_printf(seq, "High (High)\n"); + break; + case 8: + seq_printf(seq, "High Catastrophic (HiCat)\n"); + break; + } + + seq_printf(seq, "Event_enable : 0x%02X\n", result.event_enable); + seq_printf(seq, " [%s] Operational state change. \n", + (result.event_enable & 0x01) ? "+" : "-"); + seq_printf(seq, " [%s] Low catastrophic. \n", + (result.event_enable & 0x02) ? "+" : "-"); + seq_printf(seq, " [%s] Low reading. \n", + (result.event_enable & 0x04) ? "+" : "-"); + seq_printf(seq, " [%s] Low warning. \n", + (result.event_enable & 0x08) ? "+" : "-"); + seq_printf(seq, + " [%s] Change back to normal from out of range state. \n", + (result.event_enable & 0x10) ? "+" : "-"); + seq_printf(seq, " [%s] High warning. \n", + (result.event_enable & 0x20) ? "+" : "-"); + seq_printf(seq, " [%s] High reading. \n", + (result.event_enable & 0x40) ? "+" : "-"); + seq_printf(seq, " [%s] High catastrophic. \n", + (result.event_enable & 0x80) ? "+" : "-"); - spin_unlock(&i2o_proc_lock); - return len; + return 0; } +static int i2o_seq_open_hrt(struct inode *inode, struct file *file) +{ + return single_open(file, i2o_seq_show_hrt, PDE(inode)->data); +}; -static int print_serial_number(char *buff, int pos, u8 *serialno, int max_len) +static int i2o_seq_open_lct(struct inode *inode, struct file *file) { - int i; + return single_open(file, i2o_seq_show_lct, PDE(inode)->data); +}; - /* 19990419 -sralston - * The I2O v1.5 (and v2.0 so far) "official specification" - * got serial numbers WRONG! - * Apparently, and despite what Section 3.4.4 says and - * Figure 3-35 shows (pg 3-39 in the pdf doc), - * the convention / consensus seems to be: - * + First byte is SNFormat - * + Second byte is SNLen (but only if SNFormat==7 (?)) - * + (v2.0) SCSI+BS may use IEEE Registered (64 or 128 bit) format - */ - switch(serialno[0]) - { - case I2O_SNFORMAT_BINARY: /* Binary */ - pos += sprintf(buff+pos, "0x"); - for(i = 0; i < serialno[1]; i++) - { - pos += sprintf(buff+pos, "%02X", serialno[2+i]); - } - break; - - case I2O_SNFORMAT_ASCII: /* ASCII */ - if ( serialno[1] < ' ' ) /* printable or SNLen? */ - { - /* sanity */ - max_len = (max_len < serialno[1]) ? max_len : serialno[1]; - serialno[1+max_len] = '\0'; +static int i2o_seq_open_status(struct inode *inode, struct file *file) +{ + return single_open(file, i2o_seq_show_status, PDE(inode)->data); +}; - /* just print it */ - pos += sprintf(buff+pos, "%s", &serialno[2]); - } - else - { - /* print chars for specified length */ - for(i = 0; i < serialno[1]; i++) - { - pos += sprintf(buff+pos, "%c", serialno[2+i]); - } - } - break; +static int i2o_seq_open_hw(struct inode *inode, struct file *file) +{ + return single_open(file, i2o_seq_show_hw, PDE(inode)->data); +}; - case I2O_SNFORMAT_UNICODE: /* UNICODE */ - pos += sprintf(buff+pos, "UNICODE Format. Can't Display\n"); - break; +static int i2o_seq_open_ddm_table(struct inode *inode, struct file *file) +{ + return single_open(file, i2o_seq_show_ddm_table, PDE(inode)->data); +}; - case I2O_SNFORMAT_LAN48_MAC: /* LAN-48 MAC Address */ - pos += sprintf(buff+pos, - "LAN-48 MAC address @ %02X:%02X:%02X:%02X:%02X:%02X", - serialno[2], serialno[3], - serialno[4], serialno[5], - serialno[6], serialno[7]); - break; +static int i2o_seq_open_driver_store(struct inode *inode, struct file *file) +{ + return single_open(file, i2o_seq_show_driver_store, PDE(inode)->data); +}; - case I2O_SNFORMAT_WAN: /* WAN MAC Address */ - /* FIXME: Figure out what a WAN access address looks like?? */ - pos += sprintf(buff+pos, "WAN Access Address"); - break; +static int i2o_seq_open_drivers_stored(struct inode *inode, struct file *file) +{ + return single_open(file, i2o_seq_show_drivers_stored, PDE(inode)->data); +}; -/* plus new in v2.0 */ - case I2O_SNFORMAT_LAN64_MAC: /* LAN-64 MAC Address */ - /* FIXME: Figure out what a LAN-64 address really looks like?? */ - pos += sprintf(buff+pos, - "LAN-64 MAC address @ [?:%02X:%02X:?] %02X:%02X:%02X:%02X:%02X:%02X", - serialno[8], serialno[9], - serialno[2], serialno[3], - serialno[4], serialno[5], - serialno[6], serialno[7]); - break; +static int i2o_seq_open_groups(struct inode *inode, struct file *file) +{ + return single_open(file, i2o_seq_show_groups, PDE(inode)->data); +}; +static int i2o_seq_open_phys_device(struct inode *inode, struct file *file) +{ + return single_open(file, i2o_seq_show_phys_device, PDE(inode)->data); +}; - case I2O_SNFORMAT_DDM: /* I2O DDM */ - pos += sprintf(buff+pos, - "DDM: Tid=%03Xh, Rsvd=%04Xh, OrgId=%04Xh", - *(u16*)&serialno[2], - *(u16*)&serialno[4], - *(u16*)&serialno[6]); - break; +static int i2o_seq_open_claimed(struct inode *inode, struct file *file) +{ + return single_open(file, i2o_seq_show_claimed, PDE(inode)->data); +}; - case I2O_SNFORMAT_IEEE_REG64: /* IEEE Registered (64-bit) */ - case I2O_SNFORMAT_IEEE_REG128: /* IEEE Registered (128-bit) */ - /* FIXME: Figure if this is even close?? */ - pos += sprintf(buff+pos, - "IEEE NodeName(hi,lo)=(%08Xh:%08Xh), PortName(hi,lo)=(%08Xh:%08Xh)\n", - *(u32*)&serialno[2], - *(u32*)&serialno[6], - *(u32*)&serialno[10], - *(u32*)&serialno[14]); - break; +static int i2o_seq_open_users(struct inode *inode, struct file *file) +{ + return single_open(file, i2o_seq_show_users, PDE(inode)->data); +}; +static int i2o_seq_open_priv_msgs(struct inode *inode, struct file *file) +{ + return single_open(file, i2o_seq_show_priv_msgs, PDE(inode)->data); +}; - case I2O_SNFORMAT_UNKNOWN: /* Unknown 0 */ - case I2O_SNFORMAT_UNKNOWN2: /* Unknown 0xff */ - default: - pos += sprintf(buff+pos, "Unknown data format (0x%02x)", - serialno[0]); - break; - } +static int i2o_seq_open_authorized_users(struct inode *inode, struct file *file) +{ + return single_open(file, i2o_seq_show_authorized_users, + PDE(inode)->data); +}; - return pos; -} +static int i2o_seq_open_dev_identity(struct inode *inode, struct file *file) +{ + return single_open(file, i2o_seq_show_dev_identity, PDE(inode)->data); +}; -const char * i2o_get_connector_type(int conn) +static int i2o_seq_open_ddm_identity(struct inode *inode, struct file *file) { - int idx = 16; - static char *i2o_connector_type[] = { - "OTHER", - "UNKNOWN", - "AUI", - "UTP", - "BNC", - "RJ45", - "STP DB9", - "FIBER MIC", - "APPLE AUI", - "MII", - "DB9", - "HSSDC", - "DUPLEX SC FIBER", - "DUPLEX ST FIBER", - "TNC/BNC", - "HW DEFAULT" - }; + return single_open(file, i2o_seq_show_ddm_identity, PDE(inode)->data); +}; - switch(conn) - { - case 0x00000000: - idx = 0; - break; - case 0x00000001: - idx = 1; - break; - case 0x00000002: - idx = 2; - break; - case 0x00000003: - idx = 3; - break; - case 0x00000004: - idx = 4; - break; - case 0x00000005: - idx = 5; - break; - case 0x00000006: - idx = 6; - break; - case 0x00000007: - idx = 7; - break; - case 0x00000008: - idx = 8; - break; - case 0x00000009: - idx = 9; - break; - case 0x0000000A: - idx = 10; - break; - case 0x0000000B: - idx = 11; - break; - case 0x0000000C: - idx = 12; - break; - case 0x0000000D: - idx = 13; - break; - case 0x0000000E: - idx = 14; - break; - case 0xFFFFFFFF: - idx = 15; - break; - } +static int i2o_seq_open_uinfo(struct inode *inode, struct file *file) +{ + return single_open(file, i2o_seq_show_uinfo, PDE(inode)->data); +}; - return i2o_connector_type[idx]; -} +static int i2o_seq_open_sgl_limits(struct inode *inode, struct file *file) +{ + return single_open(file, i2o_seq_show_sgl_limits, PDE(inode)->data); +}; +static int i2o_seq_open_sensors(struct inode *inode, struct file *file) +{ + return single_open(file, i2o_seq_show_sensors, PDE(inode)->data); +}; -const char * i2o_get_connection_type(int conn) +static int i2o_seq_open_dev_name(struct inode *inode, struct file *file) { - int idx = 0; - static char *i2o_connection_type[] = { - "Unknown", - "AUI", - "10BASE5", - "FIORL", - "10BASE2", - "10BROAD36", - "10BASE-T", - "10BASE-FP", - "10BASE-FB", - "10BASE-FL", - "100BASE-TX", - "100BASE-FX", - "100BASE-T4", - "1000BASE-SX", - "1000BASE-LX", - "1000BASE-CX", - "1000BASE-T", - "100VG-ETHERNET", - "100VG-TOKEN RING", - "4MBIT TOKEN RING", - "16 Mb Token Ring", - "125 MBAUD FDDI", - "Point-to-point", - "Arbitrated loop", - "Public loop", - "Fabric", - "Emulation", - "Other", - "HW default" - }; + return single_open(file, i2o_seq_show_dev_name, PDE(inode)->data); +}; - switch(conn) - { - case I2O_LAN_UNKNOWN: - idx = 0; - break; - case I2O_LAN_AUI: - idx = 1; - break; - case I2O_LAN_10BASE5: - idx = 2; - break; - case I2O_LAN_FIORL: - idx = 3; - break; - case I2O_LAN_10BASE2: - idx = 4; - break; - case I2O_LAN_10BROAD36: - idx = 5; - break; - case I2O_LAN_10BASE_T: - idx = 6; - break; - case I2O_LAN_10BASE_FP: - idx = 7; - break; - case I2O_LAN_10BASE_FB: - idx = 8; - break; - case I2O_LAN_10BASE_FL: - idx = 9; - break; - case I2O_LAN_100BASE_TX: - idx = 10; - break; - case I2O_LAN_100BASE_FX: - idx = 11; - break; - case I2O_LAN_100BASE_T4: - idx = 12; - break; - case I2O_LAN_1000BASE_SX: - idx = 13; - break; - case I2O_LAN_1000BASE_LX: - idx = 14; - break; - case I2O_LAN_1000BASE_CX: - idx = 15; - break; - case I2O_LAN_1000BASE_T: - idx = 16; - break; - case I2O_LAN_100VG_ETHERNET: - idx = 17; - break; - case I2O_LAN_100VG_TR: - idx = 18; - break; - case I2O_LAN_4MBIT: - idx = 19; - break; - case I2O_LAN_16MBIT: - idx = 20; - break; - case I2O_LAN_125MBAUD: - idx = 21; - break; - case I2O_LAN_POINT_POINT: - idx = 22; - break; - case I2O_LAN_ARB_LOOP: - idx = 23; - break; - case I2O_LAN_PUBLIC_LOOP: - idx = 24; - break; - case I2O_LAN_FABRIC: - idx = 25; - break; - case I2O_LAN_EMULATION: - idx = 26; - break; - case I2O_LAN_OTHER: - idx = 27; - break; - case I2O_LAN_DEFAULT: - idx = 28; - break; - } - - return i2o_connection_type[idx]; -} - - -/* LAN group 0000h - Device info (scalar) */ -int i2o_proc_read_lan_dev_info(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u32 work32[56]; - static u8 *work8 = (u8*)work32; - static u16 *work16 = (u16*)work32; - static u64 *work64 = (u64*)work32; - int token; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0000, -1, &work32, 56*4); - if (token < 0) { - len += i2o_report_query_status(buf+len, token, "0x0000 LAN Device Info"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "LAN Type : "); - switch (work16[0]) - { - case 0x0030: - len += sprintf(buf+len, "Ethernet, "); - break; - case 0x0040: - len += sprintf(buf+len, "100Base VG, "); - break; - case 0x0050: - len += sprintf(buf+len, "Token Ring, "); - break; - case 0x0060: - len += sprintf(buf+len, "FDDI, "); - break; - case 0x0070: - len += sprintf(buf+len, "Fibre Channel, "); - break; - default: - len += sprintf(buf+len, "Unknown type (0x%04x), ", work16[0]); - break; - } - - if (work16[1]&0x00000001) - len += sprintf(buf+len, "emulated LAN, "); - else - len += sprintf(buf+len, "physical LAN port, "); - - if (work16[1]&0x00000002) - len += sprintf(buf+len, "full duplex\n"); - else - len += sprintf(buf+len, "simplex\n"); - - len += sprintf(buf+len, "Address format : "); - switch(work8[4]) { - case 0x00: - len += sprintf(buf+len, "IEEE 48bit\n"); - break; - case 0x01: - len += sprintf(buf+len, "FC IEEE\n"); - break; - default: - len += sprintf(buf+len, "Unknown (0x%02x)\n", work8[4]); - break; - } - - len += sprintf(buf+len, "State : "); - switch(work8[5]) - { - case 0x00: - len += sprintf(buf+len, "Unknown\n"); - break; - case 0x01: - len += sprintf(buf+len, "Unclaimed\n"); - break; - case 0x02: - len += sprintf(buf+len, "Operational\n"); - break; - case 0x03: - len += sprintf(buf+len, "Suspended\n"); - break; - case 0x04: - len += sprintf(buf+len, "Resetting\n"); - break; - case 0x05: - len += sprintf(buf+len, "ERROR: "); - if(work16[3]&0x0001) - len += sprintf(buf+len, "TxCU inoperative "); - if(work16[3]&0x0002) - len += sprintf(buf+len, "RxCU inoperative "); - if(work16[3]&0x0004) - len += sprintf(buf+len, "Local mem alloc "); - len += sprintf(buf+len, "\n"); - break; - case 0x06: - len += sprintf(buf+len, "Operational no Rx\n"); - break; - case 0x07: - len += sprintf(buf+len, "Suspended no Rx\n"); - break; - default: - len += sprintf(buf+len, "Unspecified\n"); - break; - } - - len += sprintf(buf+len, "Min packet size : %d\n", work32[2]); - len += sprintf(buf+len, "Max packet size : %d\n", work32[3]); - len += sprintf(buf+len, "HW address : " - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - work8[16],work8[17],work8[18],work8[19], - work8[20],work8[21],work8[22],work8[23]); - - len += sprintf(buf+len, "Max Tx wire speed : %d bps\n", (int)work64[3]); - len += sprintf(buf+len, "Max Rx wire speed : %d bps\n", (int)work64[4]); - - len += sprintf(buf+len, "Min SDU packet size : 0x%08x\n", work32[10]); - len += sprintf(buf+len, "Max SDU packet size : 0x%08x\n", work32[11]); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0001h - MAC address table (scalar) */ -int i2o_proc_read_lan_mac_addr(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u32 work32[48]; - static u8 *work8 = (u8*)work32; - int token; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0001, -1, &work32, 48*4); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0001 LAN MAC Address"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "Active address : " - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - work8[0],work8[1],work8[2],work8[3], - work8[4],work8[5],work8[6],work8[7]); - len += sprintf(buf+len, "Current address : " - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - work8[8],work8[9],work8[10],work8[11], - work8[12],work8[13],work8[14],work8[15]); - len += sprintf(buf+len, "Functional address mask : " - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - work8[16],work8[17],work8[18],work8[19], - work8[20],work8[21],work8[22],work8[23]); - - len += sprintf(buf+len,"HW/DDM capabilities : 0x%08x\n", work32[7]); - len += sprintf(buf+len," [%s] Unicast packets supported\n", - (work32[7]&0x00000001)?"+":"-"); - len += sprintf(buf+len," [%s] Promiscuous mode supported\n", - (work32[7]&0x00000002)?"+":"-"); - len += sprintf(buf+len," [%s] Promiscuous multicast mode supported\n", - (work32[7]&0x00000004)?"+":"-"); - len += sprintf(buf+len," [%s] Broadcast reception disabling supported\n", - (work32[7]&0x00000100)?"+":"-"); - len += sprintf(buf+len," [%s] Multicast reception disabling supported\n", - (work32[7]&0x00000200)?"+":"-"); - len += sprintf(buf+len," [%s] Functional address disabling supported\n", - (work32[7]&0x00000400)?"+":"-"); - len += sprintf(buf+len," [%s] MAC reporting supported\n", - (work32[7]&0x00000800)?"+":"-"); - - len += sprintf(buf+len,"Filter mask : 0x%08x\n", work32[6]); - len += sprintf(buf+len," [%s] Unicast packets disable\n", - (work32[6]&0x00000001)?"+":"-"); - len += sprintf(buf+len," [%s] Promiscuous mode enable\n", - (work32[6]&0x00000002)?"+":"-"); - len += sprintf(buf+len," [%s] Promiscuous multicast mode enable\n", - (work32[6]&0x00000004)?"+":"-"); - len += sprintf(buf+len," [%s] Broadcast packets disable\n", - (work32[6]&0x00000100)?"+":"-"); - len += sprintf(buf+len," [%s] Multicast packets disable\n", - (work32[6]&0x00000200)?"+":"-"); - len += sprintf(buf+len," [%s] Functional address disable\n", - (work32[6]&0x00000400)?"+":"-"); - - if (work32[7]&0x00000800) { - len += sprintf(buf+len, " MAC reporting mode : "); - if (work32[6]&0x00000800) - len += sprintf(buf+len, "Pass only priority MAC packets to user\n"); - else if (work32[6]&0x00001000) - len += sprintf(buf+len, "Pass all MAC packets to user\n"); - else if (work32[6]&0x00001800) - len += sprintf(buf+len, "Pass all MAC packets (promiscuous) to user\n"); - else - len += sprintf(buf+len, "Do not pass MAC packets to user\n"); - } - len += sprintf(buf+len, "Number of multicast addresses : %d\n", work32[8]); - len += sprintf(buf+len, "Perfect filtering for max %d multicast addresses\n", - work32[9]); - len += sprintf(buf+len, "Imperfect filtering for max %d multicast addresses\n", - work32[10]); - - spin_unlock(&i2o_proc_lock); - - return len; -} - -/* LAN group 0002h - Multicast MAC address table (table) */ -int i2o_proc_read_lan_mcast_addr(char *buf, char **start, off_t offset, - int len, int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; - int i; - u8 mc_addr[8]; +static struct file_operations i2o_seq_fops_lct = { + .open = i2o_seq_open_lct, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; - struct - { - u16 result_count; - u16 pad; - u16 block_size; - u8 block_status; - u8 error_info_size; - u16 row_count; - u16 more_flag; - u8 mc_addr[256][8]; - } *result; +static struct file_operations i2o_seq_fops_hrt = { + .open = i2o_seq_open_hrt, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; - result = kmalloc(sizeof(*result), GFP_KERNEL); - if(!result) - return -ENOMEM; +static struct file_operations i2o_seq_fops_status = { + .open = i2o_seq_open_status, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; - spin_lock(&i2o_proc_lock); - len = 0; +static struct file_operations i2o_seq_fops_hw = { + .open = i2o_seq_open_hw, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, 0x0002, -1, - NULL, 0, result, sizeof(*result)); +static struct file_operations i2o_seq_fops_ddm_table = { + .open = i2o_seq_open_ddm_table, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x002 LAN Multicast MAC Address"); - goto out; - } +static struct file_operations i2o_seq_fops_driver_store = { + .open = i2o_seq_open_driver_store, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; - for (i = 0; i < result->row_count; i++) - { - memcpy(mc_addr, result->mc_addr[i], 8); +static struct file_operations i2o_seq_fops_drivers_stored = { + .open = i2o_seq_open_drivers_stored, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; - len += sprintf(buf+len, "MC MAC address[%d]: " - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - i, mc_addr[0], mc_addr[1], mc_addr[2], - mc_addr[3], mc_addr[4], mc_addr[5], - mc_addr[6], mc_addr[7]); - } -out: - spin_unlock(&i2o_proc_lock); - kfree(result); - return len; -} +static struct file_operations i2o_seq_fops_groups = { + .open = i2o_seq_open_groups, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; -/* LAN group 0003h - Batch Control (scalar) */ -int i2o_proc_read_lan_batch_control(char *buf, char **start, off_t offset, - int len, int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u32 work32[9]; - int token; +static struct file_operations i2o_seq_fops_phys_device = { + .open = i2o_seq_open_phys_device, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; - spin_lock(&i2o_proc_lock); - len = 0; +static struct file_operations i2o_seq_fops_claimed = { + .open = i2o_seq_open_claimed, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0003, -1, &work32, 9*4); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0003 LAN Batch Control"); - spin_unlock(&i2o_proc_lock); - return len; - } +static struct file_operations i2o_seq_fops_users = { + .open = i2o_seq_open_users, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; - len += sprintf(buf, "Batch mode "); - if (work32[0]&0x00000001) - len += sprintf(buf+len, "disabled"); - else - len += sprintf(buf+len, "enabled"); - if (work32[0]&0x00000002) - len += sprintf(buf+len, " (current setting)"); - if (work32[0]&0x00000004) - len += sprintf(buf+len, ", forced"); - else - len += sprintf(buf+len, ", toggle"); - len += sprintf(buf+len, "\n"); +static struct file_operations i2o_seq_fops_priv_msgs = { + .open = i2o_seq_open_priv_msgs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; - len += sprintf(buf+len, "Max Rx batch count : %d\n", work32[5]); - len += sprintf(buf+len, "Max Rx batch delay : %d\n", work32[6]); - len += sprintf(buf+len, "Max Tx batch delay : %d\n", work32[7]); - len += sprintf(buf+len, "Max Tx batch count : %d\n", work32[8]); +static struct file_operations i2o_seq_fops_authorized_users = { + .open = i2o_seq_open_authorized_users, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; - spin_unlock(&i2o_proc_lock); - return len; -} +static struct file_operations i2o_seq_fops_dev_name = { + .open = i2o_seq_open_dev_name, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; -/* LAN group 0004h - LAN Operation (scalar) */ -int i2o_proc_read_lan_operation(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u32 work32[5]; - int token; +static struct file_operations i2o_seq_fops_dev_identity = { + .open = i2o_seq_open_dev_identity, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; - spin_lock(&i2o_proc_lock); - len = 0; +static struct file_operations i2o_seq_fops_ddm_identity = { + .open = i2o_seq_open_ddm_identity, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0004, -1, &work32, 20); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0004 LAN Operation"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "Packet prepadding (32b words) : %d\n", work32[0]); - len += sprintf(buf+len, "Transmission error reporting : %s\n", - (work32[1]&1)?"on":"off"); - len += sprintf(buf+len, "Bad packet handling : %s\n", - (work32[1]&0x2)?"by host":"by DDM"); - len += sprintf(buf+len, "Packet orphan limit : %d\n", work32[2]); - - len += sprintf(buf+len, "Tx modes : 0x%08x\n", work32[3]); - len += sprintf(buf+len, " [%s] HW CRC suppression\n", - (work32[3]&0x00000004) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW IPv4 checksum\n", - (work32[3]&0x00000100) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW TCP checksum\n", - (work32[3]&0x00000200) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW UDP checksum\n", - (work32[3]&0x00000400) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW RSVP checksum\n", - (work32[3]&0x00000800) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW ICMP checksum\n", - (work32[3]&0x00001000) ? "+" : "-"); - len += sprintf(buf+len, " [%s] Loopback suppression enable\n", - (work32[3]&0x00002000) ? "+" : "-"); - - len += sprintf(buf+len, "Rx modes : 0x%08x\n", work32[4]); - len += sprintf(buf+len, " [%s] FCS in payload\n", - (work32[4]&0x00000004) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW IPv4 checksum validation\n", - (work32[4]&0x00000100) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW TCP checksum validation\n", - (work32[4]&0x00000200) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW UDP checksum validation\n", - (work32[4]&0x00000400) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW RSVP checksum validation\n", - (work32[4]&0x00000800) ? "+" : "-"); - len += sprintf(buf+len, " [%s] HW ICMP checksum validation\n", - (work32[4]&0x00001000) ? "+" : "-"); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0005h - Media operation (scalar) */ -int i2o_proc_read_lan_media_operation(char *buf, char **start, off_t offset, - int len, int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - int token; +static struct file_operations i2o_seq_fops_uinfo = { + .open = i2o_seq_open_uinfo, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; - struct - { - u32 connector_type; - u32 connection_type; - u64 current_tx_wire_speed; - u64 current_rx_wire_speed; - u8 duplex_mode; - u8 link_status; - u8 reserved; - u8 duplex_mode_target; - u32 connector_type_target; - u32 connection_type_target; - } result; +static struct file_operations i2o_seq_fops_sgl_limits = { + .open = i2o_seq_open_sgl_limits, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; - spin_lock(&i2o_proc_lock); - len = 0; +static struct file_operations i2o_seq_fops_sensors = { + .open = i2o_seq_open_sensors, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0005, -1, &result, sizeof(result)); - if (token < 0) { - len += i2o_report_query_status(buf+len, token, "0x0005 LAN Media Operation"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "Connector type : %s\n", - i2o_get_connector_type(result.connector_type)); - len += sprintf(buf+len, "Connection type : %s\n", - i2o_get_connection_type(result.connection_type)); - - len += sprintf(buf+len, "Current Tx wire speed : %d bps\n", (int)result.current_tx_wire_speed); - len += sprintf(buf+len, "Current Rx wire speed : %d bps\n", (int)result.current_rx_wire_speed); - len += sprintf(buf+len, "Duplex mode : %s duplex\n", - (result.duplex_mode)?"Full":"Half"); - - len += sprintf(buf+len, "Link status : "); - switch (result.link_status) - { - case 0x00: - len += sprintf(buf+len, "Unknown\n"); - break; - case 0x01: - len += sprintf(buf+len, "Normal\n"); - break; - case 0x02: - len += sprintf(buf+len, "Failure\n"); - break; - case 0x03: - len += sprintf(buf+len, "Reset\n"); - break; - default: - len += sprintf(buf+len, "Unspecified\n"); - } - - len += sprintf(buf+len, "Duplex mode target : "); - switch (result.duplex_mode_target){ - case 0: - len += sprintf(buf+len, "Half duplex\n"); - break; - case 1: - len += sprintf(buf+len, "Full duplex\n"); - break; - default: - len += sprintf(buf+len, "\n"); - } +/* + * IOP specific entries...write field just in case someone + * ever wants one. + */ +static i2o_proc_entry i2o_proc_generic_iop_entries[] = { + {"hrt", S_IFREG | S_IRUGO, &i2o_seq_fops_hrt}, + {"lct", S_IFREG | S_IRUGO, &i2o_seq_fops_lct}, + {"status", S_IFREG | S_IRUGO, &i2o_seq_fops_status}, + {"hw", S_IFREG | S_IRUGO, &i2o_seq_fops_hw}, + {"ddm_table", S_IFREG | S_IRUGO, &i2o_seq_fops_ddm_table}, + {"driver_store", S_IFREG | S_IRUGO, &i2o_seq_fops_driver_store}, + {"drivers_stored", S_IFREG | S_IRUGO, &i2o_seq_fops_drivers_stored}, + {NULL, 0, NULL} +}; - len += sprintf(buf+len, "Connector type target : %s\n", - i2o_get_connector_type(result.connector_type_target)); - len += sprintf(buf+len, "Connection type target : %s\n", - i2o_get_connection_type(result.connection_type_target)); +/* + * Device specific entries + */ +static i2o_proc_entry generic_dev_entries[] = { + {"groups", S_IFREG | S_IRUGO, &i2o_seq_fops_groups}, + {"phys_dev", S_IFREG | S_IRUGO, &i2o_seq_fops_phys_device}, + {"claimed", S_IFREG | S_IRUGO, &i2o_seq_fops_claimed}, + {"users", S_IFREG | S_IRUGO, &i2o_seq_fops_users}, + {"priv_msgs", S_IFREG | S_IRUGO, &i2o_seq_fops_priv_msgs}, + {"authorized_users", S_IFREG | S_IRUGO, &i2o_seq_fops_authorized_users}, + {"dev_identity", S_IFREG | S_IRUGO, &i2o_seq_fops_dev_identity}, + {"ddm_identity", S_IFREG | S_IRUGO, &i2o_seq_fops_ddm_identity}, + {"user_info", S_IFREG | S_IRUGO, &i2o_seq_fops_uinfo}, + {"sgl_limits", S_IFREG | S_IRUGO, &i2o_seq_fops_sgl_limits}, + {"sensors", S_IFREG | S_IRUGO, &i2o_seq_fops_sensors}, + {NULL, 0, NULL} +}; - spin_unlock(&i2o_proc_lock); - return len; -} +/* + * Storage unit specific entries (SCSI Periph, BS) with device names + */ +static i2o_proc_entry rbs_dev_entries[] = { + {"dev_name", S_IFREG | S_IRUGO, &i2o_seq_fops_dev_name}, + {NULL, 0, NULL} +}; -/* LAN group 0006h - Alternate address (table) (optional) */ -int i2o_proc_read_lan_alt_addr(char *buf, char **start, off_t offset, int len, - int *eof, void *data) +/** + * i2o_proc_create_entries - Creates proc dir entries + * @dir: proc dir entry under which the entries should be placed + * @i2o_pe: pointer to the entries which should be added + * @data: pointer to I2O controller or device + * + * Create proc dir entries for a I2O controller or I2O device. + * + * Returns 0 on success or negative error code on failure. + */ +static int i2o_proc_create_entries(struct proc_dir_entry *dir, + i2o_proc_entry * i2o_pe, void *data) { - struct i2o_device *d = (struct i2o_device*)data; - int token; - int i; - u8 alt_addr[8]; - struct - { - u16 result_count; - u16 pad; - u16 block_size; - u8 block_status; - u8 error_info_size; - u16 row_count; - u16 more_flag; - u8 alt_addr[256][8]; - } *result; - - result = kmalloc(sizeof(*result), GFP_KERNEL); - if(!result) - return -ENOMEM; + struct proc_dir_entry *tmp; - spin_lock(&i2o_proc_lock); - len = 0; + while (i2o_pe->name) { + tmp = create_proc_entry(i2o_pe->name, i2o_pe->mode, dir); + if (!tmp) + return -1; - token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data.tid, - 0x0006, -1, NULL, 0, result, sizeof(*result)); + tmp->data = data; + tmp->proc_fops = i2o_pe->fops; - if (token < 0) { - len += i2o_report_query_status(buf+len, token, "0x0006 LAN Alternate Address (optional)"); - goto out; + i2o_pe++; } - for (i=0; i < result->row_count; i++) - { - memcpy(alt_addr,result->alt_addr[i],8); - len += sprintf(buf+len, "Alternate address[%d]: " - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", - i, alt_addr[0], alt_addr[1], alt_addr[2], - alt_addr[3], alt_addr[4], alt_addr[5], - alt_addr[6], alt_addr[7]); - } -out: - spin_unlock(&i2o_proc_lock); - kfree(result); - return len; + return 0; } - -/* LAN group 0007h - Transmit info (scalar) */ -int i2o_proc_read_lan_tx_info(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u32 work32[8]; - int token; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0007, -1, &work32, 8*4); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0007 LAN Transmit Info"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "Tx Max SG elements per packet : %d\n", work32[0]); - len += sprintf(buf+len, "Tx Max SG elements per chain : %d\n", work32[1]); - len += sprintf(buf+len, "Tx Max outstanding packets : %d\n", work32[2]); - len += sprintf(buf+len, "Tx Max packets per request : %d\n", work32[3]); - - len += sprintf(buf+len, "Tx modes : 0x%08x\n", work32[4]); - len += sprintf(buf+len, " [%s] No DA in SGL\n", - (work32[4]&0x00000002) ? "+" : "-"); - len += sprintf(buf+len, " [%s] CRC suppression\n", - (work32[4]&0x00000004) ? "+" : "-"); - len += sprintf(buf+len, " [%s] MAC insertion\n", - (work32[4]&0x00000010) ? "+" : "-"); - len += sprintf(buf+len, " [%s] RIF insertion\n", - (work32[4]&0x00000020) ? "+" : "-"); - len += sprintf(buf+len, " [%s] IPv4 checksum generation\n", - (work32[4]&0x00000100) ? "+" : "-"); - len += sprintf(buf+len, " [%s] TCP checksum generation\n", - (work32[4]&0x00000200) ? "+" : "-"); - len += sprintf(buf+len, " [%s] UDP checksum generation\n", - (work32[4]&0x00000400) ? "+" : "-"); - len += sprintf(buf+len, " [%s] RSVP checksum generation\n", - (work32[4]&0x00000800) ? "+" : "-"); - len += sprintf(buf+len, " [%s] ICMP checksum generation\n", - (work32[4]&0x00001000) ? "+" : "-"); - len += sprintf(buf+len, " [%s] Loopback enabled\n", - (work32[4]&0x00010000) ? "+" : "-"); - len += sprintf(buf+len, " [%s] Loopback suppression enabled\n", - (work32[4]&0x00020000) ? "+" : "-"); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0008h - Receive info (scalar) */ -int i2o_proc_read_lan_rx_info(char *buf, char **start, off_t offset, int len, - int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u32 work32[8]; - int token; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0008, -1, &work32, 8*4); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0008 LAN Receive Info"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf ,"Rx Max size of chain element : %d\n", work32[0]); - len += sprintf(buf+len, "Rx Max Buckets : %d\n", work32[1]); - len += sprintf(buf+len, "Rx Max Buckets in Reply : %d\n", work32[3]); - len += sprintf(buf+len, "Rx Max Packets in Bucket : %d\n", work32[4]); - len += sprintf(buf+len, "Rx Max Buckets in Post : %d\n", work32[5]); - - len += sprintf(buf+len, "Rx Modes : 0x%08x\n", work32[2]); - len += sprintf(buf+len, " [%s] FCS reception\n", - (work32[2]&0x00000004) ? "+" : "-"); - len += sprintf(buf+len, " [%s] IPv4 checksum validation \n", - (work32[2]&0x00000100) ? "+" : "-"); - len += sprintf(buf+len, " [%s] TCP checksum validation \n", - (work32[2]&0x00000200) ? "+" : "-"); - len += sprintf(buf+len, " [%s] UDP checksum validation \n", - (work32[2]&0x00000400) ? "+" : "-"); - len += sprintf(buf+len, " [%s] RSVP checksum validation \n", - (work32[2]&0x00000800) ? "+" : "-"); - len += sprintf(buf+len, " [%s] ICMP checksum validation \n", - (work32[2]&0x00001000) ? "+" : "-"); - - spin_unlock(&i2o_proc_lock); - return len; -} - -static int i2o_report_opt_field(char *buf, char *field_name, - int field_nbr, int supp_fields, u64 *value) -{ - if (supp_fields & (1 << field_nbr)) - return sprintf(buf, "%-24s : " FMT_U64_HEX "\n", field_name, U64_VAL(value)); - else - return sprintf(buf, "%-24s : Not supported\n", field_name); -} - -/* LAN group 0100h - LAN Historical statistics (scalar) */ -/* LAN group 0180h - Supported Optional Historical Statistics (scalar) */ -/* LAN group 0182h - Optional Non Media Specific Transmit Historical Statistics (scalar) */ -/* LAN group 0183h - Optional Non Media Specific Receive Historical Statistics (scalar) */ - -int i2o_proc_read_lan_hist_stats(char *buf, char **start, off_t offset, int len, - int *eof, void *data) +/** + * i2o_proc_subdir_remove - Remove child entries from a proc entry + * @dir: proc dir entry from which the childs should be removed + * + * Iterate over each i2o proc entry under dir and remove it. If the child + * also has entries, remove them too. + */ +static void i2o_proc_subdir_remove(struct proc_dir_entry *dir) { - struct i2o_device *d = (struct i2o_device*)data; - int token; - - struct - { - u64 tx_packets; - u64 tx_bytes; - u64 rx_packets; - u64 rx_bytes; - u64 tx_errors; - u64 rx_errors; - u64 rx_dropped; - u64 adapter_resets; - u64 adapter_suspends; - } stats; // 0x0100 - - static u64 supp_groups[4]; // 0x0180 - - struct - { - u64 tx_retries; - u64 tx_directed_bytes; - u64 tx_directed_packets; - u64 tx_multicast_bytes; - u64 tx_multicast_packets; - u64 tx_broadcast_bytes; - u64 tx_broadcast_packets; - u64 tx_group_addr_packets; - u64 tx_short_packets; - } tx_stats; // 0x0182 - - struct - { - u64 rx_crc_errors; - u64 rx_directed_bytes; - u64 rx_directed_packets; - u64 rx_multicast_bytes; - u64 rx_multicast_packets; - u64 rx_broadcast_bytes; - u64 rx_broadcast_packets; - u64 rx_group_addr_packets; - u64 rx_short_packets; - u64 rx_long_packets; - u64 rx_runt_packets; - } rx_stats; // 0x0183 - - struct - { - u64 ipv4_generate; - u64 ipv4_validate_success; - u64 ipv4_validate_errors; - u64 tcp_generate; - u64 tcp_validate_success; - u64 tcp_validate_errors; - u64 udp_generate; - u64 udp_validate_success; - u64 udp_validate_errors; - u64 rsvp_generate; - u64 rsvp_validate_success; - u64 rsvp_validate_errors; - u64 icmp_generate; - u64 icmp_validate_success; - u64 icmp_validate_errors; - } chksum_stats; // 0x0184 - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0100, -1, &stats, sizeof(stats)); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x100 LAN Statistics"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "Tx packets : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_packets)); - len += sprintf(buf+len, "Tx bytes : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_bytes)); - len += sprintf(buf+len, "Rx packets : " FMT_U64_HEX "\n", - U64_VAL(&stats.rx_packets)); - len += sprintf(buf+len, "Rx bytes : " FMT_U64_HEX "\n", - U64_VAL(&stats.rx_bytes)); - len += sprintf(buf+len, "Tx errors : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_errors)); - len += sprintf(buf+len, "Rx errors : " FMT_U64_HEX "\n", - U64_VAL(&stats.rx_errors)); - len += sprintf(buf+len, "Rx dropped : " FMT_U64_HEX "\n", - U64_VAL(&stats.rx_dropped)); - len += sprintf(buf+len, "Adapter resets : " FMT_U64_HEX "\n", - U64_VAL(&stats.adapter_resets)); - len += sprintf(buf+len, "Adapter suspends : " FMT_U64_HEX "\n", - U64_VAL(&stats.adapter_suspends)); - - /* Optional statistics follows */ - /* Get 0x0180 to see which optional groups/fields are supported */ - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0180, -1, &supp_groups, sizeof(supp_groups)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token, "0x180 LAN Supported Optional Statistics"); - spin_unlock(&i2o_proc_lock); - return len; + struct proc_dir_entry *pe, *tmp; + pe = dir->subdir; + while (pe) { + tmp = pe->next; + i2o_proc_subdir_remove(pe); + remove_proc_entry(pe->name, dir); + pe = tmp; } +}; - if (supp_groups[1]) /* 0x0182 */ - { - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0182, -1, &tx_stats, sizeof(tx_stats)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x182 LAN Optional Tx Historical Statistics"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "==== Optional TX statistics (group 0182h)\n"); - - len += i2o_report_opt_field(buf+len, "Tx RetryCount", - 0, supp_groups[1], &tx_stats.tx_retries); - len += i2o_report_opt_field(buf+len, "Tx DirectedBytes", - 1, supp_groups[1], &tx_stats.tx_directed_bytes); - len += i2o_report_opt_field(buf+len, "Tx DirectedPackets", - 2, supp_groups[1], &tx_stats.tx_directed_packets); - len += i2o_report_opt_field(buf+len, "Tx MulticastBytes", - 3, supp_groups[1], &tx_stats.tx_multicast_bytes); - len += i2o_report_opt_field(buf+len, "Tx MulticastPackets", - 4, supp_groups[1], &tx_stats.tx_multicast_packets); - len += i2o_report_opt_field(buf+len, "Tx BroadcastBytes", - 5, supp_groups[1], &tx_stats.tx_broadcast_bytes); - len += i2o_report_opt_field(buf+len, "Tx BroadcastPackets", - 6, supp_groups[1], &tx_stats.tx_broadcast_packets); - len += i2o_report_opt_field(buf+len, "Tx TotalGroupAddrPackets", - 7, supp_groups[1], &tx_stats.tx_group_addr_packets); - len += i2o_report_opt_field(buf+len, "Tx TotalPacketsTooShort", - 8, supp_groups[1], &tx_stats.tx_short_packets); - } - - if (supp_groups[2]) /* 0x0183 */ - { - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0183, -1, &rx_stats, sizeof(rx_stats)); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x183 LAN Optional Rx Historical Stats"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "==== Optional RX statistics (group 0183h)\n"); - - len += i2o_report_opt_field(buf+len, "Rx CRCErrorCount", - 0, supp_groups[2], &rx_stats.rx_crc_errors); - len += i2o_report_opt_field(buf+len, "Rx DirectedBytes", - 1, supp_groups[2], &rx_stats.rx_directed_bytes); - len += i2o_report_opt_field(buf+len, "Rx DirectedPackets", - 2, supp_groups[2], &rx_stats.rx_directed_packets); - len += i2o_report_opt_field(buf+len, "Rx MulticastBytes", - 3, supp_groups[2], &rx_stats.rx_multicast_bytes); - len += i2o_report_opt_field(buf+len, "Rx MulticastPackets", - 4, supp_groups[2], &rx_stats.rx_multicast_packets); - len += i2o_report_opt_field(buf+len, "Rx BroadcastBytes", - 5, supp_groups[2], &rx_stats.rx_broadcast_bytes); - len += i2o_report_opt_field(buf+len, "Rx BroadcastPackets", - 6, supp_groups[2], &rx_stats.rx_broadcast_packets); - len += i2o_report_opt_field(buf+len, "Rx TotalGroupAddrPackets", - 7, supp_groups[2], &rx_stats.rx_group_addr_packets); - len += i2o_report_opt_field(buf+len, "Rx TotalPacketsTooShort", - 8, supp_groups[2], &rx_stats.rx_short_packets); - len += i2o_report_opt_field(buf+len, "Rx TotalPacketsTooLong", - 9, supp_groups[2], &rx_stats.rx_long_packets); - len += i2o_report_opt_field(buf+len, "Rx TotalPacketsRunt", - 10, supp_groups[2], &rx_stats.rx_runt_packets); - } - - if (supp_groups[3]) /* 0x0184 */ - { - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0184, -1, &chksum_stats, sizeof(chksum_stats)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x184 LAN Optional Chksum Historical Stats"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "==== Optional CHKSUM statistics (group 0x0184)\n"); - - len += i2o_report_opt_field(buf+len, "IPv4 Generate", - 0, supp_groups[3], &chksum_stats.ipv4_generate); - len += i2o_report_opt_field(buf+len, "IPv4 ValidateSuccess", - 1, supp_groups[3], &chksum_stats.ipv4_validate_success); - len += i2o_report_opt_field(buf+len, "IPv4 ValidateError", - 2, supp_groups[3], &chksum_stats.ipv4_validate_errors); - len += i2o_report_opt_field(buf+len, "TCP Generate", - 3, supp_groups[3], &chksum_stats.tcp_generate); - len += i2o_report_opt_field(buf+len, "TCP ValidateSuccess", - 4, supp_groups[3], &chksum_stats.tcp_validate_success); - len += i2o_report_opt_field(buf+len, "TCP ValidateError", - 5, supp_groups[3], &chksum_stats.tcp_validate_errors); - len += i2o_report_opt_field(buf+len, "UDP Generate", - 6, supp_groups[3], &chksum_stats.udp_generate); - len += i2o_report_opt_field(buf+len, "UDP ValidateSuccess", - 7, supp_groups[3], &chksum_stats.udp_validate_success); - len += i2o_report_opt_field(buf+len, "UDP ValidateError", - 8, supp_groups[3], &chksum_stats.udp_validate_errors); - len += i2o_report_opt_field(buf+len, "RSVP Generate", - 9, supp_groups[3], &chksum_stats.rsvp_generate); - len += i2o_report_opt_field(buf+len, "RSVP ValidateSuccess", - 10, supp_groups[3], &chksum_stats.rsvp_validate_success); - len += i2o_report_opt_field(buf+len, "RSVP ValidateError", - 11, supp_groups[3], &chksum_stats.rsvp_validate_errors); - len += i2o_report_opt_field(buf+len, "ICMP Generate", - 12, supp_groups[3], &chksum_stats.icmp_generate); - len += i2o_report_opt_field(buf+len, "ICMP ValidateSuccess", - 13, supp_groups[3], &chksum_stats.icmp_validate_success); - len += i2o_report_opt_field(buf+len, "ICMP ValidateError", - 14, supp_groups[3], &chksum_stats.icmp_validate_errors); - } - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0200h - Required Ethernet Statistics (scalar) */ -/* LAN group 0280h - Optional Ethernet Statistics Supported (scalar) */ -/* LAN group 0281h - Optional Ethernet Historical Statistics (scalar) */ -int i2o_proc_read_lan_eth_stats(char *buf, char **start, off_t offset, - int len, int *eof, void *data) +/** + * i2o_proc_device_add - Add an I2O device to the proc dir + * @dir: proc dir entry to which the device should be added + * @dev: I2O device which should be added + * + * Add an I2O device to the proc dir entry dir and create the entries for + * the device depending on the class of the I2O device. + */ +static void i2o_proc_device_add(struct proc_dir_entry *dir, + struct i2o_device *dev) { - struct i2o_device *d = (struct i2o_device*)data; - int token; - - struct - { - u64 rx_align_errors; - u64 tx_one_collisions; - u64 tx_multiple_collisions; - u64 tx_deferred; - u64 tx_late_collisions; - u64 tx_max_collisions; - u64 tx_carrier_lost; - u64 tx_excessive_deferrals; - } stats; - - static u64 supp_fields; - struct - { - u64 rx_overrun; - u64 tx_underrun; - u64 tx_heartbeat_failure; - } hist_stats; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0200, -1, &stats, sizeof(stats)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0200 LAN Ethernet Statistics"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "Rx alignment errors : " FMT_U64_HEX "\n", - U64_VAL(&stats.rx_align_errors)); - len += sprintf(buf+len, "Tx one collisions : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_one_collisions)); - len += sprintf(buf+len, "Tx multicollisions : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_multiple_collisions)); - len += sprintf(buf+len, "Tx deferred : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_deferred)); - len += sprintf(buf+len, "Tx late collisions : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_late_collisions)); - len += sprintf(buf+len, "Tx max collisions : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_max_collisions)); - len += sprintf(buf+len, "Tx carrier lost : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_carrier_lost)); - len += sprintf(buf+len, "Tx excessive deferrals : " FMT_U64_HEX "\n", - U64_VAL(&stats.tx_excessive_deferrals)); + char buff[10]; + struct proc_dir_entry *devdir; + i2o_proc_entry *i2o_pe = NULL; - /* Optional Ethernet statistics follows */ - /* Get 0x0280 to see which optional fields are supported */ + sprintf(buff, "%03x", dev->lct_data.tid); - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0280, -1, &supp_fields, sizeof(supp_fields)); + pr_debug("Adding device /proc/i2o/iop%d/%s\n", dev->iop->unit, buff); - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0280 LAN Supported Optional Ethernet Statistics"); - spin_unlock(&i2o_proc_lock); - return len; + devdir = proc_mkdir(buff, dir); + if (!devdir) { + printk(KERN_WARNING "i2o: Could not allocate procdir!\n"); + return; } - if (supp_fields) /* 0x0281 */ - { - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0281, -1, &stats, sizeof(stats)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0281 LAN Optional Ethernet Statistics"); - spin_unlock(&i2o_proc_lock); - return len; - } + devdir->data = dev; - len += sprintf(buf+len, "==== Optional ETHERNET statistics (group 0x0281)\n"); + i2o_proc_create_entries(devdir, generic_dev_entries, dev); - len += i2o_report_opt_field(buf+len, "Rx Overrun", - 0, supp_fields, &hist_stats.rx_overrun); - len += i2o_report_opt_field(buf+len, "Tx Underrun", - 1, supp_fields, &hist_stats.tx_underrun); - len += i2o_report_opt_field(buf+len, "Tx HeartbeatFailure", - 2, supp_fields, &hist_stats.tx_heartbeat_failure); - } - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0300h - Required Token Ring Statistics (scalar) */ -/* LAN group 0380h, 0381h - Optional Statistics not yet defined (TODO) */ -int i2o_proc_read_lan_tr_stats(char *buf, char **start, off_t offset, - int len, int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u64 work64[13]; - int token; - - static char *ring_status[] = - { - "", - "", - "", - "", - "", - "Ring Recovery", - "Single Station", - "Counter Overflow", - "Remove Received", - "", - "Auto-Removal Error 1", - "Lobe Wire Fault", - "Transmit Beacon", - "Soft Error", - "Hard Error", - "Signal Loss" - }; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0300, -1, &work64, sizeof(work64)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0300 Token Ring Statistics"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf, "LineErrors : " FMT_U64_HEX "\n", - U64_VAL(&work64[0])); - len += sprintf(buf+len, "LostFrames : " FMT_U64_HEX "\n", - U64_VAL(&work64[1])); - len += sprintf(buf+len, "ACError : " FMT_U64_HEX "\n", - U64_VAL(&work64[2])); - len += sprintf(buf+len, "TxAbortDelimiter : " FMT_U64_HEX "\n", - U64_VAL(&work64[3])); - len += sprintf(buf+len, "BursErrors : " FMT_U64_HEX "\n", - U64_VAL(&work64[4])); - len += sprintf(buf+len, "FrameCopiedErrors : " FMT_U64_HEX "\n", - U64_VAL(&work64[5])); - len += sprintf(buf+len, "FrequencyErrors : " FMT_U64_HEX "\n", - U64_VAL(&work64[6])); - len += sprintf(buf+len, "InternalErrors : " FMT_U64_HEX "\n", - U64_VAL(&work64[7])); - len += sprintf(buf+len, "LastRingStatus : %s\n", ring_status[work64[8]]); - len += sprintf(buf+len, "TokenError : " FMT_U64_HEX "\n", - U64_VAL(&work64[9])); - len += sprintf(buf+len, "UpstreamNodeAddress : " FMT_U64_HEX "\n", - U64_VAL(&work64[10])); - len += sprintf(buf+len, "LastRingID : " FMT_U64_HEX "\n", - U64_VAL(&work64[11])); - len += sprintf(buf+len, "LastBeaconType : " FMT_U64_HEX "\n", - U64_VAL(&work64[12])); - - spin_unlock(&i2o_proc_lock); - return len; -} - -/* LAN group 0400h - Required FDDI Statistics (scalar) */ -/* LAN group 0480h, 0481h - Optional Statistics, not yet defined (TODO) */ -int i2o_proc_read_lan_fddi_stats(char *buf, char **start, off_t offset, - int len, int *eof, void *data) -{ - struct i2o_device *d = (struct i2o_device*)data; - static u64 work64[11]; - int token; - - static char *conf_state[] = - { - "Isolated", - "Local a", - "Local b", - "Local ab", - "Local s", - "Wrap a", - "Wrap b", - "Wrap ab", - "Wrap s", - "C-Wrap a", - "C-Wrap b", - "C-Wrap s", - "Through", - }; - - static char *ring_state[] = - { - "Isolated", - "Non-op", - "Rind-op", - "Detect", - "Non-op-Dup", - "Ring-op-Dup", - "Directed", - "Trace" - }; - - static char *link_state[] = - { - "Off", - "Break", - "Trace", - "Connect", - "Next", - "Signal", - "Join", - "Verify", - "Active", - "Maintenance" - }; - - spin_lock(&i2o_proc_lock); - len = 0; - - token = i2o_query_scalar(d->controller, d->lct_data.tid, - 0x0400, -1, &work64, sizeof(work64)); - - if (token < 0) { - len += i2o_report_query_status(buf+len, token,"0x0400 FDDI Required Statistics"); - spin_unlock(&i2o_proc_lock); - return len; - } - - len += sprintf(buf+len, "ConfigurationState : %s\n", conf_state[work64[0]]); - len += sprintf(buf+len, "UpstreamNode : " FMT_U64_HEX "\n", - U64_VAL(&work64[1])); - len += sprintf(buf+len, "DownStreamNode : " FMT_U64_HEX "\n", - U64_VAL(&work64[2])); - len += sprintf(buf+len, "FrameErrors : " FMT_U64_HEX "\n", - U64_VAL(&work64[3])); - len += sprintf(buf+len, "FramesLost : " FMT_U64_HEX "\n", - U64_VAL(&work64[4])); - len += sprintf(buf+len, "RingMgmtState : %s\n", ring_state[work64[5]]); - len += sprintf(buf+len, "LCTFailures : " FMT_U64_HEX "\n", - U64_VAL(&work64[6])); - len += sprintf(buf+len, "LEMRejects : " FMT_U64_HEX "\n", - U64_VAL(&work64[7])); - len += sprintf(buf+len, "LEMCount : " FMT_U64_HEX "\n", - U64_VAL(&work64[8])); - len += sprintf(buf+len, "LConnectionState : %s\n", - link_state[work64[9]]); - - spin_unlock(&i2o_proc_lock); - return len; -} - -static int i2o_proc_create_entries(void *data, i2o_proc_entry *pentry, - struct proc_dir_entry *parent) -{ - struct proc_dir_entry *ent; - - while(pentry->name != NULL) - { - ent = create_proc_entry(pentry->name, pentry->mode, parent); - if(!ent) return -1; - - ent->data = data; - ent->read_proc = pentry->read_proc; - ent->write_proc = pentry->write_proc; - if(pentry->fops_proc) - ent->proc_fops = pentry->fops_proc; - - ent->nlink = 1; - - pentry++; - } - - return 0; -} - -static void i2o_proc_remove_entries(i2o_proc_entry *pentry, - struct proc_dir_entry *parent) -{ - while(pentry->name != NULL) - { - remove_proc_entry(pentry->name, parent); - pentry++; + /* Inform core that we want updates about this device's status */ + switch (dev->lct_data.class_id) { + case I2O_CLASS_SCSI_PERIPHERAL: + case I2O_CLASS_RANDOM_BLOCK_STORAGE: + i2o_pe = rbs_dev_entries; + break; + default: + break; } + if (i2o_pe) + i2o_proc_create_entries(devdir, i2o_pe, dev); } -static int i2o_proc_add_controller(struct i2o_controller *pctrl, - struct proc_dir_entry *root ) +/** + * i2o_proc_iop_add - Add an I2O controller to the i2o proc tree + * @dir: parent proc dir entry + * @c: I2O controller which should be added + * + * Add the entries to the parent proc dir entry. Also each device is added + * to the controllers proc dir entry. + * + * Returns 0 on success or negative error code on failure. + */ +static int i2o_proc_iop_add(struct proc_dir_entry *dir, + struct i2o_controller *c) { - struct proc_dir_entry *dir, *dir1; + struct proc_dir_entry *iopdir; struct i2o_device *dev; char buff[10]; - sprintf(buff, "iop%d", pctrl->unit); + snprintf(buff, 10, "iop%d", c->unit); - dir = proc_mkdir(buff, root); - if(!dir) - return -1; - - pctrl->proc_entry = dir; + pr_debug("Adding IOP /proc/i2o/%s\n", buff); - i2o_proc_create_entries(pctrl, generic_iop_entries, dir); - - for(dev = pctrl->devices; dev; dev = dev->next) - { - sprintf(buff, "%0#5x", dev->lct_data.tid); + iopdir = proc_mkdir(buff, dir); + if (!iopdir) + return -1; - dir1 = proc_mkdir(buff, dir); - dev->proc_entry = dir1; + iopdir->data = c; - if(!dir1) - printk(KERN_INFO "i2o_proc: Could not allocate proc dir\n"); + i2o_proc_create_entries(iopdir, i2o_proc_generic_iop_entries, c); - i2o_proc_add_device(dev, dir1); - } + list_for_each_entry(dev, &c->devices, list) + i2o_proc_device_add(iopdir, dev); return 0; } -void i2o_proc_new_dev(struct i2o_controller *c, struct i2o_device *d) -{ - char buff[10]; - -#ifdef DRIVERDEBUG - printk(KERN_INFO "Adding new device to /proc/i2o/iop%d\n", c->unit); -#endif - sprintf(buff, "%0#5x", d->lct_data.tid); - - d->proc_entry = proc_mkdir(buff, c->proc_entry); - - if(!d->proc_entry) - { - printk(KERN_WARNING "i2o: Could not allocate procdir!\n"); - return; - } - - i2o_proc_add_device(d, d->proc_entry); -} - -void i2o_proc_add_device(struct i2o_device *dev, struct proc_dir_entry *dir) -{ - i2o_proc_create_entries(dev, generic_dev_entries, dir); - - /* Inform core that we want updates about this device's status */ - i2o_device_notify_on(dev, &i2o_proc_handler); - switch(dev->lct_data.class_id) - { - case I2O_CLASS_SCSI_PERIPHERAL: - case I2O_CLASS_RANDOM_BLOCK_STORAGE: - i2o_proc_create_entries(dev, rbs_dev_entries, dir); - break; - case I2O_CLASS_LAN: - i2o_proc_create_entries(dev, lan_entries, dir); - switch(dev->lct_data.sub_class) - { - case I2O_LAN_ETHERNET: - i2o_proc_create_entries(dev, lan_eth_entries, dir); - break; - case I2O_LAN_FDDI: - i2o_proc_create_entries(dev, lan_fddi_entries, dir); - break; - case I2O_LAN_TR: - i2o_proc_create_entries(dev, lan_tr_entries, dir); - break; - default: - break; - } - break; - default: - break; - } -} - -static void i2o_proc_remove_controller(struct i2o_controller *pctrl, - struct proc_dir_entry *parent) -{ - char buff[10]; - struct i2o_device *dev; - - /* Remove unused device entries */ - for(dev=pctrl->devices; dev; dev=dev->next) - i2o_proc_remove_device(dev); - - if(!atomic_read(&pctrl->proc_entry->count)) - { - sprintf(buff, "iop%d", pctrl->unit); - - i2o_proc_remove_entries(generic_iop_entries, pctrl->proc_entry); - remove_proc_entry(buff, parent); - pctrl->proc_entry = NULL; - } -} - -void i2o_proc_remove_device(struct i2o_device *dev) +/** + * i2o_proc_iop_remove - Removes an I2O controller from the i2o proc tree + * @dir: parent proc dir entry + * @c: I2O controller which should be removed + * + * Iterate over each i2o proc entry and search controller c. If it is found + * remove it from the tree. + */ +static void i2o_proc_iop_remove(struct proc_dir_entry *dir, + struct i2o_controller *c) { - struct proc_dir_entry *de=dev->proc_entry; - char dev_id[10]; - - sprintf(dev_id, "%0#5x", dev->lct_data.tid); + struct proc_dir_entry *pe, *tmp; - i2o_device_notify_off(dev, &i2o_proc_handler); - /* Would it be safe to remove _files_ even if they are in use? */ - if((de) && (!atomic_read(&de->count))) - { - i2o_proc_remove_entries(generic_dev_entries, de); - switch(dev->lct_data.class_id) - { - case I2O_CLASS_SCSI_PERIPHERAL: - case I2O_CLASS_RANDOM_BLOCK_STORAGE: - i2o_proc_remove_entries(rbs_dev_entries, de); - break; - case I2O_CLASS_LAN: - { - i2o_proc_remove_entries(lan_entries, de); - switch(dev->lct_data.sub_class) - { - case I2O_LAN_ETHERNET: - i2o_proc_remove_entries(lan_eth_entries, de); - break; - case I2O_LAN_FDDI: - i2o_proc_remove_entries(lan_fddi_entries, de); - break; - case I2O_LAN_TR: - i2o_proc_remove_entries(lan_tr_entries, de); - break; - } - } + pe = dir->subdir; + while (pe) { + tmp = pe->next; + if (pe->data == c) { + i2o_proc_subdir_remove(pe); + remove_proc_entry(pe->name, dir); } - remove_proc_entry(dev_id, dev->controller->proc_entry); + pr_debug("Removing IOP /proc/i2o/iop%d\n", c->unit); + pe = tmp; } } - -void i2o_proc_dev_del(struct i2o_controller *c, struct i2o_device *d) -{ -#ifdef DRIVERDEBUG - printk(KERN_INFO "Deleting device %d from iop%d\n", - d->lct_data.tid, c->unit); -#endif - - i2o_proc_remove_device(d); -} -static int create_i2o_procfs(void) +/** + * i2o_proc_fs_create - Create the i2o proc fs. + * + * Iterate over each I2O controller and create the entries for it. + * + * Returns 0 on success or negative error code on failure. + */ +static int __init i2o_proc_fs_create(void) { - struct i2o_controller *pctrl = NULL; - int i; + struct i2o_controller *c; i2o_proc_dir_root = proc_mkdir("i2o", NULL); - if(!i2o_proc_dir_root) + if (!i2o_proc_dir_root) return -1; + i2o_proc_dir_root->owner = THIS_MODULE; - for(i = 0; i < MAX_I2O_CONTROLLERS; i++) - { - pctrl = i2o_find_controller(i); - if(pctrl) - { - i2o_proc_add_controller(pctrl, i2o_proc_dir_root); - i2o_unlock_controller(pctrl); - } - }; + list_for_each_entry(c, &i2o_controllers, list) + i2o_proc_iop_add(i2o_proc_dir_root, c); return 0; -} +}; -static int __exit destroy_i2o_procfs(void) +/** + * i2o_proc_fs_destroy - Cleanup the all i2o proc entries + * + * Iterate over each I2O controller and remove the entries for it. + * + * Returns 0 on success or negative error code on failure. + */ +static int __exit i2o_proc_fs_destroy(void) { - struct i2o_controller *pctrl = NULL; - int i; + struct i2o_controller *c; - for(i = 0; i < MAX_I2O_CONTROLLERS; i++) - { - pctrl = i2o_find_controller(i); - if(pctrl) - { - i2o_proc_remove_controller(pctrl, i2o_proc_dir_root); - i2o_unlock_controller(pctrl); - } - } + list_for_each_entry(c, &i2o_controllers, list) + i2o_proc_iop_remove(i2o_proc_dir_root, c); - if(!atomic_read(&i2o_proc_dir_root->count)) - remove_proc_entry("i2o", NULL); - else - return -1; + remove_proc_entry("i2o", NULL); return 0; -} +}; -int __init i2o_proc_init(void) +/** + * i2o_proc_init - Init function for procfs + * + * Registers Proc OSM and creates procfs entries. + * + * Returns 0 on success or negative error code on failure. + */ +static int __init i2o_proc_init(void) { - if (i2o_install_handler(&i2o_proc_handler) < 0) - { - printk(KERN_ERR "i2o_proc: Unable to install PROC handler.\n"); - return 0; - } + int rc; - if(create_i2o_procfs()) - return -EBUSY; + rc = i2o_driver_register(&i2o_proc_driver); + if (rc) + return rc; + + rc = i2o_proc_fs_create(); + if (rc) { + i2o_driver_unregister(&i2o_proc_driver); + return rc; + } return 0; -} +}; + +/** + * i2o_proc_exit - Exit function for procfs + * + * Unregisters Proc OSM and removes procfs entries. + */ +static void __exit i2o_proc_exit(void) +{ + i2o_driver_unregister(&i2o_proc_driver); + i2o_proc_fs_destroy(); +}; MODULE_AUTHOR("Deepak Saxena"); MODULE_DESCRIPTION("I2O procfs Handler"); MODULE_LICENSE("GPL"); -static void __exit i2o_proc_exit(void) -{ - destroy_i2o_procfs(); - i2o_remove_handler(&i2o_proc_handler); -} - -#ifdef MODULE module_init(i2o_proc_init); -#endif module_exit(i2o_proc_exit); - --- linux-2.6.9-rc1/drivers/message/i2o/i2o_scsi.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/message/i2o/i2o_scsi.c 2004-09-02 20:51:52.000000000 -0700 @@ -1,4 +1,4 @@ -/* +/* * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any @@ -19,13 +19,13 @@ * * o Each (bus,lun) is a logical device in I2O. We keep a map * table. We spoof failed selection for unmapped units - * o Request sense buffers can come back for free. + * o Request sense buffers can come back for free. * o Scatter gather is a bit dynamic. We have to investigate at * setup time. * o Some of our resources are dynamically shared. The i2o core * needs a message reservation protocol to avoid swap v net * deadlocking. We need to back off queue requests. - * + * * In general the firmware wants to help. Where its help isn't performance * useful we just ignore the aid. Its not worth the code in truth. * @@ -40,7 +40,6 @@ * Fix the resource management problems. */ - #include #include #include @@ -53,79 +52,229 @@ #include #include #include +#include +#include + #include #include #include #include -#include -#include #include -#include -#include #include - +#include +#include #define VERSION_STRING "Version 0.1.2" -//#define DRIVERDEBUG +static struct i2o_driver i2o_scsi_driver; -#ifdef DRIVERDEBUG -#define dprintk(s, args...) printk(s, ## args) -#else -#define dprintk(s, args...) -#endif +static int i2o_scsi_max_id = 16; +static int i2o_scsi_max_lun = 8; + +struct i2o_scsi_host { + struct Scsi_Host *scsi_host; /* pointer to the SCSI host */ + struct i2o_controller *iop; /* pointer to the I2O controller */ + struct i2o_device *channel[0]; /* channel->i2o_dev mapping table */ +}; + +static struct scsi_host_template i2o_scsi_host_template; #define I2O_SCSI_CAN_QUEUE 4 -#define MAXHOSTS 32 -struct i2o_scsi_host -{ - struct i2o_controller *controller; - s16 task[16][8]; /* Allow 16 devices for now */ - unsigned long tagclock[16][8]; /* Tag clock for queueing */ - s16 bus_task; /* The adapter TID */ +/* SCSI OSM class handling definition */ +static struct i2o_class_id i2o_scsi_class_id[] = { + {I2O_CLASS_SCSI_PERIPHERAL}, + {I2O_CLASS_END} }; -static int scsi_context; -static int lun_done; -static int i2o_scsi_hosts; - -static u32 *retry[32]; -static struct i2o_controller *retry_ctrl[32]; -static struct timer_list retry_timer; -static spinlock_t retry_lock = SPIN_LOCK_UNLOCKED; -static int retry_ct = 0; +static struct i2o_scsi_host *i2o_scsi_host_alloc(struct i2o_controller *c) +{ + struct i2o_scsi_host *i2o_shost; + struct i2o_device *i2o_dev; + struct Scsi_Host *scsi_host; + int max_channel = 0; + u8 type; + int i; + size_t size; + i2o_status_block *sb; -static atomic_t queue_depth; + list_for_each_entry(i2o_dev, &c->devices, list) + if (i2o_dev->lct_data.class_id == I2O_CLASS_BUS_ADAPTER_PORT) { + if (i2o_parm_field_get(i2o_dev, 0x0000, 0, &type, 1) || (type == 1)) /* SCSI bus */ + max_channel++; + } -/* - * SG Chain buffer support... + if (!max_channel) { + printk(KERN_WARNING "scsi-osm: no channels found on %s\n", + c->name); + return ERR_PTR(-EFAULT); + } + + size = max_channel * sizeof(struct i2o_device *) + + sizeof(struct i2o_scsi_host); + + scsi_host = scsi_host_alloc(&i2o_scsi_host_template, size); + if (!scsi_host) { + printk(KERN_WARNING "scsi-osm: Could not allocate SCSI host\n"); + return ERR_PTR(-ENOMEM); + } + + scsi_host->max_channel = max_channel - 1; + scsi_host->max_id = i2o_scsi_max_id; + scsi_host->max_lun = i2o_scsi_max_lun; + scsi_host->this_id = c->unit; + + sb = c->status_block.virt; + + scsi_host->sg_tablesize = (sb->inbound_frame_size - + sizeof(struct i2o_message) / 4 - 6) / 2; + + i2o_shost = (struct i2o_scsi_host *)scsi_host->hostdata; + i2o_shost->scsi_host = scsi_host; + i2o_shost->iop = c; + + i = 0; + list_for_each_entry(i2o_dev, &c->devices, list) + if (i2o_dev->lct_data.class_id == I2O_CLASS_BUS_ADAPTER_PORT) { + if (i2o_parm_field_get(i2o_dev, 0x0000, 0, &type, 1) || (type == 1)) /* only SCSI bus */ + i2o_shost->channel[i++] = i2o_dev; + + if (i >= max_channel) + break; + } + + return i2o_shost; +}; + +/** + * i2o_scsi_get_host - Get an I2O SCSI host + * @c: I2O controller to for which to get the SCSI host + * + * If the I2O controller already exists as SCSI host, the SCSI host + * is returned, otherwise the I2O controller is added to the SCSI + * core. + * + * Returns pointer to the I2O SCSI host on success or NULL on failure. */ +static struct i2o_scsi_host *i2o_scsi_get_host(struct i2o_controller *c) +{ + return c->driver_data[i2o_scsi_driver.context]; +}; -#define SG_MAX_FRAGS 64 +/** + * i2o_scsi_remove - Remove I2O device from SCSI core + * @dev: device which should be removed + * + * Removes the I2O device from the SCSI core again. + * + * Returns 0 on success. + */ +static int i2o_scsi_remove(struct device *dev) +{ + struct i2o_device *i2o_dev = to_i2o_device(dev); + struct i2o_controller *c = i2o_dev->iop; + struct i2o_scsi_host *i2o_shost; + struct scsi_device *scsi_dev; -/* - * FIXME: we should allocate one of these per bus we find as we - * locate them not in a lump at boot. + i2o_shost = i2o_scsi_get_host(c); + + shost_for_each_device(scsi_dev, i2o_shost->scsi_host) + if (scsi_dev->hostdata == i2o_dev) { + scsi_remove_device(scsi_dev); + scsi_device_put(scsi_dev); + break; + } + + return 0; +}; + +/** + * i2o_scsi_probe - verify if dev is a I2O SCSI device and install it + * @dev: device to verify if it is a I2O SCSI device + * + * Retrieve channel, id and lun for I2O device. If everthing goes well + * register the I2O device as SCSI device on the I2O SCSI controller. + * + * Returns 0 on success or negative error code on failure. */ - -typedef struct _chain_buf +static int i2o_scsi_probe(struct device *dev) +{ + struct i2o_device *i2o_dev = to_i2o_device(dev); + struct i2o_controller *c = i2o_dev->iop; + struct i2o_scsi_host *i2o_shost; + struct Scsi_Host *scsi_host; + struct i2o_device *parent; + struct scsi_device *scsi_dev; + u32 id; + u64 lun; + int channel = -1; + int i; + + i2o_shost = i2o_scsi_get_host(c); + if (!i2o_shost) + return -EFAULT; + + scsi_host = i2o_shost->scsi_host; + + if (i2o_parm_field_get(i2o_dev, 0, 3, &id, 4) < 0) + return -EFAULT; + + if (id >= scsi_host->max_id) { + printk(KERN_WARNING "scsi-osm: SCSI device id (%d) >= max_id " + "of I2O host (%d)", id, scsi_host->max_id); + return -EFAULT; + } + + if (i2o_parm_field_get(i2o_dev, 0, 4, &lun, 8) < 0) + return -EFAULT; + if (lun >= scsi_host->max_lun) { + printk(KERN_WARNING "scsi-osm: SCSI device id (%d) >= max_lun " + "of I2O host (%d)", (unsigned int)lun, + scsi_host->max_lun); + return -EFAULT; + } + + parent = i2o_iop_find_device(c, i2o_dev->lct_data.parent_tid); + if (!parent) { + printk(KERN_WARNING "scsi-osm: can not find parent of device " + "%03x\n", i2o_dev->lct_data.tid); + return -EFAULT; + } + + for (i = 0; i <= i2o_shost->scsi_host->max_channel; i++) + if (i2o_shost->channel[i] == parent) + channel = i; + + if (channel == -1) { + printk(KERN_WARNING "scsi-osm: can not find channel of device " + "%03x\n", i2o_dev->lct_data.tid); + return -EFAULT; + } + + scsi_dev = + __scsi_add_device(i2o_shost->scsi_host, channel, id, lun, i2o_dev); + + if (!scsi_dev) { + printk(KERN_WARNING "scsi-osm: can not add SCSI device " + "%03x\n", i2o_dev->lct_data.tid); + return -EFAULT; + } + + pr_debug("Added new SCSI device %03x (cannel: %d, id: %d, lun: %d)\n", + i2o_dev->lct_data.tid, channel, id, (unsigned int)lun); + + return 0; +}; + +static const char *i2o_scsi_info(struct Scsi_Host *SChost) { - u32 sg_flags_cnt[SG_MAX_FRAGS]; - u32 sg_buf[SG_MAX_FRAGS]; -} chain_buf; - -#define SG_CHAIN_BUF_SZ sizeof(chain_buf) - -#define SG_MAX_BUFS (i2o_num_controllers * I2O_SCSI_CAN_QUEUE) -#define SG_CHAIN_POOL_SZ (SG_MAX_BUFS * SG_CHAIN_BUF_SZ) - -static int max_sg_len = 0; -static chain_buf *sg_chain_pool = NULL; -static int sg_chain_tag = 0; -static int sg_max_frags = SG_MAX_FRAGS; + struct i2o_scsi_host *hostdata; + hostdata = (struct i2o_scsi_host *)SChost->hostdata; + return hostdata->iop->name; +} +#if 0 /** * i2o_retry_run - retry on timeout * @f: unused @@ -136,16 +285,16 @@ static int sg_max_frags = SG_MAX_FRAGS; * and its default handler should be this in the core, and this * call a 2nd "I give up" handler in the OSM ? */ - + static void i2o_retry_run(unsigned long f) { int i; unsigned long flags; - + spin_lock_irqsave(&retry_lock, flags); - for(i=0;i 0 + * on success and if the reply should be flushed. Returns negative error + * code on failure and if the reply should be flushed. */ - -static void i2o_scsi_reply(struct i2o_handler *h, struct i2o_controller *c, struct i2o_message *msg) +static int i2o_scsi_reply(struct i2o_controller *c, u32 m, + struct i2o_message *msg) { - struct scsi_cmnd *current_command; - spinlock_t *lock; - u32 *m = (u32 *)msg; - u8 as,ds,st; - unsigned long flags; + struct scsi_cmnd *cmd; + struct device *dev; + u8 as, ds, st; + + cmd = i2o_cntxt_list_get(c, readl(&msg->u.s.tcntxt)); + + if (msg->u.head[0] & (1 << 13)) { + struct i2o_message *pmsg; /* preserved message */ + u32 pm; + + pm = readl(&msg->body[3]); + + pmsg = c->in_queue.virt + pm; - if(m[0] & (1<<13)) - { printk("IOP fail.\n"); printk("From %d To %d Cmd %d.\n", - (m[1]>>12)&0xFFF, - m[1]&0xFFF, - m[1]>>24); - printk("Failure Code %d.\n", m[4]>>24); - if(m[4]&(1<<16)) + (msg->u.head[1] >> 12) & 0xFFF, + msg->u.head[1] & 0xFFF, msg->u.head[1] >> 24); + printk("Failure Code %d.\n", msg->body[0] >> 24); + if (msg->body[0] & (1 << 16)) printk("Format error.\n"); - if(m[4]&(1<<17)) + if (msg->body[0] & (1 << 17)) printk("Path error.\n"); - if(m[4]&(1<<18)) + if (msg->body[0] & (1 << 18)) printk("Path State.\n"); - if(m[4]&(1<<18)) + if (msg->body[0] & (1 << 18)) printk("Congestion.\n"); - - m=(u32 *)bus_to_virt(m[7]); - printk("Failing message is %p.\n", m); - - /* This isnt a fast path .. */ - spin_lock_irqsave(&retry_lock, flags); - - if((m[4]&(1<<18)) && retry_ct < 32) - { - retry_ctrl[retry_ct]=c; - retry[retry_ct]=m; - if(!retry_ct++) - { - retry_timer.expires=jiffies+1; - add_timer(&retry_timer); - } - spin_unlock_irqrestore(&retry_lock, flags); - } - else - { - spin_unlock_irqrestore(&retry_lock, flags); - /* Create a scsi error for this */ - current_command = (struct scsi_cmnd *)i2o_context_list_get(m[3], c); - if(!current_command) - return; - - lock = current_command->device->host->host_lock; - printk("Aborted %ld\n", current_command->serial_number); - - spin_lock_irqsave(lock, flags); - current_command->result = DID_ERROR << 16; - current_command->scsi_done(current_command); - spin_unlock_irqrestore(lock, flags); - - /* Now flush the message by making it a NOP */ - m[0]&=0x00FFFFFF; - m[0]|=(I2O_CMD_UTIL_NOP)<<24; - i2o_post_message(c,virt_to_bus(m)); - } - return; + + printk("Failing message is %p.\n", pmsg); + + cmd = i2o_cntxt_list_get(c, readl(&pmsg->u.s.tcntxt)); + if (!cmd) + return 1; + + printk("Aborted %ld\n", cmd->serial_number); + cmd->result = DID_ERROR << 16; + cmd->scsi_done(cmd); + + /* Now flush the message by making it a NOP */ + i2o_msg_nop(c, pm); + + return 1; } - - prefetchw(&queue_depth); - - + /* - * Low byte is device status, next is adapter status, - * (then one byte reserved), then request status. + * Low byte is device status, next is adapter status, + * (then one byte reserved), then request status. */ - ds=(u8)le32_to_cpu(m[4]); - as=(u8)le32_to_cpu(m[4]>>8); - st=(u8)le32_to_cpu(m[4]>>24); - - dprintk(KERN_INFO "i2o got a scsi reply %08X: ", m[0]); - dprintk(KERN_INFO "m[2]=%08X: ", m[2]); - dprintk(KERN_INFO "m[4]=%08X\n", m[4]); - - if(m[2]&0x80000000) - { - if(m[2]&0x40000000) - { - dprintk(KERN_INFO "Event.\n"); - lun_done=1; - return; - } - printk(KERN_INFO "i2o_scsi: bus reset completed.\n"); - return; - } + ds = (u8) readl(&msg->body[0]); + as = (u8) (readl(&msg->body[0]) >> 8); + st = (u8) (readl(&msg->body[0]) >> 24); - current_command = (struct scsi_cmnd *)i2o_context_list_get(m[3], c); - /* - * Is this a control request coming back - eg an abort ? + * Is this a control request coming back - eg an abort ? */ - - atomic_dec(&queue_depth); - if(current_command==NULL) - { - if(st) - dprintk(KERN_WARNING "SCSI abort: %08X", m[4]); - dprintk(KERN_INFO "SCSI abort completed.\n"); - return; + if (!cmd) { + if (st) + printk(KERN_WARNING "SCSI abort: %08X", + readl(&msg->body[0])); + printk(KERN_INFO "SCSI abort completed.\n"); + return -EFAULT; } - - dprintk(KERN_INFO "Completed %ld\n", current_command->serial_number); - - if(st == 0x06) - { - if(le32_to_cpu(m[5]) < current_command->underflow) - { - int i; - printk(KERN_ERR "SCSI: underflow 0x%08X 0x%08X\n", - le32_to_cpu(m[5]), current_command->underflow); - printk("Cmd: "); - for(i=0;i<15;i++) - printk("%02X ", current_command->cmnd[i]); - printk(".\n"); - } - else st=0; - } - - if(st) - { - /* An error has occurred */ - dprintk(KERN_WARNING "SCSI error %08X", m[4]); - - if (as == 0x0E) - /* SCSI Reset */ - current_command->result = DID_RESET << 16; - else if (as == 0x0F) - current_command->result = DID_PARITY << 16; - else - current_command->result = DID_ERROR << 16; - } - else - /* - * It worked maybe ? - */ - current_command->result = DID_OK << 16 | ds; - - if (current_command->use_sg) { - pci_unmap_sg(c->pdev, - (struct scatterlist *)current_command->buffer, - current_command->use_sg, - current_command->sc_data_direction); - } else if (current_command->request_bufflen) { - pci_unmap_single(c->pdev, - (dma_addr_t)((long)current_command->SCp.ptr), - current_command->request_bufflen, - current_command->sc_data_direction); - } - - lock = current_command->device->host->host_lock; - spin_lock_irqsave(lock, flags); - current_command->scsi_done(current_command); - spin_unlock_irqrestore(lock, flags); - return; -} + pr_debug("Completed %ld\n", cmd->serial_number); -struct i2o_handler i2o_scsi_handler = { - .reply = i2o_scsi_reply, - .name = "I2O SCSI OSM", - .class = I2O_CLASS_SCSI_PERIPHERAL, -}; + if (st) { + u32 count, error; + /* An error has occurred */ -/** - * i2o_find_lun - report the lun of an i2o device - * @c: i2o controller owning the device - * @d: i2o disk device - * @target: filled in with target id - * @lun: filled in with target lun - * - * Query an I2O device to find out its SCSI lun and target numbering. We - * don't currently handle some of the fancy SCSI-3 stuff although our - * querying is sufficient to do so. - */ - -static int i2o_find_lun(struct i2o_controller *c, struct i2o_device *d, int *target, int *lun) -{ - u8 reply[8]; - - if(i2o_query_scalar(c, d->lct_data.tid, 0, 3, reply, 4)<0) - return -1; - - *target=reply[0]; - - if(i2o_query_scalar(c, d->lct_data.tid, 0, 4, reply, 8)<0) - return -1; + switch (st) { + case 0x06: + count = readl(&msg->body[1]); + if (count < cmd->underflow) { + int i; + printk(KERN_ERR "SCSI: underflow 0x%08X 0x%08X" + "\n", count, cmd->underflow); + printk("Cmd: "); + for (i = 0; i < 15; i++) + printk("%02X ", cmd->cmnd[i]); + printk(".\n"); + cmd->result = (DID_ERROR << 16); + } + break; - *lun=reply[1]; + default: + error = readl(&msg->body[0]); - dprintk(KERN_INFO "SCSI (%d,%d)\n", *target, *lun); - return 0; -} + printk(KERN_ERR "scsi-osm: SCSI error %08x\n", error); -/** - * i2o_scsi_init - initialize an i2o device for scsi - * @c: i2o controller owning the device - * @d: scsi controller - * @shpnt: scsi device we wish it to become - * - * Enumerate the scsi peripheral/fibre channel peripheral class - * devices that are children of the controller. From that we build - * a translation map for the command queue code. Since I2O works on - * its own tid's we effectively have to think backwards to get what - * the midlayer wants - */ - -static void i2o_scsi_init(struct i2o_controller *c, struct i2o_device *d, struct Scsi_Host *shpnt) -{ - struct i2o_device *unit; - struct i2o_scsi_host *h =(struct i2o_scsi_host *)shpnt->hostdata; - int lun; - int target; - - h->controller=c; - h->bus_task=d->lct_data.tid; - - for(target=0;target<16;target++) - for(lun=0;lun<8;lun++) - h->task[target][lun] = -1; - - for(unit=c->devices;unit!=NULL;unit=unit->next) - { - dprintk(KERN_INFO "Class %03X, parent %d, want %d.\n", - unit->lct_data.class_id, unit->lct_data.parent_tid, d->lct_data.tid); - - /* Only look at scsi and fc devices */ - if ( (unit->lct_data.class_id != I2O_CLASS_SCSI_PERIPHERAL) - && (unit->lct_data.class_id != I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL) - ) - continue; - - /* On our bus ? */ - dprintk(KERN_INFO "Found a disk (%d).\n", unit->lct_data.tid); - if ((unit->lct_data.parent_tid == d->lct_data.tid) - || (unit->lct_data.parent_tid == d->lct_data.parent_tid) - ) - { - u16 limit; - dprintk(KERN_INFO "Its ours.\n"); - if(i2o_find_lun(c, unit, &target, &lun)==-1) - { - printk(KERN_ERR "i2o_scsi: Unable to get lun for tid %d.\n", unit->lct_data.tid); - continue; + if ((error & 0xff) == 0x02 /*CHECK_CONDITION */ ) { + int i; + u32 len = sizeof(cmd->sense_buffer); + len = (len > 40) ? 40 : len; + // Copy over the sense data + memcpy(cmd->sense_buffer, (void *)&msg->body[3], + len); + for (i = 0; i <= len; i++) + printk(KERN_INFO "%02x\n", + cmd->sense_buffer[i]); + if (cmd->sense_buffer[0] == 0x70 + && cmd->sense_buffer[2] == DATA_PROTECT) { + /* This is to handle an array failed */ + cmd->result = (DID_TIME_OUT << 16); + printk(KERN_WARNING "%s: SCSI Data " + "Protect-Device (%d,%d,%d) " + "hba_status=0x%x, dev_status=" + "0x%x, cmd=0x%x\n", c->name, + (u32) cmd->device->channel, + (u32) cmd->device->id, + (u32) cmd->device->lun, + (error >> 8) & 0xff, + error & 0xff, cmd->cmnd[0]); + } else + cmd->result = (DID_ERROR << 16); + + break; } - dprintk(KERN_INFO "Found disk %d %d.\n", target, lun); - h->task[target][lun]=unit->lct_data.tid; - h->tagclock[target][lun]=jiffies; - - /* Get the max fragments/request */ - i2o_query_scalar(c, d->lct_data.tid, 0xF103, 3, &limit, 2); - - /* sanity */ - if ( limit == 0 ) - { - printk(KERN_WARNING "i2o_scsi: Ignoring unreasonable SG limit of 0 from IOP!\n"); - limit = 1; + + switch (as) { + case 0x0E: + /* SCSI Reset */ + cmd->result = DID_RESET << 16; + break; + + case 0x0F: + cmd->result = DID_PARITY << 16; + break; + + default: + cmd->result = DID_ERROR << 16; + break; } - - shpnt->sg_tablesize = limit; - dprintk(KERN_INFO "i2o_scsi: set scatter-gather to %d.\n", - shpnt->sg_tablesize); + break; } - } -} -/** - * i2o_scsi_detect - probe for I2O scsi devices - * @tpnt: scsi layer template - * - * I2O is a little odd here. The I2O core already knows what the - * devices are. It also knows them by disk and tape as well as - * by controller. We register each I2O scsi class object as a - * scsi controller and then let the enumeration fake up the rest - */ - -static int i2o_scsi_detect(struct scsi_host_template * tpnt) -{ - struct Scsi_Host *shpnt = NULL; - int i; - int count; + cmd->scsi_done(cmd); + return 1; + } - printk(KERN_INFO "i2o_scsi.c: %s\n", VERSION_STRING); + cmd->result = DID_OK << 16 | ds; - if(i2o_install_handler(&i2o_scsi_handler)<0) - { - printk(KERN_ERR "i2o_scsi: Unable to install OSM handler.\n"); - return 0; - } - scsi_context = i2o_scsi_handler.context; - - if((sg_chain_pool = kmalloc(SG_CHAIN_POOL_SZ, GFP_KERNEL)) == NULL) - { - printk(KERN_INFO "i2o_scsi: Unable to alloc %d byte SG chain buffer pool.\n", SG_CHAIN_POOL_SZ); - printk(KERN_INFO "i2o_scsi: SG chaining DISABLED!\n"); - sg_max_frags = 11; - } - else - { - printk(KERN_INFO " chain_pool: %d bytes @ %p\n", SG_CHAIN_POOL_SZ, sg_chain_pool); - printk(KERN_INFO " (%d byte buffers X %d can_queue X %d i2o controllers)\n", - SG_CHAIN_BUF_SZ, I2O_SCSI_CAN_QUEUE, i2o_num_controllers); - sg_max_frags = SG_MAX_FRAGS; // 64 - } - - init_timer(&retry_timer); - retry_timer.data = 0UL; - retry_timer.function = i2o_retry_run; - -// printk("SCSI OSM at %d.\n", scsi_context); - - for (count = 0, i = 0; i < MAX_I2O_CONTROLLERS; i++) - { - struct i2o_controller *c=i2o_find_controller(i); - struct i2o_device *d; - /* - * This controller doesn't exist. - */ - - if(c==NULL) - continue; - - /* - * Fixme - we need some altered device locking. This - * is racing with device addition in theory. Easy to fix. - */ - - for(d=c->devices;d!=NULL;d=d->next) - { - /* - * bus_adapter, SCSI (obsolete), or FibreChannel busses only - */ - if( (d->lct_data.class_id!=I2O_CLASS_BUS_ADAPTER_PORT) // bus_adapter -// && (d->lct_data.class_id!=I2O_CLASS_FIBRE_CHANNEL_PORT) // FC_PORT - ) - continue; - - shpnt = scsi_register(tpnt, sizeof(struct i2o_scsi_host)); - if(shpnt==NULL) - continue; - shpnt->unique_id = (u32)d; - shpnt->io_port = 0; - shpnt->n_io_port = 0; - shpnt->irq = 0; - shpnt->this_id = /* Good question */15; - i2o_scsi_init(c, d, shpnt); - count++; - } - } - i2o_scsi_hosts = count; - - if(count==0) - { - if(sg_chain_pool!=NULL) - { - kfree(sg_chain_pool); - sg_chain_pool = NULL; - } - flush_pending(); - del_timer(&retry_timer); - i2o_remove_handler(&i2o_scsi_handler); + cmd->scsi_done(cmd); + + dev = &c->pdev->dev; + if (cmd->use_sg) + dma_unmap_sg(dev, (struct scatterlist *)cmd->buffer, + cmd->use_sg, cmd->sc_data_direction); + else if (cmd->request_bufflen) + dma_unmap_single(dev, (dma_addr_t) ((long)cmd->SCp.ptr), + cmd->request_bufflen, cmd->sc_data_direction); + + return 1; +}; + +/** + * i2o_scsi_notify_controller_add - Retrieve notifications of added + * controllers + * @c: the controller which was added + * + * If a I2O controller is added, we catch the notification to add a + * corresponding Scsi_Host. + */ +void i2o_scsi_notify_controller_add(struct i2o_controller *c) +{ + struct i2o_scsi_host *i2o_shost; + int rc; + + i2o_shost = i2o_scsi_host_alloc(c); + if (IS_ERR(i2o_shost)) { + printk(KERN_ERR "scsi-osm: Could not initialize" + " SCSI host\n"); + return; } - - return count; -} -static int i2o_scsi_release(struct Scsi_Host *host) -{ - if(--i2o_scsi_hosts==0) - { - if(sg_chain_pool!=NULL) - { - kfree(sg_chain_pool); - sg_chain_pool = NULL; - } - flush_pending(); - del_timer(&retry_timer); - i2o_remove_handler(&i2o_scsi_handler); + rc = scsi_add_host(i2o_shost->scsi_host, &c->device); + if (rc) { + printk(KERN_ERR "scsi-osm: Could not add SCSI " + "host\n"); + scsi_host_put(i2o_shost->scsi_host); + return; } - scsi_unregister(host); + c->driver_data[i2o_scsi_driver.context] = i2o_shost; - return 0; -} + pr_debug("new I2O SCSI host added\n"); +}; +/** + * i2o_scsi_notify_controller_remove - Retrieve notifications of removed + * controllers + * @c: the controller which was removed + * + * If a I2O controller is removed, we catch the notification to remove the + * corresponding Scsi_Host. + */ +void i2o_scsi_notify_controller_remove(struct i2o_controller *c) +{ + struct i2o_scsi_host *i2o_shost; + i2o_shost = i2o_scsi_get_host(c); + if (!i2o_shost) + return; -static const char *i2o_scsi_info(struct Scsi_Host *SChost) -{ - struct i2o_scsi_host *hostdata; - hostdata = (struct i2o_scsi_host *)SChost->hostdata; - return(&hostdata->controller->name[0]); -} + c->driver_data[i2o_scsi_driver.context] = NULL; + + scsi_remove_host(i2o_shost->scsi_host); + scsi_host_put(i2o_shost->scsi_host); + pr_debug("I2O SCSI host removed\n"); +}; + +/* SCSI OSM driver struct */ +static struct i2o_driver i2o_scsi_driver = { + .name = "scsi-osm", + .reply = i2o_scsi_reply, + .classes = i2o_scsi_class_id, + .notify_controller_add = i2o_scsi_notify_controller_add, + .notify_controller_remove = i2o_scsi_notify_controller_remove, + .driver = { + .probe = i2o_scsi_probe, + .remove = i2o_scsi_remove, + }, +}; /** - * i2o_scsi_queuecommand - queue a SCSI command + * i2o_scsi_queuecommand - queue a SCSI command * @SCpnt: scsi command pointer * @done: callback for completion * - * Issue a scsi comamnd asynchronously. Return 0 on success or 1 if - * we hit an error (normally message queue congestion). The only + * Issue a scsi command asynchronously. Return 0 on success or 1 if + * we hit an error (normally message queue congestion). The only * minor complication here is that I2O deals with the device addressing * so we have to map the bus/dev/lun back to an I2O handle as well - * as faking absent devices ourself. + * as faking absent devices ourself. * * Locks: takes the controller lock on error path only */ - + static int i2o_scsi_queuecommand(struct scsi_cmnd *SCpnt, void (*done) (struct scsi_cmnd *)) { - int i; - int tid; struct i2o_controller *c; - struct scsi_cmnd *current_command; struct Scsi_Host *host; - struct i2o_scsi_host *hostdata; - u32 *msg, *mptr; + struct i2o_device *i2o_dev; + struct device *dev; + int tid; + struct i2o_message *msg; u32 m; - u32 *lenptr; - int direction; - int scsidir; - u32 len; - u32 reqlen; - u32 tag; - unsigned long flags; - - static int max_qd = 1; - + u32 scsi_flags, sg_flags; + u32 *mptr, *lenptr; + u32 len, reqlen; + int i; + /* - * Do the incoming paperwork + * Do the incoming paperwork */ - + + i2o_dev = SCpnt->device->hostdata; host = SCpnt->device->host; - hostdata = (struct i2o_scsi_host *)host->hostdata; - - c = hostdata->controller; - prefetch(c); - prefetchw(&queue_depth); + c = i2o_dev->iop; + dev = &c->pdev->dev; SCpnt->scsi_done = done; - - if(SCpnt->device->id > 15) - { - printk(KERN_ERR "i2o_scsi: Wild target %d.\n", SCpnt->device->id); - return -1; - } - - tid = hostdata->task[SCpnt->device->id][SCpnt->device->lun]; - - dprintk(KERN_INFO "qcmd: Tid = %d\n", tid); - - current_command = SCpnt; /* set current command */ - current_command->scsi_done = done; /* set ptr to done function */ - - /* We don't have such a device. Pretend we did the command - and that selection timed out */ - - if(tid == -1) - { + + if (unlikely(!i2o_dev)) { + printk(KERN_WARNING "scsi-osm: no I2O device in request\n"); SCpnt->result = DID_NO_CONNECT << 16; done(SCpnt); return 0; } - - dprintk(KERN_INFO "Real scsi messages.\n"); + + tid = i2o_dev->lct_data.tid; + + pr_debug("qcmd: Tid = %03x\n", tid); + pr_debug("Real scsi messages.\n"); /* - * Obtain an I2O message. If there are none free then - * throw it back to the scsi layer - */ - - m = le32_to_cpu(I2O_POST_READ32(c)); - if(m==0xFFFFFFFF) - return 1; + * Obtain an I2O message. If there are none free then + * throw it back to the scsi layer + */ + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return SCSI_MLQUEUE_HOST_BUSY; - msg = (u32 *)(c->msg_virt + m); - /* - * Put together a scsi execscb message + * Put together a scsi execscb message */ - + len = SCpnt->request_bufflen; - direction = 0x00000000; // SGL IN (osm<--iop) - - if (SCpnt->sc_data_direction == DMA_NONE) { - scsidir = 0x00000000; // DATA NO XFER - } else if (SCpnt->sc_data_direction == DMA_TO_DEVICE) { - direction = 0x04000000; // SGL OUT (osm-->iop) - scsidir = 0x80000000; // DATA OUT (iop-->dev) - } else if(SCpnt->sc_data_direction == DMA_FROM_DEVICE) { - scsidir = 0x40000000; // DATA IN (iop<--dev) - } else { + + switch (SCpnt->sc_data_direction) { + case PCI_DMA_NONE: + scsi_flags = 0x00000000; // DATA NO XFER + sg_flags = 0x00000000; + break; + + case PCI_DMA_TODEVICE: + scsi_flags = 0x80000000; // DATA OUT (iop-->dev) + sg_flags = 0x14000000; + break; + + case PCI_DMA_FROMDEVICE: + scsi_flags = 0x40000000; // DATA IN (iop<--dev) + sg_flags = 0x10000000; + break; + + default: /* Unknown - kill the command */ SCpnt->result = DID_NO_CONNECT << 16; - - /* We must lock the request queue while completing */ - spin_lock_irqsave(host->host_lock, flags); done(SCpnt); - spin_unlock_irqrestore(host->host_lock, flags); return 0; } - - i2o_raw_writel(I2O_CMD_SCSI_EXEC<<24|HOST_TID<<12|tid, &msg[1]); - i2o_raw_writel(scsi_context, &msg[2]); /* So the I2O layer passes to us */ - i2o_raw_writel(i2o_context_list_add(SCpnt, c), &msg[3]); /* We want the SCSI control block back */ + writel(I2O_CMD_SCSI_EXEC << 24 | HOST_TID << 12 | tid, &msg->u.head[1]); + writel(i2o_scsi_driver.context, &msg->u.s.icntxt); + + /* We want the SCSI control block back */ + writel(i2o_cntxt_list_add(c, SCpnt), &msg->u.s.tcntxt); /* LSI_920_PCI_QUIRK * - * Intermittant observations of msg frame word data corruption - * observed on msg[4] after: - * WRITE, READ-MODIFY-WRITE - * operations. 19990606 -sralston + * Intermittant observations of msg frame word data corruption + * observed on msg[4] after: + * WRITE, READ-MODIFY-WRITE + * operations. 19990606 -sralston * - * (Hence we build this word via tag. Its good practice anyway - * we don't want fetches over PCI needlessly) + * (Hence we build this word via tag. Its good practice anyway + * we don't want fetches over PCI needlessly) */ - tag=0; - + /* Attach tags to the devices */ /* - * Attach tags to the devices - */ - if(SCpnt->device->tagged_supported) - { - /* - * Some drives are too stupid to handle fairness issues - * with tagged queueing. We throw in the odd ordered - * tag to stop them starving themselves. - */ - if((jiffies - hostdata->tagclock[SCpnt->device->id][SCpnt->device->lun]) > (5*HZ)) - { - tag=0x01800000; /* ORDERED! */ - hostdata->tagclock[SCpnt->device->id][SCpnt->device->lun]=jiffies; - } - else - { - /* Hmmm... I always see value of 0 here, - * of which {HEAD_OF, ORDERED, SIMPLE} are NOT! -sralston - */ - if(SCpnt->tag == HEAD_OF_QUEUE_TAG) - tag=0x01000000; - else if(SCpnt->tag == ORDERED_QUEUE_TAG) - tag=0x01800000; - } - } + if(SCpnt->device->tagged_supported) { + if(SCpnt->tag == HEAD_OF_QUEUE_TAG) + scsi_flags |= 0x01000000; + else if(SCpnt->tag == ORDERED_QUEUE_TAG) + scsi_flags |= 0x01800000; + } + */ /* Direction, disconnect ok, tag, CDBLen */ - i2o_raw_writel(scsidir|0x20000000|SCpnt->cmd_len|tag, &msg[4]); + writel(scsi_flags | 0x20200000 | SCpnt->cmd_len, &msg->body[0]); - mptr=msg+5; + mptr = &msg->body[1]; - /* - * Write SCSI command into the message - always 16 byte block - */ - + /* Write SCSI command into the message - always 16 byte block */ memcpy_toio(mptr, SCpnt->cmnd, 16); - mptr+=4; - lenptr=mptr++; /* Remember me - fill in when we know */ - + mptr += 4; + lenptr = mptr++; /* Remember me - fill in when we know */ + reqlen = 12; // SINGLE SGE - - /* - * Now fill in the SGList and command - * - * FIXME: we need to set the sglist limits according to the - * message size of the I2O controller. We might only have room - * for 6 or so worst case - */ - - if(SCpnt->use_sg) - { - struct scatterlist *sg = (struct scatterlist *)SCpnt->request_buffer; + + /* Now fill in the SGList and command */ + if (SCpnt->use_sg) { + struct scatterlist *sg; int sg_count; - int chain = 0; - + + sg = SCpnt->request_buffer; len = 0; - sg_count = pci_map_sg(c->pdev, sg, SCpnt->use_sg, - SCpnt->sc_data_direction); + sg_count = dma_map_sg(dev, sg, SCpnt->use_sg, + SCpnt->sc_data_direction); - /* FIXME: handle fail */ - if(!sg_count) - BUG(); - - if((sg_max_frags > 11) && (SCpnt->use_sg > 11)) - { - chain = 1; - /* - * Need to chain! - */ - i2o_raw_writel(direction|0xB0000000|(SCpnt->use_sg*2*4), mptr++); - i2o_raw_writel(virt_to_bus(sg_chain_pool + sg_chain_tag), mptr); - mptr = (u32*)(sg_chain_pool + sg_chain_tag); - if (SCpnt->use_sg > max_sg_len) - { - max_sg_len = SCpnt->use_sg; - printk("i2o_scsi: Chain SG! SCpnt=%p, SG_FragCnt=%d, SG_idx=%d\n", - SCpnt, SCpnt->use_sg, sg_chain_tag); - } - if ( ++sg_chain_tag == SG_MAX_BUFS ) - sg_chain_tag = 0; - for(i = 0 ; i < SCpnt->use_sg; i++) - { - *mptr++=cpu_to_le32(direction|0x10000000|sg_dma_len(sg)); - len+=sg_dma_len(sg); - *mptr++=cpu_to_le32(sg_dma_address(sg)); - sg++; - } - mptr[-2]=cpu_to_le32(direction|0xD0000000|sg_dma_len(sg-1)); - } - else - { - for(i = 0 ; i < SCpnt->use_sg; i++) - { - i2o_raw_writel(direction|0x10000000|sg_dma_len(sg), mptr++); - len+=sg->length; - i2o_raw_writel(sg_dma_address(sg), mptr++); - sg++; - } + if (unlikely(sg_count <= 0)) + return -ENOMEM; - /* Make this an end of list. Again evade the 920 bug and - unwanted PCI read traffic */ - - i2o_raw_writel(direction|0xD0000000|sg_dma_len(sg-1), &mptr[-2]); - } - - if(!chain) - reqlen = mptr - msg; - - i2o_raw_writel(len, lenptr); - - if(len != SCpnt->underflow) - printk("Cmd len %08X Cmd underflow %08X\n", - len, SCpnt->underflow); - } - else - { - dprintk(KERN_INFO "non sg for %p, %d\n", SCpnt->request_buffer, - SCpnt->request_bufflen); - i2o_raw_writel(len = SCpnt->request_bufflen, lenptr); - if(len == 0) - { - reqlen = 9; + for (i = SCpnt->use_sg; i > 0; i--) { + if (i == 1) + sg_flags |= 0xC0000000; + writel(sg_flags | sg_dma_len(sg), mptr++); + writel(sg_dma_address(sg), mptr++); + len += sg_dma_len(sg); + sg++; } - else - { + + reqlen = mptr - &msg->u.head[0]; + writel(len, lenptr); + } else { + len = SCpnt->request_bufflen; + + writel(len, lenptr); + + if (len > 0) { dma_addr_t dma_addr; - dma_addr = pci_map_single(c->pdev, - SCpnt->request_buffer, - SCpnt->request_bufflen, - SCpnt->sc_data_direction); - if(dma_addr == 0) - BUG(); /* How to handle ?? */ - SCpnt->SCp.ptr = (char *)(unsigned long) dma_addr; - i2o_raw_writel(0xD0000000|direction|SCpnt->request_bufflen, mptr++); - i2o_raw_writel(dma_addr, mptr++); - } + + dma_addr = dma_map_single(dev, SCpnt->request_buffer, + SCpnt->request_bufflen, + SCpnt->sc_data_direction); + if (!dma_addr) + return -ENOMEM; + + SCpnt->SCp.ptr = (void *)(unsigned long)dma_addr; + sg_flags |= 0xC0000000; + writel(sg_flags | SCpnt->request_bufflen, mptr++); + writel(dma_addr, mptr++); + } else + reqlen = 9; } - - /* - * Stick the headers on - */ - i2o_raw_writel(reqlen<<16 | SGL_OFFSET_10, msg); - + /* Stick the headers on */ + writel(reqlen << 16 | SGL_OFFSET_10, &msg->u.head[0]); + /* Queue the message */ - i2o_post_message(c,m); - - atomic_inc(&queue_depth); - - if(atomic_read(&queue_depth)> max_qd) - { - max_qd=atomic_read(&queue_depth); - printk("Queue depth now %d.\n", max_qd); - } - - mb(); - dprintk(KERN_INFO "Issued %ld\n", current_command->serial_number); - + i2o_msg_post(c, m); + + pr_debug("Issued %ld\n", SCpnt->serial_number); + return 0; -} +}; /** - * i2o_scsi_abort - abort a running command + * i2o_scsi_abort - abort a running command * @SCpnt: command to abort * * Ask the I2O controller to abort a command. This is an asynchrnous - * process and our callback handler will see the command complete - * with an aborted message if it succeeds. + * process and our callback handler will see the command complete with an + * aborted message if it succeeds. * - * Locks: no locks are held or needed + * Returns 0 if the command is successfully aborted or negative error code + * on failure. */ - -static int i2o_scsi_abort(struct scsi_cmnd * SCpnt) +int i2o_scsi_abort(struct scsi_cmnd *SCpnt) { + struct i2o_device *i2o_dev; struct i2o_controller *c; - struct Scsi_Host *host; - struct i2o_scsi_host *hostdata; - u32 msg[5]; + struct i2o_message *msg; + u32 m; int tid; int status = FAILED; - - printk(KERN_WARNING "i2o_scsi: Aborting command block.\n"); - - host = SCpnt->device->host; - hostdata = (struct i2o_scsi_host *)host->hostdata; - tid = hostdata->task[SCpnt->device->id][SCpnt->device->lun]; - if(tid==-1) - { - printk(KERN_ERR "i2o_scsi: Impossible command to abort!\n"); - return status; - } - c = hostdata->controller; - - spin_unlock_irq(host->host_lock); - - msg[0] = FIVE_WORD_MSG_SIZE; - msg[1] = I2O_CMD_SCSI_ABORT<<24|HOST_TID<<12|tid; - msg[2] = scsi_context; - msg[3] = 0; - msg[4] = i2o_context_list_remove(SCpnt, c); - if(i2o_post_wait(c, msg, sizeof(msg), 240)) - status = SUCCESS; - - spin_lock_irq(host->host_lock); - return status; -} - -/** - * i2o_scsi_bus_reset - Issue a SCSI reset - * @SCpnt: the command that caused the reset - * - * Perform a SCSI bus reset operation. In I2O this is just a message - * we pass. I2O can do clever multi-initiator and shared reset stuff - * but we don't support this. - * - * Locks: called with no lock held, requires no locks. - */ - -static int i2o_scsi_bus_reset(struct scsi_cmnd * SCpnt) -{ - int tid; - struct i2o_controller *c; - struct Scsi_Host *host; - struct i2o_scsi_host *hostdata; - u32 m; - void *msg; - unsigned long timeout; - - - /* - * Find the TID for the bus - */ - - - host = SCpnt->device->host; - spin_unlock_irq(host->host_lock); - - printk(KERN_WARNING "i2o_scsi: Attempting to reset the bus.\n"); - - hostdata = (struct i2o_scsi_host *)host->hostdata; - tid = hostdata->bus_task; - c = hostdata->controller; + printk(KERN_WARNING "i2o_scsi: Aborting command block.\n"); - /* - * Now send a SCSI reset request. Any remaining commands - * will be aborted by the IOP. We need to catch the reply - * possibly ? - */ + i2o_dev = SCpnt->device->hostdata; + c = i2o_dev->iop; + tid = i2o_dev->lct_data.tid; + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return SCSI_MLQUEUE_HOST_BUSY; + + writel(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_SCSI_ABORT << 24 | HOST_TID << 12 | tid, + &msg->u.head[1]); + writel(i2o_cntxt_list_get_ptr(c, SCpnt), &msg->body[0]); - timeout = jiffies+2*HZ; - do - { - m = le32_to_cpu(I2O_POST_READ32(c)); - if(m != 0xFFFFFFFF) - break; - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - mb(); - } - while(time_before(jiffies, timeout)); - - - msg = c->msg_virt + m; - i2o_raw_writel(FOUR_WORD_MSG_SIZE|SGL_OFFSET_0, msg); - i2o_raw_writel(I2O_CMD_SCSI_BUSRESET<<24|HOST_TID<<12|tid, msg+4); - i2o_raw_writel(scsi_context|0x80000000, msg+8); - /* We use the top bit to split controller and unit transactions */ - /* Now store unit,tid so we can tie the completion back to a specific device */ - __raw_writel(c->unit << 16 | tid, msg+12); - wmb(); - - /* We want the command to complete after we return */ - spin_lock_irq(host->host_lock); - i2o_post_message(c,m); + if (i2o_msg_post_wait(c, m, I2O_TIMEOUT_SCSI_SCB_ABORT)) + status = SUCCESS; - /* Should we wait for the reset to complete ? */ - return SUCCESS; + return status; } /** * i2o_scsi_bios_param - Invent disk geometry - * @sdev: scsi device + * @sdev: scsi device * @dev: block layer device * @capacity: size in sectors * @ip: geometry array * - * This is anyones guess quite frankly. We use the same rules everyone + * This is anyones guess quite frankly. We use the same rules everyone * else appears to and hope. It seems to work. */ - -static int i2o_scsi_bios_param(struct scsi_device * sdev, - struct block_device *dev, sector_t capacity, int *ip) + +static int i2o_scsi_bios_param(struct scsi_device *sdev, + struct block_device *dev, sector_t capacity, + int *ip) { int size; @@ -1023,25 +819,64 @@ static int i2o_scsi_bios_param(struct sc return 0; } -MODULE_AUTHOR("Red Hat Software"); -MODULE_LICENSE("GPL"); +static struct scsi_host_template i2o_scsi_host_template = { + .proc_name = "SCSI-OSM", + .name = "I2O SCSI Peripheral OSM", + .info = i2o_scsi_info, + .queuecommand = i2o_scsi_queuecommand, + .eh_abort_handler = i2o_scsi_abort, + .bios_param = i2o_scsi_bios_param, + .can_queue = I2O_SCSI_CAN_QUEUE, + .sg_tablesize = 8, + .cmd_per_lun = 6, + .use_clustering = ENABLE_CLUSTERING, +}; + +/* +int +i2o_scsi_queuecommand(struct scsi_cmnd * cmd, void (*done) (struct scsi_cmnd *)) +{ + printk(KERN_INFO "queuecommand\n"); + return SCSI_MLQUEUE_HOST_BUSY; +}; +*/ + +/** + * i2o_scsi_init - SCSI OSM initialization function + * + * Register SCSI OSM into I2O core. + * + * Returns 0 on success or negative error code on failure. + */ +static int __init i2o_scsi_init(void) +{ + int rc; + printk(KERN_INFO "I2O SCSI Peripheral OSM\n"); -static struct scsi_host_template driver_template = { - .proc_name = "i2o_scsi", - .name = "I2O SCSI Layer", - .detect = i2o_scsi_detect, - .release = i2o_scsi_release, - .info = i2o_scsi_info, - .queuecommand = i2o_scsi_queuecommand, - .eh_abort_handler = i2o_scsi_abort, - .eh_bus_reset_handler = i2o_scsi_bus_reset, - .bios_param = i2o_scsi_bios_param, - .can_queue = I2O_SCSI_CAN_QUEUE, - .this_id = 15, - .sg_tablesize = 8, - .cmd_per_lun = 6, - .use_clustering = ENABLE_CLUSTERING, + /* Register SCSI OSM into I2O core */ + rc = i2o_driver_register(&i2o_scsi_driver); + if (rc) { + printk(KERN_ERR "scsi-osm: Could not register SCSI driver\n"); + return rc; + } + + return 0; }; -#include "../../scsi/scsi_module.c" +/** + * i2o_scsi_exit - SCSI OSM exit function + * + * Unregisters SCSI OSM from I2O core. + */ +static void __exit i2o_scsi_exit(void) +{ + /* Unregister I2O SCSI OSM from I2O core */ + i2o_driver_unregister(&i2o_scsi_driver); +}; + +MODULE_AUTHOR("Red Hat Software"); +MODULE_LICENSE("GPL"); + +module_init(i2o_scsi_init); +module_exit(i2o_scsi_exit); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/message/i2o/iop.c 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,1258 @@ +/* + * Functions to handle I2O controllers and I2O message handling + * + * Copyright (C) 1999-2002 Red Hat Software + * + * Written by Alan Cox, Building Number Three Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * A lot of the I2O message side code from this is taken from the + * Red Creek RCPCI45 adapter driver by Red Creek Communications + * + * Fixes/additions: + * Philipp Rumpf + * Juha Sievänen + * Auvo Häkkinen + * Deepak Saxena + * Boji T Kannanthanam + * Alan Cox : + * Ported to Linux 2.5. + * Markus Lidel : + * Minor fixes for 2.6. + */ + +#include +#include + +/* global I2O controller list */ +LIST_HEAD(i2o_controllers); + +/* + * global I2O System Table. Contains information about all the IOPs in the + * system. Used to inform IOPs about each others existence. + */ +static struct i2o_dma i2o_systab; + +/* Module internal functions from other sources */ +extern struct i2o_driver i2o_exec_driver; +extern int i2o_exec_lct_get(struct i2o_controller *); +extern void i2o_device_remove(struct i2o_device *); + +extern int __init i2o_driver_init(void); +extern void __exit i2o_driver_exit(void); +extern int __init i2o_exec_init(void); +extern void __exit i2o_exec_exit(void); +extern int __init i2o_pci_init(void); +extern void __exit i2o_pci_exit(void); +extern int i2o_device_init(void); +extern void i2o_device_exit(void); + +/** + * i2o_msg_nop - Returns a message which is not used + * @c: I2O controller from which the message was created + * @m: message which should be returned + * + * If you fetch a message via i2o_msg_get, and can't use it, you must + * return the message with this function. Otherwise the message frame + * is lost. + */ +void i2o_msg_nop(struct i2o_controller *c, u32 m) +{ + struct i2o_message *msg = c->in_queue.virt + m; + + writel(THREE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + writel(0, &msg->u.head[2]); + writel(0, &msg->u.head[3]); + i2o_msg_post(c, m); +}; + +/** + * i2o_msg_get_wait - obtain an I2O message from the IOP + * @c: I2O controller + * @msg: pointer to a I2O message pointer + * @wait: how long to wait until timeout + * + * This function waits up to wait seconds for a message slot to be + * available. + * + * On a success the message is returned and the pointer to the message is + * set in msg. The returned message is the physical page frame offset + * address from the read port (see the i2o spec). If no message is + * available returns I2O_QUEUE_EMPTY and msg is leaved untouched. + */ +u32 i2o_msg_get_wait(struct i2o_controller *c, struct i2o_message **msg, + int wait) +{ + unsigned long timeout = jiffies + wait * HZ; + u32 m; + + while ((m = i2o_msg_get(c, msg)) == I2O_QUEUE_EMPTY) { + if (time_after(jiffies, timeout)) { + pr_debug("%s: Timeout waiting for message frame.\n", + c->name); + return I2O_QUEUE_EMPTY; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + + return m; +}; + +#if BITS_PER_LONG == 64 +/** + * i2o_cntxt_list_add - Append a pointer to context list and return a id + * @c: controller to which the context list belong + * @ptr: pointer to add to the context list + * + * Because the context field in I2O is only 32-bit large, on 64-bit the + * pointer is to large to fit in the context field. The i2o_cntxt_list + * functions therefore map pointers to context fields. + * + * Returns context id > 0 on success or 0 on failure. + */ +u32 i2o_cntxt_list_add(struct i2o_controller *c, void *ptr) +{ + struct i2o_context_list_element *entry; + unsigned long flags; + + if (!ptr) + printk(KERN_ERR "NULL pointer found!\n"); + + entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + if (!entry) { + printk(KERN_ERR "i2o: Could not allocate memory for context " + "list element\n"); + return 0; + } + + entry->ptr = ptr; + entry->timestamp = jiffies; + INIT_LIST_HEAD(&entry->list); + + spin_lock_irqsave(&c->context_list_lock, flags); + + if (unlikely(atomic_inc_and_test(&c->context_list_counter))) + atomic_inc(&c->context_list_counter); + + entry->context = atomic_read(&c->context_list_counter); + + list_add(&entry->list, &c->context_list); + + spin_unlock_irqrestore(&c->context_list_lock, flags); + + pr_debug("Add context to list %p -> %d\n", ptr, context); + + return entry->context; +}; + +/** + * i2o_cntxt_list_remove - Remove a pointer from the context list + * @c: controller to which the context list belong + * @ptr: pointer which should be removed from the context list + * + * Removes a previously added pointer from the context list and returns + * the matching context id. + * + * Returns context id on succes or 0 on failure. + */ +u32 i2o_cntxt_list_remove(struct i2o_controller *c, void *ptr) +{ + struct i2o_context_list_element *entry; + u32 context = 0; + unsigned long flags; + + spin_lock_irqsave(&c->context_list_lock, flags); + list_for_each_entry(entry, &c->context_list, list) + if (entry->ptr == ptr) { + list_del(&entry->list); + context = entry->context; + kfree(entry); + break; + } + spin_unlock_irqrestore(&c->context_list_lock, flags); + + if (!context) + printk(KERN_WARNING "i2o: Could not remove nonexistent ptr " + "%p\n", ptr); + + pr_debug("remove ptr from context list %d -> %p\n", context, ptr); + + return context; +}; + +/** + * i2o_cntxt_list_get - Get a pointer from the context list and remove it + * @c: controller to which the context list belong + * @context: context id to which the pointer belong + * + * Returns pointer to the matching context id on success or NULL on + * failure. + */ +void *i2o_cntxt_list_get(struct i2o_controller *c, u32 context) +{ + struct i2o_context_list_element *entry; + unsigned long flags; + void *ptr = NULL; + + spin_lock_irqsave(&c->context_list_lock, flags); + list_for_each_entry(entry, &c->context_list, list) + if (entry->context == context) { + list_del(&entry->list); + ptr = entry->ptr; + kfree(entry); + break; + } + spin_unlock_irqrestore(&c->context_list_lock, flags); + + if (!ptr) + printk(KERN_WARNING "i2o: context id %d not found\n", context); + + pr_debug("get ptr from context list %d -> %p\n", context, ptr); + + return ptr; +}; + +/** + * i2o_cntxt_list_get_ptr - Get a context id from the context list + * @c: controller to which the context list belong + * @ptr: pointer to which the context id should be fetched + * + * Returns context id which matches to the pointer on succes or 0 on + * failure. + */ +u32 i2o_cntxt_list_get_ptr(struct i2o_controller * c, void *ptr) +{ + struct i2o_context_list_element *entry; + u32 context = 0; + unsigned long flags; + + spin_lock_irqsave(&c->context_list_lock, flags); + list_for_each_entry(entry, &c->context_list, list) + if (entry->ptr == ptr) { + context = entry->context; + break; + } + spin_unlock_irqrestore(&c->context_list_lock, flags); + + if (!context) + printk(KERN_WARNING "i2o: Could not find nonexistent ptr " + "%p\n", ptr); + + pr_debug("get context id from context list %p -> %d\n", ptr, context); + + return context; +}; +#endif + +/** + * i2o_iop_find - Find an I2O controller by id + * @unit: unit number of the I2O controller to search for + * + * Lookup the I2O controller on the controller list. + * + * Returns pointer to the I2O controller on success or NULL if not found. + */ +struct i2o_controller *i2o_find_iop(int unit) +{ + struct i2o_controller *c; + + list_for_each_entry(c, &i2o_controllers, list) { + if (c->unit == unit) + return c; + } + + return NULL; +}; + +/** + * i2o_iop_find_device - Find a I2O device on an I2O controller + * @c: I2O controller where the I2O device hangs on + * @tid: TID of the I2O device to search for + * + * Searches the devices of the I2O controller for a device with TID tid and + * returns it. + * + * Returns a pointer to the I2O device if found, otherwise NULL. + */ +struct i2o_device *i2o_iop_find_device(struct i2o_controller *c, u16 tid) +{ + struct i2o_device *dev; + + list_for_each_entry(dev, &c->devices, list) + if (dev->lct_data.tid == tid) + return dev; + + return NULL; +}; + +/** + * i2o_quiesce_controller - quiesce controller + * @c: controller + * + * Quiesce an IOP. Causes IOP to make external operation quiescent + * (i2o 'READY' state). Internal operation of the IOP continues normally. + * + * Returns 0 on success or negative error code on failure. + */ +static int i2o_iop_quiesce(struct i2o_controller *c) +{ + struct i2o_message *msg; + u32 m; + i2o_status_block *sb = c->status_block.virt; + int rc; + + i2o_status_get(c); + + /* SysQuiesce discarded if IOP not in READY or OPERATIONAL state */ + if ((sb->iop_state != ADAPTER_STATE_READY) && + (sb->iop_state != ADAPTER_STATE_OPERATIONAL)) + return 0; + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_SYS_QUIESCE << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + + /* Long timeout needed for quiesce if lots of devices */ + if ((rc = i2o_msg_post_wait(c, m, 240))) + printk(KERN_INFO "%s: Unable to quiesce (status=%#x).\n", + c->name, -rc); + else + pr_debug("%s: Quiesced.\n", c->name); + + i2o_status_get(c); // Entered READY state + + return rc; +}; + +/** + * i2o_iop_enable - move controller from ready to OPERATIONAL + * @c: I2O controller + * + * Enable IOP. This allows the IOP to resume external operations and + * reverses the effect of a quiesce. Returns zero or an error code if + * an error occurs. + */ +static int i2o_iop_enable(struct i2o_controller *c) +{ + struct i2o_message *msg; + u32 m; + i2o_status_block *sb = c->status_block.virt; + int rc; + + i2o_status_get(c); + + /* Enable only allowed on READY state */ + if (sb->iop_state != ADAPTER_STATE_READY) + return -EINVAL; + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_SYS_ENABLE << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + + /* How long of a timeout do we need? */ + if ((rc = i2o_msg_post_wait(c, m, 240))) + printk(KERN_ERR "%s: Could not enable (status=%#x).\n", + c->name, -rc); + else + pr_debug("%s: Enabled.\n", c->name); + + i2o_status_get(c); // entered OPERATIONAL state + + return rc; +}; + +/** + * i2o_iop_quiesce_all - Quiesce all I2O controllers on the system + * + * Quiesce all I2O controllers which are connected to the system. + */ +static inline void i2o_iop_quiesce_all(void) +{ + struct i2o_controller *c, *tmp; + + list_for_each_entry_safe(c, tmp, &i2o_controllers, list) { + if (!c->no_quiesce) + i2o_iop_quiesce(c); + } +}; + +/** + * i2o_iop_enable_all - Enables all controllers on the system + * + * Enables all I2O controllers which are connected to the system. + */ +static inline void i2o_iop_enable_all(void) +{ + struct i2o_controller *c, *tmp; + + list_for_each_entry_safe(c, tmp, &i2o_controllers, list) + i2o_iop_enable(c); +}; + +/** + * i2o_clear_controller - Bring I2O controller into HOLD state + * @c: controller + * + * Clear an IOP to HOLD state, ie. terminate external operations, clear all + * input queues and prepare for a system restart. IOP's internal operation + * continues normally and the outbound queue is alive. The IOP is not + * expected to rebuild its LCT. + * + * Returns 0 on success or negative error code on failure. + */ +static int i2o_iop_clear(struct i2o_controller *c) +{ + struct i2o_message *msg; + u32 m; + int rc; + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + /* Quiesce all IOPs first */ + i2o_iop_quiesce_all(); + + writel(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_ADAPTER_CLEAR << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + + if ((rc = i2o_msg_post_wait(c, m, 30))) + printk(KERN_INFO "%s: Unable to clear (status=%#x).\n", + c->name, -rc); + else + pr_debug("%s: Cleared.\n", c->name); + + /* Enable all IOPs */ + i2o_iop_enable_all(); + + i2o_status_get(c); + + return rc; +} + +/** + * i2o_iop_reset - reset an I2O controller + * @c: controller to reset + * + * Reset the IOP into INIT state and wait until IOP gets into RESET state. + * Terminate all external operations, clear IOP's inbound and outbound + * queues, terminate all DDMs, and reload the IOP's operating environment + * and all local DDMs. The IOP rebuilds its LCT. + */ +static int i2o_iop_reset(struct i2o_controller *c) +{ + u8 *status = c->status.virt; + struct i2o_message *msg; + u32 m; + unsigned long timeout; + i2o_status_block *sb = c->status_block.virt; + int rc = 0; + + pr_debug("Resetting controller\n"); + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + memset(status, 0, 4); + + /* Quiesce all IOPs first */ + i2o_iop_quiesce_all(); + + writel(EIGHT_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_ADAPTER_RESET << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + writel(i2o_exec_driver.context, &msg->u.s.icntxt); + writel(0, &msg->u.s.tcntxt); //FIXME: use reasonable transaction context + writel(0, &msg->body[0]); + writel(0, &msg->body[1]); + writel(i2o_ptr_low((void *)c->status.phys), &msg->body[2]); + writel(i2o_ptr_high((void *)c->status.phys), &msg->body[3]); + + i2o_msg_post(c, m); + + /* Wait for a reply */ + timeout = jiffies + I2O_TIMEOUT_RESET * HZ; + while (!*status) { + if (time_after(jiffies, timeout)) { + printk(KERN_ERR "IOP reset timeout.\n"); + rc = -ETIMEDOUT; + goto exit; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + + rmb(); + } + + if (*status == I2O_CMD_IN_PROGRESS) { + /* + * Once the reset is sent, the IOP goes into the INIT state + * which is indeterminate. We need to wait until the IOP + * has rebooted before we can let the system talk to + * it. We read the inbound Free_List until a message is + * available. If we can't read one in the given ammount of + * time, we assume the IOP could not reboot properly. + */ + pr_debug("%s: Reset in progress, waiting for reboot...\n", + c->name); + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_RESET); + while (m == I2O_QUEUE_EMPTY) { + if (time_after(jiffies, timeout)) { + printk(KERN_ERR "IOP reset timeout.\n"); + rc = -ETIMEDOUT; + goto exit; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_RESET); + } + i2o_msg_nop(c, m); + } + + /* from here all quiesce commands are safe */ + c->no_quiesce = 0; + + /* If IopReset was rejected or didn't perform reset, try IopClear */ + i2o_status_get(c); + if (*status == I2O_CMD_REJECTED || sb->iop_state != ADAPTER_STATE_RESET) { + printk(KERN_WARNING "%s: Reset rejected, trying to clear\n", + c->name); + i2o_iop_clear(c); + } else + pr_debug("%s: Reset completed.\n", c->name); + + exit: + /* Enable all IOPs */ + i2o_iop_enable_all(); + + return rc; +}; + +/** + * i2o_iop_init_outbound_queue - setup the outbound message queue + * @c: I2O controller + * + * Clear and (re)initialize IOP's outbound queue and post the message + * frames to the IOP. + * + * Returns 0 on success or a negative errno code on failure. + */ +int i2o_iop_init_outbound_queue(struct i2o_controller *c) +{ + u8 *status = c->status.virt; + u32 m; + struct i2o_message *msg; + ulong timeout; + int i; + + pr_debug("%s: Initializing Outbound Queue...\n", c->name); + + memset(status, 0, 4); + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(EIGHT_WORD_MSG_SIZE | TRL_OFFSET_6, &msg->u.head[0]); + writel(I2O_CMD_OUTBOUND_INIT << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + writel(i2o_exec_driver.context, &msg->u.s.icntxt); + writel(0x0106, &msg->u.s.tcntxt); /* FIXME: why 0x0106, maybe in + Spec? */ + writel(PAGE_SIZE, &msg->body[0]); + writel(MSG_FRAME_SIZE << 16 | 0x80, &msg->body[1]); /* Outbound msg frame + size in words and Initcode */ + writel(0xd0000004, &msg->body[2]); + writel(i2o_ptr_low((void *)c->status.phys), &msg->body[3]); + writel(i2o_ptr_high((void *)c->status.phys), &msg->body[4]); + + i2o_msg_post(c, m); + + timeout = jiffies + I2O_TIMEOUT_INIT_OUTBOUND_QUEUE * HZ; + while (*status <= I2O_CMD_IN_PROGRESS) { + if (time_after(jiffies, timeout)) { + printk(KERN_WARNING "%s: Timeout Initializing\n", + c->name); + return -ETIMEDOUT; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + + rmb(); + } + + m = c->out_queue.phys; + + /* Post frames */ + for (i = 0; i < NMBR_MSG_FRAMES; i++) { + i2o_flush_reply(c, m); + m += MSG_FRAME_SIZE * 4; + } + + return 0; +} + +/** + * i2o_iop_activate - Bring controller up to HOLD + * @c: controller + * + * This function brings an I2O controller into HOLD state. The adapter + * is reset if necessary and then the queues and resource table are read. + * + * Returns 0 on success or negative error code on failure. + */ +static int i2o_iop_activate(struct i2o_controller *c) +{ + i2o_status_block *sb = c->status_block.virt; + int rc; + /* In INIT state, Wait Inbound Q to initialize (in i2o_status_get) */ + /* In READY state, Get status */ + + rc = i2o_status_get(c); + if (rc) { + printk(KERN_INFO "Unable to obtain status of %s, " + "attempting a reset.\n", c->name); + if (i2o_iop_reset(c)) + return rc; + } + + if (sb->i2o_version > I2OVER15) { + printk(KERN_ERR "%s: Not running vrs. 1.5. of the I2O " + "Specification.\n", c->name); + return -ENODEV; + } + + switch (sb->iop_state) { + case ADAPTER_STATE_FAULTED: + printk(KERN_CRIT "%s: hardware fault\n", c->name); + return -ENODEV; + + case ADAPTER_STATE_READY: + case ADAPTER_STATE_OPERATIONAL: + case ADAPTER_STATE_HOLD: + case ADAPTER_STATE_FAILED: + pr_debug("already running, trying to reset...\n"); + if (i2o_iop_reset(c)) + return -ENODEV; + } + + rc = i2o_iop_init_outbound_queue(c); + if (rc) + return rc; + + /* In HOLD state */ + + rc = i2o_hrt_get(c); + if (rc) + return rc; + + return 0; +}; + +/** + * i2o_iop_systab_set - Set the I2O System Table of the specified IOP + * @c: I2O controller to which the system table should be send + * + * Before the systab could be set i2o_systab_build() must be called. + * + * Returns 0 on success or negative error code on failure. + */ +static int i2o_iop_systab_set(struct i2o_controller *c) +{ + struct i2o_message *msg; + u32 m; + i2o_status_block *sb = c->status_block.virt; + struct device *dev = &c->pdev->dev; + struct resource *root; + int rc; + + if (sb->current_mem_size < sb->desired_mem_size) { + struct resource *res = &c->mem_resource; + res->name = c->pdev->bus->name; + res->flags = IORESOURCE_MEM; + res->start = 0; + res->end = 0; + printk("%s: requires private memory resources.\n", c->name); + root = pci_find_parent_resource(c->pdev, res); + if (root == NULL) + printk("Can't find parent resource!\n"); + if (root && allocate_resource(root, res, sb->desired_mem_size, sb->desired_mem_size, sb->desired_mem_size, 1 << 20, /* Unspecified, so use 1Mb and play safe */ + NULL, NULL) >= 0) { + c->mem_alloc = 1; + sb->current_mem_size = 1 + res->end - res->start; + sb->current_mem_base = res->start; + printk(KERN_INFO + "%s: allocated %ld bytes of PCI memory at 0x%08lX.\n", + c->name, 1 + res->end - res->start, res->start); + } + } + + if (sb->current_io_size < sb->desired_io_size) { + struct resource *res = &c->io_resource; + res->name = c->pdev->bus->name; + res->flags = IORESOURCE_IO; + res->start = 0; + res->end = 0; + printk("%s: requires private memory resources.\n", c->name); + root = pci_find_parent_resource(c->pdev, res); + if (root == NULL) + printk("Can't find parent resource!\n"); + if (root && allocate_resource(root, res, sb->desired_io_size, sb->desired_io_size, sb->desired_io_size, 1 << 20, /* Unspecified, so use 1Mb and play safe */ + NULL, NULL) >= 0) { + c->io_alloc = 1; + sb->current_io_size = 1 + res->end - res->start; + sb->current_mem_base = res->start; + printk(KERN_INFO + "%s: allocated %ld bytes of PCI I/O at 0x%08lX.\n", + c->name, 1 + res->end - res->start, res->start); + } + } + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + i2o_systab.phys = dma_map_single(dev, i2o_systab.virt, i2o_systab.len, + PCI_DMA_TODEVICE); + if (!i2o_systab.phys) { + i2o_msg_nop(c, m); + return -ENOMEM; + } + + writel(I2O_MESSAGE_SIZE(12) | SGL_OFFSET_6, &msg->u.head[0]); + writel(I2O_CMD_SYS_TAB_SET << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + + /* + * Provide three SGL-elements: + * System table (SysTab), Private memory space declaration and + * Private i/o space declaration + * + * FIXME: is this still true? + * Nasty one here. We can't use dma_alloc_coherent to send the + * same table to everyone. We have to go remap it for them all + */ + + writel(c->unit + 2, &msg->body[0]); + writel(0, &msg->body[1]); + writel(0x54000000 | i2o_systab.phys, &msg->body[2]); + writel(i2o_systab.phys, &msg->body[3]); + writel(0x54000000 | sb->current_mem_size, &msg->body[4]); + writel(sb->current_mem_base, &msg->body[5]); + writel(0xd4000000 | sb->current_io_size, &msg->body[6]); + writel(sb->current_io_base, &msg->body[6]); + + rc = i2o_msg_post_wait(c, m, 120); + + dma_unmap_single(dev, i2o_systab.phys, i2o_systab.len, + PCI_DMA_TODEVICE); + + if (rc < 0) + printk(KERN_ERR "%s: Unable to set SysTab (status=%#x).\n", + c->name, -rc); + else + pr_debug("%s: SysTab set.\n", c->name); + + i2o_status_get(c); // Entered READY state + + return rc; +} + +/** + * i2o_iop_online - Bring a controller online into OPERATIONAL state. + * @c: I2O controller + * + * Send the system table and enable the I2O controller. + * + * Returns 0 on success or negativer error code on failure. + */ +static int i2o_iop_online(struct i2o_controller *c) +{ + int rc; + + rc = i2o_iop_systab_set(c); + if (rc) + return rc; + + /* In READY state */ + pr_debug("%s: Attempting to enable...\n", c->name); + rc = i2o_iop_enable(c); + if (rc) + return rc; + + return 0; +}; + +/** + * i2o_iop_remove - Remove the I2O controller from the I2O core + * @c: I2O controller + * + * Remove the I2O controller from the I2O core. If devices are attached to + * the controller remove these also and finally reset the controller. + */ +void i2o_iop_remove(struct i2o_controller *c) +{ + struct i2o_device *dev, *tmp; + + pr_debug("Deleting controller %s\n", c->name); + + i2o_driver_notify_controller_remove_all(c); + + list_del(&c->list); + + list_for_each_entry_safe(dev, tmp, &c->devices, list) + i2o_device_remove(dev); + + /* Ask the IOP to switch to RESET state */ + i2o_iop_reset(c); +} + +/** + * i2o_systab_build - Build system table + * + * The system table contains information about all the IOPs in the system + * (duh) and is used by the Executives on the IOPs to establish peer2peer + * connections. We're not supporting peer2peer at the moment, but this + * will be needed down the road for things like lan2lan forwarding. + * + * Returns 0 on success or negative error code on failure. + */ +static int i2o_systab_build(void) +{ + struct i2o_controller *c, *tmp; + int num_controllers = 0; + u32 change_ind = 0; + int count = 0; + struct i2o_sys_tbl *systab = i2o_systab.virt; + + list_for_each_entry_safe(c, tmp, &i2o_controllers, list) + num_controllers++; + + if (systab) { + change_ind = systab->change_ind; + kfree(i2o_systab.virt); + } + + /* Header + IOPs */ + i2o_systab.len = sizeof(struct i2o_sys_tbl) + num_controllers * + sizeof(struct i2o_sys_tbl_entry); + + systab = i2o_systab.virt = kmalloc(i2o_systab.len, GFP_KERNEL); + if (!systab) { + printk(KERN_ERR "i2o: unable to allocate memory for System " + "Table\n"); + return -ENOMEM; + } + memset(systab, 0, i2o_systab.len); + + systab->version = I2OVERSION; + systab->change_ind = change_ind + 1; + + list_for_each_entry_safe(c, tmp, &i2o_controllers, list) { + i2o_status_block *sb; + + if (count >= num_controllers) { + printk(KERN_ERR "i2o: controller added while building " + "system table\n"); + break; + } + + sb = c->status_block.virt; + + /* + * Get updated IOP state so we have the latest information + * + * We should delete the controller at this point if it + * doesn't respond since if it's not on the system table + * it is techninically not part of the I2O subsystem... + */ + if (unlikely(i2o_status_get(c))) { + printk(KERN_ERR "%s: Deleting b/c could not get status" + " while attempting to build system table\n", + c->name); + i2o_iop_remove(c); + continue; // try the next one + } + + systab->iops[count].org_id = sb->org_id; + systab->iops[count].iop_id = c->unit + 2; + systab->iops[count].seg_num = 0; + systab->iops[count].i2o_version = sb->i2o_version; + systab->iops[count].iop_state = sb->iop_state; + systab->iops[count].msg_type = sb->msg_type; + systab->iops[count].frame_size = sb->inbound_frame_size; + systab->iops[count].last_changed = change_ind; + systab->iops[count].iop_capabilities = sb->iop_capabilities; + systab->iops[count].inbound_low = i2o_ptr_low(c->post_port); + systab->iops[count].inbound_high = i2o_ptr_high(c->post_port); + + count++; + } + + systab->num_entries = count; + + return 0; +}; + +/** + * i2o_parse_hrt - Parse the hardware resource table. + * @c: I2O controller + * + * We don't do anything with it except dumping it (in debug mode). + * + * Returns 0. + */ +static int i2o_parse_hrt(struct i2o_controller *c) +{ + i2o_dump_hrt(c); + return 0; +}; + +/** + * i2o_status_get - Get the status block from the I2O controller + * @c: I2O controller + * + * Issue a status query on the controller. This updates the attached + * status block. The status block could then be accessed through + * c->status_block. + * + * Returns 0 on sucess or negative error code on failure. + */ +int i2o_status_get(struct i2o_controller *c) +{ + struct i2o_message *msg; + u32 m; + u8 *status_block; + unsigned long timeout; + + status_block = (u8 *) c->status_block.virt; + memset(status_block, 0, sizeof(i2o_status_block)); + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(NINE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_STATUS_GET << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + writel(i2o_exec_driver.context, &msg->u.s.icntxt); + writel(0, &msg->u.s.tcntxt); // FIXME: use resonable transaction context + writel(0, &msg->body[0]); + writel(0, &msg->body[1]); + writel(i2o_ptr_low((void *)c->status_block.phys), &msg->body[2]); + writel(i2o_ptr_high((void *)c->status_block.phys), &msg->body[3]); + writel(sizeof(i2o_status_block), &msg->body[4]); /* always 88 bytes */ + + i2o_msg_post(c, m); + + /* Wait for a reply */ + timeout = jiffies + I2O_TIMEOUT_STATUS_GET * HZ; + while (status_block[87] != 0xFF) { + if (time_after(jiffies, timeout)) { + printk(KERN_ERR "%s: Get status timeout.\n", c->name); + return -ETIMEDOUT; + } + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + + rmb(); + } + +#ifdef DEBUG + i2o_debug_state(c); +#endif + + return 0; +} + +/* + * i2o_hrt_get - Get the Hardware Resource Table from the I2O controller + * @c: I2O controller from which the HRT should be fetched + * + * The HRT contains information about possible hidden devices but is + * mostly useless to us. + * + * Returns 0 on success or negativer error code on failure. + */ +int i2o_hrt_get(struct i2o_controller *c) +{ + int rc; + int i; + i2o_hrt *hrt = c->hrt.virt; + u32 size = sizeof(i2o_hrt); + struct device *dev = &c->pdev->dev; + + for (i = 0; i < I2O_HRT_GET_TRIES; i++) { + struct i2o_message *msg; + u32 m; + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(SIX_WORD_MSG_SIZE | SGL_OFFSET_4, &msg->u.head[0]); + writel(I2O_CMD_HRT_GET << 24 | HOST_TID << 12 | ADAPTER_TID, + &msg->u.head[1]); + writel(0xd0000000 | c->hrt.len, &msg->body[0]); + writel(c->hrt.phys, &msg->body[1]); + + rc = i2o_msg_post_wait_mem(c, m, 20, &c->hrt); + + if (rc < 0) { + printk(KERN_ERR "%s: Unable to get HRT (status=%#x)\n", + c->name, -rc); + return rc; + } + + size = hrt->num_entries * hrt->entry_len << 2; + if (size > c->hrt.len) { + if (i2o_dma_realloc(dev, &c->hrt, size, GFP_KERNEL)) + return -ENOMEM; + else + hrt = c->hrt.virt; + } else + return i2o_parse_hrt(c); + } + + printk(KERN_ERR "%s: Unable to get HRT after %d tries, giving up\n", + c->name, I2O_HRT_GET_TRIES); + + return -EBUSY; +} + +/** + * i2o_iop_alloc - Allocate and initialize a i2o_controller struct + * + * Allocate the necessary memory for a i2o_controller struct and + * initialize the lists. + * + * Returns a pointer to the I2O controller or a negative error code on + * failure. + */ +struct i2o_controller *i2o_iop_alloc(void) +{ + static int unit = 0; /* 0 and 1 are NULL IOP and Local Host */ + struct i2o_controller *c; + + c = kmalloc(sizeof(*c), GFP_KERNEL); + if (!c) { + printk(KERN_ERR "i2o: Insufficient memory to allocate the " + "controller.\n"); + return ERR_PTR(-ENOMEM); + } + memset(c, 0, sizeof(*c)); + + INIT_LIST_HEAD(&c->devices); + c->lock = SPIN_LOCK_UNLOCKED; + init_MUTEX(&c->lct_lock); + c->unit = unit++; + sprintf(c->name, "iop%d", c->unit); + +#if BITS_PER_LONG == 64 + c->context_list_lock = SPIN_LOCK_UNLOCKED; + atomic_set(&c->context_list_counter, 0); + INIT_LIST_HEAD(&c->context_list); +#endif + + return c; +}; + +/** + * i2o_iop_free - Free the i2o_controller struct + * @c: I2O controller to free + */ +void i2o_iop_free(struct i2o_controller *c) +{ + kfree(c); +}; + +/** + * i2o_iop_add - Initialize the I2O controller and add him to the I2O core + * @c: controller + * + * Initialize the I2O controller and if no error occurs add him to the I2O + * core. + * + * Returns 0 on success or negative error code on failure. + */ +int i2o_iop_add(struct i2o_controller *c) +{ + int rc; + + printk(KERN_INFO "%s: Activating I2O controller...\n", c->name); + printk(KERN_INFO "%s: This may take a few minutes if there are many " + "devices\n", c->name); + + if ((rc = i2o_iop_activate(c))) { + printk(KERN_ERR "%s: controller could not activated\n", + c->name); + i2o_iop_reset(c); + return rc; + } + + pr_debug("building sys table %s...\n", c->name); + + if ((rc = i2o_systab_build())) { + i2o_iop_reset(c); + return rc; + } + + pr_debug("online controller %s...\n", c->name); + + if ((rc = i2o_iop_online(c))) { + i2o_iop_reset(c); + return rc; + } + + pr_debug("getting LCT %s...\n", c->name); + + if ((rc = i2o_exec_lct_get(c))) { + i2o_iop_reset(c); + return rc; + } + + list_add(&c->list, &i2o_controllers); + + i2o_driver_notify_controller_add_all(c); + + printk(KERN_INFO "%s: Controller added\n", c->name); + + return 0; +}; + +/** + * i2o_event_register - Turn on/off event notification for a I2O device + * @dev: I2O device which should receive the event registration request + * @drv: driver which want to get notified + * @tcntxt: transaction context to use with this notifier + * @evt_mask: mask of events + * + * Create and posts an event registration message to the task. No reply + * is waited for, or expected. If you do not want further notifications, + * call the i2o_event_register again with a evt_mask of 0. + * + * Returns 0 on success or -ETIMEDOUT if no message could be fetched for + * sending the request. + */ +int i2o_event_register(struct i2o_device *dev, struct i2o_driver *drv, + int tcntxt, u32 evt_mask) +{ + struct i2o_controller *c = dev->iop; + struct i2o_message *msg; + u32 m; + + m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET); + if (m == I2O_QUEUE_EMPTY) + return -ETIMEDOUT; + + writel(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]); + writel(I2O_CMD_UTIL_EVT_REGISTER << 24 | HOST_TID << 12 | dev->lct_data. + tid, &msg->u.head[1]); + writel(drv->context, &msg->u.s.icntxt); + writel(tcntxt, &msg->u.s.tcntxt); + writel(evt_mask, &msg->body[0]); + + i2o_msg_post(c, m); + + return 0; +}; + +/** + * i2o_iop_init - I2O main initialization function + * + * Initialize the I2O drivers (OSM) functions, register the Executive OSM, + * initialize the I2O PCI part and finally initialize I2O device stuff. + * + * Returns 0 on success or negative error code on failure. + */ +static int __init i2o_iop_init(void) +{ + int rc = 0; + + printk(KERN_INFO "I2O Core - (C) Copyright 1999 Red Hat Software\n"); + + rc = i2o_device_init(); + if (rc) + goto exit; + + rc = i2o_driver_init(); + if (rc) + goto device_exit; + + rc = i2o_exec_init(); + if (rc) + goto driver_exit; + + rc = i2o_pci_init(); + if (rc < 0) + goto exec_exit; + + return 0; + + exec_exit: + i2o_exec_exit(); + + driver_exit: + i2o_driver_exit(); + + device_exit: + i2o_device_exit(); + + exit: + return rc; +} + +/** + * i2o_iop_exit - I2O main exit function + * + * Removes I2O controllers from PCI subsystem and shut down OSMs. + */ +static void __exit i2o_iop_exit(void) +{ + i2o_pci_exit(); + i2o_exec_exit(); + i2o_driver_exit(); + i2o_device_exit(); +}; + +module_init(i2o_iop_init); +module_exit(i2o_iop_exit); + +MODULE_AUTHOR("Red Hat Software"); +MODULE_DESCRIPTION("I2O Core"); +MODULE_LICENSE("GPL"); + +#if BITS_PER_LONG == 64 +EXPORT_SYMBOL(i2o_cntxt_list_add); +EXPORT_SYMBOL(i2o_cntxt_list_get); +EXPORT_SYMBOL(i2o_cntxt_list_remove); +EXPORT_SYMBOL(i2o_cntxt_list_get_ptr); +#endif +EXPORT_SYMBOL(i2o_msg_get_wait); +EXPORT_SYMBOL(i2o_msg_nop); +EXPORT_SYMBOL(i2o_find_iop); +EXPORT_SYMBOL(i2o_iop_find_device); +EXPORT_SYMBOL(i2o_event_register); +EXPORT_SYMBOL(i2o_status_get); +EXPORT_SYMBOL(i2o_hrt_get); +EXPORT_SYMBOL(i2o_controllers); --- linux-2.6.9-rc1/drivers/message/i2o/Makefile 2004-08-24 00:03:18.000000000 -0700 +++ 25/drivers/message/i2o/Makefile 2004-09-02 20:51:52.000000000 -0700 @@ -5,6 +5,7 @@ # In the future, some of these should be built conditionally. # +i2o_core-y += iop.o driver.o device.o debug.o pci.o exec-osm.o obj-$(CONFIG_I2O) += i2o_core.o obj-$(CONFIG_I2O_CONFIG)+= i2o_config.o obj-$(CONFIG_I2O_BLOCK) += i2o_block.o --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/message/i2o/pci.c 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,513 @@ +/* + * PCI handling of I2O controller + * + * Copyright (C) 1999-2002 Red Hat Software + * + * Written by Alan Cox, Building Number Three Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * A lot of the I2O message side code from this is taken from the Red + * Creek RCPCI45 adapter driver by Red Creek Communications + * + * Fixes/additions: + * Philipp Rumpf + * Juha Sievänen + * Auvo Häkkinen + * Deepak Saxena + * Boji T Kannanthanam + * Alan Cox : + * Ported to Linux 2.5. + * Markus Lidel : + * Minor fixes for 2.6. + * Markus Lidel : + * Support for sysfs included. + */ + +#include +#include +#include + +#ifdef CONFIG_MTRR +#include +#endif // CONFIG_MTRR + +/* Module internal functions from other sources */ +extern struct i2o_controller *i2o_iop_alloc(void); +extern void i2o_iop_free(struct i2o_controller *); + +extern int i2o_iop_add(struct i2o_controller *); +extern void i2o_iop_remove(struct i2o_controller *); + +extern int i2o_driver_dispatch(struct i2o_controller *, u32, + struct i2o_message *); + +/* PCI device id table for all I2O controllers */ +static struct pci_device_id __devinitdata i2o_pci_ids[] = { + {PCI_DEVICE_CLASS(PCI_CLASS_INTELLIGENT_I2O << 8, 0xffff00)}, + {PCI_DEVICE(PCI_VENDOR_ID_DPT, 0xa511)}, + {0} +}; + +/** + * i2o_dma_realloc - Realloc DMA memory + * @dev: struct device pointer to the PCI device of the I2O controller + * @addr: pointer to a i2o_dma struct DMA buffer + * @len: new length of memory + * @gfp_mask: GFP mask + * + * If there was something allocated in the addr, free it first. If len > 0 + * than try to allocate it and write the addresses back to the addr + * structure. If len == 0 set the virtual address to NULL. + * + * Returns the 0 on success or negative error code on failure. + */ +int i2o_dma_realloc(struct device *dev, struct i2o_dma *addr, size_t len, + unsigned int gfp_mask) +{ + i2o_dma_free(dev, addr); + + if (len) + return i2o_dma_alloc(dev, addr, len, gfp_mask); + + return 0; +}; + +/** + * i2o_pci_free - Frees the DMA memory for the I2O controller + * @c: I2O controller to free + * + * Remove all allocated DMA memory and unmap memory IO regions. If MTRR + * is enabled, also remove it again. + */ +static void __devexit i2o_pci_free(struct i2o_controller *c) +{ + struct device *dev; + + dev = &c->pdev->dev; + + i2o_dma_free(dev, &c->out_queue); + i2o_dma_free(dev, &c->status_block); + if (c->lct) + kfree(c->lct); + i2o_dma_free(dev, &c->dlct); + i2o_dma_free(dev, &c->hrt); + i2o_dma_free(dev, &c->status); + +#ifdef CONFIG_MTRR + if (c->mtrr_reg0 >= 0) + mtrr_del(c->mtrr_reg0, 0, 0); + if (c->mtrr_reg1 >= 0) + mtrr_del(c->mtrr_reg1, 0, 0); +#endif + + if (c->raptor && c->in_queue.virt) + iounmap(c->in_queue.virt); + + if (c->base.virt) + iounmap(c->base.virt); +} + +/** + * i2o_pci_alloc - Allocate DMA memory, map IO memory for I2O controller + * @c: I2O controller + * + * Allocate DMA memory for a PCI (or in theory AGP) I2O controller. All + * IO mappings are also done here. If MTRR is enabled, also do add memory + * regions here. + * + * Returns 0 on success or negative error code on failure. + */ +static int __devinit i2o_pci_alloc(struct i2o_controller *c) +{ + struct pci_dev *pdev = c->pdev; + struct device *dev = &pdev->dev; + int i; + + for (i = 0; i < 6; i++) { + /* Skip I/O spaces */ + if (!(pci_resource_flags(pdev, i) & IORESOURCE_IO)) { + if (!c->base.phys) { + c->base.phys = pci_resource_start(pdev, i); + c->base.len = pci_resource_len(pdev, i); + if (!c->raptor) + break; + } else { + c->in_queue.phys = pci_resource_start(pdev, i); + c->in_queue.len = pci_resource_len(pdev, i); + break; + } + } + } + + if (i == 6) { + printk(KERN_ERR "i2o: I2O controller has no memory regions" + " defined.\n"); + i2o_pci_free(c); + return -EINVAL; + } + + /* Map the I2O controller */ + if (c->raptor) { + printk(KERN_INFO "i2o: PCI I2O controller\n"); + printk(KERN_INFO " BAR0 at 0x%08lX size=%ld\n", + (unsigned long)c->base.phys, (unsigned long)c->base.len); + printk(KERN_INFO " BAR1 at 0x%08lX size=%ld\n", + (unsigned long)c->in_queue.phys, + (unsigned long)c->in_queue.len); + } else + printk(KERN_INFO "i2o: PCI I2O controller at %08lX size=%ld\n", + (unsigned long)c->base.phys, (unsigned long)c->base.len); + + c->base.virt = ioremap(c->base.phys, c->base.len); + if (!c->base.virt) { + printk(KERN_ERR "i2o: Unable to map controller.\n"); + return -ENOMEM; + } + + if (c->raptor) { + c->in_queue.virt = ioremap(c->in_queue.phys, c->in_queue.len); + if (!c->in_queue.virt) { + printk(KERN_ERR "i2o: Unable to map controller.\n"); + i2o_pci_free(c); + return -ENOMEM; + } + } else + c->in_queue = c->base; + + c->irq_mask = c->base.virt + 0x34; + c->post_port = c->base.virt + 0x40; + c->reply_port = c->base.virt + 0x44; + +#ifdef CONFIG_MTRR + /* Enable Write Combining MTRR for IOP's memory region */ + c->mtrr_reg0 = mtrr_add(c->in_queue.phys, c->in_queue.len, + MTRR_TYPE_WRCOMB, 1); + c->mtrr_reg1 = -1; + + if (c->mtrr_reg0 < 0) + printk(KERN_WARNING "i2o: could not enable write combining " + "MTRR\n"); + else + printk(KERN_INFO "i2o: using write combining MTRR\n"); + + /* + * If it is an INTEL i960 I/O processor then set the first 64K to + * Uncacheable since the region contains the messaging unit which + * shouldn't be cached. + */ + if ((pdev->vendor == PCI_VENDOR_ID_INTEL || + pdev->vendor == PCI_VENDOR_ID_DPT) && !c->raptor) { + printk(KERN_INFO "i2o: MTRR workaround for Intel i960 processor" + "\n"); + c->mtrr_reg1 = mtrr_add(c->base.phys, 0x10000, + MTRR_TYPE_UNCACHABLE, 1); + + if (c->mtrr_reg1 < 0) { + printk(KERN_WARNING "i2o_pci: Error in setting " + "MTRR_TYPE_UNCACHABLE\n"); + mtrr_del(c->mtrr_reg0, c->in_queue.phys, + c->in_queue.len); + c->mtrr_reg0 = -1; + } + } +#endif + + if (i2o_dma_alloc(dev, &c->status, 4, GFP_KERNEL)) { + i2o_pci_free(c); + return -ENOMEM; + } + + if (i2o_dma_alloc(dev, &c->hrt, sizeof(i2o_hrt), GFP_KERNEL)) { + i2o_pci_free(c); + return -ENOMEM; + } + + if (i2o_dma_alloc(dev, &c->dlct, 8192, GFP_KERNEL)) { + i2o_pci_free(c); + return -ENOMEM; + } + + if (i2o_dma_alloc(dev, &c->status_block, sizeof(i2o_status_block), + GFP_KERNEL)) { + i2o_pci_free(c); + return -ENOMEM; + } + + if (i2o_dma_alloc(dev, &c->out_queue, MSG_POOL_SIZE, GFP_KERNEL)) { + i2o_pci_free(c); + return -ENOMEM; + } + + pci_set_drvdata(pdev, c); + + return 0; +} + +/** + * i2o_pci_interrupt - Interrupt handler for I2O controller + * @irq: interrupt line + * @dev_id: pointer to the I2O controller + * @r: pointer to registers + * + * Handle an interrupt from a PCI based I2O controller. This turns out + * to be rather simple. We keep the controller pointer in the cookie. + */ +static irqreturn_t i2o_pci_interrupt(int irq, void *dev_id, struct pt_regs *r) +{ + struct i2o_controller *c = dev_id; + struct device *dev = &c->pdev->dev; + struct i2o_message *m; + u32 mv; + u32 *msg; + + /* + * Old 960 steppings had a bug in the I2O unit that caused + * the queue to appear empty when it wasn't. + */ + mv = I2O_REPLY_READ32(c); + if (mv == I2O_QUEUE_EMPTY) { + mv = I2O_REPLY_READ32(c); + if (unlikely(mv == I2O_QUEUE_EMPTY)) { + return IRQ_NONE; + } else + pr_debug("960 bug detected\n"); + } + + while (mv != I2O_QUEUE_EMPTY) { + /* + * Map the message from the page frame map to kernel virtual. + * Because bus_to_virt is deprecated, we have calculate the + * location by ourself! + */ + m = (struct i2o_message *)(mv - + (unsigned long)c->out_queue.phys + + (unsigned long)c->out_queue.virt); + + msg = (u32 *) m; + + /* + * Ensure this message is seen coherently but cachably by + * the processor + */ + dma_sync_single_for_cpu(dev, c->out_queue.phys, MSG_FRAME_SIZE, + PCI_DMA_FROMDEVICE); + + /* dispatch it */ + if (i2o_driver_dispatch(c, mv, m)) + /* flush it if result != 0 */ + i2o_flush_reply(c, mv); + + /* + * That 960 bug again... + */ + mv = I2O_REPLY_READ32(c); + if (mv == I2O_QUEUE_EMPTY) + mv = I2O_REPLY_READ32(c); + } + return IRQ_HANDLED; +} + +/** + * i2o_pci_irq_enable - Allocate interrupt for I2O controller + * + * Allocate an interrupt for the I2O controller, and activate interrupts + * on the I2O controller. + * + * Returns 0 on success or negative error code on failure. + */ +static int i2o_pci_irq_enable(struct i2o_controller *c) +{ + struct pci_dev *pdev = c->pdev; + int rc; + + I2O_IRQ_WRITE32(c, 0xffffffff); + + if (pdev->irq) { + rc = request_irq(pdev->irq, i2o_pci_interrupt, SA_SHIRQ, + c->name, c); + if (rc < 0) { + printk(KERN_ERR "%s: unable to allocate interrupt %d." + "\n", c->name, pdev->irq); + return rc; + } + } + + I2O_IRQ_WRITE32(c, 0x00000000); + + printk(KERN_INFO "%s: Installed at IRQ %d\n", c->name, pdev->irq); + + return 0; +} + +/** + * i2o_pci_irq_disable - Free interrupt for I2O controller + * @c: I2O controller + * + * Disable interrupts in I2O controller and then free interrupt. + */ +static void i2o_pci_irq_disable(struct i2o_controller *c) +{ + I2O_IRQ_WRITE32(c, 0xffffffff); + + if (c->pdev->irq > 0) + free_irq(c->pdev->irq, c); +} + +/** + * i2o_pci_probe - Probe the PCI device for an I2O controller + * @dev: PCI device to test + * @id: id which matched with the PCI device id table + * + * Probe the PCI device for any device which is a memory of the + * Intelligent, I2O class or an Adaptec Zero Channel Controller. We + * attempt to set up each such device and register it with the core. + * + * Returns 0 on success or negative error code on failure. + */ +static int __devinit i2o_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct i2o_controller *c; + int rc; + + printk(KERN_INFO "i2o: Checking for PCI I2O controllers...\n"); + + if ((pdev->class & 0xff) > 1) { + printk(KERN_WARNING "i2o: I2O controller found but does not " + "support I2O 1.5 (skipping).\n"); + return -ENODEV; + } + + if ((rc = pci_enable_device(pdev))) { + printk(KERN_WARNING "i2o: I2O controller found but could not be" + " enabled.\n"); + return rc; + } + + printk(KERN_INFO "i2o: I2O controller found on bus %d at %d.\n", + pdev->bus->number, pdev->devfn); + + if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) { + printk(KERN_WARNING "i2o: I2O controller on bus %d at %d: No " + "suitable DMA available!\n", pdev->bus->number, + pdev->devfn); + rc = -ENODEV; + goto disable; + } + + pci_set_master(pdev); + + c = i2o_iop_alloc(); + if (IS_ERR(c)) { + printk(KERN_ERR "i2o: memory for I2O controller could not be " + "allocated\n"); + rc = PTR_ERR(c); + goto disable; + } + + c->pdev = pdev; + c->device = pdev->dev; + + /* Cards that fall apart if you hit them with large I/O loads... */ + if (pdev->vendor == PCI_VENDOR_ID_NCR && pdev->device == 0x0630) { + c->short_req = 1; + printk(KERN_INFO "i2o: Symbios FC920 workarounds activated.\n"); + } + + if (pdev->subsystem_vendor == PCI_VENDOR_ID_PROMISE) { + c->promise = 1; + printk(KERN_INFO "i2o: Promise workarounds activated.\n"); + } + + /* Cards that go bananas if you quiesce them before you reset them. */ + if (pdev->vendor == PCI_VENDOR_ID_DPT) { + c->no_quiesce = 1; + if (pdev->device == 0xa511) + c->raptor = 1; + } + + if ((rc = i2o_pci_alloc(c))) { + printk(KERN_ERR "i2o: DMA / IO allocation for I2O controller " + " failed\n"); + goto free_controller; + } + + if (i2o_pci_irq_enable(c)) { + printk(KERN_ERR "i2o: unable to enable interrupts for I2O " + "controller\n"); + goto free_pci; + } + + if ((rc = i2o_iop_add(c))) + goto uninstall; + + return 0; + + uninstall: + i2o_pci_irq_disable(c); + + free_pci: + i2o_pci_free(c); + + free_controller: + i2o_iop_free(c); + + disable: + pci_disable_device(pdev); + + return rc; +} + +/** + * i2o_pci_remove - Removes a I2O controller from the system + * pdev: I2O controller which should be removed + * + * Reset the I2O controller, disable interrupts and remove all allocated + * resources. + */ +static void __devexit i2o_pci_remove(struct pci_dev *pdev) +{ + struct i2o_controller *c; + c = pci_get_drvdata(pdev); + + i2o_iop_remove(c); + i2o_pci_irq_disable(c); + i2o_pci_free(c); + + printk(KERN_INFO "%s: Controller removed.\n", c->name); + + i2o_iop_free(c); + pci_disable_device(pdev); +}; + +/* PCI driver for I2O controller */ +static struct pci_driver i2o_pci_driver = { + .name = "I2O controller", + .id_table = i2o_pci_ids, + .probe = i2o_pci_probe, + .remove = __devexit_p(i2o_pci_remove), +}; + +/** + * i2o_pci_init - registers I2O PCI driver in PCI subsystem + * + * Returns > 0 on success or negative error code on failure. + */ +int __init i2o_pci_init(void) +{ + return pci_register_driver(&i2o_pci_driver); +}; + +/** + * i2o_pci_exit - unregisters I2O PCI driver from PCI subsystem + */ +void __exit i2o_pci_exit(void) +{ + pci_unregister_driver(&i2o_pci_driver); +}; + +EXPORT_SYMBOL(i2o_dma_realloc); --- linux-2.6.9-rc1/drivers/misc/ibmasm/module.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/misc/ibmasm/module.c 2004-09-03 00:56:53.961957184 -0700 @@ -59,13 +59,20 @@ static int __init ibmasm_init_one(struct pci_dev *pdev, const struct pci_device_id *id) { - int result = -ENOMEM; + int err, result = -ENOMEM; struct service_processor *sp; + if ((err = pci_enable_device(pdev))) { + printk(KERN_ERR "%s: can't enable PCI device at %s\n", + DRIVER_NAME, pci_name(pdev)); + return err; + } + sp = kmalloc(sizeof(struct service_processor), GFP_KERNEL); if (sp == NULL) { dev_err(&pdev->dev, "Failed to allocate memory\n"); - return result; + result = -ENOMEM; + goto error_kmalloc; } memset(sp, 0, sizeof(struct service_processor)); @@ -148,6 +155,8 @@ error_heartbeat: ibmasm_event_buffer_exit(sp); error_eventbuffer: kfree(sp); +error_kmalloc: + pci_disable_device(pdev); return result; } @@ -166,6 +175,7 @@ static void __exit ibmasm_remove_one(str iounmap(sp->base_address); ibmasm_event_buffer_exit(sp); kfree(sp); + pci_disable_device(pdev); } static struct pci_device_id ibmasm_pci_table[] = --- linux-2.6.9-rc1/drivers/mmc/mmc_block.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/drivers/mmc/mmc_block.c 2004-09-03 00:56:32.166270632 -0700 @@ -43,7 +43,6 @@ #define MMC_SHIFT 3 static int mmc_major; -static int maxsectors = 8; /* * There is one mmc_blk_data per slot. @@ -124,7 +123,7 @@ mmc_blk_ioctl(struct inode *inode, struc geo.sectors = 16; geo.start = get_start_sect(bdev); - return copy_to_user((void *)arg, &geo, sizeof(geo)) + return copy_to_user((void __user *)arg, &geo, sizeof(geo)) ? -EFAULT : 0; } @@ -185,7 +184,7 @@ static int mmc_blk_issue_rq(struct mmc_q brq.data.timeout_ns = card->csd.tacc_ns * 10; brq.data.timeout_clks = card->csd.tacc_clks * 10; brq.data.blksz_bits = md->block_bits; - brq.data.blocks = req->current_nr_sectors >> (md->block_bits - 9); + brq.data.blocks = req->nr_sectors >> (md->block_bits - 9); brq.stop.opcode = MMC_STOP_TRANSMISSION; brq.stop.arg = 0; brq.stop.flags = MMC_RSP_SHORT | MMC_RSP_CRC | MMC_RSP_BUSY; @@ -334,11 +333,10 @@ static struct mmc_blk_data *mmc_blk_allo sprintf(md->disk->disk_name, "mmcblk%d", devidx); sprintf(md->disk->devfs_name, "mmc/blk%d", devidx); - md->block_bits = md->queue.card->csd.read_blkbits; + md->block_bits = card->csd.read_blkbits; - blk_queue_max_sectors(md->queue.queue, maxsectors); blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits); - set_capacity(md->disk, md->queue.card->csd.capacity); + set_capacity(md->disk, card->csd.capacity); } out: return md; @@ -440,7 +438,7 @@ static int mmc_blk_resume(struct mmc_car struct mmc_blk_data *md = mmc_get_drvdata(card); if (md) { - mmc_blk_set_blksize(md, md->queue.card); + mmc_blk_set_blksize(md, card); blk_start_queue(md->queue.queue); } return 0; @@ -489,9 +487,6 @@ static void __exit mmc_blk_exit(void) module_init(mmc_blk_init); module_exit(mmc_blk_exit); -module_param(maxsectors, int, 0444); - -MODULE_PARM_DESC(maxsectors, "Maximum number of sectors for a single request"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Multimedia Card (MMC) block device driver"); --- linux-2.6.9-rc1/drivers/mmc/mmc.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/mmc/mmc.c 2004-09-03 00:56:32.165270784 -0700 @@ -1,7 +1,7 @@ /* * linux/drivers/mmc/mmc.c * - * Copyright (C) 2003 Russell King, All Rights Reserved. + * Copyright (C) 2003-2004 Russell King, All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -715,14 +716,21 @@ struct mmc_host *mmc_alloc_host(int extr if (host) { memset(host, 0, sizeof(struct mmc_host) + extra); - host->priv = host + 1; - spin_lock_init(&host->lock); init_waitqueue_head(&host->wq); INIT_LIST_HEAD(&host->cards); INIT_WORK(&host->detect, mmc_rescan, host); host->dev = dev; + + /* + * By default, hosts do not support SGIO or large requests. + * They have to set these according to their abilities. + */ + host->max_hw_segs = 1; + host->max_phys_segs = 1; + host->max_sectors = 1 << (PAGE_CACHE_SHIFT - 9); + host->max_seg_size = PAGE_CACHE_SIZE; } return host; --- linux-2.6.9-rc1/drivers/mmc/mmci.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/drivers/mmc/mmci.c 2004-09-03 00:56:32.168270328 -0700 @@ -43,10 +43,9 @@ static void mmci_request_end(struct mmci_host *host, struct mmc_request *mrq) { writel(0, host->base + MMCICOMMAND); + host->mrq = NULL; host->cmd = NULL; - host->data = NULL; - host->buffer = NULL; if (mrq->data) mrq->data->bytes_xfered = host->data_xfered; @@ -60,6 +59,13 @@ mmci_request_end(struct mmci_host *host, spin_lock(&host->lock); } +static void mmci_stop_data(struct mmci_host *host) +{ + writel(0, host->base + MMCIDATACTRL); + writel(0, host->base + MMCIMASK1); + host->data = NULL; +} + static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) { unsigned int datactrl, timeout, irqmask; @@ -69,7 +75,7 @@ static void mmci_start_data(struct mmci_ 1 << data->blksz_bits, data->blocks, data->flags); host->data = data; - host->buffer = data->req->buffer; + host->offset = 0; host->size = data->blocks << data->blksz_bits; host->data_xfered = 0; @@ -94,6 +100,7 @@ static void mmci_start_data(struct mmci_ } writel(datactrl, base + MMCIDATACTRL); + writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0); writel(irqmask, base + MMCIMASK1); } @@ -147,7 +154,8 @@ mmci_data_irq(struct mmci_host *host, st status |= MCI_DATAEND; } if (status & MCI_DATAEND) { - host->data = NULL; + mmci_stop_data(host); + if (!data->stop) { mmci_request_end(host, data->mrq); } else { @@ -182,72 +190,171 @@ mmci_cmd_irq(struct mmci_host *host, str } } -/* - * PIO data transfer IRQ handler. - */ -static irqreturn_t mmci_pio_irq(int irq, void *dev_id, struct pt_regs *regs) +static int mmci_pio_read(struct mmci_host *host, struct request *req, u32 status) { - struct mmci_host *host = dev_id; void *base = host->base; - u32 status; int ret = 0; do { - status = readl(base + MMCISTATUS); + unsigned long flags; + unsigned int bio_remain; + char *buffer; - if (!(status & (MCI_RXDATAAVLBL|MCI_RXFIFOHALFFULL| - MCI_TXFIFOHALFEMPTY))) + /* + * Check for data available. + */ + if (!(status & MCI_RXDATAAVLBL)) break; - DBG(host, "irq1 %08x\n", status); + /* + * Map the BIO buffer. + */ + buffer = bio_kmap_irq(req->cbio, &flags); + bio_remain = (req->current_nr_sectors << 9) - host->offset; + + do { + int count = host->size - (readl(base + MMCIFIFOCNT) << 2); + + if (count > bio_remain) + count = bio_remain; - if (status & (MCI_RXDATAAVLBL|MCI_RXFIFOHALFFULL)) { - unsigned int count = host->size - (readl(base + MMCIFIFOCNT) << 2); - if (count < 0) - count = 0; - if (count && host->buffer) { - readsl(base + MMCIFIFO, host->buffer, count >> 2); - host->buffer += count; + if (count > 0) { + ret = 1; + readsl(base + MMCIFIFO, buffer + host->offset, count >> 2); + host->offset += count; host->size -= count; - if (host->size == 0) - host->buffer = NULL; - } else { - static int first = 1; - if (first) { - first = 0; - printk(KERN_ERR "MMCI: sinking excessive data\n"); - } - readl(base + MMCIFIFO); + bio_remain -= count; + if (bio_remain == 0) + goto next_bio; } - } + + status = readl(base + MMCISTATUS); + } while (status & MCI_RXDATAAVLBL); + + bio_kunmap_irq(buffer, &flags); + break; + + next_bio: + bio_kunmap_irq(buffer, &flags); + + /* + * Ok, we've completed that BIO, move on to next + * BIO in the chain. Note: this doesn't actually + * complete the BIO! + */ + if (!process_that_request_first(req, req->current_nr_sectors)) + break; + + host->offset = 0; + status = readl(base + MMCISTATUS); + } while (1); + + /* + * If we're nearing the end of the read, switch to + * "any data available" mode. + */ + if (host->size < MCI_FIFOSIZE) + writel(MCI_RXDATAAVLBLMASK, base + MMCIMASK1); + + return ret; +} + +static int mmci_pio_write(struct mmci_host *host, struct request *req, u32 status) +{ + void *base = host->base; + int ret = 0; + + do { + unsigned long flags; + unsigned int bio_remain; + char *buffer; /* * We only need to test the half-empty flag here - if * the FIFO is completely empty, then by definition * it is more than half empty. */ - if (status & MCI_TXFIFOHALFEMPTY) { - unsigned int maxcnt = status & MCI_TXFIFOEMPTY ? - MCI_FIFOSIZE : MCI_FIFOHALFSIZE; - unsigned int count = min(host->size, maxcnt); + if (!(status & MCI_TXFIFOHALFEMPTY)) + break; - writesl(base + MMCIFIFO, host->buffer, count >> 2); + /* + * Map the BIO buffer. + */ + buffer = bio_kmap_irq(req->cbio, &flags); + bio_remain = (req->current_nr_sectors << 9) - host->offset; - host->buffer += count; + do { + unsigned int count, maxcnt; + + maxcnt = status & MCI_TXFIFOEMPTY ? + MCI_FIFOSIZE : MCI_FIFOHALFSIZE; + count = min(bio_remain, maxcnt); + + writesl(base + MMCIFIFO, buffer + host->offset, count >> 2); + host->offset += count; host->size -= count; + bio_remain -= count; - /* - * If we run out of data, disable the data IRQs; - * this prevents a race where the FIFO becomes - * empty before the chip itself has disabled the - * data path. - */ - if (host->size == 0) - writel(0, base + MMCIMASK1); - } + ret = 1; - ret = 1; - } while (status); + if (bio_remain == 0) + goto next_bio; + + status = readl(base + MMCISTATUS); + } while (status & MCI_TXFIFOHALFEMPTY); + + bio_kunmap_irq(buffer, &flags); + break; + + next_bio: + bio_kunmap_irq(buffer, &flags); + + /* + * Ok, we've completed that BIO, move on to next + * BIO in the chain. Note: this doesn't actually + * complete the BIO! + */ + if (!process_that_request_first(req, req->current_nr_sectors)) + break; + + host->offset = 0; + status = readl(base + MMCISTATUS); + } while (1); + + return ret; +} + +/* + * PIO data transfer IRQ handler. + */ +static irqreturn_t mmci_pio_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct mmci_host *host = dev_id; + struct request *req; + void *base = host->base; + u32 status; + int ret = 0; + + status = readl(base + MMCISTATUS); + + DBG(host, "irq1 %08x\n", status); + + req = host->data->req; + if (status & MCI_RXACTIVE) + ret = mmci_pio_read(host, req, status); + else if (status & MCI_TXACTIVE) + ret = mmci_pio_write(host, req, status); + + /* + * If we run out of data, disable the data IRQs; this + * prevents a race where the FIFO becomes empty before + * the chip itself has disabled the data path, and + * stops us racing with our data end IRQ. + */ + if (host->size == 0) { + writel(0, base + MMCIMASK1); + writel(readl(base + MMCIMASK0) | MCI_DATAENDMASK, base + MMCIMASK0); + } return IRQ_RETVAL(ret); } @@ -268,11 +375,9 @@ static irqreturn_t mmci_irq(int irq, voi struct mmc_data *data; status = readl(host->base + MMCISTATUS); + status &= readl(host->base + MMCIMASK0); writel(status, host->base + MMCICLEAR); - if (!(status & MCI_IRQMASK)) - break; - DBG(host, "irq0 %08x\n", status); data = host->data; @@ -427,6 +532,25 @@ static int mmci_probe(struct amba_device mmc->f_max = min(host->mclk, fmax); mmc->ocr_avail = plat->ocr_mask; + /* + * We can do SGIO + */ + mmc->max_hw_segs = 16; + mmc->max_phys_segs = 16; + + /* + * Since we only have a 16-bit data length register, we must + * ensure that we don't exceed 2^16-1 bytes in a single request. + * Choose 64 (512-byte) sectors as the limit. + */ + mmc->max_sectors = 64; + + /* + * Set the maximum segment size. Since we aren't doing DMA + * (yet) we are only limited by the data length register. + */ + mmc->max_seg_size = mmc->max_sectors << 9; + spin_lock_init(&host->lock); writel(0, host->base + MMCIMASK0); --- linux-2.6.9-rc1/drivers/mmc/mmci.h 2004-08-24 00:03:50.000000000 -0700 +++ 25/drivers/mmc/mmci.h 2004-09-03 00:56:32.168270328 -0700 @@ -103,18 +103,15 @@ #define MMCIFIFOCNT 0x048 #define MMCIFIFO 0x080 /* to 0x0bc */ -#define MCI_IRQMASK \ - (MCI_CMDCRCFAIL|MCI_DATACRCFAIL|MCI_CMDTIMEOUT|MCI_DATATIMEOUT| \ - MCI_TXUNDERRUN|MCI_RXOVERRUN|MCI_CMDRESPEND|MCI_CMDSENT| \ - MCI_DATAEND|MCI_DATABLOCKEND) - #define MCI_IRQENABLE \ (MCI_CMDCRCFAILMASK|MCI_DATACRCFAILMASK|MCI_CMDTIMEOUTMASK| \ MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK| \ - MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATAENDMASK| \ - MCI_DATABLOCKENDMASK) + MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATABLOCKENDMASK) -#define MCI_FIFOSIZE 16 +/* + * The size of the FIFO in bytes. + */ +#define MCI_FIFOSIZE (16*4) #define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2) @@ -141,8 +138,6 @@ struct mmci_host { unsigned int oldstat; /* pio stuff */ - void *buffer; + unsigned int offset; unsigned int size; }; - -#define to_mmci_host(mmc) container_of(mmc, struct mmci_host, mmc) --- linux-2.6.9-rc1/drivers/mmc/mmc_queue.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/mmc/mmc_queue.c 2004-09-03 00:56:32.166270632 -0700 @@ -124,11 +124,12 @@ static void mmc_request(request_queue_t */ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock) { + struct mmc_host *host = card->host; u64 limit = BLK_BOUNCE_HIGH; int ret; - if (card->host->dev->dma_mask && *card->host->dev->dma_mask) - limit = *card->host->dev->dma_mask; + if (host->dev->dma_mask && *host->dev->dma_mask) + limit = *host->dev->dma_mask; mq->card = card; mq->queue = blk_init_queue(mmc_request, lock); @@ -137,6 +138,10 @@ int mmc_init_queue(struct mmc_queue *mq, blk_queue_prep_rq(mq->queue, mmc_prep_request); blk_queue_bounce_limit(mq->queue, limit); + blk_queue_max_sectors(mq->queue, host->max_sectors); + blk_queue_max_phys_segments(mq->queue, host->max_phys_segs); + blk_queue_max_hw_segments(mq->queue, host->max_hw_segs); + blk_queue_max_segment_size(mq->queue, host->max_seg_size); mq->queue->queuedata = mq; mq->req = NULL; --- linux-2.6.9-rc1/drivers/mmc/pxamci.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/drivers/mmc/pxamci.c 2004-09-03 00:56:32.169270176 -0700 @@ -530,6 +530,7 @@ static int pxamci_remove(struct device * return 0; } +#ifdef CONFIG_PM static int pxamci_suspend(struct device *dev, u32 state, u32 level) { struct mmc_host *mmc = dev_get_drvdata(dev); @@ -551,6 +552,10 @@ static int pxamci_resume(struct device * return ret; } +#else +#define pxamci_suspend NULL +#define pxamci_resume NULL +#endif static struct device_driver pxamci_driver = { .name = "pxa2xx-mci", --- linux-2.6.9-rc1/drivers/mtd/chips/cfi_cmdset_0001.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/drivers/mtd/chips/cfi_cmdset_0001.c 2004-09-03 00:57:24.891255216 -0700 @@ -1437,8 +1437,7 @@ static int do_erase_oneblock(struct map_ spin_unlock(chip->mutex); INVALIDATE_CACHED_RANGE(map, adr, len); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((chip->erase_time*HZ)/(2*1000)); + msleep(chip->erase_time / 2); spin_lock(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ --- linux-2.6.9-rc1/drivers/mtd/chips/jedec_probe.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/mtd/chips/jedec_probe.c 2004-09-02 20:51:52.000000000 -0700 @@ -29,6 +29,7 @@ #define MANUFACTURER_AMD 0x0001 #define MANUFACTURER_ATMEL 0x001f #define MANUFACTURER_FUJITSU 0x0004 +#define MANUFACTURER_HYUNDAI 0x00AD #define MANUFACTURER_INTEL 0x0089 #define MANUFACTURER_MACRONIX 0x00C2 #define MANUFACTURER_PMC 0x009D @@ -56,6 +57,7 @@ #define AM29F040 0x00A4 #define AM29LV040B 0x004F #define AM29F032B 0x0041 +#define AM29F002T 0x00B0 /* Atmel */ #define AT49BV512 0x0003 @@ -77,6 +79,8 @@ #define MBM29LV400TC 0x22B9 #define MBM29LV400BC 0x22BA +/* Hyundai */ +#define HY29F002T 0x00B0 /* Intel */ #define I28F004B3T 0x00d4 @@ -106,6 +110,7 @@ #define MX29LV160T 0x22C4 #define MX29LV160B 0x2249 #define MX29F016 0x00AD +#define MX29F002T 0x00B0 #define MX29F004T 0x0045 #define MX29F004B 0x0046 @@ -507,6 +512,17 @@ static const struct amd_flash_info jedec ERASEINFO(0x10000,8), } }, { + mfr_id: MANUFACTURER_AMD, + dev_id: AM29F002T, + name: "AMD AM29F002T", + DevSize: SIZE_256KiB, + NumEraseRegions: 4, + regions: {ERASEINFO(0x10000,3), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { .mfr_id = MANUFACTURER_ATMEL, .dev_id = AT49BV512, .name = "Atmel AT49BV512", @@ -752,6 +768,17 @@ static const struct amd_flash_info jedec ERASEINFO(0x04000,1) } }, { + mfr_id: MANUFACTURER_HYUNDAI, + dev_id: HY29F002T, + name: "Hyundai HY29F002T", + DevSize: SIZE_256KiB, + NumEraseRegions: 4, + regions: {ERASEINFO(0x10000,3), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { .mfr_id = MANUFACTURER_INTEL, .dev_id = I28F004B3B, .name = "Intel 28F004B3B", @@ -1135,6 +1162,17 @@ static const struct amd_flash_info jedec ERASEINFO(0x10000,7), } }, { + mfr_id: MANUFACTURER_MACRONIX, + dev_id: MX29F002T, + name: "Macronix MX29F002T", + DevSize: SIZE_256KiB, + NumEraseRegions: 4, + regions: {ERASEINFO(0x10000,3), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { .mfr_id = MANUFACTURER_PMC, .dev_id = PM49FL002, .name = "PMC Pm49FL002", @@ -1570,7 +1608,7 @@ static const struct amd_flash_info jedec ERASEINFO(0x02000, 2), ERASEINFO(0x04000, 1), } - } + } }; --- linux-2.6.9-rc1/drivers/mtd/maps/edb7312.c 2004-08-24 00:03:49.000000000 -0700 +++ 25/drivers/mtd/maps/edb7312.c 2004-09-02 20:51:52.000000000 -0700 @@ -71,14 +71,14 @@ static const char *probes[] = { "RedBoot #endif -static int mtd_parts_nb = 0; -static struct mtd_partition *mtd_parts = 0; +static int mtd_parts_nb; +static struct mtd_partition *mtd_parts; int __init init_edb7312nor(void) { static const char *rom_probe_types[] = PROBETYPES; const char **type; - const char *part_type = 0; + const char *part_type = NULL; printk(KERN_NOTICE MSG_PREFIX "0x%08x at 0x%08x\n", WINDOW_SIZE, WINDOW_ADDR); @@ -92,7 +92,7 @@ int __init init_edb7312nor(void) simple_map_init(&edb7312nor_map); - mymtd = 0; + mymtd = NULL; type = rom_probe_types; for(; !mymtd && *type; type++) { mymtd = do_map_probe(*type, &edb7312nor_map); --- linux-2.6.9-rc1/drivers/mtd/maps/impa7.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/mtd/maps/impa7.c 2004-09-02 20:51:52.000000000 -0700 @@ -77,7 +77,7 @@ int __init init_impa7(void) { static const char *rom_probe_types[] = PROBETYPES; const char **type; - const char *part_type = 0; + const char *part_type = NULL; int i; static struct { u_long addr; u_long size; } pt[NUM_FLASHBANKS] = { { WINDOW_ADDR0, WINDOW_SIZE0 }, @@ -99,7 +99,7 @@ int __init init_impa7(void) } simple_map_init(&impa7_map[i]); - impa7_mtd[i] = 0; + impa7_mtd[i] = NULL; type = rom_probe_types; for(; !impa7_mtd[i] && *type; type++) { impa7_mtd[i] = do_map_probe(*type, &impa7_map[i]); --- linux-2.6.9-rc1/drivers/mtd/maps/nettel.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/drivers/mtd/maps/nettel.c 2004-09-02 20:51:52.000000000 -0700 @@ -180,7 +180,7 @@ int nettel_eraseconfig(void) if (mtd) { nettel_erase.mtd = mtd; nettel_erase.callback = nettel_erasecallback; - nettel_erase.callback = 0; + nettel_erase.callback = NULL; nettel_erase.addr = 0; nettel_erase.len = mtd->size; nettel_erase.priv = (u_long) &wait_q; --- linux-2.6.9-rc1/drivers/mtd/nftlmount.c 2004-08-24 00:03:13.000000000 -0700 +++ 25/drivers/mtd/nftlmount.c 2004-09-02 20:51:52.000000000 -0700 @@ -269,7 +269,8 @@ static int memcmpb(void *a, int c, int n static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int len, int check_oob) { - int i, retlen; + int i; + size_t retlen; u8 buf[SECTORSIZE + nftl->mbd.mtd->oobsize]; for (i = 0; i < len; i += SECTORSIZE) { @@ -366,7 +367,8 @@ static void check_sectors_in_chain(struc { unsigned int block, i, status; struct nftl_bci bci; - int sectors_per_block, retlen; + int sectors_per_block; + size_t retlen; sectors_per_block = nftl->EraseSize / SECTORSIZE; block = first_block; --- linux-2.6.9-rc1/drivers/net/3c509.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/drivers/net/3c509.c 2004-09-03 00:56:32.524216216 -0700 @@ -197,9 +197,9 @@ static int el3_rx(struct net_device *dev static int el3_close(struct net_device *dev); static void set_multicast_list(struct net_device *dev); static void el3_tx_timeout (struct net_device *dev); -static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); static void el3_down(struct net_device *dev); static void el3_up(struct net_device *dev); +static struct ethtool_ops ethtool_ops; #ifdef CONFIG_PM static int el3_suspend(struct pm_dev *pdev); static int el3_resume(struct pm_dev *pdev); @@ -321,7 +321,7 @@ static int __init el3_common_init(struct dev->set_multicast_list = &set_multicast_list; dev->tx_timeout = el3_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; - dev->do_ioctl = netdev_ioctl; + SET_ETHTOOL_OPS(dev, ðtool_ops); err = register_netdev(dev); if (err) { @@ -1285,123 +1285,64 @@ el3_netdev_set_ecmd(struct net_device *d return 0; } -/** - * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls - * @dev: network interface on which out-of-band action is to be performed - * @useraddr: userspace address to which data is to be read and returned - * - * Process the various commands of the SIOCETHTOOL interface. - */ +static void el3_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); +} -static int -netdev_ethtool_ioctl (struct net_device *dev, void __user *useraddr) +static int el3_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) { - u32 ethcmd; struct el3_private *lp = netdev_priv(dev); + int ret; - /* dev_ioctl() in ../../net/core/dev.c has already checked - capable(CAP_NET_ADMIN), so don't bother with that here. */ - - if (get_user(ethcmd, (u32 __user *)useraddr)) - return -EFAULT; - - switch (ethcmd) { - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, DRV_NAME); - strcpy (info.version, DRV_VERSION); - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } - - /* get settings */ - case ETHTOOL_GSET: { - int ret; - struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - spin_lock_irq(&lp->lock); - ret = el3_netdev_get_ecmd(dev, &ecmd); - spin_unlock_irq(&lp->lock); - if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return ret; - } - - /* set settings */ - case ETHTOOL_SSET: { - int ret; - struct ethtool_cmd ecmd; - if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) - return -EFAULT; - spin_lock_irq(&lp->lock); - ret = el3_netdev_set_ecmd(dev, &ecmd); - spin_unlock_irq(&lp->lock); - return ret; - } - - /* get link status */ - case ETHTOOL_GLINK: { - struct ethtool_value edata = { ETHTOOL_GLINK }; - spin_lock_irq(&lp->lock); - edata.data = el3_link_ok(dev); - spin_unlock_irq(&lp->lock); - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = el3_debug; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - el3_debug = edata.data; - return 0; - } + spin_lock_irq(&lp->lock); + ret = el3_netdev_get_ecmd(dev, ecmd); + spin_unlock_irq(&lp->lock); + return ret; +} - default: - break; - } +static int el3_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct el3_private *lp = netdev_priv(dev); + int ret; - return -EOPNOTSUPP; + spin_lock_irq(&lp->lock); + ret = el3_netdev_set_ecmd(dev, ecmd); + spin_unlock_irq(&lp->lock); + return ret; } -/** - * netdev_ioctl: Handle network interface ioctls - * @dev: network interface on which out-of-band action is to be performed - * @rq: user request data - * @cmd: command issued by user - * - * Process the various out-of-band ioctls passed to this driver. - */ - -static int -netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) +static u32 el3_get_link(struct net_device *dev) { - int rc = 0; + struct el3_private *lp = netdev_priv(dev); + u32 ret; - switch (cmd) { - case SIOCETHTOOL: - rc = netdev_ethtool_ioctl(dev, rq->ifr_data); - break; + spin_lock_irq(&lp->lock); + ret = el3_link_ok(dev); + spin_unlock_irq(&lp->lock); + return ret; +} - default: - rc = -EOPNOTSUPP; - break; - } +static u32 el3_get_msglevel(struct net_device *dev) +{ + return el3_debug; +} - return rc; +static void el3_set_msglevel(struct net_device *dev, u32 v) +{ + el3_debug = v; } +static struct ethtool_ops ethtool_ops = { + .get_drvinfo = el3_get_drvinfo, + .get_settings = el3_get_settings, + .set_settings = el3_set_settings, + .get_link = el3_get_link, + .get_msglevel = el3_get_msglevel, + .set_msglevel = el3_set_msglevel, +}; + static void el3_down(struct net_device *dev) { --- linux-2.6.9-rc1/drivers/net/3c59x.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/drivers/net/3c59x.c 2004-09-03 00:57:11.013364976 -0700 @@ -695,7 +695,7 @@ enum Window2 { /* Window 2. */ Wn2_ResetOptions=12, }; enum Window3 { /* Window 3: MAC/config bits. */ - Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8, + Wn3_Config=0, Wn3_MaxPktSize=4, Wn3_MAC_Ctrl=6, Wn3_Options=8, }; #define BFEXT(value, offset, bitcount) \ @@ -723,7 +723,8 @@ enum Win4_Media_bits { Media_LnkBeat = 0x0800, }; enum Window7 { /* Window 7: Bus Master control. */ - Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12, + Wn7_MasterAddr = 0, Wn7_VlanEtherType=4, Wn7_MasterLen = 6, + Wn7_MasterStatus = 12, }; /* Boomerang bus master control registers. */ enum MasterCtrl { @@ -819,7 +820,8 @@ struct vortex_private { pm_state_valid:1, /* power_state[] has sane contents */ open:1, medialock:1, - must_free_region:1; /* Flag: if zero, Cardbus owns the I/O region */ + must_free_region:1, /* Flag: if zero, Cardbus owns the I/O region */ + large_frames:1; /* accept large frames */ int drv_flags; u16 status_enable; u16 intr_enable; @@ -904,6 +906,8 @@ static int vortex_ioctl(struct net_devic static void vortex_tx_timeout(struct net_device *dev); static void acpi_set_WOL(struct net_device *dev); static struct ethtool_ops vortex_ethtool_ops; +static void set_8021q_mode(struct net_device *dev, int enable); + /* This driver uses 'options' to pass the media type, full-duplex flag, etc. */ /* Option count limit only -- unlimited interfaces are supported. */ @@ -1164,6 +1168,7 @@ static int __devinit vortex_probe1(struc dev->base_addr = ioaddr; dev->irq = irq; dev->mtu = mtu; + vp->large_frames = mtu > 1500; vp->drv_flags = vci->drv_flags; vp->has_nway = (vci->drv_flags & HAS_NWAY) ? 1 : 0; vp->io_size = vci->io_size; @@ -1476,7 +1481,7 @@ static int __devinit vortex_probe1(struc #ifdef CONFIG_NET_POLL_CONTROLLER dev->poll_controller = poll_vortex; #endif - if (pdev && vp->enable_wol) { + if (pdev) { vp->pm_state_valid = 1; pci_save_state(VORTEX_PCI(vp), vp->power_state); acpi_set_WOL(dev); @@ -1533,9 +1538,10 @@ vortex_up(struct net_device *dev) unsigned int config; int i; - if (VORTEX_PCI(vp) && vp->enable_wol) { + if (VORTEX_PCI(vp)) { pci_set_power_state(VORTEX_PCI(vp), 0); /* Go active */ pci_restore_state(VORTEX_PCI(vp), vp->power_state); + pci_enable_device(VORTEX_PCI(vp)); } /* Before initializing select the active media port. */ @@ -1616,7 +1622,7 @@ vortex_up(struct net_device *dev) /* Set the full-duplex bit. */ outw( ((vp->info1 & 0x8000) || vp->full_duplex ? 0x20 : 0) | - (dev->mtu > 1500 ? 0x40 : 0) | + (vp->large_frames ? 0x40 : 0) | ((vp->full_duplex && vp->flow_ctrl && vp->partner_flow_ctrl) ? 0x100 : 0), ioaddr + Wn3_MAC_Ctrl); @@ -1700,6 +1706,8 @@ vortex_up(struct net_device *dev) } /* Set receiver mode: presumably accept b-case and phys addr only. */ set_rx_mode(dev); + /* enable 802.1q tagged frames */ + set_8021q_mode(dev, 1); outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ // issue_and_wait(dev, SetTxStart|0x07ff); @@ -1842,7 +1850,7 @@ vortex_timer(unsigned long data) /* Set the full-duplex bit. */ EL3WINDOW(3); outw( (vp->full_duplex ? 0x20 : 0) | - (dev->mtu > 1500 ? 0x40 : 0) | + (vp->large_frames ? 0x40 : 0) | ((vp->full_duplex && vp->flow_ctrl && vp->partner_flow_ctrl) ? 0x100 : 0), ioaddr + Wn3_MAC_Ctrl); if (vortex_debug > 1) @@ -2068,6 +2076,8 @@ vortex_error(struct net_device *dev, int issue_and_wait(dev, RxReset|0x07); /* Set the Rx filter to the current state. */ set_rx_mode(dev); + /* enable 802.1q VLAN tagged frames */ + set_8021q_mode(dev, 1); outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */ outw(AckIntr | HostError, ioaddr + EL3_CMD); } @@ -2672,6 +2682,9 @@ vortex_down(struct net_device *dev, int outw(RxDisable, ioaddr + EL3_CMD); outw(TxDisable, ioaddr + EL3_CMD); + /* Disable receiving 802.1q tagged frames */ + set_8021q_mode(dev, 0); + if (dev->if_port == XCVR_10base2) /* Turn off thinnet power. Green! */ outw(StopCoax, ioaddr + EL3_CMD); @@ -2684,7 +2697,7 @@ vortex_down(struct net_device *dev, int if (vp->full_bus_master_tx) outl(0, ioaddr + DownListPtr); - if (final_down && VORTEX_PCI(vp) && vp->enable_wol) { + if (final_down && VORTEX_PCI(vp)) { pci_save_state(VORTEX_PCI(vp), vp->power_state); acpi_set_WOL(dev); } @@ -2947,6 +2960,61 @@ static void set_rx_mode(struct net_devic outw(new_mode, ioaddr + EL3_CMD); } +#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) +/* Setup the card so that it can receive frames with an 802.1q VLAN tag. + Note that this must be done after each RxReset due to some backwards + compatibility logic in the Cyclone and Tornado ASICs */ + +/* The Ethernet Type used for 802.1q tagged frames */ +#define VLAN_ETHER_TYPE 0x8100 + +static void set_8021q_mode(struct net_device *dev, int enable) +{ + struct vortex_private *vp = (struct vortex_private *)dev->priv; + long ioaddr = dev->base_addr; + int old_window = inw(ioaddr + EL3_CMD); + int mac_ctrl; + + if ((vp->drv_flags&IS_CYCLONE) || (vp->drv_flags&IS_TORNADO)) { + /* cyclone and tornado chipsets can recognize 802.1q + * tagged frames and treat them correctly */ + + int max_pkt_size = dev->mtu+14; /* MTU+Ethernet header */ + if (enable) + max_pkt_size += 4; /* 802.1Q VLAN tag */ + + EL3WINDOW(3); + outw(max_pkt_size, ioaddr+Wn3_MaxPktSize); + + /* set VlanEtherType to let the hardware checksumming + treat tagged frames correctly */ + EL3WINDOW(7); + outw(VLAN_ETHER_TYPE, ioaddr+Wn7_VlanEtherType); + } else { + /* on older cards we have to enable large frames */ + + vp->large_frames = dev->mtu > 1500 || enable; + + EL3WINDOW(3); + mac_ctrl = inw(ioaddr+Wn3_MAC_Ctrl); + if (vp->large_frames) + mac_ctrl |= 0x40; + else + mac_ctrl &= ~0x40; + outw(mac_ctrl, ioaddr+Wn3_MAC_Ctrl); + } + + EL3WINDOW(old_window); +} +#else + +static void set_8021q_mode(struct net_device *dev, int enable) +{ +} + + +#endif + /* MII transceiver control section. Read and write the MII registers using software-generated serial MDIO protocol. See the MII specifications or DP83840A data sheet @@ -3052,15 +3120,17 @@ static void acpi_set_WOL(struct net_devi struct vortex_private *vp = netdev_priv(dev); long ioaddr = dev->base_addr; - /* Power up on: 1==Downloaded Filter, 2==Magic Packets, 4==Link Status. */ - EL3WINDOW(7); - outw(2, ioaddr + 0x0c); - /* The RxFilter must accept the WOL frames. */ - outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD); - outw(RxEnable, ioaddr + EL3_CMD); + if (vp->enable_wol) { + /* Power up on: 1==Downloaded Filter, 2==Magic Packets, 4==Link Status. */ + EL3WINDOW(7); + outw(2, ioaddr + 0x0c); + /* The RxFilter must accept the WOL frames. */ + outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD); + outw(RxEnable, ioaddr + EL3_CMD); + pci_enable_wake(VORTEX_PCI(vp), 0, 1); + } /* Change the power state to D3; RxEnable doesn't take effect. */ - pci_enable_wake(VORTEX_PCI(vp), 0, 1); pci_set_power_state(VORTEX_PCI(vp), 3); } @@ -3083,7 +3153,7 @@ static void __devexit vortex_remove_one */ unregister_netdev(dev); - if (VORTEX_PCI(vp) && vp->enable_wol) { + if (VORTEX_PCI(vp)) { pci_set_power_state(VORTEX_PCI(vp), 0); /* Go active */ if (vp->pm_state_valid) pci_restore_state(VORTEX_PCI(vp), vp->power_state); --- linux-2.6.9-rc1/drivers/net/8139cp.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/drivers/net/8139cp.c 2004-09-03 00:56:32.526215912 -0700 @@ -185,6 +185,9 @@ enum { RingEnd = (1 << 30), /* End of descriptor ring */ FirstFrag = (1 << 29), /* First segment of a packet */ LastFrag = (1 << 28), /* Final segment of a packet */ + LargeSend = (1 << 27), /* TCP Large Send Offload (TSO) */ + MSSShift = 16, /* MSS value position */ + MSSMask = 0xfff, /* MSS value: 11 bits */ TxError = (1 << 23), /* Tx error summary */ RxError = (1 << 20), /* Rx error summary */ IPCS = (1 << 18), /* Calculate IP checksum */ @@ -747,10 +750,11 @@ static int cp_start_xmit (struct sk_buff { struct cp_private *cp = netdev_priv(dev); unsigned entry; - u32 eor; + u32 eor, flags; #if CP_VLAN_TAG_USED u32 vlan_tag = 0; #endif + int mss = 0; spin_lock_irq(&cp->lock); @@ -770,6 +774,9 @@ static int cp_start_xmit (struct sk_buff entry = cp->tx_head; eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0; + if (dev->features & NETIF_F_TSO) + mss = skb_shinfo(skb)->tso_size; + if (skb_shinfo(skb)->nr_frags == 0) { struct cp_desc *txd = &cp->tx_ring[entry]; u32 len; @@ -781,21 +788,21 @@ static int cp_start_xmit (struct sk_buff txd->addr = cpu_to_le64(mapping); wmb(); - if (skb->ip_summed == CHECKSUM_HW) { + flags = eor | len | DescOwn | FirstFrag | LastFrag; + + if (mss) + flags |= LargeSend | ((mss & MSSMask) << MSSShift); + else if (skb->ip_summed == CHECKSUM_HW) { const struct iphdr *ip = skb->nh.iph; if (ip->protocol == IPPROTO_TCP) - txd->opts1 = cpu_to_le32(eor | len | DescOwn | - FirstFrag | LastFrag | - IPCS | TCPCS); + flags |= IPCS | TCPCS; else if (ip->protocol == IPPROTO_UDP) - txd->opts1 = cpu_to_le32(eor | len | DescOwn | - FirstFrag | LastFrag | - IPCS | UDPCS); + flags |= IPCS | UDPCS; else BUG(); - } else - txd->opts1 = cpu_to_le32(eor | len | DescOwn | - FirstFrag | LastFrag); + } + + txd->opts1 = cpu_to_le32(flags); wmb(); cp->tx_skb[entry].skb = skb; @@ -834,16 +841,19 @@ static int cp_start_xmit (struct sk_buff len, PCI_DMA_TODEVICE); eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0; - if (skb->ip_summed == CHECKSUM_HW) { - ctrl = eor | len | DescOwn | IPCS; + ctrl = eor | len | DescOwn; + + if (mss) + ctrl |= LargeSend | + ((mss & MSSMask) << MSSShift); + else if (skb->ip_summed == CHECKSUM_HW) { if (ip->protocol == IPPROTO_TCP) - ctrl |= TCPCS; + ctrl |= IPCS | TCPCS; else if (ip->protocol == IPPROTO_UDP) - ctrl |= UDPCS; + ctrl |= IPCS | UDPCS; else BUG(); - } else - ctrl = eor | len | DescOwn; + } if (frag == skb_shinfo(skb)->nr_frags - 1) ctrl |= LastFrag; @@ -1536,6 +1546,8 @@ static struct ethtool_ops cp_ethtool_ops .set_tx_csum = ethtool_op_set_tx_csum, /* local! */ .get_sg = ethtool_op_get_sg, .set_sg = ethtool_op_set_sg, + .get_tso = ethtool_op_get_tso, + .set_tso = ethtool_op_set_tso, .get_regs = cp_get_regs, .get_wol = cp_get_wol, .set_wol = cp_set_wol, @@ -1766,6 +1778,10 @@ static int cp_init_one (struct pci_dev * if (pci_using_dac) dev->features |= NETIF_F_HIGHDMA; +#if 0 /* disabled by default until verified */ + dev->features |= NETIF_F_TSO; +#endif + dev->irq = pdev->irq; rc = register_netdev(dev); --- linux-2.6.9-rc1/drivers/net/8139too.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/drivers/net/8139too.c 2004-09-03 00:56:32.528215608 -0700 @@ -390,8 +390,14 @@ enum rx_mode_bits { /* Bits in TxConfig. */ enum tx_config_bits { - TxIFG1 = (1 << 25), /* Interframe Gap Time */ - TxIFG0 = (1 << 24), /* Enabling these bits violates IEEE 802.3 */ + + /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */ + TxIFGShift = 24, + TxIFG84 = (0 << TxIFGShift), /* 8.4us / 840ns (10 / 100Mbps) */ + TxIFG88 = (1 << TxIFGShift), /* 8.8us / 880ns (10 / 100Mbps) */ + TxIFG92 = (2 << TxIFGShift), /* 9.2us / 920ns (10 / 100Mbps) */ + TxIFG96 = (3 << TxIFGShift), /* 9.6us / 960ns (10 / 100Mbps) */ + TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */ TxCRC = (1 << 16), /* DISABLE appending CRC to end of Tx packets */ TxClearAbt = (1 << 0), /* Clear abort (WO) */ @@ -593,6 +599,7 @@ struct rtl8139_private { int time_to_die; struct mii_if_info mii; unsigned int regs_len; + unsigned long fifo_copy_timeout; }; MODULE_AUTHOR ("Jeff Garzik "); @@ -723,7 +730,7 @@ static const unsigned int rtl8139_rx_con #endif static const unsigned int rtl8139_tx_config = - (TX_DMA_BURST << TxDMAShift) | (TX_RETRY << TxRetryShift); + TxIFG96 | (TX_DMA_BURST << TxDMAShift) | (TX_RETRY << TxRetryShift); static void __rtl8139_cleanup_dev (struct net_device *dev) { @@ -1399,8 +1406,6 @@ static void rtl8139_hw_start (struct net tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys; RTL_W32 (RxConfig, tp->rx_config); - - /* Check this value: the documentation for IFG contradicts ifself. */ RTL_W32 (TxConfig, rtl8139_tx_config); tp->cur_rx = 0; @@ -1927,6 +1932,24 @@ static __inline__ void wrap_copy(struct } #endif +static void rtl8139_isr_ack(struct rtl8139_private *tp) +{ + void *ioaddr = tp->mmio_addr; + u16 status; + + status = RTL_R16 (IntrStatus) & RxAckBits; + + /* Clear out errors and receive interrupts */ + if (likely(status != 0)) { + if (unlikely(status & (RxFIFOOver | RxOverflow))) { + tp->stats.rx_errors++; + if (status & RxFIFOOver) + tp->stats.rx_fifo_errors++; + } + RTL_W16_F (IntrStatus, RxAckBits); + } +} + static int rtl8139_rx(struct net_device *dev, struct rtl8139_private *tp, int budget) { @@ -1934,9 +1957,10 @@ static int rtl8139_rx(struct net_device int received = 0; unsigned char *rx_ring = tp->rx_ring; unsigned int cur_rx = tp->cur_rx; + unsigned int rx_size = 0; DPRINTK ("%s: In rtl8139_rx(), current %4.4x BufAddr %4.4x," - " free to %4.4x, Cmd %2.2x.\n", dev->name, cur_rx, + " free to %4.4x, Cmd %2.2x.\n", dev->name, (u16)cur_rx, RTL_R16 (RxBufAddr), RTL_R16 (RxBufPtr), RTL_R8 (ChipCmd)); @@ -1944,10 +1968,8 @@ static int rtl8139_rx(struct net_device && (RTL_R8 (ChipCmd) & RxBufEmpty) == 0) { u32 ring_offset = cur_rx % RX_BUF_LEN; u32 rx_status; - unsigned int rx_size; unsigned int pkt_size; struct sk_buff *skb; - u16 status; rmb(); @@ -1976,10 +1998,24 @@ static int rtl8139_rx(struct net_device * since EarlyRx is disabled. */ if (unlikely(rx_size == 0xfff0)) { + if (!tp->fifo_copy_timeout) + tp->fifo_copy_timeout = jiffies + 2; + else if (time_after(jiffies, tp->fifo_copy_timeout)) { + DPRINTK ("%s: hung FIFO. Reset.", dev->name); + rx_size = 0; + goto no_early_rx; + } + if (netif_msg_intr(tp)) { + printk(KERN_DEBUG "%s: fifo copy in progress.", + dev->name); + } tp->xstats.early_rx++; - goto done; + break; } +no_early_rx: + tp->fifo_copy_timeout = 0; + /* If Rx err or invalid rx_size/rx_status received * (which happens if we get lost in the ring), * Rx process gets reset, so we abort any further @@ -1989,7 +2025,8 @@ static int rtl8139_rx(struct net_device (rx_size < 8) || (!(rx_status & RxStatusOK)))) { rtl8139_rx_err (rx_status, dev, tp, ioaddr); - return -1; + received = -1; + goto out; } /* Malloc up new buffer, compatible with net-2e. */ @@ -2025,19 +2062,11 @@ static int rtl8139_rx(struct net_device cur_rx = (cur_rx + rx_size + 4 + 3) & ~3; RTL_W16 (RxBufPtr, (u16) (cur_rx - 16)); - /* Clear out errors and receive interrupts */ - status = RTL_R16 (IntrStatus) & RxAckBits; - if (likely(status != 0)) { - if (unlikely(status & (RxFIFOOver | RxOverflow))) { - tp->stats.rx_errors++; - if (status & RxFIFOOver) - tp->stats.rx_fifo_errors++; - } - RTL_W16_F (IntrStatus, RxAckBits); - } + rtl8139_isr_ack(tp); } - done: + if (unlikely(!received || rx_size == 0xfff0)) + rtl8139_isr_ack(tp); #if RTL8139_DEBUG > 1 DPRINTK ("%s: Done rtl8139_rx(), current %4.4x BufAddr %4.4x," @@ -2047,6 +2076,15 @@ static int rtl8139_rx(struct net_device #endif tp->cur_rx = cur_rx; + + /* + * The receive buffer should be mostly empty. + * Tell NAPI to reenable the Rx irq. + */ + if (tp->fifo_copy_timeout) + received = budget; + +out: return received; } --- linux-2.6.9-rc1/drivers/net/amd8111e.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/net/amd8111e.c 2004-09-03 00:56:32.531215152 -0700 @@ -719,7 +719,7 @@ static int amd8111e_tx(struct net_device return 0; } -#if CONFIG_AMD8111E_NAPI +#ifdef CONFIG_AMD8111E_NAPI /* This function handles the driver receive operation in polling mode */ static int amd8111e_rx_poll(struct net_device *dev, int * budget) { @@ -1464,32 +1464,25 @@ static int amd8111e_start_xmit(struct sk /* This function returns all the memory mapped registers of the device. */ -static char* amd8111e_read_regs(struct amd8111e_priv* lp) +static void amd8111e_read_regs(struct amd8111e_priv *lp, u32 *buf) { - void * mmio = lp->mmio; - u32 * reg_buff; - - reg_buff = kmalloc( AMD8111E_REG_DUMP_LEN,GFP_KERNEL); - if(NULL == reg_buff) - return NULL; - + void *mmio = lp->mmio; /* Read only necessary registers */ - reg_buff[0] = readl(mmio + XMT_RING_BASE_ADDR0); - reg_buff[1] = readl(mmio + XMT_RING_LEN0); - reg_buff[2] = readl(mmio + RCV_RING_BASE_ADDR0); - reg_buff[3] = readl(mmio + RCV_RING_LEN0); - reg_buff[4] = readl(mmio + CMD0); - reg_buff[5] = readl(mmio + CMD2); - reg_buff[6] = readl(mmio + CMD3); - reg_buff[7] = readl(mmio + CMD7); - reg_buff[8] = readl(mmio + INT0); - reg_buff[9] = readl(mmio + INTEN0); - reg_buff[10] = readl(mmio + LADRF); - reg_buff[11] = readl(mmio + LADRF+4); - reg_buff[12] = readl(mmio + STAT0); - - return (char *)reg_buff; + buf[0] = readl(mmio + XMT_RING_BASE_ADDR0); + buf[1] = readl(mmio + XMT_RING_LEN0); + buf[2] = readl(mmio + RCV_RING_BASE_ADDR0); + buf[3] = readl(mmio + RCV_RING_LEN0); + buf[4] = readl(mmio + CMD0); + buf[5] = readl(mmio + CMD2); + buf[6] = readl(mmio + CMD3); + buf[7] = readl(mmio + CMD7); + buf[8] = readl(mmio + INT0); + buf[9] = readl(mmio + INTEN0); + buf[10] = readl(mmio + LADRF); + buf[11] = readl(mmio + LADRF+4); + buf[12] = readl(mmio + STAT0); } + /* amd8111e crc generator implementation is different from the kernel ether_crc() function. @@ -1567,131 +1560,101 @@ static void amd8111e_set_multicast_list( } -/* -This function handles all the ethtool ioctls. It gives driver info, gets/sets driver speed, gets memory mapped register values, forces auto negotiation, sets/gets WOL options for ethtool application. -*/ - -static int amd8111e_ethtool_ioctl(struct net_device* dev, void __user *useraddr) +static void amd8111e_get_drvinfo(struct net_device* dev, struct ethtool_drvinfo *info) { struct amd8111e_priv *lp = netdev_priv(dev); struct pci_dev *pci_dev = lp->pci_dev; - u32 ethcmd; - - if( useraddr == NULL) - return -EINVAL; - if(copy_from_user (ðcmd, useraddr, sizeof (ethcmd))) - return -EFAULT; - - switch(ethcmd){ - - case ETHTOOL_GDRVINFO:{ - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy (info.driver, MODULE_NAME); - strcpy (info.version, MODULE_VERS); - memset(&info.fw_version, 0, sizeof(info.fw_version)); - sprintf(info.fw_version,"%u",chip_version); - strcpy (info.bus_info, pci_name(pci_dev)); - info.eedump_len = 0; - info.regdump_len = AMD8111E_REG_DUMP_LEN; - if (copy_to_user (useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - /* get settings */ - case ETHTOOL_GSET: { - struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - spin_lock_irq(&lp->lock); - mii_ethtool_gset(&lp->mii_if, &ecmd); - spin_unlock_irq(&lp->lock); - if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; - } - /* set settings */ - case ETHTOOL_SSET: { - int r; - struct ethtool_cmd ecmd; - if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) - return -EFAULT; + strcpy (info->driver, MODULE_NAME); + strcpy (info->version, MODULE_VERS); + sprintf(info->fw_version,"%u",chip_version); + strcpy (info->bus_info, pci_name(pci_dev)); +} - spin_lock_irq(&lp->lock); - r = mii_ethtool_sset(&lp->mii_if, &ecmd); - spin_unlock_irq(&lp->lock); - return r; - } - case ETHTOOL_GREGS: { - struct ethtool_regs regs; - u8 *regbuf; - int ret; - - if (copy_from_user(®s, useraddr, sizeof(regs))) - return -EFAULT; - if (regs.len > AMD8111E_REG_DUMP_LEN) - regs.len = AMD8111E_REG_DUMP_LEN; - regs.version = 0; - if (copy_to_user(useraddr, ®s, sizeof(regs))) - return -EFAULT; - - regbuf = amd8111e_read_regs(lp); - if (!regbuf) - return -ENOMEM; - - useraddr += offsetof(struct ethtool_regs, data); - ret = 0; - if (copy_to_user(useraddr, regbuf, regs.len)) - ret = -EFAULT; - kfree(regbuf); - return ret; - } - /* restart autonegotiation */ - case ETHTOOL_NWAY_RST: { - return mii_nway_restart(&lp->mii_if); - } - /* get link status */ - case ETHTOOL_GLINK: { - struct ethtool_value val = {ETHTOOL_GLINK}; - val.data = mii_link_ok(&lp->mii_if); - if (copy_to_user(useraddr, &val, sizeof(val))) - return -EFAULT; - return 0; - } - case ETHTOOL_GWOL: { - struct ethtool_wolinfo wol_info = { ETHTOOL_GWOL }; +static int amd8111e_get_regs_len(struct net_device *dev) +{ + return AMD8111E_REG_DUMP_LEN; +} - wol_info.supported = WAKE_MAGIC|WAKE_PHY; - wol_info.wolopts = 0; - if (lp->options & OPTION_WOL_ENABLE) - wol_info.wolopts = WAKE_MAGIC; - memset(&wol_info.sopass, 0, sizeof(wol_info.sopass)); - if (copy_to_user(useraddr, &wol_info, sizeof(wol_info))) - return -EFAULT; - return 0; - } - case ETHTOOL_SWOL: { - struct ethtool_wolinfo wol_info; +static void amd8111e_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *buf) +{ + struct amd8111e_priv *lp = netdev_priv(dev); + regs->version = 0; + amd8111e_read_regs(lp, buf); +} - if (copy_from_user(&wol_info, useraddr, sizeof(wol_info))) - return -EFAULT; - if (wol_info.wolopts & ~(WAKE_MAGIC |WAKE_PHY)) - return -EINVAL; - spin_lock_irq(&lp->lock); - if(wol_info.wolopts & WAKE_MAGIC) - lp->options |= - (OPTION_WOL_ENABLE | OPTION_WAKE_MAGIC_ENABLE); - else if(wol_info.wolopts & WAKE_PHY) - lp->options |= - (OPTION_WOL_ENABLE | OPTION_WAKE_PHY_ENABLE); - else - lp->options &= ~OPTION_WOL_ENABLE; - spin_unlock_irq(&lp->lock); - return 0; - } - - default: - break; - } - return -EOPNOTSUPP; +static int amd8111e_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct amd8111e_priv *lp = netdev_priv(dev); + spin_lock_irq(&lp->lock); + mii_ethtool_gset(&lp->mii_if, ecmd); + spin_unlock_irq(&lp->lock); + return 0; +} + +static int amd8111e_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct amd8111e_priv *lp = netdev_priv(dev); + int res; + spin_lock_irq(&lp->lock); + res = mii_ethtool_sset(&lp->mii_if, ecmd); + spin_unlock_irq(&lp->lock); + return res; +} + +static int amd8111e_nway_reset(struct net_device *dev) +{ + struct amd8111e_priv *lp = netdev_priv(dev); + return mii_nway_restart(&lp->mii_if); +} + +static u32 amd8111e_get_link(struct net_device *dev) +{ + struct amd8111e_priv *lp = netdev_priv(dev); + return mii_link_ok(&lp->mii_if); } + +static void amd8111e_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol_info) +{ + struct amd8111e_priv *lp = netdev_priv(dev); + wol_info->supported = WAKE_MAGIC|WAKE_PHY; + if (lp->options & OPTION_WOL_ENABLE) + wol_info->wolopts = WAKE_MAGIC; +} + +static int amd8111e_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol_info) +{ + struct amd8111e_priv *lp = netdev_priv(dev); + if (wol_info->wolopts & ~(WAKE_MAGIC|WAKE_PHY)) + return -EINVAL; + spin_lock_irq(&lp->lock); + if (wol_info->wolopts & WAKE_MAGIC) + lp->options |= + (OPTION_WOL_ENABLE | OPTION_WAKE_MAGIC_ENABLE); + else if(wol_info->wolopts & WAKE_PHY) + lp->options |= + (OPTION_WOL_ENABLE | OPTION_WAKE_PHY_ENABLE); + else + lp->options &= ~OPTION_WOL_ENABLE; + spin_unlock_irq(&lp->lock); + return 0; +} + +static struct ethtool_ops ops = { + .get_drvinfo = amd8111e_get_drvinfo, + .get_regs_len = amd8111e_get_regs_len, + .get_regs = amd8111e_get_regs, + .get_settings = amd8111e_get_settings, + .set_settings = amd8111e_set_settings, + .nway_reset = amd8111e_nway_reset, + .get_link = amd8111e_get_link, + .get_wol = amd8111e_get_wol, + .set_wol = amd8111e_set_wol, +}; + +/* +This function handles all the ethtool ioctls. It gives driver info, gets/sets driver speed, gets memory mapped register values, forces auto negotiation, sets/gets WOL options for ethtool application. +*/ + static int amd8111e_ioctl(struct net_device * dev , struct ifreq *ifr, int cmd) { struct mii_ioctl_data *data = if_mii(ifr); @@ -1703,8 +1666,6 @@ static int amd8111e_ioctl(struct net_dev return -EPERM; switch(cmd) { - case SIOCETHTOOL: - return amd8111e_ethtool_ioctl(dev, ifr->ifr_data); case SIOCGMIIPHY: data->phy_id = PHY_ID; @@ -2085,6 +2046,7 @@ static int __devinit amd8111e_probe_one( dev->set_mac_address = amd8111e_set_mac_address; dev->do_ioctl = amd8111e_ioctl; dev->change_mtu = amd8111e_change_mtu; + SET_ETHTOOL_OPS(dev, &ops); dev->irq =pdev->irq; dev->tx_timeout = amd8111e_tx_timeout; dev->watchdog_timeo = AMD8111E_TX_TIMEOUT; --- linux-2.6.9-rc1/drivers/net/b44.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/drivers/net/b44.c 2004-09-03 00:56:51.750293408 -0700 @@ -75,7 +75,7 @@ static char version[] __devinitdata = DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; MODULE_AUTHOR("David S. Miller (davem@redhat.com)"); -MODULE_DESCRIPTION("Broadcom 4400 10/100 PCI ethernet driver"); +MODULE_DESCRIPTION("Broadcom 4400/47xx 10/100 PCI ethernet driver"); MODULE_LICENSE("GPL"); MODULE_PARM(b44_debug, "i"); MODULE_PARM_DESC(b44_debug, "B44 bitmapped debugging message enable value"); @@ -89,6 +89,8 @@ static struct pci_device_id b44_pci_tbl[ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401B1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4713, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { } /* terminate list with empty entry */ }; @@ -130,41 +132,8 @@ static int b44_wait_bit(struct b44 *bp, * interrupts disabled. */ -#define SBID_SDRAM 0 -#define SBID_PCI_MEM 1 -#define SBID_PCI_CFG 2 -#define SBID_PCI_DMA 3 -#define SBID_SDRAM_SWAPPED 4 -#define SBID_ENUM 5 -#define SBID_REG_SDRAM 6 -#define SBID_REG_ILINE20 7 -#define SBID_REG_EMAC 8 -#define SBID_REG_CODEC 9 -#define SBID_REG_USB 10 -#define SBID_REG_PCI 11 -#define SBID_REG_MIPS 12 -#define SBID_REG_EXTIF 13 -#define SBID_EXTIF 14 -#define SBID_EJTAG 15 -#define SBID_MAX 16 - -static u32 ssb_get_addr(struct b44 *bp, u32 id, u32 instance) -{ - switch (id) { - case SBID_PCI_DMA: - return 0x40000000; - case SBID_ENUM: - return 0x18000000; - case SBID_REG_EMAC: - return 0x18000000; - case SBID_REG_CODEC: - return 0x18001000; - case SBID_REG_PCI: - return 0x18002000; - default: - return 0; - }; -} +#define SB_PCI_DMA 0x40000000 /* Client Mode PCI memory access space (1 GB) */ +#define BCM4400_PCI_CORE_ADDR 0x18002000 /* Address of PCI core on BCM4400 cards */ static u32 ssb_get_core_rev(struct b44 *bp) { @@ -176,8 +145,7 @@ static u32 ssb_pci_setup(struct b44 *bp, u32 bar_orig, pci_rev, val; pci_read_config_dword(bp->pdev, SSB_BAR0_WIN, &bar_orig); - pci_write_config_dword(bp->pdev, SSB_BAR0_WIN, - ssb_get_addr(bp, SBID_REG_PCI, 0)); + pci_write_config_dword(bp->pdev, SSB_BAR0_WIN, BCM4400_PCI_CORE_ADDR); pci_rev = ssb_get_core_rev(bp); val = br32(B44_SBINTVEC); @@ -307,6 +275,9 @@ static int b44_readphy(struct b44 *bp, i { int err; + if (bp->flags & B44_FLAG_NO_PHY) + return 0; + bw32(B44_EMAC_ISTAT, EMAC_INT_MII); bw32(B44_MDIO_DATA, (MDIO_DATA_SB_START | (MDIO_OP_READ << MDIO_DATA_OP_SHIFT) | @@ -321,6 +292,9 @@ static int b44_readphy(struct b44 *bp, i static int b44_writephy(struct b44 *bp, int reg, u32 val) { + if (bp->flags & B44_FLAG_NO_PHY) + return 0; + bw32(B44_EMAC_ISTAT, EMAC_INT_MII); bw32(B44_MDIO_DATA, (MDIO_DATA_SB_START | (MDIO_OP_WRITE << MDIO_DATA_OP_SHIFT) | @@ -359,6 +333,9 @@ static int b44_phy_reset(struct b44 *bp) u32 val; int err; + if (bp->flags & B44_FLAG_NO_PHY) + return 0; + err = b44_writephy(bp, MII_BMCR, BMCR_RESET); if (err) return err; @@ -429,6 +406,9 @@ static int b44_setup_phy(struct b44 *bp) u32 val; int err; + if (bp->flags & B44_FLAG_NO_PHY) + return 0; + if ((err = b44_readphy(bp, B44_MII_ALEDCTRL, &val)) != 0) goto out; if ((err = b44_writephy(bp, B44_MII_ALEDCTRL, @@ -521,6 +501,19 @@ static void b44_check_phy(struct b44 *bp { u32 bmsr, aux; + if (bp->flags & B44_FLAG_NO_PHY) { + bp->flags |= B44_FLAG_100_BASE_T; + bp->flags |= B44_FLAG_FULL_DUPLEX; + if (!netif_carrier_ok(bp->dev)) { + u32 val = br32(B44_TX_CTRL); + val |= TX_CTRL_DUPLEX; + bw32(B44_TX_CTRL, val); + netif_carrier_on(bp->dev); + b44_link_report(bp); + } + return; + } + if (!b44_readphy(bp, MII_BMSR, &bmsr) && !b44_readphy(bp, B44_MII_AUXCTRL, &aux) && (bmsr != 0xffff)) { @@ -1132,18 +1125,28 @@ static void b44_chip_reset(struct b44 *b bw32(B44_DMARX_CTRL, 0); bp->rx_prod = bp->rx_cons = 0; } else { - ssb_pci_setup(bp, (bp->core_unit == 0 ? - SBINTVEC_ENET0 : - SBINTVEC_ENET1)); + if (bp->pdev->device != PCI_DEVICE_ID_BCM4713) + ssb_pci_setup(bp, (bp->core_unit == 0 ? + SBINTVEC_ENET0 : + SBINTVEC_ENET1)); } ssb_core_reset(bp); b44_clear_stats(bp); - /* Make PHY accessible. */ - bw32(B44_MDIO_CTRL, (MDIO_CTRL_PREAMBLE | - (0x0d & MDIO_CTRL_MAXF_MASK))); + if (bp->pdev->device == PCI_DEVICE_ID_BCM4713) { + /* + * BCM47xx boards don't have a PHY. Usually there is a switch + * chip with multiple PHYs conntected to the PHY port. + */ + bp->flags |= B44_FLAG_NO_PHY; + bw32(B44_MDIO_CTRL, 0x94); + } else { + /* Make PHY accessible. */ + bw32(B44_MDIO_CTRL, (MDIO_CTRL_PREAMBLE | + (0x0d & MDIO_CTRL_MAXF_MASK))); + } br32(B44_MDIO_CTRL); if (!(br32(B44_DEVCTRL) & DEVCTRL_IPP)) { @@ -1659,21 +1662,38 @@ static int b44_read_eeprom(struct b44 *b static int __devinit b44_get_invariants(struct b44 *bp) { u8 eeprom[128]; - int err; + int err = 0; + static int instance = 0; - err = b44_read_eeprom(bp, &eeprom[0]); - if (err) - goto out; + if (bp->pdev->device == PCI_DEVICE_ID_BCM4713) { + bp->dev->dev_addr[0] = 0x01; + bp->dev->dev_addr[1] = 0x01; + bp->dev->dev_addr[2] = 0x02; + bp->dev->dev_addr[3] = 0x02; + bp->dev->dev_addr[4] = 0x01; + bp->dev->dev_addr[5] = 0x01 + instance; + + bp->phy_addr = 30; + bp->mdc_port = instance++; + + bp->dma_offset = 0; + } else { + err = b44_read_eeprom(bp, &eeprom[0]); + if (err) + goto out; - bp->dev->dev_addr[0] = eeprom[79]; - bp->dev->dev_addr[1] = eeprom[78]; - bp->dev->dev_addr[2] = eeprom[81]; - bp->dev->dev_addr[3] = eeprom[80]; - bp->dev->dev_addr[4] = eeprom[83]; - bp->dev->dev_addr[5] = eeprom[82]; + bp->dev->dev_addr[0] = eeprom[79]; + bp->dev->dev_addr[1] = eeprom[78]; + bp->dev->dev_addr[2] = eeprom[81]; + bp->dev->dev_addr[3] = eeprom[80]; + bp->dev->dev_addr[4] = eeprom[83]; + bp->dev->dev_addr[5] = eeprom[82]; - bp->phy_addr = eeprom[90] & 0x1f; - bp->mdc_port = (eeprom[90] >> 14) & 0x1; + bp->phy_addr = eeprom[90] & 0x1f; + bp->mdc_port = (eeprom[90] >> 14) & 0x1; + + bp->dma_offset = SB_PCI_DMA; + } /* With this, plus the rx_header prepended to the data by the * hardware, we'll land the ethernet header on a 2-byte boundary. @@ -1683,7 +1703,6 @@ static int __devinit b44_get_invariants( bp->imask = IMASK_DEF; bp->core_unit = ssb_core_unit(bp); - bp->dma_offset = ssb_get_addr(bp, SBID_PCI_DMA, 0); /* XXX - really required? bp->flags |= B44_FLAG_BUGGY_TXPTR; @@ -1818,7 +1837,8 @@ static int __devinit b44_init_one(struct pci_save_state(bp->pdev, bp->pci_cfg_state); - printk(KERN_INFO "%s: Broadcom 4400 10/100BaseT Ethernet ", dev->name); + printk(KERN_INFO "%s: Broadcom %s 10/100BaseT Ethernet ", dev->name, + (pdev->device == PCI_DEVICE_ID_BCM4713) ? "47xx" : "4400"); for (i = 0; i < 6; i++) printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); --- linux-2.6.9-rc1/drivers/net/b44.h 2004-08-24 00:02:59.000000000 -0700 +++ 25/drivers/net/b44.h 2004-09-03 00:56:51.750293408 -0700 @@ -356,16 +356,6 @@ #define SBIDHIGH_VC_MASK 0xffff0000 /* Vendor Code */ #define SBIDHIGH_VC_SHIFT 16 -#define CORE_CODE_ILINE20 0x801 -#define CORE_CODE_SDRAM 0x803 -#define CORE_CODE_PCI 0x804 -#define CORE_CODE_MIPS 0x805 -#define CORE_CODE_ENET 0x806 -#define CORE_CODE_CODEC 0x807 -#define CORE_CODE_USB 0x808 -#define CORE_CODE_ILINE100 0x80a -#define CORE_CODE_EXTIF 0x811 - /* SSB PCI config space registers. */ #define SSB_BAR0_WIN 0x80 #define SSB_BAR1_WIN 0x84 @@ -520,6 +510,7 @@ struct b44 { #define B44_FLAG_ADV_100HALF 0x04000000 #define B44_FLAG_ADV_100FULL 0x08000000 #define B44_FLAG_INTERNAL_PHY 0x10000000 +#define B44_FLAG_NO_PHY 0x20000000 u32 rx_offset; --- linux-2.6.9-rc1/drivers/net/cs89x0.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/net/cs89x0.c 2004-09-03 00:57:19.271109608 -0700 @@ -84,6 +84,9 @@ Oskar Schirmer : oskar@scara.com : HiCO.SH4 (superh) support added (irq#1, cs89x0_media=) + Deepak Saxena : dsaxena@plexity.net + : Intel IXDP2x01 (XScale ixp2x00 NPU) platform support + */ /* Always include 'config.h' first in case the user wants to turn on @@ -97,7 +100,11 @@ * Note that even if DMA is turned off we still support the 'dma' and 'use_dma' * module options so we don't break any startup scripts. */ +#ifndef CONFIG_ARCH_IXDP2X01 +#define ALLOW_DMA 0 +#else #define ALLOW_DMA 1 +#endif /* * Set this to zero to remove all the debug statements via @@ -162,6 +169,10 @@ static unsigned int cs8900_irq_map[] = { static unsigned int netcard_portlist[] __initdata = { 0x0300, 0}; static unsigned int cs8900_irq_map[] = {1,0,0,0}; +#elif defined(CONFIG_ARCH_IXDP2X01) +#include +static unsigned int netcard_portlist[] __initdata = {IXDP2X01_CS8900_VIRT_BASE, 0}; +static unsigned int cs8900_irq_map[] = {IRQ_IXDP2X01_CS8900, 0, 0, 0}; #else static unsigned int netcard_portlist[] __initdata = { 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0}; @@ -454,11 +465,12 @@ cs89x0_probe1(struct net_device *dev, in retval = -ENODEV; goto out2; } - ioaddr &= ~3; - outw(PP_ChipID, ioaddr + ADD_PORT); } printk("PP_addr=0x%x\n", inw(ioaddr + ADD_PORT)); + ioaddr &= ~3; + outw(PP_ChipID, ioaddr + ADD_PORT); + if (inw(ioaddr + DATA_PORT) != CHIP_EISA_ID_SIG) { printk(KERN_ERR "%s: incorrect signature 0x%x\n", dev->name, inw(ioaddr + DATA_PORT)); @@ -665,6 +677,9 @@ printk("PP_addr=0x%x\n", inw(ioaddr + AD } else { i = lp->isa_config & INT_NO_MASK; if (lp->chip_type == CS8900) { +#ifdef CONFIG_ARCH_IXDP2X01 + i = cs8900_irq_map[0]; +#else /* Translate the IRQ using the IRQ mapping table. */ if (i >= sizeof(cs8900_irq_map)/sizeof(cs8900_irq_map[0])) printk("\ncs89x0: invalid ISA interrupt number %d\n", i); @@ -681,6 +696,7 @@ printk("PP_addr=0x%x\n", inw(ioaddr + AD if ((irq_map_buff[0] & 0xff) == PNP_IRQ_FRMT) lp->irq_map = (irq_map_buff[0]>>8) | (irq_map_buff[1] << 8); } +#endif } if (!dev->irq) dev->irq = i; @@ -884,8 +900,10 @@ skip_this_frame: void __init reset_chip(struct net_device *dev) { +#ifndef CONFIG_ARCH_IXDP2X01 struct net_local *lp = netdev_priv(dev); int ioaddr = dev->base_addr; +#endif int reset_start_time; writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET); @@ -894,6 +912,7 @@ void __init reset_chip(struct net_devic current->state = TASK_INTERRUPTIBLE; schedule_timeout(30*HZ/1000); +#ifndef CONFIG_ARCH_IXDP2X01 if (lp->chip_type != CS8900) { /* Hardware problem requires PNP registers to be reconfigured after a reset */ outw(PP_CS8920_ISAINT, ioaddr + ADD_PORT); @@ -904,6 +923,8 @@ void __init reset_chip(struct net_devic outb((dev->mem_start >> 16) & 0xff, ioaddr + DATA_PORT); outb((dev->mem_start >> 8) & 0xff, ioaddr + DATA_PORT + 1); } +#endif /* IXDP2x01 */ + /* Wait until the chip is reset */ reset_start_time = jiffies; while( (readreg(dev, PP_SelfST) & INIT_DONE) == 0 && jiffies - reset_start_time < 2) @@ -1155,12 +1176,14 @@ net_open(struct net_device *dev) else #endif { +#ifndef CONFIG_ARCH_IXDP2X01 if (((1 << dev->irq) & lp->irq_map) == 0) { printk(KERN_ERR "%s: IRQ %d is not in our map of allowable IRQs, which is %x\n", dev->name, dev->irq, lp->irq_map); ret = -EAGAIN; goto bad_out; } +#endif /* FIXME: Cirrus' release had this: */ writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL)|ENABLE_IRQ ); /* And 2.3.47 had this: */ @@ -1575,7 +1598,9 @@ static void release_dma_buff(struct net_ static int net_close(struct net_device *dev) { +#if ALLOW_DMA struct net_local *lp = netdev_priv(dev); +#endif netif_stop_queue(dev); --- linux-2.6.9-rc1/drivers/net/cs89x0.h 2004-08-24 00:03:18.000000000 -0700 +++ 25/drivers/net/cs89x0.h 2004-09-03 00:56:51.606315296 -0700 @@ -16,6 +16,13 @@ #include +#ifdef CONFIG_ARCH_IXDP2X01 +/* IXDP2401/IXDP2801 uses dword-aligned register addressing */ +#define CS89x0_PORT(reg) ((reg) * 2) +#else +#define CS89x0_PORT(reg) (reg) +#endif + #define PP_ChipID 0x0000 /* offset 0h -> Corp -ID */ /* offset 2h -> Model/Product Number */ /* offset 3h -> Chip Revision Number */ @@ -324,16 +331,16 @@ #define RAM_SIZE 0x1000 /* The card has 4k bytes or RAM */ #define PKT_START PP_TxFrame /* Start of packet RAM */ -#define RX_FRAME_PORT 0x0000 +#define RX_FRAME_PORT CS89x0_PORT(0x0000) #define TX_FRAME_PORT RX_FRAME_PORT -#define TX_CMD_PORT 0x0004 +#define TX_CMD_PORT CS89x0_PORT(0x0004) #define TX_NOW 0x0000 /* Tx packet after 5 bytes copied */ #define TX_AFTER_381 0x0040 /* Tx packet after 381 bytes copied */ #define TX_AFTER_ALL 0x00c0 /* Tx packet after all bytes copied */ -#define TX_LEN_PORT 0x0006 -#define ISQ_PORT 0x0008 -#define ADD_PORT 0x000A -#define DATA_PORT 0x000C +#define TX_LEN_PORT CS89x0_PORT(0x0006) +#define ISQ_PORT CS89x0_PORT(0x0008) +#define ADD_PORT CS89x0_PORT(0x000A) +#define DATA_PORT CS89x0_PORT(0x000C) #define EEPROM_WRITE_EN 0x00F0 #define EEPROM_WRITE_DIS 0x0000 --- linux-2.6.9-rc1/drivers/net/defxx.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/drivers/net/defxx.c 2004-09-03 00:56:32.535214544 -0700 @@ -19,7 +19,7 @@ * LVS Lawrence V. Stefani * * Maintainers: - * macro Maciej W. Rozycki + * macro Maciej W. Rozycki * * Credits: * I'd like to thank Patricia Cross for helping me get started with @@ -190,6 +190,7 @@ * Feb 2001 Skb allocation fixes * Feb 2001 davej PCI enable cleanups. * 04 Aug 2003 macro Converted to the DMA API. + * 14 Aug 2004 macro Fix device names reported. */ /* Include files */ @@ -214,12 +215,14 @@ #include "defxx.h" -/* Version information string - should be updated prior to each new release!!! */ +/* Version information string should be updated prior to each new release! */ +#define DRV_NAME "defxx" +#define DRV_VERSION "v1.07" +#define DRV_RELDATE "2004/08/14" static char version[] __devinitdata = - "defxx.c:v1.06 2003/08/04 Lawrence V. Stefani and others\n"; - -#define DRV_NAME "defxx" + DRV_NAME ": " DRV_VERSION " " DRV_RELDATE + " Lawrence V. Stefani and others\n"; #define DYNAMIC_BUFFERS 1 @@ -235,7 +238,7 @@ static char version[] __devinitdata = static void dfx_bus_init(struct net_device *dev); static void dfx_bus_config_check(DFX_board_t *bp); -static int dfx_driver_init(struct net_device *dev); +static int dfx_driver_init(struct net_device *dev, const char *print_name); static int dfx_adap_init(DFX_board_t *bp, int get_buffers); static int dfx_open(struct net_device *dev); @@ -404,24 +407,25 @@ static inline void dfx_port_read_long( */ static int __devinit dfx_init_one_pci_or_eisa(struct pci_dev *pdev, long ioaddr) { + static int version_disp; + char *print_name = DRV_NAME; struct net_device *dev; DFX_board_t *bp; /* board pointer */ int alloc_size; /* total buffer size used */ int err; -#ifndef MODULE - static int version_disp; - - if (!version_disp) /* display version info if adapter is found */ - { + if (!version_disp) { /* display version info if adapter is found */ version_disp = 1; /* set display flag to TRUE so that */ printk(version); /* we only display this string ONCE */ } -#endif + + if (pdev != NULL) + print_name = pdev->slot_name; dev = alloc_fddidev(sizeof(*bp)); if (!dev) { - printk (KERN_ERR "defxx: unable to allocate fddidev, aborting\n"); + printk(KERN_ERR "%s: unable to allocate fddidev, aborting\n", + print_name); return -ENOMEM; } @@ -437,9 +441,12 @@ static int __devinit dfx_init_one_pci_or bp = dev->priv; - if (!request_region (ioaddr, pdev ? PFI_K_CSR_IO_LEN : PI_ESIC_K_CSR_IO_LEN, DRV_NAME)) { - printk (KERN_ERR "%s: Cannot reserve I/O resource 0x%x @ 0x%lx, aborting\n", - DRV_NAME, PFI_K_CSR_IO_LEN, ioaddr); + if (!request_region(ioaddr, + pdev ? PFI_K_CSR_IO_LEN : PI_ESIC_K_CSR_IO_LEN, + print_name)) { + printk(KERN_ERR "%s: Cannot reserve I/O resource " + "0x%x @ 0x%lx, aborting\n", print_name, + pdev ? PFI_K_CSR_IO_LEN : PI_ESIC_K_CSR_IO_LEN, ioaddr); err = -EBUSY; goto err_out; } @@ -468,7 +475,7 @@ static int __devinit dfx_init_one_pci_or pci_set_master (pdev); } - if (dfx_driver_init(dev) != DFX_K_SUCCESS) { + if (dfx_driver_init(dev, print_name) != DFX_K_SUCCESS) { err = -ENODEV; goto err_out_region; } @@ -477,6 +484,7 @@ static int __devinit dfx_init_one_pci_or if (err) goto err_out_kfree; + printk("%s: registered as %s\n", print_name, dev->name); return 0; err_out_kfree: @@ -770,6 +778,7 @@ static void __devinit dfx_bus_config_che * * Arguments: * dev - pointer to device information + * print_name - printable device name * * Functional Description: * This function allocates additional resources such as the host memory @@ -792,7 +801,8 @@ static void __devinit dfx_bus_config_che * returning from this routine. */ -static int __devinit dfx_driver_init(struct net_device *dev) +static int __devinit dfx_driver_init(struct net_device *dev, + const char *print_name) { DFX_board_t *bp = dev->priv; int alloc_size; /* total buffer size needed */ @@ -841,26 +851,20 @@ static int __devinit dfx_driver_init(str /* Read the factory MAC address from the adapter then save it */ - if (dfx_hw_port_ctrl_req(bp, - PI_PCTRL_M_MLA, - PI_PDATA_A_MLA_K_LO, - 0, - &data) != DFX_K_SUCCESS) - { - printk("%s: Could not read adapter factory MAC address!\n", dev->name); + if (dfx_hw_port_ctrl_req(bp, PI_PCTRL_M_MLA, PI_PDATA_A_MLA_K_LO, 0, + &data) != DFX_K_SUCCESS) { + printk("%s: Could not read adapter factory MAC address!\n", + print_name); return(DFX_K_FAILURE); - } + } memcpy(&bp->factory_mac_addr[0], &data, sizeof(u32)); - if (dfx_hw_port_ctrl_req(bp, - PI_PCTRL_M_MLA, - PI_PDATA_A_MLA_K_HI, - 0, - &data) != DFX_K_SUCCESS) - { - printk("%s: Could not read adapter factory MAC address!\n", dev->name); + if (dfx_hw_port_ctrl_req(bp, PI_PCTRL_M_MLA, PI_PDATA_A_MLA_K_HI, 0, + &data) != DFX_K_SUCCESS) { + printk("%s: Could not read adapter factory MAC address!\n", + print_name); return(DFX_K_FAILURE); - } + } memcpy(&bp->factory_mac_addr[4], &data, sizeof(u16)); /* @@ -872,27 +876,19 @@ static int __devinit dfx_driver_init(str memcpy(dev->dev_addr, bp->factory_mac_addr, FDDI_K_ALEN); if (bp->bus_type == DFX_BUS_TYPE_EISA) - printk("%s: DEFEA at I/O addr = 0x%lX, IRQ = %d, Hardware addr = %02X-%02X-%02X-%02X-%02X-%02X\n", - dev->name, - dev->base_addr, - dev->irq, - dev->dev_addr[0], - dev->dev_addr[1], - dev->dev_addr[2], - dev->dev_addr[3], - dev->dev_addr[4], - dev->dev_addr[5]); + printk("%s: DEFEA at I/O addr = 0x%lX, IRQ = %d, " + "Hardware addr = %02X-%02X-%02X-%02X-%02X-%02X\n", + print_name, dev->base_addr, dev->irq, + dev->dev_addr[0], dev->dev_addr[1], + dev->dev_addr[2], dev->dev_addr[3], + dev->dev_addr[4], dev->dev_addr[5]); else - printk("%s: DEFPA at I/O addr = 0x%lX, IRQ = %d, Hardware addr = %02X-%02X-%02X-%02X-%02X-%02X\n", - dev->name, - dev->base_addr, - dev->irq, - dev->dev_addr[0], - dev->dev_addr[1], - dev->dev_addr[2], - dev->dev_addr[3], - dev->dev_addr[4], - dev->dev_addr[5]); + printk("%s: DEFPA at I/O addr = 0x%lX, IRQ = %d, " + "Hardware addr = %02X-%02X-%02X-%02X-%02X-%02X\n", + print_name, dev->base_addr, dev->irq, + dev->dev_addr[0], dev->dev_addr[1], + dev->dev_addr[2], dev->dev_addr[3], + dev->dev_addr[4], dev->dev_addr[5]); /* * Get memory for descriptor block, consumer block, and other buffers @@ -909,11 +905,11 @@ static int __devinit dfx_driver_init(str (PI_ALIGN_K_DESC_BLK - 1); bp->kmalloced = top_v = pci_alloc_consistent(bp->pci_dev, alloc_size, &bp->kmalloced_dma); - if (top_v == NULL) - { - printk("%s: Could not allocate memory for host buffers and structures!\n", dev->name); + if (top_v == NULL) { + printk("%s: Could not allocate memory for host buffers " + "and structures!\n", print_name); return(DFX_K_FAILURE); - } + } memset(top_v, 0, alloc_size); /* zero out memory before continuing */ top_p = bp->kmalloced_dma; /* get physical address of buffer */ @@ -970,14 +966,20 @@ static int __devinit dfx_driver_init(str /* Display virtual and physical addresses if debug driver */ - DBG_printk("%s: Descriptor block virt = %0lX, phys = %0X\n", dev->name, (long)bp->descr_block_virt, bp->descr_block_phys); - DBG_printk("%s: Command Request buffer virt = %0lX, phys = %0X\n", dev->name, (long)bp->cmd_req_virt, bp->cmd_req_phys); - DBG_printk("%s: Command Response buffer virt = %0lX, phys = %0X\n", dev->name, (long)bp->cmd_rsp_virt, bp->cmd_rsp_phys); - DBG_printk("%s: Receive buffer block virt = %0lX, phys = %0X\n", dev->name, (long)bp->rcv_block_virt, bp->rcv_block_phys); - DBG_printk("%s: Consumer block virt = %0lX, phys = %0X\n", dev->name, (long)bp->cons_block_virt, bp->cons_block_phys); + DBG_printk("%s: Descriptor block virt = %0lX, phys = %0X\n", + print_name, + (long)bp->descr_block_virt, bp->descr_block_phys); + DBG_printk("%s: Command Request buffer virt = %0lX, phys = %0X\n", + print_name, (long)bp->cmd_req_virt, bp->cmd_req_phys); + DBG_printk("%s: Command Response buffer virt = %0lX, phys = %0X\n", + print_name, (long)bp->cmd_rsp_virt, bp->cmd_rsp_phys); + DBG_printk("%s: Receive buffer block virt = %0lX, phys = %0X\n", + print_name, (long)bp->rcv_block_virt, bp->rcv_block_phys); + DBG_printk("%s: Consumer block virt = %0lX, phys = %0X\n", + print_name, (long)bp->cons_block_virt, bp->cons_block_phys); return(DFX_K_SUCCESS); - } +} /* @@ -2666,12 +2668,12 @@ static int dfx_hw_dma_uninit(DFX_board_t static void my_skb_align(struct sk_buff *skb, int n) { - unsigned long x=(unsigned long)skb->data; + unsigned long x = (unsigned long)skb->data; unsigned long v; - v=(x+n-1)&~(n-1); /* Where we want to be */ + v = ALIGN(x, n); /* Where we want to be */ - skb_reserve(skb, v-x); + skb_reserve(skb, v - x); } @@ -3426,11 +3428,6 @@ static int __init dfx_init(void) { int rc_pci, rc_eisa; -/* when a module, this is printed whether or not devices are found in probe */ -#ifdef MODULE - printk(version); -#endif - rc_pci = pci_module_init(&dfx_driver); if (rc_pci >= 0) dfx_have_pci = 1; @@ -3451,6 +3448,9 @@ static void __exit dfx_cleanup(void) module_init(dfx_init); module_exit(dfx_cleanup); +MODULE_AUTHOR("Lawrence V. Stefani"); +MODULE_DESCRIPTION("DEC FDDIcontroller EISA/PCI (DEFEA/DEFPA) driver " + DRV_VERSION " " DRV_RELDATE); MODULE_LICENSE("GPL"); --- linux-2.6.9-rc1/drivers/net/defxx.h 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/net/defxx.h 2004-09-03 00:56:32.537214240 -0700 @@ -16,7 +16,7 @@ * LVS Lawrence V. Stefani * * Maintainers: - * macro Maciej W. Rozycki + * macro Maciej W. Rozycki * * Modification History: * Date Name Description --- linux-2.6.9-rc1/drivers/net/dl2k.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/drivers/net/dl2k.c 2004-09-03 00:56:32.540213784 -0700 @@ -107,7 +107,6 @@ static int change_mtu (struct net_device static void set_multicast (struct net_device *dev); static struct net_device_stats *get_stats (struct net_device *dev); static int clear_stats (struct net_device *dev); -static int rio_ethtool_ioctl (struct net_device *dev, void __user *useraddr); static int rio_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); static int rio_close (struct net_device *dev); static int find_miiphy (struct net_device *dev); @@ -122,6 +121,8 @@ static int mii_read (struct net_device * static int mii_write (struct net_device *dev, int phy_addr, int reg_num, u16 data); +static struct ethtool_ops ethtool_ops; + static int __devinit rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -168,7 +169,7 @@ rio_probe1 (struct pci_dev *pdev, const #endif dev->base_addr = ioaddr; dev->irq = irq; - np = dev->priv; + np = netdev_priv(dev); np->chip_id = chip_idx; np->pdev = pdev; spin_lock_init (&np->tx_lock); @@ -244,6 +245,7 @@ rio_probe1 (struct pci_dev *pdev, const dev->tx_timeout = &rio_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; dev->change_mtu = &change_mtu; + SET_ETHTOOL_OPS(dev, ðtool_ops); #if 0 dev->features = NETIF_F_IP_CSUM; #endif @@ -335,7 +337,7 @@ find_miiphy (struct net_device *dev) int i, phy_found = 0; struct netdev_private *np; long ioaddr; - np = dev->priv; + np = netdev_priv(dev); ioaddr = dev->base_addr; np->phy_addr = 1; @@ -362,7 +364,7 @@ parse_eeprom (struct net_device *dev) u8 *psib; u32 crc; PSROM_t psrom = (PSROM_t) sromdata; - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); int cid, next; @@ -432,7 +434,7 @@ parse_eeprom (struct net_device *dev) static int rio_open (struct net_device *dev) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); long ioaddr = dev->base_addr; int i; u16 macctrl; @@ -516,7 +518,7 @@ static void rio_timer (unsigned long data) { struct net_device *dev = (struct net_device *)data; - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); unsigned int entry; int next_tick = 1*HZ; unsigned long flags; @@ -574,7 +576,7 @@ rio_tx_timeout (struct net_device *dev) static void alloc_list (struct net_device *dev) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); int i; np->cur_rx = np->cur_tx = 0; @@ -631,7 +633,7 @@ alloc_list (struct net_device *dev) static int start_xmit (struct sk_buff *skb, struct net_device *dev) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); struct netdev_desc *txdesc; unsigned entry; u32 ioaddr; @@ -711,7 +713,7 @@ rio_interrupt (int irq, void *dev_instan int handled = 0; ioaddr = dev->base_addr; - np = dev->priv; + np = netdev_priv(dev); while (1) { int_status = readw (ioaddr + IntStatus); writew (int_status, ioaddr + IntStatus); @@ -745,7 +747,7 @@ rio_interrupt (int irq, void *dev_instan static void rio_free_tx (struct net_device *dev, int irq) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); int entry = np->old_tx % TX_RING_SIZE; int tx_use = 0; unsigned long flag = 0; @@ -798,7 +800,7 @@ tx_error (struct net_device *dev, int tx int frame_id; int i; - np = dev->priv; + np = netdev_priv(dev); frame_id = (tx_status & 0xffff0000); printk (KERN_ERR "%s: Transmit error, TxStatus %4.4x, FrameId %d.\n", @@ -855,7 +857,7 @@ tx_error (struct net_device *dev, int tx static int receive_packet (struct net_device *dev) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); int entry = np->cur_rx % RX_RING_SIZE; int cnt = 30; @@ -965,7 +967,7 @@ static void rio_error (struct net_device *dev, int int_status) { long ioaddr = dev->base_addr; - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); u16 macctrl; /* Link change event */ @@ -1016,7 +1018,7 @@ static struct net_device_stats * get_stats (struct net_device *dev) { long ioaddr = dev->base_addr; - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); #ifdef MEM_MAPPING int i; #endif @@ -1132,7 +1134,7 @@ clear_stats (struct net_device *dev) int change_mtu (struct net_device *dev, int new_mtu) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); int max = (np->jumbo) ? MAX_JUMBO : 1536; if ((new_mtu < 68) || (new_mtu > max)) { @@ -1150,7 +1152,7 @@ set_multicast (struct net_device *dev) long ioaddr = dev->base_addr; u32 hash_table[2]; u16 rx_mode = 0; - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); hash_table[0] = hash_table[1] = 0; /* RxFlowcontrol DA: 01-80-C2-00-00-01. Hash index=0x39 */ @@ -1194,137 +1196,118 @@ set_multicast (struct net_device *dev) writew (rx_mode, ioaddr + ReceiveMode); } -static int -rio_ethtool_ioctl (struct net_device *dev, void __user *useraddr) +static void rio_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + struct netdev_private *np = netdev_priv(dev); + strcpy(info->driver, "DL2K"); + strcpy(info->version, DRV_VERSION); + strcpy(info->bus_info, pci_name(np->pdev)); +} + +static int rio_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { - struct netdev_private *np = dev->priv; - u32 ethcmd; + struct netdev_private *np = netdev_priv(dev); + if (np->phy_media) { + /* fiber device */ + cmd->supported = SUPPORTED_Autoneg | SUPPORTED_FIBRE; + cmd->advertising= ADVERTISED_Autoneg | ADVERTISED_FIBRE; + cmd->port = PORT_FIBRE; + cmd->transceiver = XCVR_INTERNAL; + } else { + /* copper device */ + cmd->supported = SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half + | SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full | + SUPPORTED_Autoneg | SUPPORTED_MII; + cmd->advertising = ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full | ADVERTISED_1000baseT_Full| + ADVERTISED_Autoneg | ADVERTISED_MII; + cmd->port = PORT_MII; + cmd->transceiver = XCVR_INTERNAL; + } + if ( np->link_status ) { + cmd->speed = np->speed; + cmd->duplex = np->full_duplex ? DUPLEX_FULL : DUPLEX_HALF; + } else { + cmd->speed = -1; + cmd->duplex = -1; + } + if ( np->an_enable) + cmd->autoneg = AUTONEG_ENABLE; + else + cmd->autoneg = AUTONEG_DISABLE; - if (copy_from_user (ðcmd, useraddr, sizeof (ethcmd))) - return -EFAULT; - switch (ethcmd) { - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy(info.driver, "DL2K"); - strcpy(info.version, DRV_VERSION); - strcpy(info.bus_info, pci_name(np->pdev)); - memset(&info.fw_version, 0, sizeof(info.fw_version)); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; + cmd->phy_address = np->phy_addr; + return 0; +} + +static int rio_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct netdev_private *np = netdev_priv(dev); + netif_carrier_off(dev); + if (cmd->autoneg == AUTONEG_ENABLE) { + if (np->an_enable) return 0; + else { + np->an_enable = 1; + mii_set_media(dev); + return 0; } - - case ETHTOOL_GSET: { - struct ethtool_cmd cmd = { ETHTOOL_GSET }; - if (np->phy_media) { - /* fiber device */ - cmd.supported = SUPPORTED_Autoneg | - SUPPORTED_FIBRE; - cmd.advertising= ADVERTISED_Autoneg | - ADVERTISED_FIBRE; - cmd.port = PORT_FIBRE; - cmd.transceiver = XCVR_INTERNAL; - } else { - /* copper device */ - cmd.supported = SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half - | SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full | - SUPPORTED_Autoneg | SUPPORTED_MII; - cmd.advertising = ADVERTISED_10baseT_Half | - ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half | - ADVERTISED_100baseT_Full | ADVERTISED_1000baseT_Full| - ADVERTISED_Autoneg | ADVERTISED_MII; - cmd.port = PORT_MII; - cmd.transceiver = XCVR_INTERNAL; - } - if ( np->link_status ) { - cmd.speed = np->speed; - cmd.duplex = np->full_duplex ? - DUPLEX_FULL : DUPLEX_HALF; - } else { - cmd.speed = -1; - cmd.duplex = -1; - } - if ( np->an_enable) - cmd.autoneg = AUTONEG_ENABLE; - else - cmd.autoneg = AUTONEG_DISABLE; - - cmd.phy_address = np->phy_addr; - - if (copy_to_user(useraddr, &cmd, - sizeof(cmd))) - return -EFAULT; - return 0; - } - case ETHTOOL_SSET: { - struct ethtool_cmd cmd; - if (copy_from_user(&cmd, useraddr, sizeof(cmd))) - return -EFAULT; - netif_carrier_off(dev); - if (cmd.autoneg == AUTONEG_ENABLE) { - if (np->an_enable) - return 0; - else { - np->an_enable = 1; - mii_set_media(dev); - return 0; - } - } else { - np->an_enable = 0; - if (np->speed == 1000){ - cmd.speed = SPEED_100; - cmd.duplex = DUPLEX_FULL; - printk("Warning!! Can't disable Auto negotiation in 1000Mbps, change to Manul 100Mbps, Full duplex.\n"); - } - switch(cmd.speed + cmd.duplex){ - - case SPEED_10 + DUPLEX_HALF: - np->speed = 10; - np->full_duplex = 0; - break; - - case SPEED_10 + DUPLEX_FULL: - np->speed = 10; - np->full_duplex = 1; - break; - case SPEED_100 + DUPLEX_HALF: - np->speed = 100; - np->full_duplex = 0; - break; - case SPEED_100 + DUPLEX_FULL: - np->speed = 100; - np->full_duplex = 1; - break; - case SPEED_1000 + DUPLEX_HALF:/* not supported */ - case SPEED_1000 + DUPLEX_FULL:/* not supported */ - default: - return -EINVAL; - } - mii_set_media(dev); - } - return 0; + } else { + np->an_enable = 0; + if (np->speed == 1000) { + cmd->speed = SPEED_100; + cmd->duplex = DUPLEX_FULL; + printk("Warning!! Can't disable Auto negotiation in 1000Mbps, change to Manual 100Mbps, Full duplex.\n"); } -#ifdef ETHTOOL_GLINK - case ETHTOOL_GLINK:{ - struct ethtool_value link = { ETHTOOL_GLINK }; - link.data = np->link_status; - if (copy_to_user(useraddr, &link, sizeof(link))) - return -EFAULT; - return 0; - } -#endif + switch(cmd->speed + cmd->duplex) { + + case SPEED_10 + DUPLEX_HALF: + np->speed = 10; + np->full_duplex = 0; + break; + + case SPEED_10 + DUPLEX_FULL: + np->speed = 10; + np->full_duplex = 1; + break; + case SPEED_100 + DUPLEX_HALF: + np->speed = 100; + np->full_duplex = 0; + break; + case SPEED_100 + DUPLEX_FULL: + np->speed = 100; + np->full_duplex = 1; + break; + case SPEED_1000 + DUPLEX_HALF:/* not supported */ + case SPEED_1000 + DUPLEX_FULL:/* not supported */ default: - return -EOPNOTSUPP; - } + return -EINVAL; + } + mii_set_media(dev); + } + return 0; +} + +static u32 rio_get_link(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + return np->link_status; } +static struct ethtool_ops ethtool_ops = { + .get_drvinfo = rio_get_drvinfo, + .get_settings = rio_get_settings, + .set_settings = rio_set_settings, + .get_link = rio_get_link, +}; static int rio_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) { int phy_addr; - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); struct mii_data *miidata = (struct mii_data *) &rq->ifr_ifru; struct netdev_desc *desc; @@ -1332,8 +1315,6 @@ rio_ioctl (struct net_device *dev, struc phy_addr = np->phy_addr; switch (cmd) { - case SIOCETHTOOL: - return rio_ethtool_ioctl(dev, rq->ifr_data); case SIOCDEVPRIVATE: break; @@ -1490,7 +1471,7 @@ mii_wait_link (struct net_device *dev, i int phy_addr; struct netdev_private *np; - np = dev->priv; + np = netdev_priv(dev); phy_addr = np->phy_addr; do { @@ -1512,7 +1493,7 @@ mii_get_media (struct net_device *dev) int phy_addr; struct netdev_private *np; - np = dev->priv; + np = netdev_priv(dev); phy_addr = np->phy_addr; bmsr.image = mii_read (dev, phy_addr, MII_BMSR); @@ -1594,7 +1575,7 @@ mii_set_media (struct net_device *dev) ANAR_t anar; int phy_addr; struct netdev_private *np; - np = dev->priv; + np = netdev_priv(dev); phy_addr = np->phy_addr; /* Does user set speed? */ @@ -1684,7 +1665,7 @@ mii_get_media_pcs (struct net_device *de int phy_addr; struct netdev_private *np; - np = dev->priv; + np = netdev_priv(dev); phy_addr = np->phy_addr; bmsr.image = mii_read (dev, phy_addr, PCS_BMSR); @@ -1740,7 +1721,7 @@ mii_set_media_pcs (struct net_device *de ANAR_PCS_t anar; int phy_addr; struct netdev_private *np; - np = dev->priv; + np = netdev_priv(dev); phy_addr = np->phy_addr; /* Auto-Negotiation? */ @@ -1794,7 +1775,7 @@ static int rio_close (struct net_device *dev) { long ioaddr = dev->base_addr; - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); struct sk_buff *skb; int i; @@ -1840,7 +1821,7 @@ rio_remove1 (struct pci_dev *pdev) struct net_device *dev = pci_get_drvdata (pdev); if (dev) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); unregister_netdev (dev); pci_free_consistent (pdev, RX_TOTAL_SIZE, np->rx_ring, --- linux-2.6.9-rc1/drivers/net/dl2k.h 2004-08-24 00:03:32.000000000 -0700 +++ 25/drivers/net/dl2k.h 2004-09-02 20:51:52.000000000 -0700 @@ -92,7 +92,7 @@ enum dl2x_offsets { EepromCtrl = 0x4a, ExpromAddr = 0x4c, Exprodata = 0x50, - WakeEvent0x51, + WakeEvent = 0x51, CountDown = 0x54, IntStatusAck = 0x5a, IntEnable = 0x5c, --- linux-2.6.9-rc1/drivers/net/e1000/e1000_main.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/drivers/net/e1000/e1000_main.c 2004-09-02 20:51:52.000000000 -0700 @@ -128,8 +128,8 @@ static int e1000_xmit_frame(struct sk_bu static struct net_device_stats * e1000_get_stats(struct net_device *netdev); static int e1000_change_mtu(struct net_device *netdev, int new_mtu); static int e1000_set_mac(struct net_device *netdev, void *p); -static inline void e1000_irq_disable(struct e1000_adapter *adapter); -static inline void e1000_irq_enable(struct e1000_adapter *adapter); +static void e1000_irq_disable(struct e1000_adapter *adapter); +static void e1000_irq_enable(struct e1000_adapter *adapter); static irqreturn_t e1000_intr(int irq, void *data, struct pt_regs *regs); static boolean_t e1000_clean_tx_irq(struct e1000_adapter *adapter); #ifdef CONFIG_E1000_NAPI @@ -146,9 +146,9 @@ static int e1000_mii_ioctl(struct net_de void set_ethtool_ops(struct net_device *netdev); static void e1000_enter_82542_rst(struct e1000_adapter *adapter); static void e1000_leave_82542_rst(struct e1000_adapter *adapter); -static inline void e1000_rx_checksum(struct e1000_adapter *adapter, - struct e1000_rx_desc *rx_desc, - struct sk_buff *skb); +static void e1000_rx_checksum(struct e1000_adapter *adapter, + struct e1000_rx_desc *rx_desc, + struct sk_buff *skb); static void e1000_tx_timeout(struct net_device *dev); static void e1000_tx_timeout_task(struct net_device *dev); static void e1000_smartspeed(struct e1000_adapter *adapter); @@ -2077,7 +2077,7 @@ e1000_update_stats(struct e1000_adapter * @adapter: board private structure **/ -static inline void +static void e1000_irq_disable(struct e1000_adapter *adapter) { atomic_inc(&adapter->irq_sem); @@ -2091,7 +2091,7 @@ e1000_irq_disable(struct e1000_adapter * * @adapter: board private structure **/ -static inline void +static void e1000_irq_enable(struct e1000_adapter *adapter) { if(likely(atomic_dec_and_test(&adapter->irq_sem))) { @@ -2593,7 +2593,7 @@ e1000_mii_ioctl(struct net_device *netde * @sk_buff: socket buffer with received data **/ -static inline void +static void e1000_rx_checksum(struct e1000_adapter *adapter, struct e1000_rx_desc *rx_desc, struct sk_buff *skb) --- linux-2.6.9-rc1/drivers/net/e1000/e1000_param.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/drivers/net/e1000/e1000_param.c 2004-09-02 20:51:52.000000000 -0700 @@ -212,7 +212,7 @@ E1000_PARAM(InterruptThrottleRate, "Inte #define MAX_TXABSDELAY 0xFFFF #define MIN_TXABSDELAY 0 -#define DEFAULT_ITR 1 +#define DEFAULT_ITR 8000 #define MAX_ITR 100000 #define MIN_ITR 100 --- linux-2.6.9-rc1/drivers/net/eepro100.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/drivers/net/eepro100.c 2004-09-03 00:56:32.543213328 -0700 @@ -541,6 +541,7 @@ static struct net_device_stats *speedo_g static int speedo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static void set_rx_mode(struct net_device *dev); static void speedo_show_state(struct net_device *dev); +static struct ethtool_ops ethtool_ops; @@ -895,6 +896,7 @@ static int __devinit speedo_found1(struc dev->get_stats = &speedo_get_stats; dev->set_multicast_list = &set_rx_mode; dev->do_ioctl = &speedo_ioctl; + SET_ETHTOOL_OPS(dev, ðtool_ops); #ifdef CONFIG_NET_POLL_CONTROLLER dev->poll_controller = &poll_speedo; #endif @@ -2010,82 +2012,68 @@ speedo_get_stats(struct net_device *dev) return &sp->stats; } -static int netdev_ethtool_ioctl(struct net_device *dev, void __user *useraddr) +static void speedo_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - u32 ethcmd; struct speedo_private *sp = netdev_priv(dev); - - if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) - return -EFAULT; - - switch (ethcmd) { - /* get driver-specific version/etc. info */ - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; - strncpy(info.driver, "eepro100", sizeof(info.driver)-1); - strncpy(info.version, version, sizeof(info.version)-1); - if (sp && sp->pdev) - strcpy(info.bus_info, pci_name(sp->pdev)); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - - /* get settings */ - case ETHTOOL_GSET: { - struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - spin_lock_irq(&sp->lock); - mii_ethtool_gset(&sp->mii_if, &ecmd); - spin_unlock_irq(&sp->lock); - if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; - } - /* set settings */ - case ETHTOOL_SSET: { - int r; - struct ethtool_cmd ecmd; - if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) - return -EFAULT; - spin_lock_irq(&sp->lock); - r = mii_ethtool_sset(&sp->mii_if, &ecmd); - spin_unlock_irq(&sp->lock); - return r; - } - /* restart autonegotiation */ - case ETHTOOL_NWAY_RST: { - return mii_nway_restart(&sp->mii_if); - } - /* get link status */ - case ETHTOOL_GLINK: { - struct ethtool_value edata = {ETHTOOL_GLINK}; - edata.data = mii_link_ok(&sp->mii_if); - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = sp->msg_enable; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - sp->msg_enable = edata.data; - return 0; - } + strncpy(info->driver, "eepro100", sizeof(info->driver)-1); + strncpy(info->version, version, sizeof(info->version)-1); + if (sp->pdev) + strcpy(info->bus_info, pci_name(sp->pdev)); +} - } - - return -EOPNOTSUPP; +static int speedo_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct speedo_private *sp = netdev_priv(dev); + spin_lock_irq(&sp->lock); + mii_ethtool_gset(&sp->mii_if, ecmd); + spin_unlock_irq(&sp->lock); + return 0; +} + +static int speedo_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct speedo_private *sp = netdev_priv(dev); + int res; + spin_lock_irq(&sp->lock); + res = mii_ethtool_sset(&sp->mii_if, ecmd); + spin_unlock_irq(&sp->lock); + return res; +} + +static int speedo_nway_reset(struct net_device *dev) +{ + struct speedo_private *sp = netdev_priv(dev); + return mii_nway_restart(&sp->mii_if); +} + +static u32 speedo_get_link(struct net_device *dev) +{ + struct speedo_private *sp = netdev_priv(dev); + return mii_link_ok(&sp->mii_if); } +static u32 speedo_get_msglevel(struct net_device *dev) +{ + struct speedo_private *sp = netdev_priv(dev); + return sp->msg_enable; +} + +static void speedo_set_msglevel(struct net_device *dev, u32 v) +{ + struct speedo_private *sp = netdev_priv(dev); + sp->msg_enable = v; +} + +static struct ethtool_ops ethtool_ops = { + .get_drvinfo = speedo_get_drvinfo, + .get_settings = speedo_get_settings, + .set_settings = speedo_set_settings, + .nway_reset = speedo_nway_reset, + .get_link = speedo_get_link, + .get_msglevel = speedo_get_msglevel, + .set_msglevel = speedo_set_msglevel, +}; + static int speedo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { struct speedo_private *sp = netdev_priv(dev); @@ -2121,8 +2109,6 @@ static int speedo_ioctl(struct net_devic add_timer(&sp->timer); /* may be set to the past --SAW */ pci_set_power_state(sp->pdev, saved_acpi); return 0; - case SIOCETHTOOL: - return netdev_ethtool_ioctl(dev, rq->ifr_data); default: return -EOPNOTSUPP; } --- linux-2.6.9-rc1/drivers/net/ewrk3.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/net/ewrk3.c 2004-09-03 00:56:32.546212872 -0700 @@ -305,6 +305,8 @@ static int ewrk3_close(struct net_device static struct net_device_stats *ewrk3_get_stats(struct net_device *dev); static void set_multicast_list(struct net_device *dev); static int ewrk3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static struct ethtool_ops ethtool_ops_203; +static struct ethtool_ops ethtool_ops; /* ** Private functions @@ -532,7 +534,7 @@ ewrk3_hw_init(struct net_device *dev, u_ printk(" is in I/O only mode"); } - lp = (struct ewrk3_private *) dev->priv; + lp = netdev_priv(dev); lp->shmem_base = mem_start; lp->shmem_length = shmem_length; lp->lemac = lemac; @@ -610,6 +612,10 @@ ewrk3_hw_init(struct net_device *dev, u_ dev->get_stats = ewrk3_get_stats; dev->set_multicast_list = set_multicast_list; dev->do_ioctl = ewrk3_ioctl; + if (lp->adapter_name[4] == '3') + SET_ETHTOOL_OPS(dev, ðtool_ops_203); + else + SET_ETHTOOL_OPS(dev, ðtool_ops); dev->tx_timeout = ewrk3_timeout; dev->watchdog_timeo = QUEUE_PKT_TIMEOUT; @@ -621,7 +627,7 @@ ewrk3_hw_init(struct net_device *dev, u_ static int ewrk3_open(struct net_device *dev) { - struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv; + struct ewrk3_private *lp = netdev_priv(dev); u_long iobase = dev->base_addr; int i, status = 0; u_char icr, csr; @@ -684,7 +690,7 @@ static int ewrk3_open(struct net_device */ static void ewrk3_init(struct net_device *dev) { - struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv; + struct ewrk3_private *lp = netdev_priv(dev); u_char csr, page; u_long iobase = dev->base_addr; int i; @@ -725,7 +731,7 @@ static void ewrk3_init(struct net_device static void ewrk3_timeout(struct net_device *dev) { - struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv; + struct ewrk3_private *lp = netdev_priv(dev); u_char icr, csr; u_long iobase = dev->base_addr; @@ -761,7 +767,7 @@ static void ewrk3_timeout(struct net_dev */ static int ewrk3_queue_pkt (struct sk_buff *skb, struct net_device *dev) { - struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv; + struct ewrk3_private *lp = netdev_priv(dev); u_long iobase = dev->base_addr; u_long buf = 0; u_char icr; @@ -883,7 +889,7 @@ static irqreturn_t ewrk3_interrupt(int i u_long iobase; u_char icr, cr, csr; - lp = (struct ewrk3_private *) dev->priv; + lp = netdev_priv(dev); iobase = dev->base_addr; /* get the interrupt information */ @@ -931,7 +937,7 @@ static irqreturn_t ewrk3_interrupt(int i /* Called with lp->hw_lock held */ static int ewrk3_rx(struct net_device *dev) { - struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv; + struct ewrk3_private *lp = netdev_priv(dev); u_long iobase = dev->base_addr; int i, status = 0; u_char page; @@ -1059,7 +1065,7 @@ static int ewrk3_rx(struct net_device *d */ static int ewrk3_tx(struct net_device *dev) { - struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv; + struct ewrk3_private *lp = netdev_priv(dev); u_long iobase = dev->base_addr; u_char tx_status; @@ -1095,7 +1101,7 @@ static int ewrk3_tx(struct net_device *d static int ewrk3_close(struct net_device *dev) { - struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv; + struct ewrk3_private *lp = netdev_priv(dev); u_long iobase = dev->base_addr; u_char icr, csr; @@ -1130,7 +1136,7 @@ static int ewrk3_close(struct net_device static struct net_device_stats *ewrk3_get_stats(struct net_device *dev) { - struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv; + struct ewrk3_private *lp = netdev_priv(dev); /* Null body since there is no framing error counter */ return &lp->stats; @@ -1141,7 +1147,7 @@ static struct net_device_stats *ewrk3_ge */ static void set_multicast_list(struct net_device *dev) { - struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv; + struct ewrk3_private *lp = netdev_priv(dev); u_long iobase = dev->base_addr; u_char csr; @@ -1174,7 +1180,7 @@ static void set_multicast_list(struct ne */ static void SetMulticastFilter(struct net_device *dev) { - struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv; + struct ewrk3_private *lp = netdev_priv(dev); struct dev_mc_list *dmi = dev->mc_list; u_long iobase = dev->base_addr; int i; @@ -1520,187 +1526,165 @@ static int __init EISA_signature(char *n return status; /* return the device name string */ } -static int ewrk3_ethtool_ioctl(struct net_device *dev, void __user *useraddr) +static void ewrk3_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv; - u_long iobase = dev->base_addr; - u32 ethcmd; - - if (get_user(ethcmd, (u32 __user *)useraddr)) - return -EFAULT; - - switch (ethcmd) { - - /* Get driver info */ - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - int fwrev = Read_EEPROM(dev->base_addr, EEPROM_REVLVL); - - strcpy(info.driver, DRV_NAME); - strcpy(info.version, DRV_VERSION); - sprintf(info.fw_version, "%d", fwrev); - strcpy(info.bus_info, "N/A"); - info.eedump_len = EEPROM_MAX; - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - - /* Get settings */ - case ETHTOOL_GSET: { - struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - u_char cr = inb(EWRK3_CR); - - switch (lp->adapter_name[4]) { - case '3': /* DE203 */ - ecmd.supported = SUPPORTED_BNC; - ecmd.port = PORT_BNC; - break; - - case '4': /* DE204 */ - ecmd.supported = SUPPORTED_TP; - ecmd.port = PORT_TP; - break; - - case '5': /* DE205 */ - ecmd.supported = SUPPORTED_TP | SUPPORTED_BNC | SUPPORTED_AUI; - ecmd.autoneg = !(cr & CR_APD); - /* - ** Port is only valid if autoneg is disabled - ** and even then we don't know if AUI is jumpered. - */ - if (!ecmd.autoneg) - ecmd.port = (cr & CR_PSEL) ? PORT_BNC : PORT_TP; - break; - } + int fwrev = Read_EEPROM(dev->base_addr, EEPROM_REVLVL); - ecmd.supported |= SUPPORTED_10baseT_Half; - ecmd.speed = SPEED_10; - ecmd.duplex = DUPLEX_HALF; - - if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; - } - - /* Set settings */ - case ETHTOOL_SSET: { - struct ethtool_cmd ecmd; - u_char cr; - u_long flags; - - /* DE205 is the only card with anything to set */ - if (lp->adapter_name[4] != '5') - return -EOPNOTSUPP; - - if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) - return -EFAULT; - - /* Sanity-check parameters */ - if (ecmd.speed != SPEED_10) - return -EINVAL; - if (ecmd.port != PORT_TP && ecmd.port != PORT_BNC) - return -EINVAL; /* AUI is not software-selectable */ - if (ecmd.transceiver != XCVR_INTERNAL) - return -EINVAL; - if (ecmd.duplex != DUPLEX_HALF) - return -EINVAL; - if (ecmd.phy_address != 0) - return -EINVAL; + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + sprintf(info->fw_version, "%d", fwrev); + strcpy(info->bus_info, "N/A"); + info->eedump_len = EEPROM_MAX; +} - spin_lock_irqsave(&lp->hw_lock, flags); - cr = inb(EWRK3_CR); +static int ewrk3_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct ewrk3_private *lp = netdev_priv(dev); + unsigned long iobase = dev->base_addr; + u8 cr = inb(EWRK3_CR); - /* If Autoneg is set, change to Auto Port mode */ - /* Otherwise, disable Auto Port and set port explicitly */ - if (ecmd.autoneg) { - cr &= ~CR_APD; - } else { - cr |= CR_APD; - if (ecmd.port == PORT_TP) - cr &= ~CR_PSEL; /* Force TP */ - else - cr |= CR_PSEL; /* Force BNC */ - } + switch (lp->adapter_name[4]) { + case '3': /* DE203 */ + ecmd->supported = SUPPORTED_BNC; + ecmd->port = PORT_BNC; + break; - /* Commit the changes */ - outb(cr, EWRK3_CR); + case '4': /* DE204 */ + ecmd->supported = SUPPORTED_TP; + ecmd->port = PORT_TP; + break; - spin_unlock_irqrestore(&lp->hw_lock, flags); - if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; + case '5': /* DE205 */ + ecmd->supported = SUPPORTED_TP | SUPPORTED_BNC | SUPPORTED_AUI; + ecmd->autoneg = !(cr & CR_APD); + /* + ** Port is only valid if autoneg is disabled + ** and even then we don't know if AUI is jumpered. + */ + if (!ecmd->autoneg) + ecmd->port = (cr & CR_PSEL) ? PORT_BNC : PORT_TP; + break; } - /* Get link status */ - case ETHTOOL_GLINK: { - struct ethtool_value edata = { ETHTOOL_GLINK }; - u_char cmr = inb(EWRK3_CMR); + ecmd->supported |= SUPPORTED_10baseT_Half; + ecmd->speed = SPEED_10; + ecmd->duplex = DUPLEX_HALF; + return 0; +} - /* DE203 has BNC only and link status does not apply */ - if (lp->adapter_name[4] == '3') - return -EOPNOTSUPP; +static int ewrk3_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct ewrk3_private *lp = netdev_priv(dev); + unsigned long iobase = dev->base_addr; + unsigned long flags; + u8 cr; - /* On DE204 this is always valid since TP is the only port. */ - /* On DE205 this reflects TP status even if BNC or AUI is selected. */ - edata.data = !(cmr & CMR_LINK); + /* DE205 is the only card with anything to set */ + if (lp->adapter_name[4] != '5') + return -EOPNOTSUPP; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } + /* Sanity-check parameters */ + if (ecmd->speed != SPEED_10) + return -EINVAL; + if (ecmd->port != PORT_TP && ecmd->port != PORT_BNC) + return -EINVAL; /* AUI is not software-selectable */ + if (ecmd->transceiver != XCVR_INTERNAL) + return -EINVAL; + if (ecmd->duplex != DUPLEX_HALF) + return -EINVAL; + if (ecmd->phy_address != 0) + return -EINVAL; - /* Blink LED for identification */ - case ETHTOOL_PHYS_ID: { - struct ethtool_value edata; - u_long flags; - u_char cr; - int count; + spin_lock_irqsave(&lp->hw_lock, flags); + cr = inb(EWRK3_CR); - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; + /* If Autoneg is set, change to Auto Port mode */ + /* Otherwise, disable Auto Port and set port explicitly */ + if (ecmd->autoneg) { + cr &= ~CR_APD; + } else { + cr |= CR_APD; + if (ecmd->port == PORT_TP) + cr &= ~CR_PSEL; /* Force TP */ + else + cr |= CR_PSEL; /* Force BNC */ + } - /* Toggle LED 4x per second */ - count = edata.data << 2; + /* Commit the changes */ + outb(cr, EWRK3_CR); + spin_unlock_irqrestore(&lp->hw_lock, flags); + return 0; +} - spin_lock_irqsave(&lp->hw_lock, flags); +static u32 ewrk3_get_link(struct net_device *dev) +{ + unsigned long iobase = dev->base_addr; + u8 cmr = inb(EWRK3_CMR); + /* DE203 has BNC only and link status does not apply */ + /* On DE204 this is always valid since TP is the only port. */ + /* On DE205 this reflects TP status even if BNC or AUI is selected. */ + return !(cmr & CMR_LINK); +} - /* Bail if a PHYS_ID is already in progress */ - if (lp->led_mask == 0) { - spin_unlock_irqrestore(&lp->hw_lock, flags); - return -EBUSY; - } +static int ewrk3_phys_id(struct net_device *dev, u32 data) +{ + struct ewrk3_private *lp = netdev_priv(dev); + unsigned long iobase = dev->base_addr; + unsigned long flags; + u8 cr; + int count; - /* Prevent ISR from twiddling the LED */ - lp->led_mask = 0; + /* Toggle LED 4x per second */ + count = data << 2; - while (count--) { - /* Toggle the LED */ - cr = inb(EWRK3_CR); - outb(cr ^ CR_LED, EWRK3_CR); + spin_lock_irqsave(&lp->hw_lock, flags); - /* Wait a little while */ - spin_unlock_irqrestore(&lp->hw_lock, flags); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ>>2); - spin_lock_irqsave(&lp->hw_lock, flags); + /* Bail if a PHYS_ID is already in progress */ + if (lp->led_mask == 0) { + spin_unlock_irqrestore(&lp->hw_lock, flags); + return -EBUSY; + } - /* Exit if we got a signal */ - if (signal_pending(current)) - break; - } + /* Prevent ISR from twiddling the LED */ + lp->led_mask = 0; - lp->led_mask = CR_LED; + while (count--) { + /* Toggle the LED */ cr = inb(EWRK3_CR); - outb(cr & ~CR_LED, EWRK3_CR); + outb(cr ^ CR_LED, EWRK3_CR); + + /* Wait a little while */ spin_unlock_irqrestore(&lp->hw_lock, flags); - return signal_pending(current) ? -ERESTARTSYS : 0; - } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ>>2); + spin_lock_irqsave(&lp->hw_lock, flags); + /* Exit if we got a signal */ + if (signal_pending(current)) + break; } - return -EOPNOTSUPP; -} + lp->led_mask = CR_LED; + cr = inb(EWRK3_CR); + outb(cr & ~CR_LED, EWRK3_CR); + spin_unlock_irqrestore(&lp->hw_lock, flags); + return signal_pending(current) ? -ERESTARTSYS : 0; +} + +static struct ethtool_ops ethtool_ops_203 = { + .get_drvinfo = ewrk3_get_drvinfo, + .get_settings = ewrk3_get_settings, + .set_settings = ewrk3_set_settings, + .phys_id = ewrk3_phys_id, +}; + +static struct ethtool_ops ethtool_ops = { + .get_drvinfo = ewrk3_get_drvinfo, + .get_settings = ewrk3_get_settings, + .set_settings = ewrk3_set_settings, + .get_link = ewrk3_get_link, + .phys_id = ewrk3_phys_id, +}; /* ** Perform IOCTL call functions here. Some are privileged operations and the @@ -1708,7 +1692,7 @@ static int ewrk3_ethtool_ioctl(struct ne */ static int ewrk3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { - struct ewrk3_private *lp = (struct ewrk3_private *) dev->priv; + struct ewrk3_private *lp = netdev_priv(dev); struct ewrk3_ioctl *ioc = (struct ewrk3_ioctl *) &rq->ifr_ifru; u_long iobase = dev->base_addr; int i, j, status = 0; @@ -1721,11 +1705,7 @@ static int ewrk3_ioctl(struct net_device union ewrk3_addr *tmp; - /* ethtool IOCTLs are handled elsewhere */ - if (cmd == SIOCETHTOOL) - return ewrk3_ethtool_ioctl(dev, rq->ifr_data); - - /* Other than ethtool, all we handle are private IOCTLs */ + /* All we handle are private IOCTLs */ if (cmd != EWRK3IOCTL) return -EOPNOTSUPP; --- linux-2.6.9-rc1/drivers/net/forcedeth.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/drivers/net/forcedeth.c 2004-09-03 00:56:32.549212416 -0700 @@ -740,91 +740,50 @@ static struct net_device_stats *nv_get_s return &np->stats; } -static int nv_ethtool_ioctl(struct net_device *dev, void __user *useraddr) +static void nv_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct fe_priv *np = get_nvpriv(dev); - u8 *base = get_hwbase(dev); - u32 ethcmd; - - if (copy_from_user(ðcmd, useraddr, sizeof (ethcmd))) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: - { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy(info.driver, "forcedeth"); - strcpy(info.version, FORCEDETH_VERSION); - strcpy(info.bus_info, pci_name(np->pci_dev)); - if (copy_to_user(useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } - case ETHTOOL_GLINK: - { - struct ethtool_value edata = { ETHTOOL_GLINK }; - - edata.data = !!netif_carrier_ok(dev); - - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - case ETHTOOL_GWOL: - { - struct ethtool_wolinfo wolinfo; - memset(&wolinfo, 0, sizeof(wolinfo)); - wolinfo.supported = WAKE_MAGIC; - - spin_lock_irq(&np->lock); - if (np->wolenabled) - wolinfo.wolopts = WAKE_MAGIC; - spin_unlock_irq(&np->lock); - - if (copy_to_user(useraddr, &wolinfo, sizeof(wolinfo))) - return -EFAULT; - return 0; - } - case ETHTOOL_SWOL: - { - struct ethtool_wolinfo wolinfo; - if (copy_from_user(&wolinfo, useraddr, sizeof(wolinfo))) - return -EFAULT; - - spin_lock_irq(&np->lock); - if (wolinfo.wolopts == 0) { - writel(0, base + NvRegWakeUpFlags); - np->wolenabled = 0; - } - if (wolinfo.wolopts & WAKE_MAGIC) { - writel(NVREG_WAKEUPFLAGS_ENABLE, base + NvRegWakeUpFlags); - np->wolenabled = 1; - } - spin_unlock_irq(&np->lock); - return 0; - } + strcpy(info->driver, "forcedeth"); + strcpy(info->version, FORCEDETH_VERSION); + strcpy(info->bus_info, pci_name(np->pci_dev)); +} - default: - break; - } +static void nv_get_wol(struct net_device *dev, struct ethtool_wolinfo *wolinfo) +{ + struct fe_priv *np = get_nvpriv(dev); + wolinfo->supported = WAKE_MAGIC; - return -EOPNOTSUPP; + spin_lock_irq(&np->lock); + if (np->wolenabled) + wolinfo->wolopts = WAKE_MAGIC; + spin_unlock_irq(&np->lock); } -/* - * nv_ioctl: dev->do_ioctl function - * Called with rtnl_lock held. - */ -static int nv_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) + +static int nv_set_wol(struct net_device *dev, struct ethtool_wolinfo *wolinfo) { - switch(cmd) { - case SIOCETHTOOL: - return nv_ethtool_ioctl(dev, rq->ifr_data); + struct fe_priv *np = get_nvpriv(dev); + u8 *base = get_hwbase(dev); - default: - return -EOPNOTSUPP; + spin_lock_irq(&np->lock); + if (wolinfo->wolopts == 0) { + writel(0, base + NvRegWakeUpFlags); + np->wolenabled = 0; + } + if (wolinfo->wolopts & WAKE_MAGIC) { + writel(NVREG_WAKEUPFLAGS_ENABLE, base + NvRegWakeUpFlags); + np->wolenabled = 1; } + spin_unlock_irq(&np->lock); + return 0; } +static struct ethtool_ops ops = { + .get_drvinfo = nv_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_wol = nv_get_wol, + .set_wol = nv_set_wol, +}; + /* * nv_alloc_rx: fill rx ring entries. * Return 1 if the allocations for the skbs failed and the @@ -1759,7 +1718,7 @@ static int __devinit nv_probe(struct pci dev->get_stats = nv_get_stats; dev->change_mtu = nv_change_mtu; dev->set_multicast_list = nv_set_multicast; - dev->do_ioctl = nv_ioctl; + SET_ETHTOOL_OPS(dev, &ops); dev->tx_timeout = nv_tx_timeout; dev->watchdog_timeo = NV_WATCHDOG_TIMEO; --- linux-2.6.9-rc1/drivers/net/gianfar.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/drivers/net/gianfar.c 2004-09-02 20:51:52.000000000 -0700 @@ -307,8 +307,8 @@ static int gfar_probe(struct ocp_device /* Print out the device info */ printk(KERN_INFO DEVICE_NAME, dev->name); for (idx = 0; idx < 6; idx++) - printk(KERN_INFO "%2.2x%c", dev->dev_addr[idx], idx == 5 ? ' ' : ':'); - printk(KERN_INFO "\n"); + printk("%2.2x%c", dev->dev_addr[idx], idx == 5 ? ' ' : ':'); + printk("\n"); /* Even more device info helps when determining which kernel */ /* provided which set of benchmarks. Since this is global for all */ --- linux-2.6.9-rc1/drivers/net/hamachi.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/net/hamachi.c 2004-09-03 00:56:32.552211960 -0700 @@ -559,13 +559,14 @@ static void hamachi_tx_timeout(struct ne static void hamachi_init_ring(struct net_device *dev); static int hamachi_start_xmit(struct sk_buff *skb, struct net_device *dev); static irqreturn_t hamachi_interrupt(int irq, void *dev_instance, struct pt_regs *regs); -static inline int hamachi_rx(struct net_device *dev); +static int hamachi_rx(struct net_device *dev); static inline int hamachi_tx(struct net_device *dev); static void hamachi_error(struct net_device *dev, int intr_status); static int hamachi_close(struct net_device *dev); static struct net_device_stats *hamachi_get_stats(struct net_device *dev); static void set_rx_mode(struct net_device *dev); - +static struct ethtool_ops ethtool_ops; +static struct ethtool_ops ethtool_ops_no_mii; static int __devinit hamachi_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) @@ -631,7 +632,7 @@ static int __devinit hamachi_init_one (s read_eeprom(ioaddr, i), i % 16 != 15 ? " " : "\n"); #endif - hmp = dev->priv; + hmp = netdev_priv(dev); spin_lock_init(&hmp->lock); hmp->mii_if.dev = dev; @@ -725,6 +726,10 @@ static int __devinit hamachi_init_one (s dev->get_stats = &hamachi_get_stats; dev->set_multicast_list = &set_rx_mode; dev->do_ioctl = &netdev_ioctl; + if (chip_tbl[hmp->chip_id].flags & CanHaveMII) + SET_ETHTOOL_OPS(dev, ðtool_ops); + else + SET_ETHTOOL_OPS(dev, ðtool_ops_no_mii); dev->tx_timeout = &hamachi_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; if (mtu) @@ -851,7 +856,7 @@ static void mdio_write(struct net_device static int hamachi_open(struct net_device *dev) { - struct hamachi_private *hmp = dev->priv; + struct hamachi_private *hmp = netdev_priv(dev); long ioaddr = dev->base_addr; int i; u32 rx_int_var, tx_int_var; @@ -1000,7 +1005,7 @@ static int hamachi_open(struct net_devic static inline int hamachi_tx(struct net_device *dev) { - struct hamachi_private *hmp = dev->priv; + struct hamachi_private *hmp = netdev_priv(dev); /* Update the dirty pointer until we find an entry that is still owned by the card */ @@ -1032,7 +1037,7 @@ static inline int hamachi_tx(struct net_ static void hamachi_timer(unsigned long data) { struct net_device *dev = (struct net_device *)data; - struct hamachi_private *hmp = dev->priv; + struct hamachi_private *hmp = netdev_priv(dev); long ioaddr = dev->base_addr; int next_tick = 10*HZ; @@ -1057,7 +1062,7 @@ static void hamachi_timer(unsigned long static void hamachi_tx_timeout(struct net_device *dev) { int i; - struct hamachi_private *hmp = dev->priv; + struct hamachi_private *hmp = netdev_priv(dev); long ioaddr = dev->base_addr; printk(KERN_WARNING "%s: Hamachi transmit timed out, status %8.8x," @@ -1163,7 +1168,7 @@ static void hamachi_tx_timeout(struct ne /* Initialize the Rx and Tx rings, along with various 'dev' bits. */ static void hamachi_init_ring(struct net_device *dev) { - struct hamachi_private *hmp = dev->priv; + struct hamachi_private *hmp = netdev_priv(dev); int i; hmp->tx_full = 0; @@ -1255,7 +1260,7 @@ do { \ static int hamachi_start_xmit(struct sk_buff *skb, struct net_device *dev) { - struct hamachi_private *hmp = dev->priv; + struct hamachi_private *hmp = netdev_priv(dev); unsigned entry; u16 status; @@ -1383,7 +1388,7 @@ static irqreturn_t hamachi_interrupt(int #endif ioaddr = dev->base_addr; - hmp = dev->priv; + hmp = netdev_priv(dev); spin_lock(&hmp->lock); do { @@ -1477,7 +1482,7 @@ static irqreturn_t hamachi_interrupt(int for clarity and better register allocation. */ static int hamachi_rx(struct net_device *dev) { - struct hamachi_private *hmp = dev->priv; + struct hamachi_private *hmp = netdev_priv(dev); int entry = hmp->cur_rx % RX_RING_SIZE; int boguscnt = (hmp->dirty_rx + RX_RING_SIZE) - hmp->cur_rx; @@ -1693,7 +1698,7 @@ static int hamachi_rx(struct net_device static void hamachi_error(struct net_device *dev, int intr_status) { long ioaddr = dev->base_addr; - struct hamachi_private *hmp = dev->priv; + struct hamachi_private *hmp = netdev_priv(dev); if (intr_status & (LinkChange|NegotiationChange)) { if (hamachi_debug > 1) @@ -1727,7 +1732,7 @@ static void hamachi_error(struct net_dev static int hamachi_close(struct net_device *dev) { long ioaddr = dev->base_addr; - struct hamachi_private *hmp = dev->priv; + struct hamachi_private *hmp = netdev_priv(dev); struct sk_buff *skb; int i; @@ -1813,7 +1818,7 @@ static int hamachi_close(struct net_devi static struct net_device_stats *hamachi_get_stats(struct net_device *dev) { long ioaddr = dev->base_addr; - struct hamachi_private *hmp = dev->priv; + struct hamachi_private *hmp = netdev_priv(dev); /* We should lock this segment of code for SMP eventually, although the vulnerability window is very small and statistics are @@ -1867,84 +1872,76 @@ static void set_rx_mode(struct net_devic } } -static int netdev_ethtool_ioctl(struct net_device *dev, void __user *useraddr) +static int check_if_running(struct net_device *dev) { - struct hamachi_private *np = dev->priv; - u32 ethcmd; - - if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) - return -EFAULT; + if (!netif_running(dev)) + return -EINVAL; + return 0; +} - switch (ethcmd) { - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; - strcpy(info.driver, DRV_NAME); - strcpy(info.version, DRV_VERSION); - strcpy(info.bus_info, pci_name(np->pci_dev)); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } +static void hamachi_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + struct hamachi_private *np = netdev_priv(dev); + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->bus_info, pci_name(np->pci_dev)); +} - /* get settings */ - case ETHTOOL_GSET: { - struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - if (!(chip_tbl[np->chip_id].flags & CanHaveMII)) - return -EINVAL; - spin_lock_irq(&np->lock); - mii_ethtool_gset(&np->mii_if, &ecmd); - spin_unlock_irq(&np->lock); - if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; - } - /* set settings */ - case ETHTOOL_SSET: { - int r; - struct ethtool_cmd ecmd; - if (!(chip_tbl[np->chip_id].flags & CanHaveMII)) - return -EINVAL; - if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) - return -EFAULT; - spin_lock_irq(&np->lock); - r = mii_ethtool_sset(&np->mii_if, &ecmd); - spin_unlock_irq(&np->lock); - return r; - } - /* restart autonegotiation */ - case ETHTOOL_NWAY_RST: { - if (!(chip_tbl[np->chip_id].flags & CanHaveMII)) - return -EINVAL; - return mii_nway_restart(&np->mii_if); - } - /* get link status */ - case ETHTOOL_GLINK: { - struct ethtool_value edata = {ETHTOOL_GLINK}; - if (!(chip_tbl[np->chip_id].flags & CanHaveMII)) - return -EINVAL; - edata.data = mii_link_ok(&np->mii_if); - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - } - - return -EOPNOTSUPP; +static int hamachi_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct hamachi_private *np = netdev_priv(dev); + spin_lock_irq(&np->lock); + mii_ethtool_gset(&np->mii_if, ecmd); + spin_unlock_irq(&np->lock); + return 0; +} + +static int hamachi_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct hamachi_private *np = netdev_priv(dev); + int res; + spin_lock_irq(&np->lock); + res = mii_ethtool_sset(&np->mii_if, ecmd); + spin_unlock_irq(&np->lock); + return res; +} + +static int hamachi_nway_reset(struct net_device *dev) +{ + struct hamachi_private *np = netdev_priv(dev); + return mii_nway_restart(&np->mii_if); } +static u32 hamachi_get_link(struct net_device *dev) +{ + struct hamachi_private *np = netdev_priv(dev); + return mii_link_ok(&np->mii_if); +} + +static struct ethtool_ops ethtool_ops = { + .begin = check_if_running, + .get_drvinfo = hamachi_get_drvinfo, + .get_settings = hamachi_get_settings, + .set_settings = hamachi_set_settings, + .nway_reset = hamachi_nway_reset, + .get_link = hamachi_get_link, +}; + +static struct ethtool_ops ethtool_ops_no_mii = { + .begin = check_if_running, + .get_drvinfo = hamachi_get_drvinfo, +}; + static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { - struct hamachi_private *np = dev->priv; + struct hamachi_private *np = netdev_priv(dev); struct mii_ioctl_data *data = if_mii(rq); int rc; if (!netif_running(dev)) return -EINVAL; - if (cmd == SIOCETHTOOL) - rc = netdev_ethtool_ioctl(dev, rq->ifr_data); - - else if (cmd == (SIOCDEVPRIVATE+3)) { /* set rx,tx intr params */ + if (cmd == (SIOCDEVPRIVATE+3)) { /* set rx,tx intr params */ u32 *d = (u32 *)&rq->ifr_ifru; /* Should add this check here or an ordinary user can do nasty * things. -KDU @@ -1976,7 +1973,7 @@ static void __devexit hamachi_remove_one struct net_device *dev = pci_get_drvdata(pdev); if (dev) { - struct hamachi_private *hmp = dev->priv; + struct hamachi_private *hmp = netdev_priv(dev); pci_free_consistent(pdev, RX_TOTAL_SIZE, hmp->rx_ring, hmp->rx_ring_dma); --- linux-2.6.9-rc1/drivers/net/hamradio/dmascc.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/drivers/net/hamradio/dmascc.c 2004-09-02 20:51:52.000000000 -0700 @@ -246,8 +246,14 @@ static int scc_send_packet(struct sk_buf static struct net_device_stats *scc_get_stats(struct net_device *dev); static int scc_set_mac_address(struct net_device *dev, void *sa); -static irqreturn_t scc_isr(int irq, void *dev_id, struct pt_regs * regs); +static inline void tx_on(struct scc_priv *priv); +static inline void rx_on(struct scc_priv *priv); +static inline void rx_off(struct scc_priv *priv); +static void start_timer(struct scc_priv *priv, int t, int r15); +static inline unsigned char random(void); + static inline void z8530_isr(struct scc_info *info); +static irqreturn_t scc_isr(int irq, void *dev_id, struct pt_regs * regs); static void rx_isr(struct scc_priv *priv); static void special_condition(struct scc_priv *priv, int rc); static void rx_bh(void *arg); @@ -255,12 +261,6 @@ static void tx_isr(struct scc_priv *priv static void es_isr(struct scc_priv *priv); static void tm_isr(struct scc_priv *priv); -static inline void tx_on(struct scc_priv *priv); -static inline void rx_on(struct scc_priv *priv); -static inline void rx_off(struct scc_priv *priv); -static void start_timer(struct scc_priv *priv, int t, int r15); -static inline unsigned char random(void); - /* Initialization variables */ @@ -945,42 +945,115 @@ static int scc_set_mac_address(struct ne } -static irqreturn_t scc_isr(int irq, void *dev_id, struct pt_regs * regs) { - struct scc_info *info = dev_id; +static inline void tx_on(struct scc_priv *priv) { + int i, n; + unsigned long flags; - spin_lock(info->priv[0].register_lock); - /* At this point interrupts are enabled, and the interrupt under service - is already acknowledged, but masked off. + if (priv->param.dma >= 0) { + n = (priv->chip == Z85230) ? 3 : 1; + /* Program DMA controller */ + flags = claim_dma_lock(); + set_dma_mode(priv->param.dma, DMA_MODE_WRITE); + set_dma_addr(priv->param.dma, (int) priv->tx_buf[priv->tx_tail]+n); + set_dma_count(priv->param.dma, priv->tx_len[priv->tx_tail]-n); + release_dma_lock(flags); + /* Enable TX underrun interrupt */ + write_scc(priv, R15, TxUIE); + /* Configure DREQ */ + if (priv->type == TYPE_TWIN) + outb((priv->param.dma == 1) ? TWIN_DMA_HDX_T1 : TWIN_DMA_HDX_T3, + priv->card_base + TWIN_DMA_CFG); + else + write_scc(priv, R1, EXT_INT_ENAB | WT_FN_RDYFN | WT_RDY_ENAB); + /* Write first byte(s) */ + spin_lock_irqsave(priv->register_lock, flags); + for (i = 0; i < n; i++) + write_scc_data(priv, priv->tx_buf[priv->tx_tail][i], 1); + enable_dma(priv->param.dma); + spin_unlock_irqrestore(priv->register_lock, flags); + } else { + write_scc(priv, R15, TxUIE); + write_scc(priv, R1, EXT_INT_ENAB | WT_FN_RDYFN | TxINT_ENAB); + tx_isr(priv); + } + /* Reset EOM latch if we do not have the AUTOEOM feature */ + if (priv->chip == Z8530) write_scc(priv, R0, RES_EOM_L); +} - Interrupt processing: We loop until we know that the IRQ line is - low. If another positive edge occurs afterwards during the ISR, - another interrupt will be triggered by the interrupt controller - as soon as the IRQ level is enabled again (see asm/irq.h). - Bottom-half handlers will be processed after scc_isr(). This is - important, since we only have small ringbuffers and want new data - to be fetched/delivered immediately. */ +static inline void rx_on(struct scc_priv *priv) { + unsigned long flags; - if (info->priv[0].type == TYPE_TWIN) { - int is, card_base = info->priv[0].card_base; - while ((is = ~inb(card_base + TWIN_INT_REG)) & - TWIN_INT_MSK) { - if (is & TWIN_SCC_MSK) { - z8530_isr(info); - } else if (is & TWIN_TMR1_MSK) { - inb(card_base + TWIN_CLR_TMR1); - tm_isr(&info->priv[0]); - } else { - inb(card_base + TWIN_CLR_TMR2); - tm_isr(&info->priv[1]); - } + /* Clear RX FIFO */ + while (read_scc(priv, R0) & Rx_CH_AV) read_scc_data(priv); + priv->rx_over = 0; + if (priv->param.dma >= 0) { + /* Program DMA controller */ + flags = claim_dma_lock(); + set_dma_mode(priv->param.dma, DMA_MODE_READ); + set_dma_addr(priv->param.dma, (int) priv->rx_buf[priv->rx_head]); + set_dma_count(priv->param.dma, BUF_SIZE); + release_dma_lock(flags); + enable_dma(priv->param.dma); + /* Configure PackeTwin DMA */ + if (priv->type == TYPE_TWIN) { + outb((priv->param.dma == 1) ? TWIN_DMA_HDX_R1 : TWIN_DMA_HDX_R3, + priv->card_base + TWIN_DMA_CFG); } - } else z8530_isr(info); - spin_unlock(info->priv[0].register_lock); - return IRQ_HANDLED; + /* Sp. cond. intr. only, ext int enable, RX DMA enable */ + write_scc(priv, R1, EXT_INT_ENAB | INT_ERR_Rx | + WT_RDY_RT | WT_FN_RDYFN | WT_RDY_ENAB); + } else { + /* Reset current frame */ + priv->rx_ptr = 0; + /* Intr. on all Rx characters and Sp. cond., ext int enable */ + write_scc(priv, R1, EXT_INT_ENAB | INT_ALL_Rx | WT_RDY_RT | + WT_FN_RDYFN); + } + write_scc(priv, R0, ERR_RES); + write_scc(priv, R3, RxENABLE | Rx8 | RxCRC_ENAB); +} + + +static inline void rx_off(struct scc_priv *priv) { + /* Disable receiver */ + write_scc(priv, R3, Rx8); + /* Disable DREQ / RX interrupt */ + if (priv->param.dma >= 0 && priv->type == TYPE_TWIN) + outb(0, priv->card_base + TWIN_DMA_CFG); + else + write_scc(priv, R1, EXT_INT_ENAB | WT_FN_RDYFN); + /* Disable DMA */ + if (priv->param.dma >= 0) disable_dma(priv->param.dma); +} + + +static void start_timer(struct scc_priv *priv, int t, int r15) { + unsigned long flags; + + outb(priv->tmr_mode, priv->tmr_ctrl); + if (t == 0) { + tm_isr(priv); + } else if (t > 0) { + save_flags(flags); + cli(); + outb(t & 0xFF, priv->tmr_cnt); + outb((t >> 8) & 0xFF, priv->tmr_cnt); + if (priv->type != TYPE_TWIN) { + write_scc(priv, R15, r15 | CTSIE); + priv->rr0 |= CTS; + } + restore_flags(flags); + } } +static inline unsigned char random(void) { + /* See "Numerical Recipes in C", second edition, p. 284 */ + rand = rand * 1664525L + 1013904223L; + return (unsigned char) (rand >> 24); +} + static inline void z8530_isr(struct scc_info *info) { int is, i = 100; @@ -1009,6 +1082,42 @@ static inline void z8530_isr(struct scc_ } +static irqreturn_t scc_isr(int irq, void *dev_id, struct pt_regs * regs) { + struct scc_info *info = dev_id; + + spin_lock(info->priv[0].register_lock); + /* At this point interrupts are enabled, and the interrupt under service + is already acknowledged, but masked off. + + Interrupt processing: We loop until we know that the IRQ line is + low. If another positive edge occurs afterwards during the ISR, + another interrupt will be triggered by the interrupt controller + as soon as the IRQ level is enabled again (see asm/irq.h). + + Bottom-half handlers will be processed after scc_isr(). This is + important, since we only have small ringbuffers and want new data + to be fetched/delivered immediately. */ + + if (info->priv[0].type == TYPE_TWIN) { + int is, card_base = info->priv[0].card_base; + while ((is = ~inb(card_base + TWIN_INT_REG)) & + TWIN_INT_MSK) { + if (is & TWIN_SCC_MSK) { + z8530_isr(info); + } else if (is & TWIN_TMR1_MSK) { + inb(card_base + TWIN_CLR_TMR1); + tm_isr(&info->priv[0]); + } else { + inb(card_base + TWIN_CLR_TMR2); + tm_isr(&info->priv[1]); + } + } + } else z8530_isr(info); + spin_unlock(info->priv[0].register_lock); + return IRQ_HANDLED; +} + + static void rx_isr(struct scc_priv *priv) { if (priv->param.dma >= 0) { /* Check special condition and perform error reset. See 2.4.7.5. */ @@ -1292,114 +1401,3 @@ static void tm_isr(struct scc_priv *priv break; } } - - -static inline void tx_on(struct scc_priv *priv) { - int i, n; - unsigned long flags; - - if (priv->param.dma >= 0) { - n = (priv->chip == Z85230) ? 3 : 1; - /* Program DMA controller */ - flags = claim_dma_lock(); - set_dma_mode(priv->param.dma, DMA_MODE_WRITE); - set_dma_addr(priv->param.dma, (int) priv->tx_buf[priv->tx_tail]+n); - set_dma_count(priv->param.dma, priv->tx_len[priv->tx_tail]-n); - release_dma_lock(flags); - /* Enable TX underrun interrupt */ - write_scc(priv, R15, TxUIE); - /* Configure DREQ */ - if (priv->type == TYPE_TWIN) - outb((priv->param.dma == 1) ? TWIN_DMA_HDX_T1 : TWIN_DMA_HDX_T3, - priv->card_base + TWIN_DMA_CFG); - else - write_scc(priv, R1, EXT_INT_ENAB | WT_FN_RDYFN | WT_RDY_ENAB); - /* Write first byte(s) */ - spin_lock_irqsave(priv->register_lock, flags); - for (i = 0; i < n; i++) - write_scc_data(priv, priv->tx_buf[priv->tx_tail][i], 1); - enable_dma(priv->param.dma); - spin_unlock_irqrestore(priv->register_lock, flags); - } else { - write_scc(priv, R15, TxUIE); - write_scc(priv, R1, EXT_INT_ENAB | WT_FN_RDYFN | TxINT_ENAB); - tx_isr(priv); - } - /* Reset EOM latch if we do not have the AUTOEOM feature */ - if (priv->chip == Z8530) write_scc(priv, R0, RES_EOM_L); -} - - -static inline void rx_on(struct scc_priv *priv) { - unsigned long flags; - - /* Clear RX FIFO */ - while (read_scc(priv, R0) & Rx_CH_AV) read_scc_data(priv); - priv->rx_over = 0; - if (priv->param.dma >= 0) { - /* Program DMA controller */ - flags = claim_dma_lock(); - set_dma_mode(priv->param.dma, DMA_MODE_READ); - set_dma_addr(priv->param.dma, (int) priv->rx_buf[priv->rx_head]); - set_dma_count(priv->param.dma, BUF_SIZE); - release_dma_lock(flags); - enable_dma(priv->param.dma); - /* Configure PackeTwin DMA */ - if (priv->type == TYPE_TWIN) { - outb((priv->param.dma == 1) ? TWIN_DMA_HDX_R1 : TWIN_DMA_HDX_R3, - priv->card_base + TWIN_DMA_CFG); - } - /* Sp. cond. intr. only, ext int enable, RX DMA enable */ - write_scc(priv, R1, EXT_INT_ENAB | INT_ERR_Rx | - WT_RDY_RT | WT_FN_RDYFN | WT_RDY_ENAB); - } else { - /* Reset current frame */ - priv->rx_ptr = 0; - /* Intr. on all Rx characters and Sp. cond., ext int enable */ - write_scc(priv, R1, EXT_INT_ENAB | INT_ALL_Rx | WT_RDY_RT | - WT_FN_RDYFN); - } - write_scc(priv, R0, ERR_RES); - write_scc(priv, R3, RxENABLE | Rx8 | RxCRC_ENAB); -} - - -static inline void rx_off(struct scc_priv *priv) { - /* Disable receiver */ - write_scc(priv, R3, Rx8); - /* Disable DREQ / RX interrupt */ - if (priv->param.dma >= 0 && priv->type == TYPE_TWIN) - outb(0, priv->card_base + TWIN_DMA_CFG); - else - write_scc(priv, R1, EXT_INT_ENAB | WT_FN_RDYFN); - /* Disable DMA */ - if (priv->param.dma >= 0) disable_dma(priv->param.dma); -} - - -static void start_timer(struct scc_priv *priv, int t, int r15) { - unsigned long flags; - - outb(priv->tmr_mode, priv->tmr_ctrl); - if (t == 0) { - tm_isr(priv); - } else if (t > 0) { - save_flags(flags); - cli(); - outb(t & 0xFF, priv->tmr_cnt); - outb((t >> 8) & 0xFF, priv->tmr_cnt); - if (priv->type != TYPE_TWIN) { - write_scc(priv, R15, r15 | CTSIE); - priv->rr0 |= CTS; - } - restore_flags(flags); - } -} - - -static inline unsigned char random(void) { - /* See "Numerical Recipes in C", second edition, p. 284 */ - rand = rand * 1664525L + 1013904223L; - return (unsigned char) (rand >> 24); -} - --- linux-2.6.9-rc1/drivers/net/hp100.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/drivers/net/hp100.c 2004-09-02 20:51:52.000000000 -0700 @@ -2906,13 +2906,19 @@ static struct eisa_driver hp100_eisa_dri static int __devinit hp100_pci_probe (struct pci_dev *pdev, const struct pci_device_id *ent) { - struct net_device *dev = alloc_etherdev(sizeof(struct hp100_private)); - int ioaddr = pci_resource_start(pdev, 0); + struct net_device *dev; + int ioaddr; u_short pci_command; int err; - - if (!dev) - return -ENOMEM; + + if (pci_enable_device(pdev)) + return -ENODEV; + + dev = alloc_etherdev(sizeof(struct hp100_private)); + if (!dev) { + err = -ENOMEM; + goto out0; + } SET_MODULE_OWNER(dev); SET_NETDEV_DEV(dev, &pdev->dev); @@ -2934,7 +2940,7 @@ static int __devinit hp100_pci_probe (st pci_write_config_word(pdev, PCI_COMMAND, pci_command); } - + ioaddr = pci_resource_start(pdev, 0); err = hp100_probe1(dev, ioaddr, HP100_BUS_PCI, pdev); if (err) goto out1; @@ -2951,6 +2957,8 @@ static int __devinit hp100_pci_probe (st release_region(dev->base_addr, HP100_REGION_SIZE); out1: free_netdev(dev); + out0: + pci_disable_device(pdev); return err; } @@ -2959,6 +2967,7 @@ static void __devexit hp100_pci_remove ( struct net_device *dev = pci_get_drvdata(pdev); cleanup_dev(dev); + pci_disable_device(pdev); } --- linux-2.6.9-rc1/drivers/net/ibm_emac/ibm_emac_core.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/drivers/net/ibm_emac/ibm_emac_core.c 2004-09-02 20:51:52.000000000 -0700 @@ -90,23 +90,24 @@ MODULE_PARM_DESC(skb_res, "Amount of dat #define RGMII_PRIV(ocpdev) ((struct ibm_ocp_rgmii*)ocp_get_drvdata(ocpdev)) -static unsigned int rgmii_enable[] = - { RGMII_RTBI, RGMII_RGMII, RGMII_TBI, RGMII_GMII }; +static unsigned int rgmii_enable[] = { + RGMII_RTBI, + RGMII_RGMII, + RGMII_TBI, + RGMII_GMII +}; -static unsigned int rgmii_speed_mask[] = { 0, - 0, +static unsigned int rgmii_speed_mask[] = { RGMII_MII2_SPDMASK, RGMII_MII3_SPDMASK }; -static unsigned int rgmii_speed100[] = { 0, - 0, +static unsigned int rgmii_speed100[] = { RGMII_MII2_100MB, RGMII_MII3_100MB }; -static unsigned int rgmii_speed1000[] = { 0, - 0, +static unsigned int rgmii_speed1000[] = { RGMII_MII2_1000MB, RGMII_MII3_1000MB }; @@ -122,11 +123,21 @@ static unsigned int zmii_enable[][4] = { ~(ZMII_MDI0 | ZMII_MDI1 | ZMII_MDI3)}, {ZMII_SMII3, ZMII_RMII3, ZMII_MII3, ~(ZMII_MDI0 | ZMII_MDI1 | ZMII_MDI2)} }; -static unsigned int mdi_enable[] = - { ZMII_MDI0, ZMII_MDI1, ZMII_MDI2, ZMII_MDI3 }; + +static unsigned int mdi_enable[] = { + ZMII_MDI0, + ZMII_MDI1, + ZMII_MDI2, + ZMII_MDI3 +}; static unsigned int zmii_speed = 0x0; -static unsigned int zmii_speed100[] = { ZMII_MII0_100MB, ZMII_MII1_100MB }; +static unsigned int zmii_speed100[] = { + ZMII_MII0_100MB, + ZMII_MII1_100MB, + ZMII_MII2_100MB, + ZMII_MII3_100MB +}; /* Since multiple EMACs share MDIO lines in various ways, we need * to avoid re-using the same PHY ID in cases where the arch didn't @@ -367,6 +378,7 @@ static void emac_close_zmii(struct ocp_d int emac_phy_read(struct net_device *dev, int mii_id, int reg) { + int count; uint32_t stacr; struct ocp_enet_private *fep = dev->priv; emac_t *emacp = fep->emacp; @@ -385,9 +397,13 @@ int emac_phy_read(struct net_device *dev emacp = fep->emacp; } - udelay(MDIO_DELAY); + count = 0; + while ((((stacr = in_be32(&emacp->em0stacr)) & EMAC_STACR_OC) == 0) + && (count++ < MDIO_DELAY)) + udelay(1); + MDIO_DEBUG((" (count was %d)\n", count)); - if ((in_be32(&emacp->em0stacr) & EMAC_STACR_OC) == 0) { + if ((stacr & EMAC_STACR_OC) == 0) { printk(KERN_WARNING "%s: PHY read timeout #1!\n", dev->name); return -1; } @@ -398,8 +414,11 @@ int emac_phy_read(struct net_device *dev out_be32(&emacp->em0stacr, stacr); - udelay(MDIO_DELAY); - stacr = in_be32(&emacp->em0stacr); + count = 0; + while ((((stacr = in_be32(&emacp->em0stacr)) & EMAC_STACR_OC) == 0) + && (count++ < MDIO_DELAY)) + udelay(1); + MDIO_DEBUG((" (count was %d)\n", count)); if ((stacr & EMAC_STACR_OC) == 0) { printk(KERN_WARNING "%s: PHY read timeout #2!\n", dev->name); @@ -419,6 +438,7 @@ int emac_phy_read(struct net_device *dev void emac_phy_write(struct net_device *dev, int mii_id, int reg, int data) { + int count; uint32_t stacr; struct ocp_enet_private *fep = dev->priv; emac_t *emacp = fep->emacp; @@ -437,9 +457,13 @@ void emac_phy_write(struct net_device *d emacp = fep->emacp; } - udelay(MDIO_DELAY); + count = 0; + while ((((stacr = in_be32(&emacp->em0stacr)) & EMAC_STACR_OC) == 0) + && (count++ < MDIO_DELAY)) + udelay(1); + MDIO_DEBUG((" (count was %d)\n", count)); - if ((in_be32(&emacp->em0stacr) & EMAC_STACR_OC) == 0) { + if ((stacr & EMAC_STACR_OC) == 0) { printk(KERN_WARNING "%s: PHY write timeout #2!\n", dev->name); return; } @@ -451,9 +475,12 @@ void emac_phy_write(struct net_device *d out_be32(&emacp->em0stacr, stacr); - udelay(MDIO_DELAY); + while (((stacr = in_be32(&emacp->em0stacr) & EMAC_STACR_OC) == 0) + && (count++ < 5000)) + udelay(1); + MDIO_DEBUG((" (count was %d)\n", count)); - if ((in_be32(&emacp->em0stacr) & EMAC_STACR_OC) == 0) + if ((stacr & EMAC_STACR_OC) == 0) printk(KERN_WARNING "%s: PHY write timeout #2!\n", dev->name); /* Check for a write error */ @@ -1940,8 +1967,6 @@ static struct ocp_driver emac_driver = { static int __init emac_init(void) { - int rc; - printk(KERN_INFO DRV_NAME ": " DRV_DESC ", version " DRV_VERSION "\n"); printk(KERN_INFO "Maintained by " DRV_AUTHOR "\n"); @@ -1950,13 +1975,8 @@ static int __init emac_init(void) skb_res); skb_res = 2; } - rc = ocp_register_driver(&emac_driver); - if (rc < 0) { - ocp_unregister_driver(&emac_driver); - return -ENODEV; - } - return 0; + return ocp_register_driver(&emac_driver); } static void __exit emac_exit(void) --- linux-2.6.9-rc1/drivers/net/ibm_emac/ibm_emac_core.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/net/ibm_emac/ibm_emac_core.h 2004-09-02 20:51:52.000000000 -0700 @@ -67,7 +67,7 @@ #define TX_TIMEOUT (2*HZ) /* MDIO latency delay */ -#define MDIO_DELAY 50 +#define MDIO_DELAY 250 /* Power managment shift registers */ #define IBM_CPM_EMMII 0 /* Shift value for MII */ --- linux-2.6.9-rc1/drivers/net/ibm_emac/ibm_emac.h 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/net/ibm_emac/ibm_emac.h 2004-09-02 20:51:52.000000000 -0700 @@ -228,6 +228,21 @@ typedef struct emac_regs { (desc & EMAC_BAD_RX_PACKET) #endif +/* SoC implementation specific EMAC register defaults */ +#if defined(CONFIG_440GP) +#define EMAC_RWMR_DEFAULT 0x80009000 +#define EMAC_TMR0_DEFAULT 0x00000000 +#define EMAC_TMR1_DEFAULT 0xf8640000 +#elif defined(CONFIG_440GX) +#define EMAC_RWMR_DEFAULT 0x1000a200 +#define EMAC_TMR0_DEFAULT EMAC_TMR0_TFAE_2_32 +#define EMAC_TMR1_DEFAULT 0xa00f0000 +#else +#define EMAC_RWMR_DEFAULT 0x0f002000 +#define EMAC_TMR0_DEFAULT 0x00000000 +#define EMAC_TMR1_DEFAULT 0x380f0000 +#endif /* CONFIG_440GP */ + /* Revision specific EMAC register defaults */ #ifdef CONFIG_IBM_EMAC4 #define EMAC_M1_DEFAULT (EMAC_M1_BASE | \ @@ -236,7 +251,7 @@ typedef struct emac_regs { #define EMAC_RMR_DEFAULT (EMAC_RMR_BASE | \ EMAC_RMR_RFAF_128_2048) #define EMAC_TMR0_XMIT (EMAC_TMR0_GNP0 | \ - EMAC_TMR0_TFAE_128_2048) + EMAC_TMR0_DEFAULT) #define EMAC_TRTR_DEFAULT EMAC_TRTR_1024 #else /* !CONFIG_IBM_EMAC4 */ #define EMAC_M1_DEFAULT EMAC_M1_BASE @@ -245,19 +260,4 @@ typedef struct emac_regs { #define EMAC_TRTR_DEFAULT EMAC_TRTR_1600 #endif /* CONFIG_IBM_EMAC4 */ -/* SoC implementation specific EMAC register defaults */ -#if defined(CONFIG_440GP) -#define EMAC_RWMR_DEFAULT 0x80009000 -#define EMAC_TMR0_DEFAULT 0x00000000 -#define EMAC_TMR1_DEFAULT 0xf8640000 -#elif defined(CONFIG_440GX) -#define EMAC_RWMR_DEFAULT 0x1000a200 -#define EMAC_TMR0_DEFAULT EMAC_TMR0_TFAE_128_2048 -#define EMAC_TMR1_DEFAULT 0x88810000 -#else -#define EMAC_RWMR_DEFAULT 0x0f002000 -#define EMAC_TMR0_DEFAULT 0x00000000 -#define EMAC_TMR1_DEFAULT 0x380f0000 -#endif /* CONFIG_440GP */ - #endif --- linux-2.6.9-rc1/drivers/net/ibmlana.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/net/ibmlana.c 2004-09-03 00:56:32.553211808 -0700 @@ -885,14 +885,6 @@ static struct net_device_stats *ibmlana_ return &priv->stat; } -/* we don't support runtime reconfiguration, since am MCA card can - be unambigously identified by its POS registers. */ - -static int ibmlana_config(struct net_device *dev, struct ifmap *map) -{ - return 0; -} - /* switch receiver mode. */ static void ibmlana_set_multicast_list(struct net_device *dev) @@ -984,7 +976,6 @@ static int ibmlana_probe(struct net_devi dev->open = ibmlana_open; dev->stop = ibmlana_close; - dev->set_config = ibmlana_config; dev->hard_start_xmit = ibmlana_tx; dev->do_ioctl = NULL; dev->get_stats = ibmlana_stats; --- linux-2.6.9-rc1/drivers/net/irda/irda-usb.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/net/irda/irda-usb.c 2004-09-02 20:51:52.000000000 -0700 @@ -268,7 +268,6 @@ static void irda_usb_change_speed_xbofs( speed_bulk_callback, self); urb->transfer_buffer_length = USB_IRDA_HEADER; urb->transfer_flags = URB_ASYNC_UNLINK; - urb->timeout = msecs_to_jiffies(100); /* Irq disabled -> GFP_ATOMIC */ if ((ret = usb_submit_urb(urb, GFP_ATOMIC))) { @@ -411,8 +410,6 @@ static int irda_usb_hard_xmit(struct sk_ * after each of our packets that is exact multiple of the frame size. * This is how the dongle will detect the end of packet - Jean II */ urb->transfer_flags |= URB_ZERO_PACKET; - /* Timeout need to be shorter than NET watchdog timer */ - urb->timeout = msecs_to_jiffies(200); /* Generate min turn time. FIXME: can we do better than this? */ /* Trying to a turnaround time at this level is trying to measure --- linux-2.6.9-rc1/drivers/net/iseries_veth.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/net/iseries_veth.c 2004-09-03 00:56:32.554211656 -0700 @@ -747,60 +747,41 @@ static void veth_set_multicast_list(stru write_unlock_irqrestore(&port->mcast_gate, flags); } -static int veth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +static void veth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { -#ifdef SIOCETHTOOL - struct ethtool_cmd ecmd; - - if (cmd != SIOCETHTOOL) - return -EOPNOTSUPP; - if (copy_from_user(&ecmd, ifr->ifr_data, sizeof (ecmd))) - return -EFAULT; - switch (ecmd.cmd) { - case ETHTOOL_GSET: - ecmd.supported = (SUPPORTED_1000baseT_Full - | SUPPORTED_Autoneg | SUPPORTED_FIBRE); - ecmd.advertising = (SUPPORTED_1000baseT_Full - | SUPPORTED_Autoneg | SUPPORTED_FIBRE); - - ecmd.port = PORT_FIBRE; - ecmd.transceiver = XCVR_INTERNAL; - ecmd.phy_address = 0; - ecmd.speed = SPEED_1000; - ecmd.duplex = DUPLEX_FULL; - ecmd.autoneg = AUTONEG_ENABLE; - ecmd.maxtxpkt = 120; - ecmd.maxrxpkt = 120; - if (copy_to_user(ifr->ifr_data, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; - - case ETHTOOL_GDRVINFO:{ - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strncpy(info.driver, "veth", sizeof(info.driver) - 1); - info.driver[sizeof(info.driver) - 1] = '\0'; - strncpy(info.version, "1.0", sizeof(info.version) - 1); - if (copy_to_user(ifr->ifr_data, &info, sizeof(info))) - return -EFAULT; - return 0; - } - /* get link status */ - case ETHTOOL_GLINK:{ - struct ethtool_value edata = { ETHTOOL_GLINK }; - edata.data = 1; - if (copy_to_user(ifr->ifr_data, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - - default: - break; - } + strncpy(info->driver, "veth", sizeof(info->driver) - 1); + info->driver[sizeof(info->driver) - 1] = '\0'; + strncpy(info->version, "1.0", sizeof(info->version) - 1); +} + +static int veth_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + ecmd->supported = (SUPPORTED_1000baseT_Full + | SUPPORTED_Autoneg | SUPPORTED_FIBRE); + ecmd->advertising = (SUPPORTED_1000baseT_Full + | SUPPORTED_Autoneg | SUPPORTED_FIBRE); + ecmd->port = PORT_FIBRE; + ecmd->transceiver = XCVR_INTERNAL; + ecmd->phy_address = 0; + ecmd->speed = SPEED_1000; + ecmd->duplex = DUPLEX_FULL; + ecmd->autoneg = AUTONEG_ENABLE; + ecmd->maxtxpkt = 120; + ecmd->maxrxpkt = 120; + return 0; +} -#endif - return -EOPNOTSUPP; +static u32 veth_get_link(struct net_device *dev) +{ + return 1; } +static struct ethtool_ops ops = { + .get_drvinfo = veth_get_drvinfo, + .get_settings = veth_get_settings, + .get_link = veth_get_link, +}; + static void veth_tx_timeout(struct net_device *dev) { struct veth_port *port = (struct veth_port *)dev->priv; @@ -889,7 +870,7 @@ static struct net_device * __init veth_p dev->change_mtu = veth_change_mtu; dev->set_mac_address = NULL; dev->set_multicast_list = veth_set_multicast_list; - dev->do_ioctl = veth_ioctl; + SET_ETHTOOL_OPS(dev, &ops); dev->watchdog_timeo = 2 * (VETH_ACKTIMEOUT * HZ / 1000000); dev->tx_timeout = veth_tx_timeout; --- linux-2.6.9-rc1/drivers/net/ixgb/ixgb_ethtool.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/net/ixgb/ixgb_ethtool.c 2004-09-03 00:56:32.557211200 -0700 @@ -38,12 +38,6 @@ extern char ixgb_driver_version[]; extern int ixgb_up(struct ixgb_adapter *adapter); extern void ixgb_down(struct ixgb_adapter *adapter, boolean_t kill_watchdog); -static inline int ixgb_eeprom_size(struct ixgb_hw *hw) -{ - /* return size in bytes */ - return (IXGB_EEPROM_SIZE << 1); -} - struct ixgb_stats { char stat_string[ETH_GSTRING_LEN]; int sizeof_stat; @@ -94,9 +88,10 @@ static struct ixgb_stats ixgb_gstrings_s #define IXGB_STATS_LEN \ sizeof(ixgb_gstrings_stats) / sizeof(struct ixgb_stats) -static void -ixgb_ethtool_gset(struct ixgb_adapter *adapter, struct ethtool_cmd *ecmd) +static int +ixgb_ethtool_gset(struct net_device *netdev, struct ethtool_cmd *ecmd) { + struct ixgb_adapter *adapter = netdev->priv; ecmd->supported = (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE); ecmd->advertising = (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE); ecmd->port = PORT_FIBRE; @@ -111,11 +106,13 @@ ixgb_ethtool_gset(struct ixgb_adapter *a } ecmd->autoneg = AUTONEG_DISABLE; + return 0; } static int -ixgb_ethtool_sset(struct ixgb_adapter *adapter, struct ethtool_cmd *ecmd) +ixgb_ethtool_sset(struct net_device *netdev, struct ethtool_cmd *ecmd) { + struct ixgb_adapter *adapter = netdev->priv; if (ecmd->autoneg == AUTONEG_ENABLE || ecmd->speed + ecmd->duplex != SPEED_10000 + DUPLEX_FULL) return -EINVAL; @@ -123,14 +120,14 @@ ixgb_ethtool_sset(struct ixgb_adapter *a ixgb_down(adapter, TRUE); ixgb_up(adapter); } - return 0; } -static int -ixgb_ethtool_gpause(struct ixgb_adapter *adapter, +static void +ixgb_ethtool_gpause(struct net_device *dev, struct ethtool_pauseparam *epause) { + struct ixgb_adapter *adapter = dev->priv; struct ixgb_hw *hw = &adapter->hw; epause->autoneg = AUTONEG_DISABLE; @@ -143,14 +140,13 @@ ixgb_ethtool_gpause(struct ixgb_adapter epause->rx_pause = 1; epause->tx_pause = 1; } - - return 0; } static int -ixgb_ethtool_spause(struct ixgb_adapter *adapter, +ixgb_ethtool_spause(struct net_device *dev, struct ethtool_pauseparam *epause) { + struct ixgb_adapter *adapter = dev->priv; struct ixgb_hw *hw = &adapter->hw; if (epause->autoneg == AUTONEG_ENABLE) @@ -172,26 +168,23 @@ ixgb_ethtool_spause(struct ixgb_adapter } static void -ixgb_ethtool_gdrvinfo(struct ixgb_adapter *adapter, +ixgb_ethtool_gdrvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { + struct ixgb_adapter *adapter = netdev->priv; strncpy(drvinfo->driver, ixgb_driver_name, 32); strncpy(drvinfo->version, ixgb_driver_version, 32); strncpy(drvinfo->fw_version, "N/A", 32); strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32); - drvinfo->n_stats = IXGB_STATS_LEN; -#define IXGB_REG_DUMP_LEN 136*sizeof(uint32_t) - drvinfo->regdump_len = IXGB_REG_DUMP_LEN; - drvinfo->eedump_len = ixgb_eeprom_size(&adapter->hw); } #define IXGB_GET_STAT(_A_, _R_) _A_->stats._R_ static void -ixgb_ethtool_gregs(struct ixgb_adapter *adapter, - struct ethtool_regs *regs, uint32_t * regs_buff) +ixgb_ethtool_gregs(struct net_device *dev, struct ethtool_regs *regs, void *buf) { + struct ixgb_adapter *adapter = dev->priv; struct ixgb_hw *hw = &adapter->hw; - uint32_t *reg = regs_buff; + uint32_t *reg = buf; uint32_t *reg_start = reg; uint8_t i; @@ -323,69 +316,37 @@ ixgb_ethtool_gregs(struct ixgb_adapter * } static int -ixgb_ethtool_geeprom(struct ixgb_adapter *adapter, - struct ethtool_eeprom *eeprom, uint16_t * eeprom_buff) +ixgb_ethtool_geeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 *data) { + struct ixgb_adapter *adapter = dev->priv; struct ixgb_hw *hw = &adapter->hw; - int i, max_len, first_word, last_word; - int ret_val = 0; - - if (eeprom->len == 0) { - ret_val = -EINVAL; - goto geeprom_error; - } eeprom->magic = hw->vendor_id | (hw->device_id << 16); - max_len = ixgb_eeprom_size(hw); - /* use our function to read the eeprom and update our cache */ ixgb_get_eeprom_data(hw); - - if (eeprom->offset > eeprom->offset + eeprom->len) { - ret_val = -EINVAL; - goto geeprom_error; - } - - if ((eeprom->offset + eeprom->len) > max_len) - eeprom->len = (max_len - eeprom->offset); - - first_word = eeprom->offset >> 1; - last_word = (eeprom->offset + eeprom->len - 1) >> 1; - - for (i = 0; i <= (last_word - first_word); i++) { - eeprom_buff[i] = hw->eeprom[first_word + i]; - } - geeprom_error: - return ret_val; + memcpy(data, (char *)hw->eeprom + eeprom->offset, eeprom->len); + return 0; } static int -ixgb_ethtool_seeprom(struct ixgb_adapter *adapter, - struct ethtool_eeprom *eeprom, void __user *user_data) +ixgb_ethtool_seeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 *data) { + struct ixgb_adapter *adapter = dev->priv; struct ixgb_hw *hw = &adapter->hw; - uint16_t eeprom_buff[256]; - int i, max_len, first_word, last_word; - void *ptr; + /* We are under rtnl, so static is OK */ + static uint16_t eeprom_buff[IXGB_EEPROM_SIZE]; + int i, first_word, last_word; + char *ptr; if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16))) return -EFAULT; - if (eeprom->len == 0) - return -EINVAL; - - max_len = ixgb_eeprom_size(hw); - - if (eeprom->offset > eeprom->offset + eeprom->len) - return -EINVAL; - - if ((eeprom->offset + eeprom->len) > max_len) - eeprom->len = (max_len - eeprom->offset); - first_word = eeprom->offset >> 1; last_word = (eeprom->offset + eeprom->len - 1) >> 1; - ptr = (void *)eeprom_buff; + ptr = (char *)eeprom_buff; if (eeprom->offset & 1) { /* need read/modify/write of first changed EEPROM word */ @@ -399,8 +360,7 @@ ixgb_ethtool_seeprom(struct ixgb_adapter eeprom_buff[last_word - first_word] = ixgb_read_eeprom(hw, last_word); } - if (copy_from_user(ptr, user_data, eeprom->len)) - return -EFAULT; + memcpy(ptr, data, eeprom->len); for (i = 0; i <= (last_word - first_word); i++) ixgb_write_eeprom(hw, first_word + i, eeprom_buff[i]); @@ -431,8 +391,9 @@ static void ixgb_led_blink_callback(unsi } static int -ixgb_ethtool_led_blink(struct ixgb_adapter *adapter, struct ethtool_value *id) +ixgb_ethtool_led_blink(struct net_device *netdev, u32 data) { + struct ixgb_adapter *adapter = netdev->priv; if (!adapter->blink_timer.function) { init_timer(&adapter->blink_timer); adapter->blink_timer.function = ixgb_led_blink_callback; @@ -442,8 +403,8 @@ ixgb_ethtool_led_blink(struct ixgb_adapt mod_timer(&adapter->blink_timer, jiffies); set_current_state(TASK_INTERRUPTIBLE); - if (id->data) - schedule_timeout(id->data * HZ); + if (data) + schedule_timeout(data * HZ); else schedule_timeout(MAX_SCHEDULE_TIMEOUT); @@ -454,268 +415,141 @@ ixgb_ethtool_led_blink(struct ixgb_adapt return 0; } -int ixgb_ethtool_ioctl(struct net_device *netdev, struct ifreq *ifr) +static int ixgb_nway_reset(struct net_device *netdev) { - struct ixgb_adapter *adapter = netdev->priv; - void __user *addr = ifr->ifr_data; - uint32_t cmd; + if (netif_running(netdev)) { + struct ixgb_adapter *adapter = netdev->priv; + ixgb_down(adapter, TRUE); + ixgb_up(adapter); + } + return 0; +} - if (get_user(cmd, (uint32_t __user *) addr)) - return -EFAULT; +static int ixgb_get_stats_count(struct net_device *dev) +{ + return IXGB_STATS_LEN; +} - switch (cmd) { - case ETHTOOL_GSET:{ - struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - ixgb_ethtool_gset(adapter, &ecmd); - if (copy_to_user(addr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; - } - case ETHTOOL_SSET:{ - struct ethtool_cmd ecmd; - if (copy_from_user(&ecmd, addr, sizeof(ecmd))) - return -EFAULT; - return ixgb_ethtool_sset(adapter, &ecmd); - } - case ETHTOOL_GDRVINFO:{ - struct ethtool_drvinfo drvinfo = { ETHTOOL_GDRVINFO }; - ixgb_ethtool_gdrvinfo(adapter, &drvinfo); - if (copy_to_user(addr, &drvinfo, sizeof(drvinfo))) - return -EFAULT; - return 0; - } - case ETHTOOL_GSTRINGS:{ - struct ethtool_gstrings gstrings = { ETHTOOL_GSTRINGS }; - char *strings = NULL; - int err = 0; - - if (copy_from_user(&gstrings, addr, sizeof(gstrings))) - return -EFAULT; - switch (gstrings.string_set) { - case ETH_SS_STATS:{ - int i; - gstrings.len = IXGB_STATS_LEN; - strings = - kmalloc(IXGB_STATS_LEN * - ETH_GSTRING_LEN, - GFP_KERNEL); - if (!strings) - return -ENOMEM; - for (i = 0; i < IXGB_STATS_LEN; i++) { - memcpy(&strings - [i * ETH_GSTRING_LEN], - ixgb_gstrings_stats[i]. - stat_string, - ETH_GSTRING_LEN); - } - break; - } - default: - return -EOPNOTSUPP; - } - if (copy_to_user(addr, &gstrings, sizeof(gstrings))) - err = -EFAULT; - addr += offsetof(struct ethtool_gstrings, data); - if (!err && copy_to_user(addr, strings, - gstrings.len * - ETH_GSTRING_LEN)) - err = -EFAULT; - - kfree(strings); - return err; - } - case ETHTOOL_GREGS:{ - struct ethtool_regs regs = { ETHTOOL_GREGS }; - uint32_t regs_buff[IXGB_REG_DUMP_LEN]; - - if (copy_from_user(®s, addr, sizeof(regs))) - return -EFAULT; - ixgb_ethtool_gregs(adapter, ®s, regs_buff); - if (copy_to_user(addr, ®s, sizeof(regs))) - return -EFAULT; - - addr += offsetof(struct ethtool_regs, data); - if (copy_to_user(addr, regs_buff, regs.len)) - return -EFAULT; - - return 0; - } - case ETHTOOL_NWAY_RST:{ - if (netif_running(netdev)) { - ixgb_down(adapter, TRUE); - ixgb_up(adapter); - } - return 0; - } - case ETHTOOL_PHYS_ID:{ - struct ethtool_value id; - if (copy_from_user(&id, addr, sizeof(id))) - return -EFAULT; - return ixgb_ethtool_led_blink(adapter, &id); - } - case ETHTOOL_GLINK:{ - struct ethtool_value link = { ETHTOOL_GLINK }; - link.data = netif_carrier_ok(netdev); - if (copy_to_user(addr, &link, sizeof(link))) - return -EFAULT; - return 0; - } - - case ETHTOOL_GEEPROM:{ - struct ethtool_eeprom eeprom = { ETHTOOL_GEEPROM }; - uint16_t eeprom_buff[IXGB_EEPROM_SIZE]; - void *ptr; - int err = 0; - - if (copy_from_user(&eeprom, addr, sizeof(eeprom))) - return -EFAULT; - - if ((err = - ixgb_ethtool_geeprom(adapter, &eeprom, - eeprom_buff)) < 0) - return err; - - if (copy_to_user(addr, &eeprom, sizeof(eeprom))) - return -EFAULT; - - addr += offsetof(struct ethtool_eeprom, data); - ptr = ((void *)eeprom_buff) + (eeprom.offset & 1); - - if (copy_to_user(addr, ptr, eeprom.len)) - return -EFAULT; - return 0; - } - case ETHTOOL_SEEPROM:{ - struct ethtool_eeprom eeprom; - - if (copy_from_user(&eeprom, addr, sizeof(eeprom))) - return -EFAULT; - - addr += offsetof(struct ethtool_eeprom, data); - return ixgb_ethtool_seeprom(adapter, &eeprom, addr); - } - case ETHTOOL_GPAUSEPARAM:{ - struct ethtool_pauseparam epause = - { ETHTOOL_GPAUSEPARAM }; - ixgb_ethtool_gpause(adapter, &epause); - if (copy_to_user(addr, &epause, sizeof(epause))) - return -EFAULT; - return 0; - } - case ETHTOOL_SPAUSEPARAM:{ - struct ethtool_pauseparam epause; - if (copy_from_user(&epause, addr, sizeof(epause))) - return -EFAULT; - return ixgb_ethtool_spause(adapter, &epause); - } - case ETHTOOL_GSTATS:{ - struct { - struct ethtool_stats eth_stats; - uint64_t data[IXGB_STATS_LEN]; - } stats = { { - ETHTOOL_GSTATS, IXGB_STATS_LEN}}; - int i; - - for (i = 0; i < IXGB_STATS_LEN; i++) - stats.data[i] = - (ixgb_gstrings_stats[i].sizeof_stat == - sizeof(uint64_t)) ? *(uint64_t *) ((char *) - adapter - + - ixgb_gstrings_stats - [i]. - stat_offset) - : *(uint32_t *) ((char *)adapter + - ixgb_gstrings_stats[i]. - stat_offset); - if (copy_to_user(addr, &stats, sizeof(stats))) - return -EFAULT; - return 0; - } - case ETHTOOL_GRXCSUM:{ - struct ethtool_value edata = { ETHTOOL_GRXCSUM }; - - edata.data = adapter->rx_csum; - if (copy_to_user(addr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - case ETHTOOL_SRXCSUM:{ - struct ethtool_value edata; - - if (copy_from_user(&edata, addr, sizeof(edata))) - return -EFAULT; - adapter->rx_csum = edata.data; - ixgb_down(adapter, TRUE); - ixgb_up(adapter); - return 0; - } - case ETHTOOL_GTXCSUM:{ - struct ethtool_value edata = { ETHTOOL_GTXCSUM }; - - edata.data = (netdev->features & NETIF_F_HW_CSUM) != 0; - if (copy_to_user(addr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - case ETHTOOL_STXCSUM:{ - struct ethtool_value edata; - - if (copy_from_user(&edata, addr, sizeof(edata))) - return -EFAULT; - - if (edata.data) - netdev->features |= NETIF_F_HW_CSUM; - else - netdev->features &= ~NETIF_F_HW_CSUM; - - return 0; - } - case ETHTOOL_GSG:{ - struct ethtool_value edata = { ETHTOOL_GSG }; - - edata.data = (netdev->features & NETIF_F_SG) != 0; - if (copy_to_user(addr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - case ETHTOOL_SSG:{ - struct ethtool_value edata; - - if (copy_from_user(&edata, addr, sizeof(edata))) - return -EFAULT; - - if (edata.data) - netdev->features |= NETIF_F_SG; - else - netdev->features &= ~NETIF_F_SG; +static void ixgb_get_strings(struct net_device *dev, u32 stringset, u8 *data) +{ + int i; + for (i = 0; i < IXGB_STATS_LEN; i++) { + memcpy(data + i * ETH_GSTRING_LEN, + ixgb_gstrings_stats[i].stat_string, + ETH_GSTRING_LEN); + } +} - return 0; - } -#ifdef NETIF_F_TSO - case ETHTOOL_GTSO:{ - struct ethtool_value edata = { ETHTOOL_GTSO }; +static int ixgb_get_regs_len(struct net_device *dev) +{ + return 136*sizeof(uint32_t); +} + +static int ixgb_get_eeprom_len(struct net_device *dev) +{ + /* return size in bytes */ + return (IXGB_EEPROM_SIZE << 1); +} - edata.data = (netdev->features & NETIF_F_TSO) != 0; - if (copy_to_user(addr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - case ETHTOOL_STSO:{ - struct ethtool_value edata; - - if (copy_from_user(&edata, addr, sizeof(edata))) - return -EFAULT; - - if (edata.data) - netdev->features |= NETIF_F_TSO; - else - netdev->features &= ~NETIF_F_TSO; +static void get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct ixgb_adapter *adapter = dev->priv; + int i; - return 0; - } -#endif - default: - return -EOPNOTSUPP; + for (i = 0; i < IXGB_STATS_LEN; i++) { + void *p = (char *)adapter + ixgb_gstrings_stats[i].stat_offset; + stats->data[i] = + (ixgb_gstrings_stats[i].sizeof_stat == sizeof(uint64_t)) + ? *(uint64_t *) p + : *(uint32_t *) p; } } + +static u32 ixgb_get_rx_csum(struct net_device *dev) +{ + struct ixgb_adapter *adapter = dev->priv; + return adapter->rx_csum; +} + +static int ixgb_set_rx_csum(struct net_device *dev, u32 sum) +{ + struct ixgb_adapter *adapter = dev->priv; + adapter->rx_csum = sum; + ixgb_down(adapter, TRUE); + ixgb_up(adapter); + return 0; +} + +static u32 ixgb_get_tx_csum(struct net_device *dev) +{ + return (dev->features & NETIF_F_HW_CSUM) != 0; +} + +static int ixgb_set_tx_csum(struct net_device *dev, u32 sum) +{ + if (sum) + dev->features |= NETIF_F_HW_CSUM; + else + dev->features &= ~NETIF_F_HW_CSUM; + return 0; +} + +static u32 ixgb_get_sg(struct net_device *dev) +{ + return (dev->features & NETIF_F_SG) != 0; +} + +static int ixgb_set_sg(struct net_device *dev, u32 sum) +{ + if (sum) + dev->features |= NETIF_F_SG; + else + dev->features &= ~NETIF_F_SG; + return 0; +} + +#ifdef NETIF_F_TSO +static u32 ixgb_get_tso(struct net_device *dev) +{ + return (dev->features & NETIF_F_TSO) != 0; +} + +static int ixgb_set_tso(struct net_device *dev, u32 sum) +{ + if (sum) + dev->features |= NETIF_F_TSO; + else + dev->features &= ~NETIF_F_TSO; + return 0; +} +#endif + +struct ethtool_ops ixgb_ethtool_ops = { + .get_settings = ixgb_ethtool_gset, + .set_settings = ixgb_ethtool_sset, + .get_drvinfo = ixgb_ethtool_gdrvinfo, + .nway_reset = ixgb_nway_reset, + .get_link = ethtool_op_get_link, + .phys_id = ixgb_ethtool_led_blink, + .get_strings = ixgb_get_strings, + .get_stats_count = ixgb_get_stats_count, + .get_regs = ixgb_ethtool_gregs, + .get_regs_len = ixgb_get_regs_len, + .get_eeprom_len = ixgb_get_eeprom_len, + .get_eeprom = ixgb_ethtool_geeprom, + .set_eeprom = ixgb_ethtool_seeprom, + .get_pauseparam = ixgb_ethtool_gpause, + .set_pauseparam = ixgb_ethtool_spause, + .get_ethtool_stats = get_ethtool_stats, + .get_rx_csum = ixgb_get_rx_csum, + .set_rx_csum = ixgb_set_rx_csum, + .get_tx_csum = ixgb_get_tx_csum, + .set_tx_csum = ixgb_set_tx_csum, + .get_sg = ixgb_get_sg, + .set_sg = ixgb_set_sg, +#ifdef NETIF_F_TSO + .get_tso = ixgb_get_tso, + .set_tso = ixgb_set_tso, +#endif +}; --- linux-2.6.9-rc1/drivers/net/ixgb/ixgb_main.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/net/ixgb/ixgb_main.c 2004-09-03 00:56:53.381045496 -0700 @@ -55,6 +55,8 @@ MODULE_DEVICE_TABLE(pci, ixgb_pci_tbl); /* Local Function Prototypes */ +static inline void ixgb_irq_disable(struct ixgb_adapter *adapter); +static inline void ixgb_irq_enable(struct ixgb_adapter *adapter); int ixgb_up(struct ixgb_adapter *adapter); void ixgb_down(struct ixgb_adapter *adapter, boolean_t kill_watchdog); void ixgb_reset(struct ixgb_adapter *adapter); @@ -82,10 +84,11 @@ static struct net_device_stats *ixgb_get static int ixgb_change_mtu(struct net_device *netdev, int new_mtu); static int ixgb_set_mac(struct net_device *netdev, void *p); static void ixgb_update_stats(struct ixgb_adapter *adapter); -static inline void ixgb_irq_disable(struct ixgb_adapter *adapter); -static inline void ixgb_irq_enable(struct ixgb_adapter *adapter); static irqreturn_t ixgb_intr(int irq, void *data, struct pt_regs *regs); static boolean_t ixgb_clean_tx_irq(struct ixgb_adapter *adapter); +static inline void ixgb_rx_checksum(struct ixgb_adapter *adapter, + struct ixgb_rx_desc *rx_desc, + struct sk_buff *skb); #ifdef CONFIG_IXGB_NAPI static int ixgb_clean(struct net_device *netdev, int *budget); static boolean_t ixgb_clean_rx_irq(struct ixgb_adapter *adapter, @@ -94,10 +97,6 @@ static boolean_t ixgb_clean_rx_irq(struc static boolean_t ixgb_clean_rx_irq(struct ixgb_adapter *adapter); #endif static void ixgb_alloc_rx_buffers(struct ixgb_adapter *adapter); -static int ixgb_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd); -static inline void ixgb_rx_checksum(struct ixgb_adapter *adapter, - struct ixgb_rx_desc *rx_desc, - struct sk_buff *skb); static void ixgb_tx_timeout(struct net_device *dev); static void ixgb_tx_timeout_task(struct net_device *dev); static void ixgb_vlan_rx_register(struct net_device *netdev, @@ -124,7 +123,7 @@ struct notifier_block ixgb_notifier_rebo /* Exported from other modules */ extern void ixgb_check_options(struct ixgb_adapter *adapter); -extern int ixgb_ethtool_ioctl(struct net_device *netdev, struct ifreq *ifr); +extern struct ethtool_ops ixgb_ethtool_ops; static struct pci_driver ixgb_driver = { .name = ixgb_driver_name, @@ -185,6 +184,34 @@ static void __exit ixgb_exit_module(void module_exit(ixgb_exit_module); +/** + * ixgb_irq_disable - Mask off interrupt generation on the NIC + * @adapter: board private structure + **/ + +static inline void ixgb_irq_disable(struct ixgb_adapter *adapter) +{ + atomic_inc(&adapter->irq_sem); + IXGB_WRITE_REG(&adapter->hw, IMC, ~0); + IXGB_WRITE_FLUSH(&adapter->hw); + synchronize_irq(adapter->pdev->irq); +} + +/** + * ixgb_irq_enable - Enable default interrupt generation settings + * @adapter: board private structure + **/ + +static inline void ixgb_irq_enable(struct ixgb_adapter *adapter) +{ + if (atomic_dec_and_test(&adapter->irq_sem)) { + IXGB_WRITE_REG(&adapter->hw, IMS, + IXGB_INT_RXT0 | IXGB_INT_RXDMT0 | IXGB_INT_TXDW | + IXGB_INT_RXO | IXGB_INT_LSC); + IXGB_WRITE_FLUSH(&adapter->hw); + } +} + int ixgb_up(struct ixgb_adapter *adapter) { struct net_device *netdev = adapter->netdev; @@ -344,9 +371,9 @@ ixgb_probe(struct pci_dev *pdev, const s netdev->set_multicast_list = &ixgb_set_multi; netdev->set_mac_address = &ixgb_set_mac; netdev->change_mtu = &ixgb_change_mtu; - netdev->do_ioctl = &ixgb_ioctl; netdev->tx_timeout = &ixgb_tx_timeout; netdev->watchdog_timeo = HZ; + SET_ETHTOOL_OPS(netdev, &ixgb_ethtool_ops); #ifdef CONFIG_IXGB_NAPI netdev->poll = &ixgb_clean; netdev->weight = 64; @@ -1550,34 +1577,6 @@ static void ixgb_update_stats(struct ixg adapter->net_stats.tx_window_errors = 0; } -/** - * ixgb_irq_disable - Mask off interrupt generation on the NIC - * @adapter: board private structure - **/ - -static inline void ixgb_irq_disable(struct ixgb_adapter *adapter) -{ - atomic_inc(&adapter->irq_sem); - IXGB_WRITE_REG(&adapter->hw, IMC, ~0); - IXGB_WRITE_FLUSH(&adapter->hw); - synchronize_irq(adapter->pdev->irq); -} - -/** - * ixgb_irq_enable - Enable default interrupt generation settings - * @adapter: board private structure - **/ - -static inline void ixgb_irq_enable(struct ixgb_adapter *adapter) -{ - if (atomic_dec_and_test(&adapter->irq_sem)) { - IXGB_WRITE_REG(&adapter->hw, IMS, - IXGB_INT_RXT0 | IXGB_INT_RXDMT0 | IXGB_INT_TXDW | - IXGB_INT_RXO | IXGB_INT_LSC); - IXGB_WRITE_FLUSH(&adapter->hw); - } -} - #define IXGB_MAX_INTR 10 /** * ixgb_intr - Interrupt Handler @@ -1615,8 +1614,12 @@ static irqreturn_t ixgb_intr(int irq, vo } #else for (i = 0; i < IXGB_MAX_INTR; i++) - if (!ixgb_clean_rx_irq(adapter) & !ixgb_clean_tx_irq(adapter)) + if (ixgb_clean_rx_irq(adapter) == FALSE) + break; + for (i = 0; i < IXGB_MAX_INTR; i++) + if (ixgb_clean_tx_irq(adapter) == FALSE) break; + /* if RAIDC:EN == 1 and ICR:RXDMT0 == 1, we need to * set IMS:RXDMT0 to 1 to restart the RBD timer (POLL) */ @@ -1730,6 +1733,39 @@ static boolean_t ixgb_clean_tx_irq(struc } /** + * ixgb_rx_checksum - Receive Checksum Offload for 82597. + * @adapter: board private structure + * @rx_desc: receive descriptor + * @sk_buff: socket buffer with received data + **/ + +static inline void +ixgb_rx_checksum(struct ixgb_adapter *adapter, + struct ixgb_rx_desc *rx_desc, struct sk_buff *skb) +{ + /* Ignore Checksum bit is set OR + * TCP Checksum has not been calculated + */ + if ((rx_desc->status & IXGB_RX_DESC_STATUS_IXSM) || + (!(rx_desc->status & IXGB_RX_DESC_STATUS_TCPCS))) { + skb->ip_summed = CHECKSUM_NONE; + return; + } + + /* At this point we know the hardware did the TCP checksum */ + /* now look at the TCP checksum error bit */ + if (rx_desc->errors & IXGB_RX_DESC_ERRORS_TCPE) { + /* let the stack verify checksum errors */ + skb->ip_summed = CHECKSUM_NONE; + adapter->hw_csum_rx_error++; + } else { + /* TCP checksum is good */ + skb->ip_summed = CHECKSUM_UNNECESSARY; + adapter->hw_csum_rx_good++; + } +} + +/** * ixgb_clean_rx_irq - Send received data up the network stack, * @adapter: board private structure **/ @@ -1937,58 +1973,6 @@ static void ixgb_alloc_rx_buffers(struct } /** - * ixgb_ioctl - perform a command - e.g: ethtool:get_driver_info. - * @param netdev network interface device structure - * @param ifr data to be used/filled in by the ioctl command - * @param cmd ioctl command to execute - **/ - -static int ixgb_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) -{ - switch (cmd) { - case SIOCETHTOOL: - return ixgb_ethtool_ioctl(netdev, ifr); - default: - return -EOPNOTSUPP; - } - - return 0; -} - -/** - * ixgb_rx_checksum - Receive Checksum Offload for 82597. - * @adapter: board private structure - * @rx_desc: receive descriptor - * @sk_buff: socket buffer with received data - **/ - -static inline void -ixgb_rx_checksum(struct ixgb_adapter *adapter, - struct ixgb_rx_desc *rx_desc, struct sk_buff *skb) -{ - /* Ignore Checksum bit is set OR - * TCP Checksum has not been calculated - */ - if ((rx_desc->status & IXGB_RX_DESC_STATUS_IXSM) || - (!(rx_desc->status & IXGB_RX_DESC_STATUS_TCPCS))) { - skb->ip_summed = CHECKSUM_NONE; - return; - } - - /* At this point we know the hardware did the TCP checksum */ - /* now look at the TCP checksum error bit */ - if (rx_desc->errors & IXGB_RX_DESC_ERRORS_TCPE) { - /* let the stack verify checksum errors */ - skb->ip_summed = CHECKSUM_NONE; - adapter->hw_csum_rx_error++; - } else { - /* TCP checksum is good */ - skb->ip_summed = CHECKSUM_UNNECESSARY; - adapter->hw_csum_rx_good++; - } -} - -/** * ixgb_vlan_rx_register - enables or disables vlan tagging/stripping. * * @param netdev network interface device structure --- linux-2.6.9-rc1/drivers/net/Kconfig 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/net/Kconfig 2004-09-03 00:56:51.608314992 -0700 @@ -1353,7 +1353,7 @@ config FORCEDETH config CS89x0 tristate "CS89x0 support" - depends on NET_PCI && ISA + depends on NET_PCI && (ISA || ARCH_IXDP2X01) ---help--- Support for CS89x0 chipset based Ethernet cards. If you have a network (Ethernet) card of this type, say Y and read the @@ -1752,7 +1752,7 @@ config VIA_VELOCITY If you have a VIA "Velocity" based network card say Y here. To compile this driver as a module, choose M here. The module - will be called via-rhine. + will be called via-velocity. config LAN_SAA9730 bool "Philips SAA9730 Ethernet support (EXPERIMENTAL)" @@ -2046,8 +2046,29 @@ config R8169 config R8169_NAPI bool "Use Rx and Tx Polling (NAPI) (EXPERIMENTAL)" - depends on R8169 && EXPERIMENTAL + depends on R8169 && EXPERIMENTAL + help + NAPI is a new driver API designed to reduce CPU and interrupt load + when the driver is receiving lots of packets from the card. It is + still somewhat experimental and thus not yet enabled by default. + + If your estimated Rx load is 10kpps or more, or if the card will be + deployed on potentially unfriendly networks (e.g. in a firewall), + then say Y here. + See for more + information. + + If in doubt, say N. + +config R8169_VLAN + bool "VLAN support" + depends on R8169 && VLAN_8021Q + ---help--- + Say Y here for the r8169 driver to support the functions required + by the kernel 802.1Q code. + + If in doubt, say Y. config SK98LIN tristate "Marvell Yukon Chipset / SysKonnect SK-98xx Support" @@ -2643,3 +2664,15 @@ config NETCONSOLE If you want to log kernel messages over the network, enable this. See Documentation/networking/netconsole.txt for details. +config NETPKTLOG + tristate "Network packet Tracer using kernel probes(EXPERIMENTAL)" + depends on KPROBES && IP_NF_TARGET_LOG + ---help--- + Network packet tracing is achived using kernel probes allowing you + to see the network packets while moving up and down the stack. This + module uses kprobes mechanism which is highly efficient for dynamic + tracing. + + See driver/net/netpktlog.c for details. + To compile this driver as a module, choose M.If unsure, say N. + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/net/kgdb_eth.c 2004-09-03 00:56:36.523608216 -0700 @@ -0,0 +1,132 @@ +/* + * Network interface GDB stub + * + * Written by San Mehat (nettwerk@biodome.org) + * Based upon 'gdbserial' by David Grothe (dave@gcom.com) + * and Scott Foehner (sfoehner@engr.sgi.com) + * + * Twiddled for 2.6 by Robert Walsh + * and wangdi . + * + * Refactored for netpoll API by Matt Mackall + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define IN_BUF_SIZE 512 /* power of 2, please */ +#define OUT_BUF_SIZE 256 + +static char in_buf[IN_BUF_SIZE], out_buf[OUT_BUF_SIZE]; +static int in_head, in_tail, out_count; +static atomic_t in_count; +int kgdboe = 0; /* Default to tty mode */ + +extern void set_debug_traps(void); +extern void breakpoint(void); +static void rx_hook(struct netpoll *np, int port, char *msg, int len); + +static struct netpoll np = { + .name = "kgdboe", + .dev_name = "eth0", + .rx_hook = rx_hook, + .local_port = 6443, + .remote_port = 6442, + .remote_mac = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, +}; +static int configured; + +int eth_getDebugChar(void) +{ + int chr; + + while (atomic_read(&in_count) == 0) + netpoll_poll(&np); + + chr = in_buf[in_tail++]; + in_tail &= (IN_BUF_SIZE - 1); + atomic_dec(&in_count); + return chr; +} + +void eth_flushDebugChar(void) +{ + if(out_count && np.dev) { + netpoll_send_udp(&np, out_buf, out_count); + out_count = 0; + } +} + +void eth_putDebugChar(int chr) +{ + out_buf[out_count++] = chr; + if(out_count == OUT_BUF_SIZE) + eth_flushDebugChar(); +} + +static void rx_hook(struct netpoll *np, int port, char *msg, int len) +{ + int i; + + np->remote_port = port; + + /* Is this gdb trying to attach? */ + if (!netpoll_trap() && len == 8 && !strncmp(msg, "$Hc-1#09", 8)) + kgdb_schedule_breakpoint(); + + for (i = 0; i < len; i++) { + if (msg[i] == 3) + kgdb_schedule_breakpoint(); + + if (atomic_read(&in_count) >= IN_BUF_SIZE) { + /* buffer overflow, clear it */ + in_head = in_tail = 0; + atomic_set(&in_count, 0); + break; + } + in_buf[in_head++] = msg[i]; + in_head &= (IN_BUF_SIZE - 1); + atomic_inc(&in_count); + } +} + +static int option_setup(char *opt) +{ + configured = !netpoll_parse_options(&np, opt); + return 0; +} +__setup("kgdboe=", option_setup); + +static int init_kgdboe(void) +{ +#ifdef CONFIG_SMP + if (num_online_cpus() > CONFIG_NO_KGDB_CPUS) { + printk("kgdb: too manu cpus. Cannot enable debugger with more than %d cpus\n", CONFIG_NO_KGDB_CPUS); + return -1; + } +#endif + + set_debug_traps(); + + if(!configured || netpoll_setup(&np)) + return 1; + + kgdboe = 1; + printk(KERN_INFO "kgdb: debugging over ethernet enabled\n"); + + return 0; +} + +module_init(init_kgdboe); --- linux-2.6.9-rc1/drivers/net/mac8390.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/drivers/net/mac8390.c 2004-09-03 00:56:32.562210440 -0700 @@ -42,10 +42,6 @@ #include "8390.h" -#if (LINUX_VERSION_CODE < 0x02030e) -#define net_device device -#endif - #define WD_START_PG 0x00 /* First page of TX buffer */ #define CABLETRON_RX_START_PG 0x00 /* First page of RX buffer */ #define CABLETRON_RX_STOP_PG 0x30 /* Last page +1 of RX ring */ --- linux-2.6.9-rc1/drivers/net/Makefile 2004-08-24 00:02:25.000000000 -0700 +++ 25/drivers/net/Makefile 2004-09-03 00:56:36.524608064 -0700 @@ -195,4 +195,10 @@ obj-$(CONFIG_NET_TULIP) += tulip/ obj-$(CONFIG_HAMRADIO) += hamradio/ obj-$(CONFIG_IRDA) += irda/ +# Must come after all NICs that might use them obj-$(CONFIG_NETCONSOLE) += netconsole.o +obj-$(CONFIG_KGDB) += kgdb_eth.o + +ifeq ($(CONFIG_NETPKTLOG),y) + obj-m += netpktlog.o +endif --- linux-2.6.9-rc1/drivers/net/meth.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/net/meth.c 2004-09-03 00:56:32.563210288 -0700 @@ -368,31 +368,6 @@ static int meth_release(struct net_devic } /* - * Configuration changes (passed on by ifconfig) - */ -static int meth_config(struct net_device *dev, struct ifmap *map) -{ - if (dev->flags & IFF_UP) /* can't act on a running interface */ - return -EBUSY; - - /* Don't allow changing the I/O address */ - if (map->base_addr != dev->base_addr) { - printk(KERN_WARNING "meth: Can't change I/O address\n"); - return -EOPNOTSUPP; - } - - /* Don't allow changing the IRQ */ - if (map->irq != dev->irq) { - printk(KERN_WARNING "meth: Can't change IRQ\n"); - return -EOPNOTSUPP; - } - DPRINTK("Configured\n"); - - /* ignore other fields */ - return 0; -} - -/* * Receive a packet: retrieve, encapsulate and pass over to upper levels */ static void meth_rx(struct net_device* dev, unsigned long int_status) @@ -813,7 +788,6 @@ static struct net_device *meth_init(void dev->open = meth_open; dev->stop = meth_release; - dev->set_config = meth_config; dev->hard_start_xmit = meth_tx; dev->do_ioctl = meth_ioctl; dev->get_stats = meth_stats; --- linux-2.6.9-rc1/drivers/net/natsemi.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/drivers/net/natsemi.c 2004-09-03 00:56:32.567209680 -0700 @@ -768,6 +768,7 @@ static void enable_wol_mode(struct net_d static int netdev_close(struct net_device *dev); static int netdev_get_regs(struct net_device *dev, u8 *buf); static int netdev_get_eeprom(struct net_device *dev, u8 *buf); +static struct ethtool_ops ethtool_ops; static void move_int_phy(struct net_device *dev, int addr) { @@ -926,6 +927,7 @@ static int __devinit natsemi_probe1 (str #ifdef CONFIG_NET_POLL_CONTROLLER dev->poll_controller = &natsemi_poll_controller; #endif + SET_ETHTOOL_OPS(dev, ðtool_ops); if (mtu) dev->mtu = mtu; @@ -2457,177 +2459,136 @@ static void set_rx_mode(struct net_devic spin_unlock_irq(&np->lock); } -static int netdev_ethtool_ioctl(struct net_device *dev, void __user *useraddr) +static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct netdev_private *np = netdev_priv(dev); - u32 cmd; - - if (get_user(cmd, (u32 __user *)useraddr)) - return -EFAULT; - - switch (cmd) { - /* get driver info */ - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; - strncpy(info.driver, DRV_NAME, ETHTOOL_BUSINFO_LEN); - strncpy(info.version, DRV_VERSION, ETHTOOL_BUSINFO_LEN); - info.fw_version[0] = '\0'; - strncpy(info.bus_info, pci_name(np->pci_dev), - ETHTOOL_BUSINFO_LEN); - info.eedump_len = NATSEMI_EEPROM_SIZE; - info.regdump_len = NATSEMI_REGS_SIZE; - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - /* get settings */ - case ETHTOOL_GSET: { - struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - spin_lock_irq(&np->lock); - netdev_get_ecmd(dev, &ecmd); - spin_unlock_irq(&np->lock); - if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; - } - /* set settings */ - case ETHTOOL_SSET: { - struct ethtool_cmd ecmd; - int r; - if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) - return -EFAULT; - spin_lock_irq(&np->lock); - r = netdev_set_ecmd(dev, &ecmd); - spin_unlock_irq(&np->lock); - return r; - } - /* get wake-on-lan */ - case ETHTOOL_GWOL: { - struct ethtool_wolinfo wol = {ETHTOOL_GWOL}; - spin_lock_irq(&np->lock); - netdev_get_wol(dev, &wol.supported, &wol.wolopts); - netdev_get_sopass(dev, wol.sopass); - spin_unlock_irq(&np->lock); - if (copy_to_user(useraddr, &wol, sizeof(wol))) - return -EFAULT; - return 0; - } - /* set wake-on-lan */ - case ETHTOOL_SWOL: { - struct ethtool_wolinfo wol; - int r; - if (copy_from_user(&wol, useraddr, sizeof(wol))) - return -EFAULT; - spin_lock_irq(&np->lock); - netdev_set_wol(dev, wol.wolopts); - r = netdev_set_sopass(dev, wol.sopass); - spin_unlock_irq(&np->lock); - return r; - } - /* get registers */ - case ETHTOOL_GREGS: { - struct ethtool_regs regs; - u8 regbuf[NATSEMI_REGS_SIZE]; - int r; - - if (copy_from_user(®s, useraddr, sizeof(regs))) - return -EFAULT; - - if (regs.len > NATSEMI_REGS_SIZE) { - regs.len = NATSEMI_REGS_SIZE; - } - regs.version = NATSEMI_REGS_VER; - if (copy_to_user(useraddr, ®s, sizeof(regs))) - return -EFAULT; + strncpy(info->driver, DRV_NAME, ETHTOOL_BUSINFO_LEN); + strncpy(info->version, DRV_VERSION, ETHTOOL_BUSINFO_LEN); + strncpy(info->bus_info, pci_name(np->pci_dev), ETHTOOL_BUSINFO_LEN); +} - useraddr += offsetof(struct ethtool_regs, data); +static int get_regs_len(struct net_device *dev) +{ + return NATSEMI_REGS_SIZE; +} - spin_lock_irq(&np->lock); - r = netdev_get_regs(dev, regbuf); - spin_unlock_irq(&np->lock); +static int get_eeprom_len(struct net_device *dev) +{ + return NATSEMI_EEPROM_SIZE; +} - if (r) - return r; - if (copy_to_user(useraddr, regbuf, regs.len)) - return -EFAULT; - return 0; - } - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = np->msg_enable; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - np->msg_enable = edata.data; - return 0; - } - /* restart autonegotiation */ - case ETHTOOL_NWAY_RST: { - int tmp; - int r = -EINVAL; - /* if autoneg is off, it's an error */ - tmp = mdio_read(dev, MII_BMCR); - if (tmp & BMCR_ANENABLE) { - tmp |= (BMCR_ANRESTART); - mdio_write(dev, MII_BMCR, tmp); - r = 0; - } - return r; - } - /* get link status */ - case ETHTOOL_GLINK: { - struct ethtool_value edata = {ETHTOOL_GLINK}; - /* LSTATUS is latched low until a read - so read twice */ - mdio_read(dev, MII_BMSR); - edata.data = (mdio_read(dev, MII_BMSR)&BMSR_LSTATUS) ? 1:0; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* get EEPROM */ - case ETHTOOL_GEEPROM: { - struct ethtool_eeprom eeprom; - u8 eebuf[NATSEMI_EEPROM_SIZE]; - int r; +static int get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct netdev_private *np = netdev_priv(dev); + spin_lock_irq(&np->lock); + netdev_get_ecmd(dev, ecmd); + spin_unlock_irq(&np->lock); + return 0; +} - if (copy_from_user(&eeprom, useraddr, sizeof(eeprom))) - return -EFAULT; +static int set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct netdev_private *np = netdev_priv(dev); + int res; + spin_lock_irq(&np->lock); + res = netdev_set_ecmd(dev, ecmd); + spin_unlock_irq(&np->lock); + return res; +} - if (eeprom.offset > eeprom.offset+eeprom.len) - return -EINVAL; +static void get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct netdev_private *np = netdev_priv(dev); + spin_lock_irq(&np->lock); + netdev_get_wol(dev, &wol->supported, &wol->wolopts); + netdev_get_sopass(dev, wol->sopass); + spin_unlock_irq(&np->lock); +} - if ((eeprom.offset+eeprom.len) > NATSEMI_EEPROM_SIZE) { - eeprom.len = NATSEMI_EEPROM_SIZE-eeprom.offset; - } - eeprom.magic = PCI_VENDOR_ID_NS | (PCI_DEVICE_ID_NS_83815<<16); - if (copy_to_user(useraddr, &eeprom, sizeof(eeprom))) - return -EFAULT; +static int set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct netdev_private *np = netdev_priv(dev); + int res; + spin_lock_irq(&np->lock); + netdev_set_wol(dev, wol->wolopts); + res = netdev_set_sopass(dev, wol->sopass); + spin_unlock_irq(&np->lock); + return res; +} - useraddr += offsetof(struct ethtool_eeprom, data); +static void get_regs(struct net_device *dev, struct ethtool_regs *regs, void *buf) +{ + struct netdev_private *np = netdev_priv(dev); + regs->version = NATSEMI_REGS_VER; + spin_lock_irq(&np->lock); + netdev_get_regs(dev, buf); + spin_unlock_irq(&np->lock); +} - spin_lock_irq(&np->lock); - r = netdev_get_eeprom(dev, eebuf); - spin_unlock_irq(&np->lock); +static u32 get_msglevel(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + return np->msg_enable; +} - if (r) - return r; - if (copy_to_user(useraddr, eebuf+eeprom.offset, eeprom.len)) - return -EFAULT; - return 0; - } +static void set_msglevel(struct net_device *dev, u32 val) +{ + struct netdev_private *np = netdev_priv(dev); + np->msg_enable = val; +} +static int nway_reset(struct net_device *dev) +{ + int tmp; + int r = -EINVAL; + /* if autoneg is off, it's an error */ + tmp = mdio_read(dev, MII_BMCR); + if (tmp & BMCR_ANENABLE) { + tmp |= (BMCR_ANRESTART); + mdio_write(dev, MII_BMCR, tmp); + r = 0; } + return r; +} - return -EOPNOTSUPP; +static u32 get_link(struct net_device *dev) +{ + /* LSTATUS is latched low until a read - so read twice */ + mdio_read(dev, MII_BMSR); + return (mdio_read(dev, MII_BMSR)&BMSR_LSTATUS) ? 1:0; } +static int get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *data) +{ + struct netdev_private *np = netdev_priv(dev); + u8 eebuf[NATSEMI_EEPROM_SIZE]; + int res; + + eeprom->magic = PCI_VENDOR_ID_NS | (PCI_DEVICE_ID_NS_83815<<16); + spin_lock_irq(&np->lock); + res = netdev_get_eeprom(dev, eebuf); + spin_unlock_irq(&np->lock); + if (!res) + memcpy(data, eebuf+eeprom->offset, eeprom->len); + return res; +} + +static struct ethtool_ops ethtool_ops = { + .get_drvinfo = get_drvinfo, + .get_regs_len = get_regs_len, + .get_eeprom_len = get_eeprom_len, + .get_settings = get_settings, + .set_settings = set_settings, + .get_wol = get_wol, + .set_wol = set_wol, + .get_regs = get_regs, + .get_msglevel = get_msglevel, + .set_msglevel = set_msglevel, + .nway_reset = nway_reset, + .get_link = get_link, + .get_eeprom = get_eeprom, +}; + static int netdev_set_wol(struct net_device *dev, u32 newval) { struct netdev_private *np = netdev_priv(dev); @@ -2976,8 +2937,6 @@ static int netdev_ioctl(struct net_devic struct netdev_private *np = netdev_priv(dev); switch(cmd) { - case SIOCETHTOOL: - return netdev_ethtool_ioctl(dev, rq->ifr_data); case SIOCGMIIPHY: /* Get address of MII PHY in use. */ case SIOCDEVPRIVATE: /* for binary compat, remove in 2.5 */ data->phy_id = np->phy_addr_external; --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/net/netpktlog.c 2004-09-03 00:56:35.508762496 -0700 @@ -0,0 +1,672 @@ +/* + * Network Packet Tracer using Kernel Probes (KProbes) + * net/driver/netpktlog.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2004 + * + * 2004-Aug Created by Prasanna S Panchamukhi + * for network packet tracing using kernel probes interface. + * Suggested by Andi Kleen. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned short source_port, target_port; +module_param(source_port, ushort, S_IRUSR | S_IWUSR); +module_param(target_port, ushort, S_IRUSR | S_IWUSR); + +void dump_packet_hdr(const struct ipt_log_info *info, struct sk_buff *skb) +{ + struct iphdr iph; + + if (memcpy(&iph, skb->nh.iph, sizeof(iph)) < 0) { + printk("TRUNCATED"); + return; + } + + /* Important fields: + * TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */ + /* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */ + printk("SRC=%u.%u.%u.%u DST=%u.%u.%u.%u ", + NIPQUAD(iph.saddr), NIPQUAD(iph.daddr)); + + /* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */ + printk("LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ", + ntohs(iph.tot_len), iph.tos & IPTOS_TOS_MASK, + iph.tos & IPTOS_PREC_MASK, iph.ttl, ntohs(iph.id)); + + /* Max length: 6 "CE DF MF " */ + if (ntohs(iph.frag_off) & IP_CE) + printk("CE "); + if (ntohs(iph.frag_off) & IP_DF) + printk("DF "); + if (ntohs(iph.frag_off) & IP_MF) + printk("MF "); + + /* Max length: 11 "FRAG:65535 " */ + if (ntohs(iph.frag_off) & IP_OFFSET) + printk("FRAG:%u ", ntohs(iph.frag_off) & IP_OFFSET); + + if ((info->logflags & IPT_LOG_IPOPT) + && iph.ihl * 4 > sizeof(struct iphdr)) { + unsigned char opt[4 * 15 - sizeof(struct iphdr)]; + unsigned int i, optsize; + + optsize = iph.ihl * 4 - sizeof(struct iphdr); + if (memcpy(opt, skb->nh.iph + sizeof(iph), optsize) < 0) { + printk("TRUNCATED"); + return; + } + + /* Max length: 127 "OPT (" 15*4*2chars ") " */ + printk("OPT ("); + for (i = 0; i < optsize; i++) + printk("%02X", opt[i]); + printk(") "); + } + + switch (iph.protocol) { + case IPPROTO_TCP:{ + struct tcphdr tcph; + + /* Max length: 10 "PROTO=TCP " */ + printk("PROTO=TCP "); + + if (ntohs(iph.frag_off) & IP_OFFSET) + break; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + if (memcpy(&tcph, skb->h.th, sizeof(tcph)) + < 0) { + printk("INCOMPLETE [%u bytes] ", iph.ihl * 4); + break; + } + + /* Max length: 20 "SPT=65535 DPT=65535 " */ + printk("SPT=%u DPT=%u ", + ntohs(tcph.source), ntohs(tcph.dest)); + /* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */ + if (info->logflags & IPT_LOG_TCPSEQ) + printk("SEQ=%u ACK=%u ", + ntohl(tcph.seq), ntohl(tcph.ack_seq)); + /* Max length: 13 "WINDOW=65535 " */ + printk("WINDOW=%u ", ntohs(tcph.window)); + /* Max length: 9 "RES=0x3F " */ + printk("RES=0x%02x ", + (u8) (ntohl + (tcp_flag_word(&tcph) & TCP_RESERVED_BITS) + >> 22)); + /* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */ + if (tcph.cwr) + printk("CWR "); + if (tcph.ece) + printk("ECE "); + if (tcph.urg) + printk("URG "); + if (tcph.ack) + printk("ACK "); + if (tcph.psh) + printk("PSH "); + if (tcph.rst) + printk("RST "); + if (tcph.syn) + printk("SYN "); + if (tcph.fin) + printk("FIN "); + /* Max length: 11 "URGP=65535 " */ + printk("URGP=%u ", ntohs(tcph.urg_ptr)); + + if ((info->logflags & IPT_LOG_TCPOPT) + && tcph.doff * 4 > sizeof(struct tcphdr)) { + unsigned char opt[4 * 15 - + sizeof(struct tcphdr)]; + unsigned int i, optsize; + + optsize = tcph.doff * 4 - sizeof(struct tcphdr); + if (memcpy(opt, skb->h.th + sizeof(tcph), + optsize) < 0) { + printk("TRUNCATED"); + return; + } + + /* Max length: 127 "OPT (" 15*4*2chars ") " */ + printk("OPT ("); + for (i = 0; i < optsize; i++) + printk("%02X", opt[i]); + printk(") "); + } + break; + } + case IPPROTO_UDP:{ + struct udphdr udph; + + /* Max length: 10 "PROTO=UDP " */ + printk("PROTO=UDP "); + + if (ntohs(iph.frag_off) & IP_OFFSET) + break; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + if (memcpy(&udph, skb->h.uh, sizeof(udph)) + < 0) { + printk("INCOMPLETE [%u bytes] ", iph.ihl * 4); + break; + } + + /* Max length: 20 "SPT=65535 DPT=65535 " */ + printk("SPT=%u DPT=%u LEN=%u ", + ntohs(udph.source), ntohs(udph.dest), + ntohs(udph.len)); + break; + } + case IPPROTO_ICMP:{ + struct icmphdr icmph; + /* Max length: 11 "PROTO=ICMP " */ + printk("PROTO=ICMP "); + + if (ntohs(iph.frag_off) & IP_OFFSET) + break; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + if (memcpy(&icmph, skb->h.icmph, sizeof(icmph)) + < 0) { + printk("INCOMPLETE [bytes] "); + break; + } + + /* Max length: 18 "TYPE=255 CODE=255 " */ + printk("TYPE=%u CODE=%u ", icmph.type, icmph.code); + + switch (icmph.type) { + case ICMP_ECHOREPLY: + case ICMP_ECHO: + /* Max length: 19 "ID=65535 SEQ=65535 " */ + printk("ID=%u SEQ=%u ", + ntohs(icmph.un.echo.id), + ntohs(icmph.un.echo.sequence)); + break; + + case ICMP_PARAMETERPROB: + /* Max length: 14 "PARAMETER=255 " */ + printk("PARAMETER=%u ", + ntohl(icmph.un.gateway) >> 24); + break; + case ICMP_REDIRECT: + /* Max length: 24 "GATEWAY=255.255.255.255 " */ + printk("GATEWAY=%u.%u.%u.%u ", + NIPQUAD(icmph.un.gateway)); + /* Fall through */ + case ICMP_DEST_UNREACH: + case ICMP_SOURCE_QUENCH: + case ICMP_TIME_EXCEEDED: + /* Max length: 10 "MTU=65535 " */ + if (icmph.type == ICMP_DEST_UNREACH + && icmph.code == ICMP_FRAG_NEEDED) + printk("MTU=%u ", + ntohs(icmph.un.frag.mtu)); + } + break; + } + /* Max Length */ + case IPPROTO_AH:{ + struct ip_auth_hdr ah; + + if (ntohs(iph.frag_off) & IP_OFFSET) + break; + + /* Max length: 9 "PROTO=AH " */ + printk("PROTO=AH "); + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + if (memcpy + (&ah, skb->h.icmph + sizeof(struct icmphdr), + sizeof(ah)) < 0) { + printk("INCOMPLETE [bytes] "); + break; + } + + /* Length: 15 "SPI=0xF1234567 " */ + printk("SPI=0x%x ", ntohl(ah.spi)); + break; + } + case IPPROTO_ESP:{ + struct ip_esp_hdr esph; + + /* Max length: 10 "PROTO=ESP " */ + printk("PROTO=ESP "); + + if (ntohs(iph.frag_off) & IP_OFFSET) + break; + + /* Max length: 25 "INCOMPLETE [65535 bytes] " */ + if (memcpy + (&esph, skb->h.icmph + sizeof(struct icmphdr), + sizeof(esph)) + < 0) { + printk("INCOMPLETE [bytes] "); + break; + } + + /* Length: 15 "SPI=0xF1234567 " */ + printk("SPI=0x%x ", ntohl(esph.spi)); + break; + } + /* Max length: 10 "PROTO 255 " */ + default: + printk("PROTO=%u ", iph.protocol); + } + + /* Proto Max log string length */ + /* IP: 40+46+6+11+127 = 230 */ + /* TCP: 10+max(25,20+30+13+9+32+11+127) = 252 */ + /* UDP: 10+max(25,20) = 35 */ + /* ICMP: 11+max(25, 18+25+max(19,14,24+3+n+10,3+n+10)) = 91+n */ + /* ESP: 10+max(25)+15 = 50 */ + /* AH: 9+max(25)+15 = 49 */ + /* unknown: 10 */ + + /* (ICMP allows recursion one level deep) */ + /* maxlen = IP + ICMP + IP + max(TCP,UDP,ICMP,unknown) */ + /* maxlen = 230+ 91 + 230 + 252 = 803 */ +} + +/* + * nettrace_port: This is a generic routine that can be used to dump the + * network packet for a given source and destination port numbers. + * Network packet filtering is done based on source/target port or + * both source and target ports. + */ + +static inline int nettrace_port(unsigned short source, unsigned short dest) +{ + if (((!source_port) && (!target_port)) + || ((!source_port) && (dest == target_port)) + || ((!target_port) && (source == source_port)) + || ((source == source_port) && (dest == target_port))) + return 0; + + return 1; +} + +static void get_ports_skb(struct sk_buff *skb, unsigned short *source, + unsigned short *dest) +{ + struct iphdr *iph; + struct tcphdr *tcph; + struct udphdr *udph; + iph = (struct iphdr *)skb->data; + + switch (iph->protocol) { + case IPPROTO_TCP: + tcph = (struct tcphdr *)((unsigned long)iph + iph->ihl * 4); + *source = ntohs(tcph->source); + *dest = ntohs(tcph->dest); + break; + case IPPROTO_UDP: + udph = (struct udphdr *)((unsigned long)iph + iph->ihl * 4); + *source = ntohs(udph->source); + *dest = ntohs(udph->dest); + break; + } +} + +static void get_ports_net(struct sk_buff *skb, unsigned short *source, + unsigned short *dest) +{ + struct iphdr *iph; + struct tcphdr *tcph; + struct udphdr *udph; + + iph = (struct iphdr *)skb->nh.iph; + switch (iph->protocol) { + case IPPROTO_TCP: + tcph = (struct tcphdr *)skb->h.th; + *source = ntohs(tcph->source); + *dest = ntohs(tcph->dest); + break; + case IPPROTO_UDP: + udph = (struct udphdr *)skb->h.uh; + *source = ntohs(udph->source); + *dest = ntohs(udph->dest); + break; + } +} + +/* + * Compile the kernel with options CONFIG_KPROBES, CONFIG_NETPKTLOG, + * CONFIG_NETFILTER, CONFIG_IP_NF_IPTABLES and CONFIG_IP_NF_TARGET_LOG enabled. + * You need to specify the parameters to the netpktlog module. + * To trace the network packets based on source and target ports, insert the + * module with source and target ports. + * Example insmod netpktlog.ko source_port=35707 target_port=22 + * To trace the network packets based on only source port, insert module + * with source port. Example insmod netpktlog.ko source_port=35707 + * To trace network packets based on target port, insert module with + * target port. Example insmod netpktlog.ko target_port=22 + */ +static void jnetif_rx(struct sk_buff *skb) +{ + struct iphdr *iph; + struct ipt_log_info info; + unsigned short source, dest; + info.logflags = IPT_LOG_IPOPT; + + if ((skb->protocol == htons(ETH_P_IP)) + && (iph = (struct iphdr *)skb->data)) { + get_ports_skb(skb, &source, &dest); + if (!nettrace_port(source, dest)) { + printk("netif_rx: "); + dump_packet((struct ipt_log_info *)&info, skb, 0); + printk("\n"); + } + } + jprobe_return(); +} + +static void j__kfree_skb(struct sk_buff *skb) +{ + struct iphdr *iph; + struct ipt_log_info info; + unsigned short source, dest; + info.logflags = IPT_LOG_IPOPT; + + if ((skb->protocol == htons(ETH_P_IP)) + && (iph = (struct iphdr *)skb->nh.iph)) { + get_ports_net(skb, &source, &dest); + if (!nettrace_port(source, dest)) { + printk("__kfree_skb: "); + dump_packet_hdr((struct ipt_log_info *)&info, skb); + printk("\n"); + } + } + jprobe_return(); +} + +static int jnetif_receive_skb(struct sk_buff *skb) +{ + struct iphdr *iph; + unsigned short source, dest; + struct ipt_log_info info; + info.logflags = IPT_LOG_IPOPT; + + if ((skb->protocol == htons(ETH_P_IP)) + && (iph = (struct iphdr *)skb->data)) { + get_ports_skb(skb, &source, &dest); + if (!nettrace_port(source, dest)) { + printk("netif_receive_skb: "); + dump_packet((struct ipt_log_info *)&info, skb, 0); + printk("\n"); + } + } + jprobe_return(); + return 0; +} + +static int jip_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt) +{ + struct iphdr *iph; + struct ipt_log_info info; + unsigned short source, dest; + info.logflags = IPT_LOG_IPOPT; + + if ((skb->protocol == htons(ETH_P_IP)) + && (iph = (struct iphdr *)skb->data)) { + get_ports_skb(skb, &source, &dest); + if (!nettrace_port(source, dest)) { + printk("ip_rcv: "); + dump_packet((struct ipt_log_info *)&info, skb, 0); + printk("\n"); + } + } + jprobe_return(); + return 0; +} + +static int jip_local_deliver(struct sk_buff *skb) +{ + struct iphdr *iph; + struct ipt_log_info info; + unsigned short source, dest; + info.logflags = IPT_LOG_IPOPT; + + if ((skb->protocol == htons(ETH_P_IP)) + && (iph = (struct iphdr *)skb->data)) { + get_ports_skb(skb, &source, &dest); + if (!nettrace_port(source, dest)) { + printk("ip_local_deliver: "); + dump_packet((struct ipt_log_info *)&info, skb, 0); + printk("\n"); + } + } + jprobe_return(); + return 0; +} + +static int jip_output(struct sk_buff **pskb) +{ + struct iphdr *iph; + struct ipt_log_info info; + unsigned short source, dest; + info.logflags = IPT_LOG_IPOPT; + + iph = (struct iphdr *)(*pskb)->nh.iph; + if (iph) { + get_ports_net(*pskb, &source, &dest); + if (!nettrace_port(source, dest)) { + printk("ip_output: "); + dump_packet_hdr((struct ipt_log_info *)&info, (*pskb)); + printk("\n"); + } + } + jprobe_return(); + return 0; +} + +static int jtcp_v4_rcv(struct sk_buff *skb) +{ + struct iphdr *iph; + struct ipt_log_info info; + unsigned short source, dest; + info.logflags = IPT_LOG_IPOPT; + + if ((skb->protocol == htons(ETH_P_IP)) + && (iph = (struct iphdr *)skb->nh.iph)) { + get_ports_net(skb, &source, &dest); + if (!nettrace_port(source, dest)) { + printk("tcp_v4_rcv: "); + dump_packet_hdr((struct ipt_log_info *)&info, skb); + printk("\n"); + } + } + jprobe_return(); + return 0; +} + +static int jtcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) +{ + struct iphdr *iph; + struct ipt_log_info info; + unsigned short source, dest; + info.logflags = IPT_LOG_IPOPT; + + if ((skb->protocol == htons(ETH_P_IP)) + && (iph = (struct iphdr *)skb->nh.iph)) { + get_ports_net(skb, &source, &dest); + if (!nettrace_port(source, dest)) { + printk("tcp_v4_do_rcv: "); + dump_packet_hdr((struct ipt_log_info *)&info, skb); + printk("\n"); + } + } + jprobe_return(); + return 0; +} + +static int jtcp_rcv_established(struct sock *sk, struct sk_buff *skb, + struct tcphdr *th, unsigned len) +{ + struct iphdr *iph; + struct ipt_log_info info; + unsigned short source, dest; + info.logflags = IPT_LOG_IPOPT; + + if ((skb->protocol == htons(ETH_P_IP)) + && (iph = (struct iphdr *)skb->nh.iph)) { + get_ports_net(skb, &source, &dest); + if (!nettrace_port(source, dest)) { + printk("tcp_rcv_established: "); + dump_packet_hdr((struct ipt_log_info *)&info, skb); + printk("\n"); + } + } + jprobe_return(); + return 0; +} + +static int jskb_copy_datagram_iovec(struct sk_buff *from, int offset, + struct iovec *to, int size) +{ + struct iphdr *iph; + struct ipt_log_info info; + unsigned short source, dest; + info.logflags = IPT_LOG_IPOPT; + + if ((from->protocol == htons(ETH_P_IP)) + && (iph = (struct iphdr *)from->nh.iph)) { + get_ports_net(from, &source, &dest); + if (!nettrace_port(source, dest)) { + printk("skb_copy_datagram_iovec: "); + dump_packet_hdr((struct ipt_log_info *)&info, from); + printk("\n"); + } + } + jprobe_return(); + return 0; +} + +static void jtcp_send_dupack(struct sock *sk, struct sk_buff *skb) +{ + struct iphdr *iph; + struct ipt_log_info info; + unsigned short source, dest; + info.logflags = IPT_LOG_IPOPT; + + iph = (struct iphdr *)skb->nh.iph; + if (iph) { + get_ports_net(skb, &source, &dest); + if (!nettrace_port(source, dest)) { + printk("tcp_send_dupack: "); + dump_packet_hdr((struct ipt_log_info *)&info, skb); + printk("\n"); + } + } + jprobe_return(); +} + +static int jip_forward(struct sk_buff *skb) +{ + struct iphdr *iph; + struct ipt_log_info info; + unsigned short source, dest; + info.logflags = IPT_LOG_IPOPT; + + iph = (struct iphdr *)skb->nh.iph; + if (iph) { + get_ports_net(skb, &source, &dest); + if (!nettrace_port(source, dest)) { + printk("ip_forward: "); + dump_packet_hdr((struct ipt_log_info *)&info, skb); + printk("\n"); + } + } + jprobe_return(); + return 0; +} + +struct nettrace_obj { + const char *funcname; + struct jprobe jp; +}; + +#define NTOBJ(func) \ + { .funcname = #func, .jp = { .entry = (kprobe_opcode_t*)j##func }} +static struct nettrace_obj nettrace_objs[] = { + NTOBJ(netif_rx), + NTOBJ(__kfree_skb), + NTOBJ(netif_receive_skb), + NTOBJ(ip_rcv), + NTOBJ(ip_local_deliver), + NTOBJ(ip_output), + NTOBJ(ip_forward), + NTOBJ(tcp_v4_rcv), + NTOBJ(tcp_v4_do_rcv), + NTOBJ(tcp_rcv_established), + NTOBJ(skb_copy_datagram_iovec), + NTOBJ(tcp_send_dupack) +}; + +#define MAX_NETTRACE_ROUTINE (sizeof(nettrace_objs)/sizeof(nettrace_objs[0])) + +static int init_netpktlog(void) +{ + int i; + struct nettrace_obj *nt; + + /* first time invokation to initialize probe handler */ + /* now we are all set to register the probe */ + for (i = 0, nt = nettrace_objs; i < MAX_NETTRACE_ROUTINE; i++, nt++) { + nt = &nettrace_objs[i]; + nt->jp.kp.addr = (kprobe_opcode_t *) + kallsyms_lookup_name(nt->funcname); + if (nt->jp.kp.addr) { + printk("plant jprobe at %s (%p), handler addr %p\n", + nt->funcname, nt->jp.kp.addr, nt->jp.entry); + register_jprobe(&nt->jp); + } else { + printk("couldn't find %s to plant jprobe\n", + nt->funcname); + } + } + + printk("Network packets tracing is enabled...\n"); + return 0; +} + +static void cleanup_netpktlog(void) +{ + int i; + for (i = 0; i < MAX_NETTRACE_ROUTINE; i++) { + if (nettrace_objs[i].jp.kp.addr) + unregister_jprobe(&nettrace_objs[i].jp); + } + printk("Network packets tracing is disabled...\n"); +} + +module_init(init_netpktlog); +module_exit(cleanup_netpktlog); +MODULE_LICENSE("GPL"); --- linux-2.6.9-rc1/drivers/net/ns83820.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/drivers/net/ns83820.c 2004-09-03 00:56:32.569209376 -0700 @@ -1192,59 +1192,26 @@ static struct net_device_stats *ns83820_ return &dev->stats; } -static int ns83820_ethtool_ioctl (struct ns83820 *dev, void __user *useraddr) +static void ns83820_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info) { - u32 ethcmd; - - if (copy_from_user(ðcmd, useraddr, sizeof (ethcmd))) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: - { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy(info.driver, "ns83820"); - strcpy(info.version, VERSION); - strcpy(info.bus_info, pci_name(dev->pci_dev)); - if (copy_to_user(useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } - - /* get link status */ - case ETHTOOL_GLINK: { - struct ethtool_value edata = { ETHTOOL_GLINK }; - u32 cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY; - - if (cfg & CFG_LNKSTS) - edata.data = 1; - else - edata.data = 0; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - - default: - break; - } - - return -EOPNOTSUPP; + struct ns83820 *dev = PRIV(ndev); + strcpy(info->driver, "ns83820"); + strcpy(info->version, VERSION); + strcpy(info->bus_info, pci_name(dev->pci_dev)); } -static int ns83820_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) +static u32 ns83820_get_link(struct net_device *ndev) { struct ns83820 *dev = PRIV(ndev); - - switch(cmd) { - case SIOCETHTOOL: - return ns83820_ethtool_ioctl(dev, rq->ifr_data); - - default: - return -EOPNOTSUPP; - } + u32 cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY; + return cfg & CFG_LNKSTS ? 1 : 0; } +static struct ethtool_ops ops = { + .get_drvinfo = ns83820_get_drvinfo, + .get_link = ns83820_get_link +}; + static void ns83820_mib_isr(struct ns83820 *dev) { spin_lock(&dev->misc_lock); @@ -1884,7 +1851,7 @@ static int __devinit ns83820_init_one(st ndev->get_stats = ns83820_get_stats; ndev->change_mtu = ns83820_change_mtu; ndev->set_multicast_list = ns83820_set_multicast; - ndev->do_ioctl = ns83820_ioctl; + SET_ETHTOOL_OPS(ndev, &ops); ndev->tx_timeout = ns83820_tx_timeout; ndev->watchdog_timeo = 5 * HZ; --- linux-2.6.9-rc1/drivers/net/pcmcia/3c574_cs.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/net/pcmcia/3c574_cs.c 2004-09-03 00:56:47.992864624 -0700 @@ -519,6 +519,7 @@ static void tc574_config(dev_link_t *lin link->state &= ~DEV_CONFIG_PENDING; link->dev = &lp->node; + SET_NETDEV_DEV(dev, pcmcia_lookup_device(handle)); if (register_netdev(dev) != 0) { printk(KERN_NOTICE "3c574_cs: register_netdev() failed\n"); --- linux-2.6.9-rc1/drivers/net/pcmcia/3c589_cs.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/drivers/net/pcmcia/3c589_cs.c 2004-09-03 00:56:47.993864472 -0700 @@ -391,6 +391,7 @@ static void tc589_config(dev_link_t *lin link->dev = &lp->node; link->state &= ~DEV_CONFIG_PENDING; + SET_NETDEV_DEV(dev, pcmcia_lookup_device(handle)); if (register_netdev(dev) != 0) { printk(KERN_ERR "3c589_cs: register_netdev() failed\n"); --- linux-2.6.9-rc1/drivers/net/pcmcia/axnet_cs.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/drivers/net/pcmcia/axnet_cs.c 2004-09-03 00:56:47.995864168 -0700 @@ -458,6 +458,7 @@ static void axnet_config(dev_link_t *lin info->phy_id = (i < 32) ? i : -1; link->dev = &info->node; link->state &= ~DEV_CONFIG_PENDING; + SET_NETDEV_DEV(dev, pcmcia_lookup_device(handle)); if (register_netdev(dev) != 0) { printk(KERN_NOTICE "axnet_cs: register_netdev() failed\n"); --- linux-2.6.9-rc1/drivers/net/pcmcia/com20020_cs.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/drivers/net/pcmcia/com20020_cs.c 2004-09-03 00:56:47.996864016 -0700 @@ -394,6 +394,7 @@ static void com20020_config(dev_link_t * link->dev = &info->node; link->state &= ~DEV_CONFIG_PENDING; + SET_NETDEV_DEV(dev, pcmcia_lookup_device(handle)); i = com20020_found(dev, 0); /* calls register_netdev */ --- linux-2.6.9-rc1/drivers/net/pcmcia/fmvj18x_cs.c 2004-08-24 00:03:14.000000000 -0700 +++ 25/drivers/net/pcmcia/fmvj18x_cs.c 2004-09-03 00:56:47.997863864 -0700 @@ -591,6 +591,7 @@ static void fmvj18x_config(dev_link_t *l lp->cardtype = cardtype; link->dev = &lp->node; link->state &= ~DEV_CONFIG_PENDING; + SET_NETDEV_DEV(dev, pcmcia_lookup_device(handle)); if (register_netdev(dev) != 0) { printk(KERN_NOTICE "fmvj18x_cs: register_netdev() failed\n"); --- linux-2.6.9-rc1/drivers/net/pcmcia/ibmtr_cs.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/net/pcmcia/ibmtr_cs.c 2004-09-03 00:56:47.997863864 -0700 @@ -366,6 +366,7 @@ static void ibmtr_config(dev_link_t *lin link->dev = &info->node; link->state &= ~DEV_CONFIG_PENDING; + SET_NETDEV_DEV(dev, pcmcia_lookup_device(handle)); i = ibmtr_probe_card(dev); if (i != 0) { --- linux-2.6.9-rc1/drivers/net/pcmcia/nmclan_cs.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/net/pcmcia/nmclan_cs.c 2004-09-03 00:56:47.999863560 -0700 @@ -775,6 +775,7 @@ static void nmclan_config(dev_link_t *li link->dev = &lp->node; link->state &= ~DEV_CONFIG_PENDING; + SET_NETDEV_DEV(dev, pcmcia_lookup_device(handle)); i = register_netdev(dev); if (i != 0) { --- linux-2.6.9-rc1/drivers/net/pcmcia/pcnet_cs.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/net/pcmcia/pcnet_cs.c 2004-09-03 00:56:48.000863408 -0700 @@ -722,6 +722,7 @@ static void pcnet_config(dev_link_t *lin link->dev = &info->node; link->state &= ~DEV_CONFIG_PENDING; + SET_NETDEV_DEV(dev, pcmcia_lookup_device(handle)); #ifdef CONFIG_NET_POLL_CONTROLLER dev->poll_controller = ei_poll; --- linux-2.6.9-rc1/drivers/net/pcmcia/smc91c92_cs.c 2004-08-24 00:03:17.000000000 -0700 +++ 25/drivers/net/pcmcia/smc91c92_cs.c 2004-09-03 00:56:48.002863104 -0700 @@ -304,6 +304,7 @@ static void mdio_sync(ioaddr_t addr); static int mdio_read(struct net_device *dev, int phy_id, int loc); static void mdio_write(struct net_device *dev, int phy_id, int loc, int value); static int smc_link_ok(struct net_device *dev); +static struct ethtool_ops ethtool_ops; /*====================================================================== @@ -357,6 +358,7 @@ static dev_link_t *smc91c92_attach(void) dev->open = &smc_open; dev->stop = &smc_close; dev->do_ioctl = &smc_ioctl; + SET_ETHTOOL_OPS(dev, ðtool_ops); #ifdef HAVE_TX_TIMEOUT dev->tx_timeout = smc_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; @@ -1022,6 +1024,7 @@ static void smc91c92_config(dev_link_t * link->dev = &smc->node; link->state &= ~DEV_CONFIG_PENDING; + SET_NETDEV_DEV(dev, pcmcia_lookup_device(handle)); if (register_netdev(dev) != 0) { printk(KERN_ERR "smc91c92_cs: register_netdev() failed\n"); @@ -2118,131 +2121,130 @@ static int smc_netdev_set_ecmd(struct ne return 0; } -static int smc_ethtool_ioctl (struct net_device *dev, void __user *useraddr) +static int check_if_running(struct net_device *dev) { - u32 ethcmd; - struct smc_private *smc = netdev_priv(dev); - - if (get_user(ethcmd, (u32 __user *)useraddr)) - return -EFAULT; - - switch (ethcmd) { - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - strcpy(info.driver, DRV_NAME); - strcpy(info.version, DRV_VERSION); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; + if (!netif_running(dev)) + return -EINVAL; return 0; - } +} + +static void smc_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); +} - /* get settings */ - case ETHTOOL_GSET: { +static int smc_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct smc_private *smc = netdev_priv(dev); + ioaddr_t ioaddr = dev->base_addr; + u16 saved_bank = inw(ioaddr + BANK_SELECT); int ret; - struct ethtool_cmd ecmd = { ETHTOOL_GSET }; + + SMC_SELECT_BANK(3); spin_lock_irq(&smc->lock); if (smc->cfg & CFG_MII_SELECT) - ret = mii_ethtool_gset(&smc->mii_if, &ecmd); + ret = mii_ethtool_gset(&smc->mii_if, ecmd); else - ret = smc_netdev_get_ecmd(dev, &ecmd); + ret = smc_netdev_get_ecmd(dev, ecmd); spin_unlock_irq(&smc->lock); - if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) - return -EFAULT; + SMC_SELECT_BANK(saved_bank); return ret; - } +} - /* set settings */ - case ETHTOOL_SSET: { +static int smc_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct smc_private *smc = netdev_priv(dev); + ioaddr_t ioaddr = dev->base_addr; + u16 saved_bank = inw(ioaddr + BANK_SELECT); int ret; - struct ethtool_cmd ecmd; - if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) - return -EFAULT; + + SMC_SELECT_BANK(3); spin_lock_irq(&smc->lock); if (smc->cfg & CFG_MII_SELECT) - ret = mii_ethtool_sset(&smc->mii_if, &ecmd); + ret = mii_ethtool_sset(&smc->mii_if, ecmd); else - ret = smc_netdev_set_ecmd(dev, &ecmd); + ret = smc_netdev_set_ecmd(dev, ecmd); spin_unlock_irq(&smc->lock); + SMC_SELECT_BANK(saved_bank); return ret; - } +} + +static u32 smc_get_link(struct net_device *dev) +{ + struct smc_private *smc = netdev_priv(dev); + ioaddr_t ioaddr = dev->base_addr; + u16 saved_bank = inw(ioaddr + BANK_SELECT); + u32 ret; - /* get link status */ - case ETHTOOL_GLINK: { - struct ethtool_value edata = { ETHTOOL_GLINK }; + SMC_SELECT_BANK(3); spin_lock_irq(&smc->lock); - edata.data = smc_link_ok(dev); + ret = smc_link_ok(dev); spin_unlock_irq(&smc->lock); - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } + SMC_SELECT_BANK(saved_bank); + return ret; +} #ifdef PCMCIA_DEBUG - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = { ETHTOOL_GMSGLVL }; - edata.data = pc_debug; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } +static u32 smc_get_msglevel(struct net_device *dev) +{ + return pc_debug; +} - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - pc_debug = edata.data; - return 0; - } +static void smc_set_msglevel(struct net_device *dev, u32 val) +{ + pc_debug = val; +} #endif - /* restart autonegotiation */ - case ETHTOOL_NWAY_RST: { - if (smc->cfg & CFG_MII_SELECT) - return mii_nway_restart(&smc->mii_if); - else - return -EOPNOTSUPP; - } - - default: - break; - } - return -EOPNOTSUPP; -} +static int smc_nway_reset(struct net_device *dev) +{ + struct smc_private *smc = netdev_priv(dev); + if (smc->cfg & CFG_MII_SELECT) { + ioaddr_t ioaddr = dev->base_addr; + u16 saved_bank = inw(ioaddr + BANK_SELECT); + int res; + + SMC_SELECT_BANK(3); + res = mii_nway_restart(&smc->mii_if); + SMC_SELECT_BANK(saved_bank); + + return res; + } else + return -EOPNOTSUPP; +} + +static struct ethtool_ops ethtool_ops = { + .begin = check_if_running, + .get_drvinfo = smc_get_drvinfo, + .get_settings = smc_get_settings, + .set_settings = smc_set_settings, + .get_link = smc_get_link, +#ifdef PCMCIA_DEBUG + .get_msglevel = smc_get_msglevel, + .set_msglevel = smc_set_msglevel, +#endif + .nway_reset = smc_nway_reset, +}; static int smc_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) { - struct smc_private *smc = netdev_priv(dev); - struct mii_ioctl_data *mii; - int rc = 0; - u_short saved_bank; - ioaddr_t ioaddr = dev->base_addr; - - mii = if_mii(rq); - if (!netif_running(dev)) - return -EINVAL; + struct smc_private *smc = netdev_priv(dev); + struct mii_ioctl_data *mii = if_mii(rq); + int rc = 0; + u16 saved_bank; + ioaddr_t ioaddr = dev->base_addr; - switch (cmd) { - case SIOCETHTOOL: - saved_bank = inw(ioaddr + BANK_SELECT); - SMC_SELECT_BANK(3); - rc = smc_ethtool_ioctl(dev, rq->ifr_data); - SMC_SELECT_BANK(saved_bank); - break; + if (!netif_running(dev)) + return -EINVAL; - default: spin_lock_irq(&smc->lock); saved_bank = inw(ioaddr + BANK_SELECT); SMC_SELECT_BANK(3); rc = generic_mii_ioctl(&smc->mii_if, mii, cmd, NULL); SMC_SELECT_BANK(saved_bank); spin_unlock_irq(&smc->lock); - break; - } - - return rc; + return rc; } static struct pcmcia_driver smc91c92_cs_driver = { --- linux-2.6.9-rc1/drivers/net/pcmcia/xirc2ps_cs.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/drivers/net/pcmcia/xirc2ps_cs.c 2004-09-03 00:56:48.003862952 -0700 @@ -1121,6 +1121,7 @@ xirc2ps_config(dev_link_t * link) link->dev = &local->node; link->state &= ~DEV_CONFIG_PENDING; + SET_NETDEV_DEV(dev, pcmcia_lookup_device(handle)); if ((err=register_netdev(dev))) { printk(KNOT_XIRC "register_netdev() failed\n"); --- linux-2.6.9-rc1/drivers/net/ppp_generic.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/drivers/net/ppp_generic.c 2004-09-02 20:51:52.000000000 -0700 @@ -1595,6 +1595,7 @@ ppp_receive_nonmp_frame(struct ppp *ppp, skb->dev = ppp->dev; skb->protocol = htons(npindex_to_ethertype[npi]); skb->mac.raw = skb->data; + skb->input_dev = ppp->dev; netif_rx(skb); ppp->dev->last_rx = jiffies; } --- linux-2.6.9-rc1/drivers/net/r8169.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/net/r8169.c 2004-09-03 00:57:13.100047752 -0700 @@ -6,6 +6,7 @@ History: Feb 4 2002 - created initially by ShuChen . May 20 2002 - Add link status force-mode and TBI mode support. + 2004 - Massive updates. See kernel SCM system for details. ========================================================================= 1. [DEPRECATED: use ethtool instead] The media can be forced in 5 modes. Command: 'insmod r8169 media = SET_MEDIA' @@ -33,6 +34,13 @@ VERSION 1.2 <2002/11/30> - Copy mc_filter setup code from 8139cp (includes an optimization, and avoids set_bit use) +VERSION 1.6LK <2004/04/14> + + - Merge of Realtek's version 1.6 + - Conversion to DMA API + - Suspend/resume + - Endianness + - Misc Rx/Tx bugs */ #include @@ -42,13 +50,17 @@ VERSION 1.2 <2002/11/30> #include #include #include +#include #include +#include +#include +#include #include #include #include -#define RTL8169_VERSION "1.2" +#define RTL8169_VERSION "1.6LK" #define MODULENAME "r8169" #define RTL8169_DRIVER_NAME MODULENAME " Gigabit Ethernet driver " RTL8169_VERSION #define PFX MODULENAME ": " @@ -67,9 +79,11 @@ VERSION 1.2 <2002/11/30> #ifdef CONFIG_R8169_NAPI #define rtl8169_rx_skb netif_receive_skb +#define rtl8169_rx_hwaccel_skb vlan_hwaccel_rx #define rtl8169_rx_quota(count, quota) min(count, quota) #else #define rtl8169_rx_skb netif_rx +#define rtl8169_rx_hwaccel_skb vlan_hwaccel_receive_skb #define rtl8169_rx_quota(count, quota) count #endif @@ -87,9 +101,6 @@ static int multicast_filter_limit = 32; /* MAC address length*/ #define MAC_ADDR_LEN 6 -/* max supported gigabit ethernet frame size -- must be at least (dev->mtu+14+4).*/ -#define MAX_ETH_FRAME_SIZE 1536 - #define TX_FIFO_THRESH 256 /* In bytes */ #define RX_FIFO_THRESH 7 /* 7 means NO threshold, Rx buffer level before first PCI xfer. */ @@ -99,6 +110,7 @@ static int multicast_filter_limit = 32; #define RxPacketMaxSize 0x0800 /* Maximum size supported is 16K-1 */ #define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */ +#define R8169_REGS_SIZE 256 #define R8169_NAPI_WEIGHT 64 #define NUM_TX_DESC 64 /* Number of Tx descriptor registers */ #define NUM_RX_DESC 256 /* Number of Rx descriptor registers */ @@ -106,7 +118,6 @@ static int multicast_filter_limit = 32; #define R8169_TX_RING_BYTES (NUM_TX_DESC * sizeof(struct TxDesc)) #define R8169_RX_RING_BYTES (NUM_RX_DESC * sizeof(struct RxDesc)) -#define RTL_MIN_IO_SIZE 0x80 #define RTL8169_TX_TIMEOUT (6*HZ) #define RTL8169_PHY_TIMEOUT (10*HZ) @@ -303,26 +314,52 @@ enum RTL8169_register_content { }; enum _DescStatusBit { - OWNbit = 0x80000000, - EORbit = 0x40000000, - FSbit = 0x20000000, - LSbit = 0x10000000, + DescOwn = (1 << 31), /* Descriptor is owned by NIC */ + RingEnd = (1 << 30), /* End of descriptor ring */ + FirstFrag = (1 << 29), /* First segment of a packet */ + LastFrag = (1 << 28), /* Final segment of a packet */ + + /* Tx private */ + IPCS = (1 << 18), /* Calculate IP checksum */ + UDPCS = (1 << 17), /* Calculate UDP/IP checksum */ + TCPCS = (1 << 16), /* Calculate TCP/IP checksum */ + TxVlanTag = (1 << 17), /* Add VLAN tag */ + + /* Rx private */ + PID1 = (1 << 18), /* Protocol ID bit 1/2 */ + PID0 = (1 << 17), /* Protocol ID bit 2/2 */ + +#define RxProtoUDP (PID1) +#define RxProtoTCP (PID0) +#define RxProtoIP (PID1 | PID0) +#define RxProtoMask RxProtoIP + + IPFail = (1 << 16), /* IP checksum failed */ + UDPFail = (1 << 15), /* UDP/IP checksum failed */ + TCPFail = (1 << 14), /* TCP/IP checksum failed */ + RxVlanTag = (1 << 16), /* VLAN tag available */ }; #define RsvdMask 0x3fffc000 struct TxDesc { - u32 status; - u32 vlan_tag; + u32 opts1; + u32 opts2; u64 addr; }; struct RxDesc { - u32 status; - u32 vlan_tag; + u32 opts1; + u32 opts2; u64 addr; }; +struct ring_info { + struct sk_buff *skb; + u32 len; + u8 __pad[sizeof(void *) - sizeof(u32)]; +}; + struct rtl8169_private { void *mmio_addr; /* memory map physical address */ struct pci_dev *pci_dev; /* Index of PCI device */ @@ -340,13 +377,16 @@ struct rtl8169_private { dma_addr_t TxPhyAddr; dma_addr_t RxPhyAddr; struct sk_buff *Rx_skbuff[NUM_RX_DESC]; /* Rx data buffers */ - struct sk_buff *Tx_skbuff[NUM_TX_DESC]; /* Tx data buffers */ + struct ring_info tx_skb[NUM_TX_DESC]; /* Tx data buffers */ + unsigned rx_buf_sz; struct timer_list timer; u16 cp_cmd; u16 intr_mask; int phy_auto_nego_reg; int phy_1000_ctrl_reg; - +#ifdef CONFIG_R8169_VLAN + struct vlan_group *vlgrp; +#endif int (*set_speed)(struct net_device *, u8 autoneg, u16 speed, u8 duplex); void (*get_settings)(struct net_device *, struct ethtool_cmd *); void (*phy_reset_enable)(void *); @@ -508,6 +548,11 @@ static void rtl8169_get_drvinfo(struct n strcpy(info->bus_info, pci_name(tp->pci_dev)); } +static int rtl8169_get_regs_len(struct net_device *dev) +{ + return R8169_REGS_SIZE; +} + static int rtl8169_set_speed_tbi(struct net_device *dev, u8 autoneg, u16 speed, u8 duplex) { @@ -598,6 +643,105 @@ static int rtl8169_set_settings(struct n return ret; } +static u32 rtl8169_get_rx_csum(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + return !!(tp->cp_cmd & RxChkSum); +} + +static int rtl8169_set_rx_csum(struct net_device *dev, u32 data) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void *ioaddr = tp->mmio_addr; + unsigned long flags; + + spin_lock_irqsave(&tp->lock, flags); + + if (data) + tp->cp_cmd |= RxChkSum; + else + tp->cp_cmd &= ~RxChkSum; + + RTL_W16(CPlusCmd, tp->cp_cmd); + RTL_R16(CPlusCmd); + + spin_unlock_irqrestore(&tp->lock, flags); + + return 0; +} + +#ifdef CONFIG_R8169_VLAN + +static inline u32 rtl8169_tx_vlan_tag(struct rtl8169_private *tp, + struct sk_buff *skb) +{ + return (tp->vlgrp && vlan_tx_tag_present(skb)) ? + TxVlanTag | cpu_to_be16(vlan_tx_tag_get(skb)) : 0x00; +} + +static void rtl8169_vlan_rx_register(struct net_device *dev, + struct vlan_group *grp) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void *ioaddr = tp->mmio_addr; + unsigned long flags; + + spin_lock_irqsave(&tp->lock, flags); + tp->vlgrp = grp; + tp->cp_cmd |= RxVlan; + RTL_W16(CPlusCmd, tp->cp_cmd); + RTL_R16(CPlusCmd); + spin_unlock_irqrestore(&tp->lock, flags); +} + +static void rtl8169_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void *ioaddr = tp->mmio_addr; + unsigned long flags; + + spin_lock_irqsave(&tp->lock, flags); + tp->cp_cmd &= ~RxVlan; + RTL_W16(CPlusCmd, tp->cp_cmd); + RTL_R16(CPlusCmd); + if (tp->vlgrp) + tp->vlgrp->vlan_devices[vid] = NULL; + spin_unlock_irqrestore(&tp->lock, flags); +} + +static int rtl8169_rx_vlan_skb(struct rtl8169_private *tp, struct RxDesc *desc, + struct sk_buff *skb) +{ + u32 opts2 = desc->opts2; + int ret; + + if (tp->vlgrp && (opts2 & RxVlanTag)) { + rtl8169_rx_hwaccel_skb(skb, tp->vlgrp, + be16_to_cpu(opts2 & 0xffff)); + ret = 0; + } else + ret = -1; + desc->opts2 = 0; + return ret; +} + +#else /* !CONFIG_R8169_VLAN */ + +static inline u32 rtl8169_tx_vlan_tag(struct rtl8169_private *tp, + struct sk_buff *skb) +{ + return 0; +} + +static int rtl8169_rx_vlan_skb(struct rtl8169_private *tp, struct RxDesc *desc, + struct sk_buff *skb) +{ + return -1; +} + +#endif + static void rtl8169_gset_tbi(struct net_device *dev, struct ethtool_cmd *cmd) { struct rtl8169_private *tp = netdev_priv(dev); @@ -671,12 +815,33 @@ static int rtl8169_get_settings(struct n return 0; } +static void rtl8169_get_regs(struct net_device *dev, struct ethtool_regs *regs, + void *p) +{ + struct rtl8169_private *tp = netdev_priv(dev); + unsigned long flags; + + if (regs->len > R8169_REGS_SIZE) + regs->len = R8169_REGS_SIZE; + + spin_lock_irqsave(&tp->lock, flags); + memcpy_fromio(p, tp->mmio_addr, regs->len); + spin_unlock_irqrestore(&tp->lock, flags); +} static struct ethtool_ops rtl8169_ethtool_ops = { .get_drvinfo = rtl8169_get_drvinfo, + .get_regs_len = rtl8169_get_regs_len, .get_link = ethtool_op_get_link, .get_settings = rtl8169_get_settings, .set_settings = rtl8169_set_settings, + .get_rx_csum = rtl8169_get_rx_csum, + .set_rx_csum = rtl8169_set_rx_csum, + .get_tx_csum = ethtool_op_get_tx_csum, + .set_tx_csum = ethtool_op_set_tx_csum, + .get_sg = ethtool_op_get_sg, + .set_sg = ethtool_op_set_sg, + .get_regs = rtl8169_get_regs, }; static void rtl8169_write_gmii_reg_bit(void *ioaddr, int reg, int bitnum, @@ -907,28 +1072,31 @@ static inline void rtl8169_request_timer add_timer(timer); } +static void rtl8169_release_board(struct pci_dev *pdev, struct net_device *dev, + void *ioaddr) +{ + iounmap(ioaddr); + pci_release_regions(pdev); + pci_disable_device(pdev); + free_netdev(dev); +} + static int __devinit rtl8169_init_board(struct pci_dev *pdev, struct net_device **dev_out, void **ioaddr_out) { - void *ioaddr = NULL; + void *ioaddr; struct net_device *dev; struct rtl8169_private *tp; - unsigned long mmio_start, mmio_end, mmio_flags, mmio_len; - int rc, i, acpi_idle_state = 0, pm_cap; - + int rc = -ENOMEM, i, acpi_idle_state = 0, pm_cap; - assert(pdev != NULL); assert(ioaddr_out != NULL); - *ioaddr_out = NULL; - *dev_out = NULL; - // dev zeroed in alloc_etherdev dev = alloc_etherdev(sizeof (*tp)); if (dev == NULL) { printk(KERN_ERR PFX "unable to alloc new ethernet\n"); - return -ENOMEM; + goto err_out; } SET_MODULE_OWNER(dev); @@ -939,9 +1107,13 @@ rtl8169_init_board(struct pci_dev *pdev, rc = pci_enable_device(pdev); if (rc) { printk(KERN_ERR PFX "%s: enable failure\n", pdev->slot_name); - goto err_out; + goto err_out_free_dev; } + rc = pci_set_mwi(pdev); + if (rc < 0) + goto err_out_disable; + /* save power state before pci_enable_device overwrites it */ pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM); if (pm_cap) { @@ -952,41 +1124,38 @@ rtl8169_init_board(struct pci_dev *pdev, } else { printk(KERN_ERR PFX "Cannot find PowerManagement capability, aborting.\n"); - goto err_out_free_res; + goto err_out_mwi; } - mmio_start = pci_resource_start(pdev, 1); - mmio_end = pci_resource_end(pdev, 1); - mmio_flags = pci_resource_flags(pdev, 1); - mmio_len = pci_resource_len(pdev, 1); - // make sure PCI base addr 1 is MMIO - if (!(mmio_flags & IORESOURCE_MEM)) { + if (!(pci_resource_flags(pdev, 1) & IORESOURCE_MEM)) { printk(KERN_ERR PFX "region #1 not an MMIO resource, aborting\n"); rc = -ENODEV; - goto err_out_disable; + goto err_out_mwi; } // check for weird/broken PCI region reporting - if (mmio_len < RTL_MIN_IO_SIZE) { + if (pci_resource_len(pdev, 1) < R8169_REGS_SIZE) { printk(KERN_ERR PFX "Invalid PCI region size(s), aborting\n"); rc = -ENODEV; - goto err_out_disable; + goto err_out_mwi; } rc = pci_request_regions(pdev, MODULENAME); if (rc) { printk(KERN_ERR PFX "%s: could not request regions.\n", pdev->slot_name); - goto err_out_disable; + goto err_out_mwi; } tp->cp_cmd = PCIMulRW | RxChkSum; - if ((sizeof(dma_addr_t) > 32) && - !pci_set_dma_mask(pdev, DMA_64BIT_MASK)) + dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; + + if ((sizeof(dma_addr_t) > 4) && !pci_set_dma_mask(pdev, DMA_64BIT_MASK)) { tp->cp_cmd |= PCIDAC; - else { + dev->features |= NETIF_F_HIGHDMA; + } else { rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); if (rc < 0) { printk(KERN_ERR PFX "DMA configuration failed.\n"); @@ -994,12 +1163,10 @@ rtl8169_init_board(struct pci_dev *pdev, } } - - // enable PCI bus-mastering pci_set_master(pdev); // ioremap MMIO region - ioaddr = ioremap(mmio_start, mmio_len); + ioaddr = ioremap(pci_resource_start(pdev, 1), R8169_REGS_SIZE); if (ioaddr == NULL) { printk(KERN_ERR PFX "cannot remap MMIO, aborting\n"); rc = -EIO; @@ -1036,26 +1203,35 @@ rtl8169_init_board(struct pci_dev *pdev, } tp->chipset = i; + tp->rx_buf_sz = RX_BUF_SIZE; + *ioaddr_out = ioaddr; *dev_out = dev; - return 0; +out: + return rc; err_out_free_res: pci_release_regions(pdev); +err_out_mwi: + pci_clear_mwi(pdev); + err_out_disable: pci_disable_device(pdev); -err_out: +err_out_free_dev: free_netdev(dev); - return rc; +err_out: + *ioaddr_out = NULL; + *dev_out = NULL; + goto out; } static int __devinit rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { struct net_device *dev = NULL; - struct rtl8169_private *tp = NULL; + struct rtl8169_private *tp; void *ioaddr = NULL; static int board_idx = -1; static int printed_version = 0; @@ -1079,8 +1255,6 @@ rtl8169_init_one(struct pci_dev *pdev, c tp = dev->priv; assert(ioaddr != NULL); - assert(dev != NULL); - assert(tp != NULL); if (RTL_R8(PHYstatus) & TBI_Enable) { tp->set_speed = rtl8169_set_speed_tbi; @@ -1105,18 +1279,26 @@ rtl8169_init_one(struct pci_dev *pdev, c dev->open = rtl8169_open; dev->hard_start_xmit = rtl8169_start_xmit; dev->get_stats = rtl8169_get_stats; - dev->ethtool_ops = &rtl8169_ethtool_ops; + SET_ETHTOOL_OPS(dev, &rtl8169_ethtool_ops); dev->stop = rtl8169_close; dev->tx_timeout = rtl8169_tx_timeout; dev->set_multicast_list = rtl8169_set_rx_mode; dev->watchdog_timeo = RTL8169_TX_TIMEOUT; dev->irq = pdev->irq; dev->base_addr = (unsigned long) ioaddr; + #ifdef CONFIG_R8169_NAPI dev->poll = rtl8169_poll; dev->weight = R8169_NAPI_WEIGHT; printk(KERN_INFO PFX "NAPI enabled\n"); #endif + +#ifdef CONFIG_R8169_VLAN + dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; + dev->vlan_rx_register = rtl8169_vlan_rx_register; + dev->vlan_rx_kill_vid = rtl8169_vlan_rx_kill_vid; +#endif + tp->intr_mask = 0xffff; tp->pci_dev = pdev; tp->mmio_addr = ioaddr; @@ -1125,10 +1307,7 @@ rtl8169_init_one(struct pci_dev *pdev, c rc = register_netdev(dev); if (rc) { - iounmap(ioaddr); - pci_release_regions(pdev); - pci_disable_device(pdev); - free_netdev(dev); + rtl8169_release_board(pdev, dev, ioaddr); return rc; } @@ -1184,11 +1363,7 @@ rtl8169_remove_one(struct pci_dev *pdev) assert(tp != NULL); unregister_netdev(dev); - iounmap(tp->mmio_addr); - pci_release_regions(pdev); - - pci_disable_device(pdev); - free_netdev(dev); + rtl8169_release_board(pdev, dev, tp->mmio_addr); pci_set_drvdata(pdev, NULL); } @@ -1354,49 +1529,51 @@ rtl8169_hw_start(struct net_device *dev) static inline void rtl8169_make_unusable_by_asic(struct RxDesc *desc) { desc->addr = 0x0badbadbadbadbadull; - desc->status &= ~cpu_to_le32(OWNbit | RsvdMask); + desc->opts1 &= ~cpu_to_le32(DescOwn | RsvdMask); } -static void rtl8169_free_rx_skb(struct pci_dev *pdev, struct sk_buff **sk_buff, - struct RxDesc *desc) +static void rtl8169_free_rx_skb(struct rtl8169_private *tp, + struct sk_buff **sk_buff, struct RxDesc *desc) { - pci_unmap_single(pdev, le64_to_cpu(desc->addr), RX_BUF_SIZE, + struct pci_dev *pdev = tp->pci_dev; + + pci_unmap_single(pdev, le64_to_cpu(desc->addr), tp->rx_buf_sz, PCI_DMA_FROMDEVICE); dev_kfree_skb(*sk_buff); *sk_buff = NULL; rtl8169_make_unusable_by_asic(desc); } -static inline void rtl8169_return_to_asic(struct RxDesc *desc) +static inline void rtl8169_return_to_asic(struct RxDesc *desc, int rx_buf_sz) { - desc->status |= cpu_to_le32(OWNbit + RX_BUF_SIZE); + desc->opts1 |= cpu_to_le32(DescOwn + rx_buf_sz); } -static inline void rtl8169_give_to_asic(struct RxDesc *desc, dma_addr_t mapping) +static inline void rtl8169_give_to_asic(struct RxDesc *desc, dma_addr_t mapping, + int rx_buf_sz) { desc->addr = cpu_to_le64(mapping); - desc->status |= cpu_to_le32(OWNbit + RX_BUF_SIZE); + desc->opts1 |= cpu_to_le32(DescOwn + rx_buf_sz); } -static int rtl8169_alloc_rx_skb(struct pci_dev *pdev, struct net_device *dev, - struct sk_buff **sk_buff, struct RxDesc *desc) +static int rtl8169_alloc_rx_skb(struct pci_dev *pdev, struct sk_buff **sk_buff, + struct RxDesc *desc, int rx_buf_sz) { struct sk_buff *skb; dma_addr_t mapping; int ret = 0; - skb = dev_alloc_skb(RX_BUF_SIZE); + skb = dev_alloc_skb(rx_buf_sz); if (!skb) goto err_out; - skb->dev = dev; skb_reserve(skb, 2); *sk_buff = skb; - mapping = pci_map_single(pdev, skb->tail, RX_BUF_SIZE, + mapping = pci_map_single(pdev, skb->tail, rx_buf_sz, PCI_DMA_FROMDEVICE); - rtl8169_give_to_asic(desc, mapping); + rtl8169_give_to_asic(desc, mapping, rx_buf_sz); out: return ret; @@ -1413,7 +1590,7 @@ static void rtl8169_rx_clear(struct rtl8 for (i = 0; i < NUM_RX_DESC; i++) { if (tp->Rx_skbuff[i]) { - rtl8169_free_rx_skb(tp->pci_dev, tp->Rx_skbuff + i, + rtl8169_free_rx_skb(tp, tp->Rx_skbuff + i, tp->RxDescArray + i); } } @@ -1430,8 +1607,8 @@ static u32 rtl8169_rx_fill(struct rtl816 if (tp->Rx_skbuff[i]) continue; - ret = rtl8169_alloc_rx_skb(tp->pci_dev, dev, tp->Rx_skbuff + i, - tp->RxDescArray + i); + ret = rtl8169_alloc_rx_skb(tp->pci_dev, tp->Rx_skbuff + i, + tp->RxDescArray + i, tp->rx_buf_sz); if (ret < 0) break; } @@ -1440,7 +1617,7 @@ static u32 rtl8169_rx_fill(struct rtl816 static inline void rtl8169_mark_as_last_descriptor(struct RxDesc *desc) { - desc->status |= cpu_to_le32(EORbit); + desc->opts1 |= cpu_to_le32(RingEnd); } static int rtl8169_init_ring(struct net_device *dev) @@ -1449,10 +1626,8 @@ static int rtl8169_init_ring(struct net_ tp->cur_rx = tp->dirty_rx = 0; tp->cur_tx = tp->dirty_tx = 0; - memset(tp->TxDescArray, 0x0, NUM_TX_DESC * sizeof (struct TxDesc)); - memset(tp->RxDescArray, 0x0, NUM_RX_DESC * sizeof (struct RxDesc)); - memset(tp->Tx_skbuff, 0x0, NUM_TX_DESC * sizeof(struct sk_buff *)); + memset(tp->tx_skb, 0x0, NUM_TX_DESC * sizeof(struct ring_info)); memset(tp->Rx_skbuff, 0x0, NUM_RX_DESC * sizeof(struct sk_buff *)); if (rtl8169_rx_fill(tp, dev, 0, NUM_RX_DESC) != NUM_RX_DESC) @@ -1467,33 +1642,39 @@ err_out: return -ENOMEM; } -static void rtl8169_unmap_tx_skb(struct pci_dev *pdev, struct sk_buff **sk_buff, +static void rtl8169_unmap_tx_skb(struct pci_dev *pdev, struct ring_info *tx_skb, struct TxDesc *desc) { - u32 len = sk_buff[0]->len; + unsigned int len = tx_skb->len; - pci_unmap_single(pdev, le64_to_cpu(desc->addr), - len < ETH_ZLEN ? ETH_ZLEN : len, PCI_DMA_TODEVICE); + pci_unmap_single(pdev, le64_to_cpu(desc->addr), len, PCI_DMA_TODEVICE); + desc->opts2 = 0x00; desc->addr = 0x00; - *sk_buff = NULL; + tx_skb->len = 0; } -static void -rtl8169_tx_clear(struct rtl8169_private *tp) +static void rtl8169_tx_clear(struct rtl8169_private *tp) { - int i; + unsigned int i; - tp->cur_tx = 0; - for (i = 0; i < NUM_TX_DESC; i++) { - struct sk_buff *skb = tp->Tx_skbuff[i]; + for (i = tp->dirty_tx; i < tp->dirty_tx + NUM_TX_DESC; i++) { + unsigned int entry = i % NUM_TX_DESC; + struct ring_info *tx_skb = tp->tx_skb + entry; + unsigned int len = tx_skb->len; - if (skb) { - rtl8169_unmap_tx_skb(tp->pci_dev, tp->Tx_skbuff + i, - tp->TxDescArray + i); - dev_kfree_skb(skb); + if (len) { + struct sk_buff *skb = tx_skb->skb; + + rtl8169_unmap_tx_skb(tp->pci_dev, tx_skb, + tp->TxDescArray + entry); + if (skb) { + dev_kfree_skb(skb); + tx_skb->skb = NULL; + } tp->stats.tx_dropped++; } } + tp->cur_tx = tp->dirty_tx = 0; } static void @@ -1523,51 +1704,122 @@ rtl8169_tx_timeout(struct net_device *de netif_wake_queue(dev); } -static int -rtl8169_start_xmit(struct sk_buff *skb, struct net_device *dev) +static int rtl8169_xmit_frags(struct rtl8169_private *tp, struct sk_buff *skb, + u32 opts1) +{ + struct skb_shared_info *info = skb_shinfo(skb); + unsigned int cur_frag, entry; + struct TxDesc *txd; + + entry = tp->cur_tx; + for (cur_frag = 0; cur_frag < info->nr_frags; cur_frag++) { + skb_frag_t *frag = info->frags + cur_frag; + dma_addr_t mapping; + u32 status, len; + void *addr; + + entry = (entry + 1) % NUM_TX_DESC; + + txd = tp->TxDescArray + entry; + len = frag->size; + addr = ((void *) page_address(frag->page)) + frag->page_offset; + mapping = pci_map_single(tp->pci_dev, addr, len, PCI_DMA_TODEVICE); + + /* anti gcc 2.95.3 bugware (sic) */ + status = opts1 | len | (RingEnd * !((entry + 1) % NUM_TX_DESC)); + + txd->opts1 = cpu_to_le32(status); + txd->addr = cpu_to_le64(mapping); + + tp->tx_skb[entry].len = len; + } + + if (cur_frag) { + tp->tx_skb[entry].skb = skb; + txd->opts1 |= cpu_to_le32(LastFrag); + } + + return cur_frag; +} + +static inline u32 rtl8169_tx_csum(struct sk_buff *skb) +{ + if (skb->ip_summed == CHECKSUM_HW) { + const struct iphdr *ip = skb->nh.iph; + + if (ip->protocol == IPPROTO_TCP) + return IPCS | TCPCS; + else if (ip->protocol == IPPROTO_UDP) + return IPCS | UDPCS; + BUG(); + } + return 0; +} + +static int rtl8169_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct rtl8169_private *tp = netdev_priv(dev); + unsigned int frags, entry = tp->cur_tx % NUM_TX_DESC; + struct TxDesc *txd = tp->TxDescArray + entry; void *ioaddr = tp->mmio_addr; - unsigned int entry = tp->cur_tx % NUM_TX_DESC; - u32 len = skb->len; + dma_addr_t mapping; + u32 status, len; + u32 opts1; + + if (unlikely(tp->cur_tx - tp->dirty_tx < skb_shinfo(skb)->nr_frags)) { + netif_stop_queue(dev); + printk(KERN_ERR PFX "%s: BUG! Tx Ring full when queue awake!\n", + dev->name); + return 1; + } + + if (unlikely(le32_to_cpu(txd->opts1) & DescOwn)) + goto err_drop; + + opts1 = DescOwn | rtl8169_tx_csum(skb); + + frags = rtl8169_xmit_frags(tp, skb, opts1); + if (frags) { + len = skb_headlen(skb); + opts1 |= FirstFrag; + } else { + len = skb->len; + + if (unlikely(len < ETH_ZLEN)) { + skb = skb_padto(skb, ETH_ZLEN); + if (!skb) + goto err_update_stats; + len = ETH_ZLEN; + } - if (unlikely(skb->len < ETH_ZLEN)) { - skb = skb_padto(skb, ETH_ZLEN); - if (!skb) - goto err_update_stats; - len = ETH_ZLEN; + opts1 |= FirstFrag | LastFrag; + tp->tx_skb[entry].skb = skb; } - - if (!(le32_to_cpu(tp->TxDescArray[entry].status) & OWNbit)) { - dma_addr_t mapping; - u32 status; - mapping = pci_map_single(tp->pci_dev, skb->data, len, - PCI_DMA_TODEVICE); + mapping = pci_map_single(tp->pci_dev, skb->data, len, PCI_DMA_TODEVICE); - tp->Tx_skbuff[entry] = skb; - tp->TxDescArray[entry].addr = cpu_to_le64(mapping); + tp->tx_skb[entry].len = len; + txd->addr = cpu_to_le64(mapping); + txd->opts2 = rtl8169_tx_vlan_tag(tp, skb); - /* anti gcc 2.95.3 bugware */ - status = OWNbit | FSbit | LSbit | len | - (EORbit * !((entry + 1) % NUM_TX_DESC)); - tp->TxDescArray[entry].status = cpu_to_le32(status); - - RTL_W8(TxPoll, 0x40); //set polling bit + wmb(); - dev->trans_start = jiffies; + /* anti gcc 2.95.3 bugware (sic) */ + status = opts1 | len | (RingEnd * !((entry + 1) % NUM_TX_DESC)); + txd->opts1 = cpu_to_le32(status); - tp->cur_tx++; - smp_wmb(); - } else - goto err_drop; + dev->trans_start = jiffies; - if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx) { - u32 dirty = tp->dirty_tx; - + tp->cur_tx += frags + 1; + + smp_wmb(); + + RTL_W8(TxPoll, 0x40); //set polling bit + + if (tp->cur_tx - tp->dirty_tx < MAX_SKB_FRAGS) { netif_stop_queue(dev); smp_rmb(); - if (dirty != tp->dirty_tx) + if (tp->cur_tx - tp->dirty_tx >= MAX_SKB_FRAGS) netif_wake_queue(dev); } @@ -1597,22 +1849,24 @@ rtl8169_tx_interrupt(struct net_device * while (tx_left > 0) { unsigned int entry = dirty_tx % NUM_TX_DESC; - struct sk_buff *skb = tp->Tx_skbuff[entry]; + struct ring_info *tx_skb = tp->tx_skb + entry; + u32 len = tx_skb->len; u32 status; rmb(); - status = le32_to_cpu(tp->TxDescArray[entry].status); - if (status & OWNbit) + status = le32_to_cpu(tp->TxDescArray[entry].opts1); + if (status & DescOwn) break; - /* FIXME: is it really accurate for TxErr ? */ - tp->stats.tx_bytes += skb->len >= ETH_ZLEN ? - skb->len : ETH_ZLEN; + tp->stats.tx_bytes += len; tp->stats.tx_packets++; - rtl8169_unmap_tx_skb(tp->pci_dev, tp->Tx_skbuff + entry, - tp->TxDescArray + entry); - dev_kfree_skb_irq(skb); - tp->Tx_skbuff[entry] = NULL; + + rtl8169_unmap_tx_skb(tp->pci_dev, tx_skb, tp->TxDescArray + entry); + + if (status & LastFrag) { + dev_kfree_skb_irq(tx_skb->skb); + tx_skb->skb = NULL; + } dirty_tx++; tx_left--; } @@ -1625,9 +1879,21 @@ rtl8169_tx_interrupt(struct net_device * } } +static inline void rtl8169_rx_csum(struct sk_buff *skb, struct RxDesc *desc) +{ + u32 opts1 = desc->opts1; + u32 status = opts1 & RxProtoMask; + + if (((status == RxProtoTCP) && !(opts1 & TCPFail)) || + ((status == RxProtoUDP) && !(opts1 & UDPFail)) || + ((status == RxProtoIP) && !(opts1 & IPFail))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb->ip_summed = CHECKSUM_NONE; +} + static inline int rtl8169_try_rx_copy(struct sk_buff **sk_buff, int pkt_size, - struct RxDesc *desc, - struct net_device *dev) + struct RxDesc *desc, int rx_buf_sz) { int ret = -1; @@ -1636,11 +1902,10 @@ static inline int rtl8169_try_rx_copy(st skb = dev_alloc_skb(pkt_size + 2); if (skb) { - skb->dev = dev; skb_reserve(skb, 2); eth_copy_and_sum(skb, sk_buff[0]->tail, pkt_size, 0); *sk_buff = skb; - rtl8169_return_to_asic(desc); + rtl8169_return_to_asic(desc, rx_buf_sz); ret = 0; } } @@ -1667,9 +1932,9 @@ rtl8169_rx_interrupt(struct net_device * u32 status; rmb(); - status = le32_to_cpu(tp->RxDescArray[entry].status); + status = le32_to_cpu(tp->RxDescArray[entry].opts1); - if (status & OWNbit) + if (status & DescOwn) break; if (status & RxRES) { printk(KERN_INFO "%s: Rx ERROR!!!\n", dev->name); @@ -1685,22 +1950,27 @@ rtl8169_rx_interrupt(struct net_device * void (*pci_action)(struct pci_dev *, dma_addr_t, size_t, int) = pci_dma_sync_single_for_device; - + rtl8169_rx_csum(skb, desc); + pci_dma_sync_single_for_cpu(tp->pci_dev, - le64_to_cpu(desc->addr), RX_BUF_SIZE, + le64_to_cpu(desc->addr), tp->rx_buf_sz, PCI_DMA_FROMDEVICE); - if (rtl8169_try_rx_copy(&skb, pkt_size, desc, dev)) { + if (rtl8169_try_rx_copy(&skb, pkt_size, desc, + tp->rx_buf_sz)) { pci_action = pci_unmap_single; tp->Rx_skbuff[entry] = NULL; } pci_action(tp->pci_dev, le64_to_cpu(desc->addr), - RX_BUF_SIZE, PCI_DMA_FROMDEVICE); + tp->rx_buf_sz, PCI_DMA_FROMDEVICE); + skb->dev = dev; skb_put(skb, pkt_size); skb->protocol = eth_type_trans(skb, dev); - rtl8169_rx_skb(skb); + + if (rtl8169_rx_vlan_skb(tp, desc, skb) < 0) + rtl8169_rx_skb(skb); dev->last_rx = jiffies; tp->stats.rx_bytes += pkt_size; --- linux-2.6.9-rc1/drivers/net/rrunner.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/net/rrunner.c 2004-09-02 20:51:52.000000000 -0700 @@ -1139,6 +1139,49 @@ static irqreturn_t rr_interrupt(int irq, return IRQ_HANDLED; } +static inline void rr_raz_tx(struct rr_private *rrpriv, + struct net_device *dev) +{ + int i; + + for (i = 0; i < TX_RING_ENTRIES; i++) { + struct sk_buff *skb = rrpriv->tx_skbuff[i]; + + if (skb) { + struct tx_desc *desc = &(rrpriv->tx_ring[i]); + + pci_unmap_single(rrpriv->pci_dev, desc->addr.addrlo, + skb->len, PCI_DMA_TODEVICE); + desc->size = 0; + set_rraddr(&desc->addr, 0); + dev_kfree_skb(skb); + rrpriv->tx_skbuff[i] = NULL; + } + } +} + + +static inline void rr_raz_rx(struct rr_private *rrpriv, + struct net_device *dev) +{ + int i; + + for (i = 0; i < RX_RING_ENTRIES; i++) { + struct sk_buff *skb = rrpriv->rx_skbuff[i]; + + if (skb) { + struct rx_desc *desc = &(rrpriv->rx_ring[i]); + + pci_unmap_single(rrpriv->pci_dev, desc->addr.addrlo, + dev->mtu + HIPPI_HLEN, PCI_DMA_FROMDEVICE); + desc->size = 0; + set_rraddr(&desc->addr, 0); + dev_kfree_skb(skb); + rrpriv->rx_skbuff[i] = NULL; + } + } +} + static void rr_timer(unsigned long data) { struct net_device *dev = (struct net_device *)data; @@ -1254,49 +1297,6 @@ static int rr_open(struct net_device *de } -static inline void rr_raz_tx(struct rr_private *rrpriv, - struct net_device *dev) -{ - int i; - - for (i = 0; i < TX_RING_ENTRIES; i++) { - struct sk_buff *skb = rrpriv->tx_skbuff[i]; - - if (skb) { - struct tx_desc *desc = &(rrpriv->tx_ring[i]); - - pci_unmap_single(rrpriv->pci_dev, desc->addr.addrlo, - skb->len, PCI_DMA_TODEVICE); - desc->size = 0; - set_rraddr(&desc->addr, 0); - dev_kfree_skb(skb); - rrpriv->tx_skbuff[i] = NULL; - } - } -} - - -static inline void rr_raz_rx(struct rr_private *rrpriv, - struct net_device *dev) -{ - int i; - - for (i = 0; i < RX_RING_ENTRIES; i++) { - struct sk_buff *skb = rrpriv->rx_skbuff[i]; - - if (skb) { - struct rx_desc *desc = &(rrpriv->rx_ring[i]); - - pci_unmap_single(rrpriv->pci_dev, desc->addr.addrlo, - dev->mtu + HIPPI_HLEN, PCI_DMA_FROMDEVICE); - desc->size = 0; - set_rraddr(&desc->addr, 0); - dev_kfree_skb(skb); - rrpriv->rx_skbuff[i] = NULL; - } - } -} - static void rr_dump(struct net_device *dev) { struct rr_private *rrpriv; --- linux-2.6.9-rc1/drivers/net/sis900.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/drivers/net/sis900.c 2004-09-02 20:51:52.000000000 -0700 @@ -124,6 +124,7 @@ static struct mii_chip_info { { "AMD 79C901 HomePNA PHY", 0x0000, 0x6B90, HOME}, { "ICS LAN PHY", 0x0015, 0xF440, LAN }, { "NS 83851 PHY", 0x2000, 0x5C20, MIX }, + { "NS 83847 PHY", 0x2000, 0x5C30, MIX }, { "Realtek RTL8201 PHY", 0x0000, 0x8200, LAN }, { "VIA 6103 PHY", 0x0101, 0x8f20, LAN }, {NULL,}, --- linux-2.6.9-rc1/drivers/net/sk98lin/skge.c 2004-08-24 00:01:55.000000000 -0700 +++ 25/drivers/net/sk98lin/skge.c 2004-09-02 20:51:52.000000000 -0700 @@ -110,10 +110,7 @@ #include #include - -#ifdef CONFIG_PROC_FS #include -#endif #include "h/skdrv1st.h" #include "h/skdrv2nd.h" @@ -5113,9 +5110,12 @@ static void __devexit skge_remove_one(st if ((pAC->GIni.GIMacsFound == 2) && pAC->RlmtNets == 2) have_second_mac = 1; + remove_proc_entry(dev->name, pSkRootDir); unregister_netdev(dev); - if (have_second_mac) + if (have_second_mac) { + remove_proc_entry(pAC->dev[1]->name, pSkRootDir); unregister_netdev(pAC->dev[1]); + } SkGeYellowLED(pAC, pAC->IoBase, 0); @@ -5182,9 +5182,9 @@ static int __init skge_init(void) { int error; +#ifdef CONFIG_PROC_FS memcpy(&SK_Root_Dir_entry, BOOT_STRING, sizeof(SK_Root_Dir_entry) - 1); -#ifdef CONFIG_PROC_FS pSkRootDir = proc_mkdir(SK_Root_Dir_entry, proc_net); if (!pSkRootDir) { printk(KERN_WARNING "Unable to create /proc/net/%s", --- linux-2.6.9-rc1/drivers/net/sk_mca.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/drivers/net/sk_mca.c 2004-09-03 00:56:32.578208008 -0700 @@ -972,14 +972,6 @@ static struct net_device_stats *skmca_st return &(priv->stat); } -/* we don't support runtime reconfiguration, since an MCA card can - be unambigously identified by its POS registers. */ - -static int skmca_config(struct net_device *dev, struct ifmap *map) -{ - return 0; -} - /* switch receiver mode. We use the LANCE's multicast filter to prefilter multicast addresses. */ @@ -1147,7 +1139,6 @@ struct net_device * __init skmca_probe(i /* set methods */ dev->open = skmca_open; dev->stop = skmca_close; - dev->set_config = skmca_config; dev->hard_start_xmit = skmca_tx; dev->do_ioctl = NULL; dev->get_stats = skmca_stats; --- linux-2.6.9-rc1/drivers/net/smc9194.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/net/smc9194.c 2004-09-02 20:51:52.000000000 -0700 @@ -1191,133 +1191,6 @@ static void smc_timeout(struct net_devic netif_wake_queue(dev); } -/*-------------------------------------------------------------------- - . - . This is the main routine of the driver, to handle the device when - . it needs some attention. - . - . So: - . first, save state of the chipset - . branch off into routines to handle each case, and acknowledge - . each to the interrupt register - . and finally restore state. - . - ---------------------------------------------------------------------*/ - -static irqreturn_t smc_interrupt(int irq, void * dev_id, struct pt_regs * regs) -{ - struct net_device *dev = dev_id; - int ioaddr = dev->base_addr; - struct smc_local *lp = netdev_priv(dev); - - byte status; - word card_stats; - byte mask; - int timeout; - /* state registers */ - word saved_bank; - word saved_pointer; - int handled = 0; - - - PRINTK3((CARDNAME": SMC interrupt started \n")); - - saved_bank = inw( ioaddr + BANK_SELECT ); - - SMC_SELECT_BANK(2); - saved_pointer = inw( ioaddr + POINTER ); - - mask = inb( ioaddr + INT_MASK ); - /* clear all interrupts */ - outb( 0, ioaddr + INT_MASK ); - - - /* set a timeout value, so I don't stay here forever */ - timeout = 4; - - PRINTK2((KERN_WARNING CARDNAME ": MASK IS %x \n", mask )); - do { - /* read the status flag, and mask it */ - status = inb( ioaddr + INTERRUPT ) & mask; - if (!status ) - break; - - handled = 1; - - PRINTK3((KERN_WARNING CARDNAME - ": Handling interrupt status %x \n", status )); - - if (status & IM_RCV_INT) { - /* Got a packet(s). */ - PRINTK2((KERN_WARNING CARDNAME - ": Receive Interrupt\n")); - smc_rcv(dev); - } else if (status & IM_TX_INT ) { - PRINTK2((KERN_WARNING CARDNAME - ": TX ERROR handled\n")); - smc_tx(dev); - outb(IM_TX_INT, ioaddr + INTERRUPT ); - } else if (status & IM_TX_EMPTY_INT ) { - /* update stats */ - SMC_SELECT_BANK( 0 ); - card_stats = inw( ioaddr + COUNTER ); - /* single collisions */ - lp->stats.collisions += card_stats & 0xF; - card_stats >>= 4; - /* multiple collisions */ - lp->stats.collisions += card_stats & 0xF; - - /* these are for when linux supports these statistics */ - - SMC_SELECT_BANK( 2 ); - PRINTK2((KERN_WARNING CARDNAME - ": TX_BUFFER_EMPTY handled\n")); - outb( IM_TX_EMPTY_INT, ioaddr + INTERRUPT ); - mask &= ~IM_TX_EMPTY_INT; - lp->stats.tx_packets += lp->packets_waiting; - lp->packets_waiting = 0; - - } else if (status & IM_ALLOC_INT ) { - PRINTK2((KERN_DEBUG CARDNAME - ": Allocation interrupt \n")); - /* clear this interrupt so it doesn't happen again */ - mask &= ~IM_ALLOC_INT; - - smc_hardware_send_packet( dev ); - - /* enable xmit interrupts based on this */ - mask |= ( IM_TX_EMPTY_INT | IM_TX_INT ); - - /* and let the card send more packets to me */ - netif_wake_queue(dev); - - PRINTK2((CARDNAME": Handoff done successfully.\n")); - } else if (status & IM_RX_OVRN_INT ) { - lp->stats.rx_errors++; - lp->stats.rx_fifo_errors++; - outb( IM_RX_OVRN_INT, ioaddr + INTERRUPT ); - } else if (status & IM_EPH_INT ) { - PRINTK((CARDNAME ": UNSUPPORTED: EPH INTERRUPT \n")); - } else if (status & IM_ERCV_INT ) { - PRINTK((CARDNAME ": UNSUPPORTED: ERCV INTERRUPT \n")); - outb( IM_ERCV_INT, ioaddr + INTERRUPT ); - } - } while ( timeout -- ); - - - /* restore state register */ - SMC_SELECT_BANK( 2 ); - outb( mask, ioaddr + INT_MASK ); - - PRINTK3(( KERN_WARNING CARDNAME ": MASK is now %x \n", mask )); - outw( saved_pointer, ioaddr + POINTER ); - - SMC_SELECT_BANK( saved_bank ); - - PRINTK3((CARDNAME ": Interrupt done\n")); - return IRQ_RETVAL(handled); -} - /*------------------------------------------------------------- . . smc_rcv - receive a packet from the card @@ -1509,6 +1382,134 @@ static void smc_tx( struct net_device * return; } +/*-------------------------------------------------------------------- + . + . This is the main routine of the driver, to handle the device when + . it needs some attention. + . + . So: + . first, save state of the chipset + . branch off into routines to handle each case, and acknowledge + . each to the interrupt register + . and finally restore state. + . + ---------------------------------------------------------------------*/ + +static irqreturn_t smc_interrupt(int irq, void * dev_id, struct pt_regs * regs) +{ + struct net_device *dev = dev_id; + int ioaddr = dev->base_addr; + struct smc_local *lp = netdev_priv(dev); + + byte status; + word card_stats; + byte mask; + int timeout; + /* state registers */ + word saved_bank; + word saved_pointer; + int handled = 0; + + + PRINTK3((CARDNAME": SMC interrupt started \n")); + + saved_bank = inw( ioaddr + BANK_SELECT ); + + SMC_SELECT_BANK(2); + saved_pointer = inw( ioaddr + POINTER ); + + mask = inb( ioaddr + INT_MASK ); + /* clear all interrupts */ + outb( 0, ioaddr + INT_MASK ); + + + /* set a timeout value, so I don't stay here forever */ + timeout = 4; + + PRINTK2((KERN_WARNING CARDNAME ": MASK IS %x \n", mask )); + do { + /* read the status flag, and mask it */ + status = inb( ioaddr + INTERRUPT ) & mask; + if (!status ) + break; + + handled = 1; + + PRINTK3((KERN_WARNING CARDNAME + ": Handling interrupt status %x \n", status )); + + if (status & IM_RCV_INT) { + /* Got a packet(s). */ + PRINTK2((KERN_WARNING CARDNAME + ": Receive Interrupt\n")); + smc_rcv(dev); + } else if (status & IM_TX_INT ) { + PRINTK2((KERN_WARNING CARDNAME + ": TX ERROR handled\n")); + smc_tx(dev); + outb(IM_TX_INT, ioaddr + INTERRUPT ); + } else if (status & IM_TX_EMPTY_INT ) { + /* update stats */ + SMC_SELECT_BANK( 0 ); + card_stats = inw( ioaddr + COUNTER ); + /* single collisions */ + lp->stats.collisions += card_stats & 0xF; + card_stats >>= 4; + /* multiple collisions */ + lp->stats.collisions += card_stats & 0xF; + + /* these are for when linux supports these statistics */ + + SMC_SELECT_BANK( 2 ); + PRINTK2((KERN_WARNING CARDNAME + ": TX_BUFFER_EMPTY handled\n")); + outb( IM_TX_EMPTY_INT, ioaddr + INTERRUPT ); + mask &= ~IM_TX_EMPTY_INT; + lp->stats.tx_packets += lp->packets_waiting; + lp->packets_waiting = 0; + + } else if (status & IM_ALLOC_INT ) { + PRINTK2((KERN_DEBUG CARDNAME + ": Allocation interrupt \n")); + /* clear this interrupt so it doesn't happen again */ + mask &= ~IM_ALLOC_INT; + + smc_hardware_send_packet( dev ); + + /* enable xmit interrupts based on this */ + mask |= ( IM_TX_EMPTY_INT | IM_TX_INT ); + + /* and let the card send more packets to me */ + netif_wake_queue(dev); + + PRINTK2((CARDNAME": Handoff done successfully.\n")); + } else if (status & IM_RX_OVRN_INT ) { + lp->stats.rx_errors++; + lp->stats.rx_fifo_errors++; + outb( IM_RX_OVRN_INT, ioaddr + INTERRUPT ); + } else if (status & IM_EPH_INT ) { + PRINTK((CARDNAME ": UNSUPPORTED: EPH INTERRUPT \n")); + } else if (status & IM_ERCV_INT ) { + PRINTK((CARDNAME ": UNSUPPORTED: ERCV INTERRUPT \n")); + outb( IM_ERCV_INT, ioaddr + INTERRUPT ); + } + } while ( timeout -- ); + + + /* restore state register */ + SMC_SELECT_BANK( 2 ); + outb( mask, ioaddr + INT_MASK ); + + PRINTK3(( KERN_WARNING CARDNAME ": MASK is now %x \n", mask )); + outw( saved_pointer, ioaddr + POINTER ); + + SMC_SELECT_BANK( saved_bank ); + + PRINTK3((CARDNAME ": Interrupt done\n")); + return IRQ_RETVAL(handled); +} + + /*---------------------------------------------------- . smc_close . --- linux-2.6.9-rc1/drivers/net/smc91x.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/net/smc91x.c 2004-09-02 20:51:52.000000000 -0700 @@ -2154,11 +2154,13 @@ static struct device_driver smc_driver = static int __init smc_init(void) { #ifdef MODULE +#ifdef CONFIG_ISA if (io == -1) printk(KERN_WARNING "%s: You shouldn't use auto-probing with insmod!\n", CARDNAME); #endif +#endif return driver_register(&smc_driver); } --- linux-2.6.9-rc1/drivers/net/smc91x.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/net/smc91x.h 2004-09-03 00:57:15.506681888 -0700 @@ -160,6 +160,49 @@ SMC_outw(u16 val, unsigned long ioaddr, #define SMC_insw(a, r, p, l) insw((a) + (r), p, l) #define SMC_outsw(a, r, p, l) outsw((a) + (r), p, l) +#elif defined(CONFIG_MACH_LPD7A400) || defined(CONFIG_MACH_LPD7A404) + +#include /* IOBARRIER_VIRT */ + +#define SMC_CAN_USE_8BIT 0 +#define SMC_CAN_USE_16BIT 1 +#define SMC_CAN_USE_32BIT 0 +#define SMC_NOWAIT 0 +#define SMC_IOBARRIER ({ barrier (); readl (IOBARRIER_VIRT); }) + +static inline unsigned short SMC_inw (unsigned long a, int r) +{ + unsigned short v; + v = readw (a + r); + SMC_IOBARRIER; + return v; +} + +static inline void SMC_outw (unsigned short v, unsigned long a, int r) +{ + writew (v, a + r); + SMC_IOBARRIER; +} + +static inline void SMC_insw (unsigned long a, int r, unsigned char* p, int l) +{ + while (l-- > 0) { + *((unsigned short*)p)++ = readw (a + r); + SMC_IOBARRIER; + } +} + +static inline void SMC_outsw (unsigned long a, int r, unsigned char* p, int l) +{ + while (l-- > 0) { + writew (*((unsigned short*)p)++, a + r); + SMC_IOBARRIER; + } +} + +#define RPC_LSA_DEFAULT RPC_LED_TX_RX +#define RPC_LSB_DEFAULT RPC_LED_100_10 + #else #define SMC_CAN_USE_8BIT 1 @@ -809,9 +852,10 @@ static const char * chip_ids[ 16 ] = { do { \ char *__ptr = (p); \ int __len = (l); \ - if (__len >= 2 && (long)__ptr & 2) { \ + if (__len >= 2 && (unsigned long)__ptr & 2) { \ __len -= 2; \ - SMC_outw( *((u16 *)__ptr)++, ioaddr, DATA_REG );\ + SMC_outw( *(u16 *)__ptr, ioaddr, DATA_REG ); \ + __ptr += 2; \ } \ SMC_outsl( ioaddr, DATA_REG, __ptr, __len >> 2); \ if (__len & 2) { \ @@ -823,7 +867,7 @@ static const char * chip_ids[ 16 ] = { do { \ char *__ptr = (p); \ int __len = (l); \ - if ((long)__ptr & 2) { \ + if ((unsigned long)__ptr & 2) { \ /* \ * We want 32bit alignment here. \ * Since some buses perform a full 32bit \ @@ -831,7 +875,7 @@ static const char * chip_ids[ 16 ] = { * SMC_inw() here. Back both source (on chip \ * and destination) pointers of 2 bytes. \ */ \ - (long)__ptr &= ~2; \ + __ptr -= 2; \ __len += 2; \ SMC_SET_PTR( 2|PTR_READ|PTR_RCV|PTR_AUTOINC ); \ } \ --- linux-2.6.9-rc1/drivers/net/starfire.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/drivers/net/starfire.c 2004-09-03 00:56:32.581207552 -0700 @@ -799,12 +799,13 @@ static struct net_device_stats *get_stat static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static int netdev_close(struct net_device *dev); static void netdev_media_change(struct net_device *dev); +static struct ethtool_ops ethtool_ops; #ifdef VLAN_SUPPORT static void netdev_vlan_rx_register(struct net_device *dev, struct vlan_group *grp) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); spin_lock(&np->lock); if (debug > 2) @@ -816,7 +817,7 @@ static void netdev_vlan_rx_register(stru static void netdev_vlan_rx_add_vid(struct net_device *dev, unsigned short vid) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); spin_lock(&np->lock); if (debug > 1) @@ -827,7 +828,7 @@ static void netdev_vlan_rx_add_vid(struc static void netdev_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); spin_lock(&np->lock); if (debug > 1) @@ -951,7 +952,7 @@ static int __devinit starfire_init_one(s dev->base_addr = ioaddr; dev->irq = irq; - np = dev->priv; + np = netdev_priv(dev); spin_lock_init(&np->lock); pci_set_drvdata(pdev, dev); @@ -1015,6 +1016,7 @@ static int __devinit starfire_init_one(s dev->get_stats = &get_stats; dev->set_multicast_list = &set_rx_mode; dev->do_ioctl = &netdev_ioctl; + SET_ETHTOOL_OPS(dev, ðtool_ops); if (mtu) dev->mtu = mtu; @@ -1102,7 +1104,7 @@ static void mdio_write(struct net_device static int netdev_open(struct net_device *dev) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); long ioaddr = dev->base_addr; int i, retval; size_t tx_done_q_size, rx_done_q_size, tx_ring_size, rx_ring_size; @@ -1267,7 +1269,7 @@ static int netdev_open(struct net_device static void check_duplex(struct net_device *dev) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); u16 reg0; int silly_count = 1000; @@ -1302,7 +1304,7 @@ static void check_duplex(struct net_devi static void tx_timeout(struct net_device *dev) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); long ioaddr = dev->base_addr; int old_debug; @@ -1332,7 +1334,7 @@ static void tx_timeout(struct net_device /* Initialize the Rx and Tx rings, along with various 'dev' bits. */ static void init_ring(struct net_device *dev) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); int i; np->cur_rx = np->cur_tx = np->reap_tx = 0; @@ -1378,7 +1380,7 @@ static void init_ring(struct net_device static int start_tx(struct sk_buff *skb, struct net_device *dev) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); unsigned int entry; u32 status; int i; @@ -1497,7 +1499,7 @@ static irqreturn_t intr_handler(int irq, int handled = 0; ioaddr = dev->base_addr; - np = dev->priv; + np = netdev_priv(dev); do { u32 intr_status = readl(ioaddr + IntrClear); @@ -1597,7 +1599,7 @@ static irqreturn_t intr_handler(int irq, for clarity, code sharing between NAPI/non-NAPI, and better register allocation. */ static int __netdev_rx(struct net_device *dev, int *quota) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); u32 desc_status; int retcode = 0; @@ -1752,7 +1754,7 @@ static int netdev_poll(struct net_device static void refill_rx_ring(struct net_device *dev) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); struct sk_buff *skb; int entry = -1; @@ -1780,7 +1782,7 @@ static void refill_rx_ring(struct net_de static void netdev_media_change(struct net_device *dev) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); long ioaddr = dev->base_addr; u16 reg0, reg1, reg4, reg5; u32 new_tx_mode; @@ -1855,7 +1857,7 @@ static void netdev_media_change(struct n static void netdev_error(struct net_device *dev, int intr_status) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); /* Came close to underrunning the Tx FIFO, increase threshold. */ if (intr_status & IntrTxDataLow) { @@ -1883,7 +1885,7 @@ static void netdev_error(struct net_devi static struct net_device_stats *get_stats(struct net_device *dev) { long ioaddr = dev->base_addr; - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); /* This adapter architecture needs no SMP locks. */ np->stats.tx_bytes = readl(ioaddr + 0x57010); @@ -1917,7 +1919,7 @@ static void set_rx_mode(struct net_devic struct dev_mc_list *mclist; int i; #ifdef VLAN_SUPPORT - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); rx_mode |= VlanMode; if (np->vlgrp) { @@ -1996,106 +1998,89 @@ static void set_rx_mode(struct net_devic writel(rx_mode, ioaddr + RxFilterMode); } +static int check_if_running(struct net_device *dev) +{ + if (!netif_running(dev)) + return -EINVAL; + return 0; +} -static int netdev_ethtool_ioctl(struct net_device *dev, void __user *useraddr) +static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - struct ethtool_cmd ecmd; - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->bus_info, PCI_SLOT_NAME(np->pci_dev)); +} - if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) - return -EFAULT; +static int get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct netdev_private *np = netdev_priv(dev); + spin_lock_irq(&np->lock); + mii_ethtool_gset(&np->mii_if, ecmd); + spin_unlock_irq(&np->lock); + return 0; +} - switch (ecmd.cmd) { - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info; - memset(&info, 0, sizeof(info)); - info.cmd = ecmd.cmd; - strcpy(info.driver, DRV_NAME); - strcpy(info.version, DRV_VERSION); - *info.fw_version = 0; - strcpy(info.bus_info, PCI_SLOT_NAME(np->pci_dev)); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } +static int set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct netdev_private *np = netdev_priv(dev); + int res; + spin_lock_irq(&np->lock); + res = mii_ethtool_sset(&np->mii_if, ecmd); + spin_unlock_irq(&np->lock); + check_duplex(dev); + return res; +} - /* get settings */ - case ETHTOOL_GSET: { - struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - spin_lock_irq(&np->lock); - mii_ethtool_gset(&np->mii_if, &ecmd); - spin_unlock_irq(&np->lock); - if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; - } - /* set settings */ - case ETHTOOL_SSET: { - int r; - struct ethtool_cmd ecmd; - if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) - return -EFAULT; - spin_lock_irq(&np->lock); - r = mii_ethtool_sset(&np->mii_if, &ecmd); - spin_unlock_irq(&np->lock); - check_duplex(dev); - return r; - } - /* restart autonegotiation */ - case ETHTOOL_NWAY_RST: { - return mii_nway_restart(&np->mii_if); - } - /* get link status */ - case ETHTOOL_GLINK: { - struct ethtool_value edata = {ETHTOOL_GLINK}; - edata.data = mii_link_ok(&np->mii_if); - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } +static int nway_reset(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + return mii_nway_restart(&np->mii_if); +} - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = debug; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - debug = edata.data; - return 0; - } - default: - return -EOPNOTSUPP; - } +static u32 get_link(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + return mii_link_ok(&np->mii_if); } +static u32 get_msglevel(struct net_device *dev) +{ + return debug; +} + +static void set_msglevel(struct net_device *dev, u32 val) +{ + debug = val; +} + +static struct ethtool_ops ethtool_ops = { + .begin = check_if_running, + .get_drvinfo = get_drvinfo, + .get_settings = get_settings, + .set_settings = set_settings, + .nway_reset = nway_reset, + .get_link = get_link, + .get_msglevel = get_msglevel, + .set_msglevel = set_msglevel, +}; static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); + struct mii_ioctl_data *data = if_mii(rq); int rc; if (!netif_running(dev)) return -EINVAL; - if (cmd == SIOCETHTOOL) - rc = netdev_ethtool_ioctl(dev, rq->ifr_data); + spin_lock_irq(&np->lock); + rc = generic_mii_ioctl(&np->mii_if, data, cmd, NULL); + spin_unlock_irq(&np->lock); - else { - struct mii_ioctl_data *data = if_mii(rq); - spin_lock_irq(&np->lock); - rc = generic_mii_ioctl(&np->mii_if, data, cmd, NULL); - spin_unlock_irq(&np->lock); - - if ((cmd == SIOCSMIIREG) && (data->phy_id == np->phys[0])) - check_duplex(dev); - } + if ((cmd == SIOCSMIIREG) && (data->phy_id == np->phys[0])) + check_duplex(dev); return rc; } @@ -2103,7 +2088,7 @@ static int netdev_ioctl(struct net_devic static int netdev_close(struct net_device *dev) { long ioaddr = dev->base_addr; - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); int i; netif_stop_queue(dev); @@ -2174,16 +2159,16 @@ static int netdev_close(struct net_devic static void __devexit starfire_remove_one (struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); - struct netdev_private *np; + struct netdev_private *np = netdev_priv(dev); if (!dev) BUG(); - np = dev->priv; + unregister_netdev(dev); + if (np->queue_mem) pci_free_consistent(pdev, np->queue_mem_size, np->queue_mem, np->queue_mem_dma); - unregister_netdev(dev); /* XXX: add wakeup code -- requires firmware for MagicPacket */ pci_set_power_state(pdev, 3); /* go to sleep in D3 mode */ --- linux-2.6.9-rc1/drivers/net/sundance.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/net/sundance.c 2004-09-03 00:56:32.584207096 -0700 @@ -511,8 +511,7 @@ static int __set_mac_addr(struct net_dev static struct net_device_stats *get_stats(struct net_device *dev); static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static int netdev_close(struct net_device *dev); - - +static struct ethtool_ops ethtool_ops; static int __devinit sundance_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent) @@ -567,7 +566,7 @@ static int __devinit sundance_probe1 (st dev->base_addr = ioaddr; dev->irq = irq; - np = dev->priv; + np = netdev_priv(dev); np->pci_dev = pdev; np->chip_id = chip_idx; np->msg_enable = (1 << debug) - 1; @@ -600,6 +599,7 @@ static int __devinit sundance_probe1 (st dev->get_stats = &get_stats; dev->set_multicast_list = &set_rx_mode; dev->do_ioctl = &netdev_ioctl; + SET_ETHTOOL_OPS(dev, ðtool_ops); dev->tx_timeout = &tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; dev->change_mtu = &change_mtu; @@ -787,7 +787,7 @@ static void mdio_sync(long mdio_addr) static int mdio_read(struct net_device *dev, int phy_id, int location) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); long mdio_addr = dev->base_addr + MIICtrl; int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; int i, retval = 0; @@ -817,7 +817,7 @@ static int mdio_read(struct net_device * static void mdio_write(struct net_device *dev, int phy_id, int location, int value) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); long mdio_addr = dev->base_addr + MIICtrl; int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; int i; @@ -846,7 +846,7 @@ static void mdio_write(struct net_device static int netdev_open(struct net_device *dev) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); long ioaddr = dev->base_addr; int i; @@ -879,7 +879,7 @@ static int netdev_open(struct net_device if (dev->if_port == 0) dev->if_port = np->default_port; - np->mcastlock = (spinlock_t) SPIN_LOCK_UNLOCKED; + np->mcastlock = SPIN_LOCK_UNLOCKED; set_rx_mode(dev); writew(0, ioaddr + IntrEnable); @@ -916,7 +916,7 @@ static int netdev_open(struct net_device static void check_duplex(struct net_device *dev) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); long ioaddr = dev->base_addr; int mii_lpa = mdio_read(dev, np->phys[0], MII_LPA); int negotiated = mii_lpa & np->mii_if.advertising; @@ -945,7 +945,7 @@ static void check_duplex(struct net_devi static void netdev_timer(unsigned long data) { struct net_device *dev = (struct net_device *)data; - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); long ioaddr = dev->base_addr; int next_tick = 10*HZ; @@ -962,7 +962,7 @@ static void netdev_timer(unsigned long d static void tx_timeout(struct net_device *dev) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); long ioaddr = dev->base_addr; unsigned long flag; @@ -1015,7 +1015,7 @@ static void tx_timeout(struct net_device /* Initialize the Rx and Tx rings, along with various 'dev' bits. */ static void init_ring(struct net_device *dev) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); int i; np->cur_rx = np->cur_tx = 0; @@ -1058,7 +1058,7 @@ static void init_ring(struct net_device static void tx_poll (unsigned long data) { struct net_device *dev = (struct net_device *)data; - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); unsigned head = np->cur_task % TX_RING_SIZE; struct netdev_desc *txdesc = &np->tx_ring[(np->cur_tx - 1) % TX_RING_SIZE]; @@ -1085,7 +1085,7 @@ static void tx_poll (unsigned long data) static int start_tx (struct sk_buff *skb, struct net_device *dev) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); struct netdev_desc *txdesc; unsigned entry; @@ -1127,7 +1127,7 @@ start_tx (struct sk_buff *skb, struct ne static int reset_tx (struct net_device *dev) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); long ioaddr = dev->base_addr; struct sk_buff *skb; int i; @@ -1176,7 +1176,7 @@ static irqreturn_t intr_handler(int irq, int handled = 0; ioaddr = dev->base_addr; - np = dev->priv; + np = netdev_priv(dev); do { int intr_status = readw(ioaddr + IntrStatus); @@ -1301,7 +1301,7 @@ static irqreturn_t intr_handler(int irq, static void rx_poll(unsigned long data) { struct net_device *dev = (struct net_device *)data; - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); int entry = np->cur_rx % RX_RING_SIZE; int boguscnt = np->budget; long ioaddr = dev->base_addr; @@ -1398,7 +1398,7 @@ not_done: static void refill_rx (struct net_device *dev) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); int entry; int cnt = 0; @@ -1429,7 +1429,7 @@ static void refill_rx (struct net_device static void netdev_error(struct net_device *dev, int intr_status) { long ioaddr = dev->base_addr; - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); u16 mii_ctl, mii_advertise, mii_lpa; int speed; @@ -1483,7 +1483,7 @@ static void netdev_error(struct net_devi static struct net_device_stats *get_stats(struct net_device *dev) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); long ioaddr = dev->base_addr; int i; @@ -1512,7 +1512,7 @@ static struct net_device_stats *get_stat static void set_rx_mode(struct net_device *dev) { long ioaddr = dev->base_addr; - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); u16 mc_filter[4]; /* Multicast hash filter */ u32 rx_mode; int i; @@ -1565,91 +1565,79 @@ static int __set_mac_addr(struct net_dev writew(addr16, dev->base_addr + StationAddr+4); return 0; } - -static int netdev_ethtool_ioctl(struct net_device *dev, void __user *useraddr) +static int check_if_running(struct net_device *dev) { - struct netdev_private *np = dev->priv; - u32 ethcmd; + if (!netif_running(dev)) + return -EINVAL; + return 0; +} - if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) - return -EFAULT; +static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + struct netdev_private *np = netdev_priv(dev); + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->bus_info, pci_name(np->pci_dev)); +} - switch (ethcmd) { - /* get constant driver settings/info */ - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; - strcpy(info.driver, DRV_NAME); - strcpy(info.version, DRV_VERSION); - strcpy(info.bus_info, pci_name(np->pci_dev)); - memset(&info.fw_version, 0, sizeof(info.fw_version)); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } +static int get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct netdev_private *np = netdev_priv(dev); + spin_lock_irq(&np->lock); + mii_ethtool_gset(&np->mii_if, ecmd); + spin_unlock_irq(&np->lock); + return 0; +} - /* get media settings */ - case ETHTOOL_GSET: { - struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - spin_lock_irq(&np->lock); - mii_ethtool_gset(&np->mii_if, &ecmd); - spin_unlock_irq(&np->lock); - if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; - } - /* set media settings */ - case ETHTOOL_SSET: { - int r; - struct ethtool_cmd ecmd; - if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) - return -EFAULT; - spin_lock_irq(&np->lock); - r = mii_ethtool_sset(&np->mii_if, &ecmd); - spin_unlock_irq(&np->lock); - return r; - } - - /* restart autonegotiation */ - case ETHTOOL_NWAY_RST: { - return mii_nway_restart(&np->mii_if); - } - - /* get link status */ - case ETHTOOL_GLINK: { - struct ethtool_value edata = {ETHTOOL_GLINK}; - edata.data = mii_link_ok(&np->mii_if); - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } +static int set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct netdev_private *np = netdev_priv(dev); + int res; + spin_lock_irq(&np->lock); + res = mii_ethtool_sset(&np->mii_if, ecmd); + spin_unlock_irq(&np->lock); + return res; +} - /* get message-level */ - case ETHTOOL_GMSGLVL: { - struct ethtool_value edata = {ETHTOOL_GMSGLVL}; - edata.data = np->msg_enable; - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL: { - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - np->msg_enable = edata.data; - return 0; - } +static int nway_reset(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + return mii_nway_restart(&np->mii_if); +} + +static u32 get_link(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + return mii_link_ok(&np->mii_if); +} - default: - return -EOPNOTSUPP; +static u32 get_msglevel(struct net_device *dev) +{ + struct netdev_private *np = netdev_priv(dev); + return np->msg_enable; +} - } +static void set_msglevel(struct net_device *dev, u32 val) +{ + struct netdev_private *np = netdev_priv(dev); + np->msg_enable = val; } +static struct ethtool_ops ethtool_ops = { + .begin = check_if_running, + .get_drvinfo = get_drvinfo, + .get_settings = get_settings, + .set_settings = set_settings, + .nway_reset = nway_reset, + .get_link = get_link, + .get_msglevel = get_msglevel, + .set_msglevel = set_msglevel, +}; + static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); int rc; int i; long ioaddr = dev->base_addr; @@ -1657,14 +1645,9 @@ static int netdev_ioctl(struct net_devic if (!netif_running(dev)) return -EINVAL; - if (cmd == SIOCETHTOOL) - rc = netdev_ethtool_ioctl(dev, rq->ifr_data); - - else { - spin_lock_irq(&np->lock); - rc = generic_mii_ioctl(&np->mii_if, if_mii(rq), cmd, NULL); - spin_unlock_irq(&np->lock); - } + spin_lock_irq(&np->lock); + rc = generic_mii_ioctl(&np->mii_if, if_mii(rq), cmd, NULL); + spin_unlock_irq(&np->lock); switch (cmd) { case SIOCDEVPRIVATE: for (i=0; ibase_addr; - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); struct sk_buff *skb; int i; @@ -1775,7 +1758,7 @@ static void __devexit sundance_remove1 ( struct net_device *dev = pci_get_drvdata(pdev); if (dev) { - struct netdev_private *np = dev->priv; + struct netdev_private *np = netdev_priv(dev); unregister_netdev(dev); pci_free_consistent(pdev, RX_TOTAL_SIZE, np->rx_ring, --- linux-2.6.9-rc1/drivers/net/tg3.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/drivers/net/tg3.c 2004-09-02 20:51:52.000000000 -0700 @@ -4,6 +4,9 @@ * Copyright (C) 2001, 2002, 2003, 2004 David S. Miller (davem@redhat.com) * Copyright (C) 2001, 2002, 2003 Jeff Garzik (jgarzik@pobox.com) * Copyright (C) 2004 Sun Microsystems Inc. + * + * Firmware is: + * Copyright (C) 2000-2003 Broadcom Corporation. */ #include @@ -57,8 +60,8 @@ #define DRV_MODULE_NAME "tg3" #define PFX DRV_MODULE_NAME ": " -#define DRV_MODULE_VERSION "3.8" -#define DRV_MODULE_RELDATE "July 14, 2004" +#define DRV_MODULE_VERSION "3.9" +#define DRV_MODULE_RELDATE "August 30, 2004" #define TG3_DEF_MAC_MODE 0 #define TG3_DEF_RX_MODE 0 @@ -442,9 +445,14 @@ static void tg3_switch_clocks(struct tg3 0x1f); tp->pci_clock_ctrl = clock_ctrl; - if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 && - GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5750 && - (orig_clock_ctrl & CLOCK_CTRL_44MHZ_CORE) != 0) { + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) { + if (orig_clock_ctrl & CLOCK_CTRL_625_CORE) { + tw32_f(TG3PCI_CLOCK_CTRL, + clock_ctrl | CLOCK_CTRL_625_CORE); + udelay(40); + } + } else if ((orig_clock_ctrl & CLOCK_CTRL_44MHZ_CORE) != 0) { tw32_f(TG3PCI_CLOCK_CTRL, clock_ctrl | (CLOCK_CTRL_44MHZ_CORE | CLOCK_CTRL_ALTCLK)); @@ -980,7 +988,7 @@ static int tg3_set_power_state(struct tg tp->link_config.orig_autoneg = tp->link_config.autoneg; } - if (tp->phy_id != PHY_ID_SERDES) { + if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)) { tp->link_config.speed = SPEED_10; tp->link_config.duplex = DUPLEX_HALF; tp->link_config.autoneg = AUTONEG_ENABLE; @@ -992,7 +1000,7 @@ static int tg3_set_power_state(struct tg if (tp->tg3_flags & TG3_FLAG_WOL_ENABLE) { u32 mac_mode; - if (tp->phy_id != PHY_ID_SERDES) { + if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)) { tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x5a); udelay(40); @@ -1487,6 +1495,18 @@ static int tg3_setup_copper_phy(struct t current_speed = SPEED_INVALID; current_duplex = DUPLEX_INVALID; + if (tp->tg3_flags2 & TG3_FLG2_CAPACITIVE_COUPLING) { + u32 val; + + tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x4007); + tg3_readphy(tp, MII_TG3_AUX_CTRL, &val); + if (!(val & (1 << 10))) { + val |= (1 << 10); + tg3_writephy(tp, MII_TG3_AUX_CTRL, val); + goto relink; + } + } + bmsr = 0; for (i = 0; i < 100; i++) { tg3_readphy(tp, MII_BMSR, &bmsr); @@ -1566,7 +1586,7 @@ static int tg3_setup_copper_phy(struct t tg3_setup_flow_control(tp, local_adv, remote_adv); } } - +relink: if (current_link_up == 0) { u32 tmp; @@ -1616,7 +1636,7 @@ static int tg3_setup_copper_phy(struct t tw32_f(MAC_MODE, tp->mac_mode); udelay(40); - if (tp->tg3_flags & (TG3_FLAG_USE_LINKCHG_REG | TG3_FLAG_POLL_SERDES)) { + if (tp->tg3_flags & TG3_FLAG_USE_LINKCHG_REG) { /* Polled via timer. */ tw32_f(MAC_EVENT, 0); } else { @@ -1965,62 +1985,264 @@ static int tg3_fiber_aneg_smachine(struc static int fiber_autoneg(struct tg3 *tp, u32 *flags) { int res = 0; + struct tg3_fiber_aneginfo aninfo; + int status = ANEG_FAILED; + unsigned int tick; + u32 tmp; - if (tp->tg3_flags2 & TG3_FLG2_HW_AUTONEG) { - u32 dig_status; + tw32_f(MAC_TX_AUTO_NEG, 0); - dig_status = tr32(SG_DIG_STATUS); - *flags = 0; - if (dig_status & SG_DIG_PARTNER_ASYM_PAUSE) - *flags |= MR_LP_ADV_ASYM_PAUSE; - if (dig_status & SG_DIG_PARTNER_PAUSE_CAPABLE) - *flags |= MR_LP_ADV_SYM_PAUSE; - - if ((dig_status & SG_DIG_AUTONEG_COMPLETE) && - !(dig_status & (SG_DIG_AUTONEG_ERROR | - SG_DIG_PARTNER_FAULT_MASK))) - res = 1; - } else { - struct tg3_fiber_aneginfo aninfo; - int status = ANEG_FAILED; - unsigned int tick; - u32 tmp; + tmp = tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK; + tw32_f(MAC_MODE, tmp | MAC_MODE_PORT_MODE_GMII); + udelay(40); - tw32_f(MAC_TX_AUTO_NEG, 0); + tw32_f(MAC_MODE, tp->mac_mode | MAC_MODE_SEND_CONFIGS); + udelay(40); - tmp = tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK; - tw32_f(MAC_MODE, tmp | MAC_MODE_PORT_MODE_GMII); - udelay(40); + memset(&aninfo, 0, sizeof(aninfo)); + aninfo.flags |= MR_AN_ENABLE; + aninfo.state = ANEG_STATE_UNKNOWN; + aninfo.cur_time = 0; + tick = 0; + while (++tick < 195000) { + status = tg3_fiber_aneg_smachine(tp, &aninfo); + if (status == ANEG_DONE || status == ANEG_FAILED) + break; - tw32_f(MAC_MODE, tp->mac_mode | MAC_MODE_SEND_CONFIGS); - udelay(40); + udelay(1); + } - memset(&aninfo, 0, sizeof(aninfo)); - aninfo.flags |= MR_AN_ENABLE; - aninfo.state = ANEG_STATE_UNKNOWN; - aninfo.cur_time = 0; - tick = 0; - while (++tick < 195000) { - status = tg3_fiber_aneg_smachine(tp, &aninfo); - if (status == ANEG_DONE || status == ANEG_FAILED) - break; + tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS; + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); - udelay(1); + *flags = aninfo.flags; + + if (status == ANEG_DONE && + (aninfo.flags & (MR_AN_COMPLETE | MR_LINK_OK | + MR_LP_ADV_FULL_DUPLEX))) + res = 1; + + return res; +} + +static void tg3_init_bcm8002(struct tg3 *tp) +{ + u32 mac_status = tr32(MAC_STATUS); + int i; + + /* Reset when initting first time or we have a link. */ + if ((tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) && + !(mac_status & MAC_STATUS_PCS_SYNCED)) + return; + + /* Set PLL lock range. */ + tg3_writephy(tp, 0x16, 0x8007); + + /* SW reset */ + tg3_writephy(tp, MII_BMCR, BMCR_RESET); + + /* Wait for reset to complete. */ + /* XXX schedule_timeout() ... */ + for (i = 0; i < 500; i++) + udelay(10); + + /* Config mode; select PMA/Ch 1 regs. */ + tg3_writephy(tp, 0x10, 0x8411); + + /* Enable auto-lock and comdet, select txclk for tx. */ + tg3_writephy(tp, 0x11, 0x0a10); + + tg3_writephy(tp, 0x18, 0x00a0); + tg3_writephy(tp, 0x16, 0x41ff); + + /* Assert and deassert POR. */ + tg3_writephy(tp, 0x13, 0x0400); + udelay(40); + tg3_writephy(tp, 0x13, 0x0000); + + tg3_writephy(tp, 0x11, 0x0a50); + udelay(40); + tg3_writephy(tp, 0x11, 0x0a10); + + /* Wait for signal to stabilize */ + /* XXX schedule_timeout() ... */ + for (i = 0; i < 15000; i++) + udelay(10); + + /* Deselect the channel register so we can read the PHYID + * later. + */ + tg3_writephy(tp, 0x10, 0x8011); +} + +static int tg3_setup_fiber_hw_autoneg(struct tg3 *tp, u32 mac_status) +{ + u32 sg_dig_ctrl, sg_dig_status; + u32 serdes_cfg, expected_sg_dig_ctrl; + int workaround, port_a; + int current_link_up; + + serdes_cfg = 0; + expected_sg_dig_ctrl = 0; + workaround = 0; + port_a = 1; + current_link_up = 0; + + if (tp->pci_chip_rev_id != CHIPREV_ID_5704_A0 && + tp->pci_chip_rev_id != CHIPREV_ID_5704_A1) { + workaround = 1; + if (tr32(TG3PCI_DUAL_MAC_CTRL) & DUAL_MAC_CTRL_ID) + port_a = 0; + + serdes_cfg = tr32(MAC_SERDES_CFG) & + ((1 << 23) | (1 << 22) | (1 << 21) | (1 << 20)); + } + + sg_dig_ctrl = tr32(SG_DIG_CTRL); + + if (tp->link_config.autoneg != AUTONEG_ENABLE) { + if (sg_dig_ctrl & (1 << 31)) { + if (workaround) { + u32 val = serdes_cfg; + + if (port_a) + val |= 0xc010880; + else + val |= 0x4010880; + tw32_f(MAC_SERDES_CFG, val); + } + tw32_f(SG_DIG_CTRL, 0x01388400); } + if (mac_status & MAC_STATUS_PCS_SYNCED) { + tg3_setup_flow_control(tp, 0, 0); + current_link_up = 1; + } + goto out; + } - tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS; - tw32_f(MAC_MODE, tp->mac_mode); - udelay(40); + /* Want auto-negotiation. */ + expected_sg_dig_ctrl = 0x81388400; + + /* Pause capability */ + expected_sg_dig_ctrl |= (1 << 11); - *flags = aninfo.flags; + /* Asymettric pause */ + expected_sg_dig_ctrl |= (1 << 12); - if (status == ANEG_DONE && - (aninfo.flags & (MR_AN_COMPLETE | MR_LINK_OK | - MR_LP_ADV_FULL_DUPLEX))) - res = 1; + if (sg_dig_ctrl != expected_sg_dig_ctrl) { + if (workaround) + tw32_f(MAC_SERDES_CFG, serdes_cfg | 0xc011880); + tw32_f(SG_DIG_CTRL, expected_sg_dig_ctrl | (1 << 30)); + udelay(5); + tw32_f(SG_DIG_CTRL, expected_sg_dig_ctrl); + + tp->tg3_flags2 |= TG3_FLG2_PHY_JUST_INITTED; + } else if (mac_status & (MAC_STATUS_PCS_SYNCED | + MAC_STATUS_SIGNAL_DET)) { + sg_dig_status = tr32(SG_DIG_STATUS); + + if ((sg_dig_status & (1 << 1)) && + (mac_status & MAC_STATUS_PCS_SYNCED)) { + u32 local_adv, remote_adv; + + local_adv = ADVERTISE_PAUSE_CAP; + remote_adv = 0; + if (sg_dig_status & (1 << 19)) + remote_adv |= LPA_PAUSE_CAP; + if (sg_dig_status & (1 << 20)) + remote_adv |= LPA_PAUSE_ASYM; + + tg3_setup_flow_control(tp, local_adv, remote_adv); + current_link_up = 1; + tp->tg3_flags2 &= ~TG3_FLG2_PHY_JUST_INITTED; + } else if (!(sg_dig_status & (1 << 1))) { + if (tp->tg3_flags2 & TG3_FLG2_PHY_JUST_INITTED) + tp->tg3_flags2 &= ~TG3_FLG2_PHY_JUST_INITTED; + else { + if (workaround) { + u32 val = serdes_cfg; + + if (port_a) + val |= 0xc010880; + else + val |= 0x4010880; + + tw32_f(MAC_SERDES_CFG, serdes_cfg); + } + + tw32_f(SG_DIG_CTRL, 0x01388400); + udelay(40); + + mac_status = tr32(MAC_STATUS); + if (mac_status & MAC_STATUS_PCS_SYNCED) { + tg3_setup_flow_control(tp, 0, 0); + current_link_up = 1; + } + } + } } - return res; +out: + return current_link_up; +} + +static int tg3_setup_fiber_by_hand(struct tg3 *tp, u32 mac_status) +{ + int current_link_up = 0; + + if (!(mac_status & MAC_STATUS_PCS_SYNCED)) { + tp->tg3_flags &= ~TG3_FLAG_GOT_SERDES_FLOWCTL; + goto out; + } + + if (tp->link_config.autoneg == AUTONEG_ENABLE) { + u32 flags; + int i; + + if (fiber_autoneg(tp, &flags)) { + u32 local_adv, remote_adv; + + local_adv = ADVERTISE_PAUSE_CAP; + remote_adv = 0; + if (flags & MR_LP_ADV_SYM_PAUSE) + remote_adv |= LPA_PAUSE_CAP; + if (flags & MR_LP_ADV_ASYM_PAUSE) + remote_adv |= LPA_PAUSE_ASYM; + + tg3_setup_flow_control(tp, local_adv, remote_adv); + + tp->tg3_flags |= TG3_FLAG_GOT_SERDES_FLOWCTL; + current_link_up = 1; + } + for (i = 0; i < 30; i++) { + udelay(20); + tw32_f(MAC_STATUS, + (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)); + udelay(40); + if ((tr32(MAC_STATUS) & + (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)) == 0) + break; + } + + mac_status = tr32(MAC_STATUS); + if (current_link_up == 0 && + (mac_status & MAC_STATUS_PCS_SYNCED) && + !(mac_status & MAC_STATUS_RCVD_CFG)) + current_link_up = 1; + } else { + /* Forcing 1000FD link up. */ + current_link_up = 1; + tp->tg3_flags |= TG3_FLAG_GOT_SERDES_FLOWCTL; + + tw32_f(MAC_MODE, (tp->mac_mode | MAC_MODE_SEND_CONFIGS)); + udelay(40); + } + +out: + return current_link_up; } static int tg3_setup_fiber_phy(struct tg3 *tp, int force_reset) @@ -2028,6 +2250,7 @@ static int tg3_setup_fiber_phy(struct tg u32 orig_pause_cfg; u16 orig_active_speed; u8 orig_active_duplex; + u32 mac_status; int current_link_up; int i; @@ -2037,118 +2260,43 @@ static int tg3_setup_fiber_phy(struct tg orig_active_speed = tp->link_config.active_speed; orig_active_duplex = tp->link_config.active_duplex; - tp->mac_mode &= ~(MAC_MODE_PORT_MODE_MASK | MAC_MODE_HALF_DUPLEX); - tp->mac_mode |= MAC_MODE_PORT_MODE_TBI; - tw32_f(MAC_MODE, tp->mac_mode); - udelay(40); - - if (tp->tg3_flags2 & TG3_FLG2_HW_AUTONEG) { - /* Allow time for the hardware to auto-negotiate (195ms) */ - unsigned int tick = 0; - - while (++tick < 195000) { - if (tr32(SG_DIG_STATUS) & SG_DIG_AUTONEG_COMPLETE) - break; - udelay(1); + if (!(tp->tg3_flags2 & TG3_FLG2_HW_AUTONEG) && + netif_carrier_ok(tp->dev) && + (tp->tg3_flags & TG3_FLAG_INIT_COMPLETE)) { + mac_status = tr32(MAC_STATUS); + mac_status &= (MAC_STATUS_PCS_SYNCED | + MAC_STATUS_SIGNAL_DET | + MAC_STATUS_CFG_CHANGED | + MAC_STATUS_RCVD_CFG); + if (mac_status == (MAC_STATUS_PCS_SYNCED | + MAC_STATUS_SIGNAL_DET)) { + tw32_f(MAC_STATUS, (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)); + return 0; } - if (tick >= 195000) - printk(KERN_INFO PFX "%s: HW autoneg failed !\n", - tp->dev->name); } - /* Reset when initting first time or we have a link. */ - if (!(tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) || - (tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED)) { - /* Set PLL lock range. */ - tg3_writephy(tp, 0x16, 0x8007); - - /* SW reset */ - tg3_writephy(tp, MII_BMCR, BMCR_RESET); - - /* Wait for reset to complete. */ - /* XXX schedule_timeout() ... */ - for (i = 0; i < 500; i++) - udelay(10); - - /* Config mode; select PMA/Ch 1 regs. */ - tg3_writephy(tp, 0x10, 0x8411); - - /* Enable auto-lock and comdet, select txclk for tx. */ - tg3_writephy(tp, 0x11, 0x0a10); - - tg3_writephy(tp, 0x18, 0x00a0); - tg3_writephy(tp, 0x16, 0x41ff); - - /* Assert and deassert POR. */ - tg3_writephy(tp, 0x13, 0x0400); - udelay(40); - tg3_writephy(tp, 0x13, 0x0000); + tw32_f(MAC_TX_AUTO_NEG, 0); - tg3_writephy(tp, 0x11, 0x0a50); - udelay(40); - tg3_writephy(tp, 0x11, 0x0a10); + tp->mac_mode &= ~(MAC_MODE_PORT_MODE_MASK | MAC_MODE_HALF_DUPLEX); + tp->mac_mode |= MAC_MODE_PORT_MODE_TBI; + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); - /* Wait for signal to stabilize */ - /* XXX schedule_timeout() ... */ - for (i = 0; i < 15000; i++) - udelay(10); + if (tp->phy_id == PHY_ID_BCM8002) + tg3_init_bcm8002(tp); - /* Deselect the channel register so we can read the PHYID - * later. - */ - tg3_writephy(tp, 0x10, 0x8011); - } - - /* Enable link change interrupt unless serdes polling. */ - if (!(tp->tg3_flags & TG3_FLAG_POLL_SERDES)) - tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); - else - tw32_f(MAC_EVENT, 0); + /* Enable link change event even when serdes polling. */ + tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED); udelay(40); current_link_up = 0; - if (tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED) { - if (tp->link_config.autoneg == AUTONEG_ENABLE) { - u32 flags; - - if (fiber_autoneg(tp, &flags)) { - u32 local_adv, remote_adv; - - local_adv = ADVERTISE_PAUSE_CAP; - remote_adv = 0; - if (flags & MR_LP_ADV_SYM_PAUSE) - remote_adv |= LPA_PAUSE_CAP; - if (flags & MR_LP_ADV_ASYM_PAUSE) - remote_adv |= LPA_PAUSE_ASYM; + mac_status = tr32(MAC_STATUS); - tg3_setup_flow_control(tp, local_adv, remote_adv); - - tp->tg3_flags |= - TG3_FLAG_GOT_SERDES_FLOWCTL; - current_link_up = 1; - } - for (i = 0; i < 60; i++) { - udelay(20); - tw32_f(MAC_STATUS, - (MAC_STATUS_SYNC_CHANGED | - MAC_STATUS_CFG_CHANGED)); - udelay(40); - if ((tr32(MAC_STATUS) & - (MAC_STATUS_SYNC_CHANGED | - MAC_STATUS_CFG_CHANGED)) == 0) - break; - } - if (current_link_up == 0 && - (tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED)) { - current_link_up = 1; - } - } else { - /* Forcing 1000FD link up. */ - current_link_up = 1; - tp->tg3_flags |= TG3_FLAG_GOT_SERDES_FLOWCTL; - } - } else - tp->tg3_flags &= ~TG3_FLAG_GOT_SERDES_FLOWCTL; + if (tp->tg3_flags2 & TG3_FLG2_HW_AUTONEG) + current_link_up = tg3_setup_fiber_hw_autoneg(tp, mac_status); + else + current_link_up = tg3_setup_fiber_by_hand(tp, mac_status); tp->mac_mode &= ~MAC_MODE_LINK_POLARITY; tw32_f(MAC_MODE, tp->mac_mode); @@ -2159,19 +2307,24 @@ static int tg3_setup_fiber_phy(struct tg (tp->hw_status->status & ~SD_STATUS_LINK_CHG)); for (i = 0; i < 100; i++) { - udelay(20); - tw32_f(MAC_STATUS, - (MAC_STATUS_SYNC_CHANGED | - MAC_STATUS_CFG_CHANGED)); - udelay(40); - if ((tr32(MAC_STATUS) & - (MAC_STATUS_SYNC_CHANGED | - MAC_STATUS_CFG_CHANGED)) == 0) + tw32_f(MAC_STATUS, (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)); + udelay(5); + if ((tr32(MAC_STATUS) & (MAC_STATUS_SYNC_CHANGED | + MAC_STATUS_CFG_CHANGED)) == 0) break; } - if ((tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED) == 0) + mac_status = tr32(MAC_STATUS); + if ((mac_status & MAC_STATUS_PCS_SYNCED) == 0) { current_link_up = 0; + if (tp->link_config.autoneg == AUTONEG_ENABLE) { + tw32_f(MAC_MODE, (tp->mac_mode | + MAC_MODE_SEND_CONFIGS)); + udelay(1); + tw32_f(MAC_MODE, tp->mac_mode); + } + } if (current_link_up == 1) { tp->link_config.active_speed = SPEED_1000; @@ -2203,15 +2356,6 @@ static int tg3_setup_fiber_phy(struct tg tg3_link_report(tp); } - if ((tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED) == 0) { - tw32_f(MAC_MODE, tp->mac_mode | MAC_MODE_LINK_POLARITY); - udelay(40); - if (tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) { - tw32_f(MAC_MODE, tp->mac_mode); - udelay(40); - } - } - return 0; } @@ -2219,7 +2363,7 @@ static int tg3_setup_phy(struct tg3 *tp, { int err; - if (tp->phy_id == PHY_ID_SERDES) { + if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) { err = tg3_setup_fiber_phy(tp, force_reset); } else { err = tg3_setup_copper_phy(tp, force_reset); @@ -2738,11 +2882,11 @@ static void tg3_reset_task(void *_data) tg3_halt(tp); tg3_init_hw(tp); + tg3_netif_start(tp); + spin_unlock(&tp->tx_lock); spin_unlock_irq(&tp->lock); - tg3_netif_start(tp); - if (restart_timer) mod_timer(&tp->timer, jiffies + 1); } @@ -3102,9 +3246,10 @@ static int tg3_change_mtu(struct net_dev tg3_init_hw(tp); + tg3_netif_start(tp); + spin_unlock(&tp->tx_lock); spin_unlock_irq(&tp->lock); - tg3_netif_start(tp); return 0; } @@ -3595,6 +3740,8 @@ static void tg3_write_sig_legacy(struct } } +static void tg3_stop_fw(struct tg3 *); + /* tp->lock is held. */ static int tg3_chip_reset(struct tg3 *tp) { @@ -3697,6 +3844,11 @@ static int tg3_chip_reset(struct tg3 *tp tw32(MEMARB_MODE, MEMARB_MODE_ENABLE); + if (tp->pci_chip_rev_id == CHIPREV_ID_5750_A3) { + tg3_stop_fw(tp); + tw32(0x5000, 0x400); + } + tw32(GRC_MODE, tp->grc_mode); if (tp->pci_chip_rev_id == CHIPREV_ID_5705_A0) { @@ -3713,7 +3865,7 @@ static int tg3_chip_reset(struct tg3 *tp tw32(TG3PCI_CLOCK_CTRL, tp->pci_clock_ctrl); } - if (tp->phy_id == PHY_ID_SERDES) { + if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) { tp->mac_mode = MAC_MODE_PORT_MODE_TBI; tw32_f(MAC_MODE, tp->mac_mode); } else @@ -5243,14 +5395,14 @@ static int tg3_reset_hw(struct tg3 *tp) tw32(MAC_LED_CTRL, tp->led_ctrl); tw32(MAC_MI_STAT, MAC_MI_STAT_LNKSTAT_ATTN_ENAB); - if (tp->phy_id == PHY_ID_SERDES) { + if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) { tw32_f(MAC_RX_MODE, RX_MODE_RESET); udelay(10); } tw32_f(MAC_RX_MODE, tp->rx_mode); udelay(10); - if (tp->phy_id == PHY_ID_SERDES) { + if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) { if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) { /* Set drive transmission level to 1.2V */ val = tr32(MAC_SERDES_CFG); @@ -5268,22 +5420,8 @@ static int tg3_reset_hw(struct tg3 *tp) tw32_f(MAC_LOW_WMARK_MAX_RX_FRAME, 2); if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 && - tp->phy_id == PHY_ID_SERDES) { - /* Enable hardware link auto-negotiation */ - u32 digctrl, txctrl; - - digctrl = SG_DIG_USING_HW_AUTONEG | SG_DIG_CRC16_CLEAR_N | - SG_DIG_LOCAL_DUPLEX_STATUS | SG_DIG_LOCAL_LINK_STATUS | - (2 << SG_DIG_SPEED_STATUS_SHIFT) | SG_DIG_FIBER_MODE | - SG_DIG_GBIC_ENABLE; - - txctrl = tr32(MAC_SERDES_CFG); - tw32_f(MAC_SERDES_CFG, txctrl | MAC_SERDES_CFG_EDGE_SELECT); - tw32_f(SG_DIG_CTRL, digctrl | SG_DIG_SOFT_RESET); - tr32(SG_DIG_CTRL); - udelay(5); - tw32_f(SG_DIG_CTRL, digctrl); - + (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)) { + /* Use hardware link auto-negotiation */ tp->tg3_flags2 |= TG3_FLG2_HW_AUTONEG; } @@ -5291,7 +5429,7 @@ static int tg3_reset_hw(struct tg3 *tp) if (err) return err; - if (tp->phy_id != PHY_ID_SERDES) { + if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)) { u32 tmp; /* Clear CRC stats. */ @@ -5483,7 +5621,8 @@ static void tg3_timer(unsigned long __op need_setup = 1; } if (! netif_carrier_ok(tp->dev) && - (mac_stat & MAC_STATUS_PCS_SYNCED)) { + (mac_stat & (MAC_STATUS_PCS_SYNCED | + MAC_STATUS_SIGNAL_DET))) { need_setup = 1; } if (need_setup) { @@ -5879,7 +6018,7 @@ static unsigned long calc_crc_errors(str { struct tg3_hw_stats *hw_stats = tp->hw_stats; - if (tp->phy_id != PHY_ID_SERDES && + if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) && (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701)) { unsigned long flags; @@ -6310,7 +6449,7 @@ static int tg3_get_settings(struct net_d cmd->supported |= (SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full); - if (tp->phy_id != PHY_ID_SERDES) + if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)) cmd->supported |= (SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_10baseT_Half | @@ -6339,7 +6478,7 @@ static int tg3_set_settings(struct net_d tp->link_config.phy_is_low_power) return -EAGAIN; - if (tp->phy_id == PHY_ID_SERDES) { + if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) { /* These are the only valid advertisement bits allowed. */ if (cmd->autoneg == AUTONEG_ENABLE && (cmd->advertising & ~(ADVERTISED_1000baseT_Half | @@ -6397,7 +6536,7 @@ static int tg3_set_wol(struct net_device if (wol->wolopts & ~WAKE_MAGIC) return -EINVAL; if ((wol->wolopts & WAKE_MAGIC) && - tp->phy_id == PHY_ID_SERDES && + tp->tg3_flags2 & TG3_FLG2_PHY_SERDES && !(tp->tg3_flags & TG3_FLAG_SERDES_WOL_CAP)) return -EINVAL; @@ -6493,10 +6632,9 @@ static int tg3_set_ringparam(struct net_ tg3_halt(tp); tg3_init_hw(tp); - netif_wake_queue(tp->dev); + tg3_netif_start(tp); spin_unlock(&tp->tx_lock); spin_unlock_irq(&tp->lock); - tg3_netif_start(tp); return 0; } @@ -6531,9 +6669,9 @@ static int tg3_set_pauseparam(struct net tp->tg3_flags &= ~TG3_FLAG_PAUSE_TX; tg3_halt(tp); tg3_init_hw(tp); + tg3_netif_start(tp); spin_unlock(&tp->tx_lock); spin_unlock_irq(&tp->lock); - tg3_netif_start(tp); return 0; } @@ -6620,7 +6758,7 @@ static int tg3_ioctl(struct net_device * case SIOCGMIIREG: { u32 mii_regval; - if (tp->phy_id == PHY_ID_SERDES) + if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) break; /* We have no PHY */ spin_lock_irq(&tp->lock); @@ -6633,7 +6771,7 @@ static int tg3_ioctl(struct net_device * } case SIOCSMIIREG: - if (tp->phy_id == PHY_ID_SERDES) + if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) break; /* We have no PHY */ if (!capable(CAP_NET_ADMIN)) @@ -6870,10 +7008,10 @@ static struct subsys_tbl_ent subsys_id_t { PCI_VENDOR_ID_BROADCOM, 0x1644, PHY_ID_BCM5401 }, /* BCM95700A6 */ { PCI_VENDOR_ID_BROADCOM, 0x0001, PHY_ID_BCM5701 }, /* BCM95701A5 */ { PCI_VENDOR_ID_BROADCOM, 0x0002, PHY_ID_BCM8002 }, /* BCM95700T6 */ - { PCI_VENDOR_ID_BROADCOM, 0x0003, PHY_ID_SERDES }, /* BCM95700A9 */ + { PCI_VENDOR_ID_BROADCOM, 0x0003, 0 }, /* BCM95700A9 */ { PCI_VENDOR_ID_BROADCOM, 0x0005, PHY_ID_BCM5701 }, /* BCM95701T1 */ { PCI_VENDOR_ID_BROADCOM, 0x0006, PHY_ID_BCM5701 }, /* BCM95701T8 */ - { PCI_VENDOR_ID_BROADCOM, 0x0007, PHY_ID_SERDES }, /* BCM95701A7 */ + { PCI_VENDOR_ID_BROADCOM, 0x0007, 0 }, /* BCM95701A7 */ { PCI_VENDOR_ID_BROADCOM, 0x0008, PHY_ID_BCM5701 }, /* BCM95701A10 */ { PCI_VENDOR_ID_BROADCOM, 0x8008, PHY_ID_BCM5701 }, /* BCM95701A12 */ { PCI_VENDOR_ID_BROADCOM, 0x0009, PHY_ID_BCM5703 }, /* BCM95703Ax1 */ @@ -6882,7 +7020,7 @@ static struct subsys_tbl_ent subsys_id_t /* 3com boards. */ { PCI_VENDOR_ID_3COM, 0x1000, PHY_ID_BCM5401 }, /* 3C996T */ { PCI_VENDOR_ID_3COM, 0x1006, PHY_ID_BCM5701 }, /* 3C996BT */ - { PCI_VENDOR_ID_3COM, 0x1004, PHY_ID_SERDES }, /* 3C996SX */ + { PCI_VENDOR_ID_3COM, 0x1004, 0 }, /* 3C996SX */ { PCI_VENDOR_ID_3COM, 0x1007, PHY_ID_BCM5701 }, /* 3C1000T */ { PCI_VENDOR_ID_3COM, 0x1008, PHY_ID_BCM5701 }, /* 3C940BR01 */ @@ -6895,37 +7033,43 @@ static struct subsys_tbl_ent subsys_id_t /* Compaq boards. */ { PCI_VENDOR_ID_COMPAQ, 0x007c, PHY_ID_BCM5701 }, /* BANSHEE */ { PCI_VENDOR_ID_COMPAQ, 0x009a, PHY_ID_BCM5701 }, /* BANSHEE_2 */ - { PCI_VENDOR_ID_COMPAQ, 0x007d, PHY_ID_SERDES }, /* CHANGELING */ + { PCI_VENDOR_ID_COMPAQ, 0x007d, 0 }, /* CHANGELING */ { PCI_VENDOR_ID_COMPAQ, 0x0085, PHY_ID_BCM5701 }, /* NC7780 */ { PCI_VENDOR_ID_COMPAQ, 0x0099, PHY_ID_BCM5701 }, /* NC7780_2 */ /* IBM boards. */ - { PCI_VENDOR_ID_IBM, 0x0281, PHY_ID_SERDES } /* IBM??? */ + { PCI_VENDOR_ID_IBM, 0x0281, 0 } /* IBM??? */ }; -static int __devinit tg3_phy_probe(struct tg3 *tp) +static inline struct subsys_tbl_ent *lookup_by_subsys(struct tg3 *tp) { - u32 eeprom_phy_id, hw_phy_id_1, hw_phy_id_2; - u32 hw_phy_id, hw_phy_id_masked; - u32 val; - int i, eeprom_signature_found, err; + int i; - tp->phy_id = PHY_ID_INVALID; for (i = 0; i < ARRAY_SIZE(subsys_id_to_phy_id); i++) { if ((subsys_id_to_phy_id[i].subsys_vendor == tp->pdev->subsystem_vendor) && (subsys_id_to_phy_id[i].subsys_devid == - tp->pdev->subsystem_device)) { - tp->phy_id = subsys_id_to_phy_id[i].phy_id; - break; - } + tp->pdev->subsystem_device)) + return &subsys_id_to_phy_id[i]; } + return NULL; +} + +static int __devinit tg3_phy_probe(struct tg3 *tp) +{ + u32 eeprom_phy_id, hw_phy_id_1, hw_phy_id_2; + u32 hw_phy_id, hw_phy_id_masked; + u32 val; + int eeprom_signature_found, eeprom_phy_serdes, err; + tp->phy_id = PHY_ID_INVALID; eeprom_phy_id = PHY_ID_INVALID; + eeprom_phy_serdes = 0; eeprom_signature_found = 0; tg3_read_mem(tp, NIC_SRAM_DATA_SIG, &val); if (val == NIC_SRAM_DATA_SIG_MAGIC) { u32 nic_cfg, led_cfg; + u32 nic_phy_id, cfg2; tg3_read_mem(tp, NIC_SRAM_DATA_CFG, &nic_cfg); tp->nic_sram_data_cfg = nic_cfg; @@ -6933,21 +7077,19 @@ static int __devinit tg3_phy_probe(struc eeprom_signature_found = 1; if ((nic_cfg & NIC_SRAM_DATA_CFG_PHY_TYPE_MASK) == - NIC_SRAM_DATA_CFG_PHY_TYPE_FIBER) { - eeprom_phy_id = PHY_ID_SERDES; - } else { - u32 nic_phy_id; + NIC_SRAM_DATA_CFG_PHY_TYPE_FIBER) + eeprom_phy_serdes = 1; - tg3_read_mem(tp, NIC_SRAM_DATA_PHY_ID, &nic_phy_id); - if (nic_phy_id != 0) { - u32 id1 = nic_phy_id & NIC_SRAM_DATA_PHY_ID1_MASK; - u32 id2 = nic_phy_id & NIC_SRAM_DATA_PHY_ID2_MASK; - - eeprom_phy_id = (id1 >> 16) << 10; - eeprom_phy_id |= (id2 & 0xfc00) << 16; - eeprom_phy_id |= (id2 & 0x03ff) << 0; - } - } + tg3_read_mem(tp, NIC_SRAM_DATA_PHY_ID, &nic_phy_id); + if (nic_phy_id != 0) { + u32 id1 = nic_phy_id & NIC_SRAM_DATA_PHY_ID1_MASK; + u32 id2 = nic_phy_id & NIC_SRAM_DATA_PHY_ID2_MASK; + + eeprom_phy_id = (id1 >> 16) << 10; + eeprom_phy_id |= (id2 & 0xfc00) << 16; + eeprom_phy_id |= (id2 & 0x03ff) << 0; + } else + eeprom_phy_id = 0; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) { tg3_read_mem(tp, NIC_SRAM_DATA_CFG_2, &led_cfg); @@ -7009,6 +7151,10 @@ static int __devinit tg3_phy_probe(struc } if (nic_cfg & NIC_SRAM_DATA_CFG_FIBER_WOL) tp->tg3_flags |= TG3_FLAG_SERDES_WOL_CAP; + + tg3_read_mem(tp, NIC_SRAM_DATA_PHY_ID, &cfg2); + if (cfg2 & (1 << 17)) + tp->tg3_flags2 |= TG3_FLG2_CAPACITIVE_COUPLING; } /* Reading the PHY ID register can conflict with ASF @@ -7035,20 +7181,31 @@ static int __devinit tg3_phy_probe(struc if (!err && KNOWN_PHY_ID(hw_phy_id_masked)) { tp->phy_id = hw_phy_id; + if (hw_phy_id_masked == PHY_ID_BCM8002) + tp->tg3_flags2 |= TG3_FLG2_PHY_SERDES; } else { - /* phy_id currently holds the value found in the - * subsys_id_to_phy_id[] table or PHY_ID_INVALID - * if a match was not found there. - */ - if (tp->phy_id == PHY_ID_INVALID) { - if (!eeprom_signature_found || - !KNOWN_PHY_ID(eeprom_phy_id & PHY_ID_MASK)) - return -ENODEV; + if (eeprom_signature_found) { tp->phy_id = eeprom_phy_id; + if (eeprom_phy_serdes) + tp->tg3_flags2 |= TG3_FLG2_PHY_SERDES; + } else { + struct subsys_tbl_ent *p; + + /* No eeprom signature? Try the hardcoded + * subsys device table. + */ + p = lookup_by_subsys(tp); + if (!p) + return -ENODEV; + + tp->phy_id = p->phy_id; + if (!tp->phy_id || + tp->phy_id == PHY_ID_BCM8002) + tp->tg3_flags2 |= TG3_FLG2_PHY_SERDES; } } - if (tp->phy_id != PHY_ID_SERDES && + if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) && !(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) { u32 bmsr, adv_reg, tg3_ctrl; @@ -7105,7 +7262,7 @@ skip_phy_reset: if (!eeprom_signature_found) tp->led_ctrl = LED_CTRL_MODE_PHY_1; - if (tp->phy_id == PHY_ID_SERDES) + if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) tp->link_config.advertising = (ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full | @@ -7492,12 +7649,14 @@ static int __devinit tg3_get_invariants( grc_misc_cfg = tr32(GRC_MISC_CFG); grc_misc_cfg &= GRC_MISC_CFG_BOARD_ID_MASK; + /* Broadcom's driver says that CIOBE multisplit has a bug */ +#if 0 if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 && grc_misc_cfg == GRC_MISC_CFG_BOARD_ID_5704CIOBE) { tp->tg3_flags |= TG3_FLAG_SPLIT_MODE; tp->split_mode_max_reqs = SPLIT_MODE_5704_MAX_REQ; } - +#endif if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 && (grc_misc_cfg == GRC_MISC_CFG_BOARD_ID_5788 || grc_misc_cfg == GRC_MISC_CFG_BOARD_ID_5788M)) @@ -7524,7 +7683,7 @@ static int __devinit tg3_get_invariants( tg3_read_partno(tp); - if (tp->phy_id == PHY_ID_SERDES) { + if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) { tp->tg3_flags &= ~TG3_FLAG_USE_MI_INTERRUPT; } else { if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) @@ -7547,13 +7706,13 @@ static int __devinit tg3_get_invariants( * upon subsystem IDs. */ if (tp->pdev->subsystem_vendor == PCI_VENDOR_ID_DELL && - tp->phy_id != PHY_ID_SERDES) { + !(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)) { tp->tg3_flags |= (TG3_FLAG_USE_MI_INTERRUPT | TG3_FLAG_USE_LINKCHG_REG); } /* For all SERDES we poll the MAC status register. */ - if (tp->phy_id == PHY_ID_SERDES) + if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) tp->tg3_flags |= TG3_FLAG_POLL_SERDES; else tp->tg3_flags &= ~TG3_FLAG_POLL_SERDES; @@ -7988,8 +8147,8 @@ static char * __devinit tg3_phy_string(s case PHY_ID_BCM5704: return "5704"; case PHY_ID_BCM5705: return "5705"; case PHY_ID_BCM5750: return "5750"; - case PHY_ID_BCM8002: return "8002"; - case PHY_ID_SERDES: return "serdes"; + case PHY_ID_BCM8002: return "8002/serdes"; + case 0: return "serdes"; default: return "unknown"; }; } @@ -8371,11 +8530,11 @@ static int tg3_suspend(struct pci_dev *p tp->timer.expires = jiffies + tp->timer_offset; add_timer(&tp->timer); - spin_unlock(&tp->tx_lock); - spin_unlock_irq(&tp->lock); - netif_device_attach(dev); tg3_netif_start(tp); + + spin_unlock(&tp->tx_lock); + spin_unlock_irq(&tp->lock); } return err; @@ -8408,11 +8567,11 @@ static int tg3_resume(struct pci_dev *pd tg3_enable_ints(tp); + tg3_netif_start(tp); + spin_unlock(&tp->tx_lock); spin_unlock_irq(&tp->lock); - tg3_netif_start(tp); - return 0; } --- linux-2.6.9-rc1/drivers/net/tg3.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/net/tg3.h 2004-09-02 20:51:52.000000000 -0700 @@ -124,6 +124,7 @@ #define CHIPREV_ID_5705_A3 0x3003 #define CHIPREV_ID_5750_A0 0x4000 #define CHIPREV_ID_5750_A1 0x4001 +#define CHIPREV_ID_5750_A3 0x4003 #define GET_ASIC_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 12) #define ASIC_REV_5700 0x07 #define ASIC_REV_5701 0x00 @@ -2089,6 +2090,9 @@ struct tg3 { #define TG3_FLG2_PCI_EXPRESS 0x00000200 #define TG3_FLG2_ASF_NEW_HANDSHAKE 0x00000400 #define TG3_FLG2_HW_AUTONEG 0x00000800 +#define TG3_FLG2_PHY_JUST_INITTED 0x00001000 +#define TG3_FLG2_PHY_SERDES 0x00002000 +#define TG3_FLG2_CAPACITIVE_COUPLING 0x00004000 u32 split_mode_max_reqs; #define SPLIT_MODE_5704_MAX_REQ 3 @@ -2136,7 +2140,6 @@ struct tg3 { #define PHY_ID_BCM5705 0x600081a0 #define PHY_ID_BCM5750 0x60008180 #define PHY_ID_BCM8002 0x60010140 -#define PHY_ID_SERDES 0xfeedbee0 #define PHY_ID_INVALID 0xffffffff #define PHY_ID_REV_MASK 0x0000000f #define PHY_REV_BCM5401_B0 0x1 @@ -2159,7 +2162,7 @@ struct tg3 { (X) == PHY_ID_BCM5411 || (X) == PHY_ID_BCM5701 || \ (X) == PHY_ID_BCM5703 || (X) == PHY_ID_BCM5704 || \ (X) == PHY_ID_BCM5705 || (X) == PHY_ID_BCM5750 || \ - (X) == PHY_ID_BCM8002 || (X) == PHY_ID_SERDES) + (X) == PHY_ID_BCM8002) struct tg3_hw_stats *hw_stats; dma_addr_t stats_mapping; --- linux-2.6.9-rc1/drivers/net/tokenring/ibmtr.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/drivers/net/tokenring/ibmtr.c 2004-09-02 20:51:52.000000000 -0700 @@ -895,7 +895,7 @@ static int tok_open(struct net_device *d ti->sram_virt &= ~1; /* to reverse what we do in tok_close */ /* init the spinlock */ - ti->lock = (spinlock_t) SPIN_LOCK_UNLOCKED; + ti->lock = SPIN_LOCK_UNLOCKED; init_timer(&ti->tr_timer); i = tok_init_card(dev); --- linux-2.6.9-rc1/drivers/net/tulip/de4x5.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/drivers/net/tulip/de4x5.c 2004-09-03 00:56:53.966956424 -0700 @@ -1141,7 +1141,7 @@ de4x5_hw_init(struct net_device *dev, u_ lp->asBitValid = TRUE; lp->timeout = -1; lp->gendev = gendev; - lp->lock = (spinlock_t) SPIN_LOCK_UNLOCKED; + lp->lock = SPIN_LOCK_UNLOCKED; init_timer(&lp->timer); de4x5_parse_params(dev); @@ -1316,7 +1316,7 @@ de4x5_open(struct net_device *dev) ** Re-initialize the DE4X5... */ status = de4x5_init(dev); - lp->lock = (spinlock_t) SPIN_LOCK_UNLOCKED; + lp->lock = SPIN_LOCK_UNLOCKED; lp->state = OPEN; de4x5_dbg_open(dev); @@ -2242,8 +2242,13 @@ static int __devinit de4x5_pci_probe (st return -ENODEV; /* Ok, the device seems to be for us. */ - if (!(dev = alloc_etherdev (sizeof (struct de4x5_private)))) - return -ENOMEM; + if ((error = pci_enable_device (pdev))) + return error; + + if (!(dev = alloc_etherdev (sizeof (struct de4x5_private)))) { + error = -ENOMEM; + goto disable_dev; + } lp = netdev_priv(dev); lp->bus = PCI; @@ -2327,6 +2332,8 @@ static int __devinit de4x5_pci_probe (st release_region (iobase, DE4X5_PCI_TOTAL_SIZE); free_dev: free_netdev (dev); + disable_dev: + pci_disable_device (pdev); return error; } @@ -2341,6 +2348,7 @@ static void __devexit de4x5_pci_remove ( unregister_netdev (dev); free_netdev (dev); release_region (iobase, DE4X5_PCI_TOTAL_SIZE); + pci_disable_device (pdev); } static struct pci_device_id de4x5_pci_tbl[] = { @@ -5081,7 +5089,7 @@ mii_get_phy(struct net_device *dev) lp->useMII = TRUE; /* Search the MII address space for possible PHY devices */ - for (n=0, lp->mii_cnt=0, i=1; !((i==1) && (n==1)); i=(++i)%DE4X5_MAX_MII) { + for (n=0, lp->mii_cnt=0, i=1; !((i==1) && (n==1)); i=(i+1)%DE4X5_MAX_MII) { lp->phy[lp->active].addr = i; if (i==0) n++; /* Count cycles */ while (de4x5_reset_phy(dev)<0) udelay(100);/* Wait for reset */ --- linux-2.6.9-rc1/drivers/net/tulip/dmfe.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/net/tulip/dmfe.c 2004-09-02 20:51:52.000000000 -0700 @@ -314,13 +314,13 @@ static u16 phy_read_1bit(unsigned long); static u8 dmfe_sense_speed(struct dmfe_board_info *); static void dmfe_process_mode(struct dmfe_board_info *); static void dmfe_timer(unsigned long); +static inline u32 cal_CRC(unsigned char *, unsigned int, u8); static void dmfe_rx_packet(struct DEVICE *, struct dmfe_board_info *); static void dmfe_free_tx_pkt(struct DEVICE *, struct dmfe_board_info *); static void dmfe_reuse_skb(struct dmfe_board_info *, struct sk_buff *); static void dmfe_dynamic_reset(struct DEVICE *); static void dmfe_free_rxbuffer(struct dmfe_board_info *); static void dmfe_init_dm910x(struct DEVICE *); -static inline u32 cal_CRC(unsigned char *, unsigned int, u8); static void dmfe_parse_srom(struct dmfe_board_info *); static void dmfe_program_DM9801(struct dmfe_board_info *, int); static void dmfe_program_DM9802(struct dmfe_board_info *); @@ -885,6 +885,20 @@ static void dmfe_free_tx_pkt(struct DEVI /* + * Calculate the CRC valude of the Rx packet + * flag = 1 : return the reverse CRC (for the received packet CRC) + * 0 : return the normal CRC (for Hash Table index) + */ + +static inline u32 cal_CRC(unsigned char * Data, unsigned int Len, u8 flag) +{ + u32 crc = crc32(~0, Data, Len); + if (flag) crc = ~crc; + return crc; +} + + +/* * Receive the come packet and pass to upper layer */ @@ -1774,20 +1788,6 @@ static u16 phy_read_1bit(unsigned long i /* - * Calculate the CRC valude of the Rx packet - * flag = 1 : return the reverse CRC (for the received packet CRC) - * 0 : return the normal CRC (for Hash Table index) - */ - -static inline u32 cal_CRC(unsigned char * Data, unsigned int Len, u8 flag) -{ - u32 crc = crc32(~0, Data, Len); - if (flag) crc = ~crc; - return crc; -} - - -/* * Parser SROM and media mode */ --- linux-2.6.9-rc1/drivers/net/tulip/tulip_core.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/net/tulip/tulip_core.c 2004-09-03 00:56:32.590206184 -0700 @@ -830,30 +830,18 @@ static struct net_device_stats *tulip_ge } -static int netdev_ethtool_ioctl(struct net_device *dev, void __user *useraddr) +static void tulip_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct tulip_private *np = netdev_priv(dev); - u32 ethcmd; - - if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; - strcpy(info.driver, DRV_NAME); - strcpy(info.version, DRV_VERSION); - strcpy(info.bus_info, pci_name(np->pdev)); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - - } - - return -EOPNOTSUPP; + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->bus_info, pci_name(np->pdev)); } +static struct ethtool_ops ops = { + .get_drvinfo = tulip_get_drvinfo +}; + /* Provide ioctl() calls to examine the MII xcvr state. */ static int private_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) { @@ -865,9 +853,6 @@ static int private_ioctl (struct net_dev unsigned int regnum = data->reg_num; switch (cmd) { - case SIOCETHTOOL: - return netdev_ethtool_ioctl(dev, rq->ifr_data); - case SIOCGMIIPHY: /* Get address of MII PHY in use. */ if (tp->mii_cnt) data->phy_id = phy; @@ -1644,6 +1629,7 @@ static int __devinit tulip_init_one (str #ifdef CONFIG_NET_POLL_CONTROLLER dev->poll_controller = &poll_tulip; #endif + SET_ETHTOOL_OPS(dev, &ops); if (register_netdev(dev)) goto err_out_free_ring; --- linux-2.6.9-rc1/drivers/net/tulip/xircom_tulip_cb.c 2004-08-24 00:01:55.000000000 -0700 +++ 25/drivers/net/tulip/xircom_tulip_cb.c 2004-09-03 00:56:32.594205576 -0700 @@ -350,6 +350,7 @@ static struct net_device_stats *xircom_g static int xircom_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static void set_rx_mode(struct net_device *dev); static void check_duplex(struct net_device *dev); +static struct ethtool_ops ops; /* The Xircom cards are picky about when certain bits in CSR6 can be @@ -450,7 +451,7 @@ static void __devinit read_mac_address(s */ static void find_mii_transceivers(struct net_device *dev) { - struct xircom_private *tp = dev->priv; + struct xircom_private *tp = netdev_priv(dev); int phy, phy_idx; if (media_cap[tp->default_port] & MediaIsMII) { @@ -505,7 +506,7 @@ static void find_mii_transceivers(struct */ static void transceiver_voodoo(struct net_device *dev) { - struct xircom_private *tp = dev->priv; + struct xircom_private *tp = netdev_priv(dev); long ioaddr = dev->base_addr; /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */ @@ -584,7 +585,7 @@ static int __devinit xircom_init_one(str /* Clear the missed-packet counter. */ (volatile int)inl(ioaddr + CSR8); - tp = dev->priv; + tp = netdev_priv(dev); tp->lock = SPIN_LOCK_UNLOCKED; tp->pdev = pdev; @@ -626,6 +627,7 @@ static int __devinit xircom_init_one(str #endif dev->tx_timeout = xircom_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; + SET_ETHTOOL_OPS(dev, &ops); transceiver_voodoo(dev); @@ -749,7 +751,7 @@ static void mdio_write(struct net_device static void xircom_up(struct net_device *dev) { - struct xircom_private *tp = dev->priv; + struct xircom_private *tp = netdev_priv(dev); long ioaddr = dev->base_addr; int i; @@ -804,7 +806,7 @@ xircom_up(struct net_device *dev) static int xircom_open(struct net_device *dev) { - struct xircom_private *tp = dev->priv; + struct xircom_private *tp = netdev_priv(dev); if (request_irq(dev->irq, &xircom_interrupt, SA_SHIRQ, dev->name, dev)) return -EAGAIN; @@ -818,7 +820,7 @@ xircom_open(struct net_device *dev) static void xircom_tx_timeout(struct net_device *dev) { - struct xircom_private *tp = dev->priv; + struct xircom_private *tp = netdev_priv(dev); long ioaddr = dev->base_addr; if (media_cap[dev->if_port] & MediaIsMII) { @@ -870,7 +872,7 @@ static void xircom_tx_timeout(struct net /* Initialize the Rx and Tx rings, along with various 'dev' bits. */ static void xircom_init_ring(struct net_device *dev) { - struct xircom_private *tp = dev->priv; + struct xircom_private *tp = netdev_priv(dev); int i; tp->tx_full = 0; @@ -919,7 +921,7 @@ static void xircom_init_ring(struct net_ static int xircom_start_xmit(struct sk_buff *skb, struct net_device *dev) { - struct xircom_private *tp = dev->priv; + struct xircom_private *tp = netdev_priv(dev); int entry; u32 flag; @@ -971,7 +973,7 @@ xircom_start_xmit(struct sk_buff *skb, s static void xircom_media_change(struct net_device *dev) { - struct xircom_private *tp = dev->priv; + struct xircom_private *tp = netdev_priv(dev); long ioaddr = dev->base_addr; u16 reg0, reg1, reg4, reg5; u32 csr6 = inl(ioaddr + CSR6), newcsr6; @@ -1032,7 +1034,7 @@ static void xircom_media_change(struct n static void check_duplex(struct net_device *dev) { - struct xircom_private *tp = dev->priv; + struct xircom_private *tp = netdev_priv(dev); u16 reg0; mdio_write(dev, tp->phys[0], MII_BMCR, BMCR_RESET); @@ -1065,7 +1067,7 @@ static void check_duplex(struct net_devi static irqreturn_t xircom_interrupt(int irq, void *dev_instance, struct pt_regs *regs) { struct net_device *dev = dev_instance; - struct xircom_private *tp = dev->priv; + struct xircom_private *tp = netdev_priv(dev); long ioaddr = dev->base_addr; int csr5, work_budget = max_interrupt_work; int handled = 0; @@ -1203,7 +1205,7 @@ static irqreturn_t xircom_interrupt(int static int xircom_rx(struct net_device *dev) { - struct xircom_private *tp = dev->priv; + struct xircom_private *tp = netdev_priv(dev); int entry = tp->cur_rx % RX_RING_SIZE; int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx; int work_done = 0; @@ -1303,7 +1305,7 @@ static void xircom_down(struct net_device *dev) { long ioaddr = dev->base_addr; - struct xircom_private *tp = dev->priv; + struct xircom_private *tp = netdev_priv(dev); /* Disable interrupts by clearing the interrupt mask. */ outl(0, ioaddr + CSR7); @@ -1321,7 +1323,7 @@ static int xircom_close(struct net_device *dev) { long ioaddr = dev->base_addr; - struct xircom_private *tp = dev->priv; + struct xircom_private *tp = netdev_priv(dev); int i; if (xircom_debug > 1) @@ -1359,7 +1361,7 @@ xircom_close(struct net_device *dev) static struct net_device_stats *xircom_get_stats(struct net_device *dev) { - struct xircom_private *tp = dev->priv; + struct xircom_private *tp = netdev_priv(dev); long ioaddr = dev->base_addr; if (netif_device_present(dev)) @@ -1368,18 +1370,10 @@ static struct net_device_stats *xircom_g return &tp->stats; } - -static int xircom_ethtool_ioctl(struct net_device *dev, void __user *useraddr) +static int xircom_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) { - struct ethtool_cmd ecmd; - struct xircom_private *tp = dev->priv; - - if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) - return -EFAULT; - - switch (ecmd.cmd) { - case ETHTOOL_GSET: - ecmd.supported = + struct xircom_private *tp = netdev_priv(dev); + ecmd->supported = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | @@ -1387,98 +1381,90 @@ static int xircom_ethtool_ioctl(struct n SUPPORTED_Autoneg | SUPPORTED_MII; - ecmd.advertising = ADVERTISED_MII; - if (tp->advertising[0] & ADVERTISE_10HALF) - ecmd.advertising |= ADVERTISED_10baseT_Half; - if (tp->advertising[0] & ADVERTISE_10FULL) - ecmd.advertising |= ADVERTISED_10baseT_Full; - if (tp->advertising[0] & ADVERTISE_100HALF) - ecmd.advertising |= ADVERTISED_100baseT_Half; - if (tp->advertising[0] & ADVERTISE_100FULL) - ecmd.advertising |= ADVERTISED_100baseT_Full; - if (tp->autoneg) { - ecmd.advertising |= ADVERTISED_Autoneg; - ecmd.autoneg = AUTONEG_ENABLE; - } else - ecmd.autoneg = AUTONEG_DISABLE; - - ecmd.port = PORT_MII; - ecmd.transceiver = XCVR_INTERNAL; - ecmd.phy_address = tp->phys[0]; - ecmd.speed = tp->speed100 ? SPEED_100 : SPEED_10; - ecmd.duplex = tp->full_duplex ? DUPLEX_FULL : DUPLEX_HALF; - ecmd.maxtxpkt = TX_RING_SIZE / 2; - ecmd.maxrxpkt = 0; + ecmd->advertising = ADVERTISED_MII; + if (tp->advertising[0] & ADVERTISE_10HALF) + ecmd->advertising |= ADVERTISED_10baseT_Half; + if (tp->advertising[0] & ADVERTISE_10FULL) + ecmd->advertising |= ADVERTISED_10baseT_Full; + if (tp->advertising[0] & ADVERTISE_100HALF) + ecmd->advertising |= ADVERTISED_100baseT_Half; + if (tp->advertising[0] & ADVERTISE_100FULL) + ecmd->advertising |= ADVERTISED_100baseT_Full; + if (tp->autoneg) { + ecmd->advertising |= ADVERTISED_Autoneg; + ecmd->autoneg = AUTONEG_ENABLE; + } else + ecmd->autoneg = AUTONEG_DISABLE; - if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; + ecmd->port = PORT_MII; + ecmd->transceiver = XCVR_INTERNAL; + ecmd->phy_address = tp->phys[0]; + ecmd->speed = tp->speed100 ? SPEED_100 : SPEED_10; + ecmd->duplex = tp->full_duplex ? DUPLEX_FULL : DUPLEX_HALF; + ecmd->maxtxpkt = TX_RING_SIZE / 2; + ecmd->maxrxpkt = 0; + return 0; +} - case ETHTOOL_SSET: { - u16 autoneg, speed100, full_duplex; +static int xircom_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct xircom_private *tp = netdev_priv(dev); + u16 autoneg, speed100, full_duplex; - autoneg = (ecmd.autoneg == AUTONEG_ENABLE); - speed100 = (ecmd.speed == SPEED_100); - full_duplex = (ecmd.duplex == DUPLEX_FULL); - - tp->autoneg = autoneg; - if (speed100 != tp->speed100 || - full_duplex != tp->full_duplex) { - tp->speed100 = speed100; - tp->full_duplex = full_duplex; - /* change advertising bits */ - tp->advertising[0] &= ~(ADVERTISE_10HALF | - ADVERTISE_10FULL | - ADVERTISE_100HALF | - ADVERTISE_100FULL | - ADVERTISE_100BASE4); - if (speed100) { - if (full_duplex) - tp->advertising[0] |= ADVERTISE_100FULL; - else - tp->advertising[0] |= ADVERTISE_100HALF; - } else { - if (full_duplex) - tp->advertising[0] |= ADVERTISE_10FULL; - else - tp->advertising[0] |= ADVERTISE_10HALF; - } + autoneg = (ecmd->autoneg == AUTONEG_ENABLE); + speed100 = (ecmd->speed == SPEED_100); + full_duplex = (ecmd->duplex == DUPLEX_FULL); + + tp->autoneg = autoneg; + if (speed100 != tp->speed100 || + full_duplex != tp->full_duplex) { + tp->speed100 = speed100; + tp->full_duplex = full_duplex; + /* change advertising bits */ + tp->advertising[0] &= ~(ADVERTISE_10HALF | + ADVERTISE_10FULL | + ADVERTISE_100HALF | + ADVERTISE_100FULL | + ADVERTISE_100BASE4); + if (speed100) { + if (full_duplex) + tp->advertising[0] |= ADVERTISE_100FULL; + else + tp->advertising[0] |= ADVERTISE_100HALF; + } else { + if (full_duplex) + tp->advertising[0] |= ADVERTISE_10FULL; + else + tp->advertising[0] |= ADVERTISE_10HALF; } - check_duplex(dev); - return 0; - } - - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info; - memset(&info, 0, sizeof(info)); - info.cmd = ecmd.cmd; - strcpy(info.driver, DRV_NAME); - strcpy(info.version, DRV_VERSION); - *info.fw_version = 0; - strcpy(info.bus_info, pci_name(tp->pdev)); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; } + check_duplex(dev); + return 0; +} - default: - return -EOPNOTSUPP; - } +static void xircom_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + struct xircom_private *tp = netdev_priv(dev); + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->bus_info, pci_name(tp->pdev)); } +static struct ethtool_ops ops = { + .get_settings = xircom_get_settings, + .set_settings = xircom_set_settings, + .get_drvinfo = xircom_get_drvinfo, +}; /* Provide ioctl() calls to examine the MII xcvr state. */ static int xircom_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { - struct xircom_private *tp = dev->priv; + struct xircom_private *tp = netdev_priv(dev); u16 *data = (u16 *)&rq->ifr_ifru; int phy = tp->phys[0] & 0x1f; unsigned long flags; switch(cmd) { - case SIOCETHTOOL: - return xircom_ethtool_ioctl(dev, rq->ifr_data); - /* Legacy mii-diag interface */ case SIOCGMIIPHY: /* Get address of MII PHY in use. */ if (tp->mii_cnt) @@ -1531,7 +1517,7 @@ static int xircom_ioctl(struct net_devic when re-entered but still correct. */ static void set_rx_mode(struct net_device *dev) { - struct xircom_private *tp = dev->priv; + struct xircom_private *tp = netdev_priv(dev); struct dev_mc_list *mclist; long ioaddr = dev->base_addr; int csr6 = inl(ioaddr + CSR6); @@ -1672,7 +1658,7 @@ MODULE_DEVICE_TABLE(pci, xircom_pci_tabl static int xircom_suspend(struct pci_dev *pdev, u32 state) { struct net_device *dev = pci_get_drvdata(pdev); - struct xircom_private *tp = dev->priv; + struct xircom_private *tp = netdev_priv(dev); printk(KERN_INFO "xircom_suspend(%s)\n", dev->name); if (tp->open) xircom_down(dev); @@ -1688,7 +1674,7 @@ static int xircom_suspend(struct pci_dev static int xircom_resume(struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); - struct xircom_private *tp = dev->priv; + struct xircom_private *tp = netdev_priv(dev); printk(KERN_INFO "xircom_resume(%s)\n", dev->name); pci_set_power_state(pdev,0); --- linux-2.6.9-rc1/drivers/net/typhoon.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/drivers/net/typhoon.c 2004-09-03 00:56:32.597205120 -0700 @@ -688,7 +688,7 @@ out: static void typhoon_vlan_rx_register(struct net_device *dev, struct vlan_group *grp) { - struct typhoon *tp = (struct typhoon *) dev->priv; + struct typhoon *tp = netdev_priv(dev); struct cmd_desc xp_cmd; int err; @@ -726,7 +726,7 @@ typhoon_vlan_rx_register(struct net_devi static void typhoon_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) { - struct typhoon *tp = (struct typhoon *) dev->priv; + struct typhoon *tp = netdev_priv(dev); spin_lock_bh(&tp->state_lock); if(tp->vlgrp) tp->vlgrp->vlan_devices[vid] = NULL; @@ -757,7 +757,7 @@ typhoon_tso_fill(struct sk_buff *skb, st static int typhoon_start_tx(struct sk_buff *skb, struct net_device *dev) { - struct typhoon *tp = (struct typhoon *) dev->priv; + struct typhoon *tp = netdev_priv(dev); struct transmit_ring *txRing; struct tx_desc *txd, *first_txd; dma_addr_t skb_dma; @@ -908,7 +908,7 @@ typhoon_start_tx(struct sk_buff *skb, st static void typhoon_set_rx_mode(struct net_device *dev) { - struct typhoon *tp = (struct typhoon *) dev->priv; + struct typhoon *tp = netdev_priv(dev); struct cmd_desc xp_cmd; u32 mc_filter[2]; u16 filter; @@ -1002,7 +1002,7 @@ typhoon_do_get_stats(struct typhoon *tp) static struct net_device_stats * typhoon_get_stats(struct net_device *dev) { - struct typhoon *tp = (struct typhoon *) dev->priv; + struct typhoon *tp = netdev_priv(dev); struct net_device_stats *stats = &tp->stats; struct net_device_stats *saved = &tp->stats_saved; @@ -1155,90 +1155,55 @@ typhoon_ethtool_sset(struct typhoon *tp, return 0; } -static inline int -typhoon_ethtool_ioctl(struct net_device *dev, void __user *useraddr) +static void typhoon_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - struct typhoon *tp = (struct typhoon *) dev->priv; - u32 ethcmd; - - if(copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - - typhoon_ethtool_gdrvinfo(tp, &info); - if(copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - case ETHTOOL_GSET: { - struct ethtool_cmd cmd = { ETHTOOL_GSET }; - - typhoon_ethtool_gset(tp, &cmd); - if(copy_to_user(useraddr, &cmd, sizeof(cmd))) - return -EFAULT; - return 0; - } - case ETHTOOL_SSET: { - struct ethtool_cmd cmd; - if(copy_from_user(&cmd, useraddr, sizeof(cmd))) - return -EFAULT; - - return typhoon_ethtool_sset(tp, &cmd); - } - case ETHTOOL_GLINK:{ - struct ethtool_value edata = { ETHTOOL_GLINK }; - - edata.data = netif_carrier_ok(dev) ? 1 : 0; - if(copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - case ETHTOOL_GWOL: { - struct ethtool_wolinfo wol = { ETHTOOL_GWOL }; - - if(tp->wol_events & TYPHOON_WAKE_LINK_EVENT) - wol.wolopts |= WAKE_PHY; - if(tp->wol_events & TYPHOON_WAKE_MAGIC_PKT) - wol.wolopts |= WAKE_MAGIC; - if(copy_to_user(useraddr, &wol, sizeof(wol))) - return -EFAULT; - return 0; - } - case ETHTOOL_SWOL: { - struct ethtool_wolinfo wol; - - if(copy_from_user(&wol, useraddr, sizeof(wol))) - return -EFAULT; - tp->wol_events = 0; - if(wol.wolopts & WAKE_PHY) - tp->wol_events |= TYPHOON_WAKE_LINK_EVENT; - if(wol.wolopts & WAKE_MAGIC) - tp->wol_events |= TYPHOON_WAKE_MAGIC_PKT; - return 0; - } - default: - break; - } + struct typhoon *tp = netdev_priv(dev); + typhoon_ethtool_gdrvinfo(tp, info); +} - return -EOPNOTSUPP; +static int typhoon_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct typhoon *tp = netdev_priv(dev); + typhoon_ethtool_gset(tp, cmd); + return 0; } -static int -typhoon_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +static int typhoon_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) { - switch (cmd) { - case SIOCETHTOOL: - return typhoon_ethtool_ioctl(dev, ifr->ifr_data); - default: - break; - } + struct typhoon *tp = netdev_priv(dev); + typhoon_ethtool_sset(tp, cmd); + return 0; +} - return -EOPNOTSUPP; +static void typhoon_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct typhoon *tp = netdev_priv(dev); + if (tp->wol_events & TYPHOON_WAKE_LINK_EVENT) + wol->wolopts |= WAKE_PHY; + if (tp->wol_events & TYPHOON_WAKE_MAGIC_PKT) + wol->wolopts |= WAKE_MAGIC; +} + +static int typhoon_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct typhoon *tp = netdev_priv(dev); + tp->wol_events = 0; + if (wol->wolopts & WAKE_PHY) + tp->wol_events |= TYPHOON_WAKE_LINK_EVENT; + if (wol->wolopts & WAKE_MAGIC) + tp->wol_events |= TYPHOON_WAKE_MAGIC_PKT; + return 0; } +static struct ethtool_ops ops = { + .get_drvinfo = typhoon_get_drvinfo, + .get_settings = typhoon_get_settings, + .set_settings = typhoon_set_settings, + .get_link = ethtool_op_get_link, + .get_wol = typhoon_get_wol, + .set_wol = typhoon_set_wol, +}; + static int typhoon_wait_interrupt(unsigned long ioaddr) { @@ -1756,7 +1721,7 @@ typhoon_fill_free_ring(struct typhoon *t static int typhoon_poll(struct net_device *dev, int *total_budget) { - struct typhoon *tp = (struct typhoon *) dev->priv; + struct typhoon *tp = netdev_priv(dev); struct typhoon_indexes *indexes = tp->indexes; int orig_budget = *total_budget; int budget, work_done, done; @@ -2068,7 +2033,7 @@ typhoon_stop_runtime(struct typhoon *tp, static void typhoon_tx_timeout(struct net_device *dev) { - struct typhoon *tp = (struct typhoon *) dev->priv; + struct typhoon *tp = netdev_priv(dev); if(typhoon_reset(dev->base_addr, WaitNoSleep) < 0) { printk(KERN_WARNING "%s: could not reset in tx timeout\n", @@ -2098,7 +2063,7 @@ truely_dead: static int typhoon_open(struct net_device *dev) { - struct typhoon *tp = (struct typhoon *) dev->priv; + struct typhoon *tp = netdev_priv(dev); int err; err = typhoon_wakeup(tp, WaitSleep); @@ -2140,7 +2105,7 @@ out: static int typhoon_close(struct net_device *dev) { - struct typhoon *tp = (struct typhoon *) dev->priv; + struct typhoon *tp = netdev_priv(dev); netif_stop_queue(dev); @@ -2168,7 +2133,7 @@ static int typhoon_resume(struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); - struct typhoon *tp = (struct typhoon *) dev->priv; + struct typhoon *tp = netdev_priv(dev); /* If we're down, resume when we are upped. */ @@ -2200,7 +2165,7 @@ static int typhoon_suspend(struct pci_dev *pdev, u32 state) { struct net_device *dev = pci_get_drvdata(pdev); - struct typhoon *tp = (struct typhoon *) dev->priv; + struct typhoon *tp = netdev_priv(dev); struct cmd_desc xp_cmd; /* If we're down, we're already suspended. @@ -2366,7 +2331,7 @@ typhoon_init_one(struct pci_dev *pdev, c } dev->irq = pdev->irq; - tp = dev->priv; + tp = netdev_priv(dev); tp->shared = (struct typhoon_shared *) shared; tp->shared_dma = shared_dma; tp->pdev = pdev; @@ -2464,9 +2429,9 @@ typhoon_init_one(struct pci_dev *pdev, c dev->watchdog_timeo = TX_TIMEOUT; dev->get_stats = typhoon_get_stats; dev->set_mac_address = typhoon_set_mac_address; - dev->do_ioctl = typhoon_ioctl; dev->vlan_rx_register = typhoon_vlan_rx_register; dev->vlan_rx_kill_vid = typhoon_vlan_rx_kill_vid; + SET_ETHTOOL_OPS(dev, &ops); /* We can handle scatter gather, up to 16 entries, and * we can do IP checksumming (only version 4, doh...) @@ -2537,7 +2502,7 @@ static void __devexit typhoon_remove_one(struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); - struct typhoon *tp = (struct typhoon *) (dev->priv); + struct typhoon *tp = netdev_priv(dev); unregister_netdev(dev); pci_set_power_state(pdev, 0); --- linux-2.6.9-rc1/drivers/net/via-rhine.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/net/via-rhine.c 2004-09-02 20:51:52.000000000 -0700 @@ -346,7 +346,7 @@ enum rhine_revs { VT6105L = 0x8A, VT6107 = 0x8C, VTunknown2 = 0x8E, - VT6105M = 0x90, + VT6105M = 0x90, /* Management adapter */ }; enum rhine_quirks { @@ -485,6 +485,7 @@ struct rhine_private { dma_addr_t tx_bufs_dma; struct pci_dev *pdev; + long pioaddr; struct net_device_stats stats; spinlock_t lock; @@ -593,7 +594,7 @@ static void rhine_power_init(struct net_ default: reason = "Unknown"; } - printk("%s: Woke system up. Reason: %s.\n", + printk(KERN_INFO "%s: Woke system up. Reason: %s.\n", DRV_NAME, reason); } } @@ -703,7 +704,7 @@ static int __devinit rhine_init_one(stru long memaddr; long ioaddr; int io_size, phy_id; - const char *name, *mname; + const char *name; /* when built into the kernel, we only print version if device is found */ #ifndef MODULE @@ -718,41 +719,24 @@ static int __devinit rhine_init_one(stru phy_id = 0; quirks = 0; name = "Rhine"; - mname = "unknown"; if (pci_rev < VTunknown0) { quirks = rqRhineI; io_size = 128; - mname = "VT86C100A"; } else if (pci_rev >= VT6102) { quirks = rqWOL | rqForceReset; if (pci_rev < VT6105) { name = "Rhine II"; quirks |= rqStatusWBRace; /* Rhine-II exclusive */ - if (pci_rev < VT8231) - mname = "VT6102"; - else if (pci_rev < VT8233) - mname = "VT8231"; - else if (pci_rev < VT8235) - mname = "VT8233"; - else if (pci_rev < VT8237) - mname = "VT8235"; - else if (pci_rev < VTunknown1) - mname = "VT8237"; } else { - name = "Rhine III"; phy_id = 1; /* Integrated PHY, phy_id fixed to 1 */ if (pci_rev >= VT6105_B0) quirks |= rq6patterns; - if (pci_rev < VT6105L) - mname = "VT6105"; - else if (pci_rev < VT6107) - mname = "VT6105L"; - else if (pci_rev < VT6105M) - mname = "VT6107"; - else if (pci_rev >= VT6105M) - mname = "Management Adapter VT6105M"; + if (pci_rev < VT6105M) + name = "Rhine III"; + else + name = "Rhine III (Management Adapter)"; } } @@ -790,6 +774,11 @@ static int __devinit rhine_init_one(stru SET_MODULE_OWNER(dev); SET_NETDEV_DEV(dev, &pdev->dev); + rp = netdev_priv(dev); + rp->quirks = quirks; + rp->pioaddr = pioaddr; + rp->pdev = pdev; + rc = pci_request_regions(pdev, DRV_NAME); if (rc) goto err_out_free_netdev; @@ -823,8 +812,6 @@ static int __devinit rhine_init_one(stru #endif /* USE_MMIO */ dev->base_addr = ioaddr; - rp = netdev_priv(dev); - rp->quirks = quirks; /* Get chip registers into a sane state */ rhine_power_init(dev); @@ -846,7 +833,6 @@ static int __devinit rhine_init_one(stru dev->irq = pdev->irq; spin_lock_init(&rp->lock); - rp->pdev = pdev; rp->mii_if.dev = dev; rp->mii_if.mdio_read = mdio_read; rp->mii_if.mdio_write = mdio_write; @@ -874,8 +860,8 @@ static int __devinit rhine_init_one(stru if (rc) goto err_out_unmap; - printk(KERN_INFO "%s: VIA %s (%s) at 0x%lx, ", - dev->name, name, mname, + printk(KERN_INFO "%s: VIA %s at 0x%lx, ", + dev->name, name, #ifdef USE_MMIO memaddr #else @@ -890,7 +876,10 @@ static int __devinit rhine_init_one(stru pci_set_drvdata(pdev, dev); { + u16 mii_cmd; int mii_status = mdio_read(dev, phy_id, 1); + mii_cmd = mdio_read(dev, phy_id, MII_BMCR) & ~BMCR_ISOLATE; + mdio_write(dev, phy_id, MII_BMCR, mii_cmd); if (mii_status != 0xffff && mii_status != 0x0000) { rp->mii_if.advertising = mdio_read(dev, phy_id, 4); printk(KERN_INFO "%s: MII PHY found at address " @@ -1172,7 +1161,7 @@ static int mdio_read(struct net_device * rhine_disable_linkmon(ioaddr, rp->quirks); - writeb(0, ioaddr + MIICmd); + /* rhine_disable_linkmon already cleared MIICmd */ writeb(phy_id, ioaddr + MIIPhyAddr); writeb(regnum, ioaddr + MIIRegAddr); writeb(0x40, ioaddr + MIICmd); /* Trigger read */ @@ -1190,7 +1179,7 @@ static void mdio_write(struct net_device rhine_disable_linkmon(ioaddr, rp->quirks); - writeb(0, ioaddr + MIICmd); + /* rhine_disable_linkmon already cleared MIICmd */ writeb(phy_id, ioaddr + MIIPhyAddr); writeb(regnum, ioaddr + MIIRegAddr); writew(value, ioaddr + MIIData); @@ -1951,11 +1940,70 @@ static void rhine_shutdown (struct devic } +#ifdef CONFIG_PM +static int rhine_suspend(struct pci_dev *pdev, u32 state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct rhine_private *rp = netdev_priv(dev); + unsigned long flags; + + if (!netif_running(dev)) + return 0; + + netif_device_detach(dev); + pci_save_state(pdev, pdev->saved_config_space); + + spin_lock_irqsave(&rp->lock, flags); + rhine_shutdown(&pdev->dev); + spin_unlock_irqrestore(&rp->lock, flags); + + return 0; +} + +static int rhine_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct rhine_private *rp = netdev_priv(dev); + unsigned long flags; + int ret; + + if (!netif_running(dev)) + return 0; + + ret = pci_set_power_state(pdev, 0); + if (debug > 1) + printk(KERN_INFO "%s: Entering power state D0 %s (%d).\n", + dev->name, ret ? "failed" : "succeeded", ret); + + pci_restore_state(pdev, pdev->saved_config_space); + + spin_lock_irqsave(&rp->lock, flags); +#ifdef USE_MMIO + enable_mmio(rp->pioaddr, rp->quirks); +#endif + rhine_power_init(dev); + free_tbufs(dev); + free_rbufs(dev); + alloc_tbufs(dev); + alloc_rbufs(dev); + init_registers(dev); + spin_unlock_irqrestore(&rp->lock, flags); + + netif_device_attach(dev); + + return 0; +} +#endif /* CONFIG_PM */ + static struct pci_driver rhine_driver = { .name = DRV_NAME, .id_table = rhine_pci_tbl, .probe = rhine_init_one, .remove = __devexit_p(rhine_remove_one), +#ifdef CONFIG_PM + .suspend = rhine_suspend, + .resume = rhine_resume, +#endif /* CONFIG_PM */ .driver = { .shutdown = rhine_shutdown, } --- linux-2.6.9-rc1/drivers/net/via-velocity.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/net/via-velocity.c 2004-09-02 20:51:52.000000000 -0700 @@ -262,6 +262,7 @@ static u32 check_connection_type(struct static int velocity_set_media_mode(struct velocity_info *vptr, u32 mii_status); #ifdef CONFIG_PM + static int velocity_suspend(struct pci_dev *pdev, u32 state); static int velocity_resume(struct pci_dev *pdev); @@ -270,9 +271,26 @@ static int velocity_netdev_event(struct static struct notifier_block velocity_inetaddr_notifier = { .notifier_call = velocity_netdev_event, }; -static int velocity_notifier_registered; -#endif /* CONFIG_PM */ +static spinlock_t velocity_dev_list_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD(velocity_dev_list); + +static void velocity_register_notifier(void) +{ + register_inetaddr_notifier(&velocity_inetaddr_notifier); +} + +static void velocity_unregister_notifier(void) +{ + unregister_inetaddr_notifier(&velocity_inetaddr_notifier); +} + +#else /* CONFIG_PM */ + +#define velocity_register_notifier() do {} while (0) +#define velocity_unregister_notifier() do {} while (0) + +#endif /* !CONFIG_PM */ /* * Internal board variants. At the moment we have only one @@ -327,6 +345,14 @@ static void __devexit velocity_remove1(s struct net_device *dev = pci_get_drvdata(pdev); struct velocity_info *vptr = dev->priv; +#ifdef CONFIG_PM + unsigned long flags; + + spin_lock_irqsave(&velocity_dev_list_lock, flags); + if (!list_empty(&velocity_dev_list)) + list_del(&vptr->list); + spin_unlock_irqrestore(&velocity_dev_list_lock, flags); +#endif unregister_netdev(dev); iounmap(vptr->mac_regs); pci_release_regions(pdev); @@ -782,13 +808,16 @@ static int __devinit velocity_found1(str /* and leave the chip powered down */ pci_set_power_state(pdev, 3); -out: #ifdef CONFIG_PM - if (ret == 0 && !velocity_notifier_registered) { - velocity_notifier_registered = 1; - register_inetaddr_notifier(&velocity_inetaddr_notifier); + { + unsigned long flags; + + spin_lock_irqsave(&velocity_dev_list_lock, flags); + list_add(&vptr->list, &velocity_dev_list); + spin_unlock_irqrestore(&velocity_dev_list_lock, flags); } #endif +out: return ret; err_iounmap: @@ -843,6 +872,8 @@ static void __devinit velocity_init_info spin_lock_init(&vptr->lock); spin_lock_init(&vptr->xmit_lock); + + INIT_LIST_HEAD(&vptr->list); } /** @@ -2211,8 +2242,11 @@ static struct pci_driver velocity_driver static int __init velocity_init_module(void) { int ret; - ret = pci_module_init(&velocity_driver); + velocity_register_notifier(); + ret = pci_module_init(&velocity_driver); + if (ret < 0) + velocity_unregister_notifier(); return ret; } @@ -2227,12 +2261,7 @@ static int __init velocity_init_module(v static void __exit velocity_cleanup_module(void) { -#ifdef CONFIG_PM - if (velocity_notifier_registered) { - unregister_inetaddr_notifier(&velocity_inetaddr_notifier); - velocity_notifier_registered = 0; - } -#endif + velocity_unregister_notifier(); pci_unregister_driver(&velocity_driver); } @@ -3252,13 +3281,20 @@ static int velocity_resume(struct pci_de static int velocity_netdev_event(struct notifier_block *nb, unsigned long notification, void *ptr) { struct in_ifaddr *ifa = (struct in_ifaddr *) ptr; - struct net_device *dev; - struct velocity_info *vptr; if (ifa) { - dev = ifa->ifa_dev->dev; - vptr = dev->priv; - velocity_get_ip(vptr); + struct net_device *dev = ifa->ifa_dev->dev; + struct velocity_info *vptr; + unsigned long flags; + + spin_lock_irqsave(&velocity_dev_list_lock, flags); + list_for_each_entry(vptr, &velocity_dev_list, list) { + if (vptr->dev == dev) { + velocity_get_ip(vptr); + break; + } + } + spin_unlock_irqrestore(&velocity_dev_list_lock, flags); } return NOTIFY_DONE; } --- linux-2.6.9-rc1/drivers/net/via-velocity.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/net/via-velocity.h 2004-09-02 20:51:52.000000000 -0700 @@ -1733,14 +1733,13 @@ struct velocity_opt { }; struct velocity_info { - struct velocity_info *next; - struct velocity_info *prev; + struct list_head list; struct pci_dev *pdev; struct net_device *dev; struct net_device_stats stats; -#if CONFIG_PM +#ifdef CONFIG_PM u32 pci_state[16]; #endif --- linux-2.6.9-rc1/drivers/net/wan/cycx_x25.c 2004-08-24 00:02:22.000000000 -0700 +++ 25/drivers/net/wan/cycx_x25.c 2004-09-02 20:51:52.000000000 -0700 @@ -186,7 +186,7 @@ static void nibble_to_byte(u8 *s, u8 *d, reset_timer(struct net_device *dev); static u8 bps_to_speed_code(u32 bps); -static u8 log2(u32 n); +static u8 cycx_log2(u32 n); static unsigned dec_to_uint(u8 *str, int len); @@ -263,7 +263,7 @@ int cycx_x25_wan_init(struct cycx_device else card->wandev.mtu = 64; - cfg.pktlen = log2(card->wandev.mtu); + cfg.pktlen = cycx_log2(card->wandev.mtu); if (conf->station == WANOPT_DTE) { cfg.locaddr = 3; /* DTE */ @@ -1513,7 +1513,7 @@ static u8 bps_to_speed_code(u32 bps) } /* log base 2 */ -static u8 log2(u32 n) +static u8 cycx_log2(u32 n) { u8 log = 0; --- linux-2.6.9-rc1/drivers/net/wan/lmc/lmc_debug.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/drivers/net/wan/lmc/lmc_debug.c 2004-09-02 20:51:52.000000000 -0700 @@ -64,7 +64,7 @@ void lmcEventLog (u_int32_t EventNum, u_ #endif } -inline void lmc_trace(struct net_device *dev, char *msg){ +void lmc_trace(struct net_device *dev, char *msg){ #ifdef LMC_TRACE unsigned long j = jiffies + 3; /* Wait for 50 ms */ --- linux-2.6.9-rc1/drivers/net/wan/lmc/lmc_main.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/drivers/net/wan/lmc/lmc_main.c 2004-09-03 00:56:32.599204816 -0700 @@ -101,7 +101,6 @@ static int lmc_open(struct net_device *d static int lmc_close(struct net_device *dev); static struct net_device_stats *lmc_get_stats(struct net_device *dev); static irqreturn_t lmc_interrupt(int irq, void *dev_instance, struct pt_regs *regs); -static int lmc_set_config(struct net_device *dev, struct ifmap *map); static void lmc_initcsrs(lmc_softc_t * const sc, lmc_csrptr_t csr_base, size_t csr_size); static void lmc_softreset(lmc_softc_t * const); static void lmc_running_reset(struct net_device *dev); @@ -814,7 +813,6 @@ static void lmc_setup(struct net_device dev->stop = lmc_close; dev->get_stats = lmc_get_stats; dev->do_ioctl = lmc_ioctl; - dev->set_config = lmc_set_config; dev->tx_timeout = lmc_driver_timeout; dev->watchdog_timeo = (HZ); /* 1 second */ @@ -1975,13 +1973,6 @@ static void lmc_softreset (lmc_softc_t * lmc_trace(sc->lmc_device, "lmc_softreset out"); } -static int lmc_set_config(struct net_device *dev, struct ifmap *map) /*fold00*/ -{ - lmc_trace(dev, "lmc_set_config in"); - lmc_trace(dev, "lmc_set_config out"); - return -EOPNOTSUPP; -} - void lmc_gpio_mkinput(lmc_softc_t * const sc, u_int32_t bits) /*fold00*/ { lmc_trace(sc->lmc_device, "lmc_gpio_mkinput in"); --- linux-2.6.9-rc1/drivers/net/wan/pc300_tty.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/drivers/net/wan/pc300_tty.c 2004-09-02 20:51:52.000000000 -0700 @@ -789,6 +789,10 @@ void cpc_tty_receive(pc300dev_t *pc300de cpc_writel(card->hw.scabase + DRX_REG(EDAL, ch), RX_BD_ADDR(ch, pc300chan->rx_last_bd)); } + if (new) { + kfree(new); + new = NULL; + } return; } @@ -834,7 +838,8 @@ void cpc_tty_receive(pc300dev_t *pc300de cpc_tty->name); cpc_tty_rx_disc_frame(pc300chan); rx_len = 0; - kfree((unsigned char *)new); + kfree(new); + new = NULL; break; /* read next frame - while(1) */ } @@ -843,7 +848,8 @@ void cpc_tty_receive(pc300dev_t *pc300de cpc_tty_rx_disc_frame(pc300chan); stats->rx_dropped++; rx_len = 0; - kfree((unsigned char *)new); + kfree(new); + new = NULL; break; /* read next frame - while(1) */ } --- linux-2.6.9-rc1/drivers/net/wan/sbni.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/drivers/net/wan/sbni.c 2004-09-03 00:56:33.071133072 -0700 @@ -294,7 +294,7 @@ sbni_pci_probe( struct net_device *dev { struct pci_dev *pdev = NULL; - while( (pdev = pci_find_class( PCI_CLASS_NETWORK_OTHER << 8, pdev )) + while( (pdev = pci_get_class( PCI_CLASS_NETWORK_OTHER << 8, pdev )) != NULL ) { int pci_irq_line; unsigned long pci_ioaddr; @@ -331,10 +331,14 @@ sbni_pci_probe( struct net_device *dev /* avoiding re-enable dual adapters */ if( (pci_ioaddr & 7) == 0 && pci_enable_device( pdev ) ) { release_region( pci_ioaddr, SBNI_IO_EXTENT ); + pci_dev_put( pdev ); return -EIO; } if( sbni_probe1( dev, pci_ioaddr, pci_irq_line ) ) { SET_NETDEV_DEV(dev, &pdev->dev); + /* not the best thing to do, but this is all messed up + for hotplug systems anyway... */ + pci_dev_put( pdev ); return 0; } } --- linux-2.6.9-rc1/drivers/net/wireless/airo.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/net/wireless/airo.c 2004-09-03 00:57:27.375877496 -0700 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -1188,6 +1189,7 @@ struct airo_info { struct iw_statistics wstats; // wireless stats unsigned long scan_timestamp; /* Time started to scan */ struct iw_spy_data spy_data; + struct iw_public_data wireless_data; #endif /* WIRELESS_EXT */ #ifdef MICSUPPORT /* MIC stuff */ @@ -2626,8 +2628,7 @@ static void wifi_setup(struct net_device dev->set_mac_address = &airo_set_mac_address; dev->do_ioctl = &airo_ioctl; #ifdef WIRELESS_EXT - dev->get_wireless_stats = airo_get_wireless_stats; - dev->wireless_handlers = (struct iw_handler_def *)&airo_handler_def; + dev->wireless_handlers = &airo_handler_def; #endif /* WIRELESS_EXT */ dev->change_mtu = &airo_change_mtu; dev->open = &airo_open; @@ -2654,6 +2655,9 @@ static struct net_device *init_wifidev(s dev->priv = ethdev->priv; dev->irq = ethdev->irq; dev->base_addr = ethdev->base_addr; +#ifdef WIRELESS_EXT + dev->wireless_data = ethdev->wireless_data; +#endif /* WIRELESS_EXT */ memcpy(dev->dev_addr, ethdev->dev_addr, dev->addr_len); err = register_netdev(dev); if (err<0) { @@ -2681,7 +2685,8 @@ int reset_card( struct net_device *dev , } struct net_device *_init_airo_card( unsigned short irq, int port, - int is_pcmcia, struct pci_dev *pci ) + int is_pcmcia, struct pci_dev *pci, + struct device *dmdev ) { struct net_device *dev; struct airo_info *ai; @@ -2733,8 +2738,9 @@ struct net_device *_init_airo_card( unsi dev->set_mac_address = &airo_set_mac_address; dev->do_ioctl = &airo_ioctl; #ifdef WIRELESS_EXT - dev->get_wireless_stats = airo_get_wireless_stats; - dev->wireless_handlers = (struct iw_handler_def *)&airo_handler_def; + dev->wireless_handlers = &airo_handler_def; + ai->wireless_data.spy_data = &ai->spy_data; + dev->wireless_data = &ai->wireless_data; #endif /* WIRELESS_EXT */ dev->change_mtu = &airo_change_mtu; dev->open = &airo_open; @@ -2742,10 +2748,8 @@ struct net_device *_init_airo_card( unsi dev->irq = irq; dev->base_addr = port; - /* what is with PCMCIA ??? */ - if (pci) { - SET_NETDEV_DEV(dev, &pci->dev); - } + SET_NETDEV_DEV(dev, dmdev); + if (test_bit(FLAG_MPI,&ai->flags)) reset_card (dev, 1); @@ -2827,9 +2831,10 @@ err_out_free: return NULL; } -struct net_device *init_airo_card( unsigned short irq, int port, int is_pcmcia ) +struct net_device *init_airo_card( unsigned short irq, int port, int is_pcmcia, + struct device *dmdev) { - return _init_airo_card ( irq, port, is_pcmcia, NULL); + return _init_airo_card ( irq, port, is_pcmcia, NULL, dmdev); } EXPORT_SYMBOL(init_airo_card); @@ -3217,7 +3222,7 @@ badrx: goto exitrx; } } -#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ +#ifdef WIRELESS_SPY if (apriv->spy_data.spy_number > 0) { char *sa; struct iw_quality wstats; @@ -3237,7 +3242,7 @@ badrx: /* Update spy records */ wireless_spy_update(dev, sa, &wstats); } -#endif /* IW_WIRELESS_SPY */ +#endif /* WIRELESS_SPY */ OUT4500( apriv, EVACK, EV_RX); if (test_bit(FLAG_802_11, &apriv->flags)) { @@ -3467,7 +3472,7 @@ badmic: #else memcpy(buffer, ai->rxfids[0].virtual_host_addr, len); #endif -#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ +#ifdef WIRELESS_SPY if (ai->spy_data.spy_number > 0) { char *sa; struct iw_quality wstats; @@ -3479,7 +3484,7 @@ badmic: /* Update spy records */ wireless_spy_update(ai->dev, sa, &wstats); } -#endif /* IW_WIRELESS_SPY */ +#endif /* WIRELESS_SPY */ skb->dev = ai->dev; skb->ip_summed = CHECKSUM_NONE; @@ -5461,9 +5466,9 @@ static int __devinit airo_pci_probe(stru pci_set_master(pdev); if (pdev->device == 0x5000 || pdev->device == 0xa504) - dev = _init_airo_card(pdev->irq, pdev->resource[0].start, 0, pdev); + dev = _init_airo_card(pdev->irq, pdev->resource[0].start, 0, pdev, &pdev->dev); else - dev = _init_airo_card(pdev->irq, pdev->resource[2].start, 0, pdev); + dev = _init_airo_card(pdev->irq, pdev->resource[2].start, 0, pdev, &pdev->dev); if (!dev) return -ENODEV; @@ -5566,7 +5571,7 @@ static int __init airo_init_module( void printk( KERN_INFO "airo: Trying to configure ISA adapter at irq=%d io=0x%x\n", irq[i], io[i] ); - if (init_airo_card( irq[i], io[i], 0 )) + if (init_airo_card( irq[i], io[i], 0, NULL )) have_isa_dev = 1; } @@ -6505,6 +6510,13 @@ static int airo_get_range(struct net_dev range->avg_qual.level = 176; /* -80 dBm */ range->avg_qual.noise = 0; + /* Event capability (kernel + driver) */ + range->event_capa[0] = (IW_EVENT_CAPA_K_0 | + IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) | + IW_EVENT_CAPA_MASK(SIOCGIWAP) | + IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); + range->event_capa[1] = IW_EVENT_CAPA_K_1; + range->event_capa[4] = IW_EVENT_CAPA_MASK(IWEVTXDROP); return 0; } @@ -6872,9 +6884,15 @@ static int airo_get_scan(struct net_devi while((!rc) && (BSSList.index != 0xffff)) { /* Translate to WE format this entry */ current_ev = airo_translate_scan(dev, current_ev, - extra + IW_SCAN_MAX_DATA, + extra + dwrq->length, &BSSList); + /* Check if there is space for one more entry */ + if((extra + dwrq->length - current_ev) <= IW_EV_ADDR_LEN) { + /* Ask user space to try again with a bigger buffer */ + return -E2BIG; + } + /* Read next entry */ rc = PC4500_readrid(ai, RID_BSSLISTNEXT, &BSSList, sizeof(BSSList), 1); @@ -7010,12 +7028,10 @@ static const struct iw_handler_def airo_ .num_standard = sizeof(airo_handler)/sizeof(iw_handler), .num_private = sizeof(airo_private_handler)/sizeof(iw_handler), .num_private_args = sizeof(airo_private_args)/sizeof(struct iw_priv_args), - .standard = (iw_handler *) airo_handler, - .private = (iw_handler *) airo_private_handler, - .private_args = (struct iw_priv_args *) airo_private_args, - .spy_offset = ((void *) (&((struct airo_info *) NULL)->spy_data) - - (void *) NULL), - + .standard = airo_handler, + .private = airo_private_handler, + .private_args = airo_private_args, + .get_wireless_stats = airo_get_wireless_stats, }; #endif /* WIRELESS_EXT */ --- linux-2.6.9-rc1/drivers/net/wireless/airo_cs.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/net/wireless/airo_cs.c 2004-09-03 00:56:48.204832400 -0700 @@ -89,7 +89,7 @@ MODULE_PARM(irq_list, "1-4i"); event handler. */ -struct net_device *init_airo_card( int, int, int ); +struct net_device *init_airo_card( int, int, int, struct device * ); void stop_airo_card( struct net_device *, int ); int reset_airo_card( struct net_device * ); @@ -450,7 +450,7 @@ static void airo_config(dev_link_t *link CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); ((local_info_t*)link->priv)->eth_dev = init_airo_card( link->irq.AssignedIRQ, - link->io.BasePort1, 1 ); + link->io.BasePort1, 1, pcmcia_lookup_device(handle) ); if (!((local_info_t*)link->priv)->eth_dev) goto cs_failed; /* --- linux-2.6.9-rc1/drivers/net/wireless/atmel.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/drivers/net/wireless/atmel.c 2004-09-02 20:51:52.000000000 -0700 @@ -2430,12 +2430,13 @@ static int atmel_ioctl(struct net_device rc = -ENOMEM; break; } - + if (copy_from_user(new_firmware, com.data, com.len)) { + kfree(new_firmware); rc = -EFAULT; break; } - + if (priv->firmware) kfree(priv->firmware); --- linux-2.6.9-rc1/drivers/net/wireless/atmel_cs.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/net/wireless/atmel_cs.c 2004-09-03 00:56:48.205832248 -0700 @@ -347,21 +347,6 @@ static struct { { 0, 0, "11WAVE/11WP611AL-E", "atmel_at76c502e%s.bin", "11WAVE WaveBuddy" } }; -/* This is strictly temporary, until PCMCIA devices get integrated into the device model. */ -static struct device *atmel_device(void) -{ - static char *kobj_name = "atmel_cs"; - - static struct device dev = { - .bus_id = "pcmcia", - }; - dev.kobj.k_name = kmalloc(strlen(kobj_name)+1, GFP_KERNEL); - strcpy(dev.kobj.k_name, kobj_name); - kobject_init(&dev.kobj); - - return &dev; -} - static void atmel_config(dev_link_t *link) { client_handle_t handle; @@ -552,7 +537,7 @@ static void atmel_config(dev_link_t *lin init_atmel_card(link->irq.AssignedIRQ, link->io.BasePort1, card_index == -1 ? NULL : card_table[card_index].firmware, - atmel_device(), + pcmcia_lookup_device(handle), card_present, link); if (!((local_info_t*)link->priv)->eth_dev) --- linux-2.6.9-rc1/drivers/net/wireless/netwave_cs.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/net/wireless/netwave_cs.c 2004-09-03 00:56:48.206832096 -0700 @@ -219,7 +219,6 @@ static void netwave_reset(struct net_dev /* Misc device stuff */ static int netwave_open(struct net_device *dev); /* Open the device */ static int netwave_close(struct net_device *dev); /* Close the device */ -static int netwave_config(struct net_device *dev, struct ifmap *map); /* Packet transmission and Packet reception */ static int netwave_start_xmit( struct sk_buff *skb, struct net_device *dev); @@ -482,7 +481,6 @@ static dev_link_t *netwave_attach(void) /* Netwave specific entries in the device structure */ SET_MODULE_OWNER(dev); dev->hard_start_xmit = &netwave_start_xmit; - dev->set_config = &netwave_config; dev->get_stats = &netwave_get_stats; dev->set_multicast_list = &set_multicast_list; /* wireless extensions */ @@ -1075,6 +1073,8 @@ static void netwave_pcmcia_config(dev_li dev->irq = link->irq.AssignedIRQ; dev->base_addr = link->io.BasePort1; + SET_NETDEV_DEV(dev, pcmcia_lookup_device(handle)); + if (register_netdev(dev) != 0) { printk(KERN_DEBUG "netwave_cs: register_netdev() failed\n"); goto failed; @@ -1289,16 +1289,6 @@ static void netwave_reset(struct net_dev } /* - * Function netwave_config (dev, map) - * - * Configure device, this work is done by netwave_pcmcia_config when a - * card is inserted - */ -static int netwave_config(struct net_device *dev, struct ifmap *map) { - return 0; -} - -/* * Function netwave_hw_xmit (data, len, dev) */ static int netwave_hw_xmit(unsigned char* data, int len, --- linux-2.6.9-rc1/drivers/net/wireless/orinoco_cs.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/net/wireless/orinoco_cs.c 2004-09-03 00:56:48.207831944 -0700 @@ -454,6 +454,7 @@ orinoco_cs_config(dev_link_t *link) /* register_netdev will give us an ethX name */ dev->name[0] = '\0'; + SET_NETDEV_DEV(dev, pcmcia_lookup_device(handle)); /* Tell the stack we exist */ if (register_netdev(dev) != 0) { printk(KERN_ERR PFX "register_netdev() failed\n"); --- linux-2.6.9-rc1/drivers/net/wireless/ray_cs.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/drivers/net/wireless/ray_cs.c 2004-09-03 00:56:48.209831640 -0700 @@ -570,6 +570,7 @@ static void ray_config(dev_link_t *link) return; } + SET_NETDEV_DEV(dev, pcmcia_lookup_device(handle)); i = register_netdev(dev); if (i != 0) { printk("ray_config register_netdev() failed\n"); --- linux-2.6.9-rc1/drivers/net/wireless/wavelan.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/drivers/net/wireless/wavelan.c 2004-09-03 00:56:32.610203144 -0700 @@ -2172,6 +2172,11 @@ static int wavelan_get_range(struct net_ range->num_bitrates = 1; range->bitrate[0] = 2000000; /* 2 Mb/s */ + /* Event capability (kernel + driver) */ + range->event_capa[0] = (IW_EVENT_CAPA_MASK(0x8B02) | + IW_EVENT_CAPA_MASK(0x8B04)); + range->event_capa[1] = IW_EVENT_CAPA_K_1; + /* Disable interrupts and save flags. */ spin_lock_irqsave(&lp->spinlock, flags); @@ -2403,11 +2408,10 @@ static const struct iw_handler_def wavel .num_standard = sizeof(wavelan_handler)/sizeof(iw_handler), .num_private = sizeof(wavelan_private_handler)/sizeof(iw_handler), .num_private_args = sizeof(wavelan_private_args)/sizeof(struct iw_priv_args), - .standard = (iw_handler *) wavelan_handler, - .private = (iw_handler *) wavelan_private_handler, - .private_args = (struct iw_priv_args *) wavelan_private_args, - .spy_offset = ((void *) (&((net_local *) NULL)->spy_data) - - (void *) NULL), + .standard = wavelan_handler, + .private = wavelan_private_handler, + .private_args = wavelan_private_args, + .get_wireless_stats = wavelan_get_wireless_stats, }; /*------------------------------------------------------------------*/ @@ -4190,8 +4194,9 @@ static int __init wavelan_config(struct #endif /* SET_MAC_ADDRESS */ #ifdef WIRELESS_EXT /* if wireless extension exists in the kernel */ - dev->get_wireless_stats = wavelan_get_wireless_stats; - dev->wireless_handlers = (struct iw_handler_def *)&wavelan_handler_def; + dev->wireless_handlers = &wavelan_handler_def; + lp->wireless_data.spy_data = &lp->spy_data; + dev->wireless_data = &lp->wireless_data; #endif dev->mtu = WAVELAN_MTU; --- linux-2.6.9-rc1/drivers/net/wireless/wavelan_cs.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/net/wireless/wavelan_cs.c 2004-09-03 00:56:48.213831032 -0700 @@ -502,7 +502,7 @@ unsigned char WAVELAN_BEACON_ADDRESS[]= void wv_roam_init(struct net_device *dev) { - net_local *lp= (net_local *)dev->priv; + net_local *lp= netdev_priv(dev); /* Do not remove this unless you have a good reason */ printk(KERN_NOTICE "%s: Warning, you have enabled roaming on" @@ -532,7 +532,7 @@ void wv_roam_init(struct net_device *dev void wv_roam_cleanup(struct net_device *dev) { wavepoint_history *ptr,*old_ptr; - net_local *lp= (net_local *)dev->priv; + net_local *lp= netdev_priv(dev); printk(KERN_DEBUG "WaveLAN: Roaming Disabled on device %s\n",dev->name); @@ -762,7 +762,7 @@ static inline void wl_roam_gather(struct unsigned short nwid=ntohs(beacon->nwid); unsigned short sigqual=stats[2] & MMR_SGNL_QUAL; /* SNR of beacon */ wavepoint_history *wavepoint=NULL; /* WavePoint table entry */ - net_local *lp=(net_local *)dev->priv; /* Device info */ + net_local *lp = netdev_priv(dev); /* Device info */ #ifdef I_NEED_THIS_FEATURE /* Some people don't need this, some other may need it */ @@ -1006,7 +1006,7 @@ read_ringbuf(struct net_device * dev, static inline void wv_82593_reconfig(struct net_device * dev) { - net_local * lp = (net_local *)dev->priv; + net_local * lp = netdev_priv(dev); dev_link_t * link = lp->link; unsigned long flags; @@ -1135,7 +1135,7 @@ static void wv_mmc_show(struct net_device * dev) { ioaddr_t base = dev->base_addr; - net_local * lp = (net_local *)dev->priv; + net_local * lp = netdev_priv(dev); mmr_t m; /* Basic check */ @@ -1224,7 +1224,7 @@ wv_mmc_show(struct net_device * dev) static void wv_ru_show(struct net_device * dev) { - net_local *lp = (net_local *) dev->priv; + net_local *lp = netdev_priv(dev); printk(KERN_DEBUG "##### wavelan i82593 receiver status: #####\n"); printk(KERN_DEBUG "ru: rfp %d stop %d", lp->rfp, lp->stop); @@ -1258,9 +1258,7 @@ wv_dev_show(struct net_device * dev) static void wv_local_show(struct net_device * dev) { - net_local *lp; - - lp = (net_local *)dev->priv; + net_local *lp = netdev_priv(dev); printk(KERN_DEBUG "local:"); /* @@ -1418,7 +1416,7 @@ wavelan_get_stats(struct net_device * de printk(KERN_DEBUG "%s: <>wavelan_get_stats()\n", dev->name); #endif - return(&((net_local *) dev->priv)->stats); + return(&((net_local *)netdev_priv(dev))->stats); } /*------------------------------------------------------------------*/ @@ -1433,7 +1431,7 @@ wavelan_get_stats(struct net_device * de static void wavelan_set_multicast_list(struct net_device * dev) { - net_local * lp = (net_local *) dev->priv; + net_local * lp = netdev_priv(dev); #ifdef DEBUG_IOCTL_TRACE printk(KERN_DEBUG "%s: ->wavelan_set_multicast_list()\n", dev->name); @@ -1550,7 +1548,6 @@ wavelan_set_mac_address(struct net_devic /* * Frequency setting (for hardware able of it) * It's a bit complicated and you don't really want to look into it... - * (called in wavelan_ioctl) */ static inline int wv_set_frequency(u_long base, /* i/o port of the card */ @@ -1826,7 +1823,7 @@ static inline void wl_his_gather(struct net_device * dev, u_char * stats) /* Statistics to gather */ { - net_local * lp = (net_local *) dev->priv; + net_local * lp = netdev_priv(dev); u_char level = stats[0] & MMR_SIGNAL_LVL; int i; @@ -1840,28 +1837,15 @@ wl_his_gather(struct net_device * dev, } #endif /* HISTOGRAM */ -static inline int -wl_netdev_ethtool_ioctl(struct net_device *dev, void __user *useraddr) +static void wl_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - u32 ethcmd; - - if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; - - strncpy(info.driver, "wavelan_cs", sizeof(info.driver)-1); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - } - - return -EOPNOTSUPP; + strncpy(info->driver, "wavelan_cs", sizeof(info->driver)-1); } +static struct ethtool_ops ops = { + .get_drvinfo = wl_get_drvinfo +}; + /*------------------------------------------------------------------*/ /* * Wireless Handler : get protocol name @@ -1885,7 +1869,7 @@ static int wavelan_set_nwid(struct net_d char *extra) { ioaddr_t base = dev->base_addr; - net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + net_local *lp = netdev_priv(dev); psa_t psa; mm_t m; unsigned long flags; @@ -1943,7 +1927,7 @@ static int wavelan_get_nwid(struct net_d union iwreq_data *wrqu, char *extra) { - net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + net_local *lp = netdev_priv(dev); psa_t psa; unsigned long flags; int ret = 0; @@ -1975,7 +1959,7 @@ static int wavelan_set_freq(struct net_d char *extra) { ioaddr_t base = dev->base_addr; - net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + net_local *lp = netdev_priv(dev); unsigned long flags; int ret; @@ -2005,7 +1989,7 @@ static int wavelan_get_freq(struct net_d char *extra) { ioaddr_t base = dev->base_addr; - net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + net_local *lp = netdev_priv(dev); psa_t psa; unsigned long flags; int ret = 0; @@ -2051,7 +2035,7 @@ static int wavelan_set_sens(struct net_d char *extra) { ioaddr_t base = dev->base_addr; - net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + net_local *lp = netdev_priv(dev); psa_t psa; unsigned long flags; int ret = 0; @@ -2086,7 +2070,7 @@ static int wavelan_get_sens(struct net_d union iwreq_data *wrqu, char *extra) { - net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + net_local *lp = netdev_priv(dev); psa_t psa; unsigned long flags; int ret = 0; @@ -2117,7 +2101,7 @@ static int wavelan_set_encode(struct net char *extra) { ioaddr_t base = dev->base_addr; - net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + net_local *lp = netdev_priv(dev); unsigned long flags; psa_t psa; int ret = 0; @@ -2187,7 +2171,7 @@ static int wavelan_get_encode(struct net char *extra) { ioaddr_t base = dev->base_addr; - net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + net_local *lp = netdev_priv(dev); psa_t psa; unsigned long flags; int ret = 0; @@ -2234,7 +2218,7 @@ static int wavelan_set_essid(struct net_ union iwreq_data *wrqu, char *extra) { - net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + net_local *lp = netdev_priv(dev); unsigned long flags; int ret = 0; @@ -2282,7 +2266,7 @@ static int wavelan_get_essid(struct net_ union iwreq_data *wrqu, char *extra) { - net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + net_local *lp = netdev_priv(dev); /* Is the domain ID active ? */ wrqu->data.flags = lp->filter_domains; @@ -2347,7 +2331,7 @@ static int wavelan_set_mode(struct net_d union iwreq_data *wrqu, char *extra) { - net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + net_local *lp = netdev_priv(dev); unsigned long flags; int ret = 0; @@ -2406,7 +2390,7 @@ static int wavelan_get_range(struct net_ char *extra) { ioaddr_t base = dev->base_addr; - net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + net_local *lp = netdev_priv(dev); struct iw_range *range = (struct iw_range *) extra; unsigned long flags; int ret = 0; @@ -2438,6 +2422,12 @@ static int wavelan_get_range(struct net_ range->num_bitrates = 1; range->bitrate[0] = 2000000; /* 2 Mb/s */ + /* Event capability (kernel + driver) */ + range->event_capa[0] = (IW_EVENT_CAPA_MASK(0x8B02) | + IW_EVENT_CAPA_MASK(0x8B04) | + IW_EVENT_CAPA_MASK(0x8B06)); + range->event_capa[1] = IW_EVENT_CAPA_K_1; + /* Disable interrupts and save flags. */ spin_lock_irqsave(&lp->spinlock, flags); @@ -2476,7 +2466,7 @@ static int wavelan_set_qthr(struct net_d char *extra) { ioaddr_t base = dev->base_addr; - net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + net_local *lp = netdev_priv(dev); psa_t psa; unsigned long flags; @@ -2507,7 +2497,7 @@ static int wavelan_get_qthr(struct net_d union iwreq_data *wrqu, char *extra) { - net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + net_local *lp = netdev_priv(dev); psa_t psa; unsigned long flags; @@ -2535,7 +2525,7 @@ static int wavelan_set_roam(struct net_d union iwreq_data *wrqu, char *extra) { - net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + net_local *lp = netdev_priv(dev); unsigned long flags; /* Disable interrupts and save flags. */ @@ -2580,7 +2570,7 @@ static int wavelan_set_histo(struct net_ union iwreq_data *wrqu, char *extra) { - net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + net_local *lp = netdev_priv(dev); /* Check the number of intervals. */ if (wrqu->data.length > 16) { @@ -2623,7 +2613,7 @@ static int wavelan_get_histo(struct net_ union iwreq_data *wrqu, char *extra) { - net_local *lp = (net_local *) dev->priv; /* lp is not unused */ + net_local *lp = netdev_priv(dev); /* Set the number of intervals. */ wrqu->data.length = lp->his_number; @@ -2737,50 +2727,14 @@ static const struct iw_handler_def wavel .num_standard = sizeof(wavelan_handler)/sizeof(iw_handler), .num_private = sizeof(wavelan_private_handler)/sizeof(iw_handler), .num_private_args = sizeof(wavelan_private_args)/sizeof(struct iw_priv_args), - .standard = (iw_handler *) wavelan_handler, - .private = (iw_handler *) wavelan_private_handler, - .private_args = (struct iw_priv_args *) wavelan_private_args, - .spy_offset = ((void *) (&((net_local *) NULL)->spy_data) - - (void *) NULL), + .standard = wavelan_handler, + .private = wavelan_private_handler, + .private_args = wavelan_private_args, + .get_wireless_stats = wavelan_get_wireless_stats, }; /*------------------------------------------------------------------*/ /* - * Perform ioctl : config & info stuff - * This is here that are treated the wireless extensions (iwconfig) - */ -static int -wavelan_ioctl(struct net_device * dev, /* Device on wich the ioctl apply */ - struct ifreq * rq, /* Data passed */ - int cmd) /* Ioctl number */ -{ - int ret = 0; - -#ifdef DEBUG_IOCTL_TRACE - printk(KERN_DEBUG "%s: ->wavelan_ioctl(cmd=0x%X)\n", dev->name, cmd); -#endif - - /* Look what is the request */ - switch(cmd) - { - case SIOCETHTOOL: - ret = wl_netdev_ethtool_ioctl(dev, rq->ifr_data); - break; - - /* ------------------- OTHER IOCTL ------------------- */ - - default: - ret = -EOPNOTSUPP; - } - -#ifdef DEBUG_IOCTL_TRACE - printk(KERN_DEBUG "%s: <-wavelan_ioctl()\n", dev->name); -#endif - return ret; -} - -/*------------------------------------------------------------------*/ -/* * Get wireless statistics * Called by /proc/net/wireless... */ @@ -2788,7 +2742,7 @@ static iw_stats * wavelan_get_wireless_stats(struct net_device * dev) { ioaddr_t base = dev->base_addr; - net_local * lp = (net_local *) dev->priv; + net_local * lp = netdev_priv(dev); mmr_t m; iw_stats * wstats; unsigned long flags; @@ -2913,7 +2867,7 @@ wv_packet_read(struct net_device * dev, int fd_p, int sksize) { - net_local * lp = (net_local *) dev->priv; + net_local * lp = netdev_priv(dev); struct sk_buff * skb; #ifdef DEBUG_RX_TRACE @@ -3015,7 +2969,7 @@ static inline void wv_packet_rcv(struct net_device * dev) { ioaddr_t base = dev->base_addr; - net_local * lp = (net_local *) dev->priv; + net_local * lp = netdev_priv(dev); int newrfp; int rp; int len; @@ -3150,7 +3104,7 @@ wv_packet_write(struct net_device * dev, void * buf, short length) { - net_local * lp = (net_local *) dev->priv; + net_local * lp = netdev_priv(dev); ioaddr_t base = dev->base_addr; unsigned long flags; int clen = length; @@ -3211,7 +3165,7 @@ static int wavelan_packet_xmit(struct sk_buff * skb, struct net_device * dev) { - net_local * lp = (net_local *)dev->priv; + net_local * lp = netdev_priv(dev); unsigned long flags; #ifdef DEBUG_TX_TRACE @@ -3470,7 +3424,7 @@ static int wv_ru_stop(struct net_device * dev) { ioaddr_t base = dev->base_addr; - net_local * lp = (net_local *) dev->priv; + net_local * lp = netdev_priv(dev); unsigned long flags; int status; int spin; @@ -3533,7 +3487,7 @@ static int wv_ru_start(struct net_device * dev) { ioaddr_t base = dev->base_addr; - net_local * lp = (net_local *) dev->priv; + net_local * lp = netdev_priv(dev); unsigned long flags; #ifdef DEBUG_CONFIG_TRACE @@ -3621,7 +3575,7 @@ static int wv_82593_config(struct net_device * dev) { ioaddr_t base = dev->base_addr; - net_local * lp = (net_local *) dev->priv; + net_local * lp = netdev_priv(dev); struct i82593_conf_block cfblk; int ret = TRUE; @@ -3796,7 +3750,7 @@ wv_pcmcia_reset(struct net_device * dev) { int i; conf_reg_t reg = { 0, CS_READ, CISREG_COR, 0 }; - dev_link_t * link = ((net_local *) dev->priv)->link; + dev_link_t * link = ((net_local *)netdev_priv(dev))->link; #ifdef DEBUG_CONFIG_TRACE printk(KERN_DEBUG "%s: ->wv_pcmcia_reset()\n", dev->name); @@ -3856,7 +3810,7 @@ wv_pcmcia_reset(struct net_device * dev) static int wv_hw_config(struct net_device * dev) { - net_local * lp = (net_local *) dev->priv; + net_local * lp = netdev_priv(dev); ioaddr_t base = dev->base_addr; unsigned long flags; int ret = FALSE; @@ -3963,7 +3917,7 @@ wv_hw_config(struct net_device * dev) static inline void wv_hw_reset(struct net_device * dev) { - net_local * lp = (net_local *) dev->priv; + net_local * lp = netdev_priv(dev); #ifdef DEBUG_CONFIG_TRACE printk(KERN_DEBUG "%s: ->wv_hw_reset()\n", dev->name); @@ -4112,6 +4066,7 @@ wv_pcmcia_config(dev_link_t * link) (u_int) dev->mem_start, dev->irq, (u_int) dev->base_addr); #endif + SET_NETDEV_DEV(dev, pcmcia_lookup_device(handle)); i = register_netdev(dev); if(i != 0) { @@ -4131,8 +4086,8 @@ wv_pcmcia_config(dev_link_t * link) return FALSE; } - strcpy(((net_local *) dev->priv)->node.dev_name, dev->name); - link->dev = &((net_local *) dev->priv)->node; + strcpy(((net_local *) netdev_priv(dev))->node.dev_name, dev->name); + link->dev = &((net_local *) netdev_priv(dev))->node; #ifdef DEBUG_CONFIG_TRACE printk(KERN_DEBUG "<-wv_pcmcia_config()\n"); @@ -4203,7 +4158,7 @@ wavelan_interrupt(int irq, printk(KERN_DEBUG "%s: ->wavelan_interrupt()\n", dev->name); #endif - lp = (net_local *) dev->priv; + lp = netdev_priv(dev); base = dev->base_addr; #ifdef DEBUG_INTERRUPT_INFO @@ -4453,7 +4408,7 @@ wavelan_interrupt(int irq, static void wavelan_watchdog(struct net_device * dev) { - net_local * lp = (net_local *) dev->priv; + net_local * lp = netdev_priv(dev); ioaddr_t base = dev->base_addr; unsigned long flags; int aborted = FALSE; @@ -4528,8 +4483,8 @@ wavelan_watchdog(struct net_device * dev static int wavelan_open(struct net_device * dev) { - dev_link_t * link = ((net_local *) dev->priv)->link; - net_local * lp = (net_local *)dev->priv; + net_local * lp = netdev_priv(dev); + dev_link_t * link = lp->link; ioaddr_t base = dev->base_addr; #ifdef DEBUG_CALLBACK_TRACE @@ -4583,7 +4538,7 @@ wavelan_open(struct net_device * dev) static int wavelan_close(struct net_device * dev) { - dev_link_t * link = ((net_local *) dev->priv)->link; + dev_link_t * link = ((net_local *)netdev_priv(dev))->link; ioaddr_t base = dev->base_addr; #ifdef DEBUG_CALLBACK_TRACE @@ -4686,7 +4641,7 @@ wavelan_attach(void) } link->priv = link->irq.Instance = dev; - lp = dev->priv; + lp = netdev_priv(dev); /* Init specific data */ lp->configured = 0; @@ -4718,11 +4673,12 @@ wavelan_attach(void) /* Set the watchdog timer */ dev->tx_timeout = &wavelan_watchdog; dev->watchdog_timeo = WATCHDOG_JIFFIES; + SET_ETHTOOL_OPS(dev, &ops); #ifdef WIRELESS_EXT /* If wireless extension exist in the kernel */ - dev->wireless_handlers = (struct iw_handler_def *)&wavelan_handler_def; - dev->do_ioctl = wavelan_ioctl; /* old wireless extensions */ - dev->get_wireless_stats = wavelan_get_wireless_stats; + dev->wireless_handlers = &wavelan_handler_def; + lp->wireless_data.spy_data = &lp->spy_data; + dev->wireless_data = &lp->wireless_data; #endif /* Other specific data */ @@ -4820,8 +4776,8 @@ wavelan_detach(dev_link_t * link) if (link->dev) unregister_netdev(dev); link->dev = NULL; - ((net_local *) dev->priv)->link = NULL; - ((net_local *) dev->priv)->dev = NULL; + ((net_local *)netdev_priv(dev))->link = NULL; + ((net_local *)netdev_priv(dev))->dev = NULL; free_netdev(dev); } kfree(link); --- linux-2.6.9-rc1/drivers/net/wireless/wavelan_cs.p.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/net/wireless/wavelan_cs.p.h 2004-09-03 00:56:32.617202080 -0700 @@ -629,6 +629,7 @@ struct net_local iw_stats wstats; /* Wireless specific stats */ struct iw_spy_data spy_data; + struct iw_public_data wireless_data; #endif #ifdef HISTOGRAM @@ -725,6 +726,8 @@ static inline void /* ------------------- IOCTL, STATS & RECONFIG ------------------- */ static en_stats * wavelan_get_stats(struct net_device *); /* Give stats /proc/net/dev */ +static iw_stats * + wavelan_get_wireless_stats(struct net_device *); /* ----------------------- PACKET RECEPTION ----------------------- */ static inline int wv_start_of_frame(struct net_device *, /* Seek beggining of current frame */ --- linux-2.6.9-rc1/drivers/net/wireless/wavelan.p.h 2004-08-24 00:03:19.000000000 -0700 +++ 25/drivers/net/wireless/wavelan.p.h 2004-09-03 00:56:32.618201928 -0700 @@ -510,6 +510,7 @@ struct net_local iw_stats wstats; /* Wireless-specific statistics */ struct iw_spy_data spy_data; + struct iw_public_data wireless_data; #endif #ifdef HISTOGRAM @@ -614,6 +615,8 @@ static inline void /* ------------------- IOCTL, STATS & RECONFIG ------------------- */ static en_stats * wavelan_get_stats(struct net_device *); /* Give stats /proc/net/dev */ +static iw_stats * + wavelan_get_wireless_stats(struct net_device *); static void wavelan_set_multicast_list(struct net_device *); /* ----------------------- PACKET RECEPTION ----------------------- */ --- linux-2.6.9-rc1/drivers/net/wireless/wl3501_cs.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/drivers/net/wireless/wl3501_cs.c 2004-09-03 00:56:48.215830728 -0700 @@ -1487,55 +1487,14 @@ struct iw_statistics *wl3501_get_wireles return wstats; } -static inline int wl3501_ethtool_ioctl(struct net_device *dev, void __user *uaddr) +static void wl3501_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - u32 ethcmd; - int rc = -EFAULT; - - if (copy_from_user(ðcmd, uaddr, sizeof(ethcmd))) - goto out; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = { .cmd = ETHTOOL_GDRVINFO, }; - - strlcpy(info.driver, wl3501_dev_info, sizeof(info.driver)); - rc = copy_to_user(uaddr, &info, sizeof(info)) ? -EFAULT : 1; - } - default: - rc = -EOPNOTSUPP; - break; - } -out: - return rc; + strlcpy(info->driver, wl3501_dev_info, sizeof(info->driver)); } -/** - * wl3501_ioctl - Perform IOCTL call functions - * @dev - network device - * @ifreq - request - * @cmd - command - * - * Perform IOCTL call functions here. Some are privileged operations and the - * effective uid is checked in those cases. - * - * This part is optional. Needed only if you want to run wlu (unix version). - * - * CAUTION: To prevent interrupted by wl3501_interrupt() and timer-based - * wl3501_hard_start_xmit() from other interrupts, this should be run - * single-threaded. - */ -static int wl3501_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - int rc = -ENODEV; - - if (netif_device_present(dev)) { - rc = -EOPNOTSUPP; - if (cmd == SIOCETHTOOL) - rc = wl3501_ethtool_ioctl(dev, rq->ifr_data); - } - return rc; -} +static struct ethtool_ops ops = { + .get_drvinfo = wl3501_get_drvinfo +}; /** * wl3501_detach - deletes a driver "instance" @@ -2047,8 +2006,8 @@ static dev_link_t *wl3501_attach(void) dev->watchdog_timeo = 5 * HZ; dev->get_stats = wl3501_get_stats; dev->get_wireless_stats = wl3501_get_wireless_stats; - dev->do_ioctl = wl3501_ioctl; dev->wireless_handlers = (struct iw_handler_def *)&wl3501_handler_def; + SET_ETHTOOL_OPS(dev, &ops); netif_stop_queue(dev); link->priv = link->irq.Instance = dev; @@ -2146,6 +2105,7 @@ static void wl3501_config(dev_link_t *li dev->irq = link->irq.AssignedIRQ; dev->base_addr = link->io.BasePort1; + SET_NETDEV_DEV(dev, pcmcia_lookup_device(handle)); if (register_netdev(dev)) { printk(KERN_NOTICE "wl3501_cs: register_netdev() failed\n"); goto failed; --- linux-2.6.9-rc1/drivers/net/yellowfin.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/drivers/net/yellowfin.c 2004-09-03 00:56:32.622201320 -0700 @@ -406,6 +406,7 @@ static void yellowfin_error(struct net_d static int yellowfin_close(struct net_device *dev); static struct net_device_stats *yellowfin_get_stats(struct net_device *dev); static void set_rx_mode(struct net_device *dev); +static struct ethtool_ops ethtool_ops; static int __devinit yellowfin_init_one(struct pci_dev *pdev, @@ -440,7 +441,7 @@ static int __devinit yellowfin_init_one( SET_MODULE_OWNER(dev); SET_NETDEV_DEV(dev, &pdev->dev); - np = dev->priv; + np = netdev_priv(dev); if (pci_request_regions(pdev, DRV_NAME)) goto err_out_free_netdev; @@ -521,6 +522,7 @@ static int __devinit yellowfin_init_one( dev->get_stats = &yellowfin_get_stats; dev->set_multicast_list = &set_rx_mode; dev->do_ioctl = &netdev_ioctl; + SET_ETHTOOL_OPS(dev, ðtool_ops); dev->tx_timeout = yellowfin_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; @@ -619,7 +621,7 @@ static void mdio_write(long ioaddr, int static int yellowfin_open(struct net_device *dev) { - struct yellowfin_private *yp = dev->priv; + struct yellowfin_private *yp = netdev_priv(dev); long ioaddr = dev->base_addr; int i; @@ -701,7 +703,7 @@ static int yellowfin_open(struct net_dev static void yellowfin_timer(unsigned long data) { struct net_device *dev = (struct net_device *)data; - struct yellowfin_private *yp = dev->priv; + struct yellowfin_private *yp = netdev_priv(dev); long ioaddr = dev->base_addr; int next_tick = 60*HZ; @@ -735,7 +737,7 @@ static void yellowfin_timer(unsigned lon static void yellowfin_tx_timeout(struct net_device *dev) { - struct yellowfin_private *yp = dev->priv; + struct yellowfin_private *yp = netdev_priv(dev); long ioaddr = dev->base_addr; printk(KERN_WARNING "%s: Yellowfin transmit timed out at %d/%d Tx " @@ -772,7 +774,7 @@ static void yellowfin_tx_timeout(struct /* Initialize the Rx and Tx rings, along with various 'dev' bits. */ static void yellowfin_init_ring(struct net_device *dev) { - struct yellowfin_private *yp = dev->priv; + struct yellowfin_private *yp = netdev_priv(dev); int i; yp->tx_full = 0; @@ -855,7 +857,7 @@ static void yellowfin_init_ring(struct n static int yellowfin_start_xmit(struct sk_buff *skb, struct net_device *dev) { - struct yellowfin_private *yp = dev->priv; + struct yellowfin_private *yp = netdev_priv(dev); unsigned entry; int len = skb->len; @@ -953,7 +955,7 @@ static irqreturn_t yellowfin_interrupt(i #endif ioaddr = dev->base_addr; - yp = dev->priv; + yp = netdev_priv(dev); spin_lock (&yp->lock); @@ -1095,7 +1097,7 @@ static irqreturn_t yellowfin_interrupt(i for clarity and better register allocation. */ static int yellowfin_rx(struct net_device *dev) { - struct yellowfin_private *yp = dev->priv; + struct yellowfin_private *yp = netdev_priv(dev); int entry = yp->cur_rx % RX_RING_SIZE; int boguscnt = yp->dirty_rx + RX_RING_SIZE - yp->cur_rx; @@ -1239,7 +1241,7 @@ static int yellowfin_rx(struct net_devic static void yellowfin_error(struct net_device *dev, int intr_status) { - struct yellowfin_private *yp = dev->priv; + struct yellowfin_private *yp = netdev_priv(dev); printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n", dev->name, intr_status); @@ -1253,7 +1255,7 @@ static void yellowfin_error(struct net_d static int yellowfin_close(struct net_device *dev) { long ioaddr = dev->base_addr; - struct yellowfin_private *yp = dev->priv; + struct yellowfin_private *yp = netdev_priv(dev); int i; netif_stop_queue (dev); @@ -1340,7 +1342,7 @@ static int yellowfin_close(struct net_de static struct net_device_stats *yellowfin_get_stats(struct net_device *dev) { - struct yellowfin_private *yp = dev->priv; + struct yellowfin_private *yp = netdev_priv(dev); return &yp->stats; } @@ -1348,7 +1350,7 @@ static struct net_device_stats *yellowfi static void set_rx_mode(struct net_device *dev) { - struct yellowfin_private *yp = dev->priv; + struct yellowfin_private *yp = netdev_priv(dev); long ioaddr = dev->base_addr; u16 cfg_value = inw(ioaddr + Cnfg); @@ -1394,39 +1396,25 @@ static void set_rx_mode(struct net_devic outw(cfg_value | 0x1000, ioaddr + Cnfg); } -static int netdev_ethtool_ioctl(struct net_device *dev, void __user *useraddr) +static void yellowfin_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - struct yellowfin_private *np = dev->priv; - u32 ethcmd; - - if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; - strcpy(info.driver, DRV_NAME); - strcpy(info.version, DRV_VERSION); - strcpy(info.bus_info, pci_name(np->pci_dev)); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - - } - - return -EOPNOTSUPP; + struct yellowfin_private *np = netdev_priv(dev); + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->bus_info, pci_name(np->pci_dev)); } +static struct ethtool_ops ethtool_ops = { + .get_drvinfo = yellowfin_get_drvinfo +}; + static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { - struct yellowfin_private *np = dev->priv; + struct yellowfin_private *np = netdev_priv(dev); long ioaddr = dev->base_addr; struct mii_ioctl_data *data = if_mii(rq); switch(cmd) { - case SIOCETHTOOL: - return netdev_ethtool_ioctl(dev, rq->ifr_data); case SIOCGMIIPHY: /* Get address of MII PHY in use. */ data->phy_id = np->phys[0] & 0x1f; /* Fall Through */ @@ -1466,7 +1454,7 @@ static void __devexit yellowfin_remove_o if (!dev) BUG(); - np = dev->priv; + np = netdev_priv(dev); pci_free_consistent(pdev, STATUS_TOTAL_SIZE, np->tx_status, np->tx_status_dma); --- linux-2.6.9-rc1/drivers/oprofile/buffer_sync.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/drivers/oprofile/buffer_sync.c 2004-09-02 20:51:52.000000000 -0700 @@ -32,61 +32,65 @@ #include "cpu_buffer.h" #include "buffer_sync.h" -#define DEFAULT_EXPIRE (HZ / 4) - -static void wq_sync_buffers(void *); -static DECLARE_WORK(sync_wq, wq_sync_buffers, NULL); - -static struct timer_list sync_timer; -static void timer_ping(unsigned long data); -static void sync_cpu_buffers(void); +static LIST_HEAD(dying_tasks); +static LIST_HEAD(dead_tasks); +cpumask_t marked_cpus = CPU_MASK_NONE; +static spinlock_t task_mortuary = SPIN_LOCK_UNLOCKED; +void process_task_mortuary(void); - -/* We must make sure to process every entry in the CPU buffers - * before a task got the PF_EXITING flag, otherwise we will hold - * references to a possibly freed task_struct. We are safe with - * samples past the PF_EXITING point in do_exit(), because we - * explicitly check for that in cpu_buffer.c + +/* Take ownership of the task struct and place it on the + * list for processing. Only after two full buffer syncs + * does the task eventually get freed, because by then + * we are sure we will not reference it again. */ -static int exit_task_notify(struct notifier_block * self, unsigned long val, void * data) +static int task_free_notify(struct notifier_block * self, unsigned long val, void * data) { - sync_cpu_buffers(); - return 0; + struct task_struct * task = (struct task_struct *)data; + spin_lock(&task_mortuary); + list_add(&task->tasks, &dying_tasks); + spin_unlock(&task_mortuary); + return NOTIFY_OK; } - -/* There are two cases of tasks modifying task->mm->mmap list we - * must concern ourselves with. First, when a task is about to - * exit (exit_mmap()), we should process the buffer to deal with - * any samples in the CPU buffer, before we lose the ->mmap information - * we need. It is vital to get this case correct, otherwise we can - * end up trying to access a freed task_struct. + + +/* The task is on its way out. A sync of the buffer means we can catch + * any remaining samples for this task. */ -static int mm_notify(struct notifier_block * self, unsigned long val, void * data) +static int task_exit_notify(struct notifier_block * self, unsigned long val, void * data) { - sync_cpu_buffers(); - return 0; + /* To avoid latency problems, we only process the current CPU, + * hoping that most samples for the task are on this CPU + */ + sync_buffer(smp_processor_id()); + return 0; } -/* Second, a task may unmap (part of) an executable mmap, - * so we want to process samples before that happens too. This is merely - * a QOI issue not a correctness one. +/* The task is about to try a do_munmap(). We peek at what it's going to + * do, and if it's an executable region, process the samples first, so + * we don't lose any. This does not have to be exact, it's a QoI issue + * only. */ static int munmap_notify(struct notifier_block * self, unsigned long val, void * data) { - /* Note that we cannot sync the buffers directly, because we might end up - * taking the the mmap_sem that we hold now inside of event_buffer_read() - * on a page fault, whilst holding buffer_sem - deadlock. - * - * This would mean a threaded reader of the event buffer, but we should - * prevent it anyway. - * - * Delaying the work in a context that doesn't hold the mmap_sem means - * that we won't lose samples from other mappings that current() may - * have. Note that either way, we lose any pending samples for what is - * being unmapped. - */ - schedule_work(&sync_wq); + unsigned long addr = (unsigned long)data; + struct mm_struct * mm = current->mm; + struct vm_area_struct * mpnt; + + down_read(&mm->mmap_sem); + + mpnt = find_vma(mm, addr); + if (mpnt && mpnt->vm_file && (mpnt->vm_flags & VM_EXEC)) { + up_read(&mm->mmap_sem); + /* To avoid latency problems, we only process the current CPU, + * hoping that most samples for the task are on this CPU + */ + sync_buffer(smp_processor_id()); + return 0; + } + + up_read(&mm->mmap_sem); return 0; } @@ -100,7 +104,7 @@ static int module_load_notify(struct not if (val != MODULE_STATE_COMING) return 0; - sync_cpu_buffers(); + /* FIXME: should we process all CPU buffers ? */ down(&buffer_sem); add_event_entry(ESCAPE_CODE); add_event_entry(MODULE_LOADED_CODE); @@ -110,16 +114,16 @@ static int module_load_notify(struct not } -static struct notifier_block exit_task_nb = { - .notifier_call = exit_task_notify, +static struct notifier_block task_free_nb = { + .notifier_call = task_free_notify, }; -static struct notifier_block exec_unmap_nb = { - .notifier_call = munmap_notify, +static struct notifier_block task_exit_nb = { + .notifier_call = task_exit_notify, }; -static struct notifier_block exit_mmap_nb = { - .notifier_call = mm_notify, +static struct notifier_block munmap_nb = { + .notifier_call = munmap_notify, }; static struct notifier_block module_load_nb = { @@ -127,11 +131,12 @@ static struct notifier_block module_load }; -static void end_sync_timer(void) +static void end_sync(void) { - del_timer_sync(&sync_timer); - /* timer might have queued work, make sure it's completed. */ - flush_scheduled_work(); + end_cpu_timers(); + /* make sure we don't leak task structs */ + process_task_mortuary(); + process_task_mortuary(); } @@ -139,18 +144,15 @@ int sync_start(void) { int err; - init_timer(&sync_timer); - sync_timer.function = timer_ping; - sync_timer.expires = jiffies + DEFAULT_EXPIRE; - add_timer(&sync_timer); + start_cpu_timers(); - err = profile_event_register(EXIT_TASK, &exit_task_nb); + err = task_handoff_register(&task_free_nb); if (err) goto out1; - err = profile_event_register(EXIT_MMAP, &exit_mmap_nb); + err = profile_event_register(PROFILE_TASK_EXIT, &task_exit_nb); if (err) goto out2; - err = profile_event_register(EXEC_UNMAP, &exec_unmap_nb); + err = profile_event_register(PROFILE_MUNMAP, &munmap_nb); if (err) goto out3; err = register_module_notifier(&module_load_nb); @@ -160,13 +162,13 @@ int sync_start(void) out: return err; out4: - profile_event_unregister(EXEC_UNMAP, &exec_unmap_nb); + profile_event_unregister(PROFILE_MUNMAP, &munmap_nb); out3: - profile_event_unregister(EXIT_MMAP, &exit_mmap_nb); + profile_event_unregister(PROFILE_TASK_EXIT, &task_exit_nb); out2: - profile_event_unregister(EXIT_TASK, &exit_task_nb); + task_handoff_unregister(&task_free_nb); out1: - end_sync_timer(); + end_sync(); goto out; } @@ -174,10 +176,10 @@ out1: void sync_stop(void) { unregister_module_notifier(&module_load_nb); - profile_event_unregister(EXIT_TASK, &exit_task_nb); - profile_event_unregister(EXIT_MMAP, &exit_mmap_nb); - profile_event_unregister(EXEC_UNMAP, &exec_unmap_nb); - end_sync_timer(); + profile_event_unregister(PROFILE_MUNMAP, &munmap_nb); + profile_event_unregister(PROFILE_TASK_EXIT, &task_exit_nb); + task_handoff_unregister(&task_free_nb); + end_sync(); } @@ -417,24 +419,80 @@ static void increment_tail(struct oprofi } +/* Move tasks along towards death. Any tasks on dead_tasks + * will definitely have no remaining references in any + * CPU buffers at this point, because we use two lists, + * and to have reached the list, it must have gone through + * one full sync already. + */ +void process_task_mortuary(void) +{ + struct list_head * pos; + struct list_head * pos2; + struct task_struct * task; + + spin_lock(&task_mortuary); + + list_for_each_safe(pos, pos2, &dead_tasks) { + task = list_entry(pos, struct task_struct, tasks); + list_del(&task->tasks); + free_task(task); + } + + list_for_each_safe(pos, pos2, &dying_tasks) { + task = list_entry(pos, struct task_struct, tasks); + list_del(&task->tasks); + list_add_tail(&task->tasks, &dead_tasks); + } + + spin_unlock(&task_mortuary); +} + + +static void mark_done(int cpu) +{ + int i; + + cpu_set(cpu, marked_cpus); + + for_each_online_cpu(i) { + if (!cpu_isset(i, marked_cpus)) + return; + } + + /* All CPUs have been processed at least once, + * we can process the mortuary once + */ + process_task_mortuary(); + + cpus_clear(marked_cpus); +} + + /* Sync one of the CPU's buffers into the global event buffer. * Here we need to go through each batch of samples punctuated * by context switch notes, taking the task's mmap_sem and doing * lookup in task->mm->mmap to convert EIP into dcookie/offset * value. */ -static void sync_buffer(struct oprofile_cpu_buffer * cpu_buf) +void sync_buffer(int cpu) { + struct oprofile_cpu_buffer * cpu_buf = &cpu_buffer[cpu]; struct mm_struct *mm = NULL; struct task_struct * new; unsigned long cookie = 0; int in_kernel = 1; unsigned int i; + unsigned long available; + + down(&buffer_sem); + add_cpu_switch(cpu); + /* Remember, only we can modify tail_pos */ - unsigned long const available = get_slots(cpu_buf); - + available = get_slots(cpu_buf); + for (i=0; i < available; ++i) { struct op_sample * s = &cpu_buf->buffer[cpu_buf->tail_pos]; @@ -462,50 +520,8 @@ static void sync_buffer(struct oprofile_ increment_tail(cpu_buf); } release_mm(mm); -} - - -/* Process each CPU's local buffer into the global - * event buffer. - */ -static void sync_cpu_buffers(void) -{ - int i; - down(&buffer_sem); - - for (i = 0; i < NR_CPUS; ++i) { - struct oprofile_cpu_buffer * cpu_buf; - - if (!cpu_possible(i)) - continue; - - cpu_buf = &cpu_buffer[i]; - - add_cpu_switch(i); - sync_buffer(cpu_buf); - } + mark_done(cpu); up(&buffer_sem); - - mod_timer(&sync_timer, jiffies + DEFAULT_EXPIRE); -} - - -static void wq_sync_buffers(void * data) -{ - sync_cpu_buffers(); -} - - -/* It is possible that we could have no munmap() or - * other events for a period of time. This will lead - * the CPU buffers to overflow and lose samples and - * context switches. We try to reduce the problem - * by timing out when nothing happens for a while. - */ -static void timer_ping(unsigned long data) -{ - schedule_work(&sync_wq); - /* timer is re-added by the scheduled task */ } --- linux-2.6.9-rc1/drivers/oprofile/buffer_sync.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/oprofile/buffer_sync.h 2004-09-02 20:51:52.000000000 -0700 @@ -16,4 +16,7 @@ int sync_start(void); /* remove the hooks */ void sync_stop(void); +/* sync the given CPU's buffer */ +void sync_buffer(int cpu); + #endif /* OPROFILE_BUFFER_SYNC_H */ --- linux-2.6.9-rc1/drivers/oprofile/cpu_buffer.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/drivers/oprofile/cpu_buffer.c 2004-09-02 20:51:52.000000000 -0700 @@ -9,7 +9,7 @@ * Each CPU has a local buffer that stores PC value/event * pairs. We also log context switches when we notice them. * Eventually each CPU's buffer is processed into the global - * event buffer by sync_cpu_buffers(). + * event buffer by sync_buffer(). * * We use a local buffer for two reasons: an NMI or similar * interrupt cannot synchronise, and high sampling rates @@ -22,21 +22,24 @@ #include #include "cpu_buffer.h" +#include "buffer_sync.h" #include "oprof.h" struct oprofile_cpu_buffer cpu_buffer[NR_CPUS] __cacheline_aligned; +static void wq_sync_buffer(void *); +static void timer_ping(unsigned long data); +#define DEFAULT_TIMER_EXPIRE (HZ / 2) +int timers_enabled; + static void __free_cpu_buffers(int num) { int i; - for (i=0; i < num; ++i) { - struct oprofile_cpu_buffer * b = &cpu_buffer[i]; - - if (!cpu_possible(i)) + for (i = 0; i < NR_CPUS; ++i) { + if (!cpu_online(i)) continue; - - vfree(b->buffer); + vfree(cpu_buffer[i].buffer); } } @@ -47,12 +50,12 @@ int alloc_cpu_buffers(void) unsigned long buffer_size = fs_cpu_buffer_size; - for (i=0; i < NR_CPUS; ++i) { + for (i = 0; i < NR_CPUS; ++i) { struct oprofile_cpu_buffer * b = &cpu_buffer[i]; - if (!cpu_possible(i)) + if (!cpu_online(i)) continue; - + b->buffer = vmalloc(sizeof(struct op_sample) * buffer_size); if (!b->buffer) goto fail; @@ -64,9 +67,15 @@ int alloc_cpu_buffers(void) b->head_pos = 0; b->sample_received = 0; b->sample_lost_overflow = 0; - b->sample_lost_task_exit = 0; + b->cpu = i; + init_timer(&b->timer); + b->timer.function = timer_ping; + b->timer.data = i; + b->timer.expires = jiffies + DEFAULT_TIMER_EXPIRE; + INIT_WORK(&b->work, wq_sync_buffer, b); } return 0; + fail: __free_cpu_buffers(i); return -ENOMEM; @@ -79,6 +88,42 @@ void free_cpu_buffers(void) } +void start_cpu_timers(void) +{ + int i; + + timers_enabled = 1; + + for (i = 0; i < NR_CPUS; ++i) { + struct oprofile_cpu_buffer * b = &cpu_buffer[i]; + + if (!cpu_online(i)) + continue; + + add_timer_on(&b->timer, i); + } +} + + +void end_cpu_timers(void) +{ + int i; + + timers_enabled = 0; + + for (i = 0; i < NR_CPUS; ++i) { + struct oprofile_cpu_buffer * b = &cpu_buffer[i]; + + if (!cpu_online(i)) + continue; + + del_timer_sync(&b->timer); + } + + flush_scheduled_work(); +} + + /* compute number of available slots in cpu_buffer queue */ static unsigned long nr_available_slots(struct oprofile_cpu_buffer const * b) { @@ -145,21 +190,9 @@ void oprofile_add_sample(unsigned long e /* notice a task switch */ if (cpu_buf->last_task != task) { cpu_buf->last_task = task; - if (!(task->flags & PF_EXITING)) { - cpu_buf->buffer[cpu_buf->head_pos].eip = ~0UL; - cpu_buf->buffer[cpu_buf->head_pos].event = (unsigned long)task; - increment_head(cpu_buf); - } - } - - /* If the task is exiting it's not safe to take a sample - * as the task_struct is about to be freed. We can't just - * notify at release_task() time because of CLONE_DETACHED - * tasks that release_task() themselves. - */ - if (task->flags & PF_EXITING) { - cpu_buf->sample_lost_task_exit++; - return; + cpu_buf->buffer[cpu_buf->head_pos].eip = ~0UL; + cpu_buf->buffer[cpu_buf->head_pos].event = (unsigned long)task; + increment_head(cpu_buf); } cpu_buf->buffer[cpu_buf->head_pos].eip = eip; @@ -178,3 +211,36 @@ void cpu_buffer_reset(struct oprofile_cp cpu_buf->last_is_kernel = -1; cpu_buf->last_task = NULL; } + + +/* FIXME: not guaranteed to be on our CPU */ +static void wq_sync_buffer(void * data) +{ + struct oprofile_cpu_buffer * b = (struct oprofile_cpu_buffer *)data; + if (b->cpu != smp_processor_id()) { + printk("WQ on CPU%d, prefer CPU%d\n", + smp_processor_id(), b->cpu); + } + sync_buffer(b->cpu); + + /* don't re-add the timer if we're shutting down */ + if (timers_enabled) { + del_timer_sync(&b->timer); + add_timer_on(&b->timer, b->cpu); + } +} + + +/* This serves to avoid cpu buffer overflow, and makes sure + * the task mortuary progresses + */ +static void timer_ping(unsigned long data) +{ + struct oprofile_cpu_buffer * b = &cpu_buffer[data]; + if (b->cpu != smp_processor_id()) { + printk("Timer on CPU%d, prefer CPU%d\n", + smp_processor_id(), b->cpu); + } + schedule_work(&b->work); + /* work will re-enable our timer */ +} --- linux-2.6.9-rc1/drivers/oprofile/cpu_buffer.h 2004-08-24 00:03:38.000000000 -0700 +++ 25/drivers/oprofile/cpu_buffer.h 2004-09-02 20:51:52.000000000 -0700 @@ -12,15 +12,18 @@ #include #include +#include +#include #include struct task_struct; -/* allocate a sample buffer for each CPU */ int alloc_cpu_buffers(void); - void free_cpu_buffers(void); +void start_cpu_timers(void); +void end_cpu_timers(void); + /* CPU buffer is composed of such entries (which are * also used for context switch notes) */ @@ -38,11 +41,13 @@ struct oprofile_cpu_buffer { struct op_sample * buffer; unsigned long sample_received; unsigned long sample_lost_overflow; - unsigned long sample_lost_task_exit; + int cpu; + struct timer_list timer; + struct work_struct work; } ____cacheline_aligned; extern struct oprofile_cpu_buffer cpu_buffer[]; -void cpu_buffer_reset(struct oprofile_cpu_buffer *cpu_buf); +void cpu_buffer_reset(struct oprofile_cpu_buffer * cpu_buf); #endif /* OPROFILE_CPU_BUFFER_H */ --- linux-2.6.9-rc1/drivers/oprofile/oprofile_stats.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/drivers/oprofile/oprofile_stats.c 2004-09-02 20:51:52.000000000 -0700 @@ -29,7 +29,6 @@ void oprofile_reset_stats(void) cpu_buf = &cpu_buffer[i]; cpu_buf->sample_received = 0; cpu_buf->sample_lost_overflow = 0; - cpu_buf->sample_lost_task_exit = 0; } atomic_set(&oprofile_stats.sample_lost_no_mm, 0); @@ -66,8 +65,6 @@ void oprofile_create_stats_files(struct &cpu_buf->sample_received); oprofilefs_create_ro_ulong(sb, cpudir, "sample_lost_overflow", &cpu_buf->sample_lost_overflow); - oprofilefs_create_ro_ulong(sb, cpudir, "sample_lost_task_exit", - &cpu_buf->sample_lost_task_exit); } oprofilefs_create_ro_atomic(sb, dir, "sample_lost_no_mm", --- linux-2.6.9-rc1/drivers/parisc/lasi.c 2004-08-24 00:03:17.000000000 -0700 +++ 25/drivers/parisc/lasi.c 2004-09-02 20:51:52.000000000 -0700 @@ -11,7 +11,7 @@ * (at your option) any later version. * * by Alan Cox and - * Alex deVries + * Alex deVries */ #include --- linux-2.6.9-rc1/drivers/parisc/superio.c 2004-08-24 00:03:12.000000000 -0700 +++ 25/drivers/parisc/superio.c 2004-09-02 20:51:52.000000000 -0700 @@ -8,7 +8,7 @@ * (C) Copyright 2000 Linuxcare, Inc. * (C) Copyright 2000 Linuxcare Canada, Inc. * (C) Copyright 2000 Martin K. Petersen - * (C) Copyright 2000 Alex deVries + * (C) Copyright 2000 Alex deVries * (C) Copyright 2001 John Marvin * (C) Copyright 2003 Grant Grundler * --- linux-2.6.9-rc1/drivers/parport/parport_pc.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/drivers/parport/parport_pc.c 2004-09-02 23:57:37.158673472 -0700 @@ -1194,7 +1194,7 @@ struct parport_operations parport_pc_ops #ifdef CONFIG_PARPORT_PC_SUPERIO /* Super-IO chipset detection, Winbond, SMSC */ -static void __devinit show_parconfig_smsc37c669(int io, int key) +static int __devinit show_parconfig_smsc37c669(int io, int key) { int cr1,cr4,cra,cr23,cr26,cr27,i=0; static const char *modes[]={ "SPP and Bidirectional (PS/2)", @@ -1261,17 +1261,26 @@ static void __devinit show_parconfig_sms superios[i].io = 0x278; superios[i].irq = 5; } + if (io != superios[i].io) { + /* how many bytes? */ + if (!request_region(superios[i].io, 3, "smsc parport")) { + superios[i].io = 0; + return 0; + } + } d=(cr26 &0x0f); if((d==1) || (d==3)) superios[i].dma= d; else superios[i].dma= PARPORT_DMA_NONE; + return 1; } } + return 0; } -static void __devinit show_parconfig_winbond(int io, int key) +static int __devinit show_parconfig_winbond(int io, int key) { int cr30,cr60,cr61,cr70,cr74,crf0,i=0; static const char *modes[] = { @@ -1327,14 +1336,23 @@ static void __devinit show_parconfig_win printk(KERN_INFO "Super-IO: too many chips!\n"); else { superios[i].io = (cr60<<8)|cr61; + if (io != superios[i].io) { + /* how many bytes? */ + if (!request_region(superios[i].io, 3, "winbond parport")) { + superios[i].io = 0; + return 0; + } + } superios[i].irq = cr70&0x0f; superios[i].dma = (((cr74 & 0x07) > 3) ? PARPORT_DMA_NONE : (cr74 & 0x07)); + return 1; } } + return 0; } -static void __devinit decode_winbond(int efer, int key, int devid, int devrev, int oldid) +static int __devinit decode_winbond(int efer, int key, int devid, int devrev, int oldid) { const char *type = "unknown"; int id,progif=2; @@ -1342,7 +1360,7 @@ static void __devinit decode_winbond(int if (devid == devrev) /* simple heuristics, we happened to read some non-winbond register */ - return; + return 0; id=(devid<<8) | devrev; @@ -1367,19 +1385,20 @@ static void __devinit decode_winbond(int efer, key, devid, devrev, oldid, type); if (progif == 2) - show_parconfig_winbond(efer,key); + return show_parconfig_winbond(efer,key); + return 0; } -static void __devinit decode_smsc(int efer, int key, int devid, int devrev) +static int __devinit decode_smsc(int efer, int key, int devid, int devrev) { const char *type = "unknown"; - void (*func)(int io, int key); + int (*func)(int io, int key); int id; if (devid == devrev) /* simple heuristics, we happened to read some non-smsc register */ - return; + return 0; func=NULL; id=(devid<<8) | devrev; @@ -1395,7 +1414,8 @@ static void __devinit decode_smsc(int ef efer, key, devid, devrev, type); if (func) - func(efer,key); + return func(efer,key); + return 0; } @@ -1403,6 +1423,9 @@ static void __devinit winbond_check(int { int devid,devrev,oldid,x_devid,x_devrev,x_oldid; + if (!request_region(io, 3, __FUNCTION__)) + return; + /* First probe without key */ outb(0x20,io); x_devid=inb(io+1); @@ -1423,15 +1446,21 @@ static void __devinit winbond_check(int outb(0xaa,io); /* Magic Seal */ if ((x_devid == devid) && (x_devrev == devrev) && (x_oldid == oldid)) - return; /* protection against false positives */ + goto out; /* protection against false positives */ - decode_winbond(io,key,devid,devrev,oldid); + if (decode_winbond(io,key,devid,devrev,oldid)); + return; +out: + release_region(io, 3); } static void __devinit winbond_check2(int io,int key) { int devid,devrev,oldid,x_devid,x_devrev,x_oldid; + if (!request_region(io, 3, __FUNCTION__)) + return; + /* First probe without the key */ outb(0x20,io+2); x_devid=inb(io+2); @@ -1451,15 +1480,21 @@ static void __devinit winbond_check2(int outb(0xaa,io); /* Magic Seal */ if ((x_devid == devid) && (x_devrev == devrev) && (x_oldid == oldid)) - return; /* protection against false positives */ + goto out; /* protection against false positives */ - decode_winbond(io,key,devid,devrev,oldid); + if (decode_winbond(io,key,devid,devrev,oldid)) + return; +out: + release_region(io, 3); } static void __devinit smsc_check(int io, int key) { int id,rev,oldid,oldrev,x_id,x_rev,x_oldid,x_oldrev; + if (!request_region(io, 3, __FUNCTION__)) + return; + /* First probe without the key */ outb(0x0d,io); x_oldid=inb(io+1); @@ -1485,9 +1520,12 @@ static void __devinit smsc_check(int io, if ((x_id == id) && (x_oldrev == oldrev) && (x_oldid == oldid) && (x_rev == rev)) - return; /* protection against false positives */ + goto out; /* protection against false positives */ - decode_smsc(io,key,oldid,oldrev); + if (decode_smsc(io,key,oldid,oldrev)) + return; +out: + release_region(io, 3); } @@ -2636,6 +2674,10 @@ enum parport_pc_pci_cards { netmos_9805, netmos_9815, netmos_9855, + netmos_9735, + netmos_9835, + netmos_9755, + netmos_9715 }; @@ -2709,6 +2751,10 @@ static struct parport_pc_pci { /* netmos_9805 */ { 1, { { 0, -1 }, } }, /* untested */ /* netmos_9815 */ { 2, { { 0, -1 }, { 2, -1 }, } }, /* untested */ /* netmos_9855 */ { 2, { { 0, -1 }, { 2, -1 }, } }, /* untested */ + /* netmos_9735 */ { 1, { { 2, 3 }, } }, /* untested */ + /* netmos_9835 */ { 1, { { 2, 3 }, } }, /* untested */ + /* netmos_9755 */ { 2, { { 0, 1 }, { 2, 3 },} }, /* untested */ + /* netmos_9715 */ { 2, { { 0, 1 }, { 2, 3 },} }, /* untested */ }; static struct pci_device_id parport_pc_pci_tbl[] = { @@ -2786,6 +2832,14 @@ static struct pci_device_id parport_pc_p PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9815 }, { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855, PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9855 }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9735, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9735 }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9835 }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9755, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9755 }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9715, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9715 }, { 0, } /* terminate list */ }; MODULE_DEVICE_TABLE(pci,parport_pc_pci_tbl); --- linux-2.6.9-rc1/drivers/pci/hotplug/acpiphp_core.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/pci/hotplug/acpiphp_core.c 2004-09-02 20:51:52.000000000 -0700 @@ -61,7 +61,7 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); -module_param(debug, bool, 644); +module_param(debug, bool, 0644); /* export the attention callback registration methods */ EXPORT_SYMBOL_GPL(acpiphp_register_attention); --- linux-2.6.9-rc1/drivers/pci/hotplug/acpiphp_ibm.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/pci/hotplug/acpiphp_ibm.c 2004-09-02 20:51:52.000000000 -0700 @@ -340,7 +340,7 @@ static ssize_t ibm_read_apci_table(struc int bytes_read = -EINVAL; char *table = NULL; - dbg("%s: pos = %d, size = %d\n", __FUNCTION__, (int)pos, size); + dbg("%s: pos = %d, size = %zd\n", __FUNCTION__, (int)pos, size); if (pos == 0) { bytes_read = ibm_get_table_from_acpi(&table); @@ -383,8 +383,8 @@ static acpi_status __init ibm_find_acpi_ if(info.current_status && (info.valid & ACPI_VALID_HID) && (!strcmp(info.hardware_id.value, IBM_HARDWARE_ID1) || !strcmp(info.hardware_id.value, IBM_HARDWARE_ID2))) { - dbg("found hardware: %s, handle: %x\n", info.hardware_id.value, - (unsigned int)handle); + dbg("found hardware: %s, handle: %p\n", info.hardware_id.value, + handle); *phandle = handle; /* returning non-zero causes the search to stop * and returns this value to the caller of --- linux-2.6.9-rc1/drivers/pci/hotplug/cpcihp_zt5550.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/pci/hotplug/cpcihp_zt5550.c 2004-09-02 20:51:52.000000000 -0700 @@ -298,7 +298,7 @@ module_exit(zt5550_exit); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -module_param(debug, bool, 644); +module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); -module_param(poll, bool, 644); +module_param(poll, bool, 0644); MODULE_PARM_DESC(poll, "#ENUM polling mode enabled or not"); --- linux-2.6.9-rc1/drivers/pci/hotplug/cpqphp_core.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/drivers/pci/hotplug/cpqphp_core.c 2004-09-02 20:51:52.000000000 -0700 @@ -69,10 +69,10 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -module_param(power_mode, bool, 644); +module_param(power_mode, bool, 0644); MODULE_PARM_DESC(power_mode, "Power mode enabled or not"); -module_param(debug, bool, 644); +module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); #define CPQHPC_MODULE_MINOR 208 --- linux-2.6.9-rc1/drivers/pci/hotplug/pciehp_core.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/drivers/pci/hotplug/pciehp_core.c 2004-09-02 20:51:52.000000000 -0700 @@ -57,9 +57,9 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -module_param(pciehp_debug, bool, 644); -module_param(pciehp_poll_mode, bool, 644); -module_param(pciehp_poll_time, int, 644); +module_param(pciehp_debug, bool, 0644); +module_param(pciehp_poll_mode, bool, 0644); +module_param(pciehp_poll_time, int, 0644); MODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not"); MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not"); MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds"); --- linux-2.6.9-rc1/drivers/pci/hotplug/pci_hotplug_core.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/pci/hotplug/pci_hotplug_core.c 2004-09-02 20:51:52.000000000 -0700 @@ -701,7 +701,7 @@ module_exit(pci_hotplug_exit); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -module_param(debug, bool, 644); +module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); EXPORT_SYMBOL_GPL(pci_hotplug_slots_subsys); --- linux-2.6.9-rc1/drivers/pci/hotplug/pcihp_skeleton.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/drivers/pci/hotplug/pcihp_skeleton.c 2004-09-02 20:51:52.000000000 -0700 @@ -70,7 +70,7 @@ static int num_slots; MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -module_param(debug, bool, 644); +module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); static int enable_slot (struct hotplug_slot *slot); --- linux-2.6.9-rc1/drivers/pci/hotplug/shpchp_core.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/drivers/pci/hotplug/shpchp_core.c 2004-09-02 20:51:52.000000000 -0700 @@ -57,9 +57,9 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -module_param(shpchp_debug, bool, 644); -module_param(shpchp_poll_mode, bool, 644); -module_param(shpchp_poll_time, int, 644); +module_param(shpchp_debug, bool, 0644); +module_param(shpchp_poll_mode, bool, 0644); +module_param(shpchp_poll_time, int, 0644); MODULE_PARM_DESC(shpchp_debug, "Debugging mode enabled or not"); MODULE_PARM_DESC(shpchp_poll_mode, "Using polling mechanism for hot-plug events or not"); MODULE_PARM_DESC(shpchp_poll_time, "Polling mechanism frequency, in seconds"); --- linux-2.6.9-rc1/drivers/pci/pci.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/drivers/pci/pci.c 2004-09-03 00:56:53.806980744 -0700 @@ -382,8 +382,13 @@ pci_enable_device_bars(struct pci_dev *d int pci_enable_device(struct pci_dev *dev) { + int err; + dev->is_enabled = 1; - return pci_enable_device_bars(dev, (1 << PCI_NUM_RESOURCES) - 1); + if ((err = pci_enable_device_bars(dev, (1 << PCI_NUM_RESOURCES) - 1))) + return err; + pci_fixup_device(pci_fixup_enable, dev); + return 0; } /** --- linux-2.6.9-rc1/drivers/pci/pci-driver.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/drivers/pci/pci-driver.c 2004-09-02 20:51:52.000000000 -0700 @@ -390,10 +390,9 @@ pci_populate_driver_dir(struct pci_drive * pci_register_driver - register a new pci driver * @drv: the driver structure to register * - * Adds the driver structure to the list of registered drivers - * Returns the number of pci devices which were claimed by the driver - * during registration. The driver remains registered even if the - * return value is zero. + * Adds the driver structure to the list of registered drivers. + * Returns a negative value on error. The driver remains registered + * even if no device was claimed during registration. */ int pci_register_driver(struct pci_driver *drv) --- linux-2.6.9-rc1/drivers/pci/pci.ids 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/pci/pci.ids 2004-09-02 20:51:52.000000000 -0700 @@ -225,10 +225,22 @@ 1000 0530 MegaRAID 530 SCSI 320-0X RAID Controller 1000 0531 MegaRAID 531 SCSI 320-4X RAID Controller 1000 0532 MegaRAID 532 SCSI 320-2X RAID Controller - 1028 0533 PowerEdge Expandable RAID Controller 4/QC - 8086 0532 Storage RAID Controller SRCU42X + 1028 0531 PowerEdge Expandable RAID Controller 4/QC + 8086 0530 MegaRAID Intel RAID Controller SRCZCRX + 8086 0532 MegaRAID Intel RAID Controller SRCU42X 0408 MegaRAID - 1028 0002 PowerEdge Expandable RAID Controller 4e/DC + 1000 0001 MegaRAID SCSI 320-1E RAID Controller + 1000 0002 MegaRAID SCSI 320-2E RAID Controller + 1028 0001 Dell PowerEdge RAID Controller PERC4e/SC + 1028 0002 Dell PowerEdge RAID Controller PERC4e/DC + 1734 1065 FSC MegaRAID PCI Express ROMB + 8086 0002 MegaRAID Intel RAID Controller SRCU42E + 0409 MegaRAID + 1000 3004 MegaRAID SATA 300-4X RAID Controller + 1000 3008 MegaRAID SATA 300-8X RAID Controller + 8086 3008 MegaRAID Intel RAID Controller SRCS28X + 8086 3431 MegaRAID Intel RAID Controller Alief SROMBU42E + 8086 3499 MegaRAID Intel RAID Controller Harwich SROMBU42E 0621 FC909 Fibre Channel Adapter 0622 FC929 Fibre Channel Adapter 1000 1020 44929 O Dual Fibre Channel card @@ -252,7 +264,7 @@ 1960 MegaRAID 1000 0518 MegaRAID 518 SCSI 320-2 Controller 1000 0520 MegaRAID 520 SCSI 320-1 Controller - 1000 0522 MegaRAID 522 i4133 RAID Controller + 1000 0522 MegaRAID 522 i4 133 RAID Controller 1000 0523 MegaRAID SATA 150-6 RAID Controller 1000 4523 MegaRAID SATA 150-4 RAID Controller 1000 a520 MegaRAID ZCR SCSI 320-0 Controller @@ -260,6 +272,8 @@ 1028 0520 MegaRAID 520 DELL PERC 4/SC RAID Controller 1028 0531 PowerEdge Expandable RAID Controller 4/QC 1028 0533 PowerEdge Expandable RAID Controller 4/QC + 8086 0520 MegaRAID Intel RAID Controller SRCU41L + 8086 0523 MegaRAID Intel RAID Controller SRCS16 1001 Kolter Electronic 0010 PCI 1616 Measurement card with 32 digital I/O lines 0011 OPTO-PCI Opto-Isolated digital I/O board @@ -959,6 +973,7 @@ 0180 Snipe chipset SCSI controller 1014 0241 iSeries 2757 DASD IOA 1014 0264 Quad Channel PCI-X U320 SCSI RAID Adapter (2780) + 1014 02BD Quad Channel PCI-X U320 DDR SCSI RAID Adapter (570F) 01a7 PCI-X to PCI-X Bridge 01bd ServeRAID Controller 1014 01be ServeRAID-4M @@ -981,6 +996,8 @@ 0266 PCI-X Dual Channel SCSI 0268 Gigabit Ethernet-SX Adapter (PCI-X) 0269 10/100/1000 Base-TX Ethernet Adapter (PCI-X) + 028C Citrine chipset SCSI controller + 1014 02BE Dual Channel PCI-X U320 DDR SCSI RAID Adapter (571B) 0302 X-Architecture Bridge [Summit] ffff MPIC-2 interrupt controller 1015 LSI Logic Corp of Canada @@ -1244,6 +1261,8 @@ 1028 016c PowerEdge Expandable RAID Controller 4e/Si 1028 016d PowerEdge Expandable RAID Controller 4e/Di 1028 016e PowerEdge Expandable RAID Controller 4e/Di + 1028 016f PowerEdge Expandable RAID Controller 4e/Di + 1028 0170 PowerEdge Expandable RAID Controller 4e/Di 0014 Remote Access Card 4 Daughter Card SMIC interface 1029 Siemens Nixdorf IS 102a LSI Logic @@ -2118,6 +2137,8 @@ 1014 0242 iSeries 2872 DASD IOA 1014 0266 Dual Channel PCI-X U320 SCSI Adapter 1014 0278 Dual Channel PCI-X U320 SCSI RAID Adapter + 1014 02D3 Dual Channel PCI-X U320 SCSI Adapter + 1014 02D4 Dual Channel PCI-X U320 SCSI RAID Adapter ba55 eXtremeRAID 1100 support Device ba56 eXtremeRAID 2000/3000 support Device 106a Aten Research Inc --- linux-2.6.9-rc1/drivers/pci/probe.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/drivers/pci/probe.c 2004-09-02 20:51:52.000000000 -0700 @@ -571,6 +571,11 @@ static int pci_cfg_space_size(struct pci return PCI_CFG_SPACE_SIZE; } +static void pci_release_bus_bridge_dev(struct device *dev) +{ + kfree(dev); +} + /* * Read the config data for a PCI device, sanity-check it * and fill in the dev structure... @@ -772,6 +777,7 @@ struct pci_bus * __devinit pci_scan_bus_ memset(dev, 0, sizeof(*dev)); dev->parent = parent; + dev->release = pci_release_bus_bridge_dev; sprintf(dev->bus_id, "pci%04x:%02x", pci_domain_nr(b), bus); device_register(dev); b->bridge = get_device(dev); --- linux-2.6.9-rc1/drivers/pci/quirks.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/drivers/pci/quirks.c 2004-09-03 00:56:53.807980592 -0700 @@ -491,9 +491,9 @@ static void __devinit quirk_via_irqpic(s pci_write_config_byte(dev, PCI_INTERRUPT_LINE, new_irq); } } -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_2, quirk_via_irqpic ); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5, quirk_via_irqpic ); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_6, quirk_via_irqpic ); +DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_2, quirk_via_irqpic ); +DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5, quirk_via_irqpic ); +DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_6, quirk_via_irqpic ); /* @@ -1003,6 +1003,9 @@ extern struct pci_fixup __start_pci_fixu extern struct pci_fixup __end_pci_fixups_header[]; extern struct pci_fixup __start_pci_fixups_final[]; extern struct pci_fixup __end_pci_fixups_final[]; +extern struct pci_fixup __start_pci_fixups_enable[]; +extern struct pci_fixup __end_pci_fixups_enable[]; + void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) { @@ -1018,6 +1021,12 @@ void pci_fixup_device(enum pci_fixup_pas start = __start_pci_fixups_final; end = __end_pci_fixups_final; break; + + case pci_fixup_enable: + start = __start_pci_fixups_enable; + end = __end_pci_fixups_enable; + break; + default: /* stupid compiler warning, you would think with an enum... */ return; --- linux-2.6.9-rc1/drivers/pci/remove.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/pci/remove.c 2004-09-02 20:51:52.000000000 -0700 @@ -59,6 +59,18 @@ int pci_remove_device_safe(struct pci_de } EXPORT_SYMBOL(pci_remove_device_safe); +void pci_remove_bus(struct pci_bus *b) +{ + pci_proc_detach_bus(b); + + spin_lock(&pci_bus_lock); + list_del(&b->node); + spin_unlock(&pci_bus_lock); + + class_device_unregister(&b->class_dev); +} +EXPORT_SYMBOL(pci_remove_bus); + /** * pci_remove_bus_device - remove a PCI device and any children * @dev: the device to remove @@ -77,13 +89,7 @@ void pci_remove_bus_device(struct pci_de struct pci_bus *b = dev->subordinate; pci_remove_behind_bridge(dev); - pci_proc_detach_bus(b); - - spin_lock(&pci_bus_lock); - list_del(&b->node); - spin_unlock(&pci_bus_lock); - - class_device_unregister(&b->class_dev); + pci_remove_bus(b); dev->subordinate = NULL; } --- linux-2.6.9-rc1/drivers/pci/search.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/drivers/pci/search.c 2004-09-03 00:56:33.072132920 -0700 @@ -1,10 +1,10 @@ /* * PCI searching functions. * - * Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter, - * David Mosberger-Tang - * Copyright 1997 -- 2000 Martin Mares - * Copyright 2003 -- Greg Kroah-Hartman + * Copyright (C) 1993 -- 1997 Drew Eckhardt, Frederic Potter, + * David Mosberger-Tang + * Copyright (C) 1997 -- 2000 Martin Mares + * Copyright (C) 2003 -- 2004 Greg Kroah-Hartman */ #include @@ -156,10 +156,11 @@ struct pci_dev * pci_get_slot(struct pci * the pci device returned by this function can disappear at any moment in * time. */ -struct pci_dev * -pci_find_subsys(unsigned int vendor, unsigned int device, - unsigned int ss_vendor, unsigned int ss_device, - const struct pci_dev *from) +static struct pci_dev * pci_find_subsys(unsigned int vendor, + unsigned int device, + unsigned int ss_vendor, + unsigned int ss_device, + const struct pci_dev *from) { struct list_head *n; struct pci_dev *dev; @@ -257,12 +258,6 @@ exit: * @from: Previous PCI device found in search, or %NULL for new search. * * Iterates through the list of known PCI devices. If a PCI device is - * found with a matching @vendor and @device, a pointer to its device structure is - * returned. Otherwise, %NULL is returned. - * A new search is initiated by passing %NULL to the @from argument. - * Otherwise if @from is not %NULL, searches continue from next device on the global list. - * - * Iterates through the list of known PCI devices. If a PCI device is * found with a matching @vendor and @device, the reference count to the * device is incremented and a pointer to its device structure is returned. * Otherwise, %NULL is returned. A new search is initiated by passing %NULL @@ -312,25 +307,26 @@ exit: return dev; } - /** - * pci_find_class - begin or continue searching for a PCI device by class + * pci_get_class - begin or continue searching for a PCI device by class * @class: search for a PCI device with this class designation * @from: Previous PCI device found in search, or %NULL for new search. * * Iterates through the list of known PCI devices. If a PCI device is - * found with a matching @class, a pointer to its device structure is - * returned. Otherwise, %NULL is returned. + * found with a matching @class, the reference count to the device is + * incremented and a pointer to its device structure is returned. + * Otherwise, %NULL is returned. * A new search is initiated by passing %NULL to the @from argument. * Otherwise if @from is not %NULL, searches continue from next device - * on the global list. + * on the global list. The reference count for @from is always decremented + * if it is not %NULL. */ -struct pci_dev * -pci_find_class(unsigned int class, const struct pci_dev *from) +struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from) { struct list_head *n; struct pci_dev *dev; + WARN_ON(in_interrupt()); spin_lock(&pci_bus_lock); n = from ? from->global_list.next : pci_devices.next; @@ -342,16 +338,17 @@ pci_find_class(unsigned int class, const } dev = NULL; exit: + pci_dev_put(from); + dev = pci_dev_get(dev); spin_unlock(&pci_bus_lock); return dev; } EXPORT_SYMBOL(pci_find_bus); -EXPORT_SYMBOL(pci_find_class); EXPORT_SYMBOL(pci_find_device); EXPORT_SYMBOL(pci_find_device_reverse); EXPORT_SYMBOL(pci_find_slot); -EXPORT_SYMBOL(pci_find_subsys); EXPORT_SYMBOL(pci_get_device); EXPORT_SYMBOL(pci_get_subsys); EXPORT_SYMBOL(pci_get_slot); +EXPORT_SYMBOL(pci_get_class); --- linux-2.6.9-rc1/drivers/pcmcia/au1000_generic.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/pcmcia/au1000_generic.c 2004-09-02 20:51:52.000000000 -0700 @@ -566,7 +566,6 @@ static int au1000_pcmcia_set_mem_map(unsigned int sock, struct pccard_mem_map *map) { unsigned int speed; - unsigned long start; u_long flags; if(map->map>=MAX_WIN){ @@ -588,25 +587,19 @@ au1000_pcmcia_set_mem_map(unsigned int s } spin_lock_irqsave(&pcmcia_lock, flags); - start=map->sys_start; - - if(map->sys_stop==0) - map->sys_stop=MAP_SIZE-1; - if (map->flags & MAP_ATTRIB) { - map->sys_start = pcmcia_socket[sock].phys_attr + + map->static_start = pcmcia_socket[sock].phys_attr + map->card_start; } else { - map->sys_start = pcmcia_socket[sock].phys_mem + + map->static_start = pcmcia_socket[sock].phys_mem + map->card_start; } - map->sys_stop=map->sys_start+(map->sys_stop-start); pcmcia_socket[sock].mem_map[map->map]=*map; spin_unlock_irqrestore(&pcmcia_lock, flags); - debug(3, "set_mem_map %d start %x stop %x card_start %x\n", - map->map, map->sys_start, map->sys_stop, + debug(3, "set_mem_map %d start %x card_start %x\n", + map->map, map->static_start, map->card_start); return 0; --- linux-2.6.9-rc1/drivers/pcmcia/cistpl.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/drivers/pcmcia/cistpl.c 2004-09-02 20:51:52.000000000 -0700 @@ -114,8 +114,6 @@ set_cis_map(struct pcmcia_socket *s, uns printk(KERN_NOTICE "cs: unable to map card memory!\n"); return NULL; } - mem->sys_start = mem->res->start; - mem->sys_stop = mem->res->end; s->cis_virt = ioremap(mem->res->start, s->map_size); } mem->card_start = card_offset; @@ -124,7 +122,7 @@ set_cis_map(struct pcmcia_socket *s, uns if (s->features & SS_CAP_STATIC_MAP) { if (s->cis_virt) iounmap(s->cis_virt); - s->cis_virt = ioremap(mem->sys_start, s->map_size); + s->cis_virt = ioremap(mem->static_start, s->map_size); } return s->cis_virt; } --- linux-2.6.9-rc1/drivers/pcmcia/cs.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/drivers/pcmcia/cs.c 2004-09-03 00:56:47.821890616 -0700 @@ -1160,6 +1160,22 @@ EXPORT_SYMBOL(pcmcia_lookup_bus); #endif +/*===================================================================== + + Return the driver model device associated with a card.. + +======================================================================*/ + +struct device *pcmcia_lookup_device(client_handle_t handle) +{ + if (CHECK_HANDLE(handle)) + return NULL; + + return handle->device; +} + +EXPORT_SYMBOL(pcmcia_lookup_device); + /*====================================================================== Get the current socket state bits. We don't support the latched @@ -1886,11 +1902,6 @@ int pcmcia_request_window(client_handle_ (*handle)->dev_info, s); if (!win->ctl.res) return CS_IN_USE; - win->ctl.sys_start = win->ctl.res->start; - win->ctl.sys_stop = win->ctl.res->end; - } else { - win->ctl.sys_start = req->Base; - win->ctl.sys_stop = req->Base + req->Size - 1; } (*handle)->state |= CLIENT_WIN_REQ(w); @@ -1912,7 +1923,11 @@ int pcmcia_request_window(client_handle_ s->state |= SOCKET_WIN_REQ(w); /* Return window handle */ - req->Base = win->ctl.sys_start; + if (s->features & SS_CAP_STATIC_MAP) { + req->Base = win->ctl.static_start; + } else { + req->Base = win->ctl.res->start; + } *wh = win; return CS_SUCCESS; @@ -2154,6 +2169,7 @@ EXPORT_SYMBOL(pcmcia_set_event_mask); EXPORT_SYMBOL(pcmcia_suspend_card); EXPORT_SYMBOL(pcmcia_validate_cis); EXPORT_SYMBOL(pcmcia_write_memory); +EXPORT_SYMBOL(read_tuple); EXPORT_SYMBOL(dead_socket); EXPORT_SYMBOL(MTDHelperEntry); --- linux-2.6.9-rc1/drivers/pcmcia/cs_internal.h 2004-08-24 00:03:50.000000000 -0700 +++ 25/drivers/pcmcia/cs_internal.h 2004-09-03 00:56:47.821890616 -0700 @@ -33,6 +33,7 @@ typedef struct eraseq_t { typedef struct client_t { u_short client_magic; struct pcmcia_socket *Socket; + struct device *device; u_char Function; dev_info_t dev_info; u_int Attributes; --- linux-2.6.9-rc1/drivers/pcmcia/ds.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/pcmcia/ds.c 2004-09-03 00:56:48.498787712 -0700 @@ -119,6 +119,8 @@ struct pcmcia_bus_socket { struct work_struct removal; socket_bind_t *bind; struct pcmcia_socket *parent; + struct list_head devices; + struct semaphore device_mutex; }; #define DS_SOCKET_PRESENT 0x01 @@ -164,6 +166,7 @@ static int pcmcia_bind_device(bind_req_t client->client_magic = CLIENT_MAGIC; strlcpy(client->dev_info, (char *)req->dev_info, DEV_NAME_LEN); client->Socket = s; + client->device = &req->device->dev; client->Function = req->Function; client->state = CLIENT_UNBOUND; client->erase_busy.next = &client->erase_busy; @@ -354,6 +357,8 @@ EXPORT_SYMBOL(cs_error); /*======================================================================*/ +static struct pcmcia_device * get_pcmcia_device (struct pcmcia_bus_socket *s, int function); +static void put_pcmcia_device(struct pcmcia_device *dev); static struct pcmcia_driver * get_pcmcia_driver (dev_info_t *dev_info); static struct pcmcia_bus_socket * get_socket_info_by_nr(unsigned int nr); @@ -430,6 +435,203 @@ static int proc_read_drivers(char *buf, /*====================================================================== + sysfs support + +======================================================================*/ + +static ssize_t +pcmcia_show_product_string(struct pcmcia_device *dev, char *buf, int index) +{ + char *str = buf; + + if (dev->id_mask & DEVICE_HAS_VERSION_INFO && index < dev->vers_1.ns) + str += sprintf(str,"%s\n", + dev->vers_1.str+dev->vers_1.ofs[index]); + else + str += sprintf(str,"\n"); + return (str - buf); +} + +#define pcmcia_prod_str_attr(field, index) \ +static ssize_t \ +show_##field (struct device *dmdev, char *buf) \ +{ \ + struct pcmcia_device *dev; \ + \ + dev = to_pcmcia_device (dmdev); \ + return pcmcia_show_product_string(dev,buf,index); \ +} \ +static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL); + +pcmcia_prod_str_attr(prod_str0,0); +pcmcia_prod_str_attr(prod_str1,1); +pcmcia_prod_str_attr(prod_str2,2); +pcmcia_prod_str_attr(prod_str3,3); + +static ssize_t +pcmcia_show_manfid(struct device *dmdev, char *buf) +{ + struct pcmcia_device *dev = to_pcmcia_device(dmdev); + char *str = buf; + + if (dev->id_mask & DEVICE_HAS_MANF_INFO) + str += sprintf(str,"0x%04x, 0x%04x\n", + dev->manfid.manf, + dev->manfid.card); + else + str += sprintf(str,"\n"); + return (str - buf); +} + +static DEVICE_ATTR(manfid,S_IRUGO,pcmcia_show_manfid,NULL); + +static void pcmcia_sysfs_attach(struct pcmcia_device *dev) +{ + device_create_file (&dev->dev, &dev_attr_prod_str0); + device_create_file (&dev->dev, &dev_attr_prod_str1); + device_create_file (&dev->dev, &dev_attr_prod_str2); + device_create_file (&dev->dev, &dev_attr_prod_str3); + device_create_file (&dev->dev, &dev_attr_manfid); +} + +/*====================================================================== + + device addition, removal, and hotplug functions for the driver model + +======================================================================*/ + +static void pcmcia_bus_release_device(struct device *pdev) +{ + struct pcmcia_device *dev = to_pcmcia_device(pdev); + kfree(dev); +} + +static int pcmcia_bus_insert_card(struct pcmcia_bus_socket *s) +{ + struct pcmcia_device *dev; + int i, ret, function_count = 0, has_cis = 0; + cisinfo_t cisinfo; + + if (!(s->state & DS_SOCKET_PRESENT)) + return CS_NO_CARD; + + down(&s->device_mutex); + if (!list_empty(&s->devices)) { + ret = -EBUSY; + goto out; + } + + ret = pcmcia_validate_cis(s->handle, &cisinfo); + if (ret) + goto out; + + if (cisinfo.Chains) { + cistpl_longlink_mfc_t mfc; + + has_cis = 1; + if (!read_tuple(s->handle, CISTPL_LONGLINK_MFC, &mfc)) + function_count = mfc.nfn; + } + + for (i=0; i<=function_count; i++) { + dev = kmalloc(sizeof(struct pcmcia_device), GFP_KERNEL); + if (!dev) + continue; + + memset(dev, 0, sizeof(struct pcmcia_device)); + dev->socket = s; + dev->dev.parent = s->parent->dev.dev; + dev->dev.bus = &pcmcia_bus_type; + dev->dev.release = &pcmcia_bus_release_device; + snprintf(dev->dev.bus_id, BUS_ID_SIZE, "%u:%u", s->parent->sock, i); + + dev->function = i; + if (has_cis) { + if (!read_tuple(s->handle, CISTPL_VERS_1, &dev->vers_1)) + dev->id_mask |= DEVICE_HAS_VERSION_INFO; + if (!read_tuple(s->handle, CISTPL_MANFID, &dev->manfid)) + dev->id_mask |= DEVICE_HAS_MANF_INFO; + } + + ret = device_register(&dev->dev); + if (!ret) { + list_add_tail(&dev->device_list, &s->devices); + pcmcia_sysfs_attach(dev); + } else + kfree(dev); + } + +out: + up(&s->device_mutex); + return ret; +} + +static void pcmcia_bus_remove_card(struct pcmcia_bus_socket *s) +{ + struct pcmcia_device *dev, *tmp; + down(&s->device_mutex); + list_for_each_entry_safe(dev, tmp, &s->devices, device_list) { + list_del(&dev->device_list); + device_unregister(&dev->dev); + } + up(&s->device_mutex); +} + +#ifdef CONFIG_HOTPLUG + +int pcmcia_bus_hotplug(struct device *pdev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + struct pcmcia_device *dev; + char *scratch; + int i = 0; + int length = 0; + + if (!pdev) + return -ENODEV; + + dev = to_pcmcia_device(pdev); + + scratch = buffer; + + /* stuff we want to pass to /sbin/hotplug */ + envp[i++] = scratch; + length += snprintf (scratch, buffer_size - length, "PRODUCT="); + for (i = 0; i < dev->vers_1.ns; i++) { + length += snprintf(scratch,buffer_size - length, "%s\"%s\"", (i>0) ? "," : "", + dev->vers_1.str+dev->vers_1.ofs[i]); + } + + if ((buffer_size - length <= 0) || (i >= num_envp)) + return -ENOMEM; + ++length; + scratch += length; + + envp [i++] = scratch; + length += snprintf (scratch, buffer_size - length, "MANFID=0x%04x,0x%04x", + dev->manfid.manf, dev->manfid.card); + if ((buffer_size - length <= 0) || (i >= num_envp)) + return -ENOMEM; + ++length; + scratch += length; + + envp[i] = 0; + + return 0; +} + +#else /* CONFIG_HOTPLUG */ + +int pcmcia_bus_hotplug(struct device *pdev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + return -ENODEV; +} + +#endif /* CONFIG_HOTPLUG */ + +/*====================================================================== + These manage a ring buffer of events pending for one user process ======================================================================*/ @@ -501,6 +703,7 @@ static int ds_event(event_t event, int p case CS_EVENT_CARD_REMOVAL: s->state &= ~DS_SOCKET_PRESENT; + pcmcia_bus_remove_card(s); if (!(s->state & DS_SOCKET_REMOVAL_PENDING)) { s->state |= DS_SOCKET_REMOVAL_PENDING; schedule_delayed_work(&s->removal, HZ/10); @@ -562,6 +765,7 @@ static int bind_mtd(struct pcmcia_bus_so static int bind_request(struct pcmcia_bus_socket *s, bind_info_t *bind_info) { struct pcmcia_driver *driver; + struct pcmcia_device *device; socket_bind_t *b; bind_req_t bind_req; int ret; @@ -587,7 +791,12 @@ static int bind_request(struct pcmcia_bu if (!try_module_get(driver->owner)) return -EINVAL; + device = get_pcmcia_device(s, bind_info->function); + if (!device) + return -EINVAL; + bind_req.Socket = s->parent; + bind_req.device = device; bind_req.Function = bind_info->function; bind_req.dev_info = (dev_info_t *) driver->drv.name; ret = pcmcia_bind_device(&bind_req); @@ -614,16 +823,25 @@ static int bind_request(struct pcmcia_bu b->next = s->bind; s->bind = b; + down_write(&device->dev.bus->subsys.rwsem); + device->dev.driver = &driver->drv; + if (driver->attach) { b->instance = driver->attach(); if (b->instance == NULL) { printk(KERN_NOTICE "ds: unable to create instance " "of '%s'!\n", (char *)bind_info->dev_info); module_put(driver->owner); + device->dev.driver = NULL; + up_write(&device->dev.bus->subsys.rwsem); return -ENODEV; } } + device_bind_driver(&device->dev); + up_write(&device->dev.bus->subsys.rwsem); + put_pcmcia_device(device); + return 0; } /* bind_request */ @@ -699,6 +917,7 @@ static int get_device_info(struct pcmcia static int unbind_request(struct pcmcia_bus_socket *s, bind_info_t *bind_info) { socket_bind_t **b, *c; + struct pcmcia_device *device; ds_dbg(2, "unbind_request(%d, '%s')\n", s->parent->sock, (char *)bind_info->dev_info); @@ -710,6 +929,14 @@ static int unbind_request(struct pcmcia_ if (*b == NULL) return -ENODEV; + device = get_pcmcia_device(s, bind_info->function); + if (device) { + down_write(&device->dev.bus->subsys.rwsem); + device_release_driver(&device->dev); + up_write(&device->dev.bus->subsys.rwsem); + put_pcmcia_device(device); + } + c = *b; c->driver->use_count--; if (c->driver->detach) { @@ -1005,6 +1232,7 @@ static int ds_ioctl(struct inode * inode break; case DS_BIND_REQUEST: if (!capable(CAP_SYS_ADMIN)) return -EPERM; + pcmcia_bus_insert_card(s); err = bind_request(s, &buf.bind_info); break; case DS_GET_DEVICE_INFO: @@ -1087,6 +1315,8 @@ static int __devinit pcmcia_bus_add_sock init_waitqueue_head(&s->request); /* initialize data */ + INIT_LIST_HEAD(&s->devices); + init_MUTEX(&s->device_mutex); INIT_WORK(&s->removal, handle_removal, s); s->parent = socket; @@ -1135,6 +1365,8 @@ static void pcmcia_bus_remove_socket(str pcmcia_deregister_client(socket->pcmcia->handle); + pcmcia_bus_remove_card(socket->pcmcia); + socket->pcmcia->state |= DS_SOCKET_DEAD; pcmcia_put_bus_socket(socket->pcmcia); socket->pcmcia = NULL; @@ -1153,7 +1385,9 @@ static struct class_interface pcmcia_bus struct bus_type pcmcia_bus_type = { .name = "pcmcia", + .hotplug = pcmcia_bus_hotplug, }; + EXPORT_SYMBOL(pcmcia_bus_type); @@ -1186,7 +1420,6 @@ fs_initcall(init_pcmcia_bus); /* one lev static void __exit exit_pcmcia_bus(void) { - class_interface_unregister(&pcmcia_bus_interface); #ifdef CONFIG_PROC_FS if (proc_pccard) { @@ -1197,6 +1430,7 @@ static void __exit exit_pcmcia_bus(void) if (major_dev != -1) unregister_chrdev(major_dev, "pcmcia"); + class_interface_unregister(&pcmcia_bus_interface); bus_unregister(&pcmcia_bus_type); } module_exit(exit_pcmcia_bus); @@ -1244,3 +1478,24 @@ static struct pcmcia_driver * get_pcmcia return cmp.drv; return NULL; } + +static struct pcmcia_device * get_pcmcia_device (struct pcmcia_bus_socket *s, int function) +{ + struct pcmcia_device *dev, *tmp; + down(&s->device_mutex); + list_for_each_entry_safe(dev, tmp, &s->devices, device_list) { + if (dev->function == function) { + if (!get_device(&dev->dev)) + break; + up(&s->device_mutex); + return dev; + } + } + up(&s->device_mutex); + return NULL; +} + +static void put_pcmcia_device(struct pcmcia_device *dev) +{ + put_device(&dev->dev); +} --- linux-2.6.9-rc1/drivers/pcmcia/hd64465_ss.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/pcmcia/hd64465_ss.c 2004-09-02 20:51:52.000000000 -0700 @@ -636,19 +636,17 @@ static int hs_set_mem_map(struct pcmcia_ hs_socket_t *sp = container_of(s, struct hs_socket_t, socket); struct pccard_mem_map *smem; int map = mem->map; - unsigned long paddr, size; + unsigned long paddr; #if 0 - DPRINTK("hs_set_mem_map(sock=%d, map=%d, flags=0x%x, sys_start=0x%08lx, sys_end=0x%08lx, card_start=0x%08x)\n", - sock, map, mem->flags, mem->sys_start, mem->sys_stop, mem->card_start); + DPRINTK("hs_set_mem_map(sock=%d, map=%d, flags=0x%x, card_start=0x%08x)\n", + sock, map, mem->flags, mem->card_start); #endif if (map >= MAX_WIN) return -EINVAL; smem = &sp->mem_maps[map]; - size = mem->sys_stop - mem->sys_start + 1; - paddr = sp->mem_base; /* base of Attribute mapping */ if (!(mem->flags & MAP_ATTRIB)) paddr += HD64465_PCC_WINDOW; /* base of Common mapping */ @@ -660,8 +658,7 @@ static int hs_set_mem_map(struct pcmcia_ * queries our fixed mapping. I wish this fact had been * documented - Greg Banks. */ - mem->sys_start = paddr; - mem->sys_stop = paddr + size - 1; + mem->static_start = paddr; *smem = *mem; --- linux-2.6.9-rc1/drivers/pcmcia/i82092.c 2004-08-24 00:03:49.000000000 -0700 +++ 25/drivers/pcmcia/i82092.c 2004-09-02 20:51:52.000000000 -0700 @@ -422,7 +422,7 @@ static int i82092aa_init(struct pcmcia_s int i; struct resource res = { .start = 0, .end = 0x0fff }; pccard_io_map io = { 0, 0, 0, 0, 1 }; - pccard_mem_map mem = { .res = &res, .sys_stop = 0x0fff, }; + pccard_mem_map mem = { .res = &res, }; enter("i82092aa_init"); @@ -706,11 +706,15 @@ static int i82092aa_set_io_map(struct pc static int i82092aa_set_mem_map(struct pcmcia_socket *socket, struct pccard_mem_map *mem) { - unsigned int sock = container_of(socket, struct socket_info, socket)->number; + struct socket_info *sock_info = container_of(socket, struct socket_info, socket); + unsigned int sock = sock_info->number; + struct pci_bus_region region; unsigned short base, i; unsigned char map; enter("i82092aa_set_mem_map"); + + pcibios_resource_to_bus(sock_info->dev, ®ion, mem->res); map = mem->map; if (map > 4) { @@ -719,10 +723,10 @@ static int i82092aa_set_mem_map(struct p } - if ( (mem->card_start > 0x3ffffff) || (mem->sys_start > mem->sys_stop) || + if ( (mem->card_start > 0x3ffffff) || (region.start > region.end) || (mem->speed > 1000) ) { leave("i82092aa_set_mem_map: invalid address / speed"); - printk("invalid mem map for socket %i : %lx to %lx with a start of %x \n",sock,mem->sys_start, mem->sys_stop, mem->card_start); + printk("invalid mem map for socket %i : %lx to %lx with a start of %x \n",sock,region.start, region.end, mem->card_start); return -EINVAL; } @@ -731,11 +735,11 @@ static int i82092aa_set_mem_map(struct p indirect_resetbit(sock, I365_ADDRWIN, I365_ENA_MEM(map)); -/* printk("set_mem_map: Setting map %i range to %x - %x on socket %i, speed is %i, active = %i \n",map, mem->sys_start,mem->sys_stop,sock,mem->speed,mem->flags & MAP_ACTIVE); */ +/* printk("set_mem_map: Setting map %i range to %x - %x on socket %i, speed is %i, active = %i \n",map, region.start,region.end,sock,mem->speed,mem->flags & MAP_ACTIVE); */ /* write the start address */ base = I365_MEM(map); - i = (mem->sys_start >> 12) & 0x0fff; + i = (region.start >> 12) & 0x0fff; if (mem->flags & MAP_16BIT) i |= I365_MEM_16BIT; if (mem->flags & MAP_0WS) @@ -744,7 +748,7 @@ static int i82092aa_set_mem_map(struct p /* write the stop address */ - i= (mem->sys_stop >> 12) & 0x0fff; + i= (region.end >> 12) & 0x0fff; switch (to_cycles(mem->speed)) { case 0: break; @@ -763,7 +767,7 @@ static int i82092aa_set_mem_map(struct p /* card start */ - i = ((mem->card_start - mem->sys_start) >> 12) & 0x3fff; + i = ((mem->card_start - region.start) >> 12) & 0x3fff; if (mem->flags & MAP_WRPROT) i |= I365_MEM_WRPROT; if (mem->flags & MAP_ATTRIB) { --- linux-2.6.9-rc1/drivers/pcmcia/i82365.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/pcmcia/i82365.c 2004-09-02 20:51:52.000000000 -0700 @@ -1159,13 +1159,13 @@ static int i365_set_mem_map(u_short sock debug(1, "SetMemMap(%d, %d, %#2.2x, %d ns, %#5.5lx-%#5.5" "lx, %#5.5x)\n", sock, mem->map, mem->flags, mem->speed, - mem->sys_start, mem->sys_stop, mem->card_start); + mem->res->start, mem->res->end, mem->card_start); map = mem->map; if ((map > 4) || (mem->card_start > 0x3ffffff) || - (mem->sys_start > mem->sys_stop) || (mem->speed > 1000)) + (mem->res->start > mem->res->end) || (mem->speed > 1000)) return -EINVAL; - if ((mem->sys_start > 0xffffff) || (mem->sys_stop > 0xffffff)) + if ((mem->res->start > 0xffffff) || (mem->res->end > 0xffffff)) return -EINVAL; /* Turn off the window before changing anything */ @@ -1173,12 +1173,12 @@ static int i365_set_mem_map(u_short sock i365_bclr(sock, I365_ADDRWIN, I365_ENA_MEM(map)); base = I365_MEM(map); - i = (mem->sys_start >> 12) & 0x0fff; + i = (mem->res->start >> 12) & 0x0fff; if (mem->flags & MAP_16BIT) i |= I365_MEM_16BIT; if (mem->flags & MAP_0WS) i |= I365_MEM_0WS; i365_set_pair(sock, base+I365_W_START, i); - i = (mem->sys_stop >> 12) & 0x0fff; + i = (mem->res->end >> 12) & 0x0fff; switch (to_cycles(mem->speed)) { case 0: break; case 1: i |= I365_MEM_WS0; break; @@ -1187,7 +1187,7 @@ static int i365_set_mem_map(u_short sock } i365_set_pair(sock, base+I365_W_STOP, i); - i = ((mem->card_start - mem->sys_start) >> 12) & 0x3fff; + i = ((mem->card_start - mem->res->start) >> 12) & 0x3fff; if (mem->flags & MAP_WRPROT) i |= I365_MEM_WRPROT; if (mem->flags & MAP_ATTRIB) i |= I365_MEM_REG; i365_set_pair(sock, base+I365_W_OFF, i); @@ -1309,7 +1309,7 @@ static int pcic_init(struct pcmcia_socke int i; struct resource res = { .start = 0, .end = 0x1000 }; pccard_io_map io = { 0, 0, 0, 0, 1 }; - pccard_mem_map mem = { .res = &res, .sys_stop = 0x1000, }; + pccard_mem_map mem = { .res = &res, }; for (i = 0; i < 2; i++) { io.map = i; --- linux-2.6.9-rc1/drivers/pcmcia/pd6729.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/pcmcia/pd6729.c 2004-09-02 20:51:52.000000000 -0700 @@ -502,10 +502,10 @@ static int pd6729_set_mem_map(struct pcm return -EINVAL; } - if ((mem->sys_start > mem->sys_stop) || (mem->speed > 1000)) { + if ((mem->res->start > mem->res->end) || (mem->speed > 1000)) { printk("pd6729_set_mem_map: invalid address / speed"); /* printk("invalid mem map for socket %i : %lx to %lx with a start of %x\n", - sock, mem->sys_start, mem->sys_stop, mem->card_start); */ + sock, mem->res->start, mem->res->end, mem->card_start); */ return -EINVAL; } @@ -515,7 +515,7 @@ static int pd6729_set_mem_map(struct pcm /* write the start address */ base = I365_MEM(map); - i = (mem->sys_start >> 12) & 0x0fff; + i = (mem->res->start >> 12) & 0x0fff; if (mem->flags & MAP_16BIT) i |= I365_MEM_16BIT; if (mem->flags & MAP_0WS) @@ -524,7 +524,7 @@ static int pd6729_set_mem_map(struct pcm /* write the stop address */ - i= (mem->sys_stop >> 12) & 0x0fff; + i= (mem->res->end >> 12) & 0x0fff; switch (to_cycles(mem->speed)) { case 0: break; @@ -543,11 +543,11 @@ static int pd6729_set_mem_map(struct pcm /* Take care of high byte */ indirect_write(socket, PD67_EXT_INDEX, PD67_MEM_PAGE(map)); - indirect_write(socket, PD67_EXT_DATA, mem->sys_start >> 24); + indirect_write(socket, PD67_EXT_DATA, mem->res->start >> 24); /* card start */ - i = ((mem->card_start - mem->sys_start) >> 12) & 0x3fff; + i = ((mem->card_start - mem->res->start) >> 12) & 0x3fff; if (mem->flags & MAP_WRPROT) i |= I365_MEM_WRPROT; if (mem->flags & MAP_ATTRIB) { @@ -577,7 +577,7 @@ static int pd6729_init(struct pcmcia_soc int i; struct resource res = { .end = 0x0fff }; pccard_io_map io = { 0, 0, 0, 0, 1 }; - pccard_mem_map mem = { .res = &res, .sys_stop = 0x0fff }; + pccard_mem_map mem = { .res = &res, }; pd6729_set_socket(sock, &dead_socket); for (i = 0; i < 2; i++) { --- linux-2.6.9-rc1/drivers/pcmcia/rsrc_mgr.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/pcmcia/rsrc_mgr.c 2004-09-03 00:56:48.375806408 -0700 @@ -301,8 +301,6 @@ static int readable(struct pcmcia_socket { int ret = -1; - s->cis_mem.sys_start = res->start; - s->cis_mem.sys_stop = res->end; s->cis_mem.res = res; s->cis_virt = ioremap(res->start, s->map_size); if (s->cis_virt) { @@ -312,8 +310,6 @@ static int readable(struct pcmcia_socket s->cis_virt = NULL; destroy_cis_cache(s); } - s->cis_mem.sys_start = 0; - s->cis_mem.sys_stop = 0; s->cis_mem.res = NULL; if ((ret != 0) || (info->Chains == 0)) return 0; @@ -332,8 +328,6 @@ static int checksum(struct pcmcia_socket map.map = 0; map.flags = MAP_ACTIVE; map.speed = 0; - map.sys_start = res->start; - map.sys_stop = res->end; map.res = res; map.card_start = 0; s->ops->set_mem_map(s, &map); @@ -520,12 +514,8 @@ static void validate_mem(struct pcmcia_s void pcmcia_validate_mem(struct pcmcia_socket *s) { - down(&s->skt_sem); - if (probe_mem && s->state & SOCKET_PRESENT) validate_mem(s); - - up(&s->skt_sem); } EXPORT_SYMBOL(pcmcia_validate_mem); --- linux-2.6.9-rc1/drivers/pcmcia/soc_common.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/pcmcia/soc_common.c 2004-09-02 20:51:52.000000000 -0700 @@ -448,9 +448,7 @@ soc_common_pcmcia_set_mem_map(struct pcm skt->ops->set_timing(skt); - map->sys_stop -= map->sys_start; - map->sys_stop += res->start + map->card_start; - map->sys_start = res->start + map->card_start; + map->static_start = res->start + map->card_start; return 0; } @@ -662,6 +660,7 @@ static void soc_pcmcia_cpufreq_unregiste int soc_common_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops, int first, int nr) { struct skt_dev_info *sinfo; + struct soc_pcmcia_socket *skt; int ret, i; down(&soc_pcmcia_sockets_lock); @@ -679,7 +678,7 @@ int soc_common_drv_pcmcia_probe(struct d * Initialise the per-socket structure. */ for (i = 0; i < nr; i++) { - struct soc_pcmcia_socket *skt = &sinfo->skt[i]; + skt = &sinfo->skt[i]; skt->socket.ops = &soc_common_pcmcia_operations; skt->socket.owner = ops->owner; @@ -777,7 +776,7 @@ int soc_common_drv_pcmcia_probe(struct d goto out; do { - struct soc_pcmcia_socket *skt = &sinfo->skt[i]; + skt = &sinfo->skt[i]; del_timer_sync(&skt->poll_timer); pcmcia_unregister_socket(&skt->socket); --- linux-2.6.9-rc1/drivers/pcmcia/tcic.c 2004-08-24 00:02:57.000000000 -0700 +++ 25/drivers/pcmcia/tcic.c 2004-09-02 20:51:52.000000000 -0700 @@ -829,15 +829,15 @@ static int tcic_set_mem_map(struct pcmci debug(1, "SetMemMap(%d, %d, %#2.2x, %d ns, " "%#5.5lx-%#5.5lx, %#5.5x)\n", psock, mem->map, mem->flags, - mem->speed, mem->sys_start, mem->sys_stop, mem->card_start); + mem->speed, mem->res->start, mem->res->end, mem->card_start); if ((mem->map > 3) || (mem->card_start > 0x3ffffff) || - (mem->sys_start > 0xffffff) || (mem->sys_stop > 0xffffff) || - (mem->sys_start > mem->sys_stop) || (mem->speed > 1000)) + (mem->res->start > 0xffffff) || (mem->res->end > 0xffffff) || + (mem->res->start > mem->res->end) || (mem->speed > 1000)) return -EINVAL; tcic_setw(TCIC_ADDR+2, TCIC_ADR2_INDREG | (psock << TCIC_SS_SHFT)); addr = TCIC_MWIN(psock, mem->map); - base = mem->sys_start; len = mem->sys_stop - mem->sys_start; + base = mem->res->start; len = mem->res->end - mem->res->start; if ((len & (len+1)) || (base & len)) return -EINVAL; if (len == 0x0fff) base = (base >> TCIC_MBASE_HA_SHFT) | TCIC_MBASE_4K_BIT; @@ -846,7 +846,7 @@ static int tcic_set_mem_map(struct pcmci tcic_setw(TCIC_ADDR, addr + TCIC_MBASE_X); tcic_setw(TCIC_DATA, base); - mmap = mem->card_start - mem->sys_start; + mmap = mem->card_start - mem->res->start; mmap = (mmap >> TCIC_MMAP_CA_SHFT) & TCIC_MMAP_CA_MASK; if (mem->flags & MAP_ATTRIB) mmap |= TCIC_MMAP_REG; tcic_setw(TCIC_ADDR, addr + TCIC_MMAP_X); @@ -870,7 +870,7 @@ static int tcic_init(struct pcmcia_socke int i; struct resource res = { .start = 0, .end = 0x1000 }; pccard_io_map io = { 0, 0, 0, 0, 1 }; - pccard_mem_map mem = { .res = &res, .sys_stop = 0x1000, }; + pccard_mem_map mem = { .res = &res, }; for (i = 0; i < 2; i++) { io.map = i; --- linux-2.6.9-rc1/drivers/pcmcia/yenta_socket.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/pcmcia/yenta_socket.c 2004-09-02 20:51:52.000000000 -0700 @@ -343,14 +343,17 @@ static int yenta_set_io_map(struct pcmci static int yenta_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *mem) { struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); + struct pci_bus_region region; int map; unsigned char addr, enable; unsigned int start, stop, card_start; unsigned short word; + pcibios_resource_to_bus(socket->dev, ®ion, mem->res); + map = mem->map; - start = mem->sys_start; - stop = mem->sys_stop; + start = region.start; + stop = region.end; card_start = mem->card_start; if (map > 4 || start > stop || ((start ^ stop) >> 24) || @@ -447,7 +450,7 @@ static void yenta_clear_maps(struct yent int i; struct resource res = { .start = 0, .end = 0x0fff }; pccard_io_map io = { 0, 0, 0, 0, 1 }; - pccard_mem_map mem = { .res = &res, .sys_stop = 0x0fff, }; + pccard_mem_map mem = { .res = &res, }; yenta_set_socket(&socket->socket, &dead_socket); for (i = 0; i < 2; i++) { --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/perfctr/cpumask.h 2004-09-03 00:56:46.046160568 -0700 @@ -0,0 +1,25 @@ +/* $Id: cpumask.h,v 1.7 2004/05/12 19:59:01 mikpe Exp $ + * Performance-monitoring counters driver. + * Partial simulation of cpumask_t on non-cpumask_t kernels. + * Extension to allow inspecting a cpumask_t as array of ulong. + * Appropriate definition of perfctr_cpus_forbidden_mask. + * + * Copyright (C) 2003-2004 Mikael Pettersson + */ + +#ifdef CPU_ARRAY_SIZE +#define PERFCTR_CPUMASK_NRLONGS CPU_ARRAY_SIZE +#else +#define PERFCTR_CPUMASK_NRLONGS 1 +#endif + +/* CPUs in `perfctr_cpus_forbidden_mask' must not use the + performance-monitoring counters. TSC use is unrestricted. + This is needed to prevent resource conflicts on hyper-threaded P4s. */ +#ifdef CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK +extern cpumask_t perfctr_cpus_forbidden_mask; +#define perfctr_cpu_is_forbidden(cpu) cpu_isset((cpu), perfctr_cpus_forbidden_mask) +#else +#define perfctr_cpus_forbidden_mask CPU_MASK_NONE +#define perfctr_cpu_is_forbidden(cpu) 0 /* cpu_isset() needs an lvalue :-( */ +#endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/perfctr/init.c 2004-09-03 00:56:46.218134424 -0700 @@ -0,0 +1,95 @@ +/* $Id: init.c,v 1.76 2004/05/31 18:18:55 mikpe Exp $ + * Performance-monitoring counters driver. + * Top-level initialisation code. + * + * Copyright (C) 1999-2004 Mikael Pettersson + */ +#include +#include +#include +#include +#include + +#include + +#include "cpumask.h" +#include "virtual.h" +#include "version.h" + +struct perfctr_info perfctr_info = { + .abi_version = PERFCTR_ABI_VERSION, + .driver_version = VERSION, +}; + +char *perfctr_cpu_name __initdata; + +static int cpus_copy_to_user(struct perfctr_cpu_mask __user *argp, const cpumask_t *cpus) +{ + const unsigned int k_nrwords = PERFCTR_CPUMASK_NRLONGS*(sizeof(long)/sizeof(int)); + unsigned int u_nrwords; + unsigned int ui, ki, j; + + if (get_user(u_nrwords, &argp->nrwords)) + return -EFAULT; + if (put_user(k_nrwords, &argp->nrwords)) + return -EFAULT; + if (u_nrwords < k_nrwords) + return -EOVERFLOW; + for(ui = 0, ki = 0; ki < PERFCTR_CPUMASK_NRLONGS; ++ki) { + unsigned long mask = cpus_addr(*cpus)[ki]; + for(j = 0; j < sizeof(long)/sizeof(int); ++j) { + if (put_user((unsigned int)mask, &argp->mask[ui])) + return -EFAULT; + ++ui; + mask = (mask >> (8*sizeof(int)-1)) >> 1; + } + } + return 0; +} + +asmlinkage long sys_perfctr_info(struct perfctr_info __user *infop, + struct perfctr_cpu_mask __user *cpusp, + struct perfctr_cpu_mask __user *forbiddenp) +{ + if (infop && copy_to_user(infop, &perfctr_info, sizeof perfctr_info)) + return -EFAULT; + if (cpusp) { + int err = cpus_copy_to_user(cpusp, &cpu_online_map); + if (err) + return err; + } + if (forbiddenp) { + int err = cpus_copy_to_user(forbiddenp, &perfctr_cpus_forbidden_mask); + if (err) + return err; + } + return 0; +} + +static int __init perfctr_init(void) +{ + int err; + + err = perfctr_cpu_init(); + if (err) { + printk(KERN_INFO "perfctr: not supported by this processor\n"); + return err; + } + err = vperfctr_init(); + if (err) + return err; + printk(KERN_INFO "perfctr: driver %s, cpu type %s at %u kHz\n", + perfctr_info.driver_version, + perfctr_cpu_name, + perfctr_info.cpu_khz); + return 0; +} + +static void __exit perfctr_exit(void) +{ + vperfctr_exit(); + perfctr_cpu_exit(); +} + +module_init(perfctr_init) +module_exit(perfctr_exit) --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/perfctr/Kconfig 2004-09-03 00:56:46.047160416 -0700 @@ -0,0 +1,63 @@ +# $Id: Kconfig,v 1.10 2004/05/24 11:00:55 mikpe Exp $ +# Performance-monitoring counters driver configuration +# + +menu "Performance-monitoring counters support" + +config PERFCTR + bool "Performance monitoring counters support" + help + This driver provides access to the performance-monitoring counter + registers available in some (but not all) modern processors. + These special-purpose registers can be programmed to count low-level + performance-related events which occur during program execution, + such as cache misses, pipeline stalls, etc. + + You can safely say Y here, even if you intend to run the kernel + on a processor without performance-monitoring counters. + + At you can find + the corresponding user-space components, as well as other + versions of this package. A mailing list is also available, at + . + +config PERFCTR_INIT_TESTS + bool "Init-time hardware tests" + depends on PERFCTR + default n + help + This option makes the driver perform additional hardware tests + during initialisation, and log their results in the kernel's + message buffer. For most supported processors, these tests simply + measure the runtime overheads of performance counter operations. + + If you have a less well-known processor (one not listed in the + etc/costs/ directory in the user-space package), you should enable + this option and email the results to the perfctr developers. + + If unsure, say N. + +config PERFCTR_VIRTUAL + bool "Virtual performance counters support" + depends on PERFCTR + default y + help + The processor's performance-monitoring counters are special-purpose + global registers. This option adds support for virtual per-process + performance-monitoring counters which only run when the process + to which they belong is executing. This improves the accuracy of + performance measurements by reducing "noise" from other processes. + + Say Y. + +config PERFCTR_INTERRUPT_SUPPORT + bool + depends on PERFCTR + default y if X86_LOCAL_APIC + +config PERFCTR_CPUS_FORBIDDEN_MASK + bool + depends on PERFCTR + default y if X86 && SMP + +endmenu --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/perfctr/Makefile 2004-09-03 00:56:44.653372304 -0700 @@ -0,0 +1,16 @@ +# $Id: Makefile,v 1.26 2004/05/30 23:02:14 mikpe Exp $ +# Makefile for the Performance-monitoring counters driver. + +# This also covers x86_64. +perfctr-objs-$(CONFIG_X86) := x86.o +tests-objs-$(CONFIG_X86) := x86_tests.o + +perfctr-objs-$(CONFIG_PPC32) := ppc.o +tests-objs-$(CONFIG_PPC32) := ppc_tests.o + +perfctr-objs-y += init.o +perfctr-objs-$(CONFIG_PERFCTR_INIT_TESTS) += $(tests-objs-y) +perfctr-objs-$(CONFIG_PERFCTR_VIRTUAL) += virtual.o + +perfctr-objs := $(perfctr-objs-y) +obj-$(CONFIG_PERFCTR) := perfctr.o --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/perfctr/ppc.c 2004-09-03 00:56:46.048160264 -0700 @@ -0,0 +1,926 @@ +/* $Id: ppc.c,v 1.12 2004/05/31 18:13:42 mikpe Exp $ + * PPC32 performance-monitoring counters driver. + * + * Copyright (C) 2004 Mikael Pettersson + */ +#include +#include +#include +#include +#include +#include +#include /* tb_ticks_per_jiffy, get_tbl() */ + +#include "ppc_tests.h" + +/* Support for lazy evntsel and perfctr SPR updates. */ +struct per_cpu_cache { /* roughly a subset of perfctr_cpu_state */ + union { + unsigned int id; /* cache owner id */ + } k1; + /* Physically indexed cache of the MMCRs. */ + unsigned int ppc_mmcr[3]; +} ____cacheline_aligned; +static DEFINE_PER_CPU(struct per_cpu_cache, per_cpu_cache); +#define get_cpu_cache() (&__get_cpu_var(per_cpu_cache)) + +/* Structure for counter snapshots, as 32-bit values. */ +struct perfctr_low_ctrs { + unsigned int tsc; + unsigned int pmc[6]; +}; + +enum pm_type { + PM_NONE, + PM_604, + PM_604e, + PM_750, /* XXX: Minor event set diffs between IBM and Moto. */ + PM_7400, + PM_7450, +}; +static enum pm_type pm_type; + +/* Bits users shouldn't set in control.ppc.mmcr0: + * - PMXE because we don't yet support overflow interrupts + * - PMC1SEL/PMC2SEL because event selectors are in control.evntsel[] + */ +#define MMCR0_RESERVED (MMCR0_PMXE | MMCR0_PMC1SEL | MMCR0_PMC2SEL) + +static unsigned int new_id(void) +{ + static spinlock_t lock = SPIN_LOCK_UNLOCKED; + static unsigned int counter; + int id; + + spin_lock(&lock); + id = ++counter; + spin_unlock(&lock); + return id; +} + +#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT +static void perfctr_default_ihandler(unsigned long pc) +{ +} + +static perfctr_ihandler_t perfctr_ihandler = perfctr_default_ihandler; + +void do_perfctr_interrupt(struct pt_regs *regs) +{ + preempt_disable(); + (*perfctr_ihandler)(regs->nip); + preempt_enable_no_resched(); +} + +void perfctr_cpu_set_ihandler(perfctr_ihandler_t ihandler) +{ + perfctr_ihandler = ihandler ? ihandler : perfctr_default_ihandler; +} + +#else +#define perfctr_cstatus_has_ictrs(cstatus) 0 +#endif + +#if defined(CONFIG_SMP) && defined(CONFIG_PERFCTR_INTERRUPT_SUPPORT) + +static inline void +set_isuspend_cpu(struct perfctr_cpu_state *state, int cpu) +{ + state->k1.isuspend_cpu = cpu; +} + +static inline int +is_isuspend_cpu(const struct perfctr_cpu_state *state, int cpu) +{ + return state->k1.isuspend_cpu == cpu; +} + +static inline void clear_isuspend_cpu(struct perfctr_cpu_state *state) +{ + state->k1.isuspend_cpu = NR_CPUS; +} + +#else +static inline void set_isuspend_cpu(struct perfctr_cpu_state *state, int cpu) { } +static inline int is_isuspend_cpu(const struct perfctr_cpu_state *state, int cpu) { return 1; } +static inline void clear_isuspend_cpu(struct perfctr_cpu_state *state) { } +#endif + +/**************************************************************** + * * + * Driver procedures. * + * * + ****************************************************************/ + +/* + * The PowerPC 604/750/74xx family. + * + * Common features + * --------------- + * - Per counter event selection data in subfields of control registers. + * MMCR0 contains both global control and PMC1/PMC2 event selectors. + * - Overflow interrupt support is present in all processors, but an + * erratum makes it difficult to use in 750/7400/7410 processors. + * - There is no concept of per-counter qualifiers: + * - User-mode/supervisor-mode restrictions are global. + * - Two groups of counters, PMC1 and PMC2-PMC. Each group + * has a single overflow interrupt/event enable/disable flag. + * - The instructions used to read (mfspr) and write (mtspr) the control + * and counter registers (SPRs) only support hardcoded register numbers. + * There is no support for accessing an SPR via a runtime value. + * - Each counter supports its own unique set of events. However, events + * 0-1 are common for PMC1-PMC4, and events 2-4 are common for PMC1-PMC4. + * - There is no separate high-resolution core clock counter. + * The time-base counter is available, but it typically runs an order of + * magnitude slower than the core clock. + * Any performance counter can be programmed to count core clocks, but + * doing this (a) reserves one PMC, and (b) needs indirect accesses + * since the SPR number in general isn't known at compile-time. + * + * Driver notes + * ------------ + * - The driver currently does not support performance monitor interrupts, + * mostly because of the 750/7400/7410 erratum. Working around it would + * require disabling the decrementer interrupt, reserving a performance + * counter and setting it up for TBL bit-flip events, and having the PMI + * handler invoke the decrementer handler. + * + * 604 + * --- + * 604 has MMCR0, PMC1, PMC2, SIA, and SDA. + * + * MMCR0[THRESHOLD] is not automatically multiplied. + * + * On the 604, software must always reset MMCR0[ENINT] after + * taking a PMI. This is not the case for the 604e. + * + * 604e + * ---- + * 604e adds MMCR1, PMC3, and PMC4. + * Bus-to-core multiplier is available via HID1[PLL_CFG]. + * + * MMCR0[THRESHOLD] is automatically multiplied by 4. + * + * When the 604e vectors to the PMI handler, it automatically + * clears any pending PMIs. Unlike the 604, the 604e does not + * require MMCR0[ENINT] to be cleared (and possibly reset) + * before external interrupts can be re-enabled. + * + * 750 + * --- + * 750 adds user-readable MMCRn/PMCn/SIA registers, and removes SDA. + * + * MMCR0[THRESHOLD] is not automatically multiplied. + * + * Motorola MPC750UM.pdf, page C-78, states: "The performance monitor + * of the MPC755 functions the same as that of the MPC750, (...), except + * that for both the MPC750 and MPC755, no combination of the thermal + * assist unit, the decrementer register, and the performance monitor + * can be used at any one time. If exceptions for any two of these + * functional blocks are enabled together, multiple exceptions caused + * by any of these three blocks cause unpredictable results." + * + * IBM 750CXe_Err_DD2X.pdf, Erratum #13, states that a PMI which + * occurs immediately after a delayed decrementer exception can + * corrupt SRR0, causing the processor to hang. It also states that + * PMIs via TB bit transitions can be used to simulate the decrementer. + * + * 750FX adds dual-PLL support and programmable core frequency switching. + * + * 74xx + * ---- + * 7400 adds MMCR2 and BAMR. + * + * MMCR0[THRESHOLD] is multiplied by 2 or 32, as specified + * by MMCR2[THRESHMULT]. + * + * 74xx changes the semantics of several MMCR0 control bits, + * compared to 604/750. + * + * PPC7410 Erratum No. 10: Like the MPC750 TAU/DECR/PMI erratum. + * Erratum No. 14 marks TAU as unsupported in 7410, but this leaves + * perfmon and decrementer interrupts as being mutually exclusive. + * Affects PPC7410 1.0-1.2 (PVR 0x800C1100-0x800C1102). 1.3 and up + * (PVR 0x800C1103 up) are Ok. + * + * 7450 adds PMC5 and PMC6. + * + * 7455/7445 V3.3 (PVR 80010303) and later use the 7457 PLL table, + * earlier revisions use the 7450 PLL table + */ + +static inline unsigned int read_pmc(unsigned int pmc) +{ + switch (pmc) { + default: /* impossible, but silences gcc warning */ + case 0: + return mfspr(SPRN_PMC1); + case 1: + return mfspr(SPRN_PMC2); + case 2: + return mfspr(SPRN_PMC3); + case 3: + return mfspr(SPRN_PMC4); + case 4: + return mfspr(SPRN_PMC5); + case 5: + return mfspr(SPRN_PMC6); + } +} + +static void ppc_read_counters(/*const*/ struct perfctr_cpu_state *state, + struct perfctr_low_ctrs *ctrs) +{ + unsigned int cstatus, nrctrs, i; + + cstatus = state->cstatus; + if (perfctr_cstatus_has_tsc(cstatus)) + ctrs->tsc = get_tbl(); + nrctrs = perfctr_cstatus_nractrs(cstatus); + for(i = 0; i < nrctrs; ++i) { + unsigned int pmc = state->pmc[i].map; + ctrs->pmc[i] = read_pmc(pmc); + } + /* handle MMCR0 changes due to FCECE or TRIGGER on 74xx */ + if (state->cstatus & (1<<30)) { + unsigned int mmcr0 = mfspr(SPRN_MMCR0); + state->ppc_mmcr[0] = mmcr0; + get_cpu_cache()->ppc_mmcr[0] = mmcr0; + } +} + +static unsigned int pmc_max_event(unsigned int pmc) +{ + switch (pmc) { + default: /* impossible, but silences gcc warning */ + case 0: + return 127; + case 1: + return 63; + case 2: + return 31; + case 3: + return 31; + case 4: + return 31; + case 5: + return 63; + } +} + +static unsigned int get_nr_pmcs(void) +{ + switch (pm_type) { + case PM_7450: + return 6; + case PM_7400: + case PM_750: + case PM_604e: + return 4; + case PM_604: + return 2; + default: /* PM_NONE, but silences gcc warning */ + return 0; + } +} + +static int ppc_check_control(struct perfctr_cpu_state *state) +{ + unsigned int i, nrctrs, pmc_mask, pmc; + unsigned int nr_pmcs, evntsel[6]; + + nr_pmcs = get_nr_pmcs(); + nrctrs = state->control.nractrs; + if (state->control.nrictrs || nrctrs > nr_pmcs) + return -EINVAL; + + pmc_mask = 0; + memset(evntsel, 0, sizeof evntsel); + for(i = 0; i < nrctrs; ++i) { + pmc = state->control.pmc_map[i]; + state->pmc[i].map = pmc; + if (pmc >= nr_pmcs || (pmc_mask & (1<control.evntsel[i]; + if (evntsel[pmc] > pmc_max_event(pmc)) + return -EINVAL; + } + + switch (pm_type) { + case PM_7450: + case PM_7400: + if (state->control.ppc.mmcr2 & MMCR2_RESERVED) + return -EINVAL; + state->ppc_mmcr[2] = state->control.ppc.mmcr2; + break; + default: + if (state->control.ppc.mmcr2) + return -EINVAL; + state->ppc_mmcr[2] = 0; + } + + if (state->control.ppc.mmcr0 & MMCR0_RESERVED) + return -EINVAL; + state->ppc_mmcr[0] = (state->control.ppc.mmcr0 + | (evntsel[0] << (31-25)) + | (evntsel[1] << (31-31))); + + state->ppc_mmcr[1] = (( evntsel[2] << (31-4)) + | (evntsel[3] << (31-9)) + | (evntsel[4] << (31-14)) + | (evntsel[5] << (31-20))); + + state->k1.id = new_id(); + + /* + * MMCR0[FC] and MMCR0[TRIGGER] may change on 74xx if FCECE or + * TRIGGER is set. To avoid undoing those changes, we must read + * MMCR0 back into state->ppc_mmcr[0] and the cache at suspends. + */ + switch (pm_type) { + case PM_7450: + case PM_7400: + if (state->ppc_mmcr[0] & (MMCR0_FCECE | MMCR0_TRIGGER)) + state->cstatus |= (1<<30); + default: + ; + } + + return 0; +} + +#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT +static void ppc_isuspend(struct perfctr_cpu_state *state) +{ + // XXX +} + +static void ppc_iresume(const struct perfctr_cpu_state *state) +{ + // XXX +} +#endif + +static void ppc_write_control(const struct perfctr_cpu_state *state) +{ + struct per_cpu_cache *cache; + unsigned int value; + + cache = get_cpu_cache(); + if (cache->k1.id == state->k1.id) + return; + /* + * Order matters here: update threshmult and event + * selectors before updating global control, which + * potentially enables PMIs. + * + * Since mtspr doesn't accept a runtime value for the + * SPR number, unroll the loop so each mtspr targets + * a constant SPR. + * + * For processors without MMCR2, we ensure that the + * cache and the state indicate the same value for it, + * preventing any actual mtspr to it. Ditto for MMCR1. + */ + value = state->ppc_mmcr[2]; + if (value != cache->ppc_mmcr[2]) { + cache->ppc_mmcr[2] = value; + mtspr(SPRN_MMCR2, value); + } + value = state->ppc_mmcr[1]; + if (value != cache->ppc_mmcr[1]) { + cache->ppc_mmcr[1] = value; + mtspr(SPRN_MMCR1, value); + } + value = state->ppc_mmcr[0]; + if (value != cache->ppc_mmcr[0]) { + cache->ppc_mmcr[0] = value; + mtspr(SPRN_MMCR0, value); + } + cache->k1.id = state->k1.id; +} + +static void ppc_clear_counters(void) +{ + switch (pm_type) { + case PM_7450: + case PM_7400: + mtspr(SPRN_MMCR2, 0); + mtspr(SPRN_BAMR, 0); + case PM_750: + case PM_604e: + mtspr(SPRN_MMCR1, 0); + case PM_604: + mtspr(SPRN_MMCR0, 0); + case PM_NONE: + ; + } + switch (pm_type) { + case PM_7450: + mtspr(SPRN_PMC6, 0); + mtspr(SPRN_PMC5, 0); + case PM_7400: + case PM_750: + case PM_604e: + mtspr(SPRN_PMC4, 0); + mtspr(SPRN_PMC3, 0); + case PM_604: + mtspr(SPRN_PMC2, 0); + mtspr(SPRN_PMC1, 0); + case PM_NONE: + ; + } +} + +/* + * Driver methods, internal and exported. + */ + +static void perfctr_cpu_write_control(const struct perfctr_cpu_state *state) +{ + return ppc_write_control(state); +} + +static void perfctr_cpu_read_counters(/*const*/ struct perfctr_cpu_state *state, + struct perfctr_low_ctrs *ctrs) +{ + return ppc_read_counters(state, ctrs); +} + +#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT +static void perfctr_cpu_isuspend(struct perfctr_cpu_state *state) +{ + return ppc_isuspend(state); +} + +static void perfctr_cpu_iresume(const struct perfctr_cpu_state *state) +{ + return ppc_iresume(state); +} + +/* Call perfctr_cpu_ireload() just before perfctr_cpu_resume() to + bypass internal caching and force a reload if the I-mode PMCs. */ +void perfctr_cpu_ireload(struct perfctr_cpu_state *state) +{ +#ifdef CONFIG_SMP + clear_isuspend_cpu(state); +#else + get_cpu_cache()->k1.id = 0; +#endif +} + +/* PRE: the counters have been suspended and sampled by perfctr_cpu_suspend() */ +unsigned int perfctr_cpu_identify_overflow(struct perfctr_cpu_state *state) +{ + unsigned int cstatus, nrctrs, pmc, pmc_mask; + + cstatus = state->cstatus; + pmc = perfctr_cstatus_nractrs(cstatus); + nrctrs = perfctr_cstatus_nrctrs(cstatus); + + for(pmc_mask = 0; pmc < nrctrs; ++pmc) { + if ((int)state->pmc[pmc].start < 0) { /* PPC-specific */ + /* XXX: "+=" to correct for overshots */ + state->pmc[pmc].start = state->control.ireset[pmc]; + pmc_mask |= (1 << pmc); + } + } + /* XXX: if pmc_mask == 0, then it must have been a TBL bit flip */ + /* XXX: HW cleared MMCR0[ENINT]. We presumably cleared the entire + MMCR0, so the re-enable occurs automatically later, no? */ + return pmc_mask; +} + +static inline int check_ireset(const struct perfctr_cpu_state *state) +{ + unsigned int nrctrs, i; + + i = state->control.nractrs; + nrctrs = i + state->control.nrictrs; + for(; i < nrctrs; ++i) + if (state->control.ireset[i] < 0) /* PPC-specific */ + return -EINVAL; + return 0; +} + +static inline void setup_imode_start_values(struct perfctr_cpu_state *state) +{ + unsigned int cstatus, nrctrs, i; + + cstatus = state->cstatus; + nrctrs = perfctr_cstatus_nrctrs(cstatus); + for(i = perfctr_cstatus_nractrs(cstatus); i < nrctrs; ++i) + state->pmc[i].start = state->control.ireset[i]; +} + +#else /* CONFIG_PERFCTR_INTERRUPT_SUPPORT */ +static inline void perfctr_cpu_isuspend(struct perfctr_cpu_state *state) { } +static inline void perfctr_cpu_iresume(const struct perfctr_cpu_state *state) { } +static inline int check_ireset(const struct perfctr_cpu_state *state) { return 0; } +static inline void setup_imode_start_values(struct perfctr_cpu_state *state) { } +#endif /* CONFIG_PERFCTR_INTERRUPT_SUPPORT */ + +static int check_control(struct perfctr_cpu_state *state) +{ + return ppc_check_control(state); +} + +int perfctr_cpu_update_control(struct perfctr_cpu_state *state, int is_global) +{ + int err; + + clear_isuspend_cpu(state); + state->cstatus = 0; + + /* disallow i-mode counters if we cannot catch the interrupts */ + if (!(perfctr_info.cpu_features & PERFCTR_FEATURE_PCINT) + && state->control.nrictrs) + return -EPERM; + + err = check_ireset(state); + if (err < 0) + return err; + err = check_control(state); /* may initialise state->cstatus */ + if (err < 0) + return err; + state->cstatus |= perfctr_mk_cstatus(state->control.tsc_on, + state->control.nractrs, + state->control.nrictrs); + setup_imode_start_values(state); + return 0; +} + +void perfctr_cpu_suspend(struct perfctr_cpu_state *state) +{ + unsigned int i, cstatus, nractrs; + struct perfctr_low_ctrs now; + + if (perfctr_cstatus_has_ictrs(state->cstatus)) + perfctr_cpu_isuspend(state); + perfctr_cpu_read_counters(state, &now); + cstatus = state->cstatus; + if (perfctr_cstatus_has_tsc(cstatus)) + state->tsc_sum += now.tsc - state->tsc_start; + nractrs = perfctr_cstatus_nractrs(cstatus); + for(i = 0; i < nractrs; ++i) + state->pmc[i].sum += now.pmc[i] - state->pmc[i].start; +} + +void perfctr_cpu_resume(struct perfctr_cpu_state *state) +{ + if (perfctr_cstatus_has_ictrs(state->cstatus)) + perfctr_cpu_iresume(state); + perfctr_cpu_write_control(state); + //perfctr_cpu_read_counters(state, &state->start); + { + struct perfctr_low_ctrs now; + unsigned int i, cstatus, nrctrs; + perfctr_cpu_read_counters(state, &now); + cstatus = state->cstatus; + if (perfctr_cstatus_has_tsc(cstatus)) + state->tsc_start = now.tsc; + nrctrs = perfctr_cstatus_nractrs(cstatus); + for(i = 0; i < nrctrs; ++i) + state->pmc[i].start = now.pmc[i]; + } + /* XXX: if (SMP && start.tsc == now.tsc) ++now.tsc; */ +} + +void perfctr_cpu_sample(struct perfctr_cpu_state *state) +{ + unsigned int i, cstatus, nractrs; + struct perfctr_low_ctrs now; + + perfctr_cpu_read_counters(state, &now); + cstatus = state->cstatus; + if (perfctr_cstatus_has_tsc(cstatus)) { + state->tsc_sum += now.tsc - state->tsc_start; + state->tsc_start = now.tsc; + } + nractrs = perfctr_cstatus_nractrs(cstatus); + for(i = 0; i < nractrs; ++i) { + state->pmc[i].sum += now.pmc[i] - state->pmc[i].start; + state->pmc[i].start = now.pmc[i]; + } +} + +static void perfctr_cpu_clear_counters(void) +{ + struct per_cpu_cache *cache; + + cache = get_cpu_cache(); + memset(cache, 0, sizeof *cache); + cache->k1.id = -1; + + ppc_clear_counters(); +} + +/**************************************************************** + * * + * Processor detection and initialisation procedures. * + * * + ****************************************************************/ + +/* Derive CPU core frequency from TB frequency and PLL_CFG. */ + +enum pll_type { + PLL_NONE, /* for e.g. 604 which has no HID1[PLL_CFG] */ + PLL_604e, + PLL_750, + PLL_750FX, + PLL_7400, + PLL_7450, + PLL_7457, +}; + +/* These are the known bus-to-core ratios, indexed by PLL_CFG. + Multiplied by 2 since half-multiplier steps are present. */ + +static unsigned char cfg_ratio_604e[16] __initdata = { // *2 + 2, 2, 14, 2, 4, 13, 5, 9, + 6, 11, 8, 10, 3, 12, 7, 0 +}; + +static unsigned char cfg_ratio_750[16] __initdata = { // *2 + 5, 15, 14, 2, 4, 13, 20, 9, // 0b0110 is 18 if L1_TSTCLK=0, but that is abnormal + 6, 11, 8, 10, 16, 12, 7, 0 +}; + +static unsigned char cfg_ratio_750FX[32] __initdata = { // *2 + 0, 0, 2, 2, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 22, 24, 26, + 28, 30, 32, 34, 36, 38, 40, 0 +}; + +static unsigned char cfg_ratio_7400[16] __initdata = { // *2 + 18, 15, 14, 2, 4, 13, 5, 9, + 6, 11, 8, 10, 16, 12, 7, 0 +}; + +static unsigned char cfg_ratio_7450[32] __initdata = { // *2 + 1, 0, 15, 30, 14, 0, 2, 0, + 4, 0, 13, 26, 5, 0, 9, 18, + 6, 0, 11, 22, 8, 20, 10, 24, + 16, 28, 12, 32, 7, 0, 0, 0 +}; + +static unsigned char cfg_ratio_7457[32] __initdata = { // *2 + 23, 34, 15, 30, 14, 36, 2, 40, + 4, 42, 13, 26, 17, 48, 19, 18, + 6, 21, 11, 22, 8, 20, 10, 24, + 16, 28, 12, 32, 27, 56, 0, 25 +}; + +static unsigned int __init tb_to_core_ratio(enum pll_type pll_type) +{ + unsigned char *cfg_ratio; + unsigned int shift = 28, mask = 0xF, hid1, pll_cfg, ratio; + + switch (pll_type) { + case PLL_604e: + cfg_ratio = cfg_ratio_604e; + break; + case PLL_750: + cfg_ratio = cfg_ratio_750; + break; + case PLL_750FX: + cfg_ratio = cfg_ratio_750FX; + hid1 = mfspr(SPRN_HID1); + switch ((hid1 >> 16) & 0x3) { /* HID1[PI0,PS] */ + case 0: /* PLL0 with external config */ + shift = 31-4; /* access HID1[PCE] */ + break; + case 2: /* PLL0 with internal config */ + shift = 31-20; /* access HID1[PC0] */ + break; + case 1: case 3: /* PLL1 */ + shift = 31-28; /* access HID1[PC1] */ + break; + } + mask = 0x1F; + break; + case PLL_7400: + cfg_ratio = cfg_ratio_7400; + break; + case PLL_7450: + cfg_ratio = cfg_ratio_7450; + shift = 12; + mask = 0x1F; + break; + case PLL_7457: + cfg_ratio = cfg_ratio_7457; + shift = 12; + mask = 0x1F; + break; + default: + return 0; + } + hid1 = mfspr(SPRN_HID1); + pll_cfg = (hid1 >> shift) & mask; + ratio = cfg_ratio[pll_cfg]; + if (!ratio) + printk(KERN_WARNING "perfctr: unknown PLL_CFG 0x%x\n", pll_cfg); + return (4/2) * ratio; +} + +static unsigned int __init pll_to_core_khz(enum pll_type pll_type) +{ + unsigned int tb_to_core = tb_to_core_ratio(pll_type); + perfctr_info.tsc_to_cpu_mult = tb_to_core; + return tb_ticks_per_jiffy * tb_to_core * (HZ/10) / (1000/10); +} + +/* Extract core and timebase frequencies from Open Firmware. */ + +static unsigned int __init of_to_core_khz(void) +{ + struct device_node *cpu; + unsigned int *fp, core, tb; + + cpu = find_type_devices("cpu"); + if (!cpu) + return 0; + fp = (unsigned int*)get_property(cpu, "clock-frequency", NULL); + if (!fp || !(core = *fp)) + return 0; + fp = (unsigned int*)get_property(cpu, "timebase-frequency", NULL); + if (!fp || !(tb = *fp)) + return 0; + perfctr_info.tsc_to_cpu_mult = core / tb; + return core / 1000; +} + +static unsigned int __init detect_cpu_khz(enum pll_type pll_type) +{ + unsigned int khz; + + khz = pll_to_core_khz(pll_type); + if (khz) + return khz; + + khz = of_to_core_khz(); + if (khz) + return khz; + + printk(KERN_WARNING "perfctr: unable to determine CPU speed\n"); + return 0; +} + +static int __init known_init(void) +{ + static char known_name[] __initdata = "PowerPC 60x/7xx/74xx"; + unsigned int features; + enum pll_type pll_type; + unsigned int pvr; + int have_mmcr1; + + features = PERFCTR_FEATURE_RDTSC | PERFCTR_FEATURE_RDPMC; + have_mmcr1 = 1; + pvr = mfspr(SPRN_PVR); + switch (PVR_VER(pvr)) { + case 0x0004: /* 604 */ + pm_type = PM_604; + pll_type = PLL_NONE; + features = PERFCTR_FEATURE_RDTSC; + have_mmcr1 = 0; + break; + case 0x0009: /* 604e; */ + case 0x000A: /* 604ev */ + pm_type = PM_604e; + pll_type = PLL_604e; + features = PERFCTR_FEATURE_RDTSC; + break; + case 0x0008: /* 750/740 */ + pm_type = PM_750; + pll_type = PLL_750; + break; + case 0x7000: case 0x7001: /* IBM750FX */ + case 0x7002: /* IBM750GX */ + pm_type = PM_750; + pll_type = PLL_750FX; + break; + case 0x000C: /* 7400 */ + pm_type = PM_7400; + pll_type = PLL_7400; + break; + case 0x800C: /* 7410 */ + pm_type = PM_7400; + pll_type = PLL_7400; + break; + case 0x8000: /* 7451/7441 */ + pm_type = PM_7450; + pll_type = PLL_7450; + break; + case 0x8001: /* 7455/7445 */ + pm_type = PM_7450; + pll_type = ((pvr & 0xFFFF) < 0x0303) ? PLL_7450 : PLL_7457; + break; + case 0x8002: /* 7457/7447 */ + pm_type = PM_7450; + pll_type = PLL_7457; + break; + default: + return -ENODEV; + } + perfctr_info.cpu_features = features; + perfctr_info.cpu_type = 0; /* user-space should inspect PVR */ + perfctr_cpu_name = known_name; + perfctr_info.cpu_khz = detect_cpu_khz(pll_type); + perfctr_ppc_init_tests(have_mmcr1); + return 0; +} + +static int __init unknown_init(void) +{ + static char unknown_name[] __initdata = "Generic PowerPC with TB"; + unsigned int khz; + + khz = detect_cpu_khz(PLL_NONE); + if (!khz) + return -ENODEV; + perfctr_info.cpu_features = PERFCTR_FEATURE_RDTSC; + perfctr_info.cpu_type = 0; + perfctr_cpu_name = unknown_name; + perfctr_info.cpu_khz = khz; + pm_type = PM_NONE; + return 0; +} + +static void perfctr_cpu_clear_one(void *ignore) +{ + /* PREEMPT note: when called via on_each_cpu(), + this is in IRQ context with preemption disabled. */ + perfctr_cpu_clear_counters(); +} + +static void perfctr_cpu_reset(void) +{ + on_each_cpu(perfctr_cpu_clear_one, NULL, 1, 1); + perfctr_cpu_set_ihandler(NULL); +} + +static int init_done; + +int __init perfctr_cpu_init(void) +{ + int err; + + perfctr_info.cpu_features = 0; + + err = known_init(); + if (err) { + err = unknown_init(); + if (err) + goto out; + } + + perfctr_cpu_reset(); + init_done = 1; + out: + return err; +} + +void __exit perfctr_cpu_exit(void) +{ + perfctr_cpu_reset(); +} + +/**************************************************************** + * * + * Hardware reservation. * + * * + ****************************************************************/ + +static DECLARE_MUTEX(mutex); +static const char *current_service = 0; + +const char *perfctr_cpu_reserve(const char *service) +{ + const char *ret; + + if (!init_done) + return "unsupported hardware"; + down(&mutex); + ret = current_service; + if (!ret) + current_service = service; + up(&mutex); + return ret; +} + +void perfctr_cpu_release(const char *service) +{ + down(&mutex); + if (service != current_service) { + printk(KERN_ERR "%s: attempt by %s to release while reserved by %s\n", + __FUNCTION__, service, current_service); + } else { + /* power down the counters */ + perfctr_cpu_reset(); + current_service = 0; + } + up(&mutex); +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/perfctr/ppc_tests.c 2004-09-03 00:56:45.410257240 -0700 @@ -0,0 +1,288 @@ +/* $Id: ppc_tests.c,v 1.4 2004/05/21 16:57:53 mikpe Exp $ + * Performance-monitoring counters driver. + * Optional PPC32-specific init-time tests. + * + * Copyright (C) 2004 Mikael Pettersson + */ +#include +#include +#include +#include +#include +#include +#include /* for tb_ticks_per_jiffy */ +#include "ppc_tests.h" + +#define NITER 256 +#define X2(S) S"; "S +#define X8(S) X2(X2(X2(S))) + +static void __init do_read_tbl(unsigned int unused) +{ + unsigned int i, dummy; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8("mftbl %0") : "=r"(dummy)); +} + +static void __init do_read_pmc1(unsigned int unused) +{ + unsigned int i, dummy; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8("mfspr %0," __stringify(SPRN_PMC1)) : "=r"(dummy)); +} + +static void __init do_read_pmc2(unsigned int unused) +{ + unsigned int i, dummy; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8("mfspr %0," __stringify(SPRN_PMC2)) : "=r"(dummy)); +} + +static void __init do_read_pmc3(unsigned int unused) +{ + unsigned int i, dummy; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8("mfspr %0," __stringify(SPRN_PMC3)) : "=r"(dummy)); +} + +static void __init do_read_pmc4(unsigned int unused) +{ + unsigned int i, dummy; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8("mfspr %0," __stringify(SPRN_PMC4)) : "=r"(dummy)); +} + +static void __init do_read_mmcr0(unsigned int unused) +{ + unsigned int i, dummy; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8("mfspr %0," __stringify(SPRN_MMCR0)) : "=r"(dummy)); +} + +static void __init do_read_mmcr1(unsigned int unused) +{ + unsigned int i, dummy; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8("mfspr %0," __stringify(SPRN_MMCR1)) : "=r"(dummy)); +} + +static void __init do_write_pmc2(unsigned int arg) +{ + unsigned int i; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8("mtspr " __stringify(SPRN_PMC2) ",%0") : : "r"(arg)); +} + +static void __init do_write_pmc3(unsigned int arg) +{ + unsigned int i; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8("mtspr " __stringify(SPRN_PMC3) ",%0") : : "r"(arg)); +} + +static void __init do_write_pmc4(unsigned int arg) +{ + unsigned int i; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8("mtspr " __stringify(SPRN_PMC4) ",%0") : : "r"(arg)); +} + +static void __init do_write_mmcr1(unsigned int arg) +{ + unsigned int i; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8("mtspr " __stringify(SPRN_MMCR1) ",%0") : : "r"(arg)); +} + +static void __init do_write_mmcr0(unsigned int arg) +{ + unsigned int i; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8("mtspr " __stringify(SPRN_MMCR0) ",%0") : : "r"(arg)); +} + +static void __init do_empty_loop(unsigned int unused) +{ + unsigned i; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__("" : : ); +} + +static unsigned __init run(void (*doit)(unsigned int), unsigned int arg) +{ + unsigned int start, stop; + start = mfspr(SPRN_PMC1); + (*doit)(arg); /* should take < 2^32 cycles to complete */ + stop = mfspr(SPRN_PMC1); + return stop - start; +} + +static void __init init_tests_message(void) +{ + unsigned int pvr = mfspr(SPRN_PVR); + printk(KERN_INFO "Please email the following PERFCTR INIT lines " + "to mikpe@csd.uu.se\n" + KERN_INFO "To remove this message, rebuild the driver " + "with CONFIG_PERFCTR_INIT_TESTS=n\n"); + printk(KERN_INFO "PERFCTR INIT: PVR 0x%08x, CPU clock %u kHz, TB clock %u kHz\n", + pvr, + perfctr_info.cpu_khz, + tb_ticks_per_jiffy*(HZ/10)/(1000/10)); +} + +static void __init clear(int have_mmcr1) +{ + mtspr(SPRN_MMCR0, 0); + mtspr(SPRN_PMC1, 0); + mtspr(SPRN_PMC2, 0); + if (have_mmcr1) { + mtspr(SPRN_MMCR1, 0); + mtspr(SPRN_PMC3, 0); + mtspr(SPRN_PMC4, 0); + } +} + +static void __init check_fcece(unsigned int pmc1ce) +{ + unsigned int mmcr0; + + /* + * This test checks if MMCR0[FC] is set after PMC1 overflows + * when MMCR0[FCECE] is set. + * 74xx documentation states this behaviour, while documentation + * for 604/750 processors doesn't mention this at all. + * + * Also output the value of PMC1 shortly after the overflow. + * This tells us if PMC1 really was frozen. On 604/750, it may not + * freeze since we don't enable PMIs. [No freeze confirmed on 750.] + * + * When pmc1ce == 0, MMCR0[PMC1CE] is zero. It's unclear whether + * this masks all PMC1 overflow events or just PMC1 PMIs. + * + * PMC1 counts processor cycles, with 100 to go before overflowing. + * FCECE is set. + * PMC1CE is clear if !pmc1ce, otherwise set. + */ + mtspr(SPRN_PMC1, 0x80000000-100); + mmcr0 = (1<<(31-6)) | (0x01 << 6); + if (pmc1ce) + mmcr0 |= (1<<(31-16)); + mtspr(SPRN_MMCR0, mmcr0); + do { + do_empty_loop(0); + } while (!(mfspr(SPRN_PMC1) & 0x80000000)); + do_empty_loop(0); + printk(KERN_INFO "PERFCTR INIT: %s(%u): MMCR0[FC] is %u, PMC1 is %#x\n", + __FUNCTION__, pmc1ce, + !!(mfspr(SPRN_MMCR0) & (1<<(31-0))), mfspr(SPRN_PMC1)); + mtspr(SPRN_MMCR0, 0); + mtspr(SPRN_PMC1, 0); +} + +static void __init check_trigger(unsigned int pmc1ce) +{ + unsigned int mmcr0; + + /* + * This test checks if MMCR0[TRIGGER] is reset after PMC1 overflows. + * 74xx documentation states this behaviour, while documentation + * for 604/750 processors doesn't mention this at all. + * [No reset confirmed on 750.] + * + * Also output the values of PMC1 and PMC2 shortly after the overflow. + * PMC2 should be equal to PMC1-0x80000000. + * + * When pmc1ce == 0, MMCR0[PMC1CE] is zero. It's unclear whether + * this masks all PMC1 overflow events or just PMC1 PMIs. + * + * PMC1 counts processor cycles, with 100 to go before overflowing. + * PMC2 counts processor cycles, starting from 0. + * TRIGGER is set, so PMC2 doesn't start until PMC1 overflows. + * PMC1CE is clear if !pmc1ce, otherwise set. + */ + mtspr(SPRN_PMC2, 0); + mtspr(SPRN_PMC1, 0x80000000-100); + mmcr0 = (1<<(31-18)) | (0x01 << 6) | (0x01 << 0); + if (pmc1ce) + mmcr0 |= (1<<(31-16)); + mtspr(SPRN_MMCR0, mmcr0); + do { + do_empty_loop(0); + } while (!(mfspr(SPRN_PMC1) & 0x80000000)); + do_empty_loop(0); + printk(KERN_INFO "PERFCTR INIT: %s(%u): MMCR0[TRIGGER] is %u, PMC1 is %#x, PMC2 is %#x\n", + __FUNCTION__, pmc1ce, + !!(mfspr(SPRN_MMCR0) & (1<<(31-18))), mfspr(SPRN_PMC1), mfspr(SPRN_PMC2)); + mtspr(SPRN_MMCR0, 0); + mtspr(SPRN_PMC1, 0); + mtspr(SPRN_PMC2, 0); +} + +static void __init +measure_overheads(int have_mmcr1) +{ + int i; + unsigned int mmcr0, loop, ticks[12]; + const char *name[12]; + + clear(have_mmcr1); + + /* PMC1 = "processor cycles", + PMC2 = "completed instructions", + not disabled in any mode, + no interrupts */ + mmcr0 = (0x01 << 6) | (0x02 << 0); + mtspr(SPRN_MMCR0, mmcr0); + + name[0] = "mftbl"; + ticks[0] = run(do_read_tbl, 0); + name[1] = "mfspr (pmc1)"; + ticks[1] = run(do_read_pmc1, 0); + name[2] = "mfspr (pmc2)"; + ticks[2] = run(do_read_pmc2, 0); + name[3] = "mfspr (pmc3)"; + ticks[3] = have_mmcr1 ? run(do_read_pmc3, 0) : 0; + name[4] = "mfspr (pmc4)"; + ticks[4] = have_mmcr1 ? run(do_read_pmc4, 0) : 0; + name[5] = "mfspr (mmcr0)"; + ticks[5] = run(do_read_mmcr0, 0); + name[6] = "mfspr (mmcr1)"; + ticks[6] = have_mmcr1 ? run(do_read_mmcr1, 0) : 0; + name[7] = "mtspr (pmc2)"; + ticks[7] = run(do_write_pmc2, 0); + name[8] = "mtspr (pmc3)"; + ticks[8] = have_mmcr1 ? run(do_write_pmc3, 0) : 0; + name[9] = "mtspr (pmc4)"; + ticks[9] = have_mmcr1 ? run(do_write_pmc4, 0) : 0; + name[10] = "mtspr (mmcr1)"; + ticks[10] = have_mmcr1 ? run(do_write_mmcr1, 0) : 0; + name[11] = "mtspr (mmcr0)"; + ticks[11] = run(do_write_mmcr0, mmcr0); + + loop = run(do_empty_loop, 0); + + clear(have_mmcr1); + + init_tests_message(); + printk(KERN_INFO "PERFCTR INIT: NITER == %u\n", NITER); + printk(KERN_INFO "PERFCTR INIT: loop overhead is %u cycles\n", loop); + for(i = 0; i < ARRAY_SIZE(ticks); ++i) { + unsigned int x; + if (!ticks[i]) + continue; + x = ((ticks[i] - loop) * 10) / NITER; + printk(KERN_INFO "PERFCTR INIT: %s cost is %u.%u cycles (%u total)\n", + name[i], x/10, x%10, ticks[i]); + } + check_fcece(0); + check_fcece(1); + check_trigger(0); + check_trigger(1); +} + +void __init perfctr_ppc_init_tests(int have_mmcr1) +{ + preempt_disable(); + measure_overheads(have_mmcr1); + preempt_enable(); +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/perfctr/ppc_tests.h 2004-09-03 00:56:45.410257240 -0700 @@ -0,0 +1,12 @@ +/* $Id: ppc_tests.h,v 1.1 2004/01/12 01:59:11 mikpe Exp $ + * Performance-monitoring counters driver. + * Optional PPC32-specific init-time tests. + * + * Copyright (C) 2004 Mikael Pettersson + */ + +#ifdef CONFIG_PERFCTR_INIT_TESTS +extern void perfctr_ppc_init_tests(int have_mmcr1); +#else +static inline void perfctr_ppc_init_tests(int have_mmcr1) { } +#endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/perfctr/version.h 2004-09-03 00:56:46.924027112 -0700 @@ -0,0 +1 @@ +#define VERSION "2.7.5" --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/perfctr/virtual.c 2004-09-03 00:56:46.925026960 -0700 @@ -0,0 +1,1109 @@ +/* $Id: virtual.c,v 1.104 2004/08/09 09:42:22 mikpe Exp $ + * Virtual per-process performance counters. + * + * Copyright (C) 1999-2004 Mikael Pettersson + */ +#include +#include +#include /* for unlikely() in 2.4.18 and older */ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "cpumask.h" +#include "virtual.h" + +/**************************************************************** + * * + * Data types and macros. * + * * + ****************************************************************/ + +struct vperfctr { +/* User-visible fields: (must be first for mmap()) */ + struct perfctr_cpu_state cpu_state; +/* Kernel-private fields: */ + int si_signo; + atomic_t count; + spinlock_t owner_lock; + struct task_struct *owner; + /* sampling_timer and bad_cpus_allowed are frequently + accessed, so they get to share a cache line */ + unsigned int sampling_timer ____cacheline_aligned; +#ifdef CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK + atomic_t bad_cpus_allowed; +#endif +#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT + unsigned int iresume_cstatus; +#endif + /* children_lock protects inheritance_id and children, + when parent is not the one doing release_task() */ + spinlock_t children_lock; + unsigned long long inheritance_id; + struct perfctr_sum_ctrs children; + /* schedule_work() data for when an operation cannot be + done in the current context due to locking rules */ + struct work_struct work; + struct task_struct *parent_tsk; +}; +#define IS_RUNNING(perfctr) perfctr_cstatus_enabled((perfctr)->cpu_state.cstatus) + +#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT + +static void vperfctr_ihandler(unsigned long pc); + +static inline void vperfctr_set_ihandler(void) +{ + perfctr_cpu_set_ihandler(vperfctr_ihandler); +} + +static inline void vperfctr_clear_iresume_cstatus(struct vperfctr *perfctr) +{ + perfctr->iresume_cstatus = 0; +} + +#else +static inline void vperfctr_set_ihandler(void) { } +static inline void vperfctr_clear_iresume_cstatus(struct vperfctr *perfctr) { } +#endif + +#ifdef CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK + +static inline void vperfctr_init_bad_cpus_allowed(struct vperfctr *perfctr) +{ + atomic_set(&perfctr->bad_cpus_allowed, 0); +} + +#else /* !CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK */ +static inline void vperfctr_init_bad_cpus_allowed(struct vperfctr *perfctr) { } +#endif /* !CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK */ + +/**************************************************************** + * * + * Resource management. * + * * + ****************************************************************/ + +/* XXX: perhaps relax this to number of _live_ perfctrs */ +static spinlock_t nrctrs_lock = SPIN_LOCK_UNLOCKED; +static int nrctrs; +static const char this_service[] = __FILE__; + +static int inc_nrctrs(void) +{ + const char *other; + + other = NULL; + spin_lock(&nrctrs_lock); + if (++nrctrs == 1) { + other = perfctr_cpu_reserve(this_service); + if (other) + nrctrs = 0; + } + spin_unlock(&nrctrs_lock); + if (other) { + printk(KERN_ERR __FILE__ + ": cannot operate, perfctr hardware taken by '%s'\n", + other); + return -EBUSY; + } + vperfctr_set_ihandler(); + return 0; +} + +static void dec_nrctrs(void) +{ + spin_lock(&nrctrs_lock); + if (--nrctrs == 0) + perfctr_cpu_release(this_service); + spin_unlock(&nrctrs_lock); +} + +/* Allocate a `struct vperfctr'. Claim and reserve + an entire page so that it can be mmap():ed. */ +static struct vperfctr *vperfctr_alloc(void) +{ + unsigned long page; + + if (inc_nrctrs() != 0) + return ERR_PTR(-EBUSY); + page = get_zeroed_page(GFP_KERNEL); + if (!page) { + dec_nrctrs(); + return ERR_PTR(-ENOMEM); + } + SetPageReserved(virt_to_page(page)); + return (struct vperfctr*) page; +} + +static void vperfctr_free(struct vperfctr *perfctr) +{ + ClearPageReserved(virt_to_page(perfctr)); + free_page((unsigned long)perfctr); + dec_nrctrs(); +} + +static struct vperfctr *get_empty_vperfctr(void) +{ + struct vperfctr *perfctr = vperfctr_alloc(); + if (!IS_ERR(perfctr)) { + atomic_set(&perfctr->count, 1); + vperfctr_init_bad_cpus_allowed(perfctr); + spin_lock_init(&perfctr->owner_lock); + spin_lock_init(&perfctr->children_lock); + } + return perfctr; +} + +static void put_vperfctr(struct vperfctr *perfctr) +{ + if (atomic_dec_and_test(&perfctr->count)) + vperfctr_free(perfctr); +} + +static void scheduled_vperfctr_free(void *perfctr) +{ + vperfctr_free((struct vperfctr*)perfctr); +} + +static void schedule_put_vperfctr(struct vperfctr *perfctr) +{ + if (!atomic_dec_and_test(&perfctr->count)) + return; + INIT_WORK(&perfctr->work, scheduled_vperfctr_free, perfctr); + schedule_work(&perfctr->work); +} + +static unsigned long long new_inheritance_id(void) +{ + static spinlock_t lock = SPIN_LOCK_UNLOCKED; + static unsigned long long counter; + unsigned long long id; + + spin_lock(&lock); + id = ++counter; + spin_unlock(&lock); + return id; +} + +/**************************************************************** + * * + * Basic counter operations. * + * These must all be called by the owner process only. * + * These must all be called with preemption disabled. * + * * + ****************************************************************/ + +/* PRE: IS_RUNNING(perfctr) + * Suspend the counters. + * XXX: When called from switch_to(), perfctr belongs to 'prev' + * but current is 'next'. + */ +static inline void vperfctr_suspend(struct vperfctr *perfctr) +{ + perfctr_cpu_suspend(&perfctr->cpu_state); +} + +static inline void vperfctr_reset_sampling_timer(struct vperfctr *perfctr) +{ + /* XXX: base the value on perfctr_info.cpu_khz instead! */ + perfctr->sampling_timer = HZ/2; +} + +/* PRE: perfctr == current->thread.perfctr && IS_RUNNING(perfctr) + * Restart the counters. + */ +static inline void vperfctr_resume(struct vperfctr *perfctr) +{ + perfctr_cpu_resume(&perfctr->cpu_state); + vperfctr_reset_sampling_timer(perfctr); +} + +/* Sample the counters but do not suspend them. */ +static void vperfctr_sample(struct vperfctr *perfctr) +{ + if (IS_RUNNING(perfctr)) { + perfctr_cpu_sample(&perfctr->cpu_state); + vperfctr_reset_sampling_timer(perfctr); + } +} + +#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT +/* vperfctr interrupt handler (XXX: add buffering support) */ +/* PREEMPT note: called in IRQ context with preemption disabled. */ +static void vperfctr_ihandler(unsigned long pc) +{ + struct task_struct *tsk = current; + struct vperfctr *perfctr; + unsigned int pmc_mask; + siginfo_t si; + + perfctr = tsk->thread.perfctr; + if (!perfctr) { + printk(KERN_ERR "%s: BUG! pid %d has no vperfctr\n", + __FUNCTION__, tsk->pid); + return; + } + if (!perfctr_cstatus_has_ictrs(perfctr->cpu_state.cstatus)) { + printk(KERN_ERR "%s: BUG! vperfctr has cstatus %#x (pid %d, comm %s)\n", + __FUNCTION__, perfctr->cpu_state.cstatus, tsk->pid, tsk->comm); + return; + } + vperfctr_suspend(perfctr); + pmc_mask = perfctr_cpu_identify_overflow(&perfctr->cpu_state); + if (!pmc_mask) { + printk(KERN_ERR "%s: BUG! pid %d has unidentifiable overflow source\n", + __FUNCTION__, tsk->pid); + return; + } + /* suspend a-mode and i-mode PMCs, leaving only TSC on */ + /* XXX: some people also want to suspend the TSC */ + perfctr->iresume_cstatus = perfctr->cpu_state.cstatus; + if (perfctr_cstatus_has_tsc(perfctr->iresume_cstatus)) { + perfctr->cpu_state.cstatus = perfctr_mk_cstatus(1, 0, 0); + vperfctr_resume(perfctr); + } else + perfctr->cpu_state.cstatus = 0; + si.si_signo = perfctr->si_signo; + si.si_errno = 0; + si.si_code = SI_PMC_OVF; + si.si_pmc_ovf_mask = pmc_mask; + if (!send_sig_info(si.si_signo, &si, tsk)) + send_sig(si.si_signo, tsk, 1); +} +#endif + +/**************************************************************** + * * + * Process management operations. * + * These must all, with the exception of vperfctr_unlink() * + * and __vperfctr_set_cpus_allowed(), be called by the owner * + * process only. * + * * + ****************************************************************/ + +/* do_fork() -> copy_process() -> copy_thread() -> __vperfctr_copy(). + * Inherit the parent's perfctr settings to the child. + * PREEMPT note: do_fork() etc do not run with preemption disabled. +*/ +void __vperfctr_copy(struct task_struct *child_tsk, struct pt_regs *regs) +{ + struct vperfctr *parent_perfctr; + struct vperfctr *child_perfctr; + + /* Do not inherit perfctr settings to kernel-generated + threads, like those created by kmod. */ + child_perfctr = NULL; + if (!user_mode(regs)) + goto out; + + /* Allocation may sleep. Do it before the critical region. */ + child_perfctr = get_empty_vperfctr(); + if (IS_ERR(child_perfctr)) { + child_perfctr = NULL; + goto out; + } + + /* Although we're executing in the parent, if it is scheduled + then a remote monitor may attach and change the perfctr + pointer or the object it points to. This may already have + occurred when we get here, so the old copy of the pointer + in the child cannot be trusted. */ + preempt_disable(); + parent_perfctr = current->thread.perfctr; + if (parent_perfctr) { + child_perfctr->cpu_state.control = parent_perfctr->cpu_state.control; + child_perfctr->si_signo = parent_perfctr->si_signo; + child_perfctr->inheritance_id = parent_perfctr->inheritance_id; + } + preempt_enable(); + if (!parent_perfctr) { + put_vperfctr(child_perfctr); + child_perfctr = NULL; + goto out; + } + (void)perfctr_cpu_update_control(&child_perfctr->cpu_state, 0); + child_perfctr->owner = child_tsk; + out: + child_tsk->thread.perfctr = child_perfctr; +} + +/* Called from exit_thread() or do_vperfctr_unlink(). + * If the counters are running, stop them and sample their final values. + * Mark the vperfctr object as dead. + * Optionally detach the vperfctr object from its owner task. + * PREEMPT note: exit_thread() does not run with preemption disabled. + */ +static void vperfctr_unlink(struct task_struct *owner, struct vperfctr *perfctr, int do_unlink) +{ + /* this synchronises with sys_vperfctr() */ + spin_lock(&perfctr->owner_lock); + perfctr->owner = NULL; + spin_unlock(&perfctr->owner_lock); + + /* perfctr suspend+detach must be atomic wrt process suspend */ + /* this also synchronises with perfctr_set_cpus_allowed() */ + task_lock(owner); + if (IS_RUNNING(perfctr) && owner == current) + vperfctr_suspend(perfctr); + if (do_unlink) + owner->thread.perfctr = NULL; + task_unlock(owner); + + perfctr->cpu_state.cstatus = 0; + vperfctr_clear_iresume_cstatus(perfctr); + if (do_unlink) + put_vperfctr(perfctr); +} + +void __vperfctr_exit(struct vperfctr *perfctr) +{ + vperfctr_unlink(current, perfctr, 0); +} + +/* release_task() -> perfctr_release_task() -> __vperfctr_release(). + * A task is being released. If it inherited its perfctr settings + * from its parent, then merge its final counts back into the parent. + * Then unlink the child's perfctr. + * PRE: caller has write_lock_irq(&tasklist_lock). + * PREEMPT note: preemption is disabled due to tasklist_lock. + * + * When current == parent_tsk, the child's counts can be merged + * into the parent's immediately. This is the common case. + * + * When current != parent_tsk, the parent must be task_lock()ed + * before its perfctr state can be accessed. task_lock() is illegal + * here due to the write_lock_irq(&tasklist_lock) in release_task(), + * so the operation is done via schedule_work(). + */ +static void do_vperfctr_release(struct vperfctr *child_perfctr, struct task_struct *parent_tsk) +{ + struct vperfctr *parent_perfctr; + unsigned int cstatus, nrctrs, i; + + parent_perfctr = parent_tsk->thread.perfctr; + if (parent_perfctr && child_perfctr) { + spin_lock(&parent_perfctr->children_lock); + if (parent_perfctr->inheritance_id == child_perfctr->inheritance_id) { + cstatus = parent_perfctr->cpu_state.cstatus; + if (perfctr_cstatus_has_tsc(cstatus)) + parent_perfctr->children.tsc += + child_perfctr->cpu_state.tsc_sum + + child_perfctr->children.tsc; + nrctrs = perfctr_cstatus_nrctrs(cstatus); + for(i = 0; i < nrctrs; ++i) + parent_perfctr->children.pmc[i] += + child_perfctr->cpu_state.pmc[i].sum + + child_perfctr->children.pmc[i]; + } + spin_unlock(&parent_perfctr->children_lock); + } + schedule_put_vperfctr(child_perfctr); +} + +static void scheduled_release(void *data) +{ + struct vperfctr *child_perfctr = data; + struct task_struct *parent_tsk = child_perfctr->parent_tsk; + + task_lock(parent_tsk); + do_vperfctr_release(child_perfctr, parent_tsk); + task_unlock(parent_tsk); + put_task_struct(parent_tsk); +} + +void __vperfctr_release(struct task_struct *child_tsk) +{ + struct task_struct *parent_tsk = child_tsk->parent; + struct vperfctr *child_perfctr = child_tsk->thread.perfctr; + + child_tsk->thread.perfctr = NULL; + if (parent_tsk == current) + do_vperfctr_release(child_perfctr, parent_tsk); + else { + get_task_struct(parent_tsk); + INIT_WORK(&child_perfctr->work, scheduled_release, child_perfctr); + child_perfctr->parent_tsk = parent_tsk; + schedule_work(&child_perfctr->work); + } +} + +/* schedule() --> switch_to() --> .. --> __vperfctr_suspend(). + * If the counters are running, suspend them. + * PREEMPT note: switch_to() runs with preemption disabled. + */ +void __vperfctr_suspend(struct vperfctr *perfctr) +{ + if (IS_RUNNING(perfctr)) + vperfctr_suspend(perfctr); +} + +/* schedule() --> switch_to() --> .. --> __vperfctr_resume(). + * PRE: perfctr == current->thread.perfctr + * If the counters are runnable, resume them. + * PREEMPT note: switch_to() runs with preemption disabled. + */ +void __vperfctr_resume(struct vperfctr *perfctr) +{ + if (IS_RUNNING(perfctr)) { +#ifdef CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK + if (unlikely(atomic_read(&perfctr->bad_cpus_allowed)) && + perfctr_cstatus_nrctrs(perfctr->cpu_state.cstatus)) { + perfctr->cpu_state.cstatus = 0; + vperfctr_clear_iresume_cstatus(perfctr); + BUG_ON(current->state != TASK_RUNNING); + send_sig(SIGILL, current, 1); + return; + } +#endif + vperfctr_resume(perfctr); + } +} + +/* Called from update_one_process() [triggered by timer interrupt]. + * PRE: perfctr == current->thread.perfctr. + * Sample the counters but do not suspend them. + * Needed to avoid precision loss due to multiple counter + * wraparounds between resume/suspend for CPU-bound processes. + * PREEMPT note: called in IRQ context with preemption disabled. + */ +void __vperfctr_sample(struct vperfctr *perfctr) +{ + if (--perfctr->sampling_timer == 0) + vperfctr_sample(perfctr); +} + +#ifdef CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK +/* Called from set_cpus_allowed(). + * PRE: current holds task_lock(owner) + * PRE: owner->thread.perfctr == perfctr + */ +void __vperfctr_set_cpus_allowed(struct task_struct *owner, + struct vperfctr *perfctr, + cpumask_t new_mask) +{ + if (cpus_intersects(new_mask, perfctr_cpus_forbidden_mask)) { + atomic_set(&perfctr->bad_cpus_allowed, 1); + if (printk_ratelimit()) + printk(KERN_WARNING "perfctr: process %d (comm %s) issued unsafe" + " set_cpus_allowed() on process %d (comm %s)\n", + current->pid, current->comm, owner->pid, owner->comm); + } else + atomic_set(&perfctr->bad_cpus_allowed, 0); +} +#endif + +/**************************************************************** + * * + * Virtual perfctr system calls implementation. * + * These can be called by the owner process (tsk == current), * + * a monitor process which has the owner under ptrace ATTACH * + * control (tsk && tsk != current), or anyone with a handle to * + * an unlinked perfctr (!tsk). * + * * + ****************************************************************/ + +static int do_vperfctr_control(struct vperfctr *perfctr, + const struct vperfctr_control __user *argp, + struct task_struct *tsk) +{ + struct vperfctr_control *control; + int err; + unsigned int next_cstatus; + unsigned int nrctrs, i; + + if (!tsk) + return -ESRCH; /* attempt to update unlinked perfctr */ + + /* The control object can be large (over 300 bytes on i386), + so kmalloc() it instead of storing it on the stack. + We must use task-private storage to prevent racing with a + monitor process attaching to us before the non-preemptible + perfctr update step. Therefore we cannot store the copy + in the perfctr object itself. */ + control = kmalloc(sizeof(*control), GFP_USER); + if (!control) + return -ENOMEM; + + err = -EFAULT; + if (copy_from_user(control, argp, sizeof *control)) + goto out_kfree; + + if (control->cpu_control.nractrs || control->cpu_control.nrictrs) { + cpumask_t old_mask, new_mask; + + old_mask = tsk->cpus_allowed; + cpus_andnot(new_mask, old_mask, perfctr_cpus_forbidden_mask); + + err = -EINVAL; + if (cpus_empty(new_mask)) + goto out_kfree; + if (!cpus_equal(new_mask, old_mask)) + set_cpus_allowed(tsk, new_mask); + } + + /* PREEMPT note: preemption is disabled over the entire + region since we're updating an active perfctr. */ + preempt_disable(); + if (IS_RUNNING(perfctr)) { + if (tsk == current) + vperfctr_suspend(perfctr); + perfctr->cpu_state.cstatus = 0; + vperfctr_clear_iresume_cstatus(perfctr); + } + perfctr->cpu_state.control = control->cpu_control; + /* remote access note: perfctr_cpu_update_control() is ok */ + err = perfctr_cpu_update_control(&perfctr->cpu_state, 0); + if (err < 0) + goto out; + next_cstatus = perfctr->cpu_state.cstatus; + if (!perfctr_cstatus_enabled(next_cstatus)) + goto out; + + /* XXX: validate si_signo? */ + perfctr->si_signo = control->si_signo; + + if (!perfctr_cstatus_has_tsc(next_cstatus)) + perfctr->cpu_state.tsc_sum = 0; + + nrctrs = perfctr_cstatus_nrctrs(next_cstatus); + for(i = 0; i < nrctrs; ++i) + if (!(control->preserve & (1<cpu_state.pmc[i].sum = 0; + + spin_lock(&perfctr->children_lock); + perfctr->inheritance_id = new_inheritance_id(); + memset(&perfctr->children, 0, sizeof perfctr->children); + spin_unlock(&perfctr->children_lock); + + if (tsk == current) + vperfctr_resume(perfctr); + + out: + preempt_enable(); + out_kfree: + kfree(control); + return err; +} + +static int do_vperfctr_iresume(struct vperfctr *perfctr, const struct task_struct *tsk) +{ +#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT + unsigned int iresume_cstatus; + + if (!tsk) + return -ESRCH; /* attempt to update unlinked perfctr */ + + iresume_cstatus = perfctr->iresume_cstatus; + if (!perfctr_cstatus_has_ictrs(iresume_cstatus)) + return -EPERM; + + /* PREEMPT note: preemption is disabled over the entire + region because we're updating an active perfctr. */ + preempt_disable(); + + if (IS_RUNNING(perfctr) && tsk == current) + vperfctr_suspend(perfctr); + + perfctr->cpu_state.cstatus = iresume_cstatus; + perfctr->iresume_cstatus = 0; + + /* remote access note: perfctr_cpu_ireload() is ok */ + perfctr_cpu_ireload(&perfctr->cpu_state); + + if (tsk == current) + vperfctr_resume(perfctr); + + preempt_enable(); + + return 0; +#else + return -ENOSYS; +#endif +} + +static int do_vperfctr_unlink(struct vperfctr *perfctr, struct task_struct *tsk) +{ + if (tsk) + vperfctr_unlink(tsk, perfctr, 1); + return 0; +} + +static int do_vperfctr_read(struct vperfctr *perfctr, + struct perfctr_sum_ctrs __user *sump, + struct vperfctr_control __user *controlp, + struct perfctr_sum_ctrs __user *childrenp, + struct task_struct *tsk) +{ + struct { + struct perfctr_sum_ctrs sum; + struct vperfctr_control control; + struct perfctr_sum_ctrs children; + } *tmp; + int ret; + + /* The state snapshot can be large (more than 600 bytes on i386), + so kmalloc() it instead of storing it on the stack. + We must use task-private storage to prevent racing with a + monitor process attaching to us during the preemptible + copy_to_user() step. Therefore we cannot store the snapshot + in the perfctr object itself. */ + tmp = kmalloc(sizeof(*tmp), GFP_USER); + if (!tmp) + return -ENOMEM; + + /* PREEMPT note: While we're reading our own control, another + process may ptrace ATTACH to us and update our control. + Disable preemption to ensure we get a consistent copy. + Not needed for other cases since the perfctr is either + unlinked or its owner is ptrace ATTACH suspended by us. */ + if (tsk == current) { + preempt_disable(); + if (sump) + vperfctr_sample(perfctr); + } + if (sump) { //sum = perfctr->cpu_state.sum; + int j; + tmp->sum.tsc = perfctr->cpu_state.tsc_sum; + for(j = 0; j < ARRAY_SIZE(tmp->sum.pmc); ++j) + tmp->sum.pmc[j] = perfctr->cpu_state.pmc[j].sum; + } + if (controlp) { + tmp->control.si_signo = perfctr->si_signo; + tmp->control.cpu_control = perfctr->cpu_state.control; + tmp->control.preserve = 0; + } + if (childrenp) { + if (tsk) + spin_lock(&perfctr->children_lock); + tmp->children = perfctr->children; + if (tsk) + spin_unlock(&perfctr->children_lock); + } + if (tsk == current) + preempt_enable(); + ret = -EFAULT; + if (sump && copy_to_user(sump, &tmp->sum, sizeof tmp->sum)) + goto out; + if (controlp && copy_to_user(controlp, &tmp->control, sizeof tmp->control)) + goto out; + if (childrenp && copy_to_user(childrenp, &tmp->children, sizeof tmp->children)) + goto out; + ret = 0; + out: + kfree(tmp); + return ret; +} + +/**************************************************************** + * * + * Virtual perfctr file operations. * + * * + ****************************************************************/ + +static int vperfctr_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct vperfctr *perfctr; + + /* Only allow read-only mapping of first page. */ + if ((vma->vm_end - vma->vm_start) != PAGE_SIZE || + vma->vm_pgoff != 0 || + (pgprot_val(vma->vm_page_prot) & _PAGE_RW) || + (vma->vm_flags & (VM_WRITE | VM_MAYWRITE))) + return -EPERM; + perfctr = filp->private_data; + if (!perfctr) + return -EPERM; + return remap_page_range(vma, vma->vm_start, virt_to_phys(perfctr), + PAGE_SIZE, vma->vm_page_prot); +} + +static int vperfctr_release(struct inode *inode, struct file *filp) +{ + struct vperfctr *perfctr = filp->private_data; + filp->private_data = NULL; + if (perfctr) + put_vperfctr(perfctr); + return 0; +} + +static struct file_operations vperfctr_file_ops = { + .mmap = vperfctr_mmap, + .release = vperfctr_release, +}; + +/**************************************************************** + * * + * File system for virtual perfctrs. Based on pipefs. * + * * + ****************************************************************/ + +#define VPERFCTRFS_MAGIC (('V'<<24)|('P'<<16)|('M'<<8)|('C')) + +/* The code to set up a `struct file_system_type' for a pseudo fs + is unfortunately not the same in 2.4 and 2.6. */ +#include /* needed for 2.6, included by fs.h in 2.4 */ + +static struct super_block * +vperfctrfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) +{ + return get_sb_pseudo(fs_type, "vperfctr:", NULL, VPERFCTRFS_MAGIC); +} + +static struct file_system_type vperfctrfs_type = { + .name = "vperfctrfs", + .get_sb = vperfctrfs_get_sb, + .kill_sb = kill_anon_super, +}; + +/* XXX: check if s/vperfctr_mnt/vperfctrfs_type.kern_mnt/ would work */ +static struct vfsmount *vperfctr_mnt; +#define vperfctr_fs_init_done() (vperfctr_mnt != NULL) + +static int __init vperfctrfs_init(void) +{ + int err = register_filesystem(&vperfctrfs_type); + if (!err) { + vperfctr_mnt = kern_mount(&vperfctrfs_type); + if (!IS_ERR(vperfctr_mnt)) + return 0; + err = PTR_ERR(vperfctr_mnt); + unregister_filesystem(&vperfctrfs_type); + vperfctr_mnt = NULL; + } + return err; +} + +static void __exit vperfctrfs_exit(void) +{ + unregister_filesystem(&vperfctrfs_type); + mntput(vperfctr_mnt); +} + +static struct inode *vperfctr_get_inode(void) +{ + struct inode *inode; + + inode = new_inode(vperfctr_mnt->mnt_sb); + if (!inode) + return NULL; + inode->i_fop = &vperfctr_file_ops; + inode->i_state = I_DIRTY; + inode->i_mode = S_IFCHR | S_IRUSR | S_IWUSR; + inode->i_uid = current->fsuid; + inode->i_gid = current->fsgid; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_blksize = 0; + return inode; +} + +static int vperfctrfs_delete_dentry(struct dentry *dentry) +{ + return 1; +} + +static struct dentry_operations vperfctrfs_dentry_operations = { + .d_delete = vperfctrfs_delete_dentry, +}; + +static struct dentry *vperfctr_d_alloc_root(struct inode *inode) +{ + struct qstr this; + char name[32]; + struct dentry *dentry; + + sprintf(name, "[%lu]", inode->i_ino); + this.name = name; + this.len = strlen(name); + this.hash = inode->i_ino; /* will go */ + dentry = d_alloc(vperfctr_mnt->mnt_sb->s_root, &this); + if (dentry) { + dentry->d_op = &vperfctrfs_dentry_operations; + d_add(dentry, inode); + } + return dentry; +} + +static struct file *vperfctr_get_filp(void) +{ + struct file *filp; + struct inode *inode; + struct dentry *dentry; + + filp = get_empty_filp(); + if (!filp) + goto out; + inode = vperfctr_get_inode(); + if (!inode) + goto out_filp; + dentry = vperfctr_d_alloc_root(inode); + if (!dentry) + goto out_inode; + + filp->f_vfsmnt = mntget(vperfctr_mnt); + filp->f_dentry = dentry; + filp->f_mapping = dentry->d_inode->i_mapping; + + filp->f_pos = 0; + filp->f_flags = 0; + filp->f_op = &vperfctr_file_ops; /* fops_get() if MODULE */ + filp->f_mode = FMODE_READ; + filp->f_version = 0; + + return filp; + + out_inode: + iput(inode); + out_filp: + put_filp(filp); /* doesn't run ->release() like fput() does */ + out: + return NULL; +} + +/**************************************************************** + * * + * Virtual perfctr actual system calls. * + * * + ****************************************************************/ + +/* tid is the actual task/thread id (née pid, stored as ->pid), + pid/tgid is that 2.6 thread group id crap (stored as ->tgid) */ +asmlinkage long sys_vperfctr_open(int tid, int creat) +{ + struct file *filp; + struct task_struct *tsk; + struct vperfctr *perfctr; + int err; + int fd; + + if (!vperfctr_fs_init_done()) + return -ENODEV; + filp = vperfctr_get_filp(); + if (!filp) + return -ENOMEM; + err = fd = get_unused_fd(); + if (err < 0) + goto err_filp; + perfctr = NULL; + if (creat) { + perfctr = get_empty_vperfctr(); /* may sleep */ + if (IS_ERR(perfctr)) { + err = PTR_ERR(perfctr); + goto err_fd; + } + } + tsk = current; + if (tid != 0 && tid != tsk->pid) { /* remote? */ + read_lock(&tasklist_lock); + tsk = find_task_by_pid(tid); + if (tsk) + get_task_struct(tsk); + read_unlock(&tasklist_lock); + err = -ESRCH; + if (!tsk) + goto err_perfctr; + err = ptrace_check_attach(tsk, 0); + if (err < 0) + goto err_tsk; + } + if (creat) { + /* check+install must be atomic to prevent remote-control races */ + task_lock(tsk); + if (!tsk->thread.perfctr) { + perfctr->owner = tsk; + tsk->thread.perfctr = perfctr; + err = 0; + } else + err = -EEXIST; + task_unlock(tsk); + if (err) + goto err_tsk; + } else { + perfctr = tsk->thread.perfctr; + /* XXX: Old API needed to allow NULL perfctr here. + Do we want to keep or change that rule? */ + } + filp->private_data = perfctr; + if (perfctr) + atomic_inc(&perfctr->count); + if (tsk != current) + put_task_struct(tsk); + fd_install(fd, filp); + return fd; + err_tsk: + if (tsk != current) + put_task_struct(tsk); + err_perfctr: + if (perfctr) /* can only occur if creat != 0 */ + put_vperfctr(perfctr); + err_fd: + put_unused_fd(fd); + err_filp: + fput(filp); + return err; +} + +static struct vperfctr *fd_get_vperfctr(int fd) +{ + struct vperfctr *perfctr; + struct file *filp; + int err; + + err = -EBADF; + filp = fget(fd); + if (!filp) + goto out; + err = -EINVAL; + if (filp->f_op != &vperfctr_file_ops) + goto out_filp; + perfctr = filp->private_data; + if (!perfctr) + goto out_filp; + atomic_inc(&perfctr->count); + fput(filp); + return perfctr; + out_filp: + fput(filp); + out: + return ERR_PTR(err); +} + +static struct task_struct *vperfctr_get_tsk(struct vperfctr *perfctr) +{ + struct task_struct *tsk; + + tsk = current; + if (perfctr != current->thread.perfctr) { + /* this synchronises with vperfctr_unlink() and itself */ + spin_lock(&perfctr->owner_lock); + tsk = perfctr->owner; + if (tsk) + get_task_struct(tsk); + spin_unlock(&perfctr->owner_lock); + if (tsk) { + int ret = ptrace_check_attach(tsk, 0); + if (ret < 0) { + put_task_struct(tsk); + return ERR_PTR(ret); + } + } + } + return tsk; +} + +static void vperfctr_put_tsk(struct task_struct *tsk) +{ + if (tsk && tsk != current) + put_task_struct(tsk); +} + +asmlinkage long sys_vperfctr_control(int fd, + const struct vperfctr_control __user *control) +{ + struct vperfctr *perfctr; + struct task_struct *tsk; + int ret; + + perfctr = fd_get_vperfctr(fd); + if (IS_ERR(perfctr)) + return PTR_ERR(perfctr); + tsk = vperfctr_get_tsk(perfctr); + if (IS_ERR(tsk)) { + ret = PTR_ERR(tsk); + goto out; + } + ret = do_vperfctr_control(perfctr, control, tsk); + vperfctr_put_tsk(tsk); + out: + put_vperfctr(perfctr); + return ret; +} + +asmlinkage long sys_vperfctr_unlink(int fd) +{ + struct vperfctr *perfctr; + struct task_struct *tsk; + int ret; + + perfctr = fd_get_vperfctr(fd); + if (IS_ERR(perfctr)) + return PTR_ERR(perfctr); + tsk = vperfctr_get_tsk(perfctr); + if (IS_ERR(tsk)) { + ret = PTR_ERR(tsk); + goto out; + } + ret = do_vperfctr_unlink(perfctr, tsk); + vperfctr_put_tsk(tsk); + out: + put_vperfctr(perfctr); + return ret; +} + +asmlinkage long sys_vperfctr_iresume(int fd) +{ + struct vperfctr *perfctr; + struct task_struct *tsk; + int ret; + + perfctr = fd_get_vperfctr(fd); + if (IS_ERR(perfctr)) + return PTR_ERR(perfctr); + tsk = vperfctr_get_tsk(perfctr); + if (IS_ERR(tsk)) { + ret = PTR_ERR(tsk); + goto out; + } + ret = do_vperfctr_iresume(perfctr, tsk); + vperfctr_put_tsk(tsk); + out: + put_vperfctr(perfctr); + return ret; +} + +asmlinkage long sys_vperfctr_read(int fd, + struct perfctr_sum_ctrs __user *sum, + struct vperfctr_control __user *control, + struct perfctr_sum_ctrs __user *children) +{ + struct vperfctr *perfctr; + struct task_struct *tsk; + int ret; + + perfctr = fd_get_vperfctr(fd); + if (IS_ERR(perfctr)) + return PTR_ERR(perfctr); + tsk = vperfctr_get_tsk(perfctr); + if (IS_ERR(tsk)) { + ret = PTR_ERR(tsk); + goto out; + } + ret = do_vperfctr_read(perfctr, sum, control, children, tsk); + vperfctr_put_tsk(tsk); + out: + put_vperfctr(perfctr); + return ret; +} + +/**************************************************************** + * * + * module_init/exit * + * * + ****************************************************************/ + +int __init vperfctr_init(void) +{ + return vperfctrfs_init(); +} + +void __exit vperfctr_exit(void) +{ + vperfctrfs_exit(); +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/perfctr/virtual.h 2004-09-03 00:56:45.597228816 -0700 @@ -0,0 +1,13 @@ +/* $Id: virtual.h,v 1.13 2004/05/31 18:18:55 mikpe Exp $ + * Virtual per-process performance counters. + * + * Copyright (C) 1999-2004 Mikael Pettersson + */ + +#ifdef CONFIG_PERFCTR_VIRTUAL +extern int vperfctr_init(void); +extern void vperfctr_exit(void); +#else +static inline int vperfctr_init(void) { return 0; } +static inline void vperfctr_exit(void) { } +#endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/perfctr/x86.c 2004-09-03 00:56:46.051159808 -0700 @@ -0,0 +1,1632 @@ +/* $Id: x86.c,v 1.146 2004/08/02 17:29:23 mikpe Exp $ + * x86/x86_64 performance-monitoring counters driver. + * + * Copyright (C) 1999-2004 Mikael Pettersson + */ +#include +#include +#include +#include +#include + +#include +#undef MSR_P6_PERFCTR0 +#undef MSR_IA32_MISC_ENABLE +#include +#include +struct hw_interrupt_type; +#include +#include /* cpu_khz */ + +#include "cpumask.h" +#include "x86_tests.h" + +/* Support for lazy evntsel and perfctr MSR updates. */ +struct per_cpu_cache { /* roughly a subset of perfctr_cpu_state */ + union { + unsigned int p5_cesr; + unsigned int id; /* cache owner id */ + } k1; + struct { + /* NOTE: these caches have physical indices, not virtual */ + unsigned int evntsel[18]; + unsigned int escr[0x3E2-0x3A0]; + unsigned int pebs_enable; + unsigned int pebs_matrix_vert; + } control; +} ____cacheline_aligned; +static DEFINE_PER_CPU(struct per_cpu_cache, per_cpu_cache); +#define __get_cpu_cache(cpu) (&per_cpu(per_cpu_cache, cpu)) +#define get_cpu_cache() (&__get_cpu_var(per_cpu_cache)) + +/* Structure for counter snapshots, as 32-bit values. */ +struct perfctr_low_ctrs { + unsigned int tsc; + unsigned int pmc[18]; +}; + +/* Intel P5, Cyrix 6x86MX/MII/III, Centaur WinChip C6/2/3 */ +#define MSR_P5_CESR 0x11 +#define MSR_P5_CTR0 0x12 /* .. 0x13 */ +#define P5_CESR_CPL 0x00C0 +#define P5_CESR_RESERVED (~0x01FF) +#define MII_CESR_RESERVED (~0x05FF) +#define C6_CESR_RESERVED (~0x00FF) + +/* Intel P6, VIA C3 */ +#define MSR_P6_PERFCTR0 0xC1 /* .. 0xC2 */ +#define MSR_P6_EVNTSEL0 0x186 /* .. 0x187 */ +#define P6_EVNTSEL_ENABLE 0x00400000 +#define P6_EVNTSEL_INT 0x00100000 +#define P6_EVNTSEL_CPL 0x00030000 +#define P6_EVNTSEL_RESERVED 0x00280000 +#define VC3_EVNTSEL1_RESERVED (~0x1FF) + +/* AMD K7 */ +#define MSR_K7_EVNTSEL0 0xC0010000 /* .. 0xC0010003 */ +#define MSR_K7_PERFCTR0 0xC0010004 /* .. 0xC0010007 */ + +/* Intel P4, Intel Pentium M */ +#define MSR_IA32_MISC_ENABLE 0x1A0 +#define MSR_IA32_MISC_ENABLE_PERF_AVAIL (1<<7) /* read-only status bit */ +#define MSR_IA32_MISC_ENABLE_PEBS_UNAVAIL (1<<12) /* read-only status bit */ + +/* Intel P4 */ +#define MSR_P4_PERFCTR0 0x300 /* .. 0x311 */ +#define MSR_P4_CCCR0 0x360 /* .. 0x371 */ +#define MSR_P4_ESCR0 0x3A0 /* .. 0x3E1, with some gaps */ + +#define MSR_P4_PEBS_ENABLE 0x3F1 +#define P4_PE_REPLAY_TAG_BITS 0x00000607 +#define P4_PE_UOP_TAG 0x01000000 +#define P4_PE_RESERVED 0xFEFFF9F8 /* only allow ReplayTagging */ + +#define MSR_P4_PEBS_MATRIX_VERT 0x3F2 +#define P4_PMV_REPLAY_TAG_BITS 0x00000003 +#define P4_PMV_RESERVED 0xFFFFFFFC + +#define P4_CCCR_OVF 0x80000000 +#define P4_CCCR_CASCADE 0x40000000 +#define P4_CCCR_OVF_PMI_T1 0x08000000 +#define P4_CCCR_OVF_PMI_T0 0x04000000 +#define P4_CCCR_FORCE_OVF 0x02000000 +#define P4_CCCR_ACTIVE_THREAD 0x00030000 +#define P4_CCCR_ENABLE 0x00001000 +#define P4_CCCR_ESCR_SELECT(X) (((X) >> 13) & 0x7) +#define P4_CCCR_EXTENDED_CASCADE 0x00000800 +#define P4_CCCR_RESERVED (0x300007FF|P4_CCCR_OVF|P4_CCCR_OVF_PMI_T1) + +#define P4_ESCR_CPL_T1 0x00000003 +#define P4_ESCR_CPL_T0 0x0000000C +#define P4_ESCR_TAG_ENABLE 0x00000010 +#define P4_ESCR_RESERVED (0x80000000) + +#define P4_FAST_RDPMC 0x80000000 +#define P4_MASK_FAST_RDPMC 0x0000001F /* we only need low 5 bits */ + +/* missing from */ +#define cpu_has_msr boot_cpu_has(X86_FEATURE_MSR) + +#define rdmsr_low(msr,low) \ + __asm__ __volatile__("rdmsr" : "=a"(low) : "c"(msr) : "edx") +#define rdpmc_low(ctr,low) \ + __asm__ __volatile__("rdpmc" : "=a"(low) : "c"(ctr) : "edx") + +static void clear_msr_range(unsigned int base, unsigned int n) +{ + unsigned int i; + + for(i = 0; i < n; ++i) + wrmsr(base+i, 0, 0); +} + +static inline void set_in_cr4_local(unsigned int mask) +{ + write_cr4(read_cr4() | mask); +} + +static inline void clear_in_cr4_local(unsigned int mask) +{ + write_cr4(read_cr4() & ~mask); +} + +static unsigned int new_id(void) +{ + static spinlock_t lock = SPIN_LOCK_UNLOCKED; + static unsigned int counter; + int id; + + spin_lock(&lock); + id = ++counter; + spin_unlock(&lock); + return id; +} + +#ifdef CONFIG_X86_LOCAL_APIC +static void perfctr_default_ihandler(unsigned long pc) +{ +} + +static perfctr_ihandler_t perfctr_ihandler = perfctr_default_ihandler; + +asmlinkage void smp_perfctr_interrupt(struct pt_regs *regs) +{ + /* PREEMPT note: invoked via an interrupt gate, which + masks interrupts. We're still on the originating CPU. */ + /* XXX: recursive interrupts? delay the ACK, mask LVTPC, or queue? */ + ack_APIC_irq(); + irq_enter(); + (*perfctr_ihandler)(instruction_pointer(regs)); + irq_exit(); +} + +void perfctr_cpu_set_ihandler(perfctr_ihandler_t ihandler) +{ + perfctr_ihandler = ihandler ? ihandler : perfctr_default_ihandler; +} + +#else +#define perfctr_cstatus_has_ictrs(cstatus) 0 +#undef cpu_has_apic +#define cpu_has_apic 0 +#undef apic_write +#define apic_write(reg,vector) do{}while(0) +#endif + +#if defined(CONFIG_SMP) + +static inline void +set_isuspend_cpu(struct perfctr_cpu_state *state, int cpu) +{ + state->k1.isuspend_cpu = cpu; +} + +static inline int +is_isuspend_cpu(const struct perfctr_cpu_state *state, int cpu) +{ + return state->k1.isuspend_cpu == cpu; +} + +static inline void clear_isuspend_cpu(struct perfctr_cpu_state *state) +{ + state->k1.isuspend_cpu = NR_CPUS; +} + +#else +static inline void set_isuspend_cpu(struct perfctr_cpu_state *state, int cpu) { } +static inline int is_isuspend_cpu(const struct perfctr_cpu_state *state, int cpu) { return 1; } +static inline void clear_isuspend_cpu(struct perfctr_cpu_state *state) { } +#endif + +/**************************************************************** + * * + * Driver procedures. * + * * + ****************************************************************/ + +/* + * Intel P5 family (Pentium, family code 5). + * - One TSC and two 40-bit PMCs. + * - A single 32-bit CESR (MSR 0x11) controls both PMCs. + * CESR has two halves, each controlling one PMC. + * To keep the API reasonably clean, the user puts 16 bits of + * control data in each counter's evntsel; the driver combines + * these to a single 32-bit CESR value. + * - Overflow interrupts are not available. + * - Pentium MMX added the RDPMC instruction. RDPMC has lower + * overhead than RDMSR and it can be used in user-mode code. + * - The MMX events are not symmetric: some events are only available + * for some PMC, and some event codes denote different events + * depending on which PMCs they control. + */ + +/* shared with MII and C6 */ +static int p5_like_check_control(struct perfctr_cpu_state *state, + unsigned int reserved_bits, int is_c6) +{ + unsigned short cesr_half[2]; + unsigned int pmc, evntsel, i; + + if (state->control.nrictrs != 0 || state->control.nractrs > 2) + return -EINVAL; + cesr_half[0] = 0; + cesr_half[1] = 0; + for(i = 0; i < state->control.nractrs; ++i) { + pmc = state->control.pmc_map[i]; + state->pmc[i].map = pmc; + if (pmc > 1 || cesr_half[pmc] != 0) + return -EINVAL; + evntsel = state->control.evntsel[i]; + /* protect reserved bits */ + if ((evntsel & reserved_bits) != 0) + return -EPERM; + /* the CPL field (if defined) must be non-zero */ + if (!is_c6 && !(evntsel & P5_CESR_CPL)) + return -EINVAL; + cesr_half[pmc] = evntsel; + } + state->k1.id = (cesr_half[1] << 16) | cesr_half[0]; + return 0; +} + +static int p5_check_control(struct perfctr_cpu_state *state, int is_global) +{ + return p5_like_check_control(state, P5_CESR_RESERVED, 0); +} + +/* shared with MII but not C6 */ +static void p5_write_control(const struct perfctr_cpu_state *state) +{ + struct per_cpu_cache *cache; + unsigned int cesr; + + cesr = state->k1.id; + if (!cesr) /* no PMC is on (this test doesn't work on C6) */ + return; + cache = get_cpu_cache(); + if (cache->k1.p5_cesr != cesr) { + cache->k1.p5_cesr = cesr; + wrmsr(MSR_P5_CESR, cesr, 0); + } +} + +static void p5_read_counters(const struct perfctr_cpu_state *state, + struct perfctr_low_ctrs *ctrs) +{ + unsigned int cstatus, nrctrs, i; + + /* The P5 doesn't allocate a cache line on a write miss, so do + a dummy read to avoid a write miss here _and_ a read miss + later in our caller. */ + asm("" : : "r"(ctrs->tsc)); + + cstatus = state->cstatus; + if (perfctr_cstatus_has_tsc(cstatus)) + rdtscl(ctrs->tsc); + nrctrs = perfctr_cstatus_nractrs(cstatus); + for(i = 0; i < nrctrs; ++i) { + unsigned int pmc = state->pmc[i].map; + rdmsr_low(MSR_P5_CTR0+pmc, ctrs->pmc[i]); + } +} + +/* used by all except pre-MMX P5 */ +static void rdpmc_read_counters(const struct perfctr_cpu_state *state, + struct perfctr_low_ctrs *ctrs) +{ + unsigned int cstatus, nrctrs, i; + + cstatus = state->cstatus; + if (perfctr_cstatus_has_tsc(cstatus)) + rdtscl(ctrs->tsc); + nrctrs = perfctr_cstatus_nractrs(cstatus); + for(i = 0; i < nrctrs; ++i) { + unsigned int pmc = state->pmc[i].map; + rdpmc_low(pmc, ctrs->pmc[i]); + } +} + +/* shared with MII and C6 */ +static void p5_clear_counters(void) +{ + clear_msr_range(MSR_P5_CESR, 1+2); +} + +/* + * Cyrix 6x86/MII/III. + * - Same MSR assignments as P5 MMX. Has RDPMC and two 48-bit PMCs. + * - Event codes and CESR formatting as in the plain P5 subset. + * - Many but not all P5 MMX event codes are implemented. + * - Cyrix adds a few more event codes. The event code is widened + * to 7 bits, and Cyrix puts the high bit in CESR bit 10 + * (and CESR bit 26 for PMC1). + */ + +static int mii_check_control(struct perfctr_cpu_state *state, int is_global) +{ + return p5_like_check_control(state, MII_CESR_RESERVED, 0); +} + +/* + * Centaur WinChip C6/2/3. + * - Same MSR assignments as P5 MMX. Has RDPMC and two 40-bit PMCs. + * - CESR is formatted with two halves, like P5. However, there + * are no defined control fields for e.g. CPL selection, and + * there is no defined method for stopping the counters. + * - Only a few event codes are defined. + * - The 64-bit TSC is synthesised from the low 32 bits of the + * two PMCs, and CESR has to be set up appropriately. + * Reprogramming CESR causes RDTSC to yield invalid results. + * (The C6 may also hang in this case, due to C6 erratum I-13.) + * Therefore, using the PMCs on any of these processors requires + * that the TSC is not accessed at all: + * 1. The kernel must be configured or a TSC-less processor, i.e. + * generic 586 or less. + * 2. The "notsc" boot parameter must be passed to the kernel. + * 3. User-space libraries and code must also be configured and + * compiled for a generic 586 or less. + */ + +#if !defined(CONFIG_X86_TSC) +static int c6_check_control(struct perfctr_cpu_state *state, int is_global) +{ + if (state->control.tsc_on) + return -EINVAL; + return p5_like_check_control(state, C6_CESR_RESERVED, 1); +} + +static void c6_write_control(const struct perfctr_cpu_state *state) +{ + struct per_cpu_cache *cache; + unsigned int cesr; + + if (perfctr_cstatus_nractrs(state->cstatus) == 0) /* no PMC is on */ + return; + cache = get_cpu_cache(); + cesr = state->k1.id; + if (cache->k1.p5_cesr != cesr) { + cache->k1.p5_cesr = cesr; + wrmsr(MSR_P5_CESR, cesr, 0); + } +} +#endif + +/* + * Intel P6 family (Pentium Pro, Pentium II, and Pentium III cores, + * and Xeon and Celeron versions of Pentium II and III cores). + * - One TSC and two 40-bit PMCs. + * - One 32-bit EVNTSEL MSR for each PMC. + * - EVNTSEL0 contains a global enable/disable bit. + * That bit is reserved in EVNTSEL1. + * - Each EVNTSEL contains a CPL field. + * - Overflow interrupts are possible, but requires that the + * local APIC is available. Some Mobile P6s have no local APIC. + * - The PMCs cannot be initialised with arbitrary values, since + * wrmsr fills the high bits by sign-extending from bit 31. + * - Most events are symmetric, but a few are not. + */ + +/* shared with K7 */ +static int p6_like_check_control(struct perfctr_cpu_state *state, int is_k7) +{ + unsigned int evntsel, i, nractrs, nrctrs, pmc_mask, pmc; + + nractrs = state->control.nractrs; + nrctrs = nractrs + state->control.nrictrs; + if (nrctrs < nractrs || nrctrs > (is_k7 ? 4 : 2)) + return -EINVAL; + + pmc_mask = 0; + for(i = 0; i < nrctrs; ++i) { + pmc = state->control.pmc_map[i]; + state->pmc[i].map = pmc; + if (pmc >= (is_k7 ? 4 : 2) || (pmc_mask & (1<control.evntsel[i]; + /* protect reserved bits */ + if (evntsel & P6_EVNTSEL_RESERVED) + return -EPERM; + /* check ENable bit */ + if (is_k7) { + /* ENable bit must be set in each evntsel */ + if (!(evntsel & P6_EVNTSEL_ENABLE)) + return -EINVAL; + } else { + /* only evntsel[0] has the ENable bit */ + if (evntsel & P6_EVNTSEL_ENABLE) { + if (pmc > 0) + return -EPERM; + } else { + if (pmc == 0) + return -EINVAL; + } + } + /* the CPL field must be non-zero */ + if (!(evntsel & P6_EVNTSEL_CPL)) + return -EINVAL; + /* INT bit must be off for a-mode and on for i-mode counters */ + if (evntsel & P6_EVNTSEL_INT) { + if (i < nractrs) + return -EINVAL; + } else { + if (i >= nractrs) + return -EINVAL; + } + } + state->k1.id = new_id(); + return 0; +} + +static int p6_check_control(struct perfctr_cpu_state *state, int is_global) +{ + return p6_like_check_control(state, 0); +} + +#ifdef CONFIG_X86_LOCAL_APIC +/* PRE: perfctr_cstatus_has_ictrs(state->cstatus) != 0 */ +/* shared with K7 and P4 */ +static void p6_like_isuspend(struct perfctr_cpu_state *state, + unsigned int msr_evntsel0) +{ + struct per_cpu_cache *cache; + unsigned int cstatus, nrctrs, i; + int cpu; + + cpu = smp_processor_id(); + set_isuspend_cpu(state, cpu); /* early to limit cpu's live range */ + cache = __get_cpu_cache(cpu); + cstatus = state->cstatus; + nrctrs = perfctr_cstatus_nrctrs(cstatus); + for(i = perfctr_cstatus_nractrs(cstatus); i < nrctrs; ++i) { + unsigned int pmc_raw, pmc_idx, now; + pmc_raw = state->pmc[i].map; + /* Note: P4_MASK_FAST_RDPMC is a no-op for P6 and K7. + We don't need to make it into a parameter. */ + pmc_idx = pmc_raw & P4_MASK_FAST_RDPMC; + cache->control.evntsel[pmc_idx] = 0; + /* On P4 this intensionally also clears the CCCR.OVF flag. */ + wrmsr(msr_evntsel0+pmc_idx, 0, 0); + /* P4 erratum N17 does not apply since we read only low 32 bits. */ + rdpmc_low(pmc_raw, now); + state->pmc[i].sum += now - state->pmc[i].start; + state->pmc[i].start = now; + } + /* cache->k1.id is still == state->k1.id */ +} + +/* PRE: perfctr_cstatus_has_ictrs(state->cstatus) != 0 */ +/* shared with K7 and P4 */ +static void p6_like_iresume(const struct perfctr_cpu_state *state, + unsigned int msr_evntsel0, + unsigned int msr_perfctr0) +{ + struct per_cpu_cache *cache; + unsigned int cstatus, nrctrs, i; + int cpu; + + cpu = smp_processor_id(); + cache = __get_cpu_cache(cpu); + if (cache->k1.id == state->k1.id) { + cache->k1.id = 0; /* force reload of cleared EVNTSELs */ + if (is_isuspend_cpu(state, cpu)) + return; /* skip reload of PERFCTRs */ + } + cstatus = state->cstatus; + nrctrs = perfctr_cstatus_nrctrs(cstatus); + for(i = perfctr_cstatus_nractrs(cstatus); i < nrctrs; ++i) { + /* Note: P4_MASK_FAST_RDPMC is a no-op for P6 and K7. + We don't need to make it into a parameter. */ + unsigned int pmc = state->pmc[i].map & P4_MASK_FAST_RDPMC; + /* If the control wasn't ours we must disable the evntsels + before reinitialising the counters, to prevent unexpected + counter increments and missed overflow interrupts. */ + if (cache->control.evntsel[pmc]) { + cache->control.evntsel[pmc] = 0; + wrmsr(msr_evntsel0+pmc, 0, 0); + } + /* P4 erratum N15 does not apply since the CCCR is disabled. */ + wrmsr(msr_perfctr0+pmc, state->pmc[i].start, -1); + } + /* cache->k1.id remains != state->k1.id */ +} + +static void p6_isuspend(struct perfctr_cpu_state *state) +{ + p6_like_isuspend(state, MSR_P6_EVNTSEL0); +} + +static void p6_iresume(const struct perfctr_cpu_state *state) +{ + p6_like_iresume(state, MSR_P6_EVNTSEL0, MSR_P6_PERFCTR0); +} +#endif /* CONFIG_X86_LOCAL_APIC */ + +/* shared with K7 and VC3 */ +static void p6_like_write_control(const struct perfctr_cpu_state *state, + unsigned int msr_evntsel0) +{ + struct per_cpu_cache *cache; + unsigned int nrctrs, i; + + cache = get_cpu_cache(); + if (cache->k1.id == state->k1.id) + return; + nrctrs = perfctr_cstatus_nrctrs(state->cstatus); + for(i = 0; i < nrctrs; ++i) { + unsigned int evntsel = state->control.evntsel[i]; + unsigned int pmc = state->pmc[i].map; + if (evntsel != cache->control.evntsel[pmc]) { + cache->control.evntsel[pmc] = evntsel; + wrmsr(msr_evntsel0+pmc, evntsel, 0); + } + } + cache->k1.id = state->k1.id; +} + +/* shared with VC3, Generic*/ +static void p6_write_control(const struct perfctr_cpu_state *state) +{ + p6_like_write_control(state, MSR_P6_EVNTSEL0); +} + +static void p6_clear_counters(void) +{ + clear_msr_range(MSR_P6_EVNTSEL0, 2); + clear_msr_range(MSR_P6_PERFCTR0, 2); +} + +/* + * AMD K7 family (Athlon, Duron). + * - Somewhat similar to the Intel P6 family. + * - Four 48-bit PMCs. + * - Four 32-bit EVNTSEL MSRs with similar layout as in P6. + * - Completely different MSR assignments :-( + * - Fewer countable events defined :-( + * - The events appear to be completely symmetric. + * - The EVNTSEL MSRs are symmetric since each has its own enable bit. + * - Publicly available documentation is incomplete. + * - K7 model 1 does not have a local APIC. AMD Document #22007 + * Revision J hints that it may use debug interrupts instead. + * + * The K8 has the same hardware layout as the K7. It also has + * better documentation and a different set of available events. + */ + +static int k7_check_control(struct perfctr_cpu_state *state, int is_global) +{ + return p6_like_check_control(state, 1); +} + +#ifdef CONFIG_X86_LOCAL_APIC +static void k7_isuspend(struct perfctr_cpu_state *state) +{ + p6_like_isuspend(state, MSR_K7_EVNTSEL0); +} + +static void k7_iresume(const struct perfctr_cpu_state *state) +{ + p6_like_iresume(state, MSR_K7_EVNTSEL0, MSR_K7_PERFCTR0); +} +#endif /* CONFIG_X86_LOCAL_APIC */ + +static void k7_write_control(const struct perfctr_cpu_state *state) +{ + p6_like_write_control(state, MSR_K7_EVNTSEL0); +} + +static void k7_clear_counters(void) +{ + clear_msr_range(MSR_K7_EVNTSEL0, 4+4); +} + +/* + * VIA C3 family. + * - A Centaur design somewhat similar to the P6/Celeron. + * - PERFCTR0 is an alias for the TSC, and EVNTSEL0 is read-only. + * - PERFCTR1 is 32 bits wide. + * - EVNTSEL1 has no defined control fields, and there is no + * defined method for stopping the counter. + * - According to testing, the reserved fields in EVNTSEL1 have + * no function. We always fill them with zeroes. + * - Only a few event codes are defined. + * - No local APIC or interrupt-mode support. + * - pmc_map[0] must be 1, if nractrs == 1. + */ +static int vc3_check_control(struct perfctr_cpu_state *state, int is_global) +{ + if (state->control.nrictrs || state->control.nractrs > 1) + return -EINVAL; + if (state->control.nractrs == 1) { + if (state->control.pmc_map[0] != 1) + return -EINVAL; + state->pmc[0].map = 1; + if (state->control.evntsel[0] & VC3_EVNTSEL1_RESERVED) + return -EPERM; + state->k1.id = state->control.evntsel[0]; + } else + state->k1.id = 0; + return 0; +} + +static void vc3_clear_counters(void) +{ + /* Not documented, but seems to be default after boot. */ + wrmsr(MSR_P6_EVNTSEL0+1, 0x00070079, 0); +} + +/* + * Intel Pentium 4. + * Current implementation restrictions: + * - No DS/PEBS support. + * + * Known quirks: + * - OVF_PMI+FORCE_OVF counters must have an ireset value of -1. + * This allows the regular overflow check to also handle FORCE_OVF + * counters. Not having this restriction would lead to MAJOR + * complications in the driver's "detect overflow counters" code. + * There is no loss of functionality since the ireset value doesn't + * affect the counter's PMI rate for FORCE_OVF counters. + * - In experiments with FORCE_OVF counters, and regular OVF_PMI + * counters with small ireset values between -8 and -1, it appears + * that the faulting instruction is subjected to a new PMI before + * it can complete, ad infinitum. This occurs even though the driver + * clears the CCCR (and in testing also the ESCR) and invokes a + * user-space signal handler before restoring the CCCR and resuming + * the instruction. + */ + +/* + * Table 15-4 in the IA32 Volume 3 manual contains a 18x8 entry mapping + * from counter/CCCR number (0-17) and ESCR SELECT value (0-7) to the + * actual ESCR MSR number. This mapping contains some repeated patterns, + * so we can compact it to a 4x8 table of MSR offsets: + * + * 1. CCCRs 16 and 17 are mapped just like CCCRs 13 and 14, respectively. + * Thus, we only consider the 16 CCCRs 0-15. + * 2. The CCCRs are organised in pairs, and both CCCRs in a pair use the + * same mapping. Thus, we only consider the 8 pairs 0-7. + * 3. In each pair of pairs, the second odd-numbered pair has the same domain + * as the first even-numbered pair, and the range is 1+ the range of the + * the first even-numbered pair. For example, CCCR(0) and (1) map ESCR + * SELECT(7) to 0x3A0, and CCCR(2) and (3) map it to 0x3A1. + * The only exception is that pair (7) [CCCRs 14 and 15] does not have + * ESCR SELECT(3) in its domain, like pair (6) [CCCRs 12 and 13] has. + * NOTE: Revisions of IA32 Volume 3 older than #245472-007 had an error + * in this table: CCCRs 12, 13, and 16 had their mappings for ESCR SELECT + * values 2 and 3 swapped. + * 4. All MSR numbers are on the form 0x3??. Instead of storing these as + * 16-bit numbers, the table only stores the 8-bit offsets from 0x300. + */ + +static const unsigned char p4_cccr_escr_map[4][8] = { + /* 0x00 and 0x01 as is, 0x02 and 0x03 are +1 */ + [0x00/4] { [7] 0xA0, + [6] 0xA2, + [2] 0xAA, + [4] 0xAC, + [0] 0xB2, + [1] 0xB4, + [3] 0xB6, + [5] 0xC8, }, + /* 0x04 and 0x05 as is, 0x06 and 0x07 are +1 */ + [0x04/4] { [0] 0xC0, + [2] 0xC2, + [1] 0xC4, }, + /* 0x08 and 0x09 as is, 0x0A and 0x0B are +1 */ + [0x08/4] { [1] 0xA4, + [0] 0xA6, + [5] 0xA8, + [2] 0xAE, + [3] 0xB0, }, + /* 0x0C, 0x0D, and 0x10 as is, + 0x0E, 0x0F, and 0x11 are +1 except [3] is not in the domain */ + [0x0C/4] { [4] 0xB8, + [5] 0xCC, + [6] 0xE0, + [0] 0xBA, + [2] 0xBC, + [3] 0xBE, + [1] 0xCA, }, +}; + +static unsigned int p4_escr_addr(unsigned int pmc, unsigned int cccr_val) +{ + unsigned int escr_select, pair, escr_offset; + + escr_select = P4_CCCR_ESCR_SELECT(cccr_val); + if (pmc > 0x11) + return 0; /* pmc range error */ + if (pmc > 0x0F) + pmc -= 3; /* 0 <= pmc <= 0x0F */ + pair = pmc / 2; /* 0 <= pair <= 7 */ + escr_offset = p4_cccr_escr_map[pair / 2][escr_select]; + if (!escr_offset || (pair == 7 && escr_select == 3)) + return 0; /* ESCR SELECT range error */ + return escr_offset + (pair & 1) + 0x300; +}; + +static int p4_IQ_ESCR_ok; /* only models <= 2 can use IQ_ESCR{0,1} */ +static int p4_is_ht; /* affects several CCCR & ESCR fields */ +static int p4_extended_cascade_ok; /* only models >= 2 can use extended cascading */ + +static int p4_check_control(struct perfctr_cpu_state *state, int is_global) +{ + unsigned int i, nractrs, nrctrs, pmc_mask; + + nractrs = state->control.nractrs; + nrctrs = nractrs + state->control.nrictrs; + if (nrctrs < nractrs || nrctrs > 18) + return -EINVAL; + + pmc_mask = 0; + for(i = 0; i < nrctrs; ++i) { + unsigned int pmc, cccr_val, escr_val, escr_addr; + /* check that pmc_map[] is well-defined; + pmc_map[i] is what we pass to RDPMC, the PMC itself + is extracted by masking off the FAST_RDPMC flag */ + pmc = state->control.pmc_map[i] & ~P4_FAST_RDPMC; + state->pmc[i].map = state->control.pmc_map[i]; + if (pmc >= 18 || (pmc_mask & (1<control.evntsel[i]; + if (cccr_val & P4_CCCR_RESERVED) + return -EPERM; + if (cccr_val & P4_CCCR_EXTENDED_CASCADE) { + if (!p4_extended_cascade_ok) + return -EPERM; + if (!(pmc == 12 || pmc >= 15)) + return -EPERM; + } + if ((cccr_val & P4_CCCR_ACTIVE_THREAD) != P4_CCCR_ACTIVE_THREAD && !p4_is_ht) + return -EINVAL; + if (!(cccr_val & (P4_CCCR_ENABLE | P4_CCCR_CASCADE | P4_CCCR_EXTENDED_CASCADE))) + return -EINVAL; + if (cccr_val & P4_CCCR_OVF_PMI_T0) { + if (i < nractrs) + return -EINVAL; + if ((cccr_val & P4_CCCR_FORCE_OVF) && + state->control.ireset[i] != -1) + return -EINVAL; + } else { + if (i >= nractrs) + return -EINVAL; + } + /* check ESCR contents */ + escr_val = state->control.p4.escr[i]; + if (escr_val & P4_ESCR_RESERVED) + return -EPERM; + if ((escr_val & P4_ESCR_CPL_T1) && (!p4_is_ht || !is_global)) + return -EINVAL; + /* compute and cache ESCR address */ + escr_addr = p4_escr_addr(pmc, cccr_val); + if (!escr_addr) + return -EINVAL; /* ESCR SELECT range error */ + /* IQ_ESCR0 and IQ_ESCR1 only exist in models <= 2 */ + if ((escr_addr & ~0x001) == 0x3BA && !p4_IQ_ESCR_ok) + return -EINVAL; + /* XXX: Two counters could map to the same ESCR. Should we + check that they use the same ESCR value? */ + state->p4_escr_map[i] = escr_addr - MSR_P4_ESCR0; + } + /* check ReplayTagging control (PEBS_ENABLE and PEBS_MATRIX_VERT) */ + if (state->control.p4.pebs_enable) { + if (!nrctrs) + return -EPERM; + if (state->control.p4.pebs_enable & P4_PE_RESERVED) + return -EPERM; + if (!(state->control.p4.pebs_enable & P4_PE_UOP_TAG)) + return -EINVAL; + if (!(state->control.p4.pebs_enable & P4_PE_REPLAY_TAG_BITS)) + return -EINVAL; + if (state->control.p4.pebs_matrix_vert & P4_PMV_RESERVED) + return -EPERM; + if (!(state->control.p4.pebs_matrix_vert & P4_PMV_REPLAY_TAG_BITS)) + return -EINVAL; + } else if (state->control.p4.pebs_matrix_vert) + return -EPERM; + state->k1.id = new_id(); + return 0; +} + +#ifdef CONFIG_X86_LOCAL_APIC +static void p4_isuspend(struct perfctr_cpu_state *state) +{ + return p6_like_isuspend(state, MSR_P4_CCCR0); +} + +static void p4_iresume(const struct perfctr_cpu_state *state) +{ + return p6_like_iresume(state, MSR_P4_CCCR0, MSR_P4_PERFCTR0); +} +#endif /* CONFIG_X86_LOCAL_APIC */ + +static void p4_write_control(const struct perfctr_cpu_state *state) +{ + struct per_cpu_cache *cache; + unsigned int nrctrs, i; + + cache = get_cpu_cache(); + if (cache->k1.id == state->k1.id) + return; + nrctrs = perfctr_cstatus_nrctrs(state->cstatus); + for(i = 0; i < nrctrs; ++i) { + unsigned int escr_val, escr_off, cccr_val, pmc; + escr_val = state->control.p4.escr[i]; + escr_off = state->p4_escr_map[i]; + if (escr_val != cache->control.escr[escr_off]) { + cache->control.escr[escr_off] = escr_val; + wrmsr(MSR_P4_ESCR0+escr_off, escr_val, 0); + } + cccr_val = state->control.evntsel[i]; + pmc = state->pmc[i].map & P4_MASK_FAST_RDPMC; + if (cccr_val != cache->control.evntsel[pmc]) { + cache->control.evntsel[pmc] = cccr_val; + wrmsr(MSR_P4_CCCR0+pmc, cccr_val, 0); + } + } + if (state->control.p4.pebs_enable != cache->control.pebs_enable) { + cache->control.pebs_enable = state->control.p4.pebs_enable; + wrmsr(MSR_P4_PEBS_ENABLE, state->control.p4.pebs_enable, 0); + } + if (state->control.p4.pebs_matrix_vert != cache->control.pebs_matrix_vert) { + cache->control.pebs_matrix_vert = state->control.p4.pebs_matrix_vert; + wrmsr(MSR_P4_PEBS_MATRIX_VERT, state->control.p4.pebs_matrix_vert, 0); + } + cache->k1.id = state->k1.id; +} + +static void p4_clear_counters(void) +{ + /* MSR 0x3F0 seems to have a default value of 0xFC00, but current + docs doesn't fully define it, so leave it alone for now. */ + /* clear PEBS_ENABLE and PEBS_MATRIX_VERT; they handle both PEBS + and ReplayTagging, and should exist even if PEBS is disabled */ + clear_msr_range(0x3F1, 2); + clear_msr_range(0x3A0, 26); + if (p4_IQ_ESCR_ok) + clear_msr_range(0x3BA, 2); + clear_msr_range(0x3BC, 3); + clear_msr_range(0x3C0, 6); + clear_msr_range(0x3C8, 6); + clear_msr_range(0x3E0, 2); + clear_msr_range(MSR_P4_CCCR0, 18); + clear_msr_range(MSR_P4_PERFCTR0, 18); +} + +/* + * Generic driver for any x86 with a working TSC. + */ + +static int generic_check_control(struct perfctr_cpu_state *state, int is_global) +{ + if (state->control.nractrs || state->control.nrictrs) + return -EINVAL; + return 0; +} + +static void generic_clear_counters(void) +{ +} + +/* + * Driver methods, internal and exported. + * + * Frequently called functions (write_control, read_counters, + * isuspend and iresume) are back-patched to invoke the correct + * processor-specific methods directly, thereby saving the + * overheads of indirect function calls. + * + * Backpatchable call sites must have been "finalised" after + * initialisation. The reason for this is that unsynchronised code + * modification doesn't work in multiprocessor systems, due to + * Intel P6 errata. Consequently, all backpatchable call sites + * must be known and local to this file. + * + * Backpatchable calls must initially be to 'noinline' stubs. + * Otherwise the compiler may inline the stubs, which breaks + * redirect_call() and finalise_backpatching(). + */ + +static int redirect_call_disable; + +static noinline void redirect_call(void *ra, void *to) +{ + /* XXX: make this function __init later */ + if (redirect_call_disable) + printk(KERN_ERR __FILE__ ":%s: unresolved call to %p at %p\n", + __FUNCTION__, to, ra); + /* we can only redirect `call near relative' instructions */ + if (*((unsigned char*)ra - 5) != 0xE8) { + printk(KERN_WARNING __FILE__ ":%s: unable to redirect caller %p to %p\n", + __FUNCTION__, ra, to); + return; + } + *(int*)((char*)ra - 4) = (char*)to - (char*)ra; +} + +static void (*write_control)(const struct perfctr_cpu_state*); +static noinline void perfctr_cpu_write_control(const struct perfctr_cpu_state *state) +{ + redirect_call(__builtin_return_address(0), write_control); + return write_control(state); +} + +static void (*read_counters)(const struct perfctr_cpu_state*, + struct perfctr_low_ctrs*); +static noinline void perfctr_cpu_read_counters(const struct perfctr_cpu_state *state, + struct perfctr_low_ctrs *ctrs) +{ + redirect_call(__builtin_return_address(0), read_counters); + return read_counters(state, ctrs); +} + +#ifdef CONFIG_X86_LOCAL_APIC +static void (*cpu_isuspend)(struct perfctr_cpu_state*); +static noinline void perfctr_cpu_isuspend(struct perfctr_cpu_state *state) +{ + redirect_call(__builtin_return_address(0), cpu_isuspend); + return cpu_isuspend(state); +} + +static void (*cpu_iresume)(const struct perfctr_cpu_state*); +static noinline void perfctr_cpu_iresume(const struct perfctr_cpu_state *state) +{ + redirect_call(__builtin_return_address(0), cpu_iresume); + return cpu_iresume(state); +} + +/* Call perfctr_cpu_ireload() just before perfctr_cpu_resume() to + bypass internal caching and force a reload if the I-mode PMCs. */ +void perfctr_cpu_ireload(struct perfctr_cpu_state *state) +{ +#ifdef CONFIG_SMP + clear_isuspend_cpu(state); +#else + get_cpu_cache()->k1.id = 0; +#endif +} + +/* PRE: the counters have been suspended and sampled by perfctr_cpu_suspend() */ +static int lvtpc_reinit_needed; +unsigned int perfctr_cpu_identify_overflow(struct perfctr_cpu_state *state) +{ + unsigned int cstatus, nrctrs, pmc, pmc_mask; + + cstatus = state->cstatus; + pmc = perfctr_cstatus_nractrs(cstatus); + nrctrs = perfctr_cstatus_nrctrs(cstatus); + + for(pmc_mask = 0; pmc < nrctrs; ++pmc) { + if ((int)state->pmc[pmc].start >= 0) { /* XXX: ">" ? */ + /* XXX: "+=" to correct for overshots */ + state->pmc[pmc].start = state->control.ireset[pmc]; + pmc_mask |= (1 << pmc); + /* On a P4 we should now clear the OVF flag in the + counter's CCCR. However, p4_isuspend() already + did that as a side-effect of clearing the CCCR + in order to stop the i-mode counters. */ + } + } + if (lvtpc_reinit_needed) + apic_write(APIC_LVTPC, LOCAL_PERFCTR_VECTOR); + return pmc_mask; +} + +static inline int check_ireset(const struct perfctr_cpu_state *state) +{ + unsigned int nrctrs, i; + + i = state->control.nractrs; + nrctrs = i + state->control.nrictrs; + for(; i < nrctrs; ++i) + if (state->control.ireset[i] >= 0) + return -EINVAL; + return 0; +} + +static inline void setup_imode_start_values(struct perfctr_cpu_state *state) +{ + unsigned int cstatus, nrctrs, i; + + cstatus = state->cstatus; + nrctrs = perfctr_cstatus_nrctrs(cstatus); + for(i = perfctr_cstatus_nractrs(cstatus); i < nrctrs; ++i) + state->pmc[i].start = state->control.ireset[i]; +} + +#else /* CONFIG_X86_LOCAL_APIC */ +static inline void perfctr_cpu_isuspend(struct perfctr_cpu_state *state) { } +static inline void perfctr_cpu_iresume(const struct perfctr_cpu_state *state) { } +static inline int check_ireset(const struct perfctr_cpu_state *state) { return 0; } +static inline void setup_imode_start_values(struct perfctr_cpu_state *state) { } +#endif /* CONFIG_X86_LOCAL_APIC */ + +static int (*check_control)(struct perfctr_cpu_state*, int); +int perfctr_cpu_update_control(struct perfctr_cpu_state *state, int is_global) +{ + int err; + + clear_isuspend_cpu(state); + state->cstatus = 0; + + /* disallow i-mode counters if we cannot catch the interrupts */ + if (!(perfctr_info.cpu_features & PERFCTR_FEATURE_PCINT) + && state->control.nrictrs) + return -EPERM; + + err = check_control(state, is_global); + if (err < 0) + return err; + err = check_ireset(state); + if (err < 0) + return err; + state->cstatus = perfctr_mk_cstatus(state->control.tsc_on, + state->control.nractrs, + state->control.nrictrs); + setup_imode_start_values(state); + return 0; +} + +void perfctr_cpu_suspend(struct perfctr_cpu_state *state) +{ + unsigned int i, cstatus, nractrs; + struct perfctr_low_ctrs now; + + if (perfctr_cstatus_has_ictrs(state->cstatus)) + perfctr_cpu_isuspend(state); + perfctr_cpu_read_counters(state, &now); + cstatus = state->cstatus; + if (perfctr_cstatus_has_tsc(cstatus)) + state->tsc_sum += now.tsc - state->tsc_start; + nractrs = perfctr_cstatus_nractrs(cstatus); + for(i = 0; i < nractrs; ++i) + state->pmc[i].sum += now.pmc[i] - state->pmc[i].start; + /* perfctr_cpu_disable_rdpmc(); */ /* not for x86 */ +} + +void perfctr_cpu_resume(struct perfctr_cpu_state *state) +{ + if (perfctr_cstatus_has_ictrs(state->cstatus)) + perfctr_cpu_iresume(state); + /* perfctr_cpu_enable_rdpmc(); */ /* not for x86 or global-mode */ + perfctr_cpu_write_control(state); + //perfctr_cpu_read_counters(state, &state->start); + { + struct perfctr_low_ctrs now; + unsigned int i, cstatus, nrctrs; + perfctr_cpu_read_counters(state, &now); + cstatus = state->cstatus; + if (perfctr_cstatus_has_tsc(cstatus)) + state->tsc_start = now.tsc; + nrctrs = perfctr_cstatus_nractrs(cstatus); + for(i = 0; i < nrctrs; ++i) + state->pmc[i].start = now.pmc[i]; + } + /* XXX: if (SMP && start.tsc == now.tsc) ++now.tsc; */ +} + +void perfctr_cpu_sample(struct perfctr_cpu_state *state) +{ + unsigned int i, cstatus, nractrs; + struct perfctr_low_ctrs now; + + perfctr_cpu_read_counters(state, &now); + cstatus = state->cstatus; + if (perfctr_cstatus_has_tsc(cstatus)) { + state->tsc_sum += now.tsc - state->tsc_start; + state->tsc_start = now.tsc; + } + nractrs = perfctr_cstatus_nractrs(cstatus); + for(i = 0; i < nractrs; ++i) { + state->pmc[i].sum += now.pmc[i] - state->pmc[i].start; + state->pmc[i].start = now.pmc[i]; + } +} + +static void (*clear_counters)(void); +static void perfctr_cpu_clear_counters(void) +{ + return clear_counters(); +} + +/**************************************************************** + * * + * Processor detection and initialisation procedures. * + * * + ****************************************************************/ + +static inline void clear_perfctr_cpus_forbidden_mask(void) +{ +#if !defined(perfctr_cpus_forbidden_mask) + cpus_clear(perfctr_cpus_forbidden_mask); +#endif +} + +static inline void set_perfctr_cpus_forbidden_mask(cpumask_t mask) +{ +#if !defined(perfctr_cpus_forbidden_mask) + perfctr_cpus_forbidden_mask = mask; +#endif +} + +/* see comment above at redirect_call() */ +static void __init finalise_backpatching(void) +{ + struct per_cpu_cache *cache; + struct perfctr_cpu_state state; + cpumask_t old_mask; + + old_mask = perfctr_cpus_forbidden_mask; + clear_perfctr_cpus_forbidden_mask(); + + cache = get_cpu_cache(); + memset(cache, 0, sizeof *cache); + memset(&state, 0, sizeof state); + state.cstatus = + (perfctr_info.cpu_features & PERFCTR_FEATURE_PCINT) + ? __perfctr_mk_cstatus(0, 1, 0, 0) + : 0; + perfctr_cpu_sample(&state); + perfctr_cpu_resume(&state); + perfctr_cpu_suspend(&state); + + set_perfctr_cpus_forbidden_mask(old_mask); + + redirect_call_disable = 1; +} + +#ifdef CONFIG_SMP + +cpumask_t perfctr_cpus_forbidden_mask; + +static void __init p4_ht_mask_setup_cpu(void *forbidden) +{ + unsigned int local_apic_physical_id = cpuid_ebx(1) >> 24; + unsigned int logical_processor_id = local_apic_physical_id & 1; + if (logical_processor_id != 0) + /* We rely on cpu_set() being atomic! */ + cpu_set(smp_processor_id(), *(cpumask_t*)forbidden); +} + +static int __init p4_ht_smp_init(void) +{ + cpumask_t forbidden; + unsigned int cpu; + + cpus_clear(forbidden); + smp_call_function(p4_ht_mask_setup_cpu, &forbidden, 1, 1); + p4_ht_mask_setup_cpu(&forbidden); + if (cpus_empty(forbidden)) + return 0; + perfctr_cpus_forbidden_mask = forbidden; + printk(KERN_INFO "perfctr/x86.c: hyper-threaded P4s detected:" + " restricting access for CPUs"); + for(cpu = 0; cpu < NR_CPUS; ++cpu) + if (cpu_isset(cpu, forbidden)) + printk(" %u", cpu); + printk("\n"); + return 0; +} +#else /* SMP */ +#define p4_ht_smp_init() (0) +#endif /* SMP */ + +static int __init p4_ht_init(void) +{ + unsigned int nr_siblings; + + if (!cpu_has_ht) + return 0; + nr_siblings = (cpuid_ebx(1) >> 16) & 0xFF; + if (nr_siblings > 2) { + printk(KERN_WARNING "perfctr/x86.c: hyper-threaded P4s detected:" + " unsupported number of siblings: %u -- bailing out\n", + nr_siblings); + return -ENODEV; + } + if (nr_siblings < 2) + return 0; + p4_is_ht = 1; /* needed even in a UP kernel */ + return p4_ht_smp_init(); +} + +static int __init intel_init(void) +{ + static char p5_name[] __initdata = "Intel P5"; + static char p6_name[] __initdata = "Intel P6"; + static char p4_name[] __initdata = "Intel P4"; + unsigned int misc_enable; + + if (!cpu_has_tsc) + return -ENODEV; + switch (current_cpu_data.x86) { + case 5: + if (cpu_has_mmx) { + read_counters = rdpmc_read_counters; + + /* Avoid Pentium Erratum 74. */ + if (current_cpu_data.x86_model == 4 && + (current_cpu_data.x86_mask == 4 || + (current_cpu_data.x86_mask == 3 && + ((cpuid_eax(1) >> 12) & 0x3) == 1))) + perfctr_info.cpu_features &= ~PERFCTR_FEATURE_RDPMC; + } else { + perfctr_info.cpu_features &= ~PERFCTR_FEATURE_RDPMC; + read_counters = p5_read_counters; + } + perfctr_set_tests_type(PTT_P5); + perfctr_cpu_name = p5_name; + write_control = p5_write_control; + check_control = p5_check_control; + clear_counters = p5_clear_counters; + return 0; + case 6: + if (current_cpu_data.x86_model == 9 || + current_cpu_data.x86_model == 13) { /* Pentium M */ + /* Pentium M added the MISC_ENABLE MSR from P4. */ + rdmsr_low(MSR_IA32_MISC_ENABLE, misc_enable); + if (!(misc_enable & MSR_IA32_MISC_ENABLE_PERF_AVAIL)) + break; + /* Erratum Y3 probably does not apply since we + read only the low 32 bits. */ + } else if (current_cpu_data.x86_model < 3) { /* Pentium Pro */ + /* Avoid Pentium Pro Erratum 26. */ + if (current_cpu_data.x86_mask < 9) + perfctr_info.cpu_features &= ~PERFCTR_FEATURE_RDPMC; + } + perfctr_set_tests_type(PTT_P6); + perfctr_cpu_name = p6_name; + read_counters = rdpmc_read_counters; + write_control = p6_write_control; + check_control = p6_check_control; + clear_counters = p6_clear_counters; +#ifdef CONFIG_X86_LOCAL_APIC + if (cpu_has_apic) { + perfctr_info.cpu_features |= PERFCTR_FEATURE_PCINT; + cpu_isuspend = p6_isuspend; + cpu_iresume = p6_iresume; + /* P-M apparently inherited P4's LVTPC auto-masking :-( */ + if (current_cpu_data.x86_model == 9 || + current_cpu_data.x86_model == 13) + lvtpc_reinit_needed = 1; + } +#endif + return 0; + case 15: /* Pentium 4 */ + rdmsr_low(MSR_IA32_MISC_ENABLE, misc_enable); + if (!(misc_enable & MSR_IA32_MISC_ENABLE_PERF_AVAIL)) + break; + if (p4_ht_init() != 0) + break; + if (current_cpu_data.x86_model <= 2) + p4_IQ_ESCR_ok = 1; + if (current_cpu_data.x86_model >= 2) + p4_extended_cascade_ok = 1; + perfctr_set_tests_type(PTT_P4); + perfctr_cpu_name = p4_name; + read_counters = rdpmc_read_counters; + write_control = p4_write_control; + check_control = p4_check_control; + clear_counters = p4_clear_counters; +#ifdef CONFIG_X86_LOCAL_APIC + if (cpu_has_apic) { + perfctr_info.cpu_features |= PERFCTR_FEATURE_PCINT; + cpu_isuspend = p4_isuspend; + cpu_iresume = p4_iresume; + lvtpc_reinit_needed = 1; + } +#endif + return 0; + } + return -ENODEV; +} + +static int __init amd_init(void) +{ + static char amd_name[] __initdata = "AMD K7/K8"; + + if (!cpu_has_tsc) + return -ENODEV; + switch (current_cpu_data.x86) { + case 6: /* K7 */ + case 15: /* K8. Like a K7 with a different event set. */ + break; + default: + return -ENODEV; + } + perfctr_set_tests_type(PTT_AMD); + perfctr_cpu_name = amd_name; + read_counters = rdpmc_read_counters; + write_control = k7_write_control; + check_control = k7_check_control; + clear_counters = k7_clear_counters; +#ifdef CONFIG_X86_LOCAL_APIC + if (cpu_has_apic) { + perfctr_info.cpu_features |= PERFCTR_FEATURE_PCINT; + cpu_isuspend = k7_isuspend; + cpu_iresume = k7_iresume; + } +#endif + return 0; +} + +static int __init cyrix_init(void) +{ + static char mii_name[] __initdata = "Cyrix 6x86MX/MII/III"; + if (!cpu_has_tsc) + return -ENODEV; + switch (current_cpu_data.x86) { + case 6: /* 6x86MX, MII, or III */ + perfctr_set_tests_type(PTT_P5); + perfctr_cpu_name = mii_name; + read_counters = rdpmc_read_counters; + write_control = p5_write_control; + check_control = mii_check_control; + clear_counters = p5_clear_counters; + return 0; + } + return -ENODEV; +} + +static int __init centaur_init(void) +{ +#if !defined(CONFIG_X86_TSC) + static char winchip_name[] __initdata = "WinChip C6/2/3"; +#endif + static char vc3_name[] __initdata = "VIA C3"; + switch (current_cpu_data.x86) { +#if !defined(CONFIG_X86_TSC) + case 5: + switch (current_cpu_data.x86_model) { + case 4: /* WinChip C6 */ + case 8: /* WinChip 2, 2A, or 2B */ + case 9: /* WinChip 3, a 2A with larger cache and lower voltage */ + break; + default: + return -ENODEV; + } + perfctr_set_tests_type(PTT_WINCHIP); + perfctr_cpu_name = winchip_name; + /* + * TSC must be inaccessible for perfctrs to work. + */ + if (!(read_cr4() & X86_CR4_TSD) || cpu_has_tsc) + return -ENODEV; + perfctr_info.cpu_features &= ~PERFCTR_FEATURE_RDTSC; + read_counters = rdpmc_read_counters; + write_control = c6_write_control; + check_control = c6_check_control; + clear_counters = p5_clear_counters; + return 0; +#endif + case 6: /* VIA C3 */ + if (!cpu_has_tsc) + return -ENODEV; + switch (current_cpu_data.x86_model) { + case 6: /* Cyrix III */ + case 7: /* Samuel 2, Ezra (steppings >= 8) */ + case 8: /* Ezra-T */ + case 9: /* Antaur/Nehemiah */ + break; + default: + return -ENODEV; + } + perfctr_set_tests_type(PTT_VC3); + perfctr_cpu_name = vc3_name; + read_counters = rdpmc_read_counters; + write_control = p6_write_control; + check_control = vc3_check_control; + clear_counters = vc3_clear_counters; + return 0; + } + return -ENODEV; +} + +static int __init generic_init(void) +{ + static char generic_name[] __initdata = "Generic x86 with TSC"; + if (!cpu_has_tsc) + return -ENODEV; + perfctr_info.cpu_features &= ~PERFCTR_FEATURE_RDPMC; + perfctr_set_tests_type(PTT_GENERIC); + perfctr_cpu_name = generic_name; + check_control = generic_check_control; + write_control = p6_write_control; + read_counters = rdpmc_read_counters; + clear_counters = generic_clear_counters; + return 0; +} + +static void perfctr_cpu_invalidate_cache(void) +{ + /* + * per_cpu_cache[] is initialised to contain "impossible" + * evntsel values guaranteed to differ from anything accepted + * by perfctr_cpu_update_control(). + * All-bits-one works for all currently supported processors. + * The memset also sets the ids to -1, which is intentional. + */ + memset(get_cpu_cache(), ~0, sizeof(struct per_cpu_cache)); +} + +static void perfctr_cpu_init_one(void *ignore) +{ + /* PREEMPT note: when called via smp_call_function(), + this is in IRQ context with preemption disabled. */ + perfctr_cpu_clear_counters(); + perfctr_cpu_invalidate_cache(); + if (cpu_has_apic) + apic_write(APIC_LVTPC, LOCAL_PERFCTR_VECTOR); + if (perfctr_info.cpu_features & PERFCTR_FEATURE_RDPMC) + set_in_cr4_local(X86_CR4_PCE); +} + +static void perfctr_cpu_exit_one(void *ignore) +{ + /* PREEMPT note: when called via smp_call_function(), + this is in IRQ context with preemption disabled. */ + perfctr_cpu_clear_counters(); + perfctr_cpu_invalidate_cache(); + if (cpu_has_apic) + apic_write(APIC_LVTPC, APIC_DM_NMI | APIC_LVT_MASKED); + if (perfctr_info.cpu_features & PERFCTR_FEATURE_RDPMC) + clear_in_cr4_local(X86_CR4_PCE); +} + +#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_PM) + +static void perfctr_pm_suspend(void) +{ + /* XXX: clear control registers */ + printk("perfctr/x86: PM suspend\n"); +} + +static void perfctr_pm_resume(void) +{ + /* XXX: reload control registers */ + printk("perfctr/x86: PM resume\n"); +} + +#include + +static int perfctr_device_suspend(struct sys_device *dev, u32 state) +{ + perfctr_pm_suspend(); + return 0; +} + +static int perfctr_device_resume(struct sys_device *dev) +{ + perfctr_pm_resume(); + return 0; +} + +static struct sysdev_class perfctr_sysclass = { + set_kset_name("perfctr"), + .resume = perfctr_device_resume, + .suspend = perfctr_device_suspend, +}; + +static struct sys_device device_perfctr = { + .id = 0, + .cls = &perfctr_sysclass, +}; + +static void x86_pm_init(void) +{ + if (sysdev_class_register(&perfctr_sysclass) == 0) + sysdev_register(&device_perfctr); +} + +static void x86_pm_exit(void) +{ + sysdev_unregister(&device_perfctr); + sysdev_class_unregister(&perfctr_sysclass); +} + +#else + +static inline void x86_pm_init(void) { } +static inline void x86_pm_exit(void) { } + +#endif /* CONFIG_X86_LOCAL_APIC && CONFIG_PM */ + +#if !defined(CONFIG_X86_LOCAL_APIC) +static inline int reserve_lapic_nmi(void) { return 0; } +static inline void release_lapic_nmi(void) { } +#endif + +static void do_init_tests(void) +{ +#ifdef CONFIG_PERFCTR_INIT_TESTS + if (reserve_lapic_nmi() >= 0) { + perfctr_x86_init_tests(); + release_lapic_nmi(); + } +#endif +} + +static int init_done; + +int __init perfctr_cpu_init(void) +{ + int err = -ENODEV; + + preempt_disable(); + + /* RDPMC and RDTSC are on by default. They will be disabled + by the init procedures if necessary. */ + perfctr_info.cpu_features = PERFCTR_FEATURE_RDPMC | PERFCTR_FEATURE_RDTSC; + + if (cpu_has_msr) { + switch (current_cpu_data.x86_vendor) { + case X86_VENDOR_INTEL: + err = intel_init(); + break; + case X86_VENDOR_AMD: + err = amd_init(); + break; + case X86_VENDOR_CYRIX: + err = cyrix_init(); + break; + case X86_VENDOR_CENTAUR: + err = centaur_init(); + } + } + if (err) { + err = generic_init(); /* last resort */ + if (err) + goto out; + } + do_init_tests(); + finalise_backpatching(); + + perfctr_info.cpu_khz = cpu_khz; + perfctr_info.tsc_to_cpu_mult = 1; + init_done = 1; + + out: + preempt_enable(); + return err; +} + +void __exit perfctr_cpu_exit(void) +{ +} + +/**************************************************************** + * * + * Hardware reservation. * + * * + ****************************************************************/ + +static DECLARE_MUTEX(mutex); +static const char *current_service = 0; + +const char *perfctr_cpu_reserve(const char *service) +{ + const char *ret; + + if (!init_done) + return "unsupported hardware"; + down(&mutex); + ret = current_service; + if (ret) + goto out_up; + ret = "unknown driver (oprofile?)"; + if (reserve_lapic_nmi() < 0) + goto out_up; + current_service = service; + if (perfctr_info.cpu_features & PERFCTR_FEATURE_RDPMC) + mmu_cr4_features |= X86_CR4_PCE; + on_each_cpu(perfctr_cpu_init_one, NULL, 1, 1); + perfctr_cpu_set_ihandler(NULL); + x86_pm_init(); + ret = NULL; + out_up: + up(&mutex); + return ret; +} + +void perfctr_cpu_release(const char *service) +{ + down(&mutex); + if (service != current_service) { + printk(KERN_ERR "%s: attempt by %s to release while reserved by %s\n", + __FUNCTION__, service, current_service); + goto out_up; + } + /* power down the counters */ + if (perfctr_info.cpu_features & PERFCTR_FEATURE_RDPMC) + mmu_cr4_features &= ~X86_CR4_PCE; + on_each_cpu(perfctr_cpu_exit_one, NULL, 1, 1); + perfctr_cpu_set_ihandler(NULL); + x86_pm_exit(); + current_service = 0; + release_lapic_nmi(); + out_up: + up(&mutex); +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/perfctr/x86_tests.c 2004-09-03 00:56:46.051159808 -0700 @@ -0,0 +1,308 @@ +/* $Id: x86_tests.c,v 1.34 2004/08/08 19:54:40 mikpe Exp $ + * Performance-monitoring counters driver. + * Optional x86/x86_64-specific init-time tests. + * + * Copyright (C) 1999-2004 Mikael Pettersson + */ +#include +#include +#include +#include +#include +#include +#undef MSR_P6_PERFCTR0 +#undef MSR_P4_IQ_CCCR0 +#undef MSR_P4_CRU_ESCR0 +#include +#include +#include /* cpu_khz */ +#include "x86_tests.h" + +#define MSR_P5_CESR 0x11 +#define MSR_P5_CTR0 0x12 +#define P5_CESR_VAL (0x16 | (3<<6)) +#define MSR_P6_PERFCTR0 0xC1 +#define MSR_P6_EVNTSEL0 0x186 +#define P6_EVNTSEL0_VAL (0xC0 | (3<<16) | (1<<22)) +#define MSR_K7_EVNTSEL0 0xC0010000 +#define MSR_K7_PERFCTR0 0xC0010004 +#define K7_EVNTSEL0_VAL (0xC0 | (3<<16) | (1<<22)) +#define VC3_EVNTSEL1_VAL 0xC0 +#define MSR_P4_IQ_COUNTER0 0x30C +#define MSR_P4_IQ_CCCR0 0x36C +#define MSR_P4_CRU_ESCR0 0x3B8 +#define P4_CRU_ESCR0_VAL ((2<<25) | (1<<9) | (0x3<<2)) +#define P4_IQ_CCCR0_VAL ((0x3<<16) | (4<<13) | (1<<12)) + +#define NITER 64 +#define X2(S) S";"S +#define X8(S) X2(X2(X2(S))) + +#ifdef __x86_64__ +#define CR4MOV "movq" +#else +#define CR4MOV "movl" +#endif + +#ifndef CONFIG_X86_LOCAL_APIC +#undef apic_write +#define apic_write(reg,vector) do{}while(0) +#endif + +#if !defined(__x86_64__) +/* Avoid speculative execution by the CPU */ +extern inline void sync_core(void) +{ + int tmp; + asm volatile("cpuid" : "=a" (tmp) : "0" (1) : "ebx","ecx","edx","memory"); +} +#endif + +static void __init do_rdpmc(unsigned pmc, unsigned unused2) +{ + unsigned i; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8("rdpmc") : : "c"(pmc) : "eax", "edx"); +} + +static void __init do_rdmsr(unsigned msr, unsigned unused2) +{ + unsigned i; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8("rdmsr") : : "c"(msr) : "eax", "edx"); +} + +static void __init do_wrmsr(unsigned msr, unsigned data) +{ + unsigned i; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8("wrmsr") : : "c"(msr), "a"(data), "d"(0)); +} + +static void __init do_rdcr4(unsigned unused1, unsigned unused2) +{ + unsigned i; + unsigned long dummy; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8(CR4MOV" %%cr4,%0") : "=r"(dummy)); +} + +static void __init do_wrcr4(unsigned cr4, unsigned unused2) +{ + unsigned i; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8(CR4MOV" %0,%%cr4") : : "r"((long)cr4)); +} + +static void __init do_rdtsc(unsigned unused1, unsigned unused2) +{ + unsigned i; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__(X8("rdtsc") : : : "eax", "edx"); +} + +static void __init do_wrlvtpc(unsigned val, unsigned unused2) +{ + unsigned i; + for(i = 0; i < NITER/8; ++i) { + apic_write(APIC_LVTPC, val); + apic_write(APIC_LVTPC, val); + apic_write(APIC_LVTPC, val); + apic_write(APIC_LVTPC, val); + apic_write(APIC_LVTPC, val); + apic_write(APIC_LVTPC, val); + apic_write(APIC_LVTPC, val); + apic_write(APIC_LVTPC, val); + } +} + +static void __init do_sync_core(unsigned unused1, unsigned unused2) +{ + unsigned i; + for(i = 0; i < NITER/8; ++i) { + sync_core(); + sync_core(); + sync_core(); + sync_core(); + sync_core(); + sync_core(); + sync_core(); + sync_core(); + } +} + +static void __init do_empty_loop(unsigned unused1, unsigned unused2) +{ + unsigned i; + for(i = 0; i < NITER/8; ++i) + __asm__ __volatile__("" : : "c"(0)); +} + +static unsigned __init run(void (*doit)(unsigned, unsigned), + unsigned arg1, unsigned arg2) +{ + unsigned start, dummy, stop; + sync_core(); + rdtsc(start, dummy); + (*doit)(arg1, arg2); /* should take < 2^32 cycles to complete */ + sync_core(); + rdtsc(stop, dummy); + return stop - start; +} + +static void __init init_tests_message(void) +{ + printk(KERN_INFO "Please email the following PERFCTR INIT lines " + "to mikpe@csd.uu.se\n" + KERN_INFO "To remove this message, rebuild the driver " + "with CONFIG_PERFCTR_INIT_TESTS=n\n"); + printk(KERN_INFO "PERFCTR INIT: vendor %u, family %u, model %u, stepping %u, clock %u kHz\n", + current_cpu_data.x86_vendor, + current_cpu_data.x86, + current_cpu_data.x86_model, + current_cpu_data.x86_mask, + (unsigned int)cpu_khz); +} + +static void __init +measure_overheads(unsigned msr_evntsel0, unsigned evntsel0, unsigned msr_perfctr0, + unsigned msr_cccr, unsigned cccr_val) +{ + int i; + unsigned int loop, ticks[13]; + const char *name[13]; + + if (msr_evntsel0) + wrmsr(msr_evntsel0, 0, 0); + if (msr_cccr) + wrmsr(msr_cccr, 0, 0); + + name[0] = "rdtsc"; + ticks[0] = run(do_rdtsc, 0, 0); + name[1] = "rdpmc"; + ticks[1] = (perfctr_info.cpu_features & PERFCTR_FEATURE_RDPMC) + ? run(do_rdpmc,1,0) : 0; + name[2] = "rdmsr (counter)"; + ticks[2] = msr_perfctr0 ? run(do_rdmsr, msr_perfctr0, 0) : 0; + name[3] = msr_cccr ? "rdmsr (escr)" : "rdmsr (evntsel)"; + ticks[3] = msr_evntsel0 ? run(do_rdmsr, msr_evntsel0, 0) : 0; + name[4] = "wrmsr (counter)"; + ticks[4] = msr_perfctr0 ? run(do_wrmsr, msr_perfctr0, 0) : 0; + name[5] = msr_cccr ? "wrmsr (escr)" : "wrmsr (evntsel)"; + ticks[5] = msr_evntsel0 ? run(do_wrmsr, msr_evntsel0, evntsel0) : 0; + name[6] = "read cr4"; + ticks[6] = run(do_rdcr4, 0, 0); + name[7] = "write cr4"; + ticks[7] = run(do_wrcr4, read_cr4(), 0); + name[8] = "rdpmc (fast)"; + ticks[8] = msr_cccr ? run(do_rdpmc, 0x80000001, 0) : 0; + name[9] = "rdmsr (cccr)"; + ticks[9] = msr_cccr ? run(do_rdmsr, msr_cccr, 0) : 0; + name[10] = "wrmsr (cccr)"; + ticks[10] = msr_cccr ? run(do_wrmsr, msr_cccr, cccr_val) : 0; + name[11] = "write LVTPC"; + ticks[11] = (perfctr_info.cpu_features & PERFCTR_FEATURE_PCINT) + ? run(do_wrlvtpc, APIC_DM_NMI|APIC_LVT_MASKED, 0) : 0; + name[12] = "sync_core"; + ticks[12] = run(do_sync_core, 0, 0); + + loop = run(do_empty_loop, 0, 0); + + if (msr_evntsel0) + wrmsr(msr_evntsel0, 0, 0); + if (msr_cccr) + wrmsr(msr_cccr, 0, 0); + + init_tests_message(); + printk(KERN_INFO "PERFCTR INIT: NITER == %u\n", NITER); + printk(KERN_INFO "PERFCTR INIT: loop overhead is %u cycles\n", loop); + for(i = 0; i < ARRAY_SIZE(ticks); ++i) { + unsigned int x; + if (!ticks[i]) + continue; + x = ((ticks[i] - loop) * 10) / NITER; + printk(KERN_INFO "PERFCTR INIT: %s cost is %u.%u cycles (%u total)\n", + name[i], x/10, x%10, ticks[i]); + } +} + +#ifndef __x86_64__ +static inline void perfctr_p5_init_tests(void) +{ + measure_overheads(MSR_P5_CESR, P5_CESR_VAL, MSR_P5_CTR0, 0, 0); +} + +static inline void perfctr_p6_init_tests(void) +{ + measure_overheads(MSR_P6_EVNTSEL0, P6_EVNTSEL0_VAL, MSR_P6_PERFCTR0, 0, 0); +} + +#if !defined(CONFIG_X86_TSC) +static inline void perfctr_c6_init_tests(void) +{ + unsigned int cesr, dummy; + + rdmsr(MSR_P5_CESR, cesr, dummy); + init_tests_message(); + printk(KERN_INFO "PERFCTR INIT: boot CESR == %#08x\n", cesr); +} +#endif + +static inline void perfctr_vc3_init_tests(void) +{ + measure_overheads(MSR_P6_EVNTSEL0+1, VC3_EVNTSEL1_VAL, MSR_P6_PERFCTR0+1, 0, 0); +} +#endif /* !__x86_64__ */ + +static inline void perfctr_p4_init_tests(void) +{ + measure_overheads(MSR_P4_CRU_ESCR0, P4_CRU_ESCR0_VAL, MSR_P4_IQ_COUNTER0, + MSR_P4_IQ_CCCR0, P4_IQ_CCCR0_VAL); +} + +static inline void perfctr_k7_init_tests(void) +{ + measure_overheads(MSR_K7_EVNTSEL0, K7_EVNTSEL0_VAL, MSR_K7_PERFCTR0, 0, 0); +} + +static inline void perfctr_generic_init_tests(void) +{ + measure_overheads(0, 0, 0, 0, 0); +} + +enum perfctr_x86_tests_type perfctr_x86_tests_type __initdata = PTT_UNKNOWN; + +void __init perfctr_x86_init_tests(void) +{ + switch (perfctr_x86_tests_type) { +#ifndef __x86_64__ + case PTT_P5: /* Intel P5, P5MMX; Cyrix 6x86MX, MII, III */ + perfctr_p5_init_tests(); + break; + case PTT_P6: /* Intel PPro, PII, PIII, PENTM */ + perfctr_p6_init_tests(); + break; +#if !defined(CONFIG_X86_TSC) + case PTT_WINCHIP: /* WinChip C6, 2, 3 */ + perfctr_c6_init_tests(); + break; +#endif + case PTT_VC3: /* VIA C3 */ + perfctr_vc3_init_tests(); + break; +#endif /* !__x86_64__ */ + case PTT_P4: /* Intel P4 */ + perfctr_p4_init_tests(); + break; + case PTT_AMD: /* AMD K7, K8 */ + perfctr_k7_init_tests(); + break; + case PTT_GENERIC: + perfctr_generic_init_tests(); + break; + default: + printk(KERN_INFO "%s: unknown CPU type %u\n", + __FUNCTION__, perfctr_x86_tests_type); + break; + } +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/perfctr/x86_tests.h 2004-09-03 00:56:44.878338104 -0700 @@ -0,0 +1,30 @@ +/* $Id: x86_tests.h,v 1.10 2004/05/22 20:48:57 mikpe Exp $ + * Performance-monitoring counters driver. + * Optional x86/x86_64-specific init-time tests. + * + * Copyright (C) 1999-2004 Mikael Pettersson + */ + +/* 'enum perfctr_x86_tests_type' classifies CPUs according + to relevance for perfctr_x86_init_tests(). */ +enum perfctr_x86_tests_type { + PTT_UNKNOWN, + PTT_GENERIC, + PTT_P5, + PTT_P6, + PTT_P4, + PTT_AMD, + PTT_WINCHIP, + PTT_VC3, +}; + +extern enum perfctr_x86_tests_type perfctr_x86_tests_type; + +static inline void perfctr_set_tests_type(enum perfctr_x86_tests_type t) +{ +#ifdef CONFIG_PERFCTR_INIT_TESTS + perfctr_x86_tests_type = t; +#endif +} + +extern void perfctr_x86_init_tests(void); --- linux-2.6.9-rc1/drivers/pnp/driver.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/pnp/driver.c 2004-09-03 00:56:33.269102976 -0700 @@ -50,6 +50,11 @@ int compare_pnp_id(struct pnp_id *pos, c return 0; } +static struct pnp_device_id generic_id = { + .id = "ANYDEVS", + .driver_data = 0, +}; + static const struct pnp_device_id * match_device(struct pnp_driver *drv, struct pnp_dev *dev) { const struct pnp_device_id *drv_id = drv->id_table; @@ -61,6 +66,8 @@ static const struct pnp_device_id * matc return drv_id; drv_id++; } + if ((drv->match && !drv->match(dev))) + return &generic_id; return NULL; } --- linux-2.6.9-rc1/drivers/pnp/interface.c 2004-08-24 00:01:55.000000000 -0700 +++ 25/drivers/pnp/interface.c 2004-09-02 20:51:52.000000000 -0700 @@ -240,12 +240,14 @@ static ssize_t pnp_show_current_resource { struct pnp_dev *dev = to_pnp_dev(dmdev); int i, ret; - pnp_info_buffer_t *buffer = (pnp_info_buffer_t *) - pnp_alloc(sizeof(pnp_info_buffer_t)); - if (!buffer) - return -ENOMEM; + pnp_info_buffer_t *buffer; + if (!dev) return -EINVAL; + + buffer = (pnp_info_buffer_t *) pnp_alloc(sizeof(pnp_info_buffer_t)); + if (!buffer) + return -ENOMEM; buffer->len = PAGE_SIZE; buffer->buffer = buf; buffer->curr = buffer->buffer; --- linux-2.6.9-rc1/drivers/pnp/isapnp/core.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/drivers/pnp/isapnp/core.c 2004-09-02 20:51:52.000000000 -0700 @@ -655,8 +655,10 @@ static int __init isapnp_create_device(s if ((dev = isapnp_parse_device(card, size, number++)) == NULL) return 1; option = pnp_register_independent_option(dev); - if (!option) + if (!option) { + kfree(dev); return 1; + } pnp_add_card_device(card,dev); while (1) { --- linux-2.6.9-rc1/drivers/pnp/pnpbios/core.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/drivers/pnp/pnpbios/core.c 2004-09-02 20:51:52.000000000 -0700 @@ -252,8 +252,10 @@ static int pnpbios_set_resources(struct node = pnpbios_kmalloc(node_info.max_node_size, GFP_KERNEL); if (!node) return -1; - if (pnp_bios_get_dev_node(&nodenum, (char )PNPMODE_DYNAMIC, node)) + if (pnp_bios_get_dev_node(&nodenum, (char )PNPMODE_DYNAMIC, node)) { + kfree(node); return -ENODEV; + } if(pnpbios_write_resources_to_node(res, node)<0) { kfree(node); return -1; --- linux-2.6.9-rc1/drivers/pnp/pnpbios/proc.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/drivers/pnp/pnpbios/proc.c 2004-09-02 20:51:52.000000000 -0700 @@ -90,8 +90,10 @@ static int proc_read_escd(char *buf, cha tmpbuf = pnpbios_kmalloc(escd.escd_size, GFP_KERNEL); if (!tmpbuf) return -ENOMEM; - if (pnp_bios_read_escd(tmpbuf, escd.nv_storage_base)) + if (pnp_bios_read_escd(tmpbuf, escd.nv_storage_base)) { + kfree(tmpbuf); return -EIO; + } escd_size = (unsigned char)(tmpbuf[0]) + (unsigned char)(tmpbuf[1])*256; @@ -168,8 +170,10 @@ static int proc_read_node(char *buf, cha node = pnpbios_kmalloc(node_info.max_node_size, GFP_KERNEL); if (!node) return -ENOMEM; - if (pnp_bios_get_dev_node(&nodenum, boot, node)) + if (pnp_bios_get_dev_node(&nodenum, boot, node)) { + kfree(node); return -EIO; + } len = node->size - sizeof(struct pnp_bios_node); memcpy(buf, node->data, len); kfree(node); --- linux-2.6.9-rc1/drivers/s390/char/sclp.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/s390/char/sclp.c 2004-09-02 20:51:52.000000000 -0700 @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -60,6 +61,7 @@ static volatile unsigned long sclp_statu #define SCLP_INIT 0 #define SCLP_RUNNING 1 #define SCLP_READING 2 +#define SCLP_SHUTDOWN 3 #define SCLP_INIT_POLL_INTERVAL 1 #define SCLP_BUSY_POLL_INTERVAL 1 @@ -606,10 +608,12 @@ sclp_init_mask(void) sccb->mask_length = sizeof(sccb_mask_t); /* copy in the sccb mask of the registered event types */ spin_lock_irqsave(&sclp_lock, flags); - list_for_each(l, &sclp_reg_list) { - t = list_entry(l, struct sclp_register, list); - sccb->receive_mask |= t->receive_mask; - sccb->send_mask |= t->send_mask; + if (!test_bit(SCLP_SHUTDOWN, &sclp_status)) { + list_for_each(l, &sclp_reg_list) { + t = list_entry(l, struct sclp_register, list); + sccb->receive_mask |= t->receive_mask; + sccb->send_mask |= t->send_mask; + } } sccb->sclp_receive_mask = 0; sccb->sclp_send_mask = 0; @@ -647,9 +651,10 @@ sclp_init_mask(void) /* WRITEMASK failed - we cannot rely on receiving a state change event, so initially, polling is the only alternative for us to ever become operational. */ - if (!timer_pending(&retry_timer) || - !mod_timer(&retry_timer, - jiffies + SCLP_INIT_POLL_INTERVAL*HZ)) { + if (!test_bit(SCLP_SHUTDOWN, &sclp_status) && + (!timer_pending(&retry_timer) || + !mod_timer(&retry_timer, + jiffies + SCLP_INIT_POLL_INTERVAL*HZ))) { retry_timer.function = sclp_init_mask_retry; retry_timer.data = 0; retry_timer.expires = jiffies + @@ -671,6 +676,26 @@ sclp_init_mask_retry(unsigned long data) sclp_init_mask(); } +/* Reboot event handler - reset send and receive mask to prevent pending SCLP + * events from interfering with rebooted system. */ +static int +sclp_reboot_event(struct notifier_block *this, unsigned long event, void *ptr) +{ + unsigned long flags; + + /* Note: need spinlock to maintain atomicity when accessing global + * variables. */ + spin_lock_irqsave(&sclp_lock, flags); + set_bit(SCLP_SHUTDOWN, &sclp_status); + spin_unlock_irqrestore(&sclp_lock, flags); + sclp_init_mask(); + return NOTIFY_DONE; +} + +static struct notifier_block sclp_reboot_notifier = { + .notifier_call = sclp_reboot_event +}; + /* * sclp setup function. Called early (no kmalloc!) from sclp_console_init(). */ @@ -691,6 +716,10 @@ sclp_init(void) list_add(&sclp_state_change_event.list, &sclp_reg_list); list_add(&sclp_quiesce_event.list, &sclp_reg_list); + rc = register_reboot_notifier(&sclp_reboot_notifier); + if (rc) + return rc; + /* * request the 0x2401 external interrupt * The sclp driver is initialized early (before kmalloc works). We --- linux-2.6.9-rc1/drivers/s390/cio/cio.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/s390/cio/cio.c 2004-09-03 00:56:41.016925128 -0700 @@ -17,8 +17,8 @@ #include #include #include +#include -#include #include #include #include --- linux-2.6.9-rc1/drivers/s390/cio/qdio.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/s390/cio/qdio.c 2004-09-02 20:51:52.000000000 -0700 @@ -56,7 +56,7 @@ #include "ioasm.h" #include "chsc.h" -#define VERSION_QDIO_C "$Revision: 1.84 $" +#define VERSION_QDIO_C "$Revision: 1.86 $" /****************** MODULE PARAMETER VARIABLES ********************/ MODULE_AUTHOR("Utz Bacher "); @@ -365,7 +365,7 @@ qdio_stop_polling(struct qdio_q *q) * small window we can miss between resetting it and * checking for PRIMED state */ - if (q->is_iqdio_q) + if (q->is_thinint_q) tiqdio_set_summary_bit((__u32*)q->dev_st_chg_ind); return 0; --- linux-2.6.9-rc1/drivers/s390/net/lcs.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/s390/net/lcs.c 2004-09-02 20:51:52.000000000 -0700 @@ -11,7 +11,7 @@ * Frank Pavlic (pavlic@de.ibm.com) and * Martin Schwidefsky * - * $Revision: 1.85 $ $Date: 2004/08/04 11:05:43 $ + * $Revision: 1.89 $ $Date: 2004/08/24 10:49:27 $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -59,7 +59,7 @@ /** * initialization string for output */ -#define VERSION_LCS_C "$Revision: 1.85 $" +#define VERSION_LCS_C "$Revision: 1.89 $" static char version[] __initdata = "LCS driver ("VERSION_LCS_C "/" VERSION_LCS_H ")"; static char debug_buffer[255]; @@ -70,6 +70,7 @@ static char debug_buffer[255]; static void lcs_tasklet(unsigned long); static void lcs_start_kernel_thread(struct lcs_card *card); static void lcs_get_frames_cb(struct lcs_channel *, struct lcs_buffer *); +static int lcs_send_delipm(struct lcs_card *, struct lcs_ipm_list *); /** * Debug Facility Stuff @@ -100,9 +101,9 @@ lcs_register_debug_facility(void) return -ENOMEM; } debug_register_view(lcs_dbf_setup, &debug_hex_ascii_view); - debug_set_level(lcs_dbf_setup, 2); + debug_set_level(lcs_dbf_setup, 4); debug_register_view(lcs_dbf_trace, &debug_hex_ascii_view); - debug_set_level(lcs_dbf_trace, 2); + debug_set_level(lcs_dbf_trace, 4); return 0; } @@ -316,7 +317,106 @@ lcs_setup_write(struct lcs_card *card) init_waitqueue_head(&card->write.wait_q); } +static void +lcs_set_allowed_threads(struct lcs_card *card, unsigned long threads) +{ + unsigned long flags; + + spin_lock_irqsave(&card->mask_lock, flags); + card->thread_allowed_mask = threads; + spin_unlock_irqrestore(&card->mask_lock, flags); + wake_up(&card->wait_q); +} +static inline int +lcs_threads_running(struct lcs_card *card, unsigned long threads) +{ + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&card->mask_lock, flags); + rc = (card->thread_running_mask & threads); + spin_unlock_irqrestore(&card->mask_lock, flags); + return rc; +} + +static int +lcs_wait_for_threads(struct lcs_card *card, unsigned long threads) +{ + return wait_event_interruptible(card->wait_q, + lcs_threads_running(card, threads) == 0); +} + +static inline int +lcs_set_thread_start_bit(struct lcs_card *card, unsigned long thread) +{ + unsigned long flags; + + spin_lock_irqsave(&card->mask_lock, flags); + if ( !(card->thread_allowed_mask & thread) || + (card->thread_start_mask & thread) ) { + spin_unlock_irqrestore(&card->mask_lock, flags); + return -EPERM; + } + card->thread_start_mask |= thread; + spin_unlock_irqrestore(&card->mask_lock, flags); + return 0; +} + +static void +lcs_clear_thread_running_bit(struct lcs_card *card, unsigned long thread) +{ + unsigned long flags; + + spin_lock_irqsave(&card->mask_lock, flags); + card->thread_running_mask &= ~thread; + spin_unlock_irqrestore(&card->mask_lock, flags); + wake_up(&card->wait_q); +} + +static inline int +__lcs_do_run_thread(struct lcs_card *card, unsigned long thread) +{ + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&card->mask_lock, flags); + if (card->thread_start_mask & thread){ + if ((card->thread_allowed_mask & thread) && + !(card->thread_running_mask & thread)){ + rc = 1; + card->thread_start_mask &= ~thread; + card->thread_running_mask |= thread; + } else + rc = -EPERM; + } + spin_unlock_irqrestore(&card->mask_lock, flags); + return rc; +} + +static int +lcs_do_run_thread(struct lcs_card *card, unsigned long thread) +{ + int rc = 0; + wait_event(card->wait_q, + (rc = __lcs_do_run_thread(card, thread)) >= 0); + return rc; +} + +static int +lcs_do_start_thread(struct lcs_card *card, unsigned long thread) +{ + unsigned long flags; + int rc = 0; + spin_lock_irqsave(&card->mask_lock, flags); + LCS_DBF_TEXT_(4, trace, " %02x%02x%02x", + (u8) card->thread_start_mask, + (u8) card->thread_allowed_mask, + (u8) card->thread_running_mask); + rc = (card->thread_start_mask & thread); + spin_unlock_irqrestore(&card->mask_lock, flags); + return rc; +} /** * Initialize channels,card and state machines. @@ -337,34 +437,49 @@ lcs_setup_card(struct lcs_card *card) /* Initialize kernel thread task used for LGW commands. */ INIT_WORK(&card->kernel_thread_starter, (void *)lcs_start_kernel_thread,card); - card->thread_mask = 0; + card->thread_start_mask = 0; + card->thread_allowed_mask = 0; + card->thread_running_mask = 0; + init_waitqueue_head(&card->wait_q); spin_lock_init(&card->lock); spin_lock_init(&card->ipm_lock); + spin_lock_init(&card->mask_lock); #ifdef CONFIG_IP_MULTICAST INIT_LIST_HEAD(&card->ipm_list); #endif INIT_LIST_HEAD(&card->lancmd_waiters); } +static inline void +lcs_clear_multicast_list(struct lcs_card *card) +{ +#ifdef CONFIG_IP_MULTICAST + struct list_head *l, *n; + struct lcs_ipm_list *ipm; + unsigned long flags; + /* Free multicast list. */ + LCS_DBF_TEXT(3, setup, "clmclist"); + spin_lock_irqsave(&card->ipm_lock, flags); + list_for_each_safe(l, n, &card->ipm_list) { + ipm = list_entry(l, struct lcs_ipm_list, list); + list_del(&ipm->list); + if (ipm->ipm_state != LCS_IPM_STATE_SET_REQUIRED) + lcs_send_delipm(card, ipm); + kfree(ipm); + } + spin_unlock_irqrestore(&card->ipm_lock, flags); +#endif +} /** * Cleanup channels,card and state machines. */ static void lcs_cleanup_card(struct lcs_card *card) { - struct list_head *l, *n; - struct lcs_ipm_list *ipm_list; LCS_DBF_TEXT(3, setup, "cleancrd"); LCS_DBF_HEX(2,setup,&card,sizeof(void*)); -#ifdef CONFIG_IP_MULTICAST - /* Free multicast list. */ - list_for_each_safe(l, n, &card->ipm_list) { - ipm_list = list_entry(l, struct lcs_ipm_list, list); - list_del(&ipm_list->list); - kfree(ipm_list); - } -#endif + if (card->dev != NULL) free_netdev(card->dev); /* Cleanup channels. */ @@ -651,6 +766,44 @@ lcs_get_lancmd(struct lcs_card *card, in return buffer; } + +static void +lcs_get_reply(struct lcs_reply *reply) +{ + WARN_ON(atomic_read(&reply->refcnt) <= 0); + atomic_inc(&reply->refcnt); +} + +static void +lcs_put_reply(struct lcs_reply *reply) +{ + WARN_ON(atomic_read(&reply->refcnt) <= 0); + if (atomic_dec_and_test(&reply->refcnt)) { + kfree(reply); + } + +} + +static struct lcs_reply * +lcs_alloc_reply(struct lcs_cmd *cmd) +{ + struct lcs_reply *reply; + + LCS_DBF_TEXT(4, trace, "getreply"); + + reply = kmalloc(sizeof(struct lcs_reply), GFP_ATOMIC); + if (!reply) + return NULL; + memset(reply,0,sizeof(struct lcs_reply)); + atomic_set(&reply->refcnt,1); + reply->sequence_no = cmd->sequence_no; + reply->received = 0; + reply->rc = 0; + init_waitqueue_head(&reply->wait_q); + + return reply; +} + /** * Notifier function for lancmd replies. Called from read irq. */ @@ -665,12 +818,14 @@ lcs_notify_lancmd_waiters(struct lcs_car list_for_each_safe(l, n, &card->lancmd_waiters) { reply = list_entry(l, struct lcs_reply, list); if (reply->sequence_no == cmd->sequence_no) { - list_del(&reply->list); + lcs_get_reply(reply); + list_del_init(&reply->list); if (reply->callback != NULL) reply->callback(card, cmd); reply->received = 1; reply->rc = cmd->return_code; wake_up(&reply->wait_q); + lcs_put_reply(reply); break; } } @@ -683,50 +838,66 @@ lcs_notify_lancmd_waiters(struct lcs_car void lcs_lancmd_timeout(unsigned long data) { - struct lcs_reply *reply; + struct lcs_reply *reply, *list_reply, *r; + unsigned long flags; LCS_DBF_TEXT(4, trace, "timeout"); reply = (struct lcs_reply *) data; - list_del(&reply->list); - reply->received = 1; - reply->rc = -ETIME; - wake_up(&reply->wait_q); + spin_lock_irqsave(&reply->card->lock, flags); + list_for_each_entry_safe(list_reply, r, + &reply->card->lancmd_waiters,list) { + if (reply == list_reply) { + lcs_get_reply(reply); + list_del_init(&reply->list); + spin_unlock_irqrestore(&reply->card->lock, flags); + reply->received = 1; + reply->rc = -ETIME; + wake_up(&reply->wait_q); + lcs_put_reply(reply); + return; + } + } + spin_unlock_irqrestore(&reply->card->lock, flags); } static int lcs_send_lancmd(struct lcs_card *card, struct lcs_buffer *buffer, void (*reply_callback)(struct lcs_card *, struct lcs_cmd *)) { - struct lcs_reply reply; + struct lcs_reply *reply; struct lcs_cmd *cmd; struct timer_list timer; + unsigned long flags; int rc; LCS_DBF_TEXT(4, trace, "sendcmd"); cmd = (struct lcs_cmd *) buffer->data; - cmd->sequence_no = ++card->sequence_no; cmd->return_code = 0; - reply.sequence_no = cmd->sequence_no; - reply.callback = reply_callback; - reply.received = 0; - reply.rc = 0; - init_waitqueue_head(&reply.wait_q); - spin_lock(&card->lock); - list_add_tail(&reply.list, &card->lancmd_waiters); - spin_unlock(&card->lock); + cmd->sequence_no = card->sequence_no++; + reply = lcs_alloc_reply(cmd); + if (!reply) + return -ENOMEM; + reply->callback = reply_callback; + reply->card = card; + spin_lock_irqsave(&card->lock, flags); + list_add_tail(&reply->list, &card->lancmd_waiters); + spin_unlock_irqrestore(&card->lock, flags); + buffer->callback = lcs_release_buffer; rc = lcs_ready_buffer(&card->write, buffer); if (rc) return rc; init_timer(&timer); timer.function = lcs_lancmd_timeout; - timer.data = (unsigned long) &reply; + timer.data = (unsigned long) reply; timer.expires = jiffies + HZ*card->lancmd_timeout; add_timer(&timer); - wait_event(reply.wait_q, reply.received); - del_timer(&timer); - LCS_DBF_TEXT_(4, trace, "rc:%d",reply.rc); - return reply.rc ? -EIO : 0; + wait_event(reply->wait_q, reply->received); + del_timer_sync(&timer); + LCS_DBF_TEXT_(4, trace, "rc:%d",reply->rc); + rc = reply->rc; + lcs_put_reply(reply); + return rc ? -EIO : 0; } /** @@ -942,9 +1113,10 @@ lcs_fix_multicast_list(struct lcs_card * { struct list_head *l, *n; struct lcs_ipm_list *ipm; + unsigned long flags; LCS_DBF_TEXT(4,trace, "fixipm"); - spin_lock(&card->ipm_lock); + spin_lock_irqsave(&card->ipm_lock, flags); list_for_each_safe(l, n, &card->ipm_list) { ipm = list_entry(l, struct lcs_ipm_list, list); switch (ipm->ipm_state) { @@ -964,9 +1136,9 @@ lcs_fix_multicast_list(struct lcs_card * break; } } + spin_unlock_irqrestore(&card->ipm_lock, flags); if (card->state == DEV_STATE_UP) netif_wake_queue(card->dev); - spin_unlock(&card->ipm_lock); } /** @@ -985,51 +1157,67 @@ lcs_get_mac_for_ipm(__u32 ipm, char *mac /** * function called by net device to handle multicast address relevant things */ -static int -lcs_register_mc_addresses(void *data) +static inline void +lcs_remove_mc_addresses(struct lcs_card *card, struct in_device *in4_dev) { - struct lcs_card *card; - char buf[MAX_ADDR_LEN]; - struct list_head *l; struct ip_mc_list *im4; - struct in_device *in4_dev; - struct lcs_ipm_list *ipm, *tmp; - - daemonize("regipm"); - LCS_DBF_TEXT(4, trace, "regmulti"); + struct list_head *l; + struct lcs_ipm_list *ipm; + unsigned long flags; + char buf[MAX_ADDR_LEN]; - card = (struct lcs_card *) data; - in4_dev = in_dev_get(card->dev); - if (in4_dev == NULL) - return 0; - read_lock(&in4_dev->mc_list_lock); - spin_lock(&card->ipm_lock); - /* Check for multicast addresses to be removed. */ + LCS_DBF_TEXT(4, trace, "remmclst"); + spin_lock_irqsave(&card->ipm_lock, flags); list_for_each(l, &card->ipm_list) { ipm = list_entry(l, struct lcs_ipm_list, list); for (im4 = in4_dev->mc_list; im4 != NULL; im4 = im4->next) { lcs_get_mac_for_ipm(im4->multiaddr, buf, card->dev); - if (memcmp(buf, &ipm->ipm.mac_addr, - LCS_MAC_LENGTH) == 0 && - ipm->ipm.ip_addr == im4->multiaddr) + if ( (ipm->ipm.ip_addr == im4->multiaddr) && + (memcmp(buf, &ipm->ipm.mac_addr, + LCS_MAC_LENGTH) == 0) ) break; } if (im4 == NULL) ipm->ipm_state = LCS_IPM_STATE_DEL_REQUIRED; } - /* Check for multicast addresses to be added. */ + spin_unlock_irqrestore(&card->ipm_lock, flags); +} + +static inline struct lcs_ipm_list * +lcs_check_addr_entry(struct lcs_card *card, struct ip_mc_list *im4, char *buf) +{ + struct lcs_ipm_list *tmp, *ipm = NULL; + struct list_head *l; + unsigned long flags; + + LCS_DBF_TEXT(4, trace, "chkmcent"); + spin_lock_irqsave(&card->ipm_lock, flags); + list_for_each(l, &card->ipm_list) { + tmp = list_entry(l, struct lcs_ipm_list, list); + if ( (tmp->ipm.ip_addr == im4->multiaddr) && + (memcmp(buf, &tmp->ipm.mac_addr, + LCS_MAC_LENGTH) == 0) ) { + ipm = tmp; + break; + } + } + spin_unlock_irqrestore(&card->ipm_lock, flags); + return ipm; +} + +static inline void +lcs_set_mc_addresses(struct lcs_card *card, struct in_device *in4_dev) +{ + + struct ip_mc_list *im4; + struct lcs_ipm_list *ipm; + char buf[MAX_ADDR_LEN]; + unsigned long flags; + + LCS_DBF_TEXT(4, trace, "setmclst"); for (im4 = in4_dev->mc_list; im4; im4 = im4->next) { lcs_get_mac_for_ipm(im4->multiaddr, buf, card->dev); - ipm = NULL; - list_for_each(l, &card->ipm_list) { - tmp = list_entry(l, struct lcs_ipm_list, list); - if (memcmp(buf, &tmp->ipm.mac_addr, - LCS_MAC_LENGTH) == 0 && - tmp->ipm.ip_addr == im4->multiaddr) { - ipm = tmp; - break; - } - } + ipm = lcs_check_addr_entry(card, im4, buf); if (ipm != NULL) continue; /* Address already in list. */ ipm = (struct lcs_ipm_list *) @@ -1043,12 +1231,37 @@ lcs_register_mc_addresses(void *data) memcpy(&ipm->ipm.mac_addr, buf, LCS_MAC_LENGTH); ipm->ipm.ip_addr = im4->multiaddr; ipm->ipm_state = LCS_IPM_STATE_SET_REQUIRED; + spin_lock_irqsave(&card->ipm_lock, flags); list_add(&ipm->list, &card->ipm_list); + spin_unlock_irqrestore(&card->ipm_lock, flags); } - spin_unlock(&card->ipm_lock); +} + +static int +lcs_register_mc_addresses(void *data) +{ + struct lcs_card *card; + struct in_device *in4_dev; + + card = (struct lcs_card *) data; + daemonize("regipm"); + + if (!lcs_do_run_thread(card, LCS_SET_MC_THREAD)) + return 0; + LCS_DBF_TEXT(4, trace, "regmulti"); + + in4_dev = in_dev_get(card->dev); + if (in4_dev == NULL) + goto out; + read_lock(&in4_dev->mc_list_lock); + lcs_remove_mc_addresses(card,in4_dev); + lcs_set_mc_addresses(card, in4_dev); read_unlock(&in4_dev->mc_list_lock); in_dev_put(in4_dev); + lcs_fix_multicast_list(card); +out: + lcs_clear_thread_running_bit(card, LCS_SET_MC_THREAD); return 0; } /** @@ -1062,8 +1275,10 @@ lcs_set_multicast_list(struct net_device LCS_DBF_TEXT(4, trace, "setmulti"); card = (struct lcs_card *) dev->priv; - set_bit(3, &card->thread_mask); - schedule_work(&card->kernel_thread_starter); + + if (!lcs_set_thread_start_bit(card, LCS_SET_MC_THREAD)) { + schedule_work(&card->kernel_thread_starter); + } } #endif /* CONFIG_IP_MULTICAST */ @@ -1427,6 +1642,7 @@ lcs_resetcard(struct lcs_card *card) return -EIO; } + /** * LCS Stop card */ @@ -1440,6 +1656,7 @@ lcs_stopcard(struct lcs_card *card) if (card->read.state != CH_STATE_STOPPED && card->write.state != CH_STATE_STOPPED && card->state == DEV_STATE_UP) { + lcs_clear_multicast_list(card); rc = lcs_send_stoplan(card,LCS_INITIATOR_TCPIP); rc = lcs_send_shutdown(card); } @@ -1459,6 +1676,9 @@ lcs_lgw_startlan_thread(void *data) card = (struct lcs_card *) data; daemonize("lgwstpln"); + + if (!lcs_do_run_thread(card, LCS_STARTLAN_THREAD)) + return 0; LCS_DBF_TEXT(4, trace, "lgwstpln"); if (card->dev) netif_stop_queue(card->dev); @@ -1471,6 +1691,7 @@ lcs_lgw_startlan_thread(void *data) } else PRINT_ERR("LCS Startlan for device %s failed!\n", card->dev->name); + lcs_clear_thread_running_bit(card, LCS_STARTLAN_THREAD); return 0; } @@ -1485,8 +1706,11 @@ lcs_lgw_startup_thread(void *data) struct lcs_card *card; card = (struct lcs_card *) data; - daemonize("lgwstpln"); - LCS_DBF_TEXT(4, trace, "lgwstpln"); + daemonize("lgwstaln"); + + if (!lcs_do_run_thread(card, LCS_STARTUP_THREAD)) + return 0; + LCS_DBF_TEXT(4, trace, "lgwstaln"); if (card->dev) netif_stop_queue(card->dev); rc = lcs_send_startup(card, LCS_INITIATOR_LGW); @@ -1511,6 +1735,7 @@ Done: else PRINT_ERR("LCS Startup for device %s failed!\n", card->dev->name); + lcs_clear_thread_running_bit(card, LCS_STARTUP_THREAD); return 0; } @@ -1526,6 +1751,9 @@ lcs_lgw_stoplan_thread(void *data) card = (struct lcs_card *) data; daemonize("lgwstop"); + + if (!lcs_do_run_thread(card, LCS_STOPLAN_THREAD)) + return 0; LCS_DBF_TEXT(4, trace, "lgwstop"); if (card->dev) netif_stop_queue(card->dev); @@ -1539,6 +1767,7 @@ lcs_lgw_stoplan_thread(void *data) rc = lcs_resetcard(card); if (rc != 0) rc = lcs_stopcard(card); + lcs_clear_thread_running_bit(card, LCS_STOPLAN_THREAD); return rc; } @@ -1549,14 +1778,14 @@ static void lcs_start_kernel_thread(struct lcs_card *card) { LCS_DBF_TEXT(5, trace, "krnthrd"); - if (test_and_clear_bit(0, &card->thread_mask)) + if (lcs_do_start_thread(card, LCS_STARTUP_THREAD)) kernel_thread(lcs_lgw_startup_thread, (void *) card, SIGCHLD); - if (test_and_clear_bit(1, &card->thread_mask)) + if (lcs_do_start_thread(card, LCS_STARTLAN_THREAD)) kernel_thread(lcs_lgw_startlan_thread, (void *) card, SIGCHLD); - if (test_and_clear_bit(2, &card->thread_mask)) + if (lcs_do_start_thread(card, LCS_STOPLAN_THREAD)) kernel_thread(lcs_lgw_stoplan_thread, (void *) card, SIGCHLD); #ifdef CONFIG_IP_MULTICAST - if (test_and_clear_bit(3, &card->thread_mask)) + if (lcs_do_start_thread(card, LCS_SET_MC_THREAD)) kernel_thread(lcs_register_mc_addresses, (void *) card, SIGCHLD); #endif } @@ -1571,16 +1800,19 @@ lcs_get_control(struct lcs_card *card, s if (cmd->initiator == LCS_INITIATOR_LGW) { switch(cmd->cmd_code) { case LCS_CMD_STARTUP: - set_bit(0, &card->thread_mask); - schedule_work(&card->kernel_thread_starter); + if (!lcs_set_thread_start_bit(card, + LCS_STARTUP_THREAD)) + schedule_work(&card->kernel_thread_starter); break; case LCS_CMD_STARTLAN: - set_bit(1, &card->thread_mask); - schedule_work(&card->kernel_thread_starter); + if (!lcs_set_thread_start_bit(card, + LCS_STARTLAN_THREAD)) + schedule_work(&card->kernel_thread_starter); break; case LCS_CMD_STOPLAN: - set_bit(2, &card->thread_mask); - schedule_work(&card->kernel_thread_starter); + if (!lcs_set_thread_start_bit(card, + LCS_STOPLAN_THREAD)) + schedule_work(&card->kernel_thread_starter); break; default: PRINT_INFO("UNRECOGNIZED LGW COMMAND\n"); @@ -1953,7 +2185,9 @@ netdev_out: card->dev->set_multicast_list = lcs_set_multicast_list; #endif netif_stop_queue(card->dev); + lcs_set_allowed_threads(card,0xffffffff); if (recover_state == DEV_STATE_RECOVER) { + lcs_set_multicast_list(card->dev); card->dev->flags |= IFF_UP; netif_wake_queue(card->dev); card->state = DEV_STATE_UP; @@ -1982,7 +2216,9 @@ lcs_shutdown_device(struct ccwgroup_devi card = (struct lcs_card *)ccwgdev->dev.driver_data; if (!card) return -ENODEV; - + lcs_set_allowed_threads(card, 0); + if (lcs_wait_for_threads(card, LCS_SET_MC_THREAD)) + return -ERESTARTSYS; LCS_DBF_HEX(3, setup, &card, sizeof(void*)); recover_state = card->state; --- linux-2.6.9-rc1/drivers/s390/net/lcs.h 2004-08-24 00:03:19.000000000 -0700 +++ 25/drivers/s390/net/lcs.h 2004-09-02 20:51:52.000000000 -0700 @@ -6,7 +6,7 @@ #include #include -#define VERSION_LCS_H "$Revision: 1.17 $" +#define VERSION_LCS_H "$Revision: 1.18 $" #define LCS_DBF_TEXT(level, name, text) \ do { \ @@ -152,6 +152,12 @@ enum lcs_dev_states { DEV_STATE_RECOVER, }; +enum lcs_threads { + LCS_SET_MC_THREAD = 1, + LCS_STARTLAN_THREAD = 2, + LCS_STOPLAN_THREAD = 4, + LCS_STARTUP_THREAD = 8, +}; /** * LCS struct declarations */ @@ -247,9 +253,11 @@ struct lcs_buffer { struct lcs_reply { struct list_head list; __u16 sequence_no; + atomic_t refcnt; /* Callback for completion notification. */ void (*callback)(struct lcs_card *, struct lcs_cmd *); wait_queue_head_t wait_q; + struct lcs_card *card; int received; int rc; }; @@ -268,6 +276,7 @@ struct lcs_channel { int buf_idx; }; + /** * definition of the lcs card */ @@ -287,7 +296,11 @@ struct lcs_card { int lancmd_timeout; struct work_struct kernel_thread_starter; - unsigned long thread_mask; + spinlock_t mask_lock; + unsigned long thread_start_mask; + unsigned long thread_running_mask; + unsigned long thread_allowed_mask; + wait_queue_head_t wait_q; #ifdef CONFIG_IP_MULTICAST struct list_head ipm_list; --- linux-2.6.9-rc1/drivers/s390/net/qeth_main.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/s390/net/qeth_main.c 2004-09-02 20:51:52.000000000 -0700 @@ -1,6 +1,6 @@ /* * - * linux/drivers/s390/net/qeth_main.c ($Revision: 1.130 $) + * linux/drivers/s390/net/qeth_main.c ($Revision: 1.132 $) * * Linux on zSeries OSA Express and HiperSockets support * @@ -12,7 +12,7 @@ * Frank Pavlic (pavlic@de.ibm.com) and * Thomas Spatzier * - * $Revision: 1.130 $ $Date: 2004/08/05 11:21:50 $ + * $Revision: 1.132 $ $Date: 2004/08/19 12:39:43 $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -79,7 +79,7 @@ qeth_eyecatcher(void) #include "qeth_mpc.h" #include "qeth_fs.h" -#define VERSION_QETH_C "$Revision: 1.130 $" +#define VERSION_QETH_C "$Revision: 1.132 $" static const char *version = "qeth S/390 OSA-Express driver"; /** @@ -4547,7 +4547,8 @@ qeth_do_ioctl(struct net_device *dev, st if (!card) return -ENODEV; - if (card->state != CARD_STATE_UP) + if ((card->state != CARD_STATE_UP) && + (card->state != CARD_STATE_SOFTSETUP)) return -ENODEV; switch (cmd){ --- linux-2.6.9-rc1/drivers/s390/scsi/zfcp_aux.c 2004-08-24 00:03:49.000000000 -0700 +++ 25/drivers/s390/scsi/zfcp_aux.c 2004-09-02 20:51:52.000000000 -0700 @@ -29,7 +29,7 @@ */ /* this drivers version (do not edit !!! generated and updated by cvs) */ -#define ZFCP_AUX_REVISION "$Revision: 1.115 $" +#define ZFCP_AUX_REVISION "$Revision: 1.129 $" #include "zfcp_ext.h" @@ -47,11 +47,11 @@ static void zfcp_ns_gid_pn_handler(unsig /* miscellaneous */ static inline int zfcp_sg_list_alloc(struct zfcp_sg_list *, size_t); -static inline int zfcp_sg_list_free(struct zfcp_sg_list *); -static inline int zfcp_sg_list_copy_from_user(struct zfcp_sg_list *, void *, - size_t); -static inline int zfcp_sg_list_copy_to_user(void *, struct zfcp_sg_list *, - size_t); +static inline void zfcp_sg_list_free(struct zfcp_sg_list *); +static inline int zfcp_sg_list_copy_from_user(struct zfcp_sg_list *, + void __user *, size_t); +static inline int zfcp_sg_list_copy_to_user(void __user *, + struct zfcp_sg_list *, size_t); static int zfcp_cfdc_dev_ioctl(struct inode *, struct file *, unsigned int, unsigned long); @@ -95,7 +95,7 @@ MODULE_PARM_DESC(device, "specify initia module_param(loglevel, uint, 0); MODULE_PARM_DESC(loglevel, "log levels, 8 nibbles: " - "(unassigned) ERP QDIO DIO Config FSF SCSI Other, " + "FC ERP QDIO CIO Config FSF SCSI Other, " "levels: 0=none 1=normal 2=devel 3=trace"); #ifdef ZFCP_PRINT_FLAGS @@ -257,24 +257,20 @@ zfcp_device_setup(char *str) static void __init zfcp_init_device_configure(void) { - int found = 0; struct zfcp_adapter *adapter; struct zfcp_port *port; struct zfcp_unit *unit; down(&zfcp_data.config_sema); read_lock_irq(&zfcp_data.config_lock); - list_for_each_entry(adapter, &zfcp_data.adapter_list_head, list) - if (strcmp(zfcp_data.init_busid, - zfcp_get_busid_by_adapter(adapter)) == 0) { - zfcp_adapter_get(adapter); - found = 1; - break; - } + adapter = zfcp_get_adapter_by_busid(zfcp_data.init_busid); + if (adapter) + zfcp_adapter_get(adapter); read_unlock_irq(&zfcp_data.config_lock); - if (!found) + + if (adapter == NULL) goto out_adapter; - port = zfcp_port_enqueue(adapter, zfcp_data.init_wwpn, 0); + port = zfcp_port_enqueue(adapter, zfcp_data.init_wwpn, 0, 0); if (!port) goto out_port; unit = zfcp_unit_enqueue(port, zfcp_data.init_fcp_lun); @@ -377,12 +373,13 @@ zfcp_module_init(void) * -ENOMEM - Insufficient memory * -EFAULT - User space memory I/O operation fault * -EPERM - Cannot create or queue FSF request or create SBALs + * -ERESTARTSYS- Received signal (is mapped to EAGAIN by VFS) */ static int zfcp_cfdc_dev_ioctl(struct inode *inode, struct file *file, unsigned int command, unsigned long buffer) { - struct zfcp_cfdc_sense_data sense_data, *sense_data_user; + struct zfcp_cfdc_sense_data sense_data, __user *sense_data_user; struct zfcp_adapter *adapter = NULL; struct zfcp_fsf_req *fsf_req = NULL; struct zfcp_sg_list *sg_list = NULL; @@ -403,7 +400,7 @@ zfcp_cfdc_dev_ioctl(struct inode *inode, goto out; } - if ((sense_data_user = (struct zfcp_cfdc_sense_data*)buffer) == NULL) { + if ((sense_data_user = (void __user *) buffer) == NULL) { ZFCP_LOG_INFO("sense data record is required\n"); retval = -EINVAL; goto out; @@ -467,22 +464,17 @@ zfcp_cfdc_dev_ioctl(struct inode *inode, (sense_data.devno >> 16) & 0xFF, (sense_data.devno & 0xFFFF)); - retval = -ENXIO; read_lock_irq(&zfcp_data.config_lock); - list_for_each_entry(adapter, &zfcp_data.adapter_list_head, list) { - if (strncmp(bus_id, zfcp_get_busid_by_adapter(adapter), - BUS_ID_SIZE) == 0) { - zfcp_adapter_get(adapter); - retval = 0; - break; - } - } + adapter = zfcp_get_adapter_by_busid(bus_id); + if (adapter) + zfcp_adapter_get(adapter); read_unlock_irq(&zfcp_data.config_lock); kfree(bus_id); - if (retval != 0) { + if (adapter == NULL) { ZFCP_LOG_INFO("invalid adapter\n"); + retval = -ENXIO; goto out; } @@ -520,6 +512,12 @@ zfcp_cfdc_dev_ioctl(struct inode *inode, wait_event(fsf_req->completion_wq, fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED); + if ((fsf_req->qtcb->prefix.prot_status != FSF_PROT_GOOD) && + (fsf_req->qtcb->prefix.prot_status != FSF_PROT_FSF_STATUS_PRESENTED)) { + retval = -ENXIO; + goto out; + } + sense_data.fsf_status = fsf_req->qtcb->header.fsf_status; memcpy(&sense_data.fsf_status_qual, &fsf_req->qtcb->header.fsf_status_qual, @@ -559,13 +557,16 @@ zfcp_cfdc_dev_ioctl(struct inode *inode, } -/* - * function: zfcp_sg_list_alloc - * - * purpose: Create a scatter-gather list of the specified size +/** + * zfcp_sg_list_alloc - create a scatter-gather list of the specified size + * @sg_list: structure describing a scatter gather list + * @size: size of scatter-gather list + * Return: 0 on success, else -ENOMEM * - * returns: 0 - Scatter gather list is created - * -ENOMEM - Insufficient memory (*list_ptr is then set to NULL) + * In sg_list->sg a pointer to the created scatter-gather list is returned, + * or NULL if we run out of memory. sg_list->count specifies the number of + * elements of the scatter-gather list. The maximum size of a single element + * in the scatter-gather list is PAGE_SIZE. */ static inline int zfcp_sg_list_alloc(struct zfcp_sg_list *sg_list, size_t size) @@ -573,6 +574,9 @@ zfcp_sg_list_alloc(struct zfcp_sg_list * struct scatterlist *sg; unsigned int i; int retval = 0; + void *address; + + BUG_ON(sg_list == NULL); sg_list->count = size >> PAGE_SHIFT; if (size & ~PAGE_MASK) @@ -588,7 +592,8 @@ zfcp_sg_list_alloc(struct zfcp_sg_list * for (i = 0, sg = sg_list->sg; i < sg_list->count; i++, sg++) { sg->length = min(size, PAGE_SIZE); sg->offset = 0; - sg->page = alloc_pages(GFP_KERNEL, 0); + address = (void *) get_zeroed_page(GFP_KERNEL); + zfcp_address_to_sg(address, sg); if (sg->page == NULL) { sg_list->count = i; zfcp_sg_list_free(sg_list); @@ -603,41 +608,61 @@ zfcp_sg_list_alloc(struct zfcp_sg_list * } -/* - * function: zfcp_sg_list_free - * - * purpose: Destroy a scatter-gather list and release memory +/** + * zfcp_sg_list_free - free memory of a scatter-gather list + * @sg_list: structure describing a scatter-gather list * - * returns: Always 0 + * Memory for each element in the scatter-gather list is freed. + * Finally sg_list->sg is freed itself and sg_list->count is reset. */ -static inline int +static inline void zfcp_sg_list_free(struct zfcp_sg_list *sg_list) { struct scatterlist *sg; unsigned int i; - int retval = 0; BUG_ON(sg_list == NULL); for (i = 0, sg = sg_list->sg; i < sg_list->count; i++, sg++) __free_pages(sg->page, 0); + sg_list->count = 0; kfree(sg_list->sg); +} - return retval; +/** + * zfcp_sg_size - determine size of a scatter-gather list + * @sg: array of (struct scatterlist) + * @sg_count: elements in array + * Return: size of entire scatter-gather list + */ +size_t +zfcp_sg_size(struct scatterlist *sg, unsigned int sg_count) +{ + unsigned int i; + struct scatterlist *p; + size_t size; + + size = 0; + for (i = 0, p = sg; i < sg_count; i++, p++) { + BUG_ON(p == NULL); + size += p->length; + } + + return size; } -/* - * function: zfcp_sg_list_copy_from_user - * - * purpose: Copy data from user space memory to the scatter-gather list - * - * returns: 0 - The data has been copied from user - * -EFAULT - Memory I/O operation fault +/** + * zfcp_sg_list_copy_from_user -copy data from user space to scatter-gather list + * @sg_list: structure describing a scatter-gather list + * @user_buffer: pointer to buffer in user space + * @size: number of bytes to be copied + * Return: 0 on success, -EFAULT if copy_from_user fails. */ static inline int -zfcp_sg_list_copy_from_user(struct zfcp_sg_list *sg_list, void *user_buffer, +zfcp_sg_list_copy_from_user(struct zfcp_sg_list *sg_list, + void __user *user_buffer, size_t size) { struct scatterlist *sg; @@ -645,10 +670,14 @@ zfcp_sg_list_copy_from_user(struct zfcp_ void *zfcp_buffer; int retval = 0; + BUG_ON(sg_list == NULL); + + if (zfcp_sg_size(sg_list->sg, sg_list->count) < size) + return -EFAULT; + for (sg = sg_list->sg; size > 0; sg++) { length = min((unsigned int)size, sg->length); - zfcp_buffer = (void*) - ((page_to_pfn(sg->page) << PAGE_SHIFT) + sg->offset); + zfcp_buffer = zfcp_sg_to_address(sg); if (copy_from_user(zfcp_buffer, user_buffer, length)) { retval = -EFAULT; goto out; @@ -662,16 +691,16 @@ zfcp_sg_list_copy_from_user(struct zfcp_ } -/* - * function: zfcp_sg_list_copy_to_user - * - * purpose: Copy data from the scatter-gather list to user space memory - * - * returns: 0 - The data has been copied to user - * -EFAULT - Memory I/O operation fault +/** + * zfcp_sg_list_copy_to_user - copy data from scatter-gather list to user space + * @user_buffer: pointer to buffer in user space + * @sg_list: structure describing a scatter-gather list + * @size: number of bytes to be copied + * Return: 0 on success, -EFAULT if copy_to_user fails */ static inline int -zfcp_sg_list_copy_to_user(void *user_buffer, struct zfcp_sg_list *sg_list, +zfcp_sg_list_copy_to_user(void __user *user_buffer, + struct zfcp_sg_list *sg_list, size_t size) { struct scatterlist *sg; @@ -679,10 +708,14 @@ zfcp_sg_list_copy_to_user(void *user_buf void *zfcp_buffer; int retval = 0; + BUG_ON(sg_list == NULL); + + if (zfcp_sg_size(sg_list->sg, sg_list->count) < size) + return -EFAULT; + for (sg = sg_list->sg; size > 0; sg++) { - length = min((unsigned int)size, sg->length); - zfcp_buffer = (void*) - ((page_to_pfn(sg->page) << PAGE_SHIFT) + sg->offset); + length = min((unsigned int) size, sg->length); + zfcp_buffer = zfcp_sg_to_address(sg); if (copy_to_user(user_buffer, zfcp_buffer, length)) { retval = -EFAULT; goto out; @@ -705,13 +738,12 @@ zfcp_sg_list_copy_to_user(void *user_buf #define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG /** - * zfcp_get_unit_by_lun - find unit in unit list of port by fcp lun + * zfcp_get_unit_by_lun - find unit in unit list of port by FCP LUN * @port: pointer to port to search for unit - * @fcp_lun: lun to search for - * Traverses list of all units of a port and returns pointer to a unit - * if lun of a unit matches. + * @fcp_lun: FCP LUN to search for + * Traverse list of all units of a port and return pointer to a unit + * with the given FCP LUN. */ - struct zfcp_unit * zfcp_get_unit_by_lun(struct zfcp_port *port, fcp_lun_t fcp_lun) { @@ -730,13 +762,12 @@ zfcp_get_unit_by_lun(struct zfcp_port *p } /** - * zfcp_get_port_by_wwpn - find unit in unit list of port by fcp lun + * zfcp_get_port_by_wwpn - find port in port list of adapter by wwpn * @adapter: pointer to adapter to search for port * @wwpn: wwpn to search for - * Traverses list of all ports of an adapter and returns a pointer to a port - * if wwpn of a port matches. + * Traverse list of all ports of an adapter and return pointer to a port + * with the given wwpn. */ - struct zfcp_port * zfcp_get_port_by_wwpn(struct zfcp_adapter *adapter, wwn_t wwpn) { @@ -745,6 +776,30 @@ zfcp_get_port_by_wwpn(struct zfcp_adapte list_for_each_entry(port, &adapter->port_list_head, list) { if ((port->wwpn == wwpn) && + !(atomic_read(&port->status) & + (ZFCP_STATUS_PORT_NO_WWPN | ZFCP_STATUS_COMMON_REMOVE))) { + found = 1; + break; + } + } + return found ? port : NULL; +} + +/** + * zfcp_get_port_by_did - find port in port list of adapter by d_id + * @adapter: pointer to adapter to search for port + * @d_id: d_id to search for + * Traverse list of all ports of an adapter and return pointer to a port + * with the given d_id. + */ +struct zfcp_port * +zfcp_get_port_by_did(struct zfcp_adapter *adapter, u32 d_id) +{ + struct zfcp_port *port; + int found = 0; + + list_for_each_entry(port, &adapter->port_list_head, list) { + if ((port->d_id == d_id) && !atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status)) { found = 1; @@ -754,14 +809,38 @@ zfcp_get_port_by_wwpn(struct zfcp_adapte return found ? port : NULL; } -/* - * Enqueues a logical unit at the end of the unit list associated with the - * specified port. Also sets up some unit internal structures. +/** + * zfcp_get_adapter_by_busid - find adpater in adapter list by bus_id + * @bus_id: bus_id to search for + * Traverse list of all adapters and return pointer to an adapter + * with the given bus_id. + */ +struct zfcp_adapter * +zfcp_get_adapter_by_busid(char *bus_id) +{ + struct zfcp_adapter *adapter; + int found = 0; + + list_for_each_entry(adapter, &zfcp_data.adapter_list_head, list) { + if ((strncmp(bus_id, zfcp_get_busid_by_adapter(adapter), + BUS_ID_SIZE) == 0) && + !atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, + &adapter->status)){ + found = 1; + break; + } + } + return found ? adapter : NULL; +} + +/** + * zfcp_unit_enqueue - enqueue unit to unit list of a port. + * @port: pointer to port where unit is added + * @fcp_lun: FCP LUN of unit to be enqueued + * Return: pointer to enqueued unit on success, NULL on error + * Locks: config_sema must be held to serialize changes to the unit list * - * returns: pointer to unit with a usecount of 1 if a new unit was - * successfully enqueued - * NULL otherwise - * locks: config_sema must be held to serialise changes to the unit list + * Sets up some unit internal structures and creates sysfs entry. */ struct zfcp_unit * zfcp_unit_enqueue(struct zfcp_port *port, fcp_lun_t fcp_lun) @@ -1022,6 +1101,12 @@ zfcp_adapter_debug_unregister(struct zfc debug_unregister(adapter->in_els_dbf); } +void +zfcp_dummy_release(struct device *dev) +{ + return; +} + /* * Enqueues an adapter at the end of the adapter list in the driver data. * All adapter internal structures are set up. @@ -1113,6 +1198,14 @@ zfcp_adapter_enqueue(struct ccw_device * if (zfcp_sysfs_adapter_create_files(&ccw_device->dev)) goto sysfs_failed; + adapter->generic_services.parent = &adapter->ccw_device->dev; + adapter->generic_services.release = zfcp_dummy_release; + snprintf(adapter->generic_services.bus_id, BUS_ID_SIZE, + "generic_services"); + + if (device_register(&adapter->generic_services)) + goto generic_services_failed; + /* put allocated adapter at list tail */ write_lock_irq(&zfcp_data.config_lock); atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status); @@ -1123,6 +1216,8 @@ zfcp_adapter_enqueue(struct ccw_device * goto out; + generic_services_failed: + zfcp_sysfs_adapter_remove_files(&adapter->ccw_device->dev); sysfs_failed: dev_set_drvdata(&ccw_device->dev, NULL); failed_low_mem_buffers: @@ -1153,6 +1248,7 @@ zfcp_adapter_dequeue(struct zfcp_adapter int retval = 0; unsigned long flags; + device_unregister(&adapter->generic_services); zfcp_sysfs_adapter_remove_files(&adapter->ccw_device->dev); dev_set_drvdata(&adapter->ccw_device->dev, NULL); /* sanity check: no pending FSF requests */ @@ -1195,15 +1291,22 @@ zfcp_adapter_dequeue(struct zfcp_adapter return; } -/* - * Enqueues a remote port to the port list. All port internal structures - * are set up and the sysfs entry is also generated. +/** + * zfcp_port_enqueue - enqueue port to port list of adapter + * @adapter: adapter where remote port is added + * @wwpn: WWPN of the remote port to be enqueued + * @status: initial status for the port + * @d_id: destination id of the remote port to be enqueued + * Return: pointer to enqueued port on success, NULL on error + * Locks: config_sema must be held to serialize changes to the port list * - * returns: pointer to port or NULL - * locks: config_sema must be held to serialise changes to the port list + * All port internal structures are set up and the sysfs entry is generated. + * d_id is used to enqueue ports with a well known address like the Directory + * Service for nameserver lookup. */ struct zfcp_port * -zfcp_port_enqueue(struct zfcp_adapter *adapter, wwn_t wwpn, u32 status) +zfcp_port_enqueue(struct zfcp_adapter *adapter, wwn_t wwpn, u32 status, + u32 d_id) { struct zfcp_port *port, *tmp_port; int check_wwpn; @@ -1243,12 +1346,39 @@ zfcp_port_enqueue(struct zfcp_adapter *a atomic_set_mask(status, &port->status); /* setup for sysfs registration */ - if (status & ZFCP_STATUS_PORT_NAMESERVER) - snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE, "nameserver"); - else + if (status & ZFCP_STATUS_PORT_WKA) { + switch (d_id) { + case ZFCP_DID_DIRECTORY_SERVICE: + snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE, + "directory"); + break; + case ZFCP_DID_MANAGEMENT_SERVICE: + snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE, + "management"); + break; + case ZFCP_DID_KEY_DISTRIBUTION_SERVICE: + snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE, + "key_distribution"); + break; + case ZFCP_DID_ALIAS_SERVICE: + snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE, + "alias"); + break; + case ZFCP_DID_TIME_SERVICE: + snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE, + "time"); + break; + default: + kfree(port); + return NULL; + } + port->d_id = d_id; + port->sysfs_device.parent = &adapter->generic_services; + } else { snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE, "0x%016llx", wwpn); port->sysfs_device.parent = &adapter->ccw_device->dev; + } port->sysfs_device.release = zfcp_sysfs_port_release; dev_set_drvdata(&port->sysfs_device, port); @@ -1287,9 +1417,12 @@ zfcp_port_enqueue(struct zfcp_adapter *a list_add_tail(&port->list, &adapter->port_list_head); atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status); atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &port->status); + if (d_id == ZFCP_DID_DIRECTORY_SERVICE) + if (!adapter->nameserver_port) + adapter->nameserver_port = port; + adapter->ports++; write_unlock_irq(&zfcp_data.config_lock); - adapter->ports++; zfcp_adapter_get(adapter); return port; @@ -1301,8 +1434,8 @@ zfcp_port_dequeue(struct zfcp_port *port zfcp_port_wait(port); write_lock_irq(&zfcp_data.config_lock); list_del(&port->list); - write_unlock_irq(&zfcp_data.config_lock); port->adapter->ports--; + write_unlock_irq(&zfcp_data.config_lock); zfcp_adapter_put(port->adapter); zfcp_sysfs_port_remove_files(&port->sysfs_device, atomic_read(&port->status)); @@ -1315,17 +1448,14 @@ zfcp_nameserver_enqueue(struct zfcp_adap { struct zfcp_port *port; - /* generate port structure */ - port = zfcp_port_enqueue(adapter, 0, ZFCP_STATUS_PORT_NAMESERVER); + port = zfcp_port_enqueue(adapter, 0, ZFCP_STATUS_PORT_WKA, + ZFCP_DID_DIRECTORY_SERVICE); if (!port) { ZFCP_LOG_INFO("error: enqueue of nameserver port for " "adapter %s failed\n", zfcp_get_busid_by_adapter(adapter)); return -ENXIO; } - /* set special D_ID */ - port->d_id = ZFCP_DID_NAMESERVER; - adapter->nameserver_port = port; zfcp_port_put(port); return 0; @@ -1389,7 +1519,7 @@ zfcp_fsf_incoming_els_rscn(struct zfcp_a read_lock_irqsave(&zfcp_data.config_lock, flags); list_for_each_entry(port, &adapter->port_list_head, list) { if (atomic_test_mask - (ZFCP_STATUS_PORT_NAMESERVER, &port->status)) + (ZFCP_STATUS_PORT_WKA, &port->status)) continue; /* Do we know this port? If not skip it. */ if (!atomic_test_mask @@ -1646,15 +1776,7 @@ static void zfcp_ns_gid_pn_handler(unsig ct_iu_req = zfcp_sg_to_address(ct->req); ct_iu_resp = zfcp_sg_to_address(ct->resp); - if (ct_iu_resp->header.revision != ZFCP_CT_REVISION) - goto failed; - if (ct_iu_resp->header.gs_type != ZFCP_CT_DIRECTORY_SERVICE) - goto failed; - if (ct_iu_resp->header.gs_subtype != ZFCP_CT_NAME_SERVER) - goto failed; - if (ct_iu_resp->header.options != ZFCP_CT_SYNCHRONOUS) - goto failed; - if (ct_iu_resp->header.cmd_rsp_code != ZFCP_CT_ACCEPT) { + if ((ct->status != 0) || zfcp_check_ct_response(&ct_iu_resp->header)) { /* FIXME: do we need some specific erp entry points */ atomic_set_mask(ZFCP_STATUS_PORT_INVALID_WWPN, &port->status); goto failed; @@ -1665,7 +1787,7 @@ static void zfcp_ns_gid_pn_handler(unsig "lookup does not match expected wwpn 0x%016Lx " "for adapter %s\n", ct_iu_req->wwpn, port->wwpn, zfcp_get_busid_by_port(port)); - goto failed; + goto mismatch; } /* looks like a valid d_id */ @@ -1675,19 +1797,185 @@ static void zfcp_ns_gid_pn_handler(unsig zfcp_get_busid_by_port(port), port->wwpn, port->d_id); goto out; -failed: - ZFCP_LOG_NORMAL("warning: failed gid_pn nameserver request for wwpn " - "0x%016Lx for adapter %s\n", - port->wwpn, zfcp_get_busid_by_port(port)); + mismatch: ZFCP_LOG_DEBUG("CT IUs do not match:\n"); ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, (char *) ct_iu_req, sizeof(struct ct_iu_gid_pn_req)); ZFCP_HEX_DUMP(ZFCP_LOG_LEVEL_DEBUG, (char *) ct_iu_resp, sizeof(struct ct_iu_gid_pn_resp)); + failed: + ZFCP_LOG_NORMAL("warning: failed gid_pn nameserver request for wwpn " + "0x%016Lx for adapter %s\n", + port->wwpn, zfcp_get_busid_by_port(port)); out: zfcp_gid_pn_buffers_free(gid_pn); return; } +/* reject CT_IU reason codes acc. to FC-GS-4 */ +static const struct zfcp_rc_entry zfcp_ct_rc[] = { + {0x01, "invalid command code"}, + {0x02, "invalid version level"}, + {0x03, "logical error"}, + {0x04, "invalid CT_IU size"}, + {0x05, "logical busy"}, + {0x07, "protocol error"}, + {0x09, "unable to perform command request"}, + {0x0b, "command not supported"}, + {0x0d, "server not available"}, + {0x0e, "session could not be established"}, + {0xff, "vendor specific error"}, + {0, NULL}, +}; + +/* LS_RJT reason codes acc. to FC-FS */ +static const struct zfcp_rc_entry zfcp_ls_rjt_rc[] = { + {0x01, "invalid LS_Command code"}, + {0x03, "logical error"}, + {0x05, "logical busy"}, + {0x07, "protocol error"}, + {0x09, "unable to perform command request"}, + {0x0b, "command not supported"}, + {0x0e, "command already in progress"}, + {0xff, "vendor specific error"}, + {0, NULL}, +}; + +/* reject reason codes according to FC-PH/FC-FS */ +static const struct zfcp_rc_entry zfcp_p_rjt_rc[] = { + {0x01, "invalid D_ID"}, + {0x02, "invalid S_ID"}, + {0x03, "Nx_Port not available, temporary"}, + {0x04, "Nx_Port not available, permament"}, + {0x05, "class not supported"}, + {0x06, "delimiter usage error"}, + {0x07, "TYPE not supported"}, + {0x08, "invalid Link_Control"}, + {0x09, "invalid R_CTL field"}, + {0x0a, "invalid F_CTL field"}, + {0x0b, "invalid OX_ID"}, + {0x0c, "invalid RX_ID"}, + {0x0d, "invalid SEQ_ID"}, + {0x0e, "invalid DF_CTL"}, + {0x0f, "invalid SEQ_CNT"}, + {0x10, "invalid parameter field"}, + {0x11, "exchange error"}, + {0x12, "protocol error"}, + {0x13, "incorrect length"}, + {0x14, "unsupported ACK"}, + {0x15, "class of service not supported by entity at FFFFFE"}, + {0x16, "login required"}, + {0x17, "excessive sequences attempted"}, + {0x18, "unable to establish exchange"}, + {0x1a, "fabric path not available"}, + {0x1b, "invalid VC_ID (class 4)"}, + {0x1c, "invalid CS_CTL field"}, + {0x1d, "insufficient resources for VC (class 4)"}, + {0x1f, "invalid class of service"}, + {0x20, "preemption request rejected"}, + {0x21, "preemption not enabled"}, + {0x22, "multicast error"}, + {0x23, "multicast error terminate"}, + {0x24, "process login required"}, + {0xff, "vendor specific reject"}, + {0, NULL}, +}; + +/** + * zfcp_rc_description - return description for given reaon code + * @code: reason code + * @rc_table: table of reason codes and descriptions + */ +static inline const char * +zfcp_rc_description(u8 code, const struct zfcp_rc_entry *rc_table) +{ + const char *descr = "unknown reason code"; + + do { + if (code == rc_table->code) { + descr = rc_table->description; + break; + } + rc_table++; + } while (rc_table->code && rc_table->description); + + return descr; +} + +/** + * zfcp_check_ct_response - evaluate reason code for CT_IU + * @rjt: response payload to an CT_IU request + * Return: 0 for accept CT_IU, 1 for reject CT_IU or invlid response code + */ +int +zfcp_check_ct_response(struct ct_hdr *rjt) +{ + if (rjt->cmd_rsp_code == ZFCP_CT_ACCEPT) + return 0; + + if (rjt->cmd_rsp_code != ZFCP_CT_REJECT) { + ZFCP_LOG_NORMAL("error: invalid Generic Service command/" + "response code (0x%04hx)\n", + rjt->cmd_rsp_code); + return 1; + } + + ZFCP_LOG_INFO("Generic Service command rejected\n"); + ZFCP_LOG_INFO("%s (0x%02x, 0x%02x, 0x%02x)\n", + zfcp_rc_description(rjt->reason_code, zfcp_ct_rc), + (u32) rjt->reason_code, (u32) rjt->reason_code_expl, + (u32) rjt->vendor_unique); + + return 1; +} + +/** + * zfcp_print_els_rjt - print reject parameter and description for ELS reject + * @rjt_par: reject parameter acc. to FC-PH/FC-FS + * @rc_table: table of reason codes and descriptions + */ +static inline void +zfcp_print_els_rjt(struct zfcp_ls_rjt_par *rjt_par, + const struct zfcp_rc_entry *rc_table) +{ + ZFCP_LOG_INFO("%s (%02x %02x %02x %02x)\n", + zfcp_rc_description(rjt_par->reason_code, rc_table), + (u32) rjt_par->action, (u32) rjt_par->reason_code, + (u32) rjt_par->reason_expl, (u32) rjt_par->vendor_unique); +} + +/** + * zfcp_fsf_handle_els_rjt - evaluate status qualifier/reason code on ELS reject + * @sq: status qualifier word + * @rjt_par: reject parameter as described in FC-PH and FC-FS + * Return: -EROMTEIO for LS_RJT, -EREMCHG for invalid D_ID, -EIO else + */ +int +zfcp_handle_els_rjt(u32 sq, struct zfcp_ls_rjt_par *rjt_par) +{ + int ret = -EIO; + + if (sq == FSF_IOSTAT_NPORT_RJT) { + ZFCP_LOG_INFO("ELS rejected (P_RJT)\n"); + zfcp_print_els_rjt(rjt_par, zfcp_p_rjt_rc); + /* invalid d_id */ + if (rjt_par->reason_code == 0x01) + ret = -EREMCHG; + } else if (sq == FSF_IOSTAT_FABRIC_RJT) { + ZFCP_LOG_INFO("ELS rejected (F_RJT)\n"); + zfcp_print_els_rjt(rjt_par, zfcp_p_rjt_rc); + /* invalid d_id */ + if (rjt_par->reason_code == 0x01) + ret = -EREMCHG; + } else if (sq == FSF_IOSTAT_LS_RJT) { + ZFCP_LOG_INFO("ELS rejected (LS_RJT)\n"); + zfcp_print_els_rjt(rjt_par, zfcp_ls_rjt_rc); + ret = -EREMOTEIO; + } else + ZFCP_LOG_INFO("unexpected SQ: 0x%02x\n", sq); + + return ret; +} + #undef ZFCP_LOG_AREA --- linux-2.6.9-rc1/drivers/s390/scsi/zfcp_def.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/s390/scsi/zfcp_def.h 2004-09-02 20:51:52.000000000 -0700 @@ -33,7 +33,7 @@ #define ZFCP_DEF_H /* this drivers version (do not edit !!! generated and updated by cvs) */ -#define ZFCP_DEF_REVISION "$Revision: 1.81 $" +#define ZFCP_DEF_REVISION "$Revision: 1.91 $" /*************************** INCLUDES *****************************************/ @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -72,12 +73,22 @@ /* zfcp version number, it consists of major, minor, and patch-level number */ #define ZFCP_VERSION "4.1.3" +/** + * zfcp_sg_to_address - determine kernel address from struct scatterlist + * @list: struct scatterlist + * Return: kernel address + */ static inline void * zfcp_sg_to_address(struct scatterlist *list) { return (void *) (page_address(list->page) + list->offset); } +/** + * zfcp_address_to_sg - set up struct scatterlist from kernel address + * @address: kernel address + * @list: struct scatterlist + */ static inline void zfcp_address_to_sg(void *address, struct scatterlist *list) { @@ -146,6 +157,9 @@ typedef u32 scsi_lun_t; #define ZFCP_EXCHANGE_CONFIG_DATA_RETRIES 6 #define ZFCP_EXCHANGE_CONFIG_DATA_SLEEP 50 +/* timeout value for "default timer" for fsf requests */ +#define ZFCP_FSF_REQUEST_TIMEOUT (60*HZ); + /*************** FIBRE CHANNEL PROTOCOL SPECIFIC DEFINES ********************/ typedef unsigned long long wwn_t; @@ -158,7 +172,6 @@ typedef unsigned int fcp_dl_t; /* timeout for name-server lookup (in seconds) */ #define ZFCP_NS_GID_PN_TIMEOUT 10 -#define ZFCP_NS_GA_NXT_TIMEOUT 120 /* largest SCSI command we can process */ /* FCP-2 (FCP_CMND IU) allows up to (255-3+16) */ @@ -276,33 +289,17 @@ struct fcp_logo { #define R_A_TOV 10 /* seconds */ #define ZFCP_ELS_TIMEOUT (2 * R_A_TOV) -#define ZFCP_LS_RJT 0x01 -#define ZFCP_LS_ACC 0x02 #define ZFCP_LS_RTV 0x0E #define ZFCP_LS_RLS 0x0F #define ZFCP_LS_PDISC 0x50 #define ZFCP_LS_ADISC 0x52 -#define ZFCP_LS_RSCN 0x61 -#define ZFCP_LS_RNID 0x78 -#define ZFCP_LS_RLIR 0x7A #define ZFCP_LS_RTV_E_D_TOV_FLAG 0x04000000 -/* LS_ACC Reason Codes */ -#define ZFCP_LS_RJT_INVALID_COMMAND_CODE 0x01 -#define ZFCP_LS_RJT_LOGICAL_ERROR 0x03 -#define ZFCP_LS_RJT_LOGICAL_BUSY 0x05 -#define ZFCP_LS_RJT_PROTOCOL_ERROR 0x07 -#define ZFCP_LS_RJT_UNABLE_TO_PERFORM 0x09 -#define ZFCP_LS_RJT_COMMAND_NOT_SUPPORTED 0x0B -#define ZFCP_LS_RJT_VENDOR_UNIQUE_ERROR 0xFF - -struct zfcp_ls_rjt { - u8 code; - u8 field[3]; - u8 reserved; - u8 reason_code; - u8 reason_expl; - u8 vendor_unique; +struct zfcp_ls_rjt_par { + u8 action; + u8 reason_code; + u8 reason_expl; + u8 vendor_unique; } __attribute__ ((packed)); struct zfcp_ls_rtv { @@ -383,45 +380,10 @@ struct zfcp_ls_adisc_acc { fc_id_t nport_id; } __attribute__ ((packed)); -struct zfcp_ls_rnid { - u8 code; - u8 field[3]; - u8 node_id_format; - u8 reserved[3]; -} __attribute__((packed)); - -/* common identification data */ -struct zfcp_ls_rnid_common_id { - u64 n_port_name; - u64 node_name; -} __attribute__((packed)); - -/* general topology specific identification data */ -struct zfcp_ls_rnid_general_topology_id { - u8 vendor_unique[16]; - u32 associated_type; - u32 physical_port_number; - u32 nr_attached_nodes; - u8 node_management; - u8 ip_version; - u16 port_number; - u8 ip_address[16]; - u8 reserved[2]; - u16 vendor_specific; -} __attribute__((packed)); - -struct zfcp_ls_rnid_acc { - u8 code; - u8 field[3]; - u8 node_id_format; - u8 common_id_length; - u8 reserved; - u8 specific_id_length; - struct zfcp_ls_rnid_common_id - common_id; - struct zfcp_ls_rnid_general_topology_id - specific_id; -} __attribute__((packed)); +struct zfcp_rc_entry { + u8 code; + const char *description; +}; /* * FC-GS-2 stuff @@ -431,9 +393,9 @@ struct zfcp_ls_rnid_acc { #define ZFCP_CT_NAME_SERVER 0x02 #define ZFCP_CT_SYNCHRONOUS 0x00 #define ZFCP_CT_GID_PN 0x0121 -#define ZFCP_CT_GA_NXT 0x0100 #define ZFCP_CT_MAX_SIZE 0x1020 #define ZFCP_CT_ACCEPT 0x8002 +#define ZFCP_CT_REJECT 0x8001 /* * FC-GS-4 stuff @@ -530,23 +492,29 @@ struct zfcp_ls_rnid_acc { __LINE__ , ##args); #define ZFCP_LOG(level, fmt, args...) \ +do { \ if (ZFCP_LOG_CHECK(level)) \ - _ZFCP_LOG(fmt , ##args) + _ZFCP_LOG(fmt, ##args); \ +} while (0) #if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_NORMAL # define ZFCP_LOG_NORMAL(fmt, args...) #else # define ZFCP_LOG_NORMAL(fmt, args...) \ +do { \ if (ZFCP_LOG_CHECK(ZFCP_LOG_LEVEL_NORMAL)) \ - printk(KERN_ERR ZFCP_NAME": " fmt , ##args); + printk(KERN_ERR ZFCP_NAME": " fmt, ##args); \ +} while (0) #endif #if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_INFO # define ZFCP_LOG_INFO(fmt, args...) #else # define ZFCP_LOG_INFO(fmt, args...) \ +do { \ if (ZFCP_LOG_CHECK(ZFCP_LOG_LEVEL_INFO)) \ - printk(KERN_ERR ZFCP_NAME": " fmt , ##args); + printk(KERN_ERR ZFCP_NAME": " fmt, ##args); \ +} while (0) #endif #if ZFCP_LOG_LEVEL_LIMIT < ZFCP_LOG_LEVEL_DEBUG @@ -568,8 +536,10 @@ struct zfcp_ls_rnid_acc { #else extern u32 flags_dump; # define ZFCP_LOG_FLAGS(level, fmt, args...) \ +do { \ if (level <= flags_dump) \ - _ZFCP_LOG(fmt , ##args) + _ZFCP_LOG(fmt, ##args); \ +} while (0) #endif /*************** ADAPTER/PORT/UNIT AND FSF_REQ STATUS FLAGS ******************/ @@ -606,7 +576,12 @@ extern u32 flags_dump; ZFCP_STATUS_ADAPTER_REGISTERED) -#define ZFCP_DID_NAMESERVER 0xFFFFFC +/* FC-PH/FC-GS well-known address identifiers for generic services */ +#define ZFCP_DID_MANAGEMENT_SERVICE 0xFFFFFA +#define ZFCP_DID_TIME_SERVICE 0xFFFFFB +#define ZFCP_DID_DIRECTORY_SERVICE 0xFFFFFC +#define ZFCP_DID_ALIAS_SERVICE 0xFFFFF8 +#define ZFCP_DID_KEY_DISTRIBUTION_SERVICE 0xFFFFF7 /* remote port status */ #define ZFCP_STATUS_PORT_PHYS_OPEN 0x00000001 @@ -616,7 +591,8 @@ extern u32 flags_dump; #define ZFCP_STATUS_PORT_NO_SCSI_ID 0x00000010 #define ZFCP_STATUS_PORT_INVALID_WWPN 0x00000020 -#define ZFCP_STATUS_PORT_NAMESERVER \ +/* for ports with well known addresses */ +#define ZFCP_STATUS_PORT_WKA \ (ZFCP_STATUS_PORT_NO_WWPN | \ ZFCP_STATUS_PORT_NO_SCSI_ID) @@ -789,43 +765,29 @@ struct ct_iu_gid_pn_req { wwn_t wwpn; } __attribute__ ((packed)); -/* nameserver request CT_IU -- for requests where - * a port identifier is required */ -struct ct_iu_ga_nxt_req { - struct ct_hdr header; - fc_id_t d_id; -} __attribute__ ((packed)); - /* FS_ACC IU and data unit for GID_PN nameserver request */ struct ct_iu_gid_pn_resp { struct ct_hdr header; fc_id_t d_id; } __attribute__ ((packed)); -/* FS_ACC IU and data unit for GA_NXT nameserver request */ -struct ct_iu_ga_nxt_resp { - struct ct_hdr header; - u8 port_type; - u8 port_id[3]; - u64 port_wwn; - u8 port_symbolic_name_length; - u8 port_symbolic_name[255]; - u64 node_wwn; - u8 node_symbolic_name_length; - u8 node_symbolic_name[255]; - u64 initial_process_associator; - u8 node_ip[16]; - u32 cos; - u8 fc4_types[32]; - u8 port_ip[16]; - u64 fabric_wwn; - u8 reserved; - u8 hard_address[3]; -} __attribute__ ((packed)); - typedef void (*zfcp_send_ct_handler_t)(unsigned long); -/* used to pass parameters to zfcp_send_ct() */ +/** + * struct zfcp_send_ct - used to pass parameters to function zfcp_fsf_send_ct + * @port: port where the request is sent to + * @req: scatter-gather list for request + * @resp: scatter-gather list for response + * @req_count: number of elements in request scatter-gather list + * @resp_count: number of elements in response scatter-gather list + * @handler: handler function (called for response to the request) + * @handler_data: data passed to handler function + * @pool: pointer to memory pool for ct request structure + * @timeout: FSF timeout for this request + * @timer: timer (e.g. for request initiated by erp) + * @completion: completion for synchronization purposes + * @status: used to pass error status to calling function + */ struct zfcp_send_ct { struct zfcp_port *port; struct scatterlist *req; @@ -834,7 +796,7 @@ struct zfcp_send_ct { unsigned int resp_count; zfcp_send_ct_handler_t handler; unsigned long handler_data; - mempool_t *pool; /* mempool for ct not for fsf_req */ + mempool_t *pool; int timeout; struct timer_list *timer; struct completion *completion; @@ -851,10 +813,22 @@ struct zfcp_gid_pn_data { struct zfcp_port *port; }; -typedef int (*zfcp_send_els_handler_t)(unsigned long); +typedef void (*zfcp_send_els_handler_t)(unsigned long); -/* used to pass parameters to zfcp_send_els() */ -/* ToDo merge send_ct() and send_els() and corresponding structs */ +/** + * struct zfcp_send_els - used to pass parameters to function zfcp_fsf_send_els + * @port: port where the request is sent to + * @req: scatter-gather list for request + * @resp: scatter-gather list for response + * @req_count: number of elements in request scatter-gather list + * @resp_count: number of elements in response scatter-gather list + * @handler: handler function (called for response to the request) + * @handler_data: data passed to handler function + * @timer: timer (e.g. for request initiated by erp) + * @completion: completion for synchronization purposes + * @ls_code: hex code of ELS command + * @status: used to pass error status to calling function + */ struct zfcp_send_els { struct zfcp_port *port; struct scatterlist *req; @@ -863,6 +837,7 @@ struct zfcp_send_els { unsigned int resp_count; zfcp_send_els_handler_t handler; unsigned long handler_data; + struct timer_list *timer; struct completion *completion; int ls_code; int status; @@ -892,6 +867,7 @@ union zfcp_req_data { struct zfcp_send_ct *send_ct; struct zfcp_send_els *send_els; struct zfcp_status_read status_read; + struct fsf_qtcb_bottom_port *port_data; }; struct zfcp_qdio_queue { @@ -979,6 +955,7 @@ struct zfcp_adapter { rwlock_t cmd_dbf_lock; struct zfcp_adapter_mempool pool; /* Adapter memory pools */ struct qdio_initialize qdio_init_data; /* for qdio_establish */ + struct device generic_services; /* directory for WKA ports */ }; /* @@ -1080,6 +1057,11 @@ struct zfcp_data { fcp_lun_t init_fcp_lun; }; +/** + * struct zfcp_sg_list - struct describing a scatter-gather list + * @sg: pointer to array of (struct scatterlist) + * @count: number of elements in scatter-gather list + */ struct zfcp_sg_list { struct scatterlist *sg; unsigned int count; --- linux-2.6.9-rc1/drivers/s390/scsi/zfcp_erp.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/s390/scsi/zfcp_erp.c 2004-09-02 20:51:52.000000000 -0700 @@ -31,12 +31,12 @@ #define ZFCP_LOG_AREA ZFCP_LOG_AREA_ERP /* this drivers version (do not edit !!! generated and updated by cvs) */ -#define ZFCP_ERP_REVISION "$Revision: 1.61 $" +#define ZFCP_ERP_REVISION "$Revision: 1.65 $" #include "zfcp_ext.h" static int zfcp_els(struct zfcp_port *, u8); -static int zfcp_els_handler(unsigned long); +static void zfcp_els_handler(unsigned long); static int zfcp_erp_adapter_reopen_internal(struct zfcp_adapter *, int); static int zfcp_erp_port_forced_reopen_internal(struct zfcp_port *, int); @@ -126,6 +126,25 @@ static void zfcp_erp_memwait_handler(uns static void zfcp_erp_timeout_handler(unsigned long); static inline void zfcp_erp_timeout_init(struct zfcp_erp_action *); +/** + * zfcp_fsf_request_timeout_handler - called if a request timed out + * @data: pointer to adapter for handler function + * + * This function needs to be called if requests (ELS, Generic Service, + * or SCSI commands) exceed a certain time limit. The assumption is + * that after the time limit the adapter get stuck. So we trigger a reopen of + * the adapter. This should not be used for error recovery, SCSI abort + * commands and SCSI requests from SCSI mid-layer. + */ +void +zfcp_fsf_request_timeout_handler(unsigned long data) +{ + struct zfcp_adapter *adapter; + + adapter = (struct zfcp_adapter *) data; + + zfcp_erp_adapter_reopen(adapter, 0); +} /* * function: zfcp_fsf_scsi_er_timeout_handler @@ -324,6 +343,7 @@ zfcp_els(struct zfcp_port *port, u8 ls_c send_els->completion = NULL; req = zfcp_sg_to_address(send_els->req); + memset(req, 0, PAGE_SIZE); *(u32*)req = 0; *(u8*)req = ls_code; @@ -412,185 +432,99 @@ out: } -/* - * function: zfcp_els_handler - * - * purpose: Handler for all kind of ELSs - * - * returns: 0 - Operation completed successfuly - * -ENXIO - ELS has been rejected - * -EPERM - Port forced reopen failed +/** + * zfcp_els_handler - handler for ELS commands + * @data: pointer to struct zfcp_send_els + * If ELS failed (LS_RJT or timed out) forced reopen of the port is triggered. */ -int +void zfcp_els_handler(unsigned long data) { struct zfcp_send_els *send_els = (struct zfcp_send_els*)data; struct zfcp_port *port = send_els->port; - struct zfcp_ls_rjt *rjt; struct zfcp_ls_rtv_acc *rtv; struct zfcp_ls_rls_acc *rls; struct zfcp_ls_pdisc_acc *pdisc; struct zfcp_ls_adisc_acc *adisc; void *req, *resp; - u8 req_code, resp_code; - int retval = 0; + u8 req_code; + /* request rejected or timed out */ if (send_els->status != 0) { ZFCP_LOG_NORMAL("ELS request timed out, force physical port " "reopen of port 0x%016Lx on adapter %s\n", port->wwpn, zfcp_get_busid_by_port(port)); debug_text_event(port->adapter->erp_dbf, 3, "forcreop"); - retval = zfcp_erp_port_forced_reopen(port, 0); - if (retval != 0) { + if (zfcp_erp_port_forced_reopen(port, 0)) ZFCP_LOG_NORMAL("reopen of remote port 0x%016Lx " "on adapter %s failed\n", port->wwpn, zfcp_get_busid_by_port(port)); - retval = -EPERM; - } - goto skip_fsfstatus; + goto out; } - req = (void*)((page_to_pfn(send_els->req->page) << PAGE_SHIFT) + send_els->req->offset); - resp = (void*)((page_to_pfn(send_els->resp->page) << PAGE_SHIFT) + send_els->resp->offset); + req = zfcp_sg_to_address(send_els->req); + resp = zfcp_sg_to_address(send_els->resp); req_code = *(u8*)req; - resp_code = *(u8*)resp; - - switch (resp_code) { - - case ZFCP_LS_RJT: - rjt = (struct zfcp_ls_rjt*)resp; - - switch (rjt->reason_code) { - - case ZFCP_LS_RJT_INVALID_COMMAND_CODE: - ZFCP_LOG_INFO("invalid LS command code " - "(wwpn=0x%016Lx, command=0x%02x)\n", - port->wwpn, req_code); - break; - - case ZFCP_LS_RJT_LOGICAL_ERROR: - ZFCP_LOG_INFO("logical error (wwpn=0x%016Lx, " - "reason_expl=0x%02x)\n", - port->wwpn, rjt->reason_expl); - break; - - case ZFCP_LS_RJT_LOGICAL_BUSY: - ZFCP_LOG_INFO("logical busy (wwpn=0x%016Lx, " - "reason_expl=0x%02x)\n", - port->wwpn, rjt->reason_expl); - break; - - case ZFCP_LS_RJT_PROTOCOL_ERROR: - ZFCP_LOG_INFO("protocol error (wwpn=0x%016Lx, " - "reason_expl=0x%02x)\n", - port->wwpn, rjt->reason_expl); - break; - case ZFCP_LS_RJT_UNABLE_TO_PERFORM: - ZFCP_LOG_INFO("unable to perform command requested " - "(wwpn=0x%016Lx, reason_expl=0x%02x)\n", - port->wwpn, rjt->reason_expl); - break; - - case ZFCP_LS_RJT_COMMAND_NOT_SUPPORTED: - ZFCP_LOG_INFO("command not supported (wwpn=0x%016Lx, " - "command=0x%02x)\n", - port->wwpn, req_code); - break; + switch (req_code) { - case ZFCP_LS_RJT_VENDOR_UNIQUE_ERROR: - ZFCP_LOG_INFO("vendor specific error (wwpn=0x%016Lx, " - "vendor_unique=0x%02x)\n", - port->wwpn, rjt->vendor_unique); - break; - - default: - ZFCP_LOG_NORMAL("ELS rejected by remote port 0x%016Lx " - "on adapter %s (reason_code=0x%02x)\n", - port->wwpn, - zfcp_get_busid_by_port(port), - rjt->reason_code); - } - retval = -ENXIO; - break; - - case ZFCP_LS_ACC: - switch (req_code) { - - case ZFCP_LS_RTV: - rtv = (struct zfcp_ls_rtv_acc*)resp; - ZFCP_LOG_INFO("RTV response from d_id 0x%08x to s_id " - "0x%08x (R_A_TOV=%ds E_D_TOV=%d%cs)\n", - port->d_id, port->adapter->s_id, - rtv->r_a_tov, rtv->e_d_tov, - rtv->qualifier & - ZFCP_LS_RTV_E_D_TOV_FLAG ? 'n' : 'm'); - break; - - case ZFCP_LS_RLS: - rls = (struct zfcp_ls_rls_acc*)resp; - ZFCP_LOG_INFO("RLS response from d_id 0x%08x to s_id " - "0x%08x (link_failure_count=%u, " - "loss_of_sync_count=%u, " - "loss_of_signal_count=%u, " - "primitive_sequence_protocol_error=%u, " - "invalid_transmition_word=%u, " - "invalid_crc_count=%u)\n", - port->d_id, port->adapter->s_id, - rls->link_failure_count, - rls->loss_of_sync_count, - rls->loss_of_signal_count, - rls->prim_seq_prot_error, - rls->invalid_transmition_word, - rls->invalid_crc_count); - break; + case ZFCP_LS_RTV: + rtv = (struct zfcp_ls_rtv_acc*)resp; + ZFCP_LOG_INFO("RTV response from d_id 0x%08x to s_id " + "0x%08x (R_A_TOV=%ds E_D_TOV=%d%cs)\n", + port->d_id, port->adapter->s_id, + rtv->r_a_tov, rtv->e_d_tov, + rtv->qualifier & + ZFCP_LS_RTV_E_D_TOV_FLAG ? 'n' : 'm'); + break; - case ZFCP_LS_PDISC: - pdisc = (struct zfcp_ls_pdisc_acc*)resp; - ZFCP_LOG_INFO("PDISC response from d_id 0x%08x to s_id " - "0x%08x (wwpn=0x%016Lx, wwnn=0x%016Lx, " - "vendor='%-16s')\n", port->d_id, - port->adapter->s_id, pdisc->wwpn, - pdisc->wwnn, pdisc->vendor_version); - break; + case ZFCP_LS_RLS: + rls = (struct zfcp_ls_rls_acc*)resp; + ZFCP_LOG_INFO("RLS response from d_id 0x%08x to s_id " + "0x%08x (link_failure_count=%u, " + "loss_of_sync_count=%u, " + "loss_of_signal_count=%u, " + "primitive_sequence_protocol_error=%u, " + "invalid_transmition_word=%u, " + "invalid_crc_count=%u)\n", + port->d_id, port->adapter->s_id, + rls->link_failure_count, + rls->loss_of_sync_count, + rls->loss_of_signal_count, + rls->prim_seq_prot_error, + rls->invalid_transmition_word, + rls->invalid_crc_count); + break; - case ZFCP_LS_ADISC: - adisc = (struct zfcp_ls_adisc_acc*)resp; - ZFCP_LOG_INFO("ADISC response from d_id 0x%08x to s_id " - "0x%08x (wwpn=0x%016Lx, wwnn=0x%016Lx, " - "hard_nport_id=0x%08x, " - "nport_id=0x%08x)\n", port->d_id, - port->adapter->s_id, adisc->wwpn, - adisc->wwnn, adisc->hard_nport_id, - adisc->nport_id); - /* FIXME: set wwnn in during open port */ - if (port->wwnn == 0) - port->wwnn = adisc->wwnn; - break; - } + case ZFCP_LS_PDISC: + pdisc = (struct zfcp_ls_pdisc_acc*)resp; + ZFCP_LOG_INFO("PDISC response from d_id 0x%08x to s_id " + "0x%08x (wwpn=0x%016Lx, wwnn=0x%016Lx, " + "vendor='%-16s')\n", port->d_id, + port->adapter->s_id, pdisc->wwpn, + pdisc->wwnn, pdisc->vendor_version); break; - default: - ZFCP_LOG_NORMAL("unknown payload code 0x%02x received for " - "request 0x%02x to d_id 0x%08x, reopen needed " - "for port 0x%016Lx on adapter %s\n", resp_code, - req_code, port->d_id, port->wwpn, - zfcp_get_busid_by_port(port)); - retval = zfcp_erp_port_forced_reopen(port, 0); - if (retval != 0) { - ZFCP_LOG_NORMAL("reopen of remote port 0x%016Lx on " - "adapter %s failed\n", port->wwpn, - zfcp_get_busid_by_port(port)); - retval = -EPERM; - } + case ZFCP_LS_ADISC: + adisc = (struct zfcp_ls_adisc_acc*)resp; + ZFCP_LOG_INFO("ADISC response from d_id 0x%08x to s_id " + "0x%08x (wwpn=0x%016Lx, wwnn=0x%016Lx, " + "hard_nport_id=0x%08x, " + "nport_id=0x%08x)\n", port->d_id, + port->adapter->s_id, adisc->wwpn, + adisc->wwnn, adisc->hard_nport_id, + adisc->nport_id); + /* FIXME: set wwnn in during open port */ + if (port->wwnn == 0) + port->wwnn = adisc->wwnn; + break; } -skip_fsfstatus: + out: __free_pages(send_els->req->page, 0); kfree(send_els->req); kfree(send_els->resp); - - return retval; + kfree(send_els); } @@ -735,14 +669,15 @@ zfcp_erp_port_reopen_internal(struct zfc return retval; } -/* - * function: - * - * purpose: Wrappper for zfcp_erp_port_reopen_internal - * used to ensure the correct locking - * - * returns: 0 - initiated action succesfully - * <0 - failed to initiate action +/** + * zfcp_erp_port_reopen - initiate reopen of a remote port + * @port: port to be reopened + * @clear_mask: specifies flags in port status to be cleared + * Return: 0 on success, < 0 on error + * + * This is a wrappper function for zfcp_erp_port_reopen_internal. It ensures + * correct locking. An error recovery task is initiated to do the reopen. + * To wait for the completion of the reopen zfcp_erp_wait should be used. */ int zfcp_erp_port_reopen(struct zfcp_port *port, int clear_mask) @@ -802,14 +737,15 @@ zfcp_erp_unit_reopen_internal(struct zfc return retval; } -/* - * function: - * - * purpose: Wrappper for zfcp_erp_unit_reopen_internal - * used to ensure the correct locking - * - * returns: 0 - initiated action succesfully - * <0 - failed to initiate action +/** + * zfcp_erp_unit_reopen - initiate reopen of a unit + * @unit: unit to be reopened + * @clear_mask: specifies flags in unit status to be cleared + * Return: 0 on success, < 0 on error + * + * This is a wrappper for zfcp_erp_unit_reopen_internal. It ensures correct + * locking. An error recovery task is initiated to do the reopen. + * To wait for the completion of the reopen zfcp_erp_wait should be used. */ int zfcp_erp_unit_reopen(struct zfcp_unit *unit, int clear_mask) @@ -1987,12 +1923,10 @@ zfcp_erp_strategy_check_queues(struct zf return retval; } -/* - * function: - * - * purpose: - * - * returns: +/** + * zfcp_erp_wait - wait for completion of error recovery on an adapter + * @adapter: adapter for which to wait for completion of its error recovery + * Return: 0 */ int zfcp_erp_wait(struct zfcp_adapter *adapter) @@ -2130,7 +2064,7 @@ zfcp_erp_port_reopen_all_internal(struct struct zfcp_port *port; list_for_each_entry(port, &adapter->port_list_head, list) - if (!atomic_test_mask(ZFCP_STATUS_PORT_NAMESERVER, &port->status)) + if (!atomic_test_mask(ZFCP_STATUS_PORT_WKA, &port->status)) zfcp_erp_port_reopen_internal(port, clear_mask); return retval; @@ -2725,7 +2659,7 @@ zfcp_erp_port_strategy_open(struct zfcp_ { int retval; - if (atomic_test_mask(ZFCP_STATUS_PORT_NAMESERVER, + if (atomic_test_mask(ZFCP_STATUS_PORT_WKA, &erp_action->port->status)) retval = zfcp_erp_port_strategy_open_nameserver(erp_action); else @@ -2863,10 +2797,10 @@ zfcp_erp_port_strategy_open_nameserver(s case ZFCP_ERP_STEP_PORT_OPENING: if (atomic_test_mask(ZFCP_STATUS_COMMON_OPEN, &port->status)) { - ZFCP_LOG_DEBUG("nameserver port is open\n"); + ZFCP_LOG_DEBUG("WKA port is open\n"); retval = ZFCP_ERP_SUCCEEDED; } else { - ZFCP_LOG_DEBUG("open failed for nameserver port\n"); + ZFCP_LOG_DEBUG("open failed for WKA port\n"); retval = ZFCP_ERP_FAILED; } /* this is needed anyway (dont care for retval of wakeup) */ --- linux-2.6.9-rc1/drivers/s390/scsi/zfcp_ext.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/s390/scsi/zfcp_ext.h 2004-09-02 20:51:52.000000000 -0700 @@ -31,7 +31,7 @@ #ifndef ZFCP_EXT_H #define ZFCP_EXT_H /* this drivers version (do not edit !!! generated and updated by cvs) */ -#define ZFCP_EXT_REVISION "$Revision: 1.51 $" +#define ZFCP_EXT_REVISION "$Revision: 1.57 $" #include "zfcp_def.h" @@ -50,15 +50,16 @@ extern void zfcp_sysfs_port_release(stru extern void zfcp_sysfs_unit_release(struct device *); /**************************** CONFIGURATION *********************************/ -extern struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *, - fcp_lun_t fcp_lun); -extern struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *, - wwn_t wwpn); +extern struct zfcp_unit *zfcp_get_unit_by_lun(struct zfcp_port *, fcp_lun_t); +extern struct zfcp_port *zfcp_get_port_by_wwpn(struct zfcp_adapter *, wwn_t); +extern struct zfcp_port *zfcp_get_port_by_did(struct zfcp_adapter *, u32); +struct zfcp_adapter *zfcp_get_adapter_by_busid(char *); extern struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *); extern int zfcp_adapter_debug_register(struct zfcp_adapter *); extern void zfcp_adapter_dequeue(struct zfcp_adapter *); extern void zfcp_adapter_debug_unregister(struct zfcp_adapter *); -extern struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *, wwn_t, u32); +extern struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *, wwn_t, + u32, u32); extern void zfcp_port_dequeue(struct zfcp_port *); extern struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *, fcp_lun_t); extern void zfcp_unit_dequeue(struct zfcp_unit *); @@ -94,8 +95,11 @@ extern int zfcp_fsf_open_unit(struct zf extern int zfcp_fsf_close_unit(struct zfcp_erp_action *); extern int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *); +extern int zfcp_fsf_exchange_port_data(struct zfcp_adapter *, + struct fsf_qtcb_bottom_port *); extern int zfcp_fsf_control_file(struct zfcp_adapter *, struct zfcp_fsf_req **, u32, u32, struct zfcp_sg_list *); +extern void zfcp_fsf_request_timeout_handler(unsigned long); extern void zfcp_fsf_scsi_er_timeout_handler(unsigned long); extern int zfcp_fsf_req_dismiss_all(struct zfcp_adapter *); extern int zfcp_fsf_status_read(struct zfcp_adapter *, int); @@ -108,7 +112,7 @@ extern int zfcp_fsf_req_wait_and_cleanu extern int zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *, struct zfcp_unit *, struct scsi_cmnd *, - int); + struct timer_list*, int); extern int zfcp_fsf_req_complete(struct zfcp_fsf_req *); extern void zfcp_fsf_incoming_els(struct zfcp_fsf_req *); extern void zfcp_fsf_req_cleanup(struct zfcp_fsf_req *); @@ -117,9 +121,11 @@ extern struct zfcp_fsf_req *zfcp_fsf_sen extern struct zfcp_fsf_req *zfcp_fsf_abort_fcp_command( unsigned long, struct zfcp_adapter *, struct zfcp_unit *, int); -/******************************** FCP ****************************************/ +/******************************* FC/FCP **************************************/ extern int zfcp_nameserver_enqueue(struct zfcp_adapter *); extern int zfcp_ns_gid_pn_request(struct zfcp_erp_action *); +extern int zfcp_check_ct_response(struct ct_hdr *); +extern int zfcp_handle_els_rjt(u32, struct zfcp_ls_rjt_par *); /******************************* SCSI ****************************************/ extern int zfcp_adapter_scsi_register(struct zfcp_adapter *); @@ -132,10 +138,10 @@ extern char *zfcp_get_fcp_sns_info_ptr(s extern void zfcp_fsf_start_scsi_er_timer(struct zfcp_adapter *); extern fcp_dl_t zfcp_get_fcp_dl(struct fcp_cmnd_iu *); -extern int zfcp_scsi_command_async(struct zfcp_adapter *,struct zfcp_unit *unit, - struct scsi_cmnd *scsi_cmnd); -extern int zfcp_scsi_command_sync(struct zfcp_unit *unit, - struct scsi_cmnd *scsi_cmnd); +extern int zfcp_scsi_command_async(struct zfcp_adapter *,struct zfcp_unit *, + struct scsi_cmnd *, struct timer_list *); +extern int zfcp_scsi_command_sync(struct zfcp_unit *, struct scsi_cmnd *, + struct timer_list *); extern struct scsi_transport_template *zfcp_transport_template; extern struct fc_function_template zfcp_transport_functions; --- linux-2.6.9-rc1/drivers/s390/scsi/zfcp_fsf.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/drivers/s390/scsi/zfcp_fsf.c 2004-09-02 20:51:52.000000000 -0700 @@ -29,11 +29,12 @@ */ /* this drivers version (do not edit !!! generated and updated by cvs) */ -#define ZFCP_FSF_C_REVISION "$Revision: 1.55 $" +#define ZFCP_FSF_C_REVISION "$Revision: 1.65 $" #include "zfcp_ext.h" static int zfcp_fsf_exchange_config_data_handler(struct zfcp_fsf_req *); +static void zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *); static int zfcp_fsf_open_port_handler(struct zfcp_fsf_req *); static int zfcp_fsf_close_port_handler(struct zfcp_fsf_req *); static int zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *); @@ -48,7 +49,7 @@ static int zfcp_fsf_status_read_handler( static int zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *); static int zfcp_fsf_send_els_handler(struct zfcp_fsf_req *); static int zfcp_fsf_control_file_handler(struct zfcp_fsf_req *); -static inline int zfcp_fsf_req_create_sbal_check( +static inline int zfcp_fsf_req_sbal_check( unsigned long *, struct zfcp_qdio_queue *, int); static inline int zfcp_use_one_sbal( struct scatterlist *, int, struct scatterlist *, int); @@ -79,10 +80,9 @@ static u32 fsf_qtcb_type[] = { }; static const char zfcp_act_subtable_type[5][8] = { - {"unknown"}, {"OS"}, {"WWPN"}, {"DID"}, {"LUN"} + "unknown", "OS", "WWPN", "DID", "LUN" }; - /****************************************************************/ /*************** FSF related Functions *************************/ /****************************************************************/ @@ -684,13 +684,11 @@ zfcp_fsf_fsfstatus_qual_eval(struct zfcp break; case FSF_SQ_ULP_PROGRAMMING_ERROR: ZFCP_LOG_FLAGS(0, "FSF_SQ_ULP_PROGRAMMING_ERROR\n"); - ZFCP_LOG_NORMAL("bug: An illegal amount of data was attempted " - "to be sent to the adapter %s " - "Stopping all operations on this adapter. ", + ZFCP_LOG_NORMAL("error: not enough SBALs for data transfer " + "(adapter %s)\n", zfcp_get_busid_by_adapter(fsf_req->adapter)); debug_text_exception(fsf_req->adapter->erp_dbf, 0, "fsf_sq_ulp_err"); - zfcp_erp_adapter_shutdown(fsf_req->adapter, 0); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE: @@ -785,6 +783,11 @@ zfcp_fsf_req_dispatch(struct zfcp_fsf_re zfcp_fsf_exchange_config_data_handler(fsf_req); break; + case FSF_QTCB_EXCHANGE_PORT_DATA : + ZFCP_LOG_FLAGS(2, "FSF_QTCB_EXCHANGE_PORT_DATA\n"); + zfcp_fsf_exchange_port_data_handler(fsf_req); + break; + case FSF_QTCB_SEND_ELS : ZFCP_LOG_FLAGS(2, "FSF_QTCB_SEND_ELS\n"); zfcp_fsf_send_els_handler(fsf_req); @@ -1624,26 +1627,6 @@ zfcp_fsf_send_ct_handler(struct zfcp_fsf fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - case FSF_REQUEST_BUF_NOT_VALID : - ZFCP_LOG_FLAGS(2, "FSF_REQUEST_BUF_NOT_VALID\n"); - ZFCP_LOG_NORMAL("error: The port 0x%016Lx on adapter %s has " - "rejected a generic services command " - "due to invalid request buffer.\n", - port->wwpn, zfcp_get_busid_by_port(port)); - debug_text_event(adapter->erp_dbf, 1, "fsf_s_reqiv"); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; - break; - - case FSF_RESPONSE_BUF_NOT_VALID : - ZFCP_LOG_FLAGS(2, "FSF_RESPONSE_BUF_NOT_VALID\n"); - ZFCP_LOG_NORMAL("error: The port 0x%016Lx on adapter %s has " - "rejected a generic services command " - "due to invalid response buffer.\n", - port->wwpn, zfcp_get_busid_by_port(port)); - debug_text_event(adapter->erp_dbf, 1, "fsf_s_resiv"); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; - break; - case FSF_PORT_BOXED : ZFCP_LOG_FLAGS(2, "FSF_PORT_BOXED\n"); ZFCP_LOG_INFO("The remote port 0x%016Lx on adapter %s " @@ -1665,9 +1648,10 @@ zfcp_fsf_send_ct_handler(struct zfcp_fsf } skip_fsfstatus: - if (send_ct->handler != NULL) { + send_ct->status = retval; + + if (send_ct->handler != NULL) send_ct->handler(send_ct->handler_data); - } return retval; } @@ -1769,7 +1753,7 @@ zfcp_fsf_send_els(struct zfcp_send_els * sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); /* start QDIO request for this FSF request */ - ret = zfcp_fsf_req_send(fsf_req, NULL); + ret = zfcp_fsf_req_send(fsf_req, els->timer); if (ret) { ZFCP_LOG_DEBUG("error: initiation of ELS request failed " "(adapter %s, port 0x%016Lx)\n", @@ -1863,6 +1847,10 @@ static int zfcp_fsf_send_els_handler(str /* ERP strategy will escalate */ debug_text_event(adapter->erp_dbf, 1, "fsf_sq_ulp"); fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; + retval = + zfcp_handle_els_rjt(header->fsf_status_qual.word[1], + (struct zfcp_ls_rjt_par *) + &header->fsf_status_qual.word[2]); break; case FSF_SQ_RETRY_IF_POSSIBLE: ZFCP_LOG_FLAGS(2, "FSF_SQ_RETRY_IF_POSSIBLE\n"); @@ -1921,15 +1909,6 @@ static int zfcp_fsf_send_els_handler(str bottom->resp_buf_length); break; - case FSF_UNKNOWN_COMMAND: - ZFCP_LOG_FLAGS(2, "FSF_UNKNOWN_COMMAND\n"); - ZFCP_LOG_INFO( - "FSF command 0x%x is not supported by FCP adapter " - "(adapter: %s)\n", fsf_req->fsf_command, - zfcp_get_busid_by_port(port)); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; - break; - case FSF_ACCESS_DENIED: ZFCP_LOG_FLAGS(2, "FSF_ACCESS_DENIED\n"); ZFCP_LOG_NORMAL("Access denied, cannot send ELS " @@ -1971,8 +1950,6 @@ skip_fsfstatus: if (send_els->handler != 0) send_els->handler(send_els->handler_data); - kfree(send_els); - return retval; } @@ -2219,6 +2196,111 @@ zfcp_fsf_exchange_config_data_handler(st return 0; } +/** + * zfcp_fsf_exchange_port_data - request information about local port + * @adapter: for which port data is requested + * @data: response to exchange port data request + */ +int +zfcp_fsf_exchange_port_data(struct zfcp_adapter *adapter, + struct fsf_qtcb_bottom_port *data) +{ + volatile struct qdio_buffer_element *sbale; + int retval = 0; + unsigned long lock_flags; + struct zfcp_fsf_req *fsf_req; + struct timer_list *timer; + + if(!(adapter->supported_features & FSF_FEATURE_HBAAPI_MANAGEMENT)){ + ZFCP_LOG_INFO("error: exchange port data " + "command not supported by adapter %s\n", + zfcp_get_busid_by_adapter(adapter)); + return -EOPNOTSUPP; + } + + timer = kmalloc(sizeof(struct timer_list), GFP_KERNEL); + if (!timer) + return -ENOMEM; + + /* setup new FSF request */ + retval = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA, + 0, 0, &lock_flags, &fsf_req); + if (retval < 0) { + ZFCP_LOG_INFO("error: Out of resources. Could not create an " + "exchange port data request for" + "the adapter %s.\n", + zfcp_get_busid_by_adapter(adapter)); + write_unlock_irqrestore(&adapter->request_queue.queue_lock, + lock_flags); + goto out; + } + + sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0); + sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; + sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; + + fsf_req->data.port_data = data; + + init_timer(timer); + timer->function = zfcp_fsf_request_timeout_handler; + timer->data = (unsigned long) adapter; + timer->expires = ZFCP_FSF_REQUEST_TIMEOUT; + + retval = zfcp_fsf_req_send(fsf_req, timer); + if (retval) { + ZFCP_LOG_INFO("error: Could not send an exchange port data " + "command on the adapter %s\n", + zfcp_get_busid_by_adapter(adapter)); + zfcp_fsf_req_free(fsf_req); + write_unlock_irqrestore(&adapter->request_queue.queue_lock, + lock_flags); + goto out; + } + + ZFCP_LOG_DEBUG("Exchange Port Data request initiated (adapter %s)\n", + zfcp_get_busid_by_adapter(adapter)); + + write_unlock_irqrestore(&adapter->request_queue.queue_lock, + lock_flags); + + wait_event(fsf_req->completion_wq, + fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED); + del_timer_sync(timer); + zfcp_fsf_req_cleanup(fsf_req); + out: + kfree(timer); + return retval; +} + + +/** + * zfcp_fsf_exchange_port_data_handler - handler for exchange_port_data request + * @fsf_req: pointer to struct zfcp_fsf_req + */ +static void +zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *fsf_req) +{ + struct fsf_qtcb_bottom_port *bottom; + struct fsf_qtcb_bottom_port *data = fsf_req->data.port_data; + + if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR) + return; + + switch (fsf_req->qtcb->header.fsf_status) { + case FSF_GOOD : + ZFCP_LOG_FLAGS(2,"FSF_GOOD\n"); + bottom = &fsf_req->qtcb->bottom.port; + memcpy(data, bottom, sizeof(*data)); + break; + + default: + debug_text_event(fsf_req->adapter->erp_dbf, 0, "xchg-port-ng"); + debug_event(fsf_req->adapter->erp_dbf, 0, + &fsf_req->qtcb->header.fsf_status, sizeof(u32)); + } +} + + /* * function: zfcp_fsf_open_port * @@ -3319,19 +3401,19 @@ zfcp_fsf_close_unit_handler(struct zfcp_ return retval; } -/* - * function: zfcp_fsf_send_fcp_command_task - * - * purpose: - * - * returns: - * - * note: we do not employ linked commands (not supported by HBA anyway) +/** + * zfcp_fsf_send_fcp_command_task - initiate an FCP command (for a SCSI command) + * @adapter: adapter where scsi command is issued + * @unit: unit where command is sent to + * @scsi_cmnd: scsi command to be sent + * @timer: timer to be started when request is initiated + * @req_flags: flags for fsf_request */ int zfcp_fsf_send_fcp_command_task(struct zfcp_adapter *adapter, struct zfcp_unit *unit, - struct scsi_cmnd * scsi_cmnd, int req_flags) + struct scsi_cmnd * scsi_cmnd, + struct timer_list *timer, int req_flags) { struct zfcp_fsf_req *fsf_req = NULL; struct fcp_cmnd_iu *fcp_cmnd_iu; @@ -3486,7 +3568,7 @@ zfcp_fsf_send_fcp_command_task(struct zf * start QDIO request for this FSF request * covered by an SBALE) */ - retval = zfcp_fsf_req_send(fsf_req, NULL); + retval = zfcp_fsf_req_send(fsf_req, timer); if (unlikely(retval < 0)) { ZFCP_LOG_INFO("error: Could not send FCP command request " "on adapter %s, port 0x%016Lx, unit 0x%016Lx\n", @@ -3788,44 +3870,6 @@ zfcp_fsf_send_fcp_command_handler(struct fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; - /* FIXME: this should be obsolete, isn' it? */ - case FSF_INBOUND_DATA_LENGTH_NOT_VALID: - ZFCP_LOG_FLAGS(0, "FSF_INBOUND_DATA_LENGTH_NOT_VALID\n"); - ZFCP_LOG_NORMAL("bug: An invalid inbound data length field " - "was found in a command for unit 0x%016Lx " - "on port 0x%016Lx on adapter %s.\n", - unit->fcp_lun, - unit->port->wwpn, zfcp_get_busid_by_unit(unit)); - /* stop operation for this adapter */ - debug_text_event(fsf_req->adapter->erp_dbf, 0, - "fsf_s_in_dl_nv"); - zfcp_erp_adapter_shutdown(unit->port->adapter, 0); - zfcp_cmd_dbf_event_fsf("idleninv", - fsf_req, - &header->fsf_status_qual, - sizeof (union fsf_status_qual)); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; - break; - - /* FIXME: this should be obsolete, isn' it? */ - case FSF_OUTBOUND_DATA_LENGTH_NOT_VALID: - ZFCP_LOG_FLAGS(0, "FSF_OUTBOUND_DATA_LENGTH_NOT_VALID\n"); - ZFCP_LOG_NORMAL("bug: An invalid outbound data length field " - "was found in a command unit 0x%016Lx on port " - "0x%016Lx on adapter %s\n", - unit->fcp_lun, - unit->port->wwpn, - zfcp_get_busid_by_unit(unit)); - /* stop operation for this adapter */ - debug_text_event(fsf_req->adapter->erp_dbf, 0, - "fsf_s_out_dl_nv"); - zfcp_erp_adapter_shutdown(unit->port->adapter, 0); - zfcp_cmd_dbf_event_fsf("odleninv", fsf_req, - &header->fsf_status_qual, - sizeof (union fsf_status_qual)); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; - break; - case FSF_CMND_LENGTH_NOT_VALID: ZFCP_LOG_FLAGS(0, "FSF_CMND_LENGTH_NOT_VALID\n"); ZFCP_LOG_NORMAL @@ -4157,87 +4201,6 @@ zfcp_fsf_send_fcp_command_task_handler(s } skip_fsfstatus: -#if 0 - /* - * This nasty chop at the problem is not working anymore - * as we do not adjust the retry count anylonger in order - * to have a number of retries that avoids I/O errors. - * The manipulation of the retry count has been removed - * in favour of a safe tape device handling. We must not - * sent SCSI commands more than once to a device if no - * retries are permitted by the high level driver. Generally - * speaking, it was a mess to change retry counts. So it is - * fine that this sort of workaround is gone. - * Then, we had to face a certain number of immediate retries in case of - * busy and queue full conditions (see below). - * This is not acceptable - * for the latter. Queue full conditions are used - * by devices to indicate to a host that the host can rely - * on the completion (or timeout) of at least one outstanding - * command as a suggested trigger for command retries. - * Busy conditions require a different trigger since - * no commands are outstanding for that initiator from the - * devices perspective. - * The drawback of mapping a queue full condition to a - * busy condition is the chance of wasting all retries prior - * to the time when the device indicates that a command - * rejected due to a queue full condition should be re-driven. - * This case would lead to unnecessary I/O errors that - * have to be considered fatal if for example ext3's - * journaling would be torpedoed by such an avoidable - * I/O error. - * So, what issues are there with not mapping a queue-full - * condition to a busy condition? - * Due to the 'exclusive LUN' - * policy enforced by the zSeries FCP channel, this - * Linux instance is the only initiator with regard to - * this adapter. It is safe to rely on the information - * 'don't disturb me now ... and btw. no other commands - * pending for you' (= queue full) sent by the LU, - * since no other Linux can use this LUN via this adapter - * at the same time. If there is a potential race - * introduced by the FCP channel by not inhibiting Linux A - * to give up a LU with commands pending while Linux B - * grabs this LU and sends commands - thus providing - * an exploit at the 'exclusive LUN' policy - then this - * issue has to be considered a hardware problem. It should - * be tracked as such if it really occurs. Even if the - * FCP Channel spec. begs exploiters to wait for the - * completion of all request sent to a LU prior to - * closing this LU connection. - * This spec. statement in conjunction with - * the 'exclusive LUN' policy is not consistent design. - * Another issue is how resource constraints for SCSI commands - * might be handled by the FCP channel (just guessing for now). - * If the FCP channel would always map resource constraints, - * e.g. no free FC exchange ID due to I/O stress caused by - * other sharing Linux instances, to faked queue-full - * conditions then this would be a misinterpretation and - * violation of SCSI standards. - * If there are SCSI stack races as indicated below - * then they need to be fixed just there. - * Providing all issue above are not applicable or will - * be fixed appropriately, removing the following hack - * is the right thing to do. - */ - - /* - * Note: This is a rather nasty chop at the problem. We cannot - * risk adding to the mlqueue however as this will block the - * device. If it is the last outstanding command for this host - * it will remain blocked indefinitely. This would be quite possible - * on the zSeries FCP adapter. - * Also, there exists a race with scsi_insert_special relying on - * scsi_request_fn to recalculate some command data which may not - * happen when q->plugged is true in scsi_request_fn - */ - if (status_byte(scpnt->result) == QUEUE_FULL) { - ZFCP_LOG_DEBUG("Changing QUEUE_FULL to BUSY....\n"); - scpnt->result &= ~(QUEUE_FULL << 1); - scpnt->result |= (BUSY << 1); - } -#endif - ZFCP_LOG_DEBUG("scpnt->result =0x%x\n", scpnt->result); zfcp_cmd_dbf_event_scsi("response", scpnt); @@ -4585,16 +4548,6 @@ zfcp_fsf_control_file_handler(struct zfc retval = -EIO; break; - case FSF_UNKNOWN_COMMAND: - ZFCP_LOG_FLAGS(2, "FSF_UNKNOWN_COMMAND\n"); - ZFCP_LOG_NORMAL( - "FSF command 0x%x is not supported by the adapter %s\n", - fsf_req->fsf_command, - zfcp_get_busid_by_adapter(adapter)); - fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR; - retval = -EINVAL; - break; - case FSF_UNKNOWN_OP_SUBTYPE: ZFCP_LOG_FLAGS(2, "FSF_UNKNOWN_OP_SUBTYPE\n"); ZFCP_LOG_NORMAL( @@ -4682,8 +4635,8 @@ zfcp_fsf_req_wait_and_cleanup(struct zfc } static inline int -zfcp_fsf_req_create_sbal_check(unsigned long *flags, - struct zfcp_qdio_queue *queue, int needed) +zfcp_fsf_req_sbal_check(unsigned long *flags, + struct zfcp_qdio_queue *queue, int needed) { write_lock_irqsave(&queue->queue_lock, *flags); if (likely(atomic_read(&queue->free_count) >= needed)) @@ -4712,30 +4665,25 @@ zfcp_fsf_req_qtcb_init(struct zfcp_fsf_r * zfcp_fsf_req_sbal_get - try to get one SBAL in the request queue * @adapter: adapter for which request queue is examined * @req_flags: flags indicating whether to wait for needed SBAL or not - * @lock_flags: lock_flags is queue_lock is taken - * - * locking: on success the queue_lock for the request queue of the adapter - * is held + * @lock_flags: lock_flags if queue_lock is taken + * Return: 0 on success, otherwise -EIO, or -ERESTARTSYS + * Locks: lock adapter->request_queue->queue_lock on success */ static int zfcp_fsf_req_sbal_get(struct zfcp_adapter *adapter, int req_flags, unsigned long *lock_flags) { - int condition; + long ret; struct zfcp_qdio_queue *req_queue = &adapter->request_queue; if (unlikely(req_flags & ZFCP_WAIT_FOR_SBAL)) { - wait_event_interruptible_timeout(adapter->request_wq, - (condition = - zfcp_fsf_req_create_sbal_check - (lock_flags, req_queue, 1)), - ZFCP_SBAL_TIMEOUT); - if (!condition) { - return -EIO; - } - } else if (!zfcp_fsf_req_create_sbal_check(lock_flags, req_queue, 1)) { + ret = wait_event_interruptible_timeout(adapter->request_wq, + zfcp_fsf_req_sbal_check(lock_flags, req_queue, 1), + ZFCP_SBAL_TIMEOUT); + if (ret < 0) + return ret; + } else if (!zfcp_fsf_req_sbal_check(lock_flags, req_queue, 1)) return -EIO; - } return 0; } --- linux-2.6.9-rc1/drivers/s390/scsi/zfcp_fsf.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/s390/scsi/zfcp_fsf.h 2004-09-02 20:51:52.000000000 -0700 @@ -84,19 +84,12 @@ #define FSF_SERVICE_CLASS_NOT_SUPPORTED 0x00000006 #define FSF_FCPLUN_NOT_VALID 0x00000009 #define FSF_ACCESS_DENIED 0x00000010 -#define FSF_ACCESS_TYPE_NOT_VALID 0x00000011 #define FSF_LUN_SHARING_VIOLATION 0x00000012 -#define FSF_COMMAND_ABORTED_ULP 0x00000020 -#define FSF_COMMAND_ABORTED_ADAPTER 0x00000021 #define FSF_FCP_COMMAND_DOES_NOT_EXIST 0x00000022 #define FSF_DIRECTION_INDICATOR_NOT_VALID 0x00000030 -#define FSF_INBOUND_DATA_LENGTH_NOT_VALID 0x00000031 /* FIX: obsolete? */ -#define FSF_OUTBOUND_DATA_LENGTH_NOT_VALID 0x00000032 /* FIX: obsolete? */ #define FSF_CMND_LENGTH_NOT_VALID 0x00000033 #define FSF_MAXIMUM_NUMBER_OF_PORTS_EXCEEDED 0x00000040 #define FSF_MAXIMUM_NUMBER_OF_LUNS_EXCEEDED 0x00000041 -#define FSF_REQUEST_BUF_NOT_VALID 0x00000042 -#define FSF_RESPONSE_BUF_NOT_VALID 0x00000043 #define FSF_ELS_COMMAND_REJECTED 0x00000050 #define FSF_GENERIC_COMMAND_REJECTED 0x00000051 #define FSF_OPERATION_PARTIALLY_SUCCESSFUL 0x00000052 @@ -227,6 +220,10 @@ #define FSF_HBA_PORTSTATE_LINKDOWN 0x00000006 #define FSF_HBA_PORTSTATE_ERROR 0x00000007 +/* IO states of adapter */ +#define FSF_IOSTAT_NPORT_RJT 0x00000004 +#define FSF_IOSTAT_FABRIC_RJT 0x00000005 +#define FSF_IOSTAT_LS_RJT 0x00000009 struct fsf_queue_designator; struct fsf_status_read_buffer; --- linux-2.6.9-rc1/drivers/s390/scsi/zfcp_scsi.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/drivers/s390/scsi/zfcp_scsi.c 2004-09-02 20:51:52.000000000 -0700 @@ -31,7 +31,7 @@ #define ZFCP_LOG_AREA ZFCP_LOG_AREA_SCSI /* this drivers version (do not edit !!! generated and updated by cvs) */ -#define ZFCP_SCSI_REVISION "$Revision: 1.65 $" +#define ZFCP_SCSI_REVISION "$Revision: 1.68 $" #include "zfcp_ext.h" @@ -247,15 +247,16 @@ zfcp_scsi_command_fail(struct scsi_cmnd /** * zfcp_scsi_command_async - worker for zfcp_scsi_queuecommand and * zfcp_scsi_command_sync - * @adapter: adapter for where scsi command is issued + * @adapter: adapter where scsi command is issued * @unit: unit to which scsi command is sent * @scpnt: scsi command to be sent + * @timer: timer to be started if request is successfully initiated * * Note: In scsi_done function must be set in scpnt. */ int zfcp_scsi_command_async(struct zfcp_adapter *adapter, struct zfcp_unit *unit, - struct scsi_cmnd *scpnt) + struct scsi_cmnd *scpnt, struct timer_list *timer) { int tmp; int retval; @@ -291,7 +292,7 @@ zfcp_scsi_command_async(struct zfcp_adap goto out; } - tmp = zfcp_fsf_send_fcp_command_task(adapter, unit, scpnt, + tmp = zfcp_fsf_send_fcp_command_task(adapter, unit, scpnt, timer, ZFCP_REQ_AUTO_CLEANUP); if (unlikely(tmp < 0)) { @@ -313,18 +314,28 @@ zfcp_scsi_command_sync_handler(struct sc /** * zfcp_scsi_command_sync - send a SCSI command and wait for completion - * returns 0, errors are indicated by scsi_cmnd->result + * @unit: unit where command is sent to + * @scpnt: scsi command to be sent + * @timer: timer to be started if request is successfully initiated + * Return: 0 + * + * Errors are indicated in scpnt->result */ int -zfcp_scsi_command_sync(struct zfcp_unit *unit, struct scsi_cmnd *scpnt) +zfcp_scsi_command_sync(struct zfcp_unit *unit, struct scsi_cmnd *scpnt, + struct timer_list *timer) { + int ret; DECLARE_COMPLETION(wait); scpnt->SCp.ptr = (void *) &wait; /* silent re-use */ - scpnt->done = zfcp_scsi_command_sync_handler; - zfcp_scsi_command_async(unit->port->adapter, unit, scpnt); + scpnt->scsi_done = zfcp_scsi_command_sync_handler; + ret = zfcp_scsi_command_async(unit->port->adapter, unit, scpnt, timer); + if ((ret == 0) && (scpnt->result == 0)) wait_for_completion(&wait); + scpnt->SCp.ptr = NULL; + return 0; } @@ -355,7 +366,7 @@ zfcp_scsi_queuecommand(struct scsi_cmnd adapter = (struct zfcp_adapter *) scpnt->device->host->hostdata[0]; unit = (struct zfcp_unit *) scpnt->device->hostdata; - return zfcp_scsi_command_async(adapter, unit, scpnt); + return zfcp_scsi_command_async(adapter, unit, scpnt, NULL); } /* @@ -430,7 +441,7 @@ zfcp_scsi_eh_abort_handler(struct scsi_c u64 dbf_fsf_req = 0; u64 dbf_fsf_status = 0; u64 dbf_fsf_qual[2] = { 0, 0 }; - char dbf_result[ZFCP_ABORT_DBF_LENGTH] = { "##undef" }; + char dbf_result[ZFCP_ABORT_DBF_LENGTH] = "##undef"; memset(dbf_opcode, 0, ZFCP_ABORT_DBF_LENGTH); memcpy(dbf_opcode, --- linux-2.6.9-rc1/drivers/s390/scsi/zfcp_sysfs_adapter.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/drivers/s390/scsi/zfcp_sysfs_adapter.c 2004-09-02 20:51:52.000000000 -0700 @@ -26,18 +26,18 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#define ZFCP_SYSFS_ADAPTER_C_REVISION "$Revision: 1.33 $" +#define ZFCP_SYSFS_ADAPTER_C_REVISION "$Revision: 1.37 $" #include "zfcp_ext.h" #define ZFCP_LOG_AREA ZFCP_LOG_AREA_CONFIG static const char fc_topologies[5][25] = { - {""}, - {"point-to-point"}, - {"fabric"}, - {"arbitrated loop"}, - {"fabric (virt. adapter)"} + "", + "point-to-point", + "fabric", + "arbitrated loop", + "fabric (virt. adapter)" }; /** @@ -74,29 +74,8 @@ ZFCP_DEFINE_ADAPTER_ATTR(hardware_versio adapter->hardware_version); ZFCP_DEFINE_ADAPTER_ATTR(serial_number, "%17s\n", adapter->serial_number); ZFCP_DEFINE_ADAPTER_ATTR(scsi_host_no, "0x%x\n", adapter->scsi_host_no); - -/** - * zfcp_sysfs_adapter_in_recovery_show - recovery state of adapter - * @dev: pointer to belonging device - * @buf: pointer to input buffer - * - * Show function of "in_recovery" attribute of adapter. Will be - * "0" if no error recovery is pending for adapter, otherwise "1". - */ -static ssize_t -zfcp_sysfs_adapter_in_recovery_show(struct device *dev, char *buf) -{ - struct zfcp_adapter *adapter; - - adapter = dev_get_drvdata(dev); - if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status)) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} - -static DEVICE_ATTR(in_recovery, S_IRUGO, - zfcp_sysfs_adapter_in_recovery_show, NULL); +ZFCP_DEFINE_ADAPTER_ATTR(in_recovery, "%d\n", atomic_test_mask + (ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status)); /** * zfcp_sysfs_port_add_store - add a port to sysfs tree @@ -127,7 +106,7 @@ zfcp_sysfs_port_add_store(struct device if ((endp + 1) < (buf + count)) goto out; - port = zfcp_port_enqueue(adapter, wwpn, 0); + port = zfcp_port_enqueue(adapter, wwpn, 0, 0); if (!port) goto out; @@ -138,7 +117,7 @@ zfcp_sysfs_port_add_store(struct device zfcp_port_put(port); out: up(&zfcp_data.config_sema); - return retval ? retval : count; + return retval ? retval : (ssize_t) count; } static DEVICE_ATTR(port_add, S_IWUSR, NULL, zfcp_sysfs_port_add_store); @@ -197,7 +176,7 @@ zfcp_sysfs_port_remove_store(struct devi zfcp_port_dequeue(port); out: up(&zfcp_data.config_sema); - return retval ? retval : count; + return retval ? retval : (ssize_t) count; } static DEVICE_ATTR(port_remove, S_IWUSR, NULL, zfcp_sysfs_port_remove_store); @@ -241,7 +220,7 @@ zfcp_sysfs_adapter_failed_store(struct d zfcp_erp_wait(adapter); out: up(&zfcp_data.config_sema); - return retval ? retval : count; + return retval ? retval : (ssize_t) count; } /** --- linux-2.6.9-rc1/drivers/s390/scsi/zfcp_sysfs_driver.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/s390/scsi/zfcp_sysfs_driver.c 2004-09-02 20:51:52.000000000 -0700 @@ -26,7 +26,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#define ZFCP_SYSFS_DRIVER_C_REVISION "$Revision: 1.14 $" +#define ZFCP_SYSFS_DRIVER_C_REVISION "$Revision: 1.15 $" #include "zfcp_ext.h" @@ -65,7 +65,7 @@ static ssize_t zfcp_sysfs_loglevel_##_na static ssize_t zfcp_sysfs_loglevel_##_name##_show(struct device_driver *dev, \ char *buf) \ { \ - return sprintf(buf,"%d\n", \ + return sprintf(buf,"%d\n", (unsigned int) \ ZFCP_GET_LOG_VALUE(ZFCP_LOG_AREA_##_define)); \ } \ \ --- linux-2.6.9-rc1/drivers/s390/scsi/zfcp_sysfs_port.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/drivers/s390/scsi/zfcp_sysfs_port.c 2004-09-02 20:51:52.000000000 -0700 @@ -26,7 +26,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#define ZFCP_SYSFS_PORT_C_REVISION "$Revision: 1.41 $" +#define ZFCP_SYSFS_PORT_C_REVISION "$Revision: 1.44 $" #include "zfcp_ext.h" @@ -66,6 +66,8 @@ ZFCP_DEFINE_PORT_ATTR(status, "0x%08x\n" ZFCP_DEFINE_PORT_ATTR(wwnn, "0x%016llx\n", port->wwnn); ZFCP_DEFINE_PORT_ATTR(d_id, "0x%06x\n", port->d_id); ZFCP_DEFINE_PORT_ATTR(scsi_id, "0x%x\n", port->scsi_id); +ZFCP_DEFINE_PORT_ATTR(in_recovery, "%d\n", atomic_test_mask + (ZFCP_STATUS_COMMON_ERP_INUSE, &port->status)); /** * zfcp_sysfs_unit_add_store - add a unit to sysfs tree @@ -107,7 +109,7 @@ zfcp_sysfs_unit_add_store(struct device zfcp_unit_put(unit); out: up(&zfcp_data.config_sema); - return retval ? retval : count; + return retval ? retval : (ssize_t) count; } static DEVICE_ATTR(unit_add, S_IWUSR, NULL, zfcp_sysfs_unit_add_store); @@ -164,7 +166,7 @@ zfcp_sysfs_unit_remove_store(struct devi zfcp_unit_dequeue(unit); out: up(&zfcp_data.config_sema); - return retval ? retval : count; + return retval ? retval : (ssize_t) count; } static DEVICE_ATTR(unit_remove, S_IWUSR, NULL, zfcp_sysfs_unit_remove_store); @@ -206,7 +208,7 @@ zfcp_sysfs_port_failed_store(struct devi zfcp_erp_wait(port->adapter); out: up(&zfcp_data.config_sema); - return retval ? retval : count; + return retval ? retval : (ssize_t) count; } /** @@ -233,29 +235,6 @@ static DEVICE_ATTR(failed, S_IWUSR | S_I zfcp_sysfs_port_failed_store); /** - * zfcp_sysfs_port_in_recovery_show - recovery state of port - * @dev: pointer to belonging device - * @buf: pointer to input buffer - * - * Show function of "in_recovery" attribute of port. Will be - * "0" if no error recovery is pending for port, otherwise "1". - */ -static ssize_t -zfcp_sysfs_port_in_recovery_show(struct device *dev, char *buf) -{ - struct zfcp_port *port; - - port = dev_get_drvdata(dev); - if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status)) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} - -static DEVICE_ATTR(in_recovery, S_IRUGO, zfcp_sysfs_port_in_recovery_show, - NULL); - -/** * zfcp_port_common_attrs * sysfs attributes that are common for all kind of fc ports. */ @@ -300,7 +279,7 @@ zfcp_sysfs_port_create_files(struct devi retval = sysfs_create_group(&dev->kobj, &zfcp_port_common_attr_group); - if ((flags & ZFCP_STATUS_PORT_NAMESERVER) || retval) + if ((flags & ZFCP_STATUS_PORT_WKA) || retval) return retval; retval = sysfs_create_group(&dev->kobj, &zfcp_port_no_ns_attr_group); @@ -320,7 +299,7 @@ void zfcp_sysfs_port_remove_files(struct device *dev, u32 flags) { sysfs_remove_group(&dev->kobj, &zfcp_port_common_attr_group); - if (!(flags & ZFCP_STATUS_PORT_NAMESERVER)) + if (!(flags & ZFCP_STATUS_PORT_WKA)) sysfs_remove_group(&dev->kobj, &zfcp_port_no_ns_attr_group); } --- linux-2.6.9-rc1/drivers/s390/scsi/zfcp_sysfs_unit.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/drivers/s390/scsi/zfcp_sysfs_unit.c 2004-09-02 20:51:52.000000000 -0700 @@ -26,7 +26,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#define ZFCP_SYSFS_UNIT_C_REVISION "$Revision: 1.25 $" +#define ZFCP_SYSFS_UNIT_C_REVISION "$Revision: 1.27 $" #include "zfcp_ext.h" @@ -64,6 +64,8 @@ static DEVICE_ATTR(_name, S_IRUGO, zfcp_ ZFCP_DEFINE_UNIT_ATTR(status, "0x%08x\n", atomic_read(&unit->status)); ZFCP_DEFINE_UNIT_ATTR(scsi_lun, "0x%x\n", unit->scsi_lun); +ZFCP_DEFINE_UNIT_ATTR(in_recovery, "%d\n", atomic_test_mask + (ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status)); /** * zfcp_sysfs_unit_failed_store - failed state of unit @@ -101,7 +103,7 @@ zfcp_sysfs_unit_failed_store(struct devi zfcp_erp_wait(unit->port->adapter); out: up(&zfcp_data.config_sema); - return retval ? retval : count; + return retval ? retval : (ssize_t) count; } /** @@ -127,29 +129,6 @@ zfcp_sysfs_unit_failed_show(struct devic static DEVICE_ATTR(failed, S_IWUSR | S_IRUGO, zfcp_sysfs_unit_failed_show, zfcp_sysfs_unit_failed_store); -/** - * zfcp_sysfs_unit_in_recovery_show - recovery state of unit - * @dev: pointer to belonging device - * @buf: pointer to input buffer - * - * Show function of "in_recovery" attribute of unit. Will be - * "0" if no error recovery is pending for unit, otherwise "1". - */ -static ssize_t -zfcp_sysfs_unit_in_recovery_show(struct device *dev, char *buf) -{ - struct zfcp_unit *unit; - - unit = dev_get_drvdata(dev); - if (atomic_test_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status)) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} - -static DEVICE_ATTR(in_recovery, S_IRUGO, zfcp_sysfs_unit_in_recovery_show, - NULL); - static struct attribute *zfcp_unit_attrs[] = { &dev_attr_scsi_lun.attr, &dev_attr_failed.attr, --- linux-2.6.9-rc1/drivers/scsi/3w-xxxx.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/drivers/scsi/3w-xxxx.c 2004-09-03 00:57:25.291194416 -0700 @@ -184,6 +184,8 @@ 1.26.00.039 - Fix bug in tw_chrdev_ioctl() polling code. Fix data_buffer_length usage in tw_chrdev_ioctl(). Update contact information. + 1.02.00.XXX - Miquel van Smoorenburg - make queue_depth sysfs parameter + writable, adjust initial queue nr_requests to 2*queue_depth */ #include @@ -3388,8 +3390,6 @@ int tw_slave_configure(Scsi_Device *SDpt { int max_cmds; - dprintk(KERN_WARNING "3w-xxxx: tw_slave_configure()\n"); - if (cmds_per_lun) { max_cmds = cmds_per_lun; if (max_cmds > TW_MAX_CMDS_PER_LUN) @@ -3399,6 +3399,10 @@ int tw_slave_configure(Scsi_Device *SDpt } scsi_adjust_queue_depth(SDptr, MSG_ORDERED_TAG, max_cmds); + /* make sure blockdev queue depth is at least 2 * scsi depth */ + if (SDptr->request_queue->nr_requests < 2 * max_cmds) + SDptr->request_queue->nr_requests = 2 * max_cmds; + return 0; } /* End tw_slave_configure() */ @@ -3482,6 +3486,34 @@ void tw_unmask_command_interrupt(TW_Devi outl(control_reg_value, control_reg_addr); } /* End tw_unmask_command_interrupt() */ +static ssize_t +tw_store_queue_depth(struct device *dev, const char *buf, size_t count) +{ + int depth; + + struct scsi_device *SDp = to_scsi_device(dev); + if (sscanf(buf, "%d", &depth) != 1) + return -EINVAL; + if (depth < 1 || depth > TW_MAX_CMDS_PER_LUN) + return -EINVAL; + scsi_adjust_queue_depth(SDp, MSG_ORDERED_TAG, depth); + + return count; +} + +static struct device_attribute tw_queue_depth_attr = { + .attr = { + .name = "queue_depth", + .mode = S_IWUSR, + }, + .store = tw_store_queue_depth, +}; + +static struct device_attribute *tw_dev_attrs[] = { + &tw_queue_depth_attr, + NULL, +}; + static Scsi_Host_Template driver_template = { .proc_name = "3w-xxxx", .proc_info = tw_scsi_proc_info, @@ -3499,6 +3531,7 @@ static Scsi_Host_Template driver_templat .max_sectors = TW_MAX_SECTORS, .cmd_per_lun = TW_MAX_CMDS_PER_LUN, .use_clustering = ENABLE_CLUSTERING, + .sdev_attrs = tw_dev_attrs, .emulated = 1 }; #include "scsi_module.c" --- linux-2.6.9-rc1/drivers/scsi/aacraid/aachba.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/drivers/scsi/aacraid/aachba.c 2004-09-02 20:51:52.000000000 -0700 @@ -179,6 +179,20 @@ static int aac_send_srb_fib(struct scsi_ static char *aac_get_status_string(u32 status); #endif +/* + * Non dasd selection is handled entirely in aachba now + */ + +MODULE_PARM(nondasd, "i"); +MODULE_PARM_DESC(nondasd, "Control scanning of hba for nondasd devices. 0=off, 1=on"); +MODULE_PARM(dacmode, "i"); +MODULE_PARM_DESC(dacmode, "Control whether dma addressing is using 64 bit DAC. 0=off, 1=on"); +MODULE_PARM(commit, "i"); +MODULE_PARM_DESC(commit, "Control whether a COMMIT_CONFIG is issued to the adapter for foreign arrays.\nThis is typically needed in systems that do not have a BIOS. 0=off, 1=on"); + +static int nondasd = -1; +static int dacmode = -1; + /** * aac_get_containers - list containers * @common: adapter to probe @@ -481,8 +495,7 @@ int aac_get_adapter_info(struct aac_dev* dev->nondasd_support = 0; if(dev->adapter_info.options & AAC_OPT_NONDASD){ -// dev->nondasd_support = 1; -// dmb - temporarily disable nondasd + dev->nondasd_support = 1; } if(nondasd != -1) { dev->nondasd_support = (nondasd!=0); @@ -491,18 +504,30 @@ int aac_get_adapter_info(struct aac_dev* printk(KERN_INFO "%s%d: Non-DASD support enabled.\n",dev->name, dev->id); } - dev->pae_support = 0; + dev->dac_support = 0; if( (sizeof(dma_addr_t) > 4) && (dev->adapter_info.options & AAC_OPT_SGMAP_HOST64)){ printk(KERN_INFO "%s%d: 64bit support enabled.\n", dev->name, dev->id); - dev->pae_support = 1; + dev->dac_support = 1; } - if(paemode != -1){ - dev->pae_support = (paemode!=0); + if(dacmode != -1) { + dev->dac_support = (dacmode!=0); } - if(dev->pae_support != 0) { - printk(KERN_INFO"%s%d: 64 Bit PAE enabled\n", dev->name, dev->id); - pci_set_dma_mask(dev->pdev, (dma_addr_t)0xFFFFFFFFFFFFFFFFULL); + if(dev->dac_support != 0) { + if (!pci_set_dma_mask(dev->pdev, 0xFFFFFFFFFFFFFFFFULL) && + !pci_set_consistent_dma_mask(dev->pdev, 0xFFFFFFFFFFFFFFFFULL)) { + printk(KERN_INFO"%s%d: 64 Bit DAC enabled\n", + dev->name, dev->id); + } else if (!pci_set_dma_mask(dev->pdev, 0xFFFFFFFFULL) && + !pci_set_consistent_dma_mask(dev->pdev, 0xFFFFFFFFULL)) { + printk(KERN_INFO"%s%d: DMA mask set failed, 64 Bit DAC disabled\n", + dev->name, dev->id); + dev->dac_support = 0; + } else { + printk(KERN_WARNING"%s%d: No suitable DMA available.\n", + dev->name, dev->id); + rcode = -ENOMEM; + } } fib_complete(fibptr); @@ -537,7 +562,7 @@ static void read_callback(void *context, scsicmd->use_sg, scsicmd->sc_data_direction); else if(scsicmd->request_bufflen) - pci_unmap_single(dev->pdev, (dma_addr_t)(ulong)scsicmd->SCp.ptr, + pci_unmap_single(dev->pdev, scsicmd->SCp.dma_handle, scsicmd->request_bufflen, scsicmd->sc_data_direction); readreply = (struct aac_read_reply *)fib_data(fibptr); @@ -582,7 +607,7 @@ static void write_callback(void *context scsicmd->use_sg, scsicmd->sc_data_direction); else if(scsicmd->request_bufflen) - pci_unmap_single(dev->pdev, (dma_addr_t)(ulong)scsicmd->SCp.ptr, + pci_unmap_single(dev->pdev, scsicmd->SCp.dma_handle, scsicmd->request_bufflen, scsicmd->sc_data_direction); @@ -644,7 +669,7 @@ int aac_read(struct scsi_cmnd * scsicmd, fib_init(cmd_fibcontext); - if(dev->pae_support == 1){ + if(dev->dac_support == 1) { struct aac_read64 *readcmd; readcmd = (struct aac_read64 *) fib_data(cmd_fibcontext); readcmd->command = cpu_to_le32(VM_CtHostRead64); @@ -752,7 +777,7 @@ static int aac_write(struct scsi_cmnd * } fib_init(cmd_fibcontext); - if(dev->pae_support == 1){ + if(dev->dac_support == 1) { struct aac_write64 *writecmd; writecmd = (struct aac_write64 *) fib_data(cmd_fibcontext); writecmd->command = cpu_to_le32(VM_CtHostWrite64); @@ -1220,7 +1245,7 @@ static void aac_srb_callback(void *conte scsicmd->use_sg, scsicmd->sc_data_direction); else if(scsicmd->request_bufflen) - pci_unmap_single(dev->pdev, (ulong)scsicmd->SCp.ptr, scsicmd->request_bufflen, + pci_unmap_single(dev->pdev, scsicmd->SCp.dma_handle, scsicmd->request_bufflen, scsicmd->sc_data_direction); /* @@ -1348,7 +1373,12 @@ static void aac_srb_callback(void *conte case SRB_STATUS_DOMAIN_VALIDATION_FAIL: default: #ifdef AAC_DETAILED_STATUS_INFO - printk("aacraid: SRB ERROR(%u) %s scsi cmd 0x%x - scsi status 0x%x\n",le32_to_cpu(srbreply->srb_status&0x3f),aac_get_status_string(le32_to_cpu(srbreply->srb_status)), scsicmd->cmnd[0], le32_to_cpu(srbreply->scsi_status) ); + printk("aacraid: SRB ERROR(%u) %s scsi cmd 0x%x - scsi status 0x%x\n", + le32_to_cpu(srbreply->srb_status & 0x3F), + aac_get_status_string( + le32_to_cpu(srbreply->srb_status) & 0x3F), + scsicmd->cmnd[0], + le32_to_cpu(srbreply->scsi_status)); #endif scsicmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8; break; @@ -1358,7 +1388,10 @@ static void aac_srb_callback(void *conte scsicmd->result |= SAM_STAT_CHECK_CONDITION; len = (srbreply->sense_data_size > sizeof(scsicmd->sense_buffer))? sizeof(scsicmd->sense_buffer):srbreply->sense_data_size; - dprintk((KERN_WARNING "aac_srb_callback: check condition, status = %d len=%d\n", le32_to_cpu(srbreply->status), len)); +#ifdef AAC_DETAILED_STATUS_INFO + dprintk((KERN_WARNING "aac_srb_callback: check condition, status = %d len=%d\n", + le32_to_cpu(srbreply->status), len)); +#endif memcpy(scsicmd->sense_buffer, srbreply->sense_data, len); } @@ -1437,7 +1470,7 @@ static int aac_send_srb_fib(struct scsi_ srbcmd->retry_limit =cpu_to_le32(0); // Obsolete parameter srbcmd->cdb_size = cpu_to_le32(scsicmd->cmd_len); - if( dev->pae_support ==1 ) { + if( dev->dac_support == 1 ) { aac_build_sg64(scsicmd, (struct sgmap64*) &srbcmd->sg); srbcmd->count = cpu_to_le32(scsicmd->request_bufflen); @@ -1532,7 +1565,7 @@ static unsigned long aac_build_sg(struct psg->count = cpu_to_le32(1); psg->sg[0].addr = cpu_to_le32(addr); psg->sg[0].count = cpu_to_le32(scsicmd->request_bufflen); - scsicmd->SCp.ptr = (char *)(ulong)addr; + scsicmd->SCp.dma_handle = addr; byte_count = scsicmd->request_bufflen; } return byte_count; @@ -1593,7 +1626,7 @@ static unsigned long aac_build_sg64(stru psg->sg[0].addr[1] = (u32)(le_addr>>32); psg->sg[0].addr[0] = (u32)(le_addr & 0xffffffff); psg->sg[0].count = cpu_to_le32(scsicmd->request_bufflen); - scsicmd->SCp.ptr = (char *)(ulong)addr; + scsicmd->SCp.dma_handle = addr; byte_count = scsicmd->request_bufflen; } return byte_count; --- linux-2.6.9-rc1/drivers/scsi/aacraid/aacraid.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/scsi/aacraid/aacraid.h 2004-09-02 20:51:52.000000000 -0700 @@ -28,10 +28,7 @@ #define aac_phys_to_logical(x) (x+1) #define aac_logical_to_phys(x) (x?x-1:0) -#define AAC_DETAILED_STATUS_INFO - -extern int nondasd; -extern int paemode; +/* #define AAC_DETAILED_STATUS_INFO */ struct diskparm { @@ -404,15 +401,15 @@ struct aac_init }; enum aac_log_level { - LOG_INIT = 10, - LOG_INFORMATIONAL = 20, - LOG_WARNING = 30, - LOG_LOW_ERROR = 40, - LOG_MEDIUM_ERROR = 50, - LOG_HIGH_ERROR = 60, - LOG_PANIC = 70, - LOG_DEBUG = 80, - LOG_WINDBG_PRINT = 90 + LOG_AAC_INIT = 10, + LOG_AAC_INFORMATIONAL = 20, + LOG_AAC_WARNING = 30, + LOG_AAC_LOW_ERROR = 40, + LOG_AAC_MEDIUM_ERROR = 50, + LOG_AAC_HIGH_ERROR = 60, + LOG_AAC_PANIC = 70, + LOG_AAC_DEBUG = 80, + LOG_AAC_WINDBG_PRINT = 90 }; #define FSAFS_NTC_GET_ADAPTER_FIB_CONTEXT 0x030b @@ -841,7 +838,7 @@ struct aac_dev * lets break them out so we don't have to do an AND to check them */ u8 nondasd_support; - u8 pae_support; + u8 dac_support; }; #define AllocateAndMapFibSpace(dev, MapFibContext) \ --- linux-2.6.9-rc1/drivers/scsi/aacraid/commctrl.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/drivers/scsi/aacraid/commctrl.c 2004-09-02 20:51:52.000000000 -0700 @@ -480,7 +480,7 @@ int aac_send_raw_srb(struct aac_dev* dev default: data_dir = DMA_NONE; } - if (dev->pae_support == 1) { + if (dev->dac_support == 1) { struct sgmap64* psg = (struct sgmap64*)&srbcmd->sg; byte_count = 0; --- linux-2.6.9-rc1/drivers/scsi/aacraid/commsup.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/drivers/scsi/aacraid/commsup.c 2004-09-02 20:51:52.000000000 -0700 @@ -761,7 +761,7 @@ void aac_printf(struct aac_dev *dev, u32 length = 255; if (cp[length] != 0) cp[length] = 0; - if (level == LOG_HIGH_ERROR) + if (level == LOG_AAC_HIGH_ERROR) printk(KERN_WARNING "aacraid:%s", cp); else printk(KERN_INFO "aacraid:%s", cp); --- linux-2.6.9-rc1/drivers/scsi/aacraid/linit.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/drivers/scsi/aacraid/linit.c 2004-09-02 20:51:52.000000000 -0700 @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -62,15 +63,7 @@ MODULE_DESCRIPTION("Dell PERC2, 2/Si, 3/ "Adaptec Advanced Raid Products, " "and HP NetRAID-4M SCSI driver"); MODULE_LICENSE("GPL"); - - -int nondasd = -1; -module_param(nondasd, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(nondasd, "Control scanning of hba for nondasd devices. 0=off, 1=on"); - -int paemode = -1; -module_param(paemode, int, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(paemode, "Control whether dma addressing is using PAE. 0=off, 1=on"); +MODULE_VERSION(AAC_DRIVER_VERSION); struct aac_dev *aac_devices[MAXIMUM_NUM_ADAPTERS]; static unsigned aac_count; @@ -83,44 +76,54 @@ static int aac_cfg_major = -1; * Note: The last field is used to index into aac_drivers below. */ static struct pci_device_id aac_pci_tbl[] = { - { 0x1028, 0x0001, 0x1028, 0x0001, 0, 0, 0 }, /* PERC 2/Si */ - { 0x1028, 0x0002, 0x1028, 0x0002, 0, 0, 1 }, /* PERC 3/Di */ - { 0x1028, 0x0003, 0x1028, 0x0003, 0, 0, 2 }, /* PERC 3/Si */ - { 0x1028, 0x0004, 0x1028, 0x00d0, 0, 0, 3 }, /* PERC 3/Si */ - { 0x1028, 0x0002, 0x1028, 0x00d1, 0, 0, 4 }, /* PERC 3/Di */ - { 0x1028, 0x0002, 0x1028, 0x00d9, 0, 0, 5 }, /* PERC 3/Di */ - { 0x1028, 0x000a, 0x1028, 0x0106, 0, 0, 6 }, /* PERC 3/Di */ - { 0x1028, 0x000a, 0x1028, 0x011b, 0, 0, 7 }, /* PERC 3/Di */ - { 0x1028, 0x000a, 0x1028, 0x0121, 0, 0, 8 }, /* PERC 3/Di */ - { 0x9005, 0x0283, 0x9005, 0x0283, 0, 0, 9 }, /* catapult*/ - { 0x9005, 0x0284, 0x9005, 0x0284, 0, 0, 10 }, /* tomcat*/ - { 0x9005, 0x0285, 0x9005, 0x0286, 0, 0, 11 }, /* Adaptec 2120S (Crusader)*/ - { 0x9005, 0x0285, 0x9005, 0x0285, 0, 0, 12 }, /* Adaptec 2200S (Vulcan)*/ - { 0x9005, 0x0285, 0x9005, 0x0287, 0, 0, 13 }, /* Adaptec 2200S (Vulcan-2m)*/ - { 0x9005, 0x0285, 0x17aa, 0x0286, 0, 0, 14 }, /* Legend S220*/ - { 0x9005, 0x0285, 0x17aa, 0x0287, 0, 0, 15 }, /* Legend S230*/ - - { 0x9005, 0x0285, 0x9005, 0x0288, 0, 0, 16 }, /* Adaptec 3230S (Harrier)*/ - { 0x9005, 0x0285, 0x9005, 0x0289, 0, 0, 17 }, /* Adaptec 3240S (Tornado)*/ - { 0x9005, 0x0285, 0x9005, 0x028a, 0, 0, 18 }, /* ASR-2020 ZCR PCI-X U320 */ - { 0x9005, 0x0285, 0x9005, 0x028b, 0, 0, 19 }, /* ASR-2025 ZCR DIMM U320 */ - { 0x9005, 0x0285, 0x9005, 0x0290, 0, 0, 20 }, /* AAR-2410SA PCI SATA 4ch (Jaguar II)*/ - - { 0x9005, 0x0285, 0x1028, 0x0287, 0, 0, 21 }, /* Perc 320/DC*/ - { 0x1011, 0x0046, 0x9005, 0x0365, 0, 0, 22 }, /* Adaptec 5400S (Mustang)*/ - { 0x1011, 0x0046, 0x9005, 0x0364, 0, 0, 23 }, /* Adaptec 5400S (Mustang)*/ - { 0x1011, 0x0046, 0x9005, 0x1364, 0, 0, 24 }, /* Dell PERC2 "Quad Channel" */ - { 0x1011, 0x0046, 0x103c, 0x10c2, 0, 0, 25 }, /* HP NetRAID-4M */ - + { 0x1028, 0x0001, 0x1028, 0x0001, 0, 0, 0 }, /* PERC 2/Si (Iguana/PERC2Si) */ + { 0x1028, 0x0002, 0x1028, 0x0002, 0, 0, 1 }, /* PERC 3/Di (Opal/PERC3Di) */ + { 0x1028, 0x0003, 0x1028, 0x0003, 0, 0, 2 }, /* PERC 3/Si (SlimFast/PERC3Si */ + { 0x1028, 0x0004, 0x1028, 0x00d0, 0, 0, 3 }, /* PERC 3/Di (Iguana FlipChip/PERC3DiF */ + { 0x1028, 0x0002, 0x1028, 0x00d1, 0, 0, 4 }, /* PERC 3/Di (Viper/PERC3DiV) */ + { 0x1028, 0x0002, 0x1028, 0x00d9, 0, 0, 5 }, /* PERC 3/Di (Lexus/PERC3DiL) */ + { 0x1028, 0x000a, 0x1028, 0x0106, 0, 0, 6 }, /* PERC 3/Di (Jaguar/PERC3DiJ) */ + { 0x1028, 0x000a, 0x1028, 0x011b, 0, 0, 7 }, /* PERC 3/Di (Dagger/PERC3DiD) */ + { 0x1028, 0x000a, 0x1028, 0x0121, 0, 0, 8 }, /* PERC 3/Di (Boxster/PERC3DiB) */ + { 0x9005, 0x0283, 0x9005, 0x0283, 0, 0, 9 }, /* catapult */ + { 0x9005, 0x0284, 0x9005, 0x0284, 0, 0, 10 }, /* tomcat */ + { 0x9005, 0x0285, 0x9005, 0x0286, 0, 0, 11 }, /* Adaptec 2120S (Crusader) */ + { 0x9005, 0x0285, 0x9005, 0x0285, 0, 0, 12 }, /* Adaptec 2200S (Vulcan) */ + { 0x9005, 0x0285, 0x9005, 0x0287, 0, 0, 13 }, /* Adaptec 2200S (Vulcan-2m) */ + { 0x9005, 0x0285, 0x17aa, 0x0286, 0, 0, 14 }, /* Legend S220 (Legend Crusader) */ + { 0x9005, 0x0285, 0x17aa, 0x0287, 0, 0, 15 }, /* Legend S230 (Legend Vulcan) */ + + { 0x9005, 0x0285, 0x9005, 0x0288, 0, 0, 16 }, /* Adaptec 3230S (Harrier) */ + { 0x9005, 0x0285, 0x9005, 0x0289, 0, 0, 17 }, /* Adaptec 3240S (Tornado) */ + { 0x9005, 0x0285, 0x9005, 0x028a, 0, 0, 18 }, /* ASR-2020ZCR SCSI PCI-X ZCR (Skyhawk) */ + { 0x9005, 0x0285, 0x9005, 0x028b, 0, 0, 19 }, /* ASR-2025ZCR SCSI SO-DIMM PCI-X ZCR (Terminator) */ + { 0x9005, 0x0286, 0x9005, 0x028c, 0, 0, 20 }, /* ASR-2230S + ASR-2230SLP PCI-X (Lancer) */ + { 0x9005, 0x0286, 0x9005, 0x028d, 0, 0, 21 }, /* ASR-2130S (Lancer) */ + { 0x9005, 0x0286, 0x9005, 0x0800, 0, 0, 22 }, /* Jupiter Platform */ + { 0x9005, 0x0285, 0x9005, 0x028e, 0, 0, 23 }, /* ASR-2020SA SATA PCI-X ZCR (Skyhawk) */ + { 0x9005, 0x0285, 0x9005, 0x028f, 0, 0, 24 }, /* ASR-2025SA SATA SO-DIMM PCI-X ZCR (Terminator) */ + { 0x9005, 0x0285, 0x9005, 0x0290, 0, 0, 25 }, /* AAR-2410SA PCI SATA 4ch (Jaguar II) */ { 0x9005, 0x0285, 0x1028, 0x0291, 0, 0, 26 }, /* CERC SATA RAID 2 PCI SATA 6ch (DellCorsair) */ { 0x9005, 0x0285, 0x9005, 0x0292, 0, 0, 27 }, /* AAR-2810SA PCI SATA 8ch (Corsair-8) */ { 0x9005, 0x0285, 0x9005, 0x0293, 0, 0, 28 }, /* AAR-21610SA PCI SATA 16ch (Corsair-16) */ { 0x9005, 0x0285, 0x9005, 0x0294, 0, 0, 29 }, /* ESD SO-DIMM PCI-X SATA ZCR (Prowler) */ - { 0x9005, 0x0285, 0x0E11, 0x0295, 0, 0, 30 }, /* SATA 6Ch (Bearcat) */ - - { 0x9005, 0x0286, 0x9005, 0x028c, 0, 0, 31 }, /* ASR-2230S + ASR-2230SLP PCI-X (Lancer) */ - { 0x9005, 0x0285, 0x9005, 0x028e, 0, 0, 32 }, /* ASR-2020SA (ZCR PCI-X SATA) */ - { 0x9005, 0x0285, 0x9005, 0x028f, 0, 0, 33 }, /* ASR-2025SA (ZCR DIMM SATA) */ + { 0x9005, 0x0285, 0x0E11, 0x0295, 0, 0, 30 }, /* AAR-2610SA PCI SATA 6ch */ + { 0x9005, 0x0285, 0x9005, 0x0296, 0, 0, 31 }, /* ASR-2240S */ + { 0x9005, 0x0285, 0x9005, 0x0296, 0, 0, 32 }, /* ASR-4005SAS */ + { 0x9005, 0x0285, 0x9005, 0x0296, 0, 0, 33 }, /* ASR-4000SAS */ + { 0x9005, 0x0285, 0x9005, 0x0296, 0, 0, 34 }, /* ASR-4800SAS */ + { 0x9005, 0x0285, 0x9005, 0x0296, 0, 0, 35 }, /* ASR-4805SAS */ + + { 0x9005, 0x0285, 0x1028, 0x0287, 0, 0, 36 }, /* Perc 320/DC*/ + { 0x1011, 0x0046, 0x9005, 0x0365, 0, 0, 37 }, /* Adaptec 5400S (Mustang)*/ + { 0x1011, 0x0046, 0x9005, 0x0364, 0, 0, 38 }, /* Adaptec 5400S (Mustang)*/ + { 0x1011, 0x0046, 0x9005, 0x1364, 0, 0, 39 }, /* Dell PERC2/QC */ + { 0x1011, 0x0046, 0x103c, 0x10c2, 0, 0, 40 }, /* HP NetRAID-4M */ + + { 0x9005, 0x0285, 0x1028, PCI_ANY_ID, 0, 0, 41 }, /* Dell Catchall */ + { 0x9005, 0x0285, 0x17aa, PCI_ANY_ID, 0, 0, 42 }, /* Legend Catchall */ + { 0x9005, 0x0285, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 43 }, /* Adaptec Catch All */ + { 0x9005, 0x0286, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 44 }, /* Adaptec Rocket Catch All */ { 0,} }; MODULE_DEVICE_TABLE(pci, aac_pci_tbl); @@ -131,44 +134,54 @@ MODULE_DEVICE_TABLE(pci, aac_pci_tbl); * for the card. At that time we can remove the channels from here */ static struct aac_driver_ident aac_drivers[] = { - { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT }, /* PERC 2/Si */ - { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT }, /* PERC 3/Di */ - { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT }, /* PERC 3/Si */ - { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT }, /* PERC 3/Si */ - { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT }, /* PERC 3/Di */ - { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT }, /* PERC 3/Di */ - { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT }, /* PERC 3/Di */ - { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT }, /* PERC 3/Di */ - { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT }, /* PERC 3/Di */ - { aac_rx_init, "aacraid", "ADAPTEC ", "catapult ", 2, AAC_QUIRK_31BIT }, /* catapult*/ - { aac_rx_init, "aacraid", "ADAPTEC ", "tomcat ", 2, AAC_QUIRK_31BIT }, /* tomcat*/ - { aac_rx_init, "aacraid", "ADAPTEC ", "Adaptec 2120S ", 1, AAC_QUIRK_31BIT }, /* Adaptec 2120S (Crusader)*/ - { aac_rx_init, "aacraid", "ADAPTEC ", "Adaptec 2200S ", 2, AAC_QUIRK_31BIT }, /* Adaptec 2200S (Vulcan)*/ - { aac_rx_init, "aacraid", "ADAPTEC ", "Adaptec 2200S ", 2, AAC_QUIRK_31BIT }, /* Adaptec 2200S (Vulcan-2m)*/ - { aac_rx_init, "aacraid", "Legend ", "Legend S220 ", 1, AAC_QUIRK_31BIT }, /* Legend S220*/ - { aac_rx_init, "aacraid", "Legend ", "Legend S230 ", 2, AAC_QUIRK_31BIT }, /* Legend S230*/ - - { aac_rx_init, "aacraid", "ADAPTEC ", "Adaptec 3230S ", 2 }, /* Adaptec 3230S (Harrier)*/ - { aac_rx_init, "aacraid", "ADAPTEC ", "Adaptec 3240S ", 2 }, /* Adaptec 3240S (Tornado)*/ - { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-2020ZCR ", 2 }, /* ASR-2020 ZCR PCI-X U320 */ - { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-2025ZCR ", 2 }, /* ASR-2025 ZCR DIMM U320 */ - { aac_rx_init, "aacraid", "ADAPTEC ", "AAR-2410SA SATA ", 2 }, /* AAR-2410SA PCI SATA 4ch (Jaguar II)*/ - - { aac_rx_init, "percraid", "DELL ", "PERC 320/DC ", 2, AAC_QUIRK_31BIT }, /* Perc 320/DC*/ - { aac_sa_init, "aacraid", "ADAPTEC ", "Adaptec 5400S ", 4 }, /* Adaptec 5400S (Mustang)*/ - { aac_sa_init, "aacraid", "ADAPTEC ", "AAC-364 ", 4 }, /* Adaptec 5400S (Mustang)*/ - { aac_sa_init, "percraid", "DELL ", "PERCRAID ", 4, AAC_QUIRK_31BIT }, /* Dell PERC2 "Quad Channel" */ - { aac_sa_init, "hpnraid", "HP ", "NetRAID ", 4 }, /* HP NetRAID-4M */ - + { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT }, /* PERC 2/Si (Iguana/PERC2Si) */ + { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT }, /* PERC 3/Di (Opal/PERC3Di) */ + { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT }, /* PERC 3/Si (SlimFast/PERC3Si */ + { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT }, /* PERC 3/Di (Iguana FlipChip/PERC3DiF */ + { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT }, /* PERC 3/Di (Viper/PERC3DiV) */ + { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT }, /* PERC 3/Di (Lexus/PERC3DiL) */ + { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 1, AAC_QUIRK_31BIT }, /* PERC 3/Di (Jaguar/PERC3DiJ) */ + { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT }, /* PERC 3/Di (Dagger/PERC3DiD) */ + { aac_rx_init, "percraid", "DELL ", "PERCRAID ", 2, AAC_QUIRK_31BIT }, /* PERC 3/Di (Boxster/PERC3DiB) */ + { aac_rx_init, "aacraid", "ADAPTEC ", "catapult ", 2, AAC_QUIRK_31BIT }, /* catapult */ + { aac_rx_init, "aacraid", "ADAPTEC ", "tomcat ", 2, AAC_QUIRK_31BIT }, /* tomcat */ + { aac_rx_init, "aacraid", "ADAPTEC ", "Adaptec 2120S ", 1, AAC_QUIRK_31BIT }, /* Adaptec 2120S (Crusader) */ + { aac_rx_init, "aacraid", "ADAPTEC ", "Adaptec 2200S ", 2, AAC_QUIRK_31BIT }, /* Adaptec 2200S (Vulcan) */ + { aac_rx_init, "aacraid", "ADAPTEC ", "Adaptec 2200S ", 2, AAC_QUIRK_31BIT }, /* Adaptec 2200S (Vulcan-2m) */ + { aac_rx_init, "aacraid", "Legend ", "Legend S220 ", 1, AAC_QUIRK_31BIT }, /* Legend S220 (Legend Crusader) */ + { aac_rx_init, "aacraid", "Legend ", "Legend S230 ", 2, AAC_QUIRK_31BIT }, /* Legend S230 (Legend Vulcan) */ + + { aac_rx_init, "aacraid", "ADAPTEC ", "Adaptec 3230S ", 2 }, /* Adaptec 3230S (Harrier) */ + { aac_rx_init, "aacraid", "ADAPTEC ", "Adaptec 3240S ", 2 }, /* Adaptec 3240S (Tornado) */ + { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-2020ZCR ", 2 }, /* ASR-2020ZCR SCSI PCI-X ZCR (Skyhawk) */ + { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-2025ZCR ", 2 }, /* ASR-2025ZCR SCSI SO-DIMM PCI-X ZCR (Terminator) */ + { aac_rkt_init, "aacraid", "ADAPTEC ", "ASR-2230S PCI-X ", 2 }, /* ASR-2230S + ASR-2230SLP PCI-X (Lancer) */ + { aac_rkt_init, "aacraid", "ADAPTEC ", "ASR-2130S PCI-X ", 1 }, /* ASR-2130S (Lancer) */ + { aac_rkt_init, "aacraid", "ADAPTEC ", "Callisto ", 2 }, /* Jupiter Platform */ + { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-2020SA ", 1 }, /* ASR-2020SA SATA PCI-X ZCR (Skyhawk) */ + { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-2025SA ", 1 }, /* ASR-2025SA SATA SO-DIMM PCI-X ZCR (Terminator) */ + { aac_rx_init, "aacraid", "ADAPTEC ", "AAR-2410SA SATA ", 1 }, /* AAR-2410SA PCI SATA 4ch (Jaguar II) */ { aac_rx_init, "aacraid", "DELL ", "CERC SR2 ", 1 }, /* CERC SATA RAID 2 PCI SATA 6ch (DellCorsair) */ { aac_rx_init, "aacraid", "ADAPTEC ", "AAR-2810SA SATA ", 1 }, /* AAR-2810SA PCI SATA 8ch (Corsair-8) */ { aac_rx_init, "aacraid", "ADAPTEC ", "AAR-21610SA SATA", 1 }, /* AAR-21610SA PCI SATA 16ch (Corsair-16) */ { aac_rx_init, "aacraid", "ADAPTEC ", "SO-DIMM SATA ZCR", 1 }, /* ESD SO-DIMM PCI-X SATA ZCR (Prowler) */ - { aac_rx_init, "aacraid", "ADAPTEC ", "SATA 6Channel ", 1 }, /* SATA 6Ch (Bearcat) */ + { aac_rx_init, "aacraid", "ADAPTEC ", "AAR-2610SA ", 1 }, /* SATA 6Ch (Bearcat) */ + { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-2240S ", 1 }, /* ASR-2240S */ + { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-4005SAS ", 1 }, /* ASR-4005SAS */ + { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-4000SAS ", 1 }, /* ASR-4000SAS */ + { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-4800SAS ", 1 }, /* ASR-4800SAS */ + { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-4805SAS ", 1 }, /* ASR-4805SAS */ - { aac_rkt_init,"aacraid", "ADAPTEC ", "ASR-2230S PCI-X ", 2 }, /* ASR-2230S + ASR-2230SLP PCI-X (Lancer) */ - { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-2020SA ", 1 }, /* ASR-2020SA (ZCR PCI-X SATA) */ - { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-2025SA ", 1 }, /* ASR-2025SA (ZCR DIMM SATA) */ + { aac_rx_init, "percraid", "DELL ", "PERC 320/DC ", 2, AAC_QUIRK_31BIT }, /* Perc 320/DC*/ + { aac_sa_init, "aacraid", "ADAPTEC ", "Adaptec 5400S ", 4 }, /* Adaptec 5400S (Mustang)*/ + { aac_sa_init, "aacraid", "ADAPTEC ", "AAC-364 ", 4 }, /* Adaptec 5400S (Mustang)*/ + { aac_sa_init, "percraid", "DELL ", "PERCRAID ", 4, AAC_QUIRK_31BIT }, /* Dell PERC2/QC */ + { aac_sa_init, "hpnraid", "HP ", "NetRAID ", 4 }, /* HP NetRAID-4M */ + + { aac_rx_init, "aacraid", "DELL ", "RAID ", 2, AAC_QUIRK_31BIT }, /* Dell Catchall */ + { aac_rx_init, "aacraid", "Legend ", "RAID ", 2, AAC_QUIRK_31BIT }, /* Legend Catchall */ + { aac_rx_init, "aacraid", "ADAPTEC ", "RAID ", 2, AAC_QUIRK_31BIT }, /* Adaptec Catch All */ + { aac_rkt_init, "aacraid", "ADAPTEC ", "RAID ", 2 } /* Adaptec Rocket Catch All */ }; #ifdef CONFIG_COMPAT @@ -408,15 +421,17 @@ static int aac_eh_reset(struct scsi_cmnd } } spin_unlock_irqrestore(&dev->list_lock, flags); + if (active) + break; - /* - * We can exit If all the commands are complete - */ - if (active == 0) - return SUCCESS; } + /* + * We can exit If all the commands are complete + */ + if (active == 0) + return SUCCESS; spin_unlock_irq(host->host_lock); - scsi_sleep(HZ); + ssleep(1); spin_lock_irq(host->host_lock); } printk(KERN_ERR "%s: SCSI bus appears hung\n", AAC_DRIVERNAME); --- linux-2.6.9-rc1/drivers/scsi/aacraid/README 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/scsi/aacraid/README 2004-09-02 20:51:52.000000000 -0700 @@ -10,14 +10,23 @@ the original). Supported Cards/Chipsets ------------------------- - AAR-2410SA SATA + Adaptec 2020S + Adaptec 2025S Adaptec 2120S Adaptec 2200S Adaptec 2230S + Adaptec 2240S + Adaptec 2410SA + Adaptec 2610SA + Adaptec 2810SA + Adaptec 21610SA Adaptec 3230S Adaptec 3240S + Adaptec 4000SAS + Adaptec 4005SAS + Adaptec 4800SAS + Adaptec 4805SAS Adaptec 5400S - ASR-2020S PCI-X Dell PERC 2 Quad Channel Dell PERC 2/Si Dell PERC 3/Si @@ -49,7 +58,6 @@ Adaptec Unix OEM Product Group Mailing List ------------------------- linux-scsi@vger.kernel.org (Interested parties troll here) -http://mbserver.adaptec.com/ (Currently more Community Support than Devel Support) Also note this is very different to Brian's original driver so don't expect him to support it. Adaptec does support this driver. Contact either tech support or Mark Salyzyn. --- linux-2.6.9-rc1/drivers/scsi/advansys.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/drivers/scsi/advansys.c 2004-09-02 20:51:52.000000000 -0700 @@ -15,16 +15,6 @@ * As of March 8, 2000 Advanced System Products, Inc. (AdvanSys) * changed its name to ConnectCom Solutions, Inc. * - * There is an AdvanSys Linux WWW page at: - * http://www.connectcom.net/downloads/software/os/linux.html - * http://www.advansys.com/linux.html - * - * The latest released version of the AdvanSys driver is available at: - * ftp://ftp.advansys.com/pub/linux/linux.tgz - * ftp://ftp.connectcom.net/pub/linux/linux.tgz - * - * Please send questions, comments, bug reports to: - * support@connectcom.net */ /* @@ -41,7 +31,6 @@ H. Release History I. Known Problems/Fix List J. Credits (Chronological Order) - K. ConnectCom (AdvanSys) Contact Information A. Linux Kernels Supported by this Driver @@ -2032,9 +2021,6 @@ STATIC ASC_DCNT AscGetMaxDmaCount(ushor #define ADV_LIB_VERSION_MAJOR 5 #define ADV_LIB_VERSION_MINOR 14 -/* d_os_dep.h */ -#define ADV_OS_LINUX - /* * Define Adv Library required special types. */ @@ -3370,9 +3356,9 @@ do { \ /* * Default EEPROM Configuration structure defined in a_init.c. */ -extern ADVEEP_3550_CONFIG Default_3550_EEPROM_Config; -extern ADVEEP_38C0800_CONFIG Default_38C0800_EEPROM_Config; -extern ADVEEP_38C1600_CONFIG Default_38C1600_EEPROM_Config; +static ADVEEP_3550_CONFIG Default_3550_EEPROM_Config; +static ADVEEP_38C0800_CONFIG Default_38C0800_EEPROM_Config; +static ADVEEP_38C1600_CONFIG Default_38C1600_EEPROM_Config; /* * DvcGetPhyAddr() flag arguments --- linux-2.6.9-rc1/drivers/scsi/advansys.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/scsi/advansys.h 2004-09-02 20:51:52.000000000 -0700 @@ -13,37 +13,11 @@ * As of March 8, 2000 Advanced System Products, Inc. (AdvanSys) * changed its name to ConnectCom Solutions, Inc. * - * There is an AdvanSys Linux WWW page at: - * http://www.connectcom.net/downloads/software/os/linux.html - * http://www.advansys.com/linux.html - * - * The latest released version of the AdvanSys driver is available at: - * ftp://ftp.advansys.com/pub/linux/linux.tgz - * ftp://ftp.connectcom.net/pub/linux/linux.tgz - * - * Please send questions, comments, bug reports to: - * linux@connectcom.net or bfrey@turbolinux.com.cn */ #ifndef _ADVANSYS_H #define _ADVANSYS_H -#include -#ifndef LINUX_VERSION_CODE -#include -#endif /* LINUX_VERSION_CODE */ - -/* Convert Linux Version, Patch-level, Sub-level to LINUX_VERSION_CODE. */ -#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S)) -/* Driver supported only in version 2.2 and version >= 2.4. */ -#if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,2,0) || \ - (LINUX_VERSION_CODE > ASC_LINUX_VERSION(2,3,0) && \ - LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,4,0)) -#error "AdvanSys driver supported only in 2.2 and 2.4 or greater kernels." -#endif -#define ASC_LINUX_KERNEL22 (LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,4,0)) -#define ASC_LINUX_KERNEL24 (LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,4,0)) - /* * Scsi_Host_Template function prototypes. */ --- linux-2.6.9-rc1/drivers/scsi/aha1542.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/scsi/aha1542.c 2004-09-02 20:51:52.000000000 -0700 @@ -1479,7 +1479,7 @@ static int aha1542_bus_reset(Scsi_Cmnd * * we are pretty desperate anyways. */ spin_unlock_irq(SCpnt->device->host->host_lock); - scsi_sleep(4 * HZ); + ssleep(4); spin_lock_irq(SCpnt->device->host->host_lock); WAIT(STATUS(SCpnt->device->host->io_port), @@ -1543,7 +1543,7 @@ static int aha1542_host_reset(Scsi_Cmnd * we are pretty desperate anyways. */ spin_unlock_irq(SCpnt->device->host->host_lock); - scsi_sleep(4 * HZ); + ssleep(4); spin_lock_irq(SCpnt->device->host->host_lock); WAIT(STATUS(SCpnt->device->host->io_port), --- linux-2.6.9-rc1/drivers/scsi/aha1542.h 2004-08-24 00:03:18.000000000 -0700 +++ 25/drivers/scsi/aha1542.h 2004-09-02 20:51:52.000000000 -0700 @@ -91,10 +91,12 @@ struct chain { }; /* These belong in scsi.h also */ -#define any2scsi(up, p) \ -(up)[0] = (((unsigned long)(p)) >> 16) ; \ -(up)[1] = (((unsigned long)(p)) >> 8); \ -(up)[2] = ((unsigned long)(p)); +static inline void any2scsi(u8 *p, u32 v) +{ + p[0] = v >> 16; + p[1] = v >> 8; + p[2] = v; +} #define scsi2int(up) ( (((long)*(up)) << 16) + (((long)(up)[1]) << 8) + ((long)(up)[2]) ) --- linux-2.6.9-rc1/drivers/scsi/aic7xxx/aic79xx_osm.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/drivers/scsi/aic7xxx/aic79xx_osm.c 2004-09-02 20:51:52.000000000 -0700 @@ -61,6 +61,7 @@ #endif #include /* For fetching system memory size */ +#include /* For ssleep/msleep */ /* * Lock protecting manipulation of the ahd softc list. @@ -415,7 +416,6 @@ uint32_t aic79xx_periodic_otag; /* * Module information and settable options. */ -#ifdef MODULE static char *aic79xx = NULL; /* * Just in case someone uses commas to separate items on the insmod @@ -426,9 +426,8 @@ static char dummy_buffer[60] = "Please d MODULE_AUTHOR("Maintainer: Justin T. Gibbs "); MODULE_DESCRIPTION("Adaptec Aic790X U320 SCSI Host Bus Adapter driver"); -#ifdef MODULE_LICENSE MODULE_LICENSE("Dual BSD/GPL"); -#endif +MODULE_VERSION(AIC79XX_DRIVER_VERSION); MODULE_PARM(aic79xx, "s"); MODULE_PARM_DESC(aic79xx, "period delimited, options string.\n" @@ -463,7 +462,6 @@ MODULE_PARM_DESC(aic79xx, " Change Read Streaming for Controller's 2 and 3\n" "\n" " options aic79xx 'aic79xx=rd_strm:{..0xFFF0.0xC0F0}'"); -#endif static void ahd_linux_handle_scsi_status(struct ahd_softc *, struct ahd_linux_device *, @@ -3173,7 +3171,7 @@ ahd_linux_dv_transition(struct ahd_softc break; } if (status & SSQ_DELAY) - scsi_sleep(1 * HZ); + ssleep(1); break; case SS_START: @@ -3333,7 +3331,7 @@ ahd_linux_dv_transition(struct ahd_softc } if (targ->dv_state_retry <= 10) { if ((status & (SSQ_DELAY_RANDOM|SSQ_DELAY))!= 0) - scsi_sleep(ahd->our_id*HZ/10); + msleep(ahd->our_id*1000/10); break; } #ifdef AHD_DEBUG @@ -3377,7 +3375,7 @@ ahd_linux_dv_transition(struct ahd_softc targ->dv_state_retry--; } else if (targ->dv_state_retry < 60) { if ((status & SSQ_DELAY) != 0) - scsi_sleep(1 * HZ); + ssleep(1); } else { #ifdef AHD_DEBUG if (ahd_debug & AHD_SHOW_DV) { --- linux-2.6.9-rc1/drivers/scsi/aic7xxx/aic79xx_pci.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/drivers/scsi/aic7xxx/aic79xx_pci.c 2004-09-02 20:51:52.000000000 -0700 @@ -452,8 +452,10 @@ ahd_pci_test_register_access(struct ahd_ * or read prefetching could be initiated by the * CPU or host bridge. Our device does not support * either, so look for data corruption and/or flaged - * PCI errors. + * PCI errors. First pause without causing another + * chip reset. */ + hcntrl &= ~CHIPRST; ahd_outb(ahd, HCNTRL, hcntrl|PAUSE); while (ahd_is_paused(ahd) == 0) ; --- linux-2.6.9-rc1/drivers/scsi/aic7xxx/aic7xxx_osm.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/scsi/aic7xxx/aic7xxx_osm.c 2004-09-02 20:51:52.000000000 -0700 @@ -140,6 +140,7 @@ #include /* For fetching system memory size */ #include /* For block_size() */ +#include /* For ssleep/msleep */ /* * Lock protecting manipulation of the ahc softc list. @@ -436,7 +437,6 @@ uint32_t aic7xxx_periodic_otag; /* * Module information and settable options. */ -#ifdef MODULE static char *aic7xxx = NULL; /* * Just in case someone uses commas to separate items on the insmod @@ -447,9 +447,8 @@ static char dummy_buffer[60] = "Please d MODULE_AUTHOR("Maintainer: Justin T. Gibbs "); MODULE_DESCRIPTION("Adaptec Aic77XX/78XX SCSI Host Bus Adapter driver"); -#ifdef MODULE_LICENSE MODULE_LICENSE("Dual BSD/GPL"); -#endif +MODULE_VERSION(AIC7XXX_DRIVER_VERSION); MODULE_PARM(aic7xxx, "s"); MODULE_PARM_DESC(aic7xxx, "period delimited, options string.\n" @@ -479,7 +478,6 @@ MODULE_PARM_DESC(aic7xxx, "\n" " options aic7xxx 'aic7xxx=probe_eisa_vl.tag_info:{{}.{.10}}.seltime:1'\n" ); -#endif static void ahc_linux_handle_scsi_status(struct ahc_softc *, struct ahc_linux_device *, @@ -2825,7 +2823,7 @@ ahc_linux_dv_transition(struct ahc_softc break; } if (status & SSQ_DELAY) - scsi_sleep(1 * HZ); + ssleep(1); break; case SS_START: @@ -2985,7 +2983,7 @@ ahc_linux_dv_transition(struct ahc_softc } if (targ->dv_state_retry <= 10) { if ((status & (SSQ_DELAY_RANDOM|SSQ_DELAY))!= 0) - scsi_sleep(ahc->our_id*HZ/10); + msleep(ahc->our_id*1000/10); break; } #ifdef AHC_DEBUG @@ -3029,7 +3027,7 @@ ahc_linux_dv_transition(struct ahc_softc targ->dv_state_retry--; } else if (targ->dv_state_retry < 60) { if ((status & SSQ_DELAY) != 0) - scsi_sleep(1 * HZ); + ssleep(1); } else { #ifdef AHC_DEBUG if (ahc_debug & AHC_SHOW_DV) { --- linux-2.6.9-rc1/drivers/scsi/aic7xxx/aic7xxx_pci.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/drivers/scsi/aic7xxx/aic7xxx_pci.c 2004-09-02 20:51:52.000000000 -0700 @@ -1284,8 +1284,10 @@ ahc_pci_test_register_access(struct ahc_ * or read prefetching could be initiated by the * CPU or host bridge. Our device does not support * either, so look for data corruption and/or flagged - * PCI errors. + * PCI errors. First pause without causing another + * chip reset. */ + hcntrl &= ~CHIPRST; ahc_outb(ahc, HCNTRL, hcntrl|PAUSE); while (ahc_is_paused(ahc) == 0) ; --- linux-2.6.9-rc1/drivers/scsi/aic7xxx/aicasm/Makefile 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/scsi/aic7xxx/aicasm/Makefile 2004-09-02 20:51:52.000000000 -0700 @@ -34,10 +34,14 @@ $(PROG): ${GENHDRS} $(SRCS) $(AICASM_CC) $(AICASM_CFLAGS) $(SRCS) -o $(PROG) $(LIBS) aicdb.h: - @if [ -e "/usr/include/db3/db_185.h" ]; then \ + @if [ -e "/usr/include/db4/db_185.h" ]; then \ + echo "#include " > aicdb.h; \ + elif [ -e "/usr/include/db3/db_185.h" ]; then \ echo "#include " > aicdb.h; \ elif [ -e "/usr/include/db2/db_185.h" ]; then \ echo "#include " > aicdb.h; \ + elif [ -e "/usr/include/db1/db_185.h" ]; then \ + echo "#include " > aicdb.h; \ elif [ -e "/usr/include/db/db_185.h" ]; then \ echo "#include " > aicdb.h; \ elif [ -e "/usr/include/db_185.h" ]; then \ --- linux-2.6.9-rc1/drivers/scsi/aic7xxx/Kconfig.aic79xx 2004-08-24 00:02:32.000000000 -0700 +++ 25/drivers/scsi/aic7xxx/Kconfig.aic79xx 2004-09-02 20:51:52.000000000 -0700 @@ -46,7 +46,7 @@ config AIC79XX_RESET_DELAY_MS config AIC79XX_BUILD_FIRMWARE bool "Build Adapter Firmware with Kernel Build" - depends on SCSI_AIC79XX + depends on SCSI_AIC79XX && !PREVENT_FIRMWARE_BUILD help This option should only be enabled if you are modifying the firmware source to the aic79xx driver and wish to have the generated firmware --- linux-2.6.9-rc1/drivers/scsi/aic7xxx/Kconfig.aic7xxx 2004-08-24 00:02:25.000000000 -0700 +++ 25/drivers/scsi/aic7xxx/Kconfig.aic7xxx 2004-09-02 20:51:52.000000000 -0700 @@ -61,7 +61,7 @@ config AIC7XXX_PROBE_EISA_VL config AIC7XXX_BUILD_FIRMWARE bool "Build Adapter Firmware with Kernel Build" - depends on SCSI_AIC7XXX + depends on SCSI_AIC7XXX && !PREVENT_FIRMWARE_BUILD help This option should only be enabled if you are modifying the firmware source to the aic7xxx driver and wish to have the generated firmware --- linux-2.6.9-rc1/drivers/scsi/aic7xxx_old.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/drivers/scsi/aic7xxx_old.c 2004-09-02 20:51:52.000000000 -0700 @@ -10489,7 +10489,7 @@ aic7xxx_bus_device_reset(Scsi_Cmnd *cmd) aic_outb(p, lastphase | ATNO, SCSISIGO); unpause_sequencer(p, FALSE); spin_unlock_irq(p->host->host_lock); - scsi_sleep(HZ); + ssleep(1); spin_lock_irq(p->host->host_lock); if(aic_dev->flags & BUS_DEVICE_RESET_PENDING) return FAILED; @@ -10548,7 +10548,7 @@ aic7xxx_bus_device_reset(Scsi_Cmnd *cmd) aic_outb(p, saved_scbptr, SCBPTR); unpause_sequencer(p, FALSE); spin_unlock_irq(p->host->host_lock); - scsi_sleep(HZ/4); + msleep(1000/4); spin_lock_irq(p->host->host_lock); if(aic_dev->flags & BUS_DEVICE_RESET_PENDING) return FAILED; @@ -10786,7 +10786,7 @@ aic7xxx_abort(Scsi_Cmnd *cmd) } unpause_sequencer(p, FALSE); spin_unlock_irq(p->host->host_lock); - scsi_sleep(HZ/4); + msleep(1000/4); spin_lock_irq(p->host->host_lock); if (p->flags & AHC_ABORT_PENDING) { @@ -10887,7 +10887,7 @@ aic7xxx_reset(Scsi_Cmnd *cmd) aic7xxx_run_done_queue(p, TRUE); unpause_sequencer(p, FALSE); spin_unlock_irq(p->host->host_lock); - scsi_sleep(2 * HZ); + ssleep(2); spin_lock_irq(p->host->host_lock); return SUCCESS; } @@ -11139,6 +11139,7 @@ aic7xxx_print_scratch_ram(struct aic7xxx #include "aic7xxx_old/aic7xxx_proc.c" MODULE_LICENSE("Dual BSD/GPL"); +MODULE_VERSION(AIC7XXX_H_VERSION); static Scsi_Host_Template driver_template = { --- linux-2.6.9-rc1/drivers/scsi/arm/cumana_1.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/scsi/arm/cumana_1.c 2004-09-02 20:51:52.000000000 -0700 @@ -321,6 +321,7 @@ static void __devexit cumanascsi1_remove scsi_remove_host(host); free_irq(host->irq, host); + NCR5380_exit(host); release_region(host->io_port, host->n_io_port); scsi_host_put(host); } --- linux-2.6.9-rc1/drivers/scsi/arm/ecoscsi.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/drivers/scsi/arm/ecoscsi.c 2004-09-02 20:51:52.000000000 -0700 @@ -222,6 +222,7 @@ static void __exit ecoscsi_exit(void) if (shpnt->irq != IRQ_NONE) free_irq(shpnt->irq, NULL); + NCR5380_exit(host); if (shpnt->io_port) release_region(shpnt->io_port, shpnt->n_io_port); --- linux-2.6.9-rc1/drivers/scsi/arm/eesox.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/scsi/arm/eesox.c 2004-09-02 20:51:52.000000000 -0700 @@ -231,7 +231,8 @@ static void eesoxscsi_buffer_in(void *bu * Align buffer. */ if (((u32)buf) & 2 && status >= 2) { - *((u16 *)buf)++ = readl(reg_dmadata); + *(u16 *)buf = readl(reg_dmadata); + buf += 2; status -= 2; length -= 2; } @@ -243,8 +244,10 @@ static void eesoxscsi_buffer_in(void *bu l1 |= readl(reg_dmadata) << 16; l2 = readl(reg_dmadata) & mask; l2 |= readl(reg_dmadata) << 16; - *((u32 *)buf)++ = l1; - *((u32 *)buf)++ = l2; + *(u32 *)buf = l1; + buf += 4; + *(u32 *)buf = l2; + buf += 4; length -= 8; continue; } @@ -255,13 +258,15 @@ static void eesoxscsi_buffer_in(void *bu l1 = readl(reg_dmadata) & mask; l1 |= readl(reg_dmadata) << 16; - *((u32 *)buf)++ = l1; + *(u32 *)buf = l1; + buf += 4; length -= 4; continue; } if (status >= 2) { - *((u16 *)buf)++ = readl(reg_dmadata); + *(u16 *)buf = readl(reg_dmadata); + buf += 2; length -= 2; } } while (length); @@ -305,7 +310,8 @@ static void eesoxscsi_buffer_out(void *b * Align buffer. */ if (((u32)buf) & 2 && status >= 2) { - writel(*((u16 *)buf)++ << 16, reg_dmadata); + writel(*(u16 *)buf << 16, reg_dmadata); + buf += 2; status -= 2; length -= 2; } @@ -313,8 +319,10 @@ static void eesoxscsi_buffer_out(void *b if (status >= 8) { unsigned long l1, l2; - l1 = *((u32 *)buf)++; - l2 = *((u32 *)buf)++; + l1 = *(u32 *)buf; + buf += 4; + l2 = *(u32 *)buf; + buf += 4; writel(l1 << 16, reg_dmadata); writel(l1, reg_dmadata); @@ -327,7 +335,8 @@ static void eesoxscsi_buffer_out(void *b if (status >= 4) { unsigned long l1; - l1 = *((u32 *)buf)++; + l1 = *(u32 *)buf; + buf += 4; writel(l1 << 16, reg_dmadata); writel(l1, reg_dmadata); @@ -336,7 +345,8 @@ static void eesoxscsi_buffer_out(void *b } if (status >= 2) { - writel(*((u16 *)buf)++ << 16, reg_dmadata); + writel(*(u16 *)buf << 16, reg_dmadata); + buf += 2; length -= 2; } } while (length); --- linux-2.6.9-rc1/drivers/scsi/arm/fas216.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/drivers/scsi/arm/fas216.c 2004-09-02 20:51:52.000000000 -0700 @@ -2681,7 +2681,7 @@ int fas216_eh_host_reset(Scsi_Cmnd *SCpn * IRQs after the sleep. */ spin_unlock_irq(info->host->host_lock); - scsi_sleep(50 * HZ/100); + msleep(50 * 1000/100); spin_lock_irq(info->host->host_lock); /* @@ -2920,7 +2920,7 @@ int fas216_add(struct Scsi_Host *host, s * scsi standard says wait 250ms */ spin_unlock_irq(info->host->host_lock); - scsi_sleep(100*HZ/100); + msleep(100*1000/100); spin_lock_irq(info->host->host_lock); fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); --- linux-2.6.9-rc1/drivers/scsi/arm/oak.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/drivers/scsi/arm/oak.c 2004-09-02 20:51:52.000000000 -0700 @@ -179,6 +179,7 @@ static void __devexit oakscsi_remove(str ecard_set_drvdata(ec, NULL); scsi_remove_host(host); + NCR5380_exit(host); release_region(host->io_port, host->n_io_port); scsi_host_put(host); } --- linux-2.6.9-rc1/drivers/scsi/ata_piix.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/scsi/ata_piix.c 2004-09-02 20:51:52.000000000 -0700 @@ -389,7 +389,7 @@ static void piix_sata_phy_reset(struct a static void piix_set_piomode (struct ata_port *ap, struct ata_device *adev) { - unsigned int pio = adev->pio_mode; + unsigned int pio = adev->pio_mode - XFER_PIO_0; struct pci_dev *dev = ap->host_set->pdev; unsigned int is_slave = (adev->devno != 0); unsigned int master_port= ap->port_no ? 0x42 : 0x40; --- linux-2.6.9-rc1/drivers/scsi/cpqfcTSinit.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/drivers/scsi/cpqfcTSinit.c 2004-09-02 20:51:52.000000000 -0700 @@ -302,10 +302,16 @@ int cpqfcTS_detect(Scsi_Host_Template *S cpqfc_boards[i].device_id, PciDev))) { + if (pci_enable_device(PciDev)) { + printk(KERN_ERR + "cpqfc: can't enable PCI device at %s\n", pci_name(PciDev)); + goto err_continue; + } + if (pci_set_dma_mask(PciDev, CPQFCTS_DMA_MASK) != 0) { printk(KERN_WARNING "cpqfc: HBA cannot support required DMA mask, skipping.\n"); - continue; + goto err_disable_dev; } // NOTE: (kernel 2.2.12-32) limits allocation to 128k bytes... @@ -314,8 +320,11 @@ int cpqfcTS_detect(Scsi_Host_Template *S HostAdapter = scsi_register( ScsiHostTemplate, sizeof( CPQFCHBA ) ); - if(HostAdapter == NULL) - continue; + if(HostAdapter == NULL) { + printk(KERN_WARNING + "cpqfc: can't register SCSI HBA, skipping.\n"); + goto err_disable_dev; + } DEBUG_PCI( printk(" HBA found!\n")); DEBUG_PCI( printk(" HostAdapter->PciDev->irq = %u\n", PciDev->irq) ); DEBUG_PCI(printk(" PciDev->baseaddress[0]= %lx\n", @@ -367,9 +376,8 @@ int cpqfcTS_detect(Scsi_Host_Template *S DEV_NAME, HostAdapter) ) { - printk(" IRQ %u already used\n", HostAdapter->irq); - scsi_unregister( HostAdapter); - continue; + printk(KERN_WARNING "cpqfc: IRQ %u already used\n", HostAdapter->irq); + goto err_unregister; } // Since we have two 256-byte I/O port ranges (upper @@ -377,22 +385,17 @@ int cpqfcTS_detect(Scsi_Host_Template *S if( !request_region( cpqfcHBAdata->fcChip.Registers.IOBaseU, 0xff, DEV_NAME ) ) { - printk(" cpqfcTS address in use: %x\n", + printk(KERN_WARNING "cpqfc: address in use: %x\n", cpqfcHBAdata->fcChip.Registers.IOBaseU); - free_irq( HostAdapter->irq, HostAdapter); - scsi_unregister( HostAdapter); - continue; + goto err_free_irq; } if( !request_region( cpqfcHBAdata->fcChip.Registers.IOBaseL, 0xff, DEV_NAME ) ) { - printk(" cpqfcTS address in use: %x\n", + printk(KERN_WARNING "cpqfc: address in use: %x\n", cpqfcHBAdata->fcChip.Registers.IOBaseL); - release_region( cpqfcHBAdata->fcChip.Registers.IOBaseU, 0xff ); - free_irq( HostAdapter->irq, HostAdapter); - scsi_unregister( HostAdapter); - continue; + goto err_release_region_U; } // OK, we have grabbed everything we need now. @@ -424,7 +427,7 @@ int cpqfcTS_detect(Scsi_Host_Template *S // now initialize our hardware... if (cpqfcHBAdata->fcChip.InitializeTachyon( cpqfcHBAdata, 1,1)) { printk(KERN_WARNING "cpqfc: initialization of HBA hardware failed.\n"); - // FIXME: might want to do something better than nothing here. + goto err_release_region_L; } cpqfcHBAdata->fcStatsTime = jiffies; // (for FC Statistics delta) @@ -455,6 +458,21 @@ int cpqfcTS_detect(Scsi_Host_Template *S spin_lock_irq(HostAdapter->host_lock); NumberOfAdapters++; spin_unlock_irq(HostAdapter->host_lock); + + continue; + +err_release_region_L: + release_region( cpqfcHBAdata->fcChip.Registers.IOBaseL, 0xff ); +err_release_region_U: + release_region( cpqfcHBAdata->fcChip.Registers.IOBaseU, 0xff ); +err_free_irq: + free_irq( HostAdapter->irq, HostAdapter); +err_unregister: + scsi_unregister( HostAdapter); +err_disable_dev: + pci_disable_device( PciDev ); +err_continue: + continue; } // end of while() } @@ -811,6 +829,7 @@ int cpqfcTS_release(struct Scsi_Host *Ho cpqfcHBAdata->fcChip.Registers.ReMapMemBase) vfree( cpqfcHBAdata->fcChip.Registers.ReMapMemBase); */ + pci_disable_device( cpqfcHBAdata->PciDev); LEAVE("cpqfcTS_release"); return 0; --- linux-2.6.9-rc1/drivers/scsi/dc390.h 2004-08-24 00:02:23.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,32 +0,0 @@ -/*********************************************************************** - * FILE NAME : DC390.H * - * BY : C.L. Huang * - * Description: Device Driver for Tekram DC-390(T) PCI SCSI * - * Bus Master Host Adapter * - ***********************************************************************/ -/* $Id: dc390.h,v 2.43.2.22 2000/12/20 00:39:36 garloff Exp $ */ - -/* - * DC390/AMD 53C974 driver, header file - */ - -#ifndef DC390_H -#define DC390_H - -#include - -#define DC390_BANNER "Tekram DC390/AM53C974" -#define DC390_VERSION "2.1d 2004-05-27" - -/* We don't have eh_abort_handler, eh_device_reset_handler, - * eh_bus_reset_handler, eh_host_reset_handler yet! - * So long: Use old exception handling :-( */ -#define OLD_EH - -#if LINUX_VERSION_CODE < KERNEL_VERSION (2,1,70) || defined (OLD_EH) -# define NEW_EH -#else -# define NEW_EH use_new_eh_code: 1, -# define USE_NEW_EH -#endif -#endif /* DC390_H */ --- linux-2.6.9-rc1/drivers/scsi/dc395x.c 2004-08-24 00:01:55.000000000 -0700 +++ 25/drivers/scsi/dc395x.c 2004-09-02 20:51:52.000000000 -0700 @@ -376,6 +376,8 @@ static void disconnect(struct AdapterCtl static void reselect(struct AdapterCtlBlk *acb); static u8 start_scsi(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb, struct ScsiReqBlk *srb); +static inline void enable_msgout_abort(struct AdapterCtlBlk *acb, + struct ScsiReqBlk *srb); static void build_srb(struct scsi_cmnd *cmd, struct DeviceCtlBlk *dcb, struct ScsiReqBlk *srb); static void doing_srb_done(struct AdapterCtlBlk *acb, u8 did_code, @@ -384,13 +386,11 @@ static void scsi_reset_detect(struct Ada static void pci_unmap_srb(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb); static void pci_unmap_srb_sense(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb); -static inline void enable_msgout_abort(struct AdapterCtlBlk *acb, - struct ScsiReqBlk *srb); static void srb_done(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb, struct ScsiReqBlk *srb); static void request_sense(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb, struct ScsiReqBlk *srb); -static inline void set_xfer_rate(struct AdapterCtlBlk *acb, +static void set_xfer_rate(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb); static void waiting_timeout(unsigned long ptr); @@ -1676,6 +1676,23 @@ static u8 start_scsi(struct AdapterCtlBl } +#define DC395x_ENABLE_MSGOUT \ + DC395x_write16 (acb, TRM_S1040_SCSI_CONTROL, DO_SETATN); \ + srb->state |= SRB_MSGOUT + + +/* abort command */ +static inline void enable_msgout_abort(struct AdapterCtlBlk *acb, + struct ScsiReqBlk *srb) +{ + srb->msgout_buf[0] = ABORT; + srb->msg_count = 1; + DC395x_ENABLE_MSGOUT; + srb->state &= ~SRB_MSGIN; + srb->state |= SRB_MSGOUT; +} + + /** * dc395x_handle_interrupt - Handle an interrupt that has been confirmed to * have been triggered for this card. @@ -2583,11 +2600,6 @@ static inline u8 msgin_completed(u8 * ms return 1; } -#define DC395x_ENABLE_MSGOUT \ - DC395x_write16 (acb, TRM_S1040_SCSI_CONTROL, DO_SETATN); \ - srb->state |= SRB_MSGOUT - - /* reject_msg */ static inline void msgin_reject(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb) @@ -2603,18 +2615,6 @@ static inline void msgin_reject(struct A } -/* abort command */ -static inline void enable_msgout_abort(struct AdapterCtlBlk *acb, - struct ScsiReqBlk *srb) -{ - srb->msgout_buf[0] = ABORT; - srb->msg_count = 1; - DC395x_ENABLE_MSGOUT; - srb->state &= ~SRB_MSGIN; - srb->state |= SRB_MSGOUT; -} - - static struct ScsiReqBlk *msgin_qtag(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb, u8 tag) { --- linux-2.6.9-rc1/drivers/scsi/dec_esp.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/drivers/scsi/dec_esp.c 2004-09-03 00:57:09.779552544 -0700 @@ -120,7 +120,7 @@ static int dec_esp_release(struct Scsi_H } static Scsi_Host_Template driver_template = { - .proc_name = "esp", + .proc_name = "dec_esp", .proc_info = &esp_proc_info, .name = "NCR53C94", .detect = dec_esp_detect, --- linux-2.6.9-rc1/drivers/scsi/dmx3191d.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/scsi/dmx3191d.c 2004-09-02 20:51:52.000000000 -0700 @@ -106,9 +106,10 @@ static const char * dmx3191d_info(struct static int dmx3191d_release_resources(struct Scsi_Host *instance) { - release_region(instance->io_port, DMX3191D_REGION); if(instance->irq!=SCSI_IRQ_NONE) free_irq(instance->irq, instance); + NCR5380_exit(instance); + release_region(instance->io_port, DMX3191D_REGION); return 0; } --- linux-2.6.9-rc1/drivers/scsi/dtc.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/drivers/scsi/dtc.c 2004-09-02 20:51:52.000000000 -0700 @@ -451,6 +451,7 @@ static int dtc_release(struct Scsi_Host { if (shost->irq) free_irq(shost->irq, NULL); + NCR5380_exit(shost); if (shost->io_port && shost->n_io_port) release_region(shost->io_port, shost->n_io_port); scsi_unregister(shost); --- linux-2.6.9-rc1/drivers/scsi/eata.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/scsi/eata.c 2004-09-03 00:56:33.075132464 -0700 @@ -1005,7 +1005,7 @@ static struct pci_dev *get_pci_dev(unsig unsigned int addr; struct pci_dev *dev = NULL; - while((dev = pci_find_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) { + while((dev = pci_get_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) { addr = pci_resource_start (dev, 0); #if defined(DEBUG_PCI_DETECT) @@ -1013,6 +1013,11 @@ static struct pci_dev *get_pci_dev(unsig driver_name, dev->bus->number, dev->devfn, addr); #endif + /* we are in so much trouble for a pci hotplug system with this driver + * anyway, so doing this at least lets people unload the driver and not + * cause memory problems, but in general this is a bad thing to do (this + * driver needs to be converted to the proper PCI api someday... */ + pci_dev_put(dev); if (addr + PCI_BASE_ADDRESS_0 == port_base) return dev; } @@ -1027,7 +1032,7 @@ static void enable_pci_ports(void) { struct pci_dev *dev = NULL; - while((dev = pci_find_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) { + while((dev = pci_get_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) { #if defined(DEBUG_PCI_DETECT) printk("%s: enable_pci_ports, bus %d, devfn 0x%x.\n", @@ -1454,7 +1459,7 @@ static void add_pci_ports(void) { for (k = 0; k < MAX_PCI; k++) { - if (!(dev = pci_find_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) break; + if (!(dev = pci_get_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) break; if (pci_enable_device (dev)) { @@ -1478,6 +1483,7 @@ static void add_pci_ports(void) { addr + PCI_BASE_ADDRESS_0; } + pci_dev_put(dev); #endif /* end CONFIG_PCI */ return; --- linux-2.6.9-rc1/drivers/scsi/eata_pio.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/drivers/scsi/eata_pio.c 2004-09-02 20:51:52.000000000 -0700 @@ -59,6 +59,8 @@ #include #include #include +#include + #include #include @@ -511,8 +513,7 @@ static int eata_pio_host_reset(struct sc HD(cmd)->state = RESET; spin_unlock_irq(host->host_lock); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(3 * HZ); + msleep(3000); spin_lock_irq(host->host_lock); DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: interrupts disabled, " "loops %d.\n", limit)); --- linux-2.6.9-rc1/drivers/scsi/fd_mcs.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/drivers/scsi/fd_mcs.c 2004-09-02 20:51:52.000000000 -0700 @@ -78,6 +78,7 @@ **************************************************************************/ #include +#include #include #include #include @@ -288,7 +289,7 @@ static irqreturn_t fd_mcs_intr(int irq, static unsigned long addresses[] = { 0xc8000, 0xca000, 0xce000, 0xde000 }; static unsigned short ports[] = { 0x140, 0x150, 0x160, 0x170 }; -static unsigned short ints[] = { 3, 5, 10, 11, 12, 14, 15, 0 }; +static unsigned short interrupts[] = { 3, 5, 10, 11, 12, 14, 15, 0 }; /* host information */ static int found = 0; @@ -297,16 +298,20 @@ static struct Scsi_Host *hosts[FD_MAX_HO static int user_fifo_count = 0; static int user_fifo_size = 0; -static void fd_mcs_setup(char *str, int *ints) +static int __init fd_mcs_setup(char *str) { static int done_setup = 0; + int ints[3]; + get_options(str, 3, ints); if (done_setup++ || ints[0] < 1 || ints[0] > 2 || ints[1] < 1 || ints[1] > 16) { printk("fd_mcs: usage: fd_mcs=FIFO_COUNT, FIFO_SIZE\n"); + return 0; } user_fifo_count = ints[0] >= 1 ? ints[1] : 0; user_fifo_size = ints[0] >= 2 ? ints[2] : 0; + return 1; } __setup("fd_mcs=", fd_mcs_setup); @@ -391,7 +396,7 @@ static int fd_mcs_detect(Scsi_Host_Templ } else { bios = addresses[pos2 >> 6]; port = ports[(pos2 >> 4) & 0x03]; - irq = ints[(pos2 >> 1) & 0x07]; + irq = interrupts[(pos2 >> 1) & 0x07]; } if (irq) { --- linux-2.6.9-rc1/drivers/scsi/fdomain.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/drivers/scsi/fdomain.c 2004-09-02 20:51:52.000000000 -0700 @@ -733,17 +733,20 @@ static int fdomain_isa_detect( int *irq, printk( " %x,", base ); #endif - for (flag = 0, i = 0; !flag && i < PORT_COUNT; i++) { - if (base == ports[i]) - ++flag; + for (i = 0; i < PORT_COUNT; i++) { + if (base == ports[i]) { + if (!request_region(base, 0x10, "fdomain")) + break; + if (!fdomain_is_valid_port(base)) { + release_region(base, 0x10); + break; + } + *irq = fdomain_get_irq( base ); + *iobase = base; + return 1; + } } - if (flag && fdomain_is_valid_port( base )) { - *irq = fdomain_get_irq( base ); - *iobase = base; - return 1; - } - /* This is a bad sign. It usually means that someone patched the BIOS signature list (the signatures variable) to contain a BIOS signature for a board *OTHER THAN* the TMC-1660/TMC-1680. */ @@ -764,7 +767,7 @@ static int fdomain_isa_detect( int *irq, for (i = 0; i < PORT_COUNT; i++) { base = ports[i]; - if (check_region( base, 0x10 )) { + if (!request_region(base, 0x10, "fdomain")) { #if DEBUG_DETECT printk( " (%x inuse),", base ); #endif @@ -773,7 +776,10 @@ static int fdomain_isa_detect( int *irq, #if DEBUG_DETECT printk( " %x,", base ); #endif - if ((flag = fdomain_is_valid_port( base ))) break; + flag = fdomain_is_valid_port(base); + if (flag) + break; + release_region(base, 0x10); } #if DEBUG_DETECT @@ -832,6 +838,9 @@ static int fdomain_pci_bios_detect( int pci_base = pci_resource_start(pdev, 0); pci_irq = pdev->irq; + if (!request_region( pci_base, 0x10, "fdomain" )) + return 0; + /* Now we have the I/O base address and interrupt from the PCI configuration registers. */ @@ -844,8 +853,9 @@ static int fdomain_pci_bios_detect( int " IRQ = %d, I/O base = 0x%x [0x%lx]\n", *irq, *iobase, pci_base ); #endif - if (!fdomain_is_valid_port( *iobase )) { + if (!fdomain_is_valid_port(pci_base)) { printk(KERN_ERR "scsi: PCI card detected, but driver not loaded (invalid port)\n" ); + release_region(pci_base, 0x10); return 0; } @@ -870,10 +880,16 @@ struct Scsi_Host *__fdomain_16x0_detect( printk( "scsi: No BIOS, using port_base = 0x%x, irq = %d\n", port_base, interrupt_level ); #endif + if (!request_region(port_base, 0x10, "fdomain")) { + printk( "scsi: port 0x%x is busy\n", port_base ); + printk( "scsi: Bad LILO/INSMOD parameters?\n" ); + return NULL; + } if (!fdomain_is_valid_port( port_base )) { printk( "scsi: Cannot locate chip at port base 0x%x\n", port_base ); printk( "scsi: Bad LILO/INSMOD parameters?\n" ); + release_region(port_base, 0x10); return NULL; } } else { @@ -915,6 +931,7 @@ struct Scsi_Host *__fdomain_16x0_detect( if (setup_called) { printk(KERN_ERR "scsi: Bad LILO/INSMOD parameters?\n"); } + release_region(port_base, 0x10); return NULL; } @@ -935,8 +952,10 @@ struct Scsi_Host *__fdomain_16x0_detect( get resources. */ shpnt = scsi_register( tpnt, 0 ); - if(shpnt == NULL) + if(shpnt == NULL) { + release_region(port_base, 0x10); return NULL; + } shpnt->irq = interrupt_level; shpnt->io_port = port_base; scsi_set_device(shpnt, &pdev->dev); @@ -946,6 +965,7 @@ struct Scsi_Host *__fdomain_16x0_detect( /* Log IRQ with kernel */ if (!interrupt_level) { printk(KERN_ERR "scsi: Card Detected, but driver not loaded (no IRQ)\n" ); + release_region(port_base, 0x10); return NULL; } else { /* Register the IRQ with the kernel */ @@ -967,13 +987,10 @@ struct Scsi_Host *__fdomain_16x0_detect( printk(KERN_ERR " Send mail to faith@acm.org\n" ); } printk(KERN_ERR "scsi: Detected, but driver not loaded (IRQ)\n" ); + release_region(port_base, 0x10); return NULL; } } - - /* Log I/O ports with kernel */ - request_region( port_base, 0x10, "fdomain" ); - return shpnt; } --- linux-2.6.9-rc1/drivers/scsi/gdth.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/scsi/gdth.c 2004-09-02 20:51:52.000000000 -0700 @@ -618,9 +618,6 @@ static unchar gdth_direction_tab[0x100] }; /* __initfunc, __initdata macros */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -#define __devinitdata -#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) #define GDTH_INITFUNC(type, func) type __init func #include --- linux-2.6.9-rc1/drivers/scsi/g_NCR5380.c 2004-08-24 00:02:22.000000000 -0700 +++ 25/drivers/scsi/g_NCR5380.c 2004-09-02 20:51:52.000000000 -0700 @@ -501,6 +501,10 @@ int generic_NCR5380_release_resources(st { NCR5380_local_declare(); NCR5380_setup(instance); + + if (instance->irq != SCSI_IRQ_NONE) + free_irq(instance->irq, NULL); + NCR5380_exit(instance); #ifndef CONFIG_SCSI_G_NCR5380_MEM release_region(instance->NCR5380_instance_name, instance->n_io_port); @@ -508,8 +512,6 @@ int generic_NCR5380_release_resources(st release_mem_region(instance->NCR5380_instance_name, NCR5380_region_size); #endif - if (instance->irq != SCSI_IRQ_NONE) - free_irq(instance->irq, NULL); return 0; } --- linux-2.6.9-rc1/drivers/scsi/hosts.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/drivers/scsi/hosts.c 2004-09-02 20:51:52.000000000 -0700 @@ -75,9 +75,9 @@ void scsi_host_cancel(struct Scsi_Host * **/ void scsi_remove_host(struct Scsi_Host *shost) { + scsi_forget_host(shost); scsi_host_cancel(shost, 0); scsi_proc_host_rm(shost); - scsi_forget_host(shost); set_bit(SHOST_DEL, &shost->shost_state); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/scsi/ibmvscsi/ibmvscsi.c 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,1393 @@ +/* ------------------------------------------------------------ + * ibmvscsi.c + * (C) Copyright IBM Corporation 1994, 2004 + * Authors: Colin DeVilbiss (devilbis@us.ibm.com) + * Santiago Leon (santil@us.ibm.com) + * Dave Boutcher (sleddog@us.ibm.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + * ------------------------------------------------------------ + * Emulation of a SCSI host adapter for Virtual I/O devices + * + * This driver supports the SCSI adapter implemented by the IBM + * Power5 firmware. That SCSI adapter is not a physical adapter, + * but allows Linux SCSI peripheral drivers to directly + * access devices in another logical partition on the physical system. + * + * The virtual adapter(s) are present in the open firmware device + * tree just like real adapters. + * + * One of the capabilities provided on these systems is the ability + * to DMA between partitions. The architecture states that for VSCSI, + * the server side is allowed to DMA to and from the client. The client + * is never trusted to DMA to or from the server directly. + * + * Messages are sent between partitions on a "Command/Response Queue" + * (CRQ), which is just a buffer of 16 byte entries in the receiver's + * Senders cannot access the buffer directly, but send messages by + * making a hypervisor call and passing in the 16 bytes. The hypervisor + * puts the message in the next 16 byte space in round-robbin fashion, + * turns on the high order bit of the message (the valid bit), and + * generates an interrupt to the receiver (if interrupts are turned on.) + * The receiver just turns off the valid bit when they have copied out + * the message. + * + * The VSCSI client builds a SCSI Remote Protocol (SRP) Information Unit + * (IU) (as defined in the T10 standard available at www.t10.org), gets + * a DMA address for the message, and sends it to the server as the + * payload of a CRQ message. The server DMAs the SRP IU and processes it, + * including doing any additional data transfers. When it is done, it + * DMAs the SRP response back to the same address as the request came from, + * and sends a CRQ message back to inform the client that the request has + * completed. + * + * Note that some of the underlying infrastructure is different between + * machines conforming to the "RS/6000 Platform Architecture" (RPA) and + * the older iSeries hypervisor models. To support both, some low level + * routines have been broken out into rpa_vscsi.c and iseries_vscsi.c. + * The Makefile should pick one, not two, not zero, of these. + * + * TODO: This is currently pretty tied to the IBM i/pSeries hypervisor + * interfaces. It would be really nice to abstract this above an RDMA + * layer. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ibmvscsi.h" + +/* The values below are somewhat arbitrary default values, but + * OS/400 will use 3 busses (disks, CDs, tapes, I think.) + * Note that there are 3 bits of channel value, 6 bits of id, and + * 5 bits of LUN. + */ +static int max_id = 64; +static int max_channel = 3; +static int init_timeout = 5; +static int max_requests = 50; + +#define IBMVSCSI_VERSION "1.5.1" + +MODULE_DESCRIPTION("IBM Virtual SCSI"); +MODULE_AUTHOR("Dave Boutcher"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(IBMVSCSI_VERSION); + +module_param_named(max_id, max_id, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(max_id, "Largest ID value for each channel"); +module_param_named(max_channel, max_channel, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(max_channel, "Largest channel value"); +module_param_named(init_timeout, init_timeout, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(init_timeout, "Initialization timeout in seconds"); +module_param_named(max_requests, max_requests, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(max_requests, "Maximum requests for this adapter"); + +/* ------------------------------------------------------------ + * Routines for the event pool and event structs + */ +/** + * initialize_event_pool: - Allocates and initializes the event pool for a host + * @pool: event_pool to be initialized + * @size: Number of events in pool + * @hostdata: ibmvscsi_host_data who owns the event pool + * + * Returns zero on success. +*/ +static int initialize_event_pool(struct event_pool *pool, + int size, struct ibmvscsi_host_data *hostdata) +{ + int i; + + pool->size = size; + pool->next = 0; + pool->events = kmalloc(pool->size * sizeof(*pool->events), GFP_KERNEL); + if (!pool->events) + return -ENOMEM; + memset(pool->events, 0x00, pool->size * sizeof(*pool->events)); + + pool->iu_storage = + dma_alloc_coherent(hostdata->dev, + pool->size * sizeof(*pool->iu_storage), + &pool->iu_token, 0); + if (!pool->iu_storage) { + kfree(pool->events); + return -ENOMEM; + } + + for (i = 0; i < pool->size; ++i) { + struct srp_event_struct *evt = &pool->events[i]; + memset(&evt->crq, 0x00, sizeof(evt->crq)); + atomic_set(&evt->free, 1); + evt->crq.valid = 0x80; + evt->crq.IU_length = sizeof(*evt->xfer_iu); + evt->crq.IU_data_ptr = pool->iu_token + + sizeof(*evt->xfer_iu) * i; + evt->xfer_iu = pool->iu_storage + i; + evt->hostdata = hostdata; + } + + return 0; +} + +/** + * release_event_pool: - Frees memory of an event pool of a host + * @pool: event_pool to be released + * @hostdata: ibmvscsi_host_data who owns the even pool + * + * Returns zero on success. +*/ +static void release_event_pool(struct event_pool *pool, + struct ibmvscsi_host_data *hostdata) +{ + int i, in_use = 0; + for (i = 0; i < pool->size; ++i) + if (atomic_read(&pool->events[i].free) != 1) + ++in_use; + if (in_use) + printk(KERN_WARNING + "ibmvscsi: releasing event pool with %d " + "events still in use?\n", in_use); + kfree(pool->events); + dma_free_coherent(hostdata->dev, + pool->size * sizeof(*pool->iu_storage), + pool->iu_storage, pool->iu_token); +} + +/** + * valid_event_struct: - Determines if event is valid. + * @pool: event_pool that contains the event + * @evt: srp_event_struct to be checked for validity + * + * Returns zero if event is invalid, one otherwise. +*/ +static int valid_event_struct(struct event_pool *pool, + struct srp_event_struct *evt) +{ + int index = evt - pool->events; + if (index < 0 || index >= pool->size) /* outside of bounds */ + return 0; + if (evt != pool->events + index) /* unaligned */ + return 0; + return 1; +} + +/** + * ibmvscsi_free-event_struct: - Changes status of event to "free" + * @pool: event_pool that contains the event + * @evt: srp_event_struct to be modified + * +*/ +static void free_event_struct(struct event_pool *pool, + struct srp_event_struct *evt) +{ + if (!valid_event_struct(pool, evt)) { + printk(KERN_ERR + "ibmvscsi: Freeing invalid event_struct %p " + "(not in pool %p)\n", evt, pool->events); + return; + } + if (atomic_inc_return(&evt->free) != 1) { + printk(KERN_ERR + "ibmvscsi: Freeing event_struct %p " + "which is not in use!\n", evt); + return; + } +} + +/** + * get_evt_struct: - Gets the next free event in pool + * @pool: event_pool that contains the events to be searched + * + * Returns the next event in "free" state, and NULL if none are free. + * Note that no synchronization is done here, we assume the host_lock + * will syncrhonze things. +*/ +static struct srp_event_struct *get_event_struct(struct event_pool *pool) +{ + int i; + int poolsize = pool->size; + int offset = pool->next; + + for (i = 0; i < poolsize; i++) { + offset = (offset + 1) % poolsize; + if (!atomic_dec_if_positive(&pool->events[offset].free)) { + pool->next = offset; + return &pool->events[offset]; + } + } + + printk(KERN_ERR "ibmvscsi: found no event struct in pool!\n"); + return NULL; +} + +/** + * init_event_struct: Initialize fields in an event struct that are always + * required. + * @evt: The event + * @done: Routine to call when the event is responded to + * @format: SRP or MAD format + * @timeout: timeout value set in the CRQ + */ +static void init_event_struct(struct srp_event_struct *evt_struct, + void (*done) (struct srp_event_struct *), + u8 format, + int timeout) +{ + evt_struct->cmnd = NULL; + evt_struct->cmnd_done = NULL; + evt_struct->crq.format = format; + evt_struct->crq.timeout = timeout; + evt_struct->done = done; +} + +/* ------------------------------------------------------------ + * Routines for receiving SCSI responses from the hosting partition + */ + +/** + * set_srp_direction: Set the fields in the srp related to data + * direction and number of buffers based on the direction in + * the scsi_cmnd and the number of buffers + */ +static void set_srp_direction(struct scsi_cmnd *cmd, + struct srp_cmd *srp_cmd, + int numbuf) +{ + if (numbuf == 0) + return; + + if (numbuf == 1) { + if (cmd->sc_data_direction == DMA_TO_DEVICE) + srp_cmd->data_out_format = SRP_DIRECT_BUFFER; + else + srp_cmd->data_in_format = SRP_DIRECT_BUFFER; + } else { + if (cmd->sc_data_direction == DMA_TO_DEVICE) { + srp_cmd->data_out_format = SRP_INDIRECT_BUFFER; + srp_cmd->data_out_count = numbuf; + } else { + srp_cmd->data_in_format = SRP_INDIRECT_BUFFER; + srp_cmd->data_in_count = numbuf; + } + } +} + +/** + * unmap_cmd_data: - Unmap data pointed in srp_cmd based on the format + * @cmd: srp_cmd whose additional_data member will be unmapped + * @dev: device for which the memory is mapped + * +*/ +static void unmap_cmd_data(struct srp_cmd *cmd, struct device *dev) +{ + int i; + + if ((cmd->data_out_format == SRP_NO_BUFFER) && + (cmd->data_in_format == SRP_NO_BUFFER)) + return; + else if ((cmd->data_out_format == SRP_DIRECT_BUFFER) || + (cmd->data_in_format == SRP_DIRECT_BUFFER)) { + struct memory_descriptor *data = + (struct memory_descriptor *)cmd->additional_data; + dma_unmap_single(dev, data->virtual_address, data->length, + DMA_BIDIRECTIONAL); + } else { + struct indirect_descriptor *indirect = + (struct indirect_descriptor *)cmd->additional_data; + int num_mapped = indirect->head.length / + sizeof(indirect->list[0]); + for (i = 0; i < num_mapped; ++i) { + struct memory_descriptor *data = &indirect->list[i]; + dma_unmap_single(dev, + data->virtual_address, + data->length, DMA_BIDIRECTIONAL); + } + } +} + +/** + * map_sg_data: - Maps dma for a scatterlist and initializes decriptor fields + * @cmd: Scsi_Cmnd with the scatterlist + * @srp_cmd: srp_cmd that contains the memory descriptor + * @dev: device for which to map dma memory + * + * Called by map_data_for_srp_cmd() when building srp cmd from scsi cmd. + * Returns 1 on success. +*/ +static int map_sg_data(struct scsi_cmnd *cmd, + struct srp_cmd *srp_cmd, struct device *dev) +{ + + int i, sg_mapped; + u64 total_length = 0; + struct scatterlist *sg = cmd->request_buffer; + struct memory_descriptor *data = + (struct memory_descriptor *)srp_cmd->additional_data; + struct indirect_descriptor *indirect = + (struct indirect_descriptor *)data; + + sg_mapped = dma_map_sg(dev, sg, cmd->use_sg, DMA_BIDIRECTIONAL); + + if (sg_mapped == 0) + return 0; + + set_srp_direction(cmd, srp_cmd, sg_mapped); + + /* special case; we can use a single direct descriptor */ + if (sg_mapped == 1) { + data->virtual_address = sg_dma_address(&sg[0]); + data->length = sg_dma_len(&sg[0]); + data->memory_handle = 0; + return 1; + } + + if (sg_mapped > MAX_INDIRECT_BUFS) { + printk(KERN_ERR + "ibmvscsi: More than %d mapped sg entries, got %d\n", + MAX_INDIRECT_BUFS, sg_mapped); + return 0; + } + + indirect->head.virtual_address = 0; + indirect->head.length = sg_mapped * sizeof(indirect->list[0]); + indirect->head.memory_handle = 0; + for (i = 0; i < sg_mapped; ++i) { + struct memory_descriptor *descr = &indirect->list[i]; + struct scatterlist *sg_entry = &sg[i]; + descr->virtual_address = sg_dma_address(sg_entry); + descr->length = sg_dma_len(sg_entry); + descr->memory_handle = 0; + total_length += sg_dma_len(sg_entry); + } + indirect->total_length = total_length; + + return 1; +} + +/** + * map_single_data: - Maps memory and initializes memory decriptor fields + * @cmd: struct scsi_cmnd with the memory to be mapped + * @srp_cmd: srp_cmd that contains the memory descriptor + * @dev: device for which to map dma memory + * + * Called by map_data_for_srp_cmd() when building srp cmd from scsi cmd. + * Returns 1 on success. +*/ +static int map_single_data(struct scsi_cmnd *cmd, + struct srp_cmd *srp_cmd, struct device *dev) +{ + struct memory_descriptor *data = + (struct memory_descriptor *)srp_cmd->additional_data; + + data->virtual_address = + dma_map_single(dev, cmd->request_buffer, + cmd->request_bufflen, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(data->virtual_address)) { + printk(KERN_ERR + "ibmvscsi: Unable to map request_buffer for command!\n"); + return 0; + } + data->length = cmd->request_bufflen; + data->memory_handle = 0; + + set_srp_direction(cmd, srp_cmd, 1); + + return 1; +} + +/** + * map_data_for_srp_cmd: - Calls functions to map data for srp cmds + * @cmd: struct scsi_cmnd with the memory to be mapped + * @srp_cmd: srp_cmd that contains the memory descriptor + * @dev: dma device for which to map dma memory + * + * Called by scsi_cmd_to_srp_cmd() when converting scsi cmds to srp cmds + * Returns 1 on success. +*/ +static int map_data_for_srp_cmd(struct scsi_cmnd *cmd, + struct srp_cmd *srp_cmd, struct device *dev) +{ + switch (cmd->sc_data_direction) { + case DMA_FROM_DEVICE: + case DMA_TO_DEVICE: + break; + case DMA_NONE: + return 1; + case DMA_BIDIRECTIONAL: + printk(KERN_ERR + "ibmvscsi: Can't map DMA_BIDIRECTIONAL to read/write\n"); + return 0; + default: + printk(KERN_ERR + "ibmvscsi: Unknown data direction 0x%02x; can't map!\n", + cmd->sc_data_direction); + return 0; + } + + if (!cmd->request_buffer) + return 1; + if (cmd->use_sg) + return map_sg_data(cmd, srp_cmd, dev); + return map_single_data(cmd, srp_cmd, dev); +} + +/* ------------------------------------------------------------ + * Routines for sending and receiving SRPs + */ +/** + * ibmvscsi_send_srp_event: - Transforms event to u64 array and calls send_crq() + * @evt_struct: evt_struct to be sent + * @hostdata: ibmvscsi_host_data of host + * + * Returns the value returned from ibmvscsi_send_crq(). (Zero for success) + * Note that this routine assumes that host_lock is held for synchronization +*/ +static int ibmvscsi_send_srp_event(struct srp_event_struct *evt_struct, + struct ibmvscsi_host_data *hostdata) +{ + struct scsi_cmnd *cmnd = evt_struct->cmnd; + u64 *crq_as_u64 = (u64 *) &evt_struct->crq; + int rc; + + /* If we have exhausted our request limit, just fail this request. + * Note that there are rare cases involving driver generated requests + * (such as task management requests) that the mid layer may think we + * can handle more requests (can_queue) when we actually can't + */ + if ((evt_struct->crq.format == VIOSRP_SRP_FORMAT) && + (atomic_dec_if_positive(&hostdata->request_limit) < 0)) { + /* See if the adapter is disabled */ + if (atomic_read(&hostdata->request_limit) < 0) { + if (cmnd) + cmnd->result = DID_ERROR << 16; + if (evt_struct->cmnd_done) + evt_struct->cmnd_done(cmnd); + unmap_cmd_data(&evt_struct->iu.srp.cmd, + hostdata->dev); + free_event_struct(&hostdata->pool, evt_struct); + return 0; + } else { + printk("ibmvscsi: Warning, request_limit exceeded\n"); + unmap_cmd_data(&evt_struct->iu.srp.cmd, + hostdata->dev); + free_event_struct(&hostdata->pool, evt_struct); + return SCSI_MLQUEUE_HOST_BUSY; + } + } + + /* Copy the IU into the transfer area */ + *evt_struct->xfer_iu = evt_struct->iu; + evt_struct->xfer_iu->srp.generic.tag = (u64)evt_struct; + + /* Add this to the sent list. We need to do this + * before we actually send + * in case it comes back REALLY fast + */ + list_add_tail(&evt_struct->list, &hostdata->sent); + + if ((rc = + ibmvscsi_send_crq(hostdata, crq_as_u64[0], crq_as_u64[1])) != 0) { + list_del(&evt_struct->list); + + cmnd = evt_struct->cmnd; + printk(KERN_ERR "ibmvscsi: failed to send event struct rc %d\n", + rc); + unmap_cmd_data(&evt_struct->iu.srp.cmd, hostdata->dev); + free_event_struct(&hostdata->pool, evt_struct); + if (cmnd) + cmnd->result = DID_ERROR << 16; + if (evt_struct->cmnd_done) + evt_struct->cmnd_done(cmnd); + } + + return 0; +} + +/** + * handle_cmd_rsp: - Handle responses from commands + * @evt_struct: srp_event_struct to be handled + * + * Used as a callback by when sending scsi cmds. + * Gets called by ibmvscsi_handle_crq() +*/ +static void handle_cmd_rsp(struct srp_event_struct *evt_struct) +{ + struct srp_rsp *rsp = &evt_struct->xfer_iu->srp.rsp; + struct scsi_cmnd *cmnd = evt_struct->cmnd; + + if (cmnd) { + cmnd->result = rsp->status; + if (((cmnd->result >> 1) & 0x1f) == CHECK_CONDITION) + memcpy(cmnd->sense_buffer, + rsp->sense_and_response_data, + rsp->sense_data_list_length); + unmap_cmd_data(&evt_struct->iu.srp.cmd, + evt_struct->hostdata->dev); + + if (rsp->doover) + cmnd->resid = rsp->data_out_residual_count; + else if (rsp->diover) + cmnd->resid = rsp->data_in_residual_count; + } + + if (evt_struct->cmnd_done) + evt_struct->cmnd_done(cmnd); +} + +/** + * lun_from_dev: - Returns the lun of the scsi device + * @dev: struct scsi_device + * +*/ +static inline u16 lun_from_dev(struct scsi_device *dev) +{ + return (0x2 << 14) | (dev->id << 8) | (dev->channel << 5) | dev->lun; +} + +/** + * ibmvscsi_queue: - The queuecommand function of the scsi template + * @cmd: struct scsi_cmnd to be executed + * @done: Callback function to be called when cmd is completed +*/ +static int ibmvscsi_queuecommand(struct scsi_cmnd *cmnd, + void (*done) (struct scsi_cmnd *)) +{ + struct srp_cmd *srp_cmd; + struct srp_event_struct *evt_struct; + struct ibmvscsi_host_data *hostdata = + (struct ibmvscsi_host_data *)&cmnd->device->host->hostdata; + u16 lun = lun_from_dev(cmnd->device); + + evt_struct = get_event_struct(&hostdata->pool); + if (!evt_struct) + return SCSI_MLQUEUE_HOST_BUSY; + + init_event_struct(evt_struct, + handle_cmd_rsp, + VIOSRP_SRP_FORMAT, + cmnd->timeout); + + evt_struct->cmnd = cmnd; + evt_struct->cmnd_done = done; + + /* Set up the actual SRP IU */ + srp_cmd = &evt_struct->iu.srp.cmd; + memset(srp_cmd, 0x00, sizeof(*srp_cmd)); + srp_cmd->type = SRP_CMD_TYPE; + memcpy(srp_cmd->cdb, cmnd->cmnd, sizeof(cmnd->cmnd)); + srp_cmd->lun = ((u64) lun) << 48; + + if (!map_data_for_srp_cmd(cmnd, srp_cmd, hostdata->dev)) { + printk(KERN_ERR "ibmvscsi: couldn't convert cmd to srp_cmd\n"); + free_event_struct(&hostdata->pool, evt_struct); + return SCSI_MLQUEUE_HOST_BUSY; + } + + /* Fix up dma address of the buffer itself */ + if ((srp_cmd->data_out_format == SRP_INDIRECT_BUFFER) || + (srp_cmd->data_in_format == SRP_INDIRECT_BUFFER)) { + struct indirect_descriptor *indirect = + (struct indirect_descriptor *)srp_cmd->additional_data; + indirect->head.virtual_address = evt_struct->crq.IU_data_ptr + + offsetof(struct srp_cmd, additional_data) + + offsetof(struct indirect_descriptor, list); + } + + return ibmvscsi_send_srp_event(evt_struct, hostdata); +} + +/* ------------------------------------------------------------ + * Routines for driver initialization + */ +/** + * adapter_info_rsp: - Handle response to MAD adapter info request + * @evt_struct: srp_event_struct with the response + * + * Used as a "done" callback by when sending adapter_info. Gets called + * by ibmvscsi_handle_crq() +*/ +static void adapter_info_rsp(struct srp_event_struct *evt_struct) +{ + struct ibmvscsi_host_data *hostdata = evt_struct->hostdata; + dma_unmap_single(hostdata->dev, + evt_struct->iu.mad.adapter_info.buffer, + evt_struct->iu.mad.adapter_info.common.length, + DMA_BIDIRECTIONAL); + + if (evt_struct->xfer_iu->mad.adapter_info.common.status) { + printk("ibmvscsi: error %d getting adapter info\n", + evt_struct->xfer_iu->mad.adapter_info.common.status); + } else { + printk("ibmvscsi: host srp version: %s, " + "host partition %s (%d), OS %d\n", + hostdata->madapter_info.srp_version, + hostdata->madapter_info.partition_name, + hostdata->madapter_info.partition_number, + hostdata->madapter_info.os_type); + } +} + +/** + * send_mad_adapter_info: - Sends the mad adapter info request + * and stores the result so it can be retrieved with + * sysfs. We COULD consider causing a failure if the + * returned SRP version doesn't match ours. + * @hostdata: ibmvscsi_host_data of host + * + * Returns zero if successful. +*/ +static void send_mad_adapter_info(struct ibmvscsi_host_data *hostdata) +{ + struct viosrp_adapter_info *req; + struct srp_event_struct *evt_struct; + + memset(&hostdata->madapter_info, 0x00, sizeof(hostdata->madapter_info)); + + evt_struct = get_event_struct(&hostdata->pool); + if (!evt_struct) { + printk(KERN_ERR "ibmvscsi: couldn't allocate an event " + "for ADAPTER_INFO_REQ!\n"); + return; + } + + init_event_struct(evt_struct, + adapter_info_rsp, + VIOSRP_MAD_FORMAT, + init_timeout * HZ); + + req = &evt_struct->iu.mad.adapter_info; + memset(req, 0x00, sizeof(*req)); + + req->common.type = VIOSRP_ADAPTER_INFO_TYPE; + req->common.length = sizeof(hostdata->madapter_info); + req->buffer = dma_map_single(hostdata->dev, + &hostdata->madapter_info, + sizeof(hostdata->madapter_info), + DMA_BIDIRECTIONAL); + + if (dma_mapping_error(req->buffer)) { + printk(KERN_ERR + "ibmvscsi: Unable to map request_buffer " + "for adapter_info!\n"); + free_event_struct(&hostdata->pool, evt_struct); + return; + } + + if (ibmvscsi_send_srp_event(evt_struct, hostdata)) + printk(KERN_ERR "ibmvscsi: couldn't send ADAPTER_INFO_REQ!\n"); +}; + +/** + * login_rsp: - Handle response to SRP login request + * @evt_struct: srp_event_struct with the response + * + * Used as a "done" callback by when sending srp_login. Gets called + * by ibmvscsi_handle_crq() +*/ +static void login_rsp(struct srp_event_struct *evt_struct) +{ + struct ibmvscsi_host_data *hostdata = evt_struct->hostdata; + switch (evt_struct->xfer_iu->srp.generic.type) { + case SRP_LOGIN_RSP_TYPE: /* it worked! */ + break; + case SRP_LOGIN_REJ_TYPE: /* refused! */ + printk(KERN_INFO "ibmvscsi: SRP_LOGIN_REQ rejected\n"); + /* Login failed. */ + atomic_set(&hostdata->request_limit, -1); + return; + default: + printk(KERN_ERR + "ibmvscsi: Invalid login response typecode 0x%02x!\n", + evt_struct->xfer_iu->srp.generic.type); + /* Login failed. */ + atomic_set(&hostdata->request_limit, -1); + return; + } + + printk(KERN_INFO "ibmvscsi: SRP_LOGIN succeeded\n"); + + if (evt_struct->xfer_iu->srp.login_rsp.request_limit_delta > + (max_requests - 2)) + evt_struct->xfer_iu->srp.login_rsp.request_limit_delta = + max_requests - 2; + + /* Now we know what the real request-limit is */ + atomic_set(&hostdata->request_limit, + evt_struct->xfer_iu->srp.login_rsp.request_limit_delta); + + hostdata->host->can_queue = + evt_struct->xfer_iu->srp.login_rsp.request_limit_delta - 2; + + if (hostdata->host->can_queue < 1) { + printk(KERN_ERR "ibmvscsi: Invalid request_limit_delta\n"); + return; + } + + send_mad_adapter_info(hostdata); + return; +} + +/** + * send_srp_login: - Sends the srp login + * @hostdata: ibmvscsi_host_data of host + * + * Returns zero if successful. +*/ +static int send_srp_login(struct ibmvscsi_host_data *hostdata) +{ + int rc; + unsigned long flags; + struct srp_login_req *login; + struct srp_event_struct *evt_struct = get_event_struct(&hostdata->pool); + if (!evt_struct) { + printk(KERN_ERR + "ibmvscsi: couldn't allocate an event for login req!\n"); + return FAILED; + } + + init_event_struct(evt_struct, + login_rsp, + VIOSRP_SRP_FORMAT, + init_timeout * HZ); + + login = &evt_struct->iu.srp.login_req; + login->type = SRP_LOGIN_REQ_TYPE; + login->max_requested_initiator_to_target_iulen = sizeof(union srp_iu); + login->required_buffer_formats = 0x0006; + + /* Start out with a request limit of 1, since this is negotiated in + * the login request we are just sending + */ + atomic_set(&hostdata->request_limit, 1); + + spin_lock_irqsave(hostdata->host->host_lock, flags); + rc = ibmvscsi_send_srp_event(evt_struct, hostdata); + spin_unlock_irqrestore(hostdata->host->host_lock, flags); + return rc; +}; + +/** + * sync_completion: Signal that a synchronous command has completed + * Note that after returning from this call, the evt_struct is freed. + * the caller waiting on this completion shouldn't touch the evt_struct + * again. + */ +static void sync_completion(struct srp_event_struct *evt_struct) +{ + complete(&evt_struct->comp); +} + +/** + * ibmvscsi_abort: Abort a command...from scsi host template + * send this over to the server and wait synchronously for the response + */ +static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd) +{ + struct ibmvscsi_host_data *hostdata = + (struct ibmvscsi_host_data *)cmd->device->host->hostdata; + struct srp_tsk_mgmt *tsk_mgmt; + struct srp_event_struct *evt; + struct srp_event_struct *tmp_evt, *found_evt; + u16 lun = lun_from_dev(cmd->device); + + /* First, find this command in our sent list so we can figure + * out the correct tag + */ + found_evt = NULL; + list_for_each_entry(tmp_evt, &hostdata->sent, list) { + if (tmp_evt->cmnd == cmd) { + found_evt = tmp_evt; + break; + } + } + + if (!found_evt) + return FAILED; + + evt = get_event_struct(&hostdata->pool); + if (evt == NULL) { + printk(KERN_ERR "ibmvscsi: failed to allocate abort event\n"); + return FAILED; + } + + init_event_struct(evt, + sync_completion, + VIOSRP_SRP_FORMAT, + init_timeout * HZ); + + tsk_mgmt = &evt->iu.srp.tsk_mgmt; + + /* Set up an abort SRP command */ + memset(tsk_mgmt, 0x00, sizeof(*tsk_mgmt)); + tsk_mgmt->type = SRP_TSK_MGMT_TYPE; + tsk_mgmt->lun = ((u64) lun) << 48; + tsk_mgmt->task_mgmt_flags = 0x01; /* ABORT TASK */ + tsk_mgmt->managed_task_tag = (u64) found_evt; + + printk(KERN_INFO "ibmvscsi: aborting command. lun 0x%lx, tag 0x%lx\n", + tsk_mgmt->lun, tsk_mgmt->managed_task_tag); + + init_completion(&evt->comp); + if (ibmvscsi_send_srp_event(evt, hostdata) != 0) { + printk(KERN_ERR "ibmvscsi: failed to send abort() event\n"); + return FAILED; + } + + spin_unlock_irq(hostdata->host->host_lock); + wait_for_completion(&evt->comp); + spin_lock_irq(hostdata->host->host_lock); + + /* Because we dropped the spinlock above, it's possible + * The event is no longer in our list. Make sure it didn't + * complete while we were aborting + */ + found_evt = NULL; + list_for_each_entry(tmp_evt, &hostdata->sent, list) { + if (tmp_evt->cmnd == cmd) { + found_evt = tmp_evt; + break; + } + } + + printk(KERN_INFO + "ibmvscsi: successfully aborted task tag 0x%lx\n", + tsk_mgmt->managed_task_tag); + + if (found_evt == NULL) + return SUCCESS; + + cmd->result = (DID_ABORT << 16); + list_del(&found_evt->list); + unmap_cmd_data(&found_evt->iu.srp.cmd, found_evt->hostdata->dev); + free_event_struct(&found_evt->hostdata->pool, found_evt); + atomic_inc(&hostdata->request_limit); + return SUCCESS; +} + +/** + * ibmvscsi_eh_device_reset_handler: Reset a single LUN...from scsi host + * template send this over to the server and wait synchronously for the + * response + */ +static int ibmvscsi_eh_device_reset_handler(struct scsi_cmnd *cmd) +{ + struct ibmvscsi_host_data *hostdata = + (struct ibmvscsi_host_data *)cmd->device->host->hostdata; + + struct srp_tsk_mgmt *tsk_mgmt; + struct srp_event_struct *evt; + struct srp_event_struct *tmp_evt, *pos; + u16 lun = lun_from_dev(cmd->device); + + evt = get_event_struct(&hostdata->pool); + if (evt == NULL) { + printk(KERN_ERR "ibmvscsi: failed to allocate reset event\n"); + return FAILED; + } + + init_event_struct(evt, + sync_completion, + VIOSRP_SRP_FORMAT, + init_timeout * HZ); + + tsk_mgmt = &evt->iu.srp.tsk_mgmt; + + /* Set up a lun reset SRP command */ + memset(tsk_mgmt, 0x00, sizeof(*tsk_mgmt)); + tsk_mgmt->type = SRP_TSK_MGMT_TYPE; + tsk_mgmt->lun = ((u64) lun) << 48; + tsk_mgmt->task_mgmt_flags = 0x08; /* LUN RESET */ + + printk(KERN_INFO "ibmvscsi: resetting device. lun 0x%lx\n", + tsk_mgmt->lun); + + init_completion(&evt->comp); + if (ibmvscsi_send_srp_event(evt, hostdata) != 0) { + printk(KERN_ERR "ibmvscsi: failed to send reset event\n"); + return FAILED; + } + + spin_unlock_irq(hostdata->host->host_lock); + wait_for_completion(&evt->comp); + spin_lock_irq(hostdata->host->host_lock); + + /* We need to find all commands for this LUN that have not yet been + * responded to, and fail them with DID_RESET + */ + list_for_each_entry_safe(tmp_evt, pos, &hostdata->sent, list) { + if ((tmp_evt->cmnd) && (tmp_evt->cmnd->device == cmd->device)) { + if (tmp_evt->cmnd) + tmp_evt->cmnd->result = (DID_RESET << 16); + list_del(&tmp_evt->list); + unmap_cmd_data(&tmp_evt->iu.srp.cmd, tmp_evt->hostdata->dev); + free_event_struct(&tmp_evt->hostdata->pool, + tmp_evt); + atomic_inc(&hostdata->request_limit); + if (tmp_evt->cmnd_done) + tmp_evt->cmnd_done(tmp_evt->cmnd); + else if (tmp_evt->done) + tmp_evt->done(tmp_evt); + } + } + return SUCCESS; +} + +/** + * purge_requests: Our virtual adapter just shut down. purge any sent requests + * @hostdata: the adapter + */ +static void purge_requests(struct ibmvscsi_host_data *hostdata) +{ + struct srp_event_struct *tmp_evt, *pos; + unsigned long flags; + + spin_lock_irqsave(hostdata->host->host_lock, flags); + list_for_each_entry_safe(tmp_evt, pos, &hostdata->sent, list) { + list_del(&tmp_evt->list); + if (tmp_evt->cmnd) { + tmp_evt->cmnd->result = (DID_ERROR << 16); + unmap_cmd_data(&tmp_evt->iu.srp.cmd, + tmp_evt->hostdata->dev); + if (tmp_evt->cmnd_done) + tmp_evt->cmnd_done(tmp_evt->cmnd); + } else { + if (tmp_evt->done) { + tmp_evt->done(tmp_evt); + } + } + free_event_struct(&tmp_evt->hostdata->pool, tmp_evt); + } + spin_unlock_irqrestore(hostdata->host->host_lock, flags); +} + +/** + * ibmvscsi_handle_crq: - Handles and frees received events in the CRQ + * @crq: Command/Response queue + * @hostdata: ibmvscsi_host_data of host + * +*/ +void ibmvscsi_handle_crq(struct viosrp_crq *crq, + struct ibmvscsi_host_data *hostdata) +{ + unsigned long flags; + struct srp_event_struct *evt_struct = + (struct srp_event_struct *)crq->IU_data_ptr; + switch (crq->valid) { + case 0xC0: /* initialization */ + switch (crq->format) { + case 0x01: /* Initialization message */ + printk(KERN_INFO "ibmvscsi: partner initialized\n"); + /* Send back a response */ + if (ibmvscsi_send_crq(hostdata, + 0xC002000000000000LL, 0) == 0) { + /* Now login */ + send_srp_login(hostdata); + } else { + printk(KERN_ERR + "ibmvscsi: Unable to send init rsp\n"); + } + + break; + case 0x02: /* Initialization response */ + printk(KERN_INFO + "ibmvscsi: partner initialization complete\n"); + + /* Now login */ + send_srp_login(hostdata); + break; + default: + printk(KERN_ERR "ibmvscsi: unknown crq message type\n"); + } + return; + case 0xFF: /* Hypervisor telling us the connection is closed */ + printk(KERN_INFO "ibmvscsi: Virtual adapter failed!\n"); + + atomic_set(&hostdata->request_limit, -1); + purge_requests(hostdata); + ibmvscsi_reset_crq_queue(&hostdata->queue, hostdata); + return; + case 0x80: /* real payload */ + break; + default: + printk(KERN_ERR + "ibmvscsi: got an invalid message type 0x%02x\n", + crq->valid); + return; + } + + /* The only kind of payload CRQs we should get are responses to + * things we send. Make sure this response is to something we + * actually sent + */ + if (!valid_event_struct(&hostdata->pool, evt_struct)) { + printk(KERN_ERR + "ibmvscsi: returned correlation_token 0x%p is invalid!\n", + (void *)crq->IU_data_ptr); + return; + } + + if (crq->format == VIOSRP_SRP_FORMAT) + atomic_add(evt_struct->xfer_iu->srp.rsp.request_limit_delta, + &hostdata->request_limit); + + if (evt_struct->done) + evt_struct->done(evt_struct); + else + printk(KERN_ERR + "ibmvscsi: returned done() is NULL; not running it!\n"); + + /* + * Lock the host_lock before messing with these structures, since we + * are running in a task context + */ + spin_lock_irqsave(evt_struct->hostdata->host->host_lock, flags); + list_del(&evt_struct->list); + free_event_struct(&evt_struct->hostdata->pool, evt_struct); + spin_unlock_irqrestore(evt_struct->hostdata->host->host_lock, flags); +} + +/** + * ibmvscsi_get_host_config: Send the command to the server to get host + * configuration data. The data is opaque to us. + */ +static int ibmvscsi_do_host_config(struct ibmvscsi_host_data *hostdata, + unsigned char *buffer, int length) +{ + struct viosrp_host_config *host_config; + struct srp_event_struct *evt_struct; + int rc; + + evt_struct = get_event_struct(&hostdata->pool); + if (!evt_struct) { + printk(KERN_ERR + "ibmvscsi: could't allocate event for HOST_CONFIG!\n"); + return -1; + } + + init_event_struct(evt_struct, + sync_completion, + VIOSRP_MAD_FORMAT, + init_timeout * HZ); + + host_config = &evt_struct->iu.mad.host_config; + + /* Set up a lun reset SRP command */ + memset(host_config, 0x00, sizeof(*host_config)); + host_config->common.type = VIOSRP_HOST_CONFIG_TYPE; + host_config->common.length = length; + host_config->buffer = dma_map_single(hostdata->dev, buffer, length, + DMA_BIDIRECTIONAL); + + if (dma_mapping_error(host_config->buffer)) { + printk(KERN_ERR + "ibmvscsi: dma_mapping error " "getting host config\n"); + free_event_struct(&hostdata->pool, evt_struct); + return -1; + } + + init_completion(&evt_struct->comp); + rc = ibmvscsi_send_srp_event(evt_struct, hostdata); + if (rc == 0) { + wait_for_completion(&evt_struct->comp); + dma_unmap_single(hostdata->dev, host_config->buffer, + length, DMA_BIDIRECTIONAL); + } + + return rc; +} + +/* ------------------------------------------------------------ + * sysfs attributes + */ +static ssize_t show_host_srp_version(struct class_device *class_dev, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(class_dev); + struct ibmvscsi_host_data *hostdata = + (struct ibmvscsi_host_data *)shost->hostdata; + int len; + + len = snprintf(buf, PAGE_SIZE, "%s\n", + hostdata->madapter_info.srp_version); + return len; +} + +static struct class_device_attribute ibmvscsi_host_srp_version = { + .attr = { + .name = "srp_version", + .mode = S_IRUGO, + }, + .show = show_host_srp_version, +}; + +static ssize_t show_host_partition_name(struct class_device *class_dev, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(class_dev); + struct ibmvscsi_host_data *hostdata = + (struct ibmvscsi_host_data *)shost->hostdata; + int len; + + len = snprintf(buf, PAGE_SIZE, "%s\n", + hostdata->madapter_info.partition_name); + return len; +} + +static struct class_device_attribute ibmvscsi_host_partition_name = { + .attr = { + .name = "partition_name", + .mode = S_IRUGO, + }, + .show = show_host_partition_name, +}; + +static ssize_t show_host_partition_number(struct class_device *class_dev, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(class_dev); + struct ibmvscsi_host_data *hostdata = + (struct ibmvscsi_host_data *)shost->hostdata; + int len; + + len = snprintf(buf, PAGE_SIZE, "%d\n", + hostdata->madapter_info.partition_number); + return len; +} + +static struct class_device_attribute ibmvscsi_host_partition_number = { + .attr = { + .name = "partition_number", + .mode = S_IRUGO, + }, + .show = show_host_partition_number, +}; + +static ssize_t show_host_mad_version(struct class_device *class_dev, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(class_dev); + struct ibmvscsi_host_data *hostdata = + (struct ibmvscsi_host_data *)shost->hostdata; + int len; + + len = snprintf(buf, PAGE_SIZE, "%d\n", + hostdata->madapter_info.mad_version); + return len; +} + +static struct class_device_attribute ibmvscsi_host_mad_version = { + .attr = { + .name = "mad_version", + .mode = S_IRUGO, + }, + .show = show_host_mad_version, +}; + +static ssize_t show_host_os_type(struct class_device *class_dev, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(class_dev); + struct ibmvscsi_host_data *hostdata = + (struct ibmvscsi_host_data *)shost->hostdata; + int len; + + len = snprintf(buf, PAGE_SIZE, "%d\n", hostdata->madapter_info.os_type); + return len; +} + +static struct class_device_attribute ibmvscsi_host_os_type = { + .attr = { + .name = "os_type", + .mode = S_IRUGO, + }, + .show = show_host_os_type, +}; + +static ssize_t show_host_config(struct class_device *class_dev, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(class_dev); + struct ibmvscsi_host_data *hostdata = + (struct ibmvscsi_host_data *)shost->hostdata; + + /* returns null-terminated host config data */ + if (ibmvscsi_do_host_config(hostdata, buf, PAGE_SIZE) == 0) + return strlen(buf); + else + return 0; +} + +static struct class_device_attribute ibmvscsi_host_config = { + .attr = { + .name = "config", + .mode = S_IRUGO, + }, + .show = show_host_config, +}; + +static struct class_device_attribute *ibmvscsi_attrs[] = { + &ibmvscsi_host_srp_version, + &ibmvscsi_host_partition_name, + &ibmvscsi_host_partition_number, + &ibmvscsi_host_mad_version, + &ibmvscsi_host_os_type, + &ibmvscsi_host_config, + NULL +}; + +/* ------------------------------------------------------------ + * SCSI driver registration + */ +static struct scsi_host_template driver_template = { + .module = THIS_MODULE, + .name = "IBM POWER Virtual SCSI Adapter " IBMVSCSI_VERSION, + .proc_name = "ibmvscsi", + .queuecommand = ibmvscsi_queuecommand, + .eh_abort_handler = ibmvscsi_eh_abort_handler, + .eh_device_reset_handler = ibmvscsi_eh_device_reset_handler, + .cmd_per_lun = 16, + .can_queue = 1, /* Updated after SRP_LOGIN */ + .this_id = -1, + .sg_tablesize = MAX_INDIRECT_BUFS, + .use_clustering = ENABLE_CLUSTERING, + .shost_attrs = ibmvscsi_attrs, +}; + +/** + * Called by bus code for each adapter + */ +static int ibmvscsi_probe(struct vio_dev *vdev, const struct vio_device_id *id) +{ + struct ibmvscsi_host_data *hostdata; + struct Scsi_Host *host; + struct device *dev = &vdev->dev; + unsigned long wait_switch = 0; + + vdev->dev.driver_data = NULL; + + host = scsi_host_alloc(&driver_template, sizeof(*hostdata)); + if (!host) { + printk(KERN_ERR "ibmvscsi: couldn't allocate host data\n"); + goto scsi_host_alloc_failed; + } + + hostdata = (struct ibmvscsi_host_data *)host->hostdata; + memset(hostdata, 0x00, sizeof(*hostdata)); + INIT_LIST_HEAD(&hostdata->sent); + hostdata->host = host; + hostdata->dev = dev; + atomic_set(&hostdata->request_limit, -1); + + if (ibmvscsi_init_crq_queue(&hostdata->queue, hostdata, + max_requests) != 0) { + printk(KERN_ERR "ibmvscsi: couldn't initialize crq\n"); + goto init_crq_failed; + } + if (initialize_event_pool(&hostdata->pool, max_requests, hostdata) != 0) { + printk(KERN_ERR "ibmvscsi: couldn't initialize event pool\n"); + goto init_pool_failed; + } + + host->max_lun = 8; + host->max_id = max_id; + host->max_channel = max_channel; + + if (scsi_add_host(hostdata->host, hostdata->dev)) + goto add_host_failed; + + /* Try to send an initialization message. Note that this is allowed + * to fail if the other end is not acive. In that case we don't + * want to scan + */ + if (ibmvscsi_send_crq(hostdata, 0xC001000000000000LL, 0) == 0) { + /* + * Wait around max init_timeout secs for the adapter to finish + * initializing. When we are done initializing, we will have a + * valid request_limit. We don't want Linux scanning before + * we are ready. + */ + for (wait_switch = jiffies + (init_timeout * HZ); + time_before(jiffies, wait_switch) && + atomic_read(&hostdata->request_limit) < 0;) { + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 100); + } + + /* if we now have a valid request_limit, initiate a scan */ + if (atomic_read(&hostdata->request_limit) > 0) + scsi_scan_host(host); + } + + vdev->dev.driver_data = hostdata; + return 0; + + add_host_failed: + release_event_pool(&hostdata->pool, hostdata); + init_pool_failed: + ibmvscsi_release_crq_queue(&hostdata->queue, hostdata, max_requests); + init_crq_failed: + scsi_host_put(host); + scsi_host_alloc_failed: + return -1; +} + +static int ibmvscsi_remove(struct vio_dev *vdev) +{ + struct ibmvscsi_host_data *hostdata = vdev->dev.driver_data; + release_event_pool(&hostdata->pool, hostdata); + ibmvscsi_release_crq_queue(&hostdata->queue, hostdata, + max_requests); + + scsi_remove_host(hostdata->host); + scsi_host_put(hostdata->host); + + return 0; +} + +/** + * ibmvscsi_device_table: Used by vio.c to match devices in the device tree we + * support. + */ +static struct vio_device_id ibmvscsi_device_table[] __devinitdata = { + {"vscsi", "IBM,v-scsi"}, + {0,} +}; + +MODULE_DEVICE_TABLE(vio, ibmvscsi_device_table); +static struct vio_driver ibmvscsi_driver = { + .name = "ibmvscsi", + .id_table = ibmvscsi_device_table, + .probe = ibmvscsi_probe, + .remove = ibmvscsi_remove +}; + +int __init ibmvscsi_module_init(void) +{ + return vio_register_driver(&ibmvscsi_driver); +} + +void __exit ibmvscsi_module_exit(void) +{ + vio_unregister_driver(&ibmvscsi_driver); +} + +module_init(ibmvscsi_module_init); +module_exit(ibmvscsi_module_exit); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/scsi/ibmvscsi/ibmvscsi.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,108 @@ +/* ------------------------------------------------------------ + * ibmvscsi.h + * (C) Copyright IBM Corporation 1994, 2003 + * Authors: Colin DeVilbiss (devilbis@us.ibm.com) + * Santiago Leon (santil@us.ibm.com) + * Dave Boutcher (sleddog@us.ibm.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + * ------------------------------------------------------------ + * Emulation of a SCSI host adapter for Virtual I/O devices + * + * This driver allows the Linux SCSI peripheral drivers to directly + * access devices in the hosting partition, either on an iSeries + * hypervisor system or a converged hypervisor system. + */ +#ifndef IBMVSCSI_H +#define IBMVSCSI_H +#include +#include +#include +#include +#include "viosrp.h" + +struct scsi_cmnd; +struct Scsi_Host; + +/* Number of indirect bufs...the list of these has to fit in the + * additional data of the srp_cmd struct along with the indirect + * descriptor + */ +#define MAX_INDIRECT_BUFS 10 + +/* ------------------------------------------------------------ + * Data Structures + */ +/* an RPA command/response transport queue */ +struct crq_queue { + struct viosrp_crq *msgs; + int size, cur; + dma_addr_t msg_token; + spinlock_t lock; +}; + +/* a unit of work for the hosting partition */ +struct srp_event_struct { + union viosrp_iu *xfer_iu; + struct scsi_cmnd *cmnd; + struct list_head list; + void (*done) (struct srp_event_struct *); + struct viosrp_crq crq; + struct ibmvscsi_host_data *hostdata; + atomic_t free; + union viosrp_iu iu; + void (*cmnd_done) (struct scsi_cmnd *); + struct completion comp; +}; + +/* a pool of event structs for use */ +struct event_pool { + struct srp_event_struct *events; + u32 size; + int next; + union viosrp_iu *iu_storage; + dma_addr_t iu_token; +}; + +/* all driver data associated with a host adapter */ +struct ibmvscsi_host_data { + atomic_t request_limit; + struct device *dev; + struct event_pool pool; + struct crq_queue queue; + struct tasklet_struct srp_task; + struct list_head sent; + struct Scsi_Host *host; + struct mad_adapter_info_data madapter_info; +}; + +/* routines for managing a command/response queue */ +int ibmvscsi_init_crq_queue(struct crq_queue *queue, + struct ibmvscsi_host_data *hostdata, + int max_requests); +void ibmvscsi_release_crq_queue(struct crq_queue *queue, + struct ibmvscsi_host_data *hostdata, + int max_requests); +void ibmvscsi_reset_crq_queue(struct crq_queue *queue, + struct ibmvscsi_host_data *hostdata); + +void ibmvscsi_handle_crq(struct viosrp_crq *crq, + struct ibmvscsi_host_data *hostdata); +int ibmvscsi_send_crq(struct ibmvscsi_host_data *hostdata, + u64 word1, u64 word2); + +#endif /* IBMVSCSI_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/scsi/ibmvscsi/iseries_vscsi.c 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,144 @@ +/* ------------------------------------------------------------ + * iSeries_vscsi.c + * (C) Copyright IBM Corporation 1994, 2003 + * Authors: Colin DeVilbiss (devilbis@us.ibm.com) + * Santiago Leon (santil@us.ibm.com) + * Dave Boutcher (sleddog@us.ibm.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + * ------------------------------------------------------------ + * iSeries-specific functions of the SCSI host adapter for Virtual I/O devices + * + * This driver allows the Linux SCSI peripheral drivers to directly + * access devices in the hosting partition, either on an iSeries + * hypervisor system or a converged hypervisor system. + */ + +#include +#include +#include +#include +#include +#include +#include "ibmvscsi.h" + +/* global variables */ +static struct ibmvscsi_host_data *single_host_data; + +/* ------------------------------------------------------------ + * Routines for direct interpartition interaction + */ +struct srp_lp_event { + struct HvLpEvent lpevt; /* 0x00-0x17 */ + u32 reserved1; /* 0x18-0x1B; unused */ + u16 version; /* 0x1C-0x1D; unused */ + u16 subtype_rc; /* 0x1E-0x1F; unused */ + struct viosrp_crq crq; /* 0x20-0x3F */ +}; + +/** + * standard interface for handling logical partition events. + */ +static void ibmvscsi_handle_event(struct HvLpEvent *lpevt) +{ + struct srp_lp_event *evt = (struct srp_lp_event *)lpevt; + + if (!evt) { + printk(KERN_ERR "ibmvscsi: received null event\n"); + return; + } + + if (single_host_data == NULL) { + printk(KERN_ERR + "ibmvscsi: received event, no adapter present\n"); + return; + } + + ibmvscsi_handle_crq(&evt->crq, single_host_data); +} + +/* ------------------------------------------------------------ + * Routines for driver initialization + */ +int ibmvscsi_init_crq_queue(struct crq_queue *queue, + struct ibmvscsi_host_data *hostdata, + int max_requests) +{ + int rc; + + single_host_data = hostdata; + rc = viopath_open(viopath_hostLp, viomajorsubtype_scsi, 0); + if (rc < 0) { + printk("viopath_open failed with rc %d in open_event_path\n", + rc); + goto viopath_open_failed; + } + + rc = vio_setHandler(viomajorsubtype_scsi, ibmvscsi_handle_event); + if (rc < 0) { + printk("vio_setHandler failed with rc %d in open_event_path\n", + rc); + goto vio_setHandler_failed; + } + return 0; + + vio_setHandler_failed: + viopath_close(viopath_hostLp, viomajorsubtype_scsi, max_requests); + viopath_open_failed: + return -1; +} + +void ibmvscsi_release_crq_queue(struct crq_queue *queue, + struct ibmvscsi_host_data *hostdata, + int max_requests) +{ + vio_clearHandler(viomajorsubtype_scsi); + viopath_close(viopath_hostLp, viomajorsubtype_scsi, max_requests); +} + +/** + * reset_crq_queue: - resets a crq after a failure + * @queue: crq_queue to initialize and register + * @hostdata: ibmvscsi_host_data of host + * + * no-op for iSeries + */ +void ibmvscsi_reset_crq_queue(struct crq_queue *queue, + struct ibmvscsi_host_data *hostdata) +{ +} + +/** + * ibmvscsi_send_crq: - Send a CRQ + * @hostdata: the adapter + * @word1: the first 64 bits of the data + * @word2: the second 64 bits of the data + */ +int ibmvscsi_send_crq(struct ibmvscsi_host_data *hostdata, u64 word1, u64 word2) +{ + single_host_data = hostdata; + return HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_scsi, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + 0, + VIOVERSION << 16, word1, word2, 0, + 0); +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/scsi/ibmvscsi/Makefile 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,5 @@ +obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsic.o + +ibmvscsic-y += ibmvscsi.o +ibmvscsic-$(CONFIG_PPC_ISERIES) += iseries_vscsi.o +ibmvscsic-$(CONFIG_PPC_PSERIES) += rpa_vscsi.o --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/scsi/ibmvscsi/rpa_vscsi.c 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,260 @@ +/* ------------------------------------------------------------ + * rpa_vscsi.c + * (C) Copyright IBM Corporation 1994, 2003 + * Authors: Colin DeVilbiss (devilbis@us.ibm.com) + * Santiago Leon (santil@us.ibm.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + * ------------------------------------------------------------ + * RPA-specific functions of the SCSI host adapter for Virtual I/O devices + * + * This driver allows the Linux SCSI peripheral drivers to directly + * access devices in the hosting partition, either on an iSeries + * hypervisor system or a converged hypervisor system. + */ + +#include +#include +#include +#include +#include +#include "ibmvscsi.h" + +/* ------------------------------------------------------------ + * Routines for managing the command/response queue + */ +/** + * ibmvscsi_handle_event: - Interrupt handler for crq events + * @irq: number of irq to handle, not used + * @dev_instance: ibmvscsi_host_data of host that received interrupt + * @regs: pt_regs with registers + * + * Disables interrupts and schedules srp_task + * Always returns IRQ_HANDLED + */ +static irqreturn_t ibmvscsi_handle_event(int irq, + void *dev_instance, + struct pt_regs *regs) +{ + struct ibmvscsi_host_data *hostdata = + (struct ibmvscsi_host_data *)dev_instance; + vio_disable_interrupts(to_vio_dev(hostdata->dev)); + tasklet_schedule(&hostdata->srp_task); + return IRQ_HANDLED; +} + +/** + * release_crq_queue: - Deallocates data and unregisters CRQ + * @queue: crq_queue to initialize and register + * @host_data: ibmvscsi_host_data of host + * + * Frees irq, deallocates a page for messages, unmaps dma, and unregisters + * the crq with the hypervisor. + */ +void ibmvscsi_release_crq_queue(struct crq_queue *queue, + struct ibmvscsi_host_data *hostdata, + int max_requests) +{ + long rc; + struct vio_dev *vdev = to_vio_dev(hostdata->dev); + free_irq(vdev->irq, (void *)hostdata); + tasklet_kill(&hostdata->srp_task); + do { + rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); + } while ((rc == H_Busy) || (H_isLongBusy(rc))); + dma_unmap_single(hostdata->dev, + queue->msg_token, + queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); + free_page((unsigned long)queue->msgs); +} + +/** + * crq_queue_next_crq: - Returns the next entry in message queue + * @queue: crq_queue to use + * + * Returns pointer to next entry in queue, or NULL if there are no new + * entried in the CRQ. + */ +static struct viosrp_crq *crq_queue_next_crq(struct crq_queue *queue) +{ + struct viosrp_crq *crq; + unsigned long flags; + + spin_lock_irqsave(&queue->lock, flags); + crq = &queue->msgs[queue->cur]; + if (crq->valid & 0x80) { + if (++queue->cur == queue->size) + queue->cur = 0; + } else + crq = NULL; + spin_unlock_irqrestore(&queue->lock, flags); + + return crq; +} + +/** + * ibmvscsi_send_crq: - Send a CRQ + * @hostdata: the adapter + * @word1: the first 64 bits of the data + * @word2: the second 64 bits of the data + */ +int ibmvscsi_send_crq(struct ibmvscsi_host_data *hostdata, u64 word1, u64 word2) +{ + struct vio_dev *vdev = to_vio_dev(hostdata->dev); + + return plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, word1, word2); +} + +/** + * ibmvscsi_task: - Process srps asynchronously + * @data: ibmvscsi_host_data of host + */ +static void ibmvscsi_task(void *data) +{ + struct ibmvscsi_host_data *hostdata = (struct ibmvscsi_host_data *)data; + struct vio_dev *vdev = to_vio_dev(hostdata->dev); + struct viosrp_crq *crq; + int done = 0; + + while (!done) { + /* Pull all the valid messages off the CRQ */ + while ((crq = crq_queue_next_crq(&hostdata->queue)) != NULL) { + ibmvscsi_handle_crq(crq, hostdata); + crq->valid = 0x00; + } + + vio_enable_interrupts(vdev); + if ((crq = crq_queue_next_crq(&hostdata->queue)) != NULL) { + vio_disable_interrupts(vdev); + ibmvscsi_handle_crq(crq, hostdata); + crq->valid = 0x00; + } else { + done = 1; + } + } +} + +/** + * initialize_crq_queue: - Initializes and registers CRQ with hypervisor + * @queue: crq_queue to initialize and register + * @hostdata: ibmvscsi_host_data of host + * + * Allocates a page for messages, maps it for dma, and registers + * the crq with the hypervisor. + * Returns zero on success. + */ +int ibmvscsi_init_crq_queue(struct crq_queue *queue, + struct ibmvscsi_host_data *hostdata, + int max_requests) +{ + int rc; + struct vio_dev *vdev = to_vio_dev(hostdata->dev); + + queue->msgs = (struct viosrp_crq *)get_zeroed_page(GFP_KERNEL); + + if (!queue->msgs) + goto malloc_failed; + queue->size = PAGE_SIZE / sizeof(*queue->msgs); + + queue->msg_token = dma_map_single(hostdata->dev, queue->msgs, + queue->size * sizeof(*queue->msgs), + DMA_BIDIRECTIONAL); + + if (dma_mapping_error(queue->msg_token)) + goto map_failed; + + rc = plpar_hcall_norets(H_REG_CRQ, + vdev->unit_address, + queue->msg_token, PAGE_SIZE); + if (rc == 2) { + /* Adapter is good, but other end is not ready */ + printk(KERN_WARNING "ibmvscsi: Partner adapter not ready\n"); + } else if (rc != 0) { + printk(KERN_WARNING "ibmvscsi: Error %d opening adapter\n", rc); + goto reg_crq_failed; + } + + if (request_irq(vdev->irq, + ibmvscsi_handle_event, + 0, "ibmvscsi", (void *)hostdata) != 0) { + printk(KERN_ERR "ibmvscsi: couldn't register irq 0x%x\n", + vdev->irq); + goto req_irq_failed; + } + + rc = vio_enable_interrupts(vdev); + if (rc != 0) { + printk(KERN_ERR "ibmvscsi: Error %d enabling interrupts!!!\n", + rc); + goto req_irq_failed; + } + + queue->cur = 0; + queue->lock = SPIN_LOCK_UNLOCKED; + + tasklet_init(&hostdata->srp_task, (void *)ibmvscsi_task, + (unsigned long)hostdata); + + return 0; + + req_irq_failed: + do { + rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); + } while ((rc == H_Busy) || (H_isLongBusy(rc))); + reg_crq_failed: + dma_unmap_single(hostdata->dev, + queue->msg_token, + queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); + map_failed: + free_page((unsigned long)queue->msgs); + malloc_failed: + return -1; +} + +/** + * reset_crq_queue: - resets a crq after a failure + * @queue: crq_queue to initialize and register + * @hostdata: ibmvscsi_host_data of host + * + */ +void ibmvscsi_reset_crq_queue(struct crq_queue *queue, + struct ibmvscsi_host_data *hostdata) +{ + int rc; + struct vio_dev *vdev = to_vio_dev(hostdata->dev); + + /* Close the CRQ */ + do { + rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); + } while ((rc == H_Busy) || (H_isLongBusy(rc))); + + /* Clean out the queue */ + memset(queue->msgs, 0x00, PAGE_SIZE); + queue->cur = 0; + + /* And re-open it again */ + rc = plpar_hcall_norets(H_REG_CRQ, + vdev->unit_address, + queue->msg_token, PAGE_SIZE); + if (rc == 2) { + /* Adapter is good, but other end is not ready */ + printk(KERN_WARNING "ibmvscsi: Partner adapter not ready\n"); + } else if (rc != 0) { + printk(KERN_WARNING + "ibmvscsi: couldn't register crq--rc 0x%x\n", rc); + } +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/scsi/ibmvscsi/srp.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,225 @@ +/*****************************************************************************/ +/* srp.h -- SCSI RDMA Protocol definitions */ +/* */ +/* Written By: Colin Devilbis, IBM Corporation */ +/* */ +/* Copyright (C) 2003 IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/* */ +/* This file contains structures and definitions for the SCSI RDMA Protocol */ +/* (SRP) as defined in the T10 standard available at www.t10.org. This */ +/* file was based on the 16a version of the standard */ +/* */ +/*****************************************************************************/ +#ifndef SRP_H +#define SRP_H + +#define PACKED __attribute__((packed)) + +enum srp_types { + SRP_LOGIN_REQ_TYPE = 0x00, + SRP_LOGIN_RSP_TYPE = 0xC0, + SRP_LOGIN_REJ_TYPE = 0x80, + SRP_I_LOGOUT_TYPE = 0x03, + SRP_T_LOGOUT_TYPE = 0x80, + SRP_TSK_MGMT_TYPE = 0x01, + SRP_CMD_TYPE = 0x02, + SRP_RSP_TYPE = 0xC1, + SRP_CRED_REQ_TYPE = 0x81, + SRP_CRED_RSP_TYPE = 0x41, + SRP_AER_REQ_TYPE = 0x82, + SRP_AER_RSP_TYPE = 0x42 +}; + +enum srp_descriptor_formats { + SRP_NO_BUFFER = 0x00, + SRP_DIRECT_BUFFER = 0x01, + SRP_INDIRECT_BUFFER = 0x02 +}; + +struct memory_descriptor { + u64 virtual_address; + u32 memory_handle; + u32 length; +}; + +struct indirect_descriptor { + struct memory_descriptor head; + u32 total_length; + struct memory_descriptor list[1] PACKED; +}; + +struct srp_generic { + u8 type; + u8 reserved1[7]; + u64 tag; +}; + +struct srp_login_req { + u8 type; + u8 reserved1[7]; + u64 tag; + u32 max_requested_initiator_to_target_iulen; + u32 reserved2; + u16 required_buffer_formats; + u8 reserved3:6; + u8 multi_channel_action:2; + u8 reserved4; + u32 reserved5; + u8 initiator_port_identifier[16]; + u8 target_port_identifier[16]; +}; + +struct srp_login_rsp { + u8 type; + u8 reserved1[3]; + u32 request_limit_delta; + u64 tag; + u32 max_initiator_to_target_iulen; + u32 max_target_to_initiator_iulen; + u16 supported_buffer_formats; + u8 reserved2:6; + u8 multi_channel_result:2; + u8 reserved3; + u8 reserved4[24]; +}; + +struct srp_login_rej { + u8 type; + u8 reserved1[3]; + u32 reason; + u64 tag; + u64 reserved2; + u16 supported_buffer_formats; + u8 reserved3[6]; +}; + +struct srp_i_logout { + u8 type; + u8 reserved1[7]; + u64 tag; +}; + +struct srp_t_logout { + u8 type; + u8 reserved1[3]; + u32 reason; + u64 tag; +}; + +struct srp_tsk_mgmt { + u8 type; + u8 reserved1[7]; + u64 tag; + u32 reserved2; + u64 lun PACKED; + u8 reserved3; + u8 reserved4; + u8 task_mgmt_flags; + u8 reserved5; + u64 managed_task_tag; + u64 reserved6; +}; + +struct srp_cmd { + u8 type; + u32 reserved1 PACKED; + u8 data_out_format:4; + u8 data_in_format:4; + u8 data_out_count; + u8 data_in_count; + u64 tag; + u32 reserved2; + u64 lun PACKED; + u8 reserved3; + u8 reserved4:5; + u8 task_attribute:3; + u8 reserved5; + u8 additional_cdb_len; + u8 cdb[16]; + u8 additional_data[0x100 - 0x30]; +}; + +struct srp_rsp { + u8 type; + u8 reserved1[3]; + u32 request_limit_delta; + u64 tag; + u16 reserved2; + u8 reserved3:2; + u8 diunder:1; + u8 diover:1; + u8 dounder:1; + u8 doover:1; + u8 snsvalid:1; + u8 rspvalid:1; + u8 status; + u32 data_in_residual_count; + u32 data_out_residual_count; + u32 sense_data_list_length; + u32 response_data_list_length; + u8 sense_and_response_data[18]; +}; + +struct srp_cred_req { + u8 type; + u8 reserved1[3]; + u32 request_limit_delta; + u64 tag; +}; + +struct srp_cred_rsp { + u8 type; + u8 reserved1[7]; + u64 tag; +}; + +struct srp_aer_req { + u8 type; + u8 reserved1[3]; + u32 request_limit_delta; + u64 tag; + u32 reserved2; + u64 lun; + u32 sense_data_list_length; + u32 reserved3; + u8 sense_data[20]; +}; + +struct srp_aer_rsp { + u8 type; + u8 reserved1[7]; + u64 tag; +}; + +union srp_iu { + struct srp_generic generic; + struct srp_login_req login_req; + struct srp_login_rsp login_rsp; + struct srp_login_rej login_rej; + struct srp_i_logout i_logout; + struct srp_t_logout t_logout; + struct srp_tsk_mgmt tsk_mgmt; + struct srp_cmd cmd; + struct srp_rsp rsp; + struct srp_cred_req cred_req; + struct srp_cred_rsp cred_rsp; + struct srp_aer_req aer_req; + struct srp_aer_rsp aer_rsp; +}; + +#endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/scsi/ibmvscsi/viosrp.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,126 @@ +/*****************************************************************************/ +/* srp.h -- SCSI RDMA Protocol definitions */ +/* */ +/* Written By: Colin Devilbis, IBM Corporation */ +/* */ +/* Copyright (C) 2003 IBM Corporation */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/* */ +/* This file contains structures and definitions for IBM RPA (RS/6000 */ +/* platform architecture) implementation of the SRP (SCSI RDMA Protocol) */ +/* standard. SRP is used on IBM iSeries and pSeries platforms to send SCSI */ +/* commands between logical partitions. */ +/* */ +/* SRP Information Units (IUs) are sent on a "Command/Response Queue" (CRQ) */ +/* between partitions. The definitions in this file are architected, */ +/* and cannot be changed without breaking compatibility with other versions */ +/* of Linux and other operating systems (AIX, OS/400) that talk this protocol*/ +/* between logical partitions */ +/*****************************************************************************/ +#ifndef VIOSRP_H +#define VIOSRP_H +#include "srp.h" + +enum viosrp_crq_formats { + VIOSRP_SRP_FORMAT = 0x01, + VIOSRP_MAD_FORMAT = 0x02, + VIOSRP_OS400_FORMAT = 0x03, + VIOSRP_AIX_FORMAT = 0x04, + VIOSRP_LINUX_FORMAT = 0x06, + VIOSRP_INLINE_FORMAT = 0x07 +}; + +struct viosrp_crq { + u8 valid; /* used by RPA */ + u8 format; /* SCSI vs out-of-band */ + u8 reserved; + u8 status; /* non-scsi failure? (e.g. DMA failure) */ + u16 timeout; /* in seconds */ + u16 IU_length; /* in bytes */ + u64 IU_data_ptr; /* the TCE for transferring data */ +}; + +/* MADs are Management requests above and beyond the IUs defined in the SRP + * standard. + */ +enum viosrp_mad_types { + VIOSRP_EMPTY_IU_TYPE = 0x01, + VIOSRP_ERROR_LOG_TYPE = 0x02, + VIOSRP_ADAPTER_INFO_TYPE = 0x03, + VIOSRP_HOST_CONFIG_TYPE = 0x04 +}; + +/* + * Common MAD header + */ +struct mad_common { + u32 type; + u16 status; + u16 length; + u64 tag; +}; + +/* + * All SRP (and MAD) requests normally flow from the + * client to the server. There is no way for the server to send + * an asynchronous message back to the client. The Empty IU is used + * to hang out a meaningless request to the server so that it can respond + * asynchrouously with something like a SCSI AER + */ +struct viosrp_empty_iu { + struct mad_common common; + u64 buffer; + u32 port; +}; + +struct viosrp_error_log { + struct mad_common common; + u64 buffer; +}; + +struct viosrp_adapter_info { + struct mad_common common; + u64 buffer; +}; + +struct viosrp_host_config { + struct mad_common common; + u64 buffer; +}; + +union mad_iu { + struct viosrp_empty_iu empty_iu; + struct viosrp_error_log error_log; + struct viosrp_adapter_info adapter_info; + struct viosrp_host_config host_config; +}; + +union viosrp_iu { + union srp_iu srp; + union mad_iu mad; +}; + +struct mad_adapter_info_data { + char srp_version[8]; + char partition_name[96]; + u32 partition_number; + u32 mad_version; + u32 os_type; + u32 port_max_txu[8]; /* per-port maximum transfer */ +}; + +#endif --- linux-2.6.9-rc1/drivers/scsi/imm.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/scsi/imm.c 2004-09-02 20:51:52.000000000 -0700 @@ -758,7 +758,7 @@ static void imm_interrupt(void *data) case DID_OK: break; case DID_NO_CONNECT: - printk("imm: no device at SCSI ID %i\n", cmd->target); + printk("imm: no device at SCSI ID %i\n", cmd->device->id); break; case DID_BUS_BUSY: printk("imm: BUS BUSY - EPP timeout detected\n"); --- linux-2.6.9-rc1/drivers/scsi/ipr.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/drivers/scsi/ipr.c 2004-09-03 00:56:28.963757488 -0700 @@ -1,7 +1,7 @@ /* * ipr.c -- driver for IBM Power Linux RAID adapters * - * Written By: Brian King, IBM Corporation + * Written By: Brian King , IBM Corporation * * Copyright (C) 2003, 2004 IBM Corporation * @@ -93,7 +93,7 @@ static spinlock_t ipr_driver_lock = SPIN /* This table describes the differences between DMA controller chips */ static const struct ipr_chip_cfg_t ipr_chip_cfg[] = { - { /* Gemstone */ + { /* Gemstone and Citrine */ .mailbox = 0x0042C, .cache_line_size = 0x20, { @@ -208,6 +208,8 @@ struct ipr_error_table_t ipr_error_table "Synchronization required"}, {0x024E0000, 0, 0, "No ready, IOA shutdown"}, + {0x025A0000, 0, 0, + "Not ready, IOA has been shutdown"}, {0x02670100, 0, 1, "3020: Storage subsystem configuration error"}, {0x03110B00, 0, 0, @@ -880,11 +882,13 @@ static void ipr_process_ccn(struct ipr_c **/ static void ipr_log_vpd(struct ipr_std_inq_vpids *vpids, u8 *serial_num) { - char buffer[max_t(int, sizeof(struct ipr_std_inq_vpids), - IPR_SERIAL_NUM_LEN) + 1]; + char buffer[IPR_VENDOR_ID_LEN + IPR_PROD_ID_LEN + + IPR_SERIAL_NUM_LEN]; - memcpy(buffer, vpids, sizeof(struct ipr_std_inq_vpids)); - buffer[sizeof(struct ipr_std_inq_vpids)] = '\0'; + memcpy(buffer, vpids->vendor_id, IPR_VENDOR_ID_LEN); + memcpy(buffer + IPR_VENDOR_ID_LEN, vpids->product_id, + IPR_PROD_ID_LEN); + buffer[IPR_VENDOR_ID_LEN + IPR_PROD_ID_LEN] = '\0'; ipr_err("Vendor/Product ID: %s\n", buffer); memcpy(buffer, serial_num, IPR_SERIAL_NUM_LEN); @@ -1766,6 +1770,33 @@ static void ipr_get_ioa_dump(struct ipr_ #endif /** + * ipr_release_dump - Free adapter dump memory + * @kref: kref struct + * + * Return value: + * nothing + **/ +static void ipr_release_dump(struct kref *kref) +{ + struct ipr_dump *dump = container_of(kref,struct ipr_dump,kref); + struct ipr_ioa_cfg *ioa_cfg = dump->ioa_cfg; + unsigned long lock_flags = 0; + int i; + + ENTER; + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + ioa_cfg->dump = NULL; + ioa_cfg->sdt_state = INACTIVE; + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + + for (i = 0; i < dump->ioa_dump.next_page_index; i++) + free_page((unsigned long) dump->ioa_dump.ioa_data[i]); + + kfree(dump); + LEAVE; +} + +/** * ipr_worker_thread - Worker thread * @data: ioa config struct * @@ -1791,13 +1822,14 @@ static void ipr_worker_thread(void *data if (ioa_cfg->sdt_state == GET_DUMP) { dump = ioa_cfg->dump; - if (!dump || !kobject_get(&dump->kobj)) { + if (!dump) { spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); return; } + kref_get(&dump->kref); spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); ipr_get_ioa_dump(ioa_cfg, dump); - kobject_put(&dump->kobj); + kref_put(&dump->kref, ipr_release_dump); spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); if (ioa_cfg->sdt_state == DUMP_OBTAINED) @@ -2008,7 +2040,7 @@ static ssize_t ipr_store_diagnostics(str wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload); /* Wait for a second for any errors to be logged */ - schedule_timeout(HZ); + msleep(1000); } else { spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); return -EIO; @@ -2392,15 +2424,15 @@ static ssize_t ipr_read_dump(struct kobj spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); dump = ioa_cfg->dump; - if (ioa_cfg->sdt_state != DUMP_OBTAINED || !dump || !kobject_get(&dump->kobj)) { + if (ioa_cfg->sdt_state != DUMP_OBTAINED || !dump) { spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); return 0; } - + kref_get(&dump->kref); spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); if (off > dump->driver_dump.hdr.len) { - kobject_put(&dump->kobj); + kref_put(&dump->kref, ipr_release_dump); return 0; } @@ -2450,42 +2482,11 @@ static ssize_t ipr_read_dump(struct kobj count -= len; } - kobject_put(&dump->kobj); + kref_put(&dump->kref, ipr_release_dump); return rc; } /** - * ipr_release_dump - Free adapter dump memory - * @kobj: kobject struct - * - * Return value: - * nothing - **/ -static void ipr_release_dump(struct kobject *kobj) -{ - struct ipr_dump *dump = container_of(kobj,struct ipr_dump,kobj); - struct ipr_ioa_cfg *ioa_cfg = dump->ioa_cfg; - unsigned long lock_flags = 0; - int i; - - ENTER; - spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); - ioa_cfg->dump = NULL; - ioa_cfg->sdt_state = INACTIVE; - spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); - - for (i = 0; i < dump->ioa_dump.next_page_index; i++) - free_page((unsigned long) dump->ioa_dump.ioa_data[i]); - - kfree(dump); - LEAVE; -} - -static struct kobj_type ipr_dump_kobj_type = { - .release = ipr_release_dump, -}; - -/** * ipr_alloc_dump - Prepare for adapter dump * @ioa_cfg: ioa config struct * @@ -2506,8 +2507,7 @@ static int ipr_alloc_dump(struct ipr_ioa } memset(dump, 0, sizeof(struct ipr_dump)); - kobject_init(&dump->kobj); - dump->kobj.ktype = &ipr_dump_kobj_type; + kref_init(&dump->kref); dump->ioa_cfg = ioa_cfg; spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); @@ -2554,7 +2554,7 @@ static int ipr_free_dump(struct ipr_ioa_ ioa_cfg->dump = NULL; spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); - kobject_put(&dump->kobj); + kref_put(&dump->kref, ipr_release_dump); LEAVE; return 0; @@ -2690,8 +2690,6 @@ static ssize_t ipr_store_tcq_enable(stru struct ipr_resource_entry *res; unsigned long lock_flags = 0; int tcq_active = simple_strtoul(buf, NULL, 10); - int qdepth = IPR_MAX_CMD_PER_LUN; - int tagged = 0; ssize_t len = -ENXIO; spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); @@ -2699,13 +2697,13 @@ static ssize_t ipr_store_tcq_enable(stru res = (struct ipr_resource_entry *)sdev->hostdata; if (res) { - res->tcq_active = 0; - qdepth = res->qdepth; - if (ipr_is_gscsi(res) && sdev->tagged_supported) { if (tcq_active) { - tagged = MSG_ORDERED_TAG; res->tcq_active = 1; + scsi_activate_tcq(sdev, res->qdepth); + } else { + res->tcq_active = 0; + scsi_deactivate_tcq(sdev, res->qdepth); } len = strlen(buf); @@ -2715,7 +2713,6 @@ static ssize_t ipr_store_tcq_enable(stru } spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); - scsi_adjust_queue_depth(sdev, tagged, qdepth); return len; } @@ -2785,7 +2782,8 @@ static int ipr_biosparam(struct scsi_dev struct block_device *block_device, sector_t capacity, int *parm) { - int heads, sectors, cylinders; + int heads, sectors; + sector_t cylinders; heads = 128; sectors = 32; @@ -2849,8 +2847,8 @@ static int ipr_slave_configure(struct sc sdev->scsi_level = 4; if (ipr_is_vset_device(res)) sdev->timeout = IPR_VSET_RW_TIMEOUT; - - sdev->allow_restart = 1; + if (IPR_IS_DASD_DEVICE(res->cfgte.std_inq_data)) + sdev->allow_restart = 1; scsi_adjust_queue_depth(sdev, 0, res->qdepth); } spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); @@ -3080,7 +3078,7 @@ static int ipr_cancel_op(struct scsi_cmn struct ipr_ioa_cfg *ioa_cfg; struct ipr_resource_entry *res; struct ipr_cmd_pkt *cmd_pkt; - u32 ioasc, ioarcb_addr; + u32 ioasc; int op_found = 0; ENTER; @@ -3101,21 +3099,15 @@ static int ipr_cancel_op(struct scsi_cmn if (!op_found) return SUCCESS; - ioarcb_addr = be32_to_cpu(ipr_cmd->ioarcb.ioarcb_host_pci_addr); - ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg); ipr_cmd->ioarcb.res_handle = res->cfgte.res_handle; cmd_pkt = &ipr_cmd->ioarcb.cmd_pkt; cmd_pkt->request_type = IPR_RQTYPE_IOACMD; - cmd_pkt->cdb[0] = IPR_ABORT_TASK; - cmd_pkt->cdb[2] = (ioarcb_addr >> 24) & 0xff; - cmd_pkt->cdb[3] = (ioarcb_addr >> 16) & 0xff; - cmd_pkt->cdb[4] = (ioarcb_addr >> 8) & 0xff; - cmd_pkt->cdb[5] = ioarcb_addr & 0xff; + cmd_pkt->cdb[0] = IPR_CANCEL_ALL_REQUESTS; ipr_cmd->u.sdev = scsi_cmd->device; ipr_sdev_err(scsi_cmd->device, "Aborting command: %02X\n", scsi_cmd->cmnd[0]); - ipr_send_blocking_cmd(ipr_cmd, ipr_abort_timeout, IPR_ABORT_TASK_TIMEOUT); + ipr_send_blocking_cmd(ipr_cmd, ipr_abort_timeout, IPR_CANCEL_ALL_TIMEOUT); ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc); /* @@ -3737,7 +3729,7 @@ static void ipr_erp_start(struct ipr_ioa switch (ioasc & IPR_IOASC_IOASC_MASK) { case IPR_IOASC_ABORTED_CMD_TERM_BY_HOST: - scsi_cmd->result |= (DID_ERROR << 16); + scsi_cmd->result |= (DID_IMM_RETRY << 16); break; case IPR_IOASC_IR_RESOURCE_HANDLE: scsi_cmd->result |= (DID_NO_CONNECT << 16); @@ -3873,7 +3865,7 @@ static int ipr_queuecommand(struct scsi_ * We have told the host to stop giving us new requests, but * ERP ops don't count. FIXME */ - if (unlikely(!ioa_cfg->allow_cmds)) + if (unlikely(!ioa_cfg->allow_cmds && !ioa_cfg->ioa_is_dead)) return SCSI_MLQUEUE_HOST_BUSY; /* @@ -5437,13 +5429,15 @@ static void ipr_free_mem(struct ipr_ioa_ **/ static void ipr_free_all_resources(struct ipr_ioa_cfg *ioa_cfg) { + struct pci_dev *pdev = ioa_cfg->pdev; + ENTER; - free_irq(ioa_cfg->pdev->irq, ioa_cfg); + free_irq(pdev->irq, ioa_cfg); iounmap((void *) ioa_cfg->hdw_dma_regs); - release_mem_region(ioa_cfg->hdw_dma_regs_pci, - pci_resource_len(ioa_cfg->pdev, 0)); + pci_release_regions(pdev); ipr_free_mem(ioa_cfg); scsi_host_put(ioa_cfg->host); + pci_disable_device(pdev); LEAVE; } @@ -5458,7 +5452,7 @@ static int __devinit ipr_alloc_cmd_blks( { struct ipr_cmnd *ipr_cmd; struct ipr_ioarcb *ioarcb; - u32 dma_addr; + dma_addr_t dma_addr; int i; ioa_cfg->ipr_cmd_pool = pci_pool_create (IPR_NAME, ioa_cfg->pdev, @@ -5508,14 +5502,15 @@ static int __devinit ipr_alloc_cmd_blks( **/ static int __devinit ipr_alloc_mem(struct ipr_ioa_cfg *ioa_cfg) { - int i; + struct pci_dev *pdev = ioa_cfg->pdev; + int i, rc = -ENOMEM; ENTER; ioa_cfg->res_entries = kmalloc(sizeof(struct ipr_resource_entry) * IPR_MAX_PHYSICAL_DEVS, GFP_KERNEL); if (!ioa_cfg->res_entries) - goto cleanup; + goto out; memset(ioa_cfg->res_entries, 0, sizeof(struct ipr_resource_entry) * IPR_MAX_PHYSICAL_DEVS); @@ -5528,24 +5523,24 @@ static int __devinit ipr_alloc_mem(struc &ioa_cfg->vpd_cbs_dma); if (!ioa_cfg->vpd_cbs) - goto cleanup; + goto out_free_res_entries; if (ipr_alloc_cmd_blks(ioa_cfg)) - goto cleanup; + goto out_free_vpd_cbs; ioa_cfg->host_rrq = pci_alloc_consistent(ioa_cfg->pdev, sizeof(u32) * IPR_NUM_CMD_BLKS, &ioa_cfg->host_rrq_dma); if (!ioa_cfg->host_rrq) - goto cleanup; + goto out_ipr_free_cmd_blocks; ioa_cfg->cfg_table = pci_alloc_consistent(ioa_cfg->pdev, sizeof(struct ipr_config_table), &ioa_cfg->cfg_table_dma); if (!ioa_cfg->cfg_table) - goto cleanup; + goto out_free_host_rrq; for (i = 0; i < IPR_NUM_HCAMS; i++) { ioa_cfg->hostrcb[i] = pci_alloc_consistent(ioa_cfg->pdev, @@ -5553,9 +5548,8 @@ static int __devinit ipr_alloc_mem(struc &ioa_cfg->hostrcb_dma[i]); if (!ioa_cfg->hostrcb[i]) - goto cleanup; + goto out_free_hostrcb_dma; - memset(ioa_cfg->hostrcb[i], 0, sizeof(struct ipr_hostrcb)); ioa_cfg->hostrcb[i]->hostrcb_dma = ioa_cfg->hostrcb_dma[i] + offsetof(struct ipr_hostrcb, hcam); list_add_tail(&ioa_cfg->hostrcb[i]->queue, &ioa_cfg->hostrcb_free_q); @@ -5565,19 +5559,35 @@ static int __devinit ipr_alloc_mem(struc IPR_NUM_TRACE_ENTRIES, GFP_KERNEL); if (!ioa_cfg->trace) - goto cleanup; + goto out_free_hostrcb_dma; memset(ioa_cfg->trace, 0, sizeof(struct ipr_trace_entry) * IPR_NUM_TRACE_ENTRIES); + rc = 0; +out: LEAVE; - return 0; - -cleanup: - ipr_free_mem(ioa_cfg); + return rc; - LEAVE; - return -ENOMEM; +out_free_hostrcb_dma: + while (i-- > 0) { + pci_free_consistent(pdev, sizeof(struct ipr_hostrcb), + ioa_cfg->hostrcb[i], + ioa_cfg->hostrcb_dma[i]); + } + pci_free_consistent(pdev, sizeof(struct ipr_config_table), + ioa_cfg->cfg_table, ioa_cfg->cfg_table_dma); +out_free_host_rrq: + pci_free_consistent(pdev, sizeof(u32) * IPR_NUM_CMD_BLKS, + ioa_cfg->host_rrq, ioa_cfg->host_rrq_dma); +out_ipr_free_cmd_blocks: + ipr_free_cmd_blks(ioa_cfg); +out_free_vpd_cbs: + pci_free_consistent(pdev, sizeof(struct ipr_misc_cbs), + ioa_cfg->vpd_cbs, ioa_cfg->vpd_cbs_dma); +out_free_res_entries: + kfree(ioa_cfg->res_entries); + goto out; } /** @@ -5678,7 +5688,7 @@ static int __devinit ipr_probe_ioa(struc if ((rc = pci_enable_device(pdev))) { dev_err(&pdev->dev, "Cannot enable adapter\n"); - return rc; + goto out; } dev_info(&pdev->dev, "Found IOA with IRQ: %d\n", pdev->irq); @@ -5687,7 +5697,8 @@ static int __devinit ipr_probe_ioa(struc if (!host) { dev_err(&pdev->dev, "call to scsi_host_alloc failed!\n"); - return -ENOMEM; + rc = -ENOMEM; + goto out_disable; } ioa_cfg = (struct ipr_ioa_cfg *)host->hostdata; @@ -5697,12 +5708,11 @@ static int __devinit ipr_probe_ioa(struc ipr_regs_pci = pci_resource_start(pdev, 0); - if (!request_mem_region(ipr_regs_pci, - pci_resource_len(pdev, 0), IPR_NAME)) { + rc = pci_request_regions(pdev, IPR_NAME); + if (rc < 0) { dev_err(&pdev->dev, "Couldn't register memory range of registers\n"); - scsi_host_put(host); - return -ENOMEM; + goto out_scsi_host_put; } ipr_regs = (unsigned long)ioremap(ipr_regs_pci, @@ -5711,9 +5721,8 @@ static int __devinit ipr_probe_ioa(struc if (!ipr_regs) { dev_err(&pdev->dev, "Couldn't map memory range of registers\n"); - release_mem_region(ipr_regs_pci, pci_resource_len(pdev, 0)); - scsi_host_put(host); - return -ENOMEM; + rc = -ENOMEM; + goto out_release_regions; } ioa_cfg->hdw_dma_regs = ipr_regs; @@ -5723,11 +5732,10 @@ static int __devinit ipr_probe_ioa(struc ipr_init_ioa_cfg(ioa_cfg, host, pdev); pci_set_master(pdev); - rc = pci_set_dma_mask(pdev, 0xffffffff); - if (rc != PCIBIOS_SUCCESSFUL) { + rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (rc < 0) { dev_err(&pdev->dev, "Failed to set PCI DMA mask\n"); - rc = -EIO; goto cleanup_nomem; } @@ -5755,8 +5763,12 @@ static int __devinit ipr_probe_ioa(struc if ((rc = ipr_set_pcix_cmd_reg(ioa_cfg))) goto cleanup_nomem; - if ((rc = ipr_alloc_mem(ioa_cfg))) - goto cleanup; + rc = ipr_alloc_mem(ioa_cfg); + if (rc < 0) { + dev_err(&pdev->dev, + "Couldn't allocate enough memory for device driver!\n"); + goto cleanup_nomem; + } ipr_mask_and_clear_interrupts(ioa_cfg, ~IPR_PCII_IOA_TRANS_TO_OPER); rc = request_irq(pdev->irq, ipr_isr, SA_SHIRQ, IPR_NAME, ioa_cfg); @@ -5772,18 +5784,20 @@ static int __devinit ipr_probe_ioa(struc spin_unlock(&ipr_driver_lock); LEAVE; - return 0; +out: + return rc; -cleanup: - dev_err(&pdev->dev, "Couldn't allocate enough memory for device driver!\n"); cleanup_nolog: ipr_free_mem(ioa_cfg); cleanup_nomem: iounmap((void *) ipr_regs); - release_mem_region(ipr_regs_pci, pci_resource_len(pdev, 0)); +out_release_regions: + pci_release_regions(pdev); +out_scsi_host_put: scsi_host_put(host); - - return rc; +out_disable: + pci_disable_device(pdev); + goto out; } /** @@ -5988,9 +6002,15 @@ static struct pci_device_id ipr_pci_tabl { PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE, PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_573D, 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] }, + { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE, + PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_571B, + 0, 0, (kernel_ulong_t)&ipr_chip_cfg[0] }, { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE, PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_2780, 0, 0, (kernel_ulong_t)&ipr_chip_cfg[1] }, + { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE, + PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_570F, + 0, 0, (kernel_ulong_t)&ipr_chip_cfg[1] }, { } }; MODULE_DEVICE_TABLE(pci, ipr_pci_table); @@ -6009,16 +6029,14 @@ static struct pci_driver ipr_driver = { * ipr_init - Module entry point * * Return value: - * 0 on success / non-zero on failure + * 0 on success / negative value on failure **/ static int __init ipr_init(void) { ipr_info("IBM Power RAID SCSI Device Driver version: %s %s\n", IPR_DRIVER_VERSION, IPR_DRIVER_DATE); - pci_register_driver(&ipr_driver); - - return 0; + return pci_module_init(&ipr_driver); } /** --- linux-2.6.9-rc1/drivers/scsi/ipr.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/scsi/ipr.h 2004-09-02 20:51:52.000000000 -0700 @@ -1,7 +1,7 @@ /* * ipr.h -- driver for IBM Power Linux RAID adapters * - * Written By: Brian King, IBM Corporation + * Written By: Brian King , IBM Corporation * * Copyright (C) 2003, 2004 IBM Corporation * @@ -19,6 +19,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * + * Alan Cox - Removed several careless u32/dma_addr_t errors + * that broke 64bit platforms. */ #ifndef _IPR_H @@ -27,6 +29,7 @@ #include #include #include +#include #include #include #ifdef CONFIG_KDB @@ -36,8 +39,8 @@ /* * Literals */ -#define IPR_DRIVER_VERSION "2.0.10" -#define IPR_DRIVER_DATE "(June 7, 2004)" +#define IPR_DRIVER_VERSION "2.0.11" +#define IPR_DRIVER_DATE "(August 3, 2004)" /* * IPR_DBG_TRACE: Setting this to 1 will turn on some general function tracing @@ -72,6 +75,8 @@ #define IPR_SUBS_DEV_ID_5703 0x0278 #define IPR_SUBS_DEV_ID_572E 0x02D3 #define IPR_SUBS_DEV_ID_573D 0x02D4 +#define IPR_SUBS_DEV_ID_570F 0x02BD +#define IPR_SUBS_DEV_ID_571B 0x02BE #define IPR_NAME "ipr" @@ -148,7 +153,6 @@ #define IPR_BUS_RESET 0x10 #define IPR_ID_HOST_RR_Q 0xC4 #define IPR_QUERY_IOA_CONFIG 0xC5 -#define IPR_ABORT_TASK 0xC7 #define IPR_CANCEL_ALL_REQUESTS 0xCE #define IPR_HOST_CONTROLLED_ASYNC 0xCF #define IPR_HCAM_CDB_OP_CODE_CONFIG_CHANGE 0x01 @@ -667,7 +671,7 @@ struct ipr_hcam { struct ipr_hostrcb { struct ipr_hcam hcam; - u32 hostrcb_dma; + dma_addr_t hostrcb_dma; struct list_head queue; }; @@ -850,7 +854,7 @@ struct ipr_ioa_cfg { char cfg_table_start[8]; #define IPR_CFG_TBL_START "cfg" struct ipr_config_table *cfg_table; - u32 cfg_table_dma; + dma_addr_t cfg_table_dma; char resource_table_label[8]; #define IPR_RES_TABLE_LABEL "res_tbl" @@ -861,12 +865,12 @@ struct ipr_ioa_cfg { char ipr_hcam_label[8]; #define IPR_HCAM_LABEL "hcams" struct ipr_hostrcb *hostrcb[IPR_NUM_HCAMS]; - u32 hostrcb_dma[IPR_NUM_HCAMS]; + dma_addr_t hostrcb_dma[IPR_NUM_HCAMS]; struct list_head hostrcb_free_q; struct list_head hostrcb_pending_q; u32 *host_rrq; - u32 host_rrq_dma; + dma_addr_t host_rrq_dma; #define IPR_HRRQ_REQ_RESP_HANDLE_MASK 0xfffffffc #define IPR_HRRQ_RESP_BIT_SET 0x00000002 #define IPR_HRRQ_TOGGLE_BIT 0x00000001 @@ -905,7 +909,7 @@ struct ipr_ioa_cfg { enum ipr_sdt_state sdt_state; struct ipr_misc_cbs *vpd_cbs; - u32 vpd_cbs_dma; + dma_addr_t vpd_cbs_dma; struct pci_pool *ipr_cmd_pool; @@ -1029,7 +1033,7 @@ struct ipr_ioa_dump { }__attribute__((packed, aligned (4))); struct ipr_dump { - struct kobject kobj; + struct kref kref; struct ipr_ioa_cfg *ioa_cfg; struct ipr_driver_dump driver_dump; struct ipr_ioa_dump ioa_dump; --- linux-2.6.9-rc1/drivers/scsi/ips.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/drivers/scsi/ips.c 2004-09-02 20:51:52.000000000 -0700 @@ -474,21 +474,17 @@ static uint32_t ips_statupd_copperhead(i static uint32_t ips_statupd_copperhead_memio(ips_ha_t *); static uint32_t ips_statupd_morpheus(ips_ha_t *); static ips_scb_t *ips_getscb(ips_ha_t *); -static inline void ips_putq_scb_head(ips_scb_queue_t *, ips_scb_t *); -static inline void ips_putq_scb_tail(ips_scb_queue_t *, ips_scb_t *); -static inline void ips_putq_wait_head(ips_wait_queue_t *, Scsi_Cmnd *); -static inline void ips_putq_wait_tail(ips_wait_queue_t *, Scsi_Cmnd *); -static inline void ips_putq_copp_head(ips_copp_queue_t *, +static void ips_putq_scb_head(ips_scb_queue_t *, ips_scb_t *); +static void ips_putq_wait_tail(ips_wait_queue_t *, Scsi_Cmnd *); +static void ips_putq_copp_tail(ips_copp_queue_t *, ips_copp_wait_item_t *); -static inline void ips_putq_copp_tail(ips_copp_queue_t *, - ips_copp_wait_item_t *); -static inline ips_scb_t *ips_removeq_scb_head(ips_scb_queue_t *); -static inline ips_scb_t *ips_removeq_scb(ips_scb_queue_t *, ips_scb_t *); -static inline Scsi_Cmnd *ips_removeq_wait_head(ips_wait_queue_t *); -static inline Scsi_Cmnd *ips_removeq_wait(ips_wait_queue_t *, Scsi_Cmnd *); -static inline ips_copp_wait_item_t *ips_removeq_copp(ips_copp_queue_t *, +static ips_scb_t *ips_removeq_scb_head(ips_scb_queue_t *); +static ips_scb_t *ips_removeq_scb(ips_scb_queue_t *, ips_scb_t *); +static Scsi_Cmnd *ips_removeq_wait_head(ips_wait_queue_t *); +static Scsi_Cmnd *ips_removeq_wait(ips_wait_queue_t *, Scsi_Cmnd *); +static ips_copp_wait_item_t *ips_removeq_copp(ips_copp_queue_t *, ips_copp_wait_item_t *); -static inline ips_copp_wait_item_t *ips_removeq_copp_head(ips_copp_queue_t *); +static ips_copp_wait_item_t *ips_removeq_copp_head(ips_copp_queue_t *); static int ips_is_passthru(Scsi_Cmnd *); static int ips_make_passthru(ips_ha_t *, Scsi_Cmnd *, ips_scb_t *, int); @@ -1885,7 +1881,7 @@ ips_flash_bios(ips_ha_t * ha, ips_passth /* Fill in a single scb sg_list element from an address */ /* return a -1 if a breakup occurred */ /****************************************************************************/ -static inline int +static int ips_fill_scb_sg_single(ips_ha_t * ha, dma_addr_t busaddr, ips_scb_t * scb, int indx, unsigned int e_len) { @@ -2950,7 +2946,7 @@ ips_next(ips_ha_t * ha, int intr) /* ASSUMED to be called from within the HA lock */ /* */ /****************************************************************************/ -static inline void +static void ips_putq_scb_head(ips_scb_queue_t * queue, ips_scb_t * item) { METHOD_TRACE("ips_putq_scb_head", 1); @@ -2969,38 +2965,6 @@ ips_putq_scb_head(ips_scb_queue_t * queu /****************************************************************************/ /* */ -/* Routine Name: ips_putq_scb_tail */ -/* */ -/* Routine Description: */ -/* */ -/* Add an item to the tail of the queue */ -/* */ -/* ASSUMED to be called from within the HA lock */ -/* */ -/****************************************************************************/ -static inline void -ips_putq_scb_tail(ips_scb_queue_t * queue, ips_scb_t * item) -{ - METHOD_TRACE("ips_putq_scb_tail", 1); - - if (!item) - return; - - item->q_next = NULL; - - if (queue->tail) - queue->tail->q_next = item; - - queue->tail = item; - - if (!queue->head) - queue->head = item; - - queue->count++; -} - -/****************************************************************************/ -/* */ /* Routine Name: ips_removeq_scb_head */ /* */ /* Routine Description: */ @@ -3010,7 +2974,7 @@ ips_putq_scb_tail(ips_scb_queue_t * queu /* ASSUMED to be called from within the HA lock */ /* */ /****************************************************************************/ -static inline ips_scb_t * +static ips_scb_t * ips_removeq_scb_head(ips_scb_queue_t * queue) { ips_scb_t *item; @@ -3045,7 +3009,7 @@ ips_removeq_scb_head(ips_scb_queue_t * q /* ASSUMED to be called from within the HA lock */ /* */ /****************************************************************************/ -static inline ips_scb_t * +static ips_scb_t * ips_removeq_scb(ips_scb_queue_t * queue, ips_scb_t * item) { ips_scb_t *p; @@ -3082,34 +3046,6 @@ ips_removeq_scb(ips_scb_queue_t * queue, /****************************************************************************/ /* */ -/* Routine Name: ips_putq_wait_head */ -/* */ -/* Routine Description: */ -/* */ -/* Add an item to the head of the queue */ -/* */ -/* ASSUMED to be called from within the HA lock */ -/* */ -/****************************************************************************/ -static inline void -ips_putq_wait_head(ips_wait_queue_t * queue, Scsi_Cmnd * item) -{ - METHOD_TRACE("ips_putq_wait_head", 1); - - if (!item) - return; - - item->host_scribble = (char *) queue->head; - queue->head = item; - - if (!queue->tail) - queue->tail = item; - - queue->count++; -} - -/****************************************************************************/ -/* */ /* Routine Name: ips_putq_wait_tail */ /* */ /* Routine Description: */ @@ -3119,7 +3055,7 @@ ips_putq_wait_head(ips_wait_queue_t * qu /* ASSUMED to be called from within the HA lock */ /* */ /****************************************************************************/ -static inline void +static void ips_putq_wait_tail(ips_wait_queue_t * queue, Scsi_Cmnd * item) { METHOD_TRACE("ips_putq_wait_tail", 1); @@ -3151,7 +3087,7 @@ ips_putq_wait_tail(ips_wait_queue_t * qu /* ASSUMED to be called from within the HA lock */ /* */ /****************************************************************************/ -static inline Scsi_Cmnd * +static Scsi_Cmnd * ips_removeq_wait_head(ips_wait_queue_t * queue) { Scsi_Cmnd *item; @@ -3186,7 +3122,7 @@ ips_removeq_wait_head(ips_wait_queue_t * /* ASSUMED to be called from within the HA lock */ /* */ /****************************************************************************/ -static inline Scsi_Cmnd * +static Scsi_Cmnd * ips_removeq_wait(ips_wait_queue_t * queue, Scsi_Cmnd * item) { Scsi_Cmnd *p; @@ -3223,34 +3159,6 @@ ips_removeq_wait(ips_wait_queue_t * queu /****************************************************************************/ /* */ -/* Routine Name: ips_putq_copp_head */ -/* */ -/* Routine Description: */ -/* */ -/* Add an item to the head of the queue */ -/* */ -/* ASSUMED to be called from within the HA lock */ -/* */ -/****************************************************************************/ -static inline void -ips_putq_copp_head(ips_copp_queue_t * queue, ips_copp_wait_item_t * item) -{ - METHOD_TRACE("ips_putq_copp_head", 1); - - if (!item) - return; - - item->next = queue->head; - queue->head = item; - - if (!queue->tail) - queue->tail = item; - - queue->count++; -} - -/****************************************************************************/ -/* */ /* Routine Name: ips_putq_copp_tail */ /* */ /* Routine Description: */ @@ -3260,7 +3168,7 @@ ips_putq_copp_head(ips_copp_queue_t * qu /* ASSUMED to be called from within the HA lock */ /* */ /****************************************************************************/ -static inline void +static void ips_putq_copp_tail(ips_copp_queue_t * queue, ips_copp_wait_item_t * item) { METHOD_TRACE("ips_putq_copp_tail", 1); @@ -3292,7 +3200,7 @@ ips_putq_copp_tail(ips_copp_queue_t * qu /* ASSUMED to be called from within the HA lock */ /* */ /****************************************************************************/ -static inline ips_copp_wait_item_t * +static ips_copp_wait_item_t * ips_removeq_copp_head(ips_copp_queue_t * queue) { ips_copp_wait_item_t *item; @@ -3327,7 +3235,7 @@ ips_removeq_copp_head(ips_copp_queue_t * /* ASSUMED to be called from within the HA lock */ /* */ /****************************************************************************/ -static inline ips_copp_wait_item_t * +static ips_copp_wait_item_t * ips_removeq_copp(ips_copp_queue_t * queue, ips_copp_wait_item_t * item) { ips_copp_wait_item_t *p; --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/scsi/iteraid.c 2004-09-03 00:56:53.230068448 -0700 @@ -0,0 +1,5371 @@ +/* + * linux/drivers/scsi/iteraid.c + * + * (C) Copyright 2002-2004 ITE Tech, inc. + * + * Nov 11, 2002 Mark Lu file created. + * + * Aug 2 , 2004 Donald Huang published the ITE driver. + * + * ITE IT8212 RAID controller device driver for Linux. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * Revision 0.0 2002/12/05 15:12:12 root + * Empty function bodies; detect() works. + * + * Revision 0.1 2002/12/17 19:21:37 root + * First "dma thing doesn't work" version. + * + * Revision 0.3 2002/12/23 17:12:09 root + * Rewrite the dma routines. Reference the ide-dma.c. + * + * Revision 0.4 2002/12/26 10:19:29 root + * The dma read/write works, using some ways to prove it. But there is a + * problem about the "unknown partition table". The fdisk is ok, but + * after writing the created partitions, it still shows the "unknown + * partition table" and i can't access the created partitions. + * + * Revision 0.5 2003/01/07 21:49:30 root + * The problem of "unknown partition table" has been solved. + * We must "ENABLE CLUSTERING". There is still a another problem about + * the SCATTER/GATHER. + * + * Revision 0.6 2003/01/10 17:45:32 root + * The SCATTER/GATHER problem has been solved. Now verify the read/write + * function and make sure each RAID configurations are workable. If testing + * is OK, then it will be a version 1.0..... + * + * Revision 1.0 2003/01/16 17:45:32 root + * First release version. + * + * FixME 1: + * In RedHat 7.3, if using SG_ALL, the SCSI will timeout. It looks like + * an command is requested but the interrupt is not been asserted. So + * try to add a watchdog timer to monitor the interrupts. But this kind + * of situration will not happen in Mandrake 9.0 and also when using + * SG_NONE in RedHat 7.3. + * + * FixME 2: + * Module load problem in RedHat 7.3. + * + * Fixed: Compile in the graphic mode (GNOME or KDE) will fix the + * module load problem. + * + * Revision 1.1 2003/02/10 10:32:21 root + * Compile in the graphic mode (GNOME or KDE) will fix the + * module load problem. + * + * Revision 1.2 2003/02/18 14:10:35 root + * Fix the interrupt service routine for share irq problem. + * + * ATAPI support ---> schedule is three weeks. (2003/02/28) + * + * Revision 1.3 2003/02/27 + * First relase ATAPI version. But there will be an error if no disc in the + * CD-ROM. Because the commands like TEST_UNIT_READY and READ_CAPACITY will + * get the error response. This situration in WINDOWS will be then send the + * REQUEST SENSE command to the device but in Linux, it will never get + * REQUEST SENSE command. So can we send by ourself??? + * + * 2003/03/05 root + * + * Note 1: + * According to "The Linux SCSI Generic (sg) HOWTO", the device will respond + * with a single byte value call the 'scsi_status'. GOOD is the scsi status + * indicating everything has gone well. The most common other status is + * CHECK CONDITION. In this latter case, the SCSI mid layer issues a REQUEST + * SENSE SCSI command. The response of the REQUEST SENSE is 18 bytes or more + * in length and is called the "sense buffer". It will indicate why the original + * command may not have been executed. It is important to realize that a CHECK + * CONDITION may very in severity from informative (e.g. command needed to be + * retried before succeeding) to fatal (e.g. 'medium error' which often + * indicates it is time to replace the disk). + * + * Note 2: + * When using the ATAPI BIOS, we also do not need to set up the timimg in linux + * driver. But it is necessary to write the timing routine in win system, + * cause it has a s1, s2, s3 mode and devices wake up from these modes need to + * be initialized again and do not pass through the BIOS. + * + * Note 3: + * The 48-bit support and AP for RAID in linux will the next job. + * + * Revision 1.31 2003/03/14 09:40:35 root + * Fix an error when no disc is on the CD-ROM and the audio cd is ready to play. + * + * 2003/04/08 root + * The ioctl code sklection is finished. But there is a problem about + * "Bad address" when copy_from_user() is called. + * + * Fixed: Use kmalloc() and kfree() to allocate the buffer instead of automatic + * variables (use stack). The stack size is limited in kernel space. + * + * Revision 1.32 2003/04/14 18:20:23 root + * Complete the IOCTLs code. + * + * The IOCTLs are listed below + * =========================== + * + * (1) ITE_IOC_GET_IDENTIFY_DATA + * + * Return virtual drive 512 bytes identification data. + * + * (2) ITE_IOC_GET_PHY_DISK_STATUS + * + * Developer can decide to return 4 physical disk information in + * 512 bytes (data structure should be defined) or 512 bytes + * identification data of the physical disk specified by AP. + * + * (3) ITE_IOC_CREATE_DISK_ARRAY + * + * Create a new array and erase (or keep) boot sector. + * + * (4) ITE_IOC_REBUILD_START + * + * AP nees to specify target/source drive, starting LBA and length. + * + * (5) ITE_IOC_GET_REBUILD_STATUS + * + * Return rebuild percentage or last LBA No. + * + * (6) ITE_IOC_RESET_ADAPTER + * + * Reset the controller. + * + * Revision 1.33 2003/04/15 11:10:08 root + * The 48-bit support. + * + * Revision 1.34 2003/04/20 13:20:38 root + * Change some values in iteraid.h, so it will not hang in Red Hat Linux + * and improve the performance. + * + * can_queue: 1 --------------------> can_queue: 124 + * sg_tablesize: SG_NONE -----------> sg_tablesize: 16 + * cmd_per_lun: 128 ----------------> cmd_per_lun: 1 + * use_clustering: ENABLE_CLUSTER --> use_clustering: DISABLE_CLUSTER + * + * 2003/04/25 root + * The code will hang on Gigabyte's motherboard when the sourth bridge is + * sis 962L and 963. + * + * Revision 1.35 2003/04/28 10:06:20 root + * Fixed: Do not enable interrupt again when send each command in + * IdeSendCommand() routine. + * + * 2003/05/20 root + * Linux SCSI error handling code should be changed to new one. + * + * The shortcomings of the existing code. + * + * 1. The old error handling code is an imperfect state machine. It + * would occasionally get stuck in loops whereby the bus would be reset + * over and over again, where the problem would never be resolved and + * control of the machine would never return to the user. + * + * Reference the http://www.andante.org/scsi.html + * + * The kernel after 2.5 or 2.6 will not use the old error handling codes. + * + * In iteraid.h + * + * #define ITERAID \ + * { \ + * proc_name: "it8212", \ + * proc_info: iteraid_proc_info, \ + * . \ + * . \ + * eh_about_handler: iteraid_about_eh, \ --> New added + * eh_device_reset_handler: NULL \ --> New added + * eh_bus_reset_handler: NULL \ --> New added + * eh_host_reset_handler: iteraid_reset_eh \ --> New added + * use_new_eh_code: 0 --> 1 \ + * } + * + * 2003/06/23 root 17:30:41 + * TODO: Error code still use the old method. + * + * Revision 1.36 2003/06/23 19:52:31 root + * Fixed: Use the new error handling code. + * + * Revision 1.40 2003/07/25 10:00:00 root + * Released version 1.40 by Mark Lu. + * + * Revision 1.41 2003/08/06 13:55:17 root + * Added support for clean shutdown notification/feature table. + * + * Revision 1.42 2003/08/21 11:38:57 root + * Problem: When linux was installed onto IT8212 controller with two disks, + * configured as RAID 1 (P0S0), the hot swap will hang the system. + * Solve: Use the AtapiResetController() instead of only IT8212ResetAdapter(). + * + * Revision 1.43 2003/12/24 23:19:07 root + * Fixed: Fixed a compile error at line 5815. Just move up the variable + * rebuild_info of type PRAID_REBUILD_INFO with other variables. + * + * Revision 1.44 2004/03/16 13:12:35 root + * Fixed: (1) The crash problem when using "rmmod" to remove the iteraid module. + * (2) Support two IT8212 cards or chips. + * (3) A bug when accessing the slave disk more than 137G. + * Thanks for Martine Purschke kindly help to find this bug and + * fix it. + * (4) can_queue: 12 --------------------> can_queue: 1 + * (5) Change the Transparent(Bypass) mode initial PCI registers setting. + * (6) Change IDE I/O, control and dma base address from USHORT to ULONG, + * so that the non x86 platform, like MIPS, will load the correct + * address. + * (7) Add GPL license in iteraid.h. + * + * Revision 1.45 2004/05/07 11:07:16 root + * Fixed : (1) 64-bit support. + * (2) In IT8212SetBestTransferMode() there are a number of arrays, + * all of which are defined read/write and assigned on the stack. + * Now put them in a R/O segment, by replacing e.g. "UCHAR + * udmaTiming" with "static const UCHAR udmaTiming". + */ + +#include +MODULE_AUTHOR("ITE Tech,Inc."); +MODULE_DESCRIPTION("ITE IT8212 RAID Controller Linux Driver"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "scsi.h" +#include + +#include "iteraid.h" +MODULE_LICENSE("GPL"); + +#define MARK_DEBUG_DUMP_MEM 0 /* 1=Enable dump memory content */ +#define MARK_DEBUG_BYPASS_MODE 0 /* 1=Enable use bypass mode */ +#define MARK_DUMP_CREATE_INFO 0 /* 1=Dump raid create info */ +#define MARK_SET_BEST_TRANSFER 0 /* 0=BIOS set best trans mode */ + +#define PRD_BYTES 8 /* PRD table size */ +#define PRD_ENTRIES (PAGE_SIZE / (2 * PRD_BYTES)) +struct Scsi_Host *ite_vhost = 0; /* SCSI virtual host */ +Scsi_Cmnd *it8212_req_last = 0; /* SRB request list */ +unsigned int NumAdapters = 0; /* Adapters number */ +PITE_ADAPTER ite_adapters[2]; /* How many adapters support */ + +/************************************************************************ + * Notifier blockto get a notify on system shutdown/halt/reboot. + ************************************************************************/ +static int ite_halt(struct notifier_block *nb, ulong event, void *buf); +struct notifier_block ite_notifier = { ite_halt, NULL, 0 +}; +static struct semaphore mimd_entry_mtx; +static spinlock_t queue_request_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t io_request_lock = SPIN_LOCK_UNLOCKED; + +static int iteraid_detect(Scsi_Host_Template *); +static int iteraid_release(struct Scsi_Host *); +#if 0 +static int iteraid_command(Scsi_Cmnd *); +#endif +static int iteraid_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *)); +static int iteraid_biosparam(struct scsi_device *, struct block_device *, + sector_t, int *); +static int iteraid_proc_info(struct Scsi_Host *, char *buffer, char **start, + off_t offset, int length, int inout); + +static void TaskStart(PChannel, Scsi_Cmnd *); +static void TaskQueue(void); +static void TaskDone(PChannel, PSCSI_REQUEST_BLOCK); +static u32 IdeSendCommand(PChannel, PSCSI_REQUEST_BLOCK); +//static void IdeMediaStatus(u8, PChannel, u8); +static void IdeSetupDma(PChannel, unsigned long, unsigned short); +static void MapRequest(Scsi_Cmnd *, PSCSI_REQUEST_BLOCK); +static u8 IssueIdentify(PChannel, u8, u8); +static u8 IT8212ResetAdapter(PITE_ADAPTER); +static void AtapiStartIo(PChannel, PSCSI_REQUEST_BLOCK); +static u8 AtapiInterrupt(PChannel); +static void AtapiResetController(PITE_ADAPTER pAdap, PChannel pChan); + +static int itedev_open(struct inode *, struct file *); +static int itedev_ioctl_entry(struct inode *, struct file *, unsigned int, + unsigned long); +static int itedev_ioctl(struct inode *, struct file *, unsigned int, + unsigned long); +static int itedev_close(struct inode *, struct file *); + + +#define DRV_VER_8212 "1.45" +static int driver_ver = 145; /* Current driver version */ +static int ite_major = 0; /* itedev chardev major number */ + +/************************************************************************ + * The File Operations structure for the ioctl interface of the driver. + ************************************************************************/ +static struct file_operations itedev_fops = + {.owner = THIS_MODULE,.ioctl = itedev_ioctl_entry,.open = +itedev_open,.release = itedev_close +}; + +#if (MARK_DEBUG_DUMP_MEM) +/************************************************************************ + * Dump buffer. + ************************************************************************/ +static void HexDump(unsigned char *buf, int length) +{ + unsigned int i = 0; + unsigned int j = 0; + + printk("\n"); + for (i = 0; i < length; i += 16) { + printk("%04X ", i); + for (j = i; (j < i + 8) && (j < length); j++) + printk(" %02X", buf[j]); + if (j == i + 8) + printk("-"); + for (j = i + 8; (j < i + 16) && (j < length); j++) + printk("%02X ", buf[j]); + printk("\n"); + } +} /* end HexDump */ +#endif + +/************************************************************************ + * This routine maps ATAPI and IDE errors to specific SRB statuses. + ************************************************************************/ +static u8 MapError(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) +{ + u8 errorByte; + u8 srbStatus; + u8 scsiStatus; + + /* + * Read the error register. + */ + errorByte = inb(pChan->io_ports[IDE_ERROR_OFFSET]); + printk("MapError: error register is %x\n", errorByte); + + /* + * If this is ATAPI error. + */ + if (pChan->DeviceFlags[Srb->TargetId & 1] & DFLAGS_ATAPI_DEVICE) { + switch (errorByte >> 4) { + case SCSI_SENSE_NO_SENSE: + printk("ATAPI: no sense information\n"); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + case SCSI_SENSE_RECOVERED_ERROR: + printk("ATAPI: recovered error\n"); + scsiStatus = 0; + srbStatus = SRB_STATUS_SUCCESS; + break; + case SCSI_SENSE_NOT_READY: + printk("ATAPI: device not ready\n"); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + case SCSI_SENSE_MEDIUM_ERROR: + printk("ATAPI: media error\n"); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + case SCSI_SENSE_HARDWARE_ERROR: + printk("ATAPI: hardware error\n"); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + case SCSI_SENSE_ILLEGAL_REQUEST: + printk("ATAPI: illegal request\n"); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + case SCSI_SENSE_UNIT_ATTENTION: + printk("ATAPI: unit attention\n"); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + case SCSI_SENSE_DATA_PROTECT: + printk("ATAPI: data protect\n"); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + case SCSI_SENSE_BLANK_CHECK: + printk("ATAPI: blank check\n"); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + case SCSI_SENSE_ABORTED_COMMAND: + printk("ATAPI: command Aborted\n"); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + break; + default: + printk("ATAPI: invalid sense information\n"); + scsiStatus = 0; + srbStatus = SRB_STATUS_ERROR; + break; + } + } else { + /* + * If this is IDE error. + */ + scsiStatus = 0; + srbStatus = SRB_STATUS_ERROR; + + /* + * Save errorByte, to be used by SCSIOP_REQUEST_SENSE. + */ + pChan->ReturningMediaStatus = errorByte; + if (errorByte & IDE_ERROR_MEDIA_CHANGE_REQ) { + printk("IDE: media change\n"); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + } else if (errorByte & IDE_ERROR_COMMAND_ABORTED) { + printk("IDE: command abort\n"); + srbStatus = SRB_STATUS_ABORTED; + scsiStatus = SCSISTAT_CHECK_CONDITION; + if (Srb->SenseInfoBuffer) { + PSENSE_DATA senseBuffer = + (PSENSE_DATA) Srb->SenseInfoBuffer; + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xB; + senseBuffer->SenseKey = + SCSI_SENSE_ABORTED_COMMAND; + senseBuffer->AdditionalSenseCode = 0; + senseBuffer->AdditionalSenseCodeQualifier = 0; + srbStatus |= SRB_STATUS_AUTOSENSE_VALID; + } + + /* + * pChan->ErrorCount++; + */ + } else if (errorByte & IDE_ERROR_END_OF_MEDIA) { + printk("IDE: end of media\n"); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + if (! + (pChan-> + DeviceFlags[Srb-> + TargetId & 1] & + DFLAGS_MEDIA_STATUS_ENABLED)) { + + /* + * pChan->ErrorCount++; + */ + } + } else if (errorByte & IDE_ERROR_ILLEGAL_LENGTH) { + printk("IDE: illegal length\n"); + srbStatus = SRB_STATUS_INVALID_REQUEST; + } else if (errorByte & IDE_ERROR_BAD_BLOCK) { + printk("IDE: bad block\n"); + srbStatus = SRB_STATUS_ERROR; + scsiStatus = SCSISTAT_CHECK_CONDITION; + if (Srb->SenseInfoBuffer) { + PSENSE_DATA senseBuffer = + (PSENSE_DATA) Srb->SenseInfoBuffer; + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xB; + senseBuffer->SenseKey = SCSI_SENSE_MEDIUM_ERROR; + senseBuffer->AdditionalSenseCode = 0; + senseBuffer->AdditionalSenseCodeQualifier = 0; + srbStatus |= SRB_STATUS_AUTOSENSE_VALID; + } + } else if (errorByte & IDE_ERROR_ID_NOT_FOUND) { + printk("IDE: id not found\n"); + srbStatus = SRB_STATUS_ERROR; + scsiStatus = SCSISTAT_CHECK_CONDITION; + if (Srb->SenseInfoBuffer) { + PSENSE_DATA senseBuffer = + (PSENSE_DATA) Srb->SenseInfoBuffer; + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xB; + senseBuffer->SenseKey = SCSI_SENSE_MEDIUM_ERROR; + senseBuffer->AdditionalSenseCode = 0; + senseBuffer->AdditionalSenseCodeQualifier = 0; + srbStatus |= SRB_STATUS_AUTOSENSE_VALID; + } + + /* + * pChan->ErrorCount++; + */ + } else if (errorByte & IDE_ERROR_MEDIA_CHANGE) { + printk("IDE: media change\n"); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + if (Srb->SenseInfoBuffer) { + PSENSE_DATA senseBuffer = + (PSENSE_DATA) Srb->SenseInfoBuffer; + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xD; + senseBuffer->SenseKey = + SCSI_SENSE_UNIT_ATTENTION; + senseBuffer->AdditionalSenseCode = + SCSI_ADSENSE_MEDIUM_CHANGED; + senseBuffer->AdditionalSenseCodeQualifier = 0; + srbStatus |= SRB_STATUS_AUTOSENSE_VALID; + } + } else if (errorByte & IDE_ERROR_DATA_ERROR) { + printk("IDE: data error\n"); + scsiStatus = SCSISTAT_CHECK_CONDITION; + srbStatus = SRB_STATUS_ERROR; + if (! + (pChan-> + DeviceFlags[Srb-> + TargetId & 1] & + DFLAGS_MEDIA_STATUS_ENABLED)) { + + /* + * pChan->ErrorCount++; + */ + } + + /* + * Build sense buffer. + */ + if (Srb->SenseInfoBuffer) { + PSENSE_DATA senseBuffer = + (PSENSE_DATA) Srb->SenseInfoBuffer; + senseBuffer->ErrorCode = 0x70; + senseBuffer->Valid = 1; + senseBuffer->AdditionalSenseLength = 0xB; + senseBuffer->SenseKey = SCSI_SENSE_MEDIUM_ERROR; + senseBuffer->AdditionalSenseCode = 0; + senseBuffer->AdditionalSenseCodeQualifier = 0; + srbStatus |= SRB_STATUS_AUTOSENSE_VALID; + } + } + } + + /* + * Set SCSI status to indicate a check condition. + */ + Srb->ScsiStatus = scsiStatus; + return srbStatus; +} /* end MapError */ + +#if 0 +/************************************************************************ + * Just get the higest bit value. + ************************************************************************/ +static u8 RaidGetHighestBit(u8 Number) +{ + char bit; + + for (bit = 7; bit >= 0; bit--) { + if (Number & (1 << bit)) + return bit; + } + return 0xFF; +} /* end RaidGetHighestBit */ +#endif + +/************************************************************************ + * Reset IDE controller or ATAPI device. + ************************************************************************/ +static void AtapiResetController(PITE_ADAPTER pAdap, PChannel pChan) +{ + u8 resetResult; + u8 status; + u8 i; + unsigned long dma_base; + SCSI_REQUEST_BLOCK srb; + + printk("AtapiResetController enter\n"); + dma_base = pChan->dma_base; + resetResult = FALSE; + + /* + * Check and see if we are processing an internal srb. + */ + if (pChan->OriginalSrb) { + pChan->CurrentSrb = pChan->OriginalSrb; + pChan->OriginalSrb = NULL; + } + + /* + * To avoid unexpected interrupts occurs during reset procedure. + * + * 1. Stop bus master operation. + */ + outb(0, dma_base); + for (i = 0; i < 2; i++) { + outb((u8) ((i << 4) | 0xA0), + pChan->io_ports[ATAPI_SELECT_OFFSET]); + + /* + * 2. Clear interrupts if there is any. + */ + GetBaseStatus(pChan, status); + + /* + * 3. Disable interrupts. + */ + outb(IDE_DC_DISABLE_INTERRUPTS, + pChan->io_ports[ATAPI_CONTROL_OFFSET]); + + /* + * 4. Clear interrupts again. + */ + GetBaseStatus(pChan, status); + } + + /* + * Check if request is in progress. + */ + if (pChan->CurrentSrb) { + + /* + * Complete outstanding request with SRB_STATUS_BUS_RESET. + */ + srb.SrbStatus = SRB_STATUS_BUS_RESET; + + /* + * Clear request tracking fields. + */ + pChan->CurrentSrb = NULL; + pChan->WordsLeft = 0; + pChan->DataBuffer = NULL; + + /* + * Indicate ready for next request. + */ + TaskDone(pChan, &srb); + } + + /* + * Clear expecting interrupt flag. + */ + pChan->ExpectingInterrupt = FALSE; + pChan->RDP = FALSE; + resetResult = IT8212ResetAdapter(pAdap); + + /* + * Set transfer modes after resetting the adapter. + */ + + /* + * Reenable interrupts. + */ + for (i = 0; i < 4; i++) { + outb((u8) (((i & 1) << 4) | 0xA0), + pChan->io_ports[ATAPI_SELECT_OFFSET]); + outb(IDE_DC_REENABLE_CONTROLLER, + pChan->io_ports[ATAPI_CONTROL_OFFSET]); + } + printk("AtapiResetController exit\n"); +} /* end AtapiResetController */ + +/************************************************************************ + * IDE start read/write transfer. + ************************************************************************/ +static void IdeStartTransfer(PChannel pChan, PSCSI_REQUEST_BLOCK Srb, + u32 startingSector, u32 SectorNumber) +{ + u8 DiskId; + u8 drvSelect; + u8 bmClearStat; + unsigned long dma_base; + + dprintk("IdeStartTransfer enter\n"); + DiskId = (u8) Srb->TargetId; + dma_base = pChan->dma_base; + + /* + * 48-bit support. + */ + if ((startingSector + SectorNumber) > 0x0FFFFFFF) { + + /* + * Select drive and set LBA mode. + */ + outb((u8) (((DiskId & 0x1) << 4) | 0xA0 | 0x40), + pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Sector count register. + */ + outb((u8) (SectorNumber >> 8), + pChan->io_ports[IDE_NSECTOR_OFFSET]); + outb((u8) SectorNumber, pChan->io_ports[IDE_NSECTOR_OFFSET]); + + /* + * LBA low register. + */ + outb((u8) (startingSector >> 24), + pChan->io_ports[IDE_LOCYL_OFFSET]); + outb((u8) startingSector, pChan->io_ports[IDE_LOCYL_OFFSET]); + + /* + * LBA mid register. + */ + outb((u8) 0, pChan->io_ports[IDE_MIDCYL_OFFSET]); + outb((u8) (startingSector >> 8), + pChan->io_ports[IDE_MIDCYL_OFFSET]); + + /* + * LBA high register. + */ + outb((u8) 0, pChan->io_ports[IDE_HCYL_OFFSET]); + outb((u8) (startingSector >> 16), + pChan->io_ports[IDE_HCYL_OFFSET]); + + /* + * Start the IDE read/write DMA command. + */ + if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) { + outb(IDE_COMMAND_READ_DMA_EXT, + pChan->io_ports[IDE_COMMAND_OFFSET]); + } else if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) { + outb(IDE_COMMAND_WRITE_DMA_EXT, + pChan->io_ports[IDE_COMMAND_OFFSET]); + } + } else { + /* + * 28-bit addressing. + */ + + /* + * Select drive and set LBA mode. + */ + drvSelect = (u8) (startingSector >> 24); + drvSelect = drvSelect | (((u8) DiskId & 0x1) << 4)|0x40|0xA0; + outb(drvSelect, pChan->io_ports[IDE_SELECT_OFFSET]); + outb((u8) SectorNumber, pChan->io_ports[IDE_NSECTOR_OFFSET]); + outb((u8) startingSector, pChan->io_ports[IDE_LOCYL_OFFSET]); + outb((u8) (startingSector >> 8), + pChan->io_ports[IDE_MIDCYL_OFFSET]); + outb((u8) (startingSector >> 16), + pChan->io_ports[IDE_HCYL_OFFSET]); + + /* + * Start the IDE read/write DMA command. + */ + if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) { + outb(IDE_COMMAND_READ_DMA, + pChan->io_ports[IDE_COMMAND_OFFSET]); + } else if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) { + outb(IDE_COMMAND_WRITE_DMA, + pChan->io_ports[IDE_COMMAND_OFFSET]); + } + } + + /* + * Indicate expecting an interrupt. + */ + pChan->ExpectingInterrupt = TRUE; + + /* + * Setup PRD table physical address. + */ + outl(pChan->dmatable_dma, dma_base + 4); + + /* + * Read Bus Master status. + */ + bmClearStat = inb(dma_base + 2); + if (Srb->TargetId & 1) { + bmClearStat = + bmClearStat | BM_DRV1_DMA_CAPABLE | BM_STAT_FLG_INT | + BM_STAT_FLG_ERR; + } else { + bmClearStat = + bmClearStat | BM_DRV0_DMA_CAPABLE | BM_STAT_FLG_INT | + BM_STAT_FLG_ERR; + } + outb(0, dma_base); + + /* + * Clear INTR and ERROR flags. + */ + outb(bmClearStat, dma_base + 2); + + /* + * Start DMA read/write. + */ + if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) + outb(BM_CMD_FLG_START | BM_CMD_FLG_WRTTOMEM, dma_base); + else if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) + outb(BM_CMD_FLG_START | BM_CMD_FLG_WRTTODSK, dma_base); + dprintk("IdeStartTransfer exit\n"); +} /* end IdeStartTransfer */ + +/************************************************************************ + * Setup the PRD table. + ************************************************************************/ +static int IdeBuildSglist(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) +{ + int nents = 0; + u32 bytesRemaining = Srb->DataTransferLength; + unsigned char *virt_addr = Srb->DataBuffer; + struct scatterlist *sg = pChan->sg_table; + + if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) + pChan->sg_dma_direction = PCI_DMA_FROMDEVICE; + else + pChan->sg_dma_direction = PCI_DMA_TODEVICE; + + /* + * The upper layer will never give the memory more than 64K bytes. + */ + memset(&sg[nents], 0, sizeof(*sg)); + sg[nents].dma_address = (dma_addr_t) virt_addr; + sg[nents].length = bytesRemaining; + nents++; + return pci_map_sg(pChan->pPciDev, sg, nents, pChan->sg_dma_direction); +} /* end IdeBuildSglist */ + +/************************************************************************ + * Prepares a dma request. + ************************************************************************/ +static int IdeBuildDmaTable(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) +{ + unsigned long *table = pChan->dmatable_cpu; + unsigned int count = 0; + int i; + struct scatterlist *sg; + + i = IdeBuildSglist(pChan, Srb); + sg = pChan->sg_table; + while (i && sg_dma_len(sg)) { + u32 cur_len; + u32 cur_addr; + cur_addr = sg_dma_address(sg); + cur_len = sg_dma_len(sg); + + /* + * Fill in the dma table, without crossing any 64kB boundaries. + */ + while (cur_len) { + if (count++ >= PRD_ENTRIES) { + printk(KERN_WARNING "@@DMA table too small\n"); + } else { + u32 xcount, bcount = + 0x10000 - (cur_addr & 0xFFFF); + if (bcount > cur_len) + bcount = cur_len; + *table++ = cpu_to_le32(cur_addr); + xcount = bcount & 0xFFFF; + if (xcount == 0x0000) { + /* + * Most chipsets correctly interpret a + * length of 0x0000 as 64KB, but at + * least one (e.g. CS5530) misinterprets + * it as zero (!). So here we break the + * 64KB entry into two 32KB entries + * instead. + */ + if (count++ >= PRD_ENTRIES) + printk(KERN_WARNING + "##DMA table too small\n"); + *table++ = cpu_to_le32(0x8000); + *table++ = + cpu_to_le32(cur_addr + 0x8000); + xcount = 0x8000; + } + *table++ = cpu_to_le32(xcount); + cur_addr += bcount; + cur_len -= bcount; + } + } + sg++; + i--; + } + if (count) { + *--table |= cpu_to_le32(0x80000000); + return count; + } else { + printk(KERN_WARNING "Empty DMA table?\n"); + } + return count; +} /* end IdeBuildDmaTable */ + +/************************************************************************ + * Prepares a dma scatter/gather request. + ************************************************************************/ +static void IdeBuildDmaSgTable(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) +{ + int use_sg = 0; + int i; + PPRD_TABLE_ENTRY pSG = (PPRD_TABLE_ENTRY) pChan->dmatable_cpu; + struct scatterlist *sg = (struct scatterlist *)Srb->DataBuffer; + + if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) + pChan->sg_dma_direction = PCI_DMA_FROMDEVICE; + else + pChan->sg_dma_direction = PCI_DMA_TODEVICE; + + use_sg = pci_map_sg(pChan->pPciDev, Srb->DataBuffer, Srb->UseSg, + pChan->sg_dma_direction); + for (i = 0; i < use_sg; i++) { + pSG[i].PhysicalBaseAddress = sg_dma_address(&sg[i]); + pSG[i].ByteCount = sg_dma_len(&sg[i]); + pSG[i].EndOfTable = (i == use_sg - 1) ? SG_FLAG_EOT : 0; + } +} /* end IdeBuildDmaSgTable */ + +/************************************************************************ + * Setup DMA table for channel. + ************************************************************************/ +static void +IdeSetupDma(PChannel pChan, unsigned long dma_base, unsigned short num_ports) +{ + printk("Channel[%d] BM-DMA at 0x%lX-0x%lX\n", pChan->channel, + dma_base, dma_base + num_ports - 1); + + /* + * Allocate IDE DMA buffer. + */ + pChan->dmatable_cpu = + pci_alloc_consistent(pChan->pPciDev, PRD_ENTRIES * PRD_BYTES, + &pChan->dmatable_dma); + if (pChan->dmatable_cpu == NULL) { + printk("IdeSetupDma: allocate prd table failed.\n"); + return; + } + memset(pChan->dmatable_cpu, 0, PRD_ENTRIES * PRD_BYTES); + + /* + * Allocate SCATTER/GATHER table buffer. + */ + pChan->sg_table = + kmalloc(sizeof(struct scatterlist) * PRD_ENTRIES, GFP_KERNEL); + if (pChan->sg_table == NULL) { + printk("IdeSetupDma: allocate sg_table failed.\n"); + pci_free_consistent(pChan->pPciDev, PRD_ENTRIES * PRD_BYTES, + pChan->dmatable_cpu, pChan->dmatable_dma); + return; + } + return; +} /* end IdeSetupDma */ + +/************************************************************************ + * This will be only used in RAID mode. + ************************************************************************/ +void IT8212ReconfigChannel(PChannel pChan, u8 ArrayId, u8 Operation) +{ + u8 enableVirtualChannel; + struct pci_dev *pPciDev = pChan->pPciDev; + + pci_read_config_byte(pPciDev, 0x43, &enableVirtualChannel); + if (Operation == DisableChannel) { + enableVirtualChannel &= ~(1 << ArrayId); + printk("IT8212ReconfigChannel: disable channel %X\n", ArrayId); + } else { + enableVirtualChannel |= ~(1 << ArrayId); + printk("IT8212ReconfigChannel: enable channel %X\n", ArrayId); + } + printk("IT8212ReconfigChannel: channel enabled after set 0x%X\n", + enableVirtualChannel); + + /* + * Set enabled virtual channels. + */ + pci_write_config_byte(pPciDev, 0x43, enableVirtualChannel); +} /* end IT8212ReconfigChannel */ + +/************************************************************************ + * Get the chip status. This is a vendor specific command. According to + * all of the device configurations, the BIOS then can consider the + * existing RAID configuration reasonable. If the existing RAID configur- + * ation is not reasonable, or if there is NO existing RAID configuration + * , the BIOS can ask the user to setup the RAID configuration. Finally, + * the BIOS or AP should send the SET CHIP STATUS to every virtual device. + * Only after receiving SET CHIP STATUS command, the corresponding virtual + * device will be active. + ************************************************************************/ +u8 IT8212GetChipStatus(uioctl_t * ioc) +{ + u8 PriMasterIsNull = FALSE; + u8 statusByte; + u8 srbStatus; + PChannel pChan; + PITE_ADAPTER pAdap; + PHYSICAL_DISK_STATUS *pPhyDiskInfo; + + dprintk("IT8212GetChipStatus enter\n"); + + /* + * Only support one controller now! In the future, we can pass the + * argument (user ioctl structure) to know which controller need to be + * identified. + */ + pAdap = ite_adapters[0]; + pChan = &pAdap->IDEChannel[0]; + + /* + * Allocate space for PHYSICAL_DISK_STATUS. + */ + pPhyDiskInfo = kmalloc(sizeof(PHYSICAL_DISK_STATUS) * 4, GFP_KERNEL); + if (pPhyDiskInfo == NULL) { + printk("IT8212GetChipStatus: error kmalloc for " + "PHYSCIAL_DISK_STATUS.\n"); + return -ENOMEM; + } + memset(pPhyDiskInfo, 0, sizeof(PHYSICAL_DISK_STATUS)); + + /* + * Always send GET CHIP STATUS command to primary channel master device. + * Select device. + */ + outb((u8) 0xA0, pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * If primary master channel is not enabled, enable it. + */ + statusByte = inb(pChan->io_ports[IDE_ALTERNATE_OFFSET]); + if (statusByte == 0) { + PriMasterIsNull = TRUE; + IT8212ReconfigChannel(pChan, 0, EnableChannel); + } + + /* + * Wait for device ready (Not BUSY and not DRQ) + */ + WaitForDeviceReady(pChan, statusByte); + if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) + || (statusByte == 0)) { + printk("IT8212GetChipStatus: disk[0] not ready. status=0x%X\n", + statusByte); + srbStatus = SRB_STATUS_BUSY; + goto exit; + } + + /* + * Disable interrupt to avoid the unexpected interrupt. + */ + outb(IDE_DC_DISABLE_INTERRUPTS, pChan->io_ports[IDE_CONTROL_OFFSET]); + + /* + * Issue the command. + */ + outb(IDE_COMMAND_GET_CHIP_STATUS, pChan->io_ports[IDE_COMMAND_OFFSET]); + + /* + * Wait for BUSY = 0, DRQ = 1. + */ + CheckBusyDrq(pChan, statusByte) if (statusByte != 0x58) { + printk("IT8212GetChipStatus: disk[0] return unexpected " + "status after"); + printk("issue command. status=0x%X\n", statusByte); + goto exit_error; + } + + /* + * Read the physical disk info. + */ + ReadBuffer(pChan, (unsigned short *)pPhyDiskInfo, 256); + +#if (0) + HexDump((unsigned char *)pPhyDiskInfo, 512); + +#endif + + /* + * Copy physical disk info to user area. + */ + copy_to_user((unsigned short *)ioc->data, + (unsigned char *)pPhyDiskInfo, 512); + + /* + * Check error. + */ + WaitForCommandComplete(pChan, statusByte); + if (statusByte != IDE_STATUS_IDLE) { + printk("IT8212GetChipStatus: disk[0] return unexpected " + "status after read data. status=0x%X\n", statusByte); + goto exit_error; + } + srbStatus = SRB_STATUS_SUCCESS; + goto exit; +exit_error: + /* + * If fail, hard reset to avoid the DRQ status pending. + */ + srbStatus = SRB_STATUS_ERROR; + IdeHardReset(pChan, statusByte); +exit: + /* + * Reenable interrupt after command complete. + */ + outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]); + + /* + * If primary master is null, disable primary master channel before we + * leave. + */ + if (PriMasterIsNull) + IT8212ReconfigChannel(pChan, 0, DisableChannel); + dprintk("IT8212GetChipStatus exit\n"); + return srbStatus; +} /* end IT8212GetChipStatus */ + +/************************************************************************ + * Erase the partition table. + ************************************************************************/ +unsigned char IT8212ErasePartition(uioctl_t * pioc) +{ + unsigned char drvSelect; + unsigned char statusByte = 0; + unsigned char srbStatus; + unsigned char *buffer; + PRAID_CREATE_INFO createInfo = (PRAID_CREATE_INFO) pioc->data; + PITE_ADAPTER pAdap; + PChannel pChan; + + printk("IT8212ErasePartition enter\n"); + printk("createInfo->DiskArrayId = %d\n", createInfo->DiskArrayId); + if (createInfo->ErasePartition == 0 + || (createInfo->RaidType == RAID_LEVEL_NODISK)) + return SRB_STATUS_SUCCESS; + pAdap = ite_adapters[0]; + if (createInfo->DiskArrayId < 2) + pChan = &pAdap->IDEChannel[0]; + else + pChan = &pAdap->IDEChannel[1]; + + /* + * Allocate 512 bytes for buffer. + */ + if ((buffer = kmalloc(512, GFP_KERNEL)) == NULL) { + printk("IT8212ErasePartition: error kmalloc.\n"); + return -ENOMEM; + } + memset(buffer, 0, 512); + + /* + * Select device. + */ + drvSelect = (((u8) createInfo->DiskArrayId & 0x1) << 4) | 0xA0 | 0x40; + outb(drvSelect, pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Wait for device ready (not BUSY and not DRQ). + */ + WaitForDeviceReady(pChan, statusByte); + if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) + || (statusByte == 0)) { + printk + ("IT8212ErasePartition: disk[%d] not ready. status=0x%X\n", + createInfo->DiskArrayId, statusByte); + return SRB_STATUS_BUSY; + } + + /* + * Disable interrupt to avoid the unexpected interrupt. + */ + outb(IDE_DC_DISABLE_INTERRUPTS, pChan->io_ports[IDE_CONTROL_OFFSET]); + + /* + * Write LBA 0 (1 sector). + */ + outb(1, pChan->io_ports[IDE_NSECTOR_OFFSET]); + outb(0, pChan->io_ports[IDE_LOCYL_OFFSET]); + outb(0, pChan->io_ports[IDE_MIDCYL_OFFSET]); + outb(0, pChan->io_ports[IDE_HCYL_OFFSET]); + outb(drvSelect, pChan->io_ports[IDE_SELECT_OFFSET]); + outb(IDE_COMMAND_WRITE_SECTOR, pChan->io_ports[IDE_COMMAND_OFFSET]); + + /* + * Wait for BUSY = 0, DRQ = 1. + */ + CheckBusyDrq(pChan, statusByte); + if (statusByte != 0x58) { + printk("IT8212ErasePartition: disk[%d] error status. " + "status=0x%X\n", createInfo->DiskArrayId, statusByte); + goto exit_error; + } + + /* + * Start erase partition table. + */ + WriteBuffer(pChan, (unsigned short *)buffer, 256); + + /* + * Check error. + */ + WaitForCommandComplete(pChan, statusByte); + if (statusByte != IDE_STATUS_IDLE) { + printk("IT8212ErasePartition: disk[%d] error status. " + "status=0x%X\n", createInfo->DiskArrayId, statusByte); + goto exit_error; + } + srbStatus = SRB_STATUS_SUCCESS; + goto exit; +exit_error: + /* + * If failed, hard reset to avoid the DRQ status pending. + */ + IdeHardReset(pChan, statusByte); + srbStatus = SRB_STATUS_ERROR; +exit: + /* + * Reenable interrupt after command complete. + */ + outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]); + printk("IT8212ErasePartition exit\n"); + return srbStatus; +} /* end IT8212ErasePartition */ + +/************************************************************************ + * + ************************************************************************/ +static u32 IT8212TruncateReduentSectors(u32 OriginalSectorCount, + u16 StripeSizeInKBytes) +{ + u16 stripeSizeInSector; + + /* + * 0 means using default value (32 sectors). + */ + if (StripeSizeInKBytes == 0) + stripeSizeInSector = 64 * 2; + else + stripeSizeInSector = StripeSizeInKBytes * 2; + return ((OriginalSectorCount / stripeSizeInSector) * + stripeSizeInSector); +} /* end IT8212TruncateReduentSectors */ + +/************************************************************************ + * Calculate the addressable sector for this RAID. + ************************************************************************/ +static u32 IT8212DiskArrayAddressableSector(unsigned char *DiskArrayCreateInfo) +{ + u8 DiskNo; + u8 NumOfDisks; + u32 MinDiskCapacity; + u32 ArrayCapacity; + PRAID_CREATE_INFO createInfo = (PRAID_CREATE_INFO) DiskArrayCreateInfo; + + MinDiskCapacity = ArrayCapacity = NumOfDisks = 0; + printk("createInfo->AddressableSectors[0] = 0x%X\n", + createInfo->AddressableSectors[0]); + printk("createInfo->AddressableSectors[1] = 0x%X\n", + createInfo->AddressableSectors[1]); + printk("createInfo->AddressableSectors[2] = 0x%X\n", + createInfo->AddressableSectors[2]); + printk("createInfo->AddressableSectors[3] = 0x%X\n", + createInfo->AddressableSectors[3]); + for (DiskNo = 0; DiskNo < 4; DiskNo++) { + + /* + * If disk exist. + */ + if ((createInfo->ContainingDisks >> DiskNo) & 0x1) { + NumOfDisks += 1; + if (!MinDiskCapacity + || (createInfo->AddressableSectors[DiskNo] < + MinDiskCapacity)) { + MinDiskCapacity = + createInfo->AddressableSectors[DiskNo]; + } + } + } + switch (createInfo->RaidType) { + + /* + * Containing 2 or 3 or 4 disks. + */ + case RAID_LEVEL_0: + MinDiskCapacity = + IT8212TruncateReduentSectors(MinDiskCapacity - 2, + createInfo->StripeSize); + ArrayCapacity = MinDiskCapacity * NumOfDisks; + break; + + /* + * Containing 2 disks. + */ + case RAID_LEVEL_1: + ArrayCapacity = MinDiskCapacity - 2; + break; + + /* + * Containing 4 disks. + */ + case RAID_LEVEL_10: + MinDiskCapacity = + IT8212TruncateReduentSectors(MinDiskCapacity - 2, + createInfo->StripeSize); + ArrayCapacity = MinDiskCapacity * 2; + break; + + /* + * Containing 2, 3, or 4 disks. + */ + case RAID_LEVEL_JBOD: + for (DiskNo = 0; DiskNo < 4; DiskNo++) { + if ((createInfo->ContainingDisks >> DiskNo) & 0x1) { + ArrayCapacity = + ArrayCapacity + + (createInfo->AddressableSectors[DiskNo] - + 2); + } + } + break; + + /* + * Containing only 1 disk. + */ + case RAID_LEVEL_NORMAL: + ArrayCapacity = MinDiskCapacity; + break; + } + return ArrayCapacity; +} /* end IT8212DiskArrayAddressableSector */ + +/************************************************************************ + * Create a new array. + ************************************************************************/ +static u8 IT8212CreateDiskArray(uioctl_t * pioc) +{ + u8 i; + u8 subCommand = 0xFF; + u8 statusByte; + u8 dmaSupported; + u8 udmaSupported; + u8 srbStatus; + u8 PriMasterIsNull = FALSE; + u32 UserAddressableSectors; + void *buffer; + PChannel pChan; + PITE_ADAPTER pAdap; + PRAID_CREATE_INFO createInfo = (PRAID_CREATE_INFO) pioc->data; + PIDENTIFY_DATA2 identifyData; + PIT8212_SET_CHIP_STATUS_INFO setChipStatus; + static const u16 IT8212TimingTable[7] = { 0x3133, /* UDMA timimg register 01 */ + 0x2121, /* UDMA timimg register 23 */ + 0x9111, /* UDMA timimg register 45 */ + 0x0091, /* UDMA timimg register 6 */ + 0x3266, /* DMA timimg register 01 */ + 0x0021, /* DMA timimg register 2 */ + 0x0021 /* PIO timimg register */ + }; + static const u16 IT8212ClockTable[7] = { 0x0505, /* UDMA clock register 01 */ + 0x0005, /* UDMA clock register 23 */ + 0x0500, /* UDMA clock register 45 */ + 0x0000, /* UDMA clock register 6 */ + 0x0005, /* DMA clock register 01 */ + 0x0005, /* DMA clock register 2 */ + 0x0005 /* PIO clock register */ + }; + + printk("IT8212CreateDiskArray enter\n"); + +#if (MARK_DUMP_CREATE_INFO) + printk("createInfo->DiskArrayId = %d\n", + createInfo->DiskArrayId); + printk("createInfo->ModelNumber = %s\n", + createInfo->ModelNumber); + printk("createInfo->RaidType = %d\n", + createInfo->RaidType); + printk("createInfo->ContainingDisks = %d\n", + createInfo->ContainingDisks); + printk("createInfo->AutoRebuildEnable = %d\n", + createInfo->AutoRebuildEnable); + printk("createInfo->StripeSize = %d\n", + createInfo->StripeSize); + printk("createInfo->BootableDisk = %d\n", + createInfo->BootableDisk); + printk("createInfo->NewlyCreated = %d\n", + createInfo->NewlyCreated); + printk("createInfo->ErasePartition = %d\n", + createInfo->ErasePartition); + printk("createInfo->DMASupported[0] = 0x%x\n", + createInfo->DMASupported[0]); + printk("createInfo->DMASupported[1] = 0x%x\n", + createInfo->DMASupported[1]); + printk("createInfo->DMASupported[2] = 0x%x\n", + createInfo->DMASupported[2]); + printk("createInfo->DMASupported[3] = 0x%x\n", + createInfo->DMASupported[3]); + printk("createInfo->UDMASupported[0] = 0x%x\n", + createInfo->UDMASupported[0]); + printk("createInfo->UDMASupported[1] = 0x%x\n", + createInfo->UDMASupported[1]); + printk("createInfo->UDMASupported[2] = 0x%x\n", + createInfo->UDMASupported[2]); + printk("createInfo->UDMASupported[3] = 0x%x\n", + createInfo->UDMASupported[3]); + printk("createInfo->AddressableSectors[0] = 0x%lX\n", + createInfo->AddressableSectors[0]); + printk("createInfo->AddressableSectors[1] = 0x%lX\n", + createInfo->AddressableSectors[1]); + printk("createInfo->AddressableSectors[2] = 0x%lX\n", + createInfo->AddressableSectors[2]); + printk("createInfo->AddressableSectors[3] = 0x%lX\n", + createInfo->AddressableSectors[3]); + +#endif /* */ + switch (createInfo->RaidType) { + case RAID_LEVEL_0: + case RAID_LEVEL_1: + case RAID_LEVEL_10: + case RAID_LEVEL_JBOD: + case RAID_LEVEL_NORMAL: + subCommand = 0x50; + break; + case RAID_LEVEL_NODISK: + subCommand = 0x48; + break; + } + + /* + * The command should be sent to virtual primary master. + */ + pAdap = ite_adapters[0]; + pChan = &pAdap->IDEChannel[0]; + + /* + * Allocate 512-bytes buffer. + */ + if ((buffer = kmalloc(512, GFP_KERNEL)) == NULL) { + printk("IT8212CreateDiskArray: error kmalloc.\n"); + return -ENOMEM; + } + identifyData = (PIDENTIFY_DATA2) buffer; + + /* + * 2003/05/08 + * Remember the vendor specific parameters starts from word 129 not 128. + */ + setChipStatus = (PIT8212_SET_CHIP_STATUS_INFO) (buffer + 258); + + /* + * Configure to RAID or NORMAL. + */ + if (subCommand == 0x50) { + + /* + * Zero identify data structure. + */ + memset((unsigned char *)identifyData, 0, sizeof(IDENTIFY_DATA)); + + /* + * Fill up identify data. + */ + memmove(identifyData->ModelNumber, createInfo->ModelNumber, 40); + memmove(identifyData->SerialNumber, &createInfo->SerialNumber, + sizeof(RAID_SERIAL_NUMBER)); + + /* + * Set disk array virtual capacity. + */ + UserAddressableSectors = + IT8212DiskArrayAddressableSector(pioc->data); + printk("IT8212CreateDiskArray: array capacity = %X\n", + UserAddressableSectors); + identifyData->Capacity_48bit_LOW = UserAddressableSectors; + identifyData->Capacity_48bit_HIGH = 0; + if (UserAddressableSectors > 0x0FFFFFFF) + identifyData->UserAddressableSectors = 0x0FFFFFFF; + else + identifyData->UserAddressableSectors = + UserAddressableSectors; + + /* + * Get DMA supported mode and UDMA supported mode. + */ + dmaSupported = udmaSupported = 0xFF; + for (i = 0; i < 4; i++) { + if ((createInfo->ContainingDisks >> i) & 1) { + dmaSupported &= + (u8) createInfo->DMASupported[i]; + udmaSupported &= + (u8) createInfo->UDMASupported[i]; + } + } + identifyData->MultiWordDMASupport = dmaSupported; + identifyData->UltraDMASupport = udmaSupported; + + /* + * Fill up SET CHIP STATUS data (word 129 - 153) + */ + setChipStatus->RaidType = createInfo->RaidType; + setChipStatus->ContainingDisks = createInfo->ContainingDisks; + setChipStatus->UltraDmaTiming01 = IT8212TimingTable[0]; + setChipStatus->UltraDmaTiming23 = IT8212TimingTable[1]; + setChipStatus->UltraDmaTiming45 = IT8212TimingTable[2]; + setChipStatus->UltraDmaTiming6 = IT8212TimingTable[3]; + setChipStatus->MultiWordDmaTiming01 = IT8212TimingTable[4]; + setChipStatus->UltraDmaTiming2 = IT8212TimingTable[5]; + setChipStatus->PioTiming4 = IT8212TimingTable[6]; + setChipStatus->AutoRebuildEnable = + createInfo->AutoRebuildEnable; + setChipStatus->IdeClkUDma01 = IT8212ClockTable[0]; + setChipStatus->IdeClkUDma23 = IT8212ClockTable[1]; + setChipStatus->IdeClkUDma45 = IT8212ClockTable[2]; + setChipStatus->IdeClkUDma6 = IT8212ClockTable[3]; + setChipStatus->IdeClkMDma01 = IT8212ClockTable[4]; + setChipStatus->IdeClkMDma2 = IT8212ClockTable[5]; + setChipStatus->IdeClkPio4 = IT8212ClockTable[6]; + setChipStatus->StripeSize = createInfo->StripeSize; + setChipStatus->BootableDisk = createInfo->BootableDisk; + setChipStatus->CheckHotSwapInterval = 0; + setChipStatus->TargetSourceDisk = createInfo->TargetSourceDisk; + setChipStatus->RebuildBlockSize = 0; + setChipStatus->ResetInterval1 = 0; + setChipStatus->ResetInterval2 = 0; + setChipStatus->RebuildRetryTimes = 0; + setChipStatus->NewlyCreated = createInfo->NewlyCreated; + } +#if (MARK_DEBUG_DUMP_MEM) + HexDump(buffer, 512); + +#endif + + /* + * There are some contrains of disk placement. AP will take care of it. + */ + + /* + * Select device. + */ + outb((u8) 0xA0, pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * If primary master channel is not enabled, enable it. + */ + statusByte = inb(pChan->io_ports[IDE_CONTROL_OFFSET]); + if (statusByte == 0) { + PriMasterIsNull = TRUE; + IT8212ReconfigChannel(pChan, 0, EnableChannel); + } + + /* + * Wait for device ready (not BUSY and not DRQ) + */ + WaitForDeviceReady(pChan, statusByte); + if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) + || (statusByte == 0)) { + printk + ("IT8212CreateDiskArray: disk[0] not ready. status=0x%X\n", + statusByte); + srbStatus = SRB_STATUS_BUSY; + goto exit; + } + + /* + * Disable interrupt to avoid the unexpected interrupt. + */ + outb(IDE_DC_DISABLE_INTERRUPTS, pChan->io_ports[IDE_CONTROL_OFFSET]); + outb(subCommand, pChan->io_ports[IDE_FEATURE_OFFSET]); + outb(createInfo->DiskArrayId, pChan->io_ports[IDE_SELECT_OFFSET]); + outb(IDE_COMMAND_SET_CHIP_STATUS, pChan->io_ports[IDE_COMMAND_OFFSET]); + + /* + * No disk (no data command protocol) + */ + if (subCommand == 0x48) { + WaitForCommandComplete(pChan, statusByte); + if (statusByte != IDE_STATUS_IDLE) { + printk("IT8212CreateDiskArray: disk[0] return " + "unexpected status after issue command.\n"); + goto exit_error; + } + IT8212ReconfigChannel(pChan, createInfo->DiskArrayId, + DisableChannel); + srbStatus = SRB_STATUS_SUCCESS; + goto exit; + } + + /* + * Create RAID (PIO data out command protocol). + */ + if (subCommand == 0x50) { + + /* + * Wait for BUSY=0, DRQ=1. + */ + CheckBusyDrq(pChan, statusByte); + if (statusByte != 0x58) { + printk("IT8212CreateDiskArray: disk[0] return " + "unexpected status after issue command.\n"); + goto exit_error; + } + WriteBuffer(pChan, buffer, 256); + + /* + * Check error. + */ + WaitForCommandComplete(pChan, statusByte); + if (statusByte != IDE_STATUS_IDLE) { + printk("IT8212CreateDiskArray: disk[0] return " + "unexpected status after issue command.\n"); + goto exit_error; + } + IT8212ReconfigChannel(pChan, createInfo->DiskArrayId, + EnableChannel); + srbStatus = SRB_STATUS_SUCCESS; + goto exit; + } +exit_error: + /* + * If fail, hard reset to avoid the DRQ pending. + */ + IdeHardReset(pChan, statusByte); + srbStatus = SRB_STATUS_ERROR; +exit: + /* + * If primary master is null, and we are not configuring array 0. + * Disable primary master channel again. + */ + if (PriMasterIsNull && createInfo->DiskArrayId) + IT8212ReconfigChannel(pChan, 0, DisableChannel); + + /* + * Reenable interrupt after command complete. + */ + outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]); + printk("IT8212CreateDiskArray exit\n"); + return srbStatus; +} /* end IT8212CreateDiskArray */ + +#if 0 +/************************************************************************ + * Return "virtual" drive 512 bytes identification data. + ************************************************************************/ +static u8 IT8212IssueIdentify(uioctl_t *pioc) +{ + u8 channum; + u8 devnum; + u8 statusByte; + u8 srbStatus; + PITE_ADAPTER pAdap; + PChannel pChan; + + /* + * Only support one adapter now! In the future, we can pass the argument + * to know which adapter need to be identified. + */ + pAdap = ite_adapters[0]; + memset(pioc->data, 0, 512 * 4); + + /* + * Two channels per controller. + */ + for (channum = 0; channum < pAdap->num_channels; channum++) { + pChan = &pAdap->IDEChannel[channum]; + + /* + * Two devices per channel. + */ + for (devnum = 0; devnum < 2; devnum++) { + + /* + * Select device. + */ + outb((u8) ((devnum << 4) | 0xA0), + pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Check if disk online? + */ + statusByte = inb(pChan->io_ports[IDE_ALTERNATE_OFFSET]); + if ((statusByte & 0x40) != 0x40) { + printk("IT8212IssueIdentify: disk[%d] is " + "offline\n", devnum + channum * 2); + continue; + } + + /* + * Wait for device ready (Not busy and not DRQ) + */ + WaitForDeviceReady(pChan, statusByte); + if ((statusByte & IDE_STATUS_BUSY) + || (statusByte & IDE_STATUS_DRQ) + || (statusByte == 0)) { + printk("IT8212IssueIdentify: disk[%d] not " + "ready. status=0x%X\n", + devnum + channum * 2, statusByte); + srbStatus = SRB_STATUS_BUSY; + goto exit; + } + + /* + * Disable interrupt to avoid the unexpected interrupt. + */ + outb(IDE_DC_DISABLE_INTERRUPTS, + pChan->io_ports[IDE_CONTROL_OFFSET]); + + /* + * Issue command. + */ + outb(IDE_COMMAND_IDENTIFY, + pChan->io_ports[IDE_COMMAND_OFFSET]); + + /* + * Wait for BUSY = 0 and DRQ = 1. + */ + CheckBusyDrq(pChan, statusByte); + if (statusByte != 0x58) { + printk("IT8212IssueIndetify: disk[%d] returns " + "unexpedted status after issue command." + " status=0x%X\n", + devnum + channum * 2, statusByte); + goto error; + } + + /* + * Read the identify data. + */ + ReadBuffer(pChan, + (unsigned short *)&pChan-> + FullIdentifyData, 256); + + /* + * Then copy to user area. + */ + copy_to_user((unsigned short *)(pioc->data + + ((devnum + + channum * 2) * + 512)), + (unsigned short *)&pChan-> + FullIdentifyData, 256); + + /* + * Check error after reading data. + */ + WaitForCommandComplete(pChan, statusByte); + if (statusByte != IDE_STATUS_IDLE) { + printk("IT8212IssueIdentify: disk[%d] returns " + "unexpected status after read data. " + "status=0x%X\n", + devnum + channum * 2, statusByte); + goto error; + } + } /* end for each device */ + } /* end for each channel */ + srbStatus = SRB_STATUS_SUCCESS; + goto exit; +error: + /* + * If failed, hard reset to avoid the IRQ pending. + */ + IdeHardReset(pChan, statusByte); + srbStatus = SRB_STATUS_ERROR; +exit: + /* + * Reenable interrupt after command complete. + */ + for (channum = 0; channum < pAdap->num_channels; channum++) { + pChan = &pAdap->IDEChannel[channum]; + outb(IDE_DC_REENABLE_CONTROLLER, + pChan->io_ports[IDE_CONTROL_OFFSET]); + } + return srbStatus; +} /* end IT8212IssueIdentify */ +#endif + +/************************************************************************ + * Reset the controller. + ************************************************************************/ +static u8 IT8212ResetAdapter(PITE_ADAPTER pAdap) +{ + u8 resetChannel[2]; + u8 channel; + u8 device; + u8 status[4]; + int i; + PChannel pChan; + + /* + * First, perform ATAPI soft reset if ATAPI devices are attached. + */ + for (channel = 0; channel < 2; channel++) { + pChan = &pAdap->IDEChannel[channel]; + resetChannel[channel] = FALSE; + for (device = 0; device < 2; device++) { + if (pChan->DeviceFlags[device] & DFLAGS_DEVICE_PRESENT) { + if (pChan-> + DeviceFlags[device] & DFLAGS_ATAPI_DEVICE) { + printk("IT8212ResetAdapter: perform " + "ATAPI soft reset (%d, %d)\n", + channel, device); + AtapiSoftReset(pChan, device); + } else { + resetChannel[channel] = TRUE; + } + } + } + } + + /* + * If ATA device is present on this channel, perform channel reset. + */ + for (channel = 0; channel < 2; channel++) { + pChan = &pAdap->IDEChannel[channel]; + if (resetChannel[channel]) { + printk("IT8212ResetAdapter: reset channel %d\n", + channel); + outb(IDE_DC_RESET_CONTROLLER, + pChan->io_ports[IDE_CONTROL_OFFSET]); + mdelay(50); + outb(IDE_DC_REENABLE_CONTROLLER, + pChan->io_ports[IDE_CONTROL_OFFSET]); + } + } + + /* + * Check device status after reset. + */ + for (i = 0; i < 1000 * 1000; i++) { + for (channel = 0; channel < 2; channel++) { + pChan = &pAdap->IDEChannel[channel]; + for (device = 0; device < 2; device++) { + if (pChan-> + DeviceFlags[device] & + DFLAGS_DEVICE_PRESENT) { + outb((u8) ((device << 4) | 0xA0), + pChan-> + io_ports[IDE_SELECT_OFFSET]); + status[(channel * 2) + device] = + inb(pChan-> + io_ports[IDE_COMMAND_OFFSET]); + } else { + status[(channel * 2) + device] = 0; + } + } + } + + /* + * ATA device should present status 0x50 after reset. + * ATAPI device should present status 0 after reset. + */ + if ((status[0] != IDE_STATUS_IDLE && status[0] != 0x0) || + (status[1] != IDE_STATUS_IDLE && status[1] != 0x0) || + (status[2] != IDE_STATUS_IDLE && status[2] != 0x0) || + (status[3] != IDE_STATUS_IDLE && status[3] != 0x0)) { + udelay(30); + } else { + break; + } + } + if (i == 1000 * 1000) { + printk("IT8212ResetAdapter Fail!\n"); + printk("Device status after reset = [0x%x, 0x%x, 0x%x, 0x%x]\n", + status[0], status[1], status[2], status[3]); + return FALSE; + } else { + printk("IT8212ResetAdapter Success!\n"); + return TRUE; + } +} /* end IT8212ResetAdapter */ + +/************************************************************************ + * Rebuild disk array. + ************************************************************************/ +u8 IT8212Rebuild(uioctl_t * pioc) +{ + u8 rebuildDirection; + u8 statusByte = 0; + PRAID_REBUILD_INFO apRebuildInfo = (PRAID_REBUILD_INFO) pioc->data; + PITE_ADAPTER pAdap; + PChannel pChan; + + dprintk("IT8212Rebuild enter\n"); + rebuildDirection = + (apRebuildInfo->Resume << 4) | (apRebuildInfo-> + DestDisk << 2) | apRebuildInfo-> + SrcDisk; + apRebuildInfo->Status = 0xFF; + pAdap = ite_adapters[0]; + printk("IT8212Rebuild: diskArrayId=%d\n", apRebuildInfo->DiskArrayId); + if (apRebuildInfo->DiskArrayId < 2) + pChan = &pAdap->IDEChannel[0]; + else + pChan = &pAdap->IDEChannel[1]; + + /* + * Selcet device. + */ + outb((u8) ((apRebuildInfo->DiskArrayId & 0x1) << 4 | 0xA0), + pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Wait for device ready (not BUSY and not DRQ). + */ + WaitForDeviceReady(pChan, statusByte); + if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) + || (statusByte == 0)) { + apRebuildInfo->Status = REBUILD_ERR_DISK_BUSY; + printk("IT8212Rebuild: disk[%d] not ready. status=0x%X\n", + apRebuildInfo->DiskArrayId, statusByte); + return SRB_STATUS_BUSY; + } + + /* + * Disable interrupt to avoid the unexpected interrupt. + */ + outb(IDE_DC_DISABLE_INTERRUPTS, pChan->io_ports[IDE_CONTROL_OFFSET]); + + /* + * Give a direction. + */ + outb(rebuildDirection, pChan->io_ports[IDE_FEATURE_OFFSET]); + + /* + * Issue a REBUILD commmand. + */ + outb(IDE_COMMAND_REBUILD, pChan->io_ports[IDE_COMMAND_OFFSET]); + + /* + * Check for errors. + */ + WaitForCommandComplete(pChan, statusByte); + + /* + * Reenable interrupt after command complete. + */ + outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]); + if (statusByte != IDE_STATUS_IDLE) { + if (statusByte & IDE_STATUS_ERROR) { + apRebuildInfo->Status = + inb(pChan->io_ports[IDE_NSECTOR_OFFSET]); + printk("IT8212Rebuild: rebuild error. reason=0x%X\n", + apRebuildInfo->Status); + } + return apRebuildInfo->Status; + } + dprintk("IT8212Rebuild exit\n"); + return SRB_STATUS_PENDING; +} /* end IT8212Rebuild */ + +/************************************************************************ + * Switch to DMA mode if necessary. + * + * offset 0x50 = PCI Mode Control Register + * + * Bit 0 = PCI Mode Select (1=firmware mode, 0=transparent mode) + * Bit 1 = Primary Channel IDE Clock Frequency Select (1=50, 0=66) + * Bit 2 = Secondary Channel IDE Clock Freq Select (1=50, 0=66) + * Bit 3 = Primary Channel Dev 0 Transfer Mode (1=MultiWord DMA, 0=Ultra DMA) + * Bit 4 = Primary Channel Dev 1 Transfer Mode (1=MultiWord DMA, 0=Ultra DMA) + * Bit 5 = Secondary Channel Dev 0 Transfer Mode (1=MultiWord DMA, 0=Ultra DMA) + * Bit 6 = Secondary Channel Dev 1 Transfer Mode (1=MultiWord DMA, 0=Ultra DMA) + * Bit 7 = PCI Mode Reset + ************************************************************************/ +static void IT8212SwitchDmaMode(PChannel pChan, u8 DeviceId) +{ + u8 pciControl; + u8 channel; + u8 device; + u8 configByte = 0; + u8 RevisionID; + struct pci_dev *pPciDev = pChan->pPciDev; + + /* + * These tables are for performance issue. Better formance than lots + * of "Shifts". + */ + static const u8 dmaModeV10[4] = { 0x18, 0x18, 0x60, 0x60 }; + static const u8 udmaModeV10[4] = { 0xE7, 0xE7, 0x9F, 0x9F }; + static const u8 ideClock[4] = { 0xFD, 0xFD, 0xFB, 0xFB }; + + /* + * channel --> 0-1; device --> 0-1; DeviceId --> 0-3; + */ + channel = DeviceId >> 1; + device = DeviceId & 1; + + /* + * Do nothing if the mode switch is unnecessary. + */ + if (!pChan->DoSwitch || pChan->ActiveDevice == DeviceId) { + dprintk("IT8212SwitchDmaMode: do not need to switch mode!\n"); + return; + } + printk("IT8212SwitchDmaMode: switch DMA mode for dev (%x)\n", DeviceId); + pci_read_config_byte(pPciDev, 0x50, &pciControl); + pci_read_config_byte(pPciDev, 0x08, &RevisionID); + + /* + * Running on MULTIWORD_DMA mode. + */ + if (pChan->DmaType[device] == USE_MULTIWORD_DMA) { + + /* + * Switch to DMA mode. + */ + if (RevisionID == 0x10) + configByte = pciControl | dmaModeV10[DeviceId]; + pci_write_config_byte(pPciDev, 0x50, configByte); + } else { + /* + * Running on ULTRA DMA mode. + */ + + /* + * Select UDMA mode. + */ + configByte = pciControl; + if (RevisionID == 0x10) + configByte &= udmaModeV10[DeviceId]; + + /* + * Select IDE clock. + */ + configByte = (configByte & ideClock[DeviceId]) | + (pChan->IdeClock[device] << (channel + 1)); + pci_write_config_byte(pPciDev, 0x50, configByte); + + /* + * Set UDMA timing. + * + * offset 0x56 = PCI Mode Primary Device 0 Ultra DMA Timing Registers + * offset 0x57 = PCI Mode Primary Device 1 Ultra DMA Timing Registers + * offset 0x5A = PCI Mode Secondary Device 0 Ultra DMA Timing Registers + * offset 0x5B = PCI Mode Secondary Device 1 Ultra DMA Timing Registers + */ + if (RevisionID == 0x10) { + configByte = pChan->UdmaTiming[device]; + pci_write_config_byte(pPciDev, + (u8) (0x56 + (channel * 4)), + configByte); + pci_write_config_byte(pPciDev, + (u8) (0x56 + (channel * 4) + 1), + configByte); + } + + /* + * Set PIO/DMA timing (Becasuse maybe the IDE clock is changed.) + */ + configByte = pChan->PioDmaTiming[pChan->IdeClock[device]]; + pci_write_config_byte(pPciDev, (u8) (0x54 + (channel * 4)), + configByte); + } + + /* + * Record the Active device on this channel + */ + pChan->ActiveDevice = device; +} /* end IT8212SwitchDmaMode */ + +/************************************************************************ + * IT8212 read/write routine. + ************************************************************************/ +static u32 IT8212ReadWrite(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) +{ + u8 statusByte = 0; + u32 startingSector; + u32 sectorNumber; + u32 capacity; + PITE_ADAPTER pAdap; + + if (Srb->TargetId >= 4) { + pAdap = ite_adapters[1]; + if (Srb->TargetId < 6) + pChan = &pAdap->IDEChannel[0]; + + else + pChan = &pAdap->IDEChannel[1]; + } else { + pAdap = ite_adapters[0]; + if (Srb->TargetId < 2) + pChan = &pAdap->IDEChannel[0]; + + else + pChan = &pAdap->IDEChannel[1]; + } + + /* + * Return error if overrun. + */ + startingSector = ((PCDB) Srb->Cdb)->CDB10.LogicalBlockByte3 | + ((PCDB) Srb->Cdb)->CDB10.LogicalBlockByte2 << 8 | + ((PCDB) Srb->Cdb)->CDB10.LogicalBlockByte1 << 16 | + ((PCDB) Srb->Cdb)->CDB10.LogicalBlockByte0 << 24; + sectorNumber = + (unsigned short)((Srb->DataTransferLength + 0x1FF) / 0x200); + capacity = + pChan->IdentifyData[Srb->TargetId & 0x1].UserAddressableSectors; + if (capacity == 0x0FFFFFFF) { + capacity = + pChan->IdentifyData[Srb->TargetId & 0x1].Capacity_48bit_LOW; + } + if ((startingSector + sectorNumber - 1) > capacity) { + printk("IT8212ReadWrite: disk[%d] over disk size.\n", + Srb->TargetId); + printk + ("capacity: %d. starting sector: %d. sector number: %d\n", + capacity, startingSector, sectorNumber); + return SRB_STATUS_ERROR; + } + + /* + * Select device. + */ + outb((u8) ((Srb->TargetId & 0x1) << 4 | 0xA0), + pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Wait for device ready (Not Busy and Not DRQ). + */ + WaitForDeviceReady(pChan, statusByte); + if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) + || (statusByte == 0)) { + printk("IT8212ReadWrite: disk[%d] not ready. status=0x%x\n", + Srb->TargetId, statusByte); + return SRB_STATUS_BUSY; + } + + /* + * First, switch to DMA or UDMA mode if running on bypass mode. + */ + if (pAdap->bypass_mode) + IT8212SwitchDmaMode(pChan, Srb->TargetId); + + /* + * Check the SCATTER/GATHER count. The upper will give the different + * memory address depend on whether use_sg is used or not. + */ + if (Srb->UseSg == 0) + IdeBuildDmaTable(pChan, Srb); + else + IdeBuildDmaSgTable(pChan, Srb); + + /* + * Start transfer the data. + */ + IdeStartTransfer(pChan, Srb, startingSector, sectorNumber); + + /* + * Wait for interrupt. + */ + return SRB_STATUS_PENDING; +} /* end IT8212ReadWrite */ + +#if 0 +/************************************************************************ + * Setup the transfer mode. + ************************************************************************/ +static void IT8212SetTransferMode(PChannel pChan, u32 DiskId, u8 TransferMode, + u8 ModeNumber) +{ + u8 statusByte = 0; + + /* + * Select device. + */ + outb((u8) ((DiskId & 0x1) << 4 | 0xA0), + pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Wait for device ready (Not Busy and Not DRQ). + */ + WaitForDeviceReady(pChan, statusByte); + if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ)) { + printk("IT8212SetTransferMode: disk[%d] not ready. " + "status=0x%x\n", DiskId, statusByte); + return; + } + + /* + * Feature number ==> 03 + * + * Mode contained in Sector Count Register. + * + * Bits(7:3) Bits(2:0) Mode + * + * 00000 000 PIO default mode + * 00000 001 PIO default mode, disable IORDY + * 00001 mode PIO flow control transfer mode + * 00010 mode Single Word DMA mode + * 00100 mode Multi-word DMA mode + * 01000 mode Ultra DMA mode + */ + TransferMode |= ModeNumber; + outb(0x03, pChan->io_ports[IDE_FEATURE_OFFSET]); + outb(TransferMode, pChan->io_ports[IDE_NSECTOR_OFFSET]); + outb(0, pChan->io_ports[IDE_HCYL_OFFSET]); + outb(0, pChan->io_ports[IDE_MIDCYL_OFFSET]); + outb(IDE_COMMAND_SET_FEATURE, pChan->io_ports[IDE_COMMAND_OFFSET]); + + /* + * Check error. + */ + WaitForBaseCommandComplete(pChan, statusByte); + if ((statusByte != IDE_STATUS_IDLE) && (statusByte != 0)) { + printk("IT8212SetTransferMode: disk[%d]", DiskId); + printk("return unexpected status after issue command. 0x%x\n", + statusByte); + } +} /* end IT8212SetTransferMode */ +#endif + +#if 0 +/************************************************************************ + * Set the best transfer mode for device. + ************************************************************************/ +static void IT8212SetBestTransferMode(PITE_ADAPTER pAdap, PChannel pChan, + u8 channel) +{ + u8 i; + u8 k; + u8 transferMode; + u8 modeNumber; + u8 pciControl; + u8 device; + u8 configByte; + u8 cableStatus[2] = { + CABLE_40_PIN, CABLE_40_PIN + }; + u8 RevisionID; + struct pci_dev *pPciDev = pChan->pPciDev; + PIDENTIFY_DATA2 ideIdentifyData; + + /* + * UDMA timing table for 66MHz clock. + * UDMA timing table for 50MHz clock. + * Best of IDE clock in this mode. + */ + static const u8 udmaTiming[3][7] = { + {0x44, 0x42, 0x31, 0x21, 0x11, 0x22, 0x11}, + {0x33, 0x31, 0x21, 0x21, 0x11, 0x11, 0x11}, + {IDE_CLOCK_66, IDE_CLOCK_50, IDE_CLOCK_66, IDE_CLOCK_66, + IDE_CLOCK_66, IDE_CLOCK_50, + IDE_CLOCK_66} + }; + + /* + * DMA timing table for 66 MHz clock. + * DMA timing table for 50 MHz clock. + */ + static const u8 dmaTiming[2][3] = + { {0x88, 0x32, 0x31}, {0x66, 0x22, 0x21} + }; + + /* + * PIO timing table for 66 MHz clock. + * PIO timing table for 50 MHz clock. + */ + static const u8 pioTiming[2][5] = + { {0xAA, 0xA3, 0xA1, 0x33, 0x31}, {0x88, 0x82, 0x81, 0x32, 0x21} + }; + u8 pio_dma_timing[2][2][4] = { + {{ + 0, 0, 0, 0}, { + 0, 0, 0, 0}}, {{ + 0, 0, 0, 0}, { + 0, 0, 0, 0}} + }; + + /* + * These tables are for performance issue. Better formance than lots + * of "Shifts". + */ + static const u8 udmaModeV10[4] = { 0xE7, 0xE7, 0x9F, 0x9F }; + static const u8 dmaMode[4] = { 0x08, 0x10, 0x20, 0x40 }; + static const u8 udmaMode[4] = { 0xF7, 0xEF, 0xDF, 0xBF }; + static const u8 ideClock[4] = { 0xFD, 0xFD, 0xFB, 0xFB }; + + /* + * 2003/07/24 + * If running on Firmware mode, get cable status from it. + */ + for (i = 0; i < 2; i++) { + + /* + * The dafault of cable status is in PCI configuration 0x40. + */ + cableStatus[i] = pChan->Cable80[i]; + + /* + * channel -->0 to 1. + * device -->0 or 1. + */ + pChan->UseDma[i] = TRUE; + device = i & 1; + if (!(pChan->DeviceFlags[i] & DFLAGS_DEVICE_PRESENT) || + (pChan->DeviceFlags[i] & DFLAGS_CONFIG_CHANGED)) { + pio_dma_timing[0][channel][device] = + pio_dma_timing[0][channel][device + 2] = 0; + pio_dma_timing[1][channel][device] = + pio_dma_timing[1][channel][device + 2] = 0; + continue; + } + + /* + * Set PIO Mode. + */ + ideIdentifyData = &pChan->IdentifyData[i]; + if ((!(ideIdentifyData->ValidFieldIndicator & 0x02)) + || (ideIdentifyData->AdvancedPIOModes == 0)) { + transferMode = PIO_FLOW_CONTROL; + modeNumber = 2; + } else { + transferMode = PIO_FLOW_CONTROL; + modeNumber = + RaidGetHighestBit((u8) ideIdentifyData-> + AdvancedPIOModes) + 3; + } + IT8212SetTransferMode(pChan, i, transferMode, modeNumber); + + /* + * Record the PIO timing for later use.(0 to 4) + */ + pio_dma_timing[0][channel][device] = pioTiming[0][modeNumber]; + pio_dma_timing[1][channel][device] = pioTiming[1][modeNumber]; + + /* + * Get the best transfer mode (maybe Ultra DMA or Multi-Word + * DMA). + */ + ideIdentifyData = &pChan->IdentifyData[i]; + if ((!(ideIdentifyData->ValidFieldIndicator & 0x04)) + || (ideIdentifyData->UltraDMASupport == 0)) { + + /* + * UltraDMA is not valid. + */ + transferMode = MULTIWORD_DMA; + modeNumber = + RaidGetHighestBit(ideIdentifyData-> + MultiWordDMASupport); + printk("The best transfer mode of Device[%d] is " + "DMA-%d\n", i, modeNumber); + } else { + transferMode = ULTRA_DMA; + modeNumber = + RaidGetHighestBit(ideIdentifyData->UltraDMASupport); + printk("The best transfer mode of Device[%d] is " + "Ultra-%d\n", i, modeNumber); + + /* + * If this is 40-pin cable. Limit to Ultra DMA mode 2. + */ +#if (0) + if ((cableStatus[i] == CABLE_40_PIN) + && (modeNumber > 2)) { + printk("Reduce trans mode of Device[%d] to " + "Ultra-2 for cable issue.\n", i); + modeNumber = 0x02; + } +#endif + } + IT8212SetTransferMode(pChan, i, transferMode, modeNumber); + + /* + * If running on ByPass mode, driver must take the + * responsibility to set the PIO/DMA/UDMA timing. + */ + if (pAdap->bypass_mode) { + pci_read_config_byte(pPciDev, 0x50, &pciControl); + pci_read_config_byte(pPciDev, 0x08, &RevisionID); + if (transferMode == ULTRA_DMA) { + + /* + * Set this channel to UDMA mode (not only the + * device). + */ + if (RevisionID == 0x10) { + configByte = + pciControl & udmaModeV10[i + + channel * + 2]; + } else { + configByte = + pciControl & udmaMode[i + + channel * 2]; + } + + /* + * Select IDE clock (50MHz or 66MHz). + */ + configByte &= ideClock[i + channel * 2]; + configByte |= + (udmaTiming[2][modeNumber] << + (channel + 1)); + pci_write_config_byte(pPciDev, 0x50, + configByte); + + /* + * Set UDMA timing. + */ + configByte = + udmaTiming[udmaTiming[2][modeNumber]] + [modeNumber]; + if (modeNumber == 5 || modeNumber == 6) { + + /* + * Enable UDMA mode 5/6 + */ + configByte |= UDMA_MODE_5_6; + } + + /* + * Bug Bug. Fill these two fields into the same + * value. + */ + if (RevisionID == 0x10) { + pci_write_config_byte(pPciDev, + (u8) (0x56 + + (channel * + 4)), + configByte); + pci_write_config_byte(pPciDev, + (u8) (0x56 + + (channel * + 4) + 1), + configByte); + } else { + pci_write_config_byte(pPciDev, + (u8) (0x56 + + (channel * + 4) + + device), + configByte); + } + + /* + * Record the best UDMA mode for this device. + */ + pChan->DmaType[i] = ULTRA_DMA; + pChan->IdeClock[i] = udmaTiming[2][modeNumber]; + pChan->UdmaTiming[i] = configByte; + } else if (transferMode == MULTIWORD_DMA) { + + /* + * If an ATAPI device with DMA mode, force it + * to run in PIO mode. + */ + if (RevisionID == 0x10 + && pChan-> + DeviceFlags[i] & DFLAGS_ATAPI_DEVICE) { + pChan->UseDma[i] = FALSE; + } else { + + /* + * Set this device to DMA mode. + */ + configByte = + pciControl | dmaMode[i + + channel * 2]; + pci_write_config_byte(pPciDev, 0x50, + configByte); + + /* + * Record DMA timing (for later use). + */ + pio_dma_timing[0][channel][device + + 2] = + dmaTiming[0][modeNumber]; + pio_dma_timing[1][channel][device + + 2] = + dmaTiming[1][modeNumber]; + } + pChan->DmaType[i] = USE_MULTIWORD_DMA; + } + pChan->ActiveDevice = device; + } + } + + /* + * Because each channel owns one PIO/DMA timimg register only, so we + * must set the timing to the slowest one to fit all. Really stupid + * H/W! :( + */ + if (pAdap->bypass_mode) { + + /* + * Loop for the two IDE clocks (50 MHz and 66 MHz). + */ + for (i = 0; i < 2; i++) { + configByte = 0; + for (k = 0; k < 4; k++) { + + /* + * High part. + */ + if ((pio_dma_timing[i][channel][k] & 0xF0) > + (configByte & 0xF0)) { + configByte = + (configByte & 0xF) | + (pio_dma_timing[i][channel][k] & + 0xF0); + } + + /* + * Low part. + */ + if ((pio_dma_timing[i][channel][k] & 0xF) > + (configByte & 0xF)) { + configByte = + (configByte & 0xF0) | + (pio_dma_timing[i][channel][k] & + 0xF); + } + } + + /* + * Record the PIO/DMA timing for this channel. + */ + pChan->PioDmaTiming[i] = configByte; + } + + /* + * Set PIO/DMA timing register for each channel. + */ + configByte = + pChan->PioDmaTiming[(pciControl >> (channel + 1)) & 1]; + if (configByte != 0) + pci_write_config_byte(pPciDev, + (u8) (0x54 + (channel * 4)), + configByte); + + /* + * Check shall we do switch between the two devices + */ + for (i = 0; i < 2; i++) { + pChan->DoSwitch = TRUE; + + /* + * Master is not present + */ + if (!(pChan->DeviceFlags[i] & DFLAGS_DEVICE_PRESENT) + || (pChan->DeviceFlags[i] & DFLAGS_CONFIG_CHANGED)) { + printk("Channel %x: master is not present. No " + "switch mode.\n", channel); + pChan->DoSwitch = FALSE; + continue; + } + + /* + * Slave is not present + */ + if (!(pChan->DeviceFlags[i + 1] & DFLAGS_DEVICE_PRESENT) + || (pChan-> + DeviceFlags[i + 1] & DFLAGS_CONFIG_CHANGED)) { + printk("Channel %x: slave is not present. No " + "switch mode.\n", channel); + pChan->DoSwitch = FALSE; + continue; + } + + /* + * If both devices are running on DMA mode, no switch. + */ + if (pChan->DmaType[i] == USE_MULTIWORD_DMA + && pChan->DmaType[i + 1] == USE_MULTIWORD_DMA) { + printk("Channel %x: run on DMA mode only. No " + "switch mode.\n", channel); + pChan->DoSwitch = FALSE; + continue; + } + + /* + * No switch if the two devices are running on the same mode. + */ + if ((pChan->DmaType[i] == pChan->DmaType[i + 1]) + && (pChan->UdmaTiming[i] == + pChan->UdmaTiming[i + 1]) + && (pChan->IdeClock[i] == pChan->IdeClock[i + 1])) { + printk("Channel %x: two dev run on the same " + "mode. No switch mode.\n", channel); + pChan->DoSwitch = FALSE; + continue; + } + printk("Channel %x: switch mode if needed.\n", channel); + } + } +} /* end IT8212SetBestTransferMode */ +#endif + +#if 0 +/************************************************************************ + * Initialize bypass(transparent) mode if BIOS is not ready. + ************************************************************************/ +static u8 IT8212InitBypassMode(struct pci_dev *pPciDev) +{ + + /* + * Reset local CPU, and set BIOS not ready. + */ + pci_write_config_byte(pPciDev, 0x5E, 0x01); + + /* + * Set to bypass mode, and reset PCI bus. + */ + pci_write_config_byte(pPciDev, 0x50, 0x00); + pci_write_config_word(pPciDev, 0x4, 0x0047); + pci_write_config_word(pPciDev, 0x40, 0xA0F3); + pci_write_config_dword(pPciDev, 0x4C, 0x02040204); + pci_write_config_byte(pPciDev, 0x42, 0x36); + pci_write_config_byte(pPciDev, 0x0D, 0x00); + return TRUE; +} /* end IT8212InitBypassMode */ +#endif + +/************************************************************************ + * This is the interrupt service routine for ATAPI IDE miniport driver. + * TURE if expecting an interrupt. + ************************************************************************/ +static u8 IT8212Interrupt(PChannel pChan, u8 bypass_mode) +{ + u8 statusByte; + u8 bmstatus; + u32 i; + unsigned long bmbase; + PSCSI_REQUEST_BLOCK Srb; + + bmstatus = 0; + bmbase = pChan->dma_base; + Srb = pChan->CurrentSrb; + if (Srb == 0 || pChan->ExpectingInterrupt == 0) { + dprintk("IT8212Interrupt: suspicious interrupt!\n"); + + /* + * Clear interrupt by reading status register. + */ + outb((u8) 0xA0, pChan->io_ports[IDE_SELECT_OFFSET]); + GetBaseStatus(pChan, statusByte); + outb((u8) 0xB0, pChan->io_ports[IDE_SELECT_OFFSET]); + GetBaseStatus(pChan, statusByte); + outb(bmbase + 2, (u8) (inb(bmbase + 2) | BM_STAT_FLG_INT)); + return FALSE; + } + + /* + * To handle share IRQ condition. If the interrupt is not ours, just + * return FALSE. + */ + bmstatus = inb(bmbase + 2); + if ((bmstatus & BM_STAT_FLG_INT) == 0) { + dprintk("IT8212Interrupt: suspicious interrupt (int bit is not " + "on)\n"); + return FALSE; + } + + /* + * Bug Fixed: All PIO access are blocked during bus master operation, so + * stop bus master operation before we try to access IDE registers. + */ + if (bypass_mode) + outb(bmbase, 0); + + /* + * Clear interrupt by reading status register. + */ + GetBaseStatus(pChan, statusByte); + outb(bmbase + 2, (u8) (bmstatus | BM_STAT_FLG_INT)); + + /* + * Handle ATAPI interrupt. + */ + if (pChan->DeviceFlags[Srb->TargetId & 1] & DFLAGS_ATAPI_DEVICE) + return AtapiInterrupt(pChan); + pChan->ExpectingInterrupt = FALSE; + if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ)) { + + /* + * Ensure BUSY and DRQ is non-asserted. + */ + for (i = 0; i < 100; i++) { + GetBaseStatus(pChan, statusByte); + if (!(statusByte & IDE_STATUS_BUSY) + && !(statusByte & IDE_STATUS_DRQ)) { + break; + } + mdelay(5); + } + if (i == 100) { + printk("IT8212Interrupt: disk[%x] return busy or drq " + "status. status = 0x%x\n", + Srb->TargetId, statusByte); + return FALSE; + } + } + if (statusByte & IDE_STATUS_ERROR) { + + /* + * Stop bus master operation. + */ + outb(bmbase, 0); + printk("IT8212Interrupt: error!\n"); + + /* + * Map error to specific SRB status and handle request sense. + */ + Srb->SrbStatus = MapError(pChan, Srb); + } else { + Srb->SrbStatus = SRB_STATUS_SUCCESS; + } + pChan->CurrentSrb = NULL; + TaskDone(pChan, Srb); + return TRUE; +} /* end IT8212Interrupt */ + +/************************************************************************ + * This is the interrupt service routine for ATAPI IDE miniport driver. + * TRUE if expecting an interrupt. Remember the ATAPI io registers are + * different from IDE io registers and this is for each channel not for + * entire controller. + ************************************************************************/ +static u8 AtapiInterrupt(PChannel pChan) +{ + u32 wordCount; + u32 wordsThisInterrupt; + u32 status; + u32 i; + u8 statusByte; + u8 interruptReason; + u8 target_id; + PSCSI_REQUEST_BLOCK srb; + PITE_ADAPTER pAdap; + + wordCount = 0; + wordsThisInterrupt = 256; + srb = pChan->CurrentSrb; + target_id = srb->TargetId; + if (target_id >= 4) { + pAdap = ite_adapters[1]; + if (target_id < 6) + pChan = &pAdap->IDEChannel[0]; + + else + pChan = &pAdap->IDEChannel[1]; + } else { + pAdap = ite_adapters[0]; + if (target_id < 2) + pChan = &pAdap->IDEChannel[0]; + + else + pChan = &pAdap->IDEChannel[1]; + } + + /* + * Clear interrupt by reading status. + */ + GetBaseStatus(pChan, statusByte); + dprintk("AtapiInterrupt: entered with status (%x)\n", statusByte); + if (statusByte & IDE_STATUS_BUSY) { + + /* + * Ensure BUSY is non-asserted. + */ + for (i = 0; i < 10; i++) { + GetBaseStatus(pChan, statusByte); + if (!(statusByte & IDE_STATUS_BUSY)) { + break; + } + mdelay(5); + } + if (i == 10) { + printk("AtapiInterrupt: BUSY on entry. Status %x\n", + statusByte); + return FALSE; + } + } + + /* + * Check for error conditions. + */ + if (statusByte & IDE_STATUS_ERROR) { + if (srb->Cdb[0] != SCSIOP_REQUEST_SENSE) { + + /* + * Fail this request. + */ + status = SRB_STATUS_ERROR; + goto CompleteRequest; + } + } + + /* + * Check reason for this interrupt. + */ + interruptReason = (inb(pChan->io_ports[ATAPI_INTREASON_OFFSET]) & 0x3); + wordsThisInterrupt = 256; + if (interruptReason == 0x1 && (statusByte & IDE_STATUS_DRQ)) { + + /* + * Write the packet. + */ + printk("AtapiInterrupt: writing Atapi packet.\n"); + + /* + * Send CDB to device. + */ + WriteBuffer(pChan, (unsigned short *)srb->Cdb, 6); + return TRUE; + } else if (interruptReason == 0x0 && (statusByte & IDE_STATUS_DRQ)) { + + /* + * Write the data. + */ + + /* + * Pick up bytes to transfer and convert to words. + */ + wordCount = inb(pChan->io_ports[ATAPI_LCYL_OFFSET]); + wordCount |= inb(pChan->io_ports[ATAPI_HCYL_OFFSET]) << 8; + + /* + * Covert bytes to words. + */ + wordCount >>= 1; + if (wordCount != pChan->WordsLeft) { + printk("AtapiInterrupt: %d words requested; %d words " + "xferred\n", pChan->WordsLeft, wordCount); + } + + /* + * Verify this makes sense. + */ + if (wordCount > pChan->WordsLeft) + wordCount = pChan->WordsLeft; + + /* + * Ensure that this is a write command. + */ + if (srb->SrbFlags & SRB_FLAGS_DATA_OUT) { + dprintk("AtapiInterrupt: write interrupt\n"); + WaitOnBusy(pChan, statusByte); + WriteBuffer(pChan, pChan->DataBuffer, wordCount); + +#if (0) + /* + * Translate ATAPI data back to SCSI data if needed + * (don't convert if the original command is + * SCSIOP_MODE_SELECT10) + */ + if (srb->Cdb[0] == ATAPI_MODE_SELECT + && pchan->ConvertCdb) { + Atapi2Scsi(pChan, srb, + (char *)pChan->DataBuffer, + wordCount << 1); + } +#endif + } else { + printk("AtapiInterrupt: int reason %x, but srb is for " + "a write %p.\n", interruptReason, srb); + + /* + * Fail this request. + */ + status = SRB_STATUS_ERROR; + goto CompleteRequest; + } + + /* + * Advance data buffer pointer and bytes left. + */ + pChan->DataBuffer += wordCount; + pChan->WordsLeft -= wordCount; + return TRUE; + } else if (interruptReason == 0x2 && (statusByte & IDE_STATUS_DRQ)) { + + /* + * Pick up bytes to transfer and convert to words. + */ + wordCount = inb(pChan->io_ports[ATAPI_LCYL_OFFSET]); + wordCount |= inb(pChan->io_ports[ATAPI_HCYL_OFFSET]) << 8; + + /* + * Covert bytes to words. + */ + wordCount >>= 1; + if (wordCount != pChan->WordsLeft) { + printk("AtapiInterrupt: %d words requested; %d words " + "xferred\n", pChan->WordsLeft, wordCount); + } + + /* + * Verify this makes sense. + */ + if (wordCount > pChan->WordsLeft) + wordCount = pChan->WordsLeft; + + /* + * Ensure that this is a read command. + */ + if (srb->SrbFlags & SRB_FLAGS_DATA_IN) { + dprintk("AtapiInterrupt: read interrupt\n"); + WaitOnBusy(pChan, statusByte); + ReadBuffer(pChan, pChan->DataBuffer, wordCount); + + /* + * You should typically set the ANSI-approved Version + * field, in the INQUIRY response, to at least 2. + */ + if (srb->Cdb[0] == SCSIOP_INQUIRY) { + + /* + * Maybe it's not necessary in Linux driver. + */ + *((unsigned char *)pChan->DataBuffer + 2) = 2; + } + } else { + printk("AtapiInterrupt: int reason %x, but srb is for " + "a read %p.\n", interruptReason, srb); + + /* + * Fail this request. + */ + status = SRB_STATUS_ERROR; + goto CompleteRequest; + } + + /* + * Advance data buffer pointer and bytes left. + */ + pChan->DataBuffer += wordCount; + pChan->WordsLeft -= wordCount; + + /* + * Check for read command complete. + */ + if (pChan->WordsLeft == 0) { + + /* + * Work around to make many atapi devices return + * correct sector size of 2048. Also certain devices + * will have sector count == 0x00, check for that also. + */ + if ((srb->Cdb[0] == 0x25) && + ((pChan->IdentifyData[srb->TargetId & 1]. + GeneralConfiguration >> 8) & 0x1F) == 0x05) { + pChan->DataBuffer -= wordCount; + if (pChan->DataBuffer[0] == 0x00) { + *((u32 *) & (pChan->DataBuffer[0])) = + 0xFFFFFF7F; + } + *((u32 *) & (pChan->DataBuffer[2])) = + 0x00080000; + pChan->DataBuffer += wordCount; + } + } + return TRUE; + } else if (interruptReason == 0x3 && !(statusByte & IDE_STATUS_DRQ)) { + dprintk("AtapiInterrupt: command complete!\n"); + + /* + * Command complete. + */ + if (pChan->WordsLeft) + status = SRB_STATUS_DATA_OVERRUN; + else + status = SRB_STATUS_SUCCESS; +CompleteRequest: + if (status == SRB_STATUS_ERROR) { + + /* + * Map error to specific SRB status and handle request + * sense. + */ + printk("AtapiInterrupt error\n"); + status = MapError(pChan, srb); + + /* + * Try to recover it.... 2003/02/27 + */ + pChan->RDP = FALSE; + } else { + + /* + * Wait for busy to drop. + */ + for (i = 0; i < 30; i++) { + GetStatus(pChan, statusByte); + if (!(statusByte & IDE_STATUS_BUSY)) { + break; + } + udelay(500); + } + if (i == 30) { + + /* + * Reset the controller. + */ + printk("AtapiInterrupt: resetting due to BSY " + "still up - %x.\n", statusByte); + AtapiResetController(pAdap, pChan); + return TRUE; + } + + /* + * Check to see if DRQ is still up. + */ + if (statusByte & IDE_STATUS_DRQ) { + for (i = 0; i < 500; i++) { + GetStatus(pChan, statusByte); + if (!(statusByte & IDE_STATUS_DRQ)) { + break; + } + udelay(100); + } + if (i == 500) { + + /* + * Reset the controller. + */ + printk("AtapiInterrupt: resetting due " + "to DRQ still up - %x\n", + statusByte); + AtapiResetController(pAdap, pChan); + return TRUE; + } + } + } + + /* + * Clear interrupt expecting flag. + */ + pChan->ExpectingInterrupt = FALSE; + + /* + * Sanity check that there is a current request. + */ + if (srb != NULL) { + + /* + * Set status in SRB. + */ + srb->SrbStatus = (u8) status; + + /* + * Check for underflow. + */ + if (pChan->WordsLeft) { + + /* + * Subtract out residual words and update if + * filemark hit, setmark hit , end of data, + * end of media... + */ + if (!(pChan->DeviceFlags[srb->TargetId & 1] & + DFLAGS_TAPE_DEVICE)) { + if (status == SRB_STATUS_DATA_OVERRUN) { + srb->DataTransferLength -= + pChan->WordsLeft * 2; + } else { + srb->DataTransferLength = 0; + } + } else { + srb->DataTransferLength -= + pChan->WordsLeft * 2; + } + } + GetBaseStatus(pChan, statusByte); + if (pChan->RDP && !(statusByte & IDE_STATUS_DSC)) { + printk("-@@-\n"); + } else { + + /* + * Clear current SRB. Indicate ready for next + * request. + */ + pChan->CurrentSrb = NULL; + TaskDone(pChan, srb); + } + } else { + printk("AtapiInterrupt: no SRB!\n"); + } + return TRUE; + } else { + + /* + * Unexpected int. + */ + printk("AtapiInterrupt: unexpected interrupt. intReason %x. " + "status %x.\n", interruptReason, statusByte); + return FALSE; + } + return TRUE; +} /* end AtapiInterrupt */ + +/************************************************************************ + * IRQ handler. + ************************************************************************/ +static irqreturn_t Irq_Handler(int irq, void *dev_id, struct pt_regs *regs) +{ + int handled = 0; + u8 i; + u8 j; + unsigned long flags; + PITE_ADAPTER pAdap; + + spin_lock_irqsave(&io_request_lock, flags); + + /* + * Scan for interrupt to process. + */ + for (i = 0; i < NumAdapters; i++) { + pAdap = ite_adapters[i]; + if (pAdap->irq != irq) + continue; + handled = 1; + for (j = 0; j < pAdap->num_channels; j++) { + IT8212Interrupt(&pAdap->IDEChannel[j], + pAdap->bypass_mode); + } + } + spin_unlock_irqrestore(&io_request_lock, flags); + return IRQ_RETVAL(handled); +} /* end Irq_Handler */ + +/************************************************************************ + * This routine handles IDE Verify. + ************************************************************************/ +static u8 IdeVerify(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) +{ + u8 drvSelect; + u8 statusByte = 0; + u32 startingSector; + u32 sectors; + u32 endSector; + u32 sectorCount; + + /* + * Select device + */ + outb((u8) ((Srb->TargetId & 0x1) << 4 | 0xA0), + pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Wait for device ready (Not BUSY and Not DRQ) + */ + WaitForDeviceReady(pChan, statusByte); + if ((statusByte & IDE_STATUS_BUSY) || (statusByte & IDE_STATUS_DRQ) + || (statusByte == 0)) { + printk("IdeVerify: disk[%d] not ready. status=0x%x\n", + Srb->TargetId, statusByte); + return SRB_STATUS_BUSY; + } + + /* + * Get the starting sector number from CDB. + */ + startingSector = ((PCDB) Srb->Cdb)->CDB10.LogicalBlockByte3 | + ((PCDB) Srb->Cdb)->CDB10.LogicalBlockByte2 << 8 | + ((PCDB) Srb->Cdb)->CDB10.LogicalBlockByte1 << 16 | + ((PCDB) Srb->Cdb)->CDB10.LogicalBlockByte0 << 24; + sectorCount = + (u16) (((PCDB) Srb->Cdb)->CDB10. + TransferBlocksMsb << 8 | ((PCDB) Srb->Cdb)->CDB10. + TransferBlocksLsb); + endSector = startingSector + sectorCount; + + /* + * Drive has these number sectors. + * + * 48-bit addressing. + */ + if (endSector > 0x0FFFFFFF) { + sectors = + pChan->IdentifyData[Srb->TargetId & 0x01]. + Capacity_48bit_LOW; + printk("IdeVerify (48-bit): starting sector %d, Ending " + "sector %d\n", startingSector, endSector); + if (endSector > sectors) { + + /* + * Too big, round down. + */ + printk + ("IdeVerify: truncating request to %x blocks\n", + sectors - startingSector - 1); + outb((u8) ((sectors - startingSector - 1) >> 8), + pChan->io_ports[IDE_NSECTOR_OFFSET]); + outb((u8) (sectors - startingSector - 1), + pChan->io_ports[IDE_NSECTOR_OFFSET]); + } else { + + /* + * Set up sector count register. Round up to next block. + */ + if (sectorCount > 0xFFFF) { + sectorCount = (u16) 0xFFFF; + } + outb((u8) (sectorCount >> 8), + pChan->io_ports[IDE_NSECTOR_OFFSET]); + outb((u8) sectorCount, + pChan->io_ports[IDE_NSECTOR_OFFSET]); + } + + /* + * Indicate expecting an interrupt. + */ + pChan->ExpectingInterrupt = TRUE; + + /* + * Set up LBA address + */ + outb((u8) (startingSector >> 24), + pChan->io_ports[IDE_LOCYL_OFFSET]); + outb((u8) startingSector, pChan->io_ports[IDE_LOCYL_OFFSET]); + outb((u8) 0, pChan->io_ports[IDE_MIDCYL_OFFSET]); + outb((u8) (startingSector >> 8), + pChan->io_ports[IDE_MIDCYL_OFFSET]); + outb((u8) 0, pChan->io_ports[IDE_HCYL_OFFSET]); + outb((u8) (startingSector >> 16), + pChan->io_ports[IDE_HCYL_OFFSET]); + + /* + * Send verify command. + */ + outb(IDE_COMMAND_READ_VERIFY_EXT, + pChan->io_ports[IDE_COMMAND_OFFSET]); + } else { + /* + * 28-bit addressing + */ + sectors = pChan->IdentifyData[Srb->TargetId & 0x01]. + UserAddressableSectors; + printk("IdeVerify: starting sector %d, ending sector %d\n", + startingSector, endSector); + if (endSector > sectors) { + + /* + * Too big, round down. + */ + printk + ("IdeVerify: truncating request to %d blocks\n", + sectors - startingSector - 1); + outb((u8) (sectors - startingSector - 1), + pChan->io_ports[IDE_NSECTOR_OFFSET]); + } else { + + /* + * Set up sector count register. Round up to next block. + */ + if (sectorCount > 0xFF) + sectorCount = (u16) 0xFF; + outb((u8)sectorCount, + pChan->io_ports[IDE_NSECTOR_OFFSET]); + } + + /* + * Indicate expecting an interrupt. + */ + pChan->ExpectingInterrupt = TRUE; + + /* + * Set up LBA address + */ + outb((u8) startingSector, pChan->io_ports[IDE_LOCYL_OFFSET]); + outb((u8) (startingSector >> 8), + pChan->io_ports[IDE_MIDCYL_OFFSET]); + outb((u8) (startingSector >> 16), + pChan->io_ports[IDE_HCYL_OFFSET]); + + /* + * Select driver, set LBA mode, set LBA (27:27) + */ + drvSelect = (u8) (startingSector >> 24); + drvSelect = + drvSelect | (((u8) Srb->TargetId & 0x1) << 4) | 0xA0 | 0x40; + outb(drvSelect, pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Send verify command. + */ + outb(IDE_COMMAND_READ_VERIFY, + pChan->io_ports[IDE_COMMAND_OFFSET]); + } + + /* + * Wait for interrupt. + */ + return SRB_STATUS_PENDING; +} /* end IdeVerify */ + +/************************************************************************ + * This function is used to copy memory with overlapped destination and + * source. I guess ScsiPortMoveMemory cannot handle this well. Can it? + ************************************************************************/ +void +IT8212MoveMemory(unsigned char *DestAddr, unsigned char *SrcAddr, u32 ByteCount) +{ + long i; + + dprintk("IT8212MoveMemory: DestAddr=0x%p, SrcAddr=0x%p, " + "ByteCount=0x%x\n", DestAddr, SrcAddr, ByteCount); + if (DestAddr > SrcAddr) { + + /* + * If Destination Area is in the back of the Source Area, copy + * from the end of the requested area. + */ + for (i = (ByteCount - 1); i >= 0; i--) + *(DestAddr + i) = *(SrcAddr + i); + } else if (DestAddr < SrcAddr) { + + /* + * If Destination Area is in the front of the Source Area, copy + * from the begin of the requested area. + */ + for (i = 0; i < ByteCount; i++) + *(DestAddr + i) = *(SrcAddr + i); + } +} /* end IT8212MoveMemory */ + +/************************************************************************ + * Convert SCSI packet command to Atapi packet command. + ************************************************************************/ +static void Scsi2Atapi(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) +{ + + /* + * Change the cdb length. + */ + Srb->CdbLength = 12; + + /* + * Because the block descripter and the header translation, we must + * adjust the requested length. + */ + Srb->DataTransferLength -= 4; + + /* + * Record the original CDB for later restore. + */ + memcpy(pChan->TempCdb, Srb->Cdb, MAXIMUM_CDB_SIZE); + + /* + * Indicate that we have performed Scsi2Atapi function. And we must + * restore the CDB back once the command complete. + */ + pChan->ConvertCdb = TRUE; + switch (Srb->Cdb[0]) { + + /* + * Convert the command from SCSIOP_MODE_SENSE (0x1A) to + * SCSIOP_MODE_SENSE10 (0x5A). + */ + case SCSIOP_MODE_SENSE: + { + PSCSI_MODE_SENSE10 modeSense10 = + (PSCSI_MODE_SENSE10) Srb->Cdb; + PSCSI_MODE_SENSE6 modeSense6 = + (PSCSI_MODE_SENSE6) pChan->TempCdb; + + /* + * 1. Zero out the whole CDB. + */ + memset((unsigned char *)modeSense10, 0, + MAXIMUM_CDB_SIZE); + + /* + * 2. Fill in command code (SCSI_MODE_SENSE10). + */ + modeSense10->OperationCode = ATAPI_MODE_SENSE; + modeSense10->Dbd = modeSense6->Dbd; + modeSense10->PageCode = modeSense6->PageCode; + modeSense10->Pc = modeSense6->Pc; + modeSense10->SubpageCode = modeSense6->SubpageCode; + modeSense10->AllocationLengthLsb = + modeSense6->AllocationLength; + modeSense10->Control = modeSense6->Control; + + /* + * 3. Becasuse we will fake a block descripter (-8), and + * translate the header (+4), so the requested length + * should be modified. That is, -8+4=-4 bytes. + */ + modeSense10->AllocationLengthLsb -= 4; + break; + } + + /* + * Convert the command from SCSIOP_MODE_SELECT (0x15) to + * SCSIOP_MODE_SELECT10 (0x5A). + */ + case SCSIOP_MODE_SELECT: + { + u8 tempHeader[sizeof(PSCSI_MODE_PARAMETER_HEADER6)]; + u16 byteCount; + PSCSI_MODE_PARAMETER_HEADER10 header10 = + (PSCSI_MODE_PARAMETER_HEADER10) Srb->DataBuffer; + PSCSI_MODE_PARAMETER_HEADER6 header6 = + (PSCSI_MODE_PARAMETER_HEADER6) tempHeader; + PSCSI_MODE_SELECT10 modeSelect10 = + (PSCSI_MODE_SELECT10) Srb->Cdb; + PSCSI_MODE_SELECT6 modeSelect6 = + (PSCSI_MODE_SELECT6) pChan->TempCdb; + + /* + * First, convert the command block. + */ + + /* + * 1. Zero out the whole CDB. + */ + memset((unsigned char *)modeSelect10, 0, + MAXIMUM_CDB_SIZE); + + /* + * 2. Fill in command code (SCSI_MODE_SENSE10). + */ + modeSelect10->OperationCode = ATAPI_MODE_SELECT; + modeSelect10->SPBit = modeSelect6->SPBit; + modeSelect10->PFBit = modeSelect6->PFBit; + modeSelect10->ParameterListLengthLsb = + modeSelect6->ParameterListLength; + modeSelect10->Control = modeSelect6->Control; + + /* + * 3. Becasuse we will remove the block descripter (-8), + * and translate the header (+4), so the requested + * length should be modified. That is, -8+4=-4 bytes. + */ + modeSelect10->ParameterListLengthLsb -= 4; + + /* + * Second, convert the parameter page format from SCSI + * to ATAPI. + */ + + /* + * Remove the mode parameter data (except the header + * and the block descripter). + */ + byteCount = + modeSelect6->ParameterListLength - + sizeof(SCSI_MODE_PARAMETER_HEADER6) - + sizeof(SCSI_MODE_PARAMTER_BLOCK_DESCRIPTER); + if (byteCount > 0) { + IT8212MoveMemory((unsigned char *)header10 + + sizeof + (SCSI_MODE_PARAMETER_HEADER10), + (unsigned char *)header10 + + sizeof + (SCSI_MODE_PARAMETER_HEADER6) + + + sizeof + (SCSI_MODE_PARAMTER_BLOCK_DESCRIPTER), + byteCount); + } + + /* + * Keep the original header6 (4 bytes) in tempHeader for + * later use + */ + memcpy(tempHeader, header10, + sizeof(SCSI_MODE_PARAMETER_HEADER6)); + + /* + * Change the "mode parameter header(6)" to "mode + * parameter header(10)" + * Notice: Remove the block descripter in SCSI-2 command + * out. It won't be used in MMC. + */ + memset((unsigned char *)header10, 0, + sizeof(SCSI_MODE_PARAMETER_HEADER10)); + header10->ModeDataLengthLsb = header6->ModeDataLength; + header10->MediumType = header6->MediumType; + header10->DeviceSpecificParameter = + header6->DeviceSpecificParameter; + header10->BlockDescriptorLengthLsb = + header6->BlockDescriptorLength; + + /* + * ATAPI doesn't support block descripter, so remove it + * from the mode paramter. + */ + header10->BlockDescriptorLengthLsb = 0; + break; + } + } +} /* end Scsi2Atapi */ + +/************************************************************************ + * Send ATAPI packet command to device. + ************************************************************************/ +static u32 AtapiSendCommand(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) +{ + u8 statusByte; + u8 byteCountLow; + u8 byteCountHigh; + u8 useDMA; + u8 RevisionID = 0; + u8 bmClearStat; + u32 flags; + int i; + unsigned long bmAddress = pChan->dma_base; + PITE_ADAPTER pAdap = ite_adapters[0]; + + dprintk("AtapiSendCommand: command 0x%X to device %d\n", Srb->Cdb[0], + Srb->TargetId); + + /* + * Default use PIO mode. + */ + useDMA = 0; + pChan->ConvertCdb = FALSE; + + /* + * Make sure command is to ATAPI device. + */ + flags = pChan->DeviceFlags[Srb->TargetId & 1]; + if (flags & (DFLAGS_SANYO_ATAPI_CHANGER | DFLAGS_ATAPI_CHANGER)) { + if ((Srb->Lun) > (pChan->DiscsPresent[Srb->TargetId & 1] - 1)) { + + /* + * Indicate no device found at this address. + */ + return SRB_STATUS_SELECTION_TIMEOUT; + } + } else if (Srb->Lun > 0) { + return SRB_STATUS_SELECTION_TIMEOUT; + } + + if (!(flags & DFLAGS_ATAPI_DEVICE)) + return SRB_STATUS_SELECTION_TIMEOUT; + + /* + * Select device 0 or 1. + */ + outb((u8) (((Srb->TargetId & 0x1) << 4) | 0xA0), + pChan->io_ports[ATAPI_SELECT_OFFSET]); + + /* + * Try to enable interrupt again. (2003/02/25) + */ +#if (0) + outb(0x00, pChan->io_ports[ATAPI_CONTROL_OFFSET]); + +#endif + + /* + * Verify that controller is ready for next command. + */ + GetStatus(pChan, statusByte); + dprintk("AtapiSendCommand: entered with status %x\n", statusByte); + if (statusByte & IDE_STATUS_BUSY) { + printk("AtapiSendCommand: device busy (%x)\n", statusByte); + return SRB_STATUS_BUSY; + } + if (statusByte & IDE_STATUS_ERROR) { + if (Srb->Cdb[0] != SCSIOP_REQUEST_SENSE) { + printk("AtapiSendCommand: error on entry: (%x)\n", + statusByte); + + /* + * Read the error reg. to clear it and fail this request. + */ + return MapError(pChan, Srb); + } + } + + /* + * If a tape drive doesn't have DSC set and the last command is + * restrictive, don't send the next command. See discussion of + * Restrictive Delayed Process commands in QIC-157. + */ + if ((!(statusByte & IDE_STATUS_DSC)) && (flags & DFLAGS_TAPE_DEVICE) + && pChan->RDP) { + mdelay(1); + printk("AtapiSendCommand: DSC not set. %x\n", statusByte); + return SRB_STATUS_BUSY; + } + if (statusByte & IDE_STATUS_DRQ) { + printk("AtapiSendCommand: enter with status (%x). Attempt to " + "recover.\n", statusByte); + + /* + * Try to drain the data that one preliminary device thinks that + * it has to transfer. Hopefully this random assertion of DRQ + * will not be present in production devices. + */ + for (i = 0; i < 0x10000; i++) { + GetStatus(pChan, statusByte); + if (statusByte & IDE_STATUS_DRQ) { + + /* + * Note: The data register is always referenced + * as a 16-bit word. + */ + inw(pChan->io_ports[ATAPI_DATA_OFFSET]); + } else { + break; + } + } + if (i == 0x10000) { + printk("AtapiSendCommand: DRQ still asserted.Status " + "(%x)\n", statusByte); + printk("AtapiSendCommand: issued soft reset to Atapi " + "device. \n"); + AtapiSoftReset(pChan, Srb->TargetId); + + /* + * Re-initialize Atapi device. + */ + IssueIdentify(pChan, (Srb->TargetId & 1), + IDE_COMMAND_ATAPI_IDENTIFY); + + /* + * Inform the port driver that the bus has been reset. + */ + + /* + * Clean up device extension fields that AtapiStartIo + * won't. + */ + pChan->ExpectingInterrupt = FALSE; + pChan->RDP = FALSE; + return SRB_STATUS_BUS_RESET; + } + } + if (flags & (DFLAGS_SANYO_ATAPI_CHANGER | DFLAGS_ATAPI_CHANGER)) { + + /* + * As the cdrom driver sets the LUN field in the cdb, it must + * be removed. + */ + Srb->Cdb[1] &= ~0xE0; + if ((Srb->Cdb[0] == SCSIOP_TEST_UNIT_READY) + && (flags & DFLAGS_SANYO_ATAPI_CHANGER)) { + + /* + * Torisan changer. TUR's are overloaded to be platter + * switches. + */ + Srb->Cdb[7] = Srb->Lun; + } + } + + /* + * Convert SCSI to ATAPI commands if needed + */ + switch (Srb->Cdb[0]) { + case SCSIOP_MODE_SENSE: + case SCSIOP_MODE_SELECT: + if (flags & DFLAGS_ATAPI_DEVICE) { + Scsi2Atapi(pChan, Srb); + } + break; + } + if (pChan->UseDma[Srb->TargetId & 1]) { + switch (Srb->Cdb[0]) { + case SCSIOP_READ: /* (0x28) */ + case 0xA8: /* READ(12) */ + case SCSIOP_READ_CD: + if (Srb->DataTransferLength == 0) { + break; + } + + /* + * First, switch to DMA or UDMA mode if running on + * Bypass mode. + */ + if (pAdap->bypass_mode) + IT8212SwitchDmaMode(pChan, Srb->TargetId); + + /* + * Check the SCATTER/GATHER count. The upper will give + * the different memory address depend on whether + * use_sg is used or not. + */ + if (Srb->UseSg == 0) + IdeBuildDmaTable(pChan, Srb); + else + IdeBuildDmaSgTable(pChan, Srb); + bmClearStat = inb(bmAddress + 2); + if (Srb->TargetId & 0x01) { + bmClearStat = + bmClearStat | BM_DRV1_DMA_CAPABLE | + BM_STAT_FLG_INT | BM_STAT_FLG_ERR; + } else { + bmClearStat = + bmClearStat | BM_DRV0_DMA_CAPABLE | + BM_STAT_FLG_INT | BM_STAT_FLG_ERR; + } + useDMA = 1; + outb(0, bmAddress); + + /* + * Setup PRD table physical address. + */ + outl(pChan->dmatable_dma, bmAddress + 4); + + /* + * Clear the status. + */ + outb(bmClearStat, bmAddress + 2); + break; + } /* end switch (Srb->Cdb[0]) */ + } + + /* + * Set data buffer pointer and words left. + */ + pChan->DataBuffer = (unsigned short *)Srb->DataBuffer; + if (useDMA) + pChan->WordsLeft = 0; + else + pChan->WordsLeft = Srb->DataTransferLength / 2; + outb((u8) (((Srb->TargetId & 0x1) << 4) | 0xA0), + pChan->io_ports[ATAPI_SELECT_OFFSET]); + WaitOnBusy(pChan, statusByte); + + /* + * Write transfer byte count to registers. + */ + byteCountLow = (u8) (Srb->DataTransferLength & 0xFF); + byteCountHigh = (u8) (Srb->DataTransferLength >> 8); + if (Srb->DataTransferLength >= 0x10000) + byteCountLow = byteCountHigh = 0xFF; + outb(byteCountLow, pChan->io_ports[ATAPI_LCYL_OFFSET]); + outb(byteCountHigh, pChan->io_ports[ATAPI_HCYL_OFFSET]); + outb(0, pChan->io_ports[ATAPI_INTREASON_OFFSET]); + outb(0, pChan->io_ports[ATAPI_UNUSED1_OFFSET]); + outb(useDMA, pChan->io_ports[ATAPI_FEATURE_OFFSET]); + WaitOnBusy(pChan, statusByte); + if (flags & DFLAGS_INT_DRQ) { + + /* + * This device interrupts when ready to receive the packet. + * + * Write ATAPI packet command. + */ + outb(IDE_COMMAND_ATAPI_PACKET, + pChan->io_ports[IDE_COMMAND_OFFSET]); + printk("AtapiSendCommand: wait for int. to send packet. " + "status (%x)\n", statusByte); + pChan->ExpectingInterrupt = TRUE; + return SRB_STATUS_PENDING; + } else { + + /* + * Write ATAPI packet command. + */ + outb(IDE_COMMAND_ATAPI_PACKET, + pChan->io_ports[IDE_COMMAND_OFFSET]); + + /* + * Wait for DRQ. + */ + WaitOnBusy(pChan, statusByte); + WaitForDrq(pChan, statusByte); + if (!(statusByte & IDE_STATUS_DRQ)) { + printk("AtapiSendCommand: DRQ never asserted (%x)\n", + statusByte); + return SRB_STATUS_ERROR; + } + } + + /* + * Need to read status register. + */ + GetBaseStatus(pChan, statusByte); + + /* + * Send CDB to device. + * After detecting DRQ, the host writes the 12 bytes(6 words) of Command + * to the Data Register. + */ + WaitOnBusy(pChan, statusByte); + WriteBuffer(pChan, (unsigned short *)Srb->Cdb, 6); + + /* + * If running on DMA mode, start BUS MASTER operation. + */ + if (useDMA) { + + /* + * If SCSIOP_READ command is sent to an Audio CD, error will be + * returned. But the error will be blocked by our controller if + * bus master operation started. So wait for a short period to + * check if error occurs. If error occurs, don't start bus + * master operation. + */ + if (RevisionID == 0x10) { + for (i = 0; i < 500; i++) { + udelay(1); + statusByte = inb(bmAddress + 2); + if (statusByte & BM_STAT_FLG_INT) { + + /* + * If error occurs, give up this round. + */ + printk("AtapiSendCommand: command " + "failed. Don't start bus " + "master."); + printk("status=%x, i=%d\n", statusByte, + i); + pChan->ExpectingInterrupt = TRUE; + return SRB_STATUS_PENDING; + } + } + } + if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) + outb(BM_CMD_FLG_START | BM_CMD_FLG_WRTTOMEM, bmAddress); + else if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) + outb(BM_CMD_FLG_START | BM_CMD_FLG_WRTTODSK, bmAddress); + } + + /* end if (useDMA) */ + /* + * Indicate expecting an interrupt and wait for it. + */ + pChan->ExpectingInterrupt = TRUE; + return SRB_STATUS_PENDING; +} /* end AtapiSendCommand */ + +/************************************************************************ + * Program ATA registers for IDE disk transfer. + ************************************************************************/ +static u32 IdeSendCommand(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) +{ + u8 statusByte; + u32 status; + u32 i; + Scsi_Cmnd *pREQ; + unsigned char *request_buffer; + PINQUIRYDATA inquiryData; + + pREQ = Srb->pREQ; + status = SRB_STATUS_SUCCESS; + statusByte = 0; + switch (Srb->Cdb[0]) { + case SCSIOP_INQUIRY: + dprintk("SCSIOP_INQUIRY\n"); + + /* + * Filter out all TIDs but 0 and 1 since this is an IDE + * interface which support up to two devices. + */ + if ((pREQ->device->lun != 0) || + (!pChan-> + DeviceFlags[pREQ->device-> + id & 1] & DFLAGS_DEVICE_PRESENT)) { + + /* + * Indicate no device found at this address. + */ + status = SRB_STATUS_INVALID_TARGET_ID; + break; + } else { + request_buffer = Srb->DataBuffer; + inquiryData = Srb->DataBuffer; + + /* + * Zero INQUIRY data structure. + */ + memset(request_buffer, 0, Srb->DataTransferLength); + + /* + * Standard IDE interface only supports disks. + */ + inquiryData->DeviceType = DIRECT_ACCESS_DEVICE; + + /* + * Device type modifer. + */ + request_buffer[1] = 0; + + /* + * No ANSI/ISO compliance. + */ + request_buffer[2] = 0; + + /* + * Additional length. + */ + request_buffer[4] = 31; + memcpy(&request_buffer[8], "ITE ", 8); + memcpy(&request_buffer[16], "IT8212F ", 16); + memcpy(&request_buffer[32], DRV_VER_8212, 4); + + /* + * Set the removable bit, if applicable. + */ + if (pChan-> + DeviceFlags[pREQ->device-> + id & 1] & DFLAGS_REMOVABLE_DRIVE) { + inquiryData->RemovableMedia = 1; + } + status = SRB_STATUS_SUCCESS; + } + break; + case SCSIOP_MODE_SENSE: + status = SRB_STATUS_INVALID_REQUEST; + break; + case SCSIOP_TEST_UNIT_READY: + status = SRB_STATUS_SUCCESS; + break; + case SCSIOP_READ_CAPACITY: + + /* + * Claim 512 byte blocks (big-endian). + */ + ((PREAD_CAPACITY_DATA) Srb->DataBuffer)->BytesPerBlock = + 0x20000; + + /* + * Calculate last sector. + */ + if (pChan->IdentifyData[pREQ->device->id & 0x01]. + UserAddressableSectors == 0x0FFFFFFF) { + i = pChan->IdentifyData[pREQ->device->id & 0x01]. + Capacity_48bit_LOW - 1; + } else { + i = pChan->IdentifyData[pREQ->device->id & 0x01]. + UserAddressableSectors - 1; + } + ((PREAD_CAPACITY_DATA) Srb->DataBuffer)->LogicalBlockAddress = + (((unsigned char *)&i)[0] << 24) | + (((unsigned char *)&i)[1] << 16) | + (((unsigned char *)&i)[2] << 8) | ((unsigned char *)&i)[3]; + status = SRB_STATUS_SUCCESS; + break; + case SCSIOP_VERIFY: + status = IdeVerify(pChan, Srb); + break; + case SCSIOP_READ: + case SCSIOP_WRITE: + status = IT8212ReadWrite(pChan, Srb); + break; + case SCSIOP_START_STOP_UNIT: + + /* + * Determine what type of operation we should perform + */ + status = SRB_STATUS_SUCCESS; + break; + case SCSIOP_REQUEST_SENSE: + + /* + * This function makes sense buffers to report the results + * of the original GET_MEDIA_STATUS command + */ + status = SRB_STATUS_INVALID_REQUEST; + break; + default: + printk("IdeSendCommand: unsupported command %x\n", Srb->Cdb[0]); + status = SRB_STATUS_INVALID_REQUEST; + } /* end switch */ + return status; +} /* end IdeSendCommand */ + +/************************************************************************ + * This routine is called from the SCSI port driver synchronized with + * the kernel to start an IO request. If the current SRB is busy, return + * FALSE, else return TURE. + ************************************************************************/ +static void AtapiStartIo(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) +{ + u32 status = 0; + + /* + * Determine which function. + */ + switch (Srb->Function) { + case SRB_FUNCTION_EXECUTE_SCSI: + + /* + * Sanity check. Only one request can be outstanding on a + * controller. + */ + if (pChan->CurrentSrb) { + printk("AtapiStartIo: already have a request!\n"); + status = SRB_STATUS_BUSY; + Srb->SrbStatus = SRB_STATUS_BUSY; + goto busy; + } + + /* + * Indicate that a request is active on the controller. + */ + pChan->CurrentSrb = Srb; + Srb->SrbStatus = SRB_STATUS_PENDING; + + /* + * Send command to device. + */ + if (pChan->DeviceFlags[Srb->TargetId & 1] & + DFLAGS_ATAPI_DEVICE) { + + /* + * If this is ATAPI device. + */ + status = AtapiSendCommand(pChan, Srb); + } else if (pChan->DeviceFlags[Srb->TargetId & 1] & + DFLAGS_DEVICE_PRESENT) { + + /* + * If this is IDE device. + */ + status = IdeSendCommand(pChan, Srb); + } else { + + /* + * Nothing else. + */ + status = SRB_STATUS_SELECTION_TIMEOUT; + } + break; + case SRB_FUNCTION_IO_CONTROL: + + /* + * IO control function. + */ + printk("AtapiStartIo: IO control\n"); + break; + default: + + /* + * Indicate unsupported command. + */ + status = SRB_STATUS_INVALID_REQUEST; + break; + } /* end switch */ +busy: + if (status != SRB_STATUS_PENDING) { + + /* + * Set status in SRB. + */ + Srb->SrbStatus = (u8) status; + dprintk("AtapiStartIo: status=%x\n", status); + TaskDone(pChan, Srb); + } +} /* end AtapiStartIo */ + +/************************************************************************ + * Convert Scsi_Cmnd structure to SCSI_REQUEST_BLOCK. + ************************************************************************/ +static void MapRequest(Scsi_Cmnd * pREQ, PSCSI_REQUEST_BLOCK Srb) +{ + Srb->Length = sizeof(SCSI_REQUEST_BLOCK); + Srb->CdbLength = pREQ->cmd_len; + Srb->TargetId = pREQ->device->id; + Srb->Lun = pREQ->device->lun; + Srb->UseSg = pREQ->use_sg; + + /* + * Copy the actual command from Scsi_Cmnd to CDB. + */ + memcpy(Srb->Cdb, pREQ->cmnd, Srb->CdbLength); + + /* + * Always the SCSI_FUNCTION_EXECUTE_SCSI now. + */ + Srb->Function = SRB_FUNCTION_EXECUTE_SCSI; + Srb->SrbStatus = 0; + Srb->ScsiStatus = 0; + Srb->SenseInfoBufferLength = 16; + + /* + * The CDB's first byte is operation code. + */ + if ((Srb->Cdb[0] == SCSIOP_WRITE6) || (Srb->Cdb[0] == SCSIOP_WRITE) + || (Srb->Cdb[0] == SCSIOP_MODE_SELECT10)) { + Srb->SrbFlags = SRB_FLAGS_DATA_OUT; + } else { + Srb->SrbFlags = SRB_FLAGS_DATA_IN; + } + Srb->TimeOutValue = 0; + Srb->SenseInfoBuffer = pREQ->sense_buffer; + if (Srb->Cdb[0] == SCSIOP_REQUEST_SENSE) { + Srb->DataTransferLength = 0x40; + Srb->DataBuffer = pREQ->sense_buffer; + } else { + Srb->DataTransferLength = pREQ->request_bufflen; + Srb->DataBuffer = pREQ->request_buffer; + } + + if (pREQ->use_sg) { + Srb->WorkingFlags |= SRB_WFLAGS_USE_SG; + } + Srb->pREQ = pREQ; +} /* end MapRequest */ + +/************************************************************************ + * A task execution has been done. For OS request, we need to Notify OS + * and invoke next take which wait at queue. + ************************************************************************/ +static void TaskDone(PChannel pChan, PSCSI_REQUEST_BLOCK Srb) +{ + Scsi_Cmnd *pREQ = Srb->pREQ; + pChan->CurrentSrb = NULL; + pChan->RetryCount = 0; + switch (SRB_STATUS(Srb->SrbStatus)) { + case SRB_STATUS_SUCCESS: + pREQ->result = (DID_OK << 16); + break; + case SRB_STATUS_SELECTION_TIMEOUT: + pREQ->result = (DID_NO_CONNECT << 16); + break; + case SRB_STATUS_BUSY: + pREQ->result = (DID_BUS_BUSY << 16); + break; + case SRB_STATUS_BUS_RESET: + pREQ->result = (DID_RESET << 16); + break; + case SRB_STATUS_INVALID_TARGET_ID: + case SRB_STATUS_INVALID_PATH_ID: + case SRB_STATUS_INVALID_LUN: + case SRB_STATUS_NO_HBA: + pREQ->result = (DID_BAD_TARGET << 16); + break; + case SRB_STATUS_NO_DEVICE: + pREQ->result = (DID_BAD_TARGET << 16); + break; + case SRB_STATUS_ERROR: + pREQ->result = (DRIVER_SENSE << 24) | (DID_OK << 16) | + (CHECK_CONDITION << 1); + break; + } + dprintk("TaskDone(pChan=%p, pREQ=%p, result=%x)\n", pChan, pREQ, + pREQ->result); + + /* + * Notify OS that this OS request has been done. + */ + pREQ->scsi_done(pREQ); + + /* + * Check the queue again. + */ + TaskQueue(); +} /* end TaskDone */ + +/************************************************************************ + * Start a command, doing convert first. + ************************************************************************/ +static void TaskStart(PChannel pChan, Scsi_Cmnd * pREQ) +{ + PSCSI_REQUEST_BLOCK Srb; + dprintk("TaskStart(pChan=%p, pREQ=%p)\n", pChan, pREQ); + Srb = &pChan->_Srb; + + /* + * Clear the SRB structure. + */ + memset(Srb, 0, sizeof(SCSI_REQUEST_BLOCK)); + + /* + * Convert Scsi_Cmnd structure to SCSI_REQUEST_BLOCK. + */ + MapRequest(pREQ, Srb); + + /* + * Start IDE I/O command. + */ + AtapiStartIo(pChan, Srb); +} /* end TaskStart */ + +/************************************************************************ + * Check if queue is empty. If there are request in queue, transfer the + * request to HIM's request and execute the request. + ************************************************************************/ +static void TaskQueue(void) +{ + unsigned long flags; + Scsi_Cmnd *SCpnt; + PChannel pChan; + PITE_ADAPTER pAdap; + +check_next: + if (it8212_req_last != NULL) { + spin_lock_irqsave(&queue_request_lock, flags); + + SCpnt = (Scsi_Cmnd *) it8212_req_last->SCp.ptr; + if (it8212_req_last == SCpnt) + it8212_req_last = NULL; + else + it8212_req_last->SCp.ptr = (char *)SCpnt->SCp.ptr; + + spin_unlock_irqrestore(&queue_request_lock, flags); + + /* + * Check the command. + */ + if (SCpnt->device->host) { + if ((SCpnt->device->channel != 0) || + (SCpnt->device->id >= (4 * NumAdapters))) { + + /* + * Returns that we have a bad target. + */ + SCpnt->result = (DID_BAD_TARGET << 16); + SCpnt->scsi_done(SCpnt); + goto check_next; + } + } + if (SCpnt->device->id >= 4) { + pAdap = ite_adapters[1]; + if (SCpnt->device->id < 6) + pChan = &pAdap->IDEChannel[0]; + + else + pChan = &pAdap->IDEChannel[1]; + } else { + pAdap = ite_adapters[0]; + if (SCpnt->device->id < 2) + pChan = &pAdap->IDEChannel[0]; + + else + pChan = &pAdap->IDEChannel[1]; + } + TaskStart(pChan, SCpnt); + return; + } +} /* end TaskQueue */ + +/**************************************************************** + * Name: iteraid_queuecommand + * Description: Process a queued command from the SCSI manager. + * Parameters: SCpnt - Pointer to SCSI command structure. + * done - Pointer to done function to call. + * Returns: Status code. + ****************************************************************/ +static int iteraid_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) +{ + unsigned long flags; + dprintk("##Queuecommand enter##\n"); + + /* + * Hooks the done routine. + */ + SCpnt->scsi_done = (void *)done; + spin_lock_irqsave(&queue_request_lock, flags); + if (it8212_req_last == NULL) { + SCpnt->SCp.ptr = (char *)SCpnt; + } else { + SCpnt->SCp.ptr = it8212_req_last->SCp.ptr; + it8212_req_last->SCp.ptr = (char *)SCpnt; + } + it8212_req_last = SCpnt; + spin_unlock_irqrestore(&queue_request_lock, flags); + TaskQueue(); + dprintk("@@Queuecommand exit@@\n"); + return 0; +} /* end iteraid_queuecommand */ + +#if 0 +/**************************************************************** + * Name: internal_done :LOCAL + * Description: Done handler for non-queued commands + * Parameters: SCpnt - Pointer to SCSI command structure. + * Returns: Nothing. + ****************************************************************/ +static void internal_done(Scsi_Cmnd * SCpnt) +{ + SCpnt->SCp.Status++; +} /* end internal_done */ +#endif + +#if 0 +/**************************************************************** + * Name: iteraid_command + * Description: Process a command from the SCSI manager. + * Parameters: SCpnt - Pointer to SCSI command structure. + * Returns: Status code. + ****************************************************************/ +static int iteraid_command(Scsi_Cmnd * SCpnt) +{ + unsigned long timeout; + SCpnt->SCp.Status = 0; + iteraid_queuecommand(SCpnt, internal_done); + + /* + * Should be longer than hard-reset time. + */ + timeout = jiffies + 60 * HZ; + while (!SCpnt->SCp.Status && time_before(jiffies, timeout)) + barrier(); + if (!SCpnt->SCp.Status) + SCpnt->result = (DID_ERROR << 16); + return SCpnt->result; +} /* end iteraid_command */ +#endif + +/************************************************************************ + * Enables/disables media status notification. + ************************************************************************/ +#if 0 +static void IdeMediaStatus(u8 EnableMSN, PChannel pChan, u8 Device) +{ + u8 statusByte; + u8 errorByte; + statusByte = 0; + if (EnableMSN == TRUE) { + + /* + * If supported enable Media Status Notification support. + */ + if ((pChan->DeviceFlags[Device] & DFLAGS_REMOVABLE_DRIVE)) { + outb((u8) (0x95), pChan->io_ports[IDE_FEATURE_OFFSET]); + outb(IDE_COMMAND_ENABLE_MEDIA_STATUS, + pChan->io_ports[IDE_COMMAND_OFFSET]); + WaitOnBaseBusy(pChan, statusByte); + if (statusByte & IDE_STATUS_ERROR) { + + /* + * Read the error register. + */ + errorByte = + inb(pChan->io_ports[IDE_ERROR_OFFSET]); + printk("IdeMediaStatus: error enabling media " + "status. status %u, error byte %u\n", + statusByte, errorByte); + } else { + pChan->DeviceFlags[Device] |= + DFLAGS_MEDIA_STATUS_ENABLED; + printk("IdeMediaStatus: media status " + "notification supported!\n"); + pChan->ReturningMediaStatus = 0; + } + } + } else { /* end if EnableMSN == TRUE */ + + /* + * Disable if previously enabled. + */ + if ((pChan->DeviceFlags[Device] & + DFLAGS_MEDIA_STATUS_ENABLED)) { + outb((u8) (0x31), pChan->io_ports[IDE_FEATURE_OFFSET]); + outb(IDE_COMMAND_ENABLE_MEDIA_STATUS, + pChan->io_ports[IDE_COMMAND_OFFSET]); + WaitOnBaseBusy(pChan, statusByte); + pChan->DeviceFlags[Device] &= + ~DFLAGS_MEDIA_STATUS_ENABLED; + } + } +} /* end IdeMediaStatus */ + +#endif /* */ +/************************************************************************ + * Issue IDENTIFY command to a device. + * Either the standard (EC) or the ATAPI packet (A1) IDENTIFY. + ************************************************************************/ +static u8 IssueIdentify(PChannel pChan, u8 DeviceNumber, u8 Command) +{ + u8 statusByte = 0; + u32 i; + u32 j; + + /* + * Check that the status register makes sense. + */ + GetBaseStatus(pChan, statusByte); + if (Command == IDE_COMMAND_IDENTIFY) { + + /* + * Mask status byte ERROR bits. + */ + statusByte &= ~(IDE_STATUS_ERROR | IDE_STATUS_INDEX); + dprintk("IssueIdentify: checking for IDE. status (%x)\n", + statusByte); + + /* + * Check if register value is reasonable. + */ + if (statusByte != IDE_STATUS_IDLE) { + + /* + * Reset the channel. + */ + printk("IssueIdentify: resetting channel.\n"); + IdeHardReset(pChan, statusByte); + outb((u8) ((DeviceNumber << 4) | 0xA0), + pChan->io_ports[IDE_SELECT_OFFSET]); + GetBaseStatus(pChan, statusByte); + statusByte &= ~IDE_STATUS_INDEX; + if (statusByte != IDE_STATUS_IDLE) { + + /* + * Give up on this. + */ + printk("IssueIdentify(IDE): disk[%d] not " + "ready. status=0x%x\n", + DeviceNumber, statusByte); + return FALSE; + } + } + } else { + dprintk("IssueIdentify: checking for ATAPI. status (%x)\n", + statusByte); + if ((statusByte & IDE_STATUS_BUSY) + || (statusByte & IDE_STATUS_DRQ)) { + + /* + * Reset the device. + */ + dprintk("IssueIdentify: resetting device.\n"); + AtapiSoftReset(pChan, DeviceNumber); + outb((u8) ((DeviceNumber << 4) | 0xA0), + pChan->io_ports[IDE_SELECT_OFFSET]); + GetBaseStatus(pChan, statusByte); + if (statusByte != 0) { + + /* + * Give up on this. + */ + printk("IssueIdentify(ATAPI): disk[%d] not " + "ready. status=0x%x\n", + DeviceNumber, statusByte); + return FALSE; + } + } + } + for (j = 0; j < 2; j++) { + + /* + * Wait for device ready (Not Busy and Not DRQ). + */ + outb((u8) ((DeviceNumber << 4) | 0xA0), + pChan->io_ports[IDE_SELECT_OFFSET]); + WaitForDeviceReady(pChan, statusByte); + if ((statusByte & IDE_STATUS_BUSY) + || (statusByte & IDE_STATUS_DRQ)) { + printk + ("IssueIdentify: disk[%d] not ready. status=0x%x\n", + DeviceNumber, statusByte); + continue; + } + + /* + * Send IDENTIFY command. + */ + outb(Command, pChan->io_ports[IDE_COMMAND_OFFSET]); + + /* + * Wait for DRQ. + */ + WaitForBaseDrq(pChan, statusByte); + if (!(statusByte & IDE_STATUS_DRQ)) { + printk("IssueIdentify: disk[%d] DRQ never asserted. " + "status=%x\n", DeviceNumber, statusByte); + + /* + * Give one more chance. + */ + if (Command == IDE_COMMAND_IDENTIFY) + IdeHardReset(pChan, statusByte); + else + AtapiSoftReset(pChan, DeviceNumber); + } else { + break; + } + } + + /* + * Check for error on really stupid master devices that assert random + * patterns of bits in the status register at the slave address. + */ + outb((u8) ((DeviceNumber << 4) | 0xA0), + pChan->io_ports[IDE_SELECT_OFFSET]); + GetBaseStatus(pChan, statusByte); + if (statusByte & IDE_STATUS_ERROR) { + printk("IssueIdentify: disk[%d] returns error status\n", + DeviceNumber); + return FALSE; + } + dprintk("IssueIdentify: status before read words %x\n", statusByte); + + /* + * Suck out 256 words. After waiting for one model that asserts busy + * after receiving the Packet Identify command. + */ + WaitOnBusy(pChan, statusByte); + if (!(statusByte & IDE_STATUS_DRQ)) { + return FALSE; + } + ReadBuffer(pChan, (unsigned short *)&pChan->FullIdentifyData, 256); + + /* + * Check out a few capabilities / limitations of the device. + * 01/29/2003 + */ + if (pChan->FullIdentifyData.SpecialFunctionsEnabled & 1) { + + /* + * Determine if this drive supports the MSN functions. + */ + printk("Marking drive %x as removable. SFE = %x\n", + DeviceNumber, + pChan->FullIdentifyData.SpecialFunctionsEnabled); + pChan->DeviceFlags[DeviceNumber] |= DFLAGS_REMOVABLE_DRIVE; + } + memcpy(&pChan->IdentifyData[DeviceNumber], &pChan->FullIdentifyData, + sizeof(IDENTIFY_DATA2)); + if (pChan->IdentifyData[DeviceNumber].GeneralConfiguration & 0x20 + && Command != IDE_COMMAND_IDENTIFY) { + + /* + * This device interrupts with the assertion of DRQ after + * receiving Atapi Packet Command. + */ + pChan->DeviceFlags[DeviceNumber] |= DFLAGS_INT_DRQ; + dprintk(KERN_NOTICE "Device interrupts on assertion of DRQ.\n"); + } else { + dprintk(KERN_NOTICE + "Device does't interrupt on assertion of DRQ.\n"); + } + if (((pChan->IdentifyData[DeviceNumber]. + GeneralConfiguration & 0xF00) == 0x100) + && Command != IDE_COMMAND_IDENTIFY) { + + /* + * This is a tape. + */ + pChan->DeviceFlags[DeviceNumber] |= DFLAGS_TAPE_DEVICE; + printk(KERN_NOTICE "IssueIdentify: device is a tape drive.\n"); + } else { + dprintk(KERN_NOTICE + "IssueIdentify: device is not a tape drive.\n"); + } + + /* + * Work around for some IDE and one model Atapi that will present more + * then 256 bytes for the Identify data. + */ + WaitOnBaseBusy(pChan, statusByte); + for (i = 0; i < 0x10000; i++) { + GetStatus(pChan, statusByte); + if (statusByte & IDE_STATUS_DRQ) { + + /* + * Suck out any remaining bytes and throw away. + */ + inw(pChan->io_ports[IDE_DATA_OFFSET]); + } else { + break; + } + } + return TRUE; +} /* end IssueIdentify() */ + +/************************************************************************ + * Check this is the IDE or ATAPI disk then identify it. + ************************************************************************/ +static u8 iteraid_find_device(PChannel pChan, u8 channel) +{ + u8 deviceNumber; + u8 signatureLow; + u8 signatureHigh; + u8 deviceResponded = FALSE; + u8 statusByte = 0; + + /* + * Clear expecting interrupt flag and current SRB field. + */ + pChan->ExpectingInterrupt = FALSE; + pChan->CurrentSrb = NULL; + + /* + * Search for devices in each channel. + */ + for (deviceNumber = 0; deviceNumber < 2; deviceNumber++) { + + /* + * Select the device. + */ + outb((u8) ((deviceNumber << 4) | 0xA0), + pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Disable interrupts during initialization. + */ + outb(IDE_DC_DISABLE_INTERRUPTS, + pChan->io_ports[IDE_CONTROL_OFFSET]); + + /* + * Check here for some SCSI adapters that incorporate IDE + * emulation. + */ + statusByte = inb(pChan->io_ports[IDE_CONTROL_OFFSET]); + + /* + * Do soft reset on selected device. (AtapiSoftReset) + */ + AtapiSoftReset(pChan, deviceNumber); + WaitOnBusy(pChan, statusByte); + signatureLow = inb(pChan->io_ports[IDE_MIDCYL_OFFSET]); + signatureHigh = inb(pChan->io_ports[IDE_HCYL_OFFSET]); + if (signatureLow == 0x14 && signatureHigh == 0xEB) { + + /* + * ATAPI signature found. Issue ATAPI packet identify + * command. + */ + if (IssueIdentify(pChan, deviceNumber, + IDE_COMMAND_ATAPI_IDENTIFY)) { + + /* + * Indicate ATAPI device. + */ + printk("iteraid_find_device: channel %x " + "device %x is ATAPI.\n", + channel, deviceNumber); + pChan->DeviceFlags[deviceNumber] |= + DFLAGS_ATAPI_DEVICE; + pChan->DeviceFlags[deviceNumber] |= + DFLAGS_DEVICE_PRESENT; + pChan->DeviceFlags[deviceNumber] &= + ~DFLAGS_CONFIG_CHANGED; + deviceResponded = TRUE; + GetStatus(pChan, statusByte); + if (statusByte & IDE_STATUS_ERROR) { + AtapiSoftReset(pChan, deviceNumber); + } + } else { + + /* + * Indicate no working device. + */ + printk("iteraid_find_device: channel %x device " + "%x doesn't respond.\n", + channel, deviceNumber); + pChan->DeviceFlags[deviceNumber] &= + ~DFLAGS_DEVICE_PRESENT; + } + } else { + + /* + * Select the device. + */ + outb((u8) ((deviceNumber << 4) | 0xA0), + pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Check here for some SCSI adapters that incorporate + * IDE emulation. + */ + GetStatus(pChan, statusByte); + + /* + * No Disk. + */ + if (statusByte == 0xFF || statusByte == 0x7F + || statusByte == 0x0) { + dprintk("FindDevices: cannot find IDE device. " + "status = %x\n", statusByte); + continue; + } + + /* + * Issue IDE Identify. If an ATAPI device is actually + * present, the signature will be asserted, and the + * drive will be recognized as such. + */ + if (IssueIdentify(pChan, deviceNumber, + IDE_COMMAND_IDENTIFY)) { + + /* + * IDE drive found. + */ + printk(KERN_WARNING + "FindDevices: device %u is IDE\n", + (channel * 2) + deviceNumber); + pChan->DeviceFlags[deviceNumber] |= + DFLAGS_DEVICE_PRESENT; + pChan->DeviceFlags[deviceNumber] &= + ~DFLAGS_ATAPI_DEVICE; + pChan->DeviceFlags[deviceNumber] &= + ~DFLAGS_CONFIG_CHANGED; + deviceResponded = TRUE; + } else { + printk(KERN_WARNING + "FindDevices: device %u is not present\n", + (channel * 2) + deviceNumber); + pChan->DeviceFlags[deviceNumber] &= + ~DFLAGS_DEVICE_PRESENT; + } + } + } + return deviceResponded; +} /* end iteraid_find_device */ + +#if 0 +/************************************************************************ + * IDE disk hardware initialize. + ************************************************************************/ +static u8 AtapiHwInitialize(PITE_ADAPTER pAdap, PChannel pChan, u8 channel) +{ + u8 i; + u8 statusByte = 0; + + /* + * For two devices in this channel. + */ + for (i = 0; i < 2; i++) { + + /* + * only check in Fireware mode. + */ + if (pAdap->bypass_mode == FALSE) { + outb((u8) (0xA0 | ((u8) i << 4)), + pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Check if card at this address. + */ + outb(0xAA, pChan->io_ports[IDE_MIDCYL_OFFSET]); + + /* + * Check if indentifier can be read back. + */ + if ((statusByte = + inb(pChan->io_ports[IDE_MIDCYL_OFFSET])) != 0xAA) { + printk("AtapiHwInitialize: identifier read " + "back from (%x, %x) = %x\n", + channel, i, statusByte); + + /* + * ***** Dont free it....For later use ***** + * ScsiPortFreeDeviceBase(HwDeviceExtension, + * ioSpace1); + */ + continue; + } + printk("AtapiHwInitialize: found ATA device (%x, %x)n", + channel, i); + } + } + return TRUE; +} /* end AtapiHwInitialize */ +#endif + +/************************************************************************ + * Initialize a adapter, return 0 means success. + ************************************************************************/ +static int iteraid_init(PITE_ADAPTER pAdap, struct pci_dev *pPciDev) +{ + u8 z; + u8 i; + u8 j; + u8 set_irq; + unsigned long control_addr; /* Control reg base address */ + unsigned long base_addr; /* IDE I/O port base address */ + unsigned long bm_base_addr; /* Bus Master base address */ + PChannel pChan; /* Use for each channel */ + dprintk("iteraid_init enter\n"); + + /* + * Common settings. + */ + pAdap->pci_bus = pPciDev->bus->number; + pAdap->devfn = pPciDev->devfn; + pAdap->irq = pPciDev->irq; + pAdap->irqOwned = 0; + printk(KERN_NOTICE "Found Controller: %s\n", pAdap->name); + + /* + * Allocate buffer for IDE channles (One IT8212 supports two channels) + */ + pAdap->IDEChannel = + (PChannel) kmalloc(sizeof(Channel) * pAdap->num_channels, + GFP_ATOMIC); + if (pAdap->IDEChannel == 0) { + printk("iteraid_init: pChan allocate failed.\n"); + return -1; + } + memset(pAdap->IDEChannel, 0, sizeof(Channel) * pAdap->num_channels); + set_irq = 1; + for (i = 0; i < NumAdapters; i++) { + if (ite_adapters[i]->irqOwned == pAdap->irq) + set_irq = 0; + } + + /* + * Request the irq (share irq) and hook the interrupt service routine. + */ + if (set_irq) { + if (request_irq + (pAdap->irq, Irq_Handler, SA_SHIRQ, PROC_DIR_NAME, + pAdap) < 0) { + printk("iteraid_init: unable to allocate IRQ for %s\n", + pAdap->name); + return -1; + } + pAdap->irqOwned = pAdap->irq; + } + + /* + * Get the IDE port and DMA registers. + */ + for (i = 0; i < pAdap->num_channels; i++) { + pChan = &pAdap->IDEChannel[i]; + + /* + * Reference the book "LINUX DEVICE DRIVER 2nd", Page 484 + * unsigned long pci_resource_start(struct pci_dev *dev, + * int bar); + */ + base_addr = pci_resource_start(pPciDev, i * 2); + control_addr = pci_resource_start(pPciDev, i * 2 + 1); + bm_base_addr = pci_resource_start(pPciDev, 4); + pChan->dma_base = bm_base_addr + i * 8; + for (j = 0; j <= IDE_STATUS_OFFSET; j++) { + pChan->io_ports[j] = base_addr; + base_addr += 1; + } + pChan->io_ports[IDE_CONTROL_OFFSET] = control_addr + 2; + } + + /* + * Initialize channels. + */ + for (z = 0; z < pAdap->num_channels; z++) { + pChan = &pAdap->IDEChannel[z]; + pChan->pPciDev = pPciDev; + pChan->channel = z; + + /* + * This section should be masked off if BIOS is ready. + */ +#if (MARK_DEBUG_BYPASS_MODE) + /* + * BIOS is not ready, so I change to ByPass Mode by myself. + */ + pAdap->bypass_mode = TRUE; + + /* + * Change to bypass mode. + */ + IT8212InitBypassMode(pPciDev); + +#endif + + /* + * Hardware initialize. + */ +#if (0) + AtapiHwInitialize(pAdap, pChan, z); + +#endif + + /* + * Find and identify the IDE or ATAPI device. + */ + iteraid_find_device(pChan, z); + + /* + * Set the best transfer mode. + */ +#if (MARK_SET_BEST_TRANSFER) + IT8212SetBestTransferMode(pAdap, pChan, z); + +#endif + + /* + * Set Scatter/Gather List buffer for the channel. + */ + IdeSetupDma(pChan, pChan->dma_base, 8); + } + dprintk("iteraid_init exit\n"); + return 0; +} /* end iteraid_init */ + +/************************************************************************ + * This function will find and initialize any cards. + ************************************************************************/ +static int iteraid_detect(Scsi_Host_Template * tpnt) +{ + u8 i; + u8 j; + u8 mode; + u8 pci_id; + PChannel pChan; + PITE_ADAPTER pAdap; + struct pci_dev *pPciDev; + dprintk("iteraid_detect enter\n"); + + /* + * Search ITE IT8212 chip. + */ + pPciDev = NULL; + pci_id = 0; + while ((pPciDev = + pci_find_device(ITE_VENDOR_ID, ITE_DEVICE_ID, pPciDev))) { + if (PCI_FUNC(pPciDev->devfn)) + continue; + + if (pci_enable_device(pPciDev)) + continue; + + /* + * Allocate memory for Adapter. + */ + pAdap = (PITE_ADAPTER) kmalloc(sizeof(ITE_ADAPTER), GFP_ATOMIC); + if (pAdap == NULL) { + printk("iteraid_detect: pAdap allocate failed.\n"); + pci_disable_device(pPciDev); + continue; + } + memset(pAdap, 0, sizeof(ITE_ADAPTER)); + pAdap->name = CONTROLLER_NAME_IT8212; + pAdap->num_channels = 2; + pAdap->pci_dev = pPciDev; + + /* + * Check if we are in bypass(transparent) or firmware mode. + */ + pci_read_config_byte(pPciDev, 0x50, &mode); + if (mode & 1) { + dprintk("Firmware mode in PCI#%d\n", pci_id); + pAdap->bypass_mode = FALSE; + } else { + dprintk("Transparent mode in PCI#%d\n", pci_id); + pAdap->bypass_mode = TRUE; + } + if (iteraid_init(pAdap, pPciDev) == 0) + ite_adapters[NumAdapters++] = pAdap; + pci_id++; + } + + /* + * Reenable interrupt after initialization. 2003/04/28 + */ + for (i = 0; i < NumAdapters; i++) { + pAdap = ite_adapters[i]; + for (j = 0; j < pAdap->num_channels; j++) { + pChan = &pAdap->IDEChannel[j]; + outb(IDE_DC_REENABLE_CONTROLLER, + pChan->io_ports[IDE_CONTROL_OFFSET]); + } + } + if (NumAdapters) { + + /* + * Register a virtual host. + */ + ite_vhost = scsi_register(tpnt, 0); + ite_vhost->io_port = 0; + ite_vhost->n_io_port = 0; + ite_vhost->max_channel = 0; + ite_vhost->max_id = MAX_DEVICES; + ite_vhost->max_lun = 1; + +#if (0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) + scsi_set_device(ite_vhost, &pPciDev->dev); + +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4) + scsi_set_pci_device(ite_vhost, pPciDev); + +#endif +#endif + + /* + * Register the driver as a character device, for applications + * to access it for ioctls. Ideally, this should go in the + * init_module() routine, but since it is hidden in the file + * "scsi_module.c" (included in the end), we define it here. + * First argument (major) to register_chrdev implies a dynamic + * major number allocation. + */ + ite_major = register_chrdev(0, "itedev", &itedev_fops); + + /* + * Register the Shutdown Notification hook in the kernel. + */ + register_reboot_notifier(&ite_notifier); + + /* + * Initialize ioctl semphore. + */ + init_MUTEX(&mimd_entry_mtx); + } + dprintk("iteraid_detect exit\n"); + return 1; +} /* end iteraid_detect() */ + +/************************************************************************ + * Name: iteraid_release + * Description: Release resources allocated for a single each adapter. + * Parameters: pshost - Pointer to SCSI command structure. + * Returns: zero. + ************************************************************************/ +static int iteraid_release(struct Scsi_Host *pshost) +{ + u8 i; + PITE_ADAPTER pAdap; + + /* + * Unregister the character device. + */ + if (ite_major > 0) { + unregister_chrdev(ite_major, "itedev"); + ite_major = -1; + } + + /* + * Free irq and memory. + */ + for (i = 0; i < NumAdapters; i++) { + pAdap = ite_adapters[i]; + if (pAdap->irqOwned) + free_irq(pAdap->irq, pAdap); + if (pAdap->IDEChannel != NULL) { + kfree(pAdap->IDEChannel); + } + pci_disable_device(pAdap->pci_dev); + if (pAdap != NULL) { + kfree(pAdap); + } + } + + /* + * Unregister the reboot notifier. + */ + unregister_reboot_notifier(&ite_notifier); + + /* + * Tell kernel scsi-layer we are gone. + */ + scsi_unregister(pshost); + return 0; +} /* end iteraid_Release */ + +/************************************************************************ + * This is the new scsi eh reset function. + ************************************************************************/ +static int iteraid_reset_eh(Scsi_Cmnd * SCpnt) +{ + u8 i; + u8 j; + PChannel pChan; + PITE_ADAPTER pAdap; + + if (SCpnt == NULL) { + printk("iteraid_reset_eh: invalid Scsi_Cmnd\n"); + return FAILED; + } + for (i = 0; i < NumAdapters; i++) { + pAdap = ite_adapters[i]; + for (j = 0; j < pAdap->num_channels; j++) { + pChan = &pAdap->IDEChannel[j]; + AtapiResetController(pAdap, pChan); + } + } + return SUCCESS; +} /* end iteraid_reset_eh */ + +/************************************************************************ + * The new error handling code. + ************************************************************************/ +static int iteraid_abort_eh(Scsi_Cmnd * SCpnt) +{ + if (SCpnt == NULL) { + printk("iteraid_reset_eh: invalid Scsi_Cmnd\n"); + return FAILED; + } + return SUCCESS; +} /* end iteraid_abort_eh */ + +/************************************************************************ + * Name: iteraid_biosparam + * Description: Process the biosparam request from the SCSI manager to + * return C/H/S data. + * Parameters: disk - Pointer to SCSI disk structure. + * dev - Major/minor number from kernel. + * geom - Pointer to integer array to place geometry data. + * Returns: zero. + ************************************************************************/ +static int iteraid_biosparam(struct scsi_device *sdev, + struct block_device *bdev, sector_t capacity, int geom[]) +{ + int heads; + int sectors; + int cylinders; + + /* + * Default heads (64) & sectors (32) + * Handle extended translation size for logical drives > 1Gb + */ + if (capacity >= 0x200000) { + heads = 255; + sectors = 63; + } else { + heads = 64; + sectors = 32; + } + cylinders = (unsigned long)capacity / (heads * sectors); + + /* + * Return result + */ + geom[0] = heads; + geom[1] = sectors; + geom[2] = cylinders; + return 0; +} /* end iteraid_biosparam */ + +/************************************************************************ + * Shutdown routine. + ************************************************************************/ +static int ite_halt(struct notifier_block *nb, ulong event, void *buf) +{ + if (event != SYS_RESTART && event != SYS_HALT && + event != SYS_POWER_OFF) { + return NOTIFY_DONE; + } + unregister_reboot_notifier(&ite_notifier); + return NOTIFY_OK; +} /* end ite_halt */ + +/************************************************************************ + * PROC information. + ************************************************************************/ +static int iteraid_proc_info(struct Scsi_Host *shost, char *buffer, + char **start, off_t offset, int length, int inout) +{ + return 0; +} /* end iteraid_proc_info */ + +/************************************************************************ + * IOCTL open entry. + ************************************************************************/ +static int itedev_open(struct inode *inode, struct file *filep) +{ + + //MOD_INC_USE_COUNT; + return 0; +} /* end itedev_open */ + +/************************************************************************ + * IOCTL code entry. + ************************************************************************/ +static int itedev_ioctl_entry(struct inode *inode, struct file *filep, + unsigned int cmd, unsigned long arg) +{ + int ret = -1; + + /* + * We do not allow parallel ioctls to the driver as of now. + */ + down(&mimd_entry_mtx); + ret = itedev_ioctl(inode, filep, cmd, arg); + up(&mimd_entry_mtx); + return ret; +} /* end itedev_ioctl_entry */ + +/************************************************************************ + * Real IOCTL function handles ioctl for the character device. + ************************************************************************/ +static int itedev_ioctl(struct inode *inode, struct file *filep, + unsigned int cmd, unsigned long arg) +{ + u8 diskArrayId; + u8 statusByte = 0; + u8 srbStatus; + u8 progress = 0; + u8 status = 0; + uioctl_t *pioc; + PITE_ADAPTER pAdap; + PChannel pChan; + PRAID_REBUILD_INFO rebuild_info; + dprintk("itedev_ioctl enter\n"); + + /* + * Extract the type and number bitfield. + */ + if (_IOC_TYPE(cmd) != ITE_IOCMAGIC) + return -EINVAL; + + /* + * Allocate space for ioctl data structure. + */ + if ((pioc = kmalloc(sizeof(uioctl_t), GFP_KERNEL)) == NULL) { + printk("itedev_ioctl: error kmalloc on ioctl\n"); + return -ENOMEM; + } + + /* + * Get the user ioctl structure. + */ + if (copy_from_user(pioc, (uioctl_t *) arg, sizeof(uioctl_t))) { + kfree(pioc); + return -EFAULT; + } + + /* + * Check which command to do. + */ + switch (cmd) { + case ITE_IOC_GET_PHY_DISK_STATUS: + dprintk("ITE_IOC_GET_PHY_DISK_STATUS\n"); + + /* + * Get the physical disk status. + */ + status = IT8212GetChipStatus(pioc); + return 0; + case ITE_IOC_CREATE_DISK_ARRAY: + dprintk("ITE_IOC_CREATE_DISK_ARRAY\n"); + + /* + * Create disk array. + */ + status = IT8212CreateDiskArray(pioc); + if (status != SRB_STATUS_SUCCESS) + return status; + status = IT8212ErasePartition(pioc); + return 0; + case ITE_IOC_REBUILD_START: + dprintk("ITE_IOC_REBUILD_START\n"); + + /* + * Rebuild array. + */ + status = IT8212Rebuild(pioc); + put_user(status, (u8 *) pioc->data); + return 0; + case ITE_IOC_GET_REBUILD_STATUS: + dprintk("ITE_IOC_GET_REBUILD_STATUS\n"); + pAdap = ite_adapters[0]; + + /* + * Get the rebuild disk ID. + */ + rebuild_info = (PRAID_REBUILD_INFO) pioc->data; + diskArrayId = rebuild_info->DiskArrayId; + + /* + * Select channel. + */ + if (diskArrayId < 2) + pChan = &pAdap->IDEChannel[0]; + else + pChan = &pAdap->IDEChannel[1]; + + /* + * Select device. + */ + outb(((u8) (diskArrayId << 4) | 0xA0), + pChan->io_ports[IDE_SELECT_OFFSET]); + + /* + * Wait for device ready (not BUSY and not DRQ). + */ + WaitForDeviceReady(pChan, statusByte); + if ((statusByte & IDE_STATUS_BUSY) + || (statusByte & IDE_STATUS_DRQ) || (statusByte == 0)) { + printk("IT8212GetRebuildStatus: Disk[%d] busy. " + "Status=0x%X\n", + diskArrayId, statusByte); + srbStatus = SRB_STATUS_BUSY; + goto exit; + } + + /* + * Disable interrupt to avoid the unexpected interrupt. + */ + outb(IDE_DC_DISABLE_INTERRUPTS, + pChan->io_ports[IDE_CONTROL_OFFSET]); + + /* + * Issue command. + */ + outb(IDE_COMMAND_REBUILD_STATUS, + pChan->io_ports[IDE_COMMAND_OFFSET]); + + /* + * Check error. + */ + WaitForCommandComplete(pChan, statusByte); + + /* + * Reenable interrupt after command complete. + */ + outb(IDE_DC_REENABLE_CONTROLLER, + pChan->io_ports[IDE_CONTROL_OFFSET]); + if (statusByte != IDE_STATUS_IDLE) { + srbStatus = SRB_STATUS_ERROR; + printk("GetRebuildStatus: ERROR\n"); + goto exit; + } + progress = inb(pChan->io_ports[IDE_NSECTOR_OFFSET]); + srbStatus = SRB_STATUS_SUCCESS; + if (progress != 0xFF) + progress = 0x64 - progress; + + /* + * Put the rebuild status to user space. + */ + return put_user(progress, (u8 *) pioc->data); +exit: + return 0; + case ITE_IOC_RESET_ADAPTER: + dprintk("ITE_IOC_RESET_ADAPTER\n"); + + /* + * Reset the adapter. + */ +#if (0) + status = IT8212ResetAdapter(); + +#endif + + /* + * Return TURE or FALSE to user space. + */ + put_user(status, (u8 *) arg); + return 0; + case ITE_IOC_GET_DRIVER_VERSION: + dprintk("ITE_IOC_GET_DRIVER_VERSION\n"); + + /* + * Get the current driver version. + */ + put_user(driver_ver, (int *)arg); + return 0; + default: + return -EINVAL; + } /* end switch */ +} /* end itedev_ioctl */ + +/************************************************************************ + * IOCTL close routine. + ************************************************************************/ +static int itedev_close(struct inode *inode, struct file *filep) +{ + + //MOD_DEC_USE_COUNT; + return 0; +} /* end itedev_close */ + +/************************************************************************ + * Scsi_Host_Template Initializer + ************************************************************************/ +static Scsi_Host_Template driver_template = { + .proc_name = "IT8212", + .proc_info = iteraid_proc_info, + .name = "ITE RAIDExpress133", + .detect = iteraid_detect, + .release = iteraid_release, + .queuecommand = iteraid_queuecommand, + .eh_abort_handler = iteraid_abort_eh, + .eh_host_reset_handler = iteraid_reset_eh, + .bios_param = iteraid_biosparam, + .can_queue = 1, + .this_id = -1, + .sg_tablesize = 32, + .max_sectors = 256, + .cmd_per_lun = 1, + .use_clustering = DISABLE_CLUSTERING, + .emulated = 1, +}; + +#include "scsi_module.c" --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/scsi/iteraid.h 2004-09-03 00:56:53.238067232 -0700 @@ -0,0 +1,1463 @@ +/* + * linux/drivers/scsi/iteraid.h + * + * (C) Copyright 2002-2004 ITE Tech, inc. + * + * Nov 11, 2002 Mark Lu file created. + * + * Aug 2, 2004 Donald Huang published the ITE driver. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ITE IT8212 RAID Controller device driver header for Linux. + */ + +#ifndef _ITERAID_H_ +#define _ITERAID_H_ + +#include +#include + +#define ITE_VENDOR_ID 0x1283 /* Vendor ID (ITE) */ +#define ITE_DEVICE_ID 0x8212 /* Device IF (IT8212) */ +#define MAX_ADAPTERS 2 /* Max Board supported */ +#define MAX_DEVICES (MAX_ADAPTERS * 4) /* Max Dev supported */ + +#define TRUE 1 +#define FALSE 0 + +/* + * Undef macros which may conflict + */ +#undef START_STOP + +/************************************************************************ + * Debugging macro + ************************************************************************/ +#ifdef ITE_DEBUG +#define dprintk(msg...) printk(msg) +#else +#define dprintk(msg...) do { } while(0) +#endif + +/************************************************************************ + * Raid level definitions + ************************************************************************/ +#define RAID_LEVEL_0 0 +#define RAID_LEVEL_1 1 +#define RAID_LEVEL_10 2 +#define RAID_LEVEL_JBOD 3 +#define RAID_LEVEL_NORMAL 4 +#define RAID_LEVEL_NODISK 5 + +/************************************************************************ + * Physical disk status definitions + ************************************************************************/ +#define DISK_KEY_OFF 0 +#define DISK_OFF_LINE 1 +#define DISK_ON_LINE 2 +#define DISK_REBUILDING 3 +#define DISK_PLUGGING 4 +#define DISK_PLUGGING_OK 5 + +#define MaximumLBAOf28Bit 0x10000000 + +#define DisableChannel 1 +#define EnableChannel 2 + +#define CABLE_40_PIN 0 +#define CABLE_80_PIN 1 + +#define RaidActive 0 +#define RaidInactive 1 + +#define IDE_CLOCK_66 0 +#define IDE_CLOCK_50 1 + +#define USE_ULTRA_DMA 0 +#define USE_MULTIWORD_DMA 1 + +/************************************************************************ + * Vendor specific information + ************************************************************************/ +typedef struct _PHYSICAL_DISK_STATUS { + u8 ModelNumber[40]; /* Byte 00-39 */ + u32 UserAddressableSectors_LOW; /* Byte 40-43 */ + u32 UserAddressableSectors_HIGH; /* Byte 44-47 */ + u8 MultiWordDMASupport; /* Byte 48 */ + u8 MultiWordDMAActive; /* Byte 49 */ + u8 UltraDMASupport; /* Byte 50 */ + u8 UltraDMAActive; /* Byte 51 */ + u8 RaidType; /* Byte 52 */ + u8 RaidNumber; /* Byte 53 */ + u8 SerialNumber[20]; /* Byte 54-73 */ + u8 DiskStatus; /* Byte 74 */ + u8 DiskOriginalLocation; /* Byte 75 */ + u8 Cable80Pin; /* Byte 76 */ + u8 BootableDisk; /* Byte 77 */ + u8 StorageSize[8]; /* Byte 78-85 */ + u8 Reserved[35]; /* Byte 86-120 */ + u8 UpdateYear; /* Byte 121 */ + u8 UpdateMonth; /* Byte 122 */ + u8 UpdateDay; /* Byte 123 */ + u8 FirmwareVer; /* Byte 124 */ + u8 RebuildStatus; /* Byte 125 */ + u8 StripeSize; /* Byte 126 */ + u8 AutoRebuildEnable; /* Byte 127 */ +} PHYSICAL_DISK_STATUS, *PPHYSICAL_DISK_STATUS; + +/************************************************************************ + * vendor specific information + ************************************************************************/ +typedef struct _IT8212_SET_CHIP_STATUS_INFO { + u16 RaidType; /* Word 129 */ + u16 ContainingDisks; /* Word 130 */ + u16 UltraDmaTiming01; /* Word 131 */ + u16 UltraDmaTiming23; /* Word 132 */ + u16 UltraDmaTiming45; /* Word 133 */ + u16 UltraDmaTiming6; /* Word 134 */ + u16 MultiWordDmaTiming01; /* Word 135 */ + u16 UltraDmaTiming2; /* Word 136 */ + u16 PioTiming4; /* Word 137 */ + u16 AutoRebuildEnable; /* Word 138 */ + u16 IdeClkUDma01; /* Word 139 */ + u16 IdeClkUDma23; /* Word 140 */ + u16 IdeClkUDma45; /* Word 141 */ + u16 IdeClkUDma6; /* Word 142 */ + u16 IdeClkMDma01; /* Word 143 */ + u16 IdeClkMDma2; /* Word 144 */ + u16 IdeClkPio4; /* Word 145 */ + u16 StripeSize; /* Word 146 */ + u16 BootableDisk; /* Word 147 */ + u16 CheckHotSwapInterval; /* Word 148 */ + u16 TargetSourceDisk; /* Word 149 */ + u16 RebuildBlockSize; /* Word 150 */ + u16 ResetInterval1; /* Word 151 */ + u16 ResetInterval2; /* Word 152 */ + u16 RebuildRetryTimes; /* Word 153 */ + u16 NewlyCreated; /* Word 154 */ +} IT8212_SET_CHIP_STATUS_INFO, *PIT8212_SET_CHIP_STATUS_INFO; + +/************************************************************************ + * Serial number written to HDD (20 bytes) + ************************************************************************/ +typedef struct _RAID_SERIAL_NUMBER { + u16 Year; + u8 Month; + u8 Date; + u8 Day; + u8 Hour; + u8 Minute; + u8 Second; + u8 MiniSec; + u8 RaidType; + u8 ContainingDisks; + u8 DontCare[9]; +} RAID_SERIAL_NUMBER, *PRAID_SERIAL_NUMBER; + +/************************************************************************ + * Disk array create information + * + * Following items index definition + * 0: Primary Master + * 1: Secondary Master + * 2: Primary Slave + * 3: Secondary Slave + ************************************************************************/ +typedef struct _RAID_CREATE_INFO { + u8 DiskArrayId; + RAID_SERIAL_NUMBER SerialNumber; + u8 ModelNumber[40]; + u16 RaidType; + u16 ContainingDisks; + u16 AutoRebuildEnable; + u16 StripeSize; + u16 BootableDisk; + u16 TargetSourceDisk; + u8 ErasePartition; + u8 DMASupported[4]; + u8 UDMASupported[4]; + u8 AddressableSectors[4]; + u8 NewlyCreated; + u8 Reserved; +} RAID_CREATE_INFO, *PRAID_CREATE_INFO; + +/************************************************************************ + * Rebuild data structure + ************************************************************************/ +typedef struct _RAID_REBUILD_INFO { + u8 DiskArrayId; /* Virtual device number (0-3) */ + u8 SrcDisk; /* Source disk (0-3) */ + u8 DestDisk; /* Destination disk (0-3) */ + u8 Resume; /* 1: Resume the last time rebuild */ + /* 0: Rebuild from LBA 0 */ + u8 Status; /* Indicate the status of the current */ + /* rebuild command filled by drivers */ + u8 Reserved[3]; /* For aligement */ +} RAID_REBUILD_INFO, *PRAID_REBUILD_INFO; + +/************************************************************************ + * ATA transfer modes + ************************************************************************/ +#define PIO_DEFAULT 0x00 +#define PIO_DEFAULT_IORDY_DISABLE 0x01 +#define PIO_FLOW_CONTROL 0x08 +#define SINGLEWORD_DMA 0x10 +#define MULTIWORD_DMA 0x20 +#define ULTRA_DMA 0x40 + +#define ITE_DRV_SIGNATURE "ITE RAID CONTROLLER" +#define ITE_DRV_BYPASS "ITE BYPASS MODE" + +/************************************************************************ + * Extra IDE commands supported by Accusys + ************************************************************************/ +#define IDE_COMMAND_GET_CHIP_STATUS 0xFA +#define IDE_COMMAND_SET_CHIP_STATUS 0xFB +#define IDE_COMMAND_REBUILD 0xFC +#define IDE_COMMAND_REBUILD_STATUS 0xFD + +#define REBUILD_ERR_WRONG_ARRAY_TYPE 0x01 +#define REBUILD_ERR_DISK_TOO_SMALL 0x02 +#define REBUILD_ERR_SRC_DISK_LOCATION_INCORRECT 0x03 +#define REBUILD_ERR_SRC_DISK_OFFLINE 0x04 +#define REBUILD_ERR_DEST_DISK_OFFLINE 0x05 +#define REBUILD_ERR_DISK_BUSY 0x10 + +/************************************************************************ + * ATA transfer modes + ************************************************************************/ +#define PIO_DEFAULT 0x00 +#define PIO_DEFAULT_IORDY_DISABLE 0x01 +#define PIO_FLOW_CONTROL 0x08 +#define SINGLEWORD_DMA 0x10 +#define MULTIWORD_DMA 0x20 +#define ULTRA_DMA 0x40 + +/************************************************************************ + * IDE registers offset + ************************************************************************/ +#define IDE_NR_PORTS 10 + +#define IDE_DATA_OFFSET 0 +#define IDE_ERROR_OFFSET 1 +#define IDE_NSECTOR_OFFSET 2 +#define IDE_LOCYL_OFFSET 3 +#define IDE_MIDCYL_OFFSET 4 +#define IDE_HCYL_OFFSET 5 +#define IDE_SELECT_OFFSET 6 +#define IDE_STATUS_OFFSET 7 +#define IDE_CONTROL_OFFSET 8 +#define IDE_IRQ_OFFSET 9 + +#define IDE_FEATURE_OFFSET IDE_ERROR_OFFSET +#define IDE_COMMAND_OFFSET IDE_STATUS_OFFSET +#define IDE_ALTERNATE_OFFSET IDE_CONTROL_OFFSET + +/************************************************************************ + * ATAPI registers offset + ************************************************************************/ +#define ATAPI_DATA_OFFSET 0 +#define ATAPI_ERROR_OFFSET 1 +#define ATAPI_INTREASON_OFFSET 2 +#define ATAPI_UNUSED1_OFFSET 3 +#define ATAPI_LCYL_OFFSET 4 +#define ATAPI_HCYL_OFFSET 5 +#define ATAPI_SELECT_OFFSET 6 +#define ATAPI_STATUS_OFFSET 7 +#define ATAPI_CONTROL_OFFSET 8 + +#define ATAPI_COMMAND_OFFSET ATAPI_STATUS_OFFSET +#define ATAPI_FEATURE_OFFSET ATAPI_ERROR_OFFSET + +/************************************************************************ + * Following structures are according to SPC-3 (by Chanel) + ************************************************************************/ +typedef struct _SCSI_MODE_SENSE6 { + u8 OperationCode; + u8 Reserved1:3; + u8 Dbd:1; + u8 Reserved2:4; + u8 PageCode:6; + u8 Pc:2; + u8 SubpageCode; + u8 AllocationLength; + u8 Control; +} SCSI_MODE_SENSE6, *PSCSI_MODE_SENSE6; + +typedef struct _SCSI_MODE_SENSE10 { + u8 OperationCode; + u8 Reserved1:3; + u8 Dbd:1; + u8 LLBAA:1; + u8 Reserved2:3; + u8 PageCode:6; + u8 Pc:2; + u8 SubpageCode; + u8 Reserved3[3]; + u8 AllocationLengthMsb; + u8 AllocationLengthLsb; + u8 Control; +} SCSI_MODE_SENSE10, *PSCSI_MODE_SENSE10; + +typedef struct _SCSI_MODE_SELECT6 { + u8 OperationCode; + u8 SPBit:1; + u8 Reserved1:3; + u8 PFBit:1; + u8 Reserved2:3; + u8 Reserved3[2]; + u8 ParameterListLength; + u8 Control; +} SCSI_MODE_SELECT6, *PSCSI_MODE_SELECT6; + +typedef struct _SCSI_MODE_SELECT10 { + u8 OperationCode; + u8 SPBit:1; + u8 Reserved1:3; + u8 PFBit:1; + u8 Reserved2:3; + u8 Reserved3[5]; + u8 ParameterListLengthMsb; + u8 ParameterListLengthLsb; + u8 Control; +} SCSI_MODE_SELECT10, *PSCSI_MODE_SELECT10; + +typedef struct _SCSI_MODE_PARAMETER_HEADER6 { + u8 ModeDataLength; + u8 MediumType; + u8 DeviceSpecificParameter; + u8 BlockDescriptorLength; +} SCSI_MODE_PARAMETER_HEADER6, *PSCSI_MODE_PARAMETER_HEADER6; + +typedef struct _SCSI_MODE_PARAMETER_HEADER10 { + u8 ModeDataLengthMsb; + u8 ModeDataLengthLsb; + u8 MediumType; + u8 DeviceSpecificParameter; + u8 Reserved[2]; + u8 BlockDescriptorLengthMsb; + u8 BlockDescriptorLengthLsb; +} SCSI_MODE_PARAMETER_HEADER10, *PSCSI_MODE_PARAMETER_HEADER10; + +typedef struct _SCSI_MODE_PARAMTER_BLOCK_DESCRIPTER { + u8 DesityCode; + u8 NumberOfBlocks2; + u8 NumberOfBlocks1; + u8 NumberOfBlocks0; + u8 Reserved; + u8 BlockLength2; + u8 BlockLength1; + u8 BlockLength0; +} SCSI_MODE_PARAMTER_BLOCK_DESCRIPTER, *PSCSI_MODE_PARAMTER_BLOCK_DESCRIPTER; + +/************************************************************************ + * IDE command definitions + ************************************************************************/ +#define IDE_COMMAND_ATAPI_RESET 0x08 +#define IDE_COMMAND_RECALIBRATE 0x10 +#define IDE_COMMAND_READ_SECTOR 0x20 +#define IDE_COMMAND_READ_SECTOR_EXT 0x24 +#define IDE_COMMAND_READ_DMA_EXT 0x25 +#define IDE_COMMAND_READ_MULTIPLE_EXT 0x29 +#define IDE_COMMAND_WRITE_SECTOR 0x30 +#define IDE_COMMAND_WRITE_SECTOR_EXT 0x34 +#define IDE_COMMAND_WRITE_DMA_EXT 0x35 +#define IDE_COMMAND_WRITE_MULTIPLE_EXT 0x39 +#define IDE_COMMAND_READ_VERIFY 0x40 +#define IDE_COMMAND_READ_VERIFY_EXT 0x42 +#define IDE_COMMAND_SEEK 0x70 +#define IDE_COMMAND_SET_DRIVE_PARAMETERS 0x91 +#define IDE_COMMAND_ATAPI_PACKET 0xA0 +#define IDE_COMMAND_ATAPI_IDENTIFY 0xA1 +#define IDE_COMMAND_READ_MULTIPLE 0xC4 +#define IDE_COMMAND_WRITE_MULTIPLE 0xC5 +#define IDE_COMMAND_SET_MULTIPLE 0xC6 +#define IDE_COMMAND_READ_DMA 0xC8 +#define IDE_COMMAND_WRITE_DMA 0xCA +#define IDE_COMMAND_GET_MEDIA_STATUS 0xDA +#define IDE_COMMAND_ENABLE_MEDIA_STATUS 0xEF +#define IDE_COMMAND_SET_FEATURE 0xEF +#define IDE_COMMAND_IDENTIFY 0xEC +#define IDE_COMMAND_MEDIA_EJECT 0xED + +/************************************************************************ + * IDE status definitions + ************************************************************************/ +#define IDE_STATUS_ERROR 0x01 +#define IDE_STATUS_INDEX 0x02 +#define IDE_STATUS_CORRECTED_ERROR 0x04 +#define IDE_STATUS_DRQ 0x08 +#define IDE_STATUS_DSC 0x10 +#define IDE_STATUS_DRDY 0x40 +#define IDE_STATUS_IDLE 0x50 +#define IDE_STATUS_BUSY 0x80 + +/************************************************************************ + * IDE drive control definitions. + ************************************************************************/ +#define IDE_DC_DISABLE_INTERRUPTS 0x02 +#define IDE_DC_RESET_CONTROLLER 0x04 +#define IDE_DC_REENABLE_CONTROLLER 0x00 + +/************************************************************************ + * IDE error definitions. + ************************************************************************/ +#define IDE_ERROR_BAD_BLOCK 0x80 +#define IDE_ERROR_DATA_ERROR 0x40 +#define IDE_ERROR_MEDIA_CHANGE 0x20 +#define IDE_ERROR_ID_NOT_FOUND 0x10 +#define IDE_ERROR_MEDIA_CHANGE_REQ 0x08 +#define IDE_ERROR_COMMAND_ABORTED 0x04 +#define IDE_ERROR_END_OF_MEDIA 0x02 +#define IDE_ERROR_ILLEGAL_LENGTH 0x01 + +/************************************************************************ + * IDENTIFY data. + ************************************************************************/ +typedef struct _IDENTIFY_DATA { + u16 GeneralConfiguration; /* 00 00 */ + u16 NumberOfCylinders; /* 02 1 */ + u16 Reserved1; /* 04 2 */ + u16 NumberOfHeads; /* 06 3 */ + u16 UnformattedBytesPerTrack; /* 08 4 */ + u16 UnformattedBytesPerSector; /* 0A 5 */ + u16 SectorsPerTrack; /* 0C 6 */ + u16 VendorUnique1[3]; /* 0E 7-9 */ + u16 SerialNumber[10]; /* 14 10-19 */ + u16 BufferType; /* 28 20 */ + u16 BufferSectorSize; /* 2A 21 */ + u16 NumberOfEccBytes; /* 2C 22 */ + u16 FirmwareRevision[4]; /* 2E 23-26 */ + u16 ModelNumber[20]; /* 36 27-46 */ + u8 MaximumBlockTransfer; /* 5E 47 */ + u8 VendorUnique2; /* 5F */ + u16 DoubleWordIo; /* 60 48 */ + u16 Capabilities; /* 62 49 */ + u16 Reserved2; /* 64 50 */ + u8 VendorUnique3; /* 66 51 */ + u8 PioCycleTimingMode; /* 67 */ + u8 VendorUnique4; /* 68 52 */ + u8 DmaCycleTimingMode; /* 69 */ + u16 TranslationFieldsValid:1; /* 6A 53 */ + u16 Reserved3:15; /* */ + u16 NumberOfCurrentCylinders; /* 6C 54 */ + u16 NumberOfCurrentHeads; /* 6E 55 */ + u16 CurrentSectorsPerTrack; /* 70 56 */ + u32 CurrentSectorCapacity; /* 72 57-58 */ + u16 CurrentMultiSectorSetting; /* 59 */ + u32 UserAddressableSectors; /* 60-61 */ + u16 SingleWordDMASupport:8; /* 62 */ + u16 SingleWordDMAActive:8; /* */ + u16 MultiWordDMASupport:8; /* 63 */ + u16 MultiWordDMAActive:8; /* */ + u16 AdvancedPIOModes:8; /* 64 */ + u16 Reserved4:8; /* */ + u16 MinimumMWXferCycleTime; /* 65 */ + u16 RecommendedMWXferCycleTime; /* 66 */ + u16 MinimumPIOCycleTime; /* 67 */ + u16 MinimumPIOCycleTimeIORDY; /* 68 */ + u16 Reserved5[2]; /* 69-70 */ + u16 ReleaseTimeOverlapped; /* 71 */ + u16 ReleaseTimeServiceCommand; /* 72 */ + u16 MajorRevision; /* 73 */ + u16 MinorRevision; /* 74 */ + u16 Reserved6[50]; /* 75-126 */ + u16 SpecialFunctionsEnabled; /* 127 */ + u16 Reserved7[128]; /* 128-255 */ +} IDENTIFY_DATA, *PIDENTIFY_DATA; + +/************************************************************************ + * Identify data without the Reserved4. + ************************************************************************/ +typedef struct _IDENTIFY_DATA2 { + u16 GeneralConfiguration; /* 00 */ + u16 NumberOfCylinders; /* 01 */ + u16 Reserved1; /* 02 */ + u16 NumberOfHeads; /* 03 */ + u16 Reserved2[2]; /* 04-05 */ + u16 SectorsPerTrack; /* 06 */ + u16 Reserved3[3]; /* 07-09 */ + u16 SerialNumber[10]; /* 10-19 */ + u16 Reserved4[3]; /* 20-22 */ + u16 FirmwareRevision[4]; /* 23-26 */ + u16 ModelNumber[20]; /* 27-46 */ + u16 MaximumBlockTransfer; /* 47 */ + u16 Reserved5; /* 48 */ + u16 Capabilities[2]; /* 49-50 */ + u16 Reserved6[2]; /* 51-52 */ + u16 ValidFieldIndicator; /* 53 */ + u16 NumberOfCurrentCylinders; /* 54 */ + u16 NumberOfCurrentHeads; /* 55 */ + u16 CurrentSectorsPerTrack; /* 56 */ + u16 CurrentSectorCapacityLow; /* 57 */ + u16 CurrentSectorCapacityHigh; /* 58 */ + u16 CurrentMultiSectorSetting; /* 59 */ + u32 UserAddressableSectors; /* 60-61 */ + u16 Reserved7; /* 62 */ + u8 MultiWordDMASupport; /* 63 */ + u8 MultiWordDMAActive; /* */ + u16 AdvancedPIOModes; /* 64 */ + u16 MinimumMWXferCycleTime; /* 65 */ + u16 RecommendedMWXferCycleTime; /* 66 */ + u16 MinimumPIOCycleTime; /* 67 */ + u16 MinimumPIOCycleTimeIORDY; /* 68 */ + u16 Reserved8[6]; /* 69-74 */ + u16 QueueDepth; /* 75 */ + u16 Reserved9[4]; /* 76-79 */ + u16 MajorVersionNumber; /* 80 */ + u16 MinorVersionNumber; /* 81 */ + u32 CmdSetSupported; /* 82-83 */ + u16 CmdSetFeatureSupportedExt; /* 84 */ + u16 CmdSetFeatureEnabledLow; /* 85 */ + u16 CmdSetFeatureEnabledHigh; /* 86 */ + u16 CmdSetFeatureDefault; /* 87 */ + u8 UltraDMASupport; /* 88 */ + u8 UltraDMAActive; /* */ + u16 SecurityEraseTime; /* 89 */ + u16 EnhancedSecurityEraseTime; /* 90 */ + u16 PowerManagementValue; /* 91 */ + u16 MasterPasswordRevision; /* 92 */ + u16 HwResetResult; /* 93 */ + u16 Reserved11[6]; /* 94-99 */ + u32 Capacity_48bit_LOW; /* 100-101 */ + u32 Capacity_48bit_HIGH; /* 102-103 */ + u16 Reserved12[24]; /* 104-127 */ + u16 SecurityStatus; /* 128 */ + u16 Reserved13[31]; /* 129-159 vendor spec */ + u16 Reserved14[96]; /* 160-255 */ +} IDENTIFY_DATA2, *PIDENTIFY_DATA2; + +#define IDENTIFY_DATA_SIZE sizeof(IDENTIFY_DATA) + +/************************************************************************ + * IDENTIFY capability bit definitions. + ************************************************************************/ +#define IDENTIFY_CAPABILITIES_DMA_SUPPORTED 0x0100 +#define IDENTIFY_CAPABILITIES_LBA_SUPPORTED 0x0200 + +/************************************************************************ + * IDENTIFY DMA timing cycle modes. + ************************************************************************/ +#define IDENTIFY_DMA_CYCLES_MODE_0 0x00 +#define IDENTIFY_DMA_CYCLES_MODE_1 0x01 +#define IDENTIFY_DMA_CYCLES_MODE_2 0x02 + +typedef struct _SENSE_DATA { + u8 ErrorCode:7; + u8 Valid:1; + u8 SegmentNumber; + u8 SenseKey:4; + u8 Reserved:1; + u8 IncorrectLength:1; + u8 EndOfMedia:1; + u8 FileMark:1; + u8 Information[4]; + u8 AdditionalSenseLength; + u8 CommandSpecificInformation[4]; + u8 AdditionalSenseCode; + u8 AdditionalSenseCodeQualifier; + u8 FieldReplaceableUnitCode; + u8 SenseKeySpecific[3]; +} SENSE_DATA, *PSENSE_DATA; + +/************************************************************************ + * Sense codes + ************************************************************************/ +#define SCSI_SENSE_NO_SENSE 0x00 +#define SCSI_SENSE_RECOVERED_ERROR 0x01 +#define SCSI_SENSE_NOT_READY 0x02 +#define SCSI_SENSE_MEDIUM_ERROR 0x03 +#define SCSI_SENSE_HARDWARE_ERROR 0x04 +#define SCSI_SENSE_ILLEGAL_REQUEST 0x05 +#define SCSI_SENSE_UNIT_ATTENTION 0x06 +#define SCSI_SENSE_DATA_PROTECT 0x07 +#define SCSI_SENSE_BLANK_CHECK 0x08 +#define SCSI_SENSE_UNIQUE 0x09 +#define SCSI_SENSE_COPY_ABORTED 0x0A +#define SCSI_SENSE_ABORTED_COMMAND 0x0B +#define SCSI_SENSE_EQUAL 0x0C +#define SCSI_SENSE_VOL_OVERFLOW 0x0D +#define SCSI_SENSE_MISCOMPARE 0x0E +#define SCSI_SENSE_RESERVED 0x0F + +/************************************************************************ + * Additional Sense codes + ************************************************************************/ +#define SCSI_ADSENSE_NO_SENSE 0x00 +#define SCSI_ADSENSE_MAN_INTERV 0x03 +#define SCSI_ADSENSE_LUN_NOT_READY 0x04 +#define SCSI_ADSENSE_ILLEGAL_COMMAND 0x20 +#define SCSI_ADSENSE_ILLEGAL_BLOCK 0x21 +#define SCSI_ADSENSE_INVALID_LUN 0x25 +#define SCSI_ADSENSE_SELECT_TIMEOUT 0x45 +#define SCSI_ADSENSE_MUSIC_AREA 0xA0 +#define SCSI_ADSENSE_DATA_AREA 0xA1 +#define SCSI_ADSENSE_VOLUME_OVERFLOW 0xA7 + +#define SCSI_ADSENSE_NO_MEDIA_IN_DEVICE 0x3A +#define SCSI_ADWRITE_PROTECT 0x27 +#define SCSI_ADSENSE_MEDIUM_CHANGED 0x28 +#define SCSI_ADSENSE_BUS_RESET 0x29 +#define SCSI_ADSENSE_TRACK_ERROR 0x14 +#define SCSI_ADSENSE_SEEK_ERROR 0x15 +#define SCSI_ADSENSE_REC_DATA_NOECC 0x17 +#define SCSI_ADSENSE_REC_DATA_ECC 0x18 +#define SCSI_ADSENSE_ILLEGAL_MODE 0x64 +#define SCSI_ADSENSE_BAD_CDB 0x24 +#define SCSI_ADSENSE_BAD_PARM_LIST 0x26 +#define SCSI_ADSENSE_CANNOT_READ_MEDIUM 0x30 + +#define SCSISTAT_CHECK_CONDITION 0x02 + +/************************************************************************ + * Inquiry buffer structure. This is the data returned from the target + * after it receives an inquiry. + * + * This structure may be extended by the number of bytes specified + * in the field AdditionalLength. The defined size constant only + * includes fields through ProductRevisionLevel. + * + * The NT SCSI drivers are only interested in the first 36 bytes of data. + ************************************************************************/ + +#define INQUIRYDATABUFFERSIZE 36 + +typedef struct _INQUIRYDATA { + u8 DeviceType:5; + u8 DeviceTypeQualifier:3; + u8 DeviceTypeModifier:7; + u8 RemovableMedia:1; + u8 Versions; + u8 ResponseDataFormat; + u8 AdditionalLength; + u8 Reserved[2]; + u8 SoftReset:1; + u8 CommandQueue:1; + u8 Reserved2:1; + u8 LinkedCommands:1; + u8 Synchronous:1; + u8 Wide16Bit:1; + u8 Wide32Bit:1; + u8 RelativeAddressing:1; + u8 VendorId[8]; + u8 ProductId[16]; + u8 ProductRevisionLevel[4]; + u8 VendorSpecific[20]; + u8 Reserved3[40]; +} INQUIRYDATA, *PINQUIRYDATA; + +#define DIRECT_ACCESS_DEVICE 0x00 /* Disks */ + +/************************************************************************ + * Read Capacity Data - returned in Big Endian format + ************************************************************************/ +typedef struct _READ_CAPACITY_DATA { + u32 LogicalBlockAddress; + u32 BytesPerBlock; +} READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA; + +#define MAXIMUM_CDB_SIZE 12 + +/************************************************************************ + * CDB (Command Descriptor Block) + ************************************************************************/ +typedef union _CDB { + /* + * Standard 6-byte CDB + */ + struct _CDB6READWRITE { + u8 OperationCode; /* Opcode */ + u8 LogicalBlockMsb1:5; /* Logical block MSB 5-bit */ + u8 LogicalUnitNumber:3; /* LUN */ + u8 LogicalBlockMsb0; /* Logical block MSB 8-bit */ + u8 LogicalBlockLsb; /* Logical block LSB 8-bit */ + u8 TransferBlocks; /* Data length */ + u8 Control; /* Control byte */ + } CDB6READWRITE, *PCDB6READWRITE; + + /* + * Standard 10-byte CDB + */ + struct _CDB10 { + u8 OperationCode; + u8 Reserved1:5; + u8 LogicalUnitNumber:3; + u8 LogicalBlockByte0; + u8 LogicalBlockByte1; + u8 LogicalBlockByte2; + u8 LogicalBlockByte3; + u8 Reserved2; + u8 TransferBlocksMsb; + u8 TransferBlocksLsb; + u8 Control; + } CDB10, *PCDB10; + + struct _START_STOP { + u8 OperationCode; + u8 Immediate:1; + u8 Reserved1:4; + u8 LogicalUnitNumber:3; + u8 Reserved2[2]; + u8 Start:1; + u8 LoadEject:1; + u8 Reserved3:6; + u8 Control; + } START_STOP, *PSTART_STOP; + +} CDB, *PCDB; + +/************************************************************************ + * SCSI CDB operation codes + ************************************************************************/ +#define SCSIOP_TEST_UNIT_READY 0x00 +#define SCSIOP_REZERO_UNIT 0x01 +#define SCSIOP_REWIND 0x01 +#define SCSIOP_REQUEST_BLOCK_ADDR 0x02 +#define SCSIOP_REQUEST_SENSE 0x03 +#define SCSIOP_FORMAT_UNIT 0x04 +#define SCSIOP_READ_BLOCK_LIMITS 0x05 +#define SCSIOP_REASSIGN_BLOCKS 0x07 +#define SCSIOP_READ6 0x08 +#define SCSIOP_RECEIVE 0x08 +#define SCSIOP_WRITE6 0x0A +#define SCSIOP_PRINT 0x0A +#define SCSIOP_SEND 0x0A +#define SCSIOP_SEEK6 0x0B +#define SCSIOP_TRACK_SELECT 0x0B +#define SCSIOP_SLEW_PRINT 0x0B +#define SCSIOP_SEEK_BLOCK 0x0C +#define SCSIOP_PARTITION 0x0D +#define SCSIOP_READ_REVERSE 0x0F +#define SCSIOP_WRITE_FILEMARKS 0x10 +#define SCSIOP_FLUSH_BUFFER 0x10 +#define SCSIOP_SPACE 0x11 +#define SCSIOP_INQUIRY 0x12 +#define SCSIOP_VERIFY6 0x13 +#define SCSIOP_RECOVER_BUF_DATA 0x14 +#define SCSIOP_MODE_SELECT 0x15 +#define SCSIOP_RESERVE_UNIT 0x16 +#define SCSIOP_RELEASE_UNIT 0x17 +#define SCSIOP_COPY 0x18 +#define SCSIOP_ERASE 0x19 +#define SCSIOP_MODE_SENSE 0x1A +#define SCSIOP_START_STOP_UNIT 0x1B +#define SCSIOP_STOP_PRINT 0x1B +#define SCSIOP_LOAD_UNLOAD 0x1B +#define SCSIOP_RECEIVE_DIAGNOSTIC 0x1C +#define SCSIOP_SEND_DIAGNOSTIC 0x1D +#define SCSIOP_MEDIUM_REMOVAL 0x1E +#define SCSIOP_READ_CAPACITY 0x25 +#define SCSIOP_READ 0x28 +#define SCSIOP_WRITE 0x2A +#define SCSIOP_SEEK 0x2B +#define SCSIOP_LOCATE 0x2B +#define SCSIOP_WRITE_VERIFY 0x2E +#define SCSIOP_VERIFY 0x2F +#define SCSIOP_SEARCH_DATA_HIGH 0x30 +#define SCSIOP_SEARCH_DATA_EQUAL 0x31 +#define SCSIOP_SEARCH_DATA_LOW 0x32 +#define SCSIOP_SET_LIMITS 0x33 +#define SCSIOP_READ_POSITION 0x34 +#define SCSIOP_SYNCHRONIZE_CACHE 0x35 +#define SCSIOP_COMPARE 0x39 +#define SCSIOP_COPY_COMPARE 0x3A +#define SCSIOP_WRITE_DATA_BUFF 0x3B +#define SCSIOP_READ_DATA_BUFF 0x3C +#define SCSIOP_CHANGE_DEFINITION 0x40 +#define SCSIOP_READ_SUB_CHANNEL 0x42 +#define SCSIOP_READ_TOC 0x43 +#define SCSIOP_READ_HEADER 0x44 +#define SCSIOP_PLAY_AUDIO 0x45 +#define SCSIOP_PLAY_AUDIO_MSF 0x47 +#define SCSIOP_PLAY_TRACK_INDEX 0x48 +#define SCSIOP_PLAY_TRACK_RELATIVE 0x49 +#define SCSIOP_PAUSE_RESUME 0x4B +#define SCSIOP_LOG_SELECT 0x4C +#define SCSIOP_LOG_SENSE 0x4D +#define SCSIOP_MODE_SELECT10 0x55 +#define SCSIOP_MODE_SENSE10 0x5A +#define SCSIOP_LOAD_UNLOAD_SLOT 0xA6 +#define SCSIOP_MECHANISM_STATUS 0xBD +#define SCSIOP_READ_CD 0xBE + +#define DRIVER_NAME "Device Driver for IT8212 RAID Controller" +#define COMPANY_NAME "Integrated Technology Express, Inc." +#define CONTROLLER_NAME_IT8212 "IT8212 UDMA/ATA133 RAID Controller" +#define PROC_DIR_NAME "it8212" +#define ITE_MAX_CMDS 124 + +#define PCI_IOSEN 0x01 /* Enable IO space */ +#define PCI_BMEN 0x04 /* Enable IDE bus master */ + +/************************************************************************ + * PRD (Physical Region Descriptor) = Scatter-gather table + * + * | byte3 | byte2 | byte1 | byte0 | + * +--------------------------------------------+ + * | Memory Region Physical Base Address[31:1] | + * +----+----------------+----------------------+ + * |EOT | reserved | Byte count[15:1] | + * +----+----------------+----------------------+ + ************************************************************************/ +typedef struct _PRD_TABLE_ENTRY { + u32 PhysicalBaseAddress; /* Byte0 - Byte3 */ + u16 ByteCount; /* Byte4 - Byte5 */ + u16 EndOfTable; /* Byte6 - Byte7 */ +} PRD_TABLE_ENTRY, *PPRD_TABLE_ENTRY; + +#define SG_FLAG_EOT 0x8000 /* End of PRD */ +#define MAX_SG_DESCRIPTORS 17 /* 17 -- maximum 64K */ + +#define NUM_OF_PRD_TABLE_ENTRY 0x10 + +/************************************************************************ + * Bus master register bits definition + ************************************************************************/ +#define BM_CMD_FLG_START 0x01 +#define BM_CMD_FLG_WRTTOMEM 0x08 +#define BM_CMD_FLG_WRTTODSK 0x00 + +#define BM_STAT_FLG_ACTIVE 0x01 +#define BM_STAT_FLG_ERR 0x02 +#define BM_STAT_FLG_INT 0x04 +#define BM_DRV0_DMA_CAPABLE 0x20 +#define BM_DRV1_DMA_CAPABLE 0x40 + +#define BM_PRD_FLG_EOT 0x8000 + +/************************************************************************ + * SRB Functions + ************************************************************************/ +#define SRB_FUNCTION_EXECUTE_SCSI 0x00 +#define SRB_FUNCTION_IO_CONTROL 0x02 +#define SRB_FUNCTION_SHUTDOWN 0x07 +#define SRB_FUNCTION_FLUSH 0x08 + +/************************************************************************ + * SRB Status + ************************************************************************/ +#define SRB_STATUS_PENDING 0x00 +#define SRB_STATUS_SUCCESS 0x01 +#define SRB_STATUS_ABORTED 0x02 +#define SRB_STATUS_ABORT_FAILED 0x03 +#define SRB_STATUS_ERROR 0x04 +#define SRB_STATUS_BUSY 0x05 +#define SRB_STATUS_INVALID_REQUEST 0x06 +#define SRB_STATUS_INVALID_PATH_ID 0x07 +#define SRB_STATUS_NO_DEVICE 0x08 +#define SRB_STATUS_TIMEOUT 0x09 +#define SRB_STATUS_SELECTION_TIMEOUT 0x0A +#define SRB_STATUS_COMMAND_TIMEOUT 0x0B +#define SRB_STATUS_MESSAGE_REJECTED 0x0D +#define SRB_STATUS_BUS_RESET 0x0E +#define SRB_STATUS_PARITY_ERROR 0x0F +#define SRB_STATUS_REQUEST_SENSE_FAILED 0x10 +#define SRB_STATUS_NO_HBA 0x11 +#define SRB_STATUS_DATA_OVERRUN 0x12 +#define SRB_STATUS_UNEXPECTED_BUS_FREE 0x13 +#define SRB_STATUS_BAD_SRB_BLOCK_LENGTH 0x15 +#define SRB_STATUS_REQUEST_FLUSHED 0x16 +#define SRB_STATUS_INVALID_LUN 0x20 +#define SRB_STATUS_INVALID_TARGET_ID 0x21 +#define SRB_STATUS_BAD_FUNCTION 0x22 +#define SRB_STATUS_ERROR_RECOVERY 0x23 +#define SRB_STATUS_NEED_REQUEUE 0x24 + +/************************************************************************ + * SRB Status Masks + ************************************************************************/ +#define SRB_STATUS_QUEUE_FROZEN 0x40 +#define SRB_STATUS_AUTOSENSE_VALID 0x80 + +#define SRB_STATUS(Status) \ + (Status & ~(SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_QUEUE_FROZEN)) + +/************************************************************************ + * SRB Flag Bits + ************************************************************************/ +#define SRB_FLAGS_DATA_IN 0x00000040 +#define SRB_FLAGS_DATA_OUT 0x00000080 + +/************************************************************************ + * SRB Working flags define area + ************************************************************************/ +#define SRB_WFLAGS_USE_INTERNAL_BUFFER 0x00000001 +#define SRB_WFLAGS_IGNORE_ARRAY 0x00000002 +#define SRB_WFLAGS_HAS_CALL_BACK 0x00000004 +#define SRB_WFLAGS_MUST_DONE 0x00000008 +#define SRB_WFLAGS_ON_MIRROR_DISK 0x00000010 +#define SRB_WFLAGS_ON_SOURCE_DISK 0x00000020 +#define SRB_WFLAGS_ARRAY_IO_STARTED 0x10000000 +#define SRB_WFLAGS_WATCHTIMER_CALLED 0x20000000 +#define SRB_WFLAGS_USE_SG 0x40000000 + +/************************************************************************ + * SCSI I/O Request Block + ************************************************************************/ +typedef struct _SCSI_REQUEST_BLOCK { + u16 Length; + u8 Function; + u8 SrbStatus; + u8 ScsiStatus; + u8 TargetId; + u8 Lun; + u8 CdbLength; + u8 SenseInfoBufferLength; + u8 UseSg; + u8 reseved[2]; + u32 WorkingFlags; + u32 SrbFlags; + u32 DataTransferLength; + u32 TimeOutValue; + void *DataBuffer; + void *SenseInfoBuffer; + u8 Cdb[16]; + Scsi_Cmnd *pREQ; +} SCSI_REQUEST_BLOCK, *PSCSI_REQUEST_BLOCK; + +#define SCSI_REQUEST_BLOCK_SIZE sizeof(SCSI_REQUEST_BLOCK) + +/************************************************************************ + * Second device flags + ***********************************************************************/ +#define DFLAGS_REDUCE_MODE 0x00010000 +#define DFLAGS_DEVICE_DISABLED 0x00020000 +#define DFLAGS_BOOTABLE_DEVICE 0x00080000 +#define DFLAGS_BOOT_MARK 0x00100000 +#define DFLAGS_NEW_ADDED 0x40000000 +#define DFLAGS_REMAINED_MEMBER 0x80000000 + +/************************************************************************ + * Device Extension Device Flags + ************************************************************************/ +/* + * Indicates that some device is present. + */ +#define DFLAGS_DEVICE_PRESENT 0x0001 + +/* + * Indicates whether ATAPI commands can be used. + */ +#define DFLAGS_ATAPI_DEVICE 0x0002 + +/* + * Indicates whether this is a tape device. + */ +#define DFLAGS_TAPE_DEVICE 0x0004 + +/* + * Indicates whether device interrupts as DRQ is set after + * receiving Atapi Packet Command. + */ +#define DFLAGS_INT_DRQ 0x0008 + +/* + * Indicates that the drive has the 'removable' bit set in + * identify data (offset 128) + */ +#define DFLAGS_REMOVABLE_DRIVE 0x0010 + +/* + * Media status notification enabled. + */ +#define DFLAGS_MEDIA_STATUS_ENABLED 0x0020 + +/* + * Indicates atapi 2.5 changer present. + */ +#define DFLAGS_ATAPI_CHANGER 0x0040 + +/* + * Indicates multi-platter device, not conforming to the 2.5 spec. + */ +#define DFLAGS_SANYO_ATAPI_CHANGER 0x0080 + +/* + * Indicates that the init path for changers has already been done. + */ +#define DFLAGS_CHANGER_INITED 0x0100 +#define DFLAGS_CONFIG_CHANGED 0x0200 + +#define UDMA_MODE_5_6 0x80 + +/************************************************************************ + * Used to disable 'advanced' features. + ************************************************************************/ +#define MAX_ERRORS 4 + +/************************************************************************ + * ATAPI command definitions + ************************************************************************/ +#define ATAPI_MODE_SENSE 0x5A +#define ATAPI_MODE_SELECT 0x55 +#define ATAPI_FORMAT_UNIT 0x24 + +/************************************************************************ + * User IOCTL structure + * Notes: + * (1) Data transfers are limited to PAGE_SIZE (4k on i386, 8k for alpha) + ************************************************************************/ +typedef struct _uioctl_t { + u16 inlen; /* Length of data written to device */ + u16 outlen; /* Length of data read from device */ + void *data; /* Data read from devic starts here */ + u8 status; /* Status return from driver */ + u8 reserved[3]; /* For 4-byte alignment */ +} uioctl_t; + +/************************************************************************ + * IOCTL commands for RAID + ************************************************************************/ +#define ITE_IOCMAGIC 't' + +#define ITE_IOC_GET_PHY_DISK_STATUS _IO(ITE_IOCMAGIC, 1) +#define ITE_IOC_CREATE_DISK_ARRAY _IO(ITE_IOCMAGIC, 2) +#define ITE_IOC_REBUILD_START _IO(ITE_IOCMAGIC, 3) +#define ITE_IOC_GET_REBUILD_STATUS _IO(ITE_IOCMAGIC, 4) +#define ITE_IOC_RESET_ADAPTER _IO(ITE_IOCMAGIC, 5) +#define ITE_IOC_GET_DRIVER_VERSION _IO(ITE_IOCMAGIC, 6) + +/************************************************************************ + * _Channel + ************************************************************************/ +typedef struct _Channel { + /* + * IDE (ATAPI) io port address. + */ + unsigned long io_ports[IDE_NR_PORTS]; + + /* + * DMA base address. + */ + unsigned long dma_base; + + /* + * Flags word for each possible device. + */ + u16 DeviceFlags[2]; + + /* + * Indicates number of platters on changer-ish devices. + */ + u32 DiscsPresent[2]; + + /* + * Indicates expecting an interrupt. + */ + u8 ExpectingInterrupt; + + /* + * Indicate last tape command was DSC Restrictive. + */ + u8 RDP; + + /* + * Interrupt level. + */ + u8 InterruptLevel; + + /* + * Placeholder for status register after a GET_MEDIA_STATUS command. + */ + u8 ReturningMediaStatus; + + /* + * Remember the channel number (0, 1) + */ + u8 channel; + + /* + * Indicates cable status. + */ + u8 Cable80[2]; + + /* + * Reserved for alignment. + */ + u8 reserved1[0]; + + /* + * Data buffer pointer. + */ + unsigned short *DataBuffer; + + /* + * Data words left. + */ + u32 WordsLeft; + + /* + * Retry count. + */ + u32 RetryCount; + + /* + * Keep DMA type (MULTIWORD_DMA or ULTRA_DMA) for each device. + */ + u8 DmaType[2]; + + /* + * Keep UDMA timing for each device. + */ + u8 UdmaTiming[2]; + + /* + * Keep PIO/DMA timing for each channel. PioDmaTiming[clock][channel] + */ + u8 PioDmaTiming[2]; + + /* + * Keep IDE clock (50 MHz or 66 MHz) for each device. + */ + u8 IdeClock[2]; + + /* + * Keep the active device for each channel. + */ + u8 ActiveDevice; + + /* + * Indicate whether we should perform DMA mode switch on this channel? + */ + u8 DoSwitch; + + /* + * ??? + */ + u8 ConvertCdb; + + /* + * Use or do not use DMA. + */ + u8 UseDma[2]; + + /* + * Reserved for alignment. + */ + u8 reserved2[3]; + + /* + * Identify data for device. + */ + IDENTIFY_DATA FullIdentifyData; + IDENTIFY_DATA2 IdentifyData[2]; + + /* + * DMA PRD table physical address. + */ + dma_addr_t dmatable_dma; + + /* + * DMA PRD table virtual address. + */ + unsigned long *dmatable_cpu; + + /* + * Point to SCATTER/GATHER data buffer. + */ + struct scatterlist *sg_table; + + /* + * DMA read or write. + */ + int sg_dma_direction; + + /* + * Current request on controller. + */ + PSCSI_REQUEST_BLOCK CurrentSrb; + + /* + * Original request on controller. + */ + PSCSI_REQUEST_BLOCK OriginalSrb; + + /* + * Internal SRB. + */ + SCSI_REQUEST_BLOCK _Srb; + + /* + * Remember the PCI device. + */ + struct pci_dev *pPciDev; + + /* + * Placeholder for CDB. + */ + u8 TempCdb[MAXIMUM_CDB_SIZE]; +} Channel, *PChannel; + +/************************************************************************ + * _Adapter + ************************************************************************/ +typedef struct _Adapter { + char *name; /* Adapter's name */ + u8 num_channels; /* How many channels support */ + unsigned int irq; /* irq number */ + unsigned int irqOwned; /* If any irq is use */ + u8 pci_bus; /* PCI bus number */ + u8 devfn; /* Device and function number */ + u8 offline; /* On line or off line */ + u8 bypass_mode; /* bypass or firware mode */ + u8 reserved2[1]; /* Reserved for alignment */ + Channel *IDEChannel; /* IT8212 supports two channels */ + struct pci_dev *pci_dev; /* For PCI device */ +} ITE_ADAPTER, *PITE_ADAPTER; + +/************************************************************************ + * Beautification macros + ************************************************************************/ +#define ScheduleRetryProcess(pChan) do { \ + pChan->retry_timer->expires = jiffies + 10; \ + add_timer(pChan->retry_timer); \ + } while (0) + +#define CancelRetryProcess(pChan) del_timer(pChan->retry_timer) + +#define GetStatus(pChan, Status) \ + Status = inb(pChan->io_ports[IDE_CONTROL_OFFSET]); + +#define GetBaseStatus(pChan, Status) \ + Status = inb(pChan->io_ports[IDE_COMMAND_OFFSET]); + +#define GetError(pChan, Error) \ + Error = inb(pChan->io_ports[IDE_ERROR_OFFSET]); + +#define ReadBuffer(pChan, Buffer, Count) \ + insw(pChan->io_ports[IDE_DATA_OFFSET], Buffer, Count); + +#define WriteCommand(BaseIoAddress, Command) \ + outb(pChan->io_ports[IDE_COMMAND_OFFSET], Command); + +#define WriteBuffer(pChan, Buffer, Count) \ + outsw(pChan->io_ports[IDE_DATA_OFFSET], Buffer, Count); + +#define WaitOnBusy(pChan, Status) \ +{ \ + int i; \ + for (i = 0; i < 20000; i++) \ + { \ + GetStatus(pChan, Status); \ + if (Status & IDE_STATUS_BUSY) \ + { \ + udelay(150); \ + continue; \ + } \ + else \ + { \ + break; \ + } \ + } \ +} + +#define WaitOnBaseBusy(pChan, Status) \ +{ \ + int i; \ + for (i = 0; i < 20000; i++) \ + { \ + GetBaseStatus(pChan, Status); \ + if (Status & IDE_STATUS_BUSY) \ + { \ + udelay(150); \ + continue; \ + } \ + else \ + { \ + break; \ + } \ + } \ +} + +#define WaitForDrq(pChan, Status) \ +{ \ + int i; \ + for (i = 0; i < 1000; i++) \ + { \ + GetStatus(pChan, Status); \ + if (Status & IDE_STATUS_BUSY) \ + { \ + udelay(100); \ + } \ + else if (Status & IDE_STATUS_DRQ) \ + { \ + break; \ + } \ + else \ + { \ + udelay(200); \ + } \ + } \ +} + +#define WaitForBaseDrq(pChan, Status) \ +{ \ + int i; \ + for (i = 0; i < 50000; i++) \ + { \ + GetBaseStatus(pChan, Status); \ + if (Status & IDE_STATUS_BUSY) \ + { \ + udelay(100); \ + } \ + else if (Status & IDE_STATUS_DRQ) \ + { \ + break; \ + } \ + else \ + { \ + udelay(200); \ + } \ + } \ +} + +#define CheckBusyDrq(pChan, Status) \ +{ \ + int i; \ + for (i = 0; i < 50000; i++) \ + { \ + GetBaseStatus(pChan, Status); \ + if ((Status & IDE_STATUS_BUSY) || \ + !(Status & IDE_STATUS_DRQ)) \ + { \ + udelay(200); \ + } \ + else \ + { \ + break; \ + } \ + } \ +} + +#define WaitShortForDrq(pChan, Status) \ +{ \ + int i; \ + for (i = 0; i < 2; i++) \ + { \ + GetStatus(pChan, Status); \ + if (Status & IDE_STATUS_BUSY) \ + { \ + udelay(100); \ + } \ + else if (Status & IDE_STATUS_DRQ) \ + { \ + break; \ + } \ + else \ + { \ + udelay(100); \ + } \ + } \ +} + +#define WaitForDeviceReady(pChan, Status) \ +{ \ + int i; \ + for (i = 0; i < 50000; i++) \ + { \ + GetStatus(pChan, Status); \ + if (Status == 0) \ + { \ + break; \ + } \ + if ((Status & IDE_STATUS_BUSY) || (Status & IDE_STATUS_DRQ)) \ + { \ + udelay(200); \ + continue; \ + } \ + else \ + { \ + break; \ + } \ + } \ +} + +#define WaitForCommandComplete(pChan, Status) \ +{ \ + int i; \ + for (i = 0; i < 50000; i++) \ + { \ + GetStatus(pChan, Status); \ + if ((Status == 0) || (Status & IDE_STATUS_ERROR) \ + || (Status == IDE_STATUS_IDLE)) \ + { \ + break; \ + } \ + udelay(200); \ + continue; \ + } \ +} + +#define WaitForBaseCommandComplete(pChan, Status) \ +{ \ + int i; \ + for (i = 0; i < 50000; i++) \ + { \ + GetBaseStatus(pChan, Status); \ + if ((Status == 0) || (Status & IDE_STATUS_ERROR) \ + || (Status == IDE_STATUS_IDLE)) \ + { \ + break; \ + } \ + udelay(200); \ + continue; \ + } \ +} + +#define AtapiSoftReset(pChan, DevNum) \ +{ \ + unsigned char statusByte; \ + outb((unsigned char)(((DevNum & 0x1) << 4) | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]); \ + udelay(500); \ + outb(IDE_COMMAND_ATAPI_RESET, pChan->io_ports[IDE_COMMAND_OFFSET]); \ + mdelay(1000); \ + outb((unsigned char)(((DevNum & 0x1) << 4) | 0xA0), pChan->io_ports[IDE_SELECT_OFFSET]); \ + WaitOnBusy(pChan, statusByte); \ + udelay(500); \ +} + +#define IdeHardReset(pChan, result) \ +do { \ + unsigned char statusByte; \ + int i; \ + outb(IDE_DC_RESET_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]); \ + mdelay(50); \ + outb(IDE_DC_REENABLE_CONTROLLER, pChan->io_ports[IDE_CONTROL_OFFSET]); \ + for (i = 0; i < 1000 * 1000; i++) \ + { \ + statusByte = inb(pChan->io_ports[IDE_CONTROL_OFFSET]); \ + if (statusByte != IDE_STATUS_IDLE && statusByte != 0x0) \ + { \ + udelay(30); \ + } \ + else \ + { \ + break; \ + } \ + } \ + if (i == 1000 * 1000) \ + { \ + printk("IdeHardReset Fail!\n"); \ + result = FALSE; \ + } \ + else \ + { \ + dprintk("IdeHardReset Success!\n"); \ + result = TRUE; \ + } \ +} while (0) + +/************************************************************************ + * Function prototypes + ************************************************************************/ + +#endif /* #ifndef _ITERAID_H_ */ --- linux-2.6.9-rc1/drivers/scsi/jazz_esp.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/scsi/jazz_esp.c 2004-09-03 00:57:09.779552544 -0700 @@ -286,7 +286,7 @@ static void dma_led_on(struct NCR_ESP *e } static Scsi_Host_Template driver_template = { - .proc_name = "esp", + .proc_name = "jazz_esp", .proc_info = &esp_proc_info, .name = "ESP 100/100a/200", .detect = jazz_esp_detect, --- linux-2.6.9-rc1/drivers/scsi/Kconfig 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/scsi/Kconfig 2004-09-03 00:56:53.240066928 -0700 @@ -270,6 +270,15 @@ config SCSI_ACARD To compile this driver as a module, choose M here: the module will be called atp870u. +config SCSI_ITERAID + tristate "ITE IT8212 RAID CARD support" + depends on PCI && SCSI + help + This driver supports the ITE IT8212 RAID card. + + To compile this driver as a module, choose M here: the + module will be called iteraid. + config SCSI_AHA152X tristate "Adaptec AHA152X/2825 support" depends on ISA && SCSI && !64BIT @@ -320,7 +329,7 @@ source "drivers/scsi/aic7xxx/Kconfig.aic config SCSI_AIC7XXX_OLD tristate "Adaptec AIC7xxx support (old driver)" - depends on SCSI + depends on (ISA || EISA || PCI ) && SCSI help WARNING This driver is an older aic7xxx driver and is no longer under active development. Adaptec, Inc. is writing a new driver to @@ -395,15 +404,7 @@ config SCSI_IN2000 To compile this driver as a module, choose M here: the module will be called in2000. -config SCSI_MEGARAID - tristate "AMI MegaRAID support" - depends on PCI && SCSI - help - This driver supports the AMI MegaRAID 418, 428, 438, 466, 762, 490 - and 467 SCSI host adapters. - - To compile this driver as a module, choose M here: the - module will be called megaraid. +source "drivers/scsi/megaraid/Kconfig.megaraid" config SCSI_SATA bool "Serial ATA (SATA) support" @@ -595,7 +596,7 @@ config SCSI_EATA_MAX_TAGS config SCSI_EATA_PIO tristate "EATA-PIO (old DPT PM2001, PM2012A) support" - depends on SCSI + depends on (ISA || EISA || PCI) && SCSI ---help--- This driver supports all EATA-PIO protocol compliant SCSI Host Adapters like the DPT PM2001 and the PM2012A. EATA-DMA compliant @@ -780,6 +781,15 @@ config SCSI_IPS To compile this driver as a module, choose M here: the module will be called ips. +config SCSI_IBMVSCSI + tristate "IBM Virtual SCSI support" + depends on PPC_PSERIES || PPC_ISERIES + help + This is the IBM POWER Virtual SCSI Client + + To compile this driver as a module, choose M here: the + module will be called ibmvscsic. + config SCSI_INITIO tristate "Initio 9100U(W) support" depends on PCI && SCSI && BROKEN @@ -1001,7 +1011,7 @@ config SCSI_SYM53C8XX_IOMAPPED config SCSI_IPR tristate "IBM Power Linux RAID adapter support" - depends on PCI && SCSI + depends on PCI && SCSI && PPC select FW_LOADER ---help--- This driver supports the IBM Power Linux family RAID adapters. @@ -1462,7 +1472,7 @@ config SCSI_DEBUG config SCSI_MESH tristate "MESH (Power Mac internal SCSI) support" - depends on PPC_PMAC && SCSI + depends on PPC32 && PPC_PMAC && SCSI help Many Power Macintoshes and clones have a MESH (Macintosh Enhanced SCSI Hardware) SCSI bus adaptor (the 7200 doesn't, but all of the @@ -1507,7 +1517,7 @@ source "drivers/scsi/arm/Kconfig" config JAZZ_ESP bool "MIPS JAZZ FAS216 SCSI support" - depends on MIPS_JAZZ && SCSI + depends on MACH_JAZZ && SCSI help This is the driver for the onboard SCSI host adapter of MIPS Magnum 4000, Acer PICA, Olivetti M700-10 and a few other identical OEM --- linux-2.6.9-rc1/drivers/scsi/libata-core.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/drivers/scsi/libata-core.c 2004-09-02 20:51:52.000000000 -0700 @@ -1836,7 +1836,7 @@ static void ata_fill_sg(struct ata_queue idx = 0; for (nelem = qc->n_elem; nelem; nelem--,sg++) { - u32 addr, boundary; + u32 addr, offset; u32 sg_len, len; /* determine if physical DMA addr spans 64K boundary. @@ -1847,10 +1847,10 @@ static void ata_fill_sg(struct ata_queue sg_len = sg_dma_len(sg); while (sg_len) { - boundary = (addr & ~0xffff) + (0xffff + 1); + offset = addr & 0xffff; len = sg_len; - if ((addr + sg_len) > boundary) - len = boundary - addr; + if ((offset + sg_len) > 0x10000) + len = 0x10000 - offset; ap->prd[idx].addr = cpu_to_le32(addr); ap->prd[idx].flags_len = cpu_to_le32(len & 0xffff); --- linux-2.6.9-rc1/drivers/scsi/libata-scsi.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/drivers/scsi/libata-scsi.c 2004-09-02 20:51:52.000000000 -0700 @@ -94,7 +94,7 @@ int ata_scsi_ioctl(struct scsi_device *s return 0; case ATA_IOC_SET_IO32: - val = (long) arg; + val = (unsigned long) arg; if (val != 0) return -EINVAL; return 0; --- linux-2.6.9-rc1/drivers/scsi/mac_esp.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/scsi/mac_esp.c 2004-09-03 00:57:09.780552392 -0700 @@ -731,7 +731,7 @@ static void dma_setup_quick(struct NCR_E } static Scsi_Host_Template driver_template = { - .proc_name = "esp", + .proc_name = "mac_esp", .name = "Mac 53C9x SCSI", .detect = mac_esp_detect, .slave_alloc = esp_slave_alloc, --- linux-2.6.9-rc1/drivers/scsi/mac_scsi.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/scsi/mac_scsi.c 2004-09-02 20:51:52.000000000 -0700 @@ -326,6 +326,7 @@ int macscsi_release (struct Scsi_Host *s { if (shpnt->irq != SCSI_IRQ_NONE) free_irq (shpnt->irq, NCR5380_intr); + NCR5380_exit(shpnt); return 0; } --- linux-2.6.9-rc1/drivers/scsi/Makefile 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/scsi/Makefile 2004-09-03 00:56:53.240066928 -0700 @@ -95,8 +95,10 @@ obj-$(CONFIG_SCSI_IBMMCA) += ibmmca.o obj-$(CONFIG_SCSI_EATA) += eata.o obj-$(CONFIG_SCSI_DC395x) += dc395x.o obj-$(CONFIG_SCSI_DC390T) += tmscsim.o -obj-$(CONFIG_SCSI_MEGARAID) += megaraid.o +obj-$(CONFIG_MEGARAID_LEGACY) += megaraid.o +obj-$(CONFIG_MEGARAID_NEWGEN) += megaraid/ obj-$(CONFIG_SCSI_ACARD) += atp870u.o +obj-$(CONFIG_SCSI_ITERAID) += iteraid.o obj-$(CONFIG_SCSI_SUNESP) += esp.o obj-$(CONFIG_SCSI_GDTH) += gdth.o obj-$(CONFIG_SCSI_INITIO) += initio.o @@ -119,6 +121,7 @@ obj-$(CONFIG_SCSI_CPQFCTS) += cpqfc.o obj-$(CONFIG_SCSI_LASI700) += 53c700.o lasi700.o obj-$(CONFIG_SCSI_NSP32) += nsp32.o obj-$(CONFIG_SCSI_IPR) += ipr.o +obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsi/ obj-$(CONFIG_SCSI_SATA_SVW) += libata.o sata_svw.o obj-$(CONFIG_SCSI_ATA_PIIX) += libata.o ata_piix.o obj-$(CONFIG_SCSI_SATA_PROMISE) += libata.o sata_promise.o --- linux-2.6.9-rc1/drivers/scsi/mca_53c9x.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/drivers/scsi/mca_53c9x.c 2004-09-03 00:57:09.781552240 -0700 @@ -445,7 +445,7 @@ static void dma_led_off(struct NCR_ESP * } static Scsi_Host_Template driver_template = { - .proc_name = "esp", + .proc_name = "mca_53c9x", .name = "NCR 53c9x SCSI", .detect = mca_esp_detect, .slave_alloc = esp_slave_alloc, --- linux-2.6.9-rc1/drivers/scsi/megaraid.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/drivers/scsi/megaraid.c 2004-09-02 20:51:52.000000000 -0700 @@ -25,11 +25,8 @@ * 518, 520, 531, 532 * * This driver is supported by LSI Logic, with assistance from Red Hat, Dell, - * and others. Please send updates to the public mailing list - * linux-megaraid-devel@dell.com, and subscribe to and read archives of this - * list at http://lists.us.dell.com/. - * - * For history of changes, see ChangeLog.megaraid. + * and others. Please send updates to the mailing list + * linux-scsi@vger.kernel.org . * */ @@ -53,9 +50,12 @@ #include "megaraid.h" +#define MEGARAID_MODULE_VERSION "2.00.3" + MODULE_AUTHOR ("LSI Logic Corporation"); MODULE_DESCRIPTION ("LSI Logic MegaRAID driver"); MODULE_LICENSE ("GPL"); +MODULE_VERSION(MEGARAID_MODULE_VERSION); static unsigned int max_cmd_per_lun = DEF_CMD_PER_LUN; MODULE_PARM(max_cmd_per_lun, "i"); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/scsi/megaraid/Kconfig.megaraid 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,77 @@ +config MEGARAID_NEWGEN + bool "LSI Logic New Generation RAID Device Drivers" + depends on PCI && SCSI + help + LSI Logic RAID Device Drivers + +config MEGARAID_MM + tristate "LSI Logic Management Module (New Driver)" + depends on PCI && SCSI && MEGARAID_NEWGEN + help + Management Module provides ioctl, sysfs support for LSI Logic + RAID controllers. + To compile this driver as a module, choose M here: the + module will be called megaraid_mm + + +config MEGARAID_MAILBOX + tristate "LSI Logic MegaRAID Driver (New Driver)" + depends on PCI && SCSI && MEGARAID_MM + help + List of supported controllers + + OEM Product Name VID :DID :SVID:SSID + --- ------------ ---- ---- ---- ---- + Dell PERC3/QC 101E:1960:1028:0471 + Dell PERC3/DC 101E:1960:1028:0493 + Dell PERC3/SC 101E:1960:1028:0475 + Dell PERC3/Di 1028:000E:1028:0123 + Dell PERC4/SC 1000:1960:1028:0520 + Dell PERC4/DC 1000:1960:1028:0518 + Dell PERC4/QC 1000:0407:1028:0531 + Dell PERC4/Di 1028:000F:1028:014A + Dell PERC 4e/Si 1028:0013:1028:016c + Dell PERC 4e/Di 1028:0013:1028:016d + Dell PERC 4e/Di 1028:0013:1028:016e + Dell PERC 4e/Di 1028:0013:1028:016f + Dell PERC 4e/Di 1028:0013:1028:0170 + Dell PERC 4e/DC 1000:0408:1028:0002 + Dell PERC 4e/SC 1000:0408:1028:0001 + LSI MegaRAID SCSI 320-0 1000:1960:1000:A520 + LSI MegaRAID SCSI 320-1 1000:1960:1000:0520 + LSI MegaRAID SCSI 320-2 1000:1960:1000:0518 + LSI MegaRAID SCSI 320-0X 1000:0407:1000:0530 + LSI MegaRAID SCSI 320-2X 1000:0407:1000:0532 + LSI MegaRAID SCSI 320-4X 1000:0407:1000:0531 + LSI MegaRAID SCSI 320-1E 1000:0408:1000:0001 + LSI MegaRAID SCSI 320-2E 1000:0408:1000:0002 + LSI MegaRAID SATA 150-4 1000:1960:1000:4523 + LSI MegaRAID SATA 150-6 1000:1960:1000:0523 + LSI MegaRAID SATA 300-4X 1000:0409:1000:3004 + LSI MegaRAID SATA 300-8X 1000:0409:1000:3008 + INTEL RAID Controller SRCU42X 1000:0407:8086:0532 + INTEL RAID Controller SRCS16 1000:1960:8086:0523 + INTEL RAID Controller SRCU42E 1000:0408:8086:0002 + INTEL RAID Controller SRCZCRX 1000:0407:8086:0530 + INTEL RAID Controller SRCS28X 1000:0409:8086:3008 + INTEL RAID Controller SROMBU42E 1000:0408:8086:3431 + INTEL RAID Controller SROMBU42E 1000:0408:8086:3499 + INTEL RAID Controller SRCU51L 1000:1960:8086:0520 + FSC MegaRAID PCI Express ROMB 1000:0408:1734:1065 + ACER MegaRAID ROMB-2E 1000:0408:1025:004D + + To compile this driver as a module, choose M here: the + module will be called megaraid_mbox + +if MEGARAID_NEWGEN=n +config MEGARAID_LEGACY + tristate "LSI Logic Legacy MegaRAID Driver" + depends on PCI && SCSI + help + This driver supports the LSI MegaRAID 418, 428, 438, 466, 762, 490 + and 467 SCSI host adapters. This driver also support the all U320 + RAID controllers + + To compile this driver as a module, choose M here: the + module will be called megaraid +endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/scsi/megaraid/Makefile 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,2 @@ +obj-$(CONFIG_MEGARAID_MM) += megaraid_mm.o +obj-$(CONFIG_MEGARAID_MAILBOX) += megaraid_mbox.o --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/scsi/megaraid/mbox_defs.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,790 @@ +/* + * + * Linux MegaRAID Unified device driver + * + * Copyright (c) 2003-2004 LSI Logic Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * FILE : mbox_defs.h + * + */ +#ifndef _MRAID_MBOX_DEFS_H_ +#define _MRAID_MBOX_DEFS_H_ + +#include + +/* + * Commands and states for mailbox based controllers + */ + +#define MBOXCMD_LREAD 0x01 +#define MBOXCMD_LWRITE 0x02 +#define MBOXCMD_PASSTHRU 0x03 +#define MBOXCMD_ADPEXTINQ 0x04 +#define MBOXCMD_ADAPTERINQ 0x05 +#define MBOXCMD_LREAD64 0xA7 +#define MBOXCMD_LWRITE64 0xA8 +#define MBOXCMD_PASSTHRU64 0xC3 +#define MBOXCMD_EXTPTHRU 0xE3 + +#define MAIN_MISC_OPCODE 0xA4 +#define GET_MAX_SG_SUPPORT 0x01 +#define SUPPORT_EXT_CDB 0x16 + +#define FC_NEW_CONFIG 0xA1 +#define NC_SUBOP_PRODUCT_INFO 0x0E +#define NC_SUBOP_ENQUIRY3 0x0F +#define ENQ3_GET_SOLICITED_FULL 0x02 +#define OP_DCMD_READ_CONFIG 0x04 +#define NEW_READ_CONFIG_8LD 0x67 +#define READ_CONFIG_8LD 0x07 +#define FLUSH_ADAPTER 0x0A +#define FLUSH_SYSTEM 0xFE + +/* + * Command for random deletion of logical drives + */ +#define FC_DEL_LOGDRV 0xA4 +#define OP_SUP_DEL_LOGDRV 0x2A +#define OP_GET_LDID_MAP 0x18 +#define OP_DEL_LOGDRV 0x1C + +/* + * BIOS commands + */ +#define IS_BIOS_ENABLED 0x62 +#define GET_BIOS 0x01 +#define CHNL_CLASS 0xA9 +#define GET_CHNL_CLASS 0x00 +#define SET_CHNL_CLASS 0x01 +#define CH_RAID 0x01 +#define CH_SCSI 0x00 +#define BIOS_PVT_DATA 0x40 +#define GET_BIOS_PVT_DATA 0x00 + + +/* + * Commands to support clustering + */ +#define GET_TARGET_ID 0x7D +#define CLUSTER_OP 0x70 +#define GET_CLUSTER_MODE 0x02 +#define CLUSTER_CMD 0x6E +#define RESERVE_LD 0x01 +#define RELEASE_LD 0x02 +#define RESET_RESERVATIONS 0x03 +#define RESERVATION_STATUS 0x04 +#define RESERVE_PD 0x05 +#define RELEASE_PD 0x06 + + +/* + * Module battery status + */ +#define BATTERY_MODULE_MISSING 0x01 +#define BATTERY_LOW_VOLTAGE 0x02 +#define BATTERY_TEMP_HIGH 0x04 +#define BATTERY_PACK_MISSING 0x08 +#define BATTERY_CHARGE_MASK 0x30 +#define BATTERY_CHARGE_DONE 0x00 +#define BATTERY_CHARGE_INPROG 0x10 +#define BATTERY_CHARGE_FAIL 0x20 +#define BATTERY_CYCLES_EXCEEDED 0x40 + +/* + * Physical drive states. + */ +#define PDRV_UNCNF 0 +#define PDRV_ONLINE 3 +#define PDRV_FAILED 4 +#define PDRV_RBLD 5 +#define PDRV_HOTSPARE 6 + + +/* + * Raid logical drive states. + */ +#define RDRV_OFFLINE 0 +#define RDRV_DEGRADED 1 +#define RDRV_OPTIMAL 2 +#define RDRV_DELETED 3 + +/* + * Read, write and cache policies + */ +#define NO_READ_AHEAD 0 +#define READ_AHEAD 1 +#define ADAP_READ_AHEAD 2 +#define WRMODE_WRITE_THRU 0 +#define WRMODE_WRITE_BACK 1 +#define CACHED_IO 0 +#define DIRECT_IO 1 + +#define MAX_LOGICAL_DRIVES_8LD 8 +#define MAX_LOGICAL_DRIVES_40LD 40 +#define FC_MAX_PHYSICAL_DEVICES 256 +#define MAX_MBOX_CHANNELS 5 +#define MAX_MBOX_TARGET 15 +#define MBOX_MAX_PHYSICAL_DRIVES MAX_MBOX_CHANNELS*MAX_MBOX_TARGET +#define MAX_ROW_SIZE_40LD 32 +#define MAX_ROW_SIZE_8LD 8 +#define SPAN_DEPTH_8_SPANS 8 +#define SPAN_DEPTH_4_SPANS 4 +#define MAX_REQ_SENSE_LEN 0x20 + + + +/** + * struct mbox_t - Driver and f/w handshake structure. + * @cmd : firmware command + * @cmdid : command id + * @numsectors : number of sectors to be transferred + * @lba : Logical Block Address on LD + * @xferaddr : DMA address for data transfer + * @logdrv : logical drive number + * @numsge : number of scatter gather elements in sg list + * @resvd : reserved + * @busy : f/w busy, must wait to issue more commands. + * @numstatus : number of commands completed. + * @status : status of the commands completed + * @completed : array of completed command ids. + * @poll : poll and ack sequence + * @ack : poll and ack sequence + * + * The central handshake structure between the driver and the firmware. This + * structure must be allocated by the driver and aligned at 8-byte boundary. + */ +#define MBOX_MAX_FIRMWARE_STATUS 46 +typedef struct { + uint8_t cmd; + uint8_t cmdid; + uint16_t numsectors; + uint32_t lba; + uint32_t xferaddr; + uint8_t logdrv; + uint8_t numsge; + uint8_t resvd; + uint8_t busy; + uint8_t numstatus; + uint8_t status; + uint8_t completed[MBOX_MAX_FIRMWARE_STATUS]; + uint8_t poll; + uint8_t ack; +} __attribute__ ((packed)) mbox_t; + + +/** + * mbox64_t - 64-bit extension for the mailbox + * @segment_lo : the low 32-bits of the address of the scatter-gather list + * @segment_hi : the upper 32-bits of the address of the scatter-gather list + * @mbox : 32-bit mailbox, whose xferadder field must be set to + * 0xFFFFFFFF + * + * This is the extension of the 32-bit mailbox to be able to perform DMA + * beyond 4GB address range. + */ +typedef struct { + uint32_t xferaddr_lo; + uint32_t xferaddr_hi; + mbox_t mbox32; +} __attribute__ ((packed)) mbox64_t; + +/* + * mailbox structure used for internal commands + */ +typedef struct { + u8 cmd; + u8 cmdid; + u8 opcode; + u8 subopcode; + u32 lba; + u32 xferaddr; + u8 logdrv; + u8 rsvd[3]; + u8 numstatus; + u8 status; +} __attribute__ ((packed)) int_mbox_t; + +/** + * mraid_passthru_t - passthru structure to issue commands to physical devices + * @timeout : command timeout, 0=6sec, 1=60sec, 2=10min, 3=3hr + * @ars : set if ARS required after check condition + * @islogical : set if command meant for logical devices + * @logdrv : logical drive number if command for LD + * @channel : Channel on which physical device is located + * @target : SCSI target of the device + * @queuetag : unused + * @queueaction : unused + * @cdb : SCSI CDB + * @cdblen : length of the CDB + * @reqsenselen : amount of request sense data to be returned + * @reqsensearea : Sense information buffer + * @numsge : number of scatter-gather elements in the sg list + * @scsistatus : SCSI status of the command completed. + * @dataxferaddr : DMA data transfer address + * @dataxferlen : amount of the data to be transferred. + */ +typedef struct { + uint8_t timeout :3; + uint8_t ars :1; + uint8_t reserved :3; + uint8_t islogical :1; + uint8_t logdrv; + uint8_t channel; + uint8_t target; + uint8_t queuetag; + uint8_t queueaction; + uint8_t cdb[10]; + uint8_t cdblen; + uint8_t reqsenselen; + uint8_t reqsensearea[MAX_REQ_SENSE_LEN]; + uint8_t numsge; + uint8_t scsistatus; + uint32_t dataxferaddr; + uint32_t dataxferlen; +} __attribute__ ((packed)) mraid_passthru_t; + +typedef struct { + + uint32_t dataxferaddr_lo; + uint32_t dataxferaddr_hi; + mraid_passthru_t pthru32; + +} __attribute__ ((packed)) mega_passthru64_t; + +/** + * mraid_epassthru_t - passthru structure to issue commands to physical devices + * @timeout : command timeout, 0=6sec, 1=60sec, 2=10min, 3=3hr + * @ars : set if ARS required after check condition + * @rsvd1 : reserved field + * @cd_rom : (?) + * @rsvd2 : reserved field + * @islogical : set if command meant for logical devices + * @logdrv : logical drive number if command for LD + * @channel : Channel on which physical device is located + * @target : SCSI target of the device + * @queuetag : unused + * @queueaction : unused + * @cdblen : length of the CDB + * @rsvd3 : reserved field + * @cdb : SCSI CDB + * @numsge : number of scatter-gather elements in the sg list + * @status : SCSI status of the command completed. + * @reqsenselen : amount of request sense data to be returned + * @reqsensearea : Sense information buffer + * @rsvd4 : reserved field + * @dataxferaddr : DMA data transfer address + * @dataxferlen : amount of the data to be transferred. + */ +typedef struct { + uint8_t timeout :3; + uint8_t ars :1; + uint8_t rsvd1 :1; + uint8_t cd_rom :1; + uint8_t rsvd2 :1; + uint8_t islogical :1; + uint8_t logdrv; + uint8_t channel; + uint8_t target; + uint8_t queuetag; + uint8_t queueaction; + uint8_t cdblen; + uint8_t rsvd3; + uint8_t cdb[16]; + uint8_t numsge; + uint8_t status; + uint8_t reqsenselen; + uint8_t reqsensearea[MAX_REQ_SENSE_LEN]; + uint8_t rsvd4; + uint32_t dataxferaddr; + uint32_t dataxferlen; +} __attribute__ ((packed)) mraid_epassthru_t; + + +/** + * mraid_pinfo_t - product info, static information about the controller + * @data_size : current size in bytes (not including resvd) + * @config_signature : Current value is 0x00282008 + * @fw_version : Firmware version + * @bios_version : version of the BIOS + * @product_name : Name given to the controller + * @max_commands : Maximum concurrent commands supported + * @nchannels : Number of SCSI Channels detected + * @fc_loop_present : Number of Fibre Loops detected + * @mem_type : EDO, FPM, SDRAM etc + * @signature : + * @dram_size : In terms of MB + * @subsysid : device PCI subsystem ID + * @subsysvid : device PCI subsystem vendor ID + * @notify_counters : + * @pad1k : 135 + 889 resvd = 1024 total size + * + * This structures holds the information about the controller which is not + * expected to change dynamically. + * + * The current value of config signature is 0x00282008: + * 0x28 = MAX_LOGICAL_DRIVES, + * 0x20 = Number of stripes and + * 0x08 = Number of spans + */ +typedef struct { + uint32_t data_size; + uint32_t config_signature; + uint8_t fw_version[16]; + uint8_t bios_version[16]; + uint8_t product_name[80]; + uint8_t max_commands; + uint8_t nchannels; + uint8_t fc_loop_present; + uint8_t mem_type; + uint32_t signature; + uint16_t dram_size; + uint16_t subsysid; + uint16_t subsysvid; + uint8_t notify_counters; + uint8_t pad1k[889]; +} __attribute__ ((packed)) mraid_pinfo_t; + + +/** + * mraid_notify_t - the notification structure + * @global_counter : Any change increments this counter + * @param_counter : Indicates any params changed + * @param_id : Param modified - defined below + * @param_val : New val of last param modified + * @write_config_counter : write config occurred + * @write_config_rsvd : + * @ldrv_op_counter : Indicates ldrv op started/completed + * @ldrv_opid : ldrv num + * @ldrv_opcmd : ldrv operation - defined below + * @ldrv_opstatus : status of the operation + * @ldrv_state_counter : Indicates change of ldrv state + * @ldrv_state_id : ldrv num + * @ldrv_state_new : New state + * @ldrv_state_old : old state + * @pdrv_state_counter : Indicates change of ldrv state + * @pdrv_state_id : pdrv id + * @pdrv_state_new : New state + * @pdrv_state_old : old state + * @pdrv_fmt_counter : Indicates pdrv format started/over + * @pdrv_fmt_id : pdrv id + * @pdrv_fmt_val : format started/over + * @pdrv_fmt_rsvd : + * @targ_xfer_counter : Indicates SCSI-2 Xfer rate change + * @targ_xfer_id : pdrv Id + * @targ_xfer_val : new Xfer params of last pdrv + * @targ_xfer_rsvd : + * @fcloop_id_chg_counter : Indicates loopid changed + * @fcloopid_pdrvid : pdrv id + * @fcloop_id0 : loopid on fc loop 0 + * @fcloop_id1 : loopid on fc loop 1 + * @fcloop_state_counter : Indicates loop state changed + * @fcloop_state0 : state of fc loop 0 + * @fcloop_state1 : state of fc loop 1 + * @fcloop_state_rsvd : + */ +typedef struct { + uint32_t global_counter; + uint8_t param_counter; + uint8_t param_id; + uint16_t param_val; + uint8_t write_config_counter; + uint8_t write_config_rsvd[3]; + uint8_t ldrv_op_counter; + uint8_t ldrv_opid; + uint8_t ldrv_opcmd; + uint8_t ldrv_opstatus; + uint8_t ldrv_state_counter; + uint8_t ldrv_state_id; + uint8_t ldrv_state_new; + uint8_t ldrv_state_old; + uint8_t pdrv_state_counter; + uint8_t pdrv_state_id; + uint8_t pdrv_state_new; + uint8_t pdrv_state_old; + uint8_t pdrv_fmt_counter; + uint8_t pdrv_fmt_id; + uint8_t pdrv_fmt_val; + uint8_t pdrv_fmt_rsvd; + uint8_t targ_xfer_counter; + uint8_t targ_xfer_id; + uint8_t targ_xfer_val; + uint8_t targ_xfer_rsvd; + uint8_t fcloop_id_chg_counter; + uint8_t fcloopid_pdrvid; + uint8_t fcloop_id0; + uint8_t fcloop_id1; + uint8_t fcloop_state_counter; + uint8_t fcloop_state0; + uint8_t fcloop_state1; + uint8_t fcloop_state_rsvd; +} __attribute__ ((packed)) mraid_notify_t; + + +/** + * mraid_inquiry3_t - enquiry for device information + * + * @data_size : current size in bytes (not including resvd) + * @notify : + * @notify_rsvd : + * @rebuild_rate : rebuild rate (0% - 100%) + * @cache_flush_int : cache flush interval in seconds + * @sense_alert : + * @drive_insert_count : drive insertion count + * @battery_status : + * @num_ldrv : no. of Log Drives configured + * @recon_state : state of reconstruct + * @ldrv_op_status : logdrv Status + * @ldrv_size : size of each log drv + * @ldrv_prop : + * @ldrv_state : state of log drives + * @pdrv_state : state of phys drvs. + * @pdrv_format : + * @targ_xfer : phys device transfer rate + * @pad1k : 761 + 263reserved = 1024 bytes total size + */ +#define MAX_NOTIFY_SIZE 0x80 +#define CUR_NOTIFY_SIZE sizeof(mraid_notify_t) + +typedef struct { + uint32_t data_size; + + mraid_notify_t notify; + + uint8_t notify_rsvd[MAX_NOTIFY_SIZE - CUR_NOTIFY_SIZE]; + + uint8_t rebuild_rate; + uint8_t cache_flush_int; + uint8_t sense_alert; + uint8_t drive_insert_count; + + uint8_t battery_status; + uint8_t num_ldrv; + uint8_t recon_state[MAX_LOGICAL_DRIVES_40LD / 8]; + uint16_t ldrv_op_status[MAX_LOGICAL_DRIVES_40LD / 8]; + + uint32_t ldrv_size[MAX_LOGICAL_DRIVES_40LD]; + uint8_t ldrv_prop[MAX_LOGICAL_DRIVES_40LD]; + uint8_t ldrv_state[MAX_LOGICAL_DRIVES_40LD]; + uint8_t pdrv_state[FC_MAX_PHYSICAL_DEVICES]; + uint16_t pdrv_format[FC_MAX_PHYSICAL_DEVICES / 16]; + + uint8_t targ_xfer[80]; + uint8_t pad1k[263]; +} __attribute__ ((packed)) mraid_inquiry3_t; + + +/** + * mraid_adapinfo_t - information about the adapter + * @max_commands : max concurrent commands supported + * @rebuild_rate : rebuild rate - 0% thru 100% + * @max_targ_per_chan : max targ per channel + * @nchannels : number of channels on HBA + * @fw_version : firmware version + * @age_of_flash : number of times FW has been flashed + * @chip_set_value : contents of 0xC0000832 + * @dram_size : in MB + * @cache_flush_interval : in seconds + * @bios_version : + * @board_type : + * @sense_alert : + * @write_config_count : increase with every configuration change + * @drive_inserted_count : increase with every drive inserted + * @inserted_drive : channel:Id of inserted drive + * @battery_status : bit 0: battery module missing + * bit 1: VBAD + * bit 2: temprature high + * bit 3: battery pack missing + * bit 4,5: + * 00 - charge complete + * 01 - fast charge in progress + * 10 - fast charge fail + * 11 - undefined + * bit 6: counter > 1000 + * bit 7: Undefined + * @dec_fault_bus_info : + */ +typedef struct { + uint8_t max_commands; + uint8_t rebuild_rate; + uint8_t max_targ_per_chan; + uint8_t nchannels; + uint8_t fw_version[4]; + uint16_t age_of_flash; + uint8_t chip_set_value; + uint8_t dram_size; + uint8_t cache_flush_interval; + uint8_t bios_version[4]; + uint8_t board_type; + uint8_t sense_alert; + uint8_t write_config_count; + uint8_t battery_status; + uint8_t dec_fault_bus_info; +} __attribute__ ((packed)) mraid_adapinfo_t; + + +/** + * mraid_ldrv_info_t - information about the logical drives + * @nldrv : Number of logical drives configured + * @rsvd : + * @size : size of each logical drive + * @prop : + * @state : state of each logical drive + */ +typedef struct { + uint8_t nldrv; + uint8_t rsvd[3]; + uint32_t size[MAX_LOGICAL_DRIVES_8LD]; + uint8_t prop[MAX_LOGICAL_DRIVES_8LD]; + uint8_t state[MAX_LOGICAL_DRIVES_8LD]; +} __attribute__ ((packed)) mraid_ldrv_info_t; + + +/** + * mraid_pdrv_info_t - information about the physical drives + * @pdrv_state : state of each physical drive + */ +typedef struct { + uint8_t pdrv_state[MBOX_MAX_PHYSICAL_DRIVES]; + uint8_t rsvd; +} __attribute__ ((packed)) mraid_pdrv_info_t; + + +/** + * mraid_inquiry_t - RAID inquiry, mailbox command 0x05 + * @mraid_adapinfo_t : adapter information + * @mraid_ldrv_info_t : logical drives information + * @mraid_pdrv_info_t : physical drives information + */ +typedef struct { + mraid_adapinfo_t adapter_info; + mraid_ldrv_info_t logdrv_info; + mraid_pdrv_info_t pdrv_info; +} __attribute__ ((packed)) mraid_inquiry_t; + + +/** + * mraid_extinq_t - RAID extended inquiry, mailbox command 0x04 + * + * @raid_inq : raid inquiry + * @phys_drv_format : + * @stack_attn : + * @modem_status : + * @rsvd : + */ +typedef struct { + mraid_inquiry_t raid_inq; + uint16_t phys_drv_format[MAX_MBOX_CHANNELS]; + uint8_t stack_attn; + uint8_t modem_status; + uint8_t rsvd[2]; +} __attribute__ ((packed)) mraid_extinq_t; + + +/** + * adap_device_t - device information + * @channel : channel fpor the device + * @target : target ID of the device + */ +typedef struct { + uint8_t channel; + uint8_t target; +}__attribute__ ((packed)) adap_device_t; + + +/** + * adap_span_40ld_t - 40LD span + * @start_blk : starting block + * @num_blks : number of blocks + */ +typedef struct { + uint32_t start_blk; + uint32_t num_blks; + adap_device_t device[MAX_ROW_SIZE_40LD]; +}__attribute__ ((packed)) adap_span_40ld_t; + + +/** + * adap_span_8ld_t - 8LD span + * @start_blk : starting block + * @num_blks : number of blocks + */ +typedef struct { + uint32_t start_blk; + uint32_t num_blks; + adap_device_t device[MAX_ROW_SIZE_8LD]; +}__attribute__ ((packed)) adap_span_8ld_t; + + +/** + * logdrv_param_t - logical drives parameters + * + * @span_depth : total number of spans + * @level : RAID level + * @read_ahead : read ahead, no read ahead, adaptive read ahead + * @stripe_sz : encoded stripe size + * @status : status of the logical drive + * @write_mode : write mode, write_through/write_back + * @direct_io : direct io or through cache + * @row_size : number of stripes in a row + */ +typedef struct { + uint8_t span_depth; + uint8_t level; + uint8_t read_ahead; + uint8_t stripe_sz; + uint8_t status; + uint8_t write_mode; + uint8_t direct_io; + uint8_t row_size; +} __attribute__ ((packed)) logdrv_param_t; + + +/** + * logdrv_40ld_t - logical drive definition for 40LD controllers + * @lparam : logical drives parameters + * @span : span + */ +typedef struct { + logdrv_param_t lparam; + adap_span_40ld_t span[SPAN_DEPTH_8_SPANS]; +}__attribute__ ((packed)) logdrv_40ld_t; + + +/** + * logdrv_8ld_span8_t - logical drive definition for 8LD controllers + * @lparam : logical drives parameters + * @span : span + * + * 8-LD logical drive with upto 8 spans + */ +typedef struct { + logdrv_param_t lparam; + adap_span_8ld_t span[SPAN_DEPTH_8_SPANS]; +}__attribute__ ((packed)) logdrv_8ld_span8_t; + + +/** + * logdrv_8ld_span4_t - logical drive definition for 8LD controllers + * @lparam : logical drives parameters + * @span : span + * + * 8-LD logical drive with upto 4 spans + */ +typedef struct { + logdrv_param_t lparam; + adap_span_8ld_t span[SPAN_DEPTH_4_SPANS]; +}__attribute__ ((packed)) logdrv_8ld_span4_t; + + +/** + * phys_drive_t - physical device information + * @type : Type of the device + * @cur_status : current status of the device + * @tag_depth : Level of tagging + * @sync_neg : sync negotiation - ENABLE or DISBALE + * @size : configurable size in terms of 512 byte + */ +typedef struct { + uint8_t type; + uint8_t cur_status; + uint8_t tag_depth; + uint8_t sync_neg; + uint32_t size; +}__attribute__ ((packed)) phys_drive_t; + + +/** + * disk_array_40ld_t - disk array for 40LD controllers + * @numldrv : number of logical drives + * @resvd : + * @ldrv : logical drives information + * @pdrv : physical drives information + */ +typedef struct { + uint8_t numldrv; + uint8_t resvd[3]; + logdrv_40ld_t ldrv[MAX_LOGICAL_DRIVES_40LD]; + phys_drive_t pdrv[MBOX_MAX_PHYSICAL_DRIVES]; +}__attribute__ ((packed)) disk_array_40ld_t; + + +/** + * disk_array_8ld_span8_t - disk array for 8LD controllers + * @numldrv : number of logical drives + * @resvd : + * @ldrv : logical drives information + * @pdrv : physical drives information + * + * Disk array for 8LD logical drives with upto 8 spans + */ +typedef struct { + uint8_t numldrv; + uint8_t resvd[3]; + logdrv_8ld_span8_t ldrv[MAX_LOGICAL_DRIVES_8LD]; + phys_drive_t pdrv[MBOX_MAX_PHYSICAL_DRIVES]; +}__attribute__ ((packed)) disk_array_8ld_span8_t; + + +/** + * disk_array_8ld_span4_t - disk array for 8LD controllers + * @numldrv : number of logical drives + * @resvd : + * @ldrv : logical drives information + * @pdrv : physical drives information + * + * Disk array for 8LD logical drives with upto 4 spans + */ +typedef struct { + uint8_t numldrv; + uint8_t resvd[3]; + logdrv_8ld_span4_t ldrv[MAX_LOGICAL_DRIVES_8LD]; + phys_drive_t pdrv[MBOX_MAX_PHYSICAL_DRIVES]; +}__attribute__ ((packed)) disk_array_8ld_span4_t; + + +/** + * private_bios_data - bios private data for boot devices + * @geometry : bits 0-3 - BIOS geometry, 0x0001 - 1GB, 0x0010 - 2GB, + * 0x1000 - 8GB, Others values are invalid + * @unused : bits 4-7 are unused + * @boot_drv : logical drive set as boot drive, 0..7 - for 8LD cards, + * 0..39 - for 40LD cards + * @cksum : 0-(sum of first 13 bytes of this structure) + */ +struct private_bios_data { + uint8_t geometry :4; + uint8_t unused :4; + uint8_t boot_drv; + uint8_t rsvd[12]; + uint16_t cksum; +} __attribute__ ((packed)); + + +/** + * mbox_sgl64 - 64-bit scatter list for mailbox based controllers + * @address : address of the buffer + * @length : data transfer length + */ +typedef struct { + uint64_t address; + uint32_t length; +} __attribute__ ((packed)) mbox_sgl64; + +/** + * mbox_sgl32 - 32-bit scatter list for mailbox based controllers + * @address : address of the buffer + * @length : data transfer length + */ +typedef struct { + uint32_t address; + uint32_t length; +} __attribute__ ((packed)) mbox_sgl32; + +#endif // _MRAID_MBOX_DEFS_H_ + +/* vim: set ts=8 sw=8 tw=78: */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/scsi/megaraid/mega_common.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,283 @@ +/* + * + * Linux MegaRAID device driver + * + * Copyright (c) 2003-2004 LSI Logic Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * FILE : mega_common.h + * + * Libaray of common routine used by all low-level megaraid drivers + */ + +#ifndef _MEGA_COMMON_H_ +#define _MEGA_COMMON_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define LSI_MAX_CHANNELS 16 +#define LSI_MAX_LOGICAL_DRIVES_64LD (64+1) + + +/** + * scb_t - scsi command control block + * @param ccb : command control block for individual driver + * @param list : list of control blocks + * @param gp : general purpose field for LLDs + * @param sno : all SCBs have a serial number + * @param scp : associated scsi command + * @param state : current state of scb + * @param dma_dir : direction of data transfer + * @param dma_type : transfer with sg list, buffer, or no data transfer + * @param dev_channel : actual channel on the device + * @param dev_target : actual target on the device + * @param status : completion status + * + * This is our central data structure to issue commands the each driver. + * Driver specific data structures are maintained in the ccb field. + * scb provides a field 'gp', which can be used by LLD for its own purposes + * + * dev_channel and dev_target must be initialized with the actual channel and + * target on the controller. + */ +typedef struct { + caddr_t ccb; + struct list_head list; + unsigned long gp; + unsigned int sno; + struct scsi_cmnd *scp; + uint32_t state; + uint32_t dma_direction; + uint32_t dma_type; + uint16_t dev_channel; + uint16_t dev_target; + uint32_t status; +} scb_t; + +/* + * SCB states as it transitions from one state to another + */ +#define SCB_FREE 0x0000 /* on the free list */ +#define SCB_ACTIVE 0x0001 /* off the free list */ +#define SCB_PENDQ 0x0002 /* on the pending queue */ +#define SCB_ISSUED 0x0004 /* issued - owner f/w */ +#define SCB_ABORT 0x0008 /* Got an abort for this one */ +#define SCB_RESET 0x0010 /* Got a reset for this one */ + +/* + * DMA types for scb + */ +#define MRAID_DMA_NONE 0x0000 /* no data transfer for this command */ +#define MRAID_DMA_WSG 0x0001 /* data transfer using a sg list */ +#define MRAID_DMA_WBUF 0x0002 /* data transfer using a contiguous buffer */ + + +/** + * struct adapter_t - driver's initialization structure + * @param dpc_h : tasklet handle + * @param pdev : pci configuration pointer for kernel + * @param host : pointer to host structure of mid-layer + * @param host_lock : pointer to appropriate lock + * @param lock : synchronization lock for mid-layer and driver + * @param quiescent : driver is quiescent for now. + * @param outstanding_cmds : number of commands pending in the driver + * @param kscb_list : pointer to the bulk of SCBs pointers for IO + * @param kscb_pool : pool of free scbs for IO + * @param kscb_pool_lock : lock for pool of free scbs + * @param pend_list : pending commands list + * @param pend_list_lock : exlusion lock for pending commands list + * @param completed_list : list of completed commands + * @param completed_list_lock : exclusion lock for list of completed commands + * @param sglen : max sg elements supported + * @param device_ids : to convert kernel device addr to our devices. + * @param raid_device : raid adapter specific pointer + * @param max_channel : maximum channel number supported - inclusive + * @param max_target : max target supported - inclusive + * @param max_lun : max lun supported - inclusive + * @param unique_id : unique identifier for each adapter + * @param irq : IRQ for this adapter + * @param ito : internal timeout value, (-1) means no timeout + * @param ibuf : buffer to issue internal commands + * @param ibuf_dma_h : dma handle for the above buffer + * @param uscb_list : SCB pointers for user cmds, common mgmt module + * @param uscb_pool : pool of SCBs for user commands + * @param uscb_pool_lock : exclusion lock for these SCBs + * @param max_cmds : max outstanding commands + * @param fw_version : firmware version + * @param bios_version : bios version + * @param max_cdb_sz : biggest CDB size supported. + * @param ha : is high availability present - clustering + * @param init_id : initiator ID, the default value should be 7 + * @param max_sectors : max sectors per request + * @param cmd_per_lun : max outstanding commands per LUN + * @param being_detached : set when unloading, no more mgmt calls + * + * + * mraid_setup_device_map() can be called anytime after the device map is + * available and MRAID_GET_DEVICE_MAP() can be called whenever the mapping is + * required, usually from LLD's queue entry point. The formar API sets up the + * MRAID_IS_LOGICAL(adapter_t *, struct scsi_cmnd *) to find out if the + * device in question is a logical drive. + * + * quiescent flag should be set by the driver if it is not accepting more + * commands + * + * NOTE: The fields of this structures are placed to minimize cache misses + */ + +// amount of space required to store the bios and firmware version strings +#define VERSION_SIZE 16 + +typedef struct { + struct tasklet_struct dpc_h; + struct pci_dev *pdev; + struct Scsi_Host *host; + spinlock_t *host_lock; + spinlock_t lock; + uint8_t quiescent; + int outstanding_cmds; + scb_t *kscb_list; + struct list_head kscb_pool; + spinlock_t kscb_pool_lock; + struct list_head pend_list; + spinlock_t pend_list_lock; + struct list_head completed_list; + spinlock_t completed_list_lock; + uint16_t sglen; + int device_ids[LSI_MAX_CHANNELS] + [LSI_MAX_LOGICAL_DRIVES_64LD]; + caddr_t raid_device; + uint8_t max_channel; + uint16_t max_target; + uint8_t max_lun; + + uint32_t unique_id; + uint8_t irq; + uint8_t ito; + caddr_t ibuf; + dma_addr_t ibuf_dma_h; + scb_t *uscb_list; + struct list_head uscb_pool; + spinlock_t uscb_pool_lock; + int max_cmds; + uint8_t fw_version[VERSION_SIZE]; + uint8_t bios_version[VERSION_SIZE]; + uint8_t max_cdb_sz; + uint8_t ha; + uint16_t init_id; + uint16_t max_sectors; + uint16_t cmd_per_lun; + atomic_t being_detached; +} adapter_t; + +#define SCSI_FREE_LIST_LOCK(adapter) (&adapter->kscb_pool_lock) +#define USER_FREE_LIST_LOCK(adapter) (&adapter->uscb_pool_lock) +#define PENDING_LIST_LOCK(adapter) (&adapter->pend_list_lock) +#define COMPLETED_LIST_LOCK(adapter) (&adapter->completed_list_lock) + + +// conversion from scsi command +#define SCP2HOST(scp) (scp)->device->host // to host +#define SCP2HOSTDATA(scp) SCP2HOST(scp)->hostdata // to soft state +#define SCP2CHANNEL(scp) (scp)->device->channel // to channel +#define SCP2TARGET(scp) (scp)->device->id // to target +#define SCP2LUN(scp) (scp)->device->lun // to LUN + +// generic macro to convert scsi command and host to controller's soft state +#define SCSIHOST2ADAP(host) (((caddr_t *)(host->hostdata))[0]) +#define SCP2ADAPTER(scp) (adapter_t *)SCSIHOST2ADAP(SCP2HOST(scp)) + + +/** + * MRAID_GET_DEVICE_MAP - device ids + * @param adp - Adapter's soft state + * @param scp - mid-layer scsi command pointer + * @param p_chan - physical channel on the controller + * @param target - target id of the device or logical drive number + * @param islogical - set if the command is for the logical drive + * + * Macro to retrieve information about device class, logical or physical and + * the corresponding physical channel and target or logical drive number + **/ +#define MRAID_IS_LOGICAL(adp, scp) \ + (SCP2CHANNEL(scp) == (adp)->max_channel) ? 1 : 0 + +#define MRAID_GET_DEVICE_MAP(adp, scp, p_chan, target, islogical) \ + /* \ + * Is the request coming for the virtual channel \ + */ \ + islogical = MRAID_IS_LOGICAL(adp, scp); \ + \ + /* \ + * Get an index into our table of drive ids mapping \ + */ \ + if (islogical) { \ + p_chan = 0xFF; \ + target = \ + (adp)->device_ids[(adp)->max_channel][SCP2TARGET(scp)]; \ + } \ + else { \ + p_chan = ((adp)->device_ids[SCP2CHANNEL(scp)] \ + [SCP2TARGET(scp)] >> 8) & 0xFF; \ + target = ((adp)->device_ids[SCP2CHANNEL(scp)] \ + [SCP2TARGET(scp)] & 0xFF); \ + } + +/* + * ### Helper routines ### + */ +#define LSI_DBGLVL mraid_debug_level // each LLD must define a global + // mraid_debug_level + +#ifdef DEBUG +#if defined (_ASSERT_PANIC) +#define ASSERT_ACTION panic +#else +#define ASSERT_ACTION printk +#endif + +#define ASSERT(expression) \ + if (!(expression)) { \ + ASSERT_ACTION("assertion failed:(%s), file: %s, line: %d:%s\n", \ + #expression, __FILE__, __LINE__, __FUNCTION__); \ + } +#else +#define ASSERT(expression) +#endif + +/* + * struct mraid_pci_blk - structure holds DMA memory block info + * @param vaddr : virtual address to a memory block + * @param dma_addr : DMA handle to a memory block + * + * This structure is filled up for the caller. It is the responsibilty of the + * caller to allocate this array big enough to store addresses for all + * requested elements + */ +struct mraid_pci_blk { + caddr_t vaddr; + dma_addr_t dma_addr; +}; + +#endif // _MEGA_COMMON_H_ + +// vim: set ts=8 sw=8 tw=78: --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/scsi/megaraid/megaraid_ioctl.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,291 @@ +/* + * + * Linux MegaRAID device driver + * + * Copyright (c) 2003-2004 LSI Logic Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * FILE : megaraid_ioctl.h + * + * Definitions to interface with user level applications + */ + +#ifndef _MEGARAID_IOCTL_H_ +#define _MEGARAID_IOCTL_H_ + +#include +#include + +#include "mbox_defs.h" + +/** + * con_log() - console log routine + * @param level : indicates the severity of the message. + * @fparam mt : format string + * + * con_log displays the error messages on the console based on the current + * debug level. Also it attaches the appropriate kernel severity level with + * the message. + * + * + * consolge messages debug levels + */ +#define CL_ANN 0 /* print unconditionally, announcements */ +#define CL_DLEVEL1 1 /* debug level 1, informative */ +#define CL_DLEVEL2 2 /* debug level 2, verbose */ +#define CL_DLEVEL3 3 /* debug level 3, very verbose */ + +#define con_log(level, fmt) if (LSI_DBGLVL >= level) printk fmt; + +/* + * Definitions & Declarations needed to use common management module + */ + +#define MEGAIOC_MAGIC 'm' +#define MEGAIOCCMD _IOWR(MEGAIOC_MAGIC, 0, mimd_t) + +#define MEGAIOC_QNADAP 'm' /* Query # of adapters */ +#define MEGAIOC_QDRVRVER 'e' /* Query driver version */ +#define MEGAIOC_QADAPINFO 'g' /* Query adapter information */ + +#define USCSICMD 0x80 +#define UIOC_RD 0x00001 +#define UIOC_WR 0x00002 + +#define MBOX_CMD 0x00000 +#define GET_DRIVER_VER 0x10000 +#define GET_N_ADAP 0x20000 +#define GET_ADAP_INFO 0x30000 +#define GET_CAP 0x40000 +#define GET_STATS 0x50000 +#define GET_IOCTL_VERSION 0x01 + +#define EXT_IOCTL_SIGN_SZ 16 +#define EXT_IOCTL_SIGN "$$_EXTD_IOCTL_$$" + +#define MBOX_LEGACY 0x00 /* ioctl has legacy mbox*/ +#define MBOX_HPE 0x01 /* ioctl has hpe mbox */ + +#define APPTYPE_MIMD 0x00 /* old existing apps */ +#define APPTYPE_UIOC 0x01 /* new apps using uioc */ + +#define IOCTL_ISSUE 0x00000001 /* Issue ioctl */ +#define IOCTL_ABORT 0x00000002 /* Abort previous ioctl */ + +#define DRVRTYPE_MBOX 0x00000001 /* regular mbox driver */ +#define DRVRTYPE_HPE 0x00000002 /* new hpe driver */ + +#define MKADAP(adapno) (MEGAIOC_MAGIC << 8 | (adapno) ) +#define GETADAP(mkadap) ((mkadap) ^ MEGAIOC_MAGIC << 8) + +#define MAX_DMA_POOLS 5 /* 4k, 8k, 16k, 32k, 64k*/ + + +/** + * struct uioc_t - the common ioctl packet structure + * + * @signature : Must be "$$_EXTD_IOCTL_$$" + * @mb_type : Type of the mail box (MB_LEGACY or MB_HPE) + * @app_type : Type of the issuing application (existing or new) + * @opcode : Opcode of the command + * @adapno : Adapter number + * @cmdbuf : Pointer to buffer - can point to mbox or plain data buffer + * @xferlen : xferlen for DCMD and non mailbox commands + * @data_dir : Direction of the data transfer + * @status : Status from the driver + * @reserved : reserved bytes for future expansion + * + * @user_data : user data transfer address is saved in this + * @user_data_len: length of the data buffer sent by user app + * @user_pthru : user passthru address is saves in this (null if DCMD) + * @pthru32 : kernel address passthru (allocated per kioc) + * @pthru32_h : physicall address of @pthru32 + * @list : for kioc free pool list maintenance + * @done : call back routine for llds to call when kioc is completed + * @buf_vaddr : dma pool buffer attached to kioc for data transfer + * @buf_paddr : physical address of the dma pool buffer + * @pool_index : index of the dma pool that @buf_vaddr is taken from + * @free_buf : indicates if buffer needs to be freed after kioc completes + * + * Note : All LSI drivers understand only this packet. Any other + * : format sent by applications would be converted to this. + */ +typedef struct uioc { + +/* User Apps: */ + + uint8_t signature[EXT_IOCTL_SIGN_SZ]; + uint16_t mb_type; + uint16_t app_type; + uint32_t opcode; + uint32_t adapno; + uint64_t cmdbuf; + uint32_t xferlen; + uint32_t data_dir; + int32_t status; + uint8_t reserved[128]; + +/* Driver Data: */ + void __user * user_data; + uint32_t user_data_len; + mraid_passthru_t __user *user_pthru; + + mraid_passthru_t *pthru32; + dma_addr_t pthru32_h; + + struct list_head list; + void (*done)(struct uioc*); + + caddr_t buf_vaddr; + dma_addr_t buf_paddr; + uint8_t pool_index; + uint8_t free_buf; + +} __attribute__ ((aligned(1024),packed)) uioc_t; + + +/** + * struct mraid_hba_info - information about the controller + * + * @param pci_vendor_id : PCI vendor id + * @param pci_device_id : PCI device id + * @param subsystem_vendor_id : PCI subsystem vendor id + * @param subsystem_device_id : PCI subsystem device id + * @param baseport : base port of hba memory + * @param pci_bus : PCI bus + * @param pci_dev_fn : PCI device/function values + * @param irq : interrupt vector for the device + * + * Extended information of 256 bytes about the controller. Align on the single + * byte boundary so that 32-bit applications can be run on 64-bit platform + * drivers withoug re-compilation. + * NOTE: reduce the number of reserved bytes whenever new field are added, so + * that total size of the structure remains 256 bytes. + */ +typedef struct mraid_hba_info { + + uint16_t pci_vendor_id; + uint16_t pci_device_id; + uint16_t subsys_vendor_id; + uint16_t subsys_device_id; + + uint64_t baseport; + uint8_t pci_bus; + uint8_t pci_dev_fn; + uint8_t pci_slot; + uint8_t irq; + + uint32_t unique_id; + uint32_t host_no; + + uint8_t num_ldrv; +} __attribute__ ((aligned(256), packed)) mraid_hba_info_t; + + +/** + * mcontroller : adapter info structure for old mimd_t apps + * + * @base : base address + * @irq : irq number + * @numldrv : number of logical drives + * @pcibus : pci bus + * @pcidev : pci device + * @pcifun : pci function + * @pciid : pci id + * @pcivendor : vendor id + * @pcislot : slot number + * @uid : unique id + */ +typedef struct mcontroller { + + uint64_t base; + uint8_t irq; + uint8_t numldrv; + uint8_t pcibus; + uint16_t pcidev; + uint8_t pcifun; + uint16_t pciid; + uint16_t pcivendor; + uint8_t pcislot; + uint32_t uid; + +} __attribute__ ((packed)) mcontroller_t; + + +/** + * mm_dmapool_t : Represents one dma pool with just one buffer + * + * @vaddr : Virtual address + * @paddr : DMA physicall address + * @bufsize : In KB - 4 = 4k, 8 = 8k etc. + * @handle : Handle to the dma pool + * @lock : lock to synchronize access to the pool + * @in_use : If pool already in use, attach new block + */ +typedef struct mm_dmapool { + caddr_t vaddr; + dma_addr_t paddr; + uint32_t buf_size; + struct dma_pool *handle; + spinlock_t lock; + uint8_t in_use; +} mm_dmapool_t; + + +/** + * mraid_mmadp_t: Structure that drivers pass during (un)registration + * + * @unique_id : Any unique id (usually PCI bus+dev+fn) + * @drvr_type : megaraid or hpe (DRVRTYPE_MBOX or DRVRTYPE_HPE) + * @drv_data : Driver specific; not touched by the common module + * @timeout : timeout for issued kiocs + * @max_kioc : Maximum ioctl packets acceptable by the lld + * @pdev : pci dev; used for allocating dma'ble memory + * @issue_uioc : Driver supplied routine to issue uioc_t commands + * : issue_uioc(drvr_data, kioc, ISSUE/ABORT, uioc_done) + * @list : attach with the global list of adapters + * @kioc_list : block of mem for @max_kioc number of kiocs + * @kioc_pool : pool of free kiocs + * @kioc_pool_lock : protection for free pool + * @kioc_semaphore : so as not to exceed @max_kioc parallel ioctls + * @mbox_list : block of mem for @max_kioc number of mboxes + * @pthru_dma_pool : DMA pool to allocate passthru packets + * @dma_pool_list : array of dma pools + */ + +typedef struct mraid_mmadp { + +/* Filled by driver */ + + uint32_t unique_id; + uint32_t drvr_type; + unsigned long drvr_data; + uint8_t timeout; + uint8_t max_kioc; + + struct pci_dev *pdev; + + int(*issue_uioc)(unsigned long, uioc_t *, uint32_t); + +/* Maintained by common module */ + + struct list_head list; + uioc_t *kioc_list; + struct list_head kioc_pool; + spinlock_t kioc_pool_lock; + struct semaphore kioc_semaphore; + + mbox64_t *mbox_list; + struct dma_pool *pthru_dma_pool; + mm_dmapool_t dma_pool_list[MAX_DMA_POOLS]; + +} mraid_mmadp_t; + +int mraid_mm_register_adp(mraid_mmadp_t *); +int mraid_mm_unregister_adp(uint32_t); + +#endif /* _MEGARAID_IOCTL_H_ */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/scsi/megaraid/megaraid_mbox.c 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,3891 @@ +/* + * + * Linux MegaRAID device driver + * + * Copyright (c) 2003-2004 LSI Logic Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * FILE : megaraid_mbox.c + * Version : v2.20.3.1 (August 24 2004) + * + * Authors: + * Atul Mukker + * Sreenivas Bagalkote + * Manoj Jose + * + * List of supported controllers + * + * OEM Product Name VID DID SSVID SSID + * --- ------------ --- --- ---- ---- + * Dell PERC3/QC 101E 1960 1028 0471 + * Dell PERC3/DC 101E 1960 1028 0493 + * Dell PERC3/SC 101E 1960 1028 0475 + * Dell PERC3/Di 1028 1960 1028 0123 + * Dell PERC4/SC 1000 1960 1028 0520 + * Dell PERC4/DC 1000 1960 1028 0518 + * Dell PERC4/QC 1000 0407 1028 0531 + * Dell PERC4/Di 1028 000F 1028 014A + * Dell PERC 4e/Si 1028 0013 1028 016c + * Dell PERC 4e/Di 1028 0013 1028 016d + * Dell PERC 4e/Di 1028 0013 1028 016e + * Dell PERC 4e/Di 1028 0013 1028 016f + * Dell PERC 4e/Di 1028 0013 1028 0170 + * Dell PERC 4e/DC 1000 0408 1028 0002 + * Dell PERC 4e/SC 1000 0408 1028 0001 + * + * + * LSI MegaRAID SCSI 320-0 1000 1960 1000 A520 + * LSI MegaRAID SCSI 320-1 1000 1960 1000 0520 + * LSI MegaRAID SCSI 320-2 1000 1960 1000 0518 + * LSI MegaRAID SCSI 320-0X 1000 0407 1000 0530 + * LSI MegaRAID SCSI 320-2X 1000 0407 1000 0532 + * LSI MegaRAID SCSI 320-4X 1000 0407 1000 0531 + * LSI MegaRAID SCSI 320-1E 1000 0408 1000 0001 + * LSI MegaRAID SCSI 320-2E 1000 0408 1000 0002 + * LSI MegaRAID SATA 150-4 1000 1960 1000 4523 + * LSI MegaRAID SATA 150-6 1000 1960 1000 0523 + * LSI MegaRAID SATA 300-4X 1000 0409 1000 3004 + * LSI MegaRAID SATA 300-8X 1000 0409 1000 3008 + * + * INTEL RAID Controller SRCU42X 1000 0407 8086 0532 + * INTEL RAID Controller SRCS16 1000 1960 8086 0523 + * INTEL RAID Controller SRCU42E 1000 0408 8086 0002 + * INTEL RAID Controller SRCZCRX 1000 0407 8086 0530 + * INTEL RAID Controller SRCS28X 1000 0409 8086 3008 + * INTEL RAID Controller SROMBU42E 1000 0408 8086 3431 + * INTEL RAID Controller SROMBU42E 1000 0408 8086 3499 + * INTEL RAID Controller SRCU51L 1000 1960 8086 0520 + * + * + * FSC MegaRAID PCI Express ROMB 1000 0408 1734 1065 + * + * + * ACER MegaRAID ROMB-2E 1000 0408 1025 004D + * + * + * For history of changes, see Documentation/ChangeLog.megaraid + */ + +#include "megaraid_mbox.h" + +static int megaraid_init(void); +static void megaraid_exit(void); + +static int megaraid_probe_one(struct pci_dev*, const struct pci_device_id *); +static void megaraid_detach_one(struct pci_dev *); +static void megaraid_mbox_shutdown(struct device *); + +static int megaraid_io_attach(adapter_t *); +static void megaraid_io_detach(adapter_t *); + +static int megaraid_init_mbox(adapter_t *); +static void megaraid_fini_mbox(adapter_t *); + +static int megaraid_alloc_cmd_packets(adapter_t *); +static void megaraid_free_cmd_packets(adapter_t *); + +static int megaraid_mbox_setup_dma_pools(adapter_t *); +static void megaraid_mbox_teardown_dma_pools(adapter_t *); + +static int megaraid_abort_handler(struct scsi_cmnd *); +static int megaraid_reset_handler(struct scsi_cmnd *); + +static int mbox_post_sync_cmd(adapter_t *, uint8_t []); +static int mbox_post_sync_cmd_fast(adapter_t *, uint8_t []); +static int megaraid_busywait_mbox(mraid_device_t *); +static int megaraid_mbox_product_info(adapter_t *); +static int megaraid_mbox_extended_cdb(adapter_t *); +static int megaraid_mbox_support_ha(adapter_t *, uint16_t *); +static int megaraid_mbox_support_random_del(adapter_t *); +static int megaraid_mbox_get_max_sg(adapter_t *); +static void megaraid_mbox_enum_raid_scsi(adapter_t *); +static void megaraid_mbox_flush_cache(adapter_t *); + +static void megaraid_mbox_display_scb(adapter_t *, scb_t *); +static void megaraid_mbox_setup_device_map(adapter_t *); + +static int megaraid_queue_command(struct scsi_cmnd *, + void (*)(struct scsi_cmnd *)); +static scb_t *megaraid_mbox_build_cmd(adapter_t *, struct scsi_cmnd *, int *); +static void megaraid_mbox_runpendq(adapter_t *, scb_t *); +static void megaraid_mbox_prepare_pthru(adapter_t *, scb_t *, + struct scsi_cmnd *); +static void megaraid_mbox_prepare_epthru(adapter_t *, scb_t *, + struct scsi_cmnd *); + +static irqreturn_t megaraid_isr(int, void *, struct pt_regs *); + +static void megaraid_mbox_dpc(unsigned long); + +static int megaraid_cmm_register(adapter_t *); +static int megaraid_cmm_unregister(adapter_t *); +static int megaraid_mbox_mm_handler(unsigned long, uioc_t *, uint32_t); +static int megaraid_mbox_mm_command(adapter_t *, uioc_t *); +static void megaraid_mbox_mm_done(adapter_t *, scb_t *); +static int gather_hbainfo(adapter_t *, mraid_hba_info_t *); +static int wait_till_fw_empty(adapter_t *); + + + +MODULE_AUTHOR("LSI Logic Corporation"); +MODULE_DESCRIPTION("LSI Logic MegaRAID Mailbox Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(MEGARAID_VERSION); + +/* + * ### modules parameters for driver ### + */ + +/** + * Set to enable driver to expose unconfigured disk to kernel + */ +static int megaraid_expose_unconf_disks = 0; +module_param_named(unconf_disks, megaraid_expose_unconf_disks, int, 0); +MODULE_PARM_DESC(unconf_disks, + "Set to expose unconfigured disks to kernel (default=0)"); + +/** + * driver wait time if the adapter's mailbox is busy + */ +static unsigned int max_mbox_busy_wait = MBOX_BUSY_WAIT; +module_param_named(busy_wait, max_mbox_busy_wait, int, 0); +MODULE_PARM_DESC(busy_wait, + "Max wait for mailbox in microseconds if busy (default=10)"); + +/** + * number of sectors per IO command + */ +static unsigned int megaraid_max_sectors = MBOX_MAX_SECTORS; +module_param_named(max_sectors, megaraid_max_sectors, int, 0); +MODULE_PARM_DESC(max_sectors, + "Maximum number of sectors per IO command (default=128)"); + +/** + * number of commands per logical unit + */ +static unsigned int megaraid_cmd_per_lun = MBOX_DEF_CMD_PER_LUN; +module_param_named(cmd_per_lun, megaraid_cmd_per_lun, int, 0); +MODULE_PARM_DESC(cmd_per_lun, + "Maximum number of commands per logical unit (default=64)"); + + +/** + * Fast driver load option, skip scanning for physical devices during load. + * This would result in non-disk devices being skipped during driver load + * time. These can be later added though, using /proc/scsi/scsi + */ +static unsigned int megaraid_fast_load = 0; +module_param_named(fast_load, megaraid_fast_load, int, 0); +MODULE_PARM_DESC(fast_load, + "Faster loading of the driver, skips physical devices! (default=0)"); + + +/** + * mraid_debug level - threshold for amount of information to be displayed by + * the driver. This level can be changed through modules parameters, ioctl or + * sysfs/proc interface. By default, print the announcement messages only. + */ +int mraid_debug_level = CL_ANN; +module_param_named(debug_level, mraid_debug_level, int, 0); +MODULE_PARM_DESC(debug_level, "Debug level for driver (default=0)"); + +/* + * ### global data ### + */ +static uint8_t megaraid_mbox_version[8] = + { 0x02, 0x20, 0x02, 0x00, 7, 22, 20, 4 }; + + +/* + * PCI table for all supported controllers. + */ +static struct pci_device_id pci_id_table_g[] = { + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC4_DI_DISCOVERY, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4_DI_DISCOVERY, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_PERC4_SC, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4_SC, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_PERC4_DC, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4_DC, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_PERC4_QC, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4_QC, + }, + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC4_DI_EVERGLADES, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4_DI_EVERGLADES, + }, + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC4E_SI_BIGBEND, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4E_SI_BIGBEND, + }, + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC4E_DI_KOBUK, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4E_DI_KOBUK, + }, + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC4E_DI_CORVETTE, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4E_DI_CORVETTE, + }, + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC4E_DI_EXPEDITION, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4E_DI_EXPEDITION, + }, + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC4E_DI_GUADALUPE, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4E_DI_GUADALUPE, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_PERC4E_DC_320_2E, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4E_DC_320_2E, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_PERC4E_SC_320_1E, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC4E_SC_320_1E, + }, + { + PCI_VENDOR_ID_AMI, + PCI_DEVICE_ID_AMI_MEGARAID3, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC3_QC, + }, + { + PCI_VENDOR_ID_AMI, + PCI_DEVICE_ID_AMI_MEGARAID3, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC3_DC, + }, + { + PCI_VENDOR_ID_AMI, + PCI_DEVICE_ID_AMI_MEGARAID3, + PCI_VENDOR_ID_DELL, + PCI_SUBSYS_ID_PERC3_SC, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_0, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_0, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_1, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_1, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_2, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_2, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_0x, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_0x, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_2x, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_2x, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_4x, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_4x, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_1E, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_1E, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SCSI_320_2E, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SCSI_320_2E, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_I4_133_RAID, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_I4_133_RAID, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SATA_150_4, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SATA_150_4, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SATA_150_6, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SATA_150_6, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SATA_300_4x, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SATA_300_4x, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_SATA_300_8x, + PCI_VENDOR_ID_LSI_LOGIC, + PCI_SUBSYS_ID_MEGARAID_SATA_300_8x, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SRCU42X, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SRCU42X, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SRCS16, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SRCS16, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SRCU42E, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SRCU42E, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SRCZCRX, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SRCZCRX, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SRCS28X, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SRCS28X, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SROMBU42E_ALIEF, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SROMBU42E_ALIEF, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SROMBU42E_HARWICH, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SROMBU42E_HARWICH, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_INTEL_RAID_SRCU41L_LAKE_SHETEK, + PCI_VENDOR_ID_INTEL, + PCI_SUBSYS_ID_INTEL_RAID_SRCU41L_LAKE_SHETEK, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_FSC_MEGARAID_PCI_EXPRESS_ROMB, + PCI_SUBSYS_ID_FSC, + PCI_SUBSYS_ID_FSC_MEGARAID_PCI_EXPRESS_ROMB, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_MEGARAID_ACER_ROMB_2E, + PCI_VENDOR_ID_AI, + PCI_SUBSYS_ID_MEGARAID_ACER_ROMB_2E, + }, + {0} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(pci, pci_id_table_g); + + +static struct pci_driver megaraid_pci_driver_g = { + .name = "megaraid", + .id_table = pci_id_table_g, + .probe = megaraid_probe_one, + .remove = __devexit_p(megaraid_detach_one), + .driver = { + .shutdown = megaraid_mbox_shutdown, + } +}; + + +/* + * Scsi host template for megaraid unified driver + */ +static struct scsi_host_template megaraid_template_g = { + .module = THIS_MODULE, + .name = "LSI Logic MegaRAID driver", + .proc_name = "megaraid", + .queuecommand = megaraid_queue_command, + .eh_abort_handler = megaraid_abort_handler, + .eh_device_reset_handler = megaraid_reset_handler, + .eh_bus_reset_handler = megaraid_reset_handler, + .eh_host_reset_handler = megaraid_reset_handler, + .use_clustering = ENABLE_CLUSTERING, +}; + + +/** + * megaraid_init - module load hook + * + * We register ourselves as hotplug enabled module and let PCI subsystem + * discover our adaters + **/ +static int __init +megaraid_init(void) +{ + int rval; + + // Announce the driver version + con_log(CL_ANN, (KERN_INFO "megaraid: %s %s\n", MEGARAID_VERSION, + MEGARAID_EXT_VERSION)); + + // check validity of module parameters + if (megaraid_cmd_per_lun > MBOX_MAX_SCSI_CMDS) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid mailbox: max commands per lun reset to %d\n", + MBOX_MAX_SCSI_CMDS)); + + megaraid_cmd_per_lun = MBOX_MAX_SCSI_CMDS; + } + + + // register as a PCI hot-plug driver module + if ((rval = pci_module_init(&megaraid_pci_driver_g))) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: could not register hotplug support.\n")); + } + + return rval; +} + + +/** + * megaraid_exit - driver unload entry point + * + * We simply unwrap the megaraid_init routine here + */ +static void __exit +megaraid_exit(void) +{ + con_log(CL_DLEVEL1, (KERN_NOTICE "megaraid: unloading framework\n")); + + // unregister as PCI hotplug driver + pci_unregister_driver(&megaraid_pci_driver_g); + + return; +} + + +/** + * megaraid_probe_one - PCI hotplug entry point + * @param pdev : handle to this controller's PCI configuration space + * @param id : pci device id of the class of controllers + * + * This routine should be called whenever a new adapter is detected by the + * PCI hotplug susbsytem. + **/ +static int __devinit +megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) +{ + adapter_t *adapter; + + + // detected a new controller + con_log(CL_ANN, (KERN_INFO + "megaraid: probe new device %#4.04x:%#4.04x:%#4.04x:%#4.04x: ", + pdev->vendor, pdev->device, pdev->subsystem_vendor, + pdev->subsystem_device)); + + con_log(CL_ANN, ("bus %d:slot %d:func %d\n", pdev->bus->number, + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn))); + + if (pci_enable_device(pdev)) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: pci_enable_device failed\n")); + + return -ENODEV; + } + + // Enable bus-mastering on this controller + pci_set_master(pdev); + + // Allocate the per driver initialization structure + adapter = kmalloc(sizeof(adapter_t), GFP_KERNEL); + + if (adapter == NULL) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: out of memory, %s %d.\n", __FUNCTION__, __LINE__)); + + goto out_probe_one; + } + memset(adapter, 0, sizeof(adapter_t)); + + + // set up PCI related soft state and other pre-known parameters + adapter->unique_id = pdev->bus->number << 8 | pdev->devfn; + adapter->irq = pdev->irq; + adapter->pdev = pdev; + + atomic_set(&adapter->being_detached, 0); + + // Setup the default DMA mask. This would be changed later on + // depending on hardware capabilities + if (pci_set_dma_mask(adapter->pdev, 0xFFFFFFFF) != 0) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid: pci_set_dma_mask failed:%d\n", __LINE__)); + + goto out_free_adapter; + } + + + // Initialize the synchronization lock for kernel and LLD + spin_lock_init(&adapter->lock); + adapter->host_lock = &adapter->lock; + + + // Initialize the command queues: the list of free SCBs and the list + // of pending SCBs. + INIT_LIST_HEAD(&adapter->kscb_pool); + spin_lock_init(SCSI_FREE_LIST_LOCK(adapter)); + + INIT_LIST_HEAD(&adapter->pend_list); + spin_lock_init(PENDING_LIST_LOCK(adapter)); + + INIT_LIST_HEAD(&adapter->completed_list); + spin_lock_init(COMPLETED_LIST_LOCK(adapter)); + + + // Start the mailbox based controller + if (megaraid_init_mbox(adapter) != 0) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: maibox adapter did not initialize\n")); + + goto out_free_adapter; + } + + // Register with LSI Common Management Module + if (megaraid_cmm_register(adapter) != 0) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid: could not register with management module\n")); + + goto out_fini_mbox; + } + + // setup adapter handle in PCI soft state + pci_set_drvdata(pdev, adapter); + + // attach with scsi mid-layer + if (megaraid_io_attach(adapter) != 0) { + + con_log(CL_ANN, (KERN_WARNING "megaraid: io attach failed\n")); + + goto out_cmm_unreg; + } + + return 0; + +out_cmm_unreg: + pci_set_drvdata(pdev, NULL); + megaraid_cmm_unregister(adapter); +out_fini_mbox: + megaraid_fini_mbox(adapter); +out_free_adapter: + kfree(adapter); +out_probe_one: + pci_disable_device(pdev); + + return -ENODEV; +} + + +/** + * megaraid_detach_one - release the framework resources and call LLD release + * routine + * @param pdev : handle for our PCI cofiguration space + * + * This routine is called during driver unload. We free all the allocated + * resources and call the corresponding LLD so that it can also release all + * its resources. + * + * This routine is also called from the PCI hotplug system + **/ +static void +megaraid_detach_one(struct pci_dev *pdev) +{ + adapter_t *adapter; + struct Scsi_Host *host; + + + // Start a rollback on this adapter + adapter = pci_get_drvdata(pdev); + + if (!adapter) { + con_log(CL_ANN, (KERN_CRIT + "megaraid: Invalid detach on %#4.04x:%#4.04x:%#4.04x:%#4.04x\n", + pdev->vendor, pdev->device, pdev->subsystem_vendor, + pdev->subsystem_device)); + + return; + } + else { + con_log(CL_ANN, (KERN_NOTICE + "megaraid: detaching device %#4.04x:%#4.04x:%#4.04x:%#4.04x\n", + pdev->vendor, pdev->device, pdev->subsystem_vendor, + pdev->subsystem_device)); + } + + + host = adapter->host; + + // do not allow any more requests from the management module for this + // adapter. + // FIXME: How do we account for the request which might still be + // pending with us? + atomic_set(&adapter->being_detached, 1); + + // detach from the IO sub-system + megaraid_io_detach(adapter); + + // reset the device state in the PCI structure. We check this + // condition when we enter here. If the device state is NULL, + // that would mean the device has already been removed + pci_set_drvdata(pdev, NULL); + + // Unregister from common management module + // + // FIXME: this must return success or failure for conditions if there + // is a command pending with LLD or not. + megaraid_cmm_unregister(adapter); + + // finalize the mailbox based controller and release all resources + megaraid_fini_mbox(adapter); + + kfree(adapter); + + scsi_host_put(host); + + pci_disable_device(pdev); + + return; +} + + +/** + * megaraid_mbox_shutdown - PCI shutdown for megaraid HBA + * @param device : generice driver model device + * + * Shutdown notification, perform flush cache + */ +static void +megaraid_mbox_shutdown(struct device *device) +{ + adapter_t *adapter = pci_get_drvdata(to_pci_dev(device)); + static int counter; + + if (!adapter) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: null device in shutdown\n")); + return; + } + + // flush caches now + con_log(CL_ANN, (KERN_INFO "megaraid: flushing adapter %d...", + counter++)); + + megaraid_mbox_flush_cache(adapter); + + con_log(CL_ANN, ("done\n")); +} + + +/** + * megaraid_io_attach - attach a device with the IO subsystem + * @param adapter : controller's soft state + * + * Attach this device with the IO subsystem + **/ +static int +megaraid_io_attach(adapter_t *adapter) +{ + struct Scsi_Host *host; + + // Initialize SCSI Host structure + host = scsi_host_alloc(&megaraid_template_g, 8); + if (!host) { + con_log(CL_ANN, (KERN_WARNING + "megaraid mbox: scsi_register failed\n")); + + return -1; + } + + SCSIHOST2ADAP(host) = (caddr_t)adapter; + adapter->host = host; + + // export the parameters required by the mid-layer + scsi_assign_lock(host, adapter->host_lock); + scsi_set_device(host, &adapter->pdev->dev); + + host->irq = adapter->irq; + host->unique_id = adapter->unique_id; + host->can_queue = adapter->max_cmds; + host->this_id = adapter->init_id; + host->sg_tablesize = adapter->sglen; + host->max_sectors = adapter->max_sectors; + host->cmd_per_lun = adapter->cmd_per_lun; + host->max_channel = adapter->max_channel; + host->max_id = adapter->max_target; + host->max_lun = adapter->max_lun; + + + // notify mid-layer about the new controller + if (scsi_add_host(host, &adapter->pdev->dev)) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid mbox: scsi_add_host failed\n")); + + scsi_host_put(host); + + return -1; + } + + scsi_scan_host(host); + + return 0; +} + + +/** + * megaraid_io_detach - detach a device from the IO subsystem + * @param adapter : controller's soft state + * + * Detach this device from the IO subsystem + **/ +static void +megaraid_io_detach(adapter_t *adapter) +{ + struct Scsi_Host *host; + + con_log(CL_DLEVEL1, (KERN_INFO "megaraid: io detach\n")); + + host = adapter->host; + + scsi_remove_host(host); + + return; +} + + +/* + * START: Mailbox Low Level Driver + * + * This is section specific to the single mailbox based controllers + */ + +/** + * megaraid_init_mbox - initialize controller + * @param adapter - our soft state + * + * . Allocate 16-byte aligned mailbox memory for firmware handshake + * . Allocate controller's memory resources + * . Find out all initialization data + * . Allocate memory required for all the commands + * . Use internal library of FW routines, build up complete soft state + */ +static int __init +megaraid_init_mbox(adapter_t *adapter) +{ + struct pci_dev *pdev; + mraid_device_t *raid_dev; + int i; + + + adapter->ito = MBOX_TIMEOUT; + pdev = adapter->pdev; + + /* + * Allocate and initialize the init data structure for mailbox + * controllers + */ + raid_dev = kmalloc(sizeof(mraid_device_t), GFP_KERNEL); + if (raid_dev == NULL) return -1; + + memset(raid_dev, 0, sizeof(mraid_device_t)); + + /* + * Attach the adapter soft state to raid device soft state + */ + adapter->raid_device = (caddr_t)raid_dev; + raid_dev->fast_load = megaraid_fast_load; + + + // our baseport + raid_dev->baseport = pci_resource_start(pdev, 0); + + if (pci_request_regions(pdev, "MegaRAID: LSI Logic Corporation") != 0) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid: mem region busy\n")); + + goto out_free_raid_dev; + } + + raid_dev->baseaddr = (unsigned long) + ioremap_nocache(raid_dev->baseport, 128); + + if (!raid_dev->baseaddr) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid: could not map hba memory\n") ); + + goto out_release_regions; + } + + // + // Setup the rest of the soft state using the library of FW routines + // + + // request IRQ and register the interrupt service routine + if (request_irq(adapter->irq, megaraid_isr, SA_SHIRQ, "megaraid", + adapter)) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid: Couldn't register IRQ %d!\n", adapter->irq)); + + goto out_iounmap; + } + + + // initialize the mutual exclusion lock for the mailbox + spin_lock_init(&raid_dev->mailbox_lock); + + // allocate memory required for commands + if (megaraid_alloc_cmd_packets(adapter) != 0) { + goto out_free_irq; + } + + // Product info + if (megaraid_mbox_product_info(adapter) != 0) { + goto out_alloc_cmds; + } + + // Do we support extended CDBs + adapter->max_cdb_sz = 10; + if (megaraid_mbox_extended_cdb(adapter) == 0) { + adapter->max_cdb_sz = 16; + } + + /* + * Do we support cluster environment, if we do, what is the initiator + * id. + * NOTE: In a non-cluster aware firmware environment, the LLD should + * return 7 as initiator id. + */ + adapter->ha = 0; + adapter->init_id = -1; + if (megaraid_mbox_support_ha(adapter, &adapter->init_id) == 0) { + adapter->ha = 1; + } + + /* + * Prepare the device ids array to have the mapping between the kernel + * device address and megaraid device address. + * We export the physical devices on their actual addresses. The + * logical drives are exported on a virtual SCSI channel + */ + megaraid_mbox_setup_device_map(adapter); + + // If the firmware supports random deletion, update the device id map + if (megaraid_mbox_support_random_del(adapter)) { + + // Change the logical drives numbers in device_ids array one + // slot in device_ids is reserved for target id, that's why + // "<=" below + for (i = 0; i <= MAX_LOGICAL_DRIVES_40LD; i++) { + adapter->device_ids[adapter->max_channel][i] += 0x80; + } + adapter->device_ids[adapter->max_channel][adapter->init_id] = + 0xFF; + } + + /* + * find out the maximum number of scatter-gather elements supported by + * this firmware + */ + adapter->sglen = megaraid_mbox_get_max_sg(adapter); + + // enumerate RAID and SCSI channels so that all devices on SCSI + // channels can later be exported, including disk devices + megaraid_mbox_enum_raid_scsi(adapter); + + /* + * Other parameters required by upper layer + * + * maximum number of sectors per IO command + */ + adapter->max_sectors = megaraid_max_sectors; + + /* + * number of queued commands per LUN. + */ + adapter->cmd_per_lun = megaraid_cmd_per_lun; + + // Set the DMA mask to 64-bit. All supported controllers as capable of + // DMA in this range + if (pci_set_dma_mask(adapter->pdev, 0xFFFFFFFFFFFFFFFFULL) != 0) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid: could not set DMA mask for 64-bit.\n")); + + goto out_alloc_cmds; + } + + // setup tasklet for DPC + tasklet_init(&adapter->dpc_h, megaraid_mbox_dpc, + (unsigned long)adapter); + + con_log(CL_DLEVEL1, (KERN_INFO + "megaraid mbox hba successfully initialized\n")); + + return 0; + +out_alloc_cmds: + megaraid_free_cmd_packets(adapter); +out_free_irq: + free_irq(adapter->irq, adapter); +out_iounmap: + iounmap((caddr_t)raid_dev->baseaddr); +out_release_regions: + pci_release_regions(pdev); +out_free_raid_dev: + kfree(raid_dev); + + return -1; +} + + +/** + * megaraid_fini_mbox - undo controller initialization + * @param adapter : our soft state + */ +static void +megaraid_fini_mbox(adapter_t *adapter) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + + // flush all caches + megaraid_mbox_flush_cache(adapter); + + tasklet_kill(&adapter->dpc_h); + + megaraid_free_cmd_packets(adapter); + + free_irq(adapter->irq, adapter); + + iounmap((caddr_t)raid_dev->baseaddr); + + pci_release_regions(adapter->pdev); + + kfree(raid_dev); + + return; +} + + +/** + * megaraid_alloc_cmd_packets - allocate shared mailbox + * @param adapter : soft state of the raid controller + * + * Allocate and align the shared mailbox. This maibox is used to issue + * all the commands. For IO based controllers, the mailbox is also regsitered + * with the FW. Allocate memory for all commands as well. + * This is our big allocator + */ +static int +megaraid_alloc_cmd_packets(adapter_t *adapter) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + struct pci_dev *pdev; + unsigned long align; + scb_t *scb; + mbox_ccb_t *ccb; + struct mraid_pci_blk *epthru_pci_blk; + struct mraid_pci_blk *sg_pci_blk; + struct mraid_pci_blk *mbox_pci_blk; + int i; + + pdev = adapter->pdev; + + /* + * Setup the mailbox + * Allocate the common 16-byte aligned memory for the handshake + * mailbox. + */ + raid_dev->una_mbox64 = pci_alloc_consistent(adapter->pdev, + sizeof(mbox64_t), &raid_dev->una_mbox64_dma); + + if (!raid_dev->una_mbox64) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: out of memory, %s %d\n", __FUNCTION__, + __LINE__)); + return -1; + } + memset(raid_dev->una_mbox64, 0, sizeof(mbox64_t)); + + /* + * Align the mailbox at 16-byte boundary + */ + raid_dev->mbox = &raid_dev->una_mbox64->mbox32; + + raid_dev->mbox = (mbox_t *)((((unsigned long)raid_dev->mbox) + 15) & + (~0UL ^ 0xFUL)); + + raid_dev->mbox64 = (mbox64_t *)(((unsigned long)raid_dev->mbox) - 8); + + align = ((void *)raid_dev->mbox - + ((void *)&raid_dev->una_mbox64->mbox32)); + + raid_dev->mbox_dma = (unsigned long)raid_dev->una_mbox64_dma + 8 + + align; + + // Allocate memory for commands issued internally + adapter->ibuf = pci_alloc_consistent(pdev, MBOX_IBUF_SIZE, + &adapter->ibuf_dma_h); + if (!adapter->ibuf) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid: out of memory, %s %d\n", __FUNCTION__, + __LINE__)); + + goto out_free_common_mbox; + } + memset(adapter->ibuf, 0, MBOX_IBUF_SIZE); + + // Allocate memory for our SCSI Command Blocks and their associated + // memory + + /* + * Allocate memory for the base list of scb. Later allocate memory for + * CCBs and embedded components of each CCB and point the pointers in + * scb to the allocated components + * NOTE: The code to allocate SCB will be duplicated in all the LLD + * since the calling routine does not yet know the number of available + * commands. + */ + adapter->kscb_list = kmalloc(sizeof(scb_t) * MBOX_MAX_SCSI_CMDS, + GFP_KERNEL); + + if (adapter->kscb_list == NULL) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: out of memory, %s %d\n", __FUNCTION__, + __LINE__)); + goto out_free_ibuf; + } + memset(adapter->kscb_list, 0, sizeof(scb_t) * MBOX_MAX_SCSI_CMDS); + + // memory allocation for our command packets + if (megaraid_mbox_setup_dma_pools(adapter) != 0) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: out of memory, %s %d\n", __FUNCTION__, + __LINE__)); + goto out_free_scb_list; + } + + // Adjust the scb pointers and link in the free pool + epthru_pci_blk = raid_dev->epthru_pool; + sg_pci_blk = raid_dev->sg_pool; + mbox_pci_blk = raid_dev->mbox_pool; + + for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) { + scb = adapter->kscb_list + i; + ccb = raid_dev->ccb_list + i; + + ccb->mbox = (mbox_t *)(mbox_pci_blk[i].vaddr + 16); + ccb->raw_mbox = (uint8_t *)ccb->mbox; + ccb->mbox64 = (mbox64_t *)(mbox_pci_blk[i].vaddr + 8); + ccb->mbox_dma_h = (unsigned long)mbox_pci_blk[i].dma_addr + 16; + + // make sure the mailbox is aligned properly + if (ccb->mbox_dma_h & 0x0F) { + con_log(CL_ANN, (KERN_CRIT + "megaraid mbox: not aligned on 16-bytes\n")); + + goto out_teardown_dma_pools; + } + + ccb->epthru = (mraid_epassthru_t *) + epthru_pci_blk[i].vaddr; + ccb->epthru_dma_h = epthru_pci_blk[i].dma_addr; + ccb->pthru = (mraid_passthru_t *)ccb->epthru; + ccb->pthru_dma_h = ccb->epthru_dma_h; + + + ccb->sgl64 = (mbox_sgl64 *)sg_pci_blk[i].vaddr; + ccb->sgl_dma_h = sg_pci_blk[i].dma_addr; + ccb->sgl32 = (mbox_sgl32 *)ccb->sgl64; + + scb->ccb = (caddr_t)ccb; + scb->gp = 0; + + scb->sno = i; // command index + + scb->scp = NULL; + scb->state = SCB_FREE; + scb->dma_direction = PCI_DMA_NONE; + scb->dma_type = MRAID_DMA_NONE; + scb->dev_channel = -1; + scb->dev_target = -1; + + // put scb in the free pool + list_add_tail(&scb->list, &adapter->kscb_pool); + } + + return 0; + +out_teardown_dma_pools: + megaraid_mbox_teardown_dma_pools(adapter); +out_free_scb_list: + kfree(adapter->kscb_list); +out_free_ibuf: + pci_free_consistent(pdev, MBOX_IBUF_SIZE, (void *)adapter->ibuf, + adapter->ibuf_dma_h); +out_free_common_mbox: + pci_free_consistent(adapter->pdev, sizeof(mbox64_t), + (caddr_t)raid_dev->una_mbox64, raid_dev->una_mbox64_dma); + + return -1; +} + + +/** + * megaraid_free_cmd_packets - free memory + * @param adapter : soft state of the raid controller + * + * Release memory resources allocated for commands + */ +static void +megaraid_free_cmd_packets(adapter_t *adapter) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + + megaraid_mbox_teardown_dma_pools(adapter); + + kfree(adapter->kscb_list); + + pci_free_consistent(adapter->pdev, MBOX_IBUF_SIZE, + (void *)adapter->ibuf, adapter->ibuf_dma_h); + + pci_free_consistent(adapter->pdev, sizeof(mbox64_t), + (caddr_t)raid_dev->una_mbox64, raid_dev->una_mbox64_dma); + return; +} + + +/** + * megaraid_mbox_setup_dma_pools - setup dma pool for command packets + * @param adapter : HBA soft state + * + * setup the dma pools for mailbox, passthru and extended passthru structures, + * and scatter-gather lists + */ +static int +megaraid_mbox_setup_dma_pools(adapter_t *adapter) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + struct mraid_pci_blk *epthru_pci_blk; + struct mraid_pci_blk *sg_pci_blk; + struct mraid_pci_blk *mbox_pci_blk; + int i; + + + + // Allocate memory for 16-bytes aligned mailboxes + raid_dev->mbox_pool_handle = pci_pool_create("megaraid mbox pool", + adapter->pdev, + sizeof(mbox64_t) + 16, + 16, 0); + + if (raid_dev->mbox_pool_handle == NULL) { + goto fail_setup_dma_pool; + } + + mbox_pci_blk = raid_dev->mbox_pool; + for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) { + mbox_pci_blk[i].vaddr = pci_pool_alloc( + raid_dev->mbox_pool_handle, + GFP_KERNEL, + &mbox_pci_blk[i].dma_addr); + if (!mbox_pci_blk[i].vaddr) { + goto fail_setup_dma_pool; + } + } + + /* + * Allocate memory for each embedded passthru strucuture pointer + * Request for a 128 bytes aligned structure for each passthru command + * structure + * Since passthru and extended passthru commands are exclusive, they + * share common memory pool. Passthru structures piggyback on memory + * allocted to extended passthru since passthru is smaller of the two + */ + raid_dev->epthru_pool_handle = pci_pool_create("megaraid mbox pthru", + adapter->pdev, sizeof(mraid_epassthru_t), 128, 0); + + if (raid_dev->epthru_pool_handle == NULL) { + goto fail_setup_dma_pool; + } + + epthru_pci_blk = raid_dev->epthru_pool; + for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) { + epthru_pci_blk[i].vaddr = pci_pool_alloc( + raid_dev->epthru_pool_handle, + GFP_KERNEL, + &epthru_pci_blk[i].dma_addr); + if (!epthru_pci_blk[i].vaddr) { + goto fail_setup_dma_pool; + } + } + + + // Allocate memory for each scatter-gather list. Request for 512 bytes + // alignment for each sg list + raid_dev->sg_pool_handle = pci_pool_create("megaraid mbox sg", + adapter->pdev, + sizeof(mbox_sgl64) * MBOX_MAX_SG_SIZE, + 512, 0); + + if (raid_dev->sg_pool_handle == NULL) { + goto fail_setup_dma_pool; + } + + sg_pci_blk = raid_dev->sg_pool; + for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) { + sg_pci_blk[i].vaddr = pci_pool_alloc( + raid_dev->sg_pool_handle, + GFP_KERNEL, + &sg_pci_blk[i].dma_addr); + if (!sg_pci_blk[i].vaddr) { + goto fail_setup_dma_pool; + } + } + + return 0; + +fail_setup_dma_pool: + megaraid_mbox_teardown_dma_pools(adapter); + return -1; +} + + +/** + * megaraid_mbox_teardown_dma_pools - teardown dma pools for command packets + * @param adapter : HBA soft state + * + * teardown the dma pool for mailbox, passthru and extended passthru + * structures, and scatter-gather lists + */ +static void +megaraid_mbox_teardown_dma_pools(adapter_t *adapter) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + struct mraid_pci_blk *epthru_pci_blk; + struct mraid_pci_blk *sg_pci_blk; + struct mraid_pci_blk *mbox_pci_blk; + int i; + + + sg_pci_blk = raid_dev->sg_pool; + for (i = 0; i < MBOX_MAX_SCSI_CMDS && sg_pci_blk[i].vaddr; i++) { + pci_pool_free(raid_dev->sg_pool_handle, sg_pci_blk[i].vaddr, + sg_pci_blk[i].dma_addr); + } + if (raid_dev->sg_pool_handle) + pci_pool_destroy(raid_dev->sg_pool_handle); + + + epthru_pci_blk = raid_dev->epthru_pool; + for (i = 0; i < MBOX_MAX_SCSI_CMDS && epthru_pci_blk[i].vaddr; i++) { + pci_pool_free(raid_dev->epthru_pool_handle, + epthru_pci_blk[i].vaddr, epthru_pci_blk[i].dma_addr); + } + if (raid_dev->epthru_pool_handle) + pci_pool_destroy(raid_dev->epthru_pool_handle); + + + mbox_pci_blk = raid_dev->mbox_pool; + for (i = 0; i < MBOX_MAX_SCSI_CMDS && mbox_pci_blk[i].vaddr; i++) { + pci_pool_free(raid_dev->mbox_pool_handle, + mbox_pci_blk[i].vaddr, mbox_pci_blk[i].dma_addr); + } + if (raid_dev->mbox_pool_handle) + pci_pool_destroy(raid_dev->mbox_pool_handle); + + return; +} + + +/** + * megaraid_alloc_scb - detach and return a scb from the free list + * @adapter : controller's soft state + * + * return the scb from the head of the free list. NULL if there are none + * available + **/ +static inline scb_t * +megaraid_alloc_scb(adapter_t *adapter, struct scsi_cmnd *scp) +{ + struct list_head *head = &adapter->kscb_pool; + scb_t *scb = NULL; + unsigned long flags; + + // detach scb from free pool + spin_lock_irqsave(SCSI_FREE_LIST_LOCK(adapter), flags); + + if (list_empty(head)) { + spin_unlock_irqrestore(SCSI_FREE_LIST_LOCK(adapter), flags); + return NULL; + } + + scb = list_entry(head->next, scb_t, list); + list_del_init(&scb->list); + + spin_unlock_irqrestore(SCSI_FREE_LIST_LOCK(adapter), flags); + + scb->state = SCB_ACTIVE; + scb->scp = scp; + scb->dma_type = MRAID_DMA_NONE; + + return scb; +} + + +/** + * megaraid_dealloc_scb - return the scb to the free pool + * @adapter : controller's soft state + * @scb : scb to be freed + * + * return the scb back to the free list of scbs. The caller must 'flush' the + * SCB before calling us. E.g., performing pci_unamp and/or pci_sync etc. + * NOTE NOTE: Make sure the scb is not on any list before calling this + * routine. + **/ +static inline void +megaraid_dealloc_scb(adapter_t *adapter, scb_t *scb) +{ + unsigned long flags; + + // put scb in the free pool + scb->state = SCB_FREE; + scb->scp = NULL; + spin_lock_irqsave(SCSI_FREE_LIST_LOCK(adapter), flags); + + list_add(&scb->list, &adapter->kscb_pool); + + spin_unlock_irqrestore(SCSI_FREE_LIST_LOCK(adapter), flags); + + return; +} + + +/** + * megaraid_mbox_mksgl - make the scatter-gather list + * @adapter - controller's soft state + * @scb - scsi control block + * + * prepare the scatter-gather list + */ +static inline int +megaraid_mbox_mksgl(adapter_t *adapter, scb_t *scb) +{ + struct scatterlist *sgl; + mbox_ccb_t *ccb; + struct page *page; + unsigned long offset; + struct scsi_cmnd *scp; + int sgcnt; + int i; + + + scp = scb->scp; + ccb = (mbox_ccb_t *)scb->ccb; + + // no mapping required if no data to be transferred + if (!scp->request_buffer || !scp->request_bufflen) + return 0; + + if (!scp->use_sg) { /* scatter-gather list not used */ + + page = virt_to_page(scp->request_buffer); + + offset = ((unsigned long)scp->request_buffer & ~PAGE_MASK); + + ccb->buf_dma_h = pci_map_page(adapter->pdev, page, offset, + scp->request_bufflen, + scb->dma_direction); + scb->dma_type = MRAID_DMA_WBUF; + + /* + * We need to handle special 64-bit commands that need a + * minimum of 1 SG + */ + sgcnt = 1; + ccb->sgl64[0].address = ccb->buf_dma_h; + ccb->sgl64[0].length = scp->request_bufflen; + + return sgcnt; + } + + sgl = (struct scatterlist *)scp->request_buffer; + + // The number of sg elements returned must not exceed our limit + sgcnt = pci_map_sg(adapter->pdev, sgl, scp->use_sg, + scb->dma_direction); + + if (sgcnt > adapter->sglen) { + con_log(CL_ANN, (KERN_CRIT + "megaraid critical: too many sg elements:%d\n", + sgcnt)); + BUG(); + } + + scb->dma_type = MRAID_DMA_WSG; + + for (i = 0; i < sgcnt; i++, sgl++) { + ccb->sgl64[i].address = sg_dma_address(sgl); + ccb->sgl64[i].length = sg_dma_len(sgl); + } + + // Return count of SG nodes + return sgcnt; +} + + +/** + * mbox_post_cmd - issue a mailbox command + * @adapter - controller's soft state + * @scb - command to be issued + * + * post the command to the controller if mailbox is availble. + */ +static inline int +mbox_post_cmd(adapter_t *adapter, scb_t *scb) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + mbox64_t *mbox64; + mbox_t *mbox; + mbox_ccb_t *ccb; + unsigned long flags; + unsigned int i = 0; + + + ccb = (mbox_ccb_t *)scb->ccb; + mbox = raid_dev->mbox; + mbox64 = raid_dev->mbox64; + + /* + * Check for busy mailbox. If it is, return failure - the caller + * should retry later. + */ + spin_lock_irqsave(MAILBOX_LOCK(raid_dev), flags); + + if (unlikely(mbox->busy)) { + do { + udelay(1); + i++; + rmb(); + } while(mbox->busy && (i < max_mbox_busy_wait)); + + if (mbox->busy) { + + spin_unlock_irqrestore(MAILBOX_LOCK(raid_dev), flags); + + return -1; + } + } + + + // Copy this command's mailbox data into "adapter's" mailbox + memcpy((caddr_t)mbox64, (caddr_t)ccb->mbox64, 22); + mbox->cmdid = scb->sno; + + adapter->outstanding_cmds++; + + if (scb->dma_direction == PCI_DMA_TODEVICE) { + if (!scb->scp->use_sg) { // sg list not used + pci_dma_sync_single(adapter->pdev, ccb->buf_dma_h, + scb->scp->request_bufflen, + PCI_DMA_TODEVICE); + } + else { + pci_dma_sync_sg(adapter->pdev, scb->scp->request_buffer, + scb->scp->use_sg, PCI_DMA_TODEVICE); + } + } + + mbox->busy = 1; // Set busy + mbox->poll = 0; + mbox->ack = 0; + wmb(); + + WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x1); + + spin_unlock_irqrestore(MAILBOX_LOCK(raid_dev), flags); + + return 0; +} + + +/** + * megaraid_queue_command - generic queue entry point for all LLDs + * @scp : pointer to the scsi command to be executed + * @done : callback routine to be called after the cmd has be completed + * + * Queue entry point for mailbox based controllers. + */ +static int +megaraid_queue_command(struct scsi_cmnd *scp, void (* done)(struct scsi_cmnd *)) +{ + adapter_t *adapter; + scb_t *scb; + int if_busy; + + adapter = SCP2ADAPTER(scp); + scp->scsi_done = done; + scp->result = 0; + + ASSERT(spin_is_locked(adapter->host_lock)); + + spin_unlock(adapter->host_lock); + + /* + * Allocate and build a SCB request + * if_busy flag will be set if megaraid_mbox_build_cmd() command could + * not allocate scb. We will return non-zero status in that case. + * NOTE: scb can be null even though certain commands completed + * successfully, e.g., MODE_SENSE and TEST_UNIT_READY, it would + * return 0 in that case, and we would do the callback right away. + */ + if_busy = 0; + scb = megaraid_mbox_build_cmd(adapter, scp, &if_busy); + + if (scb) { + megaraid_mbox_runpendq(adapter, scb); + } + + spin_lock(adapter->host_lock); + + if (!scb) { // command already completed + done(scp); + } + + return if_busy; +} + + +/** + * megaraid_mbox_build_cmd - transform the mid-layer scsi command to megaraid + * firmware lingua + * @adapter - controller's soft state + * @scp - mid-layer scsi command pointer + * @busy - set if request could not be completed because of lack of + * resources + * + * convert the command issued by mid-layer to format understood by megaraid + * firmware. We also complete certain command without sending them to firmware + */ +static scb_t * +megaraid_mbox_build_cmd(adapter_t *adapter, struct scsi_cmnd *scp, int *busy) +{ + mraid_device_t *rdev = ADAP2RAIDDEV(adapter); + int channel; + int target; + int islogical; + mbox_ccb_t *ccb; + mraid_passthru_t *pthru; + mbox64_t *mbox64; + mbox_t *mbox; + scb_t *scb; + char skip[] = "skipping"; + char scan[] = "scanning"; + char *ss; + + + /* + * Get the appropriate device map for the device this command is + * intended for + */ + MRAID_GET_DEVICE_MAP(adapter, scp, channel, target, islogical); + + /* + * Logical drive commands + */ + if (islogical) { + switch (scp->cmnd[0]) { + case TEST_UNIT_READY: + /* + * Do we support clustering and is the support enabled + * If no, return success always + */ + if (!adapter->ha) { + scp->result = (DID_OK << 16); + return NULL; + } + + if (!(scb = megaraid_alloc_scb(adapter, scp))) { + scp->result = (DID_ERROR << 16); + *busy = 1; + return NULL; + } + + scb->dma_direction = scp->sc_data_direction; + scb->dev_channel = 0xFF; + scb->dev_target = target; + ccb = (mbox_ccb_t *)scb->ccb; + + /* + * The command id will be provided by the command + * issuance routine + */ + ccb->raw_mbox[0] = CLUSTER_CMD; + ccb->raw_mbox[2] = RESERVATION_STATUS; + ccb->raw_mbox[3] = target; + + return scb; + + case MODE_SENSE: + if (scp->use_sg) { + struct scatterlist *sgl; + caddr_t vaddr; + + sgl = (struct scatterlist *)scp->request_buffer; + if (sgl->page) { + vaddr = (caddr_t) + (page_address((&sgl[0])->page) + + (&sgl[0])->offset); + + memset(vaddr, 0, scp->cmnd[4]); + } + else { + con_log(CL_ANN, (KERN_WARNING + "megaraid mailbox: invalid sg:%d\n", + __LINE__)); + } + } + else { + memset(scp->request_buffer, 0, scp->cmnd[4]); + } + scp->result = (DID_OK << 16); + return NULL; + + case INQUIRY: + /* + * Display the channel scan for logical drives + * Do not display scan for a channel if already done. + */ + if (!(rdev->last_disp & (1L << SCP2CHANNEL(scp)))) { + + con_log(CL_ANN, (KERN_INFO + "scsi[%d]: scanning scsi channel %d", + adapter->host->host_no, + SCP2CHANNEL(scp))); + + con_log(CL_ANN, ( + " [virtual] for logical drives\n")); + + rdev->last_disp |= (1L << SCP2CHANNEL(scp)); + } + + /* Fall through */ + + case READ_CAPACITY: + /* + * Do not allow LUN > 0 for logical drives and + * requests for more than 40 logical drives + */ + if (SCP2LUN(scp)) { + scp->result = (DID_BAD_TARGET << 16); + return NULL; + } + if ((target % 0x80) >= MAX_LOGICAL_DRIVES_40LD) { + scp->result = (DID_BAD_TARGET << 16); + return NULL; + } + + + /* Allocate a SCB and initialize passthru */ + if (!(scb = megaraid_alloc_scb(adapter, scp))) { + scp->result = (DID_ERROR << 16); + *busy = 1; + return NULL; + } + + ccb = (mbox_ccb_t *)scb->ccb; + scb->dev_channel = 0xFF; + scb->dev_target = target; + pthru = ccb->pthru; + mbox = ccb->mbox; + mbox64 = ccb->mbox64; + + pthru->timeout = 0; + pthru->ars = 1; + pthru->reqsenselen = 14; + pthru->islogical = 1; + pthru->logdrv = target; + pthru->cdblen = scp->cmd_len; + memcpy(pthru->cdb, scp->cmnd, scp->cmd_len); + + mbox->cmd = MBOXCMD_PASSTHRU64; + scb->dma_direction = scp->sc_data_direction; + + pthru->dataxferlen = scp->request_bufflen; + pthru->dataxferaddr = ccb->sgl_dma_h; + pthru->numsge = megaraid_mbox_mksgl(adapter, + scb); + + mbox->xferaddr = 0xFFFFFFFF; + mbox64->xferaddr_lo = (uint32_t )ccb->pthru_dma_h; + mbox64->xferaddr_hi = 0; + + return scb; + + case READ_6: + case WRITE_6: + case READ_10: + case WRITE_10: + case READ_12: + case WRITE_12: + + /* + * Allocate a SCB and initialize mailbox + */ + if (!(scb = megaraid_alloc_scb(adapter, scp))) { + scp->result = (DID_ERROR << 16); + *busy = 1; + return NULL; + } + ccb = (mbox_ccb_t *)scb->ccb; + scb->dev_channel = 0xFF; + scb->dev_target = target; + mbox = ccb->mbox; + mbox64 = ccb->mbox64; + mbox->logdrv = target; + + /* + * A little HACK: 2nd bit is zero for all scsi read + * commands and is set for all scsi write commands + */ + mbox->cmd = (scp->cmnd[0] & 0x02) ? MBOXCMD_LWRITE64: + MBOXCMD_LREAD64 ; + + /* + * 6-byte READ(0x08) or WRITE(0x0A) cdb + */ + if (scp->cmd_len == 6) { + mbox->numsectors = (uint32_t)scp->cmnd[4]; + mbox->lba = + ((uint32_t)scp->cmnd[1] << 16) | + ((uint32_t)scp->cmnd[2] << 8) | + (uint32_t)scp->cmnd[3]; + + mbox->lba &= 0x1FFFFF; + } + + /* + * 10-byte READ(0x28) or WRITE(0x2A) cdb + */ + else if (scp->cmd_len == 10) { + mbox->numsectors = + (uint32_t)scp->cmnd[8] | + ((uint32_t)scp->cmnd[7] << 8); + mbox->lba = + ((uint32_t)scp->cmnd[2] << 24) | + ((uint32_t)scp->cmnd[3] << 16) | + ((uint32_t)scp->cmnd[4] << 8) | + (uint32_t)scp->cmnd[5]; + } + + /* + * 12-byte READ(0xA8) or WRITE(0xAA) cdb + */ + else if (scp->cmd_len == 12) { + mbox->lba = + ((uint32_t)scp->cmnd[2] << 24) | + ((uint32_t)scp->cmnd[3] << 16) | + ((uint32_t)scp->cmnd[4] << 8) | + (uint32_t)scp->cmnd[5]; + + mbox->numsectors = + ((uint32_t)scp->cmnd[6] << 24) | + ((uint32_t)scp->cmnd[7] << 16) | + ((uint32_t)scp->cmnd[8] << 8) | + (uint32_t)scp->cmnd[9]; + } + else { + con_log(CL_ANN, (KERN_WARNING + "megaraid: unsupported CDB length\n")); + + megaraid_dealloc_scb(adapter, scb); + + scp->result = (DID_ERROR << 16); + return NULL; + } + + scb->dma_direction = scp->sc_data_direction; + + // Calculate Scatter-Gather info + mbox64->xferaddr_lo = (uint32_t )ccb->sgl_dma_h; + mbox->numsge = megaraid_mbox_mksgl(adapter, + scb); + mbox->xferaddr = 0xFFFFFFFF; + mbox64->xferaddr_hi = 0; + + return scb; + + case RESERVE: + case RELEASE: + /* + * Do we support clustering and is the support enabled + */ + if (!adapter->ha) { + scp->result = (DID_BAD_TARGET << 16); + return NULL; + } + + /* + * Allocate a SCB and initialize mailbox + */ + if (!(scb = megaraid_alloc_scb(adapter, scp))) { + scp->result = (DID_ERROR << 16); + *busy = 1; + return NULL; + } + + ccb = (mbox_ccb_t *)scb->ccb; + scb->dev_channel = 0xFF; + scb->dev_target = target; + ccb->raw_mbox[0] = CLUSTER_CMD; + ccb->raw_mbox[2] = (scp->cmnd[0] == RESERVE) ? + RESERVE_LD : RELEASE_LD; + + ccb->raw_mbox[3] = target; + scb->dma_direction = scp->sc_data_direction; + + return scb; + + default: + scp->result = (DID_BAD_TARGET << 16); + return NULL; + } + } + else { // Passthru device commands + + // Do not allow access to target id > 15 or LUN > 7 + if (target > 15 || SCP2LUN(scp) > 7) { + scp->result = (DID_BAD_TARGET << 16); + return NULL; + } + + // if fast load option was set and scan for last device is + // over, reset the fast_load flag so that during a possible + // next scan, devices can be made available + if (rdev->fast_load && (target == 15) && + (SCP2CHANNEL(scp) == adapter->max_channel -1)) { + + con_log(CL_ANN, (KERN_INFO + "megaraid[%d]: physical device scan re-enabled\n", + adapter->host->host_no)); + rdev->fast_load = 0; + } + + /* + * Display the channel scan for physical devices + */ + if (!(rdev->last_disp & (1L << SCP2CHANNEL(scp)))) { + + ss = rdev->fast_load ? skip : scan; + + con_log(CL_ANN, (KERN_INFO + "scsi[%d]: %s scsi channel %d [Phy %d]", + adapter->host->host_no, ss, SCP2CHANNEL(scp), + channel)); + + con_log(CL_ANN, ( + " for non-raid devices\n")); + + rdev->last_disp |= (1L << SCP2CHANNEL(scp)); + } + + // disable channel sweep if fast load option given + if (rdev->fast_load) { + scp->result = (DID_BAD_TARGET << 16); + return NULL; + } + + // Allocate a SCB and initialize passthru + if (!(scb = megaraid_alloc_scb(adapter, scp))) { + scp->result = (DID_ERROR << 16); + *busy = 1; + return NULL; + } + + ccb = (mbox_ccb_t *)scb->ccb; + scb->dev_channel = channel; + scb->dev_target = target; + scb->dma_direction = scp->sc_data_direction; + mbox = ccb->mbox; + mbox64 = ccb->mbox64; + + // Does this firmware support extended CDBs + if (adapter->max_cdb_sz == 16) { + mbox->cmd = MBOXCMD_EXTPTHRU; + + megaraid_mbox_prepare_epthru(adapter, scb, scp); + + mbox64->xferaddr_lo = (uint32_t)ccb->epthru_dma_h; + mbox64->xferaddr_hi = 0; + mbox->xferaddr = 0xFFFFFFFF; + } + else { + mbox->cmd = MBOXCMD_PASSTHRU64; + + megaraid_mbox_prepare_pthru(adapter, scb, scp); + + mbox64->xferaddr_lo = (uint32_t)ccb->pthru_dma_h; + mbox64->xferaddr_hi = 0; + mbox->xferaddr = 0xFFFFFFFF; + } + return scb; + } + + // NOT REACHED +} + + +/** + * megaraid_mbox_runpendq - execute commands queued in the pending queue + * @adapter : controller's soft state + * @scb : SCB to be queued in the pending list + * + * scan the pending list for commands which are not yet issued and try to + * post to the controller. The SCB can be a null pointer, which would indicate + * no SCB to be queue, just try to execute the ones in the pending list. + * + * NOTE: We do not actually traverse the pending list. The SCBs are plucked + * out from the head of the pending list. If it is successfully issued, the + * next SCB is at the head now. + */ +static void +megaraid_mbox_runpendq(adapter_t *adapter, scb_t *scb_q) +{ + scb_t *scb; + unsigned long flags; + + spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags); + + if (scb_q) { + scb_q->state = SCB_PENDQ; + list_add_tail(&scb_q->list, &adapter->pend_list); + } + + // if the adapter in not in quiescent mode, post the commands to FW + if (adapter->quiescent) { + spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags); + return; + } + + while (!list_empty(&adapter->pend_list)) { + + ASSERT(spin_is_locked(PENDING_LIST_LOCK(adapter))); + + scb = list_entry(adapter->pend_list.next, scb_t, list); + + // remove the scb from the pending list and try to + // issue. If we are unable to issue it, put back in + // the pending list and return + + list_del_init(&scb->list); + + spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags); + + // if mailbox was busy, return SCB back to pending + // list. Make sure to add at the head, since that's + // where it would have been removed from + + scb->state = SCB_ISSUED; + + if (mbox_post_cmd(adapter, scb) != 0) { + + spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags); + + scb->state = SCB_PENDQ; + + list_add(&scb->list, &adapter->pend_list); + + spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), + flags); + + return; + } + + spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags); + } + + spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags); + + + return; +} + + +/** + * megaraid_mbox_prepare_pthru - prepare a command for physical devices + * @adapter - pointer to controller's soft state + * @scb - scsi control block + * @scp - scsi command from the mid-layer + * + * prepare a command for the scsi physical devices + */ +static void +megaraid_mbox_prepare_pthru(adapter_t *adapter, scb_t *scb, + struct scsi_cmnd *scp) +{ + mbox_ccb_t *ccb; + mraid_passthru_t *pthru; + uint8_t channel; + uint8_t target; + + ccb = (mbox_ccb_t *)scb->ccb; + pthru = ccb->pthru; + channel = scb->dev_channel; + target = scb->dev_target; + + pthru->timeout = 1; // 0=6sec, 1=60sec, 2=10min, 3=3hrs + pthru->ars = 1; + pthru->islogical = 0; + pthru->channel = 0; + pthru->target = (channel << 4) | target; + pthru->logdrv = SCP2LUN(scp); + pthru->reqsenselen = 14; + pthru->cdblen = scp->cmd_len; + + memcpy(pthru->cdb, scp->cmnd, scp->cmd_len); + + if (scp->request_bufflen) { + pthru->dataxferlen = scp->request_bufflen; + pthru->dataxferaddr = ccb->sgl_dma_h; + pthru->numsge = megaraid_mbox_mksgl(adapter, scb); + } + else { + pthru->dataxferaddr = 0; + pthru->dataxferlen = 0; + pthru->numsge = 0; + } + return; +} + + +/** + * megaraid_mbox_prepare_epthru - prepare a command for physical devices + * @adapter - pointer to controller's soft state + * @scb - scsi control block + * @scp - scsi command from the mid-layer + * + * prepare a command for the scsi physical devices. This rountine prepares + * commands for devices which can take extended CDBs (>10 bytes) + */ +static void +megaraid_mbox_prepare_epthru(adapter_t *adapter, scb_t *scb, + struct scsi_cmnd *scp) +{ + mbox_ccb_t *ccb; + mraid_epassthru_t *epthru; + uint8_t channel; + uint8_t target; + + ccb = (mbox_ccb_t *)scb->ccb; + epthru = ccb->epthru; + channel = scb->dev_channel; + target = scb->dev_target; + + epthru->timeout = 1; // 0=6sec, 1=60sec, 2=10min, 3=3hrs + epthru->ars = 1; + epthru->islogical = 0; + epthru->channel = 0; + epthru->target = (channel << 4) | target; + epthru->logdrv = SCP2LUN(scp); + epthru->reqsenselen = 14; + epthru->cdblen = scp->cmd_len; + + memcpy(epthru->cdb, scp->cmnd, scp->cmd_len); + + if (scp->request_bufflen) { + epthru->dataxferlen = scp->request_bufflen; + epthru->dataxferaddr = ccb->sgl_dma_h; + epthru->numsge = megaraid_mbox_mksgl(adapter, scb); + } + else { + epthru->dataxferaddr = 0; + epthru->dataxferlen = 0; + epthru->numsge = 0; + } + return; +} + + +/** + * megaraid_ack_sequence - interrupt ack sequence for memory mapped HBAs + * @adapter - controller's soft state + * + * Interrupt ackrowledgement sequence for memory mapped HBAs. Find out the + * completed command and put them on the completed list for later processing. + * + * Returns: 1 if the interrupt is valid, 0 otherwise + */ +static inline int +megaraid_ack_sequence(adapter_t *adapter) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + mbox_t *mbox; + scb_t *scb; + uint8_t nstatus; + uint8_t completed[MBOX_MAX_FIRMWARE_STATUS]; + struct list_head clist; + int handled; + uint32_t dword; + unsigned long flags; + int i, j; + + + mbox = raid_dev->mbox; + + // move the SCBs from the firmware completed array to our local list + INIT_LIST_HEAD(&clist); + + // loop till F/W has more commands for us to complete + handled = 0; + spin_lock_irqsave(MAILBOX_LOCK(raid_dev), flags); + do { + /* + * Check if a valid interrupt is pending. If found, force the + * interrupt line low. + */ + dword = RDOUTDOOR(raid_dev); + if (dword != 0x10001234) break; + + handled = 1; + + WROUTDOOR(raid_dev, 0x10001234); + + nstatus = 0; + // wait for valid numstatus to post + for (i = 0; i < 0xFFFFF; i++) { + if (mbox->numstatus != 0xFF) { + nstatus = mbox->numstatus; + break; + } + rmb(); + } + mbox->numstatus = 0xFF; + + adapter->outstanding_cmds -= nstatus; + + for (i = 0; i < nstatus; i++) { + + // wait for valid command index to post + for (j = 0; j < 0xFFFFF; j++) { + if (mbox->completed[i] != 0xFF) break; + rmb(); + } + completed[i] = mbox->completed[i]; + mbox->completed[i] = 0xFF; + + if (completed[i] == 0xFF) { + con_log(CL_ANN, (KERN_CRIT + "megaraid: command posting timed out\n")); + + BUG(); + continue; + } + + // Get SCB associated with this command id + if (completed[i] >= MBOX_MAX_SCSI_CMDS) { + // a cmm command + scb = adapter->uscb_list + (completed[i] - + MBOX_MAX_SCSI_CMDS); + } + else { + // an os command + scb = adapter->kscb_list + completed[i]; + } + + scb->status = mbox->status; + list_add_tail(&scb->list, &clist); + } + + // Acknowledge interrupt + WRINDOOR(raid_dev, 0x02); + + } while(1); + + spin_unlock_irqrestore(MAILBOX_LOCK(raid_dev), flags); + + + // put the completed commands in the completed list. DPC would + // complete these commands later + spin_lock_irqsave(COMPLETED_LIST_LOCK(adapter), flags); + + list_splice(&clist, &adapter->completed_list); + + spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter), flags); + + + // schedule the DPC if there is some work for it + if (handled) + tasklet_schedule(&adapter->dpc_h); + + return handled; +} + + +/** + * megaraid_isr - isr for memory based mailbox based controllers + * @irq - irq + * @devp - pointer to our soft state + * @regs - unused + * + * Interrupt service routine for memory-mapped mailbox controllers. + */ +static irqreturn_t +megaraid_isr(int irq, void *devp, struct pt_regs *regs) +{ + adapter_t *adapter = devp; + int handled; + + handled = megaraid_ack_sequence(adapter); + + /* Loop through any pending requests */ + if (!adapter->quiescent) { + megaraid_mbox_runpendq(adapter, NULL); + } + + return IRQ_RETVAL(handled); +} + + +/** + * megaraid_mbox_sync_scb - sync kernel buffers + * @adapter : controller's soft state + * @scb : pointer to the resource packet + * + * DMA sync if required. + */ +static inline void +megaraid_mbox_sync_scb(adapter_t *adapter, scb_t *scb) +{ + mbox_ccb_t *ccb; + + ccb = (mbox_ccb_t *)scb->ccb; + + switch (scb->dma_type) { + + case MRAID_DMA_WBUF: + if (scb->dma_direction == PCI_DMA_FROMDEVICE) { + pci_dma_sync_single(adapter->pdev, + ccb->buf_dma_h, + scb->scp->request_bufflen, + PCI_DMA_FROMDEVICE); + } + + pci_unmap_page(adapter->pdev, ccb->buf_dma_h, + scb->scp->request_bufflen, scb->dma_direction); + + break; + + case MRAID_DMA_WSG: + if (scb->dma_direction == PCI_DMA_FROMDEVICE) { + pci_dma_sync_sg(adapter->pdev, + scb->scp->request_buffer, + scb->scp->use_sg, PCI_DMA_FROMDEVICE); + } + + pci_unmap_sg(adapter->pdev, scb->scp->request_buffer, + scb->scp->use_sg, scb->dma_direction); + + break; + + default: + break; + } + + return; +} + + +/** + * megaraid_mbox_dpc - the tasklet to complete the commands from completed list + * @devp : pointer to HBA soft state + * + * Pick up the commands from the completed list and send back to the owners. + * This is a reentrant function and does not assume any locks are held while + * it is being called. + */ +static void +megaraid_mbox_dpc(unsigned long devp) +{ + adapter_t *adapter = (adapter_t *)devp; + mraid_device_t *raid_dev; + struct list_head clist; + struct scatterlist *sgl; + scb_t *scb; + scb_t *tmp; + struct scsi_cmnd *scp; + mraid_passthru_t *pthru; + mraid_epassthru_t *epthru; + mbox_ccb_t *ccb; + int islogical; + int pdev_index; + int pdev_state; + mbox_t *mbox; + unsigned long flags; + uint8_t c; + int status; + + + if (!adapter) return; + + raid_dev = ADAP2RAIDDEV(adapter); + + // move the SCBs from the completed list to our local list + INIT_LIST_HEAD(&clist); + + spin_lock_irqsave(COMPLETED_LIST_LOCK(adapter), flags); + + list_splice_init(&adapter->completed_list, &clist); + + spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter), flags); + + + list_for_each_entry_safe(scb, tmp, &clist, list) { + + status = scb->status; + scp = scb->scp; + ccb = (mbox_ccb_t *)scb->ccb; + pthru = ccb->pthru; + epthru = ccb->epthru; + mbox = ccb->mbox; + + // Make sure f/w has completed a valid command + if (scb->state != SCB_ISSUED) { + con_log(CL_ANN, (KERN_CRIT + "megaraid critical err: invalid command %d:%d:%p\n", + scb->sno, scb->state, scp)); + BUG(); + continue; // Must never happen! + } + + // check for the management command and complete it right away + if (scb->sno >= MBOX_MAX_SCSI_CMDS) { + scb->state = SCB_FREE; + scb->status = status; + + // remove from local clist + list_del_init(&scb->list); + + megaraid_mbox_mm_done(adapter, scb); + + continue; + } + + // Was an abort issued for this command earlier + if (scb->state & SCB_ABORT) { + con_log(CL_ANN, (KERN_NOTICE + "megaraid: aborted cmd %lx[%x] completed\n", + scp->serial_number, scb->sno)); + } + + /* + * If the inquiry came of a disk drive which is not part of + * any RAID array, expose it to the kernel. For this to be + * enabled, user must set the "megaraid_expose_unconf_disks" + * flag to 1 by specifying it on module parameter list. + * This would enable data migration off drives from other + * configurations. + */ + islogical = MRAID_IS_LOGICAL(adapter, scp); + if (scp->cmnd[0] == INQUIRY && status == 0 && islogical == 0 + && IS_RAID_CH(raid_dev, scb->dev_channel)) { + + if (scp->use_sg) { + sgl = (struct scatterlist *) + scp->request_buffer; + + if (sgl->page) { + c = *(unsigned char *) + (page_address((&sgl[0])->page) + + (&sgl[0])->offset); + } + else { + con_log(CL_ANN, (KERN_WARNING + "megaraid mailbox: invalid sg:%d\n", + __LINE__)); + c = 0; + } + } + else { + c = *(uint8_t *)scp->request_buffer; + } + + if ((c & 0x1F ) == TYPE_DISK) { + pdev_index = (scb->dev_channel * 16) + + scb->dev_target; + pdev_state = + raid_dev->pdrv_state[pdev_index] & 0x0F; + + if (pdev_state == PDRV_ONLINE || + pdev_state == PDRV_FAILED || + pdev_state == PDRV_RBLD || + pdev_state == PDRV_HOTSPARE || + megaraid_expose_unconf_disks == 0) { + + status = 0xF0; + } + } + } + + // Convert MegaRAID status to Linux error code + switch (status) { + + case 0x00: + + scp->result = (DID_OK << 16); + break; + + case 0x02: + + /* set sense_buffer and result fields */ + if (mbox->cmd == MBOXCMD_PASSTHRU || + mbox->cmd == MBOXCMD_PASSTHRU64) { + + memcpy(scp->sense_buffer, pthru->reqsensearea, + 14); + + scp->result = DRIVER_SENSE << 24 | + DID_OK << 16 | CHECK_CONDITION << 1; + } + else { + if (mbox->cmd == MBOXCMD_EXTPTHRU) { + + memcpy(scp->sense_buffer, + epthru->reqsensearea, 14); + + scp->result = DRIVER_SENSE << 24 | + DID_OK << 16 | + CHECK_CONDITION << 1; + } else { + scp->sense_buffer[0] = 0x70; + scp->sense_buffer[2] = ABORTED_COMMAND; + scp->result = CHECK_CONDITION << 1; + } + } + break; + + case 0x08: + + scp->result = DID_BUS_BUSY << 16 | status; + break; + + default: + + /* + * If TEST_UNIT_READY fails, we know RESERVATION_STATUS + * failed + */ + if (scp->cmnd[0] == TEST_UNIT_READY) { + scp->result = DID_ERROR << 16 | + RESERVATION_CONFLICT << 1; + } + else + /* + * Error code returned is 1 if Reserve or Release + * failed or the input parameter is invalid + */ + if (status == 1 && (scp->cmnd[0] == RESERVE || + scp->cmnd[0] == RELEASE)) { + + scp->result = DID_ERROR << 16 | + RESERVATION_CONFLICT << 1; + } + else { + scp->result = DID_BAD_TARGET << 16 | status; + } + } + + // print a debug message for all failed commands + if (status) { + megaraid_mbox_display_scb(adapter, scb); + } + + // Free our internal resources and call the mid-layer callback + // routine + megaraid_mbox_sync_scb(adapter, scb); + + // remove from local clist + list_del_init(&scb->list); + + // put back in free list + megaraid_dealloc_scb(adapter, scb); + + // send the scsi packet back to kernel + spin_lock(adapter->host_lock); + scp->scsi_done(scp); + spin_unlock(adapter->host_lock); + } + + return; +} + + +/** + * megaraid_abort_handler - abort the scsi command + * @scp : command to be aborted + * + * Abort a previous SCSI request. Only commands on the pending list can be + * aborted. All the commands issued to the F/W must complete. + **/ +static int +megaraid_abort_handler(struct scsi_cmnd *scp) +{ + adapter_t *adapter; + mraid_device_t *raid_dev; + scb_t *scb; + scb_t *tmp; + int found; + unsigned long flags; + int i; + + + adapter = SCP2ADAPTER(scp); + raid_dev = ADAP2RAIDDEV(adapter); + + ASSERT(spin_is_locked(adapter->host_lock)); + + con_log(CL_ANN, (KERN_WARNING + "megaraid: aborting-%ld cmd=%x \n", + scp->serial_number, scp->cmnd[0], SCP2CHANNEL(scp), + SCP2TARGET(scp), SCP2LUN(scp))); + + // If FW has stopped responding, simply return failure + if (raid_dev->hw_error) { + con_log(CL_ANN, (KERN_NOTICE + "megaraid: hw error, not aborting\n")); + return FAILED; + } + + // There might a race here, where the command was completed by the + // firmware and now it is on the completed list. Before we could + // complete the command to the kernel in dpc, the abort came. + // Find out if this is the case to avoid the race. + scb = NULL; + spin_lock_irqsave(COMPLETED_LIST_LOCK(adapter), flags); + list_for_each_entry_safe(scb, tmp, &adapter->completed_list, list) { + + if (scb->scp == scp) { // Found command + + list_del_init(&scb->list); // from completed list + + con_log(CL_ANN, (KERN_WARNING + "megaraid: %ld:%d[%d:%d], abort from completed list\n", + scp->serial_number, scb->sno, + scb->dev_channel, scb->dev_target)); + + scp->result = (DID_ABORT << 16); + scp->scsi_done(scp); + + megaraid_dealloc_scb(adapter, scb); + + spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter), + flags); + + return SUCCESS; + } + } + spin_unlock_irqrestore(COMPLETED_LIST_LOCK(adapter), flags); + + + // Find out if this command is still on the pending list. If it is and + // was never issued, abort and return success. If the command is owned + // by the firmware, we must wait for it to complete by the FW. + spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags); + list_for_each_entry_safe(scb, tmp, &adapter->pend_list, list) { + + if (scb->scp == scp) { // Found command + + list_del_init(&scb->list); // from pending list + + ASSERT(!(scb->state & SCB_ISSUED)); + + con_log(CL_ANN, (KERN_WARNING + "megaraid abort: %ld[%d:%d], driver owner\n", + scp->serial_number, scb->dev_channel, + scb->dev_target)); + + scp->result = (DID_ABORT << 16); + scp->scsi_done(scp); + + megaraid_dealloc_scb(adapter, scb); + + spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), + flags); + + return SUCCESS; + } + } + spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags); + + + // Check do we even own this command, in which case this would be + // owned by the firmware. The only way to locate the FW scb is to + // traverse through the list of all SCB, since driver does not + // maintain these SCBs on any list + found = 0; + for (i = 0; i < MBOX_MAX_SCSI_CMDS; i++) { + scb = adapter->kscb_list + i; + + if (scb->scp == scp) { + + found = 1; + + if (!(scb->state & SCB_ISSUED)) { + con_log(CL_ANN, (KERN_WARNING + "megaraid abort: %ld%d[%d:%d], invalid state\n", + scp->serial_number, scb->sno, scb->dev_channel, + scb->dev_target)); + BUG(); + } + else { + con_log(CL_ANN, (KERN_WARNING + "megaraid abort: %ld:%d[%d:%d], fw owner\n", + scp->serial_number, scb->sno, scb->dev_channel, + scb->dev_target)); + } + } + } + + if (!found) { + con_log(CL_ANN, (KERN_WARNING + "megaraid abort: scsi cmd:%ld, do now own\n", + scp->serial_number)); + + // FIXME: Should there be a callback for this command? + return SUCCESS; + } + + // We cannot actually abort a command owned by firmware, return + // failure and wait for reset. In host reset handler, we will find out + // if the HBA is still live + return FAILED; +} + + +/** + * megaraid_reset_handler - device reset hadler for mailbox based driver + * @scp : reference command + * + * Reset handler for the mailbox based controller. First try to find out if + * the FW is still live, in which case the outstanding commands counter mut go + * down to 0. If that happens, also issue the reservation reset command to + * relinquish (possible) reservations on the logical drives connected to this + * host + **/ +static int +megaraid_reset_handler(struct scsi_cmnd *scp) +{ + adapter_t *adapter; + scb_t *scb; + scb_t *tmp; + mraid_device_t *raid_dev; + unsigned long flags; + uint8_t raw_mbox[sizeof(mbox_t)]; + int rval; + int recovery_window; + int recovering; + int i; + + adapter = SCP2ADAPTER(scp); + raid_dev = ADAP2RAIDDEV(adapter); + + ASSERT(spin_is_locked(adapter->host_lock)); + + con_log(CL_ANN, (KERN_WARNING "megaraid: reseting the host...\n")); + + // return failure if adapter is not responding + if (raid_dev->hw_error) { + con_log(CL_ANN, (KERN_NOTICE + "megaraid: hw error, cannot reset\n")); + return FAILED; + } + + + // Under exceptional conditions, FW can take up to 3 minutes to + // complete command processing. Wait for additional 2 minutes for the + // pending commands counter to go down to 0. If it doesn't, let the + // controller be marked offline + // Also, reset all the commands currently owned by the driver + spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags); + list_for_each_entry_safe(scb, tmp, &adapter->pend_list, list) { + + list_del_init(&scb->list); // from pending list + + con_log(CL_ANN, (KERN_WARNING + "megaraid: %ld:%d[%d:%d], reset from pending list\n", + scp->serial_number, scb->sno, + scb->dev_channel, scb->dev_target)); + + scp->result = (DID_RESET << 16); + scp->scsi_done(scp); + + megaraid_dealloc_scb(adapter, scb); + } + spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags); + + if (adapter->outstanding_cmds) { + con_log(CL_ANN, (KERN_NOTICE + "megaraid: %d outstanding commands. Max wait %d sec\n", + adapter->outstanding_cmds, MBOX_RESET_WAIT)); + } + + spin_unlock(adapter->host_lock); + + recovery_window = MBOX_RESET_WAIT + MBOX_RESET_EXT_WAIT; + + recovering = adapter->outstanding_cmds; + + for (i = 0; i < recovery_window && adapter->outstanding_cmds; i++) { + + megaraid_ack_sequence(adapter); + + // print a message once every 5 seconds only + if (!(i % 5)) { + con_log(CL_ANN, ( + "megaraid mbox: Wait for %d commands to complete:%d\n", + adapter->outstanding_cmds, + MBOX_RESET_WAIT - i)); + } + + // bailout if no recovery happended in reset time + if ((i == MBOX_RESET_WAIT) && + (recovering == adapter->outstanding_cmds)) { + break; + } + + msleep(1000); + } + + spin_lock(adapter->host_lock); + + // If still outstanding commands, bail out + if (adapter->outstanding_cmds) { + con_log(CL_ANN, (KERN_WARNING + "megaraid mbox: critical hardware error!\n")); + + raid_dev->hw_error = 1; + + return FAILED; + } + else { + con_log(CL_ANN, (KERN_NOTICE + "megaraid mbox: reset sequence completed sucessfully\n")); + } + + + // If the controller supports clustering, reset reservations + if (!adapter->ha) return SUCCESS; + + // clear reservations if any + raw_mbox[0] = CLUSTER_CMD; + raw_mbox[2] = RESET_RESERVATIONS; + + rval = SUCCESS; + if (mbox_post_sync_cmd_fast(adapter, raw_mbox) == 0) { + con_log(CL_ANN, + (KERN_INFO "megaraid: reservation reset\n")); + } + else { + rval = FAILED; + con_log(CL_ANN, (KERN_WARNING + "megaraid: reservation reset failed\n")); + } + + return rval; +} + + +/* + * START: internal commands library + * + * This section of the driver has the common routine used by the driver and + * also has all the FW routines + */ + +/** + * mbox_post_sync_cmd() - blocking command to the mailbox based controllers + * @adapter - controller's soft state + * @raw_mbox - the mailbox + * + * Issue a scb in synchronous and non-interrupt mode for mailbox based + * controllers + */ +static int +mbox_post_sync_cmd(adapter_t *adapter, uint8_t raw_mbox[]) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + mbox64_t *mbox64; + mbox_t *mbox; + uint8_t status; + int i; + + + mbox64 = raid_dev->mbox64; + mbox = raid_dev->mbox; + + /* + * Wait until mailbox is free + */ + if (megaraid_busywait_mbox(raid_dev) != 0) + goto blocked_mailbox; + + /* + * Copy mailbox data into host structure + */ + memcpy((caddr_t)mbox, (caddr_t)raw_mbox, 16); + mbox->cmdid = 0xFE; + mbox->busy = 1; + mbox->poll = 0; + mbox->ack = 0; + mbox->numstatus = 0xFF; + mbox->status = 0xFF; + + wmb(); + WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x1); + + // wait for maximum 1 second for status to post. If the status is not + // available within 1 second, assume FW is initializing and wait + // for an extended amount of time + if (mbox->numstatus == 0xFF) { // status not yet available + udelay(25);; + + for (i = 0; mbox->numstatus == 0xFF && i < 1000; i++) { + rmb(); + msleep(1); + } + + + if (i == 1000) { + con_log(CL_ANN, (KERN_NOTICE + "megaraid mailbox: wait for FW to boot ")); + + for (i = 0; (mbox->numstatus == 0xFF) && + (i < MBOX_RESET_WAIT); i++) { + rmb(); + con_log(CL_ANN, ("\b\b\b\b\b[%03d]", + MBOX_RESET_WAIT - i)); + msleep(1000); + } + + if (i == MBOX_RESET_WAIT) { + + con_log(CL_ANN, ( + "\nmegaraid mailbox: status not available\n")); + + return -1; + } + con_log(CL_ANN, ("\b\b\b\b\b[ok] \n")); + } + } + + // wait for maximum 1 second for poll semaphore + if (mbox->poll != 0x77) { + udelay(25); + + for (i = 0; (mbox->poll != 0x77) && (i < 1000); i++) { + rmb(); + msleep(1); + } + + if (i == 1000) { + con_log(CL_ANN, (KERN_WARNING + "megaraid mailbox: could not get poll semaphore\n")); + return -1; + } + } + + WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x2); + wmb(); + + // wait for maximum 1 second for acknowledgement + if (RDINDOOR(raid_dev) & 0x2) { + udelay(25); + + for (i = 0; (RDINDOOR(raid_dev) & 0x2) && (i < 1000); i++) { + rmb(); + msleep(1); + } + + if (i == 1000) { + con_log(CL_ANN, (KERN_WARNING + "megaraid mailbox: could not acknowledge\n")); + return -1; + } + } + mbox->poll = 0; + mbox->ack = 0x77; + + status = mbox->status; + + // invalidate the completed command id array. After command + // completion, firmware would write the valid id. + mbox->numstatus = 0xFF; + mbox->status = 0xFF; + for (i = 0; i < MBOX_MAX_FIRMWARE_STATUS; i++) { + mbox->completed[i] = 0xFF; + } + + return status; + +blocked_mailbox: + + con_log(CL_ANN, (KERN_WARNING "megaraid: blocked mailbox\n") ); + return -1; +} + + +/** + * mbox_post_sync_cmd_fast - blocking command to the mailbox based controllers + * @adapter - controller's soft state + * @raw_mbox - the mailbox + * + * Issue a scb in synchronous and non-interrupt mode for mailbox based + * controllers. This is a faster version of the synchronous command and + * therefore can be called in interrupt-context as well + */ +static int +mbox_post_sync_cmd_fast(adapter_t *adapter, uint8_t raw_mbox[]) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + mbox_t *mbox; + long i; + + + mbox = raid_dev->mbox; + + // return immediately if the mailbox is busy + if (mbox->busy) return -1; + + // Copy mailbox data into host structure + memcpy((caddr_t)mbox, (caddr_t)raw_mbox, 14); + mbox->cmdid = 0xFE; + mbox->busy = 1; + mbox->poll = 0; + mbox->ack = 0; + mbox->numstatus = 0xFF; + mbox->status = 0xFF; + + wmb(); + WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x1); + + for (i = 0; i < 0xFFFFF; i++) { + if (mbox->numstatus != 0xFF) break; + } + + if (i == 0xFFFFF) { + // We may need to re-calibrate the counter + con_log(CL_ANN, (KERN_CRIT + "megaraid: fast sync command timed out\n")); + } + + WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x2); + wmb(); + + return mbox->status; +} + + +/** + * megaraid_busywait_mbox() - Wait until the controller's mailbox is available + * @raid_dev - RAID device (HBA) soft state + * + * wait until the controller's mailbox is available to accept more commands. + * wait for at most 1 second + */ +static int +megaraid_busywait_mbox(mraid_device_t *raid_dev) +{ + mbox_t *mbox = raid_dev->mbox; + int i = 0; + + if (mbox->busy) { + udelay(25); + for (i = 0; mbox->busy && i < 1000; i++) + msleep(1); + } + + if (i < 1000) return 0; + else return -1; +} + + +/** + * megaraid_mbox_product_info - some static information about the controller + * @adapter - our soft state + * + * issue commands to the controller to grab some parameters required by our + * caller. + */ +static int +megaraid_mbox_product_info(adapter_t *adapter) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + mbox_t *mbox; + uint8_t raw_mbox[sizeof(mbox_t)]; + mraid_pinfo_t *pinfo; + dma_addr_t pinfo_dma_h; + mraid_inquiry3_t *mraid_inq3; + int i; + + + memset((caddr_t)raw_mbox, 0, sizeof(raw_mbox)); + mbox = (mbox_t *)raw_mbox; + + /* + * Issue an ENQUIRY3 command to find out certain adapter parameters, + * e.g., max channels, max commands etc. + */ + pinfo = pci_alloc_consistent(adapter->pdev, sizeof(mraid_pinfo_t), + &pinfo_dma_h); + + if (pinfo == NULL) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: out of memory, %s %d\n", __FUNCTION__, + __LINE__)); + + return -1; + } + memset(pinfo, 0, sizeof(mraid_pinfo_t)); + + mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h; + memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE); + + raw_mbox[0] = FC_NEW_CONFIG; + raw_mbox[2] = NC_SUBOP_ENQUIRY3; + raw_mbox[3] = ENQ3_GET_SOLICITED_FULL; + + // Issue the command + if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) { + + con_log(CL_ANN, (KERN_WARNING "megaraid: Inquiry3 failed\n")); + + pci_free_consistent(adapter->pdev, sizeof(mraid_pinfo_t), + pinfo, pinfo_dma_h); + + return -1; + } + + /* + * Collect information about state of each physical drive + * attached to the controller. We will expose all the disks + * which are not part of RAID + */ + mraid_inq3 = (mraid_inquiry3_t *)adapter->ibuf; + for (i = 0; i < MBOX_MAX_PHYSICAL_DRIVES; i++) { + raid_dev->pdrv_state[i] = mraid_inq3->pdrv_state[i]; + } + + /* + * Get product info for information like number of channels, + * maximum commands supported. + */ + memset((caddr_t)raw_mbox, 0, sizeof(raw_mbox)); + mbox->xferaddr = (uint32_t)pinfo_dma_h; + + raw_mbox[0] = FC_NEW_CONFIG; + raw_mbox[2] = NC_SUBOP_PRODUCT_INFO; + + if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid: product info failed\n")); + + pci_free_consistent(adapter->pdev, sizeof(mraid_pinfo_t), + pinfo, pinfo_dma_h); + + return -1; + } + + /* + * Setup some parameters for host, as required by our caller + */ + adapter->max_channel = pinfo->nchannels; + + /* + * we will export all the logical drives on a single channel. + * Add 1 since inquires do not come for inititor ID + */ + adapter->max_target = MAX_LOGICAL_DRIVES_40LD + 1; + adapter->max_lun = 8; // up to 8 LUNs for non-disk devices + + /* + * These are the maximum outstanding commands for the scsi-layer + */ + adapter->max_cmds = MBOX_MAX_SCSI_CMDS; + + memset(adapter->fw_version, 0, VERSION_SIZE); + memset(adapter->bios_version, 0, VERSION_SIZE); + + memcpy(adapter->fw_version, pinfo->fw_version, 4); + adapter->fw_version[4] = 0; + + memcpy(adapter->bios_version, pinfo->bios_version, 4); + adapter->bios_version[4] = 0; + + con_log(CL_ANN, (KERN_NOTICE + "megaraid: fw version:[%s] bios version:[%s]\n", + adapter->fw_version, adapter->bios_version)); + + pci_free_consistent(adapter->pdev, sizeof(mraid_pinfo_t), pinfo, + pinfo_dma_h); + + return 0; +} + + + +/** + * megaraid_mbox_extended_cdb - check for support for extended CDBs + * @adapter - soft state for the controller + * + * this routine check whether the controller in question supports extended + * ( > 10 bytes ) CDBs + */ +static int +megaraid_mbox_extended_cdb(adapter_t *adapter) +{ + mbox_t *mbox; + uint8_t raw_mbox[sizeof(mbox_t)]; + int rval; + + mbox = (mbox_t *)raw_mbox; + + memset((caddr_t)raw_mbox, 0, sizeof(raw_mbox)); + mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h; + + memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE); + + raw_mbox[0] = MAIN_MISC_OPCODE; + raw_mbox[2] = SUPPORT_EXT_CDB; + + /* + * Issue the command + */ + rval = 0; + if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) { + rval = -1; + } + + return rval; +} + + +/** + * megaraid_mbox_support_ha - Do we support clustering + * @adapter - soft state for the controller + * @init_id - ID of the initiator + * + * Determine if the firmware supports clustering and the ID of the initiator. + */ +static int +megaraid_mbox_support_ha(adapter_t *adapter, uint16_t *init_id) +{ + mbox_t *mbox; + uint8_t raw_mbox[sizeof(mbox_t)]; + int rval; + + + mbox = (mbox_t *)raw_mbox; + + memset((caddr_t)raw_mbox, 0, sizeof(raw_mbox)); + + mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h; + + memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE); + + raw_mbox[0] = GET_TARGET_ID; + + // Issue the command + *init_id = 7; + rval = -1; + if (mbox_post_sync_cmd(adapter, raw_mbox) == 0) { + + *init_id = *(uint8_t *)adapter->ibuf; + + con_log(CL_ANN, (KERN_INFO + "megaraid: cluster firmware, initiator ID: %d\n", + *init_id)); + + rval = 0; + } + + return rval; +} + + +/** + * megaraid_mbox_support_random_del - Do we support random deletion + * @adapter - soft state for the controller + * + * Determine if the firmware supports random deletion + * Return: 1 is operation supported, 0 otherwise + */ +static int +megaraid_mbox_support_random_del(adapter_t *adapter) +{ + mbox_t *mbox; + uint8_t raw_mbox[sizeof(mbox_t)]; + int rval; + + + mbox = (mbox_t *)raw_mbox; + + memset((caddr_t)raw_mbox, 0, sizeof(mbox_t)); + + raw_mbox[0] = FC_DEL_LOGDRV; + raw_mbox[0] = OP_SUP_DEL_LOGDRV; + + // Issue the command + rval = 0; + if (mbox_post_sync_cmd(adapter, raw_mbox) == 0) { + + con_log(CL_DLEVEL1, ("megaraid: supports random deletion\n")); + + rval = 1; + } + + return rval; +} + + +/** + * megaraid_mbox_get_max_sg - maximum sg elements supported by the firmware + * @adapter - soft state for the controller + * + * Find out the maximum number of scatter-gather elements supported by the + * firmware + */ +static int +megaraid_mbox_get_max_sg(adapter_t *adapter) +{ + mbox_t *mbox; + uint8_t raw_mbox[sizeof(mbox_t)]; + int nsg; + + + mbox = (mbox_t *)raw_mbox; + + memset((caddr_t)raw_mbox, 0, sizeof(mbox_t)); + + mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h; + + memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE); + + raw_mbox[0] = MAIN_MISC_OPCODE; + raw_mbox[2] = GET_MAX_SG_SUPPORT; + + // Issue the command + if (mbox_post_sync_cmd(adapter, raw_mbox) == 0) { + nsg = *(uint8_t *)adapter->ibuf; + } + else { + nsg = MBOX_DEFAULT_SG_SIZE; + } + + if (nsg > MBOX_MAX_SG_SIZE) nsg = MBOX_MAX_SG_SIZE; + + return nsg; +} + + +/** + * megaraid_mbox_enum_raid_scsi - enumerate the RAID and SCSI channels + * @adapter - soft state for the controller + * + * Enumerate the RAID and SCSI channels for ROMB platoforms so that channels + * can be exported as regular SCSI channels + */ +static void +megaraid_mbox_enum_raid_scsi(adapter_t *adapter) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + mbox_t *mbox; + uint8_t raw_mbox[sizeof(mbox_t)]; + + + mbox = (mbox_t *)raw_mbox; + + memset((caddr_t)raw_mbox, 0, sizeof(mbox_t)); + + mbox->xferaddr = (uint32_t)adapter->ibuf_dma_h; + + memset((void *)adapter->ibuf, 0, MBOX_IBUF_SIZE); + + raw_mbox[0] = CHNL_CLASS; + raw_mbox[2] = GET_CHNL_CLASS; + + // Issue the command. If the command fails, all channels are RAID + // channels + raid_dev->channel_class = 0xFF; + if (mbox_post_sync_cmd(adapter, raw_mbox) == 0) { + raid_dev->channel_class = *(uint8_t *)adapter->ibuf; + } + + return; +} + + +/** + * megaraid_mbox_flush_cache - flush adapter and disks cache + * @param adapter : soft state for the controller + * + * Flush adapter cache followed by disks cache + */ +static void +megaraid_mbox_flush_cache(adapter_t *adapter) +{ + mbox_t *mbox; + uint8_t raw_mbox[sizeof(mbox_t)]; + + + mbox = (mbox_t *)raw_mbox; + + memset((caddr_t)raw_mbox, 0, sizeof(mbox_t)); + + raw_mbox[0] = FLUSH_ADAPTER; + + if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) { + con_log(CL_ANN, ("megaraid: flush adapter failed\n")); + } + + raw_mbox[0] = FLUSH_SYSTEM; + + if (mbox_post_sync_cmd(adapter, raw_mbox) != 0) { + con_log(CL_ANN, ("megaraid: flush disks cache failed\n")); + } + + return; +} + + +/** + * megaraid_mbox_display_scb - display SCB information, mostly debug purposes + * @param adapter : controllers' soft state + * @param scb : SCB to be displayed + * @param level : debug level for console print + * + * Diplay information about the given SCB iff the current debug level is + * verbose + */ +static void +megaraid_mbox_display_scb(adapter_t *adapter, scb_t *scb) +{ + mbox_ccb_t *ccb; + struct scsi_cmnd *scp; + mbox_t *mbox; + int level; + int i; + + + ccb = (mbox_ccb_t *)scb->ccb; + scp = scb->scp; + mbox = ccb->mbox; + + level = CL_DLEVEL3; + + con_log(level, (KERN_NOTICE + "megaraid mailbox: status:%#x cmd:%#x id:%#x ", scb->status, + mbox->cmd, scb->sno)); + + con_log(level, ("sec:%#x lba:%#x addr:%#x ld:%d sg:%d\n", + mbox->numsectors, mbox->lba, mbox->xferaddr, mbox->logdrv, + mbox->numsge)); + + if (!scp) return; + + con_log(level, (KERN_NOTICE "scsi cmnd: ")); + + for (i = 0; i < scp->cmd_len; i++) { + con_log(level, ("%#2.02x ", scp->cmnd[i])); + } + + con_log(level, ("\n")); + + return; +} + + +/** + * megaraid_mbox_setup_device_map - manage device ids + * @adapter : Driver's soft state + * + * Manange the device ids to have an appropraite mapping between the kernel + * scsi addresses and megaraid scsi and logical drive addresses. We export + * scsi devices on their actual addresses, whereas the logical drives are + * exported on a virtual scsi channel. + **/ +static void +megaraid_mbox_setup_device_map(adapter_t *adapter) +{ + uint8_t c; + uint8_t t; + + /* + * First fill the values on the logical drive channel + */ + for (t = 0; t < LSI_MAX_LOGICAL_DRIVES_64LD; t++) + adapter->device_ids[adapter->max_channel][t] = + (t < adapter->init_id) ? t : t - 1; + + adapter->device_ids[adapter->max_channel][adapter->init_id] = 0xFF; + + /* + * Fill the values on the physical devices channels + */ + for (c = 0; c < adapter->max_channel; c++) + for (t = 0; t < LSI_MAX_LOGICAL_DRIVES_64LD; t++) + adapter->device_ids[c][t] = (c << 8) | t; +} + + +/* + * END: internal commands library + */ + +/* + * START: Interface for the common management module + * + * This is the module, which interfaces with the common mangement module to + * provide support for ioctl and sysfs + */ + +/** + * megaraid_cmm_register - register with the mangement module + * @param adapter : HBA soft state + * + * Register with the management module, which allows applications to issue + * ioctl calls to the drivers. This interface is used by the management module + * to setup sysfs support as well. + */ +static int +megaraid_cmm_register(adapter_t *adapter) +{ + mraid_device_t *raid_dev = ADAP2RAIDDEV(adapter); + mraid_mmadp_t adp; + scb_t *scb; + mbox_ccb_t *ccb; + int rval; + int i; + + // Allocate memory for the base list of scb for management module. + adapter->uscb_list = kmalloc(sizeof(scb_t) * MBOX_MAX_USER_CMDS, + GFP_KERNEL); + + if (adapter->uscb_list == NULL) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: out of memory, %s %d\n", __FUNCTION__, + __LINE__)); + return -1; + } + memset(adapter->uscb_list, 0, sizeof(scb_t) * MBOX_MAX_USER_CMDS); + + + // Initialize the synchronization parameters for resources for + // commands for management module + INIT_LIST_HEAD(&adapter->uscb_pool); + + spin_lock_init(USER_FREE_LIST_LOCK(adapter)); + + + + // link all the packets. Note, CCB for commands, coming from the + // commom management module, mailbox physical address are already + // setup by it. We just need placeholder for that in our local command + // control blocks + for (i = 0; i < MBOX_MAX_USER_CMDS; i++) { + + scb = adapter->uscb_list + i; + ccb = raid_dev->ccb_list + i; + + scb->ccb = (caddr_t)ccb; + ccb->mbox64 = raid_dev->umbox64 + i; + ccb->mbox = &ccb->mbox64->mbox32; + ccb->raw_mbox = (uint8_t *)ccb->mbox; + + scb->gp = 0; + + // COMMAND ID 0 - (MBOX_MAX_SCSI_CMDS-1) ARE RESERVED FOR + // COMMANDS COMING FROM IO SUBSYSTEM (MID-LAYER) + scb->sno = i + MBOX_MAX_SCSI_CMDS; + + scb->scp = NULL; + scb->state = SCB_FREE; + scb->dma_direction = PCI_DMA_NONE; + scb->dma_type = MRAID_DMA_NONE; + scb->dev_channel = -1; + scb->dev_target = -1; + + // put scb in the free pool + list_add_tail(&scb->list, &adapter->uscb_pool); + } + + adp.unique_id = adapter->unique_id; + adp.drvr_type = DRVRTYPE_MBOX; + adp.drvr_data = (unsigned long)adapter; + adp.pdev = adapter->pdev; + adp.issue_uioc = megaraid_mbox_mm_handler; + adp.timeout = 30; + adp.max_kioc = MBOX_MAX_USER_CMDS; + + if ((rval = mraid_mm_register_adp(&adp)) != 0) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid mbox: did not register with CMM\n")); + + kfree(adapter->uscb_list); + } + + return rval; +} + + +/** + * megaraid_cmm_unregister - un-register with the mangement module + * @param adapter : HBA soft state + * + * Un-register with the management module. + * FIXME: mgmt module must return failure for unregister if it has pending + * commands in LLD + */ +static int +megaraid_cmm_unregister(adapter_t *adapter) +{ + kfree(adapter->uscb_list); + mraid_mm_unregister_adp(adapter->unique_id); + return 0; +} + + +/** + * megaraid_mbox_mm_handler - interface for CMM to issue commands to LLD + * @param drvr_data : LLD specific data + * @param kioc : CMM interface packet + * @param action : command action + * + * This routine is invoked whenever the Common Mangement Module (CMM) has a + * command for us. The 'action' parameter specifies if this is a new command + * or otherwise. + */ +static int +megaraid_mbox_mm_handler(unsigned long drvr_data, uioc_t *kioc, uint32_t action) +{ + adapter_t *adapter; + + if (action != IOCTL_ISSUE) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: unsupported management action:%#2x\n", + action)); + return (-ENOTSUPP); + } + + adapter = (adapter_t *)drvr_data; + + // make sure this adapter is not being detached right now. + if (atomic_read(&adapter->being_detached)) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: reject management request, detaching\n")); + return (-ENODEV); + } + + switch (kioc->opcode) { + + case GET_ADAP_INFO: + + kioc->status = gather_hbainfo(adapter, (mraid_hba_info_t *) + (unsigned long)kioc->buf_vaddr); + + kioc->done(kioc); + + return kioc->status; + + case MBOX_CMD: + + return megaraid_mbox_mm_command(adapter, kioc); + + default: + kioc->status = (-EINVAL); + kioc->done(kioc); + return (-EINVAL); + } + + return 0; // not reached +} + +/** + * megaraid_mbox_mm_command - issues commands routed through CMM + * @param adapter : HBA soft state + * @param kioc : management command packet + * + * Issues commands, which are routed through the management module. + */ +static int +megaraid_mbox_mm_command(adapter_t *adapter, uioc_t *kioc) +{ + struct list_head *head = &adapter->uscb_pool; + mbox64_t *mbox64; + uint8_t *raw_mbox; + scb_t *scb; + mbox_ccb_t *ccb; + unsigned long flags; + + // detach one scb from free pool + spin_lock_irqsave(USER_FREE_LIST_LOCK(adapter), flags); + + if (list_empty(head)) { // should never happen because of CMM + + con_log(CL_ANN, (KERN_WARNING + "megaraid mbox: bug in cmm handler, lost resources\n")); + + spin_unlock_irqrestore(USER_FREE_LIST_LOCK(adapter), flags); + + return (-EINVAL); + } + + scb = list_entry(head->next, scb_t, list); + list_del_init(&scb->list); + + spin_unlock_irqrestore(USER_FREE_LIST_LOCK(adapter), flags); + + scb->state = SCB_ACTIVE; + scb->dma_type = MRAID_DMA_NONE; + + ccb = (mbox_ccb_t *)scb->ccb; + mbox64 = (mbox64_t *)(unsigned long)kioc->cmdbuf; + raw_mbox = (uint8_t *)&mbox64->mbox32; + + memcpy(ccb->mbox64, mbox64, sizeof(mbox64_t)); + + scb->gp = (unsigned long)kioc; + + /* + * If it is a logdrv random delete operation, we have to wait till + * there are no outstanding cmds at the fw and then issue it directly + */ + if (raw_mbox[0] == FC_DEL_LOGDRV && raw_mbox[2] == OP_DEL_LOGDRV) { + + if (wait_till_fw_empty(adapter)) { + con_log(CL_ANN, (KERN_NOTICE + "megaraid mbox: LD delete, timed out\n")); + + kioc->status = -ETIME; + + scb->status = -1; + + megaraid_mbox_mm_done(adapter, scb); + + return (-ETIME); + } + + INIT_LIST_HEAD(&scb->list); + + scb->state = SCB_ISSUED; + if (mbox_post_cmd(adapter, scb) != 0) { + + con_log(CL_ANN, (KERN_NOTICE + "megaraid mbox: LD delete, mailbox busy\n")); + + kioc->status = -EBUSY; + + scb->status = -1; + + megaraid_mbox_mm_done(adapter, scb); + + return (-EBUSY); + } + + return 0; + } + + // put the command on the pending list and execute + megaraid_mbox_runpendq(adapter, scb); + + return 0; +} + + +static int +wait_till_fw_empty(adapter_t *adapter) +{ + unsigned long flags = 0; + int i; + + + /* + * Set the quiescent flag to stop issuing cmds to FW. + */ + spin_lock_irqsave(adapter->host_lock, flags); + adapter->quiescent++; + spin_unlock_irqrestore(adapter->host_lock, flags); + + /* + * Wait till there are no more cmds outstanding at FW. Try for at most + * 60 seconds + */ + for (i = 0; i < 60 && adapter->outstanding_cmds; i++) { + con_log(CL_DLEVEL1, (KERN_INFO + "megaraid: FW has %d pending commands\n", + adapter->outstanding_cmds)); + + msleep(1000); + } + + return adapter->outstanding_cmds; +} + + +/** + * megaraid_mbox_mm_done - callback for CMM commands + * @adapter : HBA soft state + * @scb : completed command + * + * Callback routine for internal commands originated from the management + * module. + */ +static void +megaraid_mbox_mm_done(adapter_t *adapter, scb_t *scb) +{ + uioc_t *kioc; + mbox64_t *mbox64; + uint8_t *raw_mbox; + unsigned long flags; + + kioc = (uioc_t *)scb->gp; + kioc->status = 0; + mbox64 = (mbox64_t *)(unsigned long)kioc->cmdbuf; + mbox64->mbox32.status = scb->status; + raw_mbox = (uint8_t *)&mbox64->mbox32; + + + // put scb in the free pool + scb->state = SCB_FREE; + scb->scp = NULL; + + spin_lock_irqsave(USER_FREE_LIST_LOCK(adapter), flags); + + list_add(&scb->list, &adapter->uscb_pool); + + spin_unlock_irqrestore(USER_FREE_LIST_LOCK(adapter), flags); + + // if a delete logical drive operation succeeded, restart the + // controller + if (raw_mbox[0] == FC_DEL_LOGDRV && raw_mbox[2] == OP_DEL_LOGDRV) { + + adapter->quiescent--; + + megaraid_mbox_runpendq(adapter, NULL); + } + + kioc->done(kioc); + + return; +} + + +/** + * gather_hbainfo - HBA characteristics for the applications + * @param adapter : HBA soft state + * @param hinfo : pointer to the caller's host info strucuture + */ +static int +gather_hbainfo(adapter_t *adapter, mraid_hba_info_t *hinfo) +{ + uint8_t dmajor; + + dmajor = megaraid_mbox_version[0]; + + hinfo->pci_vendor_id = adapter->pdev->vendor; + hinfo->pci_device_id = adapter->pdev->device; + hinfo->subsys_vendor_id = adapter->pdev->subsystem_vendor; + hinfo->subsys_device_id = adapter->pdev->subsystem_device; + + hinfo->pci_bus = adapter->pdev->bus->number; + hinfo->pci_dev_fn = adapter->pdev->devfn; + hinfo->pci_slot = PCI_SLOT(adapter->pdev->devfn); + hinfo->irq = adapter->host->irq; + hinfo->baseport = ADAP2RAIDDEV(adapter)->baseport; + + hinfo->unique_id = (hinfo->pci_bus << 8) | adapter->pdev->devfn; + hinfo->host_no = adapter->host->host_no; + + return 0; +} + +/* + * END: Interface for the common management module + */ + + +/* + * END: Mailbox Low Level Driver + */ +module_init(megaraid_init); +module_exit(megaraid_exit); + +/* vim: set ts=8 sw=8 tw=78 ai si: */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/scsi/megaraid/megaraid_mbox.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,268 @@ +/* + * + * Linux MegaRAID device driver + * + * Copyright (c) 2003-2004 LSI Logic Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * FILE : megaraid_mbox.h + */ + +#ifndef _MEGARAID_H_ +#define _MEGARAID_H_ + + +#include "mega_common.h" +#include "mbox_defs.h" +#include "megaraid_ioctl.h" + + +#define MEGARAID_VERSION "2.20.3.1" +#define MEGARAID_EXT_VERSION "(Release Date: Tue Aug 24 09:43:35 EDT 2004)" + + +/* + * Define some PCI values here until they are put in the kernel + */ +#define PCI_DEVICE_ID_PERC4_DI_DISCOVERY 0x000E +#define PCI_SUBSYS_ID_PERC4_DI_DISCOVERY 0x0123 + +#define PCI_DEVICE_ID_PERC4_SC 0x1960 +#define PCI_SUBSYS_ID_PERC4_SC 0x0520 + +#define PCI_DEVICE_ID_PERC4_DC 0x1960 +#define PCI_SUBSYS_ID_PERC4_DC 0x0518 + +#define PCI_DEVICE_ID_PERC4_QC 0x0407 +#define PCI_SUBSYS_ID_PERC4_QC 0x0531 + +#define PCI_DEVICE_ID_PERC4_DI_EVERGLADES 0x000F +#define PCI_SUBSYS_ID_PERC4_DI_EVERGLADES 0x014A + +#define PCI_DEVICE_ID_PERC4E_SI_BIGBEND 0x0013 +#define PCI_SUBSYS_ID_PERC4E_SI_BIGBEND 0x016c + +#define PCI_DEVICE_ID_PERC4E_DI_KOBUK 0x0013 +#define PCI_SUBSYS_ID_PERC4E_DI_KOBUK 0x016d + +#define PCI_DEVICE_ID_PERC4E_DI_CORVETTE 0x0013 +#define PCI_SUBSYS_ID_PERC4E_DI_CORVETTE 0x016e + +#define PCI_DEVICE_ID_PERC4E_DI_EXPEDITION 0x0013 +#define PCI_SUBSYS_ID_PERC4E_DI_EXPEDITION 0x016f + +#define PCI_DEVICE_ID_PERC4E_DI_GUADALUPE 0x0013 +#define PCI_SUBSYS_ID_PERC4E_DI_GUADALUPE 0x0170 + +#define PCI_DEVICE_ID_PERC4E_DC_320_2E 0x0408 +#define PCI_SUBSYS_ID_PERC4E_DC_320_2E 0x0002 + +#define PCI_DEVICE_ID_PERC4E_SC_320_1E 0x0408 +#define PCI_SUBSYS_ID_PERC4E_SC_320_1E 0x0001 + +#define PCI_DEVICE_ID_MEGARAID_SCSI_320_0 0x1960 +#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_0 0xA520 + +#define PCI_DEVICE_ID_MEGARAID_SCSI_320_1 0x1960 +#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_1 0x0520 + +#define PCI_DEVICE_ID_MEGARAID_SCSI_320_2 0x1960 +#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_2 0x0518 + +#define PCI_DEVICE_ID_MEGARAID_SCSI_320_0x 0x0407 +#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_0x 0x0530 + +#define PCI_DEVICE_ID_MEGARAID_SCSI_320_2x 0x0407 +#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_2x 0x0532 + +#define PCI_DEVICE_ID_MEGARAID_SCSI_320_4x 0x0407 +#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_4x 0x0531 + +#define PCI_DEVICE_ID_MEGARAID_SCSI_320_1E 0x0408 +#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_1E 0x0001 + +#define PCI_DEVICE_ID_MEGARAID_SCSI_320_2E 0x0408 +#define PCI_SUBSYS_ID_MEGARAID_SCSI_320_2E 0x0002 + +#define PCI_DEVICE_ID_MEGARAID_I4_133_RAID 0x1960 +#define PCI_SUBSYS_ID_MEGARAID_I4_133_RAID 0x0522 + +#define PCI_DEVICE_ID_MEGARAID_SATA_150_4 0x1960 +#define PCI_SUBSYS_ID_MEGARAID_SATA_150_4 0x4523 + +#define PCI_DEVICE_ID_MEGARAID_SATA_150_6 0x1960 +#define PCI_SUBSYS_ID_MEGARAID_SATA_150_6 0x0523 + +#define PCI_DEVICE_ID_MEGARAID_SATA_300_4x 0x0409 +#define PCI_SUBSYS_ID_MEGARAID_SATA_300_4x 0x3004 + +#define PCI_DEVICE_ID_MEGARAID_SATA_300_8x 0x0409 +#define PCI_SUBSYS_ID_MEGARAID_SATA_300_8x 0x3008 + +#define PCI_DEVICE_ID_INTEL_RAID_SRCU42X 0x0407 +#define PCI_SUBSYS_ID_INTEL_RAID_SRCU42X 0x0532 + +#define PCI_DEVICE_ID_INTEL_RAID_SRCS16 0x1960 +#define PCI_SUBSYS_ID_INTEL_RAID_SRCS16 0x0523 + +#define PCI_DEVICE_ID_INTEL_RAID_SRCU42E 0x0408 +#define PCI_SUBSYS_ID_INTEL_RAID_SRCU42E 0x0002 + +#define PCI_DEVICE_ID_INTEL_RAID_SRCZCRX 0x0407 +#define PCI_SUBSYS_ID_INTEL_RAID_SRCZCRX 0x0530 + +#define PCI_DEVICE_ID_INTEL_RAID_SRCS28X 0x0409 +#define PCI_SUBSYS_ID_INTEL_RAID_SRCS28X 0x3008 + +#define PCI_DEVICE_ID_INTEL_RAID_SROMBU42E_ALIEF 0x0408 +#define PCI_SUBSYS_ID_INTEL_RAID_SROMBU42E_ALIEF 0x3431 + +#define PCI_DEVICE_ID_INTEL_RAID_SROMBU42E_HARWICH 0x0408 +#define PCI_SUBSYS_ID_INTEL_RAID_SROMBU42E_HARWICH 0x3499 + +#define PCI_DEVICE_ID_INTEL_RAID_SRCU41L_LAKE_SHETEK 0x1960 +#define PCI_SUBSYS_ID_INTEL_RAID_SRCU41L_LAKE_SHETEK 0x0520 + +#define PCI_DEVICE_ID_FSC_MEGARAID_PCI_EXPRESS_ROMB 0x0408 +#define PCI_SUBSYS_ID_FSC_MEGARAID_PCI_EXPRESS_ROMB 0x1065 + +#define PCI_DEVICE_ID_MEGARAID_ACER_ROMB_2E 0x0408 +#define PCI_SUBSYS_ID_MEGARAID_ACER_ROMB_2E 0x004D + +#define PCI_SUBSYS_ID_PERC3_QC 0x0471 +#define PCI_SUBSYS_ID_PERC3_DC 0x0493 +#define PCI_SUBSYS_ID_PERC3_SC 0x0475 + +#ifndef PCI_SUBSYS_ID_FSC +#define PCI_SUBSYS_ID_FSC 0x1734 +#endif + +#define MBOX_MAX_SCSI_CMDS 128 // number of cmds reserved for kernel +#define MBOX_MAX_USER_CMDS 32 // number of cmds for applications +#define MBOX_DEF_CMD_PER_LUN 64 // default commands per lun +#define MBOX_DEFAULT_SG_SIZE 26 // default sg size supported by all fw +#define MBOX_MAX_SG_SIZE 32 // maximum scatter-gather list size +#define MBOX_MAX_SECTORS 128 // maximum sectors per IO +#define MBOX_TIMEOUT 30 // timeout value for internal cmds +#define MBOX_BUSY_WAIT 10 // max usec to wait for busy mailbox +#define MBOX_RESET_WAIT 180 // wait these many seconds in reset +#define MBOX_RESET_EXT_WAIT 120 // extended wait reset + +/* + * maximum transfer that can happen through the firmware commands issued + * internnaly from the driver. + */ +#define MBOX_IBUF_SIZE 4096 + + +/** + * mbox_ccb_t - command control block specific to mailbox based controllers + * @raw_mbox : raw mailbox pointer + * @mbox : mailbox + * @mbox64 : extended mailbox + * @mbox_dma_h : maibox dma address + * @sgl64 : 64-bit scatter-gather list + * @sgl32 : 32-bit scatter-gather list + * @sgl_dma_h : dma handle for the scatter-gather list + * @pthru : passthru structure + * @pthru_dma_h : dma handle for the passthru structure + * @epthru : extended passthru structure + * @epthru_dma_h : dma handle for extended passthru structure + * @buf_dma_h : dma handle for buffers w/o sg list + * + * command control block specific to the mailbox based controllers + */ +typedef struct { + uint8_t *raw_mbox; + mbox_t *mbox; + mbox64_t *mbox64; + dma_addr_t mbox_dma_h; + mbox_sgl64 *sgl64; + mbox_sgl32 *sgl32; + dma_addr_t sgl_dma_h; + mraid_passthru_t *pthru; + dma_addr_t pthru_dma_h; + mraid_epassthru_t *epthru; + dma_addr_t epthru_dma_h; + dma_addr_t buf_dma_h; +} mbox_ccb_t; + + +/** + * mraid_device_t - adapter soft state structure for mailbox controllers + * @param una_mbox64 : 64-bit mbox - unaligned + * @param una_mbox64_dma : mbox dma addr - unaligned + * @param mbox : 32-bit mbox - aligned + * @param mbox64 : 64-bit mbox - aligned + * @param mbox_dma : mbox dma addr - aligned + * @param mailbox_lock : exclusion lock for the mailbox + * @param baseport : base port of hba memory + * @param baseaddr : mapped addr of hba memory + * @param mbox_pool : pool of mailboxes + * @param mbox_pool_handle : handle for the mailbox pool memory + * @param epthru_pool : a pool for extended passthru commands + * @param epthru_pool_handle : handle to the pool above + * @param sg_pool : pool of scatter-gather lists for this driver + * @param sg_pool_handle : handle to the pool above + * @param ccb_list : list of our command control blocks + * @param uccb_list : list of cmd control blocks for mgmt module + * @param umbox64 : array of mailbox for user commands (cmm) + * @param pdrv_state : array for state of each physical drive. + * @param last_disp : flag used to show device scanning + * @param hw_error : set if FW not responding + * @param fast_load : If set, skip physical device scanning + * @channel_class : channel class, RAID or SCSI + * + * Initialization structure for mailbox controllers: memory based and IO based + * All the fields in this structure are LLD specific and may be discovered at + * init() or start() time. + * + * NOTE: The fields of this structures are placed to minimize cache misses + */ +typedef struct { + mbox64_t *una_mbox64; + dma_addr_t una_mbox64_dma; + mbox_t *mbox; + mbox64_t *mbox64; + dma_addr_t mbox_dma; + spinlock_t mailbox_lock; + unsigned long baseport; + unsigned long baseaddr; + struct mraid_pci_blk mbox_pool[MBOX_MAX_SCSI_CMDS]; + struct dma_pool *mbox_pool_handle; + struct mraid_pci_blk epthru_pool[MBOX_MAX_SCSI_CMDS]; + struct dma_pool *epthru_pool_handle; + struct mraid_pci_blk sg_pool[MBOX_MAX_SCSI_CMDS]; + struct dma_pool *sg_pool_handle; + mbox_ccb_t ccb_list[MBOX_MAX_SCSI_CMDS]; + mbox_ccb_t uccb_list[MBOX_MAX_USER_CMDS]; + mbox64_t umbox64[MBOX_MAX_USER_CMDS]; + + uint8_t pdrv_state[MBOX_MAX_PHYSICAL_DRIVES]; + uint32_t last_disp; + int hw_error; + int fast_load; + uint8_t channel_class; +} mraid_device_t; + +// route to raid device from adapter +#define ADAP2RAIDDEV(adp) ((mraid_device_t *)((adp)->raid_device)) + +#define MAILBOX_LOCK(rdev) (&(rdev)->mailbox_lock) + +// Find out if this channel is a RAID or SCSI +#define IS_RAID_CH(rdev, ch) (((rdev)->channel_class >> (ch)) & 0x01) + + +#define RDINDOOR(rdev) readl((rdev)->baseaddr + 0x20) +#define RDOUTDOOR(rdev) readl((rdev)->baseaddr + 0x2C) +#define WRINDOOR(rdev, value) writel(value, (rdev)->baseaddr + 0x20) +#define WROUTDOOR(rdev, value) writel(value, (rdev)->baseaddr + 0x2C) + +#endif // _MEGARAID_H_ + +// vim: set ts=8 sw=8 tw=78: --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/scsi/megaraid/megaraid_mm.c 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,1160 @@ +/* + * + * Linux MegaRAID device driver + * + * Copyright (c) 2003-2004 LSI Logic Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * FILE : megaraid_mm.c + * Version : v2.20.2.0 (August 19 2004) + * + * Common management module + */ + +#include "megaraid_mm.h" + + +// Entry points for char node driver +static int mraid_mm_open(struct inode *, struct file *); +static int mraid_mm_ioctl(struct inode *, struct file *, uint, unsigned long); + + +// routines to convert to and from the old the format +static int mimd_to_kioc(mimd_t __user *, mraid_mmadp_t *, uioc_t *); +static int kioc_to_mimd(uioc_t *, mimd_t __user *); + + +// Helper functions +static int handle_drvrcmd(void __user *, uint8_t, int *); +static int lld_ioctl(mraid_mmadp_t *, uioc_t *); +static void ioctl_done(uioc_t *); +static void lld_timedout(unsigned long); +static void hinfo_to_cinfo(mraid_hba_info_t *, mcontroller_t *); +static mraid_mmadp_t *mraid_mm_get_adapter(mimd_t __user *, int *); +static uioc_t *mraid_mm_alloc_kioc(mraid_mmadp_t *); +static void mraid_mm_dealloc_kioc(mraid_mmadp_t *, uioc_t *); +static int mraid_mm_attach_buf(mraid_mmadp_t *, uioc_t *, int); +static int mraid_mm_setup_dma_pools(mraid_mmadp_t *); +static void mraid_mm_free_adp_resources(mraid_mmadp_t *); +static void mraid_mm_teardown_dma_pools(mraid_mmadp_t *); + +#ifdef CONFIG_COMPAT +static int mraid_mm_compat_ioctl(unsigned int, unsigned int, unsigned long, + struct file *); +#endif + +MODULE_AUTHOR("LSI Logic Corporation"); +MODULE_DESCRIPTION("LSI Logic Management Module"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(LSI_COMMON_MOD_VERSION); + +static int dbglevel = CL_ANN; +module_param_named(dlevel, dbglevel, int, 0); +MODULE_PARM_DESC(dlevel, "Debug level (default=0)"); + +EXPORT_SYMBOL(mraid_mm_register_adp); +EXPORT_SYMBOL(mraid_mm_unregister_adp); + +static int majorno; +static uint32_t drvr_ver = 0x02200100; + +static int adapters_count_g; +static struct list_head adapters_list_g; + +wait_queue_head_t wait_q; + +static struct file_operations lsi_fops = { + .open = mraid_mm_open, + .ioctl = mraid_mm_ioctl, + .owner = THIS_MODULE, +}; + +/** + * mraid_mm_open - open routine for char node interface + * @inod : unused + * @filep : unused + * + * allow ioctl operations by apps only if they superuser privilege + */ +static int +mraid_mm_open(struct inode *inode, struct file *filep) +{ + /* + * Only allow superuser to access private ioctl interface + */ + if (!capable(CAP_SYS_ADMIN)) return (-EACCES); + + return 0; +} + +/** + * mraid_mm_ioctl - module entry-point for ioctls + * @inode : inode (ignored) + * @filep : file operations pointer (ignored) + * @cmd : ioctl command + * @arg : user ioctl packet + */ +static int +mraid_mm_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, + unsigned long arg) +{ + uioc_t *kioc; + char signature[EXT_IOCTL_SIGN_SZ] = {0}; + int rval; + mraid_mmadp_t *adp; + uint8_t old_ioctl; + int drvrcmd_rval; + void __user *argp = (void __user *)arg; + + /* + * Make sure only USCSICMD are issued through this interface. + * MIMD application would still fire different command. + */ + + if ((_IOC_TYPE(cmd) != MEGAIOC_MAGIC) && (cmd != USCSICMD)) { + return (-EINVAL); + } + + /* + * Look for signature to see if this is the new or old ioctl format. + */ + if (copy_from_user(signature, argp, EXT_IOCTL_SIGN_SZ)) { + con_log(CL_ANN, (KERN_WARNING + "megaraid cmm: copy from usr addr failed\n")); + return (-EFAULT); + } + + if (memcmp(signature, EXT_IOCTL_SIGN, EXT_IOCTL_SIGN_SZ) == 0) + old_ioctl = 0; + else + old_ioctl = 1; + + /* + * At present, we don't support the new ioctl packet + */ + if (!old_ioctl ) + return (-EINVAL); + + /* + * If it is a driver ioctl (as opposed to fw ioctls), then we can + * handle the command locally. rval > 0 means it is not a drvr cmd + */ + rval = handle_drvrcmd(argp, old_ioctl, &drvrcmd_rval); + + if (rval < 0) + return rval; + else if (rval == 0) + return drvrcmd_rval; + + rval = 0; + if ((adp = mraid_mm_get_adapter(argp, &rval)) == NULL) { + return rval; + } + + /* + * The following call will block till a kioc is available + */ + kioc = mraid_mm_alloc_kioc(adp); + + /* + * User sent the old mimd_t ioctl packet. Convert it to uioc_t. + */ + if ((rval = mimd_to_kioc(argp, adp, kioc))) { + mraid_mm_dealloc_kioc(adp, kioc); + return rval; + } + + kioc->done = ioctl_done; + + /* + * Issue the IOCTL to the low level driver + */ + if ((rval = lld_ioctl(adp, kioc))) { + mraid_mm_dealloc_kioc(adp, kioc); + return rval; + } + + /* + * Convert the kioc back to user space + */ + rval = kioc_to_mimd(kioc, argp); + + /* + * Return the kioc to free pool + */ + mraid_mm_dealloc_kioc(adp, kioc); + + return rval; +} + + +/** + * mraid_mm_get_adapter - Returns corresponding adapters for the mimd packet + * @umimd : User space mimd_t ioctl packet + * @adapter : pointer to the adapter (OUT) + */ +static mraid_mmadp_t * +mraid_mm_get_adapter(mimd_t __user *umimd, int *rval) +{ + mraid_mmadp_t *adapter; + mimd_t mimd; + uint32_t adapno; + int iterator; + + + if (copy_from_user(&mimd, umimd, sizeof(mimd_t))) { + *rval = -EFAULT; + return NULL; + } + + adapno = GETADAP(mimd.ui.fcs.adapno); + + if (adapno >= adapters_count_g) { + *rval = -ENODEV; + return NULL; + } + + adapter = NULL; + iterator = 0; + + list_for_each_entry(adapter, &adapters_list_g, list) { + if (iterator++ == adapno) break; + } + + if (!adapter) { + *rval = -ENODEV; + return NULL; + } + + return adapter; +} + +/* + * handle_drvrcmd - This routine checks if the opcode is a driver + * cmd and if it is, handles it. + * @arg : packet sent by the user app + * @old_ioctl : mimd if 1; uioc otherwise + */ +static int +handle_drvrcmd(void __user *arg, uint8_t old_ioctl, int *rval) +{ + mimd_t __user *umimd; + mimd_t kmimd; + uint8_t opcode; + uint8_t subopcode; + + if (old_ioctl) + goto old_packet; + else + goto new_packet; + +new_packet: + return (-ENOTSUPP); + +old_packet: + *rval = 0; + umimd = arg; + + if (copy_from_user(&kmimd, umimd, sizeof(mimd_t))) + return (-EFAULT); + + opcode = kmimd.ui.fcs.opcode; + subopcode = kmimd.ui.fcs.subopcode; + + /* + * If the opcode is 0x82 and the subopcode is either GET_DRVRVER or + * GET_NUMADP, then we can handle. Otherwise we should return 1 to + * indicate that we cannot handle this. + */ + if (opcode != 0x82) + return 1; + + switch (subopcode) { + + case MEGAIOC_QDRVRVER: + + if (copy_to_user(kmimd.data, &drvr_ver, sizeof(uint32_t))) + return (-EFAULT); + + return 0; + + case MEGAIOC_QNADAP: + + *rval = adapters_count_g; + + if (copy_to_user(kmimd.data, &adapters_count_g, + sizeof(uint32_t))) + return (-EFAULT); + + return 0; + + default: + /* cannot handle */ + return 1; + } + + return 0; +} + + +/** + * mimd_to_kioc - Converter from old to new ioctl format + * + * @umimd : user space old MIMD IOCTL + * @kioc : kernel space new format IOCTL + * + * Routine to convert MIMD interface IOCTL to new interface IOCTL packet. The + * new packet is in kernel space so that driver can perform operations on it + * freely. + */ + +static int +mimd_to_kioc(mimd_t __user *umimd, mraid_mmadp_t *adp, uioc_t *kioc) +{ + mbox64_t *mbox64; + mbox_t *mbox; + mraid_passthru_t *pthru32; + uint32_t adapno; + uint8_t opcode; + uint8_t subopcode; + mimd_t mimd; + + if (copy_from_user(&mimd, umimd, sizeof(mimd_t))) + return (-EFAULT); + + /* + * Applications are not allowed to send extd pthru + */ + if ((mimd.mbox[0] == MBOXCMD_PASSTHRU64) || + (mimd.mbox[0] == MBOXCMD_EXTPTHRU)) + return (-EINVAL); + + opcode = mimd.ui.fcs.opcode; + subopcode = mimd.ui.fcs.subopcode; + adapno = GETADAP(mimd.ui.fcs.adapno); + + if (adapno >= adapters_count_g) + return (-ENODEV); + + kioc->adapno = adapno; + kioc->mb_type = MBOX_LEGACY; + kioc->app_type = APPTYPE_MIMD; + + switch (opcode) { + + case 0x82: + + if (subopcode == MEGAIOC_QADAPINFO) { + + kioc->opcode = GET_ADAP_INFO; + kioc->data_dir = UIOC_RD; + kioc->xferlen = sizeof(mraid_hba_info_t); + + if (mraid_mm_attach_buf(adp, kioc, kioc->xferlen)) + return (-ENOMEM); + } + else { + con_log(CL_ANN, (KERN_WARNING + "megaraid cmm: Invalid subop\n")); + return (-EINVAL); + } + + break; + + case 0x81: + + kioc->opcode = MBOX_CMD; + kioc->xferlen = mimd.ui.fcs.length; + kioc->user_data_len = kioc->xferlen; + kioc->user_data = mimd.ui.fcs.buffer; + + if (mraid_mm_attach_buf(adp, kioc, kioc->xferlen)) + return (-ENOMEM); + + if (mimd.outlen) kioc->data_dir = UIOC_RD; + if (mimd.inlen) kioc->data_dir |= UIOC_WR; + + break; + + case 0x80: + + kioc->opcode = MBOX_CMD; + kioc->xferlen = (mimd.outlen > mimd.inlen) ? + mimd.outlen : mimd.inlen; + kioc->user_data_len = kioc->xferlen; + kioc->user_data = mimd.data; + + if (mraid_mm_attach_buf(adp, kioc, kioc->xferlen)) + return (-ENOMEM); + + if (mimd.outlen) kioc->data_dir = UIOC_RD; + if (mimd.inlen) kioc->data_dir |= UIOC_WR; + + break; + + default: + return (-EINVAL); + } + + /* + * If driver command, nothing else to do + */ + if (opcode == 0x82) + return 0; + + /* + * This is a mailbox cmd; copy the mailbox from mimd + */ + mbox64 = (mbox64_t *)((unsigned long)kioc->cmdbuf); + mbox = &mbox64->mbox32; + memcpy(mbox, mimd.mbox, 14); + + if (mbox->cmd != MBOXCMD_PASSTHRU) { // regular DCMD + + mbox->xferaddr = (uint32_t)kioc->buf_paddr; + + if (kioc->data_dir & UIOC_WR) { + if (copy_from_user(kioc->buf_vaddr, kioc->user_data, + kioc->xferlen)) { + return (-EFAULT); + } + } + + return 0; + } + + /* + * This is a regular 32-bit pthru cmd; mbox points to pthru struct. + * Just like in above case, the beginning for memblk is treated as + * a mailbox. The passthru will begin at next 1K boundary. And the + * data will start 1K after that. + */ + pthru32 = kioc->pthru32; + kioc->user_pthru = &umimd->pthru; + mbox->xferaddr = (uint32_t)kioc->pthru32_h; + + if (copy_from_user(pthru32, kioc->user_pthru, + sizeof(mraid_passthru_t))) { + return (-EFAULT); + } + + pthru32->dataxferaddr = kioc->buf_paddr; + if (kioc->data_dir & UIOC_WR) { + if (copy_from_user(kioc->buf_vaddr, kioc->user_data, + pthru32->dataxferlen)) { + return (-EFAULT); + } + } + + return 0; +} + +/** + * mraid_mm_attch_buf - Attach a free dma buffer for required size + * + * @adp : Adapter softstate + * @kioc : kioc that the buffer needs to be attached to + * @xferlen : required length for buffer + * + * First we search for a pool with smallest buffer that is >= @xferlen. If + * that pool has no free buffer, we will try for the next bigger size. If none + * is available, we will try to allocate the smallest buffer that is >= + * @xferlen and attach it the pool. + */ +static int +mraid_mm_attach_buf(mraid_mmadp_t *adp, uioc_t *kioc, int xferlen) +{ + mm_dmapool_t *pool; + int right_pool = -1; + unsigned long flags; + int i; + + kioc->pool_index = -1; + kioc->buf_vaddr = NULL; + kioc->buf_paddr = 0; + kioc->free_buf = 0; + + /* + * We need xferlen amount of memory. See if we can get it from our + * dma pools. If we don't get exact size, we will try bigger buffer + */ + + for (i = 0; i < MAX_DMA_POOLS; i++) { + + pool = &adp->dma_pool_list[i]; + + if (xferlen > pool->buf_size) + continue; + + if (right_pool == -1) + right_pool = i; + + spin_lock_irqsave(&pool->lock, flags); + + if (!pool->in_use) { + + pool->in_use = 1; + kioc->pool_index = i; + kioc->buf_vaddr = pool->vaddr; + kioc->buf_paddr = pool->paddr; + + spin_unlock_irqrestore(&pool->lock, flags); + return 0; + } + else { + spin_unlock_irqrestore(&pool->lock, flags); + continue; + } + } + + /* + * If xferlen doesn't match any of our pools, return error + */ + if (right_pool == -1) + return -EINVAL; + + /* + * We did not get any buffer from the preallocated pool. Let us try + * to allocate one new buffer. NOTE: This is a blocking call. + */ + pool = &adp->dma_pool_list[right_pool]; + + spin_lock_irqsave(&pool->lock, flags); + + kioc->pool_index = right_pool; + kioc->free_buf = 1; + kioc->buf_vaddr = pci_pool_alloc(pool->handle, GFP_KERNEL, + &kioc->buf_paddr); + spin_unlock_irqrestore(&pool->lock, flags); + + if (!kioc->buf_vaddr) + return -ENOMEM; + + return 0; +} + +/** + * mraid_mm_alloc_kioc - Returns a uioc_t from free list + * @adp : Adapter softstate for this module + * + * The kioc_semaphore is initialized with number of kioc nodes in the + * free kioc pool. If the kioc pool is empty, this function blocks till + * a kioc becomes free. + */ +static uioc_t * +mraid_mm_alloc_kioc(mraid_mmadp_t *adp) +{ + uioc_t *kioc; + struct list_head* head; + unsigned long flags; + + down(&adp->kioc_semaphore); + + spin_lock_irqsave(&adp->kioc_pool_lock, flags); + + head = &adp->kioc_pool; + + if (list_empty(head)) { + up(&adp->kioc_semaphore); + spin_unlock_irqrestore(&adp->kioc_pool_lock, flags); + + con_log(CL_ANN, ("megaraid cmm: kioc list empty!\n")); + return NULL; + } + + kioc = list_entry(head->next, uioc_t, list); + list_del_init(&kioc->list); + + spin_unlock_irqrestore(&adp->kioc_pool_lock, flags); + + memset((caddr_t)(unsigned long)kioc->cmdbuf, 0, sizeof(mbox64_t)); + memset((caddr_t) kioc->pthru32, 0, sizeof(mraid_passthru_t)); + + kioc->buf_vaddr = NULL; + kioc->buf_paddr = 0; + kioc->pool_index =-1; + kioc->free_buf = 0; + kioc->user_data = NULL; + kioc->user_data_len = 0; + kioc->user_pthru = NULL; + + return kioc; +} + +/** + * mraid_mm_dealloc_kioc - Return kioc to free pool + * + * @adp : Adapter softstate + * @kioc : uioc_t node to be returned to free pool + */ +static void +mraid_mm_dealloc_kioc(mraid_mmadp_t *adp, uioc_t *kioc) +{ + mm_dmapool_t *pool; + unsigned long flags; + + pool = &adp->dma_pool_list[kioc->pool_index]; + + /* This routine may be called in non-isr context also */ + spin_lock_irqsave(&pool->lock, flags); + + /* + * While attaching the dma buffer, if we didn't get the required + * buffer from the pool, we would have allocated it at the run time + * and set the free_buf flag. We must free that buffer. Otherwise, + * just mark that the buffer is not in use + */ + if (kioc->free_buf == 1) + pci_pool_free(pool->handle, kioc->buf_vaddr, kioc->buf_paddr); + else + pool->in_use = 0; + + spin_unlock_irqrestore(&pool->lock, flags); + + /* Return the kioc to the free pool */ + spin_lock_irqsave(&adp->kioc_pool_lock, flags); + list_add(&kioc->list, &adp->kioc_pool); + spin_unlock_irqrestore(&adp->kioc_pool_lock, flags); + + /* increment the free kioc count */ + up(&adp->kioc_semaphore); + + return; +} + +/** + * lld_ioctl - Routine to issue ioctl to low level drvr + * + * @adp : The adapter handle + * @kioc : The ioctl packet with kernel addresses + */ +static int +lld_ioctl(mraid_mmadp_t *adp, uioc_t *kioc) +{ + int rval; + struct timer_list timer; + struct timer_list *tp = NULL; + + kioc->status = -ENODATA; + rval = adp->issue_uioc(adp->drvr_data, kioc, IOCTL_ISSUE); + + if (rval) return rval; + + /* + * Start the timer + */ + if (adp->timeout > 0) { + tp = &timer; + init_timer(tp); + + tp->function = lld_timedout; + tp->data = (unsigned long)kioc; + tp->expires = jiffies + adp->timeout * HZ; + + add_timer(tp); + } + + /* + * Wait till the low level driver completes the ioctl. After this + * call, the ioctl either completed successfully or timedout. + */ + wait_event(wait_q, (kioc->status != -ENODATA)); + if (tp) { + del_timer_sync(tp); + } + + return kioc->status; +} + + +/** + * ioctl_done - callback from the low level driver + * + * @kioc : completed ioctl packet + */ +static void +ioctl_done(uioc_t *kioc) +{ + /* + * When the kioc returns from driver, make sure it still doesn't + * have ENODATA in status. Otherwise, driver will hang on wait_event + * forever + */ + if (kioc->status == -ENODATA) { + con_log(CL_ANN, (KERN_WARNING + "megaraid cmm: lld didn't change status!\n")); + + kioc->status = -EINVAL; + } + + wake_up(&wait_q); +} + + +/* + * lld_timedout : callback from the expired timer + * + * @ptr : ioctl packet that timed out + */ +static void +lld_timedout(unsigned long ptr) +{ + uioc_t *kioc = (uioc_t *)ptr; + + kioc->status = -ETIME; + + con_log(CL_ANN, (KERN_WARNING "megaraid cmm: ioctl timed out\n")); + + wake_up(&wait_q); +} + + +/** + * kioc_to_mimd : Converter from new back to old format + * + * @kioc : Kernel space IOCTL packet (successfully issued) + * @mimd : User space MIMD packet + */ +static int +kioc_to_mimd(uioc_t *kioc, mimd_t __user *mimd) +{ + mimd_t kmimd; + uint8_t opcode; + uint8_t subopcode; + + mbox64_t *mbox64; + mraid_passthru_t __user *upthru32; + mraid_passthru_t *kpthru32; + mcontroller_t cinfo; + mraid_hba_info_t *hinfo; + + + if (copy_from_user(&kmimd, mimd, sizeof(mimd_t))) + return (-EFAULT); + + opcode = kmimd.ui.fcs.opcode; + subopcode = kmimd.ui.fcs.subopcode; + + if (opcode == 0x82) { + switch (subopcode) { + + case MEGAIOC_QADAPINFO: + + hinfo = (mraid_hba_info_t *)(unsigned long) + kioc->buf_vaddr; + + hinfo_to_cinfo(hinfo, &cinfo); + + if (copy_to_user(kmimd.data, &cinfo, sizeof(cinfo))) + return (-EFAULT); + + return 0; + + default: + return (-EINVAL); + } + + return 0; + } + + mbox64 = (mbox64_t *)(unsigned long)kioc->cmdbuf; + + if (kioc->user_pthru) { + + upthru32 = kioc->user_pthru; + kpthru32 = kioc->pthru32; + + if (copy_to_user(&upthru32->scsistatus, + &kpthru32->scsistatus, + sizeof(uint8_t))) { + return (-EFAULT); + } + } + + if (kioc->user_data) { + if (copy_to_user(kioc->user_data, kioc->buf_vaddr, + kioc->user_data_len)) { + return (-EFAULT); + } + } + + if (copy_to_user(&mimd->mbox[17], + &mbox64->mbox32.status, sizeof(uint8_t))) { + return (-EFAULT); + } + + return 0; +} + + +/** + * hinfo_to_cinfo - Convert new format hba info into old format + * + * @hinfo : New format, more comprehensive adapter info + * @cinfo : Old format adapter info to support mimd_t apps + */ +static void +hinfo_to_cinfo(mraid_hba_info_t *hinfo, mcontroller_t *cinfo) +{ + if (!hinfo || !cinfo) + return; + + cinfo->base = hinfo->baseport; + cinfo->irq = hinfo->irq; + cinfo->numldrv = hinfo->num_ldrv; + cinfo->pcibus = hinfo->pci_bus; + cinfo->pcidev = hinfo->pci_slot; + cinfo->pcifun = PCI_FUNC(hinfo->pci_dev_fn); + cinfo->pciid = hinfo->pci_device_id; + cinfo->pcivendor = hinfo->pci_vendor_id; + cinfo->pcislot = hinfo->pci_slot; + cinfo->uid = hinfo->unique_id; +} + + +/* + * mraid_mm_register_adp - Registration routine for low level drvrs + * + * @adp : Adapter objejct + */ +int +mraid_mm_register_adp(mraid_mmadp_t *lld_adp) +{ + mraid_mmadp_t *adapter; + mbox64_t *mbox_list; + uioc_t *kioc; + uint32_t rval; + int i; + + + if (lld_adp->drvr_type != DRVRTYPE_MBOX) + return (-EINVAL); + + adapter = kmalloc(sizeof(mraid_mmadp_t), GFP_KERNEL); + + if (!adapter) { + rval = -ENOMEM; + goto memalloc_error; + } + + memset(adapter, 0, sizeof(mraid_mmadp_t)); + + adapter->unique_id = lld_adp->unique_id; + adapter->drvr_type = lld_adp->drvr_type; + adapter->drvr_data = lld_adp->drvr_data; + adapter->pdev = lld_adp->pdev; + adapter->issue_uioc = lld_adp->issue_uioc; + adapter->timeout = lld_adp->timeout; + adapter->max_kioc = lld_adp->max_kioc; + + /* + * Allocate single blocks of memory for all required kiocs, + * mailboxes and passthru structures. + */ + adapter->kioc_list = kmalloc(sizeof(uioc_t) * lld_adp->max_kioc, + GFP_KERNEL); + adapter->mbox_list = kmalloc(sizeof(mbox64_t) * lld_adp->max_kioc, + GFP_KERNEL); + adapter->pthru_dma_pool = pci_pool_create("megaraid mm pthru pool", + adapter->pdev, + sizeof(mraid_passthru_t), + 16, 0); + + if (!adapter->kioc_list || !adapter->mbox_list || + !adapter->pthru_dma_pool) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid cmm: out of memory, %s %d\n", __FUNCTION__, + __LINE__)); + + rval = (-ENOMEM); + + goto memalloc_error; + } + + /* + * Slice kioc_list and make a kioc_pool with the individiual kiocs + */ + INIT_LIST_HEAD(&adapter->kioc_pool); + spin_lock_init(&adapter->kioc_pool_lock); + sema_init(&adapter->kioc_semaphore, lld_adp->max_kioc); + + mbox_list = (mbox64_t *)adapter->mbox_list; + + for (i = 0; i < lld_adp->max_kioc; i++) { + + kioc = adapter->kioc_list + i; + kioc->cmdbuf = (uint64_t)(unsigned long)(mbox_list + i); + kioc->pthru32 = pci_pool_alloc(adapter->pthru_dma_pool, + GFP_KERNEL, &kioc->pthru32_h); + + if (!kioc->pthru32) { + + con_log(CL_ANN, (KERN_WARNING + "megaraid cmm: out of memory, %s %d\n", + __FUNCTION__, __LINE__)); + + rval = (-ENOMEM); + + goto pthru_dma_pool_error; + } + + list_add_tail(&kioc->list, &adapter->kioc_pool); + } + + // Setup the dma pools for data buffers + if ((rval = mraid_mm_setup_dma_pools(adapter)) != 0) { + goto dma_pool_error; + } + + list_add_tail(&adapter->list, &adapters_list_g); + + adapters_count_g++; + + return 0; + +dma_pool_error: + /* Do nothing */ + +pthru_dma_pool_error: + + for (i = 0; i < lld_adp->max_kioc; i++) { + kioc = adapter->kioc_list + i; + if (kioc->pthru32) { + pci_pool_free(adapter->pthru_dma_pool, kioc->pthru32, + kioc->pthru32_h); + } + } + +memalloc_error: + + if (adapter->kioc_list) + kfree(adapter->kioc_list); + + if (adapter->mbox_list) + kfree(adapter->mbox_list); + + if (adapter->pthru_dma_pool) + pci_pool_destroy(adapter->pthru_dma_pool); + + if (adapter) + kfree(adapter); + + return rval; +} + +/** + * mraid_mm_setup_dma_pools - Set up dma buffer pools per adapter + * + * @adp : Adapter softstate + * + * We maintain a pool of dma buffers per each adapter. Each pool has one + * buffer. E.g, we may have 5 dma pools - one each for 4k, 8k ... 64k buffers. + * We have just one 4k buffer in 4k pool, one 8k buffer in 8k pool etc. We + * dont' want to waste too much memory by allocating more buffers per each + * pool. + */ +static int +mraid_mm_setup_dma_pools(mraid_mmadp_t *adp) +{ + mm_dmapool_t *pool; + int bufsize; + int i; + + /* + * Create MAX_DMA_POOLS number of pools + */ + bufsize = MRAID_MM_INIT_BUFF_SIZE; + + for (i = 0; i < MAX_DMA_POOLS; i++){ + + pool = &adp->dma_pool_list[i]; + + pool->buf_size = bufsize; + spin_lock_init(&pool->lock); + + pool->handle = pci_pool_create("megaraid mm data buffer", + adp->pdev, bufsize, 16, 0); + + if (!pool->handle) { + goto dma_pool_setup_error; + } + + pool->vaddr = pci_pool_alloc(pool->handle, GFP_KERNEL, + &pool->paddr); + + if (!pool->vaddr) + goto dma_pool_setup_error; + + bufsize = bufsize * 2; + } + + return 0; + +dma_pool_setup_error: + + mraid_mm_teardown_dma_pools(adp); + return (-ENOMEM); +} + + +/* + * mraid_mm_unregister_adp - Unregister routine for low level drivers + * Assume no outstanding ioctls to llds. + * + * @unique_id : UID of the adpater + */ +int +mraid_mm_unregister_adp(uint32_t unique_id) +{ + mraid_mmadp_t *adapter; + mraid_mmadp_t *tmp; + + list_for_each_entry_safe(adapter, tmp, &adapters_list_g, list) { + + + if (adapter->unique_id == unique_id) { + + adapters_count_g--; + + list_del_init(&adapter->list); + + mraid_mm_free_adp_resources(adapter); + + kfree(adapter); + + con_log(CL_ANN, ( + "megaraid cmm: Unregistered one adapter:%#x\n", + unique_id)); + + return 0; + } + } + + return (-ENODEV); +} + +/** + * mraid_mm_free_adp_resources - Free adapter softstate + * + * @adp : Adapter softstate + */ +static void +mraid_mm_free_adp_resources(mraid_mmadp_t *adp) +{ + uioc_t *kioc; + int i; + + mraid_mm_teardown_dma_pools(adp); + + for (i = 0; i < adp->max_kioc; i++) { + + kioc = adp->kioc_list + i; + + pci_pool_free(adp->pthru_dma_pool, kioc->pthru32, + kioc->pthru32_h); + } + + kfree(adp->kioc_list); + + kfree(adp->mbox_list); + + pci_pool_destroy(adp->pthru_dma_pool); + + + return; +} + + +/** + * mraid_mm_teardown_dma_pools - Free all per adapter dma buffers + * + * @adp : Adapter softstate + */ +static void +mraid_mm_teardown_dma_pools(mraid_mmadp_t *adp) +{ + int i; + mm_dmapool_t *pool; + + for (i = 0; i < MAX_DMA_POOLS; i++) { + + pool = &adp->dma_pool_list[i]; + + if (pool->handle) { + + if (pool->vaddr) + pci_pool_free(pool->handle, pool->vaddr, + pool->paddr); + + pci_pool_destroy(pool->handle); + pool->handle = NULL; + } + } + + return; +} + +/** + * mraid_mm_init : Module entry point + */ +static int __init +mraid_mm_init(void) +{ + // Announce the driver version + con_log(CL_ANN, (KERN_INFO "megaraid cmm: %s %s\n", + LSI_COMMON_MOD_VERSION, LSI_COMMON_MOD_EXT_VERSION)); + + majorno = register_chrdev(0, "megadev", &lsi_fops); + + if (majorno < 0) { + con_log(CL_ANN, ("megaraid cmm: cannot get major\n")); + return majorno; + } + + init_waitqueue_head(&wait_q); + + INIT_LIST_HEAD(&adapters_list_g); + +#ifdef CONFIG_COMPAT + register_ioctl32_conversion(MEGAIOCCMD, mraid_mm_compat_ioctl); +#endif + + return 0; +} + + +/** + * mraid_mm_compat_ioctl : 32bit to 64bit ioctl conversion routine + */ +#ifdef CONFIG_COMPAT +static int +mraid_mm_compat_ioctl(unsigned int fd, unsigned int cmd, + unsigned long arg, struct file *filep) +{ + struct inode *inode = filep->f_dentry->d_inode; + + return mraid_mm_ioctl(inode, filep, cmd, arg); +} +#endif + +/** + * mraid_mm_exit : Module exit point + */ +static void __exit +mraid_mm_exit(void) +{ + con_log(CL_DLEVEL1 , ("exiting common mod\n")); + + unregister_chrdev(majorno, "megadev"); + unregister_ioctl32_conversion(MEGAIOCCMD); +} + +module_init(mraid_mm_init); +module_exit(mraid_mm_exit); + +/* vi: set ts=8 sw=8 tw=78: */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/scsi/megaraid/megaraid_mm.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,102 @@ +/* + * + * Linux MegaRAID device driver + * + * Copyright (c) 2003-2004 LSI Logic Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * FILE : megaraid_mm.h + */ + +#ifndef MEGARAID_MM_H +#define MEGARAID_MM_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mbox_defs.h" +#include "megaraid_ioctl.h" + + +#define LSI_COMMON_MOD_VERSION "2.20.2.0" +#define LSI_COMMON_MOD_EXT_VERSION \ + "(Release Date: Thu Aug 19 09:58:33 EDT 2004)" + + +#define LSI_DBGLVL dbglevel + +// The smallest dma pool +#define MRAID_MM_INIT_BUFF_SIZE 4096 + +/** + * mimd_t : Old style ioctl packet structure (deprecated) + * + * @inlen : + * @outlen : + * @fca : + * @opcode : + * @subopcode : + * @adapno : + * @buffer : + * @pad : + * @length : + * @mbox : + * @pthru : + * @data : + * @pad : + * + * Note : This structure is DEPRECATED. New applications must use + * : uioc_t structure instead. All new hba drivers use the new + * : format. If we get this mimd packet, we will convert it into + * : new uioc_t format and send it to the hba drivers. + */ + +typedef struct mimd { + + uint32_t inlen; + uint32_t outlen; + + union { + uint8_t fca[16]; + struct { + uint8_t opcode; + uint8_t subopcode; + uint16_t adapno; +#if BITS_PER_LONG == 32 + uint8_t __user *buffer; + uint8_t pad[4]; +#endif +#if BITS_PER_LONG == 64 + uint8_t __user *buffer; +#endif + uint32_t length; + } __attribute__ ((packed)) fcs; + } __attribute__ ((packed)) ui; + + uint8_t mbox[18]; /* 16 bytes + 2 status bytes */ + mraid_passthru_t pthru; + +#if BITS_PER_LONG == 32 + char __user *data; /* buffer <= 4096 for 0x80 commands */ + char pad[4]; +#endif +#if BITS_PER_LONG == 64 + char __user *data; +#endif + +} __attribute__ ((packed))mimd_t; + +#endif // MEGARAID_MM_H + +// vi: set ts=8 sw=8 tw=78: --- linux-2.6.9-rc1/drivers/scsi/NCR5380.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/drivers/scsi/NCR5380.c 2004-09-02 20:51:52.000000000 -0700 @@ -310,12 +310,8 @@ * possible) function may be used. Before the specific driver initialization * code finishes, NCR5380_print_options should be called. */ - static int do_abort(struct Scsi_Host *host); static void do_reset(struct Scsi_Host *host); -static struct NCR5380_hostdata *first_host = NULL; -static struct NCR5380_hostdata *last_host = NULL; -static struct timer_list usleep_timer; /* * initialize_SCp - init the scsi pointer field @@ -533,9 +529,6 @@ static void NCR5380_print_phase(struct S #define USLEEP_WAITLONG USLEEP_SLEEP #endif -static struct Scsi_Host *expires_first = NULL; -static spinlock_t timer_lock; /* Guards expires list */ - /* * Function : int should_disconnect (unsigned char cmd) * @@ -578,90 +571,10 @@ static int should_disconnect(unsigned ch } } -/* - * Assumes instance->time_expires has been set in higher level code. - * We should move to a timer per host - * - * Locks: Takes the timer queue lock - */ - -static int NCR5380_set_timer(struct Scsi_Host *instance) -{ - struct Scsi_Host *tmp, **prev; - unsigned long flags; - - if (((struct NCR5380_hostdata *) (instance->hostdata))->next_timer) { - return -1; - } - - spin_lock_irqsave(&timer_lock, flags); - for (prev = &expires_first, tmp = expires_first; tmp; prev = &(((struct NCR5380_hostdata *) tmp->hostdata)->next_timer), tmp = ((struct NCR5380_hostdata *) tmp->hostdata)->next_timer) - if (((struct NCR5380_hostdata *) instance->hostdata)->time_expires < ((struct NCR5380_hostdata *) tmp->hostdata)->time_expires) - break; - - ((struct NCR5380_hostdata *) instance->hostdata)->next_timer = tmp; - *prev = instance; - - mod_timer(&usleep_timer, ((struct NCR5380_hostdata *) expires_first->hostdata)->time_expires); - - spin_unlock_irqrestore(&timer_lock, flags); - return 0; -} - -/** - * NCR5380_timer_fn - handle polled timeouts - * @unused: unused - * - * Walk the list of controllers, find which controllers have exceeded - * their expiry timeout and then schedule the processing co-routine to - * do the real work. - * - * Doing something about unwanted reentrancy here might be useful - * - * Locks: disables irqs, takes and frees the timer lock - */ - -static void NCR5380_timer_fn(unsigned long unused) -{ - struct Scsi_Host *instance; - struct NCR5380_hostdata *hostdata; - unsigned long flags; - - spin_lock_irqsave(&timer_lock, flags); - for (; expires_first && time_before_eq(((struct NCR5380_hostdata *) expires_first->hostdata)->time_expires, jiffies);) - { - hostdata = (struct NCR5380_hostdata *) expires_first->hostdata; - schedule_work(&hostdata->coroutine); - instance = hostdata->next_timer; - hostdata->next_timer = NULL; - hostdata->time_expires = 0; - expires_first = instance; - } - - del_timer(&usleep_timer); - if (expires_first) { - usleep_timer.expires = ((struct NCR5380_hostdata *) expires_first->hostdata)->time_expires; - add_timer(&usleep_timer); - } - spin_unlock_irqrestore(&timer_lock, flags); -} - -/** - * NCR5380_all_init - global setup - * - * Set up the global values and timers needed by the NCR5380 driver - */ - -static inline void NCR5380_all_init(void) +static void NCR5380_set_timer(struct NCR5380_hostdata *hostdata, unsigned long timeout) { - static int done = 0; - if (!done) { - dprintk(NDEBUG_INIT, ("scsi : NCR5380_all_init()\n")); - done = 1; - init_timer(&usleep_timer); - spin_lock_init(&timer_lock); - usleep_timer.function = NCR5380_timer_fn; - } + hostdata->time_expires = jiffies + timeout; + schedule_delayed_work(&hostdata->coroutine, hostdata->time_expires); } @@ -788,22 +701,6 @@ static void __init NCR5380_print_options } /** - * NCR5380_coroutine_running - coroutine status - * @instance: controller to check - * - * Return true if the co-routine for this controller is running - * or scheduled to run - * - * FIXME: this test function belongs in the workqueue code! - */ - -static int NCR5380_coroutine_running(struct Scsi_Host *instance) -{ - struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)instance->hostdata; - return test_bit(0, &hostdata->coroutine.pending); -} - -/** * NCR5380_print_status - dump controller info * @instance: controller to dump * @@ -819,8 +716,6 @@ static void NCR5380_print_status(struct char *start; int len; - printk("NCR5380 : coroutine is%s running.\n", NCR5380_coroutine_running(instance)? "" : "n't"); - NCR5380_dprint(NDEBUG_ANY, instance); NCR5380_dprint_phase(NDEBUG_ANY, instance); @@ -900,7 +795,6 @@ int NCR5380_proc_info(struct Scsi_Host * SPRINTF("Highwater I/O busy_spin_counts -- write: %d read: %d\n", pas_wmaxi, pas_maxi); #endif spin_lock_irq(instance->host_lock); - SPRINTF("NCR5380 : coroutine is%s running.\n", NCR5380_coroutine_running(instance) ? "" : "n't"); if (!hostdata->connected) SPRINTF("scsi%d: no currently connected command\n", instance->host_no); else @@ -912,7 +806,6 @@ int NCR5380_proc_info(struct Scsi_Host * SPRINTF("scsi%d: disconnected_queue\n", instance->host_no); for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble) pos = lprint_Scsi_Cmnd(ptr, pos, buffer, length); - spin_unlock_irq(instance->host_lock); *start = buffer; @@ -964,7 +857,7 @@ static char *lprint_opcode(int opcode, c * Locks: interrupts must be enabled when we are called */ -static int __init NCR5380_init(struct Scsi_Host *instance, int flags) +static int __devinit NCR5380_init(struct Scsi_Host *instance, int flags) { NCR5380_local_declare(); int i, pass; @@ -984,7 +877,6 @@ static int __init NCR5380_init(struct Sc #endif NCR5380_setup(instance); - NCR5380_all_init(); hostdata->aborted = 0; hostdata->id_mask = 1 << instance->this_id; @@ -1021,18 +913,8 @@ static int __init NCR5380_init(struct Sc else hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT | flags; - hostdata->next = NULL; - - if (!first_host) - first_host = hostdata; - else - last_host->next = hostdata; - - last_host = hostdata; - hostdata->host = instance; hostdata->time_expires = 0; - hostdata->next_timer = NULL; #ifndef AUTOSENSE if ((instance->cmd_per_lun > 1) || instance->can_queue > 1) @@ -1089,6 +971,19 @@ static int __init NCR5380_init(struct Sc } /** + * NCR5380_exit - remove an NCR5380 + * @instance: adapter to remove + */ + +static void __devexit NCR5380_exit(struct Scsi_Host *instance) +{ + struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata; + + cancel_delayed_work(&hostdata->coroutine); + flush_scheduled_work(); +} + +/** * NCR5380_queue_command - queue a command * @cmd: SCSI command * @done: completion handler @@ -1169,6 +1064,7 @@ static int NCR5380_queue_command(Scsi_Cm return 0; } + /** * NCR5380_main - NCR state machines * @@ -1184,28 +1080,11 @@ static int NCR5380_queue_command(Scsi_Cm static void NCR5380_main(void *p) { struct NCR5380_hostdata *hostdata = p; + struct Scsi_Host *instance = hostdata->host; Scsi_Cmnd *tmp, *prev; - struct Scsi_Host *instance; int done; - unsigned long flags = 0; - /* - * We run (with interrupts disabled) until we're sure that none of - * the host adapters have anything that can be done, at which point - * we can exit - * - * Interrupts are enabled before doing various other internal - * instructions, after we've decided that we need to run through - * the loop again. - * - * this should prevent any race conditions. - */ - - instance = hostdata->host; - - if(instance->irq != SCSI_IRQ_NONE) - spin_lock_irqsave(instance->host_lock, flags); - + spin_lock_irq(instance->host_lock); do { /* Lock held here */ done = 1; @@ -1286,8 +1165,7 @@ static void NCR5380_main(void *p) LIST(tmp, hostdata->issue_queue); tmp->host_scribble = (unsigned char *) hostdata->issue_queue; hostdata->issue_queue = tmp; - hostdata->time_expires = jiffies + USLEEP_WAITLONG; - NCR5380_set_timer(instance); + NCR5380_set_timer(hostdata, USLEEP_WAITLONG); } } /* if hostdata->selecting */ if (hostdata->connected @@ -1304,8 +1182,7 @@ static void NCR5380_main(void *p) break; } while (!done); - if(instance->irq != SCSI_IRQ_NONE) - spin_unlock_irqrestore(instance->host_lock, flags); + spin_unlock_irq(instance->host_lock); } #ifndef DONT_USE_INTR @@ -1330,12 +1207,13 @@ static irqreturn_t NCR5380_intr(int irq, struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata; int done; unsigned char basr; + unsigned long flags; dprintk(NDEBUG_INTR, ("scsi : NCR5380 irq %d triggered\n", irq)); do { done = 1; - spin_lock_irq(instance->host_lock); + spin_lock_irqsave(instance->host_lock, flags); /* Look for pending interrupts */ NCR5380_setup(instance); basr = NCR5380_read(BUS_AND_STATUS_REG); @@ -1386,7 +1264,7 @@ static irqreturn_t NCR5380_intr(int irq, #endif } } /* if BASR_IRQ */ - spin_unlock_irq(instance->host_lock); + spin_unlock_irqrestore(instance->host_lock, flags); if(!done) schedule_work(&hostdata->coroutine); } while (!done); @@ -1469,12 +1347,8 @@ static int NCR5380_select(struct Scsi_Ho int err; NCR5380_setup(instance); - if (hostdata->selecting) { - if(instance->irq != SCSI_IRQ_NONE) - spin_unlock_irq(instance->host_lock); - goto part2; /* RvC: sorry prof. Dijkstra, but it keeps the - rest of the code nearly the same */ - } + if (hostdata->selecting) + goto part2; hostdata->restart_select = 0; @@ -1495,16 +1369,12 @@ static int NCR5380_select(struct Scsi_Ho NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask); NCR5380_write(MODE_REG, MR_ARBITRATE); - if(instance->irq != SCSI_IRQ_NONE) - spin_unlock_irq(instance->host_lock); /* We can be relaxed here, interrupts are on, we are in workqueue context, the birds are singing in the trees */ - + spin_unlock_irq(instance->host_lock); err = NCR5380_poll_politely(instance, INITIATOR_COMMAND_REG, ICR_ARBITRATION_PROGRESS, ICR_ARBITRATION_PROGRESS, 5*HZ); - if(instance->irq != SCSI_IRQ_NONE) - spin_lock_irq(instance->host_lock); - + spin_lock_irq(instance->host_lock); if (err < 0) { printk(KERN_DEBUG "scsi: arbitration timeout at %d\n", __LINE__); NCR5380_write(MODE_REG, MR_BASE); @@ -1628,8 +1498,7 @@ part2: if (!value && (hostdata->select_time < HZ/4)) { /* RvC: we still must wait for a device response */ hostdata->select_time++; /* after 25 ticks the device has failed */ - hostdata->time_expires = jiffies + 1; - NCR5380_set_timer(instance); + NCR5380_set_timer(hostdata, 1); return 0; /* RvC: we return here with hostdata->selecting set, to go to sleep */ } @@ -1638,8 +1507,6 @@ part2: waiting period */ if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) == (SR_SEL | SR_IO)) { NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE); - if(instance->irq != SCSI_IRQ_NONE) - spin_lock_irq(instance->host_lock); NCR5380_reselect(instance); printk("scsi%d : reselection after won arbitration?\n", instance->host_no); NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); @@ -1665,8 +1532,6 @@ part2: NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); return -1; } - if(instance->irq != SCSI_IRQ_NONE) - spin_lock_irq(instance->host_lock); cmd->result = DID_BAD_TARGET << 16; collect_stats(hostdata, cmd); cmd->scsi_done(cmd); @@ -1693,11 +1558,13 @@ part2: */ /* Wait for start of REQ/ACK handshake */ - + + spin_unlock_irq(instance->host_lock); err = NCR5380_poll_politely(instance, STATUS_REG, SR_REQ, SR_REQ, HZ); + spin_lock_irq(instance->host_lock); - if(err) - { printk(KERN_ERR "scsi%d: timeout at NCR5380.c:%d\n", instance->host_no, __LINE__); + if(err) { + printk(KERN_ERR "scsi%d: timeout at NCR5380.c:%d\n", instance->host_no, __LINE__); NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask); goto failed; } @@ -1705,9 +1572,6 @@ part2: dprintk(NDEBUG_SELECTION, ("scsi%d : target %d selected, going into MESSAGE OUT phase.\n", instance->host_no, cmd->device->id)); tmp[0] = IDENTIFY(((instance->irq == SCSI_IRQ_NONE) ? 0 : 1), cmd->device->lun); - if(instance->irq != SCSI_IRQ_NONE) - spin_lock_irq(instance->host_lock); - len = 1; cmd->tag = 0; @@ -1720,15 +1584,14 @@ part2: hostdata->connected = cmd; hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun); - initialize_SCp(cmd); - + if (cmd->SCp.ptr != (char *)cmd->sense_buffer) { + initialize_SCp(cmd); + } return 0; /* Selection failed */ failed: - if(instance->irq != SCSI_IRQ_NONE) - spin_lock_irq(instance->host_lock); return -1; } @@ -1804,8 +1667,7 @@ static int NCR5380_transfer_pio(struct S while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ) && !break_allowed); if (!(tmp & SR_REQ)) { /* timeout condition */ - hostdata->time_expires = jiffies + USLEEP_SLEEP; - NCR5380_set_timer(instance); + NCR5380_set_timer(hostdata, USLEEP_SLEEP); break; } @@ -2643,9 +2505,8 @@ static void NCR5380_information_transfer */ NCR5380_transfer_pio(instance, &phase, &len, &data); if (!cmd->device->disconnect && should_disconnect(cmd->cmnd[0])) { - hostdata->time_expires = jiffies + USLEEP_SLEEP; + NCR5380_set_timer(hostdata, USLEEP_SLEEP); dprintk(NDEBUG_USLEEP, ("scsi%d : issued command, sleeping until %ul\n", instance->host_no, hostdata->time_expires)); - NCR5380_set_timer(instance); return; } break; @@ -2664,9 +2525,8 @@ static void NCR5380_information_transfer /* RvC: go to sleep if polling time expired */ if (!cmd->device->disconnect && time_after_eq(jiffies, poll_time)) { - hostdata->time_expires = jiffies + USLEEP_SLEEP; + NCR5380_set_timer(hostdata, USLEEP_SLEEP); dprintk(NDEBUG_USLEEP, ("scsi%d : poll timed out, sleeping until %ul\n", instance->host_no, hostdata->time_expires)); - NCR5380_set_timer(instance); return; } } --- linux-2.6.9-rc1/drivers/scsi/NCR5380.h 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/scsi/NCR5380.h 2004-09-02 20:51:52.000000000 -0700 @@ -251,7 +251,6 @@ struct NCR5380_hostdata { NCR5380_implementation_fields; /* implementation specific */ struct Scsi_Host *host; /* Host backpointer */ - struct NCR5380_hostdata *next; /* Next in our hot chain */ unsigned char id_mask, id_higher_mask; /* 1 << id, all bits greater */ unsigned char targets_present; /* targets we have connected to, so we can call a select @@ -270,7 +269,6 @@ struct NCR5380_hostdata { volatile unsigned aborted:1; /* flag, says aborted */ int flags; unsigned long time_expires; /* in jiffies, set prior to sleeping */ - struct Scsi_Host *next_timer; int select_time; /* timer in select for target response */ volatile Scsi_Cmnd *selecting; struct work_struct coroutine; /* our co-routine */ @@ -295,6 +293,7 @@ struct NCR5380_hostdata { static int NCR5380_probe_irq(struct Scsi_Host *instance, int possible); #endif static int NCR5380_init(struct Scsi_Host *instance, int flags); +static void NCR5380_exit(struct Scsi_Host *instance); static void NCR5380_information_transfer(struct Scsi_Host *instance); #ifndef DONT_USE_INTR static irqreturn_t NCR5380_intr(int irq, void *dev_id, struct pt_regs *regs); --- linux-2.6.9-rc1/drivers/scsi/NCR53c406a.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/scsi/NCR53c406a.c 2004-09-02 20:51:52.000000000 -0700 @@ -606,22 +606,24 @@ static int NCR53c406a_release(struct Scs } /* called from init/main.c */ -static void __init NCR53c406a_setup(char *str, int *ints) +static int __init NCR53c406a_setup(char *str) { static size_t setup_idx = 0; size_t i; + int ints[4]; DEB(printk("NCR53c406a: Setup called\n"); ); if (setup_idx >= PORT_COUNT - 1) { printk("NCR53c406a: Setup called too many times. Bad LILO params?\n"); - return; + return 0; } + get_options(str, 4, ints); if (ints[0] < 1 || ints[0] > 3) { printk("NCR53c406a: Malformed command line\n"); printk("NCR53c406a: Usage: ncr53c406a=[,[,]]\n"); - return; + return 0; } for (i = 0; i < PORT_COUNT && !port_base; i++) if (ports[i] == ints[1]) { @@ -631,7 +633,7 @@ static void __init NCR53c406a_setup(char } if (!port_base) { printk("NCR53c406a: Invalid PORTBASE 0x%x specified\n", ints[1]); - return; + return 0; } if (ints[0] > 1) { @@ -654,6 +656,7 @@ static void __init NCR53c406a_setup(char fast_pio = ints[3]; DEB(printk("NCR53c406a: port_base=0x%x, irq=%d, fast_pio=%d\n", port_base, irq_level, fast_pio);) + return 1; } __setup("ncr53c406a=", NCR53c406a_setup); --- linux-2.6.9-rc1/drivers/scsi/NCR_Q720.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/scsi/NCR_Q720.c 2004-09-02 20:51:52.000000000 -0700 @@ -216,7 +216,21 @@ NCR_Q720_probe(struct device *dev) goto out_free; } - mem_base = (__u32)ioremap(base_addr, mem_size); + if (dma_declare_coherent_memory(dev, base_addr, base_addr, + mem_size, DMA_MEMORY_MAP) + != DMA_MEMORY_MAP) { + printk(KERN_ERR "NCR_Q720: DMA declare memory failed\n"); + goto out_release_region; + } + + /* The first 1k of the memory buffer is a memory map of the registers + */ + mem_base = (__u32)dma_mark_declared_memory_occupied(dev, base_addr, + 1024); + if (IS_ERR((void *)mem_base)) { + printk("NCR_Q720 failed to reserve memory mapped region\n"); + goto out_release; + } /* now also enable accesses in asr 2 */ asr2 = inb(io_base + 0x0a); @@ -296,7 +310,8 @@ NCR_Q720_probe(struct device *dev) return 0; out_release: - iounmap((void *)mem_base); + dma_release_declared_memory(dev); + out_release_region: release_mem_region(base_addr, mem_size); out_free: kfree(p); @@ -321,7 +336,7 @@ NCR_Q720_remove(struct device *dev) if(p->hosts[i]) NCR_Q720_remove_one(p->hosts[i]); - iounmap((void *)p->mem_base); + dma_release_declared_memory(dev); release_mem_region(p->phys_mem_base, p->mem_size); free_irq(p->irq, p); kfree(p); --- linux-2.6.9-rc1/drivers/scsi/nsp32.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/drivers/scsi/nsp32.c 2004-09-02 20:51:52.000000000 -0700 @@ -267,8 +267,8 @@ static void nsp32_prom_stop ( static int nsp32_prom_read (nsp32_hw_data *, int); static int nsp32_prom_read_bit (nsp32_hw_data *); static void nsp32_prom_write_bit(nsp32_hw_data *, int); -static inline void nsp32_prom_set (nsp32_hw_data *, int, int); -static inline int nsp32_prom_get (nsp32_hw_data *, int); +static void nsp32_prom_set (nsp32_hw_data *, int, int); +static int nsp32_prom_get (nsp32_hw_data *, int); /* debug/warning/info message */ static void nsp32_message (const char *, int, char *, char *, ...); @@ -3342,7 +3342,7 @@ static int nsp32_prom_read(nsp32_hw_data return val; } -static inline void nsp32_prom_set(nsp32_hw_data *data, int bit, int val) +static void nsp32_prom_set(nsp32_hw_data *data, int bit, int val) { int base = data->BaseAddress; int tmp; @@ -3360,7 +3360,7 @@ static inline void nsp32_prom_set(nsp32_ udelay(10); } -static inline int nsp32_prom_get(nsp32_hw_data *data, int bit) +static int nsp32_prom_get(nsp32_hw_data *data, int bit) { int base = data->BaseAddress; int tmp, ret; --- linux-2.6.9-rc1/drivers/scsi/pas16.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/drivers/scsi/pas16.c 2004-09-02 20:51:52.000000000 -0700 @@ -605,6 +605,7 @@ static int pas16_release(struct Scsi_Hos { if (shost->irq) free_irq(shost->irq, NULL); + NCR5380_exit(shost); if (shost->dma_channel != 0xff) free_dma(shost->dma_channel); if (shost->io_port && shost->n_io_port) --- linux-2.6.9-rc1/drivers/scsi/qla1280.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/scsi/qla1280.c 2004-09-02 20:51:52.000000000 -0700 @@ -4,7 +4,7 @@ * QLogic QLA1280 (Ultra2) and QLA12160 (Ultra3) SCSI driver * Copyright (C) 2000 Qlogic Corporation (www.qlogic.com) * Copyright (C) 2001-2004 Jes Sorensen, Wild Open Source Inc. -* Copyright (C) 2003 Christoph Hellwig +* Copyright (C) 2003-2004 Christoph Hellwig * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -17,9 +17,12 @@ * General Public License for more details. * ******************************************************************************/ -#define QLA1280_VERSION "3.24.3" +#define QLA1280_VERSION "3.24.4" /***************************************************************************** Revision History: + Rev 3.24.4 June 7, 2004 Christoph Hellwig + - restructure firmware loading, cleanup initialization code + - prepare support for ISP1020/1040 chips Rev 3.24.3 January 19, 2004, Jes Sorensen - Handle PCI DMA mask settings correctly - Correct order of error handling in probe_one, free_irq should not @@ -485,6 +488,14 @@ static inline void scsi_host_put(struct #define ia64_platform_is(foo) (!strcmp(x, platform_name)) #endif + +#define IS_ISP1040(ha) (ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP1020) +#define IS_ISP1x40(ha) (ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP1020 || \ + ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP1240) +#define IS_ISP1x160(ha) (ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP10160 || \ + ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP12160) + + static int qla1280_probe_one(struct pci_dev *, const struct pci_device_id *); static void qla1280_remove_one(struct pci_dev *); @@ -501,9 +512,7 @@ static int qla1280_setup(char *s) __init /* * QLogic ISP1280 Hardware Support Function Prototypes. */ -static int qla1280_isp_firmware(struct scsi_qla_host *); -static int qla1280_chip_diag(struct scsi_qla_host *); -static int qla1280_setup_chip(struct scsi_qla_host *); +static int qla1280_load_firmware(struct scsi_qla_host *); static int qla1280_init_rings(struct scsi_qla_host *); static int qla1280_nvram_config(struct scsi_qla_host *); static int qla1280_mailbox_command(struct scsi_qla_host *, @@ -1384,16 +1393,10 @@ qla1280_set_target_parameters(struct scs uint8_t mr; uint16_t mb[MAILBOX_REGISTER_COUNT]; struct nvram *nv; - int is1x160, status; + int status; nv = &ha->nvram; - if (ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP12160 || - ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP10160) - is1x160 = 1; - else - is1x160 = 0; - mr = BIT_3 | BIT_2 | BIT_1 | BIT_0; /* Set Target Parameters. */ @@ -1403,17 +1406,16 @@ qla1280_set_target_parameters(struct scs mb[2] = (nv->bus[bus].target[target].parameter.c << 8); - if (is1x160) - mb[3] = nv->bus[bus].target[target].flags.flags1x160.sync_offset << 8; - else - mb[3] = nv->bus[bus].target[target].flags.flags1x80.sync_offset << 8; - mb[3] |= nv->bus[bus].target[target].sync_period; - - if (is1x160) { + if (IS_ISP1x160(ha)) { mb[2] |= nv->bus[bus].target[target].ppr_1x160.flags.enable_ppr << 5; - mb[6] = nv->bus[bus].target[target].ppr_1x160.flags.ppr_options << 8; - mb[6] |= nv->bus[bus].target[target].ppr_1x160.flags.ppr_bus_width; + mb[3] = (nv->bus[bus].target[target].flags.flags1x160.sync_offset << 8) | + nv->bus[bus].target[target].sync_period; + mb[6] = (nv->bus[bus].target[target].ppr_1x160.flags.ppr_options << 8) | + nv->bus[bus].target[target].ppr_1x160.flags.ppr_bus_width; mr |= BIT_6; + } else { + mb[3] = (nv->bus[bus].target[target].flags.flags1x80.sync_offset << 8) | + nv->bus[bus].target[target].sync_period; } status = qla1280_mailbox_command(ha, mr, &mb[0]); @@ -1476,8 +1478,7 @@ qla1280_slave_configure(struct scsi_devi (driver_setup.wide_mask && (~driver_setup.wide_mask & (1 << target)))) nv->bus[bus].target[target].parameter.f.enable_wide = 0; - if (ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP12160 || - ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP10160) { + if (IS_ISP1x160(ha)) { if (driver_setup.no_ppr || (driver_setup.ppr_mask && (~driver_setup.ppr_mask & (1 << target)))) @@ -1802,17 +1803,8 @@ qla1280_initialize_adapter(struct scsi_q */ spin_lock_irqsave(HOST_LOCK, flags); #endif - /* If firmware needs to be loaded */ - if (qla1280_isp_firmware(ha)) { - if (!(status = qla1280_chip_diag(ha))) { - status = qla1280_setup_chip(ha); - } - } else { - printk(KERN_ERR "scsi(%li): isp_firmware() failed!\n", - ha->host_no); - status = 1; - } + status = qla1280_load_firmware(ha); if (status) { printk(KERN_ERR "scsi(%li): initialize: pci probe failed!\n", ha->host_no); @@ -1823,36 +1815,24 @@ qla1280_initialize_adapter(struct scsi_q dprintk(1, "scsi(%ld): Configure NVRAM parameters\n", ha->host_no); qla1280_nvram_config(ha); - if (!ha->flags.disable_host_adapter && !qla1280_init_rings(ha)) { - /* Issue SCSI reset. */ - /* dg 03/13 if we can't reset twice then bus is dead */ - for (bus = 0; bus < ha->ports; bus++) { - if (!ha->bus_settings[bus].disable_scsi_reset){ - if (qla1280_bus_reset(ha, bus)) { - if (qla1280_bus_reset(ha, bus)) { - ha->bus_settings[bus].scsi_bus_dead = 1; - } - } - } - } + if (ha->flags.disable_host_adapter) { + status = 1; + goto out; + } - /* - * qla1280_bus_reset() will take care of issueing markers, - * no need to do that here as well! - */ -#if 0 - /* Issue marker command. */ - ha->flags.reset_marker = 0; - for (bus = 0; bus < ha->ports; bus++) { - ha->bus_settings[bus].reset_marker = 0; - qla1280_marker(ha, bus, 0, 0, MK_SYNC_ALL); - } -#endif + status = qla1280_init_rings(ha); + if (status) + goto out; - ha->flags.online = 1; - } else - status = 1; + /* Issue SCSI reset, if we can't reset twice then bus is dead */ + for (bus = 0; bus < ha->ports; bus++) { + if (!ha->bus_settings[bus].disable_scsi_reset && + qla1280_bus_reset(ha, bus) && + qla1280_bus_reset(ha, bus)) + ha->bus_settings[bus].scsi_bus_dead = 1; + } + ha->flags.online = 1; out: #if LINUX_VERSION_CODE >= 0x020500 spin_unlock_irqrestore(HOST_LOCK, flags); @@ -1945,13 +1925,13 @@ qla1280_chip_diag(struct scsi_qla_host * int status = 0; int cnt; uint16_t data; - dprintk(3, "qla1280_chip_diag: testing device at 0x%p \n", ®->id_l); dprintk(1, "scsi(%ld): Verifying chip\n", ha->host_no); /* Soft reset chip and wait for it to finish. */ WRT_REG_WORD(®->ictrl, ISP_RESET); + /* * We can't do a traditional PCI write flush here by reading * back the register. The card will not respond once the reset @@ -1969,145 +1949,138 @@ qla1280_chip_diag(struct scsi_qla_host * data = RD_REG_WORD(®->ictrl); } - if (cnt) { - /* Reset register cleared by chip reset. */ - dprintk(3, "qla1280_chip_diag: reset register cleared by " - "chip reset\n"); + if (!cnt) + goto fail; - WRT_REG_WORD(®->cfg_1, 0); + /* Reset register cleared by chip reset. */ + dprintk(3, "qla1280_chip_diag: reset register cleared by chip reset\n"); - /* Reset RISC and disable BIOS which - allows RISC to execute out of RAM. */ -#if 0 - WRT_REG_WORD(®->host_cmd, HC_RESET_RISC); - RD_REG_WORD(®->id_l); /* Flush PCI write */ - WRT_REG_WORD(®->host_cmd, HC_RELEASE_RISC); - RD_REG_WORD(®->id_l); /* Flush PCI write */ - WRT_REG_WORD(®->host_cmd, HC_DISABLE_BIOS); -#else - WRT_REG_WORD(®->host_cmd, HC_RESET_RISC | - HC_RELEASE_RISC | HC_DISABLE_BIOS); -#endif - RD_REG_WORD(®->id_l); /* Flush PCI write */ - data = qla1280_debounce_register(®->mailbox0); - /* - * I *LOVE* this code! - */ - for (cnt = 1000000; cnt && data == MBS_BUSY; cnt--) { - udelay(5); - data = RD_REG_WORD(®->mailbox0); - } + WRT_REG_WORD(®->cfg_1, 0); - if (cnt) { - /* Check product ID of chip */ - dprintk(3, "qla1280_chip_diag: Checking product " - "ID of chip\n"); - - if (RD_REG_WORD(®->mailbox1) != PROD_ID_1 || - (RD_REG_WORD(®->mailbox2) != PROD_ID_2 && - RD_REG_WORD(®->mailbox2) != PROD_ID_2a) || - RD_REG_WORD(®->mailbox3) != PROD_ID_3 || - RD_REG_WORD(®->mailbox4) != PROD_ID_4) { - printk(KERN_INFO "qla1280: Wrong product ID = " - "0x%x,0x%x,0x%x,0x%x\n", - RD_REG_WORD(®->mailbox1), - RD_REG_WORD(®->mailbox2), - RD_REG_WORD(®->mailbox3), - RD_REG_WORD(®->mailbox4)); - status = 1; - } else { - /* - * Enable ints early!!! - */ - qla1280_enable_intrs(ha); - - dprintk(1, "qla1280_chip_diag: Checking " - "mailboxes of chip\n"); - /* Wrap Incoming Mailboxes Test. */ - mb[0] = MBC_MAILBOX_REGISTER_TEST; - mb[1] = 0xAAAA; - mb[2] = 0x5555; - mb[3] = 0xAA55; - mb[4] = 0x55AA; - mb[5] = 0xA5A5; - mb[6] = 0x5A5A; - mb[7] = 0x2525; - if (!(status = qla1280_mailbox_command(ha, - 0xff, - &mb - [0]))) { - if (mb[1] != 0xAAAA || - mb[2] != 0x5555 || - mb[3] != 0xAA55 || - mb[4] != 0x55AA || - mb[5] != 0xA5A5 || - mb[6] != 0x5A5A || - mb[7] != 0x2525) { - status = 1; - printk(KERN_INFO "qla1280: " - "Failed mbox check\n"); - } - } - } - } else - status = 1; - } else - status = 1; + /* Reset RISC and disable BIOS which + allows RISC to execute out of RAM. */ + WRT_REG_WORD(®->host_cmd, HC_RESET_RISC | + HC_RELEASE_RISC | HC_DISABLE_BIOS); + + RD_REG_WORD(®->id_l); /* Flush PCI write */ + data = qla1280_debounce_register(®->mailbox0); + + /* + * I *LOVE* this code! + */ + for (cnt = 1000000; cnt && data == MBS_BUSY; cnt--) { + udelay(5); + data = RD_REG_WORD(®->mailbox0); + } + + if (!cnt) + goto fail; + + /* Check product ID of chip */ + dprintk(3, "qla1280_chip_diag: Checking product ID of chip\n"); + + if (RD_REG_WORD(®->mailbox1) != PROD_ID_1 || + (RD_REG_WORD(®->mailbox2) != PROD_ID_2 && + RD_REG_WORD(®->mailbox2) != PROD_ID_2a) || + RD_REG_WORD(®->mailbox3) != PROD_ID_3 || + RD_REG_WORD(®->mailbox4) != PROD_ID_4) { + printk(KERN_INFO "qla1280: Wrong product ID = " + "0x%x,0x%x,0x%x,0x%x\n", + RD_REG_WORD(®->mailbox1), + RD_REG_WORD(®->mailbox2), + RD_REG_WORD(®->mailbox3), + RD_REG_WORD(®->mailbox4)); + goto fail; + } + /* + * Enable ints early!!! + */ + qla1280_enable_intrs(ha); + + dprintk(1, "qla1280_chip_diag: Checking mailboxes of chip\n"); + /* Wrap Incoming Mailboxes Test. */ + mb[0] = MBC_MAILBOX_REGISTER_TEST; + mb[1] = 0xAAAA; + mb[2] = 0x5555; + mb[3] = 0xAA55; + mb[4] = 0x55AA; + mb[5] = 0xA5A5; + mb[6] = 0x5A5A; + mb[7] = 0x2525; + + status = qla1280_mailbox_command(ha, 0xff, mb); if (status) - dprintk(2, "qla1280_chip_diag: **** FAILED ****\n"); - else - dprintk(3, "qla1280_chip_diag: exiting normally\n"); + goto fail; + if (mb[1] != 0xAAAA || mb[2] != 0x5555 || mb[3] != 0xAA55 || + mb[4] != 0x55AA || mb[5] != 0xA5A5 || mb[6] != 0x5A5A || + mb[7] != 0x2525) { + printk(KERN_INFO "qla1280: Failed mbox check\n"); + goto fail; + } + + dprintk(3, "qla1280_chip_diag: exiting normally\n"); + return 0; + fail: + dprintk(2, "qla1280_chip_diag: **** FAILED ****\n"); return status; } -/* - * Setup chip - * Load and start RISC firmware. - * - * Input: - * ha = adapter block pointer. - * - * Returns: - * 0 = success. - */ -#define DUMP_IT_BACK 0 /* for debug of RISC loading */ static int -qla1280_setup_chip(struct scsi_qla_host *ha) +qla1280_load_firmware_pio(struct scsi_qla_host *ha) { - int status = 0; - uint16_t risc_address; - uint16_t *risc_code_address; - int risc_code_size; - uint16_t mb[MAILBOX_REGISTER_COUNT]; - uint16_t cnt; - int num, i; -#if DUMP_IT_BACK - uint8_t *sp; - uint8_t *tbuf; - dma_addr_t p_tbuf; -#endif + uint16_t risc_address, *risc_code_address, risc_code_size; + uint16_t mb[MAILBOX_REGISTER_COUNT], i; + int err; - ENTER("qla1280_setup_chip"); + /* Load RISC code. */ + risc_address = *ql1280_board_tbl[ha->devnum].fwstart; + risc_code_address = ql1280_board_tbl[ha->devnum].fwcode; + risc_code_size = *ql1280_board_tbl[ha->devnum].fwlen; - dprintk(1, "scsi(%ld): Setup chip\n", ha->host_no); + for (i = 0; i < risc_code_size; i++) { + mb[0] = MBC_WRITE_RAM_WORD; + mb[1] = risc_address + i; + mb[2] = risc_code_address[i]; + + err = qla1280_mailbox_command(ha, BIT_0 | BIT_1 | BIT_2, mb); + if (err) { + printk(KERN_ERR "scsi(%li): Failed to load firmware\n", + ha->host_no); + return err; + } + } + return 0; +} + +#define DUMP_IT_BACK 0 /* for debug of RISC loading */ +static int +qla1280_load_firmware_dma(struct scsi_qla_host *ha) +{ + uint16_t risc_address, *risc_code_address, risc_code_size; + uint16_t mb[MAILBOX_REGISTER_COUNT], cnt; + int err = 0, num, i; #if DUMP_IT_BACK - /* get consistent memory allocated for setup_chip */ + uint8_t *sp, *tbuf; + dma_addr_t p_tbuf; + tbuf = pci_alloc_consistent(ha->pdev, 8000, &p_tbuf); + if (!tbuf) + return -ENOMEM; #endif /* Load RISC code. */ risc_address = *ql1280_board_tbl[ha->devnum].fwstart; risc_code_address = ql1280_board_tbl[ha->devnum].fwcode; - risc_code_size = (int) *ql1280_board_tbl[ha->devnum].fwlen; + risc_code_size = *ql1280_board_tbl[ha->devnum].fwlen; - dprintk(1, "qla1280_setup_chip: DMA RISC code (%i) words\n", - risc_code_size); + dprintk(1, "%s: DMA RISC code (%i) words\n", + __FUNCTION__, risc_code_size); num = 0; - while (risc_code_size > 0 && !status) { + while (risc_code_size > 0) { int warn __attribute__((unused)) = 0; cnt = 2000 >> 1; @@ -2129,15 +2102,16 @@ qla1280_setup_chip(struct scsi_qla_host mb[2] = (ha->request_dma >> 16) & 0xffff; mb[7] = pci_dma_hi32(ha->request_dma) & 0xffff; mb[6] = pci_dma_hi32(ha->request_dma) >> 16; - dprintk(2, "qla1280_setup_chip: op=%d 0x%p = 0x%4x,0x%4x," - "0x%4x,0x%4x\n", mb[0], (void *)(long)ha->request_dma, - mb[6], mb[7], mb[2], mb[3]); - if ((status = qla1280_mailbox_command(ha, BIT_4 | BIT_3 | - BIT_2 | BIT_1 | BIT_0, - &mb[0]))) { + dprintk(2, "%s: op=%d 0x%p = 0x%4x,0x%4x,0x%4x,0x%4x\n", + __FUNCTION__, mb[0], + (void *)(long)ha->request_dma, + mb[6], mb[7], mb[2], mb[3]); + err = qla1280_mailbox_command(ha, BIT_4 | BIT_3 | BIT_2 | + BIT_1 | BIT_0, mb); + if (err) { printk(KERN_ERR "scsi(%li): Failed to load partial " "segment of f\n", ha->host_no); - break; + goto out; } #if DUMP_IT_BACK @@ -2149,22 +2123,22 @@ qla1280_setup_chip(struct scsi_qla_host mb[7] = pci_dma_hi32(p_tbuf) & 0xffff; mb[6] = pci_dma_hi32(p_tbuf) >> 16; - if ((status = qla1280_mailbox_command(ha, - BIT_4 | BIT_3 | BIT_2 | - BIT_1 | BIT_0, - &mb[0]))) { + err = qla1280_mailbox_command(ha, BIT_4 | BIT_3 | BIT_2 | + BIT_1 | BIT_0, mb); + if (err) { printk(KERN_ERR "Failed to dump partial segment of f/w\n"); - break; + goto out; } sp = (uint8_t *)ha->request_ring; for (i = 0; i < (cnt << 1); i++) { if (tbuf[i] != sp[i] && warn++ < 10) { - printk(KERN_ERR "qla1280_setup_chip: FW " - "compare error @ byte(0x%x) loop#=%x\n", - i, num); - printk(KERN_ERR "setup_chip: FWbyte=%x " - "FWfromChip=%x\n", sp[i], tbuf[i]); + printk(KERN_ERR "%s: FW compare error @ " + "byte(0x%x) loop#=%x\n", + __FUNCTION__, i, num); + printk(KERN_ERR "%s: FWbyte=%x " + "FWfromChip=%x\n", + __FUNCTION__, sp[i], tbuf[i]); /*break; */ } } @@ -2175,37 +2149,69 @@ qla1280_setup_chip(struct scsi_qla_host num++; } - /* Verify checksum of loaded RISC code. */ - if (!status) { - dprintk(1, "qla1280_setup_chip: Verifying checksum of " - "loaded RISC code.\n"); - mb[0] = MBC_VERIFY_CHECKSUM; - /* mb[1] = ql12_risc_code_addr01; */ - mb[1] = *ql1280_board_tbl[ha->devnum].fwstart; - - if (!(status = - qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]))) { - /* Start firmware execution. */ - dprintk(1, - "qla1280_setup_chip: start firmware running.\n"); - mb[0] = MBC_EXECUTE_FIRMWARE; - mb[1] = *ql1280_board_tbl[ha->devnum].fwstart; - qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]); - } else - printk(KERN_ERR "scsi(%li): qla1280_setup_chip: " - "Failed checksum\n", ha->host_no); - } - + out: #if DUMP_IT_BACK - /* free consistent memory allocated for setup_chip */ pci_free_consistent(ha->pdev, 8000, tbuf, p_tbuf); #endif + return err; +} - if (status) - dprintk(2, "qla1280_setup_chip: **** FAILED ****\n"); +static int +qla1280_start_firmware(struct scsi_qla_host *ha) +{ + uint16_t mb[MAILBOX_REGISTER_COUNT]; + int err; - LEAVE("qla1280_setup_chip"); - return status; + dprintk(1, "%s: Verifying checksum of loaded RISC code.\n", + __FUNCTION__); + + /* Verify checksum of loaded RISC code. */ + mb[0] = MBC_VERIFY_CHECKSUM; + /* mb[1] = ql12_risc_code_addr01; */ + mb[1] = *ql1280_board_tbl[ha->devnum].fwstart; + err = qla1280_mailbox_command(ha, BIT_1 | BIT_0, mb); + if (err) { + printk(KERN_ERR "scsi(%li): Failed checksum\n", ha->host_no); + return err; + } + + /* Start firmware execution. */ + dprintk(1, "%s: start firmware running.\n", __FUNCTION__); + mb[0] = MBC_EXECUTE_FIRMWARE; + mb[1] = *ql1280_board_tbl[ha->devnum].fwstart; + err = qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]); + if (err) { + printk(KERN_ERR "scsi(%li): Failed to start firmware\n", + ha->host_no); + } + + return err; +} + +static int +qla1280_load_firmware(struct scsi_qla_host *ha) +{ + int err = -ENODEV; + + /* If firmware needs to be loaded */ + if (!qla1280_isp_firmware(ha)) { + printk(KERN_ERR "scsi(%li): isp_firmware() failed!\n", + ha->host_no); + goto out; + } + + err = qla1280_chip_diag(ha); + if (err) + goto out; + if (IS_ISP1040(ha)) + err = qla1280_load_firmware_pio(ha); + else + err = qla1280_load_firmware_dma(ha); + if (err) + goto out; + err = qla1280_start_firmware(ha); + out: + return err; } /* @@ -2271,123 +2277,9 @@ qla1280_init_rings(struct scsi_qla_host return status; } -/* - * NVRAM configuration. - * - * Input: - * ha = adapter block pointer. - * ha->request_ring = request ring virtual address - * - * Output: - * host adapters parameters in host adapter block - * - * Returns: - * 0 = success. - */ -static int -qla1280_nvram_config(struct scsi_qla_host *ha) +static void +qla1280_print_settings(struct nvram *nv) { - struct device_reg *reg = ha->iobase; - struct nvram *nv; - int is1x160, status = 0; - int bus, target, lun; - uint16_t mb[MAILBOX_REGISTER_COUNT]; - uint16_t mask; - - ENTER("qla1280_nvram_config"); - - if (ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP12160 || - ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP10160) - is1x160 = 1; - else - is1x160 = 0; - - nv = &ha->nvram; - if (!ha->nvram_valid) { - dprintk(1, "Using defaults for NVRAM: \n"); - memset(nv, 0, sizeof(struct nvram)); - - /* nv->cntr_flags_1.disable_loading_risc_code = 1; */ - nv->firmware_feature.f.enable_fast_posting = 1; - nv->firmware_feature.f.disable_synchronous_backoff = 1; - - nv->termination.f.scsi_bus_0_control = 3; - nv->termination.f.scsi_bus_1_control = 3; - nv->termination.f.auto_term_support = 1; - - /* - * Set default FIFO magic - What appropriate values - * would be here is unknown. This is what I have found - * testing with 12160s. - * Now, I would love the magic decoder ring for this one, - * the header file provided by QLogic seems to be bogus - * or incomplete at best. - */ - nv->isp_config.c = 0x44; - - if (is1x160) - nv->isp_parameter = 0x01; - - for (bus = 0; bus < MAX_BUSES; bus++) { - nv->bus[bus].config_1.initiator_id = 7; - nv->bus[bus].bus_reset_delay = 5; - /* 8 = 5.0 clocks */ - nv->bus[bus].config_2.async_data_setup_time = 8; - nv->bus[bus].config_2.req_ack_active_negation = 1; - nv->bus[bus].config_2.data_line_active_negation = 1; - nv->bus[bus].selection_timeout = 250; - nv->bus[bus].max_queue_depth = 256; - - for (target = 0; target < MAX_TARGETS; target++) { - nv->bus[bus].target[target].parameter.f. - renegotiate_on_error = 1; - nv->bus[bus].target[target].parameter.f. - auto_request_sense = 1; - nv->bus[bus].target[target].parameter.f. - tag_queuing = 1; - nv->bus[bus].target[target].parameter.f. - enable_sync = 1; -#if 1 /* Some SCSI Processors do not seem to like this */ - nv->bus[bus].target[target].parameter.f. - enable_wide = 1; -#endif - nv->bus[bus].target[target].parameter.f. - parity_checking = 1; - nv->bus[bus].target[target].parameter.f. - disconnect_allowed = 1; - nv->bus[bus].target[target].execution_throttle= - nv->bus[bus].max_queue_depth - 1; - if (is1x160) { - nv->bus[bus].target[target].flags. - flags1x160.device_enable = 1; - nv->bus[bus].target[target].flags. - flags1x160.sync_offset = 0x0e; - nv->bus[bus].target[target]. - sync_period = 9; - nv->bus[bus].target[target]. - ppr_1x160.flags.enable_ppr = 1; - nv->bus[bus].target[target].ppr_1x160. - flags.ppr_options = 2; - nv->bus[bus].target[target].ppr_1x160. - flags.ppr_bus_width = 1; - } else { - nv->bus[bus].target[target].flags. - flags1x80.device_enable = 1; - nv->bus[bus].target[target].flags. - flags1x80.sync_offset = 0x8; - nv->bus[bus].target[target]. - sync_period = 10; - } - } - } - } else { - /* Always force AUTO sense for LINUX SCSI */ - for (bus = 0; bus < MAX_BUSES; bus++) - for (target = 0; target < MAX_TARGETS; target++) { - nv->bus[bus].target[target].parameter.f. - auto_request_sense = 1; - } - } dprintk(1, "qla1280 : initiator scsi id bus[0]=%d\n", nv->bus[0].config_1.initiator_id); dprintk(1, "qla1280 : initiator scsi id bus[1]=%d\n", @@ -2433,36 +2325,264 @@ qla1280_nvram_config(struct scsi_qla_hos nv->bus[0].max_queue_depth); dprintk(1, "qla1280 : max queue depth[1]=%d\n", nv->bus[1].max_queue_depth); +} + +static void +qla1280_set_target_defaults(struct scsi_qla_host *ha, int bus, int target) +{ + struct nvram *nv = &ha->nvram; + + nv->bus[bus].target[target].parameter.f.renegotiate_on_error = 1; + nv->bus[bus].target[target].parameter.f.auto_request_sense = 1; + nv->bus[bus].target[target].parameter.f.tag_queuing = 1; + nv->bus[bus].target[target].parameter.f.enable_sync = 1; +#if 1 /* Some SCSI Processors do not seem to like this */ + nv->bus[bus].target[target].parameter.f.enable_wide = 1; +#endif + if (!IS_ISP1040(ha)) + nv->bus[bus].target[target].parameter.f.parity_checking = 1; + + nv->bus[bus].target[target].parameter.f.disconnect_allowed = 1; + nv->bus[bus].target[target].execution_throttle = + nv->bus[bus].max_queue_depth - 1; + + if (IS_ISP1x160(ha)) { + nv->bus[bus].target[target].flags.flags1x160.device_enable = 1; + nv->bus[bus].target[target].flags.flags1x160.sync_offset = 0x0e; + nv->bus[bus].target[target].sync_period = 9; + nv->bus[bus].target[target].ppr_1x160.flags.enable_ppr = 1; + nv->bus[bus].target[target].ppr_1x160.flags.ppr_options = 2; + nv->bus[bus].target[target].ppr_1x160.flags.ppr_bus_width = 1; + } else { + nv->bus[bus].target[target].flags.flags1x80.device_enable = 1; + nv->bus[bus].target[target].flags.flags1x80.sync_offset = 12; + nv->bus[bus].target[target].sync_period = 10; + } +} + +static void +qla1280_set_defaults(struct scsi_qla_host *ha) +{ + struct nvram *nv = &ha->nvram; + int bus, target; + + dprintk(1, "Using defaults for NVRAM: \n"); + memset(nv, 0, sizeof(struct nvram)); + + /* nv->cntr_flags_1.disable_loading_risc_code = 1; */ + nv->firmware_feature.f.enable_fast_posting = 1; + nv->firmware_feature.f.disable_synchronous_backoff = 1; + nv->termination.f.scsi_bus_0_control = 3; + nv->termination.f.scsi_bus_1_control = 3; + nv->termination.f.auto_term_support = 1; + + /* + * Set default FIFO magic - What appropriate values would be here + * is unknown. This is what I have found testing with 12160s. + * + * Now, I would love the magic decoder ring for this one, the + * header file provided by QLogic seems to be bogus or incomplete + * at best. + */ + nv->isp_config.c = ISP_CFG1_BENAB|ISP_CFG1_F128; + if (IS_ISP1x160(ha)) + nv->isp_parameter = 0x01; /* fast memory enable */ + + for (bus = 0; bus < MAX_BUSES; bus++) { + nv->bus[bus].config_1.initiator_id = 7; + nv->bus[bus].config_2.req_ack_active_negation = 1; + nv->bus[bus].config_2.data_line_active_negation = 1; + nv->bus[bus].selection_timeout = 250; + nv->bus[bus].max_queue_depth = 256; + + if (IS_ISP1040(ha)) { + nv->bus[bus].bus_reset_delay = 3; + nv->bus[bus].config_2.async_data_setup_time = 6; + nv->bus[bus].retry_delay = 1; + } else { + nv->bus[bus].bus_reset_delay = 5; + nv->bus[bus].config_2.async_data_setup_time = 8; + } + + for (target = 0; target < MAX_TARGETS; target++) + qla1280_set_target_defaults(ha, bus, target); + } +} + +static int +qla1280_config_target(struct scsi_qla_host *ha, int bus, int target) +{ + struct nvram *nv = &ha->nvram; + uint16_t mb[MAILBOX_REGISTER_COUNT]; + int status, lun; + + /* Set Target Parameters. */ + mb[0] = MBC_SET_TARGET_PARAMETERS; + mb[1] = (uint16_t) (bus ? target | BIT_7 : target); + mb[1] <<= 8; + + /* + * Do not enable wide, sync, and ppr for the initial + * INQUIRY run. We enable this later if we determine + * the target actually supports it. + */ + nv->bus[bus].target[target].parameter.f. + auto_request_sense = 1; + nv->bus[bus].target[target].parameter.f. + stop_queue_on_check = 0; + + if (IS_ISP1x160(ha)) + nv->bus[bus].target[target].ppr_1x160. + flags.enable_ppr = 0; + + /* + * No sync, wide, etc. while probing + */ + mb[2] = (nv->bus[bus].target[target].parameter.c << 8) & + ~(TP_SYNC /*| TP_WIDE | TP_PPR*/); + + if (IS_ISP1x160(ha)) + mb[3] = nv->bus[bus].target[target].flags.flags1x160.sync_offset << 8; + else + mb[3] = nv->bus[bus].target[target].flags.flags1x80.sync_offset << 8; + mb[3] |= nv->bus[bus].target[target].sync_period; + + status = qla1280_mailbox_command(ha, BIT_3 | BIT_2 | BIT_1 | BIT_0, &mb[0]); + + /* Save Tag queuing enable flag. */ + mb[0] = BIT_0 << target; + if (nv->bus[bus].target[target].parameter.f.tag_queuing) + ha->bus_settings[bus].qtag_enables |= mb[0]; + + /* Save Device enable flag. */ + if (IS_ISP1x160(ha)) { + if (nv->bus[bus].target[target].flags.flags1x160.device_enable) + ha->bus_settings[bus].device_enables |= mb[0]; + ha->bus_settings[bus].lun_disables |= 0; + } else { + if (nv->bus[bus].target[target].flags.flags1x80.device_enable) + ha->bus_settings[bus].device_enables |= mb[0]; + /* Save LUN disable flag. */ + if (nv->bus[bus].target[target].flags.flags1x80.lun_disable) + ha->bus_settings[bus].lun_disables |= mb[0]; + } + + /* Set Device Queue Parameters. */ + for (lun = 0; lun < MAX_LUNS; lun++) { + mb[0] = MBC_SET_DEVICE_QUEUE; + mb[1] = (uint16_t)(bus ? target | BIT_7 : target); + mb[1] = mb[1] << 8 | lun; + mb[2] = nv->bus[bus].max_queue_depth; + mb[3] = nv->bus[bus].target[target].execution_throttle; + status |= qla1280_mailbox_command(ha, 0x0f, &mb[0]); + } + + return status; +} + +static int +qla1280_config_bus(struct scsi_qla_host *ha, int bus) +{ + struct nvram *nv = &ha->nvram; + uint16_t mb[MAILBOX_REGISTER_COUNT]; + int target, status; + + /* SCSI Reset Disable. */ + ha->bus_settings[bus].disable_scsi_reset = + nv->bus[bus].config_1.scsi_reset_disable; + + /* Initiator ID. */ + ha->bus_settings[bus].id = nv->bus[bus].config_1.initiator_id; + mb[0] = MBC_SET_INITIATOR_ID; + mb[1] = bus ? ha->bus_settings[bus].id | BIT_7 : + ha->bus_settings[bus].id; + status = qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]); + + /* Reset Delay. */ + ha->bus_settings[bus].bus_reset_delay = + nv->bus[bus].bus_reset_delay; + + /* Command queue depth per device. */ + ha->bus_settings[bus].hiwat = nv->bus[bus].max_queue_depth - 1; + + /* Set target parameters. */ + for (target = 0; target < MAX_TARGETS; target++) + status |= qla1280_config_target(ha, bus, target); + + return status; +} + +static int +qla1280_nvram_config(struct scsi_qla_host *ha) +{ + struct device_reg *reg = ha->iobase; + struct nvram *nv = &ha->nvram; + int bus, target, status = 0; + uint16_t mb[MAILBOX_REGISTER_COUNT]; + uint16_t mask; + + ENTER("qla1280_nvram_config"); + + if (ha->nvram_valid) { + /* Always force AUTO sense for LINUX SCSI */ + for (bus = 0; bus < MAX_BUSES; bus++) + for (target = 0; target < MAX_TARGETS; target++) { + nv->bus[bus].target[target].parameter.f. + auto_request_sense = 1; + } + } else { + qla1280_set_defaults(ha); + } + + qla1280_print_settings(nv); /* Disable RISC load of firmware. */ ha->flags.disable_risc_code_load = nv->cntr_flags_1.disable_loading_risc_code; - /* Set ISP hardware DMA burst */ - mb[0] = nv->isp_config.c; - /* Enable DMA arbitration on dual channel controllers */ - if (ha->ports > 1) - mb[0] |= BIT_13; - WRT_REG_WORD(®->cfg_1, mb[0]); - -#if 1 /* Is this safe? */ - /* Set SCSI termination. */ - WRT_REG_WORD(®->gpio_enable, (BIT_3 + BIT_2 + BIT_1 + BIT_0)); - mb[0] = nv->termination.c & (BIT_3 + BIT_2 + BIT_1 + BIT_0); - WRT_REG_WORD(®->gpio_data, mb[0]); -#endif + if (IS_ISP1040(ha)) { + uint16_t hwrev, cfg1, cdma_conf, ddma_conf; + + hwrev = RD_REG_WORD(®->cfg_0) & ISP_CFG0_HWMSK; + + cfg1 = RD_REG_WORD(®->cfg_1); + cdma_conf = RD_REG_WORD(®->cdma_cfg); + ddma_conf = RD_REG_WORD(®->ddma_cfg); + + /* Busted fifo, says mjacob. */ + if (hwrev == ISP_CFG0_1040A) + WRT_REG_WORD(®->cfg_1, cfg1 | ISP_CFG1_F64); + else + WRT_REG_WORD(®->cfg_1, cfg1 | ISP_CFG1_F64 | ISP_CFG1_BENAB); + + WRT_REG_WORD(®->cdma_cfg, cdma_conf | CDMA_CONF_BENAB); + WRT_REG_WORD(®->ddma_cfg, cdma_conf | DDMA_CONF_BENAB); + } else { + /* Set ISP hardware DMA burst */ + mb[0] = nv->isp_config.c; + /* Enable DMA arbitration on dual channel controllers */ + if (ha->ports > 1) + mb[0] |= BIT_13; + WRT_REG_WORD(®->cfg_1, mb[0]); + + /* Set SCSI termination. */ + WRT_REG_WORD(®->gpio_enable, (BIT_3 + BIT_2 + BIT_1 + BIT_0)); + mb[0] = nv->termination.c & (BIT_3 + BIT_2 + BIT_1 + BIT_0); + WRT_REG_WORD(®->gpio_data, mb[0]); + } /* ISP parameter word. */ mb[0] = MBC_SET_SYSTEM_PARAMETER; mb[1] = nv->isp_parameter; status |= qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]); -#if 0 - /* clock rate - for qla1240 and older, only */ - mb[0] = MBC_SET_CLOCK_RATE; - mb[1] = 0x50; - status |= qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]); -#endif + if (IS_ISP1x40(ha)) { + /* clock rate - for qla1240 and older, only */ + mb[0] = MBC_SET_CLOCK_RATE; + mb[1] = 40; + status |= qla1280_mailbox_command(ha, BIT_1 | BIT_0, mb); + } + /* Firmware feature word. */ mb[0] = MBC_SET_FIRMWARE_FEATURES; mask = BIT_5 | BIT_1 | BIT_0; @@ -2515,112 +2635,18 @@ qla1280_nvram_config(struct scsi_qla_hos mb[2] = 2; /* Command DMA Channel Burst Enable */ status |= qla1280_mailbox_command(ha, BIT_2 | BIT_1 | BIT_0, &mb[0]); + mb[0] = MBC_SET_TAG_AGE_LIMIT; + mb[1] = 8; + status |= qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]); + /* Selection timeout. */ mb[0] = MBC_SET_SELECTION_TIMEOUT; mb[1] = nv->bus[0].selection_timeout; mb[2] = nv->bus[1].selection_timeout; status |= qla1280_mailbox_command(ha, BIT_2 | BIT_1 | BIT_0, &mb[0]); - for (bus = 0; bus < ha->ports; bus++) { - /* SCSI Reset Disable. */ - ha->bus_settings[bus].disable_scsi_reset = - nv->bus[bus].config_1.scsi_reset_disable; - - /* Initiator ID. */ - ha->bus_settings[bus].id = nv->bus[bus].config_1.initiator_id; - mb[0] = MBC_SET_INITIATOR_ID; - mb[1] = bus ? ha->bus_settings[bus].id | BIT_7 : - ha->bus_settings[bus].id; - status |= qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]); - - /* Reset Delay. */ - ha->bus_settings[bus].bus_reset_delay = - nv->bus[bus].bus_reset_delay; - - /* Command queue depth per device. */ - ha->bus_settings[bus].hiwat = nv->bus[bus].max_queue_depth - 1; - - /* Set target parameters. */ - for (target = 0; target < MAX_TARGETS; target++) { - uint8_t mr = BIT_2 | BIT_1 | BIT_0; - - /* Set Target Parameters. */ - mb[0] = MBC_SET_TARGET_PARAMETERS; - mb[1] = (uint16_t) (bus ? target | BIT_7 : target); - mb[1] <<= 8; - /* - * Do not enable wide, sync, and ppr for the initial - * INQUIRY run. We enable this later if we determine - * the target actually supports it. - */ - nv->bus[bus].target[target].parameter.f. - auto_request_sense = 1; - nv->bus[bus].target[target].parameter.f. - stop_queue_on_check = 0; - - if (is1x160) - nv->bus[bus].target[target].ppr_1x160. - flags.enable_ppr = 0; - /* - * No sync, wide, etc. while probing - */ - mb[2] = (nv->bus[bus].target[target].parameter.c << 8)& - ~(TP_SYNC /*| TP_WIDE | TP_PPR*/); - - if (is1x160) - mb[3] = nv->bus[bus].target[target].flags.flags1x160.sync_offset << 8; - else - mb[3] = nv->bus[bus].target[target].flags.flags1x80.sync_offset << 8; - mb[3] |= nv->bus[bus].target[target].sync_period; - mr |= BIT_3; - - /* - * We don't want to enable ppr etc. before we have - * determined that the target actually supports it - */ -#if 0 - if (is1x160) { - mb[2] |= nv->bus[bus].target[target].ppr_1x160.flags.enable_ppr << 5; - - mb[6] = nv->bus[bus].target[target].ppr_1x160.flags.ppr_options << 8; - mb[6] |= nv->bus[bus].target[target].ppr_1x160.flags.ppr_bus_width; - mr |= BIT_6; - } -#endif - - status = qla1280_mailbox_command(ha, mr, &mb[0]); - - /* Save Tag queuing enable flag. */ - mb[0] = BIT_0 << target; - if (nv->bus[bus].target[target].parameter.f.tag_queuing) - ha->bus_settings[bus].qtag_enables |= mb[0]; - - /* Save Device enable flag. */ - if (is1x160) { - if (nv->bus[bus].target[target].flags.flags1x160.device_enable) - ha->bus_settings[bus].device_enables |= mb[0]; - ha->bus_settings[bus].lun_disables |= 0; - } else { - if (nv->bus[bus].target[target].flags.flags1x80.device_enable) - ha->bus_settings[bus].device_enables |= mb[0]; - /* Save LUN disable flag. */ - if (nv->bus[bus].target[target].flags.flags1x80.lun_disable) - ha->bus_settings[bus].lun_disables |= mb[0]; - } - - - /* Set Device Queue Parameters. */ - for (lun = 0; lun < MAX_LUNS; lun++) { - mb[0] = MBC_SET_DEVICE_QUEUE; - mb[1] = (uint16_t)(bus ? target | BIT_7 : target); - mb[1] = mb[1] << 8 | lun; - mb[2] = nv->bus[bus].max_queue_depth; - mb[3] = nv->bus[bus].target[target].execution_throttle; - status |= qla1280_mailbox_command(ha, 0x0f, - &mb[0]); - } - } - } + for (bus = 0; bus < ha->ports; bus++) + status |= qla1280_config_bus(ha, bus); if (status) dprintk(2, "qla1280_nvram_config: **** FAILED ****\n"); @@ -4231,6 +4257,7 @@ qla1280_error_entry(struct scsi_qla_host static int qla1280_abort_isp(struct scsi_qla_host *ha) { + struct device_reg *reg = ha->iobase; struct srb *sp; int status = 0; int cnt; @@ -4238,69 +4265,53 @@ qla1280_abort_isp(struct scsi_qla_host * ENTER("qla1280_abort_isp"); - if (!ha->flags.abort_isp_active && ha->flags.online) { - struct device_reg *reg = ha->iobase; - ha->flags.abort_isp_active = 1; + if (ha->flags.abort_isp_active || !ha->flags.online) + goto out; + + ha->flags.abort_isp_active = 1; - /* Disable ISP interrupts. */ - qla1280_disable_intrs(ha); - WRT_REG_WORD(®->host_cmd, HC_PAUSE_RISC); - RD_REG_WORD(®->id_l); + /* Disable ISP interrupts. */ + qla1280_disable_intrs(ha); + WRT_REG_WORD(®->host_cmd, HC_PAUSE_RISC); + RD_REG_WORD(®->id_l); - printk(KERN_INFO "scsi(%li): dequeuing outstanding commands\n", - ha->host_no); - /* Dequeue all commands in outstanding command list. */ - for (cnt = 0; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { - struct scsi_cmnd *cmd; - sp = ha->outstanding_cmds[cnt]; - if (sp) { + printk(KERN_INFO "scsi(%li): dequeuing outstanding commands\n", + ha->host_no); + /* Dequeue all commands in outstanding command list. */ + for (cnt = 0; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { + struct scsi_cmnd *cmd; + sp = ha->outstanding_cmds[cnt]; + if (sp) { - cmd = sp->cmd; - CMD_RESULT(cmd) = DID_RESET << 16; + cmd = sp->cmd; + CMD_RESULT(cmd) = DID_RESET << 16; - sp->cmd = NULL; - ha->outstanding_cmds[cnt] = NULL; + sp->cmd = NULL; + ha->outstanding_cmds[cnt] = NULL; - (*cmd->scsi_done)(cmd); + (*cmd->scsi_done)(cmd); - sp->flags = 0; - } + sp->flags = 0; } + } - /* If firmware needs to be loaded */ - if (qla1280_isp_firmware (ha)) { - if (!(status = qla1280_chip_diag(ha))) - status = qla1280_setup_chip(ha); - } + status = qla1280_load_firmware(ha); + if (status) + goto out; - if (!status) { - /* Setup adapter based on NVRAM parameters. */ - qla1280_nvram_config (ha); - - if (!(status = qla1280_init_rings(ha))) { - /* Issue SCSI reset. */ - for (bus = 0; bus < ha->ports; bus++) { - qla1280_bus_reset(ha, bus); - } - /* - * qla1280_bus_reset() will do the marker - * dance - no reason to repeat here! - */ -#if 0 - /* Issue marker command. */ - ha->flags.reset_marker = 0; - for (bus = 0; bus < ha->ports; bus++) { - ha->bus_settings[bus]. - reset_marker = 0; - qla1280_marker(ha, bus, 0, 0, - MK_SYNC_ALL); - } -#endif - ha->flags.abort_isp_active = 0; - } - } - } + /* Setup adapter based on NVRAM parameters. */ + qla1280_nvram_config (ha); + status = qla1280_init_rings(ha); + if (status) + goto out; + + /* Issue SCSI reset. */ + for (bus = 0; bus < ha->ports; bus++) + qla1280_bus_reset(ha, bus); + + ha->flags.abort_isp_active = 0; + out: if (status) { printk(KERN_WARNING "qla1280: ISP error recovery failed, board disabled"); @@ -4936,6 +4947,7 @@ module_exit(qla1280_exit); MODULE_AUTHOR("Qlogic & Jes Sorensen"); MODULE_DESCRIPTION("Qlogic ISP SCSI (qla1x80/qla1x160) driver"); MODULE_LICENSE("GPL"); +MODULE_VERSION(QLA1280_VERSION); /* * Overrides for Emacs so that we almost follow Linus's tabbing style. --- linux-2.6.9-rc1/drivers/scsi/qla1280.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/scsi/qla1280.h 2004-09-02 20:51:52.000000000 -0700 @@ -62,6 +62,7 @@ #define WRT_REG_WORD(addr, data) writew(data, addr) #else /* MEMORY_MAPPED_IO */ #define RD_REG_WORD(addr) inw((unsigned long)addr) +#define RD_REG_WORD_dmasync(addr) RD_REG_WORD(addr) #define WRT_REG_WORD(addr, data) outw(data, (unsigned long)addr) #endif /* MEMORY_MAPPED_IO */ @@ -126,7 +127,20 @@ struct device_reg { uint16_t id_l; /* ID low */ uint16_t id_h; /* ID high */ uint16_t cfg_0; /* Configuration 0 */ +#define ISP_CFG0_HWMSK 0x000f /* Hardware revision mask */ +#define ISP_CFG0_1020 BIT_0 /* ISP1020 */ +#define ISP_CFG0_1020A BIT_1 /* ISP1020A */ +#define ISP_CFG0_1040 BIT_2 /* ISP1040 */ +#define ISP_CFG0_1040A BIT_3 /* ISP1040A */ +#define ISP_CFG0_1040B BIT_4 /* ISP1040B */ +#define ISP_CFG0_1040C BIT_5 /* ISP1040C */ uint16_t cfg_1; /* Configuration 1 */ +#define ISP_CFG1_F128 BIT_6 /* 128-byte FIFO threshold */ +#define ISP_CFG1_F64 BIT_4|BIT_5 /* 128-byte FIFO threshold */ +#define ISP_CFG1_F32 BIT_5 /* 128-byte FIFO threshold */ +#define ISP_CFG1_F16 BIT_4 /* 128-byte FIFO threshold */ +#define ISP_CFG1_BENAB BIT_2 /* Global Bus burst enable */ +#define ISP_CFG1_SXP BIT_0 /* SXP register select */ uint16_t ictrl; /* Interface control */ #define ISP_RESET BIT_0 /* ISP soft reset */ #define ISP_EN_INT BIT_1 /* ISP enable interrupts. */ @@ -147,7 +161,42 @@ struct device_reg { uint16_t flash_data; /* Flash BIOS data */ uint16_t flash_address; /* Flash BIOS address */ - uint16_t unused_1[0x2e]; /* 0x14-0x6f Gap */ + uint16_t unused_1[0x06]; + + /* cdma_* and ddma_* are 1040 only */ + uint16_t cdma_cfg; +#define CDMA_CONF_SENAB BIT_3 /* SXP to DMA Data enable */ +#define CDMA_CONF_RIRQ BIT_2 /* RISC interrupt enable */ +#define CDMA_CONF_BENAB BIT_1 /* Bus burst enable */ +#define CDMA_CONF_DIR BIT_0 /* DMA direction (0=fifo->host 1=host->fifo) */ + uint16_t cdma_ctrl; + uint16_t cdma_status; + uint16_t cdma_fifo_status; + uint16_t cdma_count; + uint16_t cdma_reserved; + uint16_t cdma_address_count_0; + uint16_t cdma_address_count_1; + uint16_t cdma_address_count_2; + uint16_t cdma_address_count_3; + + uint16_t unused_2[0x06]; + + uint16_t ddma_cfg; +#define DDMA_CONF_SENAB BIT_3 /* SXP to DMA Data enable */ +#define DDMA_CONF_RIRQ BIT_2 /* RISC interrupt enable */ +#define DDMA_CONF_BENAB BIT_1 /* Bus burst enable */ +#define DDMA_CONF_DIR BIT_0 /* DMA direction (0=fifo->host 1=host->fifo) */ + uint16_t ddma_ctrl; + uint16_t ddma_status; + uint16_t ddma_fifo_status; + uint16_t ddma_xfer_count_low; + uint16_t ddma_xfer_count_high; + uint16_t ddma_addr_count_0; + uint16_t ddma_addr_count_1; + uint16_t ddma_addr_count_2; + uint16_t ddma_addr_count_3; + + uint16_t unused_3[0x0e]; uint16_t mailbox0; /* Mailbox 0 */ uint16_t mailbox1; /* Mailbox 1 */ @@ -158,18 +207,18 @@ struct device_reg { uint16_t mailbox6; /* Mailbox 6 */ uint16_t mailbox7; /* Mailbox 7 */ - uint16_t unused_2[0x20];/* 0x80-0xbf Gap */ + uint16_t unused_4[0x20];/* 0x80-0xbf Gap */ uint16_t host_cmd; /* Host command and control */ #define HOST_INT BIT_7 /* host interrupt bit */ #define BIOS_ENABLE BIT_0 - uint16_t unused_6[0x5]; /* 0xc2-0xcb Gap */ + uint16_t unused_5[0x5]; /* 0xc2-0xcb Gap */ uint16_t gpio_data; uint16_t gpio_enable; - uint16_t unused_7[0x11]; /* d0-f0 */ + uint16_t unused_6[0x11]; /* d0-f0 */ uint16_t scsiControlPins; /* f2 */ }; --- linux-2.6.9-rc1/drivers/scsi/qla2xxx/ql2100.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/drivers/scsi/qla2xxx/ql2100.c 2004-09-02 20:51:52.000000000 -0700 @@ -89,3 +89,4 @@ module_exit(qla2100_exit); MODULE_AUTHOR("QLogic Corporation"); MODULE_DESCRIPTION("QLogic ISP21xx FC-SCSI Host Bus Adapter driver"); MODULE_LICENSE("GPL"); +MODULE_VERSION(QLA2XXX_VERSION); --- linux-2.6.9-rc1/drivers/scsi/qla2xxx/ql2200.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/drivers/scsi/qla2xxx/ql2200.c 2004-09-02 20:51:52.000000000 -0700 @@ -89,3 +89,4 @@ module_exit(qla2200_exit); MODULE_AUTHOR("QLogic Corporation"); MODULE_DESCRIPTION("QLogic ISP22xx FC-SCSI Host Bus Adapter driver"); MODULE_LICENSE("GPL"); +MODULE_VERSION(QLA2XXX_VERSION); --- linux-2.6.9-rc1/drivers/scsi/qla2xxx/ql2300.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/scsi/qla2xxx/ql2300.c 2004-09-02 20:51:52.000000000 -0700 @@ -100,3 +100,4 @@ module_exit(qla2300_exit); MODULE_AUTHOR("QLogic Corporation"); MODULE_DESCRIPTION("QLogic ISP2300 FC-SCSI Host Bus Adapter driver"); MODULE_LICENSE("GPL"); +MODULE_VERSION(QLA2XXX_VERSION); --- linux-2.6.9-rc1/drivers/scsi/qla2xxx/ql2322.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/drivers/scsi/qla2xxx/ql2322.c 2004-09-02 20:51:52.000000000 -0700 @@ -105,3 +105,4 @@ module_exit(qla2322_exit); MODULE_AUTHOR("QLogic Corporation"); MODULE_DESCRIPTION("QLogic ISP2322 FC-SCSI Host Bus Adapter driver"); MODULE_LICENSE("GPL"); +MODULE_VERSION(QLA2XXX_VERSION); --- linux-2.6.9-rc1/drivers/scsi/qla2xxx/ql6312.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/scsi/qla2xxx/ql6312.c 2004-09-02 20:51:52.000000000 -0700 @@ -87,3 +87,4 @@ module_exit(qla6312_exit); MODULE_AUTHOR("QLogic Corporation"); MODULE_DESCRIPTION("QLogic ISP6312 FC-SCSI Host Bus Adapter driver"); MODULE_LICENSE("GPL"); +MODULE_VERSION(QLA2XXX_VERSION); --- linux-2.6.9-rc1/drivers/scsi/qla2xxx/ql6322.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/drivers/scsi/qla2xxx/ql6322.c 2004-09-02 20:51:52.000000000 -0700 @@ -105,3 +105,4 @@ module_exit(qla6322_exit); MODULE_AUTHOR("QLogic Corporation"); MODULE_DESCRIPTION("QLogic ISP6322 FC-SCSI Host Bus Adapter driver"); MODULE_LICENSE("GPL"); +MODULE_VERSION(QLA2XXX_VERSION); --- linux-2.6.9-rc1/drivers/scsi/qla2xxx/qla_init.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/drivers/scsi/qla2xxx/qla_init.c 2004-09-02 20:51:52.000000000 -0700 @@ -783,7 +783,6 @@ qla2x00_init_response_q_entries(scsi_qla static void qla2x00_update_fw_options(scsi_qla_host_t *ha) { - /* Setup seriallink options */ uint16_t swing, emphasis; memset(ha->fw_options, 0, sizeof(ha->fw_options)); @@ -807,7 +806,6 @@ qla2x00_update_fw_options(scsi_qla_host_ emphasis = ha->fw_seriallink_options[0] & (BIT_4 | BIT_3); emphasis >>= 3; ha->fw_options[10] = (emphasis << 14) | (swing << 8) | 0x3; - /* 2G settings */ swing = ha->fw_seriallink_options[0] & (BIT_7 | BIT_6 | BIT_5); swing >>= 5; @@ -818,7 +816,7 @@ qla2x00_update_fw_options(scsi_qla_host_ /* Return command IOCBs without waiting for an ABTS to complete. */ ha->fw_options[3] |= BIT_13; - /* Update Serial Link options. */ + /* Update firmware options. */ qla2x00_set_fw_options(ha, ha->fw_options); } @@ -869,15 +867,15 @@ qla2x00_init_rings(scsi_qla_host_t *ha) spin_unlock_irqrestore(&ha->hardware_lock, flags); + /* Update any ISP specific firmware options before initialization. */ + qla2x00_update_fw_options(ha); + DEBUG(printk("scsi(%ld): Issue init firmware.\n", ha->host_no)); rval = qla2x00_init_firmware(ha, sizeof(init_cb_t)); if (rval) { DEBUG2_3(printk("scsi(%ld): Init firmware **** FAILED ****.\n", ha->host_no)); } else { - /* Update any ISP specific firmware options. */ - qla2x00_update_fw_options(ha); - DEBUG3(printk("scsi(%ld): Init firmware -- success.\n", ha->host_no)); } --- linux-2.6.9-rc1/drivers/scsi/qla2xxx/qla_iocb.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/scsi/qla2xxx/qla_iocb.c 2004-09-02 20:51:52.000000000 -0700 @@ -22,6 +22,8 @@ #include #include +#include + static inline uint16_t qla2x00_get_cmd_direction(struct scsi_cmnd *cmd); static inline cont_entry_t *qla2x00_prep_cont_type0_iocb(scsi_qla_host_t *); static inline cont_a64_entry_t *qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *); @@ -337,6 +339,7 @@ qla2x00_start_scsi(srb_t *sp) uint16_t req_cnt; uint16_t tot_dsds; device_reg_t *reg; + char tag[2]; /* Setup device pointers. */ ret = 0; @@ -415,14 +418,17 @@ qla2x00_start_scsi(srb_t *sp) cmd_pkt->lun = cpu_to_le16(fclun->lun); /* Update tagged queuing modifier */ - cmd_pkt->control_flags = __constant_cpu_to_le16(CF_SIMPLE_TAG); - if (cmd->device->tagged_supported) { - switch (cmd->tag) { - case HEAD_OF_QUEUE_TAG: + if (scsi_populate_tag_msg(cmd, tag)) { + switch (tag[0]) { + case MSG_SIMPLE_TAG: + cmd_pkt->control_flags = + __constant_cpu_to_le16(CF_SIMPLE_TAG); + break; + case MSG_HEAD_TAG: cmd_pkt->control_flags = __constant_cpu_to_le16(CF_HEAD_TAG); break; - case ORDERED_QUEUE_TAG: + case MSG_ORDERED_TAG: cmd_pkt->control_flags = __constant_cpu_to_le16(CF_ORDERED_TAG); break; --- linux-2.6.9-rc1/drivers/scsi/qla2xxx/qla_os.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/scsi/qla2xxx/qla_os.c 2004-09-03 00:56:52.268214672 -0700 @@ -235,67 +235,6 @@ static __inline__ void sp_get(struct scs static __inline__ void qla2x00_delete_from_done_queue(scsi_qla_host_t *, srb_t *); -/************************************************************************** -* sp_put -* -* Description: -* Decrement reference count and call the callback if we're the last -* owner of the specified sp. Will get the host_lock before calling -* the callback. -* -* Input: -* ha - pointer to the scsi_qla_host_t where the callback is to occur. -* sp - pointer to srb_t structure to use. -* -* Returns: -* -**************************************************************************/ -static inline void -sp_put(struct scsi_qla_host * ha, srb_t *sp) -{ - if (atomic_read(&sp->ref_count) == 0) { - qla_printk(KERN_INFO, ha, - "%s(): **** SP->ref_count not zero\n", - __func__); - DEBUG2(BUG();) - - return; - } - - if (!atomic_dec_and_test(&sp->ref_count)) { - return; - } - - qla2x00_callback(ha, sp->cmd); -} - -/************************************************************************** -* sp_get -* -* Description: -* Increment reference count of the specified sp. -* -* Input: -* sp - pointer to srb_t structure to use. -* -* Returns: -* -**************************************************************************/ -static inline void -sp_get(struct scsi_qla_host * ha, srb_t *sp) -{ - atomic_inc(&sp->ref_count); - - if (atomic_read(&sp->ref_count) > 2) { - qla_printk(KERN_INFO, ha, - "%s(): **** SP->ref_count greater than two\n", - __func__); - DEBUG2(BUG();) - - return; - } -} - /* * qla2x00_callback * Returns the completed SCSI command to LINUX. @@ -366,6 +305,67 @@ qla2x00_callback(scsi_qla_host_t *ha, st (*(cmd)->scsi_done)(cmd); } +/************************************************************************** +* sp_put +* +* Description: +* Decrement reference count and call the callback if we're the last +* owner of the specified sp. Will get the host_lock before calling +* the callback. +* +* Input: +* ha - pointer to the scsi_qla_host_t where the callback is to occur. +* sp - pointer to srb_t structure to use. +* +* Returns: +* +**************************************************************************/ +static inline void +sp_put(struct scsi_qla_host * ha, srb_t *sp) +{ + if (atomic_read(&sp->ref_count) == 0) { + qla_printk(KERN_INFO, ha, + "%s(): **** SP->ref_count not zero\n", + __func__); + DEBUG2(BUG();) + + return; + } + + if (!atomic_dec_and_test(&sp->ref_count)) { + return; + } + + qla2x00_callback(ha, sp->cmd); +} + +/************************************************************************** +* sp_get +* +* Description: +* Increment reference count of the specified sp. +* +* Input: +* sp - pointer to srb_t structure to use. +* +* Returns: +* +**************************************************************************/ +static inline void +sp_get(struct scsi_qla_host * ha, srb_t *sp) +{ + atomic_inc(&sp->ref_count); + + if (atomic_read(&sp->ref_count) > 2) { + qla_printk(KERN_INFO, ha, + "%s(): **** SP->ref_count greater than two\n", + __func__); + DEBUG2(BUG();) + + return; + } +} + static inline void qla2x00_delete_from_done_queue(scsi_qla_host_t *dest_ha, srb_t *sp) { @@ -1663,7 +1663,7 @@ qla2xxx_eh_host_reset(struct scsi_cmnd * goto out; /* Waiting for our command in done_queue to be returned to OS.*/ - if (qla2x00_eh_wait_for_pending_commands(ha)) + if (!qla2x00_eh_wait_for_pending_commands(ha)) rval = FAILED; out: @@ -1784,7 +1784,7 @@ qla2xxx_slave_configure(struct scsi_devi ql2xmaxqdepth = queue_depth; - scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth); + scsi_activate_tcq(sdev, queue_depth); qla_printk(KERN_INFO, ha, "scsi(%d:%d:%d:%d): Enabled tagged queuing, queue " @@ -3590,7 +3590,7 @@ qla2x00_get_new_sp(scsi_qla_host_t *ha) { srb_t *sp; - sp = mempool_alloc(ha->srb_mempool, GFP_KERNEL); + sp = mempool_alloc(ha->srb_mempool, GFP_ATOMIC); if (sp) atomic_set(&sp->ref_count, 1); return (sp); @@ -4517,3 +4517,4 @@ module_exit(qla2x00_module_exit); MODULE_AUTHOR("QLogic Corporation"); MODULE_DESCRIPTION("QLogic Fibre Channel HBA Driver"); MODULE_LICENSE("GPL"); +MODULE_VERSION(QLA2XXX_VERSION); --- linux-2.6.9-rc1/drivers/scsi/qla2xxx/qla_rscn.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/drivers/scsi/qla2xxx/qla_rscn.c 2004-09-03 00:56:52.269214520 -0700 @@ -242,6 +242,20 @@ qla2x00_free_iodesc(struct io_descriptor } /** + * qla2x00_remove_iodesc_timer() - Remove an active timer from an IO descriptor. + * @iodesc: io descriptor + */ +static inline void +qla2x00_remove_iodesc_timer(struct io_descriptor *iodesc) +{ + if (iodesc->timer.function != NULL) { + del_timer_sync(&iodesc->timer); + iodesc->timer.data = (unsigned long) NULL; + iodesc->timer.function = NULL; + } +} + +/** * qla2x00_init_io_descriptors() - Initialize the pool of IO descriptors. * @ha: HA context */ @@ -311,20 +325,6 @@ qla2x00_add_iodesc_timer(struct io_descr add_timer(&iodesc->timer); } -/** - * qla2x00_remove_iodesc_timer() - Remove an active timer from an IO descriptor. - * @iodesc: io descriptor - */ -static inline void -qla2x00_remove_iodesc_timer(struct io_descriptor *iodesc) -{ - if (iodesc->timer.function != NULL) { - del_timer_sync(&iodesc->timer); - iodesc->timer.data = (unsigned long) NULL; - iodesc->timer.function = NULL; - } -} - /** * IO descriptor support routines. **/ --- linux-2.6.9-rc1/drivers/scsi/qla2xxx/qla_version.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/drivers/scsi/qla2xxx/qla_version.h 2004-09-02 20:51:52.000000000 -0700 @@ -19,9 +19,9 @@ /* * Driver version */ -#define QLA2XXX_VERSION "8.00.00b14-k" +#define QLA2XXX_VERSION "8.00.00b15-k" #define QLA_DRIVER_MAJOR_VER 8 #define QLA_DRIVER_MINOR_VER 0 #define QLA_DRIVER_PATCH_VER 0 -#define QLA_DRIVER_BETA_VER 14 +#define QLA_DRIVER_BETA_VER 15 --- linux-2.6.9-rc1/drivers/scsi/qlogicfc.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/drivers/scsi/qlogicfc.c 2004-09-03 00:56:56.030642696 -0700 @@ -815,9 +815,11 @@ int isp2x00_detect(Scsi_Host_Template * some time before recognizing it is attached to a fabric */ #if ISP2x00_FABRIC - for (wait_time = jiffies + 5 * HZ; time_before(jiffies, wait_time);) { - barrier(); - cpu_relax(); + if (hosts) { + for (wait_time = jiffies + 5 * HZ; time_before(jiffies, wait_time);) { + barrier(); + cpu_relax(); + } } #endif --- linux-2.6.9-rc1/drivers/scsi/sata_nv.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/scsi/sata_nv.c 2004-09-02 20:51:52.000000000 -0700 @@ -20,6 +20,10 @@ * If you do not delete the provisions above, a recipient may use your * version of this file under either the OSL or the GPL. * + * 0.03 + * - Fixed a bug where the hotplug handlers for non-CK804/MCP04 were using + * mmio_base, which is only set for the CK804/MCP04 case. + * * 0.02 * - Added support for CK804 SATA controller. * @@ -40,7 +44,7 @@ #include #define DRV_NAME "sata_nv" -#define DRV_VERSION "0.02" +#define DRV_VERSION "0.03" #define NV_PORTS 2 #define NV_PIO_MASK 0x1f @@ -422,33 +426,33 @@ static void nv_enable_hotplug(struct ata u8 intr_mask; outb(NV_INT_STATUS_HOTPLUG, - (unsigned long)probe_ent->mmio_base + NV_INT_STATUS); + probe_ent->port[0].scr_addr + NV_INT_STATUS); - intr_mask = inb((unsigned long)probe_ent->mmio_base + NV_INT_ENABLE); + intr_mask = inb(probe_ent->port[0].scr_addr + NV_INT_ENABLE); intr_mask |= NV_INT_ENABLE_HOTPLUG; - outb(intr_mask, (unsigned long)probe_ent->mmio_base + NV_INT_ENABLE); + outb(intr_mask, probe_ent->port[0].scr_addr + NV_INT_ENABLE); } static void nv_disable_hotplug(struct ata_host_set *host_set) { u8 intr_mask; - intr_mask = inb((unsigned long)host_set->mmio_base + NV_INT_ENABLE); + intr_mask = inb(host_set->ports[0]->ioaddr.scr_addr + NV_INT_ENABLE); intr_mask &= ~(NV_INT_ENABLE_HOTPLUG); - outb(intr_mask, (unsigned long)host_set->mmio_base + NV_INT_ENABLE); + outb(intr_mask, host_set->ports[0]->ioaddr.scr_addr + NV_INT_ENABLE); } static void nv_check_hotplug(struct ata_host_set *host_set) { u8 intr_status; - intr_status = inb((unsigned long)host_set->mmio_base + NV_INT_STATUS); + intr_status = inb(host_set->ports[0]->ioaddr.scr_addr + NV_INT_STATUS); // Clear interrupt status. - outb(0xff, (unsigned long)host_set->mmio_base + NV_INT_STATUS); + outb(0xff, host_set->ports[0]->ioaddr.scr_addr + NV_INT_STATUS); if (intr_status & NV_INT_STATUS_HOTPLUG) { if (intr_status & NV_INT_STATUS_PDEV_ADDED) --- linux-2.6.9-rc1/drivers/scsi/scsi.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/scsi/scsi.c 2004-09-02 20:51:52.000000000 -0700 @@ -897,15 +897,16 @@ void scsi_adjust_queue_depth(struct scsi */ if (tags <= 0) return; - /* - * Limit max queue depth on a single lun to 256 for now. Remember, - * we allocate a struct scsi_command for each of these and keep it - * around forever. Too deep of a depth just wastes memory. - */ - if (tags > 256) - return; spin_lock_irqsave(&device_request_lock, flags); + spin_lock(sdev->request_queue->queue_lock); + + /* Check to see if the queue is managed by the block layer + * if it is, and we fail to adjust the depth, exit */ + if (blk_queue_tagged(sdev->request_queue) && + blk_queue_resize_tags(sdev->request_queue, tags) != 0) + goto out; + sdev->queue_depth = tags; switch (tagged) { case MSG_ORDERED_TAG: @@ -926,6 +927,8 @@ void scsi_adjust_queue_depth(struct scsi sdev->queue_depth = tags; break; } + out: + spin_unlock(sdev->request_queue->queue_lock); spin_unlock_irqrestore(&device_request_lock, flags); } --- linux-2.6.9-rc1/drivers/scsi/scsi_error.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/drivers/scsi/scsi_error.c 2004-09-02 20:51:52.000000000 -0700 @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include @@ -42,8 +42,8 @@ * These should *probably* be handled by the host itself. * Since it is allowed to sleep, it probably should. */ -#define BUS_RESET_SETTLE_TIME (10*HZ) -#define HOST_RESET_SETTLE_TIME (10*HZ) +#define BUS_RESET_SETTLE_TIME (10) +#define HOST_RESET_SETTLE_TIME (10) /* called with shost->host_lock held */ void scsi_eh_wakeup(struct Scsi_Host *shost) @@ -643,15 +643,13 @@ static void scsi_eh_finish_cmd(struct sc * Notes: * This has the unfortunate side effect that if a shost adapter does * not automatically request sense information, that we end up shutting - * it down before we request it. All shosts should be doing this - * anyways, so for now all I have to say is tough noogies if you end up - * in here. On second thought, this is probably a good idea. We - * *really* want to give authors an incentive to automatically request - * this. + * it down before we request it. * - * In 2.5 this capability will be going away. + * All drivers should request sense information internally these days, + * so for now all I have to say is tough noogies if you end up in here. * - * Really? --hch + * XXX: Long term this code should go away, but that needs an audit of + * all LLDDs first. **/ static int scsi_eh_get_sense(struct list_head *work_q, struct list_head *done_q) @@ -1044,7 +1042,7 @@ static int scsi_try_bus_reset(struct scs if (rtn == SUCCESS) { if (!scmd->device->host->hostt->skip_settle_delay) - scsi_sleep(BUS_RESET_SETTLE_TIME); + ssleep(BUS_RESET_SETTLE_TIME); spin_lock_irqsave(scmd->device->host->host_lock, flags); scsi_report_bus_reset(scmd->device->host, scmd->device->channel); spin_unlock_irqrestore(scmd->device->host->host_lock, flags); @@ -1076,7 +1074,7 @@ static int scsi_try_host_reset(struct sc if (rtn == SUCCESS) { if (!scmd->device->host->hostt->skip_settle_delay) - scsi_sleep(HOST_RESET_SETTLE_TIME); + ssleep(HOST_RESET_SETTLE_TIME); spin_lock_irqsave(scmd->device->host->host_lock, flags); scsi_report_bus_reset(scmd->device->host, scmd->device->channel); spin_unlock_irqrestore(scmd->device->host->host_lock, flags); @@ -1216,43 +1214,6 @@ static void scsi_eh_offline_sdevs(struct } /** - * scsi_sleep_done - timer function for scsi_sleep - * @sem: semphore to signal - * - **/ -static void scsi_sleep_done(unsigned long data) -{ - struct semaphore *sem = (struct semaphore *)data; - - if (sem) - up(sem); -} - -/** - * scsi_sleep - sleep for specified timeout - * @timeout: timeout value - * - **/ -void scsi_sleep(int timeout) -{ - DECLARE_MUTEX_LOCKED(sem); - struct timer_list timer; - - init_timer(&timer); - timer.data = (unsigned long)&sem; - timer.expires = jiffies + timeout; - timer.function = (void (*)(unsigned long))scsi_sleep_done; - - SCSI_LOG_ERROR_RECOVERY(5, printk("sleeping for timer tics %d\n", - timeout)); - - add_timer(&timer); - - down(&sem); - del_timer(&timer); -} - -/** * scsi_decide_disposition - Disposition a cmd on return from LLD. * @scmd: SCSI cmd to examine. * @@ -1639,8 +1600,6 @@ int scsi_error_handler(void *data) int rtn; DECLARE_MUTEX_LOCKED(sem); - lock_kernel(); - /* * Flush resources */ @@ -1652,8 +1611,6 @@ int scsi_error_handler(void *data) shost->eh_wait = &sem; shost->ehandler = current; - unlock_kernel(); - /* * Wake up the thread that created us. */ --- linux-2.6.9-rc1/drivers/scsi/scsi_ioctl.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/drivers/scsi/scsi_ioctl.c 2004-09-02 20:51:52.000000000 -0700 @@ -391,6 +391,21 @@ int scsi_ioctl(struct scsi_device *sdev, if (!scsi_block_when_processing_errors(sdev)) return -ENODEV; + /* Check for deprecated ioctls ... all the ioctls which don't + * follow the new unique numbering scheme are deprecated */ + switch (cmd) { + case SCSI_IOCTL_SEND_COMMAND: + case SCSI_IOCTL_TEST_UNIT_READY: + case SCSI_IOCTL_BENCHMARK_COMMAND: + case SCSI_IOCTL_SYNC: + case SCSI_IOCTL_START_UNIT: + case SCSI_IOCTL_STOP_UNIT: + printk(KERN_WARNING "program %s is using a deprecated SCSI ioctl, please convert it to SG_IO\n", current->comm); + break; + default: + break; + } + switch (cmd) { case SCSI_IOCTL_GET_IDLUN: if (verify_area(VERIFY_WRITE, arg, sizeof(struct scsi_idlun))) @@ -417,12 +432,8 @@ int scsi_ioctl(struct scsi_device *sdev, case SCSI_IOCTL_DOORUNLOCK: return scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW); case SCSI_IOCTL_TEST_UNIT_READY: - scsi_cmd[0] = TEST_UNIT_READY; - scsi_cmd[1] = 0; - scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0; - scsi_cmd[4] = 0; - return ioctl_internal_command(sdev, scsi_cmd, - IOCTL_NORMAL_TIMEOUT, NORMAL_RETRIES); + return scsi_test_unit_ready(sdev, IOCTL_NORMAL_TIMEOUT, + NORMAL_RETRIES); case SCSI_IOCTL_START_UNIT: scsi_cmd[0] = START_STOP; scsi_cmd[1] = 0; --- linux-2.6.9-rc1/drivers/scsi/scsiiom.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/drivers/scsi/scsiiom.c 2004-09-02 20:51:52.000000000 -0700 @@ -213,8 +213,17 @@ dc390_dma_intr (struct dc390_acb* pACB) } #endif + +static void __inline__ +dc390_InvalidCmd(struct dc390_acb* pACB) +{ + if (pACB->pActiveDCB->pActiveSRB->SRBState & (SRB_START_ | SRB_MSGOUT)) + DC390_write8(ScsiCmd, CLEAR_FIFO_CMD); +} + + static irqreturn_t __inline__ -DC390_Interrupt( int irq, void *dev_id, struct pt_regs *regs) +DC390_Interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct dc390_acb *pACB, *pACB2; struct dc390_dcb *pDCB; @@ -594,7 +603,7 @@ dc390_MsgIn_reject (struct dc390_acb* pA } /* abort command */ -static void __inline__ +static void dc390_EnableMsgOut_Abort ( struct dc390_acb* pACB, struct dc390_srb* pSRB ) { pSRB->MsgOutBuf[0] = ABORT; @@ -890,14 +899,22 @@ dc390_DataIO_Comm( struct dc390_acb* pAC if (pSRB == pACB->pTmpSRB) { - if (pDCB) printk (KERN_ERR "DC390: pSRB == pTmpSRB! (TagQ Error?) (%02i-%i)\n", - pDCB->TargetID, pDCB->TargetLUN); - else printk (KERN_ERR "DC390: pSRB == pTmpSRB! (TagQ Error?) (DCB 0!)\n"); - - pSRB->pSRBDCB = pDCB; - dc390_EnableMsgOut_Abort (pACB, pSRB); - if (pDCB) pDCB->DCBFlag |= ABORT_DEV; - return; + if (pDCB) + printk(KERN_ERR "DC390: pSRB == pTmpSRB! (TagQ Error?) (%02i-%i)\n", pDCB->TargetID, pDCB->TargetLUN); + else + printk(KERN_ERR "DC390: pSRB == pTmpSRB! (TagQ Error?) (DCB 0!)\n"); + + /* Try to recover - some broken disks react badly to tagged INQUIRY */ + if (pDCB && pACB->scan_devices && pDCB->GoingSRBCnt == 1) { + pSRB = pDCB->pGoingSRB; + pDCB->pActiveSRB = pSRB; + } else { + pSRB->pSRBDCB = pDCB; + dc390_EnableMsgOut_Abort(pACB, pSRB); + if (pDCB) + pDCB->DCBFlag |= ABORT_DEV; + return; + } } if( pSRB->SGIndex < pSRB->SGcount ) @@ -1325,6 +1342,35 @@ dc390_add_dev (struct dc390_acb* pACB, s } +static void __inline__ +dc390_RequestSense(struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* pSRB) +{ + struct scsi_cmnd *pcmd; + + pcmd = pSRB->pcmd; + + REMOVABLEDEBUG(printk(KERN_INFO "DC390: RequestSense(Cmd %02x, Id %02x, LUN %02x)\n",\ + pcmd->cmnd[0], pDCB->TargetID, pDCB->TargetLUN)); + + pSRB->SRBFlag |= AUTO_REQSENSE; + pSRB->SavedSGCount = pcmd->use_sg; + pSRB->SavedTotXLen = pSRB->TotalXferredLen; + pSRB->AdaptStatus = 0; + pSRB->TargetStatus = 0; /* CHECK_CONDITION<<1; */ + + /* We are called from SRBdone, original PCI mapping has been removed + * already, new one is set up from StartSCSI */ + pSRB->SGIndex = 0; + + pSRB->TotalXferredLen = 0; + pSRB->SGToBeXferLen = 0; + if (dc390_StartSCSI(pACB, pDCB, pSRB)) { + dc390_Going_to_Waiting(pDCB, pSRB); + dc390_waiting_timer(pACB, HZ/5); + } +} + + static void dc390_SRBdone( struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* pSRB ) { @@ -1352,26 +1398,7 @@ dc390_SRBdone( struct dc390_acb* pACB, s pSRB->SRBFlag &= ~AUTO_REQSENSE; pSRB->AdaptStatus = 0; pSRB->TargetStatus = CHECK_CONDITION << 1; -#ifdef DC390_REMOVABLEDEBUG - switch (pcmd->sense_buffer[2] & 0x0f) - { - case NOT_READY: printk (KERN_INFO "DC390: ReqSense: NOT_READY (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n", - pcmd->cmnd[0], pDCB->TargetID, pDCB->TargetLUN, - status, pACB->scan_devices); break; - case UNIT_ATTENTION: printk (KERN_INFO "DC390: ReqSense: UNIT_ATTENTION (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n", - pcmd->cmnd[0], pDCB->TargetID, pDCB->TargetLUN, - status, pACB->scan_devices); break; - case ILLEGAL_REQUEST: printk (KERN_INFO "DC390: ReqSense: ILLEGAL_REQUEST (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n", - pcmd->cmnd[0], pDCB->TargetID, pDCB->TargetLUN, - status, pACB->scan_devices); break; - case MEDIUM_ERROR: printk (KERN_INFO "DC390: ReqSense: MEDIUM_ERROR (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n", - pcmd->cmnd[0], pDCB->TargetID, pDCB->TargetLUN, - status, pACB->scan_devices); break; - case HARDWARE_ERROR: printk (KERN_INFO "DC390: ReqSense: HARDWARE_ERROR (Cmnd = 0x%02x, Dev = %i-%i, Stat = %i, Scan = %i)\n", - pcmd->cmnd[0], pDCB->TargetID, pDCB->TargetLUN, - status, pACB->scan_devices); break; - } -#endif + //pcmd->result = MK_RES(DRIVER_SENSE,DID_OK,0,status); if (status == (CHECK_CONDITION << 1)) { @@ -1525,23 +1552,6 @@ dc390_SRBdone( struct dc390_acb* pACB, s pDCB->Inquiry7 = ptr->Flags; ckc_e: - if( pACB->scan_devices ) - { - if( pcmd->cmnd[0] == TEST_UNIT_READY || - pcmd->cmnd[0] == INQUIRY) - { -#ifdef DC390_DEBUG0 - printk (KERN_INFO "DC390: %s: result: %08x", - (pcmd->cmnd[0] == INQUIRY? "INQUIRY": "TEST_UNIT_READY"), - pcmd->result); - if (pcmd->result & (DRIVER_SENSE << 24)) printk (" (sense: %02x %02x %02x %02x)\n", - pcmd->sense_buffer[0], pcmd->sense_buffer[1], - pcmd->sense_buffer[2], pcmd->sense_buffer[3]); - else printk ("\n"); -#endif - } - } - if( pcmd->cmnd[0] == INQUIRY && (pcmd->result == (DID_OK << 16) || status_byte(pcmd->result) & CHECK_CONDITION) ) { @@ -1586,18 +1596,6 @@ dc390_DoingSRB_Done(struct dc390_acb* pA psrb2 = psrb->pNextSRB; pcmd = psrb->pcmd; dc390_Free_insert (pACB, psrb); -#ifndef USE_NEW_EH - /* New EH will crash on being given timed out cmnds */ - if (pcmd == cmd) - pcmd->result = MK_RES(0,DID_ABORT,0,0); - else - pcmd->result = MK_RES(0,DID_RESET,0,0); - -/* ReleaseSRB( pDCB, pSRB ); */ - - DEBUG0(printk (KERN_DEBUG "DC390: DoingSRB_Done: done pid %li\n", pcmd->pid)); - pcmd->scsi_done( pcmd ); -#endif psrb = psrb2; } pdcb->GoingSRBCnt = 0; @@ -1654,51 +1652,3 @@ dc390_ScsiRstDetect( struct dc390_acb* p } return; } - - -static void __inline__ -dc390_RequestSense( struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* pSRB ) -{ - struct scsi_cmnd *pcmd; - - pcmd = pSRB->pcmd; - - REMOVABLEDEBUG(printk (KERN_INFO "DC390: RequestSense (Cmd %02x, Id %02x, LUN %02x)\n",\ - pcmd->cmnd[0], pDCB->TargetID, pDCB->TargetLUN)); - - pSRB->SRBFlag |= AUTO_REQSENSE; - //pSRB->Segment0[0] = (u32) pSRB->CmdBlock[0]; - //pSRB->Segment0[1] = (u32) pSRB->CmdBlock[4]; - //pSRB->Segment1[0] = ((u32)(pcmd->cmd_len) << 8) + pSRB->SGcount; - //pSRB->Segment1[1] = pSRB->TotalXferredLen; - pSRB->SavedSGCount = pcmd->use_sg; - pSRB->SavedTotXLen = pSRB->TotalXferredLen; - pSRB->AdaptStatus = 0; - pSRB->TargetStatus = 0; /* CHECK_CONDITION<<1; */ - - /* We are called from SRBdone, original PCI mapping has been removed - * already, new one is set up from StartSCSI */ - pSRB->SGIndex = 0; - - //pSRB->CmdBlock[0] = REQUEST_SENSE; - //pSRB->CmdBlock[1] = pDCB->TargetLUN << 5; - //(u16) pSRB->CmdBlock[2] = 0; - //(u16) pSRB->CmdBlock[4] = sizeof(pcmd->sense_buffer); - //pSRB->ScsiCmdLen = 6; - - pSRB->TotalXferredLen = 0; - pSRB->SGToBeXferLen = 0; - if( dc390_StartSCSI( pACB, pDCB, pSRB ) ) { - dc390_Going_to_Waiting ( pDCB, pSRB ); - dc390_waiting_timer (pACB, HZ/5); - } -} - - - -static void __inline__ -dc390_InvalidCmd( struct dc390_acb* pACB ) -{ - if( pACB->pActiveDCB->pActiveSRB->SRBState & (SRB_START_+SRB_MSGOUT) ) - DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); -} --- linux-2.6.9-rc1/drivers/scsi/scsi_lib.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/scsi/scsi_lib.c 2004-09-02 20:51:52.000000000 -0700 @@ -1572,6 +1572,34 @@ scsi_mode_sense(struct scsi_device *sdev return ret; } +int +scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries) +{ + struct scsi_request *sreq; + char cmd[] = { + TEST_UNIT_READY, 0, 0, 0, 0, 0, + }; + int result; + + sreq = scsi_allocate_request(sdev, GFP_KERNEL); + if (!sreq) + return -ENOMEM; + + sreq->sr_data_direction = DMA_NONE; + scsi_wait_req(sreq, cmd, NULL, 0, timeout, retries); + + if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) && + (sreq->sr_sense_buffer[2] & 0x0f) == UNIT_ATTENTION && + sdev->removable) { + sdev->changed = 1; + sreq->sr_result = 0; + } + result = sreq->sr_result; + scsi_release_request(sreq); + return result; +} +EXPORT_SYMBOL(scsi_test_unit_ready); + /** * scsi_device_set_state - Take the given device through the device * state model. --- linux-2.6.9-rc1/drivers/scsi/scsi_scan.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/scsi/scsi_scan.c 2004-09-02 20:51:52.000000000 -0700 @@ -200,7 +200,7 @@ static void print_inquiry(unsigned char * scsi_Device pointer, or NULL on failure. **/ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost, - uint channel, uint id, uint lun) + uint channel, uint id, uint lun, void *hostdata) { struct scsi_device *sdev, *device; unsigned long flags; @@ -224,6 +224,8 @@ static struct scsi_device *scsi_alloc_sd INIT_LIST_HEAD(&sdev->starved_entry); spin_lock_init(&sdev->list_lock); + /* usually NULL and set by ->slave_alloc instead */ + sdev->hostdata = hostdata; /* if the device needs this changing, it may do so in the * slave_configure function */ @@ -697,7 +699,7 @@ static int scsi_add_lun(struct scsi_devi **/ static int scsi_probe_and_add_lun(struct Scsi_Host *host, uint channel, uint id, uint lun, int *bflagsp, - struct scsi_device **sdevp, int rescan) + struct scsi_device **sdevp, int rescan, void *hostdata) { struct scsi_device *sdev; struct scsi_request *sreq; @@ -726,7 +728,7 @@ static int scsi_probe_and_add_lun(struct } } - sdev = scsi_alloc_sdev(host, channel, id, lun); + sdev = scsi_alloc_sdev(host, channel, id, lun, hostdata); if (!sdev) goto out; sreq = scsi_allocate_request(sdev, GFP_ATOMIC); @@ -874,7 +876,7 @@ static void scsi_sequential_lun_scan(str */ for (lun = 1; lun < max_dev_lun; ++lun) if ((scsi_probe_and_add_lun(shost, channel, id, lun, - NULL, NULL, rescan) != SCSI_SCAN_LUN_PRESENT) && + NULL, NULL, rescan, NULL) != SCSI_SCAN_LUN_PRESENT) && !sparse_lun) return; } @@ -1085,7 +1087,7 @@ static int scsi_report_lun_scan(struct s int res; res = scsi_probe_and_add_lun(sdev->host, sdev->channel, - sdev->id, lun, NULL, NULL, rescan); + sdev->id, lun, NULL, NULL, rescan, NULL); if (res == SCSI_SCAN_NO_RESPONSE) { /* * Got some results, but now none, abort. @@ -1111,14 +1113,15 @@ static int scsi_report_lun_scan(struct s return 0; } -struct scsi_device *scsi_add_device(struct Scsi_Host *shost, - uint channel, uint id, uint lun) +struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel, + uint id, uint lun, void *hostdata) { struct scsi_device *sdev; int res; down(&shost->scan_mutex); - res = scsi_probe_and_add_lun(shost, channel, id, lun, NULL, &sdev, 1); + res = scsi_probe_and_add_lun(shost, channel, id, lun, NULL, + &sdev, 1, hostdata); if (res != SCSI_SCAN_LUN_PRESENT) sdev = ERR_PTR(-ENODEV); up(&shost->scan_mutex); @@ -1178,7 +1181,7 @@ static void scsi_scan_target(struct Scsi * Scan for a specific host/chan/id/lun. */ scsi_probe_and_add_lun(shost, channel, id, lun, NULL, NULL, - rescan); + rescan, NULL); return; } @@ -1187,7 +1190,7 @@ static void scsi_scan_target(struct Scsi * would not configure LUN 0 until all LUNs are scanned. */ res = scsi_probe_and_add_lun(shost, channel, id, 0, &bflags, &sdev, - rescan); + rescan, NULL); if (res == SCSI_SCAN_LUN_PRESENT) { if (scsi_report_lun_scan(sdev, bflags, rescan) != 0) /* @@ -1316,7 +1319,7 @@ struct scsi_device *scsi_get_host_dev(st { struct scsi_device *sdev; - sdev = scsi_alloc_sdev(shost, 0, shost->this_id, 0); + sdev = scsi_alloc_sdev(shost, 0, shost->this_id, 0, NULL); if (sdev) { sdev->borken = 0; } --- linux-2.6.9-rc1/drivers/scsi/scsi_syms.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/scsi/scsi_syms.c 2004-09-02 20:51:52.000000000 -0700 @@ -69,11 +69,9 @@ EXPORT_SYMBOL(scsi_track_queue_full); EXPORT_SYMBOL(scsi_get_host_dev); EXPORT_SYMBOL(scsi_free_host_dev); -EXPORT_SYMBOL(scsi_sleep); - EXPORT_SYMBOL(scsi_io_completion); -EXPORT_SYMBOL(scsi_add_device); +EXPORT_SYMBOL(__scsi_add_device); EXPORT_SYMBOL(scsi_remove_device); EXPORT_SYMBOL(scsi_device_cancel); --- linux-2.6.9-rc1/drivers/scsi/scsi_sysfs.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/scsi/scsi_sysfs.c 2004-09-02 20:51:52.000000000 -0700 @@ -525,8 +525,11 @@ int scsi_sysfs_add_sdev(struct scsi_devi **/ void scsi_remove_device(struct scsi_device *sdev) { + struct Scsi_Host *shost = sdev->host; + + down(&shost->scan_mutex); if (scsi_device_set_state(sdev, SDEV_CANCEL) != 0) - return; + goto out; class_device_unregister(&sdev->sdev_classdev); if (sdev->transport_classdev.class) @@ -538,6 +541,9 @@ void scsi_remove_device(struct scsi_devi if (sdev->host->transportt->cleanup) sdev->host->transportt->cleanup(sdev); put_device(&sdev->sdev_gendev); + +out: + up(&shost->scan_mutex); } int scsi_register_driver(struct device_driver *drv) --- linux-2.6.9-rc1/drivers/scsi/sd.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/scsi/sd.c 2004-09-02 20:51:52.000000000 -0700 @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -62,12 +63,18 @@ #include "scsi_logging.h" - /* - * Remaining dev_t-handling stuff + * More than enough for everybody ;) The huge number of majors + * is a leftover from 16bit dev_t days, we don't really need that + * much numberspace. */ #define SD_MAJORS 16 -#define SD_DISKS 32768 /* anything between 256 and 262144 */ + +/* + * This is limited by the naming scheme enforced in sd_probe, + * add another character to it if you really need more disks. + */ +#define SD_MAX_DISKS (((26 * 26) + 26 + 1) * 26) /* * Time out in seconds for disks and Magneto-opticals (which are slower). @@ -96,8 +103,7 @@ struct scsi_disk { unsigned RCD : 1; /* state of disk RCD bit, unused */ }; - -static unsigned long sd_index_bits[SD_DISKS / BITS_PER_LONG]; +static DEFINE_IDR(sd_index_idr); static spinlock_t sd_index_lock = SPIN_LOCK_UNLOCKED; /* This semaphore is used to mediate the 0->1 reference get in the @@ -130,7 +136,8 @@ static struct scsi_driver sd_template = .issue_flush = sd_issue_flush, }; -/* Device no to disk mapping: +/* + * Device no to disk mapping: * * major disc2 disc p1 * |............|.............|....|....| <- dev_t @@ -143,7 +150,6 @@ static struct scsi_driver sd_template = * As we stay compatible with our numbering scheme, we can reuse * the well-know SCSI majors 8, 65--71, 136--143. */ - static int sd_major(int major_idx) { switch (major_idx) { @@ -159,14 +165,6 @@ static int sd_major(int major_idx) } } -static unsigned int make_sd_dev(unsigned int sd_nr, unsigned int part) -{ - return (part & 0xf) | ((sd_nr & 0xf) << 4) | - (sd_major((sd_nr & 0xf0) >> 4) << 20) | (sd_nr & 0xfff00); -} - -/* reverse mapping dev -> (sd_nr, part) not currently needed */ - #define to_scsi_disk(obj) container_of(obj,struct scsi_disk,kref) static inline struct scsi_disk *scsi_disk(struct gendisk *disk) @@ -182,16 +180,14 @@ static struct scsi_disk *scsi_disk_get(s if (disk->private_data == NULL) goto out; sdkp = scsi_disk(disk); - if (!kref_get(&sdkp->kref)) - goto out_sdkp; + kref_get(&sdkp->kref); if (scsi_device_get(sdkp->device)) goto out_put; up(&sd_ref_sem); return sdkp; out_put: - kref_put(&sdkp->kref); - out_sdkp: + kref_put(&sdkp->kref, scsi_disk_release); sdkp = NULL; out: up(&sd_ref_sem); @@ -202,7 +198,7 @@ static void scsi_disk_put(struct scsi_di { down(&sd_ref_sem); scsi_device_put(sdkp->device); - kref_put(&sdkp->kref); + kref_put(&sdkp->kref, scsi_disk_release); up(&sd_ref_sem); } @@ -650,7 +646,7 @@ static int sd_media_changed(struct gendi */ retval = -ENODEV; if (scsi_block_when_processing_errors(sdp)) - retval = scsi_ioctl(sdp, SCSI_IOCTL_TEST_UNIT_READY, NULL); + retval = scsi_test_unit_ready(sdp, SD_TIMEOUT, SD_MAX_RETRIES); /* * Unable to test, unit probably not ready. This usually @@ -1405,7 +1401,7 @@ static int sd_probe(struct device *dev) struct scsi_disk *sdkp; struct gendisk *gd; u32 index; - int error, devno; + int error; error = -ENODEV; if ((sdp->type != TYPE_DISK) && (sdp->type != TYPE_MOD)) @@ -1420,27 +1416,23 @@ static int sd_probe(struct device *dev) goto out; memset (sdkp, 0, sizeof(*sdkp)); - kref_init(&sdkp->kref, scsi_disk_release); + kref_init(&sdkp->kref); - /* Note: We can accomodate 64 partitions, but the genhd code - * assumes partitions allocate consecutive minors, which they don't. - * So for now stay with max 16 partitions and leave two spare bits. - * Later, we may change the genhd code and the alloc_disk() call - * and the ->minors assignment here. KG, 2004-02-10 - */ gd = alloc_disk(16); if (!gd) goto out_free; + if (!idr_pre_get(&sd_index_idr, GFP_KERNEL)) + goto out_put; + spin_lock(&sd_index_lock); - index = find_first_zero_bit(sd_index_bits, SD_DISKS); - if (index == SD_DISKS) { - spin_unlock(&sd_index_lock); + error = idr_get_new(&sd_index_idr, NULL, &index); + spin_unlock(&sd_index_lock); + + if (index >= SD_MAX_DISKS) error = -EBUSY; + if (error) goto out_put; - } - __set_bit(index, sd_index_bits); - spin_unlock(&sd_index_lock); sdkp->device = sdp; sdkp->driver = &sd_template; @@ -1455,15 +1447,14 @@ static int sd_probe(struct device *dev) sdp->timeout = SD_MOD_TIMEOUT; } - devno = make_sd_dev(index, 0); - gd->major = MAJOR(devno); - gd->first_minor = MINOR(devno); + gd->major = sd_major((index & 0xf0) >> 4); + gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00); gd->minors = 16; gd->fops = &sd_fops; if (index < 26) { sprintf(gd->disk_name, "sd%c", 'a' + index % 26); - } else if (index < (26*27)) { + } else if (index < (26 + 1) * 26) { sprintf(gd->disk_name, "sd%c%c", 'a' + index / 26 - 1,'a' + index % 26); } else { @@ -1522,7 +1513,7 @@ static int sd_remove(struct device *dev) del_gendisk(sdkp->disk); sd_shutdown(dev); down(&sd_ref_sem); - kref_put(&sdkp->kref); + kref_put(&sdkp->kref, scsi_disk_release); up(&sd_ref_sem); return 0; @@ -1543,7 +1534,7 @@ static void scsi_disk_release(struct kre struct gendisk *disk = sdkp->disk; spin_lock(&sd_index_lock); - clear_bit(sdkp->index, sd_index_bits); + idr_remove(&sd_index_idr, sdkp->index); spin_unlock(&sd_index_lock); disk->private_data = NULL; --- linux-2.6.9-rc1/drivers/scsi/sg.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/drivers/scsi/sg.c 2004-09-02 20:51:52.000000000 -0700 @@ -47,8 +47,9 @@ static int sg_version_num = 30531; /* 2 #include #include #include - #include +#include + #include "scsi.h" #include #include @@ -205,8 +206,6 @@ static Sg_request *sg_get_rq_mark(Sg_fd static Sg_request *sg_add_request(Sg_fd * sfp); static int sg_remove_request(Sg_fd * sfp, Sg_request * srp); static int sg_res_in_use(Sg_fd * sfp); -static int sg_ms_to_jif(unsigned int msecs); -static inline unsigned sg_jif_to_ms(int jifs); static int sg_allow_access(unsigned char opcode, char dev_type); static int sg_build_direct(Sg_request * srp, Sg_fd * sfp, int dxfer_len); static Sg_device *sg_get_dev(int dev); @@ -611,7 +610,7 @@ sg_new_write(Sg_fd * sfp, const char __u return -EBUSY; /* reserve buffer already being used */ } } - timeout = sg_ms_to_jif(srp->header.timeout); + timeout = msecs_to_jiffies(srp->header.timeout); if ((!hp->cmdp) || (hp->cmd_len < 6) || (hp->cmd_len > sizeof (cmnd))) { sg_remove_request(sfp, srp); return -EMSGSIZE; @@ -719,19 +718,6 @@ sg_common_write(Sg_fd * sfp, Sg_request return 0; } -static inline unsigned -sg_jif_to_ms(int jifs) -{ - if (jifs <= 0) - return 0U; - else { - unsigned int j = (unsigned int) jifs; - return (j < - (UINT_MAX / 1000)) ? ((j * 1000) / HZ) : ((j / HZ) * - 1000); - } -} - static int sg_ioctl(struct inode *inode, struct file *filp, unsigned int cmd_in, unsigned long arg) @@ -941,7 +927,7 @@ sg_ioctl(struct inode *inode, struct fil srp->header.driver_status; rinfo[val].duration = srp->done ? srp->header.duration : - sg_jif_to_ms( + jiffies_to_msecs( jiffies - srp->header.duration); rinfo[val].orphan = srp->orphan; rinfo[val].sg_io_owned = srp->sg_io_owned; @@ -1263,7 +1249,7 @@ sg_cmd_done(Scsi_Cmnd * SCpnt) srp->header.resid = SCpnt->resid; /* N.B. unit of duration changes here from jiffies to millisecs */ srp->header.duration = - sg_jif_to_ms(jiffies - (int) srp->header.duration); + jiffies_to_msecs(jiffies - srp->header.duration); if (0 != SRpnt->sr_result) { memcpy(srp->sense_b, SRpnt->sr_sense_buffer, sizeof (srp->sense_b)); @@ -1547,7 +1533,7 @@ sg_remove(struct class_device *cl_dev) } if (delay) - scsi_sleep(2); /* dirty detach so delay device destruction */ + msleep(10); /* dirty detach so delay device destruction */ } /* Set 'perm' (4th argument) to 0 to disable module_param's definition @@ -2587,17 +2573,6 @@ sg_page_free(char *buff, int size) free_pages((unsigned long) buff, order); } -static int -sg_ms_to_jif(unsigned int msecs) -{ - if ((UINT_MAX / 2U) < msecs) - return INT_MAX; /* special case, set largest possible */ - else - return ((int) msecs < - (INT_MAX / 1000)) ? (((int) msecs * HZ) / 1000) - : (((int) msecs / 1000) * HZ); -} - static unsigned char allow_ops[] = { TEST_UNIT_READY, REQUEST_SENSE, INQUIRY, READ_CAPACITY, READ_BUFFER, READ_6, READ_10, READ_12, MODE_SENSE, MODE_SENSE_10, LOG_SENSE @@ -2960,7 +2935,7 @@ static void sg_proc_debug_helper(struct for (k = 0; (fp = sg_get_nth_sfp(sdp, k)); ++k) { seq_printf(s, " FD(%d): timeout=%dms bufflen=%d " "(res)sgat=%d low_dma=%d\n", k + 1, - sg_jif_to_ms(fp->timeout), + jiffies_to_msecs(fp->timeout), fp->reserve.bufflen, (int) fp->reserve.k_use_sg, (int) fp->low_dma); @@ -2996,8 +2971,8 @@ static void sg_proc_debug_helper(struct seq_printf(s, " dur=%d", hp->duration); else seq_printf(s, " t_o/elap=%d/%d", - new_interface ? hp->timeout : sg_jif_to_ms(fp->timeout), - sg_jif_to_ms(hp->duration ? (jiffies - hp->duration) : 0)); + new_interface ? hp->timeout : jiffies_to_msecs(fp->timeout), + jiffies_to_msecs(hp->duration ? (jiffies - hp->duration) : 0)); seq_printf(s, "ms sgat=%d op=0x%02x\n", usg, (int) srp->data.cmd_opcode); } --- linux-2.6.9-rc1/drivers/scsi/sr.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/drivers/scsi/sr.c 2004-09-03 00:56:49.237675384 -0700 @@ -140,15 +140,13 @@ static inline struct scsi_cd *scsi_cd_ge if (disk->private_data == NULL) goto out; cd = scsi_cd(disk); - if (!kref_get(&cd->kref)) - goto out_null; + kref_get(&cd->kref); if (scsi_device_get(cd->device)) goto out_put; goto out; out_put: - kref_put(&cd->kref); - out_null: + kref_put(&cd->kref, sr_kref_release); cd = NULL; out: up(&sr_ref_sem); @@ -159,7 +157,7 @@ static inline void scsi_cd_put(struct sc { down(&sr_ref_sem); scsi_device_put(cd->device); - kref_put(&cd->kref); + kref_put(&cd->kref, sr_kref_release); up(&sr_ref_sem); } @@ -183,7 +181,7 @@ int sr_media_change(struct cdrom_device_ return -EINVAL; } - retval = scsi_ioctl(cd->device, SCSI_IOCTL_TEST_UNIT_READY, NULL); + retval = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES); if (retval) { /* Unable to test, unit probably not ready. This usually * means there is no disc in the drive. Mark as changed, @@ -379,6 +377,7 @@ static int sr_init_command(struct scsi_c return 0; SCpnt->cmnd[0] = WRITE_10; SCpnt->sc_data_direction = DMA_TO_DEVICE; + cd->cdi.media_written = 1; } else if (rq_data_dir(SCpnt->request) == READ) { SCpnt->cmnd[0] = READ_10; SCpnt->sc_data_direction = DMA_FROM_DEVICE; @@ -576,7 +575,7 @@ static int sr_probe(struct device *dev) goto fail; memset(cd, 0, sizeof(*cd)); - kref_init(&cd->kref, sr_kref_release); + kref_init(&cd->kref); disk = alloc_disk(1); if (!disk) @@ -877,10 +876,10 @@ static void get_capabilities(struct scsi cd->cdi.mask |= CDC_CLOSE_TRAY; */ /* - * if DVD-RAM of MRW-W, we are randomly writeable + * if DVD-RAM, MRW-W or CD-RW, we are randomly writable */ - if ((cd->cdi.mask & (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM)) != - (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM)) { + if ((cd->cdi.mask & (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) != + (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) { cd->device->writeable = 1; } @@ -937,7 +936,7 @@ static int sr_remove(struct device *dev) del_gendisk(cd->disk); down(&sr_ref_sem); - kref_put(&cd->kref); + kref_put(&cd->kref, sr_kref_release); up(&sr_ref_sem); return 0; --- linux-2.6.9-rc1/drivers/scsi/sr_ioctl.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/drivers/scsi/sr_ioctl.c 2004-09-02 20:51:52.000000000 -0700 @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -131,7 +132,7 @@ int sr_do_ioctl(Scsi_CD *cd, struct pack printk(KERN_INFO "%s: CDROM not ready yet.\n", cd->cdi.name); if (retries++ < 10) { /* sleep 2 sec and try again */ - scsi_sleep(2 * HZ); + ssleep(2); goto retry; } else { /* 20 secs are enough? */ --- linux-2.6.9-rc1/drivers/scsi/sun3x_esp.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/drivers/scsi/sun3x_esp.c 2004-09-03 00:57:09.781552240 -0700 @@ -370,7 +370,7 @@ static int sun3x_esp_release(struct Scsi } static Scsi_Host_Template driver_template = { - .proc_name = "esp", + .proc_name = "sun3x_esp", .proc_info = &esp_proc_info, .name = "Sun ESP 100/100a/200", .detect = sun3x_esp_detect, --- linux-2.6.9-rc1/drivers/scsi/sym53c8xx_2/sym_defs.h 2004-08-24 00:02:26.000000000 -0700 +++ 25/drivers/scsi/sym53c8xx_2/sym_defs.h 2004-09-02 20:51:52.000000000 -0700 @@ -53,6 +53,9 @@ #ifndef SYM_DEFS_H #define SYM_DEFS_H +#define SYM_VERSION "2.1.18j" +#define SYM_DRIVER_NAME "sym-" SYM_VERSION + /* * Vendor. */ --- linux-2.6.9-rc1/drivers/scsi/sym53c8xx_2/sym_glue.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/drivers/scsi/sym53c8xx_2/sym_glue.c 2004-09-02 20:51:52.000000000 -0700 @@ -143,13 +143,6 @@ m_addr_t __vtobus(m_pool_ident_t dev_dma } /* - * Driver host data structure. - */ -struct host_data { - struct sym_hcb *ncb; -}; - -/* * Used by the eh thread to wait for command completion. * It is allocated on the eh thread stack. */ @@ -220,47 +213,12 @@ static int __map_scsi_sg_data(struct pci return use_sg; } -static void __sync_scsi_data_for_cpu(struct pci_dev *pdev, struct scsi_cmnd *cmd) -{ - int dma_dir = cmd->sc_data_direction; - - switch(SYM_UCMD_PTR(cmd)->data_mapped) { - case 2: - pci_dma_sync_sg_for_cpu(pdev, cmd->buffer, cmd->use_sg, dma_dir); - break; - case 1: - pci_dma_sync_single_for_cpu(pdev, SYM_UCMD_PTR(cmd)->data_mapping, - cmd->request_bufflen, dma_dir); - break; - } -} - -static void __sync_scsi_data_for_device(struct pci_dev *pdev, struct scsi_cmnd *cmd) -{ - int dma_dir = cmd->sc_data_direction; - - switch(SYM_UCMD_PTR(cmd)->data_mapped) { - case 2: - pci_dma_sync_sg_for_device(pdev, cmd->buffer, cmd->use_sg, dma_dir); - break; - case 1: - pci_dma_sync_single_for_device(pdev, SYM_UCMD_PTR(cmd)->data_mapping, - cmd->request_bufflen, dma_dir); - break; - } -} - #define unmap_scsi_data(np, cmd) \ __unmap_scsi_data(np->s.device, cmd) #define map_scsi_single_data(np, cmd) \ __map_scsi_single_data(np->s.device, cmd) #define map_scsi_sg_data(np, cmd) \ __map_scsi_sg_data(np->s.device, cmd) -#define sync_scsi_data_for_cpu(np, cmd) \ - __sync_scsi_data_for_cpu(np->s.device, cmd) -#define sync_scsi_data_for_device(np, cmd) \ - __sync_scsi_data_for_device(np->s.device, cmd) - /* * Complete a pending CAM CCB. */ @@ -417,27 +375,6 @@ void sym_set_cam_result_error(struct sym /* - * Called on successfull INQUIRY response. - */ -void sym_sniff_inquiry(struct sym_hcb *np, struct scsi_cmnd *cmd, int resid) -{ - int retv; - - if (!cmd || cmd->use_sg) - return; - - sync_scsi_data_for_cpu(np, cmd); - retv = __sym_sniff_inquiry(np, cmd->device->id, cmd->device->lun, - (u_char *) cmd->request_buffer, - cmd->request_bufflen - resid); - sync_scsi_data_for_device(np, cmd); - if (retv < 0) - return; - else if (retv) - sym_update_trans_settings(np, &np->target[cmd->device->id]); -} - -/* * Build the scatter/gather array for an I/O. */ @@ -730,14 +667,15 @@ void sym_log_bus_error(struct sym_hcb *n */ static void sym_requeue_awaiting_cmds(struct sym_hcb *np) { - struct scsi_cmnd *cmd; - struct sym_ucmd *ucp = SYM_UCMD_PTR(cmd); + struct sym_ucmd *ucp; SYM_QUEHEAD tmp_cmdq; int sts; sym_que_move(&np->s.wait_cmdq, &tmp_cmdq); while ((ucp = (struct sym_ucmd *) sym_remque_head(&tmp_cmdq)) != 0) { + struct scsi_cmnd *cmd; + sym_insque_tail(&ucp->link_cmdq, &np->s.busy_cmdq); cmd = SYM_SCMD_PTR(ucp); sts = sym_queue_command(np, cmd); @@ -1118,12 +1056,7 @@ static int sym53c8xx_slave_configure(str np = ((struct host_data *) host->hostdata)->ncb; tp = &np->target[device->id]; - - /* - * Get user settings for transfer parameters. - */ - tp->inq_byte7_valid = (INQ7_SYNC|INQ7_WIDE16); - sym_update_trans_settings(np, tp); + tp->sdev = device; /* * Allocate the LCB if not yet. @@ -2283,6 +2216,7 @@ static int sym_detach(struct sym_hcb *np } MODULE_LICENSE("Dual BSD/GPL"); +MODULE_VERSION(SYM_VERSION); /* * Driver host template. @@ -2383,13 +2317,6 @@ static void sym2_set_offset(struct scsi_ struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb; struct sym_tcb *tp = &np->target[sdev->id]; - if (tp->tinfo.curr.options & PPR_OPT_DT) { - if (offset > np->maxoffs_dt) - offset = np->maxoffs_dt; - } else { - if (offset > np->maxoffs) - offset = np->maxoffs; - } tp->tinfo.goal.offset = offset; } @@ -2407,23 +2334,11 @@ static void sym2_set_period(struct scsi_ struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb; struct sym_tcb *tp = &np->target[sdev->id]; - if (period <= 9 && np->minsync_dt) { - if (period < np->minsync_dt) - period = np->minsync_dt; - tp->tinfo.goal.options = PPR_OPT_DT; - tp->tinfo.goal.period = period; - if (!tp->tinfo.curr.offset || - tp->tinfo.curr.offset > np->maxoffs_dt) - tp->tinfo.goal.offset = np->maxoffs_dt; - } else { - if (period < np->minsync) - period = np->minsync; - tp->tinfo.goal.options = 0; - tp->tinfo.goal.period = period; - if (!tp->tinfo.curr.offset || - tp->tinfo.curr.offset > np->maxoffs) - tp->tinfo.goal.offset = np->maxoffs; - } + /* have to have DT for these transfers */ + if (period <= np->minsync) + tp->tinfo.goal.options |= PPR_OPT_DT; + + tp->tinfo.goal.period = period; } static void sym2_get_width(struct scsi_device *sdev) @@ -2439,6 +2354,10 @@ static void sym2_set_width(struct scsi_d struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb; struct sym_tcb *tp = &np->target[sdev->id]; + /* It is illegal to have DT set on narrow transfers */ + if (width == 0) + tp->tinfo.goal.options &= ~PPR_OPT_DT; + tp->tinfo.goal.width = width; } @@ -2455,17 +2374,10 @@ static void sym2_set_dt(struct scsi_devi struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb; struct sym_tcb *tp = &np->target[sdev->id]; - if (!dt) { - /* if clearing DT, then we may need to reduce the - * period and the offset */ - if (tp->tinfo.curr.period < np->minsync) - tp->tinfo.goal.period = np->minsync; - if (tp->tinfo.curr.offset > np->maxoffs) - tp->tinfo.goal.offset = np->maxoffs; - tp->tinfo.goal.options &= ~PPR_OPT_DT; - } else { + if (dt) tp->tinfo.goal.options |= PPR_OPT_DT; - } + else + tp->tinfo.goal.options &= ~PPR_OPT_DT; } --- linux-2.6.9-rc1/drivers/scsi/sym53c8xx_2/sym_glue.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/scsi/sym53c8xx_2/sym_glue.h 2004-09-02 20:51:52.000000000 -0700 @@ -90,7 +90,6 @@ #define SYM_OPT_HANDLE_DIR_UNKNOWN #define SYM_OPT_HANDLE_DEVICE_QUEUEING #define SYM_OPT_NVRAM_PRE_READ -#define SYM_OPT_SNIFF_INQUIRY #define SYM_OPT_LIMIT_COMMAND_REORDERING #define SYM_OPT_ANNOUNCE_TRANSFER_RATE @@ -440,6 +439,13 @@ struct sym_device { }; /* + * Driver host data structure. + */ +struct host_data { + struct sym_hcb *ncb; +}; + +/* * The driver definitions (sym_hipd.h) must know about a * couple of things related to the memory allocator. */ --- linux-2.6.9-rc1/drivers/scsi/sym53c8xx_2/sym_hipd.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/drivers/scsi/sym53c8xx_2/sym_hipd.c 2004-09-02 20:51:52.000000000 -0700 @@ -49,10 +49,6 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ - -#define SYM_VERSION "2.1.18j" -#define SYM_DRIVER_NAME "sym-" SYM_VERSION - #include "sym_glue.h" #include "sym_nvram.h" @@ -1042,28 +1038,11 @@ static int sym_prepare_setting(hcb_p np, for (i = 0 ; i < SYM_CONF_MAX_TARGET ; i++) { tcb_p tp = &np->target[i]; - tp->tinfo.user.scsi_version = tp->tinfo.curr.scsi_version= 2; - tp->tinfo.user.spi_version = tp->tinfo.curr.spi_version = 2; - tp->tinfo.user.period = np->minsync; - tp->tinfo.user.offset = np->maxoffs; - tp->tinfo.user.width = np->maxwide ? BUS_16_BIT : BUS_8_BIT; tp->usrflags |= (SYM_DISC_ENABLED | SYM_TAGS_ENABLED); tp->usrtags = SYM_SETUP_MAX_TAG; sym_nvram_setup_target (np, i, nvram); - /* - * Some single-ended devices may crash on receiving a - * PPR negotiation attempt. Only try PPR if we're in - * LVD mode. - */ - if (np->features & FE_ULTRA3) { - tp->tinfo.user.options |= PPR_OPT_DT; - tp->tinfo.user.period = np->minsync_dt; - tp->tinfo.user.offset = np->maxoffs_dt; - tp->tinfo.user.spi_version = 3; - } - if (!tp->usrtags) tp->usrflags &= ~SYM_TAGS_ENABLED; } @@ -1497,6 +1476,55 @@ static void sym_update_dmap_regs(hcb_p n } #endif +static void sym_check_goals(struct scsi_device *sdev) +{ + struct sym_hcb *np = ((struct host_data *)sdev->host->hostdata)->ncb; + struct sym_trans *st = &np->target[sdev->id].tinfo.goal; + + /* here we enforce all the fiddly SPI rules */ + + if (!scsi_device_wide(sdev)) + st->width = 0; + + if (!scsi_device_sync(sdev)) { + st->options = 0; + st->period = 0; + st->offset = 0; + return; + } + + if (scsi_device_dt(sdev)) { + if (scsi_device_dt_only(sdev)) + st->options |= PPR_OPT_DT; + + if (st->offset == 0) + st->options &= ~PPR_OPT_DT; + } else { + st->options &= ~PPR_OPT_DT; + } + + if (!(np->features & FE_ULTRA3)) + st->options &= ~PPR_OPT_DT; + + if (st->options & PPR_OPT_DT) { + /* all DT transfers must be wide */ + st->width = 1; + if (st->offset > np->maxoffs_dt) + st->offset = np->maxoffs_dt; + if (st->period < np->minsync_dt) + st->period = np->minsync_dt; + if (st->period > np->maxsync_dt) + st->period = np->maxsync_dt; + } else { + if (st->offset > np->maxoffs) + st->offset = np->maxoffs; + if (st->period < np->minsync) + st->period = np->minsync; + if (st->period > np->maxsync) + st->period = np->maxsync; + } +} + /* * Prepare the next negotiation message if needed. * @@ -1508,6 +1536,10 @@ static int sym_prepare_nego(hcb_p np, cc { tcb_p tp = &np->target[cp->target]; int msglen = 0; + struct scsi_device *sdev = tp->sdev; + + if (likely(sdev)) + sym_check_goals(sdev); /* * Early C1010 chips need a work-around for DT @@ -1518,19 +1550,21 @@ static int sym_prepare_nego(hcb_p np, cc /* * negotiate using PPR ? */ - if (tp->tinfo.goal.options & PPR_OPT_MASK) + if (scsi_device_dt(sdev)) { nego = NS_PPR; - /* - * negotiate wide transfers ? - */ - else if (tp->tinfo.curr.width != tp->tinfo.goal.width) - nego = NS_WIDE; - /* - * negotiate synchronous transfers? - */ - else if (tp->tinfo.curr.period != tp->tinfo.goal.period || - tp->tinfo.curr.offset != tp->tinfo.goal.offset) - nego = NS_SYNC; + } else { + /* + * negotiate wide transfers ? + */ + if (tp->tinfo.curr.width != tp->tinfo.goal.width) + nego = NS_WIDE; + /* + * negotiate synchronous transfers? + */ + else if (tp->tinfo.curr.period != tp->tinfo.goal.period || + tp->tinfo.curr.offset != tp->tinfo.goal.offset) + nego = NS_SYNC; + } switch (nego) { case NS_SYNC: @@ -3999,7 +4033,6 @@ int sym_compute_residual(hcb_p np, ccb_p static int sym_sync_nego_check(hcb_p np, int req, int target) { - tcb_p tp = &np->target[target]; u_char chg, ofs, per, fak, div; if (DEBUG_FLAGS & DEBUG_NEGO) { @@ -4019,19 +4052,11 @@ sym_sync_nego_check(hcb_p np, int req, i if (ofs) { if (ofs > np->maxoffs) {chg = 1; ofs = np->maxoffs;} - if (req) { - if (ofs > tp->tinfo.user.offset) - {chg = 1; ofs = tp->tinfo.user.offset;} - } } if (ofs) { if (per < np->minsync) {chg = 1; per = np->minsync;} - if (req) { - if (per < tp->tinfo.user.period) - {chg = 1; per = tp->tinfo.user.period;} - } } /* @@ -4151,10 +4176,6 @@ sym_ppr_nego_check(hcb_p np, int req, in } if (!wide || !(np->features & FE_ULTRA3)) dt &= ~PPR_OPT_DT; - if (req) { - if (wide > tp->tinfo.user.width) - {chg = 1; wide = tp->tinfo.user.width;} - } if (!(np->features & FE_U3EN)) /* Broken U3EN bit not supported */ dt &= ~PPR_OPT_DT; @@ -4168,10 +4189,6 @@ sym_ppr_nego_check(hcb_p np, int req, in } else if (ofs > np->maxoffs) {chg = 1; ofs = np->maxoffs;} - if (req) { - if (ofs > tp->tinfo.user.offset) - {chg = 1; ofs = tp->tinfo.user.offset;} - } } if (ofs) { @@ -4181,10 +4198,6 @@ sym_ppr_nego_check(hcb_p np, int req, in } else if (per < np->minsync) {chg = 1; per = np->minsync;} - if (req) { - if (per < tp->tinfo.user.period) - {chg = 1; per = tp->tinfo.user.period;} - } } /* @@ -4286,7 +4299,6 @@ reject_it: static int sym_wide_nego_check(hcb_p np, int req, int target) { - tcb_p tp = &np->target[target]; u_char chg, wide; if (DEBUG_FLAGS & DEBUG_NEGO) { @@ -4306,10 +4318,6 @@ sym_wide_nego_check(hcb_p np, int req, i chg = 1; wide = np->maxwide; } - if (req) { - if (wide > tp->tinfo.user.width) - {chg = 1; wide = tp->tinfo.user.width;} - } if (DEBUG_FLAGS & DEBUG_NEGO) { PRINT_TARGET(np, target); --- linux-2.6.9-rc1/drivers/scsi/sym53c8xx_2/sym_hipd.h 2004-08-24 00:02:26.000000000 -0700 +++ 25/drivers/scsi/sym53c8xx_2/sym_hipd.h 2004-09-02 20:51:52.000000000 -0700 @@ -69,11 +69,6 @@ * When this option is set, the driver will use a queue per * device and handle QUEUE FULL status requeuing internally. * - * SYM_OPT_SNIFF_INQUIRY - * When this option is set, the driver sniff out successful - * INQUIRY response and performs negotiations accordingly. - * (set for Linux) - * * SYM_OPT_LIMIT_COMMAND_REORDERING * When this option is set, the driver tries to limit tagged * command reordering to some reasonnable value. @@ -82,7 +77,6 @@ #if 0 #define SYM_OPT_HANDLE_DIR_UNKNOWN #define SYM_OPT_HANDLE_DEVICE_QUEUEING -#define SYM_OPT_SNIFF_INQUIRY #define SYM_OPT_LIMIT_COMMAND_REORDERING #endif @@ -364,7 +358,6 @@ struct sym_trans { struct sym_tinfo { struct sym_trans curr; struct sym_trans goal; - struct sym_trans user; #ifdef SYM_OPT_ANNOUNCE_TRANSFER_RATE struct sym_trans prev; #endif @@ -465,18 +458,7 @@ struct sym_tcb { */ u_char usrflags; u_short usrtags; - -#ifdef SYM_OPT_SNIFF_INQUIRY - /* - * Some minimal information from INQUIRY response. - */ - u32 cmdq_map[(SYM_CONF_MAX_LUN+31)/32]; - u_char inq_version; - u_char inq_byte7; - u_char inq_byte56; - u_char inq_byte7_valid; -#endif - + struct scsi_device *sdev; }; /* @@ -1169,26 +1151,6 @@ void sym_announce_transfer_rate(hcb_p np #endif /* - * Optionnaly, the driver may sniff inquiry data. - */ -#ifdef SYM_OPT_SNIFF_INQUIRY -#define INQ7_CMDQ (0x02) -#define INQ7_SYNC (0x10) -#define INQ7_WIDE16 (0x20) - -#define INQ56_CLOCKING (3<<2) -#define INQ56_ST_ONLY (0<<2) -#define INQ56_DT_ONLY (1<<2) -#define INQ56_ST_DT (3<<2) - -void sym_update_trans_settings(hcb_p np, tcb_p tp); -int -__sym_sniff_inquiry(hcb_p np, u_char tn, u_char ln, - u_char *inq_data, int inq_len); -#endif - - -/* * Build a scatter/gather entry. * * For 64 bit systems, we use the 8 upper bits of the size field --- linux-2.6.9-rc1/drivers/scsi/sym53c8xx_2/sym_misc.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/scsi/sym53c8xx_2/sym_misc.c 2004-09-02 20:51:52.000000000 -0700 @@ -216,121 +216,3 @@ void sym_announce_transfer_rate(hcb_p np #undef __tprev #undef __tcurr #endif /* SYM_OPT_ANNOUNCE_TRANSFER_RATE */ - - -#ifdef SYM_OPT_SNIFF_INQUIRY -/* - * Update transfer settings according to user settings - * and bits sniffed out from INQUIRY response. - */ -void sym_update_trans_settings(hcb_p np, tcb_p tp) -{ - memcpy(&tp->tinfo.goal, &tp->tinfo.user, sizeof(tp->tinfo.goal)); - - if (tp->inq_version >= 4) { - switch(tp->inq_byte56 & INQ56_CLOCKING) { - case INQ56_ST_ONLY: - tp->tinfo.goal.options = 0; - break; - case INQ56_DT_ONLY: - case INQ56_ST_DT: - default: - break; - } - } - - if (!((tp->inq_byte7 & tp->inq_byte7_valid) & INQ7_WIDE16)) { - tp->tinfo.goal.width = 0; - tp->tinfo.goal.options = 0; - } - - if (!((tp->inq_byte7 & tp->inq_byte7_valid) & INQ7_SYNC)) { - tp->tinfo.goal.offset = 0; - tp->tinfo.goal.options = 0; - } - - if (tp->tinfo.goal.options & PPR_OPT_DT) { - if (tp->tinfo.goal.offset > np->maxoffs_dt) - tp->tinfo.goal.offset = np->maxoffs_dt; - } - else { - if (tp->tinfo.goal.offset > np->maxoffs) - tp->tinfo.goal.offset = np->maxoffs; - } -} - -/* - * Snoop target capabilities from INQUIRY response. - * We only believe device versions >= SCSI-2 that use - * appropriate response data format (2). But it seems - * that some CCS devices also support SYNC (?). - */ -int -__sym_sniff_inquiry(hcb_p np, u_char tn, u_char ln, - u_char *inq_data, int inq_len) -{ - tcb_p tp = &np->target[tn]; - u_char inq_version; - u_char inq_byte7; - u_char inq_byte56; - - if (!inq_data || inq_len < 2) - return -1; - - /* - * Check device type and qualifier. - */ - if ((inq_data[0] & 0xe0) == 0x60) - return -1; - - /* - * Get SPC version. - */ - if (inq_len <= 2) - return -1; - inq_version = inq_data[2] & 0x7; - - /* - * Get SYNC/WIDE16 capabilities. - */ - inq_byte7 = tp->inq_byte7; - if (inq_version >= 2 && (inq_data[3] & 0xf) == 2) { - if (inq_len > 7) - inq_byte7 = inq_data[7]; - } - else if (inq_version == 1 && (inq_data[3] & 0xf) == 1) - inq_byte7 = INQ7_SYNC; - - /* - * Get Tagged Command Queuing capability. - */ - if (inq_byte7 & INQ7_CMDQ) - sym_set_bit(tp->cmdq_map, ln); - else - sym_clr_bit(tp->cmdq_map, ln); - inq_byte7 &= ~INQ7_CMDQ; - - /* - * Get CLOCKING capability. - */ - inq_byte56 = tp->inq_byte56; - if (inq_version >= 4 && inq_len > 56) - inq_byte56 = inq_data[56]; -#if 0 -printf("XXXXXX [%d] inq_version=%x inq_byte7=%x inq_byte56=%x XXXXX\n", - inq_len, inq_version, inq_byte7, inq_byte56); -#endif - /* - * Trigger a negotiation if needed. - */ - if (tp->inq_version != inq_version || - tp->inq_byte7 != inq_byte7 || - tp->inq_byte56 != inq_byte56) { - tp->inq_version = inq_version; - tp->inq_byte7 = inq_byte7; - tp->inq_byte56 = inq_byte56; - return 1; - } - return 0; -} -#endif /* SYM_OPT_SNIFF_INQUIRY */ --- linux-2.6.9-rc1/drivers/scsi/sym53c8xx_2/sym_nvram.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/scsi/sym53c8xx_2/sym_nvram.c 2004-09-02 20:51:52.000000000 -0700 @@ -53,11 +53,6 @@ #include "sym_glue.h" #include "sym_nvram.h" -/* - * Some poor and bogus sync table that refers to Tekram NVRAM layout. - */ -static u_char Tekram_sync[16] = - {25,31,37,43, 50,62,75,125, 12,15,18,21, 6,7,9,10}; #ifdef SYM_CONF_DEBUG_NVRAM static u_char Tekram_boot_delay[7] = {3, 5, 10, 20, 30, 60, 120}; #endif @@ -100,8 +95,6 @@ sym_Symbios_setup_target(struct sym_hcb struct sym_tcb *tp = &np->target[target]; Symbios_target *tn = &nvram->target[target]; - tp->tinfo.user.period = tn->sync_period ? (tn->sync_period + 3) / 4 : 0; - tp->tinfo.user.width = tn->bus_width == 0x10 ? BUS_16_BIT : BUS_8_BIT; tp->usrtags = (tn->flags & SYMBIOS_QUEUE_TAGS_ENABLED)? SYM_SETUP_MAX_TAG : 0; @@ -121,15 +114,6 @@ sym_Tekram_setup_target(struct sym_hcb * { struct sym_tcb *tp = &np->target[target]; struct Tekram_target *tn = &nvram->target[target]; - int i; - - if (tn->flags & TEKRAM_SYNC_NEGO) { - i = tn->sync_index & 0xf; - tp->tinfo.user.period = Tekram_sync[i]; - } - - tp->tinfo.user.width = (tn->flags & TEKRAM_WIDE_NEGO) ? - BUS_16_BIT : BUS_8_BIT; if (tn->flags & TEKRAM_TAGGED_COMMANDS) { tp->usrtags = 2 << nvram->max_tags_index; --- linux-2.6.9-rc1/drivers/scsi/t128.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/scsi/t128.c 2004-09-02 20:51:52.000000000 -0700 @@ -284,6 +284,7 @@ static int t128_release(struct Scsi_Host { if (shost->irq) free_irq(shost->irq, NULL); + NCR5380_exit(shost); if (shost->io_port && shost->n_io_port) release_region(shost->io_port, shost->n_io_port); scsi_unregister(shost); --- linux-2.6.9-rc1/drivers/scsi/tmscsim.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/drivers/scsi/tmscsim.c 2004-09-02 20:51:52.000000000 -0700 @@ -240,17 +240,15 @@ #include #include -#if 0 #include #include #include -#else -#include "scsi.h" -#endif #include #include -#include "dc390.h" + +#define DC390_BANNER "Tekram DC390/AM53C974" +#define DC390_VERSION "2.1d 2004-05-27" #define PCI_DEVICE_ID_AMD53C974 PCI_DEVICE_ID_AMD_SCSI @@ -291,20 +289,14 @@ static void dc390_SRBdone( struct dc390_ static void dc390_DoingSRB_Done( struct dc390_acb* pACB, struct scsi_cmnd * cmd); static void dc390_ScsiRstDetect( struct dc390_acb* pACB ); static void dc390_ResetSCSIBus( struct dc390_acb* pACB ); -static void __inline__ dc390_RequestSense( struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* pSRB ); -static void __inline__ dc390_InvalidCmd( struct dc390_acb* pACB ); -static void __inline__ dc390_EnableMsgOut_Abort (struct dc390_acb*, struct dc390_srb*); +static void dc390_EnableMsgOut_Abort(struct dc390_acb*, struct dc390_srb*); static irqreturn_t do_DC390_Interrupt( int, void *, struct pt_regs *); static int dc390_initAdapter(struct Scsi_Host *psh, unsigned long io_port, u8 Irq, u8 index ); static void dc390_updateDCB (struct dc390_acb* pACB, struct dc390_dcb* pDCB); -static int DC390_proc_info (struct Scsi_Host *shpnt, char *buffer, char **start, - off_t offset, int length, int inout); - static struct dc390_acb* dc390_pACB_start= NULL; static struct dc390_acb* dc390_pACB_current = NULL; -static unsigned long dc390_lastabortedpid = 0; static u32 dc390_laststatus = 0; static u8 dc390_adapterCnt = 0; @@ -617,11 +609,7 @@ static struct dc390_dcb __inline__ *dc39 { pDCB = pDCB->pNextDCB; if (pDCB == pACB->pLinkDCB) - { - DCBDEBUG(printk (KERN_WARNING "DC390: DCB not found (DCB=%p, DCBmap[%2x]=%2x)\n", - pDCB, id, pACB->DCBmap[id])); return 0; - } } DCBDEBUG1( printk (KERN_DEBUG "DCB %p (%02x,%02x) found.\n", \ pDCB, pDCB->TargetID, pDCB->TargetLUN)); @@ -942,8 +930,6 @@ static void dc390_BuildSRB (struct scsi_ { pSRB->pSRBDCB = pDCB; pSRB->pcmd = pcmd; - //pSRB->ScsiCmdLen = pcmd->cmd_len; - //memcpy (pSRB->CmdBlock, pcmd->cmnd, pcmd->cmd_len); pSRB->SGIndex = 0; pSRB->AdaptStatus = 0; @@ -991,28 +977,6 @@ static int DC390_queue_command(struct sc struct dc390_srb* pSRB; struct dc390_acb* pACB = (struct dc390_acb*) cmd->device->host->hostdata; - DEBUG0(/* if(pACB->scan_devices) */ \ - printk(KERN_INFO "DC390: Queue Cmd=%02x,Tgt=%d,LUN=%d (pid=%li), buffer=%p\n",\ - cmd->cmnd[0],cmd->device->id,cmd->device->lun,cmd->pid, cmd->buffer)); - - /* TODO: Change the policy: Always accept TEST_UNIT_READY or INQUIRY - * commands and alloc a DCB for the device if not yet there. DCB will - * be removed in dc390_SRBdone if SEL_TIMEOUT */ - if (!(pACB->scan_devices) && !(pACB->DCBmap[cmd->device->id] & (1 << cmd->device->lun))) { - printk(KERN_INFO "DC390: Ignore target %02x lun %02x\n", - cmd->device->id, cmd->device->lun); - goto fail; - } - - /* Should it be: BUG_ON(!pDCB); ? */ - - if (!pDCB) - { /* should never happen */ - printk (KERN_ERR "DC390: no DCB found, target %02x lun %02x\n", - cmd->device->id, cmd->device->lun); - goto fail; - } - pACB->Cmds++; cmd->scsi_done = done; cmd->result = 0; @@ -1033,145 +997,7 @@ static int DC390_queue_command(struct sc requeue: return 1; - fail: - cmd->result = DID_BAD_TARGET << 16; - done(cmd); - return 0; -} - -/* We ignore mapping problems, as we expect everybody to respect - * valid partition tables. Waiting for complaints ;-) */ - -#ifdef CONFIG_SCSI_DC390T_TRADMAP -/* - * The next function, partsize(), is copied from scsicam.c. - * - * This is ugly code duplication, but I didn't find another way to solve it: - * We want to respect the partition table and if it fails, we apply the - * DC390 BIOS heuristic. Too bad, just calling scsicam_bios_param() doesn't do - * the job, because we don't know, whether the values returned are from - * the part. table or determined by setsize(). Unfortunately the setsize() - * values differ from the ones chosen by the DC390 BIOS. - * - * Looking forward to seeing suggestions for a better solution! KG, 98/10/14 - */ -#include - -/* - * Function : static int partsize(unsigned char *buf, unsigned long - * capacity,unsigned int *cyls, unsigned int *hds, unsigned int *secs); - * - * Purpose : to determine the BIOS mapping used to create the partition - * table, storing the results in *cyls, *hds, and *secs - * - * Returns : -1 on failure, 0 on success. - * - */ - -static int partsize(unsigned char *buf, unsigned long capacity, - unsigned int *cyls, unsigned int *hds, unsigned int *secs) { - struct partition *p, *largest = NULL; - int i, largest_cyl; - int cyl, ext_cyl, end_head, end_cyl, end_sector; - unsigned int logical_end, physical_end, ext_physical_end; - - - if (*(unsigned short *) (buf+64) == 0xAA55) { - for (largest_cyl = -1, p = (struct partition *) buf, - i = 0; i < 4; ++i, ++p) { - if (!p->sys_ind) - continue; - cyl = p->cyl + ((p->sector & 0xc0) << 2); - if (cyl > largest_cyl) { - largest_cyl = cyl; - largest = p; - } - } - } - - if (largest) { - end_cyl = largest->end_cyl + ((largest->end_sector & 0xc0) << 2); - end_head = largest->end_head; - end_sector = largest->end_sector & 0x3f; - - physical_end = end_cyl * (end_head + 1) * end_sector + - end_head * end_sector + end_sector; - - /* This is the actual _sector_ number at the end */ - logical_end = get_unaligned(&largest->start_sect) - + get_unaligned(&largest->nr_sects); - - /* This is for >1023 cylinders */ - ext_cyl= (logical_end-(end_head * end_sector + end_sector)) - /(end_head + 1) / end_sector; - ext_physical_end = ext_cyl * (end_head + 1) * end_sector + - end_head * end_sector + end_sector; - - if ((logical_end == physical_end) || - (end_cyl==1023 && ext_physical_end==logical_end)) { - *secs = end_sector; - *hds = end_head + 1; - *cyls = capacity / ((end_head + 1) * end_sector); - return 0; - } - } - return -1; -} - -/*********************************************************************** - * Function: - * DC390_bios_param - * - * Description: - * Return the disk geometry for the given SCSI device. - * Respect the partition table, otherwise try own heuristic - * - * Note: - * In contrary to other externally callable funcs (DC390_), we don't lock - ***********************************************************************/ -static int DC390_bios_param (struct scsi_device *sdev, struct block_device *bdev, - sector_t capacity, int geom[]) -{ - int heads, sectors, cylinders; - struct dc390_acb* pACB = (struct dc390_acb*) sdev->host->hostdata; - int ret_code = -1; - int size = capacity; - unsigned char *buf; - - if ((buf = scsi_bios_ptable(bdev))) - { - /* try to infer mapping from partition table */ - ret_code = partsize (buf, (unsigned long) size, (unsigned int *) geom + 2, - (unsigned int *) geom + 0, (unsigned int *) geom + 1); - kfree (buf); - } - if (ret_code == -1) - { - heads = 64; - sectors = 32; - cylinders = size / (heads * sectors); - - if ( (pACB->Gmode2 & GREATER_1G) && (cylinders > 1024) ) - { - heads = 255; - sectors = 63; - cylinders = size / (heads * sectors); - } - - geom[0] = heads; - geom[1] = sectors; - geom[2] = cylinders; - } - - return (0); } -#else -static int DC390_bios_param (struct scsi_device *sdev, struct block_device *bdev, - sector_t capacity, int geom[]) -{ - return scsicam_bios_param (bdev, capacity, geom); -} -#endif static void dc390_dumpinfo (struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* pSRB) { @@ -1219,146 +1045,54 @@ static void dc390_dumpinfo (struct dc390 } -/*********************************************************************** - * Function : int DC390_abort (struct scsi_cmnd *cmd) - * - * Purpose : Abort an errant SCSI command - * - * Inputs : cmd - command to abort - * - * Returns : 0 on success, -1 on failure. - * - * Status: Buggy ! - ***********************************************************************/ - -static int DC390_abort (struct scsi_cmnd *cmd) +static int DC390_abort(struct scsi_cmnd *cmd) { - struct dc390_dcb *pDCB = (struct dc390_dcb*) cmd->device->hostdata; - struct dc390_srb *pSRB, *psrb; - u32 count, i; - int status; - //unsigned long sbac; - struct dc390_acb *pACB = (struct dc390_acb*) cmd->device->host->hostdata; - - printk ("DC390: Abort command (pid %li, Device %02i-%02i)\n", - cmd->pid, cmd->device->id, cmd->device->lun); - - if( !pDCB ) goto NOT_RUN; - - /* Added 98/07/02 KG */ - /* - pSRB = pDCB->pActiveSRB; - if (pSRB && pSRB->pcmd == cmd ) - goto ON_GOING; - */ - - pSRB = pDCB->pWaitingSRB; - if( !pSRB ) - goto ON_GOING; + struct dc390_acb *pACB = (struct dc390_acb*) cmd->device->host->hostdata; + struct dc390_dcb *pDCB = (struct dc390_dcb*) cmd->device->hostdata; + struct dc390_srb *pSRB, *psrb; - /* Now scan Waiting queue */ - if( pSRB->pcmd == cmd ) - { - pDCB->pWaitingSRB = pSRB->pNextSRB; - goto IN_WAIT; - } - else - { - psrb = pSRB; - if( !(psrb->pNextSRB) ) - goto ON_GOING; - while( psrb->pNextSRB->pcmd != cmd ) - { - psrb = psrb->pNextSRB; - if( !(psrb->pNextSRB) || psrb == pSRB) - goto ON_GOING; - } - pSRB = psrb->pNextSRB; - psrb->pNextSRB = pSRB->pNextSRB; - if( pSRB == pDCB->pWaitLast ) - pDCB->pWaitLast = psrb; -IN_WAIT: - dc390_Free_insert (pACB, pSRB); + printk("DC390: Abort command (pid %li, Device %02i-%02i)\n", + cmd->pid, cmd->device->id, cmd->device->lun); + + pSRB = pDCB->pWaitingSRB; + if (!pSRB) + goto on_going; + + /* Now scan Waiting queue */ + if (pSRB->pcmd != cmd) { + psrb = pSRB; + if (!(psrb->pNextSRB)) + goto on_going; + + while (psrb->pNextSRB->pcmd != cmd) { + psrb = psrb->pNextSRB; + if (!(psrb->pNextSRB) || psrb == pSRB) + goto on_going; + } + + pSRB = psrb->pNextSRB; + psrb->pNextSRB = pSRB->pNextSRB; + if (pSRB == pDCB->pWaitLast) + pDCB->pWaitLast = psrb; + } else + pDCB->pWaitingSRB = pSRB->pNextSRB; + + dc390_Free_insert(pACB, pSRB); pDCB->WaitSRBCnt--; INIT_LIST_HEAD((struct list_head*)&cmd->SCp); - status = SCSI_ABORT_SUCCESS; - goto ABO_X; - } - /* SRB has already been sent ! */ -ON_GOING: - /* abort() is too stupid for already sent commands at the moment. - * If it's called we are in trouble anyway, so let's dump some info - * into the syslog at least. (KG, 98/08/20,99/06/20) */ - dc390_dumpinfo (pACB, pDCB, pSRB); - pSRB = pDCB->pGoingSRB; - pDCB->DCBFlag |= ABORT_DEV_; - /* Now for the hard part: The command is currently processed */ - for( count = pDCB->GoingSRBCnt, i=0; ipcmd != cmd ) - pSRB = pSRB->pNextSRB; - else - { - if( (pACB->pActiveDCB == pDCB) && (pDCB->pActiveSRB == pSRB) ) - { - status = SCSI_ABORT_BUSY; - printk ("DC390: Abort current command (pid %li, SRB %p)\n", - cmd->pid, pSRB); - goto ABO_X; - } - else - { - status = SCSI_ABORT_SNOOZE; - goto ABO_X; - } - } - } + return SUCCESS; -NOT_RUN: - status = SCSI_ABORT_NOT_RUNNING; +on_going: + /* abort() is too stupid for already sent commands at the moment. + * If it's called we are in trouble anyway, so let's dump some info + * into the syslog at least. (KG, 98/08/20,99/06/20) */ + dc390_dumpinfo(pACB, pDCB, pSRB); -ABO_X: - cmd->result = DID_ABORT << 16; - printk(KERN_INFO "DC390: Aborted pid %li with status %i\n", cmd->pid, status); -#if 0 - if (cmd->pid == dc390_lastabortedpid) /* repeated failure ? */ - { - /* Let's do something to help the bus getting clean again */ - DC390_write8 (DMA_Cmd, DMA_IDLE_CMD); - DC390_write8 (ScsiCmd, DMA_COMMAND); - //DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); - //DC390_write8 (ScsiCmd, RESET_ATN_CMD); - DC390_write8 (ScsiCmd, NOP_CMD); - //udelay (10000); - //DC390_read8 (INT_Status); - //DC390_write8 (ScsiCmd, EN_SEL_RESEL); - } - sbac = DC390_read32 (DMA_ScsiBusCtrl); - if (sbac & SCSI_BUSY) - { /* clear BSY, SEL and ATN */ - printk (KERN_WARNING "DC390: Reset SCSI device: "); - //DC390_write32 (DMA_ScsiBusCtrl, (sbac | SCAM) & ~SCSI_LINES); - //udelay (250); - //sbac = DC390_read32 (DMA_ScsiBusCtrl); - //printk ("%08lx ", sbac); - //DC390_write32 (DMA_ScsiBusCtrl, sbac & ~(SCSI_LINES | SCAM)); - //udelay (100); - //sbac = DC390_read32 (DMA_ScsiBusCtrl); - //printk ("%08lx ", sbac); - DC390_write8 (ScsiCmd, RST_DEVICE_CMD); - udelay (250); - DC390_write8 (ScsiCmd, NOP_CMD); - sbac = DC390_read32 (DMA_ScsiBusCtrl); - printk ("%08lx\n", sbac); - } -#endif - dc390_lastabortedpid = cmd->pid; - //do_DC390_Interrupt (pACB->IRQLevel, 0, 0); -#ifndef USE_NEW_EH - if (status == SCSI_ABORT_SUCCESS) cmd->scsi_done(cmd); -#endif - return( status ); + pDCB->DCBFlag |= ABORT_DEV_; + printk(KERN_INFO "DC390: Aborted pid %li\n", cmd->pid); + + return FAILED; } @@ -1385,93 +1119,38 @@ static void dc390_ResetDevParam( struct } -#if 0 -/* Moves all SRBs from Going to Waiting for all DCBs */ -static void dc390_RecoverSRB( struct dc390_acb* pACB ) +static int DC390_bus_reset (struct scsi_cmnd *cmd) { - struct dc390_dcb *pDCB, *pdcb; - struct dc390_srb *psrb, *psrb2; - u32 cnt, i; - - pDCB = pACB->pLinkDCB; - if( !pDCB ) return; - pdcb = pDCB; - do - { - cnt = pdcb->GoingSRBCnt; - psrb = pdcb->pGoingSRB; - for (i=0; ipNextSRB; -/* dc390_RewaitSRB( pDCB, psrb ); */ - if( pdcb->pWaitingSRB ) - { - psrb2->pNextSRB = pdcb->pWaitingSRB; - pdcb->pWaitingSRB = psrb2; - } - else - { - pdcb->pWaitingSRB = psrb2; - pdcb->pWaitLast = psrb2; - psrb2->pNextSRB = NULL; - } - } - pdcb->GoingSRBCnt = 0; - pdcb->pGoingSRB = NULL; - pdcb->TagMask = 0; - pdcb = pdcb->pNextDCB; - } while( pdcb != pDCB ); -} -#endif + struct dc390_acb* pACB = (struct dc390_acb*) cmd->device->host->hostdata; + u8 bval; -/*********************************************************************** - * Function : int DC390_reset (struct scsi_cmnd *cmd, ...) - * - * Purpose : perform a hard reset on the SCSI bus - * - * Inputs : cmd - command which caused the SCSI RESET - * resetFlags - how hard to try - * - * Returns : 0 on success. - ***********************************************************************/ + del_timer (&pACB->Waiting_Timer); -static int DC390_reset (struct scsi_cmnd *cmd) -{ - u8 bval; - struct dc390_acb* pACB = (struct dc390_acb*) cmd->device->host->hostdata; + bval = DC390_read8(CtrlReg1) | DIS_INT_ON_SCSI_RST; + DC390_write8(CtrlReg1, bval); /* disable IRQ on bus reset */ - printk(KERN_INFO "DC390: RESET ... "); + pACB->ACBFlag |= RESET_DEV; + dc390_ResetSCSIBus(pACB); - if (timer_pending (&pACB->Waiting_Timer)) del_timer (&pACB->Waiting_Timer); - bval = DC390_read8 (CtrlReg1); - bval |= DIS_INT_ON_SCSI_RST; - DC390_write8 (CtrlReg1, bval); /* disable IRQ on bus reset */ - - pACB->ACBFlag |= RESET_DEV; - dc390_ResetSCSIBus( pACB ); - - dc390_ResetDevParam( pACB ); - udelay (1000); - pACB->pScsiHost->last_reset = jiffies + 3*HZ/2 + dc390_ResetDevParam(pACB); + udelay(1000); + pACB->pScsiHost->last_reset = jiffies + 3*HZ/2 + HZ * dc390_eepromBuf[pACB->AdapterIndex][EE_DELAY]; - DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); - DC390_read8 (INT_Status); /* Reset Pending INT */ + DC390_write8(ScsiCmd, CLEAR_FIFO_CMD); + DC390_read8(INT_Status); /* Reset Pending INT */ - dc390_DoingSRB_Done( pACB, cmd ); - /* dc390_RecoverSRB (pACB); */ - pACB->pActiveDCB = NULL; + dc390_DoingSRB_Done(pACB, cmd); - pACB->ACBFlag = 0; - bval = DC390_read8 (CtrlReg1); - bval &= ~DIS_INT_ON_SCSI_RST; - DC390_write8 (CtrlReg1, bval); /* re-enable interrupt */ + pACB->pActiveDCB = NULL; + pACB->ACBFlag = 0; - dc390_Waiting_process( pACB ); + bval = DC390_read8(CtrlReg1) & ~DIS_INT_ON_SCSI_RST; + DC390_write8(CtrlReg1, bval); /* re-enable interrupt */ - printk("done\n"); - return( SCSI_RESET_SUCCESS ); + dc390_Waiting_process(pACB); + + return SUCCESS; } #include "scsiiom.c" @@ -1548,7 +1227,6 @@ static void dc390_linkSRB( struct dc390_ static void __devinit dc390_initACB (struct Scsi_Host *psh, unsigned long io_port, u8 Irq, u8 index) { struct dc390_acb* pACB; - u8 i; psh->can_queue = MAX_CMD_QUEUE; psh->cmd_per_lun = MAX_CMD_PER_LUN; @@ -1595,8 +1273,6 @@ static void __devinit dc390_initACB (str dc390_linkSRB( pACB ); pACB->pTmpSRB = &pACB->TmpSRB; dc390_initSRB( pACB->pTmpSRB ); - for(i=0; iDCBmap[i] = 0; pACB->sel_timeout = SEL_TIMEOUT; pACB->glitch_cfg = EATER_25NS; pACB->Cmds = pACB->CmdInQ = pACB->CmdOutOfSRB = 0; @@ -1757,7 +1433,6 @@ static int dc390_slave_alloc(struct scsi pDCB->CtrlR4 |= NEGATE_REQACKDATA | NEGATE_REQACK; } - pACB->DCBmap[id] |= (1 << lun); dc390_updateDCB(pACB, pDCB); pACB->scan_devices = 1; @@ -1781,8 +1456,6 @@ static void dc390_slave_destroy(struct s BUG_ON(pDCB->GoingSRBCnt > 1); - pACB->DCBmap[pDCB->TargetID] &= ~(1 << pDCB->TargetLUN); - if (pDCB == pACB->pLinkDCB) { if (pACB->pLastDCB == pDCB) { pDCB->pNextDCB = NULL; @@ -1818,15 +1491,13 @@ static int dc390_slave_configure(struct static struct scsi_host_template driver_template = { .module = THIS_MODULE, .proc_name = "tmscsim", - .proc_info = DC390_proc_info, .name = DC390_BANNER " V" DC390_VERSION, .slave_alloc = dc390_slave_alloc, .slave_configure = dc390_slave_configure, .slave_destroy = dc390_slave_destroy, .queuecommand = DC390_queue_command, .eh_abort_handler = DC390_abort, - .eh_bus_reset_handler = DC390_reset, - .bios_param = DC390_bios_param, + .eh_bus_reset_handler = DC390_bus_reset, .can_queue = 42, .this_id = 7, .sg_tablesize = SG_ALL, @@ -1939,152 +1610,6 @@ static void __devexit dc390_remove_one(s pci_set_drvdata(dev, NULL); } -/******************************************************************** - * Function: DC390_proc_info(char* buffer, char **start, - * off_t offset, int length, int hostno, int inout) - * - * Purpose: return SCSI Adapter/Device Info - * - * Input: buffer: Pointer to a buffer where to write info - * start : - * offset: - * hostno: Host adapter index - * inout : Read (=0) or set(!=0) info - * - * Output: buffer: contains info - * length; length of info in buffer - * - * return value: length - * - ********************************************************************/ - -#undef SPRINTF -#define SPRINTF(args...) pos += sprintf(pos, ## args) - -#define YESNO(YN) \ - if (YN) SPRINTF(" Yes "); \ - else SPRINTF(" No ") - - -static int DC390_proc_info (struct Scsi_Host *shpnt, char *buffer, char **start, - off_t offset, int length, int inout) -{ - int dev, spd, spd1; - char *pos = buffer; - struct dc390_acb* pACB; - struct dc390_dcb* pDCB; - - pACB = dc390_pACB_start; - - while(pACB != (struct dc390_acb*)-1) - { - if (shpnt == pACB->pScsiHost) - break; - pACB = pACB->pNextACB; - } - - if (pACB == (struct dc390_acb*)-1) return(-ESRCH); - - if(inout) /* Has data been written to the file ? */ - return -ENOSYS; - - SPRINTF("Tekram DC390/AM53C974 PCI SCSI Host Adapter, "); - SPRINTF("Driver Version %s\n", DC390_VERSION); - - SPRINTF("SCSI Host Nr %i, ", shpnt->host_no); - SPRINTF("%s Adapter Nr %i\n", dc390_adapname, pACB->AdapterIndex); - SPRINTF("IOPortBase 0x%04x, ", pACB->IOPortBase); - SPRINTF("IRQ %02i\n", pACB->IRQLevel); - - SPRINTF("MaxID %i, MaxLUN %i, ", shpnt->max_id, shpnt->max_lun); - SPRINTF("AdapterID %i, SelTimeout %i ms, DelayReset %i s\n", - shpnt->this_id, (pACB->sel_timeout*164)/100, - dc390_eepromBuf[pACB->AdapterIndex][EE_DELAY]); - - SPRINTF("TagMaxNum %i, Status 0x%02x, ACBFlag 0x%02x, GlitchEater %i ns\n", - pACB->TagMaxNum, pACB->status, pACB->ACBFlag, GLITCH_TO_NS(pACB->glitch_cfg)*12); - - SPRINTF("Statistics: Cmnds %li, Cmnds not sent directly %i, Out of SRB conds %i\n", - pACB->Cmds, pACB->CmdInQ, pACB->CmdOutOfSRB); - SPRINTF(" Lost arbitrations %i, Sel. connected %i, Connected: %s\n", - pACB->SelLost, pACB->SelConn, pACB->Connected? "Yes": "No"); - - SPRINTF("Nr of DCBs: %i\n", pACB->DCBCnt); - SPRINTF("Map of attached LUNs: %02x %02x %02x %02x %02x %02x %02x %02x\n", - pACB->DCBmap[0], pACB->DCBmap[1], pACB->DCBmap[2], pACB->DCBmap[3], - pACB->DCBmap[4], pACB->DCBmap[5], pACB->DCBmap[6], pACB->DCBmap[7]); - - SPRINTF("Idx ID LUN Prty Sync DsCn SndS TagQ NegoPeriod SyncSpeed SyncOffs MaxCmd\n"); - - pDCB = pACB->pLinkDCB; - for (dev = 0; dev < pACB->DCBCnt; dev++) - { - SPRINTF("%02i %02i %02i ", dev, pDCB->TargetID, pDCB->TargetLUN); - YESNO(pDCB->DevMode & PARITY_CHK_); - YESNO(pDCB->SyncMode & SYNC_NEGO_DONE); - YESNO(pDCB->DevMode & EN_DISCONNECT_); - YESNO(pDCB->DevMode & SEND_START_); - YESNO(pDCB->SyncMode & EN_TAG_QUEUEING); - if (pDCB->SyncOffset & 0x0f) - { - int sp = pDCB->SyncPeriod; if (! (pDCB->CtrlR3 & FAST_SCSI)) sp++; - SPRINTF(" %03i ns ", (pDCB->NegoPeriod) << 2); - spd = 40/(sp); spd1 = 40%(sp); - spd1 = (spd1 * 10 + sp/2) / (sp); - SPRINTF(" %2i.%1i M %02i", spd, spd1, (pDCB->SyncOffset & 0x0f)); - } - else SPRINTF(" (%03i ns) ", (pDCB->NegoPeriod) << 2); - /* Add more info ...*/ - SPRINTF (" %02i\n", pDCB->MaxCommand); - pDCB = pDCB->pNextDCB; - } - if (timer_pending(&pACB->Waiting_Timer)) SPRINTF ("Waiting queue timer running\n"); - else SPRINTF ("\n"); - pDCB = pACB->pLinkDCB; - - for (dev = 0; dev < pACB->DCBCnt; dev++) - { - struct dc390_srb* pSRB; - if (pDCB->WaitSRBCnt) - SPRINTF ("DCB (%02i-%i): Waiting: %i:", pDCB->TargetID, pDCB->TargetLUN, - pDCB->WaitSRBCnt); - for (pSRB = pDCB->pWaitingSRB; pSRB; pSRB = pSRB->pNextSRB) - SPRINTF(" %li", pSRB->pcmd->pid); - if (pDCB->GoingSRBCnt) - SPRINTF ("\nDCB (%02i-%i): Going : %i:", pDCB->TargetID, pDCB->TargetLUN, - pDCB->GoingSRBCnt); - for (pSRB = pDCB->pGoingSRB; pSRB; pSRB = pSRB->pNextSRB) -#if 0 //def DC390_DEBUGTRACE - SPRINTF(" %s\n ", pSRB->debugtrace); -#else - SPRINTF(" %li", pSRB->pcmd->pid); -#endif - if (pDCB->WaitSRBCnt || pDCB->GoingSRBCnt) SPRINTF ("\n"); - pDCB = pDCB->pNextDCB; - } - -#ifdef DC390_DEBUGDCB - SPRINTF ("DCB list for ACB %p:\n", pACB); - pDCB = pACB->pLinkDCB; - SPRINTF ("%p", pDCB); - for (dev = 0; dev < pACB->DCBCnt; dev++, pDCB=pDCB->pNextDCB) - SPRINTF ("->%p", pDCB->pNextDCB); - SPRINTF("\n"); -#endif - - *start = buffer + offset; - - if (pos - buffer < offset) - return 0; - else if (pos - buffer - offset < length) - return pos - buffer - offset; - else - return length; -} - -#undef YESNO -#undef SPRINTF - static struct pci_driver dc390_driver = { .name = "tmscsim", .id_table = tmscsim_pci_tbl, --- linux-2.6.9-rc1/drivers/scsi/tmscsim.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/scsi/tmscsim.h 2004-09-02 20:51:52.000000000 -0700 @@ -163,7 +163,6 @@ struct dc390_srb *pFreeSRB; struct dc390_srb *pTmpSRB; u8 msgin123[4]; -u8 DCBmap[MAX_SCSI_ID]; u8 Connected; u8 pad; --- linux-2.6.9-rc1/drivers/scsi/wd7000.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/scsi/wd7000.c 2004-09-02 20:51:52.000000000 -0700 @@ -190,7 +190,6 @@ #include -#define ANY2SCSI_INLINE /* undef this to use old macros */ #undef WD7000_DEBUG /* general debug */ #ifdef WD7000_DEBUG #define dprintk printk @@ -726,55 +725,17 @@ static int __init wd7000_setup(char *str __setup("wd7000=", wd7000_setup); -#ifdef ANY2SCSI_INLINE -/* - * Since they're used a lot, I've redone the following from the macros - * formerly in wd7000.h, hopefully to speed them up by getting rid of - * all the shifting (it may not matter; GCC might have done as well anyway). - * - * xany2scsi and xscsi2int were not being used, and are no longer defined. - * (They were simply 4-byte versions of these routines). - */ -typedef union { /* let's cheat... */ - int i; - unchar u[sizeof(int)]; /* the sizeof(int) makes it more portable */ -} i_u; - - static inline void any2scsi(unchar * scsi, int any) { - *scsi++ = ((i_u) any).u[2]; - *scsi++ = ((i_u) any).u[1]; - *scsi++ = ((i_u) any).u[0]; + *scsi++ = (unsigned)any >> 16; + *scsi++ = (unsigned)any >> 8; + *scsi++ = any; } - static inline int scsi2int(unchar * scsi) { - i_u result; - - result.i = 0; /* clears unused bytes */ - result.u[2] = *scsi++; - result.u[1] = *scsi++; - result.u[0] = *scsi++; - - return (result.i); + return (scsi[0] << 16) | (scsi[1] << 8) | scsi[2]; } -#else -/* - * These are the old ones - I've just moved them here... - */ -#undef any2scsi -#define any2scsi(up, p) (up)[0] = (((unsigned long) (p)) >> 16); \ - (up)[1] = ((unsigned long) (p)) >> 8; \ - (up)[2] = ((unsigned long) (p)); - -#undef scsi2int -#define scsi2int(up) ( (((unsigned long) *(up)) << 16) + \ - (((unsigned long) (up)[1]) << 8) + \ - ((unsigned long) (up)[2]) ) -#endif - static inline void wd7000_enable_intr(Adapter * host) { --- linux-2.6.9-rc1/drivers/serial/68360serial.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/drivers/serial/68360serial.c 2004-09-02 20:51:52.000000000 -0700 @@ -2592,7 +2592,7 @@ int rs_360_init(void) state->icount.rx = state->icount.tx = 0; state->icount.frame = state->icount.parity = 0; state->icount.overrun = state->icount.brk = 0; - printk(KERN_INFO "ttyS%02d at irq 0x%02x is an %s\n", + printk(KERN_INFO "ttyS%d at irq 0x%02x is an %s\n", i, (unsigned int)(state->irq), (state->smc_scc_num & NUM_IS_SCC) ? "SCC" : "SMC"); --- linux-2.6.9-rc1/drivers/serial/8250.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/drivers/serial/8250.c 2004-09-03 00:57:11.449298704 -0700 @@ -130,6 +130,7 @@ struct uart_8250_port { struct timer_list timer; /* "no irq" timer */ struct list_head list; /* ports on this IRQ */ unsigned int capabilities; /* port capabilities */ + unsigned int tx_loadsz; /* transmit fifo load size */ unsigned short rev; unsigned char acr; unsigned char ier; @@ -156,23 +157,24 @@ static struct irq_info irq_lists[NR_IRQS /* * Here we define the default xmit fifo size used for each type of UART. */ -static const struct serial_uart_config uart_config[PORT_MAX_8250+1] = { - { "unknown", 1, 0 }, - { "8250", 1, 0 }, - { "16450", 1, 0 }, - { "16550", 1, 0 }, - { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, - { "Cirrus", 1, 0 }, - { "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH }, - { "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, - { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO }, - { "Startech", 1, 0 }, - { "16C950/954", 128, UART_CLEAR_FIFO | UART_USE_FIFO }, - { "ST16654", 64, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, - { "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, - { "RSA", 2048, UART_CLEAR_FIFO | UART_USE_FIFO }, - { "NS16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO | UART_NATSEMI }, - { "XScale", 32, UART_CLEAR_FIFO | UART_USE_FIFO }, +static const struct serial8250_config uart_config[PORT_MAX_8250+1] = { + { "unknown", 1, 1, 0 }, + { "8250", 1, 1, 0 }, + { "16450", 1, 1, 0 }, + { "16550", 1, 1, 0 }, + { "16550A", 16, 16, UART_CAP_FIFO }, + { "Cirrus", 1, 1, 0 }, + { "ST16650", 1, 1, UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_EFR }, + { "ST16650V2", 32, 16, UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_EFR }, + { "TI16750", 64, 64, UART_CAP_FIFO | UART_CAP_SLEEP }, + { "Startech", 1, 1, 0 }, + { "16C950/954", 128, 128, UART_CAP_FIFO }, + { "ST16654", 64, 32, UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_EFR }, + { "XR16850", 128, 128, UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_EFR }, + { "RSA", 2048, 2048, UART_CAP_FIFO }, + { "NS16550A", 16, 16, UART_CAP_FIFO | UART_NATSEMI }, + { "XScale", 32, 32, UART_CAP_FIFO }, + { "OMAP UART", 64, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, }; static _INLINE_ unsigned int serial_in(struct uart_8250_port *up, int offset) @@ -243,6 +245,41 @@ static unsigned int serial_icr_read(stru return value; } +/* + * FIFO support. + */ +static inline void serial8250_clear_fifos(struct uart_8250_port *p) +{ + if (p->capabilities & UART_CAP_FIFO) { + serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(p, UART_FCR, 0); + } +} + +/* + * IER sleep support. UARTs which have EFRs need the "extended + * capability" bit enabled. Note that on XR16C850s, we need to + * reset LCR to write to IER. + */ +static inline void serial8250_set_sleep(struct uart_8250_port *p, int sleep) +{ + if (p->capabilities & UART_CAP_SLEEP) { + if (p->capabilities & UART_CAP_EFR) { + serial_outp(p, UART_LCR, 0xBF); + serial_outp(p, UART_EFR, UART_EFR_ECB); + serial_outp(p, UART_LCR, 0); + } + serial_outp(p, UART_IER, sleep ? UART_IERX_SLEEP : 0); + if (p->capabilities & UART_CAP_EFR) { + serial_outp(p, UART_LCR, 0xBF); + serial_outp(p, UART_EFR, 0); + serial_outp(p, UART_LCR, 0); + } + } +} + #ifdef CONFIG_SERIAL_8250_RSA /* * Attempts to turn on the RSA FIFO. Returns zero on failure. @@ -697,8 +734,9 @@ static void autoconfig(struct uart_8250_ #endif serial_outp(up, UART_LCR, save_lcr); - up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size; + up->port.fifosize = uart_config[up->port.type].fifo_size; up->capabilities = uart_config[up->port.type].flags; + up->tx_loadsz = uart_config[up->port.type].tx_loadsz; if (up->port.type == PORT_UNKNOWN) goto out; @@ -711,10 +749,7 @@ static void autoconfig(struct uart_8250_ serial_outp(up, UART_RSA_FRR, 0); #endif serial_outp(up, UART_MCR, save_mcr); - serial_outp(up, UART_FCR, (UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | - UART_FCR_CLEAR_XMIT)); - serial_outp(up, UART_FCR, 0); + serial8250_clear_fifos(up); (void)serial_in(up, UART_RX); serial_outp(up, UART_IER, 0); @@ -832,7 +867,7 @@ receive_chars(struct uart_8250_port *up, if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) { tty->flip.work.func((void *)tty); if (tty->flip.count >= TTY_FLIPBUF_SIZE) - return; // if TTY_DONT_FLIP is set + return; /* if TTY_DONT_FLIP is set */ } ch = serial_inp(up, UART_RX); *tty->flip.char_buf_ptr = ch; @@ -923,7 +958,7 @@ static _INLINE_ void transmit_chars(stru return; } - count = up->port.fifosize; + count = up->tx_loadsz; do { serial_out(up, UART_TX, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); @@ -1193,12 +1228,22 @@ static void serial8250_break_ctl(struct spin_unlock_irqrestore(&up->port.lock, flags); } +#ifdef CONFIG_KGDB +static int kgdb_irq = -1; +static int kgdb_port = -1; +#endif + static int serial8250_startup(struct uart_port *port) { struct uart_8250_port *up = (struct uart_8250_port *)port; unsigned long flags; int retval; +#ifdef CONFIG_KGDB + if (up->port.irq == kgdb_irq) + return -EBUSY; +#endif + up->capabilities = uart_config[up->port.type].flags; up->mcr = 0; @@ -1215,6 +1260,28 @@ static int serial8250_startup(struct uar serial_outp(up, UART_LCR, 0); } +#ifdef CONFIG_ARCH_OMAP + if (up->port.type == PORT_OMAP) { + serial_outp(up, UART_OMAP_MDR1, 0x07); /* disable UART */ + serial_outp(up, UART_LCR, 0xBF); /* select EFR */ + serial_outp(up, UART_EFR, UART_EFR_ECB); + serial_outp(up, UART_LCR, UART_LCR_DLAB); /* set DLAB */ + serial_outp(up, UART_DLL, 0x00); + serial_outp(up, UART_DLM, 0x00); + serial_outp(up, UART_LCR, 0x00); /* reset DLAB */ + serial_outp(up, UART_OMAP_SCR, 0x08); + serial_outp(up, UART_FCR, 0x00); + serial_outp(up, UART_MCR, 0x40); /* enable TCR/TLR */ + serial_outp(up, UART_OMAP_TCR, 0x0F); + serial_outp(up, UART_OMAP_TLR, 0x00); + serial_outp(up, UART_MCR, 0x00); + serial_outp(up, UART_LCR, 0xBF); /* select EFR */ + serial_outp(up, UART_EFR, 0x00); + serial_outp(up, UART_LCR, 0x00); /* reset DLAB */ + serial_outp(up, UART_OMAP_MDR1, 0x00); /* enable UART */ + } +#endif + #ifdef CONFIG_SERIAL_8250_RSA /* * If this is an RSA port, see if we can kick it up to the @@ -1227,12 +1294,7 @@ static int serial8250_startup(struct uar * Clear the FIFO buffers and disable them. * (they will be reeanbled in set_termios()) */ - if (up->capabilities & UART_CLEAR_FIFO) { - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); - serial_outp(up, UART_FCR, 0); - } + serial8250_clear_fifos(up); /* * Clear the interrupt registers. @@ -1254,6 +1316,23 @@ static int serial8250_startup(struct uar } /* + * For a XR16C850, we need to set the trigger levels + */ + if (up->port.type == PORT_16850) { + unsigned char fctr; + + serial_outp(up, UART_LCR, 0xbf); + + fctr = serial_inp(up, UART_FCTR) & ~(UART_FCTR_RX|UART_FCTR_TX); + serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_RX); + serial_outp(up, UART_TRG, UART_TRG_96); + serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_TX); + serial_outp(up, UART_TRG, UART_TRG_96); + + serial_outp(up, UART_LCR, 0); + } + + /* * If the "interrupt" for this port doesn't correspond with any * hardware interrupt, we use a timer-based system. The original * driver used to do this with IRQ0. @@ -1345,10 +1424,7 @@ static void serial8250_shutdown(struct u * Disable break condition and FIFOs */ serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC); - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | - UART_FCR_CLEAR_XMIT); - serial_outp(up, UART_FCR, 0); + serial8250_clear_fifos(up); #ifdef CONFIG_SERIAL_8250_RSA /* @@ -1369,24 +1445,58 @@ static void serial8250_shutdown(struct u serial_unlink_irq_chain(up); } -static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud) +static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud, + unsigned int *prescaler) { - unsigned int quot; - - /* - * Handle magic divisors for baud rates above baud_base on - * SMSC SuperIO chips. + /* + * Use special handling only if user did not supply its own divider. + * spd_cust is defined in terms of baud_base, so always use default + * prescaler when spd_cust is requested. */ - if ((port->flags & UPF_MAGIC_MULTIPLIER) && - baud == (port->uartclk/4)) - quot = 0x8001; - else if ((port->flags & UPF_MAGIC_MULTIPLIER) && - baud == (port->uartclk/8)) - quot = 0x8002; - else - quot = uart_get_divisor(port, baud); - return quot; + *prescaler = 16; + if (baud != 38400 || (port->flags & UPF_SPD_MASK) != UPF_SPD_CUST) { + unsigned int quot = port->uartclk / baud; + + /* + * Handle magic divisors for baud rates above baud_base on + * SMSC SuperIO chips. + */ + if (port->flags & UPF_MAGIC_MULTIPLIER) { + if (quot == 4) { + return 0x8001; + } else if (quot == 8) { + return 0x8002; + } + } + if (port->type == PORT_16C950) { + /* + * This computes TCR value (4 to 16), not CPR value (which can + * be between 1.000 and 31.875) - chip I have uses XTAL of + * 806400Hz, and so a division by 7 is required to get 115200Bd. + * I'm leaving CPR disabled for now, until someone will + * hit even more exotic XTAL (it is needed to get 500kbps + * or 1000kbps from 18.432MHz XTAL, but I have no device + * which would benefit from doing that). + * + * If we can use divide by 16, use it. Otherwise look for + * better prescaler, from 15 to 4. If quotient cannot + * be divided by any integer value between 4 and 15, use 4. + */ + if (quot & 0x0F) { + unsigned int div; + + for (div = 15; div > 4; div--) { + if (quot % div == 0) { + break; + } + } + *prescaler = div; + return quot / div; + } + } + } + return uart_get_divisor(port, baud); } static void @@ -1396,7 +1506,7 @@ serial8250_set_termios(struct uart_port struct uart_8250_port *up = (struct uart_8250_port *)port; unsigned char cval, fcr = 0; unsigned long flags; - unsigned int baud, quot; + unsigned int baud, quot, prescaler; switch (termios->c_cflag & CSIZE) { case CS5: @@ -1428,8 +1538,13 @@ serial8250_set_termios(struct uart_port /* * Ask the core to calculate the divisor for us. */ - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); - quot = serial8250_get_divisor(port, baud); + + if (port->type == PORT_16C950 || (port->flags & UPF_MAGIC_MULTIPLIER)) { + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/4); + } else { + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + } + quot = serial8250_get_divisor(port, baud, &prescaler); /* * Work around a bug in the Oxford Semiconductor 952 rev B @@ -1440,13 +1555,17 @@ serial8250_set_termios(struct uart_port up->rev == 0x5201) quot ++; - if (up->capabilities & UART_USE_FIFO) { + if (up->capabilities & UART_CAP_FIFO && up->port.fifosize > 1) { if (baud < 2400) fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; #ifdef CONFIG_SERIAL_8250_RSA else if (up->port.type == PORT_RSA) fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14; #endif +#ifdef CONFIG_ARCH_OMAP + else if (up->port.type == PORT_OMAP) + fcr = UART_FCR_T_TRIGGER_56 | UART_FCR_R_TRIGGER_60 | UART_FCR_ENABLE_FIFO; +#endif else fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; } @@ -1514,12 +1633,22 @@ serial8250_set_termios(struct uart_port serial_out(up, UART_IER, up->ier); - if (up->capabilities & UART_STARTECH) { + if (up->capabilities & UART_CAP_EFR) { serial_outp(up, UART_LCR, 0xBF); serial_outp(up, UART_EFR, termios->c_cflag & CRTSCTS ? UART_EFR_CTS :0); } +#ifdef CONFIG_ARCH_OMAP1510 /* Needed for 1510 only */ + if (up->port.type == PORT_OMAP && cpu_is_omap1510()) { + if (baud == 115200) { + quot = 1; + serial_out(up, UART_OMAP_OSC_12M_SEL, 1); + } else + serial_out(up, UART_OMAP_OSC_12M_SEL, 0); + } +#endif + if (up->capabilities & UART_NATSEMI) { /* Switch to bank 2 not bank 1, to avoid resetting EXCR2 */ serial_outp(up, UART_LCR, 0xe0); @@ -1531,6 +1660,13 @@ serial8250_set_termios(struct uart_port serial_outp(up, UART_DLM, quot >> 8); /* MS of divisor */ /* + * Program prescaler for 16C950 chips. + */ + if (up->port.type == PORT_16C950) { + serial_icr_write(up, UART_TCR, prescaler == 16 ? 0 : prescaler); + } + + /* * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR * is written without DLAB set, this mode will be disabled. */ @@ -1554,71 +1690,12 @@ static void serial8250_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) { - struct uart_8250_port *up = (struct uart_8250_port *)port; - if (state) { - /* sleep */ - if (up->capabilities & UART_STARTECH) { - /* Arrange to enter sleep mode */ - serial_outp(up, UART_LCR, 0xBF); - serial_outp(up, UART_EFR, UART_EFR_ECB); - serial_outp(up, UART_LCR, 0); - serial_outp(up, UART_IER, UART_IERX_SLEEP); - serial_outp(up, UART_LCR, 0xBF); - serial_outp(up, UART_EFR, 0); - serial_outp(up, UART_LCR, 0); - } - if (up->port.type == PORT_16750) { - /* Arrange to enter sleep mode */ - serial_outp(up, UART_IER, UART_IERX_SLEEP); - } - - if (up->pm) - up->pm(port, state, oldstate); - } else { - /* wake */ - if (up->capabilities & UART_STARTECH) { - /* Wake up UART */ - serial_outp(up, UART_LCR, 0xBF); - serial_outp(up, UART_EFR, UART_EFR_ECB); - /* - * Turn off LCR == 0xBF so we actually set the IER - * register on the XR16C850 - */ - serial_outp(up, UART_LCR, 0); - serial_outp(up, UART_IER, 0); - /* - * Now reset LCR so we can turn off the ECB bit - */ - serial_outp(up, UART_LCR, 0xBF); - serial_outp(up, UART_EFR, 0); - /* - * For a XR16C850, we need to set the trigger levels - */ - if (up->port.type == PORT_16850) { - unsigned char fctr; + struct uart_8250_port *p = (struct uart_8250_port *)port; - fctr = serial_inp(up, UART_FCTR) & - ~(UART_FCTR_RX | UART_FCTR_TX); - serial_outp(up, UART_FCTR, fctr | - UART_FCTR_TRGD | - UART_FCTR_RX); - serial_outp(up, UART_TRG, UART_TRG_96); - serial_outp(up, UART_FCTR, fctr | - UART_FCTR_TRGD | - UART_FCTR_TX); - serial_outp(up, UART_TRG, UART_TRG_96); - } - serial_outp(up, UART_LCR, 0); - } - - if (up->port.type == PORT_16750) { - /* Wake up UART */ - serial_outp(up, UART_IER, 0); - } + serial8250_set_sleep(p, state != 0); - if (up->pm) - up->pm(port, state, oldstate); - } + if (p->pm) + p->pm(port, state, oldstate); } /* @@ -1879,6 +1956,11 @@ static void __init serial8250_register_p for (i = 0; i < UART_NR; i++) { struct uart_8250_port *up = &serial8250_ports[i]; +#ifdef CONFIG_KGDB + /* at this point irq could be 0 for the port (KGDB_EARLY) */ + if (up->port.irq == kgdb_irq || up->port.iobase == kgdb_port) + up->port.kgdb = 1; +#endif up->port.line = i; up->port.ops = &serial8250_pops; init_timer(&up->timer); @@ -2056,6 +2138,7 @@ static int __register_serial(struct seri { struct uart_port port; + port.type = req->type; port.iobase = req->port; port.membase = req->iomem_base; port.irq = req->irq; @@ -2063,10 +2146,13 @@ static int __register_serial(struct seri port.fifosize = req->xmit_fifo_size; port.regshift = req->iomem_reg_shift; port.iotype = req->io_type; - port.flags = req->flags | UPF_BOOT_AUTOCONF; + port.flags = req->flags; port.mapbase = req->iomap_base; port.line = line; + if (req->type != PORT_OMAP) + req->flags |= UPF_BOOT_AUTOCONF; + if (share_irqs) port.flags |= UPF_SHARE_IRQ; @@ -2162,6 +2248,32 @@ void serial8250_resume_port(int line) uart_resume_port(&serial8250_reg, &serial8250_ports[line].port); } +#ifdef CONFIG_KGDB +/* + * Find all the ports using the given irq and shut them down. + * Result should be that the irq will be released. + */ +void shutdown_for_kgdb(struct async_struct * info) +{ + int irq = info->state->irq; + struct uart_8250_port *up; + int ttyS; + + kgdb_irq = irq; /* save for later init */ + kgdb_port = info->port; + for (ttyS = 0; ttyS < UART_NR; ttyS++){ + up = &serial8250_ports[ttyS]; + if (up->port.irq == irq && (irq_lists + irq)->head) { +#ifdef CONFIG_DEBUG_SPINLOCK /* ugly business... */ + if(up->port.lock.magic != SPINLOCK_MAGIC) + spin_lock_init(&up->port.lock); +#endif + serial8250_shutdown(&up->port); + } + } +} +#endif /* CONFIG_KGDB */ + static int __init serial8250_init(void) { int ret, i; --- linux-2.6.9-rc1/drivers/serial/8250.h 2004-08-24 00:01:51.000000000 -0700 +++ 25/drivers/serial/8250.h 2004-09-03 00:56:33.648045368 -0700 @@ -33,6 +33,20 @@ struct old_serial_port { unsigned short iomem_reg_shift; }; +/* + * This replaces serial_uart_config in include/linux/serial.h + */ +struct serial8250_config { + const char *name; + unsigned int fifo_size; + unsigned int tx_loadsz; + unsigned int flags; +}; + +#define UART_CAP_FIFO (1 << 8) /* UART has FIFO */ +#define UART_CAP_EFR (1 << 9) /* UART has EFR */ +#define UART_CAP_SLEEP (1 << 10) /* UART has IER sleep */ + #undef SERIAL_DEBUG_PCI #if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486)) --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/serial/8250_omap.c 2004-09-03 00:56:57.199465008 -0700 @@ -0,0 +1,125 @@ +/* + * linux/drivers/serial/8250_omap.c + * Partially copied from 8250_acorn.c + * + * Copyright (C) 1996-2003 Russell King. + * Copyright (C) 2004 Nokia Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define UART_SYSC 0x15 + +#define MAX_PORTS 3 + +struct omap_serial_port { + unsigned long baddr; + unsigned int irq; +}; + +static const struct omap_serial_port omap1510_serial_ports[] = { + { .baddr = (unsigned long)IO_ADDRESS(OMAP_UART1_BASE), .irq = INT_UART1 }, + { .baddr = (unsigned long)IO_ADDRESS(OMAP_UART2_BASE), .irq = INT_UART2 }, + { .baddr = (unsigned long)IO_ADDRESS(OMAP_UART3_BASE), .irq = INT_UART3 }, +}; + +static const struct omap_serial_port omap730_serial_ports[] = { + { .baddr = (unsigned long)IO_ADDRESS(OMAP_UART1_BASE), .irq = INT_730_UART_MODEM_1 }, + { .baddr = (unsigned long)IO_ADDRESS(OMAP_UART2_BASE), .irq = INT_730_UART_MODEM_IRDA_2 }, +}; + +static void +reset_port(struct serial_struct *port) +{ + if (cpu_is_omap1510()) + return; + writeb(0x01, port->iomap_base + (UART_SYSC << port->iomem_reg_shift)); + while (!(readb(port->iomap_base + (UART_SYSC << port->iomem_reg_shift)) & 0x01)); +} + +static inline int +serial_register_onedev(const struct omap_serial_port *port) +{ + struct serial_struct req; + + memset(&req, 0, sizeof(req)); + req.type = PORT_OMAP; + req.irq = port->irq; + req.flags = UPF_SKIP_TEST; + req.io_type = UPIO_MEM; + req.iomem_base = (unsigned char *) port->baddr; + req.iomap_base = port->baddr; + + if (cpu_is_omap1510()) { + req.baud_base = OMAP1510_BASE_BAUD; + req.iomem_reg_shift = 2; + } + if (cpu_is_omap1610() || cpu_is_omap5912() || cpu_is_omap1710()) { + req.baud_base = OMAP1610_BASE_BAUD; + req.iomem_reg_shift = 2; + } + if (cpu_is_omap730()) { + req.baud_base = OMAP1610_BASE_BAUD; + req.iomem_reg_shift = 0; + } + + reset_port(&req); + + return register_serial(&req); +} + +static int port_count = 0; +static int ports[MAX_PORTS]; + +static int __init omap_serial_init(void) +{ + int i; + const struct omap_serial_port *port_table = NULL; + + if (cpu_is_omap1510() || cpu_is_omap1610() || cpu_is_omap5912() + || cpu_is_omap1710()) { + port_count = 3; + port_table = omap1510_serial_ports; + } + if (cpu_is_omap730()) { + port_count = 2; + port_table = omap730_serial_ports; + } + for (i = 0; i < port_count; i++) { + ports[i] = serial_register_onedev(&port_table[i]); + } + printk(KERN_INFO "OMAP serial: activated %d ports\n", i); + + return 0; +} + +static void __exit omap_serial_exit(void) +{ + int i; + + for (i = 0; i < port_count; i++) + unregister_serial(ports[i]); +} + +MODULE_AUTHOR("Juha Yrjölä"); +MODULE_DESCRIPTION("OMAP 8250-compatible serial port driver"); +MODULE_LICENSE("GPL"); + +module_init(omap_serial_init); +module_exit(omap_serial_exit); --- linux-2.6.9-rc1/drivers/serial/8250_pnp.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/drivers/serial/8250_pnp.c 2004-09-03 00:56:33.270102824 -0700 @@ -379,7 +379,7 @@ static int __devinit check_resources(str * PnP modems, alternatively we must hardcode all modems in pnp_devices[] * table. */ -static int __devinit serial_pnp_guess_board(struct pnp_dev *dev, int *flags) +static int serial_pnp_guess_board(struct pnp_dev *dev) { if (!(check_name(pnp_dev_name(dev)) || (dev->card && check_name(dev->card->name)))) return -ENODEV; @@ -399,7 +399,7 @@ serial_pnp_probe(struct pnp_dev * dev, c struct serial_struct serial_req; int ret, line, flags = dev_id->driver_data; if (flags & UNKNOWN_DEV) { - ret = serial_pnp_guess_board(dev, &flags); + ret = serial_pnp_guess_board(dev); if (ret < 0) return ret; } @@ -430,11 +430,17 @@ static void __devexit serial_pnp_remove( unregister_serial(line - 1); } +static int serial_pnp_match(struct pnp_dev *dev) +{ + return serial_pnp_guess_board(dev); +} + static struct pnp_driver serial_pnp_driver = { .name = "serial", .id_table = pnp_dev_table, .probe = serial_pnp_probe, .remove = __devexit_p(serial_pnp_remove), + .match = serial_pnp_match, }; static int __init serial8250_pnp_init(void) --- linux-2.6.9-rc1/drivers/serial/Kconfig 2004-08-24 00:03:32.000000000 -0700 +++ 25/drivers/serial/Kconfig 2004-09-03 00:57:11.157343088 -0700 @@ -166,6 +166,13 @@ config SERIAL_8250_ACORN system, say Y to this option. The driver can handle 1, 2, or 3 port cards. If unsure, say N. +config SERIAL_8250_OMAP + tristate "OMAP serial port support" + depends on ARM && ARCH_OMAP && SERIAL_8250 + help + Say Y or M here if you want to use the integrated UARTs of the TI + OMAP multimedia processor. + config SERIAL_AMBA_PL010 tristate "ARM AMBA PL010 serial port support" depends on ARM_AMBA @@ -682,6 +689,7 @@ config SERIAL_SGI_L1_CONSOLE bool "SGI Altix L1 serial console support" depends on IA64_GENERIC || IA64_SGI_SN2 select SERIAL_CORE + select SERIAL_CORE_CONSOLE help If you have an SGI Altix and you would like to use the system controller serial port as your console (you want this!), @@ -726,4 +734,18 @@ config SERIAL_ICOM This driver can also be built as a module. If so, the module will be called icom. +config SERIAL_MPSC + bool "Marvell MPSC serial port support" + depends on PPC32 && MV64X60 + select SERIAL_CORE + help + Say Y here if you want to use the Marvell MPSC serial controller. + +config SERIAL_MPSC_CONSOLE + bool "Support for console on Marvell MPSC serial port" + depends on SERIAL_MPSC + select SERIAL_CORE_CONSOLE + help + Say Y here if you want to support a serial console on a Marvell MPSC. + endmenu --- linux-2.6.9-rc1/drivers/serial/Makefile 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/serial/Makefile 2004-09-03 00:57:11.157343088 -0700 @@ -15,6 +15,7 @@ obj-$(CONFIG_SERIAL_21285) += 21285.o obj-$(CONFIG_SERIAL_8250) += 8250.o $(serial-8250-y) obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o +obj-$(CONFIG_SERIAL_8250_OMAP) += 8250_omap.o obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o @@ -41,3 +42,4 @@ obj-$(CONFIG_SERIAL_BAST_SIO) += bast_si obj-$(CONFIG_SERIAL_SGI_L1_CONSOLE) += sn_console.o obj-$(CONFIG_SERIAL_CPM) += cpm_uart/ obj-$(CONFIG_SERIAL_MPC52xx) += mpc52xx_uart.o +obj-$(CONFIG_SERIAL_MPSC) += mpsc/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/serial/mpsc/Makefile 2004-09-03 00:57:11.158342936 -0700 @@ -0,0 +1,6 @@ +# +# Make file for the Marvell MPSC driver. +# + +obj-$(CONFIG_SERIAL_MPSC) += mpsc.o +obj-$(CONFIG_PPC32) += mpsc_ppc32.o --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/serial/mpsc/mpsc.c 2004-09-03 00:57:11.164342024 -0700 @@ -0,0 +1,1472 @@ +/* + * drivers/serial/mpsc/mpsc.c + * + * Generic driver for the MPSC (UART mode) on Marvell parts (e.g., GT64240, + * GT64260, MV64340, MV64360, GT96100, ... ). + * + * Author: Mark A. Greer + * + * Based on an old MPSC driver that was in the linuxppc tree. It appears to + * have been created by Chris Zankel (formerly of MontaVista) but there + * is no proper Copyright so I'm not sure. Apparently, parts were also + * taken from PPCBoot (now U-Boot). Also based on drivers/serial/8250.c + * by Russell King. + * + * 2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +/* + * The MPSC interface is much like a typical network controller's interface. + * That is, you set up separate rings of descriptors for transmitting and + * receiving data. There is also a pool of buffers with (one buffer per + * descriptor) that incoming data are dma'd into or outgoing data are dma'd + * out of. + * + * The MPSC requires two other controllers to be able to work. The Baud Rate + * Generator (BRG) provides a clock at programmable frequencies which determines + * the baud rate. The Serial DMA Controller (SDMA) takes incoming data from the + * MPSC and DMA's it into memory or DMA's outgoing data and passes it to the + * MPSC. It is actually the SDMA interrupt that the driver uses to keep the + * transmit and receive "engines" going (i.e., indicate data has been + * transmitted or received). + * + * NOTES: + * + * 1) Some chips have an erratum where several regs cannot be + * read. To work around that, we keep a local copy of those regs in + * 'mpsc_port_info' and use the *_M or *_S macros when accessing those regs. + * + * 2) Some chips have an erratum where the chip will hang when the SDMA ctlr + * accesses system mem in a cache coherent region. This *should* be a + * show-stopper when coherency is turned on but it seems to work okay as + * long as there are no snoop hits. Therefore, there are explicit cache + * management macros to manage the cache ensuring there are no snoop hits. + * + * 3) AFAICT, hardware flow control isn't supported by the controller --MAG. + */ + +#include "mpsc.h" + +/* + * Define how this driver is known to the outside (we've been assigned a + * range on the "Low-density serial ports" major). + */ +#define MPSC_MAJOR 204 +#define MPSC_MINOR_START 5 /* XXXX */ +#define MPSC_DRIVER_NAME "MPSC" +#define MPSC_DEVFS_NAME "ttym/" +#define MPSC_DEV_NAME "ttyM" +#define MPSC_VERSION "1.00" + +static struct mpsc_port_info mpsc_ports[MPSC_NUM_CTLRS]; +static struct mpsc_shared_regs mpsc_shared_regs; + +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +/* + ****************************************************************************** + * + * Baud Rate Generator Routines (BRG) + * + ****************************************************************************** + */ +static void mpsc_brg_init(struct mpsc_port_info *pi, u32 clk_src) +{ + if (pi->brg_can_tune) { + MPSC_MOD_FIELD_M(pi, brg, BRG_BCR, 1, 25, 0); + } + + MPSC_MOD_FIELD_M(pi, brg, BRG_BCR, 4, 18, clk_src); + MPSC_MOD_FIELD(pi, brg, BRG_BTR, 16, 0, 0); + return; +} + +static void mpsc_brg_enable(struct mpsc_port_info *pi) +{ + MPSC_MOD_FIELD_M(pi, brg, BRG_BCR, 1, 16, 1); + return; +} + +static void mpsc_brg_disable(struct mpsc_port_info *pi) +{ + MPSC_MOD_FIELD_M(pi, brg, BRG_BCR, 1, 16, 0); + return; +} + +static inline void mpsc_set_baudrate(struct mpsc_port_info *pi, u32 baud) +{ + /* + * To set the baud, we adjust the CDV field in the BRG_BCR reg. + * From manual: Baud = clk / ((CDV+1)*2) ==> CDV = (clk / (baud*2)) - 1. + * However, the input clock is divided by 16 in the MPSC b/c of how + * 'MPSC_MMCRH' was set up so we have to divide the 'clk' used in our + * calculation by 16 to account for that. So the real calculation + * that accounts for the way the mpsc is set up is: + * CDV = (clk / (baud*2*16)) - 1 ==> CDV = (clk / (baud << 5)) - 1. + */ + u32 cdv = (pi->port.uartclk / (baud << 5)) - 1; + + mpsc_brg_disable(pi); + MPSC_MOD_FIELD_M(pi, brg, BRG_BCR, 16, 0, cdv); + mpsc_brg_enable(pi); + + return; +} + +/* + ****************************************************************************** + * + * Serial DMA Routines (SDMA) + * + ****************************************************************************** + */ + +static void mpsc_sdma_burstsize(struct mpsc_port_info *pi, u32 burst_size) +{ + u32 v; + + DBG("mpsc_sdma_burstsize[%d]: burst_size: %d\n", + pi->port.line, burst_size); + + burst_size >>= 3; /* Divide by 8 b/c reg values are 8-byte chunks */ + + if (burst_size < 2) + v = 0x0; /* 1 64-bit word */ + else if (burst_size < 4) + v = 0x1; /* 2 64-bit words */ + else if (burst_size < 8) + v = 0x2; /* 4 64-bit words */ + else + v = 0x3; /* 8 64-bit words */ + + MPSC_MOD_FIELD(pi, sdma, SDMA_SDC, 2, 12, v); + return; +} + +static void mpsc_sdma_init(struct mpsc_port_info *pi, u32 burst_size) +{ + DBG("mpsc_sdma_init[%d]: burst_size: %d\n", pi->port.line, burst_size); + + MPSC_MOD_FIELD(pi, sdma, SDMA_SDC, 10, 0, 0x03f); + mpsc_sdma_burstsize(pi, burst_size); + return; +} + +static inline u32 mpsc_sdma_intr_mask(struct mpsc_port_info *pi, u32 mask) +{ + u32 old, v; + + DBG("mpsc_sdma_intr_mask[%d]: mask: 0x%x\n", pi->port.line, mask); + + old = v = MPSC_READ_S(pi, sdma_intr, SDMA_INTR_MASK); + mask &= 0xf; + if (pi->port.line) + mask <<= 8; + v &= ~mask; + MPSC_WRITE_S(pi, sdma_intr, SDMA_INTR_MASK, v); + + if (pi->port.line) + old >>= 8; + return old & 0xf; +} + +static inline void mpsc_sdma_intr_unmask(struct mpsc_port_info *pi, u32 mask) +{ + u32 v; + + DBG("mpsc_sdma_intr_unmask[%d]: mask: 0x%x\n", pi->port.line, mask); + + v = MPSC_READ_S(pi, sdma_intr, SDMA_INTR_MASK); + mask &= 0xf; + if (pi->port.line) + mask <<= 8; + v |= mask; + MPSC_WRITE_S(pi, sdma_intr, SDMA_INTR_MASK, v); + return; +} + +static inline void mpsc_sdma_intr_ack(struct mpsc_port_info *pi) +{ + DBG("mpsc_sdma_intr_ack[%d]: Acknowledging IRQ\n", pi->port.line); + MPSC_WRITE(pi, sdma_intr, SDMA_INTR_CAUSE, 0); + return; +} + +static inline void +mpsc_sdma_set_rx_ring(struct mpsc_port_info *pi, struct mpsc_rx_desc *rxre_p) +{ + DBG("mpsc_sdma_set_rx_ring[%d]: rxre_p: 0x%x\n", + pi->port.line, (u32) rxre_p); + + MPSC_WRITE(pi, sdma, SDMA_SCRDP, (u32) rxre_p); + return; +} + +static inline void +mpsc_sdma_set_tx_ring(struct mpsc_port_info *pi, + volatile struct mpsc_tx_desc *txre_p) +{ + MPSC_WRITE(pi, sdma, SDMA_SFTDP, (u32) txre_p); + MPSC_WRITE(pi, sdma, SDMA_SCTDP, (u32) txre_p); + return; +} + +static inline void mpsc_sdma_cmd(struct mpsc_port_info *pi, u32 val) +{ + u32 v; + + v = MPSC_READ(pi, sdma, SDMA_SDCM); + if (val) + v |= val; + else + v = 0; + MPSC_WRITE(pi, sdma, SDMA_SDCM, v); + return; +} + +static inline void mpsc_sdma_start_tx(struct mpsc_port_info *pi) +{ + volatile struct mpsc_tx_desc *txre, *txre_p; + + /* If tx isn't running & there's a desc ready to go, start it */ + if (!pi->txr_running) { + txre = (volatile struct mpsc_tx_desc *)(pi->txr + + (pi->txr_tail * + MPSC_TXRE_SIZE)); + MPSC_CACHE_INVALIDATE(pi, (u32) txre, + (u32) txre + MPSC_TXRE_SIZE); + + if (be32_to_cpu(txre->cmdstat) & SDMA_DESC_CMDSTAT_O) { + txre_p = (struct mpsc_tx_desc *)(pi->txr_p + + (pi->txr_tail * + MPSC_TXRE_SIZE)); + + mpsc_sdma_set_tx_ring(pi, txre_p); + mpsc_sdma_cmd(pi, SDMA_SDCM_STD | SDMA_SDCM_TXD); + + pi->txr_running = 1; + } + } + + return; +} + +static inline void mpsc_sdma_stop(struct mpsc_port_info *pi) +{ + DBG("mpsc_sdma_stop[%d]: Stopping SDMA\n", pi->port.line); + + /* Abort any SDMA transfers */ + mpsc_sdma_cmd(pi, 0); + mpsc_sdma_cmd(pi, SDMA_SDCM_AR | SDMA_SDCM_AT); + + /* Clear the SDMA current and first TX and RX pointers */ + mpsc_sdma_set_tx_ring(pi, 0); + mpsc_sdma_set_rx_ring(pi, 0); + + /* Disable interrupts */ + mpsc_sdma_intr_mask(pi, 0xf); + mpsc_sdma_intr_ack(pi); + udelay(1000); + + return; +} + +static inline uint mpsc_sdma_tx_active(struct mpsc_port_info *pi) +{ + return MPSC_READ(pi, sdma, SDMA_SDCM) & SDMA_SDCM_TXD; +} + +/* + ****************************************************************************** + * + * Multi-Protocol Serial Controller Routines (MPSC) + * + ****************************************************************************** + */ + +static void mpsc_hw_init(struct mpsc_port_info *pi) +{ + DBG("mpsc_hw_init[%d]: Initializing hardware\n", pi->port.line); + + /* Set up clock routing */ + MPSC_MOD_FIELD_S(pi, mpsc_routing, MPSC_MRR, 3, 0, 0); + MPSC_MOD_FIELD_S(pi, mpsc_routing, MPSC_MRR, 3, 6, 0); + MPSC_MOD_FIELD_S(pi, mpsc_routing, MPSC_RCRR, 4, 0, 0); + MPSC_MOD_FIELD_S(pi, mpsc_routing, MPSC_RCRR, 4, 8, 1); + MPSC_MOD_FIELD_S(pi, mpsc_routing, MPSC_TCRR, 4, 0, 0); + MPSC_MOD_FIELD_S(pi, mpsc_routing, MPSC_TCRR, 4, 8, 1); + + /* Put MPSC in UART mode & enabel Tx/Rx egines */ + MPSC_WRITE(pi, mpsc, MPSC_MMCRL, 0x000004c4); + + /* No preamble, 16x divider, low-latency, */ + MPSC_WRITE(pi, mpsc, MPSC_MMCRH, 0x04400400); + + MPSC_WRITE_M(pi, mpsc, MPSC_CHR_1, 0); + MPSC_WRITE_M(pi, mpsc, MPSC_CHR_2, 0); + MPSC_WRITE(pi, mpsc, MPSC_CHR_3, pi->mpsc_max_idle); + MPSC_WRITE(pi, mpsc, MPSC_CHR_4, 0); + MPSC_WRITE(pi, mpsc, MPSC_CHR_5, 0); + MPSC_WRITE(pi, mpsc, MPSC_CHR_6, 0); + MPSC_WRITE(pi, mpsc, MPSC_CHR_7, 0); + MPSC_WRITE(pi, mpsc, MPSC_CHR_8, 0); + MPSC_WRITE(pi, mpsc, MPSC_CHR_9, 0); + MPSC_WRITE(pi, mpsc, MPSC_CHR_10, 0); + + return; +} + +static inline void mpsc_enter_hunt(struct mpsc_port_info *pi) +{ + u32 v; + + DBG("mpsc_enter_hunt[%d]: Hunting...\n", pi->port.line); + + MPSC_MOD_FIELD_M(pi, mpsc, MPSC_CHR_2, 1, 31, 1); + + /* If erratum prevents reading CHR_2, just delay for a while */ + if (pi->mirror_regs) { + udelay(100); + } else + do { + v = MPSC_READ_M(pi, mpsc, MPSC_CHR_2); + } while (v & MPSC_CHR_2_EH); + + return; +} + +static inline void mpsc_freeze(struct mpsc_port_info *pi) +{ + DBG("mpsc_freeze[%d]: Freezing\n", pi->port.line); + + MPSC_MOD_FIELD_M(pi, mpsc, MPSC_MPCR, 1, 9, 1); + return; +} + +static inline void mpsc_unfreeze(struct mpsc_port_info *pi) +{ + MPSC_MOD_FIELD_M(pi, mpsc, MPSC_MPCR, 1, 9, 0); + + DBG("mpsc_unfreeze[%d]: Unfrozen\n", pi->port.line); + return; +} + +static inline void mpsc_set_char_length(struct mpsc_port_info *pi, u32 len) +{ + DBG("mpsc_set_char_length[%d]: char len: %d\n", pi->port.line, len); + + MPSC_MOD_FIELD_M(pi, mpsc, MPSC_MPCR, 2, 12, len); + return; +} + +static inline void mpsc_set_stop_bit_length(struct mpsc_port_info *pi, u32 len) +{ + DBG("mpsc_set_stop_bit_length[%d]: stop bits: %d\n", pi->port.line, + len); + + MPSC_MOD_FIELD_M(pi, mpsc, MPSC_MPCR, 1, 14, len); + return; +} + +static inline void mpsc_set_parity(struct mpsc_port_info *pi, u32 p) +{ + DBG("mpsc_set_parity[%d]: parity bits: 0x%x\n", pi->port.line, p); + + MPSC_MOD_FIELD_M(pi, mpsc, MPSC_CHR_2, 2, 2, p); /* TPM */ + MPSC_MOD_FIELD_M(pi, mpsc, MPSC_CHR_2, 2, 18, p); /* RPM */ + return; +} + +/* + ****************************************************************************** + * + * Driver Init Routines + * + ****************************************************************************** + */ + +static void mpsc_init_hw(struct mpsc_port_info *pi) +{ + DBG("mpsc_init_hw[%d]: Initializing\n", pi->port.line); + + mpsc_brg_init(pi, pi->brg_clk_src); + mpsc_brg_enable(pi); + mpsc_sdma_init(pi, dma_get_cache_alignment()); /* burst a cacheline */ + mpsc_sdma_stop(pi); + mpsc_hw_init(pi); + + return; +} + +static int mpsc_alloc_ring_mem(struct mpsc_port_info *pi) +{ + int rc = 0; + static void mpsc_free_ring_mem(struct mpsc_port_info *pi); + + DBG("mpsc_alloc_ring_mem[%d]: Allocating ring mem\n", pi->port.line); + + if (!pi->dma_region) { + if (!dma_supported(pi->port.dev, 0xffffffff)) { + printk(KERN_ERR "MPSC: inadequate DMA support\n"); + rc = -ENXIO; + } else if ((pi->dma_region = dma_alloc_coherent(pi->port.dev, + MPSC_DMA_ALLOC_SIZE, &pi->dma_region_p, GFP_KERNEL)) + == NULL) { + + printk(KERN_ERR "MPSC: can't alloc Desc region\n"); + rc = -ENOMEM; + } else + MPSC_CACHE_INVALIDATE(pi, pi->dma_region, + pi->dma_region + + MPSC_DMA_ALLOC_SIZE); + } + + return rc; +} + +static void mpsc_free_ring_mem(struct mpsc_port_info *pi) +{ + DBG("mpsc_free_ring_mem[%d]: Freeing ring mem\n", pi->port.line); + + if (pi->dma_region) { + MPSC_CACHE_INVALIDATE(pi, pi->dma_region, + pi->dma_region + MPSC_DMA_ALLOC_SIZE); + dma_free_coherent(pi->port.dev, MPSC_DMA_ALLOC_SIZE, + pi->dma_region, pi->dma_region_p); + + pi->dma_region = NULL; + pi->dma_region_p = (dma_addr_t) NULL; + } + + return; +} + +static void mpsc_init_rings(struct mpsc_port_info *pi) +{ + struct mpsc_rx_desc *rxre; + struct mpsc_tx_desc *txre; + dma_addr_t dp, dp_p; + u8 *bp, *bp_p; + int i; + + DBG("mpsc_init_rings[%d]: Initializing rings\n", pi->port.line); + + BUG_ON(pi->dma_region == NULL); + + memset(pi->dma_region, 0, MPSC_DMA_ALLOC_SIZE); + + /* + * Descriptors & buffers are multiples of cacheline size and must be + * cacheline aligned. + */ + dp = ALIGN((u32) pi->dma_region, dma_get_cache_alignment()); + dp_p = ALIGN((u32) pi->dma_region_p, dma_get_cache_alignment()); + + /* + * Partition dma region into rx ring descriptor, rx buffers, + * tx ring descriptors, and tx buffers. + */ + pi->rxr = dp; + pi->rxr_p = dp_p; + dp += MPSC_RXR_SIZE; + dp_p += MPSC_RXR_SIZE; + + pi->rxb = (u8 *) dp; + pi->rxb_p = (u8 *) dp_p; + dp += MPSC_RXB_SIZE; + dp_p += MPSC_RXB_SIZE; + + pi->rxr_posn = 0; + + pi->txr = dp; + pi->txr_p = dp_p; + dp += MPSC_TXR_SIZE; + dp_p += MPSC_TXR_SIZE; + + pi->txb = (u8 *) dp; + pi->txb_p = (u8 *) dp_p; + + pi->txr_running = 0; + pi->txr_head = 0; + pi->txr_tail = 0; + + /* Init rx ring descriptors */ + dp = pi->rxr; + dp_p = pi->rxr_p; + bp = pi->rxb; + bp_p = pi->rxb_p; + + for (i = 0; i < MPSC_RXR_ENTRIES; i++) { + rxre = (struct mpsc_rx_desc *)dp; + + rxre->bufsize = cpu_to_be16(MPSC_RXBE_SIZE); + rxre->bytecnt = cpu_to_be16(0); + rxre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O | + SDMA_DESC_CMDSTAT_EI | + SDMA_DESC_CMDSTAT_F | + SDMA_DESC_CMDSTAT_L); + rxre->link = cpu_to_be32(dp_p + MPSC_RXRE_SIZE); + rxre->buf_ptr = cpu_to_be32(bp_p); + MPSC_CACHE_FLUSH(pi, dp, dp + MPSC_RXRE_SIZE); + MPSC_CACHE_INVALIDATE(pi, bp, bp + MPSC_RXBE_SIZE); + + dp += MPSC_RXRE_SIZE; + dp_p += MPSC_RXRE_SIZE; + bp += MPSC_RXBE_SIZE; + bp_p += MPSC_RXBE_SIZE; + } + rxre->link = cpu_to_be32(pi->rxr_p); /* Wrap last back to first */ + MPSC_CACHE_FLUSH(pi, dp, dp + MPSC_RXRE_SIZE); + + /* Init tx ring descriptors */ + dp = pi->txr; + dp_p = pi->txr_p; + bp = pi->txb; + bp_p = pi->txb_p; + + for (i = 0; i < MPSC_TXR_ENTRIES; i++) { + txre = (struct mpsc_tx_desc *)dp; + + txre->link = cpu_to_be32(dp_p + MPSC_TXRE_SIZE); + txre->buf_ptr = cpu_to_be32(bp_p); + MPSC_CACHE_FLUSH(pi, dp, dp + MPSC_TXRE_SIZE); + + dp += MPSC_TXRE_SIZE; + dp_p += MPSC_TXRE_SIZE; + bp += MPSC_TXBE_SIZE; + bp_p += MPSC_TXBE_SIZE; + } + txre->link = cpu_to_be32(pi->txr_p); /* Wrap last back to first */ + MPSC_CACHE_FLUSH(pi, dp, dp + MPSC_TXRE_SIZE); + + return; +} + +static void mpsc_uninit_rings(struct mpsc_port_info *pi) +{ + DBG("mpsc_uninit_rings[%d]: Uninitializing rings\n", pi->port.line); + + BUG_ON(pi->dma_region == NULL); + + pi->rxr = 0; + pi->rxr_p = 0; + pi->rxb = NULL; + pi->rxb_p = NULL; + pi->rxr_posn = 0; + + pi->txr = 0; + pi->txr_p = 0; + pi->txb = NULL; + pi->txb_p = NULL; + pi->txr_running = 0; + pi->txr_head = 0; + pi->txr_tail = 0; + + return; +} + +static int mpsc_make_ready(struct mpsc_port_info *pi) +{ + int rc; + + DBG("mpsc_make_ready[%d]: Making cltr ready\n", pi->port.line); + + if (!pi->ready) { + mpsc_init_hw(pi); + if ((rc = mpsc_alloc_ring_mem(pi))) + return rc; + mpsc_init_rings(pi); + pi->ready = 1; + } + + return 0; +} + +/* + ****************************************************************************** + * + * Interrupt Handling Routines + * + ****************************************************************************** + */ + +static inline int mpsc_rx_intr(struct mpsc_port_info *pi, struct pt_regs *regs) +{ + volatile struct mpsc_rx_desc *rxre; + struct tty_struct *tty = pi->port.info->tty; + u32 cmdstat, bytes_in; + u8 *bp; + int rc = 0; + static void mpsc_start_rx(struct mpsc_port_info *pi); + + DBG("mpsc_rx_intr[%d]: Handling Rx intr\n", pi->port.line); + + rxre = (volatile struct mpsc_rx_desc *)(pi->rxr + + (pi->rxr_posn * + MPSC_RXRE_SIZE)); + MPSC_CACHE_INVALIDATE(pi, (u32) rxre, (u32) rxre + MPSC_RXRE_SIZE); + + /* + * Loop through Rx descriptors handling ones that have been completed. + */ + while (!((cmdstat = be32_to_cpu(rxre->cmdstat)) & SDMA_DESC_CMDSTAT_O)){ + bytes_in = be16_to_cpu(rxre->bytecnt); + + if (unlikely((tty->flip.count + bytes_in) >= TTY_FLIPBUF_SIZE)){ + tty->flip.work.func((void *)tty); + + if ((tty->flip.count + bytes_in) >= TTY_FLIPBUF_SIZE) { + /* Take what we can, throw away the rest */ + bytes_in = TTY_FLIPBUF_SIZE - tty->flip.count; + cmdstat &= ~SDMA_DESC_CMDSTAT_PE; + } + } + + bp = pi->rxb + (pi->rxr_posn * MPSC_RXBE_SIZE); + MPSC_CACHE_INVALIDATE(pi, bp, bp + MPSC_RXBE_SIZE); + + /* + * Other than for parity error, the manual provides little + * info on what data will be in a frame flagged by any of + * these errors. For parity error, it is the last byte in + * the buffer that had the error. As for the rest, I guess + * we'll assume there is no data in the buffer. + * If there is...it gets lost. + */ + if (cmdstat & (SDMA_DESC_CMDSTAT_BR | SDMA_DESC_CMDSTAT_FR | + SDMA_DESC_CMDSTAT_OR)) { + + pi->port.icount.rx++; + + if (cmdstat & SDMA_DESC_CMDSTAT_BR) { /* Break */ + pi->port.icount.brk++; + + if (uart_handle_break(&pi->port)) + goto next_frame; + } else if (cmdstat & SDMA_DESC_CMDSTAT_FR)/* Framing */ + pi->port.icount.frame++; + else if (cmdstat & SDMA_DESC_CMDSTAT_OR) /* Overrun */ + pi->port.icount.overrun++; + + cmdstat &= pi->port.read_status_mask; + + if (!(cmdstat & pi->port.ignore_status_mask)) { + if (cmdstat & SDMA_DESC_CMDSTAT_BR) + *tty->flip.flag_buf_ptr = TTY_BREAK; + else if (cmdstat & SDMA_DESC_CMDSTAT_FR) + *tty->flip.flag_buf_ptr = TTY_FRAME; + else if (cmdstat & SDMA_DESC_CMDSTAT_OR) + *tty->flip.flag_buf_ptr = TTY_OVERRUN; + + tty->flip.flag_buf_ptr++; + *tty->flip.char_buf_ptr = '\0'; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + } else { + if (uart_handle_sysrq_char(&pi->port, *bp, regs)) { + bp++; + bytes_in--; + } + + memcpy(tty->flip.char_buf_ptr, bp, bytes_in); + memset(tty->flip.flag_buf_ptr, TTY_NORMAL, bytes_in); + + tty->flip.char_buf_ptr += bytes_in; + tty->flip.flag_buf_ptr += bytes_in; + tty->flip.count += bytes_in; + pi->port.icount.rx += bytes_in; + + cmdstat &= SDMA_DESC_CMDSTAT_PE; + + if (cmdstat) { /* Parity */ + pi->port.icount.parity++; + + if (!(cmdstat & pi->port.read_status_mask)) + *(tty->flip.flag_buf_ptr - 1) = + TTY_FRAME; + } + } + + next_frame: + rxre->bytecnt = cpu_to_be16(0); + wmb(); + rxre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O | + SDMA_DESC_CMDSTAT_EI | + SDMA_DESC_CMDSTAT_F | + SDMA_DESC_CMDSTAT_L); + MPSC_CACHE_FLUSH(pi, (u32) rxre, (u32) rxre + MPSC_RXRE_SIZE); + + /* Advance to next descriptor */ + pi->rxr_posn = (pi->rxr_posn + 1) & (MPSC_RXR_ENTRIES - 1); + rxre = (volatile struct mpsc_rx_desc *)(pi->rxr + + (pi->rxr_posn * + MPSC_RXRE_SIZE)); + MPSC_CACHE_INVALIDATE(pi, (u32) rxre, + (u32) rxre + MPSC_RXRE_SIZE); + + rc = 1; + } + + /* Restart rx engine, if its stopped */ + if ((MPSC_READ(pi, sdma, SDMA_SDCM) & SDMA_SDCM_ERD) == 0) { + mpsc_start_rx(pi); + } + + tty_flip_buffer_push(tty); + return rc; +} + +static inline void +mpsc_setup_tx_desc(struct mpsc_port_info *pi, u32 count, u32 intr) +{ + volatile struct mpsc_tx_desc *txre; + + txre = (volatile struct mpsc_tx_desc *)(pi->txr + + (pi->txr_head * + MPSC_TXRE_SIZE)); + MPSC_CACHE_INVALIDATE(pi, (u32) txre, (u32) txre + MPSC_TXRE_SIZE); + + txre->bytecnt = cpu_to_be16(count); + txre->shadow = txre->bytecnt; + wmb(); /* ensure cmdstat is last field updated */ + txre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O | SDMA_DESC_CMDSTAT_F | + SDMA_DESC_CMDSTAT_L | ((intr) ? + SDMA_DESC_CMDSTAT_EI + : 0)); + MPSC_CACHE_FLUSH(pi, (u32) txre, (u32) txre + MPSC_TXRE_SIZE); + + return; +} + +static inline void mpsc_copy_tx_data(struct mpsc_port_info *pi) +{ + struct circ_buf *xmit = &pi->port.info->xmit; + u8 *bp; + u32 i; + + /* Make sure the desc ring isn't full */ + while (CIRC_CNT(pi->txr_head, pi->txr_tail, MPSC_TXR_ENTRIES) < + (MPSC_TXR_ENTRIES - 1)) { + if (pi->port.x_char) { + /* + * Ideally, we should use the TCS field in + * CHR_1 to put the x_char out immediately but + * errata prevents us from being able to read + * CHR_2 to know that its safe to write to + * CHR_1. Instead, just put it in-band with + * all the other Tx data. + */ + bp = pi->txb + (pi->txr_head * MPSC_TXBE_SIZE); + MPSC_CACHE_INVALIDATE(pi, bp, bp + MPSC_TXBE_SIZE); + *bp = pi->port.x_char; + pi->port.x_char = 0; + i = 1; + } else if (!uart_circ_empty(xmit) + && !uart_tx_stopped(&pi->port)) { + i = MIN(MPSC_TXBE_SIZE, uart_circ_chars_pending(xmit)); + i = MIN(i, CIRC_CNT_TO_END(xmit->head, xmit->tail, + UART_XMIT_SIZE)); + bp = pi->txb + (pi->txr_head * MPSC_TXBE_SIZE); + MPSC_CACHE_INVALIDATE(pi, bp, bp + MPSC_TXBE_SIZE); + memcpy(bp, &xmit->buf[xmit->tail], i); + xmit->tail = (xmit->tail + i) & (UART_XMIT_SIZE - 1); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&pi->port); + } else { /* All tx data copied into ring bufs */ + return; + } + + MPSC_CACHE_FLUSH(pi, bp, bp + MPSC_TXBE_SIZE); + mpsc_setup_tx_desc(pi, i, 1); + + /* Advance to next descriptor */ + pi->txr_head = (pi->txr_head + 1) & (MPSC_TXR_ENTRIES - 1); + } + + return; +} + +static inline int mpsc_tx_intr(struct mpsc_port_info *pi) +{ + volatile struct mpsc_tx_desc *txre; + int rc = 0; + + if (pi->txr_running) { + txre = (volatile struct mpsc_tx_desc *)(pi->txr + + (pi->txr_tail * + MPSC_TXRE_SIZE)); + MPSC_CACHE_INVALIDATE(pi, (u32) txre, + (u32) txre + MPSC_TXRE_SIZE); + + while (!(be32_to_cpu(txre->cmdstat) & SDMA_DESC_CMDSTAT_O)) { + pi->txr_running = 0; + rc = 1; + pi->port.icount.tx += be16_to_cpu(txre->bytecnt); + pi->txr_tail = (pi->txr_tail + 1) & + (MPSC_TXR_ENTRIES - 1); + + /* If no more data to tx, fall out of loop */ + if (pi->txr_head == pi->txr_tail) + break; + + txre = (volatile struct mpsc_tx_desc *)(pi->txr + + (pi->txr_tail * MPSC_TXRE_SIZE)); + MPSC_CACHE_INVALIDATE(pi, (u32) txre, + (u32) txre + MPSC_TXRE_SIZE); + } + + mpsc_sdma_start_tx(pi); /* start next desc if ready */ + + /* + * It is possible to have more tx data waiting in the serial + * core's tx buffer so we need to check here so it doesn't + * get missed. + */ + if (!pi->txr_running) { + mpsc_copy_tx_data(pi); + mpsc_sdma_start_tx(pi); + } + } + + return rc; +} + +/* + * This is the driver's interrupt handler. To avoid a race, we first clear + * the interrupt, then handle any completed Rx/Tx descriptors. When done + * handling those descriptors, we restart the Rx/Tx engines if they're stopped. + */ +static irqreturn_t mpsc_sdma_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct mpsc_port_info *pi = dev_id; + ulong iflags; + int rc = IRQ_NONE; + + DBG("mpsc_sdma_intr[%d]: SDMA Interrupt Received\n", pi->port.line); + + spin_lock_irqsave(&pi->port.lock, iflags); + mpsc_sdma_intr_ack(pi); + if (mpsc_rx_intr(pi, regs) || mpsc_tx_intr(pi)) + rc = IRQ_HANDLED; + spin_unlock_irqrestore(&pi->port.lock, iflags); + + DBG("mpsc_sdma_intr[%d]: SDMA Interrupt Handled\n", pi->port.line); + return rc; +} + +/* + ****************************************************************************** + * + * serial_core.c Interface routines + * + ****************************************************************************** + */ +static uint mpsc_tx_empty(struct uart_port *port) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + ulong iflags; + uint rc; + + spin_lock_irqsave(&pi->port.lock, iflags); + rc = mpsc_sdma_tx_active(pi) ? 0 : TIOCSER_TEMT; + spin_unlock_irqrestore(&pi->port.lock, iflags); + + return rc; +} + +static void mpsc_set_mctrl(struct uart_port *port, uint mctrl) +{ + /* Have no way to set modem control lines AFAICT */ + return; +} + +static uint mpsc_get_mctrl(struct uart_port *port) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + u32 mflags, status; + ulong iflags; + + spin_lock_irqsave(&pi->port.lock, iflags); + status = MPSC_READ_M(pi, mpsc, MPSC_CHR_10); + spin_unlock_irqrestore(&pi->port.lock, iflags); + + mflags = 0; + if (status & 0x1) + mflags |= TIOCM_CTS; + if (status & 0x2) + mflags |= TIOCM_CAR; + + return mflags | TIOCM_DSR; /* No way to tell if DSR asserted */ +} + +static void mpsc_stop_tx(struct uart_port *port, uint tty_start) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + + DBG("mpsc_stop_tx[%d]: tty_start: %d\n", port->line, tty_start); + + mpsc_freeze(pi); + return; +} + +static void mpsc_start_tx(struct uart_port *port, uint tty_start) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + + mpsc_unfreeze(pi); + mpsc_copy_tx_data(pi); + mpsc_sdma_start_tx(pi); + + DBG("mpsc_start_tx[%d]: tty_start: %d\n", port->line, tty_start); + return; +} + +static void mpsc_start_rx(struct mpsc_port_info *pi) +{ + DBG("mpsc_start_rx[%d]: Starting...\n", pi->port.line); + + if (pi->rcv_data) { + mb(); + mpsc_enter_hunt(pi); + mpsc_sdma_cmd(pi, SDMA_SDCM_ERD); + } + return; +} + +static void mpsc_stop_rx(struct uart_port *port) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + + DBG("mpsc_stop_rx[%d]: Stopping...\n", port->line); + + mpsc_sdma_cmd(pi, SDMA_SDCM_AR); + return; +} + +static void mpsc_enable_ms(struct uart_port *port) +{ + return; /* Not supported */ +} + +static void mpsc_break_ctl(struct uart_port *port, int ctl) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + ulong flags; + + spin_lock_irqsave(&pi->port.lock, flags); + if (ctl) { + /* Send as many BRK chars as we can */ + MPSC_WRITE_M(pi, mpsc, MPSC_CHR_1, 0x00ff0000); + } else { + /* Stop sending BRK chars */ + MPSC_WRITE_M(pi, mpsc, MPSC_CHR_1, 0); + } + spin_unlock_irqrestore(&pi->port.lock, flags); + + return; +} + +static int mpsc_startup(struct uart_port *port) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + u32 flag = 0; + int rc; + + DBG("mpsc_startup[%d]: Starting up MPSC, irq: %d\n", + port->line, pi->port.irq); + + if ((rc = mpsc_make_ready(pi)) == 0) { + /* Setup IRQ handler */ + mpsc_sdma_intr_ack(pi); + + /* If irq's are shared, need to set flag */ + if (mpsc_ports[0].port.irq == mpsc_ports[1].port.irq) + flag = SA_SHIRQ; + + if (request_irq(pi->port.irq, mpsc_sdma_intr, flag, + "MPSC/SDMA", pi)) { + printk(KERN_ERR "MPSC: Can't get SDMA IRQ %d\n", + pi->port.irq); + } + + mpsc_sdma_intr_unmask(pi, 0xf); + mpsc_sdma_set_rx_ring(pi, (struct mpsc_rx_desc *)(pi->rxr_p + + (pi->rxr_posn * MPSC_RXRE_SIZE))); + } + + return rc; +} + +static void mpsc_shutdown(struct uart_port *port) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + static void mpsc_release_port(struct uart_port *port); + + DBG("mpsc_shutdown[%d]: Shutting down MPSC\n", port->line); + + while (mpsc_sdma_tx_active(pi)) + udelay(100); /* Let Tx ring drain */ + + mpsc_sdma_stop(pi); + free_irq(pi->port.irq, pi); + return; +} + +static void +mpsc_set_termios(struct uart_port *port, struct termios *termios, + struct termios *old) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + u32 baud, quot; + ulong flags; + u32 chr_bits, stop_bits, par; + + pi->c_iflag = termios->c_iflag; + pi->c_cflag = termios->c_cflag; + + switch (termios->c_cflag & CSIZE) { + case CS5: + chr_bits = MPSC_MPCR_CL_5; + break; + case CS6: + chr_bits = MPSC_MPCR_CL_6; + break; + case CS7: + chr_bits = MPSC_MPCR_CL_7; + break; + default: + case CS8: + chr_bits = MPSC_MPCR_CL_8; + break; + } + + if (termios->c_cflag & CSTOPB) + stop_bits = MPSC_MPCR_SBL_2; + else + stop_bits = MPSC_MPCR_SBL_1; + + par = MPSC_CHR_2_PAR_EVEN; + if (termios->c_cflag & PARENB) { + if (termios->c_cflag & PARODD) + par = MPSC_CHR_2_PAR_ODD; +#ifdef CMSPAR + if (termios->c_cflag & CMSPAR) { + if (termios->c_cflag & PARODD) + par = MPSC_CHR_2_PAR_MARK; + else + par = MPSC_CHR_2_PAR_SPACE; + } +#endif + } + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk); + quot = uart_get_divisor(port, baud); + + spin_lock_irqsave(&pi->port.lock, flags); + + uart_update_timeout(port, termios->c_cflag, baud); + + mpsc_set_char_length(pi, chr_bits); + mpsc_set_stop_bit_length(pi, stop_bits); + mpsc_set_parity(pi, par); + mpsc_set_baudrate(pi, baud); + + /* Characters/events to read */ + pi->rcv_data = 1; + pi->port.read_status_mask = SDMA_DESC_CMDSTAT_OR; + + if (termios->c_iflag & INPCK) + pi->port.read_status_mask |= SDMA_DESC_CMDSTAT_PE | + SDMA_DESC_CMDSTAT_FR; + + if (termios->c_iflag & (BRKINT | PARMRK)) + pi->port.read_status_mask |= SDMA_DESC_CMDSTAT_BR; + + /* Characters/events to ignore */ + pi->port.ignore_status_mask = 0; + + if (termios->c_iflag & IGNPAR) + pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_PE | + SDMA_DESC_CMDSTAT_FR; + + if (termios->c_iflag & IGNBRK) { + pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_BR; + + if (termios->c_iflag & IGNPAR) + pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_OR; + } + + /* Ignore all chars if CREAD not set */ + if (!(termios->c_cflag & CREAD)) { + pi->rcv_data = 0; + } else { + mpsc_start_rx(pi); + } + + spin_unlock_irqrestore(&pi->port.lock, flags); + return; +} + +static const char *mpsc_type(struct uart_port *port) +{ + DBG("mpsc_type[%d]: port type: %s\n", port->line, MPSC_DRIVER_NAME); + return MPSC_DRIVER_NAME; +} + +static int mpsc_request_port(struct uart_port *port) +{ + /* Should make chip/platform specific call */ + return 0; +} + +static void mpsc_release_port(struct uart_port *port) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + + mpsc_uninit_rings(pi); + mpsc_free_ring_mem(pi); + pi->ready = 0; + + return; +} + +static void mpsc_config_port(struct uart_port *port, int flags) +{ + return; +} + +static int mpsc_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + int rc = 0; + + DBG("mpsc_verify_port[%d]: Verifying port data\n", pi->port.line); + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_MPSC) + rc = -EINVAL; + if (pi->port.irq != ser->irq) + rc = -EINVAL; + if (ser->io_type != SERIAL_IO_MEM) + rc = -EINVAL; + if (pi->port.uartclk / 16 != ser->baud_base) /* Not sure */ + rc = -EINVAL; + if ((void *)pi->port.mapbase != ser->iomem_base) + rc = -EINVAL; + if (pi->port.iobase != ser->port) + rc = -EINVAL; + if (ser->hub6 != 0) + rc = -EINVAL; + + return rc; +} + +static struct uart_ops mpsc_pops = { + .tx_empty = mpsc_tx_empty, + .set_mctrl = mpsc_set_mctrl, + .get_mctrl = mpsc_get_mctrl, + .stop_tx = mpsc_stop_tx, + .start_tx = mpsc_start_tx, + .stop_rx = mpsc_stop_rx, + .enable_ms = mpsc_enable_ms, + .break_ctl = mpsc_break_ctl, + .startup = mpsc_startup, + .shutdown = mpsc_shutdown, + .set_termios = mpsc_set_termios, + .type = mpsc_type, + .release_port = mpsc_release_port, + .request_port = mpsc_request_port, + .config_port = mpsc_config_port, + .verify_port = mpsc_verify_port, +}; + +/* + ****************************************************************************** + * + * Console Interface Routines + * + ****************************************************************************** + */ + +#ifdef CONFIG_SERIAL_MPSC_CONSOLE +static void mpsc_console_write(struct console *co, const char *s, uint count) +{ + struct mpsc_port_info *pi = &mpsc_ports[co->index]; + u8 *bp, *dp, add_cr = 0; + int i; + + while (mpsc_sdma_tx_active(pi)) + udelay(100); + + pi->txr_running = 0; + pi->txr_tail = pi->txr_head; + + while (count > 0) { + bp = dp = pi->txb + (pi->txr_head * MPSC_TXBE_SIZE); + MPSC_CACHE_INVALIDATE(pi, bp, bp + MPSC_TXBE_SIZE); + + for (i = 0; i < MPSC_TXBE_SIZE; i++) { + if (count == 0) + break; + + if (add_cr) { + *(dp++) = '\r'; + add_cr = 0; + } else { + *(dp++) = *s; + + if (*(s++) == '\n') { /* add '\r' after '\n' */ + add_cr = 1; + count++; + } + } + + count--; + } + + MPSC_CACHE_FLUSH(pi, bp, bp + MPSC_TXBE_SIZE); + mpsc_setup_tx_desc(pi, i, 1); + mpsc_sdma_start_tx(pi); + pi->txr_head = (pi->txr_head + 1) & (MPSC_TXR_ENTRIES - 1); + + while (mpsc_sdma_tx_active(pi)) + udelay(100); + + pi->txr_running = 0; + pi->txr_tail = (pi->txr_tail + 1) & (MPSC_TXR_ENTRIES - 1); + } + + return; +} + +static int __init mpsc_console_setup(struct console *co, char *options) +{ + struct mpsc_port_info *pi; + int baud, bits, parity, flow; + + DBG("mpsc_console_setup[%d]: options: %s\n", co->index, options); + + if (co->index >= MPSC_NUM_CTLRS) + co->index = 0; + + pi = &mpsc_ports[co->index]; + + baud = pi->default_baud; + bits = pi->default_bits; + parity = pi->default_parity; + flow = pi->default_flow; + + if (!pi->port.ops) + return -ENODEV; + + spin_lock_init(&pi->port.lock); /* Temporary fix--copied from 8250.c */ + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&pi->port, co, baud, parity, bits, flow); +} + +extern struct uart_driver mpsc_reg; +static struct console mpsc_console = { + .name = MPSC_DEV_NAME, + .write = mpsc_console_write, + .device = uart_console_device, + .setup = mpsc_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &mpsc_reg, +}; + +static int __init mpsc_console_init(void) +{ + DBG("mpsc_console_init: Enter\n"); + register_console(&mpsc_console); + return 0; +} + +console_initcall(mpsc_console_init); + +static int __init mpsc_late_console_init(void) +{ + DBG("mpsc_late_console_init: Enter\n"); + + if (!(mpsc_console.flags & CON_ENABLED)) + register_console(&mpsc_console); + return 0; +} + +late_initcall(mpsc_late_console_init); + +#define MPSC_CONSOLE &mpsc_console +#else +#define MPSC_CONSOLE NULL +#endif + +/* + ****************************************************************************** + * + * Driver Interface Routines + * + ****************************************************************************** + */ + +static void mpsc_map_regs(struct mpsc_port_info *pi) +{ + pi->mpsc_base = (u32) ioremap(pi->mpsc_base_p, MPSC_REG_BLOCK_SIZE); + pi->mpsc_routing_base = (u32) ioremap(pi->mpsc_routing_base_p, + MPSC_ROUTING_REG_BLOCK_SIZE); + pi->sdma_base = (u32) ioremap(pi->sdma_base_p, SDMA_REG_BLOCK_SIZE); + pi->sdma_intr_base = (u32) ioremap(pi->sdma_intr_base_p, + SDMA_INTR_REG_BLOCK_SIZE); + pi->brg_base = (u32) ioremap(pi->brg_base_p, BRG_REG_BLOCK_SIZE); + + return; +} + +static void mpsc_unmap_regs(struct mpsc_port_info *pi) +{ + iounmap((void *)pi->mpsc_base); + iounmap((void *)pi->mpsc_routing_base); + iounmap((void *)pi->sdma_base); + iounmap((void *)pi->sdma_intr_base); + iounmap((void *)pi->brg_base); + + pi->mpsc_base = 0; + pi->mpsc_routing_base = 0; + pi->sdma_base = 0; + pi->sdma_intr_base = 0; + pi->brg_base = 0; + + return; +} + +/* Called from platform specific device probe routine */ +struct mpsc_port_info *mpsc_device_probe(int index) +{ + struct mpsc_port_info *pi = NULL; + + if ((index >= 0) && (index < MPSC_NUM_CTLRS)) + pi = &mpsc_ports[index]; + + return pi; +} + +/* Called from platform specific device remove routine */ +struct mpsc_port_info *mpsc_device_remove(int index) +{ + struct mpsc_port_info *pi = NULL; + + if ((index >= 0) && (index < MPSC_NUM_CTLRS)) + pi = &mpsc_ports[index]; + + return pi; +} + +static struct uart_driver mpsc_reg = { + .owner = THIS_MODULE, + .driver_name = MPSC_DRIVER_NAME, + .devfs_name = MPSC_DEVFS_NAME, + .dev_name = MPSC_DEV_NAME, + .major = MPSC_MAJOR, + .minor = MPSC_MINOR_START, + .nr = MPSC_NUM_CTLRS, + .cons = MPSC_CONSOLE, +}; + +static int __init mpsc_init(void) +{ + struct mpsc_port_info *pi; + int i, j, rc; + + printk(KERN_INFO "Serial: MPSC driver $Revision: 1.00 $\n"); + + mpsc_ports[0].shared_regs = &mpsc_shared_regs; + mpsc_ports[1].shared_regs = &mpsc_shared_regs; + + if ((rc = mpsc_platform_register_driver()) >= 0) { + if ((rc = uart_register_driver(&mpsc_reg)) < 0) { + mpsc_platform_unregister_driver(); + } else { + for (i = 0; i < MPSC_NUM_CTLRS; i++) { + pi = &mpsc_ports[i]; + + pi->port.line = i; + pi->port.type = PORT_MPSC; + pi->port.fifosize = MPSC_TXBE_SIZE; + pi->port.membase = (char *)pi->mpsc_base; + pi->port.mapbase = (ulong) pi->mpsc_base; + pi->port.ops = &mpsc_pops; + pi->shared_regs = &mpsc_shared_regs; + + mpsc_map_regs(pi); + + if ((rc = mpsc_make_ready(pi)) >= 0) { + uart_add_one_port(&mpsc_reg, &pi->port); + } else { /* on failure, undo everything */ + for (j = 0; j < i; j++) { + mpsc_unmap_regs(&mpsc_ports[j]); + uart_remove_one_port(&mpsc_reg, + &mpsc_ports + [j].port); + } + + uart_unregister_driver(&mpsc_reg); + mpsc_platform_unregister_driver(); + break; + } + } + } + } + + return rc; +} + +static void __exit mpsc_exit(void) +{ + int i; + + DBG("mpsc_exit: Exiting\n"); + + for (i = 0; i < MPSC_NUM_CTLRS; i++) { + mpsc_unmap_regs(&mpsc_ports[i]); + uart_remove_one_port(&mpsc_reg, &mpsc_ports[i].port); + } + + uart_unregister_driver(&mpsc_reg); + mpsc_platform_unregister_driver(); + + return; +} + +int register_serial(struct serial_struct *req) +{ + return uart_register_port(&mpsc_reg, &mpsc_ports[req->line].port); +} + +void unregister_serial(int line) +{ + uart_unregister_port(&mpsc_reg, line); + return; +} + +module_init(mpsc_init); +module_exit(mpsc_exit); + +EXPORT_SYMBOL(register_serial); +EXPORT_SYMBOL(unregister_serial); + +MODULE_AUTHOR("Mark A. Greer "); +MODULE_DESCRIPTION("Generic Marvell MPSC serial/UART driver $Revision: 1.00 $"); +MODULE_VERSION(MPSC_VERSION); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(MPSC_MAJOR); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/serial/mpsc/mpsc_defs.h 2004-09-03 00:57:11.165341872 -0700 @@ -0,0 +1,151 @@ +/* + * drivers/serial/mpsc/mpsc_defs.h + * + * Register definitions for the Marvell Multi-Protocol Serial Controller (MPSC), + * Serial DMA Controller (SDMA), and Baud Rate Generator (BRG). + * + * Author: Mark A. Greer + * + * 2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#ifndef __MPSC_DEFS_H__ +#define __MPSC_DEFS_H__ + +#define MPSC_NUM_CTLRS 2 + +/* + ***************************************************************************** + * + * Multi-Protocol Serial Controller Interface Registers + * + ***************************************************************************** + */ + +/* Main Configuratino Register Offsets */ +#define MPSC_MMCRL 0x0000 +#define MPSC_MMCRH 0x0004 +#define MPSC_MPCR 0x0008 +#define MPSC_CHR_1 0x000c +#define MPSC_CHR_2 0x0010 +#define MPSC_CHR_3 0x0014 +#define MPSC_CHR_4 0x0018 +#define MPSC_CHR_5 0x001c +#define MPSC_CHR_6 0x0020 +#define MPSC_CHR_7 0x0024 +#define MPSC_CHR_8 0x0028 +#define MPSC_CHR_9 0x002c +#define MPSC_CHR_10 0x0030 +#define MPSC_CHR_11 0x0034 +#define MPSC_REG_BLOCK_SIZE 0x0038 + +#define MPSC_MPCR_CL_5 0 +#define MPSC_MPCR_CL_6 1 +#define MPSC_MPCR_CL_7 2 +#define MPSC_MPCR_CL_8 3 +#define MPSC_MPCR_SBL_1 0 +#define MPSC_MPCR_SBL_2 3 + +#define MPSC_CHR_2_TEV (1<<1) +#define MPSC_CHR_2_TA (1<<7) +#define MPSC_CHR_2_TTCS (1<<9) +#define MPSC_CHR_2_REV (1<<17) +#define MPSC_CHR_2_RA (1<<23) +#define MPSC_CHR_2_CRD (1<<25) +#define MPSC_CHR_2_EH (1<<31) +#define MPSC_CHR_2_PAR_ODD 0 +#define MPSC_CHR_2_PAR_SPACE 1 +#define MPSC_CHR_2_PAR_EVEN 2 +#define MPSC_CHR_2_PAR_MARK 3 + +/* MPSC Signal Routing */ +#define MPSC_MRR 0x0000 +#define MPSC_RCRR 0x0004 +#define MPSC_TCRR 0x0008 +#define MPSC_ROUTING_REG_BLOCK_SIZE 0x000c + +/* + ***************************************************************************** + * + * Serial DMA Controller Interface Registers + * + ***************************************************************************** + */ + +#define SDMA_SDC 0x0000 +#define SDMA_SDCM 0x0008 +#define SDMA_RX_DESC 0x0800 +#define SDMA_RX_BUF_PTR 0x0808 +#define SDMA_SCRDP 0x0810 +#define SDMA_TX_DESC 0x0c00 +#define SDMA_SCTDP 0x0c10 +#define SDMA_SFTDP 0x0c14 +#define SDMA_REG_BLOCK_SIZE 0x0c18 + +#define SDMA_DESC_CMDSTAT_PE (1<<0) +#define SDMA_DESC_CMDSTAT_CDL (1<<1) +#define SDMA_DESC_CMDSTAT_FR (1<<3) +#define SDMA_DESC_CMDSTAT_OR (1<<6) +#define SDMA_DESC_CMDSTAT_BR (1<<9) +#define SDMA_DESC_CMDSTAT_MI (1<<10) +#define SDMA_DESC_CMDSTAT_A (1<<11) +#define SDMA_DESC_CMDSTAT_AM (1<<12) +#define SDMA_DESC_CMDSTAT_CT (1<<13) +#define SDMA_DESC_CMDSTAT_C (1<<14) +#define SDMA_DESC_CMDSTAT_ES (1<<15) +#define SDMA_DESC_CMDSTAT_L (1<<16) +#define SDMA_DESC_CMDSTAT_F (1<<17) +#define SDMA_DESC_CMDSTAT_P (1<<18) +#define SDMA_DESC_CMDSTAT_EI (1<<23) +#define SDMA_DESC_CMDSTAT_O (1<<31) + +#define SDMA_DESC_DFLT (SDMA_DESC_CMDSTAT_O | \ + SDMA_DESC_CMDSTAT_EI) + +#define SDMA_SDC_RFT (1<<0) +#define SDMA_SDC_SFM (1<<1) +#define SDMA_SDC_BLMR (1<<6) +#define SDMA_SDC_BLMT (1<<7) +#define SDMA_SDC_POVR (1<<8) +#define SDMA_SDC_RIFB (1<<9) + +#define SDMA_SDCM_ERD (1<<7) +#define SDMA_SDCM_AR (1<<15) +#define SDMA_SDCM_STD (1<<16) +#define SDMA_SDCM_TXD (1<<23) +#define SDMA_SDCM_AT (1<<31) + +#define SDMA_0_CAUSE_RXBUF (1<<0) +#define SDMA_0_CAUSE_RXERR (1<<1) +#define SDMA_0_CAUSE_TXBUF (1<<2) +#define SDMA_0_CAUSE_TXEND (1<<3) +#define SDMA_1_CAUSE_RXBUF (1<<8) +#define SDMA_1_CAUSE_RXERR (1<<9) +#define SDMA_1_CAUSE_TXBUF (1<<10) +#define SDMA_1_CAUSE_TXEND (1<<11) + +#define SDMA_CAUSE_RX_MASK (SDMA_0_CAUSE_RXBUF | SDMA_0_CAUSE_RXERR | \ + SDMA_1_CAUSE_RXBUF | SDMA_1_CAUSE_RXERR) +#define SDMA_CAUSE_TX_MASK (SDMA_0_CAUSE_TXBUF | SDMA_0_CAUSE_TXEND | \ + SDMA_1_CAUSE_TXBUF | SDMA_1_CAUSE_TXEND) + +/* SDMA Interrupt registers */ +#define SDMA_INTR_CAUSE 0x0000 +#define SDMA_INTR_MASK 0x0080 +#define SDMA_INTR_REG_BLOCK_SIZE 0x0084 + +/* + ***************************************************************************** + * + * Baud Rate Generator Interface Registers + * + ***************************************************************************** + */ + +#define BRG_BCR 0x0000 +#define BRG_BTR 0x0004 +#define BRG_REG_BLOCK_SIZE 0x0008 + +#endif /*__MPSC_DEFS_H__ */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/serial/mpsc/mpsc.h 2004-09-03 00:57:11.166341720 -0700 @@ -0,0 +1,284 @@ +/* + * drivers/serial/mpsc/mpsc.h + * + * Author: Mark A. Greer + * + * 2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef __MPSC_H__ +#define __MPSC_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#if defined(CONFIG_SERIAL_MPSC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include "mpsc_defs.h" + +/* + * Descriptors and buffers must be cache line aligned. + * Buffers lengths must be multiple of cache line size. + * Number of Tx & Rx descriptors must be powers of 2. + */ +#define MPSC_RXR_ENTRIES 32 +#define MPSC_RXRE_SIZE dma_get_cache_alignment() +#define MPSC_RXR_SIZE (MPSC_RXR_ENTRIES * MPSC_RXRE_SIZE) +#define MPSC_RXBE_SIZE dma_get_cache_alignment() +#define MPSC_RXB_SIZE (MPSC_RXR_ENTRIES * MPSC_RXBE_SIZE) + +#define MPSC_TXR_ENTRIES 32 +#define MPSC_TXRE_SIZE dma_get_cache_alignment() +#define MPSC_TXR_SIZE (MPSC_TXR_ENTRIES * MPSC_TXRE_SIZE) +#define MPSC_TXBE_SIZE dma_get_cache_alignment() +#define MPSC_TXB_SIZE (MPSC_TXR_ENTRIES * MPSC_TXBE_SIZE) + +#define MPSC_DMA_ALLOC_SIZE (MPSC_RXR_SIZE + MPSC_RXB_SIZE + \ + MPSC_TXR_SIZE + MPSC_TXB_SIZE + \ + dma_get_cache_alignment() /* for alignment */) + +/* Rx and Tx Ring entry descriptors -- assume entry size is <= cacheline size */ +struct mpsc_rx_desc { + u16 bufsize; + u16 bytecnt; + u32 cmdstat; + u32 link; + u32 buf_ptr; +} __attribute((packed)); + +struct mpsc_tx_desc { + u16 bytecnt; + u16 shadow; + u32 cmdstat; + u32 link; + u32 buf_ptr; +} __attribute((packed)); + +/* + * Some regs that have the erratum that you can't read them are are shared + * between the two MPSC controllers. This struct contains those shared regs. + */ +struct mpsc_shared_regs { + /* SDMA_INTR_CAUSE_m should be added too but never read so not needed */ + u32 MPSC_MRR_m; + u32 MPSC_RCRR_m; + u32 MPSC_TCRR_m; + u32 SDMA_INTR_MASK_m; +}; + +/* The main driver data structure */ +struct mpsc_port_info { + struct uart_port port; /* Overlay uart_port structure */ + + /* Internal driver state for this ctlr */ + u8 ready; + u8 rcv_data; + tcflag_t c_iflag; /* save termios->c_iflag */ + tcflag_t c_cflag; /* save termios->c_cflag */ + + /* Info passed in from platform */ + u8 mirror_regs; /* Need to mirror regs? */ + u8 cache_mgmt; /* Need manual cache mgmt? */ + u8 brg_can_tune; /* BRG has baud tuning? */ + u32 brg_clk_src; + u16 mpsc_max_idle; + int default_baud; + int default_bits; + int default_parity; + int default_flow; + + /* Physical addresses of various blocks of registers (from platform) */ + u32 mpsc_base_p; + u32 mpsc_routing_base_p; + u32 sdma_base_p; + u32 sdma_intr_base_p; + u32 brg_base_p; + + /* Virtual addresses of various blocks of registers (from platform) */ + u32 mpsc_base; + u32 mpsc_routing_base; + u32 sdma_base; + u32 sdma_intr_base; + u32 brg_base; + + /* Descriptor ring and buffer allocations */ + void *dma_region; + dma_addr_t dma_region_p; + + dma_addr_t rxr; /* Rx descriptor ring */ + dma_addr_t rxr_p; /* Phys addr of rxr */ + u8 *rxb; /* Rx Ring I/O buf */ + u8 *rxb_p; /* Phys addr of rxb */ + u32 rxr_posn; /* First desc w/ Rx data */ + + dma_addr_t txr; /* Tx descriptor ring */ + dma_addr_t txr_p; /* Phys addr of txr */ + u8 *txb; /* Tx Ring I/O buf */ + u8 *txb_p; /* Phys addr of txb */ + u8 txr_running; /* Tx engine running? */ + int txr_head; /* Where new data goes */ + int txr_tail; /* Where sent data comes off */ + + /* Mirrored values of regs we can't read (if 'mirror_regs' set) */ + u32 MPSC_MPCR_m; + u32 MPSC_CHR_1_m; + u32 MPSC_CHR_2_m; + u32 MPSC_CHR_10_m; + u32 BRG_BCR_m; + struct mpsc_shared_regs *shared_regs; +}; + +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) +#define MPSC_CACHE_FLUSH(pi, s, e) { \ + if (pi->cache_mgmt) { \ + /* 64x60 erratum: can't use dcbst/clean_dcache_range() */ \ + flush_dcache_range((ulong)s, (ulong)e); \ + mb(); \ + } \ +} + +#define MPSC_CACHE_INVALIDATE(pi, s, e) { \ + if (pi->cache_mgmt) { \ + invalidate_dcache_range((ulong)s, (ulong)e); \ + mb(); \ + } \ +} + +#define MPSC_CACHE_FLUSH_INVALIDATE(pi, s, e) { \ + if (pi->cache_mgmt) { \ + flush_dcache_range((ulong)s, (ulong)e); \ + mb(); \ + } \ +} +#else +/* Other architectures need to fill this in */ +#define MPSC_CACHE_FLUSH(pi, s, e) BUG() +#define MPSC_CACHE_INVALIDATE(pi, s, e) BUG() +#define MPSC_CACHE_FLUSH_INVALIDATE(pi, s, e) BUG() +#endif + +/* + * 'MASK_INSERT' takes the low-order 'n' bits of 'i', shifts it 'b' bits to + * the left, and inserts it into the target 't'. The corresponding bits in + * 't' will have been cleared before the bits in 'i' are inserted. + */ +#ifdef CONFIG_PPC32 +#define MASK_INSERT(t, i, n, b) ({ \ + u32 rval = (t); \ + __asm__ __volatile__( \ + "rlwimi %0,%2,%4,32-(%3+%4),31-%4\n" \ + : "=r" (rval) \ + : "0" (rval), "r" (i), "i" (n), "i" (b)); \ + rval; \ +}) +#else +/* These macros are really just examples. Feel free to change them --MAG */ +#define GEN_MASK(n, b) \ +({ \ + u32 m, sl, sr; \ + sl = 32 - (n); \ + sr = sl - (b); \ + m = (0xffffffff << sl) >> sr; \ +}) + +#define MASK_INSERT(t, i, n, b) \ +({ \ + u32 m, rval = (t); \ + m = GEN_MASK((n), (b)); \ + rval &= ~m; \ + rval |= (((i) << (b)) & m); \ +}) +#endif + +/* I/O macros for regs that you can read */ +#define MPSC_READ(pi, unit, offset) readl((pi)->unit##_base + (offset)) +#define MPSC_WRITE(pi, unit, offset, v) writel(v, (pi)->unit##_base + (offset)) +#define MPSC_MOD_FIELD(pi, unit, offset, num_bits, shift, val) \ +{ \ + u32 v; \ + v = readl((pi)->unit##_base + (offset)); \ + writel(MASK_INSERT(v,val,num_bits,shift), (pi)->unit##_base+(offset));\ +} + +/* Macros for regs with erratum that are not shared between MPSC ctlrs */ +#define MPSC_READ_M(pi, unit, offset) \ +({ \ + u32 v; \ + if ((pi)->mirror_regs) v = (pi)->offset##_m; \ + else v = readl((pi)->unit##_base + (offset)); \ + v; \ +}) + +#define MPSC_WRITE_M(pi, unit, offset, v) \ +({ \ + if ((pi)->mirror_regs) (pi)->offset##_m = v; \ + writel(v, (pi)->unit##_base + (offset)); \ +}) + +#define MPSC_MOD_FIELD_M(pi, unit, offset, num_bits, shift, val) \ +({ \ + u32 v; \ + if ((pi)->mirror_regs) v = (pi)->offset##_m; \ + else v = readl((pi)->unit##_base + (offset)); \ + v = MASK_INSERT(v, val, num_bits, shift); \ + if ((pi)->mirror_regs) (pi)->offset##_m = v; \ + writel(v, (pi)->unit##_base + (offset)); \ +}) + +/* Macros for regs with erratum that are shared between MPSC ctlrs */ +#define MPSC_READ_S(pi, unit, offset) \ +({ \ + u32 v; \ + if ((pi)->mirror_regs) v = (pi)->shared_regs->offset##_m; \ + else v = readl((pi)->unit##_base + (offset)); \ + v; \ +}) + +#define MPSC_WRITE_S(pi, unit, offset, v) \ +({ \ + if ((pi)->mirror_regs) (pi)->shared_regs->offset##_m = v; \ + writel(v, (pi)->unit##_base + (offset)); \ +}) + +#define MPSC_MOD_FIELD_S(pi, unit, offset, num_bits, shift, val) \ +({ \ + u32 v; \ + if ((pi)->mirror_regs) v = (pi)->shared_regs->offset##_m; \ + else v = readl((pi)->unit##_base + (offset)); \ + v = MASK_INSERT(v, val, num_bits, shift); \ + if ((pi)->mirror_regs) (pi)->shared_regs->offset##_m = v; \ + writel(v, (pi)->unit##_base + (offset)); \ +}) + +#if !defined(MIN) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +/* Hooks to platform-specific code */ +int mpsc_platform_register_driver(void); +void mpsc_platform_unregister_driver(void); + +/* Hooks back in to mpsc common to be called by platform-specific code */ +struct mpsc_port_info *mpsc_device_probe(int index); +struct mpsc_port_info *mpsc_device_remove(int index); + +#endif /* __MPSC_H__ */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/serial/mpsc/mpsc_ppc32.c 2004-09-03 00:57:11.167341568 -0700 @@ -0,0 +1,109 @@ +/* + * drivers/serial/mpsc/mpsc_ppc32.c + * + * Middle layer that sucks data from the ppc32 OCP--that is, chip & + * platform-specific data--and puts it into the mpsc_port_info structure + * for the mpsc driver to use. + * + * Author: Mark A. Greer + * + * 2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#include "mpsc.h" +#include +#include + +static void mpsc_ocp_remove(struct ocp_device *ocpdev); + +static int mpsc_ocp_probe(struct ocp_device *ocpdev) +{ + struct mpsc_port_info *pi; + struct mv64x60_ocp_mpsc_data *dp; + u32 base; + int rc = -ENODEV; + + if ((pi = mpsc_device_probe(ocpdev->def->index)) != NULL) { + dp = (struct mv64x60_ocp_mpsc_data *)ocpdev->def->additions; + + pi->mpsc_base_p = ocpdev->def->paddr; + + if (ocpdev->def->index == 0) { + base = pi->mpsc_base_p - MV64x60_MPSC_0_OFFSET; + pi->sdma_base_p = base + MV64x60_SDMA_0_OFFSET; + pi->brg_base_p = base + MV64x60_BRG_0_OFFSET; + } else { /* Must be 1 */ + base = pi->mpsc_base_p - MV64x60_MPSC_1_OFFSET; + pi->sdma_base_p = base + MV64x60_SDMA_1_OFFSET; + pi->brg_base_p = base + MV64x60_BRG_1_OFFSET; + } + + pi->mpsc_routing_base_p = base + MV64x60_MPSC_ROUTING_OFFSET; + pi->sdma_intr_base_p = base + MV64x60_SDMA_INTR_OFFSET; + + pi->port.irq = ocpdev->def->irq; + pi->port.uartclk = dp->brg_clk_freq; + + pi->mirror_regs = dp->mirror_regs; + pi->cache_mgmt = dp->cache_mgmt; + pi->brg_can_tune = dp->brg_can_tune; + pi->brg_clk_src = dp->brg_clk_src; + pi->mpsc_max_idle = dp->max_idle; + pi->default_baud = dp->default_baud; + pi->default_bits = dp->default_bits; + pi->default_parity = dp->default_parity; + pi->default_flow = dp->default_flow; + + /* Initial values of mirrored regs */ + pi->MPSC_CHR_1_m = dp->chr_1_val; + pi->MPSC_CHR_2_m = dp->chr_2_val; + pi->MPSC_CHR_10_m = dp->chr_10_val; + pi->MPSC_MPCR_m = dp->mpcr_val; + pi->BRG_BCR_m = dp->bcr_val; + + /* Seed only with the first ctlr's values */ + if (ocpdev->def->index == 0) { + pi->shared_regs->MPSC_MRR_m = dp->mrr_val; + pi->shared_regs->MPSC_RCRR_m = dp->rcrr_val; + pi->shared_regs->MPSC_TCRR_m = dp->tcrr_val; + pi->shared_regs->SDMA_INTR_MASK_m = dp->intr_mask_val; + } + + pi->port.iotype = UPIO_MEM; + + rc = 0; + } + + return rc; +} + +static void mpsc_ocp_remove(struct ocp_device *ocpdev) +{ + (void)mpsc_device_remove(ocpdev->def->index); + return; +} + +static struct ocp_device_id mpsc_ocp_ids[] = { + {.vendor = OCP_VENDOR_MARVELL,.function = OCP_FUNC_MPSC}, + {.vendor = OCP_VENDOR_INVALID} +}; + +static struct ocp_driver mpsc_ocp_driver = { + .name = "mpsc", + .id_table = mpsc_ocp_ids, + .probe = mpsc_ocp_probe, + .remove = mpsc_ocp_remove, +}; + +int mpsc_platform_register_driver(void) +{ + return ocp_register_driver(&mpsc_ocp_driver); +} + +void mpsc_platform_unregister_driver(void) +{ + ocp_unregister_driver(&mpsc_ocp_driver); + return; +} --- linux-2.6.9-rc1/drivers/serial/serial_core.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/serial/serial_core.c 2004-09-03 00:56:57.057486592 -0700 @@ -1990,6 +1990,11 @@ uart_configure_port(struct uart_driver * { unsigned int flags; +#ifdef CONFIG_KGDB + if (port->kgdb) + return; +#endif + /* * If there isn't a port here, don't do anything further. */ @@ -2375,6 +2380,7 @@ int uart_register_port(struct uart_drive * If the port is already initialised, don't touch it. */ if (state->port->type == PORT_UNKNOWN) { + state->port->type = port->type; state->port->iobase = port->iobase; state->port->membase = port->membase; state->port->irq = port->irq; --- linux-2.6.9-rc1/drivers/serial/serial_cs.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/serial/serial_cs.c 2004-09-03 00:57:11.582278488 -0700 @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -112,6 +113,8 @@ struct serial_info { int multi; int slave; int manfid; + int prodid; + int c950ctrl; dev_node_t node[4]; int line[4]; }; @@ -127,6 +130,33 @@ static void serial_detach(dev_link_t *); static dev_link_t *dev_list = NULL; +static void wakeup_card(struct serial_info *info) +{ + int ctrl = info->c950ctrl; + + if (info->manfid == MANFID_OXSEMI) { + outb(12, ctrl + 1); + } else if (info->manfid == MANFID_POSSIO && info->prodid == PRODID_POSSIO_GCC) { + /* request_region? oxsemi branch does no request_region too... */ + /* This sequence is needed to properly initialize MC45 attached to OXCF950. + * I tried decreasing these msleep()s, but it worked properly (survived + * 1000 stop/start operations) with these timeouts (or bigger). */ + outb(0xA, ctrl + 1); + msleep(100); + outb(0xE, ctrl + 1); + msleep(300); + outb(0xC, ctrl + 1); + msleep(100); + outb(0xE, ctrl + 1); + msleep(200); + outb(0xF, ctrl + 1); + msleep(100); + outb(0xE, ctrl + 1); + msleep(100); + outb(0xC, ctrl + 1); + } +} + /*====================================================================== After a card is removed, serial_remove() will unregister @@ -191,6 +221,7 @@ static void serial_resume(dev_link_t *li for (i = 0; i < info->ndev; i++) serial8250_resume_port(info->line[i]); + wakeup_card(info); } } @@ -363,9 +394,10 @@ next_tuple(client_handle_t handle, tuple /*====================================================================*/ -static int simple_config(dev_link_t * link) +static int simple_config(dev_link_t *link) { static ioaddr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; + static int size_table[2] = { 8, 16 }; client_handle_t handle = link->handle; struct serial_info *info = link->priv; tuple_t tuple; @@ -374,6 +406,7 @@ static int simple_config(dev_link_t * li cistpl_cftable_entry_t *cf = &parse.cftable_entry; config_info_t config; int i, j, try; + int s; /* If the card is already configured, look up the port and irq */ i = pcmcia_get_configuration_info(handle, &config); @@ -399,29 +432,30 @@ static int simple_config(dev_link_t * li tuple.Attributes = 0; tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; /* Two tries: without IO aliases, then with aliases */ - for (try = 0; try < 2; try++) { - i = first_tuple(handle, &tuple, &parse); - while (i != CS_NO_MORE_ITEMS) { - if (i != CS_SUCCESS) - goto next_entry; - if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM)) - link->conf.Vpp1 = link->conf.Vpp2 = - cf->vpp1.param[CISTPL_POWER_VNOM] / 10000; - if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && - (cf->io.win[0].base != 0)) { - link->conf.ConfigIndex = cf->index; - link->io.BasePort1 = cf->io.win[0].base; - link->io.IOAddrLines = (try == 0) ? - 16 : cf->io.flags & CISTPL_IO_LINES_MASK; - i = pcmcia_request_io(link->handle, &link->io); - if (i == CS_SUCCESS) - goto found_port; + for (s = 0; s < 2; s++) { + for (try = 0; try < 2; try++) { + i = first_tuple(handle, &tuple, &parse); + while (i != CS_NO_MORE_ITEMS) { + if (i != CS_SUCCESS) + goto next_entry; + if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM)) + link->conf.Vpp1 = link->conf.Vpp2 = + cf->vpp1.param[CISTPL_POWER_VNOM] / 10000; + if ((cf->io.nwin > 0) && (cf->io.win[0].len == size_table[s]) && + (cf->io.win[0].base != 0)) { + link->conf.ConfigIndex = cf->index; + link->io.BasePort1 = cf->io.win[0].base; + link->io.IOAddrLines = (try == 0) ? + 16 : cf->io.flags & CISTPL_IO_LINES_MASK; + i = pcmcia_request_io(link->handle, &link->io); + if (i == CS_SUCCESS) + goto found_port; + } +next_entry: + i = next_tuple(handle, &tuple, &parse); } - next_entry: - i = next_tuple(handle, &tuple, &parse); } } - /* Second pass: try to find an entry that isn't picky about its base address, then try to grab any standard serial port address, and finally try to get any free port. */ @@ -554,15 +588,20 @@ static int multi_config(dev_link_t * lin } /* The Oxford Semiconductor OXCF950 cards are in fact single-port: - 8 registers are for the UART, the others are extra registers */ - if (info->manfid == MANFID_OXSEMI) { + 8 registers are for the UART, the others are extra registers. + Siemen's MC45 PCMCIA (Possio's GCC) is OXCF950 based too. */ + if (info->manfid == MANFID_OXSEMI || + (info->manfid == MANFID_POSSIO && info->prodid == PRODID_POSSIO_GCC)) { + int err; + if (cf->index == 1 || cf->index == 3) { - setup_serial(info, base2, link->irq.AssignedIRQ); - outb(12, link->io.BasePort1 + 1); + err = setup_serial(info, base2, link->irq.AssignedIRQ); + base2 = link->io.BasePort1; } else { - setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ); - outb(12, base2 + 1); + err = setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ); } + info->c950ctrl = base2; + wakeup_card(info); return 0; } @@ -622,9 +661,10 @@ void serial_config(dev_link_t * link) tuple.DesiredTuple = CISTPL_MANFID; if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) { info->manfid = le16_to_cpu(buf[0]); + info->prodid = le16_to_cpu(buf[1]); for (i = 0; i < MULTI_COUNT; i++) if ((info->manfid == multi_id[i].manfid) && - (le16_to_cpu(buf[1]) == multi_id[i].prodid)) + (info->prodid == multi_id[i].prodid)) break; if (i < MULTI_COUNT) info->multi = multi_id[i].multi; --- linux-2.6.9-rc1/drivers/serial/sn_console.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/drivers/serial/sn_console.c 2004-09-03 00:57:19.518072064 -0700 @@ -50,6 +50,7 @@ #include #include +#include #include #include #include @@ -80,6 +81,12 @@ #define DEVICE_MAJOR 204 #define DEVICE_MINOR 40 +#ifdef CONFIG_MAGIC_SYSRQ +static char sysrq_serial_str[] = "\eSYS"; +static char *sysrq_serial_ptr = sysrq_serial_str; +static unsigned long sysrq_requested; +#endif /* CONFIG_MAGIC_SYSRQ */ + /* * Port definition - this kinda drives it all */ @@ -532,13 +539,15 @@ sn_debug_printf(const char *fmt, ...) * sn_receive_chars - Grab characters, pass them to tty layer * @port: Port to operate on * @regs: Saved registers (needed by uart_handle_sysrq_char) + * @flags: irq flags * * Note: If we're not registered with the serial core infrastructure yet, * we don't try to send characters to it... * */ static void -sn_receive_chars(struct sn_cons_port *port, struct pt_regs *regs) +sn_receive_chars(struct sn_cons_port *port, struct pt_regs *regs, + unsigned long flags) { int ch; struct tty_struct *tty; @@ -569,10 +578,29 @@ sn_receive_chars(struct sn_cons_port *po "obtaining data from the console (0x%0x)\n", ch); break; } -#if defined(CONFIG_SERIAL_SGI_L1_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) - if (uart_handle_sysrq_char(&port->sc_port, ch, regs)) - continue; -#endif /* CONFIG_SERIAL_SGI_L1_CONSOLE && CONFIG_MAGIC_SYSRQ */ +#ifdef CONFIG_MAGIC_SYSRQ + if (sysrq_requested) { + unsigned long sysrq_timeout = sysrq_requested + HZ*5; + + sysrq_requested = 0; + if (ch && time_before(jiffies, sysrq_timeout)) { + spin_unlock_irqrestore(&port->sc_port.lock, flags); + handle_sysrq(ch, regs, NULL); + spin_lock_irqsave(&port->sc_port.lock, flags); + /* ignore actual sysrq command char */ + continue; + } + } + if (ch == *sysrq_serial_ptr) { + if (!(*++sysrq_serial_ptr)) { + sysrq_requested = jiffies; + sysrq_serial_ptr = sysrq_serial_str; + } + continue; /* ignore the whole sysrq string */ + } + else + sysrq_serial_ptr = sysrq_serial_str; +#endif /* CONFIG_MAGIC_SYSRQ */ /* record the character to pass up to the tty layer */ if (tty) { @@ -695,7 +723,7 @@ sn_sal_interrupt(int irq, void *dev_id, spin_lock_irqsave(&port->sc_port.lock, flags); if (status & SAL_CONSOLE_INTR_RECV) { - sn_receive_chars(port, regs); + sn_receive_chars(port, regs, flags); } if (status & SAL_CONSOLE_INTR_XMIT) { sn_transmit_chars(port, TRANSMIT_BUFFERED); @@ -744,7 +772,7 @@ sn_sal_timer_poll(unsigned long data) if (!port->sc_port.irq) { spin_lock_irqsave(&port->sc_port.lock, flags); - sn_receive_chars(port, NULL); + sn_receive_chars(port, NULL, flags); sn_transmit_chars(port, TRANSMIT_RAW); spin_unlock_irqrestore(&port->sc_port.lock, flags); mod_timer(&port->sc_timer, @@ -852,7 +880,6 @@ sn_sal_switch_to_interrupts(struct sn_co * Kernel console definitions */ -#ifdef CONFIG_SERIAL_SGI_L1_CONSOLE static void sn_sal_console_write(struct console *, const char *, unsigned); static int __init sn_sal_console_setup(struct console *, char *); extern struct uart_driver sal_console_uart; @@ -868,9 +895,6 @@ static struct console sal_console = { }; #define SAL_CONSOLE &sal_console -#else -#define SAL_CONSOLE 0 -#endif /* CONFIG_SERIAL_SGI_L1_CONSOLE */ static struct uart_driver sal_console_uart = { .owner = THIS_MODULE, @@ -896,11 +920,11 @@ sn_sal_module_init(void) { int retval; - printk(KERN_INFO "sn_console: Console driver init\n"); - if (!ia64_platform_is("sn2")) return -ENODEV; + printk(KERN_INFO "sn_console: Console driver init\n"); + if (USE_DYNAMIC_MINOR == 1) { misc.minor = MISC_DYNAMIC_MINOR; misc.name = DEVICE_NAME_DYNAMIC; @@ -970,8 +994,6 @@ sn_sal_module_exit(void) module_init(sn_sal_module_init); module_exit(sn_sal_module_exit); -#ifdef CONFIG_SERIAL_SGI_L1_CONSOLE - /** * puts_raw_fixed - sn_sal_console_write helper for adding \r's as required * @puts_raw : puts function to do the writing @@ -1086,7 +1108,9 @@ sn_sal_console_write(struct console *co, spin_unlock_irqrestore(&port->sc_port.lock, flags); puts_raw_fixed(port->sc_ops->sal_puts_raw, s, count); +#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) } +#endif } else { /* Not yet registered with serial core - simple case */ @@ -1191,5 +1215,3 @@ sn_sal_serial_console_init(void) } console_initcall(sn_sal_serial_console_init); - -#endif /* CONFIG_SERIAL_SGI_L1_CONSOLE */ --- linux-2.6.9-rc1/drivers/serial/sunsab.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/drivers/serial/sunsab.c 2004-09-02 20:51:52.000000000 -0700 @@ -768,25 +768,6 @@ static void sunsab_convert_to_sab(struct writeb((readb(&up->regs->rw.ccr2) & ~0xc0) | ((ebrg >> 2) & 0xc0), &up->regs->rw.ccr2); - if (cflag & CRTSCTS) { - writeb(readb(&up->regs->rw.mode) & ~SAB82532_MODE_RTS, - &up->regs->rw.mode); - writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_FRTS, - &up->regs->rw.mode); - writeb(readb(&up->regs->rw.mode) & ~SAB82532_MODE_FCTS, - &up->regs->rw.mode); - up->interrupt_mask1 &= ~SAB82532_IMR1_CSC; - writeb(up->interrupt_mask1, &up->regs->w.imr1); - } else { - writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RTS, - &up->regs->rw.mode); - writeb(readb(&up->regs->rw.mode) & ~SAB82532_MODE_FRTS, - &up->regs->rw.mode); - writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_FCTS, - &up->regs->rw.mode); - up->interrupt_mask1 |= SAB82532_IMR1_CSC; - writeb(up->interrupt_mask1, &up->regs->w.imr1); - } writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RAC, &up->regs->rw.mode); } @@ -940,6 +921,7 @@ static int sunsab_console_setup(struct c writeb(up->interrupt_mask1, &up->regs->w.imr1); sunsab_convert_to_sab(up, con->cflag, 0, baud); + sunsab_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS); spin_unlock_irqrestore(&up->port.lock, flags); --- linux-2.6.9-rc1/drivers/serial/sunsu.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/serial/sunsu.c 2004-09-03 00:56:31.582359400 -0700 @@ -98,7 +98,7 @@ struct uart_sunsu_port { unsigned int irq; #ifdef CONFIG_SERIO - struct serio serio; + struct serio *serio; int serio_open; #endif }; @@ -520,7 +520,7 @@ static void receive_kbd_ms_chars(struct /* Stop-A is handled by drivers/char/keyboard.c now. */ if (up->su_type == SU_PORT_KBD) { #ifdef CONFIG_SERIO - serio_interrupt(&up->serio, ch, 0, regs); + serio_interrupt(up->serio, ch, 0, regs); #endif } else if (up->su_type == SU_PORT_MS) { int ret = suncore_mouse_baud_detection(ch, is_break); @@ -534,7 +534,7 @@ static void receive_kbd_ms_chars(struct case 0: #ifdef CONFIG_SERIO - serio_interrupt(&up->serio, ch, 0, regs); + serio_interrupt(up->serio, ch, 0, regs); #endif break; }; @@ -994,7 +994,7 @@ static spinlock_t sunsu_serio_lock = SPI static int sunsu_serio_write(struct serio *serio, unsigned char ch) { - struct uart_sunsu_port *up = serio->driver; + struct uart_sunsu_port *up = serio->port_data; unsigned long flags; int lsr; @@ -1014,7 +1014,7 @@ static int sunsu_serio_write(struct seri static int sunsu_serio_open(struct serio *serio) { - struct uart_sunsu_port *up = serio->driver; + struct uart_sunsu_port *up = serio->port_data; unsigned long flags; int ret; @@ -1031,7 +1031,7 @@ static int sunsu_serio_open(struct serio static void sunsu_serio_close(struct serio *serio) { - struct uart_sunsu_port *up = serio->driver; + struct uart_sunsu_port *up = serio->port_data; unsigned long flags; spin_lock_irqsave(&sunsu_serio_lock, flags); @@ -1284,54 +1284,58 @@ static struct uart_driver sunsu_reg = { .major = TTY_MAJOR, }; -static int __init sunsu_kbd_ms_init(void) +static int __init sunsu_kbd_ms_init(struct uart_sunsu_port *up, int channel) { - struct uart_sunsu_port *up; - int i; + struct serio *serio; - for (i = 0, up = sunsu_ports; i < 2; i++, up++) { - up->port.line = i; - up->port.type = PORT_UNKNOWN; - up->port.uartclk = (SU_BASE_BAUD * 16); + up->port.line = channel; + up->port.type = PORT_UNKNOWN; + up->port.uartclk = (SU_BASE_BAUD * 16); - if (up->su_type == SU_PORT_KBD) - up->cflag = B1200 | CS8 | CLOCAL | CREAD; - else - up->cflag = B4800 | CS8 | CLOCAL | CREAD; + if (up->su_type == SU_PORT_KBD) + up->cflag = B1200 | CS8 | CLOCAL | CREAD; + else + up->cflag = B4800 | CS8 | CLOCAL | CREAD; - sunsu_autoconfig(up); - if (up->port.type == PORT_UNKNOWN) - continue; + sunsu_autoconfig(up); + if (up->port.type == PORT_UNKNOWN) + return -1; - printk(KERN_INFO "su%d at 0x%p (irq = %s) is a %s\n", - i, - up->port.membase, __irq_itoa(up->irq), - sunsu_type(&up->port)); + printk(KERN_INFO "su%d at 0x%p (irq = %s) is a %s\n", + channel, + up->port.membase, __irq_itoa(up->irq), + sunsu_type(&up->port)); #ifdef CONFIG_SERIO - memset(&up->serio, 0, sizeof(up->serio)); + up->serio = serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (serio) { + memset(serio, 0, sizeof(serio)); - up->serio.driver = up; + serio->port_data = up; - up->serio.type = SERIO_RS232; + serio->type = SERIO_RS232; if (up->su_type == SU_PORT_KBD) { - up->serio.type |= SERIO_SUNKBD; - up->serio.name = "sukbd"; + serio->type |= SERIO_SUNKBD; + strlcpy(serio->name, "sukbd", sizeof(serio->name)); } else { - up->serio.type |= (SERIO_SUN | (1 << 16)); - up->serio.name = "sums"; + serio->type |= (SERIO_SUN | (1 << 16)); + strlcpy(serio->name, "sums", sizeof(serio->name)); } - up->serio.phys = (i == 0 ? "su/serio0" : "su/serio1"); + strlcpy(serio->phys, (channel == 0 ? "su/serio0" : "su/serio1"), + sizeof(serio->phys)); - up->serio.write = sunsu_serio_write; - up->serio.open = sunsu_serio_open; - up->serio.close = sunsu_serio_close; + serio->write = sunsu_serio_write; + serio->open = sunsu_serio_open; + serio->close = sunsu_serio_close; - serio_register_port(&up->serio); + serio_register_port(serio); + } else { + printk(KERN_WARNING "su%d: not enough memory for serio port\n", + channel); + } #endif - sunsu_startup(&up->port); - } + sunsu_startup(&up->port); return 0; } @@ -1680,10 +1684,12 @@ static int __init sunsu_probe(void) if (scan.msx != -1 && scan.kbx != -1) { sunsu_ports[0].su_type = SU_PORT_MS; sunsu_ports[0].port_node = scan.msnode; + sunsu_kbd_ms_init(&sunsu_ports[0], 0); + sunsu_ports[1].su_type = SU_PORT_KBD; sunsu_ports[1].port_node = scan.kbnode; + sunsu_kbd_ms_init(&sunsu_ports[1], 1); - sunsu_kbd_ms_init(); return 0; } @@ -1715,7 +1721,10 @@ static void __exit sunsu_exit(void) if (up->su_type == SU_PORT_MS || up->su_type == SU_PORT_KBD) { #ifdef CONFIG_SERIO - serio_unregister_port(&up->serio); + if (up->serio) { + serio_unregister_port(up->serio); + up->serio = NULL; + } #endif } else if (up->port.type != PORT_UNKNOWN) { uart_remove_one_port(&sunsu_reg, &up->port); --- linux-2.6.9-rc1/drivers/serial/sunzilog.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/serial/sunzilog.c 2004-09-03 00:56:31.584359096 -0700 @@ -107,7 +107,7 @@ struct uart_sunzilog_port { unsigned char prev_status; #ifdef CONFIG_SERIO - struct serio serio; + struct serio *serio; int serio_open; #endif }; @@ -291,7 +291,7 @@ static void sunzilog_kbdms_receive_chars /* Stop-A is handled by drivers/char/keyboard.c now. */ #ifdef CONFIG_SERIO if (up->serio_open) - serio_interrupt(&up->serio, ch, 0, regs); + serio_interrupt(up->serio, ch, 0, regs); #endif } else if (ZS_IS_MOUSE(up)) { int ret = suncore_mouse_baud_detection(ch, is_break); @@ -306,7 +306,7 @@ static void sunzilog_kbdms_receive_chars case 0: #ifdef CONFIG_SERIO if (up->serio_open) - serio_interrupt(&up->serio, ch, 0, regs); + serio_interrupt(up->serio, ch, 0, regs); #endif break; }; @@ -1295,7 +1295,7 @@ static spinlock_t sunzilog_serio_lock = static int sunzilog_serio_write(struct serio *serio, unsigned char ch) { - struct uart_sunzilog_port *up = serio->driver; + struct uart_sunzilog_port *up = serio->port_data; unsigned long flags; spin_lock_irqsave(&sunzilog_serio_lock, flags); @@ -1309,7 +1309,7 @@ static int sunzilog_serio_write(struct s static int sunzilog_serio_open(struct serio *serio) { - struct uart_sunzilog_port *up = serio->driver; + struct uart_sunzilog_port *up = serio->port_data; unsigned long flags; int ret; @@ -1326,7 +1326,7 @@ static int sunzilog_serio_open(struct se static void sunzilog_serio_close(struct serio *serio) { - struct uart_sunzilog_port *up = serio->driver; + struct uart_sunzilog_port *up = serio->port_data; unsigned long flags; spin_lock_irqsave(&sunzilog_serio_lock, flags); @@ -1545,33 +1545,45 @@ static void __init sunzilog_init_kbdms(s up->curregs[R15] = BRKIE; brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); sunzilog_convert_to_zs(up, up->cflag, 0, brg); + sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS); + __sunzilog_startup(up); +} #ifdef CONFIG_SERIO - memset(&up->serio, 0, sizeof(up->serio)); +static void __init sunzilog_register_serio(struct uart_sunzilog_port *up, int channel) +{ + struct serio *serio; - up->serio.driver = up; + up->serio = serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (serio) { - up->serio.type = SERIO_RS232; - if (channel == KEYBOARD_LINE) { - up->serio.type |= SERIO_SUNKBD; - up->serio.name = "zskbd"; - } else { - up->serio.type |= (SERIO_SUN | (1 << 16)); - up->serio.name = "zsms"; - } - up->serio.phys = (channel == KEYBOARD_LINE ? - "zs/serio0" : "zs/serio1"); + memset(serio, 0, sizeof(serio)); - up->serio.write = sunzilog_serio_write; - up->serio.open = sunzilog_serio_open; - up->serio.close = sunzilog_serio_close; + serio->port_data = up; - serio_register_port(&up->serio); -#endif + serio->type = SERIO_RS232; + if (channel == KEYBOARD_LINE) { + serio->type |= SERIO_SUNKBD; + strlcpy(serio->name, "zskbd", sizeof(serio->name)); + } else { + serio->type |= (SERIO_SUN | (1 << 16)); + strlcpy(serio->name, "zsms", sizeof(serio->name)); + } + strlcpy(serio->phys, + (channel == KEYBOARD_LINE ? "zs/serio0" : "zs/serio1"), + sizeof(serio->phys)); + + serio->write = sunzilog_serio_write; + serio->open = sunzilog_serio_open; + serio->close = sunzilog_serio_close; - sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS); - __sunzilog_startup(up); + serio_register_port(serio); + } else { + printk(KERN_WARNING "zs%d: not enough memory for serio port\n", + channel); + } } +#endif static void __init sunzilog_init_hw(void) { @@ -1615,6 +1627,11 @@ static void __init sunzilog_init_hw(void } spin_unlock_irqrestore(&up->port.lock, flags); + +#ifdef CONFIG_SERIO + if (i == KEYBOARD_LINE || i == MOUSE_LINE) + sunzilog_register_serio(up, i); +#endif } } @@ -1732,10 +1749,15 @@ static void __exit sunzilog_exit(void) for (i = 0; i < NUM_CHANNELS; i++) { struct uart_sunzilog_port *up = &sunzilog_port_table[i]; - if (ZS_IS_KEYB(up) || ZS_IS_MOUSE(up)) - continue; - - uart_remove_one_port(&sunzilog_reg, &up->port); + if (ZS_IS_KEYB(up) || ZS_IS_MOUSE(up)) { +#ifdef CONFIG_SERIO + if (up->serio) { + serio_unregister_port(up->serio); + up->serio = NULL; + } +#endif + } else + uart_remove_one_port(&sunzilog_reg, &up->port); } uart_unregister_driver(&sunzilog_reg); --- linux-2.6.9-rc1/drivers/tc/zs.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/tc/zs.c 2004-09-03 00:57:21.673744352 -0700 @@ -211,10 +211,6 @@ static void probe_sccs(void); static void change_speed(struct dec_serial *info); static void rs_wait_until_sent(struct tty_struct *tty, int timeout); -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif - /* * tmp_buf is used as a temporary buffer by serial_write. We need to * lock it in case the copy_from_user blocks while swapping in a page, @@ -706,7 +702,7 @@ int zs_startup(struct dec_serial * info) save_flags(flags); cli(); #ifdef SERIAL_DEBUG_OPEN - printk("starting up ttyS%02d (irq %d)...", info->line, info->irq); + printk("starting up ttyS%d (irq %d)...", info->line, info->irq); #endif /* @@ -950,16 +946,16 @@ static int rs_write(struct tty_struct * save_flags(flags); while (1) { cli(); - c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); + c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); if (c <= 0) break; if (from_user) { down(&tmp_buf_sem); copy_from_user(tmp_buf, buf, c); - c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); + c = min_t(int, c, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); up(&tmp_buf_sem); } else @@ -1356,7 +1352,7 @@ static void rs_close(struct tty_struct * } #ifdef SERIAL_DEBUG_OPEN - printk("rs_close ttyS%02d, count = %d\n", info->line, info->count); + printk("rs_close ttyS%d, count = %d\n", info->line, info->count); #endif if ((tty->count == 1) && (info->count != 1)) { /* @@ -1371,7 +1367,7 @@ static void rs_close(struct tty_struct * info->count = 1; } if (--info->count < 0) { - printk("rs_close: bad serial port count for ttyS%02d: %d\n", + printk("rs_close: bad serial port count for ttyS%d: %d\n", info->line, info->count); info->count = 0; } @@ -1446,7 +1442,7 @@ static void rs_wait_until_sent(struct tt if (char_time == 0) char_time = 1; if (timeout) - char_time = MIN(char_time, timeout); + char_time = min_t(unsigned long, char_time, timeout); while ((read_zsreg(info->zs_channel, 1) & Tx_BUF_EMP) == 0) { current->state = TASK_INTERRUPTIBLE; schedule_timeout(char_time); @@ -1531,7 +1527,7 @@ static int block_til_ready(struct tty_st retval = 0; add_wait_queue(&info->open_wait, &wait); #ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready before block: ttyS%02d, count = %d\n", + printk("block_til_ready before block: ttyS%d, count = %d\n", info->line, info->count); #endif cli(); @@ -1565,7 +1561,7 @@ static int block_til_ready(struct tty_st break; } #ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready blocking: ttyS%02d, count = %d\n", + printk("block_til_ready blocking: ttyS%d, count = %d\n", info->line, info->count); #endif schedule(); @@ -1576,7 +1572,7 @@ static int block_til_ready(struct tty_st info->count++; info->blocked_open--; #ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready after blocking: ttyS%02d, count = %d\n", + printk("block_til_ready after blocking: ttyS%d, count = %d\n", info->line, info->count); #endif if (retval) @@ -1742,14 +1738,10 @@ static void __init probe_sccs(void) * We're called early and memory managment isn't up, yet. * Thus check_region would fail. */ - if (check_region((unsigned long) + if (!request_region((unsigned long) zs_channels[n_channels].control, - ZS_CHAN_IO_SIZE) < 0) { + ZS_CHAN_IO_SIZE, "SCC")) panic("SCC I/O region is not free"); - } - request_region((unsigned long) - zs_channels[n_channels].control, - ZS_CHAN_IO_SIZE, "SCC"); #endif zs_soft[n_channels].zs_channel = &zs_channels[n_channels]; zs_soft[n_channels].irq = zs_parms->irq; @@ -1896,7 +1888,7 @@ int __init zs_init(void) info->tqueue.data = info; init_waitqueue_head(&info->open_wait); init_waitqueue_head(&info->close_wait); - printk("ttyS%02d at 0x%08x (irq = %d)", info->line, + printk("ttyS%d at 0x%08x (irq = %d)", info->line, info->port, info->irq); printk(" is a Z85C30 SCC\n"); tty_register_device(serial_driver, info->line, NULL); --- linux-2.6.9-rc1/drivers/telephony/ixj.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/drivers/telephony/ixj.c 2004-09-02 20:51:52.000000000 -0700 @@ -7718,6 +7718,23 @@ static void __exit ixj_exit(void) cleanup(); } +static IXJ *new_ixj(unsigned long port) +{ + IXJ *res; + if (!request_region(port, 16, "ixj DSP")) { + printk(KERN_INFO "ixj: can't get I/O address 0x%lx\n", port); + return NULL; + } + res = ixj_alloc(); + if (!res) { + release_region(port, 16); + printk(KERN_INFO "ixj: out of memory\n"); + return NULL; + } + res->DSPbase = port; + return res; +} + int __init ixj_probe_isapnp(int *cnt) { int probe = 0; @@ -7750,15 +7767,9 @@ int __init ixj_probe_isapnp(int *cnt) return -ENODEV; } - result = check_region(pnp_port_start(dev, 0), 16); - if (result) { - printk(KERN_INFO "ixj: can't get I/O address 0x%lx\n", pnp_port_start(dev, 0)); + j = new_ixj(pnp_port_start(dev, 0)); + if (!j) break; - } - - j = ixj_alloc(); - j->DSPbase = pnp_port_start(dev,0); - request_region(j->DSPbase, 16, "ixj DSP"); if (func != 0x110) j->XILINXbase = pnp_port_start(dev, 1); /* get real port */ @@ -7806,22 +7817,15 @@ int __init ixj_probe_isapnp(int *cnt) int __init ixj_probe_isa(int *cnt) { - int i, result, probe; + int i, probe; /* Use passed parameters for older kernels without PnP */ for (i = 0; i < IXJMAX; i++) { if (dspio[i]) { - IXJ *j; + IXJ *j = new_ixj(dspio[i]); - if ((result = check_region(ixj[*cnt].DSPbase, 16)) < 0) { - printk(KERN_INFO "ixj: can't get I/O address 0x%x\n", ixj[*cnt].DSPbase); + if (!j) break; - } - - j = ixj_alloc(); - - j->DSPbase = dspio[i]; - request_region(j->DSPbase, 16, "ixj DSP"); j->XILINXbase = xio[i]; j->cardtype = 0; @@ -7840,7 +7844,6 @@ int __init ixj_probe_pci(int *cnt) struct pci_dev *pci = NULL; int i, probe = 0; IXJ *j = NULL; - int result; for (i = 0; i < IXJMAX - *cnt; i++) { pci = pci_find_device(0x15E2, 0x0500, pci); @@ -7849,20 +7852,12 @@ int __init ixj_probe_pci(int *cnt) if (pci_enable_device(pci)) break; - if ((result = check_region(pci_resource_start(pci, 0), 16)) < 0) { - printk(KERN_INFO "ixj: can't get I/O address\n"); + j = new_ixj(pci_resource_start(pci, 0)); + if (!j) break; - } - /* Grab a device slot */ - j = ixj_alloc(); - if(j == NULL) - break; - - j->DSPbase = pci_resource_start(pci, 0); j->serial = (PCIEE_GetSerialNumber)pci_resource_start(pci, 2); j->XILINXbase = j->DSPbase + 0x10; - request_region(j->DSPbase, 16, "ixj DSP"); j->cardtype = QTI_PHONEJACK_PCI; j->board = *cnt; probe = ixj_selfprobe(j); --- linux-2.6.9-rc1/drivers/telephony/ixj.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/telephony/ixj.h 2004-09-02 20:51:52.000000000 -0700 @@ -1188,12 +1188,12 @@ typedef struct { unsigned int cid_rec_codec; unsigned int cid_rec_volume; unsigned char cid_rec_flag; - char rec_mode; + signed char rec_mode; unsigned int play_codec; unsigned int cid_play_codec; unsigned int cid_play_volume; unsigned char cid_play_flag; - char play_mode; + signed char play_mode; IXJ_FLAGS flags; unsigned long busyflags; unsigned int rec_frame_size; --- linux-2.6.9-rc1/drivers/usb/class/cdc-acm.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/drivers/usb/class/cdc-acm.c 2004-09-02 20:51:52.000000000 -0700 @@ -367,7 +367,7 @@ static int acm_tty_write(struct tty_stru acm->writeurb->dev = acm->dev; acm->ready_for_write = 0; - stat = usb_submit_urb(acm->writeurb, GFP_NOIO); + stat = usb_submit_urb(acm->writeurb, from_user ? GFP_KERNEL : GFP_ATOMIC); if (stat < 0) { dbg("usb_submit_urb(write bulk) failed"); acm->ready_for_write = 1; --- linux-2.6.9-rc1/drivers/usb/core/config.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/drivers/usb/core/config.c 2004-09-02 20:51:52.000000000 -0700 @@ -106,7 +106,7 @@ skip_to_next_endpoint_or_interface_descr return buffer - buffer0 + i; } -static void usb_release_interface_cache(struct kref *ref) +void usb_release_interface_cache(struct kref *ref) { struct usb_interface_cache *intfc = ref_to_usb_interface_cache(ref); int j; @@ -356,7 +356,7 @@ int usb_parse_configuration(struct devic if (!intfc) return -ENOMEM; memset(intfc, 0, len); - kref_init(&intfc->ref, usb_release_interface_cache); + kref_init(&intfc->ref); } /* Skip over any Class Specific or Vendor Specific descriptors; @@ -422,7 +422,8 @@ void usb_destroy_configuration(struct us for (i = 0; i < cf->desc.bNumInterfaces; i++) { if (cf->intf_cache[i]) - kref_put(&cf->intf_cache[i]->ref); + kref_put(&cf->intf_cache[i]->ref, + usb_release_interface_cache); } } kfree(dev->config); --- linux-2.6.9-rc1/drivers/usb/core/hcd.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/usb/core/hcd.c 2004-09-02 20:51:52.000000000 -0700 @@ -790,6 +790,8 @@ int usb_register_root_hub (struct usb_de usb_dev->epmaxpacketin[0] = usb_dev->epmaxpacketout[0] = 64; retval = usb_get_device_descriptor(usb_dev, USB_DT_DEVICE_SIZE); if (retval != sizeof usb_dev->descriptor) { + usb_dev->bus->root_hub = NULL; + up (&usb_bus_list_lock); dev_dbg (parent_dev, "can't read %s device descriptor %d\n", usb_dev->dev.bus_id, retval); return (retval < 0) ? retval : -EMSGSIZE; @@ -1404,6 +1406,45 @@ static int hcd_hub_resume (struct usb_bu /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_USB_OTG + +/** + * usb_bus_start_enum - start immediate enumeration (for OTG) + * @bus: the bus (must use hcd framework) + * @port: 1-based number of port; usually bus->otg_port + * Context: in_interrupt() + * + * Starts enumeration, with an immediate reset followed later by + * khubd identifying and possibly configuring the device. + * This is needed by OTG controller drivers, where it helps meet + * HNP protocol timing requirements for starting a port reset. + */ +int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num) +{ + struct usb_hcd *hcd; + int status = -EOPNOTSUPP; + + /* NOTE: since HNP can't start by grabbing the bus's address0_sem, + * boards with root hubs hooked up to internal devices (instead of + * just the OTG port) may need more attention to resetting... + */ + hcd = container_of (bus, struct usb_hcd, self); + if (port_num && hcd->driver->start_port_reset) + status = hcd->driver->start_port_reset(hcd, port_num); + + /* run khubd shortly after (first) root port reset finishes; + * it may issue others, until at least 50 msecs have passed. + */ + if (status == 0) + mod_timer(&hcd->rh_timer, jiffies + msecs_to_jiffies(10)); + return status; +} +EXPORT_SYMBOL (usb_bus_start_enum); + +#endif + +/*-------------------------------------------------------------------------*/ + /* called by khubd, rmmod, apmd, or other thread for hcd-private cleanup. * we're guaranteed that the device is fully quiesced. also, that each * endpoint has been hcd_endpoint_disabled. --- linux-2.6.9-rc1/drivers/usb/core/hcd.h 2004-08-24 00:02:25.000000000 -0700 +++ 25/drivers/usb/core/hcd.h 2004-09-02 20:51:52.000000000 -0700 @@ -212,6 +212,7 @@ struct hc_driver { char *buf, u16 wLength); int (*hub_suspend)(struct usb_hcd *); int (*hub_resume)(struct usb_hcd *); + int (*start_port_reset)(struct usb_hcd *, unsigned port_num); }; extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs); --- linux-2.6.9-rc1/drivers/usb/core/hub.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/drivers/usb/core/hub.c 2004-09-02 20:51:52.000000000 -0700 @@ -1095,6 +1095,10 @@ static inline void show_string(struct us {} #endif +#ifdef CONFIG_USB_OTG +#include "otg_whitelist.h" +#endif + /** * usb_new_device - perform initial device setup (usbcore-internal) * @udev: newly addressed device (in ADDRESS state) @@ -1144,6 +1148,79 @@ int usb_new_device(struct usb_device *ud show_string(udev, "SerialNumber", udev->descriptor.iSerialNumber); +#ifdef CONFIG_USB_OTG + /* + * OTG-aware devices on OTG-capable root hubs may be able to use SRP, + * to wake us after we've powered off VBUS; and HNP, switching roles + * "host" to "peripheral". The OTG descriptor helps figure this out. + */ + if (!udev->bus->is_b_host + && udev->config + && udev->parent == udev->bus->root_hub) { + struct usb_otg_descriptor *desc = 0; + struct usb_bus *bus = udev->bus; + + /* descriptor may appear anywhere in config */ + if (__usb_get_extra_descriptor (udev->rawdescriptors[0], + udev->config[0].desc.wTotalLength, + USB_DT_OTG, (void **) &desc) == 0) { + if (desc->bmAttributes & USB_OTG_HNP) { + unsigned port; + struct usb_device *root = udev->parent; + + for (port = 0; port < root->maxchild; port++) { + if (root->children[port] == udev) + break; + } + port++; + + dev_info(&udev->dev, + "Dual-Role OTG device on %sHNP port\n", + (port == bus->otg_port) + ? "" : "non-"); + + /* enable HNP before suspend, it's simpler */ + if (port == bus->otg_port) + bus->b_hnp_enable = 1; + err = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, 0, + bus->b_hnp_enable + ? USB_DEVICE_B_HNP_ENABLE + : USB_DEVICE_A_ALT_HNP_SUPPORT, + 0, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT); + if (err < 0) { + /* OTG MESSAGE: report errors here, + * customize to match your product. + */ + dev_info(&udev->dev, + "can't set HNP mode; %d\n", + err); + bus->b_hnp_enable = 0; + } + } + } + } + + if (!is_targeted(udev)) { + + /* Maybe it can talk to us, though we can't talk to it. + * (Includes HNP test device.) + */ + if (udev->bus->b_hnp_enable || udev->bus->is_b_host) { + static int __usb_suspend_device (struct usb_device *, + int port, u32 state); + err = __usb_suspend_device(udev, + udev->bus->otg_port - 1, + PM_SUSPEND_MEM); + if (err < 0) + dev_dbg(&udev->dev, "HNP fail, %d\n", err); + } + err = -ENODEV; + goto fail; + } +#endif + /* put device-specific files into sysfs */ err = device_add (&udev->dev); if (err) { @@ -1934,6 +2011,10 @@ hub_port_init (struct usb_device *hdev, hdev->bus->b_hnp_enable = 0; } + retval = clear_port_feature(hdev, port, USB_PORT_FEAT_SUSPEND); + if (retval < 0 && retval != -EPIPE) + dev_dbg(&udev->dev, "can't clear suspend; %d\n", retval); + /* Some low speed devices have problems with the quick delay, so */ /* be a bit pessimistic with those devices. RHbug #23670 */ if (oldspeed == USB_SPEED_LOW) @@ -2160,6 +2241,12 @@ static void hub_port_connect_change(stru usb_disconnect(&hdev->children[port]); clear_bit(port, hub->change_bits); +#ifdef CONFIG_USB_OTG + /* during HNP, don't repeat the debounce */ + if (hdev->bus->is_b_host) + portchange &= ~USB_PORT_STAT_C_CONNECTION; +#endif + if (portchange & USB_PORT_STAT_C_CONNECTION) { status = hub_port_debounce(hdev, port); if (status < 0) { --- linux-2.6.9-rc1/drivers/usb/core/hub.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/usb/core/hub.h 2004-09-02 20:51:52.000000000 -0700 @@ -190,8 +190,8 @@ struct usb_hub { struct usb_device *hdev; struct urb *urb; /* for interrupt polling pipe */ - /* buffer for urb ... 1 bit each for hub and children, rounded up */ - char (*buffer)[(USB_MAXCHILDREN + 1 + 7) / 8]; + /* buffer for urb ... with extra space in case of babble */ + char (*buffer)[8]; dma_addr_t buffer_dma; /* DMA address for buffer */ union { struct usb_hub_status hub; --- linux-2.6.9-rc1/drivers/usb/core/Kconfig 2004-08-24 00:03:32.000000000 -0700 +++ 25/drivers/usb/core/Kconfig 2004-09-02 20:51:52.000000000 -0700 @@ -71,3 +71,29 @@ config USB_SUSPEND may not yet work as expected. If you are unsure about this, say N here. + + +config USB_OTG + bool + depends on USB && EXPERIMENTAL + select USB_SUSPEND + default n + + +config USB_OTG_WHITELIST + bool "Rely on OTG Targeted Peripherals List" + depends on USB_OTG + default y + help + If you say Y here, the "otg_whitelist.h" file will be used as a + product whitelist, so USB peripherals not listed there will be + rejected during enumeration. This behavior is required by the + USB OTG specification for all devices not on your product's + "Targeted Peripherals List". + + Otherwise, peripherals not listed there will only generate a + warning and enumeration will continue. That's more like what + normal Linux-USB hosts do (other than the warning), and is + convenient for many stages of product development. + + --- linux-2.6.9-rc1/drivers/usb/core/message.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/usb/core/message.c 2004-09-02 20:51:52.000000000 -0700 @@ -248,7 +248,7 @@ static void sg_complete (struct urb *urb * unlink pending urbs so they won't rx/tx bad data. */ for (i = 0, found = 0; i < io->entries; i++) { - if (!io->urbs [i]) + if (!io->urbs [i] || !io->urbs [i]->dev) continue; if (found) { status = usb_unlink_urb (io->urbs [i]); @@ -337,7 +337,7 @@ int usb_sg_init ( if (io->entries <= 0) return io->entries; - io->count = 0; + io->count = io->entries; io->urbs = kmalloc (io->entries * sizeof *io->urbs, mem_flags); if (!io->urbs) goto nomem; @@ -347,7 +347,7 @@ int usb_sg_init ( if (usb_pipein (pipe)) urb_flags |= URB_SHORT_NOT_OK; - for (i = 0; i < io->entries; i++, io->count = i) { + for (i = 0; i < io->entries; i++) { unsigned len; io->urbs [i] = usb_alloc_urb (0, mem_flags); @@ -477,24 +477,19 @@ void usb_sg_wait (struct usb_sg_request /* fail any uncompleted urbs */ default: - spin_lock_irq (&io->lock); - io->count -= entries - i; - if (io->status == -EINPROGRESS) - io->status = retval; - if (io->count == 0) - complete (&io->complete); - spin_unlock_irq (&io->lock); - - io->urbs[i]->dev = NULL; + io->urbs [i]->dev = NULL; io->urbs [i]->status = retval; dev_dbg (&io->dev->dev, "%s, submit --> %d\n", __FUNCTION__, retval); usb_sg_cancel (io); } spin_lock_irq (&io->lock); - if (retval && io->status == -ECONNRESET) + if (retval && (io->status == 0 || io->status == -ECONNRESET)) io->status = retval; } + io->count -= entries - i; + if (io->count == 0) + complete (&io->complete); spin_unlock_irq (&io->lock); /* OK, yes, this could be packaged as non-blocking. @@ -1196,7 +1191,7 @@ static void release_interface(struct dev struct usb_interface_cache *intfc = altsetting_to_usb_interface_cache(intf->altsetting); - kref_put(&intfc->ref); + kref_put(&intfc->ref, usb_release_interface_cache); kfree(intf); } --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/usb/core/otg_whitelist.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,112 @@ +/* + * drivers/usb/core/otg_whitelist.h + * + * Copyright (C) 2004 Texas Instruments + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +/* + * This OTG Whitelist is the OTG "Targeted Peripheral List". It should + * mostly use of USB_DEVICE() or USB_DEVICE_VER() entries.. + * + * YOU _SHOULD_ CHANGE THIS LIST TO MATCH YOUR PRODUCT AND ITS TESTING! + */ + +static struct usb_device_id whitelist_table [] = { + +/* hubs are optional in OTG, but very handy ... */ +{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 0), }, +{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 1), }, + +#ifdef CONFIG_USB_PRINTER /* ignoring nonstatic linkage! */ +/* FIXME actually, printers are NOT supposed to use device classes; + * they're supposed to use interface classes... + */ +{ USB_DEVICE_INFO(7, 1, 1) }, +{ USB_DEVICE_INFO(7, 1, 2) }, +{ USB_DEVICE_INFO(7, 1, 3) }, +#endif + +#ifdef CONFIG_USB_CDCETHER +/* Linux-USB CDC Ethernet gadget */ +{ USB_DEVICE(0x0525, 0xa4a1), }, +/* Linux-USB CDC Ethernet + RNDIS gadget */ +{ USB_DEVICE(0x0525, 0xa4a2), }, +#endif + +#if defined(CONFIG_USB_TEST) || defined(CONFIG_USB_TEST_MODULE) +/* gadget zero, for testing */ +{ USB_DEVICE(0x0525, 0xa4a0), }, +#endif + +{ } /* Terminating entry */ +}; + +static int is_targeted(struct usb_device *dev) +{ + struct usb_device_id *id = whitelist_table; + + /* possible in developer configs only! */ + if (!dev->bus->otg_port) + return 1; + + /* HNP test device is _never_ targeted (see OTG spec 6.6.6) */ + if (dev->descriptor.idVendor == 0x1a0a + && dev->descriptor.idProduct == 0xbadd) + return 0; + + /* NOTE: can't use usb_match_id() since interface caches + * aren't set up yet. this is cut/paste from that code. + */ + for (id = whitelist_table; id->match_flags; id++) { + if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && + id->idVendor != dev->descriptor.idVendor) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) && + id->idProduct != dev->descriptor.idProduct) + continue; + + /* No need to test id->bcdDevice_lo != 0, since 0 is never + greater than any unsigned number. */ + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) && + (id->bcdDevice_lo > dev->descriptor.bcdDevice)) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) && + (id->bcdDevice_hi < dev->descriptor.bcdDevice)) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) && + (id->bDeviceClass != dev->descriptor.bDeviceClass)) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) && + (id->bDeviceSubClass!= dev->descriptor.bDeviceSubClass)) + continue; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) && + (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol)) + continue; + + return 1; + } + + /* add other match criteria here ... */ + + + /* OTG MESSAGE: report errors here, customize to match your product */ + dev_err(&dev->dev, "device v%04x p%04x is not supported\n", + dev->descriptor.idVendor, + dev->descriptor.idProduct); +#ifdef CONFIG_USB_OTG_WHITELIST + return 0; +#else + return 1; +#endif +} + --- linux-2.6.9-rc1/drivers/usb/core/urb.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/usb/core/urb.c 2004-09-02 20:51:52.000000000 -0700 @@ -39,7 +39,7 @@ void usb_init_urb(struct urb *urb) { if (urb) { memset(urb, 0, sizeof(*urb)); - kref_init(&urb->kref, urb_destroy); + kref_init(&urb->kref); spin_lock_init(&urb->lock); } } @@ -88,7 +88,7 @@ struct urb *usb_alloc_urb(int iso_packet void usb_free_urb(struct urb *urb) { if (urb) - kref_put(&urb->kref); + kref_put(&urb->kref, urb_destroy); } /** --- linux-2.6.9-rc1/drivers/usb/core/usb.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/usb/core/usb.h 2004-09-02 20:51:52.000000000 -0700 @@ -10,6 +10,7 @@ extern int usb_unbind_interface (struct extern void usb_disable_endpoint (struct usb_device *dev, unsigned int epaddr); extern void usb_disable_interface (struct usb_device *dev, struct usb_interface *intf); +extern void usb_release_interface_cache(struct kref *ref); extern void usb_disable_device (struct usb_device *dev, int skip_ep0); extern void usb_enable_endpoint (struct usb_device *dev, --- linux-2.6.9-rc1/drivers/usb/gadget/ether.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/drivers/usb/gadget/ether.c 2004-09-03 00:56:32.625200864 -0700 @@ -63,6 +63,7 @@ /* * Ethernet gadget driver -- with CDC and non-CDC options + * Builds on hardware support for a full duplex link. * * CDC Ethernet is the standard USB solution for sending Ethernet frames * using USB. Real hardware tends to use the same framing protocol but look @@ -242,6 +243,10 @@ MODULE_PARM_DESC(host_addr, "Host Ethern #define DEV_CONFIG_SUBSET #endif +#ifdef CONFIG_USB_GADGET_LH7A40X +#define DEV_CONFIG_CDC +#endif + #ifdef CONFIG_USB_GADGET_SA1100 /* use non-CDC for backwards compatibility */ #define DEV_CONFIG_SUBSET @@ -855,7 +860,7 @@ static char product_desc [40] = DRIVE static char ethaddr [2 * ETH_ALEN + 1]; #endif -/* static strings, in iso 8859/1 */ +/* static strings, in UTF-8 */ static struct usb_string strings [] = { { STRING_MANUFACTURER, manufacturer, }, { STRING_PRODUCT, product_desc, }, @@ -897,9 +902,9 @@ config_buf (enum usb_device_speed speed, if (type == USB_DT_OTHER_SPEED_CONFIG) hs = !hs; -#define which_fn(t) (hs ? & hs_ ## t ## _function : & fs_ ## t ## _function) +#define which_fn(t) (hs ? hs_ ## t ## _function : fs_ ## t ## _function) #else -#define which_fn(t) (& fs_ ## t ## _function) +#define which_fn(t) (fs_ ## t ## _function) #endif if (index >= device_desc.bNumConfigurations) @@ -911,14 +916,12 @@ config_buf (enum usb_device_speed speed, */ if (device_desc.bNumConfigurations == 2 && index == 0) { config = &rndis_config; - function = (const struct usb_descriptor_header **) - which_fn (rndis); + function = which_fn (rndis); } else #endif { config = ð_config; - function = (const struct usb_descriptor_header **) - which_fn (eth); + function = which_fn (eth); } /* for now, don't advertise srp-only devices */ @@ -1667,7 +1670,7 @@ eth_disconnect (struct usb_gadget *gadge static int eth_change_mtu (struct net_device *net, int new_mtu) { - struct eth_dev *dev = (struct eth_dev *) net->priv; + struct eth_dev *dev = netdev_priv(net); // FIXME if rndis, don't change while link's live @@ -1682,58 +1685,29 @@ static int eth_change_mtu (struct net_de static struct net_device_stats *eth_get_stats (struct net_device *net) { - return &((struct eth_dev *) net->priv)->stats; + return &((struct eth_dev *)netdev_priv(net))->stats; } -static int eth_ethtool_ioctl (struct net_device *net, void __user *useraddr) +static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p) { - struct eth_dev *dev = (struct eth_dev *) net->priv; - u32 cmd; - - if (get_user (cmd, (u32 __user *)useraddr)) - return -EFAULT; - switch (cmd) { - - case ETHTOOL_GDRVINFO: { /* get driver info */ - struct ethtool_drvinfo info; - - memset (&info, 0, sizeof info); - info.cmd = ETHTOOL_GDRVINFO; - strlcpy (info.driver, shortname, sizeof info.driver); - strlcpy (info.version, DRIVER_VERSION, sizeof info.version); - strlcpy (info.fw_version, dev->gadget->name, - sizeof info.fw_version); - strlcpy (info.bus_info, dev->gadget->dev.bus_id, - sizeof info.bus_info); - if (copy_to_user (useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } - - case ETHTOOL_GLINK: { /* get link status */ - struct ethtool_value edata = { ETHTOOL_GLINK }; - - edata.data = (dev->gadget->speed != USB_SPEED_UNKNOWN); - if (copy_to_user (useraddr, &edata, sizeof (edata))) - return -EFAULT; - return 0; - } - - } - /* Note that the ethtool user space code requires EOPNOTSUPP */ - return -EOPNOTSUPP; + struct eth_dev *dev = netdev_priv(net); + strlcpy(p->driver, shortname, sizeof p->driver); + strlcpy(p->version, DRIVER_VERSION, sizeof p->version); + strlcpy(p->fw_version, dev->gadget->name, sizeof p->fw_version); + strlcpy (p->bus_info, dev->gadget->dev.bus_id, sizeof p->bus_info); } -static int eth_ioctl (struct net_device *net, struct ifreq *rq, int cmd) +static u32 eth_get_link(struct net_device *net) { - switch (cmd) { - case SIOCETHTOOL: - return eth_ethtool_ioctl(net, rq->ifr_data); - default: - return -EOPNOTSUPP; - } + struct eth_dev *dev = netdev_priv(net); + return dev->gadget->speed != USB_SPEED_UNKNOWN; } +static struct ethtool_ops ops = { + .get_drvinfo = eth_get_drvinfo, + .get_link = eth_get_link +}; + static void defer_kevent (struct eth_dev *dev, int flag) { if (test_and_set_bit (flag, &dev->todo)) @@ -1994,7 +1968,7 @@ static void tx_complete (struct usb_ep * static int eth_start_xmit (struct sk_buff *skb, struct net_device *net) { - struct eth_dev *dev = (struct eth_dev *) net->priv; + struct eth_dev *dev = netdev_priv(net); int length = skb->len; int retval; struct usb_request *req = NULL; @@ -2107,7 +2081,7 @@ static void rndis_control_ack_complete ( static int rndis_control_ack (struct net_device *net) { - struct eth_dev *dev = (struct eth_dev *) net->priv; + struct eth_dev *dev = netdev_priv(net); u32 length; struct usb_request *resp; @@ -2174,7 +2148,7 @@ static void eth_start (struct eth_dev *d static int eth_open (struct net_device *net) { - struct eth_dev *dev = (struct eth_dev *) net->priv; + struct eth_dev *dev = netdev_priv(net); DEBUG (dev, "%s\n", __FUNCTION__); if (netif_carrier_ok (dev->net)) @@ -2184,7 +2158,7 @@ static int eth_open (struct net_device * static int eth_stop (struct net_device *net) { - struct eth_dev *dev = (struct eth_dev *) net->priv; + struct eth_dev *dev = netdev_priv(net); VDEBUG (dev, "%s\n", __FUNCTION__); netif_stop_queue (net); @@ -2329,6 +2303,8 @@ eth_bind (struct usb_gadget *gadget) device_desc.bcdDevice = __constant_cpu_to_le16 (0x0207); } else if (gadget_is_omap (gadget)) { device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208); + } else if (gadget_is_lh7a40x(gadget)) { + device_desc.bcdDevice = __constant_cpu_to_le16 (0x0209); } else { /* can't assume CDC works. don't want to default to * anything less functional on CDC-capable hardware, @@ -2469,7 +2445,7 @@ autoconf_fail: net = alloc_etherdev (sizeof *dev); if (!net) return status; - dev = net->priv; + dev = netdev_priv(net); spin_lock_init (&dev->lock); INIT_WORK (&dev->work, eth_work, dev); INIT_LIST_HEAD (&dev->tx_reqs); @@ -2513,7 +2489,7 @@ autoconf_fail: net->stop = eth_stop; // watchdog_timeo, tx_timeout ... // set_multicast_list - net->do_ioctl = eth_ioctl; + SET_ETHTOOL_OPS(net, &ops); /* preallocate control response and buffer */ dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL); --- linux-2.6.9-rc1/drivers/usb/gadget/file_storage.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/drivers/usb/gadget/file_storage.c 2004-09-02 20:51:52.000000000 -0700 @@ -3713,6 +3713,8 @@ static int __init check_parameters(struc mod_data.release = __constant_cpu_to_le16(0x0307); else if (gadget_is_omap(fsg->gadget)) mod_data.release = __constant_cpu_to_le16(0x0308); + else if (gadget_is_lh7a40x(gadget)) + mod_data.release = __constant_cpu_to_le16 (0x0309); else { WARN(fsg, "controller '%s' not recognized\n", fsg->gadget->name); --- linux-2.6.9-rc1/drivers/usb/gadget/gadget_chips.h 2004-08-24 00:01:50.000000000 -0700 +++ 25/drivers/usb/gadget/gadget_chips.h 2004-09-02 20:51:52.000000000 -0700 @@ -44,6 +44,12 @@ #define gadget_is_sa1100(g) 0 #endif +#ifdef CONFIG_USB_GADGET_LH7A40X +#define gadget_is_lh7a40x(g) !strcmp("lh7a40x_udc", (g)->name) +#else +#define gadget_is_lh7a40x(g) 0 +#endif + #ifdef CONFIG_USB_GADGET_MQ11XX #define gadget_is_mq11xx(g) !strcmp("mq11xx_udc", (g)->name) #else --- linux-2.6.9-rc1/drivers/usb/gadget/inode.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/drivers/usb/gadget/inode.c 2004-09-02 20:51:52.000000000 -0700 @@ -1,7 +1,7 @@ /* * inode.c -- user mode filesystem api for usb gadget controllers * - * Copyright (C) 2003 David Brownell + * Copyright (C) 2003-2004 David Brownell * Copyright (C) 2003 Agilent Technologies * * This program is free software; you can redistribute it and/or modify @@ -71,7 +71,7 @@ */ #define DRIVER_DESC "USB Gadget filesystem" -#define DRIVER_VERSION "18 Nov 2003" +#define DRIVER_VERSION "24 Aug 2004" static const char driver_desc [] = DRIVER_DESC; static const char shortname [] = "gadgetfs"; @@ -229,37 +229,12 @@ static void put_ep (struct ep_data *data /*----------------------------------------------------------------------*/ /* most "how to use the hardware" policy choices are in userspace: - * mapping endpoint roles the driver needs to the capabilities that - * the usb controller exposes. + * mapping endpoint roles (which the driver needs) to the capabilities + * which the usb controller has. most of those capabilities are exposed + * implicitly, starting with the driver name and then endpoint names. */ -#ifdef CONFIG_USB_GADGET_DUMMY_HCD -/* act (mostly) like a net2280 */ -#define CONFIG_USB_GADGET_NET2280 -#endif - -#ifdef CONFIG_USB_GADGET_NET2280 -#define CHIP "net2280" -#define HIGHSPEED -#endif - -#ifdef CONFIG_USB_GADGET_PXA2XX -#define CHIP "pxa2xx_udc" -/* earlier hardware doesn't have UDCCFR, races set_{config,interface} */ -#warning works best with pxa255 or newer -#endif - -#ifdef CONFIG_USB_GADGET_GOKU -#define CHIP "goku_udc" -#endif - -#ifdef CONFIG_USB_GADGET_OMAP -#define CHIP "omap_udc" -#endif - -#ifdef CONFIG_USB_GADGET_SA1100 -#define CHIP "sa1100" -#endif +static const char *CHIP; /*----------------------------------------------------------------------*/ @@ -558,7 +533,7 @@ struct kiocb_priv { static int ep_aio_cancel(struct kiocb *iocb, struct io_event *e) { - struct kiocb_priv *priv = (void *) &iocb->private; + struct kiocb_priv *priv = iocb->private; struct ep_data *epdata; int value; @@ -577,10 +552,10 @@ static int ep_aio_cancel(struct kiocb *i return value; } -static long ep_aio_read_retry(struct kiocb *iocb) +static ssize_t ep_aio_read_retry(struct kiocb *iocb) { - struct kiocb_priv *priv = (void *) &iocb->private; - int status = priv->actual; + struct kiocb_priv *priv = iocb->private; + ssize_t status = priv->actual; /* we "retry" to get the right mm context for this: */ status = copy_to_user(priv->ubuf, priv->buf, priv->actual); @@ -589,6 +564,7 @@ static long ep_aio_read_retry(struct kio else status = priv->actual; kfree(priv->buf); + kfree(priv); aio_put_req(iocb); return status; } @@ -596,7 +572,7 @@ static long ep_aio_read_retry(struct kio static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) { struct kiocb *iocb = req->context; - struct kiocb_priv *priv = (void *) &iocb->private; + struct kiocb_priv *priv = iocb->private; struct ep_data *epdata = priv->epdata; /* lock against disconnect (and ideally, cancel) */ @@ -607,6 +583,8 @@ static void ep_aio_complete(struct usb_e || unlikely(0 == req->actual) || unlikely(kiocbIsCancelled(iocb))) { kfree(req->buf); + kfree(priv); + iocb->private = NULL; /* aio_complete() reports bytes-transferred _and_ faults */ if (unlikely(kiocbIsCancelled(iocb))) aio_put_req(iocb); @@ -631,17 +609,33 @@ static void ep_aio_complete(struct usb_e } static ssize_t -ep_aio_rwtail(struct kiocb *iocb, char *buf, size_t len, struct ep_data *epdata) +ep_aio_rwtail( + struct kiocb *iocb, + char *buf, + size_t len, + struct ep_data *epdata, + char __user *ubuf +) { struct kiocb_priv *priv = (void *) &iocb->private; struct usb_request *req; ssize_t value; - value = get_ready_ep(iocb->ki_filp->f_flags, epdata); - if (unlikely(value < 0)) { + priv = kmalloc(sizeof *priv, GFP_KERNEL); + if (!priv) { + value = -ENOMEM; +fail: kfree(buf); return value; } + iocb->private = priv; + priv->ubuf = ubuf; + + value = get_ready_ep(iocb->ki_filp->f_flags, epdata); + if (unlikely(value < 0)) { + kfree(priv); + goto fail; + } iocb->ki_cancel = ep_aio_cancel; get_ep(epdata); @@ -671,9 +665,10 @@ ep_aio_rwtail(struct kiocb *iocb, char * up(&epdata->lock); - if (unlikely(value)) + if (unlikely(value)) { + kfree(priv); put_ep(epdata); - else + } else value = -EIOCBQUEUED; return value; } @@ -681,7 +676,6 @@ ep_aio_rwtail(struct kiocb *iocb, char * static ssize_t ep_aio_read(struct kiocb *iocb, char __user *ubuf, size_t len, loff_t o) { - struct kiocb_priv *priv = (void *) &iocb->private; struct ep_data *epdata = iocb->ki_filp->private_data; char *buf; @@ -691,8 +685,7 @@ ep_aio_read(struct kiocb *iocb, char __u if (unlikely(!buf)) return -ENOMEM; iocb->ki_retry = ep_aio_read_retry; - priv->ubuf = ubuf; - return ep_aio_rwtail(iocb, buf, len, epdata); + return ep_aio_rwtail(iocb, buf, len, epdata, ubuf); } static ssize_t @@ -710,7 +703,7 @@ ep_aio_write(struct kiocb *iocb, const c kfree(buf); return -EFAULT; } - return ep_aio_rwtail(iocb, buf, len, epdata); + return ep_aio_rwtail(iocb, buf, len, epdata, NULL); } /*----------------------------------------------------------------------*/ @@ -718,6 +711,8 @@ ep_aio_write(struct kiocb *iocb, const c /* used after endpoint configuration */ static struct file_operations ep_io_operations = { .owner = THIS_MODULE, + .llseek = no_llseek, + .read = ep_read, .write = ep_write, .ioctl = ep_ioctl, @@ -874,6 +869,8 @@ ep_open (struct inode *inode, struct fil /* used before endpoint configuration */ static struct file_operations ep_config_operations = { .owner = THIS_MODULE, + .llseek = no_llseek, + .open = ep_open, .write = ep_config, .release = ep_release, @@ -980,6 +977,18 @@ ep0_read (struct file *fd, char __user * retval = usb_ep_queue (ep, req, GFP_ATOMIC); dev->state = STATE_CONNECTED; + /* assume that was SET_CONFIGURATION */ + if (dev->current_config) { + unsigned power; +#ifdef HIGHSPEED + if (dev->gadget->speed == USB_SPEED_HIGH) + power = dev->hs_config->bMaxPower; + else +#endif + power = dev->config->bMaxPower; + usb_gadget_vbus_draw(dev->gadget, 2 * power); + } + } else { /* collect OUT data */ if ((fd->f_flags & O_NONBLOCK) != 0 && !dev->setup_out_ready) { @@ -1230,6 +1239,8 @@ static int dev_ioctl (struct inode *inod /* used after device configuration */ static struct file_operations ep0_io_operations = { .owner = THIS_MODULE, + .llseek = no_llseek, + .read = ep0_read, .write = ep0_write, .fasync = ep0_fasync, @@ -1406,19 +1417,25 @@ gadgetfs_setup (struct usb_gadget *gadge if (0 == (u8) ctrl->wValue) { value = 0; dev->current_config = 0; + usb_gadget_vbus_draw(gadget, 8 /* mA */ ); // user mode expected to disable endpoints } else { - u8 config; + u8 config, power; #ifdef HIGHSPEED - if (gadget->speed == USB_SPEED_HIGH) + if (gadget->speed == USB_SPEED_HIGH) { config = dev->hs_config->bConfigurationValue; - else + power = dev->hs_config->bMaxPower; + } else #endif + { config = dev->config->bConfigurationValue; + power = dev->config->bMaxPower; + } if (config == (u8) ctrl->wValue) { value = 0; dev->current_config = config; + usb_gadget_vbus_draw(gadget, 2 * power); } } @@ -1636,8 +1653,8 @@ gadgetfs_bind (struct usb_gadget *gadget if (!dev) return -ESRCH; if (0 != strcmp (CHIP, gadget->name)) { - printk (KERN_ERR "%s expected " CHIP " controller not %s\n", - shortname, gadget->name); + printk (KERN_ERR "%s expected %s controller not %s\n", + shortname, CHIP, gadget->name); return -ENODEV; } @@ -1727,6 +1744,26 @@ static struct usb_gadget_driver gadgetfs /*----------------------------------------------------------------------*/ +static void gadgetfs_nop(struct usb_gadget *arg) { } + +static int gadgetfs_probe (struct usb_gadget *gadget) +{ + CHIP = gadget->name; + return -EISNAM; +} + +static struct usb_gadget_driver probe_driver = { + .speed = USB_SPEED_HIGH, + .bind = gadgetfs_probe, + .unbind = gadgetfs_nop, + .setup = (void *)gadgetfs_nop, + .disconnect = gadgetfs_nop, + .driver = { + .name = "nop", + }, +}; + + /* DEVICE INITIALIZATION * * fd = open ("/dev/gadget/$CHIP", O_RDWR) @@ -1763,6 +1800,7 @@ static int is_valid_config (struct usb_c && config->bConfigurationValue != 0 && (config->bmAttributes & USB_CONFIG_ATT_ONE) != 0 && (config->bmAttributes & USB_CONFIG_ATT_WAKEUP) == 0; + /* FIXME if gadget->is_otg, _must_ include an otg descriptor */ /* FIXME check lengths: walk to end */ } @@ -1881,6 +1919,8 @@ dev_open (struct inode *inode, struct fi static struct file_operations dev_init_operations = { .owner = THIS_MODULE, + .llseek = no_llseek, + .open = dev_open, .write = dev_config, .fasync = ep0_fasync, @@ -1976,6 +2016,11 @@ gadgetfs_fill_super (struct super_block if (the_device) return -ESRCH; + /* fake probe to determine $CHIP */ + (void) usb_gadget_register_driver (&probe_driver); + if (!CHIP) + return -ENODEV; + /* superblock */ sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; --- linux-2.6.9-rc1/drivers/usb/gadget/Kconfig 2004-08-24 00:02:25.000000000 -0700 +++ 25/drivers/usb/gadget/Kconfig 2004-09-02 20:51:52.000000000 -0700 @@ -135,6 +135,18 @@ config USB_SA1100 depends on USB_GADGET_SA1100 default USB_GADGET +config USB_GADGET_LH7A40X + boolean "LH7A40X" + depends on ARCH_LH7A40X + help + This driver provides USB Device Controller driver for LH7A40x + +config USB_LH7A40X + tristate + depends on USB_GADGET_LH7A40X + default USB_GADGET + + config USB_GADGET_DUMMY_HCD boolean "Dummy HCD (DEVELOPMENT)" depends on USB && EXPERIMENTAL @@ -163,6 +175,41 @@ config USB_DUMMY_HCD depends on USB_GADGET_DUMMY_HCD default USB_GADGET +config USB_GADGET_OMAP + boolean "OMAP USB Device Controller" + depends on ARCH_OMAP + select ISP1301_OMAP if MACH_OMAP_H2 + help + Many Texas Instruments OMAP processors have flexible full + speed USB device controllers, with support for up to 30 + endpoints (plus endpoint zero). This driver supports the + controller in the OMAP 1611, and should work with controllers + in other OMAP processors too, given minor tweaks. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "omap_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_OMAP + tristate + depends on USB_GADGET_OMAP + default USB_GADGET + +config USB_OTG + boolean "OTG Support" + depends on USB_GADGET_OMAP && ARCH_OMAP_OTG && USB_OHCI_HCD + help + The most notable feature of USB OTG is support for a + "Dual-Role" device, which can act as either a device + or a host. The initial role choice can be changed + later, when two dual-role devices talk to each other. + + Select this only if your OMAP board has a Mini-AB connector. + +config USB_OMAP_PROC + boolean "/proc/driver/udc file" + depends on USB_GADGET_OMAP + endchoice config USB_GADGET_DUALSPEED --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/usb/gadget/lh7a40x_udc.c 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,2168 @@ +/* + * linux/drivers/usb/gadget/lh7a40x_udc.c + * Sharp LH7A40x on-chip full speed USB device controllers + * + * Copyright (C) 2004 Mikko Lahteenmaki, Nordic ID + * Copyright (C) 2004 Bo Henriksen, Nordic ID + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "lh7a40x_udc.h" + +//#define DEBUG printk +//#define DEBUG_EP0 printk +//#define DEBUG_SETUP printk + +#ifndef DEBUG_EP0 +# define DEBUG_EP0(fmt,args...) +#endif +#ifndef DEBUG_SETUP +# define DEBUG_SETUP(fmt,args...) +#endif +#ifndef DEBUG +# define NO_STATES +# define DEBUG(fmt,args...) +#endif + +#define DRIVER_DESC "LH7A40x USB Device Controller" +#define DRIVER_VERSION __DATE__ + +#ifndef _BIT /* FIXME - what happended to _BIT in 2.6.7bk18? */ +#define _BIT(x) (1<<(x)) +#endif + +struct lh7a40x_udc *the_controller; + +static const char driver_name[] = "lh7a40x_udc"; +static const char driver_desc[] = DRIVER_DESC; +static const char ep0name[] = "ep0-control"; + +/* + Local definintions. +*/ +#define UDC_PROC_FILE + +#ifndef NO_STATES +static char *state_names[] = { + "WAIT_FOR_SETUP", + "DATA_STATE_XMIT", + "DATA_STATE_NEED_ZLP", + "WAIT_FOR_OUT_STATUS", + "DATA_STATE_RECV" +}; +#endif + +/* + Local declarations. +*/ +static int lh7a40x_ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *); +static int lh7a40x_ep_disable(struct usb_ep *ep); +static struct usb_request *lh7a40x_alloc_request(struct usb_ep *ep, int); +static void lh7a40x_free_request(struct usb_ep *ep, struct usb_request *); +static void *lh7a40x_alloc_buffer(struct usb_ep *ep, unsigned, dma_addr_t *, + int); +static void lh7a40x_free_buffer(struct usb_ep *ep, void *, dma_addr_t, + unsigned); +static int lh7a40x_queue(struct usb_ep *ep, struct usb_request *, int); +static int lh7a40x_dequeue(struct usb_ep *ep, struct usb_request *); +static int lh7a40x_set_halt(struct usb_ep *ep, int); +static int lh7a40x_fifo_status(struct usb_ep *ep); +static int lh7a40x_fifo_status(struct usb_ep *ep); +static void lh7a40x_fifo_flush(struct usb_ep *ep); +static void lh7a40x_ep0_kick(struct lh7a40x_udc *dev, struct lh7a40x_ep *ep); +static void lh7a40x_handle_ep0(struct lh7a40x_udc *dev, u32 intr); + +static void done(struct lh7a40x_ep *ep, struct lh7a40x_request *req, + int status); +static void pio_irq_enable(int bEndpointAddress); +static void pio_irq_disable(int bEndpointAddress); +static void stop_activity(struct lh7a40x_udc *dev, + struct usb_gadget_driver *driver); +static void flush(struct lh7a40x_ep *ep); +static void udc_enable(struct lh7a40x_udc *dev); +static void udc_set_address(struct lh7a40x_udc *dev, unsigned char address); + +static struct usb_ep_ops lh7a40x_ep_ops = { + .enable = lh7a40x_ep_enable, + .disable = lh7a40x_ep_disable, + + .alloc_request = lh7a40x_alloc_request, + .free_request = lh7a40x_free_request, + + .alloc_buffer = lh7a40x_alloc_buffer, + .free_buffer = lh7a40x_free_buffer, + + .queue = lh7a40x_queue, + .dequeue = lh7a40x_dequeue, + + .set_halt = lh7a40x_set_halt, + .fifo_status = lh7a40x_fifo_status, + .fifo_flush = lh7a40x_fifo_flush, +}; + +/* Inline code */ + +static __inline__ int write_packet(struct lh7a40x_ep *ep, + struct lh7a40x_request *req, int max) +{ + u8 *buf; + int length, count; + volatile u32 *fifo = (volatile u32 *)ep->fifo; + + buf = req->req.buf + req->req.actual; + prefetch(buf); + + length = req->req.length - req->req.actual; + length = min(length, max); + req->req.actual += length; + + DEBUG("Write %d (max %d), fifo %p\n", length, max, fifo); + + count = length; + while (count--) { + *fifo = *buf++; + } + + return length; +} + +static __inline__ void usb_set_index(u32 ep) +{ + *(volatile u32 *)io_p2v(USB_INDEX) = ep; +} + +static __inline__ u32 usb_read(u32 port) +{ + return *(volatile u32 *)io_p2v(port); +} + +static __inline__ void usb_write(u32 val, u32 port) +{ + *(volatile u32 *)io_p2v(port) = val; +} + +static __inline__ void usb_set(u32 val, u32 port) +{ + volatile u32 *ioport = (volatile u32 *)io_p2v(port); + u32 after = (*ioport) | val; + *ioport = after; +} + +static __inline__ void usb_clear(u32 val, u32 port) +{ + volatile u32 *ioport = (volatile u32 *)io_p2v(port); + u32 after = (*ioport) & ~val; + *ioport = after; +} + +/*-------------------------------------------------------------------------*/ + +#define GPIO_PORTC_DR (0x80000E08) +#define GPIO_PORTC_DDR (0x80000E18) +#define GPIO_PORTC_PDR (0x80000E70) + +/* get port C pin data register */ +#define get_portc_pdr(bit) ((usb_read(GPIO_PORTC_PDR) & _BIT(bit)) != 0) +/* get port C data direction register */ +#define get_portc_ddr(bit) ((usb_read(GPIO_PORTC_DDR) & _BIT(bit)) != 0) +/* set port C data register */ +#define set_portc_dr(bit, val) (val ? usb_set(_BIT(bit), GPIO_PORTC_DR) : usb_clear(_BIT(bit), GPIO_PORTC_DR)) +/* set port C data direction register */ +#define set_portc_ddr(bit, val) (val ? usb_set(_BIT(bit), GPIO_PORTC_DDR) : usb_clear(_BIT(bit), GPIO_PORTC_DDR)) + +/* + * LPD7A404 GPIO's: + * Port C bit 1 = USB Port 1 Power Enable + * Port C bit 2 = USB Port 1 Data Carrier Detect + */ +#define is_usb_connected() get_portc_pdr(2) + +#ifdef UDC_PROC_FILE + +static const char proc_node_name[] = "driver/udc"; + +static int +udc_proc_read(char *page, char **start, off_t off, int count, + int *eof, void *_dev) +{ + char *buf = page; + struct lh7a40x_udc *dev = _dev; + char *next = buf; + unsigned size = count; + unsigned long flags; + int t; + + if (off != 0) + return 0; + + local_irq_save(flags); + + /* basic device status */ + t = scnprintf(next, size, + DRIVER_DESC "\n" + "%s version: %s\n" + "Gadget driver: %s\n" + "Host: %s\n\n", + driver_name, DRIVER_VERSION, + dev->driver ? dev->driver->driver.name : "(none)", + is_usb_connected()? "full speed" : "disconnected"); + size -= t; + next += t; + + t = scnprintf(next, size, + "GPIO:\n" + " Port C bit 1: %d, dir %d\n" + " Port C bit 2: %d, dir %d\n\n", + get_portc_pdr(1), get_portc_ddr(1), + get_portc_pdr(2), get_portc_ddr(2) + ); + size -= t; + next += t; + + t = scnprintf(next, size, + "DCP pullup: %d\n\n", + (usb_read(USB_PM) & PM_USB_DCP) != 0); + size -= t; + next += t; + + local_irq_restore(flags); + *eof = 1; + return count - size; +} + +#define create_proc_files() create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev) +#define remove_proc_files() remove_proc_entry(proc_node_name, NULL) + +#else /* !UDC_PROC_FILE */ + +#define create_proc_files() do {} while (0) +#define remove_proc_files() do {} while (0) + +#endif /* UDC_PROC_FILE */ + +/* + * udc_disable - disable USB device controller + */ +static void udc_disable(struct lh7a40x_udc *dev) +{ + DEBUG("%s, %p\n", __FUNCTION__, dev); + + udc_set_address(dev, 0); + + /* Disable interrupts */ + usb_write(0, USB_IN_INT_EN); + usb_write(0, USB_OUT_INT_EN); + usb_write(0, USB_INT_EN); + + /* Disable the USB */ + usb_write(0, USB_PM); + +#ifdef CONFIG_ARCH_LH7A404 + /* Disable USB power */ + set_portc_dr(1, 0); +#endif + + /* if hardware supports it, disconnect from usb */ + /* make_usb_disappear(); */ + + dev->ep0state = WAIT_FOR_SETUP; + dev->gadget.speed = USB_SPEED_UNKNOWN; + dev->usb_address = 0; +} + +/* + * udc_reinit - initialize software state + */ +static void udc_reinit(struct lh7a40x_udc *dev) +{ + u32 i; + + DEBUG("%s, %p\n", __FUNCTION__, dev); + + /* device/ep0 records init */ + INIT_LIST_HEAD(&dev->gadget.ep_list); + INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); + dev->ep0state = WAIT_FOR_SETUP; + + /* basic endpoint records init */ + for (i = 0; i < UDC_MAX_ENDPOINTS; i++) { + struct lh7a40x_ep *ep = &dev->ep[i]; + + if (i != 0) + list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); + + ep->desc = 0; + ep->stopped = 0; + INIT_LIST_HEAD(&ep->queue); + ep->pio_irqs = 0; + } + + /* the rest was statically initialized, and is read-only */ +} + +#define BYTES2MAXP(x) (x / 8) +#define MAXP2BYTES(x) (x * 8) + +/* until it's enabled, this UDC should be completely invisible + * to any USB host. + */ +static void udc_enable(struct lh7a40x_udc *dev) +{ + int ep; + + DEBUG("%s, %p\n", __FUNCTION__, dev); + + dev->gadget.speed = USB_SPEED_UNKNOWN; + +#ifdef CONFIG_ARCH_LH7A404 + /* Set Port C bit 1 & 2 as output */ + set_portc_ddr(1, 1); + set_portc_ddr(2, 1); + + /* Enable USB power */ + set_portc_dr(1, 0); +#endif + + /* + * C.f Chapter 18.1.3.1 Initializing the USB + */ + + /* Disable the USB */ + usb_clear(PM_USB_ENABLE, USB_PM); + + /* Reset APB & I/O sides of the USB */ + usb_set(USB_RESET_APB | USB_RESET_IO, USB_RESET); + mdelay(5); + usb_clear(USB_RESET_APB | USB_RESET_IO, USB_RESET); + + /* Set MAXP values for each */ + for (ep = 0; ep < UDC_MAX_ENDPOINTS; ep++) { + struct lh7a40x_ep *ep_reg = &dev->ep[ep]; + u32 csr; + + usb_set_index(ep); + + switch (ep_reg->ep_type) { + case ep_bulk_in: + case ep_interrupt: + usb_clear(USB_IN_CSR2_USB_DMA_EN | USB_IN_CSR2_AUTO_SET, + ep_reg->csr2); + /* Fall through */ + case ep_control: + usb_write(BYTES2MAXP(ep_maxpacket(ep_reg)), + USB_IN_MAXP); + break; + case ep_bulk_out: + usb_clear(USB_OUT_CSR2_USB_DMA_EN | + USB_OUT_CSR2_AUTO_CLR, ep_reg->csr2); + usb_write(BYTES2MAXP(ep_maxpacket(ep_reg)), + USB_OUT_MAXP); + break; + } + + /* Read & Write CSR1, just in case */ + csr = usb_read(ep_reg->csr1); + usb_write(csr, ep_reg->csr1); + + flush(ep_reg); + } + + /* Disable interrupts */ + usb_write(0, USB_IN_INT_EN); + usb_write(0, USB_OUT_INT_EN); + usb_write(0, USB_INT_EN); + + /* Enable interrupts */ + usb_set(USB_IN_INT_EP0, USB_IN_INT_EN); + usb_set(USB_INT_RESET_INT | USB_INT_RESUME_INT, USB_INT_EN); + /* Dont enable rest of the interrupts */ + /* usb_set(USB_IN_INT_EP3 | USB_IN_INT_EP1 | USB_IN_INT_EP0, USB_IN_INT_EN); + usb_set(USB_OUT_INT_EP2, USB_OUT_INT_EN); */ + + /* Enable SUSPEND */ + usb_set(PM_ENABLE_SUSPEND, USB_PM); + + /* Enable the USB */ + usb_set(PM_USB_ENABLE, USB_PM); + +#ifdef CONFIG_ARCH_LH7A404 + /* NOTE: DOES NOT WORK! */ + /* Let host detect UDC: + * Software must write a 0 to the PMR:DCP_CTRL bit to turn this + * transistor on and pull the USBDP pin HIGH. + */ + /* usb_clear(PM_USB_DCP, USB_PM); + usb_set(PM_USB_DCP, USB_PM); */ +#endif +} + +/* + Register entry point for the peripheral controller driver. +*/ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct lh7a40x_udc *dev = the_controller; + int retval; + + DEBUG("%s: %s\n", __FUNCTION__, driver->driver.name); + + if (!driver + || driver->speed != USB_SPEED_FULL + || !driver->bind + || !driver->unbind || !driver->disconnect || !driver->setup) + return -EINVAL; + if (!dev) + return -ENODEV; + if (dev->driver) + return -EBUSY; + + /* first hook up the driver ... */ + dev->driver = driver; + dev->gadget.dev.driver = &driver->driver; + + device_add(&dev->gadget.dev); + retval = driver->bind(&dev->gadget); + if (retval) { + printk("%s: bind to driver %s --> error %d\n", dev->gadget.name, + driver->driver.name, retval); + device_del(&dev->gadget.dev); + + dev->driver = 0; + dev->gadget.dev.driver = 0; + return retval; + } + + /* ... then enable host detection and ep0; and we're ready + * for set_configuration as well as eventual disconnect. + * NOTE: this shouldn't power up until later. + */ + printk("%s: registered gadget driver '%s'\n", dev->gadget.name, + driver->driver.name); + + udc_enable(dev); + + return 0; +} + +EXPORT_SYMBOL(usb_gadget_register_driver); + +/* + Unregister entry point for the peripheral controller driver. +*/ +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct lh7a40x_udc *dev = the_controller; + unsigned long flags; + + if (!dev) + return -ENODEV; + if (!driver || driver != dev->driver) + return -EINVAL; + + spin_lock_irqsave(&dev->lock, flags); + dev->driver = 0; + stop_activity(dev, driver); + spin_unlock_irqrestore(&dev->lock, flags); + + driver->unbind(&dev->gadget); + device_del(&dev->gadget.dev); + + udc_disable(dev); + + DEBUG("unregistered gadget driver '%s'\n", driver->driver.name); + return 0; +} + +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +/*-------------------------------------------------------------------------*/ + +/** Write request to FIFO (max write == maxp size) + * Return: 0 = still running, 1 = completed, negative = errno + * NOTE: INDEX register must be set for EP + */ +static int write_fifo(struct lh7a40x_ep *ep, struct lh7a40x_request *req) +{ + u32 max; + u32 csr; + + max = le16_to_cpu(ep->desc->wMaxPacketSize); + + csr = usb_read(ep->csr1); + DEBUG("CSR: %x %d\n", csr, csr & USB_IN_CSR1_FIFO_NOT_EMPTY); + + if (!(csr & USB_IN_CSR1_FIFO_NOT_EMPTY)) { + unsigned count; + int is_last, is_short; + + count = write_packet(ep, req, max); + usb_set(USB_IN_CSR1_IN_PKT_RDY, ep->csr1); + + /* last packet is usually short (or a zlp) */ + if (unlikely(count != max)) + is_last = is_short = 1; + else { + if (likely(req->req.length != req->req.actual) + || req->req.zero) + is_last = 0; + else + is_last = 1; + /* interrupt/iso maxpacket may not fill the fifo */ + is_short = unlikely(max < ep_maxpacket(ep)); + } + + DEBUG("%s: wrote %s %d bytes%s%s %d left %p\n", __FUNCTION__, + ep->ep.name, count, + is_last ? "/L" : "", is_short ? "/S" : "", + req->req.length - req->req.actual, req); + + /* requests complete when all IN data is in the FIFO */ + if (is_last) { + done(ep, req, 0); + if (list_empty(&ep->queue)) { + pio_irq_disable(ep_index(ep)); + } + return 1; + } + } else { + DEBUG("Hmm.. %d ep FIFO is not empty!\n", ep_index(ep)); + } + + return 0; +} + +/** Read to request from FIFO (max read == bytes in fifo) + * Return: 0 = still running, 1 = completed, negative = errno + * NOTE: INDEX register must be set for EP + */ +static int read_fifo(struct lh7a40x_ep *ep, struct lh7a40x_request *req) +{ + u32 csr; + u8 *buf; + unsigned bufferspace, count, is_short; + volatile u32 *fifo = (volatile u32 *)ep->fifo; + + /* make sure there's a packet in the FIFO. */ + csr = usb_read(ep->csr1); + if (!(csr & USB_OUT_CSR1_OUT_PKT_RDY)) { + DEBUG("%s: Packet NOT ready!\n", __FUNCTION__); + return -EINVAL; + } + + buf = req->req.buf + req->req.actual; + prefetchw(buf); + bufferspace = req->req.length - req->req.actual; + + /* read all bytes from this packet */ + count = usb_read(USB_OUT_FIFO_WC1); + req->req.actual += min(count, bufferspace); + + is_short = (count < ep->ep.maxpacket); + DEBUG("read %s %02x, %d bytes%s req %p %d/%d\n", + ep->ep.name, csr, count, + is_short ? "/S" : "", req, req->req.actual, req->req.length); + + while (likely(count-- != 0)) { + u8 byte = (u8) (*fifo & 0xff); + + if (unlikely(bufferspace == 0)) { + /* this happens when the driver's buffer + * is smaller than what the host sent. + * discard the extra data. + */ + if (req->req.status != -EOVERFLOW) + printk("%s overflow %d\n", ep->ep.name, count); + req->req.status = -EOVERFLOW; + } else { + *buf++ = byte; + bufferspace--; + } + } + + usb_clear(USB_OUT_CSR1_OUT_PKT_RDY, ep->csr1); + + /* completion */ + if (is_short || req->req.actual == req->req.length) { + done(ep, req, 0); + usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1); + + if (list_empty(&ep->queue)) + pio_irq_disable(ep_index(ep)); + return 1; + } + + /* finished that packet. the next one may be waiting... */ + return 0; +} + +/* + * done - retire a request; caller blocked irqs + * INDEX register is preserved to keep same + */ +static void done(struct lh7a40x_ep *ep, struct lh7a40x_request *req, int status) +{ + unsigned int stopped = ep->stopped; + u32 index; + + DEBUG("%s, %p\n", __FUNCTION__, ep); + list_del_init(&req->queue); + + if (likely(req->req.status == -EINPROGRESS)) + req->req.status = status; + else + status = req->req.status; + + if (status && status != -ESHUTDOWN) + DEBUG("complete %s req %p stat %d len %u/%u\n", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + + /* don't modify queue heads during completion callback */ + ep->stopped = 1; + /* Read current index (completion may modify it) */ + index = usb_read(USB_INDEX); + + spin_unlock(&ep->dev->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&ep->dev->lock); + + /* Restore index */ + usb_set_index(index); + ep->stopped = stopped; +} + +/** Enable EP interrupt */ +static void pio_irq_enable(int ep) +{ + DEBUG("%s: %d\n", __FUNCTION__, ep); + + switch (ep) { + case 1: + usb_set(USB_IN_INT_EP1, USB_IN_INT_EN); + break; + case 2: + usb_set(USB_OUT_INT_EP2, USB_OUT_INT_EN); + break; + case 3: + usb_set(USB_IN_INT_EP3, USB_IN_INT_EN); + break; + default: + DEBUG("Unknown endpoint: %d\n", ep); + break; + } +} + +/** Disable EP interrupt */ +static void pio_irq_disable(int ep) +{ + DEBUG("%s: %d\n", __FUNCTION__, ep); + + switch (ep) { + case 1: + usb_clear(USB_IN_INT_EP1, USB_IN_INT_EN); + break; + case 2: + usb_clear(USB_OUT_INT_EP2, USB_OUT_INT_EN); + break; + case 3: + usb_clear(USB_IN_INT_EP3, USB_IN_INT_EN); + break; + default: + DEBUG("Unknown endpoint: %d\n", ep); + break; + } +} + +/* + * nuke - dequeue ALL requests + */ +void nuke(struct lh7a40x_ep *ep, int status) +{ + struct lh7a40x_request *req; + + DEBUG("%s, %p\n", __FUNCTION__, ep); + + /* Flush FIFO */ + flush(ep); + + /* called with irqs blocked */ + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct lh7a40x_request, queue); + done(ep, req, status); + } + + /* Disable IRQ if EP is enabled (has decriptor) */ + if (ep->desc) + pio_irq_disable(ep_index(ep)); +} + +/* +void nuke_all(struct lh7a40x_udc *dev) +{ + int n; + for(n=0; nep[n]; + usb_set_index(n); + nuke(ep, 0); + } +}*/ + +/* +static void flush_all(struct lh7a40x_udc *dev) +{ + int n; + for (n = 0; n < UDC_MAX_ENDPOINTS; n++) + { + struct lh7a40x_ep *ep = &dev->ep[n]; + flush(ep); + } +} +*/ + +/** Flush EP + * NOTE: INDEX register must be set before this call + */ +static void flush(struct lh7a40x_ep *ep) +{ + DEBUG("%s, %p\n", __FUNCTION__, ep); + + switch (ep->ep_type) { + case ep_control: + /* check, by implication c.f. 15.1.2.11 */ + break; + + case ep_bulk_in: + case ep_interrupt: + /* if(csr & USB_IN_CSR1_IN_PKT_RDY) */ + usb_set(USB_IN_CSR1_FIFO_FLUSH, ep->csr1); + break; + + case ep_bulk_out: + /* if(csr & USB_OUT_CSR1_OUT_PKT_RDY) */ + usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1); + break; + } +} + +/** + * lh7a40x_in_epn - handle IN interrupt + */ +static void lh7a40x_in_epn(struct lh7a40x_udc *dev, u32 ep_idx, u32 intr) +{ + u32 csr; + struct lh7a40x_ep *ep = &dev->ep[ep_idx]; + struct lh7a40x_request *req; + + usb_set_index(ep_idx); + + csr = usb_read(ep->csr1); + DEBUG("%s: %d, csr %x\n", __FUNCTION__, ep_idx, csr); + + if (csr & USB_IN_CSR1_SENT_STALL) { + DEBUG("USB_IN_CSR1_SENT_STALL\n"); + usb_set(USB_IN_CSR1_SENT_STALL /*|USB_IN_CSR1_SEND_STALL */ , + ep->csr1); + return; + } + + if (!ep->desc) { + DEBUG("%s: NO EP DESC\n", __FUNCTION__); + return; + } + + if (list_empty(&ep->queue)) + req = 0; + else + req = list_entry(ep->queue.next, struct lh7a40x_request, queue); + + DEBUG("req: %p\n", req); + + if (!req) + return; + + write_fifo(ep, req); +} + +/* ********************************************************************************************* */ +/* Bulk OUT (recv) + */ + +static void lh7a40x_out_epn(struct lh7a40x_udc *dev, u32 ep_idx, u32 intr) +{ + struct lh7a40x_ep *ep = &dev->ep[ep_idx]; + struct lh7a40x_request *req; + + DEBUG("%s: %d\n", __FUNCTION__, ep_idx); + + usb_set_index(ep_idx); + + if (ep->desc) { + u32 csr; + csr = usb_read(ep->csr1); + + while ((csr = + usb_read(ep-> + csr1)) & (USB_OUT_CSR1_OUT_PKT_RDY | + USB_OUT_CSR1_SENT_STALL)) { + DEBUG("%s: %x\n", __FUNCTION__, csr); + + if (csr & USB_OUT_CSR1_SENT_STALL) { + DEBUG("%s: stall sent, flush fifo\n", + __FUNCTION__); + /* usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1); */ + flush(ep); + } else if (csr & USB_OUT_CSR1_OUT_PKT_RDY) { + if (list_empty(&ep->queue)) + req = 0; + else + req = + list_entry(ep->queue.next, + struct lh7a40x_request, + queue); + + if (!req) { + printk("%s: NULL REQ %d\n", + __FUNCTION__, ep_idx); + flush(ep); + break; + } else { + read_fifo(ep, req); + } + } + + } + + } else { + /* Throw packet away.. */ + printk("%s: No descriptor?!?\n", __FUNCTION__); + flush(ep); + } +} + +static void stop_activity(struct lh7a40x_udc *dev, + struct usb_gadget_driver *driver) +{ + int i; + + /* don't disconnect drivers more than once */ + if (dev->gadget.speed == USB_SPEED_UNKNOWN) + driver = 0; + dev->gadget.speed = USB_SPEED_UNKNOWN; + + /* prevent new request submissions, kill any outstanding requests */ + for (i = 0; i < UDC_MAX_ENDPOINTS; i++) { + struct lh7a40x_ep *ep = &dev->ep[i]; + ep->stopped = 1; + + usb_set_index(i); + nuke(ep, -ESHUTDOWN); + } + + /* report disconnect; the driver is already quiesced */ + if (driver) { + spin_unlock(&dev->lock); + driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } + + /* re-init driver-visible data structures */ + udc_reinit(dev); +} + +/** Handle USB RESET interrupt + */ +static void lh7a40x_reset_intr(struct lh7a40x_udc *dev) +{ +#if 0 /* def CONFIG_ARCH_LH7A404 */ + /* Does not work always... */ + + DEBUG("%s: %d\n", __FUNCTION__, dev->usb_address); + + if (!dev->usb_address) { + /*usb_set(USB_RESET_IO, USB_RESET); + mdelay(5); + usb_clear(USB_RESET_IO, USB_RESET); */ + return; + } + /* Put the USB controller into reset. */ + usb_set(USB_RESET_IO, USB_RESET); + + /* Set Device ID to 0 */ + udc_set_address(dev, 0); + + /* Let PLL2 settle down */ + mdelay(5); + + /* Release the USB controller from reset */ + usb_clear(USB_RESET_IO, USB_RESET); + + /* Re-enable UDC */ + udc_enable(dev); + +#endif + dev->gadget.speed = USB_SPEED_FULL; +} + +/* + * lh7a40x usb client interrupt handler. + */ +static irqreturn_t lh7a40x_udc_irq(int irq, void *_dev, struct pt_regs *r) +{ + struct lh7a40x_udc *dev = _dev; + + DEBUG("\n\n"); + + spin_lock(&dev->lock); + + for (;;) { + u32 intr_in = usb_read(USB_IN_INT); + u32 intr_out = usb_read(USB_OUT_INT); + u32 intr_int = usb_read(USB_INT); + + /* Test also against enable bits.. (lh7a40x errata).. Sigh.. */ + u32 in_en = usb_read(USB_IN_INT_EN); + u32 out_en = usb_read(USB_OUT_INT_EN); + + if (!intr_out && !intr_in && !intr_int) + break; + + DEBUG("%s (on state %s)\n", __FUNCTION__, + state_names[dev->ep0state]); + DEBUG("intr_out = %x\n", intr_out); + DEBUG("intr_in = %x\n", intr_in); + DEBUG("intr_int = %x\n", intr_int); + + if (intr_in) { + usb_write(intr_in, USB_IN_INT); + + if ((intr_in & USB_IN_INT_EP1) + && (in_en & USB_IN_INT_EP1)) { + DEBUG("USB_IN_INT_EP1\n"); + lh7a40x_in_epn(dev, 1, intr_in); + } + if ((intr_in & USB_IN_INT_EP3) + && (in_en & USB_IN_INT_EP3)) { + DEBUG("USB_IN_INT_EP3\n"); + lh7a40x_in_epn(dev, 3, intr_in); + } + if (intr_in & USB_IN_INT_EP0) { + DEBUG("USB_IN_INT_EP0 (control)\n"); + lh7a40x_handle_ep0(dev, intr_in); + } + } + + if (intr_out) { + usb_write(intr_out, USB_OUT_INT); + + if ((intr_out & USB_OUT_INT_EP2) + && (out_en & USB_OUT_INT_EP2)) { + DEBUG("USB_OUT_INT_EP2\n"); + lh7a40x_out_epn(dev, 2, intr_out); + } + } + + if (intr_int) { + usb_write(intr_int, USB_INT); + + if (intr_int & USB_INT_RESET_INT) { + lh7a40x_reset_intr(dev); + } + + if (intr_int & USB_INT_RESUME_INT) { + DEBUG("USB resume\n"); + + if (dev->gadget.speed != USB_SPEED_UNKNOWN + && dev->driver + && dev->driver->resume + && is_usb_connected()) { + dev->driver->resume(&dev->gadget); + } + } + + if (intr_int & USB_INT_SUSPEND_INT) { + DEBUG("USB suspend%s\n", + is_usb_connected()? "" : "+disconnect"); + if (!is_usb_connected()) { + stop_activity(dev, dev->driver); + } else if (dev->gadget.speed != + USB_SPEED_UNKNOWN && dev->driver + && dev->driver->suspend) { + dev->driver->suspend(&dev->gadget); + } + } + + } + } + + spin_unlock(&dev->lock); + + return IRQ_HANDLED; +} + +static int lh7a40x_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct lh7a40x_ep *ep; + struct lh7a40x_udc *dev; + unsigned long flags; + + DEBUG("%s, %p\n", __FUNCTION__, _ep); + + ep = container_of(_ep, struct lh7a40x_ep, ep); + if (!_ep || !desc || ep->desc || _ep->name == ep0name + || desc->bDescriptorType != USB_DT_ENDPOINT + || ep->bEndpointAddress != desc->bEndpointAddress + || ep_maxpacket(ep) < le16_to_cpu(desc->wMaxPacketSize)) { + DEBUG("%s, bad ep or descriptor\n", __FUNCTION__); + return -EINVAL; + } + + /* xfer types must match, except that interrupt ~= bulk */ + if (ep->bmAttributes != desc->bmAttributes + && ep->bmAttributes != USB_ENDPOINT_XFER_BULK + && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { + DEBUG("%s, %s type mismatch\n", __FUNCTION__, _ep->name); + return -EINVAL; + } + + /* hardware _could_ do smaller, but driver doesn't */ + if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK + && le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(ep)) + || !desc->wMaxPacketSize) { + DEBUG("%s, bad %s maxpacket\n", __FUNCTION__, _ep->name); + return -ERANGE; + } + + dev = ep->dev; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { + DEBUG("%s, bogus device state\n", __FUNCTION__); + return -ESHUTDOWN; + } + + spin_lock_irqsave(&ep->dev->lock, flags); + + ep->stopped = 0; + ep->desc = desc; + ep->pio_irqs = 0; + ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + + /* Reset halt state (does flush) */ + lh7a40x_set_halt(_ep, 0); + + spin_unlock_irqrestore(&ep->dev->lock, flags); + + DEBUG("%s: enabled %s\n", __FUNCTION__, _ep->name); + return 0; +} + +/** Disable EP + * NOTE: Sets INDEX register + */ +static int lh7a40x_ep_disable(struct usb_ep *_ep) +{ + struct lh7a40x_ep *ep; + unsigned long flags; + + DEBUG("%s, %p\n", __FUNCTION__, _ep); + + ep = container_of(_ep, struct lh7a40x_ep, ep); + if (!_ep || !ep->desc) { + DEBUG("%s, %s not enabled\n", __FUNCTION__, + _ep ? ep->ep.name : NULL); + return -EINVAL; + } + + spin_lock_irqsave(&ep->dev->lock, flags); + + usb_set_index(ep_index(ep)); + + /* Nuke all pending requests (does flush) */ + nuke(ep, -ESHUTDOWN); + + /* Disable ep IRQ */ + pio_irq_disable(ep_index(ep)); + + ep->desc = 0; + ep->stopped = 1; + + spin_unlock_irqrestore(&ep->dev->lock, flags); + + DEBUG("%s: disabled %s\n", __FUNCTION__, _ep->name); + return 0; +} + +static struct usb_request *lh7a40x_alloc_request(struct usb_ep *ep, + int gfp_flags) +{ + struct lh7a40x_request *req; + + DEBUG("%s, %p\n", __FUNCTION__, ep); + + req = kmalloc(sizeof *req, gfp_flags); + if (!req) + return 0; + + memset(req, 0, sizeof *req); + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void lh7a40x_free_request(struct usb_ep *ep, struct usb_request *_req) +{ + struct lh7a40x_request *req; + + DEBUG("%s, %p\n", __FUNCTION__, ep); + + req = container_of(_req, struct lh7a40x_request, req); + WARN_ON(!list_empty(&req->queue)); + kfree(req); +} + +static void *lh7a40x_alloc_buffer(struct usb_ep *ep, unsigned bytes, + dma_addr_t * dma, int gfp_flags) +{ + char *retval; + + DEBUG("%s (%p, %d, %d)\n", __FUNCTION__, ep, bytes, gfp_flags); + + retval = kmalloc(bytes, gfp_flags & ~(__GFP_DMA | __GFP_HIGHMEM)); + if (retval) + *dma = virt_to_bus(retval); + return retval; +} + +static void lh7a40x_free_buffer(struct usb_ep *ep, void *buf, dma_addr_t dma, + unsigned bytes) +{ + DEBUG("%s, %p\n", __FUNCTION__, ep); + kfree(buf); +} + +/** Queue one request + * Kickstart transfer if needed + * NOTE: Sets INDEX register + */ +static int lh7a40x_queue(struct usb_ep *_ep, struct usb_request *_req, + int gfp_flags) +{ + struct lh7a40x_request *req; + struct lh7a40x_ep *ep; + struct lh7a40x_udc *dev; + unsigned long flags; + + DEBUG("\n\n\n%s, %p\n", __FUNCTION__, _ep); + + req = container_of(_req, struct lh7a40x_request, req); + if (unlikely + (!_req || !_req->complete || !_req->buf + || !list_empty(&req->queue))) { + DEBUG("%s, bad params\n", __FUNCTION__); + return -EINVAL; + } + + ep = container_of(_ep, struct lh7a40x_ep, ep); + if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { + DEBUG("%s, bad ep\n", __FUNCTION__); + return -EINVAL; + } + + dev = ep->dev; + if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) { + DEBUG("%s, bogus device state %p\n", __FUNCTION__, dev->driver); + return -ESHUTDOWN; + } + + DEBUG("%s queue req %p, len %d buf %p\n", _ep->name, _req, _req->length, + _req->buf); + + spin_lock_irqsave(&dev->lock, flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + /* kickstart this i/o queue? */ + DEBUG("Add to %d Q %d %d\n", ep_index(ep), list_empty(&ep->queue), + ep->stopped); + if (list_empty(&ep->queue) && likely(!ep->stopped)) { + u32 csr; + + if (unlikely(ep_index(ep) == 0)) { + /* EP0 */ + list_add_tail(&req->queue, &ep->queue); + lh7a40x_ep0_kick(dev, ep); + req = 0; + } else if (ep_is_in(ep)) { + /* EP1 & EP3 */ + usb_set_index(ep_index(ep)); + csr = usb_read(ep->csr1); + pio_irq_enable(ep_index(ep)); + if ((csr & USB_IN_CSR1_FIFO_NOT_EMPTY) == 0) { + if (write_fifo(ep, req) == 1) + req = 0; + } + } else { + /* EP2 */ + usb_set_index(ep_index(ep)); + csr = usb_read(ep->csr1); + pio_irq_enable(ep_index(ep)); + if (!(csr & USB_OUT_CSR1_FIFO_FULL)) { + if (read_fifo(ep, req) == 1) + req = 0; + } + } + } + + /* pio or dma irq handler advances the queue. */ + if (likely(req != 0)) + list_add_tail(&req->queue, &ep->queue); + + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +/* dequeue JUST ONE request */ +static int lh7a40x_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct lh7a40x_ep *ep; + struct lh7a40x_request *req; + unsigned long flags; + + DEBUG("%s, %p\n", __FUNCTION__, _ep); + + ep = container_of(_ep, struct lh7a40x_ep, ep); + if (!_ep || ep->ep.name == ep0name) + return -EINVAL; + + spin_lock_irqsave(&ep->dev->lock, flags); + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + spin_unlock_irqrestore(&ep->dev->lock, flags); + return -EINVAL; + } + + done(ep, req, -ECONNRESET); + + spin_unlock_irqrestore(&ep->dev->lock, flags); + return 0; +} + +/** Halt specific EP + * Return 0 if success + * NOTE: Sets INDEX register to EP ! + */ +static int lh7a40x_set_halt(struct usb_ep *_ep, int value) +{ + struct lh7a40x_ep *ep; + unsigned long flags; + + ep = container_of(_ep, struct lh7a40x_ep, ep); + if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { + DEBUG("%s, bad ep\n", __FUNCTION__); + return -EINVAL; + } + + usb_set_index(ep_index(ep)); + + DEBUG("%s, ep %d, val %d\n", __FUNCTION__, ep_index(ep), value); + + spin_lock_irqsave(&ep->dev->lock, flags); + + if (ep_index(ep) == 0) { + /* EP0 */ + usb_set(EP0_SEND_STALL, ep->csr1); + } else if (ep_is_in(ep)) { + u32 csr = usb_read(ep->csr1); + if (value && ((csr & USB_IN_CSR1_FIFO_NOT_EMPTY) + || !list_empty(&ep->queue))) { + /* + * Attempts to halt IN endpoints will fail (returning -EAGAIN) + * if any transfer requests are still queued, or if the controller + * FIFO still holds bytes that the host hasn’t collected. + */ + spin_unlock_irqrestore(&ep->dev->lock, flags); + DEBUG + ("Attempt to halt IN endpoint failed (returning -EAGAIN) %d %d\n", + (csr & USB_IN_CSR1_FIFO_NOT_EMPTY), + !list_empty(&ep->queue)); + return -EAGAIN; + } + flush(ep); + if (value) + usb_set(USB_IN_CSR1_SEND_STALL, ep->csr1); + else { + usb_clear(USB_IN_CSR1_SEND_STALL, ep->csr1); + usb_set(USB_IN_CSR1_CLR_DATA_TOGGLE, ep->csr1); + } + + } else { + + flush(ep); + if (value) + usb_set(USB_OUT_CSR1_SEND_STALL, ep->csr1); + else { + usb_clear(USB_OUT_CSR1_SEND_STALL, ep->csr1); + usb_set(USB_OUT_CSR1_CLR_DATA_REG, ep->csr1); + } + } + + if (value) { + ep->stopped = 1; + } else { + ep->stopped = 0; + } + + spin_unlock_irqrestore(&ep->dev->lock, flags); + + DEBUG("%s %s halted\n", _ep->name, value == 0 ? "NOT" : "IS"); + + return 0; +} + +/** Return bytes in EP FIFO + * NOTE: Sets INDEX register to EP + */ +static int lh7a40x_fifo_status(struct usb_ep *_ep) +{ + u32 csr; + int count = 0; + struct lh7a40x_ep *ep; + + ep = container_of(_ep, struct lh7a40x_ep, ep); + if (!_ep) { + DEBUG("%s, bad ep\n", __FUNCTION__); + return -ENODEV; + } + + DEBUG("%s, %d\n", __FUNCTION__, ep_index(ep)); + + /* LPD can't report unclaimed bytes from IN fifos */ + if (ep_is_in(ep)) + return -EOPNOTSUPP; + + usb_set_index(ep_index(ep)); + + csr = usb_read(ep->csr1); + if (ep->dev->gadget.speed != USB_SPEED_UNKNOWN || + csr & USB_OUT_CSR1_OUT_PKT_RDY) { + count = usb_read(USB_OUT_FIFO_WC1); + } + + return count; +} + +/** Flush EP FIFO + * NOTE: Sets INDEX register to EP + */ +static void lh7a40x_fifo_flush(struct usb_ep *_ep) +{ + struct lh7a40x_ep *ep; + + ep = container_of(_ep, struct lh7a40x_ep, ep); + if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { + DEBUG("%s, bad ep\n", __FUNCTION__); + return; + } + + usb_set_index(ep_index(ep)); + flush(ep); +} + +/****************************************************************/ +/* End Point 0 related functions */ +/****************************************************************/ + +/* return: 0 = still running, 1 = completed, negative = errno */ +static int write_fifo_ep0(struct lh7a40x_ep *ep, struct lh7a40x_request *req) +{ + u32 max; + unsigned count; + int is_last; + + max = ep_maxpacket(ep); + + DEBUG_EP0("%s\n", __FUNCTION__); + + count = write_packet(ep, req, max); + + /* last packet is usually short (or a zlp) */ + if (unlikely(count != max)) + is_last = 1; + else { + if (likely(req->req.length != req->req.actual) || req->req.zero) + is_last = 0; + else + is_last = 1; + } + + DEBUG_EP0("%s: wrote %s %d bytes%s %d left %p\n", __FUNCTION__, + ep->ep.name, count, + is_last ? "/L" : "", req->req.length - req->req.actual, req); + + /* requests complete when all IN data is in the FIFO */ + if (is_last) { + done(ep, req, 0); + return 1; + } + + return 0; +} + +static __inline__ int lh7a40x_fifo_read(struct lh7a40x_ep *ep, + unsigned char *cp, int max) +{ + int bytes; + int count = usb_read(USB_OUT_FIFO_WC1); + volatile u32 *fifo = (volatile u32 *)ep->fifo; + + if (count > max) + count = max; + bytes = count; + while (count--) + *cp++ = *fifo & 0xFF; + return bytes; +} + +static __inline__ void lh7a40x_fifo_write(struct lh7a40x_ep *ep, + unsigned char *cp, int count) +{ + volatile u32 *fifo = (volatile u32 *)ep->fifo; + DEBUG_EP0("fifo_write: %d %d\n", ep_index(ep), count); + while (count--) + *fifo = *cp++; +} + +static int read_fifo_ep0(struct lh7a40x_ep *ep, struct lh7a40x_request *req) +{ + u32 csr; + u8 *buf; + unsigned bufferspace, count, is_short; + volatile u32 *fifo = (volatile u32 *)ep->fifo; + + DEBUG_EP0("%s\n", __FUNCTION__); + + csr = usb_read(USB_EP0_CSR); + if (!(csr & USB_OUT_CSR1_OUT_PKT_RDY)) + return 0; + + buf = req->req.buf + req->req.actual; + prefetchw(buf); + bufferspace = req->req.length - req->req.actual; + + /* read all bytes from this packet */ + if (likely(csr & EP0_OUT_PKT_RDY)) { + count = usb_read(USB_OUT_FIFO_WC1); + req->req.actual += min(count, bufferspace); + } else /* zlp */ + count = 0; + + is_short = (count < ep->ep.maxpacket); + DEBUG_EP0("read %s %02x, %d bytes%s req %p %d/%d\n", + ep->ep.name, csr, count, + is_short ? "/S" : "", req, req->req.actual, req->req.length); + + while (likely(count-- != 0)) { + u8 byte = (u8) (*fifo & 0xff); + + if (unlikely(bufferspace == 0)) { + /* this happens when the driver's buffer + * is smaller than what the host sent. + * discard the extra data. + */ + if (req->req.status != -EOVERFLOW) + DEBUG_EP0("%s overflow %d\n", ep->ep.name, + count); + req->req.status = -EOVERFLOW; + } else { + *buf++ = byte; + bufferspace--; + } + } + + /* completion */ + if (is_short || req->req.actual == req->req.length) { + done(ep, req, 0); + return 1; + } + + /* finished that packet. the next one may be waiting... */ + return 0; +} + +/** + * udc_set_address - set the USB address for this device + * @address: + * + * Called from control endpoint function after it decodes a set address setup packet. + */ +static void udc_set_address(struct lh7a40x_udc *dev, unsigned char address) +{ + DEBUG_EP0("%s: %d\n", __FUNCTION__, address); + /* c.f. 15.1.2.2 Table 15-4 address will be used after DATA_END is set */ + dev->usb_address = address; + usb_set((address & USB_FA_FUNCTION_ADDR), USB_FA); + usb_set(USB_FA_ADDR_UPDATE | (address & USB_FA_FUNCTION_ADDR), USB_FA); + /* usb_read(USB_FA); */ +} + +/* + * DATA_STATE_RECV (OUT_PKT_RDY) + * - if error + * set EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL bits + * - else + * set EP0_CLR_OUT bit + if last set EP0_DATA_END bit + */ +static void lh7a40x_ep0_out(struct lh7a40x_udc *dev, u32 csr) +{ + struct lh7a40x_request *req; + struct lh7a40x_ep *ep = &dev->ep[0]; + int ret; + + DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); + + if (list_empty(&ep->queue)) + req = 0; + else + req = list_entry(ep->queue.next, struct lh7a40x_request, queue); + + if (req) { + + if (req->req.length == 0) { + DEBUG_EP0("ZERO LENGTH OUT!\n"); + usb_set((EP0_CLR_OUT | EP0_DATA_END), USB_EP0_CSR); + dev->ep0state = WAIT_FOR_SETUP; + return; + } + ret = read_fifo_ep0(ep, req); + if (ret) { + /* Done! */ + DEBUG_EP0("%s: finished, waiting for status\n", + __FUNCTION__); + + usb_set((EP0_CLR_OUT | EP0_DATA_END), USB_EP0_CSR); + dev->ep0state = WAIT_FOR_SETUP; + } else { + /* Not done yet.. */ + DEBUG_EP0("%s: not finished\n", __FUNCTION__); + usb_set(EP0_CLR_OUT, USB_EP0_CSR); + } + } else { + DEBUG_EP0("NO REQ??!\n"); + } +} + +/* + * DATA_STATE_XMIT + */ +static int lh7a40x_ep0_in(struct lh7a40x_udc *dev, u32 csr) +{ + struct lh7a40x_request *req; + struct lh7a40x_ep *ep = &dev->ep[0]; + int ret, need_zlp = 0; + + DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); + + if (list_empty(&ep->queue)) + req = 0; + else + req = list_entry(ep->queue.next, struct lh7a40x_request, queue); + + if (!req) { + DEBUG_EP0("%s: NULL REQ\n", __FUNCTION__); + return 0; + } + + if (req->req.length == 0) { + + usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR); + dev->ep0state = WAIT_FOR_SETUP; + return 1; + } + + if (req->req.length - req->req.actual == EP0_PACKETSIZE) { + /* Next write will end with the packet size, */ + /* so we need Zero-length-packet */ + need_zlp = 1; + } + + ret = write_fifo_ep0(ep, req); + + if (ret == 1 && !need_zlp) { + /* Last packet */ + DEBUG_EP0("%s: finished, waiting for status\n", __FUNCTION__); + + usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR); + dev->ep0state = WAIT_FOR_SETUP; + } else { + DEBUG_EP0("%s: not finished\n", __FUNCTION__); + usb_set(EP0_IN_PKT_RDY, USB_EP0_CSR); + } + + if (need_zlp) { + DEBUG_EP0("%s: Need ZLP!\n", __FUNCTION__); + usb_set(EP0_IN_PKT_RDY, USB_EP0_CSR); + dev->ep0state = DATA_STATE_NEED_ZLP; + } + + return 1; +} + +static int lh7a40x_handle_get_status(struct lh7a40x_udc *dev, + struct usb_ctrlrequest *ctrl) +{ + struct lh7a40x_ep *ep0 = &dev->ep[0]; + struct lh7a40x_ep *qep; + int reqtype = (ctrl->bRequestType & USB_RECIP_MASK); + u16 val = 0; + + if (reqtype == USB_RECIP_INTERFACE) { + /* This is not supported. + * And according to the USB spec, this one does nothing.. + * Just return 0 + */ + DEBUG_SETUP("GET_STATUS: USB_RECIP_INTERFACE\n"); + } else if (reqtype == USB_RECIP_DEVICE) { + DEBUG_SETUP("GET_STATUS: USB_RECIP_DEVICE\n"); + val |= (1 << 0); /* Self powered */ + /*val |= (1<<1); *//* Remote wakeup */ + } else if (reqtype == USB_RECIP_ENDPOINT) { + int ep_num = (ctrl->wIndex & ~USB_DIR_IN); + + DEBUG_SETUP + ("GET_STATUS: USB_RECIP_ENDPOINT (%d), ctrl->wLength = %d\n", + ep_num, ctrl->wLength); + + if (ctrl->wLength > 2 || ep_num > 3) + return -EOPNOTSUPP; + + qep = &dev->ep[ep_num]; + if (ep_is_in(qep) != ((ctrl->wIndex & USB_DIR_IN) ? 1 : 0) + && ep_index(qep) != 0) { + return -EOPNOTSUPP; + } + + usb_set_index(ep_index(qep)); + + /* Return status on next IN token */ + switch (qep->ep_type) { + case ep_control: + val = + (usb_read(qep->csr1) & EP0_SEND_STALL) == + EP0_SEND_STALL; + break; + case ep_bulk_in: + case ep_interrupt: + val = + (usb_read(qep->csr1) & USB_IN_CSR1_SEND_STALL) == + USB_IN_CSR1_SEND_STALL; + break; + case ep_bulk_out: + val = + (usb_read(qep->csr1) & USB_OUT_CSR1_SEND_STALL) == + USB_OUT_CSR1_SEND_STALL; + break; + } + + /* Back to EP0 index */ + usb_set_index(0); + + DEBUG_SETUP("GET_STATUS, ep: %d (%x), val = %d\n", ep_num, + ctrl->wIndex, val); + } else { + DEBUG_SETUP("Unknown REQ TYPE: %d\n", reqtype); + return -EOPNOTSUPP; + } + + /* Clear "out packet ready" */ + usb_set((EP0_CLR_OUT), USB_EP0_CSR); + /* Put status to FIFO */ + lh7a40x_fifo_write(ep0, (u8 *) & val, sizeof(val)); + /* Issue "In packet ready" */ + usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR); + + return 0; +} + +/* + * WAIT_FOR_SETUP (OUT_PKT_RDY) + * - read data packet from EP0 FIFO + * - decode command + * - if error + * set EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL bits + * - else + * set EP0_CLR_OUT | EP0_DATA_END bits + */ +static void lh7a40x_ep0_setup(struct lh7a40x_udc *dev, u32 csr) +{ + struct lh7a40x_ep *ep = &dev->ep[0]; + struct usb_ctrlrequest ctrl; + int i, bytes, is_in; + + DEBUG_SETUP("%s: %x\n", __FUNCTION__, csr); + + /* Nuke all previous transfers */ + nuke(ep, -EPROTO); + + /* read control req from fifo (8 bytes) */ + bytes = lh7a40x_fifo_read(ep, (unsigned char *)&ctrl, 8); + + DEBUG_SETUP("Read CTRL REQ %d bytes\n", bytes); + DEBUG_SETUP("CTRL.bRequestType = %d (is_in %d)\n", ctrl.bRequestType, + ctrl.bRequestType == USB_DIR_IN); + DEBUG_SETUP("CTRL.bRequest = %d\n", ctrl.bRequest); + DEBUG_SETUP("CTRL.wLength = %d\n", ctrl.wLength); + DEBUG_SETUP("CTRL.wValue = %d (%d)\n", ctrl.wValue, ctrl.wValue >> 8); + DEBUG_SETUP("CTRL.wIndex = %d\n", ctrl.wIndex); + + /* Set direction of EP0 */ + if (likely(ctrl.bRequestType & USB_DIR_IN)) { + ep->bEndpointAddress |= USB_DIR_IN; + is_in = 1; + } else { + ep->bEndpointAddress &= ~USB_DIR_IN; + is_in = 0; + } + + dev->req_pending = 1; + + /* Handle some SETUP packets ourselves */ + switch (ctrl.bRequest) { + case USB_REQ_SET_ADDRESS: + if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) + break; + + DEBUG_SETUP("USB_REQ_SET_ADDRESS (%d)\n", ctrl.wValue); + udc_set_address(dev, ctrl.wValue); + usb_set((EP0_CLR_OUT | EP0_DATA_END), USB_EP0_CSR); + return; + + case USB_REQ_GET_STATUS:{ + if (lh7a40x_handle_get_status(dev, &ctrl) == 0) + return; + + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + if (ctrl.bRequestType == USB_RECIP_ENDPOINT) { + struct lh7a40x_ep *qep; + int ep_num = (ctrl.wIndex & 0x0f); + + /* Support only HALT feature */ + if (ctrl.wValue != 0 || ctrl.wLength != 0 + || ep_num > 3 || ep_num < 1) + break; + + qep = &dev->ep[ep_num]; + if (ctrl.bRequest == USB_REQ_SET_FEATURE) { + DEBUG_SETUP("SET_FEATURE (%d)\n", + ep_num); + lh7a40x_set_halt(&qep->ep, 1); + } else { + DEBUG_SETUP("CLR_FEATURE (%d)\n", + ep_num); + lh7a40x_set_halt(&qep->ep, 0); + } + usb_set_index(0); + + /* Reply with a ZLP on next IN token */ + usb_set((EP0_CLR_OUT | EP0_DATA_END), + USB_EP0_CSR); + return; + } + break; + } + + default: + break; + } + + if (likely(dev->driver)) { + /* device-2-host (IN) or no data setup command, process immediately */ + spin_unlock(&dev->lock); + i = dev->driver->setup(&dev->gadget, &ctrl); + spin_lock(&dev->lock); + + if (i < 0) { + /* setup processing failed, force stall */ + DEBUG_SETUP + (" --> ERROR: gadget setup FAILED (stalling), setup returned %d\n", + i); + usb_set_index(0); + usb_set((EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL), + USB_EP0_CSR); + + /* ep->stopped = 1; */ + dev->ep0state = WAIT_FOR_SETUP; + } + } +} + +/* + * DATA_STATE_NEED_ZLP + */ +static void lh7a40x_ep0_in_zlp(struct lh7a40x_udc *dev, u32 csr) +{ + DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); + + /* c.f. Table 15-14 */ + usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR); + dev->ep0state = WAIT_FOR_SETUP; +} + +/* + * handle ep0 interrupt + */ +static void lh7a40x_handle_ep0(struct lh7a40x_udc *dev, u32 intr) +{ + struct lh7a40x_ep *ep = &dev->ep[0]; + u32 csr; + + /* Set index 0 */ + usb_set_index(0); + csr = usb_read(USB_EP0_CSR); + + DEBUG_EP0("%s: csr = %x\n", __FUNCTION__, csr); + + /* + * For overview of what we should be doing see c.f. Chapter 18.1.2.4 + * We will follow that outline here modified by our own global state + * indication which provides hints as to what we think should be + * happening.. + */ + + /* + * if SENT_STALL is set + * - clear the SENT_STALL bit + */ + if (csr & EP0_SENT_STALL) { + DEBUG_EP0("%s: EP0_SENT_STALL is set: %x\n", __FUNCTION__, csr); + usb_clear((EP0_SENT_STALL | EP0_SEND_STALL), USB_EP0_CSR); + nuke(ep, -ECONNABORTED); + dev->ep0state = WAIT_FOR_SETUP; + return; + } + + /* + * if a transfer is in progress && IN_PKT_RDY and OUT_PKT_RDY are clear + * - fill EP0 FIFO + * - if last packet + * - set IN_PKT_RDY | DATA_END + * - else + * set IN_PKT_RDY + */ + if (!(csr & (EP0_IN_PKT_RDY | EP0_OUT_PKT_RDY))) { + DEBUG_EP0("%s: IN_PKT_RDY and OUT_PKT_RDY are clear\n", + __FUNCTION__); + + switch (dev->ep0state) { + case DATA_STATE_XMIT: + DEBUG_EP0("continue with DATA_STATE_XMIT\n"); + lh7a40x_ep0_in(dev, csr); + return; + case DATA_STATE_NEED_ZLP: + DEBUG_EP0("continue with DATA_STATE_NEED_ZLP\n"); + lh7a40x_ep0_in_zlp(dev, csr); + return; + default: + /* Stall? */ + DEBUG_EP0("Odd state!! state = %s\n", + state_names[dev->ep0state]); + dev->ep0state = WAIT_FOR_SETUP; + /* nuke(ep, 0); */ + /* usb_set(EP0_SEND_STALL, ep->csr1); */ + break; + } + } + + /* + * if SETUP_END is set + * - abort the last transfer + * - set SERVICED_SETUP_END_BIT + */ + if (csr & EP0_SETUP_END) { + DEBUG_EP0("%s: EP0_SETUP_END is set: %x\n", __FUNCTION__, csr); + + usb_set(EP0_CLR_SETUP_END, USB_EP0_CSR); + + nuke(ep, 0); + dev->ep0state = WAIT_FOR_SETUP; + } + + /* + * if EP0_OUT_PKT_RDY is set + * - read data packet from EP0 FIFO + * - decode command + * - if error + * set SERVICED_OUT_PKT_RDY | DATA_END bits | SEND_STALL + * - else + * set SERVICED_OUT_PKT_RDY | DATA_END bits + */ + if (csr & EP0_OUT_PKT_RDY) { + + DEBUG_EP0("%s: EP0_OUT_PKT_RDY is set: %x\n", __FUNCTION__, + csr); + + switch (dev->ep0state) { + case WAIT_FOR_SETUP: + DEBUG_EP0("WAIT_FOR_SETUP\n"); + lh7a40x_ep0_setup(dev, csr); + break; + + case DATA_STATE_RECV: + DEBUG_EP0("DATA_STATE_RECV\n"); + lh7a40x_ep0_out(dev, csr); + break; + + default: + /* send stall? */ + DEBUG_EP0("strange state!! 2. send stall? state = %d\n", + dev->ep0state); + break; + } + } +} + +static void lh7a40x_ep0_kick(struct lh7a40x_udc *dev, struct lh7a40x_ep *ep) +{ + u32 csr; + + usb_set_index(0); + csr = usb_read(USB_EP0_CSR); + + DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); + + /* Clear "out packet ready" */ + usb_set(EP0_CLR_OUT, USB_EP0_CSR); + + if (ep_is_in(ep)) { + dev->ep0state = DATA_STATE_XMIT; + lh7a40x_ep0_in(dev, csr); + } else { + dev->ep0state = DATA_STATE_RECV; + lh7a40x_ep0_out(dev, csr); + } +} + +/* --------------------------------------------------------------------------- + * device-scoped parts of the api to the usb controller hardware + * --------------------------------------------------------------------------- + */ + +static int lh7a40x_udc_get_frame(struct usb_gadget *_gadget) +{ + u32 frame1 = usb_read(USB_FRM_NUM1); /* Least significant 8 bits */ + u32 frame2 = usb_read(USB_FRM_NUM2); /* Most significant 3 bits */ + DEBUG("%s, %p\n", __FUNCTION__, _gadget); + return ((frame2 & 0x07) << 8) | (frame1 & 0xff); +} + +static int lh7a40x_udc_wakeup(struct usb_gadget *_gadget) +{ + /* host may not have enabled remote wakeup */ + /*if ((UDCCS0 & UDCCS0_DRWF) == 0) + return -EHOSTUNREACH; + udc_set_mask_UDCCR(UDCCR_RSM); */ + return -ENOTSUPP; +} + +static const struct usb_gadget_ops lh7a40x_udc_ops = { + .get_frame = lh7a40x_udc_get_frame, + .wakeup = lh7a40x_udc_wakeup, + /* current versions must always be self-powered */ +}; + +static void nop_release(struct device *dev) +{ + DEBUG("%s %s\n", __FUNCTION__, dev->bus_id); +} + +static struct lh7a40x_udc memory = { + .usb_address = 0, + + .gadget = { + .ops = &lh7a40x_udc_ops, + .ep0 = &memory.ep[0].ep, + .name = driver_name, + .dev = { + .bus_id = "gadget", + .release = nop_release, + }, + }, + + /* control endpoint */ + .ep[0] = { + .ep = { + .name = ep0name, + .ops = &lh7a40x_ep_ops, + .maxpacket = EP0_PACKETSIZE, + }, + .dev = &memory, + + .bEndpointAddress = 0, + .bmAttributes = 0, + + .ep_type = ep_control, + .fifo = io_p2v(USB_EP0_FIFO), + .csr1 = USB_EP0_CSR, + .csr2 = USB_EP0_CSR, + }, + + /* first group of endpoints */ + .ep[1] = { + .ep = { + .name = "ep1in-bulk", + .ops = &lh7a40x_ep_ops, + .maxpacket = 64, + }, + .dev = &memory, + + .bEndpointAddress = USB_DIR_IN | 1, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + + .ep_type = ep_bulk_in, + .fifo = io_p2v(USB_EP1_FIFO), + .csr1 = USB_IN_CSR1, + .csr2 = USB_IN_CSR2, + }, + + .ep[2] = { + .ep = { + .name = "ep2out-bulk", + .ops = &lh7a40x_ep_ops, + .maxpacket = 64, + }, + .dev = &memory, + + .bEndpointAddress = 2, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + + .ep_type = ep_bulk_out, + .fifo = io_p2v(USB_EP2_FIFO), + .csr1 = USB_OUT_CSR1, + .csr2 = USB_OUT_CSR2, + }, + + .ep[3] = { + .ep = { + .name = "ep3in-int", + .ops = &lh7a40x_ep_ops, + .maxpacket = 64, + }, + .dev = &memory, + + .bEndpointAddress = USB_DIR_IN | 3, + .bmAttributes = USB_ENDPOINT_XFER_INT, + + .ep_type = ep_interrupt, + .fifo = io_p2v(USB_EP3_FIFO), + .csr1 = USB_IN_CSR1, + .csr2 = USB_IN_CSR2, + }, +}; + +/* + * probe - binds to the platform device + */ +static int lh7a40x_udc_probe(struct device *_dev) +{ + struct lh7a40x_udc *dev = &memory; + int retval; + + DEBUG("%s: %p\n", __FUNCTION__, _dev); + + spin_lock_init(&dev->lock); + dev->dev = _dev; + + device_initialize(&dev->gadget.dev); + dev->gadget.dev.parent = _dev; + + the_controller = dev; + dev_set_drvdata(_dev, dev); + + udc_disable(dev); + udc_reinit(dev); + + /* irq setup after old hardware state is cleaned up */ + retval = + request_irq(IRQ_USBINTR, lh7a40x_udc_irq, SA_INTERRUPT, driver_name, + dev); + if (retval != 0) { + DEBUG(KERN_ERR "%s: can't get irq %i, err %d\n", driver_name, + IRQ_USBINTR, retval); + return -EBUSY; + } + + create_proc_files(); + + return retval; +} + +static int lh7a40x_udc_remove(struct device *_dev) +{ + struct lh7a40x_udc *dev = _dev->driver_data; + + DEBUG("%s: %p\n", __FUNCTION__, dev); + + udc_disable(dev); + remove_proc_files(); + usb_gadget_unregister_driver(dev->driver); + + free_irq(IRQ_USBINTR, dev); + + dev_set_drvdata(_dev, 0); + + the_controller = 0; + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static struct device_driver udc_driver = { + .name = (char *)driver_name, + .bus = &platform_bus_type, + .probe = lh7a40x_udc_probe, + .remove = lh7a40x_udc_remove + /* FIXME power management support */ + /* .suspend = ... disable UDC */ + /* .resume = ... re-enable UDC */ +}; + +static int __init udc_init(void) +{ + DEBUG("%s: %s version %s\n", __FUNCTION__, driver_name, DRIVER_VERSION); + return driver_register(&udc_driver); +} + +static void __exit udc_exit(void) +{ + driver_unregister(&udc_driver); +} + +module_init(udc_init); +module_exit(udc_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Mikko Lahteenmaki, Bo Henriksen"); +MODULE_LICENSE("GPL"); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/usb/gadget/lh7a40x_udc.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,261 @@ +/* + * linux/drivers/usb/gadget/lh7a40x_udc.h + * Sharp LH7A40x on-chip full speed USB device controllers + * + * Copyright (C) 2004 Mikko Lahteenmaki, Nordic ID + * Copyright (C) 2004 Bo Henriksen, Nordic ID + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __LH7A40X_H_ +#define __LH7A40X_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * Memory map + */ + +#define USB_FA 0x80000200 // function address register +#define USB_PM 0x80000204 // power management register + +#define USB_IN_INT 0x80000208 // IN interrupt register bank (EP0-EP3) +#define USB_OUT_INT 0x80000210 // OUT interrupt register bank (EP2) +#define USB_INT 0x80000218 // interrupt register bank + +#define USB_IN_INT_EN 0x8000021C // IN interrupt enable register bank +#define USB_OUT_INT_EN 0x80000224 // OUT interrupt enable register bank +#define USB_INT_EN 0x8000022C // USB interrupt enable register bank + +#define USB_FRM_NUM1 0x80000230 // Frame number1 register +#define USB_FRM_NUM2 0x80000234 // Frame number2 register +#define USB_INDEX 0x80000238 // index register + +#define USB_IN_MAXP 0x80000240 // IN MAXP register +#define USB_IN_CSR1 0x80000244 // IN CSR1 register/EP0 CSR register +#define USB_EP0_CSR 0x80000244 // IN CSR1 register/EP0 CSR register +#define USB_IN_CSR2 0x80000248 // IN CSR2 register +#define USB_OUT_MAXP 0x8000024C // OUT MAXP register + +#define USB_OUT_CSR1 0x80000250 // OUT CSR1 register +#define USB_OUT_CSR2 0x80000254 // OUT CSR2 register +#define USB_OUT_FIFO_WC1 0x80000258 // OUT FIFO write count1 register +#define USB_OUT_FIFO_WC2 0x8000025C // OUT FIFO write count2 register + +#define USB_RESET 0x8000044C // USB reset register + +#define USB_EP0_FIFO 0x80000280 +#define USB_EP1_FIFO 0x80000284 +#define USB_EP2_FIFO 0x80000288 +#define USB_EP3_FIFO 0x8000028c + +/* + * USB reset register + */ +#define USB_RESET_APB (1<<1) //resets USB APB control side WRITE +#define USB_RESET_IO (1<<0) //resets USB IO side WRITE + +/* + * USB function address register + */ +#define USB_FA_ADDR_UPDATE (1<<7) +#define USB_FA_FUNCTION_ADDR (0x7F) + +/* + * Power Management register + */ +#define PM_USB_DCP (1<<5) +#define PM_USB_ENABLE (1<<4) +#define PM_USB_RESET (1<<3) +#define PM_UC_RESUME (1<<2) +#define PM_SUSPEND_MODE (1<<1) +#define PM_ENABLE_SUSPEND (1<<0) + +/* + * IN interrupt register + */ +#define USB_IN_INT_EP3 (1<<3) +#define USB_IN_INT_EP1 (1<<1) +#define USB_IN_INT_EP0 (1<<0) + +/* + * OUT interrupt register + */ +#define USB_OUT_INT_EP2 (1<<2) + +/* + * USB interrupt register + */ +#define USB_INT_RESET_INT (1<<2) +#define USB_INT_RESUME_INT (1<<1) +#define USB_INT_SUSPEND_INT (1<<0) + +/* + * USB interrupt enable register + */ +#define USB_INT_EN_USB_RESET_INTER (1<<2) +#define USB_INT_EN_RESUME_INTER (1<<1) +#define USB_INT_EN_SUSPEND_INTER (1<<0) + +/* + * INCSR1 register + */ +#define USB_IN_CSR1_CLR_DATA_TOGGLE (1<<6) +#define USB_IN_CSR1_SENT_STALL (1<<5) +#define USB_IN_CSR1_SEND_STALL (1<<4) +#define USB_IN_CSR1_FIFO_FLUSH (1<<3) +#define USB_IN_CSR1_FIFO_NOT_EMPTY (1<<1) +#define USB_IN_CSR1_IN_PKT_RDY (1<<0) + +/* + * INCSR2 register + */ +#define USB_IN_CSR2_AUTO_SET (1<<7) +#define USB_IN_CSR2_USB_DMA_EN (1<<4) + +/* + * OUT CSR1 register + */ +#define USB_OUT_CSR1_CLR_DATA_REG (1<<7) +#define USB_OUT_CSR1_SENT_STALL (1<<6) +#define USB_OUT_CSR1_SEND_STALL (1<<5) +#define USB_OUT_CSR1_FIFO_FLUSH (1<<4) +#define USB_OUT_CSR1_FIFO_FULL (1<<1) +#define USB_OUT_CSR1_OUT_PKT_RDY (1<<0) + +/* + * OUT CSR2 register + */ +#define USB_OUT_CSR2_AUTO_CLR (1<<7) +#define USB_OUT_CSR2_USB_DMA_EN (1<<4) + +/* + * EP0 CSR + */ +#define EP0_CLR_SETUP_END (1<<7) /* Clear "Setup Ends" Bit (w) */ +#define EP0_CLR_OUT (1<<6) /* Clear "Out packet ready" Bit (w) */ +#define EP0_SEND_STALL (1<<5) /* Send STALL Handshake (rw) */ +#define EP0_SETUP_END (1<<4) /* Setup Ends (r) */ + +#define EP0_DATA_END (1<<3) /* Data end (rw) */ +#define EP0_SENT_STALL (1<<2) /* Sent Stall Handshake (r) */ +#define EP0_IN_PKT_RDY (1<<1) /* In packet ready (rw) */ +#define EP0_OUT_PKT_RDY (1<<0) /* Out packet ready (r) */ + +/* general CSR */ +#define OUT_PKT_RDY (1<<0) +#define IN_PKT_RDY (1<<0) + +/* + * IN/OUT MAXP register + */ +#define USB_OUT_MAXP_MAXP (0xF) +#define USB_IN_MAXP_MAXP (0xF) + +// Max packet size +//#define EP0_PACKETSIZE 0x10 +#define EP0_PACKETSIZE 0x8 +#define EP0_MAXPACKETSIZE 0x10 + +#define UDC_MAX_ENDPOINTS 4 + +#define WAIT_FOR_SETUP 0 +#define DATA_STATE_XMIT 1 +#define DATA_STATE_NEED_ZLP 2 +#define WAIT_FOR_OUT_STATUS 3 +#define DATA_STATE_RECV 4 + +/* ********************************************************************************************* */ +/* IO + */ + +typedef enum ep_type { + ep_control, ep_bulk_in, ep_bulk_out, ep_interrupt +} ep_type_t; + +struct lh7a40x_ep { + struct usb_ep ep; + struct lh7a40x_udc *dev; + + const struct usb_endpoint_descriptor *desc; + struct list_head queue; + unsigned long pio_irqs; + + u8 stopped; + u8 bEndpointAddress; + u8 bmAttributes; + + ep_type_t ep_type; + u32 fifo; + u32 csr1; + u32 csr2; +}; + +struct lh7a40x_request { + struct usb_request req; + struct list_head queue; +}; + +struct lh7a40x_udc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct device *dev; + spinlock_t lock; + + int ep0state; + struct lh7a40x_ep ep[UDC_MAX_ENDPOINTS]; + + unsigned char usb_address; + + unsigned req_pending:1, req_std:1, req_config:1; +}; + +extern struct lh7a40x_udc *the_controller; + +#define ep_is_in(EP) (((EP)->bEndpointAddress&USB_DIR_IN)==USB_DIR_IN) +#define ep_index(EP) ((EP)->bEndpointAddress&0xF) +#define ep_maxpacket(EP) ((EP)->ep.maxpacket) + +#endif --- linux-2.6.9-rc1/drivers/usb/gadget/Makefile 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/usb/gadget/Makefile 2004-09-02 20:51:52.000000000 -0700 @@ -5,6 +5,8 @@ obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd obj-$(CONFIG_USB_NET2280) += net2280.o obj-$(CONFIG_USB_PXA2XX) += pxa2xx_udc.o obj-$(CONFIG_USB_GOKU) += goku_udc.o +obj-$(CONFIG_USB_OMAP) += omap_udc.o +obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o # # USB gadget drivers --- linux-2.6.9-rc1/drivers/usb/gadget/net2280.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/drivers/usb/gadget/net2280.c 2004-09-02 20:51:52.000000000 -0700 @@ -307,7 +307,6 @@ static void ep_reset (struct net2280_reg | (1 << SET_NAK_OUT_PACKETS) | (1 << CLEAR_EP_HIDE_STATUS_PHASE) | (1 << CLEAR_INTERRUPT_MODE) - | (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) | (1 << CLEAR_ENDPOINT_TOGGLE) | (1 << CLEAR_ENDPOINT_HALT) , &ep->regs->ep_rsp); @@ -2511,15 +2510,23 @@ next_endpoints: static void handle_stat1_irqs (struct net2280 *dev, u32 stat) { struct net2280_ep *ep; - u32 tmp, num, scratch; + u32 tmp, num, mask, scratch; /* after disconnect there's nothing else to do! */ tmp = (1 << VBUS_INTERRUPT) | (1 << ROOT_PORT_RESET_INTERRUPT); + mask = (1 << HIGH_SPEED) | (1 << FULL_SPEED); + + /* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set. + * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRRUPT set and + * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT + * only indicates a change in the reset state). + */ if (stat & tmp) { writel (tmp, &dev->regs->irqstat1); - if (((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) != 0 - || (readl (&dev->usb->usbctl) & (1 << VBUS_PIN)) == 0 - ) && dev->gadget.speed != USB_SPEED_UNKNOWN) { + if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) && + ((readl (&dev->usb->usbstat) & mask) == 0)) + || ((readl (&dev->usb->usbctl) & (1 << VBUS_PIN)) == 0) + ) && ( dev->gadget.speed != USB_SPEED_UNKNOWN)) { DEBUG (dev, "disconnect %s\n", dev->driver->driver.name); stop_activity (dev, dev->driver); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/usb/gadget/omap_udc.c 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,2695 @@ +/* + * omap_udc.c -- for OMAP 1610 udc, with OTG support + * + * Copyright (C) 2004 Texas Instruments, Inc. + * Copyright (C) 2004 David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#undef DEBUG +#undef VERBOSE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "omap_udc.h" + +#undef USB_TRACE + +/* OUT-dma seems to be behaving */ +#define USE_DMA + +/* ISO too */ +#define USE_ISO + + +#define DRIVER_DESC "OMAP UDC driver" +#define DRIVER_VERSION "24 August 2004" + +#define DMA_ADDR_INVALID (~(dma_addr_t)0) + + +/* + * The OMAP UDC needs _very_ early endpoint setup: before enabling the + * D+ pullup to allow enumeration. That's too early for the gadget + * framework to use from usb_endpoint_enable(), which happens after + * enumeration as part of activating an interface. (But if we add an + * optional new "UDC not yet running" state to the gadget driver model, + * even just during driver binding, the endpoint autoconfig logic is the + * natural spot to manufacture new endpoints.) + * + * So instead of using endpoint enable calls to control the hardware setup, + * this driver defines a "fifo mode" parameter. It's used during driver + * initialization to choose among a set of pre-defined endpoint configs. + * See omap_udc_setup() for available modes, or to add others. That code + * lives in an init section, so use this driver as a module if you need + * to change the fifo mode after the kernel boots. + * + * Gadget drivers normally ignore endpoints they don't care about, and + * won't include them in configuration descriptors. That means only + * misbehaving hosts would even notice they exist. + */ +#ifdef USE_ISO +static unsigned fifo_mode = 3; +#else +static unsigned fifo_mode = 0; +#endif + +/* "modprobe omap_udc fifo_mode=42", or else as a kernel + * boot parameter "omap_udc:fifo_mode=42" + */ +module_param (fifo_mode, uint, 0); +MODULE_PARM_DESC (fifo_mode, "endpoint setup (0 == default)"); + + +#ifdef USE_DMA +static unsigned use_dma = 1; + +/* "modprobe omap_udc use_dma=y", or else as a kernel + * boot parameter "omap_udc:use_dma=y" + */ +module_param (use_dma, bool, 0); +MODULE_PARM_DESC (use_dma, "enable/disable DMA"); +#else /* !USE_DMA */ + +/* save a bit of code */ +#define use_dma 0 +#endif /* !USE_DMA */ + + +static const char driver_name [] = "omap_udc"; +static const char driver_desc [] = DRIVER_DESC; + +/*-------------------------------------------------------------------------*/ + +/* there's a notion of "current endpoint" for modifying endpoint + * state, and PIO access to its FIFO. + */ + +static void use_ep(struct omap_ep *ep, u16 select) +{ + u16 num = ep->bEndpointAddress & 0x0f; + + if (ep->bEndpointAddress & USB_DIR_IN) + num |= UDC_EP_DIR; + UDC_EP_NUM_REG = num | select; + /* when select, MUST deselect later !! */ +} + +static inline void deselect_ep(void) +{ + UDC_EP_NUM_REG &= ~UDC_EP_SEL; + /* 6 wait states before TX will happen */ +} + +static void dma_channel_claim(struct omap_ep *ep, unsigned preferred); + +/*-------------------------------------------------------------------------*/ + +static int omap_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct omap_ep *ep = container_of(_ep, struct omap_ep, ep); + struct omap_udc *udc; + unsigned long flags; + u16 maxp; + + /* catch various bogus parameters */ + if (!_ep || !desc || ep->desc + || desc->bDescriptorType != USB_DT_ENDPOINT + || ep->bEndpointAddress != desc->bEndpointAddress + || ep->maxpacket < le16_to_cpu + (desc->wMaxPacketSize)) { + DBG("%s, bad ep or descriptor\n", __FUNCTION__); + return -EINVAL; + } + maxp = le16_to_cpu (desc->wMaxPacketSize); + if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK + && maxp != ep->maxpacket) + || desc->wMaxPacketSize > ep->maxpacket + || !desc->wMaxPacketSize) { + DBG("%s, bad %s maxpacket\n", __FUNCTION__, _ep->name); + return -ERANGE; + } + +#ifdef USE_ISO + if ((desc->bmAttributes == USB_ENDPOINT_XFER_ISOC + && desc->bInterval != 1)) { + /* hardware wants period = 1; USB allows 2^(Interval-1) */ + DBG("%s, unsupported ISO period %dms\n", _ep->name, + 1 << (desc->bInterval - 1)); + return -EDOM; + } +#else + if (desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + DBG("%s, ISO nyet\n", _ep->name); + return -EDOM; + } +#endif + + /* xfer types must match, except that interrupt ~= bulk */ + if (ep->bmAttributes != desc->bmAttributes + && ep->bmAttributes != USB_ENDPOINT_XFER_BULK + && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { + DBG("%s, %s type mismatch\n", __FUNCTION__, _ep->name); + return -EINVAL; + } + + udc = ep->udc; + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) { + DBG("%s, bogus device state\n", __FUNCTION__); + return -ESHUTDOWN; + } + + spin_lock_irqsave(&udc->lock, flags); + + ep->desc = desc; + ep->irqs = 0; + ep->stopped = 0; + ep->ep.maxpacket = maxp; + + /* set endpoint to initial state */ + ep->dma_channel = 0; + ep->has_dma = 0; + ep->lch = -1; + use_ep(ep, UDC_EP_SEL); + UDC_CTRL_REG = UDC_RESET_EP; + ep->ackwait = 0; + deselect_ep(); + + if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) + list_add(&ep->iso, &udc->iso); + + /* maybe assign a DMA channel to this endpoint */ + if (use_dma && desc->bmAttributes == USB_ENDPOINT_XFER_BULK + && !(ep->bEndpointAddress & USB_DIR_IN)) + /* FIXME ISO can dma, but prefers first channel. + * IN can dma, but lacks debugging. + */ + dma_channel_claim(ep, 0); + + /* PIO OUT may RX packets */ + if (desc->bmAttributes != USB_ENDPOINT_XFER_ISOC + && !ep->has_dma + && !(ep->bEndpointAddress & USB_DIR_IN)) + UDC_CTRL_REG = UDC_SET_FIFO_EN; + + spin_unlock_irqrestore(&udc->lock, flags); + VDBG("%s enabled\n", _ep->name); + return 0; +} + +static void nuke(struct omap_ep *, int status); + +static int omap_ep_disable(struct usb_ep *_ep) +{ + struct omap_ep *ep = container_of(_ep, struct omap_ep, ep); + unsigned long flags; + + if (!_ep || !ep->desc) { + DBG("%s, %s not enabled\n", __FUNCTION__, + _ep ? ep->ep.name : NULL); + return -EINVAL; + } + + spin_lock_irqsave(&ep->udc->lock, flags); + ep->desc = 0; + nuke (ep, -ESHUTDOWN); + ep->ep.maxpacket = ep->maxpacket; + ep->has_dma = 0; + UDC_CTRL_REG = UDC_SET_HALT; + list_del_init(&ep->iso); + + spin_unlock_irqrestore(&ep->udc->lock, flags); + + VDBG("%s disabled\n", _ep->name); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static struct usb_request * +omap_alloc_request(struct usb_ep *ep, int gfp_flags) +{ + struct omap_req *req; + + req = kmalloc(sizeof *req, gfp_flags); + if (req) { + memset (req, 0, sizeof *req); + req->req.dma = DMA_ADDR_INVALID; + INIT_LIST_HEAD (&req->queue); + } + return &req->req; +} + +static void +omap_free_request(struct usb_ep *ep, struct usb_request *_req) +{ + struct omap_req *req = container_of(_req, struct omap_req, req); + + if (_req) + kfree (req); +} + +/*-------------------------------------------------------------------------*/ + +static void * +omap_alloc_buffer( + struct usb_ep *_ep, + unsigned bytes, + dma_addr_t *dma, + int gfp_flags +) +{ + void *retval; + struct omap_ep *ep; + + ep = container_of(_ep, struct omap_ep, ep); + if (use_dma && ep->has_dma) { + static int warned; + if (!warned && bytes < PAGE_SIZE) { + dev_warn(ep->udc->gadget.dev.parent, + "using dma_alloc_coherent for " + "small allocations wastes memory\n"); + warned++; + } + return dma_alloc_coherent(ep->udc->gadget.dev.parent, + bytes, dma, gfp_flags); + } + + retval = kmalloc(bytes, gfp_flags); + if (retval) + *dma = virt_to_phys(retval); + return retval; +} + +static void omap_free_buffer( + struct usb_ep *_ep, + void *buf, + dma_addr_t dma, + unsigned bytes +) +{ + struct omap_ep *ep; + + ep = container_of(_ep, struct omap_ep, ep); + if (use_dma && _ep && ep->has_dma) + dma_free_coherent(ep->udc->gadget.dev.parent, bytes, buf, dma); + else + kfree (buf); +} + +/*-------------------------------------------------------------------------*/ + +static void +done(struct omap_ep *ep, struct omap_req *req, int status) +{ + unsigned stopped = ep->stopped; + + list_del_init(&req->queue); + + if (req->req.status == -EINPROGRESS) + req->req.status = status; + else + status = req->req.status; + + if (use_dma && ep->has_dma) { + if (req->mapped) { + dma_unmap_single(ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + (ep->bEndpointAddress & USB_DIR_IN) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + req->req.dma = DMA_ADDR_INVALID; + req->mapped = 0; + } else + dma_sync_single_for_cpu(ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + (ep->bEndpointAddress & USB_DIR_IN) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + } + +#ifndef USB_TRACE + if (status && status != -ESHUTDOWN) +#endif + VDBG("complete %s req %p stat %d len %u/%u\n", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + + /* don't modify queue heads during completion callback */ + ep->stopped = 1; + spin_unlock(&ep->udc->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&ep->udc->lock); + ep->stopped = stopped; +} + +/*-------------------------------------------------------------------------*/ + +#define FIFO_FULL (UDC_NON_ISO_FIFO_FULL | UDC_ISO_FIFO_FULL) +#define FIFO_UNWRITABLE (UDC_EP_HALTED | FIFO_FULL) + +#define FIFO_EMPTY (UDC_NON_ISO_FIFO_EMPTY | UDC_ISO_FIFO_EMPTY) +#define FIFO_UNREADABLE (UDC_EP_HALTED | FIFO_EMPTY) + +static inline int +write_packet(u8 *buf, struct omap_req *req, unsigned max) +{ + unsigned len; + u16 *wp; + + len = min(req->req.length - req->req.actual, max); + req->req.actual += len; + + max = len; + if (likely((((int)buf) & 1) == 0)) { + wp = (u16 *)buf; + while (max >= 2) { + UDC_DATA_REG = *wp++; + max -= 2; + } + buf = (u8 *)wp; + } + while (max--) + *(volatile u8 *)&UDC_DATA_REG = *buf++; + return len; +} + +// FIXME change r/w fifo calling convention + + +// return: 0 = still running, 1 = completed, negative = errno +static int write_fifo(struct omap_ep *ep, struct omap_req *req) +{ + u8 *buf; + unsigned count; + int is_last; + u16 ep_stat; + + buf = req->req.buf + req->req.actual; + prefetch(buf); + + /* PIO-IN isn't double buffered except for iso */ + ep_stat = UDC_STAT_FLG_REG; + if (ep_stat & FIFO_UNWRITABLE) + return 0; + + count = ep->ep.maxpacket; + count = write_packet(buf, req, count); + UDC_CTRL_REG = UDC_SET_FIFO_EN; + ep->ackwait = 1; + + /* last packet is often short (sometimes a zlp) */ + if (count != ep->ep.maxpacket) + is_last = 1; + else if (req->req.length == req->req.actual + && !req->req.zero) + is_last = 1; + else + is_last = 0; + + /* NOTE: requests complete when all IN data is in a + * FIFO (or sometimes later, if a zlp was needed). + * Use usb_ep_fifo_status() where needed. + */ + if (is_last) + done(ep, req, 0); + return is_last; +} + +static inline int +read_packet(u8 *buf, struct omap_req *req, unsigned avail) +{ + unsigned len; + u16 *wp; + + len = min(req->req.length - req->req.actual, avail); + req->req.actual += len; + avail = len; + + if (likely((((int)buf) & 1) == 0)) { + wp = (u16 *)buf; + while (avail >= 2) { + *wp++ = UDC_DATA_REG; + avail -= 2; + } + buf = (u8 *)wp; + } + while (avail--) + *buf++ = *(volatile u8 *)&UDC_DATA_REG; + return len; +} + +// return: 0 = still running, 1 = queue empty, negative = errno +static int read_fifo(struct omap_ep *ep, struct omap_req *req) +{ + u8 *buf; + unsigned count, avail; + int is_last; + + buf = req->req.buf + req->req.actual; + prefetchw(buf); + + for (;;) { + u16 ep_stat = UDC_STAT_FLG_REG; + + is_last = 0; + if (ep_stat & FIFO_UNREADABLE) + break; + + if (ep_stat & (UDC_NON_ISO_FIFO_FULL|UDC_ISO_FIFO_FULL)) + avail = ep->ep.maxpacket; + else + avail = UDC_RXFSTAT_REG; + count = read_packet(buf, req, avail); + + // FIXME double buffered PIO OUT wasn't behaving... + + /* partial packet reads may not be errors */ + if (count < ep->ep.maxpacket) { + is_last = 1; + /* overflowed this request? flush extra data */ + if (count != avail) { + req->req.status = -EOVERFLOW; + avail -= count; + while (avail--) + (void) *(volatile u8 *)&UDC_DATA_REG; + } + } else if (req->req.length == req->req.actual) + is_last = 1; + else + is_last = 0; + + if (!ep->bEndpointAddress) + break; + if (!ep->double_buf) { + UDC_CTRL_REG = UDC_SET_FIFO_EN; + if (!is_last) + break; + } + + if (is_last) { + done(ep, req, 0); + if (list_empty(&ep->queue) || !ep->double_buf) + break; + req = container_of(ep->queue.next, + struct omap_req, queue); + is_last = 0; + } + } + return is_last; +} + +/*-------------------------------------------------------------------------*/ + +/* Each USB transfer request using DMA maps to one or more DMA transfers. + * When DMA completion isn't request completion, the UDC continues with + * the next DMA transfer for that USB transfer. + */ + +static void next_in_dma(struct omap_ep *ep, struct omap_req *req) +{ + u16 txdma_ctrl; + unsigned length = req->req.length - req->req.actual; + + /* measure length in either bytes or packets */ + if (length <= (UDC_TXN_TSC + 1)) { + txdma_ctrl = UDC_TXN_EOT | length; + omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, + length, 1, OMAP_DMA_SYNC_ELEMENT); + } else { + length = max(length / ep->maxpacket, + (unsigned) UDC_TXN_TSC + 1); + txdma_ctrl = length; + omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, + ep->ep.maxpacket, length, + OMAP_DMA_SYNC_ELEMENT); + length *= ep->maxpacket; + } + + omap_set_dma_src_params(ep->lch, OMAP_DMA_PORT_EMIFF, + OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual); + + omap_start_dma(ep->lch); + UDC_DMA_IRQ_EN_REG |= UDC_TX_DONE_IE(ep->dma_channel); + UDC_TXDMA_REG(ep->dma_channel) = UDC_TXN_START | txdma_ctrl; + req->dma_bytes = length; +} + +static void finish_in_dma(struct omap_ep *ep, struct omap_req *req, int status) +{ + if (status == 0) { + req->req.actual += req->dma_bytes; + + /* return if this request needs to send data or zlp */ + if (req->req.actual < req->req.length) + return; + if (req->req.zero + && req->dma_bytes != 0 + && (req->req.actual % ep->maxpacket) == 0) + return; + } else { + u32 last; + + // FIXME this surely isn't #bytes transferred + last = (omap_readw(OMAP_DMA_CSSA_U(ep->lch)) << 16) + | omap_readw(OMAP_DMA_CSSA_L(ep->lch)); + req->req.actual = last - req->req.dma; + } + + /* tx completion */ + omap_stop_dma(ep->lch); + UDC_DMA_IRQ_EN_REG &= ~UDC_TX_DONE_IE(ep->dma_channel); + done(ep, req, status); +} + +static void next_out_dma(struct omap_ep *ep, struct omap_req *req) +{ + unsigned packets; + + /* NOTE: we filtered out "short reads" before, so we know + * the buffer has only whole numbers of packets. + */ + + /* set up this DMA transfer, enable the fifo, start */ + packets = (req->req.length - req->req.actual) / ep->ep.maxpacket; + packets = min(packets, (unsigned)UDC_RXN_TC + 1); + req->dma_bytes = packets * ep->ep.maxpacket; + omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, + ep->ep.maxpacket, packets, + OMAP_DMA_SYNC_ELEMENT); + omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_EMIFF, + OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual); + + UDC_RXDMA_REG(ep->dma_channel) = UDC_RXN_STOP | (packets - 1); + UDC_DMA_IRQ_EN_REG |= UDC_RX_EOT_IE(ep->dma_channel); + UDC_EP_NUM_REG = (ep->bEndpointAddress & 0xf); + UDC_CTRL_REG = UDC_SET_FIFO_EN; + + omap_start_dma(ep->lch); +} + +static void +finish_out_dma(struct omap_ep *ep, struct omap_req *req, int status) +{ + u16 count; + + /* FIXME must be a better way to see how much dma + * happened, even when it never got going... + */ + count = omap_readw(OMAP_DMA_CDAC(ep->lch)); + count -= 0xffff & (req->req.dma + req->req.actual); + count += req->req.actual; + if (count <= req->req.length) + req->req.actual = count; + + if (count != req->dma_bytes || status) + omap_stop_dma(ep->lch); + + /* if this wasn't short, request may need another transfer */ + else if (req->req.actual < req->req.length) + return; + + /* rx completion */ + UDC_DMA_IRQ_EN_REG &= ~UDC_RX_EOT_IE(ep->dma_channel); + done(ep, req, status); +} + +static void dma_irq(struct omap_udc *udc, u16 irq_src) +{ + u16 dman_stat = UDC_DMAN_STAT_REG; + struct omap_ep *ep; + struct omap_req *req; + + /* IN dma: tx to host */ + if (irq_src & UDC_TXN_DONE) { + ep = &udc->ep[16 + UDC_DMA_TX_SRC(dman_stat)]; + ep->irqs++; + /* can see TXN_DONE after dma abort */ + if (!list_empty(&ep->queue)) { + req = container_of(ep->queue.next, + struct omap_req, queue); + finish_in_dma(ep, req, 0); + } + UDC_IRQ_SRC_REG = UDC_TXN_DONE; + + if (!list_empty (&ep->queue)) { + req = container_of(ep->queue.next, + struct omap_req, queue); + next_in_dma(ep, req); + } + } + + /* OUT dma: rx from host */ + if (irq_src & UDC_RXN_EOT) { + ep = &udc->ep[UDC_DMA_RX_SRC(dman_stat)]; + ep->irqs++; + /* can see RXN_EOT after dma abort */ + if (!list_empty(&ep->queue)) { + req = container_of(ep->queue.next, + struct omap_req, queue); + finish_out_dma(ep, req, 0); + } + UDC_IRQ_SRC_REG = UDC_RXN_EOT; + + if (!list_empty (&ep->queue)) { + req = container_of(ep->queue.next, + struct omap_req, queue); + next_out_dma(ep, req); + } + } + + if (irq_src & UDC_RXN_CNT) { + ep = &udc->ep[UDC_DMA_RX_SRC(dman_stat)]; + DBG("%s, RX_CNT irq?\n", ep->ep.name); + UDC_IRQ_SRC_REG = UDC_RXN_CNT; + } +} + +static void dma_error(int lch, u16 ch_status, void *data) +{ + struct omap_ep *ep = data; + + /* if ch_status & OMAP_DMA_DROP_IRQ ... */ + /* if ch_status & OMAP_DMA_TOUT_IRQ ... */ + ERR("%s dma error, lch %d status %02x\n", ep->ep.name, lch, ch_status); + + /* complete current transfer ... */ +} + +static void dma_channel_claim(struct omap_ep *ep, unsigned channel) +{ + u16 reg; + int status, restart, is_in; + + is_in = ep->bEndpointAddress & USB_DIR_IN; + if (is_in) + reg = UDC_TXDMA_CFG_REG; + else + reg = UDC_RXDMA_CFG_REG; + reg |= 1 << 12; /* "pulse" activated */ + + ep->dma_channel = 0; + ep->lch = -1; + if (channel == 0 || channel > 3) { + if ((reg & 0x0f00) == 0) + channel = 3; + else if ((reg & 0x00f0) == 0) + channel = 2; + else if ((reg & 0x000f) == 0) /* preferred for ISO */ + channel = 1; + else { + status = -EMLINK; + goto just_restart; + } + } + reg |= (0x0f & ep->bEndpointAddress) << (4 * (channel - 1)); + ep->dma_channel = channel; + + if (is_in) { + status = omap_request_dma(OMAP_DMA_USB_W2FC_TX0 - 1 + channel, + ep->ep.name, dma_error, ep, &ep->lch); + if (status == 0) { + UDC_TXDMA_CFG_REG = reg; + omap_set_dma_dest_params(ep->lch, + OMAP_DMA_PORT_TIPB, + OMAP_DMA_AMODE_CONSTANT, + (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG)); + } + } else { + status = omap_request_dma(OMAP_DMA_USB_W2FC_RX0 - 1 + channel, + ep->ep.name, dma_error, ep, &ep->lch); + if (status == 0) { + UDC_RXDMA_CFG_REG = reg; + omap_set_dma_src_params(ep->lch, + OMAP_DMA_PORT_TIPB, + OMAP_DMA_AMODE_CONSTANT, + (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG)); + } + } + if (status) + ep->dma_channel = 0; + else { + ep->has_dma = 1; + omap_disable_dma_irq(ep->lch, OMAP_DMA_BLOCK_IRQ); + + /* channel type P: hw synch (fifo) */ + omap_writew(2, OMAP_DMA_LCH_CTRL(ep->lch)); + } + +just_restart: + /* restart any queue, even if the claim failed */ + restart = !ep->stopped && !list_empty(&ep->queue); + + if (status) + DBG("%s no dma channel: %d%s\n", ep->ep.name, status, + restart ? " (restart)" : ""); + else + DBG("%s claimed %cxdma%d lch %d%s\n", ep->ep.name, + is_in ? 't' : 'r', + ep->dma_channel - 1, ep->lch, + restart ? " (restart)" : ""); + + if (restart) { + struct omap_req *req; + req = container_of(ep->queue.next, struct omap_req, queue); + if (ep->has_dma) + (is_in ? next_in_dma : next_out_dma)(ep, req); + else { + use_ep(ep, UDC_EP_SEL); + (is_in ? write_fifo : read_fifo)(ep, req); + deselect_ep(); + /* IN: 6 wait states before it'll tx */ + } + } +} + +static void dma_channel_release(struct omap_ep *ep) +{ + int shift = 4 * (ep->dma_channel - 1); + u16 mask = 0x0f << shift; + struct omap_req *req; + int active; + + /* abort any active usb transfer request */ + if (!list_empty(&ep->queue)) + req = container_of(ep->queue.next, struct omap_req, queue); + else + req = 0; + + active = ((1 << 7) & omap_readl(OMAP_DMA_CCR(ep->lch))) != 0; + + DBG("%s release %s %cxdma%d %p\n", ep->ep.name, + active ? "active" : "idle", + (ep->bEndpointAddress & USB_DIR_IN) ? 't' : 'r', + ep->dma_channel - 1, req); + + /* wait till current packet DMA finishes, and fifo empties */ + if (ep->bEndpointAddress & USB_DIR_IN) { + UDC_TXDMA_CFG_REG &= ~mask; + + if (req) { + if (active) + udelay(50); + finish_in_dma(ep, req, -ECONNRESET); + if (UDC_TXDMA_CFG_REG & mask) + WARN("%s, SPIN abort TX dma\n", ep->ep.name); + } + + /* host may empty the fifo (or not...) */ + while (UDC_TXDMA_CFG_REG & mask) + udelay(10); + + } else { + UDC_RXDMA_CFG_REG &= ~mask; + + /* dma empties the fifo */ + while (active && (UDC_RXDMA_CFG_REG & mask)) + udelay(10); + omap_stop_dma(ep->lch); + if (req) + finish_out_dma(ep, req, -ECONNRESET); + } + omap_free_dma(ep->lch); + ep->dma_channel = 0; + ep->lch = -1; + /* has_dma still set, till endpoint is fully quiesced */ +} + + +/*-------------------------------------------------------------------------*/ + +static int +omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags) +{ + struct omap_ep *ep = container_of(_ep, struct omap_ep, ep); + struct omap_req *req = container_of(_req, struct omap_req, req); + struct omap_udc *udc; + unsigned long flags; + int is_iso = 0; + + /* catch various bogus parameters */ + if (!_req || !req->req.complete || !req->req.buf + || !list_empty(&req->queue)) { + DBG("%s, bad params\n", __FUNCTION__); + return -EINVAL; + } + if (!_ep || (!ep->desc && ep->bEndpointAddress)) { + DBG("%s, bad ep\n", __FUNCTION__); + return -EINVAL; + } + if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + if (req->req.length > ep->ep.maxpacket) + return -EMSGSIZE; + is_iso = 1; + } + + /* this isn't bogus, but OMAP DMA isn't the only hardware to + * have a hard time with partial packet reads... reject it. + */ + if (use_dma + && ep->has_dma + && ep->bEndpointAddress != 0 + && (ep->bEndpointAddress & USB_DIR_IN) == 0 + && (req->req.length % ep->ep.maxpacket) != 0) { + DBG("%s, no partial packet OUT reads\n", __FUNCTION__); + return -EMSGSIZE; + } + + udc = ep->udc; + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + if (use_dma && ep->has_dma) { + if (req->req.dma == DMA_ADDR_INVALID) { + req->req.dma = dma_map_single( + ep->udc->gadget.dev.parent, + req->req.buf, + req->req.length, + (ep->bEndpointAddress & USB_DIR_IN) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + req->mapped = 1; + } else { + dma_sync_single_for_device( + ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + (ep->bEndpointAddress & USB_DIR_IN) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + req->mapped = 0; + } + } + + VDBG("%s queue req %p, len %d buf %p\n", + ep->ep.name, _req, _req->length, _req->buf); + + spin_lock_irqsave(&udc->lock, flags); + + req->req.status = -EINPROGRESS; + req->req.actual = 0; + + /* maybe kickstart non-iso i/o queues */ + if (is_iso) + UDC_IRQ_EN_REG |= UDC_SOF_IE; + else if (list_empty(&ep->queue) && !ep->stopped && !ep->ackwait) { + int is_in; + + if (ep->bEndpointAddress == 0) { + if (!udc->ep0_pending || !list_empty (&ep->queue)) { + spin_unlock_irqrestore(&udc->lock, flags); + return -EL2HLT; + } + + /* empty DATA stage? */ + is_in = udc->ep0_in; + if (!req->req.length) { + + /* chip became CONFIGURED or ADDRESSED + * earlier; drivers may already have queued + * requests to non-control endpoints + */ + if (udc->ep0_set_config) { + u16 irq_en = UDC_IRQ_EN_REG; + + irq_en |= UDC_DS_CHG_IE | UDC_EP0_IE; + if (!udc->ep0_reset_config) + irq_en |= UDC_EPN_RX_IE + | UDC_EPN_TX_IE; + UDC_IRQ_EN_REG = irq_en; + } + + /* STATUS is reverse direction */ + UDC_EP_NUM_REG = is_in + ? UDC_EP_SEL + : (UDC_EP_SEL|UDC_EP_DIR); + UDC_CTRL_REG = UDC_CLR_EP; + UDC_CTRL_REG = UDC_SET_FIFO_EN; + UDC_EP_NUM_REG = udc->ep0_in ? 0 : UDC_EP_DIR; + + /* cleanup */ + udc->ep0_pending = 0; + done(ep, req, 0); + req = 0; + + /* non-empty DATA stage */ + } else if (is_in) { + UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; + } else { + if (udc->ep0_setup) + goto irq_wait; + UDC_EP_NUM_REG = UDC_EP_SEL; + } + } else { + is_in = ep->bEndpointAddress & USB_DIR_IN; + if (!ep->has_dma) + use_ep(ep, UDC_EP_SEL); + /* if ISO: SOF IRQs must be enabled/disabled! */ + } + + if (ep->has_dma) + (is_in ? next_in_dma : next_out_dma)(ep, req); + else if (req) { + if ((is_in ? write_fifo : read_fifo)(ep, req) == 1) + req = 0; + deselect_ep(); + /* IN: 6 wait states before it'll tx */ + } + } + +irq_wait: + /* irq handler advances the queue */ + if (req != 0) + list_add_tail(&req->queue, &ep->queue); + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static int omap_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct omap_ep *ep = container_of(_ep, struct omap_ep, ep); + struct omap_req *req; + unsigned long flags; + + if (!_ep || !_req) + return -EINVAL; + + spin_lock_irqsave(&ep->udc->lock, flags); + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry (req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + spin_unlock_irqrestore(&ep->udc->lock, flags); + return -EINVAL; + } + + if (use_dma && ep->dma_channel && ep->queue.next == &req->queue) { + int channel = ep->dma_channel; + + /* releasing the dma completion cancels the request, + * reclaiming the channel restarts the queue + */ + dma_channel_release(ep); + dma_channel_claim(ep, channel); + } else + done(ep, req, -ECONNRESET); + spin_unlock_irqrestore(&ep->udc->lock, flags); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int omap_ep_set_halt(struct usb_ep *_ep, int value) +{ + struct omap_ep *ep = container_of(_ep, struct omap_ep, ep); + unsigned long flags; + int status = -EOPNOTSUPP; + + spin_lock_irqsave(&ep->udc->lock, flags); + + /* just use protocol stalls for ep0; real halts are annoying */ + if (ep->bEndpointAddress == 0) { + if (!ep->udc->ep0_pending) + status = -EINVAL; + else if (value) { + if (ep->udc->ep0_set_config) { + WARN("error changing config?\n"); + UDC_SYSCON2_REG = UDC_CLR_CFG; + } + UDC_SYSCON2_REG = UDC_STALL_CMD; + ep->udc->ep0_pending = 0; + status = 0; + } else /* NOP */ + status = 0; + + /* otherwise, all active non-ISO endpoints can halt */ + } else if (ep->bmAttributes != USB_ENDPOINT_XFER_ISOC && ep->desc) { + + /* IN endpoints must already be idle */ + if ((ep->bEndpointAddress & USB_DIR_IN) + && !list_empty(&ep->queue)) { + status = -EAGAIN; + goto done; + } + + if (value) { + int channel; + + if (use_dma && ep->dma_channel + && !list_empty(&ep->queue)) { + channel = ep->dma_channel; + dma_channel_release(ep); + } else + channel = 0; + + use_ep(ep, UDC_EP_SEL); + if (UDC_STAT_FLG_REG & UDC_NON_ISO_FIFO_EMPTY) { + UDC_CTRL_REG = UDC_SET_HALT; + status = 0; + } else + status = -EAGAIN; + deselect_ep(); + + if (channel) + dma_channel_claim(ep, channel); + } else { + use_ep(ep, 0); + UDC_CTRL_REG = UDC_RESET_EP; + ep->ackwait = 0; + if (!(ep->bEndpointAddress & USB_DIR_IN)) + UDC_CTRL_REG = UDC_SET_FIFO_EN; + } + } +done: + VDBG("%s %s halt stat %d\n", ep->ep.name, + value ? "set" : "clear", status); + + spin_unlock_irqrestore(&ep->udc->lock, flags); + return status; +} + +static struct usb_ep_ops omap_ep_ops = { + .enable = omap_ep_enable, + .disable = omap_ep_disable, + + .alloc_request = omap_alloc_request, + .free_request = omap_free_request, + + .alloc_buffer = omap_alloc_buffer, + .free_buffer = omap_free_buffer, + + .queue = omap_ep_queue, + .dequeue = omap_ep_dequeue, + + .set_halt = omap_ep_set_halt, + // fifo_status ... report bytes in fifo + // fifo_flush ... flush fifo +}; + +/*-------------------------------------------------------------------------*/ + +static int omap_get_frame(struct usb_gadget *gadget) +{ + u16 sof = UDC_SOF_REG; + return (sof & UDC_TS_OK) ? (sof & UDC_TS) : -EL2NSYNC; +} + +static int omap_wakeup(struct usb_gadget *gadget) +{ + struct omap_udc *udc; + unsigned long flags; + int retval = -EHOSTUNREACH; + + udc = container_of(gadget, struct omap_udc, gadget); + + spin_lock_irqsave(&udc->lock, flags); + if (udc->devstat & UDC_SUS) { + /* NOTE: OTG spec erratum says that OTG devices may + * issue wakeups without host enable. + */ + if (udc->devstat & (UDC_B_HNP_ENABLE|UDC_R_WK_OK)) { + DBG("remote wakeup...\n"); + UDC_SYSCON2_REG = UDC_RMT_WKP; + retval = 0; + } + + /* NOTE: non-OTG systems may use SRP TOO... */ + } else if (!(udc->devstat & UDC_ATT)) { + if (udc->transceiver) + retval = otg_start_srp(udc->transceiver); + } + spin_unlock_irqrestore(&udc->lock, flags); + + return retval; +} + +static int +omap_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered) +{ + struct omap_udc *udc; + unsigned long flags; + u16 syscon1; + + udc = container_of(gadget, struct omap_udc, gadget); + spin_lock_irqsave(&udc->lock, flags); + syscon1 = UDC_SYSCON1_REG; + if (is_selfpowered) + syscon1 |= UDC_SELF_PWR; + else + syscon1 &= ~UDC_SELF_PWR; + UDC_SYSCON1_REG = syscon1; + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static int can_pullup(struct omap_udc *udc) +{ + return udc->driver && udc->softconnect && udc->vbus_active; +} + +static void pullup_enable(struct omap_udc *udc) +{ + UDC_SYSCON1_REG |= UDC_PULLUP_EN; +#ifndef CONFIG_USB_OTG + OTG_CTRL_REG |= OTG_BSESSVLD; +#endif + UDC_IRQ_EN_REG = UDC_DS_CHG_IE; +} + +static void pullup_disable(struct omap_udc *udc) +{ +#ifndef CONFIG_USB_OTG + OTG_CTRL_REG &= ~OTG_BSESSVLD; +#endif + UDC_IRQ_EN_REG = UDC_DS_CHG_IE; + UDC_SYSCON1_REG &= ~UDC_PULLUP_EN; +} + +/* + * Called by whatever detects VBUS sessions: external transceiver + * driver, or maybe GPIO0 VBUS IRQ. + */ +static int omap_vbus_session(struct usb_gadget *gadget, int is_active) +{ + struct omap_udc *udc; + unsigned long flags; + + udc = container_of(gadget, struct omap_udc, gadget); + spin_lock_irqsave(&udc->lock, flags); + VDBG("VBUS %s\n", is_active ? "on" : "off"); + udc->vbus_active = (is_active != 0); + if (can_pullup(udc)) + pullup_enable(udc); + else + pullup_disable(udc); + spin_unlock_irqrestore(&udc->lock, flags); + return 0; +} + +static int omap_vbus_draw(struct usb_gadget *gadget, unsigned mA) +{ + struct omap_udc *udc; + + udc = container_of(gadget, struct omap_udc, gadget); + if (udc->transceiver) + return otg_set_power(udc->transceiver, mA); + return -EOPNOTSUPP; +} + +static int omap_pullup(struct usb_gadget *gadget, int is_on) +{ + struct omap_udc *udc; + unsigned long flags; + + udc = container_of(gadget, struct omap_udc, gadget); + spin_lock_irqsave(&udc->lock, flags); + udc->softconnect = (is_on != 0); + if (can_pullup(udc)) + pullup_enable(udc); + else + pullup_disable(udc); + spin_unlock_irqrestore(&udc->lock, flags); + return 0; +} + +static struct usb_gadget_ops omap_gadget_ops = { + .get_frame = omap_get_frame, + .wakeup = omap_wakeup, + .set_selfpowered = omap_set_selfpowered, + .vbus_session = omap_vbus_session, + .vbus_draw = omap_vbus_draw, + .pullup = omap_pullup, +}; + +/*-------------------------------------------------------------------------*/ + +/* dequeue ALL requests; caller holds udc->lock */ +static void nuke(struct omap_ep *ep, int status) +{ + struct omap_req *req; + + ep->stopped = 1; + + if (use_dma && ep->dma_channel) + dma_channel_release(ep); + + use_ep(ep, 0); + UDC_CTRL_REG = UDC_CLR_EP; + if (ep->bEndpointAddress && ep->bmAttributes != USB_ENDPOINT_XFER_ISOC) + UDC_CTRL_REG = UDC_SET_HALT; + + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct omap_req, queue); + done(ep, req, status); + } +} + +/* caller holds udc->lock */ +static void udc_quiesce(struct omap_udc *udc) +{ + struct omap_ep *ep; + + udc->gadget.speed = USB_SPEED_UNKNOWN; + nuke(&udc->ep[0], -ESHUTDOWN); + list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list) + nuke(ep, -ESHUTDOWN); +} + +/*-------------------------------------------------------------------------*/ + +static void update_otg(struct omap_udc *udc) +{ + u16 devstat; + + if (!udc->gadget.is_otg) + return; + + if (OTG_CTRL_REG & OTG_ID) + devstat = UDC_DEVSTAT_REG; + else + devstat = 0; + + udc->gadget.b_hnp_enable = !!(devstat & UDC_B_HNP_ENABLE); + udc->gadget.a_hnp_support = !!(devstat & UDC_A_HNP_SUPPORT); + udc->gadget.a_alt_hnp_support = !!(devstat & UDC_A_ALT_HNP_SUPPORT); + + /* Enable HNP early, avoiding races on suspend irq path. + * ASSUMES OTG state machine B_BUS_REQ input is true. + */ + if (udc->gadget.b_hnp_enable) + OTG_CTRL_REG = (OTG_CTRL_REG | OTG_B_HNPEN | OTG_B_BUSREQ) + & ~OTG_PULLUP; +} + +static void ep0_irq(struct omap_udc *udc, u16 irq_src) +{ + struct omap_ep *ep0 = &udc->ep[0]; + struct omap_req *req = 0; + + ep0->irqs++; + + /* Clear any pending requests and then scrub any rx/tx state + * before starting to handle the SETUP request. + */ + if (irq_src & UDC_SETUP) + nuke(ep0, 0); + + /* IN/OUT packets mean we're in the DATA or STATUS stage. + * This driver uses only uses protocol stalls (ep0 never halts), + * and if we got this far the gadget driver already had a + * chance to stall. Tries to be forgiving of host oddities. + * + * NOTE: the last chance gadget drivers have to stall control + * requests is during their request completion callback. + */ + if (!list_empty(&ep0->queue)) + req = container_of(ep0->queue.next, struct omap_req, queue); + + /* IN == TX to host */ + if (irq_src & UDC_EP0_TX) { + int stat; + + UDC_IRQ_SRC_REG = UDC_EP0_TX; + UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; + stat = UDC_STAT_FLG_REG; + if (stat & UDC_ACK) { + if (udc->ep0_in) { + /* write next IN packet from response, + * or set up the status stage. + */ + if (req) + stat = write_fifo(ep0, req); + UDC_EP_NUM_REG = UDC_EP_DIR; + if (!req && udc->ep0_pending) { + UDC_EP_NUM_REG = UDC_EP_SEL; + UDC_CTRL_REG = UDC_CLR_EP; + UDC_CTRL_REG = UDC_SET_FIFO_EN; + UDC_EP_NUM_REG = 0; + udc->ep0_pending = 0; + } /* else: 6 wait states before it'll tx */ + } else { + /* ack status stage of OUT transfer */ + UDC_EP_NUM_REG = UDC_EP_DIR; + if (req) + done(ep0, req, 0); + } + req = 0; + } else if (stat & UDC_STALL) { + UDC_CTRL_REG = UDC_CLR_HALT; + UDC_EP_NUM_REG = UDC_EP_DIR; + } else { + UDC_EP_NUM_REG = UDC_EP_DIR; + } + } + + /* OUT == RX from host */ + if (irq_src & UDC_EP0_RX) { + int stat; + + UDC_IRQ_SRC_REG = UDC_EP0_RX; + UDC_EP_NUM_REG = UDC_EP_SEL; + stat = UDC_STAT_FLG_REG; + if (stat & UDC_ACK) { + if (!udc->ep0_in) { + stat = 0; + /* read next OUT packet of request, maybe + * reactiviting the fifo; stall on errors. + */ + if (!req || (stat = read_fifo(ep0, req)) < 0) { + UDC_SYSCON2_REG = UDC_STALL_CMD; + udc->ep0_pending = 0; + stat = 0; + } else if (stat == 0) + UDC_CTRL_REG = UDC_SET_FIFO_EN; + UDC_EP_NUM_REG = 0; + + /* activate status stage */ + if (stat == 1) { + done(ep0, req, 0); + /* that may have STALLed ep0... */ + UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; + UDC_CTRL_REG = UDC_CLR_EP; + UDC_CTRL_REG = UDC_SET_FIFO_EN; + UDC_EP_NUM_REG = UDC_EP_DIR; + udc->ep0_pending = 0; + } + } else { + /* ack status stage of IN transfer */ + UDC_EP_NUM_REG = 0; + if (req) + done(ep0, req, 0); + } + } else if (stat & UDC_STALL) { + UDC_CTRL_REG = UDC_CLR_HALT; + UDC_EP_NUM_REG = 0; + } else { + UDC_EP_NUM_REG = 0; + } + } + + /* SETUP starts all control transfers */ + if (irq_src & UDC_SETUP) { + union u { + u16 word[4]; + struct usb_ctrlrequest r; + } u; + int status = -EINVAL; + struct omap_ep *ep; + + /* read the (latest) SETUP message */ + do { + UDC_EP_NUM_REG = UDC_SETUP_SEL; + /* two bytes at a time */ + u.word[0] = UDC_DATA_REG; + u.word[1] = UDC_DATA_REG; + u.word[2] = UDC_DATA_REG; + u.word[3] = UDC_DATA_REG; + UDC_EP_NUM_REG = 0; + } while (UDC_IRQ_SRC_REG & UDC_SETUP); + le16_to_cpus (&u.r.wValue); + le16_to_cpus (&u.r.wIndex); + le16_to_cpus (&u.r.wLength); + + /* Delegate almost all control requests to the gadget driver, + * except for a handful of ch9 status/feature requests that + * hardware doesn't autodecode _and_ the gadget API hides. + */ + udc->ep0_in = (u.r.bRequestType & USB_DIR_IN) != 0; + udc->ep0_set_config = 0; + udc->ep0_pending = 1; + ep0->stopped = 0; + ep0->ackwait = 0; + switch (u.r.bRequest) { + case USB_REQ_SET_CONFIGURATION: + /* udc needs to know when ep != 0 is valid */ + if (u.r.bRequestType != USB_RECIP_DEVICE) + goto delegate; + if (u.r.wLength != 0) + goto do_stall; + udc->ep0_set_config = 1; + udc->ep0_reset_config = (u.r.wValue == 0); + VDBG("set config %d\n", u.r.wValue); + + /* update udc NOW since gadget driver may start + * queueing requests immediately; clear config + * later if it fails the request. + */ + if (udc->ep0_reset_config) + UDC_SYSCON2_REG = UDC_CLR_CFG; + else + UDC_SYSCON2_REG = UDC_DEV_CFG; + update_otg(udc); + goto delegate; + case USB_REQ_CLEAR_FEATURE: + /* clear endpoint halt */ + if (u.r.bRequestType != USB_RECIP_ENDPOINT) + goto delegate; + if (u.r.wValue != USB_ENDPOINT_HALT + || u.r.wLength != 0) + goto do_stall; + ep = &udc->ep[u.r.wIndex & 0xf]; + if (ep != ep0) { + if (u.r.wIndex & USB_DIR_IN) + ep += 16; + if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC + || !ep->desc) + goto do_stall; + use_ep(ep, 0); + UDC_CTRL_REG = UDC_RESET_EP; + ep->ackwait = 0; + if (!(ep->bEndpointAddress & USB_DIR_IN)) + UDC_CTRL_REG = UDC_SET_FIFO_EN; + } + VDBG("%s halt cleared by host\n", ep->name); + goto ep0out_status_stage; + case USB_REQ_SET_FEATURE: + /* set endpoint halt */ + if (u.r.bRequestType != USB_RECIP_ENDPOINT) + goto delegate; + if (u.r.wValue != USB_ENDPOINT_HALT + || u.r.wLength != 0) + goto do_stall; + ep = &udc->ep[u.r.wIndex & 0xf]; + if (u.r.wIndex & USB_DIR_IN) + ep += 16; + if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC + || ep == ep0 || !ep->desc) + goto do_stall; + if (use_dma && ep->has_dma) { + /* this has rude side-effects (aborts) and + * can't really work if DMA-IN is active + */ + DBG("%s host set_halt, NYET \n", ep->name); + goto do_stall; + } + use_ep(ep, 0); + /* can't halt if fifo isn't empty... */ + UDC_CTRL_REG = UDC_CLR_EP; + UDC_CTRL_REG = UDC_SET_HALT; + VDBG("%s halted by host\n", ep->name); +ep0out_status_stage: + status = 0; + UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; + UDC_CTRL_REG = UDC_CLR_EP; + UDC_CTRL_REG = UDC_SET_FIFO_EN; + UDC_EP_NUM_REG = UDC_EP_DIR; + udc->ep0_pending = 0; + break; + case USB_REQ_GET_STATUS: + /* return interface status. if we were pedantic, + * we'd detect non-existent interfaces, and stall. + */ + if (u.r.bRequestType + != (USB_DIR_IN|USB_RECIP_INTERFACE)) + goto delegate; + /* return two zero bytes */ + UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; + UDC_DATA_REG = 0; + UDC_CTRL_REG = UDC_SET_FIFO_EN; + UDC_EP_NUM_REG = UDC_EP_DIR; + status = 0; + VDBG("GET_STATUS, interface %d\n", u.r.wIndex); + /* next, status stage */ + break; + default: +delegate: + /* activate the ep0out fifo right away */ + if (!udc->ep0_in && u.r.wLength) { + UDC_EP_NUM_REG = 0; + UDC_CTRL_REG = UDC_SET_FIFO_EN; + } + + /* gadget drivers see class/vendor specific requests, + * {SET,GET}_{INTERFACE,DESCRIPTOR,CONFIGURATION}, + * and more + */ + VDBG("SETUP %02x.%02x v%04x i%04x l%04x\n", + u.r.bRequestType, u.r.bRequest, + u.r.wValue, u.r.wIndex, u.r.wLength); + + /* The gadget driver may return an error here, + * causing an immediate protocol stall. + * + * Else it must issue a response, either queueing a + * response buffer for the DATA stage, or halting ep0 + * (causing a protocol stall, not a real halt). A + * zero length buffer means no DATA stage. + * + * It's fine to issue that response after the setup() + * call returns, and this IRQ was handled. + */ + udc->ep0_setup = 1; + spin_unlock(&udc->lock); + status = udc->driver->setup (&udc->gadget, &u.r); + spin_lock(&udc->lock); + udc->ep0_setup = 0; + } + + if (status < 0) { +do_stall: + VDBG("req %02x.%02x protocol STALL; stat %d\n", + u.r.bRequestType, u.r.bRequest, status); + if (udc->ep0_set_config) { + if (udc->ep0_reset_config) + WARN("error resetting config?\n"); + else + UDC_SYSCON2_REG = UDC_CLR_CFG; + } + UDC_SYSCON2_REG = UDC_STALL_CMD; + udc->ep0_pending = 0; + } + } +} + +/*-------------------------------------------------------------------------*/ + +#define OTG_FLAGS (UDC_B_HNP_ENABLE|UDC_A_HNP_SUPPORT|UDC_A_ALT_HNP_SUPPORT) + +static void devstate_irq(struct omap_udc *udc, u16 irq_src) +{ + u16 devstat, change; + + devstat = UDC_DEVSTAT_REG; + change = devstat ^ udc->devstat; + udc->devstat = devstat; + + if (change & (UDC_USB_RESET|UDC_ATT)) { + udc_quiesce(udc); + + if (change & UDC_ATT) { + /* driver for any external transceiver will + * have called omap_vbus_session() already + */ + if (devstat & UDC_ATT) { + udc->gadget.speed = USB_SPEED_FULL; + VDBG("connect\n"); + if (!udc->transceiver) + pullup_enable(udc); + // if (driver->connect) call it + } else if (udc->gadget.speed != USB_SPEED_UNKNOWN) { + udc->gadget.speed = USB_SPEED_UNKNOWN; + if (!udc->transceiver) + pullup_disable(udc); + DBG("disconnect, gadget %s\n", + udc->driver->driver.name); + if (udc->driver->disconnect) { + spin_unlock(&udc->lock); + udc->driver->disconnect(&udc->gadget); + spin_lock(&udc->lock); + } + } + change &= ~UDC_ATT; + } + + if (change & UDC_USB_RESET) { + if (devstat & UDC_USB_RESET) { + VDBG("RESET=1\n"); + } else { + udc->gadget.speed = USB_SPEED_FULL; + INFO("USB reset done, gadget %s\n", + udc->driver->driver.name); + /* ep0 traffic is legal from now on */ + UDC_IRQ_EN_REG = UDC_DS_CHG_IE | UDC_EP0_IE; + } + change &= ~UDC_USB_RESET; + } + } + if (change & UDC_SUS) { + if (udc->gadget.speed != USB_SPEED_UNKNOWN) { + // FIXME tell isp1301 to suspend/resume (?) + if (devstat & UDC_SUS) { + VDBG("suspend\n"); + update_otg(udc); + /* HNP could be under way already */ + if (udc->gadget.speed == USB_SPEED_FULL + && udc->driver->suspend) { + spin_unlock(&udc->lock); + udc->driver->suspend(&udc->gadget); + spin_lock(&udc->lock); + } + } else { + VDBG("resume\n"); + if (udc->gadget.speed == USB_SPEED_FULL + && udc->driver->resume) { + spin_unlock(&udc->lock); + udc->driver->resume(&udc->gadget); + spin_lock(&udc->lock); + } + } + } + change &= ~UDC_SUS; + } + if (change & OTG_FLAGS) { + update_otg(udc); + change &= ~OTG_FLAGS; + } + + change &= ~(UDC_CFG|UDC_DEF|UDC_ADD); + if (change) + VDBG("devstat %03x, ignore change %03x\n", + devstat, change); + + UDC_IRQ_SRC_REG = UDC_DS_CHG; +} + +static irqreturn_t +omap_udc_irq(int irq, void *_udc, struct pt_regs *r) +{ + struct omap_udc *udc = _udc; + u16 irq_src; + irqreturn_t status = IRQ_NONE; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + irq_src = UDC_IRQ_SRC_REG; + + /* Device state change (usb ch9 stuff) */ + if (irq_src & UDC_DS_CHG) { + devstate_irq(_udc, irq_src); + status = IRQ_HANDLED; + irq_src &= ~UDC_DS_CHG; + } + + /* EP0 control transfers */ + if (irq_src & (UDC_EP0_RX|UDC_SETUP|UDC_EP0_TX)) { + ep0_irq(_udc, irq_src); + status = IRQ_HANDLED; + irq_src &= ~(UDC_EP0_RX|UDC_SETUP|UDC_EP0_TX); + } + + /* DMA transfer completion */ + if (use_dma && (irq_src & (UDC_TXN_DONE|UDC_RXN_CNT|UDC_RXN_EOT))) { + dma_irq(_udc, irq_src); + status = IRQ_HANDLED; + irq_src &= ~(UDC_TXN_DONE|UDC_RXN_CNT|UDC_RXN_EOT); + } + + irq_src &= ~(UDC_SOF|UDC_EPN_TX|UDC_EPN_RX); + if (irq_src) + DBG("udc_irq, unhandled %03x\n", irq_src); + spin_unlock_irqrestore(&udc->lock, flags); + + return status; +} + +static irqreturn_t +omap_udc_pio_irq(int irq, void *_dev, struct pt_regs *r) +{ + u16 epn_stat, irq_src; + irqreturn_t status = IRQ_NONE; + struct omap_ep *ep; + int epnum; + struct omap_udc *udc = _dev; + struct omap_req *req; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + epn_stat = UDC_EPN_STAT_REG; + irq_src = UDC_IRQ_SRC_REG; + + /* handle OUT first, to avoid some wasteful NAKs */ + if (irq_src & UDC_EPN_RX) { + epnum = (epn_stat >> 8) & 0x0f; + UDC_IRQ_SRC_REG = UDC_EPN_RX; + status = IRQ_HANDLED; + ep = &udc->ep[epnum]; + ep->irqs++; + + if (!list_empty(&ep->queue)) { + UDC_EP_NUM_REG = epnum | UDC_EP_SEL; + if ((UDC_STAT_FLG_REG & UDC_ACK)) { + int stat; + req = container_of(ep->queue.next, + struct omap_req, queue); + stat = read_fifo(ep, req); + // FIXME double buffered PIO OUT should work + } + UDC_EP_NUM_REG = epnum; + } + } + + /* then IN transfers */ + if (irq_src & UDC_EPN_TX) { + epnum = epn_stat & 0x0f; + UDC_IRQ_SRC_REG = UDC_EPN_TX; + status = IRQ_HANDLED; + ep = &udc->ep[16 + epnum]; + ep->irqs++; + ep->ackwait = 0; + + if (!list_empty(&ep->queue)) { + UDC_EP_NUM_REG = epnum | UDC_EP_DIR | UDC_EP_SEL; + if ((UDC_STAT_FLG_REG & UDC_ACK)) { + req = container_of(ep->queue.next, + struct omap_req, queue); + (void) write_fifo(ep, req); + } + UDC_EP_NUM_REG = epnum | UDC_EP_DIR; + /* 6 wait states before it'll tx */ + } + } + + spin_unlock_irqrestore(&udc->lock, flags); + return status; +} + +#ifdef USE_ISO +static irqreturn_t +omap_udc_iso_irq(int irq, void *_dev, struct pt_regs *r) +{ + struct omap_udc *udc = _dev; + struct omap_ep *ep; + int pending = 0; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + + /* handle all non-DMA ISO transfers */ + list_for_each_entry (ep, &udc->iso, iso) { + u16 stat; + struct omap_req *req; + + if (ep->has_dma || list_empty(&ep->queue)) + continue; + req = list_entry(ep->queue.next, struct omap_req, queue); + + use_ep(ep, UDC_EP_SEL); + stat = UDC_STAT_FLG_REG; + + /* NOTE: like the other controller drivers, this isn't + * currently reporting lost or damaged frames. + */ + if (ep->bEndpointAddress & USB_DIR_IN) { + if (stat & UDC_MISS_IN) + /* done(ep, req, -EPROTO) */; + else + write_fifo(ep, req); + } else { + int status = 0; + + if (stat & UDC_NO_RXPACKET) + status = -EREMOTEIO; + else if (stat & UDC_ISO_ERR) + status = -EILSEQ; + else if (stat & UDC_DATA_FLUSH) + status = -ENOSR; + + if (status) + /* done(ep, req, status) */; + else + read_fifo(ep, req); + } + deselect_ep(); + /* 6 wait states before next EP */ + + ep->irqs++; + if (!list_empty(&ep->queue)) + pending = 1; + } + if (!pending) + UDC_IRQ_EN_REG &= ~UDC_SOF_IE; + UDC_IRQ_SRC_REG = UDC_SOF; + + spin_unlock_irqrestore(&udc->lock, flags); + return IRQ_HANDLED; +} +#endif + +/*-------------------------------------------------------------------------*/ + +static struct omap_udc *udc; + +int usb_gadget_register_driver (struct usb_gadget_driver *driver) +{ + int status = -ENODEV; + struct omap_ep *ep; + unsigned long flags; + + /* basic sanity tests */ + if (!udc) + return -ENODEV; + if (!driver + // FIXME if otg, check: driver->is_otg + || driver->speed < USB_SPEED_FULL + || !driver->bind + || !driver->unbind + || !driver->setup) + return -EINVAL; + + spin_lock_irqsave(&udc->lock, flags); + if (udc->driver) { + spin_unlock_irqrestore(&udc->lock, flags); + return -EBUSY; + } + + /* reset state */ + list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list) { + ep->irqs = 0; + if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) + continue; + use_ep(ep, 0); + UDC_CTRL_REG = UDC_SET_HALT; + } + udc->ep0_pending = 0; + udc->ep[0].irqs = 0; + udc->softconnect = 1; + + /* hook up the driver */ + driver->driver.bus = 0; + udc->driver = driver; + udc->gadget.dev.driver = &driver->driver; + spin_unlock_irqrestore(&udc->lock, flags); + + status = driver->bind (&udc->gadget); + if (status) { + DBG("bind to %s --> %d\n", driver->driver.name, status); + udc->gadget.dev.driver = 0; + udc->driver = 0; + goto done; + } + DBG("bound to driver %s\n", driver->driver.name); + + UDC_IRQ_SRC_REG = UDC_IRQ_SRC_MASK; + + /* connect to bus through transceiver */ + if (udc->transceiver) { + status = otg_set_peripheral(udc->transceiver, &udc->gadget); + if (status < 0) { + ERR("can't bind to transceiver\n"); + driver->unbind (&udc->gadget); + udc->gadget.dev.driver = 0; + udc->driver = 0; + goto done; + } + } else { + if (can_pullup(udc)) + pullup_enable (udc); + else + pullup_disable (udc); + } + +done: + return status; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) +{ + unsigned long flags; + int status = -ENODEV; + + if (!udc) + return -ENODEV; + if (!driver || driver != udc->driver) + return -EINVAL; + + if (udc->transceiver) + (void) otg_set_peripheral(udc->transceiver, 0); + else + pullup_disable(udc); + + spin_lock_irqsave(&udc->lock, flags); + udc_quiesce(udc); + spin_unlock_irqrestore(&udc->lock, flags); + + driver->unbind(&udc->gadget); + udc->gadget.dev.driver = 0; + udc->driver = 0; + + + DBG("unregistered driver '%s'\n", driver->driver.name); + return status; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_USB_OMAP_PROC + +#include + +static const char proc_filename[] = "driver/udc"; + +#define FOURBITS "%s%s%s%s" +#define EIGHTBITS FOURBITS FOURBITS + +static void proc_ep_show(struct seq_file *s, struct omap_ep *ep) +{ + u16 stat_flg; + struct omap_req *req; + char buf[20]; + + use_ep(ep, 0); + + if (use_dma && ep->has_dma) + snprintf(buf, sizeof buf, "(%cxdma%d lch%d) ", + (ep->bEndpointAddress & USB_DIR_IN) ? 't' : 'r', + ep->dma_channel - 1, ep->lch); + else + buf[0] = 0; + + stat_flg = UDC_STAT_FLG_REG; + seq_printf(s, + "\n%s %sirqs %ld stat %04x " EIGHTBITS FOURBITS "%s\n", + ep->name, buf, ep->irqs, stat_flg, + (stat_flg & UDC_NO_RXPACKET) ? "no_rxpacket " : "", + (stat_flg & UDC_MISS_IN) ? "miss_in " : "", + (stat_flg & UDC_DATA_FLUSH) ? "data_flush " : "", + (stat_flg & UDC_ISO_ERR) ? "iso_err " : "", + (stat_flg & UDC_ISO_FIFO_EMPTY) ? "iso_fifo_empty " : "", + (stat_flg & UDC_ISO_FIFO_FULL) ? "iso_fifo_full " : "", + (stat_flg & UDC_EP_HALTED) ? "HALT " : "", + (stat_flg & UDC_STALL) ? "STALL " : "", + (stat_flg & UDC_NAK) ? "NAK " : "", + (stat_flg & UDC_ACK) ? "ACK " : "", + (stat_flg & UDC_FIFO_EN) ? "fifo_en " : "", + (stat_flg & UDC_NON_ISO_FIFO_EMPTY) ? "fifo_empty " : "", + (stat_flg & UDC_NON_ISO_FIFO_FULL) ? "fifo_full " : ""); + + if (list_empty (&ep->queue)) + seq_printf(s, "\t(queue empty)\n"); + else + list_for_each_entry (req, &ep->queue, queue) + seq_printf(s, "\treq %p len %d/%d buf %p\n", + &req->req, req->req.actual, + req->req.length, req->req.buf); +} + +static char *trx_mode(unsigned m) +{ + switch (m) { + case 3: + case 0: return "6wire"; + case 1: return "4wire"; + case 2: return "3wire"; + default: return "unknown"; + } +} + +static int proc_udc_show(struct seq_file *s, void *_) +{ + u32 tmp; + struct omap_ep *ep; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + + seq_printf(s, "%s, version: " DRIVER_VERSION +#ifdef USE_ISO + " (iso)" +#endif + "%s\n", + driver_desc, + use_dma ? " (dma)" : ""); + + tmp = UDC_REV_REG & 0xff; + seq_printf(s, + "UDC rev %d.%d, OTG rev %d.%d, fifo mode %d, gadget %s\n" + "hmc %d, transceiver %08x %s\n", + tmp >> 4, tmp & 0xf, + OTG_REV_REG >> 4, OTG_REV_REG & 0xf, + fifo_mode, + udc->driver ? udc->driver->driver.name : "(none)", + HMC, USB_TRANSCEIVER_CTRL_REG, + udc->transceiver ? udc->transceiver->label : ""); + + /* OTG controller registers */ + tmp = OTG_SYSCON_1_REG; + seq_printf(s, "otg_syscon1 %08x usb2 %s, usb1 %s, usb0 %s," + FOURBITS "\n", tmp, + trx_mode(USB2_TRX_MODE(tmp)), + trx_mode(USB1_TRX_MODE(tmp)), + trx_mode(USB0_TRX_MODE(tmp)), + (tmp & OTG_IDLE_EN) ? " !otg" : "", + (tmp & HST_IDLE_EN) ? " !host" : "", + (tmp & DEV_IDLE_EN) ? " !dev" : "", + (tmp & OTG_RESET_DONE) ? " reset_done" : " reset_active"); + tmp = OTG_SYSCON_2_REG; + seq_printf(s, "otg_syscon2 %08x%s" EIGHTBITS + " b_ase_brst=%d hmc=%d\n", tmp, + (tmp & OTG_EN) ? " otg_en" : "", + (tmp & USBX_SYNCHRO) ? " synchro" : "", + // much more SRP stuff + (tmp & SRP_DATA) ? " srp_data" : "", + (tmp & SRP_VBUS) ? " srp_vbus" : "", + (tmp & OTG_PADEN) ? " otg_paden" : "", + (tmp & HMC_PADEN) ? " hmc_paden" : "", + (tmp & UHOST_EN) ? " uhost_en" : "", + (tmp & HMC_TLLSPEED) ? " tllspeed" : "", + (tmp & HMC_TLLATTACH) ? " tllattach" : "", + B_ASE_BRST(tmp), + OTG_HMC(tmp)); + tmp = OTG_CTRL_REG; + seq_printf(s, "otg_ctrl %06x" EIGHTBITS EIGHTBITS "%s\n", tmp, + (tmp & OTG_ASESSVLD) ? " asess" : "", + (tmp & OTG_BSESSEND) ? " bsess_end" : "", + (tmp & OTG_BSESSVLD) ? " bsess" : "", + (tmp & OTG_VBUSVLD) ? " vbus" : "", + (tmp & OTG_ID) ? " id" : "", + (tmp & OTG_DRIVER_SEL) ? " DEVICE" : " HOST", + (tmp & OTG_A_SETB_HNPEN) ? " a_setb_hnpen" : "", + (tmp & OTG_A_BUSREQ) ? " a_bus" : "", + (tmp & OTG_B_HNPEN) ? " b_hnpen" : "", + (tmp & OTG_B_BUSREQ) ? " b_bus" : "", + (tmp & OTG_BUSDROP) ? " busdrop" : "", + (tmp & OTG_PULLDOWN) ? " down" : "", + (tmp & OTG_PULLUP) ? " up" : "", + (tmp & OTG_DRV_VBUS) ? " drv" : "", + (tmp & OTG_PD_VBUS) ? " pd_vb" : "", + (tmp & OTG_PU_VBUS) ? " pu_vb" : "", + (tmp & OTG_PU_ID) ? " pu_id" : "" + ); + tmp = OTG_IRQ_EN_REG; + seq_printf(s, "otg_irq_en %04x" "\n", tmp); + tmp = OTG_IRQ_SRC_REG; + seq_printf(s, "otg_irq_src %04x" "\n", tmp); + tmp = OTG_OUTCTRL_REG; + seq_printf(s, "otg_outctrl %04x" "\n", tmp); + tmp = OTG_TEST_REG; + seq_printf(s, "otg_test %04x" "\n", tmp); + + tmp = UDC_SYSCON1_REG; + seq_printf(s, "\nsyscon1 %04x" EIGHTBITS "\n", tmp, + (tmp & UDC_CFG_LOCK) ? " cfg_lock" : "", + (tmp & UDC_DATA_ENDIAN) ? " data_endian" : "", + (tmp & UDC_DMA_ENDIAN) ? " dma_endian" : "", + (tmp & UDC_NAK_EN) ? " nak" : "", + (tmp & UDC_AUTODECODE_DIS) ? " autodecode_dis" : "", + (tmp & UDC_SELF_PWR) ? " self_pwr" : "", + (tmp & UDC_SOFF_DIS) ? " soff_dis" : "", + (tmp & UDC_PULLUP_EN) ? " PULLUP" : ""); + // syscon2 is write-only + + /* UDC controller registers */ + if (!(tmp & UDC_PULLUP_EN)) { + seq_printf(s, "(suspended)\n"); + spin_unlock_irqrestore(&udc->lock, flags); + return 0; + } + + tmp = UDC_DEVSTAT_REG; + seq_printf(s, "devstat %04x" EIGHTBITS "%s%s\n", tmp, + (tmp & UDC_B_HNP_ENABLE) ? " b_hnp" : "", + (tmp & UDC_A_HNP_SUPPORT) ? " a_hnp" : "", + (tmp & UDC_A_ALT_HNP_SUPPORT) ? " a_alt_hnp" : "", + (tmp & UDC_R_WK_OK) ? " r_wk_ok" : "", + (tmp & UDC_USB_RESET) ? " usb_reset" : "", + (tmp & UDC_SUS) ? " SUS" : "", + (tmp & UDC_CFG) ? " CFG" : "", + (tmp & UDC_ADD) ? " ADD" : "", + (tmp & UDC_DEF) ? " DEF" : "", + (tmp & UDC_ATT) ? " ATT" : ""); + seq_printf(s, "sof %04x\n", UDC_SOF_REG); + tmp = UDC_IRQ_EN_REG; + seq_printf(s, "irq_en %04x" FOURBITS "%s\n", tmp, + (tmp & UDC_SOF_IE) ? " sof" : "", + (tmp & UDC_EPN_RX_IE) ? " epn_rx" : "", + (tmp & UDC_EPN_TX_IE) ? " epn_tx" : "", + (tmp & UDC_DS_CHG_IE) ? " ds_chg" : "", + (tmp & UDC_EP0_IE) ? " ep0" : ""); + tmp = UDC_IRQ_SRC_REG; + seq_printf(s, "irq_src %04x" EIGHTBITS "%s%s\n", tmp, + (tmp & UDC_TXN_DONE) ? " txn_done" : "", + (tmp & UDC_RXN_CNT) ? " rxn_cnt" : "", + (tmp & UDC_RXN_EOT) ? " rxn_eot" : "", + (tmp & UDC_SOF) ? " sof" : "", + (tmp & UDC_EPN_RX) ? " epn_rx" : "", + (tmp & UDC_EPN_TX) ? " epn_tx" : "", + (tmp & UDC_DS_CHG) ? " ds_chg" : "", + (tmp & UDC_SETUP) ? " setup" : "", + (tmp & UDC_EP0_RX) ? " ep0out" : "", + (tmp & UDC_EP0_TX) ? " ep0in" : ""); + if (use_dma) { + unsigned i; + + tmp = UDC_DMA_IRQ_EN_REG; + seq_printf(s, "dma_irq_en %04x%s" EIGHTBITS "\n", tmp, + (tmp & UDC_TX_DONE_IE(3)) ? " tx2_done" : "", + (tmp & UDC_RX_CNT_IE(3)) ? " rx2_cnt" : "", + (tmp & UDC_RX_EOT_IE(3)) ? " rx2_eot" : "", + + (tmp & UDC_TX_DONE_IE(2)) ? " tx1_done" : "", + (tmp & UDC_RX_CNT_IE(2)) ? " rx1_cnt" : "", + (tmp & UDC_RX_EOT_IE(2)) ? " rx1_eot" : "", + + (tmp & UDC_TX_DONE_IE(1)) ? " tx0_done" : "", + (tmp & UDC_RX_CNT_IE(1)) ? " rx0_cnt" : "", + (tmp & UDC_RX_EOT_IE(1)) ? " rx0_eot" : ""); + + tmp = UDC_RXDMA_CFG_REG; + seq_printf(s, "rxdma_cfg %04x\n", tmp); + if (tmp) { + for (i = 0; i < 3; i++) { + if ((tmp & (0x0f << (i * 4))) == 0) + continue; + seq_printf(s, "rxdma[%d] %04x\n", i, + UDC_RXDMA_REG(i + 1)); + } + } + tmp = UDC_TXDMA_CFG_REG; + seq_printf(s, "txdma_cfg %04x\n", tmp); + if (tmp) { + for (i = 0; i < 3; i++) { + if (!(tmp & (0x0f << (i * 4)))) + continue; + seq_printf(s, "txdma[%d] %04x\n", i, + UDC_TXDMA_REG(i + 1)); + } + } + } + + tmp = UDC_DEVSTAT_REG; + if (tmp & UDC_ATT) { + proc_ep_show(s, &udc->ep[0]); + if (tmp & UDC_ADD) { + list_for_each_entry (ep, &udc->gadget.ep_list, + ep.ep_list) { + if (ep->desc) + proc_ep_show(s, ep); + } + } + } + spin_unlock_irqrestore(&udc->lock, flags); + return 0; +} + +static int proc_udc_open(struct inode *inode, struct file *file) +{ + return single_open(file, proc_udc_show, 0); +} + +static struct file_operations proc_ops = { + .open = proc_udc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void create_proc_file(void) +{ + struct proc_dir_entry *pde; + + pde = create_proc_entry (proc_filename, 0, NULL); + if (pde) + pde->proc_fops = &proc_ops; +} + +static void remove_proc_file(void) +{ + remove_proc_entry(proc_filename, 0); +} + +#else + +static inline void create_proc_file(void) {} +static inline void remove_proc_file(void) {} + +#endif + +/*-------------------------------------------------------------------------*/ + +/* Before this controller can enumerate, we need to pick an endpoint + * configuration, or "fifo_mode" That involves allocating 2KB of packet + * buffer space among the endpoints we'll be operating. + */ +static unsigned __init +omap_ep_setup(char *name, u8 addr, u8 type, + unsigned buf, unsigned maxp, int dbuf) +{ + struct omap_ep *ep; + u16 epn_rxtx = 0; + + /* OUT endpoints first, then IN */ + ep = &udc->ep[addr & 0xf]; + if (addr & USB_DIR_IN) + ep += 16; + + /* in case of ep init table bugs */ + BUG_ON(ep->name[0]); + + /* chip setup ... bit values are same for IN, OUT */ + if (type == USB_ENDPOINT_XFER_ISOC) { + switch (maxp) { + case 8: epn_rxtx = 0 << 12; break; + case 16: epn_rxtx = 1 << 12; break; + case 32: epn_rxtx = 2 << 12; break; + case 64: epn_rxtx = 3 << 12; break; + case 128: epn_rxtx = 4 << 12; break; + case 256: epn_rxtx = 5 << 12; break; + case 512: epn_rxtx = 6 << 12; break; + default: BUG(); + } + epn_rxtx |= UDC_EPN_RX_ISO; + dbuf = 1; + } else { + /* pio-out could potentially double-buffer, + * as can (should!) DMA-IN + */ + if (!use_dma || (addr & USB_DIR_IN)) + dbuf = 0; + + switch (maxp) { + case 8: epn_rxtx = 0 << 12; break; + case 16: epn_rxtx = 1 << 12; break; + case 32: epn_rxtx = 2 << 12; break; + case 64: epn_rxtx = 3 << 12; break; + default: BUG(); + } + if (dbuf && addr) + epn_rxtx |= UDC_EPN_RX_DB; + } + if (addr) + epn_rxtx |= UDC_EPN_RX_VALID; + BUG_ON(buf & 0x07); + epn_rxtx |= buf >> 3; + + DBG("%s addr %02x rxtx %04x maxp %d%s buf %d\n", + name, addr, epn_rxtx, maxp, dbuf ? "x2" : "", buf); + + if (addr & USB_DIR_IN) + UDC_EP_TX_REG(addr & 0xf) = epn_rxtx; + else + UDC_EP_RX_REG(addr) = epn_rxtx; + + /* next endpoint's buffer starts after this one's */ + buf += maxp; + if (dbuf) + buf += maxp; + BUG_ON(buf > 2048); + + /* set up driver data structures */ + BUG_ON(strlen(name) >= sizeof ep->name); + strlcpy(ep->name, name, sizeof ep->name); + INIT_LIST_HEAD(&ep->queue); + INIT_LIST_HEAD(&ep->iso); + ep->bEndpointAddress = addr; + ep->bmAttributes = type; + ep->double_buf = dbuf; + ep->udc = udc; + + ep->ep.name = ep->name; + ep->ep.ops = &omap_ep_ops; + ep->ep.maxpacket = ep->maxpacket = maxp; + list_add_tail (&ep->ep.ep_list, &udc->gadget.ep_list); + + return buf; +} + +static void omap_udc_release(struct device *dev) +{ + complete(udc->done); + kfree (udc); + udc = 0; +} + +static int __init +omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv) +{ + unsigned tmp, buf; + + /* abolish any previous hardware state */ + UDC_SYSCON1_REG = 0; + UDC_IRQ_EN_REG = 0; + UDC_IRQ_SRC_REG = UDC_IRQ_SRC_MASK; + UDC_DMA_IRQ_EN_REG = 0; + UDC_RXDMA_CFG_REG = 0; + UDC_TXDMA_CFG_REG = 0; + + /* UDC_PULLUP_EN gates the chip clock */ + // OTG_SYSCON_1_REG |= DEV_IDLE_EN; + + udc = kmalloc (sizeof *udc, SLAB_KERNEL); + if (!udc) + return -ENOMEM; + + memset(udc, 0, sizeof *udc); + spin_lock_init (&udc->lock); + + udc->gadget.ops = &omap_gadget_ops; + udc->gadget.ep0 = &udc->ep[0].ep; + INIT_LIST_HEAD(&udc->gadget.ep_list); + INIT_LIST_HEAD(&udc->iso); + udc->gadget.speed = USB_SPEED_UNKNOWN; + udc->gadget.name = driver_name; + + device_initialize(&udc->gadget.dev); + strcpy (udc->gadget.dev.bus_id, "gadget"); + udc->gadget.dev.release = omap_udc_release; + udc->gadget.dev.parent = &odev->dev; + if (use_dma) + udc->gadget.dev.dma_mask = odev->dev.dma_mask; + + udc->transceiver = xceiv; + + /* ep0 is special; put it right after the SETUP buffer */ + buf = omap_ep_setup("ep0", 0, USB_ENDPOINT_XFER_CONTROL, + 8 /* after SETUP */, 64 /* maxpacket */, 0); + list_del_init(&udc->ep[0].ep.ep_list); + + /* initially disable all non-ep0 endpoints */ + for (tmp = 1; tmp < 15; tmp++) { + UDC_EP_RX_REG(tmp) = 0; + UDC_EP_TX_REG(tmp) = 0; + } + +#define OMAP_BULK_EP(name,addr) \ + buf = omap_ep_setup(name "-bulk", addr, \ + USB_ENDPOINT_XFER_BULK, buf, 64, 1); +#define OMAP_INT_EP(name,addr, maxp) \ + buf = omap_ep_setup(name "-int", addr, \ + USB_ENDPOINT_XFER_INT, buf, maxp, 0); +#define OMAP_ISO_EP(name,addr, maxp) \ + buf = omap_ep_setup(name "-iso", addr, \ + USB_ENDPOINT_XFER_ISOC, buf, maxp, 1); + + switch (fifo_mode) { + case 0: + OMAP_BULK_EP("ep1in", USB_DIR_IN | 1); + OMAP_BULK_EP("ep2out", USB_DIR_OUT | 2); + OMAP_INT_EP("ep3in", USB_DIR_IN | 3, 16); + break; + case 1: + OMAP_BULK_EP("ep1in", USB_DIR_IN | 1); + OMAP_BULK_EP("ep2out", USB_DIR_OUT | 2); + OMAP_BULK_EP("ep3in", USB_DIR_IN | 3); + OMAP_BULK_EP("ep4out", USB_DIR_OUT | 4); + + OMAP_BULK_EP("ep5in", USB_DIR_IN | 5); + OMAP_BULK_EP("ep5out", USB_DIR_OUT | 5); + OMAP_BULK_EP("ep6in", USB_DIR_IN | 6); + OMAP_BULK_EP("ep6out", USB_DIR_OUT | 6); + + OMAP_BULK_EP("ep7in", USB_DIR_IN | 7); + OMAP_BULK_EP("ep7out", USB_DIR_OUT | 7); + OMAP_BULK_EP("ep8in", USB_DIR_IN | 8); + OMAP_BULK_EP("ep8out", USB_DIR_OUT | 8); + + OMAP_INT_EP("ep9in", USB_DIR_IN | 9, 16); + OMAP_INT_EP("ep10out", USB_DIR_IN | 10, 16); + OMAP_INT_EP("ep11in", USB_DIR_IN | 9, 16); + OMAP_INT_EP("ep12out", USB_DIR_IN | 10, 16); + break; + +#ifdef USE_ISO + case 2: /* mixed iso/bulk */ + OMAP_ISO_EP("ep1in", USB_DIR_IN | 1, 256); + OMAP_ISO_EP("ep2out", USB_DIR_OUT | 2, 256); + OMAP_ISO_EP("ep3in", USB_DIR_IN | 3, 128); + OMAP_ISO_EP("ep4out", USB_DIR_OUT | 4, 128); + + OMAP_INT_EP("ep5in", USB_DIR_IN | 5, 16); + + OMAP_BULK_EP("ep6in", USB_DIR_IN | 6); + OMAP_BULK_EP("ep7out", USB_DIR_OUT | 7); + OMAP_INT_EP("ep8in", USB_DIR_IN | 8, 16); + break; + case 3: /* mixed bulk/iso */ + OMAP_BULK_EP("ep1in", USB_DIR_IN | 1); + OMAP_BULK_EP("ep2out", USB_DIR_OUT | 2); + OMAP_INT_EP("ep3in", USB_DIR_IN | 3, 16); + + OMAP_BULK_EP("ep4in", USB_DIR_IN | 4); + OMAP_BULK_EP("ep5out", USB_DIR_OUT | 5); + OMAP_INT_EP("ep6in", USB_DIR_IN | 6, 16); + + OMAP_ISO_EP("ep7in", USB_DIR_IN | 7, 256); + OMAP_ISO_EP("ep8out", USB_DIR_OUT | 8, 256); + OMAP_INT_EP("ep9in", USB_DIR_IN | 9, 16); + break; +#endif + + /* add more modes as needed */ + + default: + ERR("unsupported fifo_mode #%d\n", fifo_mode); + return -ENODEV; + } + UDC_SYSCON1_REG = UDC_CFG_LOCK|UDC_SELF_PWR; + INFO("fifo mode %d, %d bytes not used\n", fifo_mode, 2048 - buf); + return 0; +} + +static int __init omap_udc_probe(struct device *dev) +{ + struct platform_device *odev = to_platform_device(dev); + int status = -ENODEV; + int hmc; + struct otg_transceiver *xceiv = 0; + const char *type = 0; + struct omap_usb_config *config = dev->platform_data; + + /* NOTE: "knows" the order of the resources! */ + if (!request_mem_region(odev->resource[0].start, + odev->resource[0].end - odev->resource[0].start + 1, + driver_name)) { + DBG("request_mem_region failed\n"); + return -EBUSY; + } + + INFO("OMAP UDC rev %d.%d, OTG rev %d.%d, %s receptacle\n", + UDC_REV_REG >> 4, UDC_REV_REG & 0xf, + OTG_REV_REG >> 4, OTG_REV_REG & 0xf, + config->otg ? "Mini-AB" : "B/Mini-B"); + + /* use the mode given to us by board init code */ + hmc = HMC; + switch (hmc) { + case 3: + case 11: + case 19: + case 25: + xceiv = otg_get_transceiver(); + if (!xceiv) { + DBG("external transceiver not registered!\n"); + goto cleanup0; + } + type = xceiv->label; + break; + case 0: /* POWERUP DEFAULT == 0 */ + case 4: + case 12: + case 20: + type = "INTEGRATED"; + break; + case 21: /* internal loopback */ + type = "(loopback)"; + break; + case 14: /* transceiverless */ + type = "(none)"; + break; + + default: + ERR("unrecognized UDC HMC mode %d\n", hmc); + return -ENODEV; + } + INFO("hmc mode %d, transceiver %s\n", hmc, type); + + /* a "gadget" abstracts/virtualizes the controller */ + status = omap_udc_setup(odev, xceiv); + if (status) { + goto cleanup0; + } + xceiv = 0; + // "udc" is now valid + pullup_disable(udc); + udc->gadget.is_otg = (config->otg != 0); + + /* USB general purpose IRQ: ep0, state changes, dma, etc */ + status = request_irq(odev->resource[1].start, omap_udc_irq, + SA_SAMPLE_RANDOM, driver_name, udc); + if (status != 0) { + ERR( "can't get irq %ld, err %d\n", + odev->resource[1].start, status); + goto cleanup1; + } + + /* USB "non-iso" IRQ (PIO for all but ep0) */ + status = request_irq(odev->resource[2].start, omap_udc_pio_irq, + SA_SAMPLE_RANDOM, "omap_udc pio", udc); + if (status != 0) { + ERR( "can't get irq %ld, err %d\n", + odev->resource[2].start, status); + goto cleanup2; + } +#ifdef USE_ISO + status = request_irq(odev->resource[3].start, omap_udc_iso_irq, + SA_INTERRUPT, "omap_udc iso", udc); + if (status != 0) { + ERR("can't get irq %ld, err %d\n", + odev->resource[3].start, status); + goto cleanup3; + } +#endif + + create_proc_file(); + device_add(&udc->gadget.dev); + return 0; + +#ifdef USE_ISO +cleanup3: + free_irq(odev->resource[2].start, udc); +#endif + +cleanup2: + free_irq(odev->resource[1].start, udc); + +cleanup1: + kfree (udc); + udc = 0; + +cleanup0: + if (xceiv) + put_device(xceiv->dev); + release_mem_region(odev->resource[0].start, + odev->resource[0].end - odev->resource[0].start + 1); + return status; +} + +static int __exit omap_udc_remove(struct device *dev) +{ + struct platform_device *odev = to_platform_device(dev); + DECLARE_COMPLETION(done); + + if (!udc) + return -ENODEV; + + udc->done = &done; + + pullup_disable(udc); + if (udc->transceiver) { + put_device(udc->transceiver->dev); + udc->transceiver = 0; + } + UDC_SYSCON1_REG = 0; + + remove_proc_file(); + +#ifdef USE_ISO + free_irq(odev->resource[3].start, udc); +#endif + free_irq(odev->resource[2].start, udc); + free_irq(odev->resource[1].start, udc); + + release_mem_region(odev->resource[0].start, + odev->resource[0].end - odev->resource[0].start + 1); + + device_unregister(&udc->gadget.dev); + wait_for_completion(&done); + + return 0; +} + +/* suspend/resume/wakeup from sysfs (echo > power/state) */ + +static int omap_udc_suspend(struct device *dev, u32 state, u32 level) +{ + if (level != 0) + return 0; + + DBG("suspend, state %d\n", state); + omap_pullup(&udc->gadget, 0); + udc->gadget.dev.power.power_state = 3; + udc->gadget.dev.parent->power.power_state = 3; + return 0; +} + +static int omap_udc_resume(struct device *dev, u32 level) +{ + if (level != 0) + return 0; + + DBG("resume + wakeup/SRP\n"); + udc->gadget.dev.parent->power.power_state = 0; + udc->gadget.dev.power.power_state = 0; + omap_pullup(&udc->gadget, 1); + + /* maybe the host would enumerate us if we nudged it */ + msleep(100); + return omap_wakeup(&udc->gadget); +} + +/*-------------------------------------------------------------------------*/ + +static struct device_driver udc_driver = { + .name = (char *) driver_name, + .bus = &platform_bus_type, + .probe = omap_udc_probe, + .remove = __exit_p(omap_udc_remove), + .suspend = omap_udc_suspend, + .resume = omap_udc_resume, +}; + +static int __init udc_init(void) +{ + /* should work on many OMAP systems with at most minor changes, + * but the 1510 doesn't have an OTG controller. + */ + if (cpu_is_omap1510()) { + DBG("no OMAP1510 support yet\n"); + return -ENODEV; + } + INFO("%s, version: " DRIVER_VERSION "%s\n", driver_desc, + use_dma ? " (dma)" : ""); + return driver_register(&udc_driver); +} +module_init(udc_init); + +static void __exit udc_exit(void) +{ + driver_unregister(&udc_driver); +} +module_exit(udc_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/drivers/usb/gadget/omap_udc.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,199 @@ +/* + * omap_udc.h -- for omap 3.2 udc, with OTG support + * + * 2004 (C) Texas Instruments, Inc. + * 2004 (C) David Brownell + */ + +/* + * USB device/endpoint management registers + */ +#define UDC_REG(offset) __REG16(UDC_BASE + (offset)) + +#define UDC_REV_REG UDC_REG(0x0) /* Revision */ +#define UDC_EP_NUM_REG UDC_REG(0x4) /* Which endpoint */ +# define UDC_SETUP_SEL (1 << 6) +# define UDC_EP_SEL (1 << 5) +# define UDC_EP_DIR (1 << 4) + /* low 4 bits for endpoint number */ +#define UDC_DATA_REG UDC_REG(0x08) /* Endpoint FIFO */ +#define UDC_CTRL_REG UDC_REG(0x0C) /* Endpoint control */ +# define UDC_CLR_HALT (1 << 7) +# define UDC_SET_HALT (1 << 6) +# define UDC_SET_FIFO_EN (1 << 2) +# define UDC_CLR_EP (1 << 1) +# define UDC_RESET_EP (1 << 0) +#define UDC_STAT_FLG_REG UDC_REG(0x10) /* Endpoint status */ +# define UDC_NO_RXPACKET (1 << 15) +# define UDC_MISS_IN (1 << 14) +# define UDC_DATA_FLUSH (1 << 13) +# define UDC_ISO_ERR (1 << 12) +# define UDC_ISO_FIFO_EMPTY (1 << 9) +# define UDC_ISO_FIFO_FULL (1 << 8) +# define UDC_EP_HALTED (1 << 6) +# define UDC_STALL (1 << 5) +# define UDC_NAK (1 << 4) +# define UDC_ACK (1 << 3) +# define UDC_FIFO_EN (1 << 2) +# define UDC_NON_ISO_FIFO_EMPTY (1 << 1) +# define UDC_NON_ISO_FIFO_FULL (1 << 0) +#define UDC_RXFSTAT_REG UDC_REG(0x14) /* OUT bytecount */ +#define UDC_SYSCON1_REG UDC_REG(0x18) /* System config 1 */ +# define UDC_CFG_LOCK (1 << 8) +# define UDC_DATA_ENDIAN (1 << 7) +# define UDC_DMA_ENDIAN (1 << 6) +# define UDC_NAK_EN (1 << 4) +# define UDC_AUTODECODE_DIS (1 << 3) +# define UDC_SELF_PWR (1 << 2) +# define UDC_SOFF_DIS (1 << 1) +# define UDC_PULLUP_EN (1 << 0) +#define UDC_SYSCON2_REG UDC_REG(0x1C) /* System config 2 */ +# define UDC_RMT_WKP (1 << 6) +# define UDC_STALL_CMD (1 << 5) +# define UDC_DEV_CFG (1 << 3) +# define UDC_CLR_CFG (1 << 2) +#define UDC_DEVSTAT_REG UDC_REG(0x20) /* Device status */ +# define UDC_B_HNP_ENABLE (1 << 9) +# define UDC_A_HNP_SUPPORT (1 << 8) +# define UDC_A_ALT_HNP_SUPPORT (1 << 7) +# define UDC_R_WK_OK (1 << 6) +# define UDC_USB_RESET (1 << 5) +# define UDC_SUS (1 << 4) +# define UDC_CFG (1 << 3) +# define UDC_ADD (1 << 2) +# define UDC_DEF (1 << 1) +# define UDC_ATT (1 << 0) +#define UDC_SOF_REG UDC_REG(0x24) /* Start of frame */ +# define UDC_FT_LOCK (1 << 12) +# define UDC_TS_OK (1 << 11) +# define UDC_TS 0x03ff +#define UDC_IRQ_EN_REG UDC_REG(0x28) /* Interrupt enable */ +# define UDC_SOF_IE (1 << 7) +# define UDC_EPN_RX_IE (1 << 5) +# define UDC_EPN_TX_IE (1 << 4) +# define UDC_DS_CHG_IE (1 << 3) +# define UDC_EP0_IE (1 << 0) +#define UDC_DMA_IRQ_EN_REG UDC_REG(0x2C) /* DMA irq enable */ + /* rx/tx dma channels numbered 1-3 not 0-2 */ +# define UDC_TX_DONE_IE(n) (1 << (4 * (n) - 2)) +# define UDC_RX_CNT_IE(n) (1 << (4 * (n) - 3)) +# define UDC_RX_EOT_IE(n) (1 << (4 * (n) - 4)) +#define UDC_IRQ_SRC_REG UDC_REG(0x30) /* Interrupt source */ +# define UDC_TXN_DONE (1 << 10) +# define UDC_RXN_CNT (1 << 9) +# define UDC_RXN_EOT (1 << 8) +# define UDC_SOF (1 << 7) +# define UDC_EPN_RX (1 << 5) +# define UDC_EPN_TX (1 << 4) +# define UDC_DS_CHG (1 << 3) +# define UDC_SETUP (1 << 2) +# define UDC_EP0_RX (1 << 1) +# define UDC_EP0_TX (1 << 0) +# define UDC_IRQ_SRC_MASK 0x7bf +#define UDC_EPN_STAT_REG UDC_REG(0x34) /* EP irq status */ +#define UDC_DMAN_STAT_REG UDC_REG(0x38) /* DMA irq status */ +# define UDC_DMA_RX_SB (1 << 12) +# define UDC_DMA_RX_SRC(x) (((x)>>8) & 0xf) +# define UDC_DMA_TX_SRC(x) (((x)>>0) & 0xf) + + +/* DMA configuration registers: up to three channels in each direction. */ +#define UDC_RXDMA_CFG_REG UDC_REG(0x40) /* 3 eps for RX DMA */ +#define UDC_TXDMA_CFG_REG UDC_REG(0x44) /* 3 eps for TX DMA */ +#define UDC_DATA_DMA_REG UDC_REG(0x48) /* rx/tx fifo addr */ + +/* rx/tx dma control, numbering channels 1-3 not 0-2 */ +#define UDC_TXDMA_REG(chan) UDC_REG(0x50 - 4 + 4 * (chan)) +# define UDC_TXN_EOT (1 << 15) /* bytes vs packets */ +# define UDC_TXN_START (1 << 14) /* start transfer */ +# define UDC_TXN_TSC 0x03ff /* units in xfer */ +#define UDC_RXDMA_REG(chan) UDC_REG(0x60 - 4 + 4 * (chan)) +# define UDC_RXN_STOP (1 << 15) /* enable EOT irq */ +# define UDC_RXN_TC 0x00ff /* packets in xfer */ + + +/* + * Endpoint configuration registers (used before CFG_LOCK is set) + * UDC_EP_TX_REG(0) is unused + */ +#define UDC_EP_RX_REG(endpoint) UDC_REG(0x80 + (endpoint)*4) +# define UDC_EPN_RX_VALID (1 << 15) +# define UDC_EPN_RX_DB (1 << 14) + /* buffer size in bits 13, 12 */ +# define UDC_EPN_RX_ISO (1 << 11) + /* buffer pointer in low 11 bits */ +#define UDC_EP_TX_REG(endpoint) UDC_REG(0xc0 + (endpoint)*4) + /* same bitfields as in RX_REG */ + +/*-------------------------------------------------------------------------*/ + +struct omap_req { + struct usb_request req; + struct list_head queue; + unsigned dma_bytes; + unsigned mapped:1; +}; + +struct omap_ep { + struct usb_ep ep; + struct list_head queue; + unsigned long irqs; + struct list_head iso; + const struct usb_endpoint_descriptor *desc; + char name[14]; + u16 maxpacket; + u8 bEndpointAddress; + u8 bmAttributes; + unsigned double_buf:1; + unsigned stopped:1; + unsigned ackwait:1; + unsigned has_dma:1; + u8 dma_channel; + int lch; + struct omap_udc *udc; +}; + +struct omap_udc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + spinlock_t lock; + struct omap_ep ep[32]; + u16 devstat; + struct otg_transceiver *transceiver; + struct list_head iso; + unsigned softconnect:1; + unsigned vbus_active:1; + unsigned ep0_pending:1; + unsigned ep0_in:1; + unsigned ep0_set_config:1; + unsigned ep0_reset_config:1; + unsigned ep0_setup:1; + unsigned hmc:6; + + struct completion *done; +}; + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +#define DBG(stuff...) printk(KERN_DEBUG "udc: " stuff) +#else +#define DBG(stuff...) do{}while(0) +#endif + +#ifdef VERBOSE +# define VDBG DBG +#else +# define VDBG(stuff...) do{}while(0) +#endif + +#define ERR(stuff...) printk(KERN_ERR "udc: " stuff) +#define WARN(stuff...) printk(KERN_WARNING "udc: " stuff) +#define INFO(stuff...) printk(KERN_INFO "udc: " stuff) + +/*-------------------------------------------------------------------------*/ + +// #define HMC_1510 ((MOD_CONF_CTRL_0_REG >> 1) & 0x3f) +#define HMC_1610 (OTG_SYSCON_2_REG & 0x3f) +#define HMC HMC_1610 + --- linux-2.6.9-rc1/drivers/usb/gadget/serial.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/usb/gadget/serial.c 2004-09-02 20:51:52.000000000 -0700 @@ -1299,6 +1299,9 @@ static int gs_bind(struct usb_gadget *ga } else if (gadget_is_omap(gadget)) { gs_device_desc.bcdDevice = __constant_cpu_to_le16(GS_VERSION_NUM|0x0007); + } else if (gadget_is_lh7a40x(gadget)) { + gs_device_desc.bcdDevice = + __constant_cpu_to_le16(GS_VERSION_NUM|0x0008); } else { printk(KERN_WARNING "gs_bind: controller '%s' not recognized\n", gadget->name); --- linux-2.6.9-rc1/drivers/usb/gadget/zero.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/drivers/usb/gadget/zero.c 2004-09-02 20:51:52.000000000 -0700 @@ -399,7 +399,7 @@ static const struct usb_descriptor_heade static char manufacturer [40]; static char serial [40]; -/* static strings, in iso 8859/1 */ +/* static strings, in UTF-8 */ static struct usb_string strings [] = { { STRING_MANUFACTURER, manufacturer, }, { STRING_PRODUCT, longname, }, @@ -960,7 +960,8 @@ zero_setup (struct usb_gadget *gadget, c case USB_DT_STRING: /* wIndex == language code. * this driver only handles one language, you can - * add others even if they don't use iso8859/1 + * add string tables for other languages, using + * any UTF-8 characters */ value = usb_gadget_get_string (&stringtab, ctrl->wValue & 0xff, req->buf); @@ -1185,6 +1186,8 @@ autoconf_fail: device_desc.bcdDevice = __constant_cpu_to_le16 (0x0207); } else if (gadget_is_omap (gadget)) { device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208); + } else if (gadget_is_lh7a40x(gadget)) { + device_desc.bcdDevice = __constant_cpu_to_le16 (0x0209); } else { /* gadget zero is so simple (for now, no altsettings) that * it SHOULD NOT have problems with bulk-capable hardware. @@ -1236,6 +1239,12 @@ autoconf_fail: loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; } + if (gadget->is_otg) { + otg_descriptor.bmAttributes |= USB_OTG_HNP, + source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + usb_gadget_set_selfpowered (gadget); init_timer (&dev->resume); --- linux-2.6.9-rc1/drivers/usb/host/ehci-hub.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/drivers/usb/host/ehci-hub.c 2004-09-02 20:51:52.000000000 -0700 @@ -146,8 +146,8 @@ static int ehci_hub_resume (struct usb_h #else -#define ehci_hub_suspend 0 -#define ehci_hub_resume 0 +#define ehci_hub_suspend NULL +#define ehci_hub_resume NULL #endif /* CONFIG_PM */ --- linux-2.6.9-rc1/drivers/usb/host/ehci-mem.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/drivers/usb/host/ehci-mem.c 2004-09-02 20:51:52.000000000 -0700 @@ -114,7 +114,7 @@ static struct ehci_qh *ehci_qh_alloc (st return qh; memset (qh, 0, sizeof *qh); - kref_init(&qh->kref, qh_destroy); + kref_init(&qh->kref); qh->ehci = ehci; qh->qh_dma = dma; // INIT_LIST_HEAD (&qh->qh_list); @@ -139,7 +139,7 @@ static inline struct ehci_qh *qh_get (st static inline void qh_put (struct ehci_qh *qh) { - kref_put(&qh->kref); + kref_put(&qh->kref, qh_destroy); } /*-------------------------------------------------------------------------*/ --- linux-2.6.9-rc1/drivers/usb/host/Kconfig 2004-08-24 00:03:38.000000000 -0700 +++ 25/drivers/usb/host/Kconfig 2004-09-02 20:51:52.000000000 -0700 @@ -52,6 +52,7 @@ config USB_EHCI_ROOT_HUB_TT config USB_OHCI_HCD tristate "OHCI HCD support" depends on USB + select ISP1301_OMAP if MACH_OMAP_H2 ---help--- The Open Host Controller Interface (OHCI) is a standard for accessing USB 1.1 host controller hardware. It does more in hardware than Intel's --- linux-2.6.9-rc1/drivers/usb/host/ohci.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/usb/host/ohci.h 2004-09-02 20:51:52.000000000 -0700 @@ -360,6 +360,13 @@ struct ohci_hcd { struct ed *periodic [NUM_INTS]; /* shadow int_table */ /* + * OTG controllers and transceivers need software interaction; + * other external transceivers should be software-transparent + */ + struct otg_transceiver *transceiver; + unsigned power_budget; + + /* * memory management for queue data structures */ struct dma_pool *td_cache; --- linux-2.6.9-rc1/drivers/usb/host/ohci-hcd.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/usb/host/ohci-hcd.c 2004-09-02 20:51:52.000000000 -0700 @@ -97,6 +97,7 @@ #include #include /* for in_interrupt () */ #include +#include #include "../core/hcd.h" #include #include /* needed by ohci-mem.c when no PCI */ @@ -693,7 +694,7 @@ static void ohci_stop (struct usb_hcd *h /* must not be called from interrupt context */ -#ifdef CONFIG_PM +#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM) static void mark_children_gone (struct usb_device *dev) { --- linux-2.6.9-rc1/drivers/usb/host/ohci-hub.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/usb/host/ohci-hub.c 2004-09-02 20:51:52.000000000 -0700 @@ -435,6 +435,89 @@ ohci_hub_descriptor ( /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_USB_OTG + +static int ohci_start_port_reset (struct usb_hcd *hcd, unsigned port) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + u32 status; + + if (!port) + return -EINVAL; + port--; + + /* start port reset before HNP protocol times out */ + status = ohci_readl(&ohci->regs->roothub.portstatus [port]); + if (!(status & RH_PS_CCS)) + return -ENODEV; + + /* khubd will finish the reset later */ + writel(RH_PS_PRS, &ohci->regs->roothub.portstatus [port]); + return 0; +} + +static void start_hnp(struct ohci_hcd *ohci); + +#else + +#define ohci_start_port_reset NULL + +#endif + +/*-------------------------------------------------------------------------*/ + + +/* See usb 7.1.7.5: root hubs must issue at least 50 msec reset signaling, + * not necessarily continuous ... to guard against resume signaling. + * The short timeout is safe for non-root hubs, and is backward-compatible + * with earlier Linux hosts. + */ +#ifdef CONFIG_USB_SUSPEND +#define PORT_RESET_MSEC 50 +#else +#define PORT_RESET_MSEC 10 +#endif + +/* this timer value might be vendor-specific ... */ +#define PORT_RESET_HW_MSEC 10 + +/* wrap-aware logic stolen from */ +#define tick_before(t1,t2) ((((s16)(t1))-((s16)(t2))) < 0) + +/* called from some task, normally khubd */ +static inline void root_port_reset (struct ohci_hcd *ohci, unsigned port) +{ + u32 *portstat = &ohci->regs->roothub.portstatus [port]; + u32 temp; + u16 now = readl(&ohci->regs->fmnumber); + u16 reset_done = now + PORT_RESET_MSEC; + + /* build a "continuous enough" reset signal, with up to + * 3msec gap between pulses. scheduler HZ==100 must work; + * this might need to be deadline-scheduled. + */ + do { + /* spin until any current reset finishes */ + for (;;) { + temp = ohci_readl (portstat); + if (!(temp & RH_PS_PRS)) + break; + udelay (500); + } + + if (!(temp & RH_PS_CCS)) + break; + if (temp & RH_PS_PRSC) + writel (RH_PS_PRSC, portstat); + + /* start the next reset, sleep till it's probably done */ + writel (RH_PS_PRS, portstat); + msleep(PORT_RESET_HW_MSEC); + now = readl(&ohci->regs->fmnumber); + } while (tick_before(now, reset_done)); + /* caller synchronizes using PRSC */ +} + static int ohci_hub_control ( struct usb_hcd *hcd, u16 typeReq, @@ -533,6 +616,12 @@ static int ohci_hub_control ( wIndex--; switch (wValue) { case USB_PORT_FEAT_SUSPEND: +#ifdef CONFIG_USB_OTG + if (ohci->hcd.self.otg_port == (wIndex + 1) + && ohci->hcd.self.b_hnp_enable) + start_hnp(ohci); + else +#endif writel (RH_PS_PSS, &ohci->regs->roothub.portstatus [wIndex]); break; @@ -541,10 +630,7 @@ static int ohci_hub_control ( &ohci->regs->roothub.portstatus [wIndex]); break; case USB_PORT_FEAT_RESET: - temp = ohci_readl (&ohci->regs->roothub.portstatus [wIndex]); - if (temp & RH_PS_CCS) - writel (RH_PS_PRS, - &ohci->regs->roothub.portstatus [wIndex]); + root_port_reset (ohci, wIndex); break; default: goto error; --- linux-2.6.9-rc1/drivers/usb/host/ohci-omap.c 2004-08-24 00:03:14.000000000 -0700 +++ 25/drivers/usb/host/ohci-omap.c 2004-09-02 20:51:52.000000000 -0700 @@ -2,27 +2,30 @@ * OHCI HCD (Host Controller Driver) for USB. * * (C) Copyright 1999 Roman Weissgaerber - * (C) Copyright 2000-2002 David Brownell + * (C) Copyright 2000-2004 David Brownell * (C) Copyright 2002 Hewlett-Packard Company * * OMAP Bus Glue * * Written by Christopher Hoover - * Based on fragments of previous driver by Rusell King et al. + * Based on fragments of previous driver by Russell King et al. * * Modified for OMAP from ohci-sa1111.c by Tony Lindgren * Based on the 2.4 OMAP OHCI driver originally done by MontaVista Software Inc. * * This file is licenced under the GPL. */ - + #include #include +#include -#include #include #include #include +#include +#include +#include #include "ohci-omap.h" @@ -34,122 +37,53 @@ extern int usb_disabled(void); extern int ocpi_enable(void); /* - * Use the first port only by default. Override with hmc_mode option. - * - * NOTE: Many OMAP-1510 Innovators supposedly have bad wiring for the USB ports - * 1 & 2, so only port 0 will work. To use the OHCI on the first port, use - * the Innovator USB client cable with a client-to-client connector and modify - * either the cable or the hub to feed 5V VBUS back to Innovator. VBUS should - * be the red lead in the cable. - * - * To mount USB hard disk as root, see the patch for do_mounts.c that tries - * remounting the root, and use root=0801 if your root is on sda1. Does not - * work with devfs. - */ -static int default_hmc_mode = 16; -static int hmc_mode = 1234; - -/* - * Set the USB host pin multiplexing and the selected HMC mode - */ -static int omap_usb_set_hmc_mode(int hmc_mode) -{ - unsigned int val; - - switch (hmc_mode) { - case 0: - /* 0: function, 1: disabled, 2: disabled */ - omap_cfg_reg(W4_USB_PUEN); - omap_cfg_reg(R18_1510_USB_GPIO0); - break; - case 4: - /* 0: function 1: host 2: host */ - omap_cfg_reg(usb1_speed); - omap_cfg_reg(usb1_susp); - omap_cfg_reg(usb1_seo); - omap_cfg_reg(usb1_txen); - omap_cfg_reg(usb1_txd); - omap_cfg_reg(usb1_vp); - omap_cfg_reg(usb1_vm); - omap_cfg_reg(usb1_rcv); - omap_cfg_reg(usb2_susp); - omap_cfg_reg(usb2_seo); - omap_cfg_reg(usb2_txen); - omap_cfg_reg(usb2_txd); - omap_cfg_reg(usb2_vp); - omap_cfg_reg(usb2_vm); - omap_cfg_reg(usb2_rcv); - break; - case 16: - /* 0: host, 1: disabled, 2: disabled */ - omap_cfg_reg(W9_USB0_TXEN); - omap_cfg_reg(AA9_USB0_VP); - omap_cfg_reg(Y5_USB0_RCV); - omap_cfg_reg(R9_USB0_VM); - omap_cfg_reg(V6_USB0_TXD); - omap_cfg_reg(W5_USB0_SE0); - break; - default: - printk("Unknown USB host configuration: %i\n", hmc_mode); - return -ENODEV; - } - - /* Write the selected HMC mode */ - val = readl(MOD_CONF_CTRL_0) & ~HMC_CLEAR; - val |= (hmc_mode << 1); - writel(val, MOD_CONF_CTRL_0); - - return 0; -} - -/* * OHCI clock initialization for OMAP-1510 and 1610 */ static int omap_ohci_clock_power(int on) { if (on) { - if (cpu_is_omap_1510()) { + if (cpu_is_omap1510()) { /* Use DPLL, not APLL */ - writel(readl(ULPD_APLL_CTRL_REG) & ~APLL_NDPLL_SWITCH, - ULPD_APLL_CTRL_REG); + omap_writel(omap_readl(ULPD_APLL_CTRL) & ~APLL_NDPLL_SWITCH, + ULPD_APLL_CTRL); /* Enable DPLL */ - writel(readl(ULPD_DPLL_CTRL_REG) | DPLL_PLL_ENABLE, - ULPD_DPLL_CTRL_REG); + omap_writel(omap_readl(ULPD_DPLL_CTRL) | DPLL_PLL_ENABLE, + ULPD_DPLL_CTRL); /* Software request for USB 48MHz clock */ - writel(readl(ULPD_SOFT_REQ_REG) | SOFT_REQ_REG_REQ, - ULPD_SOFT_REQ_REG); + omap_writel(omap_readl(ULPD_SOFT_REQ) | SOFT_REQ_REG_REQ, + ULPD_SOFT_REQ); - while (!(readl(ULPD_DPLL_CTRL_REG) & DPLL_LOCK)); + while (!(omap_readl(ULPD_DPLL_CTRL) & DPLL_LOCK)); } - if (cpu_is_omap_1610()) { + if (cpu_is_omap16xx()) { /* Enable OHCI */ - writel(readl(ULPD_SOFT_REQ_REG) | SOFT_USB_OTG_REQ, - ULPD_SOFT_REQ_REG); + omap_writel(omap_readl(ULPD_SOFT_REQ) | SOFT_USB_OTG_REQ, + ULPD_SOFT_REQ); /* USB host clock request if not using OTG */ - writel(readl(ULPD_SOFT_REQ_REG) | SOFT_USB_REQ, - ULPD_SOFT_REQ_REG); + omap_writel(omap_readl(ULPD_SOFT_REQ) | SOFT_USB_REQ, + ULPD_SOFT_REQ); - writel(readl(ULPD_STATUS_REQ_REG) | USB_HOST_DPLL_REQ, - ULPD_STATUS_REQ_REG); + omap_writel(omap_readl(ULPD_STATUS_REQ) | USB_HOST_DPLL_REQ, + ULPD_STATUS_REQ); } /* Enable 48MHz clock to USB */ - writel(readl(ULPD_CLOCK_CTRL_REG) | USB_MCLK_EN, - ULPD_CLOCK_CTRL_REG); + omap_writel(omap_readl(ULPD_CLOCK_CTRL) | USB_MCLK_EN, + ULPD_CLOCK_CTRL); - writel(readl(ARM_IDLECT2) | (1 << EN_LBFREECK) | (1 << EN_LBCK), + omap_writel(omap_readl(ARM_IDLECT2) | (1 << EN_LBFREECK) | (1 << EN_LBCK), ARM_IDLECT2); - writel(readl(MOD_CONF_CTRL_0) | USB_HOST_HHC_UHOST_EN, + omap_writel(omap_readl(MOD_CONF_CTRL_0) | USB_HOST_HHC_UHOST_EN, MOD_CONF_CTRL_0); } else { /* Disable 48MHz clock to USB */ - writel(readl(ULPD_CLOCK_CTRL_REG) & ~USB_MCLK_EN, - ULPD_CLOCK_CTRL_REG); + omap_writel(omap_readl(ULPD_CLOCK_CTRL) & ~USB_MCLK_EN, + ULPD_CLOCK_CTRL); /* FIXME: The DPLL stays on for now */ } @@ -163,13 +97,19 @@ static int omap_ohci_clock_power(int on) static int omap_ohci_transceiver_power(int on) { if (on) { - if (omap_is_innovator()) - writel(readl(OMAP1510_FPGA_HOST_CTRL) | 0x20, - OMAP1510_FPGA_HOST_CTRL); + if (machine_is_omap_innovator() && cpu_is_omap1510()) + fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL) | 0x20, + INNOVATOR_FPGA_CAM_USB_CONTROL); + else if (machine_is_omap_osk()) { + /* FIXME: GPIO1 -> 1 on the TPS65010 I2C chip */ + } } else { - if (omap_is_innovator()) - writel(readl(OMAP1510_FPGA_HOST_CTRL) & ~0x20, - OMAP1510_FPGA_HOST_CTRL); + if (machine_is_omap_innovator() && cpu_is_omap1510()) + fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL) & ~0x20, + INNOVATOR_FPGA_CAM_USB_CONTROL); + else if (machine_is_omap_osk()) { + /* FIXME: GPIO1 -> 0 on the TPS65010 I2C chip */ + } } return 0; @@ -181,10 +121,10 @@ static int omap_ohci_transceiver_power(i static int omap_1510_local_bus_power(int on) { if (on) { - writel((1 << 1) | (1 << 0), OMAP1510_LB_MMU_CTL); + omap_writel((1 << 1) | (1 << 0), OMAP1510_LB_MMU_CTL); udelay(200); } else { - writel(0, OMAP1510_LB_MMU_CTL); + omap_writel(0, OMAP1510_LB_MMU_CTL); } return 0; @@ -193,8 +133,8 @@ static int omap_1510_local_bus_power(int /* * OMAP-1510 specific Local Bus initialization * NOTE: This assumes 32MB memory size in OMAP1510LB_MEMSIZE. - * See also arch/mach-omap/memory.h for __virt_to_bus() and - * __bus_to_virt() which need to match with the physical + * See also arch/mach-omap/memory.h for __virt_to_dma() and + * __dma_to_virt() which need to match with the physical * Local Bus address below. */ static int omap_1510_local_bus_init(void) @@ -202,125 +142,115 @@ static int omap_1510_local_bus_init(void unsigned int tlb; unsigned long lbaddr, physaddr; - writel((readl(OMAP1510_LB_CLOCK_DIV) & 0xfffffff8) | 0x4, + omap_writel((omap_readl(OMAP1510_LB_CLOCK_DIV) & 0xfffffff8) | 0x4, OMAP1510_LB_CLOCK_DIV); /* Configure the Local Bus MMU table */ for (tlb = 0; tlb < OMAP1510_LB_MEMSIZE; tlb++) { lbaddr = tlb * 0x00100000 + OMAP1510_LB_OFFSET; physaddr = tlb * 0x00100000 + PHYS_OFFSET; - writel((lbaddr & 0x0fffffff) >> 22, OMAP1510_LB_MMU_CAM_H); - writel(((lbaddr & 0x003ffc00) >> 6) | 0xc, + omap_writel((lbaddr & 0x0fffffff) >> 22, OMAP1510_LB_MMU_CAM_H); + omap_writel(((lbaddr & 0x003ffc00) >> 6) | 0xc, OMAP1510_LB_MMU_CAM_L); - writel(physaddr >> 16, OMAP1510_LB_MMU_RAM_H); - writel((physaddr & 0x0000fc00) | 0x300, OMAP1510_LB_MMU_RAM_L); - writel(tlb << 4, OMAP1510_LB_MMU_LCK); - writel(0x1, OMAP1510_LB_MMU_LD_TLB); + omap_writel(physaddr >> 16, OMAP1510_LB_MMU_RAM_H); + omap_writel((physaddr & 0x0000fc00) | 0x300, OMAP1510_LB_MMU_RAM_L); + omap_writel(tlb << 4, OMAP1510_LB_MMU_LCK); + omap_writel(0x1, OMAP1510_LB_MMU_LD_TLB); } /* Enable the walking table */ - writel(readl(OMAP1510_LB_MMU_CTL) | (1 << 3), OMAP1510_LB_MMU_CTL); + omap_writel(omap_readl(OMAP1510_LB_MMU_CTL) | (1 << 3), OMAP1510_LB_MMU_CTL); udelay(200); return 0; } -/* - * OMAP-1610 specific hardware initialization - * - * Intended to configure OMAP-1610 USB host and OTG ports depending on - * the HMC mode selected. - * - * FIXME: Currently only supports alternate ping group 2 mode, should - * be easy to modify for other configurations once there is some - * hardware to test with. - */ -static int omap_1610_usb_init(int mode) -{ - u_int val = 0; - - /* Configure the OMAP transceiver settings */ - val |= (1 << 8); /* CONF_USB2_UNI TRM p 15-205*/ - val |= (4 << 4); /* TRM p 5-59, p 15-157 (1224) */ - - //val |= (1 << 3); /* Isolate integrated transceiver from port 0 */ - val |= (1 << 2); /* Disable pulldown on integrated transceiver DM */ - val |= (1 << 1); /* Disable pulldown on integraded transceiver DP */ - - writel(val, USB_TRANSCEIVER_CTRL); - - /* Set the USB0_TRX_MODE */ - val = 0; - val &= ~OTG_IDLE_EN; - val &= ~DEV_IDLE_EN; - val &= ~(7 << 16); /* Clear USB0_TRX_MODE */ - val |= (3 << 16); /* 0 or 3, 6-wire DAT/SE0, TRM p 15-159 */ - writel(val, OTG_SYSCON_1); - - /* - * Control via OTG, see TRM p 15-163 - */ - val = 0; - //val |= 1; /* REVISIT: Enable OTG = 1 */ +#ifdef CONFIG_USB_OTG - /* Control via OTG */ - val &= ~HMC_PADEN; - val &= ~OTG_PADEN; - val |= UHOST_EN; - - val &= ~0x3f; /* Clear HMC mode */ - val |= mode; /* Set HMC mode */ - val &= ~(7 << 16); /* Clear ASE0_BRST */ - val |= (4 << 16); /* Must be 4 */ - val |= USBX_SYNCHRO; /* Must be set */ - val |= SRP_VBUS; - writel(val, OTG_SYSCON_2); +static void start_hnp(struct ohci_hcd *ohci) +{ + const unsigned port = ohci->hcd.self.otg_port - 1; + unsigned long flags; - /* Enable OTG idle */ - //writel(readl(OTG_SYSCON_1) | OTG_IDLE_EN, OTG_SYSCON_1); + otg_start_hnp(ohci->transceiver); - return 0; + local_irq_save(flags); + ohci->transceiver->state = OTG_STATE_A_SUSPEND; + writel (RH_PS_PSS, &ohci->regs->roothub.portstatus [port]); + OTG_CTRL_REG &= ~OTG_A_BUSREQ; + local_irq_restore(flags); } +#endif + /*-------------------------------------------------------------------------*/ -static void omap_start_hc(struct omap_dev *dev) +static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev) { - printk(KERN_DEBUG __FILE__ - ": starting OMAP OHCI USB Controller\n"); + struct omap_usb_config *config = pdev->dev.platform_data; + int need_transceiver = (config->otg != 0); - /* - * Set the HMC mode for the USB ports - */ -#if 0 - /* See note about the Innovator wiring above */ - if (omap_is_innovator()) - hmc_mode = 4; /* 0: function 1: host 2: host */ -#endif + dev_dbg(&pdev->dev, "starting USB Controller\n"); + + if (config->otg) { + ohci->hcd.self.otg_port = config->otg; + /* default/minimum OTG power budget: 8 mA */ + ohci->power_budget = 8; + } - if (cpu_is_omap_1610()) + /* boards can use OTG transceivers in non-OTG modes */ + need_transceiver = need_transceiver + || machine_is_omap_h2(); + + if (cpu_is_omap16xx()) ocpi_enable(); - omap_usb_set_hmc_mode(hmc_mode); +#ifdef CONFIG_ARCH_OMAP_OTG + if (need_transceiver) { + ohci->transceiver = otg_get_transceiver(); + if (ohci->transceiver) { + int status = otg_set_host(ohci->transceiver, + &ohci->hcd.self); + dev_dbg(&pdev->dev, "init %s transceiver, status %d\n", + ohci->transceiver->label, status); + if (status) { + if (ohci->transceiver) + put_device(ohci->transceiver->dev); + return status; + } + } else { + dev_err(&pdev->dev, "can't find transceiver\n"); + return -ENODEV; + } + } +#endif + + if (machine_is_omap_osk()) { + omap_request_gpio(9); + omap_set_gpio_direction(9, 1); + omap_set_gpio_dataout(9, 1); + } omap_ohci_clock_power(1); + omap_ohci_transceiver_power(1); - if (cpu_is_omap_1510()) { + if (cpu_is_omap1510()) { omap_1510_local_bus_power(1); omap_1510_local_bus_init(); } - if (cpu_is_omap_1610()) - omap_1610_usb_init(hmc_mode); + /* board init will have already handled HMC and mux setup. + * any external transceiver should already be initialized + * too, so all configured ports use the right signaling now. + */ - //omap_enable_device(dev); + return 0; } -static void omap_stop_hc(struct omap_dev *dev) +static void omap_stop_hc(struct platform_device *pdev) { - printk(KERN_DEBUG __FILE__ - ": stopping OMAP OHCI USB Controller\n"); + dev_dbg(&pdev->dev, "stopping USB Controller\n"); /* * FIXME: Put the USB host controller into reset. @@ -336,16 +266,7 @@ static void omap_stop_hc(struct omap_dev /*-------------------------------------------------------------------------*/ -static irqreturn_t usb_hcd_omap_hcim_irq (int irq, void *__hcd, struct pt_regs * r) -{ - struct usb_hcd *hcd = __hcd; - - return usb_hcd_irq(irq, hcd, r); -} - -/*-------------------------------------------------------------------------*/ - -void usb_hcd_omap_remove (struct usb_hcd *, struct omap_dev *); +void usb_hcd_omap_remove (struct usb_hcd *, struct platform_device *); /* configure so an HC device and id are always provided */ /* always called with process context; sleeping is OK */ @@ -358,59 +279,71 @@ void usb_hcd_omap_remove (struct usb_hcd * Allocates basic resources for this USB host controller, and * then invokes the start() method for the HCD associated with it * through the hotplug entry's driver_data. - * - * Store this function in the HCD's struct pci_driver as probe(). */ int usb_hcd_omap_probe (const struct hc_driver *driver, - struct usb_hcd **hcd_out, - struct omap_dev *dev) + struct platform_device *pdev) { int retval; struct usb_hcd *hcd = 0; + struct ohci_hcd *ohci; - if (!request_mem_region(dev->res.start, - dev->res.end - dev->res.start + 1, hcd_name)) { - dbg("request_mem_region failed"); - return -EBUSY; + if (pdev->num_resources != 2) { + printk(KERN_ERR "hcd probe: invalid num_resources: %i\n", + pdev->num_resources); + return -ENODEV; + } + + if (pdev->resource[0].flags != IORESOURCE_MEM + || pdev->resource[1].flags != IORESOURCE_IRQ) { + printk(KERN_ERR "hcd probe: invalid resource type\n"); + return -ENODEV; } - omap_start_hc(dev); + if (!request_mem_region(pdev->resource[0].start, + pdev->resource[0].end - pdev->resource[0].start + 1, hcd_name)) { + dev_dbg(&pdev->dev, "request_mem_region failed\n"); + return -EBUSY; + } hcd = driver->hcd_alloc (); if (hcd == NULL){ - dbg ("hcd_alloc failed"); + dev_dbg(&pdev->dev, "hcd_alloc failed\n"); retval = -ENOMEM; goto err1; } + dev_set_drvdata(&pdev->dev, hcd); + ohci = hcd_to_ohci(hcd); hcd->driver = (struct hc_driver *) driver; hcd->description = driver->description; - hcd->irq = dev->irq[0]; - hcd->regs = dev->mapbase; - hcd->self.controller = &dev->dev; + hcd->irq = pdev->resource[1].start; + hcd->regs = (void *)pdev->resource[0].start; + hcd->self.controller = &pdev->dev; + + retval = omap_start_hc(ohci, pdev); + if (retval < 0) + goto err2; retval = hcd_buffer_create (hcd); if (retval != 0) { - dbg ("pool alloc fail"); + dev_dbg(&pdev->dev, "pool alloc fail\n"); goto err1; } - retval = request_irq (hcd->irq, - usb_hcd_omap_hcim_irq, + retval = request_irq (hcd->irq, usb_hcd_irq, SA_INTERRUPT, hcd->description, hcd); if (retval != 0) { - dbg("request_irq failed"); + dev_dbg(&pdev->dev, "request_irq failed\n"); retval = -EBUSY; goto err2; } - info ("%s (OMAP) at 0x%p, irq %d\n", - hcd->description, hcd->regs, hcd->irq); + dev_info(&pdev->dev, "at 0x%p, irq %d\n", hcd->regs, hcd->irq); usb_bus_init (&hcd->self); hcd->self.op = &usb_hcd_operations; hcd->self.hcpriv = (void *) hcd; - hcd->self.bus_name = "omap"; + hcd->self.bus_name = pdev->dev.bus_id; hcd->product_desc = "OMAP OHCI"; INIT_LIST_HEAD (&hcd->dev_list); @@ -418,11 +351,10 @@ int usb_hcd_omap_probe (const struct hc_ if ((retval = driver->start (hcd)) < 0) { - usb_hcd_omap_remove(hcd, dev); + usb_hcd_omap_remove(hcd, pdev); return retval; } - *hcd_out = hcd; return 0; err2: @@ -430,10 +362,12 @@ int usb_hcd_omap_probe (const struct hc_ if (hcd) driver->hcd_free(hcd); err1: - omap_stop_hc(dev); + omap_stop_hc(pdev); - release_mem_region(dev->res.start, dev->res.end - dev->res.start + 1); + release_mem_region(pdev->resource[0].start, + pdev->resource[0].end - pdev->resource[0].start + 1); + dev_set_drvdata(&pdev->dev, 0); return retval; } @@ -451,24 +385,27 @@ int usb_hcd_omap_probe (const struct hc_ * context, normally "rmmod", "apmd", or something similar. * */ -void usb_hcd_omap_remove (struct usb_hcd *hcd, struct omap_dev *dev) +void usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev) { void *base; - info ("remove: %s, state %x", hcd->self.bus_name, hcd->state); + dev_info(&pdev->dev, "remove: state %x\n", hcd->state); if (in_interrupt ()) BUG (); hcd->state = USB_STATE_QUIESCING; - dbg ("%s: roothub graceful disconnect", hcd->self.bus_name); + dev_dbg(&pdev->dev, "roothub graceful disconnect\n"); usb_disconnect (&hcd->self.root_hub); hcd->driver->stop (hcd); hcd_buffer_destroy (hcd); hcd->state = USB_STATE_HALT; + if (machine_is_omap_osk()) + omap_free_gpio(9); + free_irq (hcd->irq, hcd); usb_deregister_bus (&hcd->self); @@ -476,9 +413,10 @@ void usb_hcd_omap_remove (struct usb_hcd base = hcd->regs; hcd->driver->hcd_free (hcd); - omap_stop_hc(dev); + omap_stop_hc(pdev); - release_mem_region(dev->res.start, dev->res.end - dev->res.start + 1); + release_mem_region(pdev->resource[0].start, + pdev->resource[0].end - pdev->resource[0].start + 1); } /*-------------------------------------------------------------------------*/ @@ -486,11 +424,13 @@ void usb_hcd_omap_remove (struct usb_hcd static int __devinit ohci_omap_start (struct usb_hcd *hcd) { + struct omap_usb_config *config; struct ohci_hcd *ohci = hcd_to_ohci (hcd); int ret; - ohci->hcca = dma_alloc_consistent (hcd->self.controller, - sizeof *ohci->hcca, &ohci->hcca_dma); + config = hcd->self.controller->platform_data; + ohci->hcca = dma_alloc_coherent (hcd->self.controller, + sizeof *ohci->hcca, &ohci->hcca_dma, 0); if (!ohci->hcca) return -ENOMEM; @@ -500,6 +440,10 @@ ohci_omap_start (struct usb_hcd *hcd) return ret; } ohci->regs = hcd->regs; + + if (config->otg || config->rwc) + writel(OHCI_CTRL_RWC, &ohci->regs->control); + if (hc_reset (ohci) < 0) { ohci_stop (hcd); return -ENODEV; @@ -510,6 +454,9 @@ ohci_omap_start (struct usb_hcd *hcd) ohci_stop (hcd); return -EBUSY; } + if (ohci->power_budget) + hub_set_power_budget(ohci->hcd.self.root_hub, + ohci->power_budget); create_debug_files (ohci); #ifdef DEBUG @@ -533,10 +480,6 @@ static const struct hc_driver ohci_omap_ * basic lifecycle operations */ .start = ohci_omap_start, -#ifdef CONFIG_PM - /* suspend: ohci_omap_suspend, -- tbd */ - /* resume: ohci_omap_resume, -- tbd */ -#endif .stop = ohci_stop, /* @@ -566,104 +509,130 @@ static const struct hc_driver ohci_omap_ .hub_suspend = ohci_hub_suspend, .hub_resume = ohci_hub_resume, #endif + .start_port_reset = ohci_start_port_reset, }; /*-------------------------------------------------------------------------*/ -static int ohci_hcd_omap_drv_probe(struct omap_dev *dev) +static int ohci_hcd_omap_drv_probe(struct device *dev) { - struct usb_hcd *hcd = NULL; - int ret; - - if (usb_disabled()) - return -ENODEV; + return usb_hcd_omap_probe(&ohci_omap_hc_driver, + to_platform_device(dev)); +} - ret = usb_hcd_omap_probe(&ohci_omap_hc_driver, &hcd, dev); +static int ohci_hcd_omap_drv_remove(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct ohci_hcd *ohci = hcd_to_ohci (hcd); - if (ret == 0) - omap_set_drvdata(dev, hcd); + usb_hcd_omap_remove(hcd, pdev); + if (ohci->transceiver) { + (void) otg_set_host(ohci->transceiver, 0); + put_device(ohci->transceiver->dev); + } + dev_set_drvdata(dev, NULL); - return ret; + return 0; } -static int ohci_hcd_omap_drv_remove(struct omap_dev *dev) +/*-------------------------------------------------------------------------*/ + +#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM) + +/* states match PCI usage, always suspending the root hub except that + * 4 ~= D3cold (ACPI D3) with clock off (resume sees reset). + */ + +static int ohci_omap_suspend(struct device *dev, u32 state, u32 level) { - struct usb_hcd *hcd = omap_get_drvdata(dev); + struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev)); + int status = -EINVAL; - usb_hcd_omap_remove(hcd, dev); + if (state <= dev->power.power_state) + return 0; - omap_set_drvdata(dev, NULL); + dev_dbg(dev, "suspend to %d\n", state); + down(&ohci->hcd.self.root_hub->serialize); + status = ohci_hub_suspend(&ohci->hcd); + if (status == 0) { + if (state >= 4) { + /* power off + reset */ + OTG_SYSCON_2_REG &= ~UHOST_EN; + ohci->hcd.self.root_hub->state = USB_STATE_SUSPENDED; + state = 4; + } + ohci->hcd.state = HCD_STATE_SUSPENDED; + dev->power.power_state = state; + } + up(&ohci->hcd.self.root_hub->serialize); + return status; +} - return 0; +static int ohci_omap_resume(struct device *dev, u32 level) +{ + struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev)); + int status = 0; + + switch (dev->power.power_state) { + case 0: + break; + case 4: + if (time_before(jiffies, ohci->next_statechange)) + msleep(5); + ohci->next_statechange = jiffies; + OTG_SYSCON_2_REG |= UHOST_EN; + /* FALLTHROUGH */ + default: + dev_dbg(dev, "resume from %d\n", dev->power.power_state); +#ifdef CONFIG_USB_SUSPEND + /* get extra cleanup even if remote wakeup isn't in use */ + status = usb_resume_device(ohci->hcd.self.root_hub); +#else + down(&ohci->hcd.self.root_hub->serialize); + status = ohci_hub_resume(&ohci->hcd); + up(&ohci->hcd.self.root_hub->serialize); +#endif + if (status == 0) + dev->power.power_state = 0; + break; + } + return status; } +#endif + +/*-------------------------------------------------------------------------*/ + /* * Driver definition to register with the OMAP bus */ -static struct omap_driver ohci_hcd_omap_driver = { - .drv = { - .name = OMAP_OHCI_NAME, - }, - .devid = OMAP_OCP_DEVID_USB, - .busid = OMAP_BUS_OCP, - .clocks = 0, +static struct device_driver ohci_hcd_omap_driver = { + .name = "ohci", + .bus = &platform_bus_type, .probe = ohci_hcd_omap_drv_probe, .remove = ohci_hcd_omap_drv_remove, -}; - -/* Any dma_mask must be set for OHCI to work */ -static u64 omap_dmamask = 0xffffffffUL; - -/* - * Device definition to match the driver above - */ -static struct omap_dev ohci_hcd_omap_device = { - .name = OMAP_OHCI_NAME, - .devid = OMAP_OCP_DEVID_USB, - .busid = OMAP_BUS_OCP, - .mapbase = (void *)OMAP_OHCI_BASE, - .dma_mask = &omap_dmamask, /* Needed only for OHCI */ - .res = { - .start = OMAP_OHCI_BASE, - .end = OMAP_OHCI_BASE + OMAP_OHCI_SIZE, - }, - .irq = { - INT_USB_HHC_1, - }, +#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM) + .suspend = ohci_omap_suspend, + .resume = ohci_omap_resume, +#endif }; static int __init ohci_hcd_omap_init (void) { - int ret; - - dbg (DRIVER_INFO " (OMAP)"); - dbg ("block sizes: ed %d td %d\n", - sizeof (struct ed), sizeof (struct td)); - - if (hmc_mode < 0 || hmc_mode > 25) - hmc_mode = default_hmc_mode; - - /* Register the driver with OMAP bus */ - ret = omap_driver_register(&ohci_hcd_omap_driver); - if (ret != 0) + printk (KERN_DEBUG "%s: " DRIVER_INFO " (OMAP)\n", hcd_name); + if (usb_disabled()) return -ENODEV; - /* Register the device with OMAP bus */ - ret = omap_device_register(&ohci_hcd_omap_device); - if (ret != 0) { - omap_driver_unregister(&ohci_hcd_omap_driver); - return -ENODEV; - } + pr_debug("%s: block sizes: ed %Zd td %Zd\n", hcd_name, + sizeof (struct ed), sizeof (struct td)); - return ret; + return driver_register(&ohci_hcd_omap_driver); } -module_param(hmc_mode, int, 0); - static void __exit ohci_hcd_omap_cleanup (void) { - omap_device_unregister(&ohci_hcd_omap_device); - omap_driver_unregister(&ohci_hcd_omap_driver); + driver_unregister(&ohci_hcd_omap_driver); } module_init (ohci_hcd_omap_init); --- linux-2.6.9-rc1/drivers/usb/host/ohci-omap.h 2004-08-24 00:02:48.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,57 +0,0 @@ -/* - * linux/drivers/usb/host/ohci-omap.h - * - * OMAP OHCI USB controller specific defines - */ - -/* OMAP USB OHCI common defines */ -#define OMAP_OHCI_NAME "omap-ohci" -#define OMAP_OHCI_BASE 0xfffba000 -#define OMAP_OHCI_SIZE 4096 - -#define HMC_CLEAR (0x3f << 1) -#define APLL_NDPLL_SWITCH 0x0001 -#define DPLL_PLL_ENABLE 0x0010 -#define DPLL_LOCK 0x0001 -#define SOFT_REQ_REG_REQ 0x0001 -#define USB_MCLK_EN 0x0010 -#define USB_HOST_HHC_UHOST_EN 0x00000200 -#define SOFT_USB_OTG_REQ (1 << 8) -#define SOFT_USB_REQ (1 << 3) -#define STATUS_REQ_REG 0xfffe0840 -#define USB_HOST_DPLL_REQ (1 << 8) -#define SOFT_DPLL_REQ (1 << 0) - -/* OMAP-1510 USB OHCI defines */ -#define OMAP1510_LB_MEMSIZE 32 /* Should be same as SDRAM size */ -#define OMAP1510_LB_CLOCK_DIV 0xfffec10c -#define OMAP1510_LB_MMU_CTL 0xfffec208 -#define OMAP1510_LB_MMU_LCK 0xfffec224 -#define OMAP1510_LB_MMU_LD_TLB 0xfffec228 -#define OMAP1510_LB_MMU_CAM_H 0xfffec22c -#define OMAP1510_LB_MMU_CAM_L 0xfffec230 -#define OMAP1510_LB_MMU_RAM_H 0xfffec234 -#define OMAP1510_LB_MMU_RAM_L 0xfffec238 - -/* OMAP-1610 USB OHCI defines */ -#define USB_TRANSCEIVER_CTRL 0xfffe1064 -#define OTG_REV 0xfffb0400 - -#define OTG_SYSCON_1 0xfffb0404 -#define OTG_IDLE_EN (1 << 15) -#define DEV_IDLE_EN (1 << 13) - -#define OTG_SYSCON_2 0xfffb0408 -#define OTG_CTRL 0xfffb040c -#define OTG_IRQ_EN 0xfffb0410 -#define OTG_IRQ_SRC 0xfffb0414 - -#define OTG_EN (1 << 31) -#define USBX_SYNCHRO (1 << 30) -#define SRP_VBUS (1 << 12) -#define OTG_PADEN (1 << 10) -#define HMC_PADEN (1 << 9) -#define UHOST_EN (1 << 8) - -/* Hardware specific defines */ -#define OMAP1510_FPGA_HOST_CTRL 0xe800020c --- linux-2.6.9-rc1/drivers/usb/host/ohci-pci.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/drivers/usb/host/ohci-pci.c 2004-09-02 20:51:52.000000000 -0700 @@ -242,6 +242,7 @@ static const struct hc_driver ohci_pci_h .hub_suspend = ohci_hub_suspend, .hub_resume = ohci_hub_resume, #endif + .start_port_reset = ohci_start_port_reset, }; /*-------------------------------------------------------------------------*/ @@ -276,7 +277,7 @@ static int __init ohci_hcd_pci_init (voi if (usb_disabled()) return -ENODEV; - printk (KERN_DEBUG "%s: block sizes: ed %Zd td %Zd\n", hcd_name, + pr_debug ("%s: block sizes: ed %Zd td %Zd\n", hcd_name, sizeof (struct ed), sizeof (struct td)); return pci_module_init (&ohci_pci_driver); } --- linux-2.6.9-rc1/drivers/usb/host/ohci-q.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/drivers/usb/host/ohci-q.c 2004-09-02 20:51:52.000000000 -0700 @@ -900,9 +900,6 @@ static struct td *dl_reverse_done_list ( /*-------------------------------------------------------------------------*/ -/* wrap-aware logic stolen from */ -#define tick_before(t1,t2) ((((s16)(t1))-((s16)(t2))) < 0) - /* there are some urbs/eds to unlink; called in_irq(), with HCD locked */ static void finish_unlinks (struct ohci_hcd *ohci, u16 tick, struct pt_regs *regs) --- linux-2.6.9-rc1/drivers/usb/host/uhci-hcd.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/usb/host/uhci-hcd.c 2004-09-02 20:51:52.000000000 -0700 @@ -1340,7 +1340,7 @@ static struct urb *uhci_find_urb_ep(stru static int uhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, int mem_flags) { - int ret = -EINVAL; + int ret; struct uhci_hcd *uhci = hcd_to_uhci(hcd); unsigned long flags; struct urb *eurb; @@ -1348,7 +1348,8 @@ static int uhci_urb_enqueue(struct usb_h spin_lock_irqsave(&uhci->schedule_lock, flags); - if (urb->status != -EINPROGRESS) /* URB already unlinked! */ + ret = urb->status; + if (ret != -EINPROGRESS) /* URB already unlinked! */ goto out; eurb = uhci_find_urb_ep(uhci, urb); @@ -1633,13 +1634,6 @@ static void stall_callback(unsigned long if (up->fsbr && !up->fsbr_timeout && time_after_eq(jiffies, up->fsbrtime + IDLE_TIMEOUT)) uhci_fsbr_timeout(uhci, u); - /* Check if the URB timed out */ - if (u->timeout && u->status == -EINPROGRESS && - time_after_eq(jiffies, up->inserttime + u->timeout)) { - u->status = -ETIMEDOUT; - list_move_tail(&up->urb_list, &list); - } - spin_unlock(&u->lock); } spin_unlock_irqrestore(&uhci->schedule_lock, flags); @@ -2254,7 +2248,8 @@ static int uhci_start(struct usb_hcd *hc irq = 7; /* Only place we don't use the frame list routines */ - uhci->fl->frame[i] = cpu_to_le32(uhci->skelqh[irq]->dma_handle); + uhci->fl->frame[i] = UHCI_PTR_QH | + cpu_to_le32(uhci->skelqh[irq]->dma_handle); } /* --- linux-2.6.9-rc1/drivers/usb/input/ati_remote.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/drivers/usb/input/ati_remote.c 2004-09-02 20:51:52.000000000 -0700 @@ -112,11 +112,11 @@ #define ATI_INPUTNUM 1 /* Which input device to register as */ static unsigned long channel_mask = 0; -module_param(channel_mask, ulong, 444); +module_param(channel_mask, ulong, 0444); MODULE_PARM_DESC(channel_mask, "Bitmask of remote control channels to ignore"); static int debug = 0; -module_param(debug, int, 444); +module_param(debug, int, 0444); MODULE_PARM_DESC(debug, "Enable extra debug messages and information"); #define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0) --- linux-2.6.9-rc1/drivers/usb/input/hid-core.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/usb/input/hid-core.c 2004-09-03 00:56:31.587358640 -0700 @@ -219,17 +219,13 @@ static int hid_add_field(struct hid_pars dbg("logical range invalid %d %d", parser->global.logical_minimum, parser->global.logical_maximum); return -1; } - usages = parser->local.usage_index; + + if (!(usages = max_t(int, parser->local.usage_index, parser->global.report_count))) + return 0; /* Ignore padding fields */ offset = report->size; report->size += parser->global.report_size * parser->global.report_count; - if (usages < parser->global.report_count) - usages = parser->global.report_count; - - if (usages == 0) - return 0; /* ignore padding fields */ - if ((field = hid_register_field(report, usages, parser->global.report_count)) == NULL) return 0; @@ -923,20 +919,20 @@ static void hid_irq_in(struct urb *urb, int status; switch (urb->status) { - case 0: /* success */ - hid_input_report(HID_INPUT_REPORT, urb, regs); - break; - case -ECONNRESET: /* unlink */ - case -ENOENT: - case -ESHUTDOWN: - return; - default: /* error */ - dbg("nonzero status in input irq %d", urb->status); + case 0: /* success */ + hid_input_report(HID_INPUT_REPORT, urb, regs); + break; + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + warn("input irq status %d received", urb->status); } - status = usb_submit_urb (urb, SLAB_ATOMIC); + status = usb_submit_urb(urb, SLAB_ATOMIC); if (status) - err ("can't resubmit intr, %s-%s/input%d, status %d", + err("can't resubmit intr, %s-%s/input%d, status %d", hid->dev->bus->bus_name, hid->dev->devpath, hid->ifnum, status); } @@ -1137,23 +1133,31 @@ static void hid_irq_out(struct urb *urb, struct hid_device *hid = urb->context; unsigned long flags; - if (urb->status) - warn("output irq status %d received", urb->status); + switch (urb->status) { + case 0: /* success */ + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -ESHUTDOWN: + break; + default: /* error */ + warn("output irq status %d received", urb->status); + } spin_lock_irqsave(&hid->outlock, flags); hid->outtail = (hid->outtail + 1) & (HID_OUTPUT_FIFO_SIZE - 1); if (hid->outhead != hid->outtail) { - hid_submit_out(hid); + if (hid_submit_out(hid)) { + clear_bit(HID_OUT_RUNNING, &hid->iofl);; + wake_up(&hid->wait); + } spin_unlock_irqrestore(&hid->outlock, flags); return; } clear_bit(HID_OUT_RUNNING, &hid->iofl); - spin_unlock_irqrestore(&hid->outlock, flags); - wake_up(&hid->wait); } @@ -1166,26 +1170,34 @@ static void hid_ctrl(struct urb *urb, st struct hid_device *hid = urb->context; unsigned long flags; - if (urb->status) - warn("ctrl urb status %d received", urb->status); - spin_lock_irqsave(&hid->ctrllock, flags); - if (hid->ctrl[hid->ctrltail].dir == USB_DIR_IN) - hid_input_report(hid->ctrl[hid->ctrltail].report->type, urb, regs); + switch (urb->status) { + case 0: /* success */ + if (hid->ctrl[hid->ctrltail].dir == USB_DIR_IN) + hid_input_report(hid->ctrl[hid->ctrltail].report->type, urb, regs); + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -ESHUTDOWN: + case -EPIPE: /* report not available */ + break; + default: /* error */ + warn("ctrl urb status %d received", urb->status); + } hid->ctrltail = (hid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1); if (hid->ctrlhead != hid->ctrltail) { - hid_submit_ctrl(hid); + if (hid_submit_ctrl(hid)) { + clear_bit(HID_CTRL_RUNNING, &hid->iofl); + wake_up(&hid->wait); + } spin_unlock_irqrestore(&hid->ctrllock, flags); return; } clear_bit(HID_CTRL_RUNNING, &hid->iofl); - spin_unlock_irqrestore(&hid->ctrllock, flags); - wake_up(&hid->wait); } @@ -1211,7 +1223,8 @@ void hid_submit_report(struct hid_device hid->outhead = head; if (!test_and_set_bit(HID_OUT_RUNNING, &hid->iofl)) - hid_submit_out(hid); + if (hid_submit_out(hid)) + clear_bit(HID_OUT_RUNNING, &hid->iofl); spin_unlock_irqrestore(&hid->outlock, flags); return; @@ -1230,7 +1243,8 @@ void hid_submit_report(struct hid_device hid->ctrlhead = head; if (!test_and_set_bit(HID_CTRL_RUNNING, &hid->iofl)) - hid_submit_ctrl(hid); + if (hid_submit_ctrl(hid)) + clear_bit(HID_CTRL_RUNNING, &hid->iofl); spin_unlock_irqrestore(&hid->ctrllock, flags); } @@ -1282,7 +1296,7 @@ int hid_open(struct hid_device *hid) void hid_close(struct hid_device *hid) { if (!--hid->open) - usb_unlink_urb(hid->urbin); + usb_kill_urb(hid->urbin); } /* @@ -1439,6 +1453,11 @@ void hid_init_reports(struct hid_device #define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101 #define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104 +#define USB_VENDOR_ID_CODEMERCS 0x07c0 +#define USB_DEVICE_ID_CODEMERCS_IOW40 0x1500 +#define USB_DEVICE_ID_CODEMERCS_IOW24 0x1501 + + static struct hid_blacklist { __u16 idVendor; __u16 idProduct; @@ -1453,20 +1472,20 @@ static struct hid_blacklist { { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_PENPARTNER, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE + 1, HID_QUIRK_IGNORE }, @@ -1644,7 +1663,7 @@ static struct hid_device *usb_hid_config usb_fill_int_urb(hid->urbin, dev, pipe, hid->inbuf, len, hid_irq_in, hid, interval); hid->urbin->transfer_dma = hid->inbuf_dma; - hid->urbin->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + hid->urbin->transfer_flags |=(URB_NO_TRANSFER_DMA_MAP | URB_ASYNC_UNLINK); } else { if (hid->urbout) continue; @@ -1654,7 +1673,7 @@ static struct hid_device *usb_hid_config usb_fill_int_urb(hid->urbout, dev, pipe, hid->outbuf, 0, hid_irq_out, hid, interval); hid->urbout->transfer_dma = hid->outbuf_dma; - hid->urbout->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + hid->urbout->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_ASYNC_UNLINK); } } @@ -1704,8 +1723,7 @@ static struct hid_device *usb_hid_config hid->ctrlbuf, 1, hid_ctrl, hid); hid->urbctrl->setup_dma = hid->cr_dma; hid->urbctrl->transfer_dma = hid->ctrlbuf_dma; - hid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP - | URB_NO_SETUP_DMA_MAP); + hid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP | URB_ASYNC_UNLINK); return hid; @@ -1731,9 +1749,9 @@ static void hid_disconnect(struct usb_in return; usb_set_intfdata(intf, NULL); - usb_unlink_urb(hid->urbin); - usb_unlink_urb(hid->urbout); - usb_unlink_urb(hid->urbctrl); + usb_kill_urb(hid->urbin); + usb_kill_urb(hid->urbout); + usb_kill_urb(hid->urbctrl); if (hid->claimed & HID_CLAIMED_INPUT) hidinput_disconnect(hid); --- linux-2.6.9-rc1/drivers/usb/input/hiddev.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/drivers/usb/input/hiddev.c 2004-09-03 00:56:31.588358488 -0700 @@ -223,16 +223,6 @@ static int hiddev_fasync(int fd, struct return retval < 0 ? retval : 0; } -/* - * De-allocate a hiddev structure - */ -static struct usb_class_driver hiddev_class; -static void hiddev_cleanup(struct hiddev *hiddev) -{ - hiddev_table[hiddev->hid->minor - HIDDEV_MINOR_BASE] = NULL; - usb_deregister_dev(hiddev->hid->intf, &hiddev_class); - kfree(hiddev); -} /* * release file op @@ -253,7 +243,7 @@ static int hiddev_release(struct inode * if (list->hiddev->exist) hid_close(list->hiddev->hid); else - hiddev_cleanup(list->hiddev); + kfree(list->hiddev); } kfree(list); @@ -636,16 +626,19 @@ static int hiddev_ioctl(struct inode *in goto inval; field = report->field[uref->field_index]; - if (uref->usage_index >= field->maxusage) - goto inval; - if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) { - if (uref_multi->num_values >= HID_MAX_USAGES || - uref->usage_index >= field->maxusage || - (uref->usage_index + uref_multi->num_values) >= field->maxusage) + if (cmd == HIDIOCGCOLLECTIONINDEX) { + if (uref->usage_index >= field->maxusage) goto inval; + } else if (uref->usage_index >= field->report_count) + goto inval; + + else if ((cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) && + (uref_multi->num_values >= HID_MAX_MULTI_USAGES || + uref->usage_index + uref_multi->num_values >= field->report_count || + uref->usage_index + uref_multi->num_values < uref->usage_index)) + goto inval; } - } switch (cmd) { case HIDIOCGUSAGE: @@ -795,17 +788,21 @@ int hiddev_connect(struct hid_device *hi * This is where hid.c calls us to disconnect a hiddev device from the * corresponding hid device (usually because the usb device has disconnected) */ +static struct usb_class_driver hiddev_class; void hiddev_disconnect(struct hid_device *hid) { struct hiddev *hiddev = hid->hiddev; hiddev->exist = 0; + hiddev_table[hiddev->hid->minor - HIDDEV_MINOR_BASE] = NULL; + usb_deregister_dev(hiddev->hid->intf, &hiddev_class); + if (hiddev->open) { hid_close(hiddev->hid); wake_up_interruptible(&hiddev->wait); } else { - hiddev_cleanup(hiddev); + kfree(hiddev); } } --- linux-2.6.9-rc1/drivers/usb/media/Kconfig 2004-08-24 00:01:51.000000000 -0700 +++ 25/drivers/usb/media/Kconfig 2004-09-02 20:51:52.000000000 -0700 @@ -106,46 +106,6 @@ config USB_OV511 To compile this driver as a module, choose M here: the module will be called ov511. -config USB_PWC - tristate "USB Philips Cameras" - depends on USB && VIDEO_DEV - ---help--- - Say Y or M here if you want to use one of these Philips & OEM - webcams: - * Philips PCA645, PCA646 - * Philips PCVC675, PCVC680, PCVC690 - * Philips PCVC720/40, PCVC730, PCVC740, PCVC750 - * Askey VC010 - * Logitech QuickCam Pro 3000, 4000, 'Zoom', 'Notebook Pro' - and 'Orbit'/'Sphere' - * Samsung MPC-C10, MPC-C30 - * Creative Webcam 5, Pro Ex - * SOTEC Afina Eye - * Visionite VCS-UC300, VCS-UM100 - - The PCA635, PCVC665 and PCVC720/20 are not supported by this driver - and never will be, but the 665 and 720/20 are supported by other - drivers. - - This driver has an optional plugin (called PWCX), which is - distributed as a binary module only. It contains code that allow you - to use higher resolutions and framerates but may not be distributed - as source. But even without this plugin you can these cams for most - applications. - - See for more information and - installation instructions. - - The built-in microphone is enabled by selecting USB Audio support. - - This driver uses the Video For Linux API. You must say Y or M to - "Video For Linux" (under Character Devices) to use this driver. - Information on this API and pointers to "v4l" programs may be found - at . - - To compile this driver as a module, choose M here: the - module will be called pwc. - config USB_SE401 tristate "USB SE401 Camera support" depends on USB && VIDEO_DEV --- linux-2.6.9-rc1/drivers/usb/media/konicawc.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/drivers/usb/media/konicawc.c 2004-09-03 00:56:53.049095960 -0700 @@ -362,8 +362,8 @@ static void konicawc_isoc_irq(struct urb else if (!urb->status && !cam->last_data_urb->status) len = konicawc_compress_iso(uvd, cam->last_data_urb, urb); - resubmit_urb(uvd, urb); resubmit_urb(uvd, cam->last_data_urb); + resubmit_urb(uvd, urb); cam->last_data_urb = NULL; uvd->stats.urb_length = len; uvd->stats.data_count += len; --- linux-2.6.9-rc1/drivers/usb/media/Makefile 2004-08-24 00:01:50.000000000 -0700 +++ 25/drivers/usb/media/Makefile 2004-09-02 20:51:52.000000000 -0700 @@ -2,7 +2,6 @@ # Makefile for USB Media drivers # -pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-uncompress.o sn9c102-objs := sn9c102_core.o sn9c102_pas106b.o sn9c102_tas5110c1b.o sn9c102_tas5130d1b.o sn9c102_pas202bcb.o obj-$(CONFIG_USB_DABUSB) += dabusb.o @@ -10,7 +9,6 @@ obj-$(CONFIG_USB_DSBR) += dsbr100.o obj-$(CONFIG_USB_IBMCAM) += ibmcam.o usbvideo.o ultracam.o obj-$(CONFIG_USB_KONICAWC) += konicawc.o usbvideo.o obj-$(CONFIG_USB_OV511) += ov511.o -obj-$(CONFIG_USB_PWC) += pwc.o obj-$(CONFIG_USB_SE401) += se401.o obj-$(CONFIG_USB_SN9C102) += sn9c102.o obj-$(CONFIG_USB_STV680) += stv680.o --- linux-2.6.9-rc1/drivers/usb/media/pwc-ctrl.c 2004-08-24 00:03:38.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,1644 +0,0 @@ -/* Driver for Philips webcam - Functions that send various control messages to the webcam, including - video modes. - (C) 1999-2003 Nemosoft Unv. (webcam@smcc.demon.nl) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -/* - Changes - 2001/08/03 Alvarado Added methods for changing white balance and - red/green gains - */ - -/* Control functions for the cam; brightness, contrast, video mode, etc. */ - -#ifdef __KERNEL__ -#include -#endif -#include -#include - -#include "pwc.h" -#include "pwc-ioctl.h" -#include "pwc-uncompress.h" - -/* Request types: video */ -#define SET_LUM_CTL 0x01 -#define GET_LUM_CTL 0x02 -#define SET_CHROM_CTL 0x03 -#define GET_CHROM_CTL 0x04 -#define SET_STATUS_CTL 0x05 -#define GET_STATUS_CTL 0x06 -#define SET_EP_STREAM_CTL 0x07 -#define GET_EP_STREAM_CTL 0x08 -#define SET_MPT_CTL 0x0D -#define GET_MPT_CTL 0x0E - -/* Selectors for the Luminance controls [GS]ET_LUM_CTL */ -#define AGC_MODE_FORMATTER 0x2000 -#define PRESET_AGC_FORMATTER 0x2100 -#define SHUTTER_MODE_FORMATTER 0x2200 -#define PRESET_SHUTTER_FORMATTER 0x2300 -#define PRESET_CONTOUR_FORMATTER 0x2400 -#define AUTO_CONTOUR_FORMATTER 0x2500 -#define BACK_LIGHT_COMPENSATION_FORMATTER 0x2600 -#define CONTRAST_FORMATTER 0x2700 -#define DYNAMIC_NOISE_CONTROL_FORMATTER 0x2800 -#define FLICKERLESS_MODE_FORMATTER 0x2900 -#define AE_CONTROL_SPEED 0x2A00 -#define BRIGHTNESS_FORMATTER 0x2B00 -#define GAMMA_FORMATTER 0x2C00 - -/* Selectors for the Chrominance controls [GS]ET_CHROM_CTL */ -#define WB_MODE_FORMATTER 0x1000 -#define AWB_CONTROL_SPEED_FORMATTER 0x1100 -#define AWB_CONTROL_DELAY_FORMATTER 0x1200 -#define PRESET_MANUAL_RED_GAIN_FORMATTER 0x1300 -#define PRESET_MANUAL_BLUE_GAIN_FORMATTER 0x1400 -#define COLOUR_MODE_FORMATTER 0x1500 -#define SATURATION_MODE_FORMATTER1 0x1600 -#define SATURATION_MODE_FORMATTER2 0x1700 - -/* Selectors for the Status controls [GS]ET_STATUS_CTL */ -#define SAVE_USER_DEFAULTS_FORMATTER 0x0200 -#define RESTORE_USER_DEFAULTS_FORMATTER 0x0300 -#define RESTORE_FACTORY_DEFAULTS_FORMATTER 0x0400 -#define READ_AGC_FORMATTER 0x0500 -#define READ_SHUTTER_FORMATTER 0x0600 -#define READ_RED_GAIN_FORMATTER 0x0700 -#define READ_BLUE_GAIN_FORMATTER 0x0800 -#define SENSOR_TYPE_FORMATTER1 0x0C00 -#define READ_RAW_Y_MEAN_FORMATTER 0x3100 -#define SET_POWER_SAVE_MODE_FORMATTER 0x3200 -#define MIRROR_IMAGE_FORMATTER 0x3300 -#define LED_FORMATTER 0x3400 -#define SENSOR_TYPE_FORMATTER2 0x3700 - -/* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */ -#define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100 - -/* Formatters for the motorized pan & tilt [GS]ET_MPT_CTL */ -#define PT_RELATIVE_CONTROL_FORMATTER 0x01 -#define PT_RESET_CONTROL_FORMATTER 0x02 -#define PT_STATUS_FORMATTER 0x03 - -static char *size2name[PSZ_MAX] = -{ - "subQCIF", - "QSIF", - "QCIF", - "SIF", - "CIF", - "VGA", -}; - -/********/ - -/* Entries for the Nala (645/646) camera; the Nala doesn't have compression - preferences, so you either get compressed or non-compressed streams. - - An alternate value of 0 means this mode is not available at all. - */ - -struct Nala_table_entry { - char alternate; /* USB alternate setting */ - int compressed; /* Compressed yes/no */ - - unsigned char mode[3]; /* precomputed mode table */ -}; - -static struct Nala_table_entry Nala_table[PSZ_MAX][8] = -{ -#include "pwc_nala.h" -}; - -/* This tables contains entries for the 675/680/690 (Timon) camera, with - 4 different qualities (no compression, low, medium, high). - It lists the bandwidth requirements for said mode by its alternate interface - number. An alternate of 0 means that the mode is unavailable. - - There are 6 * 4 * 4 entries: - 6 different resolutions subqcif, qsif, qcif, sif, cif, vga - 6 framerates: 5, 10, 15, 20, 25, 30 - 4 compression modi: none, low, medium, high - - When an uncompressed mode is not available, the next available compressed mode - will be chosen (unless the decompressor is absent). Sometimes there are only - 1 or 2 compressed modes available; in that case entries are duplicated. -*/ -struct Timon_table_entry -{ - char alternate; /* USB alternate interface */ - unsigned short packetsize; /* Normal packet size */ - unsigned short bandlength; /* Bandlength when decompressing */ - unsigned char mode[13]; /* precomputed mode settings for cam */ -}; - -static struct Timon_table_entry Timon_table[PSZ_MAX][6][4] = -{ -#include "pwc_timon.h" -}; - -/* Entries for the Kiara (730/740/750) camera */ - -struct Kiara_table_entry -{ - char alternate; /* USB alternate interface */ - unsigned short packetsize; /* Normal packet size */ - unsigned short bandlength; /* Bandlength when decompressing */ - unsigned char mode[12]; /* precomputed mode settings for cam */ -}; - -static struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4] = -{ -#include "pwc_kiara.h" -}; - - -/****************************************************************************/ - - -#define SendControlMsg(request, value, buflen) \ - usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), \ - request, \ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, \ - value, \ - pdev->vcinterface, \ - &buf, buflen, HZ / 2) - -#define RecvControlMsg(request, value, buflen) \ - usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), \ - request, \ - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, \ - value, \ - pdev->vcinterface, \ - &buf, buflen, HZ / 2) - - -#if PWC_DEBUG -void pwc_hexdump(void *p, int len) -{ - int i; - unsigned char *s; - char buf[100], *d; - - s = (unsigned char *)p; - d = buf; - *d = '\0'; - Debug("Doing hexdump @ %p, %d bytes.\n", p, len); - for (i = 0; i < len; i++) { - d += sprintf(d, "%02X ", *s++); - if ((i & 0xF) == 0xF) { - Debug("%s\n", buf); - d = buf; - *d = '\0'; - } - } - if ((i & 0xF) != 0) - Debug("%s\n", buf); -} -#endif - -static inline int send_video_command(struct usb_device *udev, int index, void *buf, int buflen) -{ - return usb_control_msg(udev, - usb_sndctrlpipe(udev, 0), - SET_EP_STREAM_CTL, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - VIDEO_OUTPUT_CONTROL_FORMATTER, - index, - buf, buflen, HZ); -} - - - -static inline int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames) -{ - unsigned char buf[3]; - int ret, fps; - struct Nala_table_entry *pEntry; - int frames2frames[31] = - { /* closest match of framerate */ - 0, 0, 0, 0, 4, /* 0-4 */ - 5, 5, 7, 7, 10, /* 5-9 */ - 10, 10, 12, 12, 15, /* 10-14 */ - 15, 15, 15, 20, 20, /* 15-19 */ - 20, 20, 20, 24, 24, /* 20-24 */ - 24, 24, 24, 24, 24, /* 25-29 */ - 24 /* 30 */ - }; - int frames2table[31] = - { 0, 0, 0, 0, 0, /* 0-4 */ - 1, 1, 1, 2, 2, /* 5-9 */ - 3, 3, 4, 4, 4, /* 10-14 */ - 5, 5, 5, 5, 5, /* 15-19 */ - 6, 6, 6, 6, 7, /* 20-24 */ - 7, 7, 7, 7, 7, /* 25-29 */ - 7 /* 30 */ - }; - - if (size < 0 || size > PSZ_CIF || frames < 4 || frames > 25) - return -EINVAL; - frames = frames2frames[frames]; - fps = frames2table[frames]; - pEntry = &Nala_table[size][fps]; - if (pEntry->alternate == 0) - return -EINVAL; - - if (pEntry->compressed && pdev->decompressor == NULL) - return -ENOENT; /* Not supported. */ - - memcpy(buf, pEntry->mode, 3); - ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 3); - if (ret < 0) { - Debug("Failed to send video command... %d\n", ret); - return ret; - } - if (pEntry->compressed && pdev->decompressor != 0 && pdev->vpalette != VIDEO_PALETTE_RAW) - pdev->decompressor->init(pdev->type, pdev->release, buf, pdev->decompress_data); - - pdev->cmd_len = 3; - memcpy(pdev->cmd_buf, buf, 3); - - /* Set various parameters */ - pdev->vframes = frames; - pdev->vsize = size; - pdev->valternate = pEntry->alternate; - pdev->image = pwc_image_sizes[size]; - pdev->frame_size = (pdev->image.x * pdev->image.y * 3) / 2; - if (pEntry->compressed) { - if (pdev->release < 5) { /* 4 fold compression */ - pdev->vbandlength = 528; - pdev->frame_size /= 4; - } - else { - pdev->vbandlength = 704; - pdev->frame_size /= 3; - } - } - else - pdev->vbandlength = 0; - return 0; -} - - -static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) -{ - unsigned char buf[13]; - struct Timon_table_entry *pChoose; - int ret, fps; - - if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3) - return -EINVAL; - if (size == PSZ_VGA && frames > 15) - return -EINVAL; - fps = (frames / 5) - 1; - - /* Find a supported framerate with progressively higher compression ratios - if the preferred ratio is not available. - */ - pChoose = NULL; - if (pdev->decompressor == NULL) { -#if PWC_DEBUG - Debug("Trying to find uncompressed mode.\n"); -#endif - pChoose = &Timon_table[size][fps][0]; - } - else { - while (compression <= 3) { - pChoose = &Timon_table[size][fps][compression]; - if (pChoose->alternate != 0) - break; - compression++; - } - } - if (pChoose == NULL || pChoose->alternate == 0) - return -ENOENT; /* Not supported. */ - - memcpy(buf, pChoose->mode, 13); - if (snapshot) - buf[0] |= 0x80; - ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 13); - if (ret < 0) - return ret; - - if (pChoose->bandlength > 0 && pdev->decompressor != 0 && pdev->vpalette != VIDEO_PALETTE_RAW) - pdev->decompressor->init(pdev->type, pdev->release, buf, pdev->decompress_data); - - pdev->cmd_len = 13; - memcpy(pdev->cmd_buf, buf, 13); - - /* Set various parameters */ - pdev->vframes = frames; - pdev->vsize = size; - pdev->vsnapshot = snapshot; - pdev->valternate = pChoose->alternate; - pdev->image = pwc_image_sizes[size]; - pdev->vbandlength = pChoose->bandlength; - if (pChoose->bandlength > 0) - pdev->frame_size = (pChoose->bandlength * pdev->image.y) / 4; - else - pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8; - return 0; -} - - -static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) -{ - struct Kiara_table_entry *pChoose = NULL; - int fps, ret; - unsigned char buf[12]; - struct Kiara_table_entry RawEntry = {6, 773, 1272, {0xAD, 0xF4, 0x10, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}}; - - if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3) - return -EINVAL; - if (size == PSZ_VGA && frames > 15) - return -EINVAL; - fps = (frames / 5) - 1; - - /* special case: VGA @ 5 fps and snapshot is raw bayer mode */ - if (size == PSZ_VGA && frames == 5 && snapshot) - { - /* Only available in case the raw palette is selected or - we have the decompressor available. This mode is - only available in compressed form - */ - if (pdev->vpalette == VIDEO_PALETTE_RAW || pdev->decompressor != NULL) - { - Info("Choosing VGA/5 BAYER mode (%d).\n", pdev->vpalette); - pChoose = &RawEntry; - } - else - { - Info("VGA/5 BAYER mode _must_ have a decompressor available, or use RAW palette.\n"); - } - } - else - { - /* Find a supported framerate with progressively higher compression ratios - if the preferred ratio is not available. - Skip this step when using RAW modes. - */ - if (pdev->decompressor == NULL && pdev->vpalette != VIDEO_PALETTE_RAW) { -#if PWC_DEBUG - Debug("Trying to find uncompressed mode.\n"); -#endif - pChoose = &Kiara_table[size][fps][0]; - } - else { - while (compression <= 3) { - pChoose = &Kiara_table[size][fps][compression]; - if (pChoose->alternate != 0) - break; - compression++; - } - } - } - if (pChoose == NULL || pChoose->alternate == 0) - return -ENOENT; /* Not supported. */ - - /* usb_control_msg won't take staticly allocated arrays as argument?? */ - memcpy(buf, pChoose->mode, 12); - if (snapshot) - buf[0] |= 0x80; - - /* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */ - ret = send_video_command(pdev->udev, 4 /* pdev->vendpoint */, buf, 12); - if (ret < 0) - return ret; - - if (pChoose->bandlength > 0 && pdev->decompressor != 0 && pdev->vpalette != VIDEO_PALETTE_RAW) - pdev->decompressor->init(pdev->type, pdev->release, buf, pdev->decompress_data); - - pdev->cmd_len = 12; - memcpy(pdev->cmd_buf, buf, 12); - /* All set and go */ - pdev->vframes = frames; - pdev->vsize = size; - pdev->vsnapshot = snapshot; - pdev->valternate = pChoose->alternate; - pdev->image = pwc_image_sizes[size]; - pdev->vbandlength = pChoose->bandlength; - if (pdev->vbandlength > 0) - pdev->frame_size = (pdev->vbandlength * pdev->image.y) / 4; - else - pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8; - return 0; -} - - - -/** - @pdev: device structure - @width: viewport width - @height: viewport height - @frame: framerate, in fps - @compression: preferred compression ratio - @snapshot: snapshot mode or streaming - */ -int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot) -{ - int ret, size; - - Trace(TRACE_FLOW, "set_video_mode(%dx%d @ %d, palette %d).\n", width, height, frames, pdev->vpalette); - size = pwc_decode_size(pdev, width, height); - if (size < 0) { - Debug("Could not find suitable size.\n"); - return -ERANGE; - } - Debug("decode_size = %d.\n", size); - - ret = -EINVAL; - switch(pdev->type) { - case 645: - case 646: - ret = set_video_mode_Nala(pdev, size, frames); - break; - - case 675: - case 680: - case 690: - ret = set_video_mode_Timon(pdev, size, frames, compression, snapshot); - break; - - case 720: - case 730: - case 740: - case 750: - ret = set_video_mode_Kiara(pdev, size, frames, compression, snapshot); - break; - } - if (ret < 0) { - if (ret == -ENOENT) - Info("Video mode %s@%d fps is only supported with the decompressor module (pwcx).\n", size2name[size], frames); - else { - Err("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret); - } - return ret; - } - pdev->view.x = width; - pdev->view.y = height; - pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size; - pwc_set_image_buffer_size(pdev); - Trace(TRACE_SIZE, "Set viewport to %dx%d, image size is %dx%d.\n", width, height, pwc_image_sizes[size].x, pwc_image_sizes[size].y); - return 0; -} - - -void pwc_set_image_buffer_size(struct pwc_device *pdev) -{ - int i, factor = 0, filler = 0; - - /* for PALETTE_YUV420P */ - switch(pdev->vpalette) - { - case VIDEO_PALETTE_YUV420P: - factor = 6; - filler = 128; - break; - case VIDEO_PALETTE_RAW: - factor = 6; /* can be uncompressed YUV420P */ - filler = 0; - break; - } - - /* Set sizes in bytes */ - pdev->image.size = pdev->image.x * pdev->image.y * factor / 4; - pdev->view.size = pdev->view.x * pdev->view.y * factor / 4; - - /* Align offset, or you'll get some very weird results in - YUV420 mode... x must be multiple of 4 (to get the Y's in - place), and y even (or you'll mixup U & V). This is less of a - problem for YUV420P. - */ - pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC; - pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE; - - /* Fill buffers with gray or black */ - for (i = 0; i < MAX_IMAGES; i++) { - if (pdev->image_ptr[i] != NULL) - memset(pdev->image_ptr[i], filler, pdev->view.size); - } -} - - - -/* BRIGHTNESS */ - -int pwc_get_brightness(struct pwc_device *pdev) -{ - char buf; - int ret; - - ret = RecvControlMsg(GET_LUM_CTL, BRIGHTNESS_FORMATTER, 1); - if (ret < 0) - return ret; - return buf << 9; -} - -int pwc_set_brightness(struct pwc_device *pdev, int value) -{ - char buf; - - if (value < 0) - value = 0; - if (value > 0xffff) - value = 0xffff; - buf = (value >> 9) & 0x7f; - return SendControlMsg(SET_LUM_CTL, BRIGHTNESS_FORMATTER, 1); -} - -/* CONTRAST */ - -int pwc_get_contrast(struct pwc_device *pdev) -{ - char buf; - int ret; - - ret = RecvControlMsg(GET_LUM_CTL, CONTRAST_FORMATTER, 1); - if (ret < 0) - return ret; - return buf << 10; -} - -int pwc_set_contrast(struct pwc_device *pdev, int value) -{ - char buf; - - if (value < 0) - value = 0; - if (value > 0xffff) - value = 0xffff; - buf = (value >> 10) & 0x3f; - return SendControlMsg(SET_LUM_CTL, CONTRAST_FORMATTER, 1); -} - -/* GAMMA */ - -int pwc_get_gamma(struct pwc_device *pdev) -{ - char buf; - int ret; - - ret = RecvControlMsg(GET_LUM_CTL, GAMMA_FORMATTER, 1); - if (ret < 0) - return ret; - return buf << 11; -} - -int pwc_set_gamma(struct pwc_device *pdev, int value) -{ - char buf; - - if (value < 0) - value = 0; - if (value > 0xffff) - value = 0xffff; - buf = (value >> 11) & 0x1f; - return SendControlMsg(SET_LUM_CTL, GAMMA_FORMATTER, 1); -} - - -/* SATURATION */ - -int pwc_get_saturation(struct pwc_device *pdev) -{ - char buf; - int ret; - - if (pdev->type < 675) - return -1; - ret = RecvControlMsg(GET_CHROM_CTL, pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1); - if (ret < 0) - return ret; - return 32768 + buf * 327; -} - -int pwc_set_saturation(struct pwc_device *pdev, int value) -{ - char buf; - - if (pdev->type < 675) - return -EINVAL; - if (value < 0) - value = 0; - if (value > 0xffff) - value = 0xffff; - /* saturation ranges from -100 to +100 */ - buf = (value - 32768) / 327; - return SendControlMsg(SET_CHROM_CTL, pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1); -} - -/* AGC */ - -static inline int pwc_set_agc(struct pwc_device *pdev, int mode, int value) -{ - char buf; - int ret; - - if (mode) - buf = 0x0; /* auto */ - else - buf = 0xff; /* fixed */ - - ret = SendControlMsg(SET_LUM_CTL, AGC_MODE_FORMATTER, 1); - - if (!mode && ret >= 0) { - if (value < 0) - value = 0; - if (value > 0xffff) - value = 0xffff; - buf = (value >> 10) & 0x3F; - ret = SendControlMsg(SET_LUM_CTL, PRESET_AGC_FORMATTER, 1); - } - if (ret < 0) - return ret; - return 0; -} - -static inline int pwc_get_agc(struct pwc_device *pdev, int *value) -{ - unsigned char buf; - int ret; - - ret = RecvControlMsg(GET_LUM_CTL, AGC_MODE_FORMATTER, 1); - if (ret < 0) - return ret; - - if (buf != 0) { /* fixed */ - ret = RecvControlMsg(GET_LUM_CTL, PRESET_AGC_FORMATTER, 1); - if (ret < 0) - return ret; - if (buf > 0x3F) - buf = 0x3F; - *value = (buf << 10); - } - else { /* auto */ - ret = RecvControlMsg(GET_STATUS_CTL, READ_AGC_FORMATTER, 1); - if (ret < 0) - return ret; - /* Gah... this value ranges from 0x00 ... 0x9F */ - if (buf > 0x9F) - buf = 0x9F; - *value = -(48 + buf * 409); - } - - return 0; -} - -static inline int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value) -{ - char buf[2]; - int speed, ret; - - - if (mode) - buf[0] = 0x0; /* auto */ - else - buf[0] = 0xff; /* fixed */ - - ret = SendControlMsg(SET_LUM_CTL, SHUTTER_MODE_FORMATTER, 1); - - if (!mode && ret >= 0) { - if (value < 0) - value = 0; - if (value > 0xffff) - value = 0xffff; - switch(pdev->type) { - case 675: - case 680: - case 690: - /* speed ranges from 0x0 to 0x290 (656) */ - speed = (value / 100); - buf[1] = speed >> 8; - buf[0] = speed & 0xff; - break; - case 720: - case 730: - case 740: - case 750: - /* speed seems to range from 0x0 to 0xff */ - buf[1] = 0; - buf[0] = value >> 8; - break; - } - - ret = SendControlMsg(SET_LUM_CTL, PRESET_SHUTTER_FORMATTER, 2); - } - return ret; -} - - -/* POWER */ - -int pwc_camera_power(struct pwc_device *pdev, int power) -{ - char buf; - - if (pdev->type < 675 || (pdev->type < 730 && pdev->release < 6)) - return 0; /* Not supported by Nala or Timon < release 6 */ - - if (power) - buf = 0x00; /* active */ - else - buf = 0xFF; /* power save */ - return SendControlMsg(SET_STATUS_CTL, SET_POWER_SAVE_MODE_FORMATTER, 1); -} - - - -/* private calls */ - -static inline int pwc_restore_user(struct pwc_device *pdev) -{ - char buf; /* dummy */ - return SendControlMsg(SET_STATUS_CTL, RESTORE_USER_DEFAULTS_FORMATTER, 0); -} - -static inline int pwc_save_user(struct pwc_device *pdev) -{ - char buf; /* dummy */ - return SendControlMsg(SET_STATUS_CTL, SAVE_USER_DEFAULTS_FORMATTER, 0); -} - -static inline int pwc_restore_factory(struct pwc_device *pdev) -{ - char buf; /* dummy */ - return SendControlMsg(SET_STATUS_CTL, RESTORE_FACTORY_DEFAULTS_FORMATTER, 0); -} - - /* ************************************************* */ - /* Patch by Alvarado: (not in the original version */ - - /* - * the camera recognizes modes from 0 to 4: - * - * 00: indoor (incandescant lighting) - * 01: outdoor (sunlight) - * 02: fluorescent lighting - * 03: manual - * 04: auto - */ -static inline int pwc_set_awb(struct pwc_device *pdev, int mode) -{ - char buf; - int ret; - - if (mode < 0) - mode = 0; - - if (mode > 4) - mode = 4; - - buf = mode & 0x07; /* just the lowest three bits */ - - ret = SendControlMsg(SET_CHROM_CTL, WB_MODE_FORMATTER, 1); - - if (ret < 0) - return ret; - return 0; -} - -static inline int pwc_get_awb(struct pwc_device *pdev) -{ - unsigned char buf; - int ret; - - ret = RecvControlMsg(GET_CHROM_CTL, WB_MODE_FORMATTER, 1); - - if (ret < 0) - return ret; - return buf; -} - -static inline int pwc_set_red_gain(struct pwc_device *pdev, int value) -{ - unsigned char buf; - - if (value < 0) - value = 0; - if (value > 0xffff) - value = 0xffff; - /* only the msb is considered */ - buf = value >> 8; - return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1); -} - -static inline int pwc_get_red_gain(struct pwc_device *pdev, int *value) -{ - unsigned char buf; - int ret; - - ret = RecvControlMsg(GET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1); - if (ret < 0) - return ret; - *value = buf << 8; - return 0; -} - - -static inline int pwc_set_blue_gain(struct pwc_device *pdev, int value) -{ - unsigned char buf; - - if (value < 0) - value = 0; - if (value > 0xffff) - value = 0xffff; - /* only the msb is considered */ - buf = value >> 8; - return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1); -} - -static inline int pwc_get_blue_gain(struct pwc_device *pdev, int *value) -{ - unsigned char buf; - int ret; - - ret = RecvControlMsg(GET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1); - if (ret < 0) - return ret; - *value = buf << 8; - return 0; -} - - -/* The following two functions are different, since they only read the - internal red/blue gains, which may be different from the manual - gains set or read above. - */ -static inline int pwc_read_red_gain(struct pwc_device *pdev, int *value) -{ - unsigned char buf; - int ret; - - ret = RecvControlMsg(GET_STATUS_CTL, READ_RED_GAIN_FORMATTER, 1); - if (ret < 0) - return ret; - *value = buf << 8; - return 0; -} - -static inline int pwc_read_blue_gain(struct pwc_device *pdev, int *value) -{ - unsigned char buf; - int ret; - - ret = RecvControlMsg(GET_STATUS_CTL, READ_BLUE_GAIN_FORMATTER, 1); - if (ret < 0) - return ret; - *value = buf << 8; - return 0; -} - - -static inline int pwc_set_wb_speed(struct pwc_device *pdev, int speed) -{ - unsigned char buf; - - /* useful range is 0x01..0x20 */ - buf = speed / 0x7f0; - return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1); -} - -static inline int pwc_get_wb_speed(struct pwc_device *pdev, int *value) -{ - unsigned char buf; - int ret; - - ret = RecvControlMsg(GET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1); - if (ret < 0) - return ret; - *value = buf * 0x7f0; - return 0; -} - - -static inline int pwc_set_wb_delay(struct pwc_device *pdev, int delay) -{ - unsigned char buf; - - /* useful range is 0x01..0x3F */ - buf = (delay >> 10); - return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1); -} - -static inline int pwc_get_wb_delay(struct pwc_device *pdev, int *value) -{ - unsigned char buf; - int ret; - - ret = RecvControlMsg(GET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1); - if (ret < 0) - return ret; - *value = buf << 10; - return 0; -} - - -int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value) -{ - unsigned char buf[2]; - - if (pdev->type < 730) - return 0; - on_value /= 100; - off_value /= 100; - if (on_value < 0) - on_value = 0; - if (on_value > 0xff) - on_value = 0xff; - if (off_value < 0) - off_value = 0; - if (off_value > 0xff) - off_value = 0xff; - - buf[0] = on_value; - buf[1] = off_value; - - return SendControlMsg(SET_STATUS_CTL, LED_FORMATTER, 2); -} - -int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value) -{ - unsigned char buf[2]; - int ret; - - if (pdev->type < 730) { - *on_value = -1; - *off_value = -1; - return 0; - } - - ret = RecvControlMsg(GET_STATUS_CTL, LED_FORMATTER, 2); - if (ret < 0) - return ret; - *on_value = buf[0] * 100; - *off_value = buf[1] * 100; - return 0; -} - -static inline int pwc_set_contour(struct pwc_device *pdev, int contour) -{ - unsigned char buf; - int ret; - - if (contour < 0) - buf = 0xff; /* auto contour on */ - else - buf = 0x0; /* auto contour off */ - ret = SendControlMsg(SET_LUM_CTL, AUTO_CONTOUR_FORMATTER, 1); - if (ret < 0) - return ret; - - if (contour < 0) - return 0; - if (contour > 0xffff) - contour = 0xffff; - - buf = (contour >> 10); /* contour preset is [0..3f] */ - ret = SendControlMsg(SET_LUM_CTL, PRESET_CONTOUR_FORMATTER, 1); - if (ret < 0) - return ret; - return 0; -} - -static inline int pwc_get_contour(struct pwc_device *pdev, int *contour) -{ - unsigned char buf; - int ret; - - ret = RecvControlMsg(GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, 1); - if (ret < 0) - return ret; - - if (buf == 0) { - /* auto mode off, query current preset value */ - ret = RecvControlMsg(GET_LUM_CTL, PRESET_CONTOUR_FORMATTER, 1); - if (ret < 0) - return ret; - *contour = buf << 10; - } - else - *contour = -1; - return 0; -} - - -static inline int pwc_set_backlight(struct pwc_device *pdev, int backlight) -{ - unsigned char buf; - - if (backlight) - buf = 0xff; - else - buf = 0x0; - return SendControlMsg(SET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1); -} - -static inline int pwc_get_backlight(struct pwc_device *pdev, int *backlight) -{ - int ret; - unsigned char buf; - - ret = RecvControlMsg(GET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1); - if (ret < 0) - return ret; - *backlight = buf; - return 0; -} - - -static inline int pwc_set_flicker(struct pwc_device *pdev, int flicker) -{ - unsigned char buf; - - if (flicker) - buf = 0xff; - else - buf = 0x0; - return SendControlMsg(SET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1); -} - -static inline int pwc_get_flicker(struct pwc_device *pdev, int *flicker) -{ - int ret; - unsigned char buf; - - ret = RecvControlMsg(GET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1); - if (ret < 0) - return ret; - *flicker = buf; - return 0; -} - - -static inline int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise) -{ - unsigned char buf; - - if (noise < 0) - noise = 0; - if (noise > 3) - noise = 3; - buf = noise; - return SendControlMsg(SET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1); -} - -static inline int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise) -{ - int ret; - unsigned char buf; - - ret = RecvControlMsg(GET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1); - if (ret < 0) - return ret; - *noise = buf; - return 0; -} - -int pwc_mpt_reset(struct pwc_device *pdev, int flags) -{ - unsigned char buf; - - buf = flags & 0x03; // only lower two bits are currently used - return SendControlMsg(SET_MPT_CTL, PT_RESET_CONTROL_FORMATTER, 1); -} - -static inline int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt) -{ - unsigned char buf[4]; - - /* set new relative angle; angles are expressed in degrees * 100, - but cam as .5 degree resolution, hence devide by 200. Also - the angle must be multiplied by 64 before it's send to - the cam (??) - */ - pan = 64 * pan / 100; - tilt = -64 * tilt / 100; /* positive tilt is down, which is not what the user would expect */ - buf[0] = pan & 0xFF; - buf[1] = (pan >> 8) & 0xFF; - buf[2] = tilt & 0xFF; - buf[3] = (tilt >> 8) & 0xFF; - return SendControlMsg(SET_MPT_CTL, PT_RELATIVE_CONTROL_FORMATTER, 4); -} - -static inline int pwc_mpt_get_status(struct pwc_device *pdev, struct pwc_mpt_status *status) -{ - int ret; - unsigned char buf[5]; - - ret = RecvControlMsg(GET_MPT_CTL, PT_STATUS_FORMATTER, 5); - if (ret < 0) - return ret; - status->status = buf[0] & 0x7; // 3 bits are used for reporting - status->time_pan = (buf[1] << 8) + buf[2]; - status->time_tilt = (buf[3] << 8) + buf[4]; - return 0; -} - - -int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) -{ - unsigned char buf; - int ret = -1, request; - - if (pdev->type < 675) - request = SENSOR_TYPE_FORMATTER1; - else if (pdev->type < 730) - return -1; /* The Vesta series doesn't have this call */ - else - request = SENSOR_TYPE_FORMATTER2; - - ret = RecvControlMsg(GET_STATUS_CTL, request, 1); - if (ret < 0) - return ret; - if (pdev->type < 675) - *sensor = buf | 0x100; - else - *sensor = buf; - return 0; -} - - - /* End of Add-Ons */ - /* ************************************************* */ - -/* Linux 2.5.something and 2.6 pass direct pointers to arguments of - ioctl() calls. With 2.4, you have to do tedious copy_from_user() - and copy_to_user() calls. With these macros we circumvent this, - and let me maintain only one source file. The functionality is - exactly the same otherwise. - */ - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) - -/* define local variable for arg */ -#define ARG_DEF(ARG_type, ARG_name)\ - ARG_type *ARG_name = arg; -/* copy arg to local variable */ -#define ARG_IN(ARG_name) /* nothing */ -/* argument itself (referenced) */ -#define ARGR(ARG_name) (*ARG_name) -/* argument address */ -#define ARGA(ARG_name) ARG_name -/* copy local variable to arg */ -#define ARG_OUT(ARG_name) /* nothing */ - -#else - -#define ARG_DEF(ARG_type, ARG_name)\ - ARG_type ARG_name; -#define ARG_IN(ARG_name)\ - if (copy_from_user(&ARG_name, arg, sizeof(ARG_name))) {\ - ret = -EFAULT;\ - break;\ - } -#define ARGR(ARG_name) ARG_name -#define ARGA(ARG_name) &ARG_name -#define ARG_OUT(ARG_name)\ - if (copy_to_user(arg, &ARG_name, sizeof(ARG_name))) {\ - ret = -EFAULT;\ - break;\ - } - -#endif - -int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) -{ - int ret = 0; - - switch(cmd) { - case VIDIOCPWCRUSER: - { - if (pwc_restore_user(pdev)) - ret = -EINVAL; - break; - } - - case VIDIOCPWCSUSER: - { - if (pwc_save_user(pdev)) - ret = -EINVAL; - break; - } - - case VIDIOCPWCFACTORY: - { - if (pwc_restore_factory(pdev)) - ret = -EINVAL; - break; - } - - case VIDIOCPWCSCQUAL: - { - ARG_DEF(int, qual) - - ARG_IN(qual) - if (ARGR(qual) < 0 || ARGR(qual) > 3) - ret = -EINVAL; - else - ret = pwc_try_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, ARGR(qual), pdev->vsnapshot); - if (ret >= 0) - pdev->vcompression = ARGR(qual); - break; - } - - case VIDIOCPWCGCQUAL: - { - ARG_DEF(int, qual) - - ARGR(qual) = pdev->vcompression; - ARG_OUT(qual) - break; - } - - case VIDIOCPWCPROBE: - { - ARG_DEF(struct pwc_probe, probe) - - strcpy(ARGR(probe).name, pdev->vdev->name); - ARGR(probe).type = pdev->type; - ARG_OUT(probe) - break; - } - - case VIDIOCPWCGSERIAL: - { - ARG_DEF(struct pwc_serial, serial) - - strcpy(ARGR(serial).serial, pdev->serial); - ARG_OUT(serial) - break; - } - - case VIDIOCPWCSAGC: - { - ARG_DEF(int, agc) - - ARG_IN(agc) - if (pwc_set_agc(pdev, ARGR(agc) < 0 ? 1 : 0, ARGR(agc))) - ret = -EINVAL; - break; - } - - case VIDIOCPWCGAGC: - { - ARG_DEF(int, agc) - - if (pwc_get_agc(pdev, ARGA(agc))) - ret = -EINVAL; - ARG_OUT(agc) - break; - } - - case VIDIOCPWCSSHUTTER: - { - ARG_DEF(int, shutter_speed) - - ARG_IN(shutter_speed) - ret = pwc_set_shutter_speed(pdev, ARGR(shutter_speed) < 0 ? 1 : 0, ARGR(shutter_speed)); - break; - } - - case VIDIOCPWCSAWB: - { - ARG_DEF(struct pwc_whitebalance, wb) - - ARG_IN(wb) - ret = pwc_set_awb(pdev, ARGR(wb).mode); - if (ret >= 0 && ARGR(wb).mode == PWC_WB_MANUAL) { - pwc_set_red_gain(pdev, ARGR(wb).manual_red); - pwc_set_blue_gain(pdev, ARGR(wb).manual_blue); - } - break; - } - - case VIDIOCPWCGAWB: - { - ARG_DEF(struct pwc_whitebalance, wb) - - memset(ARGA(wb), 0, sizeof(struct pwc_whitebalance)); - ARGR(wb).mode = pwc_get_awb(pdev); - if (ARGR(wb).mode < 0) - ret = -EINVAL; - else { - if (ARGR(wb).mode == PWC_WB_MANUAL) { - ret = pwc_get_red_gain(pdev, &ARGR(wb).manual_red); - if (ret < 0) - break; - ret = pwc_get_blue_gain(pdev, &ARGR(wb).manual_blue); - if (ret < 0) - break; - } - if (ARGR(wb).mode == PWC_WB_AUTO) { - ret = pwc_read_red_gain(pdev, &ARGR(wb).read_red); - if (ret < 0) - break; - ret =pwc_read_blue_gain(pdev, &ARGR(wb).read_blue); - if (ret < 0) - break; - } - } - ARG_OUT(wb) - break; - } - - case VIDIOCPWCSAWBSPEED: - { - ARG_DEF(struct pwc_wb_speed, wbs) - - if (ARGR(wbs).control_speed > 0) { - ret = pwc_set_wb_speed(pdev, ARGR(wbs).control_speed); - } - if (ARGR(wbs).control_delay > 0) { - ret = pwc_set_wb_delay(pdev, ARGR(wbs).control_delay); - } - break; - } - - case VIDIOCPWCGAWBSPEED: - { - ARG_DEF(struct pwc_wb_speed, wbs) - - ret = pwc_get_wb_speed(pdev, &ARGR(wbs).control_speed); - if (ret < 0) - break; - ret = pwc_get_wb_delay(pdev, &ARGR(wbs).control_delay); - if (ret < 0) - break; - ARG_OUT(wbs) - break; - } - - case VIDIOCPWCSLED: - { - ARG_DEF(struct pwc_leds, leds) - - ARG_IN(leds) - ret = pwc_set_leds(pdev, ARGR(leds).led_on, ARGR(leds).led_off); - break; - } - - - case VIDIOCPWCGLED: - { - ARG_DEF(struct pwc_leds, leds) - - ret = pwc_get_leds(pdev, &ARGR(leds).led_on, &ARGR(leds).led_off); - ARG_OUT(leds) - break; - } - - case VIDIOCPWCSCONTOUR: - { - ARG_DEF(int, contour) - - ARG_IN(contour) - ret = pwc_set_contour(pdev, ARGR(contour)); - break; - } - - case VIDIOCPWCGCONTOUR: - { - ARG_DEF(int, contour) - - ret = pwc_get_contour(pdev, ARGA(contour)); - ARG_OUT(contour) - break; - } - - case VIDIOCPWCSBACKLIGHT: - { - ARG_DEF(int, backlight) - - ARG_IN(backlight) - ret = pwc_set_backlight(pdev, ARGR(backlight)); - break; - } - - case VIDIOCPWCGBACKLIGHT: - { - ARG_DEF(int, backlight) - - ret = pwc_get_backlight(pdev, ARGA(backlight)); - ARG_OUT(backlight) - break; - } - - case VIDIOCPWCSFLICKER: - { - ARG_DEF(int, flicker) - - ARG_IN(flicker) - ret = pwc_set_flicker(pdev, ARGR(flicker)); - break; - } - - case VIDIOCPWCGFLICKER: - { - ARG_DEF(int, flicker) - - ret = pwc_get_flicker(pdev, ARGA(flicker)); - ARG_OUT(flicker) - break; - } - - case VIDIOCPWCSDYNNOISE: - { - ARG_DEF(int, dynnoise) - - ARG_IN(dynnoise) - ret = pwc_set_dynamic_noise(pdev, ARGR(dynnoise)); - break; - } - - case VIDIOCPWCGDYNNOISE: - { - ARG_DEF(int, dynnoise) - - ret = pwc_get_dynamic_noise(pdev, ARGA(dynnoise)); - ARG_OUT(dynnoise); - break; - } - - case VIDIOCPWCGREALSIZE: - { - ARG_DEF(struct pwc_imagesize, size) - - ARGR(size).width = pdev->image.x; - ARGR(size).height = pdev->image.y; - ARG_OUT(size) - break; - } - - case VIDIOCPWCMPTRESET: - { - if (pdev->features & FEATURE_MOTOR_PANTILT) - { - ARG_DEF(int, flags) - - ARG_IN(flags) - ret = pwc_mpt_reset(pdev, ARGR(flags)); - if (ret >= 0) - { - pdev->pan_angle = 0; - pdev->tilt_angle = 0; - } - } - else - { - ret = -ENXIO; - } - break; - } - - case VIDIOCPWCMPTGRANGE: - { - if (pdev->features & FEATURE_MOTOR_PANTILT) - { - ARG_DEF(struct pwc_mpt_range, range) - - ARGR(range) = pdev->angle_range; - ARG_OUT(range) - } - else - { - ret = -ENXIO; - } - break; - } - - case VIDIOCPWCMPTSANGLE: - { - int new_pan, new_tilt; - - if (pdev->features & FEATURE_MOTOR_PANTILT) - { - ARG_DEF(struct pwc_mpt_angles, angles) - - ARG_IN(angles) - /* The camera can only set relative angles, so - do some calculations when getting an absolute angle . - */ - if (ARGR(angles).absolute) - { - new_pan = ARGR(angles).pan; - new_tilt = ARGR(angles).tilt; - } - else - { - new_pan = pdev->pan_angle + ARGR(angles).pan; - new_tilt = pdev->tilt_angle + ARGR(angles).tilt; - } - /* check absolute ranges */ - if (new_pan < pdev->angle_range.pan_min || - new_pan > pdev->angle_range.pan_max || - new_tilt < pdev->angle_range.tilt_min || - new_tilt > pdev->angle_range.tilt_max) - { - ret = -ERANGE; - } - else - { - /* go to relative range, check again */ - new_pan -= pdev->pan_angle; - new_tilt -= pdev->tilt_angle; - /* angles are specified in degrees * 100, thus the limit = 36000 */ - if (new_pan < -36000 || new_pan > 36000 || new_tilt < -36000 || new_tilt > 36000) - ret = -ERANGE; - } - if (ret == 0) /* no errors so far */ - { - ret = pwc_mpt_set_angle(pdev, new_pan, new_tilt); - if (ret >= 0) - { - pdev->pan_angle += new_pan; - pdev->tilt_angle += new_tilt; - } - if (ret == -EPIPE) /* stall -> out of range */ - ret = -ERANGE; - } - } - else - { - ret = -ENXIO; - } - break; - } - - case VIDIOCPWCMPTGANGLE: - { - - if (pdev->features & FEATURE_MOTOR_PANTILT) - { - ARG_DEF(struct pwc_mpt_angles, angles) - - ARGR(angles).absolute = 1; - ARGR(angles).pan = pdev->pan_angle; - ARGR(angles).tilt = pdev->tilt_angle; - ARG_OUT(angles) - } - else - { - ret = -ENXIO; - } - break; - } - - case VIDIOCPWCMPTSTATUS: - { - if (pdev->features & FEATURE_MOTOR_PANTILT) - { - ARG_DEF(struct pwc_mpt_status, status) - - ret = pwc_mpt_get_status(pdev, ARGA(status)); - ARG_OUT(status) - } - else - { - ret = -ENXIO; - } - break; - } - - case VIDIOCPWCGVIDCMD: - { - ARG_DEF(struct pwc_video_command, cmd); - - ARGR(cmd).type = pdev->type; - ARGR(cmd).release = pdev->release; - ARGR(cmd).command_len = pdev->cmd_len; - memcpy(&ARGR(cmd).command_buf, pdev->cmd_buf, pdev->cmd_len); - ARGR(cmd).bandlength = pdev->vbandlength; - ARGR(cmd).frame_size = pdev->frame_size; - ARG_OUT(cmd) - break; - } - - default: - ret = -ENOIOCTLCMD; - break; - } - - if (ret > 0) - return 0; - return ret; -} - - - --- linux-2.6.9-rc1/drivers/usb/media/pwc.h 2004-08-24 00:02:58.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,271 +0,0 @@ -/* (C) 1999-2003 Nemosoft Unv. (webcam@smcc.demon.nl) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#ifndef PWC_H -#define PWC_H - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pwc-uncompress.h" -#include "pwc-ioctl.h" - -/* Defines and structures for the Philips webcam */ -/* Used for checking memory corruption/pointer validation */ -#define PWC_MAGIC 0x89DC10ABUL -#undef PWC_MAGIC - -/* Turn some debugging options on/off */ -#define PWC_DEBUG 0 - -/* Trace certain actions in the driver */ -#define TRACE_MODULE 0x0001 -#define TRACE_PROBE 0x0002 -#define TRACE_OPEN 0x0004 -#define TRACE_READ 0x0008 -#define TRACE_MEMORY 0x0010 -#define TRACE_FLOW 0x0020 -#define TRACE_SIZE 0x0040 -#define TRACE_PWCX 0x0080 -#define TRACE_SEQUENCE 0x1000 - -#define Trace(R, A...) if (pwc_trace & R) printk(KERN_DEBUG PWC_NAME " " A) -#define Debug(A...) printk(KERN_DEBUG PWC_NAME " " A) -#define Info(A...) printk(KERN_INFO PWC_NAME " " A) -#define Err(A...) printk(KERN_ERR PWC_NAME " " A) - - -/* Defines for ToUCam cameras */ -#define TOUCAM_HEADER_SIZE 8 -#define TOUCAM_TRAILER_SIZE 4 - -#define FEATURE_MOTOR_PANTILT 0x0001 - -/* Version block */ -#define PWC_MAJOR 9 -#define PWC_MINOR 0 -#define PWC_VERSION "9.0.1" -#define PWC_NAME "pwc" - -/* Turn certain features on/off */ -#define PWC_INT_PIPE 0 - -/* Ignore errors in the first N frames, to allow for startup delays */ -#define FRAME_LOWMARK 5 - -/* Size and number of buffers for the ISO pipe. */ -#define MAX_ISO_BUFS 2 -#define ISO_FRAMES_PER_DESC 10 -#define ISO_MAX_FRAME_SIZE 960 -#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE) - -/* Frame buffers: contains compressed or uncompressed video data. */ -#define MAX_FRAMES 5 -/* Maximum size after decompression is 640x480 YUV data, 1.5 * 640 * 480 */ -#define PWC_FRAME_SIZE (460800 + TOUCAM_HEADER_SIZE + TOUCAM_TRAILER_SIZE) - -/* Absolute maximum number of buffers available for mmap() */ -#define MAX_IMAGES 10 - -/* The following structures were based on cpia.h. Why reinvent the wheel? :-) */ -struct pwc_iso_buf -{ - void *data; - int length; - int read; - struct urb *urb; -}; - -/* intermediate buffers with raw data from the USB cam */ -struct pwc_frame_buf -{ - void *data; - volatile int filled; /* number of bytes filled */ - struct pwc_frame_buf *next; /* list */ -#if PWC_DEBUG - int sequence; /* Sequence number */ -#endif -}; - -struct pwc_device -{ - struct video_device *vdev; -#ifdef PWC_MAGIC - int magic; -#endif - /* Pointer to our usb_device */ - struct usb_device *udev; - - int type; /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */ - int release; /* release number */ - int features; /* feature bits */ - char serial[30]; /* serial number (string) */ - int error_status; /* set when something goes wrong with the cam (unplugged, USB errors) */ - int usb_init; /* set when the cam has been initialized over USB */ - - /*** Video data ***/ - int vopen; /* flag */ - int vendpoint; /* video isoc endpoint */ - int vcinterface; /* video control interface */ - int valternate; /* alternate interface needed */ - int vframes, vsize; /* frames-per-second & size (see PSZ_*) */ - int vpalette; /* palette: 420P, RAW or RGBBAYER */ - int vframe_count; /* received frames */ - int vframes_dumped; /* counter for dumped frames */ - int vframes_error; /* frames received in error */ - int vmax_packet_size; /* USB maxpacket size */ - int vlast_packet_size; /* for frame synchronisation */ - int visoc_errors; /* number of contiguous ISOC errors */ - int vcompression; /* desired compression factor */ - int vbandlength; /* compressed band length; 0 is uncompressed */ - char vsnapshot; /* snapshot mode */ - char vsync; /* used by isoc handler */ - char vmirror; /* for ToUCaM series */ - - int cmd_len; - unsigned char cmd_buf[13]; - - /* The image acquisition requires 3 to 4 steps: - 1. data is gathered in short packets from the USB controller - 2. data is synchronized and packed into a frame buffer - 3a. in case data is compressed, decompress it directly into image buffer - 3b. in case data is uncompressed, copy into image buffer with viewport - 4. data is transferred to the user process - - Note that MAX_ISO_BUFS != MAX_FRAMES != MAX_IMAGES.... - We have in effect a back-to-back-double-buffer system. - */ - /* 1: isoc */ - struct pwc_iso_buf sbuf[MAX_ISO_BUFS]; - char iso_init; - - /* 2: frame */ - struct pwc_frame_buf *fbuf; /* all frames */ - struct pwc_frame_buf *empty_frames, *empty_frames_tail; /* all empty frames */ - struct pwc_frame_buf *full_frames, *full_frames_tail; /* all filled frames */ - struct pwc_frame_buf *fill_frame; /* frame currently being filled */ - struct pwc_frame_buf *read_frame; /* frame currently read by user process */ - int frame_header_size, frame_trailer_size; - int frame_size; - int frame_total_size; /* including header & trailer */ - int drop_frames; -#if PWC_DEBUG - int sequence; /* Debugging aid */ -#endif - - /* 3: decompression */ - struct pwc_decompressor *decompressor; /* function block with decompression routines */ - void *decompress_data; /* private data for decompression engine */ - - /* 4: image */ - /* We have an 'image' and a 'view', where 'image' is the fixed-size image - as delivered by the camera, and 'view' is the size requested by the - program. The camera image is centered in this viewport, laced with - a gray or black border. view_min <= image <= view <= view_max; - */ - int image_mask; /* bitmask of supported sizes */ - struct pwc_coord view_min, view_max; /* minimum and maximum viewable sizes */ - struct pwc_coord abs_max; /* maximum supported size with compression */ - struct pwc_coord image, view; /* image and viewport size */ - struct pwc_coord offset; /* offset within the viewport */ - - void *image_data; /* total buffer, which is subdivided into ... */ - void *image_ptr[MAX_IMAGES]; /* ...several images... */ - int fill_image; /* ...which are rotated. */ - int len_per_image; /* length per image */ - int image_read_pos; /* In case we read data in pieces, keep track of were we are in the imagebuffer */ - int image_used[MAX_IMAGES]; /* For MCAPTURE and SYNC */ - - struct semaphore modlock; /* to prevent races in video_open(), etc */ - spinlock_t ptrlock; /* for manipulating the buffer pointers */ - - /*** motorized pan/tilt feature */ - struct pwc_mpt_range angle_range; - int pan_angle; /* in degrees * 100 */ - int tilt_angle; /* absolute angle; 0,0 is home position */ - - /*** Misc. data ***/ - wait_queue_head_t frameq; /* When waiting for a frame to finish... */ -#if PWC_INT_PIPE - void *usb_int_handler; /* for the interrupt endpoint */ -#endif -}; - - -#ifdef __cplusplus -extern "C" { -#endif - -/* Global variables */ -extern int pwc_trace; -extern int pwc_preferred_compression; - -/** functions in pwc-if.c */ -int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot); - -/** Functions in pwc-misc.c */ -/* sizes in pixels */ -extern struct pwc_coord pwc_image_sizes[PSZ_MAX]; - -int pwc_decode_size(struct pwc_device *pdev, int width, int height); -void pwc_construct(struct pwc_device *pdev); - -/** Functions in pwc-ctrl.c */ -/* Request a certain video mode. Returns < 0 if not possible */ -extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot); -/* Calculate the number of bytes per image (not frame) */ -extern void pwc_set_image_buffer_size(struct pwc_device *pdev); - -/* Various controls; should be obvious. Value 0..65535, or < 0 on error */ -extern int pwc_get_brightness(struct pwc_device *pdev); -extern int pwc_set_brightness(struct pwc_device *pdev, int value); -extern int pwc_get_contrast(struct pwc_device *pdev); -extern int pwc_set_contrast(struct pwc_device *pdev, int value); -extern int pwc_get_gamma(struct pwc_device *pdev); -extern int pwc_set_gamma(struct pwc_device *pdev, int value); -extern int pwc_get_saturation(struct pwc_device *pdev); -extern int pwc_set_saturation(struct pwc_device *pdev, int value); -extern int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value); -extern int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value); -extern int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor); - -/* Power down or up the camera; not supported by all models */ -extern int pwc_camera_power(struct pwc_device *pdev, int power); - -/* Private ioctl()s; see pwc-ioctl.h */ -extern int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg); - - -/** pwc-uncompress.c */ -/* Expand frame to image, possibly including decompression. Uses read_frame and fill_image */ -extern int pwc_decompress(struct pwc_device *pdev); - -#ifdef __cplusplus -} -#endif - - -#endif --- linux-2.6.9-rc1/drivers/usb/media/pwc-if.c 2004-08-24 00:03:30.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,2191 +0,0 @@ -/* Linux driver for Philips webcam - USB and Video4Linux interface part. - (C) 1999-2004 Nemosoft Unv. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ - -/* - This code forms the interface between the USB layers and the Philips - specific stuff. Some adanved stuff of the driver falls under an - NDA, signed between me and Philips B.V., Eindhoven, the Netherlands, and - is thus not distributed in source form. The binary pwcx.o module - contains the code that falls under the NDA. - - In case you're wondering: 'pwc' stands for "Philips WebCam", but - I really didn't want to type 'philips_web_cam' every time (I'm lazy as - any Linux kernel hacker, but I don't like uncomprehensible abbreviations - without explanation). - - Oh yes, convention: to disctinguish between all the various pointers to - device-structures, I use these names for the pointer variables: - udev: struct usb_device * - vdev: struct video_device * - pdev: struct pwc_devive * -*/ - -/* Contributors: - - Alvarado: adding whitebalance code - - Alistar Moire: QuickCam 3000 Pro device/product ID - - Tony Hoyle: Creative Labs Webcam 5 device/product ID - - Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged - - Jk Fang: Sotec Afina Eye ID - - Xavier Roche: QuickCam Pro 4000 ID - - Jens Knudsen: QuickCam Zoom ID - - J. Debert: QuickCam for Notebooks ID -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pwc.h" -#include "pwc-ioctl.h" -#include "pwc-uncompress.h" - -/* Function prototypes and driver templates */ - -/* hotplug device table support */ -static struct usb_device_id pwc_device_table [] = { - { USB_DEVICE(0x0471, 0x0302) }, /* Philips models */ - { USB_DEVICE(0x0471, 0x0303) }, - { USB_DEVICE(0x0471, 0x0304) }, - { USB_DEVICE(0x0471, 0x0307) }, - { USB_DEVICE(0x0471, 0x0308) }, - { USB_DEVICE(0x0471, 0x030C) }, - { USB_DEVICE(0x0471, 0x0310) }, - { USB_DEVICE(0x0471, 0x0311) }, - { USB_DEVICE(0x0471, 0x0312) }, - { USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */ - { USB_DEVICE(0x069A, 0x0001) }, /* Askey */ - { USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */ - { USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */ - { USB_DEVICE(0x046D, 0x08B2) }, /* Logitech QuickCam Pro 4000 */ - { USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */ - { USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */ - { USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */ - { USB_DEVICE(0x046D, 0x08B6) }, /* Logitech (reserved) */ - { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech (reserved) */ - { USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */ - { USB_DEVICE(0x055D, 0x9000) }, /* Samsung */ - { USB_DEVICE(0x055D, 0x9001) }, - { USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */ - { USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */ - { USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */ - { USB_DEVICE(0x06BE, 0x8116) }, /* new Afina Eye */ - { USB_DEVICE(0x0d81, 0x1910) }, /* Visionite */ - { USB_DEVICE(0x0d81, 0x1900) }, - { } -}; -MODULE_DEVICE_TABLE(usb, pwc_device_table); - -static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id); -static void usb_pwc_disconnect(struct usb_interface *intf); - -static struct usb_driver pwc_driver = { - .owner = THIS_MODULE, - .name = "Philips webcam", /* name */ - .id_table = pwc_device_table, - .probe = usb_pwc_probe, /* probe() */ - .disconnect = usb_pwc_disconnect, /* disconnect() */ -}; - -#define MAX_DEV_HINTS 20 -#define MAX_ISOC_ERRORS 20 - -static int default_size = PSZ_QCIF; -static int default_fps = 10; -static int default_fbufs = 3; /* Default number of frame buffers */ -static int default_mbufs = 2; /* Default number of mmap() buffers */ - int pwc_trace = TRACE_MODULE | TRACE_FLOW | TRACE_PWCX; -static int power_save = 0; -static int led_on = 100, led_off = 0; /* defaults to LED that is on while in use */ - int pwc_preferred_compression = 2; /* 0..3 = uncompressed..high */ -static struct { - int type; - char serial_number[30]; - int device_node; - struct pwc_device *pdev; -} device_hint[MAX_DEV_HINTS]; - -/***/ - -static int pwc_video_open(struct inode *inode, struct file *file); -static int pwc_video_close(struct inode *inode, struct file *file); -static ssize_t pwc_video_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos); -static unsigned int pwc_video_poll(struct file *file, poll_table *wait); -static int pwc_video_ioctl(struct inode *inode, struct file *file, - unsigned int ioctlnr, unsigned long arg); -static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma); - -static struct file_operations pwc_fops = { - .owner = THIS_MODULE, - .open = pwc_video_open, - .release = pwc_video_close, - .read = pwc_video_read, - .poll = pwc_video_poll, - .mmap = pwc_video_mmap, - .ioctl = pwc_video_ioctl, - .llseek = no_llseek, -}; -static struct video_device pwc_template = { - .owner = THIS_MODULE, - .name = "Philips Webcam", /* Filled in later */ - .type = VID_TYPE_CAPTURE, - .hardware = VID_HARDWARE_PWC, - .release = video_device_release, - .fops = &pwc_fops, - .minor = -1, -}; - -/***************************************************************************/ - -/* Okay, this is some magic that I worked out and the reasoning behind it... - - The biggest problem with any USB device is of course: "what to do - when the user unplugs the device while it is in use by an application?" - We have several options: - 1) Curse them with the 7 plagues when they do (requires divine intervention) - 2) Tell them not to (won't work: they'll do it anyway) - 3) Oops the kernel (this will have a negative effect on a user's uptime) - 4) Do something sensible. - - Of course, we go for option 4. - - It happens that this device will be linked to two times, once from - usb_device and once from the video_device in their respective 'private' - pointers. This is done when the device is probed() and all initialization - succeeded. The pwc_device struct links back to both structures. - - When a device is unplugged while in use it will be removed from the - list of known USB devices; I also de-register it as a V4L device, but - unfortunately I can't free the memory since the struct is still in use - by the file descriptor. This free-ing is then deferend until the first - opportunity. Crude, but it works. - - A small 'advantage' is that if a user unplugs the cam and plugs it back - in, it should get assigned the same video device minor, but unfortunately - it's non-trivial to re-link the cam back to the video device... (that - would surely be magic! :)) -*/ - -/***************************************************************************/ -/* Private functions */ - -/* Here we want the physical address of the memory. - * This is used when initializing the contents of the area. - */ -static inline unsigned long kvirt_to_pa(unsigned long adr) -{ - unsigned long kva, ret; - - kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); - kva |= adr & (PAGE_SIZE-1); /* restore the offset */ - ret = __pa(kva); - return ret; -} - -static void * rvmalloc(unsigned long size) -{ - void * mem; - unsigned long adr; - - size=PAGE_ALIGN(size); - mem=vmalloc_32(size); - if (mem) - { - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr=(unsigned long) mem; - while (size > 0) - { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr+=PAGE_SIZE; - size-=PAGE_SIZE; - } - } - return mem; -} - -static void rvfree(void * mem, unsigned long size) -{ - unsigned long adr; - - if (mem) - { - adr=(unsigned long) mem; - while ((long) size > 0) - { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr+=PAGE_SIZE; - size-=PAGE_SIZE; - } - vfree(mem); - } -} - - - - -static int pwc_allocate_buffers(struct pwc_device *pdev) -{ - int i; - void *kbuf; - - Trace(TRACE_MEMORY, ">> pwc_allocate_buffers(pdev = 0x%p)\n", pdev); - - if (pdev == NULL) - return -ENXIO; - -#ifdef PWC_MAGIC - if (pdev->magic != PWC_MAGIC) { - Err("allocate_buffers(): magic failed.\n"); - return -ENXIO; - } -#endif - /* Allocate Isochronuous pipe buffers */ - for (i = 0; i < MAX_ISO_BUFS; i++) { - if (pdev->sbuf[i].data == NULL) { - kbuf = kmalloc(ISO_BUFFER_SIZE, GFP_KERNEL); - if (kbuf == NULL) { - Err("Failed to allocate iso buffer %d.\n", i); - return -ENOMEM; - } - Trace(TRACE_MEMORY, "Allocated iso buffer at %p.\n", kbuf); - pdev->sbuf[i].data = kbuf; - memset(kbuf, 0, ISO_BUFFER_SIZE); - } - } - - /* Allocate frame buffer structure */ - if (pdev->fbuf == NULL) { - kbuf = kmalloc(default_fbufs * sizeof(struct pwc_frame_buf), GFP_KERNEL); - if (kbuf == NULL) { - Err("Failed to allocate frame buffer structure.\n"); - return -ENOMEM; - } - Trace(TRACE_MEMORY, "Allocated frame buffer structure at %p.\n", kbuf); - pdev->fbuf = kbuf; - memset(kbuf, 0, default_fbufs * sizeof(struct pwc_frame_buf)); - } - /* create frame buffers, and make circular ring */ - for (i = 0; i < default_fbufs; i++) { - if (pdev->fbuf[i].data == NULL) { - kbuf = vmalloc(PWC_FRAME_SIZE); /* need vmalloc since frame buffer > 128K */ - if (kbuf == NULL) { - Err("Failed to allocate frame buffer %d.\n", i); - return -ENOMEM; - } - Trace(TRACE_MEMORY, "Allocated frame buffer %d at %p.\n", i, kbuf); - pdev->fbuf[i].data = kbuf; - memset(kbuf, 128, PWC_FRAME_SIZE); - } - } - - /* Allocate decompressor table space */ - kbuf = NULL; - if (pdev->decompressor != NULL) { - kbuf = kmalloc(pdev->decompressor->table_size, GFP_KERNEL); - if (kbuf == NULL) { - Err("Failed to allocate decompress table.\n"); - return -ENOMEM; - } - Trace(TRACE_MEMORY, "Allocated decompress table %p.\n", kbuf); - } - pdev->decompress_data = kbuf; - - /* Allocate image buffer; double buffer for mmap() */ - kbuf = rvmalloc(default_mbufs * pdev->len_per_image); - if (kbuf == NULL) { - Err("Failed to allocate image buffer(s).\n"); - return -ENOMEM; - } - Trace(TRACE_MEMORY, "Allocated image buffer at %p.\n", kbuf); - pdev->image_data = kbuf; - for (i = 0; i < default_mbufs; i++) - pdev->image_ptr[i] = kbuf + i * pdev->len_per_image; - for (; i < MAX_IMAGES; i++) - pdev->image_ptr[i] = NULL; - - kbuf = NULL; - - Trace(TRACE_MEMORY, "<< pwc_allocate_buffers()\n"); - return 0; -} - -static void pwc_free_buffers(struct pwc_device *pdev) -{ - int i; - - Trace(TRACE_MEMORY, "Entering free_buffers(%p).\n", pdev); - - if (pdev == NULL) - return; -#ifdef PWC_MAGIC - if (pdev->magic != PWC_MAGIC) { - Err("free_buffers(): magic failed.\n"); - return; - } -#endif - - /* Release Iso-pipe buffers */ - for (i = 0; i < MAX_ISO_BUFS; i++) - if (pdev->sbuf[i].data != NULL) { - Trace(TRACE_MEMORY, "Freeing ISO buffer at %p.\n", pdev->sbuf[i].data); - kfree(pdev->sbuf[i].data); - pdev->sbuf[i].data = NULL; - } - - /* The same for frame buffers */ - if (pdev->fbuf != NULL) { - for (i = 0; i < default_fbufs; i++) { - if (pdev->fbuf[i].data != NULL) { - Trace(TRACE_MEMORY, "Freeing frame buffer %d at %p.\n", i, pdev->fbuf[i].data); - vfree(pdev->fbuf[i].data); - pdev->fbuf[i].data = NULL; - } - } - kfree(pdev->fbuf); - pdev->fbuf = NULL; - } - - /* Intermediate decompression buffer & tables */ - if (pdev->decompress_data != NULL) { - Trace(TRACE_MEMORY, "Freeing decompression buffer at %p.\n", pdev->decompress_data); - kfree(pdev->decompress_data); - pdev->decompress_data = NULL; - } - pdev->decompressor = NULL; - - /* Release image buffers */ - if (pdev->image_data != NULL) { - Trace(TRACE_MEMORY, "Freeing image buffer at %p.\n", pdev->image_data); - rvfree(pdev->image_data, default_mbufs * pdev->len_per_image); - } - pdev->image_data = NULL; - - Trace(TRACE_MEMORY, "Leaving free_buffers().\n"); -} - -/* The frame & image buffer mess. - - Yes, this is a mess. Well, it used to be simple, but alas... In this - module, 3 buffers schemes are used to get the data from the USB bus to - the user program. The first scheme involves the ISO buffers (called thus - since they transport ISO data from the USB controller), and not really - interesting. Suffices to say the data from this buffer is quickly - gathered in an interrupt handler (pwc_isoc_handler) and placed into the - frame buffer. - - The frame buffer is the second scheme, and is the central element here. - It collects the data from a single frame from the camera (hence, the - name). Frames are delimited by the USB camera with a short USB packet, - so that's easy to detect. The frame buffers form a list that is filled - by the camera+USB controller and drained by the user process through - either read() or mmap(). - - The image buffer is the third scheme, in which frames are decompressed - and converted into planar format. For mmap() there is more than - one image buffer available. - - The frame buffers provide the image buffering. In case the user process - is a bit slow, this introduces lag and some undesired side-effects. - The problem arises when the frame buffer is full. I used to drop the last - frame, which makes the data in the queue stale very quickly. But dropping - the frame at the head of the queue proved to be a litte bit more difficult. - I tried a circular linked scheme, but this introduced more problems than - it solved. - - Because filling and draining are completely asynchronous processes, this - requires some fiddling with pointers and mutexes. - - Eventually, I came up with a system with 2 lists: an 'empty' frame list - and a 'full' frame list: - * Initially, all frame buffers but one are on the 'empty' list; the one - remaining buffer is our initial fill frame. - * If a frame is needed for filling, we try to take it from the 'empty' - list, unless that list is empty, in which case we take the buffer at - the head of the 'full' list. - * When our fill buffer has been filled, it is appended to the 'full' - list. - * If a frame is needed by read() or mmap(), it is taken from the head of - the 'full' list, handled, and then appended to the 'empty' list. If no - buffer is present on the 'full' list, we wait. - The advantage is that the buffer that is currently being decompressed/ - converted, is on neither list, and thus not in our way (any other scheme - I tried had the problem of old data lingering in the queue). - - Whatever strategy you choose, it always remains a tradeoff: with more - frame buffers the chances of a missed frame are reduced. On the other - hand, on slower machines it introduces lag because the queue will - always be full. - */ - -/** - \brief Find next frame buffer to fill. Take from empty or full list, whichever comes first. - */ -static inline int pwc_next_fill_frame(struct pwc_device *pdev) -{ - int ret; - unsigned long flags; - - ret = 0; - spin_lock_irqsave(&pdev->ptrlock, flags); - if (pdev->fill_frame != NULL) { - /* append to 'full' list */ - if (pdev->full_frames == NULL) { - pdev->full_frames = pdev->fill_frame; - pdev->full_frames_tail = pdev->full_frames; - } - else { - pdev->full_frames_tail->next = pdev->fill_frame; - pdev->full_frames_tail = pdev->fill_frame; - } - } - if (pdev->empty_frames != NULL) { - /* We have empty frames available. That's easy */ - pdev->fill_frame = pdev->empty_frames; - pdev->empty_frames = pdev->empty_frames->next; - } - else { - /* Hmm. Take it from the full list */ -#if PWC_DEBUG - /* sanity check */ - if (pdev->full_frames == NULL) { - Err("Neither empty or full frames available!\n"); - spin_unlock_irqrestore(&pdev->ptrlock, flags); - return -EINVAL; - } -#endif - pdev->fill_frame = pdev->full_frames; - pdev->full_frames = pdev->full_frames->next; - ret = 1; - } - pdev->fill_frame->next = NULL; -#if PWC_DEBUG - Trace(TRACE_SEQUENCE, "Assigning sequence number %d.\n", pdev->sequence); - pdev->fill_frame->sequence = pdev->sequence++; -#endif - spin_unlock_irqrestore(&pdev->ptrlock, flags); - return ret; -} - - -/** - \brief Reset all buffers, pointers and lists, except for the image_used[] buffer. - - If the image_used[] buffer is cleared too, mmap()/VIDIOCSYNC will run into trouble. - */ -static void pwc_reset_buffers(struct pwc_device *pdev) -{ - int i; - unsigned long flags; - - spin_lock_irqsave(&pdev->ptrlock, flags); - pdev->full_frames = NULL; - pdev->full_frames_tail = NULL; - for (i = 0; i < default_fbufs; i++) { - pdev->fbuf[i].filled = 0; - if (i > 0) - pdev->fbuf[i].next = &pdev->fbuf[i - 1]; - else - pdev->fbuf->next = NULL; - } - pdev->empty_frames = &pdev->fbuf[default_fbufs - 1]; - pdev->empty_frames_tail = pdev->fbuf; - pdev->read_frame = NULL; - pdev->fill_frame = pdev->empty_frames; - pdev->empty_frames = pdev->empty_frames->next; - - pdev->image_read_pos = 0; - pdev->fill_image = 0; - spin_unlock_irqrestore(&pdev->ptrlock, flags); -} - - -/** - \brief Do all the handling for getting one frame: get pointer, decompress, advance pointers. - */ -static int pwc_handle_frame(struct pwc_device *pdev) -{ - int ret = 0; - unsigned long flags; - - spin_lock_irqsave(&pdev->ptrlock, flags); - /* First grab our read_frame; this is removed from all lists, so - we can release the lock after this without problems */ - if (pdev->read_frame != NULL) { - /* This can't theoretically happen */ - Err("Huh? Read frame still in use?\n"); - } - else { - if (pdev->full_frames == NULL) { - Err("Woops. No frames ready.\n"); - } - else { - pdev->read_frame = pdev->full_frames; - pdev->full_frames = pdev->full_frames->next; - pdev->read_frame->next = NULL; - } - - if (pdev->read_frame != NULL) { -#if PWC_DEBUG - Trace(TRACE_SEQUENCE, "Decompressing frame %d\n", pdev->read_frame->sequence); -#endif - /* Decompression is a lenghty process, so it's outside of the lock. - This gives the isoc_handler the opportunity to fill more frames - in the mean time. - */ - spin_unlock_irqrestore(&pdev->ptrlock, flags); - ret = pwc_decompress(pdev); - spin_lock_irqsave(&pdev->ptrlock, flags); - - /* We're done with read_buffer, tack it to the end of the empty buffer list */ - if (pdev->empty_frames == NULL) { - pdev->empty_frames = pdev->read_frame; - pdev->empty_frames_tail = pdev->empty_frames; - } - else { - pdev->empty_frames_tail->next = pdev->read_frame; - pdev->empty_frames_tail = pdev->read_frame; - } - pdev->read_frame = NULL; - } - } - spin_unlock_irqrestore(&pdev->ptrlock, flags); - return ret; -} - -/** - \brief Advance pointers of image buffer (after each user request) -*/ -static inline void pwc_next_image(struct pwc_device *pdev) -{ - pdev->image_used[pdev->fill_image] = 0; - pdev->fill_image = (pdev->fill_image + 1) % default_mbufs; -} - - -/* This gets called for the Isochronous pipe (video). This is done in - * interrupt time, so it has to be fast, not crash, and not stall. Neat. - */ -static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs) -{ - struct pwc_device *pdev; - int i, fst, flen; - int awake; - struct pwc_frame_buf *fbuf; - unsigned char *fillptr = NULL; - unsigned char *iso_buf = NULL; - - awake = 0; - pdev = (struct pwc_device *)urb->context; - if (pdev == NULL) { - Err("isoc_handler() called with NULL device?!\n"); - return; - } -#ifdef PWC_MAGIC - if (pdev->magic != PWC_MAGIC) { - Err("isoc_handler() called with bad magic!\n"); - return; - } -#endif - if (urb->status == -ENOENT || urb->status == -ECONNRESET) { - Trace(TRACE_OPEN, "pwc_isoc_handler(): URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a"); - return; - } - if (urb->status != -EINPROGRESS && urb->status != 0) { - const char *errmsg; - - errmsg = "Unknown"; - switch(urb->status) { - case -ENOSR: errmsg = "Buffer error (overrun)"; break; - case -EPIPE: errmsg = "Stalled (device not responding)"; break; - case -EOVERFLOW: errmsg = "Babble (bad cable?)"; break; - case -EPROTO: errmsg = "Bit-stuff error (bad cable?)"; break; - case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break; - case -ETIMEDOUT: errmsg = "NAK (device does not respond)"; break; - } - Trace(TRACE_FLOW, "pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg); - /* Give up after a number of contiguous errors on the USB bus. - Appearantly something is wrong so we simulate an unplug event. - */ - if (++pdev->visoc_errors > MAX_ISOC_ERRORS) - { - Info("Too many ISOC errors, bailing out.\n"); - pdev->error_status = EIO; - awake = 1; - wake_up_interruptible(&pdev->frameq); - } - goto handler_end; // ugly, but practical - } - - fbuf = pdev->fill_frame; - if (fbuf == NULL) { - Err("pwc_isoc_handler without valid fill frame.\n"); - awake = 1; - goto handler_end; - } - else { - fillptr = fbuf->data + fbuf->filled; - } - - /* Reset ISOC error counter. We did get here, after all. */ - pdev->visoc_errors = 0; - - /* vsync: 0 = don't copy data - 1 = sync-hunt - 2 = synched - */ - /* Compact data */ - for (i = 0; i < urb->number_of_packets; i++) { - fst = urb->iso_frame_desc[i].status; - flen = urb->iso_frame_desc[i].actual_length; - iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - if (fst == 0) { - if (flen > 0) { /* if valid data... */ - if (pdev->vsync > 0) { /* ...and we are not sync-hunting... */ - pdev->vsync = 2; - - /* ...copy data to frame buffer, if possible */ - if (flen + fbuf->filled > pdev->frame_total_size) { - Trace(TRACE_FLOW, "Frame buffer overflow (flen = %d, frame_total_size = %d).\n", flen, pdev->frame_total_size); - pdev->vsync = 0; /* Hmm, let's wait for an EOF (end-of-frame) */ - pdev->vframes_error++; - } - else { - memmove(fillptr, iso_buf, flen); - fillptr += flen; - } - } - fbuf->filled += flen; - } /* ..flen > 0 */ - - if (flen < pdev->vlast_packet_size) { - /* Shorter packet... We probably have the end of an image-frame; - wake up read() process and let select()/poll() do something. - Decompression is done in user time over there. - */ - if (pdev->vsync == 2) { - /* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus - frames on the USB wire after an exposure change. This conditition is - however detected in the cam and a bit is set in the header. - */ - if (pdev->type == 730) { - unsigned char *ptr = (unsigned char *)fbuf->data; - - if (ptr[1] == 1 && ptr[0] & 0x10) { -#if PWC_DEBUG - Debug("Hyundai CMOS sensor bug. Dropping frame %d.\n", fbuf->sequence); -#endif - pdev->drop_frames += 2; - pdev->vframes_error++; - } - if ((ptr[0] ^ pdev->vmirror) & 0x01) { - if (ptr[0] & 0x01) - Info("Snapshot button pressed.\n"); - else - Info("Snapshot button released.\n"); - } - if ((ptr[0] ^ pdev->vmirror) & 0x02) { - if (ptr[0] & 0x02) - Info("Image is mirrored.\n"); - else - Info("Image is normal.\n"); - } - pdev->vmirror = ptr[0] & 0x03; - /* Sometimes the trailer of the 730 is still sent as a 4 byte packet - after a short frame; this condition is filtered out specifically. A 4 byte - frame doesn't make sense anyway. - So we get either this sequence: - drop_bit set -> 4 byte frame -> short frame -> good frame - Or this one: - drop_bit set -> short frame -> good frame - So we drop either 3 or 2 frames in all! - */ - if (fbuf->filled == 4) - pdev->drop_frames++; - } - - /* In case we were instructed to drop the frame, do so silently. - The buffer pointers are not updated either (but the counters are reset below). - */ - if (pdev->drop_frames > 0) - pdev->drop_frames--; - else { - /* Check for underflow first */ - if (fbuf->filled < pdev->frame_total_size) { - Trace(TRACE_FLOW, "Frame buffer underflow (%d bytes); discarded.\n", fbuf->filled); - pdev->vframes_error++; - } - else { - /* Send only once per EOF */ - awake = 1; /* delay wake_ups */ - - /* Find our next frame to fill. This will always succeed, since we - * nick a frame from either empty or full list, but if we had to - * take it from the full list, it means a frame got dropped. - */ - if (pwc_next_fill_frame(pdev)) { - pdev->vframes_dumped++; - if ((pdev->vframe_count > FRAME_LOWMARK) && (pwc_trace & TRACE_FLOW)) { - if (pdev->vframes_dumped < 20) - Trace(TRACE_FLOW, "Dumping frame %d.\n", pdev->vframe_count); - if (pdev->vframes_dumped == 20) - Trace(TRACE_FLOW, "Dumping frame %d (last message).\n", pdev->vframe_count); - } - } - fbuf = pdev->fill_frame; - } - } /* !drop_frames */ - pdev->vframe_count++; - } - fbuf->filled = 0; - fillptr = fbuf->data; - pdev->vsync = 1; - } /* .. flen < last_packet_size */ - pdev->vlast_packet_size = flen; - } /* ..status == 0 */ -#if PWC_DEBUG - /* This is normally not interesting to the user, unless you are really debugging something */ - else { - static int iso_error = 0; - iso_error++; - if (iso_error < 20) - Trace(TRACE_FLOW, "Iso frame %d of USB has error %d\n", i, fst); - } -#endif - } - -handler_end: - if (awake) - wake_up_interruptible(&pdev->frameq); - - urb->dev = pdev->udev; - i = usb_submit_urb(urb, GFP_ATOMIC); - if (i != 0) - Err("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i); -} - - -static int pwc_isoc_init(struct pwc_device *pdev) -{ - struct usb_device *udev; - struct urb *urb; - int i, j, ret; - - struct usb_interface *intf; - struct usb_host_interface *idesc = NULL; - - if (pdev == NULL) - return -EFAULT; - if (pdev->iso_init) - return 0; - pdev->vsync = 0; - udev = pdev->udev; - - /* Get the current alternate interface, adjust packet size */ - if (!udev->actconfig) - return -EFAULT; - intf = usb_ifnum_to_if(udev, 0); - if (intf) - idesc = usb_altnum_to_altsetting(intf, pdev->valternate); - if (!idesc) - return -EFAULT; - - /* Search video endpoint */ - pdev->vmax_packet_size = -1; - for (i = 0; i < idesc->desc.bNumEndpoints; i++) - if ((idesc->endpoint[i].desc.bEndpointAddress & 0xF) == pdev->vendpoint) { - pdev->vmax_packet_size = idesc->endpoint[i].desc.wMaxPacketSize; - break; - } - - if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) { - Err("Failed to find packet size for video endpoint in current alternate setting.\n"); - return -ENFILE; /* Odd error, that should be noticable */ - } - - /* Set alternate interface */ - ret = 0; - Trace(TRACE_OPEN, "Setting alternate interface %d\n", pdev->valternate); - ret = usb_set_interface(pdev->udev, 0, pdev->valternate); - if (ret < 0) - return ret; - - for (i = 0; i < MAX_ISO_BUFS; i++) { - urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); - if (urb == NULL) { - Err("Failed to allocate urb %d\n", i); - ret = -ENOMEM; - break; - } - pdev->sbuf[i].urb = urb; - Trace(TRACE_MEMORY, "Allocated URB at 0x%p\n", urb); - } - if (ret) { - /* De-allocate in reverse order */ - while (i >= 0) { - if (pdev->sbuf[i].urb != NULL) - usb_free_urb(pdev->sbuf[i].urb); - pdev->sbuf[i].urb = NULL; - i--; - } - return ret; - } - - /* init URB structure */ - for (i = 0; i < MAX_ISO_BUFS; i++) { - urb = pdev->sbuf[i].urb; - - urb->interval = 1; // devik - urb->dev = udev; - urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint); - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = pdev->sbuf[i].data; - urb->transfer_buffer_length = ISO_BUFFER_SIZE; - urb->complete = pwc_isoc_handler; - urb->context = pdev; - urb->start_frame = 0; - urb->number_of_packets = ISO_FRAMES_PER_DESC; - for (j = 0; j < ISO_FRAMES_PER_DESC; j++) { - urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE; - urb->iso_frame_desc[j].length = pdev->vmax_packet_size; - } - } - - /* link */ - for (i = 0; i < MAX_ISO_BUFS; i++) { - ret = usb_submit_urb(pdev->sbuf[i].urb, GFP_KERNEL); - if (ret) - Err("isoc_init() submit_urb %d failed with error %d\n", i, ret); - else - Trace(TRACE_MEMORY, "URB 0x%p submitted.\n", pdev->sbuf[i].urb); - } - - /* All is done... */ - pdev->iso_init = 1; - Trace(TRACE_OPEN, "<< pwc_isoc_init()\n"); - return 0; -} - -static void pwc_isoc_cleanup(struct pwc_device *pdev) -{ - int i; - - Trace(TRACE_OPEN, ">> pwc_isoc_cleanup()\n"); - if (pdev == NULL) - return; - - /* Unlinking ISOC buffers one by one */ - for (i = 0; i < MAX_ISO_BUFS; i++) { - struct urb *urb; - - urb = pdev->sbuf[i].urb; - if (urb != 0) { - if (pdev->iso_init) { - Trace(TRACE_MEMORY, "Unlinking URB %p\n", urb); - usb_unlink_urb(urb); - } - Trace(TRACE_MEMORY, "Freeing URB\n"); - usb_free_urb(urb); - pdev->sbuf[i].urb = NULL; - } - } - - /* Stop camera, but only if we are sure the camera is still there (unplug - is signalled by EPIPE) - */ - if (pdev->error_status && pdev->error_status != EPIPE) { - Trace(TRACE_OPEN, "Setting alternate interface 0.\n"); - usb_set_interface(pdev->udev, 0, 0); - } - - pdev->iso_init = 0; - Trace(TRACE_OPEN, "<< pwc_isoc_cleanup()\n"); -} - -int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot) -{ - int ret, start; - - /* Stop isoc stuff */ - pwc_isoc_cleanup(pdev); - /* Reset parameters */ - pwc_reset_buffers(pdev); - /* Try to set video mode... */ - start = ret = pwc_set_video_mode(pdev, width, height, new_fps, new_compression, new_snapshot); - if (ret) { - Trace(TRACE_FLOW, "pwc_set_video_mode attempt 1 failed.\n"); - /* That failed... restore old mode (we know that worked) */ - start = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot); - if (start) { - Trace(TRACE_FLOW, "pwc_set_video_mode attempt 2 failed.\n"); - } - } - if (start == 0) - { - if (pwc_isoc_init(pdev) < 0) - { - Info("Failed to restart ISOC transfers in pwc_try_video_mode.\n"); - ret = -EAGAIN; /* let's try again, who knows if it works a second time */ - } - } - pdev->drop_frames++; /* try to avoid garbage during switch */ - return ret; /* Return original error code */ -} - - -/***************************************************************************/ -/* Video4Linux functions */ - -static int pwc_video_open(struct inode *inode, struct file *file) -{ - int i; - struct video_device *vdev = video_devdata(file); - struct pwc_device *pdev; - - Trace(TRACE_OPEN, ">> video_open called(vdev = 0x%p).\n", vdev); - - pdev = (struct pwc_device *)vdev->priv; - if (pdev == NULL) - BUG(); - if (pdev->vopen) - return -EBUSY; - - down(&pdev->modlock); - if (!pdev->usb_init) { - Trace(TRACE_OPEN, "Doing first time initialization.\n"); - pdev->usb_init = 1; - - if (pwc_trace & TRACE_OPEN) - { - /* Query sensor type */ - const char *sensor_type = NULL; - int ret; - - ret = pwc_get_cmos_sensor(pdev, &i); - if (ret >= 0) - { - switch(i) { - case 0x00: sensor_type = "Hyundai CMOS sensor"; break; - case 0x20: sensor_type = "Sony CCD sensor + TDA8787"; break; - case 0x2E: sensor_type = "Sony CCD sensor + Exas 98L59"; break; - case 0x2F: sensor_type = "Sony CCD sensor + ADI 9804"; break; - case 0x30: sensor_type = "Sharp CCD sensor + TDA8787"; break; - case 0x3E: sensor_type = "Sharp CCD sensor + Exas 98L59"; break; - case 0x3F: sensor_type = "Sharp CCD sensor + ADI 9804"; break; - case 0x40: sensor_type = "UPA 1021 sensor"; break; - case 0x100: sensor_type = "VGA sensor"; break; - case 0x101: sensor_type = "PAL MR sensor"; break; - default: sensor_type = "unknown type of sensor"; break; - } - } - if (sensor_type != NULL) - Info("This %s camera is equipped with a %s (%d).\n", pdev->vdev->name, sensor_type, i); - } - } - - /* Turn on camera */ - if (power_save) { - i = pwc_camera_power(pdev, 1); - if (i < 0) - Info("Failed to restore power to the camera! (%d)\n", i); - } - /* Set LED on/off time */ - if (pwc_set_leds(pdev, led_on, led_off) < 0) - Info("Failed to set LED on/off time.\n"); - - /* Find our decompressor, if any */ - pdev->decompressor = pwc_find_decompressor(pdev->type); -#if PWC_DEBUG - Debug("Found decompressor for %d at 0x%p\n", pdev->type, pdev->decompressor); -#endif - pwc_construct(pdev); /* set min/max sizes correct */ - - /* So far, so good. Allocate memory. */ - i = pwc_allocate_buffers(pdev); - if (i < 0) { - Trace(TRACE_OPEN, "Failed to allocate buffer memory.\n"); - up(&pdev->modlock); - return i; - } - - /* Reset buffers & parameters */ - pwc_reset_buffers(pdev); - for (i = 0; i < default_mbufs; i++) - pdev->image_used[i] = 0; - pdev->vframe_count = 0; - pdev->vframes_dumped = 0; - pdev->vframes_error = 0; - pdev->visoc_errors = 0; - pdev->error_status = 0; -#if PWC_DEBUG - pdev->sequence = 0; -#endif - pwc_construct(pdev); /* set min/max sizes correct */ - - /* Set some defaults */ - pdev->vsnapshot = 0; - - /* Start iso pipe for video; first try the last used video size - (or the default one); if that fails try QCIF/10 or QSIF/10; - it that fails too, give up. - */ - i = pwc_set_video_mode(pdev, pwc_image_sizes[pdev->vsize].x, pwc_image_sizes[pdev->vsize].y, pdev->vframes, pdev->vcompression, 0); - if (i) { - Trace(TRACE_OPEN, "First attempt at set_video_mode failed.\n"); - if (pdev->type == 730 || pdev->type == 740 || pdev->type == 750) - i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QSIF].x, pwc_image_sizes[PSZ_QSIF].y, 10, pdev->vcompression, 0); - else - i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QCIF].x, pwc_image_sizes[PSZ_QCIF].y, 10, pdev->vcompression, 0); - } - if (i) { - Trace(TRACE_OPEN, "Second attempt at set_video_mode failed.\n"); - up(&pdev->modlock); - return i; - } - - i = pwc_isoc_init(pdev); - if (i) { - Trace(TRACE_OPEN, "Failed to init ISOC stuff = %d.\n", i); - up(&pdev->modlock); - return i; - } - - pdev->vopen++; - file->private_data = vdev; - /* lock decompressor; this has a small race condition, since we - could in theory unload pwcx.o between pwc_find_decompressor() - above and this call. I doubt it's ever going to be a problem. - */ - if (pdev->decompressor != NULL) - pdev->decompressor->lock(); - up(&pdev->modlock); - Trace(TRACE_OPEN, "<< video_open() returns 0.\n"); - return 0; -} - -/* Note that all cleanup is done in the reverse order as in _open */ -static int pwc_video_close(struct inode *inode, struct file *file) -{ - struct video_device *vdev = file->private_data; - struct pwc_device *pdev; - int i; - - Trace(TRACE_OPEN, ">> video_close called(vdev = 0x%p).\n", vdev); - - pdev = (struct pwc_device *)vdev->priv; - if (pdev->vopen == 0) - Info("video_close() called on closed device?\n"); - - /* Dump statistics, but only if a reasonable amount of frames were - processed (to prevent endless log-entries in case of snap-shot - programs) - */ - if (pdev->vframe_count > 20) - Info("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error); - - if (pdev->decompressor != NULL) { - pdev->decompressor->exit(); - pdev->decompressor->unlock(); - pdev->decompressor = NULL; - } - - pwc_isoc_cleanup(pdev); - pwc_free_buffers(pdev); - - /* Turn off LEDS and power down camera, but only when not unplugged */ - if (pdev->error_status != EPIPE) { - /* Turn LEDs off */ - if (pwc_set_leds(pdev, 0, 0) < 0) - Info("Failed to set LED on/off time.\n"); - if (power_save) { - i = pwc_camera_power(pdev, 0); - if (i < 0) - Err("Failed to power down camera (%d)\n", i); - } - } - pdev->vopen = 0; - Trace(TRACE_OPEN, "<< video_close()\n"); - return 0; -} - -/* - * FIXME: what about two parallel reads ???? - * ANSWER: Not supported. You can't open the device more than once, - despite what the V4L1 interface says. First, I don't see - the need, second there's no mechanism of alerting the - 2nd/3rd/... process of events like changing image size. - And I don't see the point of blocking that for the - 2nd/3rd/... process. - In multi-threaded environments reading parallel from any - device is tricky anyhow. - */ - -static ssize_t pwc_video_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct video_device *vdev = file->private_data; - struct pwc_device *pdev; - int noblock = file->f_flags & O_NONBLOCK; - DECLARE_WAITQUEUE(wait, current); - int bytes_to_read; - - Trace(TRACE_READ, "video_read(0x%p, %p, %zd) called.\n", vdev, buf, count); - if (vdev == NULL) - return -EFAULT; - pdev = vdev->priv; - if (pdev == NULL) - return -EFAULT; - if (pdev->error_status) - return -pdev->error_status; /* Something happened, report what. */ - - /* In case we're doing partial reads, we don't have to wait for a frame */ - if (pdev->image_read_pos == 0) { - /* Do wait queueing according to the (doc)book */ - add_wait_queue(&pdev->frameq, &wait); - while (pdev->full_frames == NULL) { - /* Check for unplugged/etc. here */ - if (pdev->error_status) { - remove_wait_queue(&pdev->frameq, &wait); - set_current_state(TASK_RUNNING); - return -pdev->error_status ; - } - if (noblock) { - remove_wait_queue(&pdev->frameq, &wait); - set_current_state(TASK_RUNNING); - return -EWOULDBLOCK; - } - if (signal_pending(current)) { - remove_wait_queue(&pdev->frameq, &wait); - set_current_state(TASK_RUNNING); - return -ERESTARTSYS; - } - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - } - remove_wait_queue(&pdev->frameq, &wait); - set_current_state(TASK_RUNNING); - - /* Decompress and release frame */ - if (pwc_handle_frame(pdev)) - return -EFAULT; - } - - Trace(TRACE_READ, "Copying data to user space.\n"); - if (pdev->vpalette == VIDEO_PALETTE_RAW) - bytes_to_read = pdev->frame_size; - else - bytes_to_read = pdev->view.size; - - /* copy bytes to user space; we allow for partial reads */ - if (count + pdev->image_read_pos > bytes_to_read) - count = bytes_to_read - pdev->image_read_pos; - if (copy_to_user(buf, pdev->image_ptr[pdev->fill_image] + pdev->image_read_pos, count)) - return -EFAULT; - pdev->image_read_pos += count; - if (pdev->image_read_pos >= bytes_to_read) { /* All data has been read */ - pdev->image_read_pos = 0; - pwc_next_image(pdev); - } - return count; -} - -static unsigned int pwc_video_poll(struct file *file, poll_table *wait) -{ - struct video_device *vdev = file->private_data; - struct pwc_device *pdev; - - if (vdev == NULL) - return -EFAULT; - pdev = vdev->priv; - if (pdev == NULL) - return -EFAULT; - - poll_wait(file, &pdev->frameq, wait); - if (pdev->error_status) - return POLLERR; - if (pdev->full_frames != NULL) /* we have frames waiting */ - return (POLLIN | POLLRDNORM); - - return 0; -} - -static int pwc_video_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) -{ - struct video_device *vdev = file->private_data; - struct pwc_device *pdev; - DECLARE_WAITQUEUE(wait, current); - - if (vdev == NULL) - return -EFAULT; - pdev = vdev->priv; - if (pdev == NULL) - return -EFAULT; - - switch (cmd) { - /* Query cabapilities */ - case VIDIOCGCAP: - { - struct video_capability *caps = arg; - - strcpy(caps->name, vdev->name); - caps->type = VID_TYPE_CAPTURE; - caps->channels = 1; - caps->audios = 1; - caps->minwidth = pdev->view_min.x; - caps->minheight = pdev->view_min.y; - caps->maxwidth = pdev->view_max.x; - caps->maxheight = pdev->view_max.y; - break; - } - - /* Channel functions (simulate 1 channel) */ - case VIDIOCGCHAN: - { - struct video_channel *v = arg; - - if (v->channel != 0) - return -EINVAL; - v->flags = 0; - v->tuners = 0; - v->type = VIDEO_TYPE_CAMERA; - strcpy(v->name, "Webcam"); - return 0; - } - - case VIDIOCSCHAN: - { - /* The spec says the argument is an integer, but - the bttv driver uses a video_channel arg, which - makes sense becasue it also has the norm flag. - */ - struct video_channel *v = arg; - if (v->channel != 0) - return -EINVAL; - return 0; - } - - - /* Picture functions; contrast etc. */ - case VIDIOCGPICT: - { - struct video_picture *p = arg; - int val; - - val = pwc_get_brightness(pdev); - if (val >= 0) - p->brightness = val; - else - p->brightness = 0xffff; - val = pwc_get_contrast(pdev); - if (val >= 0) - p->contrast = val; - else - p->contrast = 0xffff; - /* Gamma, Whiteness, what's the difference? :) */ - val = pwc_get_gamma(pdev); - if (val >= 0) - p->whiteness = val; - else - p->whiteness = 0xffff; - val = pwc_get_saturation(pdev); - if (val >= 0) - p->colour = val; - else - p->colour = 0xffff; - p->depth = 24; - p->palette = pdev->vpalette; - p->hue = 0xFFFF; /* N/A */ - break; - } - - case VIDIOCSPICT: - { - struct video_picture *p = arg; - /* - * FIXME: Suppose we are mid read - ANSWER: No problem: the firmware of the camera - can handle brightness/contrast/etc - changes at _any_ time, and the palette - is used exactly once in the uncompress - routine. - */ - pwc_set_brightness(pdev, p->brightness); - pwc_set_contrast(pdev, p->contrast); - pwc_set_gamma(pdev, p->whiteness); - pwc_set_saturation(pdev, p->colour); - if (p->palette && p->palette != pdev->vpalette) { - switch (p->palette) { - case VIDEO_PALETTE_YUV420P: - case VIDEO_PALETTE_RAW: - pdev->vpalette = p->palette; - return pwc_try_video_mode(pdev, pdev->image.x, pdev->image.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot); - break; - default: - return -EINVAL; - break; - } - } - break; - } - - /* Window/size parameters */ - case VIDIOCGWIN: - { - struct video_window *vw = arg; - - vw->x = 0; - vw->y = 0; - vw->width = pdev->view.x; - vw->height = pdev->view.y; - vw->chromakey = 0; - vw->flags = (pdev->vframes << PWC_FPS_SHIFT) | - (pdev->vsnapshot ? PWC_FPS_SNAPSHOT : 0); - break; - } - - case VIDIOCSWIN: - { - struct video_window *vw = arg; - int fps, snapshot, ret; - - fps = (vw->flags & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT; - snapshot = vw->flags & PWC_FPS_SNAPSHOT; - if (fps == 0) - fps = pdev->vframes; - if (pdev->view.x == vw->width && pdev->view.y && fps == pdev->vframes && snapshot == pdev->vsnapshot) - return 0; - ret = pwc_try_video_mode(pdev, vw->width, vw->height, fps, pdev->vcompression, snapshot); - if (ret) - return ret; - break; - } - - /* We don't have overlay support (yet) */ - case VIDIOCGFBUF: - { - struct video_buffer *vb = arg; - - memset(vb,0,sizeof(*vb)); - break; - } - - /* mmap() functions */ - case VIDIOCGMBUF: - { - /* Tell the user program how much memory is needed for a mmap() */ - struct video_mbuf *vm = arg; - int i; - - memset(vm, 0, sizeof(*vm)); - vm->size = default_mbufs * pdev->len_per_image; - vm->frames = default_mbufs; /* double buffering should be enough for most applications */ - for (i = 0; i < default_mbufs; i++) - vm->offsets[i] = i * pdev->len_per_image; - break; - } - - case VIDIOCMCAPTURE: - { - /* Start capture into a given image buffer (called 'frame' in video_mmap structure) */ - struct video_mmap *vm = arg; - - Trace(TRACE_READ, "VIDIOCMCAPTURE: %dx%d, frame %d, format %d\n", vm->width, vm->height, vm->frame, vm->format); - if (vm->frame < 0 || vm->frame >= default_mbufs) - return -EINVAL; - - /* xawtv is nasty. It probes the available palettes - by setting a very small image size and trying - various palettes... The driver doesn't support - such small images, so I'm working around it. - */ - if (vm->format) - { - switch (vm->format) - { - case VIDEO_PALETTE_YUV420P: - case VIDEO_PALETTE_RAW: - break; - default: - return -EINVAL; - break; - } - } - - if ((vm->width != pdev->view.x || vm->height != pdev->view.y) && - (vm->width >= pdev->view_min.x && vm->height >= pdev->view_min.y)) { - int ret; - - Trace(TRACE_OPEN, "VIDIOCMCAPTURE: changing size to please xawtv :-(.\n"); - ret = pwc_try_video_mode(pdev, vm->width, vm->height, pdev->vframes, pdev->vcompression, pdev->vsnapshot); - if (ret) - return ret; - } /* ... size mismatch */ - - /* FIXME: should we lock here? */ - if (pdev->image_used[vm->frame]) - return -EBUSY; /* buffer wasn't available. Bummer */ - pdev->image_used[vm->frame] = 1; - - /* Okay, we're done here. In the SYNC call we wait until a - frame comes available, then expand image into the given - buffer. - In contrast to the CPiA cam the Philips cams deliver a - constant stream, almost like a grabber card. Also, - we have separate buffers for the rawdata and the image, - meaning we can nearly always expand into the requested buffer. - */ - Trace(TRACE_READ, "VIDIOCMCAPTURE done.\n"); - break; - } - - case VIDIOCSYNC: - { - /* The doc says: "Whenever a buffer is used it should - call VIDIOCSYNC to free this frame up and continue." - - The only odd thing about this whole procedure is - that MCAPTURE flags the buffer as "in use", and - SYNC immediately unmarks it, while it isn't - after SYNC that you know that the buffer actually - got filled! So you better not start a CAPTURE in - the same frame immediately (use double buffering). - This is not a problem for this cam, since it has - extra intermediate buffers, but a hardware - grabber card will then overwrite the buffer - you're working on. - */ - int *mbuf = arg; - int ret; - - Trace(TRACE_READ, "VIDIOCSYNC called (%d).\n", *mbuf); - - /* bounds check */ - if (*mbuf < 0 || *mbuf >= default_mbufs) - return -EINVAL; - /* check if this buffer was requested anyway */ - if (pdev->image_used[*mbuf] == 0) - return -EINVAL; - - /* Add ourselves to the frame wait-queue. - - FIXME: needs auditing for safety. - QUESTION: In what respect? I think that using the - frameq is safe now. - */ - add_wait_queue(&pdev->frameq, &wait); - while (pdev->full_frames == NULL) { - if (pdev->error_status) { - remove_wait_queue(&pdev->frameq, &wait); - set_current_state(TASK_RUNNING); - return -pdev->error_status; - } - - if (signal_pending(current)) { - remove_wait_queue(&pdev->frameq, &wait); - set_current_state(TASK_RUNNING); - return -ERESTARTSYS; - } - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - } - remove_wait_queue(&pdev->frameq, &wait); - set_current_state(TASK_RUNNING); - - /* The frame is ready. Expand in the image buffer - requested by the user. I don't care if you - mmap() 5 buffers and request data in this order: - buffer 4 2 3 0 1 2 3 0 4 3 1 . . . - Grabber hardware may not be so forgiving. - */ - Trace(TRACE_READ, "VIDIOCSYNC: frame ready.\n"); - pdev->fill_image = *mbuf; /* tell in which buffer we want the image to be expanded */ - /* Decompress, etc */ - ret = pwc_handle_frame(pdev); - pdev->image_used[*mbuf] = 0; - if (ret) - return -EFAULT; - break; - } - - case VIDIOCGAUDIO: - { - struct video_audio *v = arg; - - strcpy(v->name, "Microphone"); - v->audio = -1; /* unknown audio minor */ - v->flags = 0; - v->mode = VIDEO_SOUND_MONO; - v->volume = 0; - v->bass = 0; - v->treble = 0; - v->balance = 0x8000; - v->step = 1; - break; - } - - case VIDIOCSAUDIO: - { - /* Dummy: nothing can be set */ - break; - } - - case VIDIOCGUNIT: - { - struct video_unit *vu = arg; - - vu->video = pdev->vdev->minor & 0x3F; - vu->audio = -1; /* not known yet */ - vu->vbi = -1; - vu->radio = -1; - vu->teletext = -1; - break; - } - default: - return pwc_ioctl(pdev, cmd, arg); - } /* ..switch */ - return 0; -} - -static int pwc_video_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(inode, file, cmd, arg, pwc_video_do_ioctl); -} - - -static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct video_device *vdev = file->private_data; - struct pwc_device *pdev; - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end-vma->vm_start; - unsigned long page, pos; - - Trace(TRACE_MEMORY, "mmap(0x%p, 0x%lx, %lu) called.\n", vdev, start, size); - pdev = vdev->priv; - - pos = (unsigned long)pdev->image_data; - while (size > 0) { - page = kvirt_to_pa(pos); - if (remap_page_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) - return -EAGAIN; - - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - - return 0; -} - -/***************************************************************************/ -/* USB functions */ - -/* This function gets called when a new device is plugged in or the usb core - * is loaded. - */ - -static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct pwc_device *pdev = NULL; - int vendor_id, product_id, type_id; - int i, hint; - int features = 0; - int video_nr = -1; /* default: use next available device */ - char serial_number[30], *name; - - /* Check if we can handle this device */ - Trace(TRACE_PROBE, "probe() called [%04X %04X], if %d\n", - udev->descriptor.idVendor, udev->descriptor.idProduct, - intf->altsetting->desc.bInterfaceNumber); - - /* the interfaces are probed one by one. We are only interested in the - video interface (0) now. - Interface 1 is the Audio Control, and interface 2 Audio itself. - */ - if (intf->altsetting->desc.bInterfaceNumber > 0) - return -ENODEV; - - vendor_id = udev->descriptor.idVendor; - product_id = udev->descriptor.idProduct; - - if (vendor_id == 0x0471) { - switch (product_id) { - case 0x0302: - Info("Philips PCA645VC USB webcam detected.\n"); - name = "Philips 645 webcam"; - type_id = 645; - break; - case 0x0303: - Info("Philips PCA646VC USB webcam detected.\n"); - name = "Philips 646 webcam"; - type_id = 646; - break; - case 0x0304: - Info("Askey VC010 type 2 USB webcam detected.\n"); - name = "Askey VC010 webcam"; - type_id = 646; - break; - case 0x0307: - Info("Philips PCVC675K (Vesta) USB webcam detected.\n"); - name = "Philips 675 webcam"; - type_id = 675; - break; - case 0x0308: - Info("Philips PCVC680K (Vesta Pro) USB webcam detected.\n"); - name = "Philips 680 webcam"; - type_id = 680; - break; - case 0x030C: - Info("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n"); - name = "Philips 690 webcam"; - type_id = 690; - break; - case 0x0310: - Info("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n"); - name = "Philips 730 webcam"; - type_id = 730; - break; - case 0x0311: - Info("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n"); - name = "Philips 740 webcam"; - type_id = 740; - break; - case 0x0312: - Info("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n"); - name = "Philips 750 webcam"; - type_id = 750; - break; - case 0x0313: - Info("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n"); - name = "Philips 720K/40 webcam"; - type_id = 720; - break; - default: - return -ENODEV; - break; - } - } - else if (vendor_id == 0x069A) { - switch(product_id) { - case 0x0001: - Info("Askey VC010 type 1 USB webcam detected.\n"); - name = "Askey VC010 webcam"; - type_id = 645; - break; - default: - return -ENODEV; - break; - } - } - else if (vendor_id == 0x046d) { - switch(product_id) { - case 0x08b0: - Info("Logitech QuickCam Pro 3000 USB webcam detected.\n"); - name = "Logitech QuickCam Pro 3000"; - type_id = 740; /* CCD sensor */ - break; - case 0x08b1: - Info("Logitech QuickCam Notebook Pro USB webcam detected.\n"); - name = "Logitech QuickCam Notebook Pro"; - type_id = 740; /* CCD sensor */ - break; - case 0x08b2: - Info("Logitech QuickCam 4000 Pro USB webcam detected.\n"); - name = "Logitech QuickCam Pro 4000"; - type_id = 740; /* CCD sensor */ - break; - case 0x08b3: - Info("Logitech QuickCam Zoom USB webcam detected.\n"); - name = "Logitech QuickCam Zoom"; - type_id = 740; /* CCD sensor */ - break; - case 0x08B4: - Info("Logitech QuickCam Zoom (new model) USB webcam detected.\n"); - name = "Logitech QuickCam Zoom"; - type_id = 740; /* CCD sensor */ - break; - case 0x08b5: - Info("Logitech QuickCam Orbit/Sphere USB webcam detected.\n"); - name = "Logitech QuickCam Orbit"; - type_id = 740; /* CCD sensor */ - features |= FEATURE_MOTOR_PANTILT; - break; - case 0x08b6: - case 0x08b7: - case 0x08b8: - Info("Logitech QuickCam detected (reserved ID).\n"); - name = "Logitech QuickCam (res.)"; - type_id = 730; /* Assuming CMOS */ - break; - default: - return -ENODEV; - break; - } - } - else if (vendor_id == 0x055d) { - /* I don't know the difference between the C10 and the C30; - I suppose the difference is the sensor, but both cameras - work equally well with a type_id of 675 - */ - switch(product_id) { - case 0x9000: - Info("Samsung MPC-C10 USB webcam detected.\n"); - name = "Samsung MPC-C10"; - type_id = 675; - break; - case 0x9001: - Info("Samsung MPC-C30 USB webcam detected.\n"); - name = "Samsung MPC-C30"; - type_id = 675; - break; - default: - return -ENODEV; - break; - } - } - else if (vendor_id == 0x041e) { - switch(product_id) { - case 0x400c: - Info("Creative Labs Webcam 5 detected.\n"); - name = "Creative Labs Webcam 5"; - type_id = 730; - break; - case 0x4011: - Info("Creative Labs Webcam Pro Ex detected.\n"); - name = "Creative Labs Webcam Pro Ex"; - type_id = 740; - break; - default: - return -ENODEV; - break; - } - } - else if (vendor_id == 0x04cc) { - switch(product_id) { - case 0x8116: - Info("Sotec Afina Eye USB webcam detected.\n"); - name = "Sotec Afina Eye"; - type_id = 730; - break; - default: - return -ENODEV; - break; - } - } - else if (vendor_id == 0x06be) { - switch(product_id) { - case 0x8116: - /* Basicly the same as the Sotec Afina Eye */ - Info("AME CU-001 USB webcam detected.\n"); - name = "AME CU-001"; - type_id = 730; - break; - default: - return -ENODEV; - break; - } - } - else if (vendor_id == 0x06be) { - switch(product_id) { - case 0x8116: - /* This is essentially the same cam as the Sotec Afina Eye */ - Info("AME Co. Afina Eye USB webcam detected.\n"); - name = "AME Co. Afina Eye"; - type_id = 750; - break; - default: - return -ENODEV; - break; - } - - } - else if (vendor_id == 0x0d81) { - switch(product_id) { - case 0x1900: - Info("Visionite VCS-UC300 USB webcam detected.\n"); - name = "Visionite VCS-UC300"; - type_id = 740; /* CCD sensor */ - break; - case 0x1910: - Info("Visionite VCS-UM100 USB webcam detected.\n"); - name = "Visionite VCS-UM100"; - type_id = 730; /* CMOS sensor */ - break; - default: - return -ENODEV; - break; - } - } - else - return -ENODEV; /* Not any of the know types; but the list keeps growing. */ - - memset(serial_number, 0, 30); - usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29); - Trace(TRACE_PROBE, "Device serial number is %s\n", serial_number); - - if (udev->descriptor.bNumConfigurations > 1) - Info("Warning: more than 1 configuration available.\n"); - - /* Allocate structure, initialize pointers, mutexes, etc. and link it to the usb_device */ - pdev = kmalloc(sizeof(struct pwc_device), GFP_KERNEL); - if (pdev == NULL) { - Err("Oops, could not allocate memory for pwc_device.\n"); - return -ENOMEM; - } - memset(pdev, 0, sizeof(struct pwc_device)); - pdev->type = type_id; - pdev->vsize = default_size; - pdev->vframes = default_fps; - strcpy(pdev->serial, serial_number); - pdev->features = features; - if (vendor_id == 0x046D && product_id == 0x08B5) - { - /* Logitech QuickCam Orbit - The ranges have been determined experimentally; they may differ from cam to cam. - Also, the exact ranges left-right and up-down are different for my cam - */ - pdev->angle_range.pan_min = -7000; - pdev->angle_range.pan_max = 7000; - pdev->angle_range.tilt_min = -3000; - pdev->angle_range.tilt_max = 2500; - } - - init_MUTEX(&pdev->modlock); - pdev->ptrlock = SPIN_LOCK_UNLOCKED; - - pdev->udev = udev; - init_waitqueue_head(&pdev->frameq); - pdev->vcompression = pwc_preferred_compression; - - /* Allocate video_device structure */ - pdev->vdev = video_device_alloc(); - if (pdev->vdev == 0) - { - Err("Err, cannot allocate video_device struture. Failing probe."); - kfree(pdev); - return -ENOMEM; - } - memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template)); - strcpy(pdev->vdev->name, name); - pdev->vdev->owner = THIS_MODULE; - video_set_drvdata(pdev->vdev, pdev); - - pdev->release = udev->descriptor.bcdDevice; - Trace(TRACE_PROBE, "Release: %04x\n", pdev->release); - - /* Now search device_hint[] table for a match, so we can hint a node number. */ - for (hint = 0; hint < MAX_DEV_HINTS; hint++) { - if (((device_hint[hint].type == -1) || (device_hint[hint].type == pdev->type)) && - (device_hint[hint].pdev == NULL)) { - /* so far, so good... try serial number */ - if ((device_hint[hint].serial_number[0] == '*') || !strcmp(device_hint[hint].serial_number, serial_number)) { - /* match! */ - video_nr = device_hint[hint].device_node; - Trace(TRACE_PROBE, "Found hint, will try to register as /dev/video%d\n", video_nr); - break; - } - } - } - - pdev->vdev->release = video_device_release; - i = video_register_device(pdev->vdev, VFL_TYPE_GRABBER, video_nr); - if (i < 0) { - Err("Failed to register as video device (%d).\n", i); - video_device_release(pdev->vdev); /* Drip... drip... drip... */ - kfree(pdev); /* Oops, no memory leaks please */ - return -EIO; - } - else { - Info("Registered as /dev/video%d.\n", pdev->vdev->minor & 0x3F); - } - - /* occupy slot */ - if (hint < MAX_DEV_HINTS) - device_hint[hint].pdev = pdev; - - Trace(TRACE_PROBE, "probe() function returning struct at 0x%p.\n", pdev); - usb_set_intfdata (intf, pdev); - return 0; -} - -/* The user janked out the cable... */ -static void usb_pwc_disconnect(struct usb_interface *intf) -{ - struct pwc_device *pdev; - int hint; - - lock_kernel(); - pdev = usb_get_intfdata (intf); - usb_set_intfdata (intf, NULL); - if (pdev == NULL) { - Err("pwc_disconnect() Called without private pointer.\n"); - goto disconnect_out; - } - if (pdev->udev == NULL) { - Err("pwc_disconnect() already called for %p\n", pdev); - goto disconnect_out; - } - if (pdev->udev != interface_to_usbdev(intf)) { - Err("pwc_disconnect() Woops: pointer mismatch udev/pdev.\n"); - goto disconnect_out; - } -#ifdef PWC_MAGIC - if (pdev->magic != PWC_MAGIC) { - Err("pwc_disconnect() Magic number failed. Consult your scrolls and try again.\n"); - goto disconnect_out; - } -#endif - - /* We got unplugged; this is signalled by an EPIPE error code */ - if (pdev->vopen) { - Info("Disconnected while webcam is in use!\n"); - pdev->error_status = EPIPE; - } - - /* Alert waiting processes */ - wake_up_interruptible(&pdev->frameq); - /* Wait until device is closed */ - while (pdev->vopen) - schedule(); - /* Device is now closed, so we can safely unregister it */ - Trace(TRACE_PROBE, "Unregistering video device in disconnect().\n"); - video_unregister_device(pdev->vdev); - - /* Free memory (don't set pdev to 0 just yet) */ - kfree(pdev); - -disconnect_out: - /* search device_hint[] table if we occupy a slot, by any chance */ - for (hint = 0; hint < MAX_DEV_HINTS; hint++) - if (device_hint[hint].pdev == pdev) - device_hint[hint].pdev = NULL; - - unlock_kernel(); -} - - -/* *grunt* We have to do atoi ourselves :-( */ -static int pwc_atoi(const char *s) -{ - int k = 0; - - k = 0; - while (*s != '\0' && *s >= '0' && *s <= '9') { - k = 10 * k + (*s - '0'); - s++; - } - return k; -} - - -/* - * Initialization code & module stuff - */ - -static char size[PSZ_MAX]; -static int fps = 0; -static int fbufs = 0; -static int mbufs = 0; -static int trace = -1; -static int compression = -1; -static char *dev_hint[MAX_DEV_HINTS] = { }; - -module_param_string(size, size, PSZ_MAX, 0); -MODULE_PARM_DESC(size, "Initial image size. One of sqcif, qsif, qcif, sif, cif, vga"); -module_param(fps, int, 0); -MODULE_PARM_DESC(fps, "Initial frames per second. Varies with model, useful range 5-30"); -module_param(fbufs, int, 0); -MODULE_PARM_DESC(fbufs, "Number of internal frame buffers to reserve"); -module_param(mbufs, int, 0); -MODULE_PARM_DESC(mbufs, "Number of external (mmap()ed) image buffers"); -module_param(trace, int, 0); -MODULE_PARM_DESC(trace, "For debugging purposes"); -module_param(power_save, int, 0); -MODULE_PARM_DESC(power_save, "Turn power save feature in camera on or off"); -module_param(compression, int, 0); -MODULE_PARM_DESC(compression, "Preferred compression quality. Range 0 (uncompressed) to 3 (high compression)"); -module_param(led_on, int, 0); -MODULE_PARM_DESC(led_on, "LED on time in milliseconds"); -module_param(led_off, int, 0); -MODULE_PARM_DESC(led_off, "LED off time in milliseconds"); -/* Commented out, as you should be using udev instead of crud like this... */ -/* MODULE_PARM(dev_hint, "0-20s"); */ -/* MODULE_PARM_DESC(dev_hint, "Device node hints"); */ - -MODULE_DESCRIPTION("Philips & OEM USB webcam driver"); -MODULE_AUTHOR("Nemosoft Unv. "); -MODULE_LICENSE("GPL"); - -static int __init usb_pwc_init(void) -{ - int i, sz; - char *sizenames[PSZ_MAX] = { "sqcif", "qsif", "qcif", "sif", "cif", "vga" }; - - Info("Philips webcam module version " PWC_VERSION " loaded.\n"); - Info("Supports Philips PCA645/646, PCVC675/680/690, PCVC720[40]/730/740/750 & PCVC830/840.\n"); - Info("Also supports the Askey VC010, various Logitech Quickcams, Samsung MPC-C10 and MPC-C30,\n"); - Info("the Creative WebCam 5 & Pro Ex, SOTEC Afina Eye and Visionite VCS-UC300 and VCS-UM100.\n"); - - if (fps) { - if (fps < 4 || fps > 30) { - Err("Framerate out of bounds (4-30).\n"); - return -EINVAL; - } - default_fps = fps; - Info("Default framerate set to %d.\n", default_fps); - } - - if (strlen(size)) { - /* string; try matching with array */ - for (sz = 0; sz < PSZ_MAX; sz++) { - if (!strcmp(sizenames[sz], size)) { /* Found! */ - default_size = sz; - break; - } - } - if (sz == PSZ_MAX) { - Err("Size not recognized; try size=[sqcif | qsif | qcif | sif | cif | vga].\n"); - return -EINVAL; - } - Info("Default image size set to %s [%dx%d].\n", sizenames[default_size], pwc_image_sizes[default_size].x, pwc_image_sizes[default_size].y); - } - if (mbufs) { - if (mbufs < 1 || mbufs > MAX_IMAGES) { - Err("Illegal number of mmap() buffers; use a number between 1 and %d.\n", MAX_IMAGES); - return -EINVAL; - } - default_mbufs = mbufs; - Info("Number of image buffers set to %d.\n", default_mbufs); - } - if (fbufs) { - if (fbufs < 2 || fbufs > MAX_FRAMES) { - Err("Illegal number of frame buffers; use a number between 2 and %d.\n", MAX_FRAMES); - return -EINVAL; - } - default_fbufs = fbufs; - Info("Number of frame buffers set to %d.\n", default_fbufs); - } - if (trace >= 0) { - Info("Trace options: 0x%04x\n", trace); - pwc_trace = trace; - } - if (compression >= 0) { - if (compression > 3) { - Err("Invalid compression setting; use a number between 0 (uncompressed) and 3 (high).\n"); - return -EINVAL; - } - pwc_preferred_compression = compression; - Info("Preferred compression set to %d.\n", pwc_preferred_compression); - } - if (power_save) - Info("Enabling power save on open/close.\n"); - - /* Big device node whoopla. Basicly, it allows you to assign a - device node (/dev/videoX) to a camera, based on its type - & serial number. The format is [type[.serialnumber]:]node. - - Any camera that isn't matched by these rules gets the next - available free device node. - */ - for (i = 0; i < MAX_DEV_HINTS; i++) { - char *s, *colon, *dot; - - /* This loop also initializes the array */ - device_hint[i].pdev = NULL; - s = dev_hint[i]; - if (s != NULL && *s != '\0') { - device_hint[i].type = -1; /* wildcard */ - strcpy(device_hint[i].serial_number, "*"); - - /* parse string: chop at ':' & '/' */ - colon = dot = s; - while (*colon != '\0' && *colon != ':') - colon++; - while (*dot != '\0' && *dot != '.') - dot++; - /* Few sanity checks */ - if (*dot != '\0' && dot > colon) { - Err("Malformed camera hint: the colon must be after the dot.\n"); - return -EINVAL; - } - - if (*colon == '\0') { - /* No colon */ - if (*dot != '\0') { - Err("Malformed camera hint: no colon + device node given.\n"); - return -EINVAL; - } - else { - /* No type or serial number specified, just a number. */ - device_hint[i].device_node = pwc_atoi(s); - } - } - else { - /* There's a colon, so we have at least a type and a device node */ - device_hint[i].type = pwc_atoi(s); - device_hint[i].device_node = pwc_atoi(colon + 1); - if (*dot != '\0') { - /* There's a serial number as well */ - int k; - - dot++; - k = 0; - while (*dot != ':' && k < 29) { - device_hint[i].serial_number[k++] = *dot; - dot++; - } - device_hint[i].serial_number[k] = '\0'; - } - } -#if PWC_DEBUG - Debug("device_hint[%d]:\n", i); - Debug(" type : %d\n", device_hint[i].type); - Debug(" serial# : %s\n", device_hint[i].serial_number); - Debug(" node : %d\n", device_hint[i].device_node); -#endif - } - else - device_hint[i].type = 0; /* not filled */ - } /* ..for MAX_DEV_HINTS */ - - Trace(TRACE_PROBE, "Registering driver at address 0x%p.\n", &pwc_driver); - return usb_register(&pwc_driver); -} - -static void __exit usb_pwc_exit(void) -{ - Trace(TRACE_MODULE, "Deregistering driver.\n"); - usb_deregister(&pwc_driver); - Info("Philips webcam module removed.\n"); -} - -module_init(usb_pwc_init); -module_exit(usb_pwc_exit); - --- linux-2.6.9-rc1/drivers/usb/media/pwc-ioctl.h 2004-08-24 00:02:33.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,279 +0,0 @@ -#ifndef PWC_IOCTL_H -#define PWC_IOCTL_H - -/* (C) 2001-2004 Nemosoft Unv. webcam@smcc.demon.nl - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -/* This is pwc-ioctl.h belonging to PWC 8.12.1 - It contains structures and defines to communicate from user space - directly to the driver. - */ - -/* - Changes - 2001/08/03 Alvarado Added ioctl constants to access methods for - changing white balance and red/blue gains - 2002/12/15 G. H. Fernandez-Toribio VIDIOCGREALSIZE - 2003/12/13 Nemosft Unv. Some modifications to make interfacing to - PWCX easier - */ - -/* These are private ioctl() commands, specific for the Philips webcams. - They contain functions not found in other webcams, and settings not - specified in the Video4Linux API. - - The #define names are built up like follows: - VIDIOC VIDeo IOCtl prefix - PWC Philps WebCam - G optional: Get - S optional: Set - ... the function - */ - - - /* Enumeration of image sizes */ -#define PSZ_SQCIF 0x00 -#define PSZ_QSIF 0x01 -#define PSZ_QCIF 0x02 -#define PSZ_SIF 0x03 -#define PSZ_CIF 0x04 -#define PSZ_VGA 0x05 -#define PSZ_MAX 6 - - -/* The frame rate is encoded in the video_window.flags parameter using - the upper 16 bits, since some flags are defined nowadays. The following - defines provide a mask and shift to filter out this value. - - In 'Snapshot' mode the camera freezes its automatic exposure and colour - balance controls. - */ -#define PWC_FPS_SHIFT 16 -#define PWC_FPS_MASK 0x00FF0000 -#define PWC_FPS_FRMASK 0x003F0000 -#define PWC_FPS_SNAPSHOT 0x00400000 - - -/* structure for transfering x & y coordinates */ -struct pwc_coord -{ - int x, y; /* guess what */ - int size; /* size, or offset */ -}; - - -/* Used with VIDIOCPWCPROBE */ -struct pwc_probe -{ - char name[32]; - int type; -}; - -struct pwc_serial -{ - char serial[30]; /* String with serial number. Contains terminating 0 */ -}; - -/* pwc_whitebalance.mode values */ -#define PWC_WB_INDOOR 0 -#define PWC_WB_OUTDOOR 1 -#define PWC_WB_FL 2 -#define PWC_WB_MANUAL 3 -#define PWC_WB_AUTO 4 - -/* Used with VIDIOCPWC[SG]AWB (Auto White Balance). - Set mode to one of the PWC_WB_* values above. - *red and *blue are the respective gains of these colour components inside - the camera; range 0..65535 - When 'mode' == PWC_WB_MANUAL, 'manual_red' and 'manual_blue' are set or read; - otherwise undefined. - 'read_red' and 'read_blue' are read-only. -*/ -struct pwc_whitebalance -{ - int mode; - int manual_red, manual_blue; /* R/W */ - int read_red, read_blue; /* R/O */ -}; - -/* - 'control_speed' and 'control_delay' are used in automatic whitebalance mode, - and tell the camera how fast it should react to changes in lighting, and - with how much delay. Valid values are 0..65535. -*/ -struct pwc_wb_speed -{ - int control_speed; - int control_delay; - -}; - -/* Used with VIDIOCPWC[SG]LED */ -struct pwc_leds -{ - int led_on; /* Led on-time; range = 0..25000 */ - int led_off; /* Led off-time; range = 0..25000 */ -}; - -/* Image size (used with GREALSIZE) */ -struct pwc_imagesize -{ - int width; - int height; -}; - -/* Defines and structures for Motorized Pan & Tilt */ -#define PWC_MPT_PAN 0x01 -#define PWC_MPT_TILT 0x02 -#define PWC_MPT_TIMEOUT 0x04 /* for status */ - -/* Set angles; when absolute != 0, the angle is absolute and the - driver calculates the relative offset for you. This can only - be used with VIDIOCPWCSANGLE; VIDIOCPWCGANGLE always returns - absolute angles. - */ -struct pwc_mpt_angles -{ - int absolute; /* write-only */ - int pan; /* degrees * 100 */ - int tilt; /* degress * 100 */ -}; - -/* Range of angles of the camera, both horizontally and vertically. - */ -struct pwc_mpt_range -{ - int pan_min, pan_max; /* degrees * 100 */ - int tilt_min, tilt_max; -}; - -struct pwc_mpt_status -{ - int status; - int time_pan; - int time_tilt; -}; - - -/* This is used for out-of-kernel decompression. With it, you can get - all the necessary information to initialize and use the decompressor - routines in standalone applications. - */ -struct pwc_video_command -{ - int type; /* camera type (645, 675, 730, etc.) */ - int release; /* release number */ - - int size; /* one of PSZ_* */ - int alternate; - int command_len; /* length of USB video command */ - unsigned char command_buf[13]; /* Actual USB video command */ - int bandlength; /* >0 = compressed */ - int frame_size; /* Size of one (un)compressed frame */ -}; - -/* Flags for PWCX subroutines. Not all modules honour all flags. */ -#define PWCX_FLAG_PLANAR 0x0001 -#define PWCX_FLAG_BAYER 0x0008 - - -/* IOCTL definitions */ - - /* Restore user settings */ -#define VIDIOCPWCRUSER _IO('v', 192) - /* Save user settings */ -#define VIDIOCPWCSUSER _IO('v', 193) - /* Restore factory settings */ -#define VIDIOCPWCFACTORY _IO('v', 194) - - /* You can manipulate the compression factor. A compression preference of 0 - means use uncompressed modes when available; 1 is low compression, 2 is - medium and 3 is high compression preferred. Of course, the higher the - compression, the lower the bandwidth used but more chance of artefacts - in the image. The driver automatically chooses a higher compression when - the preferred mode is not available. - */ - /* Set preferred compression quality (0 = uncompressed, 3 = highest compression) */ -#define VIDIOCPWCSCQUAL _IOW('v', 195, int) - /* Get preferred compression quality */ -#define VIDIOCPWCGCQUAL _IOR('v', 195, int) - - -/* Retrieve serial number of camera */ -#define VIDIOCPWCGSERIAL _IOR('v', 198, struct pwc_serial) - - /* This is a probe function; since so many devices are supported, it - becomes difficult to include all the names in programs that want to - check for the enhanced Philips stuff. So in stead, try this PROBE; - it returns a structure with the original name, and the corresponding - Philips type. - To use, fill the structure with zeroes, call PROBE and if that succeeds, - compare the name with that returned from VIDIOCGCAP; they should be the - same. If so, you can be assured it is a Philips (OEM) cam and the type - is valid. - */ -#define VIDIOCPWCPROBE _IOR('v', 199, struct pwc_probe) - - /* Set AGC (Automatic Gain Control); int < 0 = auto, 0..65535 = fixed */ -#define VIDIOCPWCSAGC _IOW('v', 200, int) - /* Get AGC; int < 0 = auto; >= 0 = fixed, range 0..65535 */ -#define VIDIOCPWCGAGC _IOR('v', 200, int) - /* Set shutter speed; int < 0 = auto; >= 0 = fixed, range 0..65535 */ -#define VIDIOCPWCSSHUTTER _IOW('v', 201, int) - - /* Color compensation (Auto White Balance) */ -#define VIDIOCPWCSAWB _IOW('v', 202, struct pwc_whitebalance) -#define VIDIOCPWCGAWB _IOR('v', 202, struct pwc_whitebalance) - - /* Auto WB speed */ -#define VIDIOCPWCSAWBSPEED _IOW('v', 203, struct pwc_wb_speed) -#define VIDIOCPWCGAWBSPEED _IOR('v', 203, struct pwc_wb_speed) - - /* LEDs on/off/blink; int range 0..65535 */ -#define VIDIOCPWCSLED _IOW('v', 205, struct pwc_leds) -#define VIDIOCPWCGLED _IOR('v', 205, struct pwc_leds) - - /* Contour (sharpness); int < 0 = auto, 0..65536 = fixed */ -#define VIDIOCPWCSCONTOUR _IOW('v', 206, int) -#define VIDIOCPWCGCONTOUR _IOR('v', 206, int) - - /* Backlight compensation; 0 = off, otherwise on */ -#define VIDIOCPWCSBACKLIGHT _IOW('v', 207, int) -#define VIDIOCPWCGBACKLIGHT _IOR('v', 207, int) - - /* Flickerless mode; = 0 off, otherwise on */ -#define VIDIOCPWCSFLICKER _IOW('v', 208, int) -#define VIDIOCPWCGFLICKER _IOR('v', 208, int) - - /* Dynamic noise reduction; 0 off, 3 = high noise reduction */ -#define VIDIOCPWCSDYNNOISE _IOW('v', 209, int) -#define VIDIOCPWCGDYNNOISE _IOR('v', 209, int) - - /* Real image size as used by the camera; tells you whether or not there's a gray border around the image */ -#define VIDIOCPWCGREALSIZE _IOR('v', 210, struct pwc_imagesize) - - /* Motorized pan & tilt functions */ -#define VIDIOCPWCMPTRESET _IOW('v', 211, int) -#define VIDIOCPWCMPTGRANGE _IOR('v', 211, struct pwc_mpt_range) -#define VIDIOCPWCMPTSANGLE _IOW('v', 212, struct pwc_mpt_angles) -#define VIDIOCPWCMPTGANGLE _IOR('v', 212, struct pwc_mpt_angles) -#define VIDIOCPWCMPTSTATUS _IOR('v', 213, struct pwc_mpt_status) - - /* Get the USB set-video command; needed for initializing libpwcx */ -#define VIDIOCPWCGVIDCMD _IOR('v', 215, struct pwc_video_command) - -#endif --- linux-2.6.9-rc1/drivers/usb/media/pwc_kiara.h 2004-08-24 00:02:25.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,270 +0,0 @@ - /* SQCIF */ - { - /* 5 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 10 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 15 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 20 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 25 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 30 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - }, - /* QSIF */ - { - /* 5 fps */ - { - {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, - {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, - {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, - {1, 146, 0, {0x1D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0x00, 0x80}}, - }, - /* 10 fps */ - { - {2, 291, 0, {0x1C, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0x01, 0x80}}, - {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, - {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, - {1, 192, 630, {0x14, 0xF4, 0x30, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xC0, 0x00, 0x80}}, - }, - /* 15 fps */ - { - {3, 437, 0, {0x1B, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x01, 0x80}}, - {2, 292, 640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}}, - {2, 292, 640, {0x13, 0xF4, 0x30, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x20, 0x24, 0x01, 0x80}}, - {1, 192, 420, {0x13, 0xF4, 0x30, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x18, 0xC0, 0x00, 0x80}}, - }, - /* 20 fps */ - { - {4, 589, 0, {0x1A, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4D, 0x02, 0x80}}, - {3, 448, 730, {0x12, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xC0, 0x01, 0x80}}, - {2, 292, 476, {0x12, 0xF4, 0x30, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0x01, 0x80}}, - {1, 192, 312, {0x12, 0xF4, 0x50, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0x00, 0x80}}, - }, - /* 25 fps */ - { - {5, 703, 0, {0x19, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x02, 0x80}}, - {3, 447, 610, {0x11, 0xF4, 0x30, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x28, 0xBF, 0x01, 0x80}}, - {2, 292, 398, {0x11, 0xF4, 0x50, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x28, 0x24, 0x01, 0x80}}, - {1, 193, 262, {0x11, 0xF4, 0x50, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x28, 0xC1, 0x00, 0x80}}, - }, - /* 30 fps */ - { - {8, 874, 0, {0x18, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x6A, 0x03, 0x80}}, - {5, 704, 730, {0x10, 0xF4, 0x30, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x28, 0xC0, 0x02, 0x80}}, - {3, 448, 492, {0x10, 0xF4, 0x30, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x28, 0xC0, 0x01, 0x80}}, - {2, 292, 320, {0x10, 0xF4, 0x50, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x28, 0x24, 0x01, 0x80}}, - }, - }, - /* QCIF */ - { - /* 5 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 10 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 15 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 20 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 25 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 30 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - }, - /* SIF */ - { - /* 5 fps */ - { - {4, 582, 0, {0x0D, 0xF4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x02, 0x80}}, - {3, 387, 1276, {0x05, 0xF4, 0x30, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x01, 0x80}}, - {2, 291, 960, {0x05, 0xF4, 0x30, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0x01, 0x80}}, - {1, 191, 630, {0x05, 0xF4, 0x50, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x18, 0xBF, 0x00, 0x80}}, - }, - /* 10 fps */ - { - {0, }, - {6, 775, 1278, {0x04, 0xF4, 0x30, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x03, 0x80}}, - {3, 447, 736, {0x04, 0xF4, 0x30, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x28, 0xBF, 0x01, 0x80}}, - {2, 292, 480, {0x04, 0xF4, 0x70, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x28, 0x24, 0x01, 0x80}}, - }, - /* 15 fps */ - { - {0, }, - {9, 955, 1050, {0x03, 0xF4, 0x30, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x03, 0x80}}, - {4, 592, 650, {0x03, 0xF4, 0x30, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x50, 0x02, 0x80}}, - {3, 448, 492, {0x03, 0xF4, 0x50, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x38, 0xC0, 0x01, 0x80}}, - }, - /* 20 fps */ - { - {0, }, - {9, 958, 782, {0x02, 0xF4, 0x30, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x03, 0x80}}, - {5, 703, 574, {0x02, 0xF4, 0x50, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x02, 0x80}}, - {3, 446, 364, {0x02, 0xF4, 0x90, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x38, 0xBE, 0x01, 0x80}}, - }, - /* 25 fps */ - { - {0, }, - {9, 958, 654, {0x01, 0xF4, 0x30, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x03, 0x80}}, - {6, 776, 530, {0x01, 0xF4, 0x50, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x03, 0x80}}, - {4, 592, 404, {0x01, 0xF4, 0x70, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x48, 0x50, 0x02, 0x80}}, - }, - /* 30 fps */ - { - {0, }, - {9, 957, 526, {0x00, 0xF4, 0x50, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x03, 0x80}}, - {6, 775, 426, {0x00, 0xF4, 0x70, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x03, 0x80}}, - {4, 590, 324, {0x00, 0x7A, 0x88, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x50, 0x4E, 0x02, 0x80}}, - }, - }, - /* CIF */ - { - /* 5 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 10 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 15 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 20 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 25 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 30 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - }, - /* VGA */ - { - /* 5 fps */ - { - {0, }, - {6, 773, 1272, {0x25, 0xF4, 0x30, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}}, - {4, 592, 976, {0x25, 0xF4, 0x50, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x02, 0x80}}, - {3, 448, 738, {0x25, 0xF4, 0x90, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x01, 0x80}}, - }, - /* 10 fps */ - { - {0, }, - {9, 956, 788, {0x24, 0xF4, 0x70, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x03, 0x80}}, - {6, 776, 640, {0x24, 0xF4, 0xB0, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x03, 0x80}}, - {4, 592, 488, {0x24, 0x7A, 0xE8, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x02, 0x80}}, - }, - /* 15 fps */ - { - {0, }, - {9, 957, 526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}}, - {9, 957, 526, {0x23, 0x7A, 0xE8, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x03, 0x80}}, - {8, 895, 492, {0x23, 0x7A, 0xE8, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x03, 0x80}}, - }, - /* 20 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 25 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 30 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - }, --- linux-2.6.9-rc1/drivers/usb/media/pwc-misc.c 2004-08-24 00:02:32.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,146 +0,0 @@ -/* Linux driver for Philips webcam - Various miscellaneous functions and tables. - (C) 1999-2003 Nemosoft Unv. (webcam@smcc.demon.nl) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include - -#include "pwc.h" - -struct pwc_coord pwc_image_sizes[PSZ_MAX] = -{ - { 128, 96, 0 }, - { 160, 120, 0 }, - { 176, 144, 0 }, - { 320, 240, 0 }, - { 352, 288, 0 }, - { 640, 480, 0 }, -}; - -/* x,y -> PSZ_ */ -int pwc_decode_size(struct pwc_device *pdev, int width, int height) -{ - int i, find; - - /* Make sure we don't go beyond our max size. - NB: we have different limits for RAW and normal modes. In case - you don't have the decompressor loaded or use RAW mode, - the maximum viewable size is smaller. - */ - if (pdev->vpalette == VIDEO_PALETTE_RAW) - { - if (width > pdev->abs_max.x || height > pdev->abs_max.y) - { - Debug("VIDEO_PALETTE_RAW: going beyond abs_max.\n"); - return -1; - } - } - else - { - if (width > pdev->view_max.x || height > pdev->view_max.y) - { - Debug("VIDEO_PALETTE_ not RAW: going beyond view_max.\n"); - return -1; - } - } - - /* Find the largest size supported by the camera that fits into the - requested size. - */ - find = -1; - for (i = 0; i < PSZ_MAX; i++) { - if (pdev->image_mask & (1 << i)) { - if (pwc_image_sizes[i].x <= width && pwc_image_sizes[i].y <= height) - find = i; - } - } - return find; -} - -/* initialize variables depending on type and decompressor*/ -void pwc_construct(struct pwc_device *pdev) -{ - switch(pdev->type) { - case 645: - case 646: - pdev->view_min.x = 128; - pdev->view_min.y = 96; - pdev->view_max.x = 352; - pdev->view_max.y = 288; - pdev->abs_max.x = 352; - pdev->abs_max.y = 288; - pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QCIF | 1 << PSZ_CIF; - pdev->vcinterface = 2; - pdev->vendpoint = 4; - pdev->frame_header_size = 0; - pdev->frame_trailer_size = 0; - break; - case 675: - case 680: - case 690: - pdev->view_min.x = 128; - pdev->view_min.y = 96; - /* Anthill bug #38: PWC always reports max size, even without PWCX */ - if (pdev->decompressor != NULL) { - pdev->view_max.x = 640; - pdev->view_max.y = 480; - } - else { - pdev->view_max.x = 352; - pdev->view_max.y = 288; - } - pdev->image_mask = 1 << PSZ_SQCIF | 1 << PSZ_QSIF | 1 << PSZ_QCIF | 1 << PSZ_SIF | 1 << PSZ_CIF | 1 << PSZ_VGA; - pdev->abs_max.x = 640; - pdev->abs_max.y = 480; - pdev->vcinterface = 3; - pdev->vendpoint = 4; - pdev->frame_header_size = 0; - pdev->frame_trailer_size = 0; - break; - case 720: - case 730: - case 740: - case 750: - pdev->view_min.x = 160; - pdev->view_min.y = 120; - /* Anthill bug #38: PWC always reports max size, even without PWCX */ - if (pdev->decompressor != NULL) { - pdev->view_max.x = 640; - pdev->view_max.y = 480; - } - else { - /* We use CIF, not SIF since some tools really need CIF. So we cheat a bit. */ - pdev->view_max.x = 352; - pdev->view_max.y = 288; - } - pdev->image_mask = 1 << PSZ_QSIF | 1 << PSZ_SIF | 1 << PSZ_VGA; - pdev->abs_max.x = 640; - pdev->abs_max.y = 480; - pdev->vcinterface = 3; - pdev->vendpoint = 5; - pdev->frame_header_size = TOUCAM_HEADER_SIZE; - pdev->frame_trailer_size = TOUCAM_TRAILER_SIZE; - break; - } - pdev->vpalette = VIDEO_PALETTE_YUV420P; /* default */ - pdev->view_min.size = pdev->view_min.x * pdev->view_min.y; - pdev->view_max.size = pdev->view_max.x * pdev->view_max.y; - /* length of image, in YUV format; always allocate enough memory. */ - pdev->len_per_image = (pdev->abs_max.x * pdev->abs_max.y * 3) / 2; -} - - --- linux-2.6.9-rc1/drivers/usb/media/pwc_nala.h 2004-08-24 00:02:23.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,66 +0,0 @@ - /* SQCIF */ - { - {0, 0, {0x04, 0x01, 0x03}}, - {8, 0, {0x05, 0x01, 0x03}}, - {7, 0, {0x08, 0x01, 0x03}}, - {7, 0, {0x0A, 0x01, 0x03}}, - {6, 0, {0x0C, 0x01, 0x03}}, - {5, 0, {0x0F, 0x01, 0x03}}, - {4, 0, {0x14, 0x01, 0x03}}, - {3, 0, {0x18, 0x01, 0x03}}, - }, - /* QSIF */ - { - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - }, - /* QCIF */ - { - {0, 0, {0x04, 0x01, 0x02}}, - {8, 0, {0x05, 0x01, 0x02}}, - {7, 0, {0x08, 0x01, 0x02}}, - {6, 0, {0x0A, 0x01, 0x02}}, - {5, 0, {0x0C, 0x01, 0x02}}, - {4, 0, {0x0F, 0x01, 0x02}}, - {1, 0, {0x14, 0x01, 0x02}}, - {1, 0, {0x18, 0x01, 0x02}}, - }, - /* SIF */ - { - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - }, - /* CIF */ - { - {4, 0, {0x04, 0x01, 0x01}}, - {7, 1, {0x05, 0x03, 0x01}}, - {6, 1, {0x08, 0x03, 0x01}}, - {4, 1, {0x0A, 0x03, 0x01}}, - {3, 1, {0x0C, 0x03, 0x01}}, - {2, 1, {0x0F, 0x03, 0x01}}, - {0}, - {0}, - }, - /* VGA */ - { - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - }, --- linux-2.6.9-rc1/drivers/usb/media/pwc_timon.h 2004-08-24 00:03:30.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,270 +0,0 @@ - /* SQCIF */ - { - /* 5 fps */ - { - {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, - {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, - {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, - {1, 140, 0, {0x05, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x8C, 0xFC, 0x80, 0x02}}, - }, - /* 10 fps */ - { - {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, - {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, - {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, - {2, 280, 0, {0x04, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x18, 0xA9, 0x80, 0x02}}, - }, - /* 15 fps */ - { - {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, - {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, - {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, - {3, 410, 0, {0x03, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x9A, 0x71, 0x80, 0x02}}, - }, - /* 20 fps */ - { - {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, - {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, - {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, - {4, 559, 0, {0x02, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x2F, 0x56, 0x80, 0x02}}, - }, - /* 25 fps */ - { - {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, - {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, - {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, - {5, 659, 0, {0x01, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x93, 0x46, 0x80, 0x02}}, - }, - /* 30 fps */ - { - {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, - {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, - {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, - {7, 838, 0, {0x00, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x46, 0x3B, 0x80, 0x02}}, - }, - }, - /* QSIF */ - { - /* 5 fps */ - { - {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, - {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, - {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, - {1, 146, 0, {0x2D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x92, 0xFC, 0xC0, 0x02}}, - }, - /* 10 fps */ - { - {2, 291, 0, {0x2C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x23, 0xA1, 0xC0, 0x02}}, - {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, - {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, - {1, 191, 630, {0x2C, 0xF4, 0x05, 0x13, 0xA9, 0x12, 0xE1, 0x17, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, - }, - /* 15 fps */ - { - {3, 437, 0, {0x2B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xB5, 0x6D, 0xC0, 0x02}}, - {2, 291, 640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, - {2, 291, 640, {0x2B, 0xF4, 0x05, 0x13, 0xF7, 0x13, 0x2F, 0x13, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, - {1, 191, 420, {0x2B, 0xF4, 0x0D, 0x0D, 0x1B, 0x0C, 0x53, 0x1E, 0x08, 0xBF, 0xF4, 0xC0, 0x02}}, - }, - /* 20 fps */ - { - {4, 588, 0, {0x2A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x4C, 0x52, 0xC0, 0x02}}, - {3, 447, 730, {0x2A, 0xF4, 0x05, 0x16, 0xC9, 0x16, 0x01, 0x0E, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, - {2, 292, 476, {0x2A, 0xF4, 0x0D, 0x0E, 0xD8, 0x0E, 0x10, 0x19, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, - {1, 192, 312, {0x2A, 0xF4, 0x1D, 0x09, 0xB3, 0x08, 0xEB, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}}, - }, - /* 25 fps */ - { - {5, 703, 0, {0x29, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0xBF, 0x42, 0xC0, 0x02}}, - {3, 447, 610, {0x29, 0xF4, 0x05, 0x13, 0x0B, 0x12, 0x43, 0x14, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, - {2, 292, 398, {0x29, 0xF4, 0x0D, 0x0C, 0x6C, 0x0B, 0xA4, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, - {1, 192, 262, {0x29, 0xF4, 0x25, 0x08, 0x23, 0x07, 0x5B, 0x1E, 0x18, 0xC0, 0xF4, 0xC0, 0x02}}, - }, - /* 30 fps */ - { - {8, 873, 0, {0x28, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x69, 0x37, 0xC0, 0x02}}, - {5, 704, 774, {0x28, 0xF4, 0x05, 0x18, 0x21, 0x17, 0x59, 0x0F, 0x18, 0xC0, 0x42, 0xC0, 0x02}}, - {3, 448, 492, {0x28, 0xF4, 0x05, 0x0F, 0x5D, 0x0E, 0x95, 0x15, 0x18, 0xC0, 0x69, 0xC0, 0x02}}, - {2, 291, 320, {0x28, 0xF4, 0x1D, 0x09, 0xFB, 0x09, 0x33, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}}, - }, - }, - /* QCIF */ - { - /* 5 fps */ - { - {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, - {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, - {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, - {1, 193, 0, {0x0D, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xC1, 0xF4, 0xC0, 0x02}}, - }, - /* 10 fps */ - { - {3, 385, 0, {0x0C, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x81, 0x79, 0xC0, 0x02}}, - {2, 291, 800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, - {2, 291, 800, {0x0C, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x11, 0x08, 0x23, 0xA1, 0xC0, 0x02}}, - {1, 194, 532, {0x0C, 0xF4, 0x05, 0x10, 0x9A, 0x0F, 0xBE, 0x1B, 0x08, 0xC2, 0xF0, 0xC0, 0x02}}, - }, - /* 15 fps */ - { - {4, 577, 0, {0x0B, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x41, 0x52, 0xC0, 0x02}}, - {3, 447, 818, {0x0B, 0xF4, 0x05, 0x19, 0x89, 0x18, 0xAD, 0x0F, 0x10, 0xBF, 0x69, 0xC0, 0x02}}, - {2, 292, 534, {0x0B, 0xF4, 0x05, 0x10, 0xA3, 0x0F, 0xC7, 0x19, 0x10, 0x24, 0xA1, 0xC0, 0x02}}, - {1, 195, 356, {0x0B, 0xF4, 0x15, 0x0B, 0x11, 0x0A, 0x35, 0x1E, 0x10, 0xC3, 0xF0, 0xC0, 0x02}}, - }, - /* 20 fps */ - { - {6, 776, 0, {0x0A, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x08, 0x3F, 0xC0, 0x02}}, - {4, 591, 804, {0x0A, 0xF4, 0x05, 0x19, 0x1E, 0x18, 0x42, 0x0F, 0x18, 0x4F, 0x4E, 0xC0, 0x02}}, - {3, 447, 608, {0x0A, 0xF4, 0x05, 0x12, 0xFD, 0x12, 0x21, 0x15, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, - {2, 291, 396, {0x0A, 0xF4, 0x15, 0x0C, 0x5E, 0x0B, 0x82, 0x1E, 0x18, 0x23, 0xA1, 0xC0, 0x02}}, - }, - /* 25 fps */ - { - {9, 928, 0, {0x09, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0xA0, 0x33, 0xC0, 0x02}}, - {5, 703, 800, {0x09, 0xF4, 0x05, 0x18, 0xF4, 0x18, 0x18, 0x10, 0x18, 0xBF, 0x42, 0xC0, 0x02}}, - {3, 447, 508, {0x09, 0xF4, 0x0D, 0x0F, 0xD2, 0x0E, 0xF6, 0x1B, 0x18, 0xBF, 0x69, 0xC0, 0x02}}, - {2, 292, 332, {0x09, 0xF4, 0x1D, 0x0A, 0x5A, 0x09, 0x7E, 0x1E, 0x18, 0x24, 0xA1, 0xC0, 0x02}}, - }, - /* 30 fps */ - { - {0, }, - {9, 956, 876, {0x08, 0xF4, 0x05, 0x1B, 0x58, 0x1A, 0x7C, 0x0E, 0x20, 0xBC, 0x33, 0x10, 0x02}}, - {4, 592, 542, {0x08, 0xF4, 0x05, 0x10, 0xE4, 0x10, 0x08, 0x17, 0x20, 0x50, 0x4E, 0x10, 0x02}}, - {2, 291, 266, {0x08, 0xF4, 0x25, 0x08, 0x48, 0x07, 0x6C, 0x1E, 0x20, 0x23, 0xA1, 0x10, 0x02}}, - }, - }, - /* SIF */ - { - /* 5 fps */ - { - {4, 582, 0, {0x35, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x46, 0x52, 0x60, 0x02}}, - {3, 387, 1276, {0x35, 0xF4, 0x05, 0x27, 0xD8, 0x26, 0x48, 0x03, 0x10, 0x83, 0x79, 0x60, 0x02}}, - {2, 291, 960, {0x35, 0xF4, 0x0D, 0x1D, 0xF2, 0x1C, 0x62, 0x04, 0x10, 0x23, 0xA1, 0x60, 0x02}}, - {1, 191, 630, {0x35, 0xF4, 0x1D, 0x13, 0xA9, 0x12, 0x19, 0x05, 0x08, 0xBF, 0xF4, 0x60, 0x02}}, - }, - /* 10 fps */ - { - {0, }, - {6, 775, 1278, {0x34, 0xF4, 0x05, 0x27, 0xE8, 0x26, 0x58, 0x05, 0x30, 0x07, 0x3F, 0x10, 0x02}}, - {3, 447, 736, {0x34, 0xF4, 0x15, 0x16, 0xFB, 0x15, 0x6B, 0x05, 0x18, 0xBF, 0x69, 0x10, 0x02}}, - {2, 291, 480, {0x34, 0xF4, 0x2D, 0x0E, 0xF9, 0x0D, 0x69, 0x09, 0x18, 0x23, 0xA1, 0x10, 0x02}}, - }, - /* 15 fps */ - { - {0, }, - {9, 955, 1050, {0x33, 0xF4, 0x05, 0x20, 0xCF, 0x1F, 0x3F, 0x06, 0x48, 0xBB, 0x33, 0x10, 0x02}}, - {4, 591, 650, {0x33, 0xF4, 0x15, 0x14, 0x44, 0x12, 0xB4, 0x08, 0x30, 0x4F, 0x4E, 0x10, 0x02}}, - {3, 448, 492, {0x33, 0xF4, 0x25, 0x0F, 0x52, 0x0D, 0xC2, 0x09, 0x28, 0xC0, 0x69, 0x10, 0x02}}, - }, - /* 20 fps */ - { - {0, }, - {9, 958, 782, {0x32, 0xF4, 0x0D, 0x18, 0x6A, 0x16, 0xDA, 0x0B, 0x58, 0xBE, 0x33, 0xD0, 0x02}}, - {5, 703, 574, {0x32, 0xF4, 0x1D, 0x11, 0xE7, 0x10, 0x57, 0x0B, 0x40, 0xBF, 0x42, 0xD0, 0x02}}, - {3, 446, 364, {0x32, 0xF4, 0x3D, 0x0B, 0x5C, 0x09, 0xCC, 0x0E, 0x30, 0xBE, 0x69, 0xD0, 0x02}}, - }, - /* 25 fps */ - { - {0, }, - {9, 958, 654, {0x31, 0xF4, 0x15, 0x14, 0x66, 0x12, 0xD6, 0x0B, 0x50, 0xBE, 0x33, 0x90, 0x02}}, - {6, 776, 530, {0x31, 0xF4, 0x25, 0x10, 0x8C, 0x0E, 0xFC, 0x0C, 0x48, 0x08, 0x3F, 0x90, 0x02}}, - {4, 592, 404, {0x31, 0xF4, 0x35, 0x0C, 0x96, 0x0B, 0x06, 0x0B, 0x38, 0x50, 0x4E, 0x90, 0x02}}, - }, - /* 30 fps */ - { - {0, }, - {9, 957, 526, {0x30, 0xF4, 0x25, 0x10, 0x68, 0x0E, 0xD8, 0x0D, 0x58, 0xBD, 0x33, 0x60, 0x02}}, - {6, 775, 426, {0x30, 0xF4, 0x35, 0x0D, 0x48, 0x0B, 0xB8, 0x0F, 0x50, 0x07, 0x3F, 0x60, 0x02}}, - {4, 590, 324, {0x30, 0x7A, 0x4B, 0x0A, 0x1C, 0x08, 0xB4, 0x0E, 0x40, 0x4E, 0x52, 0x60, 0x02}}, - }, - }, - /* CIF */ - { - /* 5 fps */ - { - {6, 771, 0, {0x15, 0xF4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x3F, 0x80, 0x02}}, - {4, 465, 1278, {0x15, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x03, 0x18, 0xD1, 0x65, 0x80, 0x02}}, - {2, 291, 800, {0x15, 0xF4, 0x15, 0x18, 0xF4, 0x17, 0x3C, 0x05, 0x18, 0x23, 0xA1, 0x80, 0x02}}, - {1, 193, 528, {0x15, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x18, 0xC1, 0xF4, 0x80, 0x02}}, - }, - /* 10 fps */ - { - {0, }, - {9, 932, 1278, {0x14, 0xF4, 0x05, 0x27, 0xEE, 0x26, 0x36, 0x04, 0x30, 0xA4, 0x33, 0x10, 0x02}}, - {4, 591, 812, {0x14, 0xF4, 0x15, 0x19, 0x56, 0x17, 0x9E, 0x06, 0x28, 0x4F, 0x4E, 0x10, 0x02}}, - {2, 291, 400, {0x14, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x28, 0x23, 0xA1, 0x10, 0x02}}, - }, - /* 15 fps */ - { - {0, }, - {9, 956, 876, {0x13, 0xF4, 0x0D, 0x1B, 0x58, 0x19, 0xA0, 0x05, 0x38, 0xBC, 0x33, 0x60, 0x02}}, - {5, 703, 644, {0x13, 0xF4, 0x1D, 0x14, 0x1C, 0x12, 0x64, 0x08, 0x38, 0xBF, 0x42, 0x60, 0x02}}, - {3, 448, 410, {0x13, 0xF4, 0x3D, 0x0C, 0xC4, 0x0B, 0x0C, 0x0E, 0x38, 0xC0, 0x69, 0x60, 0x02}}, - }, - /* 20 fps */ - { - {0, }, - {9, 956, 650, {0x12, 0xF4, 0x1D, 0x14, 0x4A, 0x12, 0x92, 0x09, 0x48, 0xBC, 0x33, 0x10, 0x03}}, - {6, 776, 528, {0x12, 0xF4, 0x2D, 0x10, 0x7E, 0x0E, 0xC6, 0x0A, 0x40, 0x08, 0x3F, 0x10, 0x03}}, - {4, 591, 402, {0x12, 0xF4, 0x3D, 0x0C, 0x8F, 0x0A, 0xD7, 0x0E, 0x40, 0x4F, 0x4E, 0x10, 0x03}}, - }, - /* 25 fps */ - { - {0, }, - {9, 956, 544, {0x11, 0xF4, 0x25, 0x10, 0xF4, 0x0F, 0x3C, 0x0A, 0x48, 0xBC, 0x33, 0xC0, 0x02}}, - {7, 840, 478, {0x11, 0xF4, 0x2D, 0x0E, 0xEB, 0x0D, 0x33, 0x0B, 0x48, 0x48, 0x3B, 0xC0, 0x02}}, - {5, 703, 400, {0x11, 0xF4, 0x3D, 0x0C, 0x7A, 0x0A, 0xC2, 0x0E, 0x48, 0xBF, 0x42, 0xC0, 0x02}}, - }, - /* 30 fps */ - { - {0, }, - {9, 956, 438, {0x10, 0xF4, 0x35, 0x0D, 0xAC, 0x0B, 0xF4, 0x0D, 0x50, 0xBC, 0x33, 0x10, 0x02}}, - {7, 838, 384, {0x10, 0xF4, 0x45, 0x0B, 0xFD, 0x0A, 0x45, 0x0F, 0x50, 0x46, 0x3B, 0x10, 0x02}}, - {6, 773, 354, {0x10, 0x7A, 0x4B, 0x0B, 0x0C, 0x09, 0x80, 0x10, 0x50, 0x05, 0x3F, 0x10, 0x02}}, - }, - }, - /* VGA */ - { - /* 5 fps */ - { - {0, }, - {6, 773, 1272, {0x1D, 0xF4, 0x15, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x3F, 0x10, 0x02}}, - {4, 592, 976, {0x1D, 0xF4, 0x25, 0x1E, 0x78, 0x1B, 0x58, 0x03, 0x30, 0x50, 0x4E, 0x10, 0x02}}, - {3, 448, 738, {0x1D, 0xF4, 0x3D, 0x17, 0x0C, 0x13, 0xEC, 0x04, 0x30, 0xC0, 0x69, 0x10, 0x02}}, - }, - /* 10 fps */ - { - {0, }, - {9, 956, 788, {0x1C, 0xF4, 0x35, 0x18, 0x9C, 0x15, 0x7C, 0x03, 0x48, 0xBC, 0x33, 0x10, 0x02}}, - {6, 776, 640, {0x1C, 0x7A, 0x53, 0x13, 0xFC, 0x11, 0x2C, 0x04, 0x48, 0x08, 0x3F, 0x10, 0x02}}, - {4, 592, 488, {0x1C, 0x7A, 0x6B, 0x0F, 0x3C, 0x0C, 0x6C, 0x06, 0x48, 0x50, 0x4E, 0x10, 0x02}}, - }, - /* 15 fps */ - { - {0, }, - {9, 957, 526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}}, - {9, 957, 526, {0x1B, 0x7A, 0x63, 0x10, 0x68, 0x0D, 0x98, 0x06, 0x58, 0xBD, 0x33, 0x80, 0x02}}, - {8, 895, 492, {0x1B, 0x7A, 0x6B, 0x0F, 0x5D, 0x0C, 0x8D, 0x06, 0x58, 0x7F, 0x37, 0x80, 0x02}}, - }, - /* 20 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 25 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - /* 30 fps */ - { - {0, }, - {0, }, - {0, }, - {0, }, - }, - }, --- linux-2.6.9-rc1/drivers/usb/media/pwc-uncompress.c 2004-08-24 00:02:48.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,180 +0,0 @@ -/* Linux driver for Philips webcam - Decompression frontend. - (C) 1999-2003 Nemosoft Unv. (webcam@smcc.demon.nl) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -/* - This is where the decompression routines register and unregister - themselves. It also has a decompressor wrapper function. -*/ - -#include -#include -// #include - -#include "pwc.h" -#include "pwc-uncompress.h" - - -/* This contains a list of all registered decompressors */ -static LIST_HEAD(pwc_decompressor_list); - -/* Should the pwc_decompress structure ever change, we increase the - version number so that we don't get nasty surprises, or can - dynamically adjust our structure. - */ -const int pwc_decompressor_version = PWC_MAJOR; - -/* Add decompressor to list, ignoring duplicates */ -void pwc_register_decompressor(struct pwc_decompressor *pwcd) -{ - if (pwc_find_decompressor(pwcd->type) == NULL) { - Trace(TRACE_PWCX, "Adding decompressor for model %d.\n", pwcd->type); - list_add_tail(&pwcd->pwcd_list, &pwc_decompressor_list); - } -} - -/* Remove decompressor from list */ -void pwc_unregister_decompressor(int type) -{ - struct pwc_decompressor *find; - - find = pwc_find_decompressor(type); - if (find != NULL) { - Trace(TRACE_PWCX, "Removing decompressor for model %d.\n", type); - list_del(&find->pwcd_list); - } -} - -/* Find decompressor in list */ -struct pwc_decompressor *pwc_find_decompressor(int type) -{ - struct list_head *tmp; - struct pwc_decompressor *pwcd; - - list_for_each(tmp, &pwc_decompressor_list) { - pwcd = list_entry(tmp, struct pwc_decompressor, pwcd_list); - if (pwcd->type == type) - return pwcd; - } - return NULL; -} - - - -int pwc_decompress(struct pwc_device *pdev) -{ - struct pwc_frame_buf *fbuf; - int n, line, col, stride; - void *yuv, *image; - u16 *src; - u16 *dsty, *dstu, *dstv; - - if (pdev == NULL) - return -EFAULT; -#if defined(__KERNEL__) && defined(PWC_MAGIC) - if (pdev->magic != PWC_MAGIC) { - Err("pwc_decompress(): magic failed.\n"); - return -EFAULT; - } -#endif - - fbuf = pdev->read_frame; - if (fbuf == NULL) - return -EFAULT; - image = pdev->image_ptr[pdev->fill_image]; - if (!image) - return -EFAULT; - - yuv = fbuf->data + pdev->frame_header_size; /* Skip header */ - - /* Raw format; that's easy... */ - if (pdev->vpalette == VIDEO_PALETTE_RAW) - { - memcpy(image, yuv, pdev->frame_size); - return 0; - } - - if (pdev->vbandlength == 0) { - /* Uncompressed mode. We copy the data into the output buffer, - using the viewport size (which may be larger than the image - size). Unfortunately we have to do a bit of byte stuffing - to get the desired output format/size. - */ - /* - * We do some byte shuffling here to go from the - * native format to YUV420P. - */ - src = (u16 *)yuv; - n = pdev->view.x * pdev->view.y; - - /* offset in Y plane */ - stride = pdev->view.x * pdev->offset.y + pdev->offset.x; - dsty = (u16 *)(image + stride); - - /* offsets in U/V planes */ - stride = pdev->view.x * pdev->offset.y / 4 + pdev->offset.x / 2; - dstu = (u16 *)(image + n + stride); - dstv = (u16 *)(image + n + n / 4 + stride); - - /* increment after each line */ - stride = (pdev->view.x - pdev->image.x) / 2; /* u16 is 2 bytes */ - - for (line = 0; line < pdev->image.y; line++) { - for (col = 0; col < pdev->image.x; col += 4) { - *dsty++ = *src++; - *dsty++ = *src++; - if (line & 1) - *dstv++ = *src++; - else - *dstu++ = *src++; - } - dsty += stride; - if (line & 1) - dstv += (stride >> 1); - else - dstu += (stride >> 1); - } - } - else { - /* Compressed; the decompressor routines will write the data - in planar format immediately. - */ - int flags; - - flags = PWCX_FLAG_PLANAR; - if (pdev->vsize == PSZ_VGA && pdev->vframes == 5 && pdev->vsnapshot) - flags |= PWCX_FLAG_BAYER; - - if (pdev->decompressor) - pdev->decompressor->decompress( - &pdev->image, &pdev->view, &pdev->offset, - yuv, image, - flags, - pdev->decompress_data, pdev->vbandlength); - else - return -ENXIO; /* No such device or address: missing decompressor */ - } - return 0; -} - -/* Make sure these functions are available for the decompressor plugin - both when this code is compiled into the kernel or as as module. - */ - -EXPORT_SYMBOL_NOVERS(pwc_decompressor_version); -EXPORT_SYMBOL(pwc_register_decompressor); -EXPORT_SYMBOL(pwc_unregister_decompressor); --- linux-2.6.9-rc1/drivers/usb/media/pwc-uncompress.h 2004-08-24 00:02:25.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,84 +0,0 @@ -/* (C) 1999-2003 Nemosoft Unv. (webcam@smcc.demon.nl) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -/* This file is the bridge between the kernel module and the plugin; it - describes the structures and datatypes used in both modules. Any - significant change should be reflected by increasing the - pwc_decompressor_version major number. - */ -#ifndef PWC_UNCOMPRESS_H -#define PWC_UNCOMPRESS_H - -#include -#include -#include - -#include "pwc-ioctl.h" - -/* from pwc-dec.h */ -#define PWCX_FLAG_PLANAR 0x0001 -/* */ - - -#ifdef __cplusplus -extern "C" { -#endif - -/* The decompressor structure. - Every type of decompressor registers itself with the main module. - When a device is opened, it looks up the correct compressor, and - uses that when a compressed video mode is requested. - */ -struct pwc_decompressor -{ - int type; /* type of camera (645, 680, etc) */ - int table_size; /* memory needed */ - - void (* init)(int type, int release, void *buffer, void *table); /* Initialization routine; should be called after each set_video_mode */ - void (* exit)(void); /* Cleanup routine */ - void (* decompress)(struct pwc_coord *image, struct pwc_coord *view, - struct pwc_coord *offset, - void *src, void *dst, int flags, - void *table, int bandlength); - void (* lock)(void); /* make sure module cannot be unloaded */ - void (* unlock)(void); /* release lock on module */ - - struct list_head pwcd_list; -}; - - -/* Our structure version number. Is set to the version number major */ -extern const int pwc_decompressor_version; - -/* Adds decompressor to list, based on its 'type' field (which matches the 'type' field in pwc_device; ignores any double requests */ -extern void pwc_register_decompressor(struct pwc_decompressor *pwcd); -/* Removes decompressor, based on the type number */ -extern void pwc_unregister_decompressor(int type); -/* Returns pointer to decompressor struct, or NULL if it doesn't exist */ -extern struct pwc_decompressor *pwc_find_decompressor(int type); - -#ifdef CONFIG_USB_PWCX -/* If the decompressor is compiled in, we must call these manually */ -extern int usb_pwcx_init(void); -extern void usb_pwcx_exit(void); -#endif - -#ifdef __cplusplus -} -#endif - -#endif --- linux-2.6.9-rc1/drivers/usb/media/sn9c102_core.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/drivers/usb/media/sn9c102_core.c 2004-09-02 20:51:52.000000000 -0700 @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -1731,13 +1732,15 @@ static int sn9c102_v4l2_ioctl(struct ino rect->width &= ~15L; rect->height &= ~15L; - { /* calculate the actual scaling factor */ + if (SN9C102_PRESERVE_IMGSCALE) { + /* Calculate the actual scaling factor */ u32 a, b; a = rect->width * rect->height; b = pix_format->width * pix_format->height; scale = b ? (u8)((a / b) < 4 ? 1 : ((a / b) < 16 ? 2 : 4)) : 1; - } + } else + scale = 1; if (cam->stream == STREAM_ON) { cam->stream = STREAM_INTERRUPT; @@ -2111,7 +2114,7 @@ static int sn9c102_v4l2_ioctl(struct ino spin_lock_irqsave(&cam->queue_lock, lock_flags); f = list_entry(cam->outqueue.next, struct sn9c102_frame_t, frame); - list_del(&cam->outqueue); + list_del(cam->outqueue.next); spin_unlock_irqrestore(&cam->queue_lock, lock_flags); f->state = F_UNUSED; --- linux-2.6.9-rc1/drivers/usb/media/sn9c102.h 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/usb/media/sn9c102.h 2004-09-02 20:51:52.000000000 -0700 @@ -40,6 +40,7 @@ #define SN9C102_DEBUG #define SN9C102_DEBUG_LEVEL 2 #define SN9C102_MAX_DEVICES 64 +#define SN9C102_PRESERVE_IMGSCALE 0 #define SN9C102_MAX_FRAMES 32 #define SN9C102_URBS 2 #define SN9C102_ISO_PACKETS 7 @@ -52,8 +53,8 @@ #define SN9C102_MODULE_AUTHOR "(C) 2004 Luca Risolia" #define SN9C102_AUTHOR_EMAIL "" #define SN9C102_MODULE_LICENSE "GPL" -#define SN9C102_MODULE_VERSION "1:1.07" -#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 7) +#define SN9C102_MODULE_VERSION "1:1.08" +#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 8) SN9C102_ID_TABLE; SN9C102_SENSOR_TABLE; --- linux-2.6.9-rc1/drivers/usb/media/sn9c102_pas106b.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/usb/media/sn9c102_pas106b.c 2004-09-02 20:51:52.000000000 -0700 @@ -109,7 +109,7 @@ static int pas106b_set_ctrl(struct sn9c1 err += sn9c102_i2c_write(cam, 0x0e, ctrl->value & 0x1f); break; case V4L2_CID_BRIGHTNESS: - err += sn9c102_i2c_write(cam, 0x0d, ctrl->value & 0x1f); + err += sn9c102_i2c_write(cam, 0x0d, 0x1f-(ctrl->value & 0x1f)); break; case V4L2_CID_CONTRAST: err += sn9c102_i2c_write(cam, 0x0f, ctrl->value & 0x03); @@ -180,11 +180,11 @@ static struct sn9c102_sensor pas106b = { { .id = V4L2_CID_BRIGHTNESS, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "darkness", + .name = "brightness", .minimum = 0x00, .maximum = 0x1f, .step = 0x01, - .default_value = 0x00, + .default_value = 0x1f, .flags = 0, }, { --- linux-2.6.9-rc1/drivers/usb/media/sn9c102_pas202bcb.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/usb/media/sn9c102_pas202bcb.c 2004-09-02 20:51:52.000000000 -0700 @@ -104,7 +104,7 @@ static int pas202bcb_set_ctrl(struct sn9 err += sn9c102_i2c_write(cam, 0x10, ctrl->value & 0x1f); break; case V4L2_CID_BRIGHTNESS: - err += sn9c102_i2c_write(cam, 0x06, ctrl->value & 0x0f); + err += sn9c102_i2c_write(cam, 0x06, 0x0f-(ctrl->value & 0x0f)); break; default: return -EINVAL; @@ -173,11 +173,11 @@ static struct sn9c102_sensor pas202bcb = { .id = V4L2_CID_BRIGHTNESS, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "darkness", + .name = "brightness", .minimum = 0x00, .maximum = 0x0f, .step = 0x01, - .default_value = 0x00, + .default_value = 0x0f, .flags = 0, }, }, --- linux-2.6.9-rc1/drivers/usb/media/sn9c102_tas5110c1b.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/drivers/usb/media/sn9c102_tas5110c1b.c 2004-09-02 20:51:52.000000000 -0700 @@ -27,7 +27,6 @@ static struct sn9c102_sensor tas5110c1b; static int tas5110c1b_init(struct sn9c102_device* cam) { - const u8 DARKNESS = 0xb7; int err = 0; err += sn9c102_write_reg(cam, 0x01, 0x01); @@ -41,13 +40,26 @@ static int tas5110c1b_init(struct sn9c10 err += sn9c102_i2c_try_raw_write(cam, &tas5110c1b, 4, 0x11, 0x00, 0xc0, 0x80, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &tas5110c1b, 4, 0x11, 0x02, 0x20, - DARKNESS, 0, 0); return err; } +static int tas5110c1b_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_GAIN: + return sn9c102_i2c_try_raw_write(cam, &tas5110c1b, 4, 0x11, + 0x02, 0x20, + 0xff - (ctrl->value & 0xff), + 0, 0); + default: + return -EINVAL; + } +} + + static int tas5110c1b_set_crop(struct sn9c102_device* cam, const struct v4l2_rect* rect) { @@ -74,6 +86,19 @@ static struct sn9c102_sensor tas5110c1b .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_3WIRES, .init = &tas5110c1b_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x48, + .flags = 0, + }, + }, + .set_ctrl = &tas5110c1b_set_ctrl, .cropcap = { .bounds = { .left = 0, --- linux-2.6.9-rc1/drivers/usb/media/sn9c102_tas5130d1b.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/drivers/usb/media/sn9c102_tas5130d1b.c 2004-09-02 20:51:52.000000000 -0700 @@ -27,7 +27,6 @@ static struct sn9c102_sensor tas5130d1b; static int tas5130d1b_init(struct sn9c102_device* cam) { - const u8 DARKNESS = 0xff, CONTRAST = 0xb0, GAIN = 0x08; int err = 0; err += sn9c102_write_reg(cam, 0x01, 0x01); @@ -39,19 +38,28 @@ static int tas5130d1b_init(struct sn9c10 err += sn9c102_write_reg(cam, 0x60, 0x17); err += sn9c102_write_reg(cam, 0x07, 0x18); - err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0x80, - 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0xc0, - GAIN, 0, 0); err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x00, 0x40, - CONTRAST, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, 0x02, 0x20, - DARKNESS, 0, 0); + 0x47, 0, 0); return err; } +static int tas5130d1b_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_GAIN: + return sn9c102_i2c_try_raw_write(cam, &tas5130d1b, 4, 0x11, + 0x02, 0x20, + 0xff - (ctrl->value & 0xff), + 0, 0); + default: + return -EINVAL; + } +} + + static int tas5130d1b_set_crop(struct sn9c102_device* cam, const struct v4l2_rect* rect) { @@ -78,6 +86,19 @@ static struct sn9c102_sensor tas5130d1b .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_3WIRES, .init = &tas5130d1b_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + }, + .set_ctrl = &tas5130d1b_set_ctrl, .cropcap = { .bounds = { .left = 0, --- linux-2.6.9-rc1/drivers/usb/media/stv680.c 2004-08-24 00:03:17.000000000 -0700 +++ 25/drivers/usb/media/stv680.c 2004-09-02 20:51:52.000000000 -0700 @@ -704,7 +704,6 @@ static int stv680_start_stream (struct u usb_rcvbulkpipe (stv680->udev, stv680->bulk_in_endpointAddr), stv680->sbuf[i].data, stv680->rawbufsize, stv680_video_irq, stv680); - urb->timeout = PENCAM_TIMEOUT * 2; stv680->urb[i] = urb; err = usb_submit_urb (stv680->urb[i], GFP_KERNEL); if (err) --- linux-2.6.9-rc1/drivers/usb/misc/auerswald.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/drivers/usb/misc/auerswald.c 2004-09-02 20:51:52.000000000 -0700 @@ -1037,7 +1037,8 @@ static void auerswald_int_complete (stru /* now extract the information */ channelid = cp->intbufp[2]; - bytecount = le16_to_cpup (&cp->intbufp[3]); + bytecount = (unsigned char)cp->intbufp[3]; + bytecount |= (unsigned char)cp->intbufp[4] << 8; /* check the channel id */ if (channelid >= AUH_TYPESIZE) { @@ -1930,7 +1931,7 @@ static int auerswald_probe (struct usb_i struct usb_device *usbdev = interface_to_usbdev(intf); pauerswald_t cp = NULL; unsigned int u = 0; - char *pbuf; + u16 *pbuf; int ret; dbg ("probe: vendor id 0x%x, device id 0x%x", @@ -2002,7 +2003,7 @@ static int auerswald_probe (struct usb_i info("device is a %s", cp->dev_desc); /* get the maximum allowed control transfer length */ - pbuf = (char *) kmalloc (2, GFP_KERNEL); /* use an allocated buffer because of urb target */ + pbuf = (u16 *) kmalloc (2, GFP_KERNEL); /* use an allocated buffer because of urb target */ if (!pbuf) { err( "out of memory"); goto pfail; --- linux-2.6.9-rc1/drivers/usb/misc/legousbtower.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/drivers/usb/misc/legousbtower.c 2004-09-02 20:51:52.000000000 -0700 @@ -115,7 +115,7 @@ MODULE_PARM_DESC(debug, "Debug enabled o * In this case read_buffer_size should exceed the maximal packet length * (417 for datalog uploads), and packet_timeout should be set. */ -static size_t read_buffer_size = 480; +static int read_buffer_size = 480; module_param(read_buffer_size, int, 0); MODULE_PARM_DESC(read_buffer_size, "Read buffer size"); @@ -125,7 +125,7 @@ MODULE_PARM_DESC(read_buffer_size, "Read * A problem with long writes is that the following read may time out * if the software is not prepared to wait long enough. */ -static size_t write_buffer_size = 480; +static int write_buffer_size = 480; module_param(write_buffer_size, int, 0); MODULE_PARM_DESC(write_buffer_size, "Write buffer size"); @@ -714,7 +714,7 @@ static ssize_t tower_write (struct file } /* write the data into interrupt_out_buffer from userspace */ - bytes_to_write = min(count, write_buffer_size); + bytes_to_write = min_t(int, count, write_buffer_size); dbg(4, "%s: count = %Zd, bytes_to_write = %Zd", __FUNCTION__, count, bytes_to_write); if (copy_from_user (dev->interrupt_out_buffer, buffer, bytes_to_write)) { --- linux-2.6.9-rc1/drivers/usb/misc/phidgetservo.c 2004-08-24 00:03:14.000000000 -0700 +++ 25/drivers/usb/misc/phidgetservo.c 2004-09-02 20:51:52.000000000 -0700 @@ -12,8 +12,6 @@ * controllers available at: http://www.phidgets.com/ * * Note that the driver takes input as: degrees.minutes - * -23 < degrees < 203 - * 0 < minutes < 59 * * CAUTION: Generally you should use 0 < degrees < 180 as anything else * is probably beyond the range of your servo and may damage it. @@ -21,6 +19,10 @@ * Jun 16, 2004: Sean Young * - cleanups * - was using memory after kfree() + * Aug 8, 2004: Sean Young + * - set the highest angle as high as the hardware allows, there are + * some odd servos out there + * */ #include @@ -87,6 +89,9 @@ change_position_v30(struct phidget_servo int retval; unsigned char *buffer; + if (degrees < -23 || degrees > 362) + return -EINVAL; + buffer = kmalloc(6, GFP_KERNEL); if (!buffer) { dev_err(&servo->udev->dev, "%s - out of memory\n", @@ -157,6 +162,9 @@ change_position_v20(struct phidget_servo int retval; unsigned char *buffer; + if (degrees < -23 || degrees > 278) + return -EINVAL; + buffer = kmalloc(2, GFP_KERNEL); if (!buffer) { dev_err(&servo->udev->dev, "%s - out of memory\n", @@ -212,10 +220,8 @@ static ssize_t set_servo##value (struct return -EINVAL; \ } \ \ - if (degrees < -23 || degrees > (180 + 23) || \ - minutes < 0 || minutes > 59) { \ + if (minutes < 0 || minutes > 59) \ return -EINVAL; \ - } \ \ if (servo->type & SERVO_VERSION_30) \ retval = change_position_v30 (servo, value, degrees, \ --- linux-2.6.9-rc1/drivers/usb/misc/usbtest.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/drivers/usb/misc/usbtest.c 2004-09-02 20:51:52.000000000 -0700 @@ -1054,8 +1054,7 @@ static int unlink1 (struct usbtest_dev * urb = simple_alloc_urb (testdev_to_usbdev (dev), pipe, size); if (!urb) return -ENOMEM; - if (async) - urb->transfer_flags |= URB_ASYNC_UNLINK; + urb->transfer_flags |= URB_ASYNC_UNLINK; urb->context = &completion; urb->complete = unlink1_callback; @@ -1074,17 +1073,20 @@ static int unlink1 (struct usbtest_dev * * hcd states and code paths, even with little other system load. */ msleep (jiffies % (2 * INTERRUPT_RATE)); + if (async) { retry: - retval = usb_unlink_urb (urb); - if (retval == -EBUSY || retval == -EIDRM) { - /* we can't unlink urbs while they're completing. - * or if they've completed, and we haven't resubmitted. - * "normal" drivers would prevent resubmission, but - * since we're testing unlink paths, we can't. - */ - dev_dbg (&dev->intf->dev, "unlink retry\n"); - goto retry; - } + retval = usb_unlink_urb (urb); + if (retval == -EBUSY || retval == -EIDRM) { + /* we can't unlink urbs while they're completing. + * or if they've completed, and we haven't resubmitted. + * "normal" drivers would prevent resubmission, but + * since we're testing unlink paths, we can't. + */ + dev_dbg (&dev->intf->dev, "unlink retry\n"); + goto retry; + } + } else + usb_kill_urb (urb); if (!(retval == 0 || retval == -EINPROGRESS)) { dev_dbg (&dev->intf->dev, "unlink fail %d\n", retval); return retval; @@ -1095,9 +1097,10 @@ retry: simple_free_urb (urb); if (async) - return (retval != -ECONNRESET) ? -ECONNRESET : 0; + return (retval == -ECONNRESET) ? 0 : retval - 1000; else - return (retval != -ENOENT) ? -ENOENT : 0; + return (retval == -ENOENT || retval == -EPERM) ? + 0 : retval - 2000; } static int unlink_simple (struct usbtest_dev *dev, int pipe, int len) --- linux-2.6.9-rc1/drivers/usb/net/catc.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/drivers/usb/net/catc.c 2004-09-03 00:56:32.627200560 -0700 @@ -411,7 +411,7 @@ static void catc_tx_done(struct urb *urb static int catc_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev) { - struct catc *catc = netdev->priv; + struct catc *catc = netdev_priv(netdev); unsigned long flags; char *tx_buf; @@ -442,7 +442,7 @@ static int catc_hard_start_xmit(struct s static void catc_tx_timeout(struct net_device *netdev) { - struct catc *catc = netdev->priv; + struct catc *catc = netdev_priv(netdev); warn("Transmit timed out."); catc->tx_urb->transfer_flags |= URB_ASYNC_UNLINK; @@ -604,7 +604,7 @@ static void catc_stats_timer(unsigned lo static struct net_device_stats *catc_get_stats(struct net_device *netdev) { - struct catc *catc = netdev->priv; + struct catc *catc = netdev_priv(netdev); return &catc->stats; } @@ -622,7 +622,7 @@ static void catc_multicast(unsigned char static void catc_set_multicast_list(struct net_device *netdev) { - struct catc *catc = netdev->priv; + struct catc *catc = netdev_priv(netdev); struct dev_mc_list *mc; u8 broadcast[6]; u8 rx = RxEnable | RxPolarity | RxMultiCast; @@ -664,74 +664,38 @@ static void catc_set_multicast_list(stru } } -/* - * ioctl's - */ -static int netdev_ethtool_ioctl(struct net_device *dev, void __user *useraddr) +void catc_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - struct catc *catc = dev->priv; - u32 cmd; - - if (get_user(cmd, (u32 __user *)useraddr)) - return -EFAULT; - - switch (cmd) { - /* get driver info */ - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; - strncpy(info.driver, driver_name, ETHTOOL_BUSINFO_LEN); - strncpy(info.version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN); - usb_make_path (catc->usbdev, info.bus_info, sizeof info.bus_info); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - - /* get settings */ - case ETHTOOL_GSET: - if (catc->is_f5u011) { - struct ethtool_cmd ecmd = { ETHTOOL_GSET, - SUPPORTED_10baseT_Half | SUPPORTED_TP, - ADVERTISED_10baseT_Half | ADVERTISED_TP, - SPEED_10, - DUPLEX_HALF, - PORT_TP, - 0, - XCVR_INTERNAL, - AUTONEG_DISABLE, - 1, - 1 - }; - if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; - } else { - return -EOPNOTSUPP; - } + struct catc *catc = netdev_priv(dev); + strncpy(info->driver, driver_name, ETHTOOL_BUSINFO_LEN); + strncpy(info->version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN); + usb_make_path (catc->usbdev, info->bus_info, sizeof info->bus_info); +} + +static int catc_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct catc *catc = netdev_priv(dev); + if (!catc->is_f5u011) + return -EOPNOTSUPP; - /* get link status */ - case ETHTOOL_GLINK: { - struct ethtool_value edata = {ETHTOOL_GLINK}; - edata.data = netif_carrier_ok(dev); - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - } - - return -EOPNOTSUPP; -} - -static int catc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - switch(cmd) { - case SIOCETHTOOL: - return netdev_ethtool_ioctl(dev, rq->ifr_data); - default: - return -EOPNOTSUPP; - } + cmd->supported = SUPPORTED_10baseT_Half | SUPPORTED_TP; + cmd->advertising = ADVERTISED_10baseT_Half | ADVERTISED_TP; + cmd->speed = SPEED_10; + cmd->duplex = DUPLEX_HALF; + cmd->port = PORT_TP; + cmd->phy_address = 0; + cmd->transceiver = XCVR_INTERNAL; + cmd->autoneg = AUTONEG_DISABLE; + cmd->maxtxpkt = 1; + cmd->maxrxpkt = 1; + return 0; } +static struct ethtool_ops ops = { + .get_drvinfo = catc_get_drvinfo, + .get_settings = catc_get_settings, + .get_link = ethtool_op_get_link +}; /* * Open, close. @@ -739,7 +703,7 @@ static int catc_ioctl(struct net_device static int catc_open(struct net_device *netdev) { - struct catc *catc = netdev->priv; + struct catc *catc = netdev_priv(netdev); int status; catc->irq_urb->dev = catc->usbdev; @@ -758,7 +722,7 @@ static int catc_open(struct net_device * static int catc_stop(struct net_device *netdev) { - struct catc *catc = netdev->priv; + struct catc *catc = netdev_priv(netdev); netif_stop_queue(netdev); @@ -791,17 +755,11 @@ static int catc_probe(struct usb_interfa return -EIO; } - catc = kmalloc(sizeof(struct catc), GFP_KERNEL); - if (!catc) + netdev = alloc_etherdev(sizeof(struct catc)); + if (!netdev) return -ENOMEM; - memset(catc, 0, sizeof(struct catc)); - - netdev = alloc_etherdev(0); - if (!netdev) { - kfree(catc); - return -EIO; - } + catc = netdev_priv(netdev); netdev->open = catc_open; netdev->hard_start_xmit = catc_hard_start_xmit; @@ -810,8 +768,7 @@ static int catc_probe(struct usb_interfa netdev->tx_timeout = catc_tx_timeout; netdev->watchdog_timeo = TX_TIMEOUT; netdev->set_multicast_list = catc_set_multicast_list; - netdev->do_ioctl = catc_ioctl; - netdev->priv = catc; + SET_ETHTOOL_OPS(netdev, &ops); catc->usbdev = usbdev; catc->netdev = netdev; @@ -839,7 +796,6 @@ static int catc_probe(struct usb_interfa if (catc->irq_urb) usb_free_urb(catc->irq_urb); free_netdev(netdev); - kfree(catc); return -ENOMEM; } @@ -944,7 +900,6 @@ static int catc_probe(struct usb_interfa usb_free_urb(catc->rx_urb); usb_free_urb(catc->irq_urb); free_netdev(netdev); - kfree(catc); return -EIO; } return 0; @@ -962,7 +917,6 @@ static void catc_disconnect(struct usb_i usb_free_urb(catc->rx_urb); usb_free_urb(catc->irq_urb); free_netdev(catc->netdev); - kfree(catc); } } --- linux-2.6.9-rc1/drivers/usb/net/kaweth.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/drivers/usb/net/kaweth.c 2004-09-03 00:56:32.628200408 -0700 @@ -668,7 +668,7 @@ static int kaweth_open(struct net_device INTBUFFERSIZE, int_callback, kaweth, - HZ/4); + 8); kaweth->irq_urb->transfer_dma = kaweth->intbufferhandle; kaweth->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; @@ -710,37 +710,15 @@ static int kaweth_close(struct net_devic return 0; } -static int netdev_ethtool_ioctl(struct net_device *dev, void __user *useraddr) +static void kaweth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - u32 ethcmd; - if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) - return -EFAULT; - - switch (ethcmd) { - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; - strlcpy(info.driver, driver_name, sizeof(info.driver)); - if (copy_to_user(useraddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - } - - return -EOPNOTSUPP; + strlcpy(info->driver, driver_name, sizeof(info->driver)); } -/**************************************************************** - * kaweth_ioctl - ****************************************************************/ -static int kaweth_ioctl(struct net_device *net, struct ifreq *rq, int cmd) -{ - switch (cmd) { - case SIOCETHTOOL: - return netdev_ethtool_ioctl(net, rq->ifr_data); - } - return -EOPNOTSUPP; -} +static struct ethtool_ops ops = { + .get_drvinfo = kaweth_get_drvinfo +}; /**************************************************************** * kaweth_usb_transmit_complete @@ -1107,11 +1085,11 @@ static int kaweth_probe( kaweth->net->watchdog_timeo = KAWETH_TX_TIMEOUT; kaweth->net->tx_timeout = kaweth_tx_timeout; - kaweth->net->do_ioctl = kaweth_ioctl; kaweth->net->hard_start_xmit = kaweth_start_xmit; kaweth->net->set_multicast_list = kaweth_set_rx_mode; kaweth->net->get_stats = kaweth_netdev_stats; kaweth->net->mtu = le16_to_cpu(kaweth->configuration.segment_size); + SET_ETHTOOL_OPS(kaweth->net, &ops); memset(&kaweth->stats, 0, sizeof(kaweth->stats)); --- linux-2.6.9-rc1/drivers/usb/net/pegasus.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/usb/net/pegasus.c 2004-09-03 00:56:35.112822688 -0700 @@ -308,7 +308,7 @@ static int read_mii_word(pegasus_t * peg static int mdio_read(struct net_device *dev, int phy_id, int loc) { - pegasus_t *pegasus = (pegasus_t *) dev->priv; + pegasus_t *pegasus = (pegasus_t *) netdev_priv(dev); int res; read_mii_word(pegasus, phy_id, loc, (u16 *) & res); @@ -338,7 +338,7 @@ static int write_mii_word(pegasus_t * pe static void mdio_write(struct net_device *dev, int phy_id, int loc, int val) { - pegasus_t *pegasus = (pegasus_t *) dev->priv; + pegasus_t *pegasus = (pegasus_t *) netdev_priv(dev); write_mii_word(pegasus, phy_id, loc, val); } @@ -471,7 +471,7 @@ static int enable_net_traffic(struct net { __u16 linkpart; __u8 data[4]; - pegasus_t *pegasus = dev->priv; + pegasus_t *pegasus = netdev_priv(dev); read_mii_word(pegasus, pegasus->phy, MII_LPA, &linkpart); data[0] = 0xc9; @@ -754,11 +754,7 @@ static void intr_callback(struct urb *ur static void pegasus_tx_timeout(struct net_device *net) { - pegasus_t *pegasus = net->priv; - - if (!pegasus) - return; - + pegasus_t *pegasus = netdev_priv(net); warn("%s: Tx timed out.", net->name); pegasus->tx_urb->transfer_flags |= URB_ASYNC_UNLINK; usb_unlink_urb(pegasus->tx_urb); @@ -767,7 +763,7 @@ static void pegasus_tx_timeout(struct ne static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net) { - pegasus_t *pegasus = net->priv; + pegasus_t *pegasus = netdev_priv(net); int count = ((skb->len + 2) & 0x3f) ? skb->len + 2 : skb->len + 3; int res; __u16 l16 = skb->len; @@ -804,7 +800,7 @@ static int pegasus_start_xmit(struct sk_ static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev) { - return &((pegasus_t *) dev->priv)->stats; + return &((pegasus_t *) netdev_priv(dev))->stats; } static inline void disable_net_traffic(pegasus_t * pegasus) @@ -832,10 +828,9 @@ static inline void get_interrupt_interva static void set_carrier(struct net_device *net) { - pegasus_t *pegasus; + pegasus_t *pegasus = netdev_priv(net); short tmp; - pegasus = net->priv; read_mii_word(pegasus, pegasus->phy, MII_BMSR, &tmp); if (tmp & BMSR_LSTATUS) netif_carrier_on(net); @@ -890,7 +885,7 @@ static int alloc_urbs(pegasus_t * pegasu static int pegasus_open(struct net_device *net) { - pegasus_t *pegasus = (pegasus_t *) net->priv; + pegasus_t *pegasus = netdev_priv(net); int res; if (pegasus->rx_skb == NULL) @@ -933,7 +928,7 @@ exit: static int pegasus_close(struct net_device *net) { - pegasus_t *pegasus = net->priv; + pegasus_t *pegasus = netdev_priv(net); pegasus->flags &= ~PEGASUS_RUNNING; netif_stop_queue(net); @@ -944,177 +939,124 @@ static int pegasus_close(struct net_devi return 0; } -#ifdef CONFIG_MII -static int pegasus_ethtool_ioctl(struct net_device *dev, void __user *useraddr) -{ - u32 ethcmd; - pegasus_t *pegasus = dev->priv; +void pegasus_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + pegasus_t *pegasus = netdev_priv(dev); + strncpy(info->driver, driver_name, sizeof (info->driver) - 1); + strncpy(info->version, DRIVER_VERSION, sizeof (info->version) - 1); + usb_make_path(pegasus->usb, info->bus_info, sizeof (info->bus_info)); +} - if (copy_from_user(ðcmd, useraddr, sizeof (ethcmd))) - return -EFAULT; +#ifdef CONFIG_MII +static int pegasus_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + pegasus_t *pegasus = netdev_priv(dev); + mii_ethtool_gset(&pegasus->mii, ecmd); + return 0; +} +static int pegasus_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + pegasus_t *pegasus = netdev_priv(dev); + return mii_ethtool_sset(&pegasus->mii, ecmd); +} - switch (ethcmd) { - /* get driver-specific version/etc. info */ - case ETHTOOL_GDRVINFO:{ - struct ethtool_drvinfo info; - memset (&info, 0, sizeof (info)); - info.cmd = ETHTOOL_GDRVINFO; - strncpy(info.driver, driver_name, - sizeof (info.driver) - 1); - strncpy(info.version, DRIVER_VERSION, - sizeof (info.version) - 1); - usb_make_path(pegasus->usb, info.bus_info, - sizeof (info.bus_info)); - if (copy_to_user(useraddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } +static int pegasus_nway_reset(struct net_device *dev) +{ + pegasus_t *pegasus = netdev_priv(dev); + return mii_nway_restart(&pegasus->mii); +} - /* get settings */ - case ETHTOOL_GSET:{ - struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - mii_ethtool_gset(&pegasus->mii, &ecmd); - if (copy_to_user(useraddr, &ecmd, sizeof (ecmd))) - return -EFAULT; - return 0; - } - /* set settings */ - case ETHTOOL_SSET:{ - int r; - struct ethtool_cmd ecmd; - if (copy_from_user(&ecmd, useraddr, sizeof (ecmd))) - return -EFAULT; - r = mii_ethtool_sset(&pegasus->mii, &ecmd); - return r; - } - /* restart autonegotiation */ - case ETHTOOL_NWAY_RST:{ - return mii_nway_restart(&pegasus->mii); - } +static u32 pegasus_get_link(struct net_device *dev) +{ + pegasus_t *pegasus = netdev_priv(dev); + return mii_link_ok(&pegasus->mii); +} - /* get link status */ - case ETHTOOL_GLINK:{ - struct ethtool_value edata = { ETHTOOL_GLINK }; - edata.data = mii_link_ok(&pegasus->mii); - if (copy_to_user(useraddr, &edata, sizeof (edata))) - return -EFAULT; - return 0; - } - /* get message-level */ - case ETHTOOL_GMSGLVL:{ - struct ethtool_value edata = { ETHTOOL_GMSGLVL }; - /* edata.data = pegasus->msg_enable; FIXME */ - if (copy_to_user(useraddr, &edata, sizeof (edata))) - return -EFAULT; - return 0; - } - /* set message-level */ - case ETHTOOL_SMSGLVL:{ - struct ethtool_value edata; - if (copy_from_user(&edata, useraddr, sizeof (edata))) - return -EFAULT; - /* sp->msg_enable = edata.data; FIXME */ - return 0; - } +static u32 pegasus_get_msglevel(struct net_device *dev) +{ + /* + * pegasus_t *pegasus = netdev_priv(dev); + * return pegasus->msg_enable; FIXME + */ + return 0; +} - } +static void pegasus_set_msglevel(struct net_device *dev, u32 v) +{ + /* + * pegasus_t *pegasus = netdev_priv(dev); + * pegasus->msg_enable = edata.data; FIXME + */ +} - return -EOPNOTSUPP; +static struct ethtool_ops ops = { + .get_drvinfo = pegasus_get_drvinfo, + .get_settings = pegasus_get_settings, + .set_settings = pegasus_set_settings, + .nway_reset = pegasus_nway_reset, + .get_link = pegasus_get_link, + .get_msglevel = pegasus_get_msglevel, + .set_msglevel = pegasus_set_msglevel, +}; -} #else -static int pegasus_ethtool_ioctl(struct net_device *net, void __user *uaddr) -{ - pegasus_t *pegasus; - int cmd; - pegasus = net->priv; - if (get_user(cmd, (int __user *) uaddr)) - return -EFAULT; - switch (cmd) { - case ETHTOOL_GDRVINFO:{ - struct ethtool_drvinfo info; - memset (&info, 0, sizeof (info)); - info.cmd = ETHTOOL_GDRVINFO; - strncpy(info.driver, driver_name, - sizeof (info.driver) - 1); - strncpy(info.version, DRIVER_VERSION, - sizeof (info.version) - 1); - usb_make_path(pegasus->usb, info.bus_info, - sizeof (info.bus_info)); - if (copy_to_user(uaddr, &info, sizeof (info))) - return -EFAULT; - return 0; - } - case ETHTOOL_GSET:{ - struct ethtool_cmd ecmd; - short lpa, bmcr; - u8 port; - - memset(&ecmd, 0, sizeof (ecmd)); - ecmd.supported = (SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | - SUPPORTED_Autoneg | - SUPPORTED_TP | SUPPORTED_MII); - get_registers(pegasus, Reg7b, 1, &port); - if (port == 0) - ecmd.port = PORT_MII; - else - ecmd.port = PORT_TP; - ecmd.transceiver = XCVR_INTERNAL; - ecmd.phy_address = pegasus->phy; - read_mii_word(pegasus, pegasus->phy, MII_BMCR, &bmcr); - read_mii_word(pegasus, pegasus->phy, MII_LPA, &lpa); - if (bmcr & BMCR_ANENABLE) { - ecmd.autoneg = AUTONEG_ENABLE; - ecmd.speed = lpa & (LPA_100HALF | LPA_100FULL) ? - SPEED_100 : SPEED_10; - if (ecmd.speed == SPEED_100) - ecmd.duplex = lpa & LPA_100FULL ? - DUPLEX_FULL : DUPLEX_HALF; - else - ecmd.duplex = lpa & LPA_10FULL ? - DUPLEX_FULL : DUPLEX_HALF; - } else { - ecmd.autoneg = AUTONEG_DISABLE; - ecmd.speed = bmcr & BMCR_SPEED100 ? - SPEED_100 : SPEED_10; - ecmd.duplex = bmcr & BMCR_FULLDPLX ? - DUPLEX_FULL : DUPLEX_HALF; - } - if (copy_to_user(uaddr, &ecmd, sizeof (ecmd))) - return -EFAULT; - - return 0; - } - case ETHTOOL_SSET:{ - return -EOPNOTSUPP; - } - case ETHTOOL_GLINK:{ - struct ethtool_value edata = { ETHTOOL_GLINK }; - edata.data = netif_carrier_ok(net); - if (copy_to_user(uaddr, &edata, sizeof (edata))) - return -EFAULT; - return 0; - } - default: - return -EOPNOTSUPP; +static int pegasus_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + pegasus_t *pegasus = netdev_priv(dev); + short lpa, bmcr; + u8 port; + + ecmd->supported = (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | + SUPPORTED_TP | SUPPORTED_MII); + get_registers(pegasus, Reg7b, 1, &port); + if (port == 0) + ecmd->port = PORT_MII; + else + ecmd->port = PORT_TP; + ecmd->transceiver = XCVR_INTERNAL; + ecmd->phy_address = pegasus->phy; + read_mii_word(pegasus, pegasus->phy, MII_BMCR, &bmcr); + read_mii_word(pegasus, pegasus->phy, MII_LPA, &lpa); + if (bmcr & BMCR_ANENABLE) { + ecmd->autoneg = AUTONEG_ENABLE; + ecmd->speed = lpa & (LPA_100HALF | LPA_100FULL) ? + SPEED_100 : SPEED_10; + if (ecmd->speed == SPEED_100) + ecmd->duplex = lpa & LPA_100FULL ? + DUPLEX_FULL : DUPLEX_HALF; + else + ecmd->duplex = lpa & LPA_10FULL ? + DUPLEX_FULL : DUPLEX_HALF; + } else { + ecmd->autoneg = AUTONEG_DISABLE; + ecmd->speed = bmcr & BMCR_SPEED100 ? + SPEED_100 : SPEED_10; + ecmd->duplex = bmcr & BMCR_FULLDPLX ? + DUPLEX_FULL : DUPLEX_HALF; } + return 0; } + +static struct ethtool_ops ops = { + .get_drvinfo = pegasus_get_drvinfo, + .get_settings = pegasus_get_settings, + .get_link = ethtool_op_get_link, +}; #endif + static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd) { __u16 *data = (__u16 *) & rq->ifr_ifru; - pegasus_t *pegasus = net->priv; + pegasus_t *pegasus = netdev_priv(net); int res; switch (cmd) { - case SIOCETHTOOL: - res = pegasus_ethtool_ioctl(net, rq->ifr_data); - break; case SIOCDEVPRIVATE: data[0] = pegasus->phy; case SIOCDEVPRIVATE + 1: @@ -1135,7 +1077,7 @@ static int pegasus_ioctl(struct net_devi static void pegasus_set_multicast(struct net_device *net) { - pegasus_t *pegasus = net->priv; + pegasus_t *pegasus = netdev_priv(net); if (net->flags & IFF_PROMISC) { pegasus->eth_regs[EthCtrl2] |= RX_PROMISCUOUS; @@ -1211,11 +1153,13 @@ static int pegasus_probe(struct usb_inte int res = -ENOMEM; usb_get_dev(dev); - if (!(pegasus = kmalloc(sizeof (struct pegasus), GFP_KERNEL))) { + net = alloc_etherdev(sizeof(struct pegasus)); + if (!net) { err("out of memory allocating device structure"); goto out; } + pegasus = netdev_priv(net); memset(pegasus, 0, sizeof (struct pegasus)); pegasus->dev_index = dev_index; init_waitqueue_head(&pegasus->ctrl_wait); @@ -1223,16 +1167,11 @@ static int pegasus_probe(struct usb_inte if (!alloc_urbs(pegasus)) goto out1; - net = alloc_etherdev(0); - if (!net) - goto out2; - tasklet_init(&pegasus->rx_tl, rx_fixup, (unsigned long) pegasus); pegasus->usb = dev; pegasus->net = net; SET_MODULE_OWNER(net); - net->priv = pegasus; net->open = pegasus_open; net->stop = pegasus_close; net->watchdog_timeo = PEGASUS_TX_TIMEOUT; @@ -1242,6 +1181,7 @@ static int pegasus_probe(struct usb_inte net->set_multicast_list = pegasus_set_multicast; net->get_stats = pegasus_netdev_stats; net->mtu = PEGASUS_MTU; + SET_ETHTOOL_OPS(net, &ops); pegasus->mii.dev = net; pegasus->mii.mdio_read = mdio_read; pegasus->mii.mdio_write = mdio_write; @@ -1254,7 +1194,7 @@ static int pegasus_probe(struct usb_inte if (reset_mac(pegasus)) { err("can't reset MAC"); res = -EIO; - goto out3; + goto out2; } set_ethernet_addr(pegasus); fill_skb_pool(pegasus); @@ -1271,19 +1211,17 @@ static int pegasus_probe(struct usb_inte SET_NETDEV_DEV(net, &intf->dev); res = register_netdev(net); if (res) - goto out4; + goto out3; printk("%s: %s\n", net->name, usb_dev_id[dev_index].name); return 0; -out4: +out3: usb_set_intfdata(intf, NULL); free_skb_pool(pegasus); -out3: - free_netdev(net); out2: free_all_urbs(pegasus); out1: - kfree(pegasus); + free_netdev(net); out: usb_put_dev(dev); return res; @@ -1307,7 +1245,6 @@ static void pegasus_disconnect(struct us if (pegasus->rx_skb) dev_kfree_skb(pegasus->rx_skb); free_netdev(pegasus->net); - kfree(pegasus); } static struct usb_driver pegasus_driver = { --- linux-2.6.9-rc1/drivers/usb/net/rtl8150.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/usb/net/rtl8150.c 2004-09-03 00:56:32.633199648 -0700 @@ -265,7 +265,7 @@ static int read_mii_word(rtl8150_t * dev if (i < MII_TIMEOUT) { get_registers(dev, PHYDAT, 2, data); - *reg = le16_to_cpup(data); + *reg = data[0] | (data[1] << 8); return 0; } else return 1; @@ -304,15 +304,12 @@ static inline void set_ethernet_addr(rtl static int rtl8150_set_mac_address(struct net_device *netdev, void *p) { struct sockaddr *addr = p; - rtl8150_t *dev; + rtl8150_t *dev = netdev_priv(netdev); int i; if (netif_running(netdev)) return -EBUSY; - dev = netdev->priv; - if (dev == NULL) { - return -ENODEV; - } + memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); dbg("%s: Setting MAC address to ", netdev->name); for (i = 0; i < 5; i++) @@ -651,16 +648,12 @@ static void disable_net_traffic(rtl8150_ static struct net_device_stats *rtl8150_netdev_stats(struct net_device *dev) { - return &((rtl8150_t *) dev->priv)->stats; + return &((rtl8150_t *)netdev_priv(dev))->stats; } static void rtl8150_tx_timeout(struct net_device *netdev) { - rtl8150_t *dev; - - dev = netdev->priv; - if (!dev) - return; + rtl8150_t *dev = netdev_priv(netdev); warn("%s: Tx timeout.", netdev->name); dev->tx_urb->transfer_flags |= URB_ASYNC_UNLINK; usb_unlink_urb(dev->tx_urb); @@ -669,9 +662,7 @@ static void rtl8150_tx_timeout(struct ne static void rtl8150_set_multicast(struct net_device *netdev) { - rtl8150_t *dev; - - dev = netdev->priv; + rtl8150_t *dev = netdev_priv(netdev); netif_stop_queue(netdev); if (netdev->flags & IFF_PROMISC) { dev->rx_creg |= cpu_to_le16(0x0001); @@ -691,11 +682,10 @@ static void rtl8150_set_multicast(struct static int rtl8150_start_xmit(struct sk_buff *skb, struct net_device *netdev) { - rtl8150_t *dev; + rtl8150_t *dev = netdev_priv(netdev); int count, res; netif_stop_queue(netdev); - dev = netdev->priv; count = (skb->len < 60) ? 60 : skb->len; count = (count & 0x3f) ? count : count + 1; dev->tx_skb = skb; @@ -717,7 +707,7 @@ static int rtl8150_start_xmit(struct sk_ static void set_carrier(struct net_device *netdev) { - rtl8150_t *dev = netdev->priv; + rtl8150_t *dev = netdev_priv(netdev); short tmp; get_registers(dev, CSCR, 2, &tmp); @@ -729,13 +719,9 @@ static void set_carrier(struct net_devic static int rtl8150_open(struct net_device *netdev) { - rtl8150_t *dev; + rtl8150_t *dev = netdev_priv(netdev); int res; - dev = netdev->priv; - if (dev == NULL) { - return -ENODEV; - } if (dev->rx_skb == NULL) dev->rx_skb = pull_skb(dev); if (!dev->rx_skb) @@ -761,13 +747,9 @@ static int rtl8150_open(struct net_devic static int rtl8150_close(struct net_device *netdev) { - rtl8150_t *dev; + rtl8150_t *dev = netdev_priv(netdev); int res = 0; - dev = netdev->priv; - if (!dev) - return -ENODEV; - netif_stop_queue(netdev); if (!test_bit(RTL8150_UNPLUG, &dev->flags)) disable_net_traffic(dev); @@ -776,93 +758,64 @@ static int rtl8150_close(struct net_devi return res; } -static int rtl8150_ethtool_ioctl(struct net_device *netdev, void __user *uaddr) +static void rtl8150_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) { - rtl8150_t *dev; - int cmd; - - dev = netdev->priv; - if (get_user(cmd, (int __user *) uaddr)) - return -EFAULT; + rtl8150_t *dev = netdev_priv(netdev); - switch (cmd) { - case ETHTOOL_GDRVINFO:{ - struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; - - strncpy(info.driver, driver_name, ETHTOOL_BUSINFO_LEN); - strncpy(info.version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN); - usb_make_path(dev->udev, info.bus_info, sizeof info.bus_info); - if (copy_to_user(uaddr, &info, sizeof(info))) - return -EFAULT; - return 0; - } - case ETHTOOL_GSET:{ - struct ethtool_cmd ecmd; - short lpa, bmcr; - - if (copy_from_user(&ecmd, uaddr, sizeof(ecmd))) - return -EFAULT; - ecmd.supported = (SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | - SUPPORTED_Autoneg | - SUPPORTED_TP | SUPPORTED_MII); - ecmd.port = PORT_TP; - ecmd.transceiver = XCVR_INTERNAL; - ecmd.phy_address = dev->phy; - get_registers(dev, BMCR, 2, &bmcr); - get_registers(dev, ANLP, 2, &lpa); - if (bmcr & BMCR_ANENABLE) { - ecmd.autoneg = AUTONEG_ENABLE; - ecmd.speed = (lpa & (LPA_100HALF | LPA_100FULL)) ? - SPEED_100 : SPEED_10; - if (ecmd.speed == SPEED_100) - ecmd.duplex = (lpa & LPA_100FULL) ? - DUPLEX_FULL : DUPLEX_HALF; - else - ecmd.duplex = (lpa & LPA_10FULL) ? - DUPLEX_FULL : DUPLEX_HALF; - } else { - ecmd.autoneg = AUTONEG_DISABLE; - ecmd.speed = (bmcr & BMCR_SPEED100) ? - SPEED_100 : SPEED_10; - ecmd.duplex = (bmcr & BMCR_FULLDPLX) ? + strncpy(info->driver, driver_name, ETHTOOL_BUSINFO_LEN); + strncpy(info->version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN); + usb_make_path(dev->udev, info->bus_info, sizeof info->bus_info); +} + +static int rtl8150_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) +{ + rtl8150_t *dev = netdev_priv(netdev); + short lpa, bmcr; + + ecmd->supported = (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | + SUPPORTED_TP | SUPPORTED_MII); + ecmd->port = PORT_TP; + ecmd->transceiver = XCVR_INTERNAL; + ecmd->phy_address = dev->phy; + get_registers(dev, BMCR, 2, &bmcr); + get_registers(dev, ANLP, 2, &lpa); + if (bmcr & BMCR_ANENABLE) { + ecmd->autoneg = AUTONEG_ENABLE; + ecmd->speed = (lpa & (LPA_100HALF | LPA_100FULL)) ? + SPEED_100 : SPEED_10; + if (ecmd->speed == SPEED_100) + ecmd->duplex = (lpa & LPA_100FULL) ? DUPLEX_FULL : DUPLEX_HALF; - } - if (copy_to_user(uaddr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; - } - case ETHTOOL_SSET: - return -ENOTSUPP; - case ETHTOOL_GLINK:{ - struct ethtool_value edata = { ETHTOOL_GLINK }; - - edata.data = netif_carrier_ok(netdev); - if (copy_to_user(uaddr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - default: - return -EOPNOTSUPP; + else + ecmd->duplex = (lpa & LPA_10FULL) ? + DUPLEX_FULL : DUPLEX_HALF; + } else { + ecmd->autoneg = AUTONEG_DISABLE; + ecmd->speed = (bmcr & BMCR_SPEED100) ? + SPEED_100 : SPEED_10; + ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? + DUPLEX_FULL : DUPLEX_HALF; } + return 0; } +static struct ethtool_ops ops = { + .get_drvinfo = rtl8150_get_drvinfo, + .get_settings = rtl8150_get_settings, + .get_link = ethtool_op_get_link +}; + static int rtl8150_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) { - rtl8150_t *dev; - u16 *data; - int res; - - dev = netdev->priv; - data = (u16 *) & rq->ifr_ifru; - res = 0; + rtl8150_t *dev = netdev_priv(netdev); + u16 *data = (u16 *) & rq->ifr_ifru; + int res = 0; switch (cmd) { - case SIOCETHTOOL: - res = rtl8150_ethtool_ioctl(netdev, rq->ifr_data); - break; case SIOCDEVPRIVATE: data[0] = dev->phy; case SIOCDEVPRIVATE + 1: @@ -887,23 +840,18 @@ static int rtl8150_probe(struct usb_inte rtl8150_t *dev; struct net_device *netdev; - dev = kmalloc(sizeof(rtl8150_t), GFP_KERNEL); - if (!dev) { + netdev = alloc_etherdev(sizeof(rtl8150_t)); + if (!netdev) { err("Out of memory"); return -ENOMEM; - } else - memset(dev, 0, sizeof(rtl8150_t)); + } + + dev = netdev_priv(netdev); + memset(dev, 0, sizeof(rtl8150_t)); dev->intr_buff = kmalloc(INTBUFSIZE, GFP_KERNEL); if (!dev->intr_buff) { - kfree(dev); - return -ENOMEM; - } - netdev = alloc_etherdev(0); - if (!netdev) { - kfree(dev->intr_buff); - kfree(dev); - err("Oh boy, out of memory again?!?"); + free_netdev(netdev); return -ENOMEM; } @@ -913,7 +861,6 @@ static int rtl8150_probe(struct usb_inte dev->udev = udev; dev->netdev = netdev; SET_MODULE_OWNER(netdev); - netdev->priv = dev; netdev->open = rtl8150_open; netdev->stop = rtl8150_close; netdev->do_ioctl = rtl8150_ioctl; @@ -924,6 +871,7 @@ static int rtl8150_probe(struct usb_inte netdev->set_mac_address = rtl8150_set_mac_address; netdev->get_stats = rtl8150_netdev_stats; netdev->mtu = RTL8150_MTU; + SET_ETHTOOL_OPS(netdev, &ops); dev->intr_interval = 100; /* 100ms */ if (!alloc_all_urbs(dev)) { @@ -954,7 +902,6 @@ out1: out: kfree(dev->intr_buff); free_netdev(netdev); - kfree(dev); return -EIO; } @@ -971,9 +918,8 @@ static void rtl8150_disconnect(struct us free_skb_pool(dev); if (dev->rx_skb) dev_kfree_skb(dev->rx_skb); - free_netdev(dev->netdev); kfree(dev->intr_buff); - kfree(dev); + free_netdev(dev->netdev); } } --- linux-2.6.9-rc1/drivers/usb/serial/io_edgeport.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/drivers/usb/serial/io_edgeport.c 2004-09-02 20:51:52.000000000 -0700 @@ -653,7 +653,7 @@ static void get_product_info(struct edge memset (product_info, 0, sizeof(struct edgeport_product_info)); - product_info->ProductId = (__u16)(edge_serial->serial->dev->descriptor.idProduct & ~ION_DEVICE_ID_GENERATION_2); + product_info->ProductId = (__u16)(edge_serial->serial->dev->descriptor.idProduct & ~ION_DEVICE_ID_80251_NETCHIP); product_info->NumPorts = edge_serial->manuf_descriptor.NumPorts; product_info->ProdInfoVer = 0; @@ -669,7 +669,7 @@ static void get_product_info(struct edge memcpy(product_info->ManufactureDescDate, edge_serial->manuf_descriptor.DescDate, sizeof(edge_serial->manuf_descriptor.DescDate)); // check if this is 2nd generation hardware - if (edge_serial->serial->dev->descriptor.idProduct & ION_DEVICE_ID_GENERATION_2) { + if (edge_serial->serial->dev->descriptor.idProduct & ION_DEVICE_ID_80251_NETCHIP) { product_info->FirmwareMajorVersion = OperationalCodeImageVersion_GEN2.MajorVersion; product_info->FirmwareMinorVersion = OperationalCodeImageVersion_GEN2.MinorVersion; product_info->FirmwareBuildNumber = cpu_to_le16(OperationalCodeImageVersion_GEN2.BuildNumber); @@ -1389,7 +1389,7 @@ static void send_more_port_data(struct e // to bother queueing a write. If it's too small, say a few bytes, // it's better to wait for more credits so we can do a larger // write. - if (edge_port->txCredits < EDGE_FW_GET_TX_CREDITS_SEND_THRESHOLD(edge_port->maxTxCredits)) { + if (edge_port->txCredits < EDGE_FW_GET_TX_CREDITS_SEND_THRESHOLD(edge_port->maxTxCredits,EDGE_FW_BULK_MAX_PACKET_SIZE)) { dbg("%s(%d) Not enough credit - fifo %d TxCredit %d", __FUNCTION__, edge_port->port->number, fifo->count, edge_port->txCredits ); return; } @@ -3007,9 +3007,6 @@ static void edge_shutdown (struct usb_se static int __init edgeport_init(void) { int retval; - retval = usb_serial_register(&edgeport_1port_device); - if (retval) - goto failed_1port_device_register; retval = usb_serial_register(&edgeport_2port_device); if (retval) goto failed_2port_device_register; @@ -3031,8 +3028,6 @@ failed_8port_device_register: failed_4port_device_register: usb_serial_deregister(&edgeport_2port_device); failed_2port_device_register: - usb_serial_deregister(&edgeport_1port_device); -failed_1port_device_register: return retval; } @@ -3045,7 +3040,6 @@ failed_1port_device_register: static void __exit edgeport_exit (void) { usb_deregister (&io_driver); - usb_serial_deregister (&edgeport_1port_device); usb_serial_deregister (&edgeport_2port_device); usb_serial_deregister (&edgeport_4port_device); usb_serial_deregister (&edgeport_8port_device); --- linux-2.6.9-rc1/drivers/usb/serial/io_fw_down3.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/drivers/usb/serial/io_fw_down3.h 2004-09-02 20:51:52.000000000 -0700 @@ -1,11 +1,11 @@ //************************************************************** //* Edgeport Binary Image (for TI based products) -//* Generated by TIBin2C v1.00 +//* Generated by TIBin2C v2.00 (watchport) //* Copyright (C) 2001 Inside Out Networks, All rights reserved. //************************************************************** -static int IMAGE_SIZE = 12166; +static int IMAGE_SIZE = 12749; struct EDGE_FIRMWARE_VERSION_INFO { @@ -16,7 +16,7 @@ struct EDGE_FIRMWARE_VERSION_INFO static struct EDGE_FIRMWARE_VERSION_INFO IMAGE_VERSION_NAME = { - 4, 1, 0 // Major, Minor, Build + 4, 10, 0 // Major, Minor, Build }; @@ -27,20 +27,20 @@ static unsigned char IMAGE_ARRAY_NAME[] // WORD Length; // BYTE CheckSum; // }; -0x83, 0x2f, -0x33, +0xca, 0x31, +0xa8, -0x02, 0x24, 0x84, 0x02, 0x1f, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x1a, 0x85, 0x45, -0x8c, 0x85, 0x46, 0x8a, 0xc0, 0xe0, 0xc0, 0xd0, 0xc0, 0xf0, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x00, -0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x04, 0xc0, 0x05, 0xc0, 0x06, 0xc0, 0x07, 0xe5, 0x44, -0x24, 0x08, 0xf8, 0xe6, 0x60, 0x2b, 0xe5, 0x44, 0x24, 0x10, 0xf8, 0xa6, 0x81, 0xe5, 0x44, 0x75, -0xf0, 0x21, 0xa4, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x34, 0xf8, 0xf5, 0x83, 0x78, 0x92, 0xe5, 0x81, -0x04, 0xc3, 0x98, 0xf9, 0x94, 0x22, 0x40, 0x03, 0x02, 0x11, 0x1a, 0xe6, 0xf0, 0x08, 0xa3, 0xd9, -0xfa, 0x74, 0x08, 0x25, 0x44, 0xf8, 0x05, 0x44, 0x08, 0xe6, 0x54, 0x80, 0x70, 0x0c, 0xe5, 0x44, -0xb4, 0x07, 0xf3, 0x78, 0x08, 0x75, 0x44, 0x00, 0x80, 0xef, 0xe5, 0x44, 0x24, 0x10, 0xf8, 0x86, -0x81, 0xe5, 0x44, 0x75, 0xf0, 0x21, 0xa4, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x34, 0xf8, 0xf5, 0x83, -0x78, 0x92, 0xe5, 0x81, 0x04, 0xc3, 0x98, 0xf9, 0xe0, 0xf6, 0x08, 0xa3, 0xd9, 0xfa, 0xd0, 0x07, +0x02, 0x26, 0xfe, 0x02, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x1a, 0x85, 0x3f, +0x8c, 0x85, 0x40, 0x8a, 0xc0, 0xe0, 0xc0, 0xd0, 0xc0, 0xf0, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x00, +0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x04, 0xc0, 0x05, 0xc0, 0x06, 0xc0, 0x07, 0xe5, 0x3e, +0x24, 0x08, 0xf8, 0xe6, 0x60, 0x2b, 0xe5, 0x3e, 0x24, 0x10, 0xf8, 0xa6, 0x81, 0xe5, 0x3e, 0x75, +0xf0, 0x21, 0xa4, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x34, 0xf8, 0xf5, 0x83, 0x78, 0x8c, 0xe5, 0x81, +0x04, 0xc3, 0x98, 0xf9, 0x94, 0x22, 0x40, 0x03, 0x02, 0x11, 0x94, 0xe6, 0xf0, 0x08, 0xa3, 0xd9, +0xfa, 0x74, 0x08, 0x25, 0x3e, 0xf8, 0x05, 0x3e, 0x08, 0xe6, 0x54, 0x80, 0x70, 0x0c, 0xe5, 0x3e, +0xb4, 0x07, 0xf3, 0x78, 0x08, 0x75, 0x3e, 0x00, 0x80, 0xef, 0xe5, 0x3e, 0x24, 0x10, 0xf8, 0x86, +0x81, 0xe5, 0x3e, 0x75, 0xf0, 0x21, 0xa4, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x34, 0xf8, 0xf5, 0x83, +0x78, 0x8c, 0xe5, 0x81, 0x04, 0xc3, 0x98, 0xf9, 0xe0, 0xf6, 0x08, 0xa3, 0xd9, 0xfa, 0xd0, 0x07, 0xd0, 0x06, 0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x03, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0xf0, 0xd0, 0xd0, 0xd0, 0xe0, 0x32, 0x30, 0x01, 0x4d, 0x30, 0xb4, 0x48, 0x10, 0x00, 0x45, 0x90, 0xff, 0x08, 0xe0, 0x54, 0x20, 0xf8, 0x90, 0xff, 0x48, 0xe0, 0x54, 0x20, 0xf9, @@ -50,747 +50,783 @@ static unsigned char IMAGE_ARRAY_NAME[] 0xa3, 0xe0, 0xcb, 0xf0, 0x6b, 0x60, 0x02, 0x7e, 0x04, 0x22, 0xc0, 0xe0, 0xc0, 0xd0, 0xc0, 0xf0, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x04, 0xc0, 0x05, 0xc0, 0x06, 0xc0, 0x07, 0x90, 0xff, 0x93, 0x74, 0x01, 0xf0, 0xe5, 0x81, 0x94, 0xfd, 0x40, 0x03, -0x02, 0x11, 0x1a, 0x85, 0x47, 0x8d, 0x85, 0x48, 0x8b, 0x74, 0xae, 0xf5, 0x82, 0x74, 0xfa, 0xf5, +0x02, 0x11, 0x94, 0x85, 0x41, 0x8d, 0x85, 0x42, 0x8b, 0x74, 0xaf, 0xf5, 0x82, 0x74, 0xfa, 0xf5, 0x83, 0xe0, 0xb4, 0x01, 0x1b, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x4a, 0xe0, 0x30, 0xe7, 0x2c, 0x90, 0xff, 0x4e, 0xe0, 0x30, 0xe7, 0x25, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x02, 0xf0, 0x80, 0x20, -0xb4, 0x02, 0x1d, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x7a, 0xe0, 0x30, 0xe7, 0x05, 0x12, 0x25, -0x13, 0x80, 0x09, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x03, 0xf0, 0x80, 0x04, 0xd0, 0x83, 0xd0, 0x82, +0xb4, 0x02, 0x1d, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x7a, 0xe0, 0x30, 0xe7, 0x05, 0x12, 0x27, +0x8d, 0x80, 0x09, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x03, 0xf0, 0x80, 0x04, 0xd0, 0x83, 0xd0, 0x82, 0xa3, 0xe0, 0xb4, 0x01, 0x1b, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x52, 0xe0, 0x30, 0xe7, 0x2c, -0x90, 0xff, 0x56, 0xe0, 0x30, 0xe7, 0x25, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x02, 0xf0, 0x80, 0x20, -0xb4, 0x02, 0x1d, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x7a, 0xe0, 0x30, 0xe7, 0x05, 0x12, 0x25, -0x13, 0x80, 0x09, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x03, 0xf0, 0x80, 0x04, 0xd0, 0x83, 0xd0, 0x82, -0x20, 0x02, 0x03, 0x30, 0x01, 0x7b, 0x74, 0x16, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xe0, 0x14, -0xfc, 0xf0, 0xa3, 0xe0, 0xfd, 0xa3, 0xe0, 0xfe, 0x64, 0x04, 0x70, 0x0f, 0xec, 0x70, 0x62, 0x7e, -0x01, 0x12, 0x00, 0xc9, 0x7c, 0x0a, 0x7d, 0xfa, 0x02, 0x02, 0x22, 0x12, 0x00, 0xc9, 0xee, 0x64, -0x04, 0x60, 0x1d, 0xec, 0x70, 0x4b, 0x7c, 0x0a, 0xed, 0x14, 0xfd, 0x70, 0x15, 0xee, 0x64, 0x02, -0x60, 0x07, 0x7e, 0x02, 0x7d, 0x32, 0x02, 0x02, 0x22, 0x7e, 0x01, 0x7d, 0xfa, 0x02, 0x02, 0x22, -0x7c, 0x0a, 0x74, 0x16, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xec, 0xf0, 0xa3, 0xed, 0xf0, 0xa3, -0xee, 0xf0, 0x14, 0x60, 0x18, 0x20, 0xe1, 0x0f, 0x20, 0x01, 0x06, 0xd2, 0xb1, 0xc2, 0xb0, 0x80, -0x10, 0xc2, 0xb1, 0xd2, 0xb0, 0x80, 0x0a, 0xc2, 0xb1, 0xc2, 0xb0, 0x80, 0x04, 0xd2, 0xb0, 0xd2, -0xb1, 0x78, 0x19, 0x79, 0x09, 0x7a, 0x07, 0xe7, 0x70, 0x04, 0xa6, 0x00, 0x80, 0x0b, 0xe6, 0x60, -0x08, 0x16, 0xe6, 0x70, 0x04, 0xe7, 0x44, 0x80, 0xf7, 0x08, 0x09, 0xda, 0xea, 0xe5, 0x43, 0x60, -0x13, 0x14, 0xf5, 0x43, 0x70, 0x0e, 0xe5, 0x44, 0x24, 0x08, 0xf8, 0x76, 0x00, 0x12, 0x10, 0x95, -0xd2, 0x8c, 0xd2, 0x8d, 0xd0, 0x07, 0xd0, 0x06, 0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x03, 0xd0, 0x02, -0xd0, 0x01, 0xd0, 0x00, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0xf0, 0xd0, 0xd0, 0xd0, 0xe0, 0x32, 0x90, -0xff, 0x04, 0xe0, 0x90, 0xfa, 0xb5, 0xf0, 0x90, 0xff, 0x06, 0xe0, 0xfc, 0xa3, 0xe0, 0xfa, 0xec, -0xff, 0xea, 0xfe, 0xef, 0xc3, 0x94, 0x08, 0xee, 0x94, 0x01, 0x50, 0x02, 0x80, 0x04, 0x7e, 0x01, -0x7f, 0x08, 0x8e, 0x34, 0x8f, 0x35, 0x90, 0xff, 0x02, 0xe0, 0xfc, 0xa3, 0xe0, 0xfa, 0xec, 0xff, -0xea, 0x90, 0xfa, 0xb9, 0xf0, 0xef, 0xa3, 0xf0, 0x12, 0x18, 0x49, 0x78, 0x24, 0x7c, 0x00, 0x7d, -0x00, 0x12, 0x19, 0x6c, 0x7e, 0x00, 0x7f, 0x05, 0x12, 0x13, 0x8f, 0xe4, 0xf5, 0x53, 0xe5, 0x53, -0xc3, 0x94, 0x02, 0x50, 0x0f, 0x12, 0x18, 0x2a, 0xe4, 0x12, 0x13, 0xfb, 0x05, 0x53, 0x04, 0x12, -0x18, 0x1b, 0x80, 0xea, 0x12, 0x18, 0x49, 0x90, 0xff, 0x00, 0xe0, 0xff, 0x54, 0x60, 0x24, 0xc0, -0x70, 0x03, 0x02, 0x08, 0xb8, 0x24, 0x40, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x90, 0xfa, 0xb5, 0xe0, -0xfe, 0x54, 0x0f, 0xf5, 0x53, 0xee, 0x30, 0xe7, 0x03, 0xd3, 0x80, 0x01, 0xc3, 0x92, 0x0a, 0x90, -0xff, 0x01, 0xe0, 0x12, 0x15, 0x0f, 0x03, 0x55, 0x00, 0x04, 0x28, 0x01, 0x05, 0x2f, 0x03, 0x05, -0xf6, 0x05, 0x06, 0x38, 0x06, 0x07, 0x9a, 0x08, 0x07, 0xe2, 0x09, 0x08, 0x3e, 0x0a, 0x08, 0x7e, -0x0b, 0x00, 0x00, 0x0e, 0xac, 0xe5, 0x2c, 0x20, 0xe7, 0x03, 0x02, 0x0e, 0xac, 0x90, 0xfa, 0xb9, -0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x35, 0x64, 0x02, 0x45, 0x34, -0x60, 0x03, 0x02, 0x0e, 0xac, 0xef, 0x54, 0x1f, 0x14, 0x60, 0x2b, 0x14, 0x60, 0x47, 0x24, 0x02, -0x60, 0x03, 0x02, 0x0e, 0xac, 0xee, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x12, 0x18, 0x2a, 0x74, 0x01, -0x12, 0x13, 0xfb, 0x78, 0x6d, 0xe6, 0x30, 0xe0, 0x08, 0x12, 0x18, 0x2a, 0x74, 0x02, 0x12, 0x13, -0xfb, 0x7f, 0x02, 0x02, 0x2f, 0x6a, 0xe5, 0x2c, 0x20, 0xe1, 0x09, 0x90, 0xfa, 0xb5, 0xe0, 0x60, -0x03, 0x02, 0x0e, 0xac, 0x90, 0xfa, 0xb5, 0xe0, 0xd3, 0x94, 0x01, 0x40, 0x03, 0x02, 0x0e, 0xac, -0x7f, 0x02, 0x02, 0x2f, 0x6a, 0xe5, 0x2c, 0x20, 0xe1, 0x0e, 0x90, 0xfa, 0xb5, 0xe0, 0xff, 0x60, -0x07, 0x64, 0x80, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x12, 0x0f, 0x38, 0x40, 0x03, 0x02, 0x0e, 0xac, -0xe5, 0x53, 0x70, 0x19, 0x30, 0x0a, 0x0b, 0x90, 0xff, 0x80, 0x12, 0x18, 0x27, 0x12, 0x13, 0xfb, -0x80, 0x24, 0x90, 0xff, 0x82, 0x12, 0x18, 0x27, 0x12, 0x13, 0xfb, 0x80, 0x19, 0x15, 0x53, 0x30, -0x0a, 0x0b, 0x12, 0x18, 0xbd, 0x12, 0x18, 0x25, 0x12, 0x13, 0xfb, 0x80, 0x09, 0x12, 0x18, 0xcb, -0x12, 0x18, 0x25, 0x12, 0x13, 0xfb, 0x12, 0x18, 0x2a, 0x12, 0x13, 0xb5, 0x60, 0x05, 0x74, 0x01, -0x12, 0x13, 0xfb, 0x7f, 0x02, 0x02, 0x2f, 0x6a, 0xe5, 0x2c, 0x30, 0xe7, 0x03, 0x02, 0x0e, 0xac, -0xe5, 0x35, 0x45, 0x34, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x12, 0x18, 0xd9, 0x14, 0x60, 0x2d, 0x14, -0x60, 0x59, 0x24, 0x02, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x90, 0xfa, 0xb9, 0xe0, 0x70, 0x04, 0xa3, -0xe0, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x90, 0xfa, 0xb5, 0xe0, 0x60, 0x03, 0x02, 0x0e, -0xac, 0x78, 0x6d, 0xe6, 0x54, 0xfe, 0xf6, 0xe4, 0xff, 0x02, 0x2f, 0x6a, 0xe5, 0x2c, 0x20, 0xe1, -0x06, 0x20, 0xe0, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x2c, 0x30, 0xe0, 0x09, 0x90, 0xfa, 0xb5, 0xe0, -0x60, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x2c, 0x30, 0xe1, 0x0c, 0x90, 0xfa, 0xb5, 0xe0, 0xd3, 0x94, -0x01, 0x40, 0x03, 0x02, 0x0e, 0xac, 0xe4, 0xff, 0x02, 0x2f, 0x6a, 0x90, 0xfa, 0xb9, 0xe0, 0x70, -0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x12, 0x0f, 0x38, 0x40, 0x03, 0x02, 0x0e, 0xac, -0xe5, 0x2c, 0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x2c, 0x30, 0xe0, 0x07, -0xe5, 0x53, 0x60, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x53, 0x70, 0x0f, 0x90, 0xff, 0x82, 0xe0, 0x54, -0xf7, 0xf0, 0x90, 0xff, 0x80, 0xe0, 0x54, 0xf7, 0xf0, 0x22, 0xe5, 0x53, 0x24, 0xfe, 0x60, 0x1b, -0x04, 0x70, 0x2e, 0x30, 0x0a, 0x0c, 0xa2, 0x0a, 0xe4, 0x33, 0xfd, 0x7f, 0x03, 0x12, 0x2a, 0xce, -0x80, 0x1f, 0xe4, 0xfd, 0x7f, 0x03, 0x12, 0x2a, 0xce, 0x80, 0x16, 0x30, 0x0a, 0x0c, 0xa2, 0x0a, -0xe4, 0x33, 0xfd, 0x7f, 0x04, 0x12, 0x2a, 0xce, 0x80, 0x07, 0xe4, 0xfd, 0x7f, 0x04, 0x12, 0x2a, -0xce, 0x15, 0x53, 0x30, 0x0a, 0x0b, 0x12, 0x18, 0xbd, 0xf5, 0x83, 0xe0, 0x54, 0xf7, 0xf0, 0x80, -0x09, 0x12, 0x18, 0xcb, 0xf5, 0x83, 0xe0, 0x54, 0xf7, 0xf0, 0xe4, 0xff, 0x02, 0x2f, 0x6a, 0xe5, -0x2c, 0x30, 0xe7, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x35, 0x45, 0x34, 0x60, 0x03, 0x02, 0x0e, 0xac, -0x12, 0x18, 0xd9, 0x14, 0x60, 0x2d, 0x14, 0x60, 0x55, 0x24, 0x02, 0x60, 0x03, 0x02, 0x0e, 0xac, -0x90, 0xfa, 0xb9, 0xe0, 0x70, 0x04, 0xa3, 0xe0, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x90, -0xfa, 0xb5, 0xe0, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x78, 0x6d, 0xe6, 0x44, 0x01, 0xf6, 0xe4, 0xff, -0x02, 0x2f, 0x6a, 0xe5, 0x2c, 0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x2c, -0x30, 0xe0, 0x07, 0xe5, 0x53, 0x60, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x2c, 0x30, 0xe1, 0x0a, 0xe5, -0x53, 0xd3, 0x94, 0x01, 0x40, 0x03, 0x02, 0x0e, 0xac, 0xe4, 0xff, 0x02, 0x2f, 0x6a, 0x90, 0xfa, -0xb9, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x90, 0xfa, 0xb5, 0xe0, 0xff, -0x12, 0x2f, 0x3b, 0x40, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x2c, 0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, -0x02, 0x0e, 0xac, 0xe5, 0x53, 0x70, 0x09, 0x30, 0x0a, 0x03, 0x02, 0x19, 0x7a, 0x02, 0x19, 0x3e, -0xe5, 0x2c, 0x20, 0xe1, 0x03, 0x02, 0x0e, 0xac, 0x15, 0x53, 0x30, 0x0a, 0x0b, 0x12, 0x18, 0xbd, -0xf5, 0x83, 0xe0, 0x44, 0x08, 0xf0, 0x80, 0x09, 0x12, 0x18, 0xcb, 0xf5, 0x83, 0xe0, 0x44, 0x08, -0xf0, 0xe4, 0xff, 0x02, 0x2f, 0x6a, 0xe5, 0x2c, 0x30, 0xe7, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x35, -0x45, 0x34, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x90, 0xfa, 0xb5, 0xe0, 0x60, 0x03, 0x02, 0x0e, 0xac, -0x12, 0x18, 0xd9, 0x60, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x2c, 0x30, 0xe1, 0x03, 0x02, 0x0e, 0xac, -0x90, 0xfa, 0xba, 0xe0, 0x90, 0xff, 0xff, 0xf0, 0xe0, 0x60, 0x05, 0x43, 0x2c, 0x01, 0x80, 0x03, -0x53, 0x2c, 0xfe, 0xe4, 0xff, 0x02, 0x2f, 0x6a, 0xe5, 0x2c, 0x20, 0xe7, 0x03, 0x02, 0x0e, 0xac, -0xe5, 0x35, 0x45, 0x34, 0x70, 0x03, 0x02, 0x0e, 0xac, 0x12, 0x18, 0xd9, 0x60, 0x03, 0x02, 0x0e, -0xac, 0x90, 0xfa, 0xb9, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xec, 0x24, 0xfe, 0x60, 0x3a, 0x14, 0x60, -0x75, 0x24, 0x02, 0x60, 0x03, 0x02, 0x0e, 0xac, 0xed, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x12, 0x18, -0x49, 0x12, 0x19, 0x73, 0x7d, 0x03, 0x12, 0x0e, 0xf3, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x12, 0x0e, -0xb0, 0x90, 0xfa, 0xb2, 0xe0, 0xfd, 0xa3, 0x12, 0x18, 0x93, 0x12, 0x0f, 0x0f, 0x50, 0x02, 0x80, -0x04, 0xae, 0x34, 0xaf, 0x35, 0x02, 0x0f, 0x40, 0x12, 0x18, 0x49, 0x90, 0xf9, 0x65, 0xe0, 0x30, -0xe4, 0x0d, 0x12, 0x19, 0x73, 0x7d, 0x14, 0x12, 0x0e, 0xf3, 0x60, 0x10, 0x02, 0x0e, 0xac, 0x12, -0x19, 0x73, 0x7d, 0x04, 0x12, 0x0f, 0x47, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x12, 0x0e, 0xb0, 0x90, -0xfa, 0xb2, 0xe0, 0xfd, 0xa3, 0x12, 0x18, 0x93, 0x12, 0x0f, 0x0f, 0x50, 0x02, 0x80, 0x04, 0xae, -0x34, 0xaf, 0x35, 0x02, 0x0f, 0x40, 0x12, 0x19, 0x73, 0x7d, 0x05, 0x12, 0x0f, 0x47, 0x60, 0x03, -0x02, 0x0e, 0xac, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb2, 0x12, 0x18, 0x90, 0x7d, 0x01, 0x12, 0x23, -0xee, 0x90, 0xfa, 0xb3, 0xe4, 0x75, 0xf0, 0x03, 0x12, 0x14, 0x2f, 0x90, 0xfa, 0xba, 0xe0, 0x90, -0xfa, 0xb1, 0xf0, 0xe4, 0xf5, 0x52, 0x90, 0xfa, 0xb1, 0xe0, 0xff, 0xe5, 0x52, 0xc3, 0x9f, 0x50, -0x24, 0x12, 0x18, 0x8a, 0x12, 0x0f, 0x52, 0xff, 0xfd, 0x90, 0xfa, 0xb3, 0xe4, 0x8d, 0xf0, 0x12, -0x14, 0x2f, 0x90, 0xfa, 0xb2, 0xe0, 0xc3, 0x9f, 0xf0, 0xd3, 0x94, 0x00, 0x50, 0x03, 0x02, 0x0e, -0xac, 0x05, 0x52, 0x80, 0xd1, 0x12, 0x18, 0x8a, 0x12, 0x0f, 0x52, 0x24, 0xfe, 0xff, 0x90, 0xfa, -0xb2, 0xf0, 0xfd, 0xa3, 0xe4, 0x75, 0xf0, 0x02, 0x12, 0x14, 0x2f, 0x7a, 0xf9, 0x79, 0x6e, 0x7b, -0x01, 0x8b, 0x2d, 0x8a, 0x2e, 0x89, 0x2f, 0xe9, 0x24, 0x02, 0xf9, 0xe4, 0x3a, 0xfa, 0x12, 0x18, -0x90, 0x12, 0x23, 0xee, 0x8f, 0x52, 0x05, 0x52, 0x05, 0x52, 0x12, 0x18, 0x2a, 0xe5, 0x52, 0x12, -0x13, 0xfb, 0x12, 0x18, 0x2a, 0x90, 0x00, 0x01, 0x74, 0x03, 0x12, 0x14, 0x0d, 0xaf, 0x52, 0x7e, -0x00, 0xc3, 0xef, 0x95, 0x35, 0xee, 0x95, 0x34, 0x50, 0x02, 0x80, 0x04, 0xae, 0x34, 0xaf, 0x35, -0x8e, 0x30, 0x8f, 0x31, 0x02, 0x29, 0x2d, 0x02, 0x0e, 0xac, 0xe5, 0x2c, 0x20, 0xe7, 0x03, 0x02, -0x0e, 0xac, 0xe5, 0x35, 0x64, 0x01, 0x45, 0x34, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x90, 0xfa, 0xb5, -0xe0, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x90, 0xfa, 0xb9, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, -0x02, 0x0e, 0xac, 0x12, 0x18, 0xd9, 0x60, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x2c, 0x20, 0xe0, 0x06, -0x20, 0xe1, 0x03, 0x02, 0x0e, 0xac, 0x75, 0x2d, 0x00, 0x75, 0x2e, 0x00, 0x75, 0x2f, 0x29, 0x02, -0x0f, 0x2f, 0xe5, 0x2c, 0x30, 0xe7, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x35, 0x45, 0x34, 0x60, 0x03, -0x02, 0x0e, 0xac, 0x90, 0xfa, 0xb5, 0xe0, 0x60, 0x03, 0x02, 0x0e, 0xac, 0xd3, 0x90, 0xfa, 0xba, -0xe0, 0x94, 0x01, 0x90, 0xfa, 0xb9, 0xe0, 0x94, 0x00, 0x40, 0x03, 0x02, 0x0e, 0xac, 0x12, 0x18, -0xd9, 0x60, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x2c, 0x20, 0xe0, 0x06, 0x20, 0xe1, 0x03, 0x02, 0x0e, -0xac, 0x90, 0xfa, 0xba, 0xe0, 0xf5, 0x29, 0xe5, 0x29, 0x70, 0x08, 0x43, 0x2c, 0x01, 0x53, 0x2c, -0xfd, 0x80, 0x06, 0x53, 0x2c, 0xfe, 0x43, 0x2c, 0x02, 0xe4, 0xff, 0x02, 0x2f, 0x6a, 0xe5, 0x2c, -0x20, 0xe7, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x35, 0x64, 0x01, 0x45, 0x34, 0x60, 0x03, 0x02, 0x0e, -0xac, 0x90, 0xfa, 0xb5, 0xe0, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x90, 0xfa, 0xb9, 0xe0, 0x70, 0x02, -0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0e, 0xac, 0x12, 0x18, 0xd9, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0e, -0xac, 0xe5, 0x2c, 0x20, 0xe1, 0x03, 0x02, 0x0e, 0xac, 0x7f, 0x01, 0x02, 0x2f, 0x6a, 0xe5, 0x2c, -0x30, 0xe7, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x35, 0x45, 0x34, 0x60, 0x03, 0x02, 0x0e, 0xac, 0xd3, -0x90, 0xfa, 0xba, 0xe0, 0x94, 0x00, 0x90, 0xfa, 0xb9, 0xe0, 0x94, 0x00, 0x40, 0x03, 0x02, 0x0e, -0xac, 0x12, 0x18, 0xd9, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0e, 0xac, 0xe5, 0x2c, 0x20, 0xe1, 0x03, -0x02, 0x0e, 0xac, 0xe4, 0xff, 0x02, 0x2f, 0x6a, 0x90, 0xff, 0x01, 0x12, 0x19, 0x8a, 0xef, 0x12, -0x13, 0xfb, 0x90, 0xfa, 0xb5, 0x12, 0x19, 0x8a, 0x90, 0x00, 0x01, 0xef, 0x12, 0x14, 0x0d, 0x90, -0x00, 0x02, 0xe4, 0x12, 0x14, 0x0d, 0x74, 0x03, 0x12, 0x18, 0x1b, 0x90, 0xfa, 0xb9, 0xe0, 0xff, -0xa3, 0xe0, 0x85, 0x2f, 0x82, 0x85, 0x2e, 0x83, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x90, 0xff, 0x01, -0xe0, 0x12, 0x15, 0x0f, 0x09, 0x3d, 0x02, 0x09, 0x5f, 0x04, 0x09, 0x81, 0x05, 0x09, 0xad, 0x06, -0x09, 0xcb, 0x07, 0x09, 0xe9, 0x08, 0x0a, 0x07, 0x09, 0x0a, 0x25, 0x0b, 0x0a, 0xda, 0x80, 0x0c, -0xfa, 0x81, 0x0d, 0x1c, 0x82, 0x0b, 0x21, 0x83, 0x0b, 0x6a, 0x84, 0x0b, 0x89, 0x85, 0x0b, 0xc5, -0x86, 0x0c, 0x07, 0x87, 0x0c, 0x95, 0x88, 0x0c, 0xd0, 0x89, 0x0a, 0x43, 0x92, 0x0a, 0x43, 0x93, -0x0d, 0xcf, 0xc0, 0x0e, 0x00, 0xc1, 0x0e, 0x11, 0xc2, 0x00, 0x00, 0x0e, 0x9b, 0xe5, 0x2c, 0x20, -0xe7, 0x05, 0x7f, 0x05, 0x02, 0x2e, 0xa5, 0x12, 0x18, 0xe0, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, -0xfd, 0x7c, 0x00, 0x7f, 0x07, 0x02, 0x10, 0x9c, 0xe4, 0xfd, 0x7f, 0x07, 0x02, 0x2c, 0xc0, 0xe5, -0x2c, 0x20, 0xe7, 0x05, 0x7f, 0x05, 0x02, 0x2e, 0xa5, 0x12, 0x18, 0xe0, 0x60, 0x03, 0x04, 0x70, -0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0c, 0x02, 0x10, 0x9c, 0xe4, 0xfd, 0x7f, 0x07, 0x02, 0x2c, -0xc0, 0xe5, 0x2c, 0x30, 0xe7, 0x03, 0x02, 0x0e, 0xaf, 0x12, 0x19, 0x99, 0x50, 0x06, 0xe5, 0x35, -0x45, 0x34, 0x70, 0x05, 0x7f, 0x02, 0x02, 0x2e, 0xa5, 0x90, 0xfa, 0xb5, 0xe0, 0x24, 0xfe, 0x24, -0xfd, 0x50, 0x02, 0x80, 0x03, 0x02, 0x2f, 0x28, 0x7f, 0x07, 0x02, 0x2e, 0xa5, 0xe5, 0x2c, 0x30, -0xe7, 0x03, 0x02, 0x0e, 0xaf, 0x12, 0x18, 0xe0, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, -0x00, 0x7f, 0x08, 0x02, 0x10, 0x9c, 0x7f, 0x07, 0x02, 0x2e, 0xa5, 0xe5, 0x2c, 0x30, 0xe7, 0x03, -0x02, 0x0e, 0xaf, 0x12, 0x18, 0xe0, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, -0x09, 0x02, 0x10, 0x9c, 0x7f, 0x07, 0x02, 0x2e, 0xa5, 0xe5, 0x2c, 0x30, 0xe7, 0x03, 0x02, 0x0e, -0xaf, 0x12, 0x18, 0xe0, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0a, 0x02, -0x10, 0x9c, 0x7f, 0x07, 0x02, 0x2e, 0xa5, 0xe5, 0x2c, 0x30, 0xe7, 0x03, 0x02, 0x0e, 0xaf, 0x12, -0x18, 0xe0, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0b, 0x02, 0x10, 0x9c, -0x7f, 0x07, 0x02, 0x2e, 0xa5, 0xe5, 0x2c, 0x30, 0xe7, 0x03, 0x02, 0x0e, 0xaf, 0x12, 0x18, 0xe0, -0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0e, 0x02, 0x10, 0x9c, 0x7f, 0x07, -0x02, 0x2e, 0xa5, 0xe5, 0x2c, 0x30, 0xe7, 0x56, 0x12, 0x18, 0xd9, 0x70, 0x4a, 0x90, 0xff, 0x02, -0xe0, 0xf5, 0x52, 0xe5, 0x52, 0xb4, 0x82, 0x05, 0x75, 0x52, 0x61, 0x80, 0x12, 0xe5, 0x52, 0xb4, -0x83, 0x05, 0x75, 0x52, 0x62, 0x80, 0x08, 0xe5, 0x52, 0xc4, 0x54, 0xf0, 0x04, 0xf5, 0x52, 0x12, -0x17, 0x8b, 0x12, 0x19, 0x6c, 0x12, 0x22, 0xb8, 0x12, 0x18, 0xe8, 0x12, 0x13, 0xce, 0x60, 0x05, -0x12, 0x2f, 0x76, 0x80, 0x06, 0x85, 0x2a, 0x30, 0x85, 0x2b, 0x31, 0x75, 0x2d, 0x01, 0x75, 0x2e, -0xf9, 0x75, 0x2f, 0x71, 0x02, 0x29, 0x2d, 0xe4, 0xfd, 0x7f, 0x05, 0x02, 0x2c, 0xc0, 0x12, 0x18, -0xd9, 0x60, 0x05, 0x7f, 0x05, 0x02, 0x2e, 0xa5, 0x12, 0x19, 0x99, 0x40, 0x05, 0x7f, 0x03, 0x02, -0x2e, 0xa5, 0x90, 0xff, 0x02, 0xe0, 0xf5, 0x52, 0xe5, 0x52, 0xb4, 0x82, 0x05, 0x75, 0x52, 0x61, -0x80, 0x12, 0xe5, 0x52, 0xb4, 0x83, 0x05, 0x75, 0x52, 0x62, 0x80, 0x08, 0xe5, 0x52, 0xc4, 0x54, -0xf0, 0x04, 0xf5, 0x52, 0x12, 0x17, 0x8b, 0x02, 0x2f, 0x28, 0x12, 0x19, 0xa3, 0x12, 0x27, 0x19, -0x12, 0x18, 0x9b, 0xe0, 0x54, 0x7f, 0xf0, 0x00, 0x00, 0x00, 0xe0, 0x90, 0xfa, 0xb6, 0xf0, 0x78, -0x6e, 0x12, 0x14, 0xeb, 0x90, 0x00, 0x02, 0x12, 0x13, 0xce, 0x30, 0xe7, 0xf2, 0x90, 0x00, 0x02, -0xe4, 0x12, 0x14, 0x0d, 0x90, 0xfa, 0xb6, 0xe0, 0x44, 0x80, 0xff, 0xf0, 0x78, 0x82, 0xe6, 0xfc, -0x08, 0xe6, 0x8c, 0x83, 0x12, 0x18, 0xa3, 0xef, 0xf0, 0x12, 0x2f, 0x80, 0xe4, 0xff, 0x02, 0x2e, -0xa5, 0x90, 0xfa, 0xb5, 0xe0, 0x64, 0x01, 0x70, 0x1f, 0x90, 0xfa, 0xb9, 0xe0, 0xff, 0x7e, 0x00, -0x70, 0x06, 0xa3, 0xe0, 0xf5, 0x90, 0x80, 0x2d, 0xc2, 0xaf, 0xef, 0xf4, 0x52, 0x90, 0x90, 0xfa, -0xba, 0xe0, 0x42, 0x90, 0xd2, 0xaf, 0x80, 0x1d, 0x90, 0xfa, 0xb9, 0xe0, 0xff, 0x7e, 0x00, 0x70, -0x06, 0xa3, 0xe0, 0xf5, 0xb0, 0x80, 0x0e, 0xc2, 0xaf, 0xef, 0xf4, 0x52, 0xb0, 0x90, 0xfa, 0xba, -0xe0, 0x42, 0xb0, 0xd2, 0xaf, 0xe4, 0xff, 0x02, 0x2e, 0xa5, 0x12, 0x18, 0x49, 0x90, 0xfa, 0xb5, -0xe0, 0xb4, 0x01, 0x0a, 0x12, 0x18, 0x2a, 0xe5, 0x90, 0x12, 0x13, 0xfb, 0x80, 0x08, 0x12, 0x18, -0x2a, 0xe5, 0xb0, 0x12, 0x13, 0xfb, 0x02, 0x0f, 0x2f, 0x90, 0xf9, 0x65, 0xe0, 0x20, 0xe1, 0x30, -0x12, 0x18, 0x53, 0x60, 0x18, 0x04, 0x70, 0x28, 0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x09, 0x90, 0xff, -0xa4, 0xe0, 0x44, 0x10, 0xf0, 0x80, 0x19, 0x12, 0x19, 0xad, 0xf0, 0x80, 0x13, 0x90, 0xfa, 0xb6, -0xe0, 0x60, 0x09, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x10, 0xf0, 0x80, 0x04, 0x12, 0x19, 0xb4, 0xf0, -0xe4, 0xff, 0x02, 0x2e, 0xa5, 0x90, 0xf9, 0x65, 0xe0, 0x20, 0xe1, 0x36, 0x12, 0x18, 0x53, 0x60, -0x1b, 0x04, 0x70, 0x2e, 0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x20, -0xf0, 0x80, 0x1f, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xdf, 0xf0, 0x80, 0x16, 0x90, 0xfa, 0xb6, 0xe0, -0x60, 0x09, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x07, 0x90, 0xff, 0xb4, 0xe0, 0x54, -0xdf, 0xf0, 0xe4, 0xff, 0x02, 0x2e, 0xa5, 0x12, 0x18, 0x53, 0x60, 0x46, 0x04, 0x60, 0x03, 0x02, -0x0c, 0x90, 0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x17, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x04, 0xf0, 0x90, -0xf9, 0x65, 0xe0, 0x30, 0xe1, 0x6a, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x61, 0x90, -0xff, 0xa4, 0xe0, 0x54, 0xfb, 0xf0, 0x90, 0xf9, 0x65, 0xe0, 0x30, 0xe1, 0x53, 0x30, 0x95, 0x09, -0x90, 0xff, 0xa4, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x47, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xfd, 0xf0, -0x80, 0x3e, 0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x17, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x04, 0xf0, 0x90, -0xf9, 0x65, 0xe0, 0x30, 0xe1, 0x2a, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x21, 0x90, -0xff, 0xb4, 0xe0, 0x54, 0xfb, 0xf0, 0x90, 0xf9, 0x65, 0xe0, 0x30, 0xe1, 0x13, 0x30, 0x93, 0x09, -0x90, 0xff, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x07, 0x90, 0xff, 0xb4, 0xe0, 0x54, 0xfd, 0xf0, -0xe4, 0xff, 0x02, 0x2e, 0xa5, 0x12, 0x18, 0x53, 0x60, 0x1b, 0x04, 0x70, 0x2e, 0x90, 0xfa, 0xb6, -0xe0, 0x60, 0x09, 0x90, 0xff, 0xa2, 0xe0, 0x44, 0x40, 0xf0, 0x80, 0x1f, 0x90, 0xff, 0xa2, 0xe0, -0x54, 0xbf, 0xf0, 0x80, 0x16, 0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x09, 0x90, 0xff, 0xb2, 0xe0, 0x44, -0x40, 0xf0, 0x80, 0x07, 0x90, 0xff, 0xb2, 0xe0, 0x54, 0xbf, 0xf0, 0xe4, 0xff, 0x02, 0x2e, 0xa5, -0x12, 0x18, 0x49, 0x12, 0x18, 0x5b, 0x60, 0x0f, 0x04, 0x70, 0x16, 0x90, 0xff, 0xa4, 0xe0, 0x12, -0x18, 0x2a, 0x12, 0x13, 0xfb, 0x80, 0x0a, 0x90, 0xff, 0xb4, 0xe0, 0x12, 0x18, 0x2a, 0x12, 0x13, -0xfb, 0x75, 0x30, 0x00, 0x75, 0x31, 0x01, 0x02, 0x29, 0x2d, 0xe4, 0xff, 0x12, 0x2e, 0xa5, 0x12, -0x19, 0x46, 0x7f, 0x03, 0x12, 0x11, 0x9f, 0x90, 0xff, 0xfc, 0xe0, 0x54, 0x7f, 0xf0, 0x7f, 0xff, -0x7e, 0x00, 0x12, 0x2d, 0xee, 0xc2, 0x90, 0xc2, 0xaf, 0x00, 0x80, 0xfd, 0xe4, 0xf5, 0x54, 0xf5, -0x55, 0x90, 0xfa, 0xbb, 0x74, 0x3e, 0xf0, 0xa3, 0xe4, 0xf0, 0x90, 0xfa, 0xb3, 0xf0, 0xa3, 0x74, -0x15, 0xf0, 0xe0, 0x54, 0x3f, 0xff, 0xc3, 0x74, 0x40, 0x9f, 0x90, 0xfa, 0xb8, 0xf0, 0xd3, 0x94, -0x00, 0xe4, 0x94, 0x3e, 0x40, 0x08, 0x90, 0xfa, 0xbc, 0xe0, 0x90, 0xfa, 0xb8, 0xf0, 0x12, 0x0e, -0xd6, 0xe5, 0x23, 0x45, 0x22, 0x70, 0x73, 0x12, 0x18, 0x62, 0x90, 0xfa, 0xbb, 0x12, 0x19, 0x65, -0x60, 0x27, 0xd3, 0xef, 0x94, 0x40, 0xee, 0x94, 0x00, 0x40, 0x08, 0x90, 0xfa, 0xb8, 0x74, 0x40, -0xf0, 0x80, 0x08, 0x90, 0xfa, 0xbc, 0xe0, 0x90, 0xfa, 0xb8, 0xf0, 0x12, 0x0e, 0xd6, 0xe5, 0x23, -0x45, 0x22, 0x70, 0x46, 0x12, 0x18, 0x62, 0x80, 0xd1, 0x75, 0x52, 0x02, 0x90, 0xfa, 0xbb, 0xe4, -0xf0, 0xa3, 0x04, 0xf0, 0x90, 0xfa, 0xb3, 0xe4, 0xf0, 0xa3, 0x74, 0x0f, 0xf0, 0x7b, 0x00, 0x7a, -0x00, 0x79, 0x52, 0x90, 0xfa, 0xbc, 0xe0, 0xf5, 0x50, 0x7d, 0x0f, 0x7c, 0x00, 0x12, 0x26, 0x25, -0x75, 0x22, 0x00, 0x8f, 0x23, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x52, 0xe4, 0xf5, 0x40, 0xf5, 0x41, -0x7d, 0x01, 0x12, 0x23, 0xee, 0xe4, 0xf5, 0x22, 0xf5, 0x23, 0xaf, 0x23, 0x02, 0x2e, 0xa5, 0x90, -0xfa, 0xba, 0xe0, 0x90, 0xfa, 0xb6, 0xf0, 0x30, 0xe7, 0x10, 0xe0, 0x54, 0x0f, 0x90, 0xf9, 0x62, -0xf0, 0xd3, 0x94, 0x00, 0x40, 0x15, 0xc2, 0x95, 0x80, 0x11, 0x90, 0xfa, 0xb6, 0xe0, 0x54, 0x0f, -0x90, 0xf9, 0x61, 0xf0, 0xd3, 0x94, 0x00, 0x40, 0x02, 0xc2, 0x94, 0xe4, 0xff, 0x02, 0x2e, 0xa5, -0x12, 0x19, 0xa3, 0xbf, 0x01, 0x04, 0xd2, 0x93, 0x80, 0x02, 0xc2, 0x93, 0xe4, 0xff, 0x02, 0x2e, -0xa5, 0x90, 0xfa, 0xba, 0xe0, 0x90, 0xfa, 0xb6, 0xf0, 0x54, 0x03, 0x14, 0x60, 0x0a, 0x14, 0x60, -0x0f, 0x14, 0x60, 0x08, 0x24, 0x03, 0x70, 0x2b, 0xd2, 0x91, 0x80, 0x27, 0xc2, 0x91, 0x80, 0x23, -0x12, 0x19, 0xad, 0x12, 0x0e, 0xfe, 0x60, 0x04, 0xd2, 0x91, 0x80, 0x17, 0x90, 0xff, 0xa4, 0xe0, -0x44, 0x10, 0x12, 0x0e, 0xfe, 0xff, 0xbf, 0xa0, 0x04, 0xc2, 0x91, 0x80, 0x02, 0xd2, 0x91, 0x12, -0x19, 0xad, 0xf0, 0x90, 0xfa, 0xb6, 0xe0, 0x54, 0x0c, 0xff, 0x13, 0x13, 0x54, 0x3f, 0x14, 0x60, -0x0a, 0x14, 0x60, 0x0f, 0x14, 0x60, 0x08, 0x24, 0x03, 0x70, 0x2b, 0xd2, 0x92, 0x80, 0x27, 0xc2, -0x92, 0x80, 0x23, 0x12, 0x19, 0xb4, 0x12, 0x0f, 0x1e, 0x60, 0x04, 0xd2, 0x92, 0x80, 0x17, 0x90, -0xff, 0xb4, 0xe0, 0x44, 0x10, 0x12, 0x0f, 0x1e, 0xff, 0xbf, 0xa0, 0x04, 0xc2, 0x92, 0x80, 0x02, -0xd2, 0x92, 0x12, 0x19, 0xb4, 0xf0, 0xe4, 0xff, 0x02, 0x2e, 0xa5, 0xe5, 0x2c, 0x30, 0xe7, 0x07, -0xe4, 0xfd, 0x7f, 0x05, 0x02, 0x2c, 0xc0, 0x7f, 0x05, 0x02, 0x2e, 0xa5, 0x12, 0x2f, 0x76, 0x22, -0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb2, 0x90, 0xfa, 0xb3, 0xe0, 0xf5, 0x40, 0xa3, 0xe0, 0xf5, 0x41, -0x7d, 0x01, 0x12, 0x23, 0xee, 0x90, 0xfa, 0xb3, 0xe4, 0x75, 0xf0, 0x03, 0x12, 0x14, 0x2f, 0xab, -0x2d, 0xaa, 0x2e, 0xa9, 0x2f, 0x22, 0xaa, 0x54, 0xa9, 0x55, 0x7b, 0xff, 0x90, 0xfa, 0xb3, 0xe0, -0xfc, 0xa3, 0xe0, 0xfd, 0x90, 0xfa, 0xb8, 0xe0, 0xf5, 0x50, 0x12, 0x26, 0x25, 0x75, 0x22, 0x00, -0x8f, 0x23, 0x22, 0x12, 0x20, 0xc5, 0x7e, 0x00, 0x8e, 0x22, 0x8f, 0x23, 0xef, 0x22, 0xf0, 0x7f, -0x01, 0x12, 0x11, 0x9f, 0x90, 0xff, 0xa6, 0xe0, 0x90, 0xfa, 0xb7, 0xf0, 0x54, 0xa0, 0x22, 0x12, -0x23, 0xee, 0x8f, 0x52, 0x7e, 0x00, 0xc3, 0xef, 0x95, 0x35, 0xee, 0x95, 0x34, 0x22, 0xf0, 0x7f, -0x01, 0x12, 0x11, 0x9f, 0x90, 0xff, 0xb6, 0xe0, 0x90, 0xfa, 0xb7, 0xf0, 0x54, 0xa0, 0x22, 0x75, -0x30, 0x00, 0x75, 0x31, 0x01, 0x02, 0x29, 0x2d, 0x90, 0xfa, 0xb5, 0xe0, 0xff, 0x02, 0x2f, 0x3b, -0x8e, 0x30, 0x8f, 0x31, 0x02, 0x29, 0x2d, 0x12, 0x20, 0xc5, 0x7e, 0x00, 0x8e, 0x22, 0x8f, 0x23, -0xef, 0x22, 0x7d, 0x01, 0x12, 0x23, 0xee, 0x90, 0xfa, 0xb0, 0xe0, 0x22, 0xef, 0x90, 0xf8, 0x04, -0xf0, 0x22, 0xc0, 0xa8, 0xc2, 0xaf, 0xee, 0x60, 0x0a, 0xc0, 0x05, 0x7d, 0x7f, 0xdd, 0xfe, 0xde, -0xfa, 0xd0, 0x05, 0xef, 0xc3, 0x94, 0x15, 0x50, 0x03, 0xd0, 0xa8, 0x22, 0x13, 0x70, 0x03, 0xd0, -0xa8, 0x22, 0xff, 0xd5, 0x07, 0xfd, 0xd0, 0xa8, 0x22, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, -0x04, 0xc0, 0x05, 0xe5, 0x44, 0x24, 0x08, 0xf8, 0x86, 0x05, 0x53, 0x05, 0x7f, 0x7c, 0xff, 0x12, -0x0f, 0xfe, 0x7f, 0x00, 0x7e, 0x00, 0xe5, 0x49, 0x60, 0x46, 0xfc, 0x90, 0xf9, 0x19, 0xe0, 0x54, -0x7f, 0x6d, 0x70, 0x0f, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0xa3, 0x15, -0x49, 0x80, 0x07, 0xa3, 0xa3, 0xa3, 0xdc, 0xe6, 0x80, 0x26, 0xdc, 0x06, 0xd0, 0x82, 0xd0, 0x83, -0x80, 0x1e, 0xe0, 0xf8, 0xa3, 0xe0, 0xf9, 0xa3, 0xe0, 0xfa, 0xd0, 0x82, 0xd0, 0x83, 0xe8, 0xf0, -0xa3, 0xe9, 0xf0, 0xa3, 0xea, 0xf0, 0xa3, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0x80, 0xda, -0x12, 0x10, 0x95, 0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0x22, 0x85, 0xa8, -0x4a, 0x75, 0xa8, 0x88, 0xec, 0x70, 0x02, 0x7c, 0x3f, 0x8c, 0x43, 0x22, 0xe5, 0x44, 0x24, 0x08, -0xf8, 0x76, 0x00, 0x12, 0x10, 0xec, 0x80, 0xfb, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x04, -0xc0, 0x06, 0x7c, 0xff, 0x12, 0x0f, 0xfe, 0xe5, 0x49, 0x60, 0x42, 0xfe, 0x90, 0xf9, 0x19, 0xe0, -0x54, 0x7f, 0x6f, 0x70, 0x0b, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0x15, 0x49, 0x80, 0x07, -0xa3, 0xa3, 0xa3, 0xde, 0xea, 0x80, 0x26, 0xde, 0x06, 0xd0, 0x82, 0xd0, 0x83, 0x80, 0xd8, 0xe0, -0xf8, 0xa3, 0xe0, 0xf9, 0xa3, 0xe0, 0xfa, 0xd0, 0x82, 0xd0, 0x83, 0xe8, 0xf0, 0xa3, 0xe9, 0xf0, -0xa3, 0xea, 0xf0, 0xa3, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0x80, 0xda, 0x78, 0x08, 0x08, -0x79, 0x18, 0x09, 0x7c, 0x01, 0xe6, 0x54, 0x7f, 0x6f, 0x70, 0x06, 0x76, 0x00, 0x77, 0x00, 0x80, -0x06, 0x08, 0x09, 0x0c, 0xbc, 0x08, 0xee, 0x12, 0x10, 0x95, 0xd0, 0x06, 0xd0, 0x04, 0xd0, 0x02, -0xd0, 0x01, 0xd0, 0x00, 0x22, 0x75, 0x43, 0x00, 0x85, 0x4a, 0xa8, 0x22, 0xc0, 0xf0, 0xc0, 0x82, -0xc0, 0x83, 0xc3, 0xe5, 0x49, 0x24, 0xe8, 0x50, 0x05, 0x12, 0x10, 0xec, 0x80, 0xf4, 0xef, 0x60, -0x31, 0x90, 0x2e, 0x2c, 0xe4, 0x93, 0xc3, 0x9f, 0x40, 0x2f, 0xc0, 0x04, 0x7c, 0xff, 0x12, 0x0f, -0xfe, 0xd0, 0x04, 0x43, 0x07, 0x80, 0xe5, 0x49, 0x75, 0xf0, 0x03, 0xa4, 0x24, 0x19, 0xf5, 0x82, -0xe4, 0x34, 0xf9, 0xf5, 0x83, 0xef, 0xf0, 0xec, 0xa3, 0xf0, 0xed, 0xa3, 0xf0, 0x05, 0x49, 0x12, -0x10, 0x95, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0xf0, 0x22, 0x02, 0x11, 0x1a, 0xc0, 0x04, 0x7c, 0x20, -0xd2, 0x8c, 0xd2, 0x8d, 0xd5, 0x04, 0xfd, 0xd0, 0x04, 0x22, 0x75, 0xa8, 0x00, 0x75, 0x88, 0x00, -0x75, 0xb8, 0x00, 0x75, 0xf0, 0x00, 0x75, 0xd0, 0x00, 0xe4, 0xf8, 0x90, 0xf8, 0x04, 0xf0, 0x90, -0x00, 0x00, 0xf6, 0x08, 0xb8, 0x00, 0xfb, 0x02, 0x00, 0x00, 0xc2, 0xaf, 0xe4, 0x90, 0xff, 0x48, -0xf0, 0x90, 0xff, 0x50, 0xf0, 0x90, 0xff, 0x08, 0xf0, 0x90, 0xff, 0x10, 0xf0, 0x90, 0xff, 0x80, -0xf0, 0xa3, 0xa3, 0xf0, 0xd2, 0xb1, 0xc2, 0xb0, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0x62, 0x7e, -0xff, 0x7f, 0xff, 0x12, 0x0f, 0x62, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0x62, 0xd2, 0xb0, 0xd2, -0xb1, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0x62, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0x62, 0x7e, -0xff, 0x7f, 0xff, 0x12, 0x0f, 0x62, 0x80, 0xcc, 0xc3, 0xee, 0x94, 0x02, 0x50, 0x04, 0x7e, 0x03, -0x7f, 0xe8, 0xef, 0xf4, 0xff, 0xee, 0xf4, 0xfe, 0x0f, 0xbf, 0x00, 0x01, 0x0e, 0x8f, 0x48, 0x8e, -0x47, 0x22, 0xc3, 0xef, 0x94, 0xbc, 0xee, 0x94, 0x02, 0x50, 0x04, 0x7e, 0x07, 0x7f, 0xd0, 0xef, -0xf4, 0xff, 0xee, 0xf4, 0xfe, 0x0f, 0xbf, 0x00, 0x01, 0x0e, 0x8f, 0x46, 0x8e, 0x45, 0x22, 0xef, -0x70, 0x01, 0x22, 0xc0, 0x00, 0xe5, 0x44, 0x24, 0x18, 0xf8, 0xa6, 0x07, 0xe5, 0x44, 0x24, 0x08, -0xf8, 0xc6, 0x54, 0x7f, 0xf6, 0xe6, 0x30, 0xe7, 0x03, 0xd0, 0x00, 0x22, 0x12, 0x10, 0xec, 0x80, +0x90, 0xff, 0x56, 0xe0, 0x30, 0xe7, 0x25, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x02, 0xf0, 0x80, 0x25, +0xb4, 0x02, 0x22, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x7a, 0xe0, 0x30, 0xe7, 0x05, 0x12, 0x27, +0x8d, 0x80, 0x09, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x03, 0xf0, 0x80, 0x09, 0xd0, 0x83, 0xd0, 0x82, +0x80, 0x03, 0x02, 0x02, 0x62, 0x74, 0x15, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xe0, 0x20, 0x04, +0xf1, 0x20, 0x02, 0x03, 0x30, 0x01, 0xeb, 0x74, 0x18, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xe0, +0x14, 0xfc, 0xf0, 0xa3, 0xe0, 0xfd, 0xa3, 0xe0, 0xfe, 0x64, 0x04, 0x70, 0x0f, 0xec, 0x70, 0x62, +0x7e, 0x01, 0x12, 0x00, 0xc9, 0x7c, 0x0a, 0x7d, 0xfa, 0x02, 0x02, 0x33, 0x12, 0x00, 0xc9, 0xee, +0x64, 0x04, 0x60, 0x1d, 0xec, 0x70, 0x4b, 0x7c, 0x0a, 0xed, 0x14, 0xfd, 0x70, 0x15, 0xee, 0x64, +0x02, 0x60, 0x07, 0x7e, 0x02, 0x7d, 0x32, 0x02, 0x02, 0x33, 0x7e, 0x01, 0x7d, 0xfa, 0x02, 0x02, +0x33, 0x7c, 0x0a, 0x74, 0x18, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xec, 0xf0, 0xa3, 0xed, 0xf0, +0xa3, 0xee, 0xf0, 0x14, 0x60, 0x18, 0x20, 0xe1, 0x0f, 0x20, 0x01, 0x06, 0xd2, 0xb1, 0xc2, 0xb0, +0x80, 0x10, 0xc2, 0xb1, 0xd2, 0xb0, 0x80, 0x0a, 0xc2, 0xb1, 0xc2, 0xb0, 0x80, 0x04, 0xd2, 0xb0, +0xd2, 0xb1, 0x78, 0x19, 0x79, 0x09, 0x7a, 0x07, 0xe7, 0x70, 0x04, 0xa6, 0x00, 0x80, 0x0b, 0xe6, +0x60, 0x08, 0x16, 0xe6, 0x70, 0x04, 0xe7, 0x44, 0x80, 0xf7, 0x08, 0x09, 0xda, 0xea, 0xe5, 0x3d, +0x60, 0x13, 0x14, 0xf5, 0x3d, 0x70, 0x0e, 0xe5, 0x3e, 0x24, 0x08, 0xf8, 0x76, 0x00, 0x12, 0x11, +0x0f, 0xd2, 0x8c, 0xd2, 0x8d, 0xd0, 0x07, 0xd0, 0x06, 0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x03, 0xd0, +0x02, 0xd0, 0x01, 0xd0, 0x00, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0xf0, 0xd0, 0xd0, 0xd0, 0xe0, 0x32, +0x90, 0xff, 0x04, 0xe0, 0x90, 0xfa, 0xb6, 0xf0, 0x90, 0xff, 0x06, 0xe0, 0xfc, 0xa3, 0xe0, 0xfa, +0xec, 0xff, 0xea, 0xfe, 0xef, 0xc3, 0x94, 0x08, 0xee, 0x94, 0x01, 0x50, 0x02, 0x80, 0x04, 0x7e, +0x01, 0x7f, 0x08, 0x8e, 0x3b, 0x8f, 0x3c, 0x90, 0xff, 0x02, 0xe0, 0xfc, 0xa3, 0xe0, 0xfa, 0xec, +0xff, 0xea, 0x90, 0xfa, 0xba, 0xf0, 0xef, 0xa3, 0xf0, 0x12, 0x1c, 0x30, 0xe4, 0xf5, 0x4d, 0xe5, +0x4d, 0xc3, 0x94, 0x02, 0x50, 0x0f, 0x12, 0x1c, 0x11, 0xe4, 0x12, 0x1a, 0x38, 0x05, 0x4d, 0x04, +0x12, 0x1c, 0x02, 0x80, 0xea, 0x12, 0x1c, 0x30, 0x90, 0xff, 0x00, 0xe0, 0xff, 0x54, 0x60, 0x24, +0xc0, 0x70, 0x03, 0x02, 0x08, 0xc5, 0x24, 0x40, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, +0xe0, 0xfe, 0x54, 0x0f, 0xf5, 0x4d, 0xee, 0x30, 0xe7, 0x03, 0xd3, 0x80, 0x01, 0xc3, 0x92, 0x0a, +0x90, 0xff, 0x01, 0xe0, 0x12, 0x1b, 0x4c, 0x03, 0x56, 0x00, 0x04, 0x29, 0x01, 0x05, 0x3c, 0x03, +0x06, 0x03, 0x05, 0x06, 0x45, 0x06, 0x07, 0xa7, 0x08, 0x07, 0xef, 0x09, 0x08, 0x4b, 0x0a, 0x08, +0x8b, 0x0b, 0x00, 0x00, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe7, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, +0xba, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x64, 0x02, 0x45, +0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xef, 0x54, 0x1f, 0x14, 0x60, 0x2b, 0x14, 0x60, 0x47, 0x24, +0x02, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xee, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0x11, 0x74, +0x01, 0x12, 0x1a, 0x38, 0x78, 0x67, 0xe6, 0x30, 0xe0, 0x08, 0x12, 0x1c, 0x11, 0x74, 0x02, 0x12, +0x1a, 0x38, 0x7f, 0x02, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x20, 0xe1, 0x09, 0x90, 0xfa, 0xb6, 0xe0, +0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, 0xd3, 0x94, 0x01, 0x40, 0x03, 0x02, 0x0f, +0x26, 0x7f, 0x02, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x20, 0xe1, 0x0e, 0x90, 0xfa, 0xb6, 0xe0, 0xff, +0x60, 0x07, 0x64, 0x80, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x0f, 0xb2, 0x40, 0x03, 0x02, 0x0f, +0x26, 0xe5, 0x4d, 0x70, 0x19, 0x30, 0x0a, 0x0b, 0x90, 0xff, 0x80, 0x12, 0x1c, 0x0e, 0x12, 0x1a, +0x38, 0x80, 0x24, 0x90, 0xff, 0x82, 0x12, 0x1c, 0x0e, 0x12, 0x1a, 0x38, 0x80, 0x19, 0x15, 0x4d, +0x30, 0x0a, 0x0b, 0x12, 0x1c, 0xa5, 0x12, 0x1c, 0x0c, 0x12, 0x1a, 0x38, 0x80, 0x09, 0x12, 0x1c, +0xb3, 0x12, 0x1c, 0x0c, 0x12, 0x1a, 0x38, 0x12, 0x1c, 0x11, 0x12, 0x19, 0xf2, 0x60, 0x05, 0x74, +0x01, 0x12, 0x1a, 0x38, 0x7f, 0x02, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, +0x26, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, 0x14, 0x60, 0x2d, +0x14, 0x60, 0x59, 0x24, 0x02, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xba, 0xe0, 0x70, 0x04, +0xa3, 0xe0, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x03, 0x02, +0x0f, 0x26, 0x78, 0x67, 0xe6, 0x54, 0xfe, 0xf6, 0xe4, 0xff, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x20, +0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe0, 0x09, 0x90, 0xfa, 0xb6, +0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe1, 0x0c, 0x90, 0xfa, 0xb6, 0xe0, 0xd3, +0x94, 0x01, 0x40, 0x03, 0x02, 0x0f, 0x26, 0xe4, 0xff, 0x02, 0x31, 0xb1, 0x90, 0xfa, 0xba, 0xe0, +0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x0f, 0xb2, 0x40, 0x03, 0x02, 0x0f, +0x26, 0xe5, 0x35, 0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe0, +0x07, 0xe5, 0x4d, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x4d, 0x70, 0x0f, 0x90, 0xff, 0x82, 0xe0, +0x54, 0xf7, 0xf0, 0x90, 0xff, 0x80, 0xe0, 0x54, 0xf7, 0xf0, 0x22, 0xe5, 0x4d, 0x24, 0xfe, 0x60, +0x20, 0x24, 0xfb, 0x60, 0x34, 0x24, 0x06, 0x70, 0x35, 0x30, 0x0a, 0x0c, 0xa2, 0x0a, 0xe4, 0x33, +0xfd, 0x7f, 0x03, 0x12, 0x2d, 0xa8, 0x80, 0x26, 0xe4, 0xfd, 0x7f, 0x03, 0x12, 0x2d, 0xa8, 0x80, +0x1d, 0x30, 0x0a, 0x0c, 0xa2, 0x0a, 0xe4, 0x33, 0xfd, 0x7f, 0x04, 0x12, 0x2d, 0xa8, 0x80, 0x0e, +0xe4, 0xfd, 0x7f, 0x04, 0x12, 0x2d, 0xa8, 0x80, 0x05, 0x7f, 0x87, 0x12, 0x31, 0x32, 0x15, 0x4d, +0x30, 0x0a, 0x0b, 0x12, 0x1c, 0xa5, 0xf5, 0x83, 0xe0, 0x54, 0xf7, 0xf0, 0x80, 0x09, 0x12, 0x1c, +0xb3, 0xf5, 0x83, 0xe0, 0x54, 0xf7, 0xf0, 0xe4, 0xff, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x30, 0xe7, +0x03, 0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, +0x14, 0x60, 0x2d, 0x14, 0x60, 0x55, 0x24, 0x02, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xba, +0xe0, 0x70, 0x04, 0xa3, 0xe0, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, +0x60, 0x03, 0x02, 0x0f, 0x26, 0x78, 0x67, 0xe6, 0x44, 0x01, 0xf6, 0xe4, 0xff, 0x02, 0x31, 0xb1, +0xe5, 0x35, 0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe0, 0x07, +0xe5, 0x4d, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe1, 0x0a, 0xe5, 0x4d, 0xd3, 0x94, +0x01, 0x40, 0x03, 0x02, 0x0f, 0x26, 0xe4, 0xff, 0x02, 0x31, 0xb1, 0x90, 0xfa, 0xba, 0xe0, 0x70, +0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, 0xff, 0x12, 0x31, 0x82, +0x40, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0f, 0x26, +0xe5, 0x4d, 0x70, 0x09, 0x30, 0x0a, 0x03, 0x02, 0x1d, 0x64, 0x02, 0x1d, 0x2f, 0xe5, 0x35, 0x20, +0xe1, 0x03, 0x02, 0x0f, 0x26, 0x15, 0x4d, 0x30, 0x0a, 0x0b, 0x12, 0x1c, 0xa5, 0xf5, 0x83, 0xe0, +0x44, 0x08, 0xf0, 0x80, 0x09, 0x12, 0x1c, 0xb3, 0xf5, 0x83, 0xe0, 0x44, 0x08, 0xf0, 0xe4, 0xff, +0x02, 0x31, 0xb1, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x45, 0x3b, 0x60, +0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, +0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe1, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xbb, +0xe0, 0x90, 0xff, 0xff, 0xf0, 0xe0, 0x60, 0x05, 0x43, 0x35, 0x01, 0x80, 0x03, 0x53, 0x35, 0xfe, +0xe4, 0xff, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x20, 0xe7, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x45, +0x3b, 0x70, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, +0xba, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xec, 0x24, 0xfe, 0x60, 0x3a, 0x14, 0x60, 0x75, 0x24, 0x02, +0x60, 0x03, 0x02, 0x0f, 0x26, 0xed, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0x30, 0x12, 0x1d, +0x5d, 0x7d, 0x03, 0x12, 0x0f, 0x6d, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x0f, 0x2a, 0x90, 0xfa, +0xb3, 0xe0, 0xfd, 0xa3, 0x12, 0x1c, 0x7b, 0x12, 0x0f, 0x89, 0x50, 0x02, 0x80, 0x04, 0xae, 0x3b, +0xaf, 0x3c, 0x02, 0x0f, 0xba, 0x12, 0x1c, 0x30, 0x90, 0xf9, 0x15, 0xe0, 0x30, 0xe4, 0x0d, 0x12, +0x1d, 0x5d, 0x7d, 0x14, 0x12, 0x0f, 0x6d, 0x60, 0x10, 0x02, 0x0f, 0x26, 0x12, 0x1d, 0x5d, 0x7d, +0x04, 0x12, 0x0f, 0xc1, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x0f, 0x2a, 0x90, 0xfa, 0xb3, 0xe0, +0xfd, 0xa3, 0x12, 0x1c, 0x7b, 0x12, 0x0f, 0x89, 0x50, 0x02, 0x80, 0x04, 0xae, 0x3b, 0xaf, 0x3c, +0x02, 0x0f, 0xba, 0x12, 0x1d, 0x5d, 0x7d, 0x05, 0x12, 0x0f, 0xc1, 0x60, 0x03, 0x02, 0x0f, 0x26, +0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb3, 0x12, 0x1c, 0x78, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x90, 0xfa, +0xb4, 0xe4, 0x75, 0xf0, 0x03, 0x12, 0x1a, 0x6c, 0x90, 0xfa, 0xbb, 0xe0, 0x90, 0xfa, 0xb2, 0xf0, +0xe4, 0xf5, 0x4c, 0x90, 0xfa, 0xb2, 0xe0, 0xff, 0xe5, 0x4c, 0xc3, 0x9f, 0x50, 0x24, 0x12, 0x1c, +0x72, 0x12, 0x0f, 0xcc, 0xff, 0xfd, 0x90, 0xfa, 0xb4, 0xe4, 0x8d, 0xf0, 0x12, 0x1a, 0x6c, 0x90, +0xfa, 0xb3, 0xe0, 0xc3, 0x9f, 0xf0, 0xd3, 0x94, 0x00, 0x50, 0x03, 0x02, 0x0f, 0x26, 0x05, 0x4c, +0x80, 0xd1, 0x12, 0x1c, 0x72, 0x12, 0x0f, 0xcc, 0x24, 0xfe, 0xff, 0x90, 0xfa, 0xb3, 0xf0, 0xfd, +0xa3, 0xe4, 0x75, 0xf0, 0x02, 0x12, 0x1a, 0x6c, 0x7a, 0xf9, 0x79, 0x6f, 0x7b, 0x01, 0x8b, 0x36, +0x8a, 0x37, 0x89, 0x38, 0xe9, 0x24, 0x02, 0xf9, 0xe4, 0x3a, 0xfa, 0x12, 0x1c, 0x78, 0x12, 0x25, +0xd7, 0x8f, 0x4c, 0x05, 0x4c, 0x05, 0x4c, 0x12, 0x1c, 0x11, 0xe5, 0x4c, 0x12, 0x1a, 0x38, 0x12, +0x1c, 0x11, 0x90, 0x00, 0x01, 0x74, 0x03, 0x12, 0x1a, 0x4a, 0xaf, 0x4c, 0x7e, 0x00, 0xc3, 0xef, +0x95, 0x3c, 0xee, 0x95, 0x3b, 0x50, 0x02, 0x80, 0x04, 0xae, 0x3b, 0xaf, 0x3c, 0x8e, 0x39, 0x8f, +0x3a, 0x02, 0x2c, 0x07, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe7, 0x03, 0x02, 0x0f, 0x26, 0xe5, +0x3c, 0x64, 0x01, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x03, +0x02, 0x0f, 0x26, 0x90, 0xfa, 0xba, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, +0x12, 0x1c, 0xc9, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe0, 0x06, 0x20, 0xe1, 0x03, +0x02, 0x0f, 0x26, 0x75, 0x36, 0x00, 0x75, 0x37, 0x00, 0x75, 0x38, 0x32, 0x02, 0x0f, 0xa9, 0xe5, +0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, +0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xd3, 0x90, 0xfa, 0xbb, 0xe0, 0x94, 0x01, +0x90, 0xfa, 0xba, 0xe0, 0x94, 0x00, 0x40, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, 0x60, 0x03, +0x02, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe0, 0x06, 0x20, 0xe1, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, +0xbb, 0xe0, 0xf5, 0x32, 0xe5, 0x32, 0x70, 0x08, 0x43, 0x35, 0x01, 0x53, 0x35, 0xfd, 0x80, 0x06, +0x53, 0x35, 0xfe, 0x43, 0x35, 0x02, 0xe4, 0xff, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x20, 0xe7, 0x03, +0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x64, 0x01, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, +0xb6, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xba, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, +0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, +0x20, 0xe1, 0x03, 0x02, 0x0f, 0x26, 0x7f, 0x01, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x30, 0xe7, 0x03, +0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xd3, 0x90, 0xfa, 0xbb, +0xe0, 0x94, 0x00, 0x90, 0xfa, 0xba, 0xe0, 0x94, 0x00, 0x40, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, +0xc9, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe1, 0x03, 0x02, 0x0f, 0x26, +0xe4, 0xff, 0x02, 0x31, 0xb1, 0x90, 0xff, 0x01, 0x12, 0x1d, 0x74, 0xef, 0x12, 0x1a, 0x38, 0x90, +0xfa, 0xb6, 0x12, 0x1d, 0x74, 0x90, 0x00, 0x01, 0xef, 0x12, 0x1a, 0x4a, 0x90, 0x00, 0x02, 0xe4, +0x12, 0x1a, 0x4a, 0x74, 0x03, 0x12, 0x1c, 0x02, 0x90, 0xfa, 0xba, 0xe0, 0xff, 0xa3, 0xe0, 0x85, +0x38, 0x82, 0x85, 0x37, 0x83, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x90, 0xff, 0x01, 0xe0, 0x12, 0x1b, +0x4c, 0x09, 0x4a, 0x02, 0x09, 0x6c, 0x04, 0x09, 0x8e, 0x05, 0x09, 0xba, 0x06, 0x09, 0xd8, 0x07, +0x09, 0xf6, 0x08, 0x0a, 0x14, 0x09, 0x0a, 0x32, 0x0b, 0x0a, 0xe7, 0x80, 0x0d, 0x6f, 0x81, 0x0d, +0xa0, 0x82, 0x0b, 0x2e, 0x83, 0x0b, 0x77, 0x84, 0x0b, 0x96, 0x85, 0x0b, 0xdb, 0x86, 0x0c, 0x26, +0x87, 0x0c, 0xb7, 0x88, 0x0d, 0x42, 0x89, 0x0a, 0x50, 0x92, 0x0a, 0x50, 0x93, 0x0e, 0x53, 0xc0, +0x0e, 0x7f, 0xc1, 0x0e, 0x90, 0xc2, 0x00, 0x00, 0x0f, 0x15, 0xe5, 0x35, 0x20, 0xe7, 0x05, 0x7f, +0x05, 0x02, 0x30, 0xec, 0x12, 0x1c, 0xc1, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, +0x7f, 0x07, 0x02, 0x11, 0x16, 0xe4, 0xfd, 0x7f, 0x07, 0x02, 0x2f, 0x18, 0xe5, 0x35, 0x20, 0xe7, +0x05, 0x7f, 0x05, 0x02, 0x30, 0xec, 0x12, 0x1c, 0xc1, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, +0x7c, 0x00, 0x7f, 0x0c, 0x02, 0x11, 0x16, 0xe4, 0xfd, 0x7f, 0x07, 0x02, 0x2f, 0x18, 0xe5, 0x35, +0x30, 0xe7, 0x03, 0x02, 0x0f, 0x29, 0x12, 0x1d, 0x92, 0x50, 0x06, 0xe5, 0x3c, 0x45, 0x3b, 0x70, +0x05, 0x7f, 0x02, 0x02, 0x30, 0xec, 0x90, 0xfa, 0xb6, 0xe0, 0x24, 0xfe, 0x24, 0xfd, 0x50, 0x02, +0x80, 0x03, 0x02, 0x31, 0x6f, 0x7f, 0x07, 0x02, 0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, +0x0f, 0x29, 0x12, 0x1c, 0xc1, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x08, +0x02, 0x11, 0x16, 0x7f, 0x07, 0x02, 0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x29, +0x12, 0x1c, 0xc1, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x09, 0x02, 0x11, +0x16, 0x7f, 0x07, 0x02, 0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x29, 0x12, 0x1c, +0xc1, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0a, 0x02, 0x11, 0x16, 0x7f, +0x07, 0x02, 0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x29, 0x12, 0x1c, 0xc1, 0x60, +0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0b, 0x02, 0x11, 0x16, 0x7f, 0x07, 0x02, +0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x29, 0x12, 0x1c, 0xc1, 0x60, 0x03, 0x04, +0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0e, 0x02, 0x11, 0x16, 0x7f, 0x07, 0x02, 0x30, 0xec, +0xe5, 0x35, 0x30, 0xe7, 0x56, 0x12, 0x1c, 0xc9, 0x70, 0x4a, 0x90, 0xff, 0x02, 0xe0, 0xf5, 0x4c, +0xe5, 0x4c, 0xb4, 0x82, 0x05, 0x75, 0x4c, 0x61, 0x80, 0x12, 0xe5, 0x4c, 0xb4, 0x83, 0x05, 0x75, +0x4c, 0x62, 0x80, 0x08, 0xe5, 0x4c, 0xc4, 0x54, 0xf0, 0x04, 0xf5, 0x4c, 0x12, 0x1b, 0x72, 0x12, +0x1d, 0x8b, 0x12, 0x25, 0x39, 0x12, 0x1c, 0xd9, 0x12, 0x1a, 0x0b, 0x60, 0x05, 0x12, 0x31, 0xbd, +0x80, 0x06, 0x85, 0x33, 0x39, 0x85, 0x34, 0x3a, 0x75, 0x36, 0x01, 0x75, 0x37, 0xf9, 0x75, 0x38, +0x72, 0x02, 0x2c, 0x07, 0xe4, 0xfd, 0x7f, 0x05, 0x02, 0x2f, 0x18, 0x12, 0x1c, 0xc9, 0x60, 0x05, +0x7f, 0x05, 0x02, 0x30, 0xec, 0x12, 0x1d, 0x92, 0x40, 0x05, 0x7f, 0x03, 0x02, 0x30, 0xec, 0x90, +0xff, 0x02, 0xe0, 0xf5, 0x4c, 0xe5, 0x4c, 0xb4, 0x82, 0x05, 0x75, 0x4c, 0x61, 0x80, 0x12, 0xe5, +0x4c, 0xb4, 0x83, 0x05, 0x75, 0x4c, 0x62, 0x80, 0x08, 0xe5, 0x4c, 0xc4, 0x54, 0xf0, 0x04, 0xf5, +0x4c, 0x12, 0x1b, 0x72, 0x02, 0x31, 0x6f, 0x12, 0x1d, 0x9c, 0x12, 0x2a, 0x06, 0x12, 0x1c, 0x83, +0xe0, 0x54, 0x7f, 0xf0, 0x00, 0x00, 0x00, 0xe0, 0x90, 0xfa, 0xb7, 0xf0, 0x78, 0x68, 0x12, 0x1b, +0x28, 0x90, 0x00, 0x02, 0x12, 0x1a, 0x0b, 0x30, 0xe7, 0xf2, 0x90, 0x00, 0x02, 0xe4, 0x12, 0x1a, +0x4a, 0x90, 0xfa, 0xb7, 0xe0, 0x44, 0x80, 0xff, 0xf0, 0x78, 0x7c, 0xe6, 0xfc, 0x08, 0xe6, 0x8c, +0x83, 0x12, 0x1c, 0x8b, 0xef, 0xf0, 0x12, 0x31, 0xc7, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x90, 0xfa, +0xb6, 0xe0, 0x64, 0x01, 0x70, 0x1f, 0x90, 0xfa, 0xba, 0xe0, 0xff, 0x7e, 0x00, 0x70, 0x06, 0xa3, +0xe0, 0xf5, 0x90, 0x80, 0x2d, 0xc2, 0xaf, 0xef, 0xf4, 0x52, 0x90, 0x90, 0xfa, 0xbb, 0xe0, 0x42, +0x90, 0xd2, 0xaf, 0x80, 0x1d, 0x90, 0xfa, 0xba, 0xe0, 0xff, 0x7e, 0x00, 0x70, 0x06, 0xa3, 0xe0, +0xf5, 0xb0, 0x80, 0x0e, 0xc2, 0xaf, 0xef, 0xf4, 0x52, 0xb0, 0x90, 0xfa, 0xbb, 0xe0, 0x42, 0xb0, +0xd2, 0xaf, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x12, 0x1c, 0x30, 0x90, 0xfa, 0xb6, 0xe0, 0xb4, 0x01, +0x0a, 0x12, 0x1c, 0x11, 0xe5, 0x90, 0x12, 0x1a, 0x38, 0x80, 0x08, 0x12, 0x1c, 0x11, 0xe5, 0xb0, +0x12, 0x1a, 0x38, 0x02, 0x0f, 0xa9, 0x90, 0xfa, 0xb6, 0xe0, 0xff, 0x24, 0x12, 0x12, 0x1c, 0x41, +0x20, 0xe1, 0x33, 0x12, 0x1c, 0xd0, 0xef, 0x24, 0xfc, 0x60, 0x18, 0x04, 0x70, 0x28, 0x90, 0xfa, +0xb7, 0xe0, 0x60, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x10, 0xf0, 0x80, 0x19, 0x12, 0x1d, 0xa6, +0xf0, 0x80, 0x13, 0x90, 0xfa, 0xb7, 0xe0, 0x60, 0x09, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x10, 0xf0, +0x80, 0x04, 0x12, 0x1d, 0xad, 0xf0, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x90, 0xfa, 0xb6, 0xe0, 0xff, +0x24, 0x12, 0x12, 0x1c, 0x41, 0x20, 0xe1, 0x39, 0x12, 0x1c, 0xd0, 0xef, 0x24, 0xfc, 0x60, 0x1b, +0x04, 0x70, 0x2e, 0x90, 0xfa, 0xb7, 0xe0, 0x60, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x20, 0xf0, +0x80, 0x1f, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xdf, 0xf0, 0x80, 0x16, 0x90, 0xfa, 0xb7, 0xe0, 0x60, +0x09, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x07, 0x90, 0xff, 0xb4, 0xe0, 0x54, 0xdf, +0xf0, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x12, 0x1c, 0xd0, 0x12, 0x1c, 0xc1, 0x60, 0x4d, 0x04, 0x60, +0x03, 0x02, 0x0c, 0xb2, 0x90, 0xfa, 0xb7, 0xe0, 0x60, 0x0f, 0x90, 0xff, 0xa4, 0x12, 0x1c, 0x3a, +0x30, 0xe1, 0x6f, 0x12, 0x1d, 0x7c, 0x02, 0x0c, 0xb2, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xfb, 0x12, +0x1c, 0x3d, 0xfe, 0x30, 0xe1, 0x5c, 0x30, 0xe2, 0x11, 0x30, 0xb4, 0x05, 0x12, 0x1d, 0x7c, 0x80, +0x51, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xfd, 0xf0, 0x80, 0x48, 0x30, 0x95, 0x05, 0x12, 0x1d, 0x7c, +0x80, 0x40, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xfd, 0xf0, 0x80, 0x37, 0x90, 0xfa, 0xb7, 0xe0, 0x60, +0x12, 0x90, 0xff, 0xb4, 0x12, 0x1c, 0x3a, 0x30, 0xe1, 0x28, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x02, +0xf0, 0x80, 0x1f, 0x90, 0xff, 0xb4, 0xe0, 0x54, 0xfb, 0x12, 0x1c, 0x3d, 0x30, 0xe1, 0x13, 0x30, +0x93, 0x09, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x07, 0x90, 0xff, 0xb4, 0xe0, 0x54, +0xfd, 0xf0, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x12, 0x1c, 0xd0, 0x90, 0xfa, 0xb6, 0xe0, 0x24, 0xfc, +0x60, 0x40, 0x04, 0x70, 0x78, 0x90, 0xfa, 0xb7, 0xe0, 0x60, 0x1d, 0x90, 0xff, 0xa2, 0xe0, 0x44, +0x40, 0xf0, 0xa3, 0xe0, 0xff, 0x30, 0xe7, 0x65, 0xd2, 0x03, 0xa3, 0xe0, 0x54, 0xdf, 0xf0, 0x90, +0xff, 0xa3, 0xef, 0x54, 0x7f, 0xf0, 0x80, 0x55, 0x30, 0x03, 0x0e, 0x90, 0xff, 0xa3, 0xe0, 0x44, +0x80, 0xf0, 0xc2, 0x03, 0xa3, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0xff, 0xa2, 0xe0, 0x54, 0xbf, 0xf0, +0x80, 0x3b, 0x90, 0xfa, 0xb7, 0xe0, 0x60, 0x1d, 0x90, 0xff, 0xb2, 0xe0, 0x44, 0x40, 0xf0, 0xa3, +0xe0, 0xff, 0x30, 0xe7, 0x28, 0xd2, 0x04, 0xa3, 0xe0, 0x54, 0xdf, 0xf0, 0x90, 0xff, 0xb3, 0xef, +0x54, 0x7f, 0xf0, 0x80, 0x18, 0x30, 0x04, 0x0e, 0x90, 0xff, 0xb3, 0xe0, 0x44, 0x80, 0xf0, 0xc2, +0x04, 0xa3, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0xff, 0xb2, 0xe0, 0x54, 0xbf, 0xf0, 0xe4, 0xff, 0x02, +0x30, 0xec, 0x12, 0x1c, 0x30, 0x90, 0xfa, 0xb6, 0xe0, 0x24, 0xfc, 0x60, 0x0f, 0x04, 0x70, 0x16, +0x90, 0xff, 0xa6, 0xe0, 0x12, 0x1c, 0x11, 0x12, 0x1a, 0x38, 0x80, 0x0a, 0x90, 0xff, 0xb6, 0xe0, +0x12, 0x1c, 0x11, 0x12, 0x1a, 0x38, 0x75, 0x39, 0x00, 0x75, 0x3a, 0x01, 0x02, 0x2c, 0x07, 0xe4, +0xff, 0x12, 0x30, 0xec, 0x12, 0x1d, 0x37, 0x7f, 0x03, 0x12, 0x12, 0x19, 0x90, 0xf9, 0x15, 0xe0, +0x30, 0xe4, 0x08, 0x90, 0xff, 0x93, 0x74, 0x80, 0xf0, 0x80, 0x10, 0x90, 0xff, 0xfc, 0xe0, 0x54, +0x7f, 0xf0, 0x7f, 0xff, 0x7e, 0x00, 0x12, 0x30, 0x16, 0xc2, 0x90, 0xc2, 0xaf, 0x00, 0x80, 0xfd, +0xe4, 0xf5, 0x4e, 0xf5, 0x4f, 0x90, 0xfa, 0xbc, 0x74, 0x3e, 0xf0, 0xa3, 0xe4, 0xf0, 0x90, 0xfa, +0xb4, 0xf0, 0xa3, 0x74, 0x15, 0xf0, 0xe0, 0x54, 0x3f, 0xff, 0xc3, 0x74, 0x40, 0x9f, 0x90, 0xfa, +0xb9, 0xf0, 0xd3, 0x94, 0x00, 0xe4, 0x94, 0x3e, 0x40, 0x08, 0x90, 0xfa, 0xbd, 0xe0, 0x90, 0xfa, +0xb9, 0xf0, 0x12, 0x0f, 0x50, 0xe5, 0x31, 0x45, 0x30, 0x70, 0x73, 0x12, 0x1c, 0x4a, 0x90, 0xfa, +0xbc, 0x12, 0x1d, 0x56, 0x60, 0x27, 0xd3, 0xef, 0x94, 0x40, 0xee, 0x94, 0x00, 0x40, 0x08, 0x90, +0xfa, 0xb9, 0x74, 0x40, 0xf0, 0x80, 0x08, 0x90, 0xfa, 0xbd, 0xe0, 0x90, 0xfa, 0xb9, 0xf0, 0x12, +0x0f, 0x50, 0xe5, 0x31, 0x45, 0x30, 0x70, 0x46, 0x12, 0x1c, 0x4a, 0x80, 0xd1, 0x75, 0x4c, 0x02, +0x90, 0xfa, 0xbc, 0xe4, 0xf0, 0xa3, 0x04, 0xf0, 0x90, 0xfa, 0xb4, 0xe4, 0xf0, 0xa3, 0x74, 0x0f, +0xf0, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x4c, 0x90, 0xfa, 0xbd, 0xe0, 0xf5, 0x4a, 0x7d, 0x0f, 0x7c, +0x00, 0x12, 0x28, 0x9f, 0x75, 0x30, 0x00, 0x8f, 0x31, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x4c, 0xe4, +0xf5, 0x2d, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0xe4, 0xf5, 0x30, 0xf5, 0x31, 0xaf, 0x31, +0x02, 0x30, 0xec, 0x12, 0x1c, 0xd0, 0x30, 0xe7, 0x10, 0xe0, 0x54, 0x0f, 0x90, 0xf9, 0x64, 0xf0, +0xd3, 0x94, 0x00, 0x40, 0x15, 0xc2, 0x95, 0x80, 0x11, 0x90, 0xfa, 0xb7, 0xe0, 0x54, 0x0f, 0x90, +0xf9, 0x63, 0xf0, 0xd3, 0x94, 0x00, 0x40, 0x02, 0xc2, 0x94, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x12, +0x1d, 0x9c, 0xbf, 0x01, 0x04, 0xd2, 0x93, 0x80, 0x02, 0xc2, 0x93, 0xe4, 0xff, 0x02, 0x30, 0xec, +0x12, 0x1c, 0xd0, 0x54, 0x03, 0x14, 0x60, 0x0a, 0x14, 0x60, 0x0f, 0x14, 0x60, 0x08, 0x24, 0x03, +0x70, 0x2b, 0xd2, 0x91, 0x80, 0x27, 0xc2, 0x91, 0x80, 0x23, 0x12, 0x1d, 0xa6, 0x12, 0x0f, 0x78, +0x60, 0x04, 0xd2, 0x91, 0x80, 0x17, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x10, 0x12, 0x0f, 0x78, 0xff, +0xbf, 0xa0, 0x04, 0xc2, 0x91, 0x80, 0x02, 0xd2, 0x91, 0x12, 0x1d, 0xa6, 0xf0, 0x90, 0xfa, 0xb7, +0xe0, 0x54, 0x0c, 0xff, 0x13, 0x13, 0x54, 0x3f, 0x14, 0x60, 0x0a, 0x14, 0x60, 0x0f, 0x14, 0x60, +0x08, 0x24, 0x03, 0x70, 0x2b, 0xd2, 0x92, 0x80, 0x27, 0xc2, 0x92, 0x80, 0x23, 0x12, 0x1d, 0xad, +0x12, 0x0f, 0x98, 0x60, 0x04, 0xd2, 0x92, 0x80, 0x17, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x10, 0x12, +0x0f, 0x98, 0xff, 0xbf, 0xa0, 0x04, 0xc2, 0x92, 0x80, 0x02, 0xd2, 0x92, 0x12, 0x1d, 0xad, 0xf0, +0xe4, 0xff, 0x02, 0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x07, 0xe4, 0xfd, 0x7f, 0x05, 0x02, 0x2f, +0x18, 0x7f, 0x05, 0x02, 0x30, 0xec, 0x12, 0x31, 0xbd, 0x22, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb3, +0x90, 0xfa, 0xb4, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x90, +0xfa, 0xb4, 0xe4, 0x75, 0xf0, 0x03, 0x12, 0x1a, 0x6c, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0x22, +0xaa, 0x4e, 0xa9, 0x4f, 0x7b, 0xff, 0x90, 0xfa, 0xb4, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0x90, 0xfa, +0xb9, 0xe0, 0xf5, 0x4a, 0x12, 0x28, 0x9f, 0x75, 0x30, 0x00, 0x8f, 0x31, 0x22, 0x12, 0x22, 0xa0, +0x7e, 0x00, 0x8e, 0x30, 0x8f, 0x31, 0xef, 0x22, 0xf0, 0x7f, 0x01, 0x12, 0x12, 0x19, 0x90, 0xff, +0xa6, 0xe0, 0x90, 0xfa, 0xb8, 0xf0, 0x54, 0xa0, 0x22, 0x12, 0x25, 0xd7, 0x8f, 0x4c, 0x7e, 0x00, +0xc3, 0xef, 0x95, 0x3c, 0xee, 0x95, 0x3b, 0x22, 0xf0, 0x7f, 0x01, 0x12, 0x12, 0x19, 0x90, 0xff, +0xb6, 0xe0, 0x90, 0xfa, 0xb8, 0xf0, 0x54, 0xa0, 0x22, 0x75, 0x39, 0x00, 0x75, 0x3a, 0x01, 0x02, +0x2c, 0x07, 0x90, 0xfa, 0xb6, 0xe0, 0xff, 0x02, 0x31, 0x82, 0x8e, 0x39, 0x8f, 0x3a, 0x02, 0x2c, +0x07, 0x12, 0x22, 0xa0, 0x7e, 0x00, 0x8e, 0x30, 0x8f, 0x31, 0xef, 0x22, 0x7d, 0x01, 0x12, 0x25, +0xd7, 0x90, 0xfa, 0xb1, 0xe0, 0x22, 0xef, 0x90, 0xf8, 0x04, 0xf0, 0x22, 0xc0, 0xa8, 0xc2, 0xaf, +0xee, 0x60, 0x0a, 0xc0, 0x05, 0x7d, 0x7f, 0xdd, 0xfe, 0xde, 0xfa, 0xd0, 0x05, 0xef, 0xc3, 0x94, +0x15, 0x50, 0x03, 0xd0, 0xa8, 0x22, 0x13, 0x70, 0x03, 0xd0, 0xa8, 0x22, 0xff, 0xd5, 0x07, 0xfd, +0xd0, 0xa8, 0x22, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x04, 0xc0, 0x05, 0xe5, 0x3e, 0x24, +0x08, 0xf8, 0x86, 0x05, 0x53, 0x05, 0x7f, 0x7c, 0xff, 0x12, 0x10, 0x78, 0x7f, 0x00, 0x7e, 0x00, +0xe5, 0x43, 0x60, 0x46, 0xfc, 0x90, 0xf9, 0x1b, 0xe0, 0x54, 0x7f, 0x6d, 0x70, 0x0f, 0xc0, 0x83, +0xc0, 0x82, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0xa3, 0x15, 0x43, 0x80, 0x07, 0xa3, 0xa3, 0xa3, +0xdc, 0xe6, 0x80, 0x26, 0xdc, 0x06, 0xd0, 0x82, 0xd0, 0x83, 0x80, 0x1e, 0xe0, 0xf8, 0xa3, 0xe0, +0xf9, 0xa3, 0xe0, 0xfa, 0xd0, 0x82, 0xd0, 0x83, 0xe8, 0xf0, 0xa3, 0xe9, 0xf0, 0xa3, 0xea, 0xf0, +0xa3, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0x80, 0xda, 0x12, 0x11, 0x0f, 0xd0, 0x05, 0xd0, +0x04, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0x22, 0x85, 0xa8, 0x44, 0x75, 0xa8, 0x88, 0xec, 0x70, +0x02, 0x7c, 0x3f, 0x8c, 0x3d, 0x22, 0xe5, 0x3e, 0x24, 0x08, 0xf8, 0x76, 0x00, 0x12, 0x11, 0x66, +0x80, 0xfb, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x04, 0xc0, 0x06, 0x7c, 0xff, 0x12, 0x10, +0x78, 0xe5, 0x43, 0x60, 0x42, 0xfe, 0x90, 0xf9, 0x1b, 0xe0, 0x54, 0x7f, 0x6f, 0x70, 0x0b, 0xc0, +0x83, 0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0x15, 0x43, 0x80, 0x07, 0xa3, 0xa3, 0xa3, 0xde, 0xea, 0x80, +0x26, 0xde, 0x06, 0xd0, 0x82, 0xd0, 0x83, 0x80, 0xd8, 0xe0, 0xf8, 0xa3, 0xe0, 0xf9, 0xa3, 0xe0, +0xfa, 0xd0, 0x82, 0xd0, 0x83, 0xe8, 0xf0, 0xa3, 0xe9, 0xf0, 0xa3, 0xea, 0xf0, 0xa3, 0xc0, 0x83, +0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0x80, 0xda, 0x78, 0x08, 0x08, 0x79, 0x18, 0x09, 0x7c, 0x01, 0xe6, +0x54, 0x7f, 0x6f, 0x70, 0x06, 0x76, 0x00, 0x77, 0x00, 0x80, 0x06, 0x08, 0x09, 0x0c, 0xbc, 0x08, +0xee, 0x12, 0x11, 0x0f, 0xd0, 0x06, 0xd0, 0x04, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0x22, 0x75, +0x3d, 0x00, 0x85, 0x44, 0xa8, 0x22, 0xc0, 0xf0, 0xc0, 0x82, 0xc0, 0x83, 0xc3, 0xe5, 0x43, 0x24, +0xe8, 0x50, 0x05, 0x12, 0x11, 0x66, 0x80, 0xf4, 0xef, 0x60, 0x31, 0x90, 0x30, 0x54, 0xe4, 0x93, +0xc3, 0x9f, 0x40, 0x2f, 0xc0, 0x04, 0x7c, 0xff, 0x12, 0x10, 0x78, 0xd0, 0x04, 0x43, 0x07, 0x80, +0xe5, 0x43, 0x75, 0xf0, 0x03, 0xa4, 0x24, 0x1b, 0xf5, 0x82, 0xe4, 0x34, 0xf9, 0xf5, 0x83, 0xef, +0xf0, 0xec, 0xa3, 0xf0, 0xed, 0xa3, 0xf0, 0x05, 0x43, 0x12, 0x11, 0x0f, 0xd0, 0x83, 0xd0, 0x82, +0xd0, 0xf0, 0x22, 0x02, 0x11, 0x94, 0xc0, 0x04, 0x7c, 0x20, 0xd2, 0x8c, 0xd2, 0x8d, 0xd5, 0x04, +0xfd, 0xd0, 0x04, 0x22, 0x75, 0xa8, 0x00, 0x75, 0x88, 0x00, 0x75, 0xb8, 0x00, 0x75, 0xf0, 0x00, +0x75, 0xd0, 0x00, 0xe4, 0xf8, 0x90, 0xf8, 0x04, 0xf0, 0x90, 0x00, 0x00, 0xf6, 0x08, 0xb8, 0x00, +0xfb, 0x02, 0x00, 0x00, 0xc2, 0xaf, 0xe4, 0x90, 0xff, 0x48, 0xf0, 0x90, 0xff, 0x50, 0xf0, 0x90, +0xff, 0x08, 0xf0, 0x90, 0xff, 0x10, 0xf0, 0x90, 0xff, 0x80, 0xf0, 0xa3, 0xa3, 0xf0, 0xd2, 0xb1, +0xc2, 0xb0, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0xdc, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0xdc, +0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0xdc, 0xd2, 0xb0, 0xd2, 0xb1, 0x7e, 0xff, 0x7f, 0xff, 0x12, +0x0f, 0xdc, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0xdc, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0xdc, +0x80, 0xcc, 0xc3, 0xee, 0x94, 0x02, 0x50, 0x04, 0x7e, 0x03, 0x7f, 0xe8, 0xef, 0xf4, 0xff, 0xee, +0xf4, 0xfe, 0x0f, 0xbf, 0x00, 0x01, 0x0e, 0x8f, 0x42, 0x8e, 0x41, 0x22, 0xc3, 0xef, 0x94, 0xbc, +0xee, 0x94, 0x02, 0x50, 0x04, 0x7e, 0x07, 0x7f, 0xd0, 0xef, 0xf4, 0xff, 0xee, 0xf4, 0xfe, 0x0f, +0xbf, 0x00, 0x01, 0x0e, 0x8f, 0x40, 0x8e, 0x3f, 0x22, 0xef, 0x70, 0x01, 0x22, 0xc0, 0x00, 0xc0, +0xa8, 0xc2, 0xaf, 0xe5, 0x3e, 0x24, 0x18, 0xf8, 0xa6, 0x07, 0xe5, 0x3e, 0x24, 0x08, 0xf8, 0xc6, +0x54, 0x7f, 0xf6, 0xd0, 0xa8, 0xe6, 0x30, 0xe7, 0x03, 0xd0, 0x00, 0x22, 0x12, 0x11, 0x66, 0x80, 0xf4, 0xc0, 0x00, 0x7f, 0x01, 0xef, 0x24, 0x08, 0xf8, 0xe6, 0x60, 0x09, 0x0f, 0xbf, 0x08, 0xf5, -0x12, 0x10, 0xec, 0x80, 0xee, 0xd0, 0x00, 0x22, 0xc0, 0xf0, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x00, -0xc0, 0x06, 0xc0, 0x04, 0xed, 0x24, 0x10, 0xf8, 0x76, 0xa0, 0xed, 0x75, 0xf0, 0x21, 0xa4, 0x24, +0x12, 0x11, 0x66, 0x80, 0xee, 0xd0, 0x00, 0x22, 0xc0, 0xf0, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x00, +0xc0, 0x06, 0xc0, 0x04, 0xed, 0x24, 0x10, 0xf8, 0x76, 0x9a, 0xed, 0x75, 0xf0, 0x21, 0xa4, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x34, 0xf8, 0xf5, 0x83, 0xc0, 0x82, 0xc0, 0x83, 0xa3, 0xa3, 0xe4, 0x78, -0x0d, 0xf0, 0xa3, 0xd8, 0xfc, 0xef, 0x54, 0x7f, 0x75, 0xf0, 0x02, 0xa4, 0x24, 0x0e, 0xf5, 0x82, -0xe5, 0xf0, 0x34, 0x2e, 0xf5, 0x83, 0xe4, 0x93, 0xfe, 0x74, 0x01, 0x93, 0xfc, 0xd0, 0x83, 0xd0, +0x0d, 0xf0, 0xa3, 0xd8, 0xfc, 0xef, 0x54, 0x7f, 0x75, 0xf0, 0x02, 0xa4, 0x24, 0x36, 0xf5, 0x82, +0xe5, 0xf0, 0x34, 0x30, 0xf5, 0x83, 0xe4, 0x93, 0xfe, 0x74, 0x01, 0x93, 0xfc, 0xd0, 0x83, 0xd0, 0x82, 0xec, 0xf0, 0xa3, 0xee, 0xf0, 0xed, 0x24, 0x08, 0xf8, 0xef, 0x44, 0x80, 0xf6, 0xd0, 0x04, -0xd0, 0x06, 0xd0, 0x00, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0xf0, 0x22, 0x75, 0x44, 0x00, 0x75, 0x49, +0xd0, 0x06, 0xd0, 0x00, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0xf0, 0x22, 0x75, 0x3e, 0x00, 0x75, 0x43, 0x00, 0x7a, 0x08, 0x79, 0x18, 0x78, 0x08, 0x76, 0x00, 0x77, 0x00, 0x08, 0x09, 0xda, 0xf8, 0x90, -0xf8, 0x04, 0xe0, 0xfc, 0x90, 0x2e, 0x2c, 0xe4, 0x93, 0xc3, 0x9c, 0x50, 0x05, 0xe4, 0x90, 0xf8, +0xf8, 0x04, 0xe0, 0xfc, 0x90, 0x30, 0x54, 0xe4, 0x93, 0xc3, 0x9c, 0x50, 0x05, 0xe4, 0x90, 0xf8, 0x04, 0xf0, 0x78, 0x08, 0x74, 0x80, 0x44, 0x7f, 0xf6, 0x74, 0x01, 0x44, 0x10, 0xf5, 0x89, 0x75, -0xb8, 0x00, 0xd2, 0xab, 0xd2, 0xa9, 0x22, 0x75, 0x81, 0x91, 0xd2, 0x8e, 0xd2, 0x8c, 0xd2, 0xaf, -0xe5, 0x49, 0x60, 0x36, 0xff, 0x90, 0xf9, 0x19, 0xe0, 0x54, 0x80, 0x60, 0x28, 0x78, 0x08, 0x79, +0xb8, 0x00, 0xd2, 0xab, 0xd2, 0xa9, 0x22, 0x75, 0x81, 0x8b, 0xd2, 0x8e, 0xd2, 0x8c, 0xd2, 0xaf, +0xe5, 0x43, 0x60, 0x36, 0xff, 0x90, 0xf9, 0x1b, 0xe0, 0x54, 0x80, 0x60, 0x28, 0x78, 0x08, 0x79, 0x08, 0xe0, 0x54, 0x7f, 0xfa, 0x7b, 0x00, 0xe6, 0x54, 0x7f, 0xb5, 0x02, 0x02, 0x7b, 0xff, 0x08, -0xd9, 0xf5, 0xeb, 0x70, 0x10, 0xea, 0xf0, 0xc0, 0x07, 0x12, 0x11, 0xc1, 0xad, 0x07, 0xaf, 0x02, -0x12, 0x11, 0xd8, 0xd0, 0x07, 0xa3, 0xa3, 0xa3, 0xdf, 0xce, 0x12, 0x10, 0xec, 0x80, 0xc1, 0xe7, -0x09, 0xf6, 0x08, 0xdf, 0xfa, 0x80, 0x46, 0xe7, 0x09, 0xf2, 0x08, 0xdf, 0xfa, 0x80, 0x3e, 0x88, -0x82, 0x8c, 0x83, 0xe7, 0x09, 0xf0, 0xa3, 0xdf, 0xfa, 0x80, 0x32, 0xe3, 0x09, 0xf6, 0x08, 0xdf, -0xfa, 0x80, 0x78, 0xe3, 0x09, 0xf2, 0x08, 0xdf, 0xfa, 0x80, 0x70, 0x88, 0x82, 0x8c, 0x83, 0xe3, -0x09, 0xf0, 0xa3, 0xdf, 0xfa, 0x80, 0x64, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0xa3, 0xf6, 0x08, 0xdf, -0xfa, 0x80, 0x58, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0xa3, 0xf2, 0x08, 0xdf, 0xfa, 0x80, 0x4c, 0x80, -0xd2, 0x80, 0xfa, 0x80, 0xc6, 0x80, 0xd4, 0x80, 0x69, 0x80, 0xf2, 0x80, 0x33, 0x80, 0x10, 0x80, -0xa6, 0x80, 0xea, 0x80, 0x9a, 0x80, 0xa8, 0x80, 0xda, 0x80, 0xe2, 0x80, 0xca, 0x80, 0x33, 0x89, -0x82, 0x8a, 0x83, 0xec, 0xfa, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xcc, 0xc5, 0x83, 0xcc, -0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xcc, 0xc5, 0x83, 0xcc, 0xdf, 0xe9, 0xde, 0xe7, 0x80, 0x0d, -0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0xa3, 0xf6, 0x08, 0xdf, 0xf9, 0xec, 0xfa, 0xa9, 0xf0, 0xed, -0xfb, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xec, 0xfa, 0xe0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xcc, 0xc5, -0x83, 0xcc, 0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xcc, 0xc5, 0x83, 0xcc, 0xdf, 0xea, 0xde, 0xe8, -0x80, 0xdb, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0xa3, 0xf2, 0x08, 0xdf, 0xf9, 0x80, 0xcc, 0x88, -0xf0, 0xef, 0x60, 0x01, 0x0e, 0x4e, 0x60, 0xc3, 0x88, 0xf0, 0xed, 0x24, 0x02, 0xb4, 0x04, 0x00, -0x50, 0xb9, 0xf5, 0x82, 0xeb, 0x24, 0x02, 0xb4, 0x04, 0x00, 0x50, 0xaf, 0x23, 0x23, 0x45, 0x82, -0x23, 0x90, 0x13, 0x0f, 0x73, 0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0x22, 0x50, 0x02, -0xe7, 0x22, 0xbb, 0xfe, 0x02, 0xe3, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0x22, 0xbb, 0x01, -0x0c, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0x22, 0x50, 0x06, 0xe9, -0x25, 0x82, 0xf8, 0xe6, 0x22, 0xbb, 0xfe, 0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0x22, 0xe5, 0x82, -0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe4, 0x93, 0x22, 0xbb, 0x01, 0x06, 0x89, 0x82, -0x8a, 0x83, 0xf0, 0x22, 0x50, 0x02, 0xf7, 0x22, 0xbb, 0xfe, 0x01, 0xf3, 0x22, 0xf8, 0xbb, 0x01, -0x0d, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe8, 0xf0, 0x22, 0x50, 0x06, -0xe9, 0x25, 0x82, 0xc8, 0xf6, 0x22, 0xbb, 0xfe, 0x05, 0xe9, 0x25, 0x82, 0xc8, 0xf2, 0x22, 0xc5, -0xf0, 0xf8, 0xa3, 0xe0, 0x28, 0xf0, 0xc5, 0xf0, 0xf8, 0xe5, 0x82, 0x15, 0x82, 0x70, 0x02, 0x15, -0x83, 0xe0, 0x38, 0xf0, 0x22, 0xa3, 0xf8, 0xe0, 0xc5, 0xf0, 0x25, 0xf0, 0xf0, 0xe5, 0x82, 0x15, -0x82, 0x70, 0x02, 0x15, 0x83, 0xe0, 0xc8, 0x38, 0xf0, 0xe8, 0x22, 0xbb, 0x01, 0x10, 0xe5, 0x82, -0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0xf5, 0xf0, 0xa3, 0xe0, 0x22, 0x50, 0x09, -0xe9, 0x25, 0x82, 0xf8, 0x86, 0xf0, 0x08, 0xe6, 0x22, 0xbb, 0xfe, 0x0a, 0xe9, 0x25, 0x82, 0xf8, -0xe2, 0xf5, 0xf0, 0x08, 0xe2, 0x22, 0xe5, 0x83, 0x2a, 0xf5, 0x83, 0xe9, 0x93, 0xf5, 0xf0, 0xa3, -0xe9, 0x93, 0x22, 0xbb, 0x01, 0x0a, 0x89, 0x82, 0x8a, 0x83, 0xf0, 0xe5, 0xf0, 0xa3, 0xf0, 0x22, -0x50, 0x06, 0xf7, 0x09, 0xa7, 0xf0, 0x19, 0x22, 0xbb, 0xfe, 0x06, 0xf3, 0xe5, 0xf0, 0x09, 0xf3, -0x19, 0x22, 0xf8, 0xbb, 0x01, 0x11, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, -0xe8, 0xf0, 0xe5, 0xf0, 0xa3, 0xf0, 0x22, 0x50, 0x09, 0xe9, 0x25, 0x82, 0xc8, 0xf6, 0x08, 0xa6, -0xf0, 0x22, 0xbb, 0xfe, 0x09, 0xe9, 0x25, 0x82, 0xc8, 0xf2, 0xe5, 0xf0, 0x08, 0xf2, 0x22, 0xa4, -0x25, 0x82, 0xf5, 0x82, 0xe5, 0xf0, 0x35, 0x83, 0xf5, 0x83, 0x22, 0xe6, 0xfb, 0x08, 0xe6, 0xfa, -0x08, 0xe6, 0xf9, 0x22, 0xeb, 0xf6, 0x08, 0xea, 0xf6, 0x08, 0xe9, 0xf6, 0x22, 0xe0, 0xfb, 0xa3, -0xe0, 0xfa, 0xa3, 0xe0, 0xf9, 0x22, 0xeb, 0xf0, 0xa3, 0xea, 0xf0, 0xa3, 0xe9, 0xf0, 0x22, 0xd0, -0x83, 0xd0, 0x82, 0xf8, 0xe4, 0x93, 0x70, 0x12, 0x74, 0x01, 0x93, 0x70, 0x0d, 0xa3, 0xa3, 0x93, -0xf8, 0x74, 0x01, 0x93, 0xf5, 0x82, 0x88, 0x83, 0xe4, 0x73, 0x74, 0x02, 0x93, 0x68, 0x60, 0xef, -0xa3, 0xa3, 0xa3, 0x80, 0xdf, 0x90, 0xff, 0xfa, 0x74, 0x08, 0xf0, 0xa3, 0x74, 0x16, 0xf0, 0x90, -0xff, 0xf9, 0x74, 0x02, 0xf0, 0x90, 0xfa, 0xcb, 0xe4, 0xf0, 0xa3, 0x74, 0x0b, 0xf0, 0x7b, 0x00, -0x7a, 0x00, 0x79, 0x37, 0x75, 0x40, 0x00, 0xf5, 0x41, 0x7d, 0x01, 0x12, 0x23, 0xee, 0xe5, 0x37, -0x24, 0x80, 0x90, 0xff, 0xf8, 0xf0, 0xe5, 0x37, 0x64, 0x07, 0x60, 0x0b, 0xe5, 0x37, 0x64, 0x06, -0x60, 0x05, 0xe5, 0x37, 0xb4, 0x14, 0x1b, 0xd2, 0x94, 0xd2, 0x95, 0xd2, 0x92, 0xd2, 0x93, 0xe5, -0x37, 0xb4, 0x07, 0x08, 0x90, 0xf9, 0x65, 0x74, 0x02, 0xf0, 0x80, 0x06, 0x90, 0xf9, 0x65, 0x74, -0x01, 0xf0, 0x90, 0xfa, 0xcb, 0xe4, 0xf0, 0xa3, 0x74, 0x0d, 0xf0, 0x12, 0x17, 0x71, 0x90, 0xff, -0xf5, 0xe5, 0x37, 0xf0, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xcb, 0xe4, 0xfd, 0x12, 0x20, 0xc5, 0x90, -0xfa, 0xcb, 0xe4, 0x75, 0xf0, 0x03, 0x12, 0x14, 0x2f, 0x12, 0x17, 0x71, 0xe5, 0x37, 0x30, 0xe7, -0x02, 0xd2, 0x02, 0xe4, 0xf5, 0x2c, 0xf5, 0x2a, 0xf5, 0x2b, 0xf5, 0x29, 0x12, 0x19, 0x92, 0x12, -0x18, 0x49, 0x12, 0x19, 0x6c, 0x90, 0xf9, 0x66, 0x12, 0x15, 0x06, 0x90, 0xf9, 0x6b, 0x12, 0x15, -0x06, 0x90, 0xff, 0xff, 0xe4, 0xf0, 0x90, 0xff, 0x83, 0xe0, 0xe4, 0xf0, 0x90, 0xff, 0x81, 0x74, -0x80, 0xf0, 0xa3, 0x74, 0x84, 0xf0, 0x90, 0xff, 0x80, 0xf0, 0xe4, 0xf5, 0x37, 0xe5, 0x37, 0x12, -0x18, 0xbf, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x37, 0x12, 0x18, 0xcd, 0xf5, 0x83, 0xe4, 0xf0, 0x05, -0x37, 0xe5, 0x37, 0xb4, 0x07, 0xe7, 0x78, 0x80, 0x76, 0xfe, 0x08, 0x76, 0xf0, 0x90, 0x2f, 0x06, -0xe4, 0x93, 0xff, 0x78, 0x7e, 0xf6, 0xfd, 0xad, 0x07, 0x90, 0x2f, 0x13, 0xe4, 0x93, 0xff, 0x08, -0xf6, 0xff, 0xed, 0x54, 0x0f, 0xfd, 0x12, 0x18, 0xaf, 0x74, 0x84, 0xf0, 0xed, 0x75, 0xf0, 0x08, -0xa4, 0x24, 0x47, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0xc3, 0x74, 0xf0, 0x9f, -0x78, 0x81, 0xf6, 0x74, 0xfe, 0x94, 0x00, 0x18, 0x12, 0x18, 0x41, 0xce, 0xc3, 0x13, 0xce, 0x13, -0xd8, 0xf9, 0xff, 0xed, 0x12, 0x19, 0x07, 0xef, 0xf0, 0xed, 0x12, 0x19, 0x2d, 0xe4, 0xf5, 0x37, -0xe5, 0x37, 0x90, 0x2f, 0x00, 0x93, 0xff, 0x78, 0x7e, 0xf6, 0xfd, 0xe5, 0x37, 0x25, 0xe0, 0x24, -0x07, 0xf5, 0x82, 0xe4, 0x34, 0x2f, 0xf5, 0x83, 0xe4, 0x93, 0x08, 0xf6, 0xed, 0x30, 0xe7, 0x53, -0x18, 0xe6, 0x54, 0x0f, 0xf9, 0x12, 0x18, 0xaf, 0x12, 0x19, 0x15, 0x24, 0x47, 0xf5, 0x82, 0xe4, -0x34, 0xff, 0x12, 0x18, 0x31, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0xff, 0xe9, 0x12, 0x19, -0x07, 0xef, 0xf0, 0x12, 0x18, 0x38, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0x12, 0x19, 0x1a, -0x24, 0x45, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0xe9, 0x12, 0x19, 0x2d, 0xe9, -0x75, 0xf0, 0x08, 0xa4, 0x24, 0x46, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0x74, 0x80, 0xf0, -0x02, 0x17, 0x46, 0x78, 0x7e, 0xe6, 0x54, 0x0f, 0xf9, 0x12, 0x18, 0xf9, 0x12, 0x19, 0x15, 0x24, -0x07, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0x12, 0x18, 0x31, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, -0x12, 0x19, 0x1a, 0x24, 0x01, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0x12, 0x18, -0x38, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0x12, 0x19, 0x1a, 0x24, 0x05, 0xf5, 0x82, 0xe4, -0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0xe9, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x02, 0xf5, 0x82, 0xe4, -0x34, 0xff, 0xf5, 0x83, 0xe4, 0xf0, 0xe9, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x06, 0xf5, 0x82, 0xe4, -0x34, 0xff, 0xf5, 0x83, 0xe4, 0xf0, 0x05, 0x37, 0xe5, 0x37, 0x64, 0x04, 0x60, 0x03, 0x02, 0x16, -0x70, 0x90, 0x2f, 0x05, 0xe4, 0x93, 0xff, 0x78, 0x7e, 0xf6, 0x12, 0x18, 0xf7, 0xe4, 0xf0, 0x90, -0x2f, 0x04, 0x93, 0xff, 0xf6, 0x12, 0x18, 0xad, 0xe4, 0xf0, 0x90, 0xff, 0xfd, 0x74, 0x05, 0xf0, -0x22, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x37, 0x90, 0xfa, 0xcb, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x14, -0x45, 0x85, 0xf0, 0x41, 0xf5, 0x40, 0x7d, 0x01, 0x02, 0x23, 0xee, 0xab, 0x2d, 0xaa, 0x2e, 0xa9, -0x2f, 0xe5, 0x52, 0x12, 0x13, 0xfb, 0x74, 0x01, 0x25, 0x2f, 0xf5, 0x2f, 0xe4, 0x35, 0x2e, 0xf5, -0x2e, 0xab, 0x2d, 0xfa, 0xa9, 0x2f, 0x74, 0x11, 0x12, 0x13, 0xfb, 0x74, 0x01, 0x25, 0x2f, 0xf5, -0x2f, 0xe4, 0x35, 0x2e, 0xf5, 0x2e, 0x90, 0xff, 0x06, 0xe0, 0xab, 0x2d, 0xaa, 0x2e, 0xa9, 0x2f, -0x12, 0x13, 0xfb, 0x74, 0x01, 0x25, 0x2f, 0xf5, 0x2f, 0xe4, 0x35, 0x2e, 0xf5, 0x2e, 0xab, 0x2d, -0xfa, 0xa9, 0x2f, 0xe4, 0x12, 0x13, 0xfb, 0x04, 0x25, 0x2f, 0xf5, 0x2f, 0xe4, 0x35, 0x2e, 0xf5, -0x2e, 0xab, 0x2d, 0xfa, 0xa9, 0x2f, 0xe4, 0x12, 0x13, 0xfb, 0x04, 0x25, 0x2f, 0xf5, 0x2f, 0xe4, -0x35, 0x2e, 0xf5, 0x2e, 0x90, 0xff, 0x04, 0xe0, 0xab, 0x2d, 0xaa, 0x2e, 0xa9, 0x2f, 0x12, 0x13, -0xfb, 0x74, 0x01, 0x25, 0x2f, 0xf5, 0x2f, 0xe4, 0x35, 0x2e, 0xf5, 0x2e, 0x90, 0xff, 0x05, 0xe0, -0xab, 0x2d, 0xaa, 0x2e, 0xa9, 0x2f, 0x12, 0x13, 0xfb, 0x74, 0x01, 0x25, 0x2f, 0xf5, 0x2f, 0xe4, -0x35, 0x2e, 0xf5, 0x2e, 0x22, 0xf5, 0x83, 0xe0, 0x54, 0x08, 0xab, 0x2d, 0xaa, 0x2e, 0xa9, 0x2f, -0x22, 0xf5, 0x83, 0xef, 0xf0, 0xfd, 0x7c, 0x00, 0xc3, 0x78, 0x81, 0xe6, 0x9d, 0xf6, 0x18, 0xe6, -0x9c, 0xf6, 0xe6, 0xfe, 0x08, 0xe6, 0x78, 0x03, 0x22, 0x75, 0x2d, 0x01, 0x75, 0x2e, 0xf9, 0x75, -0x2f, 0x6e, 0x22, 0x90, 0xfa, 0xba, 0xe0, 0x90, 0xfa, 0xb6, 0xf0, 0x90, 0xfa, 0xb5, 0xe0, 0x24, -0xfc, 0x22, 0x90, 0xfa, 0xb8, 0xe0, 0xff, 0x7e, 0x00, 0xc3, 0x90, 0xfa, 0xbc, 0xe0, 0x9f, 0xf0, -0x90, 0xfa, 0xbb, 0xe0, 0x9e, 0xf0, 0x90, 0xfa, 0xb3, 0xee, 0x8f, 0xf0, 0x12, 0x14, 0x2f, 0xef, -0x25, 0x55, 0xf5, 0x55, 0xee, 0x35, 0x54, 0xf5, 0x54, 0x22, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb0, -0x90, 0xfa, 0xb3, 0xe0, 0xf5, 0x40, 0xa3, 0xe0, 0xf5, 0x41, 0x22, 0x78, 0x82, 0xe6, 0xfe, 0x08, -0xe6, 0x8e, 0x83, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5, 0x83, 0x22, 0x54, 0x0f, 0x75, -0xf0, 0x08, 0xa4, 0x24, 0x40, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0x22, 0xe5, 0x53, 0x75, -0xf0, 0x08, 0xa4, 0x24, 0x48, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0x22, 0xe5, 0x53, 0x75, 0xf0, 0x08, -0xa4, 0x24, 0x08, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0x22, 0x90, 0xff, 0x00, 0xe0, 0x54, 0x1f, 0x22, -0x90, 0xfa, 0xb5, 0xe0, 0xff, 0x24, 0xfc, 0x22, 0x75, 0x2a, 0x00, 0x8f, 0x2b, 0x90, 0xf9, 0x6b, -0x12, 0x14, 0xfd, 0x90, 0x00, 0x02, 0x22, 0x54, 0x0f, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x00, 0xf5, -0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0x22, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x41, 0xf5, 0x82, 0xe4, -0x34, 0xff, 0xf5, 0x83, 0x22, 0x74, 0x80, 0xf0, 0x08, 0xe6, 0xff, 0xe9, 0x75, 0xf0, 0x08, 0xa4, -0x22, 0x74, 0xae, 0x25, 0x36, 0xf5, 0x82, 0xe4, 0x34, 0xfa, 0xf5, 0x83, 0x22, 0x75, 0xf0, 0x08, -0xa4, 0x24, 0x42, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0x74, 0x80, 0xf0, 0x22, 0x90, 0xff, -0x82, 0xe0, 0x44, 0x08, 0xf0, 0x22, 0x90, 0xff, 0xfe, 0xe0, 0x44, 0x03, 0xf0, 0x90, 0xff, 0xfc, -0xe0, 0x54, 0xfd, 0xf0, 0x22, 0x78, 0x6d, 0xe6, 0x54, 0xfd, 0xf6, 0x90, 0xff, 0xfd, 0x74, 0x65, -0xf0, 0x22, 0x12, 0x14, 0xdf, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x4e, 0x22, 0x7b, 0x01, 0x7a, 0xf9, -0x79, 0x6e, 0x22, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb3, 0x22, 0x90, 0xff, 0x80, 0xe0, 0x44, 0x08, -0xf0, 0x22, 0x90, 0xff, 0x83, 0xe0, 0x54, 0x7f, 0xf0, 0x22, 0xe0, 0xff, 0x90, 0xf9, 0x66, 0x02, -0x14, 0xfd, 0x75, 0x30, 0x01, 0x75, 0x31, 0x09, 0x22, 0xd3, 0xe5, 0x35, 0x94, 0x08, 0xe5, 0x34, -0x94, 0x01, 0x22, 0x90, 0xfa, 0xba, 0xe0, 0xff, 0x90, 0xfa, 0xb6, 0xf0, 0x22, 0x90, 0xff, 0xa4, -0xe0, 0x54, 0xef, 0x22, 0x90, 0xff, 0xb4, 0xe0, 0x54, 0xef, 0x22, 0x8f, 0x38, 0x12, 0x27, 0x19, -0x78, 0x86, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x8e, 0x83, 0x24, 0x08, 0x12, 0x20, 0x25, 0xe0, 0xfd, -0x12, 0x20, 0xa6, 0x8a, 0x83, 0x24, 0x0a, 0x12, 0x20, 0x25, 0xed, 0xf0, 0x12, 0x20, 0x7c, 0x24, -0x07, 0x12, 0x20, 0x25, 0xe0, 0xff, 0x12, 0x20, 0xbe, 0x24, 0x09, 0x12, 0x20, 0x25, 0xef, 0xf0, -0x90, 0xf9, 0x65, 0xe0, 0x30, 0xe4, 0x20, 0x08, 0x12, 0x20, 0x2f, 0xc0, 0x83, 0xc0, 0x82, 0xa3, -0xe0, 0x25, 0xe0, 0xff, 0x05, 0x82, 0xd5, 0x82, 0x02, 0x15, 0x83, 0x15, 0x82, 0xe0, 0x33, 0xd0, -0x82, 0xd0, 0x83, 0xf0, 0xa3, 0xef, 0xf0, 0x78, 0x86, 0x12, 0x20, 0x2f, 0xe0, 0xfc, 0xa3, 0xe0, -0xfd, 0xec, 0xff, 0x12, 0x20, 0xa6, 0x8a, 0x83, 0x24, 0x08, 0x12, 0x20, 0x25, 0xef, 0xf0, 0xed, -0x12, 0x20, 0xbe, 0x24, 0x07, 0x12, 0x20, 0x25, 0xed, 0xf0, 0x8b, 0x82, 0x8a, 0x83, 0xa3, 0xa3, -0xe0, 0xff, 0x53, 0x07, 0xc7, 0x08, 0xe6, 0xfc, 0x08, 0xe6, 0xfd, 0x12, 0x20, 0x69, 0xa3, 0xe0, -0x30, 0xe3, 0x12, 0x8d, 0x82, 0x8c, 0x83, 0xe5, 0x82, 0x24, 0x05, 0x12, 0x20, 0x25, 0xe0, 0x90, -0x2f, 0x4d, 0x93, 0x42, 0x07, 0x53, 0x07, 0xfb, 0x12, 0x20, 0xae, 0x24, 0x06, 0x12, 0x20, 0x25, -0xe0, 0x60, 0x03, 0x43, 0x07, 0x04, 0x53, 0x07, 0xfc, 0x78, 0x86, 0x12, 0x20, 0x96, 0x24, 0x04, -0x12, 0x20, 0x25, 0xe0, 0x42, 0x07, 0x43, 0x07, 0x80, 0x12, 0x20, 0xa6, 0xf5, 0x82, 0x8a, 0x83, -0xa3, 0xa3, 0xef, 0xf0, 0x12, 0x20, 0xbe, 0x24, 0x04, 0x12, 0x20, 0x25, 0xe0, 0xff, 0x8d, 0x82, -0x8c, 0x83, 0xa3, 0xa3, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0x30, 0xe1, 0x05, 0x53, 0x07, 0xdf, 0x80, -0x03, 0x43, 0x07, 0x20, 0xec, 0x30, 0xe4, 0x05, 0x53, 0x07, 0xef, 0x80, 0x03, 0x43, 0x07, 0x10, -0x90, 0xf9, 0x65, 0xe0, 0xfe, 0x54, 0x03, 0x60, 0x4c, 0x53, 0x07, 0xdf, 0xee, 0x30, 0xe1, 0x42, -0x12, 0x20, 0xae, 0x24, 0x09, 0x12, 0x20, 0x25, 0xe0, 0x14, 0x60, 0x31, 0x14, 0x60, 0x29, 0x14, -0x60, 0x26, 0x14, 0x60, 0x28, 0x24, 0x04, 0x70, 0x2c, 0xe5, 0x38, 0xb4, 0x03, 0x0d, 0x30, 0x95, -0x05, 0x43, 0x07, 0x02, 0x80, 0x1f, 0x53, 0x07, 0xfd, 0x80, 0x1a, 0x30, 0x93, 0x05, 0x43, 0x07, -0x02, 0x80, 0x12, 0x53, 0x07, 0xfd, 0x80, 0x0d, 0x43, 0x07, 0x02, 0x80, 0x08, 0x53, 0x07, 0xfd, -0x80, 0x03, 0x53, 0x07, 0xfd, 0x12, 0x20, 0x94, 0x24, 0x04, 0x12, 0x20, 0x25, 0xef, 0xf0, 0x8d, -0x82, 0x8c, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0xff, 0x90, 0xf9, 0x65, 0xe0, 0xfe, 0x54, 0x03, 0x60, -0x4a, 0xee, 0x30, 0xe1, 0x43, 0x08, 0x12, 0x20, 0xb0, 0x24, 0x09, 0x12, 0x20, 0x25, 0xe0, 0x14, -0x60, 0x2c, 0x14, 0x60, 0x2e, 0x14, 0x60, 0x26, 0x14, 0x60, 0x28, 0x24, 0x04, 0x70, 0x2c, 0xe5, -0x38, 0xb4, 0x03, 0x0d, 0x30, 0x94, 0x05, 0x53, 0x07, 0x7f, 0x80, 0x1f, 0x43, 0x07, 0x80, 0x80, -0x1a, 0x30, 0x92, 0x05, 0x53, 0x07, 0x7f, 0x80, 0x12, 0x43, 0x07, 0x80, 0x80, 0x0d, 0x53, 0x07, -0x7f, 0x80, 0x08, 0x43, 0x07, 0x80, 0x80, 0x03, 0x53, 0x07, 0x7f, 0x78, 0x86, 0x12, 0x20, 0x65, -0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0x30, 0xe0, 0x05, 0x43, 0x07, 0x20, 0x80, 0x03, 0x53, 0x07, 0xdf, -0xec, 0x30, 0xe3, 0x05, 0x43, 0x07, 0x40, 0x80, 0x03, 0x53, 0x07, 0xbf, 0xec, 0x30, 0xe0, 0x05, -0x43, 0x07, 0x10, 0x80, 0x03, 0x53, 0x07, 0xef, 0xed, 0x30, 0xe4, 0x05, 0x43, 0x07, 0x08, 0x80, -0x03, 0x53, 0x07, 0xf7, 0xed, 0x30, 0xe5, 0x05, 0x43, 0x07, 0x04, 0x80, 0x03, 0x53, 0x07, 0xfb, -0xed, 0x30, 0xe6, 0x05, 0x43, 0x07, 0x01, 0x80, 0x03, 0x53, 0x07, 0xfe, 0xed, 0x30, 0xe7, 0x05, -0x43, 0x07, 0x02, 0x80, 0x03, 0x53, 0x07, 0xfd, 0x78, 0x84, 0x12, 0x20, 0x65, 0xa3, 0xef, 0xf0, -0x12, 0x2f, 0x80, 0x7f, 0x00, 0x22, 0x12, 0x0f, 0x89, 0x78, 0x8e, 0xef, 0xf6, 0x12, 0x27, 0x19, -0x12, 0x20, 0x70, 0x8e, 0x83, 0x24, 0x09, 0x12, 0x20, 0x25, 0xe0, 0xfd, 0x12, 0x20, 0x53, 0x90, -0x00, 0x0a, 0x12, 0x20, 0x78, 0x24, 0x0a, 0x12, 0x20, 0x25, 0xe0, 0x90, 0x00, 0x0b, 0x12, 0x14, -0x0d, 0x12, 0x20, 0x70, 0xf5, 0x82, 0x8e, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0xf5, 0x59, 0x12, 0x20, -0x7c, 0x24, 0x04, 0x12, 0x20, 0x25, 0xe0, 0xf5, 0x5a, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0xa3, 0xe0, -0xf5, 0x5b, 0xe5, 0x59, 0xc4, 0x13, 0x13, 0x13, 0x54, 0x01, 0x78, 0x8e, 0xf6, 0xd3, 0x94, 0x00, -0x40, 0x06, 0xe5, 0x5a, 0x30, 0xe1, 0x01, 0x06, 0x78, 0x8e, 0xe6, 0x12, 0x20, 0x52, 0x90, 0x00, -0x0c, 0xef, 0x12, 0x14, 0x0d, 0x78, 0x86, 0x12, 0x20, 0x2f, 0xa3, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, -0xff, 0x53, 0x07, 0x0c, 0x53, 0x06, 0xe6, 0xe5, 0x59, 0x30, 0xe5, 0x03, 0x43, 0x07, 0x01, 0xe5, -0x5a, 0x20, 0xe5, 0x0e, 0xe5, 0x59, 0x54, 0x7f, 0x70, 0x08, 0xe5, 0x59, 0x20, 0xe7, 0x03, 0x43, -0x07, 0x02, 0xe5, 0x59, 0x30, 0xe3, 0x03, 0x43, 0x07, 0x10, 0xe5, 0x59, 0x30, 0xe2, 0x03, 0x43, -0x07, 0x20, 0xe5, 0x59, 0x54, 0x03, 0x60, 0x03, 0x43, 0x07, 0x40, 0xe5, 0x59, 0x30, 0xe1, 0x03, -0x43, 0x07, 0x80, 0xe5, 0x59, 0x30, 0xe4, 0x03, 0x43, 0x06, 0x01, 0xe5, 0x59, 0x30, 0xe6, 0x03, -0x43, 0x06, 0x08, 0xe5, 0x5a, 0x20, 0xe4, 0x0e, 0xe5, 0x59, 0x54, 0x7f, 0x70, 0x08, 0xe5, 0x59, -0x20, 0xe7, 0x03, 0x43, 0x06, 0x10, 0x53, 0x07, 0xfb, 0x53, 0x06, 0x79, 0x90, 0x00, 0x05, 0xee, -0x8f, 0xf0, 0x12, 0x14, 0xb2, 0xe5, 0x5b, 0x30, 0xe3, 0x12, 0x54, 0x30, 0xff, 0xc4, 0x54, 0x0f, -0x12, 0x20, 0x52, 0x90, 0x00, 0x08, 0xef, 0x12, 0x14, 0x0d, 0x80, 0x0a, 0x12, 0x20, 0x53, 0x90, -0x00, 0x08, 0xe4, 0x12, 0x14, 0x0d, 0xe5, 0x5b, 0x54, 0x03, 0x12, 0x20, 0x52, 0x90, 0x00, 0x07, -0xef, 0x12, 0x14, 0x0d, 0xe5, 0x5b, 0x54, 0x04, 0xff, 0xc3, 0x13, 0x90, 0x00, 0x09, 0x12, 0x14, -0x0d, 0x90, 0x00, 0x07, 0x12, 0x13, 0xce, 0x70, 0x13, 0x12, 0x20, 0x53, 0xe9, 0x24, 0x09, 0xf9, -0xe4, 0x3a, 0xfa, 0x12, 0x13, 0xb5, 0xff, 0xc3, 0x13, 0x12, 0x13, 0xfb, 0x12, 0x20, 0x94, 0x24, -0x08, 0x12, 0x20, 0x25, 0xe0, 0xfe, 0x8d, 0x82, 0x8c, 0x83, 0xe5, 0x82, 0x24, 0x07, 0x12, 0x20, -0x25, 0xe0, 0xfd, 0xee, 0xed, 0x12, 0x20, 0x52, 0x90, 0x00, 0x03, 0xee, 0x8f, 0xf0, 0x12, 0x14, -0xb2, 0x12, 0x2f, 0x80, 0x7d, 0x0a, 0xe4, 0xff, 0x12, 0x2c, 0xc0, 0x02, 0x10, 0x0c, 0x90, 0xfa, -0xe2, 0xe0, 0xb4, 0x03, 0x06, 0x7e, 0x00, 0x7f, 0x40, 0x80, 0x04, 0x7e, 0x00, 0x7f, 0x08, 0x90, -0xfa, 0xd6, 0xee, 0xf0, 0xa3, 0xef, 0xf0, 0x90, 0x00, 0x05, 0x12, 0x13, 0xce, 0xff, 0x7e, 0x00, -0x90, 0xfa, 0xd2, 0xee, 0xf0, 0xa3, 0xef, 0xf0, 0x70, 0x03, 0x7f, 0x08, 0x22, 0x90, 0x00, 0x08, -0x12, 0x14, 0x5b, 0xff, 0x90, 0xfa, 0xd4, 0xe5, 0xf0, 0xf0, 0xa3, 0xef, 0xf0, 0xae, 0x02, 0xaf, -0x01, 0x8e, 0x56, 0x8f, 0x57, 0x74, 0x0a, 0x25, 0x57, 0xf5, 0x57, 0xe4, 0x35, 0x56, 0xf5, 0x56, -0x90, 0xfa, 0xd7, 0xe0, 0xff, 0x14, 0xfe, 0x90, 0xfa, 0xd5, 0xe0, 0x5e, 0xfe, 0xc3, 0xef, 0x9e, -0xff, 0x90, 0xfa, 0xd9, 0xf0, 0xc3, 0x90, 0xfa, 0xd3, 0xe0, 0x9f, 0x90, 0xfa, 0xd2, 0xe0, 0x94, -0x00, 0x50, 0x06, 0xa3, 0xe0, 0x90, 0xfa, 0xd9, 0xf0, 0x12, 0x1e, 0x2d, 0x60, 0x03, 0xe0, 0xff, -0x22, 0x12, 0x2a, 0x80, 0x90, 0xfa, 0xd2, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x4e, 0x60, 0x2b, 0x90, -0xfa, 0xd6, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xd3, 0xef, 0x9d, 0xee, 0x9c, 0x40, 0x07, 0xe0, 0x90, -0xfa, 0xd9, 0xf0, 0x80, 0x08, 0x90, 0xfa, 0xd3, 0xe0, 0x90, 0xfa, 0xd9, 0xf0, 0x12, 0x1e, 0x2d, -0x60, 0x03, 0xe0, 0xff, 0x22, 0x12, 0x2a, 0x80, 0x80, 0xca, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x58, -0xe4, 0xf5, 0x40, 0xf5, 0x41, 0x7d, 0x01, 0x12, 0x23, 0xee, 0x7f, 0x00, 0x22, 0xaa, 0x56, 0xa9, -0x57, 0x7b, 0x01, 0x90, 0xfa, 0xd4, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0x90, 0xfa, 0xd9, 0xe0, 0xf5, -0x50, 0x12, 0x26, 0x25, 0x90, 0xfa, 0xd8, 0xef, 0xf0, 0x22, 0xef, 0x24, 0xae, 0x60, 0x52, 0x24, -0xfe, 0x60, 0x2e, 0x24, 0xfe, 0x70, 0x03, 0x02, 0x1e, 0xed, 0x24, 0x06, 0x60, 0x03, 0x02, 0x1f, -0x35, 0x78, 0x77, 0xe6, 0x54, 0xfb, 0xf6, 0x90, 0xff, 0xa5, 0xe0, 0xf5, 0x36, 0x44, 0x0f, 0xf0, -0x74, 0x33, 0x90, 0xfa, 0x90, 0xf0, 0xe5, 0x36, 0xa3, 0xf0, 0x90, 0xfa, 0xae, 0x74, 0x01, 0xf0, -0x22, 0x78, 0x78, 0xe6, 0x54, 0xfb, 0xf6, 0x90, 0xff, 0xb5, 0xe0, 0xf5, 0x36, 0x44, 0x0f, 0xf0, -0x74, 0x43, 0x90, 0xfa, 0x92, 0xf0, 0xe5, 0x36, 0xa3, 0xf0, 0x90, 0xfa, 0xaf, 0x74, 0x01, 0xf0, -0x22, 0x90, 0xfa, 0x9c, 0xe0, 0xa3, 0x20, 0xe5, 0x03, 0x02, 0x1f, 0x35, 0x90, 0xff, 0xa6, 0xe0, -0x90, 0xfa, 0xc9, 0xf0, 0xa3, 0xf0, 0x90, 0xfa, 0xc9, 0xe0, 0xff, 0x54, 0x0f, 0xfe, 0x60, 0x10, -0x90, 0xff, 0xa6, 0x12, 0x20, 0x83, 0x90, 0xff, 0xa6, 0xe0, 0x90, 0xfa, 0xc9, 0xf0, 0x80, 0xe6, -0x90, 0xfa, 0xca, 0xe0, 0xff, 0x74, 0x34, 0xfe, 0x12, 0x29, 0xda, 0xef, 0x70, 0x57, 0x90, 0xfa, -0xca, 0xe0, 0xff, 0x74, 0x34, 0x90, 0xfa, 0x94, 0xf0, 0xef, 0xa3, 0xf0, 0x22, 0x90, 0xfa, 0xa6, -0xe0, 0xa3, 0x30, 0xe5, 0x40, 0x90, 0xff, 0xb6, 0xe0, 0x90, 0xfa, 0xc9, 0xf0, 0xa3, 0xf0, 0x90, -0xfa, 0xc9, 0xe0, 0xff, 0x54, 0x0f, 0xfe, 0x60, 0x10, 0x90, 0xff, 0xb6, 0x12, 0x20, 0x83, 0x90, -0xff, 0xb6, 0xe0, 0x90, 0xfa, 0xc9, 0xf0, 0x80, 0xe6, 0x90, 0xfa, 0xca, 0xe0, 0xff, 0x74, 0x44, -0xfe, 0x12, 0x29, 0xda, 0xef, 0x70, 0x0e, 0x90, 0xfa, 0xca, 0xe0, 0xff, 0x74, 0x44, 0x90, 0xfa, -0x96, 0xf0, 0xef, 0xa3, 0xf0, 0x22, 0xc0, 0xe0, 0xc0, 0xf0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, -0x75, 0xd0, 0x00, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x04, 0xc0, 0x05, 0xc0, -0x06, 0xc0, 0x07, 0x90, 0xff, 0x92, 0xe0, 0xff, 0x90, 0xfa, 0xc8, 0xf0, 0x90, 0xff, 0x92, 0xe4, -0xf0, 0xef, 0x12, 0x15, 0x0f, 0x1f, 0xed, 0x26, 0x1f, 0xed, 0x2e, 0x1f, 0x90, 0x30, 0x1f, 0x90, -0x32, 0x1f, 0x9e, 0x38, 0x1f, 0xb0, 0x3a, 0x1f, 0xe2, 0x3e, 0x1f, 0xcd, 0x44, 0x1f, 0xc2, 0x46, -0x1f, 0xd8, 0x50, 0x1f, 0xd8, 0x52, 0x1f, 0xd8, 0x54, 0x1f, 0xd8, 0x56, 0x00, 0x00, 0x1f, 0xf2, -0x90, 0xfa, 0xc8, 0xe0, 0xfd, 0x7c, 0x00, 0x7f, 0x01, 0x12, 0x10, 0x9c, 0x80, 0x62, 0x7c, 0x00, -0x7d, 0x01, 0x7f, 0x03, 0x12, 0x10, 0x9c, 0x90, 0xff, 0xfe, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x50, -0x7c, 0x00, 0x7d, 0x01, 0x7f, 0x02, 0x12, 0x10, 0x9c, 0x90, 0xff, 0xfe, 0xe0, 0x44, 0x40, 0xf0, -0x80, 0x3e, 0x7c, 0x00, 0x7d, 0x01, 0x7f, 0x05, 0x12, 0x10, 0x9c, 0x80, 0x33, 0x7c, 0x00, 0x7d, -0x01, 0x7f, 0x06, 0x12, 0x10, 0x9c, 0x80, 0x28, 0x90, 0xfa, 0xc8, 0xe0, 0xff, 0x12, 0x1e, 0x4a, -0x80, 0x1e, 0x7c, 0x00, 0x7d, 0x01, 0x7f, 0x04, 0x12, 0x10, 0x9c, 0x80, 0x13, 0x12, 0x25, 0x13, -0x80, 0x0e, 0x90, 0xfa, 0xc8, 0xe0, 0x24, 0x00, 0xff, 0xe4, 0x34, 0xff, 0xfe, 0x12, 0x29, 0xda, -0xd0, 0x07, 0xd0, 0x06, 0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x03, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, -0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xf0, 0xd0, 0xe0, 0x32, 0x78, 0x82, 0xe6, 0xfe, 0x08, -0xe6, 0x24, 0x04, 0x8e, 0x83, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5, 0x83, 0x22, 0x78, 0x82, 0xe6, -0xfe, 0x08, 0xe6, 0xf5, 0x82, 0x8e, 0x83, 0x22, 0x78, 0x86, 0xe6, 0xfe, 0x08, 0xe6, 0xaa, 0x06, -0xf8, 0xac, 0x02, 0x7d, 0x01, 0x7b, 0xff, 0x7a, 0x2f, 0x79, 0x52, 0x7e, 0x00, 0x7f, 0x0a, 0x02, -0x13, 0x8f, 0xff, 0x90, 0xf9, 0x6b, 0x02, 0x14, 0xfd, 0x90, 0xf9, 0x66, 0x12, 0x14, 0xfd, 0x90, -0x00, 0x04, 0x02, 0x13, 0xce, 0xe6, 0xfc, 0x08, 0xe6, 0xf5, 0x82, 0x8c, 0x83, 0xa3, 0xa3, 0x22, -0x78, 0x84, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x22, 0xed, 0x12, 0x14, 0x0d, 0x8f, 0x82, 0x8e, 0x83, -0xe5, 0x82, 0x22, 0xef, 0xf0, 0x90, 0xfa, 0xca, 0xe0, 0x54, 0x0f, 0x4e, 0xfe, 0xf0, 0xef, 0x54, -0xf0, 0x4e, 0xf0, 0x22, 0x78, 0x84, 0xe6, 0xfc, 0x08, 0xe6, 0xfd, 0x8c, 0x83, 0x22, 0xa6, 0x07, -0xe6, 0x24, 0x74, 0xf8, 0xe6, 0x22, 0x78, 0x84, 0xe6, 0xfa, 0x08, 0xe6, 0xfb, 0x22, 0x78, 0x86, -0xe6, 0xfc, 0x08, 0xe6, 0x8c, 0x83, 0x22, 0x26, 0xf6, 0x18, 0xee, 0x36, 0xf6, 0x22, 0x8b, 0x82, -0x8a, 0x83, 0xe5, 0x82, 0x22, 0x8b, 0x38, 0x8a, 0x39, 0x89, 0x3a, 0x8d, 0x3b, 0x90, 0xfa, 0xce, -0xe4, 0xf0, 0xa3, 0x74, 0x02, 0xf0, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xcd, 0x90, 0xfa, 0xce, 0xe0, -0xf5, 0x40, 0xa3, 0xe0, 0xf5, 0x41, 0x7d, 0x01, 0x12, 0x23, 0xee, 0x90, 0xfa, 0xcd, 0xe0, 0x65, -0x3b, 0x60, 0x46, 0xa3, 0xe0, 0xff, 0xa3, 0xe0, 0xa3, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x12, 0x21, -0x54, 0x90, 0xfa, 0xcd, 0xe0, 0xff, 0x90, 0xfa, 0xd0, 0xe4, 0x8f, 0xf0, 0x12, 0x14, 0x2f, 0x12, -0x21, 0x54, 0x90, 0xfa, 0xd0, 0xe0, 0xff, 0xa3, 0xe0, 0x90, 0xfa, 0xce, 0xcf, 0xf0, 0xa3, 0xef, -0xf0, 0x90, 0xfa, 0xcd, 0xe0, 0xa3, 0x75, 0xf0, 0x00, 0x12, 0x14, 0x2f, 0x90, 0xfa, 0xce, 0xe4, -0x75, 0xf0, 0x04, 0x12, 0x14, 0x2f, 0x02, 0x20, 0xd6, 0x90, 0xfa, 0xcf, 0xe0, 0x24, 0x01, 0xff, -0x90, 0xfa, 0xce, 0xe0, 0x34, 0x00, 0xab, 0x38, 0xaa, 0x39, 0xa9, 0x3a, 0x8f, 0xf0, 0x12, 0x14, -0x93, 0x7f, 0x00, 0x22, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xcd, 0x90, 0xfa, 0xce, 0xe4, 0x75, 0xf0, -0x01, 0x12, 0x14, 0x2f, 0x85, 0xf0, 0x41, 0xf5, 0x40, 0x7d, 0x01, 0x02, 0x23, 0xee, 0x8f, 0x68, -0x12, 0x27, 0x19, 0x12, 0x20, 0x70, 0x8e, 0x83, 0x24, 0x0b, 0x12, 0x20, 0x25, 0xe0, 0x54, 0xfb, -0xf0, 0x44, 0x02, 0xf0, 0x08, 0x12, 0x20, 0x65, 0xe0, 0xa3, 0x30, 0xe5, 0x0c, 0x12, 0x20, 0x7c, -0x24, 0x0b, 0x12, 0x20, 0x25, 0xe0, 0x44, 0x01, 0xf0, 0x78, 0x82, 0xe6, 0xfe, 0x08, 0xe6, 0xff, -0xf5, 0x82, 0x8e, 0x83, 0xe0, 0x54, 0xb8, 0xfd, 0xf0, 0xe5, 0x68, 0x24, 0xfe, 0x44, 0x20, 0xfc, -0x4d, 0xf0, 0xe5, 0x82, 0x24, 0x04, 0x12, 0x20, 0x25, 0xe0, 0x54, 0xb8, 0xf0, 0x4c, 0xf0, 0x8f, -0x82, 0x8e, 0x83, 0xa3, 0x74, 0x03, 0xf0, 0x18, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x8e, 0x83, 0x24, -0x05, 0x12, 0x20, 0x25, 0xc0, 0x83, 0xc0, 0x82, 0xe0, 0xfd, 0x74, 0x95, 0x25, 0x68, 0xf5, 0x82, -0xe4, 0x34, 0xfa, 0xf5, 0x83, 0xe0, 0x54, 0xfc, 0x44, 0x03, 0xfc, 0xed, 0x4c, 0xd0, 0x82, 0xd0, -0x83, 0xf0, 0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x44, 0x80, 0xf0, 0xe5, 0x82, 0x24, 0x04, 0x12, 0x20, -0x25, 0xe0, 0x44, 0x80, 0xf0, 0x12, 0x2f, 0x80, 0x74, 0x74, 0x25, 0x68, 0xf8, 0x74, 0x04, 0x46, -0xf6, 0x7f, 0x00, 0x22, 0x8b, 0x62, 0x8a, 0x63, 0x89, 0x64, 0x12, 0x2a, 0x62, 0x90, 0xfa, 0xbf, -0x12, 0x15, 0x06, 0xaa, 0x63, 0xa9, 0x64, 0x90, 0xfa, 0xc2, 0x12, 0x15, 0x06, 0x90, 0xfa, 0xc3, -0xe4, 0x75, 0xf0, 0x0a, 0x12, 0x14, 0x2f, 0x90, 0xfa, 0xc2, 0x12, 0x14, 0xfd, 0xe9, 0x24, 0x01, -0xf9, 0xe4, 0x3a, 0xfa, 0x90, 0xfa, 0xc5, 0x12, 0x15, 0x06, 0xab, 0x62, 0xaa, 0x63, 0xa9, 0x64, -0x12, 0x2a, 0x6e, 0xe0, 0xff, 0xc3, 0x13, 0xf0, 0xe4, 0x78, 0x88, 0xf6, 0x90, 0xfa, 0xbd, 0xe0, -0xff, 0x78, 0x88, 0xe6, 0xc3, 0x9f, 0x50, 0x4a, 0x90, 0xfa, 0xbf, 0x12, 0x2a, 0x43, 0xff, 0x78, -0x89, 0xf6, 0x90, 0xfa, 0xc2, 0x12, 0x2a, 0x43, 0xfe, 0xf4, 0x5f, 0xff, 0x78, 0x89, 0xf6, 0x12, -0x2a, 0x40, 0x5e, 0x4f, 0xff, 0x78, 0x89, 0xf6, 0x12, 0x2a, 0x49, 0x75, 0xf0, 0x02, 0x12, 0x14, -0x2f, 0x90, 0xfa, 0xc3, 0xe4, 0x75, 0xf0, 0x02, 0x12, 0x14, 0x2f, 0xab, 0x62, 0xaa, 0x63, 0xa9, -0x64, 0x90, 0x00, 0x04, 0x12, 0x13, 0xce, 0x30, 0xe4, 0x03, 0x12, 0x2a, 0x58, 0x78, 0x88, 0x06, -0x80, 0xaa, 0xe4, 0x90, 0xfa, 0xbe, 0xf0, 0x22, 0x8b, 0x5c, 0x8a, 0x5d, 0x89, 0x5e, 0x90, 0xfa, -0xbe, 0x74, 0x06, 0xf0, 0xe4, 0x90, 0xfa, 0xbd, 0xf0, 0x12, 0x13, 0xb5, 0x24, 0x6e, 0x60, 0x26, -0x14, 0x70, 0x70, 0x12, 0x2a, 0x2f, 0x60, 0x09, 0x24, 0x30, 0x70, 0x12, 0x12, 0x22, 0x14, 0x80, -0x62, 0x12, 0x2a, 0x79, 0x12, 0x1d, 0x5e, 0x90, 0xfa, 0xbe, 0xef, 0xf0, 0x80, 0x55, 0x90, 0xfa, -0xbe, 0x74, 0x81, 0xf0, 0x80, 0x4d, 0x12, 0x2a, 0x2f, 0x60, 0x09, 0x24, 0x30, 0x70, 0x3e, 0x12, -0x29, 0x85, 0x80, 0x3f, 0xe5, 0x5e, 0x24, 0x03, 0xf9, 0xe4, 0x35, 0x5d, 0xfa, 0x7b, 0x01, 0xc0, -0x03, 0xc0, 0x02, 0xc0, 0x01, 0x12, 0x2a, 0x79, 0x90, 0x00, 0x05, 0x12, 0x13, 0xce, 0xfd, 0x90, -0x00, 0x08, 0x12, 0x14, 0x5b, 0xf5, 0x41, 0x85, 0xf0, 0x40, 0xd0, 0x01, 0xd0, 0x02, 0xd0, 0x03, -0x12, 0x23, 0xee, 0x90, 0xfa, 0xbd, 0xef, 0xf0, 0xe4, 0xa3, 0xf0, 0x80, 0x06, 0x90, 0xfa, 0xbe, -0x74, 0x81, 0xf0, 0x90, 0xfa, 0xbe, 0xe0, 0x12, 0x2a, 0x79, 0x90, 0x00, 0x02, 0x12, 0x14, 0x0d, -0x90, 0xfa, 0xbd, 0xe0, 0xff, 0x22, 0x12, 0x0f, 0x89, 0x7f, 0x02, 0x12, 0x11, 0x9f, 0x78, 0x6d, -0xe6, 0x44, 0x02, 0xf6, 0xd2, 0xb0, 0xd2, 0xb1, 0xd2, 0xb3, 0x90, 0xff, 0xa4, 0xe0, 0x90, 0xfa, -0x7a, 0xf0, 0x90, 0xff, 0xb4, 0xe0, 0x90, 0xfa, 0x7b, 0xf0, 0x90, 0xff, 0xa2, 0xe0, 0x90, 0xfa, -0x78, 0xf0, 0x90, 0xff, 0xb2, 0xe0, 0x90, 0xfa, 0x79, 0xf0, 0x90, 0xff, 0xa4, 0x74, 0x30, 0xf0, -0x90, 0xff, 0xb4, 0xf0, 0x90, 0xff, 0xa2, 0x74, 0x40, 0xf0, 0x90, 0xff, 0xb2, 0xf0, 0x90, 0xfa, -0xe3, 0xe5, 0xa8, 0xf0, 0x75, 0xa8, 0x81, 0x90, 0xff, 0x92, 0xe0, 0x60, 0x04, 0xe4, 0xf0, 0x80, -0xf6, 0x90, 0xff, 0xfd, 0x74, 0x3a, 0xf0, 0x43, 0x87, 0x01, 0x00, 0x00, 0x00, 0x90, 0xfa, 0x7a, -0xe0, 0x90, 0xff, 0xa4, 0xf0, 0x90, 0xfa, 0x7b, 0xe0, 0x90, 0xff, 0xb4, 0xf0, 0x90, 0xfa, 0x78, -0xe0, 0x90, 0xff, 0xa2, 0xf0, 0x90, 0xfa, 0x79, 0xe0, 0x90, 0xff, 0xb2, 0xf0, 0x90, 0xf9, 0x15, -0xe0, 0x60, 0x02, 0xc2, 0xb3, 0x90, 0xfa, 0xe3, 0xe0, 0xf5, 0xa8, 0x02, 0x10, 0x0c, 0x8b, 0x3c, -0x8a, 0x3d, 0x89, 0x3e, 0x8d, 0x3f, 0xe5, 0x3f, 0x70, 0x03, 0xaf, 0x3f, 0x22, 0x12, 0x2a, 0xa8, -0x70, 0x16, 0x12, 0x2a, 0xc7, 0xe5, 0x40, 0x90, 0xff, 0xf1, 0xf0, 0x12, 0x2e, 0xd4, 0x50, 0xf2, -0x12, 0x24, 0x7b, 0x40, 0x0b, 0x7f, 0x00, 0x22, 0x12, 0x2a, 0xc7, 0x12, 0x24, 0x7b, 0x50, 0xf8, -0x90, 0xff, 0xf3, 0x74, 0xa1, 0xf0, 0xe5, 0x3f, 0xb4, 0x01, 0x07, 0x90, 0xff, 0xf0, 0xe0, 0x44, -0x02, 0xf0, 0x90, 0xff, 0xf1, 0xe4, 0xf0, 0xf5, 0x42, 0xe5, 0x3f, 0x14, 0xff, 0xe5, 0x42, 0xc3, -0x9f, 0x50, 0x2a, 0x12, 0x2e, 0xbd, 0x40, 0x03, 0xaf, 0x42, 0x22, 0xc3, 0xe5, 0x3f, 0x95, 0x42, -0xff, 0xbf, 0x02, 0x07, 0x90, 0xff, 0xf0, 0xe0, 0x44, 0x02, 0xf0, 0x12, 0x2a, 0xba, 0x05, 0x42, -0x74, 0x01, 0x25, 0x3e, 0xf5, 0x3e, 0xe4, 0x35, 0x3d, 0xf5, 0x3d, 0x80, 0xcc, 0x12, 0x2e, 0xbd, -0x40, 0x03, 0x7f, 0x18, 0x22, 0x12, 0x2a, 0xba, 0xaf, 0x3f, 0x22, 0x90, 0xff, 0xf1, 0xe5, 0x41, -0xf0, 0x02, 0x2e, 0xd4, 0x75, 0xa8, 0x40, 0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x75, 0x81, 0x91, -0x02, 0x24, 0xce, 0x02, 0x2e, 0x88, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, -0x80, 0x01, 0xf2, 0x08, 0xdf, 0xf4, 0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54, 0x07, 0x24, 0x0c, -0xc8, 0xc3, 0x33, 0xc4, 0x54, 0x0f, 0x44, 0x20, 0xc8, 0x83, 0x40, 0x04, 0xf4, 0x56, 0x80, 0x01, -0x46, 0xf6, 0xdf, 0xe4, 0x80, 0x0b, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x90, 0x28, -0xcb, 0xe4, 0x7e, 0x01, 0x93, 0x60, 0xbc, 0xa3, 0xff, 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, 0x1f, -0xfe, 0xe4, 0x93, 0xa3, 0x60, 0x01, 0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8, 0x40, 0xb8, -0xe4, 0x93, 0xa3, 0xfa, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, -0xc5, 0x83, 0xca, 0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, 0xde, -0xe7, 0x80, 0xbe, 0xe4, 0xf5, 0x36, 0x12, 0x19, 0x21, 0xe0, 0xb4, 0x04, 0x0d, 0xe5, 0x36, 0x24, -0x03, 0xff, 0x12, 0x2d, 0x4f, 0x12, 0x19, 0x21, 0xe4, 0xf0, 0x05, 0x36, 0xe5, 0x36, 0xc3, 0x94, -0x02, 0x40, 0xe3, 0xe4, 0xf5, 0x36, 0x75, 0xf0, 0x02, 0xe5, 0x36, 0x90, 0xfa, 0x90, 0x12, 0x19, -0x62, 0x60, 0x2c, 0x12, 0x29, 0xda, 0xef, 0x60, 0x52, 0x75, 0xf0, 0x02, 0xe5, 0x36, 0x90, 0xfa, -0x90, 0x12, 0x14, 0xdf, 0xe4, 0xf0, 0xa3, 0xf0, 0x75, 0xf0, 0x0a, 0xe5, 0x36, 0x90, 0xfa, 0x9c, -0x12, 0x14, 0xdf, 0xe0, 0xa3, 0x30, 0xe6, 0x33, 0x12, 0x19, 0x21, 0x74, 0x04, 0xf0, 0x22, 0x75, -0xf0, 0x02, 0xe5, 0x36, 0x90, 0xfa, 0x94, 0x12, 0x19, 0x62, 0x60, 0x16, 0x12, 0x29, 0xda, 0xef, -0x60, 0x19, 0x75, 0xf0, 0x02, 0xe5, 0x36, 0x90, 0xfa, 0x94, 0x12, 0x14, 0xdf, 0xe4, 0xf0, 0xa3, -0xf0, 0x22, 0x05, 0x36, 0xe5, 0x36, 0xc3, 0x94, 0x02, 0x40, 0x9b, 0x22, 0xe4, 0xff, 0x90, 0xff, -0x83, 0xe0, 0x54, 0x0f, 0xfe, 0xef, 0xc3, 0x9e, 0x50, 0x17, 0x74, 0xf0, 0x2f, 0xf5, 0x82, 0xe4, -0x34, 0xfe, 0xf5, 0x83, 0xe0, 0x12, 0x18, 0x2a, 0x12, 0x13, 0xfb, 0x0f, 0x12, 0x18, 0x19, 0x80, -0xdd, 0xef, 0xfd, 0xc3, 0xe5, 0x31, 0x9d, 0xf5, 0x31, 0xe5, 0x30, 0x94, 0x00, 0xf5, 0x30, 0xd3, -0xe5, 0x31, 0x94, 0x00, 0xe5, 0x30, 0x94, 0x00, 0x40, 0x06, 0xe4, 0x90, 0xff, 0x83, 0xf0, 0x22, -0x12, 0x19, 0x3e, 0x12, 0x19, 0x92, 0x12, 0x19, 0x8c, 0x12, 0x13, 0xb5, 0x24, 0x6e, 0x60, 0x1e, -0x14, 0x60, 0x1b, 0x24, 0x8e, 0x70, 0x2d, 0x90, 0x00, 0x01, 0x12, 0x13, 0xce, 0xff, 0x24, 0xfc, -0x60, 0x03, 0x04, 0x70, 0x1f, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0d, 0x02, 0x10, 0x9c, 0x12, 0x19, -0x6c, 0x12, 0x22, 0xb8, 0x12, 0x18, 0xe8, 0x12, 0x13, 0xce, 0x60, 0x03, 0x02, 0x2f, 0x76, 0xe4, -0xff, 0x12, 0x2f, 0x6a, 0x22, 0x8b, 0x4b, 0x8a, 0x4c, 0x89, 0x4d, 0x8c, 0x4e, 0x8d, 0x4f, 0xd2, -0x00, 0x12, 0x2a, 0xa8, 0x70, 0x16, 0x12, 0x2a, 0xc7, 0xe5, 0x4e, 0x90, 0xff, 0xf1, 0xf0, 0x12, -0x2e, 0xd4, 0x50, 0xf2, 0x12, 0x26, 0x9a, 0x40, 0x0b, 0x7f, 0x18, 0x22, 0x12, 0x2a, 0xc7, 0x12, -0x26, 0x9a, 0x50, 0xf8, 0xe4, 0xf5, 0x51, 0xe5, 0x50, 0x14, 0xff, 0xe5, 0x51, 0xc3, 0x9f, 0x50, -0x17, 0x12, 0x26, 0x8a, 0x40, 0x03, 0x7f, 0x18, 0x22, 0x05, 0x51, 0x74, 0x01, 0x25, 0x4d, 0xf5, -0x4d, 0xe4, 0x35, 0x4c, 0xf5, 0x4c, 0x80, 0xdf, 0x90, 0xff, 0xf0, 0xe0, 0x44, 0x01, 0xf0, 0x12, -0x26, 0x8a, 0x40, 0x03, 0x7f, 0x18, 0x22, 0x7f, 0x00, 0x22, 0xab, 0x4b, 0xaa, 0x4c, 0xa9, 0x4d, -0x12, 0x13, 0xb5, 0x90, 0xff, 0xf1, 0xf0, 0x02, 0x2e, 0xd4, 0x90, 0xff, 0xf1, 0xe5, 0x4f, 0xf0, -0x02, 0x2e, 0xd4, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xcb, 0xe4, 0xfd, 0x12, 0x20, 0xc5, 0x90, 0xfa, -0xcb, 0xe4, 0x75, 0xf0, 0x09, 0x12, 0x14, 0x2f, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x37, 0x90, 0xfa, -0xcb, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x14, 0x45, 0x85, 0xf0, 0x41, 0xf5, 0x40, 0x7d, 0x01, 0x12, -0x23, 0xee, 0x90, 0xff, 0xf7, 0xe5, 0x37, 0x12, 0x26, 0xfe, 0x90, 0xff, 0xf6, 0xe5, 0x37, 0xf0, -0x90, 0xfa, 0xcb, 0xe4, 0xf0, 0xa3, 0x74, 0x06, 0x12, 0x26, 0xfe, 0xe5, 0x37, 0x30, 0xe0, 0x07, -0x90, 0xff, 0xfc, 0x74, 0x94, 0xf0, 0x22, 0x90, 0xff, 0xfc, 0x74, 0x90, 0xf0, 0x22, 0xf0, 0x7b, -0x00, 0x7a, 0x00, 0x79, 0x37, 0x90, 0xfa, 0xcb, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x14, 0x45, 0x85, -0xf0, 0x41, 0xf5, 0x40, 0x7d, 0x01, 0x02, 0x23, 0xee, 0x15, 0x6b, 0xa8, 0x6b, 0xa6, 0x07, 0x30, -0x08, 0x05, 0x12, 0x10, 0xec, 0x80, 0xf8, 0xd2, 0x08, 0xa8, 0x6b, 0xe6, 0xff, 0xb4, 0x03, 0x0f, -0x78, 0x82, 0x76, 0xff, 0x08, 0x76, 0xe0, 0x08, 0x76, 0xff, 0x08, 0x76, 0xa0, 0x80, 0x0d, 0x78, -0x82, 0x76, 0xff, 0x08, 0x76, 0xe2, 0x08, 0x76, 0xff, 0x08, 0x76, 0xb0, 0x78, 0x86, 0x76, 0xfa, -0x08, 0x76, 0x9a, 0xef, 0x24, 0xfd, 0x75, 0xf0, 0x0a, 0xa4, 0xae, 0xf0, 0x12, 0x20, 0xb7, 0x7b, -0x01, 0x7a, 0xff, 0x79, 0x48, 0x78, 0x6e, 0x12, 0x14, 0xf4, 0xa8, 0x6b, 0xe6, 0x24, 0xfd, 0x75, -0xf0, 0x08, 0xa4, 0xff, 0xae, 0xf0, 0x78, 0x70, 0x12, 0x20, 0xb7, 0x79, 0x08, 0x78, 0x71, 0x12, -0x14, 0xf4, 0x78, 0x73, 0xef, 0x12, 0x20, 0xb7, 0x05, 0x6b, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0x54, -0xab, 0xf0, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0xfa, 0xe2, 0x74, 0x02, 0xf0, 0x7b, 0x01, 0x7a, 0xfa, -0x79, 0xcb, 0xe4, 0xf5, 0x40, 0xf5, 0x41, 0x7d, 0x01, 0x12, 0x23, 0xee, 0x7e, 0x00, 0x90, 0xfa, -0xe0, 0xee, 0xf0, 0xa3, 0xef, 0xf0, 0x64, 0x01, 0x70, 0x10, 0x90, 0xfa, 0xcb, 0xe0, 0xb4, 0x52, -0x09, 0x90, 0xf9, 0x65, 0xe0, 0x54, 0xef, 0xf0, 0x80, 0x29, 0x90, 0xfa, 0xe0, 0xe0, 0x70, 0x04, -0xa3, 0xe0, 0x64, 0x01, 0x70, 0x10, 0x90, 0xfa, 0xcb, 0xe0, 0xb4, 0x10, 0x09, 0x90, 0xf9, 0x65, -0xe0, 0x44, 0x10, 0xf0, 0x80, 0x0d, 0x90, 0xfa, 0xe2, 0x74, 0x03, 0xf0, 0x90, 0xf9, 0x65, 0xe0, -0x54, 0xef, 0xf0, 0x90, 0xff, 0xf0, 0xe0, 0x44, 0x20, 0xf0, 0x22, 0x90, 0xff, 0x93, 0x74, 0x2a, -0xf0, 0x90, 0xff, 0xff, 0xe0, 0x60, 0x06, 0x90, 0xff, 0xfc, 0x74, 0x10, 0xf0, 0x90, 0xff, 0x91, -0xe0, 0x44, 0x90, 0xf0, 0x12, 0x27, 0x8b, 0x12, 0x15, 0x35, 0x12, 0x2d, 0xa5, 0x7e, 0x07, 0x7f, -0xd0, 0x12, 0x11, 0x68, 0x7e, 0x0f, 0x7f, 0xa0, 0x12, 0x11, 0x82, 0xe4, 0x78, 0x7d, 0xf6, 0x78, -0x7d, 0xe6, 0xff, 0xc3, 0x94, 0x06, 0x50, 0x0b, 0x74, 0x74, 0x2f, 0xf8, 0xe4, 0xf6, 0x78, 0x7d, -0x06, 0x80, 0xec, 0x7f, 0x03, 0x12, 0x2c, 0x5b, 0x90, 0xf9, 0x65, 0xe0, 0x20, 0xe4, 0x05, 0x7f, -0x04, 0x12, 0x2c, 0x5b, 0x90, 0xff, 0x9b, 0xe4, 0xf0, 0x90, 0xff, 0x9a, 0xf0, 0x90, 0xff, 0xe8, -0xe0, 0x54, 0x1f, 0xf0, 0xd2, 0xa8, 0x22, 0x12, 0x0f, 0x89, 0x78, 0x90, 0xef, 0xf6, 0x12, 0x27, -0x19, 0x12, 0x20, 0x59, 0x30, 0xe0, 0x25, 0x12, 0x20, 0x2d, 0xe0, 0x54, 0x7f, 0xf0, 0x78, 0x71, -0x12, 0x14, 0xeb, 0x90, 0x00, 0x02, 0x12, 0x13, 0xce, 0x30, 0xe7, 0x09, 0x90, 0x00, 0x02, 0xe4, -0x12, 0x14, 0x0d, 0x80, 0xe9, 0x12, 0x20, 0x2d, 0xe0, 0x44, 0x80, 0xf0, 0x12, 0x20, 0x59, 0x30, -0xe1, 0x1e, 0x12, 0x20, 0x1b, 0xe0, 0x54, 0x7f, 0xf0, 0x12, 0x2f, 0x15, 0x78, 0x6e, 0x12, 0x14, -0xeb, 0x90, 0x00, 0x02, 0x74, 0x80, 0x12, 0x14, 0x0d, 0x12, 0x20, 0x1b, 0xe0, 0x44, 0x80, 0xf0, -0x12, 0x2f, 0x80, 0xe4, 0xff, 0x12, 0x2e, 0xa5, 0x02, 0x10, 0x0c, 0x03, 0x6e, 0x01, 0xff, 0x48, -0x03, 0x71, 0x01, 0xff, 0x08, 0x02, 0x6c, 0x00, 0x00, 0x44, 0xfa, 0x94, 0x00, 0x00, 0x00, 0x00, -0x44, 0xfa, 0x90, 0x00, 0x00, 0x00, 0x00, 0x42, 0xfa, 0xae, 0x00, 0x00, 0x42, 0xfa, 0x7a, 0x00, -0x00, 0x42, 0xfa, 0x78, 0x00, 0x00, 0x42, 0xf9, 0x69, 0xff, 0xff, 0x42, 0xfa, 0x76, 0x00, 0x00, -0x43, 0xf9, 0x16, 0x0a, 0x32, 0x02, 0x41, 0xf9, 0x63, 0x20, 0x41, 0xf9, 0x64, 0x20, 0x41, 0xf9, -0x61, 0x00, 0x41, 0xf9, 0x62, 0x00, 0x44, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0xf9, 0x65, -0x00, 0x41, 0xf9, 0x15, 0x00, 0x01, 0x20, 0x00, 0x41, 0xf8, 0x04, 0x00, 0x00, 0x12, 0x19, 0x82, -0xe5, 0x31, 0x64, 0x09, 0x70, 0x04, 0xe5, 0x30, 0x64, 0x01, 0x60, 0x48, 0xc3, 0xe5, 0x31, 0x94, -0x08, 0xe5, 0x30, 0x94, 0x00, 0x40, 0x11, 0x7f, 0x08, 0xef, 0xe5, 0x31, 0x94, 0x08, 0xf5, 0x31, -0xe5, 0x30, 0x94, 0x00, 0xf5, 0x30, 0x80, 0x05, 0xaf, 0x31, 0x12, 0x19, 0x92, 0xe4, 0xfe, 0xee, -0xc3, 0x9f, 0x50, 0x19, 0x12, 0x18, 0x2a, 0x12, 0x13, 0xb5, 0xfd, 0x74, 0xf8, 0x2e, 0xf5, 0x82, -0xe4, 0x34, 0xfe, 0xf5, 0x83, 0xed, 0xf0, 0x0e, 0x12, 0x18, 0x19, 0x80, 0xe2, 0xef, 0x54, 0x7f, -0x90, 0xff, 0x81, 0xf0, 0x22, 0x8b, 0x5f, 0x8a, 0x60, 0x89, 0x61, 0x12, 0x2a, 0x6e, 0x70, 0x05, -0xa3, 0x74, 0x08, 0xf0, 0x22, 0xab, 0x5f, 0xaa, 0x60, 0xa9, 0x61, 0x12, 0x2a, 0x62, 0x90, 0xfa, -0xc5, 0x12, 0x15, 0x06, 0xe5, 0x61, 0x24, 0x03, 0xf9, 0xe4, 0x35, 0x60, 0xfa, 0x90, 0xfa, 0xbf, -0x12, 0x15, 0x06, 0xe4, 0x90, 0xfa, 0xbe, 0xf0, 0x78, 0x91, 0xf6, 0x90, 0xfa, 0xbd, 0xe0, 0xff, -0x78, 0x91, 0xe6, 0xc3, 0x9f, 0x50, 0x12, 0x12, 0x2a, 0x40, 0xff, 0x12, 0x2a, 0x49, 0x12, 0x2a, -0x5c, 0x78, 0x91, 0x06, 0x12, 0x2a, 0x58, 0x80, 0xe2, 0x22, 0xad, 0x07, 0xac, 0x06, 0x90, 0x2f, -0x06, 0xe4, 0x93, 0xff, 0x78, 0x7a, 0xf6, 0x54, 0x0f, 0x12, 0x19, 0x07, 0xe0, 0x08, 0x76, 0x00, -0x08, 0xf6, 0x18, 0x12, 0x18, 0x42, 0xc3, 0x33, 0xce, 0x33, 0xce, 0xd8, 0xf9, 0xff, 0x78, 0x7b, -0xee, 0xf6, 0x08, 0xef, 0xf6, 0xee, 0x44, 0xf8, 0x18, 0xf6, 0xef, 0x08, 0xf6, 0x90, 0xff, 0x7a, -0xe0, 0x20, 0xe7, 0x03, 0x7f, 0x00, 0x22, 0x78, 0x7b, 0xe6, 0xfe, 0x08, 0xe6, 0xf5, 0x82, 0x8e, -0x83, 0xec, 0xf0, 0xa3, 0xed, 0xf0, 0x90, 0xff, 0x7a, 0x74, 0x02, 0xf0, 0x7f, 0x01, 0x22, 0xab, -0x5c, 0xaa, 0x5d, 0xa9, 0x5e, 0x90, 0x00, 0x03, 0x12, 0x13, 0xce, 0x54, 0xf0, 0x24, 0xa0, 0x22, -0x90, 0xfa, 0xc5, 0x12, 0x14, 0xfd, 0x02, 0x13, 0xb5, 0x90, 0xfa, 0xbf, 0x12, 0x14, 0xfd, 0xef, -0x12, 0x13, 0xfb, 0x90, 0xfa, 0xc6, 0xe4, 0x22, 0x90, 0xfa, 0xc0, 0xe4, 0x75, 0xf0, 0x01, 0x02, -0x14, 0x2f, 0x90, 0x00, 0x08, 0x12, 0x14, 0x5b, 0xaa, 0xf0, 0xf9, 0x7b, 0x01, 0x22, 0x90, 0x00, -0x05, 0x12, 0x13, 0xce, 0x90, 0xfa, 0xbd, 0xf0, 0x22, 0xab, 0x5c, 0xaa, 0x5d, 0xa9, 0x5e, 0x22, -0x90, 0xfa, 0xd9, 0xe0, 0xff, 0x7e, 0x00, 0xc3, 0x90, 0xfa, 0xd3, 0xe0, 0x9f, 0xf0, 0x90, 0xfa, -0xd2, 0xe0, 0x9e, 0xf0, 0x90, 0xfa, 0xd4, 0xee, 0x8f, 0xf0, 0x12, 0x14, 0x2f, 0xef, 0x25, 0x57, -0xf5, 0x57, 0xee, 0x35, 0x56, 0xf5, 0x56, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0x54, 0xfe, 0xf0, 0xe0, -0x54, 0xfd, 0xf0, 0x90, 0xfa, 0xe2, 0xe0, 0x64, 0x03, 0x22, 0x90, 0xff, 0xf2, 0xe0, 0xab, 0x3c, -0xaa, 0x3d, 0xa9, 0x3e, 0x02, 0x13, 0xfb, 0x90, 0xff, 0xf3, 0x74, 0xa0, 0xf0, 0x22, 0x8f, 0x6a, -0xed, 0x70, 0x0f, 0xe5, 0x6a, 0xb4, 0x03, 0x05, 0x7f, 0x01, 0x02, 0x2e, 0xeb, 0x7f, 0x02, 0x02, -0x2e, 0xeb, 0xaf, 0x6a, 0x12, 0x27, 0x19, 0x74, 0x74, 0x25, 0x6a, 0xf8, 0xe6, 0x30, 0xe2, 0x0b, -0xd2, 0x09, 0x12, 0x18, 0x9b, 0xe0, 0x54, 0x7f, 0xf0, 0x80, 0x02, 0xc2, 0x09, 0xe5, 0x6a, 0xb4, -0x03, 0x07, 0x7f, 0x81, 0x12, 0x2e, 0xeb, 0x80, 0x05, 0x7f, 0x82, 0x12, 0x2e, 0xeb, 0x30, 0x09, -0x07, 0x12, 0x18, 0x9b, 0xe0, 0x44, 0x80, 0xf0, 0x12, 0x2f, 0x80, 0x22, 0x12, 0x0f, 0x89, 0x90, -0xff, 0xfd, 0xe0, 0x44, 0x60, 0xf0, 0xd2, 0x01, 0x90, 0xff, 0xfc, 0xe0, 0x44, 0x02, 0xf0, 0x90, -0xff, 0x00, 0xe0, 0x30, 0xe7, 0x13, 0x90, 0xff, 0x83, 0xe0, 0x44, 0x80, 0xf0, 0x43, 0x2c, 0x80, -0x90, 0xff, 0xfc, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x0d, 0x12, 0x19, 0x3e, 0x53, 0x2c, 0x7f, 0x90, -0xff, 0xfc, 0xe0, 0x54, 0xfe, 0xf0, 0x90, 0xff, 0x81, 0xe0, 0x44, 0x80, 0xf0, 0x12, 0x02, 0x9f, -0x12, 0x19, 0x46, 0x02, 0x10, 0x0c, 0x12, 0x0f, 0x89, 0x78, 0x8a, 0x12, 0x20, 0x9e, 0x30, 0xe1, -0x07, 0x7f, 0x13, 0x12, 0x2e, 0xa5, 0x80, 0x34, 0x90, 0xf9, 0x65, 0xe0, 0x54, 0x03, 0x60, 0x16, -0x78, 0x8a, 0xe6, 0xb4, 0x03, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xdf, 0xf0, 0x80, 0x07, 0x90, -0xff, 0xb4, 0xe0, 0x54, 0xdf, 0xf0, 0xc2, 0xb3, 0x90, 0xf9, 0x15, 0xe0, 0x04, 0xf0, 0x78, 0x8a, -0xe6, 0xff, 0x12, 0x20, 0x59, 0xfd, 0x12, 0x2d, 0x21, 0x12, 0x2e, 0xa5, 0x02, 0x10, 0x0c, 0x12, -0x0f, 0x89, 0x78, 0x8f, 0xef, 0xf6, 0xd2, 0x00, 0x12, 0x27, 0x19, 0x90, 0xf9, 0x66, 0x12, 0x14, -0xfd, 0xe9, 0x24, 0x03, 0xf9, 0xe4, 0x3a, 0xfa, 0xc0, 0x02, 0x78, 0x86, 0xe6, 0xfe, 0x08, 0xe6, -0xaa, 0x06, 0xf8, 0xac, 0x02, 0x7d, 0x01, 0xd0, 0x02, 0x12, 0x20, 0x4b, 0x12, 0x2f, 0x80, 0x78, -0x8f, 0xe6, 0xff, 0x12, 0x19, 0xbb, 0x12, 0x2e, 0xa5, 0x02, 0x10, 0x0c, 0x12, 0x0f, 0x89, 0x78, -0x8b, 0xef, 0xf6, 0x12, 0x2e, 0x4c, 0x12, 0x2e, 0xa5, 0x90, 0xf9, 0x65, 0xe0, 0x54, 0x03, 0x60, -0x16, 0x78, 0x8b, 0xe6, 0xb4, 0x03, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x07, -0x90, 0xff, 0xb4, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0xf9, 0x15, 0xe0, 0x14, 0xf0, 0xe0, 0x70, 0x02, -0xd2, 0xb3, 0x02, 0x10, 0x0c, 0x8f, 0x69, 0x12, 0x27, 0x19, 0x12, 0x20, 0x2d, 0xe0, 0x54, 0x3f, -0xf0, 0xe5, 0x82, 0x24, 0x04, 0x12, 0x20, 0x25, 0xe0, 0x54, 0x3f, 0xf0, 0x08, 0xe6, 0xfe, 0x08, -0xe6, 0x8e, 0x83, 0x24, 0x0b, 0x12, 0x20, 0x25, 0xe0, 0x54, 0xf8, 0xf0, 0x12, 0x2f, 0x80, 0x74, -0x74, 0x25, 0x69, 0xf8, 0x74, 0xfb, 0x56, 0xf6, 0x7f, 0x00, 0x22, 0x8f, 0x37, 0xc2, 0x08, 0x12, -0x27, 0x19, 0x12, 0x20, 0x38, 0x78, 0x84, 0x12, 0x20, 0x1d, 0xe0, 0x44, 0x01, 0xf0, 0x12, 0x20, -0x70, 0x12, 0x20, 0x21, 0xe0, 0x20, 0xe0, 0xf6, 0xef, 0x24, 0x0b, 0xf5, 0x82, 0xe4, 0x3e, 0xf5, -0x83, 0xe0, 0x54, 0xf8, 0xf0, 0x12, 0x2f, 0x80, 0xaf, 0x37, 0x12, 0x19, 0xbb, 0x22, 0x12, 0x0f, -0x89, 0x12, 0x27, 0x19, 0x12, 0x20, 0x70, 0x24, 0x06, 0x12, 0x20, 0x23, 0xe0, 0xfd, 0x12, 0x20, -0x53, 0x90, 0x00, 0x03, 0x12, 0x20, 0x78, 0x24, 0x05, 0x12, 0x20, 0x25, 0xe0, 0x90, 0x00, 0x04, -0x12, 0x14, 0x0d, 0x12, 0x2f, 0x80, 0x7d, 0x02, 0xe4, 0xff, 0x12, 0x2c, 0xc0, 0x02, 0x10, 0x0c, -0xae, 0x05, 0x12, 0x18, 0xed, 0xef, 0x12, 0x14, 0x0d, 0x0e, 0x0e, 0x0e, 0xee, 0xd3, 0x95, 0x35, -0xe4, 0x95, 0x34, 0x40, 0x02, 0xae, 0x35, 0xee, 0xd3, 0x94, 0x08, 0x74, 0x80, 0x94, 0x81, 0x40, -0x0a, 0x7e, 0x03, 0x90, 0x00, 0x02, 0x74, 0x02, 0x12, 0x14, 0x0d, 0xaf, 0x06, 0x12, 0x2f, 0x6a, -0x22, 0x12, 0x0f, 0x89, 0x78, 0x8c, 0x12, 0x20, 0x9e, 0x30, 0xe2, 0x07, 0x7f, 0x13, 0x12, 0x2e, -0xa5, 0x80, 0x1b, 0x78, 0x8c, 0xe6, 0x24, 0x74, 0xf8, 0xe6, 0x20, 0xe1, 0x07, 0x7f, 0x12, 0x12, -0x2e, 0xa5, 0x80, 0x0a, 0x78, 0x8c, 0xe6, 0xff, 0x12, 0x21, 0x6e, 0x12, 0x2e, 0xa5, 0x02, 0x10, -0x0c, 0xae, 0x07, 0xed, 0x54, 0x03, 0x64, 0x01, 0x60, 0x03, 0x7f, 0x10, 0x22, 0xed, 0x54, 0x7c, -0xc3, 0x94, 0x04, 0x50, 0x03, 0x7f, 0x0b, 0x22, 0x74, 0x74, 0x2e, 0xf8, 0x74, 0x02, 0x46, 0xf6, -0x74, 0x95, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0xfa, 0xf5, 0x83, 0xed, 0xf0, 0x7f, 0x00, 0x22, 0xbf, -0x03, 0x06, 0x7c, 0xff, 0x7d, 0xe0, 0x80, 0x04, 0x7c, 0xff, 0x7d, 0xe2, 0x8d, 0x82, 0x8c, 0x83, -0xe0, 0x44, 0x80, 0xf0, 0xe5, 0x82, 0x24, 0x04, 0x12, 0x20, 0x25, 0xe0, 0x44, 0x80, 0xf0, 0x74, -0x74, 0x2f, 0xf8, 0x74, 0x04, 0x46, 0xf6, 0x7f, 0x00, 0x22, 0x12, 0x0f, 0x89, 0xe5, 0x31, 0x64, -0x09, 0x70, 0x04, 0xe5, 0x30, 0x64, 0x01, 0x60, 0x16, 0x90, 0xff, 0x83, 0xe0, 0x54, 0x0f, 0xff, -0xc3, 0xe5, 0x31, 0x9f, 0xe5, 0x30, 0x94, 0x00, 0x40, 0x05, 0x12, 0x25, 0x9c, 0x80, 0x03, 0x12, -0x2f, 0x76, 0x02, 0x10, 0x0c, 0x90, 0xff, 0xfc, 0xe0, 0x20, 0xe7, 0x1f, 0xc2, 0xaf, 0x7d, 0xff, -0xac, 0x05, 0x1d, 0xec, 0x60, 0x15, 0x7e, 0x04, 0x7f, 0x00, 0xef, 0x1f, 0xaa, 0x06, 0x70, 0x01, -0x1e, 0x4a, 0x60, 0xec, 0x90, 0xff, 0x92, 0xe4, 0xf0, 0x80, 0xef, 0x22, 0x12, 0x0f, 0x89, 0x78, -0x6c, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x30, 0xe0, 0x12, 0x30, 0xe1, 0x0f, 0x90, 0xff, 0xfc, 0xe0, -0x44, 0x20, 0xf0, 0x7f, 0x04, 0x12, 0x11, 0x9f, 0x12, 0x19, 0x55, 0x02, 0x10, 0x0c, 0x8e, 0x65, -0x8f, 0x66, 0xe5, 0x66, 0x15, 0x66, 0xae, 0x65, 0x70, 0x02, 0x15, 0x65, 0xd3, 0x94, 0x00, 0xee, -0x94, 0x00, 0x40, 0x09, 0x7e, 0x07, 0x7f, 0xd0, 0x12, 0x0f, 0x62, 0x80, 0xe5, 0x22, 0x11, 0x1a, -0x2b, 0x1c, 0x23, 0x56, 0x2f, 0x5c, 0x2d, 0xcc, 0x2d, 0x7a, 0x2e, 0x6b, 0x2c, 0x8e, 0x2b, 0x66, -0x2b, 0xec, 0x2c, 0xf1, 0x2e, 0x2d, 0x1b, 0xe6, 0x2b, 0xaf, 0x28, 0x67, 0x0e, 0x12, 0x0f, 0x89, -0x78, 0x8d, 0x12, 0x20, 0x9e, 0x20, 0xe2, 0x07, 0x7f, 0x11, 0x12, 0x2e, 0xa5, 0x80, 0x0a, 0x78, -0x8d, 0xe6, 0xff, 0x12, 0x2c, 0x25, 0x12, 0x2e, 0xa5, 0x02, 0x10, 0x0c, 0x8f, 0x67, 0x12, 0x2c, -0x25, 0xaf, 0x67, 0x12, 0x27, 0x19, 0x12, 0x20, 0x38, 0x12, 0x2f, 0x80, 0x74, 0x74, 0x25, 0x67, -0xf8, 0x74, 0xfd, 0x56, 0xf6, 0xaf, 0x67, 0x12, 0x19, 0xbb, 0x22, 0x12, 0x0f, 0x89, 0xe5, 0x31, -0x64, 0x09, 0x70, 0x04, 0xe5, 0x30, 0x64, 0x01, 0x60, 0x05, 0x12, 0x29, 0x2d, 0x80, 0x06, 0x12, -0x19, 0x7a, 0x12, 0x19, 0x82, 0x02, 0x10, 0x0c, 0x12, 0x27, 0xfb, 0x12, 0x12, 0x3b, 0x90, 0xf8, -0x04, 0xe0, 0xff, 0x60, 0x05, 0x7d, 0x01, 0x12, 0x11, 0xd8, 0x12, 0x26, 0xa3, 0x12, 0x12, 0x77, -0x12, 0x10, 0xfa, 0x80, 0xe3, 0x12, 0x18, 0xed, 0xef, 0x12, 0x14, 0x0d, 0xe4, 0xf5, 0x2a, 0xf5, -0x2b, 0xef, 0x60, 0x03, 0x02, 0x2f, 0x76, 0xe4, 0xff, 0x12, 0x2f, 0x6a, 0x22, 0x90, 0xff, 0xf0, -0xe0, 0xff, 0x54, 0xa0, 0x60, 0xf7, 0xef, 0x30, 0xe5, 0x08, 0x90, 0xff, 0xf0, 0x44, 0x20, 0xf0, -0xc3, 0x22, 0xd3, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0xff, 0x54, 0x28, 0x60, 0xf7, 0xef, 0x30, 0xe5, -0x08, 0x90, 0xff, 0xf0, 0x44, 0x20, 0xf0, 0xc3, 0x22, 0xd3, 0x22, 0xef, 0x30, 0xe7, 0x08, 0x12, -0x18, 0xad, 0xe0, 0x54, 0xdf, 0xf0, 0x22, 0xef, 0x12, 0x18, 0xf7, 0xe0, 0x54, 0xdf, 0xf0, 0x22, -0x81, 0x01, 0x82, 0x02, 0x83, 0x03, 0x87, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, -0x00, 0x40, 0x00, 0x08, 0x00, 0x78, 0x84, 0x12, 0x20, 0x2f, 0xa3, 0xa3, 0xe0, 0xff, 0x30, 0xe7, -0x06, 0x54, 0x7f, 0xf0, 0x44, 0x80, 0xf0, 0x22, 0x85, 0x34, 0x30, 0x85, 0x35, 0x31, 0x90, 0xff, -0x82, 0xe0, 0x54, 0xf7, 0xf0, 0xa3, 0xe0, 0x54, 0x7f, 0xf0, 0x22, 0xe4, 0xfe, 0xee, 0x90, 0x2f, -0x00, 0x93, 0xb5, 0x07, 0x02, 0xd3, 0x22, 0x0e, 0xbe, 0x07, 0xf2, 0xc3, 0x22, 0x00, 0x08, 0x18, -0x38, 0x28, 0x01, 0x81, 0x10, 0x0a, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x0f, 0x89, 0x7f, -0x02, 0x12, 0x10, 0x18, 0x12, 0x19, 0x55, 0x02, 0x10, 0x0c, 0x75, 0x30, 0x00, 0x8f, 0x31, 0x12, -0x18, 0x49, 0x12, 0x29, 0x2d, 0x22, 0x12, 0x19, 0x82, 0x12, 0x19, 0x3e, 0x12, 0x19, 0x7a, 0x22, -0xc2, 0x08, 0x22, +0xd9, 0xf5, 0xeb, 0x70, 0x10, 0xea, 0xf0, 0xc0, 0x07, 0x12, 0x12, 0x41, 0xad, 0x07, 0xaf, 0x02, +0x12, 0x12, 0x58, 0xd0, 0x07, 0xa3, 0xa3, 0xa3, 0xdf, 0xce, 0x12, 0x11, 0x66, 0x80, 0xc1, 0x8f, +0x24, 0x12, 0x2a, 0x06, 0x78, 0x80, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x8e, 0x83, 0x24, 0x08, 0x12, +0x21, 0xf3, 0xe0, 0xfd, 0x12, 0x22, 0x8a, 0x8a, 0x83, 0x24, 0x0a, 0x12, 0x21, 0xf3, 0xed, 0xf0, +0x12, 0x22, 0x56, 0x24, 0x07, 0x12, 0x21, 0xf3, 0xe0, 0xff, 0x12, 0x22, 0x99, 0x24, 0x09, 0x12, +0x21, 0xf3, 0xef, 0xf0, 0x90, 0xf9, 0x15, 0xe0, 0x30, 0xe4, 0x20, 0x08, 0x12, 0x22, 0x09, 0xc0, +0x83, 0xc0, 0x82, 0xa3, 0xe0, 0x25, 0xe0, 0xff, 0x05, 0x82, 0xd5, 0x82, 0x02, 0x15, 0x83, 0x15, +0x82, 0xe0, 0x33, 0xd0, 0x82, 0xd0, 0x83, 0xf0, 0xa3, 0xef, 0xf0, 0x78, 0x80, 0x12, 0x22, 0x09, +0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xec, 0xff, 0x12, 0x22, 0x8a, 0x8a, 0x83, 0x24, 0x08, 0x12, 0x21, +0xf3, 0xef, 0xf0, 0xed, 0x12, 0x22, 0x99, 0x24, 0x07, 0x12, 0x21, 0xf3, 0xed, 0xf0, 0x12, 0x21, +0xfb, 0xe0, 0xff, 0x30, 0xe7, 0x19, 0x12, 0x22, 0x6e, 0x12, 0x21, 0xf3, 0xe0, 0x60, 0x09, 0x12, +0x21, 0xfb, 0xef, 0x44, 0x02, 0xf0, 0x80, 0x07, 0x12, 0x21, 0xfb, 0xef, 0x54, 0xfd, 0xf0, 0x78, +0x7e, 0x12, 0x22, 0x09, 0xa3, 0xa3, 0xe0, 0xff, 0x53, 0x07, 0xc7, 0x08, 0xe6, 0xfc, 0x08, 0xe6, +0xfd, 0x12, 0x22, 0x43, 0xa3, 0xe0, 0x30, 0xe3, 0x12, 0x8d, 0x82, 0x8c, 0x83, 0xe5, 0x82, 0x24, +0x05, 0x12, 0x21, 0xf3, 0xe0, 0x90, 0x31, 0x94, 0x93, 0x42, 0x07, 0x53, 0x07, 0xfb, 0x78, 0x80, +0xe6, 0xfc, 0x08, 0xe6, 0x8c, 0x83, 0x24, 0x06, 0x12, 0x21, 0xf3, 0xe0, 0x60, 0x03, 0x43, 0x07, +0x04, 0x53, 0x07, 0xfc, 0x78, 0x80, 0x12, 0x22, 0x7a, 0x24, 0x04, 0x12, 0x21, 0xf3, 0xe0, 0x42, +0x07, 0x43, 0x07, 0x80, 0x12, 0x22, 0x8a, 0xf5, 0x82, 0x8a, 0x83, 0xa3, 0xa3, 0xef, 0xf0, 0x12, +0x22, 0x99, 0x24, 0x04, 0x12, 0x21, 0xf3, 0xe0, 0xff, 0x8d, 0x82, 0x8c, 0x83, 0xa3, 0xa3, 0xe0, +0xfc, 0xa3, 0xe0, 0xfd, 0x30, 0xe1, 0x05, 0x53, 0x07, 0xdf, 0x80, 0x03, 0x43, 0x07, 0x20, 0xec, +0x30, 0xe4, 0x05, 0x53, 0x07, 0xef, 0x80, 0x03, 0x43, 0x07, 0x10, 0x12, 0x21, 0xfb, 0xe0, 0xfe, +0x54, 0x03, 0x60, 0x73, 0x53, 0x07, 0xdf, 0xee, 0x30, 0xe1, 0x69, 0x78, 0x80, 0x12, 0x22, 0x6f, +0x12, 0x21, 0xf3, 0xe0, 0x12, 0x1b, 0x4c, 0x14, 0xa6, 0x00, 0x14, 0xda, 0x01, 0x14, 0xdf, 0x03, +0x14, 0xda, 0x05, 0x14, 0xdf, 0x07, 0x14, 0xda, 0x09, 0x14, 0xdf, 0x0b, 0x14, 0xda, 0x0d, 0x14, +0xdf, 0x0f, 0x00, 0x00, 0x14, 0xe7, 0xe5, 0x24, 0x64, 0x03, 0x70, 0x21, 0x90, 0xf9, 0x15, 0xe0, +0x30, 0xe2, 0x0d, 0x30, 0xb4, 0x05, 0x43, 0x07, 0x02, 0x80, 0x2c, 0x53, 0x07, 0xfd, 0x80, 0x27, +0x30, 0x95, 0x05, 0x43, 0x07, 0x02, 0x80, 0x1f, 0x53, 0x07, 0xfd, 0x80, 0x1a, 0x30, 0x93, 0x05, +0x43, 0x07, 0x02, 0x80, 0x12, 0x53, 0x07, 0xfd, 0x80, 0x0d, 0x43, 0x07, 0x02, 0x80, 0x08, 0x53, +0x07, 0xfd, 0x80, 0x03, 0x53, 0x07, 0xfd, 0x12, 0x22, 0x78, 0x24, 0x04, 0x12, 0x21, 0xf3, 0xef, +0xf0, 0x8d, 0x82, 0x8c, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0xff, 0x12, 0x21, 0xfb, 0xe0, 0xfe, 0x54, +0x03, 0x70, 0x03, 0x02, 0x15, 0xd7, 0xee, 0x20, 0xe1, 0x03, 0x02, 0x15, 0xd4, 0x12, 0x22, 0x6e, +0x12, 0x21, 0xf3, 0xe0, 0x12, 0x1b, 0x4c, 0x15, 0x36, 0x00, 0x15, 0x6c, 0x01, 0x15, 0x6c, 0x03, +0x15, 0xa0, 0x05, 0x15, 0xa0, 0x07, 0x15, 0x86, 0x09, 0x15, 0x86, 0x0b, 0x15, 0xba, 0x0d, 0x15, +0xba, 0x0f, 0x00, 0x00, 0x15, 0xd7, 0xe5, 0x24, 0x64, 0x03, 0x70, 0x23, 0x90, 0xf9, 0x15, 0xe0, +0x30, 0xe2, 0x0f, 0x30, 0xb1, 0x06, 0x53, 0x07, 0x7f, 0x02, 0x15, 0xd7, 0x43, 0x07, 0x80, 0x02, +0x15, 0xd7, 0x30, 0x94, 0x05, 0x53, 0x07, 0x7f, 0x80, 0x7d, 0x43, 0x07, 0x80, 0x80, 0x78, 0x30, +0x92, 0x05, 0x53, 0x07, 0x7f, 0x80, 0x70, 0x43, 0x07, 0x80, 0x80, 0x6b, 0xe5, 0x24, 0xb4, 0x03, +0x09, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xef, 0xf0, 0x80, 0x07, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xdf, +0xf0, 0x53, 0x07, 0x7f, 0x80, 0x51, 0xe5, 0x24, 0xb4, 0x03, 0x09, 0x90, 0xff, 0x9e, 0xe0, 0x44, +0x10, 0xf0, 0x80, 0x07, 0x90, 0xff, 0x9e, 0xe0, 0x44, 0x20, 0xf0, 0x53, 0x07, 0x7f, 0x80, 0x37, +0xe5, 0x24, 0xb4, 0x03, 0x09, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xef, 0xf0, 0x80, 0x07, 0x90, 0xff, +0x9e, 0xe0, 0x54, 0xdf, 0xf0, 0x43, 0x07, 0x80, 0x80, 0x1d, 0xe5, 0x24, 0xb4, 0x03, 0x09, 0x90, +0xff, 0x9e, 0xe0, 0x44, 0x10, 0xf0, 0x80, 0x07, 0x90, 0xff, 0x9e, 0xe0, 0x44, 0x20, 0xf0, 0x43, +0x07, 0x80, 0x80, 0x03, 0x53, 0x07, 0x7f, 0x78, 0x80, 0x12, 0x22, 0x3f, 0xe0, 0xfc, 0xa3, 0xe0, +0xfd, 0x30, 0xe0, 0x05, 0x43, 0x07, 0x20, 0x80, 0x03, 0x53, 0x07, 0xdf, 0xec, 0x30, 0xe3, 0x05, +0x43, 0x07, 0x40, 0x80, 0x03, 0x53, 0x07, 0xbf, 0xec, 0x30, 0xe0, 0x05, 0x43, 0x07, 0x10, 0x80, +0x03, 0x53, 0x07, 0xef, 0xed, 0x30, 0xe4, 0x05, 0x43, 0x07, 0x08, 0x80, 0x03, 0x53, 0x07, 0xf7, +0xed, 0x30, 0xe5, 0x05, 0x43, 0x07, 0x04, 0x80, 0x03, 0x53, 0x07, 0xfb, 0xed, 0x30, 0xe6, 0x05, +0x43, 0x07, 0x01, 0x80, 0x03, 0x53, 0x07, 0xfe, 0xed, 0x30, 0xe7, 0x05, 0x43, 0x07, 0x02, 0x80, +0x03, 0x53, 0x07, 0xfd, 0x78, 0x7e, 0x12, 0x22, 0x3f, 0xa3, 0xef, 0xf0, 0x12, 0x31, 0xc7, 0x7f, +0x00, 0x22, 0x90, 0xff, 0xfa, 0x74, 0x08, 0xf0, 0xa3, 0x74, 0x16, 0xf0, 0x90, 0xff, 0xf9, 0x74, +0x02, 0xf0, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xcc, 0xe4, 0xfd, 0x12, 0x22, 0xa0, 0x90, 0xfa, 0xcc, +0xe4, 0x75, 0xf0, 0x03, 0x12, 0x1a, 0x6c, 0x12, 0x18, 0xe2, 0xe5, 0x23, 0x30, 0xe7, 0x02, 0xd2, +0x02, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x24, 0x90, 0xfa, 0xcc, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0, 0xf5, +0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x90, 0xfa, 0xcc, 0xe4, 0xf0, 0xa3, 0x74, 0x0b, 0xf0, 0x7b, +0x00, 0x7a, 0x00, 0x79, 0x23, 0x75, 0x2d, 0x00, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0xe5, +0x23, 0x24, 0x80, 0x90, 0xff, 0xf8, 0xf0, 0xe5, 0x23, 0x64, 0x07, 0x60, 0x1e, 0xe5, 0x23, 0x64, +0x06, 0x60, 0x18, 0xe5, 0x23, 0x64, 0x14, 0x60, 0x12, 0xe5, 0x23, 0x64, 0x41, 0x60, 0x0c, 0xe5, +0x23, 0x64, 0x1a, 0x70, 0x46, 0xe5, 0x24, 0x64, 0x02, 0x70, 0x40, 0xe5, 0x23, 0xb4, 0x07, 0x16, +0xd2, 0x94, 0xd2, 0x95, 0xd2, 0x92, 0xd2, 0x93, 0x90, 0xf9, 0x15, 0xe0, 0x44, 0x02, 0xf0, 0xa3, +0xe0, 0x44, 0x02, 0xf0, 0x80, 0x1e, 0xe5, 0x23, 0xb4, 0x41, 0x12, 0x90, 0xf9, 0x15, 0xe0, 0x44, +0x06, 0xf0, 0xa3, 0xe0, 0x44, 0x06, 0xf0, 0xd2, 0xb1, 0xd2, 0xb4, 0x80, 0x07, 0x90, 0xf9, 0x15, +0xe0, 0x44, 0x01, 0xf0, 0x90, 0xf9, 0x16, 0xe0, 0x44, 0x01, 0xf0, 0xe5, 0x23, 0x64, 0x42, 0x60, +0x05, 0xe5, 0x23, 0xb4, 0x43, 0x0c, 0x90, 0xf9, 0x15, 0xe0, 0x44, 0x80, 0xf0, 0xa3, 0xe0, 0x44, +0x80, 0xf0, 0x90, 0xfa, 0xcc, 0xe4, 0xf0, 0xa3, 0x74, 0x0d, 0xf0, 0x12, 0x18, 0xe2, 0x90, 0xff, +0xf5, 0xe5, 0x23, 0xf0, 0xe4, 0xf5, 0x35, 0xf5, 0x33, 0xf5, 0x34, 0xf5, 0x32, 0x12, 0x1d, 0x84, +0x12, 0x1c, 0x30, 0x12, 0x1d, 0x8b, 0x90, 0xf9, 0x67, 0x12, 0x1b, 0x43, 0x90, 0xf9, 0x6c, 0x12, +0x1b, 0x43, 0x90, 0xff, 0xff, 0xe4, 0xf0, 0x90, 0xff, 0x83, 0xe0, 0xe4, 0xf0, 0x90, 0xff, 0x81, +0x74, 0x80, 0xf0, 0xa3, 0x74, 0x84, 0xf0, 0x90, 0xff, 0x80, 0xf0, 0xe4, 0xf5, 0x23, 0xe5, 0x23, +0x12, 0x1c, 0xa7, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x23, 0x12, 0x1c, 0xb5, 0xf5, 0x83, 0xe4, 0xf0, +0x05, 0x23, 0xe5, 0x23, 0xb4, 0x07, 0xe7, 0x78, 0x7a, 0x76, 0xfe, 0x08, 0x76, 0xf0, 0x90, 0x31, +0x4d, 0xe4, 0x93, 0xff, 0x78, 0x78, 0xf6, 0xfd, 0xad, 0x07, 0x90, 0x31, 0x5a, 0xe4, 0x93, 0xff, +0x08, 0xf6, 0xff, 0xed, 0x54, 0x0f, 0xfd, 0x12, 0x1c, 0x97, 0x74, 0x84, 0xf0, 0xed, 0x75, 0xf0, +0x08, 0xa4, 0x24, 0x47, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0xc3, 0x74, 0xf0, +0x9f, 0x78, 0x7b, 0xf6, 0x74, 0xfe, 0x94, 0x00, 0x18, 0x12, 0x1c, 0x28, 0xce, 0xc3, 0x13, 0xce, +0x13, 0xd8, 0xf9, 0xff, 0xed, 0x12, 0x1c, 0xf8, 0xef, 0xf0, 0xed, 0x12, 0x1d, 0x1e, 0xe4, 0xf5, +0x23, 0xe5, 0x23, 0x90, 0x31, 0x47, 0x93, 0xff, 0x78, 0x78, 0xf6, 0xfd, 0xe5, 0x23, 0x25, 0xe0, +0x24, 0x4e, 0xf5, 0x82, 0xe4, 0x34, 0x31, 0xf5, 0x83, 0xe4, 0x93, 0x08, 0xf6, 0xed, 0x30, 0xe7, +0x53, 0x18, 0xe6, 0x54, 0x0f, 0xf9, 0x12, 0x1c, 0x97, 0x12, 0x1d, 0x06, 0x24, 0x47, 0xf5, 0x82, +0xe4, 0x34, 0xff, 0x12, 0x1c, 0x18, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0xff, 0xe9, 0x12, +0x1c, 0xf8, 0xef, 0xf0, 0x12, 0x1c, 0x1f, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0x12, 0x1d, +0x0b, 0x24, 0x45, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0xe9, 0x12, 0x1d, 0x1e, +0xe9, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x46, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0x74, 0x80, +0xf0, 0x02, 0x18, 0xb7, 0x78, 0x78, 0xe6, 0x54, 0x0f, 0xf9, 0x12, 0x1c, 0xea, 0x12, 0x1d, 0x06, +0x24, 0x07, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0x12, 0x1c, 0x18, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, +0xf9, 0x12, 0x1d, 0x0b, 0x24, 0x01, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0x12, +0x1c, 0x1f, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0x12, 0x1d, 0x0b, 0x24, 0x05, 0xf5, 0x82, +0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0xe9, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x02, 0xf5, 0x82, +0xe4, 0x34, 0xff, 0xf5, 0x83, 0xe4, 0xf0, 0xe9, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x06, 0xf5, 0x82, +0xe4, 0x34, 0xff, 0xf5, 0x83, 0xe4, 0xf0, 0x05, 0x23, 0xe5, 0x23, 0x64, 0x04, 0x60, 0x03, 0x02, +0x17, 0xe1, 0x90, 0x31, 0x4c, 0xe4, 0x93, 0xff, 0x78, 0x78, 0xf6, 0x12, 0x1c, 0xe8, 0xe4, 0xf0, +0x90, 0x31, 0x4b, 0x93, 0xff, 0xf6, 0x12, 0x1c, 0x95, 0xe4, 0xf0, 0x90, 0xff, 0xfd, 0x74, 0x05, +0xf0, 0x22, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x23, 0x90, 0xfa, 0xcc, 0xe4, 0x75, 0xf0, 0x01, 0x12, +0x1a, 0x82, 0x85, 0xf0, 0x2e, 0xf5, 0x2d, 0x7d, 0x01, 0x02, 0x25, 0xd7, 0xe7, 0x09, 0xf6, 0x08, +0xdf, 0xfa, 0x80, 0x46, 0xe7, 0x09, 0xf2, 0x08, 0xdf, 0xfa, 0x80, 0x3e, 0x88, 0x82, 0x8c, 0x83, +0xe7, 0x09, 0xf0, 0xa3, 0xdf, 0xfa, 0x80, 0x32, 0xe3, 0x09, 0xf6, 0x08, 0xdf, 0xfa, 0x80, 0x78, +0xe3, 0x09, 0xf2, 0x08, 0xdf, 0xfa, 0x80, 0x70, 0x88, 0x82, 0x8c, 0x83, 0xe3, 0x09, 0xf0, 0xa3, +0xdf, 0xfa, 0x80, 0x64, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0xa3, 0xf6, 0x08, 0xdf, 0xfa, 0x80, 0x58, +0x89, 0x82, 0x8a, 0x83, 0xe0, 0xa3, 0xf2, 0x08, 0xdf, 0xfa, 0x80, 0x4c, 0x80, 0xd2, 0x80, 0xfa, +0x80, 0xc6, 0x80, 0xd4, 0x80, 0x69, 0x80, 0xf2, 0x80, 0x33, 0x80, 0x10, 0x80, 0xa6, 0x80, 0xea, +0x80, 0x9a, 0x80, 0xa8, 0x80, 0xda, 0x80, 0xe2, 0x80, 0xca, 0x80, 0x33, 0x89, 0x82, 0x8a, 0x83, +0xec, 0xfa, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xcc, 0xc5, 0x83, 0xcc, 0xf0, 0xa3, 0xc8, +0xc5, 0x82, 0xc8, 0xcc, 0xc5, 0x83, 0xcc, 0xdf, 0xe9, 0xde, 0xe7, 0x80, 0x0d, 0x89, 0x82, 0x8a, +0x83, 0xe4, 0x93, 0xa3, 0xf6, 0x08, 0xdf, 0xf9, 0xec, 0xfa, 0xa9, 0xf0, 0xed, 0xfb, 0x22, 0x89, +0x82, 0x8a, 0x83, 0xec, 0xfa, 0xe0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xcc, 0xc5, 0x83, 0xcc, 0xf0, +0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xcc, 0xc5, 0x83, 0xcc, 0xdf, 0xea, 0xde, 0xe8, 0x80, 0xdb, 0x89, +0x82, 0x8a, 0x83, 0xe4, 0x93, 0xa3, 0xf2, 0x08, 0xdf, 0xf9, 0x80, 0xcc, 0x88, 0xf0, 0xef, 0x60, +0x01, 0x0e, 0x4e, 0x60, 0xc3, 0x88, 0xf0, 0xed, 0x24, 0x02, 0xb4, 0x04, 0x00, 0x50, 0xb9, 0xf5, +0x82, 0xeb, 0x24, 0x02, 0xb4, 0x04, 0x00, 0x50, 0xaf, 0x23, 0x23, 0x45, 0x82, 0x23, 0x90, 0x19, +0x4c, 0x73, 0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0x22, 0x50, 0x02, 0xe7, 0x22, 0xbb, +0xfe, 0x02, 0xe3, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0x22, 0xbb, 0x01, 0x0c, 0xe5, 0x82, +0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0x22, 0x50, 0x06, 0xe9, 0x25, 0x82, 0xf8, +0xe6, 0x22, 0xbb, 0xfe, 0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0x22, 0xe5, 0x82, 0x29, 0xf5, 0x82, +0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe4, 0x93, 0x22, 0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xf0, +0x22, 0x50, 0x02, 0xf7, 0x22, 0xbb, 0xfe, 0x01, 0xf3, 0x22, 0xf8, 0xbb, 0x01, 0x0d, 0xe5, 0x82, +0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe8, 0xf0, 0x22, 0x50, 0x06, 0xe9, 0x25, 0x82, +0xc8, 0xf6, 0x22, 0xbb, 0xfe, 0x05, 0xe9, 0x25, 0x82, 0xc8, 0xf2, 0x22, 0xc5, 0xf0, 0xf8, 0xa3, +0xe0, 0x28, 0xf0, 0xc5, 0xf0, 0xf8, 0xe5, 0x82, 0x15, 0x82, 0x70, 0x02, 0x15, 0x83, 0xe0, 0x38, +0xf0, 0x22, 0xa3, 0xf8, 0xe0, 0xc5, 0xf0, 0x25, 0xf0, 0xf0, 0xe5, 0x82, 0x15, 0x82, 0x70, 0x02, +0x15, 0x83, 0xe0, 0xc8, 0x38, 0xf0, 0xe8, 0x22, 0xbb, 0x01, 0x10, 0xe5, 0x82, 0x29, 0xf5, 0x82, +0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0xf5, 0xf0, 0xa3, 0xe0, 0x22, 0x50, 0x09, 0xe9, 0x25, 0x82, +0xf8, 0x86, 0xf0, 0x08, 0xe6, 0x22, 0xbb, 0xfe, 0x0a, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0xf5, 0xf0, +0x08, 0xe2, 0x22, 0xe5, 0x83, 0x2a, 0xf5, 0x83, 0xe9, 0x93, 0xf5, 0xf0, 0xa3, 0xe9, 0x93, 0x22, +0xbb, 0x01, 0x0a, 0x89, 0x82, 0x8a, 0x83, 0xf0, 0xe5, 0xf0, 0xa3, 0xf0, 0x22, 0x50, 0x06, 0xf7, +0x09, 0xa7, 0xf0, 0x19, 0x22, 0xbb, 0xfe, 0x06, 0xf3, 0xe5, 0xf0, 0x09, 0xf3, 0x19, 0x22, 0xf8, +0xbb, 0x01, 0x11, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe8, 0xf0, 0xe5, +0xf0, 0xa3, 0xf0, 0x22, 0x50, 0x09, 0xe9, 0x25, 0x82, 0xc8, 0xf6, 0x08, 0xa6, 0xf0, 0x22, 0xbb, +0xfe, 0x09, 0xe9, 0x25, 0x82, 0xc8, 0xf2, 0xe5, 0xf0, 0x08, 0xf2, 0x22, 0xa4, 0x25, 0x82, 0xf5, +0x82, 0xe5, 0xf0, 0x35, 0x83, 0xf5, 0x83, 0x22, 0xe6, 0xfb, 0x08, 0xe6, 0xfa, 0x08, 0xe6, 0xf9, +0x22, 0xeb, 0xf6, 0x08, 0xea, 0xf6, 0x08, 0xe9, 0xf6, 0x22, 0xe0, 0xfb, 0xa3, 0xe0, 0xfa, 0xa3, +0xe0, 0xf9, 0x22, 0xeb, 0xf0, 0xa3, 0xea, 0xf0, 0xa3, 0xe9, 0xf0, 0x22, 0xd0, 0x83, 0xd0, 0x82, +0xf8, 0xe4, 0x93, 0x70, 0x12, 0x74, 0x01, 0x93, 0x70, 0x0d, 0xa3, 0xa3, 0x93, 0xf8, 0x74, 0x01, +0x93, 0xf5, 0x82, 0x88, 0x83, 0xe4, 0x73, 0x74, 0x02, 0x93, 0x68, 0x60, 0xef, 0xa3, 0xa3, 0xa3, +0x80, 0xdf, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0xe5, 0x4c, 0x12, 0x1a, 0x38, 0x74, 0x01, 0x25, +0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0xab, 0x36, 0xfa, 0xa9, 0x38, 0x74, 0x11, 0x12, +0x1a, 0x38, 0x74, 0x01, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0x90, 0xff, 0x06, +0xe0, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0x12, 0x1a, 0x38, 0x74, 0x01, 0x25, 0x38, 0xf5, 0x38, +0xe4, 0x35, 0x37, 0xf5, 0x37, 0xab, 0x36, 0xfa, 0xa9, 0x38, 0xe4, 0x12, 0x1a, 0x38, 0x04, 0x25, +0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0xab, 0x36, 0xfa, 0xa9, 0x38, 0xe4, 0x12, 0x1a, +0x38, 0x04, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0x90, 0xff, 0x04, 0xe0, 0xab, +0x36, 0xaa, 0x37, 0xa9, 0x38, 0x12, 0x1a, 0x38, 0x74, 0x01, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35, +0x37, 0xf5, 0x37, 0x90, 0xff, 0x05, 0xe0, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0x12, 0x1a, 0x38, +0x74, 0x01, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0x22, 0xf5, 0x83, 0xe0, 0x54, +0x08, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0x22, 0xf5, 0x83, 0xef, 0xf0, 0xfd, 0x7c, 0x00, 0xc3, +0x78, 0x7b, 0xe6, 0x9d, 0xf6, 0x18, 0xe6, 0x9c, 0xf6, 0xe6, 0xfe, 0x08, 0xe6, 0x78, 0x03, 0x22, +0x75, 0x36, 0x01, 0x75, 0x37, 0xf9, 0x75, 0x38, 0x6f, 0x22, 0xe0, 0x44, 0x04, 0xf0, 0x74, 0x12, +0x2f, 0xf5, 0x82, 0xe4, 0x34, 0xf9, 0xf5, 0x83, 0xe0, 0x22, 0x90, 0xfa, 0xb9, 0xe0, 0xff, 0x7e, +0x00, 0xc3, 0x90, 0xfa, 0xbd, 0xe0, 0x9f, 0xf0, 0x90, 0xfa, 0xbc, 0xe0, 0x9e, 0xf0, 0x90, 0xfa, +0xb4, 0xee, 0x8f, 0xf0, 0x12, 0x1a, 0x6c, 0xef, 0x25, 0x4f, 0xf5, 0x4f, 0xee, 0x35, 0x4e, 0xf5, +0x4e, 0x22, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb1, 0x90, 0xfa, 0xb4, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0, +0xf5, 0x2e, 0x22, 0x78, 0x7c, 0xe6, 0xfe, 0x08, 0xe6, 0x8e, 0x83, 0x24, 0x04, 0xf5, 0x82, 0xe4, +0x35, 0x83, 0xf5, 0x83, 0x22, 0x54, 0x0f, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x40, 0xf5, 0x82, 0xe4, +0x34, 0xff, 0xf5, 0x83, 0x22, 0xe5, 0x4d, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x48, 0xf5, 0x82, 0xe4, +0x34, 0xff, 0x22, 0xe5, 0x4d, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x08, 0xf5, 0x82, 0xe4, 0x34, 0xff, +0x22, 0x90, 0xfa, 0xb6, 0xe0, 0xff, 0x24, 0xfc, 0x22, 0x90, 0xff, 0x00, 0xe0, 0x54, 0x1f, 0x22, +0x90, 0xfa, 0xbb, 0xe0, 0x90, 0xfa, 0xb7, 0xf0, 0x22, 0x75, 0x33, 0x00, 0x8f, 0x34, 0x90, 0xf9, +0x6c, 0x12, 0x1b, 0x3a, 0x90, 0x00, 0x02, 0x22, 0x54, 0x0f, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x00, +0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0x22, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x41, 0xf5, 0x82, +0xe4, 0x34, 0xff, 0xf5, 0x83, 0x22, 0x74, 0x80, 0xf0, 0x08, 0xe6, 0xff, 0xe9, 0x75, 0xf0, 0x08, +0xa4, 0x22, 0x74, 0xaf, 0x25, 0x22, 0xf5, 0x82, 0xe4, 0x34, 0xfa, 0xf5, 0x83, 0x22, 0x75, 0xf0, +0x08, 0xa4, 0x24, 0x42, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0x74, 0x80, 0xf0, 0x22, 0x90, +0xff, 0x82, 0xe0, 0x44, 0x08, 0xf0, 0x22, 0x90, 0xff, 0xfe, 0xe0, 0x44, 0x03, 0xf0, 0x90, 0xff, +0xfc, 0xe0, 0x54, 0xfd, 0xf0, 0x22, 0x78, 0x67, 0xe6, 0x54, 0xfd, 0xf6, 0x90, 0xff, 0xfd, 0x74, +0x65, 0xf0, 0x22, 0x12, 0x1b, 0x1c, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x4e, 0x22, 0x7b, 0x01, 0x7a, +0xfa, 0x79, 0xb4, 0x22, 0x90, 0xff, 0x80, 0xe0, 0x44, 0x08, 0xf0, 0x22, 0x90, 0xff, 0x83, 0xe0, +0x54, 0x7f, 0xf0, 0x22, 0xe0, 0xff, 0x90, 0xf9, 0x67, 0x02, 0x1b, 0x3a, 0x90, 0xff, 0xa4, 0xe0, +0x44, 0x02, 0xf0, 0x22, 0x75, 0x39, 0x01, 0x75, 0x3a, 0x09, 0x22, 0x7b, 0x01, 0x7a, 0xf9, 0x79, +0x6f, 0x22, 0xd3, 0xe5, 0x3c, 0x94, 0x08, 0xe5, 0x3b, 0x94, 0x01, 0x22, 0x90, 0xfa, 0xbb, 0xe0, +0xff, 0x90, 0xfa, 0xb7, 0xf0, 0x22, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xef, 0x22, 0x90, 0xff, 0xb4, +0xe0, 0x54, 0xef, 0x22, 0x12, 0x10, 0x03, 0x78, 0x88, 0xef, 0xf6, 0x12, 0x2a, 0x06, 0x12, 0x22, +0x4a, 0x8e, 0x83, 0x24, 0x09, 0x12, 0x21, 0xf3, 0xe0, 0xfd, 0x12, 0x22, 0x2d, 0x90, 0x00, 0x0a, +0x12, 0x22, 0x52, 0x24, 0x0a, 0x12, 0x21, 0xf3, 0xe0, 0x90, 0x00, 0x0b, 0x12, 0x1a, 0x4a, 0x12, +0x22, 0x4a, 0xf5, 0x82, 0x8e, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0xf5, 0x53, 0x12, 0x22, 0x56, 0x24, +0x04, 0x12, 0x21, 0xf3, 0xe0, 0xf5, 0x54, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0xa3, 0xe0, 0xf5, 0x55, +0xe5, 0x53, 0xc4, 0x13, 0x13, 0x13, 0x54, 0x01, 0x78, 0x88, 0xf6, 0xd3, 0x94, 0x00, 0x40, 0x06, +0xe5, 0x54, 0x30, 0xe1, 0x01, 0x06, 0x78, 0x88, 0xe6, 0x12, 0x22, 0x2c, 0x90, 0x00, 0x0c, 0xef, +0x12, 0x1a, 0x4a, 0x78, 0x80, 0x12, 0x22, 0x09, 0xa3, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x53, +0x07, 0x0c, 0x53, 0x06, 0xe6, 0xe5, 0x53, 0x30, 0xe5, 0x03, 0x43, 0x07, 0x01, 0xe5, 0x54, 0x20, +0xe5, 0x0e, 0xe5, 0x53, 0x54, 0x7f, 0x70, 0x08, 0xe5, 0x53, 0x20, 0xe7, 0x03, 0x43, 0x07, 0x02, +0xe5, 0x53, 0x30, 0xe3, 0x03, 0x43, 0x07, 0x10, 0xe5, 0x53, 0x30, 0xe2, 0x03, 0x43, 0x07, 0x20, +0xe5, 0x53, 0x54, 0x03, 0x60, 0x03, 0x43, 0x07, 0x40, 0xe5, 0x53, 0x30, 0xe1, 0x03, 0x43, 0x07, +0x80, 0xe5, 0x53, 0x30, 0xe4, 0x03, 0x43, 0x06, 0x01, 0xe5, 0x53, 0x30, 0xe6, 0x03, 0x43, 0x06, +0x08, 0xe5, 0x54, 0x20, 0xe4, 0x0e, 0xe5, 0x53, 0x54, 0x7f, 0x70, 0x08, 0xe5, 0x53, 0x20, 0xe7, +0x03, 0x43, 0x06, 0x10, 0x53, 0x07, 0xfb, 0x53, 0x06, 0x79, 0x90, 0x00, 0x05, 0xee, 0x8f, 0xf0, +0x12, 0x1a, 0xef, 0xe5, 0x55, 0x30, 0xe3, 0x12, 0x54, 0x30, 0xff, 0xc4, 0x54, 0x0f, 0x12, 0x22, +0x2c, 0x90, 0x00, 0x08, 0xef, 0x12, 0x1a, 0x4a, 0x80, 0x0a, 0x12, 0x22, 0x2d, 0x90, 0x00, 0x08, +0xe4, 0x12, 0x1a, 0x4a, 0xe5, 0x55, 0x54, 0x03, 0x12, 0x22, 0x2c, 0x90, 0x00, 0x07, 0xef, 0x12, +0x1a, 0x4a, 0xe5, 0x55, 0x54, 0x04, 0xff, 0xc3, 0x13, 0x90, 0x00, 0x09, 0x12, 0x1a, 0x4a, 0x90, +0x00, 0x07, 0x12, 0x1a, 0x0b, 0x70, 0x13, 0x12, 0x22, 0x2d, 0xe9, 0x24, 0x09, 0xf9, 0xe4, 0x3a, +0xfa, 0x12, 0x19, 0xf2, 0xff, 0xc3, 0x13, 0x12, 0x1a, 0x38, 0x12, 0x22, 0x78, 0x24, 0x08, 0x12, +0x21, 0xf3, 0xe0, 0xfe, 0x8d, 0x82, 0x8c, 0x83, 0xe5, 0x82, 0x24, 0x07, 0x12, 0x21, 0xf3, 0xe0, +0xfd, 0xee, 0xed, 0x12, 0x22, 0x2c, 0x90, 0x00, 0x03, 0xee, 0x8f, 0xf0, 0x12, 0x1a, 0xef, 0x12, +0x31, 0xc7, 0x7d, 0x0a, 0xe4, 0xff, 0x12, 0x2f, 0x18, 0x02, 0x10, 0x86, 0x90, 0xfa, 0xe3, 0xe0, +0xb4, 0x03, 0x06, 0x7e, 0x00, 0x7f, 0x40, 0x80, 0x04, 0x7e, 0x00, 0x7f, 0x08, 0x90, 0xfa, 0xd7, +0xee, 0xf0, 0xa3, 0xef, 0xf0, 0x90, 0x00, 0x05, 0x12, 0x1a, 0x0b, 0xff, 0x7e, 0x00, 0x90, 0xfa, +0xd3, 0xee, 0xf0, 0xa3, 0xef, 0xf0, 0x70, 0x03, 0x7f, 0x08, 0x22, 0x90, 0x00, 0x08, 0x12, 0x1a, +0x98, 0xff, 0x90, 0xfa, 0xd5, 0xe5, 0xf0, 0xf0, 0xa3, 0xef, 0xf0, 0xae, 0x02, 0xaf, 0x01, 0x8e, +0x50, 0x8f, 0x51, 0x74, 0x0a, 0x25, 0x51, 0xf5, 0x51, 0xe4, 0x35, 0x50, 0xf5, 0x50, 0x90, 0xfa, +0xd8, 0xe0, 0xff, 0x14, 0xfe, 0x90, 0xfa, 0xd6, 0xe0, 0x5e, 0xfe, 0xc3, 0xef, 0x9e, 0xff, 0x90, +0xfa, 0xda, 0xf0, 0xc3, 0x90, 0xfa, 0xd4, 0xe0, 0x9f, 0x90, 0xfa, 0xd3, 0xe0, 0x94, 0x00, 0x50, +0x06, 0xa3, 0xe0, 0x90, 0xfa, 0xda, 0xf0, 0x12, 0x1f, 0xfb, 0x60, 0x03, 0xe0, 0xff, 0x22, 0x12, +0x2d, 0x5a, 0x90, 0xfa, 0xd3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x4e, 0x60, 0x2b, 0x90, 0xfa, 0xd7, +0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xd3, 0xef, 0x9d, 0xee, 0x9c, 0x40, 0x07, 0xe0, 0x90, 0xfa, 0xda, +0xf0, 0x80, 0x08, 0x90, 0xfa, 0xd4, 0xe0, 0x90, 0xfa, 0xda, 0xf0, 0x12, 0x1f, 0xfb, 0x60, 0x03, +0xe0, 0xff, 0x22, 0x12, 0x2d, 0x5a, 0x80, 0xca, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x52, 0xe4, 0xf5, +0x2d, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x7f, 0x00, 0x22, 0xaa, 0x50, 0xa9, 0x51, 0x7b, +0x01, 0x90, 0xfa, 0xd5, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0x90, 0xfa, 0xda, 0xe0, 0xf5, 0x4a, 0x12, +0x28, 0x9f, 0x90, 0xfa, 0xd9, 0xef, 0xf0, 0x22, 0xef, 0x24, 0xae, 0x60, 0x52, 0x24, 0xfe, 0x60, +0x2e, 0x24, 0xfe, 0x70, 0x03, 0x02, 0x20, 0xbb, 0x24, 0x06, 0x60, 0x03, 0x02, 0x21, 0x03, 0x78, +0x71, 0xe6, 0x54, 0xfb, 0xf6, 0x90, 0xff, 0xa5, 0xe0, 0xf5, 0x22, 0x44, 0x0f, 0xf0, 0x74, 0x33, +0x90, 0xfa, 0x91, 0xf0, 0xe5, 0x22, 0xa3, 0xf0, 0x90, 0xfa, 0xaf, 0x74, 0x01, 0xf0, 0x22, 0x78, +0x72, 0xe6, 0x54, 0xfb, 0xf6, 0x90, 0xff, 0xb5, 0xe0, 0xf5, 0x22, 0x44, 0x0f, 0xf0, 0x74, 0x43, +0x90, 0xfa, 0x93, 0xf0, 0xe5, 0x22, 0xa3, 0xf0, 0x90, 0xfa, 0xb0, 0x74, 0x01, 0xf0, 0x22, 0x90, +0xfa, 0x9d, 0xe0, 0xa3, 0x20, 0xe5, 0x03, 0x02, 0x21, 0x03, 0x90, 0xff, 0xa6, 0xe0, 0x90, 0xfa, +0xca, 0xf0, 0xa3, 0xf0, 0x90, 0xfa, 0xca, 0xe0, 0xff, 0x54, 0x0f, 0xfe, 0x60, 0x10, 0x90, 0xff, +0xa6, 0x12, 0x22, 0x5d, 0x90, 0xff, 0xa6, 0xe0, 0x90, 0xfa, 0xca, 0xf0, 0x80, 0xe6, 0x90, 0xfa, +0xcb, 0xe0, 0xff, 0x74, 0x34, 0xfe, 0x12, 0x2c, 0xb4, 0xef, 0x70, 0x57, 0x90, 0xfa, 0xcb, 0xe0, +0xff, 0x74, 0x34, 0x90, 0xfa, 0x95, 0xf0, 0xef, 0xa3, 0xf0, 0x22, 0x90, 0xfa, 0xa7, 0xe0, 0xa3, +0x30, 0xe5, 0x40, 0x90, 0xff, 0xb6, 0xe0, 0x90, 0xfa, 0xca, 0xf0, 0xa3, 0xf0, 0x90, 0xfa, 0xca, +0xe0, 0xff, 0x54, 0x0f, 0xfe, 0x60, 0x10, 0x90, 0xff, 0xb6, 0x12, 0x22, 0x5d, 0x90, 0xff, 0xb6, +0xe0, 0x90, 0xfa, 0xca, 0xf0, 0x80, 0xe6, 0x90, 0xfa, 0xcb, 0xe0, 0xff, 0x74, 0x44, 0xfe, 0x12, +0x2c, 0xb4, 0xef, 0x70, 0x0e, 0x90, 0xfa, 0xcb, 0xe0, 0xff, 0x74, 0x44, 0x90, 0xfa, 0x97, 0xf0, +0xef, 0xa3, 0xf0, 0x22, 0xc0, 0xe0, 0xc0, 0xf0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, +0x00, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x04, 0xc0, 0x05, 0xc0, 0x06, 0xc0, +0x07, 0x90, 0xff, 0x92, 0xe0, 0xff, 0x90, 0xfa, 0xc9, 0xf0, 0x90, 0xff, 0x92, 0xe4, 0xf0, 0xef, +0x12, 0x1b, 0x4c, 0x21, 0xbb, 0x26, 0x21, 0xbb, 0x2e, 0x21, 0x5e, 0x30, 0x21, 0x5e, 0x32, 0x21, +0x6c, 0x38, 0x21, 0x7e, 0x3a, 0x21, 0xb0, 0x3e, 0x21, 0x9b, 0x44, 0x21, 0x90, 0x46, 0x21, 0xa6, +0x50, 0x21, 0xa6, 0x52, 0x21, 0xa6, 0x54, 0x21, 0xa6, 0x56, 0x00, 0x00, 0x21, 0xc0, 0x90, 0xfa, +0xc9, 0xe0, 0xfd, 0x7c, 0x00, 0x7f, 0x01, 0x12, 0x11, 0x16, 0x80, 0x62, 0x7c, 0x00, 0x7d, 0x01, +0x7f, 0x03, 0x12, 0x11, 0x16, 0x90, 0xff, 0xfe, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x50, 0x7c, 0x00, +0x7d, 0x01, 0x7f, 0x02, 0x12, 0x11, 0x16, 0x90, 0xff, 0xfe, 0xe0, 0x44, 0x40, 0xf0, 0x80, 0x3e, +0x7c, 0x00, 0x7d, 0x01, 0x7f, 0x05, 0x12, 0x11, 0x16, 0x80, 0x33, 0x7c, 0x00, 0x7d, 0x01, 0x7f, +0x06, 0x12, 0x11, 0x16, 0x80, 0x28, 0x90, 0xfa, 0xc9, 0xe0, 0xff, 0x12, 0x20, 0x18, 0x80, 0x1e, +0x7c, 0x00, 0x7d, 0x01, 0x7f, 0x04, 0x12, 0x11, 0x16, 0x80, 0x13, 0x12, 0x27, 0x8d, 0x80, 0x0e, +0x90, 0xfa, 0xc9, 0xe0, 0x24, 0x00, 0xff, 0xe4, 0x34, 0xff, 0xfe, 0x12, 0x2c, 0xb4, 0xd0, 0x07, +0xd0, 0x06, 0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x03, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0xd0, 0xd0, +0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xf0, 0xd0, 0xe0, 0x32, 0x78, 0x7c, 0xe6, 0xfe, 0x08, 0xe6, 0x24, +0x04, 0x8e, 0x83, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5, 0x83, 0x22, 0x74, 0x12, 0x25, 0x24, 0xf5, +0x82, 0xe4, 0x34, 0xf9, 0xf5, 0x83, 0x22, 0x78, 0x7c, 0xe6, 0xfe, 0x08, 0xe6, 0xf5, 0x82, 0x8e, +0x83, 0x22, 0x78, 0x80, 0xe6, 0xfe, 0x08, 0xe6, 0xaa, 0x06, 0xf8, 0xac, 0x02, 0x7d, 0x01, 0x7b, +0xff, 0x7a, 0x31, 0x79, 0x99, 0x7e, 0x00, 0x7f, 0x0a, 0x02, 0x19, 0xcc, 0xff, 0x90, 0xf9, 0x6c, +0x02, 0x1b, 0x3a, 0x90, 0xf9, 0x67, 0x12, 0x1b, 0x3a, 0x90, 0x00, 0x04, 0x02, 0x1a, 0x0b, 0xe6, +0xfc, 0x08, 0xe6, 0xf5, 0x82, 0x8c, 0x83, 0xa3, 0xa3, 0x22, 0x78, 0x7e, 0xe6, 0xfe, 0x08, 0xe6, +0xff, 0x22, 0xed, 0x12, 0x1a, 0x4a, 0x8f, 0x82, 0x8e, 0x83, 0xe5, 0x82, 0x22, 0xef, 0xf0, 0x90, +0xfa, 0xcb, 0xe0, 0x54, 0x0f, 0x4e, 0xfe, 0xf0, 0xef, 0x54, 0xf0, 0x4e, 0xf0, 0x22, 0x08, 0xe6, +0xfc, 0x08, 0xe6, 0x8c, 0x83, 0x24, 0x09, 0x22, 0x78, 0x7e, 0xe6, 0xfc, 0x08, 0xe6, 0xfd, 0x8c, +0x83, 0x22, 0xa6, 0x07, 0xe6, 0x24, 0x6e, 0xf8, 0xe6, 0x22, 0x78, 0x7e, 0xe6, 0xfa, 0x08, 0xe6, +0xfb, 0x22, 0x26, 0xf6, 0x18, 0xee, 0x36, 0xf6, 0x22, 0x8b, 0x82, 0x8a, 0x83, 0xe5, 0x82, 0x22, +0x8b, 0x25, 0x8a, 0x26, 0x89, 0x27, 0x8d, 0x28, 0x90, 0xfa, 0xcf, 0xe4, 0xf0, 0xa3, 0x74, 0x02, +0xf0, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xce, 0x90, 0xfa, 0xcf, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0, 0xf5, +0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x90, 0xfa, 0xce, 0xe0, 0x65, 0x28, 0x60, 0x46, 0xa3, 0xe0, +0xff, 0xa3, 0xe0, 0xa3, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x12, 0x23, 0x2f, 0x90, 0xfa, 0xce, 0xe0, +0xff, 0x90, 0xfa, 0xd1, 0xe4, 0x8f, 0xf0, 0x12, 0x1a, 0x6c, 0x12, 0x23, 0x2f, 0x90, 0xfa, 0xd1, +0xe0, 0xff, 0xa3, 0xe0, 0x90, 0xfa, 0xcf, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x90, 0xfa, 0xce, 0xe0, +0xa3, 0x75, 0xf0, 0x00, 0x12, 0x1a, 0x6c, 0x90, 0xfa, 0xcf, 0xe4, 0x75, 0xf0, 0x04, 0x12, 0x1a, +0x6c, 0x02, 0x22, 0xb1, 0x90, 0xfa, 0xd0, 0xe0, 0x24, 0x01, 0xff, 0x90, 0xfa, 0xcf, 0xe0, 0x34, +0x00, 0xab, 0x25, 0xaa, 0x26, 0xa9, 0x27, 0x8f, 0xf0, 0x12, 0x1a, 0xd0, 0x7f, 0x00, 0x22, 0x7b, +0x01, 0x7a, 0xfa, 0x79, 0xce, 0x90, 0xfa, 0xcf, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x1a, 0x6c, 0x85, +0xf0, 0x2e, 0xf5, 0x2d, 0x7d, 0x01, 0x02, 0x25, 0xd7, 0x8f, 0x62, 0x12, 0x2a, 0x06, 0x12, 0x22, +0x4a, 0x8e, 0x83, 0x24, 0x0b, 0x12, 0x21, 0xf3, 0xe0, 0x54, 0xfb, 0xf0, 0x44, 0x02, 0xf0, 0x08, +0x12, 0x22, 0x3f, 0xe0, 0xa3, 0x30, 0xe5, 0x0c, 0x12, 0x22, 0x56, 0x24, 0x0b, 0x12, 0x21, 0xf3, +0xe0, 0x44, 0x01, 0xf0, 0x78, 0x7c, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0xf5, 0x82, 0x8e, 0x83, 0xe0, +0x54, 0xb8, 0xfd, 0xf0, 0xe5, 0x62, 0x24, 0xfe, 0x44, 0x20, 0xfc, 0x4d, 0xf0, 0xe5, 0x82, 0x24, +0x04, 0x12, 0x21, 0xf3, 0xe0, 0x54, 0xb8, 0xf0, 0x4c, 0xf0, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0x74, +0x03, 0xf0, 0x18, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x8e, 0x83, 0x24, 0x05, 0x12, 0x21, 0xf3, 0xc0, +0x83, 0xc0, 0x82, 0xe0, 0xfd, 0x74, 0x96, 0x25, 0x62, 0xf5, 0x82, 0xe4, 0x34, 0xfa, 0xf5, 0x83, +0xe0, 0x54, 0xfc, 0x44, 0x03, 0xfc, 0xed, 0x4c, 0xd0, 0x82, 0xd0, 0x83, 0xf0, 0x8f, 0x82, 0x8e, +0x83, 0xe0, 0x44, 0x80, 0xf0, 0xe5, 0x82, 0x24, 0x04, 0x12, 0x21, 0xf3, 0xe0, 0x44, 0x80, 0xf0, +0x12, 0x31, 0xc7, 0x74, 0x6e, 0x25, 0x62, 0xf8, 0x74, 0x04, 0x46, 0xf6, 0x7f, 0x00, 0x22, 0x12, +0x10, 0x03, 0x7f, 0x02, 0x12, 0x12, 0x19, 0x78, 0x67, 0xe6, 0x44, 0x02, 0xf6, 0xd2, 0xb0, 0xd2, +0xb1, 0x90, 0xf9, 0x15, 0xe0, 0x30, 0xe7, 0x07, 0x90, 0xff, 0x9e, 0xe4, 0xf0, 0x80, 0x36, 0xd2, +0xb3, 0x90, 0xff, 0xa4, 0xe0, 0x90, 0xfa, 0x7b, 0xf0, 0x90, 0xff, 0xb4, 0xe0, 0x90, 0xfa, 0x7c, +0xf0, 0x90, 0xff, 0xa2, 0xe0, 0x90, 0xfa, 0x79, 0xf0, 0x90, 0xff, 0xb2, 0xe0, 0x90, 0xfa, 0x7a, +0xf0, 0x90, 0xff, 0xa4, 0x74, 0x30, 0xf0, 0x90, 0xff, 0xb4, 0xf0, 0x90, 0xff, 0xa2, 0x74, 0x40, +0xf0, 0x90, 0xff, 0xb2, 0xf0, 0x90, 0xfa, 0xe4, 0xe5, 0xa8, 0xf0, 0x75, 0xa8, 0x81, 0x90, 0xff, +0x92, 0xe0, 0x60, 0x04, 0xe4, 0xf0, 0x80, 0xf6, 0x90, 0xff, 0xfd, 0x74, 0x3a, 0xf0, 0x43, 0x87, +0x01, 0x00, 0x00, 0x00, 0x90, 0xfa, 0x7b, 0xe0, 0x90, 0xff, 0xa4, 0xf0, 0x90, 0xfa, 0x7c, 0xe0, +0x90, 0xff, 0xb4, 0xf0, 0x90, 0xfa, 0x79, 0xe0, 0x90, 0xff, 0xa2, 0xf0, 0x90, 0xfa, 0x7a, 0xe0, +0x90, 0xff, 0xb2, 0xf0, 0x90, 0xf9, 0x17, 0xe0, 0x60, 0x02, 0xc2, 0xb3, 0x90, 0xfa, 0xe4, 0xe0, +0xf5, 0xa8, 0x02, 0x10, 0x86, 0x8b, 0x5c, 0x8a, 0x5d, 0x89, 0x5e, 0x12, 0x2d, 0x3c, 0x90, 0xfa, +0xc0, 0x12, 0x1b, 0x43, 0xaa, 0x5d, 0xa9, 0x5e, 0x90, 0xfa, 0xc3, 0x12, 0x1b, 0x43, 0x90, 0xfa, +0xc4, 0xe4, 0x75, 0xf0, 0x0a, 0x12, 0x1a, 0x6c, 0x90, 0xfa, 0xc3, 0x12, 0x1b, 0x3a, 0xe9, 0x24, +0x01, 0xf9, 0xe4, 0x3a, 0xfa, 0x90, 0xfa, 0xc6, 0x12, 0x1b, 0x43, 0xab, 0x5c, 0xaa, 0x5d, 0xa9, +0x5e, 0x12, 0x2d, 0x48, 0xe0, 0xff, 0xc3, 0x13, 0xf0, 0xe4, 0x78, 0x82, 0xf6, 0x90, 0xfa, 0xbe, +0xe0, 0xff, 0x78, 0x82, 0xe6, 0xc3, 0x9f, 0x50, 0x4a, 0x90, 0xfa, 0xc0, 0x12, 0x2d, 0x1d, 0xff, +0x78, 0x83, 0xf6, 0x90, 0xfa, 0xc3, 0x12, 0x2d, 0x1d, 0xfe, 0xf4, 0x5f, 0xff, 0x78, 0x83, 0xf6, +0x12, 0x2d, 0x1a, 0x5e, 0x4f, 0xff, 0x78, 0x83, 0xf6, 0x12, 0x2d, 0x23, 0x75, 0xf0, 0x02, 0x12, +0x1a, 0x6c, 0x90, 0xfa, 0xc4, 0xe4, 0x75, 0xf0, 0x02, 0x12, 0x1a, 0x6c, 0xab, 0x5c, 0xaa, 0x5d, +0xa9, 0x5e, 0x90, 0x00, 0x04, 0x12, 0x1a, 0x0b, 0x30, 0xe4, 0x03, 0x12, 0x2d, 0x32, 0x78, 0x82, +0x06, 0x80, 0xaa, 0xe4, 0x90, 0xfa, 0xbf, 0xf0, 0x22, 0x8b, 0x56, 0x8a, 0x57, 0x89, 0x58, 0x90, +0xfa, 0xbf, 0x74, 0x06, 0xf0, 0xe4, 0x90, 0xfa, 0xbe, 0xf0, 0x12, 0x19, 0xf2, 0x24, 0x6e, 0x60, +0x26, 0x14, 0x70, 0x70, 0x12, 0x2d, 0x09, 0x60, 0x09, 0x24, 0x30, 0x70, 0x12, 0x12, 0x24, 0x95, +0x80, 0x62, 0x12, 0x2d, 0x53, 0x12, 0x1f, 0x2c, 0x90, 0xfa, 0xbf, 0xef, 0xf0, 0x80, 0x55, 0x90, +0xfa, 0xbf, 0x74, 0x81, 0xf0, 0x80, 0x4d, 0x12, 0x2d, 0x09, 0x60, 0x09, 0x24, 0x30, 0x70, 0x3e, +0x12, 0x2c, 0x5f, 0x80, 0x3f, 0xe5, 0x58, 0x24, 0x03, 0xf9, 0xe4, 0x35, 0x57, 0xfa, 0x7b, 0x01, +0xc0, 0x03, 0xc0, 0x02, 0xc0, 0x01, 0x12, 0x2d, 0x53, 0x90, 0x00, 0x05, 0x12, 0x1a, 0x0b, 0xfd, +0x90, 0x00, 0x08, 0x12, 0x1a, 0x98, 0xf5, 0x2e, 0x85, 0xf0, 0x2d, 0xd0, 0x01, 0xd0, 0x02, 0xd0, +0x03, 0x12, 0x25, 0xd7, 0x90, 0xfa, 0xbe, 0xef, 0xf0, 0xe4, 0xa3, 0xf0, 0x80, 0x06, 0x90, 0xfa, +0xbf, 0x74, 0x81, 0xf0, 0x90, 0xfa, 0xbf, 0xe0, 0x12, 0x2d, 0x53, 0x90, 0x00, 0x02, 0x12, 0x1a, +0x4a, 0x90, 0xfa, 0xbe, 0xe0, 0xff, 0x22, 0x8b, 0x29, 0x8a, 0x2a, 0x89, 0x2b, 0x8d, 0x2c, 0xe5, +0x2c, 0x70, 0x03, 0xaf, 0x2c, 0x22, 0x12, 0x2d, 0x82, 0x70, 0x16, 0x12, 0x2d, 0xa1, 0xe5, 0x2d, +0x90, 0xff, 0xf1, 0xf0, 0x12, 0x31, 0x1b, 0x50, 0xf2, 0x12, 0x26, 0x64, 0x40, 0x0b, 0x7f, 0x00, +0x22, 0x12, 0x2d, 0xa1, 0x12, 0x26, 0x64, 0x50, 0xf8, 0x90, 0xff, 0xf3, 0x74, 0xa1, 0xf0, 0xe5, +0x2c, 0xb4, 0x01, 0x07, 0x90, 0xff, 0xf0, 0xe0, 0x44, 0x02, 0xf0, 0x90, 0xff, 0xf1, 0xe4, 0xf0, +0xf5, 0x2f, 0xe5, 0x2c, 0x14, 0xff, 0xe5, 0x2f, 0xc3, 0x9f, 0x50, 0x2a, 0x12, 0x31, 0x04, 0x40, +0x03, 0xaf, 0x2f, 0x22, 0xc3, 0xe5, 0x2c, 0x95, 0x2f, 0xff, 0xbf, 0x02, 0x07, 0x90, 0xff, 0xf0, +0xe0, 0x44, 0x02, 0xf0, 0x12, 0x2d, 0x94, 0x05, 0x2f, 0x74, 0x01, 0x25, 0x2b, 0xf5, 0x2b, 0xe4, +0x35, 0x2a, 0xf5, 0x2a, 0x80, 0xcc, 0x12, 0x31, 0x04, 0x40, 0x03, 0x7f, 0x18, 0x22, 0x12, 0x2d, +0x94, 0xaf, 0x2c, 0x22, 0x90, 0xff, 0xf1, 0xe5, 0x2e, 0xf0, 0x02, 0x31, 0x1b, 0x12, 0x10, 0x03, +0x78, 0x84, 0x12, 0x22, 0x82, 0x30, 0xe1, 0x08, 0x7f, 0x13, 0x12, 0x30, 0xec, 0x02, 0x26, 0xfb, +0x78, 0x84, 0xe6, 0xf9, 0x24, 0x12, 0x12, 0x21, 0xff, 0xe0, 0xff, 0x30, 0xe7, 0x40, 0x54, 0x03, +0x60, 0x1e, 0xe9, 0xb4, 0x03, 0x0d, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xfe, 0xf0, 0xe0, 0x44, 0x04, +0xf0, 0x80, 0x46, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xfd, 0xf0, 0xe0, 0x44, 0x08, 0xf0, 0x80, 0x39, +0xe9, 0xb4, 0x03, 0x0d, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xfb, 0xf0, 0xe0, 0x44, 0x01, 0xf0, 0x80, +0x28, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xf7, 0xf0, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x1b, 0xef, 0x54, +0x03, 0x60, 0x14, 0xe9, 0xb4, 0x03, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xdf, 0xf0, 0x80, 0x07, +0x90, 0xff, 0xb4, 0xe0, 0x54, 0xdf, 0xf0, 0xc2, 0xb3, 0x90, 0xf9, 0x17, 0xe0, 0x04, 0xf0, 0xaf, +0x01, 0x12, 0x22, 0x33, 0xfd, 0x12, 0x2f, 0x49, 0x12, 0x30, 0xec, 0x02, 0x10, 0x86, 0x75, 0xa8, +0x40, 0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x75, 0x81, 0x8b, 0x02, 0x27, 0x48, 0x02, 0x30, 0xcf, +0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, 0x80, 0x01, 0xf2, 0x08, 0xdf, 0xf4, +0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54, 0x07, 0x24, 0x0c, 0xc8, 0xc3, 0x33, 0xc4, 0x54, 0x0f, +0x44, 0x20, 0xc8, 0x83, 0x40, 0x04, 0xf4, 0x56, 0x80, 0x01, 0x46, 0xf6, 0xdf, 0xe4, 0x80, 0x0b, +0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x90, 0x2b, 0x4c, 0xe4, 0x7e, 0x01, 0x93, 0x60, +0xbc, 0xa3, 0xff, 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, 0x1f, 0xfe, 0xe4, 0x93, 0xa3, 0x60, 0x01, +0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8, 0x40, 0xb8, 0xe4, 0x93, 0xa3, 0xfa, 0xe4, 0x93, +0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xf0, 0xa3, 0xc8, +0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, 0xde, 0xe7, 0x80, 0xbe, 0xe4, 0xf5, 0x22, +0x12, 0x1d, 0x12, 0xe0, 0xb4, 0x04, 0x0d, 0xe5, 0x22, 0x24, 0x03, 0xff, 0x12, 0x2f, 0x77, 0x12, +0x1d, 0x12, 0xe4, 0xf0, 0x05, 0x22, 0xe5, 0x22, 0xc3, 0x94, 0x02, 0x40, 0xe3, 0xe4, 0xf5, 0x22, +0x75, 0xf0, 0x02, 0xe5, 0x22, 0x90, 0xfa, 0x91, 0x12, 0x1d, 0x53, 0x60, 0x2c, 0x12, 0x2c, 0xb4, +0xef, 0x60, 0x52, 0x75, 0xf0, 0x02, 0xe5, 0x22, 0x90, 0xfa, 0x91, 0x12, 0x1b, 0x1c, 0xe4, 0xf0, +0xa3, 0xf0, 0x75, 0xf0, 0x0a, 0xe5, 0x22, 0x90, 0xfa, 0x9d, 0x12, 0x1b, 0x1c, 0xe0, 0xa3, 0x30, +0xe6, 0x33, 0x12, 0x1d, 0x12, 0x74, 0x04, 0xf0, 0x22, 0x75, 0xf0, 0x02, 0xe5, 0x22, 0x90, 0xfa, +0x95, 0x12, 0x1d, 0x53, 0x60, 0x16, 0x12, 0x2c, 0xb4, 0xef, 0x60, 0x19, 0x75, 0xf0, 0x02, 0xe5, +0x22, 0x90, 0xfa, 0x95, 0x12, 0x1b, 0x1c, 0xe4, 0xf0, 0xa3, 0xf0, 0x22, 0x05, 0x22, 0xe5, 0x22, +0xc3, 0x94, 0x02, 0x40, 0x9b, 0x22, 0xe4, 0xff, 0x90, 0xff, 0x83, 0xe0, 0x54, 0x0f, 0xfe, 0xef, +0xc3, 0x9e, 0x50, 0x17, 0x74, 0xf0, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0xfe, 0xf5, 0x83, 0xe0, 0x12, +0x1c, 0x11, 0x12, 0x1a, 0x38, 0x0f, 0x12, 0x1c, 0x00, 0x80, 0xdd, 0xef, 0xfd, 0xc3, 0xe5, 0x3a, +0x9d, 0xf5, 0x3a, 0xe5, 0x39, 0x94, 0x00, 0xf5, 0x39, 0xd3, 0xe5, 0x3a, 0x94, 0x00, 0xe5, 0x39, +0x94, 0x00, 0x40, 0x06, 0xe4, 0x90, 0xff, 0x83, 0xf0, 0x22, 0x12, 0x1d, 0x2f, 0x12, 0x1d, 0x84, +0x12, 0x1d, 0x76, 0x12, 0x19, 0xf2, 0x24, 0x6e, 0x60, 0x1e, 0x14, 0x60, 0x1b, 0x24, 0x8e, 0x70, +0x2d, 0x90, 0x00, 0x01, 0x12, 0x1a, 0x0b, 0xff, 0x24, 0xfc, 0x60, 0x03, 0x04, 0x70, 0x1f, 0xef, +0xfd, 0x7c, 0x00, 0x7f, 0x0d, 0x02, 0x11, 0x16, 0x12, 0x1d, 0x8b, 0x12, 0x25, 0x39, 0x12, 0x1c, +0xd9, 0x12, 0x1a, 0x0b, 0x60, 0x03, 0x02, 0x31, 0xbd, 0xe4, 0xff, 0x12, 0x31, 0xb1, 0x22, 0x8b, +0x45, 0x8a, 0x46, 0x89, 0x47, 0x8c, 0x48, 0x8d, 0x49, 0xd2, 0x00, 0x12, 0x2d, 0x82, 0x70, 0x16, +0x12, 0x2d, 0xa1, 0xe5, 0x48, 0x90, 0xff, 0xf1, 0xf0, 0x12, 0x31, 0x1b, 0x50, 0xf2, 0x12, 0x29, +0x14, 0x40, 0x0b, 0x7f, 0x18, 0x22, 0x12, 0x2d, 0xa1, 0x12, 0x29, 0x14, 0x50, 0xf8, 0xe4, 0xf5, +0x4b, 0xe5, 0x4a, 0x14, 0xff, 0xe5, 0x4b, 0xc3, 0x9f, 0x50, 0x17, 0x12, 0x29, 0x04, 0x40, 0x03, +0x7f, 0x18, 0x22, 0x05, 0x4b, 0x74, 0x01, 0x25, 0x47, 0xf5, 0x47, 0xe4, 0x35, 0x46, 0xf5, 0x46, +0x80, 0xdf, 0x90, 0xff, 0xf0, 0xe0, 0x44, 0x01, 0xf0, 0x12, 0x29, 0x04, 0x40, 0x03, 0x7f, 0x18, +0x22, 0x7f, 0x00, 0x22, 0xab, 0x45, 0xaa, 0x46, 0xa9, 0x47, 0x12, 0x19, 0xf2, 0x90, 0xff, 0xf1, +0xf0, 0x02, 0x31, 0x1b, 0x90, 0xff, 0xf1, 0xe5, 0x49, 0xf0, 0x02, 0x31, 0x1b, 0x7b, 0x01, 0x7a, +0xfa, 0x79, 0xcc, 0xe4, 0xfd, 0x12, 0x22, 0xa0, 0x90, 0xfa, 0xcc, 0xe4, 0x75, 0xf0, 0x09, 0x12, +0x1a, 0x6c, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x23, 0x90, 0xfa, 0xcc, 0xe4, 0x75, 0xf0, 0x01, 0x12, +0x1a, 0x82, 0x85, 0xf0, 0x2e, 0xf5, 0x2d, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x90, 0xff, 0xf7, 0xe5, +0x23, 0x12, 0x29, 0x78, 0x90, 0xff, 0xf6, 0xe5, 0x23, 0xf0, 0x90, 0xfa, 0xcc, 0xe4, 0xf0, 0xa3, +0x74, 0x06, 0x12, 0x29, 0x78, 0xe5, 0x23, 0x30, 0xe0, 0x07, 0x90, 0xff, 0xfc, 0x74, 0x94, 0xf0, +0x22, 0x90, 0xff, 0xfc, 0x74, 0x90, 0xf0, 0x22, 0xf0, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x23, 0x90, +0xfa, 0xcc, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x1a, 0x82, 0x85, 0xf0, 0x2e, 0xf5, 0x2d, 0x7d, 0x01, +0x02, 0x25, 0xd7, 0x90, 0xff, 0x93, 0x74, 0x2a, 0xf0, 0x90, 0xff, 0xff, 0xe0, 0x60, 0x06, 0x90, +0xff, 0xfc, 0x74, 0x10, 0xf0, 0x90, 0xff, 0x91, 0xe0, 0x44, 0x90, 0xf0, 0xe4, 0x90, 0xf9, 0x15, +0xf0, 0xa3, 0xf0, 0x12, 0x2a, 0x78, 0x12, 0x16, 0x42, 0x12, 0x2f, 0xcd, 0x7e, 0x07, 0x7f, 0xd0, +0x12, 0x11, 0xe2, 0x7e, 0x0f, 0x7f, 0xa0, 0x12, 0x11, 0xfc, 0xe4, 0x78, 0x77, 0xf6, 0x78, 0x77, +0xe6, 0xff, 0xc3, 0x94, 0x06, 0x50, 0x0b, 0x74, 0x6e, 0x2f, 0xf8, 0xe4, 0xf6, 0x78, 0x77, 0x06, +0x80, 0xec, 0x7f, 0x03, 0x12, 0x2e, 0xb3, 0x90, 0xf9, 0x15, 0xe0, 0x20, 0xe4, 0x05, 0x7f, 0x04, +0x12, 0x2e, 0xb3, 0x90, 0xff, 0x9b, 0xe4, 0xf0, 0x90, 0xff, 0x9a, 0xf0, 0x90, 0xff, 0xe8, 0xe0, +0x54, 0x1f, 0xf0, 0xd2, 0xa8, 0x22, 0x15, 0x65, 0xa8, 0x65, 0xa6, 0x07, 0x30, 0x08, 0x05, 0x12, +0x11, 0x66, 0x80, 0xf8, 0xd2, 0x08, 0xa8, 0x65, 0xe6, 0xff, 0xb4, 0x03, 0x0f, 0x78, 0x7c, 0x76, +0xff, 0x08, 0x76, 0xe0, 0x08, 0x76, 0xff, 0x08, 0x76, 0xa0, 0x80, 0x0d, 0x78, 0x7c, 0x76, 0xff, +0x08, 0x76, 0xe2, 0x08, 0x76, 0xff, 0x08, 0x76, 0xb0, 0x78, 0x80, 0x76, 0xfa, 0x08, 0x76, 0x9b, +0xef, 0x24, 0xfd, 0x75, 0xf0, 0x0a, 0xa4, 0xae, 0xf0, 0x12, 0x22, 0x92, 0x7b, 0x01, 0x7a, 0xff, +0x79, 0x48, 0x78, 0x68, 0x12, 0x1b, 0x31, 0xa8, 0x65, 0xe6, 0x24, 0xfd, 0x75, 0xf0, 0x08, 0xa4, +0xff, 0xae, 0xf0, 0x78, 0x6a, 0x12, 0x22, 0x92, 0x79, 0x08, 0x78, 0x6b, 0x12, 0x1b, 0x31, 0x78, +0x6d, 0xef, 0x12, 0x22, 0x92, 0x05, 0x65, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0x54, 0xab, 0xf0, 0xe0, +0x44, 0x20, 0xf0, 0x90, 0xfa, 0xe3, 0x74, 0x02, 0xf0, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xcc, 0xe4, +0xf5, 0x2d, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x7e, 0x00, 0x90, 0xfa, 0xe1, 0xee, 0xf0, +0xa3, 0xef, 0xf0, 0x64, 0x01, 0x70, 0x10, 0x90, 0xfa, 0xcc, 0xe0, 0xb4, 0x52, 0x09, 0x90, 0xf9, +0x15, 0xe0, 0x54, 0xef, 0xf0, 0x80, 0x29, 0x90, 0xfa, 0xe1, 0xe0, 0x70, 0x04, 0xa3, 0xe0, 0x64, +0x01, 0x70, 0x10, 0x90, 0xfa, 0xcc, 0xe0, 0xb4, 0x10, 0x09, 0x90, 0xf9, 0x15, 0xe0, 0x44, 0x10, +0xf0, 0x80, 0x0d, 0x90, 0xfa, 0xe3, 0x74, 0x03, 0xf0, 0x90, 0xf9, 0x15, 0xe0, 0x54, 0xef, 0xf0, +0x90, 0xff, 0xf0, 0xe0, 0x44, 0x20, 0xf0, 0x22, 0x12, 0x10, 0x03, 0x78, 0x8a, 0xef, 0xf6, 0x12, +0x2a, 0x06, 0x12, 0x22, 0x33, 0x30, 0xe0, 0x25, 0x12, 0x22, 0x07, 0xe0, 0x54, 0x7f, 0xf0, 0x78, +0x6b, 0x12, 0x1b, 0x28, 0x90, 0x00, 0x02, 0x12, 0x1a, 0x0b, 0x30, 0xe7, 0x09, 0x90, 0x00, 0x02, +0xe4, 0x12, 0x1a, 0x4a, 0x80, 0xe9, 0x12, 0x22, 0x07, 0xe0, 0x44, 0x80, 0xf0, 0x12, 0x22, 0x33, +0x30, 0xe1, 0x1e, 0x12, 0x21, 0xe9, 0xe0, 0x54, 0x7f, 0xf0, 0x12, 0x31, 0x5c, 0x78, 0x68, 0x12, +0x1b, 0x28, 0x90, 0x00, 0x02, 0x74, 0x80, 0x12, 0x1a, 0x4a, 0x12, 0x21, 0xe9, 0xe0, 0x44, 0x80, +0xf0, 0x12, 0x31, 0xc7, 0xe4, 0xff, 0x12, 0x30, 0xec, 0x02, 0x10, 0x86, 0x03, 0x68, 0x01, 0xff, +0x48, 0x03, 0x6b, 0x01, 0xff, 0x08, 0x02, 0x66, 0x00, 0x00, 0x44, 0xfa, 0x95, 0x00, 0x00, 0x00, +0x00, 0x44, 0xfa, 0x91, 0x00, 0x00, 0x00, 0x00, 0x42, 0xfa, 0xaf, 0x00, 0x00, 0x42, 0xfa, 0x7b, +0x00, 0x00, 0x42, 0xfa, 0x79, 0x00, 0x00, 0x42, 0xf9, 0x6a, 0xff, 0xff, 0x42, 0xfa, 0x77, 0x00, +0x00, 0x43, 0xf9, 0x18, 0x0a, 0x32, 0x02, 0x41, 0xf9, 0x65, 0x20, 0x41, 0xf9, 0x66, 0x20, 0x41, +0xf9, 0x63, 0x00, 0x41, 0xf9, 0x64, 0x00, 0x44, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xf9, +0x15, 0x00, 0x00, 0x41, 0xf9, 0x17, 0x00, 0x01, 0x20, 0x00, 0x41, 0xf8, 0x04, 0x00, 0x00, 0x12, +0x10, 0x03, 0x78, 0x85, 0xef, 0xf6, 0x12, 0x30, 0x93, 0x12, 0x30, 0xec, 0x78, 0x85, 0xe6, 0xff, +0x24, 0x12, 0x12, 0x21, 0xff, 0xe0, 0xfe, 0x30, 0xe7, 0x16, 0xef, 0xb4, 0x03, 0x09, 0x90, 0xff, +0x9e, 0xe0, 0x54, 0xfa, 0xf0, 0x80, 0x22, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xf5, 0xf0, 0x80, 0x19, +0xee, 0x54, 0x03, 0x60, 0x14, 0xef, 0xb4, 0x03, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x20, 0xf0, +0x80, 0x07, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0xf9, 0x17, 0xe0, 0x14, 0xf0, 0xe0, +0x70, 0x02, 0xd2, 0xb3, 0x02, 0x10, 0x86, 0x12, 0x1d, 0x6c, 0xe5, 0x3a, 0x64, 0x09, 0x70, 0x04, +0xe5, 0x39, 0x64, 0x01, 0x60, 0x48, 0xc3, 0xe5, 0x3a, 0x94, 0x08, 0xe5, 0x39, 0x94, 0x00, 0x40, +0x11, 0x7f, 0x08, 0xef, 0xe5, 0x3a, 0x94, 0x08, 0xf5, 0x3a, 0xe5, 0x39, 0x94, 0x00, 0xf5, 0x39, +0x80, 0x05, 0xaf, 0x3a, 0x12, 0x1d, 0x84, 0xe4, 0xfe, 0xee, 0xc3, 0x9f, 0x50, 0x19, 0x12, 0x1c, +0x11, 0x12, 0x19, 0xf2, 0xfd, 0x74, 0xf8, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0xfe, 0xf5, 0x83, 0xed, +0xf0, 0x0e, 0x12, 0x1c, 0x00, 0x80, 0xe2, 0xef, 0x54, 0x7f, 0x90, 0xff, 0x81, 0xf0, 0x22, 0x8b, +0x59, 0x8a, 0x5a, 0x89, 0x5b, 0x12, 0x2d, 0x48, 0x70, 0x05, 0xa3, 0x74, 0x08, 0xf0, 0x22, 0xab, +0x59, 0xaa, 0x5a, 0xa9, 0x5b, 0x12, 0x2d, 0x3c, 0x90, 0xfa, 0xc6, 0x12, 0x1b, 0x43, 0xe5, 0x5b, +0x24, 0x03, 0xf9, 0xe4, 0x35, 0x5a, 0xfa, 0x90, 0xfa, 0xc0, 0x12, 0x1b, 0x43, 0xe4, 0x90, 0xfa, +0xbf, 0xf0, 0x78, 0x8b, 0xf6, 0x90, 0xfa, 0xbe, 0xe0, 0xff, 0x78, 0x8b, 0xe6, 0xc3, 0x9f, 0x50, +0x12, 0x12, 0x2d, 0x1a, 0xff, 0x12, 0x2d, 0x23, 0x12, 0x2d, 0x36, 0x78, 0x8b, 0x06, 0x12, 0x2d, +0x32, 0x80, 0xe2, 0x22, 0xad, 0x07, 0xac, 0x06, 0x90, 0x31, 0x4d, 0xe4, 0x93, 0xff, 0x78, 0x74, +0xf6, 0x54, 0x0f, 0x12, 0x1c, 0xf8, 0xe0, 0x08, 0x76, 0x00, 0x08, 0xf6, 0x18, 0x12, 0x1c, 0x29, +0xc3, 0x33, 0xce, 0x33, 0xce, 0xd8, 0xf9, 0xff, 0x78, 0x75, 0xee, 0xf6, 0x08, 0xef, 0xf6, 0xee, +0x44, 0xf8, 0x18, 0xf6, 0xef, 0x08, 0xf6, 0x90, 0xff, 0x7a, 0xe0, 0x20, 0xe7, 0x03, 0x7f, 0x00, +0x22, 0x78, 0x75, 0xe6, 0xfe, 0x08, 0xe6, 0xf5, 0x82, 0x8e, 0x83, 0xec, 0xf0, 0xa3, 0xed, 0xf0, +0x90, 0xff, 0x7a, 0x74, 0x02, 0xf0, 0x7f, 0x01, 0x22, 0xab, 0x56, 0xaa, 0x57, 0xa9, 0x58, 0x90, +0x00, 0x03, 0x12, 0x1a, 0x0b, 0x54, 0xf0, 0x24, 0xa0, 0x22, 0x90, 0xfa, 0xc6, 0x12, 0x1b, 0x3a, +0x02, 0x19, 0xf2, 0x90, 0xfa, 0xc0, 0x12, 0x1b, 0x3a, 0xef, 0x12, 0x1a, 0x38, 0x90, 0xfa, 0xc7, +0xe4, 0x22, 0x90, 0xfa, 0xc1, 0xe4, 0x75, 0xf0, 0x01, 0x02, 0x1a, 0x6c, 0x90, 0x00, 0x08, 0x12, +0x1a, 0x98, 0xaa, 0xf0, 0xf9, 0x7b, 0x01, 0x22, 0x90, 0x00, 0x05, 0x12, 0x1a, 0x0b, 0x90, 0xfa, +0xbe, 0xf0, 0x22, 0xab, 0x56, 0xaa, 0x57, 0xa9, 0x58, 0x22, 0x90, 0xfa, 0xda, 0xe0, 0xff, 0x7e, +0x00, 0xc3, 0x90, 0xfa, 0xd4, 0xe0, 0x9f, 0xf0, 0x90, 0xfa, 0xd3, 0xe0, 0x9e, 0xf0, 0x90, 0xfa, +0xd5, 0xee, 0x8f, 0xf0, 0x12, 0x1a, 0x6c, 0xef, 0x25, 0x51, 0xf5, 0x51, 0xee, 0x35, 0x50, 0xf5, +0x50, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0x54, 0xfe, 0xf0, 0xe0, 0x54, 0xfd, 0xf0, 0x90, 0xfa, 0xe3, +0xe0, 0x64, 0x03, 0x22, 0x90, 0xff, 0xf2, 0xe0, 0xab, 0x29, 0xaa, 0x2a, 0xa9, 0x2b, 0x02, 0x1a, +0x38, 0x90, 0xff, 0xf3, 0x74, 0xa0, 0xf0, 0x22, 0x8f, 0x64, 0xed, 0x70, 0x0f, 0xe5, 0x64, 0xb4, +0x03, 0x05, 0x7f, 0x01, 0x02, 0x31, 0x32, 0x7f, 0x02, 0x02, 0x31, 0x32, 0xaf, 0x64, 0x12, 0x2a, +0x06, 0x74, 0x6e, 0x25, 0x64, 0xf8, 0xe6, 0x30, 0xe2, 0x0b, 0xd2, 0x09, 0x12, 0x1c, 0x83, 0xe0, +0x54, 0x7f, 0xf0, 0x80, 0x02, 0xc2, 0x09, 0xe5, 0x64, 0xb4, 0x03, 0x07, 0x7f, 0x81, 0x12, 0x31, +0x32, 0x80, 0x05, 0x7f, 0x82, 0x12, 0x31, 0x32, 0x30, 0x09, 0x07, 0x12, 0x1c, 0x83, 0xe0, 0x44, +0x80, 0xf0, 0x12, 0x31, 0xc7, 0x22, 0x12, 0x10, 0x03, 0x90, 0xff, 0xfd, 0xe0, 0x44, 0x60, 0xf0, +0xd2, 0x01, 0x90, 0xff, 0xfc, 0xe0, 0x44, 0x02, 0xf0, 0x90, 0xff, 0x00, 0xe0, 0x30, 0xe7, 0x13, +0x90, 0xff, 0x83, 0xe0, 0x44, 0x80, 0xf0, 0x43, 0x35, 0x80, 0x90, 0xff, 0xfc, 0xe0, 0x44, 0x01, +0xf0, 0x80, 0x0d, 0x12, 0x1d, 0x2f, 0x53, 0x35, 0x7f, 0x90, 0xff, 0xfc, 0xe0, 0x54, 0xfe, 0xf0, +0x90, 0xff, 0x81, 0xe0, 0x44, 0x80, 0xf0, 0x12, 0x02, 0xb0, 0x12, 0x1d, 0x37, 0x02, 0x10, 0x86, +0x12, 0x10, 0x03, 0x78, 0x89, 0xef, 0xf6, 0xd2, 0x00, 0x12, 0x2a, 0x06, 0x90, 0xf9, 0x67, 0x12, +0x1b, 0x3a, 0xe9, 0x24, 0x03, 0xf9, 0xe4, 0x3a, 0xfa, 0xc0, 0x02, 0x78, 0x80, 0xe6, 0xfe, 0x08, +0xe6, 0xaa, 0x06, 0xf8, 0xac, 0x02, 0x7d, 0x01, 0xd0, 0x02, 0x12, 0x22, 0x25, 0x12, 0x31, 0xc7, +0x78, 0x89, 0xe6, 0xff, 0x12, 0x13, 0x3f, 0x12, 0x30, 0xec, 0x02, 0x10, 0x86, 0x8f, 0x63, 0x12, +0x2a, 0x06, 0x12, 0x22, 0x07, 0xe0, 0x54, 0x3f, 0xf0, 0xe5, 0x82, 0x24, 0x04, 0x12, 0x21, 0xf3, +0xe0, 0x54, 0x3f, 0xf0, 0x08, 0xe6, 0xfe, 0x08, 0xe6, 0x8e, 0x83, 0x24, 0x0b, 0x12, 0x21, 0xf3, +0xe0, 0x54, 0xf8, 0xf0, 0x12, 0x31, 0xc7, 0x74, 0x6e, 0x25, 0x63, 0xf8, 0x74, 0xfb, 0x56, 0xf6, +0x7f, 0x00, 0x22, 0x8f, 0x23, 0xc2, 0x08, 0x12, 0x2a, 0x06, 0x12, 0x22, 0x12, 0x78, 0x7e, 0x12, +0x21, 0xeb, 0xe0, 0x44, 0x01, 0xf0, 0x12, 0x22, 0x4a, 0x12, 0x21, 0xef, 0xe0, 0x20, 0xe0, 0xf6, +0xef, 0x24, 0x0b, 0xf5, 0x82, 0xe4, 0x3e, 0xf5, 0x83, 0xe0, 0x54, 0xf8, 0xf0, 0x12, 0x31, 0xc7, +0xaf, 0x23, 0x12, 0x13, 0x3f, 0x22, 0x12, 0x10, 0x03, 0x12, 0x2a, 0x06, 0x12, 0x22, 0x4a, 0x24, +0x06, 0x12, 0x21, 0xf1, 0xe0, 0xfd, 0x12, 0x22, 0x2d, 0x90, 0x00, 0x03, 0x12, 0x22, 0x52, 0x24, +0x05, 0x12, 0x21, 0xf3, 0xe0, 0x90, 0x00, 0x04, 0x12, 0x1a, 0x4a, 0x12, 0x31, 0xc7, 0x7d, 0x02, +0xe4, 0xff, 0x12, 0x2f, 0x18, 0x02, 0x10, 0x86, 0xae, 0x05, 0x12, 0x1c, 0xde, 0xef, 0x12, 0x1a, +0x4a, 0x0e, 0x0e, 0x0e, 0xee, 0xd3, 0x95, 0x3c, 0xe4, 0x95, 0x3b, 0x40, 0x02, 0xae, 0x3c, 0xee, +0xd3, 0x94, 0x08, 0x74, 0x80, 0x94, 0x81, 0x40, 0x0a, 0x7e, 0x03, 0x90, 0x00, 0x02, 0x74, 0x02, +0x12, 0x1a, 0x4a, 0xaf, 0x06, 0x12, 0x31, 0xb1, 0x22, 0xae, 0x07, 0xed, 0x54, 0x03, 0x64, 0x01, +0x60, 0x03, 0x7f, 0x10, 0x22, 0xed, 0x54, 0x7c, 0xc3, 0x94, 0x04, 0x50, 0x03, 0x7f, 0x0b, 0x22, +0x74, 0x6e, 0x2e, 0xf8, 0x74, 0x02, 0x46, 0xf6, 0x74, 0x96, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0xfa, +0xf5, 0x83, 0xed, 0xf0, 0x7f, 0x00, 0x22, 0xbf, 0x03, 0x06, 0x7c, 0xff, 0x7d, 0xe0, 0x80, 0x04, +0x7c, 0xff, 0x7d, 0xe2, 0x8d, 0x82, 0x8c, 0x83, 0xe0, 0x44, 0x80, 0xf0, 0xe5, 0x82, 0x24, 0x04, +0x12, 0x21, 0xf3, 0xe0, 0x44, 0x80, 0xf0, 0x74, 0x6e, 0x2f, 0xf8, 0x74, 0x04, 0x46, 0xf6, 0x7f, +0x00, 0x22, 0x12, 0x10, 0x03, 0xe5, 0x3a, 0x64, 0x09, 0x70, 0x04, 0xe5, 0x39, 0x64, 0x01, 0x60, +0x16, 0x90, 0xff, 0x83, 0xe0, 0x54, 0x0f, 0xff, 0xc3, 0xe5, 0x3a, 0x9f, 0xe5, 0x39, 0x94, 0x00, +0x40, 0x05, 0x12, 0x28, 0x16, 0x80, 0x03, 0x12, 0x31, 0xbd, 0x02, 0x10, 0x86, 0x90, 0xff, 0xfc, +0xe0, 0x20, 0xe7, 0x1f, 0xc2, 0xaf, 0x7d, 0xff, 0xac, 0x05, 0x1d, 0xec, 0x60, 0x15, 0x7e, 0x04, +0x7f, 0x00, 0xef, 0x1f, 0xaa, 0x06, 0x70, 0x01, 0x1e, 0x4a, 0x60, 0xec, 0x90, 0xff, 0x92, 0xe4, +0xf0, 0x80, 0xef, 0x22, 0x12, 0x10, 0x03, 0x78, 0x66, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x30, 0xe0, +0x12, 0x30, 0xe1, 0x0f, 0x90, 0xff, 0xfc, 0xe0, 0x44, 0x20, 0xf0, 0x7f, 0x04, 0x12, 0x12, 0x19, +0x12, 0x1d, 0x46, 0x02, 0x10, 0x86, 0x8e, 0x5f, 0x8f, 0x60, 0xe5, 0x60, 0x15, 0x60, 0xae, 0x5f, +0x70, 0x02, 0x15, 0x5f, 0xd3, 0x94, 0x00, 0xee, 0x94, 0x00, 0x40, 0x09, 0x7e, 0x07, 0x7f, 0xd0, +0x12, 0x0f, 0xdc, 0x80, 0xe5, 0x22, 0x11, 0x94, 0x2d, 0xf6, 0x23, 0xef, 0x31, 0xa3, 0x2f, 0xf4, +0x2f, 0xa2, 0x30, 0xb2, 0x2e, 0xe6, 0x26, 0x6d, 0x2b, 0xaf, 0x30, 0x55, 0x30, 0x74, 0x1d, 0xb4, +0x2e, 0x40, 0x2a, 0xe8, 0x0e, 0x12, 0x10, 0x03, 0x78, 0x86, 0x12, 0x22, 0x82, 0x20, 0xe1, 0x07, +0x7f, 0x12, 0x12, 0x30, 0xec, 0x80, 0x0a, 0x78, 0x86, 0xe6, 0xff, 0x12, 0x23, 0x49, 0x12, 0x30, +0xec, 0x02, 0x10, 0x86, 0x12, 0x10, 0x03, 0x78, 0x87, 0x12, 0x22, 0x82, 0x20, 0xe2, 0x07, 0x7f, +0x11, 0x12, 0x30, 0xec, 0x80, 0x0a, 0x78, 0x87, 0xe6, 0xff, 0x12, 0x2e, 0x7d, 0x12, 0x30, 0xec, +0x02, 0x10, 0x86, 0x8f, 0x61, 0x12, 0x2e, 0x7d, 0xaf, 0x61, 0x12, 0x2a, 0x06, 0x12, 0x22, 0x12, +0x12, 0x31, 0xc7, 0x74, 0x6e, 0x25, 0x61, 0xf8, 0x74, 0xfd, 0x56, 0xf6, 0xaf, 0x61, 0x12, 0x13, +0x3f, 0x22, 0x12, 0x10, 0x03, 0xe5, 0x3a, 0x64, 0x09, 0x70, 0x04, 0xe5, 0x39, 0x64, 0x01, 0x60, +0x05, 0x12, 0x2c, 0x07, 0x80, 0x06, 0x12, 0x1d, 0x64, 0x12, 0x1d, 0x6c, 0x02, 0x10, 0x86, 0x12, +0x29, 0x93, 0x12, 0x12, 0xbb, 0x90, 0xf8, 0x04, 0xe0, 0xff, 0x60, 0x05, 0x7d, 0x01, 0x12, 0x12, +0x58, 0x12, 0x29, 0x1d, 0x12, 0x12, 0xf7, 0x12, 0x11, 0x74, 0x80, 0xe3, 0x12, 0x1c, 0xde, 0xef, +0x12, 0x1a, 0x4a, 0xe4, 0xf5, 0x33, 0xf5, 0x34, 0xef, 0x60, 0x03, 0x02, 0x31, 0xbd, 0xe4, 0xff, +0x12, 0x31, 0xb1, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0xff, 0x54, 0xa0, 0x60, 0xf7, 0xef, 0x30, 0xe5, +0x08, 0x90, 0xff, 0xf0, 0x44, 0x20, 0xf0, 0xc3, 0x22, 0xd3, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0xff, +0x54, 0x28, 0x60, 0xf7, 0xef, 0x30, 0xe5, 0x08, 0x90, 0xff, 0xf0, 0x44, 0x20, 0xf0, 0xc3, 0x22, +0xd3, 0x22, 0xef, 0x30, 0xe7, 0x08, 0x12, 0x1c, 0x95, 0xe0, 0x54, 0xdf, 0xf0, 0x22, 0xef, 0x12, +0x1c, 0xe8, 0xe0, 0x54, 0xdf, 0xf0, 0x22, 0x81, 0x01, 0x82, 0x02, 0x83, 0x03, 0x87, 0x40, 0x00, +0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x08, 0x00, 0x78, 0x7e, 0x12, 0x22, +0x09, 0xa3, 0xa3, 0xe0, 0xff, 0x30, 0xe7, 0x06, 0x54, 0x7f, 0xf0, 0x44, 0x80, 0xf0, 0x22, 0x85, +0x3b, 0x39, 0x85, 0x3c, 0x3a, 0x90, 0xff, 0x82, 0xe0, 0x54, 0xf7, 0xf0, 0xa3, 0xe0, 0x54, 0x7f, +0xf0, 0x22, 0xe4, 0xfe, 0xee, 0x90, 0x31, 0x47, 0x93, 0xb5, 0x07, 0x02, 0xd3, 0x22, 0x0e, 0xbe, +0x07, 0xf2, 0xc3, 0x22, 0x00, 0x08, 0x18, 0x28, 0x38, 0x01, 0x81, 0x10, 0x0a, 0x02, 0x00, 0x00, +0x00, 0x00, 0x00, 0x12, 0x10, 0x03, 0x7f, 0x02, 0x12, 0x10, 0x92, 0x12, 0x1d, 0x46, 0x02, 0x10, +0x86, 0x75, 0x39, 0x00, 0x8f, 0x3a, 0x12, 0x1c, 0x30, 0x12, 0x2c, 0x07, 0x22, 0x12, 0x1d, 0x6c, +0x12, 0x1d, 0x2f, 0x12, 0x1d, 0x64, 0x22, 0xc2, 0x08, 0x22, }; #undef IMAGE_VERSION_NAME --- linux-2.6.9-rc1/drivers/usb/serial/io_fw_down.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/drivers/usb/serial/io_fw_down.h 2004-09-02 20:51:52.000000000 -0700 @@ -17,7 +17,7 @@ unsigned short Addr; unsigned short Len; unsigned char Data[0]; - } __attribute ((packed)); + } __attribute__ ((packed)); struct edge_firmware_version_info { unsigned char MajorVersion; @@ -36,11 +36,11 @@ static unsigned char IMAGE_ARRAY_NAME[] // Segment #1, Start Address 00ff0000, Length 6 0xff,0x00,0x00,0x00,0x06,0x00, - 0x02, 0x00, 0x80, 0x02, 0x49, 0x39, + 0x02, 0x00, 0x80, 0x02, 0x44, 0xb0, // Segment #2, Start Address 00ff000b, Length 3 0xff,0x00,0x0b,0x00,0x03,0x00, - 0x02, 0x44, 0x1a, + 0x02, 0x44, 0x72, // Segment #3, Start Address 00ff0013, Length 3 0xff,0x00,0x13,0x00,0x03,0x00, @@ -72,11 +72,11 @@ static unsigned char IMAGE_ARRAY_NAME[] // Segment #10, Start Address 00ff004b, Length 3 0xff,0x00,0x4b,0x00,0x03,0x00, - 0x02, 0x70, 0xd4, + 0x02, 0x6e, 0xc4, // Segment #11, Start Address 00ff0053, Length 3 0xff,0x00,0x53,0x00,0x03,0x00, - 0x02, 0x77, 0x9d, + 0x02, 0x75, 0x8d, // Segment #12, Start Address 00ff007b, Length 3 0xff,0x00,0x7b,0x00,0x03,0x00, @@ -86,657 +86,745 @@ static unsigned char IMAGE_ARRAY_NAME[] 0xff,0x00,0x80,0x00,0x07,0x00, 0x7e, 0x14, 0x00, 0x00, 0x02, 0x40, 0x51, -// Segment #14, Start Address 00ff4000, Length 15920 -0xff,0x00,0x00,0x40,0x30,0x3e, +// Segment #14, Start Address 00ff3000, Length 2178 +0xff,0x00,0x00,0x30,0x82,0x08, + 0x12, 0x37, 0x28, 0x12, 0x30, 0x3e, 0x12, 0x30, 0x54, 0x12, 0x30, 0xe5, 0x12, 0x31, 0x68, 0x12, + 0x35, 0x20, 0x12, 0x38, 0x58, 0x12, 0x31, 0x15, 0x12, 0x31, 0x40, 0x12, 0x30, 0xa0, 0x80, 0xe0, + 0xe5, 0x23, 0x60, 0x19, 0x7e, 0x14, 0x00, 0x00, 0x09, 0xb1, 0x01, 0xcf, 0xb4, 0x00, 0x02, 0x80, + 0x05, 0x14, 0x19, 0xb1, 0x01, 0xcf, 0xa5, 0x0b, 0xbe, 0x31, 0x2f, 0x78, 0xeb, 0x22, 0xc2, 0xaf, + 0x7e, 0xb3, 0x3f, 0xf1, 0xb4, 0x01, 0x0a, 0xc0, 0xf1, 0x75, 0xf1, 0x02, 0x12, 0x70, 0xef, 0xd0, + 0xf1, 0xd2, 0xaf, 0x22, 0xc2, 0xaf, 0xe5, 0x22, 0x60, 0x43, 0x7e, 0x07, 0x01, 0xe1, 0xbe, 0x04, + 0x03, 0x80, 0x38, 0x39, 0x7e, 0x04, 0x80, 0x00, 0x7e, 0x20, 0x00, 0x13, 0x50, 0x21, 0x09, 0xa0, + 0x00, 0x04, 0x4e, 0xa0, 0x05, 0x19, 0xa0, 0x00, 0x04, 0x0a, 0x32, 0x09, 0x53, 0x67, 0x8e, 0x5e, + 0x51, 0x27, 0x68, 0x0b, 0x09, 0xa0, 0x00, 0x10, 0x4e, 0xa0, 0x01, 0x19, 0xa0, 0x00, 0x10, 0x2e, + 0x04, 0x01, 0x00, 0xa5, 0x0a, 0xbe, 0x21, 0x2f, 0x78, 0xd1, 0x75, 0x22, 0x00, 0xd2, 0xaf, 0x22, + 0xc2, 0xaf, 0x7e, 0x20, 0x00, 0x7e, 0x30, 0x01, 0x7c, 0xb2, 0x23, 0x0a, 0x2b, 0x49, 0x32, 0x01, + 0x8f, 0xbe, 0x34, 0x00, 0x00, 0x68, 0x12, 0x7e, 0xb1, 0x21, 0xa5, 0x4b, 0x7a, 0xb1, 0x21, 0xca, + 0x19, 0x49, 0x22, 0x30, 0xd5, 0x99, 0x24, 0xda, 0x19, 0x3e, 0x30, 0xa5, 0x0a, 0xbe, 0x21, 0x2f, + 0x78, 0xd6, 0xd2, 0xaf, 0x22, 0x46, 0x0f, 0x49, 0x67, 0x4c, 0xbf, 0x50, 0x17, 0x53, 0x6f, 0x56, + 0xc7, 0x5a, 0x1f, 0x5d, 0x77, 0xc2, 0xaf, 0xe5, 0x32, 0x60, 0x14, 0x7e, 0x20, 0x00, 0x13, 0x50, + 0x07, 0xca, 0xb8, 0x12, 0x31, 0x02, 0xda, 0xb8, 0xa5, 0x0a, 0xbe, 0x21, 0x2f, 0x78, 0xef, 0xd2, + 0xaf, 0x22, 0xca, 0x28, 0x12, 0x67, 0xab, 0xda, 0x28, 0x40, 0x09, 0x0a, 0x22, 0x09, 0xb2, 0x67, + 0x8e, 0xf4, 0x52, 0x32, 0x22, 0xc2, 0xaf, 0xe5, 0x34, 0x60, 0x14, 0x7e, 0x20, 0x00, 0x13, 0x50, + 0x07, 0xca, 0xb8, 0x12, 0x31, 0x32, 0xda, 0xb8, 0xa5, 0x0a, 0xbe, 0x21, 0x2f, 0x78, 0xef, 0xd2, + 0xaf, 0x22, 0xca, 0x28, 0x0a, 0x22, 0x09, 0x42, 0x00, 0x3e, 0x12, 0x69, 0xc2, 0xda, 0x28, 0x22, + 0xc2, 0xaf, 0xe5, 0x35, 0x60, 0x14, 0x7e, 0x20, 0x00, 0x13, 0x50, 0x07, 0xca, 0xb8, 0x12, 0x31, + 0x5d, 0xda, 0xb8, 0xa5, 0x0a, 0xbe, 0x21, 0x2f, 0x78, 0xef, 0xd2, 0xaf, 0x22, 0xca, 0x28, 0x7e, + 0x40, 0x00, 0x12, 0x6c, 0x5b, 0xda, 0x28, 0x22, 0xc2, 0xaf, 0xe5, 0x23, 0x60, 0x14, 0x7e, 0x20, + 0x00, 0x13, 0x50, 0x07, 0xca, 0xb8, 0x12, 0x31, 0x85, 0xda, 0xb8, 0xa5, 0x0a, 0xbe, 0x21, 0x2f, + 0x78, 0xef, 0xd2, 0xaf, 0x22, 0x7c, 0xb2, 0x23, 0x0a, 0x2b, 0x49, 0x22, 0x31, 0x90, 0x89, 0x24, + 0x31, 0xa0, 0x32, 0x10, 0x32, 0x80, 0x32, 0xf0, 0x33, 0x60, 0x33, 0xd0, 0x34, 0x40, 0x34, 0xb0, + 0x7e, 0x27, 0x01, 0x8f, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x80, 0x00, 0x09, 0xb2, + 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, + 0x49, 0x30, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, + 0x01, 0x9f, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0x9f, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xcf, 0x80, + 0x2e, 0x7e, 0x63, 0x01, 0xcf, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, + 0xa0, 0xc8, 0x12, 0x62, 0xb6, 0x40, 0x18, 0x75, 0x31, 0xb3, 0x12, 0x7c, 0x15, 0xc2, 0x18, 0x6c, + 0x00, 0x7a, 0x03, 0x01, 0xcf, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, + 0x7e, 0x27, 0x01, 0x91, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x81, 0x00, 0x09, 0xb2, + 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, + 0x4c, 0x88, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, + 0x01, 0xa1, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0xa1, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xd0, 0x80, + 0x2e, 0x7e, 0x63, 0x01, 0xd0, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, + 0xa0, 0xc8, 0x12, 0x62, 0xb6, 0x40, 0x18, 0x75, 0x31, 0xb3, 0x12, 0x7c, 0x15, 0xc2, 0x19, 0x6c, + 0x00, 0x7a, 0x03, 0x01, 0xd0, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, + 0x7e, 0x27, 0x01, 0x93, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x82, 0x00, 0x09, 0xb2, + 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, + 0x4f, 0xe0, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, + 0x01, 0xa3, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0xa3, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xd1, 0x80, + 0x2e, 0x7e, 0x63, 0x01, 0xd1, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, + 0xa0, 0xc8, 0x12, 0x62, 0xb6, 0x40, 0x18, 0x75, 0x31, 0xb3, 0x12, 0x7c, 0x15, 0xc2, 0x1a, 0x6c, + 0x00, 0x7a, 0x03, 0x01, 0xd1, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, + 0x7e, 0x27, 0x01, 0x95, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x83, 0x00, 0x09, 0xb2, + 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, + 0x53, 0x38, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, + 0x01, 0xa5, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0xa5, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xd2, 0x80, + 0x2e, 0x7e, 0x63, 0x01, 0xd2, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, + 0xa0, 0xc8, 0x12, 0x62, 0xb6, 0x40, 0x18, 0x75, 0x31, 0xb3, 0x12, 0x7c, 0x15, 0xc2, 0x1b, 0x6c, + 0x00, 0x7a, 0x03, 0x01, 0xd2, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, + 0x7e, 0x27, 0x01, 0x97, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x84, 0x00, 0x09, 0xb2, + 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, + 0x56, 0x90, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, + 0x01, 0xa7, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0xa7, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xd3, 0x80, + 0x2e, 0x7e, 0x63, 0x01, 0xd3, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, + 0xa0, 0xc8, 0x12, 0x62, 0xb6, 0x40, 0x18, 0x75, 0x31, 0xb3, 0x12, 0x7c, 0x15, 0xc2, 0x1c, 0x6c, + 0x00, 0x7a, 0x03, 0x01, 0xd3, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, + 0x7e, 0x27, 0x01, 0x99, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x85, 0x00, 0x09, 0xb2, + 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, + 0x59, 0xe8, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, + 0x01, 0xa9, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0xa9, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xd4, 0x80, + 0x2e, 0x7e, 0x63, 0x01, 0xd4, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, + 0xa0, 0xc8, 0x12, 0x62, 0xb6, 0x40, 0x18, 0x75, 0x31, 0xb3, 0x12, 0x7c, 0x15, 0xc2, 0x1d, 0x6c, + 0x00, 0x7a, 0x03, 0x01, 0xd4, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, + 0x7e, 0x27, 0x01, 0x9b, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x86, 0x00, 0x09, 0xb2, + 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, + 0x5d, 0x40, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, + 0x01, 0xab, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0xab, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xd5, 0x80, + 0x2e, 0x7e, 0x63, 0x01, 0xd5, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, + 0xa0, 0xc8, 0x12, 0x62, 0xb6, 0x40, 0x18, 0x75, 0x31, 0xb3, 0x12, 0x7c, 0x15, 0xc2, 0x1e, 0x6c, + 0x00, 0x7a, 0x03, 0x01, 0xd5, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, + 0x7e, 0x27, 0x01, 0x9d, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x87, 0x00, 0x09, 0xb2, + 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, + 0x60, 0x98, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, + 0x01, 0xad, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0xad, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xd6, 0x80, + 0x2e, 0x7e, 0x63, 0x01, 0xd6, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, + 0xa0, 0xc8, 0x12, 0x62, 0xb6, 0x40, 0x18, 0x75, 0x31, 0xb3, 0x12, 0x7c, 0x15, 0xc2, 0x1f, 0x6c, + 0x00, 0x7a, 0x03, 0x01, 0xd6, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, + 0xc2, 0xaf, 0xe5, 0x24, 0x60, 0x14, 0x7e, 0x20, 0x00, 0x13, 0x50, 0x07, 0xca, 0xb8, 0x12, 0x35, + 0x3d, 0xda, 0xb8, 0xa5, 0x0a, 0xbe, 0x21, 0x2f, 0x78, 0xef, 0xd2, 0xaf, 0x22, 0x7c, 0xb2, 0x23, + 0x0a, 0x2b, 0x49, 0x22, 0x35, 0x48, 0x89, 0x24, 0x35, 0x58, 0x35, 0x92, 0x35, 0xcc, 0x36, 0x06, + 0x36, 0x40, 0x36, 0x7a, 0x36, 0xb4, 0x36, 0xee, 0x7e, 0x24, 0x80, 0x00, 0x09, 0xb2, 0x00, 0x14, + 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x49, 0x30, + 0x7d, 0x21, 0xda, 0x19, 0x5e, 0xb0, 0x01, 0x7e, 0xa0, 0x90, 0x12, 0x62, 0x93, 0x40, 0x12, 0x75, + 0x31, 0xb8, 0x12, 0x7c, 0x15, 0xc2, 0x20, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, + 0xf1, 0x22, 0x7e, 0x24, 0x81, 0x00, 0x09, 0xb2, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, + 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x4c, 0x88, 0x7d, 0x21, 0xda, 0x19, 0x5e, 0xb0, + 0x01, 0x7e, 0xa0, 0x90, 0x12, 0x62, 0x93, 0x40, 0x12, 0x75, 0x31, 0xb8, 0x12, 0x7c, 0x15, 0xc2, + 0x21, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x7e, 0x24, 0x82, 0x00, + 0x09, 0xb2, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, + 0x12, 0x12, 0x4f, 0xe0, 0x7d, 0x21, 0xda, 0x19, 0x5e, 0xb0, 0x01, 0x7e, 0xa0, 0x90, 0x12, 0x62, + 0x93, 0x40, 0x12, 0x75, 0x31, 0xb8, 0x12, 0x7c, 0x15, 0xc2, 0x22, 0xc0, 0xf1, 0x75, 0xf1, 0x01, + 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x7e, 0x24, 0x83, 0x00, 0x09, 0xb2, 0x00, 0x14, 0xca, 0xb8, + 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x53, 0x38, 0x7d, 0x21, + 0xda, 0x19, 0x5e, 0xb0, 0x01, 0x7e, 0xa0, 0x90, 0x12, 0x62, 0x93, 0x40, 0x12, 0x75, 0x31, 0xb8, + 0x12, 0x7c, 0x15, 0xc2, 0x23, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, + 0x7e, 0x24, 0x84, 0x00, 0x09, 0xb2, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, + 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x56, 0x90, 0x7d, 0x21, 0xda, 0x19, 0x5e, 0xb0, 0x01, 0x7e, + 0xa0, 0x90, 0x12, 0x62, 0x93, 0x40, 0x12, 0x75, 0x31, 0xb8, 0x12, 0x7c, 0x15, 0xc2, 0x24, 0xc0, + 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x7e, 0x24, 0x85, 0x00, 0x09, 0xb2, + 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, + 0x59, 0xe8, 0x7d, 0x21, 0xda, 0x19, 0x5e, 0xb0, 0x01, 0x7e, 0xa0, 0x90, 0x12, 0x62, 0x93, 0x40, + 0x12, 0x75, 0x31, 0xb8, 0x12, 0x7c, 0x15, 0xc2, 0x25, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, + 0xd9, 0xd0, 0xf1, 0x22, 0x7e, 0x24, 0x86, 0x00, 0x09, 0xb2, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, + 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x5d, 0x40, 0x7d, 0x21, 0xda, 0x19, + 0x5e, 0xb0, 0x01, 0x7e, 0xa0, 0x90, 0x12, 0x62, 0x93, 0x40, 0x12, 0x75, 0x31, 0xb8, 0x12, 0x7c, + 0x15, 0xc2, 0x26, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x7e, 0x24, + 0x87, 0x00, 0x09, 0xb2, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, + 0x19, 0x7d, 0x12, 0x12, 0x60, 0x98, 0x7d, 0x21, 0xda, 0x19, 0x5e, 0xb0, 0x01, 0x7e, 0xa0, 0x90, + 0x12, 0x62, 0x93, 0x40, 0x12, 0x75, 0x31, 0xb8, 0x12, 0x7c, 0x15, 0xc2, 0x27, 0xc0, 0xf1, 0x75, + 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0xc2, 0xaf, 0xe5, 0x33, 0x60, 0x14, 0x7e, 0x20, + 0x00, 0x13, 0x50, 0x07, 0xca, 0xb8, 0x12, 0x37, 0x45, 0xda, 0xb8, 0xa5, 0x0a, 0xbe, 0x21, 0x2f, + 0x78, 0xef, 0xd2, 0xaf, 0x22, 0x7c, 0xb2, 0x23, 0x0a, 0x2b, 0x49, 0x22, 0x37, 0x50, 0x89, 0x24, + 0x37, 0x60, 0x37, 0x7f, 0x37, 0x9e, 0x37, 0xbd, 0x37, 0xdc, 0x37, 0xfb, 0x38, 0x1a, 0x38, 0x39, + 0x7e, 0x24, 0x80, 0x00, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x48, 0x1b, 0xda, 0x19, 0x10, 0x04, 0x02, + 0x80, 0x0c, 0xd2, 0x01, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x7e, + 0x24, 0x81, 0x00, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x4b, 0x73, 0xda, 0x19, 0x10, 0x04, 0x02, 0x80, + 0x0c, 0xd2, 0x01, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x7e, 0x24, + 0x82, 0x00, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x4e, 0xcb, 0xda, 0x19, 0x10, 0x04, 0x02, 0x80, 0x0c, + 0xd2, 0x01, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x7e, 0x24, 0x83, + 0x00, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x52, 0x23, 0xda, 0x19, 0x10, 0x04, 0x02, 0x80, 0x0c, 0xd2, + 0x01, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x7e, 0x24, 0x84, 0x00, + 0xca, 0x19, 0x7d, 0x12, 0x12, 0x55, 0x7b, 0xda, 0x19, 0x10, 0x04, 0x02, 0x80, 0x0c, 0xd2, 0x01, + 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x7e, 0x24, 0x85, 0x00, 0xca, + 0x19, 0x7d, 0x12, 0x12, 0x58, 0xd3, 0xda, 0x19, 0x10, 0x04, 0x02, 0x80, 0x0c, 0xd2, 0x01, 0xc0, + 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x7e, 0x24, 0x86, 0x00, 0xca, 0x19, + 0x7d, 0x12, 0x12, 0x5c, 0x2b, 0xda, 0x19, 0x10, 0x04, 0x02, 0x80, 0x0c, 0xd2, 0x01, 0xc0, 0xf1, + 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x7e, 0x24, 0x87, 0x00, 0xca, 0x19, 0x7d, + 0x12, 0x12, 0x5f, 0x83, 0xda, 0x19, 0x10, 0x04, 0x02, 0x80, 0x0c, 0xd2, 0x01, 0xc0, 0xf1, 0x75, + 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0xc2, 0xaf, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0xa9, + 0x32, 0xf2, 0x1a, 0x7e, 0x07, 0x01, 0xe1, 0xbe, 0x04, 0x00, 0x00, 0x78, 0x10, 0xe5, 0xf5, 0x33, + 0x82, 0xe7, 0x40, 0x09, 0x85, 0x31, 0x31, 0x12, 0x7c, 0x15, 0x75, 0xf6, 0x00, 0xd0, 0xf1, 0xd2, + 0xaf, 0x22, + +// Segment #15, Start Address 00ff4000, Length 15381 +0xff,0x00,0x00,0x40,0x15,0x3c, 0x7e, 0x04, 0x00, 0x01, 0x7e, 0x14, 0x7f, 0xf8, 0x7e, 0x24, 0x00, 0xfe, 0x7d, 0x31, 0x0b, 0x1a, - 0x50, 0x1b, 0x0a, 0x50, 0x7e, 0x14, 0x40, 0x1b, 0x02, 0x40, 0x6a, 0x7e, 0xf8, 0x00, 0x59, 0xd2, - 0x04, 0xc2, 0x94, 0xd2, 0x95, 0x7e, 0xf4, 0x40, 0x2c, 0x02, 0x40, 0x7c, 0x12, 0x7f, 0x3f, 0xf5, - 0x2e, 0x7a, 0xa1, 0x2d, 0x7a, 0x11, 0x58, 0x12, 0x77, 0xda, 0x12, 0x40, 0xdc, 0x7e, 0xb3, 0x3f, - 0xf1, 0x60, 0x03, 0x12, 0x43, 0x68, 0x75, 0xf1, 0x00, 0x12, 0x78, 0x7f, 0xd2, 0xaf, 0x02, 0x44, - 0x06, 0x7e, 0x04, 0x00, 0xff, 0x7e, 0x18, 0x40, 0x5f, 0x7a, 0x1c, 0x00, 0x01, 0x89, 0x18, 0xa9, + 0x50, 0x1b, 0x0a, 0x50, 0x7e, 0x14, 0x40, 0x1b, 0x02, 0x40, 0x6a, 0x7e, 0xf8, 0x00, 0x6f, 0xd2, + 0x04, 0xc2, 0x94, 0xd2, 0x95, 0x7e, 0xf4, 0x40, 0x2c, 0x02, 0x40, 0x7c, 0x12, 0x7d, 0x30, 0xf5, + 0x2f, 0x7a, 0xa1, 0x30, 0x7a, 0x11, 0x6e, 0x12, 0x75, 0xca, 0x12, 0x40, 0xdc, 0x7e, 0xb3, 0x3f, + 0xf1, 0x60, 0x03, 0x12, 0x43, 0xd4, 0x75, 0xf1, 0x00, 0x12, 0x76, 0x6f, 0xd2, 0xaf, 0x02, 0x30, + 0x00, 0x7e, 0x04, 0x00, 0xff, 0x7e, 0x18, 0x40, 0x5f, 0x7a, 0x1c, 0x00, 0x01, 0x89, 0x18, 0xa9, 0x25, 0x87, 0x03, 0xa9, 0xd5, 0x87, 0xd2, 0x93, 0x89, 0x08, 0x7e, 0x04, 0x00, 0xff, 0x7e, 0x18, 0x40, 0x78, 0x7a, 0x1c, 0x00, 0x01, 0x89, 0x18, 0xc2, 0x93, 0x89, 0x08, 0x7e, 0x08, 0x00, 0x20, - 0x7e, 0x44, 0x04, 0x00, 0x7e, 0x40, 0x00, 0x7e, 0xe4, 0x40, 0x8e, 0x02, 0x7e, 0x4b, 0x7e, 0x08, - 0x01, 0x59, 0x7e, 0x44, 0x28, 0x7c, 0x7e, 0x40, 0x00, 0x7e, 0xe4, 0x40, 0xa0, 0x02, 0x7e, 0x4b, - 0x7e, 0x08, 0x00, 0x59, 0x7e, 0x44, 0x01, 0x00, 0x7e, 0x40, 0x53, 0x7e, 0xe4, 0x40, 0xb2, 0x02, - 0x7e, 0x4b, 0x75, 0x57, 0x20, 0x75, 0x56, 0x30, 0x7e, 0x04, 0x00, 0x08, 0x75, 0x54, 0x58, 0x75, - 0x55, 0x08, 0x75, 0x51, 0x08, 0x75, 0x53, 0x01, 0x75, 0x89, 0x01, 0x75, 0x8a, 0x01, 0x75, 0x8c, - 0x00, 0xd2, 0x8c, 0x7e, 0x04, 0x00, 0x02, 0x7a, 0x05, 0x42, 0x89, 0xf4, 0x75, 0xb7, 0x7f, 0x75, + 0x7e, 0x44, 0x04, 0x00, 0x7e, 0x40, 0x00, 0x7e, 0xe4, 0x40, 0x8e, 0x02, 0x7c, 0x30, 0x7e, 0x08, + 0x01, 0x6f, 0x7e, 0x44, 0x28, 0x7c, 0x7e, 0x40, 0x00, 0x7e, 0xe4, 0x40, 0xa0, 0x02, 0x7c, 0x30, + 0x7e, 0x08, 0x00, 0x6f, 0x7e, 0x44, 0x01, 0x00, 0x7e, 0x40, 0x53, 0x7e, 0xe4, 0x40, 0xb2, 0x02, + 0x7c, 0x30, 0x75, 0x6d, 0x20, 0x75, 0x6c, 0x30, 0x7e, 0x04, 0x00, 0x08, 0x75, 0x6a, 0x58, 0x75, + 0x6b, 0x08, 0x75, 0x67, 0x08, 0x75, 0x69, 0x01, 0x75, 0x89, 0x01, 0x75, 0x8a, 0x01, 0x75, 0x8c, + 0x00, 0xd2, 0x8c, 0x7e, 0x04, 0x00, 0x02, 0x7a, 0x05, 0x58, 0x89, 0xf4, 0x75, 0xb7, 0x7f, 0x75, 0xb8, 0x7f, 0x75, 0xb3, 0x07, 0x75, 0xb2, 0x07, 0xd2, 0xa9, 0x22, 0xd2, 0x92, 0xe4, 0xd5, 0xe0, - 0xfd, 0xc2, 0x92, 0x7e, 0x24, 0x80, 0x00, 0x7e, 0x11, 0x2e, 0x7e, 0xa0, 0x08, 0x19, 0xa2, 0x00, - 0x10, 0x2e, 0x24, 0x01, 0x00, 0xa5, 0xd9, 0xf2, 0x7e, 0x20, 0x00, 0x12, 0x41, 0x72, 0x0b, 0x20, - 0xbe, 0x21, 0x2e, 0x78, 0xf6, 0x22, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0x74, 0xbf, 0x19, 0xb0, + 0xfd, 0xc2, 0x92, 0x7e, 0x24, 0x80, 0x00, 0x7e, 0x11, 0x2f, 0x7e, 0xa0, 0x08, 0x19, 0xa2, 0x00, + 0x10, 0x2e, 0x24, 0x01, 0x00, 0xa5, 0xd9, 0xf2, 0x7e, 0x20, 0x00, 0x12, 0x41, 0x7e, 0x0b, 0x20, + 0xbe, 0x21, 0x2f, 0x78, 0xf6, 0x22, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0x74, 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0x74, 0x10, 0x19, 0xb0, 0x00, 0x08, 0x74, 0x80, 0x19, 0xb0, 0x00, 0x0c, 0x7e, 0x54, 0x00, 0x02, 0x19, 0xa0, 0x00, 0x04, 0x19, 0xb0, 0x00, 0x00, 0x74, 0x03, 0x19, 0xb0, 0x00, 0x0c, - 0x74, 0x07, 0x20, 0x68, 0x02, 0x74, 0x0f, 0x19, 0xb0, 0x00, 0x04, 0x30, 0x6b, 0x17, 0x74, 0xbf, - 0x19, 0xb0, 0x00, 0x0c, 0x74, 0x28, 0x20, 0x68, 0x02, 0x74, 0x20, 0x19, 0xb0, 0x00, 0x04, 0x74, - 0x03, 0x19, 0xb0, 0x00, 0x0c, 0x74, 0xa7, 0x19, 0xb0, 0x00, 0x08, 0x74, 0x0c, 0x19, 0xb0, 0x00, - 0x10, 0x22, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0xe4, 0x19, 0xb0, 0x00, 0x04, 0x09, 0xb0, 0x00, - 0x10, 0x54, 0x08, 0x19, 0xb0, 0x00, 0x10, 0x74, 0xa7, 0x19, 0xb0, 0x00, 0x08, 0x22, 0x7c, 0xb2, - 0x23, 0x0a, 0x2b, 0x49, 0x22, 0x41, 0x99, 0x89, 0x24, 0x41, 0xa9, 0x41, 0xc6, 0x41, 0xe3, 0x42, - 0x00, 0x42, 0x1d, 0x42, 0x3a, 0x42, 0x57, 0x42, 0x74, 0xc2, 0x10, 0xc2, 0x18, 0xc2, 0x08, 0x7e, - 0x04, 0x09, 0xcd, 0x7a, 0x07, 0x01, 0x59, 0x7a, 0x07, 0x01, 0x69, 0x6d, 0x00, 0x7a, 0x07, 0x01, - 0x79, 0x7a, 0x07, 0x01, 0x89, 0x22, 0xc2, 0x11, 0xc2, 0x19, 0xc2, 0x09, 0x7e, 0x04, 0x0d, 0xcd, - 0x7a, 0x07, 0x01, 0x5b, 0x7a, 0x07, 0x01, 0x6b, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x7b, 0x7a, 0x07, - 0x01, 0x8b, 0x22, 0xc2, 0x12, 0xc2, 0x1a, 0xc2, 0x0a, 0x7e, 0x04, 0x11, 0xcd, 0x7a, 0x07, 0x01, - 0x5d, 0x7a, 0x07, 0x01, 0x6d, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x7d, 0x7a, 0x07, 0x01, 0x8d, 0x22, - 0xc2, 0x13, 0xc2, 0x1b, 0xc2, 0x0b, 0x7e, 0x04, 0x15, 0xcd, 0x7a, 0x07, 0x01, 0x5f, 0x7a, 0x07, - 0x01, 0x6f, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x7f, 0x7a, 0x07, 0x01, 0x8f, 0x22, 0xc2, 0x14, 0xc2, - 0x1c, 0xc2, 0x0c, 0x7e, 0x04, 0x19, 0xcd, 0x7a, 0x07, 0x01, 0x61, 0x7a, 0x07, 0x01, 0x71, 0x6d, - 0x00, 0x7a, 0x07, 0x01, 0x81, 0x7a, 0x07, 0x01, 0x91, 0x22, 0xc2, 0x15, 0xc2, 0x1d, 0xc2, 0x0d, - 0x7e, 0x04, 0x1d, 0xcd, 0x7a, 0x07, 0x01, 0x63, 0x7a, 0x07, 0x01, 0x73, 0x6d, 0x00, 0x7a, 0x07, - 0x01, 0x83, 0x7a, 0x07, 0x01, 0x93, 0x22, 0xc2, 0x16, 0xc2, 0x1e, 0xc2, 0x0e, 0x7e, 0x04, 0x21, - 0xcd, 0x7a, 0x07, 0x01, 0x65, 0x7a, 0x07, 0x01, 0x75, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x85, 0x7a, - 0x07, 0x01, 0x95, 0x22, 0xc2, 0x17, 0xc2, 0x1f, 0xc2, 0x0f, 0x7e, 0x04, 0x25, 0xcd, 0x7a, 0x07, - 0x01, 0x67, 0x7a, 0x07, 0x01, 0x77, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x87, 0x7a, 0x07, 0x01, 0x97, - 0x22, 0x7c, 0xb2, 0x23, 0x0a, 0x2b, 0x49, 0x22, 0x42, 0x9c, 0x89, 0x24, 0x42, 0xac, 0x42, 0xc3, - 0x42, 0xda, 0x42, 0xf1, 0x43, 0x08, 0x43, 0x1f, 0x43, 0x36, 0x43, 0x4d, 0x30, 0x40, 0x07, 0x20, - 0x58, 0x04, 0xc2, 0x28, 0x80, 0x0c, 0x30, 0x48, 0x07, 0x20, 0x50, 0x04, 0xc2, 0x28, 0x80, 0x02, - 0xd2, 0x28, 0x22, 0x30, 0x41, 0x07, 0x20, 0x59, 0x04, 0xc2, 0x29, 0x80, 0x0c, 0x30, 0x49, 0x07, - 0x20, 0x51, 0x04, 0xc2, 0x29, 0x80, 0x02, 0xd2, 0x29, 0x22, 0x30, 0x42, 0x07, 0x20, 0x5a, 0x04, - 0xc2, 0x2a, 0x80, 0x0c, 0x30, 0x4a, 0x07, 0x20, 0x52, 0x04, 0xc2, 0x2a, 0x80, 0x02, 0xd2, 0x2a, - 0x22, 0x30, 0x43, 0x07, 0x20, 0x5b, 0x04, 0xc2, 0x2b, 0x80, 0x0c, 0x30, 0x4b, 0x07, 0x20, 0x53, - 0x04, 0xc2, 0x2b, 0x80, 0x02, 0xd2, 0x2b, 0x22, 0x30, 0x44, 0x07, 0x20, 0x5c, 0x04, 0xc2, 0x2c, - 0x80, 0x0c, 0x30, 0x4c, 0x07, 0x20, 0x54, 0x04, 0xc2, 0x2c, 0x80, 0x02, 0xd2, 0x2c, 0x22, 0x30, - 0x45, 0x07, 0x20, 0x5d, 0x04, 0xc2, 0x2d, 0x80, 0x0c, 0x30, 0x4d, 0x07, 0x20, 0x55, 0x04, 0xc2, - 0x2d, 0x80, 0x02, 0xd2, 0x2d, 0x22, 0x30, 0x46, 0x07, 0x20, 0x5e, 0x04, 0xc2, 0x2e, 0x80, 0x0c, - 0x30, 0x4e, 0x07, 0x20, 0x56, 0x04, 0xc2, 0x2e, 0x80, 0x02, 0xd2, 0x2e, 0x22, 0x30, 0x47, 0x07, - 0x20, 0x5f, 0x04, 0xc2, 0x2f, 0x80, 0x0c, 0x30, 0x4f, 0x07, 0x20, 0x57, 0x04, 0xc2, 0x2f, 0x80, - 0x02, 0xd2, 0x2f, 0x22, 0x43, 0xcc, 0x43, 0x79, 0xbe, 0xb0, 0x02, 0x40, 0x01, 0x22, 0x23, 0x0a, - 0x5b, 0x49, 0x55, 0x43, 0x64, 0x99, 0x54, 0xd3, 0x22, 0xa9, 0xc5, 0x87, 0x12, 0x43, 0xd7, 0x7e, - 0x04, 0x05, 0xcd, 0x7a, 0x07, 0x01, 0xc1, 0x7a, 0x07, 0x01, 0xc3, 0x7e, 0x04, 0x01, 0xcd, 0x7a, - 0x07, 0x01, 0xc7, 0x7a, 0x07, 0x01, 0xc9, 0x7e, 0x04, 0x76, 0xbd, 0x7a, 0x05, 0x4b, 0x75, 0xf1, - 0x01, 0x75, 0xe1, 0x1f, 0x75, 0xe4, 0x04, 0x75, 0xf4, 0x04, 0x75, 0xf1, 0x02, 0x75, 0xe1, 0x03, - 0x75, 0xe4, 0x04, 0x75, 0xf4, 0x04, 0x43, 0xa2, 0x1c, 0x12, 0x40, 0xeb, 0x7e, 0x20, 0x00, 0x12, - 0x41, 0x8e, 0x0b, 0x20, 0xbe, 0x21, 0x2e, 0x78, 0xf6, 0xd2, 0xa8, 0x22, 0xa9, 0xd5, 0x87, 0x12, - 0x43, 0xd7, 0xd2, 0x92, 0xc2, 0xa8, 0x22, 0x75, 0xa3, 0x00, 0x53, 0xa2, 0x03, 0x75, 0xc1, 0x00, - 0x53, 0xc0, 0x03, 0x7e, 0x00, 0x05, 0x7a, 0x01, 0xf1, 0x43, 0xf4, 0x80, 0x43, 0xe4, 0x80, 0xe5, - 0xf2, 0x54, 0x7f, 0x44, 0x08, 0xf5, 0xf2, 0xe5, 0xe2, 0x54, 0x7f, 0x44, 0x08, 0xf5, 0xe2, 0x75, - 0xe1, 0x10, 0xa5, 0xd8, 0xe1, 0x22, 0x12, 0x44, 0x76, 0x12, 0x44, 0x8c, 0x12, 0x45, 0x27, 0x12, - 0x45, 0x57, 0x12, 0x49, 0x0f, 0x12, 0x44, 0xd8, 0x80, 0xec, 0xca, 0x09, 0x12, 0x44, 0x58, 0x10, - 0x01, 0x12, 0xd5, 0x51, 0x1e, 0x63, 0x53, 0x01, 0x7e, 0x00, 0x54, 0x2e, 0x01, 0x53, 0xa5, 0xe6, - 0xf5, 0x51, 0x80, 0x12, 0x20, 0x02, 0x1e, 0x75, 0x53, 0x00, 0x85, 0x54, 0x51, 0xd2, 0x02, 0x74, - 0x00, 0x80, 0x0d, 0x30, 0x02, 0x0f, 0xc2, 0x02, 0x7e, 0x00, 0x56, 0x2e, 0x01, 0x53, 0xa5, 0xe6, - 0x53, 0x90, 0xcf, 0x42, 0x90, 0xda, 0x09, 0x32, 0xe5, 0x23, 0x60, 0x19, 0x7e, 0x14, 0x00, 0x00, - 0x09, 0xb1, 0x01, 0xb9, 0xb4, 0x00, 0x02, 0x80, 0x05, 0x14, 0x19, 0xb1, 0x01, 0xb9, 0xa5, 0x0a, - 0xbe, 0x21, 0x2e, 0x78, 0xeb, 0x22, 0xc2, 0xaf, 0x7e, 0xb3, 0x3f, 0xf1, 0xb4, 0x01, 0x0a, 0xc0, - 0xf1, 0x75, 0xf1, 0x02, 0x12, 0x72, 0xff, 0xd0, 0xf1, 0xd2, 0xaf, 0x22, 0xc2, 0xaf, 0xe5, 0x22, - 0x60, 0x43, 0x7e, 0x07, 0x01, 0xcb, 0xbe, 0x04, 0x03, 0x80, 0x38, 0x39, 0x7e, 0x04, 0x80, 0x00, - 0x7e, 0x20, 0x00, 0x13, 0x50, 0x21, 0x09, 0xa0, 0x00, 0x04, 0x4e, 0xa0, 0x05, 0x19, 0xa0, 0x00, - 0x04, 0x0a, 0x32, 0x09, 0x53, 0x6a, 0x93, 0x5e, 0x51, 0x27, 0x68, 0x0b, 0x09, 0xa0, 0x00, 0x10, - 0x4e, 0xa0, 0x01, 0x19, 0xa0, 0x00, 0x10, 0x2e, 0x04, 0x01, 0x00, 0xa5, 0x0a, 0xbe, 0x21, 0x2e, - 0x78, 0xd1, 0x75, 0x22, 0x00, 0xd2, 0xaf, 0x22, 0xc2, 0xaf, 0xe5, 0x26, 0x60, 0x36, 0x7e, 0x20, - 0x00, 0x7e, 0x30, 0x01, 0xe5, 0x26, 0xa5, 0x5b, 0x68, 0x21, 0x7c, 0xb2, 0x23, 0x0a, 0x2b, 0x49, - 0x32, 0x01, 0x79, 0xbe, 0x34, 0x00, 0x00, 0x68, 0x12, 0x7e, 0xb1, 0x21, 0xa5, 0x4b, 0x7a, 0xb1, - 0x21, 0xca, 0x19, 0x49, 0x22, 0x45, 0x17, 0x99, 0x24, 0xda, 0x19, 0x3e, 0x30, 0xa5, 0x0a, 0xbe, - 0x21, 0x2e, 0x78, 0xd0, 0xd2, 0xaf, 0x22, 0x4a, 0x98, 0x4d, 0xcd, 0x51, 0x02, 0x54, 0x37, 0x57, - 0x6c, 0x5a, 0xa1, 0x5d, 0xd6, 0x61, 0x0b, 0xc2, 0xaf, 0xe5, 0x24, 0x60, 0x14, 0x7e, 0x20, 0x00, - 0x13, 0x50, 0x07, 0xca, 0xb8, 0x12, 0x45, 0x44, 0xda, 0xb8, 0xa5, 0x0a, 0xbe, 0x21, 0x2e, 0x78, - 0xef, 0xd2, 0xaf, 0x22, 0xca, 0x28, 0x12, 0x6a, 0xac, 0xda, 0x28, 0x40, 0x09, 0x0a, 0x22, 0x09, - 0xb2, 0x6a, 0x93, 0xf4, 0x52, 0x24, 0x22, 0xc2, 0xaf, 0xe5, 0x23, 0x60, 0x14, 0x7e, 0x20, 0x00, - 0x13, 0x50, 0x07, 0xca, 0xb8, 0x12, 0x45, 0x74, 0xda, 0xb8, 0xa5, 0x0a, 0xbe, 0x21, 0x2e, 0x78, - 0xef, 0xd2, 0xaf, 0x22, 0x7c, 0xb2, 0x23, 0x0a, 0x2b, 0x49, 0x22, 0x45, 0x7f, 0x89, 0x24, 0x45, - 0x8f, 0x45, 0xff, 0x46, 0x6f, 0x46, 0xdf, 0x47, 0x4f, 0x47, 0xbf, 0x48, 0x2f, 0x48, 0x9f, 0x7e, - 0x27, 0x01, 0x79, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x80, 0x00, 0x09, 0xb2, 0x00, - 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x4d, - 0x96, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, 0x01, - 0x89, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0x89, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xb9, 0x80, 0x2e, - 0x7e, 0x63, 0x01, 0xb9, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, 0xa0, - 0xc8, 0x12, 0x65, 0xbf, 0x40, 0x18, 0x75, 0x2f, 0xb3, 0x12, 0x7e, 0x30, 0xc2, 0x18, 0x6c, 0x00, - 0x7a, 0x03, 0x01, 0xb9, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x71, 0xe9, 0xd0, 0xf1, 0x22, 0x7e, - 0x27, 0x01, 0x7b, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x81, 0x00, 0x09, 0xb2, 0x00, - 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x50, - 0xcb, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, 0x01, - 0x8b, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0x8b, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xba, 0x80, 0x2e, - 0x7e, 0x63, 0x01, 0xba, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, 0xa0, - 0xc8, 0x12, 0x65, 0xbf, 0x40, 0x18, 0x75, 0x2f, 0xb3, 0x12, 0x7e, 0x30, 0xc2, 0x19, 0x6c, 0x00, - 0x7a, 0x03, 0x01, 0xba, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x71, 0xe9, 0xd0, 0xf1, 0x22, 0x7e, - 0x27, 0x01, 0x7d, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x82, 0x00, 0x09, 0xb2, 0x00, - 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x54, - 0x00, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, 0x01, - 0x8d, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0x8d, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xbb, 0x80, 0x2e, - 0x7e, 0x63, 0x01, 0xbb, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, 0xa0, - 0xc8, 0x12, 0x65, 0xbf, 0x40, 0x18, 0x75, 0x2f, 0xb3, 0x12, 0x7e, 0x30, 0xc2, 0x1a, 0x6c, 0x00, - 0x7a, 0x03, 0x01, 0xbb, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x71, 0xe9, 0xd0, 0xf1, 0x22, 0x7e, - 0x27, 0x01, 0x7f, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x83, 0x00, 0x09, 0xb2, 0x00, - 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x57, - 0x35, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, 0x01, - 0x8f, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0x8f, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xbc, 0x80, 0x2e, - 0x7e, 0x63, 0x01, 0xbc, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, 0xa0, - 0xc8, 0x12, 0x65, 0xbf, 0x40, 0x18, 0x75, 0x2f, 0xb3, 0x12, 0x7e, 0x30, 0xc2, 0x1b, 0x6c, 0x00, - 0x7a, 0x03, 0x01, 0xbc, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x71, 0xe9, 0xd0, 0xf1, 0x22, 0x7e, - 0x27, 0x01, 0x81, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x84, 0x00, 0x09, 0xb2, 0x00, - 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x5a, - 0x6a, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, 0x01, - 0x91, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0x91, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xbd, 0x80, 0x2e, - 0x7e, 0x63, 0x01, 0xbd, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, 0xa0, - 0xc8, 0x12, 0x65, 0xbf, 0x40, 0x18, 0x75, 0x2f, 0xb3, 0x12, 0x7e, 0x30, 0xc2, 0x1c, 0x6c, 0x00, - 0x7a, 0x03, 0x01, 0xbd, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x71, 0xe9, 0xd0, 0xf1, 0x22, 0x7e, - 0x27, 0x01, 0x83, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x85, 0x00, 0x09, 0xb2, 0x00, - 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x5d, - 0x9f, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, 0x01, - 0x93, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0x93, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xbe, 0x80, 0x2e, - 0x7e, 0x63, 0x01, 0xbe, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, 0xa0, - 0xc8, 0x12, 0x65, 0xbf, 0x40, 0x18, 0x75, 0x2f, 0xb3, 0x12, 0x7e, 0x30, 0xc2, 0x1d, 0x6c, 0x00, - 0x7a, 0x03, 0x01, 0xbe, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x71, 0xe9, 0xd0, 0xf1, 0x22, 0x7e, - 0x27, 0x01, 0x85, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x86, 0x00, 0x09, 0xb2, 0x00, - 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x60, - 0xd4, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, 0x01, - 0x95, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0x95, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xbf, 0x80, 0x2e, - 0x7e, 0x63, 0x01, 0xbf, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, 0xa0, - 0xc8, 0x12, 0x65, 0xbf, 0x40, 0x18, 0x75, 0x2f, 0xb3, 0x12, 0x7e, 0x30, 0xc2, 0x1e, 0x6c, 0x00, - 0x7a, 0x03, 0x01, 0xbf, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x71, 0xe9, 0xd0, 0xf1, 0x22, 0x7e, - 0x27, 0x01, 0x87, 0xbe, 0x24, 0x00, 0x00, 0x78, 0x24, 0x7e, 0x24, 0x87, 0x00, 0x09, 0xb2, 0x00, - 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x0b, 0xca, 0x19, 0x7d, 0x12, 0x12, 0x64, - 0x09, 0x7d, 0x21, 0xda, 0x19, 0x30, 0xe6, 0x18, 0x7e, 0x60, 0x00, 0x80, 0x1e, 0xbe, 0x27, 0x01, - 0x97, 0x68, 0x0d, 0x7a, 0x27, 0x01, 0x97, 0x7e, 0x60, 0x9c, 0x7a, 0x63, 0x01, 0xc0, 0x80, 0x2e, - 0x7e, 0x63, 0x01, 0xc0, 0xa5, 0xbe, 0x00, 0x26, 0x7e, 0x60, 0x01, 0x7e, 0xb0, 0x00, 0x7e, 0xa0, - 0xc8, 0x12, 0x65, 0xbf, 0x40, 0x18, 0x75, 0x2f, 0xb3, 0x12, 0x7e, 0x30, 0xc2, 0x1f, 0x6c, 0x00, - 0x7a, 0x03, 0x01, 0xc0, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x71, 0xe9, 0xd0, 0xf1, 0x22, 0xc2, - 0xaf, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0xa9, 0x32, 0xf2, 0x1a, 0x7e, 0x07, 0x01, 0xcb, 0xbe, 0x04, - 0x00, 0x00, 0x78, 0x10, 0xe5, 0xf5, 0x33, 0x82, 0xe7, 0x40, 0x09, 0x85, 0x31, 0x2f, 0x12, 0x7e, - 0x30, 0x75, 0xf6, 0x00, 0xd0, 0xf1, 0xd2, 0xaf, 0x22, 0xc0, 0xd0, 0xc0, 0xd1, 0xc0, 0xe0, 0xc0, - 0xf0, 0xca, 0x0b, 0xca, 0x1b, 0xca, 0x2b, 0xd2, 0x01, 0x75, 0x2f, 0x89, 0x12, 0x7e, 0x30, 0x7e, - 0x14, 0x80, 0x00, 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, 0x02, 0x49, 0xd8, 0x20, 0x70, 0x5a, - 0xa5, 0x0a, 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, 0x02, 0x49, 0xf0, 0x20, 0x71, 0x4b, 0xa5, - 0x0a, 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, 0x02, 0x4a, 0x08, 0xa5, 0x0a, 0x09, 0xb1, 0x00, - 0x08, 0x20, 0xe0, 0x03, 0x02, 0x4a, 0x20, 0x20, 0x72, 0x30, 0xa5, 0x0a, 0x09, 0xb1, 0x00, 0x08, - 0x20, 0xe0, 0x03, 0x02, 0x4a, 0x38, 0xa5, 0x0a, 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, 0x02, - 0x4a, 0x50, 0xa5, 0x0a, 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, 0x02, 0x4a, 0x68, 0xa5, 0x0a, - 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, 0x02, 0x4a, 0x80, 0x30, 0x04, 0x0c, 0xc2, 0x04, 0xc0, - 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x71, 0xe9, 0xd0, 0xf1, 0xda, 0x2b, 0xda, 0x1b, 0xda, 0x0b, 0xd0, - 0xf0, 0xd0, 0xe0, 0xd0, 0xd1, 0xd0, 0xd0, 0x32, 0x75, 0x2f, 0x80, 0x12, 0x7e, 0x30, 0x54, 0x3e, - 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x66, 0x34, 0xca, 0x06, 0x4f, 0x49, 0x89, 0x54, - 0x75, 0x2f, 0x81, 0x12, 0x7e, 0x30, 0x54, 0x3e, 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, - 0x66, 0x74, 0xca, 0x06, 0x4f, 0x49, 0x89, 0x54, 0x75, 0x2f, 0x82, 0x12, 0x7e, 0x30, 0x54, 0x3e, - 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x66, 0xb4, 0xca, 0x06, 0x4f, 0x49, 0x89, 0x54, - 0x75, 0x2f, 0x83, 0x12, 0x7e, 0x30, 0x54, 0x3e, 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, - 0x66, 0xf4, 0xca, 0x06, 0x4f, 0x49, 0x89, 0x54, 0x75, 0x2f, 0x84, 0x12, 0x7e, 0x30, 0x54, 0x3e, - 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x67, 0x34, 0xca, 0x06, 0x4f, 0x49, 0x89, 0x54, - 0x75, 0x2f, 0x85, 0x12, 0x7e, 0x30, 0x54, 0x3e, 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, - 0x67, 0x74, 0xca, 0x06, 0x4f, 0x49, 0x89, 0x54, 0x75, 0x2f, 0x86, 0x12, 0x7e, 0x30, 0x54, 0x3e, - 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x67, 0xb4, 0xca, 0x06, 0x4f, 0x49, 0x89, 0x54, - 0x75, 0x2f, 0x87, 0x12, 0x7e, 0x30, 0x54, 0x3e, 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, - 0x67, 0xf4, 0xca, 0x06, 0x4f, 0x49, 0x89, 0x54, 0x10, 0x08, 0x01, 0x22, 0x20, 0x28, 0x03, 0xd2, - 0x08, 0x22, 0x75, 0x2f, 0xa0, 0x12, 0x7e, 0x30, 0x7e, 0x14, 0x80, 0x00, 0x80, 0x06, 0x20, 0x28, - 0x03, 0xd2, 0x08, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, - 0x03, 0x12, 0x4d, 0x96, 0x30, 0x30, 0x06, 0x20, 0xe6, 0x4f, 0xd2, 0x08, 0x22, 0x30, 0xe6, 0x02, - 0xd2, 0x60, 0x7e, 0x37, 0x01, 0x79, 0x7e, 0x27, 0x01, 0x99, 0x9d, 0x32, 0x40, 0x31, 0x7d, 0x02, - 0x2e, 0x05, 0x32, 0x7a, 0x05, 0x32, 0x7a, 0x37, 0x01, 0x79, 0x7e, 0x37, 0x01, 0x59, 0x7d, 0x43, - 0x2d, 0x42, 0xbe, 0x44, 0x0d, 0xcc, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x59, 0x75, 0x2f, 0x94, 0x12, - 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x12, 0x68, 0x34, 0x10, 0x60, 0xc4, 0x22, 0xc2, - 0x60, 0x2d, 0x23, 0x68, 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, 0x79, 0xbe, 0x24, 0x00, - 0x00, 0x68, 0x6a, 0xbe, 0x27, 0x01, 0x99, 0x28, 0x04, 0x7e, 0x27, 0x01, 0x99, 0x7e, 0x37, 0x01, - 0x79, 0x9d, 0x32, 0x7d, 0x02, 0x2e, 0x05, 0x32, 0x7a, 0x05, 0x32, 0x7a, 0x37, 0x01, 0x79, 0x7e, - 0x37, 0x01, 0x59, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x0d, 0xcc, 0x38, 0x13, 0x7a, 0x47, 0x01, - 0x59, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x02, 0x68, 0x34, - 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x0d, 0xcd, - 0x9d, 0x24, 0x12, 0x68, 0x34, 0x7e, 0x34, 0x09, 0xcd, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, - 0x59, 0x12, 0x68, 0x34, 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x4b, 0x0b, 0x22, 0xd2, 0x08, 0x7e, - 0x04, 0x09, 0xcd, 0x7a, 0x07, 0x01, 0x59, 0x7a, 0x07, 0x01, 0x69, 0x75, 0x2f, 0x94, 0x12, 0x7e, - 0x30, 0x75, 0x2f, 0x00, 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x92, 0x12, 0x7e, 0x30, 0xd2, 0x04, - 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, 0x7e, 0x37, 0x01, 0xcb, - 0x7e, 0x27, 0x01, 0xa9, 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, 0x04, 0x00, 0x38, 0x3c, - 0x7d, 0x02, 0x2e, 0x05, 0x30, 0x7a, 0x05, 0x30, 0x7a, 0x37, 0x01, 0xcb, 0x7e, 0x37, 0x01, 0xc9, - 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x05, 0xcc, 0x38, 0x44, 0x7a, 0x47, 0x01, 0xc9, 0x7e, 0x24, - 0x00, 0x00, 0x2e, 0x27, 0x01, 0xa9, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, 0x51, 0x2f, 0x12, 0x7e, - 0x30, 0xbe, 0x50, 0x38, 0x78, 0x03, 0x02, 0x69, 0x1f, 0x02, 0x69, 0x04, 0x75, 0x2f, 0x99, 0x12, - 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x38, 0x0a, 0x09, - 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x10, 0x22, 0x80, 0x7f, 0x7a, 0x51, - 0x2f, 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x05, 0xcd, 0x9d, 0x24, 0x7e, 0x64, 0x00, 0x00, 0x2e, 0x67, - 0x01, 0xa9, 0x9e, 0x24, 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, 0x35, 0x12, 0x69, 0x04, - 0x7e, 0x34, 0x01, 0xcd, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xc9, 0x02, 0x69, 0x04, 0x7a, - 0x39, 0xc0, 0x7e, 0x34, 0x01, 0xcd, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, 0x44, 0x80, 0xe5, 0x9d, - 0x32, 0x7c, 0xb6, 0x54, 0x0f, 0x23, 0x23, 0x23, 0x44, 0x00, 0x7a, 0x69, 0xb0, 0x7a, 0x79, 0x70, - 0x0b, 0x35, 0x75, 0x2f, 0x93, 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, 0x7e, 0x30, 0xbd, 0x04, - 0x68, 0x2b, 0x7a, 0x07, 0x01, 0xc9, 0x7e, 0x47, 0x01, 0xcb, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xcb, - 0x2e, 0x35, 0x30, 0x7a, 0x35, 0x30, 0x22, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0x13, - 0x22, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x28, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x2a, 0x7e, 0x04, 0x01, - 0xcd, 0x80, 0xcf, 0x7e, 0x07, 0x01, 0xcb, 0x7e, 0x24, 0x03, 0xfe, 0x9d, 0x20, 0x28, 0x40, 0x7e, - 0x07, 0x01, 0xc9, 0x7e, 0x44, 0x05, 0xcd, 0x7d, 0x60, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd2, 0x7d, - 0x70, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd0, 0x7d, 0x54, 0x9d, 0x50, 0xbd, 0x25, 0x40, 0x02, 0x7d, - 0x25, 0x7d, 0x32, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, 0x01, 0x31, 0xda, 0xb8, - 0x7e, 0x19, 0xb0, 0x7a, 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, 0x02, 0x4c, 0x6f, 0x75, - 0x2f, 0x99, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, - 0x38, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x10, 0x22, 0xda, - 0xb8, 0x30, 0xe0, 0xd8, 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x4c, 0x6f, 0xda, 0xb8, 0x02, - 0x4d, 0x96, 0x09, 0xb1, 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x2f, 0x90, 0x12, 0x7e, 0x30, 0xf5, - 0x2f, 0x12, 0x7e, 0x30, 0xa5, 0xfd, 0x5e, 0x50, 0x0a, 0x68, 0x1d, 0xa5, 0xfd, 0x5e, 0x50, 0x20, - 0x68, 0x04, 0xd2, 0x58, 0x80, 0x02, 0xc2, 0x58, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, - 0x50, 0x80, 0x02, 0xc2, 0x50, 0x12, 0x42, 0xac, 0x02, 0x65, 0x9c, 0x75, 0x2f, 0x91, 0x12, 0x7e, - 0x30, 0x09, 0xb1, 0x00, 0x14, 0x7a, 0xb1, 0x2f, 0x12, 0x7e, 0x30, 0x20, 0xe0, 0x08, 0xd2, 0x04, - 0x7e, 0xa0, 0x80, 0x02, 0x65, 0x9c, 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, 0x12, 0x65, - 0x9c, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, 0x61, 0x00, - 0x00, 0x12, 0x65, 0xbf, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x4c, 0xc3, 0x75, - 0x2f, 0x95, 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x96, 0x12, 0x7e, 0x30, 0x22, 0x10, 0x09, 0x01, - 0x22, 0x20, 0x29, 0x03, 0xd2, 0x09, 0x22, 0x75, 0x2f, 0xa1, 0x12, 0x7e, 0x30, 0x7e, 0x14, 0x81, - 0x00, 0x80, 0x06, 0x20, 0x29, 0x03, 0xd2, 0x09, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x5e, - 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, 0x12, 0x50, 0xcb, 0x30, 0x31, 0x06, 0x20, 0xe6, 0x4f, 0xd2, - 0x09, 0x22, 0x30, 0xe6, 0x02, 0xd2, 0x61, 0x7e, 0x37, 0x01, 0x7b, 0x7e, 0x27, 0x01, 0x9b, 0x9d, - 0x32, 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x34, 0x7a, 0x05, 0x34, 0x7a, 0x37, 0x01, 0x7b, 0x7e, - 0x37, 0x01, 0x5b, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x11, 0xcc, 0x38, 0x68, 0x7a, 0x47, 0x01, - 0x5b, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x12, 0x68, 0x34, - 0x10, 0x61, 0xc4, 0x22, 0xc2, 0x61, 0x2d, 0x23, 0x68, 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, - 0x01, 0x7b, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, 0x27, 0x01, 0x9b, 0x28, 0x04, 0x7e, 0x27, - 0x01, 0x9b, 0x7e, 0x37, 0x01, 0x7b, 0x9d, 0x32, 0x7d, 0x02, 0x2e, 0x05, 0x34, 0x7a, 0x05, 0x34, - 0x7a, 0x37, 0x01, 0x7b, 0x7e, 0x37, 0x01, 0x5b, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x11, 0xcc, - 0x38, 0x13, 0x7a, 0x47, 0x01, 0x5b, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, - 0x7e, 0x30, 0x02, 0x68, 0x34, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, - 0x30, 0x9e, 0x44, 0x11, 0xcd, 0x9d, 0x24, 0x12, 0x68, 0x34, 0x7e, 0x34, 0x0d, 0xcd, 0x7d, 0x24, - 0x2d, 0x43, 0x7a, 0x47, 0x01, 0x5b, 0x12, 0x68, 0x34, 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x4e, - 0x40, 0x22, 0xd2, 0x09, 0x7e, 0x04, 0x0d, 0xcd, 0x7a, 0x07, 0x01, 0x5b, 0x7a, 0x07, 0x01, 0x6b, - 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x75, 0x2f, 0x00, 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x92, - 0x12, 0x7e, 0x30, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, - 0x70, 0x7e, 0x37, 0x01, 0xcb, 0x7e, 0x27, 0x01, 0xab, 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, - 0x34, 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, 0x30, 0x7a, 0x05, 0x30, 0x7a, 0x37, 0x01, - 0xcb, 0x7e, 0x37, 0x01, 0xc9, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x05, 0xcc, 0x38, 0x44, 0x7a, - 0x47, 0x01, 0xc9, 0x7e, 0x24, 0x01, 0x00, 0x2e, 0x27, 0x01, 0xab, 0x1b, 0x38, 0x20, 0x0b, 0x35, - 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0xbe, 0x50, 0x38, 0x78, 0x03, 0x02, 0x69, 0x1f, 0x02, 0x69, - 0x04, 0x75, 0x2f, 0x99, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, - 0x04, 0x30, 0x39, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x11, - 0x22, 0x80, 0x7f, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x05, 0xcd, 0x9d, 0x24, 0x7e, - 0x64, 0x01, 0x00, 0x2e, 0x67, 0x01, 0xab, 0x9e, 0x24, 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, - 0x0b, 0x35, 0x12, 0x69, 0x04, 0x7e, 0x34, 0x01, 0xcd, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, - 0xc9, 0x02, 0x69, 0x04, 0x7a, 0x39, 0xc0, 0x7e, 0x34, 0x01, 0xcd, 0x7a, 0x39, 0xd0, 0x0b, 0x34, - 0x1b, 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, 0x0f, 0x23, 0x23, 0x23, 0x44, 0x01, 0x7a, - 0x69, 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x2f, 0x93, 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, - 0x12, 0x7e, 0x30, 0xbd, 0x04, 0x68, 0x2b, 0x7a, 0x07, 0x01, 0xc9, 0x7e, 0x47, 0x01, 0xcb, 0x2d, - 0x43, 0x7a, 0x47, 0x01, 0xcb, 0x2e, 0x35, 0x30, 0x7a, 0x35, 0x30, 0x22, 0xd2, 0x04, 0x09, 0xb1, - 0x00, 0x14, 0x20, 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x28, 0x7e, 0x04, 0x01, 0xcd, - 0x80, 0x2a, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0xcf, 0x7e, 0x07, 0x01, 0xcb, 0x7e, 0x24, 0x03, 0xfe, - 0x9d, 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xc9, 0x7e, 0x44, 0x05, 0xcd, 0x7d, 0x60, 0x0b, 0x04, - 0xbd, 0x04, 0x68, 0xd2, 0x7d, 0x70, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd0, 0x7d, 0x54, 0x9d, 0x50, - 0xbd, 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, - 0xb4, 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, - 0xe7, 0x02, 0x4f, 0xa4, 0x75, 0x2f, 0x99, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, - 0x19, 0xb1, 0x00, 0x04, 0x30, 0x39, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, - 0x10, 0xd2, 0x11, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, - 0x4f, 0xa4, 0xda, 0xb8, 0x02, 0x50, 0xcb, 0x09, 0xb1, 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x2f, - 0x90, 0x12, 0x7e, 0x30, 0xf5, 0x2f, 0x12, 0x7e, 0x30, 0xa5, 0xfd, 0x5e, 0x50, 0x0a, 0x68, 0x1d, - 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x59, 0x80, 0x02, 0xc2, 0x59, 0xa5, 0xfd, 0x5e, - 0x50, 0x80, 0x68, 0x04, 0xd2, 0x51, 0x80, 0x02, 0xc2, 0x51, 0x12, 0x42, 0xc3, 0x02, 0x65, 0x9c, - 0x75, 0x2f, 0x91, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x14, 0x7a, 0xb1, 0x2f, 0x12, 0x7e, 0x30, - 0x20, 0xe0, 0x08, 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x65, 0x9c, 0xd2, 0x04, 0x30, 0xe1, 0x06, - 0x7e, 0xa0, 0x80, 0x12, 0x65, 0x9c, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, 0xda, 0xb8, 0x68, 0x12, 0x7e, - 0xa0, 0xc0, 0x09, 0x61, 0x00, 0x00, 0x12, 0x65, 0xbf, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0xdb, - 0x22, 0x02, 0x4f, 0xf8, 0x75, 0x2f, 0x95, 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x96, 0x12, 0x7e, - 0x30, 0x22, 0x10, 0x0a, 0x01, 0x22, 0x20, 0x2a, 0x03, 0xd2, 0x0a, 0x22, 0x75, 0x2f, 0xa2, 0x12, - 0x7e, 0x30, 0x7e, 0x14, 0x82, 0x00, 0x80, 0x06, 0x20, 0x2a, 0x03, 0xd2, 0x0a, 0x22, 0x09, 0xb1, - 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, 0x12, 0x54, 0x00, 0x30, 0x32, - 0x06, 0x20, 0xe6, 0x4f, 0xd2, 0x0a, 0x22, 0x30, 0xe6, 0x02, 0xd2, 0x62, 0x7e, 0x37, 0x01, 0x7d, - 0x7e, 0x27, 0x01, 0x9d, 0x9d, 0x32, 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x36, 0x7a, 0x05, 0x36, - 0x7a, 0x37, 0x01, 0x7d, 0x7e, 0x37, 0x01, 0x5d, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x15, 0xcc, - 0x38, 0x68, 0x7a, 0x47, 0x01, 0x5d, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, - 0x7e, 0x30, 0x12, 0x68, 0x34, 0x10, 0x62, 0xc4, 0x22, 0xc2, 0x62, 0x2d, 0x23, 0x68, 0x78, 0x6d, - 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, 0x7d, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, 0x27, 0x01, - 0x9d, 0x28, 0x04, 0x7e, 0x27, 0x01, 0x9d, 0x7e, 0x37, 0x01, 0x7d, 0x9d, 0x32, 0x7d, 0x02, 0x2e, - 0x05, 0x36, 0x7a, 0x05, 0x36, 0x7a, 0x37, 0x01, 0x7d, 0x7e, 0x37, 0x01, 0x5d, 0x7d, 0x43, 0x2d, - 0x42, 0xbe, 0x44, 0x15, 0xcc, 0x38, 0x13, 0x7a, 0x47, 0x01, 0x5d, 0x75, 0x2f, 0x94, 0x12, 0x7e, - 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x02, 0x68, 0x34, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, - 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x15, 0xcd, 0x9d, 0x24, 0x12, 0x68, 0x34, 0x7e, - 0x34, 0x11, 0xcd, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0x5d, 0x12, 0x68, 0x34, 0xbe, 0x25, - 0x20, 0x78, 0x03, 0x02, 0x51, 0x75, 0x22, 0xd2, 0x0a, 0x7e, 0x04, 0x11, 0xcd, 0x7a, 0x07, 0x01, - 0x5d, 0x7a, 0x07, 0x01, 0x6d, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x75, 0x2f, 0x00, 0x12, 0x7e, - 0x30, 0x22, 0x75, 0x2f, 0x92, 0x12, 0x7e, 0x30, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, - 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, 0x7e, 0x37, 0x01, 0xcb, 0x7e, 0x27, 0x01, 0xad, 0x2e, 0x24, - 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, 0x30, 0x7a, - 0x05, 0x30, 0x7a, 0x37, 0x01, 0xcb, 0x7e, 0x37, 0x01, 0xc9, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, - 0x05, 0xcc, 0x38, 0x44, 0x7a, 0x47, 0x01, 0xc9, 0x7e, 0x24, 0x02, 0x00, 0x2e, 0x27, 0x01, 0xad, - 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0xbe, 0x50, 0x38, 0x78, 0x03, - 0x02, 0x69, 0x1f, 0x02, 0x69, 0x04, 0x75, 0x2f, 0x99, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, - 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3a, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, - 0xb1, 0x00, 0x10, 0xd2, 0x12, 0x22, 0x80, 0x7f, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x9e, 0x44, - 0x05, 0xcd, 0x9d, 0x24, 0x7e, 0x64, 0x02, 0x00, 0x2e, 0x67, 0x01, 0xad, 0x9e, 0x24, 0x00, 0x02, - 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, 0x35, 0x12, 0x69, 0x04, 0x7e, 0x34, 0x01, 0xcd, 0x7d, 0x24, - 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xc9, 0x02, 0x69, 0x04, 0x7a, 0x39, 0xc0, 0x7e, 0x34, 0x01, 0xcd, - 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, 0x0f, 0x23, - 0x23, 0x23, 0x44, 0x02, 0x7a, 0x69, 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x2f, 0x93, 0x12, - 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, 0x7e, 0x30, 0xbd, 0x04, 0x68, 0x2b, 0x7a, 0x07, 0x01, 0xc9, - 0x7e, 0x47, 0x01, 0xcb, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xcb, 0x2e, 0x35, 0x30, 0x7a, 0x35, 0x30, - 0x22, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xcd, 0x80, - 0x28, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x2a, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0xcf, 0x7e, 0x07, 0x01, - 0xcb, 0x7e, 0x24, 0x03, 0xfe, 0x9d, 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xc9, 0x7e, 0x44, 0x05, - 0xcd, 0x7d, 0x60, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd2, 0x7d, 0x70, 0x0b, 0x04, 0xbd, 0x04, 0x68, - 0xd0, 0x7d, 0x54, 0x9d, 0x50, 0xbd, 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, 0xb1, 0x00, - 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, 0x09, 0xb0, - 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, 0x02, 0x52, 0xd9, 0x75, 0x2f, 0x99, 0x12, 0x7e, 0x30, 0x09, - 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3a, 0x0a, 0x09, 0xb1, 0x00, 0x10, - 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x12, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, 0xbd, 0x32, - 0x68, 0x07, 0xca, 0xb8, 0x12, 0x52, 0xd9, 0xda, 0xb8, 0x02, 0x54, 0x00, 0x09, 0xb1, 0x00, 0x18, - 0x7e, 0xa0, 0x88, 0x75, 0x2f, 0x90, 0x12, 0x7e, 0x30, 0xf5, 0x2f, 0x12, 0x7e, 0x30, 0xa5, 0xfd, - 0x5e, 0x50, 0x0a, 0x68, 0x1d, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x5a, 0x80, 0x02, - 0xc2, 0x5a, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x52, 0x80, 0x02, 0xc2, 0x52, 0x12, - 0x42, 0xda, 0x02, 0x65, 0x9c, 0x75, 0x2f, 0x91, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x14, 0x7a, - 0xb1, 0x2f, 0x12, 0x7e, 0x30, 0x20, 0xe0, 0x08, 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x65, 0x9c, - 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, 0x12, 0x65, 0x9c, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, - 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, 0x61, 0x00, 0x00, 0x12, 0x65, 0xbf, 0x09, 0xb1, - 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x53, 0x2d, 0x75, 0x2f, 0x95, 0x12, 0x7e, 0x30, 0x22, - 0x75, 0x2f, 0x96, 0x12, 0x7e, 0x30, 0x22, 0x10, 0x0b, 0x01, 0x22, 0x20, 0x2b, 0x03, 0xd2, 0x0b, - 0x22, 0x75, 0x2f, 0xa3, 0x12, 0x7e, 0x30, 0x7e, 0x14, 0x83, 0x00, 0x80, 0x06, 0x20, 0x2b, 0x03, + 0x74, 0x07, 0xa9, 0x20, 0x30, 0x0b, 0xa9, 0x35, 0x30, 0x05, 0xbe, 0x20, 0x01, 0x28, 0x02, 0x74, + 0x0f, 0x19, 0xb0, 0x00, 0x04, 0xa9, 0x33, 0x30, 0x18, 0x74, 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0x74, + 0x28, 0xa9, 0x20, 0x30, 0x02, 0x74, 0x20, 0x19, 0xb0, 0x00, 0x04, 0x74, 0x03, 0x19, 0xb0, 0x00, + 0x0c, 0x74, 0xa7, 0x19, 0xb0, 0x00, 0x08, 0x74, 0x0c, 0x19, 0xb0, 0x00, 0x10, 0x22, 0x7e, 0x04, + 0x80, 0x00, 0x4c, 0x02, 0xe4, 0x19, 0xb0, 0x00, 0x04, 0x09, 0xb0, 0x00, 0x10, 0x54, 0x08, 0x19, + 0xb0, 0x00, 0x10, 0x74, 0xa7, 0x19, 0xb0, 0x00, 0x08, 0x22, 0x7c, 0xb2, 0x23, 0x0a, 0x2b, 0x49, + 0x22, 0x41, 0xa5, 0x89, 0x24, 0x41, 0xb5, 0x41, 0xd4, 0x41, 0xf3, 0x42, 0x12, 0x42, 0x31, 0x42, + 0x50, 0x42, 0x6f, 0x42, 0x8e, 0xc2, 0x10, 0xc2, 0x18, 0xc2, 0x20, 0xc2, 0x08, 0x7e, 0x04, 0x09, + 0xe3, 0x7a, 0x07, 0x01, 0x6f, 0x7a, 0x07, 0x01, 0x7f, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x8f, 0x7a, + 0x07, 0x01, 0x9f, 0x22, 0xc2, 0x11, 0xc2, 0x19, 0xc2, 0x21, 0xc2, 0x09, 0x7e, 0x04, 0x0d, 0xe3, + 0x7a, 0x07, 0x01, 0x71, 0x7a, 0x07, 0x01, 0x81, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x91, 0x7a, 0x07, + 0x01, 0xa1, 0x22, 0xc2, 0x12, 0xc2, 0x1a, 0xc2, 0x22, 0xc2, 0x0a, 0x7e, 0x04, 0x11, 0xe3, 0x7a, + 0x07, 0x01, 0x73, 0x7a, 0x07, 0x01, 0x83, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x93, 0x7a, 0x07, 0x01, + 0xa3, 0x22, 0xc2, 0x13, 0xc2, 0x1b, 0xc2, 0x23, 0xc2, 0x0b, 0x7e, 0x04, 0x15, 0xe3, 0x7a, 0x07, + 0x01, 0x75, 0x7a, 0x07, 0x01, 0x85, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x95, 0x7a, 0x07, 0x01, 0xa5, + 0x22, 0xc2, 0x14, 0xc2, 0x1c, 0xc2, 0x24, 0xc2, 0x0c, 0x7e, 0x04, 0x19, 0xe3, 0x7a, 0x07, 0x01, + 0x77, 0x7a, 0x07, 0x01, 0x87, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x97, 0x7a, 0x07, 0x01, 0xa7, 0x22, + 0xc2, 0x15, 0xc2, 0x1d, 0xc2, 0x25, 0xc2, 0x0d, 0x7e, 0x04, 0x1d, 0xe3, 0x7a, 0x07, 0x01, 0x79, + 0x7a, 0x07, 0x01, 0x89, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x99, 0x7a, 0x07, 0x01, 0xa9, 0x22, 0xc2, + 0x16, 0xc2, 0x1e, 0xc2, 0x26, 0xc2, 0x0e, 0x7e, 0x04, 0x21, 0xe3, 0x7a, 0x07, 0x01, 0x7b, 0x7a, + 0x07, 0x01, 0x8b, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x9b, 0x7a, 0x07, 0x01, 0xab, 0x22, 0xc2, 0x17, + 0xc2, 0x1f, 0xc2, 0x27, 0xc2, 0x0f, 0x7e, 0x04, 0x25, 0xe3, 0x7a, 0x07, 0x01, 0x7d, 0x7a, 0x07, + 0x01, 0x8d, 0x6d, 0x00, 0x7a, 0x07, 0x01, 0x9d, 0x7a, 0x07, 0x01, 0xad, 0x22, 0x7c, 0xb2, 0x23, + 0x0a, 0x2b, 0x49, 0x22, 0x42, 0xb8, 0x89, 0x24, 0x42, 0xc8, 0x42, 0xe9, 0x43, 0x0a, 0x43, 0x2b, + 0x43, 0x4c, 0x43, 0x6d, 0x43, 0x8e, 0x43, 0xaf, 0x30, 0x50, 0x07, 0x20, 0x68, 0x04, 0xc2, 0x28, + 0x80, 0x16, 0x30, 0x40, 0x07, 0x20, 0x60, 0x04, 0xc2, 0x28, 0x80, 0x0c, 0x30, 0x48, 0x07, 0x20, + 0x58, 0x04, 0xc2, 0x28, 0x80, 0x02, 0xd2, 0x28, 0x22, 0x30, 0x51, 0x07, 0x20, 0x69, 0x04, 0xc2, + 0x29, 0x80, 0x16, 0x30, 0x41, 0x07, 0x20, 0x61, 0x04, 0xc2, 0x29, 0x80, 0x0c, 0x30, 0x49, 0x07, + 0x20, 0x59, 0x04, 0xc2, 0x29, 0x80, 0x02, 0xd2, 0x29, 0x22, 0x30, 0x52, 0x07, 0x20, 0x6a, 0x04, + 0xc2, 0x2a, 0x80, 0x16, 0x30, 0x42, 0x07, 0x20, 0x62, 0x04, 0xc2, 0x2a, 0x80, 0x0c, 0x30, 0x4a, + 0x07, 0x20, 0x5a, 0x04, 0xc2, 0x2a, 0x80, 0x02, 0xd2, 0x2a, 0x22, 0x30, 0x53, 0x07, 0x20, 0x6b, + 0x04, 0xc2, 0x2b, 0x80, 0x16, 0x30, 0x43, 0x07, 0x20, 0x63, 0x04, 0xc2, 0x2b, 0x80, 0x0c, 0x30, + 0x4b, 0x07, 0x20, 0x5b, 0x04, 0xc2, 0x2b, 0x80, 0x02, 0xd2, 0x2b, 0x22, 0x30, 0x54, 0x07, 0x20, + 0x6c, 0x04, 0xc2, 0x2c, 0x80, 0x16, 0x30, 0x44, 0x07, 0x20, 0x64, 0x04, 0xc2, 0x2c, 0x80, 0x0c, + 0x30, 0x4c, 0x07, 0x20, 0x5c, 0x04, 0xc2, 0x2c, 0x80, 0x02, 0xd2, 0x2c, 0x22, 0x30, 0x55, 0x07, + 0x20, 0x6d, 0x04, 0xc2, 0x2d, 0x80, 0x16, 0x30, 0x45, 0x07, 0x20, 0x65, 0x04, 0xc2, 0x2d, 0x80, + 0x0c, 0x30, 0x4d, 0x07, 0x20, 0x5d, 0x04, 0xc2, 0x2d, 0x80, 0x02, 0xd2, 0x2d, 0x22, 0x30, 0x56, + 0x07, 0x20, 0x6e, 0x04, 0xc2, 0x2e, 0x80, 0x16, 0x30, 0x46, 0x07, 0x20, 0x66, 0x04, 0xc2, 0x2e, + 0x80, 0x0c, 0x30, 0x4e, 0x07, 0x20, 0x5e, 0x04, 0xc2, 0x2e, 0x80, 0x02, 0xd2, 0x2e, 0x22, 0x30, + 0x57, 0x07, 0x20, 0x6f, 0x04, 0xc2, 0x2f, 0x80, 0x16, 0x30, 0x47, 0x07, 0x20, 0x67, 0x04, 0xc2, + 0x2f, 0x80, 0x0c, 0x30, 0x4f, 0x07, 0x20, 0x5f, 0x04, 0xc2, 0x2f, 0x80, 0x02, 0xd2, 0x2f, 0x22, + 0x44, 0x38, 0x43, 0xe5, 0xbe, 0xb0, 0x02, 0x40, 0x01, 0x22, 0x23, 0x0a, 0x5b, 0x49, 0x55, 0x43, + 0xd0, 0x99, 0x54, 0xd3, 0x22, 0xa9, 0xc5, 0x87, 0x12, 0x44, 0x43, 0x7e, 0x04, 0x05, 0xe3, 0x7a, + 0x07, 0x01, 0xd7, 0x7a, 0x07, 0x01, 0xd9, 0x7e, 0x04, 0x01, 0xe3, 0x7a, 0x07, 0x01, 0xdd, 0x7a, + 0x07, 0x01, 0xdf, 0x7e, 0x04, 0x74, 0xad, 0x7a, 0x05, 0x61, 0x75, 0xf1, 0x01, 0x75, 0xe1, 0x1f, + 0x75, 0xe4, 0x04, 0x75, 0xf4, 0x04, 0x75, 0xf1, 0x02, 0x75, 0xe1, 0x03, 0x75, 0xe4, 0x04, 0x75, + 0xf4, 0x04, 0x43, 0xa2, 0x1c, 0x12, 0x40, 0xeb, 0x7e, 0x20, 0x00, 0x12, 0x41, 0x9a, 0x0b, 0x20, + 0xbe, 0x21, 0x2f, 0x78, 0xf6, 0xd2, 0xa8, 0x22, 0xa9, 0xd5, 0x87, 0x12, 0x44, 0x43, 0xd2, 0x92, + 0xc2, 0xa8, 0x22, 0x75, 0xa3, 0x00, 0x53, 0xa2, 0x03, 0x75, 0xc1, 0x00, 0x53, 0xc0, 0x03, 0x7e, + 0x00, 0x05, 0x7a, 0x01, 0xf1, 0x43, 0xf4, 0x80, 0x43, 0xe4, 0x80, 0xe5, 0xf2, 0x54, 0x7f, 0x44, + 0x08, 0xf5, 0xf2, 0xe5, 0xe2, 0x54, 0x7f, 0x44, 0x08, 0xf5, 0xe2, 0x75, 0xe1, 0x10, 0xa5, 0xd8, + 0xe1, 0x22, 0xca, 0x09, 0x12, 0x30, 0x20, 0x10, 0x01, 0x12, 0xd5, 0x67, 0x1e, 0x63, 0x69, 0x01, + 0x7e, 0x00, 0x6a, 0x2e, 0x01, 0x69, 0xa5, 0xe6, 0xf5, 0x67, 0x80, 0x12, 0x20, 0x02, 0x1e, 0x75, + 0x69, 0x00, 0x85, 0x6a, 0x67, 0xd2, 0x02, 0x74, 0x00, 0x80, 0x0d, 0x30, 0x02, 0x0f, 0xc2, 0x02, + 0x7e, 0x00, 0x6c, 0x2e, 0x01, 0x69, 0xa5, 0xe6, 0x53, 0x90, 0xcf, 0x42, 0x90, 0xda, 0x09, 0x32, + 0xc0, 0xd0, 0xc0, 0xd1, 0xc0, 0xe0, 0xc0, 0xf0, 0xca, 0x0b, 0xca, 0x1b, 0xca, 0x2b, 0xd2, 0x01, + 0x75, 0x31, 0x89, 0x12, 0x7c, 0x15, 0x7e, 0x14, 0x80, 0x00, 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, + 0x03, 0x02, 0x45, 0x4f, 0x20, 0x78, 0x5a, 0xa5, 0x0a, 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, + 0x02, 0x45, 0x67, 0x20, 0x79, 0x4b, 0xa5, 0x0a, 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, 0x02, + 0x45, 0x7f, 0xa5, 0x0a, 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, 0x02, 0x45, 0x97, 0x20, 0x7a, + 0x30, 0xa5, 0x0a, 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, 0x02, 0x45, 0xaf, 0xa5, 0x0a, 0x09, + 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, 0x02, 0x45, 0xc7, 0xa5, 0x0a, 0x09, 0xb1, 0x00, 0x08, 0x20, + 0xe0, 0x03, 0x02, 0x45, 0xdf, 0xa5, 0x0a, 0x09, 0xb1, 0x00, 0x08, 0x20, 0xe0, 0x03, 0x02, 0x45, + 0xf7, 0x30, 0x04, 0x0c, 0xc2, 0x04, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, + 0xda, 0x2b, 0xda, 0x1b, 0xda, 0x0b, 0xd0, 0xf0, 0xd0, 0xe0, 0xd0, 0xd1, 0xd0, 0xd0, 0x32, 0x75, + 0x31, 0x80, 0x12, 0x7c, 0x15, 0x54, 0x3e, 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x63, + 0x2b, 0xca, 0x06, 0xc6, 0x44, 0x89, 0x54, 0x75, 0x31, 0x81, 0x12, 0x7c, 0x15, 0x54, 0x3e, 0x0a, + 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x63, 0x6b, 0xca, 0x06, 0xc6, 0x44, 0x89, 0x54, 0x75, + 0x31, 0x82, 0x12, 0x7c, 0x15, 0x54, 0x3e, 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x63, + 0xab, 0xca, 0x06, 0xc6, 0x44, 0x89, 0x54, 0x75, 0x31, 0x83, 0x12, 0x7c, 0x15, 0x54, 0x3e, 0x0a, + 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x63, 0xeb, 0xca, 0x06, 0xc6, 0x44, 0x89, 0x54, 0x75, + 0x31, 0x84, 0x12, 0x7c, 0x15, 0x54, 0x3e, 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x64, + 0x2b, 0xca, 0x06, 0xc6, 0x44, 0x89, 0x54, 0x75, 0x31, 0x85, 0x12, 0x7c, 0x15, 0x54, 0x3e, 0x0a, + 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x64, 0x6b, 0xca, 0x06, 0xc6, 0x44, 0x89, 0x54, 0x75, + 0x31, 0x86, 0x12, 0x7c, 0x15, 0x54, 0x3e, 0x0a, 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x64, + 0xab, 0xca, 0x06, 0xc6, 0x44, 0x89, 0x54, 0x75, 0x31, 0x87, 0x12, 0x7c, 0x15, 0x54, 0x3e, 0x0a, + 0x5b, 0x7e, 0x44, 0x00, 0xff, 0x69, 0x52, 0x64, 0xeb, 0xca, 0x06, 0xc6, 0x44, 0x89, 0x54, 0x10, + 0x08, 0x01, 0x22, 0x20, 0x28, 0x03, 0xd2, 0x08, 0x22, 0x75, 0x31, 0xa0, 0x12, 0x7c, 0x15, 0x7e, + 0x14, 0x80, 0x00, 0x80, 0x06, 0x20, 0x28, 0x03, 0xd2, 0x08, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, + 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, 0x12, 0x49, 0x30, 0x20, 0xe6, 0x03, 0xd2, 0x08, + 0x22, 0x30, 0x30, 0x49, 0xd2, 0x70, 0x7e, 0x37, 0x01, 0x8f, 0x7e, 0x27, 0x01, 0xaf, 0x9d, 0x32, + 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x48, 0x7a, 0x05, 0x48, 0x7a, 0x37, 0x01, 0x8f, 0x7e, 0x37, + 0x01, 0x6f, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x0d, 0xe2, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x6f, + 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x12, 0x65, 0x2b, 0x10, + 0x70, 0xc4, 0x22, 0xc2, 0x70, 0x2d, 0x23, 0x68, 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, + 0x8f, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, 0x27, 0x01, 0xaf, 0x28, 0x04, 0x7e, 0x27, 0x01, + 0xaf, 0x7e, 0x37, 0x01, 0x8f, 0x9d, 0x32, 0x7d, 0x02, 0x2e, 0x05, 0x48, 0x7a, 0x05, 0x48, 0x7a, + 0x37, 0x01, 0x8f, 0x7e, 0x37, 0x01, 0x6f, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x0d, 0xe2, 0x38, + 0x13, 0x7a, 0x47, 0x01, 0x6f, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, + 0x15, 0x02, 0x65, 0x2b, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, + 0x9e, 0x44, 0x0d, 0xe3, 0x9d, 0x24, 0x12, 0x65, 0x2b, 0x7e, 0x34, 0x09, 0xe3, 0x7d, 0x24, 0x2d, + 0x43, 0x7a, 0x47, 0x01, 0x6f, 0x12, 0x65, 0x2b, 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x46, 0x7f, + 0x22, 0xd2, 0x08, 0x7e, 0x04, 0x09, 0xe3, 0x7a, 0x07, 0x01, 0x6f, 0x7a, 0x07, 0x01, 0x7f, 0x75, + 0x31, 0x94, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x00, 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x92, 0x12, + 0x7c, 0x15, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, + 0x7e, 0x37, 0x01, 0xe1, 0x7e, 0x27, 0x01, 0xbf, 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, + 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, 0x46, 0x7a, 0x05, 0x46, 0x7a, 0x37, 0x01, 0xe1, + 0x7e, 0x37, 0x01, 0xdf, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x05, 0xe2, 0x38, 0x44, 0x7a, 0x47, + 0x01, 0xdf, 0x7e, 0x24, 0x00, 0x00, 0x2e, 0x27, 0x01, 0xbf, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, + 0x51, 0x31, 0x12, 0x7c, 0x15, 0xbe, 0x50, 0x38, 0x78, 0x03, 0x02, 0x66, 0x16, 0x02, 0x65, 0xfb, + 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, + 0x30, 0x38, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x10, 0x22, + 0x80, 0x7d, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x9e, 0x44, 0x05, 0xe3, 0x9d, 0x24, 0x7e, 0x64, + 0x00, 0x00, 0x2e, 0x67, 0x01, 0xbf, 0x9e, 0x24, 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, + 0x35, 0x12, 0x65, 0xfb, 0x7e, 0x34, 0x01, 0xe3, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xdf, + 0x02, 0x65, 0xfb, 0x7a, 0x39, 0xc0, 0x7e, 0x34, 0x01, 0xe3, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, + 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, 0x0f, 0x23, 0x23, 0x23, 0x44, 0x00, 0x7a, 0x69, + 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x31, 0x93, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, + 0x7c, 0x15, 0xbd, 0x04, 0x68, 0x29, 0x7a, 0x07, 0x01, 0xdf, 0x7e, 0x47, 0x01, 0xe1, 0x2d, 0x43, + 0x7a, 0x47, 0x01, 0xe1, 0x2e, 0x35, 0x46, 0x7a, 0x35, 0x46, 0x22, 0x09, 0xb1, 0x00, 0x14, 0x20, + 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2a, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2c, 0x7e, + 0x04, 0x01, 0xe3, 0x80, 0xd1, 0xd2, 0x04, 0x7e, 0x07, 0x01, 0xe1, 0x7e, 0x24, 0x03, 0xfe, 0x9d, + 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xdf, 0x7e, 0x44, 0x05, 0xe3, 0x7d, 0x60, 0x0b, 0x04, 0xbd, + 0x04, 0x68, 0xd0, 0x7d, 0x70, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xce, 0x7d, 0x54, 0x9d, 0x50, 0xbd, + 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, + 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, + 0x02, 0x47, 0xe3, 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, + 0xb1, 0x00, 0x04, 0x30, 0x38, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, + 0xd2, 0x10, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x47, + 0xe3, 0xda, 0xb8, 0x02, 0x49, 0x30, 0x09, 0xb1, 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x31, 0x90, + 0x12, 0x7c, 0x15, 0xf5, 0x31, 0x12, 0x7c, 0x15, 0xa5, 0xfc, 0x5e, 0xb0, 0xf0, 0xa5, 0xfd, 0x09, + 0xb1, 0x00, 0x18, 0x4c, 0x4b, 0x5e, 0xb0, 0xf0, 0xbc, 0xb5, 0x78, 0xf1, 0x5e, 0x40, 0x0f, 0x4c, + 0x54, 0x7c, 0xb5, 0x5e, 0x50, 0x0b, 0x68, 0x2a, 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, + 0x68, 0x80, 0x02, 0xc2, 0x68, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x60, 0x80, 0x02, + 0xc2, 0x60, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x58, 0x80, 0x02, 0xc2, 0x58, 0x12, + 0x42, 0xc8, 0x02, 0x62, 0x93, 0x75, 0x31, 0x91, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x14, 0x7a, + 0xb1, 0x31, 0x12, 0x7c, 0x15, 0x20, 0xe0, 0x08, 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x62, 0x93, + 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, 0x12, 0x62, 0x93, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, + 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, 0x61, 0x00, 0x00, 0x12, 0x62, 0xb6, 0x09, 0xb1, + 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x48, 0x35, 0x75, 0x31, 0x95, 0x12, 0x7c, 0x15, 0x22, + 0x75, 0x31, 0x96, 0x12, 0x7c, 0x15, 0x22, 0x10, 0x09, 0x01, 0x22, 0x20, 0x29, 0x03, 0xd2, 0x09, + 0x22, 0x75, 0x31, 0xa1, 0x12, 0x7c, 0x15, 0x7e, 0x14, 0x81, 0x00, 0x80, 0x06, 0x20, 0x29, 0x03, + 0xd2, 0x09, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, + 0x12, 0x4c, 0x88, 0x20, 0xe6, 0x03, 0xd2, 0x09, 0x22, 0x30, 0x31, 0x49, 0xd2, 0x71, 0x7e, 0x37, + 0x01, 0x91, 0x7e, 0x27, 0x01, 0xb1, 0x9d, 0x32, 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x4a, 0x7a, + 0x05, 0x4a, 0x7a, 0x37, 0x01, 0x91, 0x7e, 0x37, 0x01, 0x71, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, + 0x11, 0xe2, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x71, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, + 0x31, 0x12, 0x7c, 0x15, 0x12, 0x65, 0x2b, 0x10, 0x71, 0xc4, 0x22, 0xc2, 0x71, 0x2d, 0x23, 0x68, + 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, 0x91, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, + 0x27, 0x01, 0xb1, 0x28, 0x04, 0x7e, 0x27, 0x01, 0xb1, 0x7e, 0x37, 0x01, 0x91, 0x9d, 0x32, 0x7d, + 0x02, 0x2e, 0x05, 0x4a, 0x7a, 0x05, 0x4a, 0x7a, 0x37, 0x01, 0x91, 0x7e, 0x37, 0x01, 0x71, 0x7d, + 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x11, 0xe2, 0x38, 0x13, 0x7a, 0x47, 0x01, 0x71, 0x75, 0x31, 0x94, + 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x02, 0x65, 0x2b, 0x75, 0x31, 0x94, 0x12, + 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x9e, 0x44, 0x11, 0xe3, 0x9d, 0x24, 0x12, 0x65, + 0x2b, 0x7e, 0x34, 0x0d, 0xe3, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0x71, 0x12, 0x65, 0x2b, + 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x49, 0xd7, 0x22, 0xd2, 0x09, 0x7e, 0x04, 0x0d, 0xe3, 0x7a, + 0x07, 0x01, 0x71, 0x7a, 0x07, 0x01, 0x81, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x00, + 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x92, 0x12, 0x7c, 0x15, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, + 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, 0x7e, 0x37, 0x01, 0xe1, 0x7e, 0x27, 0x01, 0xc1, + 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, + 0x46, 0x7a, 0x05, 0x46, 0x7a, 0x37, 0x01, 0xe1, 0x7e, 0x37, 0x01, 0xdf, 0x7d, 0x43, 0x2d, 0x42, + 0xbe, 0x44, 0x05, 0xe2, 0x38, 0x44, 0x7a, 0x47, 0x01, 0xdf, 0x7e, 0x24, 0x01, 0x00, 0x2e, 0x27, + 0x01, 0xc1, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0xbe, 0x50, 0x38, + 0x78, 0x03, 0x02, 0x66, 0x16, 0x02, 0x65, 0xfb, 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, + 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x39, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, + 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x11, 0x22, 0x80, 0x7d, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, + 0x9e, 0x44, 0x05, 0xe3, 0x9d, 0x24, 0x7e, 0x64, 0x01, 0x00, 0x2e, 0x67, 0x01, 0xc1, 0x9e, 0x24, + 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, 0x35, 0x12, 0x65, 0xfb, 0x7e, 0x34, 0x01, 0xe3, + 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xdf, 0x02, 0x65, 0xfb, 0x7a, 0x39, 0xc0, 0x7e, 0x34, + 0x01, 0xe3, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, + 0x0f, 0x23, 0x23, 0x23, 0x44, 0x01, 0x7a, 0x69, 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x31, + 0x93, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, 0x7c, 0x15, 0xbd, 0x04, 0x68, 0x29, 0x7a, 0x07, + 0x01, 0xdf, 0x7e, 0x47, 0x01, 0xe1, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xe1, 0x2e, 0x35, 0x46, 0x7a, + 0x35, 0x46, 0x22, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xe3, 0x80, + 0x2a, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2c, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0xd1, 0xd2, 0x04, 0x7e, + 0x07, 0x01, 0xe1, 0x7e, 0x24, 0x03, 0xfe, 0x9d, 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xdf, 0x7e, + 0x44, 0x05, 0xe3, 0x7d, 0x60, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd0, 0x7d, 0x70, 0x0b, 0x04, 0xbd, + 0x04, 0x68, 0xce, 0x7d, 0x54, 0x9d, 0x50, 0xbd, 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, + 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, + 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, 0x02, 0x4b, 0x3b, 0x75, 0x31, 0x99, 0x12, 0x7c, + 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x39, 0x0a, 0x09, 0xb1, + 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x11, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, + 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x4b, 0x3b, 0xda, 0xb8, 0x02, 0x4c, 0x88, 0x09, 0xb1, + 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x31, 0x90, 0x12, 0x7c, 0x15, 0xf5, 0x31, 0x12, 0x7c, 0x15, + 0xa5, 0xfc, 0x5e, 0xb0, 0xf0, 0xa5, 0xfd, 0x09, 0xb1, 0x00, 0x18, 0x4c, 0x4b, 0x5e, 0xb0, 0xf0, + 0xbc, 0xb5, 0x78, 0xf1, 0x5e, 0x40, 0x0f, 0x4c, 0x54, 0x7c, 0xb5, 0x5e, 0x50, 0x0b, 0x68, 0x2a, + 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, 0x69, 0x80, 0x02, 0xc2, 0x69, 0xa5, 0xfd, 0x5e, + 0x50, 0x20, 0x68, 0x04, 0xd2, 0x61, 0x80, 0x02, 0xc2, 0x61, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, + 0x04, 0xd2, 0x59, 0x80, 0x02, 0xc2, 0x59, 0x12, 0x42, 0xe9, 0x02, 0x62, 0x93, 0x75, 0x31, 0x91, + 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x14, 0x7a, 0xb1, 0x31, 0x12, 0x7c, 0x15, 0x20, 0xe0, 0x08, + 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x62, 0x93, 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, + 0x12, 0x62, 0x93, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, + 0x61, 0x00, 0x00, 0x12, 0x62, 0xb6, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x4b, + 0x8d, 0x75, 0x31, 0x95, 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x96, 0x12, 0x7c, 0x15, 0x22, 0x10, + 0x0a, 0x01, 0x22, 0x20, 0x2a, 0x03, 0xd2, 0x0a, 0x22, 0x75, 0x31, 0xa2, 0x12, 0x7c, 0x15, 0x7e, + 0x14, 0x82, 0x00, 0x80, 0x06, 0x20, 0x2a, 0x03, 0xd2, 0x0a, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, + 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, 0x12, 0x4f, 0xe0, 0x20, 0xe6, 0x03, 0xd2, 0x0a, + 0x22, 0x30, 0x32, 0x49, 0xd2, 0x72, 0x7e, 0x37, 0x01, 0x93, 0x7e, 0x27, 0x01, 0xb3, 0x9d, 0x32, + 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x4c, 0x7a, 0x05, 0x4c, 0x7a, 0x37, 0x01, 0x93, 0x7e, 0x37, + 0x01, 0x73, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x15, 0xe2, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x73, + 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x12, 0x65, 0x2b, 0x10, + 0x72, 0xc4, 0x22, 0xc2, 0x72, 0x2d, 0x23, 0x68, 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, + 0x93, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, 0x27, 0x01, 0xb3, 0x28, 0x04, 0x7e, 0x27, 0x01, + 0xb3, 0x7e, 0x37, 0x01, 0x93, 0x9d, 0x32, 0x7d, 0x02, 0x2e, 0x05, 0x4c, 0x7a, 0x05, 0x4c, 0x7a, + 0x37, 0x01, 0x93, 0x7e, 0x37, 0x01, 0x73, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x15, 0xe2, 0x38, + 0x13, 0x7a, 0x47, 0x01, 0x73, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, + 0x15, 0x02, 0x65, 0x2b, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, + 0x9e, 0x44, 0x15, 0xe3, 0x9d, 0x24, 0x12, 0x65, 0x2b, 0x7e, 0x34, 0x11, 0xe3, 0x7d, 0x24, 0x2d, + 0x43, 0x7a, 0x47, 0x01, 0x73, 0x12, 0x65, 0x2b, 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x4d, 0x2f, + 0x22, 0xd2, 0x0a, 0x7e, 0x04, 0x11, 0xe3, 0x7a, 0x07, 0x01, 0x73, 0x7a, 0x07, 0x01, 0x83, 0x75, + 0x31, 0x94, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x00, 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x92, 0x12, + 0x7c, 0x15, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, + 0x7e, 0x37, 0x01, 0xe1, 0x7e, 0x27, 0x01, 0xc3, 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, + 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, 0x46, 0x7a, 0x05, 0x46, 0x7a, 0x37, 0x01, 0xe1, + 0x7e, 0x37, 0x01, 0xdf, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x05, 0xe2, 0x38, 0x44, 0x7a, 0x47, + 0x01, 0xdf, 0x7e, 0x24, 0x02, 0x00, 0x2e, 0x27, 0x01, 0xc3, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, + 0x51, 0x31, 0x12, 0x7c, 0x15, 0xbe, 0x50, 0x38, 0x78, 0x03, 0x02, 0x66, 0x16, 0x02, 0x65, 0xfb, + 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, + 0x30, 0x3a, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x12, 0x22, + 0x80, 0x7d, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x9e, 0x44, 0x05, 0xe3, 0x9d, 0x24, 0x7e, 0x64, + 0x02, 0x00, 0x2e, 0x67, 0x01, 0xc3, 0x9e, 0x24, 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, + 0x35, 0x12, 0x65, 0xfb, 0x7e, 0x34, 0x01, 0xe3, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xdf, + 0x02, 0x65, 0xfb, 0x7a, 0x39, 0xc0, 0x7e, 0x34, 0x01, 0xe3, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, + 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, 0x0f, 0x23, 0x23, 0x23, 0x44, 0x02, 0x7a, 0x69, + 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x31, 0x93, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, + 0x7c, 0x15, 0xbd, 0x04, 0x68, 0x29, 0x7a, 0x07, 0x01, 0xdf, 0x7e, 0x47, 0x01, 0xe1, 0x2d, 0x43, + 0x7a, 0x47, 0x01, 0xe1, 0x2e, 0x35, 0x46, 0x7a, 0x35, 0x46, 0x22, 0x09, 0xb1, 0x00, 0x14, 0x20, + 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2a, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2c, 0x7e, + 0x04, 0x01, 0xe3, 0x80, 0xd1, 0xd2, 0x04, 0x7e, 0x07, 0x01, 0xe1, 0x7e, 0x24, 0x03, 0xfe, 0x9d, + 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xdf, 0x7e, 0x44, 0x05, 0xe3, 0x7d, 0x60, 0x0b, 0x04, 0xbd, + 0x04, 0x68, 0xd0, 0x7d, 0x70, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xce, 0x7d, 0x54, 0x9d, 0x50, 0xbd, + 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, + 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, + 0x02, 0x4e, 0x93, 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, + 0xb1, 0x00, 0x04, 0x30, 0x3a, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, + 0xd2, 0x12, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x4e, + 0x93, 0xda, 0xb8, 0x02, 0x4f, 0xe0, 0x09, 0xb1, 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x31, 0x90, + 0x12, 0x7c, 0x15, 0xf5, 0x31, 0x12, 0x7c, 0x15, 0xa5, 0xfc, 0x5e, 0xb0, 0xf0, 0xa5, 0xfd, 0x09, + 0xb1, 0x00, 0x18, 0x4c, 0x4b, 0x5e, 0xb0, 0xf0, 0xbc, 0xb5, 0x78, 0xf1, 0x5e, 0x40, 0x0f, 0x4c, + 0x54, 0x7c, 0xb5, 0x5e, 0x50, 0x0b, 0x68, 0x2a, 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, + 0x6a, 0x80, 0x02, 0xc2, 0x6a, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x62, 0x80, 0x02, + 0xc2, 0x62, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x5a, 0x80, 0x02, 0xc2, 0x5a, 0x12, + 0x43, 0x0a, 0x02, 0x62, 0x93, 0x75, 0x31, 0x91, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x14, 0x7a, + 0xb1, 0x31, 0x12, 0x7c, 0x15, 0x20, 0xe0, 0x08, 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x62, 0x93, + 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, 0x12, 0x62, 0x93, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, + 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, 0x61, 0x00, 0x00, 0x12, 0x62, 0xb6, 0x09, 0xb1, + 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x4e, 0xe5, 0x75, 0x31, 0x95, 0x12, 0x7c, 0x15, 0x22, + 0x75, 0x31, 0x96, 0x12, 0x7c, 0x15, 0x22, 0x10, 0x0b, 0x01, 0x22, 0x20, 0x2b, 0x03, 0xd2, 0x0b, + 0x22, 0x75, 0x31, 0xa3, 0x12, 0x7c, 0x15, 0x7e, 0x14, 0x83, 0x00, 0x80, 0x06, 0x20, 0x2b, 0x03, 0xd2, 0x0b, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, - 0x12, 0x57, 0x35, 0x30, 0x33, 0x06, 0x20, 0xe6, 0x4f, 0xd2, 0x0b, 0x22, 0x30, 0xe6, 0x02, 0xd2, - 0x63, 0x7e, 0x37, 0x01, 0x7f, 0x7e, 0x27, 0x01, 0x9f, 0x9d, 0x32, 0x40, 0x31, 0x7d, 0x02, 0x2e, - 0x05, 0x38, 0x7a, 0x05, 0x38, 0x7a, 0x37, 0x01, 0x7f, 0x7e, 0x37, 0x01, 0x5f, 0x7d, 0x43, 0x2d, - 0x42, 0xbe, 0x44, 0x19, 0xcc, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x5f, 0x75, 0x2f, 0x94, 0x12, 0x7e, - 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x12, 0x68, 0x34, 0x10, 0x63, 0xc4, 0x22, 0xc2, 0x63, - 0x2d, 0x23, 0x68, 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, 0x7f, 0xbe, 0x24, 0x00, 0x00, - 0x68, 0x6a, 0xbe, 0x27, 0x01, 0x9f, 0x28, 0x04, 0x7e, 0x27, 0x01, 0x9f, 0x7e, 0x37, 0x01, 0x7f, - 0x9d, 0x32, 0x7d, 0x02, 0x2e, 0x05, 0x38, 0x7a, 0x05, 0x38, 0x7a, 0x37, 0x01, 0x7f, 0x7e, 0x37, - 0x01, 0x5f, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x19, 0xcc, 0x38, 0x13, 0x7a, 0x47, 0x01, 0x5f, - 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x02, 0x68, 0x34, 0x75, - 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x19, 0xcd, 0x9d, - 0x24, 0x12, 0x68, 0x34, 0x7e, 0x34, 0x15, 0xcd, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0x5f, - 0x12, 0x68, 0x34, 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x54, 0xaa, 0x22, 0xd2, 0x0b, 0x7e, 0x04, - 0x15, 0xcd, 0x7a, 0x07, 0x01, 0x5f, 0x7a, 0x07, 0x01, 0x6f, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, - 0x75, 0x2f, 0x00, 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x92, 0x12, 0x7e, 0x30, 0xd2, 0x04, 0x09, - 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, 0x7e, 0x37, 0x01, 0xcb, 0x7e, - 0x27, 0x01, 0xaf, 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, 0x04, 0x00, 0x38, 0x3c, 0x7d, - 0x02, 0x2e, 0x05, 0x30, 0x7a, 0x05, 0x30, 0x7a, 0x37, 0x01, 0xcb, 0x7e, 0x37, 0x01, 0xc9, 0x7d, - 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x05, 0xcc, 0x38, 0x44, 0x7a, 0x47, 0x01, 0xc9, 0x7e, 0x24, 0x03, - 0x00, 0x2e, 0x27, 0x01, 0xaf, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, - 0xbe, 0x50, 0x38, 0x78, 0x03, 0x02, 0x69, 0x1f, 0x02, 0x69, 0x04, 0x75, 0x2f, 0x99, 0x12, 0x7e, - 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3b, 0x0a, 0x09, 0xb1, - 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x13, 0x22, 0x80, 0x7f, 0x7a, 0x51, 0x2f, - 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x05, 0xcd, 0x9d, 0x24, 0x7e, 0x64, 0x03, 0x00, 0x2e, 0x67, 0x01, - 0xaf, 0x9e, 0x24, 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, 0x35, 0x12, 0x69, 0x04, 0x7e, - 0x34, 0x01, 0xcd, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xc9, 0x02, 0x69, 0x04, 0x7a, 0x39, - 0xc0, 0x7e, 0x34, 0x01, 0xcd, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, 0x44, 0x80, 0xe5, 0x9d, 0x32, - 0x7c, 0xb6, 0x54, 0x0f, 0x23, 0x23, 0x23, 0x44, 0x03, 0x7a, 0x69, 0xb0, 0x7a, 0x79, 0x70, 0x0b, - 0x35, 0x75, 0x2f, 0x93, 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, 0x7e, 0x30, 0xbd, 0x04, 0x68, - 0x2b, 0x7a, 0x07, 0x01, 0xc9, 0x7e, 0x47, 0x01, 0xcb, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xcb, 0x2e, - 0x35, 0x30, 0x7a, 0x35, 0x30, 0x22, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0x13, 0x22, - 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x28, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x2a, 0x7e, 0x04, 0x01, 0xcd, - 0x80, 0xcf, 0x7e, 0x07, 0x01, 0xcb, 0x7e, 0x24, 0x03, 0xfe, 0x9d, 0x20, 0x28, 0x40, 0x7e, 0x07, - 0x01, 0xc9, 0x7e, 0x44, 0x05, 0xcd, 0x7d, 0x60, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd2, 0x7d, 0x70, - 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd0, 0x7d, 0x54, 0x9d, 0x50, 0xbd, 0x25, 0x40, 0x02, 0x7d, 0x25, - 0x7d, 0x32, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, 0x01, 0x31, 0xda, 0xb8, 0x7e, - 0x19, 0xb0, 0x7a, 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, 0x02, 0x56, 0x0e, 0x75, 0x2f, - 0x99, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3b, - 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x13, 0x22, 0xda, 0xb8, - 0x30, 0xe0, 0xd8, 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x56, 0x0e, 0xda, 0xb8, 0x02, 0x57, - 0x35, 0x09, 0xb1, 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x2f, 0x90, 0x12, 0x7e, 0x30, 0xf5, 0x2f, - 0x12, 0x7e, 0x30, 0xa5, 0xfd, 0x5e, 0x50, 0x0a, 0x68, 0x1d, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, - 0x04, 0xd2, 0x5b, 0x80, 0x02, 0xc2, 0x5b, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x53, - 0x80, 0x02, 0xc2, 0x53, 0x12, 0x42, 0xf1, 0x02, 0x65, 0x9c, 0x75, 0x2f, 0x91, 0x12, 0x7e, 0x30, - 0x09, 0xb1, 0x00, 0x14, 0x7a, 0xb1, 0x2f, 0x12, 0x7e, 0x30, 0x20, 0xe0, 0x08, 0xd2, 0x04, 0x7e, - 0xa0, 0x80, 0x02, 0x65, 0x9c, 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, 0x12, 0x65, 0x9c, - 0xca, 0xb8, 0x5e, 0xb0, 0x1c, 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, 0x61, 0x00, 0x00, - 0x12, 0x65, 0xbf, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x56, 0x62, 0x75, 0x2f, - 0x95, 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x96, 0x12, 0x7e, 0x30, 0x22, 0x10, 0x0c, 0x01, 0x22, - 0x20, 0x2c, 0x03, 0xd2, 0x0c, 0x22, 0x75, 0x2f, 0xa4, 0x12, 0x7e, 0x30, 0x7e, 0x14, 0x84, 0x00, - 0x80, 0x06, 0x20, 0x2c, 0x03, 0xd2, 0x0c, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, - 0x1e, 0xda, 0xb8, 0x68, 0x03, 0x12, 0x5a, 0x6a, 0x30, 0x34, 0x06, 0x20, 0xe6, 0x4f, 0xd2, 0x0c, - 0x22, 0x30, 0xe6, 0x02, 0xd2, 0x64, 0x7e, 0x37, 0x01, 0x81, 0x7e, 0x27, 0x01, 0xa1, 0x9d, 0x32, - 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x3a, 0x7a, 0x05, 0x3a, 0x7a, 0x37, 0x01, 0x81, 0x7e, 0x37, - 0x01, 0x61, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x1d, 0xcc, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x61, - 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x12, 0x68, 0x34, 0x10, - 0x64, 0xc4, 0x22, 0xc2, 0x64, 0x2d, 0x23, 0x68, 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, - 0x81, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, 0x27, 0x01, 0xa1, 0x28, 0x04, 0x7e, 0x27, 0x01, - 0xa1, 0x7e, 0x37, 0x01, 0x81, 0x9d, 0x32, 0x7d, 0x02, 0x2e, 0x05, 0x3a, 0x7a, 0x05, 0x3a, 0x7a, - 0x37, 0x01, 0x81, 0x7e, 0x37, 0x01, 0x61, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x1d, 0xcc, 0x38, - 0x13, 0x7a, 0x47, 0x01, 0x61, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, - 0x30, 0x02, 0x68, 0x34, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, - 0x9e, 0x44, 0x1d, 0xcd, 0x9d, 0x24, 0x12, 0x68, 0x34, 0x7e, 0x34, 0x19, 0xcd, 0x7d, 0x24, 0x2d, - 0x43, 0x7a, 0x47, 0x01, 0x61, 0x12, 0x68, 0x34, 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x57, 0xdf, - 0x22, 0xd2, 0x0c, 0x7e, 0x04, 0x19, 0xcd, 0x7a, 0x07, 0x01, 0x61, 0x7a, 0x07, 0x01, 0x71, 0x75, - 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x75, 0x2f, 0x00, 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x92, 0x12, - 0x7e, 0x30, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, - 0x7e, 0x37, 0x01, 0xcb, 0x7e, 0x27, 0x01, 0xb1, 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, - 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, 0x30, 0x7a, 0x05, 0x30, 0x7a, 0x37, 0x01, 0xcb, - 0x7e, 0x37, 0x01, 0xc9, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x05, 0xcc, 0x38, 0x44, 0x7a, 0x47, - 0x01, 0xc9, 0x7e, 0x24, 0x04, 0x00, 0x2e, 0x27, 0x01, 0xb1, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, - 0x51, 0x2f, 0x12, 0x7e, 0x30, 0xbe, 0x50, 0x38, 0x78, 0x03, 0x02, 0x69, 0x1f, 0x02, 0x69, 0x04, - 0x75, 0x2f, 0x99, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, + 0x12, 0x53, 0x38, 0x20, 0xe6, 0x03, 0xd2, 0x0b, 0x22, 0x30, 0x33, 0x49, 0xd2, 0x73, 0x7e, 0x37, + 0x01, 0x95, 0x7e, 0x27, 0x01, 0xb5, 0x9d, 0x32, 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x4e, 0x7a, + 0x05, 0x4e, 0x7a, 0x37, 0x01, 0x95, 0x7e, 0x37, 0x01, 0x75, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, + 0x19, 0xe2, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x75, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, + 0x31, 0x12, 0x7c, 0x15, 0x12, 0x65, 0x2b, 0x10, 0x73, 0xc4, 0x22, 0xc2, 0x73, 0x2d, 0x23, 0x68, + 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, 0x95, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, + 0x27, 0x01, 0xb5, 0x28, 0x04, 0x7e, 0x27, 0x01, 0xb5, 0x7e, 0x37, 0x01, 0x95, 0x9d, 0x32, 0x7d, + 0x02, 0x2e, 0x05, 0x4e, 0x7a, 0x05, 0x4e, 0x7a, 0x37, 0x01, 0x95, 0x7e, 0x37, 0x01, 0x75, 0x7d, + 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x19, 0xe2, 0x38, 0x13, 0x7a, 0x47, 0x01, 0x75, 0x75, 0x31, 0x94, + 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x02, 0x65, 0x2b, 0x75, 0x31, 0x94, 0x12, + 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x9e, 0x44, 0x19, 0xe3, 0x9d, 0x24, 0x12, 0x65, + 0x2b, 0x7e, 0x34, 0x15, 0xe3, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0x75, 0x12, 0x65, 0x2b, + 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x50, 0x87, 0x22, 0xd2, 0x0b, 0x7e, 0x04, 0x15, 0xe3, 0x7a, + 0x07, 0x01, 0x75, 0x7a, 0x07, 0x01, 0x85, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x00, + 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x92, 0x12, 0x7c, 0x15, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, + 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, 0x7e, 0x37, 0x01, 0xe1, 0x7e, 0x27, 0x01, 0xc5, + 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, + 0x46, 0x7a, 0x05, 0x46, 0x7a, 0x37, 0x01, 0xe1, 0x7e, 0x37, 0x01, 0xdf, 0x7d, 0x43, 0x2d, 0x42, + 0xbe, 0x44, 0x05, 0xe2, 0x38, 0x44, 0x7a, 0x47, 0x01, 0xdf, 0x7e, 0x24, 0x03, 0x00, 0x2e, 0x27, + 0x01, 0xc5, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0xbe, 0x50, 0x38, + 0x78, 0x03, 0x02, 0x66, 0x16, 0x02, 0x65, 0xfb, 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, + 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3b, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, + 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x13, 0x22, 0x80, 0x7d, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, + 0x9e, 0x44, 0x05, 0xe3, 0x9d, 0x24, 0x7e, 0x64, 0x03, 0x00, 0x2e, 0x67, 0x01, 0xc5, 0x9e, 0x24, + 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, 0x35, 0x12, 0x65, 0xfb, 0x7e, 0x34, 0x01, 0xe3, + 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xdf, 0x02, 0x65, 0xfb, 0x7a, 0x39, 0xc0, 0x7e, 0x34, + 0x01, 0xe3, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, + 0x0f, 0x23, 0x23, 0x23, 0x44, 0x03, 0x7a, 0x69, 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x31, + 0x93, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, 0x7c, 0x15, 0xbd, 0x04, 0x68, 0x29, 0x7a, 0x07, + 0x01, 0xdf, 0x7e, 0x47, 0x01, 0xe1, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xe1, 0x2e, 0x35, 0x46, 0x7a, + 0x35, 0x46, 0x22, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xe3, 0x80, + 0x2a, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2c, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0xd1, 0xd2, 0x04, 0x7e, + 0x07, 0x01, 0xe1, 0x7e, 0x24, 0x03, 0xfe, 0x9d, 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xdf, 0x7e, + 0x44, 0x05, 0xe3, 0x7d, 0x60, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd0, 0x7d, 0x70, 0x0b, 0x04, 0xbd, + 0x04, 0x68, 0xce, 0x7d, 0x54, 0x9d, 0x50, 0xbd, 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, + 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, + 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, 0x02, 0x51, 0xeb, 0x75, 0x31, 0x99, 0x12, 0x7c, + 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3b, 0x0a, 0x09, 0xb1, + 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x13, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, + 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x51, 0xeb, 0xda, 0xb8, 0x02, 0x53, 0x38, 0x09, 0xb1, + 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x31, 0x90, 0x12, 0x7c, 0x15, 0xf5, 0x31, 0x12, 0x7c, 0x15, + 0xa5, 0xfc, 0x5e, 0xb0, 0xf0, 0xa5, 0xfd, 0x09, 0xb1, 0x00, 0x18, 0x4c, 0x4b, 0x5e, 0xb0, 0xf0, + 0xbc, 0xb5, 0x78, 0xf1, 0x5e, 0x40, 0x0f, 0x4c, 0x54, 0x7c, 0xb5, 0x5e, 0x50, 0x0b, 0x68, 0x2a, + 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, 0x6b, 0x80, 0x02, 0xc2, 0x6b, 0xa5, 0xfd, 0x5e, + 0x50, 0x20, 0x68, 0x04, 0xd2, 0x63, 0x80, 0x02, 0xc2, 0x63, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, + 0x04, 0xd2, 0x5b, 0x80, 0x02, 0xc2, 0x5b, 0x12, 0x43, 0x2b, 0x02, 0x62, 0x93, 0x75, 0x31, 0x91, + 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x14, 0x7a, 0xb1, 0x31, 0x12, 0x7c, 0x15, 0x20, 0xe0, 0x08, + 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x62, 0x93, 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, + 0x12, 0x62, 0x93, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, + 0x61, 0x00, 0x00, 0x12, 0x62, 0xb6, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x52, + 0x3d, 0x75, 0x31, 0x95, 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x96, 0x12, 0x7c, 0x15, 0x22, 0x10, + 0x0c, 0x01, 0x22, 0x20, 0x2c, 0x03, 0xd2, 0x0c, 0x22, 0x75, 0x31, 0xa4, 0x12, 0x7c, 0x15, 0x7e, + 0x14, 0x84, 0x00, 0x80, 0x06, 0x20, 0x2c, 0x03, 0xd2, 0x0c, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, + 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, 0x12, 0x56, 0x90, 0x20, 0xe6, 0x03, 0xd2, 0x0c, + 0x22, 0x30, 0x34, 0x49, 0xd2, 0x74, 0x7e, 0x37, 0x01, 0x97, 0x7e, 0x27, 0x01, 0xb7, 0x9d, 0x32, + 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x50, 0x7a, 0x05, 0x50, 0x7a, 0x37, 0x01, 0x97, 0x7e, 0x37, + 0x01, 0x77, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x1d, 0xe2, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x77, + 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x12, 0x65, 0x2b, 0x10, + 0x74, 0xc4, 0x22, 0xc2, 0x74, 0x2d, 0x23, 0x68, 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, + 0x97, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, 0x27, 0x01, 0xb7, 0x28, 0x04, 0x7e, 0x27, 0x01, + 0xb7, 0x7e, 0x37, 0x01, 0x97, 0x9d, 0x32, 0x7d, 0x02, 0x2e, 0x05, 0x50, 0x7a, 0x05, 0x50, 0x7a, + 0x37, 0x01, 0x97, 0x7e, 0x37, 0x01, 0x77, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x1d, 0xe2, 0x38, + 0x13, 0x7a, 0x47, 0x01, 0x77, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, + 0x15, 0x02, 0x65, 0x2b, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, + 0x9e, 0x44, 0x1d, 0xe3, 0x9d, 0x24, 0x12, 0x65, 0x2b, 0x7e, 0x34, 0x19, 0xe3, 0x7d, 0x24, 0x2d, + 0x43, 0x7a, 0x47, 0x01, 0x77, 0x12, 0x65, 0x2b, 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x53, 0xdf, + 0x22, 0xd2, 0x0c, 0x7e, 0x04, 0x19, 0xe3, 0x7a, 0x07, 0x01, 0x77, 0x7a, 0x07, 0x01, 0x87, 0x75, + 0x31, 0x94, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x00, 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x92, 0x12, + 0x7c, 0x15, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, + 0x7e, 0x37, 0x01, 0xe1, 0x7e, 0x27, 0x01, 0xc7, 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, + 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, 0x46, 0x7a, 0x05, 0x46, 0x7a, 0x37, 0x01, 0xe1, + 0x7e, 0x37, 0x01, 0xdf, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x05, 0xe2, 0x38, 0x44, 0x7a, 0x47, + 0x01, 0xdf, 0x7e, 0x24, 0x04, 0x00, 0x2e, 0x27, 0x01, 0xc7, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, + 0x51, 0x31, 0x12, 0x7c, 0x15, 0xbe, 0x50, 0x38, 0x78, 0x03, 0x02, 0x66, 0x16, 0x02, 0x65, 0xfb, + 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3c, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x14, 0x22, - 0x80, 0x7f, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x05, 0xcd, 0x9d, 0x24, 0x7e, 0x64, - 0x04, 0x00, 0x2e, 0x67, 0x01, 0xb1, 0x9e, 0x24, 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, - 0x35, 0x12, 0x69, 0x04, 0x7e, 0x34, 0x01, 0xcd, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xc9, - 0x02, 0x69, 0x04, 0x7a, 0x39, 0xc0, 0x7e, 0x34, 0x01, 0xcd, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, + 0x80, 0x7d, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x9e, 0x44, 0x05, 0xe3, 0x9d, 0x24, 0x7e, 0x64, + 0x04, 0x00, 0x2e, 0x67, 0x01, 0xc7, 0x9e, 0x24, 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, + 0x35, 0x12, 0x65, 0xfb, 0x7e, 0x34, 0x01, 0xe3, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xdf, + 0x02, 0x65, 0xfb, 0x7a, 0x39, 0xc0, 0x7e, 0x34, 0x01, 0xe3, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, 0x0f, 0x23, 0x23, 0x23, 0x44, 0x04, 0x7a, 0x69, - 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x2f, 0x93, 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, - 0x7e, 0x30, 0xbd, 0x04, 0x68, 0x2b, 0x7a, 0x07, 0x01, 0xc9, 0x7e, 0x47, 0x01, 0xcb, 0x2d, 0x43, - 0x7a, 0x47, 0x01, 0xcb, 0x2e, 0x35, 0x30, 0x7a, 0x35, 0x30, 0x22, 0xd2, 0x04, 0x09, 0xb1, 0x00, - 0x14, 0x20, 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x28, 0x7e, 0x04, 0x01, 0xcd, 0x80, - 0x2a, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0xcf, 0x7e, 0x07, 0x01, 0xcb, 0x7e, 0x24, 0x03, 0xfe, 0x9d, - 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xc9, 0x7e, 0x44, 0x05, 0xcd, 0x7d, 0x60, 0x0b, 0x04, 0xbd, - 0x04, 0x68, 0xd2, 0x7d, 0x70, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd0, 0x7d, 0x54, 0x9d, 0x50, 0xbd, + 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x31, 0x93, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, + 0x7c, 0x15, 0xbd, 0x04, 0x68, 0x29, 0x7a, 0x07, 0x01, 0xdf, 0x7e, 0x47, 0x01, 0xe1, 0x2d, 0x43, + 0x7a, 0x47, 0x01, 0xe1, 0x2e, 0x35, 0x46, 0x7a, 0x35, 0x46, 0x22, 0x09, 0xb1, 0x00, 0x14, 0x20, + 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2a, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2c, 0x7e, + 0x04, 0x01, 0xe3, 0x80, 0xd1, 0xd2, 0x04, 0x7e, 0x07, 0x01, 0xe1, 0x7e, 0x24, 0x03, 0xfe, 0x9d, + 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xdf, 0x7e, 0x44, 0x05, 0xe3, 0x7d, 0x60, 0x0b, 0x04, 0xbd, + 0x04, 0x68, 0xd0, 0x7d, 0x70, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xce, 0x7d, 0x54, 0x9d, 0x50, 0xbd, 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, - 0x02, 0x59, 0x43, 0x75, 0x2f, 0x99, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, + 0x02, 0x55, 0x43, 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3c, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, - 0xd2, 0x14, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x59, - 0x43, 0xda, 0xb8, 0x02, 0x5a, 0x6a, 0x09, 0xb1, 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x2f, 0x90, - 0x12, 0x7e, 0x30, 0xf5, 0x2f, 0x12, 0x7e, 0x30, 0xa5, 0xfd, 0x5e, 0x50, 0x0a, 0x68, 0x1d, 0xa5, - 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x5c, 0x80, 0x02, 0xc2, 0x5c, 0xa5, 0xfd, 0x5e, 0x50, - 0x80, 0x68, 0x04, 0xd2, 0x54, 0x80, 0x02, 0xc2, 0x54, 0x12, 0x43, 0x08, 0x02, 0x65, 0x9c, 0x75, - 0x2f, 0x91, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x14, 0x7a, 0xb1, 0x2f, 0x12, 0x7e, 0x30, 0x20, - 0xe0, 0x08, 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x65, 0x9c, 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, - 0xa0, 0x80, 0x12, 0x65, 0x9c, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, - 0xc0, 0x09, 0x61, 0x00, 0x00, 0x12, 0x65, 0xbf, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, - 0x02, 0x59, 0x97, 0x75, 0x2f, 0x95, 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x96, 0x12, 0x7e, 0x30, - 0x22, 0x10, 0x0d, 0x01, 0x22, 0x20, 0x2d, 0x03, 0xd2, 0x0d, 0x22, 0x75, 0x2f, 0xa5, 0x12, 0x7e, - 0x30, 0x7e, 0x14, 0x85, 0x00, 0x80, 0x06, 0x20, 0x2d, 0x03, 0xd2, 0x0d, 0x22, 0x09, 0xb1, 0x00, - 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, 0x12, 0x5d, 0x9f, 0x30, 0x35, 0x06, - 0x20, 0xe6, 0x4f, 0xd2, 0x0d, 0x22, 0x30, 0xe6, 0x02, 0xd2, 0x65, 0x7e, 0x37, 0x01, 0x83, 0x7e, - 0x27, 0x01, 0xa3, 0x9d, 0x32, 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x3c, 0x7a, 0x05, 0x3c, 0x7a, - 0x37, 0x01, 0x83, 0x7e, 0x37, 0x01, 0x63, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x21, 0xcc, 0x38, - 0x68, 0x7a, 0x47, 0x01, 0x63, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, - 0x30, 0x12, 0x68, 0x34, 0x10, 0x65, 0xc4, 0x22, 0xc2, 0x65, 0x2d, 0x23, 0x68, 0x78, 0x6d, 0x33, - 0x80, 0x1a, 0x7e, 0x27, 0x01, 0x83, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, 0x27, 0x01, 0xa3, - 0x28, 0x04, 0x7e, 0x27, 0x01, 0xa3, 0x7e, 0x37, 0x01, 0x83, 0x9d, 0x32, 0x7d, 0x02, 0x2e, 0x05, - 0x3c, 0x7a, 0x05, 0x3c, 0x7a, 0x37, 0x01, 0x83, 0x7e, 0x37, 0x01, 0x63, 0x7d, 0x43, 0x2d, 0x42, - 0xbe, 0x44, 0x21, 0xcc, 0x38, 0x13, 0x7a, 0x47, 0x01, 0x63, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, - 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x02, 0x68, 0x34, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, - 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x21, 0xcd, 0x9d, 0x24, 0x12, 0x68, 0x34, 0x7e, 0x34, - 0x1d, 0xcd, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0x63, 0x12, 0x68, 0x34, 0xbe, 0x25, 0x20, - 0x78, 0x03, 0x02, 0x5b, 0x14, 0x22, 0xd2, 0x0d, 0x7e, 0x04, 0x1d, 0xcd, 0x7a, 0x07, 0x01, 0x63, - 0x7a, 0x07, 0x01, 0x73, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x75, 0x2f, 0x00, 0x12, 0x7e, 0x30, - 0x22, 0x75, 0x2f, 0x92, 0x12, 0x7e, 0x30, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, - 0x82, 0xda, 0xb8, 0x78, 0x70, 0x7e, 0x37, 0x01, 0xcb, 0x7e, 0x27, 0x01, 0xb3, 0x2e, 0x24, 0x00, - 0x02, 0x2d, 0x32, 0xbe, 0x34, 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, 0x30, 0x7a, 0x05, - 0x30, 0x7a, 0x37, 0x01, 0xcb, 0x7e, 0x37, 0x01, 0xc9, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x05, - 0xcc, 0x38, 0x44, 0x7a, 0x47, 0x01, 0xc9, 0x7e, 0x24, 0x05, 0x00, 0x2e, 0x27, 0x01, 0xb3, 0x1b, - 0x38, 0x20, 0x0b, 0x35, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0xbe, 0x50, 0x38, 0x78, 0x03, 0x02, - 0x69, 0x1f, 0x02, 0x69, 0x04, 0x75, 0x2f, 0x99, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, - 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3d, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, - 0x00, 0x10, 0xd2, 0x15, 0x22, 0x80, 0x7f, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x05, - 0xcd, 0x9d, 0x24, 0x7e, 0x64, 0x05, 0x00, 0x2e, 0x67, 0x01, 0xb3, 0x9e, 0x24, 0x00, 0x02, 0x40, - 0x17, 0x1b, 0x38, 0x60, 0x0b, 0x35, 0x12, 0x69, 0x04, 0x7e, 0x34, 0x01, 0xcd, 0x7d, 0x24, 0x2d, - 0x43, 0x7a, 0x47, 0x01, 0xc9, 0x02, 0x69, 0x04, 0x7a, 0x39, 0xc0, 0x7e, 0x34, 0x01, 0xcd, 0x7a, - 0x39, 0xd0, 0x0b, 0x34, 0x1b, 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, 0x0f, 0x23, 0x23, - 0x23, 0x44, 0x05, 0x7a, 0x69, 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x2f, 0x93, 0x12, 0x7e, - 0x30, 0x7a, 0x71, 0x2f, 0x12, 0x7e, 0x30, 0xbd, 0x04, 0x68, 0x2b, 0x7a, 0x07, 0x01, 0xc9, 0x7e, - 0x47, 0x01, 0xcb, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xcb, 0x2e, 0x35, 0x30, 0x7a, 0x35, 0x30, 0x22, - 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x28, - 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x2a, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0xcf, 0x7e, 0x07, 0x01, 0xcb, - 0x7e, 0x24, 0x03, 0xfe, 0x9d, 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xc9, 0x7e, 0x44, 0x05, 0xcd, - 0x7d, 0x60, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd2, 0x7d, 0x70, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd0, - 0x7d, 0x54, 0x9d, 0x50, 0xbd, 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, 0xb1, 0x00, 0x14, - 0xca, 0xb8, 0x54, 0x1f, 0xb4, 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, 0x09, 0xb0, 0x0b, - 0x04, 0x1b, 0x24, 0x78, 0xe7, 0x02, 0x5c, 0x78, 0x75, 0x2f, 0x99, 0x12, 0x7e, 0x30, 0x09, 0xb1, + 0xd2, 0x14, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x55, + 0x43, 0xda, 0xb8, 0x02, 0x56, 0x90, 0x09, 0xb1, 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x31, 0x90, + 0x12, 0x7c, 0x15, 0xf5, 0x31, 0x12, 0x7c, 0x15, 0xa5, 0xfc, 0x5e, 0xb0, 0xf0, 0xa5, 0xfd, 0x09, + 0xb1, 0x00, 0x18, 0x4c, 0x4b, 0x5e, 0xb0, 0xf0, 0xbc, 0xb5, 0x78, 0xf1, 0x5e, 0x40, 0x0f, 0x4c, + 0x54, 0x7c, 0xb5, 0x5e, 0x50, 0x0b, 0x68, 0x2a, 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, + 0x6c, 0x80, 0x02, 0xc2, 0x6c, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x64, 0x80, 0x02, + 0xc2, 0x64, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x5c, 0x80, 0x02, 0xc2, 0x5c, 0x12, + 0x43, 0x4c, 0x02, 0x62, 0x93, 0x75, 0x31, 0x91, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x14, 0x7a, + 0xb1, 0x31, 0x12, 0x7c, 0x15, 0x20, 0xe0, 0x08, 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x62, 0x93, + 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, 0x12, 0x62, 0x93, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, + 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, 0x61, 0x00, 0x00, 0x12, 0x62, 0xb6, 0x09, 0xb1, + 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x55, 0x95, 0x75, 0x31, 0x95, 0x12, 0x7c, 0x15, 0x22, + 0x75, 0x31, 0x96, 0x12, 0x7c, 0x15, 0x22, 0x10, 0x0d, 0x01, 0x22, 0x20, 0x2d, 0x03, 0xd2, 0x0d, + 0x22, 0x75, 0x31, 0xa5, 0x12, 0x7c, 0x15, 0x7e, 0x14, 0x85, 0x00, 0x80, 0x06, 0x20, 0x2d, 0x03, + 0xd2, 0x0d, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, + 0x12, 0x59, 0xe8, 0x20, 0xe6, 0x03, 0xd2, 0x0d, 0x22, 0x30, 0x35, 0x49, 0xd2, 0x75, 0x7e, 0x37, + 0x01, 0x99, 0x7e, 0x27, 0x01, 0xb9, 0x9d, 0x32, 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x52, 0x7a, + 0x05, 0x52, 0x7a, 0x37, 0x01, 0x99, 0x7e, 0x37, 0x01, 0x79, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, + 0x21, 0xe2, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x79, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, + 0x31, 0x12, 0x7c, 0x15, 0x12, 0x65, 0x2b, 0x10, 0x75, 0xc4, 0x22, 0xc2, 0x75, 0x2d, 0x23, 0x68, + 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, 0x99, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, + 0x27, 0x01, 0xb9, 0x28, 0x04, 0x7e, 0x27, 0x01, 0xb9, 0x7e, 0x37, 0x01, 0x99, 0x9d, 0x32, 0x7d, + 0x02, 0x2e, 0x05, 0x52, 0x7a, 0x05, 0x52, 0x7a, 0x37, 0x01, 0x99, 0x7e, 0x37, 0x01, 0x79, 0x7d, + 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x21, 0xe2, 0x38, 0x13, 0x7a, 0x47, 0x01, 0x79, 0x75, 0x31, 0x94, + 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x02, 0x65, 0x2b, 0x75, 0x31, 0x94, 0x12, + 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x9e, 0x44, 0x21, 0xe3, 0x9d, 0x24, 0x12, 0x65, + 0x2b, 0x7e, 0x34, 0x1d, 0xe3, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0x79, 0x12, 0x65, 0x2b, + 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x57, 0x37, 0x22, 0xd2, 0x0d, 0x7e, 0x04, 0x1d, 0xe3, 0x7a, + 0x07, 0x01, 0x79, 0x7a, 0x07, 0x01, 0x89, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x00, + 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x92, 0x12, 0x7c, 0x15, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, + 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, 0x7e, 0x37, 0x01, 0xe1, 0x7e, 0x27, 0x01, 0xc9, + 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, + 0x46, 0x7a, 0x05, 0x46, 0x7a, 0x37, 0x01, 0xe1, 0x7e, 0x37, 0x01, 0xdf, 0x7d, 0x43, 0x2d, 0x42, + 0xbe, 0x44, 0x05, 0xe2, 0x38, 0x44, 0x7a, 0x47, 0x01, 0xdf, 0x7e, 0x24, 0x05, 0x00, 0x2e, 0x27, + 0x01, 0xc9, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0xbe, 0x50, 0x38, + 0x78, 0x03, 0x02, 0x66, 0x16, 0x02, 0x65, 0xfb, 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3d, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, - 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x15, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, 0xbd, 0x32, 0x68, - 0x07, 0xca, 0xb8, 0x12, 0x5c, 0x78, 0xda, 0xb8, 0x02, 0x5d, 0x9f, 0x09, 0xb1, 0x00, 0x18, 0x7e, - 0xa0, 0x88, 0x75, 0x2f, 0x90, 0x12, 0x7e, 0x30, 0xf5, 0x2f, 0x12, 0x7e, 0x30, 0xa5, 0xfd, 0x5e, - 0x50, 0x0a, 0x68, 0x1d, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x5d, 0x80, 0x02, 0xc2, - 0x5d, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x55, 0x80, 0x02, 0xc2, 0x55, 0x12, 0x43, - 0x1f, 0x02, 0x65, 0x9c, 0x75, 0x2f, 0x91, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x14, 0x7a, 0xb1, - 0x2f, 0x12, 0x7e, 0x30, 0x20, 0xe0, 0x08, 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x65, 0x9c, 0xd2, - 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, 0x12, 0x65, 0x9c, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, 0xda, - 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, 0x61, 0x00, 0x00, 0x12, 0x65, 0xbf, 0x09, 0xb1, 0x00, - 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x5c, 0xcc, 0x75, 0x2f, 0x95, 0x12, 0x7e, 0x30, 0x22, 0x75, - 0x2f, 0x96, 0x12, 0x7e, 0x30, 0x22, 0x10, 0x0e, 0x01, 0x22, 0x20, 0x2e, 0x03, 0xd2, 0x0e, 0x22, - 0x75, 0x2f, 0xa6, 0x12, 0x7e, 0x30, 0x7e, 0x14, 0x86, 0x00, 0x80, 0x06, 0x20, 0x2e, 0x03, 0xd2, - 0x0e, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, 0x12, - 0x60, 0xd4, 0x30, 0x36, 0x06, 0x20, 0xe6, 0x4f, 0xd2, 0x0e, 0x22, 0x30, 0xe6, 0x02, 0xd2, 0x66, - 0x7e, 0x37, 0x01, 0x85, 0x7e, 0x27, 0x01, 0xa5, 0x9d, 0x32, 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, - 0x3e, 0x7a, 0x05, 0x3e, 0x7a, 0x37, 0x01, 0x85, 0x7e, 0x37, 0x01, 0x65, 0x7d, 0x43, 0x2d, 0x42, - 0xbe, 0x44, 0x25, 0xcc, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x65, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, - 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x12, 0x68, 0x34, 0x10, 0x66, 0xc4, 0x22, 0xc2, 0x66, 0x2d, - 0x23, 0x68, 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, 0x85, 0xbe, 0x24, 0x00, 0x00, 0x68, - 0x6a, 0xbe, 0x27, 0x01, 0xa5, 0x28, 0x04, 0x7e, 0x27, 0x01, 0xa5, 0x7e, 0x37, 0x01, 0x85, 0x9d, - 0x32, 0x7d, 0x02, 0x2e, 0x05, 0x3e, 0x7a, 0x05, 0x3e, 0x7a, 0x37, 0x01, 0x85, 0x7e, 0x37, 0x01, - 0x65, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x25, 0xcc, 0x38, 0x13, 0x7a, 0x47, 0x01, 0x65, 0x75, - 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x02, 0x68, 0x34, 0x75, 0x2f, - 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x25, 0xcd, 0x9d, 0x24, - 0x12, 0x68, 0x34, 0x7e, 0x34, 0x21, 0xcd, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0x65, 0x12, - 0x68, 0x34, 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x5e, 0x49, 0x22, 0xd2, 0x0e, 0x7e, 0x04, 0x21, - 0xcd, 0x7a, 0x07, 0x01, 0x65, 0x7a, 0x07, 0x01, 0x75, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x75, - 0x2f, 0x00, 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x92, 0x12, 0x7e, 0x30, 0xd2, 0x04, 0x09, 0xb1, - 0x00, 0x14, 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, 0x7e, 0x37, 0x01, 0xcb, 0x7e, 0x27, - 0x01, 0xb5, 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, - 0x2e, 0x05, 0x30, 0x7a, 0x05, 0x30, 0x7a, 0x37, 0x01, 0xcb, 0x7e, 0x37, 0x01, 0xc9, 0x7d, 0x43, - 0x2d, 0x42, 0xbe, 0x44, 0x05, 0xcc, 0x38, 0x44, 0x7a, 0x47, 0x01, 0xc9, 0x7e, 0x24, 0x06, 0x00, - 0x2e, 0x27, 0x01, 0xb5, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0xbe, - 0x50, 0x38, 0x78, 0x03, 0x02, 0x69, 0x1f, 0x02, 0x69, 0x04, 0x75, 0x2f, 0x99, 0x12, 0x7e, 0x30, - 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3e, 0x0a, 0x09, 0xb1, 0x00, - 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x16, 0x22, 0x80, 0x7f, 0x7a, 0x51, 0x2f, 0x12, - 0x7e, 0x30, 0x9e, 0x44, 0x05, 0xcd, 0x9d, 0x24, 0x7e, 0x64, 0x06, 0x00, 0x2e, 0x67, 0x01, 0xb5, - 0x9e, 0x24, 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, 0x35, 0x12, 0x69, 0x04, 0x7e, 0x34, - 0x01, 0xcd, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xc9, 0x02, 0x69, 0x04, 0x7a, 0x39, 0xc0, - 0x7e, 0x34, 0x01, 0xcd, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, - 0xb6, 0x54, 0x0f, 0x23, 0x23, 0x23, 0x44, 0x06, 0x7a, 0x69, 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, - 0x75, 0x2f, 0x93, 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, 0x7e, 0x30, 0xbd, 0x04, 0x68, 0x2b, - 0x7a, 0x07, 0x01, 0xc9, 0x7e, 0x47, 0x01, 0xcb, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xcb, 0x2e, 0x35, - 0x30, 0x7a, 0x35, 0x30, 0x22, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0x13, 0x22, 0x7e, - 0x04, 0x01, 0xcd, 0x80, 0x28, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x2a, 0x7e, 0x04, 0x01, 0xcd, 0x80, - 0xcf, 0x7e, 0x07, 0x01, 0xcb, 0x7e, 0x24, 0x03, 0xfe, 0x9d, 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, - 0xc9, 0x7e, 0x44, 0x05, 0xcd, 0x7d, 0x60, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd2, 0x7d, 0x70, 0x0b, - 0x04, 0xbd, 0x04, 0x68, 0xd0, 0x7d, 0x54, 0x9d, 0x50, 0xbd, 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, - 0x32, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, - 0xb0, 0x7a, 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, 0x02, 0x5f, 0xad, 0x75, 0x2f, 0x99, - 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3e, 0x0a, - 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x16, 0x22, 0xda, 0xb8, 0x30, - 0xe0, 0xd8, 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x5f, 0xad, 0xda, 0xb8, 0x02, 0x60, 0xd4, - 0x09, 0xb1, 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x2f, 0x90, 0x12, 0x7e, 0x30, 0xf5, 0x2f, 0x12, - 0x7e, 0x30, 0xa5, 0xfd, 0x5e, 0x50, 0x0a, 0x68, 0x1d, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, - 0xd2, 0x5e, 0x80, 0x02, 0xc2, 0x5e, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x56, 0x80, - 0x02, 0xc2, 0x56, 0x12, 0x43, 0x36, 0x02, 0x65, 0x9c, 0x75, 0x2f, 0x91, 0x12, 0x7e, 0x30, 0x09, - 0xb1, 0x00, 0x14, 0x7a, 0xb1, 0x2f, 0x12, 0x7e, 0x30, 0x20, 0xe0, 0x08, 0xd2, 0x04, 0x7e, 0xa0, - 0x80, 0x02, 0x65, 0x9c, 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, 0x12, 0x65, 0x9c, 0xca, - 0xb8, 0x5e, 0xb0, 0x1c, 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, 0x61, 0x00, 0x00, 0x12, - 0x65, 0xbf, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x60, 0x01, 0x75, 0x2f, 0x95, - 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x96, 0x12, 0x7e, 0x30, 0x22, 0x10, 0x0f, 0x01, 0x22, 0x20, - 0x2f, 0x03, 0xd2, 0x0f, 0x22, 0x75, 0x2f, 0xa7, 0x12, 0x7e, 0x30, 0x7e, 0x14, 0x87, 0x00, 0x80, - 0x06, 0x20, 0x2f, 0x03, 0xd2, 0x0f, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, - 0xda, 0xb8, 0x68, 0x03, 0x12, 0x64, 0x09, 0x30, 0x37, 0x06, 0x20, 0xe6, 0x4f, 0xd2, 0x0f, 0x22, - 0x30, 0xe6, 0x02, 0xd2, 0x67, 0x7e, 0x37, 0x01, 0x87, 0x7e, 0x27, 0x01, 0xa7, 0x9d, 0x32, 0x40, - 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x40, 0x7a, 0x05, 0x40, 0x7a, 0x37, 0x01, 0x87, 0x7e, 0x37, 0x01, - 0x67, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x29, 0xcc, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x67, 0x75, - 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x12, 0x68, 0x34, 0x10, 0x67, - 0xc4, 0x22, 0xc2, 0x67, 0x2d, 0x23, 0x68, 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, 0x87, - 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, 0x27, 0x01, 0xa7, 0x28, 0x04, 0x7e, 0x27, 0x01, 0xa7, - 0x7e, 0x37, 0x01, 0x87, 0x9d, 0x32, 0x7d, 0x02, 0x2e, 0x05, 0x40, 0x7a, 0x05, 0x40, 0x7a, 0x37, - 0x01, 0x87, 0x7e, 0x37, 0x01, 0x67, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x29, 0xcc, 0x38, 0x13, - 0x7a, 0x47, 0x01, 0x67, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, - 0x02, 0x68, 0x34, 0x75, 0x2f, 0x94, 0x12, 0x7e, 0x30, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x9e, - 0x44, 0x29, 0xcd, 0x9d, 0x24, 0x12, 0x68, 0x34, 0x7e, 0x34, 0x25, 0xcd, 0x7d, 0x24, 0x2d, 0x43, - 0x7a, 0x47, 0x01, 0x67, 0x12, 0x68, 0x34, 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x61, 0x7e, 0x22, - 0xd2, 0x0f, 0x7e, 0x04, 0x25, 0xcd, 0x7a, 0x07, 0x01, 0x67, 0x7a, 0x07, 0x01, 0x77, 0x75, 0x2f, - 0x94, 0x12, 0x7e, 0x30, 0x75, 0x2f, 0x00, 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x92, 0x12, 0x7e, - 0x30, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, 0x7e, - 0x37, 0x01, 0xcb, 0x7e, 0x27, 0x01, 0xb7, 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, 0x04, - 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, 0x30, 0x7a, 0x05, 0x30, 0x7a, 0x37, 0x01, 0xcb, 0x7e, - 0x37, 0x01, 0xc9, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x05, 0xcc, 0x38, 0x44, 0x7a, 0x47, 0x01, - 0xc9, 0x7e, 0x24, 0x07, 0x00, 0x2e, 0x27, 0x01, 0xb7, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, 0x51, - 0x2f, 0x12, 0x7e, 0x30, 0xbe, 0x50, 0x38, 0x78, 0x03, 0x02, 0x69, 0x1f, 0x02, 0x69, 0x04, 0x75, - 0x2f, 0x99, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, - 0x3f, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x17, 0x22, 0x80, - 0x7f, 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x05, 0xcd, 0x9d, 0x24, 0x7e, 0x64, 0x07, - 0x00, 0x2e, 0x67, 0x01, 0xb7, 0x9e, 0x24, 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, 0x35, - 0x12, 0x69, 0x04, 0x7e, 0x34, 0x01, 0xcd, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xc9, 0x02, - 0x69, 0x04, 0x7a, 0x39, 0xc0, 0x7e, 0x34, 0x01, 0xcd, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, 0x44, - 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, 0x0f, 0x23, 0x23, 0x23, 0x44, 0x07, 0x7a, 0x69, 0xb0, - 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x2f, 0x93, 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, 0x7e, - 0x30, 0xbd, 0x04, 0x68, 0x2b, 0x7a, 0x07, 0x01, 0xc9, 0x7e, 0x47, 0x01, 0xcb, 0x2d, 0x43, 0x7a, - 0x47, 0x01, 0xcb, 0x2e, 0x35, 0x30, 0x7a, 0x35, 0x30, 0x22, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, - 0x20, 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x28, 0x7e, 0x04, 0x01, 0xcd, 0x80, 0x2a, - 0x7e, 0x04, 0x01, 0xcd, 0x80, 0xcf, 0x7e, 0x07, 0x01, 0xcb, 0x7e, 0x24, 0x03, 0xfe, 0x9d, 0x20, - 0x28, 0x40, 0x7e, 0x07, 0x01, 0xc9, 0x7e, 0x44, 0x05, 0xcd, 0x7d, 0x60, 0x0b, 0x04, 0xbd, 0x04, - 0x68, 0xd2, 0x7d, 0x70, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd0, 0x7d, 0x54, 0x9d, 0x50, 0xbd, 0x25, - 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, 0x01, - 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, 0x02, - 0x62, 0xe2, 0x75, 0x2f, 0x99, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, - 0x00, 0x04, 0x30, 0x3f, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, - 0x17, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x62, 0xe2, - 0xda, 0xb8, 0x02, 0x64, 0x09, 0x09, 0xb1, 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x2f, 0x90, 0x12, - 0x7e, 0x30, 0xf5, 0x2f, 0x12, 0x7e, 0x30, 0xa5, 0xfd, 0x5e, 0x50, 0x0a, 0x68, 0x1d, 0xa5, 0xfd, - 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x5f, 0x80, 0x02, 0xc2, 0x5f, 0xa5, 0xfd, 0x5e, 0x50, 0x80, - 0x68, 0x04, 0xd2, 0x57, 0x80, 0x02, 0xc2, 0x57, 0x12, 0x43, 0x4d, 0x02, 0x65, 0x9c, 0x75, 0x2f, - 0x91, 0x12, 0x7e, 0x30, 0x09, 0xb1, 0x00, 0x14, 0x7a, 0xb1, 0x2f, 0x12, 0x7e, 0x30, 0x20, 0xe0, - 0x08, 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x65, 0x9c, 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, - 0x80, 0x12, 0x65, 0x9c, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, - 0x09, 0x61, 0x00, 0x00, 0x12, 0x65, 0xbf, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, - 0x63, 0x36, 0x75, 0x2f, 0x95, 0x12, 0x7e, 0x30, 0x22, 0x75, 0x2f, 0x96, 0x12, 0x7e, 0x30, 0x22, - 0x7c, 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, 0xfd, 0x5e, 0x50, - 0x20, 0x68, 0x04, 0xd2, 0x58, 0x80, 0x02, 0xc2, 0x58, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, - 0xd2, 0x50, 0x80, 0x02, 0xc2, 0x50, 0x02, 0x65, 0x88, 0x7c, 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, - 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x59, 0x80, 0x02, - 0xc2, 0x59, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x51, 0x80, 0x02, 0xc2, 0x51, 0x02, - 0x65, 0x88, 0x7c, 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, 0xfd, - 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x5a, 0x80, 0x02, 0xc2, 0x5a, 0xa5, 0xfd, 0x5e, 0x50, 0x80, - 0x68, 0x04, 0xd2, 0x52, 0x80, 0x02, 0xc2, 0x52, 0x02, 0x65, 0x88, 0x7c, 0x02, 0x7e, 0x14, 0x80, - 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x5b, - 0x80, 0x02, 0xc2, 0x5b, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x53, 0x80, 0x02, 0xc2, - 0x53, 0x02, 0x65, 0x88, 0x7c, 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, 0x18, - 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x5c, 0x80, 0x02, 0xc2, 0x5c, 0xa5, 0xfd, 0x5e, - 0x50, 0x80, 0x68, 0x04, 0xd2, 0x54, 0x80, 0x02, 0xc2, 0x54, 0x02, 0x65, 0x88, 0x7c, 0x02, 0x7e, - 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, - 0xd2, 0x5d, 0x80, 0x02, 0xc2, 0x5d, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x55, 0x80, - 0x02, 0xc2, 0x55, 0x02, 0x65, 0x88, 0x7c, 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, 0xb1, - 0x00, 0x18, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x5e, 0x80, 0x02, 0xc2, 0x5e, 0xa5, - 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x56, 0x80, 0x02, 0xc2, 0x56, 0x02, 0x65, 0x88, 0x7c, - 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, 0xfd, 0x5e, 0x50, 0x20, - 0x68, 0x04, 0xd2, 0x5f, 0x80, 0x02, 0xc2, 0x5f, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, - 0x57, 0x80, 0x02, 0xc2, 0x57, 0x02, 0x65, 0x88, 0x54, 0xf0, 0xc4, 0xa5, 0xff, 0xc4, 0xa5, 0x4f, - 0x75, 0x2f, 0x90, 0x12, 0x7e, 0x30, 0xf5, 0x2f, 0x12, 0x7e, 0x30, 0x22, 0xca, 0x19, 0x5e, 0x20, - 0x07, 0x4c, 0xa2, 0x7e, 0x74, 0x29, 0xcd, 0xca, 0x79, 0x7a, 0x79, 0xa0, 0x0b, 0x74, 0x7a, 0x79, - 0xb0, 0x0b, 0x74, 0xda, 0x79, 0x7e, 0x30, 0x02, 0x7e, 0x64, 0x00, 0x02, 0x02, 0x65, 0xe7, 0xca, - 0x19, 0x5e, 0x20, 0x07, 0x4c, 0xa2, 0x7e, 0x74, 0x29, 0xcd, 0xca, 0x79, 0x7a, 0x79, 0xa0, 0x0b, - 0x74, 0x7a, 0x79, 0xb0, 0x0b, 0x74, 0x7a, 0x79, 0x60, 0x0b, 0x74, 0xda, 0x79, 0x7e, 0x30, 0x03, - 0x7e, 0x64, 0x00, 0x03, 0x02, 0x65, 0xe7, 0xd2, 0x04, 0x7e, 0x27, 0x01, 0xcb, 0x2d, 0x26, 0xbe, - 0x24, 0x04, 0x00, 0x38, 0x2e, 0x7e, 0x07, 0x01, 0xc9, 0x7e, 0x44, 0x05, 0xcd, 0x7e, 0x79, 0xa0, - 0x7a, 0x09, 0xa0, 0x0b, 0x04, 0x0b, 0x74, 0xbd, 0x04, 0x68, 0x23, 0xa5, 0xdb, 0xef, 0x7a, 0x27, - 0x01, 0xcb, 0x7e, 0x25, 0x30, 0x2d, 0x26, 0x7a, 0x25, 0x30, 0x7a, 0x07, 0x01, 0xc9, 0xda, 0x19, - 0xc2, 0xd7, 0x22, 0x75, 0x2f, 0x9a, 0x12, 0x7e, 0x30, 0xda, 0x19, 0xd2, 0xd7, 0x22, 0x7e, 0x04, - 0x01, 0xcd, 0x80, 0xd7, 0x4d, 0x42, 0x4a, 0xae, 0x4b, 0xa8, 0x4d, 0x7b, 0x49, 0x4f, 0x49, 0x4f, - 0x4c, 0xa7, 0x49, 0x4f, 0x4d, 0xbf, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x4d, 0xc6, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x50, 0x77, 0x4d, 0xe3, 0x4e, 0xdd, 0x50, 0xb0, 0x49, 0x4f, 0x49, 0x4f, - 0x4f, 0xdc, 0x49, 0x4f, 0x50, 0xf4, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x50, 0xfb, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x53, 0xac, 0x51, 0x18, 0x52, 0x12, 0x53, 0xe5, 0x49, 0x4f, 0x49, 0x4f, - 0x53, 0x11, 0x49, 0x4f, 0x54, 0x29, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x54, 0x30, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x56, 0xe1, 0x54, 0x4d, 0x55, 0x47, 0x57, 0x1a, 0x49, 0x4f, 0x49, 0x4f, - 0x56, 0x46, 0x49, 0x4f, 0x57, 0x5e, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x57, 0x65, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x5a, 0x16, 0x57, 0x82, 0x58, 0x7c, 0x5a, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x59, 0x7b, 0x49, 0x4f, 0x5a, 0x93, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x5a, 0x9a, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x5d, 0x4b, 0x5a, 0xb7, 0x5b, 0xb1, 0x5d, 0x84, 0x49, 0x4f, 0x49, 0x4f, - 0x5c, 0xb0, 0x49, 0x4f, 0x5d, 0xc8, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x5d, 0xcf, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x60, 0x80, 0x5d, 0xec, 0x5e, 0xe6, 0x60, 0xb9, 0x49, 0x4f, 0x49, 0x4f, - 0x5f, 0xe5, 0x49, 0x4f, 0x60, 0xfd, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x61, 0x04, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x63, 0xb5, 0x61, 0x21, 0x62, 0x1b, 0x63, 0xee, 0x49, 0x4f, 0x49, 0x4f, - 0x63, 0x1a, 0x49, 0x4f, 0x64, 0x32, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x64, 0x39, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, 0x49, 0x4f, - 0x49, 0x4f, 0x49, 0x4f, 0xca, 0x29, 0x1e, 0x50, 0x40, 0x0d, 0x7e, 0x54, 0x0b, 0x10, 0x9c, 0xb5, - 0xa4, 0x2e, 0x54, 0x68, 0x51, 0x89, 0x54, 0x7e, 0x39, 0x00, 0x7a, 0x19, 0x00, 0x0b, 0x34, 0x80, - 0xe9, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, - 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, - 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, - 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, + 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x15, 0x22, 0x80, 0x7d, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, + 0x9e, 0x44, 0x05, 0xe3, 0x9d, 0x24, 0x7e, 0x64, 0x05, 0x00, 0x2e, 0x67, 0x01, 0xc9, 0x9e, 0x24, + 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, 0x35, 0x12, 0x65, 0xfb, 0x7e, 0x34, 0x01, 0xe3, + 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xdf, 0x02, 0x65, 0xfb, 0x7a, 0x39, 0xc0, 0x7e, 0x34, + 0x01, 0xe3, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, + 0x0f, 0x23, 0x23, 0x23, 0x44, 0x05, 0x7a, 0x69, 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x31, + 0x93, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, 0x7c, 0x15, 0xbd, 0x04, 0x68, 0x29, 0x7a, 0x07, + 0x01, 0xdf, 0x7e, 0x47, 0x01, 0xe1, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xe1, 0x2e, 0x35, 0x46, 0x7a, + 0x35, 0x46, 0x22, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xe3, 0x80, + 0x2a, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2c, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0xd1, 0xd2, 0x04, 0x7e, + 0x07, 0x01, 0xe1, 0x7e, 0x24, 0x03, 0xfe, 0x9d, 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xdf, 0x7e, + 0x44, 0x05, 0xe3, 0x7d, 0x60, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd0, 0x7d, 0x70, 0x0b, 0x04, 0xbd, + 0x04, 0x68, 0xce, 0x7d, 0x54, 0x9d, 0x50, 0xbd, 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, + 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, + 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, 0x02, 0x58, 0x9b, 0x75, 0x31, 0x99, 0x12, 0x7c, + 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3d, 0x0a, 0x09, 0xb1, + 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x15, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, + 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x58, 0x9b, 0xda, 0xb8, 0x02, 0x59, 0xe8, 0x09, 0xb1, + 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x31, 0x90, 0x12, 0x7c, 0x15, 0xf5, 0x31, 0x12, 0x7c, 0x15, + 0xa5, 0xfc, 0x5e, 0xb0, 0xf0, 0xa5, 0xfd, 0x09, 0xb1, 0x00, 0x18, 0x4c, 0x4b, 0x5e, 0xb0, 0xf0, + 0xbc, 0xb5, 0x78, 0xf1, 0x5e, 0x40, 0x0f, 0x4c, 0x54, 0x7c, 0xb5, 0x5e, 0x50, 0x0b, 0x68, 0x2a, + 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, 0x6d, 0x80, 0x02, 0xc2, 0x6d, 0xa5, 0xfd, 0x5e, + 0x50, 0x20, 0x68, 0x04, 0xd2, 0x65, 0x80, 0x02, 0xc2, 0x65, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, + 0x04, 0xd2, 0x5d, 0x80, 0x02, 0xc2, 0x5d, 0x12, 0x43, 0x6d, 0x02, 0x62, 0x93, 0x75, 0x31, 0x91, + 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x14, 0x7a, 0xb1, 0x31, 0x12, 0x7c, 0x15, 0x20, 0xe0, 0x08, + 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x62, 0x93, 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, + 0x12, 0x62, 0x93, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, + 0x61, 0x00, 0x00, 0x12, 0x62, 0xb6, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x58, + 0xed, 0x75, 0x31, 0x95, 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x96, 0x12, 0x7c, 0x15, 0x22, 0x10, + 0x0e, 0x01, 0x22, 0x20, 0x2e, 0x03, 0xd2, 0x0e, 0x22, 0x75, 0x31, 0xa6, 0x12, 0x7c, 0x15, 0x7e, + 0x14, 0x86, 0x00, 0x80, 0x06, 0x20, 0x2e, 0x03, 0xd2, 0x0e, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, + 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, 0x12, 0x5d, 0x40, 0x20, 0xe6, 0x03, 0xd2, 0x0e, + 0x22, 0x30, 0x36, 0x49, 0xd2, 0x76, 0x7e, 0x37, 0x01, 0x9b, 0x7e, 0x27, 0x01, 0xbb, 0x9d, 0x32, + 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x54, 0x7a, 0x05, 0x54, 0x7a, 0x37, 0x01, 0x9b, 0x7e, 0x37, + 0x01, 0x7b, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x25, 0xe2, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x7b, + 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x12, 0x65, 0x2b, 0x10, + 0x76, 0xc4, 0x22, 0xc2, 0x76, 0x2d, 0x23, 0x68, 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, + 0x9b, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, 0x27, 0x01, 0xbb, 0x28, 0x04, 0x7e, 0x27, 0x01, + 0xbb, 0x7e, 0x37, 0x01, 0x9b, 0x9d, 0x32, 0x7d, 0x02, 0x2e, 0x05, 0x54, 0x7a, 0x05, 0x54, 0x7a, + 0x37, 0x01, 0x9b, 0x7e, 0x37, 0x01, 0x7b, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x25, 0xe2, 0x38, + 0x13, 0x7a, 0x47, 0x01, 0x7b, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, + 0x15, 0x02, 0x65, 0x2b, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, + 0x9e, 0x44, 0x25, 0xe3, 0x9d, 0x24, 0x12, 0x65, 0x2b, 0x7e, 0x34, 0x21, 0xe3, 0x7d, 0x24, 0x2d, + 0x43, 0x7a, 0x47, 0x01, 0x7b, 0x12, 0x65, 0x2b, 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x5a, 0x8f, + 0x22, 0xd2, 0x0e, 0x7e, 0x04, 0x21, 0xe3, 0x7a, 0x07, 0x01, 0x7b, 0x7a, 0x07, 0x01, 0x8b, 0x75, + 0x31, 0x94, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x00, 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x92, 0x12, + 0x7c, 0x15, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, + 0x7e, 0x37, 0x01, 0xe1, 0x7e, 0x27, 0x01, 0xcb, 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, + 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, 0x46, 0x7a, 0x05, 0x46, 0x7a, 0x37, 0x01, 0xe1, + 0x7e, 0x37, 0x01, 0xdf, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x05, 0xe2, 0x38, 0x44, 0x7a, 0x47, + 0x01, 0xdf, 0x7e, 0x24, 0x06, 0x00, 0x2e, 0x27, 0x01, 0xcb, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, + 0x51, 0x31, 0x12, 0x7c, 0x15, 0xbe, 0x50, 0x38, 0x78, 0x03, 0x02, 0x66, 0x16, 0x02, 0x65, 0xfb, + 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, + 0x30, 0x3e, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x16, 0x22, + 0x80, 0x7d, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x9e, 0x44, 0x05, 0xe3, 0x9d, 0x24, 0x7e, 0x64, + 0x06, 0x00, 0x2e, 0x67, 0x01, 0xcb, 0x9e, 0x24, 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, + 0x35, 0x12, 0x65, 0xfb, 0x7e, 0x34, 0x01, 0xe3, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xdf, + 0x02, 0x65, 0xfb, 0x7a, 0x39, 0xc0, 0x7e, 0x34, 0x01, 0xe3, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, + 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, 0x0f, 0x23, 0x23, 0x23, 0x44, 0x06, 0x7a, 0x69, + 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x31, 0x93, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, + 0x7c, 0x15, 0xbd, 0x04, 0x68, 0x29, 0x7a, 0x07, 0x01, 0xdf, 0x7e, 0x47, 0x01, 0xe1, 0x2d, 0x43, + 0x7a, 0x47, 0x01, 0xe1, 0x2e, 0x35, 0x46, 0x7a, 0x35, 0x46, 0x22, 0x09, 0xb1, 0x00, 0x14, 0x20, + 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2a, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2c, 0x7e, + 0x04, 0x01, 0xe3, 0x80, 0xd1, 0xd2, 0x04, 0x7e, 0x07, 0x01, 0xe1, 0x7e, 0x24, 0x03, 0xfe, 0x9d, + 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xdf, 0x7e, 0x44, 0x05, 0xe3, 0x7d, 0x60, 0x0b, 0x04, 0xbd, + 0x04, 0x68, 0xd0, 0x7d, 0x70, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xce, 0x7d, 0x54, 0x9d, 0x50, 0xbd, + 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, + 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, + 0x02, 0x5b, 0xf3, 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, + 0xb1, 0x00, 0x04, 0x30, 0x3e, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, + 0xd2, 0x16, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x5b, + 0xf3, 0xda, 0xb8, 0x02, 0x5d, 0x40, 0x09, 0xb1, 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x31, 0x90, + 0x12, 0x7c, 0x15, 0xf5, 0x31, 0x12, 0x7c, 0x15, 0xa5, 0xfc, 0x5e, 0xb0, 0xf0, 0xa5, 0xfd, 0x09, + 0xb1, 0x00, 0x18, 0x4c, 0x4b, 0x5e, 0xb0, 0xf0, 0xbc, 0xb5, 0x78, 0xf1, 0x5e, 0x40, 0x0f, 0x4c, + 0x54, 0x7c, 0xb5, 0x5e, 0x50, 0x0b, 0x68, 0x2a, 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, + 0x6e, 0x80, 0x02, 0xc2, 0x6e, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x66, 0x80, 0x02, + 0xc2, 0x66, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x5e, 0x80, 0x02, 0xc2, 0x5e, 0x12, + 0x43, 0x8e, 0x02, 0x62, 0x93, 0x75, 0x31, 0x91, 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x14, 0x7a, + 0xb1, 0x31, 0x12, 0x7c, 0x15, 0x20, 0xe0, 0x08, 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x62, 0x93, + 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, 0x12, 0x62, 0x93, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, + 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, 0x61, 0x00, 0x00, 0x12, 0x62, 0xb6, 0x09, 0xb1, + 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x5c, 0x45, 0x75, 0x31, 0x95, 0x12, 0x7c, 0x15, 0x22, + 0x75, 0x31, 0x96, 0x12, 0x7c, 0x15, 0x22, 0x10, 0x0f, 0x01, 0x22, 0x20, 0x2f, 0x03, 0xd2, 0x0f, + 0x22, 0x75, 0x31, 0xa7, 0x12, 0x7c, 0x15, 0x7e, 0x14, 0x87, 0x00, 0x80, 0x06, 0x20, 0x2f, 0x03, + 0xd2, 0x0f, 0x22, 0x09, 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x5e, 0xb0, 0x1e, 0xda, 0xb8, 0x68, 0x03, + 0x12, 0x60, 0x98, 0x20, 0xe6, 0x03, 0xd2, 0x0f, 0x22, 0x30, 0x37, 0x49, 0xd2, 0x77, 0x7e, 0x37, + 0x01, 0x9d, 0x7e, 0x27, 0x01, 0xbd, 0x9d, 0x32, 0x40, 0x31, 0x7d, 0x02, 0x2e, 0x05, 0x56, 0x7a, + 0x05, 0x56, 0x7a, 0x37, 0x01, 0x9d, 0x7e, 0x37, 0x01, 0x7d, 0x7d, 0x43, 0x2d, 0x42, 0xbe, 0x44, + 0x29, 0xe2, 0x38, 0x68, 0x7a, 0x47, 0x01, 0x7d, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x7a, 0x51, + 0x31, 0x12, 0x7c, 0x15, 0x12, 0x65, 0x2b, 0x10, 0x77, 0xc4, 0x22, 0xc2, 0x77, 0x2d, 0x23, 0x68, + 0x78, 0x6d, 0x33, 0x80, 0x1a, 0x7e, 0x27, 0x01, 0x9d, 0xbe, 0x24, 0x00, 0x00, 0x68, 0x6a, 0xbe, + 0x27, 0x01, 0xbd, 0x28, 0x04, 0x7e, 0x27, 0x01, 0xbd, 0x7e, 0x37, 0x01, 0x9d, 0x9d, 0x32, 0x7d, + 0x02, 0x2e, 0x05, 0x56, 0x7a, 0x05, 0x56, 0x7a, 0x37, 0x01, 0x9d, 0x7e, 0x37, 0x01, 0x7d, 0x7d, + 0x43, 0x2d, 0x42, 0xbe, 0x44, 0x29, 0xe2, 0x38, 0x13, 0x7a, 0x47, 0x01, 0x7d, 0x75, 0x31, 0x94, + 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x02, 0x65, 0x2b, 0x75, 0x31, 0x94, 0x12, + 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x9e, 0x44, 0x29, 0xe3, 0x9d, 0x24, 0x12, 0x65, + 0x2b, 0x7e, 0x34, 0x25, 0xe3, 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0x7d, 0x12, 0x65, 0x2b, + 0xbe, 0x25, 0x20, 0x78, 0x03, 0x02, 0x5d, 0xe7, 0x22, 0xd2, 0x0f, 0x7e, 0x04, 0x25, 0xe3, 0x7a, + 0x07, 0x01, 0x7d, 0x7a, 0x07, 0x01, 0x8d, 0x75, 0x31, 0x94, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x00, + 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x92, 0x12, 0x7c, 0x15, 0xd2, 0x04, 0x09, 0xb1, 0x00, 0x14, + 0xca, 0xb8, 0x54, 0x82, 0xda, 0xb8, 0x78, 0x70, 0x7e, 0x37, 0x01, 0xe1, 0x7e, 0x27, 0x01, 0xcd, + 0x2e, 0x24, 0x00, 0x02, 0x2d, 0x32, 0xbe, 0x34, 0x04, 0x00, 0x38, 0x3c, 0x7d, 0x02, 0x2e, 0x05, + 0x46, 0x7a, 0x05, 0x46, 0x7a, 0x37, 0x01, 0xe1, 0x7e, 0x37, 0x01, 0xdf, 0x7d, 0x43, 0x2d, 0x42, + 0xbe, 0x44, 0x05, 0xe2, 0x38, 0x44, 0x7a, 0x47, 0x01, 0xdf, 0x7e, 0x24, 0x07, 0x00, 0x2e, 0x27, + 0x01, 0xcd, 0x1b, 0x38, 0x20, 0x0b, 0x35, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0xbe, 0x50, 0x38, + 0x78, 0x03, 0x02, 0x66, 0x16, 0x02, 0x65, 0xfb, 0x75, 0x31, 0x99, 0x12, 0x7c, 0x15, 0x09, 0xb1, + 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3f, 0x0a, 0x09, 0xb1, 0x00, 0x10, 0x54, + 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x17, 0x22, 0x80, 0x7d, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, + 0x9e, 0x44, 0x05, 0xe3, 0x9d, 0x24, 0x7e, 0x64, 0x07, 0x00, 0x2e, 0x67, 0x01, 0xcd, 0x9e, 0x24, + 0x00, 0x02, 0x40, 0x17, 0x1b, 0x38, 0x60, 0x0b, 0x35, 0x12, 0x65, 0xfb, 0x7e, 0x34, 0x01, 0xe3, + 0x7d, 0x24, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xdf, 0x02, 0x65, 0xfb, 0x7a, 0x39, 0xc0, 0x7e, 0x34, + 0x01, 0xe3, 0x7a, 0x39, 0xd0, 0x0b, 0x34, 0x1b, 0x44, 0x80, 0xe5, 0x9d, 0x32, 0x7c, 0xb6, 0x54, + 0x0f, 0x23, 0x23, 0x23, 0x44, 0x07, 0x7a, 0x69, 0xb0, 0x7a, 0x79, 0x70, 0x0b, 0x35, 0x75, 0x31, + 0x93, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, 0x7c, 0x15, 0xbd, 0x04, 0x68, 0x29, 0x7a, 0x07, + 0x01, 0xdf, 0x7e, 0x47, 0x01, 0xe1, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xe1, 0x2e, 0x35, 0x46, 0x7a, + 0x35, 0x46, 0x22, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0x13, 0x22, 0x7e, 0x04, 0x01, 0xe3, 0x80, + 0x2a, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0x2c, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0xd1, 0xd2, 0x04, 0x7e, + 0x07, 0x01, 0xe1, 0x7e, 0x24, 0x03, 0xfe, 0x9d, 0x20, 0x28, 0x40, 0x7e, 0x07, 0x01, 0xdf, 0x7e, + 0x44, 0x05, 0xe3, 0x7d, 0x60, 0x0b, 0x04, 0xbd, 0x04, 0x68, 0xd0, 0x7d, 0x70, 0x0b, 0x04, 0xbd, + 0x04, 0x68, 0xce, 0x7d, 0x54, 0x9d, 0x50, 0xbd, 0x25, 0x40, 0x02, 0x7d, 0x25, 0x7d, 0x32, 0x09, + 0xb1, 0x00, 0x14, 0xca, 0xb8, 0x54, 0x1f, 0xb4, 0x01, 0x31, 0xda, 0xb8, 0x7e, 0x19, 0xb0, 0x7a, + 0x09, 0xb0, 0x0b, 0x04, 0x1b, 0x24, 0x78, 0xe7, 0x02, 0x5f, 0x4b, 0x75, 0x31, 0x99, 0x12, 0x7c, + 0x15, 0x09, 0xb1, 0x00, 0x04, 0x54, 0xfa, 0x19, 0xb1, 0x00, 0x04, 0x30, 0x3f, 0x0a, 0x09, 0xb1, + 0x00, 0x10, 0x54, 0xfe, 0x19, 0xb1, 0x00, 0x10, 0xd2, 0x17, 0x22, 0xda, 0xb8, 0x30, 0xe0, 0xd8, + 0xbd, 0x32, 0x68, 0x07, 0xca, 0xb8, 0x12, 0x5f, 0x4b, 0xda, 0xb8, 0x02, 0x60, 0x98, 0x09, 0xb1, + 0x00, 0x18, 0x7e, 0xa0, 0x88, 0x75, 0x31, 0x90, 0x12, 0x7c, 0x15, 0xf5, 0x31, 0x12, 0x7c, 0x15, + 0xa5, 0xfc, 0x5e, 0xb0, 0xf0, 0xa5, 0xfd, 0x09, 0xb1, 0x00, 0x18, 0x4c, 0x4b, 0x5e, 0xb0, 0xf0, + 0xbc, 0xb5, 0x78, 0xf1, 0x5e, 0x40, 0x0f, 0x4c, 0x54, 0x7c, 0xb5, 0x5e, 0x50, 0x0b, 0x68, 0x2a, + 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, 0x6f, 0x80, 0x02, 0xc2, 0x6f, 0xa5, 0xfd, 0x5e, + 0x50, 0x20, 0x68, 0x04, 0xd2, 0x67, 0x80, 0x02, 0xc2, 0x67, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, + 0x04, 0xd2, 0x5f, 0x80, 0x02, 0xc2, 0x5f, 0x12, 0x43, 0xaf, 0x02, 0x62, 0x93, 0x75, 0x31, 0x91, + 0x12, 0x7c, 0x15, 0x09, 0xb1, 0x00, 0x14, 0x7a, 0xb1, 0x31, 0x12, 0x7c, 0x15, 0x20, 0xe0, 0x08, + 0xd2, 0x04, 0x7e, 0xa0, 0x80, 0x02, 0x62, 0x93, 0xd2, 0x04, 0x30, 0xe1, 0x06, 0x7e, 0xa0, 0x80, + 0x12, 0x62, 0x93, 0xca, 0xb8, 0x5e, 0xb0, 0x1c, 0xda, 0xb8, 0x68, 0x12, 0x7e, 0xa0, 0xc0, 0x09, + 0x61, 0x00, 0x00, 0x12, 0x62, 0xb6, 0x09, 0xb1, 0x00, 0x14, 0x20, 0xe0, 0xdb, 0x22, 0x02, 0x5f, + 0x9d, 0x75, 0x31, 0x95, 0x12, 0x7c, 0x15, 0x22, 0x75, 0x31, 0x96, 0x12, 0x7c, 0x15, 0x22, 0x7c, + 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, 0xfd, 0x5e, 0x50, 0x10, + 0x68, 0x04, 0xd2, 0x68, 0x80, 0x02, 0xc2, 0x68, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, + 0x60, 0x80, 0x02, 0xc2, 0x60, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x58, 0x80, 0x02, + 0xc2, 0x58, 0x02, 0x62, 0x7f, 0x7c, 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, + 0x18, 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, 0x69, 0x80, 0x02, 0xc2, 0x69, 0xa5, 0xfd, + 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x61, 0x80, 0x02, 0xc2, 0x61, 0xa5, 0xfd, 0x5e, 0x50, 0x80, + 0x68, 0x04, 0xd2, 0x59, 0x80, 0x02, 0xc2, 0x59, 0x02, 0x62, 0x7f, 0x7c, 0x02, 0x7e, 0x14, 0x80, + 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, 0x6a, + 0x80, 0x02, 0xc2, 0x6a, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x62, 0x80, 0x02, 0xc2, + 0x62, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x5a, 0x80, 0x02, 0xc2, 0x5a, 0x02, 0x62, + 0x7f, 0x7c, 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, 0xfd, 0x5e, + 0x50, 0x10, 0x68, 0x04, 0xd2, 0x6b, 0x80, 0x02, 0xc2, 0x6b, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, + 0x04, 0xd2, 0x63, 0x80, 0x02, 0xc2, 0x63, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x5b, + 0x80, 0x02, 0xc2, 0x5b, 0x02, 0x62, 0x7f, 0x7c, 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, + 0xb1, 0x00, 0x18, 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, 0x6c, 0x80, 0x02, 0xc2, 0x6c, + 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x64, 0x80, 0x02, 0xc2, 0x64, 0xa5, 0xfd, 0x5e, + 0x50, 0x80, 0x68, 0x04, 0xd2, 0x5c, 0x80, 0x02, 0xc2, 0x5c, 0x02, 0x62, 0x7f, 0x7c, 0x02, 0x7e, + 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, + 0xd2, 0x6d, 0x80, 0x02, 0xc2, 0x6d, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x65, 0x80, + 0x02, 0xc2, 0x65, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x5d, 0x80, 0x02, 0xc2, 0x5d, + 0x02, 0x62, 0x7f, 0x7c, 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, + 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, 0x6e, 0x80, 0x02, 0xc2, 0x6e, 0xa5, 0xfd, 0x5e, 0x50, + 0x20, 0x68, 0x04, 0xd2, 0x66, 0x80, 0x02, 0xc2, 0x66, 0xa5, 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, + 0xd2, 0x5e, 0x80, 0x02, 0xc2, 0x5e, 0x02, 0x62, 0x7f, 0x7c, 0x02, 0x7e, 0x14, 0x80, 0x00, 0x4c, + 0x20, 0x09, 0xb1, 0x00, 0x18, 0xa5, 0xfd, 0x5e, 0x50, 0x10, 0x68, 0x04, 0xd2, 0x6f, 0x80, 0x02, + 0xc2, 0x6f, 0xa5, 0xfd, 0x5e, 0x50, 0x20, 0x68, 0x04, 0xd2, 0x67, 0x80, 0x02, 0xc2, 0x67, 0xa5, + 0xfd, 0x5e, 0x50, 0x80, 0x68, 0x04, 0xd2, 0x5f, 0x80, 0x02, 0xc2, 0x5f, 0x02, 0x62, 0x7f, 0x54, + 0xf0, 0xc4, 0xa5, 0xff, 0xc4, 0xa5, 0x4f, 0x75, 0x31, 0x90, 0x12, 0x7c, 0x15, 0xf5, 0x31, 0x12, + 0x7c, 0x15, 0x22, 0xca, 0x19, 0x5e, 0x20, 0x07, 0x4c, 0xa2, 0x7e, 0x74, 0x29, 0xe3, 0xca, 0x79, + 0x7a, 0x79, 0xa0, 0x0b, 0x74, 0x7a, 0x79, 0xb0, 0x0b, 0x74, 0xda, 0x79, 0x7e, 0x30, 0x02, 0x7e, + 0x64, 0x00, 0x02, 0x02, 0x62, 0xde, 0xca, 0x19, 0x5e, 0x20, 0x07, 0x4c, 0xa2, 0x7e, 0x74, 0x29, + 0xe3, 0xca, 0x79, 0x7a, 0x79, 0xa0, 0x0b, 0x74, 0x7a, 0x79, 0xb0, 0x0b, 0x74, 0x7a, 0x79, 0x60, + 0x0b, 0x74, 0xda, 0x79, 0x7e, 0x30, 0x03, 0x7e, 0x64, 0x00, 0x03, 0x02, 0x62, 0xde, 0xd2, 0x04, + 0x7e, 0x27, 0x01, 0xe1, 0x2d, 0x26, 0xbe, 0x24, 0x04, 0x00, 0x38, 0x2e, 0x7e, 0x07, 0x01, 0xdf, + 0x7e, 0x44, 0x05, 0xe3, 0x7e, 0x79, 0xa0, 0x7a, 0x09, 0xa0, 0x0b, 0x04, 0x0b, 0x74, 0xbd, 0x04, + 0x68, 0x23, 0xa5, 0xdb, 0xef, 0x7a, 0x27, 0x01, 0xe1, 0x7e, 0x25, 0x46, 0x2d, 0x26, 0x7a, 0x25, + 0x46, 0x7a, 0x07, 0x01, 0xdf, 0xda, 0x19, 0xc2, 0xd7, 0x22, 0x75, 0x31, 0x9a, 0x12, 0x7c, 0x15, + 0xda, 0x19, 0xd2, 0xd7, 0x22, 0x7e, 0x04, 0x01, 0xe3, 0x80, 0xd7, 0x48, 0xb6, 0x46, 0x25, 0x47, + 0x1c, 0x49, 0x15, 0x44, 0xc6, 0x44, 0xc6, 0x48, 0x1b, 0x44, 0xc6, 0x49, 0x59, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x49, 0x60, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x4c, 0x0e, 0x49, 0x7d, 0x4a, + 0x74, 0x4c, 0x6d, 0x44, 0xc6, 0x44, 0xc6, 0x4b, 0x73, 0x44, 0xc6, 0x4c, 0xb1, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x4c, 0xb8, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x4f, 0x66, 0x4c, 0xd5, 0x4d, + 0xcc, 0x4f, 0xc5, 0x44, 0xc6, 0x44, 0xc6, 0x4e, 0xcb, 0x44, 0xc6, 0x50, 0x09, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x50, 0x10, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x52, 0xbe, 0x50, 0x2d, 0x51, + 0x24, 0x53, 0x1d, 0x44, 0xc6, 0x44, 0xc6, 0x52, 0x23, 0x44, 0xc6, 0x53, 0x61, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x53, 0x68, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x56, 0x16, 0x53, 0x85, 0x54, + 0x7c, 0x56, 0x75, 0x44, 0xc6, 0x44, 0xc6, 0x55, 0x7b, 0x44, 0xc6, 0x56, 0xb9, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x56, 0xc0, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x59, 0x6e, 0x56, 0xdd, 0x57, + 0xd4, 0x59, 0xcd, 0x44, 0xc6, 0x44, 0xc6, 0x58, 0xd3, 0x44, 0xc6, 0x5a, 0x11, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x5a, 0x18, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x5c, 0xc6, 0x5a, 0x35, 0x5b, + 0x2c, 0x5d, 0x25, 0x44, 0xc6, 0x44, 0xc6, 0x5c, 0x2b, 0x44, 0xc6, 0x5d, 0x69, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x5d, 0x70, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x60, 0x1e, 0x5d, 0x8d, 0x5e, + 0x84, 0x60, 0x7d, 0x44, 0xc6, 0x44, 0xc6, 0x5f, 0x83, 0x44, 0xc6, 0x60, 0xc1, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x60, 0xc8, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, + 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0x44, 0xc6, 0xca, 0x29, 0x1e, 0x50, 0x40, + 0x0d, 0x7e, 0x54, 0x0b, 0x10, 0x9c, 0xb5, 0xa4, 0x2e, 0x54, 0x65, 0x48, 0x89, 0x54, 0x7e, 0x39, + 0x00, 0x7a, 0x19, 0x00, 0x0b, 0x34, 0x80, 0xe9, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, @@ -744,12 +832,12 @@ static unsigned char IMAGE_ARRAY_NAME[] 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, - 0x35, 0xda, 0x29, 0x22, 0x1e, 0x50, 0x40, 0x0d, 0x7e, 0x54, 0x0b, 0x1c, 0x9c, 0xb5, 0xa4, 0x2e, - 0x54, 0x69, 0x1f, 0x89, 0x54, 0x7e, 0x19, 0x00, 0x7a, 0x39, 0x00, 0x0b, 0x34, 0x80, 0xe9, 0x7e, - 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, - 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, - 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, - 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, + 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, + 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, + 0x0b, 0x35, 0x0b, 0x38, 0x00, 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0x0b, 0x38, 0x00, + 0x7a, 0x19, 0x00, 0x7a, 0x19, 0x10, 0x0b, 0x35, 0xda, 0x29, 0x22, 0x1e, 0x50, 0x40, 0x0d, 0x7e, + 0x54, 0x0b, 0x1c, 0x9c, 0xb5, 0xa4, 0x2e, 0x54, 0x66, 0x16, 0x89, 0x54, 0x7e, 0x19, 0x00, 0x7a, + 0x39, 0x00, 0x0b, 0x34, 0x80, 0xe9, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, @@ -765,157 +853,176 @@ static unsigned char IMAGE_ARRAY_NAME[] 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, - 0x00, 0x0b, 0x35, 0x22, 0x6a, 0x9b, 0x6c, 0x4f, 0x6c, 0x67, 0x6c, 0x82, 0x6d, 0x1d, 0x6d, 0xb5, - 0x6d, 0xd0, 0x6e, 0x62, 0x6d, 0xeb, 0x6e, 0x2c, 0x7c, 0xb3, 0xbe, 0xb0, 0x09, 0x28, 0x14, 0x75, - 0x2f, 0x09, 0x12, 0x7e, 0x30, 0x75, 0x57, 0x10, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x43, 0xe1, 0xc0, - 0xd0, 0xf1, 0x22, 0xc0, 0xa8, 0xc2, 0xaf, 0x23, 0x6c, 0xaa, 0x2e, 0x54, 0x6a, 0x54, 0x0b, 0x58, - 0x50, 0x89, 0x54, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x75, 0x2f, 0xb0, 0x12, 0x7e, - 0x30, 0x0a, 0x22, 0x09, 0xb2, 0x6a, 0x93, 0x42, 0x24, 0xd0, 0xa8, 0x22, 0x7c, 0xb2, 0x23, 0x0a, - 0x3b, 0x49, 0x33, 0x6a, 0xb7, 0x89, 0x34, 0x6a, 0xc7, 0x6a, 0xf5, 0x6b, 0x23, 0x6b, 0x51, 0x6b, - 0x7f, 0x6b, 0xad, 0x6b, 0xdb, 0x6c, 0x09, 0x12, 0x41, 0xa9, 0xd2, 0x28, 0xd2, 0x08, 0xc2, 0x40, - 0xc2, 0x48, 0xc2, 0x38, 0xc2, 0x30, 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xb9, 0x7e, 0x04, 0x00, 0x20, - 0x7a, 0x07, 0x01, 0x99, 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, 0x01, 0xa9, 0x12, 0x41, 0x16, 0x12, - 0x64, 0x40, 0x02, 0x6c, 0x37, 0x12, 0x41, 0xc6, 0xd2, 0x29, 0xd2, 0x09, 0xc2, 0x41, 0xc2, 0x49, - 0xc2, 0x39, 0xc2, 0x31, 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xba, 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, - 0x01, 0x9b, 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, 0x01, 0xab, 0x12, 0x41, 0x16, 0x12, 0x64, 0x69, - 0x02, 0x6c, 0x37, 0x12, 0x41, 0xe3, 0xd2, 0x2a, 0xd2, 0x0a, 0xc2, 0x42, 0xc2, 0x4a, 0xc2, 0x3a, - 0xc2, 0x32, 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xbb, 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, 0x01, 0x9d, - 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, 0x01, 0xad, 0x12, 0x41, 0x16, 0x12, 0x64, 0x92, 0x02, 0x6c, - 0x37, 0x12, 0x42, 0x00, 0xd2, 0x2b, 0xd2, 0x0b, 0xc2, 0x43, 0xc2, 0x4b, 0xc2, 0x3b, 0xc2, 0x33, - 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xbc, 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, 0x01, 0x9f, 0x7e, 0x04, - 0x00, 0x38, 0x7a, 0x07, 0x01, 0xaf, 0x12, 0x41, 0x16, 0x12, 0x64, 0xbb, 0x02, 0x6c, 0x37, 0x12, - 0x42, 0x1d, 0xd2, 0x2c, 0xd2, 0x0c, 0xc2, 0x44, 0xc2, 0x4c, 0xc2, 0x3c, 0xc2, 0x34, 0x6d, 0x00, - 0x7a, 0x03, 0x01, 0xbd, 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, 0x01, 0xa1, 0x7e, 0x04, 0x00, 0x38, - 0x7a, 0x07, 0x01, 0xb1, 0x12, 0x41, 0x16, 0x12, 0x64, 0xe4, 0x02, 0x6c, 0x37, 0x12, 0x42, 0x3a, - 0xd2, 0x2d, 0xd2, 0x0d, 0xc2, 0x45, 0xc2, 0x4d, 0xc2, 0x3d, 0xc2, 0x35, 0x6d, 0x00, 0x7a, 0x03, - 0x01, 0xbe, 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, 0x01, 0xa3, 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, - 0x01, 0xb3, 0x12, 0x41, 0x16, 0x12, 0x65, 0x0d, 0x02, 0x6c, 0x37, 0x12, 0x42, 0x57, 0xd2, 0x2e, - 0xd2, 0x0e, 0xc2, 0x46, 0xc2, 0x4e, 0xc2, 0x3e, 0xc2, 0x36, 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xbf, - 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, 0x01, 0xa5, 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, 0x01, 0xb5, - 0x12, 0x41, 0x16, 0x12, 0x65, 0x36, 0x02, 0x6c, 0x37, 0x12, 0x42, 0x74, 0xd2, 0x2f, 0xd2, 0x0f, - 0xc2, 0x47, 0xc2, 0x4f, 0xc2, 0x3f, 0xc2, 0x37, 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xc0, 0x7e, 0x04, - 0x00, 0x20, 0x7a, 0x07, 0x01, 0xa7, 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, 0x01, 0xb7, 0x12, 0x41, - 0x16, 0x12, 0x65, 0x5f, 0x02, 0x6c, 0x37, 0x7e, 0xa0, 0xd0, 0x7e, 0x60, 0x0f, 0x12, 0x65, 0xbf, - 0x40, 0x0c, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x71, 0xe9, 0xd0, 0xf1, 0xc2, 0xd7, 0x22, 0x75, - 0x2f, 0xb1, 0x12, 0x7e, 0x30, 0x0a, 0x52, 0x23, 0x6d, 0x00, 0x59, 0x05, 0x00, 0x32, 0x12, 0x41, - 0x72, 0x12, 0x41, 0x8e, 0xd0, 0xa8, 0x22, 0x75, 0x2f, 0xb2, 0x12, 0x7e, 0x30, 0x0a, 0x22, 0x09, - 0xb2, 0x6a, 0x93, 0x42, 0x23, 0x7e, 0xb0, 0x9c, 0x19, 0xb2, 0x01, 0xb9, 0x12, 0x45, 0x74, 0xd0, - 0xa8, 0x22, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0x09, 0x30, 0x00, 0x0c, 0x74, 0xbf, 0x19, 0xb0, - 0x00, 0x0c, 0x09, 0xb0, 0x00, 0x08, 0x19, 0x30, 0x00, 0x0c, 0x7c, 0x74, 0x5e, 0x70, 0x01, 0x68, - 0x12, 0x44, 0x40, 0xca, 0xb8, 0x09, 0xb0, 0x00, 0x10, 0x44, 0x02, 0x19, 0xb0, 0x00, 0x10, 0xda, - 0xb8, 0x80, 0x02, 0x54, 0xbf, 0x7c, 0x74, 0x5e, 0x70, 0x08, 0x68, 0x04, 0x44, 0x08, 0x80, 0x02, - 0x54, 0xf7, 0x09, 0x30, 0x00, 0x0c, 0xca, 0xb8, 0x74, 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0xda, 0xb8, - 0x19, 0xb0, 0x00, 0x08, 0x19, 0x30, 0x00, 0x0c, 0x0a, 0x62, 0x09, 0xb6, 0x6a, 0x93, 0x3e, 0x20, - 0x0a, 0x62, 0x7c, 0x74, 0x5e, 0x70, 0x02, 0x68, 0x20, 0x42, 0x27, 0xca, 0xb8, 0x74, 0x61, 0x19, - 0xb0, 0x00, 0x08, 0x7e, 0x44, 0x00, 0x10, 0x59, 0x46, 0x01, 0xa9, 0x09, 0xb0, 0x00, 0x10, 0x44, - 0x01, 0x19, 0xb0, 0x00, 0x10, 0xda, 0xb8, 0x80, 0x11, 0xf4, 0x52, 0x27, 0x74, 0xa1, 0x19, 0xb0, - 0x00, 0x08, 0x7e, 0x44, 0x00, 0x38, 0x59, 0x46, 0x01, 0xa9, 0xd0, 0xa8, 0x22, 0x7c, 0x74, 0x7e, - 0x04, 0x80, 0x00, 0x4c, 0x02, 0x09, 0x30, 0x00, 0x0c, 0x74, 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0x09, - 0xb0, 0x00, 0x08, 0x7c, 0x74, 0x5e, 0x70, 0x01, 0x68, 0x04, 0x44, 0x80, 0x80, 0x02, 0x54, 0x7f, - 0x7c, 0x74, 0x5e, 0x70, 0x08, 0x68, 0x04, 0x44, 0x02, 0x80, 0x02, 0x54, 0xfd, 0x19, 0xb0, 0x00, - 0x08, 0x19, 0x30, 0x00, 0x0c, 0x0a, 0x62, 0x09, 0xb6, 0x6a, 0x93, 0xa5, 0xfd, 0xf4, 0xa5, 0xfe, - 0xca, 0x28, 0x3e, 0x20, 0x0a, 0x62, 0xda, 0x28, 0x7c, 0x74, 0x5e, 0x70, 0x02, 0x68, 0x10, 0xa5, - 0xed, 0x42, 0x28, 0x42, 0x26, 0x7e, 0x44, 0x00, 0x08, 0x59, 0x46, 0x01, 0x99, 0x80, 0x04, 0xa5, - 0xee, 0x52, 0x28, 0x7c, 0x74, 0x5e, 0x70, 0x04, 0x68, 0x10, 0xa5, 0xed, 0x42, 0x29, 0x42, 0x26, - 0x7e, 0x44, 0x00, 0x08, 0x59, 0x46, 0x01, 0x99, 0x80, 0x15, 0xa5, 0xee, 0x52, 0x29, 0x7c, 0x74, - 0x5e, 0x70, 0x02, 0x78, 0x0a, 0x52, 0x26, 0x7e, 0x44, 0x00, 0x20, 0x59, 0x46, 0x01, 0x99, 0x12, - 0x42, 0x91, 0xd0, 0xa8, 0x22, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0x09, 0x30, 0x00, 0x0c, 0x74, - 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0x19, 0x40, 0x00, 0x10, 0x19, 0x30, 0x00, 0x0c, 0xd0, 0xa8, 0x22, - 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0x09, 0x30, 0x00, 0x0c, 0x74, 0xbf, 0x19, 0xb0, 0x00, 0x0c, - 0x19, 0x40, 0x00, 0x18, 0x19, 0x30, 0x00, 0x0c, 0xd0, 0xa8, 0x22, 0x75, 0x2f, 0xb5, 0x12, 0x7e, - 0x30, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0x09, 0xb0, 0x00, 0x0c, 0x44, 0x40, 0x19, 0xb0, 0x00, - 0x0c, 0xe5, 0x58, 0xb4, 0x07, 0x23, 0x09, 0xb0, 0x00, 0x10, 0x4e, 0xb0, 0x02, 0x19, 0xb0, 0x00, - 0x10, 0x09, 0x30, 0x00, 0x0c, 0x74, 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0x09, 0xb0, 0x00, 0x04, 0x54, - 0xf7, 0x19, 0xb0, 0x00, 0x04, 0x19, 0x30, 0x00, 0x0c, 0xd0, 0xa8, 0x22, 0x75, 0x2f, 0xb6, 0x12, - 0x7e, 0x30, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0xe5, 0x58, 0xb4, 0x07, 0x18, 0x09, 0x30, 0x00, - 0x0c, 0x74, 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0x09, 0xb0, 0x00, 0x04, 0x44, 0x08, 0x19, 0xb0, 0x00, - 0x04, 0x19, 0x30, 0x00, 0x0c, 0x09, 0xb0, 0x00, 0x0c, 0x54, 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0xd0, - 0xa8, 0x22, 0x75, 0x2f, 0xb4, 0x12, 0x7e, 0x30, 0x7a, 0x21, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x41, - 0x2f, 0x12, 0x7e, 0x30, 0x7e, 0xb0, 0x01, 0x7e, 0xa0, 0xc8, 0x7c, 0x64, 0x12, 0x65, 0xbf, 0xc0, - 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x71, 0xe9, 0xd0, 0xf1, 0xd0, 0xa8, 0x22, 0x6e, 0x9c, 0x6e, 0xe3, - 0x6f, 0x2a, 0x6f, 0x71, 0x6f, 0xb8, 0x6f, 0xff, 0x70, 0x46, 0x70, 0x8d, 0x75, 0x2f, 0x55, 0x12, - 0x7e, 0x30, 0x75, 0x2f, 0x00, 0x12, 0x7e, 0x30, 0x7a, 0x61, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x71, - 0x2f, 0x12, 0x7e, 0x30, 0x7e, 0x17, 0x01, 0x69, 0x7e, 0x27, 0x01, 0x79, 0x2d, 0x23, 0x7e, 0x09, - 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x0d, 0xcc, 0x38, 0x0f, 0x1b, 0x34, - 0x78, 0xec, 0x7a, 0x17, 0x01, 0x69, 0x7a, 0x27, 0x01, 0x79, 0x02, 0x4a, 0x98, 0x7e, 0x14, 0x09, - 0xcd, 0x80, 0xeb, 0x75, 0x2f, 0x55, 0x12, 0x7e, 0x30, 0x75, 0x2f, 0x01, 0x12, 0x7e, 0x30, 0x7a, - 0x61, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, 0x7e, 0x30, 0x7e, 0x17, 0x01, 0x6b, 0x7e, - 0x27, 0x01, 0x7b, 0x2d, 0x23, 0x7e, 0x09, 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, - 0x14, 0x11, 0xcc, 0x38, 0x0f, 0x1b, 0x34, 0x78, 0xec, 0x7a, 0x17, 0x01, 0x6b, 0x7a, 0x27, 0x01, - 0x7b, 0x02, 0x4d, 0xcd, 0x7e, 0x14, 0x0d, 0xcd, 0x80, 0xeb, 0x75, 0x2f, 0x55, 0x12, 0x7e, 0x30, - 0x75, 0x2f, 0x02, 0x12, 0x7e, 0x30, 0x7a, 0x61, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, - 0x7e, 0x30, 0x7e, 0x17, 0x01, 0x6d, 0x7e, 0x27, 0x01, 0x7d, 0x2d, 0x23, 0x7e, 0x09, 0xb0, 0x0b, - 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x15, 0xcc, 0x38, 0x0f, 0x1b, 0x34, 0x78, 0xec, - 0x7a, 0x17, 0x01, 0x6d, 0x7a, 0x27, 0x01, 0x7d, 0x02, 0x51, 0x02, 0x7e, 0x14, 0x11, 0xcd, 0x80, - 0xeb, 0x75, 0x2f, 0x55, 0x12, 0x7e, 0x30, 0x75, 0x2f, 0x03, 0x12, 0x7e, 0x30, 0x7a, 0x61, 0x2f, - 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, 0x7e, 0x30, 0x7e, 0x17, 0x01, 0x6f, 0x7e, 0x27, 0x01, - 0x7f, 0x2d, 0x23, 0x7e, 0x09, 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x19, - 0xcc, 0x38, 0x0f, 0x1b, 0x34, 0x78, 0xec, 0x7a, 0x17, 0x01, 0x6f, 0x7a, 0x27, 0x01, 0x7f, 0x02, - 0x54, 0x37, 0x7e, 0x14, 0x15, 0xcd, 0x80, 0xeb, 0x75, 0x2f, 0x55, 0x12, 0x7e, 0x30, 0x75, 0x2f, - 0x04, 0x12, 0x7e, 0x30, 0x7a, 0x61, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, 0x7e, 0x30, - 0x7e, 0x17, 0x01, 0x71, 0x7e, 0x27, 0x01, 0x81, 0x2d, 0x23, 0x7e, 0x09, 0xb0, 0x0b, 0x04, 0x7a, - 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x1d, 0xcc, 0x38, 0x0f, 0x1b, 0x34, 0x78, 0xec, 0x7a, 0x17, - 0x01, 0x71, 0x7a, 0x27, 0x01, 0x81, 0x02, 0x57, 0x6c, 0x7e, 0x14, 0x19, 0xcd, 0x80, 0xeb, 0x75, - 0x2f, 0x55, 0x12, 0x7e, 0x30, 0x75, 0x2f, 0x05, 0x12, 0x7e, 0x30, 0x7a, 0x61, 0x2f, 0x12, 0x7e, - 0x30, 0x7a, 0x71, 0x2f, 0x12, 0x7e, 0x30, 0x7e, 0x17, 0x01, 0x73, 0x7e, 0x27, 0x01, 0x83, 0x2d, - 0x23, 0x7e, 0x09, 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x21, 0xcc, 0x38, - 0x0f, 0x1b, 0x34, 0x78, 0xec, 0x7a, 0x17, 0x01, 0x73, 0x7a, 0x27, 0x01, 0x83, 0x02, 0x5a, 0xa1, - 0x7e, 0x14, 0x1d, 0xcd, 0x80, 0xeb, 0x75, 0x2f, 0x55, 0x12, 0x7e, 0x30, 0x75, 0x2f, 0x06, 0x12, - 0x7e, 0x30, 0x7a, 0x61, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, 0x7e, 0x30, 0x7e, 0x17, - 0x01, 0x75, 0x7e, 0x27, 0x01, 0x85, 0x2d, 0x23, 0x7e, 0x09, 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, - 0x0b, 0x14, 0xbe, 0x14, 0x25, 0xcc, 0x38, 0x0f, 0x1b, 0x34, 0x78, 0xec, 0x7a, 0x17, 0x01, 0x75, - 0x7a, 0x27, 0x01, 0x85, 0x02, 0x5d, 0xd6, 0x7e, 0x14, 0x21, 0xcd, 0x80, 0xeb, 0x75, 0x2f, 0x55, - 0x12, 0x7e, 0x30, 0x75, 0x2f, 0x07, 0x12, 0x7e, 0x30, 0x7a, 0x61, 0x2f, 0x12, 0x7e, 0x30, 0x7a, - 0x71, 0x2f, 0x12, 0x7e, 0x30, 0x7e, 0x17, 0x01, 0x77, 0x7e, 0x27, 0x01, 0x87, 0x2d, 0x23, 0x7e, - 0x09, 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x29, 0xcc, 0x38, 0x0f, 0x1b, - 0x34, 0x78, 0xec, 0x7a, 0x17, 0x01, 0x77, 0x7a, 0x27, 0x01, 0x87, 0x02, 0x61, 0x0b, 0x7e, 0x14, - 0x25, 0xcd, 0x80, 0xeb, 0xca, 0xb8, 0xc0, 0xf1, 0x75, 0x2f, 0x02, 0x12, 0x7e, 0x30, 0xe5, 0xc0, - 0x54, 0x03, 0x68, 0x05, 0x12, 0x77, 0xdd, 0x80, 0xf5, 0x30, 0xc2, 0x08, 0x75, 0xf1, 0x01, 0x12, - 0x71, 0xe9, 0x80, 0x14, 0x30, 0xc3, 0x08, 0x75, 0xf1, 0x01, 0x12, 0x71, 0x0d, 0x80, 0x09, 0x30, - 0xc4, 0x06, 0x75, 0xf1, 0x02, 0x12, 0x72, 0xf9, 0xd0, 0xf1, 0xda, 0xb8, 0x32, 0x75, 0x2f, 0x10, - 0x12, 0x7e, 0x30, 0xca, 0x0b, 0xca, 0x39, 0xca, 0x59, 0xc2, 0xc3, 0xa9, 0x21, 0xe2, 0x5c, 0xe5, - 0xe5, 0x54, 0xc0, 0x68, 0x4f, 0xe5, 0xe6, 0x6c, 0xaa, 0x7e, 0x37, 0x01, 0xc5, 0x2d, 0x35, 0xbe, - 0x34, 0x04, 0x00, 0x38, 0x4a, 0x7a, 0x37, 0x01, 0xc5, 0x7e, 0x37, 0x01, 0xc3, 0x7d, 0x43, 0x2d, - 0x45, 0xbe, 0x44, 0x09, 0xcc, 0x38, 0x40, 0x7a, 0x47, 0x01, 0xc3, 0x75, 0x2f, 0x11, 0x12, 0x7e, - 0x30, 0x7a, 0xb1, 0x2f, 0x12, 0x7e, 0x30, 0x12, 0x73, 0xc8, 0xa9, 0x21, 0xe5, 0x1f, 0xa9, 0xd4, + 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, + 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, + 0x38, 0x00, 0x0b, 0x35, 0x7e, 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x7e, + 0x19, 0x00, 0x7e, 0x19, 0x10, 0x1b, 0x38, 0x00, 0x0b, 0x35, 0x22, 0x67, 0x96, 0x69, 0x63, 0x69, + 0x7b, 0x6a, 0x49, 0x6a, 0xe4, 0x6b, 0x8e, 0x6b, 0xa9, 0x6c, 0x3b, 0x6b, 0xc4, 0x6c, 0x05, 0x69, + 0x96, 0x69, 0xaa, 0x7c, 0xb3, 0xbe, 0xb0, 0x0b, 0x28, 0x14, 0x75, 0x31, 0x09, 0x12, 0x7c, 0x15, + 0x75, 0x6d, 0x10, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x43, 0xe1, 0xc0, 0xd0, 0xf1, 0x22, 0xc0, 0xa8, + 0xc2, 0xaf, 0x23, 0x6c, 0xaa, 0x2e, 0x54, 0x67, 0x4b, 0x0b, 0x58, 0x50, 0x89, 0x54, 0x01, 0x02, + 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x75, 0x31, 0xb0, 0x12, 0x7c, 0x15, 0x0a, 0x32, 0x09, 0xb3, + 0x67, 0x8e, 0x42, 0x32, 0x19, 0x43, 0x00, 0x36, 0xd0, 0xa8, 0x22, 0x7c, 0xb2, 0x23, 0x0a, 0x3b, + 0x49, 0x33, 0x67, 0xcb, 0x0a, 0x22, 0x09, 0x32, 0x00, 0x36, 0x09, 0xb2, 0x67, 0x8e, 0xa5, 0xbb, + 0x00, 0x05, 0xf4, 0x52, 0x33, 0x80, 0x02, 0x42, 0x33, 0x89, 0x34, 0x67, 0xdb, 0x68, 0x09, 0x68, + 0x37, 0x68, 0x65, 0x68, 0x93, 0x68, 0xc1, 0x68, 0xef, 0x69, 0x1d, 0x12, 0x41, 0xb5, 0xd2, 0x28, + 0xd2, 0x08, 0xc2, 0x40, 0xc2, 0x48, 0xc2, 0x38, 0xc2, 0x30, 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xcf, + 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, 0x01, 0xaf, 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, 0x01, 0xbf, + 0x12, 0x41, 0x16, 0x12, 0x60, 0xcf, 0x02, 0x69, 0x4b, 0x12, 0x41, 0xd4, 0xd2, 0x29, 0xd2, 0x09, + 0xc2, 0x41, 0xc2, 0x49, 0xc2, 0x39, 0xc2, 0x31, 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xd0, 0x7e, 0x04, + 0x00, 0x20, 0x7a, 0x07, 0x01, 0xb1, 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, 0x01, 0xc1, 0x12, 0x41, + 0x16, 0x12, 0x61, 0x05, 0x02, 0x69, 0x4b, 0x12, 0x41, 0xf3, 0xd2, 0x2a, 0xd2, 0x0a, 0xc2, 0x42, + 0xc2, 0x4a, 0xc2, 0x3a, 0xc2, 0x32, 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xd1, 0x7e, 0x04, 0x00, 0x20, + 0x7a, 0x07, 0x01, 0xb3, 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, 0x01, 0xc3, 0x12, 0x41, 0x16, 0x12, + 0x61, 0x3b, 0x02, 0x69, 0x4b, 0x12, 0x42, 0x12, 0xd2, 0x2b, 0xd2, 0x0b, 0xc2, 0x43, 0xc2, 0x4b, + 0xc2, 0x3b, 0xc2, 0x33, 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xd2, 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, + 0x01, 0xb5, 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, 0x01, 0xc5, 0x12, 0x41, 0x16, 0x12, 0x61, 0x71, + 0x02, 0x69, 0x4b, 0x12, 0x42, 0x31, 0xd2, 0x2c, 0xd2, 0x0c, 0xc2, 0x44, 0xc2, 0x4c, 0xc2, 0x3c, + 0xc2, 0x34, 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xd3, 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, 0x01, 0xb7, + 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, 0x01, 0xc7, 0x12, 0x41, 0x16, 0x12, 0x61, 0xa7, 0x02, 0x69, + 0x4b, 0x12, 0x42, 0x50, 0xd2, 0x2d, 0xd2, 0x0d, 0xc2, 0x45, 0xc2, 0x4d, 0xc2, 0x3d, 0xc2, 0x35, + 0x6d, 0x00, 0x7a, 0x03, 0x01, 0xd4, 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, 0x01, 0xb9, 0x7e, 0x04, + 0x00, 0x38, 0x7a, 0x07, 0x01, 0xc9, 0x12, 0x41, 0x16, 0x12, 0x61, 0xdd, 0x02, 0x69, 0x4b, 0x12, + 0x42, 0x6f, 0xd2, 0x2e, 0xd2, 0x0e, 0xc2, 0x46, 0xc2, 0x4e, 0xc2, 0x3e, 0xc2, 0x36, 0x6d, 0x00, + 0x7a, 0x03, 0x01, 0xd5, 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, 0x01, 0xbb, 0x7e, 0x04, 0x00, 0x38, + 0x7a, 0x07, 0x01, 0xcb, 0x12, 0x41, 0x16, 0x12, 0x62, 0x13, 0x02, 0x69, 0x4b, 0x12, 0x42, 0x8e, + 0xd2, 0x2f, 0xd2, 0x0f, 0xc2, 0x47, 0xc2, 0x4f, 0xc2, 0x3f, 0xc2, 0x37, 0x6d, 0x00, 0x7a, 0x03, + 0x01, 0xd6, 0x7e, 0x04, 0x00, 0x20, 0x7a, 0x07, 0x01, 0xbd, 0x7e, 0x04, 0x00, 0x38, 0x7a, 0x07, + 0x01, 0xcd, 0x12, 0x41, 0x16, 0x12, 0x62, 0x49, 0x02, 0x69, 0x4b, 0x7e, 0xa0, 0xd0, 0x7e, 0x60, + 0x0f, 0x12, 0x62, 0xb6, 0x40, 0x0c, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, + 0xc2, 0xd7, 0x22, 0x75, 0x31, 0xb1, 0x12, 0x7c, 0x15, 0x0a, 0x52, 0x23, 0x6d, 0x00, 0x59, 0x05, + 0x00, 0x48, 0x12, 0x41, 0x7e, 0x12, 0x41, 0x9a, 0xd0, 0xa8, 0x22, 0x75, 0x31, 0xb2, 0x12, 0x7c, + 0x15, 0x0a, 0x22, 0x09, 0xb2, 0x67, 0x8e, 0x42, 0x23, 0x7e, 0xb0, 0x9c, 0x19, 0xb2, 0x01, 0xcf, + 0x12, 0x31, 0x85, 0xd0, 0xa8, 0x22, 0x75, 0x31, 0xb7, 0x12, 0x7c, 0x15, 0x0a, 0x22, 0x09, 0xb2, + 0x67, 0x8e, 0x42, 0x24, 0x12, 0x35, 0x3d, 0xd0, 0xa8, 0x22, 0x75, 0x31, 0xb9, 0x12, 0x7c, 0x15, + 0x0a, 0x32, 0x09, 0xb3, 0x67, 0x8e, 0x42, 0x34, 0x19, 0x43, 0x00, 0x3e, 0x12, 0x69, 0xc2, 0xd0, + 0xa8, 0x22, 0x7c, 0xb2, 0x23, 0x0a, 0x0b, 0x7c, 0xb4, 0x20, 0xe0, 0x04, 0x6d, 0x33, 0x80, 0x04, + 0x49, 0x30, 0x01, 0x8f, 0x7e, 0xa0, 0xd8, 0xa5, 0xef, 0xca, 0x0b, 0xca, 0x29, 0x12, 0x62, 0xb6, + 0xda, 0x29, 0xda, 0x0b, 0x40, 0x62, 0x75, 0x31, 0xba, 0x12, 0x7c, 0x15, 0x7c, 0xb4, 0x30, 0xe0, + 0x1e, 0x6d, 0x33, 0x59, 0x30, 0x01, 0x8f, 0x7e, 0x34, 0x09, 0xe3, 0x0a, 0x82, 0x7e, 0x94, 0x04, + 0x00, 0xad, 0x89, 0x2d, 0x39, 0x59, 0x30, 0x01, 0x6f, 0x59, 0x30, 0x01, 0x7f, 0x7c, 0xb4, 0x30, + 0xe1, 0x10, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0x09, 0xb0, 0x00, 0x08, 0x44, 0x04, 0x19, 0xb0, + 0x00, 0x08, 0x0a, 0x02, 0x09, 0xb0, 0x67, 0x8e, 0x42, 0x21, 0xf4, 0x52, 0x34, 0x7c, 0xb2, 0x23, + 0x0a, 0x0b, 0xca, 0x19, 0x49, 0x00, 0x30, 0xd5, 0x99, 0x04, 0xda, 0x19, 0xc0, 0xf1, 0x75, 0xf1, + 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0xc2, 0xd7, 0x22, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0x09, + 0x30, 0x00, 0x0c, 0x74, 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0x09, 0xb0, 0x00, 0x08, 0x19, 0x30, 0x00, + 0x0c, 0x7c, 0x74, 0x5e, 0x70, 0x01, 0x68, 0x12, 0x44, 0x40, 0xca, 0xb8, 0x09, 0xb0, 0x00, 0x10, + 0x44, 0x02, 0x19, 0xb0, 0x00, 0x10, 0xda, 0xb8, 0x80, 0x02, 0x54, 0xbf, 0x7c, 0x74, 0x5e, 0x70, + 0x08, 0x68, 0x04, 0x44, 0x08, 0x80, 0x02, 0x54, 0xf7, 0x09, 0x30, 0x00, 0x0c, 0xca, 0xb8, 0x74, + 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0xda, 0xb8, 0x19, 0xb0, 0x00, 0x08, 0x19, 0x30, 0x00, 0x0c, 0x0a, + 0x62, 0x09, 0xb6, 0x67, 0x8e, 0x3e, 0x20, 0x0a, 0x62, 0x7c, 0x74, 0x5e, 0x70, 0x02, 0x68, 0x20, + 0x42, 0x27, 0xca, 0xb8, 0x74, 0x61, 0x19, 0xb0, 0x00, 0x08, 0x7e, 0x44, 0x00, 0x10, 0x59, 0x46, + 0x01, 0xbf, 0x09, 0xb0, 0x00, 0x10, 0x44, 0x01, 0x19, 0xb0, 0x00, 0x10, 0xda, 0xb8, 0x80, 0x11, + 0xf4, 0x52, 0x27, 0x74, 0xa1, 0x19, 0xb0, 0x00, 0x08, 0x7e, 0x44, 0x00, 0x38, 0x59, 0x46, 0x01, + 0xbf, 0xd0, 0xa8, 0x22, 0x7c, 0x74, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0x0a, 0x62, 0x09, 0xb6, + 0x67, 0x8e, 0xa5, 0xfd, 0xf4, 0xa5, 0xfe, 0xca, 0x28, 0x3e, 0x20, 0x0a, 0x62, 0xa5, 0xee, 0x52, + 0x26, 0x7e, 0x44, 0x00, 0x20, 0x59, 0x46, 0x01, 0xaf, 0xda, 0x28, 0x09, 0x30, 0x00, 0x0c, 0x74, + 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0x09, 0xb0, 0x00, 0x08, 0x7c, 0x74, 0x5e, 0x70, 0x01, 0x68, 0x0c, + 0x44, 0x80, 0xca, 0xb8, 0xa5, 0xed, 0x42, 0x2a, 0xda, 0xb8, 0x80, 0x0a, 0x54, 0x7f, 0xca, 0xb8, + 0xa5, 0xee, 0x52, 0x2a, 0xda, 0xb8, 0x7c, 0x74, 0x5e, 0x70, 0x08, 0x68, 0x04, 0x44, 0x02, 0x80, + 0x02, 0x54, 0xfd, 0x19, 0xb0, 0x00, 0x08, 0x19, 0x30, 0x00, 0x0c, 0x7c, 0x74, 0x5e, 0x70, 0x02, + 0x68, 0x10, 0xa5, 0xed, 0x42, 0x28, 0x42, 0x26, 0x7e, 0x44, 0x00, 0x08, 0x59, 0x46, 0x01, 0xaf, + 0x80, 0x04, 0xa5, 0xee, 0x52, 0x28, 0x7c, 0x74, 0x5e, 0x70, 0x04, 0x68, 0x10, 0xa5, 0xed, 0x42, + 0x29, 0x42, 0x26, 0x7e, 0x44, 0x00, 0x08, 0x59, 0x46, 0x01, 0xaf, 0x80, 0x0b, 0xa5, 0xee, 0x52, + 0x29, 0x7c, 0x74, 0x5e, 0x70, 0x02, 0x78, 0x00, 0x12, 0x42, 0xad, 0xd0, 0xa8, 0x22, 0x7e, 0x04, + 0x80, 0x00, 0x4c, 0x02, 0x09, 0x30, 0x00, 0x0c, 0x74, 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0x19, 0x40, + 0x00, 0x10, 0x19, 0x30, 0x00, 0x0c, 0xd0, 0xa8, 0x22, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, 0x09, + 0x30, 0x00, 0x0c, 0x74, 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0x19, 0x40, 0x00, 0x18, 0x19, 0x30, 0x00, + 0x0c, 0xd0, 0xa8, 0x22, 0x75, 0x31, 0xb5, 0x12, 0x7c, 0x15, 0x7e, 0x04, 0x80, 0x00, 0x4c, 0x02, + 0x09, 0xb0, 0x00, 0x0c, 0x44, 0x40, 0x19, 0xb0, 0x00, 0x0c, 0xe5, 0x6e, 0xb4, 0x07, 0x23, 0x09, + 0xb0, 0x00, 0x10, 0x4e, 0xb0, 0x02, 0x19, 0xb0, 0x00, 0x10, 0x09, 0x30, 0x00, 0x0c, 0x74, 0xbf, + 0x19, 0xb0, 0x00, 0x0c, 0x09, 0xb0, 0x00, 0x04, 0x54, 0xf7, 0x19, 0xb0, 0x00, 0x04, 0x19, 0x30, + 0x00, 0x0c, 0xd0, 0xa8, 0x22, 0x75, 0x31, 0xb6, 0x12, 0x7c, 0x15, 0x7e, 0x04, 0x80, 0x00, 0x4c, + 0x02, 0xe5, 0x6e, 0xb4, 0x07, 0x18, 0x09, 0x30, 0x00, 0x0c, 0x74, 0xbf, 0x19, 0xb0, 0x00, 0x0c, + 0x09, 0xb0, 0x00, 0x04, 0x44, 0x08, 0x19, 0xb0, 0x00, 0x04, 0x19, 0x30, 0x00, 0x0c, 0x09, 0xb0, + 0x00, 0x0c, 0x54, 0xbf, 0x19, 0xb0, 0x00, 0x0c, 0xd0, 0xa8, 0x22, 0x75, 0x31, 0xb4, 0x12, 0x7c, + 0x15, 0x7a, 0x21, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x41, 0x31, 0x12, 0x7c, 0x15, 0x0a, 0x32, 0x09, + 0xb3, 0x67, 0x8e, 0x42, 0x35, 0x12, 0x6c, 0x5b, 0xd0, 0xa8, 0x22, 0x7e, 0xb0, 0x01, 0x7e, 0xa0, + 0xc8, 0x7c, 0x64, 0x12, 0x62, 0xb6, 0x40, 0x13, 0x0a, 0x32, 0x09, 0xb3, 0x67, 0x8e, 0xf4, 0x52, + 0x35, 0xc0, 0xf1, 0x75, 0xf1, 0x01, 0x12, 0x6f, 0xd9, 0xd0, 0xf1, 0x22, 0x6c, 0x8c, 0x6c, 0xd3, + 0x6d, 0x1a, 0x6d, 0x61, 0x6d, 0xa8, 0x6d, 0xef, 0x6e, 0x36, 0x6e, 0x7d, 0x75, 0x31, 0x55, 0x12, + 0x7c, 0x15, 0x75, 0x31, 0x00, 0x12, 0x7c, 0x15, 0x7a, 0x61, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x71, + 0x31, 0x12, 0x7c, 0x15, 0x7e, 0x17, 0x01, 0x7f, 0x7e, 0x27, 0x01, 0x8f, 0x2d, 0x23, 0x7e, 0x09, + 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x0d, 0xe2, 0x38, 0x0f, 0x1b, 0x34, + 0x78, 0xec, 0x7a, 0x17, 0x01, 0x7f, 0x7a, 0x27, 0x01, 0x8f, 0x02, 0x46, 0x0f, 0x7e, 0x14, 0x09, + 0xe3, 0x80, 0xeb, 0x75, 0x31, 0x55, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x01, 0x12, 0x7c, 0x15, 0x7a, + 0x61, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, 0x7c, 0x15, 0x7e, 0x17, 0x01, 0x81, 0x7e, + 0x27, 0x01, 0x91, 0x2d, 0x23, 0x7e, 0x09, 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, + 0x14, 0x11, 0xe2, 0x38, 0x0f, 0x1b, 0x34, 0x78, 0xec, 0x7a, 0x17, 0x01, 0x81, 0x7a, 0x27, 0x01, + 0x91, 0x02, 0x49, 0x67, 0x7e, 0x14, 0x0d, 0xe3, 0x80, 0xeb, 0x75, 0x31, 0x55, 0x12, 0x7c, 0x15, + 0x75, 0x31, 0x02, 0x12, 0x7c, 0x15, 0x7a, 0x61, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, + 0x7c, 0x15, 0x7e, 0x17, 0x01, 0x83, 0x7e, 0x27, 0x01, 0x93, 0x2d, 0x23, 0x7e, 0x09, 0xb0, 0x0b, + 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x15, 0xe2, 0x38, 0x0f, 0x1b, 0x34, 0x78, 0xec, + 0x7a, 0x17, 0x01, 0x83, 0x7a, 0x27, 0x01, 0x93, 0x02, 0x4c, 0xbf, 0x7e, 0x14, 0x11, 0xe3, 0x80, + 0xeb, 0x75, 0x31, 0x55, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x03, 0x12, 0x7c, 0x15, 0x7a, 0x61, 0x31, + 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, 0x7c, 0x15, 0x7e, 0x17, 0x01, 0x85, 0x7e, 0x27, 0x01, + 0x95, 0x2d, 0x23, 0x7e, 0x09, 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x19, + 0xe2, 0x38, 0x0f, 0x1b, 0x34, 0x78, 0xec, 0x7a, 0x17, 0x01, 0x85, 0x7a, 0x27, 0x01, 0x95, 0x02, + 0x50, 0x17, 0x7e, 0x14, 0x15, 0xe3, 0x80, 0xeb, 0x75, 0x31, 0x55, 0x12, 0x7c, 0x15, 0x75, 0x31, + 0x04, 0x12, 0x7c, 0x15, 0x7a, 0x61, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, 0x7c, 0x15, + 0x7e, 0x17, 0x01, 0x87, 0x7e, 0x27, 0x01, 0x97, 0x2d, 0x23, 0x7e, 0x09, 0xb0, 0x0b, 0x04, 0x7a, + 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x1d, 0xe2, 0x38, 0x0f, 0x1b, 0x34, 0x78, 0xec, 0x7a, 0x17, + 0x01, 0x87, 0x7a, 0x27, 0x01, 0x97, 0x02, 0x53, 0x6f, 0x7e, 0x14, 0x19, 0xe3, 0x80, 0xeb, 0x75, + 0x31, 0x55, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x05, 0x12, 0x7c, 0x15, 0x7a, 0x61, 0x31, 0x12, 0x7c, + 0x15, 0x7a, 0x71, 0x31, 0x12, 0x7c, 0x15, 0x7e, 0x17, 0x01, 0x89, 0x7e, 0x27, 0x01, 0x99, 0x2d, + 0x23, 0x7e, 0x09, 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x21, 0xe2, 0x38, + 0x0f, 0x1b, 0x34, 0x78, 0xec, 0x7a, 0x17, 0x01, 0x89, 0x7a, 0x27, 0x01, 0x99, 0x02, 0x56, 0xc7, + 0x7e, 0x14, 0x1d, 0xe3, 0x80, 0xeb, 0x75, 0x31, 0x55, 0x12, 0x7c, 0x15, 0x75, 0x31, 0x06, 0x12, + 0x7c, 0x15, 0x7a, 0x61, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x71, 0x31, 0x12, 0x7c, 0x15, 0x7e, 0x17, + 0x01, 0x8b, 0x7e, 0x27, 0x01, 0x9b, 0x2d, 0x23, 0x7e, 0x09, 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, + 0x0b, 0x14, 0xbe, 0x14, 0x25, 0xe2, 0x38, 0x0f, 0x1b, 0x34, 0x78, 0xec, 0x7a, 0x17, 0x01, 0x8b, + 0x7a, 0x27, 0x01, 0x9b, 0x02, 0x5a, 0x1f, 0x7e, 0x14, 0x21, 0xe3, 0x80, 0xeb, 0x75, 0x31, 0x55, + 0x12, 0x7c, 0x15, 0x75, 0x31, 0x07, 0x12, 0x7c, 0x15, 0x7a, 0x61, 0x31, 0x12, 0x7c, 0x15, 0x7a, + 0x71, 0x31, 0x12, 0x7c, 0x15, 0x7e, 0x17, 0x01, 0x8d, 0x7e, 0x27, 0x01, 0x9d, 0x2d, 0x23, 0x7e, + 0x09, 0xb0, 0x0b, 0x04, 0x7a, 0x19, 0xb0, 0x0b, 0x14, 0xbe, 0x14, 0x29, 0xe2, 0x38, 0x0f, 0x1b, + 0x34, 0x78, 0xec, 0x7a, 0x17, 0x01, 0x8d, 0x7a, 0x27, 0x01, 0x9d, 0x02, 0x5d, 0x77, 0x7e, 0x14, + 0x25, 0xe3, 0x80, 0xeb, 0xca, 0xb8, 0xc0, 0xf1, 0x75, 0x31, 0x02, 0x12, 0x7c, 0x15, 0xe5, 0xc0, + 0x54, 0x03, 0x68, 0x05, 0x12, 0x75, 0xcd, 0x80, 0xf5, 0x30, 0xc2, 0x08, 0x75, 0xf1, 0x01, 0x12, + 0x6f, 0xd9, 0x80, 0x14, 0x30, 0xc3, 0x08, 0x75, 0xf1, 0x01, 0x12, 0x6e, 0xfd, 0x80, 0x09, 0x30, + 0xc4, 0x06, 0x75, 0xf1, 0x02, 0x12, 0x70, 0xe9, 0xd0, 0xf1, 0xda, 0xb8, 0x32, 0x75, 0x31, 0x10, + 0x12, 0x7c, 0x15, 0xca, 0x0b, 0xca, 0x39, 0xca, 0x59, 0xc2, 0xc3, 0xa9, 0x21, 0xe2, 0x5c, 0xe5, + 0xe5, 0x54, 0xc0, 0x68, 0x4f, 0xe5, 0xe6, 0x6c, 0xaa, 0x7e, 0x37, 0x01, 0xdb, 0x2d, 0x35, 0xbe, + 0x34, 0x04, 0x00, 0x38, 0x4a, 0x7a, 0x37, 0x01, 0xdb, 0x7e, 0x37, 0x01, 0xd9, 0x7d, 0x43, 0x2d, + 0x45, 0xbe, 0x44, 0x09, 0xe2, 0x38, 0x40, 0x7a, 0x47, 0x01, 0xd9, 0x75, 0x31, 0x11, 0x12, 0x7c, + 0x15, 0x7a, 0xb1, 0x31, 0x12, 0x7c, 0x15, 0x12, 0x71, 0xb8, 0xa9, 0x21, 0xe5, 0x1f, 0xa9, 0xd4, 0xe4, 0xa9, 0x24, 0xe4, 0xfc, 0xc2, 0xc3, 0xa9, 0x21, 0xe2, 0x3b, 0xe5, 0xe5, 0x54, 0xc0, 0x78, - 0xb4, 0x12, 0x76, 0x6a, 0xda, 0x59, 0xda, 0x39, 0xda, 0x0b, 0x22, 0x80, 0x29, 0x80, 0x58, 0x75, - 0x2f, 0x16, 0x12, 0x7e, 0x30, 0x80, 0xed, 0x75, 0x2f, 0x12, 0x12, 0x7e, 0x30, 0x7a, 0xb1, 0x2f, - 0x12, 0x7e, 0x30, 0x9e, 0x44, 0x09, 0xcd, 0x9d, 0x54, 0x12, 0x73, 0xc8, 0x7e, 0x34, 0x05, 0xcd, - 0x7d, 0x54, 0x2d, 0x43, 0x80, 0xa1, 0xe5, 0xe5, 0x54, 0x03, 0x78, 0x12, 0x75, 0x2f, 0x13, 0x12, - 0x7e, 0x30, 0x7e, 0x0f, 0x29, 0xe9, 0x0b, 0x0c, 0x7a, 0x0f, 0x29, 0xe9, 0x80, 0xa7, 0x75, 0x2f, - 0x14, 0x12, 0x7e, 0x30, 0x7e, 0x0f, 0x29, 0xed, 0x0b, 0x0c, 0x7a, 0x0f, 0x29, 0xed, 0xa9, 0xd7, - 0xe4, 0xa9, 0x27, 0xe4, 0xfc, 0x80, 0x9d, 0x75, 0x2f, 0x15, 0x12, 0x7e, 0x30, 0x7e, 0x0f, 0x29, - 0xf1, 0x0b, 0x0c, 0x7a, 0x0f, 0x29, 0xf1, 0x80, 0xe5, 0x75, 0x2f, 0x18, 0x12, 0x7e, 0x30, 0xca, + 0xb4, 0x12, 0x74, 0x5a, 0xda, 0x59, 0xda, 0x39, 0xda, 0x0b, 0x22, 0x80, 0x29, 0x80, 0x58, 0x75, + 0x31, 0x16, 0x12, 0x7c, 0x15, 0x80, 0xed, 0x75, 0x31, 0x12, 0x12, 0x7c, 0x15, 0x7a, 0xb1, 0x31, + 0x12, 0x7c, 0x15, 0x9e, 0x44, 0x09, 0xe3, 0x9d, 0x54, 0x12, 0x71, 0xb8, 0x7e, 0x34, 0x05, 0xe3, + 0x7d, 0x54, 0x2d, 0x43, 0x80, 0xa1, 0xe5, 0xe5, 0x54, 0x03, 0x78, 0x12, 0x75, 0x31, 0x13, 0x12, + 0x7c, 0x15, 0x7e, 0x0f, 0x29, 0xff, 0x0b, 0x0c, 0x7a, 0x0f, 0x29, 0xff, 0x80, 0xa7, 0x75, 0x31, + 0x14, 0x12, 0x7c, 0x15, 0x7e, 0x0f, 0x2a, 0x03, 0x0b, 0x0c, 0x7a, 0x0f, 0x2a, 0x03, 0xa9, 0xd7, + 0xe4, 0xa9, 0x27, 0xe4, 0xfc, 0x80, 0x9d, 0x75, 0x31, 0x15, 0x12, 0x7c, 0x15, 0x7e, 0x0f, 0x2a, + 0x07, 0x0b, 0x0c, 0x7a, 0x0f, 0x2a, 0x07, 0x80, 0xe5, 0x75, 0x31, 0x18, 0x12, 0x7c, 0x15, 0xca, 0x09, 0xca, 0x39, 0xca, 0x2b, 0xc2, 0xc2, 0xa9, 0x21, 0xf2, 0x52, 0xe5, 0xf5, 0x33, 0x82, 0xe7, - 0x40, 0x44, 0x7e, 0x37, 0x01, 0xcb, 0x7e, 0x54, 0x00, 0x40, 0x9d, 0x35, 0x40, 0x43, 0x7a, 0x37, - 0x01, 0xcb, 0x7e, 0x37, 0x01, 0xc7, 0x7d, 0x43, 0x2d, 0x45, 0xbe, 0x44, 0x05, 0xcc, 0x38, 0x52, - 0x7a, 0x47, 0x01, 0xc7, 0x7d, 0x45, 0x12, 0x75, 0x26, 0xa9, 0x20, 0xf5, 0x22, 0x75, 0x2f, 0x19, - 0x12, 0x7e, 0x30, 0x7a, 0x91, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x81, 0xf7, 0x7a, 0x91, 0xf6, 0xe5, + 0x40, 0x44, 0x7e, 0x37, 0x01, 0xe1, 0x7e, 0x54, 0x00, 0x40, 0x9d, 0x35, 0x40, 0x43, 0x7a, 0x37, + 0x01, 0xe1, 0x7e, 0x37, 0x01, 0xdd, 0x7d, 0x43, 0x2d, 0x45, 0xbe, 0x44, 0x05, 0xe2, 0x38, 0x52, + 0x7a, 0x47, 0x01, 0xdd, 0x7d, 0x45, 0x12, 0x73, 0x16, 0xa9, 0x20, 0xf5, 0x22, 0x75, 0x31, 0x19, + 0x12, 0x7c, 0x15, 0x7a, 0x91, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x81, 0xf7, 0x7a, 0x91, 0xf6, 0xe5, 0xf5, 0x33, 0x82, 0xe7, 0x50, 0xbc, 0xda, 0x2b, 0xda, 0x39, 0xda, 0x09, 0x22, 0x80, 0x41, 0x80, - 0x64, 0x2d, 0x53, 0x6d, 0x33, 0x70, 0xb7, 0x7e, 0x04, 0x01, 0xcd, 0x7a, 0x07, 0x01, 0xc9, 0x7a, - 0x07, 0x01, 0xc7, 0xa9, 0x32, 0xf2, 0xdf, 0x85, 0x30, 0x2f, 0x12, 0x7e, 0x30, 0x75, 0xf6, 0x00, - 0x80, 0xd4, 0xca, 0x59, 0x9e, 0x44, 0x05, 0xcd, 0x9d, 0x54, 0x12, 0x75, 0x26, 0x7e, 0x34, 0x01, - 0xcd, 0x7d, 0x54, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xc7, 0x12, 0x75, 0x26, 0xda, 0x49, 0x80, 0x99, - 0xe5, 0xf5, 0x54, 0x03, 0x78, 0x1f, 0x7e, 0x0f, 0x29, 0xd9, 0x0b, 0x0c, 0x7a, 0x0f, 0x29, 0xd9, - 0x80, 0x9d, 0x7e, 0x0f, 0x29, 0xe1, 0x0b, 0x0c, 0x7a, 0x0f, 0x29, 0xe1, 0xa9, 0xd7, 0xf4, 0xa9, - 0x27, 0xf4, 0xfc, 0x80, 0x8a, 0x7e, 0x0f, 0x29, 0xdd, 0x0b, 0x0c, 0x7a, 0x0f, 0x29, 0xdd, 0x80, - 0xeb, 0xe5, 0xf5, 0x54, 0x03, 0x78, 0x1f, 0x7e, 0x2f, 0x29, 0xf9, 0x0b, 0x2c, 0x7a, 0x2f, 0x29, - 0xf9, 0x80, 0x34, 0x7e, 0x2f, 0x2a, 0x01, 0x0b, 0x2c, 0x7a, 0x2f, 0x2a, 0x01, 0xa9, 0xd7, 0xf4, - 0xa9, 0x27, 0xf4, 0xfc, 0x80, 0x21, 0x7e, 0x2f, 0x29, 0xfd, 0x0b, 0x2c, 0x7a, 0x2f, 0x29, 0xfd, - 0x80, 0xeb, 0xda, 0x2b, 0xda, 0x1b, 0xda, 0x0b, 0x22, 0x75, 0x2f, 0x28, 0x12, 0x7e, 0x30, 0xca, + 0x64, 0x2d, 0x53, 0x6d, 0x33, 0x70, 0xb7, 0x7e, 0x04, 0x01, 0xe3, 0x7a, 0x07, 0x01, 0xdf, 0x7a, + 0x07, 0x01, 0xdd, 0xa9, 0x32, 0xf2, 0xdf, 0x85, 0x30, 0x31, 0x12, 0x7c, 0x15, 0x75, 0xf6, 0x00, + 0x80, 0xd4, 0xca, 0x59, 0x9e, 0x44, 0x05, 0xe3, 0x9d, 0x54, 0x12, 0x73, 0x16, 0x7e, 0x34, 0x01, + 0xe3, 0x7d, 0x54, 0x2d, 0x43, 0x7a, 0x47, 0x01, 0xdd, 0x12, 0x73, 0x16, 0xda, 0x49, 0x80, 0x99, + 0xe5, 0xf5, 0x54, 0x03, 0x78, 0x1f, 0x7e, 0x0f, 0x29, 0xef, 0x0b, 0x0c, 0x7a, 0x0f, 0x29, 0xef, + 0x80, 0x9d, 0x7e, 0x0f, 0x29, 0xf7, 0x0b, 0x0c, 0x7a, 0x0f, 0x29, 0xf7, 0xa9, 0xd7, 0xf4, 0xa9, + 0x27, 0xf4, 0xfc, 0x80, 0x8a, 0x7e, 0x0f, 0x29, 0xf3, 0x0b, 0x0c, 0x7a, 0x0f, 0x29, 0xf3, 0x80, + 0xeb, 0xe5, 0xf5, 0x54, 0x03, 0x78, 0x1f, 0x7e, 0x2f, 0x2a, 0x0f, 0x0b, 0x2c, 0x7a, 0x2f, 0x2a, + 0x0f, 0x80, 0x34, 0x7e, 0x2f, 0x2a, 0x17, 0x0b, 0x2c, 0x7a, 0x2f, 0x2a, 0x17, 0xa9, 0xd7, 0xf4, + 0xa9, 0x27, 0xf4, 0xfc, 0x80, 0x21, 0x7e, 0x2f, 0x2a, 0x13, 0x0b, 0x2c, 0x7a, 0x2f, 0x2a, 0x13, + 0x80, 0xeb, 0xda, 0x2b, 0xda, 0x1b, 0xda, 0x0b, 0x22, 0x75, 0x31, 0x28, 0x12, 0x7c, 0x15, 0xca, 0x0b, 0xca, 0x1b, 0xca, 0x2b, 0xc2, 0xc4, 0xa9, 0x21, 0xf2, 0xb6, 0xe5, 0xf5, 0x33, 0x72, 0xe7, - 0x40, 0xe0, 0x7e, 0x0d, 0x30, 0x7e, 0x1d, 0x34, 0x7e, 0x2d, 0x38, 0x7e, 0x3d, 0x3c, 0x7e, 0x85, - 0x40, 0x7d, 0x90, 0x4d, 0x91, 0x4d, 0x92, 0x4d, 0x93, 0x4d, 0x94, 0x4d, 0x95, 0x4d, 0x96, 0x4d, + 0x40, 0xe0, 0x7e, 0x0d, 0x46, 0x7e, 0x1d, 0x4a, 0x7e, 0x2d, 0x4e, 0x7e, 0x3d, 0x52, 0x7e, 0x85, + 0x56, 0x7d, 0x90, 0x4d, 0x91, 0x4d, 0x92, 0x4d, 0x93, 0x4d, 0x94, 0x4d, 0x95, 0x4d, 0x96, 0x4d, 0x97, 0x4d, 0x98, 0x68, 0x72, 0x7a, 0x11, 0xf3, 0x7a, 0x01, 0xf3, 0x7a, 0x31, 0xf3, 0x7a, 0x21, 0xf3, 0x7a, 0x51, 0xf3, 0x7a, 0x41, 0xf3, 0x7a, 0x71, 0xf3, 0x7a, 0x61, 0xf3, 0x7a, 0x91, 0xf3, - 0x7a, 0x81, 0xf3, 0x30, 0x73, 0x1a, 0x7a, 0xb1, 0xf3, 0x7a, 0xa1, 0xf3, 0x7a, 0xd1, 0xf3, 0x7a, + 0x7a, 0x81, 0xf3, 0x30, 0x7b, 0x1a, 0x7a, 0xb1, 0xf3, 0x7a, 0xa1, 0xf3, 0x7a, 0xd1, 0xf3, 0x7a, 0xc1, 0xf3, 0x7a, 0xf1, 0xf3, 0x7a, 0xe1, 0xf3, 0x7d, 0x78, 0x7a, 0xf1, 0xf3, 0x7a, 0xe1, 0xf3, - 0xa9, 0x30, 0xf5, 0x03, 0x02, 0x72, 0xe6, 0x75, 0x2f, 0x29, 0x12, 0x7e, 0x30, 0x20, 0x73, 0x0b, - 0x75, 0x2f, 0x0a, 0x12, 0x7e, 0x30, 0x75, 0xf6, 0x0a, 0x80, 0x09, 0x75, 0x2f, 0x12, 0x12, 0x7e, - 0x30, 0x75, 0xf6, 0x12, 0x6d, 0x00, 0x7d, 0x10, 0x7a, 0x0d, 0x30, 0x7a, 0x0d, 0x34, 0x7a, 0x0d, - 0x38, 0x7a, 0x0d, 0x3c, 0x7a, 0x05, 0x40, 0xda, 0x2b, 0xda, 0x1b, 0xda, 0x0b, 0x22, 0x1e, 0xb0, - 0x40, 0x0c, 0x7e, 0xa0, 0x0a, 0xa4, 0x7e, 0x04, 0x75, 0x0b, 0x9d, 0x05, 0x89, 0x04, 0x7e, 0xa1, + 0xa9, 0x30, 0xf5, 0x03, 0x02, 0x70, 0xd6, 0x75, 0x31, 0x29, 0x12, 0x7c, 0x15, 0x20, 0x7b, 0x0b, + 0x75, 0x31, 0x0a, 0x12, 0x7c, 0x15, 0x75, 0xf6, 0x0a, 0x80, 0x09, 0x75, 0x31, 0x12, 0x12, 0x7c, + 0x15, 0x75, 0xf6, 0x12, 0x6d, 0x00, 0x7d, 0x10, 0x7a, 0x0d, 0x46, 0x7a, 0x0d, 0x4a, 0x7a, 0x0d, + 0x4e, 0x7a, 0x0d, 0x52, 0x7a, 0x05, 0x56, 0xda, 0x2b, 0xda, 0x1b, 0xda, 0x0b, 0x22, 0x1e, 0xb0, + 0x40, 0x0c, 0x7e, 0xa0, 0x0a, 0xa4, 0x7e, 0x04, 0x72, 0xfb, 0x9d, 0x05, 0x89, 0x04, 0x7e, 0xa1, 0xe3, 0x7a, 0x39, 0xa0, 0x0b, 0x34, 0x80, 0xea, 0xb4, 0x40, 0xe3, 0x7e, 0xa1, 0xe3, 0xe5, 0xe3, 0x1b, 0x38, 0x50, 0x0b, 0x35, 0x7e, 0xa1, 0xe3, 0xe5, 0xe3, 0x1b, 0x38, 0x50, 0x0b, 0x35, 0x7e, 0xa1, 0xe3, 0xe5, 0xe3, 0x1b, 0x38, 0x50, 0x0b, 0x35, 0x7e, 0xa1, 0xe3, 0xe5, 0xe3, 0x1b, 0x38, @@ -937,7 +1044,7 @@ static unsigned char IMAGE_ARRAY_NAME[] 0x50, 0x0b, 0x35, 0x7e, 0xa1, 0xe3, 0xe5, 0xe3, 0x1b, 0x38, 0x50, 0x0b, 0x35, 0x7e, 0xa1, 0xe3, 0xe5, 0xe3, 0x1b, 0x38, 0x50, 0x0b, 0x35, 0x7e, 0xa1, 0xe3, 0xe5, 0xe3, 0x1b, 0x38, 0x50, 0x0b, 0x35, 0x7e, 0xa1, 0xe3, 0xe5, 0xe3, 0x1b, 0x38, 0x50, 0x0b, 0x35, 0x22, 0x1e, 0xb0, 0x40, 0x0c, - 0x7e, 0xa0, 0x0a, 0xa4, 0x7e, 0x04, 0x76, 0x69, 0x9d, 0x05, 0x89, 0x04, 0x7e, 0x39, 0xa0, 0x7a, + 0x7e, 0xa0, 0x0a, 0xa4, 0x7e, 0x04, 0x74, 0x59, 0x9d, 0x05, 0x89, 0x04, 0x7e, 0x39, 0xa0, 0x7a, 0xa1, 0xf3, 0x0b, 0x34, 0x80, 0xea, 0xb4, 0x40, 0xe3, 0x0b, 0x38, 0x50, 0x7a, 0xa1, 0xf3, 0xf5, 0xf3, 0x0b, 0x35, 0x0b, 0x38, 0x50, 0x7a, 0xa1, 0xf3, 0xf5, 0xf3, 0x0b, 0x35, 0x0b, 0x38, 0x50, 0x7a, 0xa1, 0xf3, 0xf5, 0xf3, 0x0b, 0x35, 0x0b, 0x38, 0x50, 0x7a, 0xa1, 0xf3, 0xf5, 0xf3, 0x0b, @@ -958,144 +1065,144 @@ static unsigned char IMAGE_ARRAY_NAME[] 0x7a, 0xa1, 0xf3, 0xf5, 0xf3, 0x0b, 0x35, 0x0b, 0x38, 0x50, 0x7a, 0xa1, 0xf3, 0xf5, 0xf3, 0x0b, 0x35, 0x0b, 0x38, 0x50, 0x7a, 0xa1, 0xf3, 0xf5, 0xf3, 0x0b, 0x35, 0x0b, 0x38, 0x50, 0x7a, 0xa1, 0xf3, 0xf5, 0xf3, 0x0b, 0x35, 0x0b, 0x38, 0x50, 0x7a, 0xa1, 0xf3, 0xf5, 0xf3, 0x0b, 0x35, 0x0b, - 0x38, 0x50, 0x7a, 0xa1, 0xf3, 0xf5, 0xf3, 0x0b, 0x35, 0x22, 0xc2, 0xaf, 0x7e, 0x37, 0x01, 0xc5, - 0x4d, 0x33, 0x68, 0x3b, 0x7e, 0x07, 0x01, 0xc1, 0x7e, 0x54, 0x09, 0xcd, 0x9d, 0x50, 0xbd, 0x35, - 0x40, 0x02, 0x7d, 0x35, 0xca, 0x39, 0x7e, 0x65, 0x4b, 0x99, 0x64, 0xda, 0x39, 0x7e, 0x07, 0x01, - 0xc5, 0x9d, 0x03, 0x7a, 0x07, 0x01, 0xc5, 0x2e, 0x37, 0x01, 0xc1, 0x7a, 0x37, 0x01, 0xc1, 0xbe, - 0x34, 0x09, 0xcc, 0x28, 0xc7, 0x7e, 0x34, 0x05, 0xcd, 0x7a, 0x37, 0x01, 0xc1, 0x80, 0xbd, 0xd2, - 0xaf, 0x22, 0x75, 0x2f, 0x53, 0x12, 0x7e, 0x30, 0x7e, 0x15, 0x4d, 0x80, 0x11, 0x75, 0x2f, 0x51, - 0x12, 0x7e, 0x30, 0x0b, 0x08, 0x10, 0x0b, 0x05, 0x9e, 0x34, 0x00, 0x02, 0x28, 0x4d, 0x7c, 0xb2, - 0x20, 0xe7, 0x27, 0x54, 0x07, 0x23, 0x0a, 0x2b, 0x49, 0x22, 0x6e, 0x8c, 0x7c, 0xb2, 0x54, 0x78, - 0x03, 0x03, 0x03, 0x7c, 0x2b, 0x9d, 0x13, 0x40, 0x1a, 0x68, 0x12, 0x7a, 0x15, 0x4d, 0x7a, 0x25, - 0x4f, 0x7e, 0x64, 0x77, 0x47, 0x7a, 0x65, 0x4b, 0x89, 0x24, 0x02, 0x77, 0x55, 0x7e, 0x64, 0x76, - 0xbd, 0x80, 0xf2, 0x2d, 0x13, 0x9d, 0x31, 0xca, 0x39, 0x7d, 0x31, 0x2d, 0x10, 0xca, 0x19, 0xca, - 0x29, 0x99, 0x24, 0xda, 0x29, 0xda, 0x09, 0xda, 0x39, 0x80, 0xa2, 0x7a, 0x15, 0x4d, 0x7e, 0x64, - 0x77, 0x33, 0x4d, 0x33, 0x78, 0x09, 0x7c, 0xb2, 0x20, 0xe7, 0x2a, 0x7e, 0x64, 0x76, 0xb2, 0x7a, - 0x65, 0x4b, 0x22, 0x75, 0x2f, 0x52, 0x12, 0x7e, 0x30, 0x7e, 0x21, 0x4d, 0x7e, 0x09, 0x30, 0x0b, - 0x04, 0x1b, 0x34, 0x78, 0x89, 0x80, 0xd4, 0x75, 0x2f, 0x54, 0x12, 0x7e, 0x30, 0x7e, 0x15, 0x4d, - 0x7e, 0x25, 0x4f, 0x80, 0x90, 0x5e, 0x20, 0x07, 0x54, 0x78, 0x7e, 0x44, 0x77, 0xd1, 0x30, 0xe6, - 0x16, 0x4d, 0x33, 0x68, 0x26, 0x1b, 0x34, 0x7e, 0x09, 0x40, 0x0b, 0x04, 0x7e, 0x44, 0x6a, 0x68, - 0x20, 0xe3, 0x04, 0x7e, 0x44, 0x77, 0xd9, 0xca, 0x09, 0xca, 0x39, 0x99, 0x44, 0xda, 0x39, 0xda, - 0x09, 0x7e, 0x64, 0x76, 0xbd, 0x4d, 0x33, 0x68, 0xa6, 0x89, 0x64, 0x7a, 0x15, 0x4d, 0xf5, 0x4f, - 0x7e, 0x64, 0x77, 0x96, 0x80, 0x99, 0x7e, 0x15, 0x4d, 0xe5, 0x4f, 0x80, 0xc4, 0xc0, 0xd0, 0xc0, - 0xd1, 0xc0, 0xe0, 0xca, 0x19, 0xa9, 0x20, 0xdf, 0x12, 0xa9, 0x21, 0xdf, 0x1b, 0x75, 0x2f, 0x01, - 0x12, 0x7e, 0x30, 0x53, 0xdf, 0xf7, 0x12, 0x40, 0xdc, 0x80, 0x0d, 0x75, 0x2f, 0xfe, 0x12, 0x7e, - 0x30, 0x7e, 0x14, 0x00, 0x53, 0x02, 0x40, 0x51, 0xda, 0x19, 0xd0, 0xe0, 0xd0, 0xd1, 0xd0, 0xd0, - 0x32, 0x03, 0xa5, 0xcb, 0x19, 0xb1, 0x80, 0x00, 0x22, 0x22, 0x02, 0x78, 0x52, 0xca, 0x0b, 0xca, + 0x38, 0x50, 0x7a, 0xa1, 0xf3, 0xf5, 0xf3, 0x0b, 0x35, 0x22, 0xc2, 0xaf, 0x7e, 0x37, 0x01, 0xdb, + 0x4d, 0x33, 0x68, 0x3b, 0x7e, 0x07, 0x01, 0xd7, 0x7e, 0x54, 0x09, 0xe3, 0x9d, 0x50, 0xbd, 0x35, + 0x40, 0x02, 0x7d, 0x35, 0xca, 0x39, 0x7e, 0x65, 0x61, 0x99, 0x64, 0xda, 0x39, 0x7e, 0x07, 0x01, + 0xdb, 0x9d, 0x03, 0x7a, 0x07, 0x01, 0xdb, 0x2e, 0x37, 0x01, 0xd7, 0x7a, 0x37, 0x01, 0xd7, 0xbe, + 0x34, 0x09, 0xe2, 0x28, 0xc7, 0x7e, 0x34, 0x05, 0xe3, 0x7a, 0x37, 0x01, 0xd7, 0x80, 0xbd, 0xd2, + 0xaf, 0x22, 0x75, 0x31, 0x53, 0x12, 0x7c, 0x15, 0x7e, 0x15, 0x63, 0x80, 0x11, 0x75, 0x31, 0x51, + 0x12, 0x7c, 0x15, 0x0b, 0x08, 0x10, 0x0b, 0x05, 0x9e, 0x34, 0x00, 0x02, 0x28, 0x4d, 0x7c, 0xb2, + 0x20, 0xe7, 0x27, 0x54, 0x07, 0x23, 0x0a, 0x2b, 0x49, 0x22, 0x6c, 0x7c, 0x7c, 0xb2, 0x54, 0x78, + 0x03, 0x03, 0x03, 0x7c, 0x2b, 0x9d, 0x13, 0x40, 0x1a, 0x68, 0x12, 0x7a, 0x15, 0x63, 0x7a, 0x25, + 0x65, 0x7e, 0x64, 0x75, 0x37, 0x7a, 0x65, 0x61, 0x89, 0x24, 0x02, 0x75, 0x45, 0x7e, 0x64, 0x74, + 0xad, 0x80, 0xf2, 0x2d, 0x13, 0x9d, 0x31, 0xca, 0x39, 0x7d, 0x31, 0x2d, 0x10, 0xca, 0x19, 0xca, + 0x29, 0x99, 0x24, 0xda, 0x29, 0xda, 0x09, 0xda, 0x39, 0x80, 0xa2, 0x7a, 0x15, 0x63, 0x7e, 0x64, + 0x75, 0x23, 0x4d, 0x33, 0x78, 0x09, 0x7c, 0xb2, 0x20, 0xe7, 0x2a, 0x7e, 0x64, 0x74, 0xa2, 0x7a, + 0x65, 0x61, 0x22, 0x75, 0x31, 0x52, 0x12, 0x7c, 0x15, 0x7e, 0x21, 0x63, 0x7e, 0x09, 0x30, 0x0b, + 0x04, 0x1b, 0x34, 0x78, 0x89, 0x80, 0xd4, 0x75, 0x31, 0x54, 0x12, 0x7c, 0x15, 0x7e, 0x15, 0x63, + 0x7e, 0x25, 0x65, 0x80, 0x90, 0x5e, 0x20, 0x07, 0x54, 0x78, 0x7e, 0x44, 0x75, 0xc1, 0x30, 0xe6, + 0x16, 0x4d, 0x33, 0x68, 0x26, 0x1b, 0x34, 0x7e, 0x09, 0x40, 0x0b, 0x04, 0x7e, 0x44, 0x67, 0x63, + 0x20, 0xe3, 0x04, 0x7e, 0x44, 0x75, 0xc9, 0xca, 0x09, 0xca, 0x39, 0x99, 0x44, 0xda, 0x39, 0xda, + 0x09, 0x7e, 0x64, 0x74, 0xad, 0x4d, 0x33, 0x68, 0xa6, 0x89, 0x64, 0x7a, 0x15, 0x63, 0xf5, 0x65, + 0x7e, 0x64, 0x75, 0x86, 0x80, 0x99, 0x7e, 0x15, 0x63, 0xe5, 0x65, 0x80, 0xc4, 0xc0, 0xd0, 0xc0, + 0xd1, 0xc0, 0xe0, 0xca, 0x19, 0xa9, 0x20, 0xdf, 0x12, 0xa9, 0x21, 0xdf, 0x1b, 0x75, 0x31, 0x01, + 0x12, 0x7c, 0x15, 0x53, 0xdf, 0xf7, 0x12, 0x40, 0xdc, 0x80, 0x0d, 0x75, 0x31, 0xfe, 0x12, 0x7c, + 0x15, 0x7e, 0x14, 0x00, 0x53, 0x02, 0x40, 0x51, 0xda, 0x19, 0xd0, 0xe0, 0xd0, 0xd1, 0xd0, 0xd0, + 0x32, 0x03, 0xa5, 0xcb, 0x19, 0xb1, 0x80, 0x00, 0x22, 0x22, 0x02, 0x76, 0x42, 0xca, 0x0b, 0xca, 0x1b, 0xca, 0x2b, 0xca, 0x3b, 0xca, 0x4b, 0xca, 0x5b, 0xca, 0x6b, 0xca, 0x7b, 0xca, 0xeb, 0xc0, - 0xf1, 0x7e, 0xb3, 0x2a, 0x1d, 0xb4, 0x00, 0x02, 0x80, 0x19, 0xb4, 0x01, 0x16, 0x30, 0xc0, 0x08, - 0x75, 0xf1, 0x00, 0x12, 0x78, 0x3c, 0x80, 0x1f, 0x30, 0xc1, 0x1c, 0x75, 0xf1, 0x00, 0x12, 0x78, - 0xcd, 0x80, 0x14, 0x30, 0xc1, 0x08, 0x75, 0xf1, 0x00, 0x12, 0x78, 0xcd, 0x80, 0x09, 0x30, 0xc0, - 0x06, 0x75, 0xf1, 0x00, 0x12, 0x78, 0x3c, 0xd0, 0xf1, 0xda, 0xeb, 0xda, 0x7b, 0xda, 0x6b, 0xda, + 0xf1, 0x7e, 0xb3, 0x2a, 0x33, 0xb4, 0x00, 0x02, 0x80, 0x19, 0xb4, 0x01, 0x16, 0x30, 0xc0, 0x08, + 0x75, 0xf1, 0x00, 0x12, 0x76, 0x2c, 0x80, 0x1f, 0x30, 0xc1, 0x1c, 0x75, 0xf1, 0x00, 0x12, 0x76, + 0xbd, 0x80, 0x14, 0x30, 0xc1, 0x08, 0x75, 0xf1, 0x00, 0x12, 0x76, 0xbd, 0x80, 0x09, 0x30, 0xc0, + 0x06, 0x75, 0xf1, 0x00, 0x12, 0x76, 0x2c, 0xd0, 0xf1, 0xda, 0xeb, 0xda, 0x7b, 0xda, 0x6b, 0xda, 0x5b, 0xda, 0x4b, 0xda, 0x3b, 0xda, 0x2b, 0xda, 0x1b, 0xda, 0x0b, 0x22, 0xc2, 0xc0, 0x7e, 0xb3, - 0x2a, 0x1d, 0xb4, 0x02, 0x07, 0x12, 0x78, 0x5e, 0x02, 0x78, 0x52, 0x22, 0xb4, 0x01, 0xfc, 0x02, - 0x78, 0x98, 0x7e, 0x00, 0x00, 0x7a, 0x03, 0x2a, 0x1d, 0x7a, 0x03, 0x2a, 0x1e, 0x22, 0x7e, 0xb3, - 0x2a, 0x15, 0x54, 0x60, 0x60, 0x05, 0xb4, 0x40, 0x15, 0x80, 0x13, 0x7e, 0xb3, 0x2a, 0x16, 0xb4, - 0x05, 0x0c, 0x75, 0x2f, 0x71, 0x12, 0x7e, 0x30, 0x7e, 0xb3, 0x2a, 0x18, 0xf5, 0x8f, 0x22, 0x75, - 0xf6, 0x00, 0x22, 0xbe, 0x57, 0x2a, 0x1b, 0x28, 0x04, 0x7e, 0x57, 0x2a, 0x1b, 0x7a, 0x0f, 0x2a, - 0x20, 0x7a, 0x57, 0x2a, 0x24, 0x02, 0x78, 0x98, 0x7e, 0xef, 0x2a, 0x20, 0x7e, 0xf7, 0x2a, 0x24, - 0x7e, 0x07, 0x2a, 0x24, 0x4d, 0x00, 0x68, 0x21, 0x7e, 0x00, 0x00, 0x7e, 0xeb, 0xb0, 0xf5, 0xf3, - 0xa3, 0xa5, 0x08, 0x1b, 0xf4, 0x68, 0x04, 0xa5, 0xb8, 0x08, 0xf0, 0x7a, 0xef, 0x2a, 0x20, 0x7a, - 0xf7, 0x2a, 0x24, 0x75, 0x2f, 0x06, 0x12, 0x7e, 0x30, 0x7a, 0x01, 0xf6, 0x22, 0xc2, 0xc1, 0x75, - 0x2f, 0x03, 0x12, 0x7e, 0x30, 0xa9, 0x36, 0xe2, 0x16, 0xe5, 0xf5, 0x54, 0xc0, 0x68, 0x07, 0xa9, - 0xd7, 0xf4, 0xa9, 0x27, 0xf4, 0xfc, 0x53, 0xe1, 0x3f, 0x43, 0xf2, 0x88, 0x02, 0x79, 0x44, 0x7e, - 0xb3, 0x2a, 0x1e, 0xb4, 0x02, 0x0f, 0xa9, 0xd4, 0xe4, 0x7e, 0xb0, 0x00, 0x7a, 0xb3, 0x2a, 0x1e, - 0x7a, 0xb3, 0x2a, 0x1d, 0x22, 0xb4, 0x01, 0x39, 0x7e, 0x21, 0xe6, 0x7c, 0x32, 0x7e, 0x13, 0x2a, - 0x1f, 0x2c, 0x21, 0x7a, 0x23, 0x2a, 0x1f, 0x7e, 0x00, 0x00, 0x2e, 0x04, 0x2a, 0x26, 0xe5, 0xe3, - 0x7a, 0x09, 0xb0, 0x0b, 0x04, 0xa5, 0xdb, 0xf6, 0xa9, 0xd4, 0xe4, 0x75, 0x2f, 0x70, 0x12, 0x7e, - 0x30, 0x7e, 0xb3, 0x2a, 0x1f, 0x7e, 0xa3, 0x2a, 0x1c, 0xbc, 0xab, 0x78, 0x03, 0x12, 0x79, 0xdb, - 0x22, 0x02, 0x7d, 0x44, 0xe5, 0xe6, 0xb4, 0x08, 0x65, 0xa9, 0xc4, 0xe2, 0x7e, 0x01, 0xe3, 0x7e, - 0x11, 0xe3, 0x7e, 0x31, 0xe3, 0x7e, 0x21, 0xe3, 0x7e, 0x51, 0xe3, 0x7e, 0x41, 0xe3, 0x7e, 0x71, - 0xe3, 0x7e, 0x61, 0xe3, 0x7a, 0x0f, 0x2a, 0x15, 0x7a, 0x1f, 0x2a, 0x19, 0x75, 0x2f, 0x04, 0x12, - 0x7e, 0x30, 0x7a, 0x01, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x11, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x21, - 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x31, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x41, 0x2f, 0x12, 0x7e, 0x30, - 0x7a, 0x51, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x61, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x71, 0x2f, 0x12, - 0x7e, 0x30, 0xa9, 0xd4, 0xe4, 0xa9, 0xd7, 0xf4, 0xa9, 0xc6, 0xe2, 0x12, 0x79, 0xaf, 0x22, 0x6d, - 0x00, 0x7e, 0x14, 0x01, 0x02, 0x7a, 0x07, 0x2a, 0x24, 0x7a, 0x03, 0x2a, 0x1f, 0x7e, 0xb3, 0x2a, - 0x15, 0x20, 0xe7, 0x0f, 0x7a, 0x23, 0x2a, 0x1e, 0x7a, 0x33, 0x2a, 0x1d, 0xbe, 0x07, 0x2a, 0x1b, - 0x68, 0x09, 0x22, 0x7a, 0x33, 0x2a, 0x1e, 0x7a, 0x23, 0x2a, 0x1d, 0x7e, 0xb3, 0x2a, 0x15, 0x54, - 0xe3, 0x23, 0x23, 0x30, 0xe0, 0x02, 0xd2, 0xe5, 0x30, 0xe7, 0x02, 0xd2, 0xe4, 0x30, 0xe5, 0x06, - 0x30, 0xe4, 0x03, 0x02, 0x7d, 0x44, 0x54, 0x3e, 0xf5, 0xf0, 0x03, 0x54, 0x1f, 0xc3, 0x25, 0xf0, - 0x90, 0x7a, 0x07, 0x75, 0x84, 0xff, 0x73, 0x02, 0x7b, 0x5b, 0x02, 0x7a, 0x4f, 0x02, 0x7b, 0xf8, - 0x02, 0x7c, 0x13, 0x02, 0x7a, 0xf4, 0x02, 0x7a, 0xb5, 0x02, 0x7c, 0x2c, 0x02, 0x7c, 0x2c, 0x02, - 0x7c, 0x2f, 0x02, 0x7c, 0x2f, 0x02, 0x7c, 0x2f, 0x02, 0x7c, 0x2f, 0x02, 0x7c, 0x2f, 0x02, 0x7c, - 0x2f, 0x02, 0x7c, 0x2f, 0x02, 0x7c, 0x2f, 0x02, 0x7c, 0x35, 0x02, 0x7c, 0xe9, 0x02, 0x7c, 0x32, - 0x02, 0x7c, 0x32, 0x02, 0x7c, 0x32, 0x02, 0x7c, 0x32, 0x02, 0x7c, 0x32, 0x02, 0x7c, 0x32, 0x7e, - 0xb3, 0x2a, 0x16, 0xb4, 0x06, 0x2a, 0x7e, 0xb3, 0x2a, 0x17, 0x60, 0x56, 0x7c, 0x0b, 0x7e, 0x13, - 0x2a, 0x18, 0x7e, 0x17, 0x2a, 0x19, 0x75, 0x2f, 0x72, 0x12, 0x7e, 0x30, 0x7a, 0x01, 0x2f, 0x12, - 0x7e, 0x30, 0x7a, 0x11, 0x2f, 0x12, 0x7e, 0x30, 0x12, 0x7d, 0x4e, 0x40, 0x35, 0x02, 0x78, 0x83, - 0xb4, 0x08, 0x10, 0x75, 0x2f, 0x74, 0x12, 0x7e, 0x30, 0x7e, 0xb3, 0x3f, 0xf1, 0xf5, 0xf3, 0x75, - 0xf6, 0x01, 0x22, 0xb4, 0x00, 0x1c, 0x75, 0x2f, 0x75, 0x12, 0x7e, 0x30, 0x7e, 0xb3, 0x3f, 0xf2, - 0x30, 0xe0, 0x05, 0x75, 0xf3, 0x02, 0x80, 0x03, 0x75, 0xf3, 0x00, 0x75, 0xf3, 0x00, 0x75, 0xf6, - 0x02, 0x22, 0x02, 0x7d, 0x44, 0x7e, 0xb3, 0x2a, 0x16, 0xb4, 0x00, 0x35, 0x75, 0x2f, 0x76, 0x12, - 0x7e, 0x30, 0x7e, 0xb3, 0x2a, 0x1a, 0x54, 0x0f, 0xf5, 0xf1, 0x7e, 0xb3, 0x2a, 0x1a, 0x20, 0xe7, - 0x09, 0xe5, 0xe1, 0x30, 0xe7, 0x0d, 0x74, 0x01, 0x80, 0x0b, 0xe5, 0xe1, 0x30, 0xe6, 0x04, 0x74, - 0x01, 0x80, 0x02, 0x74, 0x00, 0x53, 0xf1, 0x80, 0xf5, 0xf3, 0x75, 0xf3, 0x00, 0x75, 0xf6, 0x02, - 0x22, 0x02, 0x7d, 0x44, 0xc0, 0xf1, 0x7e, 0xb3, 0x2a, 0x1a, 0x54, 0x0f, 0x42, 0xf1, 0x7e, 0xb3, - 0x2a, 0x18, 0xb4, 0x00, 0x45, 0x7e, 0xb3, 0x2a, 0x16, 0xb4, 0x01, 0x24, 0x75, 0x2f, 0x77, 0x12, - 0x7e, 0x30, 0x7e, 0xb3, 0x2a, 0x1a, 0x54, 0x0f, 0x78, 0x05, 0x53, 0xe1, 0x3f, 0x80, 0x37, 0x7e, - 0xb3, 0x2a, 0x1a, 0x20, 0xe7, 0x05, 0x53, 0xe1, 0x7f, 0x80, 0x2b, 0x53, 0xe1, 0xbf, 0x80, 0x26, - 0xb4, 0x03, 0x17, 0x75, 0x2f, 0x78, 0x12, 0x7e, 0x30, 0x7e, 0xb3, 0x2a, 0x1a, 0x20, 0xe7, 0x05, - 0x43, 0xe1, 0x80, 0x80, 0x11, 0x43, 0xe1, 0x40, 0x80, 0x0c, 0x43, 0xe1, 0xc0, 0xd0, 0xf1, 0x75, - 0x2f, 0x07, 0x12, 0x7e, 0x30, 0x22, 0xd0, 0xf1, 0x02, 0x78, 0x7f, 0x7e, 0xb3, 0x2a, 0x16, 0xb4, - 0x09, 0x23, 0x75, 0x2f, 0x79, 0x12, 0x7e, 0x30, 0x7e, 0xb3, 0x2a, 0x18, 0xbe, 0xb3, 0x3f, 0xf1, - 0x68, 0x11, 0xca, 0xb8, 0xc0, 0xf1, 0x12, 0x43, 0x68, 0xd0, 0xf1, 0xda, 0xb8, 0x50, 0x76, 0x7a, - 0xb3, 0x3f, 0xf1, 0x80, 0x6d, 0xb4, 0x05, 0x08, 0x75, 0x2f, 0x7a, 0x12, 0x7e, 0x30, 0x80, 0x62, - 0xb4, 0x03, 0x19, 0x75, 0x2f, 0x7b, 0x12, 0x7e, 0x30, 0x7e, 0xb3, 0x2a, 0x18, 0xb4, 0x01, 0x55, - 0x7e, 0xb3, 0x3f, 0xf2, 0x44, 0x01, 0x7a, 0xb3, 0x3f, 0xf2, 0x80, 0x46, 0xb4, 0x01, 0x19, 0x75, - 0x2f, 0x7c, 0x12, 0x7e, 0x30, 0x7e, 0xb3, 0x2a, 0x18, 0xb4, 0x01, 0x39, 0x7e, 0xb3, 0x3f, 0xf2, - 0x54, 0xfe, 0x7a, 0xb3, 0x3f, 0xf2, 0x80, 0x2a, 0xb4, 0x07, 0x2a, 0x7e, 0xb3, 0x2a, 0x17, 0x60, - 0x24, 0x7c, 0x0b, 0x7e, 0x13, 0x2a, 0x18, 0x7e, 0x17, 0x2a, 0x19, 0x75, 0x2f, 0x73, 0x12, 0x7e, - 0x30, 0x7a, 0x01, 0x2f, 0x12, 0x7e, 0x30, 0x7a, 0x11, 0x2f, 0x12, 0x7e, 0x30, 0x12, 0x7d, 0x7a, - 0x40, 0x03, 0x02, 0x78, 0x7f, 0x02, 0x7d, 0x44, 0x7e, 0xb3, 0x2a, 0x16, 0xb4, 0x0b, 0xf6, 0x75, - 0x2f, 0x7d, 0x12, 0x7e, 0x30, 0x7e, 0xb3, 0x2a, 0x18, 0x7e, 0xa3, 0x2a, 0x1a, 0x4c, 0xab, 0x78, - 0xe4, 0x80, 0xdf, 0x7e, 0xb3, 0x2a, 0x16, 0xb4, 0x0a, 0xdb, 0x75, 0x2f, 0x7e, 0x12, 0x7e, 0x30, - 0x7e, 0xb3, 0x2a, 0x18, 0x70, 0xcf, 0xf5, 0xf3, 0x75, 0xf6, 0x01, 0x22, 0x02, 0x7d, 0x44, 0x02, - 0x7d, 0x44, 0x02, 0x7d, 0x44, 0x7e, 0xb3, 0x2a, 0x16, 0xb4, 0x04, 0x20, 0x75, 0x2f, 0xc3, 0x12, - 0x7e, 0x30, 0x7e, 0x04, 0x00, 0x01, 0x7e, 0x17, 0x2a, 0x17, 0x7e, 0x18, 0x2a, 0x26, 0x7a, 0x1c, - 0x00, 0x00, 0x7e, 0x47, 0x2a, 0x1b, 0x12, 0x7e, 0x3c, 0x02, 0x7c, 0xe3, 0xb4, 0x06, 0x3a, 0x75, - 0x2f, 0xc1, 0x12, 0x7e, 0x30, 0x7e, 0x58, 0x00, 0x00, 0x7a, 0x5c, 0x00, 0xfe, 0x7d, 0xca, 0x7e, - 0xd7, 0x2a, 0x17, 0x7e, 0x78, 0x2a, 0x26, 0x7a, 0x7c, 0x00, 0x00, 0x7e, 0x77, 0x2a, 0x1b, 0x75, - 0x2f, 0xc1, 0x12, 0x7e, 0x30, 0xc0, 0xa8, 0xc0, 0x87, 0xc2, 0xaf, 0xa9, 0xd5, 0x87, 0x12, 0x7e, - 0x75, 0xd0, 0x87, 0xd0, 0xa8, 0x40, 0x4f, 0x80, 0x4a, 0xb4, 0x00, 0x1c, 0xc2, 0xaf, 0xa9, 0xd5, - 0x87, 0x12, 0x78, 0x7f, 0xe4, 0x8d, 0xef, 0x8d, 0xef, 0x8d, 0xef, 0xd5, 0xe0, 0xf7, 0xc0, 0xd1, - 0xca, 0x02, 0xff, 0xca, 0x06, 0x00, 0x00, 0x32, 0xb4, 0x09, 0x12, 0x7e, 0x57, 0x2a, 0x17, 0x4d, - 0x55, 0x68, 0x05, 0xa9, 0xd2, 0xb1, 0x80, 0x03, 0xa9, 0xc2, 0xb1, 0x80, 0x16, 0xb4, 0x07, 0x16, - 0xc2, 0xaf, 0x7e, 0x07, 0x2a, 0x19, 0x7e, 0x17, 0x2a, 0x17, 0xc0, 0xd1, 0xca, 0x18, 0xca, 0x38, - 0xca, 0x28, 0x32, 0x02, 0x78, 0x7f, 0x02, 0x7d, 0x44, 0x7e, 0xb3, 0x2a, 0x16, 0xb4, 0x03, 0x15, - 0x75, 0x2f, 0xc2, 0x12, 0x7e, 0x30, 0x7e, 0x04, 0x00, 0x01, 0x7e, 0x17, 0x2a, 0x17, 0x7e, 0x57, - 0x2a, 0x1b, 0x02, 0x78, 0x83, 0xb4, 0x05, 0x39, 0x75, 0x2f, 0xc0, 0x12, 0x7e, 0x30, 0xc0, 0xa8, - 0xc0, 0x87, 0xc2, 0xaf, 0xa9, 0xd5, 0x87, 0x7e, 0x08, 0x2a, 0x26, 0x7a, 0x0c, 0x00, 0x00, 0x7e, - 0x24, 0x00, 0xfe, 0x7e, 0x37, 0x2a, 0x17, 0x7e, 0x47, 0x2a, 0x1b, 0x12, 0x7e, 0x3c, 0xd0, 0x87, - 0xd0, 0xa8, 0x7e, 0x08, 0x2a, 0x26, 0x7a, 0x0c, 0x00, 0x00, 0x7e, 0x57, 0x2a, 0x1b, 0x02, 0x78, - 0x83, 0x02, 0x7d, 0x44, 0x75, 0x2f, 0x07, 0x12, 0x7e, 0x30, 0x43, 0xe1, 0xc0, 0x22, 0xc0, 0xa8, - 0xc0, 0x87, 0xc2, 0xaf, 0xa9, 0xd5, 0x87, 0x12, 0x7d, 0xb9, 0x40, 0x19, 0x7e, 0x08, 0x2a, 0x26, - 0x7a, 0x0c, 0x00, 0x00, 0xca, 0x0b, 0xca, 0x49, 0x12, 0x7e, 0x3c, 0xda, 0x59, 0xda, 0x0b, 0xd0, - 0x87, 0xd0, 0xa8, 0xc3, 0x22, 0xd0, 0x87, 0xd0, 0xa8, 0x22, 0xc0, 0xa8, 0xc0, 0x87, 0xc2, 0xaf, - 0xa9, 0xd5, 0x87, 0x12, 0x7d, 0xb9, 0x40, 0x2b, 0x7e, 0x58, 0x00, 0x00, 0x7a, 0x5c, 0x00, 0xfe, - 0x7f, 0x61, 0x7e, 0x78, 0x2a, 0x26, 0x7a, 0x7c, 0x00, 0x00, 0x7e, 0x77, 0x2a, 0x1b, 0xbd, 0x74, - 0x78, 0x11, 0x75, 0x2f, 0xc1, 0x12, 0x7e, 0x30, 0x12, 0x7e, 0x75, 0x40, 0x06, 0xd0, 0x87, 0xd0, - 0xa8, 0xc3, 0x22, 0xd0, 0x87, 0xd0, 0xa8, 0xd3, 0x22, 0x7e, 0x24, 0x00, 0xfe, 0x7e, 0x34, 0x7f, - 0xca, 0x0b, 0x1a, 0x50, 0xc5, 0xf0, 0x7d, 0x62, 0x7d, 0x75, 0x7d, 0x87, 0x7e, 0x34, 0x7f, 0xc2, - 0x7e, 0x1b, 0xb0, 0x7e, 0x34, 0x7f, 0x03, 0xb4, 0x01, 0x04, 0x7e, 0x34, 0x7f, 0xcc, 0x7e, 0x1b, - 0xb0, 0xbc, 0x0b, 0x50, 0x49, 0x3e, 0x00, 0x3e, 0x00, 0x0a, 0x50, 0x2d, 0x75, 0x0b, 0x3a, 0x30, - 0x69, 0x53, 0x00, 0x02, 0xbd, 0x38, 0x50, 0x02, 0x2d, 0x38, 0xbc, 0x1b, 0x50, 0x30, 0x3e, 0x10, - 0x3e, 0x10, 0x0a, 0x51, 0x2d, 0x35, 0x69, 0x41, 0x00, 0x02, 0x0b, 0x1a, 0x30, 0xbd, 0x38, 0x50, - 0x02, 0x2d, 0x38, 0xbe, 0x44, 0xff, 0xff, 0x78, 0x05, 0x7e, 0x1b, 0x90, 0x0a, 0x49, 0x4d, 0x44, - 0x68, 0x0c, 0xbe, 0x44, 0x00, 0xff, 0x28, 0x04, 0x7e, 0x44, 0x00, 0xff, 0xc3, 0x22, 0xd3, 0x22, + 0x2a, 0x33, 0xb4, 0x02, 0x07, 0x12, 0x76, 0x4e, 0x02, 0x76, 0x42, 0x22, 0xb4, 0x01, 0xfc, 0x02, + 0x76, 0x88, 0x7e, 0x00, 0x00, 0x7a, 0x03, 0x2a, 0x33, 0x7a, 0x03, 0x2a, 0x34, 0x22, 0x7e, 0xb3, + 0x2a, 0x2b, 0x54, 0x60, 0x60, 0x05, 0xb4, 0x40, 0x15, 0x80, 0x13, 0x7e, 0xb3, 0x2a, 0x2c, 0xb4, + 0x05, 0x0c, 0x75, 0x31, 0x71, 0x12, 0x7c, 0x15, 0x7e, 0xb3, 0x2a, 0x2e, 0xf5, 0x8f, 0x22, 0x75, + 0xf6, 0x00, 0x22, 0xbe, 0x57, 0x2a, 0x31, 0x28, 0x04, 0x7e, 0x57, 0x2a, 0x31, 0x7a, 0x0f, 0x2a, + 0x36, 0x7a, 0x57, 0x2a, 0x3a, 0x02, 0x76, 0x88, 0x7e, 0xef, 0x2a, 0x36, 0x7e, 0xf7, 0x2a, 0x3a, + 0x7e, 0x07, 0x2a, 0x3a, 0x4d, 0x00, 0x68, 0x21, 0x7e, 0x00, 0x00, 0x7e, 0xeb, 0xb0, 0xf5, 0xf3, + 0xa3, 0xa5, 0x08, 0x1b, 0xf4, 0x68, 0x04, 0xa5, 0xb8, 0x08, 0xf0, 0x7a, 0xef, 0x2a, 0x36, 0x7a, + 0xf7, 0x2a, 0x3a, 0x75, 0x31, 0x06, 0x12, 0x7c, 0x15, 0x7a, 0x01, 0xf6, 0x22, 0xc2, 0xc1, 0x75, + 0x31, 0x03, 0x12, 0x7c, 0x15, 0xa9, 0x36, 0xe2, 0x16, 0xe5, 0xf5, 0x54, 0xc0, 0x68, 0x07, 0xa9, + 0xd7, 0xf4, 0xa9, 0x27, 0xf4, 0xfc, 0x53, 0xe1, 0x3f, 0x43, 0xf2, 0x88, 0x02, 0x77, 0x29, 0x7e, + 0xb3, 0x2a, 0x34, 0xb4, 0x02, 0x04, 0xa9, 0xd4, 0xe4, 0x22, 0xb4, 0x01, 0x39, 0x7e, 0x21, 0xe6, + 0x7c, 0x32, 0x7e, 0x13, 0x2a, 0x35, 0x2c, 0x21, 0x7a, 0x23, 0x2a, 0x35, 0x7e, 0x00, 0x00, 0x2e, + 0x04, 0x2a, 0x3c, 0xe5, 0xe3, 0x7a, 0x09, 0xb0, 0x0b, 0x04, 0xa5, 0xdb, 0xf6, 0xa9, 0xd4, 0xe4, + 0x75, 0x31, 0x70, 0x12, 0x7c, 0x15, 0x7e, 0xb3, 0x2a, 0x35, 0x7e, 0xa3, 0x2a, 0x32, 0xbc, 0xab, + 0x78, 0x03, 0x12, 0x77, 0xc0, 0x22, 0x02, 0x7b, 0x29, 0xe5, 0xe6, 0xb4, 0x08, 0x65, 0xa9, 0xc4, + 0xe2, 0x7e, 0x01, 0xe3, 0x7e, 0x11, 0xe3, 0x7e, 0x31, 0xe3, 0x7e, 0x21, 0xe3, 0x7e, 0x51, 0xe3, + 0x7e, 0x41, 0xe3, 0x7e, 0x71, 0xe3, 0x7e, 0x61, 0xe3, 0x7a, 0x0f, 0x2a, 0x2b, 0x7a, 0x1f, 0x2a, + 0x2f, 0x75, 0x31, 0x04, 0x12, 0x7c, 0x15, 0x7a, 0x01, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x11, 0x31, + 0x12, 0x7c, 0x15, 0x7a, 0x21, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x31, 0x31, 0x12, 0x7c, 0x15, 0x7a, + 0x41, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x51, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x61, 0x31, 0x12, 0x7c, + 0x15, 0x7a, 0x71, 0x31, 0x12, 0x7c, 0x15, 0xa9, 0xd4, 0xe4, 0xa9, 0xd7, 0xf4, 0xa9, 0xc6, 0xe2, + 0x12, 0x77, 0x94, 0x22, 0x6d, 0x00, 0x7e, 0x14, 0x01, 0x02, 0x7a, 0x07, 0x2a, 0x3a, 0x7a, 0x03, + 0x2a, 0x35, 0x7e, 0xb3, 0x2a, 0x2b, 0x20, 0xe7, 0x0f, 0x7a, 0x23, 0x2a, 0x34, 0x7a, 0x33, 0x2a, + 0x33, 0xbe, 0x07, 0x2a, 0x31, 0x68, 0x09, 0x22, 0x7a, 0x33, 0x2a, 0x34, 0x7a, 0x23, 0x2a, 0x33, + 0x7e, 0xb3, 0x2a, 0x2b, 0x54, 0xe3, 0x23, 0x23, 0x30, 0xe0, 0x02, 0xd2, 0xe5, 0x30, 0xe7, 0x02, + 0xd2, 0xe4, 0x30, 0xe5, 0x06, 0x30, 0xe4, 0x03, 0x02, 0x7b, 0x29, 0x54, 0x3e, 0xf5, 0xf0, 0x03, + 0x54, 0x1f, 0xc3, 0x25, 0xf0, 0x90, 0x77, 0xec, 0x75, 0x84, 0xff, 0x73, 0x02, 0x79, 0x40, 0x02, + 0x78, 0x34, 0x02, 0x79, 0xdd, 0x02, 0x79, 0xf8, 0x02, 0x78, 0xd9, 0x02, 0x78, 0x9a, 0x02, 0x7a, + 0x11, 0x02, 0x7a, 0x11, 0x02, 0x7a, 0x14, 0x02, 0x7a, 0x14, 0x02, 0x7a, 0x14, 0x02, 0x7a, 0x14, + 0x02, 0x7a, 0x14, 0x02, 0x7a, 0x14, 0x02, 0x7a, 0x14, 0x02, 0x7a, 0x14, 0x02, 0x7a, 0x1a, 0x02, + 0x7a, 0xce, 0x02, 0x7a, 0x17, 0x02, 0x7a, 0x17, 0x02, 0x7a, 0x17, 0x02, 0x7a, 0x17, 0x02, 0x7a, + 0x17, 0x02, 0x7a, 0x17, 0x7e, 0xb3, 0x2a, 0x2c, 0xb4, 0x06, 0x2a, 0x7e, 0xb3, 0x2a, 0x2d, 0x60, + 0x56, 0x7c, 0x0b, 0x7e, 0x13, 0x2a, 0x2e, 0x7e, 0x17, 0x2a, 0x2f, 0x75, 0x31, 0x72, 0x12, 0x7c, + 0x15, 0x7a, 0x01, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x11, 0x31, 0x12, 0x7c, 0x15, 0x12, 0x7b, 0x33, + 0x40, 0x35, 0x02, 0x76, 0x73, 0xb4, 0x08, 0x10, 0x75, 0x31, 0x74, 0x12, 0x7c, 0x15, 0x7e, 0xb3, + 0x3f, 0xf1, 0xf5, 0xf3, 0x75, 0xf6, 0x01, 0x22, 0xb4, 0x00, 0x1c, 0x75, 0x31, 0x75, 0x12, 0x7c, + 0x15, 0x7e, 0xb3, 0x3f, 0xf2, 0x30, 0xe0, 0x05, 0x75, 0xf3, 0x02, 0x80, 0x03, 0x75, 0xf3, 0x00, + 0x75, 0xf3, 0x00, 0x75, 0xf6, 0x02, 0x22, 0x02, 0x7b, 0x29, 0x7e, 0xb3, 0x2a, 0x2c, 0xb4, 0x00, + 0x35, 0x75, 0x31, 0x76, 0x12, 0x7c, 0x15, 0x7e, 0xb3, 0x2a, 0x30, 0x54, 0x0f, 0xf5, 0xf1, 0x7e, + 0xb3, 0x2a, 0x30, 0x20, 0xe7, 0x09, 0xe5, 0xe1, 0x30, 0xe7, 0x0d, 0x74, 0x01, 0x80, 0x0b, 0xe5, + 0xe1, 0x30, 0xe6, 0x04, 0x74, 0x01, 0x80, 0x02, 0x74, 0x00, 0x53, 0xf1, 0x80, 0xf5, 0xf3, 0x75, + 0xf3, 0x00, 0x75, 0xf6, 0x02, 0x22, 0x02, 0x7b, 0x29, 0xc0, 0xf1, 0x7e, 0xb3, 0x2a, 0x30, 0x54, + 0x0f, 0x42, 0xf1, 0x7e, 0xb3, 0x2a, 0x2e, 0xb4, 0x00, 0x45, 0x7e, 0xb3, 0x2a, 0x2c, 0xb4, 0x01, + 0x24, 0x75, 0x31, 0x77, 0x12, 0x7c, 0x15, 0x7e, 0xb3, 0x2a, 0x30, 0x54, 0x0f, 0x78, 0x05, 0x53, + 0xe1, 0x3f, 0x80, 0x37, 0x7e, 0xb3, 0x2a, 0x30, 0x20, 0xe7, 0x05, 0x53, 0xe1, 0x7f, 0x80, 0x2b, + 0x53, 0xe1, 0xbf, 0x80, 0x26, 0xb4, 0x03, 0x17, 0x75, 0x31, 0x78, 0x12, 0x7c, 0x15, 0x7e, 0xb3, + 0x2a, 0x30, 0x20, 0xe7, 0x05, 0x43, 0xe1, 0x80, 0x80, 0x11, 0x43, 0xe1, 0x40, 0x80, 0x0c, 0x43, + 0xe1, 0xc0, 0xd0, 0xf1, 0x75, 0x31, 0x07, 0x12, 0x7c, 0x15, 0x22, 0xd0, 0xf1, 0x02, 0x76, 0x6f, + 0x7e, 0xb3, 0x2a, 0x2c, 0xb4, 0x09, 0x23, 0x75, 0x31, 0x79, 0x12, 0x7c, 0x15, 0x7e, 0xb3, 0x2a, + 0x2e, 0xbe, 0xb3, 0x3f, 0xf1, 0x68, 0x11, 0xca, 0xb8, 0xc0, 0xf1, 0x12, 0x43, 0xd4, 0xd0, 0xf1, + 0xda, 0xb8, 0x50, 0x76, 0x7a, 0xb3, 0x3f, 0xf1, 0x80, 0x6d, 0xb4, 0x05, 0x08, 0x75, 0x31, 0x7a, + 0x12, 0x7c, 0x15, 0x80, 0x62, 0xb4, 0x03, 0x19, 0x75, 0x31, 0x7b, 0x12, 0x7c, 0x15, 0x7e, 0xb3, + 0x2a, 0x2e, 0xb4, 0x01, 0x55, 0x7e, 0xb3, 0x3f, 0xf2, 0x44, 0x01, 0x7a, 0xb3, 0x3f, 0xf2, 0x80, + 0x46, 0xb4, 0x01, 0x19, 0x75, 0x31, 0x7c, 0x12, 0x7c, 0x15, 0x7e, 0xb3, 0x2a, 0x2e, 0xb4, 0x01, + 0x39, 0x7e, 0xb3, 0x3f, 0xf2, 0x54, 0xfe, 0x7a, 0xb3, 0x3f, 0xf2, 0x80, 0x2a, 0xb4, 0x07, 0x2a, + 0x7e, 0xb3, 0x2a, 0x2d, 0x60, 0x24, 0x7c, 0x0b, 0x7e, 0x13, 0x2a, 0x2e, 0x7e, 0x17, 0x2a, 0x2f, + 0x75, 0x31, 0x73, 0x12, 0x7c, 0x15, 0x7a, 0x01, 0x31, 0x12, 0x7c, 0x15, 0x7a, 0x11, 0x31, 0x12, + 0x7c, 0x15, 0x12, 0x7b, 0x5f, 0x40, 0x03, 0x02, 0x76, 0x6f, 0x02, 0x7b, 0x29, 0x7e, 0xb3, 0x2a, + 0x2c, 0xb4, 0x0b, 0xf6, 0x75, 0x31, 0x7d, 0x12, 0x7c, 0x15, 0x7e, 0xb3, 0x2a, 0x2e, 0x7e, 0xa3, + 0x2a, 0x30, 0x4c, 0xab, 0x78, 0xe4, 0x80, 0xdf, 0x7e, 0xb3, 0x2a, 0x2c, 0xb4, 0x0a, 0xdb, 0x75, + 0x31, 0x7e, 0x12, 0x7c, 0x15, 0x7e, 0xb3, 0x2a, 0x2e, 0x70, 0xcf, 0xf5, 0xf3, 0x75, 0xf6, 0x01, + 0x22, 0x02, 0x7b, 0x29, 0x02, 0x7b, 0x29, 0x02, 0x7b, 0x29, 0x7e, 0xb3, 0x2a, 0x2c, 0xb4, 0x04, + 0x20, 0x75, 0x31, 0xc3, 0x12, 0x7c, 0x15, 0x7e, 0x04, 0x00, 0x01, 0x7e, 0x17, 0x2a, 0x2d, 0x7e, + 0x18, 0x2a, 0x3c, 0x7a, 0x1c, 0x00, 0x00, 0x7e, 0x47, 0x2a, 0x31, 0x12, 0x7c, 0x21, 0x02, 0x7a, + 0xc8, 0xb4, 0x06, 0x3a, 0x75, 0x31, 0xc1, 0x12, 0x7c, 0x15, 0x7e, 0x58, 0x00, 0x00, 0x7a, 0x5c, + 0x00, 0xfe, 0x7d, 0xca, 0x7e, 0xd7, 0x2a, 0x2d, 0x7e, 0x78, 0x2a, 0x3c, 0x7a, 0x7c, 0x00, 0x00, + 0x7e, 0x77, 0x2a, 0x31, 0x75, 0x31, 0xc1, 0x12, 0x7c, 0x15, 0xc0, 0xa8, 0xc0, 0x87, 0xc2, 0xaf, + 0xa9, 0xd5, 0x87, 0x12, 0x7c, 0x5a, 0xd0, 0x87, 0xd0, 0xa8, 0x40, 0x4f, 0x80, 0x4a, 0xb4, 0x00, + 0x1c, 0xc2, 0xaf, 0xa9, 0xd5, 0x87, 0x12, 0x76, 0x6f, 0xe4, 0x8d, 0xef, 0x8d, 0xef, 0x8d, 0xef, + 0xd5, 0xe0, 0xf7, 0xc0, 0xd1, 0xca, 0x02, 0xff, 0xca, 0x06, 0x00, 0x00, 0x32, 0xb4, 0x09, 0x12, + 0x7e, 0x57, 0x2a, 0x2d, 0x4d, 0x55, 0x68, 0x05, 0xa9, 0xd2, 0xb1, 0x80, 0x03, 0xa9, 0xc2, 0xb1, + 0x80, 0x16, 0xb4, 0x07, 0x16, 0xc2, 0xaf, 0x7e, 0x07, 0x2a, 0x2f, 0x7e, 0x17, 0x2a, 0x2d, 0xc0, + 0xd1, 0xca, 0x18, 0xca, 0x38, 0xca, 0x28, 0x32, 0x02, 0x76, 0x6f, 0x02, 0x7b, 0x29, 0x7e, 0xb3, + 0x2a, 0x2c, 0xb4, 0x03, 0x15, 0x75, 0x31, 0xc2, 0x12, 0x7c, 0x15, 0x7e, 0x04, 0x00, 0x01, 0x7e, + 0x17, 0x2a, 0x2d, 0x7e, 0x57, 0x2a, 0x31, 0x02, 0x76, 0x73, 0xb4, 0x05, 0x39, 0x75, 0x31, 0xc0, + 0x12, 0x7c, 0x15, 0xc0, 0xa8, 0xc0, 0x87, 0xc2, 0xaf, 0xa9, 0xd5, 0x87, 0x7e, 0x08, 0x2a, 0x3c, + 0x7a, 0x0c, 0x00, 0x00, 0x7e, 0x24, 0x00, 0xfe, 0x7e, 0x37, 0x2a, 0x2d, 0x7e, 0x47, 0x2a, 0x31, + 0x12, 0x7c, 0x21, 0xd0, 0x87, 0xd0, 0xa8, 0x7e, 0x08, 0x2a, 0x3c, 0x7a, 0x0c, 0x00, 0x00, 0x7e, + 0x57, 0x2a, 0x31, 0x02, 0x76, 0x73, 0x02, 0x7b, 0x29, 0x75, 0x31, 0x07, 0x12, 0x7c, 0x15, 0x43, + 0xe1, 0xc0, 0x22, 0xc0, 0xa8, 0xc0, 0x87, 0xc2, 0xaf, 0xa9, 0xd5, 0x87, 0x12, 0x7b, 0x9e, 0x40, + 0x19, 0x7e, 0x08, 0x2a, 0x3c, 0x7a, 0x0c, 0x00, 0x00, 0xca, 0x0b, 0xca, 0x49, 0x12, 0x7c, 0x21, + 0xda, 0x59, 0xda, 0x0b, 0xd0, 0x87, 0xd0, 0xa8, 0xc3, 0x22, 0xd0, 0x87, 0xd0, 0xa8, 0x22, 0xc0, + 0xa8, 0xc0, 0x87, 0xc2, 0xaf, 0xa9, 0xd5, 0x87, 0x12, 0x7b, 0x9e, 0x40, 0x2b, 0x7e, 0x58, 0x00, + 0x00, 0x7a, 0x5c, 0x00, 0xfe, 0x7f, 0x61, 0x7e, 0x78, 0x2a, 0x3c, 0x7a, 0x7c, 0x00, 0x00, 0x7e, + 0x77, 0x2a, 0x31, 0xbd, 0x74, 0x78, 0x11, 0x75, 0x31, 0xc1, 0x12, 0x7c, 0x15, 0x12, 0x7c, 0x5a, + 0x40, 0x06, 0xd0, 0x87, 0xd0, 0xa8, 0xc3, 0x22, 0xd0, 0x87, 0xd0, 0xa8, 0xd3, 0x22, 0x7e, 0x24, + 0x00, 0xfe, 0x7e, 0x34, 0x7f, 0xca, 0x0b, 0x1a, 0x50, 0xc5, 0xf0, 0x7d, 0x62, 0x7d, 0x75, 0x7d, + 0x87, 0x7e, 0x34, 0x7f, 0xc2, 0x7e, 0x1b, 0xb0, 0x7e, 0x34, 0x7f, 0x03, 0xb4, 0x01, 0x04, 0x7e, + 0x34, 0x7f, 0xcc, 0x7e, 0x1b, 0xb0, 0xbc, 0x0b, 0x50, 0x49, 0x3e, 0x00, 0x3e, 0x00, 0x0a, 0x50, + 0x2d, 0x75, 0x0b, 0x3a, 0x30, 0x69, 0x53, 0x00, 0x02, 0xbd, 0x38, 0x50, 0x02, 0x2d, 0x38, 0xbc, + 0x1b, 0x50, 0x30, 0x3e, 0x10, 0x3e, 0x10, 0x0a, 0x51, 0x2d, 0x35, 0x69, 0x41, 0x00, 0x02, 0x0b, + 0x1a, 0x30, 0xbd, 0x38, 0x50, 0x02, 0x2d, 0x38, 0xbe, 0x44, 0xff, 0xff, 0x78, 0x05, 0x7e, 0x1b, + 0x90, 0x0a, 0x49, 0x4d, 0x44, 0x68, 0x0c, 0xbe, 0x44, 0x00, 0xff, 0x28, 0x04, 0x7e, 0x44, 0x00, + 0xff, 0xc3, 0x22, 0xd3, 0x22, -// Segment #15, Start Address 00ff7fc6, Length 4 +// Segment #16, Start Address 00ff7fc6, Length 4 0xff,0x00,0xc6,0x7f,0x04,0x00, - 0x01, 0x0c, 0x03, 0x00, + 0x01, 0x10, 0x04, 0x00, -// Segment #16, Start Address 00ff7e30, Length 315 -0xff,0x00,0x30,0x7e,0x3b,0x01, - 0xca, 0x08, 0x7e, 0x01, 0x2f, 0x7a, 0x03, 0x3f, 0xf0, 0xda, 0x08, 0x22, 0x7e, 0x1b, 0xc0, 0x7a, +// Segment #17, Start Address 00ff7c15, Length 330 +0xff,0x00,0x15,0x7c,0x4a,0x01, + 0xca, 0x08, 0x7e, 0x01, 0x31, 0x7a, 0x03, 0x3f, 0xf0, 0xda, 0x08, 0x22, 0x7e, 0x1b, 0xc0, 0x7a, 0x0b, 0xc0, 0x0b, 0x14, 0x0b, 0x34, 0x1b, 0x44, 0x78, 0xf2, 0x22, 0x7f, 0x6f, 0x7f, 0xf0, 0x1b, 0xfc, 0x7c, 0x54, 0x7d, 0x32, 0x80, 0x08, 0xca, 0x1b, 0xca, 0x1b, 0xca, 0x1b, 0xca, 0x1b, 0x9e, 0x44, 0x00, 0x10, 0x50, 0xf2, 0x2e, 0x44, 0x00, 0x10, 0x68, 0x06, 0xca, 0x48, 0x1b, 0x44, 0x78, 0xfa, 0x7f, 0xf6, 0x89, 0xe4, 0xca, 0x6b, 0x5e, 0xd4, 0x00, 0x3f, 0x68, 0x20, 0x7e, 0x84, 0x00, - 0x40, 0x9d, 0x8d, 0xda, 0x6b, 0xbd, 0x87, 0x38, 0x16, 0xca, 0x79, 0x7d, 0x78, 0x12, 0x7e, 0x9f, + 0x40, 0x9d, 0x8d, 0xda, 0x6b, 0xbd, 0x87, 0x38, 0x16, 0xca, 0x79, 0x7d, 0x78, 0x12, 0x7c, 0x84, 0xda, 0x79, 0x40, 0x08, 0x9d, 0x78, 0x68, 0x02, 0x80, 0x05, 0xc2, 0xd7, 0x22, 0xda, 0x6b, 0x43, 0x90, 0x30, 0x74, 0xaa, 0x39, 0xb5, 0x55, 0x55, 0x74, 0x55, 0x39, 0xb5, 0x2a, 0xaa, 0x74, 0xa0, 0x39, 0xb5, 0x55, 0x55, 0x7e, 0x04, 0x00, 0x40, 0x9d, 0x70, 0x50, 0x06, 0x2d, 0x70, 0x7d, 0x07, @@ -1104,16 +1211,17 @@ static unsigned char IMAGE_ARRAY_NAME[] 0x1b, 0x54, 0x78, 0xf5, 0x80, 0x2c, 0x6d, 0x00, 0x7c, 0x20, 0x7f, 0x16, 0x9f, 0x10, 0x7f, 0x27, 0x9f, 0x20, 0x7e, 0x2b, 0x00, 0x7e, 0x1b, 0x10, 0xbc, 0x01, 0x78, 0x16, 0x0b, 0x2c, 0x0b, 0x1c, 0xa5, 0xdb, 0xef, 0x7c, 0xb6, 0x20, 0xe0, 0x03, 0x63, 0x90, 0x30, 0x4d, 0x77, 0x78, 0x93, 0xc2, - 0xd7, 0x22, 0xd2, 0xd7, 0x22, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x06, 0x04, 0x02, 0x04, 0x00, - 0x02, 0x01, 0x04, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x04, 0x00, 0x08, 0x10, 0x02, 0x10, 0x04, 0x02, 0x08, 0x00, 0x01, 0x01, 0x08, 0x7e, - 0x18, 0x7f, 0xbd, 0x7a, 0x1c, 0x00, 0xfe, 0x0b, 0x1a, 0x00, 0xbe, 0x10, 0x14, 0x38, 0x1a, 0x0a, - 0x51, 0x23, 0x7e, 0x18, 0x7f, 0x15, 0x7a, 0x1c, 0x00, 0xff, 0x2d, 0x35, 0x0b, 0x1a, 0x50, 0x60, - 0x08, 0xa5, 0xb8, 0x02, 0x03, 0x4e, 0xa0, 0x08, 0x22, 0x80, 0xfe, + 0xd7, 0x22, 0xd2, 0xd7, 0x22, 0x00, 0x04, 0x00, 0x04, 0x42, 0x08, 0x06, 0x04, 0x02, 0x04, 0x00, + 0x02, 0x01, 0x04, 0x01, 0x02, 0x82, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x04, 0x02, 0x08, 0x10, 0x02, 0x10, 0x04, 0x02, 0x08, 0x00, 0x01, 0x01, 0x08, 0x00, + 0x01, 0x00, 0x02, 0x00, 0x02, 0x02, 0x08, 0x02, 0x04, 0x20, 0x04, 0x7e, 0x18, 0x7f, 0xbd, 0x7a, + 0x1c, 0x00, 0xfe, 0x0b, 0x1a, 0x00, 0x5e, 0x10, 0x1f, 0xbe, 0x10, 0x1a, 0x38, 0x1a, 0x0a, 0x51, + 0x23, 0x7e, 0x18, 0x7c, 0xfa, 0x7a, 0x1c, 0x00, 0xff, 0x2d, 0x35, 0x0b, 0x1a, 0x50, 0x60, 0x08, + 0xa5, 0xb8, 0x02, 0x03, 0x4e, 0xa0, 0x08, 0x22, 0x80, 0xfe, }; static struct edge_firmware_version_info IMAGE_VERSION_NAME = { - 1, 12, 3 }; // Major, Minor, Build + 1, 16, 4 }; // Major, Minor, Build #undef IMAGE_VERSION_NAME --- linux-2.6.9-rc1/drivers/usb/serial/io_tables.h 2004-08-24 00:02:59.000000000 -0700 +++ 25/drivers/usb/serial/io_tables.h 2004-09-02 20:51:52.000000000 -0700 @@ -14,22 +14,12 @@ #ifndef IO_TABLES_H #define IO_TABLES_H -static struct usb_device_id edgeport_1port_id_table [] = { - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_PARALLEL_PORT) }, - { } -}; - static struct usb_device_id edgeport_2port_id_table [] = { { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2I) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_421) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_21) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2_DIN) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_2) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_2I) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_421) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_21) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_2_DIN) }, { } }; @@ -41,12 +31,9 @@ static struct usb_device_id edgeport_4po { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4I) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4_DIN) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_22I) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_4) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_COMPATIBLE) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_4) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_4T) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_4I) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_8_DUAL_CPU) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_4_DIN) }, { } }; @@ -54,9 +41,9 @@ static struct usb_device_id edgeport_8po { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8I) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_8) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_16_DUAL_CPU) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_8I) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) }, { } }; @@ -69,7 +56,6 @@ static struct usb_device_id id_table_com { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4I) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2I) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_PARALLEL_PORT) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_421) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_21) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8_DUAL_CPU) }, @@ -77,51 +63,18 @@ static struct usb_device_id id_table_com { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_2_DIN) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4_DIN) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_22I) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_4) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_COMPATIBLE) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8I) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_2) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_2I) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_421) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_21) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_2_DIN) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_4) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_4T) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_4I) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_8_DUAL_CPU) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_4_DIN) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_8) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_16_DUAL_CPU) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BB_EDGEPORT_8I) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, id_table_combined); -static struct usb_serial_device_type edgeport_1port_device = { - .owner = THIS_MODULE, - .name = "Edgeport 1 port adapter", - .short_name = "edgeport_1", - .id_table = edgeport_1port_id_table, - .num_interrupt_in = 1, - .num_bulk_in = 1, - .num_bulk_out = 1, - .num_ports = 1, - .open = edge_open, - .close = edge_close, - .throttle = edge_throttle, - .unthrottle = edge_unthrottle, - .attach = edge_startup, - .shutdown = edge_shutdown, - .ioctl = edge_ioctl, - .set_termios = edge_set_termios, - .tiocmget = edge_tiocmget, - .tiocmset = edge_tiocmset, - .write = edge_write, - .write_room = edge_write_room, - .chars_in_buffer = edge_chars_in_buffer, - .break_ctl = edge_break, -}; - static struct usb_serial_device_type edgeport_2port_device = { .owner = THIS_MODULE, .name = "Edgeport 2 port adapter", --- linux-2.6.9-rc1/drivers/usb/serial/io_ti.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/drivers/usb/serial/io_ti.c 2004-09-02 20:51:52.000000000 -0700 @@ -124,15 +124,11 @@ static struct usb_device_id edgeport_2po { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_2C) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_2I) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_421) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_421_BOOT) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_421_DOWN) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_21) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_21_BOOT) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_21_DOWN) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_42) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4I) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_22) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_22I) }, { } }; @@ -143,15 +139,11 @@ static struct usb_device_id id_table_com { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_2C) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_2I) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_421) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_421_BOOT) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_421_DOWN) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_21) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_21_BOOT) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_21_DOWN) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_42) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4I) }, - { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_22) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_22I) }, { } }; --- linux-2.6.9-rc1/drivers/usb/serial/io_usbvend.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/usb/serial/io_usbvend.h 2004-09-02 20:51:52.000000000 -0700 @@ -18,14 +18,6 @@ #if !defined(_USBVEND_H) #define _USBVEND_H -#ifndef __KERNEL__ -#include "ionprag.h" /* Extra I/O Networks pragmas */ - -#include - -#include "iondef.h" /* Standard I/O Networks definitions */ -#endif - /************************************************************************ * * D e f i n e s / T y p e d e f s @@ -37,6 +29,7 @@ // #define USB_VENDOR_ID_ION 0x1608 // Our VID +#define USB_VENDOR_ID_TI 0x0451 // TI VID // // Definitions of USB product IDs (PID) @@ -48,36 +41,41 @@ // ION-device OEM IDs #define ION_OEM_ID_ION 0 // 00h Inside Out Networks -#define ION_OEM_ID_NLYNX 1 // 01h NLynx Systems +#define ION_OEM_ID_NLYNX 1 // 01h NLynx Systems #define ION_OEM_ID_GENERIC 2 // 02h Generic OEM #define ION_OEM_ID_MAC 3 // 03h Mac Version #define ION_OEM_ID_MEGAWOLF 4 // 04h Lupusb OEM Mac version (MegaWolf) #define ION_OEM_ID_MULTITECH 5 // 05h Multitech Rapidports +#define ION_OEM_ID_AGILENT 6 // 06h AGILENT board - -// ION-device Device IDs -// Product IDs - assigned to match middle digit of serial number +// ION-device Device IDs +// Product IDs - assigned to match middle digit of serial number (No longer true) -// The ION_DEVICE_ID_GENERATION_2 bit (0x20) will be ORed into the existing edgeport -// PIDs to identify 80251+Netchip hardware. This will guarantee that if a second -// generation edgeport device is plugged into a PC with an older (pre 2.0) driver, -// it will not enumerate. +#define ION_DEVICE_ID_80251_NETCHIP 0x020 // This bit is set in the PID if this edgeport hardware$ + // is based on the 80251+Netchip. -#define ION_DEVICE_ID_GENERATION_2 0x020 // This bit is set in the PID if this edgeport hardware - // is based on the 80251+Netchip. +#define ION_DEVICE_ID_GENERATION_1 0x00 // Value for 930 based edgeports +#define ION_DEVICE_ID_GENERATION_2 0x01 // Value for 80251+Netchip. +#define ION_DEVICE_ID_GENERATION_3 0x02 // Value for Texas Instruments TUSB5052 chip +#define ION_DEVICE_ID_GENERATION_4 0x03 // Watchport Family of products +#define ION_GENERATION_MASK 0x03 + +#define ION_DEVICE_ID_HUB_MASK 0x0080 // This bit in the PID designates a HUB device + // for example 8C would be a 421 4 port hub + // and 8D would be a 2 port embedded hub -#define EDGEPORT_DEVICE_ID_MASK 0x3df // Not including GEN_2 bit +#define EDGEPORT_DEVICE_ID_MASK 0x0ff // Not including OEM or GENERATION fields #define ION_DEVICE_ID_UNCONFIGURED_EDGE_DEVICE 0x000 // In manufacturing only #define ION_DEVICE_ID_EDGEPORT_4 0x001 // Edgeport/4 RS232 -// ION_DEVICE_ID_HUBPORT_7 0x002 // Hubport/7 (Placeholder, not used by software) +#define ION_DEVICE_ID_EDGEPORT_8R 0x002 // Edgeport with RJ45 no Ring #define ION_DEVICE_ID_RAPIDPORT_4 0x003 // Rapidport/4 #define ION_DEVICE_ID_EDGEPORT_4T 0x004 // Edgeport/4 RS232 for Telxon (aka "Fleetport") #define ION_DEVICE_ID_EDGEPORT_2 0x005 // Edgeport/2 RS232 #define ION_DEVICE_ID_EDGEPORT_4I 0x006 // Edgeport/4 RS422 #define ION_DEVICE_ID_EDGEPORT_2I 0x007 // Edgeport/2 RS422/RS485 -// ION_DEVICE_ID_HUBPORT_4 0x008 // Hubport/4 (Placeholder, not used by software) +#define ION_DEVICE_ID_EDGEPORT_8RR 0x008 // Edgeport with RJ45 with Data and RTS/CTS only // ION_DEVICE_ID_EDGEPORT_8_HANDBUILT 0x009 // Hand-built Edgeport/8 (Placeholder, used in middle digit of serial number only!) // ION_DEVICE_ID_MULTIMODEM_4X56 0x00A // MultiTech version of RP/4 (Placeholder, used in middle digit of serial number only!) #define ION_DEVICE_ID_EDGEPORT_PARALLEL_PORT 0x00B // Edgeport/(4)21 Parallel port (USS720) @@ -90,41 +88,134 @@ #define ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU 0x012 // Half of an Edgeport/16 (the kind with 2 EP/8s) #define ION_DEVICE_ID_EDGEPORT_COMPATIBLE 0x013 // Edgeport Compatible, for NCR, Axiohm etc. testing #define ION_DEVICE_ID_EDGEPORT_8I 0x014 // Edgeport/8 RS422 (single-CPU) +#define ION_DEVICE_ID_EDGEPORT_1 0x015 // Edgeport/1 RS232 +#define ION_DEVICE_ID_EPOS44 0x016 // Half of an EPOS/44 (TIUMP BASED) +#define ION_DEVICE_ID_EDGEPORT_42 0x017 // Edgeport/42 +#define ION_DEVICE_ID_EDGEPORT_412_8 0x018 // Edgeport/412 8 port part +#define ION_DEVICE_ID_EDGEPORT_412_4 0x019 // Edgeport/412 4 port part +#define ION_DEVICE_ID_EDGEPORT_22I 0x01A // Edgeport/22I is an Edgeport/4 with ports 1&2 RS422 and ports 3&4 RS232 + +// Compact Form factor TI based devices 2c, 21c, 22c, 221c +#define ION_DEVICE_ID_EDGEPORT_2C 0x01B // Edgeport/2c is a TI based Edgeport/2 - Small I2c +#define ION_DEVICE_ID_EDGEPORT_221C 0x01C // Edgeport/221c is a TI based Edgeport/2 with lucent chip and + // 2 external hub ports - Large I2C +#define ION_DEVICE_ID_EDGEPORT_22C 0x01D // Edgeport/22c is a TI based Edgeport/2 with + // 2 external hub ports - Large I2C +#define ION_DEVICE_ID_EDGEPORT_21C 0x01E // Edgeport/21c is a TI based Edgeport/2 with lucent chip + // Small I2C + + +/* + * DANGER DANGER The 0x20 bit was used to indicate a 8251/netchip GEN 2 device. + * Since the MAC, Linux, and Optimal drivers still used the old code + * I suggest that you skip the 0x20 bit when creating new PIDs + */ + + +// Generation 3 devices -- 3410 based edgport/1 (256 byte I2C) +#define ION_DEVICE_ID_TI3410_EDGEPORT_1 0x040 // Edgeport/1 RS232 +#define ION_DEVICE_ID_TI3410_EDGEPORT_1I 0x041 // Edgeport/1i- RS422 model + +// Ti based software switchable RS232/RS422/RS485 devices +#define ION_DEVICE_ID_EDGEPORT_4S 0x042 // Edgeport/4s - software switchable model +#define ION_DEVICE_ID_EDGEPORT_8S 0x043 // Edgeport/8s - software switchable model + +// Usb to Ethernet dongle +#define ION_DEVICE_ID_EDGEPORT_E 0x0E0 // Edgeport/E Usb to Ethernet + +// Edgeport TI based devices +#define ION_DEVICE_ID_TI_EDGEPORT_4 0x0201 // Edgeport/4 RS232 +#define ION_DEVICE_ID_TI_EDGEPORT_2 0x0205 // Edgeport/2 RS232 +#define ION_DEVICE_ID_TI_EDGEPORT_4I 0x0206 // Edgeport/4i RS422 +#define ION_DEVICE_ID_TI_EDGEPORT_2I 0x0207 // Edgeport/2i RS422/RS485 +#define ION_DEVICE_ID_TI_EDGEPORT_421 0x020C // Edgeport/421 4 hub 2 RS232 + Parallel (lucent on a different hub port) +#define ION_DEVICE_ID_TI_EDGEPORT_21 0x020D // Edgeport/21 2 RS232 + Parallel (lucent on a different hub port) +#define ION_DEVICE_ID_TI_EDGEPORT_8 0x020F // Edgeport/8 (single-CPU) +#define ION_DEVICE_ID_TI_EDGEPORT_1 0x0215 // Edgeport/1 RS232 +#define ION_DEVICE_ID_TI_EDGEPORT_42 0x0217 // Edgeport/42 4 hub 2 RS232 +#define ION_DEVICE_ID_TI_EDGEPORT_22I 0x021A // Edgeport/22I is an Edgeport/4 with ports 1&2 RS422 and ports 3&4 RS232 +#define ION_DEVICE_ID_TI_EDGEPORT_2C 0x021B // Edgeport/2c RS232 +#define ION_DEVICE_ID_TI_EDGEPORT_221C 0x021C // Edgeport/221c is a TI based Edgeport/2 with lucent chip and + // 2 external hub ports - Large I2C +#define ION_DEVICE_ID_TI_EDGEPORT_22C 0x021D // Edgeport/22c is a TI based Edgeport/2 with + // 2 external hub ports - Large I2C +#define ION_DEVICE_ID_TI_EDGEPORT_21C 0x021E // Edgeport/21c is a TI based Edgeport/2 with lucent chip + +// Generation 3 devices -- 3410 based edgport/1 (256 byte I2C) +#define ION_DEVICE_ID_TI_TI3410_EDGEPORT_1 0x240 // Edgeport/1 RS232 +#define ION_DEVICE_ID_TI_TI3410_EDGEPORT_1I 0x241 // Edgeport/1i- RS422 model + +// Ti based software switchable RS232/RS422/RS485 devices +#define ION_DEVICE_ID_TI_EDGEPORT_4S 0x242 // Edgeport/4s - software switchable model +#define ION_DEVICE_ID_IT_EDGEPORT_8S 0x243 // Edgeport/8s - software switchable model + + +/************************************************************************ + * + * Generation 4 devices + * + ************************************************************************/ + +// Watchport based on 3410 both 1-wire and binary products (16K I2C) +#define ION_DEVICE_ID_WP_UNSERIALIZED 0x300 // Watchport based on 3410 both 1-wire and binary products +#define ION_DEVICE_ID_WP_PROXIMITY 0x301 // Watchport/P Discontinued +#define ION_DEVICE_ID_WP_MOTION 0x302 // Watchport/M +#define ION_DEVICE_ID_WP_MOISTURE 0x303 // Watchport/W +#define ION_DEVICE_ID_WP_TEMPERATURE 0x304 // Watchport/T +#define ION_DEVICE_ID_WP_HUMIDITY 0x305 // Watchport/H + +#define ION_DEVICE_ID_WP_POWER 0x306 // Watchport +#define ION_DEVICE_ID_WP_LIGHT 0x307 // Watchport +#define ION_DEVICE_ID_WP_RADIATION 0x308 // Watchport +#define ION_DEVICE_ID_WP_ACCELERATION 0x309 // Watchport/A +#define ION_DEVICE_ID_WP_DISTANCE 0x30A // Watchport/D Discontinued +#define ION_DEVICE_ID_WP_PROX_DIST 0x30B // Watchport/D uses distance sensor + // Default to /P function + +#define ION_DEVICE_ID_PLUS_PWR_HP4CD 0x30C // 5052 Plus Power HubPort/4CD+ (for Dell) +#define ION_DEVICE_ID_PLUS_PWR_HP4C 0x30D // 5052 Plus Power HubPort/4C+ +#define ION_DEVICE_ID_PLUS_PWR_PCI 0x30E // 3410 Plus Power PCI Host Controller 4 port + + +// +// Definitions for AXIOHM USB product IDs +// +#define USB_VENDOR_ID_AXIOHM 0x05D9 // Axiohm VID + +#define AXIOHM_DEVICE_ID_MASK 0xffff +#define AXIOHM_DEVICE_ID_EPIC_A758 0xA758 +#define AXIOHM_DEVICE_ID_EPIC_A794 0xA794 +#define AXIOHM_DEVICE_ID_EPIC_A225 0xA225 + + +// +// Definitions for NCR USB product IDs +// +#define USB_VENDOR_ID_NCR 0x0404 // NCR VID + +#define NCR_DEVICE_ID_MASK 0xffff +#define NCR_DEVICE_ID_EPIC_0202 0x0202 +#define NCR_DEVICE_ID_EPIC_0203 0x0203 +#define NCR_DEVICE_ID_EPIC_0310 0x0310 +#define NCR_DEVICE_ID_EPIC_0311 0x0311 +#define NCR_DEVICE_ID_EPIC_0312 0x0312 + + +// +// Definitions for SYMBOL USB product IDs +// +#define USB_VENDOR_ID_SYMBOL 0x05E0 // Symbol VID +#define SYMBOL_DEVICE_ID_MASK 0xffff +#define SYMBOL_DEVICE_ID_KEYFOB 0x0700 + + +// +// Definitions for other product IDs #define ION_DEVICE_ID_MT4X56USB 0x1403 // OEM device -// BlackBox OEM devices -#define ION_DEVICE_ID_BB_EDGEPORT_4 0x001 // Edgeport/4 RS232 -#define ION_DEVICE_ID_BB_EDGEPORT_4T 0x004 // Edgeport/4 RS232 for Telxon (aka "Fleetport") -#define ION_DEVICE_ID_BB_EDGEPORT_2 0x005 // Edgeport/2 RS232 -#define ION_DEVICE_ID_BB_EDGEPORT_4I 0x006 // Edgeport/4 RS422 -#define ION_DEVICE_ID_BB_EDGEPORT_2I 0x007 // Edgeport/2 RS422/RS485 -#define ION_DEVICE_ID_BB_EDGEPORT_421 0x00C // Edgeport/421 Hub+RS232+Parallel -#define ION_DEVICE_ID_BB_EDGEPORT_21 0x00D // Edgeport/21 RS232+Parallel -#define ION_DEVICE_ID_BB_EDGEPORT_8_DUAL_CPU 0x00E // Half of an Edgeport/8 (the kind with 2 EP/4s on 1 PCB) -#define ION_DEVICE_ID_BB_EDGEPORT_8 0x00F // Edgeport/8 (single-CPU) -#define ION_DEVICE_ID_BB_EDGEPORT_2_DIN 0x010 // Edgeport/2 RS232 with Apple DIN connector -#define ION_DEVICE_ID_BB_EDGEPORT_4_DIN 0x011 // Edgeport/4 RS232 with Apple DIN connector -#define ION_DEVICE_ID_BB_EDGEPORT_16_DUAL_CPU 0x012 // Half of an Edgeport/16 (the kind with 2 EP/8s) -#define ION_DEVICE_ID_BB_EDGEPORT_8I 0x014 // Edgeport/8 RS422 (single-CPU) - - -/* Edgeport TI based devices */ -#define ION_DEVICE_ID_TI_EDGEPORT_4 0x0201 /* Edgeport/4 RS232 */ -#define ION_DEVICE_ID_TI_EDGEPORT_2 0x0205 /* Edgeport/2 RS232 */ -#define ION_DEVICE_ID_TI_EDGEPORT_4I 0x0206 /* Edgeport/4i RS422 */ -#define ION_DEVICE_ID_TI_EDGEPORT_2I 0x0207 /* Edgeport/2i RS422/RS485 */ -#define ION_DEVICE_ID_TI_EDGEPORT_421 0x020C /* Edgeport/421 4 hub 2 RS232 + Parallel (lucent on a different hub port) */ -#define ION_DEVICE_ID_TI_EDGEPORT_21 0x020D /* Edgeport/21 2 RS232 + Parallel (lucent on a different hub port) */ -#define ION_DEVICE_ID_TI_EDGEPORT_1 0x0215 /* Edgeport/1 RS232 */ -#define ION_DEVICE_ID_TI_EDGEPORT_42 0x0217 /* Edgeport/42 4 hub 2 RS232 */ -#define ION_DEVICE_ID_TI_EDGEPORT_22 0x021A /* Edgeport/22 Edgeport/22I is an Edgeport/4 with ports 1&2 RS422 and ports 3&4 RS232 */ -#define ION_DEVICE_ID_TI_EDGEPORT_2C 0x021B /* Edgeport/2c RS232 */ - -#define ION_DEVICE_ID_TI_EDGEPORT_421_BOOT 0x0240 /* Edgeport/421 in boot mode */ -#define ION_DEVICE_ID_TI_EDGEPORT_421_DOWN 0x0241 /* Edgeport/421 in download mode first interface is 2 RS232 (Note that the second interface of this multi interface device should be a standard USB class 7 printer port) */ -#define ION_DEVICE_ID_TI_EDGEPORT_21_BOOT 0x0242 /* Edgeport/21 in boot mode */ -#define ION_DEVICE_ID_TI_EDGEPORT_21_DOWN 0x0243 /*Edgeport/42 in download mode: first interface is 2 RS232 (Note that the second interface of this multi interface device should be a standard USB class 7 printer port) */ +#define GENERATION_ID_FROM_USB_PRODUCT_ID( ProductId ) \ + ( (__u16) ((ProductId >> 8) & (ION_GENERATION_MASK)) ) #define MAKE_USB_PRODUCT_ID( OemId, DeviceId ) \ ( (__u16) (((OemId) << 10) || (DeviceId)) ) @@ -143,7 +234,7 @@ // TxCredits value below which driver won't bother sending (to prevent too many small writes). // Send only if above 25% -#define EDGE_FW_GET_TX_CREDITS_SEND_THRESHOLD(InitialCredit) (max(((InitialCredit) / 4), EDGE_FW_BULK_MAX_PACKET_SIZE)) +#define EDGE_FW_GET_TX_CREDITS_SEND_THRESHOLD(InitialCredit, MaxPacketSize) (max( ((InitialCredit) / 4), (MaxPacketSize) )) #define EDGE_FW_BULK_MAX_PACKET_SIZE 64 // Max Packet Size for Bulk In Endpoint (EP1) #define EDGE_FW_BULK_READ_BUFFER_SIZE 1024 // Size to use for Bulk reads @@ -158,8 +249,8 @@ // Definitions of I/O Networks vendor-specific requests // for default endpoint // -// bmRequestType = 00100000 Set vendor-specific, to device -// bmRequestType = 10100000 Get vendor-specific, to device +// bmRequestType = 01000000 Set vendor-specific, to device +// bmRequestType = 11000000 Get vendor-specific, to device // // These are the definitions for the bRequest field for the // above bmRequestTypes. @@ -184,11 +275,87 @@ #define USB_REQUEST_ION_ENABLE_SUSPEND 9 // Enable/Disable suspend feature // (wValue != 0: Enable; wValue = 0: Disable) +#define USB_REQUEST_ION_SEND_IOSP 10 // Send an IOSP command to the edgeport over the control pipe +#define USB_REQUEST_ION_RECV_IOSP 11 // Receive an IOSP command from the edgeport over the control pipe + + +#define USB_REQUEST_ION_DIS_INT_TIMER 0x80 // Sent to Axiohm to enable/ disable + // interrupt token timer + // wValue = 1, enable (default) + // wValue = 0, disable // // Define parameter values for our vendor-specific commands // +// +// Edgeport Compatiblity Descriptor +// +// This descriptor is only returned by Edgeport-compatible devices +// supporting the EPiC spec. True ION devices do not return this +// descriptor, but instead return STALL on receipt of the +// GET_EPIC_DESC command. The driver interprets a STALL to mean that +// this is a "real" Edgeport. +// + +struct edge_compatibility_bits +{ + // This __u32 defines which Vendor-specific commands/functionality + // the device supports on the default EP0 pipe. + + __u32 VendEnableSuspend : 1; // 0001 Set if device supports ION_ENABLE_SUSPEND + __u32 VendUnused : 31; // Available for future expansion, must be 0 + + // This __u32 defines which IOSP commands are supported over the + // bulk pipe EP1. + + // xxxx Set if device supports: + __u32 IOSPOpen : 1; // 0001 OPEN / OPEN_RSP (Currently must be 1) + __u32 IOSPClose : 1; // 0002 CLOSE + __u32 IOSPChase : 1; // 0004 CHASE / CHASE_RSP + __u32 IOSPSetRxFlow : 1; // 0008 SET_RX_FLOW + __u32 IOSPSetTxFlow : 1; // 0010 SET_TX_FLOW + __u32 IOSPSetXChar : 1; // 0020 SET_XON_CHAR/SET_XOFF_CHAR + __u32 IOSPRxCheck : 1; // 0040 RX_CHECK_REQ/RX_CHECK_RSP + __u32 IOSPSetClrBreak : 1; // 0080 SET_BREAK/CLEAR_BREAK + __u32 IOSPWriteMCR : 1; // 0100 MCR register writes (set/clr DTR/RTS) + __u32 IOSPWriteLCR : 1; // 0200 LCR register writes (wordlen/stop/parity) + __u32 IOSPSetBaudRate : 1; // 0400 setting Baud rate (writes to LCR.80h and DLL/DLM register) + __u32 IOSPDisableIntPipe : 1; // 0800 Do not use the interrupt pipe for TxCredits or RxButesAvailable + __u32 IOSPRxDataAvail : 1; // 1000 Return status of RX Fifo (Data available in Fifo) + __u32 IOSPTxPurge : 1; // 2000 Purge TXBuffer and/or Fifo in Edgeport hardware + __u32 IOSPUnused : 18; // Available for future expansion, must be 0 + + // This __u32 defines which 'general' features are supported + + __u32 TrueEdgeport : 1; // 0001 Set if device is a 'real' Edgeport + // (Used only by driver, NEVER set by an EPiC device) + __u32 GenUnused : 31; // Available for future expansion, must be 0 + +}; + +struct edge_compatibility_descriptor +{ + __u8 Length; // Descriptor Length (per USB spec) + __u8 DescType; // Descriptor Type (per USB spec, =DEVICE type) + __u8 EpicVer; // Version of EPiC spec supported + // (Currently must be 1) + __u8 NumPorts; // Number of serial ports supported + __u8 iDownloadFile; // Index of string containing download code filename + // 0=no download, FF=download compiled into driver. + __u8 Unused[ 3 ]; // Available for future expansion, must be 0 + // (Currently must be 0). + __u8 MajorVersion; // Firmware version: xx. + __u8 MinorVersion; // yy. + __u16 BuildNumber; // zzzz (LE format) + + // The following structure contains __u32s, with each bit + // specifying whether the EPiC device supports the given + // command or functionality. + + struct edge_compatibility_bits Supports; + +}; // Values for iDownloadFile #define EDGE_DOWNLOAD_FILE_NONE 0 // No download requested @@ -272,7 +439,7 @@ struct edge_manuf_descriptor { __u8 NumPorts; // F08 Number of ports __u8 DescDate[3]; // F09 MM/DD/YY when descriptor template was compiler, - // so host can track changes to USB-only descriptors. + // so host can track changes to USB-only descriptors. __u8 SerNumLength; // F0C USB string descriptor len __u8 SerNumDescType; // F0D USB descriptor type (=STRING type) @@ -294,8 +461,8 @@ struct edge_manuf_descriptor { __u8 UartType; // FBD Uart Type __u8 IonPid; // FBE Product ID, == LSB of USB DevDesc.PID - // (Note: Edgeport/4s before 11/98 will have - // 00 here instead of 01) + // (Note: Edgeport/4s before 11/98 will have + // 00 here instead of 01) __u8 IonConfig; // FBF Config byte for ION manufacturing use // FBF end of structure, total len = 3C0h @@ -312,7 +479,7 @@ struct edge_manuf_descriptor { // both 00 and 01 values mean '654. #define MANUF_UART_EXAR_654_EARLY 0 // Exar 16C654 in Edgeport/4s before 11/98 #define MANUF_UART_EXAR_654 1 // Exar 16C654 -#define MANUF_UART_EXAR_2852 2 // Exar 16C2852 +#define MANUF_UART_EXAR_2852 2 // Exar 16C2852 // // Note: The CpuRev and BoardRev values do not conform to manufacturing @@ -334,25 +501,22 @@ struct edge_manuf_descriptor { #define MANUF_BOARD_REV_GENERATION_2 0x20 // Second generaiton edgeport - - // Values of bottom 5 bits of CpuRev & BoardRev for // Implementation 1 (ie, 251+Netchip-based) #define MANUF_CPU_REV_1 1 // C251TB Rev 1 (Need actual Intel rev here) #define MANUF_BOARD_REV_A 1 // First rev of 251+Netchip design - - #define MANUF_SERNUM_LENGTH sizeof(((struct edge_manuf_descriptor *)0)->SerialNumber) #define MANUF_ASSYNUM_LENGTH sizeof(((struct edge_manuf_descriptor *)0)->AssemblyNumber) #define MANUF_OEMASSYNUM_LENGTH sizeof(((struct edge_manuf_descriptor *)0)->OemAssyNumber) #define MANUF_MANUFDATE_LENGTH sizeof(((struct edge_manuf_descriptor *)0)->ManufDate) -#define MANUF_ION_CONFIG_MASTER 0x80 // 1=Master mode, 0=Normal -#define MANUF_ION_CONFIG_DIAG 0x40 // 1=Run h/w diags, 0=norm -#define MANUF_ION_CONFIG_DIAG_NO_LOOP 0x20 // As above but no ext loopback test - +#define MANUF_ION_CONFIG_DIAG_NO_LOOP 0x20 // As below but no ext loopback test +#define MANUF_ION_CONFIG_DIAG 0x40 // 930 based device: 1=Run h/w diags, 0=norm + // TIUMP Device : 1=IONSERIAL needs to run Final Test +#define MANUF_ION_CONFIG_MASTER 0x80 // 930 based device: 1=Master mode, 0=Normal + // TIUMP Device : 1=First device on a multi TIUMP Device // // This structure describes parameters for the boot code, and @@ -398,23 +562,29 @@ struct edge_boot_descriptor { #define BOOT_CAP_RESET_CMD 0x0001 // If set, boot correctly supports ION_RESET_DEVICE - /************************************************************************ T I U M P D E F I N I T I O N S ***********************************************************************/ +// Chip definitions in I2C +#define UMP5152 0x52 +#define UMP3410 0x10 + + //************************************************************************ // TI I2C Format Definitions //************************************************************************ -#define I2C_DESC_TYPE_INFO_BASIC 1 -#define I2C_DESC_TYPE_FIRMWARE_BASIC 2 -#define I2C_DESC_TYPE_DEVICE 3 -#define I2C_DESC_TYPE_CONFIG 4 -#define I2C_DESC_TYPE_STRING 5 -#define I2C_DESC_TYPE_FIRMWARE_BLANK 0xf2 +#define I2C_DESC_TYPE_INFO_BASIC 0x01 +#define I2C_DESC_TYPE_FIRMWARE_BASIC 0x02 +#define I2C_DESC_TYPE_DEVICE 0x03 +#define I2C_DESC_TYPE_CONFIG 0x04 +#define I2C_DESC_TYPE_STRING 0x05 +#define I2C_DESC_TYPE_FIRMWARE_AUTO 0x07 // for 3410 download +#define I2C_DESC_TYPE_CONFIG_KLUDGE 0x14 // for 3410 +#define I2C_DESC_TYPE_WATCHPORT_VERSION 0x15 // firmware version number for watchport +#define I2C_DESC_TYPE_WATCHPORT_CALIBRATION_DATA 0x16 // Watchport Calibration Data -#define I2C_DESC_TYPE_MAX 5 -// 3410 may define types 6, 7 for other firmware downloads +#define I2C_DESC_TYPE_FIRMWARE_BLANK 0xf2 // Special section defined by ION #define I2C_DESC_TYPE_ION 0 // Not defined by TI @@ -428,7 +598,9 @@ struct ti_i2c_desc __u8 Data[0]; // Data starts here }__attribute__((packed)); -struct ti_i2c_firmware_rec +// for 5152 devices only (type 2 record) +// for 3410 the version is stored in the WATCHPORT_FIRMWARE_VERSION descriptor +struct ti_i2c_firmware_rec { __u8 Ver_Major; // Firmware Major version number __u8 Ver_Minor; // Firmware Minor version number @@ -436,6 +608,14 @@ struct ti_i2c_firmware_rec }__attribute__((packed)); +struct watchport_firmware_version +{ +// Added 2 bytes for version number + __u8 Version_Major; // Download Version (for Watchport) + __u8 Version_Minor; +}__attribute__((packed)); + + // Structure of header of download image in fw_down.h struct ti_i2c_image_header { @@ -461,6 +641,15 @@ struct ti_basic_descriptor } __attribute__((packed)); +// CPU / Board Rev Definitions +#define TI_CPU_REV_5052 2 // 5052 based edgeports +#define TI_CPU_REV_3410 3 // 3410 based edgeports + +#define TI_BOARD_REV_TI_EP 0 // Basic ti based edgeport +#define TI_BOARD_REV_COMPACT 1 // Compact board +#define TI_BOARD_REV_WATCHPORT 2 // Watchport + + #define TI_GET_CPU_REVISION(x) (__u8)((((x)>>4)&0x0f)) #define TI_GET_BOARD_REVISION(x) (__u8)(((x)&0x0f)) @@ -469,20 +658,30 @@ struct ti_basic_descriptor #define TI_MAX_I2C_SIZE ( 16 * 1024 ) -/* TI USB 5052 definitions */ +#define TI_MANUF_VERSION_0 0 + +// IonConig2 flags +#define TI_CONFIG2_RS232 0x01 +#define TI_CONFIG2_RS422 0x02 +#define TI_CONFIG2_RS485 0x04 +#define TI_CONFIG2_SWITCHABLE 0x08 + +#define TI_CONFIG2_WATCHPORT 0x10 + + struct edge_ti_manuf_descriptor { __u8 IonConfig; // Config byte for ION manufacturing use __u8 IonConfig2; // Expansion - __u8 Version; // Verqsion + __u8 Version; // Version __u8 CpuRev_BoardRev; // CPU revision level (0xF0) and Board Rev Level (0x0F) __u8 NumPorts; // Number of ports for this UMP __u8 NumVirtualPorts; // Number of Virtual ports __u8 HubConfig1; // Used to configure the Hub __u8 HubConfig2; // Used to configure the Hub __u8 TotalPorts; // Total Number of Com Ports for the entire device (All UMPs) - __u8 Reserved; + __u8 Reserved; // Reserved }__attribute__((packed)); -#endif // if !defined() +#endif // if !defined(_USBVEND_H) --- linux-2.6.9-rc1/drivers/usb/serial/usb-serial.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/drivers/usb/serial/usb-serial.c 2004-09-02 20:51:52.000000000 -0700 @@ -421,6 +421,63 @@ static void return_serial (struct usb_se return; } +static void destroy_serial(struct kref *kref) +{ + struct usb_serial *serial; + struct usb_serial_port *port; + int i; + + serial = to_usb_serial(kref); + + dbg ("%s - %s", __FUNCTION__, serial->type->name); + + serial->type->shutdown(serial); + + /* return the minor range that this device had */ + return_serial(serial); + + for (i = 0; i < serial->num_ports; ++i) + serial->port[i]->open_count = 0; + + /* the ports are cleaned up and released in port_release() */ + for (i = 0; i < serial->num_ports; ++i) + if (serial->port[i]->dev.parent != NULL) { + device_unregister(&serial->port[i]->dev); + serial->port[i] = NULL; + } + + /* If this is a "fake" port, we have to clean it up here, as it will + * not get cleaned up in port_release() as it was never registered with + * the driver core */ + if (serial->num_ports < serial->num_port_pointers) { + for (i = serial->num_ports; i < serial->num_port_pointers; ++i) { + port = serial->port[i]; + if (!port) + continue; + if (port->read_urb) { + usb_unlink_urb(port->read_urb); + usb_free_urb(port->read_urb); + } + if (port->write_urb) { + usb_unlink_urb(port->write_urb); + usb_free_urb(port->write_urb); + } + if (port->interrupt_in_urb) { + usb_unlink_urb(port->interrupt_in_urb); + usb_free_urb(port->interrupt_in_urb); + } + kfree(port->bulk_in_buffer); + kfree(port->bulk_out_buffer); + kfree(port->interrupt_in_buffer); + } + } + + usb_put_dev(serial->dev); + + /* free up any memory that we allocated */ + kfree (serial); +} + /***************************************************************************** * Driver tty interface functions *****************************************************************************/ @@ -465,7 +522,7 @@ static int serial_open (struct tty_struc if (retval) { port->open_count = 0; module_put(serial->type->owner); - kref_put(&serial->kref); + kref_put(&serial->kref, destroy_serial); } } bailout: @@ -496,7 +553,7 @@ static void serial_close(struct tty_stru } module_put(port->serial->type->owner); - kref_put(&port->serial->kref); + kref_put(&port->serial->kref, destroy_serial); } static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count) @@ -654,13 +711,6 @@ exit: ; } -static void serial_shutdown (struct usb_serial *serial) -{ - dbg ("%s", __FUNCTION__); - - serial->type->shutdown(serial); -} - static int serial_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) { struct usb_serial *serial; @@ -694,7 +744,7 @@ static int serial_read_proc (char *page, begin += length; length = 0; } - kref_put(&serial->kref); + kref_put(&serial->kref, destroy_serial); } *eof = 1; done: @@ -763,62 +813,6 @@ void usb_serial_port_softint(void *priva wake_up_interruptible(&tty->write_wait); } -static void destroy_serial(struct kref *kref) -{ - struct usb_serial *serial; - struct usb_serial_port *port; - int i; - - serial = to_usb_serial(kref); - - dbg ("%s - %s", __FUNCTION__, serial->type->name); - serial_shutdown (serial); - - /* return the minor range that this device had */ - return_serial(serial); - - for (i = 0; i < serial->num_ports; ++i) - serial->port[i]->open_count = 0; - - /* the ports are cleaned up and released in port_release() */ - for (i = 0; i < serial->num_ports; ++i) - if (serial->port[i]->dev.parent != NULL) { - device_unregister(&serial->port[i]->dev); - serial->port[i] = NULL; - } - - /* If this is a "fake" port, we have to clean it up here, as it will - * not get cleaned up in port_release() as it was never registered with - * the driver core */ - if (serial->num_ports < serial->num_port_pointers) { - for (i = serial->num_ports; i < serial->num_port_pointers; ++i) { - port = serial->port[i]; - if (!port) - continue; - if (port->read_urb) { - usb_unlink_urb(port->read_urb); - usb_free_urb(port->read_urb); - } - if (port->write_urb) { - usb_unlink_urb(port->write_urb); - usb_free_urb(port->write_urb); - } - if (port->interrupt_in_urb) { - usb_unlink_urb(port->interrupt_in_urb); - usb_free_urb(port->interrupt_in_urb); - } - kfree(port->bulk_in_buffer); - kfree(port->bulk_out_buffer); - kfree(port->interrupt_in_buffer); - } - } - - usb_put_dev(serial->dev); - - /* free up any memory that we allocated */ - kfree (serial); -} - static void port_release(struct device *dev) { struct usb_serial_port *port = to_usb_serial_port(dev); @@ -859,7 +853,7 @@ static struct usb_serial * create_serial serial->interface = interface; serial->vendor = dev->descriptor.idVendor; serial->product = dev->descriptor.idProduct; - kref_init(&serial->kref, destroy_serial); + kref_init(&serial->kref); return serial; } @@ -1209,7 +1203,7 @@ void usb_serial_disconnect(struct usb_in if (serial) { /* let the last holder of this object * cause it to be cleaned up */ - kref_put(&serial->kref); + kref_put(&serial->kref, destroy_serial); } dev_info(dev, "device disconnected\n"); } --- linux-2.6.9-rc1/drivers/usb/storage/transport.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/drivers/usb/storage/transport.c 2004-09-02 20:51:52.000000000 -0700 @@ -911,6 +911,7 @@ int usb_stor_Bulk_max_lun(struct us_data int result; /* issue the command */ + us->iobuf[0] = 0; result = usb_stor_control_msg(us, us->recv_ctrl_pipe, US_BULK_GET_MAX_LUN, USB_DIR_IN | USB_TYPE_CLASS | @@ -921,7 +922,7 @@ int usb_stor_Bulk_max_lun(struct us_data result, us->iobuf[0]); /* if we have a successful request, return the result */ - if (result == 1) + if (result >= 0) return us->iobuf[0]; /* --- linux-2.6.9-rc1/drivers/usb/storage/unusual_devs.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/drivers/usb/storage/unusual_devs.h 2004-09-02 20:51:52.000000000 -0700 @@ -490,6 +490,13 @@ UNUSUAL_DEV( 0x066b, 0x0105, 0x0100, 0x US_FL_SINGLE_LUN ), #endif +/* Reported by Darsen Lu */ +UNUSUAL_DEV( 0x066f, 0x8000, 0x0001, 0x0001, + "SigmaTel", + "USBMSC Audio Player", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY ), + /* Submitted by Benny Sjostrand */ UNUSUAL_DEV( 0x0686, 0x4011, 0x0001, 0x0001, "Minolta", --- linux-2.6.9-rc1/drivers/video/bw2.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/drivers/video/bw2.c 2004-09-02 20:51:52.000000000 -0700 @@ -338,7 +338,7 @@ static void bw2_init_one(struct sbus_dev } #endif all->info.var.red.length = all->info.var.green.length = - all->info.var.blue.length = all_info.var.bits_per_pixel; + all->info.var.blue.length = all->info.var.bits_per_pixel; all->info.var.red.offset = all->info.var.green.offset = all->info.var.blue.offset = 0; --- linux-2.6.9-rc1/drivers/video/console/Kconfig 2004-08-24 00:02:23.000000000 -0700 +++ 25/drivers/video/console/Kconfig 2004-09-02 20:51:52.000000000 -0700 @@ -43,7 +43,7 @@ config VIDEO_SELECT about the Video mode selection support. If unsure, say N. config MDA_CONSOLE - depends on !M68K + depends on !M68K && ISA tristate "MDA text console (dual-headed) (EXPERIMENTAL)" ---help--- Say Y here if you have an old MDA or monochrome Hercules graphics --- linux-2.6.9-rc1/drivers/video/fbmem.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/drivers/video/fbmem.c 2004-09-02 20:51:52.000000000 -0700 @@ -878,6 +878,8 @@ fb_read(struct file *file, char __user * struct inode *inode = file->f_dentry->d_inode; int fbidx = iminor(inode); struct fb_info *info = registered_fb[fbidx]; + u32 *buffer, *dst, *src; + int c, i, cnt = 0, err = 0; if (!info || ! info->screen_base) return -ENODEV; @@ -894,18 +896,45 @@ fb_read(struct file *file, char __user * count = info->fix.smem_len; if (count + p > info->fix.smem_len) count = info->fix.smem_len - p; + + cnt = 0; + buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, + GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + src = (u32 *) (info->screen_base + p); + if (info->fbops->fb_sync) info->fbops->fb_sync(info); - if (count) { - char *base_addr; - base_addr = info->screen_base; - count -= copy_to_user(buf, base_addr+p, count); - if (!count) - return -EFAULT; - *ppos += count; + while (count) { + c = (count > PAGE_SIZE) ? PAGE_SIZE : count; + dst = buffer; + for (i = c >> 2; i--; ) + *dst++ = fb_readl(src++); + if (c & 3) { + u8 *dst8 = (u8 *) dst; + u8 *src8 = (u8 *) src; + + for (i = c & 3; i--;) + *dst8++ = fb_readb(src8++); + + src = (u32 *) src8; + } + + if (copy_to_user(buf, buffer, c)) { + err = -EFAULT; + break; + } + *ppos += c; + buf += c; + cnt += c; + count -= c; } - return count; + + kfree(buffer); + return (err) ? err : cnt; } static ssize_t @@ -915,7 +944,8 @@ fb_write(struct file *file, const char _ struct inode *inode = file->f_dentry->d_inode; int fbidx = iminor(inode); struct fb_info *info = registered_fb[fbidx]; - int err; + u32 *buffer, *dst, *src; + int c, i, cnt = 0, err; if (!info || !info->screen_base) return -ENODEV; @@ -935,19 +965,43 @@ fb_write(struct file *file, const char _ count = info->fix.smem_len - p; err = -ENOSPC; } + cnt = 0; + buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, + GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + dst = (u32 *) (info->screen_base + p); + if (info->fbops->fb_sync) info->fbops->fb_sync(info); - if (count) { - char *base_addr; - base_addr = info->screen_base; - count -= copy_from_user(base_addr+p, buf, count); - *ppos += count; - err = -EFAULT; + while (count) { + c = (count > PAGE_SIZE) ? PAGE_SIZE : count; + src = buffer; + if (copy_from_user(src, buf, c)) { + err = -EFAULT; + break; + } + for (i = c >> 2; i--; ) + fb_writel(*src++, dst++); + if (c & 3) { + u8 *src8 = (u8 *) src; + u8 *dst8 = (u8 *) dst; + + for (i = c & 3; i--; ) + fb_writeb(*src8++, dst8++); + + dst = (u32 *) dst8; + } + *ppos += c; + buf += c; + cnt += c; + count -= c; } - if (count) - return count; - return err; + kfree(buffer); + + return (err) ? err : cnt; } #ifdef CONFIG_KMOD @@ -974,7 +1028,7 @@ fb_cursor(struct fb_info *info, struct f { struct fb_cursor_user cursor_user; struct fb_cursor cursor; - char *data = NULL, *mask = NULL; + char *data = NULL, *mask = NULL, *info_mask = NULL; u16 *red = NULL, *green = NULL, *blue = NULL, *transp = NULL; int err = -EINVAL; @@ -982,12 +1036,12 @@ fb_cursor(struct fb_info *info, struct f return -EFAULT; memcpy(&cursor, &cursor_user, sizeof(cursor_user)); - cursor.mask = NULL; - cursor.image.data = NULL; - cursor.image.cmap.red = NULL; - cursor.image.cmap.green = NULL; - cursor.image.cmap.blue = NULL; - cursor.image.cmap.transp = NULL; + cursor.mask = info->cursor.mask; + cursor.image.data = info->cursor.image.data; + cursor.image.cmap.red = info->cursor.image.cmap.red; + cursor.image.cmap.green = info->cursor.image.cmap.green; + cursor.image.cmap.blue = info->cursor.image.cmap.blue; + cursor.image.cmap.transp = info->cursor.image.cmap.transp; cursor.data = NULL; if (cursor.set & FB_CUR_SETCUR) @@ -1047,6 +1101,8 @@ fb_cursor(struct fb_info *info, struct f cursor.image.data = data; cursor.mask = mask; + info_mask = (char *) info->cursor.mask; + info->cursor.mask = mask; } info->cursor.set = cursor.set; info->cursor.rop = cursor.rop; @@ -1058,6 +1114,8 @@ out: kfree(green); kfree(blue); kfree(transp); + if (info_mask) + info->cursor.mask = info_mask; return err; } --- linux-2.6.9-rc1/drivers/video/ffb.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/drivers/video/ffb.c 2004-09-02 20:51:52.000000000 -0700 @@ -362,61 +362,6 @@ struct ffb_par { struct list_head list; }; -#undef FFB_DO_DEBUG_LOG - -#ifdef FFB_DO_DEBUG_LOG -#define FFB_DEBUG_LOG_ENTS 32 -static struct ffb_log { - int op; -#define OP_FILLRECT 1 -#define OP_IMAGEBLIT 2 - - int depth, x, y, w, h; -} ffb_debug_log[FFB_DEBUG_LOG_ENTS]; -static int ffb_debug_log_ent; - -static void ffb_do_log(unsigned long unused) -{ - int i; - - for (i = 0; i < FFB_DEBUG_LOG_ENTS; i++) { - struct ffb_log *p = &ffb_debug_log[i]; - - printk("FFB_LOG: OP[%s] depth(%d) x(%d) y(%d) w(%d) h(%d)\n", - (p->op == OP_FILLRECT ? "FILLRECT" : "IMAGEBLIT"), - p->depth, p->x, p->y, p->w, p->h); - } -} -static struct timer_list ffb_log_timer = - TIMER_INITIALIZER(ffb_do_log, 0, 0); - -static void ffb_log(int op, int depth, int x, int y, int w, int h) -{ - if (ffb_debug_log_ent < FFB_DEBUG_LOG_ENTS) { - struct ffb_log *p = &ffb_debug_log[ffb_debug_log_ent]; - - if (ffb_debug_log_ent != 0 && - p[-1].op == op && p[-1].depth == depth) - return; - p->op = op; - p->depth = depth; - p->x = x; - p->y = y; - p->w = w; - p->h = h; - - if (++ffb_debug_log_ent == FFB_DEBUG_LOG_ENTS) { - ffb_log_timer.expires = jiffies + 2; - add_timer(&ffb_log_timer); - } - } -} -#else -#define ffb_log(a,b,c,d,e,f) do { } while(0) -#endif - -#undef FORCE_WAIT_EVERY_ROP - static void FFBFifo(struct ffb_par *par, int n) { struct ffb_fbc *fbc; @@ -479,7 +424,7 @@ static void ffb_switch_from_graph(struct upa_writel(0x2000707f, &fbc->fbc); upa_writel(par->rop_cache, &fbc->rop); upa_writel(0xffffffff, &fbc->pmask); - upa_writel((0 << 16) | (32 << 0), &fbc->fontinc); + upa_writel((1 << 16) | (0 << 0), &fbc->fontinc); upa_writel(par->fg_cache, &fbc->fg); upa_writel(par->bg_cache, &fbc->bg); FFBWait(par); @@ -526,8 +471,6 @@ static void ffb_fillrect(struct fb_info if (rect->rop != ROP_COPY && rect->rop != ROP_XOR) BUG(); - ffb_log(OP_FILLRECT, 0, rect->dx, rect->dy, rect->width, rect->height); - fg = ((u32 *)info->pseudo_palette)[rect->color]; spin_lock_irqsave(&par->lock, flags); @@ -548,9 +491,6 @@ static void ffb_fillrect(struct fb_info upa_writel(rect->dx, &fbc->bx); upa_writel(rect->height, &fbc->bh); upa_writel(rect->width, &fbc->bw); -#ifdef FORCE_WAIT_EVERY_ROP - FFBWait(par); -#endif spin_unlock_irqrestore(&par->lock, flags); } @@ -572,7 +512,7 @@ ffb_copyarea(struct fb_info *info, const unsigned long flags; if (area->dx != area->sx || - area->dy == area->dy) { + area->dy == area->sy) { cfb_copyarea(info, area); return; } @@ -609,10 +549,7 @@ static void ffb_imageblit(struct fb_info unsigned long flags; u32 fg, bg, xy; u64 fgbg; - int i, width; - - ffb_log(OP_IMAGEBLIT, image->depth, - image->dx, image->dy, image->width, image->height); + int i, width, stride; if (image->depth > 1) { cfb_imageblit(info, image); @@ -623,6 +560,8 @@ static void ffb_imageblit(struct fb_info bg = ((u32 *)info->pseudo_palette)[image->bg_color]; fgbg = ((u64) fg << 32) | (u64) bg; xy = (image->dy << 16) | image->dx; + width = image->width; + stride = ((width + 7) >> 3); spin_lock_irqsave(&par->lock, flags); @@ -632,55 +571,49 @@ static void ffb_imageblit(struct fb_info *(u64 *)&par->fg_cache = fgbg; } - ffb_rop(par, FFB_ROP_NEW); + if (width >= 32) { + FFBFifo(par, 1); + upa_writel(32, &fbc->fontw); + } - for (i = 0; i < image->height; i++) { - width = image->width; + while (width >= 32) { + const u8 *next_data = data + 4; FFBFifo(par, 1); upa_writel(xy, &fbc->fontxy); - xy += (1 << 16); - - while (width >= 32) { - u32 val; - - FFBFifo(par, 2); - upa_writel(32, &fbc->fontw); + xy += (32 << 0); - val = ((u32)data[0] << 24) | - ((u32)data[1] << 16) | - ((u32)data[2] << 8) | - ((u32)data[3] << 0); + for (i = 0; i < image->height; i++) { + u32 val = (((u32)data[0] << 24) | + ((u32)data[1] << 16) | + ((u32)data[2] << 8) | + ((u32)data[3] << 0)); + FFBFifo(par, 1); upa_writel(val, &fbc->font); - data += 4; - width -= 32; + data += stride; } - if (width) { - u32 val; + data = next_data; + width -= 32; + } + + if (width) { + FFBFifo(par, 2); + upa_writel(width, &fbc->fontw); + upa_writel(xy, &fbc->fontxy); - FFBFifo(par, 2); - upa_writel(width, &fbc->fontw); - if (width <= 8) { - val = (u32) data[0] << 24; - data += 1; - } else if (width <= 16) { - val = ((u32) data[0] << 24) | - ((u32) data[1] << 16); - data += 2; - } else { - val = ((u32) data[0] << 24) | - ((u32) data[1] << 16) | - ((u32) data[2] << 8); - data += 3; - } + for (i = 0; i < image->height; i++) { + u32 val = (((u32)data[0] << 24) | + ((u32)data[1] << 16) | + ((u32)data[2] << 8) | + ((u32)data[3] << 0)); + FFBFifo(par, 1); upa_writel(val, &fbc->font); + + data += stride; } } -#ifdef FORCE_WAIT_EVERY_ROP - FFBWait(par); -#endif spin_unlock_irqrestore(&par->lock, flags); } @@ -1027,7 +960,13 @@ static void ffb_init_one(int node, int p all->par.prom_node = node; all->par.prom_parent_node = parent; - all->info.flags = FBINFO_DEFAULT; + /* Don't mention copyarea, so SCROLL_REDRAW is always + * used. It is the fastest on this chip. + */ + all->info.flags = (FBINFO_DEFAULT | + /* FBINFO_HWACCEL_COPYAREA | */ + FBINFO_HWACCEL_FILLRECT | + FBINFO_HWACCEL_IMAGEBLIT); all->info.fbops = &ffb_ops; all->info.screen_base = (char *) all->par.physbase + FFB_DFB24_POFF; all->info.currcon = -1; --- linux-2.6.9-rc1/drivers/video/Makefile 2004-08-24 00:01:54.000000000 -0700 +++ 25/drivers/video/Makefile 2004-09-02 20:51:52.000000000 -0700 @@ -34,7 +34,11 @@ obj-$(CONFIG_FB_CYBER) += cyb obj-$(CONFIG_FB_CYBER2000) += cyber2000fb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o obj-$(CONFIG_FB_GBE) += gbefb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o obj-$(CONFIG_FB_SGIVW) += sgivwfb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o -obj-$(CONFIG_FB_3DFX) += tdfxfb.o cfbimgblt.o +obj-$(CONFIG_FB_3DFX) += tdfxfb.o tdfxfb_lib.o +tdfxfb_lib-y := cfbimgblt.o +ifneq ($(CONFIG_FB_3DFX_ACCEL),y) +tdfxfb_lib-y += cfbfillrect.o cfbcopyarea.o +endif obj-$(CONFIG_FB_MAC) += macfb.o macmodes.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o obj-$(CONFIG_FB_HP300) += hpfb.o cfbfillrect.o cfbimgblt.o obj-$(CONFIG_FB_OF) += offb.o cfbfillrect.o cfbimgblt.o cfbcopyarea.o --- linux-2.6.9-rc1/drivers/video/riva/fbdev.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/drivers/video/riva/fbdev.c 2004-09-03 00:57:16.844478512 -0700 @@ -1713,7 +1713,7 @@ static int __devinit riva_get_EDID_OF(st } #endif /* CONFIG_PPC_OF */ -#ifdef CONFIG_FB_RIVA_I2C +#if defined(CONFIG_FB_RIVA_I2C) && !defined(CONFIG_PPC_OF) static int __devinit riva_get_EDID_i2c(struct fb_info *info) { struct riva_par *par = (struct riva_par *) info->par; --- linux-2.6.9-rc1/drivers/video/softcursor.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/drivers/video/softcursor.c 2004-09-02 20:51:52.000000000 -0700 @@ -22,7 +22,8 @@ int soft_cursor(struct fb_info *info, st unsigned int scan_align = info->sprite.scan_align - 1; unsigned int buf_align = info->sprite.buf_align - 1; unsigned int i, size, dsize, s_pitch, d_pitch; - u8 *dst, src[64]; + struct fb_cursor *cur; + u8 *dst, *src; if (cursor->set & FB_CUR_SETSIZE) { info->cursor.image.height = cursor->image.height; @@ -48,9 +49,17 @@ int soft_cursor(struct fb_info *info, st info->cursor.image.depth = cursor->image.depth; } + info->cursor.image.data = cursor->image.data; + if (info->state != FBINFO_STATE_RUNNING) return 0; + src = kmalloc(64 + sizeof(struct fb_cursor), GFP_ATOMIC); + if (!src) + return -ENOMEM; + cur = (struct fb_cursor *) (src + 64); + *cur = info->cursor; + s_pitch = (info->cursor.image.width + 7) >> 3; dsize = s_pitch * info->cursor.image.height; d_pitch = (s_pitch + scan_align) & ~scan_align; @@ -79,9 +88,12 @@ int soft_cursor(struct fb_info *info, st else fb_sysmove_buf_aligned(info, &info->sprite, dst, d_pitch, src, s_pitch, info->cursor.image.height); - info->cursor.image.data = dst; + cur->image.data = dst; - info->fbops->fb_imageblit(info, &info->cursor.image); + info->fbops->fb_imageblit(info, &cur->image); + + kfree(src); + return 0; } --- linux-2.6.9-rc1/fs/adfs/super.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/fs/adfs/super.c 2004-09-02 20:51:52.000000000 -0700 @@ -241,7 +241,7 @@ static int init_inodecache(void) { adfs_inode_cachep = kmem_cache_create("adfs_inode_cache", sizeof(struct adfs_inode_info), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (adfs_inode_cachep == NULL) return -ENOMEM; --- linux-2.6.9-rc1/fs/affs/super.c 2004-08-24 00:03:17.000000000 -0700 +++ 25/fs/affs/super.c 2004-09-02 20:51:52.000000000 -0700 @@ -115,7 +115,7 @@ static int init_inodecache(void) { affs_inode_cachep = kmem_cache_create("affs_inode_cache", sizeof(struct affs_inode_info), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (affs_inode_cachep == NULL) return -ENOMEM; --- linux-2.6.9-rc1/fs/afs/main.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/fs/afs/main.c 2004-09-03 00:56:55.614705928 -0700 @@ -10,6 +10,7 @@ */ #include +#include #include #include #include @@ -47,7 +48,7 @@ MODULE_LICENSE("GPL"); static char *rootcell; -MODULE_PARM(rootcell, "s"); +module_param(rootcell, charp, 0); MODULE_PARM_DESC(rootcell, "root AFS cell name and VL server IP addr list"); @@ -99,7 +100,7 @@ static int afs_init(void) goto error; #endif -#ifdef CONFIG_KEYS +#ifdef CONFIG_KEYS_TURNED_OFF ret = afs_key_register(); if (ret < 0) goto error_cache; @@ -141,7 +142,7 @@ static int afs_init(void) error_kafstimod: afs_kafstimod_stop(); error_keys: -#ifdef CONFIG_KEYS +#ifdef CONFIG_KEYS_TURNED_OFF afs_key_unregister(); error_cache: #endif @@ -168,7 +169,7 @@ static void __exit afs_exit(void) afs_kafstimod_stop(); afs_kafsasyncd_stop(); afs_cell_purge(); -#ifdef CONFIG_KEYS +#ifdef CONFIG_KEYS_TURNED_OFF afs_key_unregister(); #endif #ifdef AFS_CACHING_SUPPORT --- linux-2.6.9-rc1/fs/afs/mntpt.c 2004-08-24 00:02:20.000000000 -0700 +++ 25/fs/afs/mntpt.c 2004-09-02 20:51:52.000000000 -0700 @@ -235,8 +235,8 @@ static struct vfsmount *afs_mntpt_do_aut */ static int afs_mntpt_follow_link(struct dentry *dentry, struct nameidata *nd) { - struct nameidata newnd; struct vfsmount *newmnt; + struct dentry *old_dentry; int err; kenter("%p{%s},{%s:%p{%s}}", @@ -247,15 +247,19 @@ static int afs_mntpt_follow_link(struct nd->dentry->d_name.name); newmnt = afs_mntpt_do_automount(dentry); - if (IS_ERR(newmnt)) + if (IS_ERR(newmnt)) { + path_release(nd); return PTR_ERR(newmnt); + } - newnd = *nd; - newnd.dentry = dentry; - err = do_add_mount(newmnt, &newnd, 0, &afs_vfsmounts); + old_dentry = nd->dentry; + nd->dentry = dentry; + err = do_add_mount(newmnt, nd, 0, &afs_vfsmounts); + nd->dentry = old_dentry; + + path_release(nd); if (!err) { - path_release(nd); mntget(newmnt); nd->mnt = newmnt; dget(newmnt->mnt_root); --- linux-2.6.9-rc1/fs/aio.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/fs/aio.c 2004-09-03 00:56:59.328141400 -0700 @@ -39,6 +39,9 @@ #define dprintk(x...) do { ; } while (0) #endif +long aio_run = 0; /* for testing only */ +long aio_wakeups = 0; /* for testing only */ + /*------ sysctl variables----*/ atomic_t aio_nr = ATOMIC_INIT(0); /* current system wide number of aio requests */ unsigned aio_max_nr = 0x10000; /* system wide maximum number of aio requests */ @@ -277,6 +280,7 @@ static void aio_cancel_all(struct kioctx struct kiocb *iocb = list_kiocb(pos); list_del_init(&iocb->ki_list); cancel = iocb->ki_cancel; + kiocbSetCancelled(iocb); if (cancel) { iocb->ki_users++; spin_unlock_irq(&ctx->ctx_lock); @@ -337,6 +341,11 @@ void fastcall exit_aio(struct mm_struct aio_cancel_all(ctx); wait_for_all_aios(ctx); + /* + * this is an overkill, but ensures we don't leave + * the ctx on the aio_wq + */ + flush_workqueue(aio_wq); if (1 != atomic_read(&ctx->users)) printk(KERN_DEBUG @@ -359,6 +368,8 @@ void fastcall __put_ioctx(struct kioctx if (unlikely(ctx->reqs_active)) BUG(); + cancel_delayed_work(&ctx->wq); + flush_workqueue(aio_wq); aio_free_ring(ctx); mmdrop(ctx->mm); ctx->mm = NULL; @@ -398,6 +409,7 @@ static struct kiocb fastcall *__aio_get_ req->ki_obj.user = NULL; req->ki_dtor = NULL; req->private = NULL; + INIT_LIST_HEAD(&req->ki_run_list); /* Check if the completion queue has enough free space to * accept an event from this io. @@ -543,85 +555,367 @@ struct kioctx *lookup_ioctx(unsigned lon return ioctx; } -static void use_mm(struct mm_struct *mm) +/* + * use_mm + * Makes the calling kernel thread take on the specified + * mm context. + * Called by the retry thread execute retries within the + * iocb issuer's mm context, so that copy_from/to_user + * operations work seamlessly for aio. + * (Note: this routine is intended to be called only + * from a kernel thread context) + */ +void use_mm(struct mm_struct *mm) { struct mm_struct *active_mm; + struct task_struct *tsk = current; + task_lock(tsk); + active_mm = tsk->active_mm; atomic_inc(&mm->mm_count); - task_lock(current); - active_mm = current->active_mm; - current->mm = mm; - if (mm != active_mm) { - current->active_mm = mm; - activate_mm(active_mm, mm); - } - task_unlock(current); + tsk->mm = mm; + tsk->active_mm = mm; + activate_mm(active_mm, mm); + task_unlock(tsk); + mmdrop(active_mm); } -static void unuse_mm(struct mm_struct *mm) +/* + * unuse_mm + * Reverses the effect of use_mm, i.e. releases the + * specified mm context which was earlier taken on + * by the calling kernel thread + * (Note: this routine is intended to be called only + * from a kernel thread context) + * + * Comments: Called with ctx->ctx_lock held. This nests + * task_lock instead ctx_lock. + */ +void unuse_mm(struct mm_struct *mm) { - task_lock(current); - current->mm = NULL; - task_unlock(current); + struct task_struct *tsk = current; + + task_lock(tsk); + tsk->mm = NULL; /* active_mm is still 'mm' */ - enter_lazy_tlb(mm, current); + enter_lazy_tlb(mm, tsk); + task_unlock(tsk); } -/* Run on kevent's context. FIXME: needs to be per-cpu and warn if an - * operation blocks. +/* + * Queue up a kiocb to be retried. Assumes that the kiocb + * has already been marked as kicked, and places it on + * the retry run list for the corresponding ioctx, if it + * isn't already queued. Returns 1 if it actually queued + * the kiocb (to tell the caller to activate the work + * queue to process it), or 0, if it found that it was + * already queued. + * + * Should be called with the spin lock iocb->ki_ctx->ctx_lock + * held */ -static void aio_kick_handler(void *data) +static inline int __queue_kicked_iocb(struct kiocb *iocb) { - struct kioctx *ctx = data; + struct kioctx *ctx = iocb->ki_ctx; - use_mm(ctx->mm); + if (list_empty(&iocb->ki_run_list)) { + list_add_tail(&iocb->ki_run_list, + &ctx->run_list); + iocb->ki_queued++; + return 1; + } + return 0; +} - spin_lock_irq(&ctx->ctx_lock); - while (!list_empty(&ctx->run_list)) { - struct kiocb *iocb; - long ret; +/* aio_run_iocb + * This is the core aio execution routine. It is + * invoked both for initial i/o submission and + * subsequent retries via the aio_kick_handler. + * Expects to be invoked with iocb->ki_ctx->lock + * already held. The lock is released and reaquired + * as needed during processing. + * + * Calls the iocb retry method (already setup for the + * iocb on initial submission) for operation specific + * handling, but takes care of most of common retry + * execution details for a given iocb. The retry method + * needs to be non-blocking as far as possible, to avoid + * holding up other iocbs waiting to be serviced by the + * retry kernel thread. + * + * The trickier parts in this code have to do with + * ensuring that only one retry instance is in progress + * for a given iocb at any time. Providing that guarantee + * simplifies the coding of individual aio operations as + * it avoids various potential races. + */ +static ssize_t aio_run_iocb(struct kiocb *iocb) +{ + struct kioctx *ctx = iocb->ki_ctx; + ssize_t (*retry)(struct kiocb *); + ssize_t ret; - iocb = list_entry(ctx->run_list.next, struct kiocb, - ki_run_list); - list_del(&iocb->ki_run_list); - iocb->ki_users ++; - spin_unlock_irq(&ctx->ctx_lock); + if (iocb->ki_retried++ > 1024*1024) { + printk("Maximal retry count. Bytes done %Zd\n", + iocb->ki_nbytes - iocb->ki_left); + return -EAGAIN; + } + + if (!(iocb->ki_retried & 0xff)) { + pr_debug("%ld retry: %d of %d (kick %ld, Q %ld run %ld, wake %ld)\n", + iocb->ki_retried, + iocb->ki_nbytes - iocb->ki_left, iocb->ki_nbytes, + iocb->ki_kicked, iocb->ki_queued, aio_run, aio_wakeups); + } + + if (!(retry = iocb->ki_retry)) { + printk("aio_run_iocb: iocb->ki_retry = NULL\n"); + return 0; + } + + /* + * We don't want the next retry iteration for this + * operation to start until this one has returned and + * updated the iocb state. However, wait_queue functions + * can trigger a kick_iocb from interrupt context in the + * meantime, indicating that data is available for the next + * iteration. We want to remember that and enable the + * next retry iteration _after_ we are through with + * this one. + * + * So, in order to be able to register a "kick", but + * prevent it from being queued now, we clear the kick + * flag, but make the kick code *think* that the iocb is + * still on the run list until we are actually done. + * When we are done with this iteration, we check if + * the iocb was kicked in the meantime and if so, queue + * it up afresh. + */ + + kiocbClearKicked(iocb); + + /* + * This is so that aio_complete knows it doesn't need to + * pull the iocb off the run list (We can't just call + * INIT_LIST_HEAD because we don't want a kick_iocb to + * queue this on the run list yet) + */ + iocb->ki_run_list.next = iocb->ki_run_list.prev = NULL; + spin_unlock_irq(&ctx->ctx_lock); + + /* Quit retrying if the i/o has been cancelled */ + if (kiocbIsCancelled(iocb)) { + ret = -EINTR; + aio_complete(iocb, ret, 0); + /* must not access the iocb after this */ + goto out; + } - kiocbClearKicked(iocb); - ret = iocb->ki_retry(iocb); - if (-EIOCBQUEUED != ret) { + /* + * Now we are all set to call the retry method in async + * context. By setting this thread's io_wait context + * to point to the wait queue entry inside the currently + * running iocb for the duration of the retry, we ensure + * that async notification wakeups are queued by the + * operation instead of blocking waits, and when notified, + * cause the iocb to be kicked for continuation (through + * the aio_wake_function callback). + */ + BUG_ON(current->io_wait != NULL); + current->io_wait = &iocb->ki_wait; + ret = retry(iocb); + current->io_wait = NULL; + + if (-EIOCBRETRY != ret) { + if (-EIOCBQUEUED != ret) { + BUG_ON(!list_empty(&iocb->ki_wait.task_list)); aio_complete(iocb, ret, 0); - iocb = NULL; + /* must not access the iocb after this */ } + } else { + /* + * Issue an additional retry to avoid waiting forever if + * no waits were queued (e.g. in case of a short read). + */ + if (list_empty(&iocb->ki_wait.task_list)) + kiocbSetKicked(iocb); + } +out: + spin_lock_irq(&ctx->ctx_lock); - spin_lock_irq(&ctx->ctx_lock); - if (NULL != iocb) - __aio_put_req(ctx, iocb); + if (-EIOCBRETRY == ret) { + /* + * OK, now that we are done with this iteration + * and know that there is more left to go, + * this is where we let go so that a subsequent + * "kick" can start the next iteration + */ + + /* will make __queue_kicked_iocb succeed from here on */ + INIT_LIST_HEAD(&iocb->ki_run_list); + /* we must queue the next iteration ourselves, if it + * has already been kicked */ + if (kiocbIsKicked(iocb)) { + __queue_kicked_iocb(iocb); + } } + return ret; +} + +/* + * __aio_run_iocbs: + * Process all pending retries queued on the ioctx + * run list. + * Assumes it is operating within the aio issuer's mm + * context. Expects to be called with ctx->ctx_lock held + */ +static int __aio_run_iocbs(struct kioctx *ctx) +{ + struct kiocb *iocb; + int count = 0; + LIST_HEAD(run_list); + + list_splice_init(&ctx->run_list, &run_list); + while (!list_empty(&run_list)) { + iocb = list_entry(run_list.next, struct kiocb, + ki_run_list); + list_del(&iocb->ki_run_list); + /* + * Hold an extra reference while retrying i/o. + */ + iocb->ki_users++; /* grab extra reference */ + aio_run_iocb(iocb); + if (__aio_put_req(ctx, iocb)) /* drop extra ref */ + put_ioctx(ctx); + count++; + } + aio_run++; + if (!list_empty(&ctx->run_list)) + return 1; + return 0; +} + +static void aio_queue_work(struct kioctx * ctx) +{ + unsigned long timeout; + /* + * if someone is waiting, get the work started right + * away, otherwise, use a longer delay + */ + smp_mb(); + if (waitqueue_active(&ctx->wait)) + timeout = 1; + else + timeout = HZ/10; + queue_delayed_work(aio_wq, &ctx->wq, timeout); +} + + +/* + * aio_run_iocbs: + * Process all pending retries queued on the ioctx + * run list. + * Assumes it is operating within the aio issuer's mm + * context. + */ +static inline void aio_run_iocbs(struct kioctx *ctx) +{ + int requeue; + + spin_lock_irq(&ctx->ctx_lock); + + requeue = __aio_run_iocbs(ctx); spin_unlock_irq(&ctx->ctx_lock); + if (requeue) + aio_queue_work(ctx); +} - unuse_mm(ctx->mm); +/* + * just like aio_run_iocbs, but keeps running them until + * the list stays empty + */ +static inline void aio_run_all_iocbs(struct kioctx *ctx) +{ + spin_lock_irq(&ctx->ctx_lock); + while (__aio_run_iocbs(ctx)) + ; + spin_unlock_irq(&ctx->ctx_lock); } -void fastcall kick_iocb(struct kiocb *iocb) +/* + * aio_kick_handler: + * Work queue handler triggered to process pending + * retries on an ioctx. Takes on the aio issuer's + * mm context before running the iocbs, so that + * copy_xxx_user operates on the issuer's address + * space. + * Run on aiod's context. + */ +static void aio_kick_handler(void *data) { - struct kioctx *ctx = iocb->ki_ctx; + struct kioctx *ctx = data; + mm_segment_t oldfs = get_fs(); + int requeue; + + set_fs(USER_DS); + use_mm(ctx->mm); + spin_lock_irq(&ctx->ctx_lock); + requeue =__aio_run_iocbs(ctx); + unuse_mm(ctx->mm); + spin_unlock_irq(&ctx->ctx_lock); + set_fs(oldfs); + /* + * we're in a worker thread already, don't use queue_delayed_work, + */ + if (requeue) + queue_work(aio_wq, &ctx->wq); +} + + +/* + * Called by kick_iocb to queue the kiocb for retry + * and if required activate the aio work queue to process + * it + */ +void queue_kicked_iocb(struct kiocb *iocb) +{ + struct kioctx *ctx = iocb->ki_ctx; + unsigned long flags; + int run = 0; + WARN_ON((!list_empty(&iocb->ki_wait.task_list))); + + spin_lock_irqsave(&ctx->ctx_lock, flags); + run = __queue_kicked_iocb(iocb); + spin_unlock_irqrestore(&ctx->ctx_lock, flags); + if (run) { + aio_queue_work(ctx); + aio_wakeups++; + } +} + +/* + * kick_iocb: + * Called typically from a wait queue callback context + * (aio_wake_function) to trigger a retry of the iocb. + * The retry is usually executed by aio workqueue + * threads (See aio_kick_handler). + */ +void fastcall kick_iocb(struct kiocb *iocb) +{ /* sync iocbs are easy: they can only ever be executing from a * single context. */ if (is_sync_kiocb(iocb)) { kiocbSetKicked(iocb); - wake_up_process(iocb->ki_obj.tsk); + wake_up_process(iocb->ki_obj.tsk); return; } + iocb->ki_kicked++; + /* If its already kicked we shouldn't queue it again */ if (!kiocbTryKick(iocb)) { - unsigned long flags; - spin_lock_irqsave(&ctx->ctx_lock, flags); - list_add_tail(&iocb->ki_run_list, &ctx->run_list); - spin_unlock_irqrestore(&ctx->ctx_lock, flags); - queue_work(aio_wq, &ctx->wq); + queue_kicked_iocb(iocb); } } EXPORT_SYMBOL(kick_iocb); @@ -675,6 +969,16 @@ int fastcall aio_complete(struct kiocb * */ spin_lock_irqsave(&ctx->ctx_lock, flags); + if (iocb->ki_run_list.prev && !list_empty(&iocb->ki_run_list)) + list_del_init(&iocb->ki_run_list); + + /* + * cancelled requests don't get events, userland was given one + * when the event got cancelled. + */ + if (kiocbIsCancelled(iocb)) + goto put_rq; + ring = kmap_atomic(info->ring_pages[0], KM_IRQ1); tail = info->tail; @@ -703,6 +1007,11 @@ int fastcall aio_complete(struct kiocb * pr_debug("added to ring %p at [%lu]\n", iocb, tail); + pr_debug("%ld retries: %d of %d (kicked %ld, Q %ld run %ld wake %ld)\n", + iocb->ki_retried, + iocb->ki_nbytes - iocb->ki_left, iocb->ki_nbytes, + iocb->ki_kicked, iocb->ki_queued, aio_run, aio_wakeups); +put_rq: /* everything turned out well, dispose of the aiocb. */ ret = __aio_put_req(ctx, iocb); @@ -809,13 +1118,15 @@ static int read_events(struct kioctx *ct int i = 0; struct io_event ent; struct aio_timeout to; + int event_loop = 0; /* testing only */ + int retry = 0; /* needed to zero any padding within an entry (there shouldn't be * any, but C is fun! */ memset(&ent, 0, sizeof(ent)); +retry: ret = 0; - while (likely(i < nr)) { ret = aio_read_evt(ctx, &ent); if (unlikely(ret <= 0)) @@ -844,6 +1155,13 @@ static int read_events(struct kioctx *ct /* End fast path */ + /* racey check, but it gets redone */ + if (!retry && unlikely(!list_empty(&ctx->run_list))) { + retry = 1; + aio_run_all_iocbs(ctx); + goto retry; + } + init_timeout(&to); if (timeout) { struct timespec ts; @@ -858,7 +1176,6 @@ static int read_events(struct kioctx *ct add_wait_queue_exclusive(&ctx->wait, &wait); do { set_task_state(tsk, TASK_INTERRUPTIBLE); - ret = aio_read_evt(ctx, &ent); if (ret) break; @@ -868,6 +1185,7 @@ static int read_events(struct kioctx *ct if (to.timed_out) /* Only check after read evt */ break; schedule(); + event_loop++; if (signal_pending(tsk)) { ret = -EINTR; break; @@ -895,6 +1213,9 @@ static int read_events(struct kioctx *ct if (timeout) clear_timeout(&to); out: + pr_debug("event loop executed %d times\n", event_loop); + pr_debug("aio_run %ld\n", aio_run); + pr_debug("aio_wakeups %ld\n", aio_wakeups); return i ? i : ret; } @@ -962,7 +1283,7 @@ asmlinkage long sys_io_setup(unsigned nr ret = put_user(ioctx->user_id, ctxp); if (!ret) return 0; - get_ioctx(ioctx); + io_destroy(ioctx); } @@ -987,13 +1308,181 @@ asmlinkage long sys_io_destroy(aio_conte return -EINVAL; } +/* + * Default retry method for aio_read (also used for first time submit) + * Responsible for updating iocb state as retries progress + */ +static ssize_t aio_pread(struct kiocb *iocb) +{ + struct file *file = iocb->ki_filp; + struct address_space *mapping = file->f_mapping; + struct inode *inode = mapping->host; + ssize_t ret = 0; + + ret = file->f_op->aio_read(iocb, iocb->ki_buf, + iocb->ki_left, iocb->ki_pos); + + /* + * Can't just depend on iocb->ki_left to determine + * whether we are done. This may have been a short read. + */ + if (ret > 0) { + iocb->ki_buf += ret; + iocb->ki_left -= ret; + /* + * For pipes and sockets we return once we have + * some data; for regular files we retry till we + * complete the entire read or find that we can't + * read any more data (e.g short reads). + */ + if (!S_ISFIFO(inode->i_mode) && !S_ISSOCK(inode->i_mode)) + ret = -EIOCBRETRY; + } + + /* This means we must have transferred all that we could */ + /* No need to retry anymore */ + if ((ret == 0) || (iocb->ki_left == 0)) + ret = iocb->ki_nbytes - iocb->ki_left; + + return ret; +} + +/* + * Default retry method for aio_write (also used for first time submit) + * Responsible for updating iocb state as retries progress + */ +static ssize_t aio_pwrite(struct kiocb *iocb) +{ + struct file *file = iocb->ki_filp; + ssize_t ret = 0; + + ret = file->f_op->aio_write(iocb, iocb->ki_buf, + iocb->ki_left, iocb->ki_pos); + + if (ret > 0) { + iocb->ki_buf += ret; + iocb->ki_left -= ret; + + ret = -EIOCBRETRY; + } + + /* This means we must have transferred all that we could */ + /* No need to retry anymore */ + if ((ret == 0) || (iocb->ki_left == 0)) + ret = iocb->ki_nbytes - iocb->ki_left; + + return ret; +} + +static ssize_t aio_fdsync(struct kiocb *iocb) +{ + struct file *file = iocb->ki_filp; + ssize_t ret = -EINVAL; + + if (file->f_op->aio_fsync) + ret = file->f_op->aio_fsync(iocb, 1); + return ret; +} + +static ssize_t aio_fsync(struct kiocb *iocb) +{ + struct file *file = iocb->ki_filp; + ssize_t ret = -EINVAL; + + if (file->f_op->aio_fsync) + ret = file->f_op->aio_fsync(iocb, 0); + return ret; +} + +/* + * aio_setup_iocb: + * Performs the initial checks and aio retry method + * setup for the kiocb at the time of io submission. + */ +ssize_t aio_setup_iocb(struct kiocb *kiocb) +{ + struct file *file = kiocb->ki_filp; + ssize_t ret = 0; + + switch (kiocb->ki_opcode) { + case IOCB_CMD_PREAD: + ret = -EBADF; + if (unlikely(!(file->f_mode & FMODE_READ))) + break; + ret = -EFAULT; + if (unlikely(!access_ok(VERIFY_WRITE, kiocb->ki_buf, + kiocb->ki_left))) + break; + ret = -EINVAL; + if (file->f_op->aio_read) + kiocb->ki_retry = aio_pread; + break; + case IOCB_CMD_PWRITE: + ret = -EBADF; + if (unlikely(!(file->f_mode & FMODE_WRITE))) + break; + ret = -EFAULT; + if (unlikely(!access_ok(VERIFY_READ, kiocb->ki_buf, + kiocb->ki_left))) + break; + ret = -EINVAL; + if (file->f_op->aio_write) + kiocb->ki_retry = aio_pwrite; + break; + case IOCB_CMD_FDSYNC: + ret = -EINVAL; + if (file->f_op->aio_fsync) + kiocb->ki_retry = aio_fdsync; + break; + case IOCB_CMD_FSYNC: + ret = -EINVAL; + if (file->f_op->aio_fsync) + kiocb->ki_retry = aio_fsync; + break; + default: + dprintk("EINVAL: io_submit: no operation provided\n"); + ret = -EINVAL; + } + + if (!kiocb->ki_retry) + return ret; + + return 0; +} + +/* + * aio_wake_function: + * wait queue callback function for aio notification, + * Simply triggers a retry of the operation via kick_iocb. + * + * This callback is specified in the wait queue entry in + * a kiocb (current->io_wait points to this wait queue + * entry when an aio operation executes; it is used + * instead of a synchronous wait when an i/o blocking + * condition is encountered during aio). + * + * Note: + * This routine is executed with the wait queue lock held. + * Since kick_iocb acquires iocb->ctx->ctx_lock, it nests + * the ioctx lock inside the wait queue lock. This is safe + * because this callback isn't used for wait queues which + * are nested inside ioctx lock (i.e. ctx->wait) + */ +int aio_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key) +{ + struct kiocb *iocb = container_of(wait, struct kiocb, ki_wait); + + list_del_init(&wait->task_list); + kick_iocb(iocb); + return 1; +} + int fastcall io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, struct iocb *iocb) { struct kiocb *req; struct file *file; ssize_t ret; - char __user *buf; /* enforce forwards compatibility on users */ if (unlikely(iocb->aio_reserved1 || iocb->aio_reserved2 || @@ -1034,58 +1523,31 @@ int fastcall io_submit_one(struct kioctx req->ki_user_data = iocb->aio_data; req->ki_pos = iocb->aio_offset; - buf = (char __user *)(unsigned long)iocb->aio_buf; + req->ki_buf = (char __user *)(unsigned long)iocb->aio_buf; + req->ki_left = req->ki_nbytes = iocb->aio_nbytes; + req->ki_opcode = iocb->aio_lio_opcode; + init_waitqueue_func_entry(&req->ki_wait, aio_wake_function); + INIT_LIST_HEAD(&req->ki_wait.task_list); + req->ki_run_list.next = req->ki_run_list.prev = NULL; + req->ki_retry = NULL; + req->ki_retried = 0; + req->ki_kicked = 0; + req->ki_queued = 0; + aio_run = 0; + aio_wakeups = 0; - switch (iocb->aio_lio_opcode) { - case IOCB_CMD_PREAD: - ret = -EBADF; - if (unlikely(!(file->f_mode & FMODE_READ))) - goto out_put_req; - ret = -EFAULT; - if (unlikely(!access_ok(VERIFY_WRITE, buf, iocb->aio_nbytes))) - goto out_put_req; - ret = security_file_permission (file, MAY_READ); - if (ret) - goto out_put_req; - ret = -EINVAL; - if (file->f_op->aio_read) - ret = file->f_op->aio_read(req, buf, - iocb->aio_nbytes, req->ki_pos); - break; - case IOCB_CMD_PWRITE: - ret = -EBADF; - if (unlikely(!(file->f_mode & FMODE_WRITE))) - goto out_put_req; - ret = -EFAULT; - if (unlikely(!access_ok(VERIFY_READ, buf, iocb->aio_nbytes))) - goto out_put_req; - ret = security_file_permission (file, MAY_WRITE); - if (ret) - goto out_put_req; - ret = -EINVAL; - if (file->f_op->aio_write) - ret = file->f_op->aio_write(req, buf, - iocb->aio_nbytes, req->ki_pos); - break; - case IOCB_CMD_FDSYNC: - ret = -EINVAL; - if (file->f_op->aio_fsync) - ret = file->f_op->aio_fsync(req, 1); - break; - case IOCB_CMD_FSYNC: - ret = -EINVAL; - if (file->f_op->aio_fsync) - ret = file->f_op->aio_fsync(req, 0); - break; - default: - dprintk("EINVAL: io_submit: no operation provided\n"); - ret = -EINVAL; - } + ret = aio_setup_iocb(req); + if (ret) + goto out_put_req; + + spin_lock_irq(&ctx->ctx_lock); + list_add_tail(&req->ki_run_list, &ctx->run_list); + /* drain the run list */ + while (__aio_run_iocbs(ctx)) + ; + spin_unlock_irq(&ctx->ctx_lock); aio_put_req(req); /* drop extra ref to req */ - if (likely(-EIOCBQUEUED == ret)) - return 0; - aio_complete(req, ret, 0); /* will drop i/o ref to req */ return 0; out_put_req: @@ -1201,6 +1663,7 @@ asmlinkage long sys_io_cancel(aio_contex if (kiocb && kiocb->ki_cancel) { cancel = kiocb->ki_cancel; kiocb->ki_users ++; + kiocbSetCancelled(kiocb); } else cancel = NULL; spin_unlock_irq(&ctx->ctx_lock); --- linux-2.6.9-rc1/fs/autofs/root.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/fs/autofs/root.c 2004-09-02 20:51:52.000000000 -0700 @@ -238,9 +238,15 @@ static struct dentry *autofs_root_lookup * a signal. If so we can force a restart.. */ if (dentry->d_flags & DCACHE_AUTOFS_PENDING) { + /* See if we were interrupted */ if (signal_pending(current)) { - unlock_kernel(); - return ERR_PTR(-ERESTARTNOINTR); + sigset_t *sigset = ¤t->pending.signal; + if (sigismember (sigset, SIGKILL) || + sigismember (sigset, SIGQUIT) || + sigismember (sigset, SIGINT)) { + unlock_kernel(); + return ERR_PTR(-ERESTARTNOINTR); + } } } unlock_kernel(); --- linux-2.6.9-rc1/fs/befs/linuxvfs.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/fs/befs/linuxvfs.c 2004-09-02 20:51:52.000000000 -0700 @@ -433,7 +433,7 @@ befs_init_inodecache(void) { befs_inode_cachep = kmem_cache_create("befs_inode_cache", sizeof (struct befs_inode_info), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (befs_inode_cachep == NULL) { printk(KERN_ERR "befs_init_inodecache: " @@ -857,10 +857,14 @@ befs_fill_super(struct super_block *sb, befs_sb->nls = load_nls(befs_sb->mount_opts.iocharset); if (!befs_sb->nls) { befs_warning(sb, "Cannot load nls %s" - "loding default nls", - befs_sb->mount_opts.iocharset); + " loading default nls", + befs_sb->mount_opts.iocharset); befs_sb->nls = load_nls_default(); } + /* load default nls if none is specified in mount options */ + } else { + befs_debug(sb, "Loading default nls"); + befs_sb->nls = load_nls_default(); } return 0; --- linux-2.6.9-rc1/fs/bfs/inode.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/fs/bfs/inode.c 2004-09-02 20:51:52.000000000 -0700 @@ -245,7 +245,7 @@ static int init_inodecache(void) { bfs_inode_cachep = kmem_cache_create("bfs_inode_cache", sizeof(struct bfs_inode_info), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (bfs_inode_cachep == NULL) return -ENOMEM; --- linux-2.6.9-rc1/fs/binfmt_aout.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/fs/binfmt_aout.c 2004-09-02 20:51:52.000000000 -0700 @@ -307,7 +307,7 @@ static int load_aout_binary(struct linux (current->mm->start_data = N_DATADDR(ex)); current->mm->brk = ex.a_bss + (current->mm->start_brk = N_BSSADDR(ex)); - current->mm->free_area_cache = TASK_UNMAPPED_BASE; + current->mm->free_area_cache = current->mm->mmap_base; current->mm->rss = 0; current->mm->mmap = NULL; --- linux-2.6.9-rc1/fs/binfmt_elf.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/fs/binfmt_elf.c 2004-09-02 20:51:52.000000000 -0700 @@ -40,6 +40,7 @@ #include #include +#include #include @@ -703,10 +704,12 @@ static int load_elf_binary(struct linux_ if (elf_read_implies_exec(elf_ex, have_pt_gnu_stack)) current->personality |= READ_IMPLIES_EXEC; + arch_pick_mmap_layout(current->mm); + /* Do this so that we can load the interpreter, if need be. We will change some of these later */ current->mm->rss = 0; - current->mm->free_area_cache = TASK_UNMAPPED_BASE; + current->mm->free_area_cache = current->mm->mmap_base; retval = setup_arg_pages(bprm, executable_stack); if (retval < 0) { send_sig(SIGKILL, current, 0); @@ -1176,10 +1179,27 @@ static void fill_prstatus(struct elf_prs prstatus->pr_ppid = p->parent->pid; prstatus->pr_pgrp = process_group(p); prstatus->pr_sid = p->signal->session; - jiffies_to_timeval(p->utime, &prstatus->pr_utime); - jiffies_to_timeval(p->stime, &prstatus->pr_stime); - jiffies_to_timeval(p->cutime, &prstatus->pr_cutime); - jiffies_to_timeval(p->cstime, &prstatus->pr_cstime); + if (p->pid == p->tgid) { + /* + * This is the record for the group leader. Add in the + * cumulative times of previous dead threads. This total + * won't include the time of each live thread whose state + * is included in the core dump. The final total reported + * to our parent process when it calls wait4 will include + * those sums as well as the little bit more time it takes + * this and each other thread to finish dying after the + * core dump synchronization phase. + */ + jiffies_to_timeval(p->utime + p->signal->utime, + &prstatus->pr_utime); + jiffies_to_timeval(p->stime + p->signal->stime, + &prstatus->pr_stime); + } else { + jiffies_to_timeval(p->utime, &prstatus->pr_utime); + jiffies_to_timeval(p->stime, &prstatus->pr_stime); + } + jiffies_to_timeval(p->signal->cutime, &prstatus->pr_cutime); + jiffies_to_timeval(p->signal->cstime, &prstatus->pr_cstime); } static void fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p, --- linux-2.6.9-rc1/fs/bio.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/fs/bio.c 2004-09-02 20:51:52.000000000 -0700 @@ -388,20 +388,17 @@ int bio_uncopy_user(struct bio *bio) struct bio_vec *bvec; int i, ret = 0; - if (bio_data_dir(bio) == READ) { - char *uaddr = bio->bi_private; + char *uaddr = bio->bi_private; - __bio_for_each_segment(bvec, bio, i, 0) { - char *addr = page_address(bvec->bv_page); - - if (!ret && copy_to_user(uaddr, addr, bvec->bv_len)) - ret = -EFAULT; + __bio_for_each_segment(bvec, bio, i, 0) { + char *addr = page_address(bvec->bv_page); + if (bio_data_dir(bio) == READ && !ret && + copy_to_user(uaddr, addr, bvec->bv_len)) + ret = -EFAULT; - __free_page(bvec->bv_page); - uaddr += bvec->bv_len; - } + __free_page(bvec->bv_page); + uaddr += bvec->bv_len; } - bio_put(bio); return ret; } @@ -457,6 +454,7 @@ struct bio *bio_copy_user(request_queue_ */ if (!ret) { if (!write_to_vm) { + unsigned long p = uaddr; bio->bi_rw |= (1 << BIO_RW); /* * for a write, copy in data to kernel pages @@ -465,8 +463,9 @@ struct bio *bio_copy_user(request_queue_ bio_for_each_segment(bvec, bio, i) { char *addr = page_address(bvec->bv_page); - if (copy_from_user(addr, (char *) uaddr, bvec->bv_len)) + if (copy_from_user(addr, (char *) p, bvec->bv_len)) goto cleanup; + p += bvec->bv_len; } } --- linux-2.6.9-rc1/fs/buffer.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/fs/buffer.c 2004-09-03 00:57:10.578431096 -0700 @@ -43,34 +43,6 @@ static void invalidate_bh_lrus(void); #define BH_ENTRY(list) list_entry((list), struct buffer_head, b_assoc_buffers) -struct bh_wait_queue { - struct buffer_head *bh; - wait_queue_t wait; -}; - -#define __DEFINE_BH_WAIT(name, b, f) \ - struct bh_wait_queue name = { \ - .bh = b, \ - .wait = { \ - .task = current, \ - .flags = f, \ - .func = bh_wake_function, \ - .task_list = \ - LIST_HEAD_INIT(name.wait.task_list),\ - }, \ - } -#define DEFINE_BH_WAIT(name, bh) __DEFINE_BH_WAIT(name, bh, 0) -#define DEFINE_BH_WAIT_EXCLUSIVE(name, bh) \ - __DEFINE_BH_WAIT(name, bh, WQ_FLAG_EXCLUSIVE) - -/* - * Hashed waitqueue_head's for wait_on_buffer() - */ -#define BH_WAIT_TABLE_ORDER 7 -static struct bh_wait_queue_head { - wait_queue_head_t wqh; -} ____cacheline_aligned_in_smp bh_wait_queue_heads[1<b_private = private; } -/* - * Return the address of the waitqueue_head to be used for this - * buffer_head - */ -wait_queue_head_t *bh_waitq_head(struct buffer_head *bh) -{ - return &bh_wait_queue_heads[hash_ptr(bh, BH_WAIT_TABLE_ORDER)].wqh; -} -EXPORT_SYMBOL(bh_waitq_head); - void wake_up_buffer(struct buffer_head *bh) { - wait_queue_head_t *wq = bh_waitq_head(bh); - smp_mb(); - if (waitqueue_active(wq)) - __wake_up(wq, TASK_INTERRUPTIBLE|TASK_UNINTERRUPTIBLE, 1, bh); + wake_up_bit(&bh->b_state, BH_Lock); } EXPORT_SYMBOL(wake_up_buffer); -static int bh_wake_function(wait_queue_t *wait, unsigned mode, - int sync, void *key) -{ - struct buffer_head *bh = key; - struct bh_wait_queue *wq; - - wq = container_of(wait, struct bh_wait_queue, wait); - if (wq->bh != bh || buffer_locked(bh)) - return 0; - else - return autoremove_wake_function(wait, mode, sync, key); -} - -static void sync_buffer(struct buffer_head *bh) +static int sync_buffer(void *word) { struct block_device *bd; + struct buffer_head *bh + = container_of(word, struct buffer_head, b_state); smp_mb(); bd = bh->b_bdev; if (bd) blk_run_address_space(bd->bd_inode->i_mapping); + io_schedule(); + return 0; } void fastcall __lock_buffer(struct buffer_head *bh) { - wait_queue_head_t *wqh = bh_waitq_head(bh); - DEFINE_BH_WAIT_EXCLUSIVE(wait, bh); - - do { - prepare_to_wait_exclusive(wqh, &wait.wait, - TASK_UNINTERRUPTIBLE); - if (buffer_locked(bh)) { - sync_buffer(bh); - io_schedule(); - } - } while (test_set_buffer_locked(bh)); - finish_wait(wqh, &wait.wait); + wait_on_bit_lock(&bh->b_state, BH_Lock, sync_buffer, + TASK_UNINTERRUPTIBLE); } EXPORT_SYMBOL(__lock_buffer); @@ -152,17 +92,7 @@ void fastcall unlock_buffer(struct buffe */ void __wait_on_buffer(struct buffer_head * bh) { - wait_queue_head_t *wqh = bh_waitq_head(bh); - DEFINE_BH_WAIT(wait, bh); - - do { - prepare_to_wait(wqh, &wait.wait, TASK_UNINTERRUPTIBLE); - if (buffer_locked(bh)) { - sync_buffer(bh); - io_schedule(); - } - } while (buffer_locked(bh)); - finish_wait(wqh, &wait.wait); + wait_on_bit(&bh->b_state, BH_Lock, sync_buffer, TASK_UNINTERRUPTIBLE); } static void @@ -802,6 +732,8 @@ void buffer_insert_list(spinlock_t *lock */ static inline void __remove_assoc_queue(struct buffer_head *bh) { + BUG_ON(bh->b_assoc_buffers.next == NULL); + BUG_ON(bh->b_assoc_buffers.prev == NULL); list_del_init(&bh->b_assoc_buffers); } @@ -947,7 +879,7 @@ int __set_page_dirty_buffers(struct page spin_unlock(&mapping->private_lock); if (!TestSetPageDirty(page)) { - spin_lock_irq(&mapping->tree_lock); + write_lock_irq(&mapping->tree_lock); if (page->mapping) { /* Race with truncate? */ if (!mapping->backing_dev_info->memory_backed) inc_page_state(nr_dirty); @@ -955,7 +887,7 @@ int __set_page_dirty_buffers(struct page page_index(page), PAGECACHE_TAG_DIRTY); } - spin_unlock_irq(&mapping->tree_lock); + write_unlock_irq(&mapping->tree_lock); __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); } @@ -1073,7 +1005,10 @@ int remove_inode_buffers(struct inode *i spin_lock(&buffer_mapping->private_lock); while (!list_empty(list)) { - struct buffer_head *bh = BH_ENTRY(list->next); + struct buffer_head *bh; + + BUG_ON(list->next == NULL); + bh = BH_ENTRY(list->next); if (buffer_dirty(bh)) { ret = 0; break; @@ -1551,6 +1486,7 @@ __getblk(struct block_device *bdev, sect { struct buffer_head *bh = __find_get_block(bdev, block, size); + might_sleep(); if (bh == NULL) bh = __getblk_slow(bdev, block, size); return bh; @@ -1776,6 +1712,8 @@ void unmap_underlying_metadata(struct bl { struct buffer_head *old_bh; + might_sleep(); + old_bh = __find_get_block_slow(bdev, block, 0); if (old_bh) { clear_buffer_dirty(old_bh); @@ -3124,14 +3062,11 @@ static int buffer_cpu_notify(struct noti void __init buffer_init(void) { - int i; int nrpages; bh_cachep = kmem_cache_create("buffer_head", sizeof(struct buffer_head), 0, SLAB_PANIC, init_buffer_head, NULL); - for (i = 0; i < ARRAY_SIZE(bh_wait_queue_heads); i++) - init_waitqueue_head(&bh_wait_queue_heads[i].wqh); /* * Limit the bh occupancy to 10% of ZONE_NORMAL --- linux-2.6.9-rc1/fs/cifs/asn1.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/fs/cifs/asn1.c 2004-09-02 20:51:52.000000000 -0700 @@ -375,7 +375,7 @@ asn1_subid_decode(struct asn1_ctx *ctx, return 1; } -static unsigned char +static int asn1_oid_decode(struct asn1_ctx *ctx, unsigned char *eoc, unsigned long **oid, unsigned int *len) { @@ -454,11 +454,11 @@ decode_negTokenInit(unsigned char *secur struct asn1_ctx ctx; unsigned char *end; unsigned char *sequence_end; - unsigned long *oid; + unsigned long *oid = NULL; unsigned int cls, con, tag, oidlen, rc; int use_ntlmssp = FALSE; - *secType = NTLM; /* BB eventually make Kerberos or NLTMSSP the default */ + *secType = NTLM; /* BB eventually make Kerberos or NLTMSSP the default */ /* cifs_dump_mem(" Received SecBlob ", security_blob, length); */ @@ -543,16 +543,19 @@ decode_negTokenInit(unsigned char *secur return 0; } if ((tag == ASN1_OJI) && (con == ASN1_PRI)) { - asn1_oid_decode(&ctx, end, &oid, &oidlen); - cFYI(1, - ("OID len = %d oid = 0x%lx 0x%lx 0x%lx 0x%lx", - oidlen, *oid, *(oid + 1), *(oid + 2), - *(oid + 3))); - rc = compare_oid(oid, oidlen, NTLMSSP_OID, + rc = asn1_oid_decode(&ctx, end, &oid, &oidlen); + if(rc) { + cFYI(1, + ("OID len = %d oid = 0x%lx 0x%lx 0x%lx 0x%lx", + oidlen, *oid, *(oid + 1), *(oid + 2), + *(oid + 3))); + rc = compare_oid(oid, oidlen, NTLMSSP_OID, NTLMSSP_OID_LEN); - kfree(oid); - if (rc) - use_ntlmssp = TRUE; + if(oid) + kfree(oid); + if (rc) + use_ntlmssp = TRUE; + } } else { cFYI(1,("This should be an oid what is going on? ")); } --- linux-2.6.9-rc1/fs/cifs/AUTHORS 2004-08-24 00:01:52.000000000 -0700 +++ 25/fs/cifs/AUTHORS 2004-09-02 20:51:52.000000000 -0700 @@ -24,6 +24,7 @@ Shobhit Dayal Sergey Vlasov Richard Hughes Yury Umanets +Mark Hamzy Test case and Bug Report contributors ------------------------------------- @@ -31,7 +32,9 @@ Thanks to those in the community who hav and debug of problems they have found: Jochen Dolze, David Blaine, Rene Scharfe, Martin Josefsson, Alexander Wild, Anthony Liguori, Lars Muller, Urban Widmark, Massimiliano Ferrero, Howard Owen, -Olaf Kirch, Kieron Briggs, Nick Millington and others. +Olaf Kirch, Kieron Briggs, Nick Millington and others. Also special +mention to the Stanford Checker (SWAT) which pointed out many minor +bugs in error paths. And thanks to the IBM LTC and Power test teams and SuSE testers for finding multiple bugs during excellent stress test runs. --- linux-2.6.9-rc1/fs/cifs/CHANGES 2004-08-24 00:03:31.000000000 -0700 +++ 25/fs/cifs/CHANGES 2004-09-02 20:51:52.000000000 -0700 @@ -1,7 +1,9 @@ Version 1.22 ------------ Add config option to enable XATTR (extended attribute) support, mapping -xattr names in the "user." namespace space to SMB/CIFS EAs. +xattr names in the "user." namespace space to SMB/CIFS EAs. Lots of +minor fixes pointed out by the Stanford SWAT checker (mostly missing +or out of order NULL pointer checks in little used error paths). Version 1.21 ------------ --- linux-2.6.9-rc1/fs/cifs/cifsencrypt.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/fs/cifs/cifsencrypt.c 2004-09-02 20:51:52.000000000 -0700 @@ -153,7 +153,7 @@ int CalcNTLMv2_partial_mac_key(struct ci wchar_t * unicode_buf; unsigned int i,user_name_len,dom_name_len; - if(ses) + if(ses == NULL) return -EINVAL; E_md4hash(ses->password, temp_hash); @@ -167,7 +167,13 @@ int CalcNTLMv2_partial_mac_key(struct ci return -EINVAL; ucase_buf = kmalloc((MAX_USERNAME_SIZE+1), GFP_KERNEL); + if(ucase_buf == NULL) + return -ENOMEM; unicode_buf = kmalloc((MAX_USERNAME_SIZE+1)*4, GFP_KERNEL); + if(unicode_buf == NULL) { + kfree(ucase_buf); + return -ENOMEM; + } for(i=0;icharset2upper[(int)ses->userName[i]]; --- linux-2.6.9-rc1/fs/cifs/cifsfs.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/fs/cifs/cifsfs.c 2004-09-02 20:51:52.000000000 -0700 @@ -255,12 +255,14 @@ cifs_show_options(struct seq_file *s, st if (cifs_sb) { if (cifs_sb->tcon) { seq_printf(s, ",unc=%s", cifs_sb->tcon->treeName); - if ((cifs_sb->tcon->ses) && (cifs_sb->tcon->ses->userName)) - seq_printf(s, ",username=%s", + if (cifs_sb->tcon->ses) { + if (cifs_sb->tcon->ses->userName) + seq_printf(s, ",username=%s", cifs_sb->tcon->ses->userName); - if(cifs_sb->tcon->ses->domainName) - seq_printf(s, ",domain=%s", - cifs_sb->tcon->ses->domainName); + if(cifs_sb->tcon->ses->domainName) + seq_printf(s, ",domain=%s", + cifs_sb->tcon->ses->domainName); + } } seq_printf(s, ",rsize=%d",cifs_sb->rsize); seq_printf(s, ",wsize=%d",cifs_sb->wsize); @@ -510,8 +512,9 @@ struct inode_operations cifs_file_inode_ }; struct inode_operations cifs_symlink_inode_ops = { - .readlink = cifs_readlink, + .readlink = generic_readlink, .follow_link = cifs_follow_link, + .put_link = cifs_put_link, .permission = cifs_permission, /* BB add the following two eventually */ /* revalidate: cifs_revalidate, @@ -561,7 +564,7 @@ cifs_init_inodecache(void) { cifs_inode_cachep = kmem_cache_create("cifs_inode_cache", sizeof (struct cifsInodeInfo), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, cifs_init_once, NULL); if (cifs_inode_cachep == NULL) return -ENOMEM; @@ -756,11 +759,14 @@ init_cifs(void) if (!rc) { rc = register_filesystem(&cifs_fs_type); if (!rc) { - kernel_thread(cifs_oplock_thread, NULL, + rc = (int)kernel_thread(cifs_oplock_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_VM); - return rc; /* Success */ - } else - cifs_destroy_request_bufs(); + if(rc > 0) + return 0; + else + cERROR(1,("error %d create oplock thread",rc)); + } + cifs_destroy_request_bufs(); } cifs_destroy_mids(); } --- linux-2.6.9-rc1/fs/cifs/cifsfs.h 2004-08-24 00:02:22.000000000 -0700 +++ 25/fs/cifs/cifsfs.h 2004-09-02 20:51:52.000000000 -0700 @@ -81,6 +81,7 @@ extern struct dentry_operations cifs_den /* Functions related to symlinks */ extern int cifs_follow_link(struct dentry *direntry, struct nameidata *nd); +extern void cifs_put_link(struct dentry *direntry, struct nameidata *nd); extern int cifs_readlink(struct dentry *direntry, char __user *buffer, int buflen); extern int cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname); --- linux-2.6.9-rc1/fs/cifs/cifspdu.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/fs/cifs/cifspdu.h 2004-09-02 20:51:52.000000000 -0700 @@ -1726,6 +1726,7 @@ struct data_blob { void (*free) (struct data_blob * data_blob); }; + #ifdef CONFIG_CIFS_POSIX /* For better POSIX semantics from Linux client, (even better @@ -1774,9 +1775,9 @@ struct data_blob { COPY (note support for copy across directories) - FUTURE, OPTIONAL setting/getting OS/2 EAs - FUTURE (BB can this handle - setting Linux xattrs perfectly) - OPTIONAL - dnotify - FUTURE, OPTIONAL - quota - FUTURE, OPTIONAL + setting Linux xattrs perfectly) - OPTIONAL + dnotify - FUTURE, OPTIONAL + quota - FUTURE, OPTIONAL Note that various requests implemented for NT interop such as NT_TRANSACT (IOCTL) QueryReparseInfo @@ -1801,6 +1802,34 @@ struct data_blob { */ + +/* xsymlink is a symlink format that can be used + to save symlink info in a regular file when + mounted to operating systems that do not + support the cifs Unix extensions or EAs (for xattr + based symlinks). For such a file to be recognized + as containing symlink data: + + 1) file size must be 1067, + 2) signature must begin file data, + 3) length field must be set to ASCII representation + of a number which is less than or equal to 1024, + 4) md5 must match that of the path data */ + +struct xsymlink { + /* 1067 bytes */ + char signature[4]; /* XSym */ /* not null terminated */ + char cr0; /* \n */ +/* ASCII representation of length (4 bytes decimal) terminated by \n not null */ + char length[4]; + char cr1; /* \n */ +/* md5 of valid subset of path ie path[0] through path[length-1] */ + __u8 md5[32]; + char cr2; /* \n */ +/* if room left, then end with \n then 0x20s by convention but not required */ + char path[1024]; +}; + #endif #pragma pack() /* resume default structure packing */ --- linux-2.6.9-rc1/fs/cifs/cifsproto.h 2004-08-24 00:01:52.000000000 -0700 +++ 25/fs/cifs/cifsproto.h 2004-09-02 20:51:52.000000000 -0700 @@ -37,7 +37,7 @@ extern int smb_send(struct socket *, str extern unsigned int _GetXid(void); extern void _FreeXid(unsigned int); #define GetXid() (int)_GetXid(); cFYI(1,("CIFS VFS: in %s as Xid: %d with uid: %d",__FUNCTION__, xid,current->fsuid)); -#define FreeXid(curr_xid) {_FreeXid(curr_xid); cFYI(1,("CIFS VFS: leaving %s (xid = %d) rc = %d",__FUNCTION__,curr_xid,rc));} +#define FreeXid(curr_xid) {_FreeXid(curr_xid); cFYI(1,("CIFS VFS: leaving %s (xid = %d) rc = %d",__FUNCTION__,curr_xid,(int)rc));} extern char *build_path_from_dentry(struct dentry *); extern char *build_wildcard_path_from_dentry(struct dentry *direntry); extern void renew_parental_timestamps(struct dentry *direntry); --- linux-2.6.9-rc1/fs/cifs/cifssmb.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/fs/cifs/cifssmb.c 2004-09-02 20:51:52.000000000 -0700 @@ -234,7 +234,8 @@ CIFSSMBNegotiate(unsigned int xid, struc /* BB might be helpful to save off the domain of server here */ - if (pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) { + if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) && + (server->capabilities & CAP_EXTENDED_SECURITY)) { if (pSMBr->ByteCount < 16) rc = -EIO; else if (pSMBr->ByteCount == 16) { @@ -365,9 +366,12 @@ CIFSSMBLogoff(const int xid, struct cifs rc = smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL /* no tcon anymore */, (void **) &pSMB, (void **) &smb_buffer_response); - - if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) - pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + + if(ses->server) { + if(ses->server->secMode & + (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; + } if (rc) { up(&ses->sesSem); @@ -818,7 +822,7 @@ CIFSSMBLock(const int xid, struct cifsTc pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = smb_file_id; /* netfid stays le */ - if(numLock != 0) { + if((numLock != 0) || (numUnlock != 0)) { pSMB->Locks[0].Pid = cpu_to_le16(current->tgid); /* BB where to store pid high? */ temp = cpu_to_le64(len); @@ -2173,6 +2177,10 @@ getDFSRetry: /* BB add check for name_len bigger than bcc */ *targetUNCs = kmalloc(name_len+1+ (*number_of_UNC_in_array),GFP_KERNEL); + if(*targetUNCs == NULL) { + rc = -ENOMEM; + goto GetDFSRefExit; + } /* copy the ref strings */ referrals = (struct dfs_referral_level_3 *) @@ -2196,6 +2204,7 @@ getDFSRetry: } } +GetDFSRefExit: if (pSMB) cifs_buf_release(pSMB); @@ -3105,7 +3114,7 @@ QAllEAsRetry: rc += temp_fea->name_len; /* account for prefix user. and trailing null */ rc = rc + 5 + 1; - if(rcname_len); @@ -3141,7 +3150,7 @@ QAllEAsRetry: if (rc == -EAGAIN) goto QAllEAsRetry; - return rc; + return (ssize_t)rc; } ssize_t CIFSSMBQueryEA(const int xid,struct cifsTconInfo * tcon, @@ -3255,7 +3264,7 @@ QEARetry: /* found a match */ rc = temp_fea->value_len; /* account for prefix user. and trailing null */ - if(rc<=buf_size) { + if(rc<=(int)buf_size) { memcpy(ea_value, temp_fea->name+temp_fea->name_len+1, rc); @@ -3288,7 +3297,7 @@ QEARetry: if (rc == -EAGAIN) goto QEARetry; - return rc; + return (ssize_t)rc; } int --- linux-2.6.9-rc1/fs/cifs/connect.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/fs/cifs/connect.c 2004-09-02 20:51:52.000000000 -0700 @@ -814,19 +814,23 @@ cifs_parse_mount_options(char *options, } static struct cifsSesInfo * -cifs_find_tcp_session(__u32 new_target_ip_addr, +cifs_find_tcp_session(struct in_addr * target_ip_addr, + struct in6_addr *target_ip6_addr, char *userName, struct TCP_Server_Info **psrvTcp) { struct list_head *tmp; struct cifsSesInfo *ses; - *psrvTcp = NULL; read_lock(&GlobalSMBSeslock); + list_for_each(tmp, &GlobalSMBSessionList) { ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); if (ses->server) { - if (ses->server->addr.sockAddr.sin_addr.s_addr == - new_target_ip_addr) { + if((target_ip_addr && + (ses->server->addr.sockAddr.sin_addr.s_addr + == target_ip_addr->s_addr)) || (target_ip6_addr + && memcmp(&ses->server->addr.sockAddr6.sin6_addr, + target_ip6_addr,sizeof(*target_ip6_addr)))){ /* BB lock server and tcp session and increment use count here?? */ *psrvTcp = ses->server; /* found a match on the TCP session */ /* BB check if reconnection needed */ @@ -1029,7 +1033,7 @@ ipv4_connect(struct sockaddr_in *psin_se /* send RFC1001 sessinit */ - if(psin_server->sin_port == htons(139)) { + if(psin_server->sin_port == htons(RFC1001_PORT)) { /* some servers require RFC1001 sessinit before sending negprot - BB check reconnection in case where second sessinit is sent but no second negprot */ @@ -1072,6 +1076,7 @@ ipv6_connect(struct sockaddr_in6 *psin_s { int rc = 0; int connected = 0; + unsigned short int orig_port = 0; if(*csocket == NULL) { rc = sock_create_kern(PF_INET6, SOCK_STREAM, IPPROTO_TCP, csocket); @@ -1097,6 +1102,10 @@ ipv6_connect(struct sockaddr_in6 *psin_s } if(!connected) { + /* save original port so we can retry user specified port + later if fall back ports fail this time */ + + orig_port = psin_server->sin6_port; /* do not retry on the same port we just failed on */ if(psin_server->sin6_port != htons(CIFS_PORT)) { psin_server->sin6_port = htons(CIFS_PORT); @@ -1119,6 +1128,8 @@ ipv6_connect(struct sockaddr_in6 *psin_s /* give up here - unless we want to retry on different protocol families some day */ if (!connected) { + if(orig_port) + psin_server->sin6_port = orig_port; cFYI(1,("Error %d connecting to server via ipv6",rc)); sock_release(*csocket); *csocket = NULL; @@ -1138,6 +1149,7 @@ cifs_mount(struct super_block *sb, struc { int rc = 0; int xid; + int address_type = AF_INET; struct socket *csocket = NULL; struct sockaddr_in sin_server; struct sockaddr_in6 sin_server6; @@ -1179,12 +1191,16 @@ cifs_mount(struct super_block *sb, struc if (volume_info.UNCip && volume_info.UNC) { rc = cifs_inet_pton(AF_INET, volume_info.UNCip,&sin_server.sin_addr.s_addr); - if(rc == 0) { + if(rc <= 0) { /* not ipv4 address, try ipv6 */ rc = cifs_inet_pton(AF_INET6,volume_info.UNCip,&sin_server6.sin6_addr.in6_u); - } + if(rc > 0) + address_type = AF_INET6; + } else { + address_type = AF_INET; + } - if(rc != 1) { + if(rc <= 0) { /* we failed translating address */ if(volume_info.UNC) kfree(volume_info.UNC); @@ -1234,9 +1250,24 @@ cifs_mount(struct super_block *sb, struc } } - existingCifsSes = - cifs_find_tcp_session(sin_server.sin_addr.s_addr, - volume_info.username, &srvTcp); + if(address_type == AF_INET) + existingCifsSes = cifs_find_tcp_session(&sin_server.sin_addr, + NULL /* no ipv6 addr */, + volume_info.username, &srvTcp); + else if(address_type == AF_INET6) + existingCifsSes = cifs_find_tcp_session(NULL /* no ipv4 addr */, + &sin_server6.sin6_addr, + volume_info.username, &srvTcp); + else { + if(volume_info.UNC) + kfree(volume_info.UNC); + if(volume_info.password) + kfree(volume_info.password); + FreeXid(xid); + return -EINVAL; + } + + if (srvTcp) { cFYI(1, ("Existing tcp session with server found ")); } else { /* create socket */ @@ -1283,8 +1314,19 @@ cifs_mount(struct super_block *sb, struc so no need to spinlock this init of tcpStatus */ srvTcp->tcpStatus = CifsNew; init_MUTEX(&srvTcp->tcpSem); - kernel_thread((void *)(void *)cifs_demultiplex_thread, srvTcp, + rc = (int)kernel_thread((void *)(void *)cifs_demultiplex_thread, srvTcp, CLONE_FS | CLONE_FILES | CLONE_VM); + if(rc < 0) { + rc = -ENOMEM; + sock_release(csocket); + if(volume_info.UNC) + kfree(volume_info.UNC); + if(volume_info.password) + kfree(volume_info.password); + FreeXid(xid); + return rc; + } else + rc = 0; memcpy(srvTcp->workstation_RFC1001_name, volume_info.source_rfc1001_name,16); } } @@ -1464,15 +1506,18 @@ CIFSSessSetup(unsigned int xid, struct c SESSION_SETUP_ANDX *pSMB; SESSION_SETUP_ANDX *pSMBr; char *bcc_ptr; - char *user = ses->userName; - char *domain = ses->domainName; + char *user; + char *domain; int rc = 0; int remaining_words = 0; int bytes_returned = 0; int len; cFYI(1, ("In sesssetup ")); - + if(ses == NULL) + return -EINVAL; + user = ses->userName; + domain = ses->domainName; smb_buffer = cifs_buf_get(); if (smb_buffer == 0) { return -ENOMEM; @@ -1591,111 +1636,104 @@ CIFSSessSetup(unsigned int xid, struct c pSMBr->resp.Action = le16_to_cpu(pSMBr->resp.Action); if (pSMBr->resp.Action & GUEST_LOGIN) cFYI(1, (" Guest login")); /* do we want to mark SesInfo struct ? */ - if (ses) { - ses->Suid = smb_buffer_response->Uid; /* UID left in wire format (le) */ - cFYI(1, ("UID = %d ", ses->Suid)); + ses->Suid = smb_buffer_response->Uid; /* UID left in wire format (le) */ + cFYI(1, ("UID = %d ", ses->Suid)); /* response can have either 3 or 4 word count - Samba sends 3 */ - bcc_ptr = pByteArea(smb_buffer_response); - if ((pSMBr->resp.hdr.WordCount == 3) - || ((pSMBr->resp.hdr.WordCount == 4) - && (pSMBr->resp.SecurityBlobLength < - pSMBr->resp.ByteCount))) { - if (pSMBr->resp.hdr.WordCount == 4) - bcc_ptr += - pSMBr->resp.SecurityBlobLength; - - if (smb_buffer->Flags2 & SMBFLG2_UNICODE) { - if ((long) (bcc_ptr) % 2) { - remaining_words = - (BCC(smb_buffer_response) - - 1) / 2; - bcc_ptr++; /* Unicode strings must be word aligned */ - } else { - remaining_words = - BCC - (smb_buffer_response) / 2; - } - len = - UniStrnlen((wchar_t *) bcc_ptr, - remaining_words - 1); + bcc_ptr = pByteArea(smb_buffer_response); + if ((pSMBr->resp.hdr.WordCount == 3) + || ((pSMBr->resp.hdr.WordCount == 4) + && (pSMBr->resp.SecurityBlobLength < + pSMBr->resp.ByteCount))) { + if (pSMBr->resp.hdr.WordCount == 4) + bcc_ptr += + pSMBr->resp.SecurityBlobLength; + + if (smb_buffer->Flags2 & SMBFLG2_UNICODE) { + if ((long) (bcc_ptr) % 2) { + remaining_words = + (BCC(smb_buffer_response) - 1) /2; + bcc_ptr++; /* Unicode strings must be word aligned */ + } else { + remaining_words = + BCC(smb_buffer_response) / 2; + } + len = + UniStrnlen((wchar_t *) bcc_ptr, + remaining_words - 1); /* We look for obvious messed up bcc or strings in response so we do not go off the end since (at least) WIN2K and Windows XP have a major bug in not null terminating last Unicode string in response */ - ses->serverOS = cifs_kcalloc(2 * (len + 1), GFP_KERNEL); - cifs_strfromUCS_le(ses->serverOS, - (wchar_t *)bcc_ptr, len,nls_codepage); + ses->serverOS = cifs_kcalloc(2 * (len + 1), GFP_KERNEL); + cifs_strfromUCS_le(ses->serverOS, + (wchar_t *)bcc_ptr, len,nls_codepage); + bcc_ptr += 2 * (len + 1); + remaining_words -= len + 1; + ses->serverOS[2 * len] = 0; + ses->serverOS[1 + (2 * len)] = 0; + if (remaining_words > 0) { + len = UniStrnlen((wchar_t *)bcc_ptr, + remaining_words-1); + ses->serverNOS =cifs_kcalloc(2 * (len + 1),GFP_KERNEL); + cifs_strfromUCS_le(ses->serverNOS, + (wchar_t *)bcc_ptr,len,nls_codepage); bcc_ptr += 2 * (len + 1); + ses->serverNOS[2 * len] = 0; + ses->serverNOS[1 + (2 * len)] = 0; remaining_words -= len + 1; - ses->serverOS[2 * len] = 0; - ses->serverOS[1 + (2 * len)] = 0; if (remaining_words > 0) { - len = UniStrnlen((wchar_t *)bcc_ptr, - remaining_words - - 1); - ses->serverNOS =cifs_kcalloc(2 * (len + 1),GFP_KERNEL); - cifs_strfromUCS_le(ses->serverNOS, - (wchar_t *)bcc_ptr,len,nls_codepage); - bcc_ptr += 2 * (len + 1); - ses->serverNOS[2 * len] = 0; - ses->serverNOS[1 + (2 * len)] = 0; - remaining_words -= len + 1; - if (remaining_words > 0) { - len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words); + len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words); /* last string is not always null terminated (for e.g. for Windows XP & 2000) */ - ses->serverDomain = - cifs_kcalloc(2*(len+1),GFP_KERNEL); - cifs_strfromUCS_le(ses->serverDomain, - (wchar_t *)bcc_ptr,len,nls_codepage); - bcc_ptr += 2 * (len + 1); - ses->serverDomain[2*len] = 0; - ses->serverDomain[1+(2*len)] = 0; - } /* else no more room so create dummy domain string */ - else - ses->serverDomain = - cifs_kcalloc(2, - GFP_KERNEL); - } else { /* no room so create dummy domain and NOS string */ ses->serverDomain = - cifs_kcalloc(2, GFP_KERNEL); - ses->serverNOS = - cifs_kcalloc(2, GFP_KERNEL); - } - } else { /* ASCII */ - len = strnlen(bcc_ptr, 1024); - if (((long) bcc_ptr + len) - (long) - pByteArea(smb_buffer_response) + cifs_kcalloc(2*(len+1),GFP_KERNEL); + cifs_strfromUCS_le(ses->serverDomain, + (wchar_t *)bcc_ptr,len,nls_codepage); + bcc_ptr += 2 * (len + 1); + ses->serverDomain[2*len] = 0; + ses->serverDomain[1+(2*len)] = 0; + } /* else no more room so create dummy domain string */ + else + ses->serverDomain = + cifs_kcalloc(2, + GFP_KERNEL); + } else { /* no room so create dummy domain and NOS string */ + ses->serverDomain = + cifs_kcalloc(2, GFP_KERNEL); + ses->serverNOS = + cifs_kcalloc(2, GFP_KERNEL); + } + } else { /* ASCII */ + len = strnlen(bcc_ptr, 1024); + if (((long) bcc_ptr + len) - (long) + pByteArea(smb_buffer_response) <= BCC(smb_buffer_response)) { - ses->serverOS = cifs_kcalloc(len + 1,GFP_KERNEL); - strncpy(ses->serverOS,bcc_ptr, len); + ses->serverOS = cifs_kcalloc(len + 1,GFP_KERNEL); + strncpy(ses->serverOS,bcc_ptr, len); - bcc_ptr += len; - bcc_ptr[0] = 0; /* null terminate the string */ - bcc_ptr++; + bcc_ptr += len; + bcc_ptr[0] = 0; /* null terminate the string */ + bcc_ptr++; - len = strnlen(bcc_ptr, 1024); - ses->serverNOS = cifs_kcalloc(len + 1,GFP_KERNEL); - strncpy(ses->serverNOS, bcc_ptr, len); - bcc_ptr += len; - bcc_ptr[0] = 0; - bcc_ptr++; + len = strnlen(bcc_ptr, 1024); + ses->serverNOS = cifs_kcalloc(len + 1,GFP_KERNEL); + strncpy(ses->serverNOS, bcc_ptr, len); + bcc_ptr += len; + bcc_ptr[0] = 0; + bcc_ptr++; - len = strnlen(bcc_ptr, 1024); - ses->serverDomain = cifs_kcalloc(len + 1,GFP_KERNEL); - strncpy(ses->serverDomain, bcc_ptr, len); - bcc_ptr += len; - bcc_ptr[0] = 0; - bcc_ptr++; - } else - cFYI(1, - ("Variable field of length %d extends beyond end of smb ", - len)); - } - } else { - cERROR(1, - (" Security Blob Length extends beyond end of SMB")); + len = strnlen(bcc_ptr, 1024); + ses->serverDomain = cifs_kcalloc(len + 1,GFP_KERNEL); + strncpy(ses->serverDomain, bcc_ptr, len); + bcc_ptr += len; + bcc_ptr[0] = 0; + bcc_ptr++; + } else + cFYI(1, + ("Variable field of length %d extends beyond end of smb ", + len)); } } else { - cERROR(1, ("No session structure passed in.")); + cERROR(1, + (" Security Blob Length extends beyond end of SMB")); } } else { cERROR(1, @@ -1720,14 +1758,18 @@ CIFSSpnegoSessSetup(unsigned int xid, st SESSION_SETUP_ANDX *pSMB; SESSION_SETUP_ANDX *pSMBr; char *bcc_ptr; - char *user = ses->userName; - char *domain = ses->domainName; + char *user; + char *domain; int rc = 0; int remaining_words = 0; int bytes_returned = 0; int len; cFYI(1, ("In spnego sesssetup ")); + if(ses == NULL) + return -EINVAL; + user = ses->userName; + domain = ses->domainName; smb_buffer = cifs_buf_get(); if (smb_buffer == 0) { @@ -1979,7 +2021,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned i SESSION_SETUP_ANDX *pSMB; SESSION_SETUP_ANDX *pSMBr; char *bcc_ptr; - char *domain = ses->domainName; + char *domain; int rc = 0; int remaining_words = 0; int bytes_returned = 0; @@ -1989,6 +2031,9 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned i PCHALLENGE_MESSAGE SecurityBlob2; cFYI(1, ("In NTLMSSP sesssetup (negotiate) ")); + if(ses == NULL) + return -EINVAL; + domain = ses->domainName; *pNTLMv2_flag = FALSE; smb_buffer = cifs_buf_get(); if (smb_buffer == 0) { @@ -2319,8 +2364,8 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xi SESSION_SETUP_ANDX *pSMB; SESSION_SETUP_ANDX *pSMBr; char *bcc_ptr; - char *user = ses->userName; - char *domain = ses->domainName; + char *user; + char *domain; int rc = 0; int remaining_words = 0; int bytes_returned = 0; @@ -2329,7 +2374,10 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xi PAUTHENTICATE_MESSAGE SecurityBlob; cFYI(1, ("In NTLMSSPSessSetup (Authenticate)")); - + if(ses == NULL) + return -EINVAL; + user = ses->userName; + domain = ses->domainName; smb_buffer = cifs_buf_get(); if (smb_buffer == 0) { return -ENOMEM; --- linux-2.6.9-rc1/fs/cifs/file.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/fs/cifs/file.c 2004-09-02 20:51:52.000000000 -0700 @@ -148,7 +148,7 @@ cifs_open(struct inode *inode, struct fi and the first handle has writebehind data, we might be able to simply do a filemap_fdatawrite/filemap_fdatawait first */ buf = kmalloc(sizeof(FILE_ALL_INFO),GFP_KERNEL); - if(buf==0) { + if(buf== NULL) { if (full_path) kfree(full_path); FreeXid(xid); @@ -1687,8 +1687,12 @@ cifs_readdir(struct file *file, void *di } data = kmalloc(bufsize, GFP_KERNEL); pfindData = (FILE_DIRECTORY_INFO *) data; - + if(data == NULL) { + FreeXid(xid); + return -ENOMEM; + } if(file->f_dentry == NULL) { + kfree(data); FreeXid(xid); return -EIO; } @@ -1696,7 +1700,11 @@ cifs_readdir(struct file *file, void *di full_path = build_wildcard_path_from_dentry(file->f_dentry); up(&file->f_dentry->d_sb->s_vfs_rename_sem); - + if(full_path == NULL) { + kfree(data); + FreeXid(xid); + return -ENOMEM; + } cFYI(1, ("Full path: %s start at: %lld ", full_path, file->f_pos)); switch ((int) file->f_pos) { @@ -1784,6 +1792,10 @@ cifs_readdir(struct file *file, void *di cFYI(1,("Last file: %s with name %d bytes long", lastFindData->FileName, cifsFile->resume_name_length)); + if(cifsFile->search_resume_name == NULL) { + rc = -ENOMEM; + break; + } memcpy(cifsFile->search_resume_name, lastFindData->FileName, cifsFile->resume_name_length); @@ -1813,6 +1825,10 @@ cifs_readdir(struct file *file, void *di cFYI(1,("Last file: %s with name %d bytes long", pfindDataUnix->FileName, cifsFile->resume_name_length)); + if(cifsFile->search_resume_name == NULL) { + rc = -ENOMEM; + break; + } memcpy(cifsFile->search_resume_name, pfindDataUnix->FileName, cifsFile->resume_name_length); @@ -1966,6 +1982,11 @@ cifs_readdir(struct file *file, void *di cFYI(1,("Last file: %s with name %d bytes long", lastFindData->FileName, cifsFile->resume_name_length)); + if(cifsFile->search_resume_name == NULL) { + rc = -ENOMEM; + break; + } + memcpy(cifsFile->search_resume_name, lastFindData->FileName, cifsFile->resume_name_length); @@ -2001,6 +2022,10 @@ cifs_readdir(struct file *file, void *di cFYI(1,("fnext last file: %s with name %d bytes long", pfindDataUnix->FileName, cifsFile->resume_name_length)); + if(cifsFile->search_resume_name == NULL) { + rc = -ENOMEM; + break; + } memcpy(cifsFile->search_resume_name, pfindDataUnix->FileName, cifsFile->resume_name_length); --- linux-2.6.9-rc1/fs/cifs/inode.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/fs/cifs/inode.c 2004-09-02 20:51:52.000000000 -0700 @@ -201,6 +201,8 @@ cifs_get_inode_info(struct inode **pinod /* if file info not passed in then get it from server */ if(pfindData == NULL) { buf = kmalloc(sizeof(FILE_ALL_INFO),GFP_KERNEL); + if(buf == NULL) + return -ENOMEM; pfindData = (FILE_ALL_INFO *)buf; /* could do find first instead but this returns more info */ rc = CIFSSMBQPathInfo(xid, pTcon, search_path, pfindData, @@ -293,7 +295,7 @@ cifs_get_inode_info(struct inode **pinod /* 512 bytes (2**9) is the fake blocksize that must be used */ /* for this calculation */ - inode->i_blocks = (512 - 1 + pfindData->AllocationSize) + inode->i_blocks = (512 - 1 + le64_to_cpu(pfindData->AllocationSize)) >> 9; } pfindData->AllocationSize = le64_to_cpu(pfindData->AllocationSize); --- linux-2.6.9-rc1/fs/cifs/link.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/fs/cifs/link.c 2004-09-02 20:51:52.000000000 -0700 @@ -20,6 +20,7 @@ */ #include #include +#include #include "cifsfs.h" #include "cifspdu.h" #include "cifsglob.h" @@ -94,7 +95,7 @@ cifs_follow_link(struct dentry *direntry int rc = -EACCES; int xid; char *full_path = NULL; - char * target_path; + char * target_path = ERR_PTR(-ENOMEM); struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; @@ -104,22 +105,17 @@ cifs_follow_link(struct dentry *direntry full_path = build_path_from_dentry(direntry); up(&direntry->d_sb->s_vfs_rename_sem); - if(full_path == NULL) { - FreeXid(xid); - return -ENOMEM; - } + if (!full_path) + goto out_no_free; + cFYI(1, ("Full path: %s inode = 0x%p", full_path, inode)); cifs_sb = CIFS_SB(inode->i_sb); pTcon = cifs_sb->tcon; target_path = kmalloc(PATH_MAX, GFP_KERNEL); - if(target_path == NULL) { - if (full_path) - kfree(full_path); - FreeXid(xid); - return -ENOMEM; + if (!target_path) { + target_path = ERR_PTR(-ENOMEM); + goto out; } - /* can not call the following line due to EFAULT in vfs_readlink which is presumably expecting a user space buffer */ - /* length = cifs_readlink(direntry,target_path, sizeof(target_path) - 1); */ /* BB add read reparse point symlink code and Unix extensions symlink code here BB */ if (pTcon->ses->capabilities & CAP_UNIX) @@ -130,25 +126,25 @@ cifs_follow_link(struct dentry *direntry else { /* rc = CIFSSMBQueryReparseLinkInfo */ /* BB Add code to Query ReparsePoint info */ + /* BB Add MAC style xsymlink check here if enabled */ } - /* BB Anything else to do to handle recursive links? */ - /* BB Should we be using page symlink ops here? */ if (rc == 0) { /* BB Add special case check for Samba DFS symlinks */ target_path[PATH_MAX-1] = 0; - rc = vfs_follow_link(nd, target_path); + } else { + kfree(target_path); + target_path = ERR_PTR(rc); } - /* else EACCESS */ - if (target_path) - kfree(target_path); - if (full_path) - kfree(full_path); +out: + kfree(full_path); +out_no_free: FreeXid(xid); - return rc; + nd_set_link(nd, target_path); + return 0; } int @@ -297,10 +293,7 @@ cifs_readlink(struct dentry *direntry, c if(referrals) kfree(referrals); kfree(tmp_path); - if(referrals) { - kfree(referrals); - } - } +} /* BB add code like else decode referrals then memcpy to tmpbuffer and free referrals string array BB */ } @@ -326,3 +319,10 @@ cifs_readlink(struct dentry *direntry, c FreeXid(xid); return rc; } + +void cifs_put_link(struct dentry *direntry, struct nameidata *nd) +{ + char *p = nd_get_link(nd); + if (!IS_ERR(p)) + kfree(p); +} --- linux-2.6.9-rc1/fs/cifs/misc.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/fs/cifs/misc.c 2004-09-02 20:51:52.000000000 -0700 @@ -276,7 +276,7 @@ header_assemble(struct smb_hdr *buffer, } if (treeCon->Flags & SMB_SHARE_IS_IN_DFS) buffer->Flags2 |= SMBFLG2_DFS; - if(treeCon->ses->server) + if((treeCon->ses) && (treeCon->ses->server)) if(treeCon->ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; --- linux-2.6.9-rc1/fs/cifs/transport.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/fs/cifs/transport.c 2004-09-02 20:51:52.000000000 -0700 @@ -181,7 +181,7 @@ SendReceive(const unsigned int xid, stru { int rc = 0; unsigned int receive_len; - long timeout; + unsigned long timeout; struct mid_q_entry *midQ; if (ses == NULL) { @@ -307,23 +307,25 @@ SendReceive(const unsigned int xid, stru if (signal_pending(current)) { /* if signal pending do not hold up user for full smb timeout but we still give response a change to complete */ - if(midQ->midState & MID_REQUEST_SUBMITTED) { - set_current_state(TASK_UNINTERRUPTIBLE); - timeout = sleep_on_timeout(&ses->server->response_q,2 * HZ); - } - } else { /* using normal timeout */ - /* timeout = wait_event_interruptible_timeout(ses->server->response_q, + timeout = 2 * HZ; + + } + + /* No user interrupts in wait - wreaks havoc with performance */ + if(timeout != MAX_SCHEDULE_TIMEOUT) { + timeout += jiffies; + wait_event(ses->server->response_q, (midQ->midState & MID_RESPONSE_RECEIVED) || + time_after(jiffies, timeout) || ((ses->server->tcpStatus != CifsGood) && - (ses->server->tcpStatus != CifsNew)), - timeout); */ - /* Can not allow user interrupts- wreaks havoc with performance */ - if(midQ->midState & MID_REQUEST_SUBMITTED) { - set_current_state(TASK_UNINTERRUPTIBLE); - timeout = sleep_on_timeout(&ses->server->response_q,timeout); - } + (ses->server->tcpStatus != CifsNew))); + } else { + wait_event(ses->server->response_q, + (midQ->midState & MID_RESPONSE_RECEIVED) || + ((ses->server->tcpStatus != CifsGood) && + (ses->server->tcpStatus != CifsNew))); } - + spin_lock(&GlobalMid_Lock); if (midQ->resp_buf) { spin_unlock(&GlobalMid_Lock); --- linux-2.6.9-rc1/fs/coda/coda_linux.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/fs/coda/coda_linux.c 2004-09-03 00:57:22.847565904 -0700 @@ -28,7 +28,7 @@ int coda_fake_statfs; char * coda_f2s(struct CodaFid *f) { static char s[60]; -#ifdef CODA_FS_OLD_API +#ifdef CONFIG_CODA_FS_OLD_API sprintf(s, "(%08x.%08x.%08x)", f->opaque[0], f->opaque[1], f->opaque[2]); #else sprintf(s, "(%08x.%08x.%08x.%08x)", f->opaque[0], f->opaque[1], f->opaque[2], f->opaque[3]); --- linux-2.6.9-rc1/fs/coda/file.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/fs/coda/file.c 2004-09-03 00:57:22.986544776 -0700 @@ -45,6 +45,23 @@ coda_file_read(struct file *coda_file, c } static ssize_t +coda_file_sendfile(struct file *coda_file, loff_t *ppos, size_t count, + read_actor_t actor, void __user *target) +{ + struct coda_file_info *cfi; + struct file *host_file; + + cfi = CODA_FTOC(coda_file); + BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); + host_file = cfi->cfi_container; + + if (!host_file->f_op || !host_file->f_op->sendfile) + return -EINVAL; + + return host_file->f_op->sendfile(host_file, ppos, count, actor, target); +} + +static ssize_t coda_file_write(struct file *coda_file, const char __user *buf, size_t count, loff_t *ppos) { struct inode *host_inode, *coda_inode = coda_file->f_dentry->d_inode; @@ -278,5 +295,6 @@ struct file_operations coda_file_operati .flush = coda_flush, .release = coda_release, .fsync = coda_fsync, + .sendfile = coda_file_sendfile, }; --- linux-2.6.9-rc1/fs/coda/inode.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/fs/coda/inode.c 2004-09-02 20:51:52.000000000 -0700 @@ -69,7 +69,7 @@ int coda_init_inodecache(void) { coda_inode_cachep = kmem_cache_create("coda_inode_cache", sizeof(struct coda_inode_info), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (coda_inode_cachep == NULL) return -ENOMEM; --- linux-2.6.9-rc1/fs/coda/psdev.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/fs/coda/psdev.c 2004-09-03 00:57:22.848565752 -0700 @@ -405,7 +405,7 @@ static int __init init_coda(void) int status; int i; printk(KERN_INFO "Coda Kernel/Venus communications, " -#ifdef CODA_FS_OLD_API +#ifdef CONFIG_CODA_FS_OLD_API "v5.3.20" #else "v6.0.0" --- linux-2.6.9-rc1/fs/coda/upcall.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/fs/coda/upcall.c 2004-09-03 00:57:22.849565600 -0700 @@ -55,7 +55,7 @@ static void *alloc_upcall(int opcode, in inp->ih.opcode = opcode; inp->ih.pid = current->pid; inp->ih.pgid = process_group(current); -#ifdef CODA_FS_OLD_API +#ifdef CONFIG_CODA_FS_OLD_API memset(&inp->ih.cred, 0, sizeof(struct coda_cred)); inp->ih.cred.cr_fsuid = current->fsuid; #else @@ -172,7 +172,7 @@ int venus_store(struct super_block *sb, union inputArgs *inp; union outputArgs *outp; int insize, outsize, error; -#ifdef CODA_FS_OLD_API +#ifdef CONFIG_CODA_FS_OLD_API struct coda_cred cred = { 0, }; cred.cr_fsuid = uid; #endif @@ -180,7 +180,7 @@ int venus_store(struct super_block *sb, insize = SIZE(store); UPARG(CODA_STORE); -#ifdef CODA_FS_OLD_API +#ifdef CONFIG_CODA_FS_OLD_API memcpy(&(inp->ih.cred), &cred, sizeof(cred)); #else inp->ih.uid = uid; @@ -219,7 +219,7 @@ int venus_close(struct super_block *sb, union inputArgs *inp; union outputArgs *outp; int insize, outsize, error; -#ifdef CODA_FS_OLD_API +#ifdef CONFIG_CODA_FS_OLD_API struct coda_cred cred = { 0, }; cred.cr_fsuid = uid; #endif @@ -227,7 +227,7 @@ int venus_close(struct super_block *sb, insize = SIZE(release); UPARG(CODA_CLOSE); -#ifdef CODA_FS_OLD_API +#ifdef CONFIG_CODA_FS_OLD_API memcpy(&(inp->ih.cred), &cred, sizeof(cred)); #else inp->ih.uid = uid; --- linux-2.6.9-rc1/fs/compat.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/fs/compat.c 2004-09-03 00:57:23.248504952 -0700 @@ -41,6 +41,7 @@ #include #include #include +#include #include /* siocdevprivate_ioctl */ @@ -247,7 +248,8 @@ out: /* ioctl32 stuff, used by sparc64, parisc, s390x, ppc64, x86_64, MIPS */ #define IOCTL_HASHSIZE 256 -struct ioctl_trans *ioctl32_hash_table[IOCTL_HASHSIZE]; +static struct ioctl_trans *ioctl32_hash_table[IOCTL_HASHSIZE]; +static DECLARE_RWSEM(ioctl32_sem); extern struct ioctl_trans ioctl_start[]; extern int ioctl_table_size; @@ -291,8 +293,8 @@ static int __init init_sys32_ioctl(void) __initcall(init_sys32_ioctl); -int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, - unsigned int, unsigned long, struct file *)) +int register_ioctl32_conversion(unsigned int cmd, + ioctl_trans_handler_t handler) { struct ioctl_trans *t; struct ioctl_trans *new_t; @@ -302,12 +304,12 @@ int register_ioctl32_conversion(unsigned if (!new_t) return -ENOMEM; - lock_kernel(); + down_write(&ioctl32_sem); for (t = ioctl32_hash_table[hash]; t; t = t->next) { if (t->cmd == cmd) { printk(KERN_ERR "Trying to register duplicated ioctl32 " "handler %x\n", cmd); - unlock_kernel(); + up_write(&ioctl32_sem); kfree(new_t); return -EINVAL; } @@ -317,7 +319,7 @@ int register_ioctl32_conversion(unsigned new_t->handler = handler; ioctl32_insert_translation(new_t); - unlock_kernel(); + up_write(&ioctl32_sem); return 0; } EXPORT_SYMBOL(register_ioctl32_conversion); @@ -337,11 +339,11 @@ int unregister_ioctl32_conversion(unsign unsigned long hash = ioctl32_hash(cmd); struct ioctl_trans *t, *t1; - lock_kernel(); + down_write(&ioctl32_sem); t = ioctl32_hash_table[hash]; if (!t) { - unlock_kernel(); + up_write(&ioctl32_sem); return -EINVAL; } @@ -351,7 +353,7 @@ int unregister_ioctl32_conversion(unsign __builtin_return_address(0), cmd); } else { ioctl32_hash_table[hash] = t->next; - unlock_kernel(); + up_write(&ioctl32_sem); kfree(t); return 0; } @@ -366,7 +368,7 @@ int unregister_ioctl32_conversion(unsign goto out; } else { t->next = t1->next; - unlock_kernel(); + up_write(&ioctl32_sem); kfree(t1); return 0; } @@ -376,7 +378,7 @@ int unregister_ioctl32_conversion(unsign printk(KERN_ERR "Trying to free unknown 32bit ioctl handler %x\n", cmd); out: - unlock_kernel(); + up_write(&ioctl32_sem); return -EINVAL; } EXPORT_SYMBOL(unregister_ioctl32_conversion); @@ -397,7 +399,7 @@ asmlinkage long compat_sys_ioctl(unsigne goto out; } - lock_kernel(); + down_read(&ioctl32_sem); t = ioctl32_hash_table[ioctl32_hash (cmd)]; @@ -405,14 +407,16 @@ asmlinkage long compat_sys_ioctl(unsigne t = t->next; if (t) { if (t->handler) { + lock_kernel(); error = t->handler(fd, cmd, arg, filp); unlock_kernel(); + up_read(&ioctl32_sem); } else { - unlock_kernel(); + up_read(&ioctl32_sem); error = sys_ioctl(fd, cmd, arg); } } else { - unlock_kernel(); + up_read(&ioctl32_sem); if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) { error = siocdevprivate_ioctl(fd, cmd, arg); } else { @@ -429,6 +433,8 @@ asmlinkage long compat_sys_ioctl(unsigne fn = d_path(filp->f_dentry, filp->f_vfsmnt, path, PAGE_SIZE); + if (IS_ERR(fn)) + fn = "?"; } sprintf(buf,"'%c'", (cmd>>24) & 0x3f); @@ -979,19 +985,14 @@ static int compat_filldir64(void * __buf dirent = buf->previous; if (dirent) { - if (__put_user(offset, (u32 __user *)&dirent->d_off)) - goto efault; - if (__put_user(offset >> 32, - ((u32 __user *)&dirent->d_off) + 1)) + if (__put_user_unaligned(offset, &dirent->d_off)) goto efault; } dirent = buf->current_dir; - if ((__put_user(ino, (u32 __user *)&dirent->d_ino)) - || (__put_user(ino >> 32, ((u32 __user *)&dirent->d_ino) + 1))) + if (__put_user_unaligned(ino, &dirent->d_ino)) goto efault; off = 0; - if ((__put_user(off, (u32 __user *)&dirent->d_off)) - || (__put_user(off >> 32, ((u32 __user *)&dirent->d_off) + 1))) + if (__put_user_unaligned(off, &dirent->d_off)) goto efault; if (__put_user(reclen, &dirent->d_reclen)) goto efault; @@ -1040,8 +1041,7 @@ asmlinkage long compat_sys_getdents64(un lastdirent = buf.previous; if (lastdirent) { typeof(lastdirent->d_off) d_off = file->f_pos; - __put_user(d_off, (u32 __user *)&lastdirent->d_off); - __put_user(d_off >> 32, ((u32 __user *)&lastdirent->d_off) + 1); + __put_user_unaligned(d_off, &lastdirent->d_off); error = count - buf.count; } @@ -1373,14 +1373,14 @@ int compat_do_execve(char * filename, int retval; int i; - sched_balance_exec(); - file = open_exec(filename); retval = PTR_ERR(file); if (IS_ERR(file)) return retval; + sched_exec(); + bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0])); @@ -1390,6 +1390,8 @@ int compat_do_execve(char * filename, bprm.sh_bang = 0; bprm.loader = 0; bprm.exec = 0; + bprm.interp_flags = 0; + bprm.interp_data = 0; bprm.security = NULL; bprm.mm = mm_alloc(); retval = -ENOMEM; --- linux-2.6.9-rc1/fs/compat_ioctl.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/fs/compat_ioctl.c 2004-09-03 00:56:49.706604096 -0700 @@ -114,6 +114,9 @@ #include #include #include +#include + +#include #undef INCLUDES #endif --- linux-2.6.9-rc1/fs/dcache.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/fs/dcache.c 2004-09-02 20:51:52.000000000 -0700 @@ -144,6 +144,8 @@ void dput(struct dentry *dentry) return; repeat: + if (atomic_read(&dentry->d_count) == 1) + might_sleep(); if (!atomic_dec_and_lock(&dentry->d_count, &dcache_lock)) return; --- linux-2.6.9-rc1/fs/devpts/inode.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/fs/devpts/inode.c 2004-09-02 20:51:52.000000000 -0700 @@ -179,8 +179,12 @@ struct tty_struct *devpts_get_tty(int nu struct dentry *dentry = get_node(number); struct tty_struct *tty; - tty = (IS_ERR(dentry) || !dentry->d_inode) ? NULL : - dentry->d_inode->u.generic_ip; + tty = NULL; + if (!IS_ERR(dentry)) { + if (dentry->d_inode) + tty = dentry->d_inode->u.generic_ip; + dput(dentry); + } up(&devpts_root->d_inode->i_sem); --- linux-2.6.9-rc1/fs/efs/super.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/fs/efs/super.c 2004-09-02 20:51:52.000000000 -0700 @@ -58,7 +58,7 @@ static int init_inodecache(void) { efs_inode_cachep = kmem_cache_create("efs_inode_cache", sizeof(struct efs_inode_info), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (efs_inode_cachep == NULL) return -ENOMEM; --- linux-2.6.9-rc1/fs/exec.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/fs/exec.c 2004-09-03 00:56:54.691846224 -0700 @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -433,7 +434,7 @@ int setup_arg_pages(struct linux_binprm mpnt->vm_flags |= mm->def_flags; mpnt->vm_page_prot = protection_map[mpnt->vm_flags & 0x7]; insert_vm_struct(mm, mpnt); - mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT; + mm->stack_vm = mm->total_vm = vma_pages(mpnt); } for (i = 0 ; i < MAX_ARG_PAGES ; i++) { @@ -546,6 +547,7 @@ static int exec_mmap(struct mm_struct *m tsk->active_mm = mm; activate_mm(active_mm, mm); task_unlock(tsk); + arch_pick_mmap_layout(mm); if (old_mm) { if (active_mm != old_mm) BUG(); mmput(old_mm); @@ -857,8 +859,10 @@ int flush_old_exec(struct linux_binprm * if (bprm->e_uid != current->euid || bprm->e_gid != current->egid || permission(bprm->file->f_dentry->d_inode,MAY_READ, NULL) || - (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)) + (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)) { + suid_keys(current); current->mm->dumpable = 0; + } /* An exec changes our domain. We are no longer part of the thread group */ @@ -952,6 +956,11 @@ static inline int unsafe_exec(struct tas void compute_creds(struct linux_binprm *bprm) { int unsafe; + + if (bprm->e_uid != current->uid) + suid_keys(current); + exec_keys(current); + task_lock(current); unsafe = unsafe_exec(current); security_bprm_apply_creds(bprm, unsafe); @@ -1102,7 +1111,7 @@ int do_execve(char * filename, if (IS_ERR(file)) return retval; - sched_balance_exec(); + sched_exec(); bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0])); @@ -1210,7 +1219,7 @@ EXPORT_SYMBOL(set_binfmt); * name into corename, which must have space for at least * CORENAME_MAX_SIZE bytes plus one byte for the zero terminator. */ -void format_corename(char *corename, const char *pattern, long signr) +static void format_corename(char *corename, const char *pattern, long signr) { const char *pat_ptr = pattern; char *out_ptr = corename; @@ -1374,7 +1383,6 @@ int do_coredump(long signr, int exit_cod struct file * file; int retval = 0; - lock_kernel(); binfmt = current->binfmt; if (!binfmt || !binfmt->core_dump) goto fail; @@ -1392,7 +1400,13 @@ int do_coredump(long signr, int exit_cod if (current->rlim[RLIMIT_CORE].rlim_cur < binfmt->min_coredump) goto fail_unlock; - format_corename(corename, core_pattern, signr); + /* + * lock_kernel() because format_corename() is controlled by sysctl, which + * uses lock_kernel() + */ + lock_kernel(); + format_corename(corename, core_pattern, signr); + unlock_kernel(); file = filp_open(corename, O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE, 0600); if (IS_ERR(file)) goto fail_unlock; @@ -1419,6 +1433,5 @@ close_fail: fail_unlock: complete_all(&mm->core_done); fail: - unlock_kernel(); return retval; } --- linux-2.6.9-rc1/fs/exportfs/expfs.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/fs/exportfs/expfs.c 2004-09-03 00:57:26.216053816 -0700 @@ -283,7 +283,12 @@ find_exported_dentry(struct super_block /* drat - I just cannot find anything acceptable */ dput(result); - return ERR_PTR(-ESTALE); + /* It might be justifiable to return ESTALE here, + * but the filehandle at-least looks reasonable good + * and it just be a permission problem, so returning + * -EACCESS is safer + */ + return ERR_PTR(-EACCES); err_target: dput(target_dir); --- linux-2.6.9-rc1/fs/ext2/ext2.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/fs/ext2/ext2.h 2004-09-02 20:51:52.000000000 -0700 @@ -131,9 +131,6 @@ extern int ext2_ioctl (struct inode *, s /* super.c */ extern void ext2_error (struct super_block *, const char *, const char *, ...) __attribute__ ((format (printf, 3, 4))); -extern NORET_TYPE void ext2_panic (struct super_block *, const char *, - const char *, ...) - __attribute__ ((NORET_AND format (printf, 3, 4))); extern void ext2_warning (struct super_block *, const char *, const char *, ...) __attribute__ ((format (printf, 3, 4))); extern void ext2_update_dynamic_rev (struct super_block *sb); --- linux-2.6.9-rc1/fs/ext2/super.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/fs/ext2/super.c 2004-09-02 20:51:52.000000000 -0700 @@ -190,7 +190,7 @@ static int init_inodecache(void) { ext2_inode_cachep = kmem_cache_create("ext2_inode_cache", sizeof(struct ext2_inode_info), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (ext2_inode_cachep == NULL) return -ENOMEM; --- linux-2.6.9-rc1/fs/ext3/balloc.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/fs/ext3/balloc.c 2004-09-03 00:56:47.079003552 -0700 @@ -96,9 +96,87 @@ read_block_bitmap(struct super_block *sb error_out: return bh; } +/* + * The reservation window structure operations + * -------------------------------------------- + * Operations include: + * dump, find, add, remove, is_empty, find_next_reservable_window, etc. + * + * We use sorted double linked list for the per-filesystem reservation + * window list. (like in vm_region). + * + * Initially, we keep those small operations in the abstract functions, + * so later if we need a better searching tree than double linked-list, + * we could easily switch to that without changing too much + * code. + */ +static inline void rsv_window_dump(struct reserve_window *head, char *fn) +{ + struct reserve_window *rsv; + + printk("Block Allocation Reservation Windows Map (%s):\n", fn); + list_for_each_entry(rsv, &head->rsv_list, rsv_list) { + printk("reservation window 0x%p start: %d, end: %d\n", + rsv, rsv->rsv_start, rsv->rsv_end); + } +} + +static int +goal_in_my_reservation(struct reserve_window *rsv, int goal, + unsigned int group, struct super_block * sb) +{ + unsigned long group_first_block, group_last_block; + + group_first_block = le32_to_cpu(EXT3_SB(sb)->s_es->s_first_data_block) + + group * EXT3_BLOCKS_PER_GROUP(sb); + group_last_block = group_first_block + EXT3_BLOCKS_PER_GROUP(sb) - 1; + + if ((rsv->rsv_start > group_last_block) || + (rsv->rsv_end < group_first_block)) + return 0; + if ((goal >= 0) && ((goal + group_first_block < rsv->rsv_start) + || (goal + group_first_block > rsv->rsv_end))) + return 0; + return 1; +} + +static inline void rsv_window_add(struct reserve_window *rsv, + struct reserve_window *prev) +{ + /* insert the new reservation window after the head */ + list_add(&rsv->rsv_list, &prev->rsv_list); +} + +static inline void rsv_window_remove(struct reserve_window *rsv) +{ + rsv->rsv_start = 0; + rsv->rsv_end = 0; + rsv->rsv_alloc_hit = 0; + list_del(&rsv->rsv_list); + INIT_LIST_HEAD(&rsv->rsv_list); +} + +static inline int rsv_is_empty(struct reserve_window *rsv) +{ + /* a valid reservation end block could not be 0 */ + return (rsv->rsv_end == 0); +} + +void ext3_discard_reservation(struct inode *inode) +{ + struct ext3_inode_info *ei = EXT3_I(inode); + struct reserve_window *rsv = &ei->i_rsv_window; + spinlock_t *rsv_lock = &EXT3_SB(inode->i_sb)->s_rsv_window_lock; + + if (!rsv_is_empty(rsv)) { + spin_lock(rsv_lock); + rsv_window_remove(rsv); + spin_unlock(rsv_lock); + } +} /* Free given blocks, update quota and i_blocks field */ -void ext3_free_blocks (handle_t *handle, struct inode * inode, +void ext3_free_blocks(handle_t *handle, struct inode *inode, unsigned long block, unsigned long count) { struct buffer_head *bitmap_bh = NULL; @@ -275,7 +353,7 @@ do_more: error_return: brelse(bitmap_bh); ext3_std_error(sb, err); - if (dquot_freed_blocks) + if (dquot_freed_blocks && !(EXT3_I(inode)->i_state & EXT3_STATE_RESIZE)) DQUOT_FREE_BLOCK(inode, dquot_freed_blocks); return; } @@ -296,7 +374,7 @@ error_return: * data-writes at some point, and disable it for metadata allocations or * sync-data inodes. */ -static inline int ext3_test_allocatable(int nr, struct buffer_head *bh) +static int ext3_test_allocatable(int nr, struct buffer_head *bh) { int ret; struct journal_head *jh = bh2jh(bh); @@ -313,6 +391,33 @@ static inline int ext3_test_allocatable( return ret; } +static int +bitmap_search_next_usable_block(int start, struct buffer_head *bh, + int maxblocks) +{ + int next; + struct journal_head *jh = bh2jh(bh); + + /* + * The bitmap search --- search forward alternately through the actual + * bitmap and the last-committed copy until we find a bit free in + * both + */ + while (start < maxblocks) { + next = ext3_find_next_zero_bit(bh->b_data, maxblocks, start); + if (next >= maxblocks) + return -1; + if (ext3_test_allocatable(next, bh)) + return next; + jbd_lock_bh_state(bh); + if (jh->b_committed_data) + start = ext3_find_next_zero_bit(jh->b_committed_data, + maxblocks, next); + jbd_unlock_bh_state(bh); + } + return -1; +} + /* * Find an allocatable block in a bitmap. We honour both the bitmap and * its last-committed copy (if that exists), and perform the "most @@ -325,7 +430,6 @@ find_next_usable_block(int start, struct { int here, next; char *p, *r; - struct journal_head *jh = bh2jh(bh); if (start > 0) { /* @@ -337,6 +441,8 @@ find_next_usable_block(int start, struct * next 64-bit boundary is simple.. */ int end_goal = (start + 63) & ~63; + if (end_goal > maxblocks) + end_goal = maxblocks; here = ext3_find_next_zero_bit(bh->b_data, end_goal, start); if (here < end_goal && ext3_test_allocatable(here, bh)) return here; @@ -351,7 +457,7 @@ find_next_usable_block(int start, struct r = memscan(p, 0, (maxblocks - here + 7) >> 3); next = (r - ((char *)bh->b_data)) << 3; - if (next < maxblocks && ext3_test_allocatable(next, bh)) + if (next < maxblocks && next >= start && ext3_test_allocatable(next, bh)) return next; /* @@ -359,19 +465,8 @@ find_next_usable_block(int start, struct * bitmap and the last-committed copy until we find a bit free in * both */ - while (here < maxblocks) { - next = ext3_find_next_zero_bit(bh->b_data, maxblocks, here); - if (next >= maxblocks) - return -1; - if (ext3_test_allocatable(next, bh)) - return next; - jbd_lock_bh_state(bh); - if (jh->b_committed_data) - here = ext3_find_next_zero_bit(jh->b_committed_data, - maxblocks, next); - jbd_unlock_bh_state(bh); - } - return -1; + here = bitmap_search_next_usable_block(here, bh, maxblocks); + return here; } /* @@ -407,62 +502,464 @@ claim_block(spinlock_t *lock, int block, */ static int ext3_try_to_allocate(struct super_block *sb, handle_t *handle, int group, - struct buffer_head *bitmap_bh, int goal, int *errp) + struct buffer_head *bitmap_bh, int goal, struct reserve_window *my_rsv) { - int i; - int fatal; - int credits = 0; + int group_first_block, start, end; - *errp = 0; - - /* - * Make sure we use undo access for the bitmap, because it is critical - * that we do the frozen_data COW on bitmap buffers in all cases even - * if the buffer is in BJ_Forget state in the committing transaction. - */ - BUFFER_TRACE(bitmap_bh, "get undo access for new block"); - fatal = ext3_journal_get_undo_access(handle, bitmap_bh, &credits); - if (fatal) { - *errp = fatal; - goto fail; + /* we do allocation within the reservation window if we have a window */ + if (my_rsv) { + group_first_block = + le32_to_cpu(EXT3_SB(sb)->s_es->s_first_data_block) + + group * EXT3_BLOCKS_PER_GROUP(sb); + if (my_rsv->rsv_start >= group_first_block) + start = my_rsv->rsv_start - group_first_block; + else + /* reservation window cross group boundary */ + start = 0; + end = my_rsv->rsv_end - group_first_block + 1; + if (end > EXT3_BLOCKS_PER_GROUP(sb)) + /* reservation window crosses group boundary */ + end = EXT3_BLOCKS_PER_GROUP(sb); + if ((start <= goal) && (goal < end)) + start = goal; + else + goal = -1; + } else { + if (goal > 0) + start = goal; + else + start = 0; + end = EXT3_BLOCKS_PER_GROUP(sb); } + BUG_ON(start > EXT3_BLOCKS_PER_GROUP(sb)); + repeat: if (goal < 0 || !ext3_test_allocatable(goal, bitmap_bh)) { - goal = find_next_usable_block(goal, bitmap_bh, - EXT3_BLOCKS_PER_GROUP(sb)); + goal = find_next_usable_block(start, bitmap_bh, end); if (goal < 0) goto fail_access; + if (!my_rsv) { + int i; - for (i = 0; i < 7 && goal > 0 && - ext3_test_allocatable(goal - 1, bitmap_bh); - i++, goal--); + for (i = 0; i < 7 && goal > start && + ext3_test_allocatable(goal - 1, + bitmap_bh); + i++, goal--) + ; + } } + start = goal; if (!claim_block(sb_bgl_lock(EXT3_SB(sb), group), goal, bitmap_bh)) { /* * The block was allocated by another thread, or it was * allocated and then freed by another thread */ + start++; goal++; - if (goal >= EXT3_BLOCKS_PER_GROUP(sb)) + if (start >= end) goto fail_access; goto repeat; } + if (my_rsv) + my_rsv->rsv_alloc_hit++; + return goal; +fail_access: + return -1; +} + +/** + * find_next_reservable_window(): + * find a reservable space within the given range + * It does not allocate the reservation window for now + * alloc_new_reservation() will do the work later. + * + * @search_head: the head of the searching list; + * This is not necessary the list head of the whole filesystem + * + * we have both head and start_block to assist the search + * for the reservable space. The list start from head, + * but we will shift to the place where start_block is, + * then start from there, we looking for a resevable space. + * + * @fs_rsv_head: per-filesystem reervation list head. + * + * @size: the target new reservation window size + * @group_first_block: the first block we consider to start + * the real search from + * + * @last_block: + * the maxium block number that our goal reservable space + * could start from. This is normally the last block in this + * group. The search will end when we found the start of next + * possiblereservable space is out of this boundary. + * This could handle the cross bounday reservation window request. + * + * basically we search from the given range, rather than the whole + * reservation double linked list, (start_block, last_block) + * to find a free region that of of my size and has not + * been reserved. + * + * on succeed, it returns the reservation window to be append to. + * failed, return NULL. + */ +static inline +struct reserve_window *find_next_reservable_window( + struct reserve_window *search_head, + struct reserve_window *fs_rsv_head, + unsigned long size, int *start_block, + int last_block) +{ + struct reserve_window *rsv; + int cur; + + /* TODO:make the start of the reservation window byte alligned */ + /*cur = *start_block & 8;*/ + cur = *start_block; + rsv = list_entry(search_head->rsv_list.next, + struct reserve_window, rsv_list); + while (rsv != fs_rsv_head) { + if (cur + size <= rsv->rsv_start) { + /* + * Found a reserveable space big enough. We could + * have a reservation across the group boundary here + */ + break; + } + if (cur <= rsv->rsv_end) + cur = rsv->rsv_end + 1; - BUFFER_TRACE(bitmap_bh, "journal_dirty_metadata for bitmap block"); - fatal = ext3_journal_dirty_metadata(handle, bitmap_bh); + /* TODO? + * in the case we could not find a reservable space + * that is what is expected, during the re-search, we could + * remember what's the largest reservable space we could have + * and return that one. + * + * For now it will fail if we could not find the reservable + * space with expected-size (or more)... + */ + rsv = list_entry(rsv->rsv_list.next, + struct reserve_window, rsv_list); + if (cur > last_block) + return NULL; /* fail */ + } + /* + * we come here either : + * when we rearch to the end of the whole list, + * and there is empty reservable space after last entry in the list. + * append it to the end of the list. + * + * or we found one reservable space in the middle of the list, + * return the reservation window that we could append to. + * succeed. + */ + *start_block = cur; + return list_entry(rsv->rsv_list.prev, struct reserve_window, rsv_list); +} + +/** + * alloc_new_reservation()--allocate a new reservation window + * if there is an existing reservation, discard it first + * then allocate the new one from there + * otherwise allocate the new reservation from the given + * start block, or the beginning of the group, if a goal + * is not given. + * + * To make a new reservation, we search part of the filesystem + * reservation list(the list that inside the group). + * + * If we have a old reservation, the search goal is the end of + * last reservation. If we do not have a old reservatio, then we + * start from a given goal, or the first block of the group, if + * the goal is not given. + * + * We first find a reservable space after the goal, then from + * there,we check the bitmap for the first free block after + * it. If there is no free block until the end of group, then the + * whole group is full, we failed. Otherwise, check if the free + * block is inside the expected reservable space, if so, we + * succeed. + * If the first free block is outside the reseravle space, then + * start from the first free block, we search for next avalibale + * space, and go on. + * + * on succeed, a new reservation will be found and inserted into the list + * It contains at least one free block, and it is not overlap with other + * reservation window. + * + * failed: we failed to found a reservation window in this group + * + * @rsv: the reservation + * + * @goal: The goal. It is where the search for a + * free reservable space should start from. + * if we have a old reservation, start_block is the end of + * old reservation. Otherwise, + * if we have a goal(goal >0 ), then start from there, + * no goal(goal = -1), we start from the first block + * of the group. + * + * @sb: the super block + * @group: the group we are trying to do allocate in + * @bitmap_bh: the block group block bitmap + */ +static int alloc_new_reservation(struct reserve_window *my_rsv, + int goal, struct super_block *sb, + unsigned int group, struct buffer_head *bitmap_bh) +{ + struct reserve_window *search_head; + int group_first_block, group_end_block, start_block; + int first_free_block; + int reservable_space_start; + struct reserve_window *prev_rsv; + struct reserve_window *fs_rsv_head = &EXT3_SB(sb)->s_rsv_window_head; + unsigned long size; + + group_first_block = le32_to_cpu(EXT3_SB(sb)->s_es->s_first_data_block) + + group * EXT3_BLOCKS_PER_GROUP(sb); + group_end_block = group_first_block + EXT3_BLOCKS_PER_GROUP(sb) - 1; + + if (goal < 0) + start_block = group_first_block; + else + start_block = goal + group_first_block; + + size = atomic_read(&my_rsv->rsv_goal_size); + /* if we have a old reservation, start the search from the old rsv */ + if (!rsv_is_empty(my_rsv)) { + /* + * if the old reservation is cross group boundary + * we will come here when we just failed to allocate from + * the first part of the window. We still have another part + * that belongs to the next group. In this case, there is no + * point to discard our window and try to allocate a new one + * in this group(which will fail). we should + * keep the reservation window, just simply move on. + * + * Maybe we could shift the start block of the reservation + * window to the first block of next group. + */ + + if ((my_rsv->rsv_start <= group_end_block) && + (my_rsv->rsv_end > group_end_block)) + return -1; + + /* remember where we are before we discard the old one */ + if (my_rsv->rsv_end + 1 > start_block) + start_block = my_rsv->rsv_end + 1; + search_head = my_rsv; + if ((my_rsv->rsv_alloc_hit > (my_rsv->rsv_end - my_rsv->rsv_start + 1) / 2)) { + /* + * if we previously allocation hit ration is greater than half + * we double the size of reservation window next time + * otherwise keep the same + */ + size = size * 2; + if (size > EXT3_MAX_RESERVE_BLOCKS) + size = EXT3_MAX_RESERVE_BLOCKS; + atomic_set(&my_rsv->rsv_goal_size, size); + } + } + else { + /* + * we don't have a reservation, + * we set our goal(start_block) and + * the list head for the search + */ + search_head = fs_rsv_head; + } + + /* + * find_next_reservable_window() simply find a reservable window + * inside the given range(start_block, group_end_block). + * + * To make sure the reservation window has a free bit inside it, we + * need to check the bitmap after we found a reservable window. + */ +retry: + prev_rsv = find_next_reservable_window(search_head, fs_rsv_head, size, + &start_block, group_end_block); + if (prev_rsv == NULL) + goto failed; + reservable_space_start = start_block; + /* + * On success, find_next_reservable_window() returns the + * reservation window where there is a reservable space after it. + * Before we reserve this reservable space, we need + * to make sure there is at least a free block inside this region. + * + * searching the first free bit on the block bitmap and copy of + * last committed bitmap alternatively, until we found a allocatable + * block. Search start from the start block of the reservable space + * we just found. + */ + first_free_block = bitmap_search_next_usable_block( + reservable_space_start - group_first_block, + bitmap_bh, group_end_block - group_first_block + 1); + + if (first_free_block < 0) { + /* + * no free block left on the bitmap, no point + * to reserve the space. return failed. + */ + goto failed; + } + start_block = first_free_block + group_first_block; + /* + * check if the first free block is within the + * free space we just found + */ + if ((start_block >= reservable_space_start) && + (start_block < reservable_space_start + size)) + goto found_rsv_window; + /* + * if the first free bit we found is out of the reservable space + * this means there is no free block on the reservable space + * we should continue search for next reservable space, + * start from where the free block is, + * we also shift the list head to where we stopped last time + */ + search_head = prev_rsv; + goto retry; + +found_rsv_window: + /* + * great! the reservable space contains some free blocks. + * if the search returns that we should add the new + * window just next to where the old window, we don't + * need to remove the old window first then add it to the + * same place, just update the new start and new end. + */ + if (my_rsv != prev_rsv) { + if (!rsv_is_empty(my_rsv)) + rsv_window_remove(my_rsv); + rsv_window_add(my_rsv, prev_rsv); + } + my_rsv->rsv_start = reservable_space_start; + my_rsv->rsv_end = my_rsv->rsv_start + size - 1; + return 0; /* succeed */ +failed: + return -1; /* failed */ +} + +/* + * This is the main function used to allocate a new block and its reservation + * window. + * + * Each time when a new block allocation is need, first try to allocate from + * its own reservation. If it does not have a reservation window, instead of + * looking for a free bit on bitmap first, then look up the reservation list to + * see if it is inside somebody else's reservation window, we try to allocate a + * reservation window for it start from the goal first. Then do the block + * allocation within the reservation window. + * + * This will aviod keep searching the reservation list again and again when + * someboday is looking for a free block(without reservation), and there are + * lots of free blocks, but they are all being reserved + * + * We use a sorted double linked list for the per-filesystem reservation list. + * The insert, remove and find a free space(non-reserved) operations for the + * sorted double linked list should be fast. + * + */ +static int +ext3_try_to_allocate_with_rsv(struct super_block *sb, handle_t *handle, + unsigned int group, struct buffer_head *bitmap_bh, + int goal, struct reserve_window * my_rsv, + int *errp) +{ + spinlock_t *rsv_lock; + unsigned long group_first_block; + int ret = 0; + int fatal; + int credits = 0; + + *errp = 0; + + /* + * Make sure we use undo access for the bitmap, because it is critical + * that we do the frozen_data COW on bitmap buffers in all cases even + * if the buffer is in BJ_Forget state in the committing transaction. + */ + BUFFER_TRACE(bitmap_bh, "get undo access for new block"); + fatal = ext3_journal_get_undo_access(handle, bitmap_bh, &credits); if (fatal) { *errp = fatal; - goto fail; + return -1; + } + + /* + * we don't deal with reservation when + * filesystem is mounted without reservation + * or the file is not a regular file + * of last attemp of allocating a block with reservation turn on failed + */ + if (my_rsv == NULL ) { + ret = ext3_try_to_allocate(sb, handle, group, bitmap_bh, goal, NULL); + goto out; + } + rsv_lock = &EXT3_SB(sb)->s_rsv_window_lock; + /* + * goal is a group relative block number (if there is a goal) + * 0 < goal < EXT3_BLOCKS_PER_GROUP(sb) + * first block is a filesystem wide block number + * first block is the block number of the first block in this group + */ + group_first_block = le32_to_cpu(EXT3_SB(sb)->s_es->s_first_data_block) + + group * EXT3_BLOCKS_PER_GROUP(sb); + + /* + * Basically we will allocate a new block from inode's reservation + * window. + * + * We need to allocate a new reservation window, if: + * a) inode does not have a reservation window; or + * b) last attemp of allocating a block from existing reservation + * failed; or + * c) we come here with a goal and with a reservation window + * + * We do not need to allocate a new reservation window if we come here + * at the beginning with a goal and the goal is inside the window, or + * or we don't have a goal but already have a reservation window. + * then we could go to allocate from the reservation window directly. + */ + while (1) { + if (rsv_is_empty(my_rsv) || (ret < 0) || + !goal_in_my_reservation(my_rsv, goal, group, sb)) { + spin_lock(rsv_lock); + ret = alloc_new_reservation(my_rsv, goal, sb, + group, bitmap_bh); + spin_unlock(rsv_lock); + if (ret < 0) + break; /* failed */ + + if (!goal_in_my_reservation(my_rsv, goal, group, sb)) + goal = -1; + } + if ((my_rsv->rsv_start >= group_first_block + EXT3_BLOCKS_PER_GROUP(sb)) + || (my_rsv->rsv_end < group_first_block)) + BUG(); + ret = ext3_try_to_allocate(sb, handle, group, bitmap_bh, goal, + my_rsv); + if (ret >= 0) + break; /* succeed */ + } +out: + if (ret >= 0) { + BUFFER_TRACE(bitmap_bh, "journal_dirty_metadata for " + "bitmap block"); + fatal = ext3_journal_dirty_metadata(handle, bitmap_bh); + if (fatal) { + *errp = fatal; + return -1; + } + return ret; } - return goal; -fail_access: BUFFER_TRACE(bitmap_bh, "journal_release_buffer"); ext3_journal_release_buffer(handle, bitmap_bh, credits); -fail: - return -1; + return ret; } static int ext3_has_free_blocks(struct ext3_sb_info *sbi) @@ -503,16 +1000,16 @@ int ext3_should_retry_alloc(struct super * bitmap, and then for any free bit if that fails. * This function also updates quota and i_blocks field. */ -int -ext3_new_block(handle_t *handle, struct inode *inode, unsigned long goal, - u32 *prealloc_count, u32 *prealloc_block, int *errp) -{ - struct buffer_head *bitmap_bh = NULL; /* bh */ - struct buffer_head *gdp_bh; /* bh2 */ - int group_no; /* i */ - int ret_block; /* j */ - int bgi; /* blockgroup iteration index */ - int target_block; /* tmp */ +int ext3_new_block(handle_t *handle, struct inode *inode, + unsigned long goal, int *errp) +{ + struct buffer_head *bitmap_bh = NULL; + struct buffer_head *gdp_bh; + int group_no; + int goal_group; + int ret_block; + int bgi; /* blockgroup iteration index */ + int target_block; int fatal = 0, err; int performed_allocation = 0; int free_blocks; @@ -520,6 +1017,7 @@ ext3_new_block(handle_t *handle, struct struct ext3_group_desc *gdp; struct ext3_super_block *es; struct ext3_sb_info *sbi; + struct reserve_window *my_rsv = NULL; #ifdef EXT3FS_DEBUG static int goal_hits, goal_attempts; #endif @@ -541,7 +1039,8 @@ ext3_new_block(handle_t *handle, struct sbi = EXT3_SB(sb); es = EXT3_SB(sb)->s_es; ext3_debug("goal=%lu.\n", goal); - + if (test_opt(sb, RESERVATION) && S_ISREG(inode->i_mode)) + my_rsv = &EXT3_I(inode)->i_rsv_window; if (!ext3_has_free_blocks(sbi)) { *errp = -ENOSPC; goto out; @@ -559,6 +1058,8 @@ ext3_new_block(handle_t *handle, struct if (!gdp) goto io_error; + goal_group = group_no; +retry: free_blocks = le16_to_cpu(gdp->bg_free_blocks_count); if (free_blocks > 0) { ret_block = ((goal - le32_to_cpu(es->s_first_data_block)) % @@ -566,8 +1067,8 @@ ext3_new_block(handle_t *handle, struct bitmap_bh = read_block_bitmap(sb, group_no); if (!bitmap_bh) goto io_error; - ret_block = ext3_try_to_allocate(sb, handle, group_no, - bitmap_bh, ret_block, &fatal); + ret_block = ext3_try_to_allocate_with_rsv(sb, handle, group_no, + bitmap_bh, ret_block, my_rsv, &fatal); if (fatal) goto out; if (ret_block >= 0) @@ -595,14 +1096,25 @@ ext3_new_block(handle_t *handle, struct bitmap_bh = read_block_bitmap(sb, group_no); if (!bitmap_bh) goto io_error; - ret_block = ext3_try_to_allocate(sb, handle, group_no, - bitmap_bh, -1, &fatal); + ret_block = ext3_try_to_allocate_with_rsv(sb, handle, group_no, + bitmap_bh, -1, my_rsv, &fatal); if (fatal) goto out; if (ret_block >= 0) goto allocated; } - + /* + * We may end up a bogus ealier ENOSPC error due to + * filesystem is "full" of reservations, but + * there maybe indeed free blocks avaliable on disk + * In this case, we just forget about the reservations + * just do block allocation as without reservations. + */ + if (my_rsv) { + my_rsv = NULL; + group_no = goal_group; + goto retry; + } /* No space left on the device */ *errp = -ENOSPC; goto out; --- linux-2.6.9-rc1/fs/ext3/file.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/fs/ext3/file.c 2004-09-03 00:56:44.103455904 -0700 @@ -33,8 +33,10 @@ */ static int ext3_release_file (struct inode * inode, struct file * filp) { - if (filp->f_mode & FMODE_WRITE) - ext3_discard_prealloc (inode); + /* if we are the last writer on the inode, drop the block reservation */ + if ((filp->f_mode & FMODE_WRITE) && + (atomic_read(&inode->i_writecount) == 1)) + ext3_discard_reservation(inode); if (is_dx(inode) && filp->private_data) ext3_htree_free_dir_info(filp->private_data); --- linux-2.6.9-rc1/fs/ext3/ialloc.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/fs/ext3/ialloc.c 2004-09-03 00:56:43.796502568 -0700 @@ -581,10 +581,11 @@ got: ei->i_file_acl = 0; ei->i_dir_acl = 0; ei->i_dtime = 0; -#ifdef EXT3_PREALLOCATE - ei->i_prealloc_block = 0; - ei->i_prealloc_count = 0; -#endif + ei->i_rsv_window.rsv_start = 0; + ei->i_rsv_window.rsv_end = 0; + atomic_set(&ei->i_rsv_window.rsv_goal_size, EXT3_DEFAULT_RESERVE_BLOCKS); + ei->i_rsv_window.rsv_alloc_hit = 0; + INIT_LIST_HEAD(&ei->i_rsv_window.rsv_list); ei->i_block_group = group; ext3_set_inode_flags(inode); --- linux-2.6.9-rc1/fs/ext3/inode.c 2004-08-24 00:03:14.000000000 -0700 +++ 25/fs/ext3/inode.c 2004-09-03 00:56:47.687910984 -0700 @@ -66,6 +66,8 @@ int ext3_forget(handle_t *handle, int is { int err; + might_sleep(); + BUFFER_TRACE(bh, "enter"); jbd_debug(4, "forgetting bh %p: is_metadata = %d, mode %o, " @@ -177,19 +179,6 @@ static int ext3_journal_test_restart(han } /* - * Called at each iput() - * - * The inode may be "bad" if ext3_read_inode() saw an error from - * ext3_get_inode(), so we need to check that to avoid freeing random disk - * blocks. - */ -void ext3_put_inode(struct inode *inode) -{ - if (!is_bad_inode(inode)) - ext3_discard_prealloc(inode); -} - -/* * Called at the last iput() if i_nlink is zero. */ void ext3_delete_inode (struct inode * inode) @@ -242,62 +231,12 @@ no_delete: clear_inode(inode); /* We must guarantee clearing of inode... */ } -void ext3_discard_prealloc (struct inode * inode) -{ -#ifdef EXT3_PREALLOCATE - struct ext3_inode_info *ei = EXT3_I(inode); - /* Writer: ->i_prealloc* */ - if (ei->i_prealloc_count) { - unsigned short total = ei->i_prealloc_count; - unsigned long block = ei->i_prealloc_block; - ei->i_prealloc_count = 0; - ei->i_prealloc_block = 0; - /* Writer: end */ - ext3_free_blocks (inode, block, total); - } -#endif -} - static int ext3_alloc_block (handle_t *handle, struct inode * inode, unsigned long goal, int *err) { unsigned long result; -#ifdef EXT3_PREALLOCATE -#ifdef EXT3FS_DEBUG - static unsigned long alloc_hits, alloc_attempts; -#endif - struct ext3_inode_info *ei = EXT3_I(inode); - /* Writer: ->i_prealloc* */ - if (ei->i_prealloc_count && - (goal == ei->i_prealloc_block || - goal + 1 == ei->i_prealloc_block)) - { - result = ei->i_prealloc_block++; - ei->i_prealloc_count--; - /* Writer: end */ - ext3_debug ("preallocation hit (%lu/%lu).\n", - ++alloc_hits, ++alloc_attempts); - } else { - ext3_discard_prealloc (inode); - ext3_debug ("preallocation miss (%lu/%lu).\n", - alloc_hits, ++alloc_attempts); - if (S_ISREG(inode->i_mode)) - result = ext3_new_block (inode, goal, - &ei->i_prealloc_count, - &ei->i_prealloc_block, err); - else - result = ext3_new_block(inode, goal, NULL, NULL, err); - /* - * AKPM: this is somewhat sticky. I'm not surprised it was - * disabled in 2.2's ext3. Need to integrate b_committed_data - * guarding with preallocation, if indeed preallocation is - * effective. - */ - } -#else - result = ext3_new_block(handle, inode, goal, NULL, NULL, err); -#endif + result = ext3_new_block(handle, inode, goal, err); return result; } @@ -974,52 +913,17 @@ struct buffer_head *ext3_bread(handle_t int block, int create, int *err) { struct buffer_head * bh; - int prev_blocks; - prev_blocks = inode->i_blocks; - - bh = ext3_getblk (handle, inode, block, create, err); + bh = ext3_getblk(handle, inode, block, create, err); if (!bh) return bh; -#ifdef EXT3_PREALLOCATE - /* - * If the inode has grown, and this is a directory, then use a few - * more of the preallocated blocks to keep directory fragmentation - * down. The preallocated blocks are guaranteed to be contiguous. - */ - if (create && - S_ISDIR(inode->i_mode) && - inode->i_blocks > prev_blocks && - EXT3_HAS_COMPAT_FEATURE(inode->i_sb, - EXT3_FEATURE_COMPAT_DIR_PREALLOC)) { - int i; - struct buffer_head *tmp_bh; - - for (i = 1; - EXT3_I(inode)->i_prealloc_count && - i < EXT3_SB(inode->i_sb)->s_es->s_prealloc_dir_blocks; - i++) { - /* - * ext3_getblk will zero out the contents of the - * directory for us - */ - tmp_bh = ext3_getblk(handle, inode, - block+i, create, err); - if (!tmp_bh) { - brelse (bh); - return 0; - } - brelse (tmp_bh); - } - } -#endif if (buffer_uptodate(bh)) return bh; - ll_rw_block (READ, 1, &bh); - wait_on_buffer (bh); + ll_rw_block(READ, 1, &bh); + wait_on_buffer(bh); if (buffer_uptodate(bh)) return bh; - brelse (bh); + put_bh(bh); *err = -EIO; return NULL; } @@ -2155,7 +2059,7 @@ void ext3_truncate(struct inode * inode) if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) return; - ext3_discard_prealloc(inode); + ext3_discard_reservation(inode); /* * We have to lock the EOF page here, because lock_page() nests @@ -2311,8 +2215,10 @@ static unsigned long ext3_get_inode_bloc struct buffer_head *bh; struct ext3_group_desc * gdp; + if ((ino != EXT3_ROOT_INO && ino != EXT3_JOURNAL_INO && + ino != EXT3_RESIZE_INO && ino < EXT3_FIRST_INO(sb)) || ino > le32_to_cpu( EXT3_SB(sb)->s_es->s_inodes_count)) { @@ -2548,11 +2454,11 @@ void ext3_read_inode(struct inode * inod } ei->i_disksize = inode->i_size; inode->i_generation = le32_to_cpu(raw_inode->i_generation); -#ifdef EXT3_PREALLOCATE - ei->i_prealloc_count = 0; -#endif ei->i_block_group = iloc.block_group; - + ei->i_rsv_window.rsv_start = 0; + ei->i_rsv_window.rsv_end= 0; + atomic_set(&ei->i_rsv_window.rsv_goal_size, EXT3_DEFAULT_RESERVE_BLOCKS); + INIT_LIST_HEAD(&ei->i_rsv_window.rsv_list); /* * NOTE! The in-memory inode i_data array is in little-endian order * even on big-endian machines: we do NOT byteswap the block numbers! @@ -2966,6 +2872,7 @@ int ext3_mark_inode_dirty(handle_t *hand struct ext3_iloc iloc; int err; + might_sleep(); err = ext3_reserve_inode_write(handle, inode, &iloc); if (!err) err = ext3_mark_iloc_dirty(handle, inode, &iloc); --- linux-2.6.9-rc1/fs/ext3/ioctl.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/fs/ext3/ioctl.c 2004-09-03 00:56:47.083002944 -0700 @@ -20,6 +20,7 @@ int ext3_ioctl (struct inode * inode, st { struct ext3_inode_info *ei = EXT3_I(inode); unsigned int flags; + unsigned short rsv_window_size; ext3_debug ("cmd = %u, arg = %lu\n", cmd, arg); @@ -151,6 +152,74 @@ flags_err: return ret; } #endif + case EXT3_IOC_GETRSVSZ: + if (test_opt(inode->i_sb, RESERVATION) && S_ISREG(inode->i_mode)) { + rsv_window_size = atomic_read(&ei->i_rsv_window.rsv_goal_size); + return put_user(rsv_window_size, (int *)arg); + } + return -ENOTTY; + case EXT3_IOC_SETRSVSZ: + if (!test_opt(inode->i_sb, RESERVATION) ||!S_ISREG(inode->i_mode)) + return -ENOTTY; + + if (IS_RDONLY(inode)) + return -EROFS; + + if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + return -EACCES; + + if (get_user(rsv_window_size, (int *)arg)) + return -EFAULT; + + if (rsv_window_size > EXT3_MAX_RESERVE_BLOCKS) + rsv_window_size = EXT3_MAX_RESERVE_BLOCKS; + atomic_set(&ei->i_rsv_window.rsv_goal_size, rsv_window_size); + return 0; + case EXT3_IOC_GROUP_EXTEND: { + unsigned long n_blocks_count; + struct super_block *sb = inode->i_sb; + int err; + + if (!capable(CAP_SYS_RESOURCE)) + return -EACCES; + + if (sb->s_flags & MS_RDONLY) + return -EROFS; + + if (get_user(n_blocks_count, (__u32 *)arg)) + return -EFAULT; + + err = ext3_group_extend(sb, EXT3_SB(sb)->s_es, n_blocks_count); + journal_lock_updates(EXT3_SB(sb)->s_journal); + journal_flush(EXT3_SB(sb)->s_journal); + journal_unlock_updates(EXT3_SB(sb)->s_journal); + + return err; + } + case EXT3_IOC_GROUP_ADD: { + struct ext3_new_group_data input; + struct super_block *sb = inode->i_sb; + int err; + + if (!capable(CAP_SYS_RESOURCE)) + return -EACCES; + + if (inode->i_sb->s_flags & MS_RDONLY) + return -EROFS; + + if (copy_from_user(&input, (struct ext3_new_group_input *)arg, + sizeof(input))) + return -EFAULT; + + err = ext3_group_add(sb, &input); + journal_lock_updates(EXT3_SB(sb)->s_journal); + journal_flush(EXT3_SB(sb)->s_journal); + journal_unlock_updates(EXT3_SB(sb)->s_journal); + + return err; + } + + default: return -ENOTTY; } --- linux-2.6.9-rc1/fs/ext3/Makefile 2004-08-24 00:01:54.000000000 -0700 +++ 25/fs/ext3/Makefile 2004-09-03 00:56:47.083002944 -0700 @@ -5,7 +5,7 @@ obj-$(CONFIG_EXT3_FS) += ext3.o ext3-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \ - ioctl.o namei.o super.o symlink.o hash.o + ioctl.o namei.o super.o symlink.o hash.o resize.o ext3-$(CONFIG_EXT3_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o ext3-$(CONFIG_EXT3_FS_POSIX_ACL) += acl.o --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/ext3/resize.c 2004-09-03 00:56:47.088002184 -0700 @@ -0,0 +1,951 @@ +/* + * linux/fs/ext3/resize.c + * + * Support for resizing an ext3 filesystem while it is mounted. + * + * Copyright (C) 2001, 2002 Andreas Dilger + * + * This could probably be made into a module, because it is not often in use. + */ + +#include + +#define EXT3FS_DEBUG + +#include +#include +#include + +#include +#include + + +#define outside(b, first, last) ((b) < (first) || (b) >= (last)) +#define inside(b, first, last) ((b) >= (first) && (b) < (last)) + +static int verify_group_input(struct super_block *sb, + struct ext3_new_group_data *input) +{ + struct ext3_sb_info *sbi = EXT3_SB(sb); + struct ext3_super_block *es = sbi->s_es; + unsigned start = le32_to_cpu(es->s_blocks_count); + unsigned end = start + input->blocks_count; + unsigned group = input->group; + unsigned itend = input->inode_table + EXT3_SB(sb)->s_itb_per_group; + unsigned overhead = ext3_bg_has_super(sb, group) ? + (1 + ext3_bg_num_gdb(sb, group) + + le16_to_cpu(es->s_reserved_gdt_blocks)) : 0; + unsigned metaend = start + overhead; + struct buffer_head *bh; + int free_blocks_count; + int err = -EINVAL; + + input->free_blocks_count = free_blocks_count = + input->blocks_count - 2 - overhead - sbi->s_itb_per_group; + + if (test_opt(sb, DEBUG)) + printk("EXT3-fs: adding %s group %u: %u blocks " + "(%d free, %u reserved)\n", + ext3_bg_has_super(sb, input->group) ? "normal" : + "no-super", input->group, input->blocks_count, + free_blocks_count, input->reserved_blocks); + + if (group != sbi->s_groups_count) + ext3_warning(sb, __FUNCTION__, + "Cannot add at group %u (only %lu groups)", + input->group, sbi->s_groups_count); + else if ((start - le32_to_cpu(es->s_first_data_block)) % + EXT3_BLOCKS_PER_GROUP(sb)) + ext3_warning(sb, __FUNCTION__, "Last group not full"); + else if (input->reserved_blocks > input->blocks_count / 5) + ext3_warning(sb, __FUNCTION__, "Reserved blocks too high (%u)", + input->reserved_blocks); + else if (free_blocks_count < 0) + ext3_warning(sb, __FUNCTION__, "Bad blocks count %u", + input->blocks_count); + else if (!(bh = sb_bread(sb, end - 1))) + ext3_warning(sb, __FUNCTION__, "Cannot read last block (%u)", + end - 1); + else if (outside(input->block_bitmap, start, end)) + ext3_warning(sb, __FUNCTION__, + "Block bitmap not in group (block %u)", + input->block_bitmap); + else if (outside(input->inode_bitmap, start, end)) + ext3_warning(sb, __FUNCTION__, + "Inode bitmap not in group (block %u)", + input->inode_bitmap); + else if (outside(input->inode_table, start, end) || + outside(itend - 1, start, end)) + ext3_warning(sb, __FUNCTION__, + "Inode table not in group (blocks %u-%u)", + input->inode_table, itend - 1); + else if (input->inode_bitmap == input->block_bitmap) + ext3_warning(sb, __FUNCTION__, + "Block bitmap same as inode bitmap (%u)", + input->block_bitmap); + else if (inside(input->block_bitmap, input->inode_table, itend)) + ext3_warning(sb, __FUNCTION__, + "Block bitmap (%u) in inode table (%u-%u)", + input->block_bitmap, input->inode_table, itend-1); + else if (inside(input->inode_bitmap, input->inode_table, itend)) + ext3_warning(sb, __FUNCTION__, + "Inode bitmap (%u) in inode table (%u-%u)", + input->inode_bitmap, input->inode_table, itend-1); + else if (inside(input->block_bitmap, start, metaend)) + ext3_warning(sb, __FUNCTION__, + "Block bitmap (%u) in GDT table (%u-%u)", + input->block_bitmap, start, metaend - 1); + else if (inside(input->inode_bitmap, start, metaend)) + ext3_warning(sb, __FUNCTION__, + "Inode bitmap (%u) in GDT table (%u-%u)", + input->inode_bitmap, start, metaend - 1); + else if (inside(input->inode_table, start, metaend) || + inside(itend - 1, start, metaend)) + ext3_warning(sb, __FUNCTION__, + "Inode table (%u-%u) overlaps GDT table (%u-%u)", + input->inode_table, itend - 1, start, metaend - 1); + else { + brelse(bh); + err = 0; + } + + return err; +} + +static struct buffer_head *bclean(handle_t *handle, struct super_block *sb, + unsigned long blk) +{ + struct buffer_head *bh; + int err; + + bh = sb_getblk(sb, blk); + set_buffer_uptodate(bh); + if ((err = ext3_journal_get_write_access(handle, bh))) { + brelse(bh); + bh = ERR_PTR(err); + } else + memset(bh->b_data, 0, sb->s_blocksize); + + return bh; +} + +/* + * To avoid calling the atomic setbit hundreds or thousands of times, we only + * need to use it within a single byte (to ensure we get endianness right). + * We can use memset for the rest of the bitmap as there are no other users. + */ +static void mark_bitmap_end(int start_bit, int end_bit, char *bitmap) +{ + int i; + + if (start_bit >= end_bit) + return; + + ext3_debug("mark end bits +%d through +%d used\n", start_bit, end_bit); + for (i = start_bit; i < ((start_bit + 7) & ~7UL); i++) + ext3_set_bit(i, bitmap); + if (i < end_bit) + memset(bitmap + (i >> 3), 0xff, (end_bit - i) >> 3); +} + +/* + * Set up the block and inode bitmaps, and the inode table for the new group. + * This doesn't need to be part of the main transaction, since we are only + * changing blocks outside the actual filesystem. We still do journaling to + * ensure the recovery is correct in case of a failure just after resize. + * If any part of this fails, we simply abort the resize. + * + * We only pass inode because of the ext3 journal wrappers. + */ +static int setup_new_group_blocks(struct super_block *sb, struct inode *inode, + struct ext3_new_group_data *input) +{ + struct ext3_sb_info *sbi = EXT3_SB(sb); + unsigned long start = input->group * sbi->s_blocks_per_group + + le32_to_cpu(sbi->s_es->s_first_data_block); + int reserved_gdb = ext3_bg_has_super(sb, input->group) ? + le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks) : 0; + unsigned long gdblocks = ext3_bg_num_gdb(sb, input->group); + struct buffer_head *bh; + handle_t *handle; + unsigned long block; + int bit; + int i; + int err = 0, err2; + + handle = ext3_journal_start(inode, reserved_gdb + gdblocks + + 2 + sbi->s_itb_per_group); + if (IS_ERR(handle)) + return PTR_ERR(handle); + + lock_super(sb); + if (input->group != sbi->s_groups_count) { + err = -EBUSY; + goto exit_journal; + } + + if (IS_ERR(bh = bclean(handle, sb, input->block_bitmap))) { + err = PTR_ERR(bh); + goto exit_journal; + } + + if (ext3_bg_has_super(sb, input->group)) { + ext3_debug("mark backup superblock %#04lx (+0)\n", start); + ext3_set_bit(0, bh->b_data); + } + + /* Copy all of the GDT blocks into the backup in this group */ + for (i = 0, bit = 1, block = start + 1; + i < gdblocks; i++, block++, bit++) { + struct buffer_head *gdb; + + ext3_debug("update backup group %#04lx (+%d)\n", block, bit); + + gdb = sb_getblk(sb, block); + set_buffer_uptodate(gdb); + if ((err = ext3_journal_get_write_access(handle, gdb))) { + brelse(gdb); + goto exit_bh; + } + memcpy(gdb->b_data, sbi->s_group_desc[i], bh->b_size); + ext3_journal_dirty_metadata(handle, gdb); + ext3_set_bit(bit, bh->b_data); + brelse(gdb); + } + + /* Zero out all of the reserved backup group descriptor table blocks */ + for (i = 0, bit = gdblocks + 1, block = start + bit; + i < reserved_gdb; i++, block++, bit++) { + struct buffer_head *gdb; + + ext3_debug("clear reserved block %#04lx (+%d)\n", block, bit); + + if (IS_ERR(gdb = bclean(handle, sb, block))) { + err = PTR_ERR(bh); + goto exit_bh; + } + ext3_journal_dirty_metadata(handle, gdb); + ext3_set_bit(bit, bh->b_data); + brelse(gdb); + } + ext3_debug("mark block bitmap %#04x (+%ld)\n", input->block_bitmap, + input->block_bitmap - start); + ext3_set_bit(input->block_bitmap - start, bh->b_data); + ext3_debug("mark inode bitmap %#04x (+%ld)\n", input->inode_bitmap, + input->inode_bitmap - start); + ext3_set_bit(input->inode_bitmap - start, bh->b_data); + + /* Zero out all of the inode table blocks */ + for (i = 0, block = input->inode_table, bit = block - start; + i < sbi->s_itb_per_group; i++, bit++, block++) { + struct buffer_head *it; + + ext3_debug("clear inode block %#04x (+%ld)\n", block, bit); + if (IS_ERR(it = bclean(handle, sb, block))) { + err = PTR_ERR(it); + goto exit_bh; + } + ext3_journal_dirty_metadata(handle, it); + brelse(it); + ext3_set_bit(bit, bh->b_data); + } + mark_bitmap_end(input->blocks_count, EXT3_BLOCKS_PER_GROUP(sb), + bh->b_data); + ext3_journal_dirty_metadata(handle, bh); + brelse(bh); + + /* Mark unused entries in inode bitmap used */ + ext3_debug("clear inode bitmap %#04x (+%ld)\n", + input->inode_bitmap, input->inode_bitmap - start); + if (IS_ERR(bh = bclean(handle, sb, input->inode_bitmap))) { + err = PTR_ERR(bh); + goto exit_journal; + } + + mark_bitmap_end(EXT3_INODES_PER_GROUP(sb), EXT3_BLOCKS_PER_GROUP(sb), + bh->b_data); + ext3_journal_dirty_metadata(handle, bh); +exit_bh: + brelse(bh); + +exit_journal: + unlock_super(sb); + if ((err2 = ext3_journal_stop(handle)) && !err) + err = err2; + + return err; +} + +/* + * Iterate through the groups which hold BACKUP superblock/GDT copies in an + * ext3 filesystem. The counters should be initialized to 1, 5, and 7 before + * calling this for the first time. In a sparse filesystem it will be the + * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ... + * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ... + */ +unsigned ext3_list_backups(struct super_block *sb, unsigned *three, + unsigned *five, unsigned *seven) +{ + unsigned *min = three; + int mult = 3; + unsigned ret; + + if (!EXT3_HAS_RO_COMPAT_FEATURE(sb, + EXT3_FEATURE_RO_COMPAT_SPARSE_SUPER)) { + ret = *min; + *min += 1; + return ret; + } + + if (*five < *min) { + min = five; + mult = 5; + } + if (*seven < *min) { + min = seven; + mult = 7; + } + + ret = *min; + *min *= mult; + + return ret; +} + +/* + * Check that all of the backup GDT blocks are held in the primary GDT block. + * It is assumed that they are stored in group order. Returns the number of + * groups in current filesystem that have BACKUPS, or -ve error code. + */ +static int verify_reserved_gdb(struct super_block *sb, + struct buffer_head *primary) +{ + const unsigned long blk = primary->b_blocknr; + const unsigned long end = EXT3_SB(sb)->s_groups_count; + unsigned three = 1; + unsigned five = 5; + unsigned seven = 7; + unsigned grp; + __u32 *p = (__u32 *)primary->b_data; + int gdbackups = 0; + + while ((grp = ext3_list_backups(sb, &three, &five, &seven)) < end) { + if (le32_to_cpu(*p++) != grp * EXT3_BLOCKS_PER_GROUP(sb) + blk){ + ext3_warning(sb, __FUNCTION__, + "reserved GDT %ld missing grp %d (%ld)\n", + blk, grp, + grp * EXT3_BLOCKS_PER_GROUP(sb) + blk); + return -EINVAL; + } + if (++gdbackups > EXT3_ADDR_PER_BLOCK(sb)) + return -EFBIG; + } + + return gdbackups; +} + +/* + * Called when we need to bring a reserved group descriptor table block into + * use from the resize inode. The primary copy of the new GDT block currently + * is an indirect block (under the double indirect block in the resize inode). + * The new backup GDT blocks will be stored as leaf blocks in this indirect + * block, in group order. Even though we know all the block numbers we need, + * we check to ensure that the resize inode has actually reserved these blocks. + * + * Don't need to update the block bitmaps because the blocks are still in use. + * + * We get all of the error cases out of the way, so that we are sure to not + * fail once we start modifying the data on disk, because JBD has no rollback. + */ +static int add_new_gdb(handle_t *handle, struct inode *inode, + struct ext3_new_group_data *input, + struct buffer_head **primary) +{ + struct super_block *sb = inode->i_sb; + struct ext3_super_block *es = EXT3_SB(sb)->s_es; + unsigned long gdb_num = input->group / EXT3_DESC_PER_BLOCK(sb); + unsigned long gdblock = EXT3_SB(sb)->s_sbh->b_blocknr + 1 + gdb_num; + struct buffer_head **o_group_desc, **n_group_desc; + struct buffer_head *dind; + int gdbackups; + struct ext3_iloc iloc; + __u32 *data; + int err; + + if (test_opt(sb, DEBUG)) + printk("EXT3-fs: ext3_add_new_gdb: adding group block %lu\n", + gdb_num); + + /* + * If we are not using the primary superblock/GDT copy don't resize, + * because the user tools have no way of handling this. Probably a + * bad time to do it anyways. + */ + if (EXT3_SB(sb)->s_sbh->b_blocknr != + le32_to_cpu(EXT3_SB(sb)->s_es->s_first_data_block)) { + ext3_warning(sb, __FUNCTION__, + "won't resize using backup superblock at %llu\n", + (unsigned long long)EXT3_SB(sb)->s_sbh->b_blocknr); + return -EPERM; + } + + *primary = sb_bread(sb, gdblock); + if (!*primary) + return -EIO; + + if ((gdbackups = verify_reserved_gdb(sb, *primary)) < 0) { + err = gdbackups; + goto exit_bh; + } + + data = EXT3_I(inode)->i_data + EXT3_DIND_BLOCK; + dind = sb_bread(sb, le32_to_cpu(*data)); + if (!dind) { + err = -EIO; + goto exit_bh; + } + + data = (__u32 *)dind->b_data; + if (le32_to_cpu(data[gdb_num % EXT3_ADDR_PER_BLOCK(sb)]) != gdblock) { + ext3_warning(sb, __FUNCTION__, + "new group %u GDT block %lu not reserved\n", + input->group, gdblock); + err = -EINVAL; + goto exit_dind; + } + + if ((err = ext3_journal_get_write_access(handle, EXT3_SB(sb)->s_sbh))) + goto exit_dind; + + if ((err = ext3_journal_get_write_access(handle, *primary))) + goto exit_sbh; + + if ((err = ext3_journal_get_write_access(handle, dind))) + goto exit_primary; + + /* ext3_reserve_inode_write() gets a reference on the iloc */ + if ((err = ext3_reserve_inode_write(handle, inode, &iloc))) + goto exit_dindj; + + n_group_desc = (struct buffer_head **)kmalloc((gdb_num + 1) * + sizeof(struct buffer_head *), GFP_KERNEL); + if (!n_group_desc) { + err = -ENOMEM; + ext3_warning (sb, __FUNCTION__, + "not enough memory for %lu groups", gdb_num + 1); + goto exit_inode; + } + + /* + * Finally, we have all of the possible failures behind us... + * + * Remove new GDT block from inode double-indirect block and clear out + * the new GDT block for use (which also "frees" the backup GDT blocks + * from the reserved inode). We don't need to change the bitmaps for + * these blocks, because they are marked as in-use from being in the + * reserved inode, and will become GDT blocks (primary and backup). + */ + data[gdb_num % EXT3_ADDR_PER_BLOCK(sb)] = 0; + ext3_journal_dirty_metadata(handle, dind); + brelse(dind); + inode->i_blocks -= (gdbackups + 1) * sb->s_blocksize >> 9; + ext3_mark_iloc_dirty(handle, inode, &iloc); + memset((*primary)->b_data, 0, sb->s_blocksize); + ext3_journal_dirty_metadata(handle, *primary); + + o_group_desc = EXT3_SB(sb)->s_group_desc; + memcpy(n_group_desc, o_group_desc, + EXT3_SB(sb)->s_gdb_count * sizeof(struct buffer_head *)); + n_group_desc[gdb_num] = *primary; + EXT3_SB(sb)->s_group_desc = n_group_desc; + EXT3_SB(sb)->s_gdb_count++; + kfree(o_group_desc); + + es->s_reserved_gdt_blocks = + cpu_to_le16(le16_to_cpu(es->s_reserved_gdt_blocks) - 1); + ext3_journal_dirty_metadata(handle, EXT3_SB(sb)->s_sbh); + + return 0; + +exit_inode: + //ext3_journal_release_buffer(handle, iloc.bh); + brelse(iloc.bh); +exit_dindj: + //ext3_journal_release_buffer(handle, dind); +exit_primary: + //ext3_journal_release_buffer(handle, *primary); +exit_sbh: + //ext3_journal_release_buffer(handle, *primary); +exit_dind: + brelse(dind); +exit_bh: + brelse(*primary); + + ext3_debug("leaving with error %d\n", err); + return err; +} + +/* + * Called when we are adding a new group which has a backup copy of each of + * the GDT blocks (i.e. sparse group) and there are reserved GDT blocks. + * We need to add these reserved backup GDT blocks to the resize inode, so + * that they are kept for future resizing and not allocated to files. + * + * Each reserved backup GDT block will go into a different indirect block. + * The indirect blocks are actually the primary reserved GDT blocks, + * so we know in advance what their block numbers are. We only get the + * double-indirect block to verify it is pointing to the primary reserved + * GDT blocks so we don't overwrite a data block by accident. The reserved + * backup GDT blocks are stored in their reserved primary GDT block. + */ +static int reserve_backup_gdb(handle_t *handle, struct inode *inode, + struct ext3_new_group_data *input) +{ + struct super_block *sb = inode->i_sb; + int reserved_gdb =le16_to_cpu(EXT3_SB(sb)->s_es->s_reserved_gdt_blocks); + struct buffer_head **primary; + struct buffer_head *dind; + struct ext3_iloc iloc; + unsigned long blk; + __u32 *data, *end; + int gdbackups = 0; + int res, i; + int err; + + primary = kmalloc(reserved_gdb * sizeof(*primary), GFP_KERNEL); + if (!primary) + return -ENOMEM; + + data = EXT3_I(inode)->i_data + EXT3_DIND_BLOCK; + dind = sb_bread(sb, le32_to_cpu(*data)); + if (!dind) { + err = -EIO; + goto exit_free; + } + + blk = EXT3_SB(sb)->s_sbh->b_blocknr + 1 + EXT3_SB(sb)->s_gdb_count; + data = (__u32 *)dind->b_data + EXT3_SB(sb)->s_gdb_count; + end = (__u32 *)dind->b_data + EXT3_ADDR_PER_BLOCK(sb); + + /* Get each reserved primary GDT block and verify it holds backups */ + for (res = 0; res < reserved_gdb; res++, blk++) { + if (le32_to_cpu(*data) != blk) { + ext3_warning(sb, __FUNCTION__, + "reserved block %lu not at offset %ld\n", + blk, (long)(data - (__u32 *)dind->b_data)); + err = -EINVAL; + goto exit_bh; + } + primary[res] = sb_bread(sb, blk); + if (!primary[res]) { + err = -EIO; + goto exit_bh; + } + if ((gdbackups = verify_reserved_gdb(sb, primary[res])) < 0) { + brelse(primary[res]); + err = gdbackups; + goto exit_bh; + } + if (++data >= end) + data = (__u32 *)dind->b_data; + } + + for (i = 0; i < reserved_gdb; i++) { + if ((err = ext3_journal_get_write_access(handle, primary[i]))) { + /* + int j; + for (j = 0; j < i; j++) + ext3_journal_release_buffer(handle, primary[j]); + */ + goto exit_bh; + } + } + + if ((err = ext3_reserve_inode_write(handle, inode, &iloc))) + goto exit_bh; + + /* + * Finally we can add each of the reserved backup GDT blocks from + * the new group to its reserved primary GDT block. + */ + blk = input->group * EXT3_BLOCKS_PER_GROUP(sb); + for (i = 0; i < reserved_gdb; i++) { + int err2; + data = (__u32 *)primary[i]->b_data; + /* printk("reserving backup %lu[%u] = %lu\n", + primary[i]->b_blocknr, gdbackups, + blk + primary[i]->b_blocknr); */ + data[gdbackups] = cpu_to_le32(blk + primary[i]->b_blocknr); + err2 = ext3_journal_dirty_metadata(handle, primary[i]); + if (!err) + err = err2; + } + inode->i_blocks += reserved_gdb * sb->s_blocksize >> 9; + ext3_mark_iloc_dirty(handle, inode, &iloc); + +exit_bh: + while (--res >= 0) + brelse(primary[res]); + brelse(dind); + +exit_free: + kfree(primary); + + return err; +} + +/* + * Update the backup copies of the ext3 metadata. These don't need to be part + * of the main resize transaction, because e2fsck will re-write them if there + * is a problem (basically only OOM will cause a problem). However, we + * _should_ update the backups if possible, in case the primary gets trashed + * for some reason and we need to run e2fsck from a backup superblock. The + * important part is that the new block and inode counts are in the backup + * superblocks, and the location of the new group metadata in the GDT backups. + * + * We do not need lock_super() for this, because these blocks are not + * otherwise touched by the filesystem code when it is mounted. We don't + * need to worry about last changing from sbi->s_groups_count, because the + * worst that can happen is that we do not copy the full number of backups + * at this time. The resize which changed s_groups_count will backup again. + * + * We only pass inode because of the ext3 journal wrappers. + */ +static void update_backups(struct super_block *sb, struct inode *inode, + int blk_off, char *data, int size) +{ + struct ext3_sb_info *sbi = EXT3_SB(sb); + const unsigned long last = sbi->s_groups_count; + const int bpg = EXT3_BLOCKS_PER_GROUP(sb); + unsigned three = 1; + unsigned five = 5; + unsigned seven = 7; + unsigned group; + int rest = sb->s_blocksize - size; + handle_t *handle; + int err = 0, err2; + + handle = ext3_journal_start(inode, EXT3_MAX_TRANS_DATA); + if (IS_ERR(handle)) { + group = 1; + err = PTR_ERR(handle); + goto exit_err; + } + + while ((group = ext3_list_backups(sb, &three, &five, &seven)) < last) { + struct buffer_head *bh; + + /* Out of journal space, and can't get more - abort - so sad */ + if (handle->h_buffer_credits == 0 && + ext3_journal_extend(handle, EXT3_MAX_TRANS_DATA) && + (err = ext3_journal_restart(handle, EXT3_MAX_TRANS_DATA))) + break; + + bh = sb_getblk(sb, group * bpg + blk_off); + set_buffer_uptodate(bh); + ext3_debug(sb, __FUNCTION__, "update metadata backup %#04lx\n", + bh->b_blocknr); + if ((err = ext3_journal_get_write_access(handle, bh))) + break; + memcpy(bh->b_data, data, size); + if (rest) + memset(bh->b_data + size, 0, rest); + ext3_journal_dirty_metadata(handle, bh); + brelse(bh); + } + if ((err2 = ext3_journal_stop(handle)) && !err) + err = err2; + + /* + * Ugh! Need to have e2fsck write the backup copies. It is too + * late to revert the resize, we shouldn't fail just because of + * the backup copies (they are only needed in case of corruption). + * + * However, if we got here we have a journal problem too, so we + * can't really start a transaction to mark the superblock. + * Chicken out and just set the flag on the hope it will be written + * to disk, and if not - we will simply wait until next fsck. + */ +exit_err: + if (err) { + ext3_warning(sb, __FUNCTION__, + "can't update backup for group %d (err %d), " + "forcing fsck on next reboot\n", group, err); + sbi->s_mount_state &= ~EXT3_VALID_FS; + sbi->s_es->s_state &= ~cpu_to_le16(EXT3_VALID_FS); + mark_buffer_dirty(sbi->s_sbh); + } +} + +/* Add group descriptor data to an existing or new group descriptor block. + * Ensure we handle all possible error conditions _before_ we start modifying + * the filesystem, because we cannot abort the transaction and not have it + * write the data to disk. + * + * If we are on a GDT block boundary, we need to get the reserved GDT block. + * Otherwise, we may need to add backup GDT blocks for a sparse group. + * + * We only need to hold the superblock lock while we are actually adding + * in the new group's counts to the superblock. Prior to that we have + * not really "added" the group at all. We re-check that we are still + * adding in the last group in case things have changed since verifying. + */ +int ext3_group_add(struct super_block *sb, struct ext3_new_group_data *input) +{ + struct ext3_sb_info *sbi = EXT3_SB(sb); + struct ext3_super_block *es = sbi->s_es; + int reserved_gdb = ext3_bg_has_super(sb, input->group) ? + le16_to_cpu(es->s_reserved_gdt_blocks) : 0; + struct buffer_head *primary = NULL; + struct ext3_group_desc *gdp; + struct inode *inode = NULL; + struct inode bogus; + handle_t *handle; + int gdb_off, gdb_num; + int err, err2; + + gdb_num = input->group / EXT3_DESC_PER_BLOCK(sb); + gdb_off = input->group % EXT3_DESC_PER_BLOCK(sb); + + if (gdb_off == 0 && !EXT3_HAS_RO_COMPAT_FEATURE(sb, + EXT3_FEATURE_RO_COMPAT_SPARSE_SUPER)) { + ext3_warning(sb, __FUNCTION__, + "Can't resize non-sparse filesystem further\n"); + return -EPERM; + } + + if (reserved_gdb || gdb_off == 0) { + if (!EXT3_HAS_COMPAT_FEATURE(sb, + EXT3_FEATURE_COMPAT_RESIZE_INODE)){ + ext3_warning(sb, __FUNCTION__, + "No reserved GDT blocks, can't resize\n"); + return -EPERM; + } + inode = iget(sb, EXT3_RESIZE_INO); + if (!inode || is_bad_inode(inode)) { + ext3_warning(sb, __FUNCTION__, + "Error opening resize inode\n"); + iput(inode); + return -ENOENT; + } + } else { + /* Used only for ext3 journal wrapper functions to get sb */ + inode = &bogus; + bogus.i_sb = sb; + } + + if ((err = verify_group_input(sb, input))) + goto exit_put; + + if ((err = setup_new_group_blocks(sb, inode, input))) + goto exit_put; + + /* + * We will always be modifying at least the superblock and a GDT + * block. If we are adding a group past the last current GDT block, + * we will also modify the inode and the dindirect block. If we + * are adding a group with superblock/GDT backups we will also + * modify each of the reserved GDT dindirect blocks. + */ + handle = ext3_journal_start(inode, ext3_bg_has_super(sb, input->group) ? + 3 + reserved_gdb : 4); + if (IS_ERR(handle)) { + err = PTR_ERR(handle); + goto exit_put; + } + + lock_super(sb); + if (input->group != EXT3_SB(sb)->s_groups_count) { + ext3_warning(sb, __FUNCTION__, + "multiple resizers run on filesystem!\n"); + goto exit_journal; + } + + if ((err = ext3_journal_get_write_access(handle, sbi->s_sbh))) + goto exit_journal; + + /* + * We will only either add reserved group blocks to a backup group + * or remove reserved blocks for the first group in a new group block. + * Doing both would be mean more complex code, and sane people don't + * use non-sparse filesystems anymore. This is already checked above. + */ + if (gdb_off) { + primary = sbi->s_group_desc[gdb_num]; + if ((err = ext3_journal_get_write_access(handle, primary))) + goto exit_journal; + + if (reserved_gdb && ext3_bg_num_gdb(sb, input->group) && + (err = reserve_backup_gdb(handle, inode, input))) + goto exit_journal; + } else if ((err = add_new_gdb(handle, inode, input, &primary))) + goto exit_journal; + + /* Finally update group descriptor block for new group */ + gdp = (struct ext3_group_desc *)primary->b_data + gdb_off; + + gdp->bg_block_bitmap = cpu_to_le32(input->block_bitmap); + gdp->bg_inode_bitmap = cpu_to_le32(input->inode_bitmap); + gdp->bg_inode_table = cpu_to_le32(input->inode_table); + gdp->bg_free_blocks_count = cpu_to_le16(input->free_blocks_count); + gdp->bg_free_inodes_count = cpu_to_le16(EXT3_INODES_PER_GROUP(sb)); + + EXT3_SB(sb)->s_groups_count++; + ext3_journal_dirty_metadata(handle, primary); + + /* Update superblock with new block counts */ + es->s_blocks_count = cpu_to_le32(le32_to_cpu(es->s_blocks_count) + + input->blocks_count); + es->s_free_blocks_count = + cpu_to_le32(le32_to_cpu(es->s_free_blocks_count) + + input->free_blocks_count); + es->s_r_blocks_count = cpu_to_le32(le32_to_cpu(es->s_r_blocks_count) + + input->reserved_blocks); + es->s_inodes_count = cpu_to_le32(le32_to_cpu(es->s_inodes_count) + + EXT3_INODES_PER_GROUP(sb)); + es->s_free_inodes_count = + cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) + + EXT3_INODES_PER_GROUP(sb)); + ext3_journal_dirty_metadata(handle, EXT3_SB(sb)->s_sbh); + sb->s_dirt = 1; + +exit_journal: + unlock_super(sb); + handle->h_sync = 1; + if ((err2 = ext3_journal_stop(handle)) && !err) + err = err2; + if (!err) { + update_backups(sb, inode, sbi->s_sbh->b_blocknr, (char *)es, + sizeof(struct ext3_super_block)); + update_backups(sb, inode, primary->b_blocknr, primary->b_data, + primary->b_size); + } +exit_put: + if (inode != &bogus) + iput(inode); + return err; +} /* ext3_group_add */ + +/* Extend the filesystem to the new number of blocks specified. This entry + * point is only used to extend the current filesystem to the end of the last + * existing group. It can be accessed via ioctl, or by "remount,resize=" + * for emergencies (because it has no dependencies on reserved blocks). + * + * If we _really_ wanted, we could use default values to call ext3_group_add() + * allow the "remount" trick to work for arbitrary resizing, assuming enough + * GDT blocks are reserved to grow to the desired size. + */ +int ext3_group_extend(struct super_block *sb, struct ext3_super_block *es, + unsigned long n_blocks_count) +{ + unsigned long o_blocks_count; + unsigned long o_groups_count; + unsigned long last; + int add; + struct inode *inode; + struct buffer_head * bh; + handle_t *handle; + int err; + + o_blocks_count = le32_to_cpu(es->s_blocks_count); + o_groups_count = EXT3_SB(sb)->s_groups_count; + + if (test_opt(sb, DEBUG)) + printk("EXT3-fs: extending last group from %lu to %lu blocks\n", + o_blocks_count, n_blocks_count); + + if (n_blocks_count == 0 || n_blocks_count == o_blocks_count) + return 0; + + if (n_blocks_count < o_blocks_count) { + ext3_warning(sb, __FUNCTION__, + "can't shrink FS - resize aborted"); + return -EBUSY; + } + + /* Handle the remaining blocks in the last group only. */ + last = (o_blocks_count - le32_to_cpu(es->s_first_data_block)) % + EXT3_BLOCKS_PER_GROUP(sb); + + if (last == 0) { + ext3_warning(sb, __FUNCTION__, + "need to use ext2online to resize further\n"); + return -EPERM; + } + + add = EXT3_BLOCKS_PER_GROUP(sb) - last; + + if (o_blocks_count + add > n_blocks_count) + add = n_blocks_count - o_blocks_count; + + if (o_blocks_count + add < n_blocks_count) + ext3_warning(sb, __FUNCTION__, + "will only finish group (%lu blocks, %u new)", + o_blocks_count + add, add); + + /* See if the device is actually as big as what was requested */ + bh = sb_bread(sb, o_blocks_count + add -1); + if (!bh) { + ext3_warning(sb, __FUNCTION__, + "can't read last block, resize aborted"); + return -ENOSPC; + } + brelse(bh); + + /* Get a bogus inode to "free" the new blocks in this group. */ + if (!(inode = new_inode(sb))) { + ext3_warning(sb, __FUNCTION__, + "error getting dummy resize inode"); + return -ENOMEM; + } + inode->i_ino = 0; + + EXT3_I(inode)->i_state = EXT3_STATE_RESIZE; + + /* We will update the superblock, one block bitmap, and + * one group descriptor via ext3_free_blocks(). + */ + handle = ext3_journal_start(inode, 3); + if (IS_ERR(handle)) { + err = PTR_ERR(handle); + ext3_warning(sb, __FUNCTION__, "error %d on journal start",err); + goto exit_put; + } + + lock_super(sb); + if (o_blocks_count != le32_to_cpu(es->s_blocks_count)) { + ext3_warning(sb, __FUNCTION__, + "multiple resizers run on filesystem!\n"); + err = -EBUSY; + goto exit_put; + } + + if ((err = ext3_journal_get_write_access(handle, + EXT3_SB(sb)->s_sbh))) { + ext3_warning(sb, __FUNCTION__, + "error %d on journal write access", err); + unlock_super(sb); + ext3_journal_stop(handle); + goto exit_put; + } + es->s_blocks_count = cpu_to_le32(o_blocks_count + add); + ext3_journal_dirty_metadata(handle, EXT3_SB(sb)->s_sbh); + sb->s_dirt = 1; + unlock_super(sb); + ext3_debug("freeing blocks %ld through %ld\n", o_blocks_count, + o_blocks_count + add); + ext3_free_blocks(handle, inode, o_blocks_count, add); + ext3_debug("freed blocks %ld through %ld\n", o_blocks_count, + o_blocks_count + add); + if ((err = ext3_journal_stop(handle))) + goto exit_put; + if (test_opt(sb, DEBUG)) + printk("EXT3-fs: extended group to %u blocks\n", + le32_to_cpu(es->s_blocks_count)); + update_backups(sb, inode, EXT3_SB(sb)->s_sbh->b_blocknr, (char *)es, + sizeof(struct ext3_super_block)); +exit_put: + iput(inode); + + return err; +} /* ext3_group_extend */ --- linux-2.6.9-rc1/fs/ext3/super.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/fs/ext3/super.c 2004-09-03 00:56:47.090001880 -0700 @@ -477,7 +477,7 @@ static int init_inodecache(void) { ext3_inode_cachep = kmem_cache_create("ext3_inode_cache", sizeof(struct ext3_inode_info), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (ext3_inode_cachep == NULL) return -ENOMEM; @@ -490,10 +490,9 @@ static void destroy_inodecache(void) printk(KERN_INFO "ext3_inode_cache: not all structures were freed\n"); } -#ifdef CONFIG_EXT3_FS_POSIX_ACL - static void ext3_clear_inode(struct inode *inode) { +#ifdef CONFIG_EXT3_FS_POSIX_ACL if (EXT3_I(inode)->i_acl && EXT3_I(inode)->i_acl != EXT3_ACL_NOT_CACHED) { posix_acl_release(EXT3_I(inode)->i_acl); @@ -504,11 +503,10 @@ static void ext3_clear_inode(struct inod posix_acl_release(EXT3_I(inode)->i_default_acl); EXT3_I(inode)->i_default_acl = EXT3_ACL_NOT_CACHED; } -} - -#else -# define ext3_clear_inode NULL #endif + if (!is_bad_inode(inode)) + ext3_discard_reservation(inode); +} #ifdef CONFIG_QUOTA @@ -558,7 +556,6 @@ static struct super_operations ext3_sops .read_inode = ext3_read_inode, .write_inode = ext3_write_inode, .dirty_inode = ext3_dirty_inode, - .put_inode = ext3_put_inode, .delete_inode = ext3_delete_inode, .put_super = ext3_put_super, .write_super = ext3_write_super, @@ -579,12 +576,13 @@ enum { Opt_bsd_df, Opt_minix_df, Opt_grpid, Opt_nogrpid, Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic, Opt_err_ro, Opt_nouid32, Opt_check, Opt_nocheck, Opt_debug, Opt_oldalloc, Opt_orlov, - Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl, Opt_noload, + Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl, + Opt_reservation, Opt_noreservation, Opt_noload, Opt_commit, Opt_journal_update, Opt_journal_inum, Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback, Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota, Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, - Opt_ignore, Opt_barrier, Opt_err, + Opt_ignore, Opt_barrier, Opt_err, Opt_resize, }; static match_table_t tokens = { @@ -611,6 +609,8 @@ static match_table_t tokens = { {Opt_nouser_xattr, "nouser_xattr"}, {Opt_acl, "acl"}, {Opt_noacl, "noacl"}, + {Opt_reservation, "reservation"}, + {Opt_noreservation, "noreservation"}, {Opt_noload, "noload"}, {Opt_commit, "commit=%u"}, {Opt_journal_update, "journal=update"}, @@ -630,7 +630,8 @@ static match_table_t tokens = { {Opt_ignore, "quota"}, {Opt_ignore, "usrquota"}, {Opt_barrier, "barrier=%u"}, - {Opt_err, NULL} + {Opt_err, NULL}, + {Opt_resize, "resize"}, }; static unsigned long get_sb_block(void **data) @@ -654,7 +655,7 @@ static unsigned long get_sb_block(void * } static int parse_options (char * options, struct super_block *sb, - unsigned long * inum, int is_remount) + unsigned long * inum, unsigned long *n_blocks_count, int is_remount) { struct ext3_sb_info *sbi = EXT3_SB(sb); char * p; @@ -765,6 +766,12 @@ static int parse_options (char * options printk("EXT3 (no)acl options not supported\n"); break; #endif + case Opt_reservation: + set_opt(sbi->s_mount_opt, RESERVATION); + break; + case Opt_noreservation: + clear_opt(sbi->s_mount_opt, RESERVATION); + break; case Opt_journal_update: /* @@@ FIXME */ /* Eventually we will want to be able to create @@ -905,6 +912,15 @@ clear_qf_name: break; case Opt_ignore: break; + case Opt_resize: + if (!n_blocks_count) { + printk("EXT3-fs: resize option only available " + "for remount\n"); + return 0; + } + match_int(&args[0], &option); + *n_blocks_count = option; + break; default: printk (KERN_ERR "EXT3-fs: Unrecognized mount option \"%s\" " @@ -1294,7 +1310,9 @@ static int ext3_fill_super (struct super sbi->s_resuid = le16_to_cpu(es->s_def_resuid); sbi->s_resgid = le16_to_cpu(es->s_def_resgid); - if (!parse_options ((char *) data, sb, &journal_inum, 0)) + set_opt(sbi->s_mount_opt, RESERVATION); + + if (!parse_options ((char *) data, sb, &journal_inum, NULL, 0)) goto failed_mount; sb->s_flags |= MS_ONE_SECOND; @@ -1468,6 +1486,14 @@ static int ext3_fill_super (struct super sbi->s_gdb_count = db_count; get_random_bytes(&sbi->s_next_generation, sizeof(u32)); spin_lock_init(&sbi->s_next_gen_lock); + /* per fileystem reservation list head & lock */ + spin_lock_init(&sbi->s_rsv_window_lock); + INIT_LIST_HEAD(&sbi->s_rsv_window_head.rsv_list); + sbi->s_rsv_window_head.rsv_start = 0; + sbi->s_rsv_window_head.rsv_end = 0; + sbi->s_rsv_window_head.rsv_alloc_hit = 0; + atomic_set(&sbi->s_rsv_window_head.rsv_goal_size, 0); + /* * set up enough so that it can read an inode */ @@ -2026,11 +2052,12 @@ int ext3_remount (struct super_block * s struct ext3_super_block * es; struct ext3_sb_info *sbi = EXT3_SB(sb); unsigned long tmp; + unsigned long n_blocks_count = 0; /* * Allow the "check" option to be passed as a remount option. */ - if (!parse_options(data, sb, &tmp, 1)) + if (!parse_options(data, sb, &tmp, &n_blocks_count, 1)) return -EINVAL; if (sbi->s_mount_opt & EXT3_MOUNT_ABORT) @@ -2043,7 +2070,8 @@ int ext3_remount (struct super_block * s ext3_init_journal_params(sb, sbi->s_journal); - if ((*flags & MS_RDONLY) != (sb->s_flags & MS_RDONLY)) { + if ((*flags & MS_RDONLY) != (sb->s_flags & MS_RDONLY) || + n_blocks_count > le32_to_cpu(es->s_blocks_count)) { if (sbi->s_mount_opt & EXT3_MOUNT_ABORT) return -EROFS; @@ -2082,6 +2110,8 @@ int ext3_remount (struct super_block * s */ ext3_clear_journal_err(sb, es); sbi->s_mount_state = le16_to_cpu(es->s_state); + if ((ret = ext3_group_extend(sb, es, n_blocks_count))) + return ret; if (!ext3_setup_super (sb, es, 0)) sb->s_flags &= ~MS_RDONLY; } --- linux-2.6.9-rc1/fs/ext3/xattr.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/fs/ext3/xattr.c 2004-09-03 00:56:43.616529928 -0700 @@ -786,8 +786,7 @@ ext3_xattr_set_handle2(handle_t *handle, EXT3_SB(sb)->s_es->s_first_data_block) + EXT3_I(inode)->i_block_group * EXT3_BLOCKS_PER_GROUP(sb); - int block = ext3_new_block(handle, inode, goal, - NULL, NULL, &error); + int block = ext3_new_block(handle, inode, goal, &error); if (error) goto cleanup; ea_idebug(inode, "creating block %d", block); --- linux-2.6.9-rc1/fs/fat/dir.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/fs/fat/dir.c 2004-09-03 00:57:15.783639784 -0700 @@ -93,14 +93,6 @@ static void dump_de(struct msdos_dir_ent } #endif -static inline unsigned char -fat_tolower(struct nls_table *t, unsigned char c) -{ - unsigned char nc = t->charset2lower[c]; - - return nc ? nc : c; -} - static inline int fat_short2uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *uni) { @@ -140,17 +132,6 @@ fat_short2lower_uni(struct nls_table *t, return charlen; } -static int -fat_strnicmp(struct nls_table *t, const unsigned char *s1, - const unsigned char *s2, int len) -{ - while(len--) - if (fat_tolower(t, *s1++) != fat_tolower(t, *s2++)) - return 1; - - return 0; -} - static inline int fat_shortname2uni(struct nls_table *nls, unsigned char *buf, int buf_size, wchar_t *uni_buf, unsigned short opt, int lower) @@ -311,7 +292,7 @@ parse_long: :uni16_to_x8(bufname, bufuname, uni_xlate, nls_io); if (xlate_len == name_len) if ((!anycase && !memcmp(name, bufname, xlate_len)) || - (anycase && !fat_strnicmp(nls_io, name, bufname, + (anycase && !nls_strnicmp(nls_io, name, bufname, xlate_len))) goto Found; @@ -322,7 +303,7 @@ parse_long: if (xlate_len != name_len) continue; if ((!anycase && !memcmp(name, bufname, xlate_len)) || - (anycase && !fat_strnicmp(nls_io, name, bufname, + (anycase && !nls_strnicmp(nls_io, name, bufname, xlate_len))) goto Found; } --- linux-2.6.9-rc1/fs/fat/file.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/fs/fat/file.c 2004-09-02 20:51:52.000000000 -0700 @@ -90,12 +90,6 @@ void fat_truncate(struct inode *inode) const unsigned int cluster_size = sbi->cluster_size; int nr_clusters; - /* Why no return value? Surely the disk could fail... */ - if (IS_RDONLY (inode)) - return /* -EPERM */; - if (IS_IMMUTABLE(inode)) - return /* -EPERM */; - /* * This protects against truncating a file bigger than it was then * trying to write into the hole. --- linux-2.6.9-rc1/fs/fat/inode.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/fs/fat/inode.c 2004-09-02 20:51:52.000000000 -0700 @@ -744,7 +744,7 @@ int __init fat_init_inodecache(void) { fat_inode_cachep = kmem_cache_create("fat_inode_cache", sizeof(struct msdos_inode_info), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (fat_inode_cachep == NULL) return -ENOMEM; --- linux-2.6.9-rc1/fs/fcntl.c 2004-08-24 00:03:13.000000000 -0700 +++ 25/fs/fcntl.c 2004-09-02 20:51:52.000000000 -0700 @@ -497,11 +497,9 @@ void send_sigio(struct fown_struct *fown send_sigio_to_task(p, fown, fd, band); } } else { - struct list_head *l; - struct pid *pidptr; - for_each_task_pid(-pid, PIDTYPE_PGID, p, l, pidptr) { + do_each_task_pid(-pid, PIDTYPE_PGID, p) { send_sigio_to_task(p, fown, fd, band); - } + } while_each_task_pid(-pid, PIDTYPE_PGID, p); } read_unlock(&tasklist_lock); out_unlock_fown: @@ -534,11 +532,9 @@ int send_sigurg(struct fown_struct *fown send_sigurg_to_task(p, fown); } } else { - struct list_head *l; - struct pid *pidptr; - for_each_task_pid(-pid, PIDTYPE_PGID, p, l, pidptr) { + do_each_task_pid(-pid, PIDTYPE_PGID, p) { send_sigurg_to_task(p, fown); - } + } while_each_task_pid(-pid, PIDTYPE_PGID, p); } read_unlock(&tasklist_lock); out_unlock_fown: --- linux-2.6.9-rc1/fs/file_table.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/fs/file_table.c 2004-09-02 20:51:52.000000000 -0700 @@ -123,6 +123,7 @@ void fastcall __fput(struct file *file) struct vfsmount *mnt = file->f_vfsmnt; struct inode *inode = dentry->d_inode; + might_sleep(); /* * The function eventpoll_release() should be the first called * in the file cleanup chain. --- linux-2.6.9-rc1/fs/freevxfs/vxfs_extern.h 2004-08-24 00:03:38.000000000 -0700 +++ 25/fs/freevxfs/vxfs_extern.h 2004-09-03 00:57:16.313559224 -0700 @@ -59,7 +59,7 @@ extern void vxfs_put_fake_inode(struct extern struct vxfs_inode_info * vxfs_blkiget(struct super_block *, u_long, ino_t); extern struct vxfs_inode_info * vxfs_stiget(struct super_block *, ino_t); extern void vxfs_read_inode(struct inode *); -extern void vxfs_put_inode(struct inode *); +extern void vxfs_clear_inode(struct inode *); /* vxfs_lookup.c */ extern struct inode_operations vxfs_dir_inode_ops; --- linux-2.6.9-rc1/fs/freevxfs/vxfs_inode.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/fs/freevxfs/vxfs_inode.c 2004-09-03 00:57:16.314559072 -0700 @@ -337,16 +337,15 @@ vxfs_read_inode(struct inode *ip) } /** - * vxfs_put_inode - remove inode from main memory + * vxfs_clear_inode - remove inode from main memory * @ip: inode to discard. * * Description: - * vxfs_put_inode() is called on each iput. If we are the last - * link in memory, free the fspriv inode area. + * vxfs_clear_inode() is called on the final iput and frees the private + * inode area. */ void -vxfs_put_inode(struct inode *ip) +vxfs_clear_inode(struct inode *ip) { - if (atomic_read(&ip->i_count) == 1) - kmem_cache_free(vxfs_inode_cachep, ip->u.generic_ip); + kmem_cache_free(vxfs_inode_cachep, ip->u.generic_ip); } --- linux-2.6.9-rc1/fs/freevxfs/vxfs_super.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/fs/freevxfs/vxfs_super.c 2004-09-03 00:57:16.314559072 -0700 @@ -60,7 +60,7 @@ static int vxfs_remount(struct super_bl static struct super_operations vxfs_super_ops = { .read_inode = vxfs_read_inode, - .put_inode = vxfs_put_inode, + .clear_inode = vxfs_clear_inode, .put_super = vxfs_put_super, .statfs = vxfs_statfs, .remount_fs = vxfs_remount, --- linux-2.6.9-rc1/fs/fs-writeback.c 2004-08-24 00:03:49.000000000 -0700 +++ 25/fs/fs-writeback.c 2004-09-03 00:57:14.698804704 -0700 @@ -240,6 +240,8 @@ static int __writeback_single_inode(struct inode *inode, struct writeback_control *wbc) { + wait_queue_head_t *wqh; + if ((wbc->sync_mode != WB_SYNC_ALL) && (inode->i_state & I_LOCK)) { list_move(&inode->i_list, &inode->i_sb->s_dirty); return 0; @@ -248,12 +250,18 @@ __writeback_single_inode(struct inode *i /* * It's a data-integrity sync. We must wait. */ - while (inode->i_state & I_LOCK) { - __iget(inode); - spin_unlock(&inode_lock); - __wait_on_inode(inode); - iput(inode); - spin_lock(&inode_lock); + if (inode->i_state & I_LOCK) { + DEFINE_WAIT_BIT(wq, &inode->i_state, __I_LOCK); + + wqh = bit_waitqueue(&inode->i_state, __I_LOCK); + do { + __iget(inode); + spin_unlock(&inode_lock); + __wait_on_bit(wqh, &wq, inode_wait, + TASK_UNINTERRUPTIBLE); + iput(inode); + spin_lock(&inode_lock); + } while (inode->i_state & I_LOCK); } return __sync_single_inode(inode, wbc); } @@ -289,7 +297,7 @@ __writeback_single_inode(struct inode *i * throttled threads: we don't want them all piling up on __wait_on_inode. */ static void -sync_sb_inodes(struct super_block *sb, struct writeback_control *wbc) +generic_sync_sb_inodes(struct super_block *sb, struct writeback_control *wbc) { const unsigned long start = jiffies; /* livelock avoidance */ @@ -374,6 +382,15 @@ sync_sb_inodes(struct super_block *sb, s return; /* Leave any unwritten inodes on s_io */ } +static void +sync_sb_inodes(struct super_block *sb, struct writeback_control *wbc) +{ + if (sb->s_op->sync_inodes) + sb->s_op->sync_inodes(sb, wbc); + else + generic_sync_sb_inodes(sb, wbc); +} + /* * Start writeback of dirty pagecache data against all unlocked inodes. * @@ -398,6 +415,7 @@ writeback_inodes(struct writeback_contro { struct super_block *sb; + might_sleep(); spin_lock(&inode_lock); spin_lock(&sb_lock); restart: @@ -553,6 +571,7 @@ void write_inode_now(struct inode *inode if (inode->i_mapping->backing_dev_info->memory_backed) return; + might_sleep(); spin_lock(&inode_lock); __writeback_single_inode(inode, &wbc); spin_unlock(&inode_lock); --- linux-2.6.9-rc1/fs/hfs/inode.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/fs/hfs/inode.c 2004-09-02 20:51:52.000000000 -0700 @@ -609,6 +609,7 @@ struct file_operations hfs_file_operatio .read = generic_file_read, .write = generic_file_write, .mmap = generic_file_mmap, + .sendfile = generic_file_sendfile, .fsync = file_fsync, .open = hfs_file_open, .release = hfs_file_release, --- linux-2.6.9-rc1/fs/hfsplus/inode.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/fs/hfsplus/inode.c 2004-09-02 20:51:52.000000000 -0700 @@ -308,6 +308,7 @@ struct file_operations hfsplus_file_oper .read = generic_file_read, .write = generic_file_write, .mmap = generic_file_mmap, + .sendfile = generic_file_sendfile, .fsync = file_fsync, .open = hfsplus_file_open, .release = hfsplus_file_release, --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/hostfs/hostfs.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,79 @@ +#ifndef __UM_FS_HOSTFS +#define __UM_FS_HOSTFS + +#include "os.h" + +/* These are exactly the same definitions as in fs.h, but the names are + * changed so that this file can be included in both kernel and user files. + */ + +#define HOSTFS_ATTR_MODE 1 +#define HOSTFS_ATTR_UID 2 +#define HOSTFS_ATTR_GID 4 +#define HOSTFS_ATTR_SIZE 8 +#define HOSTFS_ATTR_ATIME 16 +#define HOSTFS_ATTR_MTIME 32 +#define HOSTFS_ATTR_CTIME 64 +#define HOSTFS_ATTR_ATIME_SET 128 +#define HOSTFS_ATTR_MTIME_SET 256 +#define HOSTFS_ATTR_FORCE 512 /* Not a change, but a change it */ +#define HOSTFS_ATTR_ATTR_FLAG 1024 + +struct hostfs_iattr { + unsigned int ia_valid; + mode_t ia_mode; + uid_t ia_uid; + gid_t ia_gid; + loff_t ia_size; + struct timespec ia_atime; + struct timespec ia_mtime; + struct timespec ia_ctime; + unsigned int ia_attr_flags; +}; + +extern int stat_file(const char *path, unsigned long long *inode_out, + int *mode_out, int *nlink_out, int *uid_out, int *gid_out, + unsigned long long *size_out, struct timespec *atime_out, + struct timespec *mtime_out, struct timespec *ctime_out, + int *blksize_out, unsigned long long *blocks_out); +extern int access_file(char *path, int r, int w, int x); +extern int open_file(char *path, int r, int w, int append); +extern int file_type(const char *path, int *rdev); +extern void *open_dir(char *path, int *err_out); +extern char *read_dir(void *stream, unsigned long long *pos, + unsigned long long *ino_out, int *len_out); +extern void close_file(void *stream); +extern void close_dir(void *stream); +extern int read_file(int fd, unsigned long long *offset, char *buf, int len); +extern int write_file(int fd, unsigned long long *offset, const char *buf, + int len); +extern int lseek_file(int fd, long long offset, int whence); +extern int file_create(char *name, int ur, int uw, int ux, int gr, + int gw, int gx, int or, int ow, int ox); +extern int set_attr(const char *file, struct hostfs_iattr *attrs); +extern int make_symlink(const char *from, const char *to); +extern int unlink_file(const char *file); +extern int do_mkdir(const char *file, int mode); +extern int do_rmdir(const char *file); +extern int do_mknod(const char *file, int mode, int dev); +extern int link_file(const char *from, const char *to); +extern int do_readlink(char *file, char *buf, int size); +extern int rename_file(char *from, char *to); +extern int do_statfs(char *root, long *bsize_out, long long *blocks_out, + long long *bfree_out, long long *bavail_out, + long long *files_out, long long *ffree_out, + void *fsid_out, int fsid_size, long *namelen_out, + long *spare_out); + +#endif + +/* + * 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: + */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/hostfs/hostfs_kern.c 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,1024 @@ +/* + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + * + * Ported the filesystem routines to 2.5. + * 2003-02-10 Petr Baudis + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hostfs.h" +#include "kern_util.h" +#include "kern.h" +#include "user_util.h" +#include "2_5compat.h" +#include "init.h" + +struct hostfs_inode_info { + char *host_filename; + int fd; + int mode; + struct inode vfs_inode; +}; + +static inline struct hostfs_inode_info *HOSTFS_I(struct inode *inode) +{ + return(list_entry(inode, struct hostfs_inode_info, vfs_inode)); +} + +#define FILE_HOSTFS_I(file) HOSTFS_I((file)->f_dentry->d_inode) + +int hostfs_d_delete(struct dentry *dentry) +{ + return(1); +} + +struct dentry_operations hostfs_dentry_ops = { + .d_delete = hostfs_d_delete, +}; + +/* Changed in hostfs_args before the kernel starts running */ +static char *root_ino = "/"; +static int append = 0; + +#define HOSTFS_SUPER_MAGIC 0x00c0ffee + +static struct inode_operations hostfs_iops; +static struct inode_operations hostfs_dir_iops; +static struct address_space_operations hostfs_link_aops; + +#ifndef MODULE +static int __init hostfs_args(char *options, int *add) +{ + char *ptr; + + ptr = strchr(options, ','); + if(ptr != NULL) + *ptr++ = '\0'; + if(*options != '\0') + root_ino = options; + + options = ptr; + while(options){ + ptr = strchr(options, ','); + if(ptr != NULL) + *ptr++ = '\0'; + if(*options != '\0'){ + if(!strcmp(options, "append")) + append = 1; + else printf("hostfs_args - unsupported option - %s\n", + options); + } + options = ptr; + } + return(0); +} + +__uml_setup("hostfs=", hostfs_args, +"hostfs=,,...\n" +" This is used to set hostfs parameters. The root directory argument\n" +" is used to confine all hostfs mounts to within the specified directory\n" +" tree on the host. If this isn't specified, then a user inside UML can\n" +" mount anything on the host that's accessible to the user that's running\n" +" it.\n" +" The only flag currently supported is 'append', which specifies that all\n" +" files opened by hostfs will be opened in append mode.\n\n" +); +#endif + +static char *dentry_name(struct dentry *dentry, int extra) +{ + struct dentry *parent; + char *root, *name; + int len; + + len = 0; + parent = dentry; + while(parent->d_parent != parent){ + len += parent->d_name.len + 1; + parent = parent->d_parent; + } + + root = HOSTFS_I(parent->d_inode)->host_filename; + len += strlen(root); + name = kmalloc(len + extra + 1, GFP_KERNEL); + if(name == NULL) return(NULL); + + name[len] = '\0'; + parent = dentry; + while(parent->d_parent != parent){ + len -= parent->d_name.len + 1; + name[len] = '/'; + strncpy(&name[len + 1], parent->d_name.name, + parent->d_name.len); + parent = parent->d_parent; + } + strncpy(name, root, strlen(root)); + return(name); +} + +static char *inode_name(struct inode *ino, int extra) +{ + struct dentry *dentry; + + dentry = list_entry(ino->i_dentry.next, struct dentry, d_alias); + return(dentry_name(dentry, extra)); +} + +static int read_name(struct inode *ino, char *name) +{ + /* The non-int inode fields are copied into ints by stat_file and + * then copied into the inode because passing the actual pointers + * in and having them treated as int * breaks on big-endian machines + */ + int err; + int i_mode, i_nlink, i_blksize; + unsigned long long i_size; + unsigned long long i_ino; + unsigned long long i_blocks; + + err = stat_file(name, &i_ino, &i_mode, &i_nlink, &ino->i_uid, + &ino->i_gid, &i_size, &ino->i_atime, &ino->i_mtime, + &ino->i_ctime, &i_blksize, &i_blocks); + if(err) + return(err); + + ino->i_ino = i_ino; + ino->i_mode = i_mode; + ino->i_nlink = i_nlink; + ino->i_size = i_size; + ino->i_blksize = i_blksize; + ino->i_blocks = i_blocks; + if((ino->i_sb->s_dev == ROOT_DEV) && (ino->i_uid == getuid())) + ino->i_uid = 0; + return(0); +} + +static char *follow_link(char *link) +{ + int len, n; + char *name, *resolved, *end; + + len = 64; + while(1){ + n = -ENOMEM; + name = kmalloc(len, GFP_KERNEL); + if(name == NULL) + goto out; + + n = do_readlink(link, name, len); + if(n < len) + break; + len *= 2; + kfree(name); + } + if(n < 0) + goto out_free; + + if(*name == '/') + return(name); + + end = strrchr(link, '/'); + if(end == NULL) + return(name); + + *(end + 1) = '\0'; + len = strlen(link) + strlen(name) + 1; + + resolved = kmalloc(len, GFP_KERNEL); + if(resolved == NULL){ + n = -ENOMEM; + goto out_free; + } + + sprintf(resolved, "%s%s", link, name); + kfree(name); + kfree(link); + return(resolved); + + out_free: + kfree(name); + out: + return(ERR_PTR(n)); +} + +static int read_inode(struct inode *ino) +{ + char *name; + int err = 0; + + /* Unfortunately, we are called from iget() when we don't have a dentry + * allocated yet. + */ + if(list_empty(&ino->i_dentry)) + goto out; + + err = -ENOMEM; + name = inode_name(ino, 0); + if(name == NULL) + goto out; + + if(file_type(name, NULL) == OS_TYPE_SYMLINK){ + name = follow_link(name); + if(IS_ERR(name)){ + err = PTR_ERR(name); + goto out; + } + } + + err = read_name(ino, name); + kfree(name); + out: + return(err); +} + +int hostfs_statfs(struct super_block *sb, struct kstatfs *sf) +{ + /* do_statfs uses struct statfs64 internally, but the linux kernel + * struct statfs still has 32-bit versions for most of these fields, + * so we convert them here + */ + int err; + long long f_blocks; + long long f_bfree; + long long f_bavail; + long long f_files; + long long f_ffree; + + err = do_statfs(HOSTFS_I(sb->s_root->d_inode)->host_filename, + &sf->f_bsize, &f_blocks, &f_bfree, &f_bavail, &f_files, + &f_ffree, &sf->f_fsid, sizeof(sf->f_fsid), + &sf->f_namelen, sf->f_spare); + if(err) return(err); + sf->f_blocks = f_blocks; + sf->f_bfree = f_bfree; + sf->f_bavail = f_bavail; + sf->f_files = f_files; + sf->f_ffree = f_ffree; + sf->f_type = HOSTFS_SUPER_MAGIC; + return(0); +} + +static struct inode *hostfs_alloc_inode(struct super_block *sb) +{ + struct hostfs_inode_info *hi; + + hi = kmalloc(sizeof(*hi), GFP_KERNEL); + if(hi == NULL) + return(NULL); + + *hi = ((struct hostfs_inode_info) { .host_filename = NULL, + .fd = -1, + .mode = 0 }); + inode_init_once(&hi->vfs_inode); + return(&hi->vfs_inode); +} + +static void hostfs_delete_inode(struct inode *inode) +{ + if(HOSTFS_I(inode)->fd != -1) { + close_file(&HOSTFS_I(inode)->fd); + printk("Closing host fd in .delete_inode\n"); + HOSTFS_I(inode)->fd = -1; + } + clear_inode(inode); +} + +static void hostfs_destroy_inode(struct inode *inode) +{ + if(HOSTFS_I(inode)->host_filename) + kfree(HOSTFS_I(inode)->host_filename); + + if(HOSTFS_I(inode)->fd != -1) { + close_file(&HOSTFS_I(inode)->fd); + printk("Closing host fd in .destroy_inode\n"); + } + + kfree(HOSTFS_I(inode)); +} + +static void hostfs_read_inode(struct inode *inode) +{ + read_inode(inode); +} + +static struct super_operations hostfs_sbops = { + .alloc_inode = hostfs_alloc_inode, + .drop_inode = generic_delete_inode, + .delete_inode = hostfs_delete_inode, + .destroy_inode = hostfs_destroy_inode, + .read_inode = hostfs_read_inode, + .statfs = hostfs_statfs, +}; + +int hostfs_readdir(struct file *file, void *ent, filldir_t filldir) +{ + void *dir; + char *name; + unsigned long long next, ino; + int error, len; + + name = dentry_name(file->f_dentry, 0); + if(name == NULL) return(-ENOMEM); + dir = open_dir(name, &error); + kfree(name); + if(dir == NULL) return(-error); + next = file->f_pos; + while((name = read_dir(dir, &next, &ino, &len)) != NULL){ + error = (*filldir)(ent, name, len, file->f_pos, + ino, DT_UNKNOWN); + if(error) break; + file->f_pos = next; + } + close_dir(dir); + return(0); +} + +int hostfs_file_open(struct inode *ino, struct file *file) +{ + char *name; + int mode = 0, r = 0, w = 0, fd; + + mode = file->f_mode & (FMODE_READ | FMODE_WRITE); + if((mode & HOSTFS_I(ino)->mode) == mode) + return(0); + + /* The file may already have been opened, but with the wrong access, + * so this resets things and reopens the file with the new access. + */ + if(HOSTFS_I(ino)->fd != -1){ + close_file(&HOSTFS_I(ino)->fd); + HOSTFS_I(ino)->fd = -1; + } + + HOSTFS_I(ino)->mode |= mode; + if(HOSTFS_I(ino)->mode & FMODE_READ) + r = 1; + if(HOSTFS_I(ino)->mode & FMODE_WRITE) + w = 1; + if(w) + r = 1; + + name = dentry_name(file->f_dentry, 0); + if(name == NULL) + return(-ENOMEM); + + fd = open_file(name, r, w, append); + kfree(name); + if(fd < 0) return(fd); + FILE_HOSTFS_I(file)->fd = fd; + + return(0); +} + +int hostfs_fsync(struct file *file, struct dentry *dentry, int datasync) +{ + return(0); +} + +static struct file_operations hostfs_file_fops = { + .llseek = generic_file_llseek, + .read = generic_file_read, + .write = generic_file_write, + .mmap = generic_file_mmap, + .open = hostfs_file_open, + .release = NULL, + .fsync = hostfs_fsync, +}; + +static struct file_operations hostfs_dir_fops = { + .readdir = hostfs_readdir, + .read = generic_read_dir, +}; + +int hostfs_writepage(struct page *page, struct writeback_control *wbc) +{ + struct address_space *mapping = page->mapping; + struct inode *inode = mapping->host; + char *buffer; + unsigned long long base; + int count = PAGE_CACHE_SIZE; + int end_index = inode->i_size >> PAGE_CACHE_SHIFT; + int err; + + if (page->index >= end_index) + count = inode->i_size & (PAGE_CACHE_SIZE-1); + + buffer = kmap(page); + base = ((unsigned long long) page->index) << PAGE_CACHE_SHIFT; + + err = write_file(HOSTFS_I(inode)->fd, &base, buffer, count); + if(err != count){ + ClearPageUptodate(page); + goto out; + } + + if (base > inode->i_size) + inode->i_size = base; + + if (PageError(page)) + ClearPageError(page); + err = 0; + + out: + kunmap(page); + + unlock_page(page); + return err; +} + +int hostfs_readpage(struct file *file, struct page *page) +{ + char *buffer; + long long start; + int err = 0; + + start = (long long) page->index << PAGE_CACHE_SHIFT; + buffer = kmap(page); + err = read_file(FILE_HOSTFS_I(file)->fd, &start, buffer, + PAGE_CACHE_SIZE); + if(err < 0) goto out; + + memset(&buffer[err], 0, PAGE_CACHE_SIZE - err); + + flush_dcache_page(page); + SetPageUptodate(page); + if (PageError(page)) ClearPageError(page); + err = 0; + out: + kunmap(page); + unlock_page(page); + return(err); +} + +int hostfs_prepare_write(struct file *file, struct page *page, + unsigned int from, unsigned int to) +{ + char *buffer; + long long start, tmp; + int err; + + start = (long long) page->index << PAGE_CACHE_SHIFT; + buffer = kmap(page); + if(from != 0){ + tmp = start; + err = read_file(FILE_HOSTFS_I(file)->fd, &tmp, buffer, + from); + if(err < 0) goto out; + } + if(to != PAGE_CACHE_SIZE){ + start += to; + err = read_file(FILE_HOSTFS_I(file)->fd, &start, buffer + to, + PAGE_CACHE_SIZE - to); + if(err < 0) goto out; + } + err = 0; + out: + kunmap(page); + return(err); +} + +int hostfs_commit_write(struct file *file, struct page *page, unsigned from, + unsigned to) +{ + struct address_space *mapping = page->mapping; + struct inode *inode = mapping->host; + char *buffer; + long long start; + int err = 0; + + start = (long long) (page->index << PAGE_CACHE_SHIFT) + from; + buffer = kmap(page); + err = write_file(FILE_HOSTFS_I(file)->fd, &start, buffer + from, + to - from); + if(err > 0) err = 0; + if(!err && (start > inode->i_size)) + inode->i_size = start; + + kunmap(page); + return(err); +} + +static struct address_space_operations hostfs_aops = { + .writepage = hostfs_writepage, + .readpage = hostfs_readpage, +/* .set_page_dirty = __set_page_dirty_nobuffers, */ + .prepare_write = hostfs_prepare_write, + .commit_write = hostfs_commit_write +}; + +static int init_inode(struct inode *inode, struct dentry *dentry) +{ + char *name; + int type, err = -ENOMEM, rdev; + + if(dentry){ + name = dentry_name(dentry, 0); + if(name == NULL) + goto out; + type = file_type(name, &rdev); + kfree(name); + } + else type = OS_TYPE_DIR; + + err = 0; + if(type == OS_TYPE_SYMLINK) + inode->i_op = &page_symlink_inode_operations; + else if(type == OS_TYPE_DIR) + inode->i_op = &hostfs_dir_iops; + else inode->i_op = &hostfs_iops; + + if(type == OS_TYPE_DIR) inode->i_fop = &hostfs_dir_fops; + else inode->i_fop = &hostfs_file_fops; + + if(type == OS_TYPE_SYMLINK) + inode->i_mapping->a_ops = &hostfs_link_aops; + else inode->i_mapping->a_ops = &hostfs_aops; + + switch (type) { + case OS_TYPE_CHARDEV: + init_special_inode(inode, S_IFCHR, rdev); + break; + case OS_TYPE_BLOCKDEV: + init_special_inode(inode, S_IFBLK, rdev); + break; + case OS_TYPE_FIFO: + init_special_inode(inode, S_IFIFO, 0); + break; + case OS_TYPE_SOCK: + init_special_inode(inode, S_IFSOCK, 0); + break; + } + out: + return(err); +} + +int hostfs_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd) +{ + struct inode *inode; + char *name; + int error, fd; + + error = -ENOMEM; + inode = iget(dir->i_sb, 0); + if(inode == NULL) goto out; + + error = init_inode(inode, dentry); + if(error) + goto out_put; + + error = -ENOMEM; + name = dentry_name(dentry, 0); + if(name == NULL) + goto out_put; + + fd = file_create(name, + mode & S_IRUSR, mode & S_IWUSR, mode & S_IXUSR, + mode & S_IRGRP, mode & S_IWGRP, mode & S_IXGRP, + mode & S_IROTH, mode & S_IWOTH, mode & S_IXOTH); + if(fd < 0) + error = fd; + else error = read_name(inode, name); + + kfree(name); + if(error) + goto out_put; + + HOSTFS_I(inode)->fd = fd; + HOSTFS_I(inode)->mode = FMODE_READ | FMODE_WRITE; + d_instantiate(dentry, inode); + return(0); + + out_put: + iput(inode); + out: + return(error); +} + +struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry, + struct nameidata *nd) +{ + struct inode *inode; + char *name; + int err; + + err = -ENOMEM; + inode = iget(ino->i_sb, 0); + if(inode == NULL) + goto out; + + err = init_inode(inode, dentry); + if(err) + goto out_put; + + err = -ENOMEM; + name = dentry_name(dentry, 0); + if(name == NULL) + goto out_put; + + err = read_name(inode, name); + kfree(name); + if(err == -ENOENT){ + iput(inode); + inode = NULL; + } + else if(err) + goto out_put; + + d_add(dentry, inode); + dentry->d_op = &hostfs_dentry_ops; + return(NULL); + + out_put: + iput(inode); + out: + return(ERR_PTR(err)); +} + +static char *inode_dentry_name(struct inode *ino, struct dentry *dentry) +{ + char *file; + int len; + + file = inode_name(ino, dentry->d_name.len + 1); + if(file == NULL) return(NULL); + strcat(file, "/"); + len = strlen(file); + strncat(file, dentry->d_name.name, dentry->d_name.len); + file[len + dentry->d_name.len] = '\0'; + return(file); +} + +int hostfs_link(struct dentry *to, struct inode *ino, struct dentry *from) +{ + char *from_name, *to_name; + int err; + + if((from_name = inode_dentry_name(ino, from)) == NULL) + return(-ENOMEM); + to_name = dentry_name(to, 0); + if(to_name == NULL){ + kfree(from_name); + return(-ENOMEM); + } + err = link_file(to_name, from_name); + kfree(from_name); + kfree(to_name); + return(err); +} + +int hostfs_unlink(struct inode *ino, struct dentry *dentry) +{ + char *file; + int err; + + if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM); + if(append) + return(-EPERM); + + err = unlink_file(file); + kfree(file); + return(err); +} + +int hostfs_symlink(struct inode *ino, struct dentry *dentry, const char *to) +{ + char *file; + int err; + + if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM); + err = make_symlink(file, to); + kfree(file); + return(err); +} + +int hostfs_mkdir(struct inode *ino, struct dentry *dentry, int mode) +{ + char *file; + int err; + + if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM); + err = do_mkdir(file, mode); + kfree(file); + return(err); +} + +int hostfs_rmdir(struct inode *ino, struct dentry *dentry) +{ + char *file; + int err; + + if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM); + err = do_rmdir(file); + kfree(file); + return(err); +} + +int hostfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) +{ + struct inode *inode; + char *name; + int err = -ENOMEM; + + inode = iget(dir->i_sb, 0); + if(inode == NULL) + goto out; + + err = init_inode(inode, dentry); + if(err) + goto out_put; + + err = -ENOMEM; + name = dentry_name(dentry, 0); + if(name == NULL) + goto out_put; + + init_special_inode(inode, mode, dev); + err = do_mknod(name, mode, dev); + if(err) + goto out_free; + + err = read_name(inode, name); + kfree(name); + if(err) + goto out_put; + + d_instantiate(dentry, inode); + return(0); + + out_free: + kfree(name); + out_put: + iput(inode); + out: + return(err); +} + +int hostfs_rename(struct inode *from_ino, struct dentry *from, + struct inode *to_ino, struct dentry *to) +{ + char *from_name, *to_name; + int err; + + if((from_name = inode_dentry_name(from_ino, from)) == NULL) + return(-ENOMEM); + if((to_name = inode_dentry_name(to_ino, to)) == NULL){ + kfree(from_name); + return(-ENOMEM); + } + err = rename_file(from_name, to_name); + kfree(from_name); + kfree(to_name); + return(err); +} + +void hostfs_truncate(struct inode *ino) +{ + not_implemented(); +} + +int hostfs_permission(struct inode *ino, int desired, struct nameidata *nd) +{ + char *name; + int r = 0, w = 0, x = 0, err; + + if(desired & MAY_READ) r = 1; + if(desired & MAY_WRITE) w = 1; + if(desired & MAY_EXEC) x = 1; + name = inode_name(ino, 0); + if(name == NULL) return(-ENOMEM); + err = access_file(name, r, w, x); + kfree(name); + if(!err) err = vfs_permission(ino, desired); + return(err); +} + +int hostfs_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct hostfs_iattr attrs; + char *name; + int err; + + if(append) + attr->ia_valid &= ~ATTR_SIZE; + + attrs.ia_valid = 0; + if(attr->ia_valid & ATTR_MODE){ + attrs.ia_valid |= HOSTFS_ATTR_MODE; + attrs.ia_mode = attr->ia_mode; + } + if(attr->ia_valid & ATTR_UID){ + if((dentry->d_inode->i_sb->s_dev == ROOT_DEV) && + (attr->ia_uid == 0)) + attr->ia_uid = getuid(); + attrs.ia_valid |= HOSTFS_ATTR_UID; + attrs.ia_uid = attr->ia_uid; + } + if(attr->ia_valid & ATTR_GID){ + if((dentry->d_inode->i_sb->s_dev == ROOT_DEV) && + (attr->ia_gid == 0)) + attr->ia_gid = getuid(); + attrs.ia_valid |= HOSTFS_ATTR_GID; + attrs.ia_gid = attr->ia_gid; + } + if(attr->ia_valid & ATTR_SIZE){ + attrs.ia_valid |= HOSTFS_ATTR_SIZE; + attrs.ia_size = attr->ia_size; + } + if(attr->ia_valid & ATTR_ATIME){ + attrs.ia_valid |= HOSTFS_ATTR_ATIME; + attrs.ia_atime = attr->ia_atime; + } + if(attr->ia_valid & ATTR_MTIME){ + attrs.ia_valid |= HOSTFS_ATTR_MTIME; + attrs.ia_mtime = attr->ia_mtime; + } + if(attr->ia_valid & ATTR_CTIME){ + attrs.ia_valid |= HOSTFS_ATTR_CTIME; + attrs.ia_ctime = attr->ia_ctime; + } + if(attr->ia_valid & ATTR_ATIME_SET){ + attrs.ia_valid |= HOSTFS_ATTR_ATIME_SET; + } + if(attr->ia_valid & ATTR_MTIME_SET){ + attrs.ia_valid |= HOSTFS_ATTR_MTIME_SET; + } + name = dentry_name(dentry, 0); + if(name == NULL) return(-ENOMEM); + err = set_attr(name, &attrs); + kfree(name); + if(err) + return(err); + + return(inode_setattr(dentry->d_inode, attr)); +} + +int hostfs_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) +{ + generic_fillattr(dentry->d_inode, stat); + return(0); +} + +static struct inode_operations hostfs_iops = { + .create = hostfs_create, + .link = hostfs_link, + .unlink = hostfs_unlink, + .symlink = hostfs_symlink, + .mkdir = hostfs_mkdir, + .rmdir = hostfs_rmdir, + .mknod = hostfs_mknod, + .rename = hostfs_rename, + .truncate = hostfs_truncate, + .permission = hostfs_permission, + .setattr = hostfs_setattr, + .getattr = hostfs_getattr, +}; + +static struct inode_operations hostfs_dir_iops = { + .create = hostfs_create, + .lookup = hostfs_lookup, + .link = hostfs_link, + .unlink = hostfs_unlink, + .symlink = hostfs_symlink, + .mkdir = hostfs_mkdir, + .rmdir = hostfs_rmdir, + .mknod = hostfs_mknod, + .rename = hostfs_rename, + .truncate = hostfs_truncate, + .permission = hostfs_permission, + .setattr = hostfs_setattr, + .getattr = hostfs_getattr, +}; + +int hostfs_link_readpage(struct file *file, struct page *page) +{ + char *buffer, *name; + long long start; + int err; + + start = page->index << PAGE_CACHE_SHIFT; + buffer = kmap(page); + name = inode_name(page->mapping->host, 0); + if(name == NULL) return(-ENOMEM); + err = do_readlink(name, buffer, PAGE_CACHE_SIZE); + kfree(name); + if(err == PAGE_CACHE_SIZE) + err = -E2BIG; + else if(err > 0){ + flush_dcache_page(page); + SetPageUptodate(page); + if (PageError(page)) ClearPageError(page); + err = 0; + } + kunmap(page); + unlock_page(page); + return(err); +} + +static struct address_space_operations hostfs_link_aops = { + .readpage = hostfs_link_readpage, +}; + +static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent) +{ + struct inode *root_inode; + char *name, *data = d; + int err; + + sb->s_blocksize = 1024; + sb->s_blocksize_bits = 10; + sb->s_magic = HOSTFS_SUPER_MAGIC; + sb->s_op = &hostfs_sbops; + + if((data == NULL) || (*data == '\0')) + data = root_ino; + + err = -ENOMEM; + name = kmalloc(strlen(data) + 1, GFP_KERNEL); + if(name == NULL) + goto out; + + strcpy(name, data); + + root_inode = iget(sb, 0); + if(root_inode == NULL) + goto out_free; + + err = init_inode(root_inode, NULL); + if(err) + goto out_put; + + HOSTFS_I(root_inode)->host_filename = name; + + err = -ENOMEM; + sb->s_root = d_alloc_root(root_inode); + if(sb->s_root == NULL) + goto out_put; + + err = read_inode(root_inode); + if(err) + goto out_put; + + return(0); + + out_put: + iput(root_inode); + out_free: + kfree(name); + out: + return(err); +} + +static struct super_block *hostfs_read_sb(struct file_system_type *type, + int flags, const char *dev_name, + void *data) +{ + return(get_sb_nodev(type, flags, data, hostfs_fill_sb_common)); +} + +static struct file_system_type hostfs_type = { + .owner = THIS_MODULE, + .name = "hostfs", + .get_sb = hostfs_read_sb, + .kill_sb = kill_anon_super, + .fs_flags = 0, +}; + +static int __init init_hostfs(void) +{ + return(register_filesystem(&hostfs_type)); +} + +static void __exit exit_hostfs(void) +{ + unregister_filesystem(&hostfs_type); +} + +module_init(init_hostfs) +module_exit(exit_hostfs) +MODULE_LICENSE("GPL"); + +/* + * 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: + */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/hostfs/hostfs_user.c 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hostfs.h" +#include "kern_util.h" +#include "user.h" + +int stat_file(const char *path, unsigned long long *inode_out, int *mode_out, + int *nlink_out, int *uid_out, int *gid_out, + unsigned long long *size_out, struct timespec *atime_out, + struct timespec *mtime_out, struct timespec *ctime_out, + int *blksize_out, unsigned long long *blocks_out) +{ + struct stat64 buf; + + if(lstat64(path, &buf) < 0) + return(-errno); + + /* See the Makefile for why STAT64_INO_FIELD is passed in + * by the build + */ + if(inode_out != NULL) *inode_out = buf.STAT64_INO_FIELD; + if(mode_out != NULL) *mode_out = buf.st_mode; + if(nlink_out != NULL) *nlink_out = buf.st_nlink; + if(uid_out != NULL) *uid_out = buf.st_uid; + if(gid_out != NULL) *gid_out = buf.st_gid; + if(size_out != NULL) *size_out = buf.st_size; + if(atime_out != NULL) { + atime_out->tv_sec = buf.st_atime; + atime_out->tv_nsec = 0; + } + if(mtime_out != NULL) { + mtime_out->tv_sec = buf.st_mtime; + mtime_out->tv_nsec = 0; + } + if(ctime_out != NULL) { + ctime_out->tv_sec = buf.st_ctime; + ctime_out->tv_nsec = 0; + } + if(blksize_out != NULL) *blksize_out = buf.st_blksize; + if(blocks_out != NULL) *blocks_out = buf.st_blocks; + return(0); +} + +int file_type(const char *path, int *rdev) +{ + struct stat64 buf; + + if(lstat64(path, &buf) < 0) + return(-errno); + if(rdev != NULL) + *rdev = buf.st_rdev; + + if(S_ISDIR(buf.st_mode)) return(OS_TYPE_DIR); + else if(S_ISLNK(buf.st_mode)) return(OS_TYPE_SYMLINK); + else if(S_ISCHR(buf.st_mode)) return(OS_TYPE_CHARDEV); + else if(S_ISBLK(buf.st_mode)) return(OS_TYPE_BLOCKDEV); + else if(S_ISFIFO(buf.st_mode))return(OS_TYPE_FIFO); + else if(S_ISSOCK(buf.st_mode))return(OS_TYPE_SOCK); + else return(OS_TYPE_FILE); +} + +int access_file(char *path, int r, int w, int x) +{ + int mode = 0; + + if(r) mode = R_OK; + if(w) mode |= W_OK; + if(x) mode |= X_OK; + if(access(path, mode) != 0) return(-errno); + else return(0); +} + +int open_file(char *path, int r, int w, int append) +{ + int mode = 0, fd; + + if(r && !w) + mode = O_RDONLY; + else if(!r && w) + mode = O_WRONLY; + else if(r && w) + mode = O_RDWR; + else panic("Impossible mode in open_file"); + + if(append) + mode |= O_APPEND; + fd = open64(path, mode); + if(fd < 0) return(-errno); + else return(fd); +} + +void *open_dir(char *path, int *err_out) +{ + DIR *dir; + + dir = opendir(path); + *err_out = errno; + if(dir == NULL) return(NULL); + return(dir); +} + +char *read_dir(void *stream, unsigned long long *pos, + unsigned long long *ino_out, int *len_out) +{ + DIR *dir = stream; + struct dirent *ent; + + seekdir(dir, *pos); + ent = readdir(dir); + if(ent == NULL) return(NULL); + *len_out = strlen(ent->d_name); + *ino_out = ent->d_ino; + *pos = telldir(dir); + return(ent->d_name); +} + +int read_file(int fd, unsigned long long *offset, char *buf, int len) +{ + int n; + + n = pread64(fd, buf, len, *offset); + if(n < 0) return(-errno); + *offset += n; + return(n); +} + +int write_file(int fd, unsigned long long *offset, const char *buf, int len) +{ + int n; + + n = pwrite64(fd, buf, len, *offset); + if(n < 0) return(-errno); + *offset += n; + return(n); +} + +int lseek_file(int fd, long long offset, int whence) +{ + int ret; + + ret = lseek64(fd, offset, whence); + if(ret < 0) return(-errno); + return(0); +} + +void close_file(void *stream) +{ + close(*((int *) stream)); +} + +void close_dir(void *stream) +{ + closedir(stream); +} + +int file_create(char *name, int ur, int uw, int ux, int gr, + int gw, int gx, int or, int ow, int ox) +{ + int mode, fd; + + mode = 0; + mode |= ur ? S_IRUSR : 0; + mode |= uw ? S_IWUSR : 0; + mode |= ux ? S_IXUSR : 0; + mode |= gr ? S_IRGRP : 0; + mode |= gw ? S_IWGRP : 0; + mode |= gx ? S_IXGRP : 0; + mode |= or ? S_IROTH : 0; + mode |= ow ? S_IWOTH : 0; + mode |= ox ? S_IXOTH : 0; + fd = open64(name, O_CREAT | O_RDWR, mode); + if(fd < 0) + return(-errno); + return(fd); +} + +int set_attr(const char *file, struct hostfs_iattr *attrs) +{ + struct utimbuf buf; + int err, ma; + + if(attrs->ia_valid & HOSTFS_ATTR_MODE){ + if(chmod(file, attrs->ia_mode) != 0) return(-errno); + } + if(attrs->ia_valid & HOSTFS_ATTR_UID){ + if(chown(file, attrs->ia_uid, -1)) return(-errno); + } + if(attrs->ia_valid & HOSTFS_ATTR_GID){ + if(chown(file, -1, attrs->ia_gid)) return(-errno); + } + if(attrs->ia_valid & HOSTFS_ATTR_SIZE){ + if(truncate(file, attrs->ia_size)) return(-errno); + } + ma = HOSTFS_ATTR_ATIME_SET | HOSTFS_ATTR_MTIME_SET; + if((attrs->ia_valid & ma) == ma){ + buf.actime = attrs->ia_atime.tv_sec; + buf.modtime = attrs->ia_mtime.tv_sec; + if(utime(file, &buf) != 0) return(-errno); + } + else { + struct timespec ts; + + if(attrs->ia_valid & HOSTFS_ATTR_ATIME_SET){ + err = stat_file(file, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, &ts, NULL, NULL, NULL); + if(err != 0) + return(err); + buf.actime = attrs->ia_atime.tv_sec; + buf.modtime = ts.tv_sec; + if(utime(file, &buf) != 0) + return(-errno); + } + if(attrs->ia_valid & HOSTFS_ATTR_MTIME_SET){ + err = stat_file(file, NULL, NULL, NULL, NULL, NULL, + NULL, &ts, NULL, NULL, NULL, NULL); + if(err != 0) + return(err); + buf.actime = ts.tv_sec; + buf.modtime = attrs->ia_mtime.tv_sec; + if(utime(file, &buf) != 0) + return(-errno); + } + } + if(attrs->ia_valid & HOSTFS_ATTR_CTIME) ; + if(attrs->ia_valid & (HOSTFS_ATTR_ATIME | HOSTFS_ATTR_MTIME)){ + err = stat_file(file, NULL, NULL, NULL, NULL, NULL, NULL, + &attrs->ia_atime, &attrs->ia_mtime, NULL, + NULL, NULL); + if(err != 0) return(err); + } + return(0); +} + +int make_symlink(const char *from, const char *to) +{ + int err; + + err = symlink(to, from); + if(err) return(-errno); + return(0); +} + +int unlink_file(const char *file) +{ + int err; + + err = unlink(file); + if(err) return(-errno); + return(0); +} + +int do_mkdir(const char *file, int mode) +{ + int err; + + err = mkdir(file, mode); + if(err) return(-errno); + return(0); +} + +int do_rmdir(const char *file) +{ + int err; + + err = rmdir(file); + if(err) return(-errno); + return(0); +} + +int do_mknod(const char *file, int mode, int dev) +{ + int err; + + err = mknod(file, mode, dev); + if(err) return(-errno); + return(0); +} + +int link_file(const char *to, const char *from) +{ + int err; + + err = link(to, from); + if(err) return(-errno); + return(0); +} + +int do_readlink(char *file, char *buf, int size) +{ + int n; + + n = readlink(file, buf, size); + if(n < 0) + return(-errno); + if(n < size) + buf[n] = '\0'; + return(n); +} + +int rename_file(char *from, char *to) +{ + int err; + + err = rename(from, to); + if(err < 0) return(-errno); + return(0); +} + +int do_statfs(char *root, long *bsize_out, long long *blocks_out, + long long *bfree_out, long long *bavail_out, + long long *files_out, long long *ffree_out, + void *fsid_out, int fsid_size, long *namelen_out, + long *spare_out) +{ + struct statfs64 buf; + int err; + + err = statfs64(root, &buf); + if(err < 0) return(-errno); + *bsize_out = buf.f_bsize; + *blocks_out = buf.f_blocks; + *bfree_out = buf.f_bfree; + *bavail_out = buf.f_bavail; + *files_out = buf.f_files; + *ffree_out = buf.f_ffree; + memcpy(fsid_out, &buf.f_fsid, + sizeof(buf.f_fsid) > fsid_size ? fsid_size : + sizeof(buf.f_fsid)); + *namelen_out = buf.f_namelen; + spare_out[0] = buf.f_spare[0]; + spare_out[1] = buf.f_spare[1]; + spare_out[2] = buf.f_spare[2]; + spare_out[3] = buf.f_spare[3]; + spare_out[4] = buf.f_spare[4]; + spare_out[5] = buf.f_spare[5]; + return(0); +} + +/* + * 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: + */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/hostfs/Makefile 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,26 @@ +# +# Copyright (C) 2000 Jeff Dike (jdike@karaya.com) +# Licensed under the GPL +# + +# struct stat64 changed the inode field name between 2.2 and 2.4 from st_ino +# to __st_ino. It stayed in the same place, so as long as the correct name +# is used, hostfs compiled on 2.2 should work on 2.4 and vice versa. + +STAT64_INO_FIELD := $(shell grep -q __st_ino /usr/include/bits/stat.h && \ + echo __)st_ino + +hostfs-objs := hostfs_kern.o hostfs_user.o + +obj-y = +obj-$(CONFIG_HOSTFS) += hostfs.o + +SINGLE_OBJS = $(foreach f,$(patsubst %.o,%,$(obj-y) $(obj-m)),$($(f)-objs)) + +USER_OBJS := $(filter %_user.o,$(obj-y) $(obj-m) $(SINGLE_OBJS)) +USER_OBJS := $(foreach file,$(USER_OBJS),$(obj)/$(file)) + +USER_CFLAGS += -DSTAT64_INO_FIELD=$(STAT64_INO_FIELD) + +$(USER_OBJS) : %.o: %.c + $(CC) $(CFLAGS_$(notdir $@)) $(USER_CFLAGS) -c -o $@ $< --- linux-2.6.9-rc1/fs/hpfs/super.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/fs/hpfs/super.c 2004-09-02 20:51:52.000000000 -0700 @@ -191,7 +191,7 @@ static int init_inodecache(void) { hpfs_inode_cachep = kmem_cache_create("hpfs_inode_cache", sizeof(struct hpfs_inode_info), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (hpfs_inode_cachep == NULL) return -ENOMEM; --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/hppfs/hppfs_kern.c 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,815 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "os.h" + +static int init_inode(struct inode *inode, struct dentry *dentry); + +struct hppfs_data { + struct list_head list; + char contents[PAGE_SIZE - sizeof(struct list_head)]; +}; + +struct hppfs_private { + struct file *proc_file; + int host_fd; + loff_t len; + struct hppfs_data *contents; +}; + +struct hppfs_inode_info { + struct dentry *proc_dentry; + struct inode vfs_inode; +}; + +static inline struct hppfs_inode_info *HPPFS_I(struct inode *inode) +{ + return(list_entry(inode, struct hppfs_inode_info, vfs_inode)); +} + +#define HPPFS_SUPER_MAGIC 0xb00000ee + +static struct super_operations hppfs_sbops; + +static int is_pid(struct dentry *dentry) +{ + struct super_block *sb; + int i; + + sb = dentry->d_sb; + if((sb->s_op != &hppfs_sbops) || (dentry->d_parent != sb->s_root)) + return(0); + + for(i = 0; i < dentry->d_name.len; i++){ + if(!isdigit(dentry->d_name.name[i])) + return(0); + } + return(1); +} + +static char *dentry_name(struct dentry *dentry, int extra) +{ + struct dentry *parent; + char *root, *name; + const char *seg_name; + int len, seg_len; + + len = 0; + parent = dentry; + while(parent->d_parent != parent){ + if(is_pid(parent)) + len += strlen("pid") + 1; + else len += parent->d_name.len + 1; + parent = parent->d_parent; + } + + root = "proc"; + len += strlen(root); + name = kmalloc(len + extra + 1, GFP_KERNEL); + if(name == NULL) return(NULL); + + name[len] = '\0'; + parent = dentry; + while(parent->d_parent != parent){ + if(is_pid(parent)){ + seg_name = "pid"; + seg_len = strlen("pid"); + } + else { + seg_name = parent->d_name.name; + seg_len = parent->d_name.len; + } + + len -= seg_len + 1; + name[len] = '/'; + strncpy(&name[len + 1], seg_name, seg_len); + parent = parent->d_parent; + } + strncpy(name, root, strlen(root)); + return(name); +} + +struct dentry_operations hppfs_dentry_ops = { +}; + +static int file_removed(struct dentry *dentry, const char *file) +{ + char *host_file; + int extra, fd; + + extra = 0; + if(file != NULL) extra += strlen(file) + 1; + + host_file = dentry_name(dentry, extra + strlen("/remove")); + if(host_file == NULL){ + printk("file_removed : allocation failed\n"); + return(-ENOMEM); + } + + if(file != NULL){ + strcat(host_file, "/"); + strcat(host_file, file); + } + strcat(host_file, "/remove"); + + fd = os_open_file(host_file, of_read(OPENFLAGS()), 0); + kfree(host_file); + if(fd > 0){ + os_close_file(fd); + return(1); + } + return(0); +} + +static void hppfs_read_inode(struct inode *ino) +{ + struct inode *proc_ino; + + if(HPPFS_I(ino)->proc_dentry == NULL) + return; + + proc_ino = HPPFS_I(ino)->proc_dentry->d_inode; + ino->i_uid = proc_ino->i_uid; + ino->i_gid = proc_ino->i_gid; + ino->i_atime = proc_ino->i_atime; + ino->i_mtime = proc_ino->i_mtime; + ino->i_ctime = proc_ino->i_ctime; + ino->i_ino = proc_ino->i_ino; + ino->i_mode = proc_ino->i_mode; + ino->i_nlink = proc_ino->i_nlink; + ino->i_size = proc_ino->i_size; + ino->i_blksize = proc_ino->i_blksize; + ino->i_blocks = proc_ino->i_blocks; +} + +static struct dentry *hppfs_lookup(struct inode *ino, struct dentry *dentry, + struct nameidata *nd) +{ + struct dentry *proc_dentry, *new, *parent; + struct inode *inode; + int err, deleted; + + deleted = file_removed(dentry, NULL); + if(deleted < 0) + return(ERR_PTR(deleted)); + else if(deleted) + return(ERR_PTR(-ENOENT)); + + err = -ENOMEM; + parent = HPPFS_I(ino)->proc_dentry; + down(&parent->d_inode->i_sem); + proc_dentry = d_lookup(parent, &dentry->d_name); + if(proc_dentry == NULL){ + proc_dentry = d_alloc(parent, &dentry->d_name); + if(proc_dentry == NULL){ + up(&parent->d_inode->i_sem); + goto out; + } + new = (*parent->d_inode->i_op->lookup)(parent->d_inode, + proc_dentry, NULL); + if(new){ + dput(proc_dentry); + proc_dentry = new; + } + } + up(&parent->d_inode->i_sem); + + if(IS_ERR(proc_dentry)) + return(proc_dentry); + + inode = iget(ino->i_sb, 0); + if(inode == NULL) + goto out_dput; + + err = init_inode(inode, proc_dentry); + if(err) + goto out_put; + + hppfs_read_inode(inode); + + d_add(dentry, inode); + dentry->d_op = &hppfs_dentry_ops; + return(NULL); + + out_put: + iput(inode); + out_dput: + dput(proc_dentry); + out: + return(ERR_PTR(err)); +} + +static struct inode_operations hppfs_file_iops = { +}; + +static ssize_t read_proc(struct file *file, char *buf, ssize_t count, + loff_t *ppos, int is_user) +{ + ssize_t (*read)(struct file *, char *, size_t, loff_t *); + ssize_t n; + + read = file->f_dentry->d_inode->i_fop->read; + + if(!is_user) + set_fs(KERNEL_DS); + + n = (*read)(file, buf, count, &file->f_pos); + + if(!is_user) + set_fs(USER_DS); + + if(ppos) *ppos = file->f_pos; + return(n); +} + +static ssize_t hppfs_read_file(int fd, char *buf, ssize_t count) +{ + ssize_t n; + int cur, err; + char *new_buf; + + n = -ENOMEM; + new_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if(new_buf == NULL){ + printk("hppfs_read_file : kmalloc failed\n"); + goto out; + } + n = 0; + while(count > 0){ + cur = min_t(ssize_t, count, PAGE_SIZE); + err = os_read_file(fd, new_buf, cur); + if(err < 0){ + printk("hppfs_read : read failed, errno = %d\n", + count); + n = err; + goto out_free; + } + else if(err == 0) + break; + + if(copy_to_user(buf, new_buf, err)){ + n = -EFAULT; + goto out_free; + } + n += err; + count -= err; + } + out_free: + kfree(new_buf); + out: + return(n); +} + +static ssize_t hppfs_read(struct file *file, char *buf, size_t count, + loff_t *ppos) +{ + struct hppfs_private *hppfs = file->private_data; + struct hppfs_data *data; + loff_t off; + int err; + + if(hppfs->contents != NULL){ + if(*ppos >= hppfs->len) return(0); + + data = hppfs->contents; + off = *ppos; + while(off >= sizeof(data->contents)){ + data = list_entry(data->list.next, struct hppfs_data, + list); + off -= sizeof(data->contents); + } + + if(off + count > hppfs->len) + count = hppfs->len - off; + copy_to_user(buf, &data->contents[off], count); + *ppos += count; + } + else if(hppfs->host_fd != -1){ + err = os_seek_file(hppfs->host_fd, *ppos); + if(err){ + printk("hppfs_read : seek failed, errno = %d\n", err); + return(err); + } + count = hppfs_read_file(hppfs->host_fd, buf, count); + if(count > 0) + *ppos += count; + } + else count = read_proc(hppfs->proc_file, buf, count, ppos, 1); + + return(count); +} + +static ssize_t hppfs_write(struct file *file, const char *buf, size_t len, + loff_t *ppos) +{ + struct hppfs_private *data = file->private_data; + struct file *proc_file = data->proc_file; + ssize_t (*write)(struct file *, const char *, size_t, loff_t *); + int err; + + write = proc_file->f_dentry->d_inode->i_fop->write; + + proc_file->f_pos = file->f_pos; + err = (*write)(proc_file, buf, len, &proc_file->f_pos); + file->f_pos = proc_file->f_pos; + + return(err); +} + +static int open_host_sock(char *host_file, int *filter_out) +{ + char *end; + int fd; + + end = &host_file[strlen(host_file)]; + strcpy(end, "/rw"); + *filter_out = 1; + fd = os_connect_socket(host_file); + if(fd > 0) + return(fd); + + strcpy(end, "/r"); + *filter_out = 0; + fd = os_connect_socket(host_file); + return(fd); +} + +static void free_contents(struct hppfs_data *head) +{ + struct hppfs_data *data; + struct list_head *ele, *next; + + if(head == NULL) return; + + list_for_each_safe(ele, next, &head->list){ + data = list_entry(ele, struct hppfs_data, list); + kfree(data); + } + kfree(head); +} + +static struct hppfs_data *hppfs_get_data(int fd, int filter, + struct file *proc_file, + struct file *hppfs_file, + loff_t *size_out) +{ + struct hppfs_data *data, *new, *head; + int n, err; + + err = -ENOMEM; + data = kmalloc(sizeof(*data), GFP_KERNEL); + if(data == NULL){ + printk("hppfs_get_data : head allocation failed\n"); + goto failed; + } + + INIT_LIST_HEAD(&data->list); + + head = data; + *size_out = 0; + + if(filter){ + while((n = read_proc(proc_file, data->contents, + sizeof(data->contents), NULL, 0)) > 0) + os_write_file(fd, data->contents, n); + err = os_shutdown_socket(fd, 0, 1); + if(err){ + printk("hppfs_get_data : failed to shut down " + "socket\n"); + goto failed_free; + } + } + while(1){ + n = os_read_file(fd, data->contents, sizeof(data->contents)); + if(n < 0){ + err = n; + printk("hppfs_get_data : read failed, errno = %d\n", + err); + goto failed_free; + } + else if(n == 0) + break; + + *size_out += n; + + if(n < sizeof(data->contents)) + break; + + new = kmalloc(sizeof(*data), GFP_KERNEL); + if(new == 0){ + printk("hppfs_get_data : data allocation failed\n"); + err = -ENOMEM; + goto failed_free; + } + + INIT_LIST_HEAD(&new->list); + list_add(&new->list, &data->list); + data = new; + } + return(head); + + failed_free: + free_contents(head); + failed: + return(ERR_PTR(err)); +} + +static struct hppfs_private *hppfs_data(void) +{ + struct hppfs_private *data; + + data = kmalloc(sizeof(*data), GFP_KERNEL); + if(data == NULL) + return(data); + + *data = ((struct hppfs_private ) { .host_fd = -1, + .len = -1, + .contents = NULL } ); + return(data); +} + +static int file_mode(int fmode) +{ + if(fmode == (FMODE_READ | FMODE_WRITE)) + return(O_RDWR); + if(fmode == FMODE_READ) + return(O_RDONLY); + if(fmode == FMODE_WRITE) + return(O_WRONLY); + return(0); +} + +static int hppfs_open(struct inode *inode, struct file *file) +{ + struct hppfs_private *data; + struct dentry *proc_dentry; + char *host_file; + int err, fd, type, filter; + + err = -ENOMEM; + data = hppfs_data(); + if(data == NULL) + goto out; + + host_file = dentry_name(file->f_dentry, strlen("/rw")); + if(host_file == NULL) + goto out_free2; + + proc_dentry = HPPFS_I(inode)->proc_dentry; + + /* XXX This isn't closed anywhere */ + data->proc_file = dentry_open(dget(proc_dentry), NULL, + file_mode(file->f_mode)); + err = PTR_ERR(data->proc_file); + if(IS_ERR(data->proc_file)) + goto out_free1; + + type = os_file_type(host_file); + if(type == OS_TYPE_FILE){ + fd = os_open_file(host_file, of_read(OPENFLAGS()), 0); + if(fd >= 0) + data->host_fd = fd; + else printk("hppfs_open : failed to open '%s', errno = %d\n", + host_file, -fd); + + data->contents = NULL; + } + else if(type == OS_TYPE_DIR){ + fd = open_host_sock(host_file, &filter); + if(fd > 0){ + data->contents = hppfs_get_data(fd, filter, + &data->proc_file, + file, &data->len); + if(!IS_ERR(data->contents)) + data->host_fd = fd; + } + else printk("hppfs_open : failed to open a socket in " + "'%s', errno = %d\n", host_file, -fd); + } + kfree(host_file); + + file->private_data = data; + return(0); + + out_free1: + kfree(host_file); + out_free2: + free_contents(data->contents); + kfree(data); + out: + return(err); +} + +static int hppfs_dir_open(struct inode *inode, struct file *file) +{ + struct hppfs_private *data; + struct dentry *proc_dentry; + int err; + + err = -ENOMEM; + data = hppfs_data(); + if(data == NULL) + goto out; + + proc_dentry = HPPFS_I(inode)->proc_dentry; + data->proc_file = dentry_open(dget(proc_dentry), NULL, + file_mode(file->f_mode)); + err = PTR_ERR(data->proc_file); + if(IS_ERR(data->proc_file)) + goto out_free; + + file->private_data = data; + return(0); + + out_free: + kfree(data); + out: + return(err); +} + +static loff_t hppfs_llseek(struct file *file, loff_t off, int where) +{ + struct hppfs_private *data = file->private_data; + struct file *proc_file = &data->proc_file; + loff_t (*llseek)(struct file *, loff_t, int); + loff_t ret; + + llseek = proc_file->f_dentry->d_inode->i_fop->llseek; + if(llseek != NULL){ + ret = (*llseek)(proc_file, off, where); + if(ret < 0) + return(ret); + } + + return(default_llseek(file, off, where)); +} + +static struct file_operations hppfs_file_fops = { + .owner = NULL, + .llseek = hppfs_llseek, + .read = hppfs_read, + .write = hppfs_write, + .open = hppfs_open, +}; + +struct hppfs_dirent { + void *vfs_dirent; + filldir_t filldir; + struct dentry *dentry; +}; + +static int hppfs_filldir(void *d, const char *name, int size, + loff_t offset, ino_t inode, unsigned int type) +{ + struct hppfs_dirent *dirent = d; + + if(file_removed(dirent->dentry, name)) + return(0); + + return((*dirent->filldir)(dirent->vfs_dirent, name, size, offset, + inode, type)); +} + +static int hppfs_readdir(struct file *file, void *ent, filldir_t filldir) +{ + struct hppfs_private *data = file->private_data; + struct file *proc_file = &data->proc_file; + int (*readdir)(struct file *, void *, filldir_t); + struct hppfs_dirent dirent = ((struct hppfs_dirent) + { .vfs_dirent = ent, + .filldir = filldir, + .dentry = file->f_dentry } ); + int err; + + readdir = proc_file->f_dentry->d_inode->i_fop->readdir; + + proc_file->f_pos = file->f_pos; + err = (*readdir)(proc_file, &dirent, hppfs_filldir); + file->f_pos = proc_file->f_pos; + + return(err); +} + +static int hppfs_fsync(struct file *file, struct dentry *dentry, int datasync) +{ + return(0); +} + +static struct file_operations hppfs_dir_fops = { + .owner = NULL, + .readdir = hppfs_readdir, + .open = hppfs_dir_open, + .fsync = hppfs_fsync, +}; + +static int hppfs_statfs(struct super_block *sb, struct kstatfs *sf) +{ + sf->f_blocks = 0; + sf->f_bfree = 0; + sf->f_bavail = 0; + sf->f_files = 0; + sf->f_ffree = 0; + sf->f_type = HPPFS_SUPER_MAGIC; + return(0); +} + +static struct inode *hppfs_alloc_inode(struct super_block *sb) +{ + struct hppfs_inode_info *hi; + + hi = kmalloc(sizeof(*hi), GFP_KERNEL); + if(hi == NULL) + return(NULL); + + *hi = ((struct hppfs_inode_info) { .proc_dentry = NULL }); + inode_init_once(&hi->vfs_inode); + return(&hi->vfs_inode); +} + +void hppfs_delete_inode(struct inode *ino) +{ + clear_inode(ino); +} + +static void hppfs_destroy_inode(struct inode *inode) +{ + kfree(HPPFS_I(inode)); +} + +static struct super_operations hppfs_sbops = { + .alloc_inode = hppfs_alloc_inode, + .destroy_inode = hppfs_destroy_inode, + .read_inode = hppfs_read_inode, + .delete_inode = hppfs_delete_inode, + .statfs = hppfs_statfs, +}; + +static int hppfs_readlink(struct dentry *dentry, char *buffer, int buflen) +{ + struct file *proc_file; + struct dentry *proc_dentry; + int (*readlink)(struct dentry *, char *, int); + int err, n; + + proc_dentry = HPPFS_I(dentry->d_inode)->proc_dentry; + proc_file = dentry_open(dget(proc_dentry), NULL, O_RDONLY); + err = PTR_ERR(proc_dentry); + if(IS_ERR(proc_dentry)) + return(err); + + readlink = proc_dentry->d_inode->i_op->readlink; + n = (*readlink)(proc_dentry, buffer, buflen); + + fput(proc_file); + + return(n); +} + +static int hppfs_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + struct file *proc_file; + struct dentry *proc_dentry; + int (*follow_link)(struct dentry *, struct nameidata *); + int err, n; + + proc_dentry = HPPFS_I(dentry->d_inode)->proc_dentry; + proc_file = dentry_open(dget(proc_dentry), NULL, O_RDONLY); + err = PTR_ERR(proc_dentry); + if(IS_ERR(proc_dentry)) + return(err); + + follow_link = proc_dentry->d_inode->i_op->follow_link; + n = (*follow_link)(proc_dentry, nd); + + fput(proc_file); + + return(n); +} + +static struct inode_operations hppfs_dir_iops = { + .lookup = hppfs_lookup, +}; + +static struct inode_operations hppfs_link_iops = { + .readlink = hppfs_readlink, + .follow_link = hppfs_follow_link, +}; + +static int init_inode(struct inode *inode, struct dentry *dentry) +{ + if(S_ISDIR(dentry->d_inode->i_mode)){ + inode->i_op = &hppfs_dir_iops; + inode->i_fop = &hppfs_dir_fops; + } + else if(S_ISLNK(dentry->d_inode->i_mode)){ + inode->i_op = &hppfs_link_iops; + inode->i_fop = &hppfs_file_fops; + } + else { + inode->i_op = &hppfs_file_iops; + inode->i_fop = &hppfs_file_fops; + } + + HPPFS_I(inode)->proc_dentry = dentry; + + return(0); +} + +static int hppfs_fill_super(struct super_block *sb, void *d, int silent) +{ + struct inode *root_inode; + struct file_system_type *procfs; + struct super_block *proc_sb; + int err; + + err = -ENOENT; + procfs = get_fs_type("proc"); + if(procfs == NULL) + goto out; + + if(list_empty(&procfs->fs_supers)) + goto out; + + proc_sb = list_entry(procfs->fs_supers.next, struct super_block, + s_instances); + + sb->s_blocksize = 1024; + sb->s_blocksize_bits = 10; + sb->s_magic = HPPFS_SUPER_MAGIC; + sb->s_op = &hppfs_sbops; + + root_inode = iget(sb, 0); + if(root_inode == NULL) + goto out; + + err = init_inode(root_inode, proc_sb->s_root); + if(err) + goto out_put; + + err = -ENOMEM; + sb->s_root = d_alloc_root(root_inode); + if(sb->s_root == NULL) + goto out_put; + + hppfs_read_inode(root_inode); + + return(0); + + out_put: + iput(root_inode); + out: + return(err); +} + +static struct super_block *hppfs_read_super(struct file_system_type *type, + int flags, const char *dev_name, + void *data) +{ + return(get_sb_nodev(type, flags, data, hppfs_fill_super)); +} + +static struct file_system_type hppfs_type = { + .owner = THIS_MODULE, + .name = "hppfs", + .get_sb = hppfs_read_super, + .kill_sb = kill_anon_super, + .fs_flags = 0, +}; + +static int __init init_hppfs(void) +{ + return(register_filesystem(&hppfs_type)); +} + +static void __exit exit_hppfs(void) +{ + unregister_filesystem(&hppfs_type); +} + +module_init(init_hppfs) +module_exit(exit_hppfs) +MODULE_LICENSE("GPL"); + +/* + * 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: + */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/hppfs/Makefile 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,19 @@ +# +# Copyright (C) 2002, 2003 Jeff Dike (jdike@karaya.com) +# Licensed under the GPL +# + +hppfs-objs := hppfs_kern.o + +obj-y = +obj-$(CONFIG_HPPFS) += hppfs.o + +clean: + +modules: + +fastdep: + +dep: + +archmrproper: clean --- linux-2.6.9-rc1/fs/hugetlbfs/inode.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/fs/hugetlbfs/inode.c 2004-09-03 00:56:41.728816904 -0700 @@ -52,6 +52,9 @@ static int hugetlbfs_file_mmap(struct fi loff_t len, vma_len; int ret; + if ((vma->vm_flags & (VM_MAYSHARE | VM_WRITE)) == VM_WRITE) + return -EINVAL; + if (vma->vm_pgoff & (HPAGE_SIZE / PAGE_SIZE - 1)) return -EINVAL; @@ -70,10 +73,19 @@ static int hugetlbfs_file_mmap(struct fi file_accessed(file); vma->vm_flags |= VM_HUGETLB | VM_RESERVED; vma->vm_ops = &hugetlb_vm_ops; + + ret = -ENOMEM; + len = vma_len + ((loff_t)vma->vm_pgoff << PAGE_SHIFT); + if (!(vma->vm_flags & VM_WRITE) && len > inode->i_size) + goto out; + ret = hugetlb_prefault(mapping, vma); - len = vma_len + ((loff_t)vma->vm_pgoff << PAGE_SHIFT); - if (ret == 0 && inode->i_size < len) + if (ret) + goto out; + + if (inode->i_size < len) inode->i_size = len; +out: up(&inode->i_sem); return ret; @@ -199,6 +211,7 @@ static void hugetlbfs_delete_inode(struc hlist_del_init(&inode->i_hash); list_del_init(&inode->i_list); + list_del_init(&inode->i_sb_list); inode->i_state |= I_FREEING; inodes_stat.nr_inodes--; spin_unlock(&inode_lock); @@ -241,6 +254,7 @@ static void hugetlbfs_forget_inode(struc hlist_del_init(&inode->i_hash); out_truncate: list_del_init(&inode->i_list); + list_del_init(&inode->i_sb_list); inode->i_state |= I_FREEING; inodes_stat.nr_inodes--; spin_unlock(&inode_lock); --- linux-2.6.9-rc1/fs/inode.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/fs/inode.c 2004-09-03 00:57:10.863387776 -0700 @@ -81,6 +81,7 @@ static struct hlist_head *inode_hashtabl * the i_state of an inode while it is in use.. */ spinlock_t inode_lock = SPIN_LOCK_UNLOCKED; +EXPORT_SYMBOL(inode_lock); /* * iprune_sem provides exclusion between the kswapd or try_to_free_pages @@ -96,6 +97,7 @@ DECLARE_MUTEX(iprune_sem); * Statistics gathering.. */ struct inodes_stat_t inodes_stat; +EXPORT_SYMBOL(inodes_stat); static kmem_cache_t * inode_cachep; @@ -180,7 +182,7 @@ void destroy_inode(struct inode *inode) else kmem_cache_free(inode_cachep, (inode)); } - +EXPORT_SYMBOL(destroy_inode); /* * These are initializations that only need to be done @@ -196,7 +198,7 @@ void inode_init_once(struct inode *inode sema_init(&inode->i_sem, 1); init_rwsem(&inode->i_alloc_sem); INIT_RADIX_TREE(&inode->i_data.page_tree, GFP_ATOMIC); - spin_lock_init(&inode->i_data.tree_lock); + rwlock_init(&inode->i_data.tree_lock); spin_lock_init(&inode->i_data.i_mmap_lock); atomic_set(&inode->i_data.truncate_count, 0); INIT_LIST_HEAD(&inode->i_data.private_list); @@ -232,6 +234,7 @@ void __iget(struct inode * inode) list_move(&inode->i_list, &inode_in_use); inodes_stat.nr_unused--; } +EXPORT_SYMBOL(__iget); /** * clear_inode - clear an inode @@ -243,6 +246,7 @@ void __iget(struct inode * inode) */ void clear_inode(struct inode *inode) { + might_sleep(); invalidate_inode_buffers(inode); if (inode->i_data.nrpages) @@ -295,7 +299,7 @@ static void dispose_list(struct list_hea /* * Invalidate all inodes for a device. */ -static int invalidate_list(struct list_head *head, struct super_block * sb, struct list_head * dispose) +static int invalidate_list(struct list_head *head, struct list_head *dispose) { struct list_head *next; int busy = 0, count = 0; @@ -308,12 +312,11 @@ static int invalidate_list(struct list_h next = next->next; if (tmp == head) break; - inode = list_entry(tmp, struct inode, i_list); - if (inode->i_sb != sb) - continue; + inode = list_entry(tmp, struct inode, i_sb_list); invalidate_inode_buffers(inode); if (!atomic_read(&inode->i_count)) { hlist_del_init(&inode->i_hash); + list_del(&inode->i_sb_list); list_move(&inode->i_list, dispose); inode->i_state |= I_FREEING; count++; @@ -349,10 +352,7 @@ int invalidate_inodes(struct super_block down(&iprune_sem); spin_lock(&inode_lock); - busy = invalidate_list(&inode_in_use, sb, &throw_away); - busy |= invalidate_list(&inode_unused, sb, &throw_away); - busy |= invalidate_list(&sb->s_dirty, sb, &throw_away); - busy |= invalidate_list(&sb->s_io, sb, &throw_away); + busy = invalidate_list(&sb->s_inodes, &throw_away); spin_unlock(&inode_lock); dispose_list(&throw_away); @@ -452,6 +452,7 @@ static void prune_icache(int nr_to_scan) continue; } hlist_del_init(&inode->i_hash); + list_del_init(&inode->i_sb_list); list_move(&inode->i_list, &freeable); inode->i_state |= I_FREEING; nr_pruned++; @@ -563,6 +564,7 @@ struct inode *new_inode(struct super_blo spin_lock(&inode_lock); inodes_stat.nr_inodes++; list_add(&inode->i_list, &inode_in_use); + list_add(&inode->i_sb_list, &sb->s_inodes); inode->i_ino = ++last_ino; inode->i_state = 0; spin_unlock(&inode_lock); @@ -611,6 +613,7 @@ static struct inode * get_new_inode(stru inodes_stat.nr_inodes++; list_add(&inode->i_list, &inode_in_use); + list_add(&inode->i_sb_list, &sb->s_inodes); hlist_add_head(&inode->i_hash, head); inode->i_state = I_LOCK|I_NEW; spin_unlock(&inode_lock); @@ -659,6 +662,7 @@ static struct inode * get_new_inode_fast inode->i_ino = ino; inodes_stat.nr_inodes++; list_add(&inode->i_list, &inode_in_use); + list_add(&inode->i_sb_list, &sb->s_inodes); hlist_add_head(&inode->i_hash, head); inode->i_state = I_LOCK|I_NEW; spin_unlock(&inode_lock); @@ -995,6 +999,7 @@ void generic_delete_inode(struct inode * struct super_operations *op = inode->i_sb->s_op; list_del_init(&inode->i_list); + list_del_init(&inode->i_sb_list); inode->i_state|=I_FREEING; inodes_stat.nr_inodes--; spin_unlock(&inode_lock); @@ -1023,7 +1028,7 @@ void generic_delete_inode(struct inode * EXPORT_SYMBOL(generic_delete_inode); -static void generic_forget_inode(struct inode *inode) +void generic_forget_inode(struct inode *inode) { struct super_block *sb = inode->i_sb; @@ -1040,6 +1045,7 @@ static void generic_forget_inode(struct hlist_del_init(&inode->i_hash); } list_del_init(&inode->i_list); + list_del_init(&inode->i_sb_list); inode->i_state|=I_FREEING; inodes_stat.nr_inodes--; spin_unlock(&inode_lock); @@ -1048,6 +1054,7 @@ static void generic_forget_inode(struct clear_inode(inode); destroy_inode(inode); } +EXPORT_SYMBOL(generic_forget_inode); /* * Normal UNIX filesystem behaviour: delete the @@ -1228,72 +1235,32 @@ EXPORT_SYMBOL(inode_needs_sync); /* Function back in dquot.c */ int remove_inode_dquot_ref(struct inode *, int, struct list_head *); -void remove_dquot_ref(struct super_block *sb, int type, struct list_head *tofree_head) +void remove_dquot_ref(struct super_block *sb, int type, + struct list_head *tofree_head) { struct inode *inode; - struct list_head *act_head; if (!sb->dq_op) return; /* nothing to do */ spin_lock(&inode_lock); /* This lock is for inodes code */ - /* We hold dqptr_sem so we are safe against the quota code */ - list_for_each(act_head, &inode_in_use) { - inode = list_entry(act_head, struct inode, i_list); - if (inode->i_sb == sb && !IS_NOQUOTA(inode)) - remove_inode_dquot_ref(inode, type, tofree_head); - } - list_for_each(act_head, &inode_unused) { - inode = list_entry(act_head, struct inode, i_list); - if (inode->i_sb == sb && !IS_NOQUOTA(inode)) - remove_inode_dquot_ref(inode, type, tofree_head); - } - list_for_each(act_head, &sb->s_dirty) { - inode = list_entry(act_head, struct inode, i_list); - if (!IS_NOQUOTA(inode)) - remove_inode_dquot_ref(inode, type, tofree_head); - } - list_for_each(act_head, &sb->s_io) { - inode = list_entry(act_head, struct inode, i_list); + /* + * We don't have to lock against quota code - test IS_QUOTAINIT is + * just for speedup... + */ + list_for_each_entry(inode, &sb->s_inodes, i_sb_list) if (!IS_NOQUOTA(inode)) remove_inode_dquot_ref(inode, type, tofree_head); - } + spin_unlock(&inode_lock); } #endif -/* - * Hashed waitqueues for wait_on_inode(). The table is pretty small - the - * kernel doesn't lock many inodes at the same time. - */ -#define I_WAIT_TABLE_ORDER 3 -static struct i_wait_queue_head { - wait_queue_head_t wqh; -} ____cacheline_aligned_in_smp i_wait_queue_heads[1<i_state & I_LOCK) { - schedule(); - goto repeat; - } - remove_wait_queue(wq, &wait); - __set_current_state(TASK_RUNNING); + schedule(); + return 0; } /* @@ -1302,37 +1269,41 @@ repeat: * that it isn't found. This is because iget will immediately call * ->read_inode, and we want to be sure that evidence of the deletion is found * by ->read_inode. - * - * This call might return early if an inode which shares the waitq is woken up. - * This is most easily handled by the caller which will loop around again - * looking for the inode. - * * This is called with inode_lock held. */ static void __wait_on_freeing_inode(struct inode *inode) { - DECLARE_WAITQUEUE(wait, current); - wait_queue_head_t *wq = i_waitq_head(inode); + wait_queue_head_t *wq; + DEFINE_WAIT_BIT(wait, &inode->i_state, __I_LOCK); - add_wait_queue(wq, &wait); - set_current_state(TASK_UNINTERRUPTIBLE); + /* + * I_FREEING and I_CLEAR are cleared in process context under + * inode_lock, so we have to give the tasks who would clear them + * a chance to run and acquire inode_lock. + */ + if (!(inode->i_state & I_LOCK)) { + spin_unlock(&inode_lock); + yield(); + spin_lock(&inode_lock); + return; + } + wq = bit_waitqueue(&inode->i_state, __I_LOCK); + prepare_to_wait(wq, &wait.wait, TASK_UNINTERRUPTIBLE); spin_unlock(&inode_lock); schedule(); - remove_wait_queue(wq, &wait); + finish_wait(wq, &wait.wait); spin_lock(&inode_lock); } void wake_up_inode(struct inode *inode) { - wait_queue_head_t *wq = i_waitq_head(inode); - /* * Prevent speculative execution through spin_unlock(&inode_lock); */ smp_mb(); - if (waitqueue_active(wq)) - wake_up_all(wq); + wake_up_bit(&inode->i_state, __I_LOCK); } +EXPORT_SYMBOL(wake_up_inode); static __initdata unsigned long ihash_entries; static int __init set_ihash_entries(char *str) @@ -1366,15 +1337,9 @@ void __init inode_init_early(void) void __init inode_init(unsigned long mempages) { - int i; - - for (i = 0; i < ARRAY_SIZE(i_wait_queue_heads); i++) - init_waitqueue_head(&i_wait_queue_heads[i].wqh); - /* inode slab cache */ inode_cachep = kmem_cache_create("inode_cache", sizeof(struct inode), - 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, init_once, - NULL); + 0, SLAB_PANIC, init_once, NULL); set_shrinker(DEFAULT_SEEKS, shrink_icache_memory); } --- linux-2.6.9-rc1/fs/isofs/inode.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/fs/isofs/inode.c 2004-09-02 20:51:52.000000000 -0700 @@ -108,7 +108,7 @@ static int init_inodecache(void) { isofs_inode_cachep = kmem_cache_create("isofs_inode_cache", sizeof(struct iso_inode_info), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (isofs_inode_cachep == NULL) return -ENOMEM; @@ -715,6 +715,7 @@ root_found: } s->s_magic = ISOFS_SUPER_MAGIC; + s->s_maxbytes = 0xffffffff; /* We can handle files up to 4 GB */ /* The CDROM is read-only, has no nodes (devices) on it, and since all of the files appear to be owned by root, we really do not want --- linux-2.6.9-rc1/fs/isofs/rock.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/fs/isofs/rock.c 2004-09-02 20:51:52.000000000 -0700 @@ -153,6 +153,7 @@ int get_rock_ridge_filename(struct iso_d } } MAYBE_CONTINUE(repeat,inode); + if (buffer) kfree(buffer); return retnamlen; /* If 0, this file did not have a NM field */ out: if(buffer) kfree(buffer); @@ -351,7 +352,6 @@ int parse_rock_ridge_inode_internal(stru } } MAYBE_CONTINUE(repeat,inode); - return 0; out: if(buffer) kfree(buffer); return 0; @@ -515,6 +515,8 @@ static int rock_ridge_symlink_readpage(s } } MAYBE_CONTINUE(repeat, inode); + if (buffer) + kfree(buffer); if (rpnt == link) goto fail; --- linux-2.6.9-rc1/fs/jbd/checkpoint.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/fs/jbd/checkpoint.c 2004-09-03 00:56:50.977410904 -0700 @@ -455,9 +455,8 @@ int cleanup_journal_tail(journal_t *jour * * Find all the written-back checkpoint buffers in the journal and release them. * - * Called with the journal locked. - * Called with j_list_lock held. - * Returns number of bufers reaped (for debug) + * Called with j_list_lock held, drops it. + * Returns number of bufers reaped */ int __journal_clean_checkpoint_list(journal_t *journal) @@ -467,7 +466,7 @@ int __journal_clean_checkpoint_list(jour transaction = journal->j_checkpoint_transactions; if (transaction == 0) - goto out; + goto out_unlock; last_transaction = transaction->t_cpprev; next_transaction = transaction; @@ -484,13 +483,41 @@ int __journal_clean_checkpoint_list(jour do { jh = next_jh; next_jh = jh->b_cpnext; - /* Use trylock because of the ranknig */ + /* Use trylock because of the ranking */ if (jbd_trylock_bh_state(jh2bh(jh))) ret += __try_to_free_cp_buf(jh); } while (jh != last_jh); } +#ifdef CONFIG_PREEMPT + /* + * This is potentially sucky: semi-quadratic performance if + * there are a lot of dirty buffers. So only do it if the user + * has chosen a preemptible kernel. If !CONFIG_PREEMPT we're + * optimimising for straight-line performance, after all. + * We don't test cond_resched() here because another CPU could + * be waiting on j_list_lock() while holding a different lock. + */ + if ((ret & 127) == 127) { + spin_unlock(&journal->j_list_lock); + /* + * We need to schedule away. Rotate both this + * transaction's buffer list and the checkpoint list to + * try to avoid quadratic behaviour. + */ + jh = transaction->t_checkpoint_list; + if (jh) + transaction->t_checkpoint_list = jh->b_cpnext; + + transaction = journal->j_checkpoint_transactions; + if (transaction) + journal->j_checkpoint_transactions = + transaction->t_cpnext; + return ret; + } +#endif } while (transaction != last_transaction); -out: +out_unlock: + spin_unlock(&journal->j_list_lock); return ret; } --- linux-2.6.9-rc1/fs/jbd/commit.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/fs/jbd/commit.c 2004-09-03 00:56:50.978410752 -0700 @@ -208,9 +208,16 @@ void journal_commit_transaction(journal_ * checkpoint lists. We do this *before* commit because it potentially * frees some memory */ - spin_lock(&journal->j_list_lock); - __journal_clean_checkpoint_list(journal); - spin_unlock(&journal->j_list_lock); + spin_unlock(&journal->j_state_lock); + { + int nr_cleaned; + + do { + spin_lock(&journal->j_list_lock); + nr_cleaned = __journal_clean_checkpoint_list(journal); + } while (nr_cleaned); + } + spin_lock(&journal->j_state_lock); jbd_debug (3, "JBD: commit phase 1\n"); --- linux-2.6.9-rc1/fs/jbd/journal.c 2004-08-24 00:02:20.000000000 -0700 +++ 25/fs/jbd/journal.c 2004-09-03 00:56:42.536694088 -0700 @@ -34,6 +34,7 @@ #include #include #include +#include #include EXPORT_SYMBOL(journal_start); @@ -1714,9 +1715,17 @@ repeat: if (buffer_jbd(bh)) { jh = bh2jh(bh); } else { - J_ASSERT_BH(bh, - (atomic_read(&bh->b_count) > 0) || - (bh->b_page && bh->b_page->mapping)); + if (!(atomic_read(&bh->b_count) > 0 || + (bh->b_page && bh->b_page->mapping))) { + printk(KERN_EMERG "%s: bh->b_count=%d\n", + __FUNCTION__, atomic_read(&bh->b_count)); + printk(KERN_EMERG "%s: bh->b_page=%p\n", + __FUNCTION__, bh->b_page); + if (bh->b_page) + printk(KERN_EMERG "%s: " + "bh->b_page->mapping=%p\n", + __FUNCTION__, bh->b_page->mapping); + } if (!new_jh) { jbd_unlock_bh_journal_head(bh); --- linux-2.6.9-rc1/fs/jbd/revoke.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/fs/jbd/revoke.c 2004-09-02 20:51:52.000000000 -0700 @@ -332,6 +332,7 @@ int journal_revoke(handle_t *handle, uns struct block_device *bdev; int err; + might_sleep(); if (bh_in) BUFFER_TRACE(bh_in, "enter"); --- linux-2.6.9-rc1/fs/jbd/transaction.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/fs/jbd/transaction.c 2004-09-03 00:57:10.580430792 -0700 @@ -633,21 +633,21 @@ repeat: * disk then we cannot do copy-out here. */ if (jh->b_jlist == BJ_Shadow) { - wait_queue_head_t *wqh; - DEFINE_WAIT(wait); + DEFINE_WAIT_BIT(wait, &bh->b_state, BH_Lock); + wait_queue_head_t *wqh + = bit_waitqueue(&bh->b_state, BH_Lock); JBUFFER_TRACE(jh, "on shadow: sleep"); jbd_unlock_bh_state(bh); /* commit wakes up all shadow buffers after IO */ - wqh = bh_waitq_head(bh); for ( ; ; ) { - prepare_to_wait(wqh, &wait, + prepare_to_wait(wqh, &wait.wait, TASK_UNINTERRUPTIBLE); if (jh->b_jlist != BJ_Shadow) break; schedule(); } - finish_wait(wqh, &wait); + finish_wait(wqh, &wait.wait); goto repeat; } @@ -940,7 +940,6 @@ out: int journal_dirty_data(handle_t *handle, struct buffer_head *bh) { journal_t *journal = handle->h_transaction->t_journal; - int need_brelse = 0; struct journal_head *jh; if (is_handle_aborted(handle)) @@ -1025,24 +1024,6 @@ int journal_dirty_data(handle_t *handle, goto no_journal; } - /* - * This buffer may be undergoing writeout in commit. We - * can't return from here and let the caller dirty it - * again because that can cause the write-out loop in - * commit to never terminate. - */ - if (buffer_dirty(bh)) { - get_bh(bh); - spin_unlock(&journal->j_list_lock); - jbd_unlock_bh_state(bh); - need_brelse = 1; - sync_dirty_buffer(bh); - jbd_lock_bh_state(bh); - spin_lock(&journal->j_list_lock); - /* The buffer may become locked again at any - time if it is redirtied */ - } - /* journal_clean_data_list() may have got there first */ if (jh->b_transaction != NULL) { JBUFFER_TRACE(jh, "unfile from commit"); @@ -1072,10 +1053,6 @@ int journal_dirty_data(handle_t *handle, no_journal: spin_unlock(&journal->j_list_lock); jbd_unlock_bh_state(bh); - if (need_brelse) { - BUFFER_TRACE(bh, "brelse"); - __brelse(bh); - } JBUFFER_TRACE(jh, "exit"); journal_put_journal_head(jh); return 0; --- linux-2.6.9-rc1/fs/jffs2/compr.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/fs/jffs2/compr.c 2004-09-03 00:56:55.904661848 -0700 @@ -9,7 +9,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr.c,v 1.41 2004/06/24 09:51:38 havasi Exp $ + * $Id: compr.c,v 1.42 2004/08/07 21:56:08 dwmw2 Exp $ * */ @@ -180,6 +180,11 @@ int jffs2_decompress(struct jffs2_sb_inf struct jffs2_compressor *this; int ret; + /* Older code had a bug where it would write non-zero 'usercompr' + fields. Deal with it. */ + if ((comprtype & 0xff) <= JFFS2_COMPR_ZLIB) + comprtype &= 0xff; + switch (comprtype & 0xff) { case JFFS2_COMPR_NONE: /* This should be special-cased elsewhere, but we might as well deal with it */ @@ -208,7 +213,7 @@ int jffs2_decompress(struct jffs2_sb_inf return ret; } } - printk(KERN_WARNING "JFFS2 compression type 0x%02x not avaiable.\n", comprtype); + printk(KERN_WARNING "JFFS2 compression type 0x%02x not available.\n", comprtype); spin_unlock(&jffs2_compressor_list_lock); return -EIO; } --- linux-2.6.9-rc1/fs/jffs2/super.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/fs/jffs2/super.c 2004-09-03 00:56:57.347442512 -0700 @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: super.c,v 1.97 2004/07/16 15:17:57 dwmw2 Exp $ + * $Id: super.c,v 1.99 2004/08/24 07:59:57 dwmw2 Exp $ * */ @@ -130,7 +130,7 @@ static struct super_block *jffs2_get_sb_ mtd->index, mtd->name)); sb->s_op = &jffs2_super_operations; - sb->s_flags |= MS_NOATIME; + sb->s_flags = flags | MS_NOATIME; ret = jffs2_do_fill_super(sb, data, (flags&MS_VERBOSE)?1:0); @@ -302,7 +302,7 @@ static int __init init_jffs2_fs(void) jffs2_inode_cachep = kmem_cache_create("jffs2_i", sizeof(struct jffs2_inode_info), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, jffs2_i_init_once, NULL); if (!jffs2_inode_cachep) { printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n"); @@ -330,6 +330,7 @@ static int __init init_jffs2_fs(void) out_compressors: jffs2_compressors_exit(); out: + kmem_cache_destroy(jffs2_inode_cachep); return ret; } --- linux-2.6.9-rc1/fs/jfs/acl.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/fs/jfs/acl.c 2004-09-02 20:51:52.000000000 -0700 @@ -1,7 +1,7 @@ /* - * Copyright (c) International Business Machines Corp., 2002 - * Copyright (c) Andreas Gruenbacher, 2001 - * Copyright (c) Linus Torvalds, 1991, 1992 + * Copyright (C) International Business Machines Corp., 2002-2004 + * Copyright (C) Andreas Gruenbacher, 2001 + * Copyright (C) Linus Torvalds, 1991, 1992 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,6 +20,7 @@ #include #include +#include #include "jfs_incore.h" #include "jfs_xattr.h" #include "jfs_acl.h" @@ -281,6 +282,12 @@ int jfs_setattr(struct dentry *dentry, s if (rc) return rc; + if ((iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid) || + (iattr->ia_valid & ATTR_GID && iattr->ia_gid != inode->i_gid)) { + if (DQUOT_TRANSFER(inode, iattr)) + return -EDQUOT; + } + rc = inode_setattr(inode, iattr); if (!rc && (iattr->ia_valid & ATTR_MODE)) --- linux-2.6.9-rc1/fs/jfs/inode.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/fs/jfs/inode.c 2004-09-02 20:51:52.000000000 -0700 @@ -1,6 +1,6 @@ /* - * Copyright (c) International Business Machines Corp., 2000-2002 - * Portions Copyright (c) Christoph Hellwig, 2001-2002 + * Copyright (C) International Business Machines Corp., 2000-2004 + * Portions Copyright (C) Christoph Hellwig, 2001-2002 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,7 @@ #include #include #include +#include #include "jfs_incore.h" #include "jfs_filsys.h" #include "jfs_imap.h" @@ -134,6 +135,13 @@ void jfs_delete_inode(struct inode *inod diFree(inode); + /* + * Free the inode from the quota allocation. + */ + DQUOT_INIT(inode); + DQUOT_FREE_INODE(inode); + DQUOT_DROP(inode); + clear_inode(inode); } --- linux-2.6.9-rc1/fs/jfs/jfs_dtree.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/fs/jfs/jfs_dtree.c 2004-09-02 20:51:52.000000000 -0700 @@ -101,6 +101,7 @@ */ #include +#include #include "jfs_incore.h" #include "jfs_superblock.h" #include "jfs_filsys.h" @@ -380,7 +381,8 @@ static u32 add_index(tid_t tid, struct i * It's time to move the inline table to an external * page and begin to build the xtree */ - if (dbAlloc(ip, 0, sbi->nbperpage, &xaddr)) + if (DQUOT_ALLOC_BLOCK(ip, sbi->nbperpage) || + dbAlloc(ip, 0, sbi->nbperpage, &xaddr)) goto clean_up; /* No space */ /* @@ -405,7 +407,6 @@ static u32 add_index(tid_t tid, struct i goto clean_up; } ip->i_size = PSIZE; - ip->i_blocks += LBLK2PBLK(sb, sbi->nbperpage); if ((mp = get_index_page(ip, 0)) == 0) { jfs_err("add_index: get_metapage failed!"); @@ -447,7 +448,6 @@ static u32 add_index(tid_t tid, struct i goto clean_up; } ip->i_size += PSIZE; - ip->i_blocks += LBLK2PBLK(sb, sbi->nbperpage); if ((mp = get_index_page(ip, blkno))) memset(mp->data, 0, PSIZE); /* Just looks better */ @@ -946,6 +946,7 @@ static int dtSplitUp(tid_t tid, struct dt_lock *dtlck; struct tlock *tlck; struct lv *lv; + int quota_allocation = 0; /* get split page */ smp = split->mp; @@ -992,7 +993,9 @@ static int dtSplitUp(tid_t tid, split->pxdlist = &pxdlist; rc = dtSplitRoot(tid, ip, split, &rmp); - if (!rc) + if (rc) + dbFree(ip, xaddr, xlen); + else DT_PUTPAGE(rmp); DT_PUTPAGE(smp); @@ -1017,6 +1020,14 @@ static int dtSplitUp(tid_t tid, n = xlen + (xlen << 1); else n = xlen; + + /* Allocate blocks to quota. */ + if (DQUOT_ALLOC_BLOCK(ip, n)) { + rc = -EDQUOT; + goto extendOut; + } + quota_allocation += n; + if ((rc = dbReAlloc(sbi->ipbmap, xaddr, (s64) xlen, (s64) n, &nxaddr))) goto extendOut; @@ -1285,6 +1296,10 @@ static int dtSplitUp(tid_t tid, freeKeyName: kfree(key.name); + /* Rollback quota allocation */ + if (rc && quota_allocation) + DQUOT_FREE_BLOCK(ip, quota_allocation); + dtSplitUp_Exit: return rc; @@ -1305,7 +1320,6 @@ static int dtSplitUp(tid_t tid, static int dtSplitPage(tid_t tid, struct inode *ip, struct dtsplit * split, struct metapage ** rmpp, dtpage_t ** rpp, pxd_t * rpxdp) { - struct super_block *sb = ip->i_sb; int rc = 0; struct metapage *smp; dtpage_t *sp; @@ -1344,6 +1358,12 @@ static int dtSplitPage(tid_t tid, struct if (rmp == NULL) return -EIO; + /* Allocate blocks to quota. */ + if (DQUOT_ALLOC_BLOCK(ip, lengthPXD(pxd))) { + release_metapage(rmp); + return -EDQUOT; + } + jfs_info("dtSplitPage: ip:0x%p smp:0x%p rmp:0x%p", ip, smp, rmp); BT_MARK_DIRTY(rmp, ip); @@ -1593,8 +1613,6 @@ static int dtSplitPage(tid_t tid, struct *rmpp = rmp; *rpxdp = *pxd; - ip->i_blocks += LBLK2PBLK(sb, lengthPXD(pxd)); - return rc; } @@ -1823,16 +1841,6 @@ static int dtExtendPage(tid_t tid, tpxd = (pxd_t *) & pp->slot[1]; *tpxd = *pxd; - /* Since the directory might have an EA and/or ACL associated with it - * we need to make sure we take that into account when setting the - * i_nblocks - */ - ip->i_blocks = LBLK2PBLK(ip->i_sb, xlen + - ((JFS_IP(ip)->ea.flag & DXD_EXTENT) ? - lengthDXD(&JFS_IP(ip)->ea) : 0) + - ((JFS_IP(ip)->acl.flag & DXD_EXTENT) ? - lengthDXD(&JFS_IP(ip)->acl) : 0)); - DT_PUTPAGE(pmp); return 0; } @@ -1900,6 +1908,12 @@ static int dtSplitRoot(tid_t tid, rp = rmp->data; + /* Allocate blocks to quota. */ + if (DQUOT_ALLOC_BLOCK(ip, lengthPXD(pxd))) { + release_metapage(rmp); + return -EDQUOT; + } + BT_MARK_DIRTY(rmp, ip); /* * acquire a transaction lock on the new right page @@ -2042,7 +2056,6 @@ static int dtSplitRoot(tid_t tid, *rmpp = rmp; - ip->i_blocks += LBLK2PBLK(ip->i_sb, lengthPXD(pxd)); return 0; } @@ -2265,7 +2278,9 @@ static int dtDeleteUp(tid_t tid, struct } xlen = lengthPXD(&fp->header.self); - ip->i_blocks -= LBLK2PBLK(ip->i_sb, xlen); + + /* Free quota allocation. */ + DQUOT_FREE_BLOCK(ip, xlen); /* free/invalidate its buffer page */ discard_metapage(fmp); @@ -2339,7 +2354,9 @@ static int dtDeleteUp(tid_t tid, struct } xlen = lengthPXD(&p->header.self); - ip->i_blocks -= LBLK2PBLK(ip->i_sb, xlen); + + /* Free quota allocation */ + DQUOT_FREE_BLOCK(ip, xlen); /* free/invalidate its buffer page */ discard_metapage(mp); @@ -2877,14 +2894,6 @@ void dtInitRoot(tid_t tid, struct inode /* init '..' entry */ p->header.idotdot = cpu_to_le32(idotdot); -#if 0 - ip->i_blocks = LBLK2PBLK(ip->i_sb, - ((jfs_ip->ea.flag & DXD_EXTENT) ? - lengthDXD(&jfs_ip->ea) : 0) + - ((jfs_ip->acl.flag & DXD_EXTENT) ? - lengthDXD(&jfs_ip->acl) : 0)); -#endif - return; } --- linux-2.6.9-rc1/fs/jfs/jfs_extent.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/fs/jfs/jfs_extent.c 2004-09-02 20:51:52.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (C) International Business Machines Corp., 2000-2003 + * Copyright (C) International Business Machines Corp., 2000-2004 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,6 +17,7 @@ */ #include +#include #include "jfs_incore.h" #include "jfs_superblock.h" #include "jfs_dmap.h" @@ -144,6 +145,13 @@ extAlloc(struct inode *ip, s64 xlen, s64 return (rc); } + /* Allocate blocks to quota. */ + if (DQUOT_ALLOC_BLOCK(ip, nxlen)) { + dbFree(ip, nxaddr, (s64) nxlen); + up(&JFS_IP(ip)->commit_sem); + return -EDQUOT; + } + /* determine the value of the extent flag */ xflag = (abnr == TRUE) ? XAD_NOTRECORDED : 0; @@ -161,13 +169,11 @@ extAlloc(struct inode *ip, s64 xlen, s64 */ if (rc) { dbFree(ip, nxaddr, nxlen); + DQUOT_FREE_BLOCK(ip, nxlen); up(&JFS_IP(ip)->commit_sem); return (rc); } - /* update the number of blocks allocated to the file */ - ip->i_blocks += LBLK2PBLK(ip->i_sb, nxlen); - /* set the results of the extent allocation */ XADaddress(xp, nxaddr); XADlength(xp, nxlen); @@ -254,6 +260,13 @@ int extRealloc(struct inode *ip, s64 nxl if ((rc = extBrealloc(ip, xaddr, xlen, &nxlen, &nxaddr))) goto exit; + /* Allocat blocks to quota. */ + if (DQUOT_ALLOC_BLOCK(ip, nxlen)) { + dbFree(ip, nxaddr, (s64) nxlen); + up(&JFS_IP(ip)->commit_sem); + return -EDQUOT; + } + delta = nxlen - xlen; /* check if the extend page is not abnr but the request is abnr @@ -289,6 +302,7 @@ int extRealloc(struct inode *ip, s64 nxl /* extend the extent */ if ((rc = xtExtend(0, ip, xoff + xlen, (int) nextend, 0))) { dbFree(ip, xaddr + xlen, delta); + DQUOT_FREE_BLOCK(ip, nxlen); goto exit; } } else { @@ -299,6 +313,7 @@ int extRealloc(struct inode *ip, s64 nxl */ if ((rc = xtTailgate(0, ip, xoff, (int) ntail, nxaddr, 0))) { dbFree(ip, nxaddr, nxlen); + DQUOT_FREE_BLOCK(ip, nxlen); goto exit; } } @@ -320,9 +335,6 @@ int extRealloc(struct inode *ip, s64 nxl } } - /* update the inode with the number of blocks allocated */ - ip->i_blocks += LBLK2PBLK(sb, delta); - /* set the return results */ XADaddress(xp, nxaddr); XADlength(xp, nxlen); --- linux-2.6.9-rc1/fs/jfs/jfs_imap.c 2004-08-24 00:02:19.000000000 -0700 +++ 25/fs/jfs/jfs_imap.c 2004-09-02 20:51:52.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (C) International Business Machines Corp., 2000-2003 + * Copyright (C) International Business Machines Corp., 2000-2004 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -44,6 +44,7 @@ #include #include #include +#include #include "jfs_incore.h" #include "jfs_filsys.h" @@ -504,6 +505,9 @@ struct inode *diReadSpecial(struct super ip->i_mapping->a_ops = &jfs_aops; mapping_set_gfp_mask(ip->i_mapping, GFP_NOFS); + /* Allocations to metadata inodes should not affect quotas */ + ip->i_flags |= S_NOQUOTA; + if ((inum == FILESYSTEM_I) && (JFS_IP(ip)->ipimap == sbi->ipaimap)) { sbi->gengen = le32_to_cpu(dp->di_gengen); sbi->inostamp = le32_to_cpu(dp->di_inostamp); @@ -2601,28 +2605,11 @@ diNewIAG(struct inomap * imap, int *iagn iagp->inosmap[i] = ONES; flush_metapage(mp); -#ifdef _STILL_TO_PORT - /* synchronously write the iag page */ - if (bmWrite(bp)) { - /* Free the blocks allocated for the iag since it was - * not successfully added to the inode map - */ - dbFree(ipimap, xaddr, (s64) xlen); - - /* release the inode map lock */ - IWRITE_UNLOCK(ipimap); - - rc = -EIO; - goto out; - } - - /* Now the iag is on disk */ /* * start tyransaction of update of the inode map * addressing structure pointing to the new iag page; */ -#endif /* _STILL_TO_PORT */ tid = txBegin(sb, COMMIT_FORCE); down(&JFS_IP(ipimap)->commit_sem); @@ -2644,7 +2631,7 @@ diNewIAG(struct inomap * imap, int *iagn /* update the inode map's inode to reflect the extension */ ipimap->i_size += PSIZE; - ipimap->i_blocks += LBLK2PBLK(sb, xlen); + inode_add_bytes(ipimap, PSIZE); /* * txCommit(COMMIT_FORCE) will synchronously write address @@ -3085,7 +3072,7 @@ static void duplicateIXtree(struct super } /* update the inode map's inode to reflect the extension */ ip->i_size += PSIZE; - ip->i_blocks += LBLK2PBLK(sb, xlen); + inode_add_bytes(ip, PSIZE); txCommit(tid, 1, &ip, COMMIT_FORCE); cleanup: txEnd(tid); --- linux-2.6.9-rc1/fs/jfs/jfs_inode.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/fs/jfs/jfs_inode.c 2004-09-02 20:51:52.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) International Business Machines Corp., 2000-2002 + * Copyright (C) International Business Machines Corp., 2000-2004 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,6 +17,7 @@ */ #include +#include #include "jfs_incore.h" #include "jfs_filsys.h" #include "jfs_imap.h" @@ -60,6 +61,17 @@ struct inode *ialloc(struct inode *paren } else inode->i_gid = current->fsgid; + /* + * Allocate inode to quota. + */ + if (DQUOT_ALLOC_INODE(inode)) { + DQUOT_DROP(inode); + inode->i_flags |= S_NOQUOTA; + inode->i_nlink = 0; + iput(inode); + return NULL; + } + inode->i_mode = mode; if (S_ISDIR(mode)) jfs_inode->mode2 = IDIRECTORY | mode; --- linux-2.6.9-rc1/fs/jfs/jfs_metapage.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/fs/jfs/jfs_metapage.c 2004-09-02 20:51:52.000000000 -0700 @@ -557,6 +557,7 @@ again: if (page) { block_invalidatepage(page, 0); unlock_page(page); + page_cache_release(page); } } } --- linux-2.6.9-rc1/fs/jfs/jfs_mount.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/fs/jfs/jfs_mount.c 2004-09-02 20:51:52.000000000 -0700 @@ -197,9 +197,6 @@ int jfs_mount(struct super_block *sb) /* * unwind on error */ -//errout42: /* close fileset inode allocation map */ - diUnmount(ipimap, 1); - errout41: /* close fileset inode allocation map inode */ diFreeSpecial(ipimap); --- linux-2.6.9-rc1/fs/jfs/jfs_txnmgr.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/fs/jfs/jfs_txnmgr.c 2004-09-02 20:51:52.000000000 -0700 @@ -2621,8 +2621,6 @@ void txAbort(tid_t tid, int dirty) struct tblock *tblk = tid_to_tblock(tid); struct tlock *tlck; - jfs_warn("txAbort: tid:%d dirty:0x%x", tid, dirty); - /* * free tlocks of the transaction */ --- linux-2.6.9-rc1/fs/jfs/jfs_xtree.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/fs/jfs/jfs_xtree.c 2004-09-02 20:51:52.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (C) International Business Machines Corp., 2000-2003 + * Copyright (C) International Business Machines Corp., 2000-2004 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,6 +20,7 @@ */ #include +#include #include "jfs_incore.h" #include "jfs_filsys.h" #include "jfs_metapage.h" @@ -829,8 +830,12 @@ int xtInsert(tid_t tid, /* transaction hint = addressXAD(xad) + lengthXAD(xad) - 1; } else hint = 0; - if ((rc = dbAlloc(ip, hint, (s64) xlen, &xaddr))) + if ((rc = DQUOT_ALLOC_BLOCK(ip, xlen))) goto out; + if ((rc = dbAlloc(ip, hint, (s64) xlen, &xaddr))) { + DQUOT_FREE_BLOCK(ip, xlen); + goto out; + } } /* @@ -855,8 +860,10 @@ int xtInsert(tid_t tid, /* transaction split.pxdlist = NULL; if ((rc = xtSplitUp(tid, ip, &split, &btstack))) { /* undo data extent allocation */ - if (*xaddrp == 0) + if (*xaddrp == 0) { dbFree(ip, xaddr, (s64) xlen); + DQUOT_FREE_BLOCK(ip, xlen); + } return rc; } @@ -1214,22 +1221,34 @@ xtSplitPage(tid_t tid, struct inode *ip, pxd_t *pxd; struct tlock *tlck; struct xtlock *sxtlck = NULL, *rxtlck = NULL; + int quota_allocation = 0; smp = split->mp; sp = XT_PAGE(ip, smp); INCREMENT(xtStat.split); - /* - * allocate the new right page for the split - */ pxdlist = split->pxdlist; pxd = &pxdlist->pxd[pxdlist->npxd]; pxdlist->npxd++; rbn = addressPXD(pxd); + + /* Allocate blocks to quota. */ + if (DQUOT_ALLOC_BLOCK(ip, lengthPXD(pxd))) { + rc = -EDQUOT; + goto clean_up; + } + + quota_allocation += lengthPXD(pxd); + + /* + * allocate the new right page for the split + */ rmp = get_metapage(ip, rbn, PSIZE, 1); - if (rmp == NULL) - return -EIO; + if (rmp == NULL) { + rc = -EIO; + goto clean_up; + } jfs_info("xtSplitPage: ip:0x%p smp:0x%p rmp:0x%p", ip, smp, rmp); @@ -1304,8 +1323,6 @@ xtSplitPage(tid_t tid, struct inode *ip, *rmpp = rmp; *rbnp = rbn; - ip->i_blocks += LBLK2PBLK(ip->i_sb, lengthPXD(pxd)); - jfs_info("xtSplitPage: sp:0x%p rp:0x%p", sp, rp); return 0; } @@ -1321,7 +1338,7 @@ xtSplitPage(tid_t tid, struct inode *ip, XT_GETPAGE(ip, nextbn, mp, PSIZE, p, rc); if (rc) { XT_PUTPAGE(rmp); - return rc; + goto clean_up; } BT_MARK_DIRTY(mp, ip); @@ -1420,10 +1437,16 @@ xtSplitPage(tid_t tid, struct inode *ip, *rmpp = rmp; *rbnp = rbn; - ip->i_blocks += LBLK2PBLK(ip->i_sb, lengthPXD(pxd)); - jfs_info("xtSplitPage: sp:0x%p rp:0x%p", sp, rp); return rc; + + clean_up: + + /* Rollback quota allocation. */ + if (quota_allocation) + DQUOT_FREE_BLOCK(ip, quota_allocation); + + return (rc); } @@ -1478,6 +1501,12 @@ xtSplitRoot(tid_t tid, if (rmp == NULL) return -EIO; + /* Allocate blocks to quota. */ + if (DQUOT_ALLOC_BLOCK(ip, lengthPXD(pxd))) { + release_metapage(rmp); + return -EDQUOT; + } + jfs_info("xtSplitRoot: ip:0x%p rmp:0x%p", ip, rmp); /* @@ -1561,8 +1590,6 @@ xtSplitRoot(tid_t tid, *rmpp = rmp; - ip->i_blocks += LBLK2PBLK(ip->i_sb, lengthPXD(pxd)); - jfs_info("xtSplitRoot: sp:0x%p rp:0x%p", sp, rp); return 0; } @@ -3909,8 +3936,8 @@ s64 xtTruncate(tid_t tid, struct inode * else ip->i_size = newsize; - /* update nblocks to reflect freed blocks */ - ip->i_blocks -= LBLK2PBLK(ip->i_sb, nfreed); + /* update quota allocation to reflect freed blocks */ + DQUOT_FREE_BLOCK(ip, nfreed); /* * free tlock of invalidated pages --- linux-2.6.9-rc1/fs/jfs/namei.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/fs/jfs/namei.c 2004-09-02 20:51:52.000000000 -0700 @@ -19,6 +19,7 @@ #include #include +#include #include "jfs_incore.h" #include "jfs_superblock.h" #include "jfs_inode.h" @@ -123,10 +124,10 @@ static int jfs_create(struct inode *dip, */ ino = ip->i_ino; if ((rc = dtInsert(tid, dip, &dname, &ino, &btstack))) { - jfs_err("jfs_create: dtInsert returned %d", rc); - if (rc == -EIO) + if (rc == -EIO) { + jfs_err("jfs_create: dtInsert returned -EIO"); txAbort(tid, 1); /* Marks Filesystem dirty */ - else + } else txAbort(tid, 0); /* Filesystem full */ goto out3; } @@ -250,11 +251,10 @@ static int jfs_mkdir(struct inode *dip, */ ino = ip->i_ino; if ((rc = dtInsert(tid, dip, &dname, &ino, &btstack))) { - jfs_err("jfs_mkdir: dtInsert returned %d", rc); - - if (rc == -EIO) + if (rc == -EIO) { + jfs_err("jfs_mkdir: dtInsert returned -EIO"); txAbort(tid, 1); /* Marks Filesystem dirty */ - else + } else txAbort(tid, 0); /* Filesystem full */ goto out3; } @@ -330,6 +330,9 @@ static int jfs_rmdir(struct inode *dip, jfs_info("jfs_rmdir: dip:0x%p name:%s", dip, dentry->d_name.name); + /* Init inode for quota operations. */ + DQUOT_INIT(ip); + /* directory must be empty to be removed */ if (!dtEmpty(ip)) { rc = -ENOTEMPTY; @@ -455,6 +458,9 @@ static int jfs_unlink(struct inode *dip, jfs_info("jfs_unlink: dip:0x%p name:%s", dip, dentry->d_name.name); + /* Init inode for quota operations. */ + DQUOT_INIT(ip); + if ((rc = get_UCSname(&dname, dentry))) goto out; @@ -813,7 +819,10 @@ static int jfs_link(struct dentry *old_d iplist[1] = dir; rc = txCommit(tid, 2, &iplist[0], 0); - if (!rc) + if (rc) { + ip->i_nlink--; + iput(ip); + } else d_instantiate(dentry, ip); free_dname: @@ -964,7 +973,7 @@ static int jfs_symlink(struct inode *dip mp = get_metapage(ip, xaddr, PSIZE, 1); if (mp == NULL) { - dbFree(ip, extent, xlen); + xtTruncate(tid, ip, 0, COMMIT_PWMAP); rc = -EIO; txAbort(tid, 0); goto out3; @@ -975,7 +984,6 @@ static int jfs_symlink(struct inode *dip name += copy_size; xaddr += JFS_SBI(sb)->nbperpage; } - ip->i_blocks = LBLK2PBLK(sb, xlen); } /* @@ -988,7 +996,7 @@ static int jfs_symlink(struct inode *dip } if (rc) { if (xlen) - dbFree(ip, extent, xlen); + xtTruncate(tid, ip, 0, COMMIT_PWMAP); txAbort(tid, 0); /* discard new inode */ goto out3; @@ -1104,8 +1112,11 @@ static int jfs_rename(struct inode *old_ rc = -EMLINK; goto out3; } - } else if (new_ip) + } else if (new_ip) { IWRITE_LOCK(new_ip); + /* Init inode for quota operations. */ + DQUOT_INIT(new_ip); + } /* * The real work starts here @@ -1174,8 +1185,8 @@ static int jfs_rename(struct inode *old_ ino = old_ip->i_ino; rc = dtInsert(tid, new_dir, &new_dname, &ino, &btstack); if (rc) { - jfs_err("jfs_rename: dtInsert failed w/rc = %d", - rc); + if (rc == -EIO) + jfs_err("jfs_rename: dtInsert returned -EIO"); goto out4; } if (S_ISDIR(old_ip->i_mode)) --- linux-2.6.9-rc1/fs/jfs/resize.c 2004-08-24 00:03:12.000000000 -0700 +++ 25/fs/jfs/resize.c 2004-09-02 20:51:52.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (C) International Business Machines Corp., 2000-2003 + * Copyright (C) International Business Machines Corp., 2000-2004 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,6 +18,7 @@ #include #include +#include #include "jfs_incore.h" #include "jfs_filsys.h" #include "jfs_metapage.h" @@ -390,7 +391,7 @@ int jfs_extendfs(struct super_block *sb, } /* update bmap file size */ ipbmap->i_size += xlen << sbi->l2bsize; - ipbmap->i_blocks += LBLK2PBLK(sb, xlen); + inode_add_bytes(ipbmap, xlen << sbi->l2bsize); iplist[0] = ipbmap; rc = txCommit(tid, 1, &iplist[0], COMMIT_FORCE); --- linux-2.6.9-rc1/fs/jfs/super.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/fs/jfs/super.c 2004-09-02 20:51:52.000000000 -0700 @@ -58,7 +58,7 @@ DECLARE_COMPLETION(jfsIOwait); #ifdef CONFIG_JFS_DEBUG int jfsloglevel = JFS_LOGLEVEL_WARN; -module_param(jfsloglevel, int, 644); +module_param(jfsloglevel, int, 0644); MODULE_PARM_DESC(jfsloglevel, "Specify JFS loglevel (0, 1 or 2)"); #endif --- linux-2.6.9-rc1/fs/jfs/xattr.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/fs/jfs/xattr.c 2004-09-02 20:51:52.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (C) International Business Machines Corp., 2000-2003 + * Copyright (C) International Business Machines Corp., 2000-2004 * Copyright (C) Christoph Hellwig, 2002 * * This program is free software; you can redistribute it and/or modify @@ -19,6 +19,7 @@ #include #include +#include #include "jfs_incore.h" #include "jfs_superblock.h" #include "jfs_dmap.h" @@ -251,9 +252,17 @@ static int ea_write(struct inode *ip, st /* figure out how many blocks we need */ nblocks = (size + (sb->s_blocksize - 1)) >> sb->s_blocksize_bits; + /* Allocate new blocks to quota. */ + if (DQUOT_ALLOC_BLOCK(ip, nblocks)) { + return -EDQUOT; + } + rc = dbAlloc(ip, INOHINT(ip), nblocks, &blkno); - if (rc) + if (rc) { + /*Rollback quota allocation. */ + DQUOT_FREE_BLOCK(ip, nblocks); return rc; + } /* * Now have nblocks worth of storage to stuff into the FEALIST. @@ -315,6 +324,9 @@ static int ea_write(struct inode *ip, st return 0; failed: + /* Rollback quota allocation. */ + DQUOT_FREE_BLOCK(ip, nblocks); + dbFree(ip, blkno, nblocks); return rc; } @@ -448,6 +460,7 @@ static int ea_get(struct inode *inode, s int blocks_needed, current_blocks; s64 blkno; int rc; + int quota_allocation = 0; /* When fsck.jfs clears a bad ea, it doesn't clear the size */ if (ji->ea.flag == 0) @@ -517,10 +530,16 @@ static int ea_get(struct inode *inode, s sb->s_blocksize_bits; if (blocks_needed > current_blocks) { + /* Allocate new blocks to quota. */ + if (DQUOT_ALLOC_BLOCK(inode, blocks_needed)) + return -EDQUOT; + + quota_allocation = blocks_needed; + rc = dbAlloc(inode, INOHINT(inode), (s64) blocks_needed, &blkno); if (rc) - return rc; + goto clean_up; DXDlength(&ea_buf->new_ea, blocks_needed); DXDaddress(&ea_buf->new_ea, blkno); @@ -534,7 +553,8 @@ static int ea_get(struct inode *inode, s 1); if (ea_buf->mp == NULL) { dbFree(inode, blkno, (s64) blocks_needed); - return -EIO; + rc = -EIO; + goto clean_up; } ea_buf->xattr = ea_buf->mp->data; ea_buf->max_size = (min_size + sb->s_blocksize - 1) & @@ -544,7 +564,7 @@ static int ea_get(struct inode *inode, s if ((rc = ea_read(inode, ea_buf->xattr))) { discard_metapage(ea_buf->mp); dbFree(inode, blkno, (s64) blocks_needed); - return rc; + goto clean_up; } goto size_check; } @@ -552,8 +572,10 @@ static int ea_get(struct inode *inode, s ea_buf->mp = read_metapage(inode, addressDXD(&ji->ea), lengthDXD(&ji->ea) << sb->s_blocksize_bits, 1); - if (ea_buf->mp == NULL) - return -EIO; + if (ea_buf->mp == NULL) { + rc = -EIO; + goto clean_up; + } ea_buf->xattr = ea_buf->mp->data; ea_buf->max_size = (ea_size + sb->s_blocksize - 1) & ~(sb->s_blocksize - 1); @@ -563,10 +585,18 @@ static int ea_get(struct inode *inode, s printk(KERN_ERR "ea_get: invalid extended attribute\n"); dump_mem("xattr", ea_buf->xattr, ea_size); ea_release(inode, ea_buf); - return -EIO; + rc = -EIO; + goto clean_up; } return ea_size; + + clean_up: + /* Rollback quota allocation */ + if (quota_allocation) + DQUOT_FREE_BLOCK(inode, quota_allocation); + + return (rc); } static void ea_release(struct inode *inode, struct ea_buffer *ea_buf) @@ -640,7 +670,10 @@ static int ea_put(struct inode *inode, s ji->ea.size = 0; } - inode->i_blocks += LBLK2PBLK(inode->i_sb, new_blocks - old_blocks); + /* If old blocks exist, they must be removed from quota allocation. */ + if (old_blocks) + DQUOT_FREE_BLOCK(inode, old_blocks); + inode->i_ctime = CURRENT_TIME; rc = txCommit(tid, 1, &inode, 0); txEnd(tid); --- linux-2.6.9-rc1/fs/Kconfig 2004-08-24 00:02:58.000000000 -0700 +++ 25/fs/Kconfig 2004-09-03 00:57:13.348010056 -0700 @@ -160,6 +160,8 @@ config FS_MBCACHE default y if EXT2_FS=y || EXT3_FS=y default m if EXT2_FS=m || EXT3_FS=m +source "fs/Kconfig.reiser4" + config REISERFS_FS tristate "Reiserfs support" help @@ -650,18 +652,21 @@ config FAT_DEFAULT_CODEPAGE default 437 help This option should be set to the codepage of your FAT filesystems. - It can be overridden with the 'codepage' mount option. + It can be overridden with the "codepage" mount option. + See for more information. config FAT_DEFAULT_IOCHARSET string "Default iocharset for FAT" depends on VFAT_FS default "iso8859-1" help - Set this to the default I/O character set you'd like FAT to use. - It should probably match the character set that most of your - FAT filesystems use, and can be overridded with the 'iocharset' - mount option for FAT filesystems. Note that UTF8 is *not* a - supported charset for FAT filesystems. + Set this to the default input/output character set you'd + like FAT to use. It should probably match the character set + that most of your FAT filesystems use, and can be overridden + with the "iocharset" mount option for FAT filesystems. + Note that "utf8" is not recommended for FAT filesystems. + If unsure, you shouldn't set "utf8" here. + See for more information. config UMSDOS_FS #dep_tristate ' UMSDOS: Unix-like file system on top of standard MSDOS fs' CONFIG_UMSDOS_FS $CONFIG_MSDOS_FS @@ -937,6 +942,15 @@ config RAMFS To compile this as a module, choose M here: the module will be called ramfs. +config KEYFS + bool "Key managment database interface filesystem" + depends on KEYS + help + Keyfs is a filesystem that allows access to the keys database that + can be maintained by the kernel. It allows all the operations + userspace might require to be performed using read, write, symlink + and unlink. + endmenu menu "Miscellaneous filesystems" @@ -1213,7 +1227,7 @@ config JFFS2_CMODE_SIZE endchoice config CRAMFS - tristate "Compressed ROM file system support" + tristate "Compressed ROM file system support (cramfs)" select ZLIB_INFLATE help Saying Y here includes support for CramFs (Compressed ROM File --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/Kconfig.reiser4 2004-09-03 00:57:04.019428216 -0700 @@ -0,0 +1,162 @@ +config REISER4_FS + tristate "Reiser4 (EXPERIMENTAL very fast general purpose filesystem)" + depends on EXPERIMENTAL && !4KSTACKS + default y + ---help--- + Reiser4 is more than twice as fast for both reads and writes as + ReiserFS V3, and is the fastest Linux filesystem, by a lot, + for typical IO intensive workloads. [It is slow at fsync + intensive workloads as it is not yet optimized for fsync + (sponsors are welcome for that work), and it is instead + optimized for atomicity, see below.] Benchmarks that define + "a lot" are at http://www.namesys.com/benchmarks.html. + + It is the storage layer of what will become a general purpose naming + system --- like what Microsoft wants WinFS to be except designed with a + clean new semantic layer rather than being SQL based like WinFS. + For details read http://www.namesys.com/whitepaper.html + + It performs all filesystem operations as atomic transactions, which + means that it either performs a write, or it does not, and in the + event of a crash it does not partially perform it or corrupt it. + Many applications that currently use fsync don't need to if they use + reiser4, and that means a lot for performance. An API for performing + multiple file system operations as one high performance atomic write + is almost finished. + + It stores files in dancing trees, which are like balanced trees but + faster. It packs small files together so that they share blocks + without wasting space. This means you can use it to store really + small files. It also means that it saves you disk space. It avoids + hassling you with anachronisms like having a maximum number of + inodes, and wasting space if you use less than that number. + + It can handle really large directories, because its search + algorithms are logarithmic with size not linear. With Reiser4 you + should use subdirectories because they help YOU, not because they + help your filesystem's performance, or because your filesystem won't + be able to shrink a directory once you have let it grow. For squid + and similar applications, everything in one directory should perform + better. + + It has a plugin-based infrastructure, which means that you can easily + invent new kinds of files, and so can other people, so it will evolve + rapidly. + + We will be adding a variety of security features to it that DARPA has + funded us to write. + + "reiser4" is a distinct filesystem mount type from "reiserfs" (V3), + which means that "reiserfs" filesystems will be unaffected by any + reiser4 bugs. + + ReiserFS V3 is the stablest Linux filesystem, and V4 is the fastest. + + In regards to claims by ext2 that they are the de facto + standard Linux filesystem, the most polite thing to say is that + many persons disagree, and it is interesting that those persons + seem to include the distros that are growing in market share. + See http://www.namesys.com/benchmarks.html for why many disagree. + + If you'd like to upgrade from reiserfs to reiser4, use tar to a + temporary disk, maybe using NFS/ssh/SFS to get to that disk, or ask + your favorite distro to sponsor writing a conversion program. + + Sponsored by the Defensed Advanced Research Projects Agency (DARPA) + of the United States Government. DARPA does not endorse this + project, it merely sponsors it. + See http://www.darpa.mil/ato/programs/chats.htm + + If you would like to learn about our plans to add + military grade security to reiser4, please read + http://www.namesys.com/blackbox_security.html. + + To learn more about reiser4, go to http://www.namesys.com + +config REISER4_LARGE_KEY + bool "Use larger keys on reiser4 tree" + depends on REISER4_FS + default y + ---help--- + Make keys larger and use additional bits to order bodies of files within + a directory in the order of their names, which is what you want + normally. If you turn this off, file bodies will be ordered by creation + time, which is not optimal for most users. + + Warning: flipping this option makes your file system binary + incompatible. + +config REISER4_CHECK + bool "Enable reiser4 debug options" + depends on REISER4_FS + ---help--- + Don't use this unless you are a developer debugging reiser4. If + using a kernel made by a distro that thinks they are our competitor + (sigh) rather than made by Linus, always check each release to make + sure they have not turned this on to make us look slow as was done + once in the past. This checks everything imaginable while reiser4 + runs. + + When adding features to reiser4 you should set this, and then + extensively test the code, and then send to us and we will test it + again. Include a description of what you did to test it. All + reiser4 code must be tested, reviewed, and signed off on by two + persons before it will be accepted into a stable kernel by Hans. + +config REISER4_DEBUG + bool "Assertions" + depends on REISER4_CHECK && REISER4_FS!=m + help + Turns on assertions checks. Eats a lot of CPU. + +config REISER4_DEBUG_MODIFY + bool "Dirtying" + depends on REISER4_CHECK + help + Check that node is marked dirty each time it's modified. This is done + through maintaining checksum of node content. CPU hog. + +config REISER4_DEBUG_MEMCPY + bool "Memory copying" + depends on REISER4_CHECK + help + Use special non-inlined versions on memcpy, memset, and memmove in + reiser4 to estimate amount of CPU time spent in data copying. + +config REISER4_DEBUG_NODE + bool "Node consistency" + depends on REISER4_CHECK + help + Run consistency checks on nodes in balanced tree. CPU hog. + +config REISER4_ZERO_NEW_NODE + bool "Node zeroing" + depends on REISER4_CHECK + help + Zero new node before use. + +config REISER4_TRACE + bool "Tracing" + depends on REISER4_CHECK + help + Turn on tracing facility. This enables trace_flags mount option. + +config REISER4_EVENT_LOG + bool "Log events" + depends on REISER4_CHECK + help + Log events into user supplied file. This enables trace_file mount option. + +config REISER4_STATS + bool "Statistics" + depends on REISER4_CHECK + help + Turn on statistics collection. This increases size of in-memory super + block considerably. + +config REISER4_DEBUG_OUTPUT + bool "Printing" + depends on REISER4_CHECK + help + Enable compilation of functions that print internal kernel data + structures in human readable form. Useful for debugging. --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/keyfs/keydir.c 2004-09-03 00:56:55.482725992 -0700 @@ -0,0 +1,547 @@ +/* keydir.c: key representation directory operations + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "keyfs.h" + +/* + * key directory operations + */ +static int keyfs_dir_readdir(struct file *, void *, filldir_t); + +static struct file_operations keyfs_dir_file_operations = { + .readdir = keyfs_dir_readdir, +}; + +static struct dentry *keyfs_dir_lookup(struct inode *, struct dentry *, + struct nameidata *); + +static int keyfs_dir_permission(struct inode *, int, struct nameidata *); + +static int keyfs_dir_getattr(struct vfsmount *, struct dentry *, + struct kstat *); + +static int keyfs_dir_setattr(struct dentry *, struct iattr *); + +static struct inode_operations keyfs_dir_inode_operations = { + .lookup = keyfs_dir_lookup, + .permission = keyfs_dir_permission, + .getattr = keyfs_dir_getattr, + .setattr = keyfs_dir_setattr, +}; + +static int keyfs_dir_d_revalidate(struct dentry *, struct nameidata *); + +struct dentry_operations keyfs_dir_dentry_operations = { + .d_revalidate = keyfs_dir_d_revalidate, + .d_delete = keyfs_d_delete, +}; + +/*****************************************************************************/ +/* + * update the attributes on a key management directory + */ +static void keyfs_dir_update_inode(struct inode *inode) +{ + struct key *key = inode->u.generic_ip; + + /* update the inode from the key */ + down_read(&key->sem); + + inode->i_uid = key->uid; + inode->i_gid = key->gid; + inode->i_mode &= S_IFMT; + + /* we have to provide access to a keys control files if any of + * them can be accessed for any reason */ + inode->i_mode |= S_IRUSR | S_IXUSR; + + if (key->perm & KEY_GRP_ALL) + inode->i_mode |= S_IRGRP | S_IXGRP; + + if (key->perm & KEY_OTH_ALL) + inode->i_mode |= S_IROTH | S_IXOTH; + + up_read(&key->sem); + +} /* end keyfs_dir_update_inode() */ + +/*****************************************************************************/ +/* + * get the directory for a key + */ +struct inode *keyfs_get_keydir(struct super_block *super, key_serial_t id) +{ + struct inode *inode; + struct key *key; + + /* the key must exist, and we must have at least one permission on it + * to be able to access it */ + key = key_lookup(id); + if (IS_ERR(key)) { + inode = ERR_PTR(PTR_ERR(key)); + goto error; + } + + if (!key_any_permission(key, KEY_ALL)) { + /* we pretend a key doesn't exist if a process is not allowed + * to access it */ + key_put(key); + inode = ERR_PTR(-ENOENT); + goto error; + } + + /* get the key directory inode */ + inode = iget_locked(super, (id << 16) + KEYFS_INO_K_DIR); + if (inode && inode->i_state & I_NEW) { + /* initialise it */ + inode->i_mtime = inode->i_atime = inode->i_ctime = + CURRENT_TIME; + inode->i_blocks = 0; + inode->i_blksize = 1024; + inode->i_uid = key->uid; + inode->i_gid = key->gid; + inode->i_mode = S_IFDIR; + inode->i_op = &keyfs_dir_inode_operations; + inode->i_fop = &keyfs_dir_file_operations; + inode->i_nlink = 2; + inode->u.generic_ip = key; + + /* set the ownership and permissions from the key */ + keyfs_dir_update_inode(inode); + + /* success */ + unlock_new_inode(inode); + } + else if (inode) { + /* exists */ + key_put(key); + } + else { + /* error */ + inode = ERR_PTR(-ENOMEM); + key_put(key); + } + + error: + return inode; + +} /* end keyfs_get_keydir() */ + +/*****************************************************************************/ +/* + * update the attributes on a key management directory during pathwalk + */ +static int keyfs_dir_d_revalidate(struct dentry *dentry, struct nameidata *nd) +{ + keyfs_dir_update_inode(dentry->d_inode); + return 1; + +} /* end keyfs_dir_d_revalidate() */ + +/*****************************************************************************/ +/* + * read the attributes of an inode, updating from the key + */ +static int keyfs_dir_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) +{ + /* update the inode */ + keyfs_dir_update_inode(dentry->d_inode); + + /* transfer attributes from the inode structure to the stat + * structure */ + generic_fillattr(dentry->d_inode, stat); + + return 0; + +} /* end keyfs_dir_getattr() */ + +/*****************************************************************************/ +/* + * enumerate the keys in a key directory + */ +static int keyfs_dir_readdir(struct file *file, void *cookie, filldir_t filldir) +{ + struct key *key; + ino_t ino; + int ret; + + key = file->f_dentry->d_inode->u.generic_ip; + ino = file->f_dentry->d_inode->i_ino & ~0xffffULL; + + /* read the usual "." and ".." first followed by the key control + * files */ + switch ((int)file->f_pos) { + case 0: + ret = filldir(cookie, ".", 1, file->f_pos, + file->f_dentry->d_inode->i_ino, DT_DIR); + if (ret < 0) + goto done; + file->f_pos++; + + case 1: + ret = filldir(cookie, "..", 2, file->f_pos, + parent_ino(file->f_dentry), DT_DIR); + if (ret < 0) + goto done; + file->f_pos++; + + case 2: + ret = filldir(cookie, "type", 4, file->f_pos, + ino | KEYFS_INO_K_TYPE, DT_REG); + if (ret < 0) + goto done; + file->f_pos++; + + case 3: + ret = filldir(cookie, "description", 11, file->f_pos, + ino | KEYFS_INO_K_DESC, DT_REG); + if (ret < 0) + goto done; + file->f_pos++; + + case 4: + ret = filldir(cookie, "expiry", 6, file->f_pos, + ino | KEYFS_INO_K_EXPIRY, DT_REG); + if (ret < 0) + goto done; + file->f_pos++; + + case 5: + ret = filldir(cookie, "perm", 4, file->f_pos, + ino | KEYFS_INO_K_PERM, DT_REG); + if (ret < 0) + goto done; + file->f_pos++; + + case 6: + ret = filldir(cookie, "revoke", 6, file->f_pos, + ino | KEYFS_INO_K_REVOKE, DT_REG); + if (ret < 0) + goto done; + file->f_pos++; + + case 7: + if (!(key->flags & KEY_FLAG_INSTANTIATED)) { + ret = filldir(cookie, "instantiate", 11, file->f_pos, + ino | KEYFS_INO_K_INSTANTIATE, DT_REG); + if (ret < 0) + goto done; + } + file->f_pos++; + + case 8: + if (!(key->flags & KEY_FLAG_INSTANTIATED)) { + ret = filldir(cookie, "negate", 6, file->f_pos, + ino | KEYFS_INO_K_NEGATE, DT_REG); + if (ret < 0) + goto done; + } + file->f_pos++; + + case 9: + if (key->flags & KEY_FLAG_INSTANTIATED) { + if (key->type == &key_type_keyring) { + ret = filldir(cookie, "search", 6, file->f_pos, + ino | KEYFS_INO_K_NEGATE, DT_REG); + if (ret < 0) + goto done; + } + } + file->f_pos++; + + case 10: + ret = filldir(cookie, "usage", 5, file->f_pos, + ino | KEYFS_INO_K_USAGE, DT_REG); + if (ret < 0) + goto done; + file->f_pos++; + + case 11: + ret = filldir(cookie, "flags", 5, file->f_pos, + ino | KEYFS_INO_K_USAGE, DT_REG); + if (ret < 0) + goto done; + file->f_pos++; + + case 12: + if (key->flags & KEY_FLAG_INSTANTIATED) { + ret = filldir(cookie, "add", 3, file->f_pos, + ino | KEYFS_INO_K_ADD, DT_REG); + if (ret < 0) + goto done; + } + file->f_pos++; + + case 13: + if (key->flags & KEY_FLAG_INSTANTIATED) { + if (key->type == &key_type_keyring) + ret = filldir(cookie, "keyring", 7, + file->f_pos, + ino | KEYFS_INO_K_PAYLOAD, + DT_DIR); + else + ret = filldir(cookie, "payload", 7, + file->f_pos, + ino | KEYFS_INO_K_PAYLOAD, + DT_REG); + if (ret < 0) + goto done; + } + file->f_pos++; + + default: + break; + } + + done: + return 0; + +} /* end keyfs_dir_readdir() */ + +/*****************************************************************************/ +/* + * look up an inode in a key directory + */ +static struct dentry *keyfs_dir_lookup(struct inode *dir, + struct dentry *dentry, + struct nameidata *nd) +{ + struct dentry *ret; + struct inode *target; + struct key *key; + const char *name; + + key = dir->u.generic_ip; + + /* determine which virtual file they want */ + name = dentry->d_name.name; + + switch (dentry->d_name.len) { + case 1: + if (memcmp(name, ".", 1) == 0) { + target = igrab(dir); + goto instantiate; + } + break; + + case 2: + if (memcmp(name, "..", 2)==0) { + target = igrab(dir); + goto instantiate; + } + break; + + case 3: + if (memcmp(name, "add", 3) == 0) { + target = keyfs_get_keyfile(dir, KEYFS_INO_K_ADD); + goto instantiate; + } + break; + + case 4: + if (memcmp(name, "type", 4) == 0) { + target = keyfs_get_keyfile(dir, KEYFS_INO_K_TYPE); + goto instantiate; + } + if (memcmp(name, "perm", 4) == 0) { + target = keyfs_get_keyfile(dir, KEYFS_INO_K_PERM); + goto instantiate; + } + break; + + case 5: + if (memcmp(name, "usage", 5) == 0) { + target = keyfs_get_keyfile(dir, KEYFS_INO_K_USAGE); + goto instantiate; + } + if (memcmp(name, "flags", 5) == 0) { + target = keyfs_get_keyfile(dir, KEYFS_INO_K_FLAGS); + goto instantiate; + } + break; + + case 6: + if (memcmp(name, "expiry", 6) == 0) { + target = keyfs_get_keyfile(dir, KEYFS_INO_K_EXPIRY); + goto instantiate; + } + if (memcmp(name, "revoke", 6) == 0) { + target = keyfs_get_keyfile(dir, KEYFS_INO_K_REVOKE); + goto instantiate; + } + if (memcmp(name, "negate", 6) == 0) { + target = keyfs_get_keyfile(dir, KEYFS_INO_K_NEGATE); + goto instantiate; + } + if (key->type != &key_type_keyring) + break; + if (memcmp(name, "search", 6) == 0) { + target = keyfs_get_keyfile(dir, KEYFS_INO_K_SEARCH); + goto instantiate; + } + break; + + case 7: + if (key->type == &key_type_keyring) { + if (memcmp(name, "keyring", 7) == 0) { + target = keyfs_get_keyfile( + dir, + KEYFS_INO_K_PAYLOAD); + goto instantiate; + } + } + else { + if (memcmp(name, "payload", 7) == 0) { + target = keyfs_get_keyfile( + dir, + KEYFS_INO_K_PAYLOAD); + goto instantiate; + } + } + break; + + case 11: + if (memcmp(name, "description", 11) == 0) { + target = keyfs_get_keyfile(dir, KEYFS_INO_K_DESC); + goto instantiate; + } + if (memcmp(name, "instantiate", 11) == 0) { + target = keyfs_get_keyfile(dir, + KEYFS_INO_K_INSTANTIATE); + goto instantiate; + } + break; + + default: + break; + } + + /* make a negative dentry */ + d_add(dentry, NULL); + ret = NULL; + goto out; + + /* instantiate the dentry */ + instantiate: + if (IS_ERR(target)) { + ret = ERR_PTR(PTR_ERR(target)); + goto out; + } + + dentry->d_op = &keyfs_file_dentry_operations; + d_add(dentry, target); + ret = NULL; + + out: + return ret; + +} /* end keyfs_dir_lookup() */ + +/*****************************************************************************/ +/* + * get permission for an operation on a directory + */ +static int keyfs_dir_permission(struct inode *inode, int mask, + struct nameidata *nd) +{ + struct key *key; + int ret; + + key = inode->u.generic_ip; + + /* we pretend a key doesn't exist if a process is not allowed to access + * it */ + ret = -ENOENT; + if (!key_any_permission(key, KEY_ALL)) + goto error; + + /* check the VFS permissions too */ + ret = vfs_permission(inode, mask); + error: + return ret; + +} /* end keyfs_dir_permission() */ + +/*****************************************************************************/ +/* + * permit the key's ownership to be changed + */ +static int keyfs_dir_setattr(struct dentry *dentry, struct iattr *iattr) +{ + struct inode *inode; + unsigned int ia_valid = iattr->ia_valid; + struct key *key; + int ret; + + inode = dentry->d_inode; + key = inode->u.generic_ip; + + ret = -EINVAL; + if (ia_valid & (ATTR_SIZE | ATTR_MODE)) + goto error; + + /* make the changes with the locks held to prevent chown/chown races */ + ret = -EACCES; + down_write(&key->sem); + write_lock(&key->lock); + + if (!capable(CAP_SYS_ADMIN)) { + /* only the sysadmin can chown a key to some other UID */ + if (ia_valid & ATTR_UID && key->uid != iattr->ia_uid) + goto no_access; + + /* only the sysadmin can set the key's GID to a group other + * than one of those that the current process subscribes to */ + if (ia_valid & ATTR_GID && + iattr->ia_gid != key->gid && + !in_group_p(iattr->ia_gid)) + goto no_access; + } + + /* change the UID (have to update the quotas) */ + if (ia_valid & ATTR_UID && iattr->ia_uid != key->uid) { + /* don't support UID changing yet */ + ret = -EOPNOTSUPP; + goto no_access; + } + + /* change the GID */ + if (ia_valid & ATTR_GID) { + inode->i_gid = iattr->ia_gid; + key->gid = iattr->ia_gid; + } + + ret = 0; + + if (ia_valid & ATTR_ATIME) + inode->i_atime = iattr->ia_atime; + if (ia_valid & ATTR_MTIME) + inode->i_mtime = iattr->ia_mtime; + if (ia_valid & ATTR_CTIME) + inode->i_ctime = iattr->ia_ctime; + + no_access: + write_unlock(&key->lock); + up_write(&key->sem); + + error: + return ret; + +} /* end keyfs_dir_setattr() */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/keyfs/keyfile.c 2004-09-03 00:56:55.290755176 -0700 @@ -0,0 +1,874 @@ +/* keyfile.c: keyring management files + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "keyfs.h" + +/* + * miscellaneous key file operations + */ +static ssize_t keyfs_file_read(struct file *, char __user *, size_t, loff_t *); +static ssize_t keyfs_file_write(struct file *, const char __user *, size_t, + loff_t *); + +static struct file_operations keyfs_file_file_operations = { + .read = keyfs_file_read, + .write = keyfs_file_write, + .llseek = no_llseek, +}; + +static struct inode_operations keyfs_file_inode_operations = { + .getattr = keyfs_file_getattr, +}; + +static int keyfs_file_d_revalidate(struct dentry *, struct nameidata *); + +struct dentry_operations keyfs_file_dentry_operations = { + .d_revalidate = keyfs_file_d_revalidate, + .d_delete = keyfs_d_delete, +}; + +/* + * key search file operation + */ +static ssize_t keyfs_search_file_write(struct file *, const char __user *, + size_t, loff_t *); + +static struct file_operations keyfs_search_file_file_operations = { + .open = keyfs_bifile_open, + .release = keyfs_bifile_release, + .read = keyfs_bifile_read, + .write = keyfs_search_file_write, + .llseek = no_llseek, +}; + +/* + * add/update key operation + */ +static ssize_t keyfs_add_file_write(struct file *, const char __user *, size_t, + loff_t *); + +static struct file_operations keyfs_add_file_file_operations = { + .open = keyfs_bifile_open, + .release = keyfs_bifile_release, + .read = keyfs_bifile_read, + .write = keyfs_add_file_write, + .llseek = no_llseek, +}; + +/*****************************************************************************/ +/* + * update the attributes on a key management file + */ +static void keyfs_file_update_inode(struct inode *inode) +{ + struct key *key = inode->u.generic_ip; + + /* update the inode from the key */ + down_read(&key->sem); + + inode->i_uid = key->uid; + inode->i_gid = key->gid; + inode->i_mode &= S_IFMT; + + /* we have to provide access to key management files in various ways + * depending on various factors */ + switch (inode->i_ino & 0xffff) { + case KEYFS_INO_K_PAYLOAD: + /* the payload is a file if it's an ordinary key and a + * directory if it's a keyring, so we need to adjust + * appropriately */ + if (key->perm & KEY_USR_READ) + inode->i_mode |= S_IRUSR; + + if (key->perm & KEY_USR_WRITE) + inode->i_mode |= S_IWUSR; + + if (key->perm & KEY_GRP_READ) + inode->i_mode |= S_IRGRP; + + if (key->perm & KEY_GRP_WRITE) + inode->i_mode |= S_IWGRP; + + if (key->perm & KEY_OTH_READ) + inode->i_mode |= S_IROTH; + + if (key->perm & KEY_OTH_WRITE) + inode->i_mode |= S_IWOTH; + + if (key->type == &key_type_keyring) { + /* copy R->X bits on a keyring */ + inode->i_mode |= (inode->i_mode & S_IRUGO) >> 2; + } + else { + /* turn off write on the payload if they can't + * write to it */ + if (key->flags & KEY_FLAG_INSTANTIATED && + !key->type->update) + inode->i_mode &= ~S_IWUGO; + + /* turn off read if they can't read it */ + if (!key->type->read) + inode->i_mode &= ~S_IRUGO; + } + break; + + case KEYFS_INO_K_SEARCH: + if (key->perm & KEY_USR_SEARCH) + inode->i_mode |= S_IRUSR | S_IWUSR; + if (key->perm & KEY_GRP_SEARCH) + inode->i_mode |= S_IRGRP | S_IWGRP; + if (key->perm & KEY_OTH_SEARCH) + inode->i_mode |= S_IROTH | S_IWOTH; + break; + + case KEYFS_INO_K_ADD: + if (key->perm & KEY_USR_WRITE) + inode->i_mode |= S_IRUSR | S_IWUSR; + if (key->perm & KEY_GRP_WRITE) + inode->i_mode |= S_IRGRP | S_IWGRP; + if (key->perm & KEY_OTH_WRITE) + inode->i_mode |= S_IROTH | S_IWOTH; + break; + + case KEYFS_INO_K_REVOKE: + case KEYFS_INO_K_INSTANTIATE: + case KEYFS_INO_K_NEGATE: + /* only the owner can revoke or instantiate the key */ + inode->i_mode |= S_IWUSR; + break; + + case KEYFS_INO_K_PERM: + /* only the owner can change the mode */ + inode->i_mode |= S_IWUSR | S_IRUGO; + break; + + default: + if (key->perm & KEY_USR_VIEW) + inode->i_mode |= S_IRUSR; + if (key->perm & KEY_GRP_VIEW) + inode->i_mode |= S_IRGRP; + if (key->perm & KEY_OTH_VIEW) + inode->i_mode |= S_IROTH; + break; + } + + up_read(&key->sem); + +} /* end keyfs_file_update_inode() */ + +/*****************************************************************************/ +/* + * get a key control/access file + */ +struct inode *keyfs_get_keyfile(struct inode *dir, ino_t ino) +{ + struct inode *inode; + struct key *key; + + key = dir->u.generic_ip; + + /* get the key file inode */ + inode = iget_locked(dir->i_sb, (dir->i_ino & ~(ino_t)0xffffU) | ino); + if (inode && inode->i_state & I_NEW) { + atomic_inc(&key->usage); + inode->u.generic_ip = key; + + /* initialise it */ + inode->i_mtime = inode->i_atime = inode->i_ctime = + CURRENT_TIME; + inode->i_blocks = 0; + inode->i_blksize = 1024; + inode->i_mode = S_IFREG; + inode->i_nlink = 1; + inode->i_op = &keyfs_file_inode_operations; + inode->i_fop = &keyfs_file_file_operations; + + if (key->type == &key_type_keyring) { + switch (inode->i_ino & 0xffff) { + case KEYFS_INO_K_PAYLOAD: + inode->i_mode = S_IFDIR; + inode->i_nlink = 2; + inode->i_op = &keyfs_ring_inode_operations; + inode->i_fop = &keyfs_ring_file_operations; + break; + + case KEYFS_INO_K_SEARCH: + inode->i_fop = + &keyfs_search_file_file_operations; + break; + + case KEYFS_INO_K_ADD: + inode->i_fop = &keyfs_add_file_file_operations; + break; + } + } + + /* transfer the ownership and permissions */ + keyfs_file_update_inode(inode); + + /* success */ + unlock_new_inode(inode); + } + + return inode; + +} /* end keyfs_get_keyfile() */ + +/*****************************************************************************/ +/* + * update the attributes on a key management file during pathwalk + */ +static int keyfs_file_d_revalidate(struct dentry *dentry, struct nameidata *nd) +{ + keyfs_file_update_inode(dentry->d_inode); + return 1; + +} /* end keyfs_file_d_revalidate() */ + +/*****************************************************************************/ +/* + * read the attributes of an inode, updating from the key + */ +int keyfs_file_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) +{ + keyfs_file_update_inode(dentry->d_inode); + + /* transfer attributes from the inode structure to the stat + * structure */ + generic_fillattr(dentry->d_inode, stat); + + return 0; + +} /* end keyfs_file_getattr() */ + +/*****************************************************************************/ +/* + * read the data from a key file + */ +static ssize_t keyfs_file_read(struct file *file, char __user *_buffer, + size_t buflen, loff_t *fpos) +{ + struct key *key; + const char *data; + char buffer[30]; + ssize_t ret; + size_t len; + + ret = 0; + if (*fpos > 0) + goto error; + + key = file->f_dentry->d_inode->u.generic_ip; + + switch (file->f_dentry->d_inode->i_ino & 0xffff) { + case KEYFS_INO_K_TYPE: + data = key->type->name; + goto copyout; + + case KEYFS_INO_K_DESC: + data = key->description; + goto copyout; + + case KEYFS_INO_K_EXPIRY: + sprintf(buffer, "%lu", key->expiry); + data = buffer; + goto copyout; + + case KEYFS_INO_K_PERM: + sprintf(buffer, "0x%06x", key->perm); + data = buffer; + goto copyout; + + case KEYFS_INO_K_REVOKE: + case KEYFS_INO_K_NEGATE: + ret = -EOPNOTSUPP; + goto error; + + case KEYFS_INO_K_USAGE: + sprintf(buffer, "%d", atomic_read(&key->usage)); + data = buffer; + goto copyout; + + case KEYFS_INO_K_FLAGS: + sprintf(buffer, "%08x", key->flags); + data = buffer; + goto copyout; + + case KEYFS_INO_K_PAYLOAD: + ret = -EOPNOTSUPP; + if (!key->type->read) + goto error; + + /* read the data with the semaphore held (since we + * might sleep) */ + down_read(&key->sem); + ret = key->type->read(key, _buffer, buflen); + up_read(&key->sem); + + if (ret > 0) { + if (ret > buflen) + ret = buflen; + *fpos += ret; + } + goto error; + + default: + BUG(); + } + + error: + return ret; + + /* if the return is a simple string constructed on the spot, copy that + * to userspace */ + copyout: + len = strlen(data); + if (len > buflen) + len = buflen; + + ret = -EFAULT; + if (copy_to_user(_buffer, data, len) == 0) { + if (len < buflen && put_user('\n', _buffer + len) == 0) + ret = len + 1; + else + ret = len; + } + + *fpos += ret; + goto error; + +} /* end keyfs_file_read() */ + +/*****************************************************************************/ +/* + * write command to change a key's permissions + * - format: [NL] + */ +static ssize_t keyfs_perm_write(struct key *key, + const char __user *_buffer, + size_t buflen) +{ + key_perm_t perm; + ssize_t ret; + char buffer[16], *p; + + ret = -EINVAL; + if (buflen > sizeof(buffer) - 1) + goto error; + + ret = -EFAULT; + if (copy_from_user(buffer, _buffer, buflen) != 0) + goto error; + buffer[buflen] = 0; + + ret = -EINVAL; + perm = simple_strtoul(buffer, &p, 0); + if (*p == '\n') + p++; + if (*p) + goto error; + + /* check the process's owner owns it */ + ret = -EPERM; + if (current->fsuid != key->uid) + goto error; + + /* make the changes with the locks held to prevent chown/chmod races */ + down_write(&key->sem); + write_lock(&key->lock); + key->perm = perm; + write_unlock(&key->lock); + up_write(&key->sem); + + ret = buflen; + + error: + return ret; + +} /* end keyfs_perm_write() */ + +/*****************************************************************************/ +/* + * instantiate a key + * - format: []NL[] + */ +static ssize_t keyfs_instantiate_write(struct key *key, + const char __user *_buffer, + size_t buflen) +{ + key_serial_t ringid; + struct key *keyring; + ssize_t ret; + size_t plen; + char *buffer, *bend, *p, *ring, *payload; + + ret = -EINVAL; + if (buflen > 40000) + goto error; + + /* fetch the data into kernel memory */ + ret = -ENOMEM; + buffer = kmalloc(buflen + 1, GFP_KERNEL); + if (!buffer) + goto error; + + ret = -EFAULT; + if (copy_from_user(buffer, _buffer, buflen) != 0) + goto error2; + bend = buffer + buflen; + *bend = 0; + + /* parse the string */ + ret = -EINVAL; + + ring = buffer; + + p = memchr(buffer, '\n', bend - buffer); + if (!p) + goto error2; + *p++ = 0; + payload = p; + plen = bend - payload; + if (plen == 0) + payload = NULL; + + /* if supplied, turn the ring ID into a keyring */ + keyring = NULL; + + if (*ring) { + ringid = simple_strtoul(ring, &p, 0); + if (*p) + goto error2; + + /* find the target keyring (which must be writable) */ + keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto error2; + } + } + + /* instantiate the key, potentially attaching it to the target + * keyring */ + ret = key_instantiate_and_link(key, payload, plen, keyring); + if (ret < 0) { + ret = PTR_ERR(key); + goto error3; + } + + ret = buflen; + + error3: + key_put(keyring); + error2: + kfree(buffer); + error: + return ret; + +} /* end keyfs_instantiate_write() */ + +/*****************************************************************************/ +/* + * negatively instantiate a key + * - format: []NL[]NL + */ +static ssize_t keyfs_negate_write(struct key *key, + const char __user *_buffer, + size_t buflen) +{ + key_serial_t ringid; + struct key *keyring; + unsigned timo; + ssize_t ret; + char *buffer, *bend, *p, *ring, *timeout; + + ret = -EINVAL; + if (buflen > 100) + goto error; + + /* fetch the data into kernel memory */ + ret = -ENOMEM; + buffer = kmalloc(buflen + 1, GFP_KERNEL); + if (!buffer) + goto error; + + ret = -EFAULT; + if (copy_from_user(buffer, _buffer, buflen) != 0) + goto error2; + bend = buffer + buflen; + *bend = 0; + + /* parse the string */ + ret = -EINVAL; + + ring = buffer; + + p = memchr(buffer, '\n', bend - buffer); + if (!p) + goto error2; + *p++ = 0; + timeout = p; + + p = memchr(p, '\n', bend - buffer); + if (!p) + goto error2; + *p++ = 0; + + if (p != bend) + goto error2; + + /* if supplied, turn the ring ID into a keyring */ + keyring = NULL; + + if (*ring) { + ringid = simple_strtoul(ring, &p, 0); + if (*p) + goto error2; + + /* find the target keyring (which must be writable) */ + keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto error2; + } + } + + /* turn the timeout into number */ + timo = key_negative_timeout; + + if (*timeout) { + timo = simple_strtoul(ring, &p, 0); + if (*p) + goto error3; + } + + /* negatively instantiate the key, potentially attaching to + * the target keyring */ + ret = key_negate_and_link(key, timo, keyring); + if (ret < 0) { + ret = PTR_ERR(key); + goto error3; + } + + ret = buflen; + + error3: + key_put(keyring); + error2: + kfree(buffer); + error: + return ret; + +} /* end keyfs_negate_write() */ + +/*****************************************************************************/ +/* + * update the payload + */ +static ssize_t keyfs_payload_write(struct key *key, + const char __user *_buffer, + size_t buflen) +{ + char *buffer; + ssize_t ret; + + ret = -EINVAL; + if (buflen == 0 || buflen > 32767) + goto error; + + /* must have write permission on the key */ + ret = -EACCES; + if (!key_permission(key, KEY_WRITE)) + goto error; + + /* the key must be instantiated and updatable */ + ret = -EPERM; + if (!(key->flags & KEY_FLAG_INSTANTIATED) || + !key->type->update) + goto error; + + /* fetch the data into kernel space */ + ret = -ENOMEM; + buffer = kmalloc(buflen, GFP_KERNEL); + if (!buffer) + goto error; + + ret = -EFAULT; + if (copy_from_user(buffer, _buffer, buflen) == 0) { + /* perform the update */ + ret = key_update(key, buffer, buflen); + if (ret == 0) + ret = buflen; + } + + kfree(buffer); + + error: + return ret; + +} /* end keyfs_payload_write() */ + +/*****************************************************************************/ +/* + * allow userspace to control keys by writing to certain files + */ +static ssize_t keyfs_file_write(struct file *file, const char __user *_buffer, + size_t buflen, loff_t *fpos) +{ + struct key *key; + ssize_t ret; + + key = file->f_dentry->d_inode->u.generic_ip; + + switch (file->f_dentry->d_inode->i_ino & 0xffff) { + case KEYFS_INO_K_PERM: + ret = keyfs_perm_write(key, _buffer, buflen); + break; + + case KEYFS_INO_K_REVOKE: + ret = -EACCES; + if (!key_permission(key, KEY_WRITE)) + goto error; + + key_revoke(key); + ret = 0; + goto error; + + case KEYFS_INO_K_INSTANTIATE: + ret = keyfs_instantiate_write(key, _buffer, buflen); + goto error; + + case KEYFS_INO_K_NEGATE: + ret = keyfs_negate_write(key, _buffer, buflen); + goto error; + + case KEYFS_INO_K_PAYLOAD: + ret = keyfs_payload_write(key, _buffer, buflen); + break; + + default: + ret = -EPERM; + break; + } + + error: + return ret; + +} /* end keyfs_file_write() */ + +/*****************************************************************************/ +/* + * search for a key in a keyring + * - format: NLNL[]NL + */ +static ssize_t keyfs_search_file_write(struct file *file, + const char __user *_buffer, + size_t buflen, loff_t *fpos) +{ + struct keyfs_bifile_record *rec = file->private_data; + struct key_type *ktype; + key_serial_t ringid; + struct key *key, *keyring, *dest; + ssize_t ret; + char *buffer, *bend, *p, *type, *desc, *ring; + + keyring = file->f_dentry->d_inode->u.generic_ip; + + ret = -EINVAL; + if (rec->state != KEYFS_BIFILE_WANT_PARAMS || buflen > 40000) + goto error; + + rec->state = KEYFS_BIFILE_GOT_RESULT; + + /* fetch the data into kernel memory */ + ret = -ENOMEM; + buffer = kmalloc(buflen + 1, GFP_KERNEL); + if (!buffer) + goto error; + + ret = -EFAULT; + if (copy_from_user(buffer, _buffer, buflen) != 0) + goto error2; + bend = buffer + buflen; + *bend = 0; + + /* parse the parameter string */ + ret = -EINVAL; + + type = buffer; + + p = memchr(buffer, '\n', bend - buffer); + if (!p) + goto error2; + *p++ = 0; + desc = p; + + p = memchr(p, '\n', bend - p); + if (!p) + goto error2; + *p++ = 0; + ring = p; + + p = memchr(p, '\n', bend - p); + if (!p) + goto error2; + *p++ = 0; + + if (p != bend) + goto error2; + + /* if supplied, turn the destination ring ID into a keyring */ + dest = NULL; + + if (*ring) { + ringid = simple_strtoul(ring, &p, 0); + if (*p) + goto error2; + + /* find the target keyring (which must be writable) */ + dest = lookup_user_key(ringid, 1, 0, KEY_WRITE); + if (IS_ERR(dest)) { + ret = PTR_ERR(dest); + goto error2; + } + } + + /* find the key type */ + ktype = key_type_lookup(type); + if (IS_ERR(ktype)) { + ret = PTR_ERR(ktype); + goto error3; + } + + /* search for a key */ + key = keyring_search(keyring, ktype, desc); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error4; + } + + /* link the resulting key to the destination keyring if we can */ + if (dest) { + ret = -EACCES; + if (!key_permission(key, KEY_LINK)) + goto error5; + + ret = key_link(dest, key); + if (ret < 0) + goto error5; + } + + /* we'll be returning the key ID on the next read */ + rec->id = key->serial; + + ret = buflen; + + error5: + key_put(key); + error4: + key_type_put(ktype); + error3: + key_put(dest); + error2: + kfree(buffer); + error: + if (ret < 0) + rec->error = ret; + return ret; + +} /* end keyfs_search_file_write() */ + +/*****************************************************************************/ +/* + * create a key + * - format: NLNL[] + */ +static ssize_t keyfs_add_file_write(struct file *file, + const char __user *_buffer, + size_t buflen, loff_t *fpos) +{ + struct keyfs_bifile_record *rec = file->private_data; + struct key *keyring, *key; + ssize_t ret; + size_t plen; + char *buffer, *bend, *p, *type, *desc, *payload; + + keyring = file->f_dentry->d_inode->u.generic_ip; + + ret = -EINVAL; + if (rec->state != KEYFS_BIFILE_WANT_PARAMS || buflen > 40000) + goto error; + + rec->state = KEYFS_BIFILE_GOT_RESULT; + + /* fetch the data into kernel memory */ + ret = -ENOMEM; + buffer = kmalloc(buflen + 1, GFP_KERNEL); + if (!buffer) + goto error; + + ret = -EFAULT; + if (copy_from_user(buffer, _buffer, buflen) != 0) + goto error2; + bend = buffer + buflen; + *bend = 0; + + /* parse the parameter string */ + ret = -EINVAL; + + type = buffer; + + p = memchr(buffer, '\n', bend - buffer); + if (!p) + goto error2; + *p++ = 0; + desc = p; + + p = memchr(p, '\n', bend - p); + if (!p) + goto error2; + *p++ = 0; + payload = p; + plen = bend - payload; + if (plen == 0) + payload = NULL; + + /* create or update the requested key and add it to the target + * keyring */ + key = key_create_or_update(keyring, type, desc, payload, plen, 0); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error2; + } + + /* we'll be returning the key ID on the next read */ + rec->id = key->serial; + key_put(key); + + ret = buflen; + + error2: + kfree(buffer); + error: + if (ret < 0) + rec->error = ret; + return ret; + +} /* end keyfs_add_file_write() */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/keyfs/keyfs.h 2004-09-03 00:56:55.290755176 -0700 @@ -0,0 +1,86 @@ +/* keyfs.h: keyfs defines + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include + +/* special inodes */ +#define KEYFS_INO_ROOT 1 +#define KEYFS_INO_THREAD 2 +#define KEYFS_INO_PROCESS 3 +#define KEYFS_INO_SESSION 4 +#define KEYFS_INO_USER_SESSION 5 +#define KEYFS_INO_USER 6 +#define KEYFS_INO_REQUEST_KEY 7 +#define KEYFS_INO_SEARCH 8 +#define KEYFS_INO_JOIN_SESSION 9 +#define KEYFS_INO__LAST 9 + +/* key dir inodes added to keyid << 16 */ +#define KEYFS_INO_K_SYMLINK 0 /* symlink to key's directory */ +#define KEYFS_INO_K_DIR 1 /* key's directory */ +#define KEYFS_INO_K_TYPE 2 +#define KEYFS_INO_K_DESC 3 +#define KEYFS_INO_K_EXPIRY 4 +#define KEYFS_INO_K_PERM 5 +#define KEYFS_INO_K_REVOKE 6 +#define KEYFS_INO_K_INSTANTIATE 7 +#define KEYFS_INO_K_NEGATE 8 +#define KEYFS_INO_K_SEARCH 9 +#define KEYFS_INO_K_USAGE 10 +#define KEYFS_INO_K_FLAGS 11 +#define KEYFS_INO_K_ADD 12 +#define KEYFS_INO_K_PAYLOAD 13 +#define KEYFS_INO_K__LAST 13 + +/* root.c */ +extern struct inode *keyfs_get_rootdir(struct super_block *super); +extern int keyfs_d_delete(struct dentry *dentry); + +/* rootfile.c */ +enum keyfs_bifile_state { + KEYFS_BIFILE_WANT_PARAMS, + KEYFS_BIFILE_GOT_RESULT, + KEYFS_BIFILE_DONE +}; + +struct keyfs_bifile_record { + enum keyfs_bifile_state state; + key_serial_t id; + ssize_t error; +}; + +extern ssize_t keyfs_bifile_read(struct file *, char __user *, size_t, loff_t *); +extern int keyfs_bifile_open(struct inode *, struct file *); +extern int keyfs_bifile_release(struct inode *, struct file *); + +extern struct inode *keyfs_get_rootfile(struct super_block *super, ino_t ino); + +/* rootlink.c */ +extern struct inode *keyfs_get_rootlink(struct super_block *super, ino_t ino); + +/* keydir.c */ +extern struct dentry_operations keyfs_dir_dentry_operations; + +extern struct inode *keyfs_get_keydir(struct super_block *super, + key_serial_t id); + +/* keyfile.c */ +extern struct dentry_operations keyfs_file_dentry_operations; + +extern struct inode *keyfs_get_keyfile(struct inode *dir, ino_t ino); + +extern int keyfs_file_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat); + +/* ringdir.c */ +extern struct file_operations keyfs_ring_file_operations; +extern struct inode_operations keyfs_ring_inode_operations; --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/keyfs/Makefile 2004-09-03 00:56:55.291755024 -0700 @@ -0,0 +1,14 @@ +# +# Makefile for Key view filesystem +# + +keyfs-objs := \ + super.o \ + root.o \ + rootfile.o \ + rootlink.o \ + keydir.o \ + keyfile.o \ + ringdir.o + +obj-$(CONFIG_KEYFS) := keyfs.o --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/keyfs/ringdir.c 2004-09-03 00:56:55.483725840 -0700 @@ -0,0 +1,467 @@ +/* ringdir.c: keyring directory + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "keyfs.h" + +/* + * keyring dir operations + */ +static int keyfs_ring_readdir(struct file *, void *, filldir_t); +static struct dentry *keyfs_ring_lookup(struct inode *, struct dentry *, + struct nameidata *); + +struct file_operations keyfs_ring_file_operations = { + .readdir = keyfs_ring_readdir, +}; + +static int keyfs_ring_unlink(struct inode *, struct dentry *); +static int keyfs_ring_symlink(struct inode *, struct dentry *, const char *); + +struct inode_operations keyfs_ring_inode_operations = { + .getattr = keyfs_file_getattr, + .lookup = keyfs_ring_lookup, + .unlink = keyfs_ring_unlink, + .symlink = keyfs_ring_symlink, +}; + +/* + * key link operations + */ +static struct file_operations keyfs_keylink_file_operations = { +}; + +static int keyfs_keylink_getattr(struct vfsmount *, struct dentry *, + struct kstat *); + +static int keyfs_keylink_readlink(struct dentry *, char __user *, int); +static int keyfs_keylink_follow_link(struct dentry *, struct nameidata *); + +static struct inode_operations keyfs_keylink_inode_operations = { + .getattr = keyfs_keylink_getattr, + .follow_link = keyfs_keylink_follow_link, + .readlink = keyfs_keylink_readlink, +}; + +static int keyfs_keylink_d_revalidate(struct dentry *, struct nameidata *); + +static struct dentry_operations keyfs_keylink_dentry_operations = { + .d_revalidate = keyfs_keylink_d_revalidate, + .d_delete = keyfs_d_delete, +}; + +/*****************************************************************************/ +/* + * get a key symlink + */ +static struct inode *keyfs_get_keylink(struct super_block *super, + key_serial_t id) +{ + struct inode *inode; + struct key *key; + + key = key_lookup(id); + if (IS_ERR(key)) { + inode = ERR_PTR(PTR_ERR(key)); + goto error; + } + + /* get the appropriate key symlink inode */ + inode = iget_locked(super, (id << 16) + KEYFS_INO_K_SYMLINK); + if (inode && inode->i_state & I_NEW) { + /* initialise it */ + down_read(&key->sem); + + inode->i_mtime = inode->i_atime = inode->i_ctime = + CURRENT_TIME; + inode->i_blocks = 0; + inode->i_blksize = 1024; + inode->i_uid = key->uid; + inode->i_gid = key->gid; + inode->i_mode = S_IFLNK | S_IRWXUGO; + inode->i_op = &keyfs_keylink_inode_operations; + inode->i_fop = &keyfs_keylink_file_operations; + inode->i_nlink = 2; + inode->u.generic_ip = key; + + up_read(&key->sem); + + /* success */ + unlock_new_inode(inode); + } + else if (inode) { + /* reuse one that already exists */ + key_put(key); + } + else { + /* error */ + inode = ERR_PTR(-ENOMEM); + key_put(key); + } + + error: + return inode; + +} /* end keyfs_get_keylink() */ + +/*****************************************************************************/ +/* + * update the attributes on a keyring symlink + */ +static int keyfs_keylink_d_revalidate(struct dentry *dentry, struct nameidata *nd) +{ + struct inode *inode; + struct key *key; + + inode = dentry->d_inode; + key = inode->u.generic_ip; + + /* update the inode from the key */ + down_read(&key->sem); + + inode->i_uid = key->uid; + inode->i_gid = key->gid; + + up_read(&key->sem); + + return 1; + +} /* end keyfs_keylink_d_revalidate() */ + +/*****************************************************************************/ +/* + * read the attributes of an inode, updating from the key + */ +static int keyfs_keylink_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) +{ + /* update the inode */ + keyfs_keylink_d_revalidate(dentry, NULL); + + /* transfer attributes from the inode structure to the stat + * structure */ + generic_fillattr(dentry->d_inode, stat); + + return 0; + +} /* end keyfs_keylink_getattr() */ + +/*****************************************************************************/ +/* + * enumerate the keys in the ring directory + */ +static int keyfs_ring_readdir(struct file *file, void *cookie, filldir_t filldir) +{ + struct keyring_list *klist; + struct key *key; + loff_t pos; + char id[16]; + int ret, loop, n; + + key = file->f_dentry->d_inode->u.generic_ip; + pos = file->f_pos; + + /* read the usual "." and ".." first followed by the key symlinks */ + switch ((int)file->f_pos) { + case 0: + ret = filldir(cookie, ".", 1, file->f_pos, + file->f_dentry->d_inode->i_ino, DT_DIR); + if (ret < 0) + goto done; + file->f_pos++; + + case 1: + ret = filldir(cookie, "..", 2, file->f_pos, + parent_ino(file->f_dentry), DT_DIR); + if (ret < 0) + goto done; + file->f_pos++; + pos--; + + default: + break; + } + + pos -= 2; + + /* then come the key links, a symlink for each */ + down_read(&key->sem); + + klist = key->payload.subscriptions; + if (!klist || pos >= klist->nkeys) + goto done_unlock; + + /* enumerate the keys */ + for (loop = pos; loop < klist->nkeys; loop++) { + /* each symlink's name is the corresponding key serial + * number, and the inode number is based on that too */ + n = sprintf(id, "%d", klist->keys[loop]->serial); + + ret = filldir(cookie, id, n, file->f_pos, + (ino_t) klist->keys[loop] << 16 | + KEYFS_INO_K_SYMLINK, + DT_LNK); + if (ret < 0) + goto done_unlock; + + file->f_pos++; + } + + done_unlock: + up_read(&key->sem); + + done: + return 0; + +} /* end keyfs_ring_readdir() */ + +/*****************************************************************************/ +/* + * look up an inode in the ring directory + */ +static struct dentry *keyfs_ring_lookup(struct inode *dir, + struct dentry *dentry, + struct nameidata *nd) +{ + struct keyring_list *klist; + struct dentry *ret; + struct inode *target; + struct key *key; + key_serial_t id; + const char *name; + char *p; + int loop; + + key = dir->u.generic_ip; + + /* determine which virtual file they want */ + name = dentry->d_name.name; + + switch (dentry->d_name.len) { + case 1: + if (memcmp(name, ".", 1) == 0) { + target = igrab(dir); + goto instantiate; + } + break; + + case 2: + if (memcmp(name, "..", 2)==0) { + target = igrab(dir); + goto instantiate; + } + break; + + + default: + break; + } + + /* it's going to be a keyring symlink then */ + id = simple_strtoul(name, &p, 10); + if (*p) + goto noent; /* not a decimal number */ + + /* check the keyring has a link to the specified key */ + ret = NULL; + + read_lock(&key->lock); + + klist = key->payload.subscriptions; + if (!klist) + goto noent_unlock; + + for (loop = 0; loop < klist->nkeys; loop++) + if (klist->keys[loop]->serial == id) + goto found; + + goto noent_unlock; + found: + + read_unlock(&key->lock); + + /* get the symlink inode */ + target = keyfs_get_keylink(dir->i_sb, id); + if (!IS_ERR(target)) + goto instantiate; + + if (ret == ERR_PTR(-ENOENT)) + goto noent; + + ret = ERR_PTR(PTR_ERR(target)); + goto error; + + /* instantiate the dentry */ + instantiate: + dentry->d_op = &keyfs_keylink_dentry_operations; + d_add(dentry, target); + ret = NULL; + + error: + return ret; + + noent_unlock: + read_unlock(&key->lock); + noent: + /* make a negative dentry */ + d_add(dentry, NULL); + ret = NULL; + goto error; + +} /* end keyfs_ring_lookup() */ + +/*****************************************************************************/ +/* + * unlink a key from a keyring + */ +static int keyfs_ring_unlink(struct inode *dir, struct dentry *dentry) +{ + struct key *keyring, *key; + int ret; + + keyring = dir->u.generic_ip; + key = dentry->d_inode->u.generic_ip; + + /* we must have write permission on the keyring */ + ret = -EPERM; + if (key_permission(keyring, KEY_WRITE)) + /* attempt the unlink */ + ret = key_unlink(keyring, key); + + return ret; + +} /* end keyfs_ring_unlink() */ + +/*****************************************************************************/ +/* + * link a key into a keyring + */ +static int keyfs_ring_symlink(struct inode *dir, struct dentry *dentry, + const char *to) +{ + struct inode *inode; + key_serial_t id; + struct key *keyring, *key; + char *p; + int ret; + + keyring = dir->u.generic_ip; + + /* must have write permission on the keyring */ + ret = -EPERM; + if (!key_permission(keyring, KEY_WRITE)) + goto error; + + /* extract the ID from the symlink target */ + ret = -EINVAL; + if (strncmp(to, "../../", 6) != 0) + goto error; + id = simple_strtoul(to + 6, &p, 10); + if (*p) + goto error; + + /* the new filename must specify the same ID */ + if (strlen(to + 6) != dentry->d_name.len) + goto error; + + if (memcmp(dentry->d_name.name, to + 6, dentry->d_name.len) != 0) + goto error; + + /* create an inode for the symlink */ + inode = keyfs_get_keylink(dir->i_sb, id); + if (IS_ERR(inode)) { + ret = PTR_ERR(inode); + goto error; + } + + key = inode->u.generic_ip; + + /* we don't admit a key exists if we have no permissions on it at + * all */ + ret = -ENOENT; + if (!key_any_permission(key, KEY_ALL)) + goto error2; + + /* check the key has link access */ + ret = -EPERM; + if (!key_permission(key, KEY_LINK)) + goto error2; + + /* attempt to forge a link */ + ret = key_link(keyring, key); + if (ret < 0) + goto error2; + + /* point the dentry at the inode */ + d_instantiate(dentry, inode); + inode = NULL; + ret = 0; + + error2: + iput(inode); + error: + return ret; + +} /* end keyfs_ring_symlink() */ + +/*****************************************************************************/ +/* + * read a symlink that goes from a keyring to a key + */ +static int keyfs_keylink_readlink(struct dentry *dentry, char __user *_buffer, + int buflen) +{ + key_serial_t id; + char buffer[30]; + int n; + + id = dentry->d_inode->i_ino >> 16; + + /* we render the symlink in the form required for pathwalk */ + n = sprintf(buffer, "../../%d", id); + if (buflen > n) + buflen = n; + + /* and pass back to userspace */ + if (copy_to_user(_buffer, buffer, buflen) != 0) + buflen = -EFAULT; + + return buflen; + +} /* end keyfs_keylink_readlink() */ + +/*****************************************************************************/ +/* + * follow a symlink from a keyring to a key + */ +static int keyfs_keylink_follow_link(struct dentry *dentry, + struct nameidata *nd) +{ + key_serial_t id; + char buffer[30]; + + id = dentry->d_inode->i_ino >> 16; + + /* we render the symlink in the form required for pathwalk */ + sprintf(buffer, "../../%d", id); + nd_set_link(nd, buffer); + + return 0; + +} /* end keyfs_keylink_follow_link() */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/keyfs/root.c 2004-09-03 00:56:55.482725992 -0700 @@ -0,0 +1,362 @@ +/* root.c: keyfs root inode operations + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "keyfs.h" + +/* + * root directory operations + */ +static int keyfs_root_readdir(struct file *, void *, filldir_t); + +static struct file_operations keyfs_root_file_operations = { + .readdir = keyfs_root_readdir, +}; + +static struct dentry *keyfs_root_lookup(struct inode *, struct dentry *, + struct nameidata *); + +static struct inode_operations keyfs_root_inode_operations = { + .lookup = keyfs_root_lookup, +}; + +/*****************************************************************************/ +/* + * get the root directory + */ +struct inode *keyfs_get_rootdir(struct super_block *super) +{ + struct inode *inode; + + /* get the root inode */ + inode = iget_locked(super, KEYFS_INO_ROOT); + if (inode && inode->i_state & I_NEW) { + /* initialise it */ + inode->i_mtime = inode->i_atime = inode->i_ctime = + CURRENT_TIME; + inode->i_blocks = 0; + inode->i_blksize = 1024; + inode->i_uid = 0; + inode->i_gid = 0; + inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; + inode->i_op = &keyfs_root_inode_operations; + inode->i_fop = &keyfs_root_file_operations; + inode->i_nlink = 2; + + /* success */ + unlock_new_inode(inode); + } + + return inode; + +} /* end keyfs_get_rootdir() */ + +/*****************************************************************************/ +/* + * enumerate the keys in the root directory + */ +static int keyfs_root_readdir(struct file *file, void *cookie, filldir_t filldir) +{ + struct rb_node *_p; + struct key *next, *cursor; + loff_t pos; + ino_t ino; + char buf[16]; + int ret, n; + + pos = file->f_pos; + + /* read the usual "." and ".." first and then the master control files + * and special symlinks to process/user specific keyrings + */ + switch ((int)file->f_pos) { + case 0: + ret = filldir(cookie, ".", 1, file->f_pos, + file->f_dentry->d_inode->i_ino, DT_DIR); + if (ret < 0) + goto out; + file->f_pos++; + + case 1: + ret = filldir(cookie, "..", 2, file->f_pos, + parent_ino(file->f_dentry), DT_DIR); + if (ret < 0) + goto out; + file->f_pos++; + pos--; + + case 2: + ret = filldir(cookie, "thread", 6, file->f_pos, + KEYFS_INO_THREAD, DT_LNK); + if (ret < 0) + goto out; + file->f_pos++; + + case 3: + ret = filldir(cookie, "process", 7, file->f_pos, + KEYFS_INO_PROCESS, DT_LNK); + if (ret < 0) + goto out; + file->f_pos++; + + case 4: + ret = filldir(cookie, "session", 7, file->f_pos, + KEYFS_INO_SESSION, DT_LNK); + if (ret < 0) + goto out; + file->f_pos++; + + case 5: + ret = filldir(cookie, "user-session", 12, file->f_pos, + KEYFS_INO_USER_SESSION, DT_LNK); + if (ret < 0) + goto out; + file->f_pos++; + + case 6: + ret = filldir(cookie, "user", 4, file->f_pos, + KEYFS_INO_USER, DT_LNK); + if (ret < 0) + goto out; + file->f_pos++; + + case 7: + ret = filldir(cookie, "request-key", 11, file->f_pos, + KEYFS_INO_REQUEST_KEY, DT_REG); + if (ret < 0) + goto out; + file->f_pos++; + + case 8: + ret = filldir(cookie, "search", 6, file->f_pos, + KEYFS_INO_SEARCH, DT_REG); + if (ret < 0) + goto out; + file->f_pos++; + + case 9: + ret = filldir(cookie, "join-session", 12, file->f_pos, + KEYFS_INO_REQUEST_KEY, DT_REG); + if (ret < 0) + goto out; + file->f_pos++; + + default: + break; + } + + /* then come the keys, a directory for each for each one we can see */ + pos = file->f_pos - 10; + + cursor = NULL; + ret = 0; + + spin_lock(&key_serial_lock); + + /* start with the Nth key */ + _p = rb_first(&key_serial_tree); + if (!_p) + goto out_unlock; + + while (pos > 0) { + pos--; + _p = rb_next(_p); + if (!_p) + goto out_unlock; + } + + /* loop around using the key currently being examined as a cursor (we + * keep it pinned whilst we're outside of the lock so that it doesn't + * get removed from the tree) */ + for (;;) { + next = rb_entry(_p, struct key, serial_node); + atomic_inc(&next->usage); + + spin_unlock(&key_serial_lock); + + key_put(cursor); + cursor = next; + + /* a process is only allowed to see a key if it has at least + * one applicable permission */ + if (key_any_permission(cursor, KEY_ALL)) { + /* each directory's name is the corresponding key + * serial number, and the inode number is based on that + * too */ + n = sprintf(buf, "%d", cursor->serial); + + ino = (ino_t) cursor->serial << 16 | KEYFS_INO_K_DIR; + + ret = filldir(cookie, buf, n, file->f_pos, + ino, DT_DIR); + if (ret < 0) + goto out_put; + } + + file->f_pos++; + + /* advance the cursor under lock */ + spin_lock(&key_serial_lock); + _p = rb_next(_p); + if (!_p) + goto out_unlock; + } + + out_unlock: + spin_unlock(&key_serial_lock); + out_put: + key_put(cursor); + out: + return 0; + +} /* end keyfs_root_readdir() */ + +/*****************************************************************************/ +/* + * look up an inode in the root directory + */ +static struct dentry *keyfs_root_lookup(struct inode *dir, + struct dentry *dentry, + struct nameidata *nd) +{ + struct dentry_operations *dops = NULL; + struct dentry *ret; + struct inode *target; + key_serial_t id; + const char *name; + char *p; + + /* determine which virtual file they're asking for */ + name = dentry->d_name.name; + + switch (dentry->d_name.len) { + case 1: + if (memcmp(name, ".", 1) == 0) { + target = igrab(dir); + goto instantiate; + } + break; + + case 2: + if (memcmp(name, "..", 2)==0) { + target = igrab(dir); + goto instantiate; + } + break; + + case 4: + if (memcmp(name, "user", 4) == 0) { + target = keyfs_get_rootlink(dir->i_sb, + KEYFS_INO_USER); + goto instantiate; + } + break; + + case 6: + if (memcmp(name, "thread", 6) == 0) { + target = keyfs_get_rootlink(dir->i_sb, + KEYFS_INO_THREAD); + goto instantiate; + } + if (memcmp(name, "search", 6) == 0) { + target = keyfs_get_rootfile(dir->i_sb, + KEYFS_INO_SEARCH); + goto instantiate; + } + break; + + case 7: + if (memcmp(name, "process", 7) == 0) { + target = keyfs_get_rootlink(dir->i_sb, + KEYFS_INO_PROCESS); + goto instantiate; + } + if (memcmp(name, "session", 7) == 0) { + target = keyfs_get_rootlink(dir->i_sb, + KEYFS_INO_SESSION); + goto instantiate; + } + break; + + case 11: + if (memcmp(name, "request-key", 11) == 0) { + target = keyfs_get_rootfile(dir->i_sb, + KEYFS_INO_REQUEST_KEY); + goto instantiate; + } + break; + + case 12: + if (memcmp(name, "user-session", 12) == 0) { + target = keyfs_get_rootlink(dir->i_sb, + KEYFS_INO_USER_SESSION); + goto instantiate; + } + if (memcmp(name, "join-session", 12) == 0) { + target = keyfs_get_rootfile(dir->i_sb, + KEYFS_INO_JOIN_SESSION); + goto instantiate; + } + break; + + default: + break; + } + + /* see if they asked for a virtual keyring directory */ + id = simple_strtoul(name, &p, 10); + if (*p) + goto noent; /* not a decimal number, so no */ + + target = keyfs_get_keydir(dir->i_sb, id); + if (!IS_ERR(target)) + goto instantiate_keydir; + if (target == ERR_PTR(-ENOENT)) + goto noent; + + ret = ERR_PTR(PTR_ERR(target)); + goto out; + + /* instantiate the dentry */ + instantiate_keydir: + dops = &keyfs_dir_dentry_operations; + instantiate: + dentry->d_op = dops; + d_add(dentry, target); + ret = NULL; + + out: + return ret; + + noent: + /* make a negative dentry */ + d_add(dentry, NULL); + ret = NULL; + goto out; + +} /* end keyfs_root_lookup() */ + +/*****************************************************************************/ +/* + * we don't want key-specifc files cached, lest the dentry-cache pins keys + */ +int keyfs_d_delete(struct dentry *dentry) +{ + return 1; + +} /* end keyfs_d_delete() */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/keyfs/rootfile.c 2004-09-03 00:56:55.752684952 -0700 @@ -0,0 +1,465 @@ +/* rootfile.c: keyfs root file operations + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "keyfs.h" + +static struct inode_operations keyfs_rootfile_inode_operations = { +}; + +/* + * request-key operation + */ +static ssize_t keyfs_request_key_write(struct file *, const char __user *, + size_t, loff_t *); + +static struct file_operations keyfs_request_key_file_operations = { + .open = keyfs_bifile_open, + .release = keyfs_bifile_release, + .read = keyfs_bifile_read, + .write = keyfs_request_key_write, + .llseek = no_llseek, +}; + +/* + * search operation + */ +static ssize_t keyfs_search_write(struct file *, const char __user *, + size_t, loff_t *); + +static struct file_operations keyfs_search_file_operations = { + .open = keyfs_bifile_open, + .release = keyfs_bifile_release, + .read = keyfs_bifile_read, + .write = keyfs_search_write, + .llseek = no_llseek, +}; + +/* + * join session operation + */ +static ssize_t keyfs_join_session_write(struct file *, const char __user *, + size_t, loff_t *); + +static struct file_operations keyfs_join_session_file_operations = { + .open = keyfs_bifile_open, + .release = keyfs_bifile_release, + .read = keyfs_bifile_read, + .write = keyfs_join_session_write, + .llseek = no_llseek, +}; + +/*****************************************************************************/ +/* + * get a root file inode + */ +struct inode *keyfs_get_rootfile(struct super_block *super, ino_t ino) +{ + struct inode *inode; + + /* get the inode */ + inode = iget_locked(super, ino); + if (inode && inode->i_state & I_NEW) { + /* initialise it */ + inode->i_mtime = inode->i_atime = inode->i_ctime = + CURRENT_TIME; + inode->i_blocks = 0; + inode->i_blksize = 1024; + inode->i_uid = 0; + inode->i_gid = 0; + inode->i_op = &keyfs_rootfile_inode_operations; + inode->i_nlink = 1; + + switch (ino) { + case KEYFS_INO_REQUEST_KEY: + inode->i_fop = &keyfs_request_key_file_operations; + inode->i_mode = S_IFREG | S_IRUGO | S_IWUGO; + break; + + case KEYFS_INO_SEARCH: + inode->i_fop = &keyfs_search_file_operations; + inode->i_mode = S_IFREG | S_IRUGO | S_IWUGO; + break; + + case KEYFS_INO_JOIN_SESSION: + inode->i_fop = &keyfs_join_session_file_operations; + inode->i_mode = S_IFREG | S_IRUGO | S_IWUGO; + break; + } + + /* success */ + unlock_new_inode(inode); + } + + return inode; + +} /* end keyfs_get_rootfile() */ + +/*****************************************************************************/ +/* + * open a two-phase command file + */ +int keyfs_bifile_open(struct inode *inode, struct file *file) +{ + int ret; + + /* allocate and initialise a state record */ + ret = -ENOMEM; + file->private_data = + kmalloc(sizeof(struct keyfs_bifile_record), GFP_KERNEL); + if (!file->private_data) + goto error; + + memset(file->private_data, 0, sizeof(struct keyfs_bifile_record)); + ret = 0; + + error: + return ret; + +} /* end keyfs_bifile_open() */ + +/*****************************************************************************/ +/* + * release a two-phase command file + */ +int keyfs_bifile_release(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + return 0; + +} /* end keyfs_bifile_release() */ + +/*****************************************************************************/ +/* + * read the operation result from a two-phase command file (key ID) + */ +ssize_t keyfs_bifile_read(struct file *file, char __user *_buffer, + size_t buflen, loff_t *fpos) +{ + struct keyfs_bifile_record *rec = file->private_data; + char buffer[16]; + ssize_t ret, n; + + ret = rec->error; + if (rec->error) + goto error; + + ret = -EINVAL; + if (rec->state == KEYFS_BIFILE_WANT_PARAMS) + goto error; + + ret = 0; + if (rec->state != KEYFS_BIFILE_GOT_RESULT) + goto error; + + rec->state = KEYFS_BIFILE_DONE; + + /* allow userspace to read back the new keyID */ + n = sprintf(buffer, "%d\n", rec->id); + if (n > buflen) + n = buflen; + + ret = -EAGAIN; + if (copy_to_user(_buffer, buffer, n) != 0) + goto error; + + ret = n; + + error: + return ret; + +} /* end keyfs_bifile_read() */ + +/*****************************************************************************/ +/* + * request a key + * - format: NLNL + */ +static ssize_t keyfs_request_key_write(struct file *file, + const char __user *_buffer, + size_t buflen, loff_t *fpos) +{ + struct keyfs_bifile_record *rec = file->private_data; + struct key_type *ktype; + struct key *key; + ssize_t ret; + char *buffer, *bend, *p, *type, *desc, *info; + + ret = -EINVAL; + if (rec->state != KEYFS_BIFILE_WANT_PARAMS || buflen > 40000) + goto error; + + rec->state = KEYFS_BIFILE_GOT_RESULT; + + /* fetch the data into kernel memory */ + ret = -ENOMEM; + buffer = kmalloc(buflen + 1, GFP_KERNEL); + if (!buffer) + goto error; + + ret = -EFAULT; + if (copy_from_user(buffer, _buffer, buflen) != 0) + goto error2; + bend = buffer + buflen; + *bend = 0; + + /* parse the parameter string */ + ret = -EINVAL; + info = NULL; + + type = buffer; + + p = memchr(buffer, '\n', bend - buffer); + if (!p) + goto error2; + *p++ = 0; + desc = p; + + p = memchr(p, '\n', bend - p); + if (!p) + goto error2; + *p++ = 0; + + if (p != bend) { + info = p; + + p = memchr(p, '\n', bend - p); + if (!p) + goto error2; + *p++ = 0; + } + + if (p != bend) + goto error2; + + /* find the key type */ + ktype = key_type_lookup(type); + if (IS_ERR(ktype)) { + ret = PTR_ERR(ktype); + goto error2; + } + + /* request the key */ + key = request_key(ktype, desc, info); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error3; + } + + /* we'll be returning the key ID on the next read */ + rec->id = key->serial; + key_put(key); + + ret = buflen; + + error3: + key_type_put(ktype); + error2: + kfree(buffer); + error: + if (ret < 0) + rec->error = ret; + return ret; + +} /* end keyfs_request_key_write() */ + +/*****************************************************************************/ +/* + * search for a key + * - format: NLNL[]NL + */ +static ssize_t keyfs_search_write(struct file *file, + const char __user *_buffer, + size_t buflen, loff_t *fpos) +{ + struct keyfs_bifile_record *rec = file->private_data; + struct key_type *ktype; + key_serial_t ringid; + struct key *key, *keyring; + ssize_t ret; + char *buffer, *bend, *p, *type, *desc, *ring; + + ret = -EINVAL; + if (rec->state != KEYFS_BIFILE_WANT_PARAMS || buflen > 40000) + goto error; + + rec->state = KEYFS_BIFILE_GOT_RESULT; + + /* fetch the data into kernel memory */ + ret = -ENOMEM; + buffer = kmalloc(buflen + 1, GFP_KERNEL); + if (!buffer) + goto error; + + ret = -EFAULT; + if (copy_from_user(buffer, _buffer, buflen) != 0) + goto error2; + bend = buffer + buflen; + *bend = 0; + + /* parse the parameter string */ + ret = -EINVAL; + + type = buffer; + + p = memchr(buffer, '\n', bend - buffer); + if (!p) + goto error2; + *p++ = 0; + desc = p; + + p = memchr(p, '\n', bend - p); + if (!p) + goto error2; + *p++ = 0; + ring = p; + + p = memchr(p, '\n', bend - p); + if (!p) + goto error2; + *p++ = 0; + + if (p != bend) + goto error2; + + /* if supplied, turn the ring ID into a keyring */ + keyring = NULL; + + if (*ring) { + ringid = simple_strtoul(ring, &p, 0); + if (*p) + goto error2; + + /* find the target keyring (which must be writable) */ + keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto error2; + } + } + + /* find the key type */ + ktype = key_type_lookup(type); + if (IS_ERR(ktype)) { + ret = PTR_ERR(ktype); + goto error3; + } + + /* search for the key */ + key = search_process_keyrings(ktype, desc); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error4; + } + + /* link the resulting key to the destination keyring if we can */ + if (keyring) { + ret = -EACCES; + if (!key_permission(key, KEY_LINK)) + goto error5; + + ret = key_link(keyring, key); + if (ret < 0) + goto error5; + } + + /* we'll be returning the key ID on the next read */ + rec->id = key->serial; + + ret = buflen; + + error5: + key_put(key); + error4: + key_type_put(ktype); + error3: + key_put(keyring); + error2: + kfree(buffer); + error: + if (ret < 0) + rec->error = ret; + return ret; + +} /* end keyfs_search_write() */ + +/*****************************************************************************/ +/* + * join a session keyring, creating it if it doesn't exist + * - format: [][NL] + */ +static ssize_t keyfs_join_session_write(struct file *file, + const char __user *_buffer, + size_t buflen, loff_t *fpos) +{ + struct keyfs_bifile_record *rec = file->private_data; + ssize_t ret; + long jret; + char *buffer, *bend, *p, *name; + + ret = -EINVAL; + if (rec->state != KEYFS_BIFILE_WANT_PARAMS || buflen > PAGE_SIZE) + goto error; + + rec->state = KEYFS_BIFILE_GOT_RESULT; + + /* fetch the data into kernel memory */ + ret = -ENOMEM; + buffer = kmalloc(buflen + 1, GFP_KERNEL); + if (!buffer) + goto error; + + ret = -EFAULT; + if (copy_from_user(buffer, _buffer, buflen) != 0) + goto error2; + bend = buffer + buflen; + *bend = 0; + + /* parse the parameter string */ + ret = -EINVAL; + + p = memchr(buffer, '\n', bend - buffer); + if (p) { + *p++ = 0; + if (p != bend) + goto error2; + } + + name = buffer; + if (!*buffer) + name = NULL; /* anonymous session */ + + /* attempt to join the named session */ + jret = join_session_keyring(name); + if (jret < 0) { + ret = jret; + goto error2; + } + + /* we'll be returning the key ID on the next read */ + rec->id = jret; + ret = buflen; + + error2: + kfree(buffer); + error: + if (ret < 0) + rec->error = ret; + return ret; + +} /* end keyfs_join_session_write() */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/keyfs/rootlink.c 2004-09-03 00:56:55.298753960 -0700 @@ -0,0 +1,180 @@ +/* rootlink.c: special process/user keyring symlink operations + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "keyfs.h" + +static struct file_operations keyfs_rootlink_file_operations = { +}; + +static int keyfs_rootlink_readlink(struct dentry *, char __user *, int); +static int keyfs_rootlink_follow_link(struct dentry *, struct nameidata *); + +static struct inode_operations keyfs_rootlink_inode_operations = { + .follow_link = keyfs_rootlink_follow_link, + .readlink = keyfs_rootlink_readlink, +}; + +/*****************************************************************************/ +/* + * get a special process keyring symlink inode + */ +struct inode *keyfs_get_rootlink(struct super_block *super, ino_t ino) +{ + struct inode *inode; + + /* get the inode */ + inode = iget_locked(super, ino); + if (inode && inode->i_state & I_NEW) { + /* initialise it */ + inode->i_mtime = inode->i_atime = inode->i_ctime = + CURRENT_TIME; + inode->i_blocks = 0; + inode->i_blksize = 1024; + inode->i_uid = 0; + inode->i_gid = 0; + inode->i_mode = S_IFLNK | S_IRUGO | S_IXUGO; + inode->i_op = &keyfs_rootlink_inode_operations; + inode->i_fop = &keyfs_rootlink_file_operations; + inode->i_nlink = 2; + + /* success */ + unlock_new_inode(inode); + } + + return inode; + +} /* end keyfs_get_rootlink() */ + +/*****************************************************************************/ +/* + * read the symlink + */ +static int keyfs_rootlink_readlink(struct dentry *dentry, + char __user *_buffer, + int buflen) +{ + key_serial_t id; + char buffer[12]; + int n; + + id = 0; + + /* the symlink targets the appropriate keyring directory */ + switch (dentry->d_inode->i_ino) { + case KEYFS_INO_THREAD: + if (current->thread_keyring) + id = current->thread_keyring->serial; + break; + + case KEYFS_INO_PROCESS: + if (current->process_keyring) + id = current->process_keyring->serial; + break; + + case KEYFS_INO_SESSION: + if (current->session_keyring) + id = current->session_keyring->serial; + break; + + case KEYFS_INO_USER_SESSION: + if (current->user->uid_keyring) + id = current->user->uid_keyring->serial; + break; + + case KEYFS_INO_USER: + if (current->user->session_keyring) + id = current->user->session_keyring->serial; + break; + + default: + BUG(); + } + + /* render the keyring ID as a string and write to userspace */ + n = sprintf(buffer, "%d", id); + if (buflen > n) + buflen = n; + + if (copy_to_user(_buffer, buffer, buflen) != 0) + buflen = -EFAULT; + + return buflen; + +} /* end keyfs_rootlink_readlink() */ + +/*****************************************************************************/ +/* + * follow the symlink to the appropriate directory + */ +static int keyfs_rootlink_follow_link(struct dentry *dentry, + struct nameidata *nd) +{ + key_serial_t id; + char buffer[13]; + int ret; + + id = 0; + ret = -ENOENT; + + /* target the symlink to the appropriate keyring directory */ + switch (dentry->d_inode->i_ino) { + case KEYFS_INO_THREAD: + if (!current->thread_keyring) + goto no_keyring; + id = current->thread_keyring->serial; + break; + + case KEYFS_INO_PROCESS: + if (!current->process_keyring) + goto no_keyring; + id = current->process_keyring->serial; + break; + + case KEYFS_INO_SESSION: + if (!current->session_keyring) + goto no_keyring; + id = current->session_keyring->serial; + break; + + case KEYFS_INO_USER_SESSION: + if (!current->user->uid_keyring) + goto no_keyring; + id = current->user->uid_keyring->serial; + break; + + case KEYFS_INO_USER: + if (!current->user->session_keyring) + goto no_keyring; + id = current->user->session_keyring->serial; + break; + + default: + BUG(); + } + + /* render the keyring ID as a string and pass back to pathwalk */ + sprintf(buffer, "%d", id); + nd_set_link(nd, buffer); + ret = 0; + + no_keyring: + return ret; + +} /* end keyfs_rootlink_follow_link() */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/keyfs/super.c 2004-09-03 00:56:55.298753960 -0700 @@ -0,0 +1,117 @@ +/* super.c: key management filesystme + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "keyfs.h" + +#define KEYFS_FS_MAGIC 0x4B455953 /* 'KEYS' */ + +static struct super_block *keyfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data); + +static int keyfs_fill_super(struct super_block *s, void *data, int silent); + +static struct file_system_type keyfs_fs_type = { + .owner = THIS_MODULE, + .name = "keyfs", + .get_sb = keyfs_get_sb, + .kill_sb = kill_anon_super, +}; + +static void keyfs_clear_inode(struct inode *vfs_inode); + +static struct super_operations keyfs_super_ops = { + .statfs = simple_statfs, + .clear_inode = keyfs_clear_inode, +}; + + +/*****************************************************************************/ +/* + * initialise the filesystem + */ +static int __init keyfs_fs_init(void) +{ + /* export our filesystem to lesser mortals */ + return register_filesystem(&keyfs_fs_type); + +} /* end keyfs_fs_init() */ + +fs_initcall(keyfs_fs_init); + +/*****************************************************************************/ +/* + * create the keyfs superblock (one time only) + */ +static struct super_block *keyfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data) +{ + return get_sb_single(&keyfs_fs_type, flags, data, keyfs_fill_super); + +} /* end keyfs_get_sb() */ + +/*****************************************************************************/ +/* + * fill out the superblock + */ +static int keyfs_fill_super(struct super_block *s, void *data, int silent) +{ + struct inode *inode; + int ret; + + s->s_blocksize = 1024; + s->s_blocksize_bits = 10; + s->s_magic = KEYFS_FS_MAGIC; + s->s_op = &keyfs_super_ops; + + /* allocate the root inode */ + ret = -ENOMEM; + inode = keyfs_get_rootdir(s); + + if (!IS_ERR(inode)) { + /* allocate a root dentry */ + s->s_root = d_alloc_root(inode); + if (s->s_root) { + ret = 0; + } + else { + printk("keyfs: get root dentry failed\n"); + iput(inode); + } + } + + + return ret; + +} /* end keyfs_fill_super() */ + +/*****************************************************************************/ +/* + * clear an inode + */ +static void keyfs_clear_inode(struct inode *inode) +{ + struct key *key = inode->u.generic_ip; + + inode->u.generic_ip = NULL; + + key_put(key); + +} /* end keyfs_clear_inode() */ --- linux-2.6.9-rc1/fs/libfs.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/fs/libfs.c 2004-09-02 20:51:52.000000000 -0700 @@ -27,14 +27,27 @@ int simple_statfs(struct super_block *sb } /* - * Lookup the data. This is trivial - if the dentry didn't already - * exist, we know it is negative. + * Retaining negative dentries for an in-memory filesystem just wastes + * memory and lookup time: arrange for them to be deleted immediately. */ +static int simple_delete_dentry(struct dentry *dentry) +{ + return 1; +} +/* + * Lookup the data. This is trivial - if the dentry didn't already + * exist, we know it is negative. Set d_op to delete negative dentries. + */ struct dentry *simple_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { + static struct dentry_operations simple_dentry_operations = { + .d_delete = simple_delete_dentry, + }; + if (dentry->d_name.len > NAME_MAX) return ERR_PTR(-ENAMETOOLONG); + dentry->d_op = &simple_dentry_operations; d_add(dentry, NULL); return NULL; } @@ -456,6 +469,58 @@ ssize_t simple_read_from_buffer(void __u return count; } +/* + * Transaction based IO. + * The file expects a single write which triggers the transaction, and then + * possibly a read which collects the result - which is stored in a + * file-local buffer. + */ +char *simple_transaction_get(struct file *file, const char __user *buf, size_t size) +{ + struct simple_transaction_argresp *ar; + static spinlock_t simple_transaction_lock = SPIN_LOCK_UNLOCKED; + + if (size > SIMPLE_TRANSACTION_LIMIT - 1) + return ERR_PTR(-EFBIG); + + ar = (struct simple_transaction_argresp *)get_zeroed_page(GFP_KERNEL); + if (!ar) + return ERR_PTR(-ENOMEM); + + spin_lock(&simple_transaction_lock); + + /* only one write allowed per open */ + if (file->private_data) { + spin_unlock(&simple_transaction_lock); + free_page((unsigned long)ar); + return ERR_PTR(-EBUSY); + } + + file->private_data = ar; + + spin_unlock(&simple_transaction_lock); + + if (copy_from_user(ar->data, buf, size)) + return ERR_PTR(-EFAULT); + + return ar->data; +} + +ssize_t simple_transaction_read(struct file *file, char __user *buf, size_t size, loff_t *pos) +{ + struct simple_transaction_argresp *ar = file->private_data; + + if (!ar) + return 0; + return simple_read_from_buffer(buf, size, pos, ar->data, ar->size); +} + +int simple_transaction_release(struct inode *inode, struct file *file) +{ + free_page((unsigned long)file->private_data); + return 0; +} + EXPORT_SYMBOL(dcache_dir_close); EXPORT_SYMBOL(dcache_dir_lseek); EXPORT_SYMBOL(dcache_dir_open); @@ -479,3 +544,6 @@ EXPORT_SYMBOL(simple_statfs); EXPORT_SYMBOL(simple_sync_file); EXPORT_SYMBOL(simple_unlink); EXPORT_SYMBOL(simple_read_from_buffer); +EXPORT_SYMBOL(simple_transaction_get); +EXPORT_SYMBOL(simple_transaction_read); +EXPORT_SYMBOL(simple_transaction_release); --- linux-2.6.9-rc1/fs/lockd/svc.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/fs/lockd/svc.c 2004-09-02 20:51:52.000000000 -0700 @@ -409,13 +409,13 @@ MODULE_DESCRIPTION("NFS file locking ser MODULE_LICENSE("GPL"); module_param_call(nlm_grace_period, param_set_grace_period, param_get_ulong, - &nlm_grace_period, 644); + &nlm_grace_period, 0644); module_param_call(nlm_timeout, param_set_timeout, param_get_ulong, - &nlm_timeout, 644); + &nlm_timeout, 0644); module_param_call(nlm_udpport, param_set_port, param_get_int, - &nlm_udpport, 644); + &nlm_udpport, 0644); module_param_call(nlm_tcpport, param_set_port, param_get_int, - &nlm_tcpport, 644); + &nlm_tcpport, 0644); /* * Initialising and terminating the module. --- linux-2.6.9-rc1/fs/locks.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/fs/locks.c 2004-09-03 00:57:11.859236384 -0700 @@ -1318,6 +1318,33 @@ out_unlock: } /** + * flock_lock_file_wait - Apply a FLOCK-style lock to a file + * @filp: The file to apply the lock to + * @fl: The lock to be applied + * + * Add a FLOCK style lock to a file. + */ +int flock_lock_file_wait(struct file *filp, struct file_lock *fl) +{ + int error; + might_sleep(); + for (;;) { + error = flock_lock_file(filp, fl); + if ((error != -EAGAIN) || !(fl->fl_flags & FL_SLEEP)) + break; + error = wait_event_interruptible(fl->fl_wait, !fl->fl_next); + if (!error) + continue; + + locks_delete_block(fl); + break; + } + return error; +} + +EXPORT_SYMBOL(flock_lock_file_wait); + +/** * sys_flock: - flock() system call. * @fd: the file descriptor to lock. * @cmd: the type of lock to apply. @@ -1365,17 +1392,12 @@ asmlinkage long sys_flock(unsigned int f if (error) goto out_free; - for (;;) { - error = flock_lock_file(filp, lock); - if ((error != -EAGAIN) || !can_sleep) - break; - error = wait_event_interruptible(lock->fl_wait, !lock->fl_next); - if (!error) - continue; - - locks_delete_block(lock); - break; - } + if (filp->f_op && filp->f_op->flock) + error = filp->f_op->flock(filp, + (can_sleep) ? F_SETLKW : F_SETLK, + lock); + else + error = flock_lock_file_wait(filp, lock); out_free: if (list_empty(&lock->fl_link)) { @@ -1732,6 +1754,12 @@ void locks_remove_flock(struct file *fil if (!inode->i_flock) return; + if (filp->f_op && filp->f_op->flock) { + struct file_lock fl = { .fl_flags = FL_FLOCK, + .fl_type = F_UNLCK }; + filp->f_op->flock(filp, F_SETLKW, &fl); + } + lock_kernel(); before = &inode->i_flock; @@ -2063,4 +2091,4 @@ static int __init filelock_init(void) return 0; } -module_init(filelock_init) +core_initcall(filelock_init); --- linux-2.6.9-rc1/fs/Makefile 2004-08-24 00:02:58.000000000 -0700 +++ 25/fs/Makefile 2004-09-03 00:57:03.887448280 -0700 @@ -46,6 +46,7 @@ obj-$(CONFIG_PROFILING) += dcookies.o # Do not add any filesystems before this line obj-$(CONFIG_REISERFS_FS) += reiserfs/ +obj-$(CONFIG_REISER4_FS) += reiser4/ obj-$(CONFIG_EXT3_FS) += ext3/ # Before ext2 so root fs can be ext3 obj-$(CONFIG_JBD) += jbd/ obj-$(CONFIG_EXT2_FS) += ext2/ @@ -91,3 +92,6 @@ obj-$(CONFIG_JFS_FS) += jfs/ obj-$(CONFIG_XFS_FS) += xfs/ obj-$(CONFIG_AFS_FS) += afs/ obj-$(CONFIG_BEFS_FS) += befs/ +obj-$(CONFIG_HOSTFS) += hostfs/ +obj-$(CONFIG_HPPFS) += hppfs/ +obj-$(CONFIG_KEYFS) += keyfs/ --- linux-2.6.9-rc1/fs/minix/inode.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/fs/minix/inode.c 2004-09-02 20:51:52.000000000 -0700 @@ -79,7 +79,7 @@ static int init_inodecache(void) { minix_inode_cachep = kmem_cache_create("minix_inode_cache", sizeof(struct minix_inode_info), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (minix_inode_cachep == NULL) return -ENOMEM; --- linux-2.6.9-rc1/fs/mpage.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/fs/mpage.c 2004-09-02 20:51:52.000000000 -0700 @@ -290,7 +290,8 @@ do_mpage_readpage(struct bio *bio, struc alloc_new: if (bio == NULL) { bio = mpage_alloc(bdev, blocks[0] << (blkbits - 9), - nr_pages, GFP_KERNEL); + min_t(int, nr_pages, bio_get_nr_vecs(bdev)), + GFP_KERNEL); if (bio == NULL) goto confused; } --- linux-2.6.9-rc1/fs/namei.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/fs/namei.c 2004-09-03 00:57:03.208551488 -0700 @@ -868,29 +868,31 @@ static int __emul_lookup_dentry(const ch return 0; /* something went wrong... */ if (!nd->dentry->d_inode || S_ISDIR(nd->dentry->d_inode->i_mode)) { - struct nameidata nd_root; + struct dentry *old_dentry = nd->dentry; + struct vfsmount *old_mnt = nd->mnt; + struct qstr last = nd->last; + int last_type = nd->last_type; /* * NAME was not found in alternate root or it's a directory. Try to find * it in the normal root: */ - nd_root.last_type = LAST_ROOT; - nd_root.flags = nd->flags; - nd_root.depth = 0; - memcpy(&nd_root.intent, &nd->intent, sizeof(nd_root.intent)); + nd->last_type = LAST_ROOT; read_lock(¤t->fs->lock); - nd_root.mnt = mntget(current->fs->rootmnt); - nd_root.dentry = dget(current->fs->root); + nd->mnt = mntget(current->fs->rootmnt); + nd->dentry = dget(current->fs->root); read_unlock(¤t->fs->lock); - if (path_walk(name, &nd_root)) - return 1; - if (nd_root.dentry->d_inode) { + if (path_walk(name, nd) == 0) { + if (nd->dentry->d_inode) { + dput(old_dentry); + mntput(old_mnt); + return 1; + } path_release(nd); - nd->dentry = nd_root.dentry; - nd->mnt = nd_root.mnt; - nd->last = nd_root.last; - return 1; } - path_release(&nd_root); + nd->dentry = old_dentry; + nd->mnt = old_mnt; + nd->last = last; + nd->last_type = last_type; } return 1; } @@ -943,8 +945,7 @@ int fastcall path_lookup(const char *nam } nd->mnt = mntget(current->fs->rootmnt); nd->dentry = dget(current->fs->root); - } - else{ + } else { nd->mnt = mntget(current->fs->pwdmnt); nd->dentry = dget(current->fs->pwd); } @@ -1094,8 +1095,12 @@ static inline int check_sticky(struct in static inline int may_delete(struct inode *dir,struct dentry *victim,int isdir) { int error; - if (!victim->d_inode || victim->d_parent->d_inode != dir) + + if (!victim->d_inode) return -ENOENT; + + BUG_ON(victim->d_parent->d_inode != dir); + error = permission(dir,MAY_WRITE | MAY_EXEC, NULL); if (error) return error; @@ -1165,7 +1170,7 @@ struct dentry *lock_rename(struct dentry { struct dentry *p; - if (p1 == p2) { + if (p1->d_inode == p2->d_inode) { down(&p1->d_inode->i_sem); return NULL; } @@ -1196,7 +1201,7 @@ struct dentry *lock_rename(struct dentry void unlock_rename(struct dentry *p1, struct dentry *p2) { up(&p1->d_inode->i_sem); - if (p1 != p2) { + if (p1->d_inode != p2->d_inode) { up(&p2->d_inode->i_sem); up(&p1->d_inode->i_sb->s_vfs_rename_sem); } @@ -2299,12 +2304,8 @@ int page_readlink(struct dentry *dentry, int page_follow_link_light(struct dentry *dentry, struct nameidata *nd) { struct page *page; - char *s = page_getlink(dentry, &page); - if (!IS_ERR(s)) { - nd_set_link(nd, s); - s = NULL; - } - return PTR_ERR(s); + nd_set_link(nd, page_getlink(dentry, &page)); + return 0; } void page_put_link(struct dentry *dentry, struct nameidata *nd) --- linux-2.6.9-rc1/fs/namespace.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/fs/namespace.c 2004-09-02 20:51:52.000000000 -0700 @@ -930,7 +930,35 @@ void mark_mounts_for_expiry(struct list_ EXPORT_SYMBOL_GPL(mark_mounts_for_expiry); -int copy_mount_options (const void __user *data, unsigned long *where) +/* + * Some copy_from_user() implementations do not return the exact number of + * bytes remaining to copy on a fault. But copy_mount_options() requires that. + * Note that this function differs from copy_from_user() in that it will oops + * on bad values of `to', rather than returning a short copy. + */ +static long +exact_copy_from_user(void *to, const void __user *from, unsigned long n) +{ + char *t = to; + const char __user *f = from; + char c; + + if (!access_ok(VERIFY_READ, from, n)) + return n; + + while (n) { + if (__get_user(c, f)) { + memset(t, 0, n); + break; + } + *t++ = c; + f++; + n--; + } + return n; +} + +int copy_mount_options(const void __user *data, unsigned long *where) { int i; unsigned long page; @@ -952,7 +980,7 @@ int copy_mount_options (const void __use if (size > PAGE_SIZE) size = PAGE_SIZE; - i = size - copy_from_user((void *)page, data, size); + i = size - exact_copy_from_user((void *)page, data, size); if (!i) { free_page(page); return -EFAULT; --- linux-2.6.9-rc1/fs/ncpfs/file.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/fs/ncpfs/file.c 2004-09-02 20:51:52.000000000 -0700 @@ -115,11 +115,6 @@ ncp_file_read(struct file *file, char __ if (!ncp_conn_valid(NCP_SERVER(inode))) return -EIO; - if (!S_ISREG(inode->i_mode)) { - DPRINTK("ncp_file_read: read from non-file, mode %07o\n", - inode->i_mode); - return -EINVAL; - } pos = *ppos; @@ -175,10 +170,8 @@ ncp_file_read(struct file *file, char __ *ppos = pos; - if (!IS_RDONLY(inode)) { - inode->i_atime = CURRENT_TIME; - } - + file_accessed(file); + DPRINTK("ncp_file_read: exit %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); outrel: @@ -201,11 +194,6 @@ ncp_file_write(struct file *file, const dentry->d_parent->d_name.name, dentry->d_name.name); if (!ncp_conn_valid(NCP_SERVER(inode))) return -EIO; - if (!S_ISREG(inode->i_mode)) { - DPRINTK("ncp_file_write: write to non-file, mode %07o\n", - inode->i_mode); - return -EINVAL; - } if ((ssize_t) count < 0) return -EINVAL; pos = *ppos; @@ -273,8 +261,9 @@ ncp_file_write(struct file *file, const } } vfree(bouncebuffer); - inode->i_mtime = inode->i_atime = CURRENT_TIME; - + + inode_update_time(inode, 1); + *ppos = pos; if (pos > inode->i_size) { --- linux-2.6.9-rc1/fs/ncpfs/inode.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/fs/ncpfs/inode.c 2004-09-02 20:51:52.000000000 -0700 @@ -72,7 +72,7 @@ static int init_inodecache(void) { ncp_inode_cachep = kmem_cache_create("ncp_inode_cache", sizeof(struct ncp_inode_info), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (ncp_inode_cachep == NULL) return -ENOMEM; @@ -235,8 +235,9 @@ static void ncp_set_attr(struct inode *i #if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS) static struct inode_operations ncp_symlink_inode_operations = { - .readlink = page_readlink, - .follow_link = page_follow_link, + .readlink = generic_readlink, + .follow_link = page_follow_link_light, + .put_link = page_put_link, .setattr = ncp_notify_change, }; #endif --- linux-2.6.9-rc1/fs/ncpfs/mmap.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/fs/ncpfs/mmap.c 2004-09-02 20:51:52.000000000 -0700 @@ -110,23 +110,19 @@ int ncp_mmap(struct file *file, struct v DPRINTK("ncp_mmap: called\n"); - if (!ncp_conn_valid(NCP_SERVER(inode))) { + if (!ncp_conn_valid(NCP_SERVER(inode))) return -EIO; - } + /* only PAGE_COW or read-only supported now */ if (vma->vm_flags & VM_SHARED) return -EINVAL; - if (!inode->i_sb || !S_ISREG(inode->i_mode)) - return -EACCES; /* we do not support files bigger than 4GB... We eventually supports just 4GB... */ if (((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff > (1U << (32 - PAGE_SHIFT))) return -EFBIG; - if (!IS_RDONLY(inode)) { - inode->i_atime = CURRENT_TIME; - } vma->vm_ops = &ncp_file_mmap; + file_accessed(file); return 0; } --- linux-2.6.9-rc1/fs/ncpfs/ncplib_kernel.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/fs/ncpfs/ncplib_kernel.c 2004-09-03 00:57:15.784639632 -0700 @@ -1115,22 +1115,6 @@ ncp_ClearPhysicalRecord(struct ncp_serve * from the vfat file system and hints from Petr Vandrovec. */ -inline unsigned char -ncp__tolower(struct nls_table *t, unsigned char c) -{ - unsigned char nc = t->charset2lower[c]; - - return nc ? nc : c; -} - -inline unsigned char -ncp__toupper(struct nls_table *t, unsigned char c) -{ - unsigned char nc = t->charset2upper[c]; - - return nc ? nc : c; -} - int ncp__io2vol(struct ncp_server *server, unsigned char *vname, unsigned int *vlen, const unsigned char *iname, unsigned int ilen, int cc) @@ -1346,16 +1330,3 @@ ncp__vol2io(unsigned char *iname, unsign } #endif - -inline int -ncp_strnicmp(struct nls_table *t, const unsigned char *s1, - const unsigned char *s2, int n) -{ - int i; - - for (i=0; id_inode)->nls_io) -#define ncp_tolower(t, c) ncp__tolower(t, c) -#define ncp_toupper(t, c) ncp__toupper(t, c) +#define ncp_tolower(t, c) nls_tolower(t, c) +#define ncp_toupper(t, c) nls_toupper(t, c) +#define ncp_strnicmp(t, s1, s2, len) \ + nls_strnicmp(t, s1, s2, len) #define ncp_io2vol(S,m,i,n,k,U) ncp__io2vol(S,m,i,n,k,U) #define ncp_vol2io(S,m,i,n,k,U) ncp__vol2io(S,m,i,n,k,U) @@ -159,11 +159,19 @@ int ncp__vol2io(unsigned char *, unsigne #define ncp_io2vol(S,m,i,n,k,U) ncp__io2vol(m,i,n,k,U) #define ncp_vol2io(S,m,i,n,k,U) ncp__vol2io(m,i,n,k,U) -#endif /* CONFIG_NCPFS_NLS */ -int -ncp_strnicmp(struct nls_table *, - const unsigned char *, const unsigned char *, int); +static inline int ncp_strnicmp(struct nls_table *t, const unsigned char *s1, + const unsigned char *s2, int len) +{ + while (len--) { + if (tolower(*s1++) != tolower(*s2++)) + return 1; + } + + return 0; +} + +#endif /* CONFIG_NCPFS_NLS */ #define NCP_GET_AGE(dentry) (jiffies - (dentry)->d_time) #define NCP_MAX_AGE(server) ((server)->dentry_ttl) --- linux-2.6.9-rc1/fs/nfsd/export.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/fs/nfsd/export.c 2004-09-02 20:51:52.000000000 -0700 @@ -294,6 +294,11 @@ void svc_export_request(struct cache_det qword_add(bpp, blen, exp->ex_client->name); pth = d_path(exp->ex_dentry, exp->ex_mnt, *bpp, *blen); + if (IS_ERR(pth)) { + /* is this correct? */ + (*bpp)[0] = '\n'; + return; + } qword_add(bpp, blen, pth); (*bpp)[-1] = '\n'; } --- linux-2.6.9-rc1/fs/nfsd/nfs4state.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/fs/nfsd/nfs4state.c 2004-09-03 00:57:27.101919144 -0700 @@ -236,7 +236,7 @@ static struct nfs4_client * create_client(struct xdr_netobj name) { struct nfs4_client *clp; - if(!(clp = alloc_client(name))) + if (!(clp = alloc_client(name))) goto out; INIT_LIST_HEAD(&clp->cl_idhash); INIT_LIST_HEAD(&clp->cl_strhash); @@ -268,7 +268,7 @@ copy_cred(struct svc_cred *target, struc static int cmp_name(struct xdr_netobj *n1, struct xdr_netobj *n2) { - if(!n1 || !n2) + if (!n1 || !n2) return 0; return((n1->len == n2->len) && !memcmp(n1->data, n2->data, n2->len)); } @@ -399,7 +399,7 @@ parse_ipv4(unsigned int addr_len, char * return 0; } cbaddr |= (temp << shift); - if(shift > 0) + if (shift > 0) shift -= 8; } *cbaddrp = cbaddr; @@ -411,7 +411,7 @@ parse_ipv4(unsigned int addr_len, char * return 0; } cbport |= (temp << shift); - if(shift > 0) + if (shift > 0) shift -= 8; } *cbportp = cbport; @@ -423,7 +423,7 @@ gen_callback(struct nfs4_client *clp, st { struct nfs4_callback *cb = &clp->cl_callback; - if( !(parse_ipv4(se->se_callback_addr_len, se->se_callback_addr_val, + if ( !(parse_ipv4(se->se_callback_addr_len, se->se_callback_addr_val, &cb->cb_addr, &cb->cb_port))) { printk(KERN_INFO "NFSD: BAD callback address. client will not receive delegations\n"); cb->cb_parsed = 0; @@ -431,9 +431,9 @@ gen_callback(struct nfs4_client *clp, st } cb->cb_netid.len = se->se_callback_netid_len; cb->cb_netid.data = se->se_callback_netid_val; - cb->cb_prog = se->se_callback_prog; - cb->cb_ident = se->se_callback_ident; - cb->cb_parsed = 1; + cb->cb_prog = se->se_callback_prog; + cb->cb_ident = se->se_callback_ident; + cb->cb_parsed = 1; } /* @@ -812,7 +812,7 @@ alloc_init_file(unsigned int hashval, st alloc_file++; return fp; } - return (struct nfs4_file *)NULL; + return NULL; } static void @@ -825,7 +825,7 @@ release_all_files(void) while (!list_empty(&file_hashtbl[i])) { fp = list_entry(file_hashtbl[i].next, struct nfs4_file, fi_hash); /* this should never be more than once... */ - if(!list_empty(&fp->fi_perfile)) { + if (!list_empty(&fp->fi_perfile)) { printk("ERROR: release_all_files: file %p is open, creating dangling state !!!\n",fp); } release_file(fp); @@ -839,20 +839,20 @@ alloc_stateowner(struct xdr_netobj *owne struct nfs4_stateowner *sop; if ((sop = kmalloc(sizeof(struct nfs4_stateowner),GFP_KERNEL))) { - if((sop->so_owner.data = kmalloc(owner->len, GFP_KERNEL))) { + if ((sop->so_owner.data = kmalloc(owner->len, GFP_KERNEL))) { memcpy(sop->so_owner.data, owner->data, owner->len); sop->so_owner.len = owner->len; return sop; } kfree(sop); } - return (struct nfs4_stateowner *)NULL; + return NULL; } /* should use a slab cache */ static void free_stateowner(struct nfs4_stateowner *sop) { - if(sop) { + if (sop) { kfree(sop->so_owner.data); kfree(sop); sop = NULL; @@ -867,7 +867,7 @@ alloc_init_open_stateowner(unsigned int unsigned int idhashval; if (!(sop = alloc_stateowner(&open->op_owner))) - return (struct nfs4_stateowner *)NULL; + return NULL; idhashval = ownerid_hashval(current_ownerid); INIT_LIST_HEAD(&sop->so_idhash); INIT_LIST_HEAD(&sop->so_strhash); @@ -908,7 +908,7 @@ release_stateid_lockowner(struct nfs4_st } static void -release_stateowner(struct nfs4_stateowner *sop) +unhash_stateowner(struct nfs4_stateowner *sop) { struct nfs4_stateid *stp; @@ -916,16 +916,22 @@ release_stateowner(struct nfs4_stateowne list_del(&sop->so_strhash); list_del(&sop->so_perclient); list_del(&sop->so_perlockowner); - list_del(&sop->so_close_lru); del_perclient++; while (!list_empty(&sop->so_perfilestate)) { stp = list_entry(sop->so_perfilestate.next, struct nfs4_stateid, st_perfilestate); - if(sop->so_is_open_owner) + if (sop->so_is_open_owner) release_stateid(stp, OPEN_STATE); else release_stateid(stp, LOCK_STATE); } +} + +static void +release_stateowner(struct nfs4_stateowner *sop) +{ + unhash_stateowner(sop); + list_del(&sop->so_close_lru); free_stateowner(sop); } @@ -960,7 +966,7 @@ release_stateid(struct nfs4_stateid *stp list_del_perfile++; list_del(&stp->st_perfile); list_del(&stp->st_perfilestate); - if((stp->st_vfs_set) && (flags & OPEN_STATE)) { + if ((stp->st_vfs_set) && (flags & OPEN_STATE)) { release_stateid_lockowner(stp); nfsd_close(stp->st_vfs_file); vfsclose++; @@ -986,13 +992,10 @@ void move_to_close_lru(struct nfs4_stateowner *sop) { dprintk("NFSD: move_to_close_lru nfs4_stateowner %p\n", sop); - /* remove stateowner from all other hash lists except perclient */ - list_del_init(&sop->so_idhash); - list_del_init(&sop->so_strhash); - list_del_init(&sop->so_perlockowner); - list_add_tail(&sop->so_close_lru, &close_lru); - sop->so_time = get_seconds(); + unhash_stateowner(sop); + list_add_tail(&sop->so_close_lru, &close_lru); + sop->so_time = get_seconds(); } void @@ -1030,7 +1033,7 @@ find_openstateowner_str(unsigned int has struct nfs4_stateowner *local = NULL; list_for_each_entry(local, &ownerstr_hashtbl[hashval], so_strhash) { - if(!cmp_owner_str(local, &open->op_owner, &open->op_clientid)) + if (!cmp_owner_str(local, &open->op_owner, &open->op_clientid)) continue; *op = local; return(1); @@ -1078,7 +1081,7 @@ set_access(unsigned int *access, unsigne *access = 0; for (i = 1; i < 4; i++) { - if(test_bit(i, &bmap)) + if (test_bit(i, &bmap)) *access |= i; } } @@ -1089,7 +1092,7 @@ set_deny(unsigned int *deny, unsigned lo *deny = 0; for (i = 0; i < 4; i++) { - if(test_bit(i, &bmap)) + if (test_bit(i, &bmap)) *deny |= i ; } } @@ -1278,7 +1281,7 @@ nfsd4_process_open2(struct svc_rqst *rqs /* Search for conflicting share reservations */ status = nfserr_share_denied; list_for_each_entry(stq, &fp->fi_perfile, st_perfile) { - if(stq->st_stateowner == sop) { + if (stq->st_stateowner == sop) { stp = stq; continue; } @@ -1456,7 +1459,8 @@ nfs4_laundromat(void) } dprintk("NFSD: purging unused open stateowner (so_id %d)\n", sop->so_id); - release_stateowner(sop); + list_del(&sop->so_close_lru); + free_stateowner(sop); } if (clientid_val < NFSD_LAUNDROMAT_MINTIMEOUT) clientid_val = NFSD_LAUNDROMAT_MINTIMEOUT; @@ -1484,7 +1488,7 @@ find_openstateowner_id(u32 st_id, int fl dprintk("NFSD: find_openstateowner_id %d\n", st_id); if (flags & CLOSE_STATE) { list_for_each_entry(local, &close_lru, so_close_lru) { - if(local->so_id == st_id) + if (local->so_id == st_id) return local; } } @@ -1888,16 +1892,16 @@ find_stateid(stateid_t *stid, int flags) if ((flags & LOCK_STATE) || (flags & RDWR_STATE)) { hashval = stateid_hashval(st_id, f_id); list_for_each_entry(local, &lockstateid_hashtbl[hashval], st_hash) { - if((local->st_stateid.si_stateownerid == st_id) && - (local->st_stateid.si_fileid == f_id)) + if ((local->st_stateid.si_stateownerid == st_id) && + (local->st_stateid.si_fileid == f_id)) return local; } } if ((flags & OPEN_STATE) || (flags & RDWR_STATE)) { hashval = stateid_hashval(st_id, f_id); list_for_each_entry(local, &stateid_hashtbl[hashval], st_hash) { - if((local->st_stateid.si_stateownerid == st_id) && - (local->st_stateid.si_fileid == f_id)) + if ((local->st_stateid.si_stateownerid == st_id) && + (local->st_stateid.si_fileid == f_id)) return local; } } else @@ -1946,9 +1950,10 @@ static inline void nfs4_set_lock_denied(struct file_lock *fl, struct nfsd4_lock_denied *deny) { struct nfs4_stateowner *sop = (struct nfs4_stateowner *) fl->fl_owner; + unsigned int hval = lockownerid_hashval(sop->so_id); deny->ld_sop = NULL; - if (nfs4_verify_lock_stateowner(sop, fl->fl_pid)) + if (nfs4_verify_lock_stateowner(sop, hval)) deny->ld_sop = sop; deny->ld_start = fl->fl_start; deny->ld_length = ~(u64)0; @@ -1967,7 +1972,7 @@ find_lockstateowner(struct xdr_netobj *o for (i = 0; i < LOCK_HASH_SIZE; i++) { list_for_each_entry(local, &lock_ownerid_hashtbl[i], so_idhash) { - if(!cmp_owner_str(local, owner, clid)) + if (!cmp_owner_str(local, owner, clid)) continue; return local; } @@ -1980,7 +1985,7 @@ find_lockstateowner_str(unsigned int has struct nfs4_stateowner *local = NULL; list_for_each_entry(local, &lock_ownerstr_hashtbl[hashval], so_strhash) { - if(!cmp_owner_str(local, owner, clid)) + if (!cmp_owner_str(local, owner, clid)) continue; *op = local; return(1); @@ -2005,7 +2010,7 @@ alloc_init_lock_stateowner(unsigned int unsigned int idhashval; if (!(sop = alloc_stateowner(&lock->lk_new_owner))) - return (struct nfs4_stateowner *)NULL; + return NULL; idhashval = lockownerid_hashval(current_ownerid); INIT_LIST_HEAD(&sop->so_idhash); INIT_LIST_HEAD(&sop->so_strhash); @@ -2068,7 +2073,7 @@ int check_lock_length(u64 offset, u64 length) { return ((length == 0) || ((length != ~(u64)0) && - LOFF_OVERFLOW(offset, length))); + LOFF_OVERFLOW(offset, length))); } /* @@ -2153,8 +2158,10 @@ nfsd4_lock(struct svc_rqst *rqstp, struc if (!(lock->lk_stateowner = alloc_init_lock_stateowner(strhashval, open_sop->so_client, open_stp, lock))) goto out; if ((lock_stp = alloc_init_lock_stateid(lock->lk_stateowner, - fp, open_stp)) == NULL) + fp, open_stp)) == NULL) { + release_stateowner(lock->lk_stateowner); goto out; + } /* bump the open seqid used to create the lock */ open_sop->so_seqid++; } else { @@ -2190,7 +2197,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struc goto out; } file_lock.fl_owner = (fl_owner_t) lock->lk_stateowner; - file_lock.fl_pid = lockownerid_hashval(lock->lk_stateowner->so_id); + file_lock.fl_pid = current->tgid; file_lock.fl_file = filp; file_lock.fl_flags = FL_POSIX; @@ -2210,7 +2217,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struc status = posix_lock_file(filp, &file_lock); if (file_lock.fl_ops && file_lock.fl_ops->fl_release_private) file_lock.fl_ops->fl_release_private(&file_lock); - dprintk("NFSD: nfsd4_lock: posix_test_lock passed. posix_lock_file status %d\n",status); + dprintk("NFSD: nfsd4_lock: posix_lock_file status %d\n",status); switch (-status) { case 0: /* success! */ update_stateid(&lock_stp->st_stateid); @@ -2261,7 +2268,6 @@ int nfsd4_lockt(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lockt *lockt) { struct inode *inode; - struct nfs4_stateowner *sop; struct file file; struct file_lock file_lock; struct file_lock *conflicting_lock; @@ -2313,14 +2319,9 @@ nfsd4_lockt(struct svc_rqst *rqstp, stru find_lockstateowner_str(strhashval, &lockt->lt_owner, &lockt->lt_clientid, &lockt->lt_stateowner); - sop = lockt->lt_stateowner; - if (sop) { - file_lock.fl_owner = (fl_owner_t) sop; - file_lock.fl_pid = lockownerid_hashval(sop->so_id); - } else { - file_lock.fl_owner = NULL; - file_lock.fl_pid = 0; - } + if (lockt->lt_stateowner) + file_lock.fl_owner = (fl_owner_t)lockt->lt_stateowner; + file_lock.fl_pid = current->tgid; file_lock.fl_flags = FL_POSIX; file_lock.fl_start = lockt->lt_offset; @@ -2380,7 +2381,7 @@ nfsd4_locku(struct svc_rqst *rqstp, stru locks_init_lock(&file_lock); file_lock.fl_type = F_UNLCK; file_lock.fl_owner = (fl_owner_t) locku->lu_stateowner; - file_lock.fl_pid = lockownerid_hashval(locku->lu_stateowner->so_id); + file_lock.fl_pid = current->tgid; file_lock.fl_file = filp; file_lock.fl_flags = FL_POSIX; file_lock.fl_start = locku->lu_offset; @@ -2459,7 +2460,7 @@ nfsd4_release_lockowner(struct svc_rqst nfs4_lock_state(); - status = nfs_ok; + status = nfs_ok; local = find_lockstateowner(owner, clid); if (local) { struct nfs4_stateid *stp; @@ -2469,7 +2470,7 @@ nfsd4_release_lockowner(struct svc_rqst status = nfserr_locks_held; list_for_each_entry(stp, &local->so_perfilestate, st_perfilestate) { - if(stp->st_vfs_set) { + if (stp->st_vfs_set) { if (check_for_locks(stp->st_vfs_file, local)) goto out; } @@ -2496,7 +2497,7 @@ alloc_reclaim(int namelen) kfree(crp); return NULL; } - return crp; + return crp; } /* @@ -2566,7 +2567,7 @@ nfs4_find_reclaim_client(clientid_t *cli strhashval = clientstr_hashval(client->cl_name.data, client->cl_name.len); list_for_each_entry(crp, &reclaim_str_hashtbl[strhashval], cr_strhash) { - if(cmp_name(&crp->cr_name, &client->cl_name)) { + if (cmp_name(&crp->cr_name, &client->cl_name)) { return crp; } } --- linux-2.6.9-rc1/fs/nfsd/nfs4xdr.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/fs/nfsd/nfs4xdr.c 2004-09-03 00:57:26.465015968 -0700 @@ -1414,6 +1414,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, s u64 dummy64; u32 *p = buffer; int status; + int aclsupport = 0; struct nfs4_acl *acl = NULL; BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1); @@ -1437,12 +1438,16 @@ nfsd4_encode_fattr(struct svc_fh *fhp, s goto out; fhp = &tempfh; } - if (bmval0 & FATTR4_WORD0_ACL) { + if (bmval0 & (FATTR4_WORD0_ACL | FATTR4_WORD0_ACLSUPPORT + | FATTR4_WORD0_SUPPORTED_ATTRS)) { status = nfsd4_get_nfs4_acl(rqstp, dentry, &acl); - if (status == -EOPNOTSUPP) - bmval0 &= ~FATTR4_WORD0_ACL; - else if (status < 0) - goto out_nfserr; + aclsupport = (status == 0); + if (bmval0 & FATTR4_WORD0_ACL) { + if (status == -EOPNOTSUPP) + bmval0 &= ~FATTR4_WORD0_ACL; + else if (status != 0) + goto out_nfserr; + } } if ((buflen -= 16) < 0) goto out_resource; @@ -1456,9 +1461,9 @@ nfsd4_encode_fattr(struct svc_fh *fhp, s if ((buflen -= 12) < 0) goto out_resource; WRITE32(2); - /* XXX Should depend on exported filesystem (e.g. - * for acl support) */ - WRITE32(NFSD_SUPPORTED_ATTRS_WORD0); + WRITE32(aclsupport ? + NFSD_SUPPORTED_ATTRS_WORD0 : + NFSD_SUPPORTED_ATTRS_WORD0 & ~FATTR4_WORD0_ACL); WRITE32(NFSD_SUPPORTED_ATTRS_WORD1); } if (bmval0 & FATTR4_WORD0_TYPE) { @@ -1565,8 +1570,8 @@ out_acl: if (bmval0 & FATTR4_WORD0_ACLSUPPORT) { if ((buflen -= 4) < 0) goto out_resource; - /* XXX: should depend on exported filesystem: */ - WRITE32(ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL); + WRITE32(aclsupport ? + ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0); } if (bmval0 & FATTR4_WORD0_CANSETTIME) { if ((buflen -= 4) < 0) --- linux-2.6.9-rc1/fs/nfsd/nfsctl.c 2004-08-24 00:02:22.000000000 -0700 +++ 25/fs/nfsd/nfsctl.c 2004-09-02 20:51:52.000000000 -0700 @@ -80,101 +80,31 @@ static ssize_t (*write_op[])(struct file [NFSD_Leasetime] = write_leasetime, }; -/* an argresp is stored in an allocated page and holds the - * size of the argument or response, along with its content - */ -struct argresp { - ssize_t size; - char data[0]; -}; - -/* - * transaction based IO methods. - * The file expects a single write which triggers the transaction, and then - * possibly a read which collects the result - which is stored in a - * file-local buffer. - */ -static ssize_t TA_write(struct file *file, const char __user *buf, size_t size, loff_t *pos) +static ssize_t nfsctl_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos) { ino_t ino = file->f_dentry->d_inode->i_ino; - struct argresp *ar; - ssize_t rv = 0; + char *data; + ssize_t rv; if (ino >= sizeof(write_op)/sizeof(write_op[0]) || !write_op[ino]) return -EINVAL; - if (file->private_data) - return -EINVAL; /* only one write allowed per open */ - if (size > PAGE_SIZE - sizeof(struct argresp)) - return -EFBIG; - ar = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!ar) - return -ENOMEM; - ar->size = 0; - down(&file->f_dentry->d_inode->i_sem); - if (file->private_data) - rv = -EINVAL; - else - file->private_data = ar; - up(&file->f_dentry->d_inode->i_sem); - if (rv) { - kfree(ar); - return rv; - } - if (copy_from_user(ar->data, buf, size)) - return -EFAULT; - - rv = write_op[ino](file, ar->data, size); + data = simple_transaction_get(file, buf, size); + if (IS_ERR(data)) + return PTR_ERR(data); + + rv = write_op[ino](file, data, size); if (rv>0) { - ar->size = rv; + simple_transaction_set(file, rv); rv = size; } return rv; } - -static ssize_t TA_read(struct file *file, char __user *buf, size_t size, loff_t *pos) -{ - struct argresp *ar; - ssize_t rv = 0; - - if (file->private_data == NULL) - rv = TA_write(file, buf, 0, pos); - if (rv < 0) - return rv; - - ar = file->private_data; - if (!ar) - return 0; - if (*pos >= ar->size) - return 0; - if (*pos + size > ar->size) - size = ar->size - *pos; - if (copy_to_user(buf, ar->data + *pos, size)) - return -EFAULT; - *pos += size; - return size; -} - -static int TA_open(struct inode *inode, struct file *file) -{ - file->private_data = NULL; - return 0; -} - -static int TA_release(struct inode *inode, struct file *file) -{ - void *p = file->private_data; - file->private_data = NULL; - kfree(p); - return 0; -} - static struct file_operations transaction_ops = { - .write = TA_write, - .read = TA_read, - .open = TA_open, - .release = TA_release, + .write = nfsctl_transaction_write, + .read = simple_transaction_read, + .release = simple_transaction_release, }; extern struct seq_operations nfs_exports_op; @@ -366,7 +296,7 @@ static ssize_t write_filehandle(struct f if (len) return len; - mesg = buf; len = PAGE_SIZE-sizeof(struct argresp); + mesg = buf; len = SIMPLE_TRANSACTION_LIMIT; qword_addhex(&mesg, &len, (char*)&fh.fh_base, fh.fh_size); mesg[-1] = '\n'; return mesg - buf; --- linux-2.6.9-rc1/fs/nfsd/nfsfh.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/fs/nfsd/nfsfh.c 2004-09-03 00:57:26.339035120 -0700 @@ -190,10 +190,10 @@ fh_verify(struct svc_rqst *rqstp, struct dentry = dget(exp->ex_dentry); else { struct export_operations *nop = exp->ex_mnt->mnt_sb->s_export_op; - dentry = CALL(nop,decode_fh)(exp->ex_mnt->mnt_sb, - datap, data_left, - fileid_type, - nfsd_acceptable, exp); + dentry = CALL(nop,decode_fh)(exp->ex_mnt->mnt_sb, + datap, data_left, + fileid_type, + nfsd_acceptable, exp); } if (dentry == NULL) goto out; --- linux-2.6.9-rc1/fs/nfsd/nfsproc.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/fs/nfsd/nfsproc.c 2004-09-03 00:57:26.080074488 -0700 @@ -586,6 +586,7 @@ nfserrno (int errno) { nfserr_dquot, -EDQUOT }, #endif { nfserr_stale, -ESTALE }, + { nfserr_jukebox, -EWOULDBLOCK }, { nfserr_jukebox, -ETIMEDOUT }, { nfserr_dropit, -EAGAIN }, { nfserr_dropit, -ENOMEM }, --- linux-2.6.9-rc1/fs/nfsd/nfssvc.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/fs/nfsd/nfssvc.c 2004-09-03 00:57:26.081074336 -0700 @@ -328,6 +328,8 @@ nfsd_dispatch(struct svc_rqst *rqstp, u3 /* Now call the procedure handler, and encode NFS status. */ nfserr = proc->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp); + if (nfserr == nfserr_jukebox && rqstp->rq_vers == 2) + nfserr = nfserr_dropit; if (nfserr == nfserr_dropit) { dprintk("nfsd: Dropping request due to malloc failure!\n"); nfsd_cache_update(rqstp, RC_NOCACHE, NULL); --- linux-2.6.9-rc1/fs/nfsd/vfs.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/fs/nfsd/vfs.c 2004-09-03 00:57:26.082074184 -0700 @@ -303,8 +303,8 @@ nfsd_setattr(struct svc_rqst *rqstp, str * If we are changing the size of the file, then * we need to break all leases. */ - err = break_lease(inode, FMODE_WRITE); - if (err) + err = break_lease(inode, FMODE_WRITE | O_NONBLOCK); + if (err) /* ENOMEM or EWOULDBLOCK */ goto out_nfserr; err = get_write_access(inode); @@ -669,8 +669,8 @@ nfsd_open(struct svc_rqst *rqstp, struct * Check to see if there are any leases on this file. * This may block while leases are broken. */ - err = break_lease(inode, (access & MAY_WRITE) ? FMODE_WRITE : 0); - if (err) + err = break_lease(inode, O_NONBLOCK | ((access & MAY_WRITE) ? FMODE_WRITE : 0)); + if (err) /* NOMEM or WOULDBLOCK */ goto out_nfserr; if (access & MAY_WRITE) { --- linux-2.6.9-rc1/fs/nfs/inode.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/fs/nfs/inode.c 2004-09-02 20:51:52.000000000 -0700 @@ -1377,9 +1377,6 @@ static struct super_block *nfs_get_sb(st /* Zero out the NFS state stuff */ init_nfsv4_state(server); - root = &server->fh; - nfs_copy_fh(root, (struct nfs_fh *) &data->root); - if (data->version != NFS_MOUNT_VERSION) { printk("nfs warning: mount version %s than kernel\n", data->version < NFS_MOUNT_VERSION ? "older" : "newer"); @@ -1389,18 +1386,25 @@ static struct super_block *nfs_get_sb(st data->bsize = 0; if (data->version < 4) { data->flags &= ~NFS_MOUNT_VER3; - root->size = NFS2_FHSIZE; - memcpy(root->data, data->old_root.data, NFS2_FHSIZE); + data->root.size = NFS2_FHSIZE; + memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE); } if (data->version < 5) data->flags &= ~NFS_MOUNT_SECFLAVOUR; } + root = &server->fh; + if (data->flags & NFS_MOUNT_VER3) + root->size = data->root.size; + else + root->size = NFS2_FHSIZE; if (root->size > sizeof(root->data)) { printk("nfs_get_sb: invalid root filehandle\n"); kfree(server); return ERR_PTR(-EINVAL); } + memcpy(root->data, data->root.data, root->size); + /* We now require that the mount process passes the remote address */ memcpy(&server->addr, &data->addr, sizeof(server->addr)); if (server->addr.sin_addr.s_addr == INADDR_ANY) { @@ -1859,7 +1863,7 @@ int nfs_init_inodecache(void) { nfs_inode_cachep = kmem_cache_create("nfs_inode_cache", sizeof(struct nfs_inode), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (nfs_inode_cachep == NULL) return -ENOMEM; --- linux-2.6.9-rc1/fs/nfs/nfs2xdr.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/fs/nfs/nfs2xdr.c 2004-09-02 20:51:52.000000000 -0700 @@ -57,7 +57,7 @@ extern int nfs_stat_to_errno(int stat) #define NFS_attrstat_sz (1+NFS_fattr_sz) #define NFS_diropres_sz (1+NFS_fhandle_sz+NFS_fattr_sz) -#define NFS_readlinkres_sz (1) +#define NFS_readlinkres_sz (2) #define NFS_readres_sz (1+NFS_fattr_sz+1) #define NFS_writeres_sz (NFS_attrstat_sz) #define NFS_stat_sz (1) @@ -530,7 +530,6 @@ static int nfs_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_readlinkargs *args) { struct rpc_auth *auth = req->rq_task->tk_auth; - unsigned int count = args->count - 5; unsigned int replen; p = xdr_encode_fhandle(p, args->fh); @@ -538,7 +537,7 @@ nfs_xdr_readlinkargs(struct rpc_rqst *re /* Inline the page array */ replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readlinkres_sz) << 2; - xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count); + xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen); return 0; } @@ -550,32 +549,38 @@ nfs_xdr_readlinkres(struct rpc_rqst *req { struct xdr_buf *rcvbuf = &req->rq_rcv_buf; struct kvec *iov = rcvbuf->head; - unsigned int hdrlen; - u32 *strlen, len; - char *string; + int hdrlen, len, recvd; + char *kaddr; int status; if ((status = ntohl(*p++))) return -nfs_stat_to_errno(status); + /* Convert length of symlink */ + len = ntohl(*p++); + if (len >= rcvbuf->page_len || len <= 0) { + dprintk(KERN_WARNING "nfs: server returned giant symlink!\n"); + return -ENAMETOOLONG; + } hdrlen = (u8 *) p - (u8 *) iov->iov_base; - if (iov->iov_len > hdrlen) { + if (iov->iov_len < hdrlen) { + printk(KERN_WARNING "NFS: READLINK reply header overflowed:" + "length %d > %Zu\n", hdrlen, iov->iov_len); + return -errno_NFSERR_IO; + } else if (iov->iov_len != hdrlen) { dprintk("NFS: READLINK header is short. iovec will be shifted.\n"); xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen); } - - strlen = (u32*)kmap_atomic(rcvbuf->pages[0], KM_USER0); - /* Convert length of symlink */ - len = ntohl(*strlen); - if (len > rcvbuf->page_len) { - dprintk(KERN_WARNING "nfs: server returned giant symlink!\n"); - kunmap_atomic(strlen, KM_USER0); - return -ENAMETOOLONG; + recvd = req->rq_rcv_buf.len - hdrlen; + if (recvd < len) { + printk(KERN_WARNING "NFS: server cheating in readlink reply: " + "count %u > recvd %u\n", len, recvd); + return -EIO; } - *strlen = len; + /* NULL terminate the string we got */ - string = (char *)(strlen + 1); - string[len] = '\0'; - kunmap_atomic(strlen, KM_USER0); + kaddr = (char *)kmap_atomic(rcvbuf->pages[0], KM_USER0); + kaddr[len+rcvbuf->page_base] = '\0'; + kunmap_atomic(kaddr, KM_USER0); return 0; } --- linux-2.6.9-rc1/fs/nfs/nfs3proc.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/fs/nfs/nfs3proc.c 2004-09-02 20:51:52.000000000 -0700 @@ -202,13 +202,14 @@ static int nfs3_proc_access(struct inode return status; } -static int -nfs3_proc_readlink(struct inode *inode, struct page *page) +static int nfs3_proc_readlink(struct inode *inode, struct page *page, + unsigned int pgbase, unsigned int pglen) { struct nfs_fattr fattr; struct nfs3_readlinkargs args = { .fh = NFS_FH(inode), - .count = PAGE_CACHE_SIZE, + .pgbase = pgbase, + .pglen = pglen, .pages = &page }; int status; @@ -417,20 +418,21 @@ nfs3_proc_remove(struct inode *dir, stru static int nfs3_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, struct qstr *name) { - struct nfs3_diropargs *arg; - struct nfs_fattr *res; + struct unlinkxdr { + struct nfs3_diropargs arg; + struct nfs_fattr res; + } *ptr; - arg = (struct nfs3_diropargs *)kmalloc(sizeof(*arg)+sizeof(*res), GFP_KERNEL); - if (!arg) + ptr = (struct unlinkxdr *)kmalloc(sizeof(*ptr), GFP_KERNEL); + if (!ptr) return -ENOMEM; - res = (struct nfs_fattr*)(arg + 1); - arg->fh = NFS_FH(dir->d_inode); - arg->name = name->name; - arg->len = name->len; - res->valid = 0; + ptr->arg.fh = NFS_FH(dir->d_inode); + ptr->arg.name = name->name; + ptr->arg.len = name->len; + ptr->res.valid = 0; msg->rpc_proc = &nfs3_procedures[NFS3PROC_REMOVE]; - msg->rpc_argp = arg; - msg->rpc_resp = res; + msg->rpc_argp = &ptr->arg; + msg->rpc_resp = &ptr->res; return 0; } --- linux-2.6.9-rc1/fs/nfs/nfs3xdr.c 2004-08-24 00:03:49.000000000 -0700 +++ 25/fs/nfs/nfs3xdr.c 2004-09-02 20:51:52.000000000 -0700 @@ -67,7 +67,7 @@ extern int nfs_stat_to_errno(int); #define NFS3_wccstat_sz (1+NFS3_wcc_data_sz) #define NFS3_lookupres_sz (1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz)) #define NFS3_accessres_sz (1+NFS3_post_op_attr_sz+1) -#define NFS3_readlinkres_sz (1+NFS3_post_op_attr_sz) +#define NFS3_readlinkres_sz (1+NFS3_post_op_attr_sz+1) #define NFS3_readres_sz (1+NFS3_post_op_attr_sz+3) #define NFS3_writeres_sz (1+NFS3_wcc_data_sz+4) #define NFS3_createres_sz (1+NFS3_fh_sz+NFS3_post_op_attr_sz+NFS3_wcc_data_sz) @@ -698,7 +698,6 @@ static int nfs3_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_readlinkargs *args) { struct rpc_auth *auth = req->rq_task->tk_auth; - unsigned int count = args->count - 5; unsigned int replen; p = xdr_encode_fhandle(p, args->fh); @@ -706,7 +705,7 @@ nfs3_xdr_readlinkargs(struct rpc_rqst *r /* Inline the page array */ replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readlinkres_sz) << 2; - xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count); + xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen); return 0; } @@ -718,9 +717,8 @@ nfs3_xdr_readlinkres(struct rpc_rqst *re { struct xdr_buf *rcvbuf = &req->rq_rcv_buf; struct kvec *iov = rcvbuf->head; - unsigned int hdrlen; - u32 *strlen, len; - char *string; + int hdrlen, len, recvd; + char *kaddr; int status; status = ntohl(*p++); @@ -729,25 +727,33 @@ nfs3_xdr_readlinkres(struct rpc_rqst *re if (status != 0) return -nfs_stat_to_errno(status); + /* Convert length of symlink */ + len = ntohl(*p++); + if (len >= rcvbuf->page_len || len <= 0) { + dprintk(KERN_WARNING "nfs: server returned giant symlink!\n"); + return -ENAMETOOLONG; + } + hdrlen = (u8 *) p - (u8 *) iov->iov_base; - if (iov->iov_len > hdrlen) { + if (iov->iov_len < hdrlen) { + printk(KERN_WARNING "NFS: READLINK reply header overflowed:" + "length %d > %Zu\n", hdrlen, iov->iov_len); + return -errno_NFSERR_IO; + } else if (iov->iov_len != hdrlen) { dprintk("NFS: READLINK header is short. iovec will be shifted.\n"); xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen); } - - strlen = (u32*)kmap_atomic(rcvbuf->pages[0], KM_USER0); - /* Convert length of symlink */ - len = ntohl(*strlen); - if (len > rcvbuf->page_len) { - dprintk(KERN_WARNING "nfs: server returned giant symlink!\n"); - kunmap_atomic(strlen, KM_USER0); - return -ENAMETOOLONG; + recvd = req->rq_rcv_buf.len - hdrlen; + if (recvd < len) { + printk(KERN_WARNING "NFS: server cheating in readlink reply: " + "count %u > recvd %u\n", len, recvd); + return -EIO; } - *strlen = len; + /* NULL terminate the string we got */ - string = (char *)(strlen + 1); - string[len] = '\0'; - kunmap_atomic(strlen, KM_USER0); + kaddr = (char*)kmap_atomic(rcvbuf->pages[0], KM_USER0); + kaddr[len+rcvbuf->page_base] = '\0'; + kunmap_atomic(kaddr, KM_USER0); return 0; } --- linux-2.6.9-rc1/fs/nfs/nfs4proc.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/fs/nfs/nfs4proc.c 2004-09-02 20:51:52.000000000 -0700 @@ -1173,11 +1173,13 @@ static int nfs4_proc_access(struct inode * Both of these changes to the XDR layer would in fact be quite * minor, but I decided to leave them for a subsequent patch. */ -static int _nfs4_proc_readlink(struct inode *inode, struct page *page) +static int _nfs4_proc_readlink(struct inode *inode, struct page *page, + unsigned int pgbase, unsigned int pglen) { struct nfs4_readlink args = { .fh = NFS_FH(inode), - .count = PAGE_CACHE_SIZE, + .pgbase = pgbase, + .pglen = pglen, .pages = &page, }; struct rpc_message msg = { @@ -1189,13 +1191,14 @@ static int _nfs4_proc_readlink(struct in return rpc_call_sync(NFS_CLIENT(inode), &msg, 0); } -static int nfs4_proc_readlink(struct inode *inode, struct page *page) +static int nfs4_proc_readlink(struct inode *inode, struct page *page, + unsigned int pgbase, unsigned int pglen) { struct nfs4_exception exception = { }; int err; do { err = nfs4_handle_exception(NFS_SERVER(inode), - _nfs4_proc_readlink(inode, page), + _nfs4_proc_readlink(inode, page, pgbase, pglen), &exception); } while (exception.retry); return err; --- linux-2.6.9-rc1/fs/nfs/nfs4xdr.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/fs/nfs/nfs4xdr.c 2004-09-02 20:51:52.000000000 -0700 @@ -1027,7 +1027,6 @@ static int encode_readdir(struct xdr_str static int encode_readlink(struct xdr_stream *xdr, const struct nfs4_readlink *readlink, struct rpc_rqst *req) { struct rpc_auth *auth = req->rq_task->tk_auth; - unsigned int count = readlink->count - 5; unsigned int replen; uint32_t *p; @@ -1036,10 +1035,11 @@ static int encode_readlink(struct xdr_st /* set up reply kvec * toplevel_status + taglen + rescount + OP_PUTFH + status - * + OP_READLINK + status = 7 + * + OP_READLINK + status + string length = 8 */ - replen = (RPC_REPHDRSIZE + auth->au_rslack + 7) << 2; - xdr_inline_pages(&req->rq_rcv_buf, replen, readlink->pages, 0, count); + replen = (RPC_REPHDRSIZE + auth->au_rslack + 8) << 2; + xdr_inline_pages(&req->rq_rcv_buf, replen, readlink->pages, + readlink->pgbase, readlink->pglen); return 0; } @@ -3053,21 +3053,30 @@ static int decode_readlink(struct xdr_st { struct xdr_buf *rcvbuf = &req->rq_rcv_buf; struct kvec *iov = rcvbuf->head; - uint32_t *strlen; - unsigned int hdrlen, len; - char *string; + int hdrlen, len, recvd; + uint32_t *p; + char *kaddr; int status; status = decode_op_hdr(xdr, OP_READLINK); if (status) return status; + /* Convert length of symlink */ + READ_BUF(4); + READ32(len); + if (len >= rcvbuf->page_len || len <= 0) { + dprintk(KERN_WARNING "nfs: server returned giant symlink!\n"); + return -ENAMETOOLONG; + } hdrlen = (char *) xdr->p - (char *) iov->iov_base; - if (iov->iov_len > hdrlen) { - dprintk("NFS: READLINK header is short. iovec will be shifted.\n"); - xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen); - + recvd = req->rq_rcv_buf.len - hdrlen; + if (recvd < len) { + printk(KERN_WARNING "NFS: server cheating in readlink reply: " + "count %u > recvd %u\n", len, recvd); + return -EIO; } + xdr_read_pages(xdr, len); /* * The XDR encode routine has set things up so that * the link text will be copied directly into the @@ -3075,18 +3084,9 @@ static int decode_readlink(struct xdr_st * and and null-terminate the text (the VFS expects * null-termination). */ - strlen = (uint32_t *) kmap_atomic(rcvbuf->pages[0], KM_USER0); - len = ntohl(*strlen); - if (len > rcvbuf->page_len) { - dprintk(KERN_WARNING "nfs: server returned giant symlink!\n"); - kunmap_atomic(strlen, KM_USER0); - return -ENAMETOOLONG; - } - *strlen = len; - - string = (char *)(strlen + 1); - string[len] = '\0'; - kunmap_atomic(strlen, KM_USER0); + kaddr = (char *)kmap_atomic(rcvbuf->pages[0], KM_USER0); + kaddr[len+rcvbuf->page_base] = '\0'; + kunmap_atomic(kaddr, KM_USER0); return 0; } --- linux-2.6.9-rc1/fs/nfs/nfsroot.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/fs/nfs/nfsroot.c 2004-09-02 20:51:52.000000000 -0700 @@ -495,8 +495,10 @@ static int __init root_nfs_get_handle(vo if (status < 0) printk(KERN_ERR "Root-NFS: Server returned error %d " "while mounting %s\n", status, nfs_path); - else - nfs_copy_fh(nfs_data.root, fh); + else { + nfs_data.root.size = fh.size; + memcpy(nfs_data.root.data, fh.data, fh.size); + } return status; } --- linux-2.6.9-rc1/fs/nfs/proc.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/fs/nfs/proc.c 2004-09-02 20:51:52.000000000 -0700 @@ -140,12 +140,13 @@ nfs_proc_lookup(struct inode *dir, struc return status; } -static int -nfs_proc_readlink(struct inode *inode, struct page *page) +static int nfs_proc_readlink(struct inode *inode, struct page *page, + unsigned int pgbase, unsigned int pglen) { struct nfs_readlinkargs args = { .fh = NFS_FH(inode), - .count = PAGE_CACHE_SIZE, + .pgbase = pgbase, + .pglen = pglen, .pages = &page }; int status; @@ -390,6 +391,7 @@ nfs_proc_symlink(struct inode *dir, stru return -ENAMETOOLONG; dprintk("NFS call symlink %s -> %s\n", name->name, path->name); fattr->valid = 0; + fhandle->size = 0; status = rpc_call(NFS_CLIENT(dir), NFSPROC_SYMLINK, &arg, NULL, 0); dprintk("NFS reply symlink: %d\n", status); return status; --- linux-2.6.9-rc1/fs/nfs/symlink.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/fs/nfs/symlink.c 2004-09-02 20:51:52.000000000 -0700 @@ -23,20 +23,30 @@ #include #include #include +#include /* Symlink caching in the page cache is even more simplistic * and straight-forward than readdir caching. + * + * At the beginning of the page we store pointer to struct page in question, + * simplifying nfs_put_link() (if inode got invalidated we can't find the page + * to be freed via pagecache lookup). + * The NUL-terminated string follows immediately thereafter. */ + +struct nfs_symlink { + struct page *page; + char body[0]; +}; + static int nfs_symlink_filler(struct inode *inode, struct page *page) { + const unsigned int pgbase = offsetof(struct nfs_symlink, body); + const unsigned int pglen = PAGE_SIZE - pgbase; int error; - /* We place the length at the beginning of the page, - * in host byte order, followed by the string. The - * XDR response verification will NULL terminate it. - */ lock_kernel(); - error = NFS_PROTO(inode)->readlink(inode, page); + error = NFS_PROTO(inode)->readlink(inode, page, pgbase, pglen); unlock_kernel(); if (error < 0) goto error; @@ -50,61 +60,58 @@ error: return -EIO; } -static char *nfs_getlink(struct inode *inode, struct page **ppage) +static int nfs_follow_link(struct dentry *dentry, struct nameidata *nd) { + struct inode *inode = dentry->d_inode; struct page *page; - u32 *p; - - page = ERR_PTR(nfs_revalidate_inode(NFS_SERVER(inode), inode)); - if (page) + struct nfs_symlink *p; + void *err = ERR_PTR(nfs_revalidate_inode(NFS_SERVER(inode), inode)); + if (err) goto read_failed; page = read_cache_page(&inode->i_data, 0, (filler_t *)nfs_symlink_filler, inode); - if (IS_ERR(page)) + if (IS_ERR(page)) { + err = page; goto read_failed; - if (!PageUptodate(page)) + } + if (!PageUptodate(page)) { + err = ERR_PTR(-EIO); goto getlink_read_error; - *ppage = page; + } p = kmap(page); - return (char*)(p+1); + p->page = page; + nd_set_link(nd, p->body); + return 0; getlink_read_error: page_cache_release(page); - page = ERR_PTR(-EIO); read_failed: - return (char*)page; + nd_set_link(nd, err); + return 0; } -static int nfs_readlink(struct dentry *dentry, char __user *buffer, int buflen) +static void nfs_put_link(struct dentry *dentry, struct nameidata *nd) { - struct inode *inode = dentry->d_inode; - struct page *page = NULL; - int res = vfs_readlink(dentry,buffer,buflen,nfs_getlink(inode,&page)); - if (page) { - kunmap(page); - page_cache_release(page); - } - return res; -} + char *s = nd_get_link(nd); + if (!IS_ERR(s)) { + struct nfs_symlink *p; + struct page *page; + + p = container_of(s, struct nfs_symlink, body[0]); + page = p->page; -static int nfs_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct inode *inode = dentry->d_inode; - struct page *page = NULL; - int res = vfs_follow_link(nd, nfs_getlink(inode,&page)); - if (page) { kunmap(page); page_cache_release(page); } - return res; } /* * symlinks can't do much... */ struct inode_operations nfs_symlink_inode_operations = { - .readlink = nfs_readlink, + .readlink = generic_readlink, .follow_link = nfs_follow_link, + .put_link = nfs_put_link, .getattr = nfs_getattr, .setattr = nfs_setattr, }; --- linux-2.6.9-rc1/fs/nfs/unlink.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/fs/nfs/unlink.c 2004-09-03 00:56:48.633767192 -0700 @@ -215,7 +215,6 @@ nfs_complete_unlink(struct dentry *dentr spin_lock(&dentry->d_lock); dentry->d_flags &= ~DCACHE_NFSFS_RENAMED; spin_unlock(&dentry->d_lock); - if (data->task.tk_rpcwait == &nfs_delete_queue) - rpc_wake_up_task(&data->task); + rpc_wake_up_task(&data->task); nfs_put_unlinkdata(data); } --- linux-2.6.9-rc1/fs/nls/nls_cp932.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/fs/nls/nls_cp932.c 2004-09-03 00:57:13.488988624 -0700 @@ -1619,6 +1619,33 @@ static wchar_t *page_charset2uni[256] = NULL, NULL, c2u_FA, c2u_FB, c2u_FC, NULL, NULL, NULL, }; +static unsigned char u2c_00hi[256 - 0xA0][2] = { + {0x00, 0x00}, {0x00, 0x00}, {0x81, 0x91}, {0x81, 0x92},/* 0xA0-0xA3 */ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x81, 0x98},/* 0xA4-0xA7 */ + {0x81, 0x4E}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},/* 0xA8-0xAB */ + {0x81, 0xCA}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},/* 0xAC-0xAF */ + {0x81, 0x8B}, {0x81, 0x7D}, {0x00, 0x00}, {0x00, 0x00},/* 0xB0-0xB3 */ + {0x81, 0x4C}, {0x00, 0x00}, {0x81, 0xF7}, {0x00, 0x00},/* 0xB4-0xB7 */ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},/* 0xB8-0xBB */ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},/* 0xBC-0xBF */ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},/* 0xC0-0xC3 */ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},/* 0xC4-0xC7 */ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},/* 0xC8-0xCB */ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},/* 0xCC-0xCF */ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},/* 0xD0-0xD3 */ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x81, 0x7E},/* 0xD4-0xD7 */ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},/* 0xD8-0xDB */ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},/* 0xDC-0xDF */ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},/* 0xE0-0xE3 */ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},/* 0xE4-0xE7 */ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},/* 0xE8-0xEB */ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},/* 0xEC-0xEF */ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},/* 0xF0-0xF3 */ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x81, 0x80},/* 0xF4-0xF7 */ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},/* 0xF8-0xFB */ + {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00}, {0x00, 0x00},/* 0xFC-0xFF */ +}; + static unsigned char u2c_03[512] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */ @@ -7708,7 +7735,7 @@ static unsigned char u2c_FF[512] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */ - 0x81, 0x91, 0x81, 0x92, 0xEE, 0xF9, 0x81, 0x50, /* 0xE0-0xE3 */ + 0x81, 0x91, 0x81, 0x92, 0x81, 0xCA, 0x81, 0x50, /* 0xE0-0xE3 */ 0xEE, 0xFA, 0x81, 0x8F, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */ }; @@ -7842,9 +7869,17 @@ static int uni2char(const wchar_t uni, if (out[0] == 0x00 && out[1] == 0x00) return -EINVAL; return 2; - } else if ((ch == 0) && (cl <= 0x7F)) { - out[0] = cl; - return 1; + } else if (ch == 0) { + if (cl <= 0x7F) { + out[0] = cl; + return 1; + } else if (0xA0 <= cl) { + out[0] = u2c_00hi[cl - 0xA0][0]; + out[1] = u2c_00hi[cl - 0xA0][1]; + if (out[0] && out[1]) + return 2; + } + return -EINVAL; } else return -EINVAL; --- linux-2.6.9-rc1/fs/ntfs/super.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/fs/ntfs/super.c 2004-09-02 20:51:52.000000000 -0700 @@ -28,6 +28,7 @@ #include #include #include +#include #include "ntfs.h" #include "sysctl.h" @@ -2638,7 +2639,7 @@ static int __init init_ntfs_fs(void) ntfs_inode_cache = kmem_cache_create(ntfs_inode_cache_name, sizeof(ntfs_inode), 0, - SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, NULL, NULL); + SLAB_RECLAIM_ACCOUNT, NULL, NULL); if (!ntfs_inode_cache) { printk(KERN_CRIT "NTFS: Failed to create %s!\n", ntfs_inode_cache_name); @@ -2724,7 +2725,7 @@ MODULE_AUTHOR("Anton Altaparmakov f_pos; if (copy_to_user(buf, buffer + file->f_pos, count)) return -EFAULT; - file->f_pos += count; + *ppos += count; return count; } @@ -94,7 +94,7 @@ static ssize_t property_read(struct file openprom_property *op; char buffer[64]; - if (filp->f_pos >= 0xffffff || count >= 0xffffff) + if (*ppos >= 0xffffff || count >= 0xffffff) return -EINVAL; if (!filp->private_data) { node = nodes[(u16)((long)inode->u.generic_ip)].node; @@ -180,7 +180,7 @@ static ssize_t property_read(struct file } else { i = (op->len << 1) + 1; } - k = filp->f_pos; + k = *ppos; if (k >= i) return 0; if (count > i - k) count = i - k; if (op->flag & OPP_STRING) { @@ -197,7 +197,7 @@ static ssize_t property_read(struct file j = count; if (j >= 0) { - if (copy_to_user(buf + k - filp->f_pos, + if (copy_to_user(buf + k - *ppos, op->value + k - 1, j)) return -EFAULT; count -= j; @@ -205,11 +205,11 @@ static ssize_t property_read(struct file } if (count) { - if (put_user('\'', &buf [k++ - filp->f_pos])) + if (put_user('\'', &buf [k++ - *ppos])) return -EFAULT; } if (count > 1) { - if (put_user('\n', &buf [k++ - filp->f_pos])) + if (put_user('\n', &buf [k++ - *ppos])) return -EFAULT; } } else if (op->flag & OPP_STRINGLIST) { @@ -287,7 +287,7 @@ static ssize_t property_read(struct file if ((k < i - 1) && (k & 1)) { sprintf (buffer, "%02x", (unsigned char) *(op->value + (k >> 1)) & 0xff); - if (put_user(buffer[1], &buf[k++ - filp->f_pos])) + if (put_user(buffer[1], &buf[k++ - *ppos])) return -EFAULT; count--; } @@ -295,7 +295,7 @@ static ssize_t property_read(struct file for (; (count > 1) && (k < i - 1); k += 2) { sprintf (buffer, "%02x", (unsigned char) *(op->value + (k >> 1)) & 0xff); - if (copy_to_user(buf + k - filp->f_pos, buffer, 2)) + if (copy_to_user(buf + k - *ppos, buffer, 2)) return -EFAULT; count -= 2; } @@ -303,18 +303,18 @@ static ssize_t property_read(struct file if (count && (k < i - 1)) { sprintf (buffer, "%02x", (unsigned char) *(op->value + (k >> 1)) & 0xff); - if (put_user(buffer[0], &buf[k++ - filp->f_pos])) + if (put_user(buffer[0], &buf[k++ - *ppos])) return -EFAULT; count--; } if (count) { - if (put_user('\n', &buf [k++ - filp->f_pos])) + if (put_user('\n', &buf [k++ - *ppos])) return -EFAULT; } } - count = k - filp->f_pos; - filp->f_pos = k; + count = k - *ppos; + *ppos = k; return count; } @@ -327,14 +327,14 @@ static ssize_t property_write(struct fil void *b; openprom_property *op; - if (filp->f_pos >= 0xffffff || count >= 0xffffff) + if (*ppos >= 0xffffff || count >= 0xffffff) return -EINVAL; if (!filp->private_data) { i = property_read (filp, NULL, 0, NULL); if (i) return i; } - k = filp->f_pos; + k = *ppos; op = (openprom_property *)filp->private_data; if (!(op->flag & OPP_STRING)) { u32 *first, *last; @@ -462,7 +462,7 @@ static ssize_t property_write(struct fil op->len = i; } else op->len = i; - filp->f_pos += count; + *ppos += count; } write_try_string: if (!(op->flag & OPP_BINARY)) { @@ -480,7 +480,7 @@ write_try_string: op->flag |= OPP_QUOTED; buf++; count--; - filp->f_pos++; + (*ppos)++; if (!count) { op->flag |= OPP_STRING; return 1; @@ -489,9 +489,9 @@ write_try_string: op->flag |= OPP_NOTQUOTED; } op->flag |= OPP_STRING; - if (op->alloclen <= count + filp->f_pos) { + if (op->alloclen <= count + *ppos) { b = kmalloc (sizeof (openprom_property) - + 2 * (count + filp->f_pos), GFP_KERNEL); + + 2 * (count + *ppos), GFP_KERNEL); if (!b) return -ENOMEM; memcpy (b, filp->private_data, @@ -499,14 +499,14 @@ write_try_string: + strlen (op->name) + op->alloclen); memset (((char *)b) + sizeof (openprom_property) + strlen (op->name) + op->alloclen, - 0, 2*(count - filp->f_pos) - op->alloclen); + 0, 2*(count - *ppos) - op->alloclen); op = (openprom_property *)b; - op->alloclen = 2*(count + filp->f_pos); + op->alloclen = 2*(count + *ppos); b = filp->private_data; filp->private_data = (void *)op; kfree (b); } - p = op->value + filp->f_pos - ((op->flag & OPP_QUOTED) ? 1 : 0); + p = op->value + *ppos - ((op->flag & OPP_QUOTED) ? 1 : 0); if (copy_from_user(p, buf, count)) return -EFAULT; op->flag |= OPP_DIRTY; @@ -517,17 +517,17 @@ write_try_string: } if (i < count) { op->len = p - op->value; - filp->f_pos += i + 1; + *ppos += i + 1; if ((p > op->value) && (op->flag & OPP_QUOTED) && (*(p - 1) == '\'')) op->len--; } else { if (p - op->value > op->len) op->len = p - op->value; - filp->f_pos += count; + *ppos += count; } } - return filp->f_pos - k; + return *ppos - k; } int property_release (struct inode *inode, struct file *filp) --- linux-2.6.9-rc1/fs/partitions/amiga.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/fs/partitions/amiga.c 2004-09-02 20:51:52.000000000 -0700 @@ -31,6 +31,7 @@ amiga_partition(struct parsed_partitions struct RigidDiskBlock *rdb; struct PartitionBlock *pb; int start_sect, nr_sects, blk, part, res = 0; + int blksize = 1; /* Multiplier for disk block size */ int slot = 1; char b[BDEVNAME_SIZE]; @@ -65,10 +66,14 @@ amiga_partition(struct parsed_partitions bdevname(bdev, b), blk); } - printk(" RDSK"); + /* blksize is blocks per 512 byte standard block */ + blksize = be32_to_cpu( rdb->rdb_BlockBytes ) / 512; + + printk(" RDSK (%d)", blksize * 512); /* Be more informative */ blk = be32_to_cpu(rdb->rdb_PartitionList); put_dev_sector(sect); for (part = 1; blk>0 && part<=16; part++, put_dev_sector(sect)) { + blk *= blksize; /* Read in terms partition table understands */ data = read_dev_sector(bdev, blk, §); if (!data) { if (warn_no_part) @@ -88,13 +93,32 @@ amiga_partition(struct parsed_partitions nr_sects = (be32_to_cpu(pb->pb_Environment[10]) + 1 - be32_to_cpu(pb->pb_Environment[9])) * be32_to_cpu(pb->pb_Environment[3]) * - be32_to_cpu(pb->pb_Environment[5]); + be32_to_cpu(pb->pb_Environment[5]) * + blksize; if (!nr_sects) continue; start_sect = be32_to_cpu(pb->pb_Environment[9]) * be32_to_cpu(pb->pb_Environment[3]) * - be32_to_cpu(pb->pb_Environment[5]); + be32_to_cpu(pb->pb_Environment[5]) * + blksize; put_partition(state,slot++,start_sect,nr_sects); + { + /* Be even more informative to aid mounting */ + char dostype[4]; + u32 *dt = (u32 *)dostype; + *dt = pb->pb_Environment[16]; + if (dostype[3] < ' ') + printk(" (%c%c%c^%c)", + dostype[0], dostype[1], + dostype[2], dostype[3] + '@' ); + else + printk(" (%c%c%c%c)", + dostype[0], dostype[1], + dostype[2], dostype[3]); + printk("(res %d spb %d)", + be32_to_cpu(pb->pb_Environment[6]), + be32_to_cpu(pb->pb_Environment[4])); + } res = 1; } printk("\n"); --- linux-2.6.9-rc1/fs/proc/array.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/fs/proc/array.c 2004-09-03 00:57:09.238634776 -0700 @@ -130,8 +130,9 @@ static const char *task_state_array[] = "S (sleeping)", /* 1 */ "D (disk sleep)", /* 2 */ "T (stopped)", /* 4 */ - "Z (zombie)", /* 8 */ - "X (dead)" /* 16 */ + "T (tracing stop)", /* 8 */ + "Z (zombie)", /* 16 */ + "X (dead)" /* 32 */ }; static inline const char * get_task_state(struct task_struct *tsk) @@ -141,7 +142,8 @@ static inline const char * get_task_stat TASK_UNINTERRUPTIBLE | TASK_ZOMBIE | TASK_DEAD | - TASK_STOPPED); + TASK_STOPPED | + TASK_TRACED); const char **p = &task_state_array[0]; while (state) { @@ -159,7 +161,8 @@ static inline char * task_state(struct t read_lock(&tasklist_lock); buffer += sprintf(buffer, "State:\t%s\n" - "SleepAVG:\t%lu%%\n" + "sleep_time:\t%lu\n" + "total_time:\t%lu\n" "Tgid:\t%d\n" "Pid:\t%d\n" "PPid:\t%d\n" @@ -167,7 +170,7 @@ static inline char * task_state(struct t "Uid:\t%d\t%d\t%d\t%d\n" "Gid:\t%d\t%d\t%d\t%d\n", get_task_state(p), - (p->sleep_avg/1024)*100/(1020000000/1024), + p->sleep_time, p->total_time, p->tgid, p->pid, p->pid ? p->real_parent->pid : 0, p->pid && p->ptrace ? p->parent->pid : 0, @@ -278,7 +281,6 @@ static inline char *task_cap(struct task cap_t(p->cap_effective)); } -extern char *task_mem(struct mm_struct *, char *); int proc_pid_status(struct task_struct *task, char * buffer) { char * orig = buffer; @@ -299,10 +301,9 @@ int proc_pid_status(struct task_struct * return buffer - orig; } -extern unsigned long task_vsize(struct mm_struct *); -int proc_pid_stat(struct task_struct *task, char * buffer) +static int do_task_stat(struct task_struct *task, char * buffer, int whole) { - unsigned long vsize, eip, esp, wchan; + unsigned long vsize, eip, esp, wchan = ~0UL; long priority, nice; int tty_pgrp = -1, tty_nr = 0; sigset_t sigign, sigcatch; @@ -312,25 +313,20 @@ int proc_pid_stat(struct task_struct *ta int num_threads = 0; struct mm_struct *mm; unsigned long long start_time; + unsigned long cmin_flt = 0, cmaj_flt = 0, cutime = 0, cstime = 0; + unsigned long min_flt = 0, maj_flt = 0, utime = 0, stime = 0; char tcomm[sizeof(task->comm)]; state = *get_task_state(task); vsize = eip = esp = 0; - task_lock(task); - mm = task->mm; - if(mm) - mm = mmgrab(mm); - task_unlock(task); + mm = get_task_mm(task); if (mm) { - down_read(&mm->mmap_sem); vsize = task_vsize(mm); eip = KSTK_EIP(task); esp = KSTK_ESP(task); - up_read(&mm->mmap_sem); } get_task_comm(tcomm, task); - wchan = get_wchan(task); sigemptyset(&sigign); sigemptyset(&sigcatch); @@ -348,20 +344,40 @@ int proc_pid_stat(struct task_struct *ta } pgid = process_group(task); sid = task->signal->session; + cmin_flt = task->signal->cmin_flt; + cmaj_flt = task->signal->cmaj_flt; + cutime = task->signal->cutime; + cstime = task->signal->cstime; + if (whole) { + min_flt = task->signal->min_flt; + maj_flt = task->signal->maj_flt; + utime = task->signal->utime; + stime = task->signal->stime; + } } + ppid = task->pid ? task->real_parent->pid : 0; read_unlock(&tasklist_lock); + if (!whole || num_threads<2) + wchan = get_wchan(task); + if (!whole) { + min_flt = task->min_flt; + maj_flt = task->maj_flt; + utime = task->utime; + stime = task->stime; + } + /* scale priority and nice values from timeslices to -20..20 */ /* to make it look like a "normal" Unix priority/nice value */ priority = task_prio(task); nice = task_nice(task); - read_lock(&tasklist_lock); - ppid = task->pid ? task->real_parent->pid : 0; - read_unlock(&tasklist_lock); - /* Temporary variable needed for gcc-2.96 */ - start_time = jiffies_64_to_clock_t(task->start_time - INITIAL_JIFFIES); + /* convert timespec -> nsec*/ + start_time = (unsigned long long)task->start_time.tv_sec * NSEC_PER_SEC + + task->start_time.tv_nsec; + /* convert nsec -> ticks */ + start_time = nsec_to_clock_t(start_time); res = sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \ %lu %lu %lu %lu %lu %ld %ld %ld %ld %d %ld %llu %lu %ld %lu %lu %lu %lu %lu \ @@ -375,14 +391,14 @@ int proc_pid_stat(struct task_struct *ta tty_nr, tty_pgrp, task->flags, - task->min_flt, - task->cmin_flt, - task->maj_flt, - task->cmaj_flt, - jiffies_to_clock_t(task->utime), - jiffies_to_clock_t(task->stime), - jiffies_to_clock_t(task->cutime), - jiffies_to_clock_t(task->cstime), + min_flt, + cmin_flt, + maj_flt, + cmaj_flt, + jiffies_to_clock_t(utime), + jiffies_to_clock_t(stime), + jiffies_to_clock_t(cutime), + jiffies_to_clock_t(cstime), priority, nice, num_threads, @@ -416,17 +432,23 @@ int proc_pid_stat(struct task_struct *ta return res; } -extern int task_statm(struct mm_struct *, int *, int *, int *, int *); +int proc_tid_stat(struct task_struct *task, char * buffer) +{ + return do_task_stat(task, buffer, 0); +} + +int proc_tgid_stat(struct task_struct *task, char * buffer) +{ + return do_task_stat(task, buffer, 1); +} + int proc_pid_statm(struct task_struct *task, char *buffer) { int size = 0, resident = 0, shared = 0, text = 0, lib = 0, data = 0; struct mm_struct *mm = get_task_mm(task); if (mm) { - down_read(&mm->mmap_sem); size = task_statm(mm, &shared, &text, &data, &resident); - up_read(&mm->mmap_sem); - mmput(mm); } --- linux-2.6.9-rc1/fs/proc/base.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/fs/proc/base.c 2004-09-03 00:57:14.436844528 -0700 @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,12 @@ enum pid_directory_inos { PROC_TGID_MAPS, PROC_TGID_MOUNTS, PROC_TGID_WCHAN, +#ifdef CONFIG_SCHEDSTATS + PROC_TGID_SCHEDSTAT, +#endif +#ifdef CONFIG_CPUSETS + PROC_TGID_CPUSET, +#endif #ifdef CONFIG_SECURITY PROC_TGID_ATTR, PROC_TGID_ATTR_CURRENT, @@ -83,6 +90,12 @@ enum pid_directory_inos { PROC_TID_MAPS, PROC_TID_MOUNTS, PROC_TID_WCHAN, +#ifdef CONFIG_SCHEDSTATS + PROC_TID_SCHEDSTAT, +#endif +#ifdef CONFIG_CPUSETS + PROC_TID_CPUSET, +#endif #ifdef CONFIG_SECURITY PROC_TID_ATTR, PROC_TID_ATTR_CURRENT, @@ -123,6 +136,12 @@ static struct pid_entry tgid_base_stuff[ #ifdef CONFIG_KALLSYMS E(PROC_TGID_WCHAN, "wchan", S_IFREG|S_IRUGO), #endif +#ifdef CONFIG_SCHEDSTATS + E(PROC_TGID_SCHEDSTAT, "schedstat", S_IFREG|S_IRUGO), +#endif +#ifdef CONFIG_CPUSETS + E(PROC_TGID_CPUSET, "cpuset", S_IFREG|S_IRUGO), +#endif {0,0,NULL,0} }; static struct pid_entry tid_base_stuff[] = { @@ -145,6 +164,12 @@ static struct pid_entry tid_base_stuff[] #ifdef CONFIG_KALLSYMS E(PROC_TID_WCHAN, "wchan", S_IFREG|S_IRUGO), #endif +#ifdef CONFIG_SCHEDSTATS + E(PROC_TID_SCHEDSTAT, "schedstat",S_IFREG|S_IRUGO), +#endif +#ifdef CONFIG_CPUSETS + E(PROC_TID_CPUSET, "cpuset", S_IFREG|S_IRUGO), +#endif {0,0,NULL,0} }; @@ -177,7 +202,8 @@ static inline int proc_type(struct inode return PROC_I(inode)->type; } -int proc_pid_stat(struct task_struct*,char*); +int proc_tid_stat(struct task_struct*,char*); +int proc_tgid_stat(struct task_struct*,char*); int proc_pid_status(struct task_struct*,char*); int proc_pid_statm(struct task_struct*,char*); @@ -275,7 +301,8 @@ static int proc_root_link(struct inode * #define MAY_PTRACE(task) \ (task == current || \ (task->parent == current && \ - (task->ptrace & PT_PTRACED) && task->state == TASK_STOPPED && \ + (task->ptrace & PT_PTRACED) && \ + (task->state == TASK_STOPPED || task->state == TASK_TRACED) && \ security_ptrace(current,task) == 0)) static int may_ptrace_attach(struct task_struct *task) @@ -340,7 +367,7 @@ static int proc_pid_cmdline(struct task_ // If the nul at the end of args has been overwritten, then // assume application is using setproctitle(3). - if (res > 0 && buffer[res-1] != '\0') { + if (res > 0 && buffer[res-1] != '\0' && len < PAGE_SIZE) { len = strnlen(buffer, res); if (len < res) { res = len; @@ -398,6 +425,19 @@ static int proc_pid_wchan(struct task_st } #endif /* CONFIG_KALLSYMS */ +#ifdef CONFIG_SCHEDSTATS +/* + * Provides /proc/PID/schedstat + */ +static int proc_pid_schedstat(struct task_struct *task, char *buffer) +{ + return sprintf(buffer, "%lu %lu %lu\n", + task->sched_info.cpu_time, + task->sched_info.run_delay, + task->sched_info.pcnt); +} +#endif + /************************************************************************/ /* Here the fs part begins */ /************************************************************************/ @@ -518,7 +558,6 @@ static ssize_t proc_info_read(struct fil struct inode * inode = file->f_dentry->d_inode; unsigned long page; ssize_t length; - ssize_t end; struct task_struct *task = proc_task(inode); if (count > PROC_BLOCK_SIZE) @@ -528,24 +567,10 @@ static ssize_t proc_info_read(struct fil length = PROC_I(inode)->op.proc_read(task, (char*)page); - if (length < 0) { - free_page(page); - return length; - } - /* Static 4kB (or whatever) block capacity */ - if (*ppos >= length) { - free_page(page); - return 0; - } - if (count + *ppos > length) - count = length - *ppos; - end = count + *ppos; - if (copy_to_user(buf, (char *) page + *ppos, count)) - count = -EFAULT; - else - *ppos = end; + if (length >= 0) + length = simple_read_from_buffer(buf, count, ppos, (char *)page, length); free_page(page); - return count; + return length; } static struct file_operations proc_info_file_operations = { @@ -768,10 +793,9 @@ static struct inode_operations proc_pid_ .follow_link = proc_pid_follow_link }; -static int pid_alive(struct task_struct *p) +static inline int pid_alive(struct task_struct *p) { - BUG_ON(p->pids[PIDTYPE_PID].pidptr != &p->pids[PIDTYPE_PID].pid); - return atomic_read(&p->pids[PIDTYPE_PID].pid.count); + return p->pids[PIDTYPE_PID].nr != 0; } #define NUMBUF 10 @@ -1169,7 +1193,6 @@ static ssize_t proc_pid_attr_read(struct struct inode * inode = file->f_dentry->d_inode; unsigned long page; ssize_t length; - ssize_t end; struct task_struct *task = proc_task(inode); if (count > PAGE_SIZE) @@ -1180,24 +1203,10 @@ static ssize_t proc_pid_attr_read(struct length = security_getprocattr(task, (char*)file->f_dentry->d_name.name, (void*)page, count); - if (length < 0) { - free_page(page); - return length; - } - /* Static 4kB (or whatever) block capacity */ - if (*ppos >= length) { - free_page(page); - return 0; - } - if (count + *ppos > length) - count = length - *ppos; - end = count + *ppos; - if (copy_to_user(buf, (char *) page + *ppos, count)) - count = -EFAULT; - else - *ppos = end; + if (length >= 0) + length = simple_read_from_buffer(buf, count, ppos, (char *)page, length); free_page(page); - return count; + return length; } static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf, @@ -1320,9 +1329,12 @@ static struct dentry *proc_pident_lookup ei->op.proc_read = proc_pid_status; break; case PROC_TID_STAT: + inode->i_fop = &proc_info_file_operations; + ei->op.proc_read = proc_tid_stat; + break; case PROC_TGID_STAT: inode->i_fop = &proc_info_file_operations; - ei->op.proc_read = proc_pid_stat; + ei->op.proc_read = proc_tgid_stat; break; case PROC_TID_CMDLINE: case PROC_TGID_CMDLINE: @@ -1376,6 +1388,19 @@ static struct dentry *proc_pident_lookup ei->op.proc_read = proc_pid_wchan; break; #endif +#ifdef CONFIG_SCHEDSTATS + case PROC_TID_SCHEDSTAT: + case PROC_TGID_SCHEDSTAT: + inode->i_fop = &proc_info_file_operations; + ei->op.proc_read = proc_pid_schedstat; + break; +#endif +#ifdef CONFIG_CPUSETS + case PROC_TID_CPUSET: + case PROC_TGID_CPUSET: + inode->i_fop = &proc_cpuset_operations; + break; +#endif default: printk("procfs: impossible type (%d)",p->type); iput(inode); @@ -1527,6 +1552,7 @@ struct dentry *proc_pid_unhash(struct ta void proc_pid_flush(struct dentry *proc_dentry) { + might_sleep(); if(proc_dentry != NULL) { shrink_dcache_parent(proc_dentry); dput(proc_dentry); @@ -1667,7 +1693,7 @@ static int get_tgid_list(int index, unsi p = NULL; if (version) { p = find_task_by_pid(version); - if (!thread_group_leader(p)) + if (p && !thread_group_leader(p)) p = NULL; } @@ -1730,6 +1756,7 @@ int proc_pid_readdir(struct file * filp, char buf[PROC_NUMBUF]; unsigned int nr = filp->f_pos - FIRST_PROCESS_ENTRY; unsigned int nr_tgids, i; + int next_tgid; if (!nr) { ino_t ino = fake_ino(0,PROC_TGID_INO); @@ -1739,26 +1766,45 @@ int proc_pid_readdir(struct file * filp, nr++; } - /* - * f_version caches the last tgid which was returned from readdir + /* f_version caches the tgid value that the last readdir call couldn't + * return. lseek aka telldir automagically resets f_version to 0. */ - nr_tgids = get_tgid_list(nr, filp->f_version, tgid_array); - - for (i = 0; i < nr_tgids; i++) { - int tgid = tgid_array[i]; - ino_t ino = fake_ino(tgid,PROC_TGID_INO); - unsigned long j = PROC_NUMBUF; + next_tgid = filp->f_version; + filp->f_version = 0; + for (;;) { + nr_tgids = get_tgid_list(nr, next_tgid, tgid_array); + if (!nr_tgids) { + /* no more entries ! */ + break; + } + next_tgid = 0; - do - buf[--j] = '0' + (tgid % 10); - while ((tgid /= 10) != 0); + /* do not use the last found pid, reserve it for next_tgid */ + if (nr_tgids == PROC_MAXPIDS) { + nr_tgids--; + next_tgid = tgid_array[nr_tgids]; + } - if (filldir(dirent, buf+j, PROC_NUMBUF-j, filp->f_pos, ino, DT_DIR) < 0) { - filp->f_version = tgid; - break; + for (i=0;if_pos, ino, DT_DIR) < 0) { + /* returning this tgid failed, save it as the first + * pid for the next readir call */ + filp->f_version = tgid_array[i]; + goto out; + } + filp->f_pos++; + nr++; } - filp->f_pos++; } +out: return 0; } --- linux-2.6.9-rc1/fs/proc/inode.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/fs/proc/inode.c 2004-09-02 20:51:52.000000000 -0700 @@ -120,7 +120,7 @@ int __init proc_init_inodecache(void) { proc_inode_cachep = kmem_cache_create("proc_inode_cache", sizeof(struct proc_inode), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (proc_inode_cachep == NULL) return -ENOMEM; --- linux-2.6.9-rc1/fs/proc/proc_misc.c 2004-08-24 00:01:55.000000000 -0700 +++ 25/fs/proc/proc_misc.c 2004-09-03 00:56:42.826650008 -0700 @@ -541,70 +541,6 @@ static int execdomains_read_proc(char *p return proc_calc_metrics(page, start, off, count, eof, len); } -/* - * This function accesses profiling information. The returned data is - * binary: the sampling step and the actual contents of the profile - * buffer. Use of the program readprofile is recommended in order to - * get meaningful info out of these data. - */ -static ssize_t -read_profile(struct file *file, char __user *buf, size_t count, loff_t *ppos) -{ - unsigned long p = *ppos; - ssize_t read; - char * pnt; - unsigned int sample_step = 1 << prof_shift; - - if (p >= (prof_len+1)*sizeof(unsigned int)) - return 0; - if (count > (prof_len+1)*sizeof(unsigned int) - p) - count = (prof_len+1)*sizeof(unsigned int) - p; - read = 0; - - while (p < sizeof(unsigned int) && count > 0) { - put_user(*((char *)(&sample_step)+p),buf); - buf++; p++; count--; read++; - } - pnt = (char *)prof_buffer + p - sizeof(unsigned int); - if (copy_to_user(buf,(void *)pnt,count)) - return -EFAULT; - read += count; - *ppos += read; - return read; -} - -/* - * Writing to /proc/profile resets the counters - * - * Writing a 'profiling multiplier' value into it also re-sets the profiling - * interrupt frequency, on architectures that support this. - */ -static ssize_t write_profile(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ -#ifdef CONFIG_SMP - extern int setup_profiling_timer (unsigned int multiplier); - - if (count == sizeof(int)) { - unsigned int multiplier; - - if (copy_from_user(&multiplier, buf, sizeof(int))) - return -EFAULT; - - if (setup_profiling_timer(multiplier)) - return -EINVAL; - } -#endif - - memset(prof_buffer, 0, prof_len * sizeof(*prof_buffer)); - return count; -} - -static struct file_operations proc_profile_operations = { - .read = read_profile, - .write = write_profile, -}; - #ifdef CONFIG_MAGIC_SYSRQ /* * writing 'C' to /proc/sysrq-trigger is like sysrq-C @@ -637,6 +573,36 @@ static void create_seq_entry(char *name, entry->proc_fops = f; } +#ifdef CONFIG_LOCKMETER +extern ssize_t get_lockmeter_info(char *, size_t, loff_t *); +extern ssize_t put_lockmeter_info(const char *, size_t); +extern int get_lockmeter_info_size(void); + +/* + * This function accesses lock metering information. + */ +static ssize_t read_lockmeter(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + return get_lockmeter_info(buf, count, ppos); +} + +/* + * Writing to /proc/lockmeter resets the counters + */ +static ssize_t write_lockmeter(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + return put_lockmeter_info(buf, count); +} + +static struct file_operations proc_lockmeter_operations = { + NULL, /* lseek */ + read: read_lockmeter, + write: write_lockmeter, +}; +#endif /* CONFIG_LOCKMETER */ + void __init proc_misc_init(void) { struct proc_dir_entry *entry; @@ -681,6 +647,9 @@ void __init proc_misc_init(void) #ifdef CONFIG_MODULES create_seq_entry("modules", 0, &proc_modules_operations); #endif +#ifdef CONFIG_SCHEDSTATS + create_seq_entry("schedstat", 0, &proc_schedstat_operations); +#endif #ifdef CONFIG_PROC_KCORE proc_root_kcore = create_proc_entry("kcore", S_IRUSR, NULL); if (proc_root_kcore) { @@ -689,18 +658,18 @@ void __init proc_misc_init(void) (size_t)high_memory - PAGE_OFFSET + PAGE_SIZE; } #endif - if (prof_on) { - entry = create_proc_entry("profile", S_IWUSR | S_IRUGO, NULL); - if (entry) { - entry->proc_fops = &proc_profile_operations; - entry->size = (1+prof_len) * sizeof(unsigned int); - } - } #ifdef CONFIG_MAGIC_SYSRQ entry = create_proc_entry("sysrq-trigger", S_IWUSR, NULL); if (entry) entry->proc_fops = &proc_sysrq_trigger_operations; #endif +#ifdef CONFIG_LOCKMETER + entry = create_proc_entry("lockmeter", S_IWUSR | S_IRUGO, NULL); + if (entry) { + entry->proc_fops = &proc_lockmeter_operations; + entry->size = get_lockmeter_info_size(); + } +#endif #ifdef CONFIG_PPC32 { extern struct file_operations ppc_htab_operations; --- linux-2.6.9-rc1/fs/proc/task_mmu.c 2004-08-24 00:01:55.000000000 -0700 +++ 25/fs/proc/task_mmu.c 2004-09-02 20:51:52.000000000 -0700 @@ -6,27 +6,11 @@ char *task_mem(struct mm_struct *mm, char *buffer) { - unsigned long data = 0, stack = 0, exec = 0, lib = 0; - struct vm_area_struct *vma; + unsigned long data, text, lib; - down_read(&mm->mmap_sem); - for (vma = mm->mmap; vma; vma = vma->vm_next) { - unsigned long len = (vma->vm_end - vma->vm_start) >> 10; - if (!vma->vm_file) { - data += len; - if (vma->vm_flags & VM_GROWSDOWN) - stack += len; - continue; - } - if (vma->vm_flags & VM_WRITE) - continue; - if (vma->vm_flags & VM_EXEC) { - exec += len; - if (vma->vm_flags & VM_EXECUTABLE) - continue; - lib += len; - } - } + data = mm->total_vm - mm->shared_vm - mm->stack_vm; + text = (mm->end_code - mm->start_code) >> 10; + lib = (mm->exec_vm << (PAGE_SHIFT-10)) - text; buffer += sprintf(buffer, "VmSize:\t%8lu kB\n" "VmLck:\t%8lu kB\n" @@ -35,12 +19,11 @@ char *task_mem(struct mm_struct *mm, cha "VmStk:\t%8lu kB\n" "VmExe:\t%8lu kB\n" "VmLib:\t%8lu kB\n", - mm->total_vm << (PAGE_SHIFT-10), + (mm->total_vm - mm->reserved_vm) << (PAGE_SHIFT-10), mm->locked_vm << (PAGE_SHIFT-10), mm->rss << (PAGE_SHIFT-10), - data - stack, stack, - exec - lib, lib); - up_read(&mm->mmap_sem); + data << (PAGE_SHIFT-10), + mm->stack_vm << (PAGE_SHIFT-10), text, lib); return buffer; } @@ -52,28 +35,11 @@ unsigned long task_vsize(struct mm_struc int task_statm(struct mm_struct *mm, int *shared, int *text, int *data, int *resident) { - struct vm_area_struct *vma; - int size = 0; - + *shared = mm->shared_vm; + *text = (mm->end_code - mm->start_code) >> PAGE_SHIFT; + *data = mm->total_vm - mm->shared_vm - *text; *resident = mm->rss; - for (vma = mm->mmap; vma; vma = vma->vm_next) { - int pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; - - size += pages; - if (is_vm_hugetlb_page(vma)) { - if (!(vma->vm_flags & VM_DONTCOPY)) - *shared += pages; - continue; - } - if (vma->vm_file) - *shared += pages; - if (vma->vm_flags & VM_EXECUTABLE) - *text += pages; - else - *data += pages; - } - - return size; + return mm->total_vm; } static int show_map(struct seq_file *m, void *v) --- linux-2.6.9-rc1/fs/proc/task_nommu.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/fs/proc/task_nommu.c 2004-09-02 20:51:52.000000000 -0700 @@ -68,11 +68,12 @@ unsigned long task_vsize(struct mm_struc struct mm_tblock_struct *tbp; unsigned long vsize = 0; + down_read(&mm->mmap_sem); for (tbp = &mm->context.tblock; tbp; tbp = tbp->next) { if (tbp->rblock) vsize += kobjsize(tbp->rblock->kblock); } - + up_read(&mm->mmap_sem); return vsize; } @@ -81,7 +82,8 @@ int task_statm(struct mm_struct *mm, int { struct mm_tblock_struct *tbp; int size = kobjsize(mm); - + + down_read(&mm->mmap_sem); for (tbp = &mm->context.tblock; tbp; tbp = tbp->next) { if (tbp->next) size += kobjsize(tbp->next); @@ -93,7 +95,7 @@ int task_statm(struct mm_struct *mm, int size += (*text = mm->end_code - mm->start_code); size += (*data = mm->start_stack - mm->start_data); - + up_read(&mm->mmap_sem); *resident = size; return size; } --- linux-2.6.9-rc1/fs/qnx4/inode.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/fs/qnx4/inode.c 2004-09-02 20:51:52.000000000 -0700 @@ -544,7 +544,7 @@ static int init_inodecache(void) { qnx4_inode_cachep = kmem_cache_create("qnx4_inode_cache", sizeof(struct qnx4_inode_info), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (qnx4_inode_cachep == NULL) return -ENOMEM; --- linux-2.6.9-rc1/fs/ramfs/inode.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/fs/ramfs/inode.c 2004-09-02 20:51:52.000000000 -0700 @@ -31,6 +31,7 @@ #include #include #include +#include #include @@ -39,7 +40,6 @@ static struct super_operations ramfs_ops; static struct address_space_operations ramfs_aops; -static struct file_operations ramfs_file_operations; static struct inode_operations ramfs_file_inode_operations; static struct inode_operations ramfs_dir_inode_operations; @@ -48,7 +48,7 @@ static struct backing_dev_info ramfs_bac .memory_backed = 1, /* Does not contribute to dirty memory */ }; -static struct inode *ramfs_get_inode(struct super_block *sb, int mode, dev_t dev) +struct inode *ramfs_get_inode(struct super_block *sb, int mode, dev_t dev) { struct inode * inode = new_inode(sb); @@ -146,7 +146,7 @@ static struct address_space_operations r .commit_write = simple_commit_write }; -static struct file_operations ramfs_file_operations = { +struct file_operations ramfs_file_operations = { .read = generic_file_read, .write = generic_file_write, .mmap = generic_file_mmap, @@ -199,7 +199,7 @@ static int ramfs_fill_super(struct super return 0; } -static struct super_block *ramfs_get_sb(struct file_system_type *fs_type, +struct super_block *ramfs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { return get_sb_nodev(fs_type, flags, data, ramfs_fill_super); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/as_ops.c 2004-09-03 00:57:04.957285640 -0700 @@ -0,0 +1,688 @@ +/* Copyright 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Interface to VFS. Reiser4 address_space_operations are defined here. */ + +#include "forward.h" +#include "debug.h" +#include "dformat.h" +#include "coord.h" +#include "plugin/item/item.h" +#include "plugin/file/file.h" +#include "plugin/security/perm.h" +#include "plugin/disk_format/disk_format.h" +#include "plugin/plugin.h" +#include "plugin/plugin_set.h" +#include "plugin/object.h" +#include "txnmgr.h" +#include "jnode.h" +#include "znode.h" +#include "block_alloc.h" +#include "tree.h" +#include "log.h" +#include "vfs_ops.h" +#include "inode.h" +#include "page_cache.h" +#include "ktxnmgrd.h" +#include "super.h" +#include "reiser4.h" +#include "kattr.h" +#include "entd.h" +#include "emergency_flush.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* address space operations */ + +static int reiser4_readpage(struct file *, struct page *); + +static int reiser4_prepare_write(struct file *, + struct page *, unsigned, unsigned); + +static int reiser4_commit_write(struct file *, + struct page *, unsigned, unsigned); + +static int reiser4_set_page_dirty (struct page *); +static sector_t reiser4_bmap(struct address_space *, sector_t); +/* static int reiser4_direct_IO(int, struct inode *, + struct kiobuf *, unsigned long, int); */ + +/* address space operations */ + +/* clear PAGECACHE_TAG_DIRTY tag of a page. This is used in uncapture_page. This resembles test_clear_page_dirty. The + only difference is that page's mapping exists and REISER4_MOVED tag is checked */ +reiser4_internal void +reiser4_clear_page_dirty(struct page *page) +{ + struct address_space *mapping; + unsigned long flags; + + mapping = page->mapping; + BUG_ON(mapping == NULL); + + read_lock_irqsave(&mapping->tree_lock, flags); + if (TestClearPageDirty(page)) { + /* clear dirty tag of page in address space radix tree */ + radix_tree_tag_clear(&mapping->page_tree, page->index, + PAGECACHE_TAG_DIRTY); + /* FIXME: remove this when reiser4_set_page_dirty will skip setting this tag for captured pages */ + radix_tree_tag_clear(&mapping->page_tree, page->index, + PAGECACHE_TAG_REISER4_MOVED); + + read_unlock_irqrestore(&mapping->tree_lock, flags); + if (!mapping->backing_dev_info->memory_backed) + dec_page_state(nr_dirty); + return; + } + read_unlock_irqrestore(&mapping->tree_lock, flags); +} + +/* as_ops->set_page_dirty() VFS method in reiser4_address_space_operations. + + It is used by others (except reiser4) to set reiser4 pages dirty. Reiser4 + itself uses set_page_dirty_internal(). + + The difference is that reiser4_set_page_dirty sets MOVED tag on the page and clears DIRTY tag. Pages tagged as MOVED + get processed by reiser4_writepages() to do reiser4 specific work over dirty pages (allocation jnode, capturing, atom + creation) which cannot be done in the contexts where reiser4_set_page_dirty is called. + set_page_dirty_internal sets DIRTY tag and clear MOVED +*/ +static int reiser4_set_page_dirty(struct page *page /* page to mark dirty */) +{ + if (!TestSetPageDirty(page)) { + struct address_space *mapping = page->mapping; + + if (mapping) { + read_lock_irq(&mapping->tree_lock); + /* check for race with truncate */ + if (page->mapping) { + assert("vs-1652", page->mapping == mapping); + if (!mapping->backing_dev_info->memory_backed) + inc_page_state(nr_dirty); + radix_tree_tag_clear(&mapping->page_tree, + page->index, PAGECACHE_TAG_DIRTY); + /* FIXME: if would be nice to not set this tag on pages which are captured already */ + radix_tree_tag_set(&mapping->page_tree, + page->index, PAGECACHE_TAG_REISER4_MOVED); + } + read_unlock_irq(&mapping->tree_lock); + __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); + } + } + return 0; +} + +/* ->readpage() VFS method in reiser4 address_space_operations + method serving file mmapping +*/ +static int +reiser4_readpage(struct file *f /* file to read from */ , + struct page *page /* page where to read data + * into */ ) +{ + struct inode *inode; + file_plugin *fplug; + int result; + reiser4_context ctx; + + /* + * basically calls ->readpage method of object plugin and handles + * errors. + */ + + assert("umka-078", f != NULL); + assert("umka-079", page != NULL); + assert("nikita-2280", PageLocked(page)); + assert("vs-976", !PageUptodate(page)); + + assert("vs-318", page->mapping && page->mapping->host); + assert("nikita-1352", (f == NULL) || (f->f_dentry->d_inode == page->mapping->host)); + + /* ->readpage can be called from page fault service routine */ + assert("nikita-3174", schedulable()); + + inode = page->mapping->host; + init_context(&ctx, inode->i_sb); + fplug = inode_file_plugin(inode); + if (fplug->readpage != NULL) + result = fplug->readpage(f, page); + else + result = RETERR(-EINVAL); + if (result != 0) { + SetPageError(page); + unlock_page(page); + } + + reiser4_exit_context(&ctx); + return 0; +} + +/* ->readpages() VFS method in reiser4 address_space_operations + method serving page cache readahead + + reiser4_readpages works in the following way: on input it has coord which is set on extent that addresses first of + pages for which read requests are to be issued. So, reiser4_readpages just walks forward through extent unit, finds + which blocks are to be read and start read for them. + +reiser4_readpages can be called from two places: from +sys_read->reiser4_read->read_unix_file->read_extent->page_cache_readahead and +from +handling page fault: +handle_mm_fault->do_no_page->filemap_nopage->page_cache_readaround + +In first case coord is set by reiser4 read code. This case is detected by if +(is_in_reiser4_context()). + +In second case, coord is not set and currently, reiser4_readpages does +nothing. +*/ +static int +reiser4_readpages(struct file *file, struct address_space *mapping, + struct list_head *pages, unsigned nr_pages) +{ + file_plugin *fplug; + + if (is_in_reiser4_context()) { + /* we are called from reiser4 context, typically from method + which implements read into page cache. From read_extent, + for example */ + fplug = inode_file_plugin(mapping->host); + if (fplug->readpages) + fplug->readpages(file, mapping, pages); + } else { + /* we are called from page fault. Currently, we do not + * readahead in this case. */; + } + + /* __do_page_cache_readahead expects filesystem's readpages method to + * process every page on this list */ + while (!list_empty(pages)) { + struct page *page = list_entry(pages->prev, struct page, lru); + list_del(&page->lru); + page_cache_release(page); + } + return 0; +} + +/* prepares @page to be written. This means, that if we want to modify only some + part of page, page should be read first and than modified. Actually this function + almost the same as reiser4_readpage(). The differentce is only that, it does not + unlock the page in the case of error. This is needed because loop back device + driver expects it locked. */ +static int reiser4_prepare_write(struct file *file, struct page *page, + unsigned from, unsigned to) +{ + int result; + file_plugin * fplug; + struct inode * inode; + reiser4_context ctx; + + inode = page->mapping->host; + init_context(&ctx, inode->i_sb); + fplug = inode_file_plugin(inode); + + if (fplug->prepare_write != NULL) + result = fplug->prepare_write(file, page, from, to); + else + result = RETERR(-EINVAL); + + /* don't commit transaction under inode semaphore */ + context_set_commit_async(&ctx); + reiser4_exit_context(&ctx); + + return result; +} + +/* captures jnode of @page to current atom. */ +static int reiser4_commit_write(struct file *file, struct page *page, + unsigned from, unsigned to) +{ + int result; + file_plugin *fplug; + struct inode *inode; + reiser4_context ctx; + + assert("umka-3101", file != NULL); + assert("umka-3102", page != NULL); + assert("umka-3093", PageLocked(page)); + + SetPageUptodate(page); + + inode = page->mapping->host; + init_context(&ctx, inode->i_sb); + fplug = inode_file_plugin(inode); + + if (fplug->capturepage) + result = fplug->capturepage(page); + else + result = RETERR(-EINVAL); + + /* here page is return locked. */ + assert("umka-3103", PageLocked(page)); + + /* don't commit transaction under inode semaphore */ + context_set_commit_async(&ctx); + reiser4_exit_context(&ctx); + return result; +} + +/* ->writepages() + ->vm_writeback() + ->set_page_dirty() + ->prepare_write() + ->commit_write() +*/ + +/* ->bmap() VFS method in reiser4 address_space_operations */ +reiser4_internal int +reiser4_lblock_to_blocknr(struct address_space *mapping, + sector_t lblock, reiser4_block_nr *blocknr) +{ + file_plugin *fplug; + int result; + reiser4_context ctx; + + init_context(&ctx, mapping->host->i_sb); + reiser4_stat_inc(vfs_calls.bmap); + + fplug = inode_file_plugin(mapping->host); + if (fplug && fplug->get_block) { + *blocknr = generic_block_bmap(mapping, lblock, fplug->get_block); + result = 0; + } else + result = RETERR(-EINVAL); + reiser4_exit_context(&ctx); + return result; +} + +/* ->bmap() VFS method in reiser4 address_space_operations */ +static sector_t +reiser4_bmap(struct address_space *mapping, sector_t lblock) +{ + reiser4_block_nr blocknr; + int result; + + result = reiser4_lblock_to_blocknr(mapping, lblock, &blocknr); + if (result == 0) + if (sizeof blocknr == sizeof(sector_t) || + !blocknr_is_fake(&blocknr)) + return blocknr; + else + return 0; + else + return result; +} + +/* ->invalidatepage method for reiser4 */ + +/* + * this is called for each truncated page from + * truncate_inode_pages()->truncate_{complete,partial}_page(). + * + * At the moment of call, page is under lock, and outstanding io (if any) has + * completed. + */ + +reiser4_internal int +reiser4_invalidatepage(struct page *page /* page to invalidate */, + unsigned long offset /* starting offset for partial + * invalidation */) +{ + int ret = 0; + reiser4_context ctx; + struct inode *inode; + + /* + * This is called to truncate file's page. + * + * Originally, reiser4 implemented truncate in a standard way + * (vmtruncate() calls ->invalidatepage() on all truncated pages + * first, then file system ->truncate() call-back is invoked). + * + * This lead to the problem when ->invalidatepage() was called on a + * page with jnode that was captured into atom in ASTAGE_PRE_COMMIT + * process. That is, truncate was bypassing transactions. To avoid + * this, try_capture_page_to_invalidate() call was added here. + * + * After many troubles with vmtruncate() based truncate (including + * races with flush, tail conversion, etc.) it was re-written in the + * top-to-bottom style: items are killed in cut_tree_object() and + * pages belonging to extent are invalidated in kill_hook_extent(). So + * probably now additional call to capture is not needed here. + * + */ + + assert("nikita-3137", PageLocked(page)); + assert("nikita-3138", !PageWriteback(page)); + inode = page->mapping->host; + + /* + * ->invalidatepage() should only be called for the unformatted + * jnodes. Destruction of all other types of jnodes is performed + * separately. But, during some corner cases (like handling errors + * during mount) it is simpler to let ->invalidatepage to be called on + * them. Check for this, and do nothing. + */ + if (get_super_fake(inode->i_sb) == inode) + return 0; + if (get_cc_fake(inode->i_sb) == inode) + return 0; + if (get_super_private(inode->i_sb)->bitmap == inode) + return 0; + + assert("vs-1426", PagePrivate(page)); + assert("vs-1427", page->mapping == jnode_get_mapping(jnode_by_page(page))); + + init_context(&ctx, inode->i_sb); + /* capture page being truncated. */ + ret = try_capture_page_to_invalidate(page); + if (ret != 0) { + warning("nikita-3141", "Cannot capture: %i", ret); + print_page("page", page); + } + + if (offset == 0) { + jnode *node; + + /* remove jnode from transaction and detach it from page. */ + node = jnode_by_page(page); + if (node != NULL) { + assert("vs-1435", !JF_ISSET(node, JNODE_CC)); + jref(node); + JF_SET(node, JNODE_HEARD_BANSHEE); + /* page cannot be detached from jnode concurrently, + * because it is locked */ + uncapture_page(page); + + /* this detaches page from jnode, so that jdelete will not try to lock page which is already locked */ + UNDER_SPIN_VOID(jnode, + node, + page_clear_jnode(page, node)); + unhash_unformatted_jnode(node); + + jput(node); + } + } + reiser4_exit_context(&ctx); + return ret; +} + +#define INC_STAT(page, node, counter) \ + reiser4_stat_inc_at(page->mapping->host->i_sb, \ + level[jnode_get_level(node)].counter); + +#define INC_NSTAT(node, counter) INC_STAT(jnode_page(node), node, counter) + +int is_cced(const jnode *node); + +/* help function called from reiser4_releasepage(). It returns true if jnode + * can be detached from its page and page released. */ +static int +releasable(const jnode *node /* node to check */) +{ + assert("nikita-2781", node != NULL); + assert("nikita-2783", spin_jnode_is_locked(node)); + + /* is some thread is currently using jnode page, later cannot be + * detached */ + if (atomic_read(&node->d_count) != 0) { + INC_NSTAT(node, vm.release.loaded); + return 0; + } + + assert("vs-1214", !jnode_is_loaded(node)); + + /* this jnode is just a copy. Its page cannot be released, because + * otherwise next jload() would load obsolete data from disk + * (up-to-date version may still be in memory). */ + if (is_cced(node)) { + INC_NSTAT(node, vm.release.copy); + return 0; + } + + /* emergency flushed page can be released. This is what emergency + * flush is all about after all. */ + if (JF_ISSET(node, JNODE_EFLUSH)) { + INC_NSTAT(node, vm.release.eflushed); + return 1; /* yeah! */ + } + + /* can only release page if real block number is assigned to + it. Simple check for ->atom wouldn't do, because it is possible for + node to be clean, not it atom yet, and still having fake block + number. For example, node just created in jinit_new(). */ + if (blocknr_is_fake(jnode_get_block(node))) { + INC_NSTAT(node, vm.release.fake); + return 0; + } + /* dirty jnode cannot be released. It can however be submitted to disk + * as part of early flushing, but only after getting flush-prepped. */ + if (jnode_is_dirty(node)) { + INC_NSTAT(node, vm.release.dirty); + return 0; + } + /* overwrite set is only written by log writer. */ + if (JF_ISSET(node, JNODE_OVRWR)) { + INC_NSTAT(node, vm.release.ovrwr); + return 0; + } + /* jnode is already under writeback */ + if (JF_ISSET(node, JNODE_WRITEBACK)) { + INC_NSTAT(node, vm.release.writeback); + return 0; + } + /* page was modified through mmap, but its jnode is not yet + * captured. Don't discard modified data. */ + if (jnode_is_unformatted(node) && JF_ISSET(node, JNODE_KEEPME)) { + INC_NSTAT(node, vm.release.keepme); + return 0; + } + /* don't flush bitmaps or journal records */ + if (!jnode_is_znode(node) && !jnode_is_unformatted(node)) { + INC_NSTAT(node, vm.release.bitmap); + return 0; + } + return 1; +} + +#if REISER4_DEBUG +int jnode_is_releasable(jnode *node) +{ + return UNDER_SPIN(jload, node, releasable(node)); +} +#endif + +/* + * ->releasepage method for reiser4 + * + * This is called by VM scanner when it comes across clean page. What we have + * to do here is to check whether page can really be released (freed that is) + * and if so, detach jnode from it and remove page from the page cache. + * + * Check for releasability is done by releasable() function. + */ +reiser4_internal int +reiser4_releasepage(struct page *page, int gfp UNUSED_ARG) +{ + jnode *node; + void *oid; + + assert("nikita-2257", PagePrivate(page)); + assert("nikita-2259", PageLocked(page)); + assert("nikita-2892", !PageWriteback(page)); + assert("nikita-3019", schedulable()); + + /* NOTE-NIKITA: this can be called in the context of reiser4 call. It + is not clear what to do in this case. A lot of deadlocks seems be + possible. */ + + node = jnode_by_page(page); + assert("nikita-2258", node != NULL); + assert("reiser4-4", page->mapping != NULL); + assert("reiser4-5", page->mapping->host != NULL); + + INC_STAT(page, node, vm.release.try); + + oid = (void *)(unsigned long)get_inode_oid(page->mapping->host); + + /* is_page_cache_freeable() check + (mapping + private + page_cache_get() by shrink_cache()) */ + if (page_count(page) > 3) + return 0; + + if (PageDirty(page)) + return 0; + + /* releasable() needs jnode lock, because it looks at the jnode fields + * and we need jload_lock here to avoid races with jload(). */ + LOCK_JNODE(node); + LOCK_JLOAD(node); + if (releasable(node)) { + struct address_space *mapping; + + mapping = page->mapping; + INC_STAT(page, node, vm.release.ok); + jref(node); + if (jnode_is_znode(node)) + ON_STATS(znode_at_read(JZNODE(node))); + /* there is no need to synchronize against + * jnode_extent_write() here, because pages seen by + * jnode_extent_write() are !releasable(). */ + page_clear_jnode(page, node); + UNLOCK_JLOAD(node); + UNLOCK_JNODE(node); + + /* we are under memory pressure so release jnode also. */ + jput(node); + + write_lock_irq(&mapping->tree_lock); + /* shrink_list() + radix-tree */ + if (page_count(page) == 2) { + __remove_from_page_cache(page); + __put_page(page); + } + write_unlock_irq(&mapping->tree_lock); + + return 1; + } else { + UNLOCK_JLOAD(node); + UNLOCK_JNODE(node); + assert("nikita-3020", schedulable()); + return 0; + } +} + +#undef INC_NSTAT +#undef INC_STAT + +reiser4_internal void +move_inode_out_from_sync_inodes_loop(struct address_space * mapping) +{ + /* work around infinite loop in pdflush->sync_sb_inodes. */ + /* Problem: ->writepages() is supposed to submit io for the pages from + * ->io_pages list and to clean this list. */ + mapping->host->dirtied_when = jiffies; + spin_lock(&inode_lock); + list_move(&mapping->host->i_list, &mapping->host->i_sb->s_dirty); + spin_unlock(&inode_lock); + +} + +/* reiser4 writepages() address space operation this captures anonymous pages + and anonymous jnodes. Anonymous pages are pages which are dirtied via + mmapping. Anonymous jnodes are ones which were created by reiser4_writepage + */ +reiser4_internal int +reiser4_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + int ret = 0; + struct inode *inode; + file_plugin *fplug; + + inode = mapping->host; + fplug = inode_file_plugin(inode); + if (fplug != NULL && fplug->capture != NULL) { + long captured = 0; + + /* call file plugin method to capture anonymous pages and + * anonymous jnodes */ + ret = fplug->capture(inode, wbc, &captured); + } + + move_inode_out_from_sync_inodes_loop(mapping); + return ret; +} + +/* start actual IO on @page */ +reiser4_internal int reiser4_start_up_io(struct page *page) +{ + block_sync_page(page); + return 0; +} + +/* + * reiser4 methods for VM + */ +struct address_space_operations reiser4_as_operations = { + /* called during memory pressure by kswapd */ + .writepage = reiser4_writepage, + /* called to read page from the storage when page is added into page + cache. This is done by page-fault handler. */ + .readpage = reiser4_readpage, + /* Start IO on page. This is called from wait_on_page_bit() and + lock_page() and its purpose is to actually start io by jabbing + device drivers. */ + .sync_page = reiser4_start_up_io, + /* called from + * reiser4_sync_inodes()->generic_sync_sb_inodes()->...->do_writepages() + * + * captures anonymous pages for given inode + */ + .writepages = reiser4_writepages, + /* marks page dirty. Note that this is never called by reiser4 + * directly. Reiser4 uses set_page_dirty_internal(). Reiser4 set page + * dirty is called for pages dirtied though mmap and moves dirty page + * to the special ->moved_list in its mapping. */ + .set_page_dirty = reiser4_set_page_dirty, + /* called during read-ahead */ + .readpages = reiser4_readpages, + .prepare_write = reiser4_prepare_write, /* loop back device driver and generic_file_write() call-back */ + .commit_write = reiser4_commit_write, /* loop back device driver and generic_file_write() call-back */ + /* map logical block number to disk block number. Used by FIBMAP ioctl + * and ..bmap pseudo file. */ + .bmap = reiser4_bmap, + /* called just before page is taken out from address space (on + truncate, umount, or similar). */ + .invalidatepage = reiser4_invalidatepage, + /* called when VM is about to take page from address space (due to + memory pressure). */ + .releasepage = reiser4_releasepage, + /* not yet implemented */ + .direct_IO = NULL +}; + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/block_alloc.c 2004-09-03 00:57:07.756860040 -0700 @@ -0,0 +1,1197 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#include "debug.h" +#include "dformat.h" +#include "plugin/plugin.h" +#include "txnmgr.h" +#include "znode.h" +#include "block_alloc.h" +#include "tree.h" +#include "super.h" +#include "lib.h" + +#include /* for __u?? */ +#include /* for struct super_block */ +#include + +/* THE REISER4 DISK SPACE RESERVATION SCHEME. */ + +/* We need to be able to reserve enough disk space to ensure that an atomic + operation will have enough disk space to flush (see flush.c and + http://namesys.com/v4/v4.html) and commit it once it is started. + + In our design a call for reserving disk space may fail but not an actual + block allocation. + + All free blocks, already allocated blocks, and all kinds of reserved blocks + are counted in different per-fs block counters. + + A reiser4 super block's set of block counters currently is: + + free -- free blocks, + used -- already allocated blocks, + + grabbed -- initially reserved for performing an fs operation, those blocks + are taken from free blocks, then grabbed disk space leaks from grabbed + blocks counter to other counters like "fake allocated", "flush + reserved", "used", the rest of not used grabbed space is returned to + free space at the end of fs operation; + + fake allocated -- counts all nodes without real disk block numbers assigned, + we have separate accounting for formatted and unformatted + nodes (for easier debugging); + + flush reserved -- disk space needed for flushing and committing an atom. + Each dirty already allocated block could be written as a + part of atom's overwrite set or as a part of atom's + relocate set. In both case one additional block is needed, + it is used as a wandered block if we do overwrite or as a + new location for a relocated block. + + In addition, blocks in some states are counted on per-thread and per-atom + basis. A reiser4 context has a counter of blocks grabbed by this transaction + and the sb's grabbed blocks counter is a sum of grabbed blocks counter values + of each reiser4 context. Each reiser4 atom has a counter of "flush reserved" + blocks, which are reserved for flush processing and atom commit. */ + +/* AN EXAMPLE: suppose we insert new item to the reiser4 tree. We estimate + number of blocks to grab for most expensive case of balancing when the leaf + node we insert new item to gets split and new leaf node is allocated. + + So, we need to grab blocks for + + 1) one block for possible dirtying the node we insert an item to. That block + would be used for node relocation at flush time or for allocating of a + wandered one, it depends what will be a result (what set, relocate or + overwrite the node gets assigned to) of the node processing by the flush + algorithm. + + 2) one block for either allocating a new node, or dirtying of right or left + clean neighbor, only one case may happen. + + VS-FIXME-HANS: why can only one case happen? I would expect to see dirtying of left neighbor, right neighbor, current + node, and creation of new node. have I forgotten something? email me. + + These grabbed blocks are counted in both reiser4 context "grabbed blocks" + counter and in the fs-wide one (both ctx->grabbed_blocks and + sbinfo->blocks_grabbed get incremented by 2), sb's free blocks counter is + decremented by 2. + + Suppose both two blocks were spent for dirtying of an already allocated clean + node (one block went from "grabbed" to "flush reserved") and for new block + allocating (one block went from "grabbed" to "fake allocated formatted"). + + Inserting of a child pointer to the parent node caused parent node to be + split, the balancing code takes care about this grabbing necessary space + immediately by calling reiser4_grab with BA_RESERVED flag set which means + "can use the 5% reserved disk space". + + At this moment insertion completes and grabbed blocks (if they were not used) + should be returned to the free space counter. + + However the atom life-cycle is not completed. The atom had one "flush + reserved" block added by our insertion and the new fake allocated node is + counted as a "fake allocated formatted" one. The atom has to be fully + processed by flush before commit. Suppose that the flush moved the first, + already allocated node to the atom's overwrite list, the new fake allocated + node, obviously, went into the atom relocate set. The reiser4 flush + allocates the new node using one unit from "fake allocated formatted" + counter, the log writer uses one from "flush reserved" for wandered block + allocation. + + And, it is not the end. When the wandered block is deallocated after the + atom gets fully played (see wander.c for term description), the disk space + occupied for it is returned to free blocks. */ + +/* BLOCK NUMBERS */ + +/* Any reiser4 node has a block number assigned to it. We use these numbers for + indexing in hash tables, so if a block has not yet been assigned a location + on disk we need to give it a temporary fake block number. + + Current implementation of reiser4 uses 64-bit integers for block numbers. We + use highest bit in 64-bit block number to distinguish fake and real block + numbers. So, only 63 bits may be used to addressing of real device + blocks. That "fake" block numbers space is divided into subspaces of fake + block numbers for data blocks and for shadow (working) bitmap blocks. + + Fake block numbers for data blocks are generated by a cyclic counter, which + gets incremented after each real block allocation. We assume that it is + impossible to overload this counter during one transaction life. */ + +/* Initialize a blocknr hint. */ +reiser4_internal void +blocknr_hint_init(reiser4_blocknr_hint * hint) +{ + xmemset(hint, 0, sizeof (reiser4_blocknr_hint)); +} + +/* Release any resources of a blocknr hint. */ +reiser4_internal void +blocknr_hint_done(reiser4_blocknr_hint * hint UNUSED_ARG) +{ + /* No resources should be freed in current blocknr_hint implementation.*/ +} + +/* see above for explanation of fake block number. */ +/* Audited by: green(2002.06.11) */ +reiser4_internal int +blocknr_is_fake(const reiser4_block_nr * da) +{ + /* The reason for not simply returning result of '&' operation is that + while return value is (possibly 32bit) int, the reiser4_block_nr is + at least 64 bits long, and high bit (which is the only possible + non zero bit after the masking) would be stripped off */ + return (*da & REISER4_FAKE_BLOCKNR_BIT_MASK) ? 1 : 0; +} + +/* Static functions for / block counters + arithmetic. Mostly, they are isolated to not to code same assertions in + several places. */ +static void +sub_from_ctx_grabbed(reiser4_context *ctx, __u64 count) +{ + if (ctx->grabbed_blocks < count) + print_clog(); + BUG_ON(ctx->grabbed_blocks < count); + assert("zam-527", ctx->grabbed_blocks >= count); + ctx->grabbed_blocks -= count; +} + + +static void +sub_from_sb_grabbed(reiser4_super_info_data *sbinfo, __u64 count) +{ + assert("zam-525", sbinfo->blocks_grabbed >= count); + sbinfo->blocks_grabbed -= count; +} + +/* Decrease the counter of block reserved for flush in super block. */ +static void +sub_from_sb_flush_reserved (reiser4_super_info_data *sbinfo, __u64 count) +{ + assert ("vpf-291", sbinfo->blocks_flush_reserved >= count); + sbinfo->blocks_flush_reserved -= count; +} + +static void +sub_from_sb_fake_allocated(reiser4_super_info_data *sbinfo, __u64 count, reiser4_ba_flags_t flags) +{ + if (flags & BA_FORMATTED) { + assert("zam-806", sbinfo->blocks_fake_allocated >= count); + sbinfo->blocks_fake_allocated -= count; + } else { + assert("zam-528", sbinfo->blocks_fake_allocated_unformatted >= count); + sbinfo->blocks_fake_allocated_unformatted -= count; + } +} + +static void +sub_from_sb_used(reiser4_super_info_data *sbinfo, __u64 count) +{ + assert("zam-530", sbinfo->blocks_used >= count + sbinfo->min_blocks_used); + sbinfo->blocks_used -= count; +} + +static void +sub_from_cluster_reserved(reiser4_super_info_data *sbinfo, __u64 count) +{ + assert("edward-501", sbinfo->blocks_clustered >= count); + sbinfo->blocks_clustered -= count; +} + +/* Increase the counter of block reserved for flush in atom. */ +static void +add_to_atom_flush_reserved_nolock (txn_atom * atom, __u32 count) +{ + assert ("zam-772", atom != NULL); + assert ("zam-773", spin_atom_is_locked (atom)); + atom->flush_reserved += count; +} + +/* Decrease the counter of block reserved for flush in atom. */ +static void +sub_from_atom_flush_reserved_nolock (txn_atom * atom, __u32 count) +{ + assert ("zam-774", atom != NULL); + assert ("zam-775", spin_atom_is_locked (atom)); + assert ("nikita-2790", atom->flush_reserved >= count); + atom->flush_reserved -= count; +} + +/* super block has 6 counters: free, used, grabbed, fake allocated + (formatted and unformatted) and flush reserved. Their sum must be + number of blocks on a device. This function checks this */ +reiser4_internal int +check_block_counters(const struct super_block *super) +{ + __u64 sum; + + sum = reiser4_grabbed_blocks(super) + reiser4_free_blocks(super) + + reiser4_data_blocks(super) + reiser4_fake_allocated(super) + + reiser4_fake_allocated_unformatted(super) + flush_reserved(super) + + reiser4_clustered_blocks(super); + if (reiser4_block_count(super) != sum) { + printk("super block counters: " + "used %llu, free %llu, " + "grabbed %llu, fake allocated (formatetd %llu, unformatted %llu), " + "reserved %llu, clustered %llu, sum %llu, must be (block count) %llu\n", + (unsigned long long)reiser4_data_blocks(super), + (unsigned long long)reiser4_free_blocks(super), + (unsigned long long)reiser4_grabbed_blocks(super), + (unsigned long long)reiser4_fake_allocated(super), + (unsigned long long)reiser4_fake_allocated_unformatted(super), + (unsigned long long)flush_reserved(super), + (unsigned long long)reiser4_clustered_blocks(super), + (unsigned long long)sum, + (unsigned long long)reiser4_block_count(super)); + return 0; + } + return 1; +} + +#if REISER4_DEBUG_OUTPUT +reiser4_internal void +print_block_counters(const char *prefix, + const struct super_block *super, txn_atom *atom) +{ + if (super == NULL) + super = reiser4_get_current_sb(); + printk("%s:\tsuper: G: %llu, F: %llu, D: %llu, U: %llu + %llu, R: %llu, C: %llu, T: %llu\n", + prefix, + reiser4_grabbed_blocks(super), + reiser4_free_blocks(super), + reiser4_data_blocks(super), + reiser4_fake_allocated(super), + reiser4_fake_allocated_unformatted(super), + flush_reserved(super), + reiser4_clustered_blocks(super), + reiser4_block_count(super)); + printk("\tcontext: G: %llu", + get_current_context()->grabbed_blocks); + if (atom == NULL) + atom = get_current_atom_locked_nocheck(); + if (atom != NULL) { + printk("\tatom: R: %llu", atom->flush_reserved); + UNLOCK_ATOM(atom); + } + printk("\n"); +} +#endif + +/* Adjust "working" free blocks counter for number of blocks we are going to + allocate. Record number of grabbed blocks in fs-wide and per-thread + counters. This function should be called before bitmap scanning or + allocating fake block numbers + + @super -- pointer to reiser4 super block; + @count -- number of blocks we reserve; + + @return -- 0 if success, -ENOSPC, if all + free blocks are preserved or already allocated. +*/ + +static int +reiser4_grab(reiser4_context *ctx, __u64 count, reiser4_ba_flags_t flags) +{ + __u64 free_blocks; + int ret = 0, use_reserved = flags & BA_RESERVED; + reiser4_super_info_data *sbinfo; + + assert("vs-1276", ctx == get_current_context()); + + sbinfo = get_super_private(ctx->super); + + reiser4_spin_lock_sb(sbinfo); + + free_blocks = sbinfo->blocks_free; + + ON_TRACE(TRACE_ALLOC, "reiser4_grab: free_blocks %llu\n", free_blocks); + + if ((use_reserved && free_blocks < count) || + (!use_reserved && free_blocks < count + sbinfo->blocks_reserved)) { + ret = RETERR(-ENOSPC); + + ON_TRACE(TRACE_ALLOC, "reiser4_grab: ENOSPC: count %llu\n", count); + + goto unlock_and_ret; + } + + ctx->grabbed_blocks += count; + + sbinfo->blocks_grabbed += count; + sbinfo->blocks_free -= count; + +#if REISER4_DEBUG + ctx->grabbed_initially = count; + fill_backtrace(&ctx->grabbed_at, REISER4_BACKTRACE_DEPTH, 0); +#endif + + assert("nikita-2986", check_block_counters(ctx->super)); + + ON_TRACE(TRACE_ALLOC, "%s: grabbed %llu, free blocks left %llu\n", + __FUNCTION__, count, reiser4_free_blocks (ctx->super)); + + /* disable grab space in current context */ + ctx->grab_enabled = 0; + +unlock_and_ret: + reiser4_spin_unlock_sb(sbinfo); + + return ret; +} + +reiser4_internal int +reiser4_grab_space(__u64 count, reiser4_ba_flags_t flags) +{ + int ret; + reiser4_context *ctx; + + assert("nikita-2964", ergo(flags & BA_CAN_COMMIT, + lock_stack_isclean(get_current_lock_stack()))); + ON_TRACE(TRACE_RESERVE, "grab_space: %llu block(s).", count); + + ctx = get_current_context(); + if (!(flags & BA_FORCE) && !is_grab_enabled(ctx)) { + ON_TRACE(TRACE_RESERVE, "grab disabled and not forced!\n"); + return 0; + } + + ret = reiser4_grab(ctx, count, flags); + if (ret == -ENOSPC) { + + /* Trying to commit the all transactions if BA_CAN_COMMIT flag present */ + if (flags & BA_CAN_COMMIT) { + + ON_TRACE(TRACE_RESERVE, "force commit!.."); + + txnmgr_force_commit_all(ctx->super, 0); + + ctx->grab_enabled = 1; + ret = reiser4_grab(ctx, count, flags); + } + } + ON_TRACE(TRACE_RESERVE, "%s(%d)\n", (ret == 0) ? "ok" : "failed", ret); + /* + * allocation from reserved pool cannot fail. This is severe error. + */ + assert("nikita-3005", ergo(flags & BA_RESERVED, ret == 0)); + return ret; +} + +/* + * SPACE RESERVED FOR UNLINK/TRUNCATE + * + * Unlink and truncate require space in transaction (to update stat data, at + * least). But we don't want rm(1) to fail with "No space on device" error. + * + * Solution is to reserve 5% of disk space for truncates and + * unlinks. Specifically, normal space grabbing requests don't grab space from + * reserved area. Only requests with BA_RESERVED bit in flags are allowed to + * drain it. Per super block delete_sema semaphore is used to allow only one + * thread at a time to grab from reserved area. + * + * Grabbing from reserved area should always be performed with BA_CAN_COMMIT + * flag. + * + */ + +reiser4_internal int reiser4_grab_reserved(struct super_block *super, + __u64 count, reiser4_ba_flags_t flags) +{ + reiser4_super_info_data *sbinfo = get_super_private(super); + + assert("nikita-3175", flags & BA_CAN_COMMIT); + + /* Check the delete semaphore already taken by us, we assume that + * reading of machine word is atomic. */ + if (sbinfo->delete_sema_owner == current) { + if (reiser4_grab_space(count, (flags | BA_RESERVED) & ~BA_CAN_COMMIT)) { + warning("zam-1003", "nested call of grab_reserved fails count=(%llu)", + (unsigned long long)count); + reiser4_release_reserved(super); + return RETERR(-ENOSPC); + } + return 0; + } + + if (reiser4_grab_space(count, flags)) { + down(&sbinfo->delete_sema); + assert("nikita-2929", sbinfo->delete_sema_owner == NULL); + sbinfo->delete_sema_owner = current; + + if (reiser4_grab_space(count, flags | BA_RESERVED)) { + warning("zam-833", + "reserved space is not enough (%llu)", (unsigned long long)count); + reiser4_release_reserved(super); + return RETERR(-ENOSPC); + } + } + return 0; +} + +reiser4_internal void +reiser4_release_reserved(struct super_block *super) +{ + reiser4_super_info_data *info; + + info = get_super_private(super); + if (info->delete_sema_owner == current) { + info->delete_sema_owner = NULL; + up(&info->delete_sema); + } +} + +static reiser4_super_info_data * +grabbed2fake_allocated_head(void) +{ + reiser4_context *ctx; + reiser4_super_info_data *sbinfo; + + ctx = get_current_context(); + sub_from_ctx_grabbed(ctx, 1); + + sbinfo = get_super_private(ctx->super); + reiser4_spin_lock_sb(sbinfo); + + sub_from_sb_grabbed(sbinfo, 1); + /* return sbinfo locked */ + return sbinfo; +} + +/* is called after @count fake block numbers are allocated and pointer to + those blocks are inserted into tree. */ +static void +grabbed2fake_allocated_formatted(void) +{ + reiser4_super_info_data *sbinfo; + + sbinfo = grabbed2fake_allocated_head(); + sbinfo->blocks_fake_allocated ++; + + assert("vs-922", check_block_counters(reiser4_get_current_sb())); + + reiser4_spin_unlock_sb(sbinfo); +} + +static void +grabbed2fake_allocated_unformatted(void) +{ + reiser4_super_info_data *sbinfo; + + sbinfo = grabbed2fake_allocated_head(); + sbinfo->blocks_fake_allocated_unformatted ++; + + assert("vs-9221", check_block_counters(reiser4_get_current_sb())); + + reiser4_spin_unlock_sb(sbinfo); +} + +reiser4_internal void +grabbed2cluster_reserved(int count) +{ + reiser4_context *ctx; + reiser4_super_info_data *sbinfo; + + ctx = get_current_context(); + sub_from_ctx_grabbed(ctx, count); + + sbinfo = get_super_private(ctx->super); + reiser4_spin_lock_sb(sbinfo); + + sub_from_sb_grabbed(sbinfo, count); + sbinfo->blocks_clustered += count; + + assert("edward-504", check_block_counters(ctx->super)); + + reiser4_spin_unlock_sb(sbinfo); +} + +reiser4_internal void +cluster_reserved2grabbed(int count) +{ + reiser4_context *ctx; + reiser4_super_info_data *sbinfo; + + ctx = get_current_context(); + + sbinfo = get_super_private(ctx->super); + reiser4_spin_lock_sb(sbinfo); + + sub_from_cluster_reserved(sbinfo, count); + sbinfo->blocks_grabbed += count; + + assert("edward-505", check_block_counters(ctx->super)); + + reiser4_spin_unlock_sb(sbinfo); + ctx->grabbed_blocks += count; +} + +reiser4_internal void +cluster_reserved2free(int count) +{ + reiser4_context *ctx; + reiser4_super_info_data *sbinfo; + + assert("edward-503", get_current_context()->grabbed_blocks == 0); + + ctx = get_current_context(); + sbinfo = get_super_private(ctx->super); + reiser4_spin_lock_sb(sbinfo); + + sub_from_cluster_reserved(sbinfo, count); + sbinfo->blocks_free += count; + + assert("edward-502", check_block_counters(ctx->super)); + + reiser4_spin_unlock_sb(sbinfo); +} + +static spinlock_t fake_lock = SPIN_LOCK_UNLOCKED; +static reiser4_block_nr fake_gen = 0; + +/* obtain a block number for new formatted node which will be used to refer + to this newly allocated node until real allocation is done */ +static inline void assign_fake_blocknr(reiser4_block_nr *blocknr) +{ + spin_lock(&fake_lock); + *blocknr = fake_gen++; + spin_unlock(&fake_lock); + + *blocknr &= ~REISER4_BLOCKNR_STATUS_BIT_MASK; + *blocknr |= REISER4_UNALLOCATED_STATUS_VALUE; + assert("zam-394", zlook(current_tree, blocknr) == NULL); +} + +reiser4_internal int +assign_fake_blocknr_formatted(reiser4_block_nr *blocknr) +{ + ON_TRACE(TRACE_RESERVE, "assign_fake_blocknr_formatted: moving 1 grabbed block to fake allocated formatted\n"); + + assign_fake_blocknr(blocknr); + grabbed2fake_allocated_formatted(); + + return 0; +} + +/* return fake blocknr which will be used for unformatted nodes */ +reiser4_internal reiser4_block_nr +fake_blocknr_unformatted(void) +{ + reiser4_block_nr blocknr; + + ON_TRACE(TRACE_RESERVE, "fake_blocknr_unformatted: moving 1 grabbed block to fake allocated unformatted\n"); + + assign_fake_blocknr(&blocknr); + grabbed2fake_allocated_unformatted(); + + /*XXXXX*/inc_unalloc_unfm_ptr(); + return blocknr; +} + + +/* adjust sb block counters, if real (on-disk) block allocation immediately + follows grabbing of free disk space. */ +static void +grabbed2used(reiser4_context *ctx, reiser4_super_info_data *sbinfo, __u64 count) +{ + sub_from_ctx_grabbed(ctx, count); + + reiser4_spin_lock_sb(sbinfo); + + sub_from_sb_grabbed(sbinfo, count); + sbinfo->blocks_used += count; + + assert("nikita-2679", check_block_counters(ctx->super)); + + reiser4_spin_unlock_sb(sbinfo); +} + +/* adjust sb block counters when @count unallocated blocks get mapped to disk */ +static void +fake_allocated2used(reiser4_super_info_data *sbinfo, __u64 count, reiser4_ba_flags_t flags) +{ + reiser4_spin_lock_sb(sbinfo); + + sub_from_sb_fake_allocated(sbinfo, count, flags); + sbinfo->blocks_used += count; + + assert("nikita-2680", check_block_counters(reiser4_get_current_sb())); + + reiser4_spin_unlock_sb(sbinfo); +} + +static void +flush_reserved2used(txn_atom * atom, __u64 count) +{ + reiser4_super_info_data *sbinfo; + + assert("zam-787", atom != NULL); + assert("zam-788", spin_atom_is_locked(atom)); + + sub_from_atom_flush_reserved_nolock(atom, (__u32)count); + + sbinfo = get_current_super_private(); + reiser4_spin_lock_sb(sbinfo); + + sub_from_sb_flush_reserved(sbinfo, count); + sbinfo->blocks_used += count; + + assert ("zam-789", check_block_counters(reiser4_get_current_sb())); + + reiser4_spin_unlock_sb(sbinfo); +} + +/* update the per fs blocknr hint default value. */ +reiser4_internal void +update_blocknr_hint_default (const struct super_block *s, const reiser4_block_nr * block) +{ + reiser4_super_info_data *sbinfo = get_super_private(s); + + assert("nikita-3342", !blocknr_is_fake(block)); + + reiser4_spin_lock_sb(sbinfo); + if (*block < sbinfo->block_count) { + sbinfo->blocknr_hint_default = *block; + } else { + warning("zam-676", + "block number %llu is too large to be used in a blocknr hint\n", (unsigned long long) *block); + dump_stack(); + DEBUGON(1); + } + reiser4_spin_unlock_sb(sbinfo); +} + +/* get current value of the default blocknr hint. */ +reiser4_internal void get_blocknr_hint_default(reiser4_block_nr * result) +{ + reiser4_super_info_data * sbinfo = get_current_super_private(); + + reiser4_spin_lock_sb(sbinfo); + *result = sbinfo->blocknr_hint_default; + assert("zam-677", *result < sbinfo->block_count); + reiser4_spin_unlock_sb(sbinfo); +} + +/* Allocate "real" disk blocks by calling a proper space allocation plugin + * method. Blocks are allocated in one contiguous disk region. The plugin + * independent part accounts blocks by subtracting allocated amount from grabbed + * or fake block counter and add the same amount to the counter of allocated + * blocks. + * + * @hint -- a reiser4 blocknr hint object which contains further block + * allocation hints and parameters (search start, a stage of block + * which will be mapped to disk, etc.), + * @blk -- an out parameter for the beginning of the allocated region, + * @len -- in/out parameter, it should contain the maximum number of allocated + * blocks, after block allocation completes, it contains the length of + * allocated disk region. + * @flags -- see reiser4_ba_flags_t description. + * + * @return -- 0 if success, error code otherwise. + */ +reiser4_internal int +reiser4_alloc_blocks(reiser4_blocknr_hint * hint, reiser4_block_nr * blk, + reiser4_block_nr * len, reiser4_ba_flags_t flags) +{ + __u64 needed = *len; + reiser4_context *ctx; + reiser4_super_info_data *sbinfo; + int ret; + + assert ("zam-986", hint != NULL); + + ctx = get_current_context(); + sbinfo = get_super_private(ctx->super); + + ON_TRACE(TRACE_RESERVE, "reiser4_alloc_blocks: needed %llu..", needed); + + assert("vpf-339", hint != NULL); + + ON_TRACE(TRACE_ALLOC, + "alloc_blocks: requested %llu, search from %llu\n", + (unsigned long long) *len, (unsigned long long) (hint ? hint->blk : ~0ull)); + + /* For write-optimized data we use default search start value, which is + * close to last write location. */ + if (flags & BA_USE_DEFAULT_SEARCH_START) { + reiser4_stat_inc(block_alloc.nohint); + get_blocknr_hint_default(&hint->blk); + } + + /* VITALY: allocator should grab this for internal/tx-lists/similar only. */ +/* VS-FIXME-HANS: why is this comment above addressed to vitaly (from vitaly)? */ + if (hint->block_stage == BLOCK_NOT_COUNTED) { + ret = reiser4_grab_space_force(*len, flags); + if (ret != 0) + return ret; + } + + ret = sa_alloc_blocks(get_space_allocator(ctx->super), hint, (int) needed, blk, len); + + if (!ret) { + assert("zam-680", *blk < reiser4_block_count(ctx->super)); + assert("zam-681", *blk + *len <= reiser4_block_count(ctx->super)); + + if (flags & BA_PERMANENT) { + /* we assume that current atom exists at this moment */ + txn_atom * atom = get_current_atom_locked (); + atom -> nr_blocks_allocated += *len; + UNLOCK_ATOM (atom); + } + + switch (hint->block_stage) { + case BLOCK_NOT_COUNTED: + case BLOCK_GRABBED: + ON_TRACE(TRACE_RESERVE, "ok. %llu blocks grabbed to used.\n", *len); + grabbed2used(ctx, sbinfo, *len); + break; + case BLOCK_UNALLOCATED: + ON_TRACE(TRACE_RESERVE, "ok. %llu blocks fake allocated to used.\n", *len); + fake_allocated2used(sbinfo, *len, flags); + break; + case BLOCK_FLUSH_RESERVED: + ON_TRACE(TRACE_RESERVE, "ok. %llu flush reserved to used (get wandered?)\n", *len); + { + txn_atom * atom = get_current_atom_locked (); + flush_reserved2used(atom, *len); + UNLOCK_ATOM (atom); + } + break; + default: + impossible("zam-531", "wrong block stage"); + } + } else { + assert ("zam-821", ergo(hint->max_dist == 0 && !hint->backward, ret != -ENOSPC)); + if (hint->block_stage == BLOCK_NOT_COUNTED) + grabbed2free(ctx, sbinfo, needed); + } + + return ret; +} + +/* used -> fake_allocated -> grabbed -> free */ + +/* adjust sb block counters when @count unallocated blocks get unmapped from + disk */ +static void +used2fake_allocated(reiser4_super_info_data *sbinfo, __u64 count, int formatted) +{ + reiser4_spin_lock_sb(sbinfo); + + if (formatted) + sbinfo->blocks_fake_allocated += count; + else + sbinfo->blocks_fake_allocated_unformatted += count; + + sub_from_sb_used(sbinfo, count); + + assert("nikita-2681", check_block_counters(reiser4_get_current_sb())); + + reiser4_spin_unlock_sb(sbinfo); +} + +static void +used2flush_reserved(reiser4_super_info_data *sbinfo, txn_atom * atom, __u64 count, + reiser4_ba_flags_t flags UNUSED_ARG) +{ + assert("nikita-2791", atom != NULL); + assert("nikita-2792", spin_atom_is_locked(atom)); + + add_to_atom_flush_reserved_nolock(atom, (__u32)count); + + reiser4_spin_lock_sb(sbinfo); + + sbinfo->blocks_flush_reserved += count; + /*add_to_sb_flush_reserved(sbinfo, count);*/ + sub_from_sb_used(sbinfo, count); + + assert("nikita-2681", check_block_counters(reiser4_get_current_sb())); + + reiser4_spin_unlock_sb(sbinfo); +} + +/* disk space, virtually used by fake block numbers is counted as "grabbed" again. */ +static void +fake_allocated2grabbed(reiser4_context *ctx, reiser4_super_info_data *sbinfo, __u64 count, reiser4_ba_flags_t flags) +{ + ctx->grabbed_blocks += count; + + reiser4_spin_lock_sb(sbinfo); + + assert("nikita-2682", check_block_counters(ctx->super)); + + sbinfo->blocks_grabbed += count; + sub_from_sb_fake_allocated(sbinfo, count, flags & BA_FORMATTED); + + assert("nikita-2683", check_block_counters(ctx->super)); + + reiser4_spin_unlock_sb(sbinfo); +} + +reiser4_internal void +fake_allocated2free(__u64 count, reiser4_ba_flags_t flags) +{ + reiser4_context *ctx; + reiser4_super_info_data *sbinfo; + + ctx = get_current_context(); + sbinfo = get_super_private(ctx->super); + + ON_TRACE(TRACE_RESERVE, "fake_allocated2free %llu blocks\n", count); + + fake_allocated2grabbed(ctx, sbinfo, count, flags); + grabbed2free(ctx, sbinfo, count); +} + +reiser4_internal void +grabbed2free_mark(__u64 mark) +{ + reiser4_context *ctx; + reiser4_super_info_data *sbinfo; + + ctx = get_current_context(); + sbinfo = get_super_private(ctx->super); + + assert("nikita-3007", (__s64)mark >= 0); + assert("nikita-3006", + ctx->grabbed_blocks >= mark); + grabbed2free(ctx, sbinfo, ctx->grabbed_blocks - mark); +} + +/* Adjust free blocks count for blocks which were reserved but were not used. */ +reiser4_internal void +grabbed2free(reiser4_context *ctx, reiser4_super_info_data *sbinfo, + __u64 count) +{ + ON_TRACE(TRACE_RESERVE, "grabbed2free: %llu\n", count); + + sub_from_ctx_grabbed(ctx, count); + + + reiser4_spin_lock_sb(sbinfo); + + sub_from_sb_grabbed(sbinfo, count); + sbinfo->blocks_free += count; + assert("nikita-2684", check_block_counters(ctx->super)); + + reiser4_spin_unlock_sb(sbinfo); +} + +reiser4_internal void +grabbed2flush_reserved_nolock(txn_atom * atom, __u64 count) +{ + reiser4_context *ctx; + reiser4_super_info_data *sbinfo; + + assert("vs-1095", atom); + + ctx = get_current_context(); + sbinfo = get_super_private(ctx->super); + + sub_from_ctx_grabbed(ctx, count); + + add_to_atom_flush_reserved_nolock(atom, count); + + reiser4_spin_lock_sb(sbinfo); + + sbinfo->blocks_flush_reserved += count; + sub_from_sb_grabbed(sbinfo, count); + + assert ("vpf-292", check_block_counters(ctx->super)); + + ON_TRACE(TRACE_RESERVE, "__grabbed2flush_reserved_nolock %llu blocks: atom %u has %llu flush reserved blocks\n", + count, atom->atom_id, atom->flush_reserved); + + reiser4_spin_unlock_sb(sbinfo); +} + +reiser4_internal void +grabbed2flush_reserved(__u64 count) +{ + txn_atom * atom = get_current_atom_locked (); + + ON_TRACE(TRACE_RESERVE, "__grabbed2flush_reserved\n"); + + grabbed2flush_reserved_nolock (atom, count); + + UNLOCK_ATOM (atom); +} + +reiser4_internal void flush_reserved2grabbed(txn_atom * atom, __u64 count) +{ + reiser4_context *ctx; + reiser4_super_info_data *sbinfo; + + assert("nikita-2788", atom != NULL); + assert("nikita-2789", spin_atom_is_locked(atom)); + + ctx = get_current_context(); + sbinfo = get_super_private(ctx->super); + + ctx->grabbed_blocks += count; + + sub_from_atom_flush_reserved_nolock(atom, (__u32)count); + + reiser4_spin_lock_sb(sbinfo); + + sbinfo->blocks_grabbed += count; + sub_from_sb_flush_reserved(sbinfo, count); + + assert ("vpf-292", check_block_counters (ctx->super)); + + reiser4_spin_unlock_sb (sbinfo); +} + +/* release all blocks grabbed in context which where not used. */ +reiser4_internal void +all_grabbed2free(void) +{ + reiser4_context *ctx = get_current_context(); + + grabbed2free(ctx, get_super_private(ctx->super), ctx->grabbed_blocks); +} + +/* adjust sb block counters if real (on-disk) blocks do not become unallocated + after freeing, @count blocks become "grabbed". */ +static void +used2grabbed(reiser4_context *ctx, reiser4_super_info_data *sbinfo, __u64 count) +{ + ctx->grabbed_blocks += count; + + reiser4_spin_lock_sb(sbinfo); + + sbinfo->blocks_grabbed += count; + sub_from_sb_used(sbinfo, count); + + assert("nikita-2685", check_block_counters(ctx->super)); + + reiser4_spin_unlock_sb(sbinfo); +} + +/* this used to be done through used2grabbed and grabbed2free*/ +static void +used2free(reiser4_super_info_data *sbinfo, __u64 count) +{ + reiser4_spin_lock_sb(sbinfo); + + sbinfo->blocks_free += count; + sub_from_sb_used(sbinfo, count); + + assert("nikita-2685", check_block_counters(reiser4_get_current_sb())); + + reiser4_spin_unlock_sb(sbinfo); +} + +#if REISER4_DEBUG + +/* check "allocated" state of given block range */ +void +reiser4_check_blocks(const reiser4_block_nr * start, const reiser4_block_nr * len, int desired) +{ + sa_check_blocks(start, len, desired); +} + +/* check "allocated" state of given block */ +void +reiser4_check_block(const reiser4_block_nr * block, int desired) +{ + const reiser4_block_nr one = 1; + + reiser4_check_blocks(block, &one, desired); +} + +#endif + +/* Blocks deallocation function may do an actual deallocation through space + plugin allocation or store deleted block numbers in atom's delete_set data + structure depend on @defer parameter. */ + +/* if BA_DEFER bit is not turned on, @target_stage means the stage of blocks which + will be deleted from WORKING bitmap. They might be just unmapped from disk, or + freed but disk space is still grabbed by current thread, or these blocks must + not be counted in any reiser4 sb block counters, see block_stage_t comment */ + +/* BA_FORMATTED bit is only used when BA_DEFER in not present: it is used to + distinguish blocks allocated for unformatted and formatted nodes */ + +reiser4_internal int +reiser4_dealloc_blocks(const reiser4_block_nr * start, + const reiser4_block_nr * len, + block_stage_t target_stage, reiser4_ba_flags_t flags) +{ + txn_atom *atom = NULL; + int ret; + reiser4_context *ctx; + reiser4_super_info_data *sbinfo; + + ON_TRACE(TRACE_RESERVE, "reiser4_dealloc_blocks: %llu blocks", *len); + + ctx = get_current_context(); + sbinfo = get_super_private(ctx->super); + + if (REISER4_DEBUG) { + assert("zam-431", *len != 0); + assert("zam-432", *start != 0); + assert("zam-558", !blocknr_is_fake(start)); + + reiser4_spin_lock_sb(sbinfo); + assert("zam-562", *start < sbinfo->block_count); + reiser4_spin_unlock_sb(sbinfo); + } + + if (flags & BA_DEFER) { + blocknr_set_entry *bsep = NULL; + + ON_TRACE(TRACE_RESERVE, "put on delete set\n"); + + /* storing deleted block numbers in a blocknr set + datastructure for further actual deletion */ + do { + atom = get_current_atom_locked(); + assert("zam-430", atom != NULL); + + ret = blocknr_set_add_extent(atom, &atom->delete_set, &bsep, start, len); + + if (ret == -ENOMEM) + return ret; + + /* This loop might spin at most two times */ + } while (ret == -E_REPEAT); + + assert("zam-477", ret == 0); + assert("zam-433", atom != NULL); + + UNLOCK_ATOM(atom); + + } else { + assert("zam-425", get_current_super_private() != NULL); + sa_dealloc_blocks(get_space_allocator(ctx->super), *start, *len); + + if (flags & BA_PERMANENT) { + /* These blocks were counted as allocated, we have to revert it + * back if allocation is discarded. */ + txn_atom * atom = get_current_atom_locked (); + atom->nr_blocks_allocated -= *len; + UNLOCK_ATOM (atom); + } + + switch (target_stage) { + case BLOCK_NOT_COUNTED: + assert("vs-960", flags & BA_FORMATTED); + + ON_TRACE(TRACE_RESERVE, "moved from used to free\n"); + + /* VITALY: This is what was grabbed for internal/tx-lists/similar only */ + used2free(sbinfo, *len); + break; + + case BLOCK_GRABBED: + + ON_TRACE(TRACE_RESERVE, "moved from used to grabbed\n"); + + used2grabbed(ctx, sbinfo, *len); + break; + + case BLOCK_UNALLOCATED: + + ON_TRACE(TRACE_RESERVE, "moved from used to fake allocated\n"); + + used2fake_allocated(sbinfo, *len, flags & BA_FORMATTED); + break; + + case BLOCK_FLUSH_RESERVED: { + txn_atom *atom; + + ON_TRACE(TRACE_RESERVE, "moved from used to flush reserved\n"); + + atom = get_current_atom_locked(); + used2flush_reserved(sbinfo, atom, *len, flags & BA_FORMATTED); + UNLOCK_ATOM(atom); + break; + } + default: + impossible("zam-532", "wrong block stage"); + } + } + + return 0; +} + +/* wrappers for block allocator plugin methods */ +reiser4_internal int +pre_commit_hook(void) +{ + assert("zam-502", get_current_super_private() != NULL); + sa_pre_commit_hook(); + return 0; +} + +/* an actor which applies delete set to block allocator data */ +static int +apply_dset(txn_atom * atom UNUSED_ARG, const reiser4_block_nr * a, const reiser4_block_nr * b, void *data UNUSED_ARG) +{ + reiser4_context *ctx; + reiser4_super_info_data *sbinfo; + + __u64 len = 1; + + ctx = get_current_context(); + sbinfo = get_super_private(ctx->super); + + assert("zam-877", atom->stage >= ASTAGE_PRE_COMMIT); + assert("zam-552", sbinfo != NULL); + + if (b != NULL) + len = *b; + + if (REISER4_DEBUG) { + reiser4_spin_lock_sb(sbinfo); + + assert("zam-554", *a < reiser4_block_count(ctx->super)); + assert("zam-555", *a + len <= reiser4_block_count(ctx->super)); + + reiser4_spin_unlock_sb(sbinfo); + } + + sa_dealloc_blocks(&sbinfo->space_allocator, *a, len); + /* adjust sb block counters */ + used2free(sbinfo, len); + return 0; +} + +reiser4_internal void +post_commit_hook(void) +{ + txn_atom *atom; + + atom = get_current_atom_locked(); + assert("zam-452", atom->stage == ASTAGE_POST_COMMIT); + UNLOCK_ATOM(atom); + + /* do the block deallocation which was deferred + until commit is done */ + blocknr_set_iterator(atom, &atom->delete_set, apply_dset, NULL, 1); + + assert("zam-504", get_current_super_private() != NULL); + sa_post_commit_hook(); +} + +reiser4_internal void +post_write_back_hook(void) +{ + assert("zam-504", get_current_super_private() != NULL); + + sa_post_commit_hook(); +} + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/block_alloc.h 2004-09-03 00:57:04.965284424 -0700 @@ -0,0 +1,185 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#if !defined (__FS_REISER4_BLOCK_ALLOC_H__) +#define __FS_REISER4_BLOCK_ALLOC_H__ + +#include "dformat.h" +#include "forward.h" + +#include /* for __u?? */ +#include + +/* Mask when is applied to given block number shows is that block number is a fake one */ +#define REISER4_FAKE_BLOCKNR_BIT_MASK 0x8000000000000000ULL +/* Mask which isolates a type of object this fake block number was assigned to */ +#define REISER4_BLOCKNR_STATUS_BIT_MASK 0xC000000000000000ULL + +/*result after applying the REISER4_BLOCKNR_STATUS_BIT_MASK should be compared + against these two values to understand is the object unallocated or bitmap + shadow object (WORKING BITMAP block, look at the plugin/space/bitmap.c) */ +#define REISER4_UNALLOCATED_STATUS_VALUE 0xC000000000000000ULL +#define REISER4_BITMAP_BLOCKS_STATUS_VALUE 0x8000000000000000ULL + +/* specification how block allocation was counted in sb block counters */ +typedef enum { + BLOCK_NOT_COUNTED = 0, /* reiser4 has no info about this block yet */ + BLOCK_GRABBED = 1, /* free space grabbed for further allocation + of this block */ + BLOCK_FLUSH_RESERVED = 2, /* block is reserved for flush needs. */ + BLOCK_UNALLOCATED = 3, /* block is used for existing in-memory object + ( unallocated formatted or unformatted + node) */ + BLOCK_ALLOCATED = 4 /* block is mapped to disk, real on-disk block + number assigned */ +} block_stage_t; + +/* a hint for block allocator */ +struct reiser4_blocknr_hint { + /* FIXME: I think we want to add a longterm lock on the bitmap block here. This + is to prevent jnode_flush() calls from interleaving allocations on the same + bitmap, once a hint is established. */ + + /* search start hint */ + reiser4_block_nr blk; + /* if not zero, it is a region size we search for free blocks in */ + reiser4_block_nr max_dist; + /* level for allocation, may be useful have branch-level and higher + write-optimized. */ + tree_level level; + /* block allocator assumes that blocks, which will be mapped to disk, + are in this specified block_stage */ + block_stage_t block_stage; + /* If direction = 1 allocate blocks in backward direction from the end + * of disk to the beginning of disk. */ + int backward:1; + +}; + +/* These flags control block allocation/deallocation behavior */ +enum reiser4_ba_flags { + /* do allocatations from reserved (5%) area */ + BA_RESERVED = (1 << 0), + + /* block allocator can do commit trying to recover free space */ + BA_CAN_COMMIT = (1 << 1), + + /* if operation will be applied to formatted block */ + BA_FORMATTED = (1 << 2), + + /* defer actual block freeing until transaction commit */ + BA_DEFER = (1 << 3), + + /* allocate blocks for permanent fs objects (formatted or unformatted), not + wandered of log blocks */ + BA_PERMANENT = (1 << 4), + + /* grab space even it was disabled */ + BA_FORCE = (1 << 5), + + /* use default start value for free blocks search. */ + BA_USE_DEFAULT_SEARCH_START = (1 << 6) +}; + +typedef enum reiser4_ba_flags reiser4_ba_flags_t; + +extern void blocknr_hint_init(reiser4_blocknr_hint * hint); +extern void blocknr_hint_done(reiser4_blocknr_hint * hint); +extern void update_blocknr_hint_default(const struct super_block *, const reiser4_block_nr *); +extern void get_blocknr_hint_default(reiser4_block_nr *); + +extern reiser4_block_nr reiser4_fs_reserved_space(struct super_block * super); + +int assign_fake_blocknr_formatted(reiser4_block_nr *); +reiser4_block_nr fake_blocknr_unformatted(void); + + +/* free -> grabbed -> fake_allocated -> used */ + + +int reiser4_grab_space (__u64 count, reiser4_ba_flags_t flags); +void all_grabbed2free (void); +void grabbed2free (reiser4_context *, + reiser4_super_info_data *, __u64 count); +void fake_allocated2free (__u64 count, reiser4_ba_flags_t flags); +void grabbed2flush_reserved_nolock(txn_atom * atom, __u64 count); +void grabbed2flush_reserved (__u64 count); +int reiser4_alloc_blocks (reiser4_blocknr_hint * hint, + reiser4_block_nr * start, + reiser4_block_nr * len, + reiser4_ba_flags_t flags); +int reiser4_dealloc_blocks (const reiser4_block_nr *, + const reiser4_block_nr *, + block_stage_t, reiser4_ba_flags_t flags); + +static inline int reiser4_alloc_block (reiser4_blocknr_hint * hint, reiser4_block_nr * start, + reiser4_ba_flags_t flags) +{ + reiser4_block_nr one = 1; + return reiser4_alloc_blocks(hint, start, &one, flags); +} + +static inline int reiser4_dealloc_block (const reiser4_block_nr * block, block_stage_t stage, reiser4_ba_flags_t flags) +{ + const reiser4_block_nr one = 1; + return reiser4_dealloc_blocks(block, &one, stage, flags); +} + +#define reiser4_grab_space_force(count, flags) \ + reiser4_grab_space(count, flags | BA_FORCE) + +extern void grabbed2free_mark(__u64 mark); +extern int reiser4_grab_reserved(struct super_block *, + __u64, reiser4_ba_flags_t); +extern void reiser4_release_reserved(struct super_block *super); + +/* grabbed -> fake_allocated */ + +/* fake_allocated -> used */ + +/* used -> fake_allocated -> grabbed -> free */ + +extern void flush_reserved2grabbed(txn_atom * atom, __u64 count); + +extern int blocknr_is_fake(const reiser4_block_nr * da); + +extern void grabbed2cluster_reserved(int count); +extern void cluster_reserved2grabbed(int count); +extern void cluster_reserved2free(int count); + +extern int check_block_counters(const struct super_block *); + +#if REISER4_DEBUG + +extern void reiser4_check_blocks(const reiser4_block_nr *, const reiser4_block_nr *, int); +extern void reiser4_check_block(const reiser4_block_nr *, int); + +#else + +# define reiser4_check_blocks(beg, len, val) noop +# define reiser4_check_block(beg, val) noop + +#endif + +#if REISER4_DEBUG_OUTPUT +extern void print_block_counters(const char *, + const struct super_block *, + txn_atom *atom); +#else +#define print_block_counters(p, s, a) noop +#endif + +extern int pre_commit_hook(void); +extern void post_commit_hook(void); +extern void post_write_back_hook(void); + +#endif /* __FS_REISER4_BLOCK_ALLOC_H__ */ + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/blocknrset.c 2004-09-03 00:57:04.966284272 -0700 @@ -0,0 +1,365 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* This file contains code for various block number sets used by the atom to + track the deleted set and wandered block mappings. */ + +#include "debug.h" +#include "dformat.h" +#include "type_safe_list.h" +#include "txnmgr.h" + +#include + +/* The proposed data structure for storing unordered block number sets is a + list of elements, each of which contains an array of block number or/and + array of block number pairs. That element called blocknr_set_entry is used + to store block numbers from the beginning and for extents from the end of + the data field (char data[...]). The ->nr_blocks and ->nr_pairs fields + count numbers of blocks and extents. + + +------------------- blocknr_set_entry->data ------------------+ + |block1|block2| ... ... |pair3|pair2|pair1| + +------------------------------------------------------------+ + + When current blocknr_set_entry is full, allocate a new one. */ + +/* Usage examples: blocknr sets are used in reiser4 for storing atom's delete + * set (single blocks and block extents), in that case blocknr pair represent an + * extent; atom's wandered map is also stored as a blocknr set, blocknr pairs + * there represent a (real block) -> (wandered block) mapping. */ + +typedef struct blocknr_pair blocknr_pair; + +/* The total size of a blocknr_set_entry. */ +#define BLOCKNR_SET_ENTRY_SIZE 128 + +/* The number of blocks that can fit the blocknr data area. */ +#define BLOCKNR_SET_ENTRIES_NUMBER \ + ((BLOCKNR_SET_ENTRY_SIZE - \ + 2 * sizeof (unsigned) - \ + sizeof (blocknr_set_list_link)) / \ + sizeof (reiser4_block_nr)) + +/* An entry of the blocknr_set */ +struct blocknr_set_entry { + unsigned nr_singles; + unsigned nr_pairs; + blocknr_set_list_link link; + reiser4_block_nr entries[BLOCKNR_SET_ENTRIES_NUMBER]; +}; + +/* A pair of blocks as recorded in the blocknr_set_entry data. */ +struct blocknr_pair { + reiser4_block_nr a; + reiser4_block_nr b; +}; + +/* The list definition. */ +TYPE_SAFE_LIST_DEFINE(blocknr_set, blocknr_set_entry, link); + +/* Return the number of blocknr slots available in a blocknr_set_entry. */ +/* Audited by: green(2002.06.11) */ +static unsigned +bse_avail(blocknr_set_entry * bse) +{ + unsigned used = bse->nr_singles + 2 * bse->nr_pairs; + + assert("jmacd-5088", BLOCKNR_SET_ENTRIES_NUMBER >= used); + cassert(sizeof (blocknr_set_entry) == BLOCKNR_SET_ENTRY_SIZE); + + return BLOCKNR_SET_ENTRIES_NUMBER - used; +} + +/* Initialize a blocknr_set_entry. */ +/* Audited by: green(2002.06.11) */ +static void +bse_init(blocknr_set_entry * bse) +{ + bse->nr_singles = 0; + bse->nr_pairs = 0; + blocknr_set_list_clean(bse); +} + +/* Allocate and initialize a blocknr_set_entry. */ +/* Audited by: green(2002.06.11) */ +static blocknr_set_entry * +bse_alloc(void) +{ + blocknr_set_entry *e; + + if ((e = (blocknr_set_entry *) kmalloc(sizeof (blocknr_set_entry), GFP_KERNEL)) == NULL) { + return NULL; + } + + bse_init(e); + + return e; +} + +/* Free a blocknr_set_entry. */ +/* Audited by: green(2002.06.11) */ +static void +bse_free(blocknr_set_entry * bse) +{ + kfree(bse); +} + +/* Add a block number to a blocknr_set_entry */ +/* Audited by: green(2002.06.11) */ +static void +bse_put_single(blocknr_set_entry * bse, const reiser4_block_nr * block) +{ + assert("jmacd-5099", bse_avail(bse) >= 1); + + bse->entries[bse->nr_singles++] = *block; +} + +/* Get a pair of block numbers */ +/* Audited by: green(2002.06.11) */ +static inline blocknr_pair * +bse_get_pair(blocknr_set_entry * bse, unsigned pno) +{ + assert("green-1", BLOCKNR_SET_ENTRIES_NUMBER >= 2 * (pno + 1)); + + return (blocknr_pair *) (bse->entries + BLOCKNR_SET_ENTRIES_NUMBER - 2 * (pno + 1)); +} + +/* Add a pair of block numbers to a blocknr_set_entry */ +/* Audited by: green(2002.06.11) */ +static void +bse_put_pair(blocknr_set_entry * bse, const reiser4_block_nr * a, const reiser4_block_nr * b) +{ + blocknr_pair *pair; + + assert("jmacd-5100", bse_avail(bse) >= 2 && a != NULL && b != NULL); + + pair = bse_get_pair(bse, bse->nr_pairs++); + + pair->a = *a; + pair->b = *b; +} + +/* Add either a block or pair of blocks to the block number set. The first + blocknr (@a) must be non-NULL. If @b is NULL a single blocknr is added, if + @b is non-NULL a pair is added. The block number set belongs to atom, and + the call is made with the atom lock held. There may not be enough space in + the current blocknr_set_entry. If new_bsep points to a non-NULL + blocknr_set_entry then it will be added to the blocknr_set and new_bsep + will be set to NULL. If new_bsep contains NULL then the atom lock will be + released and a new bse will be allocated in new_bsep. E_REPEAT will be + returned with the atom unlocked for the operation to be tried again. If + the operation succeeds, 0 is returned. If new_bsep is non-NULL and not + used during the call, it will be freed automatically. */ +/* Audited by: green(2002.06.11) */ +static int +blocknr_set_add(txn_atom * atom, + blocknr_set * bset, + blocknr_set_entry ** new_bsep, const reiser4_block_nr * a, const reiser4_block_nr * b) +{ + blocknr_set_entry *bse; + unsigned entries_needed; + + assert("jmacd-5101", a != NULL); + + entries_needed = (b == NULL) ? 1 : 2; + if (blocknr_set_list_empty(&bset->entries) || bse_avail(blocknr_set_list_front(&bset->entries)) + < entries_needed) { + /* See if a bse was previously allocated. */ + if (*new_bsep == NULL) { + UNLOCK_ATOM(atom); + *new_bsep = bse_alloc(); + return (*new_bsep != NULL) ? -E_REPEAT : RETERR(-ENOMEM); + } + + /* Put it on the head of the list. */ + blocknr_set_list_push_front(&bset->entries, *new_bsep); + + *new_bsep = NULL; + } + + /* Add the single or pair. */ + bse = blocknr_set_list_front(&bset->entries); + if (b == NULL) { + bse_put_single(bse, a); + } else { + bse_put_pair(bse, a, b); + } + + /* If new_bsep is non-NULL then there was an allocation race, free this copy. */ + if (*new_bsep != NULL) { + bse_free(*new_bsep); + *new_bsep = NULL; + } + + return 0; +} + +/* Add an extent to the block set. If the length is 1, it is treated as a + single block (e.g., reiser4_set_add_block). */ +/* Audited by: green(2002.06.11) */ +/* Auditor note: Entire call chain cannot hold any spinlocks, because + kmalloc might schedule. The only exception is atom spinlock, which is + properly freed. */ +reiser4_internal int +blocknr_set_add_extent(txn_atom * atom, + blocknr_set * bset, + blocknr_set_entry ** new_bsep, const reiser4_block_nr * start, const reiser4_block_nr * len) +{ + assert("jmacd-5102", start != NULL && len != NULL && *len > 0); + return blocknr_set_add(atom, bset, new_bsep, start, *len == 1 ? NULL : len); +} + +/* Add a block pair to the block set. It adds exactly a pair, which is checked + * by an assertion that both arguments are not null.*/ +/* Audited by: green(2002.06.11) */ +/* Auditor note: Entire call chain cannot hold any spinlocks, because + kmalloc might schedule. The only exception is atom spinlock, which is + properly freed. */ +reiser4_internal int +blocknr_set_add_pair(txn_atom * atom, + blocknr_set * bset, + blocknr_set_entry ** new_bsep, const reiser4_block_nr * a, const reiser4_block_nr * b) +{ + assert("jmacd-5103", a != NULL && b != NULL); + return blocknr_set_add(atom, bset, new_bsep, a, b); +} + +/* Initialize a blocknr_set. */ +/* Audited by: green(2002.06.11) */ +reiser4_internal void +blocknr_set_init(blocknr_set * bset) +{ + blocknr_set_list_init(&bset->entries); +} + +/* Release the entries of a blocknr_set. */ +/* Audited by: green(2002.06.11) */ +reiser4_internal void +blocknr_set_destroy(blocknr_set * bset) +{ + while (!blocknr_set_list_empty(&bset->entries)) { + bse_free(blocknr_set_list_pop_front(&bset->entries)); + } +} + +/* Merge blocknr_set entries out of @from into @into. */ +/* Audited by: green(2002.06.11) */ +/* Auditor comments: This merge does not know if merged sets contain + blocks pairs (As for wandered sets) or extents, so it cannot really merge + overlapping ranges if there is some. So I believe it may lead to + some blocks being presented several times in one blocknr_set. To help + debugging such problems it might help to check for duplicate entries on + actual processing of this set. Testing this kind of stuff right here is + also complicated by the fact that these sets are not sorted and going + through whole set on each element addition is going to be CPU-heavy task */ +reiser4_internal void +blocknr_set_merge(blocknr_set * from, blocknr_set * into) +{ + blocknr_set_entry *bse_into = NULL; + + /* If @from is empty, no work to perform. */ + if (blocknr_set_list_empty(&from->entries)) { + return; + } + + /* If @into is not empty, try merging partial-entries. */ + if (!blocknr_set_list_empty(&into->entries)) { + + /* Neither set is empty, pop the front to members and try to combine them. */ + blocknr_set_entry *bse_from; + unsigned into_avail; + + bse_into = blocknr_set_list_pop_front(&into->entries); + bse_from = blocknr_set_list_pop_front(&from->entries); + + /* Combine singles. */ + for (into_avail = bse_avail(bse_into); into_avail != 0 && bse_from->nr_singles != 0; into_avail -= 1) { + bse_put_single(bse_into, &bse_from->entries[--bse_from->nr_singles]); + } + + /* Combine pairs. */ + for (; into_avail > 1 && bse_from->nr_pairs != 0; into_avail -= 2) { + blocknr_pair *pair = bse_get_pair(bse_from, --bse_from->nr_pairs); + bse_put_pair(bse_into, &pair->a, &pair->b); + } + + /* If bse_from is empty, delete it now. */ + if (bse_avail(bse_from) == BLOCKNR_SET_ENTRIES_NUMBER) { + bse_free(bse_from); + } else { + /* Otherwise, bse_into is full or nearly full (e.g., + it could have one slot avail and bse_from has one + pair left). Push it back onto the list. bse_from + becomes bse_into, which will be the new partial. */ + blocknr_set_list_push_front(&into->entries, bse_into); + bse_into = bse_from; + } + } + + /* Splice lists together. */ + blocknr_set_list_splice(&into->entries, &from->entries); + + /* Add the partial entry back to the head of the list. */ + if (bse_into != NULL) { + blocknr_set_list_push_front(&into->entries, bse_into); + } +} + +/* Iterate over all blocknr set elements. */ +reiser4_internal int +blocknr_set_iterator(txn_atom * atom, blocknr_set * bset, blocknr_set_actor_f actor, void *data, int delete) +{ + + blocknr_set_entry *entry; + + assert("zam-429", atom != NULL); + assert("zam-430", atom_is_protected(atom)); + assert("zam-431", bset != 0); + assert("zam-432", actor != NULL); + + entry = blocknr_set_list_front(&bset->entries); + while (!blocknr_set_list_end(&bset->entries, entry)) { + blocknr_set_entry *tmp = blocknr_set_list_next(entry); + unsigned int i; + int ret; + + for (i = 0; i < entry->nr_singles; i++) { + ret = actor(atom, &entry->entries[i], NULL, data); + + /* We can't break a loop if delete flag is set. */ + if (ret != 0 && !delete) + return ret; + } + + for (i = 0; i < entry->nr_pairs; i++) { + struct blocknr_pair *ab; + + ab = bse_get_pair(entry, i); + + ret = actor(atom, &ab->a, &ab->b, data); + + if (ret != 0 && !delete) + return ret; + } + + if (delete) { + blocknr_set_list_remove(entry); + bse_free(entry); + } + + entry = tmp; + } + + return 0; +} + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/bufmgr/wander.txt 2004-09-03 00:57:04.968283968 -0700 @@ -0,0 +1,184 @@ + +Before discussing the format of the commit record occupying the +journal area, we must revisit the topic of free space bitmap +management. At the time an atom is closing and formatting its commit +record, the question is how to deallocate the blocks deleted by the +atom. Those blocks become free once the atom commits, but they cannot +be re-allocated before that point in time. + +Modified bitmaps are always part of the overwrite set, meaning copies +are written to wandered positions (i.e., part of the log) before later +being overwritten. + +We have defined these terms: + +WORKING BITMAPS: the "current" in-memory bitmaps + +COMMIT BITMAPS: bitmap copies written to wandered, overwrite positions + +DELETE SET: the set of deleted blocks plus the set of former positions +of relocated blocks. These block positions are deallocated when the +atom commits. + +WANDERED SET: the set of temporary locations used to store overwrite +blocks before they are actually overwritten. These block positions +are deallocated some time after the atom commits, when it is ensured +that the atom will no longer replay during crash recovery. + +Both the delete set and the wandered set are blocks to be deleted, but +the details of handling these deletions are necessarily different. + +---- Consider first the handling of the DELETE SET. + +There are two ways to handle the delete set. Before reading their +descriptions, let me offer my opinion. The first is MORE complicated +but requires LESS data to be logged in the commit record. The second +is LESS complicated but requires MORE data to be logged in the commit +record. + +Strategy #1: MORE COMPLICATED, LESS LOGGED DATA + + At the time an atom closes, it creates a snapshot of all the + modified bitmaps. In other words, it creates commit bitmaps which + are copies of the working bitmaps. The delete set are immediately + deallocated in the commit bitmaps, which are written to their + wandered positions and later overwritten in their actual positions. + + This way, the commit record does not contain any record of the + delete set. + + But there are problems with this approach, too. First, there is + extra memory pressure associated with maintaining extra copies of + modified bitmaps. Second, it is less straight forward than it may + appear at first. Suppose there are two atoms that commit in + sequence, such that the first does not complete its commit (i.e., + finish all the required writes) before the second prepares to + commit. Which bitmaps does the second committing atom copy as its + commit bitmaps? It does not just copy the working bitmaps, since + those do not yet represent the first atom deallocations. + + Instead, it looks like we would end up maintaining multiple copies + of every bitmap. Each atom's commit bitmaps are the commit bitmaps + of the previous atom plus whatever modifications were made by the + atom itself. This means in addition to maintaining the working + bitmaps, we end up maintaining separate commit bitmaps. It is not + just as simple as copying the working bitmaps at the time of commit. + + This solution looks far too complicated to me. I admit that I have + not fully tried to understand the complexity, but I do not think the + advantages (smaller commit records) will outweigh the additional + complexity, not to mention the additional memory pressure. + +Strategy #2: LESS COMPLICATED, MORE LOGGED DATA + + In this solution, the commit bitmaps are the same as the working + bitmaps--no copies are made. We commit the working bitmaps without + deallocating the delete set and we include the delete set in the + commit record instead. + + Before I describe exactly how deallocation works in this case, let + me add that there is another reason why this method is preferred. + The wandered set has to be deleted after the atom commits, since it + does not become available until the atom will no longer be + replayed. With this approach to freeing the delete set, both kinds + of deletion can be handled in the same manner, since they both take + place after the atom commits. + + In other words, since we have to address deallocating the wandered + set after commit anyway, we might as well use the same mechanism for + deallocating the delete set. It means that additional data is + logged, but it reduces complexity in my opinion. + + Here's how it works. The atom stores a record of its delete set in + memory. When a block is deallocated or relocated, the bit is of + course not immediately deallocated in the working bitmaps. + + The delete set is included in the commit record, which is written to + the journal area. The delete set is just a set of block numbers, so + there are several possible representations. The implementation + could actually dynamically chose the representation to achieve the + best compression: (a) list of blocks, (b) bitmap, and (c) extent + compression. The second two options are likely to achieve + significant compression of the delete set unless fragmentation + becomes a problem. + + The atom maintains its in-memory copy of the delete set until the + commit record is flushed to the disk. At this point, those blocks + become available for new atoms to re-allocate. The atom releases + these blocks back into the working bitmaps through the process of + "reposession". The reposession process makes a younger atom + responsible for committing a deallocation from a previous atom. + + For each block in the committed atom's delete set, a younger atom is + selected (or created) to handle the deallocation of that block. The + working bitmap corresponding to the block being deleted is or was + already captured by the younger (reposessing) atom. The block is + simply marked as deallocated in the working bitmap block captured. + + The reposessing atom may immediately use this block or not, but in + either case the deallocation is committed once the reposessing atom + commits. For recovery purposes (not discussed here), each atom also + includes a list of atoms for which it resposesses. + +---- The commit record + +The commit record includes three lists: + + DELETE SET: The set of blocks deallocated by this atom, represented + as either a list, bitmap, or using extents. + + WANDER SET: A list of block-pairs giving the original location and + the temporary wandered location. During replay the temporary + location is copied to the original location. After replay is no + longer needed, the temporary locations are deallocated using + reposession as previously described. + + REPOSESSES FOR SET: A list of the previous atoms for which this atom + reposesses deallocated blocks. This is used to know which atoms + deallocations must be replayed during crash recovery. + +I propose that all of this information is included in the commit +record, which is written to the journal area. There may be multiple +journal areas (a significant complication) or there may not, but the +key point is that all of this data is written into a reserved, +cyclical journal area. Because the journal area is reserved and +written in a simple cyclical manner, there are no allocation decisions +needed to find space for these commit records. + +---- The example + +Consider a roughly 50G file being modified in a 100G file system. +Realize that due to maintaining the preserve set, it is not possible +to transactionally write a file larger than 50G on a 100G file system. +In the absolute worst case, no extent compression is possible and the +best representation of the delete set requires a bitmap covering the +entire file system. + +A 100G file system with 4K blocks has 3.27MB of bitmaps, and this is +the same as the worst-case representation of the delete set, assuming +just about every other block is deleted. In reality, we expect the +delete set to be much smaller because extent-compression would achieve +significant savings. + +The wander set could possibly be compressed, but that is a more +difficult task. Suppose we attempt to overwrite the entire 50GB file +instead of relocating it. A 50G file has 13 million blocks, therefore +the wander set requires storing 26 million block address pairs. With +8-byte block addresses that requires writing 210MB of wander set +data. Ouch! + +We should hope that the size of the wander set does not grow so large. +After all, its parent the extent record must be modified in this case, +so these blocks are all candidates for relocation. It would take a +dumb allocate/flush plugin to try to overwrite a 50G file instead of +relocating it. + +---- The conclusion + +I maintain that it is much simpler to write all of this data inside +reserved log areas. It is possible that we could write this data +outside the log, but then it will complicate the allocation and +deallocation proceedure, since space for the log itself must then be +allocated using ordinary methods. + +Comments? --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/carry.c 2004-09-03 00:57:04.975282904 -0700 @@ -0,0 +1,1437 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ +/* Functions to "carry" tree modification(s) upward. */ +/* Tree is modified one level at a time. As we modify a level we accumulate a + set of changes that need to be propagated to the next level. We manage + node locking such that any searches that collide with carrying are + restarted, from the root if necessary. + + Insertion of a new item may result in items being moved among nodes and + this requires the delimiting key to be updated at the least common parent + of the nodes modified to preserve search tree invariants. Also, insertion + may require allocation of a new node. A pointer to the new node has to be + inserted into some node on the parent level, etc. + + Tree carrying is meant to be analogous to arithmetic carrying. + + A carry operation is always associated with some node (&carry_node). + + Carry process starts with some initial set of operations to be performed + and an initial set of already locked nodes. Operations are performed one + by one. Performing each single operation has following possible effects: + + - content of carry node associated with operation is modified + - new carry nodes are locked and involved into carry process on this level + - new carry operations are posted to the next level + + After all carry operations on this level are done, process is repeated for + the accumulated sequence on carry operations for the next level. This + starts by trying to lock (in left to right order) all carry nodes + associated with carry operations on the parent level. After this, we decide + whether more nodes are required on the left of already locked set. If so, + all locks taken on the parent level are released, new carry nodes are + added, and locking process repeats. + + It may happen that balancing process fails owing to unrecoverable error on + some of upper levels of a tree (possible causes are io error, failure to + allocate new node, etc.). In this case we should unmount the filesystem, + rebooting if it is the root, and possibly advise the use of fsck. + + USAGE: + + + int some_tree_operation( znode *node, ... ) + { + // Allocate on a stack pool of carry objects: operations and nodes. + // Most carry processes will only take objects from here, without + // dynamic allocation. + +I feel uneasy about this pool. It adds to code complexity, I understand why it exists, but.... -Hans + + carry_pool pool; + carry_level lowest_level; + carry_op *op; + + init_carry_pool( &pool ); + init_carry_level( &lowest_level, &pool ); + + // operation may be one of: + // COP_INSERT --- insert new item into node + // COP_CUT --- remove part of or whole node + // COP_PASTE --- increase size of item + // COP_DELETE --- delete pointer from parent node + // COP_UPDATE --- update delimiting key in least + // common ancestor of two + + op = post_carry( &lowest_level, operation, node, 0 ); + if( IS_ERR( op ) || ( op == NULL ) ) { + handle error + } else { + // fill in remaining fields in @op, according to carry.h:carry_op + result = carry( &lowest_level, NULL ); + } + done_carry_pool( &pool ); + } + + When you are implementing node plugin method that participates in carry + (shifting, insertion, deletion, etc.), do the following: + + int foo_node_method( znode *node, ..., carry_level *todo ) + { + carry_op *op; + + .... + + // note, that last argument to post_carry() is non-null + // here, because @op is to be applied to the parent of @node, rather + // than to the @node itself as in the previous case. + + op = node_post_carry( todo, operation, node, 1 ); + // fill in remaining fields in @op, according to carry.h:carry_op + + .... + + } + + BATCHING: + + One of the main advantages of level-by-level balancing implemented here is + ability to batch updates on a parent level and to peform them more + efficiently as a result. + + Description To Be Done (TBD). + + DIFFICULTIES AND SUBTLE POINTS: + + 1. complex plumbing is required, because: + + a. effective allocation through pools is needed + + b. target of operation is not exactly known when operation is + posted. This is worked around through bitfields in &carry_node and + logic in lock_carry_node() + + c. of interaction with locking code: node should be added into sibling + list when pointer to it is inserted into its parent, which is some time + after node was created. Between these moments, node is somewhat in + suspended state and is only registered in the carry lists + + 2. whole balancing logic is implemented here, in particular, insertion + logic is coded in make_space(). + + 3. special cases like insertion (add_tree_root()) or deletion + (kill_tree_root()) of tree root and morphing of paste into insert + (insert_paste()) have to be handled. + + 4. there is non-trivial interdependency between allocation of new nodes + and almost everything else. This is mainly due to the (1.c) above. I shall + write about this later. + +*/ + +#include "forward.h" +#include "debug.h" +#include "key.h" +#include "coord.h" +#include "plugin/item/item.h" +#include "plugin/item/extent.h" +#include "plugin/node/node.h" +#include "jnode.h" +#include "znode.h" +#include "tree_mod.h" +#include "tree_walk.h" +#include "block_alloc.h" +#include "pool.h" +#include "tree.h" +#include "carry.h" +#include "carry_ops.h" +#include "super.h" +#include "reiser4.h" +#include "prof.h" + +#include + +/* level locking/unlocking */ +static int lock_carry_level(carry_level * level); +static void unlock_carry_level(carry_level * level, int failure); +static void done_carry_level(carry_level * level); +static void unlock_carry_node(carry_level * level, carry_node * node, int fail); + +int lock_carry_node(carry_level * level, carry_node * node); +int lock_carry_node_tail(carry_node * node); + +/* carry processing proper */ +static int carry_on_level(carry_level * doing, carry_level * todo); + +/* handlers for carry operations. */ + +static void fatal_carry_error(carry_level * doing, int ecode); +static int add_new_root(carry_level * level, carry_node * node, znode * fake); + +static int carry_estimate_reserve(carry_level * level); + +#if REISER4_DEBUG +typedef enum { + CARRY_TODO, + CARRY_DOING +} carry_queue_state; +static int carry_level_invariant(carry_level * level, carry_queue_state state); +#endif + +/* main entry point for tree balancing. + + Tree carry performs operations from @doing and while doing so accumulates + information about operations to be performed on the next level ("carried" + to the parent level). Carried operations are performed, causing possibly + more operations to be carried upward etc. carry() takes care about + locking and pinning znodes while operating on them. + + For usage, see comment at the top of fs/reiser4/carry.c + +*/ +reiser4_internal int +carry(carry_level * doing /* set of carry operations to be performed */ , + carry_level * done /* set of nodes, already performed at the + * previous level. NULL in most cases */ ) +{ + int result = 0; + carry_level done_area; + carry_level todo_area; + /* queue of new requests */ + carry_level *todo; + int wasreserved; + int reserve; + ON_DEBUG(STORE_COUNTERS;) + + assert("nikita-888", doing != NULL); + + trace_stamp(TRACE_CARRY); + + todo = &todo_area; + init_carry_level(todo, doing->pool); + if (done == NULL) { + /* queue of requests performed on the previous level */ + done = &done_area; + init_carry_level(done, doing->pool); + } + + wasreserved = perthread_pages_count(); + reserve = carry_estimate_reserve(doing); + result = perthread_pages_reserve(reserve, GFP_KERNEL); + if (result != 0) + return result; + + /* iterate until there is nothing more to do */ + while (result == 0 && carry_op_num(doing) > 0) { + carry_level *tmp; + + ON_STATS(todo->level_no = doing->level_no + 1); + + /* at this point @done is locked. */ + /* repeat lock/do/unlock while + + (1) lock_carry_level() fails due to deadlock avoidance, or + + (2) carry_on_level() decides that more nodes have to + be involved. + + (3) some unexpected error occurred while balancing on the + upper levels. In this case all changes are rolled back. + + */ + while (1) { + result = lock_carry_level(doing); + if (result == 0) { + /* perform operations from @doing and + accumulate new requests in @todo */ + result = carry_on_level(doing, todo); + if (result == 0) + break; + else if (result != -E_REPEAT || + !doing->restartable) { + warning("nikita-1043", + "Fatal error during carry: %i", + result); + print_level("done", done); + print_level("doing", doing); + print_level("todo", todo); + /* do some rough stuff like aborting + all pending transcrashes and thus + pushing tree back to the consistent + state. Alternatvely, just panic. + */ + fatal_carry_error(doing, result); + return result; + } + } else if (result != -E_REPEAT) { + fatal_carry_error(doing, result); + return result; + } + reiser4_stat_level_inc(doing, carry_restart); + unlock_carry_level(doing, 1); + } + /* at this point @done can be safely unlocked */ + done_carry_level(done); + reiser4_stat_level_inc(doing, carry_done); + /* cyclically shift queues */ + tmp = done; + done = doing; + doing = todo; + todo = tmp; + init_carry_level(todo, doing->pool); + + /* give other threads chance to run */ + preempt_point(); + } + done_carry_level(done); + + assert("nikita-3460", perthread_pages_count() - wasreserved >= 0); + perthread_pages_release(perthread_pages_count() - wasreserved); + + /* all counters, but x_refs should remain the same. x_refs can change + owing to transaction manager */ + ON_DEBUG(CHECK_COUNTERS;) + return result; +} + +/* perform carry operations on given level. + + Optimizations proposed by pooh: + + (1) don't lock all nodes from queue at the same time. Lock nodes lazily as + required; + + (2) unlock node if there are no more operations to be performed upon it and + node didn't add any operation to @todo. This can be implemented by + attaching to each node two counters: counter of operaions working on this + node and counter and operations carried upward from this node. + +*/ +static int +carry_on_level(carry_level * doing /* queue of carry operations to + * do on this level */ , + carry_level * todo /* queue where new carry + * operations to be performed on + * the * parent level are + * accumulated during @doing + * processing. */ ) +{ + int result; + int (*f) (carry_op *, carry_level *, carry_level *); + carry_op *op; + carry_op *tmp_op; + + assert("nikita-1034", doing != NULL); + assert("nikita-1035", todo != NULL); + + trace_stamp(TRACE_CARRY); + + /* node can be inconsistent while in-transit */ + DISABLE_NODE_CHECK; + + /* @doing->nodes are locked. */ + + /* This function can be split into two phases: analysis and modification. + + Analysis calculates precisely what items should be moved between + nodes. This information is gathered in some structures attached to + each carry_node in a @doing queue. Analysis also determines whether + new nodes are to be allocated etc. + + After analysis is completed, actual modification is performed. Here + we can take advantage of "batch modification": if there are several + operations acting on the same node, modifications can be performed + more efficiently when batched together. + + Above is an optimization left for the future. + */ + /* Important, but delayed optimization: it's possible to batch + operations together and perform them more efficiently as a + result. For example, deletion of several neighboring items from a + node can be converted to a single ->cut() operation. + + Before processing queue, it should be scanned and "mergeable" + operations merged. + */ + result = 0; + for_all_ops(doing, op, tmp_op) { + carry_opcode opcode; + + assert("nikita-1041", op != NULL); + opcode = op->op; + assert("nikita-1042", op->op < COP_LAST_OP); + f = op_dispatch_table[op->op].handler; + result = f(op, doing, todo); + /* locking can fail with -E_REPEAT. Any different error is fatal + and will be handled by fatal_carry_error() sledgehammer. + */ + if (result != 0) + break; + } + if (result == 0) { + carry_plugin_info info; + carry_node *scan; + carry_node *tmp_scan; + + info.doing = doing; + info.todo = todo; + + assert("nikita-3002", carry_level_invariant(doing, CARRY_DOING)); + for_all_nodes(doing, scan, tmp_scan) { + znode *node; + + node = carry_real(scan); + assert("nikita-2547", node != NULL); + if (node_is_empty(node)) { + result = node_plugin_by_node(node)->prepare_removal(node, &info); + if (result != 0) + break; + } + } + } + ENABLE_NODE_CHECK; + return result; +} + +/* post carry operation + + This is main function used by external carry clients: node layout plugins + and tree operations to create new carry operation to be performed on some + level. + + New operation will be included in the @level queue. To actually perform it, + call carry( level, ... ). This function takes write lock on @node. Carry + manages all its locks by itself, don't worry about this. + + This function adds operation and node at the end of the queue. It is up to + caller to guarantee proper ordering of node queue. + +*/ +reiser4_internal carry_op * +post_carry(carry_level * level /* queue where new operation is to + * be posted at */ , + carry_opcode op /* opcode of operation */ , + znode * node /* node on which this operation + * will operate */ , + int apply_to_parent_p /* whether operation will operate + * directly on @node or on it + * parent. */ ) +{ + carry_op *result; + carry_node *child; + + assert("nikita-1046", level != NULL); + assert("nikita-1788", znode_is_write_locked(node)); + + result = add_op(level, POOLO_LAST, NULL); + if (IS_ERR(result)) + return result; + child = add_carry(level, POOLO_LAST, NULL); + if (IS_ERR(child)) { + reiser4_pool_free(&level->pool->op_pool, &result->header); + return (carry_op *) child; + } + result->node = child; + result->op = op; + child->parent = apply_to_parent_p; + if (ZF_ISSET(node, JNODE_ORPHAN)) + child->left_before = 1; + child->node = node; + return result; +} + +/* number of carry operations in a @level */ +reiser4_internal int +carry_op_num(const carry_level * level) +{ + return level->ops_num; +} + +/* initialise carry queue */ +reiser4_internal void +init_carry_level(carry_level * level /* level to initialise */ , + carry_pool * pool /* pool @level will allocate objects + * from */ ) +{ + assert("nikita-1045", level != NULL); + assert("nikita-967", pool != NULL); + + xmemset(level, 0, sizeof *level); + level->pool = pool; + + pool_level_list_init(&level->nodes); + pool_level_list_init(&level->ops); +} + +/* initialise pools within queue */ +reiser4_internal void +init_carry_pool(carry_pool * pool /* pool to initialise */ ) +{ + assert("nikita-945", pool != NULL); + + reiser4_init_pool(&pool->op_pool, sizeof (carry_op), CARRIES_POOL_SIZE, (char *) pool->op); + reiser4_init_pool(&pool->node_pool, sizeof (carry_node), NODES_LOCKED_POOL_SIZE, (char *) pool->node); +} + +/* finish with queue pools */ +reiser4_internal void +done_carry_pool(carry_pool * pool UNUSED_ARG /* pool to destroy */ ) +{ + reiser4_done_pool(&pool->op_pool); + reiser4_done_pool(&pool->node_pool); +} + +/* add new carry node to the @level. + + Returns pointer to the new carry node allocated from pool. It's up to + callers to maintain proper order in the @level. Assumption is that if carry + nodes on one level are already sorted and modifications are peroformed from + left to right, carry nodes added on the parent level will be ordered + automatically. To control ordering use @order and @reference parameters. + +*/ +reiser4_internal carry_node * +add_carry_skip(carry_level * level /* &carry_level to add node + * to */ , + pool_ordering order /* where to insert: at the + * beginning of @level, + * before @reference, after + * @reference, at the end + * of @level */ , + carry_node * reference /* reference node for + * insertion */ ) +{ + ON_DEBUG(carry_node * orig_ref = reference); + + trace_stamp(TRACE_CARRY); + if (order == POOLO_BEFORE) { + reference = find_left_carry(reference, level); + if (reference == NULL) + reference = carry_node_front(level); + else + reference = carry_node_next(reference); + } else if (order == POOLO_AFTER) { + reference = find_right_carry(reference, level); + if (reference == NULL) + reference = carry_node_back(level); + else + reference = carry_node_prev(reference); + } + assert("nikita-2209", + ergo(orig_ref != NULL, + carry_real(reference) == carry_real(orig_ref))); + return add_carry(level, order, reference); +} + +reiser4_internal carry_node * +add_carry(carry_level * level /* &carry_level to add node + * to */ , + pool_ordering order /* where to insert: at the + * beginning of @level, before + * @reference, after @reference, + * at the end of @level */ , + carry_node * reference /* reference node for + * insertion */ ) +{ + carry_node *result; + + result = (carry_node *) add_obj(&level->pool->node_pool, &level->nodes, order, &reference->header); + if (!IS_ERR(result) && (result != NULL)) + ++level->nodes_num; + return result; +} + +/* add new carry operation to the @level. + + Returns pointer to the new carry operations allocated from pool. It's up to + callers to maintain proper order in the @level. To control ordering use + @order and @reference parameters. + +*/ +reiser4_internal carry_op * +add_op(carry_level * level /* &carry_level to add node to */ , + pool_ordering order /* where to insert: at the beginning of + * @level, before @reference, after + * @reference, at the end of @level */ , + carry_op * reference /* reference node for insertion */ ) +{ + carry_op *result; + + trace_stamp(TRACE_CARRY); + result = (carry_op *) add_obj(&level->pool->op_pool, &level->ops, order, &reference->header); + if (!IS_ERR(result) && (result != NULL)) + ++level->ops_num; + return result; +} + +/* Return node on the right of which @node was created. + + Each node is created on the right of some existing node (or it is new root, + which is special case not handled here). + + @node is new node created on some level, but not yet inserted into its + parent, it has corresponding bit (JNODE_ORPHAN) set in zstate. + +*/ +reiser4_internal carry_node * +find_begetting_brother(carry_node * node /* node to start search + * from */ , + carry_level * kin UNUSED_ARG /* level to + * scan */ ) +{ + carry_node *scan; + + assert("nikita-1614", node != NULL); + assert("nikita-1615", kin != NULL); + assert("nikita-1616", LOCK_CNT_GTZ(rw_locked_tree)); + assert("nikita-1619", ergo(carry_real(node) != NULL, + ZF_ISSET(carry_real(node), JNODE_ORPHAN))); + + for (scan = node;; scan = carry_node_prev(scan)) { + assert("nikita-1617", !carry_node_end(kin, scan)); + if ((scan->node != node->node) && !ZF_ISSET(scan->node, JNODE_ORPHAN)) { + assert("nikita-1618", carry_real(scan) != NULL); + break; + } + } + return scan; +} + +static cmp_t +carry_node_cmp(carry_level * level, carry_node * n1, carry_node * n2) +{ + assert("nikita-2199", n1 != NULL); + assert("nikita-2200", n2 != NULL); + + if (n1 == n2) + return EQUAL_TO; + while (1) { + n1 = carry_node_next(n1); + if (carry_node_end(level, n1)) + return GREATER_THAN; + if (n1 == n2) + return LESS_THAN; + } + impossible("nikita-2201", "End of level reached"); +} + +reiser4_internal carry_node * +find_carry_node(carry_level * level, const znode * node) +{ + carry_node *scan; + carry_node *tmp_scan; + + assert("nikita-2202", level != NULL); + assert("nikita-2203", node != NULL); + + for_all_nodes(level, scan, tmp_scan) { + if (carry_real(scan) == node) + return scan; + } + return NULL; +} + +reiser4_internal znode * +carry_real(const carry_node * node) +{ + assert("nikita-3061", node != NULL); + + return node->lock_handle.node; +} + +reiser4_internal carry_node * +insert_carry_node(carry_level * doing, carry_level * todo, const znode * node) +{ + carry_node *base; + carry_node *scan; + carry_node *tmp_scan; + carry_node *proj; + + base = find_carry_node(doing, node); + assert("nikita-2204", base != NULL); + + for_all_nodes(todo, scan, tmp_scan) { + proj = find_carry_node(doing, scan->node); + assert("nikita-2205", proj != NULL); + if (carry_node_cmp(doing, proj, base) != LESS_THAN) + break; + } + return scan; +} + +reiser4_internal carry_node * +add_carry_atplace(carry_level *doing, carry_level *todo, znode *node) +{ + carry_node *reference; + + assert("nikita-2994", doing != NULL); + assert("nikita-2995", todo != NULL); + assert("nikita-2996", node != NULL); + + reference = insert_carry_node(doing, todo, node); + assert("nikita-2997", reference != NULL); + + return add_carry(todo, POOLO_BEFORE, reference); +} + +/* like post_carry(), but designed to be called from node plugin methods. + This function is different from post_carry() in that it finds proper place + to insert node in the queue. */ +reiser4_internal carry_op * +node_post_carry(carry_plugin_info * info /* carry parameters + * passed down to node + * plugin */ , + carry_opcode op /* opcode of operation */ , + znode * node /* node on which this + * operation will operate */ , + int apply_to_parent_p /* whether operation will + * operate directly on @node + * or on it parent. */ ) +{ + carry_op *result; + carry_node *child; + + assert("nikita-2207", info != NULL); + assert("nikita-2208", info->todo != NULL); + + if (info->doing == NULL) + return post_carry(info->todo, op, node, apply_to_parent_p); + + result = add_op(info->todo, POOLO_LAST, NULL); + if (IS_ERR(result)) + return result; + child = add_carry_atplace(info->doing, info->todo, node); + if (IS_ERR(child)) { + reiser4_pool_free(&info->todo->pool->op_pool, &result->header); + return (carry_op *) child; + } + result->node = child; + result->op = op; + child->parent = apply_to_parent_p; + if (ZF_ISSET(node, JNODE_ORPHAN)) + child->left_before = 1; + child->node = node; + return result; +} + +/* lock all carry nodes in @level */ +static int +lock_carry_level(carry_level * level /* level to lock */ ) +{ + int result; + carry_node *node; + carry_node *tmp_node; + + assert("nikita-881", level != NULL); + assert("nikita-2229", carry_level_invariant(level, CARRY_TODO)); + + trace_stamp(TRACE_CARRY); + + /* lock nodes from left to right */ + result = 0; + for_all_nodes(level, node, tmp_node) { + result = lock_carry_node(level, node); + if (result != 0) + break; + } + return result; +} + +/* Synchronize delimiting keys between @node and its left neighbor. + + To reduce contention on dk key and simplify carry code, we synchronize + delimiting keys only when carry ultimately leaves tree level (carrying + changes upward) and unlocks nodes at this level. + + This function first finds left neighbor of @node and then updates left + neighbor's right delimiting key to conincide with least key in @node. + +*/ +static void +sync_dkeys(znode *spot /* node to update */) +{ + reiser4_key pivot; + reiser4_tree *tree; + + assert("nikita-1610", spot != NULL); + assert("nikita-1612", LOCK_CNT_NIL(rw_locked_dk)); + + tree = znode_get_tree(spot); + WLOCK_DK(tree); + + assert("nikita-2192", znode_is_loaded(spot)); + + /* sync left delimiting key of @spot with key in its leftmost item */ + if (node_is_empty(spot)) + pivot = *znode_get_rd_key(spot); + else + leftmost_key_in_node(spot, &pivot); + + znode_set_ld_key(spot, &pivot); + + RLOCK_TREE(tree); + /* there can be sequence of empty nodes pending removal on the left of + @spot. Scan them and update their left and right delimiting keys to + match left delimiting key of @spot. Also, update right delimiting + key of first non-empty left neighbor. + */ + while (1) { + if (!ZF_ISSET(spot, JNODE_LEFT_CONNECTED)) + break; + + spot = spot->left; + if (spot == NULL) + break; + +#if 0 + /* on the leaf level we can only increase right delimiting key + * of a node on which we don't hold a long term lock. */ + assert("nikita-2930", + ergo(!znode_is_write_locked(spot) && + znode_get_level(spot) == LEAF_LEVEL, + keyge(&pivot, znode_get_rd_key(spot)))); +#endif + + znode_set_rd_key(spot, &pivot); + /* don't sink into the domain of another balancing */ + if (!znode_is_write_locked(spot)) + break; + if (ZF_ISSET(spot, JNODE_HEARD_BANSHEE)) + znode_set_ld_key(spot, &pivot); + else + break; + } + + RUNLOCK_TREE(tree); + WUNLOCK_DK(tree); +} + +void +check_dkeys(const znode *node); + +/* unlock all carry nodes in @level */ +static void +unlock_carry_level(carry_level * level /* level to unlock */ , + int failure /* true if unlocking owing to + * failure */ ) +{ + carry_node *node; + carry_node *tmp_node; + + assert("nikita-889", level != NULL); + + trace_stamp(TRACE_CARRY); + + if (!failure) { + znode *spot; + + spot = NULL; + /* update delimiting keys */ + for_all_nodes(level, node, tmp_node) { + if (carry_real(node) != spot) { + spot = carry_real(node); + sync_dkeys(spot); + } + } + } + + /* nodes can be unlocked in arbitrary order. In preemptible + environment it's better to unlock in reverse order of locking, + though. + */ + for_all_nodes_back(level, node, tmp_node) { + /* all allocated nodes should be already linked to their + parents at this moment. */ + assert("nikita-1631", ergo(!failure, !ZF_ISSET(carry_real(node), + JNODE_ORPHAN))); + if (!failure) + node_check(carry_real(node), REISER4_NODE_DKEYS); + ON_DEBUG(check_dkeys(carry_real(node))); + unlock_carry_node(level, node, failure); + } + level->new_root = NULL; +} + +/* finish with @level + + Unlock nodes and release all allocated resources */ +static void +done_carry_level(carry_level * level /* level to finish */ ) +{ + carry_node *node; + carry_node *tmp_node; + carry_op *op; + carry_op *tmp_op; + + assert("nikita-1076", level != NULL); + + trace_stamp(TRACE_CARRY); + + unlock_carry_level(level, 0); + for_all_nodes(level, node, tmp_node) { + assert("nikita-2113", locks_list_is_clean(&node->lock_handle)); + assert("nikita-2114", owners_list_is_clean(&node->lock_handle)); + reiser4_pool_free(&level->pool->node_pool, &node->header); + } + for_all_ops(level, op, tmp_op) + reiser4_pool_free(&level->pool->op_pool, &op->header); +} + +/* helper function to complete locking of carry node + + Finish locking of carry node. There are several ways in which new carry + node can be added into carry level and locked. Normal is through + lock_carry_node(), but also from find_{left|right}_neighbor(). This + function factors out common final part of all locking scenarios. It + supposes that @node -> lock_handle is lock handle for lock just taken and + fills ->real_node from this lock handle. + +*/ +reiser4_internal int +lock_carry_node_tail(carry_node * node /* node to complete locking of */ ) +{ + assert("nikita-1052", node != NULL); + assert("nikita-1187", carry_real(node) != NULL); + assert("nikita-1188", !node->unlock); + + node->unlock = 1; + /* Load node content into memory and install node plugin by + looking at the node header. + + Most of the time this call is cheap because the node is + already in memory. + + Corresponding zrelse() is in unlock_carry_node() + */ + return zload(carry_real(node)); +} + +/* lock carry node + + "Resolve" node to real znode, lock it and mark as locked. + This requires recursive locking of znodes. + + When operation is posted to the parent level, node it will be applied to is + not yet known. For example, when shifting data between two nodes, + delimiting has to be updated in parent or parents of nodes involved. But + their parents is not yet locked and, moreover said nodes can be reparented + by concurrent balancing. + + To work around this, carry operation is applied to special "carry node" + rather than to the znode itself. Carry node consists of some "base" or + "reference" znode and flags indicating how to get to the target of carry + operation (->real_node field of carry_node) from base. + +*/ +reiser4_internal int +lock_carry_node(carry_level * level /* level @node is in */ , + carry_node * node /* node to lock */ ) +{ + int result; + znode *reference_point; + lock_handle lh; + lock_handle tmp_lh; + + assert("nikita-887", level != NULL); + assert("nikita-882", node != NULL); + + trace_stamp(TRACE_CARRY); + + result = 0; + reference_point = node->node; + init_lh(&lh); + init_lh(&tmp_lh); + if (node->left_before) { + /* handling of new nodes, allocated on the previous level: + + some carry ops were propably posted from the new node, but + this node neither has parent pointer set, nor is + connected. This will be done in ->create_hook() for + internal item. + + No then less, parent of new node has to be locked. To do + this, first go to the "left" in the carry order. This + depends on the decision to always allocate new node on the + right of existing one. + + Loop handles case when multiple nodes, all orphans, were + inserted. + + Strictly speaking, taking tree lock is not necessary here, + because all nodes scanned by loop in + find_begetting_brother() are write-locked by this thread, + and thus, their sibling linkage cannot change. + + */ + reference_point = UNDER_RW + (tree, znode_get_tree(reference_point), read, + find_begetting_brother(node, level)->node); + assert("nikita-1186", reference_point != NULL); + } + if (node->parent && (result == 0)) { + result = reiser4_get_parent(&tmp_lh, reference_point, ZNODE_WRITE_LOCK, 0); + if (result != 0) { + ; /* nothing */ + } else if (znode_get_level(tmp_lh.node) == 0) { + assert("nikita-1347", znode_above_root(tmp_lh.node)); + result = add_new_root(level, node, tmp_lh.node); + if (result == 0) { + reference_point = level->new_root; + move_lh(&lh, &node->lock_handle); + } + } else if ((level->new_root != NULL) && (level->new_root != znode_parent_nolock(reference_point))) { + /* parent of node exists, but this level aready + created different new root, so */ + warning("nikita-1109", + /* it should be "radicis", but tradition is + tradition. do banshees read latin? */ + "hodie natus est radici frater"); + result = -EIO; + } else { + move_lh(&lh, &tmp_lh); + reference_point = lh.node; + } + } + if (node->left && (result == 0)) { + assert("nikita-1183", node->parent); + assert("nikita-883", reference_point != NULL); + result = reiser4_get_left_neighbor( + &tmp_lh, reference_point, ZNODE_WRITE_LOCK, GN_CAN_USE_UPPER_LEVELS); + if (result == 0) { + done_lh(&lh); + move_lh(&lh, &tmp_lh); + reference_point = lh.node; + } + } + if (!node->parent && !node->left && !node->left_before) { + result = longterm_lock_znode(&lh, reference_point, ZNODE_WRITE_LOCK, ZNODE_LOCK_HIPRI); + } + if (result == 0) { + move_lh(&node->lock_handle, &lh); + result = lock_carry_node_tail(node); + } + done_lh(&tmp_lh); + done_lh(&lh); + return result; +} + +/* release a lock on &carry_node. + + Release if necessary lock on @node. This opearion is pair of + lock_carry_node() and is idempotent: you can call it more than once on the + same node. + +*/ +static void +unlock_carry_node(carry_level * level, + carry_node * node /* node to be released */ , + int failure /* 0 if node is unlocked due + * to some error */ ) +{ + znode *real_node; + + assert("nikita-884", node != NULL); + + trace_stamp(TRACE_CARRY); + + real_node = carry_real(node); + /* pair to zload() in lock_carry_node_tail() */ + zrelse(real_node); + if (node->unlock && (real_node != NULL)) { + assert("nikita-899", real_node == node->lock_handle.node); + longterm_unlock_znode(&node->lock_handle); + } + if (failure) { + if (node->deallocate && (real_node != NULL)) { + /* free node in bitmap + + Prepare node for removal. Last zput() will finish + with it. + */ + ZF_SET(real_node, JNODE_HEARD_BANSHEE); + } + if (node->free) { + assert("nikita-2177", locks_list_is_clean(&node->lock_handle)); + assert("nikita-2112", owners_list_is_clean(&node->lock_handle)); + reiser4_pool_free(&level->pool->node_pool, &node->header); + } + } +} + +/* fatal_carry_error() - all-catching error handling function + + It is possible that carry faces unrecoverable error, like unability to + insert pointer at the internal level. Our simple solution is just panic in + this situation. More sophisticated things like attempt to remount + file-system as read-only can be implemented without much difficlties. + + It is believed, that: + + 1. in stead of panicking, all current transactions can be aborted rolling + system back to the consistent state. + +Umm, if you simply panic without doing anything more at all, then all current +transactions are aborted and the system is rolled back to a consistent state, +by virtue of the design of the transactional mechanism. Well, wait, let's be +precise. If an internal node is corrupted on disk due to hardware failure, +then there may be no consistent state that can be rolled back to, so instead +we should say that it will rollback the transactions, which barring other +factors means rolling back to a consistent state. + +# Nikita: there is a subtle difference between panic and aborting +# transactions: machine doesn't reboot. Processes aren't killed. Processes +# don't using reiser4 (not that we care about such processes), or using other +# reiser4 mounts (about them we do care) will simply continue to run. With +# some luck, even application using aborted file system can survive: it will +# get some error, like EBADF, from each file descriptor on failed file system, +# but applications that do care about tolerance will cope with this (squid +# will). + +It would be a nice feature though to support rollback without rebooting +followed by remount, but this can wait for later versions. + + + 2. once isolated transactions will be implemented it will be possible to + roll back offending transaction. + +2. is additional code complexity of inconsistent value (it implies that a broken tree should be kept in operation), so we must think about +it more before deciding if it should be done. -Hans + +*/ +static void +fatal_carry_error(carry_level * doing UNUSED_ARG /* carry level + * where + * unrecoverable + * error + * occurred */ , + int ecode /* error code */ ) +{ + assert("nikita-1230", doing != NULL); + assert("nikita-1231", ecode < 0); + + reiser4_panic("nikita-1232", "Carry failed: %i", ecode); +} + +/* add new root to the tree + + This function itself only manages changes in carry structures and delegates + all hard work (allocation of znode for new root, changes of parent and + sibling pointers to the add_tree_root(). + + Locking: old tree root is locked by carry at this point. Fake znode is also + locked. + +*/ +static int +add_new_root(carry_level * level /* carry level in context of which + * operation is performed */ , + carry_node * node /* carry node for existing root */ , + znode * fake /* "fake" znode already locked by + * us */ ) +{ + int result; + + assert("nikita-1104", level != NULL); + assert("nikita-1105", node != NULL); + + assert("nikita-1403", znode_is_write_locked(node->node)); + assert("nikita-1404", znode_is_write_locked(fake)); + + /* trying to create new root. */ + /* @node is root and it's already locked by us. This + means that nobody else can be trying to add/remove + tree root right now. + */ + if (level->new_root == NULL) + level->new_root = add_tree_root(node->node, fake); + if (!IS_ERR(level->new_root)) { + assert("nikita-1210", znode_is_root(level->new_root)); + node->deallocate = 1; + result = longterm_lock_znode(&node->lock_handle, level->new_root, ZNODE_WRITE_LOCK, ZNODE_LOCK_LOPRI); + if (result == 0) + zput(level->new_root); + } else { + result = PTR_ERR(level->new_root); + level->new_root = NULL; + } + return result; +} + +/* allocate new znode and add the operation that inserts the + pointer to it into the parent node into the todo level + + Allocate new znode, add it into carry queue and post into @todo queue + request to add pointer to new node into its parent. + + This is carry related routing that calls new_node() to allocate new + node. +*/ +reiser4_internal carry_node * +add_new_znode(znode * brother /* existing left neighbor of new + * node */ , + carry_node * ref /* carry node after which new + * carry node is to be inserted + * into queue. This affects + * locking. */ , + carry_level * doing /* carry queue where new node is + * to be added */ , + carry_level * todo /* carry queue where COP_INSERT + * operation to add pointer to + * new node will ne added */ ) +{ + carry_node *fresh; + znode *new_znode; + carry_op *add_pointer; + carry_plugin_info info; + + assert("nikita-1048", brother != NULL); + assert("nikita-1049", todo != NULL); + + /* There is a lot of possible variations here: to what parent + new node will be attached and where. For simplicity, always + do the following: + + (1) new node and @brother will have the same parent. + + (2) new node is added on the right of @brother + + */ + + fresh = add_carry_skip(doing, ref ? POOLO_AFTER : POOLO_LAST, ref); + if (IS_ERR(fresh)) + return fresh; + + fresh->deallocate = 1; + fresh->free = 1; + + new_znode = new_node(brother, znode_get_level(brother)); + if (IS_ERR(new_znode)) + /* @fresh will be deallocated automatically by error + handling code in the caller. */ + return (carry_node *) new_znode; + + /* new_znode returned znode with x_count 1. Caller has to decrease + it. make_space() does. */ + + ZF_SET(new_znode, JNODE_ORPHAN); + fresh->node = new_znode; + + while (ZF_ISSET(carry_real(ref), JNODE_ORPHAN)) { + ref = carry_node_prev(ref); + assert("nikita-1606", !carry_node_end(doing, ref)); + } + + info.todo = todo; + info.doing = doing; + add_pointer = node_post_carry(&info, COP_INSERT, carry_real(ref), 1); + if (IS_ERR(add_pointer)) { + /* no need to deallocate @new_znode here: it will be + deallocated during carry error handling. */ + return (carry_node *) add_pointer; + } + + add_pointer->u.insert.type = COPT_CHILD; + add_pointer->u.insert.child = fresh; + add_pointer->u.insert.brother = brother; + /* initially new node spawns empty key range */ + WLOCK_DK(znode_get_tree(brother)); + znode_set_ld_key(new_znode, + znode_set_rd_key(new_znode, znode_get_rd_key(brother))); + WUNLOCK_DK(znode_get_tree(brother)); + return fresh; +} + +/* + * Estimate how many pages of memory have to be reserved to complete execution + * of @level. + */ +static int carry_estimate_reserve(carry_level * level) +{ + carry_op *op; + carry_op *tmp_op; + int result; + + result = 0; + for_all_ops(level, op, tmp_op) + result += op_dispatch_table[op->op].estimate(op, level); + return result; +} + +/* DEBUGGING FUNCTIONS. + + Probably we also should leave them on even when + debugging is turned off to print dumps at errors. +*/ +#if REISER4_DEBUG +static int +carry_level_invariant(carry_level * level, carry_queue_state state) +{ + carry_node *node; + carry_node *tmp_node; + + if (level == NULL) + return 0; + + if (level->track_type != 0 && + level->track_type != CARRY_TRACK_NODE && + level->track_type != CARRY_TRACK_CHANGE) + return 0; + + /* check that nodes are in ascending order */ + for_all_nodes(level, node, tmp_node) { + znode *left; + znode *right; + + reiser4_key lkey; + reiser4_key rkey; + + if (node != carry_node_front(level)) { + if (state == CARRY_TODO) { + right = node->node; + left = carry_node_prev(node)->node; + } else { + right = carry_real(node); + left = carry_real(carry_node_prev(node)); + } + if (right == NULL || left == NULL) + continue; + if (node_is_empty(right) || node_is_empty(left)) + continue; + if (!keyle(leftmost_key_in_node(left, &lkey), + leftmost_key_in_node(right, &rkey))) { + print_znode("left", left); + print_node_content("left", left, ~0); + print_znode("right", right); + print_node_content("right", right, ~0); + return 0; + } + } + } + return 1; +} +#endif + +#if REISER4_DEBUG_OUTPUT +/* get symbolic name for boolean */ +static const char * +tf(int boolean /* truth value */ ) +{ + return boolean ? "t" : "f"; +} + +/* symbolic name for carry operation */ +static const char * +carry_op_name(carry_opcode op /* carry opcode */ ) +{ + switch (op) { + case COP_INSERT: + return "COP_INSERT"; + case COP_DELETE: + return "COP_DELETE"; + case COP_CUT: + return "COP_CUT"; + case COP_PASTE: + return "COP_PASTE"; + case COP_UPDATE: + return "COP_UPDATE"; + case COP_EXTENT: + return "COP_EXTENT"; + case COP_INSERT_FLOW: + return "COP_INSERT_FLOW"; + default:{ + /* not mt safe, but who cares? */ + static char buf[20]; + + sprintf(buf, "unknown op: %x", op); + return buf; + } + } +} + +/* dump information about carry node */ +reiser4_internal void +print_carry(const char *prefix /* prefix to print */ , + carry_node * node /* node to print */ ) +{ + if (node == NULL) { + printk("%s: null\n", prefix); + return; + } + printk("%s: %p parent: %s, left: %s, unlock: %s, free: %s, dealloc: %s\n", + prefix, node, tf(node->parent), tf(node->left), tf(node->unlock), tf(node->free), tf(node->deallocate)); + print_znode("\tnode", node->node); + print_znode("\treal_node", carry_real(node)); +} + +/* dump information about carry operation */ +reiser4_internal void +print_op(const char *prefix /* prefix to print */ , + carry_op * op /* operation to print */ ) +{ + if (op == NULL) { + printk("%s: null\n", prefix); + return; + } + printk("%s: %p carry_opcode: %s\n", prefix, op, carry_op_name(op->op)); + print_carry("\tnode", op->node); + switch (op->op) { + case COP_INSERT: + case COP_PASTE: + print_coord("\tcoord", op->u.insert.d ? op->u.insert.d->coord : NULL, 0); + print_key("\tkey", op->u.insert.d ? op->u.insert.d->key : NULL); + print_carry("\tchild", op->u.insert.child); + break; + case COP_DELETE: + print_carry("\tchild", op->u.delete.child); + break; + case COP_CUT: + if (op->u.cut_or_kill.is_cut) { + print_coord("\tfrom", op->u.cut_or_kill.u.kill->params.from, 0); + print_coord("\tto", op->u.cut_or_kill.u.kill->params.to, 0); + } else { + print_coord("\tfrom", op->u.cut_or_kill.u.cut->params.from, 0); + print_coord("\tto", op->u.cut_or_kill.u.cut->params.to, 0); + } + break; + case COP_UPDATE: + print_carry("\tleft", op->u.update.left); + break; + default: + /* do nothing */ + break; + } +} + +/* dump information about all nodes and operations in a @level */ +reiser4_internal void +print_level(const char *prefix /* prefix to print */ , + carry_level * level /* level to print */ ) +{ + carry_node *node; + carry_node *tmp_node; + carry_op *op; + carry_op *tmp_op; + + if (level == NULL) { + printk("%s: null\n", prefix); + return; + } + printk("%s: %p, restartable: %s\n", + prefix, level, tf(level->restartable)); + + for_all_nodes(level, node, tmp_node) + print_carry("\tcarry node", node); + for_all_ops(level, op, tmp_op) + print_op("\tcarry op", op); +} +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/carry.h 2004-09-03 00:57:04.978282448 -0700 @@ -0,0 +1,439 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Functions and data types to "carry" tree modification(s) upward. + See fs/reiser4/carry.c for details. */ + +#if !defined( __FS_REISER4_CARRY_H__ ) +#define __FS_REISER4_CARRY_H__ + +#include "forward.h" +#include "debug.h" +#include "pool.h" +#include "znode.h" + +#include + +/* &carry_node - "location" of carry node. + + "location" of node that is involved or going to be involved into + carry process. Node where operation will be carried to on the + parent level cannot be recorded explicitly. Operation will be carried + usually to the parent of some node (where changes are performed at + the current level) or, to the left neighbor of its parent. But while + modifications are performed at the current level, parent may + change. So, we have to allow some indirection (or, positevly, + flexibility) in locating carry nodes. + +*/ +typedef struct carry_node { + /* pool linkage */ + reiser4_pool_header header; + + /* base node from which real_node is calculated. See + fs/reiser4/carry.c:lock_carry_node(). */ + znode *node; + + /* how to get ->real_node */ + /* to get ->real_node obtain parent of ->node*/ + __u32 parent:1; + /* to get ->real_node obtain left neighbor of parent of + ->node*/ + __u32 left:1; + __u32 left_before:1; + + /* locking */ + + /* this node was locked by carry process and should be + unlocked when carry leaves a level */ + __u32 unlock:1; + + /* disk block for this node was allocated by carry process and + should be deallocated when carry leaves a level */ + __u32 deallocate:1; + /* this carry node was allocated by carry process and should be + freed when carry leaves a level */ + __u32 free:1; + + /* type of lock we want to take on this node */ + lock_handle lock_handle; +} carry_node; + +/* &carry_opcode - elementary operations that can be carried upward + + Operations that carry() can handle. This list is supposed to be + expanded. + + Each carry operation (cop) is handled by appropriate function defined + in fs/reiser4/carry.c. For example COP_INSERT is handled by + fs/reiser4/carry.c:carry_insert() etc. These functions in turn + call plugins of nodes affected by operation to modify nodes' content + and to gather operations to be performed on the next level. + +*/ +typedef enum { + /* insert new item into node. */ + COP_INSERT, + /* delete pointer from parent node */ + COP_DELETE, + /* remove part of or whole node. */ + COP_CUT, + /* increase size of item. */ + COP_PASTE, + /* insert extent (that is sequence of unformatted nodes). */ + COP_EXTENT, + /* update delimiting key in least common ancestor of two + nodes. This is performed when items are moved between two + nodes. + */ + COP_UPDATE, + /* insert flow */ + COP_INSERT_FLOW, + COP_LAST_OP, +} carry_opcode; + +#define CARRY_FLOW_NEW_NODES_LIMIT 20 + +/* mode (or subtype) of COP_{INSERT|PASTE} operation. Specifies how target + item is determined. */ +typedef enum { + /* target item is one containing pointer to the ->child node */ + COPT_CHILD, + /* target item is given explicitly by @coord */ + COPT_ITEM_DATA, + /* target item is given by key */ + COPT_KEY, + /* see insert_paste_common() for more comments on this. */ + COPT_PASTE_RESTARTED, +} cop_insert_pos_type; + +/* flags to cut and delete */ +typedef enum { + /* don't kill node even if it became completely empty as results of + * cut. This is needed for eottl handling. See carry_extent() for + * details. */ + DELETE_RETAIN_EMPTY = (1 << 0) +} cop_delete_flag; + +/* + * carry() implements "lock handle tracking" feature. + * + * Callers supply carry with node where to perform initial operation and lock + * handle on this node. Trying to optimize node utilization carry may actually + * move insertion point to different node. Callers expect that lock handle + * will rebe transferred to the new node also. + * + */ +typedef enum { + /* transfer lock handle along with insertion point */ + CARRY_TRACK_CHANGE = 1, + /* acquire new lock handle to the node where insertion point is. This + * is used when carry() client doesn't initially possess lock handle + * on the insertion point node, for example, by extent insertion + * code. See carry_extent(). */ + CARRY_TRACK_NODE = 2 +} carry_track_type; + +/* data supplied to COP_{INSERT|PASTE} by callers */ +typedef struct carry_insert_data { + /* position where new item is to be inserted */ + coord_t *coord; + /* new item description */ + reiser4_item_data *data; + /* key of new item */ + const reiser4_key *key; +} carry_insert_data; + +/* cut and kill are similar, so carry_cut_data and carry_kill_data share the below structure of parameters */ +struct cut_kill_params { + /* coord where cut starts (inclusive) */ + coord_t *from; + /* coord where cut stops (inclusive, this item/unit will also be + * cut) */ + coord_t *to; + /* starting key. This is necessary when item and unit pos don't + * uniquely identify what portion or tree to remove. For example, this + * indicates what portion of extent unit will be affected. */ + const reiser4_key *from_key; + /* exclusive stop key */ + const reiser4_key *to_key; + /* if this is not NULL, smallest actually removed key is stored + * here. */ + reiser4_key *smallest_removed; +}; + +struct carry_cut_data { + struct cut_kill_params params; +}; + +struct carry_kill_data { + struct cut_kill_params params; + /* parameter to be passed to the ->kill_hook() method of item + * plugin */ + /*void *iplug_params;*/ /* FIXME: unused currently */ + /* if not NULL---inode whose items are being removed. This is needed + * for ->kill_hook() of extent item to update VM structures when + * removing pages. */ + struct inode *inode; + /* sibling list maintenance is complicated by existence of eottl. When + * eottl whose left and right neighbors are formatted leaves is + * removed, one has to connect said leaves in the sibling list. This + * cannot be done when extent removal is just started as locking rules + * require sibling list update to happen atomically with removal of + * extent item. Therefore: 1. pointers to left and right neighbors + * have to be passed down to the ->kill_hook() of extent item, and + * 2. said neighbors have to be locked. */ + lock_handle *left; + lock_handle *right; + /* flags modifying behavior of kill. Currently, it may have DELETE_RETAIN_EMPTY set. */ + unsigned flags; +}; + +/* &carry_tree_op - operation to "carry" upward. + + Description of an operation we want to "carry" to the upper level of + a tree: e.g, when we insert something and there is not enough space + we allocate a new node and "carry" the operation of inserting a + pointer to the new node to the upper level, on removal of empty node, + we carry up operation of removing appropriate entry from parent. + + There are two types of carry ops: when adding or deleting node we + node at the parent level where appropriate modification has to be + performed is known in advance. When shifting items between nodes + (split, merge), delimiting key should be changed in the least common + parent of the nodes involved that is not known in advance. + + For the operations of the first type we store in &carry_op pointer to + the &carry_node at the parent level. For the operation of the second + type we store &carry_node or parents of the left and right nodes + modified and keep track of them upward until they coincide. + +*/ +typedef struct carry_op { + /* pool linkage */ + reiser4_pool_header header; + carry_opcode op; + /* node on which operation is to be performed: + + for insert, paste: node where new item is to be inserted + + for delete: node where pointer is to be deleted + + for cut: node to cut from + + for update: node where delimiting key is to be modified + + for modify: parent of modified node + + */ + carry_node *node; + union { + struct { + /* (sub-)type of insertion/paste. Taken from + cop_insert_pos_type. */ + __u8 type; + /* various operation flags. Taken from + cop_insert_flag. */ + __u8 flags; + carry_insert_data *d; + carry_node *child; + znode *brother; + } insert, paste, extent; + + struct { + int is_cut; + union { + carry_kill_data *kill; + carry_cut_data *cut; + } u; + } cut_or_kill; + + struct { + carry_node *left; + } update; + struct { + /* changed child */ + carry_node *child; + /* bitmask of changes. See &cop_modify_flag */ + __u32 flag; + } modify; + struct { + /* flags to deletion operation. Are taken from + cop_delete_flag */ + __u32 flags; + /* child to delete from parent. If this is + NULL, delete op->node. */ + carry_node *child; + } delete; + struct { + /* various operation flags. Taken from + cop_insert_flag. */ + __u32 flags; + flow_t *flow; + coord_t *insert_point; + reiser4_item_data *data; + /* flow insertion is limited by number of new blocks + added in that operation which do not get any data + but part of flow. This limit is set by macro + CARRY_FLOW_NEW_NODES_LIMIT. This field stores number + of nodes added already during one carry_flow */ + int new_nodes; + } insert_flow; + } u; +} carry_op; + +/* &carry_op_pool - preallocated pool of carry operations, and nodes */ +typedef struct carry_pool { + carry_op op[CARRIES_POOL_SIZE]; + reiser4_pool op_pool; + carry_node node[NODES_LOCKED_POOL_SIZE]; + reiser4_pool node_pool; +} carry_pool; + +/* &carry_tree_level - carry process on given level + + Description of balancing process on the given level. + + No need for locking here, as carry_tree_level is essentially per + thread thing (for now). + +*/ +struct carry_level { + /* this level may be restarted */ + __u32 restartable:1; + /* list of carry nodes on this level, ordered by key order */ + pool_level_list_head nodes; + pool_level_list_head ops; + /* pool where new objects are allocated from */ + carry_pool *pool; + int ops_num; + int nodes_num; + /* new root created on this level, if any */ + znode *new_root; + /* This is set by caller (insert_by_key(), resize_item(), etc.) when + they want ->tracked to automagically wander to the node where + insertion point moved after insert or paste. + */ + carry_track_type track_type; + /* lock handle supplied by user that we are tracking. See + above. */ + lock_handle *tracked; +#if REISER4_STATS + tree_level level_no; +#endif +}; + +/* information carry passes to plugin methods that may add new operations to + the @todo queue */ +struct carry_plugin_info { + carry_level *doing; + carry_level *todo; +}; + +int carry(carry_level * doing, carry_level * done); + +carry_node *add_carry(carry_level * level, pool_ordering order, carry_node * reference); +carry_node *add_carry_skip(carry_level * level, pool_ordering order, carry_node * reference); +carry_op *add_op(carry_level * level, pool_ordering order, carry_op * reference); + +extern carry_node *insert_carry_node(carry_level * doing, + carry_level * todo, const znode * node); + +extern carry_node *add_carry_atplace(carry_level *doing, + carry_level *todo, znode *node); + +extern carry_node *find_begetting_brother(carry_node * node, carry_level * kin); + +extern void init_carry_pool(carry_pool * pool); +extern void done_carry_pool(carry_pool * pool); + +extern void init_carry_level(carry_level * level, carry_pool * pool); + +extern carry_op *post_carry(carry_level * level, carry_opcode op, znode * node, int apply_to_parent); +extern carry_op *node_post_carry(carry_plugin_info * info, carry_opcode op, znode * node, int apply_to_parent_p); + +extern int carry_op_num(const carry_level * level); + +carry_node *add_new_znode(znode * brother, carry_node * reference, carry_level * doing, carry_level * todo); + +carry_node *find_carry_node(carry_level * level, const znode * node); + +extern znode *carry_real(const carry_node * node); + +/* helper macros to iterate over carry queues */ + +#define carry_node_next( node ) \ + ( ( carry_node * ) pool_level_list_next( &( node ) -> header ) ) + +#define carry_node_prev( node ) \ + ( ( carry_node * ) pool_level_list_prev( &( node ) -> header ) ) + +#define carry_node_front( level ) \ + ( ( carry_node * ) pool_level_list_front( &( level ) -> nodes ) ) + +#define carry_node_back( level ) \ + ( ( carry_node * ) pool_level_list_back( &( level ) -> nodes ) ) + +#define carry_node_end( level, node ) \ + ( pool_level_list_end( &( level ) -> nodes, &( node ) -> header ) ) + +/* macro to iterate over all operations in a @level */ +#define for_all_ops( level /* carry level (of type carry_level *) */, \ + op /* pointer to carry operation, modified by loop (of \ + * type carry_op *) */, \ + tmp /* pointer to carry operation (of type carry_op *), \ + * used to make iterator stable in the face of \ + * deletions from the level */ ) \ +for( op = ( carry_op * ) pool_level_list_front( &level -> ops ), \ + tmp = ( carry_op * ) pool_level_list_next( &op -> header ) ; \ + ! pool_level_list_end( &level -> ops, &op -> header ) ; \ + op = tmp, tmp = ( carry_op * ) pool_level_list_next( &op -> header ) ) + +/* macro to iterate over all nodes in a @level */ +#define for_all_nodes( level /* carry level (of type carry_level *) */, \ + node /* pointer to carry node, modified by loop (of \ + * type carry_node *) */, \ + tmp /* pointer to carry node (of type carry_node *), \ + * used to make iterator stable in the face of * \ + * deletions from the level */ ) \ +for( node = carry_node_front( level ), \ + tmp = carry_node_next( node ) ; ! carry_node_end( level, node ) ; \ + node = tmp, tmp = carry_node_next( node ) ) + +/* macro to iterate over all nodes in a @level in reverse order + + This is used, because nodes are unlocked in reversed order of locking */ +#define for_all_nodes_back( level /* carry level (of type carry_level *) */, \ + node /* pointer to carry node, modified by loop \ + * (of type carry_node *) */, \ + tmp /* pointer to carry node (of type carry_node \ + * *), used to make iterator stable in the \ + * face of deletions from the level */ ) \ +for( node = carry_node_back( level ), \ + tmp = carry_node_prev( node ) ; ! carry_node_end( level, node ) ; \ + node = tmp, tmp = carry_node_prev( node ) ) + +/* debugging function */ + +#if REISER4_DEBUG_OUTPUT +extern void print_carry(const char *prefix, carry_node * node); +extern void print_op(const char *prefix, carry_op * op); +extern void print_level(const char *prefix, carry_level * level); +#else +#define print_carry( p, n ) noop +#define print_op( p, o ) noop +#define print_level( p, l ) noop +#endif + +/* __FS_REISER4_CARRY_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/carry_ops.c 2004-09-03 00:57:04.989280776 -0700 @@ -0,0 +1,2171 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* implementation of carry operations */ + +#include "forward.h" +#include "debug.h" +#include "key.h" +#include "coord.h" +#include "plugin/item/item.h" +#include "plugin/node/node.h" +#include "jnode.h" +#include "znode.h" +#include "block_alloc.h" +#include "tree_walk.h" +#include "pool.h" +#include "tree_mod.h" +#include "carry.h" +#include "carry_ops.h" +#include "tree.h" +#include "super.h" +#include "reiser4.h" + +#include +#include + +static int carry_shift_data(sideof side, coord_t * insert_coord, znode * node, + carry_level * doing, carry_level * todo, unsigned int including_insert_coord_p); + +extern int lock_carry_node(carry_level * level, carry_node * node); +extern int lock_carry_node_tail(carry_node * node); + +/* find left neighbor of a carry node + + Look for left neighbor of @node and add it to the @doing queue. See + comments in the body. + +*/ +static carry_node * +find_left_neighbor(carry_op * op /* node to find left + * neighbor of */ , + carry_level * doing /* level to scan */ ) +{ + int result; + carry_node *node; + carry_node *left; + int flags; + reiser4_tree *tree; + + node = op->node; + + tree = current_tree; + RLOCK_TREE(tree); + /* first, check whether left neighbor is already in a @doing queue */ + if (carry_real(node)->left != NULL) { + /* NOTE: there is locking subtlety here. Look into + * find_right_neighbor() for more info */ + if (find_carry_node(doing, carry_real(node)->left) != NULL) { + RUNLOCK_TREE(tree); + left = node; + do { + left = carry_node_prev(left); + assert("nikita-3408", !carry_node_end(doing, + left)); + } while (carry_real(left) == carry_real(node)); + reiser4_stat_level_inc(doing, carry_left_in_carry); + return left; + } + } + RUNLOCK_TREE(tree); + + left = add_carry_skip(doing, POOLO_BEFORE, node); + if (IS_ERR(left)) + return left; + + left->node = node->node; + left->free = 1; + + flags = GN_TRY_LOCK; + if (!op->u.insert.flags & COPI_LOAD_LEFT) + flags |= GN_NO_ALLOC; + + /* then, feeling lucky, peek left neighbor in the cache. */ + result = reiser4_get_left_neighbor(&left->lock_handle, carry_real(node), + ZNODE_WRITE_LOCK, flags); + if (result == 0) { + /* ok, node found and locked. */ + result = lock_carry_node_tail(left); + if (result != 0) + left = ERR_PTR(result); + reiser4_stat_level_inc(doing, carry_left_in_cache); + } else if (result == -E_NO_NEIGHBOR || result == -ENOENT) { + /* node is leftmost node in a tree, or neighbor wasn't in + cache, or there is an extent on the left. */ + if (REISER4_STATS && (result == -ENOENT)) + reiser4_stat_level_inc(doing, carry_left_missed); + if (REISER4_STATS && (result == -E_NO_NEIGHBOR)) + reiser4_stat_level_inc(doing, carry_left_not_avail); + reiser4_pool_free(&doing->pool->node_pool, &left->header); + left = NULL; + } else if (doing->restartable) { + /* if left neighbor is locked, and level is restartable, add + new node to @doing and restart. */ + assert("nikita-913", node->parent != 0); + assert("nikita-914", node->node != NULL); + left->left = 1; + left->free = 0; + left = ERR_PTR(-E_REPEAT); + } else { + /* left neighbor is locked, level cannot be restarted. Just + ignore left neighbor. */ + reiser4_pool_free(&doing->pool->node_pool, &left->header); + left = NULL; + reiser4_stat_level_inc(doing, carry_left_refuse); + } + return left; +} + +/* find right neighbor of a carry node + + Look for right neighbor of @node and add it to the @doing queue. See + comments in the body. + +*/ +static carry_node * +find_right_neighbor(carry_op * op /* node to find right + * neighbor of */ , + carry_level * doing /* level to scan */ ) +{ + int result; + carry_node *node; + carry_node *right; + lock_handle lh; + int flags; + reiser4_tree *tree; + + init_lh(&lh); + + node = op->node; + + tree = current_tree; + RLOCK_TREE(tree); + /* first, check whether right neighbor is already in a @doing queue */ + if (carry_real(node)->right != NULL) { + /* + * Tree lock is taken here anyway, because, even if _outcome_ + * of (find_carry_node() != NULL) doesn't depends on + * concurrent updates to ->right, find_carry_node() cannot + * work with second argument NULL. Hence, following comment is + * of historic importance only. + * + * Subtle: + * + * Q: why don't we need tree lock here, looking for the right + * neighbor? + * + * A: even if value of node->real_node->right were changed + * during find_carry_node() execution, outcome of execution + * wouldn't change, because (in short) other thread cannot add + * elements to the @doing, and if node->real_node->right + * already was in @doing, value of node->real_node->right + * couldn't change, because node cannot be inserted between + * locked neighbors. + */ + if (find_carry_node(doing, carry_real(node)->right) != NULL) { + RUNLOCK_TREE(tree); + /* + * What we are doing here (this is also applicable to + * the find_left_neighbor()). + * + * tree_walk.c code requires that insertion of a + * pointer to a child, modification of parent pointer + * in the child, and insertion of the child into + * sibling list are atomic (see + * plugin/item/internal.c:create_hook_internal()). + * + * carry allocates new node long before pointer to it + * is inserted into parent and, actually, long before + * parent is even known. Such allocated-but-orphaned + * nodes are only trackable through carry level lists. + * + * Situation that is handled here is following: @node + * has valid ->right pointer, but there is + * allocated-but-orphaned node in the carry queue that + * is logically between @node and @node->right. Here + * we are searching for it. Critical point is that + * this is only possible if @node->right is also in + * the carry queue (this is checked above), because + * this is the only way new orphaned node could be + * inserted between them (before inserting new node, + * make_space() first tries to shift to the right, so, + * right neighbor will be locked and queued). + * + */ + right = node; + do { + right = carry_node_next(right); + assert("nikita-3408", !carry_node_end(doing, + right)); + } while (carry_real(right) == carry_real(node)); + reiser4_stat_level_inc(doing, carry_right_in_carry); + return right; + } + } + RUNLOCK_TREE(tree); + + flags = GN_CAN_USE_UPPER_LEVELS; + if (!op->u.insert.flags & COPI_LOAD_RIGHT) + flags = GN_NO_ALLOC; + + /* then, try to lock right neighbor */ + init_lh(&lh); + result = reiser4_get_right_neighbor(&lh, carry_real(node), + ZNODE_WRITE_LOCK, flags); + if (result == 0) { + /* ok, node found and locked. */ + reiser4_stat_level_inc(doing, carry_right_in_cache); + right = add_carry_skip(doing, POOLO_AFTER, node); + if (!IS_ERR(right)) { + right->node = lh.node; + move_lh(&right->lock_handle, &lh); + right->free = 1; + result = lock_carry_node_tail(right); + if (result != 0) + right = ERR_PTR(result); + } + } else if ((result == -E_NO_NEIGHBOR) || (result == -ENOENT)) { + /* node is rightmost node in a tree, or neighbor wasn't in + cache, or there is an extent on the right. */ + right = NULL; + if (REISER4_STATS && (result == -ENOENT)) + reiser4_stat_level_inc(doing, carry_right_missed); + if (REISER4_STATS && (result == -E_NO_NEIGHBOR)) + reiser4_stat_level_inc(doing, carry_right_not_avail); + } else + right = ERR_PTR(result); + done_lh(&lh); + return right; +} + +/* how much free space in a @node is needed for @op + + How much space in @node is required for completion of @op, where @op is + insert or paste operation. +*/ +static unsigned int +space_needed_for_op(znode * node /* znode data are + * inserted or + * pasted in */ , + carry_op * op /* carry + operation */ ) +{ + assert("nikita-919", op != NULL); + + switch (op->op) { + default: + impossible("nikita-1701", "Wrong opcode"); + case COP_INSERT: + return space_needed(node, NULL, op->u.insert.d->data, 1); + case COP_PASTE: + return space_needed(node, op->u.insert.d->coord, op->u.insert.d->data, 0); + } +} + +/* how much space in @node is required to insert or paste @data at + @coord. */ +reiser4_internal unsigned int +space_needed(const znode * node /* node data are inserted or + * pasted in */ , + const coord_t * coord /* coord where data are + * inserted or pasted + * at */ , + const reiser4_item_data * data /* data to insert or + * paste */ , + int insertion /* non-0 is inserting, 0---paste */ ) +{ + int result; + item_plugin *iplug; + + assert("nikita-917", node != NULL); + assert("nikita-918", node_plugin_by_node(node) != NULL); + assert("vs-230", !insertion || (coord == NULL)); + + result = 0; + iplug = data->iplug; + if (iplug->b.estimate != NULL) { + /* ask item plugin how much space is needed to insert this + item */ + result += iplug->b.estimate(insertion ? NULL : coord, data); + } else { + /* reasonable default */ + result += data->length; + } + if (insertion) { + node_plugin *nplug; + + nplug = node->nplug; + /* and add node overhead */ + if (nplug->item_overhead != NULL) { + result += nplug->item_overhead(node, 0); + } + } + return result; +} + +/* find &coord in parent where pointer to new child is to be stored. */ +static int +find_new_child_coord(carry_op * op /* COP_INSERT carry operation to + * insert pointer to new + * child */ ) +{ + int result; + znode *node; + znode *child; + + assert("nikita-941", op != NULL); + assert("nikita-942", op->op == COP_INSERT); + + trace_stamp(TRACE_CARRY); + + node = carry_real(op->node); + assert("nikita-943", node != NULL); + assert("nikita-944", node_plugin_by_node(node) != NULL); + + child = carry_real(op->u.insert.child); + result = find_new_child_ptr(node, child, op->u.insert.brother, op->u.insert.d->coord); + + build_child_ptr_data(child, op->u.insert.d->data); + return result; +} + +/* additional amount of free space in @node required to complete @op */ +static int +free_space_shortage(znode * node /* node to check */ , + carry_op * op /* operation being performed */ ) +{ + assert("nikita-1061", node != NULL); + assert("nikita-1062", op != NULL); + + switch (op->op) { + default: + impossible("nikita-1702", "Wrong opcode"); + case COP_INSERT: + case COP_PASTE: + return space_needed_for_op(node, op) - znode_free_space(node); + case COP_EXTENT: + /* when inserting extent shift data around until insertion + point is utmost in the node. */ + if (coord_wrt(op->u.insert.d->coord) == COORD_INSIDE) + return +1; + else + return -1; + } +} + +/* helper function: update node pointer in operation after insertion + point was probably shifted into @target. */ +static znode * +sync_op(carry_op * op, carry_node * target) +{ + znode *insertion_node; + + /* reget node from coord: shift might move insertion coord to + the neighbor */ + insertion_node = op->u.insert.d->coord->node; + /* if insertion point was actually moved into new node, + update carry node pointer in operation. */ + if (insertion_node != carry_real(op->node)) { + op->node = target; + assert("nikita-2540", carry_real(target) == insertion_node); + } + assert("nikita-2541", + carry_real(op->node) == op->u.insert.d->coord->node); + return insertion_node; +} + +/* + * complete make_space() call: update tracked lock handle if necessary. See + * comments for fs/reiser4/carry.h:carry_track_type + */ +static int +make_space_tail(carry_op * op, carry_level * doing, znode * orig_node) +{ + int result; + carry_track_type tracking; + znode *node; + + tracking = doing->track_type; + node = op->u.insert.d->coord->node; + + if (tracking == CARRY_TRACK_NODE || + (tracking == CARRY_TRACK_CHANGE && node != orig_node)) { + /* inserting or pasting into node different from + original. Update lock handle supplied by caller. */ + assert("nikita-1417", doing->tracked != NULL); + done_lh(doing->tracked); + init_lh(doing->tracked); + result = longterm_lock_znode(doing->tracked, node, + ZNODE_WRITE_LOCK, ZNODE_LOCK_HIPRI); + reiser4_stat_level_inc(doing, track_lh); + ON_TRACE(TRACE_CARRY, "tracking: %i: %p -> %p\n", + tracking, orig_node, node); + } else + result = 0; + return result; +} + +/* This is insertion policy function. It shifts data to the left and right + neighbors of insertion coord and allocates new nodes until there is enough + free space to complete @op. + + See comments in the body. + + Assumes that the node format favors insertions at the right end of the node + as node40 does. + + See carry_flow() on detail about flow insertion +*/ +static int +make_space(carry_op * op /* carry operation, insert or paste */ , + carry_level * doing /* current carry queue */ , + carry_level * todo /* carry queue on the parent level */ ) +{ + znode *node; + int result; + int not_enough_space; + int blk_alloc; + znode *orig_node; + __u32 flags; + + coord_t *coord; + + assert("nikita-890", op != NULL); + assert("nikita-891", todo != NULL); + assert("nikita-892", + op->op == COP_INSERT || + op->op == COP_PASTE || op->op == COP_EXTENT); + assert("nikita-1607", + carry_real(op->node) == op->u.insert.d->coord->node); + + trace_stamp(TRACE_CARRY); + + flags = op->u.insert.flags; + + /* NOTE check that new node can only be allocated after checking left + * and right neighbors. This is necessary for proper work of + * find_{left,right}_neighbor(). */ + assert("nikita-3410", ergo(flags & COPI_DONT_ALLOCATE, + flags & COPI_DONT_SHIFT_LEFT)); + assert("nikita-3411", ergo(flags & COPI_DONT_ALLOCATE, + flags & COPI_DONT_SHIFT_RIGHT)); + + coord = op->u.insert.d->coord; + orig_node = node = coord->node; + + assert("nikita-908", node != NULL); + assert("nikita-909", node_plugin_by_node(node) != NULL); + + result = 0; + /* If there is not enough space in a node, try to shift something to + the left neighbor. This is a bit tricky, as locking to the left is + low priority. This is handled by restart logic in carry(). + */ + not_enough_space = free_space_shortage(node, op); + if (not_enough_space <= 0) + /* it is possible that carry was called when there actually + was enough space in the node. For example, when inserting + leftmost item so that delimiting keys have to be updated. + */ + return make_space_tail(op, doing, orig_node); + if (!(flags & COPI_DONT_SHIFT_LEFT)) { + carry_node *left; + /* make note in statistics of an attempt to move + something into the left neighbor */ + reiser4_stat_level_inc(doing, insert_looking_left); + left = find_left_neighbor(op, doing); + if (unlikely(IS_ERR(left))) { + if (PTR_ERR(left) == -E_REPEAT) + return -E_REPEAT; + else { + /* some error other than restart request + occurred. This shouldn't happen. Issue a + warning and continue as if left neighbor + weren't existing. + */ + warning("nikita-924", + "Error accessing left neighbor: %li", + PTR_ERR(left)); + print_znode("node", node); + } + } else if (left != NULL) { + + /* shift everything possible on the left of and + including insertion coord into the left neighbor */ + result = carry_shift_data(LEFT_SIDE, coord, + carry_real(left), doing, todo, + flags & COPI_GO_LEFT); + + /* reget node from coord: shift_left() might move + insertion coord to the left neighbor */ + node = sync_op(op, left); + + not_enough_space = free_space_shortage(node, op); + /* There is not enough free space in @node, but + may be, there is enough free space in + @left. Various balancing decisions are valid here. + The same for the shifiting to the right. + */ + } + } + /* If there still is not enough space, shift to the right */ + if (not_enough_space > 0 && !(flags & COPI_DONT_SHIFT_RIGHT)) { + carry_node *right; + + reiser4_stat_level_inc(doing, insert_looking_right); + right = find_right_neighbor(op, doing); + if (IS_ERR(right)) { + warning("nikita-1065", + "Error accessing right neighbor: %li", + PTR_ERR(right)); + print_znode("node", node); + } else if (right != NULL) { + /* node containing insertion point, and its right + neighbor node are write locked by now. + + shift everything possible on the right of but + excluding insertion coord into the right neighbor + */ + result = carry_shift_data(RIGHT_SIDE, coord, + carry_real(right), + doing, todo, + flags & COPI_GO_RIGHT); + /* reget node from coord: shift_right() might move + insertion coord to the right neighbor */ + node = sync_op(op, right); + not_enough_space = free_space_shortage(node, op); + } + } + /* If there is still not enough space, allocate new node(s). + + We try to allocate new blocks if COPI_DONT_ALLOCATE is not set in + the carry operation flags (currently this is needed during flush + only). + */ + for (blk_alloc = 0; + not_enough_space > 0 && result == 0 && blk_alloc < 2 && + !(flags & COPI_DONT_ALLOCATE); ++blk_alloc) { + carry_node *fresh; /* new node we are allocating */ + coord_t coord_shadow; /* remembered insertion point before + * shifting data into new node */ + carry_node *node_shadow; /* remembered insertion node before + * shifting */ + unsigned int gointo; /* whether insertion point should move + * into newly allocated node */ + + reiser4_stat_level_inc(doing, insert_alloc_new); + if (blk_alloc > 0) + reiser4_stat_level_inc(doing, insert_alloc_many); + + /* allocate new node on the right of @node. Znode and disk + fake block number for new node are allocated. + + add_new_znode() posts carry operation COP_INSERT with + COPT_CHILD option to the parent level to add + pointer to newly created node to its parent. + + Subtle point: if several new nodes are required to complete + insertion operation at this level, they will be inserted + into their parents in the order of creation, which means + that @node will be valid "cookie" at the time of insertion. + + */ + fresh = add_new_znode(node, op->node, doing, todo); + if (IS_ERR(fresh)) + return PTR_ERR(fresh); + + /* Try to shift into new node. */ + result = lock_carry_node(doing, fresh); + zput(carry_real(fresh)); + if (result != 0) { + warning("nikita-947", + "Cannot lock new node: %i", result); + print_znode("new", carry_real(fresh)); + print_znode("node", node); + return result; + } + + /* both nodes are write locked by now. + + shift everything possible on the right of and + including insertion coord into the right neighbor. + */ + coord_dup(&coord_shadow, op->u.insert.d->coord); + node_shadow = op->node; + /* move insertion point into newly created node if: + + . insertion point is rightmost in the source node, or + . this is not the first node we are allocating in a row. + */ + gointo = + (blk_alloc > 0) || + coord_is_after_rightmost(op->u.insert.d->coord); + + result = carry_shift_data(RIGHT_SIDE, coord, carry_real(fresh), + doing, todo, gointo); + /* if insertion point was actually moved into new node, + update carry node pointer in operation. */ + node = sync_op(op, fresh); + not_enough_space = free_space_shortage(node, op); + if ((not_enough_space > 0) && (node != coord_shadow.node)) { + /* there is not enough free in new node. Shift + insertion point back to the @shadow_node so that + next new node would be inserted between + @shadow_node and @fresh. + */ + coord_normalize(&coord_shadow); + coord_dup(coord, &coord_shadow); + node = coord->node; + op->node = node_shadow; + if (1 || (flags & COPI_STEP_BACK)) { + /* still not enough space?! Maybe there is + enough space in the source node (i.e., node + data are moved from) now. + */ + not_enough_space = free_space_shortage(node, op); + } + } + } + if (not_enough_space > 0) { + if (!(flags & COPI_DONT_ALLOCATE)) + warning("nikita-948", "Cannot insert new item"); + result = -E_NODE_FULL; + } + assert("nikita-1622", ergo(result == 0, + carry_real(op->node) == coord->node)); + assert("nikita-2616", coord == op->u.insert.d->coord); + if (result == 0) + result = make_space_tail(op, doing, orig_node); + return result; +} + +/* insert_paste_common() - common part of insert and paste operations + + This function performs common part of COP_INSERT and COP_PASTE. + + There are two ways in which insertion/paste can be requested: + + . by directly supplying reiser4_item_data. In this case, op -> + u.insert.type is set to COPT_ITEM_DATA. + + . by supplying child pointer to which is to inserted into parent. In this + case op -> u.insert.type == COPT_CHILD. + + . by supplying key of new item/unit. This is currently only used during + extent insertion + + This is required, because when new node is allocated we don't know at what + position pointer to it is to be stored in the parent. Actually, we don't + even know what its parent will be, because parent can be re-balanced + concurrently and new node re-parented, and because parent can be full and + pointer to the new node will go into some other node. + + insert_paste_common() resolves pointer to child node into position in the + parent by calling find_new_child_coord(), that fills + reiser4_item_data. After this, insertion/paste proceeds uniformly. + + Another complication is with finding free space during pasting. It may + happen that while shifting items to the neighbors and newly allocated + nodes, insertion coord can no longer be in the item we wanted to paste + into. At this point, paste becomes (morphs) into insert. Moreover free + space analysis has to be repeated, because amount of space required for + insertion is different from that of paste (item header overhead, etc). + + This function "unifies" different insertion modes (by resolving child + pointer or key into insertion coord), and then calls make_space() to free + enough space in the node by shifting data to the left and right and by + allocating new nodes if necessary. Carry operation knows amount of space + required for its completion. After enough free space is obtained, caller of + this function (carry_{insert,paste,etc.}) performs actual insertion/paste + by calling item plugin method. + +*/ +static int +insert_paste_common(carry_op * op /* carry operation being + * performed */ , + carry_level * doing /* current carry level */ , + carry_level * todo /* next carry level */ , + carry_insert_data * cdata /* pointer to + * cdata */ , + coord_t * coord /* insertion/paste coord */ , + reiser4_item_data * data /* data to be + * inserted/pasted */ ) +{ + assert("nikita-981", op != NULL); + assert("nikita-980", todo != NULL); + assert("nikita-979", (op->op == COP_INSERT) || (op->op == COP_PASTE) || (op->op == COP_EXTENT)); + + trace_stamp(TRACE_CARRY); + + if (op->u.insert.type == COPT_PASTE_RESTARTED) { + /* nothing to do. Fall through to make_space(). */ + ; + } else if (op->u.insert.type == COPT_KEY) { + node_search_result intra_node; + znode *node; + /* Problem with doing batching at the lowest level, is that + operations here are given by coords where modification is + to be performed, and one modification can invalidate coords + of all following operations. + + So, we are implementing yet another type for operation that + will use (the only) "locator" stable across shifting of + data between nodes, etc.: key (COPT_KEY). + + This clause resolves key to the coord in the node. + + But node can change also. Probably some pieces have to be + added to the lock_carry_node(), to lock node by its key. + + */ + /* NOTE-NIKITA Lookup bias is fixed to FIND_EXACT. Complain + if you need something else. */ + op->u.insert.d->coord = coord; + node = carry_real(op->node); + intra_node = node_plugin_by_node(node)->lookup + (node, op->u.insert.d->key, FIND_EXACT, op->u.insert.d->coord); + if ((intra_node != NS_FOUND) && (intra_node != NS_NOT_FOUND)) { + warning("nikita-1715", "Intra node lookup failure: %i", intra_node); + print_znode("node", node); + return intra_node; + } + } else if (op->u.insert.type == COPT_CHILD) { + /* if we are asked to insert pointer to the child into + internal node, first convert pointer to the child into + coord within parent node. + */ + znode *child; + int result; + + op->u.insert.d = cdata; + op->u.insert.d->coord = coord; + op->u.insert.d->data = data; + op->u.insert.d->coord->node = carry_real(op->node); + result = find_new_child_coord(op); + child = carry_real(op->u.insert.child); + if (result != NS_NOT_FOUND) { + warning("nikita-993", "Cannot find a place for child pointer: %i", result); + print_znode("child", child); + print_znode("parent", carry_real(op->node)); + return result; + } + /* This only happens when we did multiple insertions at + the previous level, trying to insert single item and + it so happened, that insertion of pointers to all new + nodes before this one already caused parent node to + split (may be several times). + + I am going to come up with better solution. + + You are not expected to understand this. + -- v6root/usr/sys/ken/slp.c + + Basically, what happens here is the following: carry came + to the parent level and is about to insert internal item + pointing to the child node that it just inserted in the + level below. Position where internal item is to be inserted + was found by find_new_child_coord() above, but node of the + current carry operation (that is, parent node of child + inserted on the previous level), was determined earlier in + the lock_carry_level/lock_carry_node. It could so happen + that other carry operations already performed on the parent + level already split parent node, so that insertion point + moved into another node. Handle this by creating new carry + node for insertion point if necessary. + */ + if (carry_real(op->node) != op->u.insert.d->coord->node) { + pool_ordering direction; + znode *z1; + znode *z2; + reiser4_key k1; + reiser4_key k2; + + /* + * determine in what direction insertion point + * moved. Do this by comparing delimiting keys. + */ + z1 = op->u.insert.d->coord->node; + z2 = carry_real(op->node); + if (keyle(leftmost_key_in_node(z1, &k1), + leftmost_key_in_node(z2, &k2))) + /* insertion point moved to the left */ + direction = POOLO_BEFORE; + else + /* insertion point moved to the right */ + direction = POOLO_AFTER; + + op->node = add_carry_skip(doing, direction, op->node); + if (IS_ERR(op->node)) + return PTR_ERR(op->node); + op->node->node = op->u.insert.d->coord->node; + op->node->free = 1; + result = lock_carry_node(doing, op->node); + if (result != 0) + return result; + } + + /* + * set up key of an item being inserted: we are inserting + * internal item and its key is (by the very definition of + * search tree) is leftmost key in the child node. + */ + op->u.insert.d->key = UNDER_RW(dk, znode_get_tree(child), read, + leftmost_key_in_node(child, znode_get_ld_key(child))); + op->u.insert.d->data->arg = op->u.insert.brother; + } else { + assert("vs-243", op->u.insert.d->coord != NULL); + op->u.insert.d->coord->node = carry_real(op->node); + } + + /* find free space. */ + return make_space(op, doing, todo); +} + +/* handle carry COP_INSERT operation. + + Insert new item into node. New item can be given in one of two ways: + + - by passing &tree_coord and &reiser4_item_data as part of @op. This is + only applicable at the leaf/twig level. + + - by passing a child node pointer to which is to be inserted by this + operation. + +*/ +static int +carry_insert(carry_op * op /* operation to perform */ , + carry_level * doing /* queue of operations @op + * is part of */ , + carry_level * todo /* queue where new operations + * are accumulated */ ) +{ + znode *node; + carry_insert_data cdata; + coord_t coord; + reiser4_item_data data; + carry_plugin_info info; + int result; + + assert("nikita-1036", op != NULL); + assert("nikita-1037", todo != NULL); + assert("nikita-1038", op->op == COP_INSERT); + + trace_stamp(TRACE_CARRY); + reiser4_stat_level_inc(doing, insert); + + coord_init_zero(&coord); + + /* perform common functionality of insert and paste. */ + result = insert_paste_common(op, doing, todo, &cdata, &coord, &data); + if (result != 0) + return result; + + node = op->u.insert.d->coord->node; + assert("nikita-1039", node != NULL); + assert("nikita-1040", node_plugin_by_node(node) != NULL); + + assert("nikita-949", space_needed_for_op(node, op) <= znode_free_space(node)); + + /* ask node layout to create new item. */ + info.doing = doing; + info.todo = todo; + result = node_plugin_by_node(node)->create_item + (op->u.insert.d->coord, op->u.insert.d->key, op->u.insert.d->data, &info); + doing->restartable = 0; + znode_make_dirty(node); + + return result; +} + +/* + * Flow insertion code. COP_INSERT_FLOW is special tree operation that is + * supplied with a "flow" (that is, a stream of data) and inserts it into tree + * by slicing into multiple items. + */ + +#define flow_insert_point(op) ( ( op ) -> u.insert_flow.insert_point ) +#define flow_insert_flow(op) ( ( op ) -> u.insert_flow.flow ) +#define flow_insert_data(op) ( ( op ) -> u.insert_flow.data ) + +static size_t +item_data_overhead(carry_op * op) +{ + if (flow_insert_data(op)->iplug->b.estimate == NULL) + return 0; + return (flow_insert_data(op)->iplug->b.estimate(NULL /* estimate insertion */, flow_insert_data(op)) - + flow_insert_data(op)->length); +} + +/* FIXME-VS: this is called several times during one make_flow_for_insertion + and it will always return the same result. Some optimization could be made + by calculating this value once at the beginning and passing it around. That + would reduce some flexibility in future changes +*/ +static int can_paste(coord_t *, const reiser4_key *, const reiser4_item_data *); +static size_t +flow_insertion_overhead(carry_op * op) +{ + znode *node; + size_t insertion_overhead; + + node = flow_insert_point(op)->node; + insertion_overhead = 0; + if (node->nplug->item_overhead && + !can_paste(flow_insert_point(op), &flow_insert_flow(op)->key, flow_insert_data(op))) + insertion_overhead = node->nplug->item_overhead(node, 0) + item_data_overhead(op); + return insertion_overhead; +} + +/* how many bytes of flow does fit to the node */ +static int +what_can_fit_into_node(carry_op * op) +{ + size_t free, overhead; + + overhead = flow_insertion_overhead(op); + free = znode_free_space(flow_insert_point(op)->node); + if (free <= overhead) + return 0; + free -= overhead; + /* FIXME: flow->length is loff_t only to not get overflowed in case of expandign truncate */ + if (free < op->u.insert_flow.flow->length) + return free; + return (int)op->u.insert_flow.flow->length; +} + +/* in make_space_for_flow_insertion we need to check either whether whole flow + fits into a node or whether minimal fraction of flow fits into a node */ +static int +enough_space_for_whole_flow(carry_op * op) +{ + return (unsigned) what_can_fit_into_node(op) == op->u.insert_flow.flow->length; +} + +#define MIN_FLOW_FRACTION 1 +static int +enough_space_for_min_flow_fraction(carry_op * op) +{ + assert("vs-902", coord_is_after_rightmost(flow_insert_point(op))); + + return what_can_fit_into_node(op) >= MIN_FLOW_FRACTION; +} + +/* this returns 0 if left neighbor was obtained successfully and everything + upto insertion point including it were shifted and left neighbor still has + some free space to put minimal fraction of flow into it */ +static int +make_space_by_shift_left(carry_op * op, carry_level * doing, carry_level * todo) +{ + carry_node *left; + znode *orig; + + left = find_left_neighbor(op, doing); + if (unlikely(IS_ERR(left))) { + warning("vs-899", "make_space_by_shift_left: " "error accessing left neighbor: %li", PTR_ERR(left)); + return 1; + } + if (left == NULL) + /* left neighbor either does not exist or is unformatted + node */ + return 1; + + orig = flow_insert_point(op)->node; + /* try to shift content of node @orig from its head upto insert point + including insertion point into the left neighbor */ + carry_shift_data(LEFT_SIDE, flow_insert_point(op), + carry_real(left), doing, todo, 1 /* including insert + * point */); + if (carry_real(left) != flow_insert_point(op)->node) { + /* insertion point did not move */ + return 1; + } + + /* insertion point is set after last item in the node */ + assert("vs-900", coord_is_after_rightmost(flow_insert_point(op))); + + if (!enough_space_for_min_flow_fraction(op)) { + /* insertion point node does not have enough free space to put + even minimal portion of flow into it, therefore, move + insertion point back to orig node (before first item) */ + coord_init_before_first_item(flow_insert_point(op), orig); + return 1; + } + + /* part of flow is to be written to the end of node */ + op->node = left; + return 0; +} + +/* this returns 0 if right neighbor was obtained successfully and everything to + the right of insertion point was shifted to it and node got enough free + space to put minimal fraction of flow into it */ +static int +make_space_by_shift_right(carry_op * op, carry_level * doing, carry_level * todo) +{ + carry_node *right; + + right = find_right_neighbor(op, doing); + if (unlikely(IS_ERR(right))) { + warning("nikita-1065", "shift_right_excluding_insert_point: " + "error accessing right neighbor: %li", PTR_ERR(right)); + return 1; + } + if (right) { + /* shift everything possible on the right of but excluding + insertion coord into the right neighbor */ + carry_shift_data(RIGHT_SIDE, flow_insert_point(op), + carry_real(right), doing, todo, 0 /* not + * including + * insert + * point */); + } else { + /* right neighbor either does not exist or is unformatted + node */ + ; + } + if (coord_is_after_rightmost(flow_insert_point(op))) { + if (enough_space_for_min_flow_fraction(op)) { + /* part of flow is to be written to the end of node */ + return 0; + } + } + + /* new node is to be added if insert point node did not get enough + space for whole flow */ + return 1; +} + +/* this returns 0 when insert coord is set at the node end and fraction of flow + fits into that node */ +static int +make_space_by_new_nodes(carry_op * op, carry_level * doing, carry_level * todo) +{ + int result; + znode *node; + carry_node *new; + + node = flow_insert_point(op)->node; + + if (op->u.insert_flow.new_nodes == CARRY_FLOW_NEW_NODES_LIMIT) + return RETERR(-E_NODE_FULL); + /* add new node after insert point node */ + new = add_new_znode(node, op->node, doing, todo); + if (unlikely(IS_ERR(new))) { + return PTR_ERR(new); + } + result = lock_carry_node(doing, new); + zput(carry_real(new)); + if (unlikely(result)) { + return result; + } + op->u.insert_flow.new_nodes++; + if (!coord_is_after_rightmost(flow_insert_point(op))) { + carry_shift_data(RIGHT_SIDE, flow_insert_point(op), + carry_real(new), doing, todo, 0 /* not + * including + * insert + * point */); + + assert("vs-901", coord_is_after_rightmost(flow_insert_point(op))); + + if (enough_space_for_min_flow_fraction(op)) { + return 0; + } + if (op->u.insert_flow.new_nodes == CARRY_FLOW_NEW_NODES_LIMIT) + return RETERR(-E_NODE_FULL); + + /* add one more new node */ + new = add_new_znode(node, op->node, doing, todo); + if (unlikely(IS_ERR(new))) { + return PTR_ERR(new); + } + result = lock_carry_node(doing, new); + zput(carry_real(new)); + if (unlikely(result)) { + return result; + } + op->u.insert_flow.new_nodes++; + } + + /* move insertion point to new node */ + coord_init_before_first_item(flow_insert_point(op), carry_real(new)); + op->node = new; + return 0; +} + +static int +make_space_for_flow_insertion(carry_op * op, carry_level * doing, carry_level * todo) +{ + __u32 flags = op->u.insert_flow.flags; + + if (enough_space_for_whole_flow(op)) { + /* whole flow fits into insert point node */ + return 0; + } + + if (!(flags & COPI_DONT_SHIFT_LEFT) && (make_space_by_shift_left(op, doing, todo) == 0)) { + /* insert point is shifted to left neighbor of original insert + point node and is set after last unit in that node. It has + enough space to fit at least minimal fraction of flow. */ + return 0; + } + + if (enough_space_for_whole_flow(op)) { + /* whole flow fits into insert point node */ + return 0; + } + + if (!(flags & COPI_DONT_SHIFT_RIGHT) && (make_space_by_shift_right(op, doing, todo) == 0)) { + /* insert point is still set to the same node, but there is + nothing to the right of insert point. */ + return 0; + } + + if (enough_space_for_whole_flow(op)) { + /* whole flow fits into insert point node */ + return 0; + } + + return make_space_by_new_nodes(op, doing, todo); +} + +/* implements COP_INSERT_FLOW operation */ +static int +carry_insert_flow(carry_op * op, carry_level * doing, carry_level * todo) +{ + int result; + flow_t *f; + coord_t *insert_point; + node_plugin *nplug; + int something_written; + carry_plugin_info info; + znode *orig_node; + lock_handle *orig_lh; + + f = op->u.insert_flow.flow; + result = 0; + + /* this flag is used to distinguish a need to have carry to propagate + leaf level modifications up in the tree when make_space fails not in + first iteration of the loop below */ + something_written = 0; + + /* carry system needs this to work */ + info.doing = doing; + info.todo = todo; + + orig_node = flow_insert_point(op)->node; + orig_lh = doing->tracked; + + while (f->length) { + result = make_space_for_flow_insertion(op, doing, todo); + if (result) + break; + + insert_point = flow_insert_point(op); + nplug = node_plugin_by_node(insert_point->node); + + /* compose item data for insertion/pasting */ + flow_insert_data(op)->data = f->data; + flow_insert_data(op)->length = what_can_fit_into_node(op); + + if (can_paste(insert_point, &f->key, flow_insert_data(op))) { + /* insert point is set to item of file we are writing to and we have to append to it */ + assert("vs-903", insert_point->between == AFTER_UNIT); + nplug->change_item_size(insert_point, flow_insert_data(op)->length); + flow_insert_data(op)->iplug->b.paste(insert_point, flow_insert_data(op), &info); + } else { + /* new item must be inserted */ + pos_in_node_t new_pos; + flow_insert_data(op)->length += item_data_overhead(op); + + /* FIXME-VS: this is because node40_create_item changes + insert_point for obscure reasons */ + switch (insert_point->between) { + case AFTER_ITEM: + new_pos = insert_point->item_pos + 1; + break; + case EMPTY_NODE: + new_pos = 0; + break; + case BEFORE_ITEM: + assert("vs-905", insert_point->item_pos == 0); + new_pos = 0; + break; + default: + impossible("vs-906", "carry_insert_flow: invalid coord"); + new_pos = 0; + break; + } + + nplug->create_item(insert_point, &f->key, flow_insert_data(op), &info); + coord_set_item_pos(insert_point, new_pos); + } + coord_init_after_item_end(insert_point); + doing->restartable = 0; + znode_make_dirty(insert_point->node); + + move_flow_forward(f, (unsigned) flow_insert_data(op)->length); + something_written = 1; + } + + if (orig_node != flow_insert_point(op)->node) { + /* move lock to new insert point */ + done_lh(orig_lh); + init_lh(orig_lh); + result = longterm_lock_znode(orig_lh, flow_insert_point(op)->node, ZNODE_WRITE_LOCK, ZNODE_LOCK_HIPRI); + } + + return result; +} + +/* implements COP_DELETE operation + + Remove pointer to @op -> u.delete.child from it's parent. + + This function also handles killing of a tree root is last pointer from it + was removed. This is complicated by our handling of "twig" level: root on + twig level is never killed. + +*/ +static int +carry_delete(carry_op * op /* operation to be performed */ , + carry_level * doing UNUSED_ARG /* current carry + * level */ , + carry_level * todo /* next carry level */ ) +{ + int result; + coord_t coord; + coord_t coord2; + znode *parent; + znode *child; + carry_plugin_info info; + reiser4_tree *tree; + + /* + * This operation is called to delete internal item pointing to the + * child node that was removed by carry from the tree on the previous + * tree level. + */ + + assert("nikita-893", op != NULL); + assert("nikita-894", todo != NULL); + assert("nikita-895", op->op == COP_DELETE); + trace_stamp(TRACE_CARRY); + reiser4_stat_level_inc(doing, delete); + + coord_init_zero(&coord); + coord_init_zero(&coord2); + + parent = carry_real(op->node); + child = op->u.delete.child ? + carry_real(op->u.delete.child) : op->node->node; + tree = znode_get_tree(child); + RLOCK_TREE(tree); + + /* + * @parent was determined when carry entered parent level + * (lock_carry_level/lock_carry_node). Since then, actual parent of + * @child node could change due to other carry operations performed on + * the parent level. Check for this. + */ + + if (znode_parent(child) != parent) { + /* NOTE-NIKITA add stat counter for this. */ + parent = znode_parent(child); + assert("nikita-2581", find_carry_node(doing, parent)); + } + RUNLOCK_TREE(tree); + + assert("nikita-1213", znode_get_level(parent) > LEAF_LEVEL); + + /* Twig level horrors: tree should be of height at least 2. So, last + pointer from the root at twig level is preserved even if child is + empty. This is ugly, but so it was architectured. + */ + + if (znode_is_root(parent) && + znode_get_level(parent) <= REISER4_MIN_TREE_HEIGHT && + node_num_items(parent) == 1) { + /* Delimiting key manipulations. */ + WLOCK_DK(tree); + znode_set_ld_key(child, znode_set_ld_key(parent, min_key())); + znode_set_rd_key(child, znode_set_rd_key(parent, max_key())); + WUNLOCK_DK(tree); + + /* @child escaped imminent death! */ + ZF_CLR(child, JNODE_HEARD_BANSHEE); + return 0; + } + + /* convert child pointer to the coord_t */ + result = find_child_ptr(parent, child, &coord); + if (result != NS_FOUND) { + warning("nikita-994", "Cannot find child pointer: %i", result); + print_znode("child", child); + print_znode("parent", parent); + print_coord_content("coord", &coord); + return result; + } + + coord_dup(&coord2, &coord); + info.doing = doing; + info.todo = todo; + { + /* + * Actually kill internal item: prepare structure with + * arguments for ->cut_and_kill() method... + */ + + struct carry_kill_data kdata; + kdata.params.from = &coord; + kdata.params.to = &coord2; + kdata.params.from_key = NULL; + kdata.params.to_key = NULL; + kdata.params.smallest_removed = NULL; + kdata.flags = op->u.delete.flags; + kdata.inode = 0; + kdata.left = 0; + kdata.right = 0; + /* ... and call it. */ + result = node_plugin_by_node(parent)->cut_and_kill(&kdata, + &info); + } + doing->restartable = 0; + + /* check whether root should be killed violently */ + if (znode_is_root(parent) && + /* don't kill roots at and lower than twig level */ + znode_get_level(parent) > REISER4_MIN_TREE_HEIGHT && + node_num_items(parent) == 1) { + result = kill_tree_root(coord.node); + } + + return result < 0 ? : 0; +} + +/* implements COP_CUT opration + + Cuts part or whole content of node. + +*/ +static int +carry_cut(carry_op * op /* operation to be performed */ , + carry_level * doing /* current carry level */ , + carry_level * todo /* next carry level */ ) +{ + int result; + carry_plugin_info info; + node_plugin *nplug; + + assert("nikita-896", op != NULL); + assert("nikita-897", todo != NULL); + assert("nikita-898", op->op == COP_CUT); + trace_stamp(TRACE_CARRY); + reiser4_stat_level_inc(doing, cut); + + info.doing = doing; + info.todo = todo; + + nplug = node_plugin_by_node(carry_real(op->node)); + if (op->u.cut_or_kill.is_cut) + result = nplug->cut(op->u.cut_or_kill.u.cut, &info); + else + result = nplug->cut_and_kill(op->u.cut_or_kill.u.kill, &info); + + doing->restartable = 0; + return result < 0 ? : 0; +} + +/* helper function for carry_paste(): returns true if @op can be continued as + paste */ +static int +can_paste(coord_t * icoord, const reiser4_key * key, const reiser4_item_data * data) +{ + coord_t circa; + item_plugin *new_iplug; + item_plugin *old_iplug; + int result = 0; /* to keep gcc shut */ + + assert("", icoord->between != AT_UNIT); + + /* obviously, one cannot paste when node is empty---there is nothing + to paste into. */ + if (node_is_empty(icoord->node)) + return 0; + /* if insertion point is at the middle of the item, then paste */ + if (!coord_is_between_items(icoord)) + return 1; + coord_dup(&circa, icoord); + circa.between = AT_UNIT; + + old_iplug = item_plugin_by_coord(&circa); + new_iplug = data->iplug; + + /* check whether we can paste to the item @icoord is "at" when we + ignore ->between field */ + if (old_iplug == new_iplug && item_can_contain_key(&circa, key, data)) { + result = 1; + } else if (icoord->between == BEFORE_UNIT || icoord->between == BEFORE_ITEM) { + /* otherwise, try to glue to the item at the left, if any */ + coord_dup(&circa, icoord); + if (coord_set_to_left(&circa)) { + result = 0; + coord_init_before_item(icoord); + } else { + old_iplug = item_plugin_by_coord(&circa); + result = (old_iplug == new_iplug) && item_can_contain_key(icoord, key, data); + if (result) { + coord_dup(icoord, &circa); + icoord->between = AFTER_UNIT; + } + } + } else if (icoord->between == AFTER_UNIT || icoord->between == AFTER_ITEM) { + coord_dup(&circa, icoord); + /* otherwise, try to glue to the item at the right, if any */ + if (coord_set_to_right(&circa)) { + result = 0; + coord_init_after_item(icoord); + } else { + int (*cck) (const coord_t *, const reiser4_key *, const reiser4_item_data *); + + old_iplug = item_plugin_by_coord(&circa); + + cck = old_iplug->b.can_contain_key; + if (cck == NULL) + /* item doesn't define ->can_contain_key + method? So it is not expandable. */ + result = 0; + else { + result = (old_iplug == new_iplug) && cck(&circa /*icoord */ , key, data); + if (result) { + coord_dup(icoord, &circa); + icoord->between = BEFORE_UNIT; + } + } + } + } else + impossible("nikita-2513", "Nothing works"); + if (result) { + if (icoord->between == BEFORE_ITEM) { + assert("vs-912", icoord->unit_pos == 0); + icoord->between = BEFORE_UNIT; + } else if (icoord->between == AFTER_ITEM) { + coord_init_after_item_end(icoord); + } + } + return result; +} + +/* implements COP_PASTE operation + + Paste data into existing item. This is complicated by the fact that after + we shifted something to the left or right neighbors trying to free some + space, item we were supposed to paste into can be in different node than + insertion coord. If so, we are no longer doing paste, but insert. See + comments in insert_paste_common(). + +*/ +static int +carry_paste(carry_op * op /* operation to be performed */ , + carry_level * doing UNUSED_ARG /* current carry + * level */ , + carry_level * todo /* next carry level */ ) +{ + znode *node; + carry_insert_data cdata; + coord_t dcoord; + reiser4_item_data data; + int result; + int real_size; + item_plugin *iplug; + carry_plugin_info info; + coord_t *coord; + + assert("nikita-982", op != NULL); + assert("nikita-983", todo != NULL); + assert("nikita-984", op->op == COP_PASTE); + + trace_stamp(TRACE_CARRY); + reiser4_stat_level_inc(doing, paste); + + coord_init_zero(&dcoord); + + result = insert_paste_common(op, doing, todo, &cdata, &dcoord, &data); + if (result != 0) + return result; + + coord = op->u.insert.d->coord; + + /* handle case when op -> u.insert.coord doesn't point to the item + of required type. restart as insert. */ + if (!can_paste(coord, op->u.insert.d->key, op->u.insert.d->data)) { + op->op = COP_INSERT; + op->u.insert.type = COPT_PASTE_RESTARTED; + reiser4_stat_level_inc(doing, paste_restarted); + result = op_dispatch_table[COP_INSERT].handler(op, doing, todo); + + return result; + } + + node = coord->node; + iplug = item_plugin_by_coord(coord); + assert("nikita-992", iplug != NULL); + + assert("nikita-985", node != NULL); + assert("nikita-986", node_plugin_by_node(node) != NULL); + + assert("nikita-987", space_needed_for_op(node, op) <= znode_free_space(node)); + + assert("nikita-1286", coord_is_existing_item(coord)); + + /* + * if item is expanded as a result of this operation, we should first + * change item size, than call ->b.paste item method. If item is + * shrunk, it should be done other way around: first call ->b.paste + * method, then reduce item size. + */ + + real_size = space_needed_for_op(node, op); + if (real_size > 0) + node->nplug->change_item_size(coord, real_size); + + doing->restartable = 0; + info.doing = doing; + info.todo = todo; + + result = iplug->b.paste(coord, op->u.insert.d->data, &info); + + if (real_size < 0) + node->nplug->change_item_size(coord, real_size); + + /* if we pasted at the beginning of the item, update item's key. */ + if (coord->unit_pos == 0 && coord->between != AFTER_UNIT) + node->nplug->update_item_key(coord, op->u.insert.d->key, &info); + + znode_make_dirty(node); + return result; +} + +/* handle carry COP_EXTENT operation. */ +static int +carry_extent(carry_op * op /* operation to perform */ , + carry_level * doing /* queue of operations @op + * is part of */ , + carry_level * todo /* queue where new operations + * are accumulated */ ) +{ + znode *node; + carry_insert_data cdata; + coord_t coord; + reiser4_item_data data; + carry_op *delete_dummy; + carry_op *insert_extent; + int result; + carry_plugin_info info; + + assert("nikita-1751", op != NULL); + assert("nikita-1752", todo != NULL); + assert("nikita-1753", op->op == COP_EXTENT); + + trace_stamp(TRACE_CARRY); + reiser4_stat_level_inc(doing, extent); + + /* extent insertion overview: + + extents live on the TWIG LEVEL, which is level one above the leaf + one. This complicates extent insertion logic somewhat: it may + happen (and going to happen all the time) that in logical key + ordering extent has to be placed between items I1 and I2, located + at the leaf level, but I1 and I2 are in the same formatted leaf + node N1. To insert extent one has to + + (1) reach node N1 and shift data between N1, its neighbors and + possibly newly allocated nodes until I1 and I2 fall into different + nodes. Since I1 and I2 are still neighboring items in logical key + order, they will be necessary utmost items in their respective + nodes. + + (2) After this new extent item is inserted into node on the twig + level. + + Fortunately this process can reuse almost all code from standard + insertion procedure (viz. make_space() and insert_paste_common()), + due to the following observation: make_space() only shifts data up + to and excluding or including insertion point. It never + "over-moves" through insertion point. Thus, one can use + make_space() to perform step (1). All required for this is just to + instruct free_space_shortage() to keep make_space() shifting data + until insertion point is at the node border. + + */ + + /* perform common functionality of insert and paste. */ + result = insert_paste_common(op, doing, todo, &cdata, &coord, &data); + if (result != 0) + return result; + + node = op->u.extent.d->coord->node; + assert("nikita-1754", node != NULL); + assert("nikita-1755", node_plugin_by_node(node) != NULL); + assert("nikita-1700", coord_wrt(op->u.extent.d->coord) != COORD_INSIDE); + + /* NOTE-NIKITA add some checks here. Not assertions, -EIO. Check that + extent fits between items. */ + + info.doing = doing; + info.todo = todo; + + /* there is another complication due to placement of extents on the + twig level: extents are "rigid" in the sense that key-range + occupied by extent cannot grow indefinitely to the right as it is + for the formatted leaf nodes. Because of this when search finds two + adjacent extents on the twig level, it has to "drill" to the leaf + level, creating new node. Here we are removing this node. + */ + if (node_is_empty(node)) { + delete_dummy = node_post_carry(&info, COP_DELETE, node, 1); + if (IS_ERR(delete_dummy)) + return PTR_ERR(delete_dummy); + delete_dummy->u.delete.child = NULL; + delete_dummy->u.delete.flags = DELETE_RETAIN_EMPTY; + ZF_SET(node, JNODE_HEARD_BANSHEE); + } + + /* proceed with inserting extent item into parent. We are definitely + inserting rather than pasting if we get that far. */ + insert_extent = node_post_carry(&info, COP_INSERT, node, 1); + if (IS_ERR(insert_extent)) + /* @delete_dummy will be automatically destroyed on the level + exiting */ + return PTR_ERR(insert_extent); + /* NOTE-NIKITA insertion by key is simplest option here. Another + possibility is to insert on the left or right of already existing + item. + */ + insert_extent->u.insert.type = COPT_KEY; + insert_extent->u.insert.d = op->u.extent.d; + assert("nikita-1719", op->u.extent.d->key != NULL); + insert_extent->u.insert.d->data->arg = op->u.extent.d->coord; + insert_extent->u.insert.flags = znode_get_tree(node)->carry.new_extent_flags; + + /* + * if carry was asked to track lock handle we should actually track + * lock handle on the twig node rather than on the leaf where + * operation was started from. Transfer tracked lock handle. + */ + if (doing->track_type) { + assert("nikita-3242", doing->tracked != NULL); + assert("nikita-3244", todo->tracked == NULL); + todo->tracked = doing->tracked; + todo->track_type = CARRY_TRACK_NODE; + doing->tracked = NULL; + doing->track_type = 0; + } + + return 0; +} + +/* update key in @parent between pointers to @left and @right. + + Find coords of @left and @right and update delimiting key between them. + This is helper function called by carry_update(). Finds position of + internal item involved. Updates item key. Updates delimiting keys of child + nodes involved. +*/ +static int +update_delimiting_key(znode * parent /* node key is updated + * in */ , + znode * left /* child of @parent */ , + znode * right /* child of @parent */ , + carry_level * doing /* current carry + * level */ , + carry_level * todo /* parent carry + * level */ , + const char **error_msg /* place to + * store error + * message */ ) +{ + coord_t left_pos; + coord_t right_pos; + int result; + reiser4_key ldkey; + carry_plugin_info info; + + assert("nikita-1177", right != NULL); + /* find position of right left child in a parent */ + result = find_child_ptr(parent, right, &right_pos); + if (result != NS_FOUND) { + *error_msg = "Cannot find position of right child"; + return result; + } + + if ((left != NULL) && !coord_is_leftmost_unit(&right_pos)) { + /* find position of the left child in a parent */ + result = find_child_ptr(parent, left, &left_pos); + if (result != NS_FOUND) { + *error_msg = "Cannot find position of left child"; + return result; + } + assert("nikita-1355", left_pos.node != NULL); + } else + left_pos.node = NULL; + + /* check that they are separated by exactly one key and are basically + sane */ + if (REISER4_DEBUG) { + if ((left_pos.node != NULL) + && !coord_is_existing_unit(&left_pos)) { + *error_msg = "Left child is bastard"; + return RETERR(-EIO); + } + if (!coord_is_existing_unit(&right_pos)) { + *error_msg = "Right child is bastard"; + return RETERR(-EIO); + } + if (left_pos.node != NULL && + !coord_are_neighbors(&left_pos, &right_pos)) { + *error_msg = "Children are not direct siblings"; + return RETERR(-EIO); + } + } + *error_msg = NULL; + + info.doing = doing; + info.todo = todo; + + /* + * If child node is not empty, new key of internal item is a key of + * leftmost item in the child node. If the child is empty, take its + * right delimiting key as a new key of the internal item. Precise key + * in the latter case is not important per se, because the child (and + * the internal item) are going to be killed shortly anyway, but we + * have to preserve correct order of keys in the parent node. + */ + + if (!ZF_ISSET(right, JNODE_HEARD_BANSHEE)) + leftmost_key_in_node(right, &ldkey); + else + UNDER_RW_VOID(dk, znode_get_tree(parent), read, + ldkey = *znode_get_rd_key(right)); + node_plugin_by_node(parent)->update_item_key(&right_pos, &ldkey, &info); + doing->restartable = 0; + znode_make_dirty(parent); + return 0; +} + +/* implements COP_UPDATE opration + + Update delimiting keys. + +*/ +static int +carry_update(carry_op * op /* operation to be performed */ , + carry_level * doing /* current carry level */ , + carry_level * todo /* next carry level */ ) +{ + int result; + carry_node *missing UNUSED_ARG; + znode *left; + znode *right; + carry_node *lchild; + carry_node *rchild; + const char *error_msg; + reiser4_tree *tree; + + /* + * This operation is called to update key of internal item. This is + * necessary when carry shifted of cut data on the child + * level. Arguments of this operation are: + * + * @right --- child node. Operation should update key of internal + * item pointing to @right. + * + * @left --- left neighbor of @right. This parameter is optional. + */ + + assert("nikita-902", op != NULL); + assert("nikita-903", todo != NULL); + assert("nikita-904", op->op == COP_UPDATE); + trace_stamp(TRACE_CARRY); + reiser4_stat_level_inc(doing, update); + + lchild = op->u.update.left; + rchild = op->node; + + if (lchild != NULL) { + assert("nikita-1001", lchild->parent); + assert("nikita-1003", !lchild->left); + left = carry_real(lchild); + } else + left = NULL; + + tree = znode_get_tree(rchild->node); + RLOCK_TREE(tree); + right = znode_parent(rchild->node); + if (REISER4_STATS) { + znode *old_right; + if (rchild != NULL) { + assert("nikita-1000", rchild->parent); + assert("nikita-1002", !rchild->left); + old_right = carry_real(rchild); + } else + old_right = NULL; + if (znode_parent(rchild->node) != old_right) + /* parent node was split, and pointer to @rchild was + inserted/moved into new node. Wonders of balkancing + (sic.). + */ + reiser4_stat_level_inc(doing, half_split_race); + } + RUNLOCK_TREE(tree); + + if (right != NULL) { + result = update_delimiting_key(right, + lchild ? lchild->node : NULL, + rchild->node, + doing, todo, &error_msg); + } else { + error_msg = "Cannot find node to update key in"; + result = RETERR(-EIO); + } + /* operation will be reposted to the next level by the + ->update_item_key() method of node plugin, if necessary. */ + + if (result != 0) { + warning("nikita-999", "Error updating delimiting key: %s (%i)", error_msg ? : "", result); + print_znode("left", left); + print_znode("right", right); + print_znode("lchild", lchild ? lchild->node : NULL); + print_znode("rchild", rchild->node); + } + return result; +} + +/* move items from @node during carry */ +static int +carry_shift_data(sideof side /* in what direction to move data */ , + coord_t * insert_coord /* coord where new item + * is to be inserted */ , + znode * node /* node which data are moved from */ , + carry_level * doing /* active carry queue */ , + carry_level * todo /* carry queue where new + * operations are to be put + * in */ , + unsigned int including_insert_coord_p /* true if + * @insertion_coord + * can be moved */ ) +{ + int result; + znode *source; + carry_plugin_info info; + node_plugin *nplug; + + source = insert_coord->node; + + info.doing = doing; + info.todo = todo; + + nplug = node_plugin_by_node(node); + result = nplug->shift(insert_coord, node, + (side == LEFT_SIDE) ? SHIFT_LEFT : SHIFT_RIGHT, 0, + (int) including_insert_coord_p, &info); + /* the only error ->shift() method of node plugin can return is + -ENOMEM due to carry node/operation allocation. */ + assert("nikita-915", result >= 0 || result == -ENOMEM); + if (result > 0) { + /* + * if some number of bytes was actually shifted, mark nodes + * dirty, and carry level as non-restartable. + */ + doing->restartable = 0; + znode_make_dirty(source); + znode_make_dirty(node); + } + + assert("nikita-2077", coord_check(insert_coord)); + return 0; +} + +typedef carry_node *(*carry_iterator) (carry_node * node); +static carry_node *find_dir_carry(carry_node * node, carry_level * level, carry_iterator iterator); + +/* look for the left neighbor of given carry node in a carry queue. + + This is used by find_left_neighbor(), but I am not sure that this + really gives any advantage. More statistics required. + +*/ +reiser4_internal carry_node * +find_left_carry(carry_node * node /* node to fine left neighbor + * of */ , + carry_level * level /* level to scan */ ) +{ + return find_dir_carry(node, level, (carry_iterator) pool_level_list_prev); +} + +/* look for the right neighbor of given carry node in a + carry queue. + + This is used by find_right_neighbor(), but I am not sure that this + really gives any advantage. More statistics required. + +*/ +reiser4_internal carry_node * +find_right_carry(carry_node * node /* node to fine right neighbor + * of */ , + carry_level * level /* level to scan */ ) +{ + return find_dir_carry(node, level, (carry_iterator) pool_level_list_next); +} + +/* look for the left or right neighbor of given carry node in a carry + queue. + + Helper function used by find_{left|right}_carry(). +*/ +static carry_node * +find_dir_carry(carry_node * node /* node to start scanning + * from */ , + carry_level * level /* level to scan */ , + carry_iterator iterator /* operation to + * move to the next + * node */ ) +{ + carry_node *neighbor; + + assert("nikita-1059", node != NULL); + assert("nikita-1060", level != NULL); + + /* scan list of carry nodes on this list dir-ward, skipping all + carry nodes referencing the same znode. */ + neighbor = node; + while (1) { + neighbor = iterator(neighbor); + if (pool_level_list_end(&level->nodes, &neighbor->header)) + return NULL; + if (carry_real(neighbor) != carry_real(node)) + return neighbor; + } +} + +/* + * Memory reservation estimation. + * + * Carry process proceeds through tree levels upwards. Carry assumes that it + * takes tree in consistent state (e.g., that search tree invariants hold), + * and leaves tree consistent after it finishes. This means that when some + * error occurs carry cannot simply return if there are pending carry + * operations. Generic solution for this problem is carry-undo either as + * transaction manager feature (requiring checkpoints and isolation), or + * through some carry specific mechanism. + * + * Our current approach is to panic if carry hits an error while tree is + * inconsistent. Unfortunately -ENOMEM can easily be triggered. To work around + * this "memory reservation" mechanism was added. + * + * Memory reservation is implemented by perthread-pages.diff patch from + * core-patches. Its API is defined in + * + * int perthread_pages_reserve(int nrpages, int gfp); + * void perthread_pages_release(int nrpages); + * int perthread_pages_count(void); + * + * carry estimates its worst case memory requirements at the entry, reserved + * enough memory, and released unused pages before returning. + * + * Code below estimates worst case memory requirements for a given carry + * queue. This is dome by summing worst case memory requirements for each + * operation in the queue. + * + */ + +/* + * Memory memory requirements of many operations depends on the tree + * height. For example, item insertion requires new node to be inserted at + * each tree level in the worst case. What tree height should be used for + * estimation? Current tree height is wrong, because tree height can change + * between the time when estimation was done and the time when operation is + * actually performed. Maximal possible tree height (REISER4_MAX_ZTREE_HEIGHT) + * is also not desirable, because it would lead to the huge over-estimation + * all the time. Plausible solution is "capped tree height": if current tree + * height is less than some TREE_HEIGHT_CAP constant, capped tree height is + * TREE_HEIGHT_CAP, otherwise it's current tree height. Idea behind this is + * that if tree height is TREE_HEIGHT_CAP or larger, it's extremely unlikely + * to be increased even more during short interval of time. + */ +#define TREE_HEIGHT_CAP (5) + +/* return capped tree height for the @tree. See comment above. */ +static int +cap_tree_height(reiser4_tree * tree) +{ + return max_t(int, tree->height, TREE_HEIGHT_CAP); +} + +/* return capped tree height for the current tree. */ +static int capped_height(void) +{ + return cap_tree_height(current_tree); +} + +/* return number of pages required to store given number of bytes */ +static int bytes_to_pages(int bytes) +{ + return (bytes + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; +} + +/* how many pages are required to allocate znodes during item insertion. */ +static int +carry_estimate_znodes(void) +{ + /* + * Note, that there we have some problem here: there is no way to + * reserve pages specifically for the given slab. This means that + * these pages can be hijacked for some other end. + */ + + /* in the worst case we need 3 new znode on each tree level */ + return bytes_to_pages(capped_height() * sizeof(znode) * 3); +} + +/* + * how many pages are required to load bitmaps. One bitmap per level. + */ +static int +carry_estimate_bitmaps(void) +{ + if (reiser4_is_set(reiser4_get_current_sb(), REISER4_DONT_LOAD_BITMAP)) { + int bytes; + + bytes = capped_height() * + (0 + /* bnode should be added, but its is private to + * bitmap.c, skip for now. */ + 2 * sizeof(jnode)); /* working and commit jnodes */ + return bytes_to_pages(bytes) + 2; /* and their contents */ + } else + /* bitmaps were pre-loaded during mount */ + return 0; +} + +/* worst case item insertion memory requirements */ +static int +carry_estimate_insert(carry_op * op, carry_level * level) +{ + return + carry_estimate_bitmaps() + + carry_estimate_znodes() + + 1 + /* new atom */ + capped_height() + /* new block on each level */ + 1 + /* and possibly extra new block at the leaf level */ + 3; /* loading of leaves into memory */ +} + +/* worst case item deletion memory requirements */ +static int +carry_estimate_delete(carry_op * op, carry_level * level) +{ + return + carry_estimate_bitmaps() + + carry_estimate_znodes() + + 1 + /* new atom */ + 3; /* loading of leaves into memory */ +} + +/* worst case tree cut memory requirements */ +static int +carry_estimate_cut(carry_op * op, carry_level * level) +{ + return + carry_estimate_bitmaps() + + carry_estimate_znodes() + + 1 + /* new atom */ + 3; /* loading of leaves into memory */ +} + +/* worst case memory requirements of pasting into item */ +static int +carry_estimate_paste(carry_op * op, carry_level * level) +{ + return + carry_estimate_bitmaps() + + carry_estimate_znodes() + + 1 + /* new atom */ + capped_height() + /* new block on each level */ + 1 + /* and possibly extra new block at the leaf level */ + 3; /* loading of leaves into memory */ +} + +/* worst case memory requirements of extent insertion */ +static int +carry_estimate_extent(carry_op * op, carry_level * level) +{ + return + carry_estimate_insert(op, level) + /* insert extent */ + carry_estimate_delete(op, level); /* kill leaf */ +} + +/* worst case memory requirements of key update */ +static int +carry_estimate_update(carry_op * op, carry_level * level) +{ + return 0; +} + +/* worst case memory requirements of flow insertion */ +static int +carry_estimate_insert_flow(carry_op * op, carry_level * level) +{ + int newnodes; + + newnodes = min(bytes_to_pages(op->u.insert_flow.flow->length), + CARRY_FLOW_NEW_NODES_LIMIT); + /* + * roughly estimate insert_flow as a sequence of insertions. + */ + return newnodes * carry_estimate_insert(op, level); +} + +/* This is dispatch table for carry operations. It can be trivially + abstracted into useful plugin: tunable balancing policy is a good + thing. */ +reiser4_internal carry_op_handler op_dispatch_table[COP_LAST_OP] = { + [COP_INSERT] = { + .handler = carry_insert, + .estimate = carry_estimate_insert + }, + [COP_DELETE] = { + .handler = carry_delete, + .estimate = carry_estimate_delete + }, + [COP_CUT] = { + .handler = carry_cut, + .estimate = carry_estimate_cut + }, + [COP_PASTE] = { + .handler = carry_paste, + .estimate = carry_estimate_paste + }, + [COP_EXTENT] = { + .handler = carry_extent, + .estimate = carry_estimate_extent + }, + [COP_UPDATE] = { + .handler = carry_update, + .estimate = carry_estimate_update + }, + [COP_INSERT_FLOW] = { + .handler = carry_insert_flow, + .estimate = carry_estimate_insert_flow + } +}; + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/carry_ops.h 2004-09-03 00:57:04.989280776 -0700 @@ -0,0 +1,41 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* implementation of carry operations. See carry_ops.c for details. */ + +#if !defined( __CARRY_OPS_H__ ) +#define __CARRY_OPS_H__ + +#include "forward.h" +#include "znode.h" +#include "carry.h" + +/* carry operation handlers */ +typedef struct carry_op_handler { + /* perform operation */ + int (*handler) (carry_op * op, carry_level * doing, carry_level * todo); + /* estimate memory requirements for @op */ + int (*estimate) (carry_op * op, carry_level * level); +} carry_op_handler; + +/* This is dispatch table for carry operations. It can be trivially + abstracted into useful plugin: tunable balancing policy is a good + thing. */ +extern carry_op_handler op_dispatch_table[COP_LAST_OP]; + +unsigned int space_needed(const znode * node, const coord_t * coord, const reiser4_item_data * data, int inserting); +extern carry_node *find_left_carry(carry_node * node, carry_level * level); +extern carry_node *find_right_carry(carry_node * node, carry_level * level); + +/* __CARRY_OPS_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/cluster.c 2004-09-03 00:57:04.990280624 -0700 @@ -0,0 +1,71 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Contains cluster operations for cryptcompress object plugin (see + http://www.namesys.com/cryptcompress_design.txt for details). */ + +/* Concepts of clustering. Definition of cluster size. + Data clusters, page clusters, disk clusters. + + + In order to compress plain text we first should split it into chunks. + Then we process each chunk independently by the following function: + + void alg(char *input_ptr, int input_length, char *output_ptr, int *output_length); + + where: + input_ptr is a pointer to the first byte of input chunk (that contains plain text), + input_len is a length of input chunk, + output_ptr is a pointer to the first byte of output chunk (that contains processed text), + *output_len is a length of output chunk. + + the length of output chunk depends both on input_len and on the content of + input chunk. input_len (which can be assigned an arbitrary value) affects the + compression quality (the more input_len the better the compression quality). + For each cryptcompress file we assign special attribute - cluster size: + + Cluster size is a file attribute, which determines the maximal size + of input chunk that we use for compression. + + So if we wanna compress a 10K-file with a cluster size of 4K, we split this file + into three chunks (first and second - 4K, third - 2K). Those chunks are + clusters in the space of file offsets (data clusters). + + Cluster sizes are represented as (PAGE_CACHE_SIZE << shift), where + shift (= 0, 1, 2,... ). You'll note that this representation + affects the allowed values for cluster size. This is stored in + disk stat-data (CLUSTER_STAT, layout is in reiser4_cluster_stat (see + (plugin/item/static_stat.h) for details). + Note that working with + cluster_size > PAGE_SIZE (when cluster_shift > 0, and cluster contains more + then one page) is suboptimal because before compression we should assemble + all cluster pages into one flow (this means superfluous memcpy during + read/write). So the better way to increase cluster size (and therefore + compression quality) is making PAGE_SIZE larger (for instance by page + clustering stuff of William Lee). But if you need PAGE_SIZE < cluster_size, + then use the page clustering offered by reiser4. + + The inode mapping of a cryptcompress file contains pages filled by plain text. + Cluster size also defines clustering in address space. For example, + 101K-file with cluster size 16K (cluster shift = 2), which can be mapped + into 26 pages, has 7 "page clusters": first six clusters contains 4 pages + and one cluster contains 2 pages (for the file tail). + + We split each output (compressed) chunk into special items to provide + tight packing of data on disk (currently only ctails hold compressed data). + This set of items we call a "disk cluster". + + Each cluster is defined (like pages are) by its index (e.g. offset, + but the unit is cluster size instead of PAGE_SIZE). Key offset of + the first unit of the first item of each disk cluster (we call this a + "key of disk cluster") is a multiple of the cluster index. + + All read/write/truncate operations are performed upon clusters. + For example, if we wanna read 40K of a cryptcompress file with cluster size 16K + from offset = 20K, we first need to read two clusters (of indexes 1, 2). This + means that all main methods of cryptcompress object plugin call appropriate + cluster operation. + + For the same index we use one structure (type reiser4_cluster_t) to + represent all data/page/disk clusters. (EDWARD-FIXME-HANS: are you + sure that is good style? and where is the code that goes with this comment....;-) ) +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/cluster.h 2004-09-03 00:57:04.991280472 -0700 @@ -0,0 +1,182 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* This file contains page/cluster index translators and offset modulators + See http://www.namesys.com/cryptcompress_design.html for details */ + +#if !defined( __FS_REISER4_CLUSTER_H__ ) +#define __FS_REISER4_CLUSTER_H__ + +static inline loff_t min_count(loff_t a, loff_t b) +{ + return (a < b ? a : b); +} + +static inline __u8 inode_cluster_shift (struct inode * inode) +{ + assert("edward-92", inode != NULL); + assert("edward-93", reiser4_inode_data(inode) != NULL); + assert("edward-94", inode_get_flag(inode, REISER4_CLUSTER_KNOWN)); + + return reiser4_inode_data(inode)->cluster_shift; +} + +/* returns number of pages in the cluster */ +static inline int inode_cluster_pages (struct inode * inode) +{ + return (1 << inode_cluster_shift(inode)); +} + +static inline size_t inode_cluster_size (struct inode * inode) +{ + assert("edward-96", inode != NULL); + + return (PAGE_CACHE_SIZE << inode_cluster_shift(inode)); +} + +static inline unsigned long +pg_to_clust(unsigned long idx, struct inode * inode) +{ + return idx >> inode_cluster_shift(inode); +} + +static inline unsigned long +clust_to_pg(unsigned long idx, struct inode * inode) +{ + return idx << inode_cluster_shift(inode); +} + +static inline unsigned long +pg_to_clust_to_pg(unsigned long idx, struct inode * inode) +{ + return clust_to_pg(pg_to_clust(idx, inode), inode); +} + +static inline unsigned long +off_to_pg(loff_t off) +{ + return (off >> PAGE_CACHE_SHIFT); +} + +static inline loff_t +pg_to_off(unsigned long idx) +{ + return ((loff_t)(idx) << PAGE_CACHE_SHIFT); +} + +static inline unsigned long +off_to_clust(loff_t off, struct inode * inode) +{ + return pg_to_clust(off_to_pg(off), inode); +} + +static inline loff_t +clust_to_off(unsigned long idx, struct inode * inode) +{ + return pg_to_off(clust_to_pg(idx, inode)); +} + +static inline loff_t +off_to_clust_to_off(loff_t off, struct inode * inode) +{ + return clust_to_off(off_to_clust(off, inode), inode); +} + +static inline unsigned long +off_to_clust_to_pg(loff_t off, struct inode * inode) +{ + return clust_to_pg(off_to_clust(off, inode), inode); +} + +static inline unsigned +off_to_pgoff(loff_t off) +{ + return off & (PAGE_CACHE_SIZE - 1); +} + +static inline unsigned +off_to_cloff(loff_t off, struct inode * inode) +{ + return off & ((loff_t)(inode_cluster_size(inode)) - 1); +} + +static inline unsigned +pg_to_off_to_cloff(unsigned long idx, struct inode * inode) +{ + return off_to_cloff(pg_to_off(idx), inode); +} + +/* if @size != 0, returns index of the page + which contains the last byte of the file */ +static inline pgoff_t +size_to_pg(loff_t size) +{ + return (size ? off_to_pg(size - 1) : 0); +} + +/* minimal index of the page which doesn't contain + file data */ +static inline pgoff_t +size_to_next_pg(loff_t size) +{ + return (size ? off_to_pg(size - 1) + 1 : 0); +} + +static inline unsigned +off_to_pgcount(loff_t off, unsigned long idx) +{ + if (idx > off_to_pg(off)) + return 0; + if (idx < off_to_pg(off)) + return PAGE_CACHE_SIZE; + return off_to_pgoff(off); +} + +static inline unsigned +off_to_count(loff_t off, unsigned long idx, struct inode * inode) +{ + if (idx > off_to_clust(off, inode)) + return 0; + if (idx < off_to_clust(off, inode)) + return inode_cluster_size(inode); + return off_to_cloff(off, inode); +} + +static inline unsigned +fsize_to_count(reiser4_cluster_t * clust, struct inode * inode) +{ + assert("edward-288", clust != NULL); + assert("edward-289", inode != NULL); + + return off_to_count(inode->i_size, clust->index, inode); +} + +static inline int +alloc_clust_pages(reiser4_cluster_t * clust, struct inode * inode ) +{ + assert("edward-791", clust != NULL); + assert("edward-792", inode != NULL); + clust->pages = reiser4_kmalloc(sizeof(*clust->pages) << inode_cluster_shift(inode), GFP_KERNEL); + if (!clust->pages) + return -ENOMEM; + return 0; +} + +static inline void +free_clust_pages(reiser4_cluster_t * clust) +{ + reiser4_kfree(clust->pages); +} + +#endif /* __FS_REISER4_CLUSTER_H__ */ + + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/context.c 2004-09-03 00:57:07.112957928 -0700 @@ -0,0 +1,324 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Manipulation of reiser4_context */ + +/* + * global context used during system call. Variable of this type is allocated + * on the stack at the beginning of the reiser4 part of the system call and + * pointer to it is stored in the current->fs_context. This allows us to avoid + * passing pointer to current transaction and current lockstack (both in + * one-to-one mapping with threads) all over the call chain. + * + * It's kind of like those global variables the prof used to tell you not to + * use in CS1, except thread specific.;-) Nikita, this was a good idea. + * + * In some situations it is desirable to have ability to enter reiser4_context + * more than once for the same thread (nested contexts). For example, there + * are some functions that can be called either directly from VFS/VM or from + * already active reiser4 context (->writepage, for example). + * + * In such situations "child" context acts like dummy: all activity is + * actually performed in the top level context, and get_current_context() + * always returns top level context. Of course, init_context()/done_context() + * have to be properly nested any way. + * + * Note that there is an important difference between reiser4 uses + * ->fs_context and the way other file systems use it. Other file systems + * (ext3 and reiserfs) use ->fs_context only for the duration of _transaction_ + * (this is why ->fs_context was initially called ->journal_info). This means, + * that when ext3 or reiserfs finds that ->fs_context is not NULL on the entry + * to the file system, they assume that some transaction is already underway, + * and usually bail out, because starting nested transaction would most likely + * lead to the deadlock. This gives false positives with reiser4, because we + * set ->fs_context before starting transaction. + */ + +#include "debug.h" +#include "super.h" +#include "context.h" + +#include /* balance_dirty_pages() */ +#include + +#if REISER4_DEBUG_CONTEXTS +/* List of all currently active contexts, used for debugging purposes. */ +context_list_head active_contexts; +/* lock protecting access to active_contexts. */ +spinlock_t active_contexts_lock; + +void +check_contexts(void) +{ + reiser4_context *ctx; + + spin_lock(&active_contexts_lock); + for_all_type_safe_list(context, &active_contexts, ctx) { + assert("vs-$BIGNUM", ctx->magic == context_magic); + } + spin_unlock(&active_contexts_lock); +} +/* REISER4_DEBUG_CONTEXTS */ +#endif + +/* initialise context and bind it to the current thread + + This function should be called at the beginning of reiser4 part of + syscall. +*/ +reiser4_internal int +init_context(reiser4_context * context /* pointer to the reiser4 context + * being initalised */ , + struct super_block *super /* super block we are going to + * work with */) +{ + assert("nikita-2662", !in_interrupt() && !in_irq()); + assert("nikita-3356", context != NULL); + assert("nikita-3357", super != NULL); + assert("nikita-3358", super->s_op == NULL || is_reiser4_super(super)); + + xmemset(context, 0, sizeof *context); + + if (is_in_reiser4_context()) { + reiser4_context *parent; + + parent = (reiser4_context *) current->journal_info; + /* NOTE-NIKITA this is dubious */ + if (parent->super == super) { + context->parent = parent; +#if (REISER4_DEBUG) + ++context->parent->nr_children; +#endif + return 0; + } + } + + context->super = super; + context->magic = context_magic; + context->outer = current->journal_info; + current->journal_info = (void *) context; + + init_lock_stack(&context->stack); + + txn_begin(context); + + context->parent = context; + tap_list_init(&context->taps); +#if REISER4_DEBUG +#if REISER4_DEBUG_CONTEXTS + context_list_clean(context); /* to satisfy assertion */ + spin_lock(&active_contexts_lock); + context_list_check(&active_contexts); + context_list_push_front(&active_contexts, context); + /*check_contexts();*/ + spin_unlock(&active_contexts_lock); +#endif + context->task = current; +#endif + grab_space_enable(); + return 0; +} + +/* cast lock stack embedded into reiser4 context up to its container */ +reiser4_internal reiser4_context * +get_context_by_lock_stack(lock_stack * owner) +{ + return container_of(owner, reiser4_context, stack); +} + +/* true if there is already _any_ reiser4 context for the current thread */ +reiser4_internal int +is_in_reiser4_context(void) +{ + reiser4_context *ctx; + + ctx = current->journal_info; + return + ctx != NULL && + ((unsigned long) ctx->magic) == context_magic; +} + +/* + * call balance dirty pages for the current context. + * + * File system is expected to call balance_dirty_pages_ratelimited() whenever + * it dirties a page. reiser4 does this for unformatted nodes (that is, during + * write---this covers vast majority of all dirty traffic), but we cannot do + * this immediately when formatted node is dirtied, because long term lock is + * usually held at that time. To work around this, dirtying of formatted node + * simply increases ->nr_marked_dirty counter in the current reiser4 + * context. When we are about to leave this context, + * balance_dirty_pages_ratelimited() is called, if necessary. + * + * This introduces another problem: sometimes we do not want to run + * balance_dirty_pages_ratelimited() when leaving a context, for example + * because some important lock (like ->i_sem on the parent directory) is + * held. To achieve this, ->nobalance flag can be set in the current context. + */ +static void +balance_dirty_pages_at(reiser4_context * context) +{ + reiser4_super_info_data * sbinfo = get_super_private(context->super); + + /* + * call balance_dirty_pages_ratelimited() to process formatted nodes + * dirtied during this system call. + */ + if (context->nr_marked_dirty != 0 && /* were any nodes dirtied? */ + /* aren't we called early during mount? */ + sbinfo->fake && + /* don't call balance dirty pages from ->writepage(): it's + * deadlock prone */ + !(current->flags & PF_MEMALLOC) && + /* and don't stall pdflush */ + !current_is_pdflush()) + balance_dirty_pages_ratelimited(sbinfo->fake->i_mapping); +} + +/* + * exit reiser4 context. Call balance_dirty_pages_at() if necessary. Close + * transaction. Call done_context() to do context related book-keeping. + */ +reiser4_internal void reiser4_exit_context(reiser4_context * context) +{ + assert("nikita-3021", schedulable()); + + if (context == context->parent) { + if (!context->nobalance) { + txn_restart(context); + balance_dirty_pages_at(context); + } + txn_end(context); + } + done_context(context); +} + +/* release resources associated with context. + + This function should be called at the end of "session" with reiser4, + typically just before leaving reiser4 driver back to VFS. + + This is good place to put some degugging consistency checks, like that + thread released all locks and closed transcrash etc. + +*/ +reiser4_internal void +done_context(reiser4_context * context /* context being released */) +{ + reiser4_context *parent; + assert("nikita-860", context != NULL); + + parent = context->parent; + assert("nikita-2174", parent != NULL); + assert("nikita-2093", parent == parent->parent); + assert("nikita-859", parent->magic == context_magic); + assert("vs-646", (reiser4_context *) current->journal_info == parent); + assert("zam-686", !in_interrupt() && !in_irq()); + + /* only do anything when leaving top-level reiser4 context. All nested + * contexts are just dummies. */ + if (parent == context) { + assert("jmacd-673", parent->trans == NULL); + assert("jmacd-1002", lock_stack_isclean(&parent->stack)); + assert("nikita-1936", no_counters_are_held()); + assert("nikita-3403", !delayed_inode_updates(context->dirty)); + assert("nikita-2626", tap_list_empty(taps_list())); + assert("zam-1004", get_super_private(context->super)->delete_sema_owner != current); + + /* release all grabbed but as yet unused blocks */ + if (context->grabbed_blocks != 0) + all_grabbed2free(); + + /* + * synchronize against longterm_unlock_znode(): + * wake_up_requestor() wakes up requestors without holding + * zlock (otherwise they will immediately bump into that lock + * after wake up on another CPU). To work around (rare) + * situation where requestor has been woken up asynchronously + * and managed to run until completion (and destroy its + * context and lock stack) before wake_up_requestor() called + * wake_up() on it, wake_up_requestor() synchronize on lock + * stack spin lock. It has actually been observed that spin + * lock _was_ locked at this point, because + * wake_up_requestor() took interrupt. + */ + spin_lock_stack(&context->stack); + spin_unlock_stack(&context->stack); + +#if REISER4_DEBUG_CONTEXTS + /* remove from active contexts */ + spin_lock(&active_contexts_lock); + /*check_contexts();*/ + context_list_remove(parent); + spin_unlock(&active_contexts_lock); +#endif + assert("zam-684", context->nr_children == 0); + /* restore original ->fs_context value */ + current->journal_info = context->outer; + } else { +#if REISER4_DEBUG + parent->nr_children--; + assert("zam-685", parent->nr_children >= 0); +#endif + } +} + +/* Initialize list of all contexts */ +reiser4_internal int +init_context_mgr(void) +{ +#if REISER4_DEBUG_CONTEXTS + spin_lock_init(&active_contexts_lock); + context_list_init(&active_contexts); +#endif + return 0; +} + +#if REISER4_DEBUG_OUTPUT +/* debugging function: output reiser4 context contexts in the human readable + * form */ +reiser4_internal void +print_context(const char *prefix, reiser4_context * context) +{ + if (context == NULL) { + printk("%s: null context\n", prefix); + return; + } +#if REISER4_TRACE + printk("%s: trace_flags: %x\n", prefix, context->trace_flags); +#endif + print_lock_counters("\tlocks", &context->locks); +#if REISER4_DEBUG + printk("pid: %i, comm: %s\n", context->task->pid, context->task->comm); +#endif + print_lock_stack("\tlock stack", &context->stack); + info_atom("\tatom", context->trans_in_ctx.atom); +} + +#if REISER4_DEBUG_CONTEXTS +/* debugging: dump contents of all active contexts */ +void +print_contexts(void) +{ + reiser4_context *context; + + spin_lock(&active_contexts_lock); + + for_all_type_safe_list(context, &active_contexts, context) { + print_context("context", context); + } + + spin_unlock(&active_contexts_lock); +} +#endif +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/context.h 2004-09-03 00:57:07.113957776 -0700 @@ -0,0 +1,311 @@ +/* Copyright 2001, 2002, 2003, 2004 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Reiser4 context. See context.c for details. */ + +#if !defined( __REISER4_CONTEXT_H__ ) +#define __REISER4_CONTEXT_H__ + +#include "forward.h" +#include "debug.h" +#include "spin_macros.h" +#include "dformat.h" +#include "type_safe_list.h" +#include "tap.h" +#include "lock.h" + +#include /* for __u?? */ +#include /* for struct super_block */ +#include +#include /* for struct task_struct */ + +/* list of active lock stacks */ +#if REISER4_DEBUG_CONTEXTS +TYPE_SAFE_LIST_DECLARE(context); +#endif + +ON_DEBUG(TYPE_SAFE_LIST_DECLARE(flushers);) + +#if REISER4_DEBUG + +/* + * Stat-data update tracking. + * + * Some reiser4 functions (reiser4_{del,add}_nlink() take an additional + * parameter indicating whether stat-data update should be performed. This is + * because sometimes fields of the same inode are modified several times + * during single system and updating stat-data (which implies tree lookup and, + * sometimes, tree balancing) on each inode modification is too expensive. To + * avoid unnecessary stat-data updates, we pass flag to not update it during + * inode field updates, and update it manually at the end of the system call. + * + * This introduces a possibility of "missed stat data update" when final + * stat-data update is not performed in some code path. To detect and track + * down such situations following code was developed. + * + * dirty_inode_info is an array of slots. Each slot keeps information about + * "delayed stat data update", that is about a call to a function modifying + * inode field that was instructed to not update stat data. Direct call to + * reiser4_update_sd() clears corresponding slot. On leaving reiser4 context + * all slots are scanned and information about still not forced updates is + * printed. + */ + +/* how many delayed stat data update slots to remember */ +#define TRACKED_DELAYED_UPDATE (0) + +typedef struct { + ino_t ino; /* inode number of object with delayed stat data + * update */ + int delayed; /* 1 if update is delayed, 0 if update for forced */ + void *stack[4]; /* stack back-trace of the call chain where update was + * delayed */ +} dirty_inode_info[TRACKED_DELAYED_UPDATE]; + +extern void mark_inode_update(struct inode *object, int immediate); +extern int delayed_inode_updates(dirty_inode_info info); + +#else + +typedef struct {} dirty_inode_info; + +#define mark_inode_update(object, immediate) noop +#define delayed_inode_updates(info) noop + +#endif + +/* reiser4 per-thread context */ +struct reiser4_context { + /* magic constant. For identification of reiser4 contexts. */ + __u32 magic; + + /* current lock stack. See lock.[ch]. This is where list of all + locks taken by current thread is kept. This is also used in + deadlock detection. */ + lock_stack stack; + + /* current transcrash. */ + txn_handle *trans; + /* transaction handle embedded into reiser4_context. ->trans points + * here by default. */ + txn_handle trans_in_ctx; + + /* super block we are working with. To get the current tree + use &get_super_private (reiser4_get_current_sb ())->tree. */ + struct super_block *super; + + /* parent fs activation */ + struct fs_activation *outer; + + /* per-thread grabbed (for further allocation) blocks counter */ + reiser4_block_nr grabbed_blocks; + + /* parent context */ + reiser4_context *parent; + + /* list of taps currently monitored. See tap.c */ + tap_list_head taps; + + /* grabbing space is enabled */ + int grab_enabled :1; + /* should be set when we are write dirty nodes to disk in jnode_flush or + * reiser4_write_logs() */ + int writeout_mode :1; + /* true, if current thread is an ent thread */ + int entd :1; + /* true, if balance_dirty_pages() should not be run when leaving this + * context. This is used to avoid lengthly balance_dirty_pages() + * operation when holding some important resource, like directory + * ->i_sem */ + int nobalance :1; + + /* count non-trivial jnode_set_dirty() calls */ + unsigned long nr_marked_dirty; +#if REISER4_DEBUG + /* A link of all active contexts. */ + context_list_link contexts_link; + /* debugging information about reiser4 locks held by the current + * thread */ + lock_counters_info locks; + int nr_children; /* number of child contexts */ + struct task_struct *task; /* so we can easily find owner of the stack */ + + /* + * disk space grabbing debugging support + */ + /* how many disk blocks were grabbed by the first call to + * reiser4_grab_space() in this context */ + reiser4_block_nr grabbed_initially; + /* stack back-trace of the first call to reiser4_grab_space() in this + * context */ + backtrace_path grabbed_at; + + /* list of all threads doing flush currently */ + flushers_list_link flushers_link; + /* information about last error encountered by reiser4 */ + err_site err; + /* information about delayed stat data updates. See above. */ + dirty_inode_info dirty; +#endif + +#if REISER4_TRACE + /* per-thread tracing flags. Use reiser4_trace_flags enum to set + bits in it. */ + __u32 trace_flags; +#endif +#if REISER4_DEBUG_NODE + /* + * don't perform node consistency checks while this is greater than + * zero. Used during operations that temporary violate node + * consistency. + */ + int disable_node_check; +#endif +}; + +#if REISER4_DEBUG_CONTEXTS +TYPE_SAFE_LIST_DEFINE(context, reiser4_context, contexts_link); +#endif +#if REISER4_DEBUG +TYPE_SAFE_LIST_DEFINE(flushers, reiser4_context, flushers_link); +#endif + +extern reiser4_context *get_context_by_lock_stack(lock_stack *); + +/* Debugging helps. */ +extern int init_context_mgr(void); +#if REISER4_DEBUG_OUTPUT +extern void print_context(const char *prefix, reiser4_context * ctx); +#else +#define print_context(p,c) noop +#endif + +#if REISER4_DEBUG_CONTEXTS && REISER4_DEBUG_OUTPUT +extern void print_contexts(void); +#else +#define print_contexts() noop +#endif + +#if REISER4_DEBUG_CONTEXTS +extern void check_contexts(void); +#else +#define check_contexts() noop +#endif + +#define current_tree (&(get_super_private(reiser4_get_current_sb())->tree)) +#define current_blocksize reiser4_get_current_sb()->s_blocksize +#define current_blocksize_bits reiser4_get_current_sb()->s_blocksize_bits + +extern int init_context(reiser4_context * context, struct super_block *super); +extern void done_context(reiser4_context * context); + +/* magic constant we store in reiser4_context allocated at the stack. Used to + catch accesses to staled or uninitialized contexts. */ +#define context_magic ((__u32) 0x4b1b5d0b) + +extern int is_in_reiser4_context(void); + +/* + * return reiser4_context for the thread @tsk + */ +static inline reiser4_context * +get_context(const struct task_struct *tsk) +{ + assert("vs-1682", ((reiser4_context *) tsk->journal_info)->magic == context_magic); + return (reiser4_context *) tsk->journal_info; +} + +/* + * return reiser4 context of the current thread, or NULL if there is none. + */ +static inline reiser4_context * +get_current_context_check(void) +{ + if (is_in_reiser4_context()) + return get_context(current); + else + return NULL; +} + +static inline reiser4_context * get_current_context(void);/* __attribute__((const));*/ + +/* return context associated with current thread */ +static inline reiser4_context * +get_current_context(void) +{ + return get_context(current); +} + +/* + * true if current thread is in the write-out mode. Thread enters write-out + * mode during jnode_flush and reiser4_write_logs(). + */ +static inline int is_writeout_mode(void) +{ + return get_current_context()->writeout_mode; +} + +/* + * enter write-out mode + */ +static inline void writeout_mode_enable(void) +{ + assert("zam-941", !get_current_context()->writeout_mode); + get_current_context()->writeout_mode = 1; +} + +/* + * leave write-out mode + */ +static inline void writeout_mode_disable(void) +{ + assert("zam-942", get_current_context()->writeout_mode); + get_current_context()->writeout_mode = 0; +} + +static inline void grab_space_enable(void) +{ + get_current_context()->grab_enabled = 1; +} + +static inline void grab_space_disable(void) +{ + get_current_context()->grab_enabled = 0; +} + +static inline void grab_space_set_enabled (int enabled) +{ + get_current_context()->grab_enabled = enabled; +} + +static inline int is_grab_enabled(reiser4_context *ctx) +{ + return ctx->grab_enabled; +} + +/* mark transaction handle in @ctx as TXNH_DONT_COMMIT, so that no commit or + * flush would be performed when it is closed. This is necessary when handle + * has to be closed under some coarse semaphore, like i_sem of + * directory. Commit will be performed by ktxnmgrd. */ +static inline void context_set_commit_async(reiser4_context * context) +{ + context = context->parent; + context->nobalance = 1; + context->trans->flags |= TXNH_DONT_COMMIT; +} + +extern void reiser4_exit_context(reiser4_context * context); + +/* __REISER4_CONTEXT_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/coord.c 2004-09-03 00:57:07.494899864 -0700 @@ -0,0 +1,1003 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#include "forward.h" +#include "debug.h" +#include "dformat.h" +#include "tree.h" +#include "plugin/item/item.h" +#include "znode.h" +#include "coord.h" + +/* Internal constructor. */ +static inline void +coord_init_values(coord_t *coord, const znode *node, pos_in_node_t item_pos, + pos_in_node_t unit_pos, between_enum between) +{ + coord->node = (znode *) node; + coord_set_item_pos(coord, item_pos); + coord->unit_pos = unit_pos; + coord->between = between; + ON_DEBUG(coord->plug_v = 0); + ON_DEBUG(coord->body_v = 0); + + /*ON_TRACE (TRACE_COORDS, "init coord %p node %p: %u %u %s\n", coord, node, item_pos, unit_pos, coord_tween_tostring (between)); */ +} + +/* after shifting of node content, coord previously set properly may become + invalid, try to "normalize" it. */ +reiser4_internal void +coord_normalize(coord_t *coord) +{ + znode *node; + + node = coord->node; + assert("vs-683", node); + + coord_clear_iplug(coord); + + if (node_is_empty(node)) { + coord_init_first_unit(coord, node); + } else if ((coord->between == AFTER_ITEM) || (coord->between == AFTER_UNIT)) { + return; + } else if (coord->item_pos == coord_num_items(coord) && coord->between == BEFORE_ITEM) { + coord_dec_item_pos(coord); + coord->between = AFTER_ITEM; + } else if (coord->unit_pos == coord_num_units(coord) && coord->between == BEFORE_UNIT) { + coord->unit_pos--; + coord->between = AFTER_UNIT; + } else if (coord->item_pos == coord_num_items(coord) && coord->unit_pos == 0 && coord->between == BEFORE_UNIT) { + coord_dec_item_pos(coord); + coord->unit_pos = 0; + coord->between = AFTER_ITEM; + } +} + +/* Copy a coordinate. */ +reiser4_internal void +coord_dup(coord_t * coord, const coord_t * old_coord) +{ + assert("jmacd-9800", coord_check(old_coord)); + coord_dup_nocheck(coord, old_coord); +} + +/* Copy a coordinate without check. Useful when old_coord->node is not + loaded. As in cbk_tree_lookup -> connect_znode -> connect_one_side */ +reiser4_internal void +coord_dup_nocheck(coord_t * coord, const coord_t * old_coord) +{ + coord->node = old_coord->node; + coord_set_item_pos(coord, old_coord->item_pos); + coord->unit_pos = old_coord->unit_pos; + coord->between = old_coord->between; + coord->iplugid = old_coord->iplugid; + ON_DEBUG(coord->plug_v = old_coord->plug_v); + ON_DEBUG(coord->body_v = old_coord->body_v); +} + +/* Initialize an invalid coordinate. */ +reiser4_internal void +coord_init_invalid(coord_t * coord, const znode * node) +{ + coord_init_values(coord, node, 0, 0, INVALID_COORD); +} + +reiser4_internal void +coord_init_first_unit_nocheck(coord_t * coord, const znode * node) +{ + coord_init_values(coord, node, 0, 0, AT_UNIT); +} + +/* Initialize a coordinate to point at the first unit of the first item. If the node is + empty, it is positioned at the EMPTY_NODE. */ +reiser4_internal void +coord_init_first_unit(coord_t * coord, const znode * node) +{ + int is_empty = node_is_empty(node); + + coord_init_values(coord, node, 0, 0, (is_empty ? EMPTY_NODE : AT_UNIT)); + + assert("jmacd-9801", coord_check(coord)); +} + +/* Initialize a coordinate to point at the last unit of the last item. If the node is + empty, it is positioned at the EMPTY_NODE. */ +reiser4_internal void +coord_init_last_unit(coord_t * coord, const znode * node) +{ + int is_empty = node_is_empty(node); + + coord_init_values(coord, node, (is_empty ? 0 : node_num_items(node) - 1), 0, (is_empty ? EMPTY_NODE : AT_UNIT)); + if (!is_empty) + coord->unit_pos = coord_last_unit_pos(coord); + assert("jmacd-9802", coord_check(coord)); +} + +/* Initialize a coordinate to before the first item. If the node is empty, it is + positioned at the EMPTY_NODE. */ +reiser4_internal void +coord_init_before_first_item(coord_t * coord, const znode * node) +{ + int is_empty = node_is_empty(node); + + coord_init_values(coord, node, 0, 0, (is_empty ? EMPTY_NODE : BEFORE_UNIT)); + + assert("jmacd-9803", coord_check(coord)); +} + +/* Initialize a coordinate to after the last item. If the node is empty, it is positioned + at the EMPTY_NODE. */ +reiser4_internal void +coord_init_after_last_item(coord_t * coord, const znode * node) +{ + int is_empty = node_is_empty(node); + + coord_init_values(coord, node, + (is_empty ? 0 : node_num_items(node) - 1), 0, (is_empty ? EMPTY_NODE : AFTER_ITEM)); + + assert("jmacd-9804", coord_check(coord)); +} + +/* Initialize a coordinate to after last unit in the item. Coord must be set + already to existing item */ +reiser4_internal void +coord_init_after_item_end(coord_t * coord) +{ + coord->between = AFTER_UNIT; + coord->unit_pos = coord_last_unit_pos(coord); +} + +/* Initialize a coordinate to before the item. Coord must be set already to existing item */ +reiser4_internal void +coord_init_before_item(coord_t * coord) +{ + coord->unit_pos = 0; + coord->between = BEFORE_ITEM; +} + +/* Initialize a coordinate to after the item. Coord must be set already to existing item */ +reiser4_internal void +coord_init_after_item(coord_t * coord) +{ + coord->unit_pos = 0; + coord->between = AFTER_ITEM; +} + +/* Initialize a coordinate by 0s. Used in places where init_coord was used and + it was not clear how actually */ +reiser4_internal void +coord_init_zero(coord_t * coord) +{ + xmemset(coord, 0, sizeof (*coord)); +} + +/* Return the number of units at the present item. Asserts coord_is_existing_item(). */ +reiser4_internal unsigned +coord_num_units(const coord_t * coord) +{ + assert("jmacd-9806", coord_is_existing_item(coord)); + + return item_plugin_by_coord(coord)->b.nr_units(coord); +} + +/* Returns true if the coord was initializewd by coord_init_invalid (). */ +/* Audited by: green(2002.06.15) */ +reiser4_internal int +coord_is_invalid(const coord_t * coord) +{ + return coord->between == INVALID_COORD; +} + +/* Returns true if the coordinate is positioned at an existing item, not before or after + an item. It may be placed at, before, or after any unit within the item, whether + existing or not. */ +reiser4_internal int +coord_is_existing_item(const coord_t * coord) +{ + switch (coord->between) { + case EMPTY_NODE: + case BEFORE_ITEM: + case AFTER_ITEM: + case INVALID_COORD: + return 0; + + case BEFORE_UNIT: + case AT_UNIT: + case AFTER_UNIT: + return coord->item_pos < coord_num_items(coord); + } + + IF_TRACE(TRACE_COORDS, print_coord("unreachable", coord, 0)); + impossible("jmacd-9900", "unreachable coord: %p", coord); + return 0; +} + +/* Returns true if the coordinate is positioned at an existing unit, not before or after a + unit. */ +/* Audited by: green(2002.06.15) */ +reiser4_internal int +coord_is_existing_unit(const coord_t * coord) +{ + switch (coord->between) { + case EMPTY_NODE: + case BEFORE_UNIT: + case AFTER_UNIT: + case BEFORE_ITEM: + case AFTER_ITEM: + case INVALID_COORD: + return 0; + + case AT_UNIT: + return (coord->item_pos < coord_num_items(coord) && coord->unit_pos < coord_num_units(coord)); + } + + impossible("jmacd-9902", "unreachable"); + return 0; +} + +/* Returns true if the coordinate is positioned at the first unit of the first item. Not + true for empty nodes nor coordinates positioned before the first item. */ +/* Audited by: green(2002.06.15) */ +reiser4_internal int +coord_is_leftmost_unit(const coord_t * coord) +{ + return (coord->between == AT_UNIT && coord->item_pos == 0 && coord->unit_pos == 0); +} + +#if REISER4_DEBUG +/* For assertions only, checks for a valid coordinate. */ +int +coord_check(const coord_t * coord) +{ + if (coord->node == NULL) { + return 0; + } + if (znode_above_root(coord->node)) + return 1; + + switch (coord->between) { + default: + case INVALID_COORD: + return 0; + case EMPTY_NODE: + if (!node_is_empty(coord->node)) { + return 0; + } + return coord->item_pos == 0 && coord->unit_pos == 0; + + case BEFORE_UNIT: + case AFTER_UNIT: + if (node_is_empty(coord->node) && (coord->item_pos == 0) && (coord->unit_pos == 0)) + return 1; + case AT_UNIT: + break; + case AFTER_ITEM: + case BEFORE_ITEM: + /* before/after item should not set unit_pos. */ + if (coord->unit_pos != 0) { + return 0; + } + break; + } + + if (coord->item_pos >= node_num_items(coord->node)) { + return 0; + } + + /* FIXME-VS: we are going to check unit_pos. This makes no sense when + between is set either AFTER_ITEM or BEFORE_ITEM */ + if (coord->between == AFTER_ITEM || coord->between == BEFORE_ITEM) + return 1; + + if (coord_is_iplug_set(coord) && + coord->unit_pos > item_plugin_by_coord(coord)->b.nr_units(coord) - 1) { + return 0; + } + return 1; +} +#endif + +/* Adjust coordinate boundaries based on the number of items prior to coord_next/prev. + Returns 1 if the new position is does not exist. */ +static int +coord_adjust_items(coord_t * coord, unsigned items, int is_next) +{ + /* If the node is invalid, leave it. */ + if (coord->between == INVALID_COORD) { + return 1; + } + + /* If the node is empty, set it appropriately. */ + if (items == 0) { + coord->between = EMPTY_NODE; + coord_set_item_pos(coord, 0); + coord->unit_pos = 0; + return 1; + } + + /* If it was empty and it no longer is, set to BEFORE/AFTER_ITEM. */ + if (coord->between == EMPTY_NODE) { + coord->between = (is_next ? BEFORE_ITEM : AFTER_ITEM); + coord_set_item_pos(coord, 0); + coord->unit_pos = 0; + return 0; + } + + /* If the item_pos is out-of-range, set it appropriatly. */ + if (coord->item_pos >= items) { + coord->between = AFTER_ITEM; + coord_set_item_pos(coord, items - 1); + coord->unit_pos = 0; + /* If is_next, return 1 (can't go any further). */ + return is_next; + } + + return 0; +} + +/* Advances the coordinate by one unit to the right. If empty, no change. If + coord_is_rightmost_unit, advances to AFTER THE LAST ITEM. Returns 0 if new position is an + existing unit. */ +reiser4_internal int +coord_next_unit(coord_t * coord) +{ + unsigned items = coord_num_items(coord); + + if (coord_adjust_items(coord, items, 1) == 1) { + return 1; + } + + switch (coord->between) { + case BEFORE_UNIT: + /* Now it is positioned at the same unit. */ + coord->between = AT_UNIT; + return 0; + + case AFTER_UNIT: + case AT_UNIT: + /* If it was at or after a unit and there are more units in this item, + advance to the next one. */ + if (coord->unit_pos < coord_last_unit_pos(coord)) { + coord->unit_pos += 1; + coord->between = AT_UNIT; + return 0; + } + + /* Otherwise, it is crossing an item boundary and treated as if it was + after the current item. */ + coord->between = AFTER_ITEM; + coord->unit_pos = 0; + /* FALLTHROUGH */ + + case AFTER_ITEM: + /* Check for end-of-node. */ + if (coord->item_pos == items - 1) { + return 1; + } + + coord_inc_item_pos(coord); + coord->unit_pos = 0; + coord->between = AT_UNIT; + return 0; + + case BEFORE_ITEM: + /* The adjust_items checks ensure that we are valid here. */ + coord->unit_pos = 0; + coord->between = AT_UNIT; + return 0; + + case INVALID_COORD: + case EMPTY_NODE: + /* Handled in coord_adjust_items(). */ + break; + } + + impossible("jmacd-9902", "unreachable"); + return 0; +} + +/* Advances the coordinate by one item to the right. If empty, no change. If + coord_is_rightmost_unit, advances to AFTER THE LAST ITEM. Returns 0 if new position is + an existing item. */ +reiser4_internal int +coord_next_item(coord_t * coord) +{ + unsigned items = coord_num_items(coord); + + if (coord_adjust_items(coord, items, 1) == 1) { + return 1; + } + + switch (coord->between) { + case AFTER_UNIT: + case AT_UNIT: + case BEFORE_UNIT: + case AFTER_ITEM: + /* Check for end-of-node. */ + if (coord->item_pos == items - 1) { + coord->between = AFTER_ITEM; + coord->unit_pos = 0; + coord_clear_iplug(coord); + return 1; + } + + /* Anywhere in an item, go to the next one. */ + coord->between = AT_UNIT; + coord_inc_item_pos(coord); + coord->unit_pos = 0; + return 0; + + case BEFORE_ITEM: + /* The out-of-range check ensures that we are valid here. */ + coord->unit_pos = 0; + coord->between = AT_UNIT; + return 0; + case INVALID_COORD: + case EMPTY_NODE: + /* Handled in coord_adjust_items(). */ + break; + } + + impossible("jmacd-9903", "unreachable"); + return 0; +} + +/* Advances the coordinate by one unit to the left. If empty, no change. If + coord_is_leftmost_unit, advances to BEFORE THE FIRST ITEM. Returns 0 if new position + is an existing unit. */ +reiser4_internal int +coord_prev_unit(coord_t * coord) +{ + unsigned items = coord_num_items(coord); + + if (coord_adjust_items(coord, items, 0) == 1) { + return 1; + } + + switch (coord->between) { + case AT_UNIT: + case BEFORE_UNIT: + if (coord->unit_pos > 0) { + coord->unit_pos -= 1; + coord->between = AT_UNIT; + return 0; + } + + if (coord->item_pos == 0) { + coord->between = BEFORE_ITEM; + return 1; + } + + coord_dec_item_pos(coord); + coord->unit_pos = coord_last_unit_pos(coord); + coord->between = AT_UNIT; + return 0; + + case AFTER_UNIT: + /* What if unit_pos is out-of-range? */ + assert("jmacd-5442", coord->unit_pos <= coord_last_unit_pos(coord)); + coord->between = AT_UNIT; + return 0; + + case BEFORE_ITEM: + if (coord->item_pos == 0) { + return 1; + } + + coord_dec_item_pos(coord); + /* FALLTHROUGH */ + + case AFTER_ITEM: + coord->between = AT_UNIT; + coord->unit_pos = coord_last_unit_pos(coord); + return 0; + + case INVALID_COORD: + case EMPTY_NODE: + break; + } + + impossible("jmacd-9904", "unreachable"); + return 0; +} + +/* Advances the coordinate by one item to the left. If empty, no change. If + coord_is_leftmost_unit, advances to BEFORE THE FIRST ITEM. Returns 0 if new position + is an existing item. */ +reiser4_internal int +coord_prev_item(coord_t * coord) +{ + unsigned items = coord_num_items(coord); + + if (coord_adjust_items(coord, items, 0) == 1) { + return 1; + } + + switch (coord->between) { + case AT_UNIT: + case AFTER_UNIT: + case BEFORE_UNIT: + case BEFORE_ITEM: + + if (coord->item_pos == 0) { + coord->between = BEFORE_ITEM; + coord->unit_pos = 0; + return 1; + } + + coord_dec_item_pos(coord); + coord->unit_pos = 0; + coord->between = AT_UNIT; + return 0; + + case AFTER_ITEM: + coord->between = AT_UNIT; + coord->unit_pos = 0; + return 0; + + case INVALID_COORD: + case EMPTY_NODE: + break; + } + + impossible("jmacd-9905", "unreachable"); + return 0; +} + +/* Calls either coord_init_first_unit or coord_init_last_unit depending on sideof argument. */ +reiser4_internal void +coord_init_sideof_unit(coord_t * coord, const znode * node, sideof dir) +{ + assert("jmacd-9821", dir == LEFT_SIDE || dir == RIGHT_SIDE); + if (dir == LEFT_SIDE) { + coord_init_first_unit(coord, node); + } else { + coord_init_last_unit(coord, node); + } +} + +/* Calls either coord_is_before_leftmost or coord_is_after_rightmost depending on sideof + argument. */ +/* Audited by: green(2002.06.15) */ +reiser4_internal int +coord_is_after_sideof_unit(coord_t * coord, sideof dir) +{ + assert("jmacd-9822", dir == LEFT_SIDE || dir == RIGHT_SIDE); + if (dir == LEFT_SIDE) { + return coord_is_before_leftmost(coord); + } else { + return coord_is_after_rightmost(coord); + } +} + +/* Calls either coord_next_unit or coord_prev_unit depending on sideof argument. */ +/* Audited by: green(2002.06.15) */ +reiser4_internal int +coord_sideof_unit(coord_t * coord, sideof dir) +{ + assert("jmacd-9823", dir == LEFT_SIDE || dir == RIGHT_SIDE); + if (dir == LEFT_SIDE) { + return coord_prev_unit(coord); + } else { + return coord_next_unit(coord); + } +} + +#if REISER4_DEBUG +#define DEBUG_COORD_FIELDS (sizeof(c1->plug_v) + sizeof(c1->body_v)) +#else +#define DEBUG_COORD_FIELDS (0) +#endif + +reiser4_internal int +coords_equal(const coord_t * c1, const coord_t * c2) +{ + assert("nikita-2840", c1 != NULL); + assert("nikita-2841", c2 != NULL); + +#if 0 + /* assertion to track changes in coord_t */ + cassert(sizeof(*c1) == sizeof(c1->node) + + sizeof(c1->item_pos) + + sizeof(c1->unit_pos) + + sizeof(c1->iplugid) + + sizeof(c1->between) + + sizeof(c1->pad) + + sizeof(c1->offset) + + DEBUG_COORD_FIELDS); +#endif + return + c1->node == c2->node && + c1->item_pos == c2->item_pos && + c1->unit_pos == c2->unit_pos && + c1->between == c2->between; +} + +/* Returns true if two coordinates are consider equal. Coordinates that are between units + or items are considered equal. */ +/* Audited by: green(2002.06.15) */ +reiser4_internal int +coord_eq(const coord_t * c1, const coord_t * c2) +{ + assert("nikita-1807", c1 != NULL); + assert("nikita-1808", c2 != NULL); + + if (coords_equal(c1, c2)) { + return 1; + } + if (c1->node != c2->node) { + return 0; + } + + switch (c1->between) { + case INVALID_COORD: + case EMPTY_NODE: + case AT_UNIT: + return 0; + + case BEFORE_UNIT: + /* c2 must be after the previous unit. */ + return (c1->item_pos == c2->item_pos && c2->between == AFTER_UNIT && c2->unit_pos == c1->unit_pos - 1); + + case AFTER_UNIT: + /* c2 must be before the next unit. */ + return (c1->item_pos == c2->item_pos && c2->between == BEFORE_UNIT && c2->unit_pos == c1->unit_pos + 1); + + case BEFORE_ITEM: + /* c2 must be after the previous item. */ + return (c1->item_pos == c2->item_pos - 1 && c2->between == AFTER_ITEM); + + case AFTER_ITEM: + /* c2 must be before the next item. */ + return (c1->item_pos == c2->item_pos + 1 && c2->between == BEFORE_ITEM); + } + + impossible("jmacd-9906", "unreachable"); + return 0; +} + +/* If coord_is_after_rightmost return NCOORD_ON_THE_RIGHT, if coord_is_after_leftmost + return NCOORD_ON_THE_LEFT, otherwise return NCOORD_INSIDE. */ +/* Audited by: green(2002.06.15) */ +reiser4_internal coord_wrt_node coord_wrt(const coord_t * coord) +{ + if (coord_is_before_leftmost(coord)) { + return COORD_ON_THE_LEFT; + } + + if (coord_is_after_rightmost(coord)) { + return COORD_ON_THE_RIGHT; + } + + return COORD_INSIDE; +} + +/* Returns true if the coordinate is positioned after the last item or after the last unit + of the last item or it is an empty node. */ +/* Audited by: green(2002.06.15) */ +reiser4_internal int +coord_is_after_rightmost(const coord_t * coord) +{ + assert("jmacd-7313", coord_check(coord)); + + switch (coord->between) { + case INVALID_COORD: + case AT_UNIT: + case BEFORE_UNIT: + case BEFORE_ITEM: + return 0; + + case EMPTY_NODE: + return 1; + + case AFTER_ITEM: + return (coord->item_pos == node_num_items(coord->node) - 1); + + case AFTER_UNIT: + return ((coord->item_pos == node_num_items(coord->node) - 1) && + coord->unit_pos == coord_last_unit_pos(coord)); + } + + impossible("jmacd-9908", "unreachable"); + return 0; +} + +/* Returns true if the coordinate is positioned before the first item or it is an empty + node. */ +reiser4_internal int +coord_is_before_leftmost(const coord_t * coord) +{ + /* FIXME-VS: coord_check requires node to be loaded whereas it is not + necessary to check if coord is set before leftmost + assert ("jmacd-7313", coord_check (coord)); */ + switch (coord->between) { + case INVALID_COORD: + case AT_UNIT: + case AFTER_ITEM: + case AFTER_UNIT: + return 0; + + case EMPTY_NODE: + return 1; + + case BEFORE_ITEM: + case BEFORE_UNIT: + return (coord->item_pos == 0) && (coord->unit_pos == 0); + } + + impossible("jmacd-9908", "unreachable"); + return 0; +} + +/* Returns true if the coordinate is positioned after a item, before a item, after the + last unit of an item, before the first unit of an item, or at an empty node. */ +/* Audited by: green(2002.06.15) */ +reiser4_internal int +coord_is_between_items(const coord_t * coord) +{ + assert("jmacd-7313", coord_check(coord)); + + switch (coord->between) { + case INVALID_COORD: + case AT_UNIT: + return 0; + + case AFTER_ITEM: + case BEFORE_ITEM: + case EMPTY_NODE: + return 1; + + case BEFORE_UNIT: + return coord->unit_pos == 0; + + case AFTER_UNIT: + return coord->unit_pos == coord_last_unit_pos(coord); + } + + impossible("jmacd-9908", "unreachable"); + return 0; +} + +/* Returns true if the coordinates are positioned at adjacent units, regardless of + before-after or item boundaries. */ +reiser4_internal int +coord_are_neighbors(coord_t * c1, coord_t * c2) +{ + coord_t *left; + coord_t *right; + + assert("nikita-1241", c1 != NULL); + assert("nikita-1242", c2 != NULL); + assert("nikita-1243", c1->node == c2->node); + assert("nikita-1244", coord_is_existing_unit(c1)); + assert("nikita-1245", coord_is_existing_unit(c2)); + + left = right = 0; + switch (coord_compare(c1, c2)) { + case COORD_CMP_ON_LEFT: + left = c1; + right = c2; + break; + case COORD_CMP_ON_RIGHT: + left = c2; + right = c1; + break; + case COORD_CMP_SAME: + return 0; + default: + wrong_return_value("nikita-1246", "compare_coords()"); + } + assert("vs-731", left && right); + if (left->item_pos == right->item_pos) { + return left->unit_pos + 1 == right->unit_pos; + } else if (left->item_pos + 1 == right->item_pos) { + return (left->unit_pos == coord_last_unit_pos(left)) && (right->unit_pos == 0); + } else { + return 0; + } +} + +/* Assuming two coordinates are positioned in the same node, return COORD_CMP_ON_RIGHT, + COORD_CMP_ON_LEFT, or COORD_CMP_SAME depending on c1's position relative to c2. */ +/* Audited by: green(2002.06.15) */ +reiser4_internal coord_cmp coord_compare(coord_t * c1, coord_t * c2) +{ + assert("vs-209", c1->node == c2->node); + assert("vs-194", coord_is_existing_unit(c1) + && coord_is_existing_unit(c2)); + + if (c1->item_pos > c2->item_pos) + return COORD_CMP_ON_RIGHT; + if (c1->item_pos < c2->item_pos) + return COORD_CMP_ON_LEFT; + if (c1->unit_pos > c2->unit_pos) + return COORD_CMP_ON_RIGHT; + if (c1->unit_pos < c2->unit_pos) + return COORD_CMP_ON_LEFT; + return COORD_CMP_SAME; +} + +/* If the coordinate is between items, shifts it to the right. Returns 0 on success and + non-zero if there is no position to the right. */ +reiser4_internal int +coord_set_to_right(coord_t * coord) +{ + unsigned items = coord_num_items(coord); + + if (coord_adjust_items(coord, items, 1) == 1) { + return 1; + } + + switch (coord->between) { + case AT_UNIT: + return 0; + + case BEFORE_ITEM: + case BEFORE_UNIT: + coord->between = AT_UNIT; + return 0; + + case AFTER_UNIT: + if (coord->unit_pos < coord_last_unit_pos(coord)) { + coord->unit_pos += 1; + coord->between = AT_UNIT; + return 0; + } else { + + coord->unit_pos = 0; + + if (coord->item_pos == items - 1) { + coord->between = AFTER_ITEM; + return 1; + } + + coord_inc_item_pos(coord); + coord->between = AT_UNIT; + return 0; + } + + case AFTER_ITEM: + if (coord->item_pos == items - 1) { + return 1; + } + + coord_inc_item_pos(coord); + coord->unit_pos = 0; + coord->between = AT_UNIT; + return 0; + + case EMPTY_NODE: + return 1; + + case INVALID_COORD: + break; + } + + impossible("jmacd-9920", "unreachable"); + return 0; +} + +/* If the coordinate is between items, shifts it to the left. Returns 0 on success and + non-zero if there is no position to the left. */ +reiser4_internal int +coord_set_to_left(coord_t * coord) +{ + unsigned items = coord_num_items(coord); + + if (coord_adjust_items(coord, items, 0) == 1) { + return 1; + } + + switch (coord->between) { + case AT_UNIT: + return 0; + + case AFTER_UNIT: + coord->between = AT_UNIT; + return 0; + + case AFTER_ITEM: + coord->between = AT_UNIT; + coord->unit_pos = coord_last_unit_pos(coord); + return 0; + + case BEFORE_UNIT: + if (coord->unit_pos > 0) { + coord->unit_pos -= 1; + coord->between = AT_UNIT; + return 0; + } else { + + if (coord->item_pos == 0) { + coord->between = BEFORE_ITEM; + return 1; + } + + coord->unit_pos = coord_last_unit_pos(coord); + coord_dec_item_pos(coord); + coord->between = AT_UNIT; + return 0; + } + + case BEFORE_ITEM: + if (coord->item_pos == 0) { + return 1; + } + + coord_dec_item_pos(coord); + coord->unit_pos = coord_last_unit_pos(coord); + coord->between = AT_UNIT; + return 0; + + case EMPTY_NODE: + return 1; + + case INVALID_COORD: + break; + } + + impossible("jmacd-9920", "unreachable"); + return 0; +} + +reiser4_internal const char * +coord_tween_tostring(between_enum n) +{ + switch (n) { + case BEFORE_UNIT: + return "before unit"; + case BEFORE_ITEM: + return "before item"; + case AT_UNIT: + return "at unit"; + case AFTER_UNIT: + return "after unit"; + case AFTER_ITEM: + return "after item"; + case EMPTY_NODE: + return "empty node"; + case INVALID_COORD: + return "invalid"; + default:{ + static char buf[30]; + + sprintf(buf, "unknown: %i", n); + return buf; + } + } +} + +reiser4_internal void +print_coord(const char *mes, const coord_t * coord, int node) +{ + if (coord == NULL) { + printk("%s: null\n", mes); + return; + } + printk("%s: item_pos = %d, unit_pos %d, tween=%s, iplug=%d\n", + mes, coord->item_pos, coord->unit_pos, coord_tween_tostring(coord->between), coord->iplugid); + if (node) + print_znode("\tnode", coord->node); +} + +reiser4_internal int +item_utmost_child_real_block(const coord_t * coord, sideof side, reiser4_block_nr * blk) +{ + return item_plugin_by_coord(coord)->f.utmost_child_real_block(coord, side, blk); +} + +reiser4_internal int +item_utmost_child(const coord_t * coord, sideof side, jnode ** child) +{ + return item_plugin_by_coord(coord)->f.utmost_child(coord, side, child); +} + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/coord.h 2004-09-03 00:57:05.001278952 -0700 @@ -0,0 +1,341 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Coords */ + +#if !defined( __REISER4_COORD_H__ ) +#define __REISER4_COORD_H__ + +#include "forward.h" +#include "debug.h" +#include "dformat.h" + +/* insertions happen between coords in the tree, so we need some means + of specifying the sense of betweenness. */ +typedef enum { + BEFORE_UNIT, /* Note: we/init_coord depends on this value being zero. */ + AT_UNIT, + AFTER_UNIT, + BEFORE_ITEM, + AFTER_ITEM, + INVALID_COORD, + EMPTY_NODE, +} between_enum; + +/* location of coord w.r.t. its node */ +typedef enum { + COORD_ON_THE_LEFT = -1, + COORD_ON_THE_RIGHT = +1, + COORD_INSIDE = 0 +} coord_wrt_node; + +typedef enum { + COORD_CMP_SAME = 0, COORD_CMP_ON_LEFT = -1, COORD_CMP_ON_RIGHT = +1 +} coord_cmp; + +struct coord { + /* node in a tree */ + /* 0 */ znode *node; + + /* position of item within node */ + /* 4 */ pos_in_node_t item_pos; + /* position of unit within item */ + /* 6 */ pos_in_node_t unit_pos; + /* optimization: plugin of item is stored in coord_t. Until this was + implemented, item_plugin_by_coord() was major CPU consumer. ->iplugid + is invalidated (set to 0xff) on each modification of ->item_pos, + and all such modifications are funneled through coord_*_item_pos() + functions below. + */ + /* 8 */ char iplugid; + /* position of coord w.r.t. to neighboring items and/or units. + Values are taken from &between_enum above. + */ + /* 9 */ char between; + /* padding. It will be added by the compiler anyway to conform to the + * C language alignment requirements. We keep it here to be on the + * safe side and to have a clear picture of the memory layout of this + * structure. */ + /* 10 */ __u16 pad; + /* 12 */ int offset; +#if REISER4_DEBUG + unsigned long plug_v; + unsigned long body_v; +#endif +}; + +#define INVALID_PLUGID ((char)((1 << 8) - 1)) +#define INVALID_OFFSET -1 + +static inline void +coord_clear_iplug(coord_t * coord) +{ + assert("nikita-2835", coord != NULL); + coord->iplugid = INVALID_PLUGID; + coord->offset = INVALID_OFFSET; +} + +static inline int +coord_is_iplug_set(const coord_t * coord) +{ + assert("nikita-2836", coord != NULL); + return coord->iplugid != INVALID_PLUGID; +} + +static inline void +coord_set_item_pos(coord_t * coord, pos_in_node_t pos) +{ + assert("nikita-2478", coord != NULL); + coord->item_pos = pos; + coord_clear_iplug(coord); +} + +static inline void +coord_dec_item_pos(coord_t * coord) +{ + assert("nikita-2480", coord != NULL); + --coord->item_pos; + coord_clear_iplug(coord); +} + +static inline void +coord_inc_item_pos(coord_t * coord) +{ + assert("nikita-2481", coord != NULL); + ++coord->item_pos; + coord_clear_iplug(coord); +} + +static inline void +coord_add_item_pos(coord_t * coord, int delta) +{ + assert("nikita-2482", coord != NULL); + coord->item_pos += delta; + coord_clear_iplug(coord); +} + +static inline void +coord_invalid_item_pos(coord_t * coord) +{ + assert("nikita-2832", coord != NULL); + coord->item_pos = (unsigned short)~0; + coord_clear_iplug(coord); +} + +/* Reverse a direction. */ +static inline sideof +sideof_reverse(sideof side) +{ + return side == LEFT_SIDE ? RIGHT_SIDE : LEFT_SIDE; +} + +/* NOTE: There is a somewhat odd mixture of the following opposed terms: + + "first" and "last" + "next" and "prev" + "before" and "after" + "leftmost" and "rightmost" + + But I think the chosen names are decent the way they are. +*/ + +/* COORD INITIALIZERS */ + +/* Initialize an invalid coordinate. */ +extern void coord_init_invalid(coord_t * coord, const znode * node); + +extern void coord_init_first_unit_nocheck(coord_t * coord, const znode * node); + +/* Initialize a coordinate to point at the first unit of the first item. If the node is + empty, it is positioned at the EMPTY_NODE. */ +extern void coord_init_first_unit(coord_t * coord, const znode * node); + +/* Initialize a coordinate to point at the last unit of the last item. If the node is + empty, it is positioned at the EMPTY_NODE. */ +extern void coord_init_last_unit(coord_t * coord, const znode * node); + +/* Initialize a coordinate to before the first item. If the node is empty, it is + positioned at the EMPTY_NODE. */ +extern void coord_init_before_first_item(coord_t * coord, const znode * node); + +/* Initialize a coordinate to after the last item. If the node is empty, it is positioned + at the EMPTY_NODE. */ +extern void coord_init_after_last_item(coord_t * coord, const znode * node); + +/* Initialize a coordinate to after last unit in the item. Coord must be set + already to existing item */ +void coord_init_after_item_end(coord_t * coord); + +/* Initialize a coordinate to before the item. Coord must be set already to existing item */ +void coord_init_before_item(coord_t *); +/* Initialize a coordinate to after the item. Coord must be set already to existing item */ +void coord_init_after_item(coord_t *); + +/* Calls either coord_init_first_unit or coord_init_last_unit depending on sideof argument. */ +extern void coord_init_sideof_unit(coord_t * coord, const znode * node, sideof dir); + +/* Initialize a coordinate by 0s. Used in places where init_coord was used and + it was not clear how actually + FIXME-VS: added by vs (2002, june, 8) */ +extern void coord_init_zero(coord_t * coord); + +/* COORD METHODS */ + +/* after shifting of node content, coord previously set properly may become + invalid, try to "normalize" it. */ +void coord_normalize(coord_t * coord); + +/* Copy a coordinate. */ +extern void coord_dup(coord_t * coord, const coord_t * old_coord); + +/* Copy a coordinate without check. */ +void coord_dup_nocheck(coord_t * coord, const coord_t * old_coord); + +unsigned coord_num_units(const coord_t * coord); + +/* Return the last valid unit number at the present item (i.e., + coord_num_units() - 1). */ +static inline unsigned +coord_last_unit_pos(const coord_t * coord) +{ + return coord_num_units(coord) - 1; +} + +#if REISER4_DEBUG +/* For assertions only, checks for a valid coordinate. */ +extern int coord_check(const coord_t * coord); + +extern unsigned long znode_times_locked(const znode *z); + +static inline void +coord_update_v(coord_t * coord) +{ + coord->plug_v = coord->body_v = znode_times_locked(coord->node); +} +#endif + +extern int coords_equal(const coord_t * c1, const coord_t * c2); + +/* Returns true if two coordinates are consider equal. Coordinates that are between units + or items are considered equal. */ +extern int coord_eq(const coord_t * c1, const coord_t * c2); + +extern void print_coord(const char *mes, const coord_t * coord, int print_node); + +/* If coord_is_after_rightmost return NCOORD_ON_THE_RIGHT, if coord_is_after_leftmost + return NCOORD_ON_THE_LEFT, otherwise return NCOORD_INSIDE. */ +extern coord_wrt_node coord_wrt(const coord_t * coord); + +/* Returns true if the coordinates are positioned at adjacent units, regardless of + before-after or item boundaries. */ +extern int coord_are_neighbors(coord_t * c1, coord_t * c2); + +/* Assuming two coordinates are positioned in the same node, return NCOORD_CMP_ON_RIGHT, + NCOORD_CMP_ON_LEFT, or NCOORD_CMP_SAME depending on c1's position relative to c2. */ +extern coord_cmp coord_compare(coord_t * c1, coord_t * c2); + +/* COORD PREDICATES */ + +/* Returns true if the coord was initializewd by coord_init_invalid (). */ +extern int coord_is_invalid(const coord_t * coord); + +/* Returns true if the coordinate is positioned at an existing item, not before or after + an item. It may be placed at, before, or after any unit within the item, whether + existing or not. If this is true you can call methods of the item plugin. */ +extern int coord_is_existing_item(const coord_t * coord); + +/* Returns true if the coordinate is positioned after a item, before a item, after the + last unit of an item, before the first unit of an item, or at an empty node. */ +extern int coord_is_between_items(const coord_t * coord); + +/* Returns true if the coordinate is positioned at an existing unit, not before or after a + unit. */ +extern int coord_is_existing_unit(const coord_t * coord); + +/* Returns true if the coordinate is positioned at an empty node. */ +extern int coord_is_empty(const coord_t * coord); + +/* Returns true if the coordinate is positioned at the first unit of the first item. Not + true for empty nodes nor coordinates positioned before the first item. */ +extern int coord_is_leftmost_unit(const coord_t * coord); + +/* Returns true if the coordinate is positioned after the last item or after the last unit + of the last item or it is an empty node. */ +extern int coord_is_after_rightmost(const coord_t * coord); + +/* Returns true if the coordinate is positioned before the first item or it is an empty + node. */ +extern int coord_is_before_leftmost(const coord_t * coord); + +/* Calls either coord_is_before_leftmost or coord_is_after_rightmost depending on sideof + argument. */ +extern int coord_is_after_sideof_unit(coord_t * coord, sideof dir); + +/* COORD MODIFIERS */ + +/* Advances the coordinate by one unit to the right. If empty, no change. If + coord_is_rightmost_unit, advances to AFTER THE LAST ITEM. Returns 0 if new position is + an existing unit. */ +extern int coord_next_unit(coord_t * coord); + +/* Advances the coordinate by one item to the right. If empty, no change. If + coord_is_rightmost_unit, advances to AFTER THE LAST ITEM. Returns 0 if new position is + an existing item. */ +extern int coord_next_item(coord_t * coord); + +/* Advances the coordinate by one unit to the left. If empty, no change. If + coord_is_leftmost_unit, advances to BEFORE THE FIRST ITEM. Returns 0 if new position + is an existing unit. */ +extern int coord_prev_unit(coord_t * coord); + +/* Advances the coordinate by one item to the left. If empty, no change. If + coord_is_leftmost_unit, advances to BEFORE THE FIRST ITEM. Returns 0 if new position + is an existing item. */ +extern int coord_prev_item(coord_t * coord); + +/* If the coordinate is between items, shifts it to the right. Returns 0 on success and + non-zero if there is no position to the right. */ +extern int coord_set_to_right(coord_t * coord); + +/* If the coordinate is between items, shifts it to the left. Returns 0 on success and + non-zero if there is no position to the left. */ +extern int coord_set_to_left(coord_t * coord); + +/* If the coordinate is at an existing unit, set to after that unit. Returns 0 on success + and non-zero if the unit did not exist. */ +extern int coord_set_after_unit(coord_t * coord); + +/* Calls either coord_next_unit or coord_prev_unit depending on sideof argument. */ +extern int coord_sideof_unit(coord_t * coord, sideof dir); + +/* iterate over all units in @node */ +#define for_all_units( coord, node ) \ + for( coord_init_before_first_item( ( coord ), ( node ) ) ; \ + coord_next_unit( coord ) == 0 ; ) + +/* iterate over all items in @node */ +#define for_all_items( coord, node ) \ + for( coord_init_before_first_item( ( coord ), ( node ) ) ; \ + coord_next_item( coord ) == 0 ; ) + +#if REISER4_DEBUG_OUTPUT +extern const char *coord_tween_tostring(between_enum n); +#endif + +/* COORD/ITEM METHODS */ + +extern int item_utmost_child_real_block(const coord_t * coord, sideof side, reiser4_block_nr * blk); +extern int item_utmost_child(const coord_t * coord, sideof side, jnode ** child); + +/* __REISER4_COORD_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/crypt.c 2004-09-03 00:57:05.002278800 -0700 @@ -0,0 +1,92 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ +/* Crypto-plugins for reiser4 cryptcompress objects */ + +#include "debug.h" +#include "plugin/plugin.h" +#include "plugin/cryptcompress.h" +#include +#include + +#define MAX_CRYPTO_BLOCKSIZE 128 +#define NONE_EXPKEY_WORDS 8 +#define NONE_BLOCKSIZE 8 + +/* + Default align() method of the crypto-plugin (look for description of this method + in plugin/plugin.h) + +1) creates the aligning armored format of the input flow before encryption. + "armored" means that padding is filled by private data (for example, + pseudo-random sequence of bytes is not private data). +2) returns length of appended padding + + [ flow | aligning_padding ] + ^ + | + @pad +*/ +UNUSED_ARG static int +align_cluster_common(__u8 *pad /* pointer to the first byte of aligning format */, + int flow_size /* size of non-aligned flow */, + int blocksize /* crypto-block size */) +{ + int pad_size; + + assert("edward-01", pad != NULL); + assert("edward-02", flow_size != 0); + assert("edward-03", blocksize != 0 || blocksize <= MAX_CRYPTO_BLOCKSIZE); + + pad_size = blocksize - (flow_size % blocksize); + get_random_bytes (pad, pad_size); + return pad_size; +} + +/* common scale method (look for description of this method in plugin/plugin.h) + for all symmetric algorithms which doesn't scale anything +*/ +static loff_t scale_common(struct inode * inode UNUSED_ARG, + size_t blocksize UNUSED_ARG /* crypto block size, which is returned + by blocksize method of crypto plugin */, + loff_t src_off /* offset to scale */) +{ + return src_off; +} + +REGISTER_NONE_ALG(crypt, CRYPTO) + +/* EDWARD-FIXME-HANS: why is this not in the plugin directory? */ + +/* crypto plugins */ +crypto_plugin crypto_plugins[LAST_CRYPTO_ID] = { + [NONE_CRYPTO_ID] = { + .h = { + .type_id = REISER4_CRYPTO_PLUGIN_TYPE, + .id = NONE_CRYPTO_ID, + .pops = NULL, + /* If you wanna your files to not be crypto + transformed, specify this crypto pluigin */ + .label = "none", + .desc = "absence of crypto transform", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .alloc = alloc_none_crypt, + .free = free_none_crypt, + .nr_keywords = NONE_EXPKEY_WORDS, + .scale = scale_common, + .align_cluster = NULL, + .setkey = NULL, + .encrypt = NULL, + .decrypt = NULL + } +}; + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/debug.c 2004-09-03 00:57:06.722017360 -0700 @@ -0,0 +1,736 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Debugging facilities. */ + +/* + * This file contains generic debugging functions used by reiser4. Roughly + * following: + * + * panicking: reiser4_do_panic(), reiser4_print_prefix(). + * + * locking: schedulable(), lock_counters(), print_lock_counters(), + * no_counters_are_held(), commit_check_locks() + * + * {debug,trace,log}_flags: reiser4_are_all_debugged(), + * reiser4_is_debugged(), get_current_trace_flags(), + * get_current_log_flags(). + * + * kmalloc/kfree leak detection: reiser4_kmalloc(), reiser4_kfree(), + * reiser4_kfree_in_sb(). + * + * error code monitoring (see comment before RETERR macro): return_err(), + * report_err(). + * + * stack back-tracing: fill_backtrace() + * + * miscellaneous: preempt_point(), call_on_each_assert(), debugtrap(). + * + */ + +#include "kattr.h" +#include "reiser4.h" +#include "context.h" +#include "super.h" +#include "txnmgr.h" +#include "znode.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern void cond_resched(void); + +/* + * global buffer where message given to reiser4_panic is formatted. + */ +static char panic_buf[REISER4_PANIC_MSG_BUFFER_SIZE]; + +/* + * lock protecting consistency of panic_buf under concurrent panics + */ +static spinlock_t panic_guard = SPIN_LOCK_UNLOCKED; + +/* Your best friend. Call it on each occasion. This is called by + fs/reiser4/debug.h:reiser4_panic(). */ +reiser4_internal void +reiser4_do_panic(const char *format /* format string */ , ... /* rest */) +{ + static int in_panic = 0; + va_list args; + + /* + * check for recursive panic. + */ + if (in_panic == 0) { + in_panic = 1; + + spin_lock(&panic_guard); + va_start(args, format); + vsnprintf(panic_buf, sizeof(panic_buf), format, args); + va_end(args); + printk(KERN_EMERG "reiser4 panicked cowardly: %s", panic_buf); + spin_unlock(&panic_guard); + + /* + * if kernel debugger is configured---drop in. Early dropping + * into kgdb is not always convenient, because panic message + * is not yet printed most of the times. But: + * + * (1) message can be extracted from printk_buf[] + * (declared static inside of printk()), and + * + * (2) sometimes serial/kgdb combo dies while printing + * long panic message, so it's more prudent to break into + * debugger earlier. + * + */ + DEBUGON(1); + + if (get_current_context_check() != NULL) { + struct super_block *super; + reiser4_context *ctx; + + /* + * if we are within reiser4 context, print it contents: + */ + + /* lock counters... */ + print_lock_counters("pins held", lock_counters()); + /* other active contexts... */ + print_contexts(); + ctx = get_current_context(); + super = ctx->super; + if (get_super_private(super) != NULL && + reiser4_is_debugged(super, REISER4_VERBOSE_PANIC)) + /* znodes... */ + print_znodes("znodes", current_tree); +#if REISER4_DEBUG_CONTEXTS + { + extern spinlock_t active_contexts_lock; + + /* + * remove context from the list of active + * contexts. This is precaution measure: + * current is going to die, and leaving + * context on the list would render latter + * corrupted. + */ + spin_lock(&active_contexts_lock); + context_list_remove(ctx->parent); + spin_unlock(&active_contexts_lock); + } +#endif + } + } + BUG(); + /* to make gcc happy about noreturn attribute */ + panic("%s", panic_buf); +} + +reiser4_internal void +reiser4_print_prefix(const char *level, int reperr, const char *mid, + const char *function, const char *file, int lineno) +{ + const char *comm; + int pid; + + if (unlikely(in_interrupt() || in_irq())) { + comm = "interrupt"; + pid = 0; + } else { + comm = current->comm; + pid = current->pid; + } + printk("%sreiser4[%.16s(%i)]: %s (%s:%i)[%s]:\n", + level, comm, pid, function, file, lineno, mid); + if (reperr) + report_err(); +} + +/* Preemption point: this should be called periodically during long running + operations (carry, allocate, and squeeze are best examples) */ +reiser4_internal int +preempt_point(void) +{ + assert("nikita-3008", schedulable()); + cond_resched(); + return signal_pending(current); +} + +#if REISER4_DEBUG + +/* check that no spinlocks are held */ +int schedulable(void) +{ + if (get_current_context_check() != NULL) { + if (!LOCK_CNT_NIL(spin_locked)) { + print_lock_counters("in atomic", lock_counters()); + return 0; + } + } + might_sleep(); + return 1; +} +#endif + +#if REISER4_DEBUG_SPIN_LOCKS +/* Debugging aid: return struct where information about locks taken by current + thread is accumulated. This can be used to formulate lock ordering + constraints and various assertions. + +*/ +lock_counters_info * +lock_counters(void) +{ + reiser4_context *ctx = get_current_context(); + assert("jmacd-1123", ctx != NULL); + return &ctx->locks; +} + +#if REISER4_DEBUG_OUTPUT +/* + * print human readable information about locks held by the reiser4 context. + */ +void +print_lock_counters(const char *prefix, const lock_counters_info * info) +{ + printk("%s: jnode: %i, tree: %i (r:%i,w:%i), dk: %i (r:%i,w:%i)\n" + "jload: %i, " + "txnh: %i, atom: %i, stack: %i, txnmgr: %i, " + "ktxnmgrd: %i, fq: %i, reiser4_sb: %i\n" + "inode: %i, " + "cbk_cache: %i (r:%i,w%i), " + "epoch: %i, eflush: %i, " + "zlock: %i (r:%i, w:%i)\n" + "spin: %i, long: %i inode_sem: (r:%i,w:%i)\n" + "d: %i, x: %i, t: %i\n", prefix, + info->spin_locked_jnode, + info->rw_locked_tree, info->read_locked_tree, + info->write_locked_tree, + + info->rw_locked_dk, info->read_locked_dk, info->write_locked_dk, + + info->spin_locked_jload, + info->spin_locked_txnh, + info->spin_locked_atom, info->spin_locked_stack, + info->spin_locked_txnmgr, info->spin_locked_ktxnmgrd, + info->spin_locked_fq, info->spin_locked_super, + info->spin_locked_inode_object, + + info->rw_locked_cbk_cache, + info->read_locked_cbk_cache, + info->write_locked_cbk_cache, + + info->spin_locked_epoch, + info->spin_locked_super_eflush, + + info->rw_locked_zlock, + info->read_locked_zlock, + info->write_locked_zlock, + + info->spin_locked, + info->long_term_locked_znode, + info->inode_sem_r, info->inode_sem_w, + info->d_refs, info->x_refs, info->t_refs); +} + +/* + * return true, iff no locks are held. + */ +int +no_counters_are_held(void) +{ + lock_counters_info *counters; + + counters = lock_counters(); + return + (counters->rw_locked_zlock == 0) && + (counters->read_locked_zlock == 0) && + (counters->write_locked_zlock == 0) && + (counters->spin_locked_jnode == 0) && + (counters->rw_locked_tree == 0) && + (counters->read_locked_tree == 0) && + (counters->write_locked_tree == 0) && + (counters->rw_locked_dk == 0) && + (counters->read_locked_dk == 0) && + (counters->write_locked_dk == 0) && + (counters->spin_locked_txnh == 0) && + (counters->spin_locked_atom == 0) && + (counters->spin_locked_stack == 0) && + (counters->spin_locked_txnmgr == 0) && + (counters->spin_locked_inode_object == 0) && + (counters->spin_locked == 0) && + (counters->long_term_locked_znode == 0) && + (counters->inode_sem_r == 0) && + (counters->inode_sem_w == 0); +} + +/* + * return true, iff transaction commit can be done under locks held by the + * current thread. + */ +int +commit_check_locks(void) +{ + lock_counters_info *counters; + int inode_sem_r; + int inode_sem_w; + int result; + + /* + * inode's read/write semaphore is the only reiser4 lock that can be + * held during commit. + */ + + counters = lock_counters(); + inode_sem_r = counters->inode_sem_r; + inode_sem_w = counters->inode_sem_w; + + counters->inode_sem_r = counters->inode_sem_w = 0; + result = no_counters_are_held(); + counters->inode_sem_r = inode_sem_r; + counters->inode_sem_w = inode_sem_w; + return result; +} + +/* REISER4_DEBUG_OUTPUT */ +#endif + +/* REISER4_DEBUG_SPIN_LOCKS */ +#endif + +/* + * check that all bits specified by @flags are set in ->debug_flags of the + * super block. + */ +reiser4_internal int +reiser4_are_all_debugged(struct super_block *super, __u32 flags) +{ + return (get_super_private(super)->debug_flags & flags) == flags; +} + +/* + * check that some bits specified by @flags are set in ->debug_flags of the + * super block. + */ +reiser4_internal int +reiser4_is_debugged(struct super_block *super, __u32 flag) +{ + return get_super_private(super)->debug_flags & flag; +} + +#if REISER4_TRACE +/* tracing setup: global trace flags stored in global variable plus + per-thread trace flags plus per-fs trace flags. + */ +__u32 get_current_trace_flags(void) +{ + __u32 flags; + reiser4_context *ctx; + + flags = 0; + ctx = get_current_context_check(); + if (ctx) { + flags |= ctx->trace_flags; + flags |= get_super_private(ctx->super)->trace_flags; + } + return flags; +} +#endif + +#if REISER4_LOG + +/* log flags are stored in super block */ +__u32 get_current_log_flags(void) +{ + __u32 flags; + reiser4_context *ctx; + + flags = 0; + ctx = get_current_context_check(); + if (ctx) + flags = get_super_private(ctx->super)->log_flags; + return flags; +} + +/* oid of file page events of which are to be logged */ +__u32 get_current_oid_to_log(void) +{ + __u32 oid; + reiser4_context *ctx; + + oid = 0; + ctx = get_current_context_check(); + if (ctx) + oid = get_super_private(ctx->super)->oid_to_log; + return oid; +} + +#endif + +/* allocate memory. This calls kmalloc(), performs some additional checks, and + keeps track of how many memory was allocated on behalf of current super + block. */ +reiser4_internal void * +reiser4_kmalloc(size_t size /* number of bytes to allocate */ , + int gfp_flag /* allocation flag */ ) +{ + void *result; + + assert("nikita-3009", ergo(gfp_flag & __GFP_WAIT, schedulable())); + + result = kmalloc(size, gfp_flag); + if (REISER4_DEBUG && result != NULL) { + unsigned int usedsize; + reiser4_super_info_data *sbinfo; + + usedsize = ksize(result); + + sbinfo = get_current_super_private(); + + assert("nikita-3459", usedsize >= size); + assert("nikita-1407", sbinfo != NULL); + reiser4_spin_lock_sb(sbinfo); + ON_DEBUG(sbinfo->kmalloc_allocated += usedsize); + reiser4_spin_unlock_sb(sbinfo); + } + return result; +} + +/* release memory allocated by reiser4_kmalloc() and update counter. */ +reiser4_internal void +reiser4_kfree(void *area /* memory to from */) +{ + assert("nikita-1410", area != NULL); + return reiser4_kfree_in_sb(area, reiser4_get_current_sb()); +} + +/* release memory allocated by reiser4_kmalloc() for the specified + * super-block. This is useful when memory is released outside of reiser4 + * context */ +reiser4_internal void +reiser4_kfree_in_sb(void *area /* memory to from */, struct super_block *sb) +{ + assert("nikita-2729", area != NULL); + if (REISER4_DEBUG) { + unsigned int size; + reiser4_super_info_data *sbinfo; + + size = ksize(area); + + sbinfo = get_super_private(sb); + + reiser4_spin_lock_sb(sbinfo); + assert("nikita-2730", sbinfo->kmalloc_allocated >= (int) size); + ON_DEBUG(sbinfo->kmalloc_allocated -= size); + reiser4_spin_unlock_sb(sbinfo); + } + kfree(area); +} + + +#if defined(CONFIG_REISER4_NOOPT) +void __you_cannot_kmalloc_that_much(void) +{ + BUG(); +} +#endif + +#if REISER4_DEBUG + +/* + * fill "error site" in the current reiser4 context. See comment before RETERR + * macro for more details. + */ +void +return_err(int code, const char *file, int line) +{ + if (code < 0 && is_in_reiser4_context()) { + reiser4_context *ctx = get_current_context(); + + if (ctx != NULL) { + fill_backtrace(&ctx->err.path, + REISER4_BACKTRACE_DEPTH, 0); + ctx->err.code = code; + ctx->err.file = file; + ctx->err.line = line; + } + } +} + +/* + * report error information recorder by return_err(). + */ +void +report_err(void) +{ + reiser4_context *ctx = get_current_context_check(); + + if (ctx != NULL) { + if (ctx->err.code != 0) { +#ifdef CONFIG_FRAME_POINTER + int i; + for (i = 0; i < REISER4_BACKTRACE_DEPTH ; ++ i) + printk("0x%p ", ctx->err.path.trace[i]); + printk("\n"); +#endif + printk("code: %i at %s:%i\n", + ctx->err.code, ctx->err.file, ctx->err.line); + } + } +} + +#ifdef CONFIG_FRAME_POINTER + +extern int kswapd(void *); + +#include +#include "ktxnmgrd.h" +#include "repacker.h" + +/* + * true iff @addr is between @start and @end + */ +static int is_addr_in(void *addr, void *start, void *end) +{ + return start < addr && addr < end; +} + +/* + * stack back-tracing. Also see comments before REISER4_BACKTRACE_DEPTH in + * debug.h. + * + * Stack beck-trace is collected through __builtin_return_address() gcc + * builtin, which requires kernel to be compiled with frame pointers + * (CONFIG_FRAME_POINTER). Unfortunately, __builtin_return_address() doesn't + * provide means to detect when bottom of the stack is reached, and just + * crashed when trying to access non-existent frame. + * + * is_last_frame() function works around this (also see more advanced version + * in the proc-sleep patch that requires modification of core kernel code). + * + * This functions checks for common cases trying to detect that last stack + * frame was reached. + */ +static int is_last_frame(void *addr) +{ + if (addr == NULL) + return 1; + if (is_addr_in(addr, kswapd, wakeup_kswapd)) + return 1; + else if (is_addr_in(addr, reiser4_repacker, repacker_d)) + return 1; + else if (is_addr_in(addr, init_ktxnmgrd_context, ktxnmgrd_kick)) + return 1; + else if (is_addr_in(addr, init_entd_context, done_entd_context)) + return 1; + else if (!kernel_text_address((unsigned long)addr)) + return 1; + else + return 0; +} + +/* + * fill stack back-trace. + */ +reiser4_internal void +fill_backtrace(backtrace_path *path, int depth, int shift) +{ + int i; + void *addr; + + cassert(REISER4_BACKTRACE_DEPTH == 4); + assert("nikita-3229", shift < 6); + + /* long live Duff! */ + +#define FRAME(nr) \ + case (nr): \ + addr = __builtin_return_address((nr) + 2); \ + break + + xmemset(path, 0, sizeof *path); + addr = NULL; + /* + * we need this silly loop, because __builtin_return_address() only + * accepts _constant_ arguments. It reminds of the duff device + * (http://www.faqs.org/docs/jargon/D/Duff's-device.html) which + * explains the reference above. + */ + for (i = 0; i < depth; ++ i) { + switch(i + shift) { + FRAME(0); + FRAME(1); + FRAME(2); + FRAME(3); + FRAME(4); + FRAME(5); + FRAME(6); + FRAME(7); + FRAME(8); + FRAME(9); + FRAME(10); + default: + impossible("nikita-3230", "everything is wrong"); + } + path->trace[i] = addr; + if (is_last_frame(addr)) + break; + } +} +#endif + +/* + * assert() macro calls this function on each invocation. This is convenient + * place to put some debugging code that has to be executed very + * frequently. _Very_. + */ +void call_on_each_assert(void) +{ + return; + /* + * DON'T USE ASSERTIONS HERE :) + */ + if (is_in_reiser4_context()) { + reiser4_super_info_data *sinfo; + reiser4_context *ctx; + + ctx = (reiser4_context *) current->journal_info; + sinfo = ctx->super->s_fs_info; + /* put checks here */ + } +} + +/* REISER4_DEBUG */ +#endif + +#if KERNEL_DEBUGGER +/* + * this functions just drops into kernel debugger. It is a convenient place to + * put breakpoint in. + */ +void debugtrap(void) +{ + /* do nothing. Put break point here. */ +#if defined(CONFIG_KGDB) && !defined(CONFIG_REISER4_FS_MODULE) + extern void breakpoint(void); + breakpoint(); +#endif +} +#endif + + +/* debugging tool + use clog_op to make a record + use print_clog to see last CLOG_LENGTH record + */ +#define CLOG_LENGTH 256 +static spinlock_t clog_lock = SPIN_LOCK_UNLOCKED; + +typedef struct { + int id; + pid_t pid; + int op; + void *data1; + void *data2; +} clog_t; + +clog_t clog[CLOG_LENGTH]; + +int clog_start = 0; +int clog_length = 0; +int clog_id = 0; + +void +clog_op(int op, void *data1, void *data2) +{ + spin_lock(&clog_lock); + + if (clog_length == CLOG_LENGTH) { + clog[clog_start].id = clog_id ++; + clog[clog_start].op = op; + clog[clog_start].pid = current->pid; + clog[clog_start].data1 = data1; + clog[clog_start].data2 = data2; + clog_start ++; + clog_start %= CLOG_LENGTH; + } else { + assert("vs-1672", clog_start == 0); + clog[clog_length].id = clog_id ++; + clog[clog_length].op = op; + clog[clog_length].pid = current->pid; + clog[clog_length].data1 = data1; + clog[clog_length].data2 = data2; + clog_length ++; + } + + spin_unlock(&clog_lock); +} + +static const char * +op2str(int op) +{ + static const char *op_names[OP_NUM] = { + "get-user-page", + "put_user-page", + "ex-write-in", + "ex-write-out", + "readp-in", + "readp-out", + "ex-write-in-nr-locks", + "ex-write-out-nr-locks", + "link-object", + "unlink-object" + }; + assert("vs-1673", op < OP_NUM); + return op_names[op]; +} + +void +print_clog(void) +{ + int i, j; + + j = clog_start; + for (i = 0; i < clog_length; i ++) { + printk("%d(%d): id %d: pid %d, op %s, data1 %p, data2 %p\n", + i, j, clog[j].id, clog[j].pid, op2str(clog[j].op), clog[j].data1, clog[j].data2); + j ++; + j %= CLOG_LENGTH; + } + printk("clog length %d\n", clog_length); +} + +#if 0 +void +print_symname(unsigned long address) +{ + char *module; + const char *name; + char namebuf[128]; + unsigned long offset; + unsigned long size; + + name = kallsyms_lookup(address, &size, &offset, &module, namebuf); + if (name != NULL) + printk(" %s[%lx/%lx]", name, offset, size); +} +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/debug.h 2004-09-03 00:57:05.007278040 -0700 @@ -0,0 +1,559 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Declarations of debug macros. */ + +#if !defined( __FS_REISER4_DEBUG_H__ ) +#define __FS_REISER4_DEBUG_H__ + +#include "forward.h" +#include "reiser4.h" + + +/* generic function to produce formatted output, decorating it with + whatever standard prefixes/postfixes we want. "Fun" is a function + that will be actually called, can be printk, panic etc. + This is for use by other debugging macros, not by users. */ +#define DCALL(lev, fun, reperr, label, format, ...) \ +({ \ + reiser4_print_prefix(lev, reperr, label, \ + __FUNCTION__, __FILE__, __LINE__); \ + fun(lev format "\n" , ## __VA_ARGS__); \ +}) + +/* + * cause kernel to crash + */ +#define reiser4_panic(mid, format, ...) \ + DCALL("", reiser4_do_panic, 1, mid, format , ## __VA_ARGS__) + +/* print message with indication of current process, file, line and + function */ +#define reiser4_log(label, format, ...) \ + DCALL(KERN_DEBUG, printk, 0, label, format , ## __VA_ARGS__) + +/* Assertion checked during compilation. + If "cond" is false (0) we get duplicate case label in switch. + Use this to check something like famous + cassert (sizeof(struct reiserfs_journal_commit) == 4096) ; + in 3.x journal.c. If cassertion fails you get compiler error, + so no "maintainer-id". +*/ +#define cassert(cond) ({ switch(-1) { case (cond): case 0: break; } }) + +#define noop do {;} while(0) + +#if REISER4_DEBUG +/* version of info that only actually prints anything when _d_ebugging + is on */ +#define dinfo(format, ...) printk(format , ## __VA_ARGS__) +/* macro to catch logical errors. Put it into `default' clause of + switch() statement. */ +#define impossible(label, format, ...) \ + reiser4_panic(label, "impossible: " format , ## __VA_ARGS__) +/* assert assures that @cond is true. If it is not, reiser4_panic() is + called. Use this for checking logical consistency and _never_ call + this to check correctness of external data: disk blocks and user-input . */ +#define assert(label, cond) \ +({ \ + /* call_on_each_assert(); */ \ + if (cond) { \ + /* put negated check to avoid using !(cond) that would lose \ + * warnings for things like assert(a = b); */ \ + ; \ + } else { \ + DEBUGON(1); \ + reiser4_panic(label, "assertion failed: %s", #cond); \ + } \ +}) + +/* like assertion, but @expr is evaluated even if REISER4_DEBUG is off. */ +#define check_me( label, expr ) assert( label, ( expr ) ) + +#define ON_DEBUG( exp ) exp + +extern int schedulable(void); +extern void call_on_each_assert(void); + +#else + +#define dinfo( format, args... ) noop +#define impossible( label, format, args... ) noop +#define assert( label, cond ) noop +#define check_me( label, expr ) ( ( void ) ( expr ) ) +#define ON_DEBUG( exp ) +#define schedulable() might_sleep() + +/* REISER4_DEBUG */ +#endif + +#if REISER4_DEBUG_SPIN_LOCKS +/* per-thread information about lock acquired by this thread. Used by lock + * ordering checking in spin_macros.h */ +typedef struct lock_counters_info { + int rw_locked_tree; + int read_locked_tree; + int write_locked_tree; + + int rw_locked_dk; + int read_locked_dk; + int write_locked_dk; + + int rw_locked_cbk_cache; + int read_locked_cbk_cache; + int write_locked_cbk_cache; + + int rw_locked_zlock; + int read_locked_zlock; + int write_locked_zlock; + + int spin_locked_jnode; + int spin_locked_jload; + int spin_locked_txnh; + int spin_locked_atom; + int spin_locked_stack; + int spin_locked_txnmgr; + int spin_locked_ktxnmgrd; + int spin_locked_fq; + int spin_locked_super; + int spin_locked_inode_object; + int spin_locked_epoch; + int spin_locked_super_eflush; + int spin_locked; + int long_term_locked_znode; + + int inode_sem_r; + int inode_sem_w; + + int d_refs; + int x_refs; + int t_refs; +} lock_counters_info; + +extern lock_counters_info *lock_counters(void); +#define IN_CONTEXT(a, b) (is_in_reiser4_context() ? (a) : (b)) + +/* increment lock-counter @counter, if present */ +#define LOCK_CNT_INC(counter) IN_CONTEXT(++(lock_counters()->counter), 0) + +/* decrement lock-counter @counter, if present */ +#define LOCK_CNT_DEC(counter) IN_CONTEXT(--(lock_counters()->counter), 0) + +/* check that lock-counter is zero. This is for use in assertions */ +#define LOCK_CNT_NIL(counter) IN_CONTEXT(lock_counters()->counter == 0, 1) + +/* check that lock-counter is greater than zero. This is for use in + * assertions */ +#define LOCK_CNT_GTZ(counter) IN_CONTEXT(lock_counters()->counter > 0, 1) + +/* REISER4_DEBUG_SPIN_LOCKS */ +#else + +/* no-op versions on the above */ + +typedef struct lock_counters_info { +} lock_counters_info; +#define lock_counters() ((lock_counters_info *)NULL) +#define LOCK_CNT_INC(counter) noop +#define LOCK_CNT_DEC(counter) noop +#define LOCK_CNT_NIL(counter) (1) +#define LOCK_CNT_GTZ(counter) (1) +/* REISER4_DEBUG_SPIN_LOCKS */ +#endif + +/* + * back-trace recording. In several places in reiser4 we want to record stack + * back-trace for debugging purposes. This functionality is only supported + * when kernel was configured with CONFIG_FRAME_POINTER option. + */ + +#ifdef CONFIG_FRAME_POINTER + +/* + * how many stack frames to record in back-trace. + * + * update debug.c:fill_backtrace() if you change this + */ +#define REISER4_BACKTRACE_DEPTH (4) + +/* + * data type to store stack back-trace + */ +typedef struct { + void *trace[REISER4_BACKTRACE_DEPTH]; +} backtrace_path; + +extern void fill_backtrace(backtrace_path *path, int depth, int shift); +#else + +/* no-op versions on the above */ + +typedef struct {} backtrace_path; +#define fill_backtrace(path, depth, shift) noop + +#endif + + +/* flags controlling debugging behavior. Are set through debug_flags=N mount + option. */ +typedef enum { + /* print a lot of information during panic. When this is on all jnodes + * are listed. This can be *very* large output. Usually you don't want + * this. Especially over serial line. */ + REISER4_VERBOSE_PANIC = 0x00000001, + /* print a lot of information during umount */ + REISER4_VERBOSE_UMOUNT = 0x00000002, + /* print gathered statistics on umount */ + REISER4_STATS_ON_UMOUNT = 0x00000004, + /* check node consistency */ + REISER4_CHECK_NODE = 0x00000008 +} reiser4_debug_flags; + +extern int reiser4_are_all_debugged(struct super_block *super, __u32 flags); +extern int reiser4_is_debugged(struct super_block *super, __u32 flag); + +extern int is_in_reiser4_context(void); + +/* + * evaluate expression @e only if with reiser4 context + */ +#define ON_CONTEXT(e) do { \ + if(is_in_reiser4_context()) { \ + e; \ + } } while(0) + +/* + * evaluate expression @e only when within reiser4_context and debugging is + * on. + */ +#define ON_DEBUG_CONTEXT( e ) ON_DEBUG( ON_CONTEXT( e ) ) + +#if REISER4_DEBUG_MODIFY +/* + * evaluate expression @exp only if REISER4_DEBUG_MODIFY mode is on. + */ +#define ON_DEBUG_MODIFY( exp ) exp +#else +#define ON_DEBUG_MODIFY( exp ) +#endif + +/* + * complain about unexpected function result and crash. Used in "default" + * branches of switch statements and alike to assert that invalid results are + * not silently ignored. + */ +#define wrong_return_value( label, function ) \ + impossible( label, "wrong return value from " function ) + +/* Issue warning message to the console */ +#define warning( label, format, ... ) \ + DCALL( KERN_WARNING, \ + printk, 1, label, "WARNING: " format , ## __VA_ARGS__ ) + +/* mark not yet implemented functionality */ +#define not_yet( label, format, ... ) \ + reiser4_panic( label, "NOT YET IMPLEMENTED: " format , ## __VA_ARGS__ ) + +#if REISER4_TRACE +/* helper macro for tracing, see trace_stamp() below. */ +#define IF_TRACE(flags, e) \ + if(get_current_trace_flags() & (flags)) e +#else +#define IF_TRACE(flags, e) noop +#endif + +/* just print where we are: file, function, line */ +#define trace_stamp( f ) IF_TRACE( f, reiser4_log( "trace", "" ) ) +/* print value of "var" */ +#define trace_var( f, format, var ) \ + IF_TRACE( f, reiser4_log( "trace", #var ": " format, var ) ) +/* print output only if appropriate trace flag(s) is on */ +#define ON_TRACE( f, ... ) IF_TRACE(f, printk(__VA_ARGS__)) + +/* tracing flags. */ +typedef enum { + /* trace nothing */ + NO_TRACE = 0, + /* trace vfs interaction functions from vfs_ops.c */ + TRACE_VFS_OPS = (1 << 0), /* 0x00000001 */ + /* trace plugin handling functions */ + TRACE_PLUGINS = (1 << 1), /* 0x00000002 */ + /* trace tree traversals */ + TRACE_TREE = (1 << 2), /* 0x00000004 */ + /* trace znode manipulation functions */ + TRACE_ZNODES = (1 << 3), /* 0x00000008 */ + /* trace node layout functions */ + TRACE_NODES = (1 << 4), /* 0x00000010 */ + /* trace directory functions */ + TRACE_DIR = (1 << 5), /* 0x00000020 */ + /* trace flush code verbosely */ + TRACE_FLUSH_VERB = (1 << 6), /* 0x00000040 */ + /* trace flush code */ + TRACE_FLUSH = (1 << 7), /* 0x00000080 */ + /* trace carry */ + TRACE_CARRY = (1 << 8), /* 0x00000100 */ + /* trace how tree (web) of znodes if maintained through tree + balancings. */ + TRACE_ZWEB = (1 << 9), /* 0x00000200 */ + /* trace transactions. */ + TRACE_TXN = (1 << 10), /* 0x00000400 */ + /* trace object id allocation/releasing */ + TRACE_OIDS = (1 << 11), /* 0x00000800 */ + /* trace item shifts */ + TRACE_SHIFT = (1 << 12), /* 0x00001000 */ + /* trace page cache */ + TRACE_PCACHE = (1 << 13), /* 0x00002000 */ + /* trace extents */ + TRACE_EXTENTS = (1 << 14), /* 0x00004000 */ + /* trace locks */ + TRACE_LOCKS = (1 << 15), /* 0x00008000 */ + /* trace coords */ + TRACE_COORDS = (1 << 16), /* 0x00010000 */ + /* trace read-IO functions */ + TRACE_IO_R = (1 << 17), /* 0x00020000 */ + /* trace write-IO functions */ + TRACE_IO_W = (1 << 18), /* 0x00040000 */ + + /* trace log writing */ + TRACE_LOG = (1 << 19), /* 0x00080000 */ + + /* trace journal replaying */ + TRACE_REPLAY = (1 << 20), /* 0x00100000 */ + + /* trace space allocation */ + TRACE_ALLOC = (1 << 21), /* 0x00200000 */ + + /* trace space reservation */ + TRACE_RESERVE = (1 << 22), /* 0x00400000 */ + + /* trace emergency flush */ + TRACE_EFLUSH = (1 << 23), /* 0x00800000 */ + + /* trace ctails */ + TRACE_CTAIL = (1 << 24), /* 0x01000000 */ + + TRACE_PARSE = (1 << 25), /* 0x02000000 */ + + TRACE_CAPTURE_COPY = (1 << 26), /* 0x04000000 */ + + TRACE_EXTENT_ALLOC = (1 << 27), /* 0x08000000 */ + + TRACE_CAPTURE_ANONYMOUS = (1 << 28), /* 0x10000000 */ + + /* vague section: used to trace bugs. Use it to issue optional prints + at arbitrary points of code. */ + TRACE_BUG = (1 << 31), /* 0x80000000 */ + + /* trace everything above */ + TRACE_ALL = 0xffffffffu +} reiser4_trace_flags; + +#if REISER4_LOG +/* helper macro for tracing, see trace_stamp() below. */ +#define IF_LOG(flags, e) \ + if(get_current_log_flags() & (flags)) e +#else +#define IF_LOG(flags, e) noop +#endif + +/* log only if appropriate log flag(s) is on */ +#define ON_LOG( f, ... ) IF_LOG(f, printk(__VA_ARGS__)) + +typedef enum { + WRITE_NODE_LOG = (1 << 0), /* log [zj]node operations */ + WRITE_PAGE_LOG = (1 << 1), /* log make_extent calls */ + WRITE_IO_LOG = (1 << 2), /* log i/o requests */ + WRITE_TREE_LOG = (1 << 3), /* log internal tree operations */ + WRITE_SYSCALL_LOG = (1 << 4), /* log system calls */ + READAHEAD_LOG = (1 << 5), /* log read-ahead activity */ + ALLOC_EXTENT_LOG = (1 << 6), /* log extent allocation */ + LOG_FILE_PAGE_EVENT = (1 << 7) /* log events happened to certain file */ +} reiser4_log_flags; + + +extern void reiser4_do_panic(const char *format, ...) +__attribute__ ((noreturn, format(printf, 1, 2))); + +extern void reiser4_print_prefix(const char *level, int reperr, const char *mid, + const char *function, + const char *file, int lineno); + +extern int preempt_point(void); +extern void reiser4_print_stats(void); + +extern void *reiser4_kmalloc(size_t size, int gfp_flag); +extern void reiser4_kfree(void *area); +extern void reiser4_kfree_in_sb(void *area, struct super_block *sb); +extern __u32 get_current_trace_flags(void); +extern __u32 get_current_log_flags(void); +extern __u32 get_current_oid_to_log(void); + +#if REISER4_DEBUG_OUTPUT && REISER4_DEBUG_SPIN_LOCKS +extern void print_lock_counters(const char *prefix, + const lock_counters_info * info); +extern int no_counters_are_held(void); +extern int commit_check_locks(void); +#else +#define print_lock_counters(p, i) noop +#define no_counters_are_held() (1) +#define commit_check_locks() (1) +#endif + +#define REISER4_STACK_ABORT (8192 - sizeof(struct thread_info) - 30) +#define REISER4_STACK_GAP (REISER4_STACK_ABORT - 100) + +#if REISER4_DEBUG_MEMCPY +extern void *xmemcpy(void *dest, const void *src, size_t n); +extern void *xmemmove(void *dest, const void *src, size_t n); +extern void *xmemset(void *s, int c, size_t n); +#else +#define xmemcpy( d, s, n ) memcpy( ( d ), ( s ), ( n ) ) +#define xmemmove( d, s, n ) memmove( ( d ), ( s ), ( n ) ) +#define xmemset( s, c, n ) memset( ( s ), ( c ), ( n ) ) +#endif + +/* true if @i is power-of-two. Useful for rate-limited warnings, etc. */ +#define IS_POW(i) \ +({ \ + typeof(i) __i; \ + \ + __i = (i); \ + !(__i & (__i - 1)); \ +}) + +#define KERNEL_DEBUGGER (1) + +#if KERNEL_DEBUGGER +/* + * Check condition @cond and drop into kernel debugger (kgdb) if it's true. If + * kgdb is not compiled in, do nothing. + */ +#define DEBUGON(cond) \ +({ \ + extern void debugtrap(void); \ + \ + if (unlikely(cond)) \ + debugtrap(); \ +}) +#else +#define DEBUGON(cond) noop +#endif + +/* + * Error code tracing facility. (Idea is borrowed from XFS code.) + * + * Suppose some strange and/or unexpected code is returned from some function + * (for example, write(2) returns -EEXIST). It is possible to place a + * breakpoint in the reiser4_write(), but it is too late here. How to find out + * in what particular place -EEXIST was generated first? + * + * In reiser4 all places where actual error codes are produced (that is, + * statements of the form + * + * return -EFOO; // (1), or + * + * result = -EFOO; // (2) + * + * are replaced with + * + * return RETERR(-EFOO); // (1a), and + * + * result = RETERR(-EFOO); // (2a) respectively + * + * RETERR() macro fills a backtrace in reiser4_context. This back-trace is + * printed in error and warning messages. Moreover, it's possible to put a + * conditional breakpoint in return_err (low-level function called by RETERR() + * to do the actual work) to break into debugger immediately when particular + * error happens. + * + */ + +#if REISER4_DEBUG + +/* + * data-type to store information about where error happened ("error site"). + */ +typedef struct err_site { + backtrace_path path; /* stack back trace of error */ + int code; /* error code */ + const char *file; /* source file, filled by __FILE__ */ + int line; /* source file line, filled by __LINE__ */ +} err_site; + +extern void return_err(int code, const char *file, int line); +extern void report_err(void); + +/* + * fill &get_current_context()->err_site with error information. + */ +#define RETERR(code) \ +({ \ + typeof(code) __code; \ + \ + __code = (code); \ + return_err(__code, __FILE__, __LINE__); \ + __code; \ +}) + +#else + +/* + * no-op versions of the above + */ + +typedef struct err_site {} err_site; +#define RETERR(code) code +#define report_err() noop +#endif + +#if REISER4_LARGE_KEY +/* + * conditionally compile arguments only if REISER4_LARGE_KEY is on. + */ +#define ON_LARGE_KEY(...) __VA_ARGS__ +#else +#define ON_LARGE_KEY(...) +#endif + +#if REISER4_ALL_IN_ONE +/* + * declarator used by REISER4_ALL_IN_ONE mode. Every reiser4 function that is + * not used externally (that is, not used by non-reiser4 code) should be + * tagged with this. Normally it expands to nothing. In REISER4_ALL_IN_ONE + * expands to statics allowing compiler to perform better optimization. + */ +#define reiser4_internal static +#else +#define reiser4_internal +#endif + +/* operations to clog */ +/* debugging re-enterance */ + +#define GET_USER_PAGES 0 +#define PUT_USER_PAGES 1 +#define EXTENT_WRITE_IN 2 +#define EXTENT_WRITE_OUT 3 +#define READPAGE_IN 4 +#define READPAGE_OUT 5 +#define EXTENT_WRITE_IN2 6 +#define EXTENT_WRITE_OUT2 7 +#define LINK_OBJECT 8 +#define UNLINK_OBJECT 9 + +#define OP_NUM 10 + +void clog_op(int op, void *, void *); +void print_clog(void); + +/* __FS_REISER4_DEBUG_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/dformat.h 2004-09-03 00:57:05.008277888 -0700 @@ -0,0 +1,164 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Formats of on-disk data and conversion functions. */ + +/* put all item formats in the files describing the particular items, + our model is, everything you need to do to add an item to reiser4, + (excepting the changes to the plugin that uses the item which go + into the file defining that plugin), you put into one file. */ +/* Data on disk are stored in little-endian format. + To declare fields of on-disk structures, use d8, d16, d32 and d64. + d??tocpu() and cputod??() to convert. */ + +#if !defined( __FS_REISER4_DFORMAT_H__ ) +#define __FS_REISER4_DFORMAT_H__ + + +#include +#include +#include + +/* our default disk byteorder is little endian */ + +#if defined( __LITTLE_ENDIAN ) +#define CPU_IN_DISK_ORDER (1) +#else +#define CPU_IN_DISK_ORDER (0) +#endif + +/* code on-disk data-types as structs with a single field + to rely on compiler type-checking. Like include/asm-i386/page.h */ +typedef struct d8 { + __u8 datum; +} d8 __attribute__ ((aligned(1))); +typedef struct d16 { + __u16 datum; +} d16 __attribute__ ((aligned(2))); +typedef struct d32 { + __u32 datum; +} d32 __attribute__ ((aligned(4))); +typedef struct d64 { + __u64 datum; +} d64 __attribute__ ((aligned(8))); + +#define PACKED __attribute__((packed)) + +static inline __u8 +d8tocpu(const d8 * ondisk /* on-disk value to convert */ ) +{ + return ondisk->datum; +} + +static inline __u16 +d16tocpu(const d16 * ondisk /* on-disk value to convert */ ) +{ + return __le16_to_cpu(get_unaligned(&ondisk->datum)); +} + +static inline __u32 +d32tocpu(const d32 * ondisk /* on-disk value to convert */ ) +{ + return __le32_to_cpu(get_unaligned(&ondisk->datum)); +} + +static inline __u64 +d64tocpu(const d64 * ondisk /* on-disk value to convert */ ) +{ + return __le64_to_cpu(get_unaligned(&ondisk->datum)); +} + +static inline d8 * +cputod8(unsigned int oncpu /* CPU value to convert */ , + d8 * ondisk /* result */ ) +{ + assert("nikita-1264", oncpu < 0x100); + put_unaligned(oncpu, &ondisk->datum); + return ondisk; +} + +static inline d16 * +cputod16(unsigned int oncpu /* CPU value to convert */ , + d16 * ondisk /* result */ ) +{ + assert("nikita-1265", oncpu < 0x10000); + put_unaligned(__cpu_to_le16(oncpu), &ondisk->datum); + return ondisk; +} + +static inline d32 * +cputod32(__u32 oncpu /* CPU value to convert */ , + d32 * ondisk /* result */ ) +{ + put_unaligned(__cpu_to_le32(oncpu), &ondisk->datum); + return ondisk; +} + +static inline d64 * +cputod64(__u64 oncpu /* CPU value to convert */ , + d64 * ondisk /* result */ ) +{ + put_unaligned(__cpu_to_le64(oncpu), &ondisk->datum); + return ondisk; +} + +/* data-type for block number on disk: these types enable changing the block + size to other sizes, but they are only a start. Suppose we wanted to + support 48bit block numbers. The dblock_nr blk would be changed to "short + blk[3]". The block_nr type should remain an integral type greater or equal + to the dblock_nr type in size so that CPU arithmetic operations work. */ +typedef __u64 reiser4_block_nr; + +/* data-type for block number on disk, disk format */ +union reiser4_dblock_nr { + d64 blk; +}; + +static inline reiser4_block_nr +dblock_to_cpu(const reiser4_dblock_nr * dblock) +{ + return d64tocpu(&dblock->blk); +} + +static inline void +cpu_to_dblock(reiser4_block_nr block, reiser4_dblock_nr * dblock) +{ + cputod64(block, &dblock->blk); +} + +/* true if disk addresses are the same */ +static inline int +disk_addr_eq(const reiser4_block_nr * b1 /* first block + * number to + * compare */ , + const reiser4_block_nr * b2 /* second block + * number to + * compare */ ) +{ + assert("nikita-1033", b1 != NULL); + assert("nikita-1266", b2 != NULL); + + return !memcmp(b1, b2, sizeof *b1); +} + +/* structure of master reiser4 super block */ +typedef struct reiser4_master_sb { + char magic[16]; /* "ReIsEr4" */ + d16 disk_plugin_id; /* id of disk layout plugin */ + d16 blocksize; + char uuid[16]; /* unique id */ + char label[16]; /* filesystem label */ + d64 diskmap; /* location of the diskmap. 0 if not present */ +} reiser4_master_sb; + +/* __FS_REISER4_DFORMAT_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/diskmap.c 2004-09-03 00:57:05.009277736 -0700 @@ -0,0 +1,76 @@ +/* Copyright 2003 by Hans Reiser, licensing governed by reiser4/README */ +/* Functions to deal with diskmap storage - read-only storage (currently can only be + set via fs-creation process) for use by various plugins */ + + +#include "debug.h" +#include "super.h" +#include "diskmap.h" + +#include + +/* Looks through chain of diskmap blocks, looking for table entry where label and parameter + patch passed in "label" and "parameter" + Returns 0 on success, -1 if nothing was found or error have occurred. */ +reiser4_internal int +reiser4_get_diskmap_value( u32 label, u32 parameter, u64 *value) +{ + struct super_block *sb = reiser4_get_current_sb(); + int retval = -1; + + assert("green-2006", label != REISER4_FIXMAP_END_LABEL && label != REISER4_FIXMAP_NEXT_LABEL); + + if ( get_super_private(sb)->diskmap_block ) { /* If there is diskmap table, we need to read and parse it */ + struct buffer_head *diskmap_bh; + struct reiser4_diskmap *diskmap; + int i = 0; + + diskmap_bh = sb_bread(sb, get_super_private(sb)->diskmap_block); +search_table: + if ( !diskmap_bh ) { + warning("green-2005", "Cannot read diskmap while doing bitmap checks"); + return -1; + } + + diskmap = (struct reiser4_diskmap *) diskmap_bh->b_data; + if ( strncmp(diskmap->magic, REISER4_FIXMAP_MAGIC, sizeof(REISER4_FIXMAP_MAGIC)-1 ) ) { + /* Wrong magic */ + brelse(diskmap_bh); + warning("green-2004", "diskmap is specified, but its magic is wrong"); + return -1; + } + + /* Since entries in tables are sorted, we iterate until we hit item that we are looking for, + or we reach end of whole fixmap or end of current block */ + while (((d32tocpu(&diskmap->table[i].label) <= label) && + (d32tocpu(&diskmap->table[i].parameter) < parameter)) && + /* Also check that we do not fall out of current block */ + ((sb->s_blocksize - sizeof(diskmap->magic))/sizeof(diskmap->table[0]) >= i)) + i++; + + if ( i > (sb->s_blocksize - sizeof(diskmap->magic))/sizeof(diskmap->table[0]) ) { + warning("green-2004", "diskmap block %Ld is not properly terminated", (long long)diskmap_bh->b_blocknr); + brelse(diskmap_bh); + return -1; + } + + /* Is this last entry in current table that holds disk block with more data ? */ + if ( d32tocpu(&diskmap->table[i].label) == REISER4_FIXMAP_NEXT_LABEL ) { /* Need to load next diskmap block */ + sector_t next_diskmap_block = d64tocpu(&diskmap->table[i].value); + brelse(diskmap_bh); + diskmap_bh = sb_bread(sb, next_diskmap_block); + i = 0; + goto search_table; + } + + /* See if we have found table entry we are looking for */ + if ( (d32tocpu(&diskmap->table[i].label) == label) && + (d32tocpu(&diskmap->table[i].parameter) == parameter) ) { + *value = d64tocpu(&diskmap->table[i].value); + retval = 0; + } + brelse(diskmap_bh); + } + + return retval; +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/diskmap.h 2004-09-03 00:57:05.009277736 -0700 @@ -0,0 +1,52 @@ +#if !defined (__REISER4_DISKMAP_H__) +#define __REISER4_DISKMAP_H__ + +/* + * Disk map. + * + * Disk map is a special data structure used by reiser4 as an optional + * "anchor" of other meta-data. That is, disk map (if present) may contain + * disk addresses of the rest of meta-data for this file system: master + * super-block, bitmaps, journal header and footer, etc. Disk map is used to + * avoid dependency on fixed disk addresses, with the following goals: + * + * 1. allow users to experiment with tuning their file system layout, and, + * more importantly, + * + * 2. allow reiser4 to be survive bad blocks in critical disk locations. + * + * That is, disk map allows to "relocate" meta-data structures if their + * default disk addresses is not accessible. + * + * More generally, disk map can be used as a generic table used to store + * persistent parameters. + * + * Currently disk map is read-only for the kernel. It can only be + * constructed/modified by user-level utilities. + * + */ + +#include "dformat.h" + +#define REISER4_FIXMAP_MAGIC "R4FiXMaPv1.0" + +#define REISER4_FIXMAP_END_LABEL -2 +#define REISER4_FIXMAP_NEXT_LABEL -1 + +/* This is diskmap table, it's entries must be sorted ascending first in label + order, then in parameter order. End of table is marked with label + REISER4_FIXMAP_END_LABEL label REISER4_FIXMAP_NEXT_LABEL means that value + in this row contains disk block of next diskmap in diskmaps chain */ +struct reiser4_diskmap { + char magic[16]; + struct { + d32 label; + d32 parameter; + d64 value; + } table[0]; +}; + +int reiser4_get_diskmap_value(u32, u32, u64 *); + + +#endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/doc/bk.HOWTO 2004-09-03 00:57:05.010277584 -0700 @@ -0,0 +1,192 @@ + + MAINTENANCE OF REISER4 BITKEEPER REPOSITORY FOR THE CORE KERNEL. + + OVERVIEW + +Reiser4 receives linux kernel code from http://linux.bkbits.net/linux-2.5. + +This repository is pulled into laputa:~god/src/bk-linux-2.5 (BK-LINUX-2.5) by +nightly cron job (~god/bin/update-src.sh). BK-LINUX-2.5 is only used as a +local copy of Linus' repository, no changes are made in it. + +BK-LINUX-2.5 has child repository laputa:~god/projects/limbo (LIMBO), where it +is merged with the set of patches to core kernel that are necessary for +reiser4 to work. + +These patches are maintained through Andrew Morton's patch-scripts +(http://www.zipworld.com.au/~akpm/linux/patches/). Local and slightly modified +version of patch-scripts is installed at laputa:~god/run/patch-scripts/. See +laputa:~god/run/patch-scripts/docco.txt for more detailed usage instructions. + +patch-scripts are needed, because reiser4 modifications to the core kernel +should be available as a set of meaningful separately documented patches +rather than as one single huge patch (that would result from just accumulating +all changes in the bitkeeper). + +Patches themselves are stored in bitkeeper repository +laputa:~god/projects/core-patches (CORE-PATCHES). + +New versions of the core kernel are pulled into LIMBO, merged with +CORE-PATCHES, conflicts are resolved. This repository is cloned into temporary +repositories to test resulting kernel. After testing LIMBO is cloned/pulled +into thebsh:/usr/home/bk/reiser4-linux-2.6 (REISER4-LINUX-2.6). From there +individual developers clone/pull it to their heart content. + + UPGRADE INSTRUCTIONS + +1. backup LIMBO: + +$ cd ~god/projects +$ mv limbo limbo.orig + +2. clone BK-LINUX-2.5 into LIMBO + +$ bk clone ~god/src/bk-linux-2.5 limbo +$ cd limbo +$ bk -r edit -q + +3. roll LIMBO back to the desired kernel version: after a clone LIMBO contains +some version of Linus' repository, but usually we want a repository +corresponding to the exact kernel version. Use bk changes -L to find changeset +number corresponding to the desired kernel version, then do bk undo -a +to move the repository to that version. + +4. graft CORE-PATCHES into LIMBO + +$ bk clone ~god/projects/core-patches patches +$ cd patches +$ bk -r edit -q +$ cd ../ +$ . patches/setpc # set patch-script variables + +5. check status of core patches: + +$ pstatus # patch-scripts utility +1:a:2.6.6-mm2 Needs changelog +2:a:all-sources.diff Needs changelog + ... +35:a:disable-vermagic Needs changelog +36:a:make-4kstack-option Needs changelog +37:a:radix_tree_lookup_slot Needs changelog +?:-:2.6.6-rc3-mm2 Needs changelog +?:-:do_mmap2-fix.diff Needs changelog + ... + +Patches marked with ":a:" are applied (list of all currently applied patches +is in patches/applied-patches). Patches marked with ":-:" are not +applied. Patches with "?" (not numbered) are not included into "series" +(patches/series file)---these are usually some old or testing patches no +longer used. + +So, above pstatus output shows that there are 37 patches in the current +series, all applied. This is normal situation. LIMBO and CORE-PATCHES +repositories should always be left in such state after upgrading. + +6. Refresh core-patches. + +$ echo > patches/applied-patches # pretend patches are not applied + +Now for each patch do + +$ pushpatch # this applies next patch in series + +If patch could not applied successfully, "force it": + +$ pushpatch -f # this forces patch and generates .rej and .orig files + +Go through all generated .rej and .orig, resolve all conflicts, update +sources. Delete .rej and .orig files. + +Independently of whether patch was applied successfully or not, finish its +processing by refreshing it: + +$ refpatch # refresh current patch + +Repeat above pushpatch/refpatch sequence until patch-scripts report that +"Series are fully applied". + +7. Commit changes. Simplest way to do this is to do bk citool in LIMBO. But +this is time-consuming. Alternatively do: + +$ bk -r delta -a -q -y"local reiser4 patches applied" +$ bk commit -q -y"local reiser4 patches applied" +$ cd patches +$ bk citool # revise and commit modifications to the local patches +$ bk push # push modifications to the CORE-PATCHES +$ cd .. + +8. Test resulting sources + +$ cd ~god/projects/tmp +$ bk clone ../limbo + +and then follow standard build procedure: clone reiser4 repository into +tmp/limbo/fs, configure, build, etc. + +9. Pull changes to thebsh + +$ ssh thebsh +$ cd /usr/home/bk +$ mv reiser4-linux-2.6 reiser4-linux-2.6.orig +$ bk clone god@laputa:/home/god/projects/limbo reiser4-linux-2.6 + +If everything is ok, remove backup repositories limbo.orig and +reiser4-linux-2.6.orig. + + ADDING NEW PATCH + +There are two versions of adding-new-patch procedure: first for the "in-order" +patches, second for the "external" patches, like -mm patch. + + In-order patch + +1. Prepare repositories: + +$ cd ~god/projects/limbo +$ bk -r edit -q +$ cd patches +$ bk -r edit -q +$ cd .. + +2. As was mentioned above, all patches in the series should be already +applied. Put new patch into "patches": + +$ mv /tmp/new-patch.diff patches/patches/new-patch.patch + +.patch suffix is mandatory! + +$ pcpatch new-patch # this generates patches/pc/new-patch.pc +$ vi patches/txt/new-patch.txt # add patch description +$ echo new-patch.patch >> patches/series # add patch to the series file +$ pushpatch # apply it. + +If patch couldn't be applied, "force" it and resolve conflicts, see above. + +$ refpatch # and refresh it. + +This again leaves repositories in the consistent state (all patches in the +series are applied). + +3. Commit changes, don't forget to add new files to the CORE-PATCHES. See +above. + + External patch + +External patch (such as combined patch for -mm) sometimes has to be added at +the beginning of series (rather than at the end as small patches are). Such +patches are best added during upgrading. Specifically, step 6 becomes: + +6. Refresh core-patches. + +$ echo > patches/applied-patches # pretend patches are not applied +$ cp /tmp/external.patch patches/patches +$ pcpatch external +$ vi patches/txt/external.txt +$ vi patches/series # add "external.patch" at the appropriate place + +Proceed with pushpatch/refpatch as usual. + +To remove patch from series (for example, when upgrading to the new -mm +kernel), just kill appropriate line in the patches/series. + +Nikita. 2004.05.25 --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/doc/directory-service 2004-09-03 00:57:05.012277280 -0700 @@ -0,0 +1,203 @@ + + DIRECTORY SERVICE IN REISER4 + +Directory is mapping from file name to file itself. This mapping is +implemented through reiser4 internal balanced tree. Single global tree +is used as global index of all directories as opposed to having tree per +directory. Unfortunately file names cannot be used as keys until keys of +variable length are implemented, or unreasonable limitations on maximal +file name length are imposed. To work around this file name is hashed +and hash is used as key in a tree. No hash function is perfect and there +always be hash collisions, that is, file names having the same value of +a hash. Previous versions of reiserfs (3.5 and 3.6) used "generation +counter" to overcome this problem: keys for file names having the same +hash value were distinguished by having different generation +counters. This allowed to amortize hash collisions at the cost of +reducing number of bits used for hashing. This "generation counter" +technique is actually some ad hoc form of support for non-unique +keys. Keeping in mind that some form of this have to be implemented +anyway, it seems justifiable to implement more regular support for +non-unique keys in reiser4. + +NON-UNIQUE KEYS + +1. + +Non-unique keys require changes in both tree lookup and tree update +code. In addition some new API to iterate through items with identical +keys is required. + +Before going into detail let's note that non-unique keys weakens +traditional search tree invariant. Search tree with unique keys, keys of +all items in a left sub-tree of given delimiting key are less than, and +in the right sub-tree greater than or equal to the said key. In a search +tree with non-unique keys both inequalities are not strict. + +2. + +Tree lookups: we require that node layout ->lookup() methods always +return leftmost item with the key looked for. The same for item +->lookup() method for items supporting units with non-unique +keys. Standard node40 layout plugin handles this, see +fs/reiser4/plugin/node/node40.c:node40_lookup(). + +3. + +Tree balancing: it seems that only change here is the handling of +weakened search tree invariant. This can be gathered from the +observation that balancing never even compares keys, only tests them for +equality. More thought/research is required though. Looking at the +existing implementations (like Berkeley db) would be useful also. + +4. + +Iteration through items/unit with identical keys. There are two +interfaces to iterating abstraction known as "external" (also known as +"enumeration") and "internal" iterators. + +External iterator: + +external_iterator { + start(); + next(); + has_more_p(); +}; + +external_iterator eit; + +for( eit.start() ; eit.has_more_p() ; ) { + object = eit.next(); + ... do stuff with object ... +} + +Internal operator: + +internal_iterator { + iterate( int ( *function )( object *obj ) ); +}; + +internal_iterator iit; + +int do_stuff( object *obj ) +{ + ... do stuff with obj ... +} + +iit( &do_stuff ); + +External iterator seems easier to use, but they are known to be hard to +implement, especially for complex data-structures like trees (this is +because of the amount of state that should be maintained in "eit" +between its invocations). + +Internal iterators are harder to use in C, because new function has to +be declared to perform actions on objects in sequence, but are obviously +easier to implement. + +Given that in 4.0 version there will be only one client of this +iteration API (viz. directory lookup routine), it seems that internal +style is preferable for now. Later, external iterator interface can be +added if necessary. + +IMPLEMENTATION OF DIRECTORIES: + +1. + +There will be many various directory services implemented through +different plugins. Default directory plugin uses hashing techniques +described above. Let's code-name in hdir. + +2. + +Directory consists of directory entries, stored in a tree in a form of +directory items. Question about whether each directory entry should be +separate item or they can be compressed into items is left open by now. +First this decision is purely per-plugin decidable, second, compression +is good for performance, but harder to implement. + +Single directory entry is binding between file-system object and +directory. In hdir plugin it consists of full name of a file bound and +key (or part thereof) of file's stat-data: + +typedef struct hdir_entry { + /** + * key of object stat-data. It's not necessary to store + * whole key here, because it's always key of stat-data, so minor packing + * locality and offset can be omitted here. But this relies on + * particular key allocation scheme for stat-data, so, for extensibility + * sake, whole key can be stored here. + * + * We store key as array of bytes, because we don't want 8-byte alignment + * of dir entries. + */ + d8 sdkey[ sizeof( reiser4_key ) ]; + /** + * file name. Null terminated string. + */ + d8 name[ 0 ]; +} hdir_entry; + +4. + +On creation/linking/lookup of object "bar" in directory "foo" (foo/bar), +we compose key of directory entry for this object. Key has the form + +/* + * XXX this should be discussed + */ +dirent_k = (locality=foo_object_id, objectid=???, offset=hash("bar")); + +Major packing locality of dirent_k is set to foo_object_id so that all +objects (files) in this directory and their bodies are close to +respective directory entries. + +It seems that no single key allocation policy for directory entries fits +everyone's needs, so, this can be implemented as method of directory +plugin. No then less, choice of default key allocation policy is still +important decision, although not that important as in plugin-less +file-system. + +4. + +Function + +int hdir_find_entry( inode *dir, const hdir_entry *entry, + tween_coord *coord, lock_handle *lh ); + +iterates through all directory entries in @dir that have the same key as +@entry (scans hash-bucket), looking for exact match for entry->name. + +5. + +During ->create()/->link() hdir_find_entry() is used to find place to insert new +item (and to check for -EEXIST). + +During ->lookup() hdir_find_entry() is used find entry for the file +being looked for and to load stat-data afterwards. + +During ->unlink() hdir_find_entry() is used to find unit/item to be +removed. + +NOTE ON ->lookup(): + +VFS implements following protocol when creating new +file (fs/namei.c:open_namei()): + +dentry hash is searched. If search is unsuccessful, file system +->lookup() is called. +If lookup didn't find name, call ->create() + +While this protocol spares file system from dealing with dcache locking, +for reiserfs it means that tree traversal is performed twice during file +creation/deletion. Possible solution is to cache results of ->lookup() +(e.g, pointer to znode) in dentry and reuse then in ->create(). On the +other hand, point cache have more or less the same effect and is more +general. + + +^ Local variables: +^ mode-name: "Design Document" +^ indent-tabs-mode: nil +^ tab-width: 4 +^ eval: (progn (flyspell-mode) (flyspell-buffer)) +^ End: --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/doc/lock-ordering 2004-09-03 00:57:05.015276824 -0700 @@ -0,0 +1,601 @@ +---------------------------------INTRODUCTION----------------------------------- + +This document tries to provide concise description of various "locking" issues +in reiser4 code. There are two major areas here: + +1. locking as a device for the concurrency control: various synchronization +objects are used to maintain integrity of shared data structures. + +2. (induced by the former) deadlocks, livelocks, missed wake ups, and alikes. + +"Locks" above means both standard synchronization primitives like mutexes, +semaphores, condition variables and so on, and any other kind of object on +which thread execution may "block". Waiting on io completion is not considered +here, because hardware errors barred, it will ultimately finish regardless of +any other threads and locks in the system (This only holds if io completion +handlers don't acquire locks themselves.). + +-------------------------------LOCKS IN REISER4--------------------------------- + +Reiser4 introduces following locks: + +1. Per-super-block tree spin lock (tree_lock*) + +2. Per-super-block delimiting key spin lock (dk_lock*) + +3. Per-jnode spin lock (jnode_lock*) + +4. Per-znode lock with deadlock detection (longterm_lock) + +5. Per-reiser4-inode spin lock (inode_guard*) + +6. Per-atom spin lock (atom_lock*) + +7. Per-transaction-handle spin lock (txnh_lock*) + +8. Per-transaction-manager spin lock (txnmgr_lock*) + +9. Per-lock-stack spin-lock (stack_lock*) + +10. Per-inode read-write lock (inode_rw_lock) + +11. Per-super-block spin lock (super_guard*+) + +12. Per-flushing-thread spin lock (ktxnmgrd_lock) + +13. Global lnode hash table lock (lnode_guard+) + +14. Per-super-block cbk cache spin lock (cbk_guard) + +15. Per-jnode spin lock used by debugging code to access and + modify check sum (cksum_guard+) + +16. Per-super-block oid map spin lock (oid_guard+) + +17. Per-super-block spin lock used by "test" disk format plugin to serialize + block allocation (test_lock+) + +18. Per-condition-variable spin lock (kcond_lock+) + +19. Single spin lock used to serialize fake block allocation (fake_lock+) + +20. Single spin lock used to serialize calls to reiser4_panic (panic_guard+) + +21. Single spin lock used by debugging code to keep track of all active + reiser4_context instances (contexts_lock+) + +22. Per-lnode condition variable used by wait for completion of "incompatible + access mode" (lnode_kcond) + +23. Per-flushing-thread condition variable for startup waiting (ktxnmgrd_start) + +24. Per-flushing-thread condition variable (ktxnmgrd_wait) + +25. Per-lock-stack wakeup semaphore (stack_sema) + +26. Per-super-block flush serializing semaphore (flush_sema) + +27. Per-transaction-manager commit semaphore (commit_sema) + +28. Per-super-block semaphore used to arbitrate use of 5% (delete_sema) + reserved disk space + +30. Global spin lock used to serialize calls to panic (panic_guard+) + +31. Global spin lock used to protect plugin set hash table (pset_guard+) + +32. Global spin lock used to protect phash hash table (phash_guard+) + +33. Per-bitmap-block semaphore used to serialize bitmap loading (bnode_sema+) + +34. Per-super-block epoch lock, protecting updates to (epoch_lock*) + znode_epoch field, used to implement seals (seal.[ch]) + efficiently. + +35. Per-atom "event". This is not really lock. Rather, this is an event + signaled each time atom changes its state. (atom_event) + +36. Per-znode spin lock used to protect long term locking + structures (zlock*) + +37. Per flush queue lock (fq_lock*) + +38. Per-super-block zgen lock, protecting znode generation (zgen*) + counter + +39. Per-jnode spin lock used to synchronize jload() with (jload_lock*) + ->releasepage(). + +40. Per-atom imaginary read-write semaphore handle_sema (handle_sema) + + let's pretend for the sake of simplicity that there is special per-atom + read-write semaphore that threads can claim. Call it + handle_sema. This semaphore is acquired on read when thread captures first + block and is released when thread's reiser4_context is closed. Formally + thread holds this semaphore on read exactly when + get_current_context()->trans->atom != NULL, i.e., when thread is + associated with atom. Logic behind introducing this imaginary semaphore is + that while some thread is associated with an atom (that is, keeps + transaction handle opened), this atom cannot commit. In particular, other + threads waiting on fusion with atom that is in CAPTURE_WAIT stage wait + until this atom commits, that is wait (at least) until there are no opened + transaction handles for this atom. Effectively such threads wait until + handle_semaphore is free, that is, they in some sense are trying to + acquire handle_semaphore in write mode. So, this circumferential + description allows one to reduce (at least partially) problem of waiting + on atom fusion to the lock ordering. + +41. Per-super-block spin lock protecting consistency of emergency flush hash + table, ->eflushed, and ->eflushed_anon counters in inode, and ->flushed + counter in atom. (eflush_guard) + +42. Per-super-block spin lock protecting detached directory cursors for + stateless readdir (d_lock) + +99. Various locks used by the user level simulator + +Locks marked by (*) after label, are accessed through spin lock macros, +defined in reiser4.h. For them, locking ordering is checked at the runtime (at +least in the principle) when REISER4_DEBUG is on(e). + +Locks marked by (+) after label exist only for serializing concurrent access +to the shared data and are not supposed to be used in conjunction with any +other locks. They are omitted from locking ordering below to simplify the +picture. One can imaging them to be rightmost in the ordering. + +All locks, spin locks, and semaphores, except for stack_sema are subject to +normal protocol: thread that grabbed the lock will release it. stack_sema is +described in more details below. + +Also, following kernel locks are used by our code: + +1. Per-page lock (page_lock) + +2. Per-page writeback bit (page_write) + +3. Per-inode semaphore (i_sem) + +4. Per-inode I_LOCK bit-lock (I_LOCK) + +Thread also can block on the following "objects" that are not really locks: + +1. Page fault (pfault) + +2. Memory allocation (kalloc) + +3. Dirtying a page (through balance_dirty_pages()) (page_dirty) + +----------------------------------LOCK SCOPE------------------------------------ + +Section describing what data are protected by what locks. TBD. + +----------------------------------INVARIANTS------------------------------------ + +Invariants are some (formal or informal) properties of data structures. For +example, for well-formed doubly linked list, following holds: + +item->next->prev == item && item->prev->next == item + +In most cases, invariants only hold under proper locks. + +LABEL AND DESCRIPTION LOCKS + +[inode->eflushed] inode_guard + + inode->eflushed > 0, iff there are emergency flushed jnodes belonging to + this inode. Also, each emergency flushed jnode is counted as increase in + inode->i_count. + +[cbk-cache-invariant] cbk_guard + + If cbk cache is traversed in LRU order, first go all used slots (with + slot->node != NULL), then, all unused. All used slots have unique + slot->node. (Checked by cbk_cache_invariant().) + +[znode-fake] jnode_lock, tree_lock + + /* fake znode doesn't have a parent, and */ + znode_get_level(node) == 0 => znode_parent(node) == NULL, and + /* there is another way to express this very check, and */ + znode_above_root(node) => znode_parent(node) == NULL, and + /* it has special block number, and */ + znode_get_level(node) == 0 => *znode_get_block(node) == FAKE_TREE_ADDR, and + /* it is the only znode with such block number, and */ + !znode_above_root(node) && znode_is_loaded(node) => + *znode_get_block(node) != FAKE_TREE_ADDR + /* it is parent of the tree root node */ + znode_is_true_root(node) => znode_above_root(znode_parent(node)) + + (Checked by znode_invariant_f().) + +[znode-level] jnode_lock, tree_lock + + /* level of parent znode is one larger than that of child, except for the + fake znode */ + znode_parent(node) != NULL && !znode_above_root(znode_parent(node)) => + znode_get_level(znode_parent(node)) == znode_get_level(node) + 1 + /* left neighbor is at the same level, and */ + znode_is_left_connected(node) && node->left != NULL => + znode_get_level(node) == znode_get_level(node->left)) + /* right neighbor is at the same level */ + znode_is_right_connected(node) && node->right != NULL => + znode_get_level(node) == znode_get_level(node->right) + + (Checked by znode_invariant_f().) + +[znode-connected] + + /* ->left, ->right pointers form a valid list and are consistent with + JNODE_{LEFT,RIGHT}_CONNECTED bits */ + + node->left != NULL => znode_is_left_connected(node) + node->right != NULL => znode_is_right_connected(node) + node->left != NULL => + znode_is_right_connected(node->left) && + node->left->right == node + node->right != NULL => + znode_is_left_connected(node->right) && + node->right->left == node + +[znode-c_count] jnode_lock, tree_lock + + /* for any znode, c_count of its parent is greater than 0, and */ + znode_parent(node) != NULL && !znode_above_root(znode_parent(node)) => + atomic_read(&znode_parent(node)->c_count) > 0), and + /* leaves don't have children */ + znode_get_level(node) == LEAF_LEVEL => atomic_read(&node->c_count) == 0 + + (Checked by znode_invariant_f().) + +[znode-modify] zlock_lock(read), + jnode_lock, tree_lock + + /* if znode is not write-locked, its checksum remains + * invariant */ + !znode_is_wlocked(node) => znode_at_read(node) + + (Checked by znode_invariant_f().) + +[znode-refs] jnode_lock, tree_lock + + /* only referenced znode can be long-term locked */ + znode_is_locked(node) => atomic_read(&ZJNODE(node)->x_count) != 0 + + (Checked by znode_invariant_f().) + +[jnode-oid] jnode_lock, tree_lock + + /* for unformatted node ->objectid and ->mapping fields are + * consistent */ + jnode_is_unformatted(node) && node->key.j.mapping != NULL => + node->key.j.objectid == get_inode_oid(node->key.j.mapping->host) + + (Checked by znode_invariant_f().) + +[jnode-refs] jnode_lock, tree_lock + + /* only referenced jnode can be loaded */ + atomic_read(&node->x_count) >= node->d_count + + (Checked by jnode_invariant_f().) + +[jnode-dirty] jnode_lock, tree_lock + + /* dirty inode is part of atom */ + jnode_is_dirty(node) => node->atom != NULL + + (Checked by jnode_invariant_f().) + +[jnode-queued] jnode_lock, tree_lock + + /* only relocated node can be queued, except that when znode + * is being deleted, its JNODE_RELOC bit is cleared */ + JF_ISSET(node, JNODE_FLUSH_QUEUED) => + JF_ISSET(node, JNODE_RELOC) || JF_ISSET(node, JNODE_HEARD_BANSHEE) + + (Checked by jnode_invariant_f().) + +[jnode-atom-valid] jnode_lock, tree_lock + + /* node atom has valid state */ + node->atom != NULL => node->atom->stage != ASTAGE_INVALID + + (Checked by jnode_invariant_f().) + +[jnode-page-binding] jnode_lock, tree_lock + + /* if node points to page, it points back to node */ + node->pg != NULL => node->pg->private == node + + (Checked by jnode_invariant_f().) + +[sb-block-counts] super_guard + + reiser4_block_count(super) = reiser4_grabbed_blocks(super) + + reiser4_free_blocks(super) + + reiser4_data_blocks(super) + + reiser4_fake_allocated(super) + + reiser4_fake_allocated_unformatted(super) + + reiser4_flush_reserved(super) + + (Checked by check_block_counters().) + +[sb-grabbed] super_guard + + reiser4_grabbed_blocks(super) equals the sum of ctx->grabbed_blocks for + all grabbed contexts + +[sb-fake-allocated] txnmgr_lock, atom_lock + + When all atoms and transaction manager are locked, + reiser4_flush_reserved(super) equals to sum of atom->flush_reserved for + all atoms. + +[tap-sane] + + tap->mode is one of {ZNODE_NO_LOCK, ZNODE_READ_LOCK, ZNODE_WRITE_LOCK}, and + tap->coord != NULL, and + tap->lh != NULL, and + tap->loaded > 0 => znode_is_loaded(tap->coord->node), and + tap->coord->node == tap->lh->node + + (Checked by tap_invariant().) + +--------------------------------LOCK ORDERING----------------------------------- + +Lock ordering for kernel locks is taken from mm/filemap.c. Locks can be taken +from the left to the right. Locks on the same indentation level are unordered +with respect to each other. Any spin lock is righter than any long term lock, +obviously. + +i_sem +..inode_rw_lock <-------DEAD1-----+ +....handle_sema | +......I_LOCK | +......delete_sema | +......flush_sema | +........atom_event | +........longterm_lock <---DEAD2-+ | +......commit_sema | | +..........page_lock | | +............pfault | | +..............mm->mmap_sem------+-+ [do_page_fault] +..................ktxnmgrd_lock +................mapping->i_shared_sem +................kalloc +....................inode_guard +......................d_lock +....................txnmgr_lock +......................atom_lock +..........................super_guard +........................jnode_lock [->vm_writeback()->jget()] +................................eflush_guard +..........................txnh_lock +............................zlock +........................fq_lock +..............................stack_lock +..................dk_lock +..............................tree_lock +................................cbk_guard +................................epoch_lock +................................zgen_lock +..........................jload_lock +....................mm->page_table_lock +......................mapping->private_lock +........................swaplock +..........................swap_device_lock +..........................&inode_lock +............................&sb_lock +............................mapping->page_lock +..............................zone->lru_lock + ^ + +-- spin locks are starting here. Don't schedule rightward. + +NOT FINISHED. + +..............&cache_chain_sem +......................cachep->spinlock +......................zone->lock + +page_dirty +....&inode_lock +....&sb_lock +....mapping->page_lock [mpage_writepages] +..page_lock +..longterm_lock [__set_page_dirty_buffers->__mark_inode_dirty] + +Nice and clear picture with all reiser4 locks totally ordered, right? + +Unfortunately, it is not always possible to adhere to this ordering. When it +is necessary to take locks "decreasing" order, standard trylock-and-repeat +loop is employed. See: + + atom_get_locked_with_txnh_locked(), + atom_get_locked_by_jnode(), + atom_free(), and + jnode_lock_page() + +functions for examples of this. + +The only exception from the above locking oder is when thread wants to lock +object it is just created and hasn't yet announced to other threads (by means +of placing it into some shared data structure like hash table or list). There +is special spin lock macro spin_lock_foo_no_ord() defined in reiser4.h for +this purpose. + +pfault and kalloc are something special: when page fault occurs at the page +occupied by mmapped from reiser4 file, reiser4_readpage() is invoked that +starts taking locks from the very beginning. + +DEAD1 + + Scenario: + + process has mmapped reiser4 regular file and then does write(2) into + this file from buffer that is in mmaped area. copy_from_user() causes + page fault: + + sys_write() + reiser4_write() + unix_file_write() [inode_rw_lock] + . + . + . + __copy_from_user() + . + . + . + handle_page_fault() + handle_mm_fault() + handle_pte_fault() + do_no_page() + unix_file_filemap_nopage() [inode_rw_lock] + + This is safe, because inode_rw_lock is read-taken by both read/write and + unix_file_filemap_nopage(). It is only write-taken during tail<->extent + conversion and if file is mmaped is was already converted to extents. + +DEAD2 + + is safe, because copy_from_user is used only for tails and extents: + + . extent: extent_write_flow() releases longterm_lock before calling + copy_from_user. + + . tail: during copying into tail, only node containing this tail is long + term locked. It is easy to see, that ->readpage serving page fault (that + is, readpage for unformatted data) will never attempt to lock said node. + +When memory allocation tries to free some memory it + +1. asynchronously launches kswapd that will ultimately call + reiser4_writepage(). + +2. calls reiser4_writepage() synchronously. + +----------------------------------LOCK PATTERNS--------------------------------- + +This section describes where in the code what locks sequences are held. This +places restrictions on modifications to the lock ordering above and enumerates +pieces of the code that should be revised if modification of the lock ordering +is necessary. + +flush_sema + + jnode_flush() + + to serialize flushing. This behavior can be disabled with mtflush + mount option. + +atom_lock->jnode_lock + + uncapture_block() + +atom_lock->tree_lock && jnode_lock && page_lock + + uncapture_block() calls jput() + +delete_sema + + common_unlink(), shorten_file()->unlink_check_and_grab() + + to serialize access to reserved 5% of disk only used by unlinks. (This + is necessary so that it is always possible to unlink something and + free more space on file-system.) + +delete_sema->flush_sema || commit_sema + + reiser4_release_reserved() calls txnmgr_force_commit_current_atom() under + delete_sema + +inode_rw_lock->delete_sema + + unix_file_truncate()->shorten_file() takes delete_sema from under write + mode of inode_rw_lock + +kalloc->jnode_lock + + emergency_flush() takes jnode spin lock + +jnode_lock->(mapping->page_lock) + + jnode_set_dirty()->__set_page_dirty_nobuffers() + +jnode_lock->(zone->lru_lock) + + jnode_set_dirty()->mark_page_accessed() + + +I_LOCK->longterm_lock + + reiser4_iget() + +tree_lock->epoch_lock + + zget() calls znode_build_version() + +jnode_lock->stack_lock + + longterm_lock_znode(), longterm_unlock_znode(), wake_up_all_lopri_owners() + +tree_lock->cbk_guard + + znode_remove() calls cbk_cache_invalidate() + +zlock->stack_lock + + wake_up_all_lopri_owners() + +atom->stack_lock + + check_not_fused_lock_owners() + +txnh->stack_lock + + check_not_fused_lock_owners() + +jnode_lock->jload_lock + + reiser4_releasepage(), emergency_flush(). But this can actually be made + other way around. + +jnode_lock->eflush_guard + + eflush_add(), eflush_del() + +atom_lock->super_guard + + grabbed2flush_reserved_nolock() + +inode_guard->d_lock + + detach_fsdata() + +----------------------------------DEADLOCKS------------------------------------- + +Big section describing found/possible/already-worked-around deadlocks. + +1. Locking during tree traversal. + +2. Locking during balancing. + +3. Locking during squalloc. + +4. Page locking. + +5. Atom fusion. + +Please, fill gaps up. + +TBD. + +2002.09.19. Nikita. + +-------------------------------------------------------------------------------- + +^ Local variables: +^ mode-name: "Memo" +^ indent-tabs-mode: nil +^ tab-width: 4 +^ eval: (progn (flyspell-mode) (flyspell-buffer)) +^ End: --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/doc/lock-ordering.dot 2004-09-03 00:57:05.017276520 -0700 @@ -0,0 +1,276 @@ +/* this is dot(1) input file for lock-ordering diagram */ +/* it should be passed through C preprocessor first */ +/* cpp -P -DFITPAGE lock-ordering.dot | tred | dot -Tps | gv -media a4 - */ + +#define CATTR fontsize=14, fontname=Helvetica +#define NATTR CATTR +#define EATTR CATTR + +#define SYSATTR color=yellow, style=filled +#define PSEUDOATTR color=pink, style=filled, peripheries=2 + +#define LONGATTR shape=ellipse +#define SPINATTR shape=box + +#define CONDATTR color=blue, peripheries=2, LONGATTR + +#define MARKLONG(name) name -> schedulable [style=invis, weight=0] + +#define SYSLONG(name, l) name [label=l, NATTR, LONGATTR, SYSATTR]; MARKLONG(name) +#define SYSPSEUDO(name) name [NATTR, LONGATTR, PSEUDOATTR]; MARKLONG(name) +#define RLONG(name) name [NATTR, LONGATTR]; MARKLONG(name) + +#define RCOND(name, l) name [label=l, NATTR, CONDATTR]; MARKLONG(name) + +#define MARKSPIN(name) schedulable -> name [style=invis, weight=0] + +#define SYSSPIN(name, l) name [label=l, NATTR, SYSATTR, SPINATTR]; MARKSPIN(name) +#define RSPIN(name) name [NATTR, SPINATTR]; MARKSPIN(name) + +#define ARC(from, to, func, ...) from -> to [EATTR, label=func, ## __VA_ARGS__] + +digraph locks { + +//clusterrank=none +#if defined(FITPAGE) +size="7.5, 10.5"; +ratio=compress; +center=true; +#endif + +subgraph long { + /* reiser4 long term locks */ + RLONG(longterm_lock); + RLONG(inode_rw_lock); + RLONG(stack_sema); + RLONG(flush_sema); + RLONG(commit_sema); + RLONG(delete_sema); + /* txncommit is a synonym for flush_sema and commit_sema */ + txncommit [LONGATTR, PSEUDOATTR]; MARKLONG(txncommit); + txncommit -> flush_sema [style=dotted, dir=both]; + txncommit -> commit_sema [style=dotted, dir=both]; + + /* atom_event is not really a lock: you can wait on it, but cannot "own" + it. */ + RCOND(atom_event,atom_event); + + //RLONG(lnode_kcond); + //RLONG(ktxnmgrd_start); + //RLONG(ktxnmgrd_wait); + //RLONG(bnode_sema); + + /* pseudo locks */ + SYSPSEUDO(pfault); + SYSPSEUDO(kalloc); + SYSPSEUDO(schedulable); + + /* system long term locks */ + SYSLONG(page_write, page_write); + SYSLONG(mm_mmap_sem, "mm->mmap_sem"); + SYSLONG(mapping_i_shared_sem, "mapping->i_shared_sem"); + + SYSLONG(i_sem, i_sem); + SYSLONG(page_lock, page_lock); + SYSLONG(cache_chain_sem, "&cache_chain_sem"); + SYSLONG(I_LOCK, "I_LOCK"); + + SYSLONG(namespace_sem, "namespace->sem"); + // SYSLONG(bdev_bd_sem, "bdev->bd_sem"); + SYSLONG(sb_s_lock, "sb->s_lock"); + SYSLONG(sb_s_umount, "sb->s_umount"); +} + +subgraph spin { + + /* reiser4 spin locks */ + + RSPIN(tree_lock); + RSPIN(dk_lock); + RSPIN(jnode_lock); + RSPIN(inode_guard); + RSPIN(atom_lock); + RSPIN(txnh_lock); + RSPIN(txnmgr_lock); + RSPIN(ktxnmgrd_lock); + RSPIN(cbk_guard); + RSPIN(epoch_lock); + RSPIN(zgen_lock); + RSPIN(stack_lock); + RSPIN(zlock); + RSPIN(fq_lock); + RSPIN(jload_lock); + RSPIN(super_guard); + RSPIN(eflush_guard); + RSPIN(d_lock); + + //RSPIN(stack_lock); + //RSPIN(lnode_guard); + //RSPIN(cksum_guard); + //RSPIN(oid_guard); + //RSPIN(test_lock); + //RSPIN(kcond_lock); + //RSPIN(fake_lock); + //RSPIN(panic_guard); + //RSPIN(contexts_lock); + //RSPIN(pset_guard); + //RSPIN(phash_guard); + + /* system spin locks */ + SYSSPIN(bkl, "BKL"); + SYSSPIN(cachep_spinlock, "cachep->spinlock"); + SYSSPIN(zone_lock, "zone->lock"); + SYSSPIN(swaplock, "&swaplock"); + SYSSPIN(zone_lru_lock, "zone->lru_lock"); + SYSSPIN(mapping_private_lock, "mapping->private_lock"); + SYSSPIN(mapping_page_lock, "mapping->page_lock"); + SYSSPIN(inode_lock, "&inode_lock"); + SYSSPIN(swap_device_lock, "swap->device_lock"); + SYSSPIN(mm_page_table_lock, "mm->page_table_lock"); + SYSSPIN(sb_lock, "&sb_lock"); + SYSSPIN(page_chain_lock, "page->chain_lock"); + //removed at 2003.04.04 by akpm@digeo.com + //SYSSPIN(dparent_lock, "dparent_lock"); + SYSSPIN(dcache_lock, "dcache_lock"); + SYSSPIN(fs_struct_lock, "fs_struct->lock"); + SYSSPIN(tasklist_lock, "&tasklist_lock"); + SYSSPIN(sig_siglock, "sig->siglock"); + SYSSPIN(fown_lock, "fown->lock"); + SYSSPIN(task_switch_lock, "task->switch_lock"); + SYSSPIN(task_proc_lock, "task->proc_lock"); + SYSSPIN(task_alloc_lock, "task->alloc_lock"); + /* rq->lock is special: it can be unlocked by thread different from locker */ + SYSSPIN(rq_lock, "rq->lock"); + SYSSPIN(task_capability_lock, "&task_capability_lock"); + SYSSPIN(mmlist_lock, "&mmlist_lock"); + SYSSPIN(files_file_lock, "files->file_lock"); + SYSSPIN(dn_lock, "&dn_lock"); + //SYSSPIN(bdev_lock, "&bdev_lock"); + SYSSPIN(suspend_pagedir_lock, "&suspend_pagedir_lock") +} + +/* dependencies */ + +ARC(inode_guard, tree_lock, "update_sd_at()"); +ARC(inode_guard, jnode_lock, "update_sd_at()"); +ARC(inode_guard, atom_lock, "update_sd_at()"); +ARC(atom_lock, jnode_lock, "uncapture_block()"); //capture_fuse_jnode_lists() +ARC(jnode_lock, txnh_lock, "try_capture_block()"); +//alredy covered +ARC(atom_lock, txnh_lock, "capture_fuse_txnh_lists()"); +ARC(jnode_lock, tree_lock, "jdrop_in_tree()"); +ARC(tree_lock, cbk_guard, "cbk_cache_invalidate()"); +ARC(dk_lock, tree_lock, "sync_dkeys()"); +ARC(txnmgr_lock, atom_lock, "atom_dec_and_unlock()"); //txnmgr_force_commit_all(),\ncommit_some_atoms(),\nflush_one_atom()"); +ARC(txnmgr_lock, jnode_lock, "atom_begin_andlock()"); +ARC(txnmgr_lock, txnh_lock, "atom_begin_andlock()"); +ARC(i_sem, inode_rw_lock, "unix_file_setattr()");//,\nunix_file_write()"); +ARC(page_lock, i_sem, "reiserfs_unpack()"); +ARC(inode_rw_lock, delete_sema, "shorten()"); +//ARC(delete_sema, txncommit, "reiser4_release_reserved()"); +ARC(flush_sema, longterm_lock, "flush_scan_left()");//,\nflush_allocate_znode_update(),\nflush_scan_formatted(),\nflush_pos_to_child_and_alloc()"); +ARC(longterm_lock, page_lock, "cbk_level_lookup()"); +ARC(commit_sema, page_lock, "submit_write()"); +ARC(pfault, mm_mmap_sem, "handle_page_fault()"); +ARC(page_lock, pfault, "extent_write_flow()"); +ARC(mm_mmap_sem, kalloc, "unix_file_readpage()"); + +//ARC(inode_rw_lock, mm_mmap_sem, "unix_file_filemap_nopage()", style=dotted, dir=back); +//ARC(mm_mmap_sem, kalloc, "DEAD2", style="dotted"); +ARC(kalloc, jnode_lock, "emergency_flush()"); +ARC(longterm_lock, jnode_lock, "longterm_unlock_znode()");//,\nflush_allocate_znode()"); + +ARC(kalloc, inode_guard, "eflush_add()"); +ARC(ktxnmgrd_lock, txnmgr_lock, "commit_some_atoms()"); + +//already covered +ARC(mapping_i_shared_sem, mapping_private_lock, "__set_page_dirty_buffers()"); +//already covered +ARC(mapping_i_shared_sem, mapping_page_lock, ""); +ARC(mapping_i_shared_sem, mm_page_table_lock, "vma_link()"); + +ARC(inode_lock, mapping_page_lock, "__sync_single_inode()"); +ARC(inode_lock, sb_lock, "writeback_inodes()"); + +ARC(mm_page_table_lock, swap_device_lock, "try_to_unmap_one()"); +ARC(mm_page_table_lock, mapping_private_lock, "try_to_unmap_one()"); +//already covered +ARC(mm_page_table_lock, mapping_page_lock, "try_to_unmap_one()"); + +ARC(mm_mmap_sem, mapping_i_shared_sem, "do_mmap_pgoff()"); + +ARC(swaplock, swap_device_lock, "swap_info_get()"); +ARC(swap_device_lock, mapping_page_lock, "exclusive_swap_page()"); + +ARC(page_lock, page_chain_lock, "shrink_list()"); +ARC(mm_page_table_lock, page_chain_lock, "page_add_rmap()");//,\npage_remove_rmap()"); +ARC(mapping_page_lock, zone_lru_lock, "add_to_page_cache()");//,\nfilemap_fdatawait()"); +ARC(mm_page_table_lock, zone_lru_lock, "page_add_rmap()");//,\npage_remove_rmap()"); +ARC(zone_lru_lock, page_chain_lock, "rmap.c"); + +ARC(cache_chain_sem, kalloc, "cpuup_callback()"); +//ARC(cache_chain_sem, pfault, "kmem_cache_create()"); + +//obsolete ARC(dcache_lock, dparent_lock, "d_move()"); +ARC(fs_struct_lock, dcache_lock, "set_fs_pwd()");//,\nset_fs_root()"); + +ARC(namespace_sem, i_sem, "sys_pivot_root()"); + +ARC(sb_s_lock, txncommit, "reiser4_write_super()"); +ARC(sb_s_umount, txncommit, "reiser4_kill_super()"); + +ARC(task_switch_lock, rq_lock, "finish_arch_switch()"); +ARC(task_proc_lock, tasklist_lock, "unhash_process()"); // de_thread() +ARC(task_proc_lock, dcache_lock, "proc_pid_unhash()"); + +ARC(tasklist_lock, sig_siglock, "de_thread()");//,\ndo_notify_parent(),\nsys_tkill(),\ncopy_process()"); //collect_sigign_sigcatch(),\n__exit_sighand(),\nfreeze_processes() +ARC(dn_lock, fown_lock, "__inode_dir_notify()"); +ARC(fown_lock, tasklist_lock, "send_sigio()");//,\nsend_sigurg()"); +ARC(tasklist_lock, task_alloc_lock, "chroot_fs_refs()"); +ARC(tasklist_lock, rq_lock, "setscheduler()"); +ARC(task_capability_lock, tasklist_lock, "sys_capget()");//,\nsys_capset()"); +ARC(task_alloc_lock, files_file_lock, "match_comm()");//,\nmatch_pid()"); + +ARC(mmlist_lock, mm_page_table_lock, "unuse_process()"); + +ARC(tree_lock, zone_lock, "page_clear_jnode()");//,\njrelse_nolock()"); +ARC(tree_lock, zone_lru_lock, "page_clear_jnode()");//,\njrelse_nolock()"); +ARC(tree_lock, mapping_page_lock, "jdrop_in_tree()"); +ARC(tree_lock, epoch_lock, "zget()"); +ARC(tree_lock, zgen_lock, "zget()"); + +ARC(bkl, inode_lock, "iget()"); + +ARC(jnode_lock, mapping_page_lock, "jnode_set_dirty()"); +ARC(jnode_lock, zone_lru_lock, "jnode_set_dirty()"); + +ARC(I_LOCK, longterm_lock, "reiser4_iget()"); + +//one cannot wait for atom event keeping longterm lock +ARC(atom_event, longterm_lock, "flush"); +//one cannot wait for atom event keeping page lock +ARC(atom_event, page_lock, "jnode_extent_write()"); +ARC(zlock, stack_lock, "longterm_lock_znode()");//,\nlongterm_unlock_znode(), wake_up_all_lopri_owners()"); + +ARC(atom_lock, stack_lock, "check_not_fused_lock_owners()");//atom_send_event() +ARC(txnh_lock, stack_lock, "check_not_fused_lock_owners()"); +ARC(fq_lock, stack_lock, "wakeup_atom_waitfor_list()"); +ARC(atom_lock, fq_lock, "detach_fq()"); +ARC(jnode_lock, zlock, "check_not_fused_lock_owners()"); +ARC(txnh_lock, zlock, "check_not_fused_lock_owners()"); + +ARC(suspend_pagedir_lock, zone_lock, "do_magic_suspend_2()"); +ARC(cachep_spinlock, zone_lock, "cache_flusharray()"); + +ARC(mapping_page_lock, zone_lock, "add_to_page_cache()"); // find_lock_page +ARC(mapping_page_lock, zone_lru_lock, "add_to_page_cache()"); // find_lock_page +ARC(mm_page_table_lock, zone_lock, "try_to_unmap_one()"); // get_user_pages, do_wp_page, do_anonymous_page, do_no_page +ARC(mm_page_table_lock, zone_lru_lock, "try_to_unmap_one()"); // get_user_pages, do_wp_page, do_anonymous_page, do_no_page +ARC(jnode_lock, zone_lock, "page_clear_jnode()"); // uncapture_page, extent_write_flow +ARC(jnode_lock, zone_lru_lock, "page_clear_jnode()"); // uncapture_page, extent_write_flow +ARC(jnode_lock, jload_lock, "reiser4_releasepage()"); +ARC(atom_lock, super_guard, "grabbed2flush_reserved_nolock()"); + +ARC(jnode_lock, eflush_guard, "eflush_add()"); +ARC(inode_guard, d_lock, "detach_fsdata()"); +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/doc/metadata-in-pagecache 2004-09-03 00:57:05.018276368 -0700 @@ -0,0 +1,57 @@ +Hello, + +In upcoming reiser4 we are planning to use page cache to store all file system +meta data. In some cases it is straightforward; for example, bitmaps blocks, +placed on the disk through (almost) equal intervals ask to be bound to special +fake inode and indexed by their disk offsets. + +There is one important (most important actually) case where using fake inode +is inconvenient: blocks of internal balanced tree used by reiser4, known as +"formatted nodes". Natural solution of using block number as offset within +some fake inode doesn't pass, because when block size is smaller than page +some blocks mapped to the same page may be either occupied by something other +than formatted nodes, or just be free. + +This leads to the following complications: + + 1. we cannot simply use block_{read|write}_full_page(), because this will + waste IO bandwidth: block that doesn't contain formatted node will be read + into memory. Moreover, this block can be later read again, for example, + because this is data block of some file and hashed into different place in + the page cache, creating alias. This will definitely confuse buffer cache; + + 2. even is we keep track of what blocks have to be actually read, there still + will be "internal memory fragmentation", because some parts of page cache + pages will be unused. + +In brief, formatted nodes form a tree and because of this don't fit into + hashing scheme---there is no linear ordering among them. + +Moreover, formatted node is never looked up in the page cache by its block +number, because for each formatted node in memory there is special data +structure (znode) and znodes are hashed in the hash table anyway. + +So, all functionality that we need from the page cache is memory allocator +with attached memory pressure hooks (I guess, this is close to what Hans +called "sub-cache" in lkml discussions on this topic). + +It seems that we have two solutions: + + 1. change page cache to use different indexing for formatted nodes; + + 2. implement our own memory allocator sitting directly on the top of + alloc_pages() and installing proper ->mapping for pages that it grabs. + +(2) will only work if generic VM code (e.g., shrink_cache() or +page_launder_zone() in rmap VM) don't depend on particulars of page cache +hashing, that, fortunately, seems to be the case. This approach has following +advantages: + + . we can try to collocate related blocks on the same page, for example + blocks from the same transaction, of block with similar cache "hotness"; + + . we can use blocks larger than page size. + +Nikita. + + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/doc/oid-locid 2004-09-03 00:57:05.019276216 -0700 @@ -0,0 +1,108 @@ +MIME-Version: 1.0 +Content-Type: text/plain; charset=us-ascii +Content-Transfer-Encoding: 7bit +Message-ID: <15392.39020.573047.826769@laputa.namesys.com> +Date: Wed, 19 Dec 2001 16:38:52 +0300 +To: Reiserfs developers mail-list +Subject: [RFC]: objectids and localities management +X-Mailer: VM 6.96 under 21.4 (patch 3) "Academic Rigor" XEmacs Lucid +FCC: ~/documents/mail/outgoing +--text follows this line-- +Hello, + +there is one thing that seems awkward in current reiser{fs|4} design: in +a key we have both locality id (locid) and object id (oid). This is +slightly illogical because oid alone is unique, but we cannot find an +object given oid. This was, by the way, main reason behind our NFS +troubles. So, why is this strictly necessary? I'll try to reason from +the "first principles". Following account doesn't pretend to be of any +historical accuracy of course. + +1. In a data structure we use to store objects (tree) items + with close keys are packed into the same disk block. This means that + we cannot completely separate key allocation from block + allocation. That is, + + - tree forces us to encode disk location preferences in a key. (A1) + +2. If we cannot completely separate key and block allocation let's try + in stead to blend them together. That is, we rely on block allocator + to follow tree ordering and topology: blocks containing items with + close keys are allocated close on disk and blocks contiguous in tree + order are more or less contiguous on disk. How far bitmap.c fulfill + or can fulfill these goals is out of the scope of this discussion, + + - let's suppose that we have ideal block allocator. (A2) + +3. Given this, why cannot we encode disk location preferences in oid + alone? Because oid has to be unique and we cannot predict how many + objects we are going to group together in a future (how many objects + there will be in a directory that is). That is, suppose we create two + directories "a" and "b" in succession. If oid were the only thing to + store location preference, than we should leave after the oid of "a" + enough unused oids for all objects within "a", but we don't know how + many of them will be there. + +4. To solve this (locid, oid) scheme was born. It has following + advantages: + + - it is simple to implement + - it allows one to encode enough location preference into the key (A3) + +But the more people used reiserfs and the more files they started to +store in a single directory, the less valid (A3) became. oid became +inadequate location preference, because while it allows to separate +files from different directories it doesn't allow to order files within +single directory. For example readdir of big directory is slow, because +files are not sorted within directory. Various ad-hoc solutions have +been proposed (oid==hash, add "band" to oid, etc), but there is obvious +conflict between requirement that oid is unique and desire to encode +additional information in it. In effect all such solutions amount to +further splitting of (locid,oid) pair into (locid, someid, oid) for the +reasons similar to those on the steps 3,4 above. + +The scheme proposed below tries to meet following goals: + + G1. only keep unique oid in a key, thus making it possible to find file + given its inode number and additionally shrink key, increasing + fanout. + + G2. allow configurable amount of fine-grained locality preference + information to be associated with each oid, thus allowing files + to be ordered in a tree according to some hierarchical "packing + localities", for example: first order files by oid of parent + directory, then by hash of name within this directory. + + +Proposal: + +Maintain separate map (oidlocmap, implementation discussed below) from +oid to "locpref", where locpref is additional fine-grained location +preference data, associated with oid. For example locpref may be just +(locid) to emulate existing behavior, or (locid, hash) or (locid, +user-supplied-grouping-info), etc. + +Key only contains oid, that is, ceteris paribus, key has form +(item-type, oid, offset). If oid is 60 bits, this is 16 bytes. + +Ordering of items within tree (and, given (A2), their ordering on disk) +is completely determined by keycmp() function that compares two +keys. Before comparing two keys, keycmp(k1, k2) consults oidlocmap and +obtains locprefs, associated with oids of k1 and k2. locprefs then are +"pasted" into k1 and k2, producing "expanded" keys, containing full +location preferences information. Expanded keys are compared as usual. + +In simplest case oidlocmap can be implemented as normal balanced tree, +where keys are oids (60 bits) and values locprefs. If we limit ourselves +to fixed format of locpref (at least per file system) than, we get +standard text-book balanced tree storing values of fixed size which is +simple to implement. + +There is of course overhead of maintaining oidlocmap and, especially, of +consulting it on each keycmp(), but it looks to me that it will be not +that significant, because oidlocmap is compact and will be out-weighted +by increased fanout in the main tree. + +Comments? + +Nikita. --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/doc/page-cache-for-formatted-nodes 2004-09-03 00:57:05.019276216 -0700 @@ -0,0 +1,60 @@ +PROPOSAL: + +Keep formatted nodes in a page cache, binding them to the special fake inode +and using block number divided by number of blocks in a page as page index. + +ADVANTAGES: + +Page cache is preferred over buffer cache. Much more optimization and +scalability efforts are going into it. The fewer separate caches are in the +system, the simpler and better VM can handle load. + +DISADVANTAGES: + +As formatted nodes are indexed by block number, each page will contain +blocks with consequentive block numbers. This poses several problems: + + 1. When we need to read particular block from the disk (e.g., to load child + node during tree lookup), it is not clear that blocks with neighboring block + numbers are worth reading into memory at all. + + 2. Some of the blocks that have to go in the same page as block we need can + be unformatted ones. + +SOLUTIONS: + +There are several possible workarounds: + + 1. rely on the fact that in vast majority of cases block size is equal to + the page size. So, we can index formatted nodes by block number storing + exactly one block in the page. This will eliminate both problems at the + expense of the memory wasting in the setups where block size is smaller than + page size. + + 2. only load required block in the page marking other blocks mapped to this + page as up-to-date. It is not obvious that this will work at all, and in any + case, this will force us to use special API to access such pages, bypassing + VM interface. + + 3. rely on good repacker and load all blocks in the page hoping that they + are close to each other in tree order and will be accessed shortly. + + 4. allocate unformatted nodes such that they will never go into the same + frame as formatted. For example: + + - always align extent to the page boundary on the disk (page is CPU + specific though); + + - use some variation of border algorithm to separate formatted and + unformatted nodes; + + - use "enriched" bitmap where formatted and unformatted nodes are + distinguishable. + + +# Local variables: +# mode-name: "proposal" +# indent-tabs-mode: nil +# tab-width: 4 +# eval: (if (fboundp 'flyspell-mode) (flyspell-mode)) +# End: --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/doc/plugin.inheritance 2004-09-03 00:57:05.020276064 -0700 @@ -0,0 +1,119 @@ + + Report about "plugin inheritance discussion" + + 1. Basic plugin support, psets, default plugins. + + 2. Plugin inheritance. + + 3. Meta-data inheritance, light-weight files. + +1. Basic plugin support, psets, default plugins. + + Let's call Reiser4 file system object "active" when it is used by the + kernel, that is, when initialized inode exists for it. Associated with + each active object is its "plugin set" ("pset" for short) that is an array + of all plugins necessary for proper interaction with this object. Pointer + to pset is stored in inode. Pset is constructed when: + + 1. new object is created, or + + 2. existing object is looked up. + + New object is always created as a child of some already existing + object. During object creation its pset is constructed on the basic of + parent's one---this is plugin inheritance. Details of plugin inheritance + are delegated to the object plugin of new object for flexibility. + + File system has "default pset". In current implementation it is just pset + of the root directory, created by mkfs.reiser4. + + When stat-data is saved to disk, pset is saved as part of stat-data. At + least this is what default static stat-data plugin does. More advanced + stat-data plugins are free to save psets separately, implement sharing, + etc. + + As an optimization, only plugins different from default ones are stored in + stat-data. Correspondingly, when object is looked up, plugins found in + stat-data are installed into pset, and missing plugins are taken from the + default pset. + + Plugins in pset can be divided into two types: + + 1. "essential"---ones that cannot be changed without some explicit + effort. For example, hash and fibration plugins are essential, because + changing them would render directory content invalid. + + 2. "non-essential"---plugins that can be changed implicitly. For + example, security plugin and formatting-policy plugin are + non-essential. + + From previous description it is clear that essential plugins in default + pset cannot be modified once file system was created, because this would + implicitly change plugins of all objects in whose stat-data appropriate + plugin is missing, which is contrary to the definition of essential + plugin. + + This poses a problem: what to do when new member is added to pset + (consider recent addition of fibration plugin)? And, conversely, what to + do when mounting a file system with unknown member in default pset? + + The former is only an issue for essential plugins. When new essential + plugin is added to pset, backward-compatible implementation of this plugin + should be provided as default. That is, for example, when kernel with + support for fibration mounts file system without fibration plugin it the + root-directory stat-data, "lexicographic" fibration plugin should be + used. This guarantees that old file-systems can be used without corrupting + them. Of course, new versions of mkfs.reiser4 can set up whatever + fibration plugin is deemed best to be default. + + "Forward-compatibility" that is, mounting a file system with + unknown plugin in default pset, can be simply refused. + +2. Plugin inheritance. + + In addition to pset each active object also has a "hset"---"heir + set". When new child is created, it first tries to inherit plugins from + parent's hset, and only if plugin is missing there---from parent's + pset. hset is treated exactly like pset in all other respects. NOTE: + storing hset on disk is not yet implemented. + + One question still remains to be answered: how object plugin of a child + being created is selected? One possible solution is to add two new members + PSET_CREAT, and PSET_MKDIR to the pset. They specify object plugins used + when child is being created through sys_creat() and sys_mkdir() system + calls. (Other system calls, such as sys_symlink() and sys_mknod() are too + specialized for such flexibility.) NOTE: this is also not yet implemented. + +3. Meta-data inheritance, light-weight files. + + Through meta-data inheritance file system object can somehow indicate that + some portion of its meta-data should be taken from some place other than + object's stat-data. Three obvious scenarios for meta-data inheritance are: + + 1. meta-data are taken from file-system level default place, + + 2. meta-data are taken from some specially indicated place (i.e., + stat-data contains a key of item(s) where meta-data have to be taken + from), and + + 3. meta-data are taken from the parent. + + Note, that the last option is ambiguous, because the notion of _the_ + parent is not well-defined in general. This can be worked around in two + ways: + + 1. only use it when there is _the_ parent, for example, disable + light-weight files with multiple names, or + + 2. don't care, for example, allow uid of light-weight file to depend + on path-name through which this file was reached. + + In any case, meta-data inheritance can be implemented by re-using existing + static stat-data item plugin with simple additional plumbing in the kernel + code (pointer to parent inode should be passed to the stat-data + methods). It is not clear what to do when light-weight file is accessed + through NFS, and there is no parent. Simplest solution is to just disable + NFS access to them. This is trivial, because our ->{en,de}code_fh() + methods are delegated to object plugin. + + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/doc/readdir-problems-and-implementations 2004-09-03 00:57:05.021275912 -0700 @@ -0,0 +1,12 @@ +1. + +User level API. + +Standard + +^ Local variables: +^ mode-name: "Design Document" +^ indent-tabs-mode: nil +^ tab-width: 4 +^ eval: (if (fboundp 'flyspell-mode) (flyspell-mode)) +^ End: --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/doc/reiser4.writeback.overview 2004-09-03 00:57:05.021275912 -0700 @@ -0,0 +1,68 @@ +Hello, + +reiser4 has some features that make it somewhat difficult to integrate with +existing VM mechanisms. + +Reiser4 maintains all meta data in the single balanced tree. This tree is +maintained in the memory in the form different from what will be ultimately +written to the disk. Roughly speaking, before writing tree node to the disk, +some complex process ("flush") is to be performed. This process has following +characteristics: + + 1 it is not local, that is it operates on big number of nodes, possibly far + away from the starting node, both in tree and disk order. + + 2 it can involve reading of the large number of nodes from the disk (for + example, bitmap nodes are read during extent allocation that is deferred + until flush). + + 3 it can allocate unbounded amount of memory (during insertion of allocated + extents). + + 4 it participates in the locking protocol which reiser4 uses to implement + concurrent tree modifications. + + 5 it is CPU consuming and long + +As a result, flush reorganizes some part of reiser4 tree and produces large +queue of nodes ready to be submitted for io (as a matter of fact, flush write +clustering is so good that it used to hit BIO_MAX_PAGES all the time, until +checks were added for this). + +Items (3) and (4) alone make flush unsuitable for being called directly from +reiser4 ->vm_writeback() callback, because of OOM and deadlocks against +threads waiting for memory. + +So, it was decided that flush has to be performed from the separate +thread. Reiser4 has thread used to periodically commit old transactions and +this thread can be used for the flushing. That is, flushing thread does flush +and accumulates nodes prepared for the IO on the special +queue. reiser4_vm_writeback() submits nodes from this queue, if queue is +empty, it only wakes up flushing thread and immediately returns. + +Still there are some problems with integrating this stuff into VM scanning: + + 1 As ->vm_writeback() returns immediately without actually submitting pages + for IO, throttling on PG_writeback in shrink_list() will not work. This + opens a possibility (on a fast CPU), of try_to_free_pages() completing + scanning and calling out_of_memory() before flushing thread managed to add + anything to the queue. + + 2 It is possible, however unlikely, that flushing thread will be unable to flush + anything, because there is not enough memory. In this case reiser4 resorts + to the "emergency flush": some dumb algorithm that writes tree nodes to the + disk without taking locks and without optimizing tree layout. + + 3 Nodes prepared for IO can be from the active list, this means that they + will not be met/freed by shrink_list() after IO completion. New + blk_congestion_wait() should help here though. + +It looks like we need following changes to make this stuff working: + + 1 Adding ->priority field into struct writeback_control, so that file system + can vary its behavior depending on how desperate memory pressure is. + + 2 Different mechanism for scan throttling. + +Actually latter can be implemented completely within reiser4 but with some +awkwardness. --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/doc/set-theoretic-stuff.tex 2004-09-03 00:57:05.022275760 -0700 @@ -0,0 +1,82 @@ +\documentclass[a4paper, oneside, fleqn]{article} + +\usepackage{latexsym} +\usepackage{url} +\usepackage[T2A]{fontenc} + +\pagestyle{empty} +\listfiles +\setcounter{errorcontextlines}{100} +\makeindex +\pagestyle{headings} +\frenchspacing +\tolerance=1000 +\parindent=0pt +\raggedbottom +\setlength\parskip{6pt} + +\DeclareMathAlphabet{\mathbsf}{T2A}{cmss}{b}{n} +\SetMathAlphabet{\mathbsf}{normal}{T2A}{cmss}{b}{n} + +\def\qopname@#1{\mathop{\fam 0#1}\nolimits} +\newcommand{\mathsign}[1] + {\index{#1@$\mathbsf{#1}$}\qopname@{\mathbsf{#1}}} + +\def\As{\mathsign{Assoc}} +\newcommand{\svi}[2] + {\texttt{[} #1 \ V \texttt{]}} + +\begin{document} + +\thispagestyle{empty} + +%\section{Definitions} + +We have a set $X$ of objects, and ``associated-with'' relation. We shall write + +$$a\As b, \quad a\in X, \ b\in X$$ + +to denote that $a$ is associated with $b$. + +One can imagine $\As$ relation as graph where elements of $X$ are nodes and +where there is arc (arrow) from $a$ to $b$ iff $a$ is associated with +$b$. Note that no further restrictions are placed on $\As$. In particular, it +is not supposed that $\As$ is reflexive (object is not necessary associated +with itself), symmetric, or transitive. + +$\beta(X)$ is set of all subsets of $X$, that is $$\beta(X) = \{ U \subseteq X +\}$$ + +Let's define function $A:X\to^{}\beta(X)$ as follows: + +$$A(x)=\{y\in X\ |\ y\As x\}, \quad x\in X.$$ + +that is $A(x)$ is a set of all objects in $X$ associated with $x$. +Then, define \mbox{$A^*:\beta(X)\to^{}\beta(X)$} as follows: + +$$A^*(U)=\bigcup\limits_{x\in U} A(x), \quad U\subseteq X.$$ + +that is, $A(U)$ is set of all objects associated with any element of $U$. Now +we can define $\svi{U}{V}$, where $U, V\subseteq X$---``set vicinity +intersection'' operation as: + +%\begin{displaymath} +%A^+(U) = \left\{ +% \begin{array}{rl} +% U = \{x\} & \Rightarrow A(x),\\ +% \textrm{else} & \Rightarrow A^*(U) +% \end{array} \right. +%\end{displaymath} + +$$\svi{U}{V} = A^*(U) \cap A^*(V).$$ + +In other words, $\svi{U}{V}$ is a set of all objects associated with some +element of $U$ \emph{and} some element of $V$. + +\end{document} + +% Local variables: +% indent-tabs-mode: nil +% tab-width: 4 +% eval: (progn (if (fboundp 'flyspell-mode) (flyspell-mode)) (set (make-local-variable 'compile-command) "latex set-theoretic-stuff.tex ; dvips -o set-theoretic-stuff.ps set-theoretic-stuff.dvi")) +% End: --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/doc/sys-reiser4-implemenation-overview 2004-09-03 00:57:05.023275608 -0700 @@ -0,0 +1,222 @@ +SYS_REISER4 IMPLEMENTATION OVERVIEW + + +A. Basics +***************************************************************** + +sys_reiser4() system call executing a sequence of actions upon the +file-system(s). Actions are specified by the user in the form of a command +string. For the purposes of present discussion, said command string can be +thought of as a program in a special purpose programming language, which will +be further referred to as reiser4_lang. + +Canonical example of reiser4_lang program is + +/dir1/dir2/dir3/file1 <- /dir4/dir5/dir6/file2 + +It semantics is following: + +1. resolve "/dir1/dir2/dir3/file1" into file-system object (lookup operation) +2. resolve "/dir4/dir5/dir6/file2" into file-system object (lookup operation) +3. assign latter to the former. + +This is "assignment" operator. Assignment involves two "file-system objects" +and semantics of both lookup stage and assignment proper depends upon the type +of the file-system object. + +Following types of file-system objects are recognized: + +1. foreign objects: objects of different file-systems. Foreign object cannot +be target or source of an assignment. Rather, foreign objects can only appear +during path name lookup, while traversing non-reiser4 part of the file-system +name-space. Probably one should distinguish between objects belonging to +different file-system types (etx2, NFS) and objects belonging to different +reiser4 mounts. After sys_reiser4() is stable, foreign objects will be more +fully supported. + +2. reiser4 objects. + +3. pseudo-objects: these are entities injected into reiser4 name-space to +provide uniform access to various file-system meta-data. Pseudo-objects are +(usually) attached to some particular "host" object. [In the initial version,] +host objects are reiser4 objects. [Later it is possible to implement some +pseudo-objects for foreign objects.] Convention (but not enforced rule) is +that pseudo-objects are accessible through names starting with some well-known +prefix (".." is current favorite). Examples: ..owner, ..acl, etc. See comment +at the top of fs/reiser4/plugin/pseudo/pseudo.c for more details. + +B. lnodes +***************************************************************** + +lnodes are handles for file-system objects described above. They serve dual +purpose: + +1. uniform interface to the various types of objects. This allows the +reiser4_lang implementation to treat various types of objects in the same +manner. When new type of object has to be added, all changes will be grouped +in one place, rather than scattered across various files. This uniformity also +allows code sharing between reiser4_lang and VFS access paths. For example, +the same ->write method can be used by both. That is, ->read(), and ->write() +plugin methods used in VFS access paths will take lnode(s) as arguments and +can share code with sys_reiser4() implementation. For example, assignment is +particular case of write (or visa versa, depending on point of view). + + +2. synchronization. reiser4_lang doesn't use inodes and this poses a problem of +synchronization with VFS. Each lnode serves as a lock. See lnode.c for more +details. + +C. lookup +***************************************************************** + +reiser4_lang still supports only two traditional UNIX kinds of ordered names +(pathnames): absolute and relative to the current working directory. In both +cases, lookup starts from some file-system object represented by lnode. Then +lookup proceeds component-by-component as follows: + + lnode *parent; + lnode child; + + ret_code = lnode_get_dir_plugin( parent ) -> lnode_by_name( parent, + path_component, + &child ); + +1. Abovementioned locking issues require that parent lnode has to be kept +until operation on child finishes. In effect we get lock-coupling much like in +internal tree traversal. Also, possibility to use lock on node with directory +entry in stead of object lock was discussed. We have to think more on this. + + +2. Mount points crossing. It is possible, because dentries and therefore +inodes of all mount points are pinned in memory and lookup code can check at +each step whether mount point is crossed. Details are not very nice, because +for each inode in a path we have to scan list of all its dentries and check +whether correct one (corresponding to our path) is mount point. + +3. It is also possible to pass ->lnode_by_name the whole of the remaining +name, and let it decide how much of it it should handle. This will complicate +locking somewhat. But this is doable, though requires changes to the parser. + + +D. assignment +***************************************************************** + +Assignment A<-B basically means duplicating content of B into A. No +copy-on-write optimizations will be in version 4.0. + +Assignment implementation is based on the notion of flow (flow_t). Flow is a +source from which data can be obtained. Flow can be "backed up" by one of the +following: + +1. memory area in user space. (char *area, size_t length) +2. memory area in kernel space. (caddr_t *area, size_t length) +3. file-system object (lnode *obj, loff_t offset, size_t length) + +Main function to manipulate flows is: + +int flow_place( flow_t *flow, char *area, size_t length ); + +it copies @length bytes of @flow into @area and updated @flow correspondingly. +Behavior of flow_place() depends on the type of entity backing up @flow. If +@flow is based on the kernel-space area, memmove() is used to copy data. If +@flow is based on the user-space area, copy_from_user() is used. If @flow is +based on file-system object, flow_place() loads object's data into page cache +and copies them into @area. + +Thus, assignment code looks like following: + +typedef int ( *connect_t )( sink_t *target, flow_t *source ); + +int reiser4_assign( lnode *dst, lnode *src ) +{ + flow_t source; + sink_t target; + int ret_code; + file_plugin *src_fplug; + file_plugin *dst_fplug; + connect_t connection; + + /* get plugins */ + + src_fplug = lnode_get_file_plugin( src ); + dst_fplug = lnode_get_file_plugin( dst ); + + /* build source flow */ + ret_code = src_fplug -> build_flow( src, &source, 0 /* offset */ ); + + /* build target sink */ + ret_code = dst_fplug -> build_sink( dst, &target, 0 /* offset */ ); + + /* + * select how to transfer data from @src to @dst. + * + * Default implementation of this is common_transfer() (see below). + * + * Smart file plugin can choose connection based on type of @dst. + * + */ + connection = src_fplug -> select_connection( src, dst ); + + /* do transfer */ + return connection( &target, &source ); +} + + +/* look to chain conversion of (lnode * dst) -> (sink_t target) -> (lnode * dst) + I think, functions build_sink(...) and sink_object(...) - superfluous */ + +int common_transfer( sink_t *target, flow_t *source ) +{ + lnode *dst; + + dst = sink_object( target ); + while( flow_not_empty( source ) ) { + char *area; + size_t length; + + /* + * append some space to @target. Reasonable implementation will + * allocate several pagesful here + */ + ret_code = lnode_get_body_plugin( dst ) -> prepare_append( dst, + &area, + &length ); + /* why @length not depended from source? */ + /* + * put data from flow into newly alloted space. This also updates + * @flow. + */ + flow_place( source, area, length ); + /* + * perform necessary post-write activity required by @dst plugin, like + * encryption, compression, etc. Release pages. + */ + ret_code = lnode_get_body_plugin( dst ) -> commit_append( dst, + area, length ); + } +} + + +E. parsing +***************************************************************** + +It is not clear what parts of reiser4_lang processing should go into +kernel. In any case, providing direct system call as main (or, worse, the +only) way to access reiser4_lang functionality bounds as to maintain binary +compatibility in a future. To avoid this, reiser4 should be shipped with +user-level library, containing + +int reiser4( const char *cmd, size_t length ); + +function. For now, this function will directly despatch @cmd to the +sys_reiser4() in a future, it may do parsing itself and pass parse tree to the +kernel interpreter. + +***************************************************************** + +# Local variables: +# mode-name: "proposal" +# indent-tabs-mode: nil +# tab-width: 4 +# eval: (if (fboundp 'flyspell-mode) (flyspell-mode)) +# End: --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/dscale.c 2004-09-03 00:57:05.024275456 -0700 @@ -0,0 +1,173 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Scalable on-disk integers */ + +/* + * Various on-disk structures contain integer-like structures. Stat-data + * contain [yes, "data" is plural, check the dictionary] file size, link + * count; extent unit contains extent width etc. To accommodate for general + * case enough space is reserved to keep largest possible value. 64 bits in + * all cases above. But in overwhelming majority of cases numbers actually + * stored in these fields will be comparatively small and reserving 8 bytes is + * a waste of precious disk bandwidth. + * + * Scalable integers are one way to solve this problem. dscale_write() + * function stores __u64 value in the given area consuming from 1 to 9 bytes, + * depending on the magnitude of the value supplied. dscale_read() reads value + * previously stored by dscale_write(). + * + * dscale_write() produces format not completely unlike of UTF: two highest + * bits of the first byte are used to store "tag". One of 4 possible tag + * values is chosen depending on the number being encoded: + * + * 0 ... 0x3f => 0 [table 1] + * 0x40 ... 0x3fff => 1 + * 0x4000 ... 0x3fffffff => 2 + * 0x40000000 ... 0xffffffffffffffff => 3 + * + * (see dscale_range() function) + * + * Values in the range 0x40000000 ... 0xffffffffffffffff require 8 full bytes + * to be stored, so in this case there is no place in the first byte to store + * tag. For such values tag is stored in an extra 9th byte. + * + * As _highest_ bits are used for the test (which is natural) scaled integers + * are stored in BIG-ENDIAN format in contrast with the rest of reiser4 which + * uses LITTLE-ENDIAN. + * + */ + +#include "debug.h" +#include "dscale.h" + +/* return tag of scaled integer stored at @address */ +static int gettag(const unsigned char *address) +{ + /* tag is stored in two highest bits */ + return (*address) >> 6; +} + +/* clear tag from value. Clear tag embedded into @value. */ +static void cleartag(__u64 *value, int tag) +{ + /* + * W-w-what ?! + * + * Actually, this is rather simple: @value passed here was read by + * dscale_read(), converted from BIG-ENDIAN, and padded to __u64 by + * zeroes. Tag is still stored in the highest (arithmetically) + * non-zero bits of @value, but relative position of tag within __u64 + * depends on @tag. + * + * For example if @tag is 0, it's stored 2 highest bits of lowest + * byte, and its offset (counting from lowest bit) is 8 - 2 == 6 bits. + * + * If tag is 1, it's stored in two highest bits of 2nd lowest byte, + * and it's offset if (2 * 8) - 2 == 14 bits. + * + * See table 1 above for details. + * + * All these cases are captured by the formula: + */ + *value &= ~(3 << (((1 << tag) << 3) - 2)); + /* + * That is, clear two (3 == 0t11) bits at the offset + * + * 8 * (2 ^ tag) - 2, + * + * that is, two highest bits of (2 ^ tag)-th byte of @value. + */ +} + +/* return tag for @value. See table 1 above for details. */ +static int dscale_range(__u64 value) +{ + if (value > 0x3fffffff) + return 3; + if (value > 0x3fff) + return 2; + if (value > 0x3f) + return 1; + return 0; +} + +/* restore value stored at @adderss by dscale_write() and return number of + * bytes consumed */ +reiser4_internal int dscale_read(unsigned char *address, __u64 *value) +{ + int tag; + + /* read tag */ + tag = gettag(address); + switch (tag) { + case 3: + /* In this case tag is stored in an extra byte, skip this byte + * and decode value stored in the next 8 bytes.*/ + *value = __be64_to_cpu(get_unaligned((__u64 *)(address + 1))); + /* worst case: 8 bytes for value itself plus one byte for + * tag. */ + return 9; + case 0: + *value = get_unaligned(address); + break; + case 1: + *value = __be16_to_cpu(get_unaligned((__u16 *)address)); + break; + case 2: + *value = __be32_to_cpu(get_unaligned((__u32 *)address)); + break; + default: + return RETERR(-EIO); + } + /* clear tag embedded into @value */ + cleartag(value, tag); + /* number of bytes consumed is (2 ^ tag)---see table 1.*/ + return 1 << tag; +} + +/* store @value at @address and return number of bytes consumed */ +reiser4_internal int dscale_write(unsigned char *address, __u64 value) +{ + int tag; + int shift; + unsigned char *valarr; + + tag = dscale_range(value); + value = __cpu_to_be64(value); + valarr = (unsigned char *)&value; + shift = (tag == 3) ? 1 : 0; + memcpy(address + shift, valarr + sizeof value - (1 << tag), 1 << tag); + *address |= (tag << 6); + return shift + (1 << tag); +} + +/* number of bytes required to store @value */ +reiser4_internal int dscale_bytes(__u64 value) +{ + int bytes; + + bytes = 1 << dscale_range(value); + if (bytes == 8) + ++ bytes; + return bytes; +} + +/* returns true if @value and @other require the same number of bytes to be + * stored. Used by detect when data structure (like stat-data) has to be + * expanded or contracted. */ +reiser4_internal int dscale_fit(__u64 value, __u64 other) +{ + return dscale_range(value) == dscale_range(other); +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/dscale.h 2004-09-03 00:57:05.025275304 -0700 @@ -0,0 +1,27 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Scalable on-disk integers. See dscale.h for details. */ + +#if !defined( __FS_REISER4_DSCALE_H__ ) +#define __FS_REISER4_DSCALE_H__ + +#include "dformat.h" + +extern int dscale_read (unsigned char *address, __u64 *value); +extern int dscale_write(unsigned char *address, __u64 value); +extern int dscale_bytes(__u64 value); +extern int dscale_fit (__u64 value, __u64 other); + +/* __FS_REISER4_DSCALE_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/emergency_flush.c 2004-09-03 00:57:05.029274696 -0700 @@ -0,0 +1,917 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* This file exists only until VM gets fixed to reserve pages properly, which + * might or might not be very political. */ + +/* Implementation of emergency flush. */ + +/* OVERVIEW: + + Before writing a node to the disk, some complex process (flush.[ch]) is + to be performed. Flush is the main necessary preliminary step before + writing pages back to the disk, but it has some characteristics that make + it completely different from traditional ->writepage(): + + 1 It operates on a large number of nodes, possibly far away from the + starting node, both in tree and disk order. + + 2 it can involve reading of nodes from the disk (during extent + allocation, for example). + + 3 it can allocate memory (during insertion of allocated extents). + + 4 it participates in the locking protocol which reiser4 uses to + implement concurrent tree modifications. + + 5 it is CPU consuming and long + + As a result, flush reorganizes some part of reiser4 tree and produces + large queue of nodes ready to be submitted for io. + + Items (3) and (4) alone make flush unsuitable for being called directly + from reiser4 ->writepage() callback, because of OOM and deadlocks + against threads waiting for memory. + + So, flush is performed from within balance_dirty_page() path when dirty + pages are generated. If balance_dirty_page() fails to throttle writers + and page replacement finds dirty page on the inactive list, we resort to + "emergency flush" in our ->vm_writeback(). + + Emergency flush is relatively dumb algorithm, implemented in this file, + that tries to write tree nodes to the disk without taking locks and without + thoroughly optimizing tree layout. We only want to call emergency flush in + desperate situations, because it is going to produce sub-optimal disk + layouts. + + DETAILED DESCRIPTION + + Emergency flush (eflush) is designed to work as low level mechanism with + no or little impact on the rest of (already too complex) code. + + eflush is initiated from ->writepage() method called by VM on memory + pressure. It is supposed that ->writepage() is rare call path, because + balance_dirty_pages() throttles writes and tries to keep memory in + balance. + + eflush main entry point (emergency_flush()) checks whether jnode is + eligible for emergency flushing. Check is performed by flushable() + function which see for details. After successful check, new block number + ("emergency block") is allocated and io is initiated to write jnode + content to that block. + + After io is finished, jnode will be cleaned and VM will be able to free + page through call to ->releasepage(). + + emergency_flush() also contains special case invoked when it is possible + to avoid allocation of new node. + + Node selected for eflush is marked (by JNODE_EFLUSH bit in ->flags field) + and added to the special hash table of all eflushed nodes. This table + doesn't have linkage within each jnode, as this would waste memory in + assumption that eflush is rare. In stead new small memory object + (eflush_node_t) is allocated that contains pointer to jnode, emergency + block number, and is inserted into hash table. Per super block counter of + eflushed nodes is incremented. See section [INODE HANDLING] below for + more on this. + + It should be noted that emergency flush may allocate memory and wait for + io completion (bitmap read). + + Basically eflushed node has following distinctive characteristics: + + (1) JNODE_EFLUSH bit is set + + (2) no page + + (3) there is an element in hash table, for this node + + (4) node content is stored on disk in block whose number is stored + in the hash table element + + UNFLUSH + + Unflush is reverse of eflush, that is process bringing page of eflushed + inode back into memory. + + In accordance with the policy that eflush is low level and low impact + mechanism, transparent to the rest of the code, unflushing is performed + deeply within jload_gfp() which is main function used to load and pin + jnode page into memory. + + Specifically, if jload_gfp() determines that it is called on eflushed + node it gets emergency block number to start io against from the hash + table rather than from jnode itself. This is done in + jnode_get_io_block() function. After io completes, hash table element + for this node is removed and JNODE_EFLUSH bit is cleared. + + LOCKING + + The page lock is used to avoid eflush/e-unflush/jnode_get_io_block races. + emergency_flush() and jnode_get_io_block are called under the page lock. + The eflush_del() function (emergency unflush) may be called for a node w/o + page attached. In that case eflush_del() allocates a page and locks it. + + PROBLEMS + + 1. INODE HANDLING + + Usually (i.e., without eflush), jnode has a page attached to it. This + page pins corresponding struct address_space, and, hence, inode in + memory. Once inode has been eflushed, its page is gone and inode can be + wiped out of memory by the memory pressure (prune_icache()). This leads + to the number of complications: + + (1) jload_gfp() has to attach jnode tho the address space's radix + tree. This requires existence if inode. + + (2) normal flush needs jnode's inode to start slum collection from + unformatted jnode. + + (1) is really a problem, because it is too late to load inode (which + would lead to loading of stat data, etc.) within jload_gfp(). + + We, therefore, need some way to protect inode from being recycled while + having accessible eflushed nodes. + + I'll describe old solution here so it can be compared with new one. + + Original solution pinned inode by __iget() when first its node was + eflushed and released (through iput()) when last was unflushed. This + required maintenance of inode->eflushed counter in inode. + + Problem arise if last name of inode is unlinked when it has eflushed + nodes. In this case, last iput() that leads to the removal of file is + iput() made by unflushing from within jload_gfp(). Obviously, calling + truncate, and tree traversals from jload_gfp() is not a good idea. + + New solution is to pin inode in memory by adding I_EFLUSH bit to its + ->i_state field. This protects inode from being evicted by + prune_icache(). + + DISK SPACE ALLOCATION + + This section will describe how emergency block is allocated and how + block counters (allocated, grabbed, etc.) are manipulated. To be done. + + *****HISTORICAL SECTION**************************************************** + + DELAYED PARENT UPDATE + + Important point of emergency flush is that update of parent is sometimes + delayed: we don't update parent immediately if: + + 1 Child was just allocated, but parent is locked. Waiting for parent + lock in emergency flush is impossible (deadlockable). + + 2 Part of extent was allocated, but parent has not enough space to + insert allocated extent unit. Balancing in emergency flush is + impossible, because it will possibly wait on locks. + + When we delay update of parent node, we mark it as such (and possibly + also mark children to simplify delayed update later). Question: when + parent should be really updated? + + WHERE TO WRITE PAGE INTO? + + + So, it was decided that flush has to be performed from a separate + thread. Reiser4 has a thread used to periodically commit old transactions, + and this thread can be used for the flushing. That is, flushing thread + does flush and accumulates nodes prepared for the IO on the special + queue. reiser4_vm_writeback() submits nodes from this queue, if queue is + empty, it only wakes up flushing thread and immediately returns. + + Still there are some problems with integrating this stuff into VM + scanning: + + 1 As ->vm_writeback() returns immediately without actually submitting + pages for IO, throttling on PG_writeback in shrink_list() will not + work. This opens a possibility (on a fast CPU), of try_to_free_pages() + completing scanning and calling out_of_memory() before flushing thread + managed to add anything to the queue. + + 2 It is possible, however unlikely, that flushing thread will be + unable to flush anything, because there is not enough memory. In this + case reiser4 resorts to the "emergency flush": some dumb algorithm, + implemented in this file, that tries to write tree nodes to the disk + without taking locks and without thoroughly optimizing tree layout. We + only want to call emergency flush in desperate situations, because it + is going to produce sub-optimal disk layouts. + + 3 Nodes prepared for IO can be from the active list, this means that + they will not be met/freed by shrink_list() after IO completion. New + blk_congestion_wait() should help with throttling but not + freeing. This is not fatal though, because inactive list refilling + will ultimately get to these pages and reclaim them. + + REQUIREMENTS + + To make this work we need at least some hook inside VM scanning which + gets triggered after scanning (or scanning with particular priority) + failed to free pages. This is already present in the + mm/vmscan.c:set_shrinker() interface. + + Another useful thing that we would like to have is passing scanning + priority down to the ->vm_writeback() that will allow file system to + switch to the emergency flush more gracefully. + + POSSIBLE ALGORITHMS + + 1 Start emergency flush from ->vm_writeback after reaching some priority. + This allows to implement simple page based algorithm: look at the page VM + supplied us with and decide what to do. + + 2 Start emergency flush from shrinker after reaching some priority. + This delays emergency flush as far as possible. + + *****END OF HISTORICAL SECTION********************************************** + +*/ + +#include "forward.h" +#include "debug.h" +#include "page_cache.h" +#include "tree.h" +#include "jnode.h" +#include "znode.h" +#include "inode.h" +#include "super.h" +#include "block_alloc.h" +#include "emergency_flush.h" + +#include +#include +#include +#include +#include + +#if REISER4_USE_EFLUSH + +static int flushable(const jnode * node, struct page *page, int); +static int needs_allocation(const jnode * node); +static eflush_node_t *ef_alloc(int flags); +static reiser4_ba_flags_t ef_block_flags(const jnode *node); +static int ef_free_block(jnode *node, const reiser4_block_nr *blk, block_stage_t stage, eflush_node_t *ef); +static int ef_prepare(jnode *node, reiser4_block_nr *blk, eflush_node_t **enode, reiser4_blocknr_hint *hint); +static int eflush_add(jnode *node, reiser4_block_nr *blocknr, eflush_node_t *ef); + +/* slab for eflush_node_t's */ +static kmem_cache_t *eflush_slab; + +#define EFLUSH_START_BLOCK ((reiser4_block_nr)0) + +#define INC_STAT(node, counter) \ + reiser4_stat_inc_at_level(jnode_get_level(node), counter); + +/* this function exists only until VM gets fixed to reserve pages properly, + * which might or might not be very political. */ +/* try to flush @page to the disk + * + * Return 0 if page was successfully paged out. 1 if it is busy, error + * otherwise. + */ +reiser4_internal int +emergency_flush(struct page *page) +{ + struct super_block *sb; + jnode *node; + int result; + assert("nikita-2721", page != NULL); + assert("nikita-2724", PageLocked(page)); + + // warning("nikita-3112", "Emergency flush. Notify Reiser@Namesys.COM"); + + /* + * Page is locked, hence page<->jnode mapping cannot change. + */ + + sb = page->mapping->host->i_sb; + node = jprivate(page); + + assert("vs-1452", node != NULL); + + jref(node); + INC_STAT(node, vm.eflush.called); + + result = 0; + LOCK_JNODE(node); + /* + * page was dirty and under eflush. This is (only?) possible if page + * was re-dirtied through mmap(2) after eflush IO was submitted, but + * before ->releasepage() freed page. + */ + eflush_del(node, 1); + + LOCK_JLOAD(node); + if (flushable(node, page, 1)) { + if (needs_allocation(node)) { + reiser4_block_nr blk; + eflush_node_t *efnode; + reiser4_blocknr_hint hint; + + blk = 0ull; + efnode = NULL; + + /* Set JNODE_EFLUSH bit _before_ allocating a block, + * that prevents flush reserved block from using here + * and by a reiser4 flush process */ + JF_SET(node, JNODE_EFLUSH); + + blocknr_hint_init(&hint); + + INC_STAT(node, vm.eflush.needs_block); + result = ef_prepare(node, &blk, &efnode, &hint); + if (flushable(node, page, 0) && result == 0) { + assert("nikita-2759", efnode != NULL); + eflush_add(node, &blk, efnode); + + result = page_io(page, node, WRITE, + GFP_NOFS | __GFP_HIGH); + INC_STAT(node, vm.eflush.ok); + } else { + JF_CLR(node, JNODE_EFLUSH); + UNLOCK_JLOAD(node); + UNLOCK_JNODE(node); + if (blk != 0ull) { + ef_free_block(node, &blk, + hint.block_stage, efnode); + kmem_cache_free(eflush_slab, efnode); + } + ON_TRACE(TRACE_EFLUSH, "failure-2\n"); + result = 1; + INC_STAT(node, vm.eflush.nolonger); + } + + blocknr_hint_done(&hint); + } else { + txn_atom *atom; + flush_queue_t *fq; + + /* eflush without allocation temporary location for a node */ + ON_TRACE(TRACE_EFLUSH, "flushing to relocate place: %llu..", *jnode_get_block(node)); + + /* get flush queue for this node */ + result = fq_by_jnode_gfp(node, &fq, GFP_ATOMIC); + + if (result) + return result; + + atom = node->atom; + + if (!flushable(node, page, 1) || needs_allocation(node) || !jnode_is_dirty(node)) { + ON_TRACE(TRACE_EFLUSH, "failure-3\n"); + UNLOCK_JLOAD(node); + UNLOCK_JNODE(node); + UNLOCK_ATOM(atom); + fq_put(fq); + return 1; + } + + /* ok, now we can flush it */ + unlock_page(page); + + queue_jnode(fq, node); + + UNLOCK_JLOAD(node); + UNLOCK_JNODE(node); + UNLOCK_ATOM(atom); + + result = write_fq(fq, NULL, 0); + if (result != 0) + lock_page(page); + + ON_TRACE(TRACE_EFLUSH, "flushed %d blocks\n", result); + /* Even if we wrote nothing, We unlocked the page, so let know to the caller that page should + not be unlocked again */ + fq_put(fq); + } + + } else { + UNLOCK_JLOAD(node); + UNLOCK_JNODE(node); + ON_TRACE(TRACE_EFLUSH, "failure-1\n"); + result = 1; + } + + jput(node); + return result; +} + +static int +flushable(const jnode * node, struct page *page, int check_eflush) +{ + assert("nikita-2725", node != NULL); + assert("nikita-2726", spin_jnode_is_locked(node)); + assert("nikita-3388", spin_jload_is_locked(node)); + + if (jnode_is_loaded(node)) { /* loaded */ + INC_STAT(node, vm.eflush.loaded); + return 0; + } + if (JF_ISSET(node, JNODE_FLUSH_QUEUED)) { /* already pending io */ + INC_STAT(node, vm.eflush.queued); + return 0; + } + if (JF_ISSET(node, JNODE_EPROTECTED)) { /* protected from e-flush */ + INC_STAT(node, vm.eflush.protected); + return 0; + } + if (JF_ISSET(node, JNODE_HEARD_BANSHEE)) { + INC_STAT(node, vm.eflush.heard_banshee); + return 0; + } + if (page == NULL) { /* nothing to flush */ + INC_STAT(node, vm.eflush.nopage); + return 0; + } + if (PageWriteback(page)) { /* already under io */ + INC_STAT(node, vm.eflush.writeback); + return 0; + } + /* don't flush bitmaps or journal records */ + if (!jnode_is_znode(node) && !jnode_is_unformatted(node)) { + INC_STAT(node, vm.eflush.bitmap); + return 0; + } + /* don't flush cluster pages */ + if (jnode_is_cluster_page(node)) { + INC_STAT(node, vm.eflush.clustered); + return 0; + } + if (check_eflush && JF_ISSET(node, JNODE_EFLUSH)) { /* already flushed */ + INC_STAT(node, vm.eflush.eflushed); + return 0; + } + return 1; +} + +#undef INC_STAT + +/* does node need allocation for eflushing? */ +static int +needs_allocation(const jnode * node) +{ + return !(JF_ISSET(node, JNODE_RELOC) && !blocknr_is_fake(jnode_get_block(node))); +} + + +static inline int +jnode_eq(jnode * const * j1, jnode * const * j2) +{ + assert("nikita-2733", j1 != NULL); + assert("nikita-2734", j2 != NULL); + + return *j1 == *j2; +} + +static ef_hash_table * +get_jnode_enhash(const jnode *node) +{ + struct super_block *super; + + assert("nikita-2739", node != NULL); + + super = jnode_get_tree(node)->super; + return &get_super_private(super)->efhash_table; +} + +static inline __u32 +jnode_hfn(ef_hash_table *table, jnode * const * j) +{ + __u32 val; + + assert("nikita-2735", j != NULL); + assert("nikita-3346", IS_POW(table->_buckets)); + + val = (unsigned long)*j; + val /= sizeof(**j); + return val & (table->_buckets - 1); +} + + +/* The hash table definition */ +#define KMALLOC(size) vmalloc(size) +#define KFREE(ptr, size) vfree(ptr) +TYPE_SAFE_HASH_DEFINE(ef, eflush_node_t, jnode *, node, linkage, jnode_hfn, jnode_eq); +#undef KFREE +#undef KMALLOC + +reiser4_internal int +eflush_init(void) +{ + eflush_slab = kmem_cache_create("eflush", sizeof (eflush_node_t), + 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + if (eflush_slab == NULL) + return RETERR(-ENOMEM); + else + return 0; +} + +reiser4_internal int +eflush_done(void) +{ + return kmem_cache_destroy(eflush_slab); +} + +reiser4_internal int +eflush_init_at(struct super_block *super) +{ + return ef_hash_init(&get_super_private(super)->efhash_table, + 8192, + reiser4_stat(super, hashes.eflush)); +} + +reiser4_internal void +eflush_done_at(struct super_block *super) +{ + ef_hash_done(&get_super_private(super)->efhash_table); +} + +static eflush_node_t * +ef_alloc(int flags) +{ + return kmem_cache_alloc(eflush_slab, flags); +} + +#define EFLUSH_MAGIC 4335203 + +static int +eflush_add(jnode *node, reiser4_block_nr *blocknr, eflush_node_t *ef) +{ + reiser4_tree *tree; + + assert("nikita-2737", node != NULL); + assert("nikita-2738", JF_ISSET(node, JNODE_EFLUSH)); + assert("nikita-3382", !JF_ISSET(node, JNODE_EPROTECTED)); + assert("nikita-2765", spin_jnode_is_locked(node)); + assert("nikita-3381", spin_jload_is_locked(node)); + + tree = jnode_get_tree(node); + + ef->node = node; + ef->blocknr = *blocknr; + ef->hadatom = (node->atom != NULL); + ef->incatom = 0; + jref(node); + spin_lock_eflush(tree->super); + ef_hash_insert(get_jnode_enhash(node), ef); + ON_DEBUG(++ get_super_private(tree->super)->eflushed); + spin_unlock_eflush(tree->super); + + if (jnode_is_unformatted(node)) { + struct inode *inode; + reiser4_inode *info; + + WLOCK_TREE(tree); + + inode = mapping_jnode(node)->host; + info = reiser4_inode_data(inode); + + if (!ef->hadatom) { + radix_tree_tag_set(jnode_tree_by_reiser4_inode(info), + index_jnode(node), EFLUSH_TAG_ANONYMOUS); + ON_DEBUG(info->anonymous_eflushed ++); + } else { + radix_tree_tag_set(jnode_tree_by_reiser4_inode(info), + index_jnode(node), EFLUSH_TAG_CAPTURED); + ON_DEBUG(info->captured_eflushed ++); + } + WUNLOCK_TREE(tree); + /*XXXX*/ + inc_unfm_ef(); + } + + /* FIXME: do we need it here, if eflush add/del are protected by page lock? */ + UNLOCK_JLOAD(node); + + /* + * jnode_get_atom() can possible release jnode spin lock. This + * means it can only be called _after_ JNODE_EFLUSH is set, because + * otherwise we would have to re-check flushable() once more. No + * thanks. + */ + + if (ef->hadatom) { + txn_atom *atom; + + atom = jnode_get_atom(node); + if (atom != NULL) { + ++ atom->flushed; + ef->incatom = 1; + UNLOCK_ATOM(atom); + } + } + + UNLOCK_JNODE(node); + return 0; +} + +/* Arrghh... cast to keep hash table code happy. */ +#define C(node) ((jnode *const *)&(node)) + +reiser4_internal reiser4_block_nr * +eflush_get(const jnode *node) +{ + eflush_node_t *ef; + reiser4_tree *tree; + + assert("nikita-2740", node != NULL); + assert("nikita-2741", JF_ISSET(node, JNODE_EFLUSH)); + assert("nikita-2767", spin_jnode_is_locked(node)); + + + tree = jnode_get_tree(node); + spin_lock_eflush(tree->super); + ef = ef_hash_find(get_jnode_enhash(node), C(node)); + spin_unlock_eflush(tree->super); + + assert("nikita-2742", ef != NULL); + return &ef->blocknr; +} + +/* free resources taken for emergency flushing of the node */ +static void eflush_free (jnode * node) +{ + eflush_node_t *ef; + ef_hash_table *table; + reiser4_tree *tree; + txn_atom *atom; + struct inode *inode = NULL; + reiser4_block_nr blk; + + assert ("zam-1026", spin_jnode_is_locked(node)); + + table = get_jnode_enhash(node); + tree = jnode_get_tree(node); + + spin_lock_eflush(tree->super); + ef = ef_hash_find(table, C(node)); + BUG_ON(ef == NULL); + assert("nikita-2745", ef != NULL); + blk = ef->blocknr; + ef_hash_remove(table, ef); + ON_DEBUG(-- get_super_private(tree->super)->eflushed); + spin_unlock_eflush(tree->super); + + if (ef->incatom) { + atom = jnode_get_atom(node); + assert("nikita-3311", atom != NULL); + -- atom->flushed; + UNLOCK_ATOM(atom); + } + + assert("vs-1215", JF_ISSET(node, JNODE_EFLUSH)); + + if (jnode_is_unformatted(node)) { + reiser4_inode *info; + + WLOCK_TREE(tree); + + inode = mapping_jnode(node)->host; + info = reiser4_inode_data(inode); + + /* clear e-flush specific tags from node's radix tree slot */ + radix_tree_tag_clear( + jnode_tree_by_reiser4_inode(info), index_jnode(node), + ef->hadatom ? EFLUSH_TAG_CAPTURED : EFLUSH_TAG_ANONYMOUS); + ON_DEBUG(ef->hadatom ? (info->captured_eflushed --) : (info->anonymous_eflushed --)); + + assert("nikita-3355", ergo(jnode_tree_by_reiser4_inode(info)->rnode == NULL, + (info->captured_eflushed == 0 && info->anonymous_eflushed == 0))); + + WUNLOCK_TREE(tree); + + /*XXXX*/ + dec_unfm_ef(); + + } + UNLOCK_JNODE(node); + +#if REISER4_DEBUG + if (blocknr_is_fake(jnode_get_block(node))) + assert ("zam-817", ef->initial_stage == BLOCK_UNALLOCATED); + else + assert ("zam-818", ef->initial_stage == BLOCK_GRABBED); +#endif + + jput(node); + + ef_free_block(node, &blk, + blocknr_is_fake(jnode_get_block(node)) ? + BLOCK_UNALLOCATED : BLOCK_GRABBED, ef); + + kmem_cache_free(eflush_slab, ef); + + LOCK_JNODE(node); +} + +reiser4_internal void eflush_del (jnode * node, int page_locked) +{ + struct page * page; + + assert("nikita-2743", node != NULL); + assert("nikita-2770", spin_jnode_is_locked(node)); + + if (!JF_ISSET(node, JNODE_EFLUSH)) + return; + + if (page_locked) { + page = jnode_page(node); + assert("nikita-2806", page != NULL); + assert("nikita-2807", PageLocked(page)); + } else { + UNLOCK_JNODE(node); + page = jnode_get_page_locked(node, GFP_NOFS); + LOCK_JNODE(node); + if (page == NULL) { + warning ("zam-1025", "eflush_del failed to get page back\n"); + return; + } + if (unlikely(!JF_ISSET(node, JNODE_EFLUSH))) + /* race: some other thread unflushed jnode. */ + goto out; + } + + if (PageWriteback(page)) { + UNLOCK_JNODE(node); + page_cache_get(page); + reiser4_wait_page_writeback(page); + page_cache_release(page); + LOCK_JNODE(node); + if (unlikely(!JF_ISSET(node, JNODE_EFLUSH))) + /* race: some other thread unflushed jnode. */ + goto out; + } + + if (JF_ISSET(node, JNODE_KEEPME)) + set_page_dirty(page); + else + /* + * either jnode was dirty or page was dirtied through mmap. Page's dirty + * bit was cleared before io was submitted. If page is left clean, we + * would have dirty jnode with clean page. Neither ->writepage() nor + * ->releasepage() can free it. Re-dirty page, so ->writepage() will be + * called again if necessary. + */ + set_page_dirty_internal(page, 0); + + assert("nikita-2766", atomic_read(&node->x_count) > 1); + /* release allocated disk block and in-memory structures */ + eflush_free(node); + JF_CLR(node, JNODE_EFLUSH); + out: + if (!page_locked) + unlock_page(page); +} + +reiser4_internal int +emergency_unflush(jnode *node) +{ + int result; + + assert("nikita-2778", node != NULL); + assert("nikita-3046", schedulable()); + + if (JF_ISSET(node, JNODE_EFLUSH)) { + result = jload(node); + if (result == 0) { + struct page *page; + + assert("nikita-2777", !JF_ISSET(node, JNODE_EFLUSH)); + page = jnode_page(node); + assert("nikita-2779", page != NULL); + wait_on_page_writeback(page); + + jrelse(node); + } + } else + result = 0; + return result; +} + +static reiser4_ba_flags_t +ef_block_flags(const jnode *node) +{ + return jnode_is_znode(node) ? BA_FORMATTED : 0; +} + +static int ef_free_block(jnode *node, + const reiser4_block_nr *blk, + block_stage_t stage, eflush_node_t *ef) +{ + int result = 0; + + /* We cannot just ask block allocator to return block into flush + * reserved space, because there is no current atom at this point. */ + result = reiser4_dealloc_block(blk, stage, ef_block_flags(node)); + if (result == 0 && stage == BLOCK_GRABBED) { + txn_atom *atom; + + if (ef->reserve) { + /* further, transfer block from grabbed into flush + * reserved space. */ + LOCK_JNODE(node); + atom = jnode_get_atom(node); + assert("nikita-2785", atom != NULL); + grabbed2flush_reserved_nolock(atom, 1); + UNLOCK_ATOM(atom); + JF_SET(node, JNODE_FLUSH_RESERVED); + UNLOCK_JNODE(node); + } else { + reiser4_context * ctx = get_current_context(); + grabbed2free(ctx, get_super_private(ctx->super), + (__u64)1); + } + } + return result; +} + +static int +ef_prepare(jnode *node, reiser4_block_nr *blk, eflush_node_t **efnode, reiser4_blocknr_hint * hint) +{ + int result; + int usedreserve; + + assert("nikita-2760", node != NULL); + assert("nikita-2761", blk != NULL); + assert("nikita-2762", efnode != NULL); + assert("nikita-2763", spin_jnode_is_locked(node)); + assert("nikita-3387", spin_jload_is_locked(node)); + + hint->blk = EFLUSH_START_BLOCK; + hint->max_dist = 0; + hint->level = jnode_get_level(node); + usedreserve = 0; + if (blocknr_is_fake(jnode_get_block(node))) + hint->block_stage = BLOCK_UNALLOCATED; + else { + txn_atom *atom; + switch (jnode_is_leaf(node)) { + default: + /* We cannot just ask block allocator to take block from + * flush reserved space, because there is no current + * atom at this point. */ + atom = jnode_get_atom(node); + if (atom != NULL) { + if (JF_ISSET(node, JNODE_FLUSH_RESERVED)) { + usedreserve = 1; + flush_reserved2grabbed(atom, 1); + JF_CLR(node, JNODE_FLUSH_RESERVED); + UNLOCK_ATOM(atom); + break; + } else + UNLOCK_ATOM(atom); + } + /* fall through */ + /* node->atom == NULL if page was dirtied through + * mmap */ + case 0: + result = reiser4_grab_space_force((__u64)1, BA_RESERVED); + grab_space_enable(); + if (result) { + warning("nikita-3323", + "Cannot allocate eflush block"); + return result; + } + } + + hint->block_stage = BLOCK_GRABBED; + } + + /* XXX protect @node from being concurrently eflushed. Otherwise, + * there is a danger of underflowing block space */ + UNLOCK_JLOAD(node); + UNLOCK_JNODE(node); + + *efnode = ef_alloc(GFP_NOFS | __GFP_HIGH); + if (*efnode == NULL) { + result = RETERR(-ENOMEM); + goto out; + } + +#if REISER4_DEBUG + (*efnode)->initial_stage = hint->block_stage; +#endif + (*efnode)->reserve = usedreserve; + + result = reiser4_alloc_block(hint, blk, ef_block_flags(node)); + if (result) + kmem_cache_free(eflush_slab, *efnode); + out: + LOCK_JNODE(node); + LOCK_JLOAD(node); + return result; +} + +#endif /* REISER4_USE_EFLUSH */ + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 80 + LocalWords: " unflush eflushed LocalWords eflush writepage VM releasepage unflushing io " + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/emergency_flush.h 2004-09-03 00:57:05.030274544 -0700 @@ -0,0 +1,75 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Emergency flush */ + +#ifndef __EMERGENCY_FLUSH_H__ +#define __EMERGENCY_FLUSH_H__ + +#if REISER4_USE_EFLUSH + +#include "block_alloc.h" + +struct eflush_node; +typedef struct eflush_node eflush_node_t; + +TYPE_SAFE_HASH_DECLARE(ef, eflush_node_t); + +struct eflush_node { + jnode *node; + reiser4_block_nr blocknr; + ef_hash_link linkage; + struct list_head inode_link; /* for per inode list of eflush nodes */ + struct list_head inode_anon_link; + int hadatom :1; + int incatom :1; + int reserve :1; +#if REISER4_DEBUG + block_stage_t initial_stage; +#endif +}; + +int eflush_init(void); +int eflush_done(void); + +extern int eflush_init_at(struct super_block *super); +extern void eflush_done_at(struct super_block *super); + +extern reiser4_block_nr *eflush_get(const jnode *node); +extern void eflush_del(jnode *node, int page_locked); + +extern int emergency_flush(struct page *page); +extern int emergency_unflush(jnode *node); + +/* eflushed jnodes are stored in reiser4_inode's radix tree. Eflushed jnodes may be either "captured" or + * "anonymous". Use existing tags to tag jnodes in reiser4_inode's tree of eflushed jnodes */ +#define EFLUSH_TAG_ANONYMOUS PAGECACHE_TAG_DIRTY +#define EFLUSH_TAG_CAPTURED PAGECACHE_TAG_WRITEBACK + +#else /* REISER4_USE_EFLUSH */ + +#define eflush_init() (0) +#define eflush_done() (0) + +#define eflush_init_at(super) (0) +#define eflush_done_at(super) (0) + +#define eflush_get(node) NULL +#define eflush_del(node, flag) do{}while(0) + +#define emergency_unflush(node) (0) +#define emergency_flush(page) (1) + +#endif /* REISER4_USE_EFLUSH */ + +/* __EMERGENCY_FLUSH_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/entd.c 2004-09-03 00:57:05.031274392 -0700 @@ -0,0 +1,377 @@ +/* Copyright 2003, 2004 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Ent daemon. */ + +#include "debug.h" +#include "kcond.h" +#include "txnmgr.h" +#include "tree.h" +#include "entd.h" +#include "super.h" +#include "context.h" +#include "reiser4.h" +#include "vfs_ops.h" +#include "page_cache.h" + +#include /* struct task_struct */ +#include +#include +#include +#include /* INITIAL_JIFFIES */ +#include /* bdi_write_congested */ + +TYPE_SAFE_LIST_DEFINE(wbq, struct wbq, link); + +#define DEF_PRIORITY 12 +#define MAX_ENTD_ITERS 10 +#define ENTD_ASYNC_REQUESTS_LIMIT 32 + +static void entd_flush(struct super_block *super); +static int entd(void *arg); + +/* + * set ->comm field of end thread to make its state visible to the user level + */ +#define entd_set_comm(state) \ + snprintf(current->comm, sizeof(current->comm), \ + "ent:%s%s", super->s_id, (state)) + +/* get ent context for the @super */ +static inline entd_context * +get_entd_context(struct super_block *super) +{ + return &get_super_private(super)->entd; +} + +/* initialize ent thread context */ +reiser4_internal void +init_entd_context(struct super_block *super) +{ + entd_context * ctx; + + assert("nikita-3104", super != NULL); + + ctx = get_entd_context(super); + + xmemset(ctx, 0, sizeof *ctx); + kcond_init(&ctx->startup); + kcond_init(&ctx->wait); + init_completion(&ctx->finish); + spin_lock_init(&ctx->guard); + + /* start ent thread.. */ + kernel_thread(entd, super, CLONE_VM | CLONE_FS | CLONE_FILES); + + spin_lock(&ctx->guard); + /* and wait for its initialization to finish */ + while (ctx->tsk == NULL) + kcond_wait(&ctx->startup, &ctx->guard, 0); + spin_unlock(&ctx->guard); +#if REISER4_DEBUG + flushers_list_init(&ctx->flushers_list); +#endif + wbq_list_init(&ctx->wbq_list); +} + +static void wakeup_wbq (entd_context * ent, struct wbq * rq) +{ + wbq_list_remove(rq); + ent->nr_synchronous_requests --; + rq->wbc->nr_to_write --; + up(&rq->sem); +} + +static void wakeup_all_wbq (entd_context * ent) +{ + struct wbq * rq; + + spin_lock(&ent->guard); + while (!wbq_list_empty(&ent->wbq_list)) { + rq = wbq_list_front(&ent->wbq_list); + wakeup_wbq(ent, rq); + } + spin_unlock(&ent->guard); +} + +/* ent thread function */ +static int +entd(void *arg) +{ + struct super_block *super; + struct task_struct *me; + entd_context *ent; + + assert("vs-1655", list_empty(¤t->private_pages)); + + super = arg; + /* standard kernel thread prologue */ + me = current; + /* reparent_to_init() is done by daemonize() */ + daemonize("ent:%s", super->s_id); + + /* block all signals */ + spin_lock_irq(&me->sighand->siglock); + siginitsetinv(&me->blocked, 0); + recalc_sigpending(); + spin_unlock_irq(&me->sighand->siglock); + + /* do_fork() just copies task_struct into the new + thread. ->fs_context shouldn't be copied of course. This shouldn't + be a problem for the rest of the code though. + */ + me->journal_info = NULL; + + ent = get_entd_context(super); + + spin_lock(&ent->guard); + ent->tsk = me; + /* signal waiters that initialization is completed */ + kcond_broadcast(&ent->startup); + spin_unlock(&ent->guard); + while (1) { + int result = 0; + + if (me->flags & PF_FREEZE) + refrigerator(PF_FREEZE); + + spin_lock(&ent->guard); + + while (ent->nr_all_requests != 0) { + assert("zam-1043", ent->nr_all_requests >= ent->nr_synchronous_requests); + if (ent->nr_synchronous_requests != 0) { + struct wbq * rq = wbq_list_front(&ent->wbq_list); + + if (++ rq->nr_entd_iters > MAX_ENTD_ITERS) { + ent->nr_all_requests --; + wakeup_wbq(ent, rq); + continue; + } + } else { + /* endless loop avoidance. */ + ent->nr_all_requests --; + } + + spin_unlock(&ent->guard); + entd_set_comm("!"); + entd_flush(super); + spin_lock(&ent->guard); + } + + entd_set_comm("."); + + /* wait for work */ + result = kcond_wait(&ent->wait, &ent->guard, 1); + if (result != -EINTR && result != 0) + /* some other error */ + warning("nikita-3099", "Error: %i", result); + + /* we are asked to exit */ + if (ent->done) { + spin_unlock(&ent->guard); + break; + } + + spin_unlock(&ent->guard); + } + wakeup_all_wbq(ent); + complete_and_exit(&ent->finish, 0); + /* not reached. */ + return 0; +} + +/* called by umount */ +reiser4_internal void +done_entd_context(struct super_block *super) +{ + entd_context * ent; + + assert("nikita-3103", super != NULL); + + ent = get_entd_context(super); + + spin_lock(&ent->guard); + ent->done = 1; + kcond_signal(&ent->wait); + spin_unlock(&ent->guard); + + /* wait until daemon finishes */ + wait_for_completion(&ent->finish); +} + +/* called at the beginning of jnode_flush to register flusher thread with ent + * daemon */ +reiser4_internal void enter_flush (struct super_block * super) +{ + entd_context * ent; + + assert ("zam-1029", super != NULL); + ent = get_entd_context(super); + + assert ("zam-1030", ent != NULL); + + spin_lock(&ent->guard); + ent->flushers ++; +#if REISER4_DEBUG + flushers_list_push_front(&ent->flushers_list, get_current_context()); +#endif + spin_unlock(&ent->guard); +} + +/* called at the end of jnode_flush */ +reiser4_internal void leave_flush (struct super_block * super) +{ + entd_context * ent; + + assert ("zam-1027", super != NULL); + ent = get_entd_context(super); + + assert ("zam-1028", ent != NULL); + + spin_lock(&ent->guard); + ent->flushers --; + if (ent->flushers == 0 && ent->nr_synchronous_requests != 0) + kcond_signal(&ent->wait); +#if REISER4_DEBUG + flushers_list_remove_clean(get_current_context()); +#endif + spin_unlock(&ent->guard); +} + +/* signal to ent thread that it has more work to do */ +static void kick_entd(entd_context * ent) +{ + kcond_signal(&ent->wait); +} + +static void entd_capture_anonymous_pages( + struct super_block * super, struct writeback_control * wbc) +{ + spin_lock(&inode_lock); + capture_reiser4_inodes(super, wbc); + spin_unlock(&inode_lock); +} + +static void entd_flush(struct super_block *super) +{ + long nr_submitted = 0; + int result; + reiser4_context ctx; + struct writeback_control wbc = { + .bdi = NULL, + .sync_mode = WB_SYNC_NONE, + .older_than_this = NULL, + .nr_to_write = 32, + .nonblocking = 0, + }; + + init_context(&ctx, super); + + ctx.entd = 1; + + entd_capture_anonymous_pages(super, &wbc); + result = flush_some_atom(&nr_submitted, &wbc, 0); + if (result != 0) + warning("nikita-3100", "Flush failed: %i", result); + + context_set_commit_async(&ctx); + reiser4_exit_context(&ctx); +} + +void write_page_by_ent (struct page * page, struct writeback_control * wbc) +{ + struct super_block * sb; + entd_context * ent; + struct wbq rq; + int phantom; + + sb = page->mapping->host->i_sb; + ent = get_entd_context(sb); + + phantom = jprivate(page) == NULL || !jnode_check_dirty(jprivate(page)); + /* re-dirty page */ + set_page_dirty_internal(page, phantom); + /* unlock it to avoid deadlocks with the thread which will do actual i/o */ + unlock_page(page); + + /* entd is not running. */ + if (ent == NULL || ent->done) + return; + + /* init wbq */ + wbq_list_clean(&rq); + rq.nr_entd_iters = 0; + rq.page = page; + rq.wbc = wbc; + + spin_lock(&ent->guard); + if (ent->flushers == 0) + kick_entd(ent); + ent->nr_all_requests ++; + if (ent->nr_all_requests <= ent->nr_synchronous_requests + ENTD_ASYNC_REQUESTS_LIMIT) { + spin_unlock(&ent->guard); + return; + } + sema_init(&rq.sem, 0); + wbq_list_push_back(&ent->wbq_list, &rq); + ent->nr_synchronous_requests ++; + spin_unlock(&ent->guard); + down(&rq.sem); + + /* don't release rq until wakeup_wbq stops using it. */ + spin_lock(&ent->guard); + spin_unlock(&ent->guard); + /* wbq dequeued by the ent thread (by another then current thread). */ +} + +/* ent should be locked */ +static struct wbq * get_wbq (entd_context * ent) +{ + if (wbq_list_empty(&ent->wbq_list)) { + spin_unlock(&ent->guard); + return NULL; + } + return wbq_list_front(&ent->wbq_list); +} + + +void ent_writes_page (struct super_block * sb, struct page * page) +{ + entd_context * ent = get_entd_context(sb); + struct wbq * rq; + + assert("zam-1041", ent != NULL); + + if (PageActive(page) || ent->nr_all_requests == 0) + return; + + SetPageReclaim(page); + + spin_lock(&ent->guard); + if (ent->nr_all_requests > 0) { + ent->nr_all_requests --; + rq = get_wbq(ent); + if (rq == NULL) + /* get_wbq() releases entd->guard spinlock if NULL is + * returned. */ + return; + wakeup_wbq(ent, rq); + } + spin_unlock(&ent->guard); +} + +int wbq_available (void) { + struct super_block * sb = reiser4_get_current_sb(); + entd_context * ent = get_entd_context(sb); + return ent->nr_all_requests; +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 80 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/entd.h 2004-09-03 00:57:05.032274240 -0700 @@ -0,0 +1,83 @@ +/* Copyright 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Ent daemon. */ + +#ifndef __ENTD_H__ +#define __ENTD_H__ + +#include "kcond.h" +#include "context.h" + +#include +#include +#include +#include /* for struct task_struct */ +#include "type_safe_list.h" + +TYPE_SAFE_LIST_DECLARE(wbq); + +/* write-back request. */ +struct wbq { + wbq_list_link link; + struct writeback_control * wbc; + struct page * page; + struct semaphore sem; + int nr_entd_iters; +}; + +/* ent-thread context. This is used to synchronize starting/stopping ent + * threads. */ +typedef struct entd_context { + /* + * condition variable that is signaled by ent thread after it + * successfully started up. + */ + kcond_t startup; + /* + * completion that is signaled by ent thread just before it + * terminates. + */ + struct completion finish; + /* + * condition variable that ent thread waits on for more work. It's + * signaled by write_page_by_ent(). + */ + kcond_t wait; + /* spinlock protecting other fields */ + spinlock_t guard; + /* ent thread */ + struct task_struct *tsk; + /* set to indicate that ent thread should leave. */ + int done; + /* counter of active flushers */ + int flushers; +#if REISER4_DEBUG + /* list of all active flushers */ + flushers_list_head flushers_list; +#endif + int nr_all_requests; + int nr_synchronous_requests; + wbq_list_head wbq_list; +} entd_context; + +extern void init_entd_context(struct super_block *super); +extern void done_entd_context(struct super_block *super); + +extern void enter_flush(struct super_block *super); +extern void leave_flush(struct super_block *super); + +extern void write_page_by_ent(struct page *, struct writeback_control *); +extern int wbq_available (void); +extern void ent_writes_page (struct super_block *, struct page *); +/* __ENTD_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/eottl.c 2004-09-03 00:57:05.034273936 -0700 @@ -0,0 +1,372 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#include "forward.h" +#include "debug.h" +#include "key.h" +#include "coord.h" +#include "plugin/item/item.h" +#include "plugin/node/node.h" +#include "znode.h" +#include "block_alloc.h" +#include "tree_walk.h" +#include "tree_mod.h" +#include "carry.h" +#include "tree.h" +#include "super.h" + +#include /* for __u?? */ + +/* Extents on the twig level (EOTTL) handling. + + EOTTL poses some problems to the tree traversal, that are better + explained by example. + + Suppose we have block B1 on the twig level with the following items: + + 0. internal item I0 with key (0:0:0:0) (locality, key-type, object-id, offset) + 1. extent item E1 with key (1:4:100:0), having 10 blocks of 4k each + 2. internal item I2 with key (10:0:0:0) + + We are trying to insert item with key (5:0:0:0). Lookup finds node + B1, and then intra-node lookup is done. This lookup finished on the + E1, because the key we are looking for is larger than the key of E1 + and is smaller than key the of I2. + + Here search is stuck. + + After some thought it is clear what is wrong here: extents on the + twig level break some basic property of the *search* tree (on the + pretext, that they restore property of balanced tree). + + Said property is the following: if in the internal node of the search + tree we have [ ... Key1 Pointer Key2 ... ] then, all data that are or + will be keyed in the tree with the Key such that Key1 <= Key < Key2 + are accessible through the Pointer. + + This is not true, when Pointer is Extent-Pointer, simply because + extent cannot expand indefinitely to the right to include any item + with + + Key1 <= Key <= Key2. + + For example, our E1 extent is only responsible for the data with keys + + (1:4:100:0) <= key <= (1:4:100:0xffffffffffffffff), and + + so, key range + + ( (1:4:100:0xffffffffffffffff), (10:0:0:0) ) + + is orphaned: there is no way to get there from the tree root. + + In other words, extent pointers are different than normal child + pointers as far as search tree is concerned, and this creates such + problems. + + Possible solution for this problem is to insert our item into node + pointed to by I2. There are some problems through: + + (1) I2 can be in a different node. + (2) E1 can be immediately followed by another extent E2. + + (1) is solved by calling reiser4_get_right_neighbor() and accounting + for locks/coords as necessary. + + (2) is more complex. Solution here is to insert new empty leaf node + and insert internal item between E1 and E2 pointing to said leaf + node. This is further complicated by possibility that E2 is in a + different node, etc. + + Problems: + + (1) if there was internal item I2 immediately on the right of an + extent E1 we and we decided to insert new item S1 into node N2 + pointed to by I2, then key of S1 will be less than smallest key in + the N2. Normally, search key checks that key we are looking for is in + the range of keys covered by the node key is being looked in. To work + around of this situation, while preserving useful consistency check + new flag CBK_TRUST_DK was added to the cbk falgs bitmask. This flag + is automatically set on entrance to the coord_by_key() and is only + cleared when we are about to enter situation described above. + + (2) If extent E1 is immediately followed by another extent E2 and we + are searching for the key that is between E1 and E2 we only have to + insert new empty leaf node when coord_by_key was called for + insertion, rather than just for lookup. To distinguish these cases, + new flag CBK_FOR_INSERT was added to the cbk falgs bitmask. This flag + is automatically set by coord_by_key calls performed by + insert_by_key() and friends. + + (3) Insertion of new empty leaf node (possibly) requires + balancing. In any case it requires modification of node content which + is only possible under write lock. It may well happen that we only + have read lock on the node where new internal pointer is to be + inserted (common case: lookup of non-existent stat-data that fells + between two extents). If only read lock is held, tree traversal is + restarted with lock_level modified so that next time we hit this + problem, write lock will be held. Once we have write lock, balancing + will be performed. + + + + + + +*/ + +/* look to the right of @coord. If it is an item of internal type - 1 is + returned. If that item is in right neighbor and it is internal - @coord and + @lh are switched to that node: move lock handle, zload right neighbor and + zrelse znode coord was set to at the beginning +*/ +/* Audited by: green(2002.06.15) */ +static int +is_next_item_internal(coord_t * coord) +{ + if (coord->item_pos != node_num_items(coord->node) - 1) { + /* next item is in the same node */ + coord_t right; + + coord_dup(&right, coord); + check_me("vs-742", coord_next_item(&right) == 0); + if (item_is_internal(&right)) { + coord_dup(coord, &right); + return 1; + } + } + return 0; +} + +/* inserting empty leaf after (or between) item of not internal type we have to + know which right delimiting key corresponding znode has to be inserted with */ +static reiser4_key * +rd_key(coord_t * coord, reiser4_key * key) +{ + coord_t dup; + + assert("nikita-2281", coord_is_between_items(coord)); + coord_dup(&dup, coord); + + RLOCK_DK(current_tree); + + if (coord_set_to_right(&dup) == 0) + /* get right delimiting key from an item to the right of @coord */ + unit_key_by_coord(&dup, key); + else + /* use right delimiting key of parent znode */ + *key = *znode_get_rd_key(coord->node); + + RUNLOCK_DK(current_tree); + return key; +} + + +ON_DEBUG(void check_dkeys(const znode *);) + +/* this is used to insert empty node into leaf level if tree lookup can not go + further down because it stopped between items of not internal type */ +static int +add_empty_leaf(coord_t * insert_coord, lock_handle * lh, const reiser4_key * key, const reiser4_key * rdkey) +{ + int result; + carry_pool pool; + carry_level todo; + carry_op *op; + /*znode *parent_node;*/ + znode *node; + reiser4_item_data item; + carry_insert_data cdata; + reiser4_tree *tree; + + init_carry_pool(&pool); + init_carry_level(&todo, &pool); + ON_STATS(todo.level_no = TWIG_LEVEL); + assert("vs-49827", znode_contains_key_lock(insert_coord->node, key)); + + tree = znode_get_tree(insert_coord->node); + node = new_node(insert_coord->node, LEAF_LEVEL); + if (IS_ERR(node)) + return PTR_ERR(node); + + /* setup delimiting keys for node being inserted */ + WLOCK_DK(tree); + znode_set_ld_key(node, key); + znode_set_rd_key(node, rdkey); + ON_DEBUG(node->creator = current); + ON_DEBUG(node->first_key = *key); + WUNLOCK_DK(tree); + + ZF_SET(node, JNODE_ORPHAN); + op = post_carry(&todo, COP_INSERT, insert_coord->node, 0); + if (!IS_ERR(op)) { + cdata.coord = insert_coord; + cdata.key = key; + cdata.data = &item; + op->u.insert.d = &cdata; + op->u.insert.type = COPT_ITEM_DATA; + build_child_ptr_data(node, &item); + item.arg = NULL; + /* have @insert_coord to be set at inserted item after + insertion is done */ + todo.track_type = CARRY_TRACK_CHANGE; + todo.tracked = lh; + + result = carry(&todo, 0); + if (result == 0) { + /* + * pin node in memory. This is necessary for + * znode_make_dirty() below. + */ + result = zload(node); + if (result == 0) { + lock_handle local_lh; + + /* + * if we inserted new child into tree we have + * to mark it dirty so that flush will be able + * to process it. + */ + init_lh(&local_lh); + result = longterm_lock_znode(&local_lh, node, + ZNODE_WRITE_LOCK, + ZNODE_LOCK_LOPRI); + if (result == 0) { + znode_make_dirty(node); + + /* when internal item pointing to @node + was inserted into twig node + create_hook_internal did not connect + it properly because its right + neighbor was not known. Do it + here */ + WLOCK_TREE(tree); + assert("nikita-3312", znode_is_right_connected(node)); + assert("nikita-2984", node->right == NULL); + ZF_CLR(node, JNODE_RIGHT_CONNECTED); + WUNLOCK_TREE(tree); + result = connect_znode(insert_coord, node); + if (result == 0) + ON_DEBUG(check_dkeys(node)); + + done_lh(lh); + move_lh(lh, &local_lh); + assert("vs-1676", node_is_empty(node)); + coord_init_first_unit(insert_coord, node); + } else { + warning("nikita-3136", + "Cannot lock child"); + print_znode("child", node); + } + done_lh(&local_lh); + zrelse(node); + } + } + } else + result = PTR_ERR(op); + zput(node); + done_carry_pool(&pool); + return result; +} + +/* handle extent-on-the-twig-level cases in tree traversal */ +reiser4_internal int +handle_eottl(cbk_handle * h /* cbk handle */ , + int *outcome /* how traversal should proceed */ ) +{ + int result; + reiser4_key key; + coord_t *coord; + + coord = h->coord; + + if (h->level != TWIG_LEVEL || (coord_is_existing_item(coord) && item_is_internal(coord))) { + /* Continue to traverse tree downward. */ + return 0; + } + /* strange item type found on non-stop level?! Twig + horrors? */ + assert("vs-356", h->level == TWIG_LEVEL); + assert("vs-357", ( { + coord_t lcoord; + coord_dup(&lcoord, coord); + check_me("vs-733", coord_set_to_left(&lcoord) == 0); + item_is_extent(&lcoord);} + )); + + if (*outcome == NS_FOUND) { + /* we have found desired key on twig level in extent item */ + h->result = CBK_COORD_FOUND; + reiser4_stat_inc(tree.cbk_found); + *outcome = LOOKUP_DONE; + return 1; + } + + if (!(h->flags & CBK_FOR_INSERT)) { + /* tree traversal is not for insertion. Just return + CBK_COORD_NOTFOUND. */ + h->result = CBK_COORD_NOTFOUND; + *outcome = LOOKUP_DONE; + return 1; + } + + /* take a look at the item to the right of h -> coord */ + result = is_next_item_internal(coord); + if (result == 0) { + /* item to the right is also an extent one. Allocate a new node + and insert pointer to it after item h -> coord. + + This is a result of extents being located at the twig + level. For explanation, see comment just above + is_next_item_internal(). + */ + if (cbk_lock_mode(h->level, h) != ZNODE_WRITE_LOCK) { + /* we got node read locked, restart coord_by_key to + have write lock on twig level */ + h->lock_level = TWIG_LEVEL; + h->lock_mode = ZNODE_WRITE_LOCK; + *outcome = LOOKUP_REST; + return 1; + } + + result = add_empty_leaf(coord, h->active_lh, h->key, rd_key(coord, &key)); + if (result) { + h->error = "could not add empty leaf"; + h->result = result; + *outcome = LOOKUP_DONE; + return 1; + } + /* added empty leaf is locked, its parent node is unlocked, + coord is set as EMPTY */ + *outcome = LOOKUP_DONE; + h->result = CBK_COORD_NOTFOUND; + return 1; + /*assert("vs-358", keyeq(h->key, item_key_by_coord(coord, &key)));*/ + } else { + /* this is special case mentioned in the comment on + tree.h:cbk_flags. We have found internal item immediately + on the right of extent, and we are going to insert new item + there. Key of item we are going to insert is smaller than + leftmost key in the node pointed to by said internal item + (otherwise search wouldn't come to the extent in the first + place). + + This is a result of extents being located at the twig + level. For explanation, see comment just above + is_next_item_internal(). + */ + h->flags &= ~CBK_TRUST_DK; + } + assert("vs-362", item_is_internal(coord)); + return 0; +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/estimate.c 2004-09-03 00:57:05.035273784 -0700 @@ -0,0 +1,107 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#include "debug.h" +#include "dformat.h" +#include "tree.h" +#include "carry.h" +#include "inode.h" +#include "cluster.h" +#include "plugin/item/ctail.h" + +/* this returns how many nodes might get dirty and added nodes if @children nodes are dirtied + + Amount of internals which will get dirty or get allocated we estimate as 5% of the childs + 1 balancing. 1 balancing + is 2 neighbours, 2 new blocks and the current block on the leaf level, 2 neighbour nodes + the current (or 1 + neighbour and 1 new and the current) on twig level, 2 neighbour nodes on upper levels and 1 for a new root. So 5 for + leaf level, 3 for twig level, 2 on upper + 1 for root. + + Do not calculate the current node of the lowest level here - this is overhead only. + + children is almost always 1 here. Exception is flow insertion +*/ +static reiser4_block_nr +max_balance_overhead(reiser4_block_nr childen, tree_level tree_height) +{ + reiser4_block_nr ten_percent; + + ten_percent = ((103 * childen) >> 10); + + /* If we have too many balancings at the time, tree height can raise on more + then 1. Assume that if tree_height is 5, it can raise on 1 only. */ + return ((tree_height < 5 ? 5 : tree_height) * 2 + (4 + ten_percent)); +} + +/* this returns maximal possible number of nodes which can be modified plus number of new nodes which can be required to + perform insertion of one item into the tree */ +/* it is only called when tree height changes, or gets initialized */ +reiser4_internal reiser4_block_nr +calc_estimate_one_insert(tree_level height) +{ + return 1 + max_balance_overhead(1, height); +} + +reiser4_internal reiser4_block_nr +estimate_internal_amount(reiser4_block_nr children, tree_level tree_height) +{ + return max_balance_overhead(children, tree_height); +} + +reiser4_internal reiser4_block_nr +estimate_one_insert_item(reiser4_tree *tree) +{ + return tree->estimate_one_insert; +} + +/* this returns maximal possible number of nodes which can be modified plus number of new nodes which can be required to + perform insertion of one unit into an item in the tree */ +reiser4_internal reiser4_block_nr +estimate_one_insert_into_item(reiser4_tree *tree) +{ + /* estimate insert into item just like item insertion */ + return tree->estimate_one_insert; +} + +reiser4_internal reiser4_block_nr +estimate_one_item_removal(reiser4_tree *tree) +{ + /* on item removal reiser4 does not try to pack nodes more complact, so, only one node may be dirtied on leaf + level */ + return tree->estimate_one_insert; +} + +/* on leaf level insert_flow may add CARRY_FLOW_NEW_NODES_LIMIT new nodes and dirty 3 existing nodes (insert point and + both its neighbors). Max_balance_overhead should estimate number of blocks which may change/get added on internal + levels */ +reiser4_internal reiser4_block_nr +estimate_insert_flow(tree_level height) +{ + return 3 + CARRY_FLOW_NEW_NODES_LIMIT + max_balance_overhead(3 + CARRY_FLOW_NEW_NODES_LIMIT, height); +} + +/* returnes max number of nodes can be occupied by disk cluster */ +reiser4_internal reiser4_block_nr +estimate_disk_cluster(struct inode * inode) +{ + return 2 + inode_cluster_pages(inode); +} + +/* how many nodes might get dirty and added nodes during insertion of a disk cluster */ +reiser4_internal reiser4_block_nr +estimate_insert_cluster(struct inode * inode, int unprepped) +{ + int per_cluster; + per_cluster = (unprepped ? 1 : inode_cluster_pages(inode)); + + return 3 + per_cluster + max_balance_overhead(3 + per_cluster, REISER4_MAX_ZTREE_HEIGHT); +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/file_ops.c 2004-09-03 00:57:05.037273480 -0700 @@ -0,0 +1,458 @@ +/* Copyright 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* + * Interface to VFS. Reiser4 file_operations are defined here. + * + * This file contains definitions of functions that are installed into ->i_fop + * field of reiser4 inodes. + * + * By the most part these functions simply find object plugin of inode + * involved, and call appropriate plugin method to do the actual work. + */ + +#include "forward.h" +#include "debug.h" +#include "dformat.h" +#include "coord.h" +#include "plugin/item/item.h" +#include "plugin/file/file.h" +#include "plugin/security/perm.h" +#include "plugin/disk_format/disk_format.h" +#include "plugin/plugin.h" +#include "plugin/plugin_set.h" +#include "plugin/object.h" +#include "txnmgr.h" +#include "jnode.h" +#include "znode.h" +#include "block_alloc.h" +#include "tree.h" +#include "log.h" +#include "vfs_ops.h" +#include "inode.h" +#include "page_cache.h" +#include "ktxnmgrd.h" +#include "super.h" +#include "reiser4.h" +#include "kattr.h" +#include "entd.h" +#include "emergency_flush.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* file operations */ + +static loff_t reiser4_llseek(struct file *, loff_t, int); +static ssize_t reiser4_read(struct file *, char *, size_t, loff_t *); +static ssize_t reiser4_write(struct file *, const char *, size_t, loff_t *); +static int reiser4_readdir(struct file *, void *, filldir_t); +static int reiser4_ioctl(struct inode *, struct file *, unsigned int cmd, unsigned long arg); +static int reiser4_mmap(struct file *, struct vm_area_struct *); +static int reiser4_release(struct inode *, struct file *); +static int reiser4_fsync(struct file *, struct dentry *, int datasync); +static int reiser4_open(struct inode *, struct file *); +static ssize_t reiser4_sendfile(struct file *, loff_t *, size_t, read_actor_t, void __user *); + +#if 0 +static unsigned int reiser4_poll(struct file *, struct poll_table_struct *); +static int reiser4_flush(struct file *); +static int reiser4_fasync(int, struct file *, int); +static int reiser4_lock(struct file *, int, struct file_lock *); +static ssize_t reiser4_readv(struct file *, const struct iovec *, unsigned long, loff_t *); +static ssize_t reiser4_writev(struct file *, const struct iovec *, unsigned long, loff_t *); +static ssize_t reiser4_sendpage(struct file *, struct page *, int, size_t, loff_t *, int); +static unsigned long reiser4_get_unmapped_area(struct file *, unsigned long, + unsigned long, unsigned long, unsigned long); +#endif + +/* + * ->llseek() file operation for reiser4. Calls ->seek() method of object + * plugin. + */ +static loff_t +reiser4_llseek(struct file *file, loff_t off, int origin) +{ + loff_t result; + file_plugin *fplug; + struct inode *inode = file->f_dentry->d_inode; + loff_t(*seek_fn) (struct file *, loff_t, int); + reiser4_context ctx; + + init_context(&ctx, inode->i_sb); + reiser4_stat_inc(vfs_calls.llseek); + + ON_TRACE(TRACE_VFS_OPS, + "llseek: (i_ino %li, size %lld): off %lli, origin %d\n", inode->i_ino, inode->i_size, off, origin); + + fplug = inode_file_plugin(inode); + assert("nikita-2291", fplug != NULL); + seek_fn = fplug->seek ? : generic_file_llseek; + result = seek_fn(file, off, origin); + reiser4_exit_context(&ctx); + return result; +} + +/* reiser4_readdir() - our readdir() method. + + readdir(2)/getdents(2) interface is based on implicit assumption that + readdir can be restarted from any particular point by supplying file + system with off_t-full of data. That is, file system fill ->d_off + field in struct dirent and later user passes ->d_off to the + seekdir(3), which is, actually, implemented by glibc as lseek(2) on + directory. + + Reiser4 cannot restart readdir from 64 bits of data, because two last + components of the key of directory entry are unknown, which given 128 + bits: locality and type fields in the key of directory entry are + always known, to start readdir() from given point objectid and offset + fields have to be filled. + + See plugin/dir/dir.c:readdir_common() for the details of our solution. +*/ +static int +reiser4_readdir(struct file *f /* directory file being read */ , + void *dirent /* opaque data passed to us by VFS */ , + filldir_t filldir /* filler function passed to us + * by VFS */ ) +{ + dir_plugin *dplug; + int result; + struct inode *inode; + reiser4_context ctx; + + inode = f->f_dentry->d_inode; + init_context(&ctx, inode->i_sb); + write_syscall_log("%s", f->f_dentry->d_name.name); + reiser4_stat_inc(vfs_calls.readdir); + + dplug = inode_dir_plugin(inode); + if ((dplug != NULL) && (dplug->readdir != NULL)) + result = dplug->readdir(f, dirent, filldir); + else + result = RETERR(-ENOTDIR); + + /* + * directory st_atime is updated by callers (if necessary). + */ + write_syscall_log("ex"); + context_set_commit_async(&ctx); + reiser4_exit_context(&ctx); + return result; +} + +/* + reiser4_ioctl - handler for ioctl for inode supported commands: +*/ +static int +reiser4_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + int result; + reiser4_context ctx; + + init_context(&ctx, inode->i_sb); + write_syscall_log("%s", filp->f_dentry->d_name.name); + reiser4_stat_inc(vfs_calls.ioctl); + + if (inode_file_plugin(inode)->ioctl == NULL) + result = -ENOSYS; + else + result = inode_file_plugin(inode)->ioctl(inode, filp, cmd, arg); + + write_syscall_log("ex"); + reiser4_exit_context(&ctx); + return result; +} + +/* ->mmap() VFS method in reiser4 file_operations */ +static int +reiser4_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct inode *inode; + int result; + reiser4_context ctx; + + init_context(&ctx, file->f_dentry->d_inode->i_sb); + write_syscall_log("%s", file->f_dentry->d_name.name); + reiser4_stat_inc(vfs_calls.mmap); + + ON_TRACE(TRACE_VFS_OPS, "MMAP: (i_ino %lli, size %lld)\n", + get_inode_oid(file->f_dentry->d_inode), + file->f_dentry->d_inode->i_size); + + inode = file->f_dentry->d_inode; + assert("nikita-2936", inode_file_plugin(inode)->mmap != NULL); + result = inode_file_plugin(inode)->mmap(file, vma); + write_syscall_log("ex"); + reiser4_exit_context(&ctx); + return result; +} + +/* reiser4 implementation of ->read() VFS method, member of reiser4 struct file_operations + + reads some part of a file from the filesystem into the user space buffer + + gets the plugin for the file and calls its read method which does everything except some initialization + +*/ +static ssize_t +reiser4_read(struct file *file /* file to read from */ , + char *buf /* user-space buffer to put data read + * from the file */ , + size_t count /* bytes to read */ , + loff_t * off /* current position within the file, which needs to be increased by the act of reading. Reads + * start from here. */ ) +{ + ssize_t result; + struct inode *inode; + reiser4_context ctx; + + assert("umka-072", file != NULL); + assert("umka-073", buf != NULL); + assert("umka-074", off != NULL); + + inode = file->f_dentry->d_inode; + init_context(&ctx, inode->i_sb); + write_syscall_log("%s", file->f_dentry->d_name.name); + reiser4_stat_inc(vfs_calls.read); + + ON_TRACE(TRACE_VFS_OPS, + "READ: (i_ino %li, size %lld): %u bytes from pos %lli\n", + inode->i_ino, inode->i_size, count, *off); + + result = perm_chk(inode, read, file, buf, count, off); + if (likely(result == 0)) { + file_plugin *fplug; + + fplug = inode_file_plugin(inode); + assert("nikita-417", fplug != NULL); + assert("nikita-2935", fplug->write != NULL); + + /* unix_file_read is one method that might be invoked below */ + result = fplug->read(file, buf, count, off); + } + write_syscall_log("ex"); + reiser4_exit_context(&ctx); + return result; +} + +/* ->write() VFS method in reiser4 file_operations */ +static ssize_t +reiser4_write(struct file *file /* file to write on */ , + const char *buf /* user-space buffer to get data + * to write into the file */ , + size_t size /* bytes to write */ , + loff_t * off /* offset to start writing + * from. This is updated to indicate + * actual number of bytes written */ ) +{ + struct inode *inode; + ssize_t result; + reiser4_context ctx; + + assert("nikita-1421", file != NULL); + assert("nikita-1422", buf != NULL); + assert("nikita-1424", off != NULL); + + inode = file->f_dentry->d_inode; + init_context(&ctx, inode->i_sb); + write_syscall_log("%s", file->f_dentry->d_name.name); + reiser4_stat_inc(vfs_calls.write); + + ON_TRACE(TRACE_VFS_OPS, + "WRITE: (i_ino %li, size %lld): %u bytes to pos %lli\n", inode->i_ino, inode->i_size, size, *off); + + result = perm_chk(inode, write, file, buf, size, off); + if (likely(result == 0)) { + file_plugin *fplug; + + fplug = inode_file_plugin(inode); + assert("nikita-2934", fplug->read != NULL); + + result = fplug->write(file, buf, size, off); + } + write_syscall_log("ex"); + context_set_commit_async(&ctx); + reiser4_exit_context(&ctx); + return result; +} + +/* Release reiser4 file. This is f_op->release() method. Called when last + holder closes a file */ +static int +reiser4_release(struct inode *i /* inode released */ , + struct file *f /* file released */ ) +{ + file_plugin *fplug; + int result; + reiser4_context ctx; + + assert("umka-081", i != NULL); + assert("nikita-1447", f != NULL); + + init_context(&ctx, i->i_sb); + fplug = inode_file_plugin(i); + assert("umka-082", fplug != NULL); + + ON_TRACE(TRACE_VFS_OPS, + "RELEASE: (i_ino %li, size %lld)\n", i->i_ino, i->i_size); + + if (fplug->release != NULL && get_current_context() == &ctx) + result = fplug->release(i, f); + else + /* + no ->release method defined, or we are within reiser4 + context already. How latter is possible? Simple: + + (gdb) bt + #0 get_exclusive_access () + #2 0xc01e56d3 in release_unix_file () + #3 0xc01c3643 in reiser4_release () + #4 0xc014cae0 in __fput () + #5 0xc013ffc3 in remove_vm_struct () + #6 0xc0141786 in exit_mmap () + #7 0xc0118480 in mmput () + #8 0xc0133205 in oom_kill () + #9 0xc01332d1 in out_of_memory () + #10 0xc013bc1d in try_to_free_pages () + #11 0xc013427b in __alloc_pages () + #12 0xc013f058 in do_anonymous_page () + #13 0xc013f19d in do_no_page () + #14 0xc013f60e in handle_mm_fault () + #15 0xc01131e5 in do_page_fault () + #16 0xc0104935 in error_code () + #17 0xc025c0c6 in __copy_to_user_ll () + #18 0xc01d496f in read_tail () + #19 0xc01e4def in read_unix_file () + #20 0xc01c3504 in reiser4_read () + #21 0xc014bd4f in vfs_read () + #22 0xc014bf66 in sys_read () + */ + result = 0; + + reiser4_free_file_fsdata(f); + + reiser4_exit_context(&ctx); + return result; +} + +/* + * ->open file operation for reiser4. This is optional method. It's only + * present for mounts that support pseudo files. When "nopseudo" mount option + * is used, this method is zeroed, which speeds open(2) system call a bit. + */ +static int +reiser4_open(struct inode * inode, struct file * file) +{ + int result; + + reiser4_context ctx; + file_plugin *fplug; + + init_context(&ctx, inode->i_sb); + reiser4_stat_inc(vfs_calls.open); + fplug = inode_file_plugin(inode); + + if (fplug->open != NULL) + result = fplug->open(inode, file); + else + result = 0; + + reiser4_exit_context(&ctx); + return result; +} + +/* ->fsync file operation for reiser4. */ +static int +reiser4_fsync(struct file *file, struct dentry *dentry, int datasync) +{ + int result; + reiser4_context ctx; + file_plugin *fplug; + struct inode *inode; + + inode = dentry->d_inode; + init_context(&ctx, inode->i_sb); + fplug = inode_file_plugin(inode); + if (fplug->sync != NULL) + result = fplug->sync(inode, datasync); + else + result = 0; + context_set_commit_async(&ctx); + reiser4_exit_context(&ctx); + return result; +} + +/* Reads @count bytes from @file and calls @actor for every read page. This is + needed for loop back devices support. */ +static ssize_t reiser4_sendfile(struct file *file, loff_t *ppos, + size_t count, read_actor_t actor, + void __user *target) +{ + int result; + file_plugin *fplug; + reiser4_context ctx; + struct inode *inode; + + inode = file->f_dentry->d_inode; + init_context(&ctx, inode->i_sb); + + fplug = inode_file_plugin(inode); + + if (fplug->sendfile != NULL) + result = fplug->sendfile(file, ppos, count, actor, target); + else + result = RETERR(-EINVAL); + + reiser4_exit_context(&ctx); + return result; +} + + +struct file_operations reiser4_file_operations = { + .llseek = reiser4_llseek, /* d */ + .read = reiser4_read, /* d */ + .write = reiser4_write, /* d */ + .readdir = reiser4_readdir, /* d */ +/* .poll = reiser4_poll, */ + .ioctl = reiser4_ioctl, + .mmap = reiser4_mmap, /* d */ + .open = reiser4_open, +/* .flush = reiser4_flush, */ + .release = reiser4_release, /* d */ + .fsync = reiser4_fsync /* d */, + .sendfile = reiser4_sendfile, +/* .fasync = reiser4_fasync, */ +/* .lock = reiser4_lock, */ +/* .readv = reiser4_readv, */ +/* .writev = reiser4_writev, */ +/* .sendpage = reiser4_sendpage, */ +/* .get_unmapped_area = reiser4_get_unmapped_area */ +}; + + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/flush.c 2004-09-03 00:57:05.058270288 -0700 @@ -0,0 +1,3832 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* The design document for this file is at http://www.namesys.com/v4/v4.html. */ + +#include "forward.h" +#include "debug.h" +#include "dformat.h" +#include "key.h" +#include "coord.h" +#include "type_safe_list.h" +#include "plugin/item/item.h" +#include "plugin/plugin.h" +#include "plugin/object.h" +#include "txnmgr.h" +#include "jnode.h" +#include "znode.h" +#include "block_alloc.h" +#include "tree_walk.h" +#include "carry.h" +#include "tree.h" +#include "vfs_ops.h" +#include "inode.h" +#include "page_cache.h" +#include "wander.h" +#include "super.h" +#include "log.h" +#include "entd.h" +#include "reiser4.h" +#include "prof.h" +#include "flush.h" +#include "writeout.h" + +#include +#include /* for struct super_block */ +#include /* for struct page */ +#include /* for struct bio */ +#include +#include + +/* IMPLEMENTATION NOTES */ + +/* PARENT-FIRST: Some terminology: A parent-first traversal is a way of assigning a total + order to the nodes of the tree in which the parent is placed before its children, which + are ordered (recursively) in left-to-right order. When we speak of a "parent-first preceder", it + describes the node that "came before in forward parent-first order". When we speak of a + "parent-first follower", it describes the node that "comes next in parent-first + order" (alternatively the node that "came before in reverse parent-first order"). + + The following pseudo-code prints the nodes of a tree in forward parent-first order: + + void parent_first (node) + { + print_node (node); + if (node->level > leaf) { + for (i = 0; i < num_children; i += 1) { + parent_first (node->child[i]); + } + } + } +*/ + +/* JUST WHAT ARE WE TRYING TO OPTIMIZE, HERE? The idea is to optimize block allocation so + that a left-to-right scan of the tree's data (i.e., the leaves in left-to-right order) + can be accomplished with sequential reads, which results in reading nodes in their + parent-first order. This is a read-optimization aspect of the flush algorithm, and + there is also a write-optimization aspect, which is that we wish to make large + sequential writes to the disk by allocating or reallocating blocks so that they can be + written in sequence. Sometimes the read-optimization and write-optimization goals + conflict with each other, as we discuss in more detail below. +*/ + +/* STATE BITS: The flush code revolves around the state of the jnodes it covers. Here are + the relevant jnode->state bits and their relevence to flush: + + JNODE_DIRTY: If a node is dirty, it must be flushed. But in order to be written it + must be allocated first. In order to be considered allocated, the jnode must have + exactly one of { JNODE_OVRWR, JNODE_RELOC } set. These two bits are exclusive, and + all dirtied jnodes eventually have one of these bits set during each transaction. + + JNODE_CREATED: The node was freshly created in its transaction and has no previous + block address, so it is unconditionally assigned to be relocated, although this is + mainly for code-convenience. It is not being 'relocated' from anything, but in + almost every regard it is treated as part of the relocate set. The JNODE_CREATED bit + remains set even after JNODE_RELOC is set, so the actual relocate can be + distinguished from the created-and-allocated set easily: relocate-set members + (belonging to the preserve-set) have (JNODE_RELOC) set and created-set members which + have no previous location to preserve have (JNODE_RELOC | JNODE_CREATED) set. + + JNODE_OVRWR: The node belongs to atom's overwrite set. The flush algorithm made the + decision to maintain the pre-existing location for this node and it will be written + to the wandered-log. + + JNODE_RELOC: The flush algorithm made the decision to relocate this block (if it was + not created, see note above). A block with JNODE_RELOC set is eligible for + early-flushing and may be submitted during flush_empty_queues. When the JNODE_RELOC + bit is set on a znode, the parent node's internal item is modified and the znode is + rehashed. + + JNODE_SQUEEZABLE: Before shifting everything left, the flush algorithm scans the node + and calls plugin->f.squeeze() method for its items. By this technology we update disk + clusters of cryptcompress objects. Also if leftmost point that was found by flush scan + has this flag (races with write(), rare case) the flush algorythm makes the decision + to pass it to squalloc() in spite of its flushprepped status for squeezing, not for + repeated allocation. + + JNODE_FLUSH_QUEUED: This bit is set when a call to flush enters the jnode into its + flush queue. This means the jnode is not on any clean or dirty list, instead it is + moved to one of the flush queue (see flush_queue.h) object private list. This + prevents multiple concurrent flushes from attempting to start flushing from the + same node. + + (DEAD STATE BIT) JNODE_FLUSH_BUSY: This bit was set during the bottom-up + squeeze-and-allocate on a node while its children are actively being squeezed and + allocated. This flag was created to avoid submitting a write request for a node + while its children are still being allocated and squeezed. Then flush queue was + re-implemented to allow unlimited number of nodes be queued. This flag support was + commented out in source code because we decided that there was no reason to submit + queued nodes before jnode_flush() finishes. However, current code calls fq_write() + during a slum traversal and may submit "busy nodes" to disk. Probably we can + re-enable the JNODE_FLUSH_BUSY bit support in future. + + With these state bits, we describe a test used frequently in the code below, + jnode_is_flushprepped() (and the spin-lock-taking jnode_check_flushprepped()). The + test for "flushprepped" returns true if any of the following are true: + + - The node is not dirty + - The node has JNODE_RELOC set + - The node has JNODE_OVRWR set + + If either the node is not dirty or it has already been processed by flush (and assigned + JNODE_OVRWR or JNODE_RELOC), then it is prepped. If jnode_is_flushprepped() returns + true then flush has work to do on that node. +*/ + +/* FLUSH_PREP_ONCE_PER_TRANSACTION: Within a single transaction a node is never + flushprepped twice (unless an explicit call to flush_unprep is made as described in + detail below). For example a node is dirtied, allocated, and then early-flushed to + disk and set clean. Before the transaction commits, the page is dirtied again and, due + to memory pressure, the node is flushed again. The flush algorithm will not relocate + the node to a new disk location, it will simply write it to the same, previously + relocated position again. +*/ + +/* THE BOTTOM-UP VS. TOP-DOWN ISSUE: This code implements a bottom-up algorithm where we + start at a leaf node and allocate in parent-first order by iterating to the right. At + each step of the iteration, we check for the right neighbor. Before advancing to the + right neighbor, we check if the current position and the right neighbor share the same + parent. If they do not share the same parent, the parent is allocated before the right + neighbor. + + This process goes recursively up the tree and squeeze nodes level by level as long as + the right neighbor and the current position have different parents, then it allocates + the right-neighbors-with-different-parents on the way back down. This process is + described in more detail in flush_squalloc_changed_ancestor and the recursive function + squalloc_one_changed_ancestor. But the purpose here is not to discuss the + specifics of the bottom-up approach as it is to contrast the bottom-up and top-down + approaches. + + The top-down algorithm was implemented earlier (April-May 2002). In the top-down + approach, we find a starting point by scanning left along each level past dirty nodes, + then going up and repeating the process until the left node and the parent node are + clean. We then perform a parent-first traversal from the starting point, which makes + allocating in parent-first order trivial. After one subtree has been allocated in this + manner, we move to the right, try moving upward, then repeat the parent-first + traversal. + + Both approaches have problems that need to be addressed. Both are approximately the + same amount of code, but the bottom-up approach has advantages in the order it acquires + locks which, at the very least, make it the better approach. At first glance each one + makes the other one look simpler, so it is important to remember a few of the problems + with each one. + + Main problem with the top-down approach: When you encounter a clean child during the + parent-first traversal, what do you do? You would like to avoid searching through a + large tree of nodes just to find a few dirty leaves at the bottom, and there is not an + obvious solution. One of the advantages of the top-down approach is that during the + parent-first traversal you check every child of a parent to see if it is dirty. In + this way, the top-down approach easily handles the main problem of the bottom-up + approach: unallocated children. + + The unallocated children problem is that before writing a node to disk we must make + sure that all of its children are allocated. Otherwise, the writing the node means + extra I/O because the node will have to be written again when the child is finally + allocated. + + WE HAVE NOT YET ELIMINATED THE UNALLOCATED CHILDREN PROBLEM. Except for bugs, this + should not cause any file system corruption, it only degrades I/O performance because a + node may be written when it is sure to be written at least one more time in the same + transaction when the remaining children are allocated. What follows is a description + of how we will solve the problem. +*/ + +/* HANDLING UNALLOCATED CHILDREN: During flush we may allocate a parent node then, + proceeding in parent first order, allocate some of its left-children, then encounter a + clean child in the middle of the parent. We do not allocate the clean child, but there + may remain unallocated (dirty) children to the right of the clean child. If we were to + stop flushing at this moment and write everything to disk, the parent might still + contain unallocated children. + + We could try to allocate all the descendents of every node that we allocate, but this + is not necessary. Doing so could result in allocating the entire tree: if the root + node is allocated then every unallocated node would have to be allocated before + flushing. Actually, we do not have to write a node just because we allocate it. It is + possible to allocate but not write a node during flush, when it still has unallocated + children. However, this approach is probably not optimal for the following reason. + + The flush algorithm is designed to allocate nodes in parent-first order in an attempt + to optimize reads that occur in the same order. Thus we are read-optimizing for a + left-to-right scan through all the leaves in the system, and we are hoping to + write-optimize at the same time because those nodes will be written together in batch. + What happens, however, if we assign a block number to a node in its read-optimized + order but then avoid writing it because it has unallocated children? In that + situation, we lose out on the write-optimization aspect because a node will have to be + written again to the its location on the device, later, which likely means seeking back + to that location. + + So there are tradeoffs. We can choose either: + + A. Allocate all unallocated children to preserve both write-optimization and + read-optimization, but this is not always desirable because it may mean having to + allocate and flush very many nodes at once. + + B. Defer writing nodes with unallocated children, keep their read-optimized locations, + but sacrifice write-optimization because those nodes will be written again. + + C. Defer writing nodes with unallocated children, but do not keep their read-optimized + locations. Instead, choose to write-optimize them later, when they are written. To + facilitate this, we "undo" the read-optimized allocation that was given to the node so + that later it can be write-optimized, thus "unpreparing" the flush decision. This is a + case where we disturb the FLUSH_PREP_ONCE_PER_TRANSACTION rule described above. By a + call to flush_unprep() we will: if the node was wandered, unset the JNODE_OVRWR bit; + if the node was relocated, unset the JNODE_RELOC bit, non-deferred-deallocate its block + location, and set the JNODE_CREATED bit, effectively setting the node back to an + unallocated state. + + We will take the following approach in v4.0: for twig nodes we will always finish + allocating unallocated children (A). For nodes with (level > TWIG) we will defer + writing and choose write-optimization (C). + + To summarize, there are several parts to a solution that avoids the problem with + unallocated children: + + FIXME-ZAM: Still no one approach is implemented to eliminate the "UNALLOCATED CHILDREN" + problem because there was an experiment which was done showed that we have 1-2 nodes + with unallocated children for thousands of written nodes. The experiment was simple + like coping / deletion of linux kernel sources. However the problem can arise in more + complex tests. I think we have jnode_io_hook to insert a check for unallocated + children and see what kind of problem we have. + + 1. When flush reaches a stopping point (e.g., a clean node), it should continue calling + squeeze-and-allocate on any remaining unallocated children. FIXME: Difficulty to + implement: should be simple -- amounts to adding a while loop to jnode_flush, see + comments in that function. + + 2. When flush reaches flush_empty_queue(), some of the (level > TWIG) nodes may still + have unallocated children. If the twig level has unallocated children it is an + assertion failure. If a higher-level node has unallocated children, then it should be + explicitly de-allocated by a call to flush_unprep(). FIXME: Difficulty to implement: + should be simple. + + 3. (CPU-Optimization) Checking whether a node has unallocated children may consume more + CPU cycles than we would like, and it is possible (but medium complexity) to optimize + this somewhat in the case where large sub-trees are flushed. The following observation + helps: if both the left- and right-neighbor of a node are processed by the flush + algorithm then the node itself is guaranteed to have all of its children allocated. + However, the cost of this check may not be so expensive after all: it is not needed for + leaves and flush can guarantee this property for twigs. That leaves only (level > + TWIG) nodes that have to be checked, so this optimization only helps if at least three + (level > TWIG) nodes are flushed in one pass, and the savings will be very small unless + there are many more (level > TWIG) nodes. But if there are many (level > TWIG) nodes + then the number of blocks being written will be very large, so the savings may be + insignificant. That said, the idea is to maintain both the left and right edges of + nodes that are processed in flush. When flush_empty_queue() is called, a relatively + simple test will tell whether the (level > TWIG) node is on the edge. If it is on the + edge, the slow check is necessary, but if it is in the interior then it can be assumed + to have all of its children allocated. FIXME: medium complexity to implement, but + simple to verify given that we must have a slow check anyway. + + 4. (Optional) This part is optional, not for v4.0--flush should work independently of + whether this option is used or not. Called RAPID_SCAN, the idea is to amend the + left-scan operation to take unallocated children into account. Normally, the left-scan + operation goes left as long as adjacent nodes are dirty up until some large maximum + value (FLUSH_SCAN_MAXNODES) at which point it stops and begins flushing. But scan-left + may stop at a position where there are unallocated children to the left with the same + parent. When RAPID_SCAN is enabled, the ordinary scan-left operation stops after + FLUSH_RELOCATE_THRESHOLD, which is much smaller than FLUSH_SCAN_MAXNODES, then procedes + with a rapid scan. The rapid scan skips all the interior children of a node--if the + leftmost child of a twig is dirty, check its left neighbor (the rightmost child of the + twig to the left). If the left neighbor of the leftmost child is also dirty, then + continue the scan at the left twig and repeat. This option will cause flush to + allocate more twigs in a single pass, but it also has the potential to write many more + nodes than would otherwise be written without the RAPID_SCAN option. RAPID_SCAN + was partially implemented, code removed August 12, 2002 by JMACD. +*/ + +/* FLUSH CALLED ON NON-LEAF LEVEL. Most of our design considerations assume that the + starting point for flush is a leaf node, but actually the flush code cares very little + about whether or not this is true. It is possible that all the leaf nodes are flushed + and dirty parent nodes still remain, in which case jnode_flush() is called on a + non-leaf argument. Flush doesn't care--it treats the argument node as if it were a + leaf, even when it is not. This is a simple approach, and there may be a more optimal + policy but until a problem with this approach is discovered, simplest is probably best. + + NOTE: In this case, the ordering produced by flush is parent-first only if you ignore + the leaves. This is done as a matter of simplicity and there is only one (shaky) + justification. When an atom commits, it flushes all leaf level nodes first, followed + by twigs, and so on. With flushing done in this order, if flush is eventually called + on a non-leaf node it means that (somehow) we reached a point where all leaves are + clean and only internal nodes need to be flushed. If that it the case, then it means + there were no leaves that were the parent-first preceder/follower of the parent. This + is expected to be a rare case, which is why we do nothing special about it. However, + memory pressure may pass an internal node to flush when there are still dirty leaf + nodes that need to be flushed, which could prove our original assumptions + "inoperative". If this needs to be fixed, then scan_left/right should have + special checks for the non-leaf levels. For example, instead of passing from a node to + the left neighbor, it should pass from the node to the left neighbor's rightmost + descendent (if dirty). + +*/ + +/* UNIMPLEMENTED AS YET: REPACKING AND RESIZING. We walk the tree in 4MB-16MB chunks, dirtying everything and putting + it into a transaction. We tell the allocator to allocate the blocks as far as possible towards one end of the + logical device--the left (starting) end of the device if we are walking from left to right, the right end of the + device if we are walking from right to left. We then make passes in alternating directions, and as we do this the + device becomes sorted such that tree order and block number order fully correlate. + + Resizing is done by shifting everything either all the way to the left or all the way + to the right, and then reporting the last block. +*/ + +/* RELOCATE DECISIONS: The code makes a decision to relocate in several places. This + descibes the policy from the highest level: + + The FLUSH_RELOCATE_THRESHOLD parameter: If we count this many consecutive nodes on the + leaf level during flush-scan (right, left), then we unconditionally decide to relocate + leaf nodes. + + Otherwise, there are two contexts in which we make a decision to relocate: + + 1. The REVERSE PARENT-FIRST context: Implemented in reverse_relocate_test(). + During the initial stages of flush, after scan-right completes, we want to ask the + question: should we relocate this leaf node and thus dirty the parent node. Then if + the node is a leftmost child its parent is its own parent-first preceder, thus we repeat + the question at the next level up, and so on. In these cases we are moving in the + reverse-parent first direction. + + There is another case which is considered the reverse direction, which comes at the end + of a twig in reverse_relocate_end_of_twig(). As we finish processing a twig we may + reach a point where there is a clean twig to the right with a dirty leftmost child. In + this case, we may wish to relocate the child by testing if it should be relocated + relative to its parent. + + 2. The FORWARD PARENT-FIRST context: Testing for forward relocation is done in + allocate_znode. What distinguishes the forward parent-first case from the + reverse-parent first case is that the preceder has already been allocated in the + forward case, whereas in the reverse case we don't know what the preceder is until we + finish "going in reverse". That simplifies the forward case considerably, and there we + actually use the block allocator to determine whether, e.g., a block closer to the + preceder is available. +*/ + +/* SQUEEZE_LEFT_EDGE: Unimplemented idea for future consideration. The idea is, once we + finish scan-left and find a starting point, if the parent's left neighbor is dirty then + squeeze the parent's left neighbor and the parent. This may change the + flush-starting-node's parent. Repeat until the child's parent is stable. If the child + is a leftmost child, repeat this left-edge squeezing operation at the next level up. + Note that we cannot allocate extents during this or they will be out of parent-first + order. There is also some difficult coordinate maintenence issues. We can't do a tree + search to find coordinates again (because we hold locks), we have to determine them + from the two nodes being squeezed. Looks difficult, but has potential to increase + space utilization. */ + +/* Flush-scan helper functions. */ +static void scan_init(flush_scan * scan); +static void scan_done(flush_scan * scan); + +/* Flush-scan algorithm. */ +static int scan_left(flush_scan * scan, flush_scan * right, jnode * node, unsigned limit); +static int scan_right(flush_scan * scan, jnode * node, unsigned limit); +static int scan_common(flush_scan * scan, flush_scan * other); +static int scan_formatted(flush_scan * scan); +static int scan_unformatted(flush_scan * scan, flush_scan * other); +static int scan_by_coord(flush_scan * scan); + +/* Initial flush-point ancestor allocation. */ +static int alloc_pos_and_ancestors(flush_pos_t * pos); +static int alloc_one_ancestor(const coord_t * coord, flush_pos_t * pos); +static int set_preceder(const coord_t * coord_in, flush_pos_t * pos); + +/* Main flush algorithm. Note on abbreviation: "squeeze and allocate" == "squalloc". */ +static int squalloc(flush_pos_t * pos); + +/* Flush squeeze implementation. */ +static int squeeze_right_non_twig(znode * left, znode * right); +static int shift_one_internal_unit(znode * left, znode * right); + +/* Flush reverse parent-first relocation routines. */ +static int reverse_relocate_if_close_enough(const reiser4_block_nr * pblk, const reiser4_block_nr * nblk); +static int reverse_relocate_test(jnode * node, const coord_t * parent_coord, flush_pos_t * pos); +static int reverse_relocate_check_dirty_parent(jnode * node, const coord_t * parent_coord, flush_pos_t * pos); + +/* Flush allocate write-queueing functions: */ +static int allocate_znode(znode * node, const coord_t * parent_coord, flush_pos_t * pos); +static int allocate_znode_update(znode * node, const coord_t * parent_coord, flush_pos_t * pos); +static int lock_parent_and_allocate_znode (znode *, flush_pos_t *); + +/* Flush helper functions: */ +static int jnode_lock_parent_coord(jnode * node, + coord_t * coord, + lock_handle * parent_lh, + load_count * parent_zh, + znode_lock_mode mode, int try); +static int neighbor_in_slum(znode * node, lock_handle * right_lock, sideof side, znode_lock_mode mode); +static int znode_same_parents(znode * a, znode * b); + +static int +znode_check_flushprepped(znode * node) +{ + return jnode_check_flushprepped(ZJNODE(node)); +} + +/* Flush position functions */ +static void pos_init(flush_pos_t * pos); +static int pos_valid(flush_pos_t * pos); +static void pos_done(flush_pos_t * pos); +static int pos_stop(flush_pos_t * pos); + +/* check that @org is first jnode extent unit, if extent is unallocated, + * because all jnodes of unallocated extent are dirty and of the same atom. */ +#define checkchild(scan) \ +assert("nikita-3435", \ + ergo(scan->direction == LEFT_SIDE && \ + (scan->parent_coord.node->level == TWIG_LEVEL) && \ + jnode_is_unformatted(scan->node) && \ + extent_is_unallocated(&scan->parent_coord), \ + extent_unit_index(&scan->parent_coord) == index_jnode(scan->node))) + +/* Flush debug functions */ +#if REISER4_DEBUG_OUTPUT +#else +#endif + +const char *pos_tostring(flush_pos_t * pos); + +/* This flush_cnt variable is used to track the number of concurrent flush operations, + useful for debugging. It is initialized in txnmgr.c out of laziness (because flush has + no static initializer function...) */ +ON_DEBUG(atomic_t flush_cnt;) + + +/* FIXME: remove me */#define FLUSH_CHECKS_CONGESTION 1 + +#if defined (FLUSH_CHECKS_CONGESTION) +/* check fs backing device for write congestion */ +static int check_write_congestion (void) +{ + struct super_block *sb; + struct backing_dev_info * bdi; + + sb = reiser4_get_current_sb(); + bdi = get_super_fake(sb)->i_mapping->backing_dev_info; + return bdi_write_congested(bdi); +} +#endif /* FLUSH_CHECKS_CONGESTION */ + +/* conditionally write flush queue */ +static int write_prepped_nodes (flush_pos_t * pos, int check_congestion) +{ + int ret; + + assert("zam-831", pos); + assert("zam-832", pos->fq); + + if (!(pos->flags & JNODE_FLUSH_WRITE_BLOCKS)) + return 0; + +#if defined (FLUSH_CHECKS_CONGESTION) + if (check_congestion && check_write_congestion()) + return 0; +#endif /* FLUSH_CHECKS_CONGESTION */ + + /* trace_mark(flush); */ + write_current_logf(WRITE_IO_LOG, "mark=flush\n"); + + ret = write_fq(pos->fq, pos->nr_written, + WRITEOUT_SINGLE_STREAM | WRITEOUT_FOR_PAGE_RECLAIM); + return ret; +} + +/* Proper release all flush pos. resources then move flush position to new + locked node */ +static void move_flush_pos (flush_pos_t * pos, lock_handle * new_lock, + load_count * new_load, const coord_t * new_coord) +{ + assert ("zam-857", new_lock->node == new_load->node); + + if (new_coord) { + assert ("zam-858", new_coord->node == new_lock->node); + coord_dup(&pos->coord, new_coord); + } else { + coord_init_first_unit(&pos->coord, new_lock->node); + } + + if (pos->child) { + jput(pos->child); + pos->child = NULL; + } + + move_load_count(&pos->load, new_load); + done_lh(&pos->lock); + move_lh(&pos->lock, new_lock); +} + +/* delete empty node which link from the parent still exists. */ +static int delete_empty_node (znode * node) +{ + reiser4_key smallest_removed; + + assert("zam-1019", node != NULL); + assert("zam-1020", node_is_empty(node)); + assert("zam-1023", znode_is_wlocked(node)); + + return delete_node(node, &smallest_removed, NULL); +} + +/* Prepare flush position for alloc_pos_and_ancestors() and squalloc() */ +static int prepare_flush_pos(flush_pos_t *pos, jnode * org) +{ + int ret; + load_count load; + lock_handle lock; + + init_lh(&lock); + init_load_count(&load); + + if (jnode_is_znode(org)) { + ret = longterm_lock_znode(&lock, JZNODE(org), + ZNODE_WRITE_LOCK, ZNODE_LOCK_HIPRI); + if (ret) + return ret; + + ret = incr_load_count_znode(&load, JZNODE(org)); + if (ret) + return ret; + + pos->state = (jnode_get_level(org) == LEAF_LEVEL) ? POS_ON_LEAF : POS_ON_INTERNAL; + move_flush_pos(pos, &lock, &load, NULL); + } else { + coord_t parent_coord; + ret = jnode_lock_parent_coord(org, &parent_coord, &lock, + &load, ZNODE_WRITE_LOCK, 0); + if (ret) + goto done; + + pos->state = POS_ON_EPOINT; + move_flush_pos(pos, &lock, &load, &parent_coord); + pos->child = jref(org); + if (extent_is_unallocated(&parent_coord) && extent_unit_index(&parent_coord) != index_jnode(org)) { + /* @org is not first child of its parent unit. This may happen + because longerm lock of its parent node was released between + scan_left and scan_right. For now work around this having flush to repeat */ + ret = -EAGAIN; + } + } + + done: + done_load_count(&load); + done_lh(&lock); + return ret; +} + +#if REISER4_DEBUG +void check_pos(flush_pos_t *pos) +{ + znode *node; + + node = pos->lock.node; + if (node != NULL && znode_is_any_locked(node)) + assert("nikita-3562", znode_at_read(node)); +} +#endif + +#if REISER4_TRACE + +const char *coord_tween_tostring(between_enum n); + + + +reiser4_internal void +jnode_tostring_internal(jnode * node, char *buf) +{ + const char *state; + char atom[32]; + char block[48]; + char items[32]; + int fmttd; + int dirty; + int lockit; + + lockit = spin_trylock_jnode(node); + + fmttd = jnode_is_znode(node); + dirty = JF_ISSET(node, JNODE_DIRTY); + + sprintf(block, " block=%s page=%p state=%lx", sprint_address(jnode_get_block(node)), node->pg, node->state); + + if (JF_ISSET(node, JNODE_OVRWR)) { + state = dirty ? "wandr,dirty" : "wandr"; + } else if (JF_ISSET(node, JNODE_RELOC) && JF_ISSET(node, JNODE_CREATED)) { + state = dirty ? "creat,dirty" : "creat"; + } else if (JF_ISSET(node, JNODE_RELOC)) { + state = dirty ? "reloc,dirty" : "reloc"; + } else if (JF_ISSET(node, JNODE_CREATED)) { + assert("jmacd-61554", dirty); + state = "fresh"; + block[0] = 0; + } else { + state = dirty ? "dirty" : "clean"; + } + + if (node->atom == NULL) { + atom[0] = 0; + } else { + sprintf(atom, " atom=%u", node->atom->atom_id); + } + + items[0] = 0; + if (!fmttd) { + sprintf(items, " index=%lu", index_jnode(node)); + } + + sprintf(buf + strlen(buf), + "%s=%p [%s%s%s level=%u%s%s]", + fmttd ? "z" : "j", + node, + state, atom, block, jnode_get_level(node), items, JF_ISSET(node, JNODE_FLUSH_QUEUED) ? " fq" : ""); + + if (lockit == 1) { + UNLOCK_JNODE(node); + } +} + +reiser4_internal const char * +jnode_tostring(jnode * node) +{ + static char fmtbuf[256]; + fmtbuf[0] = 0; + jnode_tostring_internal(node, fmtbuf); + return fmtbuf; +} + +static const char * +flags_tostring(int flags) +{ + switch (flags) { + case JNODE_FLUSH_WRITE_BLOCKS: + return "(write blocks)"; + case JNODE_FLUSH_COMMIT: + return "(commit)"; + case JNODE_FLUSH_MEMORY_FORMATTED: + return "(memory-z)"; + case JNODE_FLUSH_MEMORY_UNFORMATTED: + return "(memory-j)"; + default: + return "(unknown)"; + } +} +reiser4_internal const char * +znode_tostring(znode * node) +{ + return jnode_tostring(ZJNODE(node)); +} + + +reiser4_internal const char * +pos_tostring(flush_pos_t * pos) +{ + static char fmtbuf[256]; + load_count load; + fmtbuf[0] = 0; + + init_load_count(&load); + + if (pos->state == POS_ON_EPOINT) { + assert("jmacd-79123", pos->lock.node == pos->load.node); + + strcat(fmtbuf, "par:"); + jnode_tostring_internal(ZJNODE(pos->lock.node), fmtbuf); + + if (incr_load_count_znode(&load, pos->lock.node)) { + return "*error*"; + } + + if (coord_is_before_leftmost(&pos->coord)) { + sprintf(fmtbuf + strlen(fmtbuf), "[left]"); + } else if (coord_is_after_rightmost(&pos->coord)) { + sprintf(fmtbuf + strlen(fmtbuf), "[right]"); + } else { + sprintf(fmtbuf + strlen(fmtbuf), "[%s i=%u/%u", + coord_tween_tostring(pos->coord.between), + pos->coord.item_pos, node_num_items(pos->coord.node)); + + if (!coord_is_existing_item(&pos->coord)) { + sprintf(fmtbuf + strlen(fmtbuf), "]"); + } else { + + sprintf(fmtbuf + strlen(fmtbuf), ",u=%u/%u %s]", + pos->coord.unit_pos, + coord_num_units(&pos->coord), coord_is_existing_unit(&pos->coord) + ? (item_is_extent(&pos->coord) ? + "ext" : (item_is_internal(&pos->coord) ? "int" : "other")) + : "tween"); + } + } + } else if (pos->lock.node != NULL) { + strcat(fmtbuf, "pt:"); + jnode_tostring_internal(ZJNODE(pos->lock.node), fmtbuf); + } + + done_load_count(&load); + return fmtbuf; +} + +#endif /* REISER4_TRACE */ + +/* TODO LIST (no particular order): */ +/* I have labelled most of the legitimate FIXME comments in this file with letters to + indicate which issue they relate to. There are a few miscellaneous FIXMEs with + specific names mentioned instead that need to be inspected/resolved. */ +/* B. There is an issue described in reverse_relocate_test having to do with an + imprecise is_preceder? check having to do with partially-dirty extents. The code that + sets preceder hints and computes the preceder is basically untested. Careful testing + needs to be done that preceder calculations are done correctly, since if it doesn't + affect correctness we will not catch this stuff during regular testing. */ +/* C. EINVAL, E_DEADLOCK, E_NO_NEIGHBOR, ENOENT handling. It is unclear which of these are + considered expected but unlikely conditions. Flush currently returns 0 (i.e., success + but no progress, i.e., restart) whenever it receives any of these in jnode_flush(). + Many of the calls that may produce one of these return values (i.e., + longterm_lock_znode, reiser4_get_parent, reiser4_get_neighbor, ...) check some of these + values themselves and, for instance, stop flushing instead of resulting in a restart. + If any of these results are true error conditions then flush will go into a busy-loop, + as we noticed during testing when a corrupt tree caused find_child_ptr to return + ENOENT. It needs careful thought and testing of corner conditions. +*/ +/* D. Atomicity of flush_prep against deletion and flush concurrency. Suppose a created + block is assigned a block number then early-flushed to disk. It is dirtied again and + flush is called again. Concurrently, that block is deleted, and the de-allocation of + its block number does not need to be deferred, since it is not part of the preserve set + (i.e., it didn't exist before the transaction). I think there may be a race condition + where flush writes the dirty, created block after the non-deferred deallocated block + number is re-allocated, making it possible to write deleted data on top of non-deleted + data. Its just a theory, but it needs to be thought out. */ +/* F. bio_alloc() failure is not handled gracefully. */ +/* G. Unallocated children. */ +/* H. Add a WANDERED_LIST to the atom to clarify the placement of wandered blocks. */ +/* I. Rename flush-scan to scan-point, (flush-pos to flush-point?) */ + +/* JNODE_FLUSH: MAIN ENTRY POINT */ +/* This is the main entry point for flushing a jnode and its dirty neighborhood (dirty + neighborhood is named "slum"). Jnode_flush() is called if reiser4 has to write dirty + blocks to disk, it happens when Linux VM decides to reduce number of dirty pages or as + a part of transaction commit. + + Our objective here is to prep and flush the slum the jnode belongs to. We want to + squish the slum together, and allocate the nodes in it as we squish because allocation + of children affects squishing of parents. + + The "argument" @node tells flush where to start. From there, flush finds the left edge + of the slum, and calls squalloc (in which nodes are squeezed and allocated). To find a + "better place" to start squalloc first we perform a flush_scan. + + Flush-scanning may be performed in both left and right directions, but for different + purposes. When scanning to the left, we are searching for a node that precedes a + sequence of parent-first-ordered nodes which we will then flush in parent-first order. + During flush-scanning, we also take the opportunity to count the number of consecutive + leaf nodes. If this number is past some threshold (FLUSH_RELOCATE_THRESHOLD), then we + make a decision to reallocate leaf nodes (thus favoring write-optimization). + + Since the flush argument node can be anywhere in a sequence of dirty leaves, there may + also be dirty nodes to the right of the argument. If the scan-left operation does not + count at least FLUSH_RELOCATE_THRESHOLD nodes then we follow it with a right-scan + operation to see whether there is, in fact, enough nodes to meet the relocate + threshold. Each right- and left-scan operation uses a single flush_scan object. + + After left-scan and possibly right-scan, we prepare a flush_position object with the + starting flush point or parent coordinate, which was determined using scan-left. + + Next we call the main flush routine, squalloc, which iterates along the + leaf level, squeezing and allocating nodes (and placing them into the flush queue). + + After squalloc returns we take extra steps to ensure that all the children + of the final twig node are allocated--this involves repeating squalloc + until we finish at a twig with no unallocated children. + + Finally, we call flush_empty_queue to submit write-requests to disk. If we encounter + any above-twig nodes during flush_empty_queue that still have unallocated children, we + flush_unprep them. + + Flush treats several "failure" cases as non-failures, essentially causing them to start + over. E_DEADLOCK is one example. FIXME:(C) EINVAL, E_NO_NEIGHBOR, ENOENT: these should + probably be handled properly rather than restarting, but there are a bunch of cases to + audit. +*/ + +static int jnode_flush(jnode * node, long *nr_to_flush, long * nr_written, flush_queue_t * fq, int flags) +{ + long ret = 0; + flush_scan right_scan; + flush_scan left_scan; + flush_pos_t flush_pos; + int todo; + struct super_block *sb; + reiser4_super_info_data *sbinfo; + jnode * leftmost_in_slum = NULL; + + assert("jmacd-76619", lock_stack_isclean(get_current_lock_stack())); + assert("nikita-3022", schedulable()); + + /* lock ordering: delete_sema and flush_sema are unordered */ + assert("nikita-3185", + get_current_super_private()->delete_sema_owner != current); + + sb = reiser4_get_current_sb(); + sbinfo = get_super_private(sb); + if (!reiser4_is_set(sb, REISER4_MTFLUSH)) { +#if REISER4_STATS + unsigned long sleep_start = jiffies; +#endif + down(&sbinfo->flush_sema); +#if REISER4_STATS + reiser4_stat_add(flush.slept_in_mtflush_sem , jiffies - sleep_start); +#endif + } + + /* Flush-concurrency debug code */ +#if REISER4_DEBUG + atomic_inc(&flush_cnt); + ON_TRACE(TRACE_FLUSH, + "flush enter: pid %ul %u concurrent procs\n", + current->pid, atomic_read(&flush_cnt)); + IF_TRACE(TRACE_FLUSH, + if (atomic_read(&flush_cnt) > 1) printk("flush concurrency\n");); +#endif + + enter_flush(sb); + + ON_TRACE(TRACE_FLUSH, "flush squalloc %s %s\n", jnode_tostring(node), flags_tostring(flags)); + + /* Initialize a flush position. */ + pos_init(&flush_pos); + + flush_pos.nr_to_flush = nr_to_flush; + flush_pos.nr_written = nr_written; + flush_pos.fq = fq; + flush_pos.flags = flags; + + scan_init(&right_scan); + scan_init(&left_scan); + + /* init linkage status of the node */ + if (jnode_is_znode(node)) { + /* if jnode is unformatted this status will be set in scan_unformatted */ + set_flush_scan_nstat(&left_scan, LINKED); + set_flush_scan_nstat(&right_scan, LINKED); + } + + /*IF_TRACE (TRACE_FLUSH_VERB, print_tree_rec ("parent_first", current_tree, REISER4_TREE_BRIEF)); */ + /*IF_TRACE (TRACE_FLUSH_VERB, print_tree_rec ("parent_first", current_tree, REISER4_TREE_CHECK)); */ + + /* First scan left and remember the leftmost scan position. If the leftmost + position is unformatted we remember its parent_coord. We scan until counting + FLUSH_SCAN_MAXNODES. + + If starting @node is unformatted, at the beginning of left scan its + parent (twig level node, containing extent item) will be long term + locked and lock handle will be stored in the + @right_scan->parent_lock. This lock is used to start the rightward + scan without redoing the tree traversal (necessary to find parent) + and, hence, is kept during leftward scan. As a result, we have to + use try-lock when taking long term locks during the leftward scan. + */ + ret = scan_left(&left_scan, &right_scan, + node, sbinfo->flush.scan_maxnodes); + if (ret != 0) + goto failed; + + leftmost_in_slum = jref(left_scan.node); + scan_done(&left_scan); + + /* Then possibly go right to decide if we will use a policy of relocating leaves. + This is only done if we did not scan past (and count) enough nodes during the + leftward scan. If we do scan right, we only care to go far enough to establish + that at least FLUSH_RELOCATE_THRESHOLD number of nodes are being flushed. The + scan limit is the difference between left_scan.count and the threshold. */ + reiser4_stat_add(flush.left, left_scan.count); + + todo = sbinfo->flush.relocate_threshold - left_scan.count; + /* scan right is inherently deadlock prone, because we are + * (potentially) holding a lock on the twig node at this moment. + * FIXME: this is incorrect comment: lock is not held */ + if (todo > 0 && (get_flush_scan_nstat(&right_scan) == LINKED)) { + ret = scan_right(&right_scan, node, (unsigned)todo); + if (ret != 0) + goto failed; + } + + /* Only the right-scan count is needed, release any rightward locks right away. */ + scan_done(&right_scan); + + ON_TRACE(TRACE_FLUSH, "flush: left: %i, right: %i\n", + left_scan.count, right_scan.count); + + reiser4_stat_add(flush.right, right_scan.count); + + /* ... and the answer is: we should relocate leaf nodes if at least + FLUSH_RELOCATE_THRESHOLD nodes were found. */ + flush_pos.leaf_relocate = JF_ISSET(node, JNODE_REPACK) || + (left_scan.count + right_scan.count >= sbinfo->flush.relocate_threshold); + + /*assert ("jmacd-6218", jnode_check_dirty (left_scan.node)); */ + + /* Funny business here. We set the 'point' in the flush_position at prior to + starting squalloc regardless of whether the first point is + formatted or unformatted. Without this there would be an invariant, in the + rest of the code, that if the flush_position is unformatted then + flush_position->point is NULL and flush_position->parent_{lock,coord} is set, + and if the flush_position is formatted then flush_position->point is non-NULL + and no parent info is set. + + This seems lazy, but it makes the initial calls to reverse_relocate_test + (which ask "is it the pos->point the leftmost child of its parent") much easier + because we know the first child already. Nothing is broken by this, but the + reasoning is subtle. Holding an extra reference on a jnode during flush can + cause us to see nodes with HEARD_BANSHEE during squalloc, because nodes are not + removed from sibling lists until they have zero reference count. Flush would + never observe a HEARD_BANSHEE node on the left-edge of flush, nodes are only + deleted to the right. So if nothing is broken, why fix it? + + NOTE-NIKITA actually, flush can meet HEARD_BANSHEE node at any + point and in any moment, because of the concurrent file system + activity (for example, truncate). */ + + /* Check jnode state after flush_scan completed. Having a lock on this + node or its parent (in case of unformatted) helps us in case of + concurrent flushing. */ + if (jnode_check_flushprepped(leftmost_in_slum) && !jnode_squeezable(leftmost_in_slum)) { + ON_TRACE(TRACE_FLUSH_VERB, "flush concurrency: %s already allocated\n", pos_tostring(&flush_pos)); + ret = 0; + goto failed; + } + + /* Now setup flush_pos using scan_left's endpoint. */ + ret = prepare_flush_pos(&flush_pos, leftmost_in_slum); + if (ret) + goto failed; + + if (znode_get_level(flush_pos.coord.node) == LEAF_LEVEL + && node_is_empty(flush_pos.coord.node)) { + znode * empty = flush_pos.coord.node; + + assert ("zam-1022", !ZF_ISSET(empty, JNODE_HEARD_BANSHEE)); + ret = delete_empty_node(empty); + goto failed; + } + + if (jnode_check_flushprepped(leftmost_in_slum) && !jnode_squeezable(leftmost_in_slum)) { + ON_TRACE(TRACE_FLUSH_VERB, "flush concurrency: %s already allocated\n", pos_tostring(&flush_pos)); + ret = 0; + goto failed; + } + + /* Set pos->preceder and (re)allocate pos and its ancestors if it is needed */ + ret = alloc_pos_and_ancestors(&flush_pos); + if (ret) + goto failed; + + /* Do the main rightward-bottom-up squeeze and allocate loop. */ + check_pos(&flush_pos); + ret = squalloc(&flush_pos); + check_pos(&flush_pos); + pos_stop(&flush_pos); + if (ret) + goto failed; + + /* FIXME_NFQUCMPD: Here, handle the twig-special case for unallocated children. + First, the pos_stop() and pos_valid() routines should be modified + so that pos_stop() sets a flush_position->stop flag to 1 without + releasing the current position immediately--instead release it in + pos_done(). This is a better implementation than the current one anyway. + + It is not clear that all fields of the flush_position should not be released, + but at the very least the parent_lock, parent_coord, and parent_load should + remain held because they are hold the last twig when pos_stop() is + called. + + When we reach this point in the code, if the parent_coord is set to after the + last item then we know that flush reached the end of a twig (and according to + the new flush queueing design, we will return now). If parent_coord is not + past the last item, we should check if the current twig has any unallocated + children to the right (we are not concerned with unallocated children to the + left--in that case the twig itself should not have been allocated). If the + twig has unallocated children to the right, set the parent_coord to that + position and then repeat the call to squalloc. + + Testing for unallocated children may be defined in two ways: if any internal + item has a fake block number, it is unallocated; if any extent item is + unallocated then all of its children are unallocated. But there is a more + aggressive approach: if there are any dirty children of the twig to the right + of the current position, we may wish to relocate those nodes now. Checking for + potential relocation is more expensive as it requires knowing whether there are + any dirty children that are not unallocated. The extent_needs_allocation + should be used after setting the correct preceder. + + When we reach the end of a twig at this point in the code, if the flush can + continue (when the queue is ready) it will need some information on the future + starting point. That should be stored away in the flush_handle using a seal, I + believe. Holding a jref() on the future starting point may break other code + that deletes that node. + */ + + /* FIXME_NFQUCMPD: Also, we don't want to do any flushing when flush is called + above the twig level. If the VM calls flush above the twig level, do nothing + and return (but figure out why this happens). The txnmgr should be modified to + only flush its leaf-level dirty list. This will do all the necessary squeeze + and allocate steps but leave unallocated branches and possibly unallocated + twigs (when the twig's leftmost child is not dirty). After flushing the leaf + level, the remaining unallocated nodes should be given write-optimized + locations. (Possibly, the remaining unallocated twigs should be allocated just + before their leftmost child.) + */ + + /* Any failure reaches this point. */ +failed: + + if (nr_to_flush != NULL) { + if (ret >= 0) { + ON_TRACE(TRACE_FLUSH, "flush_jnode wrote %u blocks\n", flush_pos.prep_or_free_cnt); + (*nr_to_flush) = flush_pos.prep_or_free_cnt; + } else { + (*nr_to_flush) = 0; + } + } + + switch (ret) { + case -E_REPEAT: + case -EINVAL: + case -E_DEADLOCK: + case -E_NO_NEIGHBOR: + case -ENOENT: + /* FIXME(C): Except for E_DEADLOCK, these should probably be handled properly + in each case. They already are handled in many cases. */ + /* Something bad happened, but difficult to avoid... Try again! */ + ON_TRACE(TRACE_FLUSH, "flush restartable failure: %ld\n", ret); + ret = 0; + } + + if (leftmost_in_slum) + jput(leftmost_in_slum); + + pos_done(&flush_pos); + scan_done(&left_scan); + scan_done(&right_scan); + + ON_DEBUG(atomic_dec(&flush_cnt)); + + write_syscall_log("ex"); + + leave_flush(sb); + + if (!reiser4_is_set(sb, REISER4_MTFLUSH)) + up(&sbinfo->flush_sema); + + return ret; +} + +/* The reiser4 flush subsystem can be turned into "rapid flush mode" means that + * flusher should submit all prepped nodes immediately without keeping them in + * flush queues for long time. The reason for rapid flush mode is to free + * memory as fast as possible. */ + +#if REISER4_USE_RAPID_FLUSH + +/** + * submit all prepped nodes if rapid flush mode is set, + * turn rapid flush mode off. + */ + +static int rapid_flush (flush_pos_t * pos) +{ + if (!wbq_available()) + return 0; + + return write_prepped_nodes(pos, 1); +} + +#else + +#define rapid_flush(pos) (0) + +#endif /* REISER4_USE_RAPID_FLUSH */ + +/* Flush some nodes of current atom, usually slum, return -E_REPEAT if there are more nodes + * to flush, return 0 if atom's dirty lists empty and keep current atom locked, return + * other errors as they are. */ +reiser4_internal int +flush_current_atom (int flags, long *nr_submitted, txn_atom ** atom) +{ + reiser4_super_info_data * sinfo = get_current_super_private(); + flush_queue_t *fq = NULL; + jnode * node; + int nr_queued; + int ret; + + assert ("zam-889", atom != NULL && *atom != NULL); + assert ("zam-890", spin_atom_is_locked(*atom)); + assert ("zam-892", get_current_context()->trans->atom == *atom); + + while(1) { + ret = fq_by_atom(*atom, &fq); + if (ret != -E_REPEAT) + break; + *atom = get_current_atom_locked(); + } + if (ret) + return ret; + + assert ("zam-891", spin_atom_is_locked(*atom)); + + /* parallel flushers limit */ + if (sinfo->tmgr.atom_max_flushers != 0) { + while ((*atom)->nr_flushers >= sinfo->tmgr.atom_max_flushers) { + /* An atom_send_event() call is inside fq_put_nolock() which is + called when flush is finished and nr_flushers is + decremented. */ + atom_wait_event(*atom); + *atom = get_current_atom_locked(); + } + } + + /* count ourself as a flusher */ + (*atom)->nr_flushers++; + + if (REISER4_LOG) { + UNLOCK_ATOM(*atom); + write_syscall_log("in"); + *atom = get_current_atom_locked(); + } + reiser4_stat_inc(flush.flush); + writeout_mode_enable(); + + nr_queued = 0; + + /* In this loop we process all already prepped (RELOC or OVRWR) and dirtied again + * nodes. The atom spin lock is not released until all dirty nodes processed or + * not prepped node found in the atom dirty lists. */ + while ((node = find_first_dirty_jnode(*atom, flags))) { + LOCK_JNODE(node); + + assert ("zam-881", jnode_is_dirty(node)); + assert ("zam-898", !JF_ISSET(node, JNODE_OVRWR)); + + if (JF_ISSET(node, JNODE_WRITEBACK)) { + capture_list_remove_clean(node); + capture_list_push_back(ATOM_WB_LIST(*atom), node); + /*XXXX*/ON_DEBUG(count_jnode(*atom, node, DIRTY_LIST, WB_LIST, 1)); + + } else if (jnode_is_znode(node) && znode_above_root(JZNODE(node))) { + /* A special case for znode-above-root. The above-root (fake) + znode is captured and dirtied when the tree height changes or + when the root node is relocated. This causes atoms to fuse so + that changes at the root are serialized. However, this node is + never flushed. This special case used to be in lock.c to + prevent the above-root node from ever being captured, but now + that it is captured we simply prevent it from flushing. The + log-writer code relies on this to properly log superblock + modifications of the tree height. */ + jnode_make_wander_nolock(node); + } else if (JF_ISSET(node, JNODE_RELOC)) { + queue_jnode(fq, node); + ++ nr_queued; + } else + break; + + UNLOCK_JNODE(node); + } + + if (node == NULL) { + if (nr_queued == 0) { + writeout_mode_disable(); + (*atom)->nr_flushers --; + atom_send_event(*atom); + fq_put_nolock(fq); + /* current atom remains locked */ + return 0; + } + UNLOCK_ATOM(*atom); + } else { + jref(node); + UNLOCK_ATOM(*atom); + UNLOCK_JNODE(node); + ret = jnode_flush(node, NULL, nr_submitted, fq, flags); + jput(node); + } + + /* trace_mark(flush); */ + write_current_logf(WRITE_IO_LOG, "mark=flush\n"); + + ret = write_fq(fq, nr_submitted, WRITEOUT_SINGLE_STREAM | WRITEOUT_FOR_PAGE_RECLAIM); + + *atom = get_current_atom_locked(); + (*atom)->nr_flushers --; + fq_put_nolock(fq); + atom_send_event(*atom); + UNLOCK_ATOM(*atom); + + writeout_mode_disable(); + write_syscall_log("ex"); + + if (ret == 0) + ret = -E_REPEAT; + + return ret; +} + +/* REVERSE PARENT-FIRST RELOCATION POLICIES */ + +/* This implements the is-it-close-enough-to-its-preceder? test for relocation in the + reverse parent-first relocate context. Here all we know is the preceder and the block + number. Since we are going in reverse, the preceder may still be relocated as well, so + we can't ask the block allocator "is there a closer block available to relocate?" here. + In the _forward_ parent-first relocate context (not here) we actually call the block + allocator to try and find a closer location. */ +static int +reverse_relocate_if_close_enough(const reiser4_block_nr * pblk, const reiser4_block_nr * nblk) +{ + reiser4_block_nr dist; + + assert("jmacd-7710", *pblk != 0 && *nblk != 0); + assert("jmacd-7711", !blocknr_is_fake(pblk)); + assert("jmacd-7712", !blocknr_is_fake(nblk)); + + /* Distance is the absolute value. */ + dist = (*pblk > *nblk) ? (*pblk - *nblk) : (*nblk - *pblk); + + /* If the block is less than FLUSH_RELOCATE_DISTANCE blocks away from its preceder + block, do not relocate. */ + if (dist <= get_current_super_private()->flush.relocate_distance) { + return 0; + } + + return 1; +} + +/* This function is a predicate that tests for relocation. Always called in the + reverse-parent-first context, when we are asking whether the current node should be + relocated in order to expand the flush by dirtying the parent level (and thus + proceeding to flush that level). When traversing in the forward parent-first direction + (not here), relocation decisions are handled in two places: allocate_znode() and + extent_needs_allocation(). */ +static int +reverse_relocate_test(jnode * node, const coord_t * parent_coord, flush_pos_t * pos) +{ + reiser4_block_nr pblk = 0; + reiser4_block_nr nblk = 0; + + assert("jmacd-8989", !jnode_is_root(node)); + + /* + * This function is called only from the + * reverse_relocate_check_dirty_parent() and only if the parent + * node is clean. This implies that the parent has the real (i.e., not + * fake) block number, and, so does the child, because otherwise the + * parent would be dirty. + */ + + /* New nodes are treated as if they are being relocated. */ + if (jnode_created(node) + || (pos->leaf_relocate && jnode_get_level(node) == LEAF_LEVEL)) { + return 1; + } + + /* Find the preceder. FIXME(B): When the child is an unformatted, previously + existing node, the coord may be leftmost even though the child is not the + parent-first preceder of the parent. If the first dirty node appears somewhere + in the middle of the first extent unit, this preceder calculation is wrong. + Needs more logic in here. */ + if (coord_is_leftmost_unit(parent_coord)) { + pblk = *znode_get_block(parent_coord->node); + } else { + pblk = pos->preceder.blk; + } + check_preceder(pblk); + + /* If (pblk == 0) then the preceder isn't allocated or isn't known: relocate. */ + if (pblk == 0) { + return 1; + } + + nblk = *jnode_get_block(node); + + if (blocknr_is_fake(&nblk)) + /* child is unallocated, mark parent dirty */ + return 1; + + return reverse_relocate_if_close_enough(&pblk, &nblk); +} + +/* This function calls reverse_relocate_test to make a reverse-parent-first + relocation decision and then, if yes, it marks the parent dirty. */ +static int +reverse_relocate_check_dirty_parent(jnode * node, const coord_t * parent_coord, flush_pos_t * pos) +{ + int ret; + + if (!znode_check_dirty(parent_coord->node)) { + + ret = reverse_relocate_test(node, parent_coord, pos); + if (ret < 0) { + return ret; + } + + /* FIXME-ZAM + if parent is already relocated - we do not want to grab space, right? */ + if (ret == 1) { + int grabbed; + + grabbed = get_current_context()->grabbed_blocks; + if (reiser4_grab_space_force((__u64)1, BA_RESERVED) != 0) + reiser4_panic("umka-1250", + "No space left during flush."); + + assert("jmacd-18923", znode_is_write_locked(parent_coord->node)); + znode_make_dirty(parent_coord->node); + grabbed2free_mark(grabbed); + } + } + + return 0; +} + +/* INITIAL ALLOCATE ANCESTORS STEP (REVERSE PARENT-FIRST ALLOCATION BEFORE FORWARD + PARENT-FIRST LOOP BEGINS) */ + +/* Get the leftmost child for given coord. */ +static int get_leftmost_child_of_unit (const coord_t * coord, jnode ** child) +{ + int ret; + + ret = item_utmost_child(coord, LEFT_SIDE, child); + + if (ret) + return ret; + + if (IS_ERR(*child)) + return PTR_ERR(*child); + + return 0; +} + +/* This step occurs after the left- and right-scans are completed, before starting the + forward parent-first traversal. Here we attempt to allocate ancestors of the starting + flush point, which means continuing in the reverse parent-first direction to the + parent, grandparent, and so on (as long as the child is a leftmost child). This + routine calls a recursive process, alloc_one_ancestor, which does the real work, + except there is special-case handling here for the first ancestor, which may be a twig. + At each level (here and alloc_one_ancestor), we check for relocation and then, if + the child is a leftmost child, repeat at the next level. On the way back down (the + recursion), we allocate the ancestors in parent-first order. */ +static int alloc_pos_and_ancestors(flush_pos_t * pos) +{ + int ret = 0; + lock_handle plock; + load_count pload; + coord_t pcoord; + + if (znode_check_flushprepped(pos->lock.node)) + return 0; + + ON_TRACE(TRACE_FLUSH_VERB, "flush alloc ancestors: %s\n", pos_tostring(pos)); + + coord_init_invalid(&pcoord, NULL); + init_lh(&plock); + init_load_count(&pload); + + if (pos->state == POS_ON_EPOINT) { + /* a special case for pos on twig level, where we already have + a lock on parent node. */ + /* The parent may not be dirty, in which case we should decide + whether to relocate the child now. If decision is made to + relocate the child, the parent is marked dirty. */ + ret = reverse_relocate_check_dirty_parent(pos->child, &pos->coord, pos); + if (ret) + goto exit; + + /* FIXME_NFQUCMPD: We only need to allocate the twig (if child + is leftmost) and the leaf/child, so recursion is not needed. + Levels above the twig will be allocated for + write-optimization before the transaction commits. */ + + /* Do the recursive step, allocating zero or more of our + * ancestors. */ + ret = alloc_one_ancestor(&pos->coord, pos); + + } else { + if (!znode_is_root(pos->lock.node)) { + /* all formatted nodes except tree root */ + ret = reiser4_get_parent(&plock, pos->lock.node, ZNODE_WRITE_LOCK, 0); + if (ret) + goto exit; + + ret = incr_load_count_znode(&pload, plock.node); + if (ret) + goto exit; + + ret = find_child_ptr(plock.node, pos->lock.node, &pcoord); + if (ret) + goto exit; + + ret = reverse_relocate_check_dirty_parent(ZJNODE(pos->lock.node), &pcoord, pos); + if (ret) + goto exit; + + ret = alloc_one_ancestor(&pcoord, pos); + if (ret) + goto exit; + } + + ret = allocate_znode(pos->lock.node, &pcoord, pos); + } +exit: + done_load_count(&pload); + done_lh(&plock); + return ret; +} + +/* This is the recursive step described in alloc_pos_and_ancestors, above. Ignoring the + call to set_preceder, which is the next function described, this checks if the + child is a leftmost child and returns if it is not. If the child is a leftmost child + it checks for relocation, possibly dirtying the parent. Then it performs the recursive + step. */ +static int alloc_one_ancestor(const coord_t * coord, flush_pos_t * pos) +{ + int ret = 0; + lock_handle alock; + load_count aload; + coord_t acoord; + + /* As we ascend at the left-edge of the region to flush, take this opportunity at + the twig level to find our parent-first preceder unless we have already set + it. */ + if (pos->preceder.blk == 0) { + ret = set_preceder(coord, pos); + if (ret != 0) + return ret; + } + + /* If the ancestor is clean or already allocated, or if the child is not a + leftmost child, stop going up, even leaving coord->node not flushprepped. */ + if (znode_check_flushprepped(coord->node)|| !coord_is_leftmost_unit(coord)) + return 0; + + init_lh(&alock); + init_load_count(&aload); + coord_init_invalid(&acoord, NULL); + + /* Only ascend to the next level if it is a leftmost child, but write-lock the + parent in case we will relocate the child. */ + if (!znode_is_root(coord->node)) { + + ret = jnode_lock_parent_coord( + ZJNODE(coord->node), &acoord, &alock, &aload, ZNODE_WRITE_LOCK, 0); + if (ret != 0) { + /* FIXME(C): check EINVAL, E_DEADLOCK */ + goto exit; + } + + ret = reverse_relocate_check_dirty_parent(ZJNODE(coord->node), &acoord, pos); + if (ret != 0) { + goto exit; + } + + /* Recursive call. */ + if (!znode_check_flushprepped(acoord.node)) { + ret = alloc_one_ancestor(&acoord, pos); + if (ret) + goto exit; + } + } + + /* Note: we call allocate with the parent write-locked (except at the root) in + case we relocate the child, in which case it will modify the parent during this + call. */ + ret = allocate_znode(coord->node, &acoord, pos); + +exit: + done_load_count(&aload); + done_lh(&alock); + return ret; +} + +/* During the reverse parent-first alloc_pos_and_ancestors process described above there is + a call to this function at the twig level. During alloc_pos_and_ancestors we may ask: + should this node be relocated (in reverse parent-first context)? We repeat this + process as long as the child is the leftmost child, eventually reaching an ancestor of + the flush point that is not a leftmost child. The preceder of that ancestors, which is + not a leftmost child, is actually on the leaf level. The preceder of that block is the + left-neighbor of the flush point. The preceder of that block is the rightmost child of + the twig on the left. So, when alloc_pos_and_ancestors passes upward through the twig + level, it stops momentarily to remember the block of the rightmost child of the twig on + the left and sets it to the flush_position's preceder_hint. + + There is one other place where we may set the flush_position's preceder hint, which is + during scan-left. +*/ +static int +set_preceder(const coord_t * coord_in, flush_pos_t * pos) +{ + int ret; + coord_t coord; + lock_handle left_lock; + load_count left_load; + +#if 0 + /* do not trust to allocation of nodes above twigs, use the block number of last + * write (write optimized approach). */ + if (znode_get_level(coord_in->node) > TWIG_LEVEL + 1) { + get_blocknr_hint_default(&pos->preceder.blk); + reiser4_stat_inc(block_alloc.nohint); + return 0; + } +#endif + + coord_dup(&coord, coord_in); + + init_lh(&left_lock); + init_load_count(&left_load); + + /* FIXME(B): Same FIXME as in "Find the preceder" in reverse_relocate_test. + coord_is_leftmost_unit is not the right test if the unformatted child is in the + middle of the first extent unit. */ + if (!coord_is_leftmost_unit(&coord)) { + coord_prev_unit(&coord); + } else { + ret = reiser4_get_left_neighbor(&left_lock, coord.node, ZNODE_READ_LOCK, GN_SAME_ATOM); + if (ret) { + /* If we fail for any reason it doesn't matter because the + preceder is only a hint. We are low-priority at this point, so + this must be the case. */ + if (ret == -E_REPEAT || ret == -E_NO_NEIGHBOR || + ret == -ENOENT || ret == -EINVAL || ret == -E_DEADLOCK) + { + ret = 0; + } + goto exit; + } + + ret = incr_load_count_znode(&left_load, left_lock.node); + if (ret) + goto exit; + + coord_init_last_unit(&coord, left_lock.node); + } + + ret = item_utmost_child_real_block(&coord, RIGHT_SIDE, &pos->preceder.blk); +exit: + check_preceder(pos->preceder.blk); + done_load_count(&left_load); + done_lh(&left_lock); + return ret; +} + +/* MAIN SQUEEZE AND ALLOCATE LOOP (THREE BIG FUNCTIONS) */ + +/* This procedure implements the outer loop of the flush algorithm. To put this in + context, here is the general list of steps taken by the flush routine as a whole: + + 1. Scan-left + 2. Scan-right (maybe) + 3. Allocate initial flush position and its ancestors + 4. + 5. + 6. + + This procedure implements the loop in steps 4 through 6 in the above listing. + + Step 4: if the current flush position is an extent item (position on the twig level), + it allocates the extent (allocate_extent_item_in_place) then shifts to the next + coordinate. If the next coordinate's leftmost child needs flushprep, we will continue. + If the next coordinate is an internal item, we descend back to the leaf level, + otherwise we repeat a step #4 (labeled ALLOC_EXTENTS below). If the "next coordinate" + brings us past the end of the twig level, then we call + reverse_relocate_end_of_twig to possibly dirty the next (right) twig, prior to + step #5 which moves to the right. + + Step 5: calls squalloc_changed_ancestors, which initiates a recursive call up the + tree to allocate any ancestors of the next-right flush position that are not also + ancestors of the current position. Those ancestors (in top-down order) are the next in + parent-first order. We squeeze adjacent nodes on the way up until the right node and + current node share the same parent, then allocate on the way back down. Finally, this + step sets the flush position to the next-right node. Then repeat steps 4 and 5. +*/ + +/* SQUEEZE CODE */ + + +/* squalloc_right_twig helper function, cut a range of extent items from + cut node to->node from the beginning up to coord @to. */ +static int squalloc_right_twig_cut(coord_t * to, reiser4_key * to_key, znode * left) +{ + coord_t from; + reiser4_key from_key; + + coord_init_first_unit(&from, to->node); + item_key_by_coord(&from, &from_key); + + return cut_node_content(&from, to, &from_key, to_key, NULL); +} + +/* Copy as much of the leading extents from @right to @left, allocating + unallocated extents as they are copied. Returns SQUEEZE_TARGET_FULL or + SQUEEZE_SOURCE_EMPTY when no more can be shifted. If the next item is an + internal item it calls shift_one_internal_unit and may then return + SUBTREE_MOVED. */ +squeeze_result squalloc_extent(znode *left, const coord_t *, flush_pos_t *, reiser4_key *stop_key); +#if REISER4_DEBUG +void *shift_check_prepare(const znode *left, const znode *right); +void shift_check(void *vp, const znode *left, const znode *right); +#endif +static int squeeze_right_twig(znode * left, znode * right, flush_pos_t * pos) +{ + int ret = SUBTREE_MOVED; + coord_t coord; /* used to iterate over items */ + reiser4_key stop_key; + + assert("jmacd-2008", !node_is_empty(right)); + coord_init_first_unit(&coord, right); + + DISABLE_NODE_CHECK; + + ON_TRACE(TRACE_FLUSH_VERB, "sq_twig before copy extents: left %s\n", znode_tostring(left)); + ON_TRACE(TRACE_FLUSH_VERB, "sq_twig before copy extents: right %s\n", znode_tostring(right)); + + /* FIXME: can be optimized to cut once */ + while (!node_is_empty(coord.node) && item_is_extent(&coord)) { + ON_DEBUG(void *vp); + + assert("vs-1468", coord_is_leftmost_unit(&coord)); + ON_DEBUG(vp = shift_check_prepare(left, coord.node)); + + /* stop_key is used to find what was copied and what to cut */ + stop_key = *min_key(); + ret = squalloc_extent(left, &coord, pos, &stop_key); + if (ret != SQUEEZE_CONTINUE) { + ON_DEBUG(reiser4_kfree(vp)); + break; + } + assert("vs-1465", !keyeq(&stop_key, min_key())); + + /* Helper function to do the cutting. */ + set_key_offset(&stop_key, get_key_offset(&stop_key) - 1); + check_me("vs-1466", squalloc_right_twig_cut(&coord, &stop_key, left) == 0); + + ON_DEBUG(shift_check(vp, left, coord.node)); + } + + if (node_is_empty(coord.node)) + ret = SQUEEZE_SOURCE_EMPTY; + + ENABLE_NODE_CHECK; + node_check(left, REISER4_NODE_DKEYS); + node_check(right, REISER4_NODE_DKEYS); + + if (ret == SQUEEZE_TARGET_FULL) { + goto out; + } + + if (node_is_empty(right)) { + /* The whole right node was copied into @left. */ + ON_TRACE(TRACE_FLUSH_VERB, "sq_twig right node empty: %s\n", znode_tostring(right)); + assert("vs-464", ret == SQUEEZE_SOURCE_EMPTY); + goto out; + } + + coord_init_first_unit(&coord, right); + + if (!item_is_internal(&coord)) { + /* we do not want to squeeze anything else to left neighbor because "slum" + is over */ + ret = SQUEEZE_TARGET_FULL; + goto out; + } + assert("jmacd-433", item_is_internal(&coord)); + + /* Shift an internal unit. The child must be allocated before shifting any more + extents, so we stop here. */ + ret = shift_one_internal_unit(left, right); + +out: + assert("jmacd-8612", ret < 0 || ret == SQUEEZE_TARGET_FULL + || ret == SUBTREE_MOVED || ret == SQUEEZE_SOURCE_EMPTY); + + if (ret == SQUEEZE_TARGET_FULL) { + /* We submit prepped nodes here and expect that this @left twig + * will not be modified again during this jnode_flush() call. */ + int ret1; + + /* NOTE: seems like io is done under long term locks. */ + ret1 = write_prepped_nodes(pos, 1); + if (ret1 < 0) + return ret1; + } + + return ret; +} + +/* This is special node method which scans node items and check for each + one, if we need to apply flush squeeze item method. This item method + may resize/kill the item, and also may change the tree. +*/ +static int squeeze_node(flush_pos_t * pos, znode * node) +{ + int ret = 0; + + item_plugin * iplug; + + assert("edward-304", pos != NULL); + assert("edward-305", pos->child == NULL); + assert("edward-475", znode_squeezable(node)); + assert("edward-669", znode_is_wlocked(node)); + + if (znode_get_level(node) != LEAF_LEVEL) + /* do not squeeze this node */ + goto exit; + + coord_init_first_unit(&pos->coord, node); + + while (1) { + ret = 0; + + if (node_is_empty(node)) + /* nothing to squeeze */ + goto exit; + if (pos->sq && item_squeeze_data(pos)) { + iplug = item_squeeze_plug(pos); + assert("edward-476", iplug->f.squeeze != NULL); + } + else if (!coord_is_existing_item(&pos->coord)) + /* finished this node */ + break; + else { + iplug = item_plugin_by_coord(&pos->coord); + if (pos->sq && item_squeeze_plug(pos) != iplug) + set_item_squeeze_count(pos, 0); + } + assert("edward-844", iplug != NULL); + if (iplug->f.squeeze == NULL) + /* unsqueezable */ + goto next; + + ret = iplug->f.squeeze(pos); + + if (ret == -E_REPEAT) + continue; + if (ret) + goto exit; + + assert("edward-307", pos->child == NULL); + + /* now we should check if item_squeeze_data is valid, and if so, + call previous method again, BUT if current item is last + and mergeable with the first item of slum right neighbor, + we set idata->mergeable = 1, go to slum right neighbor + and continue squeezing using this info + */ + next: + if (coord_next_item(&pos->coord)) { + /* node is over */ + lock_handle right_lock; + load_count right_load; + coord_t coord; + + if (!pos->sq || !item_squeeze_data(pos)) + break; + + init_lh(&right_lock); + init_load_count(&right_load); + + /* check for slum right neighbor */ + ret = neighbor_in_slum(node, &right_lock, RIGHT_SIDE, ZNODE_WRITE_LOCK); + if (ret == -E_NO_NEIGHBOR) + /* no neighbor, repeat on this node */ + continue; + else if (ret) + goto exit; + ret = incr_load_count_znode(&right_load, right_lock.node); + if (ret) { + done_lh(&right_lock); + break; + } + coord_init_after_item_end(&pos->coord); + coord_init_before_first_item(&coord, right_lock.node); + + if (iplug->b.mergeable(&pos->coord, &coord)) { + /* go to slum right neighbor */ + item_squeeze_data(pos)->mergeable = 1; + done_load_count(&right_load); + done_lh(&right_lock); + break; + } + /* first item of right neighbor is not mergeable, + repeat this node */ + done_load_count(&right_load); + done_lh(&right_lock); + } + } + exit: + JF_CLR(ZJNODE(node), JNODE_SQUEEZABLE); + znode_make_dirty(node); + return ret; +} + +/* Squeeze and allocate the right neighbor. This is called after @left and + its current children have been squeezed and allocated already. This + procedure's job is to squeeze and items from @right to @left. + + If at the leaf level, use the shift_everything_left memcpy-optimized + version of shifting (squeeze_right_leaf). + + If at the twig level, extents are allocated as they are shifted from @right + to @left (squalloc_right_twig). + + At any other level, shift one internal item and return to the caller + (squalloc_parent_first) so that the shifted-subtree can be processed in + parent-first order. + + When unit of internal item is moved, squeezing stops and SUBTREE_MOVED is + returned. When all content of @right is squeezed, SQUEEZE_SOURCE_EMPTY is + returned. If nothing can be moved into @left anymore, SQUEEZE_TARGET_FULL + is returned. +*/ + +static int squeeze_right_neighbor(flush_pos_t * pos, znode * left, znode * right) +{ + int ret; + + /* FIXME it is possible to see empty hasn't-heard-banshee node in a + * tree owing to error (for example, ENOSPC) in write */ + /* assert("jmacd-9321", !node_is_empty(left)); */ + assert("jmacd-9322", !node_is_empty(right)); + assert("jmacd-9323", znode_get_level(left) == znode_get_level(right)); + + ON_TRACE(TRACE_FLUSH_VERB, "sq_rn[%u] left %s\n", znode_get_level(left), znode_tostring(left)); + ON_TRACE(TRACE_FLUSH_VERB, "sq_rn[%u] right %s\n", znode_get_level(left), znode_tostring(right)); + + switch (znode_get_level(left)) { + case TWIG_LEVEL: + /* Shift with extent allocating until either an internal item + is encountered or everything is shifted or no free space + left in @left */ + ret = squeeze_right_twig(left, right, pos); + break; + + default: + /* All other levels can use shift_everything until we implement per-item + flush plugins. */ + ret = squeeze_right_non_twig(left, right); + break; + } + + assert("jmacd-2011", (ret < 0 || + ret == SQUEEZE_SOURCE_EMPTY || ret == SQUEEZE_TARGET_FULL || ret == SUBTREE_MOVED)); + + if (ret == SQUEEZE_SOURCE_EMPTY) { + reiser4_stat_inc(flush.squeezed_completely); + } + + ON_TRACE(TRACE_FLUSH_VERB, "sq_rn[%u] returns %s: left %s\n", + znode_get_level(left), + (ret == SQUEEZE_SOURCE_EMPTY) ? "src empty" : + ((ret == SQUEEZE_TARGET_FULL) ? "tgt full" : + ((ret == SUBTREE_MOVED) ? "tree moved" : "error")), znode_tostring(left)); + return ret; +} + +static int squeeze_right_twig_and_advance_coord (flush_pos_t * pos, znode * right) +{ + int ret; + + ret = squeeze_right_twig(pos->lock.node, right, pos); + if (ret < 0) + return ret; + if (ret > 0) { + coord_init_after_last_item(&pos->coord, pos->lock.node); + return ret; + } + + coord_init_last_unit(&pos->coord, pos->lock.node); + return 0; +} + +#if 0 +/* "prepped" check for parent node without long-term locking it */ +static inline int fast_check_parent_flushprepped (znode * node) +{ + reiser4_tree * tree = current_tree; + int prepped = 1; + + RLOCK_TREE(tree); + + if (node->in_parent.node || !jnode_is_flushprepped(ZJNODE(node))) + prepped = 0; + + RUNLOCK_TREE(tree); + + return prepped; +} +#endif + +/* forward declaration */ +static int squalloc_upper_levels (flush_pos_t *, znode *, znode *); + +/* do a fast check for "same parents" condition before calling + * squalloc_upper_levels() */ +static inline int check_parents_and_squalloc_upper_levels (flush_pos_t * pos, znode *left, znode * right) +{ + if (znode_same_parents(left, right)) + return 0; + + return squalloc_upper_levels(pos, left, right); +} + +/* Check whether the parent of given @right node needs to be processes + ((re)allocated) prior to processing of the child. If @left and @right do not + share at least the parent of the @right is after the @left but before the + @right in parent-first order, we have to (re)allocate it before the @right + gets (re)allocated. */ +static int squalloc_upper_levels (flush_pos_t * pos, znode *left, znode * right) +{ + int ret; + + lock_handle left_parent_lock; + lock_handle right_parent_lock; + + load_count left_parent_load; + load_count right_parent_load; + + + init_lh(&left_parent_lock); + init_lh(&right_parent_lock); + + init_load_count(&left_parent_load); + init_load_count(&right_parent_load); + + ret = reiser4_get_parent(&left_parent_lock, left, ZNODE_WRITE_LOCK, 0); + if (ret) + goto out; + + ret = reiser4_get_parent(&right_parent_lock, right, ZNODE_WRITE_LOCK, 0); + if (ret) + goto out; + + /* Check for same parents */ + if (left_parent_lock.node == right_parent_lock.node) + goto out; + + if (znode_check_flushprepped(right_parent_lock.node)) { + /* Keep parent-first order. In the order, the right parent node stands + before the @right node. If it is already allocated, we set the + preceder (next block search start point) to its block number, @right + node should be allocated after it. + + However, preceder is set only if the right parent is on twig level. + The explanation is the following: new branch nodes are allocated over + already allocated children while the tree grows, it is difficult to + keep tree ordered, we assume that only leaves and twings are correctly + allocated. So, only twigs are used as a preceder for allocating of the + rest of the slum. */ + if (znode_get_level(right_parent_lock.node) == TWIG_LEVEL) { + pos->preceder.blk = *znode_get_block(right_parent_lock.node); + check_preceder(pos->preceder.blk); + } + goto out; + } + + ret = incr_load_count_znode(&left_parent_load, left_parent_lock.node); + if (ret) + goto out; + + ret = incr_load_count_znode(&right_parent_load, right_parent_lock.node); + if (ret) + goto out; + + ret = squeeze_right_neighbor(pos, left_parent_lock.node, right_parent_lock.node); + /* We stop if error. We stop if some items/units were shifted (ret == 0) + * and thus @right changed its parent. It means we have not process + * right_parent node prior to processing of @right. Positive return + * values say that shifting items was not happen because of "empty + * source" or "target full" conditions. */ + if (ret <= 0) + goto out; + + /* parent(@left) and parent(@right) may have different parents also. We + * do a recursive call for checking that. */ + ret = check_parents_and_squalloc_upper_levels(pos, left_parent_lock.node, right_parent_lock.node); + if (ret) + goto out; + + /* allocate znode when going down */ + ret = lock_parent_and_allocate_znode(right_parent_lock.node, pos); + + out: + done_load_count(&left_parent_load); + done_load_count(&right_parent_load); + + done_lh(&left_parent_lock); + done_lh(&right_parent_lock); + + return ret; +} + +/* Check the leftmost child "flushprepped" status, also returns true if child + * node was not found in cache. */ +static int leftmost_child_of_unit_check_flushprepped (const coord_t *coord) +{ + int ret; + int prepped; + + jnode * child; + + ret = get_leftmost_child_of_unit(coord, &child); + + if (ret) + return ret; + + if (child) { + prepped = jnode_check_flushprepped(child); + jput(child); + } else { + /* We consider not existing child as a node which slum + processing should not continue to. Not cached node is clean, + so it is flushprepped. */ + prepped = 1; + } + + return prepped; +} + +/* (re)allocate znode with automated getting parent node */ +static int lock_parent_and_allocate_znode (znode * node, flush_pos_t * pos) +{ + int ret; + lock_handle parent_lock; + load_count parent_load; + coord_t pcoord; + + assert ("zam-851", znode_is_write_locked(node)); + + init_lh(&parent_lock); + init_load_count(&parent_load); + + ret = reiser4_get_parent(&parent_lock, node, ZNODE_WRITE_LOCK, 0); + if (ret) + goto out; + + ret = incr_load_count_znode(&parent_load, parent_lock.node); + if (ret) + goto out; + + ret = find_child_ptr(parent_lock.node, node, &pcoord); + if (ret) + goto out; + + ret = allocate_znode(node, &pcoord, pos); + + out: + done_load_count(&parent_load); + done_lh(&parent_lock); + return ret; +} + +/* Process nodes on leaf level until unformatted node or rightmost node in the + * slum reached. */ +static int handle_pos_on_formatted (flush_pos_t * pos) +{ + int ret; + lock_handle right_lock; + load_count right_load; + + init_lh(&right_lock); + init_load_count(&right_load); + + check_pos(pos); + if (znode_squeezable(pos->lock.node)) { + ret = squeeze_node(pos, pos->lock.node); + check_pos(pos); + if (ret) + return ret; + } + + while (1) { + check_pos(pos); + ret = neighbor_in_slum(pos->lock.node, &right_lock, RIGHT_SIDE, ZNODE_WRITE_LOCK); + if (ret) + break; + + /* we don't prep(allocate) nodes for flushing twice. This can be suboptimal, or it + * can be optimal. For now we choose to live with the risk that it will + * be suboptimal because it would be quite complex to code it to be + * smarter. */ + if (znode_check_flushprepped(right_lock.node) && !znode_squeezable(right_lock.node)) { + pos_stop(pos); + break; + } + + ret = incr_load_count_znode(&right_load, right_lock.node); + if (ret) + break; + + if (znode_squeezable(right_lock.node)) { + ret = squeeze_node(pos, right_lock.node); + check_pos(pos); + if (ret) + break; + if (node_is_empty(right_lock.node)) { + /* node was squeezed completely, repeat */ + done_load_count(&right_load); + done_lh(&right_lock); + continue; + } + } + + /* squeeze _before_ going upward. */ + ret = squeeze_right_neighbor(pos, pos->lock.node, right_lock.node); + check_pos(pos); + if (ret < 0) + break; + + if (znode_check_flushprepped(right_lock.node)) { + pos_stop(pos); + break; + } + + if (node_is_empty(right_lock.node)) { + /* repeat if right node was squeezed completely */ + done_load_count(&right_load); + done_lh(&right_lock); + continue; + } + + /* parent(right_lock.node) has to be processed before + * (right_lock.node) due to "parent-first" allocation order. */ + ret = check_parents_and_squalloc_upper_levels(pos, pos->lock.node, right_lock.node); + check_pos(pos); + if (ret) + break; + /* (re)allocate _after_ going upward */ + ret = lock_parent_and_allocate_znode(right_lock.node, pos); + check_pos(pos); + if (ret) + break; + + if (should_terminate_squalloc(pos)) { + set_item_squeeze_count(pos, 0); + break; + } + /* advance the flush position to the right neighbor */ + move_flush_pos(pos, &right_lock, &right_load, NULL); + + ret = rapid_flush(pos); + check_pos(pos); + if (ret) + break; + } + check_pos(pos); + + done_load_count(&right_load); + done_lh(&right_lock); + + /* This function indicates via pos whether to stop or go to twig or continue on current + * level. */ + return ret; + +} + +/* Process nodes on leaf level until unformatted node or rightmost node in the + * slum reached. */ +static int handle_pos_on_leaf (flush_pos_t * pos) +{ + int ret; + + assert ("zam-845", pos->state == POS_ON_LEAF); + + ret = handle_pos_on_formatted(pos); + + if (ret == -E_NO_NEIGHBOR) { + /* cannot get right neighbor, go process extents. */ + pos->state = POS_TO_TWIG; + return 0; + } + + return ret; +} + +/* Process slum on level > 1 */ +static int handle_pos_on_internal (flush_pos_t * pos) +{ + assert ("zam-850", pos->state == POS_ON_INTERNAL); + return handle_pos_on_formatted(pos); +} + +/* check whether squalloc should stop before processing given extent */ +static int squalloc_extent_should_stop (flush_pos_t * pos) +{ + assert("zam-869", item_is_extent(&pos->coord)); + + /* pos->child is a jnode handle_pos_on_extent() should start with in + * stead of the first child of the first extent unit. */ + if (pos->child) { + int prepped; + + assert("vs-1383", jnode_is_unformatted(pos->child)); + prepped = jnode_check_flushprepped(pos->child); + pos->pos_in_unit = jnode_get_index(pos->child) - extent_unit_index(&pos->coord); + assert("vs-1470", pos->pos_in_unit < extent_unit_width(&pos->coord)); + assert("nikita-3434", ergo(extent_is_unallocated(&pos->coord), + pos->pos_in_unit == 0)); + jput(pos->child); + pos->child = NULL; + + return prepped; + } + + pos->pos_in_unit = 0; + if (extent_is_unallocated(&pos->coord)) + return 0; + + return leftmost_child_of_unit_check_flushprepped(&pos->coord); +} + +int alloc_extent(flush_pos_t *flush_pos); + +/* Handle the case when regular reiser4 tree (znodes connected one to its + * neighbors by sibling pointers) is interrupted on leaf level by one or more + * unformatted nodes. By having a lock on twig level and use extent code + * routines to process unformatted nodes we swim around an irregular part of + * reiser4 tree. */ +static int handle_pos_on_twig (flush_pos_t * pos) +{ + int ret; + + assert ("zam-844", pos->state == POS_ON_EPOINT); + assert ("zam-843", item_is_extent(&pos->coord)); + + check_pos(pos); + /* We decide should we continue slum processing with current extent + unit: if leftmost child of current extent unit is flushprepped + (i.e. clean or already processed by flush) we stop squalloc(). There + is a fast check for unallocated extents which we assume contain all + not flushprepped nodes. */ + /* FIXME: Here we implement simple check, we are only looking on the + leftmost child. */ + ret = squalloc_extent_should_stop(pos); + if (ret != 0) { + pos_stop(pos); + return ret; + } + + while (pos_valid(pos) && coord_is_existing_unit(&pos->coord) && item_is_extent(&pos->coord)) { + check_pos(pos); + ret = alloc_extent(pos); + check_pos(pos); + if (ret) { + break; + } + coord_next_unit(&pos->coord); + } + + if (coord_is_after_rightmost(&pos->coord)) { + pos->state = POS_END_OF_TWIG; + return 0; + } + if (item_is_internal(&pos->coord)) { + pos->state = POS_TO_LEAF; + return 0; + } + + assert ("zam-860", item_is_extent(&pos->coord)); + + check_pos(pos); + /* "slum" is over */ + pos->state = POS_INVALID; + return 0; +} + +/* When we about to return flush position from twig to leaf level we can process + * the right twig node or move position to the leaf. This processes right twig + * if it is possible and jump to leaf level if not. */ +static int handle_pos_end_of_twig (flush_pos_t * pos) +{ + int ret; + lock_handle right_lock; + load_count right_load; + coord_t at_right; + jnode * child = NULL; + + + assert ("zam-848", pos->state == POS_END_OF_TWIG); + assert ("zam-849", coord_is_after_rightmost(&pos->coord)); + + init_lh(&right_lock); + init_load_count(&right_load); + + check_pos(pos); + /* We get a lock on the right twig node even it is not dirty because + * slum continues or discontinues on leaf level not on next twig. This + * lock on the right twig is needed for getting its leftmost child. */ + ret = reiser4_get_right_neighbor(&right_lock, pos->lock.node, ZNODE_WRITE_LOCK, GN_SAME_ATOM); + if (ret) + goto out; + + ret = incr_load_count_znode(&right_load, right_lock.node); + if (ret) + goto out; + + /* right twig could be not dirty */ + if (znode_check_dirty(right_lock.node)) { + /* If right twig node is dirty we always attempt to squeeze it + * content to the left... */ +became_dirty: + check_pos(pos); + ret = squeeze_right_twig_and_advance_coord(pos, right_lock.node); + check_pos(pos); + if (ret <=0) { + /* pos->coord is on internal item, go to leaf level, or + * we have an error which will be caught in squalloc() */ + pos->state = POS_TO_LEAF; + goto out; + } + + /* If right twig was squeezed completely we wave to re-lock + * right twig. now it is done through the top-level squalloc + * routine. */ + if (node_is_empty(right_lock.node)) + goto out; + + /* ... and prep it if it is not yet prepped */ + if (!znode_check_flushprepped(right_lock.node)) { + /* As usual, process parent before ...*/ + ret = check_parents_and_squalloc_upper_levels(pos, pos->lock.node, right_lock.node); + check_pos(pos); + if (ret) + goto out; + + /* ... processing the child */ + ret = lock_parent_and_allocate_znode(right_lock.node, pos); + check_pos(pos); + if (ret) + goto out; + } + } else { + coord_init_first_unit(&at_right, right_lock.node); + + /* check first child of next twig, should we continue there ? */ + ret = get_leftmost_child_of_unit(&at_right, &child); + if (ret || child == NULL || jnode_check_flushprepped(child)) { + pos_stop(pos); + goto out; + } + + /* check clean twig for possible relocation */ + if (!znode_check_flushprepped(right_lock.node)) { + check_pos(pos); + ret = reverse_relocate_check_dirty_parent(child, &at_right, pos); + check_pos(pos); + if (ret) + goto out; + if (znode_check_dirty(right_lock.node)) + goto became_dirty; + } + } + + assert ("zam-875", znode_check_flushprepped(right_lock.node)); + + /* Update the preceder by a block number of just processed right twig + * node. The code above could miss the preceder updating because + * allocate_znode() could not be called for this node. */ + pos->preceder.blk = *znode_get_block(right_lock.node); + check_preceder(pos->preceder.blk); + + coord_init_first_unit(&at_right, right_lock.node); + assert("zam-868", coord_is_existing_unit(&at_right)); + + pos->state = item_is_extent(&at_right) ? POS_ON_EPOINT : POS_TO_LEAF; + move_flush_pos(pos, &right_lock, &right_load, &at_right); + + out: + check_pos(pos); + done_load_count(&right_load); + done_lh(&right_lock); + + if (child) + jput(child); + + return ret; +} + +/* Move the pos->lock to leaf node pointed by pos->coord, check should we + * continue there. */ +static int handle_pos_to_leaf (flush_pos_t * pos) +{ + int ret; + lock_handle child_lock; + load_count child_load; + jnode * child; + + assert ("zam-846", pos->state == POS_TO_LEAF); + assert ("zam-847", item_is_internal(&pos->coord)); + + init_lh(&child_lock); + init_load_count(&child_load); + + check_pos(pos); + ret = get_leftmost_child_of_unit(&pos->coord, &child); + if (ret) + return ret; + if (child == NULL) { + pos_stop(pos); + return 0; + } + + if (jnode_check_flushprepped(child)) { + pos->state = POS_INVALID; + goto out; + } + + ret = longterm_lock_znode(&child_lock, JZNODE(child), ZNODE_WRITE_LOCK, ZNODE_LOCK_LOPRI); + if (ret) + goto out; + + ret = incr_load_count_znode(&child_load, JZNODE(child)); + if (ret) + goto out; + + ret = allocate_znode(JZNODE(child), &pos->coord, pos); + check_pos(pos); + if (ret) + goto out; + + /* move flush position to leaf level */ + pos->state = POS_ON_LEAF; + move_flush_pos(pos, &child_lock, &child_load, NULL); + + if (node_is_empty(JZNODE(child))) { + ret = delete_empty_node(JZNODE(child)); + check_pos(pos); + pos->state = POS_INVALID; + } + out: + check_pos(pos); + done_load_count(&child_load); + done_lh(&child_lock); + jput(child); + + return ret; +} +/* move pos from leaf to twig, and move lock from leaf to twig. */ +/* Move pos->lock to upper (twig) level */ +static int handle_pos_to_twig (flush_pos_t * pos) +{ + int ret; + + lock_handle parent_lock; + load_count parent_load; + coord_t pcoord; + + assert ("zam-852", pos->state == POS_TO_TWIG); + + init_lh(&parent_lock); + init_load_count(&parent_load); + + check_pos(pos); + ret = reiser4_get_parent(&parent_lock, pos->lock.node, ZNODE_WRITE_LOCK, 0); + if (ret) + goto out; + + ret = incr_load_count_znode(&parent_load, parent_lock.node); + if (ret) + goto out; + + ret = find_child_ptr(parent_lock.node, pos->lock.node, &pcoord); + if (ret) + goto out; + + assert ("zam-870", item_is_internal(&pcoord)); + coord_next_item(&pcoord); + + if (coord_is_after_rightmost(&pcoord)) + pos->state = POS_END_OF_TWIG; + else if (item_is_extent(&pcoord)) + pos->state = POS_ON_EPOINT; + else { + /* Here we understand that getting -E_NO_NEIGHBOR in + * handle_pos_on_leaf() was because of just a reaching edge of + * slum */ + pos_stop(pos); + goto out; + } + + move_flush_pos(pos, &parent_lock, &parent_load, &pcoord); + + out: + check_pos(pos); + done_load_count(&parent_load); + done_lh(&parent_lock); + + return ret; +} + +typedef int (*pos_state_handle_t)(flush_pos_t*); +static pos_state_handle_t flush_pos_handlers[] = { + /* process formatted nodes on leaf level, keep lock on a leaf node */ + [POS_ON_LEAF] = handle_pos_on_leaf, + /* process unformatted nodes, keep lock on twig node, pos->coord points to extent currently + * being processed */ + [POS_ON_EPOINT] = handle_pos_on_twig, + /* move a lock from leaf node to its parent for further processing of unformatted nodes */ + [POS_TO_TWIG] = handle_pos_to_twig, + /* move a lock from twig to leaf level when a processing of unformatted nodes finishes, + * pos->coord points to the leaf node we jump to */ + [POS_TO_LEAF] = handle_pos_to_leaf, + /* after processing last extent in the twig node, attempting to shift items from the twigs + * right neighbor and process them while shifting */ + [POS_END_OF_TWIG] = handle_pos_end_of_twig, + /* process formatted nodes on internal level, keep lock on an internal node */ + [POS_ON_INTERNAL] = handle_pos_on_internal +}; + +/* Advance flush position horizontally, prepare for flushing ((re)allocate, squeeze, + * encrypt) nodes and their ancestors in "parent-first" order */ +static int squalloc (flush_pos_t * pos) +{ + int ret = 0; + + /* maybe needs to be made a case statement with handle_pos_on_leaf as first case, for + * greater CPU efficiency? Measure and see.... -Hans */ + while (pos_valid(pos)) { + check_pos(pos); + ret = flush_pos_handlers[pos->state](pos); + check_pos(pos); + if (ret < 0) + break; + + ret = rapid_flush(pos); + check_pos(pos); + if (ret) + break; + } + + /* any positive value or -E_NO_NEIGHBOR are legal return codes for handle_pos* + routines, -E_NO_NEIGHBOR means that slum edge was reached */ + if (ret > 0 || ret == -E_NO_NEIGHBOR) + ret = 0; + + return ret; +} + +static void update_ldkey(znode *node) +{ + reiser4_key ldkey; + + assert("vs-1630", rw_dk_is_write_locked(znode_get_tree(node))); + if (node_is_empty(node)) + return; + + znode_set_ld_key(node, leftmost_key_in_node(node, &ldkey)); +} + +/* this is to be called after calling of shift node's method to shift data from @right to + @left. It sets left delimiting keys of @left and @right to keys of first items of @left + and @right correspondingly and sets right delimiting key of @left to first key of @right */ +static void +update_znode_dkeys(znode *left, znode *right) +{ + assert("nikita-1470", rw_dk_is_write_locked(znode_get_tree(right))); + assert("vs-1629", znode_is_write_locked(left) && znode_is_write_locked(right)); + + /* we need to update left delimiting of left if it was empty before shift */ + update_ldkey(left); + update_ldkey(right); + if (node_is_empty(right)) + znode_set_rd_key(left, znode_get_rd_key(right)); + else + znode_set_rd_key(left, znode_get_ld_key(right)); +} + +/* try to shift everything from @right to @left. If everything was shifted - + @right is removed from the tree. Result is the number of bytes shifted. */ +static int +shift_everything_left(znode * right, znode * left, carry_level * todo) +{ + coord_t from; + node_plugin *nplug; + carry_plugin_info info; + + coord_init_after_last_item(&from, right); + + IF_TRACE(TRACE_COORDS, print_coord("shift_everything_left:", &from, 0)); + + nplug = node_plugin_by_node(right); + info.doing = NULL; + info.todo = todo; + return nplug->shift(&from, left, SHIFT_LEFT, + 1 /* delete @right if it becomes empty */, + 1 /* move coord @from to node @left if everything will be shifted */, + &info); +} + +/* Shift as much as possible from @right to @left using the memcpy-optimized + shift_everything_left. @left and @right are formatted neighboring nodes on + leaf level. */ +static int +squeeze_right_non_twig(znode * left, znode * right) +{ + int ret; + carry_pool pool; + carry_level todo; + ON_STATS(int old_items; int old_free_space); + + assert("nikita-2246", znode_get_level(left) == znode_get_level(right)); + + if (!znode_is_dirty(left) || !znode_is_dirty(right)) + return SQUEEZE_TARGET_FULL; + + init_carry_pool(&pool); + init_carry_level(&todo, &pool); + + ON_STATS(old_items = node_num_items(left); old_free_space = znode_free_space(left)); + + ret = shift_everything_left(right, left, &todo); + if (ret > 0) { + /* something was shifted */ + reiser4_tree *tree; + __u64 grabbed; + + znode_make_dirty(left); + znode_make_dirty(right); + + /* update delimiting keys of nodes which participated in + shift. FIXME: it would be better to have this in shift + node's operation. But it can not be done there. Nobody + remembers why, though */ + tree = znode_get_tree(left); + UNDER_RW_VOID(dk, tree, write, update_znode_dkeys(left, right)); + + /* Carry is called to update delimiting key and, maybe, to remove empty + node. */ + grabbed = get_current_context()->grabbed_blocks; + ret = reiser4_grab_space_force(tree->height, BA_RESERVED); + assert("nikita-3003", ret == 0); /* reserved space is exhausted. Ask Hans. */ + + ON_STATS(todo.level_no = znode_get_level(left) + 1); + + ret = carry(&todo, NULL /* previous level */ ); + grabbed2free_mark(grabbed); + } else { + /* Shifting impossible, we return appropriate result code */ + ret = node_is_empty(right) ? SQUEEZE_SOURCE_EMPTY : SQUEEZE_TARGET_FULL; + } + + done_carry_pool(&pool); + +#if REISER4_STATS + if (znode_get_level(left) == LEAF_LEVEL) { + reiser4_stat_inc(flush.squeezed_leaves); + reiser4_stat_add(flush.squeezed_leaf_items, node_num_items(left) - old_items); + reiser4_stat_add(flush.squeezed_leaf_bytes, old_free_space - znode_free_space(left)); + } +#endif + + return ret; +} + +/* Shift first unit of first item if it is an internal one. Return + SQUEEZE_TARGET_FULL if it fails to shift an item, otherwise return + SUBTREE_MOVED. */ +static int +shift_one_internal_unit(znode * left, znode * right) +{ + int ret; + carry_pool pool; + carry_level todo; + coord_t coord; + int size, moved; + carry_plugin_info info; + + assert("nikita-2247", znode_get_level(left) == znode_get_level(right)); + assert("nikita-2435", znode_is_write_locked(left)); + assert("nikita-2436", znode_is_write_locked(right)); + assert("nikita-2434", UNDER_RW(tree, znode_get_tree(left), read, left->right == right)); + + coord_init_first_unit(&coord, right); + +#if REISER4_DEBUG + if (!node_is_empty(left)) { + coord_t last; + reiser4_key right_key; + reiser4_key left_key; + + coord_init_last_unit(&last, left); + + assert("nikita-2463", + keyle(item_key_by_coord(&last, &left_key), item_key_by_coord(&coord, &right_key))); + } +#endif + + assert("jmacd-2007", item_is_internal(&coord)); + + init_carry_pool(&pool); + init_carry_level(&todo, &pool); + + size = item_length_by_coord(&coord); + info.todo = &todo; + info.doing = NULL; + + ret = node_plugin_by_node(left)->shift(&coord, left, SHIFT_LEFT, + 1 /* delete @right if it becomes empty */, + 0 /* do not move coord @coord to node @left */, + &info); + + /* If shift returns positive, then we shifted the item. */ + assert("vs-423", ret <= 0 || size == ret); + moved = (ret > 0); + + if (moved) { + /* something was moved */ + reiser4_tree *tree; + int grabbed; + + znode_make_dirty(left); + znode_make_dirty(right); + tree = znode_get_tree(left); + UNDER_RW_VOID(dk, tree, write, update_znode_dkeys(left, right)); + + /* reserve space for delimiting keys after shifting */ + grabbed = get_current_context()->grabbed_blocks; + ret = reiser4_grab_space_force(tree->height, BA_RESERVED); + assert("nikita-3003", ret == 0); /* reserved space is exhausted. Ask Hans. */ + + ON_STATS(todo.level_no = znode_get_level(left) + 1); + + ret = carry(&todo, NULL /* previous level */ ); + grabbed2free_mark(grabbed); + } + + ON_TRACE(TRACE_FLUSH_VERB, + "shift_one %s an item: left has %u items, right has %u items\n", + moved > 0 ? "moved" : "did not move", node_num_items(left), node_num_items(right)); + + done_carry_pool(&pool); + + if (ret != 0) { + /* Shift or carry operation failed. */ + assert("jmacd-7325", ret < 0); + return ret; + } + + return moved ? SUBTREE_MOVED : SQUEEZE_TARGET_FULL; +} + +/* ALLOCATE INTERFACE */ +/* Audited by: umka (2002.06.11) */ +reiser4_internal void +jnode_set_block(jnode * node /* jnode to update */ , + const reiser4_block_nr * blocknr /* new block nr */ ) +{ + assert("nikita-2020", node != NULL); + assert("umka-055", blocknr != NULL); + assert("zam-819", ergo(JF_ISSET(node, JNODE_EFLUSH), node->blocknr == 0)); + assert("vs-1453", ergo(JF_ISSET(node, JNODE_EFLUSH), jnode_is_unformatted(node))); + node->blocknr = *blocknr; +} + +/* Make the final relocate/wander decision during forward parent-first squalloc for a + znode. For unformatted nodes this is done in plugin/item/extent.c:extent_needs_allocation(). */ +static int +allocate_znode_loaded(znode * node, + const coord_t * parent_coord, flush_pos_t * pos) +{ + int ret; + reiser4_super_info_data * sbinfo = get_current_super_private(); + /* FIXME(D): We have the node write-locked and should have checked for ! + allocated() somewhere before reaching this point, but there can be a race, so + this assertion is bogus. */ + assert("jmacd-7987", !jnode_check_flushprepped(ZJNODE(node))); + assert("jmacd-7988", znode_is_write_locked(node)); + assert("jmacd-7989", coord_is_invalid(parent_coord) + || znode_is_write_locked(parent_coord->node)); + + if (ZF_ISSET(node, JNODE_REPACK) || znode_created(node) || znode_is_root(node) || + /* We have enough nodes to relocate no matter what. */ + (pos->leaf_relocate != 0 && znode_get_level(node) == LEAF_LEVEL)) + { + /* No need to decide with new nodes, they are treated the same as + relocate. If the root node is dirty, relocate. */ + if (pos->preceder.blk == 0) { + /* preceder is unknown and we have decided to relocate node -- + using of default value for search start is better than search + from block #0. */ + get_blocknr_hint_default(&pos->preceder.blk); + reiser4_stat_inc(block_alloc.nohint); + check_preceder(pos->preceder.blk); + } + + goto best_reloc; + + } else if (pos->preceder.blk == 0) { + /* If we don't know the preceder, leave it where it is. */ + jnode_make_wander(ZJNODE(node)); + } else { + /* Make a decision based on block distance. */ + reiser4_block_nr dist; + reiser4_block_nr nblk = *znode_get_block(node); + + assert("jmacd-6172", !blocknr_is_fake(&nblk)); + assert("jmacd-6173", !blocknr_is_fake(&pos->preceder.blk)); + assert("jmacd-6174", pos->preceder.blk != 0); + + if (pos->preceder.blk == nblk - 1) { + /* Ideal. */ + jnode_make_wander(ZJNODE(node)); + } else { + + dist = (nblk < pos->preceder.blk) ? (pos->preceder.blk - nblk) : (nblk - pos->preceder.blk); + + /* See if we can find a closer block (forward direction only). */ + pos->preceder.max_dist = min((reiser4_block_nr)sbinfo->flush.relocate_distance, dist); + pos->preceder.level = znode_get_level(node); + + ret = allocate_znode_update(node, parent_coord, pos); + + pos->preceder.max_dist = 0; + + if (ret && (ret != -ENOSPC)) + return ret; + + if (ret == 0) { + /* Got a better allocation. */ + znode_make_reloc(node, pos->fq); + } else if (dist < sbinfo->flush.relocate_distance) { + /* The present allocation is good enough. */ + jnode_make_wander(ZJNODE(node)); + } else { + /* Otherwise, try to relocate to the best position. */ + best_reloc: + ret = allocate_znode_update(node, parent_coord, pos); + if (ret != 0) + return ret; + + /* set JNODE_RELOC bit _after_ node gets allocated */ + znode_make_reloc(node, pos->fq); + } + } + } + + /* This is the new preceder. */ + pos->preceder.blk = *znode_get_block(node); + check_preceder(pos->preceder.blk); + pos->alloc_cnt += 1; + + assert ("jmacd-4277", !blocknr_is_fake(&pos->preceder.blk)); + + return 0; +} + +static int +allocate_znode(znode * node, const coord_t * parent_coord, flush_pos_t * pos) +{ + /* + * perform znode allocation with znode pinned in memory to avoid races + * with asynchronous emergency flush (which plays with + * JNODE_FLUSH_RESERVED bit). + */ + return WITH_DATA(node, allocate_znode_loaded(node, parent_coord, pos)); +} + + +/* A subroutine of allocate_znode, this is called first to see if there is a close + position to relocate to. It may return ENOSPC if there is no close position. If there + is no close position it may not relocate. This takes care of updating the parent node + with the relocated block address. */ +static int +allocate_znode_update(znode * node, const coord_t * parent_coord, flush_pos_t * pos) +{ + int ret; + reiser4_block_nr blk; + lock_handle uber_lock; + int flush_reserved_used = 0; + int grabbed; + + init_lh(&uber_lock); + + grabbed = get_current_context()->grabbed_blocks; + + /* discard e-flush allocation */ + ret = zload(node); + if (ret) + return ret; + + if (ZF_ISSET(node, JNODE_CREATED)) { + assert ("zam-816", blocknr_is_fake(znode_get_block(node))); + pos->preceder.block_stage = BLOCK_UNALLOCATED; + } else { + pos->preceder.block_stage = BLOCK_GRABBED; + + /* The disk space for relocating the @node is already reserved in "flush reserved" + * counter if @node is leaf, otherwise we grab space using BA_RESERVED (means grab + * space from whole disk not from only 95%). */ + if (znode_get_level(node) == LEAF_LEVEL) { + /* + * earlier (during do_jnode_make_dirty()) we decided + * that @node can possibly go into overwrite set and + * reserved block for its wandering location. + */ + txn_atom * atom = get_current_atom_locked(); + assert("nikita-3449", + ZF_ISSET(node, JNODE_FLUSH_RESERVED)); + flush_reserved2grabbed(atom, (__u64)1); + spin_unlock_atom(atom); + /* + * we are trying to move node into relocate + * set. Allocation of relocated position "uses" + * reserved block. + */ + ZF_CLR(node, JNODE_FLUSH_RESERVED); + flush_reserved_used = 1; + } else { + ret = reiser4_grab_space_force((__u64)1, BA_RESERVED); + if (ret != 0) + goto exit; + } + } + + /* We may do not use 5% of reserved disk space here and flush will not pack tightly. */ + ret = reiser4_alloc_block(&pos->preceder, &blk, BA_FORMATTED | BA_PERMANENT); + if(ret) + goto exit; + + + if (!ZF_ISSET(node, JNODE_CREATED) && + (ret = reiser4_dealloc_block(znode_get_block(node), 0, BA_DEFER | BA_FORMATTED))) + goto exit; + + if (likely(!znode_is_root(node))) { + item_plugin *iplug; + + iplug = item_plugin_by_coord(parent_coord); + assert("nikita-2954", iplug->f.update != NULL); + iplug->f.update(parent_coord, &blk); + + znode_make_dirty(parent_coord->node); + + } else { + reiser4_tree *tree = znode_get_tree(node); + znode *uber; + + /* We take a longterm lock on the fake node in order to change + the root block number. This may cause atom fusion. */ + ret = get_uber_znode(tree, ZNODE_WRITE_LOCK, ZNODE_LOCK_HIPRI, + &uber_lock); + /* The fake node cannot be deleted, and we must have priority + here, and may not be confused with ENOSPC. */ + assert("jmacd-74412", + ret != -EINVAL && ret != -E_DEADLOCK && ret != -ENOSPC); + + if (ret) + goto exit; + + uber = uber_lock.node; + + UNDER_RW_VOID(tree, tree, write, tree->root_block = blk); + + znode_make_dirty(uber); + } + + ret = znode_rehash(node, &blk); +exit: + if(ret) { + /* Get flush reserved block back if something fails, because + * callers assume that on error block wasn't relocated and its + * flush reserved block wasn't used. */ + if (flush_reserved_used) { + /* + * ok, we failed to move node into relocate + * set. Restore status quo. + */ + grabbed2flush_reserved((__u64)1); + ZF_SET(node, JNODE_FLUSH_RESERVED); + } + } + zrelse(node); + done_lh(&uber_lock); + grabbed2free_mark(grabbed); + return ret; +} + +/* JNODE INTERFACE */ + +/* Lock a node (if formatted) and then get its parent locked, set the child's + coordinate in the parent. If the child is the root node, the above_root + znode is returned but the coord is not set. This function may cause atom + fusion, but it is only used for read locks (at this point) and therefore + fusion only occurs when the parent is already dirty. */ +/* Hans adds this note: remember to ask how expensive this operation is vs. storing parent + pointer in jnodes. */ +static int +jnode_lock_parent_coord(jnode * node, + coord_t * coord, + lock_handle * parent_lh, + load_count * parent_zh, + znode_lock_mode parent_mode, + int try) +{ + int ret; + + assert("edward-53", jnode_is_unformatted(node) || jnode_is_znode(node)); + assert("edward-54", jnode_is_unformatted(node) || znode_is_any_locked(JZNODE(node))); + + if (!jnode_is_znode(node)) { + reiser4_key key; + tree_level stop_level = TWIG_LEVEL ; + lookup_bias bias = FIND_EXACT; + + assert("edward-168", !(jnode_get_type(node) == JNODE_BITMAP)); + + /* The case when node is not znode, but can have parent coord + (unformatted node, node which represents cluster page, + etc..). Generate a key for the appropriate entry, search + in the tree using coord_by_key, which handles locking for + us. */ + + /* + * nothing is locked at this moment, so, nothing prevents + * concurrent truncate from removing jnode from inode. To + * prevent this spin-lock jnode. jnode can be truncated just + * after call to the jnode_build_key(), but this is ok, + * because coord_by_key() will just fail to find appropriate + * extent. + */ + LOCK_JNODE(node); + if (!JF_ISSET(node, JNODE_HEARD_BANSHEE)) { + jnode_build_key(node, &key); + ret = 0; + } else + ret = RETERR(-ENOENT); + UNLOCK_JNODE(node); + + if (ret != 0) + return ret; + + if (jnode_is_cluster_page(node)) + stop_level = LEAF_LEVEL; + + assert("jmacd-1812", coord != NULL); + + ret = coord_by_key(jnode_get_tree(node), &key, coord, parent_lh, + parent_mode, bias, stop_level, stop_level, CBK_UNIQUE, 0/*ra_info*/); + switch (ret) { + case CBK_COORD_NOTFOUND: + if (jnode_is_cluster_page(node)) { + int result; + assert("edward-164", jnode_page(node) != NULL); + assert("edward-165", jnode_page(node)->mapping != NULL); + assert("edward-166", jnode_page(node)->mapping->host != NULL); + assert("edward-167", inode_get_flag(jnode_page(node)->mapping->host, REISER4_CLUSTER_KNOWN)); + /* jnode of a new cluster which is not represented by any items in the tree. */ + result = incr_load_count_znode(parent_zh, parent_lh->node); + if (result != 0) + return result; + coord->between = AFTER_ITEM; + } else if (!JF_ISSET(node, JNODE_HEARD_BANSHEE)) { + warning("nikita-3177", "Parent not found"); + print_jnode("node", node); + } + return ret; + case CBK_COORD_FOUND: + if (coord->between != AT_UNIT) { + /* FIXME: comment needed */ + done_lh(parent_lh); + if (!JF_ISSET(node, JNODE_HEARD_BANSHEE)) { + warning("nikita-3178", + "Found but not happy: %i", + coord->between); + print_jnode("node", node); + } + return RETERR(-ENOENT); + } + ret = incr_load_count_znode(parent_zh, parent_lh->node); + if (ret != 0) + return ret; + /* if (jnode_is_cluster_page(node)) { + races with write() are possible + check_child_cluster (parent_lh->node); + } + */ + break; + default: + return ret; + } + + } else { + int flags; + znode *z; + + z = JZNODE(node); + /* Formatted node case: */ + assert("jmacd-2061", !znode_is_root(z)); + + flags = GN_ALLOW_NOT_CONNECTED; + if (try) + flags |= GN_TRY_LOCK; + + ret = reiser4_get_parent_flags(parent_lh, z, parent_mode, flags); + if (ret != 0) + /* -E_REPEAT is ok here, it is handled by the caller. */ + return ret; + + /* Make the child's position "hint" up-to-date. (Unless above + root, which caller must check.) */ + if (coord != NULL) { + + ret = incr_load_count_znode(parent_zh, parent_lh->node); + if (ret != 0) { + warning("jmacd-976812386", "incr_load_count_znode failed: %d", ret); + return ret; + } + + ret = find_child_ptr(parent_lh->node, z, coord); + if (ret != 0) { + warning("jmacd-976812", "find_child_ptr failed: %d", ret); + return ret; + } + } + } + + return 0; +} + +/* Get the (locked) next neighbor of a znode which is dirty and a member of the same atom. + If there is no next neighbor or the neighbor is not in memory or if there is a + neighbor but it is not dirty or not in the same atom, -E_NO_NEIGHBOR is returned. */ +static int +neighbor_in_slum( + + znode * node, /* starting point */ + + lock_handle * lock, /* lock on starting point */ + + sideof side, /* left or right direction we seek the next node in */ + + znode_lock_mode mode /* kind of lock we want */ + + ) +{ + int ret; + + assert("jmacd-6334", znode_is_connected(node)); + + ret = reiser4_get_neighbor(lock, node, mode, GN_SAME_ATOM | (side == LEFT_SIDE ? GN_GO_LEFT : 0)); + + if (ret) { + /* May return -ENOENT or -E_NO_NEIGHBOR. */ + /* FIXME(C): check EINVAL, E_DEADLOCK */ + if (ret == -ENOENT) { + ret = RETERR(-E_NO_NEIGHBOR); + } + + return ret; + } + + /* Check dirty bit of locked znode, no races here */ + if (znode_check_dirty(lock->node)) + return 0; + + done_lh(lock); + return RETERR(-E_NO_NEIGHBOR); +} + +/* Return true if two znodes have the same parent. This is called with both nodes + write-locked (for squeezing) so no tree lock is needed. */ +static int +znode_same_parents(znode * a, znode * b) +{ + assert("jmacd-7011", znode_is_write_locked(a)); + assert("jmacd-7012", znode_is_write_locked(b)); + + /* We lock the whole tree for this check.... I really don't like whole tree + * locks... -Hans */ + return UNDER_RW(tree, znode_get_tree(a), read, + (znode_parent(a) == znode_parent(b))); +} + +/* FLUSH SCAN */ + +/* Initialize the flush_scan data structure. */ +static void +scan_init(flush_scan * scan) +{ + memset(scan, 0, sizeof (*scan)); + init_lh(&scan->node_lock); + init_lh(&scan->parent_lock); + init_load_count(&scan->parent_load); + init_load_count(&scan->node_load); + coord_init_invalid(&scan->parent_coord, NULL); +} + +/* Release any resources held by the flush scan, e.g., release locks, free memory, etc. */ +static void +scan_done(flush_scan * scan) +{ + done_load_count(&scan->node_load); + if (scan->node != NULL) { + jput(scan->node); + scan->node = NULL; + } + done_load_count(&scan->parent_load); + done_lh(&scan->parent_lock); + done_lh(&scan->node_lock); +} + +/* Returns true if flush scanning is finished. */ +reiser4_internal int +scan_finished(flush_scan * scan) +{ + return scan->stop || (scan->direction == RIGHT_SIDE && + scan->count >= scan->max_count); +} + +/* Return true if the scan should continue to the @tonode. True if the node meets the + same_slum_check condition. If not, deref the "left" node and stop the scan. */ +reiser4_internal int +scan_goto(flush_scan * scan, jnode * tonode) +{ + int go = same_slum_check(scan->node, tonode, 1, 0); + + if (!go) { + scan->stop = 1; + ON_TRACE(TRACE_FLUSH_VERB, + "flush %s scan stop: stop at node %s\n", + scanning_left(scan) ? "left" : "right", jnode_tostring(scan->node)); + ON_TRACE(TRACE_FLUSH_VERB, + "flush %s scan stop: do not cont at %s\n", + scanning_left(scan) ? "left" : "right", jnode_tostring(tonode)); + jput(tonode); + } + + return go; +} + +/* Set the current scan->node, refcount it, increment count by the @add_count (number to + count, e.g., skipped unallocated nodes), deref previous current, and copy the current + parent coordinate. */ +reiser4_internal int +scan_set_current(flush_scan * scan, jnode * node, unsigned add_count, const coord_t * parent) +{ + /* Release the old references, take the new reference. */ + done_load_count(&scan->node_load); + + if (scan->node != NULL) { + jput(scan->node); + } + scan->node = node; + scan->count += add_count; + + /* This next stmt is somewhat inefficient. The scan_extent_coord code could + delay this update step until it finishes and update the parent_coord only once. + It did that before, but there was a bug and this was the easiest way to make it + correct. */ + if (parent != NULL) { + coord_dup(&scan->parent_coord, parent); + } + + /* Failure may happen at the incr_load_count call, but the caller can assume the reference + is safely taken. */ + return incr_load_count_jnode(&scan->node_load, node); +} + +/* Return true if scanning in the leftward direction. */ +reiser4_internal int +scanning_left(flush_scan * scan) +{ + return scan->direction == LEFT_SIDE; +} + +/* Performs leftward scanning starting from either kind of node. Counts the starting + node. The right-scan object is passed in for the left-scan in order to copy the parent + of an unformatted starting position. This way we avoid searching for the unformatted + node's parent when scanning in each direction. If we search for the parent once it is + set in both scan objects. The limit parameter tells flush-scan when to stop. + + Rapid scanning is used only during scan_left, where we are interested in finding the + 'leftpoint' where we begin flushing. We are interested in stopping at the left child + of a twig that does not have a dirty left neighbor. THIS IS A SPECIAL CASE. The + problem is finding a way to flush only those nodes without unallocated children, and it + is difficult to solve in the bottom-up flushing algorithm we are currently using. The + problem can be solved by scanning left at every level as we go upward, but this would + basically bring us back to using a top-down allocation strategy, which we already tried + (see BK history from May 2002), and has a different set of problems. The top-down + strategy makes avoiding unallocated children easier, but makes it difficult to + propertly flush dirty children with clean parents that would otherwise stop the + top-down flush, only later to dirty the parent once the children are flushed. So we + solve the problem in the bottom-up algorithm with a special case for twigs and leaves + only. + + The first step in solving the problem is this rapid leftward scan. After we determine + that there are at least enough nodes counted to qualify for FLUSH_RELOCATE_THRESHOLD we + are no longer interested in the exact count, we are only interested in finding a the + best place to start the flush. We could choose one of two possibilities: + + 1. Stop at the leftmost child (of a twig) that does not have a dirty left neighbor. + This requires checking one leaf per rapid-scan twig + + 2. Stop at the leftmost child (of a twig) where there are no dirty children of the twig + to the left. This requires checking possibly all of the in-memory children of each + twig during the rapid scan. + + For now we implement the first policy. +*/ +static int +scan_left(flush_scan * scan, flush_scan * right, jnode * node, unsigned limit) +{ + int ret = 0; + + scan->max_count = limit; + scan->direction = LEFT_SIDE; + + ret = scan_set_current(scan, jref(node), 1, NULL); + if (ret != 0) { + return ret; + } + + ret = scan_common(scan, right); + if (ret != 0) { + return ret; + } + + /* Before rapid scanning, we need a lock on scan->node so that we can get its + parent, only if formatted. */ + if (jnode_is_znode(scan->node)) { + ret = longterm_lock_znode(&scan->node_lock, JZNODE(scan->node), + ZNODE_WRITE_LOCK, ZNODE_LOCK_LOPRI); + } + + /* Rapid_scan would go here (with limit set to FLUSH_RELOCATE_THRESHOLD). */ + return ret; +} + +/* Performs rightward scanning... Does not count the starting node. The limit parameter + is described in scan_left. If the starting node is unformatted then the + parent_coord was already set during scan_left. The rapid_after parameter is not used + during right-scanning. + + scan_right is only called if the scan_left operation does not count at least + FLUSH_RELOCATE_THRESHOLD nodes for flushing. Otherwise, the limit parameter is set to + the difference between scan-left's count and FLUSH_RELOCATE_THRESHOLD, meaning + scan-right counts as high as FLUSH_RELOCATE_THRESHOLD and then stops. */ +static int +scan_right(flush_scan * scan, jnode * node, unsigned limit) +{ + int ret; + + scan->max_count = limit; + scan->direction = RIGHT_SIDE; + + ret = scan_set_current(scan, jref(node), 0, NULL); + if (ret != 0) { + return ret; + } + + return scan_common(scan, NULL); +} + +/* Common code to perform left or right scanning. */ +static int +scan_common(flush_scan * scan, flush_scan * other) +{ + int ret; + + assert("nikita-2376", scan->node != NULL); + assert("edward-54", jnode_is_unformatted(scan->node) || jnode_is_znode(scan->node)); + + /* Special case for starting at an unformatted node. Optimization: we only want + to search for the parent (which requires a tree traversal) once. Obviously, we + shouldn't have to call it once for the left scan and once for the right scan. + For this reason, if we search for the parent during scan-left we then duplicate + the coord/lock/load into the scan-right object. */ + if (jnode_is_unformatted(scan->node)) { + ret = scan_unformatted(scan, other); + if (ret != 0) + return ret; + } + /* This loop expects to start at a formatted position and performs chaining of + formatted regions */ + while (!scan_finished(scan)) { + + ret = scan_formatted(scan); + if (ret != 0) { + return ret; + } + } + + return 0; +} + +/* called by scan_unformatted() when jnode_lock_parent_coord + returns COORD_NOT_FOUND. +*/ +static int +scan_should_link_node(flush_scan * scan) +{ + assert("edward-311", scan->node != NULL); + if (jnode_is_cluster_page(scan->node)) { + + assert("edward-303", scan->parent_coord.between != EMPTY_NODE); + return 1; + } + return 0; +} + +static int +scan_unformatted(flush_scan * scan, flush_scan * other) +{ + int ret = 0; + int try = 0; + + if (!coord_is_invalid(&scan->parent_coord)) + goto scan; + + /* set parent coord from */ + if (!jnode_is_unformatted(scan->node)) { + /* formatted position*/ + + lock_handle lock; + assert("edward-301", jnode_is_znode(scan->node)); + init_lh(&lock); + + /* + * when flush starts from unformatted node, first thing it + * does is tree traversal to find formatted parent of starting + * node. This parent is then kept lock across scans to the + * left and to the right. This means that during scan to the + * left we cannot take left-ward lock, because this is + * dead-lock prone. So, if we are scanning to the left and + * there is already lock held by this thread, + * jnode_lock_parent_coord() should use try-lock. + */ + try = scanning_left(scan) && !lock_stack_isclean(get_current_lock_stack()); + /* Need the node locked to get the parent lock, We have to + take write lock since there is at least one call path + where this znode is already write-locked by us. */ + ret = longterm_lock_znode(&lock, JZNODE(scan->node), ZNODE_WRITE_LOCK, + scanning_left(scan) ? ZNODE_LOCK_LOPRI : ZNODE_LOCK_HIPRI); + if (ret != 0) + /* EINVAL or E_DEADLOCK here mean... try again! At this point we've + scanned too far and can't back out, just start over. */ + return ret; + + ret = jnode_lock_parent_coord(scan->node, + &scan->parent_coord, + &scan->parent_lock, + &scan->parent_load, + ZNODE_WRITE_LOCK, try); + + /* FIXME(C): check EINVAL, E_DEADLOCK */ + done_lh(&lock); + if (ret == -E_REPEAT) { + scan->stop = 1; + return 0; + } + if (ret) + return ret; + + } else { + /* unformatted position */ + + ret = jnode_lock_parent_coord(scan->node, &scan->parent_coord, &scan->parent_lock, + &scan->parent_load, ZNODE_WRITE_LOCK, try); + + if (IS_CBKERR(ret)) + return ret; + + if (ret == CBK_COORD_NOTFOUND) { + /* FIXME(C): check EINVAL, E_DEADLOCK */ + ON_TRACE(TRACE_FLUSH, + "flush_scan_common: jnode_lock_parent_coord returned %d\n", ret); + if (!scan_should_link_node(scan)) + return ret; + } + else { + /* parent was found */ + set_flush_scan_nstat(scan, LINKED); + ON_TRACE(TRACE_FLUSH, + "flush_scan_common: jnode_lock_parent_coord returned 0\n"); + assert("jmacd-8661", other != NULL); + } + + /* Duplicate the reference into the other flush_scan. */ + coord_dup(&other->parent_coord, &scan->parent_coord); + copy_lh(&other->parent_lock, &scan->parent_lock); + copy_load_count(&other->parent_load, &scan->parent_load); + set_flush_scan_nstat(other, scan->nstat); + } + scan: + return scan_by_coord(scan); +} + +/* Performs left- or rightward scanning starting from a formatted node. Follow left + pointers under tree lock as long as: + + - node->left/right is non-NULL + - node->left/right is connected, dirty + - node->left/right belongs to the same atom + - scan has not reached maximum count +*/ +static int +scan_formatted(flush_scan * scan) +{ + int ret; + znode *neighbor = NULL; + + assert("jmacd-1401", !scan_finished(scan)); + + do { + znode *node = JZNODE(scan->node); + + /* Node should be connected, but if not stop the scan. */ + if (!znode_is_connected(node)) { + scan->stop = 1; + break; + } + + /* Lock the tree, check-for and reference the next sibling. */ + RLOCK_TREE(znode_get_tree(node)); + + /* It may be that a node is inserted or removed between a node and its + left sibling while the tree lock is released, but the flush-scan count + does not need to be precise. Thus, we release the tree lock as soon as + we get the neighboring node. */ + neighbor = scanning_left(scan) ? node->left : node->right; + if (neighbor != NULL) { + zref(neighbor); + } + + RUNLOCK_TREE(znode_get_tree(node)); + + /* If neighbor is NULL at the leaf level, need to check for an unformatted + sibling using the parent--break in any case. */ + if (neighbor == NULL) { + break; + } + + ON_TRACE(TRACE_FLUSH_VERB, "format scan %s %s\n", + scanning_left(scan) ? "left" : "right", znode_tostring(neighbor)); + + /* Check the condition for going left, break if it is not met. This also + releases (jputs) the neighbor if false. */ + if (!scan_goto(scan, ZJNODE(neighbor))) { + break; + } + + /* Advance the flush_scan state to the left, repeat. */ + ret = scan_set_current(scan, ZJNODE(neighbor), 1, NULL); + if (ret != 0) { + return ret; + } + + } while (!scan_finished(scan)); + + /* If neighbor is NULL then we reached the end of a formatted region, or else the + sibling is out of memory, now check for an extent to the left (as long as + LEAF_LEVEL). */ + if (neighbor != NULL || jnode_get_level(scan->node) != LEAF_LEVEL || scan_finished(scan)) { + scan->stop = 1; + return 0; + } + /* Otherwise, calls scan_by_coord for the right(left)most item of the + left(right) neighbor on the parent level, then possibly continue. */ + + coord_init_invalid(&scan->parent_coord, NULL); + return scan_unformatted(scan, NULL); +} + +/* NOTE-EDWARD: + This scans adjacent items of the same type and calls scan flush plugin for each one. + Performs left(right)ward scanning starting from a (possibly) unformatted node. If we start + from unformatted node, then we continue only if the next neighbor is also unformatted. + When called from scan_formatted, we skip first iteration (to make sure that + right(left)most item of the left(right) neighbor on the parent level is of the same + type and set appropriate coord). */ +static int +scan_by_coord(flush_scan * scan) +{ + int ret = 0; + int scan_this_coord; + lock_handle next_lock; + load_count next_load; + coord_t next_coord; + jnode *child; + item_plugin *iplug; + + init_lh(&next_lock); + init_load_count(&next_load); + scan_this_coord = (jnode_is_unformatted(scan->node) ? 1 : 0); + + /* set initial item id */ + if (get_flush_scan_nstat(scan) == UNLINKED) + iplug = item_plugin_by_jnode(scan->node); + else + iplug = item_plugin_by_coord(&scan->parent_coord); + + for (; !scan_finished(scan); scan_this_coord = 1) { + if (scan_this_coord) { + /* Here we expect that unit is scannable. it would not be so due + * to race with extent->tail conversion. */ + if (iplug->f.scan == NULL) { + scan->stop = 1; + ret = -E_REPEAT; + /* skip the check at the end. */ + goto race; + } + + ret = iplug->f.scan(scan); + if (ret != 0) + goto exit; + + if (scan_finished(scan)) { + checkchild(scan); + break; + } + } else { + /* the same race against truncate as above is possible + * here, it seems */ + + /* NOTE-JMACD: In this case, apply the same end-of-node logic but don't scan + the first coordinate. */ + assert("jmacd-1231", item_is_internal(&scan->parent_coord)); + } + + if(iplug->f.utmost_child == NULL || znode_get_level(scan->parent_coord.node) != TWIG_LEVEL) { + /* stop this coord and continue on parrent level */ + ret = scan_set_current(scan, ZJNODE(zref(scan->parent_coord.node)), 1, NULL); + if (ret != 0) + goto exit; + break; + } + + /* Either way, the invariant is that scan->parent_coord is set to the + parent of scan->node. Now get the next unit. */ + coord_dup(&next_coord, &scan->parent_coord); + coord_sideof_unit(&next_coord, scan->direction); + + /* If off-the-end of the twig, try the next twig. */ + if (coord_is_after_sideof_unit(&next_coord, scan->direction)) { + /* We take the write lock because we may start flushing from this + * coordinate. */ + ret = neighbor_in_slum(next_coord.node, &next_lock, scan->direction, ZNODE_WRITE_LOCK); + + if (ret == -E_NO_NEIGHBOR) { + scan->stop = 1; + ret = 0; + break; + } + + if (ret != 0) { + goto exit; + } + + ret = incr_load_count_znode(&next_load, next_lock.node); + if (ret != 0) { + goto exit; + } + + coord_init_sideof_unit(&next_coord, next_lock.node, sideof_reverse(scan->direction)); + } + + iplug = item_plugin_by_coord(&next_coord); + + /* Get the next child. */ + ret = iplug->f.utmost_child(&next_coord, sideof_reverse(scan->direction), &child); + if (ret != 0) + goto exit; + /* If the next child is not in memory, or, item_utmost_child + failed (due to race with unlink, most probably), stop + here. */ + if (child == NULL || IS_ERR(child)) { + scan->stop = 1; + checkchild(scan); + break; + } + + assert("nikita-2374", jnode_is_unformatted(child) || jnode_is_znode(child)); + + /* See if it is dirty, part of the same atom. */ + if (!scan_goto(scan, child)) { + checkchild(scan); + break; + } + + /* If so, make this child current. */ + ret = scan_set_current(scan, child, 1, &next_coord); + if (ret != 0) + goto exit; + + /* Now continue. If formatted we release the parent lock and return, then + proceed. */ + if (jnode_is_znode(child)) + break; + + /* Otherwise, repeat the above loop with next_coord. */ + if (next_load.node != NULL) { + done_lh(&scan->parent_lock); + move_lh(&scan->parent_lock, &next_lock); + move_load_count(&scan->parent_load, &next_load); + } + } + + assert("jmacd-6233", scan_finished(scan) || jnode_is_znode(scan->node)); + exit: + checkchild(scan); + race: /* skip the above check */ + if (jnode_is_znode(scan->node)) { + done_lh(&scan->parent_lock); + done_load_count(&scan->parent_load); + } + + done_load_count(&next_load); + done_lh(&next_lock); + return ret; +} + +/* FLUSH POS HELPERS */ + +/* Initialize the fields of a flush_position. */ +static void +pos_init(flush_pos_t * pos) +{ + xmemset(pos, 0, sizeof *pos); + + pos->state = POS_INVALID; + coord_init_invalid(&pos->coord, NULL); + init_lh(&pos->lock); + init_load_count(&pos->load); + + blocknr_hint_init(&pos->preceder); +} + +/* The flush loop inside squalloc periodically checks pos_valid to + determine when "enough flushing" has been performed. This will return true until one + of the following conditions is met: + + 1. the number of flush-queued nodes has reached the kernel-supplied "int *nr_to_flush" + parameter, meaning we have flushed as many blocks as the kernel requested. When + flushing to commit, this parameter is NULL. + + 2. pos_stop() is called because squalloc discovers that the "next" node in the + flush order is either non-existant, not dirty, or not in the same atom. +*/ + + +static int pos_valid (flush_pos_t * pos) +{ + return pos->state != POS_INVALID; +} + +/* Release any resources of a flush_position. Called when jnode_flush finishes. */ +static void +pos_done(flush_pos_t * pos) +{ + pos_stop(pos); + blocknr_hint_done(&pos->preceder); + if (pos->sq) + free_squeeze_data(pos); +} + +/* Reset the point and parent. Called during flush subroutines to terminate the + squalloc loop. */ +static int +pos_stop(flush_pos_t * pos) +{ + pos->state = POS_INVALID; + done_lh(&pos->lock); + done_load_count(&pos->load); + coord_init_invalid(&pos->coord, NULL); + + if (pos->child) { + jput(pos->child); + pos->child = NULL; + } + + return 0; +} + +/* Return the flush_position's block allocator hint. */ +reiser4_internal reiser4_blocknr_hint * +pos_hint(flush_pos_t * pos) +{ + return &pos->preceder; +} + +/* Return true if we have decided to unconditionally relocate leaf nodes, thus write + optimizing. */ +reiser4_internal int +pos_leaf_relocate(flush_pos_t * pos) +{ + return pos->leaf_relocate; +} + +reiser4_internal flush_queue_t * pos_fq(flush_pos_t * pos) +{ + return pos->fq; +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 90 + LocalWords: preceder + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/flush.h 2004-09-03 00:57:05.060269984 -0700 @@ -0,0 +1,240 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* DECLARATIONS: */ + +#if !defined(__REISER4_FLUSH_H__) +#define __REISER4_FLUSH_H__ + +#include "plugin/item/ctail.h" /* for ctail scan/squeeze info */ + +typedef enum { + UNLINKED = 0, + LINKED = 1 +} flush_scan_node_stat_t; + +/* The flush_scan data structure maintains the state of an in-progress flush-scan on a + single level of the tree. A flush-scan is used for counting the number of adjacent + nodes to flush, which is used to determine whether we should relocate, and it is also + used to find a starting point for flush. A flush-scan object can scan in both right + and left directions via the scan_left() and scan_right() interfaces. The + right- and left-variations are similar but perform different functions. When scanning + left we (optionally perform rapid scanning and then) longterm-lock the endpoint node. + When scanning right we are simply counting the number of adjacent, dirty nodes. */ +struct flush_scan { + + /* The current number of nodes scanned on this level. */ + unsigned count; + + /* There may be a maximum number of nodes for a scan on any single level. When + going leftward, max_count is determined by FLUSH_SCAN_MAXNODES (see reiser4.h) */ + unsigned max_count; + + /* Direction: Set to one of the sideof enumeration: { LEFT_SIDE, RIGHT_SIDE }. */ + sideof direction; + + /* Initially @stop is set to false then set true once some condition stops the + search (e.g., we found a clean node before reaching max_count or we found a + node belonging to another atom). */ + int stop; + + /* The current scan position. If @node is non-NULL then its reference count has + been incremented to reflect this reference. */ + jnode *node; + + /* node specific linkage status. This indicates if the node that flush + * started from is linked to the tree (like formatted nodes, extent's jnodes), + * or not (like jnodes of newly created cluster of cryptcompressed file. + * If (nstat == UNLINKED) we don't do right scan. Also we use this status in + * scan_by_coord() to assign item plugin */ + flush_scan_node_stat_t nstat; + + /* A handle for zload/zrelse of current scan position node. */ + load_count node_load; + + /* During left-scan, if the final position (a.k.a. endpoint node) is formatted the + node is locked using this lock handle. The endpoint needs to be locked for + transfer to the flush_position object after scanning finishes. */ + lock_handle node_lock; + + /* When the position is unformatted, its parent, coordinate, and parent + zload/zrelse handle. */ + lock_handle parent_lock; + coord_t parent_coord; + load_count parent_load; + + /* The block allocator preceder hint. Sometimes flush_scan determines what the + preceder is and if so it sets it here, after which it is copied into the + flush_position. Otherwise, the preceder is computed later. */ + reiser4_block_nr preceder_blk; +}; + +static inline flush_scan_node_stat_t +get_flush_scan_nstat(flush_scan * scan) + +{ + return scan->nstat; +} + +static inline void +set_flush_scan_nstat(flush_scan * scan, flush_scan_node_stat_t nstat) +{ + scan->nstat = nstat; +} + +typedef struct squeeze_item_info { + int mergeable; + union { + ctail_squeeze_info_t ctail_info; + } u; +} squeeze_item_info_t; + +typedef struct squeeze_info { + int count; /* for squalloc terminating */ + tfm_info_t * tfm; /* transform info */ + item_plugin * iplug; /* current item plugin */ + squeeze_item_info_t * itm; /* current item info */ +} squeeze_info_t; + +typedef enum flush_position_state { + POS_INVALID, /* Invalid or stopped pos, do not continue slum + * processing */ + POS_ON_LEAF, /* pos points to already prepped, locked formatted node at + * leaf level */ + POS_ON_EPOINT, /* pos keeps a lock on twig level, "coord" field is used + * to traverse unformatted nodes */ + POS_TO_LEAF, /* pos is being moved to leaf level */ + POS_TO_TWIG, /* pos is being moved to twig level */ + POS_END_OF_TWIG, /* special case of POS_ON_TWIG, when coord is after + * rightmost unit of the current twig */ + POS_ON_INTERNAL /* same as POS_ON_LEAF, but points to internal node */ + +} flushpos_state_t; + + + +/* An encapsulation of the current flush point and all the parameters that are passed + through the entire squeeze-and-allocate stage of the flush routine. A single + flush_position object is constructed after left- and right-scanning finishes. */ +struct flush_position { + flushpos_state_t state; + + coord_t coord; /* coord to traverse unformatted nodes */ + lock_handle lock; /* current lock we hold */ + load_count load; /* load status for current locked formatted node */ + + jnode * child; /* for passing a reference to unformatted child + * across pos state changes */ + + reiser4_blocknr_hint preceder; /* The flush 'hint' state. */ + int leaf_relocate; /* True if enough leaf-level nodes were + * found to suggest a relocate policy. */ + long *nr_to_flush; /* If called under memory pressure, + * indicates how many nodes the VM asked to flush. */ + int alloc_cnt; /* The number of nodes allocated during squeeze and allococate. */ + int prep_or_free_cnt; /* The number of nodes prepared for write (allocate) or squeezed and freed. */ + flush_queue_t *fq; + long *nr_written; /* number of nodes submitted to disk */ + int flags; /* a copy of jnode_flush flags argument */ + + znode * prev_twig; /* previous parent pointer value, used to catch + * processing of new twig node */ + squeeze_info_t * sq; /* squeeze info */ + + unsigned long pos_in_unit; /* for extents only. Position + within an extent unit of first + jnode of slum */ +}; + +static inline int +item_squeeze_count (flush_pos_t * pos) +{ + return pos->sq->count; +} +static inline void +inc_item_squeeze_count (flush_pos_t * pos) +{ + pos->sq->count++; +} +static inline void +set_item_squeeze_count (flush_pos_t * pos, int count) +{ + pos->sq->count = count; +} +static inline item_plugin * +item_squeeze_plug (flush_pos_t * pos) +{ + return pos->sq->iplug; +} + +static inline squeeze_item_info_t * +item_squeeze_data (flush_pos_t * pos) +{ + return pos->sq->itm; +} + +static inline tfm_info_t * +tfm_squeeze_data (flush_pos_t * pos) +{ + return pos->sq->tfm; +} + +static inline tfm_info_t * +tfm_squeeze_idx (flush_pos_t * pos, reiser4_compression_id idx) +{ + return &pos->sq->tfm[idx]; +} + +static inline tfm_info_t +tfm_squeeze_pos (flush_pos_t * pos, reiser4_compression_id idx) +{ + return (tfm_squeeze_data(pos) ? *tfm_squeeze_idx(pos, idx) : 0); +} + +#define SQUALLOC_THRESHOLD 256 /* meaningful for ctails */ + +static inline int +should_terminate_squalloc(flush_pos_t * pos) +{ + return pos->sq && !item_squeeze_data(pos) && pos->sq->count >= SQUALLOC_THRESHOLD; +} + +void free_squeeze_data(flush_pos_t * pos); +/* used in extent.c */ +int scan_set_current(flush_scan * scan, jnode * node, unsigned add_size, const coord_t * parent); +int scan_finished(flush_scan * scan); +int scanning_left(flush_scan * scan); +int scan_goto(flush_scan * scan, jnode * tonode); +txn_atom *atom_locked_by_fq(flush_queue_t * fq); + +int init_fqs(void); +void done_fqs(void); + +#if REISER4_TRACE +const char *jnode_tostring(jnode * node); +#else +#define jnode_tostring(n) "" +#endif + +#if REISER4_DEBUG +#define check_preceder(blk) \ +assert("nikita-2588", blk < reiser4_block_count(reiser4_get_current_sb())); + +extern void check_pos(flush_pos_t *pos); +#else +#define check_preceder(b) noop +#define check_pos(pos) noop +#endif + +/* __REISER4_FLUSH_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 90 + LocalWords: preceder + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/flush_queue.c 2004-09-03 00:57:05.063269528 -0700 @@ -0,0 +1,758 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#include "debug.h" +#include "type_safe_list.h" +#include "super.h" +#include "txnmgr.h" +#include "jnode.h" +#include "znode.h" +#include "page_cache.h" +#include "wander.h" +#include "vfs_ops.h" +#include "writeout.h" + +#include +#include +#include +#include +#include + +/* A flush queue object is an accumulator for keeping jnodes prepared + by the jnode_flush() function for writing to disk. Those "queued" jnodes are + kept on the flush queue until memory pressure or atom commit asks + flush queues to write some or all from their jnodes. */ + +TYPE_SAFE_LIST_DEFINE(fq, flush_queue_t, alink); + +#if REISER4_DEBUG +# define spin_ordering_pred_fq(fq) (1) +#endif + +SPIN_LOCK_FUNCTIONS(fq, flush_queue_t, guard); + +/* + LOCKING: + + fq->guard spin lock protects fq->atom pointer and nothing else. fq->prepped + list protected by atom spin lock. fq->prepped list uses the following + locking: + + two ways to protect fq->prepped list for read-only list traversal: + + 1. atom spin-lock atom. + 2. fq is IN_USE, atom->nr_running_queues increased. + + and one for list modification: + + 1. atom is spin-locked and one condition is true: fq is IN_USE or + atom->nr_running_queues == 0. + + The deadlock-safe order for flush queues and atoms is: first lock atom, then + lock flush queue, then lock jnode. +*/ + +#define fq_in_use(fq) ((fq)->state & FQ_IN_USE) +#define fq_ready(fq) (!fq_in_use(fq)) + +#define mark_fq_in_use(fq) do { (fq)->state |= FQ_IN_USE; } while (0) +#define mark_fq_ready(fq) do { (fq)->state &= ~FQ_IN_USE; } while (0) + +/* get lock on atom from locked flush queue object */ +reiser4_internal txn_atom * +atom_get_locked_by_fq(flush_queue_t * fq) +{ + /* This code is similar to jnode_get_atom(), look at it for the + * explanation. */ + txn_atom *atom; + + assert("zam-729", spin_fq_is_locked(fq)); + + while(1) { + atom = fq->atom; + if (atom == NULL) + break; + + if (spin_trylock_atom(atom)) + break; + + atomic_inc(&atom->refcount); + spin_unlock_fq(fq); + LOCK_ATOM(atom); + spin_lock_fq(fq); + + if (fq->atom == atom) { + atomic_dec(&atom->refcount); + break; + } + + spin_unlock_fq(fq); + atom_dec_and_unlock(atom); + spin_lock_fq(fq); + } + + return atom; +} + +reiser4_internal txn_atom * +atom_locked_by_fq(flush_queue_t * fq) +{ + return UNDER_SPIN(fq, fq, atom_get_locked_by_fq(fq)); +} + +static void +init_fq(flush_queue_t * fq) +{ + xmemset(fq, 0, sizeof *fq); + + atomic_set(&fq->nr_submitted, 0); + + capture_list_init(ATOM_FQ_LIST(fq)); + + sema_init(&fq->io_sem, 0); + spin_fq_init(fq); +} + +/* slab for flush queues */ +static kmem_cache_t *fq_slab; + +reiser4_internal int init_fqs(void) +{ + fq_slab = kmem_cache_create("fq", + sizeof (flush_queue_t), + 0, + SLAB_HWCACHE_ALIGN, + NULL, + NULL); + return (fq_slab == NULL) ? RETERR(-ENOMEM) : 0; +} + +reiser4_internal void done_fqs(void) +{ + kmem_cache_destroy(fq_slab); +} + +/* create new flush queue object */ +static flush_queue_t * +create_fq(int gfp) +{ + flush_queue_t *fq; + + fq = kmem_cache_alloc(fq_slab, gfp); + if (fq) + init_fq(fq); + + return fq; +} + +/* adjust atom's and flush queue's counters of queued nodes */ +static void +count_enqueued_node(flush_queue_t * fq) +{ + ON_DEBUG(fq->atom->num_queued++); +} + +static void +count_dequeued_node(flush_queue_t * fq) +{ + assert("zam-993", fq->atom->num_queued > 0); + ON_DEBUG(fq->atom->num_queued--); +} + +/* attach flush queue object to the atom */ +static void +attach_fq(txn_atom * atom, flush_queue_t * fq) +{ + assert("zam-718", spin_atom_is_locked(atom)); + fq_list_push_front(&atom->flush_queues, fq); + fq->atom = atom; + ON_DEBUG(atom->nr_flush_queues++); +} + +static void +detach_fq(flush_queue_t * fq) +{ + assert("zam-731", spin_atom_is_locked(fq->atom)); + + spin_lock_fq(fq); + fq_list_remove_clean(fq); + assert("vs-1456", fq->atom->nr_flush_queues > 0); + ON_DEBUG(fq->atom->nr_flush_queues--); + fq->atom = NULL; + spin_unlock_fq(fq); +} + +/* destroy flush queue object */ +reiser4_internal void +done_fq(flush_queue_t * fq) +{ + assert("zam-763", capture_list_empty(ATOM_FQ_LIST(fq))); + assert("zam-766", atomic_read(&fq->nr_submitted) == 0); + + kmem_cache_free(fq_slab, fq); +} + +/* */ +reiser4_internal void +mark_jnode_queued(flush_queue_t *fq, jnode *node) +{ + JF_SET(node, JNODE_FLUSH_QUEUED); + count_enqueued_node(fq); +} + +/* Putting jnode into the flush queue. Both atom and jnode should be + spin-locked. */ +reiser4_internal void +queue_jnode(flush_queue_t * fq, jnode * node) +{ + assert("zam-711", spin_jnode_is_locked(node)); + assert("zam-713", node->atom != NULL); + assert("zam-712", spin_atom_is_locked(node->atom)); + assert("zam-714", jnode_is_dirty(node)); + assert("zam-716", fq->atom != NULL); + assert("zam-717", fq->atom == node->atom); + assert("zam-907", fq_in_use(fq)); + + assert("zam-826", JF_ISSET(node, JNODE_RELOC)); + assert("vs-1481", !JF_ISSET(node, JNODE_FLUSH_QUEUED)); + assert("vs-1481", NODE_LIST(node) != FQ_LIST); + + mark_jnode_queued(fq, node); + capture_list_remove_clean(node); + capture_list_push_back(ATOM_FQ_LIST(fq), node); + /*XXXX*/ON_DEBUG(count_jnode(node->atom, node, NODE_LIST(node), FQ_LIST, 1)); +} + +/* repeatable process for waiting io completion on a flush queue object */ +static int +wait_io(flush_queue_t * fq, int *nr_io_errors) +{ + assert("zam-738", fq->atom != NULL); + assert("zam-739", spin_atom_is_locked(fq->atom)); + assert("zam-736", fq_in_use(fq)); + assert("zam-911", capture_list_empty(ATOM_FQ_LIST(fq))); + + if (atomic_read(&fq->nr_submitted) != 0) { + struct super_block *super; + + UNLOCK_ATOM(fq->atom); + + assert("nikita-3013", schedulable()); + + super = reiser4_get_current_sb(); + + /* FIXME: this is instead of blk_run_queues() */ + blk_run_address_space(get_super_fake(super)->i_mapping); + + if ( !(super->s_flags & MS_RDONLY) ) + down(&fq->io_sem); + + /* Ask the caller to re-acquire the locks and call this + function again. Note: this technique is commonly used in + the txnmgr code. */ + return -E_REPEAT; + } + + *nr_io_errors += atomic_read(&fq->nr_errors); + return 0; +} + +/* wait on I/O completion, re-submit dirty nodes to write */ +static int +finish_fq(flush_queue_t * fq, int *nr_io_errors) +{ + int ret; + txn_atom * atom = fq->atom; + + assert("zam-801", atom != NULL); + assert("zam-744", spin_atom_is_locked(atom)); + assert("zam-762", fq_in_use(fq)); + + ret = wait_io(fq, nr_io_errors); + if (ret) + return ret; + + detach_fq(fq); + done_fq(fq); + + atom_send_event(atom); + + return 0; +} + +/* wait for all i/o for given atom to be completed, actually do one iteration + on that and return -E_REPEAT if there more iterations needed */ +static int +finish_all_fq(txn_atom * atom, int *nr_io_errors) +{ + flush_queue_t *fq; + + assert("zam-730", spin_atom_is_locked(atom)); + + if (fq_list_empty(&atom->flush_queues)) + return 0; + + for_all_type_safe_list(fq, &atom->flush_queues, fq) { + if (fq_ready(fq)) { + int ret; + + mark_fq_in_use(fq); + assert("vs-1247", fq->owner == NULL); + ON_DEBUG(fq->owner = current); + ret = finish_fq(fq, nr_io_errors); + + if ( *nr_io_errors ) + reiser4_handle_error(); + + if (ret) { + fq_put(fq); + return ret; + } + + UNLOCK_ATOM(atom); + + return -E_REPEAT; + } + } + + /* All flush queues are in use; atom remains locked */ + return -EBUSY; +} + +/* wait all i/o for current atom */ +reiser4_internal int +current_atom_finish_all_fq(void) +{ + txn_atom *atom; + int nr_io_errors = 0; + int ret = 0; + + do { + while (1) { + atom = get_current_atom_locked(); + ret = finish_all_fq(atom, &nr_io_errors); + if (ret != -EBUSY) + break; + atom_wait_event(atom); + } + } while (ret == -E_REPEAT); + + /* we do not need locked atom after this function finishes, SUCCESS or + -EBUSY are two return codes when atom remains locked after + finish_all_fq */ + if (!ret) + UNLOCK_ATOM(atom); + + assert("nikita-2696", spin_atom_is_not_locked(atom)); + + if (ret) + return ret; + + if (nr_io_errors) + return RETERR(-EIO); + + return 0; +} + +/* change node->atom field for all jnode from given list */ +static void +scan_fq_and_update_atom_ref(capture_list_head * list, txn_atom * atom) +{ + jnode *cur; + + for_all_type_safe_list(capture, list, cur) { + LOCK_JNODE(cur); + cur->atom = atom; + UNLOCK_JNODE(cur); + } +} + +/* support for atom fusion operation */ +reiser4_internal void +fuse_fq(txn_atom * to, txn_atom * from) +{ + flush_queue_t *fq; + + assert("zam-720", spin_atom_is_locked(to)); + assert("zam-721", spin_atom_is_locked(from)); + + + for_all_type_safe_list(fq, &from->flush_queues, fq) { + scan_fq_and_update_atom_ref(ATOM_FQ_LIST(fq), to); + spin_lock_fq(fq); + fq->atom = to; + spin_unlock_fq(fq); + } + + fq_list_splice(&to->flush_queues, &from->flush_queues); + +#if REISER4_DEBUG + to->num_queued += from->num_queued; + to->nr_flush_queues += from->nr_flush_queues; + from->nr_flush_queues = 0; +#endif +} + +#if REISER4_DEBUG +int atom_fq_parts_are_clean (txn_atom * atom) +{ + assert("zam-915", atom != NULL); + return fq_list_empty(&atom->flush_queues); +} +#endif +/* Bio i/o completion routine for reiser4 write operations. */ +static int +end_io_handler(struct bio *bio, unsigned int bytes_done UNUSED_ARG, int err UNUSED_ARG) +{ + int i; + int nr_errors = 0; + flush_queue_t *fq; + + assert ("zam-958", bio->bi_rw & WRITE); + + /* i/o op. is not fully completed */ + if (bio->bi_size != 0) + return 1; + + /* we expect that bio->private is set to NULL or fq object which is used + * for synchronization and error counting. */ + fq = bio->bi_private; + /* Check all elements of io_vec for correct write completion. */ + for (i = 0; i < bio->bi_vcnt; i += 1) { + struct page *pg = bio->bi_io_vec[i].bv_page; + + if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) { + SetPageError(pg); + nr_errors++; + } + + { + /* jnode WRITEBACK ("write is in progress bit") is + * atomically cleared here. */ + jnode *node; + + assert("zam-736", pg != NULL); + assert("zam-736", PagePrivate(pg)); + node = (jnode *) (pg->private); + + JF_CLR(node, JNODE_WRITEBACK); + } + + end_page_writeback(pg); + page_cache_release(pg); + } + + if (fq) { + /* count i/o error in fq object */ + atomic_add(nr_errors, &fq->nr_errors); + + /* If all write requests registered in this "fq" are done we up + * the semaphore. */ + if (atomic_sub_and_test(bio->bi_vcnt, &fq->nr_submitted)) + up(&fq->io_sem); + } + + bio_put(bio); + return 0; +} + +/* Count I/O requests which will be submitted by @bio in given flush queues + @fq */ +reiser4_internal void +add_fq_to_bio(flush_queue_t * fq, struct bio *bio) +{ + bio->bi_private = fq; + bio->bi_end_io = end_io_handler; + + if (fq) + atomic_add(bio->bi_vcnt, &fq->nr_submitted); +} + +/* Move all queued nodes out from @fq->prepped list. */ +static void release_prepped_list(flush_queue_t * fq) +{ + txn_atom * atom; + + assert ("zam-904", fq_in_use(fq)); + atom = UNDER_SPIN(fq, fq, atom_get_locked_by_fq(fq)); + + while(!capture_list_empty(ATOM_FQ_LIST(fq))) { + jnode * cur; + + cur = capture_list_front(ATOM_FQ_LIST(fq)); + capture_list_remove_clean(cur); + + count_dequeued_node(fq); + LOCK_JNODE(cur); + assert("nikita-3154", !JF_ISSET(cur, JNODE_OVRWR)); + assert("nikita-3154", JF_ISSET(cur, JNODE_RELOC)); + assert("nikita-3154", JF_ISSET(cur, JNODE_FLUSH_QUEUED)); + JF_CLR(cur, JNODE_FLUSH_QUEUED); + + if (JF_ISSET(cur, JNODE_DIRTY)) { + capture_list_push_back(ATOM_DIRTY_LIST(atom, jnode_get_level(cur)), cur); + ON_DEBUG(count_jnode(atom, cur, FQ_LIST, DIRTY_LIST, 1)); + } else { + capture_list_push_back(ATOM_CLEAN_LIST(atom), cur); + ON_DEBUG(count_jnode(atom, cur, FQ_LIST, CLEAN_LIST, 1)); + } + + UNLOCK_JNODE(cur); + } + + if (-- atom->nr_running_queues == 0) + atom_send_event(atom); + + UNLOCK_ATOM(atom); +} + +/* Submit write requests for nodes on the already filled flush queue @fq. + + @fq: flush queue object which contains jnodes we can (and will) write. + @return: number of submitted blocks (>=0) if success, otherwise -- an error + code (<0). */ +reiser4_internal int +write_fq(flush_queue_t * fq, long * nr_submitted, int flags) +{ + int ret; + txn_atom * atom; + + while (1) { + atom = UNDER_SPIN(fq, fq, atom_get_locked_by_fq(fq)); + assert ("zam-924", atom); + /* do not write fq in parallel. */ + if (atom->nr_running_queues == 0 || !(flags & WRITEOUT_SINGLE_STREAM)) + break; + atom_wait_event(atom); + } + + atom->nr_running_queues ++; + UNLOCK_ATOM(atom); + + ret = write_jnode_list(ATOM_FQ_LIST(fq), fq, nr_submitted, flags); + release_prepped_list(fq); + + return ret; +} + +/* Getting flush queue object for exclusive use by one thread. May require + several iterations which is indicated by -E_REPEAT return code. + + This function does not contain code for obtaining an atom lock because an + atom lock is obtained by different ways in different parts of reiser4, + usually it is current atom, but we need a possibility for getting fq for the + atom of given jnode. */ +reiser4_internal int +fq_by_atom_gfp(txn_atom * atom, flush_queue_t ** new_fq, int gfp) +{ + flush_queue_t *fq; + + assert("zam-745", spin_atom_is_locked(atom)); + + fq = fq_list_front(&atom->flush_queues); + while (!fq_list_end(&atom->flush_queues, fq)) { + spin_lock_fq(fq); + + if (fq_ready(fq)) { + mark_fq_in_use(fq); + assert("vs-1246", fq->owner == NULL); + ON_DEBUG(fq->owner = current); + spin_unlock_fq(fq); + + if (*new_fq) + done_fq(*new_fq); + + *new_fq = fq; + + return 0; + } + + spin_unlock_fq(fq); + + fq = fq_list_next(fq); + } + + /* Use previously allocated fq object */ + if (*new_fq) { + mark_fq_in_use(*new_fq); + assert("vs-1248", (*new_fq)->owner == 0); + ON_DEBUG((*new_fq)->owner = current); + attach_fq(atom, *new_fq); + + return 0; + } + + UNLOCK_ATOM(atom); + + *new_fq = create_fq(gfp); + + if (*new_fq == NULL) + return RETERR(-ENOMEM); + + return RETERR(-E_REPEAT); +} + +reiser4_internal int +fq_by_atom(txn_atom * atom, flush_queue_t ** new_fq) +{ + return fq_by_atom_gfp(atom, new_fq, GFP_KERNEL); +} + +/* A wrapper around fq_by_atom for getting a flush queue object for current + * atom, if success fq->atom remains locked. */ +reiser4_internal flush_queue_t * +get_fq_for_current_atom(void) +{ + flush_queue_t *fq = NULL; + txn_atom *atom; + int ret; + + do { + atom = get_current_atom_locked(); + ret = fq_by_atom(atom, &fq); + } while (ret == -E_REPEAT); + + if (ret) + return ERR_PTR(ret); + return fq; +} + +/* Releasing flush queue object after exclusive use */ +reiser4_internal void +fq_put_nolock(flush_queue_t * fq) +{ + assert("zam-747", fq->atom != NULL); + assert("zam-902", capture_list_empty(ATOM_FQ_LIST(fq))); + mark_fq_ready(fq); + assert("vs-1245", fq->owner == current); + ON_DEBUG(fq->owner = NULL); +} + +reiser4_internal void +fq_put(flush_queue_t * fq) +{ + txn_atom *atom; + + spin_lock_fq(fq); + atom = atom_get_locked_by_fq(fq); + + assert("zam-746", atom != NULL); + + fq_put_nolock(fq); + atom_send_event(atom); + + spin_unlock_fq(fq); + UNLOCK_ATOM(atom); +} + +/* A part of atom object initialization related to the embedded flush queue + list head */ + +reiser4_internal void +init_atom_fq_parts(txn_atom * atom) +{ + fq_list_init(&atom->flush_queues); +} + +/* get a flush queue for an atom pointed by given jnode (spin-locked) ; returns + * both atom and jnode locked and found and took exclusive access for flush + * queue object. */ +reiser4_internal int fq_by_jnode_gfp(jnode * node, flush_queue_t ** fq, int gfp) +{ + txn_atom * atom; + int ret; + + assert("zam-835", spin_jnode_is_locked(node)); + + *fq = NULL; + + while (1) { + /* begin with taking lock on atom */ + atom = jnode_get_atom(node); + UNLOCK_JNODE(node); + + if (atom == NULL) { + /* jnode does not point to the atom anymore, it is + * possible because jnode lock could be removed for a + * time in atom_get_locked_by_jnode() */ + if (*fq) { + done_fq(*fq); + *fq = NULL; + } + return 0; + } + + /* atom lock is required for taking flush queue */ + ret = fq_by_atom_gfp(atom, fq, gfp); + + if (ret) { + if (ret == -E_REPEAT) + /* atom lock was released for doing memory + * allocation, start with locked jnode one more + * time */ + goto lock_again; + return ret; + } + + /* It is correct to lock atom first, then lock a jnode */ + LOCK_JNODE(node); + + if (node->atom == atom) + break; /* Yes! it is our jnode. We got all of them: + * flush queue, and both locked atom and + * jnode */ + + /* release all locks and allocated objects and restart from + * locked jnode. */ + UNLOCK_JNODE(node); + + fq_put(*fq); + fq = NULL; + + UNLOCK_ATOM(atom); + + lock_again: + LOCK_JNODE(node); + } + + return 0; +} + +reiser4_internal int fq_by_jnode(jnode * node, flush_queue_t ** fq) +{ + return fq_by_jnode_gfp(node, fq, GFP_KERNEL); +} + + +#if REISER4_DEBUG + +void check_fq(const txn_atom *atom) +{ + /* check number of nodes on all atom's flush queues */ + flush_queue_t *fq; + int count; + jnode *node; + + count = 0; + for_all_type_safe_list(fq, &atom->flush_queues, fq) { + spin_lock_fq(fq); + for_all_type_safe_list(capture, ATOM_FQ_LIST(fq), node) + count ++; + spin_unlock_fq(fq); + } + if (count != atom->fq) + warning("", "fq counter %d, real %d\n", atom->fq, count); + +} + +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 80 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/forward.h 2004-09-03 00:57:05.064269376 -0700 @@ -0,0 +1,258 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Forward declarations. Thank you Kernighan. */ + +#if !defined( __REISER4_FORWARD_H__ ) +#define __REISER4_FORWARD_H__ + +#include + +typedef struct zlock zlock; +typedef struct lock_stack lock_stack; +typedef struct lock_handle lock_handle; +typedef struct znode znode; +typedef struct flow flow_t; +typedef struct coord coord_t; +typedef struct tree_access_pointer tap_t; +typedef struct item_coord item_coord; +typedef struct shift_params shift_params; +typedef struct reiser4_object_create_data reiser4_object_create_data; +typedef union reiser4_plugin reiser4_plugin; +typedef int reiser4_plugin_id; +typedef struct item_plugin item_plugin; +typedef struct jnode_plugin jnode_plugin; +typedef struct reiser4_item_data reiser4_item_data; +typedef union reiser4_key reiser4_key; +typedef union reiser4_dblock_nr reiser4_dblock_nr; +typedef struct reiser4_tree reiser4_tree; +typedef struct carry_cut_data carry_cut_data; +typedef struct carry_kill_data carry_kill_data; +typedef struct carry_tree_op carry_tree_op; +typedef struct carry_tree_node carry_tree_node; +typedef struct carry_plugin_info carry_plugin_info; +typedef struct reiser4_journal reiser4_journal; +typedef struct txn_atom txn_atom; +typedef struct txn_handle txn_handle; +typedef struct txn_mgr txn_mgr; +typedef struct reiser4_dir_entry_desc reiser4_dir_entry_desc; +typedef struct reiser4_context reiser4_context; +typedef struct carry_level carry_level; +typedef struct blocknr_set blocknr_set; +typedef struct blocknr_set_entry blocknr_set_entry; +/* super_block->s_fs_info points to this */ +typedef struct reiser4_super_info_data reiser4_super_info_data; +/* next two objects are fields of reiser4_super_info_data */ +typedef struct reiser4_oid_allocator reiser4_oid_allocator; +typedef struct reiser4_space_allocator reiser4_space_allocator; +typedef struct reiser4_file_fsdata reiser4_file_fsdata; + +typedef struct flush_scan flush_scan; +typedef struct flush_position flush_pos_t; + +typedef unsigned short pos_in_node_t; +#define MAX_POS_IN_NODE 65535 + +typedef struct jnode jnode; +typedef struct reiser4_blocknr_hint reiser4_blocknr_hint; + +typedef struct uf_coord uf_coord_t; +typedef struct hint hint_t; + +typedef struct ktxnmgrd_context ktxnmgrd_context; + +typedef struct reiser4_xattr_plugin reiser4_xattr_plugin; + +struct inode; +struct page; +struct file; +struct dentry; +struct super_block; + +/* return values of coord_by_key(). cbk == coord_by_key */ +typedef enum { + CBK_COORD_FOUND = 0, + CBK_COORD_NOTFOUND = -ENOENT, +} lookup_result; + +/* results of lookup with directory file */ +typedef enum { + FILE_NAME_FOUND = 0, + FILE_NAME_NOTFOUND = -ENOENT, + FILE_IO_ERROR = -EIO, /* FIXME: it seems silly to have special OOM, IO_ERROR return codes for each search. */ + FILE_OOM = -ENOMEM /* FIXME: it seems silly to have special OOM, IO_ERROR return codes for each search. */ +} file_lookup_result; + +/* behaviors of lookup. If coord we are looking for is actually in a tree, + both coincide. */ +typedef enum { + /* search exactly for the coord with key given */ + FIND_EXACT, + /* search for coord with the maximal key not greater than one + given */ + FIND_MAX_NOT_MORE_THAN /*LEFT_SLANT_BIAS */ +} lookup_bias; + +typedef enum { + /* number of leaf level of the tree + The fake root has (tree_level=0). */ + LEAF_LEVEL = 1, + + /* number of level one above leaf level of the tree. + + It is supposed that internal tree used by reiser4 to store file + system data and meta data will have height 2 initially (when + created by mkfs). + */ + TWIG_LEVEL = 2, +} tree_level; + +/* The "real" maximum ztree height is the 0-origin size of any per-level + array, since the zero'th level is not used. */ +#define REAL_MAX_ZTREE_HEIGHT (REISER4_MAX_ZTREE_HEIGHT-LEAF_LEVEL) + +/* enumeration of possible mutual position of item and coord. This enum is + return type of ->is_in_item() item plugin method which see. */ +typedef enum { + /* coord is on the left of an item*/ + IP_ON_THE_LEFT, + /* coord is inside item */ + IP_INSIDE, + /* coord is inside item, but to the right of the rightmost unit of + this item */ + IP_RIGHT_EDGE, + /* coord is on the right of an item */ + IP_ON_THE_RIGHT +} interposition; + +/* type of lock to acquire on znode before returning it to caller */ +typedef enum { + ZNODE_NO_LOCK = 0, + ZNODE_READ_LOCK = 1, + ZNODE_WRITE_LOCK = 2, +} znode_lock_mode; + +/* type of lock request */ +typedef enum { + ZNODE_LOCK_LOPRI = 0, + ZNODE_LOCK_HIPRI = (1 << 0), + + /* By setting the ZNODE_LOCK_NONBLOCK flag in a lock request the call to longterm_lock_znode will not sleep + waiting for the lock to become available. If the lock is unavailable, reiser4_znode_lock will immediately + return the value -E_REPEAT. */ + ZNODE_LOCK_NONBLOCK = (1 << 1), + /* An option for longterm_lock_znode which prevents atom fusion */ + ZNODE_LOCK_DONT_FUSE = (1 << 2) +} znode_lock_request; + +typedef enum { READ_OP = 0, WRITE_OP = 1 } rw_op; + +/* used to specify direction of shift. These must be -1 and 1 */ +typedef enum { + SHIFT_LEFT = 1, + SHIFT_RIGHT = -1 +} shift_direction; + +typedef enum { + LEFT_SIDE, + RIGHT_SIDE +} sideof; + +#define round_up( value, order ) \ + ( ( typeof( value ) )( ( ( long ) ( value ) + ( order ) - 1U ) & \ + ~( ( order ) - 1 ) ) ) + +/* values returned by squalloc_right_neighbor and its auxiliary functions */ +typedef enum { + /* unit of internal item is moved */ + SUBTREE_MOVED = 0, + /* nothing else can be squeezed into left neighbor */ + SQUEEZE_TARGET_FULL = 1, + /* all content of node is squeezed into its left neighbor */ + SQUEEZE_SOURCE_EMPTY = 2, + /* one more item is copied (this is only returned by + allocate_and_copy_extent to squalloc_twig)) */ + SQUEEZE_CONTINUE = 3 +} squeeze_result; + +/* Do not change items ids. If you do - there will be format change */ +typedef enum { + STATIC_STAT_DATA_ID = 0x0, + SIMPLE_DIR_ENTRY_ID = 0x1, + COMPOUND_DIR_ID = 0x2, + NODE_POINTER_ID = 0x3, + EXTENT_POINTER_ID = 0x5, + FORMATTING_ID = 0x6, + CTAIL_ID = 0x7, + BLACK_BOX_ID = 0x8, + LAST_ITEM_ID = 0x9 +} item_id; + +/* Flags passed to jnode_flush() to allow it to distinguish default settings based on + whether commit() was called or VM memory pressure was applied. */ +typedef enum { + /* submit flush queue to disk at jnode_flush completion */ + JNODE_FLUSH_WRITE_BLOCKS = 1, + + /* flush is called for commit */ + JNODE_FLUSH_COMMIT = 2, + /* not implemented */ + JNODE_FLUSH_MEMORY_FORMATTED = 4, + + /* not implemented */ + JNODE_FLUSH_MEMORY_UNFORMATTED = 8, +} jnode_flush_flags; + +/* Flags to insert/paste carry operations. Currently they only used in + flushing code, but in future, they can be used to optimize for repetitive + accesses. */ +typedef enum { + /* carry is not allowed to shift data to the left when trying to find + free space */ + COPI_DONT_SHIFT_LEFT = (1 << 0), + /* carry is not allowed to shift data to the right when trying to find + free space */ + COPI_DONT_SHIFT_RIGHT = (1 << 1), + /* carry is not allowed to allocate new node(s) when trying to find + free space */ + COPI_DONT_ALLOCATE = (1 << 2), + /* try to load left neighbor if its not in a cache */ + COPI_LOAD_LEFT = (1 << 3), + /* try to load right neighbor if its not in a cache */ + COPI_LOAD_RIGHT = (1 << 4), + /* shift insertion point to the left neighbor */ + COPI_GO_LEFT = (1 << 5), + /* shift insertion point to the right neighbor */ + COPI_GO_RIGHT = (1 << 6), + /* try to step back into original node if insertion into new node + fails after shifting data there. */ + COPI_STEP_BACK = (1 << 7) +} cop_insert_flag; + +typedef enum { + SAFE_UNLINK, /* safe-link for unlink */ + SAFE_TRUNCATE /* safe-link for truncate */ +} reiser4_safe_link_t; + +/* this is to show on which list of atom jnode is */ +typedef enum { + NOT_CAPTURED, + DIRTY_LIST, + CLEAN_LIST, + FQ_LIST, + WB_LIST, + OVRWR_LIST, + PROTECT_LIST +} atom_list; + +/* __REISER4_FORWARD_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/init_super.c 2004-09-03 00:57:05.066269072 -0700 @@ -0,0 +1,562 @@ +/* Copyright by Hans Reiser, 2003 */ + +#include "forward.h" +#include "debug.h" +#include "dformat.h" +#include "txnmgr.h" +#include "jnode.h" +#include "znode.h" +#include "tree.h" +#include "vfs_ops.h" +#include "inode.h" +#include "page_cache.h" +#include "ktxnmgrd.h" +#include "super.h" +#include "reiser4.h" +#include "kattr.h" +#include "entd.h" +#include "emergency_flush.h" +#include "prof.h" +#include "repacker.h" +#include "safe_link.h" +#include "plugin/dir/dir.h" + +#include +#include +#include +#include +#include +#include +#include + +#define _INIT_PARAM_LIST (struct super_block * s, reiser4_context * ctx, void * data, int silent) +#define _DONE_PARAM_LIST (struct super_block * s) + +#define _INIT_(subsys) static int _init_##subsys _INIT_PARAM_LIST +#define _DONE_(subsys) static void _done_##subsys _DONE_PARAM_LIST + +#define _DONE_EMPTY(subsys) _DONE_(subsys) {} + +_INIT_(mount_flags_check) +{ +/* if (bdev_read_only(s->s_bdev) || (s->s_flags & MS_RDONLY)) { + warning("nikita-3322", "Readonly reiser4 is not yet supported"); + return RETERR(-EROFS); + }*/ + return 0; +} + +_DONE_EMPTY(mount_flags_check) + +_INIT_(sinfo) +{ + reiser4_super_info_data * sbinfo; + + sbinfo = kmalloc(sizeof (reiser4_super_info_data), GFP_KERNEL); + if (!sbinfo) + return RETERR(-ENOMEM); + + s->s_fs_info = sbinfo; + s->s_op = NULL; + xmemset(sbinfo, 0, sizeof (*sbinfo)); + + ON_DEBUG(INIT_LIST_HEAD(&sbinfo->all_jnodes)); + ON_DEBUG(spin_lock_init(&sbinfo->all_guard)); + + sema_init(&sbinfo->delete_sema, 1); + sema_init(&sbinfo->flush_sema, 1); + spin_super_init(sbinfo); + spin_super_eflush_init(sbinfo); + + return 0; +} + +_DONE_(sinfo) +{ + assert("zam-990", s->s_fs_info != NULL); + rcu_barrier(); + kfree(s->s_fs_info); + s->s_fs_info = NULL; +} + +_INIT_(stat) +{ + return reiser4_stat_init(&get_super_private(s)->stats); +} + +_DONE_(stat) +{ + reiser4_stat_done(&get_super_private(s)->stats); +} + +_INIT_(context) +{ + return init_context(ctx, s); +} + +_DONE_(context) +{ + reiser4_super_info_data * sbinfo; + + sbinfo = get_super_private(s); + + close_log_file(&sbinfo->log_file); + + if (reiser4_is_debugged(s, REISER4_STATS_ON_UMOUNT)) + reiser4_print_stats(); + + /* we don't want ->write_super to be called any more. */ + if (s->s_op) + s->s_op->write_super = NULL; +#if REISER4_DEBUG + { + struct list_head *scan; + + /* print jnodes that survived umount. */ + list_for_each(scan, &sbinfo->all_jnodes) { + jnode *busy; + + busy = list_entry(scan, jnode, jnodes); + info_jnode("\nafter umount", busy); + } + } + if (sbinfo->kmalloc_allocated > 0) + warning("nikita-2622", + "%i bytes still allocated", sbinfo->kmalloc_allocated); +#endif + + get_current_context()->trans = NULL; + done_context(get_current_context()); +} + +_INIT_(parse_options) +{ + return reiser4_parse_options(s, data); +} + +_DONE_(parse_options) +{ + close_log_file(&get_super_private(s)->log_file); +} + +_INIT_(object_ops) +{ + build_object_ops(s, &get_super_private(s)->ops); + return 0; +} + +_DONE_EMPTY(object_ops) + +_INIT_(read_super) +{ + struct buffer_head *super_bh; + struct reiser4_master_sb *master_sb; + int plugin_id; + reiser4_super_info_data * sbinfo = get_super_private(s); + unsigned long blocksize; + + read_super_block: +#ifdef CONFIG_REISER4_BADBLOCKS + if ( sbinfo->altsuper ) + super_bh = sb_bread(s, (sector_t) (sbinfo->altsuper >> s->s_blocksize_bits)); + else +#endif + /* look for reiser4 magic at hardcoded place */ + super_bh = sb_bread(s, (sector_t) (REISER4_MAGIC_OFFSET / s->s_blocksize)); + + if (!super_bh) + return RETERR(-EIO); + + master_sb = (struct reiser4_master_sb *) super_bh->b_data; + /* check reiser4 magic string */ + if (!strncmp(master_sb->magic, REISER4_SUPER_MAGIC_STRING, sizeof(REISER4_SUPER_MAGIC_STRING))) { + /* reset block size if it is not a right one FIXME-VS: better comment is needed */ + blocksize = d16tocpu(&master_sb->blocksize); + + if (blocksize != PAGE_CACHE_SIZE) { + if (!silent) + warning("nikita-2609", "%s: wrong block size %ld\n", s->s_id, blocksize); + brelse(super_bh); + return RETERR(-EINVAL); + } + if (blocksize != s->s_blocksize) { + brelse(super_bh); + if (!sb_set_blocksize(s, (int) blocksize)) { + return RETERR(-EINVAL); + } + goto read_super_block; + } + + plugin_id = d16tocpu(&master_sb->disk_plugin_id); + /* only two plugins are available for now */ + assert("vs-476", plugin_id == FORMAT40_ID); + sbinfo->df_plug = disk_format_plugin_by_id(plugin_id); + sbinfo->diskmap_block = d64tocpu(&master_sb->diskmap); + brelse(super_bh); + } else { + if (!silent) { + warning("nikita-2608", "Wrong master super block magic."); + } + + /* no standard reiser4 super block found */ + brelse(super_bh); + /* FIXME-VS: call guess method for all available layout + plugins */ + /* umka (2002.06.12) Is it possible when format-specific super + block exists but there no master super block? */ + return RETERR(-EINVAL); + } + return 0; +} + +_DONE_EMPTY(read_super) + +_INIT_(tree0) +{ + reiser4_super_info_data * sbinfo = get_super_private(s); + + init_tree_0(&sbinfo->tree); + sbinfo->tree.super = s; + return 0; +} + +_DONE_EMPTY(tree0) + +_INIT_(txnmgr) +{ + txnmgr_init(&get_super_private(s)->tmgr); + return 0; +} + +_DONE_(txnmgr) +{ + txnmgr_done(&get_super_private(s)->tmgr); +} + +_INIT_(ktxnmgrd_context) +{ + return init_ktxnmgrd_context(&get_super_private(s)->tmgr); +} + +_DONE_(ktxnmgrd_context) +{ + done_ktxnmgrd_context(&get_super_private(s)->tmgr); +} + +_INIT_(ktxnmgrd) +{ + return start_ktxnmgrd(&get_super_private(s)->tmgr); +} + +_DONE_(ktxnmgrd) +{ + stop_ktxnmgrd(&get_super_private(s)->tmgr); +} + +_INIT_(formatted_fake) +{ + return init_formatted_fake(s); +} + +_DONE_(formatted_fake) +{ + reiser4_super_info_data * sbinfo; + + sbinfo = get_super_private(s); + + rcu_barrier(); + + /* done_formatted_fake just has finished with last jnodes (bitmap + * ones) */ + done_tree(&sbinfo->tree); + /* call finish_rcu(), because some znode were "released" in + * done_tree(). */ + rcu_barrier(); + done_formatted_fake(s); +} + +_INIT_(entd) +{ + init_entd_context(s); + return 0; +} + +_DONE_(entd) +{ + done_entd_context(s); +} + +_DONE_(disk_format); + +_INIT_(disk_format) +{ + return get_super_private(s)->df_plug->get_ready(s, data); +} + +_DONE_(disk_format) +{ + reiser4_super_info_data *sbinfo = get_super_private(s); + + sbinfo->df_plug->release(s); +} + +_INIT_(sb_counters) +{ + /* There are some 'committed' versions of reiser4 super block + counters, which correspond to reiser4 on-disk state. These counters + are initialized here */ + reiser4_super_info_data *sbinfo = get_super_private(s); + + sbinfo->blocks_free_committed = sbinfo->blocks_free; + sbinfo->nr_files_committed = oids_used(s); + + return 0; +} + +_DONE_EMPTY(sb_counters) + +_INIT_(d_cursor) +{ + /* this should be done before reading inode of root directory, because + * reiser4_iget() used load_cursors(). */ + return d_cursor_init_at(s); +} + +_DONE_(d_cursor) +{ + d_cursor_done_at(s); +} + +static struct { + reiser4_plugin_type type; + reiser4_plugin_id id; +} default_plugins[PSET_LAST] = { + [PSET_FILE] = { + .type = REISER4_FILE_PLUGIN_TYPE, + .id = UNIX_FILE_PLUGIN_ID + }, + [PSET_DIR] = { + .type = REISER4_DIR_PLUGIN_TYPE, + .id = HASHED_DIR_PLUGIN_ID + }, + [PSET_HASH] = { + .type = REISER4_HASH_PLUGIN_TYPE, + .id = R5_HASH_ID + }, + [PSET_FIBRATION] = { + .type = REISER4_FIBRATION_PLUGIN_TYPE, + .id = FIBRATION_DOT_O + }, + [PSET_PERM] = { + .type = REISER4_PERM_PLUGIN_TYPE, + .id = RWX_PERM_ID + }, + [PSET_FORMATTING] = { + .type = REISER4_FORMATTING_PLUGIN_TYPE, + .id = SMALL_FILE_FORMATTING_ID + }, + [PSET_SD] = { + .type = REISER4_ITEM_PLUGIN_TYPE, + .id = STATIC_STAT_DATA_ID + }, + [PSET_DIR_ITEM] = { + .type = REISER4_ITEM_PLUGIN_TYPE, + .id = COMPOUND_DIR_ID + }, + [PSET_CRYPTO] = { + .type = REISER4_CRYPTO_PLUGIN_TYPE, + .id = NONE_CRYPTO_ID + }, + [PSET_DIGEST] = { + .type = REISER4_DIGEST_PLUGIN_TYPE, + .id = NONE_DIGEST_ID + }, + [PSET_COMPRESSION] = { + .type = REISER4_COMPRESSION_PLUGIN_TYPE, + .id = NONE_COMPRESSION_ID + } +}; + +/* access to default plugin table */ +reiser4_internal reiser4_plugin * +get_default_plugin(pset_member memb) +{ + return plugin_by_id(default_plugins[memb].type, default_plugins[memb].id); +} + +_INIT_(fs_root) +{ + reiser4_super_info_data *sbinfo = get_super_private(s); + struct inode * inode; + int result = 0; + + inode = reiser4_iget(s, sbinfo->df_plug->root_dir_key(s), 0); + if (IS_ERR(inode)) + return RETERR(PTR_ERR(inode)); + + s->s_root = d_alloc_root(inode); + if (!s->s_root) { + iput(inode); + return RETERR(-ENOMEM); + } + + s->s_root->d_op = &sbinfo->ops.dentry; + + if (!is_inode_loaded(inode)) { + pset_member memb; + + for (memb = 0; memb < PSET_LAST; ++ memb) { + reiser4_plugin *plug; + + plug = get_default_plugin(memb); + result = grab_plugin_from(inode, memb, plug); + if (result != 0) + break; + } + + if (result == 0) { + if (REISER4_DEBUG) { + plugin_set *pset; + + pset = reiser4_inode_data(inode)->pset; + for (memb = 0; memb < PSET_LAST; ++ memb) + assert("nikita-3500", + pset_get(pset, memb) != NULL); + } + } else + warning("nikita-3448", "Cannot set plugins of root: %i", + result); + reiser4_iget_complete(inode); + } + s->s_maxbytes = MAX_LFS_FILESIZE; + return result; +} + +_DONE_(fs_root) +{ + shrink_dcache_parent(s->s_root); +} + +_INIT_(sysfs) +{ + return reiser4_sysfs_init(s); +} + +_DONE_(sysfs) +{ + reiser4_sysfs_done(s); +} + +_INIT_(repacker) +{ + return init_reiser4_repacker(s); +} + +_DONE_(repacker) +{ + done_reiser4_repacker(s); +} + +_INIT_(safelink) +{ + process_safelinks(s); + /* failure to process safe-links is not critical. Continue with + * mount. */ + return 0; +} + +_DONE_(safelink) +{ +} + +_INIT_(exit_context) +{ + reiser4_exit_context(ctx); + return 0; +} + +_DONE_EMPTY(exit_context) + +struct reiser4_subsys { + int (*init) _INIT_PARAM_LIST; + void (*done) _DONE_PARAM_LIST; +}; + +#define _SUBSYS(subsys) {.init = &_init_##subsys, .done = &_done_##subsys} +static struct reiser4_subsys subsys_array[] = { + _SUBSYS(mount_flags_check), + _SUBSYS(sinfo), + _SUBSYS(stat), + _SUBSYS(context), + _SUBSYS(parse_options), + _SUBSYS(object_ops), + _SUBSYS(read_super), + _SUBSYS(tree0), + _SUBSYS(txnmgr), + _SUBSYS(ktxnmgrd_context), + _SUBSYS(ktxnmgrd), + _SUBSYS(entd), + _SUBSYS(formatted_fake), + _SUBSYS(disk_format), + _SUBSYS(sb_counters), + _SUBSYS(d_cursor), + _SUBSYS(fs_root), + _SUBSYS(sysfs), + _SUBSYS(repacker), + _SUBSYS(safelink), + _SUBSYS(exit_context) +}; + +#define REISER4_NR_SUBSYS (sizeof(subsys_array) / sizeof(struct reiser4_subsys)) + +static void done_super (struct super_block * s, int last_done) +{ + int i; + for (i = last_done; i >= 0; i--) + subsys_array[i].done(s); +} + +/* read super block from device and fill remaining fields in @s. + + This is read_super() of the past. */ +reiser4_internal int +reiser4_fill_super (struct super_block * s, void * data, int silent) +{ + reiser4_context ctx; + int i; + int ret; + + assert ("zam-989", s != NULL); + + for (i = 0; i < REISER4_NR_SUBSYS; i++) { + ret = subsys_array[i].init(s, &ctx, data, silent); + if (ret) { + done_super(s, i - 1); + return ret; + } + } + return 0; +} + +#if 0 + +int reiser4_done_super (struct super_block * s) +{ + reiser4_context ctx; + + init_context(&ctx, s); + done_super(s, REISER4_NR_SUBSYS - 1); + return 0; +} + +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 80 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/init_super.h 2004-09-03 00:57:05.066269072 -0700 @@ -0,0 +1,4 @@ +/* Copyright by Hans Reiser, 2003 */ + +extern int reiser4_fill_super (struct super_block * s, void * data, int silent); +extern int reiser4_done_super (struct super_block * s); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/inode.c 2004-09-03 00:57:07.757859888 -0700 @@ -0,0 +1,806 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Inode specific operations. */ + +#include "forward.h" +#include "debug.h" +#include "key.h" +#include "kassign.h" +#include "coord.h" +#include "seal.h" +#include "dscale.h" +#include "plugin/item/item.h" +#include "plugin/security/perm.h" +#include "plugin/plugin.h" +#include "plugin/object.h" +#include "plugin/dir/dir.h" +#include "znode.h" +#include "vfs_ops.h" +#include "inode.h" +#include "super.h" +#include "reiser4.h" + +#include /* for struct super_block, address_space */ + +/* return reiser4 internal tree which inode belongs to */ +/* Audited by: green(2002.06.17) */ +reiser4_internal reiser4_tree * +tree_by_inode(const struct inode * inode /* inode queried */ ) +{ + assert("nikita-256", inode != NULL); + assert("nikita-257", inode->i_sb != NULL); + return get_tree(inode->i_sb); +} + +/* return reiser4-specific inode flags */ +static inline unsigned long * +inode_flags(const struct inode * const inode) +{ + assert("nikita-2842", inode != NULL); + return &reiser4_inode_data(inode)->flags; +} + +/* set reiser4-specific flag @f in @inode */ +reiser4_internal void +inode_set_flag(struct inode * inode, reiser4_file_plugin_flags f) +{ + assert("nikita-2248", inode != NULL); + set_bit((int) f, inode_flags(inode)); +} + +/* clear reiser4-specific flag @f in @inode */ +reiser4_internal void +inode_clr_flag(struct inode * inode, reiser4_file_plugin_flags f) +{ + assert("nikita-2250", inode != NULL); + clear_bit((int) f, inode_flags(inode)); +} + +/* true if reiser4-specific flag @f is set in @inode */ +reiser4_internal int +inode_get_flag(const struct inode * inode, reiser4_file_plugin_flags f) +{ + assert("nikita-2251", inode != NULL); + return test_bit((int) f, inode_flags(inode)); +} + +/* convert oid to inode number */ +reiser4_internal ino_t oid_to_ino(oid_t oid) +{ + return (ino_t) oid; +} + +/* convert oid to user visible inode number */ +reiser4_internal ino_t oid_to_uino(oid_t oid) +{ + /* reiser4 object is uniquely identified by oid which is 64 bit + quantity. Kernel in-memory inode is indexed (in the hash table) by + 32 bit i_ino field, but this is not a problem, because there is a + way to further distinguish inodes with identical inode numbers + (find_actor supplied to iget()). + + But user space expects unique 32 bit inode number. Obviously this + is impossible. Work-around is to somehow hash oid into user visible + inode number. + */ + oid_t max_ino = (ino_t) ~ 0; + + if (REISER4_INO_IS_OID || (oid <= max_ino)) + return oid; + else + /* this is remotely similar to algorithm used to find next pid + to use for process: after wrap-around start from some + offset rather than from 0. Idea is that there are some long + living objects with which we don't want to collide. + */ + return REISER4_UINO_SHIFT + ((oid - max_ino) & (max_ino >> 1)); +} + +/* check that "inode" is on reiser4 file-system */ +reiser4_internal int +is_reiser4_inode(const struct inode *inode /* inode queried */ ) +{ + return + inode != NULL && + (is_reiser4_super(inode->i_sb) || + inode->i_op == &reiser4_inode_operations); + +} + +/* Maximal length of a name that can be stored in directory @inode. + + This is used in check during file creation and lookup. */ +reiser4_internal int +reiser4_max_filename_len(const struct inode *inode /* inode queried */ ) +{ + assert("nikita-287", is_reiser4_inode(inode)); + assert("nikita-1710", inode_dir_item_plugin(inode)); + if (inode_dir_item_plugin(inode)->s.dir.max_name_len) + return inode_dir_item_plugin(inode)->s.dir.max_name_len(inode); + else + return 255; +} + +/* Maximal number of hash collisions for this directory. */ +reiser4_internal int +max_hash_collisions(const struct inode *dir /* inode queried */ ) +{ + assert("nikita-1711", dir != NULL); +#if REISER4_USE_COLLISION_LIMIT + return reiser4_inode_data(dir)->plugin.max_collisions; +#else + (void) dir; + return ~0; +#endif +} + +/* Install file, inode, and address_space operation on @inode, depending on + its mode. */ +reiser4_internal int +setup_inode_ops(struct inode *inode /* inode to intialise */ , + reiser4_object_create_data * data /* parameters to create + * object */ ) +{ + reiser4_super_info_data *sinfo; + + sinfo = get_super_private(inode->i_sb); + + switch (inode->i_mode & S_IFMT) { + case S_IFSOCK: + case S_IFBLK: + case S_IFCHR: + case S_IFIFO:{ + dev_t rdev; /* to keep gcc happy */ + + /* ugly hack with rdev */ + if (data == NULL) { + rdev = inode->i_rdev; + inode->i_rdev = 0; + } else + rdev = data->rdev; + inode->i_blocks = 0; + inode->i_op = &sinfo->ops.special; + /* other fields are already initialised. */ + init_special_inode(inode, inode->i_mode, rdev); + break; + } + case S_IFLNK: + inode->i_op = &sinfo->ops.symlink; + inode->i_fop = NULL; + inode->i_mapping->a_ops = &sinfo->ops.as; + break; + case S_IFDIR: + inode->i_op = &sinfo->ops.dir; + inode->i_fop = &sinfo->ops.file; + inode->i_mapping->a_ops = &sinfo->ops.as; + break; + case S_IFREG: + inode->i_op = &sinfo->ops.regular; + inode->i_fop = &sinfo->ops.file; + inode->i_mapping->a_ops = &sinfo->ops.as; + break; + default: + warning("nikita-291", "wrong file mode: %o for %llu", inode->i_mode, + (unsigned long long)get_inode_oid(inode)); + reiser4_make_bad_inode(inode); + return RETERR(-EINVAL); + } + return 0; +} + +/* initialise inode from disk data. Called with inode locked. + Return inode locked. */ +static int +init_inode(struct inode *inode /* inode to intialise */ , + coord_t * coord /* coord of stat data */ ) +{ + int result; + item_plugin *iplug; + void *body; + int length; + reiser4_inode *state; + + assert("nikita-292", coord != NULL); + assert("nikita-293", inode != NULL); + + coord_clear_iplug(coord); + result = zload(coord->node); + if (result) + return result; + iplug = item_plugin_by_coord(coord); + body = item_body_by_coord(coord); + length = item_length_by_coord(coord); + + assert("nikita-295", iplug != NULL); + assert("nikita-296", body != NULL); + assert("nikita-297", length > 0); + + /* inode is under I_LOCK now */ + + state = reiser4_inode_data(inode); + /* call stat-data plugin method to load sd content into inode */ + result = iplug->s.sd.init_inode(inode, body, length); + plugin_set_sd(&state->pset, iplug); + if (result == 0) { + result = setup_inode_ops(inode, NULL); + if (result == 0 && + inode->i_sb->s_root && inode->i_sb->s_root->d_inode) { + struct inode *root; + pset_member ind; + + /* take missing plugins from file-system defaults */ + root = inode->i_sb->s_root->d_inode; + /* file and directory plugins are already initialised. */ + for (ind = PSET_DIR + 1; ind < PSET_LAST; ++ind) { + result = grab_plugin(inode, root, ind); + if (result != 0) + break; + } + if (result != 0) { + warning("nikita-3447", + "Cannot set up plugins for %lli", + (unsigned long long)get_inode_oid(inode)); + } + } + } + zrelse(coord->node); + return result; +} + +/* read `inode' from the disk. This is what was previously in + reiserfs_read_inode2(). + + Must be called with inode locked. Return inode still locked. +*/ +static int +read_inode(struct inode *inode /* inode to read from disk */ , + const reiser4_key * key /* key of stat data */, + int silent) +{ + int result; + lock_handle lh; + reiser4_inode *info; + coord_t coord; + + assert("nikita-298", inode != NULL); + assert("nikita-1945", !is_inode_loaded(inode)); + + info = reiser4_inode_data(inode); + assert("nikita-300", info->locality_id != 0); + + coord_init_zero(&coord); + init_lh(&lh); + /* locate stat-data in a tree and return znode locked */ + result = lookup_sd(inode, ZNODE_READ_LOCK, &coord, &lh, key, silent); + assert("nikita-301", !is_inode_loaded(inode)); + if (result == 0) { + /* use stat-data plugin to load sd into inode. */ + result = init_inode(inode, &coord); + if (result == 0) { + /* initialize stat-data seal */ + spin_lock_inode(inode); + seal_init(&info->sd_seal, &coord, key); + info->sd_coord = coord; + spin_unlock_inode(inode); + + /* call file plugin's method to initialize plugin + * specific part of inode */ + if (inode_file_plugin(inode)->init_inode_data) + inode_file_plugin(inode)->init_inode_data(inode, + NULL, + 0); + /* load detached directory cursors for stateless + * directory readers (NFS). */ + load_cursors(inode); + + /* Check the opened inode for consistency. */ + result = get_super_private(inode->i_sb)->df_plug->check_open(inode); + } + } + /* lookup_sd() doesn't release coord because we want znode + stay read-locked while stat-data fields are accessed in + init_inode() */ + done_lh(&lh); + + if (result != 0) + reiser4_make_bad_inode(inode); + return result; +} + +/* initialise new reiser4 inode being inserted into hash table. */ +static int +init_locked_inode(struct inode *inode /* new inode */ , + void *opaque /* key of stat data passed to the + * iget5_locked as cookie */ ) +{ + reiser4_key *key; + + assert("nikita-1995", inode != NULL); + assert("nikita-1996", opaque != NULL); + key = opaque; + set_inode_oid(inode, get_key_objectid(key)); + reiser4_inode_data(inode)->locality_id = get_key_locality(key); + return 0; +} + +/* reiser4_inode_find_actor() - "find actor" supplied by reiser4 to iget5_locked(). + + This function is called by iget5_locked() to distinguish reiser4 inodes + having the same inode numbers. Such inodes can only exist due to some error + condition. One of them should be bad. Inodes with identical inode numbers + (objectids) are distinguished by their packing locality. + +*/ +reiser4_internal int +reiser4_inode_find_actor(struct inode *inode /* inode from hash table to + * check */ , + void *opaque /* "cookie" passed to + * iget5_locked(). This is stat data + * key */ ) +{ + reiser4_key *key; + + key = opaque; + return + /* oid is unique, so first term is enough, actually. */ + get_inode_oid(inode) == get_key_objectid(key) && + /* + * also, locality should be checked, but locality is stored in + * the reiser4-specific part of the inode, and actor can be + * called against arbitrary inode that happened to be in this + * hash chain. Hence we first have to check that this is + * reiser4 inode at least. is_reiser4_inode() is probably too + * early to call, as inode may have ->i_op not yet + * initialised. + */ + is_reiser4_super(inode->i_sb) && + /* + * usually objectid is unique, but pseudo files use counter to + * generate objectid. All pseudo files are placed into special + * (otherwise unused) locality. + */ + reiser4_inode_data(inode)->locality_id == get_key_locality(key); +} + +/* + * this is our helper function a la iget(). This is be called by + * reiser4_lookup() and reiser4_read_super(). Return inode locked or error + * encountered. + */ +reiser4_internal struct inode * +reiser4_iget(struct super_block *super /* super block */ , + const reiser4_key * key /* key of inode's stat-data */, + int silent) +{ + struct inode *inode; + int result; + reiser4_inode * info; + + assert("nikita-302", super != NULL); + assert("nikita-303", key != NULL); + + result = 0; + + /* call iget(). Our ->read_inode() is dummy, so this will either + find inode in cache or return uninitialised inode */ + inode = iget5_locked(super, + (unsigned long) get_key_objectid(key), + reiser4_inode_find_actor, + init_locked_inode, + (reiser4_key *) key); + if (inode == NULL) + return ERR_PTR(RETERR(-ENOMEM)); + if (is_bad_inode(inode) && !silent) { + warning("nikita-304", "Stat data not found"); + print_key("key", key); + iput(inode); + return ERR_PTR(RETERR(-EIO)); + } + + info = reiser4_inode_data(inode); + + /* Reiser4 inode state bit REISER4_LOADED is used to distinguish fully + loaded and initialized inode from just allocated inode. If + REISER4_LOADED bit is not set, reiser4_iget() completes loading under + info->loading. The place in reiser4 which uses not initialized inode + is the reiser4 repacker, see repacker-related functions in + plugin/item/extent.c */ + if (!is_inode_loaded(inode)) { + down(&info->loading); + if (!is_inode_loaded(inode)) { + /* locking: iget5_locked returns locked inode */ + assert("nikita-1941", !is_inode_loaded(inode)); + assert("nikita-1949", + reiser4_inode_find_actor(inode, + (reiser4_key *)key)); + /* now, inode has objectid as ->i_ino and locality in + reiser4-specific part. This is enough for + read_inode() to read stat data from the disk */ + result = read_inode(inode, key, silent); + } + } + + if (inode->i_state & I_NEW) + unlock_new_inode(inode); + + if (is_bad_inode(inode)) { + up(&info->loading); + iput(inode); + inode = ERR_PTR(result); + } else if (REISER4_DEBUG) { + reiser4_key found_key; + + build_sd_key(inode, &found_key); + if (!keyeq(&found_key, key)) { + warning("nikita-305", "Wrong key in sd"); + print_key("sought for", key); + print_key("found", &found_key); + } + if (inode_file_plugin(inode)->not_linked(inode)) { + warning("nikita-3559", "Unlinked inode found: %llu\n", + (unsigned long long)get_inode_oid(inode)); + print_inode("inode", inode); + } + } + return inode; +} + +/* reiser4_iget() may return not fully initialized inode, this function should + * be called after one completes reiser4 inode initializing. */ +reiser4_internal void reiser4_iget_complete (struct inode * inode) +{ + assert("zam-988", is_reiser4_inode(inode)); + + if (!is_inode_loaded(inode)) { + inode_set_flag(inode, REISER4_LOADED); + up(&reiser4_inode_data(inode)->loading); + } +} + +reiser4_internal void +reiser4_make_bad_inode(struct inode *inode) +{ + assert("nikita-1934", inode != NULL); + + /* clear LOADED bit */ + inode_clr_flag(inode, REISER4_LOADED); + make_bad_inode(inode); + return; +} + +reiser4_internal file_plugin * +inode_file_plugin(const struct inode * inode) +{ + assert("nikita-1997", inode != NULL); + return reiser4_inode_data(inode)->pset->file; +} + +reiser4_internal dir_plugin * +inode_dir_plugin(const struct inode * inode) +{ + assert("nikita-1998", inode != NULL); + return reiser4_inode_data(inode)->pset->dir; +} + +reiser4_internal perm_plugin * +inode_perm_plugin(const struct inode * inode) +{ + assert("nikita-1999", inode != NULL); + return reiser4_inode_data(inode)->pset->perm; +} + +reiser4_internal formatting_plugin * +inode_formatting_plugin(const struct inode * inode) +{ + assert("nikita-2000", inode != NULL); + return reiser4_inode_data(inode)->pset->formatting; +} + +reiser4_internal hash_plugin * +inode_hash_plugin(const struct inode * inode) +{ + assert("nikita-2001", inode != NULL); + return reiser4_inode_data(inode)->pset->hash; +} + +reiser4_internal fibration_plugin * +inode_fibration_plugin(const struct inode * inode) +{ + assert("nikita-2001", inode != NULL); + return reiser4_inode_data(inode)->pset->fibration; +} + +reiser4_internal crypto_plugin * +inode_crypto_plugin(const struct inode * inode) +{ + assert("edward-36", inode != NULL); + return reiser4_inode_data(inode)->pset->crypto; +} + +reiser4_internal compression_plugin * +inode_compression_plugin(const struct inode * inode) +{ + assert("edward-37", inode != NULL); + return reiser4_inode_data(inode)->pset->compression; +} + +reiser4_internal digest_plugin * +inode_digest_plugin(const struct inode * inode) +{ + assert("edward-86", inode != NULL); + return reiser4_inode_data(inode)->pset->digest; +} + +reiser4_internal item_plugin * +inode_sd_plugin(const struct inode * inode) +{ + assert("vs-534", inode != NULL); + return reiser4_inode_data(inode)->pset->sd; +} + +reiser4_internal item_plugin * +inode_dir_item_plugin(const struct inode * inode) +{ + assert("vs-534", inode != NULL); + return reiser4_inode_data(inode)->pset->dir_item; +} + +reiser4_internal void +inode_set_extension(struct inode *inode, sd_ext_bits ext) +{ + reiser4_inode *state; + + assert("nikita-2716", inode != NULL); + assert("nikita-2717", ext < LAST_SD_EXTENSION); + assert("nikita-3491", + spin_inode_object_is_locked(reiser4_inode_data(inode))); + + state = reiser4_inode_data(inode); + state->extmask |= 1 << ext; + /* force re-calculation of stat-data length on next call to + update_sd(). */ + inode_clr_flag(inode, REISER4_SDLEN_KNOWN); +} + +reiser4_internal void +inode_set_plugin(struct inode *inode, reiser4_plugin * plug, pset_member memb) +{ + assert("nikita-2718", inode != NULL); + assert("nikita-2719", plug != NULL); + + reiser4_inode_data(inode)->plugin_mask |= (1 << memb); +} + +reiser4_internal void +inode_check_scale(struct inode *inode, __u64 old, __u64 new) +{ + assert("nikita-2875", inode != NULL); + spin_lock_inode(inode); + if (!dscale_fit(old, new)) + inode_clr_flag(inode, REISER4_SDLEN_KNOWN); + spin_unlock_inode(inode); +} + +/* + * initialize ->ordering field of inode. This field defines how file stat-data + * and body is ordered within a tree with respect to other objects within the + * same parent directory. + */ +reiser4_internal void +init_inode_ordering(struct inode *inode, + reiser4_object_create_data *crd, int create) +{ + reiser4_key key; + + if (create) { + struct inode *parent; + + parent = crd->parent; + assert("nikita-3224", inode_dir_plugin(parent) != NULL); + inode_dir_plugin(parent)->build_entry_key(parent, + &crd->dentry->d_name, + &key); + } else { + coord_t *coord; + + coord = &reiser4_inode_data(inode)->sd_coord; + coord_clear_iplug(coord); + /* safe to use ->sd_coord, because node is under long term + * lock */ + WITH_DATA(coord->node, item_key_by_coord(coord, &key)); + } + + set_inode_ordering(inode, get_key_ordering(&key)); +} + +reiser4_internal znode * +inode_get_vroot(struct inode *inode) +{ + reiser4_block_nr blk; + znode *result; + reiser4_inode *info; + + info = reiser4_inode_data(inode); + LOCK_INODE(info); + blk = info->vroot; + UNLOCK_INODE(info); + if (!disk_addr_eq(&UBER_TREE_ADDR, &blk)) + result = zlook(tree_by_inode(inode), &blk); + else + result = NULL; + return result; +} + +reiser4_internal void +inode_set_vroot(struct inode *inode, znode *vroot) +{ + reiser4_inode *info; + + info = reiser4_inode_data(inode); + LOCK_INODE(info); + info->vroot = *znode_get_block(vroot); + UNLOCK_INODE(info); +} + +reiser4_internal void +inode_clean_vroot(struct inode *inode) +{ + reiser4_inode *info; + + info = reiser4_inode_data(inode); + LOCK_INODE(info); + info->vroot = UBER_TREE_ADDR; + UNLOCK_INODE(info); +} + +reiser4_internal int +get_reiser4_inode_by_key (struct inode ** result, const reiser4_key * key) +{ + struct super_block * super = reiser4_get_current_sb(); + struct inode * inode; + + /* We do not need to read reiser4 inode from disk and initialize all + * reiser4 inode fields. */ + inode = iget_locked(super, (unsigned long)get_key_objectid(key)); + if (inode == NULL) + return -ENOMEM; + if (is_bad_inode(inode)) { + iput(inode); + return -EIO; + } + + if (inode->i_state & I_NEW) { + reiser4_inode * inode_data = reiser4_inode_data(inode); + + /* These inode fields are required for tree traversal. */ + set_inode_oid(inode, get_key_objectid(key)); + inode_data->locality_id = get_key_locality(key); +#if REISER4_LARGE_KEY + inode_data->ordering = get_key_ordering(key); +#endif + + inode->i_mapping->a_ops = &reiser4_as_operations; + unlock_new_inode(inode); + } + + *result = inode; + return 0; +} + + +#if REISER4_DEBUG_OUTPUT +/* Debugging aid: print information about inode. */ +reiser4_internal void +print_inode(const char *prefix /* prefix to print */ , + const struct inode *i /* inode to print */ ) +{ + reiser4_key inode_key; + reiser4_inode *ref; + + if (i == NULL) { + printk("%s: inode: null\n", prefix); + return; + } + printk("%s: ino: %lu, count: %i, link: %i, mode: %o, size: %llu\n", + prefix, i->i_ino, atomic_read(&i->i_count), i->i_nlink, i->i_mode, (unsigned long long) i->i_size); + printk("\tuid: %i, gid: %i, dev: %i, rdev: %i\n", i->i_uid, i->i_gid, i->i_sb->s_dev, i->i_rdev); + printk("\tatime: [%li,%li], mtime: [%li,%li], ctime: [%li,%li]\n", + i->i_atime.tv_sec, i->i_atime.tv_nsec, + i->i_mtime.tv_sec, i->i_mtime.tv_nsec, + i->i_ctime.tv_sec, i->i_ctime.tv_nsec); + printk("\tblkbits: %i, blksize: %lu, blocks: %lu, bytes: %u\n", + i->i_blkbits, i->i_blksize, i->i_blocks, i->i_bytes); + printk("\tversion: %lu, generation: %i, state: %lu, flags: %u\n", + i->i_version, i->i_generation, i->i_state, i->i_flags); + printk("\tis_reiser4_inode: %i\n", is_reiser4_inode(i)); + print_key("\tkey", build_sd_key(i, &inode_key)); + ref = reiser4_inode_data(i); + print_plugin("\tfile", file_plugin_to_plugin(ref->pset->file)); + print_plugin("\tdir", dir_plugin_to_plugin(ref->pset->dir)); + print_plugin("\tperm", perm_plugin_to_plugin(ref->pset->perm)); + print_plugin("\tformatting", formatting_plugin_to_plugin(ref->pset->formatting)); + print_plugin("\thash", hash_plugin_to_plugin(ref->pset->hash)); + print_plugin("\tsd", item_plugin_to_plugin(ref->pset->sd)); + + /* FIXME-VS: this segfaults trying to print seal's coord */ + print_seal("\tsd_seal", &ref->sd_seal); + print_coord("\tsd_coord", &ref->sd_coord, 0); + printk("\tflags: %#lx, extmask: %#llx, pmask: %i, locality: %llu\n", + *inode_flags(i), ref->extmask, + ref->plugin_mask, ref->locality_id); +} +#endif + +#if REISER4_DEBUG +void +inode_invariant(const struct inode *inode) +{ + reiser4_inode * object; + + object = reiser4_inode_data(inode); + assert("nikita-3077", spin_inode_object_is_locked(object)); + + spin_lock_eflush(inode->i_sb); + + assert("nikita-3146", object->anonymous_eflushed >= 0 && object->captured_eflushed >= 0); + assert("nikita-3441", ergo(object->anonymous_eflushed > 0 || object->captured_eflushed > 0, + jnode_tree_by_reiser4_inode(object)->rnode != NULL)); + + spin_unlock_eflush(inode->i_sb); +} + +void +mark_inode_update(struct inode *object, int immediate) +{ + int i; + int pos; + reiser4_context *ctx; + + ctx = get_current_context(); + for (i = 0, pos = -1; i < TRACKED_DELAYED_UPDATE; ++i) { + if (ctx->dirty[i].ino == object->i_ino) { + pos = i; + break; + } else if (ctx->dirty[i].ino == 0) + pos = i; + } + if (pos == -1) + ;/*warning("nikita-3402", "Too many delayed inode updates");*/ + else if (immediate) { + ctx->dirty[pos].ino = 0; + } else { + ctx->dirty[pos].ino = object->i_ino; + ctx->dirty[pos].delayed = 1; +#ifdef CONFIG_FRAME_POINTER + ctx->dirty[pos].stack[0] = __builtin_return_address(0); + ctx->dirty[pos].stack[1] = __builtin_return_address(1); + ctx->dirty[pos].stack[2] = __builtin_return_address(2); + ctx->dirty[pos].stack[3] = __builtin_return_address(3); +#endif + } +} + + +int +delayed_inode_updates(dirty_inode_info info) +{ + int i; + + for (i = 0; i < TRACKED_DELAYED_UPDATE; ++i) { + if (info[i].ino != 0 && info[i].delayed) + return 1; + } + return 0; +} + +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/inode.h 2004-09-03 00:57:05.072268160 -0700 @@ -0,0 +1,428 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Inode functions. */ + +#if !defined( __REISER4_INODE_H__ ) +#define __REISER4_INODE_H__ + +#include "forward.h" +#include "debug.h" +#include "spin_macros.h" +#include "key.h" +#include "kcond.h" +#include "seal.h" +#include "plugin/plugin.h" +#include "plugin/cryptcompress.h" +#include "plugin/plugin_set.h" +#include "plugin/security/perm.h" +#include "plugin/pseudo/pseudo.h" +#include "vfs_ops.h" +#include "jnode.h" + +#include /* for __u?? , ino_t */ +#include /* for struct super_block, struct + * rw_semaphore, etc */ +#include +#include + +/* reiser4-specific inode flags. They are "transient" and are not + supposed to be stored on disk. Used to trace "state" of + inode +*/ +typedef enum { + /* this is light-weight inode, inheriting some state from its + parent */ + REISER4_LIGHT_WEIGHT = 0, + /* stat data wasn't yet created */ + REISER4_NO_SD = 1, + /* internal immutable flag. Currently is only used + to avoid race condition during file creation. + See comment in create_object(). */ + REISER4_IMMUTABLE = 2, + /* inode was read from storage */ + REISER4_LOADED = 3, + /* this bit is set for symlinks. inode->u.generic_ip points to target + name of symlink. */ + REISER4_GENERIC_PTR_USED = 4, + /* set if size of stat-data item for this inode is known. If this is + * set we can avoid recalculating size of stat-data on each update. */ + REISER4_SDLEN_KNOWN = 5, + /* reiser4_inode->crypt points to the crypto stat */ + REISER4_CRYPTO_STAT_LOADED = 6, + /* reiser4_inode->cluster_shift makes sense */ + REISER4_CLUSTER_KNOWN = 7, + /* cryptcompress_inode_data points to the secret key */ + REISER4_SECRET_KEY_INSTALLED = 8, + /* File (possibly) has pages corresponding to the tail items, that + * were created by ->readpage. It is set by mmap_unix_file() and + * sendfile_unix_file(). This bit is inspected by write_unix_file and + * kill-hook of tail items. It is never cleared once set. This bit is + * modified and inspected under i_sem. */ + REISER4_HAS_MMAP = 9, + /* file was partially converted. It's body consists of a mix of tail + * and extent items. */ + REISER4_PART_CONV = 10, +} reiser4_file_plugin_flags; + +/* state associated with each inode. + reiser4 inode. + + NOTE-NIKITA In 2.5 kernels it is not necessary that all file-system inodes + be of the same size. File-system allocates inodes by itself through + s_op->allocate_inode() method. So, it is possible to adjust size of inode + at the time of its creation. + + + Invariants involving parts of this data-type: + + [inode->eflushed] + +*/ + +typedef struct reiser4_inode reiser4_inode; +/* return pointer to reiser4-specific part of inode */ +static inline reiser4_inode * +reiser4_inode_data(const struct inode * inode /* inode queried */); + +#include "plugin/file/file.h" + +#if BITS_PER_LONG == 64 + +#define REISER4_INO_IS_OID (1) +typedef struct {; +} oid_hi_t; + +/* BITS_PER_LONG == 64 */ +#else + +#define REISER4_INO_IS_OID (0) +typedef __u32 oid_hi_t; + +/* BITS_PER_LONG == 64 */ +#endif + +struct reiser4_inode { + /* spin lock protecting fields of this structure. */ + reiser4_spin_data guard; + /* object plugins */ + plugin_set *pset; + /* plugins set for inheritance */ + plugin_set *hset; + /* high 32 bits of object id */ + oid_hi_t oid_hi; + /* seal for stat-data */ + seal_t sd_seal; + /* locality id for this file */ + oid_t locality_id; +#if REISER4_LARGE_KEY + __u64 ordering; +#endif + /* coord of stat-data in sealed node */ + coord_t sd_coord; + /* bit-mask of stat-data extentions used by this file */ + __u64 extmask; + /* bitmask of non-default plugins for this inode */ + __u16 plugin_mask; + /* cluster parameter for crypto and compression */ + __u8 cluster_shift; + /* secret key parameter for crypto */ + crypto_stat_t *crypt; + + union { + readdir_list_head readdir_list; + struct list_head not_used; + } lists; + /* per-inode flags. Filled by values of reiser4_file_plugin_flags */ + unsigned long flags; + union { + /* fields specific to unix_file plugin */ + unix_file_info_t unix_file_info; + /* fields specific to cryptcompress plugin */ + cryptcompress_info_t cryptcompress_info; + /* fields specific to pseudo file plugin */ + pseudo_info_t pseudo_info; + } file_plugin_data; + struct rw_semaphore coc_sem; /* filemap_nopage takes it for read, copy_on_capture - for write. Under this it + tries to unmap page for which it is called. This prevents process from using page which + was copied on capture */ + /* tree of jnodes. Jnodes in this tree are distinguished by radix tree + tags */ + struct radix_tree_root jnodes_tree; +#if REISER4_DEBUG + /* list of jnodes. Number of jnodes in this list is the above jnodes field */ + inode_jnodes_list_head jnodes_list; + + /* numbers of eflushed jnodes of each type in the above tree */ + int anonymous_eflushed; + int captured_eflushed; + /* number of unformatted node jnodes of this file in jnode hash table */ + unsigned long nr_jnodes; +#endif + + /* block number of virtual root for this object. See comment above + * fs/reiser4/search.c:handle_vroot() */ + reiser4_block_nr vroot; + struct semaphore loading; +}; + + +#define I_JNODES (512) /* inode state bit. Set when in hash table there are more than 0 jnodes of unformatted nodes of + an inode */ + +typedef struct reiser4_inode_object { + /* private part */ + reiser4_inode p; + /* generic fields not specific to reiser4, but used by VFS */ + struct inode vfs_inode; +} reiser4_inode_object; + +/* return pointer to the reiser4 specific portion of @inode */ +static inline reiser4_inode * +reiser4_inode_data(const struct inode * inode /* inode queried */) +{ + assert("nikita-254", inode != NULL); + return &container_of(inode, reiser4_inode_object, vfs_inode)->p; +} + +static inline struct inode * +inode_by_reiser4_inode(const reiser4_inode *r4_inode /* inode queried */) +{ + return &container_of(r4_inode, reiser4_inode_object, p)->vfs_inode; +} + +/* + * reiser4 inodes are identified by 64bit object-id (oid_t), but in struct + * inode ->i_ino field is of type ino_t (long) that can be either 32 or 64 + * bits. + * + * If ->i_ino is 32 bits we store remaining 32 bits in reiser4 specific part + * of inode, otherwise whole oid is stored in i_ino. + * + * Wrappers below ([sg]et_inode_oid()) are used to hide this difference. + */ + +#define OID_HI_SHIFT (sizeof(ino_t) * 8) + +#if REISER4_INO_IS_OID + +static inline oid_t +get_inode_oid(const struct inode *inode) +{ + return inode->i_ino; +} + +static inline void +set_inode_oid(struct inode *inode, oid_t oid) +{ + inode->i_ino = oid; +} + +/* REISER4_INO_IS_OID */ +#else + +static inline oid_t +get_inode_oid(const struct inode *inode) +{ + return + ((__u64)reiser4_inode_data(inode)->oid_hi << OID_HI_SHIFT) | + inode->i_ino; +} + +static inline void +set_inode_oid(struct inode *inode, oid_t oid) +{ + assert("nikita-2519", inode != NULL); + inode->i_ino = (ino_t)(oid); + reiser4_inode_data(inode)->oid_hi = (oid) >> OID_HI_SHIFT; + assert("nikita-2521", get_inode_oid(inode) == (oid)); +} + +/* REISER4_INO_IS_OID */ +#endif + +static inline oid_t +get_inode_locality(const struct inode *inode) +{ + return reiser4_inode_data(inode)->locality_id; +} + +#if REISER4_LARGE_KEY +static inline __u64 get_inode_ordering(const struct inode *inode) +{ + return reiser4_inode_data(inode)->ordering; +} + +static inline void set_inode_ordering(const struct inode *inode, __u64 ordering) +{ + reiser4_inode_data(inode)->ordering = ordering; +} + +#else + +#define get_inode_ordering(inode) (0) +#define set_inode_ordering(inode, val) noop + +#endif + +/* return inode in which @uf_info is embedded */ +static inline struct inode * +unix_file_info_to_inode(const unix_file_info_t *uf_info) +{ + return &container_of(uf_info, reiser4_inode_object, + p.file_plugin_data.unix_file_info)->vfs_inode; +} + +/* ordering predicate for inode spin lock: only jnode lock can be held */ +#define spin_ordering_pred_inode_object(inode) \ + ( lock_counters() -> rw_locked_dk == 0 ) && \ + ( lock_counters() -> rw_locked_tree == 0 ) && \ + ( lock_counters() -> spin_locked_txnh == 0 ) && \ + ( lock_counters() -> rw_locked_zlock == 0 ) && \ + ( lock_counters() -> spin_locked_jnode == 0 ) && \ + ( lock_counters() -> spin_locked_atom == 0 ) && \ + ( lock_counters() -> spin_locked_ktxnmgrd == 0 ) && \ + ( lock_counters() -> spin_locked_txnmgr == 0 ) + +SPIN_LOCK_FUNCTIONS(inode_object, reiser4_inode, guard); + +extern ino_t oid_to_ino(oid_t oid) __attribute__ ((const)); +extern ino_t oid_to_uino(oid_t oid) __attribute__ ((const)); + +extern reiser4_tree *tree_by_inode(const struct inode *inode); + +#if REISER4_DEBUG +extern void inode_invariant(const struct inode *inode); +#else +#define inode_invariant(inode) noop +#endif + +#define spin_lock_inode(inode) \ +({ \ + LOCK_INODE(reiser4_inode_data(inode)); \ + inode_invariant(inode); \ +}) + +#define spin_unlock_inode(inode) \ +({ \ + inode_invariant(inode); \ + UNLOCK_INODE(reiser4_inode_data(inode)); \ +}) + +extern znode *inode_get_vroot(struct inode *inode); +extern void inode_set_vroot(struct inode *inode, znode *vroot); +extern void inode_clean_vroot(struct inode *inode); + +extern int reiser4_max_filename_len(const struct inode *inode); +extern int max_hash_collisions(const struct inode *dir); +extern void reiser4_unlock_inode(struct inode *inode); +extern int is_reiser4_inode(const struct inode *inode); +extern int setup_inode_ops(struct inode *inode, reiser4_object_create_data *); +extern struct inode *reiser4_iget(struct super_block *super, const reiser4_key * key, int silent); +extern void reiser4_iget_complete (struct inode * inode); +extern int reiser4_inode_find_actor(struct inode *inode, void *opaque); +extern int get_reiser4_inode_by_key (struct inode **, const reiser4_key *); + + +extern void inode_set_flag(struct inode *inode, reiser4_file_plugin_flags f); +extern void inode_clr_flag(struct inode *inode, reiser4_file_plugin_flags f); +extern int inode_get_flag(const struct inode *inode, reiser4_file_plugin_flags f); + +/* has inode been initialized? */ +static inline int +is_inode_loaded(const struct inode *inode /* inode queried */ ) +{ + assert("nikita-1120", inode != NULL); + return inode_get_flag(inode, REISER4_LOADED); +} + +extern file_plugin *inode_file_plugin(const struct inode *inode); +extern dir_plugin *inode_dir_plugin(const struct inode *inode); +extern perm_plugin *inode_perm_plugin(const struct inode *inode); +extern formatting_plugin *inode_formatting_plugin(const struct inode *inode); +extern hash_plugin *inode_hash_plugin(const struct inode *inode); +extern fibration_plugin *inode_fibration_plugin(const struct inode *inode); +extern crypto_plugin *inode_crypto_plugin(const struct inode *inode); +extern digest_plugin *inode_digest_plugin(const struct inode *inode); +extern compression_plugin *inode_compression_plugin(const struct inode *inode); +extern item_plugin *inode_sd_plugin(const struct inode *inode); +extern item_plugin *inode_dir_item_plugin(const struct inode *inode); + +extern void inode_set_plugin(struct inode *inode, + reiser4_plugin * plug, pset_member memb); +extern void reiser4_make_bad_inode(struct inode *inode); + +extern void inode_set_extension(struct inode *inode, sd_ext_bits ext); +extern void inode_check_scale(struct inode *inode, __u64 old, __u64 new); + +/* + * update field @field in inode @i to contain value @value. + */ +#define INODE_SET_FIELD(i, field, value) \ +({ \ + struct inode *__i; \ + typeof(value) __v; \ + \ + __i = (i); \ + __v = (value); \ + inode_check_scale(__i, __i->field, __v); \ + __i->field = __v; \ +}) + +#define INODE_INC_FIELD(i, field) \ +({ \ + struct inode *__i; \ + \ + __i = (i); \ + inode_check_scale(__i, __i->field, __i->field + 1); \ + ++ __i->field; \ +}) + +#define INODE_DEC_FIELD(i, field) \ +({ \ + struct inode *__i; \ + \ + __i = (i); \ + inode_check_scale(__i, __i->field, __i->field - 1); \ + -- __i->field; \ +}) + +/* See comment before readdir_common() for description. */ +static inline readdir_list_head * +get_readdir_list(const struct inode *inode) +{ + return &reiser4_inode_data(inode)->lists.readdir_list; +} + +extern void init_inode_ordering(struct inode *inode, + reiser4_object_create_data *crd, int create); + +static inline struct radix_tree_root * +jnode_tree_by_inode(struct inode *inode) +{ + return &reiser4_inode_data(inode)->jnodes_tree; +} + +static inline struct radix_tree_root * +jnode_tree_by_reiser4_inode(reiser4_inode *r4_inode) +{ + return &r4_inode->jnodes_tree; +} + +#if REISER4_DEBUG_OUTPUT +extern void print_inode(const char *prefix, const struct inode *i); +#else +#define print_inode(p, i) noop +#endif + +/* __REISER4_INODE_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/inode_ops.c 2004-09-03 00:57:07.758859736 -0700 @@ -0,0 +1,644 @@ +/* Copyright 2003, 2004 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Interface to VFS. Reiser4 inode_operations are defined here. */ + +#include "forward.h" +#include "debug.h" +#include "dformat.h" +#include "coord.h" +#include "plugin/item/item.h" +#include "plugin/file/file.h" +#include "plugin/security/perm.h" +#include "plugin/disk_format/disk_format.h" +#include "plugin/plugin.h" +#include "plugin/plugin_set.h" +#include "plugin/object.h" +#include "txnmgr.h" +#include "jnode.h" +#include "znode.h" +#include "block_alloc.h" +#include "tree.h" +#include "log.h" +#include "vfs_ops.h" +#include "inode.h" +#include "page_cache.h" +#include "ktxnmgrd.h" +#include "super.h" +#include "reiser4.h" +#include "kattr.h" +#include "entd.h" +#include "emergency_flush.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* inode operations */ + +static int reiser4_create(struct inode *, struct dentry *, int, + struct nameidata *); +static struct dentry *reiser4_lookup(struct inode *, struct dentry *, + struct nameidata *); +static int reiser4_link(struct dentry *, struct inode *, struct dentry *); +static int reiser4_unlink(struct inode *, struct dentry *); +static int reiser4_rmdir(struct inode *, struct dentry *); +static int reiser4_symlink(struct inode *, struct dentry *, const char *); +static int reiser4_mkdir(struct inode *, struct dentry *, int); +static int reiser4_mknod(struct inode *, struct dentry *, int, dev_t); +static int reiser4_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); +static int reiser4_readlink(struct dentry *, char *, int); +static int reiser4_follow_link(struct dentry *, struct nameidata *); +static void reiser4_truncate(struct inode *); +static int reiser4_permission(struct inode *, int, struct nameidata *); +static int reiser4_setattr(struct dentry *, struct iattr *); +static int reiser4_getattr(struct vfsmount *mnt, struct dentry *, struct kstat *); + +#if 0 +static int reiser4_setxattr(struct dentry *, const char *, void *, size_t, int); +static ssize_t reiser4_getxattr(struct dentry *, const char *, void *, size_t); +static ssize_t reiser4_listxattr(struct dentry *, char *, size_t); +static int reiser4_removexattr(struct dentry *, const char *); +#endif + +reiser4_internal int invoke_create_method(struct inode *parent, + struct dentry *dentry, + reiser4_object_create_data * data); + +/* ->create() VFS method in reiser4 inode_operations */ +static int +reiser4_create(struct inode *parent /* inode of parent + * directory */, + struct dentry *dentry /* dentry of new object to + * create */, + int mode /* new object mode */, + struct nameidata *nameidata) +{ + reiser4_object_create_data data; + xmemset(&data, 0, sizeof data); + + reiser4_stat_inc_at(parent->i_sb, vfs_calls.create); + + data.mode = S_IFREG | mode; + data.id = UNIX_FILE_PLUGIN_ID; + return invoke_create_method(parent, dentry, &data); +} + +/* ->mkdir() VFS method in reiser4 inode_operations */ +static int +reiser4_mkdir(struct inode *parent /* inode of parent + * directory */ , + struct dentry *dentry /* dentry of new object to + * create */ , + int mode /* new object's mode */ ) +{ + reiser4_object_create_data data; + + reiser4_stat_inc_at(parent->i_sb, vfs_calls.mkdir); + + data.mode = S_IFDIR | mode; + data.id = DIRECTORY_FILE_PLUGIN_ID; + return invoke_create_method(parent, dentry, &data); +} + +/* ->symlink() VFS method in reiser4 inode_operations */ +static int +reiser4_symlink(struct inode *parent /* inode of parent + * directory */ , + struct dentry *dentry /* dentry of new object to + * create */ , + const char *linkname /* pathname to put into + * symlink */ ) +{ + reiser4_object_create_data data; + + reiser4_stat_inc_at(parent->i_sb, vfs_calls.symlink); + + data.name = linkname; + data.id = SYMLINK_FILE_PLUGIN_ID; + data.mode = S_IFLNK | S_IRWXUGO; + return invoke_create_method(parent, dentry, &data); +} + +/* ->mknod() VFS method in reiser4 inode_operations */ +static int +reiser4_mknod(struct inode *parent /* inode of parent directory */ , + struct dentry *dentry /* dentry of new object to + * create */ , + int mode /* new object's mode */ , + dev_t rdev /* minor and major of new device node */ ) +{ + reiser4_object_create_data data; + + reiser4_stat_inc_at(parent->i_sb, vfs_calls.mknod); + + data.mode = mode; + data.rdev = rdev; + data.id = SPECIAL_FILE_PLUGIN_ID; + return invoke_create_method(parent, dentry, &data); +} + +/* ->rename() inode operation */ +static int +reiser4_rename(struct inode *old_dir, struct dentry *old, struct inode *new_dir, struct dentry *new) +{ + int result; + reiser4_context ctx; + + assert("nikita-2314", old_dir != NULL); + assert("nikita-2315", old != NULL); + assert("nikita-2316", new_dir != NULL); + assert("nikita-2317", new != NULL); + + init_context(&ctx, old_dir->i_sb); + reiser4_stat_inc(vfs_calls.rename); + + result = perm_chk(old_dir, rename, old_dir, old, new_dir, new); + if (result == 0) { + dir_plugin *dplug; + + dplug = inode_dir_plugin(old_dir); + if (dplug == NULL) + result = RETERR(-ENOTDIR); + else if (dplug->rename == NULL) + result = RETERR(-EPERM); + else + result = dplug->rename(old_dir, old, new_dir, new); + } + context_set_commit_async(&ctx); + reiser4_exit_context(&ctx); + return result; +} + +/* reiser4_lookup() - entry point for ->lookup() method. + + This is a wrapper for lookup_object which is a wrapper for the directory + plugin that does the lookup. + + This is installed in ->lookup() in reiser4_inode_operations. +*/ +static struct dentry * +reiser4_lookup(struct inode *parent, /* directory within which we are to + * look for the name specified in + * dentry */ + struct dentry *dentry, /* this contains the name that is to + be looked for on entry, and on exit + contains a filled in dentry with a + pointer to the inode (unless name + not found) */ + struct nameidata *nameidata) +{ + dir_plugin *dplug; + int retval; + struct dentry *result; + reiser4_context ctx; + int (*lookup) (struct inode * parent_inode, struct dentry **dentry); + + assert("nikita-403", parent != NULL); + assert("nikita-404", dentry != NULL); + + init_context(&ctx, parent->i_sb); + reiser4_stat_inc(vfs_calls.lookup); + + /* find @parent directory plugin and make sure that it has lookup + method */ + dplug = inode_dir_plugin(parent); + if (dplug != NULL && dplug->lookup != NULL) + /* if parent directory has directory plugin with ->lookup + * method, use the latter to do lookup */ + lookup = dplug->lookup; + else if (!reiser4_is_set(parent->i_sb, REISER4_NO_PSEUDO)) + /* even if there is no ->lookup method, pseudo file lookup + * should still be performed, but only unless we are in + * "no-pseudo" mode */ + lookup = lookup_pseudo_file; + else + lookup = NULL; + if (lookup != NULL) { + struct dentry *name; + + name = dentry; + /* call its lookup method */ + retval = lookup(parent, &name); + if (retval == 0) { + if (name == NULL) { + /* + * new object was looked up. Initialize it. + */ + struct inode *obj; + file_plugin *fplug; + + obj = dentry->d_inode; + assert("nikita-2645", obj != NULL); + fplug = inode_file_plugin(obj); + retval = fplug->bind(obj, parent); + } + } else if (retval == -ENOENT) { + /* object not found */ + d_add(dentry, NULL); + retval = 0; + name = NULL; + } + + if (retval == 0) + /* success */ + result = name; + else + result = ERR_PTR(retval); + } else + result = ERR_PTR(-ENOTDIR); + + /* prevent balance_dirty_pages() from being called: we don't want to + * do this under directory i_sem. */ + context_set_commit_async(&ctx); + reiser4_exit_context(&ctx); + return result; +} + +/* ->readlink() inode method, returns content of symbolic link */ +static int +reiser4_readlink(struct dentry *dentry, char *buf, int buflen) +{ + assert("vs-852", S_ISLNK(dentry->d_inode->i_mode)); + reiser4_stat_inc_at(dentry->d_inode->i_sb, vfs_calls.readlink); + if (!dentry->d_inode->u.generic_ip || !inode_get_flag(dentry->d_inode, REISER4_GENERIC_PTR_USED)) + return RETERR(-EINVAL); + return vfs_readlink(dentry, buf, buflen, dentry->d_inode->u.generic_ip); +} + +/* ->follow_link() inode method. Follows a symbolic link */ +static int +reiser4_follow_link(struct dentry *dentry, struct nameidata *data) +{ + assert("vs-851", S_ISLNK(dentry->d_inode->i_mode)); + + reiser4_stat_inc_at(dentry->d_inode->i_sb, vfs_calls.follow_link); + if (!dentry->d_inode->u.generic_ip || !inode_get_flag(dentry->d_inode, REISER4_GENERIC_PTR_USED)) + return RETERR(-EINVAL); + return vfs_follow_link(data, dentry->d_inode->u.generic_ip); +} + +/* ->setattr() inode operation + + Called from notify_change. */ +static int +reiser4_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode; + int result; + reiser4_context ctx; + + assert("nikita-2269", attr != NULL); + + inode = dentry->d_inode; + assert("vs-1108", inode != NULL); + init_context(&ctx, inode->i_sb); + reiser4_stat_inc(vfs_calls.setattr); + result = perm_chk(inode, setattr, dentry, attr); + if (result == 0) { + if (!inode_get_flag(inode, REISER4_IMMUTABLE)) { + file_plugin *fplug; + + fplug = inode_file_plugin(inode); + assert("nikita-2271", fplug != NULL); + assert("nikita-2296", fplug->setattr != NULL); + result = fplug->setattr(inode, attr); + } else + result = RETERR(-E_REPEAT); + } + context_set_commit_async(&ctx); + reiser4_exit_context(&ctx); + return result; +} + +/* ->getattr() inode operation called (indirectly) by sys_stat(). */ +static int +reiser4_getattr(struct vfsmount *mnt UNUSED_ARG, struct dentry *dentry, struct kstat *stat) +{ + struct inode *inode; + int result; + reiser4_context ctx; + + inode = dentry->d_inode; + init_context(&ctx, inode->i_sb); + reiser4_stat_inc(vfs_calls.getattr); + result = perm_chk(inode, getattr, mnt, dentry, stat); + if (result == 0) { + file_plugin *fplug; + + fplug = inode_file_plugin(inode); + assert("nikita-2295", fplug != NULL); + assert("nikita-2297", fplug->getattr != NULL); + result = fplug->getattr(mnt, dentry, stat); + } + reiser4_exit_context(&ctx); + return result; +} + +/* helper function: call object plugin to truncate file to @size */ +static int +truncate_object(struct inode *inode /* object to truncate */ , + loff_t size /* size to truncate object to */ ) +{ + file_plugin *fplug; + int result; + + assert("nikita-1026", inode != NULL); + assert("nikita-1027", is_reiser4_inode(inode)); + assert("nikita-1028", inode->i_sb != NULL); + + write_syscall_log("%llu %lli", get_inode_oid(inode), size); + + fplug = inode_file_plugin(inode); + assert("vs-142", fplug != NULL); + + assert("nikita-2933", fplug->truncate != NULL); + result = fplug->truncate(inode, size); + if (result != 0) + warning("nikita-1602", "Truncate error: %i for %lli", result, + (unsigned long long)get_inode_oid(inode)); + + write_syscall_log("ex"); + return result; +} + +/* ->truncate() VFS method in reiser4 inode_operations */ +static void +reiser4_truncate(struct inode *inode /* inode to truncate */ ) +{ + reiser4_context ctx; + + assert("umka-075", inode != NULL); + + init_context(&ctx, inode->i_sb); + reiser4_stat_inc(vfs_calls.truncate); + ON_TRACE(TRACE_VFS_OPS, "TRUNCATE: i_ino %li to size %lli\n", inode->i_ino, inode->i_size); + + truncate_object(inode, inode->i_size); + + /* for mysterious reasons ->truncate() VFS call doesn't return + value */ + reiser4_exit_context(&ctx); +} + +/* ->permission() method in reiser4_inode_operations. */ +static int +reiser4_permission(struct inode *inode /* object */ , + int mask, /* mode bits to check permissions + * for */ + struct nameidata *nameidata) +{ + /* reiser4_context creation/destruction removed from here, + because permission checks currently don't require this. + + Permission plugin have to create context itself if necessary. */ + assert("nikita-1687", inode != NULL); + + return perm_chk(inode, mask, inode, mask); +} + +/* common part of both unlink and rmdir. */ +static int +unlink_file(struct inode *parent /* parent directory */ , + struct dentry *victim /* name of object being + * unlinked */ ) +{ + int result; + dir_plugin *dplug; + reiser4_context ctx; + + init_context(&ctx, parent->i_sb); + write_syscall_log("%s", victim->d_name.name); + + assert("nikita-1435", parent != NULL); + assert("nikita-1436", victim != NULL); + + ON_TRACE(TRACE_DIR | TRACE_VFS_OPS, "unlink: %lli/%s\n", + get_inode_oid(parent), victim->d_name.name); + + dplug = inode_dir_plugin(parent); + assert("nikita-1429", dplug != NULL); + if (dplug->unlink != NULL) + result = dplug->unlink(parent, victim); + else + result = RETERR(-EPERM); + write_syscall_log("ex"); + /* @victim can be already removed from the disk by this time. Inode is + then marked so that iput() wouldn't try to remove stat data. But + inode itself is still there. + */ + /* we cannot release directory semaphore here, because name has + * already been deleted, but dentry (@victim) still exists. */ + /* prevent balance_dirty_pages() from being called: we don't want to + * do this under directory i_sem. */ + + context_set_commit_async(&ctx); + reiser4_exit_context(&ctx); + return result; +} + +/* ->unlink() VFS method in reiser4 inode_operations + + remove link from @parent directory to @victim object: delegate work + to object plugin +*/ +/* Audited by: umka (2002.06.12) */ +static int +reiser4_unlink(struct inode *parent /* parent directory */ , + struct dentry *victim /* name of object being + * unlinked */ ) +{ + assert("nikita-2011", parent != NULL); + assert("nikita-2012", victim != NULL); + assert("nikita-2013", victim->d_inode != NULL); + reiser4_stat_inc_at(parent->i_sb,vfs_calls.unlink); + if (inode_dir_plugin(victim->d_inode) == NULL) + return unlink_file(parent, victim); + else + return RETERR(-EISDIR); +} + +/* ->rmdir() VFS method in reiser4 inode_operations + + The same as unlink, but only for directories. + +*/ +/* Audited by: umka (2002.06.12) */ +static int +reiser4_rmdir(struct inode *parent /* parent directory */ , + struct dentry *victim /* name of directory being + * unlinked */ ) +{ + assert("nikita-2014", parent != NULL); + assert("nikita-2015", victim != NULL); + assert("nikita-2016", victim->d_inode != NULL); + + reiser4_stat_inc_at(parent->i_sb, vfs_calls.rmdir); + if (inode_dir_plugin(victim->d_inode) != NULL) + /* there is no difference between unlink and rmdir for + reiser4 */ + return unlink_file(parent, victim); + else + return RETERR(-ENOTDIR); +} + +/* ->link() VFS method in reiser4 inode_operations + + entry point for ->link() method. + + This is installed as ->link inode operation for reiser4 + inodes. Delegates all work to object plugin +*/ +/* Audited by: umka (2002.06.12) */ +static int +reiser4_link(struct dentry *existing /* dentry of existing + * object */ , + struct inode *parent /* parent directory */ , + struct dentry *where /* new name for @existing */ ) +{ + int result; + dir_plugin *dplug; + reiser4_context ctx; + + assert("umka-080", existing != NULL); + assert("nikita-1031", parent != NULL); + + init_context(&ctx, parent->i_sb); + context_set_commit_async(&ctx); + reiser4_stat_inc(vfs_calls.link); + + dplug = inode_dir_plugin(parent); + assert("nikita-1430", dplug != NULL); + if (dplug->link != NULL) { + result = dplug->link(parent, existing, where); + if (result == 0) + d_instantiate(where, existing->d_inode); + } else { + result = RETERR(-EPERM); + } + up(&existing->d_inode->i_sem); + up(&parent->i_sem); + reiser4_exit_context(&ctx); + down(&parent->i_sem); + down(&existing->d_inode->i_sem); + return result; +} + +/* call ->create() directory plugin method. */ +reiser4_internal int +invoke_create_method(struct inode *parent /* parent directory */ , + struct dentry *dentry /* dentry of new + * object */ , + reiser4_object_create_data * data /* information + * necessary + * to create + * new + * object */ ) +{ + int result; + dir_plugin *dplug; + reiser4_context ctx; + + init_context(&ctx, parent->i_sb); + context_set_commit_async(&ctx); + write_syscall_log("%s %o", dentry->d_name.name, data->mode); + + assert("nikita-426", parent != NULL); + assert("nikita-427", dentry != NULL); + assert("nikita-428", data != NULL); + + dplug = inode_dir_plugin(parent); + if (dplug == NULL) + result = RETERR(-ENOTDIR); + else if (dplug->create_child != NULL) { + struct inode *child; + + child = NULL; + + data->parent = parent; + data->dentry = dentry; + + result = dplug->create_child(data, &child); + if (unlikely(result != 0)) { + if (child != NULL) { + /* + * what we actually want to check in the + * assertion below is that @child only + * contains items that iput()->... is going to + * remove (usually stat-data). Obvious check + * for child->i_size == 0 doesn't work for + * symlinks. + */ + assert("nikita-3140", S_ISLNK(child->i_mode) || + child->i_size == 0); + reiser4_make_bad_inode(child); + iput(child); + } + } else { + d_instantiate(dentry, child); + ON_TRACE(TRACE_VFS_OPS, "create: %s (%o) %llu\n", + dentry->d_name.name, + data->mode, get_inode_oid(child)); + } + } else + result = RETERR(-EPERM); + + write_syscall_log("ex"); + + reiser4_exit_context(&ctx); + return result; +} + +struct inode_operations reiser4_inode_operations = { + .create = reiser4_create, /* d */ + .lookup = reiser4_lookup, /* d */ + .link = reiser4_link, /* d */ + .unlink = reiser4_unlink, /* d */ + .symlink = reiser4_symlink, /* d */ + .mkdir = reiser4_mkdir, /* d */ + .rmdir = reiser4_rmdir, /* d */ + .mknod = reiser4_mknod, /* d */ + .rename = reiser4_rename, /* d */ + .readlink = NULL, + .follow_link = NULL, + .truncate = reiser4_truncate, /* d */ + .permission = reiser4_permission, /* d */ + .setattr = reiser4_setattr, /* d */ + .getattr = reiser4_getattr, /* d */ +}; + +struct inode_operations reiser4_symlink_inode_operations = { + .setattr = reiser4_setattr, /* d */ + .getattr = reiser4_getattr, /* d */ + .readlink = reiser4_readlink, + .follow_link = reiser4_follow_link +}; + +struct inode_operations reiser4_special_inode_operations = { + .setattr = reiser4_setattr, /* d */ + .getattr = reiser4_getattr /* d */ +}; + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/interpolate.c 2004-09-03 00:57:05.076267552 -0700 @@ -0,0 +1,20 @@ +/* We will use @ as the symbol for dereferencing, we won't use * because +we want to reserve it for use as a wildcard someday. + +Inheriting stat data from source_filename can be done as: + +target_filename/mode<=@source_filename/mode + +File body inheritance is accomplished by extending symlink functionality: + +file_body_inheritance example: + +target_filename/symlink<=`@freshly_interpolate_this_filename_whenever_resolving_target_filename+`here is some text stored directly in the symlink''+@interpolate_this_filename_at_symlink_creation_time+`@freshly_interpolate_this_filename2_whenever_resolving_target_filename+"this is some more text that is directly embedded in the symlink"' + +Mr. Demidov, flesh this out in detail, being careful to worry about +how to write to interpolated files. I think you need to interpret +strings that are between interpolations as the delimiters of those +interpolations, and changing those strings can then only be done by +writing to filename/sym. + +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/ioctl.h 2004-09-03 00:57:05.076267552 -0700 @@ -0,0 +1,41 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +#if !defined( __REISER4_IOCTL_H__ ) +#define __REISER4_IOCTL_H__ + +#include + +/* + * ioctl(2) command used to "unpack" reiser4 file, that is, convert it into + * extents and fix in this state. This is used by applications that rely on + * + * . files being block aligned, and + * + * . files never migrating on disk + * + * for example, boot loaders (LILO) need this. + * + * This ioctl should be used as + * + * result = ioctl(fd, REISER4_IOC_UNPACK); + * + * File behind fd descriptor will be converted to the extents (if necessary), + * and its stat-data will be updated so that it will never be converted back + * into tails again. + */ +#define REISER4_IOC_UNPACK _IOW(0xCD,1,long) + +/* __REISER4_IOCTL_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/jnode.c 2004-09-03 00:57:06.857996688 -0700 @@ -0,0 +1,2100 @@ +/* Copyright 2001, 2002, 2003, 2004 by Hans Reiser, licensing governed by + * reiser4/README */ +/* Jnode manipulation functions. */ +/* Jnode is entity used to track blocks with data and meta-data in reiser4. + + In particular, jnodes are used to track transactional information + associated with each block. Each znode contains jnode as ->zjnode field. + + Jnode stands for either Josh or Journal node. +*/ + +/* + * Taxonomy. + * + * Jnode represents block containing data or meta-data. There are jnodes + * for: + * + * unformatted blocks (jnodes proper). There are plans, however to + * have a handle per extent unit rather than per each unformatted + * block, because there are so many of them. + * + * For bitmaps. Each bitmap is actually represented by two jnodes--one + * for working and another for "commit" data, together forming bnode. + * + * For io-heads. These are used by log writer. + * + * For formatted nodes (znode). See comment at the top of znode.c for + * details specific to the formatted nodes (znodes). + * + * Node data. + * + * Jnode provides access to the data of node it represents. Data are + * stored in a page. Page is kept in a page cache. This means, that jnodes + * are highly interconnected with page cache and VM internals. + * + * jnode has a pointer to page (->pg) containing its data. Pointer to data + * themselves is cached in ->data field to avoid frequent calls to + * page_address(). + * + * jnode and page are attached to each other by jnode_attach_page(). This + * function places pointer to jnode in page->private, sets PG_private flag + * and increments page counter. + * + * Opposite operation is performed by page_clear_jnode(). + * + * jnode->pg is protected by jnode spin lock, and page->private is + * protected by page lock. See comment at the top of page_cache.c for + * more. + * + * page can be detached from jnode for two reasons: + * + * . jnode is removed from a tree (file is truncated, of formatted + * node is removed by balancing). + * + * . during memory pressure, VM calls ->releasepage() method + * (reiser4_releasepage()) to evict page from memory. + * + * (there, of course, is also umount, but this is special case we are not + * concerned with here). + * + * To protect jnode page from eviction, one calls jload() function that + * "pins" page in memory (loading it if necessary), increments + * jnode->d_count, and kmap()s page. Page is unpinned through call to + * jrelse(). + * + * Jnode life cycle. + * + * jnode is created, placed in hash table, and, optionally, in per-inode + * radix tree. Page can be attached to jnode, pinned, released, etc. + * + * When jnode is captured into atom its reference counter is + * increased. While being part of an atom, jnode can be "early + * flushed". This means that as part of flush procedure, jnode is placed + * into "relocate set", and its page is submitted to the disk. After io + * completes, page can be detached, then loaded again, re-dirtied, etc. + * + * Thread acquired reference to jnode by calling jref() and releases it by + * jput(). When last reference is removed, jnode is still retained in + * memory (cached) if it has page attached, _unless_ it is scheduled for + * destruction (has JNODE_HEARD_BANSHEE bit set). + * + * Tree read-write lock was used as "existential" lock for jnodes. That is, + * jnode->x_count could be changed from 0 to 1 only under tree write lock, + * that is, tree lock protected unreferenced jnodes stored in the hash + * table, from recycling. + * + * This resulted in high contention on tree lock, because jref()/jput() is + * frequent operation. To ameliorate this problem, RCU is used: when jput() + * is just about to release last reference on jnode it sets JNODE_RIP bit + * on it, and then proceed with jnode destruction (removing jnode from hash + * table, cbk_cache, detaching page, etc.). All places that change jnode + * reference counter from 0 to 1 (jlookup(), zlook(), zget(), and + * cbk_cache_scan_slots()) check for JNODE_RIP bit (this is done by + * jnode_rip_check() function), and pretend that nothing was found in hash + * table if bit is set. + * + * jput defers actual return of jnode into slab cache to some later time + * (by call_rcu()), this guarantees that other threads can safely continue + * working with JNODE_RIP-ped jnode. + * + */ + +#include "reiser4.h" +#include "debug.h" +#include "dformat.h" +#include "plugin/plugin_header.h" +#include "plugin/plugin.h" +#include "txnmgr.h" +#include "jnode.h" +#include "znode.h" +#include "tree.h" +#include "tree_walk.h" +#include "super.h" +#include "inode.h" +#include "page_cache.h" +#include "prof.h" + +#include /* UML needs this for PAGE_OFFSET */ +#include +#include +#include +#include /* for vmalloc(), vfree() */ +#include +#include /* for struct address_space */ +#include /* for inode_lock */ + +static kmem_cache_t *_jnode_slab = NULL; + +static void jnode_set_type(jnode * node, jnode_type type); + + +/* true if valid page is attached to jnode */ +static inline int jnode_is_parsed (jnode * node) +{ + return JF_ISSET(node, JNODE_PARSED); +} + +/* hash table support */ + +/* compare two jnode keys for equality. Used by hash-table macros */ +static inline int +jnode_key_eq(const jnode_key_t * k1, const jnode_key_t * k2) +{ + assert("nikita-2350", k1 != NULL); + assert("nikita-2351", k2 != NULL); + + return (k1->index == k2->index && k1->objectid == k2->objectid); +} + +/* Hash jnode by its key (inode plus offset). Used by hash-table macros */ +static inline __u32 +jnode_key_hashfn(j_hash_table *table, const jnode_key_t * key) +{ + assert("nikita-2352", key != NULL); + assert("nikita-3346", IS_POW(table->_buckets)); + + /* yes, this is remarkable simply (where not stupid) hash function. */ + return (key->objectid + key->index) & (table->_buckets - 1); +} + +/* The hash table definition */ +#define KMALLOC(size) vmalloc(size) +#define KFREE(ptr, size) vfree(ptr) +TYPE_SAFE_HASH_DEFINE(j, jnode, jnode_key_t, key.j, link.j, jnode_key_hashfn, jnode_key_eq); +#undef KFREE +#undef KMALLOC + +/* call this to initialise jnode hash table */ +reiser4_internal int +jnodes_tree_init(reiser4_tree * tree /* tree to initialise jnodes for */ ) +{ + assert("nikita-2359", tree != NULL); + return j_hash_init(&tree->jhash_table, 16384, + reiser4_stat(tree->super, hashes.jnode)); +} + +/* call this to destroy jnode hash table. This is called during umount. */ +reiser4_internal int +jnodes_tree_done(reiser4_tree * tree /* tree to destroy jnodes for */ ) +{ + j_hash_table *jtable; + jnode *node; + jnode *next; + + assert("nikita-2360", tree != NULL); + + /* + * Scan hash table and free all jnodes. + */ + + IF_TRACE(TRACE_ZWEB, UNDER_RW_VOID(tree, tree, read, + print_jnodes("umount", tree))); + + jtable = &tree->jhash_table; + for_all_in_htable(jtable, j, node, next) { + if (atomic_read(&node->x_count)) + info_jnode("x_count != 0", node); + assert("nikita-2361", !atomic_read(&node->x_count)); + jdrop(node); + } + + j_hash_done(&tree->jhash_table); + return 0; +} + +/* Initialize static variables in this file. */ +reiser4_internal int +jnode_init_static(void) +{ + assert("umka-168", _jnode_slab == NULL); + + _jnode_slab = kmem_cache_create("jnode", sizeof (jnode), 0, + SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + NULL, NULL); + + if (_jnode_slab == NULL) + goto error; + + return 0; + +error: + + if (_jnode_slab != NULL) + kmem_cache_destroy(_jnode_slab); + + return RETERR(-ENOMEM); +} + +/* Dual to jnode_init_static */ +reiser4_internal int +jnode_done_static(void) +{ + int ret = 0; + + if (_jnode_slab != NULL) { + ret = kmem_cache_destroy(_jnode_slab); + _jnode_slab = NULL; + } + + return ret; +} + +/* Initialize a jnode. */ +reiser4_internal void +jnode_init(jnode * node, reiser4_tree * tree, jnode_type type) +{ + assert("umka-175", node != NULL); + + xmemset(node, 0, sizeof (jnode)); + ON_DEBUG(node->magic = JMAGIC); + jnode_set_type(node, type); + atomic_set(&node->d_count, 0); + atomic_set(&node->x_count, 0); + spin_jnode_init(node); + spin_jload_init(node); + node->atom = NULL; + node->tree = tree; + capture_list_clean(node); + + ASSIGN_NODE_LIST(node, NOT_CAPTURED); + + INIT_RCU_HEAD(&node->rcu); + +#if REISER4_DEBUG + { + reiser4_super_info_data *sbinfo; + + sbinfo = get_super_private(tree->super); + spin_lock_irq(&sbinfo->all_guard); + list_add(&node->jnodes, &sbinfo->all_jnodes); + spin_unlock_irq(&sbinfo->all_guard); + /* link with which jnode is attached to reiser4_inode */ + inode_jnodes_list_clean(node); + } +#endif +} + +#if REISER4_DEBUG +/* + * Remove jnode from ->all_jnodes list. + */ +void +jnode_done(jnode * node, reiser4_tree * tree) +{ + reiser4_super_info_data *sbinfo; + + sbinfo = get_super_private(tree->super); + + spin_lock_irq(&sbinfo->all_guard); + assert("nikita-2422", !list_empty(&node->jnodes)); + list_del_init(&node->jnodes); + spin_unlock_irq(&sbinfo->all_guard); +} +#endif + +/* return already existing jnode of page */ +reiser4_internal jnode * +jnode_by_page(struct page *pg) +{ + assert("nikita-2066", pg != NULL); + assert("nikita-2400", PageLocked(pg)); + assert("nikita-2068", PagePrivate(pg)); + assert("nikita-2067", jprivate(pg) != NULL); + return jprivate(pg); +} + +/* exported functions to allocate/free jnode objects outside this file */ +reiser4_internal jnode * +jalloc(void) +{ + jnode *jal = kmem_cache_alloc(_jnode_slab, GFP_KERNEL); + return jal; +} + +/* return jnode back to the slab allocator */ +reiser4_internal inline void +jfree(jnode * node) +{ + assert("zam-449", node != NULL); + + assert("nikita-2663", capture_list_is_clean(node) && NODE_LIST(node) == NOT_CAPTURED); + assert("nikita-2774", !JF_ISSET(node, JNODE_EFLUSH)); + assert("nikita-3222", list_empty(&node->jnodes)); + assert("nikita-3221", jnode_page(node) == NULL); + + /* not yet phash_jnode_destroy(node); */ + + /* poison memory. */ + ON_DEBUG(xmemset(node, 0xad, sizeof *node)); + kmem_cache_free(_jnode_slab, node); +} + +/* + * This function is supplied as RCU callback. It actually frees jnode when + * last reference to it is gone. + */ +static void +jnode_free_actor(struct rcu_head *head) +{ + jnode * node; + jnode_type jtype; + + node = container_of(head, jnode, rcu); + jtype = jnode_get_type(node); + + ON_DEBUG(jnode_done(node, jnode_get_tree(node))); + + switch (jtype) { + case JNODE_IO_HEAD: + case JNODE_BITMAP: + case JNODE_UNFORMATTED_BLOCK: + jfree(node); + break; + case JNODE_FORMATTED_BLOCK: + zfree(JZNODE(node)); + break; + case JNODE_INODE: + default: + wrong_return_value("nikita-3197", "Wrong jnode type"); + } +} + +/* + * Free a jnode. Post a callback to be executed later through RCU when all + * references to @node are released. + */ +static inline void +jnode_free(jnode * node, jnode_type jtype) +{ + if (jtype != JNODE_INODE) { + /*assert("nikita-3219", list_empty(&node->rcu.list));*/ + call_rcu(&node->rcu, jnode_free_actor); + } else + jnode_list_remove(node); +} + +/* allocate new unformatted jnode */ +static jnode * +jnew_unformatted(void) +{ + jnode *jal; + + jal = jalloc(); + if (jal == NULL) + return NULL; + + jnode_init(jal, current_tree, JNODE_UNFORMATTED_BLOCK); + jal->key.j.mapping = 0; + jal->key.j.index = (unsigned long)-1; + jal->key.j.objectid = 0; + return jal; +} + +/* look for jnode with given mapping and offset within hash table */ +reiser4_internal jnode * +jlookup(reiser4_tree * tree, oid_t objectid, unsigned long index) +{ + jnode_key_t jkey; + jnode *node; + + assert("nikita-2353", tree != NULL); + + jkey.objectid = objectid; + jkey.index = index; + + /* + * hash table is _not_ protected by any lock during lookups. All we + * have to do is to disable preemption to keep RCU happy. + */ + + rcu_read_lock(); + node = j_hash_find(&tree->jhash_table, &jkey); + if (node != NULL) { + /* protect @node from recycling */ + jref(node); + assert("nikita-2955", jnode_invariant(node, 0, 0)); + node = jnode_rip_check(tree, node); + } + rcu_read_unlock(); + return node; +} + +/* per inode radix tree of jnodes is protected by tree's read write spin lock */ +static jnode * +jfind_nolock(struct address_space *mapping, unsigned long index) +{ + assert("vs-1694", mapping->host != NULL); + + return radix_tree_lookup(jnode_tree_by_inode(mapping->host), index); +} + +reiser4_internal jnode * +jfind(struct address_space *mapping, unsigned long index) +{ + reiser4_tree *tree; + jnode *node; + + assert("vs-1694", mapping->host != NULL); + tree = tree_by_inode(mapping->host); + + RLOCK_TREE(tree); + node = jfind_nolock(mapping, index); + if (node != NULL) + jref(node); + RUNLOCK_TREE(tree); + return node; +} + +static void inode_attach_jnode(jnode * node) +{ + struct inode * inode; + reiser4_inode * info; + struct radix_tree_root * rtree; + + assert ("zam-1043", node->key.j.mapping != NULL); + inode = node->key.j.mapping->host; + info = reiser4_inode_data(inode); + rtree = jnode_tree_by_reiser4_inode(info); + + spin_lock(&inode_lock); + assert("zam-1049", equi(rtree->rnode !=NULL, info->nr_jnodes != 0)); + check_me("zam-1045", !radix_tree_insert(rtree, node->key.j.index, node)); + ON_DEBUG(info->nr_jnodes ++); + inode->i_state |= I_JNODES; + spin_unlock(&inode_lock); +} + +static void inode_detach_jnode(jnode * node) +{ + struct inode * inode; + reiser4_inode * info; + struct radix_tree_root * rtree; + + assert ("zam-1044", node->key.j.mapping != NULL); + inode = node->key.j.mapping->host; + info = reiser4_inode_data(inode); + rtree = jnode_tree_by_reiser4_inode(info); + + spin_lock(&inode_lock); + assert("zam-1051", info->nr_jnodes != 0); + assert("zam-1052", rtree->rnode != NULL); + ON_DEBUG(info->nr_jnodes --); + check_me("zam-1046", radix_tree_delete(rtree, node->key.j.index)); + if (rtree->rnode == NULL) { + inode->i_state &= ~I_JNODES; + } + spin_unlock(&inode_lock); +} + +/* put jnode into hash table (where they can be found by flush who does not know + mapping) and to inode's tree of jnodes (where they can be found (hopefully + faster) in places where mapping is known). Currently it is used by + fs/reiser4/plugin/item/extent_file_ops.c:index_extent_jnode when new jnode is + created */ +static void +hash_unformatted_jnode(jnode *node, struct address_space *mapping, unsigned long index) +{ + j_hash_table *jtable; + + assert("vs-1446", jnode_is_unformatted(node)); + assert("vs-1442", node->key.j.mapping == 0); + assert("vs-1443", node->key.j.objectid == 0); + assert("vs-1444", node->key.j.index == (unsigned long)-1); + assert("nikita-3439", rw_tree_is_write_locked(jnode_get_tree(node))); + + node->key.j.mapping = mapping; + node->key.j.objectid = get_inode_oid(mapping->host); + node->key.j.index = index; + + jtable = &jnode_get_tree(node)->jhash_table; + + /* race with some other thread inserting jnode into the hash table is + * impossible, because we keep the page lock. */ + /* + * following assertion no longer holds because of RCU: it is possible + * jnode is in the hash table, but with JNODE_RIP bit set. + */ + /* assert("nikita-3211", j_hash_find(jtable, &node->key.j) == NULL); */ + j_hash_insert_rcu(jtable, node); + inode_attach_jnode(node); +} + +static void +unhash_unformatted_node_nolock(jnode *node) +{ + assert("vs-1683", node->key.j.mapping != NULL); + assert("vs-1684", node->key.j.objectid == get_inode_oid(node->key.j.mapping->host)); + + /* remove jnode from hash-table */ + j_hash_remove_rcu(&node->tree->jhash_table, node); + inode_detach_jnode(node); + node->key.j.mapping = 0; + node->key.j.index = (unsigned long)-1; + node->key.j.objectid = 0; + +} + +/* remove jnode from hash table and from inode's tree of jnodes. This is used in + reiser4_invalidatepage and in kill_hook_extent -> truncate_inode_jnodes -> + uncapture_jnode */ +reiser4_internal void +unhash_unformatted_jnode(jnode *node) +{ + assert("vs-1445", jnode_is_unformatted(node)); + WLOCK_TREE(node->tree); + + unhash_unformatted_node_nolock(node); + + WUNLOCK_TREE(node->tree); +} + +/* + * search hash table for a jnode with given oid and index. If not found, + * allocate new jnode, insert it, and also insert into radix tree for the + * given inode/mapping. + */ +reiser4_internal jnode * +find_get_jnode(reiser4_tree * tree, struct address_space *mapping, oid_t oid, + unsigned long index) +{ + jnode *result; + jnode *shadow; + int preload; + + result = jnew_unformatted(); + + if (unlikely(result == NULL)) + return ERR_PTR(RETERR(-ENOMEM)); + + preload = radix_tree_preload(GFP_KERNEL); + if (preload != 0) + return ERR_PTR(preload); + + WLOCK_TREE(tree); + shadow = jfind_nolock(mapping, index); + if (likely(shadow == NULL)) { + /* add new jnode to hash table and inode's radix tree of jnodes */ + jref(result); + hash_unformatted_jnode(result, mapping, index); + } else { + /* jnode is found in inode's radix tree of jnodes */ + jref(shadow); + jnode_free(result, JNODE_UNFORMATTED_BLOCK); + assert("vs-1498", shadow->key.j.mapping == mapping); + result = shadow; + } + WUNLOCK_TREE(tree); + + assert("nikita-2955", ergo(result != NULL, jnode_invariant(result, 0, 0))); + radix_tree_preload_end(); + return result; +} + + +/* jget() (a la zget() but for unformatted nodes). Returns (and possibly + creates) jnode corresponding to page @pg. jnode is attached to page and + inserted into jnode hash-table. */ +static jnode * +do_jget(reiser4_tree * tree, struct page * pg) +{ + /* + * There are two ways to create jnode: starting with pre-existing page + * and without page. + * + * When page already exists, jnode is created + * (jnode_of_page()->do_jget()) under page lock. This is done in + * ->writepage(), or when capturing anonymous page dirtied through + * mmap. + * + * Jnode without page is created by index_extent_jnode(). + * + */ + + jnode *result; + oid_t oid = get_inode_oid(pg->mapping->host); + + assert("umka-176", pg != NULL); + assert("nikita-2394", PageLocked(pg)); + + result = jprivate(pg); + if (likely(result != NULL)) + return jref(result); + + tree = tree_by_page(pg); + + /* check hash-table first */ + result = jfind(pg->mapping, pg->index); + if (unlikely(result != NULL)) { + UNDER_SPIN_VOID(jnode, result, jnode_attach_page(result, pg)); + result->key.j.mapping = pg->mapping; + return result; + } + + result = find_get_jnode(tree, pg->mapping, oid, pg->index); + if (unlikely(IS_ERR(result))) + return result; + /* attach jnode to page */ + UNDER_SPIN_VOID(jnode, result, jnode_attach_page(result, pg)); + return result; +} + +/* + * return jnode for @pg, creating it if necessary. + */ +reiser4_internal jnode * +jnode_of_page(struct page * pg) +{ + jnode * result; + + assert("umka-176", pg != NULL); + assert("nikita-2394", PageLocked(pg)); + + result = do_jget(tree_by_page(pg), pg); + + if (REISER4_DEBUG && !IS_ERR(result)) { + assert("nikita-3210", result == jprivate(pg)); + assert("nikita-2046", jnode_page(jprivate(pg)) == pg); + if (jnode_is_unformatted(jprivate(pg))) { + assert("nikita-2364", jprivate(pg)->key.j.index == pg->index); + assert("nikita-2367", + jprivate(pg)->key.j.mapping == pg->mapping); + assert("nikita-2365", + jprivate(pg)->key.j.objectid == get_inode_oid(pg->mapping->host)); + assert("vs-1200", + jprivate(pg)->key.j.objectid == pg->mapping->host->i_ino); + assert("nikita-2356", jnode_is_unformatted(jnode_by_page(pg))); + } + assert("nikita-2956", jnode_invariant(jprivate(pg), 0, 0)); + } + return result; +} + +/* attach page to jnode: set ->pg pointer in jnode, and ->private one in the + * page.*/ +reiser4_internal void +jnode_attach_page(jnode * node, struct page *pg) +{ + assert("nikita-2060", node != NULL); + assert("nikita-2061", pg != NULL); + + assert("nikita-2050", pg->private == 0ul); + assert("nikita-2393", !PagePrivate(pg)); + + assert("nikita-2396", PageLocked(pg)); + assert("nikita-2397", spin_jnode_is_locked(node)); + + page_cache_get(pg); + pg->private = (unsigned long) node; + node->pg = pg; + SetPagePrivate(pg); +} + +/* Dual to jnode_attach_page: break a binding between page and jnode */ +reiser4_internal void +page_clear_jnode(struct page *page, jnode * node) +{ + assert("nikita-2424", page != NULL); + assert("nikita-2425", PageLocked(page)); + assert("nikita-2426", node != NULL); + assert("nikita-2427", spin_jnode_is_locked(node)); + assert("nikita-2428", PagePrivate(page)); + + assert("nikita-3551", !PageWriteback(page)); + + JF_CLR(node, JNODE_PARSED); + page->private = 0ul; + ClearPagePrivate(page); + node->pg = NULL; + page_cache_release(page); + if (REISER4_DEBUG_MODIFY && jnode_is_znode(node)) + ON_DEBUG_MODIFY(JZNODE(node)->cksum = 0); +} + +/* it is only used in one place to handle error */ +reiser4_internal void +page_detach_jnode(struct page *page, struct address_space *mapping, unsigned long index) +{ + assert("nikita-2395", page != NULL); + + lock_page(page); + if ((page->mapping == mapping) && (page->index == index) && PagePrivate(page)) { + jnode *node; + + node = jprivate(page); + assert("nikita-2399", spin_jnode_is_not_locked(node)); + UNDER_SPIN_VOID(jnode, node, page_clear_jnode(page, node)); + } + unlock_page(page); +} + +/* return @node page locked. + + Locking ordering requires that one first takes page lock and afterwards + spin lock on node attached to this page. Sometimes it is necessary to go in + the opposite direction. This is done through standard trylock-and-release + loop. +*/ +reiser4_internal struct page * +jnode_lock_page(jnode * node) +{ + struct page *page; + + assert("nikita-2052", node != NULL); + assert("nikita-2401", spin_jnode_is_not_locked(node)); + + while (1) { + + LOCK_JNODE(node); + page = jnode_page(node); + if (page == NULL) { + break; + } + + /* no need to page_cache_get( page ) here, because page cannot + be evicted from memory without detaching it from jnode and + this requires spin lock on jnode that we already hold. + */ + if (!TestSetPageLocked(page)) { + /* We won a lock on jnode page, proceed. */ + break; + } + + /* Page is locked by someone else. */ + page_cache_get(page); + UNLOCK_JNODE(node); + wait_on_page_locked(page); + /* it is possible that page was detached from jnode and + returned to the free pool, or re-assigned while we were + waiting on locked bit. This will be rechecked on the next + loop iteration. + */ + page_cache_release(page); + + /* try again */ + } + return page; +} + +/* + * is JNODE_PARSED bit is not set, call ->parse() method of jnode, to verify + * validness of jnode content. + */ +static inline int +jparse(jnode * node) +{ + int result; + + assert("nikita-2466", node != NULL); + + LOCK_JNODE(node); + if (likely(!jnode_is_parsed(node))) { + result = jnode_ops(node)->parse(node); + if (likely(result == 0)) + JF_SET(node, JNODE_PARSED); + } else + result = 0; + UNLOCK_JNODE(node); + return result; +} + +/* Lock a page attached to jnode, create and attach page to jnode if it had no + * one. */ +reiser4_internal struct page * +jnode_get_page_locked(jnode * node, int gfp_flags) +{ + struct page * page; + + LOCK_JNODE(node); + page = jnode_page(node); + + if (page == NULL) { + UNLOCK_JNODE(node); + page = find_or_create_page(jnode_get_mapping(node), + jnode_get_index(node), gfp_flags); + if (page == NULL) + return ERR_PTR(RETERR(-ENOMEM)); + } else { + if (!TestSetPageLocked(page)) { + UNLOCK_JNODE(node); + return page; + } + page_cache_get(page); + UNLOCK_JNODE(node); + lock_page(page); + assert("nikita-3134", page->mapping == jnode_get_mapping(node)); + } + + LOCK_JNODE(node); + if (!jnode_page(node)) + jnode_attach_page(node, page); + UNLOCK_JNODE(node); + + page_cache_release(page); + assert ("zam-894", jnode_page(node) == page); + return page; +} + +/* Start read operation for jnode's page if page is not up-to-date. */ +static int jnode_start_read (jnode * node, struct page * page) +{ + assert ("zam-893", PageLocked(page)); + + if (PageUptodate(page)) { + unlock_page(page); + return 0; + } + return page_io(page, node, READ, GFP_KERNEL); +} + +#if REISER4_DEBUG +static void check_jload(jnode * node, struct page * page) +{ + if (jnode_is_znode(node)) { + node40_header *nh; + znode *z; + + z = JZNODE(node); + if (znode_is_any_locked(z)) { + nh = (node40_header *)kmap(page); + /* this only works for node40-only file systems. For + * debugging. */ + assert("nikita-3253", + z->nr_items == d16tocpu(&nh->nr_items)); + kunmap(page); + } + assert("nikita-3565", znode_invariant(z)); + } +} +#else +#define check_jload(node, page) noop +#endif + +/* prefetch jnode to speed up next call to jload. Call this when you are going + * to call jload() shortly. This will bring appropriate portion of jnode into + * CPU cache. */ +reiser4_internal void jload_prefetch(jnode *node) +{ + prefetchw(&node->x_count); +} + +/* load jnode's data into memory */ +reiser4_internal int +jload_gfp (jnode * node /* node to load */, + int gfp_flags /* allocation flags*/, + int do_kmap /* true if page should be kmapped */) +{ + struct page * page; + int result = 0; + int parsed; + + assert("nikita-3010", schedulable()); + write_node_log(node); + + prefetchw(&node->pg); + + /* taking d-reference implies taking x-reference. */ + jref(node); + + /* + * acquiring d-reference to @jnode and check for JNODE_PARSED bit + * should be atomic, otherwise there is a race against + * reiser4_releasepage(). + */ + LOCK_JLOAD(node); + add_d_ref(node); + parsed = jnode_is_parsed(node); + UNLOCK_JLOAD(node); + + if (unlikely(!parsed)) { + ON_TRACE(TRACE_PCACHE, "read node: %p\n", node); + + page = jnode_get_page_locked(node, gfp_flags); + if (unlikely(IS_ERR(page))) { + result = PTR_ERR(page); + goto failed; + } + + result = jnode_start_read(node, page); + if (unlikely(result != 0)) + goto failed; + + wait_on_page_locked(page); + if (unlikely(!PageUptodate(page))) { + result = RETERR(-EIO); + goto failed; + } + + if (do_kmap) + node->data = kmap(page); + + result = jparse(node); + if (unlikely(result != 0)) { + if (do_kmap) + kunmap(page); + goto failed; + } + check_jload(node, page); + } else { + page = jnode_page(node); + check_jload(node, page); + if (do_kmap) + node->data = kmap(page); + reiser4_stat_inc_at_level(jnode_get_level(node), + jnode.jload_already); + } + + if (unlikely(JF_ISSET(node, JNODE_EFLUSH))) + UNDER_SPIN_VOID(jnode, node, eflush_del(node, 0)); + + if (!is_writeout_mode()) + /* We do not mark pages active if jload is called as a part of + * jnode_flush() or reiser4_write_logs(). Both jnode_flush() + * and write_logs() add no value to cached data, there is no + * sense to mark pages as active when they go to disk, it just + * confuses vm scanning routines because clean page could be + * moved out from inactive list as a result of this + * mark_page_accessed() call. */ + mark_page_accessed(page); + + return 0; + + failed: + jrelse_tail(node); + return result; + +} + +/* start asynchronous reading for given jnode's page. */ +reiser4_internal int jstartio (jnode * node) +{ + struct page * page; + + page = jnode_get_page_locked(node, GFP_KERNEL); + if (IS_ERR(page)) + return PTR_ERR(page); + + return jnode_start_read(node, page); +} + + +/* Initialize a node by calling appropriate plugin instead of reading + * node from disk as in jload(). */ +reiser4_internal int jinit_new (jnode * node, int gfp_flags) +{ + struct page * page; + int result; + + jref(node); + add_d_ref(node); + + page = jnode_get_page_locked(node, gfp_flags); + if (IS_ERR(page)) { + result = PTR_ERR(page); + goto failed; + } + + SetPageUptodate(page); + unlock_page(page); + + node->data = kmap(page); + + if (!jnode_is_parsed(node)) { + jnode_plugin * jplug = jnode_ops(node); + result = UNDER_SPIN(jnode, node, jplug->init(node)); + if (result) { + kunmap(page); + goto failed; + } + JF_SET(node, JNODE_PARSED); + } + + return 0; + + failed: + jrelse(node); + return result; +} + +/* release a reference to jnode acquired by jload(), decrement ->d_count */ +reiser4_internal void +jrelse_tail(jnode * node /* jnode to release references to */) +{ + assert("nikita-489", atomic_read(&node->d_count) > 0); + atomic_dec(&node->d_count); + /* release reference acquired in jload_gfp() or jinit_new() */ + jput(node); + LOCK_CNT_DEC(d_refs); +} + +/* drop reference to node data. When last reference is dropped, data are + unloaded. */ +reiser4_internal void +jrelse(jnode * node /* jnode to release references to */) +{ + struct page *page; + + assert("nikita-487", node != NULL); + assert("nikita-1906", spin_jnode_is_not_locked(node)); + + ON_TRACE(TRACE_PCACHE, "release node: %p\n", node); + + page = jnode_page(node); + if (likely(page != NULL)) { + /* + * it is safe not to lock jnode here, because at this point + * @node->d_count is greater than zero (if jrelse() is used + * correctly, that is). JNODE_PARSED may be not set yet, if, + * for example, we got here as a result of error handling path + * in jload(). Anyway, page cannot be detached by + * reiser4_releasepage(). truncate will invalidate page + * regardless, but this should not be a problem. + */ + kunmap(page); + } + jrelse_tail(node); +} + +/* called from jput() to wait for io completion */ +static void jnode_finish_io(jnode * node) +{ + struct page *page; + + assert("nikita-2922", node != NULL); + + LOCK_JNODE(node); + page = jnode_page(node); + if (page != NULL) { + page_cache_get(page); + UNLOCK_JNODE(node); + wait_on_page_writeback(page); + page_cache_release(page); + } else + UNLOCK_JNODE(node); +} + +/* + * This is called by jput() when last reference to jnode is released. This is + * separate function, because we want fast path of jput() to be inline and, + * therefore, small. + */ +reiser4_internal void +jput_final(jnode * node) +{ + int r_i_p; + + /* A fast check for keeping node in cache. We always keep node in cache + * if its page is present and node was not marked for deletion */ + if (jnode_page(node) != NULL && !JF_ISSET(node, JNODE_HEARD_BANSHEE)) { + rcu_read_unlock(); + return; + } + + r_i_p = !JF_TEST_AND_SET(node, JNODE_RIP); + /* + * if r_i_p is true, we were first to set JNODE_RIP on this node. In + * this case it is safe to access node after unlock. + */ + rcu_read_unlock(); + if (r_i_p) { + jnode_finish_io(node); + if (JF_ISSET(node, JNODE_HEARD_BANSHEE)) + /* node is removed from the tree. */ + jdelete(node); + else + jnode_try_drop(node); + } + /* if !r_i_p some other thread is already killing it */ +} + +reiser4_internal int +jwait_io(jnode * node, int rw) +{ + struct page *page; + int result; + + assert("zam-447", node != NULL); + assert("zam-448", jnode_page(node) != NULL); + + page = jnode_page(node); + + result = 0; + if (rw == READ) { + wait_on_page_locked(page); + } else { + assert("nikita-2227", rw == WRITE); + wait_on_page_writeback(page); + } + if (PageError(page)) + result = RETERR(-EIO); + + return result; +} + +/* + * jnode types and plugins. + * + * jnode by itself is a "base type". There are several different jnode + * flavors, called "jnode types" (see jnode_type for a list). Sometimes code + * has to do different things based on jnode type. In the standard reiser4 way + * this is done by having jnode plugin (see fs/reiser4/plugin.h:jnode_plugin). + * + * Functions below deal with jnode types and define methods of jnode plugin. + * + */ + +/* set jnode type. This is done during jnode initialization. */ +static void +jnode_set_type(jnode * node, jnode_type type) +{ + static unsigned long type_to_mask[] = { + [JNODE_UNFORMATTED_BLOCK] = 1, + [JNODE_FORMATTED_BLOCK] = 0, + [JNODE_BITMAP] = 2, + [JNODE_IO_HEAD] = 6, + [JNODE_INODE] = 4 + }; + + assert("zam-647", type < LAST_JNODE_TYPE); + assert("nikita-2815", !jnode_is_loaded(node)); + assert("nikita-3386", node->state == 0); + + node->state |= (type_to_mask[type] << JNODE_TYPE_1); +} + +/* ->init() method of jnode plugin for jnodes that don't require plugin + * specific initialization. */ +static int +init_noinit(jnode * node UNUSED_ARG) +{ + return 0; +} + +/* ->parse() method of jnode plugin for jnodes that don't require plugin + * specific pasring. */ +static int +parse_noparse(jnode * node UNUSED_ARG) +{ + return 0; +} + +/* ->mapping() method for unformatted jnode */ +reiser4_internal struct address_space * +mapping_jnode(const jnode * node) +{ + struct address_space *map; + + assert("nikita-2713", node != NULL); + + /* mapping is stored in jnode */ + + map = node->key.j.mapping; + assert("nikita-2714", map != NULL); + assert("nikita-2897", is_reiser4_inode(map->host)); + assert("nikita-2715", get_inode_oid(map->host) == node->key.j.objectid); + assert("vs-1447", !JF_ISSET(node, JNODE_CC)); + return map; +} + +/* ->index() method for unformatted jnodes */ +reiser4_internal unsigned long +index_jnode(const jnode * node) +{ + assert("vs-1447", !JF_ISSET(node, JNODE_CC)); + /* index is stored in jnode */ + return node->key.j.index; +} + +/* ->remove() method for unformatted jnodes */ +static inline void +remove_jnode(jnode * node, reiser4_tree * tree) +{ + /* remove jnode from hash table and radix tree */ + if (node->key.j.mapping) + unhash_unformatted_node_nolock(node); +} + +/* ->mapping() method for znodes */ +static struct address_space * +mapping_znode(const jnode * node) +{ + assert("vs-1447", !JF_ISSET(node, JNODE_CC)); + /* all znodes belong to fake inode */ + return get_super_fake(jnode_get_tree(node)->super)->i_mapping; +} + +extern int znode_shift_order; +/* ->index() method for znodes */ +static unsigned long +index_znode(const jnode * node) +{ + unsigned long addr; + assert("nikita-3317", (1 << znode_shift_order) < sizeof(znode)); + + /* index of znode is just its address (shifted) */ + addr = (unsigned long)node; + return (addr - PAGE_OFFSET) >> znode_shift_order; +} + +/* ->mapping() method for bitmap jnode */ +static struct address_space * +mapping_bitmap(const jnode * node) +{ + /* all bitmap blocks belong to special bitmap inode */ + return get_super_private(jnode_get_tree(node)->super)->bitmap->i_mapping; +} + +/* ->index() method for jnodes that are indexed by address */ +static unsigned long +index_is_address(const jnode * node) +{ + unsigned long ind; + + ind = (unsigned long)node; + return ind - PAGE_OFFSET; +} + +/* resolve race with jput */ +reiser4_internal jnode * +jnode_rip_sync(reiser4_tree *t, jnode * node) +{ + /* + * This is used as part of RCU-based jnode handling. + * + * jlookup(), zlook(), zget(), and cbk_cache_scan_slots() have to work + * with unreferenced jnodes (ones with ->x_count == 0). Hash table is + * not protected during this, so concurrent thread may execute + * zget-set-HEARD_BANSHEE-zput, or somehow else cause jnode to be + * freed in jput_final(). To avoid such races, jput_final() sets + * JNODE_RIP on jnode (under tree lock). All places that work with + * unreferenced jnodes call this function. It checks for JNODE_RIP bit + * (first without taking tree lock), and if this bit is set, released + * reference acquired by the current thread and returns NULL. + * + * As a result, if jnode is being concurrently freed, NULL is returned + * and caller should pretend that jnode wasn't found in the first + * place. + * + * Otherwise it's safe to release "rcu-read-lock" and continue with + * jnode. + */ + if (unlikely(JF_ISSET(node, JNODE_RIP))) { + RLOCK_TREE(t); + if (JF_ISSET(node, JNODE_RIP)) { + dec_x_ref(node); + node = NULL; + } + RUNLOCK_TREE(t); + } + return node; +} + + +reiser4_internal reiser4_key * +jnode_build_key(const jnode * node, reiser4_key * key) +{ + struct inode *inode; + item_plugin *iplug; + loff_t off; + + assert("nikita-3092", node != NULL); + assert("nikita-3093", key != NULL); + assert("nikita-3094", jnode_is_unformatted(node)); + + + off = ((loff_t)index_jnode(node)) << PAGE_CACHE_SHIFT; + inode = mapping_jnode(node)->host; + + if (node->parent_item_id != 0) + iplug = item_plugin_by_id(node->parent_item_id); + else + iplug = NULL; + + if (iplug != NULL && iplug->f.key_by_offset) + iplug->f.key_by_offset(inode, off, key); + else { + file_plugin *fplug; + + fplug = inode_file_plugin(inode); + assert ("zam-1007", fplug != NULL); + assert ("zam-1008", fplug->key_by_inode != NULL); + + fplug->key_by_inode(inode, off, key); + } + + return key; +} + +extern int zparse(znode * node); + +/* ->parse() method for formatted nodes */ +static int +parse_znode(jnode * node) +{ + return zparse(JZNODE(node)); +} + +/* ->delete() method for formatted nodes */ +static void +delete_znode(jnode * node, reiser4_tree * tree) +{ + znode *z; + + assert("nikita-2128", rw_tree_is_write_locked(tree)); + assert("vs-898", JF_ISSET(node, JNODE_HEARD_BANSHEE)); + + z = JZNODE(node); + assert("vs-899", z->c_count == 0); + + /* delete znode from sibling list. */ + sibling_list_remove(z); + + znode_remove(z, tree); +} + +/* ->remove() method for formatted nodes */ +static int +remove_znode(jnode * node, reiser4_tree * tree) +{ + znode *z; + + assert("nikita-2128", rw_tree_is_locked(tree)); + z = JZNODE(node); + + if (z->c_count == 0) { + /* detach znode from sibling list. */ + sibling_list_drop(z); + /* this is called with tree spin-lock held, so call + znode_remove() directly (rather than znode_lock_remove()). */ + znode_remove(z, tree); + return 0; + } + return RETERR(-EBUSY); +} + +/* ->init() method for formatted nodes */ +static int +init_znode(jnode * node) +{ + znode *z; + + z = JZNODE(node); + /* call node plugin to do actual initialization */ + return z->nplug->init(z); +} + +/* jplug->clone for formatted nodes (znodes) */ +znode *zalloc(int gfp_flag); +void zinit(znode *, const znode * parent, reiser4_tree *); + +/* ->clone() method for formatted nodes */ +reiser4_internal jnode * +clone_formatted(jnode *node) +{ + znode *clone; + + assert("vs-1430", jnode_is_znode(node)); + clone = zalloc(GFP_KERNEL); + if (clone == NULL) + return ERR_PTR(RETERR(-ENOMEM)); + zinit(clone, 0, current_tree); + jnode_set_block(ZJNODE(clone), jnode_get_block(node)); + /* ZJNODE(clone)->key.z is not initialized */ + clone->level = JZNODE(node)->level; + + return ZJNODE(clone); +} + +/* jplug->clone for unformatted nodes */ +reiser4_internal jnode * +clone_unformatted(jnode *node) +{ + jnode *clone; + + assert("vs-1431", jnode_is_unformatted(node)); + clone = jalloc(); + if (clone == NULL) + return ERR_PTR(RETERR(-ENOMEM)); + + jnode_init(clone, current_tree, JNODE_UNFORMATTED_BLOCK); + jnode_set_block(clone, jnode_get_block(node)); + + return clone; + +} + +/* + * Setup jnode plugin methods for various jnode types. + */ + +jnode_plugin jnode_plugins[LAST_JNODE_TYPE] = { + [JNODE_UNFORMATTED_BLOCK] = { + .h = { + .type_id = REISER4_JNODE_PLUGIN_TYPE, + .id = JNODE_UNFORMATTED_BLOCK, + .pops = NULL, + .label = "unformatted", + .desc = "unformatted node", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .init = init_noinit, + .parse = parse_noparse, + .mapping = mapping_jnode, + .index = index_jnode, + .clone = clone_unformatted + }, + [JNODE_FORMATTED_BLOCK] = { + .h = { + .type_id = REISER4_JNODE_PLUGIN_TYPE, + .id = JNODE_FORMATTED_BLOCK, + .pops = NULL, + .label = "formatted", + .desc = "formatted tree node", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .init = init_znode, + .parse = parse_znode, + .mapping = mapping_znode, + .index = index_znode, + .clone = clone_formatted + }, + [JNODE_BITMAP] = { + .h = { + .type_id = REISER4_JNODE_PLUGIN_TYPE, + .id = JNODE_BITMAP, + .pops = NULL, + .label = "bitmap", + .desc = "bitmap node", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .init = init_noinit, + .parse = parse_noparse, + .mapping = mapping_bitmap, + .index = index_is_address, + .clone = NULL + }, + [JNODE_IO_HEAD] = { + .h = { + .type_id = REISER4_JNODE_PLUGIN_TYPE, + .id = JNODE_IO_HEAD, + .pops = NULL, + .label = "io head", + .desc = "io head", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .init = init_noinit, + .parse = parse_noparse, + .mapping = mapping_bitmap, + .index = index_is_address, + .clone = NULL + }, + [JNODE_INODE] = { + .h = { + .type_id = REISER4_JNODE_PLUGIN_TYPE, + .id = JNODE_INODE, + .pops = NULL, + .label = "inode", + .desc = "inode's builtin jnode", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .init = NULL, + .parse = NULL, + .mapping = NULL, + .index = NULL, + .clone = NULL + } +}; + +/* + * jnode destruction. + * + * Thread may use a jnode after it acquired a reference to it. References are + * counted in ->x_count field. Reference protects jnode from being + * recycled. This is different from protecting jnode data (that are stored in + * jnode page) from being evicted from memory. Data are protected by jload() + * and released by jrelse(). + * + * If thread already possesses a reference to the jnode it can acquire another + * one through jref(). Initial reference is obtained (usually) by locating + * jnode in some indexing structure that depends on jnode type: formatted + * nodes are kept in global hash table, where they are indexed by block + * number, and also in the cbk cache. Unformatted jnodes are also kept in hash + * table, which is indexed by oid and offset within file, and in per-inode + * radix tree. + * + * Reference to jnode is released by jput(). If last reference is released, + * jput_final() is called. This function determines whether jnode has to be + * deleted (this happens when corresponding node is removed from the file + * system, jnode is marked with JNODE_HEARD_BANSHEE bit in this case), or it + * should be just "removed" (deleted from memory). + * + * Jnode destruction is signally delicate dance because of locking and RCU. + */ + +/* + * Returns true if jnode cannot be removed right now. This check is called + * under tree lock. If it returns true, jnode is irrevocably committed to be + * deleted/removed. + */ +static inline int +jnode_is_busy(const jnode * node, jnode_type jtype) +{ + /* if other thread managed to acquire a reference to this jnode, don't + * free it. */ + if (atomic_read(&node->x_count) > 0) + return 1; + /* also, don't free znode that has children in memory */ + if (jtype == JNODE_FORMATTED_BLOCK && JZNODE(node)->c_count > 0) + return 1; + return 0; +} + +/* + * this is called as part of removing jnode. Based on jnode type, call + * corresponding function that removes jnode from indices and returns it back + * to the appropriate slab (through RCU). + */ +static inline void +jnode_remove(jnode * node, jnode_type jtype, reiser4_tree * tree) +{ + switch (jtype) { + case JNODE_UNFORMATTED_BLOCK: + remove_jnode(node, tree); + break; + case JNODE_IO_HEAD: + case JNODE_BITMAP: + break; + case JNODE_INODE: + break; + case JNODE_FORMATTED_BLOCK: + remove_znode(node, tree); + break; + default: + wrong_return_value("nikita-3196", "Wrong jnode type"); + } +} + +/* + * this is called as part of deleting jnode. Based on jnode type, call + * corresponding function that removes jnode from indices and returns it back + * to the appropriate slab (through RCU). + * + * This differs from jnode_remove() only for formatted nodes---for them + * sibling list handling is different for removal and deletion. + */ +static inline void +jnode_delete(jnode * node, jnode_type jtype, reiser4_tree * tree UNUSED_ARG) +{ + switch (jtype) { + case JNODE_UNFORMATTED_BLOCK: + remove_jnode(node, tree); + break; + case JNODE_IO_HEAD: + case JNODE_BITMAP: + break; + case JNODE_FORMATTED_BLOCK: + delete_znode(node, tree); + break; + case JNODE_INODE: + default: + wrong_return_value("nikita-3195", "Wrong jnode type"); + } +} + +#if REISER4_DEBUG +/* + * remove jnode from the debugging list of all jnodes hanging off super-block. + */ +void jnode_list_remove(jnode * node) +{ + reiser4_super_info_data *sbinfo; + + sbinfo = get_super_private(jnode_get_tree(node)->super); + + spin_lock_irq(&sbinfo->all_guard); + assert("nikita-2422", !list_empty(&node->jnodes)); + list_del_init(&node->jnodes); + spin_unlock_irq(&sbinfo->all_guard); +} +#endif + +/* + * this is called by jput_final() to remove jnode when last reference to it is + * released. + */ +reiser4_internal int +jnode_try_drop(jnode * node) +{ + int result; + reiser4_tree *tree; + jnode_type jtype; + + trace_stamp(TRACE_ZNODES); + assert("nikita-2491", node != NULL); + assert("nikita-2583", JF_ISSET(node, JNODE_RIP)); + + ON_TRACE(TRACE_PCACHE, "trying to drop node: %p\n", node); + + tree = jnode_get_tree(node); + jtype = jnode_get_type(node); + + LOCK_JNODE(node); + WLOCK_TREE(tree); + /* + * if jnode has a page---leave it alone. Memory pressure will + * eventually kill page and jnode. + */ + if (jnode_page(node) != NULL) { + UNLOCK_JNODE(node); + WUNLOCK_TREE(tree); + JF_CLR(node, JNODE_RIP); + return RETERR(-EBUSY); + } + + /* re-check ->x_count under tree lock. */ + result = jnode_is_busy(node, jtype); + if (result == 0) { + assert("nikita-2582", !JF_ISSET(node, JNODE_HEARD_BANSHEE)); + assert("nikita-3223", !JF_ISSET(node, JNODE_EFLUSH)); + assert("jmacd-511/b", atomic_read(&node->d_count) == 0); + + UNLOCK_JNODE(node); + /* no page and no references---despatch him. */ + jnode_remove(node, jtype, tree); + WUNLOCK_TREE(tree); + jnode_free(node, jtype); + } else { + /* busy check failed: reference was acquired by concurrent + * thread. */ + WUNLOCK_TREE(tree); + UNLOCK_JNODE(node); + JF_CLR(node, JNODE_RIP); + } + return result; +} + +/* jdelete() -- Delete jnode from the tree and file system */ +reiser4_internal int +jdelete(jnode * node /* jnode to finish with */) +{ + struct page *page; + int result; + reiser4_tree *tree; + jnode_type jtype; + + trace_stamp(TRACE_ZNODES); + assert("nikita-467", node != NULL); + assert("nikita-2531", JF_ISSET(node, JNODE_RIP)); + /* jnode cannot be eflushed at this point, because emegrency flush + * acquired additional reference counter. */ + assert("nikita-2917", !JF_ISSET(node, JNODE_EFLUSH)); + + ON_TRACE(TRACE_PCACHE, "delete node: %p\n", node); + + jtype = jnode_get_type(node); + + page = jnode_lock_page(node); + assert("nikita-2402", spin_jnode_is_locked(node)); + + tree = jnode_get_tree(node); + + WLOCK_TREE(tree); + /* re-check ->x_count under tree lock. */ + result = jnode_is_busy(node, jtype); + if (likely(!result)) { + assert("nikita-2123", JF_ISSET(node, JNODE_HEARD_BANSHEE)); + assert("jmacd-511", atomic_read(&node->d_count) == 0); + + /* detach page */ + if (page != NULL) { + /* + * FIXME this is racy against jnode_extent_write(). + */ + page_clear_jnode(page, node); + } + UNLOCK_JNODE(node); + /* goodbye */ + jnode_delete(node, jtype, tree); + WUNLOCK_TREE(tree); + jnode_free(node, jtype); + /* @node is no longer valid pointer */ + if (page != NULL) + drop_page(page); + } else { + /* busy check failed: reference was acquired by concurrent + * thread. */ + JF_CLR(node, JNODE_RIP); + WUNLOCK_TREE(tree); + UNLOCK_JNODE(node); + if (page != NULL) + unlock_page(page); + } + return result; +} + +/* drop jnode on the floor. + + Return value: + + -EBUSY: failed to drop jnode, because there are still references to it + + 0: successfully dropped jnode + +*/ +static int +jdrop_in_tree(jnode * node, reiser4_tree * tree) +{ + struct page *page; + jnode_type jtype; + int result; + + assert("zam-602", node != NULL); + assert("nikita-2362", rw_tree_is_not_locked(tree)); + assert("nikita-2403", !JF_ISSET(node, JNODE_HEARD_BANSHEE)); + // assert( "nikita-2532", JF_ISSET( node, JNODE_RIP ) ); + + ON_TRACE(TRACE_PCACHE, "drop node: %p\n", node); + + jtype = jnode_get_type(node); + + page = jnode_lock_page(node); + assert("nikita-2405", spin_jnode_is_locked(node)); + + WLOCK_TREE(tree); + + /* re-check ->x_count under tree lock. */ + result = jnode_is_busy(node, jtype); + if (!result) { + assert("nikita-2488", page == jnode_page(node)); + assert("nikita-2533", atomic_read(&node->d_count) == 0); + if (page != NULL) { + assert("nikita-2126", !PageDirty(page)); + assert("nikita-2127", PageUptodate(page)); + assert("nikita-2181", PageLocked(page)); + page_clear_jnode(page, node); + } + UNLOCK_JNODE(node); + jnode_remove(node, jtype, tree); + WUNLOCK_TREE(tree); + jnode_free(node, jtype); + if (page != NULL) { + drop_page(page); + } + } else { + /* busy check failed: reference was acquired by concurrent + * thread. */ + JF_CLR(node, JNODE_RIP); + WUNLOCK_TREE(tree); + UNLOCK_JNODE(node); + if (page != NULL) + unlock_page(page); + } + return result; +} + +/* This function frees jnode "if possible". In particular, [dcx]_count has to + be 0 (where applicable). */ +reiser4_internal void +jdrop(jnode * node) +{ + jdrop_in_tree(node, jnode_get_tree(node)); +} + + +/* IO head jnode implementation; The io heads are simple j-nodes with limited + functionality (these j-nodes are not in any hash table) just for reading + from and writing to disk. */ + +reiser4_internal jnode * +alloc_io_head(const reiser4_block_nr * block) +{ + jnode *jal = jalloc(); + + if (jal != NULL) { + jnode_init(jal, current_tree, JNODE_IO_HEAD); + jnode_set_block(jal, block); + } + + jref(jal); + + return jal; +} + +reiser4_internal void +drop_io_head(jnode * node) +{ + assert("zam-648", jnode_get_type(node) == JNODE_IO_HEAD); + + jput(node); + jdrop(node); +} + +/* protect keep jnode data from reiser4_releasepage() */ +reiser4_internal void +pin_jnode_data(jnode * node) +{ + assert("zam-671", jnode_page(node) != NULL); + page_cache_get(jnode_page(node)); +} + +/* make jnode data free-able again */ +reiser4_internal void +unpin_jnode_data(jnode * node) +{ + assert("zam-672", jnode_page(node) != NULL); + page_cache_release(jnode_page(node)); +} + +reiser4_internal struct address_space * +jnode_get_mapping(const jnode * node) +{ + assert("nikita-3162", node != NULL); + return jnode_ops(node)->mapping(node); +} + +#if REISER4_DEBUG_NODE_INVARIANT +/* debugging aid: jnode invariant */ +reiser4_internal int +jnode_invariant_f(const jnode * node, + char const **msg) +{ +#define _ergo(ant, con) \ + ((*msg) = "{" #ant "} ergo {" #con "}", ergo((ant), (con))) +#define _check(exp) ((*msg) = #exp, (exp)) + + return + _check(node != NULL) && + + /* [jnode-queued] */ + + /* only relocated node can be queued, except that when znode + * is being deleted, its JNODE_RELOC bit is cleared */ + _ergo(JF_ISSET(node, JNODE_FLUSH_QUEUED), + JF_ISSET(node, JNODE_RELOC) || + JF_ISSET(node, JNODE_HEARD_BANSHEE)) && + + _check(node->jnodes.prev != NULL) && + _check(node->jnodes.next != NULL) && + + /* [jnode-dirty] invariant */ + + /* dirty inode is part of atom */ + _ergo(jnode_is_dirty(node), node->atom != NULL) && + + /* [jnode-oid] invariant */ + + /* for unformatted node ->objectid and ->mapping fields are + * consistent */ + _ergo(jnode_is_unformatted(node) && node->key.j.mapping != NULL, + node->key.j.objectid == get_inode_oid(node->key.j.mapping->host)) && + /* [jnode-atom-valid] invariant */ + + /* node atom has valid state */ + _ergo(node->atom != NULL, + node->atom->stage != ASTAGE_INVALID) && + + /* [jnode-page-binding] invariant */ + + /* if node points to page, it points back to node */ + _ergo(node->pg != NULL, jprivate(node->pg) == node) && + + /* [jnode-refs] invariant */ + + /* only referenced jnode can be loaded */ + _check(atomic_read(&node->x_count) >= atomic_read(&node->d_count)); + +} + +/* debugging aid: check znode invariant and panic if it doesn't hold */ +int +jnode_invariant(const jnode * node, int tlocked, int jlocked) +{ + char const *failed_msg; + int result; + reiser4_tree *tree; + + tree = jnode_get_tree(node); + + assert("umka-063312", node != NULL); + assert("umka-064321", tree != NULL); + + if (!jlocked && !tlocked) + LOCK_JNODE((jnode *) node); + if (!tlocked) + RLOCK_TREE(jnode_get_tree(node)); + result = jnode_invariant_f(node, &failed_msg); + if (!result) { + info_jnode("corrupted node", node); + warning("jmacd-555", "Condition %s failed", failed_msg); + } + if (!tlocked) + RUNLOCK_TREE(jnode_get_tree(node)); + if (!jlocked && !tlocked) + UNLOCK_JNODE((jnode *) node); + return result; +} + +/* REISER4_DEBUG_NODE_INVARIANT */ +#endif + +#if REISER4_STATS +void reiser4_stat_inc_at_level_jput(const jnode * node) +{ + reiser4_stat_inc_at_level(jnode_get_level(node), jnode.jput); +} + +void reiser4_stat_inc_at_level_jputlast(const jnode * node) +{ + reiser4_stat_inc_at_level(jnode_get_level(node), jnode.jputlast); +} +/* REISER4_STATS */ +#endif + +#if REISER4_DEBUG_OUTPUT + +reiser4_internal const char * +jnode_type_name(jnode_type type) +{ + switch (type) { + case JNODE_UNFORMATTED_BLOCK: + return "unformatted"; + case JNODE_FORMATTED_BLOCK: + return "formatted"; + case JNODE_BITMAP: + return "bitmap"; + case JNODE_IO_HEAD: + return "io head"; + case JNODE_INODE: + return "inode"; + case LAST_JNODE_TYPE: + return "last"; + default:{ + static char unknown[30]; + + sprintf(unknown, "unknown %i", type); + return unknown; + } + } +} + +#define jnode_state_name( node, flag ) \ + ( JF_ISSET( ( node ), ( flag ) ) ? ((#flag "|")+6) : "" ) + +/* debugging aid: output human readable information about @node */ +reiser4_internal void +info_jnode(const char *prefix /* prefix to print */ , + const jnode * node /* node to print */ ) +{ + assert("umka-068", prefix != NULL); + + if (node == NULL) { + printk("%s: null\n", prefix); + return; + } + + printk("%s: %p: state: %lx: [%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s], level: %i," + " block: %s, d_count: %d, x_count: %d, " + "pg: %p, atom: %p, lock: %i:%i, type: %s, ", + prefix, node, node->state, + jnode_state_name(node, JNODE_PARSED), + jnode_state_name(node, JNODE_HEARD_BANSHEE), + jnode_state_name(node, JNODE_LEFT_CONNECTED), + jnode_state_name(node, JNODE_RIGHT_CONNECTED), + jnode_state_name(node, JNODE_ORPHAN), + jnode_state_name(node, JNODE_CREATED), + jnode_state_name(node, JNODE_RELOC), + jnode_state_name(node, JNODE_OVRWR), + jnode_state_name(node, JNODE_DIRTY), + jnode_state_name(node, JNODE_IS_DYING), + jnode_state_name(node, JNODE_EFLUSH), + jnode_state_name(node, JNODE_FLUSH_QUEUED), + jnode_state_name(node, JNODE_RIP), + jnode_state_name(node, JNODE_MISSED_IN_CAPTURE), + jnode_state_name(node, JNODE_WRITEBACK), + jnode_state_name(node, JNODE_NEW), + jnode_state_name(node, JNODE_DKSET), + jnode_state_name(node, JNODE_EPROTECTED), + jnode_state_name(node, JNODE_REPACK), + jnode_state_name(node, JNODE_CLUSTER_PAGE), + jnode_get_level(node), sprint_address(jnode_get_block(node)), + atomic_read(&node->d_count), atomic_read(&node->x_count), + jnode_page(node), node->atom, +#if REISER4_LOCKPROF && REISER4_LOCKPROF_OBJECTS + node->guard.held, node->guard.trying, +#else + 0, 0, +#endif + jnode_type_name(jnode_get_type(node))); + if (jnode_is_unformatted(node)) { + printk("inode: %llu, index: %lu, ", + node->key.j.objectid, node->key.j.index); + } +} + +/* debugging aid: output human readable information about @node */ +reiser4_internal void +print_jnode(const char *prefix /* prefix to print */ , + const jnode * node /* node to print */) +{ + if (jnode_is_znode(node)) + print_znode(prefix, JZNODE(node)); + else + info_jnode(prefix, node); +} + +/* this is cut-n-paste replica of print_znodes() */ +reiser4_internal void +print_jnodes(const char *prefix, reiser4_tree * tree) +{ + jnode *node; + jnode *next; + j_hash_table *htable; + int tree_lock_taken; + + if (tree == NULL) + tree = current_tree; + + /* this is a debugging function. It can be called by reiser4_panic() + with tree spin-lock already held. Trylock is not exactly what we + want here, but it is passable. + */ + tree_lock_taken = write_trylock_tree(tree); + htable = &tree->jhash_table; + + for_all_in_htable(htable, j, node, next) { + info_jnode(prefix, node); + printk("\n"); + } + if (tree_lock_taken) + WUNLOCK_TREE(tree); +} + +/* REISER4_DEBUG_OUTPUT */ +#endif + +/* this is only used to created jnode during capture copy */ +reiser4_internal jnode *jclone(jnode *node) +{ + jnode *clone; + + assert("vs-1429", jnode_ops(node)->clone); + clone = jnode_ops(node)->clone(node); + if (IS_ERR(clone)) + return clone; + + jref(clone); + JF_SET(clone, JNODE_HEARD_BANSHEE); + JF_SET(clone, JNODE_CC); + return clone; +} + + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 80 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/jnode.h 2004-09-03 00:57:06.858996536 -0700 @@ -0,0 +1,790 @@ +/* Copyright 2001, 2002, 2003, 2004 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Declaration of jnode. See jnode.c for details. */ + +#ifndef __JNODE_H__ +#define __JNODE_H__ + +#include "forward.h" +#include "type_safe_hash.h" +#include "type_safe_list.h" +#include "txnmgr.h" +#include "key.h" +#include "debug.h" +#include "dformat.h" +#include "spin_macros.h" +#include "emergency_flush.h" + +#include "plugin/plugin.h" + +#include +#include +#include +#include +#include +#include +#include + +/* declare hash table of jnodes (jnodes proper, that is, unformatted + nodes) */ +TYPE_SAFE_HASH_DECLARE(j, jnode); + +/* declare hash table of znodes */ +TYPE_SAFE_HASH_DECLARE(z, znode); + +typedef struct { + __u64 objectid; + unsigned long index; + struct address_space *mapping; +} jnode_key_t; + +/* + Jnode is the "base class" of other nodes in reiser4. It is also happens to + be exactly the node we use for unformatted tree nodes. + + Jnode provides following basic functionality: + + . reference counting and indexing. + + . integration with page cache. Jnode has ->pg reference to which page can + be attached. + + . interface to transaction manager. It is jnode that is kept in transaction + manager lists, attached to atoms, etc. (NOTE-NIKITA one may argue that this + means, there should be special type of jnode for inode.) + + Locking: + + Spin lock: the following fields are protected by the per-jnode spin lock: + + ->state + ->atom + ->capture_link + + Following fields are protected by the global tree lock: + + ->link + ->key.z (content of ->key.z is only changed in znode_rehash()) + ->key.j + + Atomic counters + + ->x_count + ->d_count + + ->pg, and ->data are protected by spin lock for unused jnode and are + immutable for used jnode (one for which fs/reiser4/vfs_ops.c:releasable() + is false). + + ->tree is immutable after creation + + Unclear + + ->blocknr: should be under jnode spin-lock, but current interface is based + on passing of block address. + + If you ever need to spin lock two nodes at once, do this in "natural" + memory order: lock znode with lower address first. (See lock_two_nodes().) + + Invariants involving this data-type: + + [jnode-dirty] + [jnode-refs] + [jnode-oid] + [jnode-queued] + [jnode-atom-valid] + [jnode-page-binding] +*/ + +struct jnode { +#if REISER4_DEBUG +#define JMAGIC 0x52654973 /* "ReIs" */ + int magic; +#endif + /* FIRST CACHE LINE (16 bytes): data used by jload */ + + /* jnode's state: bitwise flags from the reiser4_jnode_state enum. */ + /* 0 */ unsigned long state; + + /* lock, protecting jnode's fields. */ + /* 4 */ reiser4_spin_data load; + + /* counter of references to jnode itself. Increased on jref(). + Decreased on jput(). + */ + /* 8 */ atomic_t x_count; + + /* counter of references to jnode's data. Pin data page(s) in + memory while this is greater than 0. Increased on jload(). + Decreased on jrelse(). + */ + /* 12 */ atomic_t d_count; + + /* SECOND CACHE LINE: data used by hash table lookups */ + + /* 16 */ union { + /* znodes are hashed by block number */ + reiser4_block_nr z; + /* unformatted nodes are hashed by mapping plus offset */ + jnode_key_t j; + } key; + + /* THIRD CACHE LINE */ + + /* 32 */ union { + /* pointers to maintain hash-table */ + z_hash_link z; + j_hash_link j; + } link; + + /* pointer to jnode page. */ + /* 36 */ struct page *pg; + /* pointer to node itself. This is page_address(node->pg) when page is + attached to the jnode + */ + /* 40 */ void *data; + + /* 44 */ reiser4_tree *tree; + + /* FOURTH CACHE LINE: atom related fields */ + + /* 48 */ reiser4_spin_data guard; + + /* atom the block is in, if any */ + /* 52 */ txn_atom *atom; + + /* capture list */ + /* 56 */ capture_list_link capture_link; + + /* FIFTH CACHE LINE */ + + /* 64 */ struct rcu_head rcu; /* crosses cache line */ + + /* SIXTH CACHE LINE */ + + /* the real blocknr (where io is going to/from) */ + /* 80 */ reiser4_block_nr blocknr; + /* Parent item type, unformatted and CRC need it for offset => key conversion. */ + /* NOTE: this parent_item_id looks like jnode type. */ + /* 88 */ reiser4_plugin_id parent_item_id; + /* 92 */ +#if REISER4_DEBUG + /* list of all jnodes for debugging purposes. */ + struct list_head jnodes; + /* how many times this jnode was written in one transaction */ + int written; + /* this indicates which atom's list the jnode is on */ + atom_list list1; + /* for debugging jnodes of one inode are attached to inode via this list */ + inode_jnodes_list_link inode_link; +#endif +} __attribute__((aligned(16))); + + +/* + * jnode types. Enumeration of existing jnode types. + */ +typedef enum { + JNODE_UNFORMATTED_BLOCK, /* unformatted block */ + JNODE_FORMATTED_BLOCK, /* formatted block, znode */ + JNODE_BITMAP, /* bitmap */ + JNODE_IO_HEAD, /* jnode representing a block in the + * wandering log */ + JNODE_INODE, /* jnode embedded into inode */ + LAST_JNODE_TYPE +} jnode_type; + +TYPE_SAFE_LIST_DEFINE(capture, jnode, capture_link); +#if REISER4_DEBUG +TYPE_SAFE_LIST_DEFINE(inode_jnodes, jnode, inode_link); +#endif + +/* jnode states */ +typedef enum { + /* jnode's page is loaded and data checked */ + JNODE_PARSED = 0, + /* node was deleted, not all locks on it were released. This + node is empty and is going to be removed from the tree + shortly. */ + JNODE_HEARD_BANSHEE = 1, + /* left sibling pointer is valid */ + JNODE_LEFT_CONNECTED = 2, + /* right sibling pointer is valid */ + JNODE_RIGHT_CONNECTED = 3, + + /* znode was just created and doesn't yet have a pointer from + its parent */ + JNODE_ORPHAN = 4, + + /* this node was created by its transaction and has not been assigned + a block address. */ + JNODE_CREATED = 5, + + /* this node is currently relocated */ + JNODE_RELOC = 6, + /* this node is currently wandered */ + JNODE_OVRWR = 7, + + /* this znode has been modified */ + JNODE_DIRTY = 8, + + /* znode lock is being invalidated */ + JNODE_IS_DYING = 9, + + /* THIS PLACE IS INTENTIONALLY LEFT BLANK */ + + JNODE_EFLUSH = 11, + + /* jnode is queued for flushing. */ + JNODE_FLUSH_QUEUED = 12, + + /* In the following bits jnode type is encoded. */ + JNODE_TYPE_1 = 13, + JNODE_TYPE_2 = 14, + JNODE_TYPE_3 = 15, + + /* jnode is being destroyed */ + JNODE_RIP = 16, + + /* znode was not captured during locking (it might so be because + ->level != LEAF_LEVEL and lock_mode == READ_LOCK) */ + JNODE_MISSED_IN_CAPTURE = 17, + + /* write is in progress */ + JNODE_WRITEBACK = 18, + + /* FIXME: now it is used by crypto-compress plugin only */ + JNODE_NEW = 19, + + /* delimiting keys are already set for this znode. */ + JNODE_DKSET = 20, + /* if page was dirtied through mmap, we don't want to lose data, even + * though page and jnode may be clean. Mark jnode with JNODE_KEEPME so + * that ->releasepage() can tell. As this is used only for + * unformatted, we can share bit with DKSET which is only meaningful + * for formatted. */ + JNODE_KEEPME = 20, + + /* cheap and effective protection of jnode from emergency flush. This + * bit can only be set by thread that holds long term lock on jnode + * parent node (twig node, where extent unit lives). */ + JNODE_EPROTECTED = 21, + JNODE_CLUSTER_PAGE = 22, + /* Jnode is marked for repacking, that means the reiser4 flush and the + * block allocator should process this node special way */ + JNODE_REPACK = 23, + /* enable node squeezing */ + JNODE_SQUEEZABLE = 24, + + JNODE_SCANNED = 25, + JNODE_JLOADED_BY_GET_OVERWRITE_SET = 26, + /* capture copy jnode */ + JNODE_CC = 27, + /* this jnode is copy of coced original */ + JNODE_CCED = 28, + /* + * When jnode is dirtied for the first time in given transaction, + * do_jnode_make_dirty() checks whether this jnode can possible became + * member of overwrite set. If so, this bit is set, and one block is + * reserved in the ->flush_reserved space of atom. + * + * This block is "used" (and JNODE_FLUSH_RESERVED bit is cleared) when + * + * (1) flush decides that we want this block to go into relocate + * set after all. + * + * (2) wandering log is allocated (by log writer) + * + * (3) extent is allocated + * + */ + JNODE_FLUSH_RESERVED = 29 +} reiser4_jnode_state; + +/* Macros for accessing the jnode state. */ + +static inline void +JF_CLR(jnode * j, int f) +{ + assert("unknown-1", j->magic == JMAGIC); + clear_bit(f, &j->state); +} +static inline int +JF_ISSET(const jnode * j, int f) +{ + assert("unknown-2", j->magic == JMAGIC); + return test_bit(f, &((jnode *) j)->state); +} +static inline void +JF_SET(jnode * j, int f) +{ + assert("unknown-3", j->magic == JMAGIC); + set_bit(f, &j->state); +} + +static inline int +JF_TEST_AND_SET(jnode * j, int f) +{ + assert("unknown-4", j->magic == JMAGIC); + return test_and_set_bit(f, &j->state); +} + +/* ordering constraint for znode spin lock: znode lock is weaker than + tree lock and dk lock */ +#define spin_ordering_pred_jnode( node ) \ + ( ( lock_counters() -> rw_locked_tree == 0 ) && \ + ( lock_counters() -> spin_locked_txnh == 0 ) && \ + ( lock_counters() -> rw_locked_zlock == 0 ) && \ + ( lock_counters() -> rw_locked_dk == 0 ) && \ + /* \ + in addition you cannot hold more than one jnode spin lock at a \ + time. \ + */ \ + ( lock_counters() -> spin_locked_jnode < 2 ) ) + +/* Define spin_lock_jnode, spin_unlock_jnode, and spin_jnode_is_locked. + Take and release short-term spinlocks. Don't hold these across + io. +*/ +SPIN_LOCK_FUNCTIONS(jnode, jnode, guard); + +#define spin_ordering_pred_jload(node) (1) + +SPIN_LOCK_FUNCTIONS(jload, jnode, load); + +static inline int +jnode_is_in_deleteset(const jnode * node) +{ + return JF_ISSET(node, JNODE_RELOC); +} + + +extern int jnode_init_static(void); +extern int jnode_done_static(void); + +/* Jnode routines */ +extern jnode *jalloc(void); +extern void jfree(jnode * node) NONNULL; +extern jnode *jclone(jnode *); +extern jnode *jlookup(reiser4_tree * tree, + oid_t objectid, unsigned long ind) NONNULL; +extern jnode *jfind(struct address_space *, unsigned long index) NONNULL; +extern jnode *jnode_by_page(struct page *pg) NONNULL; +extern jnode *jnode_of_page(struct page *pg) NONNULL; +void jnode_attach_page(jnode * node, struct page *pg); +jnode *find_get_jnode(reiser4_tree * tree, + struct address_space *mapping, oid_t oid, + unsigned long index); + +void unhash_unformatted_jnode(jnode *); +struct page *jnode_get_page_locked(jnode *, int gfp_flags); +extern jnode *page_next_jnode(jnode * node) NONNULL; +extern void jnode_init(jnode * node, reiser4_tree * tree, jnode_type) NONNULL; +extern void jnode_make_dirty(jnode * node) NONNULL; +extern void jnode_make_clean(jnode * node) NONNULL; +extern void jnode_make_wander_nolock(jnode * node) NONNULL; +extern void jnode_make_wander(jnode*) NONNULL; +extern void znode_make_reloc(znode*, flush_queue_t*) NONNULL; +extern void unformatted_make_reloc(jnode*, flush_queue_t*) NONNULL; + +extern void jnode_set_block(jnode * node, + const reiser4_block_nr * blocknr) NONNULL; +extern struct page *jnode_lock_page(jnode *) NONNULL; +extern struct address_space *jnode_get_mapping(const jnode * node) NONNULL; + +/* block number of node */ +static inline const reiser4_block_nr * +jnode_get_block(const jnode * node /* jnode to query */) +{ + assert("nikita-528", node != NULL); + + return &node->blocknr; +} + +/* block number for IO. Usually this is the same as jnode_get_block(), unless + * jnode was emergency flushed---then block number chosen by eflush is + * used. */ +static inline const reiser4_block_nr * +jnode_get_io_block(const jnode * node) +{ + assert("nikita-2768", node != NULL); + assert("nikita-2769", spin_jnode_is_locked(node)); + + if (unlikely(JF_ISSET(node, JNODE_EFLUSH))) + return eflush_get(node); + else + return jnode_get_block(node); +} + +/* Jnode flush interface. */ +extern reiser4_blocknr_hint *pos_hint(flush_pos_t * pos); +extern int pos_leaf_relocate(flush_pos_t * pos); +extern flush_queue_t * pos_fq(flush_pos_t * pos); + +/* FIXME-VS: these are used in plugin/item/extent.c */ + +/* does extent_get_block have to be called */ +#define jnode_mapped(node) JF_ISSET (node, JNODE_MAPPED) +#define jnode_set_mapped(node) JF_SET (node, JNODE_MAPPED) +/* pointer to this block was just created (either by appending or by plugging a + hole), or zinit_new was called */ +#define jnode_created(node) JF_ISSET (node, JNODE_CREATED) +#define jnode_set_created(node) JF_SET (node, JNODE_CREATED) + +/* the node should be squeezed during flush squalloc phase */ +#define jnode_squeezable(node) JF_ISSET (node, JNODE_SQUEEZABLE) +#define jnode_set_squeezable(node) JF_SET (node, JNODE_SQUEEZABLE) + +/* Macros to convert from jnode to znode, znode to jnode. These are macros + because C doesn't allow overloading of const prototypes. */ +#define ZJNODE(x) (& (x) -> zjnode) +#define JZNODE(x) \ +({ \ + typeof (x) __tmp_x; \ + \ + __tmp_x = (x); \ + assert ("jmacd-1300", jnode_is_znode (__tmp_x)); \ + (znode*) __tmp_x; \ +}) + +extern int jnodes_tree_init(reiser4_tree * tree); +extern int jnodes_tree_done(reiser4_tree * tree); + +#if REISER4_DEBUG +extern int znode_is_any_locked(const znode * node); +extern void jnode_list_remove(jnode * node); +#else +#define jnode_list_remove(node) noop +#endif + +#if REISER4_DEBUG_NODE_INVARIANT +extern int jnode_invariant(const jnode * node, int tlocked, int jlocked); +#else +#define jnode_invariant(n, t, j) (1) +#endif + +#if REISER4_DEBUG_OUTPUT +extern void info_jnode(const char *prefix, const jnode * node); +extern void print_jnode(const char *prefix, const jnode * node); +extern void print_jnodes(const char *prefix, reiser4_tree * tree); +#else +#define info_jnode(p, n) noop +#define print_jnodes(p, t) noop +#define print_jnode(p, n) noop +#endif + +int znode_is_root(const znode * node) NONNULL; + +/* bump reference counter on @node */ +static inline void +add_x_ref(jnode * node /* node to increase x_count of */ ) +{ + assert("nikita-1911", node != NULL); + + atomic_inc(&node->x_count); + LOCK_CNT_INC(x_refs); +} + +static inline void +dec_x_ref(jnode * node) +{ + assert("nikita-3215", node != NULL); + assert("nikita-3216", atomic_read(&node->x_count) > 0); + + atomic_dec(&node->x_count); + assert("nikita-3217", LOCK_CNT_GTZ(x_refs)); + LOCK_CNT_DEC(x_refs); +} + +/* jref() - increase counter of references to jnode/znode (x_count) */ +static inline jnode * +jref(jnode * node) +{ + assert("jmacd-508", (node != NULL) && !IS_ERR(node)); + add_x_ref(node); + return node; +} + +extern int jdelete(jnode * node) NONNULL; + +/* get the page of jnode */ +static inline struct page * +jnode_page(const jnode * node) +{ + return node->pg; +} + +/* return pointer to jnode data */ +static inline char * +jdata(const jnode * node) +{ + assert("nikita-1415", node != NULL); + assert("nikita-3198", jnode_page(node) != NULL); + return node->data; +} + +static inline int +jnode_is_loaded(const jnode * node) +{ + assert("zam-506", node != NULL); + return atomic_read(&node->d_count) > 0; +} + +extern void page_detach_jnode(struct page *page, + struct address_space *mapping, + unsigned long index) NONNULL; +extern void page_clear_jnode(struct page *page, jnode * node) NONNULL; + +static inline void +jnode_set_reloc(jnode * node) +{ + assert("nikita-2431", node != NULL); + assert("nikita-2432", !JF_ISSET(node, JNODE_OVRWR)); + JF_SET(node, JNODE_RELOC); +} + +/* bump data counter on @node */ +static inline void add_d_ref(jnode * node /* node to increase d_count of */ ) +{ + assert("nikita-1962", node != NULL); + + atomic_inc(&node->d_count); + LOCK_CNT_INC(d_refs); +} + + +/* jload/jwrite/junload give a bread/bwrite/brelse functionality for jnodes */ + +extern int jload_gfp(jnode * node, int gfp, int do_kmap) NONNULL; + +static inline int jload(jnode * node) +{ + return jload_gfp(node, GFP_KERNEL, 1); +} + +extern int jinit_new(jnode * node, int gfp_flags) NONNULL; +extern int jstartio(jnode * node) NONNULL; + +extern void jdrop(jnode * node) NONNULL; +extern int jwait_io(jnode * node, int rw) NONNULL; + +void jload_prefetch(jnode *); + +extern jnode *alloc_io_head(const reiser4_block_nr * block) NONNULL; +extern void drop_io_head(jnode * node) NONNULL; + +static inline reiser4_tree * +jnode_get_tree(const jnode * node) +{ + assert("nikita-2691", node != NULL); + return node->tree; +} + +extern void pin_jnode_data(jnode *); +extern void unpin_jnode_data(jnode *); + +static inline jnode_type +jnode_get_type(const jnode * node) +{ + static const unsigned long state_mask = + (1 << JNODE_TYPE_1) | (1 << JNODE_TYPE_2) | (1 << JNODE_TYPE_3); + + static jnode_type mask_to_type[] = { + /* JNODE_TYPE_3 : JNODE_TYPE_2 : JNODE_TYPE_1 */ + + /* 000 */ + [0] = JNODE_FORMATTED_BLOCK, + /* 001 */ + [1] = JNODE_UNFORMATTED_BLOCK, + /* 010 */ + [2] = JNODE_BITMAP, + /* 011 */ + [3] = LAST_JNODE_TYPE, /*invalid */ + /* 100 */ + [4] = JNODE_INODE, + /* 101 */ + [5] = LAST_JNODE_TYPE, + /* 110 */ + [6] = JNODE_IO_HEAD, + /* 111 */ + [7] = LAST_JNODE_TYPE, /* invalid */ + }; + + return mask_to_type[(node->state & state_mask) >> JNODE_TYPE_1]; +} + +/* returns true if node is a znode */ +static inline int +jnode_is_znode(const jnode * node) +{ + return jnode_get_type(node) == JNODE_FORMATTED_BLOCK; +} + +/* return true if "node" is dirty */ +static inline int +jnode_is_dirty(const jnode * node) +{ + assert("nikita-782", node != NULL); + assert("jmacd-1800", spin_jnode_is_locked(node) || (jnode_is_znode(node) && znode_is_any_locked(JZNODE(node)))); + return JF_ISSET(node, JNODE_DIRTY); +} + +/* return true if "node" is dirty, node is unlocked */ +static inline int +jnode_check_dirty(jnode * node) +{ + assert("jmacd-7798", node != NULL); + assert("jmacd-7799", spin_jnode_is_not_locked(node)); + return UNDER_SPIN(jnode, node, jnode_is_dirty(node)); +} + +static inline int +jnode_is_flushprepped(const jnode * node) +{ + assert("jmacd-78212", node != NULL); + assert("jmacd-71276", spin_jnode_is_locked(node)); + return !jnode_is_dirty(node) || JF_ISSET(node, JNODE_RELOC) + || JF_ISSET(node, JNODE_OVRWR); +} + +/* Return true if @node has already been processed by the squeeze and allocate + process. This implies the block address has been finalized for the + duration of this atom (or it is clean and will remain in place). If this + returns true you may use the block number as a hint. */ +static inline int +jnode_check_flushprepped(jnode * node) +{ + /* It must be clean or relocated or wandered. New allocations are set to relocate. */ + assert("jmacd-71275", spin_jnode_is_not_locked(node)); + return UNDER_SPIN(jnode, node, jnode_is_flushprepped(node)); +} + +/* returns true if node is unformatted */ +static inline int +jnode_is_unformatted(const jnode * node) +{ + assert("jmacd-0123", node != NULL); + return jnode_get_type(node) == JNODE_UNFORMATTED_BLOCK; +} + +/* returns true if node represents a cluster cache page */ +static inline int +jnode_is_cluster_page(const jnode * node) +{ + assert("edward-50", node != NULL); + return (JF_ISSET(node, JNODE_CLUSTER_PAGE)); +} + +/* returns true is node is builtin inode's jnode */ +static inline int +jnode_is_inode(const jnode * node) +{ + assert("vs-1240", node != NULL); + return jnode_get_type(node) == JNODE_INODE; +} + +static inline jnode_plugin * +jnode_ops_of(const jnode_type type) +{ + assert("nikita-2367", type < LAST_JNODE_TYPE); + return jnode_plugin_by_id((reiser4_plugin_id) type); +} + +static inline jnode_plugin * +jnode_ops(const jnode * node) +{ + assert("nikita-2366", node != NULL); + + return jnode_ops_of(jnode_get_type(node)); +} + +/* Get the index of a block. */ +static inline unsigned long +jnode_get_index(jnode * node) +{ + return jnode_ops(node)->index(node); +} + +/* return true if "node" is the root */ +static inline int +jnode_is_root(const jnode * node) +{ + return jnode_is_znode(node) && znode_is_root(JZNODE(node)); +} + +extern struct address_space * mapping_jnode(const jnode * node); +extern unsigned long index_jnode(const jnode * node); + +extern int jnode_try_drop(jnode * node); + +static inline void jput(jnode * node); +extern void jput_final(jnode * node); + +#if REISER4_STATS +extern void reiser4_stat_inc_at_level_jput(const jnode * node); +extern void reiser4_stat_inc_at_level_jputlast(const jnode * node); +#else +#define reiser4_stat_inc_at_level_jput(node) noop +#define reiser4_stat_inc_at_level_jputlast(node) noop +#endif + +/* jput() - decrement x_count reference counter on znode. + + Count may drop to 0, jnode stays in cache until memory pressure causes the + eviction of its page. The c_count variable also ensures that children are + pressured out of memory before the parent. The jnode remains hashed as + long as the VM allows its page to stay in memory. +*/ +static inline void +jput(jnode * node) +{ + trace_stamp(TRACE_ZNODES); + + assert("jmacd-509", node != NULL); + assert("jmacd-510", atomic_read(&node->x_count) > 0); + assert("nikita-3065", spin_jnode_is_not_locked(node)); + assert("zam-926", schedulable()); + LOCK_CNT_DEC(x_refs); + + reiser4_stat_inc_at_level_jput(node); + rcu_read_lock(); + /* + * we don't need any kind of lock here--jput_final() uses RCU. + */ + if (unlikely(atomic_dec_and_test(&node->x_count))) { + reiser4_stat_inc_at_level_jputlast(node); + jput_final(node); + } else + rcu_read_unlock(); + assert("nikita-3473", schedulable()); +} + +extern void jrelse(jnode * node); +extern void jrelse_tail(jnode * node); + +extern jnode *jnode_rip_sync(reiser4_tree *t, jnode * node); + +/* resolve race with jput */ +static inline jnode * +jnode_rip_check(reiser4_tree *tree, jnode * node) +{ + if (unlikely(JF_ISSET(node, JNODE_RIP))) + node = jnode_rip_sync(tree, node); + return node; +} + +extern reiser4_key * jnode_build_key(const jnode * node, reiser4_key * key); + +/* __JNODE_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/kassign.c 2004-09-03 00:57:05.093264968 -0700 @@ -0,0 +1,781 @@ +/* Copyright 2001, 2002, 2003, 2004 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Key assignment policy implementation */ + +/* + * In reiser4 every piece of file system data and meta-data has a key. Keys + * are used to store information in and retrieve it from reiser4 internal + * tree. In addition to this, keys define _ordering_ of all file system + * information: things having close keys are placed into the same or + * neighboring (in the tree order) nodes of the tree. As our block allocator + * tries to respect tree order (see flush.c), keys also define order in which + * things are laid out on the disk, and hence, affect performance directly. + * + * Obviously, assignment of keys to data and meta-data should be consistent + * across whole file system. Algorithm that calculates a key for a given piece + * of data or meta-data is referred to as "key assignment". + * + * Key assignment is too expensive to be implemented as a plugin (that is, + * with an ability to support different key assignment schemas in the same + * compiled kernel image). As a compromise, all key-assignment functions and + * data-structures are collected in this single file, so that modifications to + * key assignment algorithm can be localized. Additional changes may be + * required in key.[ch]. + * + * Current default reiser4 key assignment algorithm is dubbed "Plan A". As one + * may guess, there is "Plan B" too. + * + */ + +/* + * Additional complication with key assignment implementation is a requirement + * to support different key length. + */ + +/* + * KEY ASSIGNMENT: PLAN A, LONG KEYS. + * + * DIRECTORY ITEMS + * + * | 60 | 4 | 7 |1| 56 | 64 | 64 | + * +--------------+---+---+-+-------------+------------------+-----------------+ + * | dirid | 0 | F |H| prefix-1 | prefix-2 | prefix-3/hash | + * +--------------+---+---+-+-------------+------------------+-----------------+ + * | | | | | + * | 8 bytes | 8 bytes | 8 bytes | 8 bytes | + * + * dirid objectid of directory this item is for + * + * F fibration, see fs/reiser4/plugin/fibration.[ch] + * + * H 1 if last 8 bytes of the key contain hash, + * 0 if last 8 bytes of the key contain prefix-3 + * + * prefix-1 first 7 characters of file name. + * Padded by zeroes if name is not long enough. + * + * prefix-2 next 8 characters of the file name. + * + * prefix-3 next 8 characters of the file name. + * + * hash hash of the rest of file name (i.e., portion of file + * name not included into prefix-1 and prefix-2). + * + * File names shorter than 23 (== 7 + 8 + 8) characters are completely encoded + * in the key. Such file names are called "short". They are distinguished by H + * bit set 0 in the key. + * + * Other file names are "long". For long name, H bit is 1, and first 15 (== 7 + * + 8) characters are encoded in prefix-1 and prefix-2 portions of the + * key. Last 8 bytes of the key are occupied by hash of the remaining + * characters of the name. + * + * This key assignment reaches following important goals: + * + * (1) directory entries are sorted in approximately lexicographical + * order. + * + * (2) collisions (when multiple directory items have the same key), while + * principally unavoidable in a tree with fixed length keys, are rare. + * + * STAT DATA + * + * | 60 | 4 | 64 | 4 | 60 | 64 | + * +--------------+---+-----------------+---+--------------+-----------------+ + * | locality id | 1 | ordering | 0 | objectid | 0 | + * +--------------+---+-----------------+---+--------------+-----------------+ + * | | | | | + * | 8 bytes | 8 bytes | 8 bytes | 8 bytes | + * + * locality id object id of a directory where first name was created for + * the object + * + * ordering copy of second 8-byte portion of the key of directory + * entry for the first name of this object. Ordering has a form + * { + * fibration :7; + * h :1; + * prefix1 :56; + * } + * see description of key for directory entry above. + * + * objectid object id for this object + * + * This key assignment policy is designed to keep stat-data in the same order + * as corresponding directory items, thus speeding up readdir/stat types of + * workload. + * + * FILE BODY + * + * | 60 | 4 | 64 | 4 | 60 | 64 | + * +--------------+---+-----------------+---+--------------+-----------------+ + * | locality id | 4 | ordering | 0 | objectid | offset | + * +--------------+---+-----------------+---+--------------+-----------------+ + * | | | | | + * | 8 bytes | 8 bytes | 8 bytes | 8 bytes | + * + * locality id object id of a directory where first name was created for + * the object + * + * ordering the same as in the key of stat-data for this object + * + * objectid object id for this object + * + * offset logical offset from the beginning of this file. + * Measured in bytes. + * + * + * KEY ASSIGNMENT: PLAN A, SHORT KEYS. + * + * DIRECTORY ITEMS + * + * | 60 | 4 | 7 |1| 56 | 64 | + * +--------------+---+---+-+-------------+-----------------+ + * | dirid | 0 | F |H| prefix-1 | prefix-2/hash | + * +--------------+---+---+-+-------------+-----------------+ + * | | | | + * | 8 bytes | 8 bytes | 8 bytes | + * + * dirid objectid of directory this item is for + * + * F fibration, see fs/reiser4/plugin/fibration.[ch] + * + * H 1 if last 8 bytes of the key contain hash, + * 0 if last 8 bytes of the key contain prefix-2 + * + * prefix-1 first 7 characters of file name. + * Padded by zeroes if name is not long enough. + * + * prefix-2 next 8 characters of the file name. + * + * hash hash of the rest of file name (i.e., portion of file + * name not included into prefix-1). + * + * File names shorter than 15 (== 7 + 8) characters are completely encoded in + * the key. Such file names are called "short". They are distinguished by H + * bit set in the key. + * + * Other file names are "long". For long name, H bit is 0, and first 7 + * characters are encoded in prefix-1 portion of the key. Last 8 bytes of the + * key are occupied by hash of the remaining characters of the name. + * + * STAT DATA + * + * | 60 | 4 | 4 | 60 | 64 | + * +--------------+---+---+--------------+-----------------+ + * | locality id | 1 | 0 | objectid | 0 | + * +--------------+---+---+--------------+-----------------+ + * | | | | + * | 8 bytes | 8 bytes | 8 bytes | + * + * locality id object id of a directory where first name was created for + * the object + * + * objectid object id for this object + * + * FILE BODY + * + * | 60 | 4 | 4 | 60 | 64 | + * +--------------+---+---+--------------+-----------------+ + * | locality id | 4 | 0 | objectid | offset | + * +--------------+---+---+--------------+-----------------+ + * | | | | + * | 8 bytes | 8 bytes | 8 bytes | + * + * locality id object id of a directory where first name was created for + * the object + * + * objectid object id for this object + * + * offset logical offset from the beginning of this file. + * Measured in bytes. + * + * + */ + +#include "debug.h" +#include "key.h" +#include "kassign.h" +#include "vfs_ops.h" +#include "inode.h" +#include "super.h" +#include "dscale.h" + +#include /* for __u?? */ +#include /* for struct super_block, etc */ + +#if REISER4_LARGE_KEY +#define ORDERING_CHARS (sizeof(__u64) - 1) +#define OID_CHARS (sizeof(__u64)) +#else +#define ORDERING_CHARS (0) +#define OID_CHARS (sizeof(__u64) - 1) +#endif + +#define OFFSET_CHARS (sizeof(__u64)) + +#define INLINE_CHARS (ORDERING_CHARS + OID_CHARS) + +/* bitmask for H bit (see comment at the beginning of this file */ +static const __u64 longname_mark = 0x0100000000000000ull; +/* bitmask for F and H portions of the key. */ +static const __u64 fibration_mask = 0xff00000000000000ull; + +/* return true if name is not completely encoded in @key */ +reiser4_internal int +is_longname_key(const reiser4_key *key) +{ + __u64 highpart; + + assert("nikita-2863", key != NULL); + if (get_key_type(key) != KEY_FILE_NAME_MINOR) + print_key("oops", key); + assert("nikita-2864", get_key_type(key) == KEY_FILE_NAME_MINOR); + + if (REISER4_LARGE_KEY) + highpart = get_key_ordering(key); + else + highpart = get_key_objectid(key); + + return (highpart & longname_mark) ? 1 : 0; +} + +/* return true if @name is too long to be completely encoded in the key */ +reiser4_internal int +is_longname(const char *name UNUSED_ARG, int len) +{ + return len > ORDERING_CHARS + OID_CHARS + OFFSET_CHARS; +} + +/* code ascii string into __u64. + + Put characters of @name into result (@str) one after another starting + from @start_idx-th highest (arithmetically) byte. This produces + endian-safe encoding. memcpy(2) will not do. + +*/ +static __u64 +pack_string(const char *name /* string to encode */ , + int start_idx /* highest byte in result from + * which to start encoding */ ) +{ + unsigned i; + __u64 str; + + str = 0; + for (i = 0; (i < sizeof str - start_idx) && name[i]; ++i) { + str <<= 8; + str |= (unsigned char) name[i]; + } + str <<= (sizeof str - i - start_idx) << 3; + return str; +} + +#if !REISER4_DEBUG_OUTPUT +static +#endif +/* opposite to pack_string(). Takes value produced by pack_string(), restores + * string encoded in it and stores result in @buf */ +reiser4_internal char * +unpack_string(__u64 value, char *buf) +{ + do { + *buf = value >> (64 - 8); + if (*buf) + ++ buf; + value <<= 8; + } while(value != 0); + *buf = 0; + return buf; +} + +/* obtain name encoded in @key and store it in @buf */ +reiser4_internal char * +extract_name_from_key(const reiser4_key *key, char *buf) +{ + char *c; + + assert("nikita-2868", !is_longname_key(key)); + + c = buf; + if (REISER4_LARGE_KEY) { + c = unpack_string(get_key_ordering(key) & ~fibration_mask, c); + c = unpack_string(get_key_fulloid(key), c); + } else + c = unpack_string(get_key_fulloid(key) & ~fibration_mask, c); + unpack_string(get_key_offset(key), c); + return buf; +} + +/* build key for directory entry. + ->build_entry_key() for directory plugin */ +reiser4_internal void +build_entry_key_common(const struct inode *dir /* directory where entry is + * (or will be) in.*/ , + const struct qstr *qname /* name of file referenced + * by this entry */ , + reiser4_key * result /* resulting key of directory + * entry */ ) +{ + __u64 ordering; + __u64 objectid; + __u64 offset; + const char *name; + int len; + +#if REISER4_LARGE_KEY +#define second_el ordering +#else +#define second_el objectid +#endif + + assert("nikita-1139", dir != NULL); + assert("nikita-1140", qname != NULL); + assert("nikita-1141", qname->name != NULL); + assert("nikita-1142", result != NULL); + + name = qname->name; + len = qname->len; + + assert("nikita-2867", strlen(name) == len); + + key_init(result); + /* locality of directory entry's key is objectid of parent + directory */ + set_key_locality(result, get_inode_oid(dir)); + /* minor packing locality is constant */ + set_key_type(result, KEY_FILE_NAME_MINOR); + /* dot is special case---we always want it to be first entry in + a directory. Actually, we just want to have smallest + directory entry. + */ + if (len == 1 && name[0] == '.') + return; + + /* This is our brand new proposed key allocation algorithm for + directory entries: + + If name is shorter than 7 + 8 = 15 characters, put first 7 + characters into objectid field and remaining characters (if + any) into offset field. Dream long dreamt came true: file + name as a key! + + If file name is longer than 15 characters, put first 7 + characters into objectid and hash of remaining characters + into offset field. + + To distinguish above cases, in latter set up unused high bit + in objectid field. + + + With large keys (REISER4_LARGE_KEY) algorithm is updated + appropriately. + */ + + /* objectid of key is composed of seven first characters of + file's name. This imposes global ordering on directory + entries. + */ + second_el = pack_string(name, 1); + if (REISER4_LARGE_KEY) { + if (len > ORDERING_CHARS) + objectid = pack_string(name + ORDERING_CHARS, 0); + else + objectid = 0ull; + } + + if (!is_longname(name, len)) { + if (len > INLINE_CHARS) + offset = pack_string(name + INLINE_CHARS, 0); + else + offset = 0ull; + } else { + /* note in a key the fact that offset contains hash. */ + second_el |= longname_mark; + + /* offset is the hash of the file name. */ + offset = inode_hash_plugin(dir)->hash(name + INLINE_CHARS, + len - INLINE_CHARS); + } + + assert("nikita-3480", inode_fibration_plugin(dir) != NULL); + second_el |= inode_fibration_plugin(dir)->fibre(dir, name, len); + + if (REISER4_LARGE_KEY) { + set_key_ordering(result, ordering); + set_key_fulloid(result, objectid); + } else { + /* objectid is 60 bits */ + assert("nikita-1405", !(objectid & ~KEY_OBJECTID_MASK)); + set_key_objectid(result, objectid); + } + set_key_offset(result, offset); + return; +} + +/* build key for directory entry. + ->build_entry_key() for directory plugin + + This is for directories where we want repeatable and restartable readdir() + even in case 32bit user level struct dirent (readdir(3)). +*/ +reiser4_internal void +build_entry_key_stable_entry(const struct inode *dir /* directory where + * entry is (or + * will be) in. */ , + const struct qstr *name /* name of file + * referenced by + * this entry */ , + reiser4_key * result /* resulting key of + * directory entry */ ) +{ + oid_t objectid; + + assert("nikita-2283", dir != NULL); + assert("nikita-2284", name != NULL); + assert("nikita-2285", name->name != NULL); + assert("nikita-2286", result != NULL); + + key_init(result); + /* locality of directory entry's key is objectid of parent + directory */ + set_key_locality(result, get_inode_oid(dir)); + /* minor packing locality is constant */ + set_key_type(result, KEY_FILE_NAME_MINOR); + /* dot is special case---we always want it to be first entry in + a directory. Actually, we just want to have smallest + directory entry. + */ + if ((name->len == 1) && (name->name[0] == '.')) + return; + + /* objectid of key is 31 lowest bits of hash. */ + objectid = inode_hash_plugin(dir)->hash(name->name, (int) name->len) & 0x7fffffff; + + assert("nikita-2303", !(objectid & ~KEY_OBJECTID_MASK)); + set_key_objectid(result, objectid); + + /* offset is always 0. */ + set_key_offset(result, (__u64) 0); + return; +} + +/* build key to be used by ->readdir() method. + + See reiser4_readdir() for more detailed comment. + Common implementation of dir plugin's method build_readdir_key +*/ +reiser4_internal int +build_readdir_key_common(struct file *dir /* directory being read */ , + reiser4_key * result /* where to store key */ ) +{ + reiser4_file_fsdata *fdata; + struct inode *inode; + + assert("nikita-1361", dir != NULL); + assert("nikita-1362", result != NULL); + assert("nikita-1363", dir->f_dentry != NULL); + inode = dir->f_dentry->d_inode; + assert("nikita-1373", inode != NULL); + + fdata = reiser4_get_file_fsdata(dir); + if (IS_ERR(fdata)) + return PTR_ERR(fdata); + assert("nikita-1364", fdata != NULL); + return extract_key_from_de_id(get_inode_oid(inode), &fdata->dir.readdir.position.dir_entry_key, result); + +} + +/* true, if @key is the key of "." */ +reiser4_internal int +is_dot_key(const reiser4_key * key /* key to check */ ) +{ + assert("nikita-1717", key != NULL); + assert("nikita-1718", get_key_type(key) == KEY_FILE_NAME_MINOR); + return + (get_key_ordering(key) == 0ull) && + (get_key_objectid(key) == 0ull) && + (get_key_offset(key) == 0ull); +} + +/* build key for stat-data. + + return key of stat-data of this object. This should became sd plugin + method in the future. For now, let it be here. + +*/ +reiser4_internal reiser4_key * +build_sd_key(const struct inode * target /* inode of an object */ , + reiser4_key * result /* resulting key of @target + stat-data */ ) +{ + assert("nikita-261", result != NULL); + + key_init(result); + set_key_locality(result, reiser4_inode_data(target)->locality_id); + set_key_ordering(result, get_inode_ordering(target)); + set_key_objectid(result, get_inode_oid(target)); + set_key_type(result, KEY_SD_MINOR); + set_key_offset(result, (__u64) 0); + return result; +} + +/* encode part of key into &obj_key_id + + This encodes into @id part of @key sufficient to restore @key later, + given that latter is key of object (key of stat-data). + + See &obj_key_id +*/ +reiser4_internal int +build_obj_key_id(const reiser4_key * key /* key to encode */ , + obj_key_id * id /* id where key is encoded in */ ) +{ + assert("nikita-1151", key != NULL); + assert("nikita-1152", id != NULL); + + xmemcpy(id, key, sizeof *id); + return 0; +} + +/* encode reference to @obj in @id. + + This is like build_obj_key_id() above, but takes inode as parameter. */ +reiser4_internal int +build_inode_key_id(const struct inode *obj /* object to build key of */ , + obj_key_id * id /* result */ ) +{ + reiser4_key sdkey; + + assert("nikita-1166", obj != NULL); + assert("nikita-1167", id != NULL); + + build_sd_key(obj, &sdkey); + build_obj_key_id(&sdkey, id); + return 0; +} + +/* decode @id back into @key + + Restore key of object stat-data from @id. This is dual to + build_obj_key_id() above. +*/ +reiser4_internal int +extract_key_from_id(const obj_key_id * id /* object key id to extract key + * from */ , + reiser4_key * key /* result */ ) +{ + assert("nikita-1153", id != NULL); + assert("nikita-1154", key != NULL); + + key_init(key); + xmemcpy(key, id, sizeof *id); + return 0; +} + +/* extract objectid of directory from key of directory entry within said + directory. + */ +reiser4_internal oid_t +extract_dir_id_from_key(const reiser4_key * de_key /* key of + * directory + * entry */ ) +{ + assert("nikita-1314", de_key != NULL); + return get_key_locality(de_key); +} + +/* encode into @id key of directory entry. + + Encode into @id information sufficient to later distinguish directory + entries within the same directory. This is not whole key, because all + directory entries within directory item share locality which is equal + to objectid of their directory. + +*/ +reiser4_internal int +build_de_id(const struct inode *dir /* inode of directory */ , + const struct qstr *name /* name to be given to @obj by + * directory entry being + * constructed */ , + de_id * id /* short key of directory entry */ ) +{ + reiser4_key key; + + assert("nikita-1290", dir != NULL); + assert("nikita-1292", id != NULL); + + /* NOTE-NIKITA this is suboptimal. */ + inode_dir_plugin(dir)->build_entry_key(dir, name, &key); + return build_de_id_by_key(&key, id); +} + +/* encode into @id key of directory entry. + + Encode into @id information sufficient to later distinguish directory + entries within the same directory. This is not whole key, because all + directory entries within directory item share locality which is equal + to objectid of their directory. + +*/ +reiser4_internal int +build_de_id_by_key(const reiser4_key * entry_key /* full key of directory + * entry */ , + de_id * id /* short key of directory entry */ ) +{ + xmemcpy(id, ((__u64 *) entry_key) + 1, sizeof *id); + return 0; +} + +/* restore from @id key of directory entry. + + Function dual to build_de_id(): given @id and locality, build full + key of directory entry within directory item. + +*/ +reiser4_internal int +extract_key_from_de_id(const oid_t locality /* locality of directory + * entry */ , + const de_id * id /* directory entry id */ , + reiser4_key * key /* result */ ) +{ + /* no need to initialise key here: all fields are overwritten */ + xmemcpy(((__u64 *) key) + 1, id, sizeof *id); + set_key_locality(key, locality); + set_key_type(key, KEY_FILE_NAME_MINOR); + return 0; +} + +/* compare two &obj_key_id */ +reiser4_internal cmp_t +key_id_cmp(const obj_key_id * i1 /* first object key id to compare */ , + const obj_key_id * i2 /* second object key id to compare */ ) +{ + reiser4_key k1; + reiser4_key k2; + + extract_key_from_id(i1, &k1); + extract_key_from_id(i2, &k2); + return keycmp(&k1, &k2); +} + +/* compare &obj_key_id with full key */ +reiser4_internal cmp_t +key_id_key_cmp(const obj_key_id * id /* object key id to compare */ , + const reiser4_key * key /* key to compare */ ) +{ + reiser4_key k1; + + extract_key_from_id(id, &k1); + return keycmp(&k1, key); +} + +/* compare two &de_id's */ +reiser4_internal cmp_t +de_id_cmp(const de_id * id1 /* first &de_id to compare */ , + const de_id * id2 /* second &de_id to compare */ ) +{ + /* NOTE-NIKITA ugly implementation */ + reiser4_key k1; + reiser4_key k2; + + extract_key_from_de_id((oid_t) 0, id1, &k1); + extract_key_from_de_id((oid_t) 0, id2, &k2); + return keycmp(&k1, &k2); +} + +/* compare &de_id with key */ +reiser4_internal cmp_t +de_id_key_cmp(const de_id * id /* directory entry id to compare */ , + const reiser4_key * key /* key to compare */ ) +{ + cmp_t result; + reiser4_key *k1; + + k1 = (reiser4_key *)(((unsigned long)id) - sizeof key->el[0]); + result = KEY_DIFF_EL(k1, key, 1); + if (result == EQUAL_TO) { + result = KEY_DIFF_EL(k1, key, 2); + if (REISER4_LARGE_KEY && result == EQUAL_TO) { + result = KEY_DIFF_EL(k1, key, 3); + } + } + return result; +} + +/* true if key of root directory sd */ +reiser4_internal int +is_root_dir_key(const struct super_block *super /* super block to check */ , + const reiser4_key * key /* key to check */ ) +{ + assert("nikita-1819", super != NULL); + assert("nikita-1820", key != NULL); + /* call disk plugin's root_dir_key method if it exists */ + if (get_super_private(super)->df_plug && get_super_private(super)->df_plug->root_dir_key) + return keyeq(key, get_super_private(super)->df_plug->root_dir_key(super)); + return 0; +} + +/* + * return number of bytes necessary to encode @inode identity. + */ +int inode_onwire_size(const struct inode *inode) +{ + int result; + + result = dscale_bytes(get_inode_oid(inode)); + result += dscale_bytes(get_inode_locality(inode)); + + /* + * ordering is large (it usually has highest bits set), so it makes + * little sense to dscale it. + */ + if (REISER4_LARGE_KEY) + result += sizeof(get_inode_ordering(inode)); + return result; +} + +/* + * encode @inode identity at @start + */ +char *build_inode_onwire(const struct inode *inode, char *start) +{ + start += dscale_write(start, get_inode_locality(inode)); + start += dscale_write(start, get_inode_oid(inode)); + + if (REISER4_LARGE_KEY) { + cputod64(get_inode_ordering(inode), (d64 *)start); + start += sizeof(get_inode_ordering(inode)); + } + return start; +} + +/* + * extract key that was previously encoded by build_inode_onwire() at @addr + */ +char *extract_obj_key_id_from_onwire(char *addr, obj_key_id *key_id) +{ + __u64 val; + + addr += dscale_read(addr, &val); + val = (val << KEY_LOCALITY_SHIFT) | KEY_SD_MINOR; + cputod64(val, (d64 *)key_id->locality); + addr += dscale_read(addr, &val); + cputod64(val, (d64 *)key_id->objectid); +#if REISER4_LARGE_KEY + memcpy(&key_id->ordering, addr, sizeof key_id->ordering); + addr += sizeof key_id->ordering; +#endif + return addr; +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/kassign.h 2004-09-03 00:57:05.094264816 -0700 @@ -0,0 +1,100 @@ +/* Copyright 2001, 2002, 2003, 2004 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Key assignment policy interface. See kassign.c for details. */ + +#if !defined( __KASSIGN_H__ ) +#define __KASSIGN_H__ + +#include "forward.h" +#include "key.h" +#include "dformat.h" + +#include /* for __u?? */ +#include /* for struct super_block, etc */ +#include /* for struct qstr */ + +/* key assignment functions */ + +/* Information from which key of file stat-data can be uniquely + restored. This depends on key assignment policy for + stat-data. Currently it's enough to store object id and locality id + (60+60==120) bits, because minor packing locality and offset of + stat-data key are always known constants: KEY_SD_MINOR and 0 + respectively. For simplicity 4 bits are wasted in each id, and just + two 64 bit integers are stored. + + This field has to be byte-aligned, because we don't want to waste + space in directory entries. There is another side of a coin of + course: we waste CPU and bus bandwidth in stead, by copying data back + and forth. + + Next optimization: &obj_key_id is mainly used to address stat data from + directory entries. Under the assumption that majority of files only have + only name (one hard link) from *the* parent directory it seems reasonable + to only store objectid of stat data and take its locality from key of + directory item. + + This requires some flag to be added to the &obj_key_id to distinguish + between these two cases. Remaining bits in flag byte are then asking to be + used to store file type. + + This optimization requires changes in directory item handling code. + +*/ +typedef struct obj_key_id { + d8 locality[sizeof (__u64)]; + ON_LARGE_KEY(d8 ordering[sizeof (__u64)];) + d8 objectid[sizeof (__u64)]; +} obj_key_id; + +/* Information sufficient to uniquely identify directory entry within + compressed directory item. + + For alignment issues see &obj_key_id above. +*/ +typedef struct de_id { + ON_LARGE_KEY(d8 ordering[sizeof (__u64)];) + d8 objectid[sizeof (__u64)]; + d8 offset[sizeof (__u64)]; +} de_id; + +extern int inode_onwire_size(const struct inode *obj); +extern char *build_inode_onwire(const struct inode *obj, char *area); +extern char *extract_obj_key_id_from_onwire(char *area, obj_key_id * key_id); + +extern int build_inode_key_id(const struct inode *obj, obj_key_id * id); +extern int extract_key_from_id(const obj_key_id * id, reiser4_key * key); +extern int build_obj_key_id(const reiser4_key * key, obj_key_id * id); +extern oid_t extract_dir_id_from_key(const reiser4_key * de_key); +extern int build_de_id(const struct inode *dir, const struct qstr *name, de_id * id); +extern int build_de_id_by_key(const reiser4_key * entry_key, de_id * id); +extern int extract_key_from_de_id(const oid_t locality, const de_id * id, reiser4_key * key); +extern cmp_t key_id_cmp(const obj_key_id * i1, const obj_key_id * i2); +extern cmp_t key_id_key_cmp(const obj_key_id * id, const reiser4_key * key); +extern cmp_t de_id_cmp(const de_id * id1, const de_id * id2); +extern cmp_t de_id_key_cmp(const de_id * id, const reiser4_key * key); + +extern int build_readdir_key_common(struct file *dir, reiser4_key * result); +extern void build_entry_key_common(const struct inode *dir, const struct qstr *name, reiser4_key * result); +extern void build_entry_key_stable_entry(const struct inode *dir, const struct qstr *name, reiser4_key * result); +extern int is_dot_key(const reiser4_key * key); +extern reiser4_key *build_sd_key(const struct inode *target, reiser4_key * result); +extern int is_root_dir_key(const struct super_block *super, const reiser4_key * key); + +extern int is_longname_key(const reiser4_key *key); +extern int is_longname(const char *name, int len); +extern char *extract_name_from_key(const reiser4_key *key, char *buf); + +/* __KASSIGN_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/kattr.c 2004-09-03 00:57:05.098264208 -0700 @@ -0,0 +1,641 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Interface to sysfs' attributes */ + +/* + * Reiser4 exports some of its internal data through sysfs. + * + * For details on sysfs see fs/sysfs, include/linux/sysfs.h, + * include/linux/kobject.h. Roughly speaking, one embeds struct kobject into + * some kernel data type. Objects of this type will be represented as + * _directories_ somewhere below /sys. Attributes can be registered for + * kobject and they will be visible as files within corresponding + * directory. Each attribute is represented by struct kattr. How given + * attribute reacts to read and write is determined by ->show and ->store + * operations that are properties of its parent kobject. + * + * Reiser4 exports following stuff through sysfs: + * + * path kobject or attribute + * + * /sys/fs/reiser4/ + * / sbinfo->kobj + * sb-fields def_attrs[] + * stats/ sbinfo->stats_kobj + * stat-cnts reiser4_stat_defs[] + * level-NN/ sbinfo->level[].kobj + * stat-cnts reiser4_stat_level_defs[] + * + * (For some reasons we also add /sys/fs and /sys/fs/reiser4 manually, but + * this is supposed to be done by core.) + * + * Our kattr.[ch] code depends on some additional functionality missing in the + * core kernel. This functionality is added in kobject-umount-race.patch from + * our core-patches repository. As it's obvious from its name this patch adds + * protection against /sys/fs/reiser4// * accesses and concurrent umount + * of . See commentary in this patch for more details. + * + * Shouldn't struct kobject be renamed to struct knobject? + * + */ + +#include "debug.h" +#include "super.h" +#include "kattr.h" +#include "prof.h" + +#include /* struct kobject */ +#include /* struct super_block */ + +#if REISER4_USE_SYSFS + +/* + * Super-block fields exporting. + * + * Many fields of reiser4-private part of super-block + * (fs/reiser4/super.h:reiser4_super_info_data) are exported through + * sysfs. Code below tries to minimize code duplication for this common case. + * + * Specifically, all fields that are "scalars" (i.e., basically integers) of + * 32 or 64 bits are handled by the same ->show() and ->store() + * functions. Each such field is represented by two pieces of data: + * + * 1. super_field_cookie, and + * + * 2. reiser4_kattr. + * + * super_field_cookie contains "field description": + * + * 1. field offset in bytes from the beginning of reiser4-specific portion + * of super block, and + * + * 2. printf(3) format to show field content in ->show() function. + * + * reiser4_kattr is standard object we are using to embed struct fs_attribute + * in. It stores pointer to the corresponding super_field_cookie. Also + * reiser4_kattr contains ->store and ->show function pointers that are set + * according to field width and desired access rights to + * {show,store}_{ro,rw}_{32,64}(). + * + * These functions use super_field_cookie (stored in ->cookie field of + * reiser4_kattr) to obtain/store value of field involved and format it + * properly. + * + */ + +/* convert @attr to reiser4_kattr object it is embedded in */ +typedef struct { + /* offset in bytes to the super-block field from the beginning of + * reiser4_super_info_data */ + ptrdiff_t offset; + /* desired printf(3) format for ->show() method. */ + const char *format; +} super_field_cookie; + +/* + * This macro defines super_field_cookie and reiser4_kattr for given + * super-block field. + */ +#define DEFINE_SUPER_F(aname /* unique identifier used to generate variable \ + * names */, \ + afield /* name of super-block field */, \ + aformat /* desired ->show() format */, \ + asize /* field size (as returned by sizeof()) */, \ + ashow /* show method */, \ + astore /* store method */, \ + amode /* access method */) \ +static super_field_cookie __cookie_ ## aname = { \ + .offset = offsetof(reiser4_super_info_data, afield), \ + .format = aformat "\n" \ +}; \ + \ +static reiser4_kattr kattr_super_ ## aname = { \ + .attr = { \ + .kattr = { \ + .name = (char *) #afield, \ + .mode = amode \ + }, \ + .show = ashow, \ + .store = astore \ + }, \ + .cookie = &__cookie_ ## aname \ +} + +/* + * Specialized version of DEFINE_SUPER_F() used to generate description of + * read-only fields + */ +#define DEFINE_SUPER_RO(aname, afield, aformat, asize) \ + DEFINE_SUPER_F(aname, \ + afield, aformat, asize, show_ro_ ## asize, NULL, 0440) + +/* + * Specialized version of DEFINE_SUPER_F() used to generate description of + * read-write fields + */ +#define DEFINE_SUPER_RW(aname, afield, aformat, asize) \ + DEFINE_SUPER_F(aname, \ + afield, aformat, asize, show_ro_ ## asize, \ + store_rw_ ## asize, 0660) + +/* helper macro: return field of type @type stored at the offset of @offset + * bytes from the @ptr. */ +#define getat(ptr, offset, type) *(type *)(((char *)(ptr)) + (offset)) + +/* helper macro: modify value of field to @value. See getat() above for the + * meaning of other arguments */ +#define setat(ptr, offset, type, val) \ + ({ *(type *)(((char *)(ptr)) + (offset)) = (val); }) + +/* return cookie contained in reiser4_kattr that @attr is embedded into */ +static inline void * +getcookie(struct fs_kattr *attr) +{ + return container_of(attr, reiser4_kattr, attr)->cookie; +} + +/* + * ->show method for read-only 32bit scalar super block fields. + */ +static ssize_t +show_ro_32(struct super_block * s /* super-block field belongs to */, + struct fs_kobject *o /* object attribute of which @kattr is. */, + struct fs_kattr * kattr /* file-system attribute that is + * exported */, + char * buf /* buffer to store field representation into */) +{ + char *p; + super_field_cookie *cookie; + __u32 val; + + cookie = getcookie(kattr); + /* obtain field value from super-block, ... */ + val = getat(get_super_private(s), cookie->offset, __u32); + p = buf; + /* and print it according to the format string specified in the + * cookie */ + KATTR_PRINT(p, buf, cookie->format, (unsigned long long)val); + return (p - buf); +} + +/* + * ->store method for read-write 32bit scalar super-block fields. + */ +static ssize_t +store_rw_32(struct super_block * s /* super-block field belongs to */, + struct fs_kobject *o /* object attribute of which @kattr is. */, + struct fs_kattr * kattr /* file-system attribute that is + * exported */, + const char * buf /* buffer to read field value from */, + size_t size /* buffer size */) +{ + super_field_cookie *cookie; + __u32 val; + + cookie = getcookie(kattr); + /* read value from the buffer */ + if (sscanf(buf, "%i", &val) == 1) + /* if buffer contains well-formed value, update super-block + * field. */ + setat(get_super_private(s), cookie->offset, __u32, val); + else + size = RETERR(-EINVAL); + return size; +} + +/* + * ->show method for read-only 64bit scalar super block fields. + * + * It's exactly like show_ro_32, mutatis mutandis. + */ +static ssize_t show_ro_64(struct super_block * s, struct fs_kobject *o, + struct fs_kattr * kattr, char * buf) +{ + char *p; + super_field_cookie *cookie; + __u64 val; + + cookie = getcookie(kattr); + val = getat(get_super_private(s), cookie->offset, __u64); + p = buf; + KATTR_PRINT(p, buf, cookie->format, (unsigned long long)val); + return (p - buf); +} + +#if 0 +/* We don't have writable 64bit attributes yet. */ +static ssize_t +store_rw_64(struct super_block * s, + struct fs_kobject *o, struct fs_kattr * kattr, + char * buf, size_t size) +{ + super_field_cookie *cookie; + __u64 val; + + cookie = getcookie(kattr); + if (sscanf(buf, "%lli", &val) == 1) + setat(get_super_private(s), cookie->offset, __u64, val); + else + size = RETERR(-EINVAL); + return size; +} +#endif + +#undef getat +#undef setat + +/* + * Exporting reiser4 compilation options. + * + * reiser4 compilation options are exported through + * /sys/fs//options. Read-only for now. :) + * + */ + +#define SHOW_OPTION(p, buf, option) \ + if (option) \ + KATTR_PRINT((p), (buf), #option "\n") + +static ssize_t +show_options(struct super_block * s, + struct fs_kobject *o, struct fs_kattr * kattr, char * buf) +{ + char *p; + + p = buf; + + /* + * PLEASE update this when adding new compilation option + */ + + SHOW_OPTION(p, buf, REISER4_DEBUG); + SHOW_OPTION(p, buf, REISER4_DEBUG_MODIFY); + SHOW_OPTION(p, buf, REISER4_DEBUG_MEMCPY); + SHOW_OPTION(p, buf, REISER4_DEBUG_NODE); + SHOW_OPTION(p, buf, REISER4_ZERO_NEW_NODE); + SHOW_OPTION(p, buf, REISER4_TRACE); + SHOW_OPTION(p, buf, REISER4_LOG); + SHOW_OPTION(p, buf, REISER4_STATS); + SHOW_OPTION(p, buf, REISER4_DEBUG_OUTPUT); + SHOW_OPTION(p, buf, REISER4_LOCKPROF); + SHOW_OPTION(p, buf, REISER4_LARGE_KEY); + SHOW_OPTION(p, buf, REISER4_PROF); + SHOW_OPTION(p, buf, REISER4_COPY_ON_CAPTURE); + SHOW_OPTION(p, buf, REISER4_ALL_IN_ONE); + SHOW_OPTION(p, buf, REISER4_DEBUG_NODE_INVARIANT); + SHOW_OPTION(p, buf, REISER4_DEBUG_SPIN_LOCKS); + SHOW_OPTION(p, buf, REISER4_DEBUG_CONTEXTS); + SHOW_OPTION(p, buf, REISER4_DEBUG_SIBLING_LIST); + + return (p - buf); +} + +static reiser4_kattr compile_options = { + .attr = { + .kattr = { + .name = (char *) "options", + .mode = 0444 /* r--r--r-- */ + }, + .show = show_options, + }, + .cookie = NULL +}; + +/* + * show a name of device on top of which reiser4 file system exists in + * /sys/fs/reiser4//device. + */ + +static ssize_t +show_device(struct super_block * s, + struct fs_kobject *o, struct fs_kattr * kattr, char * buf) +{ + char *p; + + p = buf; + KATTR_PRINT(p, buf, "%d:%d\n", MAJOR(s->s_dev), MINOR(s->s_dev)); + return (p - buf); +} + +static reiser4_kattr device = { + .attr = { + .kattr = { + .name = (char *) "device", + .mode = 0444 /* r--r--r-- */ + }, + .show = show_device, + }, + .cookie = NULL +}; + +#if REISER4_DEBUG + +/* + * debugging code: break into debugger on each write into this file. Useful + * when event of importance can be detected in the user space, but not in the + * kernel. + */ + +ssize_t store_bugme(struct super_block * s, struct fs_kobject *o, + struct fs_kattr *ka, const char *buf, size_t size) +{ + DEBUGON(1); + return size; +} + +static reiser4_kattr bugme = { + .attr = { + .kattr = { + .name = (char *) "bugme", + .mode = 0222 /* -w--w--w- */ + }, + .store = store_bugme, + }, + .cookie = NULL +}; + +/* REISER4_DEBUG */ +#endif + +/* + * Declare all super-block fields we want to export + */ + +DEFINE_SUPER_RO(01, mkfs_id, "%#llx", 32); +DEFINE_SUPER_RO(02, block_count, "%llu", 64); +DEFINE_SUPER_RO(03, blocks_used, "%llu", 64); +DEFINE_SUPER_RO(04, blocks_free_committed, "%llu", 64); +DEFINE_SUPER_RO(05, blocks_grabbed, "%llu", 64); +DEFINE_SUPER_RO(06, blocks_fake_allocated_unformatted, "%llu", 64); +DEFINE_SUPER_RO(07, blocks_fake_allocated, "%llu", 64); +DEFINE_SUPER_RO(08, blocks_flush_reserved, "%llu", 64); +DEFINE_SUPER_RO(09, fsuid, "%#llx", 32); +#if REISER4_DEBUG +DEFINE_SUPER_RO(10, eflushed, "%llu", 32); +#endif +DEFINE_SUPER_RO(11, blocknr_hint_default, "%lli", 64); +DEFINE_SUPER_RO(12, nr_files_committed, "%llu", 64); +DEFINE_SUPER_RO(13, tmgr.atom_count, "%llu", 32); +DEFINE_SUPER_RO(14, tmgr.id_count, "%llu", 32); +DEFINE_SUPER_RO(15, tmgr.atom_max_size, "%llu", 32); +DEFINE_SUPER_RO(16, tmgr.atom_max_age, "%llu", 32); + +/* tree fields */ +DEFINE_SUPER_RO(17, tree.root_block, "%llu", 64); +DEFINE_SUPER_RO(18, tree.height, "%llu", 32); +DEFINE_SUPER_RO(19, tree.znode_epoch, "%llu", 64); +DEFINE_SUPER_RO(20, tree.carry.new_node_flags, "%#llx", 32); +DEFINE_SUPER_RO(21, tree.carry.new_extent_flags, "%#llx", 32); +DEFINE_SUPER_RO(22, tree.carry.paste_flags, "%#llx", 32); +DEFINE_SUPER_RO(23, tree.carry.insert_flags, "%#llx", 32); + +/* not very good. Should be done by the plugin in stead */ +DEFINE_SUPER_RO(24, next_to_use, "%llu", 64); +DEFINE_SUPER_RO(25, oids_in_use, "%llu", 64); + +DEFINE_SUPER_RO(26, entd.flushers, "%llu", 32); + +DEFINE_SUPER_RW(27, trace_flags, "%#llx", 32); +DEFINE_SUPER_RW(28, log_flags, "%#llx", 32); + +#define ATTR_NO(n) &kattr_super_ ## n .attr.kattr + +static struct attribute * kattr_def_attrs[] = { + ATTR_NO(01), + ATTR_NO(02), + ATTR_NO(03), + ATTR_NO(04), + ATTR_NO(05), + ATTR_NO(06), + ATTR_NO(07), + ATTR_NO(08), + ATTR_NO(09), +#if REISER4_DEBUG + ATTR_NO(10), +#endif + ATTR_NO(11), + ATTR_NO(12), + ATTR_NO(13), + ATTR_NO(14), + ATTR_NO(15), + ATTR_NO(16), + ATTR_NO(17), + ATTR_NO(18), + ATTR_NO(19), + ATTR_NO(20), + ATTR_NO(21), + ATTR_NO(22), + ATTR_NO(23), + ATTR_NO(24), + ATTR_NO(25), + ATTR_NO(26), + ATTR_NO(27), + ATTR_NO(28), +/* + ATTR_NO(29), + ATTR_NO(30), +*/ + &compile_options.attr.kattr, + &device.attr.kattr, +#if REISER4_DEBUG + &bugme.attr.kattr, +#endif + NULL +}; + +struct kobj_type ktype_reiser4 = { + .sysfs_ops = &fs_attr_ops, + .default_attrs = kattr_def_attrs, + .release = NULL +}; + +#if REISER4_STATS + +/* + * Statistical counters exporting. + * + * When REISER4_STATS mode is on, reiser4 collects a lot of statistics. See + * stat.[ch] for more details. All these stat-counters are exported through + * sysfs in /sys/fs/reiser4//stats/ directory. This directory contains + * "global" stat-counters and also level-* sub-directories for per-level + * counters (that is counters collected for specific levels of reiser4 + * internal tree). + * + */ + +static struct kobj_type ktype_noattr = { + .sysfs_ops = &fs_attr_ops, + .default_attrs = NULL, + .release = NULL +}; + +/* + * register stat-counters for the level @i with sysfs. This is called during + * mount. + */ +static int register_level_attrs(reiser4_super_info_data *sbinfo, int i) +{ + struct fs_kobject *level /* file system kobject representing @i-th + * level*/; + struct fs_kobject *parent; /* it's parent in sysfs tree */ + int result; + + /* first, setup @level */ + parent = &sbinfo->stats_kobj; + sbinfo->level[i].level = i; + level = &sbinfo->level[i].kobj; + level->kobj.parent = kobject_get(&parent->kobj); + if (level->kobj.parent != NULL) { + snprintf(level->kobj.name, KOBJ_NAME_LEN, "level-%2.2i", i); + level->kobj.ktype = &ktype_noattr; + /* register @level with sysfs */ + result = fs_kobject_register(sbinfo->tree.super, level); + if (result == 0) + /* and ultimately populate it with attributes, that + * is, stat-counters */ + result = reiser4_populate_kattr_level_dir(&level->kobj); + } else + result = RETERR(-EBUSY); + return result; +} +#endif + +static decl_subsys(fs, NULL, NULL); +decl_subsys(reiser4, &ktype_reiser4, NULL); + +/* + * initialization function called once during kernel boot-up, or reiser4 + * module loading. + */ +reiser4_internal int +reiser4_sysfs_init_once(void) +{ + int result; + + /* add /sys/fs */ + result = subsystem_register(&fs_subsys); + if (result == 0) { + kset_set_kset_s(&reiser4_subsys, fs_subsys); + /* add /sys/fs/reiser4 */ + result = subsystem_register(&reiser4_subsys); + if (result == 0) + result = init_prof_kobject(); + } + return result; +} + +/* + * shutdown function dual to reiser4_sysfs_init_once(). Called during module + * unload + */ +reiser4_internal void +reiser4_sysfs_done_once(void) +{ + subsystem_unregister(&reiser4_subsys); + subsystem_unregister(&fs_subsys); + done_prof_kobject(); +} + +/* + * initialization function called during mount of @super + */ +reiser4_internal int +reiser4_sysfs_init(struct super_block *super) +{ + reiser4_super_info_data *sbinfo; + struct fs_kobject *kobj; + int result; + ON_STATS(struct fs_kobject *stats_kobj); + + sbinfo = get_super_private(super); + + kobj = &sbinfo->kobj; + + /* + * setup and register /sys/fs/reiser4/ object + */ + snprintf(kobj->kobj.name, KOBJ_NAME_LEN, "%s", super->s_id); + kobj_set_kset_s(&sbinfo->kobj, reiser4_subsys); + result = fs_kobject_register(super, kobj); + if (result != 0) + return result; +#if REISER4_STATS + /* add attributes representing statistical counters */ + stats_kobj = &sbinfo->stats_kobj; + stats_kobj->kobj.parent = kobject_get(&kobj->kobj); + snprintf(stats_kobj->kobj.name, KOBJ_NAME_LEN, "stats"); + stats_kobj->kobj.ktype = &ktype_noattr; + result = fs_kobject_register(super, stats_kobj); + if (result != 0) + return result; + result = reiser4_populate_kattr_dir(&stats_kobj->kobj); + if (result == 0) { + int i; + + for (i = 0; i < sizeof_array(sbinfo->level); ++i) { + result = register_level_attrs(sbinfo, i); + if (result != 0) + break; + } + } +#else + result = reiser4_populate_kattr_dir(&kobj->kobj); +#endif + + return result; +} + +reiser4_internal void +reiser4_sysfs_done(struct super_block *super) +{ + reiser4_super_info_data *sbinfo; + ON_STATS(int i); + + sbinfo = get_super_private(super); +#if REISER4_STATS + for (i = 0; i < sizeof_array(sbinfo->level); ++i) + fs_kobject_unregister(&sbinfo->level[i].kobj); + fs_kobject_unregister(&sbinfo->stats_kobj); +#endif + fs_kobject_unregister(&sbinfo->kobj); +} + +/* REISER4_USE_SYSFS */ +#else + +/* + * Below are stubs for !REISER4_USE_SYSFS case. Do nothing. + */ + +reiser4_internal int +reiser4_sysfs_init(struct super_block *super) +{ + return 0; +} + +reiser4_internal void +reiser4_sysfs_done(struct super_block *super) +{} + +reiser4_internal int +reiser4_sysfs_init_once(void) +{ + return 0; +} + +reiser4_internal void +reiser4_sysfs_done_once(void) +{} + +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/kattr.h 2004-09-03 00:57:05.098264208 -0700 @@ -0,0 +1,69 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Interface to sysfs' attributes. See kattr.c for comments */ + +#if !defined( __REISER4_KATTR_H__ ) +#define __REISER4_KATTR_H__ + +#include +#include +#include +#include + +/* fixme: access to sysfs files may cause deadlock. Do not turn for now */ +#define REISER4_USE_SYSFS (1) + +#if REISER4_USE_SYSFS + +/* helper macros used by kattr code to output information into buffer without + * caring about overflow checking. */ +#define KATTR_LEFT(p, buf) (PAGE_SIZE - (p - buf) - 1) +#define KATTR_PRINT(p, buf, ...) \ +({ \ + p += snprintf(p, KATTR_LEFT(p, buf) , ## __VA_ARGS__); \ +}) + +struct super_block; +struct reiser4_kattr; +typedef struct reiser4_kattr reiser4_kattr; + +/* + * reiser4_kattr represents a sysfs-exported attribute of reiser4 file system. + */ +struct reiser4_kattr { + struct fs_kattr attr; /* file-system attribute used to interact with + * sysfs */ + void *cookie; /* parameter used to avoid code duplication. See + * kattr.c for explanation. */ +}; + +extern struct kobj_type ktype_reiser4; + +#else + +struct reiser4_kattr { +}; + +typedef struct reiser4_kattr reiser4_kattr; +#endif /* REISER4_USE_SYSFS */ + +extern int reiser4_sysfs_init_once(void); +extern void reiser4_sysfs_done_once(void); + +extern int reiser4_sysfs_init(struct super_block *super); +extern void reiser4_sysfs_done(struct super_block *super); + +/* __REISER4_KATTR_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/kcond.c 2004-09-03 00:57:05.100263904 -0700 @@ -0,0 +1,298 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Kernel condition variables implementation. + + This is simplistic (90 LOC mod comments) condition variable + implementation. Condition variable is the most natural "synchronization + object" in some circumstances. + + Each CS text-book on multi-threading should discuss condition + variables. Also see man/info for: + + pthread_cond_init(3), + pthread_cond_destroy(3), + pthread_cond_signal(3), + pthread_cond_broadcast(3), + pthread_cond_wait(3), + pthread_cond_timedwait(3). + + See comments in kcond_wait(). + + TODO + + 1. Add an option (to kcond_init?) to make conditional variable async-safe + so that signals and broadcasts can be done from interrupt + handlers. Requires using spin_lock_irq in kcond_*(). + + 2. "Predicated" sleeps: add predicate function to the qlink and only wake + sleeper if predicate is true. Probably requires additional parameters to + the kcond_{signal,broadcast}() to supply cookie to the predicate. Standard + wait_queues already have this functionality. Idea is that if one has + object behaving like finite state automaton it is possible to use single + per-object condition variable to signal all state transitions. Predicates + allow waiters to select only transitions they are interested in without + going through context switch. + + 3. It is relatively easy to add support for sleeping on the several + condition variables at once. Does anybody need this? + +*/ + +#include "debug.h" +#include "kcond.h" +#include "spin_macros.h" + +#include +#include + +static void kcond_timeout(unsigned long datum); +static void kcond_remove(kcond_t * cvar, kcond_queue_link_t * link); + +/* initialize condition variable. Initializer for global condition variables + is macro in kcond.h */ +reiser4_internal kcond_t * +kcond_init(kcond_t * cvar /* cvar to init */ ) +{ + assert("nikita-1868", cvar != NULL); + + xmemset(cvar, 0, sizeof *cvar); + spin_lock_init(&cvar->lock); + cvar->queue = NULL; + return cvar; +} + +/* destroy condition variable. */ +reiser4_internal int +kcond_destroy(kcond_t * cvar /* cvar to destroy */ ) +{ + return kcond_are_waiters(cvar) ? -EBUSY : 0; +} + +/* Wait until condition variable is signalled. Call this with @lock locked. + If @signl is true, then sleep on condition variable will be interruptible + by signals. -EINTR is returned if sleep were interrupted by signal and 0 + otherwise. + + kcond_t is just a queue protected by spinlock. Whenever thread is going to + sleep on the kcond_t it does the following: + + (1) prepares "queue link" @qlink which is semaphore constructed locally on + the stack of the thread going to sleep. + + (2) takes @cvar spinlock + + (3) adds @qlink to the @cvar queue of waiters + + (4) releases @cvar spinlock + + (5) sleeps on semaphore constructed at step (1) + + When @cvar will be signalled or broadcasted all semaphors enqueued to the + @cvar queue will be upped and kcond_wait() will return. + + By use of local semaphore for each waiter we avoid races between going to + sleep and waking up---endemic plague of condition variables. + + For example, should kcond_broadcast() come in between steps (4) and (5) it + would call up() on semaphores already in a queue and hence, down() in the + step (5) would return immediately. + +*/ +reiser4_internal int +kcond_wait(kcond_t * cvar /* cvar to wait for */ , + spinlock_t * lock /* lock to use */ , + int signl /* if 0, ignore signals during sleep */ ) +{ + kcond_queue_link_t qlink; + int result; + + assert("nikita-1869", cvar != NULL); + assert("nikita-1870", lock != NULL); + assert("nikita-1871", check_spin_is_locked(lock)); + + spin_lock(&cvar->lock); + qlink.next = cvar->queue; + cvar->queue = &qlink; + init_MUTEX_LOCKED(&qlink.wait); + spin_unlock(&cvar->lock); + spin_unlock(lock); + + result = 0; + if (signl) + result = down_interruptible(&qlink.wait); + else + down(&qlink.wait); + spin_lock(&cvar->lock); + if (result != 0) { + /* if thread was woken up by signal, @qlink is probably still + in the queue, remove it. */ + kcond_remove(cvar, &qlink); + } + /* if it wasn't woken up by signal, spinlock here is still useful, + because we want to wait until kcond_{broadcast|signal} + finishes. Otherwise down() could interleave with up() in such a way + that, that kcond_wait() would exit and up() would see garbage in a + semaphore. + */ + spin_unlock(&cvar->lock); + spin_lock(lock); + return result; +} + +typedef struct { + kcond_queue_link_t *link; + int *woken_up; +} kcond_timer_arg; + +/* like kcond_wait(), but with timeout */ +reiser4_internal int +kcond_timedwait(kcond_t * cvar /* cvar to wait for */ , + spinlock_t * lock /* lock to use */ , + signed long timeout /* timeout in jiffies */ , + int signl /* if 0, ignore signals during sleep */ ) +{ + struct timer_list timer; + kcond_queue_link_t qlink; + int result; + int woken_up; + kcond_timer_arg targ; + + assert("nikita-2437", cvar != NULL); + assert("nikita-2438", lock != NULL); + assert("nikita-2439", check_spin_is_locked(lock)); + + spin_lock(&cvar->lock); + qlink.next = cvar->queue; + cvar->queue = &qlink; + init_MUTEX_LOCKED(&qlink.wait); + spin_unlock(&cvar->lock); + spin_unlock(lock); + + assert("nikita-3011", schedulable()); + + /* prepare timer */ + init_timer(&timer); + timer.expires = jiffies + timeout; + timer.data = (unsigned long) &targ; + timer.function = kcond_timeout; + + woken_up = 0; + + targ.link = &qlink; + targ.woken_up = &woken_up; + + /* ... and set it up */ + add_timer(&timer); + + result = 0; + if (signl) + result = down_interruptible(&qlink.wait); + else + down(&qlink.wait); + + /* cancel timer */ + del_timer_sync(&timer); + + if (woken_up) + result = -ETIMEDOUT; + + spin_lock(&cvar->lock); + if (result != 0) { + /* if thread was woken up by signal, or due to time-out, + @qlink is probably still in the queue, remove it. */ + kcond_remove(cvar, &qlink); + } + spin_unlock(&cvar->lock); + + spin_lock(lock); + return result; +} + +/* Signal condition variable: wake up one waiter, if any. */ +reiser4_internal int +kcond_signal(kcond_t * cvar /* cvar to signal */ ) +{ + kcond_queue_link_t *queue_head; + + assert("nikita-1872", cvar != NULL); + + spin_lock(&cvar->lock); + + queue_head = cvar->queue; + if (queue_head != NULL) { + cvar->queue = queue_head->next; + up(&queue_head->wait); + } + spin_unlock(&cvar->lock); + return 1; +} + +/* Broadcast condition variable: wake up all waiters. */ +reiser4_internal int +kcond_broadcast(kcond_t * cvar /* cvar to broadcast */ ) +{ + kcond_queue_link_t *queue_head; + + assert("nikita-1875", cvar != NULL); + + spin_lock(&cvar->lock); + + for (queue_head = cvar->queue; queue_head != NULL; queue_head = queue_head->next) + up(&queue_head->wait); + + cvar->queue = NULL; + spin_unlock(&cvar->lock); + return 1; +} + +/* true if there are threads sleeping on @cvar */ +reiser4_internal int +kcond_are_waiters(kcond_t * cvar /* cvar to query */ ) +{ + assert("nikita-1877", cvar != NULL); + return cvar->queue != NULL; +} + +/* timer expiration function used by kcond_timedwait */ +static void +kcond_timeout(unsigned long datum) +{ + kcond_timer_arg *arg; + + arg = (kcond_timer_arg *) datum; + *arg->woken_up = 1; + up(&arg->link->wait); +} + +/* helper function to remove @link from @cvar queue */ +static void +kcond_remove(kcond_t * cvar /* cvar to operate on */ , + kcond_queue_link_t * link /* link to remove */ ) +{ + kcond_queue_link_t *scan; + kcond_queue_link_t *prev; + + assert("nikita-2440", cvar != NULL); + assert("nikita-2441", check_spin_is_locked(&cvar->lock)); + + for (scan = cvar->queue, prev = NULL; scan != NULL; prev = scan, scan = scan->next) { + if (scan == link) { + if (prev == NULL) + cvar->queue = scan->next; + else + prev->next = scan->next; + break; + } + } +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/kcond.h 2004-09-03 00:57:05.100263904 -0700 @@ -0,0 +1,59 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Declaration of kernel condition variables and API. See kcond.c for more + info. */ + +#ifndef __KCOND_H__ +#define __KCOND_H__ + +#include +#include + +typedef struct kcond_queue_link_s kcond_queue_link_t; + +/* condition variable */ +typedef struct kcond_s { + /* lock protecting integrity of @queue */ + spinlock_t lock; + /* queue of waiters */ + kcond_queue_link_t *queue; +} kcond_t; + +/* queue link added to the kcond->queue by each waiter */ +struct kcond_queue_link_s { + /* next link in the queue */ + kcond_queue_link_t *next; + /* semaphore to signal on wake up */ + struct semaphore wait; +}; + +extern kcond_t *kcond_init(kcond_t * cvar); +extern int kcond_destroy(kcond_t * cvar); + +extern int kcond_wait(kcond_t * cvar, spinlock_t * lock, int signl); +extern int kcond_timedwait(kcond_t * cvar, spinlock_t * lock, signed long timeout, int signl); +extern int kcond_signal(kcond_t * cvar); +extern int kcond_broadcast(kcond_t * cvar); + +extern int kcond_are_waiters(kcond_t * cvar); + +extern void kcond_print(kcond_t * cvar); + +#define KCOND_STATIC_INIT \ + { \ + .lock = SPIN_LOCK_UNLOCKED, \ + .queue = NULL \ + } + +/* __KCOND_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/key.c 2004-09-03 00:57:07.758859736 -0700 @@ -0,0 +1,168 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Key manipulations. */ + +#include "debug.h" +#include "key.h" +#include "super.h" +#include "reiser4.h" + +#include /* for __u?? */ + +/* Minimal possible key: all components are zero. It is presumed that this is + independent of key scheme. */ +static const reiser4_key MINIMAL_KEY = { + .el = { + {0ull}, + ON_LARGE_KEY({0ull},) + {0ull}, + {0ull} + } +}; + +/* Maximal possible key: all components are ~0. It is presumed that this is + independent of key scheme. */ +static const reiser4_key MAXIMAL_KEY = { + .el = { + {~0ull}, + ON_LARGE_KEY({~0ull},) + {~0ull}, + {~0ull} + } +}; + +/* Initialise key. */ +reiser4_internal void +key_init(reiser4_key * key /* key to init */ ) +{ + assert("nikita-1169", key != NULL); + xmemset(key, 0, sizeof *key); +} + +/* minimal possible key in the tree. Return pointer to the static storage. */ +reiser4_internal const reiser4_key * +min_key(void) +{ + return &MINIMAL_KEY; +} + +/* maximum possible key in the tree. Return pointer to the static storage. */ +reiser4_internal const reiser4_key * +max_key(void) +{ + return &MAXIMAL_KEY; +} + +#if REISER4_DEBUG_OUTPUT +/* debugging aid: print symbolic name of key type */ +static const char * +type_name(unsigned int key_type /* key type */ ) +{ + switch (key_type) { + case KEY_FILE_NAME_MINOR: + return "file name"; + case KEY_SD_MINOR: + return "stat data"; + case KEY_ATTR_NAME_MINOR: + return "attr name"; + case KEY_ATTR_BODY_MINOR: + return "attr body"; + case KEY_BODY_MINOR: + return "file body"; + default: + return "unknown"; + } +} + +extern char *unpack_string(__u64 value, char *buf); + +/* debugging aid: print human readable information about key */ +reiser4_internal void +print_key(const char *prefix /* prefix to print */ , + const reiser4_key * key /* key to print */ ) +{ + /* turn bold on */ + /* printf ("\033[1m"); */ + if (key == NULL) + printk("%s: null key\n", prefix); + else { + if (REISER4_LARGE_KEY) + printk("%s: (%Lx:%x:%Lx:%Lx:%Lx:%Lx)", prefix, + get_key_locality(key), + get_key_type(key), + get_key_ordering(key), + get_key_band(key), + get_key_objectid(key), + get_key_offset(key)); + else + printk("%s: (%Lx:%x:%Lx:%Lx:%Lx)", prefix, + get_key_locality(key), + get_key_type(key), + get_key_band(key), + get_key_objectid(key), + get_key_offset(key)); + /* + * if this is a key of directory entry, try to decode part of + * a name stored in the key, and output it. + */ + if (get_key_type(key) == KEY_FILE_NAME_MINOR) { + char buf[DE_NAME_BUF_LEN]; + char *c; + + c = buf; + c = unpack_string(get_key_ordering(key), c); + unpack_string(get_key_fulloid(key), c); + printk("[%s", buf); + if (is_longname_key(key)) + /* + * only part of the name is stored in the key. + */ + printk("...]\n"); + else { + /* + * whole name is stored in the key. + */ + unpack_string(get_key_offset(key), buf); + printk("%s]\n", buf); + } + } else { + printk("[%s]\n", type_name(get_key_type(key))); + } + } + /* turn bold off */ + /* printf ("\033[m\017"); */ +} + +#endif + +/* like print_key() but outputs key representation into @buffer. */ +reiser4_internal int +sprintf_key(char *buffer /* buffer to print key into */ , + const reiser4_key * key /* key to print */ ) +{ + if (REISER4_LARGE_KEY) + return sprintf(buffer, "(%Lx:%x:%Lx:%Lx:%Lx:%Lx)", + (unsigned long long)get_key_locality(key), + get_key_type(key), + (unsigned long long)get_key_ordering(key), + (unsigned long long)get_key_band(key), + (unsigned long long)get_key_objectid(key), + (unsigned long long)get_key_offset(key)); + else + return sprintf(buffer, "(%Lx:%x:%Lx:%Lx:%Lx)", + (unsigned long long)get_key_locality(key), + get_key_type(key), + (unsigned long long)get_key_band(key), + (unsigned long long)get_key_objectid(key), + (unsigned long long)get_key_offset(key)); +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/key.h 2004-09-03 00:57:05.103263448 -0700 @@ -0,0 +1,389 @@ +/* Copyright 2000, 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Declarations of key-related data-structures and operations on keys. */ + +#if !defined( __REISER4_KEY_H__ ) +#define __REISER4_KEY_H__ + +#include "dformat.h" +#include "forward.h" +#include "debug.h" + +#include /* for __u?? */ + +/* Operations on keys in reiser4 tree */ + +/* No access to any of these fields shall be done except via a + wrapping macro/function, and that wrapping macro/function shall + convert to little endian order. Compare keys will consider cpu byte order. */ + +/* A storage layer implementation difference between a regular unix file body and its attributes is in the typedef below + which causes all of the attributes of a file to be near in key to all of the other attributes for all of the files + within that directory, and not near to the file itself. It is interesting to consider whether this is the wrong + approach, and whether there should be no difference at all. For current usage patterns this choice is probably the + right one. */ + +/* possible values for minor packing locality (4 bits required) */ +typedef enum { + /* file name */ + KEY_FILE_NAME_MINOR = 0, + /* stat-data */ + KEY_SD_MINOR = 1, + /* file attribute name */ + KEY_ATTR_NAME_MINOR = 2, + /* file attribute value */ + KEY_ATTR_BODY_MINOR = 3, + /* file body (tail or extent) */ + KEY_BODY_MINOR = 4, +} key_minor_locality; + +/* everything stored in the tree has a unique key, which means that the tree is (logically) fully ordered by key. + Physical order is determined by dynamic heuristics that attempt to reflect key order when allocating available space, + and by the repacker. It is stylistically better to put aggregation information into the key. Thus, if you want to + segregate extents from tails, it is better to give them distinct minor packing localities rather than changing + block_alloc.c to check the node type when deciding where to allocate the node. + + The need to randomly displace new directories and large files disturbs this symmetry unfortunately. However, it + should be noted that this is a need that is not clearly established given the existence of a repacker. Also, in our + current implementation tails have a different minor packing locality from extents, and no files have both extents and + tails, so maybe symmetry can be had without performance cost after all. Symmetry is what we ship for now.... +*/ + +/* Arbitrary major packing localities can be assigned to objects using + the reiser4(filenameA/..packing<=some_number) system call. + + In reiser4, the creat() syscall creates a directory + + whose default flow (that which is referred to if the directory is + read as a file) is the traditional unix file body. + + whose directory plugin is the 'filedir' + + whose major packing locality is that of the parent of the object created. + + The static_stat item is a particular commonly used directory + compression (the one for normal unix files). + + The filedir plugin checks to see if the static_stat item exists. + There is a unique key for static_stat. If yes, then it uses the + static_stat item for all of the values that it contains. The + static_stat item contains a flag for each stat it contains which + indicates whether one should look outside the static_stat item for its + contents. +*/ + +/* offset of fields in reiser4_key. Value of each element of this enum + is index within key (thought as array of __u64's) where this field + is. */ +typedef enum { + /* major "locale", aka dirid. Sits in 1st element */ + KEY_LOCALITY_INDEX = 0, + /* minor "locale", aka item type. Sits in 1st element */ + KEY_TYPE_INDEX = 0, + ON_LARGE_KEY(KEY_ORDERING_INDEX,) + /* "object band". Sits in 2nd element */ + KEY_BAND_INDEX, + /* objectid. Sits in 2nd element */ + KEY_OBJECTID_INDEX = KEY_BAND_INDEX, + /* full objectid. Sits in 2nd element */ + KEY_FULLOID_INDEX = KEY_BAND_INDEX, + /* Offset. Sits in 3rd element */ + KEY_OFFSET_INDEX, + /* Name hash. Sits in 3rd element */ + KEY_HASH_INDEX = KEY_OFFSET_INDEX, + KEY_CACHELINE_END = KEY_OFFSET_INDEX, + KEY_LAST_INDEX +} reiser4_key_field_index; + +/* key in reiser4 internal "balanced" tree. It is just array of three + 64bit integers in disk byte order (little-endian by default). This + array is actually indexed by reiser4_key_field. Each __u64 within + this array is called "element". Logical key component encoded within + elements are called "fields". + + We declare this as union with second component dummy to suppress + inconvenient array<->pointer casts implied in C. */ +union reiser4_key { + d64 el[KEY_LAST_INDEX]; + int pad; +}; + +/* bitmasks showing where within reiser4_key particular key is + stored. */ +typedef enum { + /* major locality occupies higher 60 bits of the first element */ + KEY_LOCALITY_MASK = 0xfffffffffffffff0ull, + /* minor locality occupies lower 4 bits of the first element */ + KEY_TYPE_MASK = 0xfull, + /* controversial band occupies higher 4 bits of the 2nd element */ + KEY_BAND_MASK = 0xf000000000000000ull, + /* objectid occupies lower 60 bits of the 2nd element */ + KEY_OBJECTID_MASK = 0x0fffffffffffffffull, + /* full 64bit objectid*/ + KEY_FULLOID_MASK = 0xffffffffffffffffull, + /* offset is just 3rd L.M.Nt itself */ + KEY_OFFSET_MASK = 0xffffffffffffffffull, + /* ordering is whole second element */ + KEY_ORDERING_MASK = 0xffffffffffffffffull, +} reiser4_key_field_mask; + +/* how many bits key element should be shifted to left to get particular field */ +typedef enum { + KEY_LOCALITY_SHIFT = 4, + KEY_TYPE_SHIFT = 0, + KEY_BAND_SHIFT = 60, + KEY_OBJECTID_SHIFT = 0, + KEY_FULLOID_SHIFT = 0, + KEY_OFFSET_SHIFT = 0, + KEY_ORDERING_SHIFT = 0, +} reiser4_key_field_shift; + +static inline __u64 +get_key_el(const reiser4_key * key, reiser4_key_field_index off) +{ + assert("nikita-753", key != NULL); + assert("nikita-754", off < KEY_LAST_INDEX); + return d64tocpu(&key->el[off]); +} + +static inline void +set_key_el(reiser4_key * key, reiser4_key_field_index off, __u64 value) +{ + assert("nikita-755", key != NULL); + assert("nikita-756", off < KEY_LAST_INDEX); + cputod64(value, &key->el[off]); +} + +/* macro to define getter and setter functions for field F with type T */ +#define DEFINE_KEY_FIELD( L, U, T ) \ +static inline T get_key_ ## L ( const reiser4_key *key ) \ +{ \ + assert( "nikita-750", key != NULL ); \ + return ( T ) ( get_key_el( key, KEY_ ## U ## _INDEX ) & \ + KEY_ ## U ## _MASK ) >> KEY_ ## U ## _SHIFT; \ +} \ + \ +static inline void set_key_ ## L ( reiser4_key *key, T loc ) \ +{ \ + __u64 el; \ + \ + assert( "nikita-752", key != NULL ); \ + \ + el = get_key_el( key, KEY_ ## U ## _INDEX ); \ + /* clear field bits in the key */ \ + el &= ~KEY_ ## U ## _MASK; \ + /* actually it should be \ + \ + el |= ( loc << KEY_ ## U ## _SHIFT ) & KEY_ ## U ## _MASK; \ + \ + but we trust user to never pass values that wouldn't fit \ + into field. Clearing extra bits is one operation, but this \ + function is time-critical. \ + But check this in assertion. */ \ + assert( "nikita-759", ( ( loc << KEY_ ## U ## _SHIFT ) & \ + ~KEY_ ## U ## _MASK ) == 0 ); \ + el |= ( loc << KEY_ ## U ## _SHIFT ); \ + set_key_el( key, KEY_ ## U ## _INDEX, el ); \ +} + +typedef __u64 oid_t; + +/* define get_key_locality(), set_key_locality() */ +DEFINE_KEY_FIELD(locality, LOCALITY, oid_t); +/* define get_key_type(), set_key_type() */ +DEFINE_KEY_FIELD(type, TYPE, key_minor_locality); +/* define get_key_band(), set_key_band() */ +DEFINE_KEY_FIELD(band, BAND, __u64); +/* define get_key_objectid(), set_key_objectid() */ +DEFINE_KEY_FIELD(objectid, OBJECTID, oid_t); +/* define get_key_fulloid(), set_key_fulloid() */ +DEFINE_KEY_FIELD(fulloid, FULLOID, oid_t); +/* define get_key_offset(), set_key_offset() */ +DEFINE_KEY_FIELD(offset, OFFSET, __u64); +#if (REISER4_LARGE_KEY) +/* define get_key_ordering(), set_key_ordering() */ +DEFINE_KEY_FIELD(ordering, ORDERING, __u64); +#else +static inline __u64 get_key_ordering(const reiser4_key *key) +{ + return 0; +} + +static inline void set_key_ordering(reiser4_key *key, __u64 val) +{ +} +#endif + +/* key comparison result */ +typedef enum { LESS_THAN = -1, /* if first key is less than second */ + EQUAL_TO = 0, /* if keys are equal */ + GREATER_THAN = +1 /* if first key is greater than second */ +} cmp_t; + +void key_init(reiser4_key * key); + +/* minimal possible key in the tree. Return pointer to the static storage. */ +extern const reiser4_key *min_key(void); +extern const reiser4_key *max_key(void); + +/* helper macro for keycmp() */ +#define KEY_DIFF(k1, k2, field) \ +({ \ + typeof (get_key_ ## field (k1)) f1; \ + typeof (get_key_ ## field (k2)) f2; \ + \ + f1 = get_key_ ## field (k1); \ + f2 = get_key_ ## field (k2); \ + \ + (f1 < f2) ? LESS_THAN : ((f1 == f2) ? EQUAL_TO : GREATER_THAN); \ +}) + +/* helper macro for keycmp() */ +#define KEY_DIFF_EL(k1, k2, off) \ +({ \ + __u64 e1; \ + __u64 e2; \ + \ + e1 = get_key_el(k1, off); \ + e2 = get_key_el(k2, off); \ + \ + (e1 < e2) ? LESS_THAN : ((e1 == e2) ? EQUAL_TO : GREATER_THAN); \ +}) + +/* compare `k1' and `k2'. This function is a heart of "key allocation + policy". All you need to implement new policy is to add yet another + clause here. */ +static inline cmp_t +keycmp(const reiser4_key * k1 /* first key to compare */ , + const reiser4_key * k2 /* second key to compare */ ) +{ + cmp_t result; + + /* + * This function is the heart of reiser4 tree-routines. Key comparison + * is among most heavily used operations in the file system. + */ + + assert("nikita-439", k1 != NULL); + assert("nikita-440", k2 != NULL); + + /* there is no actual branch here: condition is compile time constant + * and constant folding and propagation ensures that only one branch + * is actually compiled in. */ + + if (REISER4_PLANA_KEY_ALLOCATION) { + /* if physical order of fields in a key is identical + with logical order, we can implement key comparison + as three 64bit comparisons. */ + /* logical order of fields in plan-a: + locality->type->objectid->offset. */ + /* compare locality and type at once */ + result = KEY_DIFF_EL(k1, k2, 0); + if (result == EQUAL_TO) { + /* compare objectid (and band if it's there) */ + result = KEY_DIFF_EL(k1, k2, 1); + /* compare offset */ + if (result == EQUAL_TO) { + result = KEY_DIFF_EL(k1, k2, 2); + if (REISER4_LARGE_KEY && result == EQUAL_TO) { + result = KEY_DIFF_EL(k1, k2, 3); + } + } + } + } else if (REISER4_3_5_KEY_ALLOCATION) { + result = KEY_DIFF(k1, k2, locality); + if (result == EQUAL_TO) { + result = KEY_DIFF(k1, k2, objectid); + if (result == EQUAL_TO) { + result = KEY_DIFF(k1, k2, type); + if (result == EQUAL_TO) + result = KEY_DIFF(k1, k2, offset); + } + } + } else + impossible("nikita-441", "Unknown key allocation scheme!"); + return result; +} + +/* true if @k1 equals @k2 */ +static inline int +keyeq(const reiser4_key * k1 /* first key to compare */ , + const reiser4_key * k2 /* second key to compare */ ) +{ + assert("nikita-1879", k1 != NULL); + assert("nikita-1880", k2 != NULL); + return !memcmp(k1, k2, sizeof *k1); +} + +/* true if @k1 is less than @k2 */ +static inline int +keylt(const reiser4_key * k1 /* first key to compare */ , + const reiser4_key * k2 /* second key to compare */ ) +{ + assert("nikita-1952", k1 != NULL); + assert("nikita-1953", k2 != NULL); + return keycmp(k1, k2) == LESS_THAN; +} + +/* true if @k1 is less than or equal to @k2 */ +static inline int +keyle(const reiser4_key * k1 /* first key to compare */ , + const reiser4_key * k2 /* second key to compare */ ) +{ + assert("nikita-1954", k1 != NULL); + assert("nikita-1955", k2 != NULL); + return keycmp(k1, k2) != GREATER_THAN; +} + +/* true if @k1 is greater than @k2 */ +static inline int +keygt(const reiser4_key * k1 /* first key to compare */ , + const reiser4_key * k2 /* second key to compare */ ) +{ + assert("nikita-1959", k1 != NULL); + assert("nikita-1960", k2 != NULL); + return keycmp(k1, k2) == GREATER_THAN; +} + +/* true if @k1 is greater than or equal to @k2 */ +static inline int +keyge(const reiser4_key * k1 /* first key to compare */ , + const reiser4_key * k2 /* second key to compare */ ) +{ + assert("nikita-1956", k1 != NULL); + assert("nikita-1957", k2 != NULL); /* October 4: sputnik launched + * November 3: Laika */ + return keycmp(k1, k2) != LESS_THAN; +} + +static inline void +prefetchkey(reiser4_key *key) +{ + prefetch(key); + prefetch(&key->el[KEY_CACHELINE_END]); +} + +/* (%Lx:%x:%Lx:%Lx:%Lx:%Lx) = + 1 + 16 + 1 + 1 + 1 + 1 + 1 + 16 + 1 + 16 + 1 + 16 + 1 */ +/* size of a buffer suitable to hold human readable key representation */ +#define KEY_BUF_LEN (80) + +extern int sprintf_key(char *buffer, const reiser4_key * key); +#if REISER4_DEBUG_OUTPUT +extern void print_key(const char *prefix, const reiser4_key * key); +#else +#define print_key(p,k) noop +#endif + +/* __FS_REISERFS_KEY_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/ktxnmgrd.c 2004-09-03 00:57:05.104263296 -0700 @@ -0,0 +1,274 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ +/* Transaction manager daemon. */ + +/* + * ktxnmgrd is a kernel daemon responsible for committing transactions. It is + * needed/important for the following reasons: + * + * 1. in reiser4 atom is not committed immediately when last transaction + * handle closes, unless atom is either too old or too large (see + * atom_should_commit()). This is done to avoid committing too frequently. + * because: + * + * 2. sometimes we don't want to commit atom when closing last transaction + * handle even if it is old and fat enough. For example, because we are at + * this point under directory semaphore, and committing would stall all + * accesses to this directory. + * + * ktxnmgrd binds its time sleeping on condition variable. When is awakes + * either due to (tunable) timeout or because it was explicitly woken up by + * call to ktxnmgrd_kick(), it scans list of all atoms and commits ones + * eligible. + * + */ + +#include "debug.h" +#include "kcond.h" +#include "txnmgr.h" +#include "tree.h" +#include "ktxnmgrd.h" +#include "super.h" +#include "reiser4.h" + +#include /* for struct task_struct */ +#include +#include +#include + +static int scan_mgr(txn_mgr * mgr); + +reiser4_internal int +init_ktxnmgrd_context(txn_mgr * mgr) +{ + ktxnmgrd_context * ctx; + + assert ("zam-1013", mgr != NULL); + assert ("zam-1014", mgr->daemon == NULL); + + ctx = reiser4_kmalloc(sizeof(ktxnmgrd_context), GFP_KERNEL); + if (ctx == NULL) + return RETERR(-ENOMEM); + + assert("nikita-2442", ctx != NULL); + + xmemset(ctx, 0, sizeof *ctx); + init_completion(&ctx->finish); + kcond_init(&ctx->startup); + kcond_init(&ctx->wait); + spin_lock_init(&ctx->guard); + ctx->timeout = REISER4_TXNMGR_TIMEOUT; + mgr->daemon = ctx; + return 0; +} + +/* change current->comm so that ps, top, and friends will see changed + state. This serves no useful purpose whatsoever, but also costs + nothing. May be it will make lonely system administrator feeling less alone + at 3 A.M. +*/ +#define set_comm( state ) \ + snprintf( current -> comm, sizeof( current -> comm ), \ + "%s:%s:%s", __FUNCTION__, (super)->s_id, ( state ) ) + +/* The background transaction manager daemon, started as a kernel thread + during reiser4 initialization. */ +static int +ktxnmgrd(void *arg) +{ + struct task_struct *me; + struct super_block * super; + ktxnmgrd_context *ctx; + txn_mgr * mgr; + + /* standard kernel thread prologue */ + me = current; + /* reparent_to_init() is done by daemonize() */ + daemonize(__FUNCTION__); + + /* block all signals */ + spin_lock_irq(&me->sighand->siglock); + siginitsetinv(&me->blocked, 0); + recalc_sigpending(); + spin_unlock_irq(&me->sighand->siglock); + + /* do_fork() just copies task_struct into the new + thread. ->fs_context shouldn't be copied of course. This shouldn't + be a problem for the rest of the code though. + */ + me->journal_info = NULL; + + mgr = arg; + ctx = mgr->daemon; + spin_lock(&ctx->guard); + ctx->tsk = me; + super = container_of(mgr, reiser4_super_info_data, tmgr)->tree.super; + kcond_broadcast(&ctx->startup); + while (1) { + int result; + + /* software suspend support. */ + if (me->flags & PF_FREEZE) { + spin_unlock(&ctx->guard); + refrigerator(PF_FREEZE/*PF_IOTHREAD*/); + spin_lock(&ctx->guard); + } + + set_comm("wait"); + /* wait for @ctx -> timeout or explicit wake up. + + kcond_wait() is called with last argument 1 enabling wakeup + by signals so that this thread is not counted in + load-average. This doesn't require any special handling, + because all signals were blocked. + */ + result = kcond_timedwait(&ctx->wait, + &ctx->guard, ctx->timeout, 1); + + if (result != -ETIMEDOUT && result != -EINTR && result != 0) { + /* some other error */ + warning("nikita-2443", "Error: %i", result); + continue; + } + + /* we are asked to exit */ + if (ctx->done) + break; + + set_comm(result ? "timed" : "run"); + + /* wait timed out or ktxnmgrd was woken up by explicit request + to commit something. Scan list of atoms in txnmgr and look + for too old atoms. + */ + do { + ctx->rescan = 0; + scan_mgr(mgr); + spin_lock(&ctx->guard); + if (ctx->rescan) { + /* the list could be modified while ctx + spinlock was released, we have to + repeat scanning from the + beginning */ + break; + } + } while (ctx->rescan); + } + + spin_unlock(&ctx->guard); + + complete_and_exit(&ctx->finish, 0); + /* not reached. */ + return 0; +} + +#undef set_comm + +reiser4_internal void +ktxnmgrd_kick(txn_mgr * mgr) +{ + assert("nikita-3234", mgr != NULL); + assert("nikita-3235", mgr->daemon != NULL); + kcond_signal(&mgr->daemon->wait); +} + +reiser4_internal int +is_current_ktxnmgrd(void) +{ + return (get_current_super_private()->tmgr.daemon->tsk == current); +} + +/* scan one transaction manager for old atoms; should be called with ktxnmgrd + * spinlock, releases this spin lock at exit */ +static int +scan_mgr(txn_mgr * mgr) +{ + int ret; + reiser4_context ctx; + reiser4_tree *tree; + + assert("nikita-2454", mgr != NULL); + + /* NOTE-NIKITA this only works for atoms embedded into super blocks. */ + tree = &container_of(mgr, reiser4_super_info_data, tmgr)->tree; + assert("nikita-2455", tree != NULL); + assert("nikita-2456", tree->super != NULL); + + init_context(&ctx, tree->super); + + ret = commit_some_atoms(mgr); + + reiser4_exit_context(&ctx); + return ret; +} + + +reiser4_internal int start_ktxnmgrd (txn_mgr * mgr) +{ + ktxnmgrd_context * ctx; + + assert("nikita-2448", mgr != NULL); + assert("zam-1015", mgr->daemon != NULL); + + ctx = mgr->daemon; + + spin_lock(&ctx->guard); + + ctx->rescan = 1; + ctx->done = 0; + + spin_unlock(&ctx->guard); + + kernel_thread(ktxnmgrd, mgr, CLONE_KERNEL); + + spin_lock(&ctx->guard); + + /* daemon thread is not yet initialized */ + if (ctx->tsk == NULL) + /* wait until initialization completes */ + kcond_wait(&ctx->startup, &ctx->guard, 0); + + assert("nikita-2452", ctx->tsk != NULL); + + spin_unlock(&ctx->guard); + return 0; +} + +reiser4_internal void stop_ktxnmgrd (txn_mgr * mgr) +{ + ktxnmgrd_context * ctx; + + assert ("zam-1016", mgr != NULL); + assert ("zam-1017", mgr->daemon != NULL); + + ctx = mgr->daemon; + + spin_lock(&ctx->guard); + ctx->tsk = NULL; + ctx->done = 1; + spin_unlock(&ctx->guard); + + kcond_signal(&ctx->wait); + + /* wait until daemon finishes */ + wait_for_completion(&ctx->finish); +} + +reiser4_internal void +done_ktxnmgrd_context (txn_mgr * mgr) +{ + assert ("zam-1011", mgr != NULL); + assert ("zam-1012", mgr->daemon != NULL); + + reiser4_kfree(mgr->daemon); + mgr->daemon = NULL; +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/ktxnmgrd.h 2004-09-03 00:57:05.105263144 -0700 @@ -0,0 +1,63 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Transaction manager daemon. See ktxnmgrd.c for comments. */ + +#ifndef __KTXNMGRD_H__ +#define __KTXNMGRD_H__ + +#include "kcond.h" +#include "txnmgr.h" +#include "spin_macros.h" + +#include +#include +#include +#include +#include /* for struct task_struct */ + +/* in this structure all data necessary to start up, shut down and communicate + * with ktxnmgrd are kept. */ +struct ktxnmgrd_context { + /* conditional variable used to synchronize start up of ktxnmgrd */ + kcond_t startup; + /* completion used to synchronize shut down of ktxnmgrd */ + struct completion finish; + /* condition variable on which ktxnmgrd sleeps */ + kcond_t wait; + /* spin lock protecting all fields of this structure */ + spinlock_t guard; + /* timeout of sleeping on ->wait */ + signed long timeout; + /* kernel thread running ktxnmgrd */ + struct task_struct *tsk; + /* list of all file systems served by this ktxnmgrd */ + txn_mgrs_list_head queue; + /* is ktxnmgrd being shut down? */ + int done:1; + /* should ktxnmgrd repeat scanning of atoms? */ + int rescan:1; +}; + +extern int init_ktxnmgrd_context(txn_mgr *); +extern void done_ktxnmgrd_context(txn_mgr *); + +extern int start_ktxnmgrd(txn_mgr *); +extern void stop_ktxnmgrd(txn_mgr *); + +extern void ktxnmgrd_kick(txn_mgr * mgr); + +extern int is_current_ktxnmgrd(void); + +/* __KTXNMGRD_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/lib.h 2004-09-03 00:57:05.105263144 -0700 @@ -0,0 +1,75 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#if !defined (__FS_REISER4_LIB_H__) +#define __FS_REISER4_LIB_H__ + +/* These 2 functions of 64 bit numbers division were taken from + include/sound/pcm.h */ + +/* Helper function for 64 bits numbers division. */ +static inline void +divl(__u32 high, __u32 low, __u32 div, __u32 * q, __u32 * r) +{ + __u64 n = (__u64) high << 32 | low; + __u64 d = (__u64) div << 31; + __u32 q1 = 0; + int c = 32; + + while (n > 0xffffffffU) { + q1 <<= 1; + if (n >= d) { + n -= d; + q1 |= 1; + } + d >>= 1; + c--; + } + q1 <<= c; + if (n) { + low = n; + *q = q1 | (low / div); + if (r) + *r = low % div; + } else { + if (r) + *r = 0; + *q = q1; + } + return; +} + +/* Function for 64 bits numbers division. */ +static inline __u64 +div64_32(__u64 n, __u32 div, __u32 * rem) +{ + __u32 low, high; + + low = n & 0xffffffff; + high = n >> 32; + if (high) { + __u32 high1 = high % div; + __u32 low1 = low; + high /= div; + divl(high1, low1, div, &low, rem); + return (__u64) high << 32 | low; + } else { + if (rem) + *rem = low % div; + return low / div; + } + + return 0; +} + +#endif /* __FS_REISER4_LIB_H__ */ + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/linux-5_reiser4_syscall.patch 2004-09-03 00:57:05.106262992 -0700 @@ -0,0 +1,38 @@ +===== arch/um/kernel/sys_call_table.c 1.5 vs edited ===== +--- 1.5/arch/um/kernel/sys_call_table.c Wed Nov 6 17:36:22 2002 ++++ edited/arch/um/kernel/sys_call_table.c Fri Dec 6 22:15:35 2002 +@@ -232,6 +232,7 @@ + extern syscall_handler_t sys_io_cancel; + extern syscall_handler_t sys_exit_group; + extern syscall_handler_t sys_lookup_dcookie; ++extern syscall_handler_t sys_eriser4; + + #if CONFIG_NFSD + #define NFSSERVCTL sys_nfsserctl +@@ -483,6 +484,7 @@ + [ __NR_free_hugepages ] = sys_ni_syscall, + [ __NR_exit_group ] = sys_exit_group, + [ __NR_lookup_dcookie ] = sys_lookup_dcookie, ++ [ __NR_reiser4_sys_call ] = sys_reiser4, + + ARCH_SYSCALLS + [ LAST_SYSCALL + 1 ... NR_syscalls ] = +===== include/asm-i386/unistd.h 1.19 vs edited ===== +--- 1.19/include/asm-i386/unistd.h Thu Oct 31 18:28:28 2002 ++++ edited/include/asm-i386/unistd.h Fri Dec 6 22:45:24 2002 +@@ -262,6 +262,7 @@ + #define __NR_sys_epoll_ctl 255 + #define __NR_sys_epoll_wait 256 + #define __NR_remap_file_pages 257 ++#define __NR_reiser4_sys_call 258 + + + /* user-visible error numbers are in the range -1 - -124: see */ +@@ -378,6 +379,7 @@ + static inline _syscall1(int,close,int,fd) + static inline _syscall1(int,_exit,int,exitcode) + static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options) ++static inline _syscall1(long,_reiser4_sys_call,char*,p_strIng) + + #endif + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/lnode.c 2004-09-03 00:57:05.108262688 -0700 @@ -0,0 +1,431 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Lnode manipulation functions. */ +/* Lnode is light-weight node used as common data-structure by both VFS access + paths and reiser4() system call processing. + + One of the main targets of reiser4() system call is to allow manipulation + on potentially huge number of objects. This makes use of inode in reiser4() + impossible. On the other hand there is a need to synchronize reiser4() and + VFS access. + + To do this small object (lnode) is allocated (on the stack if possible) for + each object involved into reiser4() system call. Such lnode only contains + lock, information necessary to link it into global hash table, and + condition variable to wake up waiters (see below). + + In other words, lnode is handle that reiser4 keeps for a file system object + while object is being actively used. For example, when read is performed by + reiser4_read(), lnode exists for inode being read. When reiser4_read() + exits lnode is deleted, but inode is still there in the inode cache. + + As lnode only exists while object is being actively manipulated by some + threads, it follows that lnodes can always live on the stack of such + threads. + + Case-by-case: + + A. access through VFS (reiser4_{read|write|truncate|*}()): + + 1. operation starts with inode supplied by VFS. + + 2. lget( &local_lnode, LNODE_INODE, inode -> i_ino ) is called. This, + if necessary, will wait until sys_reiser4() access to this file is + finished, and + + 3. add lnode to the per super block hash table. + + B. creation of new inode in reiser4_iget(): + + 1. create new empty inode (iget(), or icreate()) + + 2. step A.3. A.2 is not necessary, because we are creating new object + and parent is in VFS access (hence sys_reiser4() cannot add/delete + objects in parent). + + 3. read stat data from disk and initialise inode + + C. sys_reiser4() access: + + 1. check for existing inode in a hash-table. + + Rationale: if inode is already here it is advantageous to use it, + because it already has information from stat data. + + If inode is found proceed as in case A. + + 2. otherwise, lget( &local_lnode, LNODE_LW, oid ) is called. + + + NOT FINISHED. + + + + + + + + INTERNAL NOTES: + + 1. fs/inode.c:inode_lock is not static: we can use it. Good. + + 2. but fs/inode.c:find_inode() is. Either write own version, or remove + static and EXPORT_SYMBOL-ize it. + + + +*/ + +#include "debug.h" +#include "kcond.h" +#include "key.h" +#include "kassign.h" +#include "plugin/plugin_header.h" +#include "plugin/plugin_set.h" +#include "lnode.h" +#include "super.h" +#include "reiser4.h" + +#include /* for struct super_block */ +#include + +static reiser4_key *lnode_dentry_key(const lnode * node, reiser4_key * result); +static reiser4_key *lnode_inode_key(const lnode * node, reiser4_key * result); +static reiser4_key *lnode_lw_key(const lnode * node, reiser4_key * result); +static int lnode_inode_eq(const lnode * node1, const lnode * node2); +static int lnode_lw_eq(const lnode * node1, const lnode * node2); + +#if REISER4_DEBUG +static int lnode_valid_type(lnode_type type); +#endif + +/* Common operations for various types of lnodes. + + NOTE-NIKITA consider making this plugin. */ +static struct { + /* get a key of the corresponding file system object */ + reiser4_key *(*key) (const lnode * node, reiser4_key * result); + /* get a plugin suitable for the corresponding file system object */ + int (*get_plugins) (const lnode * node, plugin_set * area); + /* set a plugin suitable for the corresponding file system object */ + int (*set_plugins) (lnode * node, const plugin_set * area); + /* true if @node1 and @node2 refer to the same object */ + int (*eq) (const lnode * node1, const lnode * node2); +} lnode_ops[LNODE_NR_TYPES] = { + [LNODE_DENTRY] = { + .key = lnode_dentry_key, + .get_plugins = NULL, + .set_plugins = NULL, + .eq = NULL + }, + [LNODE_INODE] = { + .key = lnode_inode_key, + .get_plugins = NULL, + .set_plugins = NULL, + .eq = lnode_inode_eq + }, + /* + [LNODE_PSEUDO] = { + .key = NULL, + .get_plugins = NULL, + .set_plugins = NULL, + .eq = NULL + }, + */ + [LNODE_LW] = { + .key = lnode_lw_key, + .get_plugins = NULL, + .set_plugins = NULL, + .eq = lnode_lw_eq + } +}; + +/* hash table support */ + +/* compare two block numbers for equality. Used by hash-table macros */ +/* Audited by: green(2002.06.15) */ +static inline int +oid_eq(const oid_t * o1 /* first oid to compare */ , + const oid_t * o2 /* second oid to compare */ ) +{ + return *o1 == *o2; +} + +/* Hash znode by block number. Used by hash-table macros */ +/* Audited by: green(2002.06.15) */ +static inline __u32 +oid_hash(ln_hash_table *table, const oid_t * o /* oid to hash */ ) +{ + return *o & (LNODE_HTABLE_BUCKETS - 1); +} + +/* The hash table definition */ +#define KMALLOC(size) kmalloc((size), GFP_KERNEL) +#define KFREE(ptr, size) kfree(ptr) +TYPE_SAFE_HASH_DEFINE(ln, lnode, oid_t, h.oid, h.link, oid_hash, oid_eq); +#undef KFREE +#undef KMALLOC + +ln_hash_table lnode_htable; +spinlock_t lnode_guard = SPIN_LOCK_UNLOCKED; + + +/* true if @required lnode type is @compatible with @set lnode type. If lnode + types are incompatible, then thread trying to obtain @required type of + access will wait until all references (lnodes) of the @set type to the file + system object are released. + + For example, thread trying to manipulate object through VFS (@required type + is LNODE_INODE) will wait if object is currently manipulated through + reiser4() call (that is, there are lnodes with type LNODE_LW). + +*/ +/* Audited by: green(2002.06.15) */ +reiser4_internal int +lnode_compatible_type(lnode_type required /* required lnode type */ , + lnode_type set /* lnode type already set */ ) +{ + return !((set == LNODE_LW) && (required != LNODE_INODE)); +} + +/* initialise lnode module for @super. */ +/* Audited by: green(2002.06.15) */ +reiser4_internal int +lnodes_init(void) +{ + ln_hash_init(&lnode_htable, LNODE_HTABLE_BUCKETS, NULL); + return 0; +} + +/* free lnode resources associated with @super. */ +/* Audited by: green(2002.06.15) */ +reiser4_internal int +lnodes_done(void) +{ + ln_hash_done(&lnode_htable); + return 0; +} + +/* Acquire handle to file system object. + + First check whether there is already lnode for this oid in a hash table. + If no---initialise @node and add it into the hash table. If hash table + already contains lnode with such oid, and incompatible type, wait until + said lnode is deleted. If compatible lnode is found in the hash table, + increase its reference counter and return. + + + + +*/ +/* Audited by: green(2002.06.15) */ +reiser4_internal lnode * +lget( /*lnode * node , lnode to add to the hash table */ + lnode_type type /* lnode type */ , oid_t oid /* objectid */ ) +{ + lnode *result; + + // assert("nikita-1862", node != NULL); + assert("nikita-1866", lnode_valid_type(type)); + + spin_lock(&lnode_guard); + /* check hash table */ + while ((result = ln_hash_find(&lnode_htable, &oid)) != 0) { + if (!lnode_compatible_type(type, result->h.type)) { + int ret; + + /* if lnode is of incompatible type, wait until all + incompatible users go away. For example, if we are + requesting lnode for VFS access (and our @type is + LNODE_INODE), wait until all reiser4() system call + manipulations with this object finish. + */ + ret = kcond_wait(&result->h.cvar, &lnode_guard, 1); + if (ret != 0) { + result = ERR_PTR(ret); + break; + } + } else { + /* compatible lnode found in the hash table. Just + return it. */ + ++result->h.ref; + break; + } + } + if (result == NULL) { + /* lnode wasn't found in the hash table, initialise @node and + add it into hash table. */ + result = ( lnode * ) kmalloc( sizeof( lnode ), GFP_KERNEL); + xmemset(result, 0, sizeof( lnode )); + result->h.type = type; + result->h.oid = oid; + kcond_init(&result->h.cvar); + result->h.ref = 1; + ln_hash_insert(&lnode_htable, result); + } + spin_unlock(&lnode_guard); + return result; +} + +/* release reference to file system object */ +/* Audited by: green(2002.06.15) */ +reiser4_internal void +lput(lnode * node /* lnode to release */ ) +{ + assert("nikita-1864", node != NULL); + assert("nikita-1961", lnode_valid_type(node->h.type)); /* man in + * a + * space */ + spin_lock(&lnode_guard); + assert("nikita-1878", ln_hash_find(&lnode_htable, &node->h.oid) == node); + if (--node->h.ref == 0) { + ln_hash_remove(&lnode_htable, node); + kcond_broadcast(&node->h.cvar); + kfree(node); + } + spin_unlock(&lnode_guard); +} + +reiser4_internal lnode * +lref(lnode * node) +{ + assert("nikita-3241", node != NULL); + assert("nikita-3242", lnode_valid_type(node->h.type)); + + spin_lock(&lnode_guard); + ++ node->h.ref; + spin_unlock(&lnode_guard); + return node; +} + +/* true if @node1 and @node2 refer to the same object */ +/* Audited by: green(2002.06.15) */ +reiser4_internal int +lnode_eq(const lnode * node1 /* first node to compare */ , + const lnode * node2 /* second node to compare */ ) +{ + assert("nikita-1921", node1 != NULL); + assert("nikita-1922", node2 != NULL); /* Finnegans Wake started */ + + if (node1->h.oid != node2->h.oid) + return 0; + else if (node1->h.type != node2->h.type) + return 0; + else + return lnode_ops[node1->h.type].eq(node1, node2); +} + +/* return key of object behind @node */ +/* Audited by: green(2002.06.15) */ +reiser4_internal reiser4_key * +lnode_key(const lnode * node /* lnode to query */ , + reiser4_key * result /* result */ ) +{ + assert("nikita-1849", node != NULL); + assert("nikita-1855", lnode_valid_type(node->h.type)); + return lnode_ops[node->h.type].key(node, result); +} + +/* return plugins of object behind @node */ +/* Audited by: green(2002.06.15) */ +reiser4_internal int +get_lnode_plugins(const lnode * node /* lnode to query */ , + plugin_set * area /* result */ ) +{ + assert("nikita-1853", node != NULL); + assert("nikita-1858", lnode_valid_type(node->h.type)); + return lnode_ops[node->h.type].get_plugins(node, area); +} + +/* set plugins of object behind @node */ +/* Audited by: green(2002.06.15) */ +reiser4_internal int +set_lnode_plugins(lnode * node /* lnode to modify */ , + const plugin_set * area /* plugins to install */ ) +{ + assert("nikita-1859", node != NULL); + assert("nikita-1860", lnode_valid_type(node->h.type)); + return lnode_ops[node->h.type].set_plugins(node, area); +} + +#if REISER4_DEBUG +/* true if @type is valid lnode type */ +/* Audited by: green(2002.06.15) */ +static int +lnode_valid_type(lnode_type type /* would-be lnode type */ ) +{ + return type < LNODE_NR_TYPES; +} +#endif + +/* return key of object behind dentry-based @node */ +reiser4_internal reiser4_key * +lnode_dentry_key(const lnode * node /* lnode to query */ , + reiser4_key * result /* result */ ) +{ + return build_sd_key(node->dentry.dentry->d_inode, result); +} + + + +/* return key of object behind inode-based @node */ +/* Audited by: green(2002.06.15) */ +static reiser4_key * +lnode_inode_key(const lnode * node /* lnode to query */ , + reiser4_key * result /* result */ ) +{ + return build_sd_key(node->inode.inode, result); +} + +/* return key of object behind lighweight @node */ +/* Audited by: green(2002.06.15) */ +static reiser4_key * +lnode_lw_key(const lnode * node /* lnode to query */ , + reiser4_key * result /* result */ ) +{ + *result = node->lw.key; + return result; +} + +/* compare two inodes */ +/* Audited by: green(2002.06.15) */ +static int +lnode_inode_eq(const lnode * node1 /* first node to compare */ , + const lnode * node2 /* second node to compare */ ) +{ + assert("nikita-1923", node1 != NULL); + assert("nikita-1924", node2 != NULL); + + assert("nikita-1927", node1->inode.inode != NULL); + assert("nikita-1928", node2->inode.inode != NULL); + + return (node1->inode.inode == node2->inode.inode); + +} + +/* compare two lw objects */ +/* Audited by: green(2002.06.15) */ +static int +lnode_lw_eq(const lnode * node1 UNUSED_ARG /* first node to + * compare */ , + const lnode * node2 UNUSED_ARG /* second node to + * compare */ ) +{ + assert("nikita-1925", node1 != NULL); + assert("nikita-1926", node2 != NULL); + + /* we only get there if oids are equal */ + assert("nikita-1929", node1->h.oid == node2->h.oid); + assert("nikita-1930", keyeq(&node1->lw.key, &node2->lw.key)); + return 1; +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/lnode.h 2004-09-03 00:57:05.108262688 -0700 @@ -0,0 +1,121 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Declaration of lnode (light-weight node). */ + +#ifndef __LNODE_H__ +#define __LNODE_H__ + +#include "forward.h" +#include "dformat.h" +#include "kcond.h" +#include "type_safe_hash.h" +#include "plugin/plugin_header.h" +#include "plugin/plugin_set.h" +#include "key.h" + +#include /* for __u?? */ +#include /* for struct super_block, etc. */ +#include /* for struct super_block, etc. */ + +typedef enum { + LNODE_DENTRY, + LNODE_INODE, + LNODE_REISER4_INODE, + LNODE_LW, + LNODE_PSEUDO, + LNODE_NR_TYPES +} lnode_type; + +typedef union lnode lnode; + +/* declare hash table of lnode_lw's */ +TYPE_SAFE_HASH_DECLARE(ln, lnode); + +/* common part of various lnode types */ +typedef struct lnode_header { + /* lnode type. Taken from lnode_type enum. Never changed after + initialisation, so needs no locking. */ + __u8 type; + /* unused. Alignment requires this anyway. */ + __u8 flags; + /* condition variable to wake up waiters */ + kcond_t cvar; + /* hash table linkage. Updated under hash-table spinlock. */ + ln_hash_link link; + /* objectid of underlying file system object. Never changed after + initialisation, so needs no locking. */ + oid_t oid; + /* reference counter. Updated under hash-table spinlock. */ + int ref; +} lnode_header; + +typedef struct lnode_dentry { + lnode_header h; + struct dentry *dentry; + struct vfsmount *mnt; +} lnode_dentry; + +typedef struct lnode_inode { + lnode_header h; + struct inode *inode; +} lnode_inode; + +typedef struct lnode_reiser4_inode { + lnode_header h; + struct reiser4_inode *inode; +} lnode_reiser4_inode; + +typedef struct lnode_lw { + lnode_header h; + struct super_block * lw_sb; + reiser4_key key; +} lnode_lw; + +struct assign_result { + loff_t len ; + int return_code ; +}; + +typedef struct lnode_pseudo { + lnode_header h; + struct assign_result rez; + + // lnode *host; + /* something to identify pseudo file type, like name or plugin */ +} lnode_pseudo; + +union lnode { + lnode_header h; + lnode_dentry dentry; + lnode_inode inode; + lnode_reiser4_inode reiser4_inode; + lnode_lw lw; + lnode_pseudo pseudo; +}; + +extern int lnodes_init(void); +extern int lnodes_done(void); + +extern lnode *lget( lnode_type type, oid_t oid); +extern void lput(lnode * node); +extern int lnode_eq(const lnode * node1, const lnode * node2); +extern lnode *lref(lnode * node); + +extern struct inode *inode_by_lnode(const lnode * node); +extern reiser4_key *lnode_key(const lnode * node, reiser4_key * result); + +extern int get_lnode_plugins(const lnode * node, plugin_set * area); +extern int set_lnode_plugins(lnode * node, const plugin_set * area); + +/* __LNODE_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/lock.c 2004-09-03 00:57:05.116261472 -0700 @@ -0,0 +1,1505 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Traditional deadlock avoidance is achieved by acquiring all locks in a single + order. V4 balances the tree from the bottom up, and searches the tree from + the top down, and that is really the way we want it, so tradition won't work + for us. + + Instead we have two lock orderings, a high priority lock ordering, and a low + priority lock ordering. Each node in the tree has a lock in its znode. + + Suppose we have a set of processes which lock (R/W) tree nodes. Each process + has a set (maybe empty) of already locked nodes ("process locked set"). Each + process may have a pending lock request to a node locked by another process. + Note: we lock and unlock, but do not transfer locks: it is possible + transferring locks instead would save some bus locking.... + + Deadlock occurs when we have a loop constructed from process locked sets and + lock request vectors. + + + NOTE: The reiser4 "tree" is a tree on disk, but its cached representation in + memory is extended with "znodes" with which we connect nodes with their left + and right neighbors using sibling pointers stored in the znodes. When we + perform balancing operations we often go from left to right and from right to + left. + + + +-P1-+ +-P3-+ + |+--+| V1 |+--+| + ||N1|| -------> ||N3|| + |+--+| |+--+| + +----+ +----+ + ^ | + |V2 |V3 + | v + +---------P2---------+ + |+--+ +--+| + ||N2| -------- |N4|| + |+--+ +--+| + +--------------------+ + + We solve this by ensuring that only low priority processes lock in top to + bottom order and from right to left, and high priority processes lock from + bottom to top and left to right. + + ZAM-FIXME-HANS: order not just node locks in this way, order atom locks, and + kill those damn busy loops. + ANSWER(ZAM): atom locks (which are introduced by ASTAGE_CAPTURE_WAIT atom + stage) cannot be ordered that way. There are no rules what nodes can belong + to the atom and what nodes cannot. We cannot define what is right or left + direction, what is top or bottom. We can take immediate parent or side + neighbor of one node, but nobody guarantees that, say, left neighbor node is + not a far right neighbor for other nodes from the same atom. It breaks + deadlock avoidance rules and hi-low priority locking cannot be applied for + atom locks. + + How does it help to avoid deadlocks ? + + Suppose we have a deadlock with n processes. Processes from one priority + class never deadlock because they take locks in one consistent + order. + + So, any possible deadlock loop must have low priority as well as high + priority processes. There are no other lock priority levels except low and + high. We know that any deadlock loop contains at least one node locked by a + low priority process and requested by a high priority process. If this + situation is caught and resolved it is sufficient to avoid deadlocks. + + V4 DEADLOCK PREVENTION ALGORITHM IMPLEMENTATION. + + The deadlock prevention algorithm is based on comparing + priorities of node owners (processes which keep znode locked) and + requesters (processes which want to acquire a lock on znode). We + implement a scheme where low-priority owners yield locks to + high-priority requesters. We created a signal passing system that + is used to ask low-priority processes to yield one or more locked + znodes. + + The condition when a znode needs to change its owners is described by the + following formula: + + ############################################# + # # + # (number of high-priority requesters) > 0 # + # AND # + # (numbers of high-priority owners) == 0 # + # # + ############################################# + + Note that a low-priority process + delays node releasing if another high-priority process owns this node. So, slightly more strictly speaking, to have a deadlock capable cycle you must have a loop in which a high priority process is waiting on a low priority process to yield a node, which is slightly different from saying a high priority process is waiting on a node owned by a low priority process. + + It is enough to avoid deadlocks if we prevent any low-priority process from + falling asleep if its locked set contains a node which satisfies the + deadlock condition. + + That condition is implicitly or explicitly checked in all places where new + high-priority requests may be added or removed from node request queue or + high-priority process takes or releases a lock on node. The main + goal of these checks is to never lose the moment when node becomes "has + wrong owners" and send "must-yield-this-lock" signals to its low-pri owners + at that time. + + The information about received signals is stored in the per-process + structure (lock stack) and analyzed before a low-priority process goes to + sleep but after a "fast" attempt to lock a node fails. Any signal wakes + sleeping process up and forces him to re-check lock status and received + signal info. If "must-yield-this-lock" signals were received the locking + primitive (longterm_lock_znode()) fails with -E_DEADLOCK error code. + + V4 LOCKING DRAWBACKS + + If we have already balanced on one level, and we are propagating our changes upward to a higher level, it could be + very messy to surrender all locks on the lower level because we put so much computational work into it, and reverting + them to their state before they were locked might be very complex. We also don't want to acquire all locks before + performing balancing because that would either be almost as much work as the balancing, or it would be too + conservative and lock too much. We want balancing to be done only at high priority. Yet, we might want to go to the + left one node and use some of its empty space... So we make one attempt at getting the node to the left using + try_lock, and if it fails we do without it, because we didn't really need it, it was only a nice to have. + + LOCK STRUCTURES DESCRIPTION + + The following data structures are used in the reiser4 locking + implementation: + + All fields related to long-term locking are stored in znode->lock. + + The lock stack is a per thread object. It owns all znodes locked by the + thread. One znode may be locked by several threads in case of read lock or + one znode may be write locked by one thread several times. The special link + objects (lock handles) support n<->m relation between znodes and lock + owners. + + + + +---------+ +---------+ + | LS1 | | LS2 | + +---------+ +---------+ + ^ ^ + |---------------+ +----------+ + v v v v + +---------+ +---------+ +---------+ +---------+ + | LH1 | | LH2 | | LH3 | | LH4 | + +---------+ +---------+ +---------+ +---------+ + ^ ^ ^ ^ + | +------------+ | + v v v + +---------+ +---------+ +---------+ + | Z1 | | Z2 | | Z3 | + +---------+ +---------+ +---------+ + + Thread 1 locked znodes Z1 and Z2, thread 2 locked znodes Z2 and Z3. The picture above shows that lock stack LS1 has a + list of 2 lock handles LH1 and LH2, lock stack LS2 has a list with lock handles LH3 and LH4 on it. Znode Z1 is + locked by only one thread, znode has only one lock handle LH1 on its list, similar situation is for Z3 which is + locked by the thread 2 only. Z2 is locked (for read) twice by different threads and two lock handles are on its + list. Each lock handle represents a single relation of a locking of a znode by a thread. Locking of a znode is an + establishing of a locking relation between the lock stack and the znode by adding of a new lock handle to a list of + lock handles, the lock stack. The lock stack links all lock handles for all znodes locked by the lock stack. The znode + list groups all lock handles for all locks stacks which locked the znode. + + Yet another relation may exist between znode and lock owners. If lock + procedure cannot immediately take lock on an object it adds the lock owner + on special `requestors' list belongs to znode. That list represents a + queue of pending lock requests. Because one lock owner may request only + only one lock object at a time, it is a 1->n relation between lock objects + and a lock owner implemented as it is described above. Full information + (priority, pointers to lock and link objects) about each lock request is + stored in lock owner structure in `request' field. + + SHORT_TERM LOCKING + + This is a list of primitive operations over lock stacks / lock handles / + znodes and locking descriptions for them. + + 1. locking / unlocking which is done by two list insertion/deletion, one + to/from znode's list of lock handles, another one is to/from lock stack's + list of lock handles. The first insertion is protected by + znode->lock.guard spinlock. The list owned by the lock stack can be + modified only by thread who owns the lock stack and nobody else can + modify/read it. There is nothing to be protected by a spinlock or + something else. + + 2. adding/removing a lock request to/from znode requesters list. The rule is + that znode->lock.guard spinlock should be taken for this. + + 3. we can traverse list of lock handles and use references to lock stacks who + locked given znode if znode->lock.guard spinlock is taken. + + 4. If a lock stack is associated with a znode as a lock requestor or lock + owner its existence is guaranteed by znode->lock.guard spinlock. Some its + (lock stack's) fields should be protected from being accessed in parallel + by two or more threads. Please look at lock_stack structure definition + for the info how those fields are protected. */ + +/* Znode lock and capturing intertwining. */ +/* In current implementation we capture formatted nodes before locking + them. Take a look on longterm lock znode, try_capture() request precedes + locking requests. The longterm_lock_znode function unconditionally captures + znode before even checking of locking conditions. + + Another variant is to capture znode after locking it. It was not tested, but + at least one deadlock condition is supposed to be there. One thread has + locked a znode (Node-1) and calls try_capture() for it. Try_capture() sleeps + because znode's atom has CAPTURE_WAIT state. Second thread is a flushing + thread, its current atom is the atom Node-1 belongs to. Second thread wants + to lock Node-1 and sleeps because Node-1 is locked by the first thread. The + described situation is a deadlock. */ + +#include "debug.h" +#include "txnmgr.h" +#include "znode.h" +#include "jnode.h" +#include "tree.h" +#include "plugin/node/node.h" +#include "super.h" + +#include + +#if REISER4_DEBUG +static int request_is_deadlock_safe(znode *, znode_lock_mode, + znode_lock_request); +#endif + +#define ADDSTAT(node, counter) \ + reiser4_stat_inc_at_level(znode_get_level(node), znode.counter) + +/* Returns a lock owner associated with current thread */ +reiser4_internal lock_stack * +get_current_lock_stack(void) +{ + return &get_current_context()->stack; +} + +/* Wakes up all low priority owners informing them about possible deadlock */ +static void +wake_up_all_lopri_owners(znode * node) +{ + lock_handle *handle; + + assert("nikita-1824", rw_zlock_is_locked(&node->lock)); + for_all_type_safe_list(owners, &node->lock.owners, handle) { + spin_lock_stack(handle->owner); + + assert("nikita-1832", handle->node == node); + /* count this signal in owner->nr_signaled */ + if (!handle->signaled) { + handle->signaled = 1; + atomic_inc(&handle->owner->nr_signaled); + } + /* Wake up a single process */ + __reiser4_wake_up(handle->owner); + + spin_unlock_stack(handle->owner); + } +} + +/* Adds a lock to a lock owner, which means creating a link to the lock and + putting the link into the two lists all links are on (the doubly linked list + that forms the lock_stack, and the doubly linked list of links attached + to a lock. +*/ +static inline void +link_object(lock_handle * handle, lock_stack * owner, znode * node) +{ + assert("jmacd-810", handle->owner == NULL); + assert("nikita-1828", owner == get_current_lock_stack()); + assert("nikita-1830", rw_zlock_is_locked(&node->lock)); + + handle->owner = owner; + handle->node = node; + + assert("reiser4-4", ergo(locks_list_empty(&owner->locks), owner->nr_locks == 0)); + locks_list_push_back(&owner->locks, handle); + owner->nr_locks ++; + + owners_list_push_front(&node->lock.owners, handle); + handle->signaled = 0; +} + +/* Breaks a relation between a lock and its owner */ +static inline void +unlink_object(lock_handle * handle) +{ + assert("zam-354", handle->owner != NULL); + assert("nikita-1608", handle->node != NULL); + assert("nikita-1633", rw_zlock_is_locked(&handle->node->lock)); + assert("nikita-1829", handle->owner == get_current_lock_stack()); + + assert("reiser4-5", handle->owner->nr_locks > 0); + locks_list_remove_clean(handle); + handle->owner->nr_locks --; + assert("reiser4-6", ergo(locks_list_empty(&handle->owner->locks), handle->owner->nr_locks == 0)); + + owners_list_remove_clean(handle); + + /* indicates that lock handle is free now */ + handle->owner = NULL; +} + +/* Actually locks an object knowing that we are able to do this */ +static void +lock_object(lock_stack * owner) +{ + lock_request *request; + znode *node; + assert("nikita-1839", owner == get_current_lock_stack()); + + request = &owner->request; + node = request->node; + assert("nikita-1834", rw_zlock_is_locked(&node->lock)); + if (request->mode == ZNODE_READ_LOCK) { + node->lock.nr_readers++; + } else { + /* check that we don't switched from read to write lock */ + assert("nikita-1840", node->lock.nr_readers <= 0); + /* We allow recursive locking; a node can be locked several + times for write by same process */ + node->lock.nr_readers--; + } + + link_object(request->handle, owner, node); + + if (owner->curpri) { + node->lock.nr_hipri_owners++; + } + ON_TRACE(TRACE_LOCKS, + "%spri lock: %p node: %p: hipri_owners: %u: nr_readers: %d\n", + owner->curpri ? "hi" : "lo", owner, node, node->lock.nr_hipri_owners, node->lock.nr_readers); +} + +/* Check for recursive write locking */ +static int +recursive(lock_stack * owner) +{ + int ret; + znode *node; + + node = owner->request.node; + + /* Owners list is not empty for a locked node */ + assert("zam-314", !owners_list_empty(&node->lock.owners)); + assert("nikita-1841", owner == get_current_lock_stack()); + assert("nikita-1848", rw_zlock_is_locked(&node->lock)); + + ret = (owners_list_front(&node->lock.owners)->owner == owner); + + /* Recursive read locking should be done usual way */ + assert("zam-315", !ret || owner->request.mode == ZNODE_WRITE_LOCK); + /* mixing of read/write locks is not allowed */ + assert("zam-341", !ret || znode_is_wlocked(node)); + + return ret; +} + +#if REISER4_DEBUG +/* Returns true if the lock is held by the calling thread. */ +int +znode_is_any_locked(const znode * node) +{ + lock_handle *handle; + lock_stack *stack; + int ret; + + if (!znode_is_locked(node)) { + return 0; + } + + stack = get_current_lock_stack(); + + spin_lock_stack(stack); + + ret = 0; + + for_all_type_safe_list(locks, &stack->locks, handle) { + if (handle->node == node) { + ret = 1; + break; + } + } + + spin_unlock_stack(stack); + + return ret; +} + +#endif + +/* Returns true if a write lock is held by the calling thread. */ +reiser4_internal int +znode_is_write_locked(const znode * node) +{ + lock_stack *stack; + lock_handle *handle; + + assert("jmacd-8765", node != NULL); + + if (!znode_is_wlocked(node)) { + return 0; + } + + stack = get_current_lock_stack(); + + /* If it is write locked, then all owner handles must equal the current stack. */ + handle = owners_list_front(&node->lock.owners); + + return (handle->owner == stack); +} + +/* This "deadlock" condition is the essential part of reiser4 locking + implementation. This condition is checked explicitly by calling + check_deadlock_condition() or implicitly in all places where znode lock + state (set of owners and request queue) is changed. Locking code is + designed to use this condition to trigger procedure of passing object from + low priority owner(s) to high priority one(s). + + The procedure results in passing an event (setting lock_handle->signaled + flag) and counting this event in nr_signaled field of owner's lock stack + object and wakeup owner's process. +*/ +static inline int +check_deadlock_condition(znode * node) +{ + assert("nikita-1833", rw_zlock_is_locked(&node->lock)); + return node->lock.nr_hipri_requests > 0 && node->lock.nr_hipri_owners == 0; +} + +/* checks lock/request compatibility */ +static int +check_lock_object(lock_stack * owner) +{ + znode *node = owner->request.node; + + assert("nikita-1842", owner == get_current_lock_stack()); + assert("nikita-1843", rw_zlock_is_locked(&node->lock)); + + /* See if the node is disconnected. */ + if (unlikely(ZF_ISSET(node, JNODE_IS_DYING))) { + ON_TRACE(TRACE_LOCKS, "attempt to lock dying znode: %p", node); + return RETERR(-EINVAL); + } + + /* Do not ever try to take a lock if we are going in low priority + direction and a node have a high priority request without high + priority owners. */ + if (unlikely(!owner->curpri && check_deadlock_condition(node))) { + return RETERR(-E_REPEAT); + } + + if (unlikely(!is_lock_compatible(node, owner->request.mode))) { + return RETERR(-E_REPEAT); + } + + return 0; +} + +/* check for lock/request compatibility and update tree statistics */ +static int +can_lock_object(lock_stack * owner) +{ + int result; + znode *node = owner->request.node; + + result = check_lock_object(owner); + if (REISER4_STATS && znode_get_level(node) > 0) { + if (result != 0) + ADDSTAT(node, lock_contented); + else + ADDSTAT(node, lock_uncontented); + } + return result; +} + +/* Setting of a high priority to the process. It clears "signaled" flags + because znode locked by high-priority process can't satisfy our "deadlock + condition". */ +static void +set_high_priority(lock_stack * owner) +{ + assert("nikita-1846", owner == get_current_lock_stack()); + /* Do nothing if current priority is already high */ + if (!owner->curpri) { + /* We don't need locking for owner->locks list, because, this + * function is only called with the lock stack of the current + * thread, and no other thread can play with owner->locks list + * and/or change ->node pointers of lock handles in this list. + * + * (Interrupts also are not involved.) + */ + lock_handle *item = locks_list_front(&owner->locks); + while (!locks_list_end(&owner->locks, item)) { + znode *node = item->node; + + WLOCK_ZLOCK(&node->lock); + + node->lock.nr_hipri_owners++; + + ON_TRACE(TRACE_LOCKS, + "set_hipri lock: %p node: %p: hipri_owners after: %u nr_readers: %d\n", + item, node, node->lock.nr_hipri_owners, node->lock.nr_readers); + + /* we can safely set signaled to zero, because + previous statement (nr_hipri_owners ++) guarantees + that signaled will be never set again. */ + item->signaled = 0; + WUNLOCK_ZLOCK(&node->lock); + + item = locks_list_next(item); + } + owner->curpri = 1; + atomic_set(&owner->nr_signaled, 0); + } +} + +/* Sets a low priority to the process. */ +static void +set_low_priority(lock_stack * owner) +{ + assert("nikita-3075", owner == get_current_lock_stack()); + /* Do nothing if current priority is already low */ + if (owner->curpri) { + /* scan all locks (lock handles) held by @owner, which is + actually current thread, and check whether we are reaching + deadlock possibility anywhere. + */ + lock_handle *handle = locks_list_front(&owner->locks); + while (!locks_list_end(&owner->locks, handle)) { + znode *node = handle->node; + WLOCK_ZLOCK(&node->lock); + /* this thread just was hipri owner of @node, so + nr_hipri_owners has to be greater than zero. */ + ON_TRACE(TRACE_LOCKS, + "set_lopri lock: %p node: %p: hipri_owners before: %u nr_readers: %d\n", + handle, node, node->lock.nr_hipri_owners, node->lock.nr_readers); + assert("nikita-1835", node->lock.nr_hipri_owners > 0); + node->lock.nr_hipri_owners--; + /* If we have deadlock condition, adjust a nr_signaled + field. It is enough to set "signaled" flag only for + current process, other low-pri owners will be + signaled and waken up after current process unlocks + this object and any high-priority requestor takes + control. */ + if (check_deadlock_condition(node) + && !handle->signaled) { + handle->signaled = 1; + atomic_inc(&owner->nr_signaled); + } + WUNLOCK_ZLOCK(&node->lock); + handle = locks_list_next(handle); + } + owner->curpri = 0; + } +} + +#define MAX_CONVOY_SIZE ((NR_CPUS - 1)) + +/* helper function used by longterm_unlock_znode() to wake up requestor(s). */ +/* + * In certain multi threaded work loads jnode spin lock is the most + * contented one. Wake up of threads waiting for znode is, thus, + * important to do right. There are three well known strategies: + * + * (1) direct hand-off. Hasn't been tried. + * + * (2) wake all (thundering herd). This degrades performance in our + * case. + * + * (3) wake one. Simplest solution where requestor in the front of + * requestors list is awaken under znode spin lock is not very + * good on the SMP, because first thing requestor will try to do + * after waking up on another CPU is to acquire znode spin lock + * that is still held by this thread. As an optimization we grab + * lock stack spin lock, release znode spin lock and wake + * requestor. done_context() synchronize against stack spin lock + * to avoid (impossible) case where requestor has been waked by + * some other thread (wake_up_all_lopri_owners(), or something + * similar) and managed to exit before we waked it up. + * + * Effect of this optimization wasn't big, after all. + * + */ +static void +wake_up_requestor(znode *node) +{ +#if NR_CPUS > 2 + requestors_list_head *creditors; + lock_stack *convoy[MAX_CONVOY_SIZE]; + int convoyused; + int convoylimit; + + assert("nikita-3180", node != NULL); + assert("nikita-3181", rw_zlock_is_locked(&node->lock)); + + ADDSTAT(node, wakeup); + + convoyused = 0; + convoylimit = min(num_online_cpus() - 1, MAX_CONVOY_SIZE); + creditors = &node->lock.requestors; + if (!requestors_list_empty(creditors)) { + convoy[0] = requestors_list_front(creditors); + convoyused = 1; + ADDSTAT(node, wakeup_found); + /* + * it has been verified experimentally, that there are no + * convoys on the leaf level. + */ + if (znode_get_level(node) != LEAF_LEVEL && + convoy[0]->request.mode == ZNODE_READ_LOCK && + convoylimit > 1) { + lock_stack *item; + + ADDSTAT(node, wakeup_found_read); + for (item = requestors_list_next(convoy[0]); + ! requestors_list_end(creditors, item); + item = requestors_list_next(item)) { + ADDSTAT(node, wakeup_scan); + if (item->request.mode == ZNODE_READ_LOCK) { + ADDSTAT(node, wakeup_convoy); + convoy[convoyused] = item; + ++ convoyused; + /* + * it is safe to spin lock multiple + * lock stacks here, because lock + * stack cannot sleep on more than one + * requestors queue. + */ + /* + * use raw spin_lock in stead of macro + * wrappers, because spin lock + * profiling code cannot cope with so + * many locks held at the same time. + */ + spin_lock(&item->sguard.lock); + if (convoyused == convoylimit) + break; + } + } + } + spin_lock(&convoy[0]->sguard.lock); + } + + WUNLOCK_ZLOCK(&node->lock); + + while (convoyused > 0) { + -- convoyused; + __reiser4_wake_up(convoy[convoyused]); + spin_unlock(&convoy[convoyused]->sguard.lock); + } +#else + /* uniprocessor case: keep it simple */ + if (!requestors_list_empty(&node->lock.requestors)) { + lock_stack *requestor; + + requestor = requestors_list_front(&node->lock.requestors); + reiser4_wake_up(requestor); + } + + WUNLOCK_ZLOCK(&node->lock); +#endif +} + +#undef MAX_CONVOY_SIZE + +/* release long-term lock, acquired by longterm_lock_znode() */ +reiser4_internal void +longterm_unlock_znode(lock_handle * handle) +{ + znode *node = handle->node; + lock_stack *oldowner = handle->owner; + int hipri; + int readers; + int rdelta; + int youdie; + + /* + * this is time-critical and highly optimized code. Modify carefully. + */ + + assert("jmacd-1021", handle != NULL); + assert("jmacd-1022", handle->owner != NULL); + assert("nikita-1392", LOCK_CNT_GTZ(long_term_locked_znode)); + + assert("zam-130", oldowner == get_current_lock_stack()); + + LOCK_CNT_DEC(long_term_locked_znode); + + ADDSTAT(node, unlock); + + /* + * to minimize amount of operations performed under lock, pre-compute + * all variables used within critical section. This makes code + * obscure. + */ + + /* was this lock of hi or lo priority */ + hipri = oldowner->curpri ? -1 : 0; + /* number of readers */ + readers = node->lock.nr_readers; + /* +1 if write lock, -1 if read lock */ + rdelta = (readers > 0) ? -1 : +1; + /* true if node is to die and write lock is released */ + youdie = ZF_ISSET(node, JNODE_HEARD_BANSHEE) && (readers < 0); + + WLOCK_ZLOCK(&node->lock); + + assert("zam-101", znode_is_locked(node)); + + /* Adjust a number of high priority owners of this lock */ + node->lock.nr_hipri_owners += hipri; + assert("nikita-1836", node->lock.nr_hipri_owners >= 0); + + ON_TRACE(TRACE_LOCKS, + "%spri unlock: %p node: %p: hipri_owners: %u nr_readers %d\n", + oldowner->curpri ? "hi" : "lo", + handle, + node, + node->lock.nr_hipri_owners, + node->lock.nr_readers); + + /* Handle znode deallocation on last write-lock release. */ + if (znode_is_wlocked_once(node)) { + if (youdie) { + forget_znode(handle); + assert("nikita-2191", znode_invariant(node)); + zput(node); + return; + } + znode_post_write(node); + } + if (znode_is_rlocked(node)) + ON_STATS(znode_at_read(node)); + + if (handle->signaled) + atomic_dec(&oldowner->nr_signaled); + + /* Unlocking means owner<->object link deletion */ + unlink_object(handle); + + /* This is enough to be sure whether an object is completely + unlocked. */ + node->lock.nr_readers += rdelta; + + /* If the node is locked it must have an owners list. Likewise, if + the node is unlocked it must have an empty owners list. */ + assert("zam-319", equi(znode_is_locked(node), + !owners_list_empty(&node->lock.owners))); + +#if REISER4_DEBUG + if (!znode_is_locked(node)) + ++ node->times_locked; +#endif + + /* If there are pending lock requests we wake up a requestor */ + if (!znode_is_wlocked(node)) + wake_up_requestor(node); + else + WUNLOCK_ZLOCK(&node->lock); + + assert("nikita-3182", rw_zlock_is_not_locked(&node->lock)); + /* minus one reference from handle->node */ + handle->node = NULL; + assert("nikita-2190", znode_invariant(node)); + ON_DEBUG(check_lock_data()); + ON_DEBUG(check_lock_node_data(node)); + zput(node); +} + +/* final portion of longterm-unlock*/ +static int +lock_tail(lock_stack *owner, int wake_up_next, int ok, znode_lock_mode mode) +{ + znode *node = owner->request.node; + + assert("jmacd-807", rw_zlock_is_locked(&node->lock)); + + /* If we broke with (ok == 0) it means we can_lock, now do it. */ + if (ok == 0) { + lock_object(owner); + owner->request.mode = 0; + if (mode == ZNODE_READ_LOCK) + wake_up_next = 1; + if (REISER4_DEBUG_MODIFY) { + if (znode_is_wlocked_once(node)) + znode_post_write(node); + else if (znode_is_rlocked(node)) + ON_STATS(znode_at_read(node)); + } + } + + if (wake_up_next) + wake_up_requestor(node); + else + WUNLOCK_ZLOCK(&node->lock); + + if (ok == 0) { + /* count a reference from lockhandle->node + + znode was already referenced at the entry to this function, + hence taking spin-lock here is not necessary (see comment + in the zref()). + */ + zref(node); + + LOCK_CNT_INC(long_term_locked_znode); + if (REISER4_DEBUG_NODE && mode == ZNODE_WRITE_LOCK) { + node_check(node, 0); + ON_DEBUG_MODIFY(znode_pre_write(node)); + } + } + + ON_DEBUG(check_lock_data()); + ON_DEBUG(check_lock_node_data(node)); + return ok; +} + +/* + * version of longterm_znode_lock() optimized for the most common case: read + * lock without any special flags. This is the kind of lock that any tree + * traversal takes on the root node of the tree, which is very frequent. + */ +static int +longterm_lock_tryfast(lock_stack * owner) +{ + int result; + int wake_up_next = 0; + znode *node; + zlock *lock; + + node = owner->request.node; + lock = &node->lock; + + assert("nikita-3340", schedulable()); + assert("nikita-3341", request_is_deadlock_safe(node, + ZNODE_READ_LOCK, + ZNODE_LOCK_LOPRI)); + + result = UNDER_RW(zlock, lock, read, can_lock_object(owner)); + + if (likely(result != -EINVAL)) { + spin_lock_znode(node); + result = try_capture( + ZJNODE(node), ZNODE_READ_LOCK, 0, 1/* can copy on capture */); + spin_unlock_znode(node); + WLOCK_ZLOCK(lock); + if (unlikely(result != 0)) { + owner->request.mode = 0; + wake_up_next = 1; + } else { + result = can_lock_object(owner); + if (unlikely(result == -E_REPEAT)) { + /* fall back to longterm_lock_znode() */ + WUNLOCK_ZLOCK(lock); + return 1; + } + } + return lock_tail(owner, wake_up_next, result, ZNODE_READ_LOCK); + } else + return 1; +} + +/* locks given lock object */ +reiser4_internal int +longterm_lock_znode( + /* local link object (allocated by lock owner thread, usually on its own + * stack) */ + lock_handle * handle, + /* znode we want to lock. */ + znode * node, + /* {ZNODE_READ_LOCK, ZNODE_WRITE_LOCK}; */ + znode_lock_mode mode, + /* {0, -EINVAL, -E_DEADLOCK}, see return codes description. */ + znode_lock_request request) +{ + int ret; + int hipri = (request & ZNODE_LOCK_HIPRI) != 0; + int wake_up_next = 0; + int non_blocking = 0; + int has_atom; + txn_capture cap_flags; + zlock *lock; + txn_handle *txnh; + tree_level level; + + /* Get current process context */ + lock_stack *owner = get_current_lock_stack(); + + /* Check that the lock handle is initialized and isn't already being + * used. */ + assert("jmacd-808", handle->owner == NULL); + assert("nikita-3026", schedulable()); + assert("nikita-3219", request_is_deadlock_safe(node, mode, request)); + /* long term locks are not allowed in the VM contexts (->writepage(), + * prune_{d,i}cache()). + * + * FIXME this doesn't work due to unused-dentry-with-unlinked-inode + * bug caused by d_splice_alias() only working for directories. + */ + assert("nikita-3547", 1 || ((current->flags & PF_MEMALLOC) == 0)); + + cap_flags = 0; + if (request & ZNODE_LOCK_NONBLOCK) { + cap_flags |= TXN_CAPTURE_NONBLOCKING; + non_blocking = 1; + } + + if (request & ZNODE_LOCK_DONT_FUSE) + cap_flags |= TXN_CAPTURE_DONT_FUSE; + + /* If we are changing our process priority we must adjust a number + of high priority owners for each znode that we already lock */ + if (hipri) { + set_high_priority(owner); + } else { + set_low_priority(owner); + } + + level = znode_get_level(node); + ADDSTAT(node, lock); + + /* Fill request structure with our values. */ + owner->request.mode = mode; + owner->request.handle = handle; + owner->request.node = node; + + txnh = get_current_context()->trans; + lock = &node->lock; + + if (mode == ZNODE_READ_LOCK && request == 0) { + ret = longterm_lock_tryfast(owner); + if (ret <= 0) + return ret; + } + + has_atom = (txnh->atom != NULL); + + /* update statistics */ + if (REISER4_STATS) { + if (mode == ZNODE_READ_LOCK) + ADDSTAT(node, lock_read); + else + ADDSTAT(node, lock_write); + + if (hipri) + ADDSTAT(node, lock_hipri); + else + ADDSTAT(node, lock_lopri); + } + + /* Synchronize on node's zlock guard lock. */ + WLOCK_ZLOCK(lock); + + if (znode_is_locked(node) && + mode == ZNODE_WRITE_LOCK && recursive(owner)) + return lock_tail(owner, 0, 0, mode); + + for (;;) { + ADDSTAT(node, lock_iteration); + + /* Check the lock's availability: if it is unavaiable we get + E_REPEAT, 0 indicates "can_lock", otherwise the node is + invalid. */ + ret = can_lock_object(owner); + + if (unlikely(ret == -EINVAL)) { + /* @node is dying. Leave it alone. */ + /* wakeup next requestor to support lock invalidating */ + wake_up_next = 1; + ADDSTAT(node, lock_dying); + break; + } + + if (unlikely(ret == -E_REPEAT && non_blocking)) { + /* either locking of @node by the current thread will + * lead to the deadlock, or lock modes are + * incompatible. */ + ADDSTAT(node, lock_cannot_lock); + break; + } + + assert("nikita-1844", (ret == 0) || ((ret == -E_REPEAT) && !non_blocking)); + /* If we can get the lock... Try to capture first before + taking the lock.*/ + + /* first handle commonest case where node and txnh are already + * in the same atom. */ + /* safe to do without taking locks, because: + * + * 1. read of aligned word is atomic with respect to writes to + * this word + * + * 2. false negatives are handled in try_capture(). + * + * 3. false positives are impossible. + * + * PROOF: left as an exercise to the curious reader. + * + * Just kidding. Here is one: + * + * At the time T0 txnh->atom is stored in txnh_atom. + * + * At the time T1 node->atom is stored in node_atom. + * + * At the time T2 we observe that + * + * txnh_atom != NULL && node_atom == txnh_atom. + * + * Imagine that at this moment we acquire node and txnh spin + * lock in this order. Suppose that under spin lock we have + * + * node->atom != txnh->atom, (S1) + * + * at the time T3. + * + * txnh->atom != NULL still, because txnh is open by the + * current thread. + * + * Suppose node->atom == NULL, that is, node was un-captured + * between T1, and T3. But un-capturing of formatted node is + * always preceded by the call to invalidate_lock(), which + * marks znode as JNODE_IS_DYING under zlock spin + * lock. Contradiction, because can_lock_object() above checks + * for JNODE_IS_DYING. Hence, node->atom != NULL at T3. + * + * Suppose that node->atom != node_atom, that is, atom, node + * belongs to was fused into another atom: node_atom was fused + * into node->atom. Atom of txnh was equal to node_atom at T2, + * which means that under spin lock, txnh->atom == node->atom, + * because txnh->atom can only follow fusion + * chain. Contradicts S1. + * + * The same for hypothesis txnh->atom != txnh_atom. Hence, + * node->atom == node_atom == txnh_atom == txnh->atom. Again + * contradicts S1. Hence S1 is false. QED. + * + */ + + if (likely(has_atom && ZJNODE(node)->atom == txnh->atom)) { + ADDSTAT(node, lock_no_capture); + } else { + /* + * unlock zlock spin lock here. It is possible for + * longterm_unlock_znode() to sneak in here, but there + * is no harm: invalidate_lock() will mark znode as + * JNODE_IS_DYING and this will be noted by + * can_lock_object() below. + */ + WUNLOCK_ZLOCK(lock); + spin_lock_znode(node); + ret = try_capture( + ZJNODE(node), mode, cap_flags, 1/* can copy on capture*/); + spin_unlock_znode(node); + WLOCK_ZLOCK(lock); + if (unlikely(ret != 0)) { + /* In the failure case, the txnmgr releases + the znode's lock (or in some cases, it was + released a while ago). There's no need to + reacquire it so we should return here, + avoid releasing the lock. */ + owner->request.mode = 0; + /* next requestor may not fail */ + wake_up_next = 1; + break; + } + + /* Check the lock's availability again -- this is + because under some circumstances the capture code + has to release and reacquire the znode spinlock. */ + ret = can_lock_object(owner); + } + + /* This time, a return of (ret == 0) means we can lock, so we + should break out of the loop. */ + if (likely(ret != -E_REPEAT || non_blocking)) { + ADDSTAT(node, lock_can_lock); + break; + } + + /* Lock is unavailable, we have to wait. */ + + /* By having semaphore initialization here we cannot lose + wakeup signal even if it comes after `nr_signaled' field + check. */ + ret = prepare_to_sleep(owner); + if (unlikely(ret != 0)) { + break; + } + + assert("nikita-1837", rw_zlock_is_locked(&node->lock)); + if (hipri) { + /* If we are going in high priority direction then + increase high priority requests counter for the + node */ + lock->nr_hipri_requests++; + /* If there are no high priority owners for a node, + then immediately wake up low priority owners, so + they can detect possible deadlock */ + if (lock->nr_hipri_owners == 0) + wake_up_all_lopri_owners(node); + /* And prepare a lock request */ + requestors_list_push_front(&lock->requestors, owner); + } else { + /* If we are going in low priority direction then we + set low priority to our process. This is the only + case when a process may become low priority */ + /* And finally prepare a lock request */ + requestors_list_push_back(&lock->requestors, owner); + } + + /* Ok, here we have prepared a lock request, so unlock + a znode ...*/ + WUNLOCK_ZLOCK(lock); + /* ... and sleep */ + go_to_sleep(owner, level); + + WLOCK_ZLOCK(lock); + + if (hipri) { + assert("nikita-1838", lock->nr_hipri_requests > 0); + lock->nr_hipri_requests--; + } + + requestors_list_remove(owner); + } + + assert("jmacd-807/a", rw_zlock_is_locked(&node->lock)); + return lock_tail(owner, wake_up_next, ret, mode); +} + +/* lock object invalidation means changing of lock object state to `INVALID' + and waiting for all other processes to cancel theirs lock requests. */ +reiser4_internal void +invalidate_lock(lock_handle * handle /* path to lock + * owner and lock + * object is being + * invalidated. */ ) +{ + znode *node = handle->node; + lock_stack *owner = handle->owner; + lock_stack *rq; + + assert("zam-325", owner == get_current_lock_stack()); + assert("zam-103", znode_is_write_locked(node)); + assert("nikita-1393", !ZF_ISSET(node, JNODE_LEFT_CONNECTED)); + assert("nikita-1793", !ZF_ISSET(node, JNODE_RIGHT_CONNECTED)); + assert("nikita-1394", ZF_ISSET(node, JNODE_HEARD_BANSHEE)); + assert("nikita-3097", znode_is_wlocked_once(node)); + assert("nikita-3338", rw_zlock_is_locked(&node->lock)); + + if (handle->signaled) + atomic_dec(&owner->nr_signaled); + + ZF_SET(node, JNODE_IS_DYING); + unlink_object(handle); + node->lock.nr_readers = 0; + + /* all requestors will be informed that lock is invalidated. */ + for_all_type_safe_list(requestors, &node->lock.requestors, rq) { + reiser4_wake_up(rq); + } + + /* We use that each unlock() will wakeup first item from requestors + list; our lock stack is the last one. */ + while (!requestors_list_empty(&node->lock.requestors)) { + requestors_list_push_back(&node->lock.requestors, owner); + + prepare_to_sleep(owner); + + WUNLOCK_ZLOCK(&node->lock); + go_to_sleep(owner, znode_get_level(node)); + WLOCK_ZLOCK(&node->lock); + + requestors_list_remove(owner); + } + + WUNLOCK_ZLOCK(&node->lock); +} + +/* Initializes lock_stack. */ +reiser4_internal void +init_lock_stack(lock_stack * owner /* pointer to + * allocated + * structure. */ ) +{ + /* xmemset(,0,) is done already as a part of reiser4 context + * initialization */ + /* xmemset(owner, 0, sizeof (lock_stack)); */ + locks_list_init(&owner->locks); + requestors_list_clean(owner); + spin_stack_init(owner); + owner->curpri = 1; + sema_init(&owner->sema, 0); +} + +/* Initializes lock object. */ +reiser4_internal void +reiser4_init_lock(zlock * lock /* pointer on allocated + * uninitialized lock object + * structure. */ ) +{ + xmemset(lock, 0, sizeof (zlock)); + rw_zlock_init(lock); + requestors_list_init(&lock->requestors); + owners_list_init(&lock->owners); +} + +/* lock handle initialization */ +reiser4_internal void +init_lh(lock_handle * handle) +{ + xmemset(handle, 0, sizeof *handle); + locks_list_clean(handle); + owners_list_clean(handle); +} + +/* freeing of lock handle resources */ +reiser4_internal void +done_lh(lock_handle * handle) +{ + assert("zam-342", handle != NULL); + if (handle->owner != NULL) + longterm_unlock_znode(handle); +} + +/* What kind of lock? */ +reiser4_internal znode_lock_mode lock_mode(lock_handle * handle) +{ + if (handle->owner == NULL) { + return ZNODE_NO_LOCK; + } else if (znode_is_rlocked(handle->node)) { + return ZNODE_READ_LOCK; + } else { + return ZNODE_WRITE_LOCK; + } +} + +/* Transfer a lock handle (presumably so that variables can be moved between stack and + heap locations). */ +static void +move_lh_internal(lock_handle * new, lock_handle * old, int unlink_old) +{ + znode *node = old->node; + lock_stack *owner = old->owner; + int signaled; + + /* locks_list, modified by link_object() is not protected by + anything. This is valid because only current thread ever modifies + locks_list of its lock_stack. + */ + assert("nikita-1827", owner == get_current_lock_stack()); + assert("nikita-1831", new->owner == NULL); + + WLOCK_ZLOCK(&node->lock); + + signaled = old->signaled; + if (unlink_old) { + unlink_object(old); + } else { + if (node->lock.nr_readers > 0) { + node->lock.nr_readers += 1; + } else { + node->lock.nr_readers -= 1; + } + if (signaled) { + atomic_inc(&owner->nr_signaled); + } + if (owner->curpri) { + node->lock.nr_hipri_owners += 1; + } + LOCK_CNT_INC(long_term_locked_znode); + + zref(node); + } + link_object(new, owner, node); + new->signaled = signaled; + + WUNLOCK_ZLOCK(&node->lock); +} + +reiser4_internal void +move_lh(lock_handle * new, lock_handle * old) +{ + move_lh_internal(new, old, /*unlink_old */ 1); +} + +reiser4_internal void +copy_lh(lock_handle * new, lock_handle * old) +{ + move_lh_internal(new, old, /*unlink_old */ 0); +} + +/* after getting -E_DEADLOCK we unlock znodes until this function returns false */ +reiser4_internal int +check_deadlock(void) +{ + lock_stack *owner = get_current_lock_stack(); + return atomic_read(&owner->nr_signaled) != 0; +} + +/* Before going to sleep we re-check "release lock" requests which might come from threads with hi-pri lock + priorities. */ +reiser4_internal int +prepare_to_sleep(lock_stack * owner) +{ + assert("nikita-1847", owner == get_current_lock_stack()); + /* NOTE(Zam): We cannot reset the lock semaphore here because it may + clear wake-up signal. The initial design was to re-check all + conditions under which we continue locking, release locks or sleep + until conditions are changed. However, even lock.c does not follow + that design. So, wake-up signal which is stored in semaphore state + could we loosen by semaphore reset. The less complex scheme without + resetting the semaphore is enough to not to loose wake-ups. + + if (0) { + + NOTE-NIKITA: I commented call to sema_init() out hoping + that it is the reason or thread sleeping in + down(&owner->sema) without any other thread running. + + Anyway, it is just an optimization: is semaphore is not + reinitialised at this point, in the worst case + longterm_lock_znode() would have to iterate its loop once + more. + spin_lock_stack(owner); + sema_init(&owner->sema, 0); + spin_unlock_stack(owner); + } + */ + + /* We return -E_DEADLOCK if one or more "give me the lock" messages are + * counted in nr_signaled */ + if (unlikely(atomic_read(&owner->nr_signaled) != 0)) { + assert("zam-959", !owner->curpri); + return RETERR(-E_DEADLOCK); + } + return 0; +} + +/* Wakes up a single thread */ +reiser4_internal void +__reiser4_wake_up(lock_stack * owner) +{ + up(&owner->sema); +} + +/* Puts a thread to sleep */ +reiser4_internal void +__go_to_sleep(lock_stack * owner +#if REISER4_STATS + , int node_level +#endif +) +{ +#if REISER4_STATS + unsigned long sleep_start = jiffies; +#endif + /* Well, we might sleep here, so holding of any spinlocks is no-no */ + assert("nikita-3027", schedulable()); + /* return down_interruptible(&owner->sema); */ + down(&owner->sema); +#if REISER4_STATS + switch (node_level) { + case ADD_TO_SLEPT_IN_WAIT_EVENT: + reiser4_stat_add(txnmgr.slept_in_wait_event, jiffies - sleep_start); + break; + case ADD_TO_SLEPT_IN_WAIT_ATOM: + reiser4_stat_add(txnmgr.slept_in_wait_atom, jiffies - sleep_start); + break; + default: + reiser4_stat_add_at_level(node_level, time_slept, + jiffies - sleep_start); + } +#endif +} + +reiser4_internal int +lock_stack_isclean(lock_stack * owner) +{ + if (locks_list_empty(&owner->locks)) { + assert("zam-353", atomic_read(&owner->nr_signaled) == 0); + return 1; + } + + return 0; +} + +#if REISER4_DEBUG_OUTPUT +/* Debugging help */ +reiser4_internal void +print_lock_stack(const char *prefix, lock_stack * owner) +{ + lock_handle *handle; + + spin_lock_stack(owner); + + printk("%s:\n", prefix); + printk(".... nr_signaled %d\n", atomic_read(&owner->nr_signaled)); + printk(".... curpri %s\n", owner->curpri ? "high" : "low"); + + if (owner->request.mode != 0) { + printk(".... current request: %s", owner->request.mode == ZNODE_WRITE_LOCK ? "write" : "read"); + print_address("", znode_get_block(owner->request.node)); + } + + printk(".... current locks:\n"); + + for_all_type_safe_list(locks, &owner->locks, handle) { + if (handle->node != NULL) + print_address(znode_is_rlocked(handle->node) ? + "...... read" : "...... write", znode_get_block(handle->node)); + } + + spin_unlock_stack(owner); +} +#endif + +#if REISER4_DEBUG + +/* + * debugging functions + */ + +/* check consistency of locking data-structures hanging of the @stack */ +void +check_lock_stack(lock_stack * stack) +{ + spin_lock_stack(stack); + /* check that stack->locks is not corrupted */ + locks_list_check(&stack->locks); + spin_unlock_stack(stack); +} + +/* check consistency of locking data structures */ +void +check_lock_data(void) +{ + check_lock_stack(&get_current_context()->stack); +} + +/* check consistency of locking data structures for @node */ +void +check_lock_node_data(znode * node) +{ + RLOCK_ZLOCK(&node->lock); + owners_list_check(&node->lock.owners); + requestors_list_check(&node->lock.requestors); + RUNLOCK_ZLOCK(&node->lock); +} + +/* check that given lock request is dead lock safe. This check is, of course, + * not exhaustive. */ +static int +request_is_deadlock_safe(znode * node, znode_lock_mode mode, + znode_lock_request request) +{ + lock_stack *owner; + + owner = get_current_lock_stack(); + /* + * check that hipri lock request is not issued when there are locked + * nodes at the higher levels. + */ + if (request & ZNODE_LOCK_HIPRI && !(request & ZNODE_LOCK_NONBLOCK) && + znode_get_level(node) != 0) { + lock_handle *item; + + for_all_type_safe_list(locks, &owner->locks, item) { + znode *other = item->node; + + if (znode_get_level(other) == 0) + continue; + if (znode_get_level(other) > znode_get_level(node)) + return 0; + } + } + return 1; +} + +#endif + +/* return pointer to static storage with name of lock_mode. For + debugging */ +reiser4_internal const char * +lock_mode_name(znode_lock_mode lock /* lock mode to get name of */ ) +{ + if (lock == ZNODE_READ_LOCK) + return "read"; + else if (lock == ZNODE_WRITE_LOCK) + return "write"; + else { + static char buf[30]; + + sprintf(buf, "unknown: %i", lock); + return buf; + } +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/lock.h 2004-09-03 00:57:05.118261168 -0700 @@ -0,0 +1,270 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Long term locking data structures. See lock.c for details. */ + +#ifndef __LOCK_H__ +#define __LOCK_H__ + +#include "forward.h" +#include "debug.h" +#include "dformat.h" +#include "spin_macros.h" +#include "key.h" +#include "coord.h" +#include "type_safe_list.h" +#include "plugin/node/node.h" +#include "jnode.h" +#include "readahead.h" + +#include +#include +#include /* for PAGE_CACHE_SIZE */ +#include +#include + +/* per-znode lock requests queue; list items are lock owner objects + which want to lock given znode. + + Locking: protected by znode spin lock. */ +TYPE_SAFE_LIST_DECLARE(requestors); +/* per-znode list of lock handles for this znode + + Locking: protected by znode spin lock. */ +TYPE_SAFE_LIST_DECLARE(owners); +/* per-owner list of lock handles that point to locked znodes which + belong to one lock owner + + Locking: this list is only accessed by the thread owning the lock stack this + list is attached to. Hence, no locking is necessary. +*/ +TYPE_SAFE_LIST_DECLARE(locks); + +/* Per-znode lock object */ +struct zlock { + reiser4_rw_data guard; + /* The number of readers if positive; the number of recursively taken + write locks if negative. Protected by zlock spin lock. */ + int nr_readers; + /* A number of processes (lock_stacks) that have this object + locked with high priority */ + unsigned nr_hipri_owners; + /* A number of attempts to lock znode in high priority direction */ + unsigned nr_hipri_requests; + /* A linked list of lock_handle objects that contains pointers + for all lock_stacks which have this lock object locked */ + owners_list_head owners; + /* A linked list of lock_stacks that wait for this lock */ + requestors_list_head requestors; +}; + +#define rw_ordering_pred_zlock(lock) \ + (lock_counters()->spin_locked_stack == 0) + +/* Define spin_lock_zlock, spin_unlock_zlock, etc. */ +RW_LOCK_FUNCTIONS(zlock, zlock, guard); + +#define lock_is_locked(lock) ((lock)->nr_readers != 0) +#define lock_is_rlocked(lock) ((lock)->nr_readers > 0) +#define lock_is_wlocked(lock) ((lock)->nr_readers < 0) +#define lock_is_wlocked_once(lock) ((lock)->nr_readers == -1) +#define lock_can_be_rlocked(lock) ((lock)->nr_readers >=0) +#define lock_mode_compatible(lock, mode) \ + (((mode) == ZNODE_WRITE_LOCK && !lock_is_locked(lock)) \ + || ((mode) == ZNODE_READ_LOCK && lock_can_be_rlocked(lock))) + + +/* Since we have R/W znode locks we need additional bidirectional `link' + objects to implement n<->m relationship between lock owners and lock + objects. We call them `lock handles'. + + Locking: see lock.c/"SHORT-TERM LOCKING" +*/ +struct lock_handle { + /* This flag indicates that a signal to yield a lock was passed to + lock owner and counted in owner->nr_signalled + + Locking: this is accessed under spin lock on ->node. + */ + int signaled; + /* A link to owner of a lock */ + lock_stack *owner; + /* A link to znode locked */ + znode *node; + /* A list of all locks for a process */ + locks_list_link locks_link; + /* A list of all owners for a znode */ + owners_list_link owners_link; +}; + +typedef struct lock_request { + /* A pointer to uninitialized link object */ + lock_handle *handle; + /* A pointer to the object we want to lock */ + znode *node; + /* Lock mode (ZNODE_READ_LOCK or ZNODE_WRITE_LOCK) */ + znode_lock_mode mode; +} lock_request; + +/* A lock stack structure for accumulating locks owned by a process */ +struct lock_stack { + /* A guard lock protecting a lock stack */ + reiser4_spin_data sguard; + /* number of znodes which were requested by high priority processes */ + atomic_t nr_signaled; + /* Current priority of a process + + This is only accessed by the current thread and thus requires no + locking. + */ + int curpri; + /* A list of all locks owned by this process. Elements can be added to + * this list only by the current thread. ->node pointers in this list + * can be only changed by the current thread. */ + locks_list_head locks; + int nr_locks; /* number of lock handles in the above list */ + /* When lock_stack waits for the lock, it puts itself on double-linked + requestors list of that lock */ + requestors_list_link requestors_link; + /* Current lock request info. + + This is only accessed by the current thread and thus requires no + locking. + */ + lock_request request; + /* It is a lock_stack's synchronization object for when process sleeps + when requested lock not on this lock_stack but which it wishes to + add to this lock_stack is not immediately available. It is used + instead of wait_queue_t object due to locking problems (lost wake + up). "lost wakeup" occurs when process is waken up before he actually + becomes 'sleepy' (through sleep_on()). Using of semaphore object is + simplest way to avoid that problem. + + A semaphore is used in the following way: only the process that is + the owner of the lock_stack initializes it (to zero) and calls + down(sema) on it. Usually this causes the process to sleep on the + semaphore. Other processes may wake him up by calling up(sema). The + advantage to a semaphore is that up() and down() calls are not + required to preserve order. Unlike wait_queue it works when process + is woken up before getting to sleep. + + NOTE-NIKITA: Transaction manager is going to have condition variables + (&kcondvar_t) anyway, so this probably will be replaced with + one in the future. + + After further discussion, Nikita has shown me that Zam's implementation is + exactly a condition variable. The znode's {zguard,requestors_list} represents + condition variable and the lock_stack's {sguard,semaphore} guards entry and + exit from the condition variable's wait queue. But the existing code can't + just be replaced with a more general abstraction, and I think its fine the way + it is. */ + struct semaphore sema; +}; + +/* defining of list manipulation functions for lists above */ +TYPE_SAFE_LIST_DEFINE(requestors, lock_stack, requestors_link); +TYPE_SAFE_LIST_DEFINE(owners, lock_handle, owners_link); +TYPE_SAFE_LIST_DEFINE(locks, lock_handle, locks_link); + +/* + User-visible znode locking functions +*/ + +extern int longterm_lock_znode (lock_handle * handle, + znode * node, + znode_lock_mode mode, + znode_lock_request request); + +extern void longterm_unlock_znode(lock_handle * handle); + +extern int check_deadlock(void); + +extern lock_stack *get_current_lock_stack(void); + +extern void init_lock_stack(lock_stack * owner); +extern void reiser4_init_lock(zlock * lock); + +extern void init_lh(lock_handle *); +extern void move_lh(lock_handle * new, lock_handle * old); +extern void copy_lh(lock_handle * new, lock_handle * old); +extern void done_lh(lock_handle *); +extern znode_lock_mode lock_mode(lock_handle *); + +extern int prepare_to_sleep(lock_stack * owner); + +#if REISER4_STATS + +#define ADD_TO_SLEPT_IN_WAIT_EVENT (-1) +#define ADD_TO_SLEPT_IN_WAIT_ATOM (-2) + +/* if REISER4_STATS __go_to_sleep() accepts additional parameter @level for + * gathering per-level sleep statistics. The go_to_sleep wrapper hides the + * __go_to_sleep() function prototypes difference. */ +void __go_to_sleep(lock_stack*, int); +#define go_to_sleep(owner, level) __go_to_sleep(owner, level); + +#else + +void __go_to_sleep(lock_stack*); +#define go_to_sleep(owner, level) __go_to_sleep(owner) + +#endif + +extern void __reiser4_wake_up(lock_stack * owner); + +extern int lock_stack_isclean(lock_stack * owner); + +/* zlock object state check macros: only used in assertions. Both forms imply that the + lock is held by the current thread. */ +extern int znode_is_write_locked(const znode * node); + +#if REISER4_DEBUG +#define spin_ordering_pred_stack_addendum (1) +#else +#define spin_ordering_pred_stack_addendum \ + ((lock_counters()->rw_locked_dk == 0) && \ + (lock_counters()->rw_locked_tree == 0)) +#endif +/* lock ordering is: first take zlock spin lock, then lock stack spin lock */ +#define spin_ordering_pred_stack(stack) \ + ((lock_counters()->spin_locked_stack == 0) && \ + (lock_counters()->spin_locked_txnmgr == 0) && \ + (lock_counters()->spin_locked_super == 0) && \ + (lock_counters()->spin_locked_inode_object == 0) && \ + (lock_counters()->rw_locked_cbk_cache == 0) && \ + (lock_counters()->spin_locked_epoch == 0) && \ + (lock_counters()->spin_locked_super_eflush == 0) && \ + spin_ordering_pred_stack_addendum) + +/* Same for lock_stack */ +SPIN_LOCK_FUNCTIONS(stack, lock_stack, sguard); + +static inline void +reiser4_wake_up(lock_stack * owner) +{ + spin_lock_stack(owner); + __reiser4_wake_up(owner); + spin_unlock_stack(owner); +} + +const char *lock_mode_name(znode_lock_mode lock); + +#if REISER4_DEBUG +extern void check_lock_data(void); +extern void check_lock_node_data(znode * node); +#else +#define check_lock_data() noop +#define check_lock_node_data() noop +#endif + +/* __LOCK_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/log.c 2004-09-03 00:57:06.723017208 -0700 @@ -0,0 +1,519 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Tree-tracing facility. Copied from reiserfs v3.x patch, never released */ + +/* + * Tree-tracing is enabled by REISER4_EVENT_LOG compile option, and + * log_file= mount option. + * + * File at is opened (created if needed) and filled with log records + * while file system is mounted. + * + * Special path /dev/null disables logging. + * + * + * Special path /dev/console is interpreted as outputting log records through + * printk(). + * + * Low-level functions to output log record are write_log() and + * write_log_raw(). Various macros defined in log.h are used as wrappers. + * + * Output to log file is buffered to reduce overhead, but as target file + * system (one where log file lives) also buffers data in memory, tracing + * can distort file system behavior significantly. It has been experimentally + * found that optimal was to log is by using log_file= and piping + * log records to another host (through netcat(1) or similar, for example). + * + */ + +#include "forward.h" +#include "debug.h" +#include "key.h" +#include "log.h" +#include "super.h" +#include "inode.h" +#include "page_cache.h" /* for jprivate() */ + +#include +#include +#include /* for struct super_block */ +#include +#include +#include +#include + +#if REISER4_LOG + +static int log_flush(reiser4_log_file * log); +static int free_space(reiser4_log_file * log, size_t * len); +static int lock_log(reiser4_log_file * log); +static void unlock_log(reiser4_log_file * log); + +/* helper macro: lock log file, return with error if locking failed. */ +#define LOCK_OR_FAIL( log ) \ +({ \ + int __result; \ + \ + __result = lock_log( log ); \ + if( __result != 0 ) \ + return __result; \ +}) + +/* open log file. This is called by mount, when log_file= option is + * used. */ +int +open_log_file(struct super_block *super, + const char *file_name, + size_t size, + reiser4_log_file * log) +{ + int gfp_mask; + + assert("nikita-2498", file_name != NULL); + assert("nikita-2499", log != NULL); + assert("nikita-2500", size > 0); + + xmemset(log, 0, sizeof *log); + + spin_lock_init(&log->lock); + INIT_LIST_HEAD(&log->wait); + + /* special case: disable logging */ + if (!strcmp(file_name, "/dev/null")) { + log->type = log_to_bucket; + return 0; + } + log->buf = vmalloc(size); + if (log->buf == NULL) + return RETERR(-ENOMEM); + log->size = size; + + /* special case: log through printk() */ + if (!strcmp(file_name, "/dev/console")) { + log->type = log_to_console; + return 0; + } + log->fd = filp_open(file_name, O_CREAT | O_WRONLY, S_IFREG | S_IWUSR); + if (IS_ERR(log->fd)) { + warning("nikita-2501", "cannot open log file '%s': %li", file_name, PTR_ERR(log->fd)); + log->fd = NULL; + return PTR_ERR(log->fd); + } + if (log->fd->f_dentry->d_inode->i_sb == super) { + warning("nikita-2506", "Refusing to log onto logd fs"); + return RETERR(-EINVAL); + } + log->fd->f_dentry->d_inode->i_flags |= S_NOATIME; + log->fd->f_flags |= O_APPEND; + + /* avoid complications with calling memory allocator by ->write() + * method of target file system, but setting GFP_NOFS bit in + * mapping->gfp_mask */ + gfp_mask = mapping_gfp_mask(log->fd->f_dentry->d_inode->i_mapping); + gfp_mask &= ~__GFP_FS; + gfp_mask |= GFP_NOFS; + mapping_set_gfp_mask(log->fd->f_dentry->d_inode->i_mapping, gfp_mask); + log->type = log_to_file; + return 0; +} + +/* write message (formatted according to @format) into log file @file */ +int +write_log(reiser4_log_file * file, const char *format, ...) +{ + size_t len; + int result; + va_list args; + + if (file == NULL || file->type == log_to_bucket || + file->buf == NULL || file->disabled > 0) + return 0; + + va_start(args, format); + len = vsnprintf((char *) format, 0, format, args) + 1; + va_end(args); + + LOCK_OR_FAIL(file); + result = free_space(file, &len); + if (result == 0) { + va_start(args, format); + file->used += vsnprintf(file->buf + file->used, + file->size - file->used, format, args); + va_end(args); + } + unlock_log(file); + return result; +} + +/* write buffer @data into @file */ +int +write_log_raw(reiser4_log_file * file, const void *data, size_t len) +{ + int result; + + if (file == NULL || file->type == log_to_bucket || + file->buf == NULL || file->disabled > 0) + return 0; + + LOCK_OR_FAIL(file); + result = free_space(file, &len); + if (result == 0) { + xmemcpy(file->buf + file->used, data, (size_t) len); + file->used += len; + } + unlock_log(file); + return result; +} + +/* close log file. This is called by umount. */ +void +close_log_file(reiser4_log_file * log) +{ + if (log->type == log_to_file && lock_log(log) == 0) { + log_flush(log); + unlock_log(log); + } + if (log->fd != NULL) + filp_close(log->fd, NULL); + if (log->buf != NULL) { + vfree(log->buf); + log->buf = NULL; + } +} + +/* temporary suspend (or resume) tracing */ +int +hold_log(reiser4_log_file * file, int flag) +{ + if (flag) + return lock_log(file); + else { + unlock_log(file); + return 0; + } +} + +/* disable or enable tracing */ +int +disable_log(reiser4_log_file * file, int flag) +{ + LOCK_OR_FAIL(file); + file->disabled += flag ? +1 : -1; + unlock_log(file); + return 0; +} + +#define START_KERNEL_IO \ + { \ + mm_segment_t __ski_old_fs; \ + \ + __ski_old_fs = get_fs(); \ + set_fs( KERNEL_DS ) + +#define END_KERNEL_IO \ + set_fs( __ski_old_fs ); \ + } + +struct __wlink { + struct list_head link; + struct semaphore sema; +}; + +/* lock log file for exclusive use */ +static int +lock_log(reiser4_log_file * log) +{ + int ret = 0; + + spin_lock(&log->lock); + + while (log->long_term) { + /* sleep on a semaphore */ + struct __wlink link; + sema_init(&link.sema, 0); + list_add(&link.link, &log->wait); + spin_unlock(&log->lock); + + ret = down_interruptible(&link.sema); + + spin_lock(&log->lock); + list_del(&link.link); + } + + return ret; +} + +/* unlock log file */ +static void +unlock_log(reiser4_log_file * log) +{ + spin_unlock(&log->lock); +} + +static void convert_to_longterm (reiser4_log_file * log) +{ + assert ("zam-833", log->long_term == 0); + log->long_term = 1; + spin_unlock(&log->lock); +} + +static void convert_to_shortterm (reiser4_log_file * log) +{ + struct list_head * pos; + + spin_lock(&log->lock); + assert ("zam-834", log->long_term); + log->long_term = 0; + list_for_each(pos, &log->wait) { + struct __wlink * link; + link = list_entry(pos, struct __wlink, link); + up(&link->sema); + } +} + +/* + * flush content of the file->buf to the logging target. Free space in buffer. + */ +static int +log_flush(reiser4_log_file * file) +{ + int result; + + result = 0; + switch (file->type) { + case log_to_file:{ + struct file *fd; + + convert_to_longterm(file); + + /* + * if logging to the file, call vfs_write() until all data are + * written + */ + + fd = file->fd; + if (fd && fd->f_op != NULL && fd->f_op->write != NULL) { + int written; + + written = 0; + START_KERNEL_IO; + while (file->used > 0) { + result = vfs_write(fd, file->buf + written, + file->used, &fd->f_pos); + if (result > 0) { + file->used -= result; + written += result; + } else { + static int log_io_failed = 0; + + if (IS_POW(log_io_failed)) + warning("nikita-2502", + "Error writing log: %i", + result); + ++ log_io_failed; + break; + } + } + END_KERNEL_IO; + } else { + warning("nikita-2504", "no ->write() in log-file"); + result = RETERR(-EINVAL); + } + + convert_to_shortterm(file); + + break; + } + default: + warning("nikita-2505", + "unknown log-file type: %i. Dumping to console", + file->type); + case log_to_console: + if (file->buf != NULL) + printk(file->buf); + case log_to_bucket: + file->used = 0; + break; + } + + return result; +} + +/* + * free *@len bytes in the file->buf + */ +static int +free_space(reiser4_log_file * file, size_t * len) +{ + if (*len > file->size) { + warning("nikita-2503", + "log record too large: %i > %i. Truncating", + *len, file->size); + *len = file->size; + } + while (*len > file->size - file->used) { + int result; + + /* flushing can sleep, so loop */ + result = log_flush(file); + if (result < 0) + return result; + } + return 0; +} + +/* + * log tree operation @op on the @tree. + */ +void +write_tree_log(reiser4_tree * tree, reiser4_log_op op, ...) +{ + va_list args; + char buf[200]; + char *rest; + reiser4_key *key; + + if (unlikely(in_interrupt() || in_irq())) { + printk("cannot write log from interrupt\n"); + return; + } + + /* + * For each operation arguments are provided by the caller. Number and + * type of arguments depends on operation type. Use va_args to extract + * them. + */ + + /* + * tree_cut: opcode, key_from, key_to + * + * tree_lookup: opcode, key + * + * tree_insert: opcode, item_data, coord, flags + * + * tree_paste: opcode, item_data, coord, flags + * + * tree_cached: opcode + * + * tree_exit: opcode + * + */ + va_start(args, op); + + rest = buf; + rest += sprintf(rest, "....tree %c ", op); + + if (op != tree_cached && op != tree_exit) { + key = va_arg(args, reiser4_key *); + rest += sprintf_key(rest, key); + *rest++ = ' '; + *rest = '\0'; + + switch (op) { + case tree_cut: { + reiser4_key *to; + + to = va_arg(args, reiser4_key *); + rest += sprintf_key(rest, to); + break; + } + case tree_lookup: + default: + break; + case tree_insert: + case tree_paste: { + reiser4_item_data *data; + coord_t *coord; + __u32 flags; + + data = va_arg(args, reiser4_item_data *); + coord = va_arg(args, coord_t *); + flags = va_arg(args, __u32); + + rest += sprintf(rest, "%s (%u,%u) %x", + data->iplug->h.label, + coord->item_pos, coord->unit_pos, flags); + } + } + } + va_end(args); + write_current_logf(WRITE_TREE_LOG, "%s", buf); +} + +/* construct in @buf jnode description to be output in the log */ +char * +jnode_short_info(const jnode *j, char *buf) +{ + if (j == NULL) { + sprintf(buf, "null"); + } else { + sprintf(buf, "%i %c %c %i", + jnode_get_level(j), + jnode_is_znode(j) ? 'Z' : + jnode_is_unformatted(j) ? 'J' : '?', + JF_ISSET(j, JNODE_OVRWR) ? 'O' : + JF_ISSET(j, JNODE_RELOC) ? 'R' : ' ', + j->atom ? j->atom->atom_id : -1); + } + return buf; +} + + +/* write jnode description in the log */ +void +write_node_log(const jnode *node) +{ + char jbuf[100]; + + jnode_short_info(node, jbuf); + write_current_logf(WRITE_NODE_LOG, ".....node %s %s", + sprint_address(jnode_get_block(node)), jbuf); +} + +/* write page description in the log */ +void +write_page_log(const struct address_space *mapping, unsigned long index) +{ + write_current_logf(WRITE_PAGE_LOG, ".....page %llu %lu", get_inode_oid(mapping->host), + index); +} + +/* write block IO request description in the log */ +void +write_io_log(const char *moniker, int rw, struct bio *bio) +{ + struct super_block *super; + reiser4_super_info_data *sbinfo; + reiser4_block_nr start; + char jbuf[100]; + + /* + * sbinfo->last_touched is last block where IO was issued to. It is + * used to output seek distance into log. + */ + + super = reiser4_get_current_sb(); + sbinfo = get_super_private(super); + + start = bio->bi_sector >> (super->s_blocksize_bits - 9); + jnode_short_info(jprivate(bio->bi_io_vec[0].bv_page), jbuf); + write_current_logf(WRITE_IO_LOG, "......bio %s %c %+lli (%llu,%u) %s", + moniker, (rw == READ) ? 'r' : 'w', + start - sbinfo->last_touched - 1, + start, bio->bi_vcnt, jbuf); + sbinfo->last_touched = start + bio->bi_vcnt - 1; +} + +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/log.h 2004-09-03 00:57:05.120260864 -0700 @@ -0,0 +1,122 @@ +/* Copyright 2000, 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Tree-tracing facility. Copied from reiserfs v3.x patch, never released. See + * log.c for comments. */ + +#if !defined( __REISER4_LOG_H__ ) +#define __REISER4_LOG_H__ + +#include "forward.h" +#include "debug.h" + +#include +#include /* for struct super_block, etc */ +#include + +/* + * Log targets + */ +typedef enum { + log_to_file, /* file */ + log_to_console /* printk */, + log_to_bucket /* nowhere */ +} log_file_type; + +#if REISER4_LOG + +/* + * data structure describing log file. + */ +typedef struct { + log_file_type type; /* type of log file */ + struct file *fd; /* actual file */ + char *buf; /* logging buffer where records are + * accumulated */ + size_t size; /* buffer size */ + size_t used; /* bytes used in the buffer */ + spinlock_t lock; /* spinlock protecting this structure */ + struct list_head wait; /* threads waiting for the free space in the + * buffer */ + int disabled; /* if > 0, logging is temporarily disabled */ + int long_term; /* if != 0, then ->wait is used for + * synchronization, otherwise--- ->lock.*/ +} reiser4_log_file; + +/* + * markers for tree operations logged. Self-describing. + */ +typedef enum { + tree_cut = 'c', + tree_lookup = 'l', + tree_insert = 'i', + tree_paste = 'p', + tree_cached = 'C', + tree_exit = 'e' +} reiser4_log_op; + +extern int open_log_file(struct super_block *super, const char *file_name, size_t size, reiser4_log_file * log); +extern int write_log(reiser4_log_file * file, const char *format, ...) + __attribute__ ((format(printf, 2, 3))); + +extern int write_log_raw(reiser4_log_file * file, const void *data, size_t len); +extern int hold_log(reiser4_log_file * file, int flag); +extern int disable_log(reiser4_log_file * file, int flag); +extern void close_log_file(reiser4_log_file * file); + +#define write_syscall_log(format, ...) \ + write_current_logf(WRITE_SYSCALL_LOG, "%s "format, __FUNCTION__ , ## __VA_ARGS__) +extern void write_node_log(const jnode *node); +struct address_space; +extern void write_page_log(const struct address_space *mapping, + unsigned long index); +extern void write_io_log(const char *moniker, int rw, struct bio *bio); +extern void write_tree_log(reiser4_tree * tree, reiser4_log_op op, ...); + +extern char *jnode_short_info(const jnode *j, char *buf); + + +#else /* NO LOG */ + +typedef struct { +} reiser4_log_file; + +#define open_log_file(super, file_name, size, log) (0) +#define write_log(file, format, ...) (0) +#define write_log_raw(file, data, len) (0) +#define hold_log(file, flag) (0) +#define disable_log(file, flag) (0) +#define close_log_file(file) noop + +#define write_syscall_log(format, ...) noop +#define write_tree_log(tree, op, ...) noop +#define write_node_log(node) noop +#define write_page_log(mapping, index) noop +#define jnode_short_info(j, buf) buf + +#endif + +#define write_current_logf(log_flag, format, ...) \ +({ \ + struct super_block *super; \ + \ + super = reiser4_get_current_sb(); \ + IF_LOG(log_flag, write_log(&get_super_private(super)->log_file, \ + "%s %s %s " format "\n", \ + current->comm, \ + super->s_id, __FUNCTION__ , ## __VA_ARGS__)); \ +}) + +/* __REISER4_LOG_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/Makefile 2004-09-03 00:57:05.121260712 -0700 @@ -0,0 +1,166 @@ +# +# reiser4/Makefile +# + +obj-$(CONFIG_REISER4_FS) += reiser4.o + +EXTRA_CFLAGS += \ + -Wformat \ + -Wundef \ + -Wunused \ + -Wcomment \ + \ + -Wno-nested-externs \ + -Wno-write-strings \ + -Wno-sign-compare + +# -Wpointer-arith \ +# -Wlarger-than-16384 \ +# -Winline \ + +ifeq ($(CONFIG_REISER4_NOOPT),y) + EXTRA_CFLAGS += -O0 -fno-inline +else +# this warning is only supported when optimization is on. + EXTRA_CFLAGS += \ + -Wuninitialized +endif + +ifeq ($(CONFIG_REISER4_ALL_IN_ONE),y) + +reiser4-objs := all-reiser4.o + +else + +reiser4-objs := \ + debug.o \ + stats.o \ + jnode.o \ + znode.o \ + key.o \ + pool.o \ + tree_mod.o \ + estimate.o \ + carry.o \ + carry_ops.o \ + lock.o \ + tree.o \ + context.o \ + tap.o \ + coord.o \ + block_alloc.o \ + txnmgr.o \ + kassign.o \ + flush.o \ + wander.o \ + eottl.o \ + search.o \ + page_cache.o \ + lnode.o \ + kcond.o \ + seal.o \ + dscale.o \ + log.o \ + flush_queue.o \ + ktxnmgrd.o \ + kattr.o \ + blocknrset.o \ + super.o \ + oid.o \ + tree_walk.o \ + inode.o \ + vfs_ops.o \ + inode_ops.o \ + file_ops.o \ + as_ops.o \ + emergency_flush.o \ + spinprof.o\ + entd.o\ + readahead.o \ + crypt.o \ + diskmap.o \ + prof.o \ + repacker.o \ + status_flags.o \ + init_super.o \ + safe_link.o \ + \ + plugin/plugin.o \ + plugin/plugin_set.o \ + plugin/node/node.o \ + plugin/object.o \ + plugin/symlink.o \ + plugin/cryptcompress.o \ + plugin/digest.o \ + plugin/node/node40.o \ + \ + plugin/compress/minilzo.o \ + plugin/compress/compress.o \ + \ + plugin/item/static_stat.o \ + plugin/item/sde.o \ + plugin/item/cde.o \ + plugin/item/blackbox.o \ + plugin/item/internal.o \ + plugin/item/tail.o \ + plugin/item/ctail.o \ + plugin/item/extent.o \ + plugin/item/extent_item_ops.o \ + plugin/item/extent_file_ops.o \ + plugin/item/extent_flush_ops.o \ + plugin/item/extent_repack_ops.o \ + \ + plugin/hash.o \ + plugin/fibration.o \ + plugin/tail_policy.o \ + plugin/item/item.o \ + \ + plugin/dir/hashed_dir.o \ + plugin/dir/pseudo_dir.o \ + plugin/dir/dir.o \ + \ + plugin/security/perm.o \ + \ + plugin/pseudo/pseudo.o \ + \ + plugin/space/bitmap.o \ + \ + plugin/disk_format/disk_format40.o \ + plugin/disk_format/disk_format.o \ + \ + plugin/file/pseudo.o \ + plugin/file/file.o \ + plugin/file/tail_conversion.o + +reiser4-objs += sys_reiser4.o + +ifeq ($(CONFIG_REISER4_FS_SYSCALL),y) + + ifeq ($(CONFIG_REISER4_FS_SYSCALL_YACC),y) + + YFLAGS= -d -v -r -b $(obj)/parser/parser + + $(obj)/parser/parser.code.c: $(obj)/parser/parser.y + + $(YACC) $(YFLAGS) $(obj)/parser/parser.y + + endif + + sys_reiser4.o: $/sys_reiser4.c \ + $/parser/parser.code.c \ + $/parser/parser.tab.c \ + $/parser/parser.tab.h \ + $/parser/lib.c \ + $/parser/pars.cls.h \ + $/parser/pars.yacc.h \ + $/parser/parser.h + + +# $(MAKE) $(obj)/parser/parser +#clean-files := parser/parser.code.c +##clean-rule =@$(MAKE) -C $/parser clean +#clean-rule =@$(MAKE) $(obj)/parser/parser.code.c +endif + +endif + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/oid.c 2004-09-03 00:57:05.122260560 -0700 @@ -0,0 +1,166 @@ +/* Copyright 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#include "debug.h" +#include "super.h" +#include "txnmgr.h" + +/* we used to have oid allocation plugin. It was removed because it + was recognized as providing unneeded level of abstraction. If one + ever will find it useful - look at yet_unneeded_abstractions/oid +*/ + +/* + * initialize in-memory data for oid allocator at @super. @nr_files and @next + * are provided by disk format plugin that reads them from the disk during + * mount. + */ +reiser4_internal int +oid_init_allocator(struct super_block *super, oid_t nr_files, oid_t next) +{ + reiser4_super_info_data *sbinfo; + + sbinfo = get_super_private(super); + + sbinfo->next_to_use = next; + sbinfo->oids_in_use = nr_files; + return 0; +} + +/* + * allocate oid and return it. ABSOLUTE_MAX_OID is returned when allocator + * runs out of oids. + */ +reiser4_internal oid_t +oid_allocate(struct super_block *super) +{ + reiser4_super_info_data *sbinfo; + oid_t oid; + + sbinfo = get_super_private(super); + + reiser4_spin_lock_sb(sbinfo); + if (sbinfo->next_to_use != ABSOLUTE_MAX_OID) { + oid = sbinfo->next_to_use ++; + sbinfo->oids_in_use ++; + } else + oid = ABSOLUTE_MAX_OID; + reiser4_spin_unlock_sb(sbinfo); + return oid; +} + +/* + * Tell oid allocator that @oid is now free. + */ +reiser4_internal int +oid_release(struct super_block *super, oid_t oid UNUSED_ARG) +{ + reiser4_super_info_data *sbinfo; + + sbinfo = get_super_private(super); + + reiser4_spin_lock_sb(sbinfo); + sbinfo->oids_in_use --; + reiser4_spin_unlock_sb(sbinfo); + return 0; +} + +/* + * return next @oid that would be allocated (i.e., returned by oid_allocate()) + * without actually allocating it. This is used by disk format plugin to save + * oid allocator state on the disk. + */ +reiser4_internal oid_t oid_next(const struct super_block *super) +{ + reiser4_super_info_data *sbinfo; + oid_t oid; + + sbinfo = get_super_private(super); + + reiser4_spin_lock_sb(sbinfo); + oid = sbinfo->next_to_use; + reiser4_spin_unlock_sb(sbinfo); + return oid; +} + +/* + * returns number of currently used oids. This is used by statfs(2) to report + * number of "inodes" and by disk format plugin to save oid allocator state on + * the disk. + */ +reiser4_internal long oids_used(const struct super_block *super) +{ + reiser4_super_info_data *sbinfo; + oid_t used; + + sbinfo = get_super_private(super); + + reiser4_spin_lock_sb(sbinfo); + used = sbinfo->oids_in_use; + reiser4_spin_unlock_sb(sbinfo); + if (used < (__u64) ((long) ~0) >> 1) + return (long) used; + else + return (long) -1; +} + +/* + * return number of "free" oids. This is used by statfs(2) to report "free" + * inodes. + */ +reiser4_internal long oids_free(const struct super_block *super) +{ + reiser4_super_info_data *sbinfo; + oid_t oids; + + sbinfo = get_super_private(super); + + reiser4_spin_lock_sb(sbinfo); + oids = ABSOLUTE_MAX_OID - OIDS_RESERVED - sbinfo->next_to_use; + reiser4_spin_unlock_sb(sbinfo); + if (oids < (__u64) ((long) ~0) >> 1) + return (long) oids; + else + return (long) -1; +} + +/* + * Count oid as allocated in atom. This is done after call to oid_allocate() + * at the point when we are irrevocably committed to creation of the new file + * (i.e., when oid allocation cannot be any longer rolled back due to some + * error). + */ +reiser4_internal void +oid_count_allocated(void) +{ + txn_atom *atom; + + atom = get_current_atom_locked(); + atom->nr_objects_created++; + UNLOCK_ATOM(atom); +} + +/* + * Count oid as free in atom. This is done after call to oid_release() at the + * point when we are irrevocably committed to the deletion of the file (i.e., + * when oid release cannot be any longer rolled back due to some error). + */ +reiser4_internal void +oid_count_released(void) +{ + txn_atom *atom; + + atom = get_current_atom_locked(); + atom->nr_objects_deleted++; + UNLOCK_ATOM(atom); +} + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/page_cache.c 2004-09-03 00:57:07.243938016 -0700 @@ -0,0 +1,917 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Memory pressure hooks. Fake inodes handling. */ +/* We store all file system meta data (and data, of course) in the page cache. + + What does this mean? In stead of using bread/brelse we create special + "fake" inode (one per super block) and store content of formatted nodes + into pages bound to this inode in the page cache. In newer kernels bread() + already uses inode attached to block device (bd_inode). Advantage of having + our own fake inode is that we can install appropriate methods in its + address_space operations. Such methods are called by VM on memory pressure + (or during background page flushing) and we can use them to react + appropriately. + + In initial version we only support one block per page. Support for multiple + blocks per page is complicated by relocation. + + To each page, used by reiser4, jnode is attached. jnode is analogous to + buffer head. Difference is that jnode is bound to the page permanently: + jnode cannot be removed from memory until its backing page is. + + jnode contain pointer to page (->pg field) and page contain pointer to + jnode in ->private field. Pointer from jnode to page is protected to by + jnode's spinlock and pointer from page to jnode is protected by page lock + (PG_locked bit). Lock ordering is: first take page lock, then jnode spin + lock. To go into reverse direction use jnode_lock_page() function that uses + standard try-lock-and-release device. + + Properties: + + 1. when jnode-to-page mapping is established (by jnode_attach_page()), page + reference counter is increased. + + 2. when jnode-to-page mapping is destroyed (by jnode_detach_page() and + page_detach_jnode()), page reference counter is decreased. + + 3. on jload() reference counter on jnode page is increased, page is + kmapped and `referenced'. + + 4. on jrelse() inverse operations are performed. + + 5. kmapping/kunmapping of unformatted pages is done by read/write methods. + + + DEADLOCKS RELATED TO MEMORY PRESSURE. [OUTDATED. Only interesting + historically.] + + [In the following discussion, `lock' invariably means long term lock on + znode.] (What about page locks?) + + There is some special class of deadlock possibilities related to memory + pressure. Locks acquired by other reiser4 threads are accounted for in + deadlock prevention mechanism (lock.c), but when ->vm_writeback() is + invoked additional hidden arc is added to the locking graph: thread that + tries to allocate memory waits for ->vm_writeback() to finish. If this + thread keeps lock and ->vm_writeback() tries to acquire this lock, deadlock + prevention is useless. + + Another related problem is possibility for ->vm_writeback() to run out of + memory itself. This is not a problem for ext2 and friends, because their + ->vm_writeback() don't allocate much memory, but reiser4 flush is + definitely able to allocate huge amounts of memory. + + It seems that there is no reliable way to cope with the problems above. In + stead it was decided that ->vm_writeback() (as invoked in the kswapd + context) wouldn't perform any flushing itself, but rather should just wake + up some auxiliary thread dedicated for this purpose (or, the same thread + that does periodic commit of old atoms (ktxnmgrd.c)). + + Details: + + 1. Page is called `reclaimable' against particular reiser4 mount F if this + page can be ultimately released by try_to_free_pages() under presumptions + that: + + a. ->vm_writeback() for F is no-op, and + + b. none of the threads accessing F are making any progress, and + + c. other reiser4 mounts obey the same memory reservation protocol as F + (described below). + + For example, clean un-pinned page, or page occupied by ext2 data are + reclaimable against any reiser4 mount. + + When there is more than one reiser4 mount in a system, condition (c) makes + reclaim-ability not easily verifiable beyond trivial cases mentioned above. + + + + + + + + + THIS COMMENT IS VALID FOR "MANY BLOCKS ON PAGE" CASE + + Fake inode is used to bound formatted nodes and each node is indexed within + fake inode by its block number. If block size of smaller than page size, it + may so happen that block mapped to the page with formatted node is occupied + by unformatted node or is unallocated. This lead to some complications, + because flushing whole page can lead to an incorrect overwrite of + unformatted node that is moreover, can be cached in some other place as + part of the file body. To avoid this, buffers for unformatted nodes are + never marked dirty. Also pages in the fake are never marked dirty. This + rules out usage of ->writepage() as memory pressure hook. In stead + ->releasepage() is used. + + Josh is concerned that page->buffer is going to die. This should not pose + significant problem though, because we need to add some data structures to + the page anyway (jnode) and all necessary book keeping can be put there. + +*/ + +/* Life cycle of pages/nodes. + + jnode contains reference to page and page contains reference back to + jnode. This reference is counted in page ->count. Thus, page bound to jnode + cannot be released back into free pool. + + 1. Formatted nodes. + + 1. formatted node is represented by znode. When new znode is created its + ->pg pointer is NULL initially. + + 2. when node content is loaded into znode (by call to zload()) for the + first time following happens (in call to ->read_node() or + ->allocate_node()): + + 1. new page is added to the page cache. + + 2. this page is attached to znode and its ->count is increased. + + 3. page is kmapped. + + 3. if more calls to zload() follow (without corresponding zrelses), page + counter is left intact and in its stead ->d_count is increased in znode. + + 4. each call to zrelse decreases ->d_count. When ->d_count drops to zero + ->release_node() is called and page is kunmapped as result. + + 5. at some moment node can be captured by a transaction. Its ->x_count + is then increased by transaction manager. + + 6. if node is removed from the tree (empty node with JNODE_HEARD_BANSHEE + bit set) following will happen (also see comment at the top of znode.c): + + 1. when last lock is released, node will be uncaptured from + transaction. This released reference that transaction manager acquired + at the step 5. + + 2. when last reference is released, zput() detects that node is + actually deleted and calls ->delete_node() + operation. page_cache_delete_node() implementation detaches jnode from + page and releases page. + + 7. otherwise (node wasn't removed from the tree), last reference to + znode will be released after transaction manager committed transaction + node was in. This implies squallocing of this node (see + flush.c). Nothing special happens at this point. Znode is still in the + hash table and page is still attached to it. + + 8. znode is actually removed from the memory because of the memory + pressure, or during umount (znodes_tree_done()). Anyway, znode is + removed by the call to zdrop(). At this moment, page is detached from + znode and removed from the inode address space. + +*/ + +#include "debug.h" +#include "dformat.h" +#include "key.h" +#include "txnmgr.h" +#include "jnode.h" +#include "znode.h" +#include "block_alloc.h" +#include "tree.h" +#include "vfs_ops.h" +#include "inode.h" +#include "super.h" +#include "entd.h" +#include "page_cache.h" +#include "ktxnmgrd.h" + +#include +#include +#include /* for struct page */ +#include /* for struct page */ +#include +#include +#include +#include + +static struct bio *page_bio(struct page *page, jnode * node, int rw, int gfp); + +static struct address_space_operations formatted_fake_as_ops; + +static const oid_t fake_ino = 0x1; +static const oid_t bitmap_ino = 0x2; +static const oid_t cc_ino = 0x3; + +/* one-time initialization of fake inodes handling functions. */ +reiser4_internal int +init_fakes() +{ + return 0; +} + +static void +init_fake_inode(struct super_block *super, struct inode *fake, struct inode **pfake) +{ + assert("nikita-2168", fake->i_state & I_NEW); + fake->i_mapping->a_ops = &formatted_fake_as_ops; + fake->i_blkbits = super->s_blocksize_bits; + fake->i_size = ~0ull; + fake->i_rdev = super->s_bdev->bd_dev; + fake->i_bdev = super->s_bdev; + *pfake = fake; + /* NOTE-NIKITA something else? */ + unlock_new_inode(fake); +} + +/* initialize fake inode to which formatted nodes are bound in the page cache. */ +reiser4_internal int +init_formatted_fake(struct super_block *super) +{ + struct inode *fake; + struct inode *bitmap; + struct inode *cc; + reiser4_super_info_data *sinfo; + + assert("nikita-1703", super != NULL); + + sinfo = get_super_private_nocheck(super); + fake = iget_locked(super, oid_to_ino(fake_ino)); + + if (fake != NULL) { + init_fake_inode(super, fake, &sinfo->fake); + + bitmap = iget_locked(super, oid_to_ino(bitmap_ino)); + if (bitmap != NULL) { + init_fake_inode(super, bitmap, &sinfo->bitmap); + + cc = iget_locked(super, oid_to_ino(cc_ino)); + if (cc != NULL) { + init_fake_inode(super, cc, &sinfo->cc); + return 0; + } else { + iput(sinfo->fake); + iput(sinfo->bitmap); + sinfo->fake = NULL; + sinfo->bitmap = NULL; + } + } else { + iput(sinfo->fake); + sinfo->fake = NULL; + } + } + return RETERR(-ENOMEM); +} + +/* release fake inode for @super */ +reiser4_internal int +done_formatted_fake(struct super_block *super) +{ + reiser4_super_info_data *sinfo; + + sinfo = get_super_private_nocheck(super); + + if (sinfo->fake != NULL) { + assert("vs-1426", sinfo->fake->i_data.nrpages == 0); + iput(sinfo->fake); + sinfo->fake = NULL; + } + + if (sinfo->bitmap != NULL) { + iput(sinfo->bitmap); + sinfo->bitmap = NULL; + } + + if (sinfo->cc != NULL) { + iput(sinfo->cc); + sinfo->cc = NULL; + } + return 0; +} + +#if REISER4_LOG +int reiser4_submit_bio_helper(const char *moniker, int rw, struct bio *bio) +{ + write_io_log(moniker, rw, bio); + submit_bio(rw, bio); + return 0; +} +#endif + +reiser4_internal void reiser4_wait_page_writeback (struct page * page) +{ + assert ("zam-783", PageLocked(page)); + + do { + unlock_page(page); + wait_on_page_writeback(page); + lock_page(page); + } while (PageWriteback(page)); +} + +/* return tree @page is in */ +reiser4_internal reiser4_tree * +tree_by_page(const struct page *page /* page to query */ ) +{ + assert("nikita-2461", page != NULL); + return &get_super_private(page->mapping->host->i_sb)->tree; +} + +#if REISER4_DEBUG_MEMCPY + +/* Our own versions of memcpy, memmove, and memset used to profile shifts of + tree node content. Coded to avoid inlining. */ + +struct mem_ops_table { + void *(*cpy) (void *dest, const void *src, size_t n); + void *(*move) (void *dest, const void *src, size_t n); + void *(*set) (void *s, int c, size_t n); +}; + +void * +xxmemcpy(void *dest, const void *src, size_t n) +{ + return memcpy(dest, src, n); +} + +void * +xxmemmove(void *dest, const void *src, size_t n) +{ + return memmove(dest, src, n); +} + +void * +xxmemset(void *s, int c, size_t n) +{ + return memset(s, c, n); +} + +struct mem_ops_table std_mem_ops = { + .cpy = xxmemcpy, + .move = xxmemmove, + .set = xxmemset +}; + +struct mem_ops_table *mem_ops = &std_mem_ops; + +void * +xmemcpy(void *dest, const void *src, size_t n) +{ + return mem_ops->cpy(dest, src, n); +} + +void * +xmemmove(void *dest, const void *src, size_t n) +{ + return mem_ops->move(dest, src, n); +} + +void * +xmemset(void *s, int c, size_t n) +{ + return mem_ops->set(s, c, n); +} + +#endif + +/* completion handler for single page bio-based read. + + mpage_end_io_read() would also do. But it's static. + +*/ +static int +end_bio_single_page_read(struct bio *bio, unsigned int bytes_done UNUSED_ARG, int err UNUSED_ARG) +{ + struct page *page; + + if (bio->bi_size != 0) { + warning("nikita-3332", "Truncated single page read: %i", + bio->bi_size); + return 1; + } + + page = bio->bi_io_vec[0].bv_page; + + if (test_bit(BIO_UPTODATE, &bio->bi_flags)) + SetPageUptodate(page); + else { + ClearPageUptodate(page); + SetPageError(page); + } + unlock_page(page); + bio_put(bio); + return 0; +} + +/* completion handler for single page bio-based write. + + mpage_end_io_write() would also do. But it's static. + +*/ +static int +end_bio_single_page_write(struct bio *bio, unsigned int bytes_done UNUSED_ARG, int err UNUSED_ARG) +{ + struct page *page; + + if (bio->bi_size != 0) { + warning("nikita-3333", "Truncated single page write: %i", + bio->bi_size); + return 1; + } + + page = bio->bi_io_vec[0].bv_page; + + if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) + SetPageError(page); + end_page_writeback(page); + bio_put(bio); + return 0; +} + +/* ->readpage() method for formatted nodes */ +static int +formatted_readpage(struct file *f UNUSED_ARG, struct page *page /* page to read */ ) +{ + assert("nikita-2412", PagePrivate(page) && jprivate(page)); + return page_io(page, jprivate(page), READ, GFP_KERNEL); +} + +/* submit single-page bio request */ +reiser4_internal int +page_io(struct page *page /* page to perform io for */ , + jnode * node /* jnode of page */ , + int rw /* read or write */ , int gfp /* GFP mask */ ) +{ + struct bio *bio; + int result; + + assert("nikita-2094", page != NULL); + assert("nikita-2226", PageLocked(page)); + assert("nikita-2634", node != NULL); + assert("nikita-2893", rw == READ || rw == WRITE); + + if (rw) { + if (unlikely(page->mapping->host->i_sb->s_flags & MS_RDONLY)) { + unlock_page(page); + return 0; + } + } + + bio = page_bio(page, node, rw, gfp); + if (!IS_ERR(bio)) { + if (rw == WRITE) { + SetPageWriteback(page); + unlock_page(page); + } + reiser4_submit_bio(rw, bio); + result = 0; + } else { + unlock_page(page); + result = PTR_ERR(bio); + } + + return result; +} + +/* helper function to construct bio for page */ +static struct bio * +page_bio(struct page *page, jnode * node, int rw, int gfp) +{ + struct bio *bio; + assert("nikita-2092", page != NULL); + assert("nikita-2633", node != NULL); + + /* Simple implementation in the assumption that blocksize == pagesize. + + We only have to submit one block, but submit_bh() will allocate bio + anyway, so lets use all the bells-and-whistles of bio code. + */ + + bio = bio_alloc(gfp, 1); + if (bio != NULL) { + int blksz; + struct super_block *super; + reiser4_block_nr blocknr; + + super = page->mapping->host->i_sb; + assert("nikita-2029", super != NULL); + blksz = super->s_blocksize; + assert("nikita-2028", blksz == (int) PAGE_CACHE_SIZE); + + blocknr = *UNDER_SPIN(jnode, node, jnode_get_io_block(node)); + + assert("nikita-2275", blocknr != (reiser4_block_nr) 0); + assert("nikita-2276", !blocknr_is_fake(&blocknr)); + + bio->bi_bdev = super->s_bdev; + /* fill bio->bi_sector before calling bio_add_page(), because + * q->merge_bvec_fn may want to inspect it (see + * drivers/md/linear.c:linear_mergeable_bvec() for example. */ + bio->bi_sector = blocknr * (blksz >> 9); + + if (!bio_add_page(bio, page, blksz, 0)) { + warning("nikita-3452", + "Single page bio cannot be constructed"); + return ERR_PTR(RETERR(-EINVAL)); + } + + /* bio -> bi_idx is filled by bio_init() */ + bio->bi_end_io = (rw == READ) ? + end_bio_single_page_read : end_bio_single_page_write; + + return bio; + } else + return ERR_PTR(RETERR(-ENOMEM)); +} + + +/* this function is internally called by jnode_make_dirty() */ +int set_page_dirty_internal (struct page * page, int tag_as_moved) +{ + if (REISER4_STATS && !PageDirty(page)) + reiser4_stat_inc(pages_dirty); + + /* the below resembles __set_page_dirty_nobuffers except that it also clears REISER4_MOVED page tag */ + if (!TestSetPageDirty(page)) { + struct address_space *mapping = page->mapping; + + if (mapping) { + read_lock_irq(&mapping->tree_lock); + if (page->mapping) { /* Race with truncate? */ + BUG_ON(page->mapping != mapping); + if (!mapping->backing_dev_info->memory_backed) + inc_page_state(nr_dirty); + radix_tree_tag_set(&mapping->page_tree, + page->index, PAGECACHE_TAG_DIRTY); + if (tag_as_moved) + radix_tree_tag_set( + &mapping->page_tree, page->index, + PAGECACHE_TAG_REISER4_MOVED); + else + radix_tree_tag_clear( + &mapping->page_tree, page->index, + PAGECACHE_TAG_REISER4_MOVED); + } + read_unlock_irq(&mapping->tree_lock); + __mark_inode_dirty(mapping->host, I_DIRTY_PAGES); + } + } + return 0; +} + +reiser4_internal void capture_reiser4_inodes ( + struct super_block * sb, struct writeback_control * wbc) +{ + const unsigned long start = jiffies; + long captured = 0; + + if (list_empty(&sb->s_io)) + list_splice_init(&sb->s_dirty, &sb->s_io); + + while (!list_empty(&sb->s_io)) { + struct inode *inode = list_entry( + sb->s_io.prev, struct inode, i_list); + + list_move(&inode->i_list, &sb->s_dirty); + + if (time_after(inode->dirtied_when, start)) + continue; + + __iget(inode); + spin_unlock(&inode_lock); + + { + file_plugin *fplug; + + fplug = inode_file_plugin(inode); + if (fplug != NULL && fplug->capture != NULL) { + /* call file plugin method to capture anonymous pages and + * anonymous jnodes */ + fplug->capture(inode, wbc, &captured); + } + } + + spin_lock(&inode_lock); + /* set inode state according what pages it has. */ + if (!(inode->i_state & I_FREEING)) { + struct address_space * mapping = inode->i_mapping; + unsigned long flags; + + read_lock_irqsave(&mapping->tree_lock, flags); + if (!radix_tree_tagged(&mapping->page_tree, PAGECACHE_TAG_DIRTY) && + !radix_tree_tagged(&mapping->page_tree, PAGECACHE_TAG_REISER4_MOVED)) + { + inode->i_state &= ~(I_DIRTY); + } + read_unlock_irqrestore(&mapping->tree_lock, flags); + } + spin_unlock(&inode_lock); + + iput(inode); + + spin_lock(&inode_lock); + if (wbc->nr_to_write <= 0) { + warning("vs-1689", "does this ever happen? nr_to_write = %ld", wbc->nr_to_write); + break; + } + } +} + + +/* Common memory pressure notification. */ +reiser4_internal int +reiser4_writepage(struct page *page /* page to start writeback from */, + struct writeback_control *wbc) +{ + struct super_block *s; + reiser4_context ctx; + reiser4_tree *tree; + txn_atom * atom; + jnode *node; + int result; + + s = page->mapping->host->i_sb; + init_context(&ctx, s); + + reiser4_stat_inc(pcwb.calls); + + assert("vs-828", PageLocked(page)); + +#if REISER4_USE_ENTD + + /* Throttle memory allocations if we were not in reiser4 */ + if (ctx.parent == &ctx) { + write_page_by_ent(page, wbc); + result = 1; + goto out; + } +#endif /* REISER4_USE_ENTD */ + + tree = &get_super_private(s)->tree; + node = jnode_of_page(page); + if (!IS_ERR(node)) { + int phantom; + + assert("nikita-2419", node != NULL); + + LOCK_JNODE(node); + /* + * page was dirty, but jnode is not. This is (only?) + * possible if page was modified through mmap(). We + * want to handle such jnodes specially. + */ + phantom = !jnode_is_dirty(node); + atom = jnode_get_atom(node); + if (atom != NULL) { + if (!(atom->flags & ATOM_FORCE_COMMIT)) { + atom->flags |= ATOM_FORCE_COMMIT; + ktxnmgrd_kick(&get_super_private(s)->tmgr); + reiser4_stat_inc(txnmgr.commit_from_writepage); + } + UNLOCK_ATOM(atom); + } + UNLOCK_JNODE(node); + + result = emergency_flush(page); + if (result != 0) { + /* + * cannot flush page right now, or some error + */ + reiser4_stat_inc(pcwb.not_written); + } else { + /* + * page was successfully flushed + */ + reiser4_stat_inc(pcwb.written); + if (phantom && jnode_is_unformatted(node)) + JF_SET(node, JNODE_KEEPME); + } + jput(node); + } else { + reiser4_stat_inc(pcwb.no_jnode); + result = PTR_ERR(node); + } + if (result != 0) { + /* + * shrink list doesn't move page to another mapping + * list when clearing dirty flag. So it is enough to + * just set dirty bit. + */ + set_page_dirty_internal(page, 0); + unlock_page(page); + } + out: + reiser4_exit_context(&ctx); + return result; +} + +/* ->set_page_dirty() method of formatted address_space */ +static int +formatted_set_page_dirty(struct page *page /* page to mark + * dirty */ ) +{ + assert("nikita-2173", page != NULL); + return __set_page_dirty_nobuffers(page); +} + +/* address space operations for the fake inode */ +static struct address_space_operations formatted_fake_as_ops = { + /* Perform a writeback of a single page as a memory-freeing + * operation. */ + .writepage = reiser4_writepage, + /* this is called to read formatted node */ + .readpage = formatted_readpage, + /* ->sync_page() method of fake inode address space operations. Called + from wait_on_page() and lock_page(). + + This is most annoyingly misnomered method. Actually it is called + from wait_on_page_bit() and lock_page() and its purpose is to + actually start io by jabbing device drivers. + */ + .sync_page = reiser4_start_up_io, + /* Write back some dirty pages from this mapping. Called from sync. + called during sync (pdflush) */ + .writepages = reiser4_writepages, + /* Set a page dirty */ + .set_page_dirty = formatted_set_page_dirty, + /* used for read-ahead. Not applicable */ + .readpages = NULL, + .prepare_write = NULL, + .commit_write = NULL, + .bmap = NULL, + /* called just before page is being detached from inode mapping and + removed from memory. Called on truncate, cut/squeeze, and + umount. */ + .invalidatepage = reiser4_invalidatepage, + /* this is called by shrink_cache() so that file system can try to + release objects (jnodes, buffers, journal heads) attached to page + and, may be made page itself free-able. + */ + .releasepage = reiser4_releasepage, + .direct_IO = NULL +}; + +/* called just before page is released (no longer used by reiser4). Callers: + jdelete() and extent2tail(). */ +reiser4_internal void +drop_page(struct page *page) +{ + assert("nikita-2181", PageLocked(page)); + clear_page_dirty(page); + ClearPageUptodate(page); +#if defined(PG_skipped) + ClearPageSkipped(page); +#endif + if (page->mapping != NULL) { + remove_from_page_cache(page); + unlock_page(page); + /* page removed from the mapping---decrement page counter */ + page_cache_release(page); + } else + unlock_page(page); +} + + +/* this is called by truncate_jnodes_range which in its turn is always called + after truncate_mapping_pages_range. Therefore, here jnode can not have + page. New pages can not be created because truncate_jnodes_range goes under + exclusive access on file obtained, where as new page creation requires + non-exclusive access obtained */ +static void +invalidate_unformatted(jnode *node) +{ + struct page *page; + + LOCK_JNODE(node); + page = node->pg; + if (page) { + loff_t from, to; + + page_cache_get(page); + UNLOCK_JNODE(node); + /* FIXME: use truncate_complete_page instead */ + from = (loff_t)page->index << PAGE_CACHE_SHIFT; + to = from + PAGE_CACHE_SIZE - 1; + truncate_inode_pages_range(page->mapping, from, to); + page_cache_release(page); + } else { + JF_SET(node, JNODE_HEARD_BANSHEE); + uncapture_jnode(node); + unhash_unformatted_jnode(node); + } +} + +#define JNODE_GANG_SIZE (16) + +/* find all eflushed jnodes from range specified and invalidate them */ +reiser4_internal int +truncate_jnodes_range(struct inode *inode, pgoff_t from, pgoff_t count) +{ + reiser4_inode *info; + int truncated_jnodes; + reiser4_tree *tree; + unsigned long index; + unsigned long end; + + truncated_jnodes = 0; + + info = reiser4_inode_data(inode); + tree = tree_by_inode(inode); + + index = from; + end = from + count; + + while (1) { + jnode *gang[JNODE_GANG_SIZE]; + int taken; + int i; + jnode *node; + + assert("nikita-3466", index <= end); + + RLOCK_TREE(tree); + taken = radix_tree_gang_lookup(jnode_tree_by_reiser4_inode(info), (void **)gang, + index, JNODE_GANG_SIZE); + for (i = 0; i < taken; ++i) { + node = gang[i]; + if (index_jnode(node) < end) + jref(node); + else + gang[i] = NULL; + } + RUNLOCK_TREE(tree); + + for (i = 0; i < taken; ++i) { + node = gang[i]; + if (node != NULL) { + index = max(index, index_jnode(node)); + invalidate_unformatted(node); + truncated_jnodes ++; + jput(node); + } else + break; + } + if (i != taken || taken == 0) + break; + } + return truncated_jnodes; +} + +reiser4_internal void +reiser4_invalidate_pages(struct address_space *mapping, pgoff_t from, unsigned long count) +{ + loff_t from_bytes, count_bytes; + + if (count == 0) + return; + from_bytes = ((loff_t)from) << PAGE_CACHE_SHIFT; + count_bytes = ((loff_t)count) << PAGE_CACHE_SHIFT; + + unmap_mapping_range(mapping, from_bytes, count_bytes, 1/*even cows*/); + truncate_inode_pages_range(mapping, from_bytes, from_bytes + count_bytes - 1); + truncate_jnodes_range(mapping->host, from, count); +} + + +#if REISER4_DEBUG_OUTPUT + +#define page_flag_name( page, flag ) \ + ( test_bit( ( flag ), &( page ) -> flags ) ? ((#flag "|")+3) : "" ) + +reiser4_internal void +print_page(const char *prefix, struct page *page) +{ + if (page == NULL) { + printk("null page\n"); + return; + } + printk("%s: page index: %lu mapping: %p count: %i private: %lx\n", + prefix, page->index, page->mapping, page_count(page), page->private); + printk("\tflags: %s%s%s%s %s%s%s %s%s%s%s %s%s%s\n", + page_flag_name(page, PG_locked), + page_flag_name(page, PG_error), + page_flag_name(page, PG_referenced), + page_flag_name(page, PG_uptodate), + page_flag_name(page, PG_dirty), + page_flag_name(page, PG_lru), + page_flag_name(page, PG_slab), + page_flag_name(page, PG_highmem), + page_flag_name(page, PG_checked), + page_flag_name(page, PG_arch_1), + page_flag_name(page, PG_reserved), + page_flag_name(page, PG_private), page_flag_name(page, PG_writeback), page_flag_name(page, PG_nosave)); + if (jprivate(page) != NULL) { + print_jnode("\tpage jnode", jprivate(page)); + printk("\n"); + } +} + +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/page_cache.h 2004-09-03 00:57:07.244937864 -0700 @@ -0,0 +1,69 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ +/* Memory pressure hooks. Fake inodes handling. See page_cache.c. */ + +#if !defined( __REISER4_PAGE_CACHE_H__ ) +#define __REISER4_PAGE_CACHE_H__ + +#include "forward.h" +#include "debug.h" + +#include /* for struct super_block, address_space */ +#include /* for struct page */ +#include /* for lock_page() */ + +extern int init_fakes(void); +extern int init_formatted_fake(struct super_block *super); +extern int done_formatted_fake(struct super_block *super); + +extern reiser4_tree *tree_by_page(const struct page *page); + +extern int set_page_dirty_internal (struct page * page, int tag_as_moved); + +#if REISER4_LOG +extern char *jnode_short_info(const jnode *j, char *buf); +extern int reiser4_submit_bio_helper(const char *moniker, + int rw, struct bio *bio); +#define reiser4_submit_bio(rw, bio) \ + reiser4_submit_bio_helper(__FUNCTION__, (rw), (bio)) +#else +#define reiser4_submit_bio(rw, bio) submit_bio((rw), (bio)) +#endif + +extern void reiser4_wait_page_writeback (struct page * page); +static inline void lock_and_wait_page_writeback (struct page * page) +{ + lock_page(page); + if (unlikely(PageWriteback(page))) + reiser4_wait_page_writeback(page); +} + +#define jprivate(page) ((jnode *) (page)->private) + +extern int page_io(struct page *page, jnode * node, int rw, int gfp); +extern int reiser4_writepage(struct page *page, struct writeback_control *wbc); +extern void drop_page(struct page *page); +extern void reiser4_invalidate_pages(struct address_space *, pgoff_t from, unsigned long count); +extern void capture_reiser4_inodes (struct super_block *, struct writeback_control *); + +#define PAGECACHE_TAG_REISER4_MOVED PAGECACHE_TAG_FS_SPECIFIC + +#if REISER4_DEBUG_OUTPUT +extern void print_page(const char *prefix, struct page *page); +#else +#define print_page(prf, p) noop +#endif + +/* __REISER4_PAGE_CACHE_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/parser/lex.l 2004-09-03 00:57:05.128259648 -0700 @@ -0,0 +1,65 @@ +%Start Normal + +LETTER [A-Za-z_] +DIG [0-9] +WRD ({LETTER}|{DIG})+ +SP [ \t\n]+ +SPECIAL [\[\]\{\}\/\\\,\:\;\*\$\@\!\`\'] + + + +%% + +%{ +BEGIN Normal; +%} + +{SP}"/"{SP} {return BLANK_SLASH_BLANK;} + +{SP}?";"{SP}? {return SEMICOLON;} +{SP}?","{SP}? {return COMMA;} +{SP}?"+"{SP}? {return PLUS;} +{SP}?"("{SP}? {return L_PARENT;} +{SP}?")"{SP}? {return R_PARENT;} +{SP}?"{"{SP}? {return L_FLX_PARENT;} +{SP}?"}"{SP}? {return R_FLX_PARENT;} +{SP}?"["{SP}? {return L_SKW_PARENT;} +{SP}?"]"{SP}? {return R_SKW_PARENT;} + +{SP}?eq{SP}? {return EQ;} +{SP}?ne{SP}? {return NE;} +{SP}?le{SP}? {return LE;} +{SP}?ge{SP}? {return GE;} +{SP}?lt{SP}? {return LT;} +{SP}?gt{SP}? {return GT;} +{SP}?is{SP}? {return IS;} +{SP}?and{SP}? {return AND;} +{SP}?or{SP}? {return OR;} +{SP}?not{SP}? {return NOT;} +{SP}?if{SP}? {return IF;} +{SP}?then{SP}? {return THEN;} +{SP}?else{SP}? {return ELSE;} +{SP}?exist{SP}? {return EXIST;} + +{SP}?"<""-"{SP}? {return L_ASSIGN;} +{SP}?"<""-""="{SP}? {return L_SYMLINK;} + +{SP}?tw"/""("{SP}? {return TRANSCRASH;} + +"/"process {return SLASH_PROCESS;} +"/"stat {return SLASH_STAT;} +"/"range {return SLASH_RANGE;} +"/""(" {return SLASH_L_PARENT;} +"/" {return SLASH;} + + +{SP}?"]"{SP}? {return BLANK_SLASH_BLANK;} + + +{WRD} { return WORD ;} + +. { return 0 ;} +%% + + + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/parser/lib.c 2004-09-03 00:57:05.136258432 -0700 @@ -0,0 +1,2003 @@ +/* + * Copyright 2001, 2002 by Hans Reiser, licensing governed by reiser4/README + */ + +/* + * functions for parser.y + */ + + +#include "lib.h" + +#include + +#define my_mntget(mess,s) mntget(s); printk ("mntget %d, %s\n",(s)->mnt_count, mess); +#define my_dget(mess,s) dget(s) ; printk ("dget %d, %s\n",(s)->d_count, mess); + +#define path4_release( pointer ) { dput( (pointer).dentry ); \ + mntput((pointer).mnt); \ + printk (" mntput %d\n",((pointer).mnt)->mnt_count);\ + printk (" dput %d\n",((pointer).dentry)->d_count);\ +} + + +#define LEX_XFORM 1001 +#define LEXERR2 1002 +#define LEX_Ste 1003 + +/* printing errors for parsing */ +static void yyerror( struct reiser4_syscall_w_space *ws /* work space ptr */, + int msgnum /* message number */, ...) +{ + char errstr[120]={"\nreiser4 parser:"}; + char * s; + va_list args; + va_start(args, msgnum); + switch (msgnum) { + case 101: + strcat(errstr,"yacc stack overflow"); + break; + case LEX_XFORM: + strcat(errstr,"x format has odd number of symbols"); + break; + case LEXERR2: +/* int state = va_arg(args, int);*/ + strcat(errstr,"internal lex table error"); + break; + case LEX_Ste: + strcat(errstr,"wrong lexem"); + break; + case 11111: + { + int state = va_arg(args, int); + { + char ss[16]; + /* int s = va_arg(args, int);*/ + sprintf( ss,"%4d ", state); + strcat( errstr, ss ); + } + strcat( errstr, " syntax error:" ); + switch(state) { + // case 4: + // strcat(errstr," wrong operation"); + // break; + case 6: + strcat(errstr," wrong assign operation"); + break; + case 7: + case 12: + strcat(errstr," wrong name"); + break; + case 27: + strcat(errstr," wrong logical operation"); + break; + case 10: + strcat(errstr," wrong THEN keyword"); + break; + case 34: + case 50: + strcat(errstr," wrong separatop"); + break; + default: + strcat(errstr," strange error"); + break; + } + } + break; + } + va_end(args); + printk( "\n%s\n", ws->ws_inline ); + for (s=ws->ws_inline; sfree_space_next; + kfree(curr); + } +} + +/* free work space*/ +static int reiser4_pars_free(struct reiser4_syscall_w_space * ws /* work space ptr */) +{ + assert("VD-reiser4_pars_free:ws_level", ws->ws_level >= 0); + assert("VD-reiser4_pars_free:cur_exp" , ws->cur_level->cur_exp != NULL); + + free_expr( ws, ws->cur_level->cur_exp ); + free_expr( ws, ws->root_e ); + if ( ws->freeSpHead ) { + freeList(ws->freeSpHead); + } + + kfree(ws); + return 0; +} + + + + +#if 0 +static inline void path4_release( struct dentry *de, struct vfsmount *mnt) +{ + assert("VD-path4_release: dentry", de != NULL); + assert("VD-path4_release: mnt", mnt != NULL); + my_dput( de ); + my_mntput( mnt ); +} +#endif + +/* FIXME:NIKITA->VOVA code below looks like custom made memory allocator. Why + * not to use slab? */ +#define INITNEXTFREESPACE(fs) (fs)->free_space_next = NULL; \ + (fs)->freeSpaceMax = (fs)->freeSpaceBase+FREESPACESIZE; \ + (fs)->freeSpace = (fs)->freeSpaceBase + + +/* allocate work space */ +static free_space_t * free_space_alloc() +{ + free_space_t * fs; + fs = ( free_space_t * ) kmalloc( sizeof( free_space_t ),GFP_KERNEL ) ; + assert("VD kmalloc work space",fs!=NULL); + memset( fs , 0, sizeof( free_space_t )); + INITNEXTFREESPACE(fs); + return fs; +} + +#define GET_FIRST_FREESPHEAD(ws) (ws)->freeSpHead +#define GET_NEXT_FREESPHEAD(curr) (curr)->free_space_next + + +/* allocate next work space */ +static free_space_t * freeSpaceNextAlloc(struct reiser4_syscall_w_space * ws /* work space ptr */ ) +{ + free_space_t * curr,* next; + curr=NULL; + next = GET_FIRST_FREESPHEAD(ws); + while (next) { + curr = next; + next = GET_NEXT_FREESPHEAD(curr); + } + next = free_space_alloc(); + if(curr==NULL) { + ws->freeSpHead=next; + } + else { + curr->free_space_next=next; + } + next->free_space_next=NULL; + return next; +} + +/* allocate field lenth=len in work space */ +static char* list_alloc(struct reiser4_syscall_w_space * ws/* work space ptr */, + int len/* lenth of structures to be allocated in bytes */) +{ + char * rez; + if( (ws->freeSpCur->freeSpace+len) > (ws->freeSpCur->freeSpaceMax) ) { + ws->freeSpCur = freeSpaceNextAlloc(ws); + } + rez = ws->freeSpCur->freeSpace; + ws->freeSpCur->freeSpace += ROUND_UP(len); + return rez; +} + +/* allocate new level of parsing in work space */ +static streg_t *alloc_new_level(struct reiser4_syscall_w_space * ws /* work space ptr */ ) +{ + return ( streg_t *) list_alloc(ws,sizeof(streg_t)); +} + +/* allocate structure of new variable of input expression */ +static pars_var_t * alloc_pars_var(struct reiser4_syscall_w_space * ws /* work space ptr */, + pars_var_t * last_pars_var /* last of allocated pars_var or NULL if list is empty */) +{ + pars_var_t * pars_var; + pars_var = (pars_var_t *)list_alloc(ws,sizeof(pars_var_t)); + if ( last_pars_var == NULL ) { + ws->Head_pars_var = pars_var; + } + else { + last_pars_var->next = pars_var; + } + pars_var->next = NULL; + return pars_var; +} + +/* free lnodes used in expression */ +static int free_expr( struct reiser4_syscall_w_space * ws, expr_v4_t * expr) +{ + expr_list_t * tmp; + int ret = 0; + assert("VD-free_expr", expr!=NULL); + + printk("free_expr: %d\n", expr->h.type ); + switch (expr->h.type) { + case EXPR_WRD: + break; + case EXPR_PARS_VAR: + pop_var_val_stack( ws, expr->pars_var.v->val ); + + + + + break; + case EXPR_LIST: + tmp=&expr->list; + while (tmp) { + assert("VD-free_expr.EXPR_LIST", tmp->h.type==EXPR_LIST); + ret |= free_expr( ws, tmp->source ); + tmp = tmp->next; + } + break; + case EXPR_ASSIGN: + ret = pop_var_val_stack( ws, expr->assgn.target->val ); + + + + ret |= free_expr( ws, expr->assgn.source ); + break; + case EXPR_LNODE: + assert("VD-free_expr.lnode.lnode", expr->lnode.lnode!=NULL); + path4_release( expr->lnode.lnode->dentry ); + lput( expr->lnode.lnode ); + break; + case EXPR_FLOW: + break; + case EXPR_OP2: + ret = free_expr( ws, expr->op2.op_r ); + ret |= free_expr( ws, expr->op2.op_l ); + break; + case EXPR_OP: + ret = free_expr( ws, expr->op.op ); + break; + } + return ret; +} + + +//ln->inode.inode->i_op->lookup(struct inode *,struct dentry *); +//current->fs->pwd->d_inode->i_op->lookup(struct inode *,struct dentry *); + +#if 0 +/* alloca te space for lnode */ +static lnode * alloc_lnode(struct reiser4_syscall_w_space * ws /* work space ptr */ ) +{ + lnode * ln; + ln = ( lnode * ) kmalloc( sizeof( lnode ), GFP_KERNEL); + assert("VD-alloc_pars_var", ln != NULL ); + memset( ln , 0, sizeof( lnode )); + return ln; +} +#endif + +/* make lnode_dentry from inode, except reiser4 inode */ +static lnode * get_lnode(struct reiser4_syscall_w_space * ws /* work space ptr */ ) +{ + lnode * ln; + reiser4_key key, * k_rez,* l_rez; + +#if 0 /*def NOT_YET*/ + if ( is_reiser4_inode( ws->nd.dentry->inode ) ) { + + k_rez = build_sd_key( ws->nd.dentry->inode, &key); + ln = lget( LNODE_REISER4_INODE, get_inode_oid( ws->nd.dentry->inode) ); + // ln->lw.lw_sb = ws->nd.dentry->inode->isb; + ln->reiser4_inode.inode = /*????*/ ws->nd.dentry->inode->isb; + ln->reiser4_inode.inode = /*????*/ ws->nd.dentry->inode->isb; + PTRACE( ws, "r4: lnode=%p", ln ); + } + else +#endif + { + ln = lget( LNODE_DENTRY, get_inode_oid( ws->nd.dentry->d_inode) ); + { + read_lock(¤t->fs->lock); + ln->dentry.mnt = my_mntget("lget", ws->nd.mnt); + ln->dentry.dentry = my_dget("lget", ws->nd.dentry); + read_unlock(¤t->fs->lock); + } + } + return ln; +} + +/* allocate work space, initialize work space, tables, take root inode and PWD inode */ +static struct reiser4_syscall_w_space * reiser4_pars_init(void) +{ + struct reiser4_syscall_w_space * ws; + + /* allocate work space for parser working variables, attached to this call */ + ws = kmalloc( sizeof( struct reiser4_syscall_w_space ), GFP_KERNEL ); + assert("VD_allock work space", ws != NULL); + memset( ws, 0, sizeof( struct reiser4_syscall_w_space )); + ws->ws_yystacksize = MAXLEVELCO; /* must be 500 by default */ + ws->ws_yymaxdepth = MAXLEVELCO; /* must be 500 by default */ + /* allocate first part of working tables + and initialise headers */ + ws->freeSpHead = free_space_alloc(); + ws->freeSpCur = ws->freeSpHead; + ws->wrdHead = NULL; + ws->cur_level = alloc_new_level(ws); + ws->root_e = init_root(ws); + ws->cur_level->cur_exp = init_pwd(ws); + ws->cur_level->wrk_exp = ws->cur_level->cur_exp; /* current wrk for new level */ + ws->cur_level->prev = NULL; + ws->cur_level->next = NULL; + ws->cur_level->level = 0; + ws->cur_level->stype = 0; + return ws; +} + +#if 0 +static expr_v4_t * named_level_down(struct reiser4_syscall_w_space *ws /* work space ptr */, + expr_v4_t * e /* name for expression */, + expr_v4_t * e1, + long type /* type of level we going to */) +{ + + static int push_var_val_stack( ws, struct pars_var * var, long type ) + + rezult->u.data = kmalloc( SIZEFOR_ASSIGN_RESULT, GFP_KERNEL ) ; + sprintf( rezult->u.data, "%d", ret_code ); + + level_down( ws, , type2 ); + return e1; +} + + +/* level up of parsing level */ +static void level_up_named(struct reiser4_syscall_w_space *ws /* work space ptr */, + expr_v4_t * e1 /* name for expression */, + long type /* type of level we going to */) +{ + pars_var_t * rezult; + + assert("wrong type of named expression", type == CD_BEGIN ); + + rezult = e1->pars_var.v; + switch ( e1->pars_var.v->val->vtype) { + case VAR_EMPTY: + break; + case VAR_LNODE: + break; + case VAR_TMP: + break; + } + + /* make name for w in this level. ???????? + not yet worked */ + + + rezult = lookup_pars_var_word( ws , sink, make_new_word(ws, ASSIGN_RESULT ), VAR_TMP); + rezult->u.data = kmalloc( SIZEFOR_ASSIGN_RESULT, GFP_KERNEL ) ; + sprintf( rezult->u.data, "%d", ret_code ); + +????? + + level_up( ws, type ); +} + +#endif + + +static expr_v4_t *target_name( expr_v4_t *assoc_name, expr_v4_t *target ) +{ + target->pars_var.v->val->associated = assoc_name->pars_var.v; + return target; +} + +/* level up of parsing level */ +static void level_up(struct reiser4_syscall_w_space *ws /* work space ptr */, + long type /* type of level we going to */) +{ + if (ws->cur_level->next==NULL) { + ws->cur_level->next = alloc_new_level(ws); + ws->cur_level->next->next = NULL; + ws->cur_level->next->prev = ws->cur_level; + ws->cur_level->next->level = ws->cur_level->level+1; + } + ws->cur_level = ws->cur_level->next; + ws->cur_level->stype = type; + ws->cur_level->cur_exp = ws->cur_level->prev->wrk_exp; /* current pwd for new level */ + ws->cur_level->wrk_exp = ws->cur_level->cur_exp; /* current wrk for new level */ +} + + +/* level down of parsing level */ +static void level_down(struct reiser4_syscall_w_space * ws /* work space ptr */, + long type1 /* type of level that was up( for checking) */, + long type2 /* type of level that is down(for checking)*/) +{ + pars_var_value_t * ret,*next; + assert("VD-level_down: type mithmatch", type1 == type2 ); + assert("VD-level_down: type mithmatch with level", type1 == ws->cur_level->stype ); + assert("VD-level_down: This is top level, prev == NULL", ws->cur_level->prev != NULL); + ret = ws->cur_level->val_level; + while( ret != NULL ) + { + next = ret->next_level; + assert("VD: level down: not top value was pop", ret == ret->host->val); + pop_var_val_stack( ws, ret ); + ret = next; + } + free_expr( ws, ws->cur_level->prev->wrk_exp ); + ws->cur_level->prev->wrk_exp = ws->cur_level->wrk_exp ; /* current wrk for prev level */ + ws->cur_level = ws->cur_level->prev; +} + +/* copy name from param to free space,*/ +static wrd_t * make_new_word(struct reiser4_syscall_w_space * ws /* work space ptr */, + char *txt /* string to put in name table */) +{ + ws->tmpWrdEnd = ws->freeSpCur->freeSpace; + strcat( ws->tmpWrdEnd, txt ); + ws->tmpWrdEnd += strlen(txt) ; + *ws->tmpWrdEnd++ = 0; + return _wrd_inittab( ws ); +} + + +/* move_selected_word - copy term from input bufer to free space. + * if it need more, move freeSpace to the end. + * otherwise next term will owerwrite it + * freeSpace is a kernel space no need make getnam(). + * exclude is for special for string: store without '' + */ +static void move_selected_word(struct reiser4_syscall_w_space * ws /* work space ptr */, + int exclude /* TRUE - for storing string without first and last symbols + FALS - for storing names */ ) +{ + int i; + /* char * s= ws->ws_pline;*/ + if (exclude) { + ws->yytext++; + } + for( ws->tmpWrdEnd = ws->freeSpCur->freeSpace; ws->yytext < curr_symbol(ws); ) { + i=0; + // while( *ws->yytext == '\'' ) + // { + // ws->yytext++; + // i++; + // } + // while ( ws->yytext > curr_symbol(ws) ) + // { + // i--; + // ws->yytext--; + // } + // if ( i ) for ( i/=2; i; i-- ) *ws->tmpWrdEnd++='\''; /* in source text for each '' - result will ' */ + /* \???????? */ + if ( *ws->yytext == '\\' ) { + int tmpI; + ws->yytext++; + switch ( tolower( (int)*(ws->yytext) ) ) { + case 'x': /* \x01..9a..e */ + i = 0; + tmpI = 1; + while( tmpI) { + if (isdigit( (int)*(ws->yytext) ) ) { + i = (i << 4) + ( *ws->yytext++ - '0' ); + } + else if( tolower( (int) *(ws->yytext) ) >= 'a' && tolower( (int)*(ws->yytext) ) <= 'e' ) { + i = (i << 4) + ( *ws->yytext++ - 'a' + 10 ); + } + else { + if ( tmpI & 1 ) { + yyerror( ws, LEX_XFORM ); /* x format has odd number of symbols */ + } + tmpI = 0; + } + if ( tmpI && !( tmpI++ & 1 ) ) { + *ws->tmpWrdEnd++ = (unsigned char) i; + i = 0; + } + } + break; + } + } + else *ws->tmpWrdEnd++ = *ws->yytext++; + if( ws->tmpWrdEnd > (ws->freeSpCur->freeSpaceMax - sizeof(wrd_t)) ) { + free_space_t * tmp; + int i; + assert ("VD sys_reiser4. selectet_word:Internal space buffer overflow: input token exceed size of bufer", + ws->freeSpCur->freeSpace > ws->freeSpCur->freeSpaceBase); + /* we can reallocate new space and copy all + symbols of current token inside it */ + tmp=ws->freeSpCur; + ws->freeSpCur = freeSpaceNextAlloc(ws); + assert ("VD sys_reiser4:Internal text buffer overflow: no enouse mem", ws->freeSpCur !=NULL); + i = ws->tmpWrdEnd - tmp->freeSpace; + memmove( ws->freeSpCur->freeSpace, tmp->freeSpace, i ); + ws->tmpWrdEnd = ws->freeSpCur->freeSpace + i; + } + } + if (exclude) { + ws->tmpWrdEnd--; + } + *ws->tmpWrdEnd++ = '\0'; +} + + +/* compare parsed word with keywords*/ +static int b_check_word(struct reiser4_syscall_w_space * ws /* work space ptr */) +{ + int i, j, l; + j=sizeof(pars_key)/(sizeof(char*)+sizeof(int))-1; + l=0; + while( ( j - l ) >= 0 ) { + i = ( j + l /*+ 1*/ ) >> 1; + switch( strcmp( pars_key[i].wrd, ws->freeSpCur->freeSpace ) ) { + case 0: + return( pars_key[i].class ); + break; + case 1: j = i - 1; break; + default: l = i + 1; break; + } + } + return(0); +} + + +/* comparing parsed word with already stored words, if not compared, storing it */ +static +//__inline__ +wrd_t * _wrd_inittab(struct reiser4_syscall_w_space * ws /* work space ptr */ ) +{ + wrd_t * cur_wrd; + wrd_t * new_wrd; + int len; + new_wrd = ws->wrdHead; +#if 0 + len = strlen( ws->freeSpCur->freeSpace) ; +#else + len = ws->tmpWrdEnd - ws->freeSpCur->freeSpace - 1 ; +#endif + cur_wrd = NULL; + while ( !( new_wrd == NULL ) ) { + cur_wrd = new_wrd; + if ( cur_wrd->u.len == len ) { + if( !memcmp( cur_wrd->u.name, ws->freeSpCur->freeSpace, cur_wrd->u.len ) ) { + return cur_wrd; + } + } + new_wrd = cur_wrd->next; + } + new_wrd = ( wrd_t *)(ws->freeSpCur->freeSpace + ROUND_UP( len+1 )); + new_wrd->u.name = ws->freeSpCur->freeSpace; + new_wrd->u.len = len; + ws->freeSpCur->freeSpace= (char*)new_wrd + ROUND_UP(sizeof(wrd_t)); + new_wrd->next = NULL; + if (cur_wrd==NULL) { + ws->wrdHead = new_wrd; + } + else { + cur_wrd->next = new_wrd; + } + return new_wrd; +} + +/* lexical analisator for yacc automat */ +static int reiser4_lex( struct reiser4_syscall_w_space * ws /* work space ptr */) +{ + char term, n, i = 0; + int ret = 0; + char lcls; +// char * s ; + +// s = curr_symbol(ws); /* first symbol or Last readed symbol of the previous token parsing */ + if ( *curr_symbol(ws) == 0 ) return 0; /* end of string is EOF */ + + while(ncl[(int)*curr_symbol(ws)]==Blk) { + next_symbol(ws); + if ( *curr_symbol(ws) == 0 ) return 0; /* end of string is EOF */ + } + + + lcls = ncl[(int)*curr_symbol(ws)]; + ws->yytext = curr_symbol(ws); + term = 1; + while( term ) { + n=lcls; + while ( n > 0 ) { + next_symbol(ws); + lcls=n; + n = lexcls[ (int)lcls ].c[ (int)i=ncl[ (int)*curr_symbol(ws) ] ]; + } + if ( n == OK ) { + term=0; + } + else { + yyerror ( ws, LEXERR2, (lcls-1)* 20+i ); + return(0); + } + } + switch (lcls) { + case Blk: + case Ste: + yyerror(ws,LEX_Ste); + break; + case Wrd: + move_selected_word( ws, lexcls[(int) lcls ].c[0] ); + /* if ret>0 this is keyword */ + if ( !(ret = b_check_word(ws)) ) { /* this is not keyword. tray check in worgs. ret = Wrd */ + ret=lexcls[(int) lcls ].term; + ws->ws_yylval.wrd = _wrd_inittab(ws); + } + break; + case Int: + case Ptr: + case Pru: + case Str: /*`......"*/ + move_selected_word( ws, lexcls[(int) lcls ].c[0] ); + ret=lexcls[(int) lcls ].term; + ws->ws_yylval.wrd = _wrd_inittab(ws); + break; + /* + move_selected_word( ws, lexcls[ lcls ].c[0] ); + ret=lexcls[ lcls ].term; + ws->ws_yyval.w = _wrd_inittab(ws); + break; + */ + case Stb: + case Com: + case Mns: + case Les: + case Slh: + case Bsl: /*\ */ + case Sp1: /*;*/ + case Sp2: /*:*/ + case Dot: /*.*/ + case Sp4: /*=*/ + case Sp5: /*>*/ + case Sp6: /*?*/ + case ASG:/*<-*/ + case App:/*<<-*/ /*???*/ + case Lnk:/*->*/ + case Pls:/*+*/ + case Nam:/*<=*/ + ret=lexcls[(int) lcls ].term; + break; + case Lpr: + case Rpr: + ws->ws_yylval.charType = CD_BEGIN ; + ret=lexcls[(int) lcls ].term; + break; + case Lsq: + case Rsq: + ws->ws_yylval.charType = UNORDERED ; + ret=lexcls[(int) lcls ].term; + break; + case Lfl: + case Rfl: + ws->ws_yylval.charType = ASYN_BEGIN ; + ret=lexcls[(int) lcls ].term; + break; + default : /* others */ + ret=*ws->yytext; + break; + } + printk("lexer:%d\n", ret ); + return ret; +} + + + +/*==========================================================*/ + +/* allocate new expression @type */ +static expr_v4_t * alloc_new_expr(struct reiser4_syscall_w_space * ws /* work space ptr */, + int type /* type of new expression */) +{ + expr_v4_t * e; + e = ( expr_v4_t *) list_alloc( ws, sizeof(expr_v4_t)); + e->h.type = type; + return e; +} + +/* store NULL name in word table */ +wrd_t * nullname(struct reiser4_syscall_w_space * ws /* work space ptr */) +{ + return make_new_word(ws,""); +} + + +/* initialize node for root lnode */ +static expr_v4_t * init_root(struct reiser4_syscall_w_space * ws /* work space ptr */) +{ + expr_v4_t * e; + e = alloc_new_expr( ws, EXPR_PARS_VAR ); + e->pars_var.v = alloc_pars_var( ws, NULL ); + e->pars_var.v->w = nullname(ws) ; /* or '/' ????? */ + e->pars_var.v->parent = NULL; + ws->nd.flags = LOOKUP_NOALT; + +// walk_init_root( "/", (&ws->nd)); /* from namei.c walk_init_root */ + { + read_lock(¤t->fs->lock); + ws->nd.mnt = my_mntget("root", current->fs->rootmnt); + ws->nd.dentry = my_dget("root", current->fs->root); + read_unlock(¤t->fs->lock); + } + if (push_var_val_stack( ws, e->pars_var.v, VAR_LNODE )) { + printk("VD-init_root: push_var_val_stack error\n"); + } + else e->pars_var.v->val->u.ln = get_lnode( ws ); + return e; +} + + +/* initialize node for PWD lnode */ +static expr_v4_t * init_pwd(struct reiser4_syscall_w_space * ws /* work space ptr */) +{ + expr_v4_t * e; + e = alloc_new_expr(ws,EXPR_PARS_VAR); + e->pars_var.v = alloc_pars_var(ws,ws->root_e->pars_var.v); + e->pars_var.v->w = nullname(ws) ; /* better if it will point to full pathname for pwd */ + e->pars_var.v->parent = ws->root_e->pars_var.v; + +// path_lookup(".",,&(ws->nd)); /* from namei.c path_lookup */ + { + read_lock(¤t->fs->lock); + ws->nd.mnt = my_mntget("pwd",current->fs->pwdmnt); + ws->nd.dentry = my_dget("pwd",current->fs->pwd); + read_unlock(¤t->fs->lock); + } + current->total_link_count = 0; + if (push_var_val_stack( ws, e->pars_var.v, VAR_LNODE )) { + printk("VD-init_pwd: push_var_val_stack error\n"); + } + else e->pars_var.v->val->u.ln=get_lnode( ws ); + return e; +} + + +/* initialize node for PWD lnode */ +static expr_v4_t * init_pseudo_name(struct reiser4_syscall_w_space * ws /* work space ptr */, + char *name /* name of pseudo */) +{ + expr_v4_t * e; + e = alloc_new_expr(ws,EXPR_PARS_VAR); + e->pars_var.v = alloc_pars_var(ws,ws->root_e->pars_var.v); + e->pars_var.v->w = make_new_word(ws, name); + e->pars_var.v->parent = ws->root_e->pars_var.v; + + current->total_link_count = 0; + push_var_val_stack( ws, e->pars_var.v, VAR_LNODE ); + e->pars_var.v->val->u.ln = get_lnode( ws ); + return e; +} + + +#if 0 +static expr_v4_t * pars_lookup(struct reiser4_syscall_w_space * ws, expr_v4_t * e1, expr_v4_t * e2) +{ + not ready; + pars_var_t * rez_pars_var; + pars_var_t * this_l; + this_l = getFirstPars_Var(e1); + while(this_l != NULL ) { + } + assert("pars_lookup:lnode is null",rez_pars_var->ln!=NULL); + memcpy( &curent_dentry.d_name , w, sizeof(struct qstr));<--------------- + if( ( rez_pars_var->ln = pars_var->ln->d_inode->i_op->lookup( pars_var->ln->d_inode, &curent_dentry) ) == NULL ) { + /* lnode not exist: we will not need create it. this is error*/ + } +} +#endif + +/* Object_Name : begin_from name %prec ROOT { $$ = pars_expr( ws, $1, $2 ) ; } + | Object_Name SLASH name { $$ = pars_expr( ws, $1, $3 ) ; } */ +static expr_v4_t * pars_expr(struct reiser4_syscall_w_space * ws /* work space ptr */, + expr_v4_t * e1 /* first expression ( not yet used)*/, + expr_v4_t * e2 /* second expression*/) +{ + ws->cur_level->wrk_exp = e2; + return e2; +} + +/* not yet */ +static pars_var_t * getFirstPars_VarFromExpr(struct reiser4_syscall_w_space * ws ) +{ + pars_var_t * ret = 0; + expr_v4_t * e = ws->cur_level->wrk_exp; + switch (e->h.type) { + case EXPR_PARS_VAR: + ret = e->pars_var.v; + break; + // default: + + } + return ret; +} + +/* seach @parent/w in internal table. if found return it, else @parent->lookup(@w) */ +static pars_var_t * lookup_pars_var_word(struct reiser4_syscall_w_space * ws /* work space ptr */, + pars_var_t * parent /* parent for w */, + wrd_t * w /* to lookup for word */, + int type) +{ + pars_var_t * rez_pars_var; + struct dentry *de; + int rez; + pars_var_t * last_pars_var; + last_pars_var = NULL; + rez_pars_var = ws->Head_pars_var; + printk("lookup_pars_var_word: parent: %p \n", parent); + while ( rez_pars_var != NULL ) { + if( rez_pars_var->parent == parent && + rez_pars_var->w == w ) { + rez_pars_var->val->count++; + return rez_pars_var; + } + last_pars_var = rez_pars_var; + rez_pars_var = rez_pars_var->next; + } +// reiser4_fs = 0; + rez_pars_var = alloc_pars_var(ws, last_pars_var); + rez_pars_var->w = w; + rez_pars_var->parent = parent; + + switch (parent->val->vtype) { + case VAR_EMPTY: + break; + case VAR_LNODE: + switch (parent->val->u.ln->h.type) { + case LNODE_INODE: /* not use it ! */ + de = d_alloc_anon(parent->val->u.ln->inode.inode); + break; + case LNODE_DENTRY: + ws->nd.dentry = parent->val->u.ln->dentry.dentry; + ws->nd.mnt = parent->val->u.ln->dentry.mnt; + ws->nd.flags = LOOKUP_NOALT ; + if ( link_path_walk( w->u.name, &(ws->nd) ) ) /* namei.c */ { + printk("lookup error\n"); + push_var_val_stack( ws, rez_pars_var, VAR_TMP ); + rez_pars_var->val->u.ln = NULL; + } + else { + push_var_val_stack( ws, rez_pars_var, VAR_LNODE ); + rez_pars_var->val->u.ln = lget( LNODE_DENTRY, get_inode_oid( ws->nd.dentry->d_inode) ); + { + read_lock(¤t->fs->lock); + rez_pars_var->val->u.ln->dentry.mnt = my_mntget("pars_v",ws->nd.mnt); + rez_pars_var->val->u.ln->dentry.dentry = my_dget("pars_v",ws->nd.dentry); + read_unlock(¤t->fs->lock); + } + } + break; + /* + case LNODE_PSEUDO: + PTRACE(ws, "parent pseudo=%p",parent->ln->pseudo.host); + break; + */ + case LNODE_LW: + break; + case LNODE_REISER4_INODE: + rez_pars_var->val->u.ln->h.type = LNODE_REISER4_INODE /* LNODE_LW */; +#if 0 /* NOT_YET ???? */ + // ln = lget( LNODE_DENTRY, get_key_objectid(&key ) ); + result = coord_by_key(get_super_private(parent->val->ln->lw.lw_sb)->tree, + parent->val->ln->lw.key, + &coord, + &lh, + ZNODE_READ_LOCK, + FIND_EXACT, + LEAF_LEVEL, + LEAF_LEVEL, + CBK_UNIQUE, + 0); + // if (REISER4_DEBUG && result == 0) + // check_sd_coord(coord, key); + if (result != 0) { + lw_key_warning(parent->val->ln->lw.key, result); + } + else { + switch(item_type_by_coord(coord)) { + case STAT_DATA_ITEM_TYPE: + printk("VD-item type is STAT_DATA\n"); + case DIR_ENTRY_ITEM_TYPE: + printk("VD-item type is DIR_ENTRY\n"); + iplug = item_plugin_by_coord(coord); + if (iplug->b.lookup != NULL) { + iplug->b.lookup(); /*????*/ + } + + case INTERNAL_ITEM_TYPE: + printk("VD-item type is INTERNAL\n"); + case ORDINARY_FILE_METADATA_TYPE: + case OTHER_ITEM_TYPE: + printk("VD-item type is OTHER\n"); + } + } + /*?? lookup_sd find_item_obsolete */ +#endif + case LNODE_NR_TYPES: + break; + } + break; + case VAR_TMP: + push_var_val_stack( ws, rez_pars_var, VAR_TMP ); + rez_pars_var->val->u.ln = NULL; + break; + } + + return rez_pars_var; +} + + +/* search pars_var for @w */ +static expr_v4_t * lookup_word(struct reiser4_syscall_w_space * ws /* work space ptr */, + wrd_t * w /* word to search for */) +{ + expr_v4_t * e; + pars_var_t * cur_pars_var; +#if 1 /* tmp. this is fist version. for II we need do "while" throus expression for all pars_var */ + cur_pars_var = ws->cur_level->wrk_exp->pars_var.v; +#else + cur_pars_var = getFirstPars_VarFromExpr(ws); + while(cur_pars_var!=NULL) { +#endif + e = alloc_new_expr( ws, EXPR_PARS_VAR ); + e->pars_var.v = lookup_pars_var_word( ws, cur_pars_var, w , VAR_LNODE); +#if 0 + cur_pars_var=getNextPars_VarFromExpr(ws); + } + all rezult mast be connected to expression. +#endif + return e; +} + +/* set work path in level to current in level */ +static inline expr_v4_t * pars_lookup_curr(struct reiser4_syscall_w_space * ws /* work space ptr */) +{ + printk("lookup curr\n"); + ws->cur_level->wrk_exp = ws->cur_level->cur_exp; /* current wrk for pwd of level */ + return ws->cur_level->wrk_exp; +} + +/* set work path in level to root */ +static inline expr_v4_t * pars_lookup_root(struct reiser4_syscall_w_space * ws) +{ + ws->cur_level->wrk_exp = ws->root_e; /* set current to root */ + return ws->cur_level->wrk_exp; +} + + + +#if 0 +/*?????*/ + +/* implementation of lookup_name() method for hashed directories + + it looks for name specified in @w in reiser4_inode @parent and if name is found - key of object found entry points + to is stored in @key */ +reiser4_internal int +lookup_name_hashed_reiser4(reiser4_inode *parent /* reiser4 inode of directory to lookup for name in */, + wrd_t *w /* name to look for */, + reiser4_key *key /* place to store key */) +{ + int result; + coord_t *coord; + lock_handle lh; + const char *name; + int len; + reiser4_dir_entry_desc entry; + + assert("nikita-1247", parent != NULL); + assert("nikita-1248", w != NULL); + +?? assert("vs-1486", dentry->d_op == &reiser4_dentry_operations); + + result = reiser4_perm_chk(parent, lookup, parent, &w->u); + + + if (result != 0) + return 0; + + name = w->u.name; + len = w->u.len; + + if ( len > parent->pset->dir_item) + /* some arbitrary error code to return */ + return RETERR(-ENAMETOOLONG); + + coord = &reiser4_get_dentry_fsdata(dentry)->dec.entry_coord; ??????? + coord_clear_iplug(coord); + + + + + init_lh(&lh); + + ON_TRACE(TRACE_DIR | TRACE_VFS_OPS, "lookup inode: %lli \"%s\"\n", get_inode_oid(parent), dentry->d_name.name); + + /* find entry in a directory. This is plugin method. */ + + + // result = find_entry(parent, dentry, &lh, ZNODE_READ_LOCK, &entry); + + + if (result == 0) { + /* entry was found, extract object key from it. */ + result = WITH_COORD(coord, item_plugin_by_coord(coord)->s.dir.extract_key(coord, key)); + } + done_lh(&lh); + return result; + +} + +node_plugin_by_node(coord->node)->lookup(coord->node, key, FIND_MAX_NOT_MORE_THAN, &twin); +item_type_by_coord(coord) + +/* + * try to look up built-in pseudo file by its name. + */ +reiser4_internal int +lookup_pseudo_file(reiser4_inode *parent /* reiser4 inode of directory to lookup for name in */, + wrd_t *w /* name to look for */, + reiser4_key *key /* place to store key */) + // struct dentry * dentry) +{ + reiser4_plugin *plugin; + const char *name; + struct inode *pseudo; + int result; + + + + + + + assert("nikita-2999", parent != NULL); + assert("nikita-3000", dentry != NULL); + + /* if pseudo files are disabled for this file system bail out */ + if (reiser4_is_set(parent->i_sb, REISER4_NO_PSEUDO)) + return RETERR(-ENOENT); + + name = dentry->d_name.name; + pseudo = ERR_PTR(-ENOENT); + /* scan all pseudo file plugins and check each */ + for_all_plugins(REISER4_PSEUDO_PLUGIN_TYPE, plugin) { + pseudo_plugin *pplug; + + pplug = &plugin->pseudo; + if (pplug->try != NULL && pplug->try(pplug, parent, name)) { + pseudo = add_pseudo(parent, pplug, dentry); + break; + } + } + if (!IS_ERR(pseudo)) + result = 0; + else + result = PTR_ERR(pseudo); + return result; +} + +#endif + +static int lookup_pars_var_lnode(struct reiser4_syscall_w_space * ws /* work space ptr */, + pars_var_t * parent /* parent for w */, + wrd_t * w /* to lookup for word */) +{ + struct dentry * de, * de_rez; + int rez; + pars_var_t * rez_pars_var; + reiser4_key key,* k_rez; + coord_t coord; + lock_handle lh; + item_plugin *iplug; + +// case EXPR_PARS_VAR: +// /* not yet */ +// ws->nd.dentry=parent->ln->dentry.dentry; +// de_rez = link_path_walk( w->u.name, &(ws->nd) ); /* namei.c */ +// break; + + PTRACE(ws, " w->u.name= %p, u.name->%s, u.len=%d",w->u.name,w->u.name,w->u.len); + + return rez; + +} + + + + +/* if_then_else procedure */ +static expr_v4_t * if_then_else(struct reiser4_syscall_w_space * ws /* work space ptr */, + expr_v4_t * e1 /* expression of condition */, + expr_v4_t * e2 /* expression of then */, + expr_v4_t * e3 /* expression of else */ ) +{ + PTRACE(ws, "%s", "begin"); + return e1; +} + +/* not yet */ +static expr_v4_t * if_then(struct reiser4_syscall_w_space * ws /* work space ptr */, + expr_v4_t * e1 /**/, + expr_v4_t * e2 /**/ ) +{ + PTRACE(ws, "%s", "begin"); + return e1; +} + +/* not yet */ +static void goto_end(struct reiser4_syscall_w_space * ws /* work space ptr */) +{ +} + + +/* STRING_CONSTANT to expression */ +static expr_v4_t * const_to_expr(struct reiser4_syscall_w_space * ws /* work space ptr */, + wrd_t * e1 /* constant for convert to expression */) +{ + expr_v4_t * new_expr = alloc_new_expr(ws, EXPR_WRD ); + new_expr->wd.s = e1; + return new_expr; +} + +/* allocate EXPR_OP2 */ +static expr_v4_t * allocate_expr_op2(struct reiser4_syscall_w_space * ws /* work space ptr */, + expr_v4_t * e1 /* first expr */, + expr_v4_t * e2 /* second expr */, + int op /* expression code */) +{ + expr_v4_t * ret; + ret = alloc_new_expr( ws, EXPR_OP2 ); + assert("VD alloc op2", ret!=NULL); + ret->h.exp_code = op; + ret->op2.op_l = e1; + ret->op2.op_r = e2; + return ret; +} + +/* allocate EXPR_OP */ +static expr_v4_t * allocate_expr_op(struct reiser4_syscall_w_space * ws /* work space ptr */, + expr_v4_t * e1 /* first expr */, + int op /* expression code */) +{ + expr_v4_t * ret; + ret = alloc_new_expr(ws, EXPR_OP2 ); + assert("VD alloc op2", ret!=NULL); + ret->h.exp_code = op; + ret->op.op = e1; + return ret; +} + + +/* concatenate expressions */ +static expr_v4_t * concat_expression(struct reiser4_syscall_w_space * ws /* work space ptr */, + expr_v4_t * e1 /* first expr of concating */, + expr_v4_t * e2 /* second expr of concating */) +{ + return allocate_expr_op2( ws, e1, e2, CONCAT ); +} + + +/* compare expressions */ +static expr_v4_t * compare_EQ_expression(struct reiser4_syscall_w_space * ws /* work space ptr */, + expr_v4_t * e1 /* first expr of comparing */, + expr_v4_t * e2 /* second expr of comparing */) +{ + return allocate_expr_op2( ws, e1, e2, COMPARE_EQ ); +} + + +static expr_v4_t * compare_NE_expression(struct reiser4_syscall_w_space * ws /* work space ptr */, + expr_v4_t * e1 /* first expr of comparing */, + expr_v4_t * e2 /* second expr of comparing */) +{ + return allocate_expr_op2( ws, e1, e2, COMPARE_NE ); +} + + +static expr_v4_t * compare_LE_expression(struct reiser4_syscall_w_space * ws /* work space ptr */, + expr_v4_t * e1 /* first expr of comparing */, + expr_v4_t * e2 /* second expr of comparing */) +{ + return allocate_expr_op2( ws, e1, e2, COMPARE_LE ); +} + + +static expr_v4_t * compare_GE_expression(struct reiser4_syscall_w_space * ws /* work space ptr */, + expr_v4_t * e1 /* first expr of comparing */, + expr_v4_t * e2 /* second expr of comparing */) +{ + return allocate_expr_op2( ws, e1, e2, COMPARE_GE ); +} + + +static expr_v4_t * compare_LT_expression(struct reiser4_syscall_w_space * ws /* work space ptr */, + expr_v4_t * e1 /* first expr of comparing */, + expr_v4_t * e2 /* second expr of comparing */) +{ + return allocate_expr_op2( ws, e1, e2, COMPARE_LT ); +} + + +static expr_v4_t * compare_GT_expression(struct reiser4_syscall_w_space * ws /* work space ptr */, + expr_v4_t * e1 /* first expr of comparing */, + expr_v4_t * e2 /* second expr of comparing */) +{ + return allocate_expr_op2( ws, e1, e2, COMPARE_GT ); +} + + +static expr_v4_t * compare_OR_expression(struct reiser4_syscall_w_space * ws /* work space ptr */, + expr_v4_t * e1 /* first expr of comparing */, + expr_v4_t * e2 /* second expr of comparing */) +{ + return allocate_expr_op2( ws, e1, e2, COMPARE_OR ); +} + + +static expr_v4_t * compare_AND_expression(struct reiser4_syscall_w_space * ws /* work space ptr */, + expr_v4_t * e1 /* first expr of comparing */, + expr_v4_t * e2 /* second expr of comparing */) +{ + return allocate_expr_op2( ws, e1, e2, COMPARE_AND ); +} + + +static expr_v4_t * not_expression(struct reiser4_syscall_w_space * ws /* work space ptr */, + expr_v4_t * e1 /* first expr of comparing */) +{ + return allocate_expr_op( ws, e1, COMPARE_NOT ); +} + + +/**/ +static expr_v4_t * check_exist(struct reiser4_syscall_w_space * ws /* work space ptr */, + expr_v4_t * e1 /* first expr of comparing */) +{ + return e1; +} + +/* union lists */ +static expr_v4_t * union_lists(struct reiser4_syscall_w_space * ws /* work space ptr */, + expr_v4_t * e1 /* first expr of connecting */, + expr_v4_t * e2 /* second expr of connecting */) +{ + expr_list_t *next, *last; + assert("VD-connect_list", e1->h.type == EXPR_LIST); + + last = (expr_list_t *)e1; + next = e1->list.next; + /* find last in list */ + while ( next ) { + last = next; + next = next->next; + } + if ( e2->h.type == EXPR_LIST ) { /* connect 2 lists */ + last->next = (expr_list_t *) e2; + } + else { /* add 2 EXPR to 1 list */ + next = (expr_list_t *) alloc_new_expr(ws, EXPR_LIST ); + assert("VD alloct list", next!=NULL); + next->next = NULL; + next->source = e2; + last->next = next; + } + return e1; +} + + +/* make list from expressions */ +static expr_v4_t * list_expression(struct reiser4_syscall_w_space * ws /* work space ptr */, + expr_v4_t * e1 /* first expr of list */, + expr_v4_t * e2 /* second expr of list */) +{ + expr_v4_t * ret; + + if ( e1->h.type == EXPR_LIST ) { + ret = union_lists( ws, e1, e2); + } + else { + + if ( e2->h.type == EXPR_LIST ) { + ret = union_lists( ws, e2, e1); + } + else { + ret = alloc_new_expr(ws, EXPR_LIST ); + assert("VD alloct list 1", ret!=NULL); + ret->list.source = e1; + ret->list.next = (expr_list_t *)alloc_new_expr(ws, EXPR_LIST ); + assert("VD alloct list 2",ret->list.next!=NULL); + ret->list.next->next = NULL; + ret->list.next->source = e2; + } + } + return ret; +} + + + +static expr_v4_t * list_async_expression(struct reiser4_syscall_w_space * ws, expr_v4_t * e1, expr_v4_t * e2 ) +{ + return list_expression( ws, e1 , e2 ); +} + + +static expr_v4_t * unname( struct reiser4_syscall_w_space * ws, expr_v4_t * e1 ) +{ + return e1; +} + + + +static expr_v4_t * assign(struct reiser4_syscall_w_space * ws, expr_v4_t * e1, expr_v4_t * e2) +{ + /* while for each pars_var in e1 */ + return pump( ws, e1->pars_var.v, e2 ); /* tmp. */ +} + + + +static expr_v4_t * assign_invert(struct reiser4_syscall_w_space * ws, expr_v4_t * e1, expr_v4_t * e2) +{ + return e2; +} + +/* not yet */ +static expr_v4_t * symlink(struct reiser4_syscall_w_space * ws, expr_v4_t * e1, expr_v4_t * e2) +{ + return e2; +} + + + +/* + A flow is a source from which data can be obtained. A Flow can be one of these types: + + 1. memory area in user space. (char *area, size_t length) + 2. memory area in kernel space. (caddr_t *area, size_t length) + 3. file-system object (lnode *obj, loff_t offset, size_t length) +*/ +#if 0 +typedef struct connect connect_t; + +struct connect +{ + expr_v4_t * (*u)(pars_var_t *dst, expr_v4_t *src); +}; + +static expr_v4_t * reiser4_assign( pars_var_t *dst, expr_v4_t *src ) +{ + int ret_code; + file_plugin *src_fplug; + file_plugin *dst_fplug; + connect_t connection; + + /* + * select how to transfer data from @src to @dst. + * + * Default implementation of this is common_transfer() (see below). + * + * Smart file plugin can choose connection based on type of @dst. + * + */ +#if 0 + connection = dst->v->fplug -> select_connection( src, dst ); +#else + /* connection.u=common_transfer;*/ +#endif + + /* do transfer */ + return common_transfer( &dst, &src ); +} + +#endif + + +static int source_not_empty(expr_v4_t *source) +{ + return 0; +} + +static mm_segment_t __ski_old_fs; + + +#define START_KERNEL_IO_GLOB \ + __ski_old_fs = get_fs(); \ + set_fs( KERNEL_DS ) + +#define END_KERNEL_IO_GLOB \ + set_fs( __ski_old_fs ); + +#define PUMP_BUF_SIZE (PAGE_CACHE_SIZE) + + +static int push_tube_stack( tube_t * tube, long type, void * pointer ) +{ + sourece_stack_t * ret; + ret = kmalloc( sizeof(struct sourece_stack), GFP_KERNEL ); + if (!IS_ERR(ret)) { + ret->prev = tube->st_current; + ret->type = type; + ret->u.pointer = pointer; + tube->st_current = ret; + return 0; + } + else { + return PTR_ERR(ret); + } +} + +static int push_tube_list_stack_done(tube_t * tube) +{ + tube->next->prev = tube->st_current; + tube->st_current = tube->last; + tube->last = NULL; + tube->next = NULL; + return 0; +} + + +static int push_tube_list_stack_init( tube_t * tube, long type, void * pointer ) +{ + sourece_stack_t * ret; + tube->last = kmalloc( sizeof(struct sourece_stack), GFP_KERNEL ); + if (!IS_ERR(tube->last)) { + tube->next = tube->last; + ret->type = type; + ret->u.pointer = pointer; + return 0; + } + else { + return PTR_ERR(tube->last); + } +} + +static int push_tube_list_stack(tube_t * tube, long type, void * pointer ) +{ + sourece_stack_t * ret; + ret = kmalloc( sizeof(struct sourece_stack), GFP_KERNEL ); + if (!IS_ERR(ret)) { + tube->next->prev = ret; + ret->type = type; + ret->u.pointer = pointer; + tube->next = ret; + return 0; + } + else { + return PTR_ERR(ret); + } +} + +static int change_tube_stack(tube_t * tube, long type, void * pointer ) +{ + tube->st_current->type = type; + tube->st_current->u.pointer = pointer; + return 0; +} + +static int pop_tube_stack( tube_t * tube ) +{ + sourece_stack_t * ret; + if ( tube->st_current == NULL ) { + return -1; + } + else { + ret = tube->st_current; + tube->st_current = tube->st_current->prev; + kfree( ret ); + return 0; + } +} + +static int push_var_val_stack(struct reiser4_syscall_w_space *ws /* work space ptr */, + struct pars_var * var, + long type) +{ + pars_var_value_t * ret; + ret = kmalloc( sizeof(pars_var_value_t), GFP_KERNEL ); + memset( ret , 0, sizeof( pars_var_value_t )); + if (!IS_ERR(ret)) { + ret->prev = var->val; + ret->vtype = type; + ret->host = var; + ret->count = 1; + ret->len = -11; + ret->off = 0; + ret->next_level = ws->cur_level->val_level; + ws->cur_level->val_level = ret; + var->val = ret; + return 0; + } + else { + return PTR_ERR(ret); + } +} + +static int pop_var_val_stack( struct reiser4_syscall_w_space *ws /* work space ptr */, + pars_var_value_t * val ) +{ + pars_var_value_t * ret; + if ( val == NULL ) { + return -1; + } + else { + switch(val->vtype) { +#if 0 + case VAR_EMPTY: + break; + case VAR_LNODE: + assert("VD-pop_var_val_stack.VAR_LNODE", val->u.ln!=NULL); + printk("var->val->count %d\n", val->count ); + // if ( !--var->val->count ) + { + path4_release( val->u.ln->dentry ); + lput( val->u.ln ); + } + break; +#endif + case VAR_TMP: + if (val->u.data!=NULL) { + kfree( val->u.data ); + } + val->host->val = val->prev; + ws->cur_level->val_level = val->next_level; + kfree( val ); + break; + } + return 0; + } +} + +/* pop onto stack for calculate expressions one step */ +static void put_tube_src(tube_t * tube) +{ + /* close readed file and pop stack */ + switch (tube->st_current->type) { + case ST_FILE: + filp_close(tube->st_current->u.file, current->files ); + case ST_DE: + case ST_WD: + case ST_DATA: + pop_tube_stack(tube); + break; + } +} + +/* push & pop onto stack for calculate expressions one step */ +static int get_tube_next_src(tube_t * tube) +{ + expr_v4_t * s; + expr_list_t * tmp; + int ret; + struct file * fl; + + tube->readoff=0; + assert ("VD stack is empty", tube->st_current != NULL ); + + /* check stack and change its head */ + switch (tube->st_current->type) { + case ST_FILE: + case ST_DE: + case ST_WD: + ret = 0; + break; + case ST_EXPR: + s = tube->st_current->u.expr; + switch (s->h.type) { + case EXPR_WRD: + change_tube_stack( tube, ST_WD , s->wd.s ); + break; + case EXPR_PARS_VAR: + assert("VD-free_expr.EXPR_PARS_VAR", s->pars_var.v!=NULL); + switch( s->pars_var.v->val->vtype) { + case VAR_EMPTY: + break; + case VAR_LNODE: + assert("VD-free_expr.EXPR_PARS_VAR.ln", s->pars_var.v->val->u.ln!=NULL); + // if ( S_ISREG(s->pars_var.v->val->u.ln->dentry.dentry->d_inode) ) + { + fl= dentry_open( s->pars_var.v->val->u.ln->dentry.dentry, + s->pars_var.v->val->u.ln->dentry.mnt, O_RDONLY ) ; + if ( !IS_ERR(fl) ) { + change_tube_stack( tube, ST_FILE , fl); + } + else printk("error for open source\n"); + } +#if 0 // not yet + else if ( S_ISDIR(s->pars_var.v->val->u.ln->dentry.dentry->d_inode) ) { + while(NOT EOF) + { + readdir(); + } + } +#endif + ret = 0; + break; + case VAR_TMP: + if ( s->pars_var.v->val->u.data == NULL ) + { + pop_tube_stack( tube ); + ret = 1; + } + else + { + change_tube_stack( tube, ST_DATA , s->pars_var.v->val->u.data); + ret = 0; + } + break; + } + break; + case EXPR_LIST: + tmp = &s->list; + push_tube_list_stack_init( tube, ST_EXPR , tmp->source ); + while (tmp) { + tmp = tmp->next; + push_tube_list_stack( tube, ST_EXPR, tmp->source ); + } + pop_tube_stack( tube ); + push_tube_list_stack_done( tube ); + ret = 1; + break; + case EXPR_ASSIGN: +#if 0 // not yet + assert("VD-free_expr.EXPR_ASSIGN", s->assgn.target!=NULL); + assert("VD-free_expr.EXPR_ASSIGN.ln", s->assgn.target->ln!=NULL); + assert("VD-free_expr.EXPR_ASSIGN.count", s->assgn.target->val->count>0); + ( s->assgn.target->ln); + ( s->assgn.source ); +#endif + break; + case EXPR_LNODE: + assert("VD-free_expr.lnode.lnode", s->lnode.lnode!=NULL); +// if ( S_ISREG(s->lnode.lnode->dentry.dentry->d_inode) ) + { + change_tube_stack( tube, ST_FILE , + dentry_open( s->lnode.lnode->dentry.dentry, + s->lnode.lnode->dentry.mnt, O_RDONLY ) ); + } + ret = 0; + break; + case EXPR_FLOW: + break; + case EXPR_OP2: + change_tube_stack( tube, ST_EXPR , s->op2.op_r ); + push_tube_stack( tube, ST_EXPR , s->op2.op_l ); + ret = 1; + break; + case EXPR_OP: + change_tube_stack( tube, ST_EXPR , s->op.op ); + ret = 1; + break; + } + break; + } + return ret; +} + + +static tube_t * get_tube_general(tube_t * tube, pars_var_t *sink, expr_v4_t *source) +{ + + char * buf; + tube = kmalloc( sizeof(struct tube), GFP_KERNEL); + if (!IS_ERR(tube)) { + START_KERNEL_IO_GLOB; + memset( tube , 0, sizeof( struct tube )); + assert("VD get_tube_general: no tube",!IS_ERR(tube)); + printk("get_tube_general:%d\n",sink->val->vtype); + tube->target = sink; + switch( sink->val->vtype ) { + case VAR_EMPTY: + break; + case VAR_LNODE: + printk("VAR_LNODE\n"); + assert("VD get_tube_general: dst no dentry",sink->val->u.ln->h.type== LNODE_DENTRY); + tube->dst = dentry_open( sink->val->u.ln->dentry.dentry, + sink->val->u.ln->dentry.mnt, + O_WRONLY|O_TRUNC ); + tube->writeoff = 0; + tube->st_current = NULL; + push_tube_stack( tube, ST_EXPR, (long *)source ); + break; + case VAR_TMP: + printk("VAR_TMP\n"); + break; + } + } + return tube; +} + +static size_t reserv_space_in_sink(tube_t * tube ) +{ + tube->buf = kmalloc( PUMP_BUF_SIZE, GFP_KERNEL); + if (!IS_ERR(tube->buf)) { + memset( tube->buf , 0, PUMP_BUF_SIZE); + return PUMP_BUF_SIZE; + } + else { + return 0; + } +} + +static size_t get_available_src_len(tube_t * tube) +{ + size_t len; + size_t s_len; + int ret = 1; + len = PUMP_BUF_SIZE; + while ( tube->st_current != NULL && ret ) { + ret = 0; + switch( tube->st_current->type ) { + case ST_FILE: + s_len = tube->st_current->u.file->f_dentry->d_inode->i_size; + /* for reiser4 find_file_size() */ + break; + case ST_DE: + break; + case ST_WD: + s_len = tube->st_current->u.wd->u.len; + break; + case ST_EXPR: + while( tube->st_current != NULL && get_tube_next_src( tube ) ) ; + len = -1; + ret = 1; + break; + case ST_DATA: + s_len = strlen((char *)tube->st_current->u.pointer); + break; + } + } + s_len -= tube->readoff; + if (tube->st_current == NULL) { + len = 0; + } + else { + if ( len > s_len ) len = s_len; + } + return len; +} + +static size_t prep_tube_general(tube_t * tube) +{ + size_t ret; + if ( tube->st_current != NULL ) { + ret = get_available_src_len( tube ) ; + } + else { + ret = 0; + } + tube->len = ret; + return ret; +} + + + +static size_t source_to_tube_general(tube_t * tube) +{ + // tube->source->fplug->read(tube->offset,tube->len); + size_t ret; + switch( tube->st_current->type ) { + case ST_FILE: + ret = vfs_read(tube->st_current->u.file, tube->buf, tube->len, &tube->readoff); + tube->len = ret; + break; + case ST_DE: + break; + case ST_WD: + if ( tube->readoff < tube->st_current->u.wd->u.len ) { + assert ("VD source to tube(wd)", tube->readoff+tube->len <= tube->st_current->u.wd->u.len); + memcpy( tube->buf, tube->st_current->u.wd->u.name + tube->readoff, ret = tube->len ); + tube->readoff += ret; + } + else ret = 0; + case ST_DATA: + if ( tube->readoff < strlen((char *)tube->st_current->u.pointer) ) { + memcpy( tube->buf, tube->st_current->u.pointer + tube->readoff, ret = tube->len ); + tube->readoff += ret; + } + else ret = 0; + break; + } + return ret; +} + +static size_t tube_to_sink_general(tube_t * tube) +{ + size_t ret; +// tube->sink->fplug->write(tube->offset,tube->len); +// tube->offset += tube->len; + switch(tube->target->val->vtype) { + case VAR_EMPTY: + break; + case VAR_LNODE: + ret = vfs_write(tube->dst, tube->buf, tube->len, &tube->writeoff); + break; + case VAR_TMP: + // memncpy(tube->target->data,tube->buf,tube->len); + // or + // tube->target->data=tube->buf; + // ? + printk("write to pseudo not yet work\n"); + tube->writeoff+=tube->len; + break; + } + return ret; + +} + +static void put_tube(tube_t * tube) +{ + PTRACE1( "%s\n", "begin"); + END_KERNEL_IO_GLOB; + assert("VD :stack not empty ",tube->st_current == NULL); + switch(tube->target->val->vtype) { + case VAR_EMPTY: + break; + case VAR_LNODE: + do_truncate( tube->dst->f_dentry, tube->writeoff); + filp_close(tube->dst, current->files ); + break; + case VAR_TMP: + break; + } + kfree(tube->buf); + kfree(tube); +} + +static int create_result_field(struct reiser4_syscall_w_space * ws, + pars_var_t *parent, /* parent for name */ + char * name , /* created name */ + int result_len, /* length of allocated space for value */ + int result) +{ + int ret; + wrd_t * tmp_wrd; + pars_var_t * rezult; + + tmp_wrd = make_new_word(ws, name ); + rezult = lookup_pars_var_word( ws , parent, tmp_wrd, VAR_TMP); + if ( rezult != NULL ) { + rezult->val->u.data = kmalloc( result_len, GFP_KERNEL ) ; + if ( rezult->val->u.data ) { + sprintf( rezult->val->u.data, "%d", result ); + ret=0; + } + } + else { + ret = 1; + } + return ret; +} + +static int create_result(struct reiser4_syscall_w_space * ws, + pars_var_t *parent, /* parent for name */ + int err_code , /* error code of assign */ + int length) /* length of assign */ +{ + int ret; + ret = create_result_field( ws, parent, + ASSIGN_RESULT, SIZEFOR_ASSIGN_RESULT, err_code ); + ret += create_result_field( ws, parent, + ASSIGN_LENGTH, SIZEFOR_ASSIGN_LENGTH, length ); + return ret; +} + + + +/* + Often connection() will be a method that employs memcpy(). Sometimes + copying data from one file plugin to another will mean transforming + the data. What reiser4_assign does depends on the type of the flow + and sink. If @flow is based on the kernel-space area, memmove() is + used to copy data. If @flow is based on the user-space area, + copy_from_user() is used. If @flow is based on a file-system object, + flow_place() uses the page cache as a universal translator, loads + the object's data into the page cache, and then copies them into + @area. Someday methods will be written to copy objects more + efficiently than using the page cache (e.g. consider copying holes + [add link to definition of a hole]), but this will not be + implemented in V4.0. +*/ +static expr_v4_t * pump(struct reiser4_syscall_w_space * ws, pars_var_t *sink, expr_v4_t *source ) +{ + pars_var_t * assoc; + expr_v4_t * ret; + tube_t * tube; + int ret_code; + size_t (*prep_tube)(tube_t *); + size_t (*source_to_tube)(tube_t *); + size_t (*tube_to_sink)(tube_t *); + PTRACE1( "%s", "begin"); + + /* remember to write code for freeing tube, error handling, etc. */ +#if 0 + ret_code = sink->fplug -> get_tube( tube, sink, source); + prep_tube = sink->fplug->prep_tube (tube); + source_to_tube = source->fplug->source_to_tube; + tube_to_sink = sink->fplug->tube_to_sink; +#else + tube = get_tube_general( tube, sink, source); + if ( tube == NULL ) { + ret_code = -1; + } + else { + prep_tube = prep_tube_general; + source_to_tube = source_to_tube_general; + tube_to_sink = tube_to_sink_general; +#endif + reserv_space_in_sink( tube ); + + while ( tube->st_current != NULL ) { + if ( ret_code = prep_tube( tube ) >0 ) { + while ( ret_code = source_to_tube( tube ) > 0 ) { + ret_code = tube_to_sink( tube ); + } + if ( ret_code < 0 ) { + printk("IO error\n"); + } + put_tube_src( tube ); + } + } + + + ret=alloc_new_expr( ws, EXPR_PARS_VAR ); + + if ( sink->val->associated == NULL) { + create_result( ws, sink, ret_code, tube->writeoff ); + ret->pars_var.v = sink; + } + else { + create_result( ws, sink->val->associated, ret_code, tube->writeoff ); + ret->pars_var.v = sink->val->associated; + sink->val->associated == NULL; + } + +#if 0 + tmp_wrd = make_new_word(ws, ASSIGN_RESULT ); + rezult = lookup_pars_var_word( ws , assoc, tmp_wrd, VAR_TMP); + rezult->val->u.data = kmalloc( SIZEFOR_ASSIGN_RESULT, GFP_KERNEL ) ; + sprintf( rezult->val->u.data, "%d", ret_code ); + + tmp_wrd = make_new_word(ws, ASSIGN_LENGTH ); + length = lookup_pars_var_word( ws , assoc, tmp_wrd, VAR_TMP); + length->val->u.data = kmalloc( SIZEFOR_ASSIGN_LENGTH, GFP_KERNEL ) ; + sprintf( length->val->u.data, "%d", tube->writeoff ); + + + /* + ret1=alloc_new_expr( ws, EXPR_PARS_VAR ); + ret->pars_var.v = rezult; + ret1=alloc_new_expr( ws, EXPR_PARS_VAR ); + ret->pars_var.v = length; + ret = list_expression( ws, list_expression( ws, ret, ret1 ), ret2 ) ; + */ +#endif + put_tube( tube ); + } + return ret; +} + + + + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/parser/lib.h 2004-09-03 00:57:05.137258280 -0700 @@ -0,0 +1,84 @@ +/* + * Copyright 2001, 2002 by Hans Reiser, licensing governed by reiser4/README + */ + +/* + * functions for parser.y + */ + + + + + + +static void yyerror( struct reiser4_syscall_w_space *ws, int msgnum , ...); +static int yywrap(void); +static free_space_t * free_space_alloc(void); +static void freeList(free_space_t * list); +static int reiser4_pars_free(struct reiser4_syscall_w_space * ws); +static free_space_t * freeSpaceAlloc(void); +static free_space_t * freeSpaceNextAlloc(struct reiser4_syscall_w_space * ws); +static char* list_alloc(struct reiser4_syscall_w_space * ws, int size); +static streg_t *alloc_new_level(struct reiser4_syscall_w_space * ws); +static pars_var_t * alloc_pars_var(struct reiser4_syscall_w_space * ws, pars_var_t * last_pars_var); +static int free_expr( struct reiser4_syscall_w_space * ws, expr_v4_t * expr); +static lnode * get_lnode(struct reiser4_syscall_w_space * ws); +static struct reiser4_syscall_w_space * reiser4_pars_init(void); +static void level_up(struct reiser4_syscall_w_space *ws, long type); +static void level_down(struct reiser4_syscall_w_space * ws, long type1, long type2); +static void move_selected_word(struct reiser4_syscall_w_space * ws, int exclude ); +static int b_check_word(struct reiser4_syscall_w_space * ws ); +static __inline__ wrd_t * _wrd_inittab(struct reiser4_syscall_w_space * ws ); +static int reiser4_lex( struct reiser4_syscall_w_space * ws ); +static expr_v4_t * alloc_new_expr(struct reiser4_syscall_w_space * ws, int type); +wrd_t * nullname(struct reiser4_syscall_w_space * ws); +static expr_v4_t * init_root(struct reiser4_syscall_w_space * ws); +static expr_v4_t * init_pwd(struct reiser4_syscall_w_space * ws); +static expr_v4_t * pars_expr(struct reiser4_syscall_w_space * ws, expr_v4_t * e1, expr_v4_t * e2); +static expr_v4_t * lookup_word(struct reiser4_syscall_w_space * ws, wrd_t * w); +static inline expr_v4_t * pars_lookup_curr(struct reiser4_syscall_w_space * ws); +static inline expr_v4_t * pars_lookup_root(struct reiser4_syscall_w_space * ws); +static pars_var_t * lookup_pars_var_word(struct reiser4_syscall_w_space * ws, pars_var_t * pars_var, wrd_t * w, int type); +static expr_v4_t * make_do_it(struct reiser4_syscall_w_space * ws, expr_v4_t * e1 ); +static expr_v4_t * if_then_else(struct reiser4_syscall_w_space * ws, expr_v4_t * e1, expr_v4_t * e2 , expr_v4_t * e3 ); +static expr_v4_t * if_then(struct reiser4_syscall_w_space * ws, expr_v4_t * e1, expr_v4_t * e2 ); +static void goto_end(struct reiser4_syscall_w_space * ws); +static expr_v4_t * constToExpr(struct reiser4_syscall_w_space * ws, wrd_t * e1 ); +static expr_v4_t * connect_expression(struct reiser4_syscall_w_space * ws, expr_v4_t * e1, expr_v4_t * e2); +static expr_v4_t * compare_EQ_expression(struct reiser4_syscall_w_space * ws, expr_v4_t * e1, expr_v4_t * e2); +static expr_v4_t * compare_NE_expression(struct reiser4_syscall_w_space * ws, expr_v4_t * e1, expr_v4_t * e2); +static expr_v4_t * compare_LE_expression(struct reiser4_syscall_w_space * ws, expr_v4_t * e1, expr_v4_t * e2); +static expr_v4_t * compare_GE_expression(struct reiser4_syscall_w_space * ws, expr_v4_t * e1, expr_v4_t * e2); +static expr_v4_t * compare_LT_expression(struct reiser4_syscall_w_space * ws, expr_v4_t * e1, expr_v4_t * e2); +static expr_v4_t * compare_GT_expression(struct reiser4_syscall_w_space * ws, expr_v4_t * e1, expr_v4_t * e2); +static expr_v4_t * compare_OR_expression(struct reiser4_syscall_w_space * ws, expr_v4_t * e1, expr_v4_t * e2); +static expr_v4_t * compare_AND_expression(struct reiser4_syscall_w_space * ws, expr_v4_t * e1, expr_v4_t * e2); +static expr_v4_t * not_expression(struct reiser4_syscall_w_space * ws, expr_v4_t * e1); +static expr_v4_t * check_exist(struct reiser4_syscall_w_space * ws, expr_v4_t * e1); +static expr_v4_t * list_expression(struct reiser4_syscall_w_space * ws, expr_v4_t * e1, expr_v4_t * e2 ); +static expr_v4_t * list_async_expression(struct reiser4_syscall_w_space * ws, expr_v4_t * e1, expr_v4_t * e2 ); +static expr_v4_t * assign(struct reiser4_syscall_w_space * ws, expr_v4_t * e1, expr_v4_t * e2); +static expr_v4_t * assign_invert(struct reiser4_syscall_w_space * ws, expr_v4_t * e1, expr_v4_t * e2); +static expr_v4_t * symlink(struct reiser4_syscall_w_space * ws, expr_v4_t * e1, expr_v4_t * e2); +static int source_not_empty(expr_v4_t *source); +static tube_t * get_tube_general(tube_t * tube, pars_var_t *sink, expr_v4_t *source); +static size_t reserv_space_in_sink(tube_t * tube); +static size_t get_available_len(tube_t * tube); +static size_t prep_tube_general(tube_t * tube); +static size_t source_to_tube_general(tube_t * tube); +static size_t tube_to_sink_general(tube_t * tube); +static void put_tube(tube_t * tube); +static expr_v4_t * pump(struct reiser4_syscall_w_space * ws, pars_var_t *sink, expr_v4_t *source ); + +static int pop_var_val_stack( struct reiser4_syscall_w_space *ws /* work space ptr */, + pars_var_value_t * val ); +static int push_var_val_stack(struct reiser4_syscall_w_space *ws /* work space ptr */, + struct pars_var * var, + long type ); +static expr_v4_t *target_name( expr_v4_t *assoc_name, expr_v4_t *target ); + +#define curr_symbol(ws) ((ws)->ws_pline) +#define next_symbol(ws) (++curr_symbol(ws)) +#define tolower(a) a +#define isdigit(a) ((a)>=0 && (a)<=9) + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/parser/Makefile 2004-09-03 00:57:05.138258128 -0700 @@ -0,0 +1,35 @@ + +.SUFFIXES= .l .y .c + +GENSRCS= parser.code.c +GENHDRS= y.t.h + +SRCS= ${GENSRCS} ${CSRCS} +CLEANFILES= ${GENSRCS} ${GENHDRS} y.output + +# Override default kernel CFLAGS. This is a userland app. +# PARS_CFLAGS:= -I/usr/include -I. -ldb +YFLAGS= -d -t -v -r -b parser + + +YACC=./yacc + +#ifdef DEBUG +CFLAGS+= -DDEBUG -g +YFLAGS+= -t -v -r -b parser +LFLAGS= -d +#endif + +#$(PROG): $(SRCS) $(GENHDRS) +# $(CC) $(PARS_CFLAGS) $(CFLAGS) $(CSRCS) -o $(PROG) +# $(CC) $(PARS_CFLAGS) $(CFLAGS) $(SRCS) -o $(PROG) + +clean: + rm -f $(CLEANFILES) $(PROG) + +parser.code.c: parser.y + $(YACC) $(YFLAGS) parser.y + + + + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/parser/pars.cls.h 2004-09-03 00:57:05.139257976 -0700 @@ -0,0 +1,242 @@ +/* + * Copyright 2001, 2002 by Hans Reiser, licensing governed by reiser4/README + */ + +/* + * definitions of common constants for lex component of parser.y + */ + + + +#define ERR -128 + + +typedef enum { + OK , + Blk , /* blank */ + Wrd , /* any symbol exept spec symbl */ + Int , /* numeric */ + + Ptr , /* pointer */ + + Pru , /* _pruner */ + + Stb , /* ` string begin */ + Ste , /* ' string end */ + Lpr , /* ( [ { */ + Rpr , /* ) ] } */ + Com , /* , */ + Mns , /* - */ + + + Les , /* < */ + Slh , /* / */ + + Lsq , /* [ */ + Rsq , /* ] */ + + Bsl , /* \ */ + + Lfl , /* { */ + Rfl , /* } */ + + Pip , /* | */ + Sp1 , /* : */ + Sp2 , /* ; */ + + Dot , /* . */ + + Sp4 , /* = */ + Sp5 , /* > */ + Sp6 , /* ? */ + Pls , /* + ???*/ + Res , /* */ + + Str , + ASG , + App , + Lnk , + Ap2 , + Nam , + LastState +} state; + +#define STRING_CONSTANT_EMPTY STRING_CONSTANT /* tmp */ + +static char ncl [256] = { + Blk, ERR, ERR, ERR, ERR, ERR, ERR, ERR, + ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, + ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, + ERR, ERR, ERR, ERR, ERR, ERR, ERR, ERR, + /* 32*/ + /* ! " # $ % & ' */ + Blk, Res, Res, Res, Res, Res, Res, Ste, + /* ( ) * + , - . / */ + Lpr, Rpr, Res, Pls, Com, Mns, Dot, Slh, + /* 0 1 2 3 4 5 6 7 */ + Int, Int, Int, Int, Int, Int, Int, Int, + /* 8 9 : ; < = > ? */ + Int, Int, Sp1, Sp2, Les, Sp4, Sp5, Sp6, + + /* 64*/ + /* @ A B C D E F G */ + Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, + /* H I J K L M N O */ + Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, + /* P Q R S T U V W */ + Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, + /* X Y Z [ \ ] ^ _ */ + Wrd, Wrd, Wrd, Lsq, Bsl, Rsq, Res, Pru, + /* 96*/ + /* ` a b c d e f g */ + Stb, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, + /* h i j k l m n o */ + Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, + /* p q r s t u v w */ + Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, + /* x y z { | } ~ */ + Wrd, Wrd, Wrd, Lfl, Pip, Rfl, Wrd, ERR, + + /*128*/ + Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, + Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, + Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, + Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, + /*160*/ + Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, + Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, + Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, + Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, + /*192*/ + Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, + Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, + Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, + Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, + /*224*/ + Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, + Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, + Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, + Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, Wrd, ERR +}; + +struct lexcls { + int term; + char c[32]; +} ; + +static struct { + char * wrd; + int class; +} +pars_key [] = { + { "and" , AND }, + { "else" , ELSE }, + { "eq" , EQ }, + { "ge" , GE }, + { "gt" , GT }, + { "if" , IF }, + { "le" , LE }, + { "lt" , LT }, + { "ne" , NE }, + { "not" , NOT }, + { "or" , OR }, + { "then" , THEN }, + { "tw/" , TRANSCRASH } +}; + + +struct lexcls lexcls[] = { +/* +.. a 1 _ ` ' ( ) , - < / [ ] \ { } | ; : . = > ? + +Blk Wrd Int Ptr Pru Stb Ste Lpr Rpr Com Mns Les Slh Lsq Rsq Bsl Lfl Rfl Pip Sp1 Sp2 Dot Sp4 Sp5 Sp6 Pls ... */ +[Blk]={ 0, {0, +Blk,Wrd,Int,Ptr,Pru,Str,ERR, Lpr,Rpr,Com,Mns,Les,Slh,Lsq,Rsq, Bsl,Lfl,Rfl,Pip,Sp1,Sp2,Dot,Sp4, Sp5,Sp6,ERR,ERR,ERR,ERR,ERR,ERR}}, +[Wrd]={ WORD, {0, +OK ,Wrd,Wrd,Wrd,Wrd,Wrd,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , Bsl,OK ,OK ,OK ,OK ,OK ,Wrd,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, + +[Int]={ WORD, {0, +OK ,Wrd,Int,Wrd,Wrd,OK ,OK , OK ,OK ,OK ,Wrd,OK ,OK ,OK ,OK , Wrd,OK ,OK ,OK ,OK ,OK ,Wrd,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, + +[Ptr]={ WORD,{0, +OK ,Wrd,Wrd,Wrd,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , Wrd,OK ,OK ,OK ,OK ,OK ,Wrd,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, +[Pru]={ P_RUNNER,{0, +OK ,Pru,Pru,Pru,Pru,OK ,OK , OK ,OK ,OK ,Pru,OK ,OK ,OK ,OK , Pru,OK ,OK ,OK ,OK ,OK ,Pru,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, +/* +[Stb]={ STRING_CONSTANT_EMPTY, {1, +Str,Str,Str,Str,Str,Str,OK , Str,Str,Str,Str,Str,Str,Str,Str, Str,Str,Str,Str,Str,Str,Str,Str, Str,Str,Str,Str,Str,Str,Str,Str}}, +*/ +[Stb]={ 0, {1, +Stb,Stb,Stb,Stb,Stb,Stb,Str, Stb,Stb,Stb,Stb,Stb,Stb,Stb,Stb, Stb,Stb,Stb,Stb,Stb,Stb,Stb,Stb, Stb,Stb,Stb,Stb,Stb,Stb,Stb,Stb}}, + +[Ste]={ 0, {0, +ERR,ERR,ERR,ERR,ERR,ERR,ERR, ERR,ERR,ERR,ERR,ERR,ERR,ERR,ERR, ERR,ERR,ERR,ERR,ERR,ERR,ERR,ERR, ERR,ERR,ERR,ERR,ERR,ERR,ERR,ERR}}, +[Lpr]={ L_BRACKET /*L_PARENT*/,{0, +OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, +[Rpr]={ R_BRACKET,{0, +OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, +[Com]={ COMMA,{0, +OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, +[Mns]={ 0,{0, +ERR,ERR,ERR,ERR,ERR,ERR,ERR, ERR,ERR,ERR,ERR,ERR,ERR,ERR,ERR, ERR,ERR,ERR,ERR,ERR,ERR,ERR,ERR, Lnk,ERR,ERR,ERR,ERR,ERR,ERR,ERR}}, +[Les]{ LT,{0, +OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,ASG,App,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,Nam, OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, + +[Slh]={ SLASH,{0, +OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,Slh,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, + +[Lsq]={ L_BRACKET,{0, +OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, +[Rsq]={ R_BRACKET,{0, +OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, +[Bsl]={ 0,{0, +Wrd,Wrd,Wrd,Wrd,Wrd,Wrd,Wrd, Wrd,Wrd,Wrd,Wrd,Wrd,Wrd,Wrd,Wrd, Wrd,Wrd,Wrd,Wrd,Wrd,Wrd,Wrd,Wrd, Wrd,Wrd,Wrd,Wrd,Wrd,Wrd,Wrd,Wrd}}, +[Lfl]={ L_BRACKET,{0, /*mast removed*/ +OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, +[Rfl]={ R_BRACKET,{0, /*mast removed*/ +OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, +[Pip]={ 0,{0, +OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, +[Sp1]={ 0,{0, +OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, +[Sp2]={ SEMICOLON,{0, +OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, +[Dot]={ WORD,{0, +OK ,Wrd,Wrd,Wrd,Wrd,Wrd,Wrd, OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,Dot,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, +[Sp4]={ 0,{0, +OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, +[Sp5]={ 0,{0, +OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, + +[Sp6]={ 0,{0, +OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, +[Pls]={ PLUS,{0, +OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, +[Res]={ 0,{0, +OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, +/* +[Str]={ STRING_CONSTANT,{1, +Str,Str,Str,Str,Str,Str,OK , Str,Str,Str,Str,Str,Str,Str,Str, Str,Str,Str,Str,Str,Str,Str,Str, Str,Str,Str,Str,Str,Str,Str,Str}}, +*/ +[Str]={ STRING_CONSTANT,{1, +OK ,OK ,OK ,OK ,OK ,OK ,Stb, OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, + + +[ASG]={ L_ASSIGN,{0, +OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, +[App]={ L_ASSIGN,{0, +ERR,ERR,ERR,ERR,ERR,ERR,ERR, ERR,ERR,ERR,ERR,Ap2,ERR,ERR,ERR, ERR,ERR,ERR,ERR,ERR,ERR,ERR,ERR, ERR,ERR,ERR,ERR,ERR,ERR,ERR,ERR}}, +/* +OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,Ap2,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, +*/ +[Lnk]={ L_SYMLINK,{0, +OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , ERR ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, + +[Ap2]={ L_APPEND,{0, +OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }}, + +[Nam]={ NAMED,{0, +OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK , OK ,OK ,OK ,OK ,OK ,OK ,OK ,OK }} + +}; + + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/parser/parser.code.c 2004-09-03 00:57:05.141257672 -0700 @@ -0,0 +1,484 @@ +#ifndef lint +/*static char yysccsid[] = "from: @(#)yaccpar 1.9 (Berkeley) 02/21/93";*/ +static char yyrcsid[] = "$Id: skeleton.c,v 1.4 1993/12/21 18:45:32 jtc Exp $\n 2002/10/22 VD reiser4"; +#endif +#define YYBYACC 1 +#define YYMAJOR 1 +#define YYMINOR 9 +#define yyclearin (yychar=(-1)) +#define yyerrok (yyerrflag=0) +#define YYRECOVERING (yyerrflag!=0) +#define YYPREFIX "yy" +#line 9 "fs/reiser4/parser/parser.y" +typedef union +{ + long charType; + expr_v4_t * expr; + wrd_t * wrd; +} YYSTYPE; +#line 20 "fs/reiser4/parser/parser.code.c" +#define L_BRACKET 257 +#define R_BRACKET 258 +#define WORD 259 +#define P_RUNNER 260 +#define STRING_CONSTANT 261 +#define TRANSCRASH 262 +#define SEMICOLON 263 +#define COMMA 264 +#define L_ASSIGN 265 +#define L_APPEND 266 +#define L_SYMLINK 267 +#define PLUS 268 +#define SLASH 269 +#define INV_L 270 +#define INV_R 271 +#define EQ 272 +#define NE 273 +#define LE 274 +#define GE 275 +#define LT 276 +#define GT 277 +#define IS 278 +#define AND 279 +#define OR 280 +#define NOT 281 +#define IF 282 +#define THEN 283 +#define ELSE 284 +#define EXIST 285 +#define NAME 286 +#define UNNAME 287 +#define NAMED 288 +#define ROOT 289 +#define YYERRCODE 256 +#define YYTABLESIZE 422 +#define YYFINAL 5 +#ifndef YYDEBUG +#define YYDEBUG 0 +#endif +#define YYMAXTOKEN 289 +#if defined(YYREISER4_DEF) +#define extern static +#endif +extern short yylhs[]; +extern short yylen[]; +extern short yydefred[]; +extern short yydgoto[]; +extern short yysindex[]; +extern short yyrindex[]; +extern short yygindex[]; +extern short yytable[]; +extern short yycheck[]; +#if YYDEBUG +extern char *yyname[]; +extern char *yyrule[]; +#endif +#if defined(YYREISER4_DEF) +#define YYSTACKSIZE 500 +#define YYMAXDEPTH 500 +#define yydebug ws->ws_yydebug +#define yynerrs ws->ws_yynerrs +#define yyerrflag ws->ws_yyerrflag +#define yychar ws->ws_yychar +#define yyssp ws->ws_yyssp +#define yyvsp ws->ws_yyvsp +#define yyval ws->ws_yyval +#define yylval ws->ws_yylval +#define yyss ws->ws_yyss +#define yyvs ws->ws_yyvs +#define yystacksize ws->ws_yystacksize +#else +#ifdef YYSTACKSIZE +#undef YYMAXDEPTH +#define YYMAXDEPTH YYSTACKSIZE +#else +#ifdef YYMAXDEPTH +#define YYSTACKSIZE YYMAXDEPTH +#else +#define YYSTACKSIZE 500 +#define YYMAXDEPTH 500 +#endif +#endif +int yydebug; +int yynerrs; +int yyerrflag; +int yychar; +short *yyssp; +YYSTYPE *yyvsp; +YYSTYPE yyval; +YYSTYPE yylval; +short yyss[YYSTACKSIZE]; +YYSTYPE yyvs[YYSTACKSIZE]; +#define yystacksize YYSTACKSIZE +#endif +#line 160 "fs/reiser4/parser/parser.y" + + +#define yyversion "4.0.0" +#include "pars.cls.h" +#include "parser.tab.c" +#include "pars.yacc.h" +#include "lib.c" + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + End: +*/ +#line 132 "fs/reiser4/parser/parser.code.c" +#define YYABORT goto yyabort +#define YYREJECT goto yyabort +#define YYACCEPT goto yyaccept +#define YYERROR goto yyerrlab +int +#if defined(YYREISER4_DEF) +yyparse(struct reiser4_syscall_w_space * ws) +#else +#if defined(__STDC__) +yyparse(void) +#else +yyparse() +#endif +#endif +{ + register int yym, yyn, yystate; +#if YYDEBUG + register char *yys; + static char *getenv(); + + if (yys = getenv("YYDEBUG")) + { + yyn = *yys; + if (yyn >= '0' && yyn <= '9') + yydebug = yyn - '0'; + } +#endif + + yynerrs = 0; + yyerrflag = 0; + yychar = (-1); + + yyssp = yyss; + yyvsp = yyvs; + *yyssp = yystate = 0; + +yyloop: + if ((yyn = yydefred[yystate]) != 0) goto yyreduce; + if (yychar < 0) + { + if ((yychar = yylex()) < 0) yychar = 0; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, yystate, yychar, yys); + } +#endif + } + if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yychar) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, shifting to state %d\n", + YYPREFIX, yystate, yytable[yyn]); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate = yytable[yyn]; + *++yyvsp = yylval; + yychar = (-1); + if (yyerrflag > 0) --yyerrflag; + goto yyloop; + } + if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yychar) + { + yyn = yytable[yyn]; + goto yyreduce; + } + if (yyerrflag) goto yyinrecovery; +#if defined(YYREISER4_DEF) + yyerror(ws,11111,yystate,yychar); +#else + yyerror("syntax error"); +#endif +#ifdef lint + goto yyerrlab; +#endif +yyerrlab: + ++yynerrs; +yyinrecovery: + if (yyerrflag < 3) + { + yyerrflag = 3; + for (;;) + { + if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, error recovery shifting\ + to state %d\n", YYPREFIX, *yyssp, yytable[yyn]); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate = yytable[yyn]; + *++yyvsp = yylval; + goto yyloop; + } + else + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: error recovery discarding state %d\n", + YYPREFIX, *yyssp); +#endif + if (yyssp <= yyss) goto yyabort; + --yyssp; + --yyvsp; + } + } + } + else + { + if (yychar == 0) goto yyabort; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, error recovery discards token %d (%s)\n", + YYPREFIX, yystate, yychar, yys); + } +#endif + yychar = (-1); + goto yyloop; + } +yyreduce: +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, reducing by rule %d (%s)\n", + YYPREFIX, yystate, yyn, yyrule[yyn]); +#endif + yym = yylen[yyn]; + yyval = yyvsp[1-yym]; + switch (yyn) + { +case 1: +#line 79 "fs/reiser4/parser/parser.y" +{ yyval.charType = free_expr( ws, yyvsp[0].expr ); } +break; +case 2: +#line 83 "fs/reiser4/parser/parser.y" +{ yyval.expr = yyvsp[0].expr;} +break; +case 3: +#line 84 "fs/reiser4/parser/parser.y" +{ yyval.expr = const_to_expr( ws, yyvsp[0].wrd ); } +break; +case 4: +#line 85 "fs/reiser4/parser/parser.y" +{ yyval.expr = unname( ws, yyvsp[0].expr ); } +break; +case 5: +#line 86 "fs/reiser4/parser/parser.y" +{ yyval.expr = concat_expression( ws, yyvsp[-2].expr, yyvsp[0].expr ); } +break; +case 6: +#line 87 "fs/reiser4/parser/parser.y" +{ yyval.expr = list_expression( ws, yyvsp[-2].expr, yyvsp[0].expr ); } +break; +case 7: +#line 88 "fs/reiser4/parser/parser.y" +{ yyval.expr = list_async_expression( ws, yyvsp[-2].expr, yyvsp[0].expr ); } +break; +case 8: +#line 89 "fs/reiser4/parser/parser.y" +{ yyval.expr = yyvsp[0].expr; level_down( ws, IF_STATEMENT, IF_STATEMENT ); } +break; +case 9: +#line 91 "fs/reiser4/parser/parser.y" +{ yyval.expr = assign( ws, yyvsp[-2].expr, yyvsp[0].expr ); } +break; +case 10: +#line 92 "fs/reiser4/parser/parser.y" +{ yyval.expr = assign( ws, yyvsp[-2].expr, yyvsp[0].expr ); } +break; +case 11: +#line 93 "fs/reiser4/parser/parser.y" +{ yyval.expr = assign_invert( ws, yyvsp[-4].expr, yyvsp[-1].expr ); } +break; +case 12: +#line 94 "fs/reiser4/parser/parser.y" +{ yyval.expr = symlink( ws, yyvsp[-2].expr, yyvsp[0].expr ); } +break; +case 13: +#line 103 "fs/reiser4/parser/parser.y" +{ yyval.expr = if_then_else( ws, yyvsp[-3].expr, yyvsp[-2].expr, yyvsp[0].expr ); } +break; +case 14: +#line 104 "fs/reiser4/parser/parser.y" +{ yyval.expr = if_then( ws, yyvsp[-1].expr, yyvsp[0].expr) ; } +break; +case 15: +#line 108 "fs/reiser4/parser/parser.y" +{ yyval.expr = yyvsp[0].expr; } +break; +case 16: +#line 111 "fs/reiser4/parser/parser.y" +{ level_up( ws, IF_STATEMENT ); } +break; +case 17: +#line 115 "fs/reiser4/parser/parser.y" +{ yyval.expr = not_expression( ws, yyvsp[0].expr ); } +break; +case 18: +#line 116 "fs/reiser4/parser/parser.y" +{ yyval.expr = check_exist( ws, yyvsp[0].expr ); } +break; +case 19: +#line 117 "fs/reiser4/parser/parser.y" +{ yyval.expr = compare_EQ_expression( ws, yyvsp[-2].expr, yyvsp[0].expr ); } +break; +case 20: +#line 118 "fs/reiser4/parser/parser.y" +{ yyval.expr = compare_NE_expression( ws, yyvsp[-2].expr, yyvsp[0].expr ); } +break; +case 21: +#line 119 "fs/reiser4/parser/parser.y" +{ yyval.expr = compare_LE_expression( ws, yyvsp[-2].expr, yyvsp[0].expr ); } +break; +case 22: +#line 120 "fs/reiser4/parser/parser.y" +{ yyval.expr = compare_GE_expression( ws, yyvsp[-2].expr, yyvsp[0].expr ); } +break; +case 23: +#line 121 "fs/reiser4/parser/parser.y" +{ yyval.expr = compare_LT_expression( ws, yyvsp[-2].expr, yyvsp[0].expr ); } +break; +case 24: +#line 122 "fs/reiser4/parser/parser.y" +{ yyval.expr = compare_GT_expression( ws, yyvsp[-2].expr, yyvsp[0].expr ); } +break; +case 25: +#line 123 "fs/reiser4/parser/parser.y" +{ yyval.expr = compare_OR_expression( ws, yyvsp[-2].expr, yyvsp[0].expr ); } +break; +case 26: +#line 124 "fs/reiser4/parser/parser.y" +{ yyval.expr = compare_AND_expression( ws, yyvsp[-2].expr, yyvsp[0].expr ); } +break; +case 27: +#line 128 "fs/reiser4/parser/parser.y" +{ goto_end( ws );} +break; +case 28: +#line 132 "fs/reiser4/parser/parser.y" +{ yyval.expr = yyvsp[0].expr;} +break; +case 29: +#line 133 "fs/reiser4/parser/parser.y" +{ yyval.expr = target_name( yyvsp[-2].expr, yyvsp[0].expr );} +break; +case 30: +#line 137 "fs/reiser4/parser/parser.y" +{ yyval.expr = pars_expr( ws, yyvsp[-1].expr, yyvsp[0].expr ) ; } +break; +case 31: +#line 138 "fs/reiser4/parser/parser.y" +{ yyval.expr = pars_expr( ws, yyvsp[-2].expr, yyvsp[0].expr ) ; } +break; +case 32: +#line 142 "fs/reiser4/parser/parser.y" +{ yyval.expr = pars_lookup_root( ws ) ; } +break; +case 33: +#line 143 "fs/reiser4/parser/parser.y" +{ yyval.expr = pars_lookup_curr( ws ) ; } +break; +case 34: +#line 147 "fs/reiser4/parser/parser.y" +{ yyval.expr = lookup_word( ws, yyvsp[0].wrd ); } +break; +case 35: +#line 148 "fs/reiser4/parser/parser.y" +{ yyval.expr = yyvsp[-1].expr; level_down( ws, yyvsp[-2].charType, yyvsp[0].charType );} +break; +case 36: +#line 152 "fs/reiser4/parser/parser.y" +{ yyval.charType = yyvsp[0].charType; level_up( ws, yyvsp[0].charType ); } +break; +#line 425 "fs/reiser4/parser/parser.code.c" + } + yyssp -= yym; + yystate = *yyssp; + yyvsp -= yym; + yym = yylhs[yyn]; + if (yystate == 0 && yym == 0) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: after reduction, shifting from state 0 to\ + state %d\n", YYPREFIX, YYFINAL); +#endif + yystate = YYFINAL; + *++yyssp = YYFINAL; + *++yyvsp = yyval; + if (yychar < 0) + { + if ((yychar = yylex()) < 0) yychar = 0; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, YYFINAL, yychar, yys); + } +#endif + } + if (yychar == 0) goto yyaccept; + goto yyloop; + } + if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yystate) + yystate = yytable[yyn]; + else + yystate = yydgoto[yym]; +#if YYDEBUG + if (yydebug) + printf("%sdebug: after reduction, shifting from state %d \ +to state %d\n", YYPREFIX, *yyssp, yystate); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate; + *++yyvsp = yyval; + goto yyloop; +yyoverflow: +#if defined(YYREISER4_DEF) + yyerror(ws,101); /*yacc stack overflow*/ +#else + yyerror("yacc stack overflow"); +#endif +yyabort: + return (1); +yyaccept: + return (0); +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/parser/parser.doc 2004-09-03 00:57:05.144257216 -0700 @@ -0,0 +1,388 @@ +/* Parser for the reiser4() system call */ + +/* Takes a string and parses it into a set of commands which are + executed. */ + +/* + +If you want to read about where future versions of this syntax will +go, and what the grand scheme is, please go to +www.namesys.com/future_vision.html. + +Names resolve into sets of keys. In Reiser4, the sets of keys all +consist of exactly one key, but this will change in future versions. + +Keys are not immutable objectids ala inode numbers. The use of +immutable objectids that all objects could be found by was the +original architecture, and before coding was started this was realized +to require lower performance due to creating immutable, and therefor +poor, locality of reference when resolving them. Keys currently do +contain unique objectids, but this objectid does not suffice for +finding the object. + +Name compounders construct names from subnames. / and [] ([] is not +implemented in reiser4, see www.namesys.com/future_vision.html for +what it will do in a later version) are name compounders. Name +compounders can use any name which can be resolved into a key as a +subname. This provides "closure", in which the type of the result of +every name operation is the same as the type of what the name +operators operate on. (Persons who create abstract models tend to +place a high value on achieving closure in their design. For more +about closure, read www.namesys.com/future_vision.html.) + +A/B indicates that B is to be passed to A, and A will resolve its +meaning. This is consistent with Unix usage of /. B is considered +a subname of A in this example. + +Reiser4 supports a plugin that implements Unix regular files (regfile), +and a plugin that implements Unix regular directories (regdir). (NIKITA-FIXME-HANS: use those names in the code) + +Plugins may resolve a subname by invoking a plugin of another object, +or by invoking methods built into themselves. + +Special characters are whitespace plus []{}()/\,:;*$@!`' and keywords +are <- and -> + +<-, ->, are assignment operators. + + +'A<-B' uses B's read method to read B, and uses A'S write method to +write to A what was read from B. It is a copy command similar to +sendfile(). + +The righthand side of an assignment defines a flow. We calculate the flow, +and then invoke the the write method defined by the lefthand side of +the assignment. + +Example: + +A<-B + +assigns the contents of the object named B to A, overwriting the contents of A if A exists. + +` and ' indicate that all special characters between them should be +ignored and parsed as a single string. That is, A<-`some text' causes A to have +contents equal to a file named `some text'. Quotes are allowed to nest. + +" indicates that the next word is inlined text. Sorry, " is the symbol least useful for something else, so it got used. + +A<-"(this is a string not a name of a file) // German style quoting: ,,ksdajfhkasdh`` + +assigns (sans the single quotes) the string `this is a string not a name of a file' to A. + +A<-"`I think that using " in a language for delimiting quoting is bad style because delimiters should be matching pairs not matching identical singletons if nesting is to work at all.' + +assigns the string `I think that using " in a language for delimiting quoting is bad style because delimiters should be matching pairs not matching identical singletons if nesting is to work at all.' to A + +A<<-B + +appends file B to file A + +We need to define multiple aspects of the object when creating it. +Additionally, we need to assign default values to those aspects of the +definition not defined. The problem arises when we have a multi-part +definition. We should avoid assigning one part, then assigning +default values for all other parts, then overwriting those default +values, some of which actually cannot be overwritten (e.g. pluginid). + +This means we need to name the object, and then perform multiple +assignments in its creation. + +(x_ and )x_ where x is any sequence (including the null sequence) of +non-special characters, are `named parenthesis'. They have the usual meaning of +parenthesis in most languages, and delimit what should be treated as a +single unit by operators. If you use named parenthesis you can avoid +the "LISP bird nest" effect. The disadvantage is that if you leave +off the whitespace following the open parenthesis you will get an +unintended effect. Note that there must be no space between ( and x. + +Referencing the contents of parenthesis by the name of the parenthesis +is planned in later versions.. + +It is an unenforced but encouraged style convention that subnames which contain +meta-information about the object, and pseudo-files that implement +methods of the object, begin with `..'. IT IS NOT A REQUIREMENT +THAT THEY START WITH `..', READ THAT AGAIN! Sorry, got tired of the complaints about +the non-existent requirement. It all depends on how you write your plugins that +use the meta-information whether the meta-data starts with `..'. + +Since what is meta-information, what is a method of the object, and +what is contained information, or methods of sub-objects, are not +necessarily always inherently susceptible to precise natural +distinction, and since we desire to allow users maximal stylistic +freedom to extend reiser4 into emulating other syntaxes, this is only +an optional plugin design convention for use when appropriate. + +One can specify whether a file is listed by readdir in reiser4. Using +that feature, subnames of files containing meta-information about +other files are by convention not listed by readdir, but can be listed +by using the command reiser4("A_listing<-A/..list"), and then reading +the file A_listing. + +For instance, if A is a regfile or regdir, then A/..owner resolves to +a file containing the owner of A, and reading the A directory shows no +file named ..owner. More generally, all of the fields returned by +stat() have a corresponding name of the form A/..field_name for all +regfiles and regdirs. The use of'..' avoids clashes between method +names and filenames. More extreme measures could be taken using +something more obscure than '..' as a prefix, but I remember that +Clearcase and WAFL never really had much in the way of problems with +namespace collisions affecting users seriously, so I don't think one +should excessively inconvenience a syntax for such reasons. + +DEMIDOV-FIXME-HANS: the paragraph below conflicts with the above +*A (similar to C language dereference) means take the contents of +that object which A is the name for, and treat those contents as a name. + + +*`A B' is a reference to a file whose name consists of the characters +A and a space and a B. + + +A;B indicates that B is not to be executed until A completes. So, `/' +orders subnames within a compound name, and `;' orders operations. + + +A,B indicates that A and B are independent of each other and +unordered. + +A/B indicates that the plugin for A is to be passed B, +and asked to handle it in its way, whatever that way is. + +/ and \ are considered equivalent, as a kindness to former windows users + +C/..invert<-A +"(some text)+ B + +indicates that C when read shall return the contents of A followed by +'some text' as a delimiter followed by the contents of B. + +if A and B are object expressions then + A+B is object expression + A\B is object expression + A<-B is possible operation + + +So, let us discuss the following example: + +Assume 357 is the user id of Michael Jackson. + +The following 3 expressions are equivalent: + +ascii_command = "/home/teletubbies/(..new(..name<-glove_location, ..object_t<-audit/regular, ..perm_t<-acl); glove_location/..acl<-( uid<-357, access<-denied ); glove_location/..audit<-mailto<-teletubbies@pbs.org; glove_location<-'we stole it quite some number of years ago, and put it in the very first loot pile (in the hole near the purple flower.')"; + +ascii_command = "/home/teletubbies/(glove_location<-( ..object_t<-audit/regular, ..perm_t<-acl); glove_location/..acl<- ( uid<-357, access<-denied ); glove_location/..audit<-mailto<-teletubbies@pbs.org; glove_location<-'we stole it quite some number of years ago, and put it in the very first loot pile (in the hole near the purple flower)')"; + + +ascii_command = "/home/teletubbies/(glove_location<-( ..object_t<-audit/regular, ..perm_t<-acl); glove_location / ( ..acl<-(uid<-357, access<-denied) ; ..audit<-mailto<-teletubbies@pbs.org); glove_location<-'we stole it quite some number of years ago, and put it in the very first loot pile (in the hole near the purple flower).')"; + +DEMIDOV-FIXME-HANS: what is the meaning of the line below, and should it be ..new rather than new +a/b/(new/(name<-"new_file_name"; type<-"regular file"; perm_t<-"acl"); new_file_name/acl<- ( uid<-"357", access<-"denied" )) + +DEMIDOV-FIXME-HANS: what is the meaning of ..anon and backing? +ascii_command = + "/home/teletubbies/glove_location<- + ( (..object_t<-audit, ..perm_t<-acl) ; + ..acl<- ( uid<-'357', access<-denied ); + ..audit/(backing<-..anon<=(..object_t<-regular); // lookup<-/home/teletubbies/some-existing-file), + log<-(mailto<-teletubbies@pbs.org)); + ..body<-'we stole it quite some number of years ago, and put it in the very first loot pile (in the hole near the purple flower)';)"; + +(a b) +result<-/subject/[elves strike] +(result /subject/[elves strike]) +/subject/[elves strike]->result + + + + +DEMIDOV-FIXME-HANS: cleanup the explanation below +The components of this example have the following meanings: +/home/teletubbies/ - directory name +/(..name - specifies that its argument is the name of the new file - parameter for ..new plugin +/glove_location, - name of new file - parameter for name submethod of ..new method +..object_t - name of submethod that assigns object type to new files - parameter for ..new plugin +/audit - plugin for file +/regular, - plugin for backing store for audit plugin +..perm_t - security plugin to be assigned +) - end of parameters for ..new plugin +; - next system call +glove_location - file name +/..acl - plugin ..acl +( - begin parameters for ..new plugin of ..acl plugin +uid - plugin of ..acl plugin +/357 - its value(parameter) +, - +access - .. +/denied - value to assign +) - end of parameter list +) - ? unbalanced brakes +; +..audit - plugin - file is unknown! +/..new +/mailto +/"teletubbies@pbs.org" +; +glove_location_file - file name +/ + +"we stole it quite some number of years ago, and put it in the very first loot pile (in the hole near the purple flower)." - body of file + +reiser4(&ascii_command, ascii_command_length, stack_binary_parameters_referenced_in_ascii_command, stack_length); + + +*/ + +/* + + w=\$v v=\$u u=5 z=\$w+$w + echo $z + eval echo $z + eval eval echo $z + +eval eval eval echo $z + +result is: + +$w+$v +$v+$u +$u+5 +5+5 + +tw/transcrash_33[ /home/reiser/(a <- b, c <- d) ] + + chgrp -- changes group ownership + chown -- changes ownership + chmod -- changes permissions + cp -- copies + dd -- copies and converts + df -- shows filesystem disk usage. + dir -- gives brief directory listing + du -- shows disk usage + ln -- creates links + ls -- lists directory contents + mkdir -- creates directories + mkfifo -- creates FIFOs (named pipes) + mknod -- creates special files + mv -- renames + rm -- removes (deletes) + rmdir -- removes empty directories + shred -- deletes a file securely + sync -- synchronizes memory and disk +*/ + + + +/* + + +Assignment, and transaction, will be the commands supported in Reiser4(); more commands will appear in Reiser5. -> and <- will be the assignment operators. + +The amount transferred by an assignment is the minimum of the size of the left hand side and the size of the right hand side. This amount is usually made one of the return values. + + * lhs (assignment target) values: + + /..process/..range/(first_byte<-(loff_t),last_byte<-(loff_t),bytes_written<-(ssize_t*) ) + assigns (writes) to the buffer starting at address first_byte in the process address space, ending at last_byte, with the number of bytes actually written + (The assignment source may be smaller or larger than the assignment target.) being written to address bytes_written. + Representation of first_byte,last_byte, and bytes_written is left to the coder to determine. + It is an issue that will be of much dispute and little importance. + Notice / is used to indicate that the order of the operands matters; see www.namesys.com/future_vision.html for details of why this is appropriate syntax design. + Note the lack of a file descriptor. + + /filename + assigns to the file named filename, wholly obliterating its body with what is assigned. + + /filename/..range/(first_byte<-(loff_t),last_byte<-(loff_t),bytes_written<-(ssize_t*) ) + writes to the body, starting at first_byte, ending not past last_byte, + recording number of bytes written in bytes_written + + /filename/..range/(first_byte<-(loff_t),bytes_written<-(ssize_t*) ) + writes to the body starting at offset, recording number of bytes written in bytes_written + + * rhs (assignment source) values: + + /..process/..range/(first_byte<-(loff_t),last_byte<-(loff_t),bytes_read<-(ssize_t*) ) + reads from the buffer starting at address first_byte in the process address space, ending at last_byte. + The number of bytes actually read (assignment source may be smaller or larger than assignment target) is written to address bytes_read. + Representation of first_byte, last_byte, and bytes_read is left to the coder to determine, as it is an issue that will be of much dispute and little importance. + + /filename + reads the entirety of the file named filename. + + /filename/..range/(first_byte<-(loff_t),last_byte<-(loff_t),bytes_read<-(ssize_t*) ) + reads from the body, starting at first_byte, ending not past last_byte, + recording number of bytes read in bytes_read + + /filename/..range/(first_byte<-(loff_t),bytes_read<-(ssize_t*) ) + reads from the body starting at offset until the end, recording number of bytes read in bytes_read + + /filename/..stat/owner + reads from the ownership field of the stat data (stat data is that which is returned by the + stat() system call (owner, permissions, etc.) and stored on a per file basis by the FS.) + + + + + +*/ + + + + +/* + +example: + + + + /path0/path1/filename/..range/(offset<-100,bytes_written<-0xff001258,last_byte<-256)<-/path0/path2/filename/..range/(first_byte<-0,bytes_readed<-0xff001250) + + /path0/(path1/filename/..range/(offset<-100,bytes_written<-0xff001258,last_byte<-256)<-path2/filename/..range(first_byte<-0,bytes_readed<-0xff001250) ) + + /path0/(path1/filename/..range/(100,256)<-path2/filename/..range(0,256) ) + + + ? + /path0/path1/filename/..range/(offset<-100,bytes_written<-0xff001258),last_byte<-256,/path0/path2/filename/..range(first_byte<-0,p_bytes_readed<-0xff001250) + +ssize_t bytes_readed; + +sprintf( string_pointer_bytes_read, "%8.8p", &bytes_readed ); + + */ + + +examples: + + +.....b/(new/(name<-"new_file_name"; type<-regular_file; permition<-acl); new_file_name/acl/( uid<-"357", access<-denied );new_file_name<-/etc/passwd) + +where: + b is a directory +we have lnode for it.and make: +b_lnode->lookup(new) +it that meen we find lnode for directory plugin "new" + and then we find new_lnode->lookup(name). this is lnode for name of new file and we assign to it string constant "new_file_name". + + then we find new_lnode->lookup(type). this is lnode of type of new file + then we find new_lnode->lookup(regular_file) this is lnode of constants of types of plugin "new" for regular file + then we copy contens regular_file_lnode throuse tube to type_lnode. + + then we find new_lnode->lookup(permition). this is lnode of type of permition of new file + then we find new_lnode->lookup(acl). this is lnode of constants of type of permition of "new" plugin, correcponding to acl + then we copy contens acl_lnode throuse tube to permition_lnode. + +then we find b_lnode->lookup(new_file_name). this is lnode for new file we jast created. +then we find new_file_name_lnode->lookup(acl) . this is lnode of acl plugin . + then we find acl_lnode->lookup(uid)this is lnode for uid field of acl and assign to it string constant "357". + then we find acl_lnode->lookup(access). + then we find acl_lnode->lookup(denied). + then we copy contens denied_lnode throuse tube to access_lnode. + +then we find root_lnode->lookup(etc), +then we find etc_lnode->lookup(passwd) +then we read contens passwd_lnode throuse tube and write to new_file_name_lnode lnode. + +ok. command string is executed. + + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/parser/parser.h 2004-09-03 00:57:05.146256912 -0700 @@ -0,0 +1,344 @@ +/* + * Copyright 2001, 2002 by Hans Reiser, licensing governed by reiser4/README + */ + +/* + * definitions of common constants and data-types used by + * parser.y + */ + + /* level type defines */ + +#include "../forward.h" +#include "../debug.h" +#include "../dformat.h" +#include "../key.h" +#include "../type_safe_list.h" +#include "../plugin/plugin_header.h" +#include "../plugin/item/static_stat.h" +#include "../plugin/item/internal.h" +#include "../plugin/item/sde.h" +#include "../plugin/item/cde.h" +#include "../plugin/item/extent.h" +#include "../plugin/item/tail.h" +#include "../plugin/file/file.h" +#include "../plugin/symlink.h" +#include "../plugin/dir/hashed_dir.h" +#include "../plugin/dir/dir.h" +#include "../plugin/item/item.h" +#include "../plugin/node/node.h" +#include "../plugin/node/node40.h" +#include "../plugin/security/perm.h" +#include "../plugin/space/bitmap.h" +#include "../plugin/space/space_allocator.h" +#include "../plugin/disk_format/disk_format40.h" +#include "../plugin/disk_format/disk_format.h" + +#include /* for struct super_block, address_space */ +#include /* for struct page */ +#include /* for struct buffer_head */ +#include /* for struct dentry */ +#include + +typedef enum { + TW_BEGIN, + ASYN_BEGIN, + CD_BEGIN, + OP_LEVEL, + NOT_HEAD, + IF_STATEMENT, + UNORDERED +} def; + +//#define printf(p1,...) PTRACE(ws,p1,...) +#define yylex() reiser4_lex(ws) +#define register +#define yyacc +//#define bizon + +#define PARSER_DEBUG + + +#if 1 +#define PTRACE(ws, format, ... ) \ +({ \ + ON_TRACE(TRACE_PARSE, "parser:%s %p %s: " format "\n", \ + __FUNCTION__, ws, (ws)->ws_pline, __VA_ARGS__); \ +}) +#else +#define PTRACE(ws, format, ... ) \ +({ \ + printk("parser:%s %p %s: " format "\n", \ + __FUNCTION__, ws, (ws)->ws_pline, __VA_ARGS__); \ +}) +#endif + +#define PTRACE1( format, ... ) \ +({ \ + ON_TRACE(TRACE_PARSE, "parser:%s " format "\n", \ + __FUNCTION__, __VA_ARGS__); \ +}) + + +#define ASSIGN_RESULT "assign_result" +#define ASSIGN_LENGTH "assign_length" + +#define SIZEFOR_ASSIGN_RESULT 16 +#define SIZEFOR_ASSIGN_LENGTH 16 + + + + + +typedef struct pars_var pars_var_t; +typedef union expr_v4 expr_v4_t; +typedef struct wrd wrd_t; +typedef struct tube tube_t; +typedef struct sourece_stack sourece_stack_t; + +typedef enum { + ST_FILE, + ST_EXPR, + ST_DE, + ST_WD, + ST_DATA +} stack_type; + +typedef enum { + noV4Space, + V4Space, + V4Plugin +} SpaceType; + +typedef enum { + CONCAT, + COMPARE_EQ, + COMPARE_NE, + COMPARE_LE, + COMPARE_GE, + COMPARE_LT, + COMPARE_GT, + COMPARE_OR, + COMPARE_AND, + COMPARE_NOT +} expr_code_type; + + + /* sizes defines */ +#define FREESPACESIZE_DEF PAGE_SIZE*4 +#define FREESPACESIZE (FREESPACESIZE_DEF - sizeof(char*)*2 - sizeof(int) ) + +#define _ROUND_UP_MASK(n) ((1UL<<(n))-1UL) + +#define _ROUND_UP(x,n) (((long)(x)+_ROUND_UP_MASK(n)) & ~_ROUND_UP_MASK(n)) + +// to be ok for alpha and others we have to align structures to 8 byte boundary. + + +#define ROUND_UP(x) _ROUND_UP((x),3) + + + +struct tube { + int type_offset; + char * offset; /* pointer to reading position */ + size_t len; /* lenth of current operation + (min of (max_of_read_lenth and max_of_write_lenth) )*/ + long used; + char * buf; /* pointer to bufer */ + loff_t readoff; /* reading offset */ + loff_t writeoff; /* writing offset */ + + sourece_stack_t * last; /* work. for special case to push list of expressions */ + sourece_stack_t * next; /* work. for special case to push list of expressions */ + sourece_stack_t * st_current; /* stack of source expressions */ + pars_var_t * target; + struct file *dst; /* target file */ +}; + +struct wrd { + wrd_t * next ; /* next word */ + struct qstr u ; /* u.name is ptr to space */ +}; + + +struct path_walk { + struct dentry *dentry; + struct vfsmount *mnt; +}; + + +/* types for vtype of struct pars_var */ +typedef enum { + VAR_EMPTY, + VAR_LNODE, + VAR_TMP +}; + +typedef struct pars_var_value pars_var_value_t; + +struct pars_var { + pars_var_t * next ; /* next */ + pars_var_t * parent; /* parent */ + wrd_t * w ; /* name: pair (parent,w) is unique */ + pars_var_value_t * val; +}; + +struct pars_var_value { + pars_var_value_t * prev; + pars_var_value_t * next_level; + pars_var_t * host; + pars_var_t * associated; + int vtype; /* Type of value */ + union { + lnode * ln; /* file/dir name lnode */ + char *data; /* ptr to data in mem (for result of assign) */ + } u; + int count; /* ref counter */ + size_t off; /* current offset read/write of object */ + size_t len; /* length of sequence of bytes for read/write (-1 no limit) */ + int vSpace ; /* v4 space name or not ??? */ + int vlevel ; /* level : lives of the name */ +} ; + +typedef struct expr_common { + __u8 type; + __u8 exp_code; +} expr_common_t; + +typedef struct expr_lnode { + expr_common_t h; + lnode *lnode; +} expr_lnode_t; + +typedef struct expr_flow { + expr_common_t h; + flow_t * flw; +} expr_flow_t; + +typedef struct expr_pars_var { + expr_common_t h; + pars_var_t * v; +} expr_pars_var_t; + + +typedef struct expr_wrd { + expr_common_t h; + wrd_t * s; +} expr_wrd_t; + +typedef struct expr_op3 { + expr_common_t h; + expr_v4_t * op; + expr_v4_t * op_l; + expr_v4_t * op_r; +} expr_op3_t; + +typedef struct expr_op2 { + expr_common_t h; + expr_v4_t * op_l; + expr_v4_t * op_r; +} expr_op2_t; + +typedef struct expr_op { + expr_common_t h; + expr_v4_t * op; +} expr_op_t; + +typedef struct expr_assign { + expr_common_t h; + pars_var_t * target; + expr_v4_t * source; +// expr_v4_t * (* construct)( lnode *, expr_v4_t * ); +} expr_assign_t; + +typedef struct expr_list expr_list_t; +struct expr_list { + expr_common_t h; + expr_list_t * next; + expr_v4_t * source; +} ; + +typedef enum { + EXPR_WRD, + EXPR_PARS_VAR, + EXPR_LIST, + EXPR_ASSIGN, + EXPR_LNODE, + EXPR_FLOW, + EXPR_OP3, + EXPR_OP2, + EXPR_OP +} expr_v4_type; + +union expr_v4 { + expr_common_t h; + expr_wrd_t wd; + expr_pars_var_t pars_var; + expr_list_t list; + expr_assign_t assgn; + expr_lnode_t lnode; + expr_flow_t flow; +// expr_op3_t op3; + expr_op2_t op2; + expr_op_t op; +}; + +/* ok this is space for names, constants and tmp*/ +typedef struct free_space free_space_t; + +struct free_space { + free_space_t * free_space_next; /* next buffer */ + char * freeSpace; /* pointer to free space */ + char * freeSpaceMax; /* for overflow control */ + char freeSpaceBase[FREESPACESIZE]; /* current buffer */ +}; + +struct sourece_stack { + sourece_stack_t * prev; + long type; /* type of current stack head */ + union { + struct file * file; + expr_v4_t * expr; +// struct dentry * de; /* ??????? what for */ + wrd_t * wd; + long * pointer; + } u; +}; + +typedef struct streg streg_t; + +struct streg { + streg_t * next; + streg_t * prev; + expr_v4_t * cur_exp; /* current (pwd) expression for this level */ + expr_v4_t * wrk_exp; /* current (work) expression for this level */ + pars_var_value_t * val_level; + int stype; /* cur type of level */ + int level; /* cur level */ +}; + + +static struct { + unsigned char numOfParam; + unsigned char typesOfParam[4] ; +} typesOfCommand[] = { + {0,{0,0,0,0}} +}; + +static struct { + void (* call_function)(void) ; + unsigned char type; /* describe parameters, and its types */ +} Code[] = { +}; + + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/parser/parser.tab.c 2004-09-03 00:57:05.147256760 -0700 @@ -0,0 +1,194 @@ +short yylhs[] = { -1, + 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 7, 7, 9, 11, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 10, 4, 4, 2, + 2, 5, 5, 3, 3, 1, +}; +short yylen[] = { 2, + 1, 1, 1, 2, 3, 3, 3, 1, 3, 3, + 5, 3, 4, 2, 2, 1, 2, 2, 3, 3, + 3, 3, 3, 3, 3, 3, 2, 1, 3, 2, + 3, 1, 0, 1, 3, 1, +}; +short yydefred[] = { 0, + 3, 32, 16, 0, 0, 0, 0, 0, 0, 8, + 0, 0, 4, 0, 0, 0, 0, 0, 36, 34, + 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, + 15, 31, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 27, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 35, 13, 0, 0, 0, 0, + 0, 0, 0, 0, 11, +}; +short yydgoto[] = { 5, + 21, 6, 22, 7, 8, 9, 10, 31, 11, 27, + 12, +}; +short yysindex[] = { -223, + 0, 0, 0, -223, 0, -266, -250, -199, -259, 0, + -283, -248, 0, -199, -258, -226, -223, -223, 0, 0, + -223, 0, -223, -223, -223, -223, -274, -223, -223, -201, + 0, 0, -240, -223, -236, -236, -236, -256, -236, -236, + 0, 0, -223, -259, -259, -223, -223, -223, -223, -223, + -223, -223, -223, -183, 0, 0, -259, -259, -259, -259, + -259, -259, -259, -259, 0, +}; +short yyrindex[] = { -191, + 0, 0, 0, -191, 0, 1, 0, 0, 57, 0, + 0, -191, 0, 0, -191, -191, -191, -191, 0, 0, + -191, 0, -191, -191, -191, -191, 139, -191, -191, 0, + 0, 0, -225, -191, 24, 47, 70, 0, 93, 116, + 0, 0, -191, -218, -214, -191, -191, -191, -191, -191, + -191, -191, -191, 0, 0, 0, -206, -200, -197, -196, + -194, -193, -283, -192, 0, +}; +short yygindex[] = { 0, + 0, 67, 78, 0, 0, 2, 0, 0, 0, 0, + 0, +}; +short yytable[] = { 26, + 2, 55, 14, 23, 24, 13, 23, 24, 25, 43, + 2, 25, 1, 30, 16, 17, 18, 35, 36, 37, + 2, 15, 38, 9, 39, 40, 41, 42, 14, 44, + 45, 25, 28, 3, 1, 54, 29, 1, 4, 29, + 29, 29, 2, 34, 56, 2, 10, 57, 58, 59, + 60, 61, 62, 63, 64, 3, 1, 19, 3, 20, + 4, 23, 24, 4, 17, 33, 25, 33, 18, 12, + 46, 47, 48, 49, 50, 51, 19, 52, 53, 23, + 24, 33, 20, 0, 25, 21, 22, 65, 23, 24, + 25, 32, 6, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, + 0, 0, 0, 2, 2, 28, 28, 28, 2, 0, + 0, 2, 2, 2, 2, 2, 2, 2, 0, 2, + 2, 9, 0, 2, 2, 0, 9, 9, 0, 0, + 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, + 9, 0, 9, 9, 10, 0, 9, 9, 0, 10, + 10, 0, 0, 0, 0, 0, 0, 10, 10, 10, + 10, 10, 10, 10, 0, 10, 10, 12, 0, 10, + 10, 0, 12, 12, 0, 0, 0, 0, 0, 0, + 12, 12, 12, 12, 12, 12, 12, 0, 12, 12, + 6, 0, 12, 12, 0, 6, 6, 0, 0, 0, + 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, + 0, 6, 6, 7, 0, 6, 6, 0, 7, 7, + 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, + 7, 7, 7, 0, 7, 7, 14, 0, 7, 7, + 0, 14, 14, 0, 0, 0, 14, 0, 0, 14, + 14, 14, 14, 14, 14, 14, 0, 14, 14, 0, + 0, 14, +}; +short yycheck[] = { 283, + 0, 258, 269, 263, 264, 4, 263, 264, 268, 284, + 269, 268, 261, 12, 265, 266, 267, 16, 17, 18, + 269, 288, 21, 0, 23, 24, 25, 26, 269, 28, + 29, 268, 281, 282, 261, 34, 285, 261, 287, 265, + 266, 267, 269, 270, 43, 269, 0, 46, 47, 48, + 49, 50, 51, 52, 53, 282, 0, 257, 282, 259, + 287, 263, 264, 287, 283, 257, 268, 259, 283, 0, + 272, 273, 274, 275, 276, 277, 283, 279, 280, 263, + 264, 15, 283, -1, 268, 283, 283, 271, 283, 283, + 283, 14, 0, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 0, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 258, -1, + -1, -1, -1, 263, 264, 265, 266, 267, 268, -1, + -1, 271, 272, 273, 274, 275, 276, 277, -1, 279, + 280, 258, -1, 283, 284, -1, 263, 264, -1, -1, + -1, -1, -1, -1, 271, 272, 273, 274, 275, 276, + 277, -1, 279, 280, 258, -1, 283, 284, -1, 263, + 264, -1, -1, -1, -1, -1, -1, 271, 272, 273, + 274, 275, 276, 277, -1, 279, 280, 258, -1, 283, + 284, -1, 263, 264, -1, -1, -1, -1, -1, -1, + 271, 272, 273, 274, 275, 276, 277, -1, 279, 280, + 258, -1, 283, 284, -1, 263, 264, -1, -1, -1, + -1, -1, -1, 271, 272, 273, 274, 275, 276, 277, + -1, 279, 280, 258, -1, 283, 284, -1, 263, 264, + -1, -1, -1, -1, -1, -1, 271, 272, 273, 274, + 275, 276, 277, -1, 279, 280, 258, -1, 283, 284, + -1, 263, 264, -1, -1, -1, 268, -1, -1, 271, + 272, 273, 274, 275, 276, 277, -1, 279, 280, -1, + -1, 283, +}; +#ifndef YYDEBUG +#define YYDEBUG 0 +#endif +#if YYDEBUG +char *yyname[] = { +"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"L_BRACKET","R_BRACKET","WORD", +"P_RUNNER","STRING_CONSTANT","TRANSCRASH","SEMICOLON","COMMA","L_ASSIGN", +"L_APPEND","L_SYMLINK","PLUS","SLASH","INV_L","INV_R","EQ","NE","LE","GE","LT", +"GT","IS","AND","OR","NOT","IF","THEN","ELSE","EXIST","NAME","UNNAME","NAMED", +"ROOT", +}; +char *yyrule[] = { +"$accept : reiser4", +"reiser4 : Expression", +"Expression : Object_Name", +"Expression : STRING_CONSTANT", +"Expression : UNNAME Expression", +"Expression : Expression PLUS Expression", +"Expression : Expression SEMICOLON Expression", +"Expression : Expression COMMA Expression", +"Expression : if_statement", +"Expression : target L_ASSIGN Expression", +"Expression : target L_APPEND Expression", +"Expression : target L_ASSIGN INV_L Expression INV_R", +"Expression : target L_SYMLINK Expression", +"if_statement : if_Begin then_operation ELSE Expression", +"if_statement : if_Begin then_operation", +"if_Begin : if if_Expression", +"if : IF", +"if_Expression : NOT Expression", +"if_Expression : EXIST Expression", +"if_Expression : Expression EQ Expression", +"if_Expression : Expression NE Expression", +"if_Expression : Expression LE Expression", +"if_Expression : Expression GE Expression", +"if_Expression : Expression LT Expression", +"if_Expression : Expression GT Expression", +"if_Expression : Expression OR Expression", +"if_Expression : Expression AND Expression", +"then_operation : THEN Expression", +"target : Object_Name", +"target : Object_Name NAMED Object_Name", +"Object_Name : begin_from name", +"Object_Name : Object_Name SLASH name", +"begin_from : SLASH", +"begin_from :", +"name : WORD", +"name : level_up Expression R_BRACKET", +"level_up : L_BRACKET", +}; +#endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/parser/parser.tab.h 2004-09-03 00:57:05.148256608 -0700 @@ -0,0 +1,40 @@ +#define L_BRACKET 257 +#define R_BRACKET 258 +#define WORD 259 +#define P_RUNNER 260 +#define STRING_CONSTANT 261 +#define TRANSCRASH 262 +#define SEMICOLON 263 +#define COMMA 264 +#define L_ASSIGN 265 +#define L_APPEND 266 +#define L_SYMLINK 267 +#define PLUS 268 +#define SLASH 269 +#define INV_L 270 +#define INV_R 271 +#define EQ 272 +#define NE 273 +#define LE 274 +#define GE 275 +#define LT 276 +#define GT 277 +#define IS 278 +#define AND 279 +#define OR 280 +#define NOT 281 +#define IF 282 +#define THEN 283 +#define ELSE 284 +#define EXIST 285 +#define NAME 286 +#define UNNAME 287 +#define NAMED 288 +#define ROOT 289 +typedef union +{ + long charType; + expr_v4_t * expr; + wrd_t * wrd; +} YYSTYPE; +extern YYSTYPE yylval; --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/parser/parser.y 2004-09-03 00:57:05.149256456 -0700 @@ -0,0 +1,175 @@ +/* + * Copyright 2001, 2002 by Hans Reiser, licensing governed by reiser4/README + */ + +/* Parser for the reiser4() system call */ + + +/* type definitions */ +%union +{ + long charType; + expr_v4_t * expr; + wrd_t * wrd; +} + +%type L_BRACKET R_BRACKET level_up reiser4 + +%type WORD +%type P_RUNNER +//%type named_level_up +%type STRING_CONSTANT + +%type Object_Name name target +//%type named_expr +%type begin_from +%type Expression + +%type if_statement +%type if_statement if_Expression if_Begin +%type then_operation + +%token TRANSCRASH +%token SEMICOLON /* ; */ +%token COMMA /* , */ +%token L_ASSIGN L_APPEND L_SYMLINK +%token PLUS /* + */ +%token L_BRACKET R_BRACKET +%token SLASH +%token INV_L INV_R +%token EQ NE LE GE LT GT +%token IS +%token AND +%token OR +%token P_RUNNER +%token NOT +%token IF +%token THEN ELSE +%token EXIST +%token NAME UNNAME NAMED +%token WORD STRING_CONSTANT +%token ROOT + + +%left SEMICOLON COMMA +%right L_SYMLINK L_APPEND L_ASSIGN + +%left PLUS /* + */ +%left UNNAME NAME +%left EQ NE LE GE LT GT +%left NOT AND OR + +%right ELSE + +%left SLASH /* / */ + +/* +For bison: +%pure_parser +*/ + +/* + Starting production of our grammar. + */ +%start reiser4 + +%% + +reiser4 + : Expression { $$ = free_expr( ws, $1 ); } +; + +Expression + : Object_Name { $$ = $1;} + | STRING_CONSTANT { $$ = const_to_expr( ws, $1 ); } + | UNNAME Expression { $$ = unname( ws, $2 ); } + | Expression PLUS Expression { $$ = concat_expression( ws, $1, $3 ); } + | Expression SEMICOLON Expression { $$ = list_expression( ws, $1, $3 ); } + | Expression COMMA Expression { $$ = list_async_expression( ws, $1, $3 ); } + | if_statement { $$ = $1; level_down( ws, IF_STATEMENT, IF_STATEMENT ); } + /* the ASSIGNMENT operator return a value: the expression of target */ + | target L_ASSIGN Expression { $$ = assign( ws, $1, $3 ); } /* <- direct assign */ + | target L_APPEND Expression { $$ = assign( ws, $1, $3 ); } /* <- direct assign */ + | target L_ASSIGN INV_L Expression INV_R { $$ = assign_invert( ws, $1, $4 ); } /* <- invert assign. destination must have ..invert method */ + | target L_SYMLINK Expression { $$ = symlink( ws, $1, $3 ); } /* -> symlink the SYMLINK operator return a value: bytes ???? */ + +// | named_level_up Expression R_BRACKET { $$ = named_level_down( ws, $1, $2, $3 ); } +; +//| level_up Expression R_BRACKET { $$ = $2 level_down( ws, $1, $3 );} +//| Expression Expression { $$ = list_unordered_expression( ws, $1, $2 ); } + + +if_statement + : if_Begin then_operation ELSE Expression %prec PLUS { $$ = if_then_else( ws, $1, $2, $4 ); } + | if_Begin then_operation %prec PLUS { $$ = if_then( ws, $1, $2) ; } +; + +if_Begin + : if if_Expression { $$ = $2; } +; + +if: IF { level_up( ws, IF_STATEMENT ); } +; + +if_Expression + : NOT Expression { $$ = not_expression( ws, $2 ); } + | EXIST Expression { $$ = check_exist( ws, $2 ); } + | Expression EQ Expression { $$ = compare_EQ_expression( ws, $1, $3 ); } + | Expression NE Expression { $$ = compare_NE_expression( ws, $1, $3 ); } + | Expression LE Expression { $$ = compare_LE_expression( ws, $1, $3 ); } + | Expression GE Expression { $$ = compare_GE_expression( ws, $1, $3 ); } + | Expression LT Expression { $$ = compare_LT_expression( ws, $1, $3 ); } + | Expression GT Expression { $$ = compare_GT_expression( ws, $1, $3 ); } + | Expression OR Expression { $$ = compare_OR_expression( ws, $1, $3 ); } + | Expression AND Expression { $$ = compare_AND_expression( ws, $1, $3 ); } +; + +then_operation + : THEN Expression %prec PLUS { goto_end( ws );} +; + +target + : Object_Name { $$ = $1;} + | Object_Name NAMED Object_Name { $$ = target_name( $1, $3 );} +; + +Object_Name + : begin_from name %prec ROOT { $$ = pars_expr( ws, $1, $2 ) ; } + | Object_Name SLASH name { $$ = pars_expr( ws, $1, $3 ) ; } +; + +begin_from + : SLASH { $$ = pars_lookup_root( ws ) ; } + | { $$ = pars_lookup_curr( ws ) ; } +; + +name + : WORD { $$ = lookup_word( ws, $1 ); } + | level_up Expression R_BRACKET { $$ = $2; level_down( ws, $1, $3 );} +; + +level_up + : L_BRACKET { $$ = $1; level_up( ws, $1 ); } +; + +//named_level_up +// : Object_Name NAMED level_up { $$ = $1; level_up_named( ws, $1, $3 );} +//; + +%% + + +#define yyversion "4.0.0" +#include "pars.cls.h" +#include "parser.tab.c" +#include "pars.yacc.h" +#include "lib.c" + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/parser/pars.yacc.h 2004-09-03 00:57:05.149256456 -0700 @@ -0,0 +1,65 @@ +/* + * Copyright 2001, 2002 by Hans Reiser, licensing governed by reiser4/README + */ + +/* + * definitions of work space for yacc generated from + * parser.y + */ + +#define MAXLEVELCO 500 +#define BEGIN_FROM_ROOT 222 +#define BEGIN_FROM_CURRENT 333 + +struct reiser4_syscall_w_space { + char * ws_inline; /* this two field used for parsing string, one (inline) stay on begin */ + char * ws_pline; /* of token, second (pline) walk to end to token */ +#ifdef yyacc + /* next field need for yacc */ + /* accesing to this fields from rules: ws->... */ + int ws_yystacksize; /*500*/ + int ws_yymaxdepth ; /*500*/ + int ws_yydebug; + int ws_yynerrs; + int ws_yyerrflag; + int ws_yychar; + int * ws_yyssp; + YYSTYPE * ws_yyvsp; + YYSTYPE ws_yyval; + YYSTYPE ws_yylval; + int ws_yyss[YYSTACKSIZE]; + YYSTYPE ws_yyvs[YYSTACKSIZE]; +#else + /* declare for bison */ +#endif + int ws_yyerrco; + int ws_level; /* current level */ + int ws_errco; /* number of errors */ + /* working fields */ + char * tmpWrdEnd; /* pointer for parsing input string */ + char * yytext; /* pointer for parsing input string */ + /* space for */ + free_space_t * freeSpHead; /* work spaces list Header */ + free_space_t * freeSpCur; /* current work space */ + wrd_t * wrdHead; /* names list Header */ + pars_var_t * Head_pars_var; /* parsed variables Header */ + streg_t * Head_level; /* parsers level list Header */ + streg_t * cur_level; /* current level */ + + expr_v4_t * root_e; /* root expression for this task */ + struct nameidata nd; /* work field for pass to VFS mount points */ +}; + + + +#define printf prink + +/* + * Make Linus happy. + * Local variables: + * c-indentation-style: "K&R" + * mode-name: "LC" + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/parser/tmp.c 2004-09-03 00:57:05.149256456 -0700 @@ -0,0 +1,32 @@ + +/* +#define SP 1 +#define SP_SIM 2 +#define WRD 3 +#define SP 22 +#define SP_SIM 23 +#define WRD 24 +*/ + +#include "../sys_reiser4.c" + +int +main() +{ + int i; + i = 0; + while (i != 307) { + printf("-------->%d\n", i = sys_reiser4("a<-b;"); + } + return 0; + } + +/* + * Make Linus happy. + * Local variables: + * c-indentation-style: "K&R" + * mode-name: "LC" + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/parser/yacc_reiser4.patch 2004-09-03 00:57:05.150256304 -0700 @@ -0,0 +1,127 @@ +--- ../yacc/skeleton.c 1993-12-22 14:28:01.000000000 +0300 ++++ skeleton.c 2004-01-29 23:05:26.000000000 +0300 +@@ -16,7 +16,7 @@ + { + "#ifndef lint", + "/*static char yysccsid[] = \"from: @(#)yaccpar 1.9 (Berkeley) 02/21/93\";*/", +- "static char yyrcsid[] = \"$Id: skeleton.c,v 1.4 1993/12/21 18:45:32 jtc Exp $\";", ++ "static char yyrcsid[] = \"$Id: skeleton.c,v 1.4 1993/12/21 18:45:32 jtc Exp $\\n 2002/10/22 VD reiser4\";", + "#endif", + "#define YYBYACC 1", + "#define YYMAJOR 1", +@@ -30,6 +30,9 @@ + + char *tables[] = + { ++ "#if defined(YYREISER4_DEF)", ++ "#define extern static", ++ "#endif", + "extern short yylhs[];", + "extern short yylen[];", + "extern short yydefred[];", +@@ -49,28 +52,45 @@ + + char *header[] = + { +- "#ifdef YYSTACKSIZE", +- "#undef YYMAXDEPTH", +- "#define YYMAXDEPTH YYSTACKSIZE", +- "#else", +- "#ifdef YYMAXDEPTH", +- "#define YYSTACKSIZE YYMAXDEPTH", ++ "#if defined(YYREISER4_DEF)", ++ "#define YYSTACKSIZE 500", ++ "#define YYMAXDEPTH 500", ++ "#define yydebug ws->ws_yydebug ", ++ "#define yynerrs ws->ws_yynerrs", ++ "#define yyerrflag ws->ws_yyerrflag", ++ "#define yychar ws->ws_yychar", ++ "#define yyssp ws->ws_yyssp", ++ "#define yyvsp ws->ws_yyvsp", ++ "#define yyval ws->ws_yyval", ++ "#define yylval ws->ws_yylval", ++ "#define yyss ws->ws_yyss", ++ "#define yyvs ws->ws_yyvs", ++ "#define yystacksize ws->ws_yystacksize", + "#else", +- "#define YYSTACKSIZE 500", +- "#define YYMAXDEPTH 500", ++ "#ifdef YYSTACKSIZE", ++ "#undef YYMAXDEPTH", ++ "#define YYMAXDEPTH YYSTACKSIZE", ++ "#else", ++ "#ifdef YYMAXDEPTH", ++ "#define YYSTACKSIZE YYMAXDEPTH", ++ "#else", ++ "#define YYSTACKSIZE 500", ++ "#define YYMAXDEPTH 500", ++ "#endif", ++ "#endif", ++ "int yydebug;", ++ "int yynerrs;", ++ "int yyerrflag;", ++ "int yychar;", ++ "short *yyssp;", ++ "YYSTYPE *yyvsp;", ++ "YYSTYPE yyval;", ++ "YYSTYPE yylval;", ++ "short yyss[YYSTACKSIZE];", ++ "YYSTYPE yyvs[YYSTACKSIZE];", ++ "#define yystacksize YYSTACKSIZE", + "#endif", +- "#endif", +- "int yydebug;", +- "int yynerrs;", +- "int yyerrflag;", +- "int yychar;", +- "short *yyssp;", +- "YYSTYPE *yyvsp;", +- "YYSTYPE yyval;", +- "YYSTYPE yylval;", +- "short yyss[YYSTACKSIZE];", +- "YYSTYPE yyvs[YYSTACKSIZE];", +- "#define yystacksize YYSTACKSIZE", ++ + 0 + }; + +@@ -82,11 +102,15 @@ + "#define YYACCEPT goto yyaccept", + "#define YYERROR goto yyerrlab", + "int", ++ "#if defined(YYREISER4_DEF)", ++ "yyparse(struct reiser4_syscall_w_space * ws)", ++ "#else", + "#if defined(__STDC__)", + "yyparse(void)", + "#else", + "yyparse()", + "#endif", ++ "#endif", + "{", + " register int yym, yyn, yystate;", + "#if YYDEBUG", +@@ -150,7 +174,11 @@ + " goto yyreduce;", + " }", + " if (yyerrflag) goto yyinrecovery;", ++ "#if defined(YYREISER4_DEF)", ++ " yyerror(ws,11111,yystate,yychar);", ++ "#else ", + " yyerror(\"syntax error\");", ++ "#endif", + "#ifdef lint", + " goto yyerrlab;", + "#endif", +@@ -275,7 +303,11 @@ + " *++yyvsp = yyval;", + " goto yyloop;", + "yyoverflow:", ++ "#if defined(YYREISER4_DEF)", ++ " yyerror(ws,101); /*yacc stack overflow*/", ++ "#else ", + " yyerror(\"yacc stack overflow\");", ++ "#endif", + "yyabort:", + " return (1);", + "yyaccept:", --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/compress/compress.c 2004-09-03 00:57:05.152256000 -0700 @@ -0,0 +1,371 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ +/* reiser4 compression transform plugins */ + +#include "../../debug.h" +#include "../plugin.h" +#include "../cryptcompress.h" +#include "minilzo.h" + +#include +#include +#include +#include + +/******************************************************************************/ +/* null compression */ +/******************************************************************************/ +static void +null_compress(void *ctx, __u8 * src_first, unsigned src_len, + __u8 * dst_first, unsigned *dst_len) +{ + int i; + assert("edward-793", ctx == NULL); + assert("edward-794", src_first != NULL); + assert("edward-795", dst_first != NULL); + assert("edward-796", src_len != 0); + assert("edward-797", dst_len != NULL); + + for (i = 0; i < NONE_NRCOPY; i++) + xmemcpy(dst_first, src_first, src_len); + *dst_len = src_len; + return; +} + +static void +null_decompress(void *ctx, __u8 * src_first, unsigned src_len, + __u8 * dst_first, unsigned *dst_len) +{ + impossible("edward-798", "trying to decompress uncompressed data"); +} + +/******************************************************************************/ +/* gzip1 compression */ +/******************************************************************************/ + +#define GZIP1_DEF_LEVEL Z_BEST_SPEED +#define GZIP1_DEF_WINBITS 15 +#define GZIP1_DEF_MEMLEVEL MAX_MEM_LEVEL + +static int gzip6_overrun(unsigned src_len UNUSED_ARG) +{ + return 0; +} + +static int gzip1_alloc(tfm_info_t * ctx, tfm_action act) +{ + int ret = -ENXIO; + assert("edward-766", *ctx == NULL); +#if REISER4_GZIP_TFM + ret = 0; + switch (act) { + case TFM_WRITE: /* compress */ + *ctx = __vmalloc(zlib_deflate_workspacesize(), + (in_softirq()? GFP_ATOMIC : GFP_KERNEL) | + __GFP_HIGHMEM, PAGE_KERNEL); + if (*ctx == NULL) { + ret = -ENOMEM; + break; + } + xmemset(*ctx, 0, zlib_deflate_workspacesize()); + break; + case TFM_READ: /* decompress */ + *ctx = reiser4_kmalloc(zlib_inflate_workspacesize(), + (in_softirq()? GFP_ATOMIC : GFP_KERNEL)); + if (*ctx == NULL) { + ret = -ENOMEM; + break; + } + xmemset(*ctx, 0, zlib_inflate_workspacesize()); + break; + default: + impossible("edward-767", + "alloc workspace for unknown tfm action"); + } +#endif + if (ret) + warning("edward-768", + "alloc workspace for gzip1 (tfm action = %d) failed\n", + act); + return ret; +} + +static void gzip1_free(tfm_info_t * ctx, tfm_action act) +{ +#if REISER4_GZIP_TFM + assert("edward-769", *ctx != NULL); + + switch (act) { + case TFM_WRITE: /* compress */ + vfree(*ctx); + break; + case TFM_READ: /* decompress */ + reiser4_kfree(*ctx); + break; + default: + impossible("edward-770", + "free workspace for unknown tfm action"); + } +#endif + return; +} + +static void +gzip1_compress(tfm_info_t ctx, __u8 * src_first, unsigned src_len, + __u8 * dst_first, unsigned *dst_len) +{ +#if REISER4_GZIP_TFM + int ret = 0; + struct z_stream_s stream; + compression_plugin *cplug = + compression_plugin_by_id(GZIP1_COMPRESSION_ID); + + xmemset(&stream, 0, sizeof(stream)); + + assert("edward-842", ctx != NULL); + + if (!ctx) { + ret = cplug->alloc(&stream.workspace, TFM_WRITE); + if (ret) + goto rollback; + } else + stream.workspace = ctx; + + ret = zlib_deflateInit2(&stream, GZIP1_DEF_LEVEL, Z_DEFLATED, + -GZIP1_DEF_WINBITS, GZIP1_DEF_MEMLEVEL, + Z_DEFAULT_STRATEGY); + if (ret != Z_OK) { + warning("edward-771", "zlib_deflateInit2 returned %d\n", ret); + goto rollback; + } + ret = zlib_deflateReset(&stream); + if (ret != Z_OK) { + warning("edward-772", "zlib_deflateReset returned %d\n", ret); + goto rollback; + } + stream.next_in = src_first; + stream.avail_in = src_len; + stream.next_out = dst_first; + stream.avail_out = *dst_len; + + ret = zlib_deflate(&stream, Z_FINISH); + if (ret != Z_STREAM_END) { + warning("edward-773", "zlib_deflate returned %d\n", ret); + goto rollback; + } + *dst_len = stream.total_out; + if (!ctx) + cplug->free(&stream.workspace, TFM_WRITE); + return; + rollback: + if (!ctx && stream.workspace) + cplug->free(&stream.workspace, TFM_WRITE); + *dst_len = src_len; +#endif + return; +} + +static void +gzip1_decompress(tfm_info_t ctx, __u8 * src_first, unsigned src_len, + __u8 * dst_first, unsigned *dst_len) +{ +#if REISER4_GZIP_TFM + int ret = 0; + struct z_stream_s stream; + compression_plugin *cplug = + compression_plugin_by_id(GZIP1_COMPRESSION_ID); + + xmemset(&stream, 0, sizeof(stream)); + + assert("edward-843", ctx == NULL); + + if (!ctx) { + ret = cplug->alloc(&stream.workspace, TFM_READ); + if (ret) + goto out; + } else + stream.workspace = ctx; + + ret = zlib_inflateInit2(&stream, -GZIP1_DEF_WINBITS); + if (ret != Z_OK) { + warning("edward-774", "zlib_inflateInit2 returned %d\n", ret); + goto out; + } + ret = zlib_inflateReset(&stream); + if (ret != Z_OK) { + warning("edward-775", "zlib_inflateReset returned %d\n", ret); + goto out; + } + + stream.next_in = src_first; + stream.avail_in = src_len; + stream.next_out = dst_first; + stream.avail_out = *dst_len; + + ret = zlib_inflate(&stream, Z_SYNC_FLUSH); + /* + * Work around a bug in zlib, which sometimes wants to taste an extra + * byte when being used in the (undocumented) raw deflate mode. + * (From USAGI). + */ + if (ret == Z_OK && !stream.avail_in && stream.avail_out) { + u8 zerostuff = 0; + stream.next_in = &zerostuff; + stream.avail_in = 1; + ret = zlib_inflate(&stream, Z_FINISH); + } + if (ret != Z_STREAM_END) { + warning("edward-776", "zlib_inflate returned %d\n", ret); + goto out; + } + *dst_len = stream.total_out; + out: + if (!ctx && stream.workspace) + cplug->free(&stream.workspace, TFM_READ); +#endif + return; +} + +/******************************************************************************/ +/* none compression */ +/******************************************************************************/ + +static int none_overrun(unsigned src_len UNUSED_ARG) +{ + return 0; +} + +/******************************************************************************/ +/* lzo1 compression */ +/******************************************************************************/ + +static int lzo1_overrun(unsigned in_len) +{ + return in_len / 64 + 16 + 3; +} + +#define HEAP_ALLOC(var,size) \ + lzo_align_t __LZO_MMODEL var [ ((size) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t) ] + +static void +lzo1_compress(tfm_info_t ctx, __u8 * src_first, unsigned src_len, + __u8 * dst_first, unsigned *dst_len) +{ + int result; + HEAP_ALLOC(wrkmem, LZO1X_1_MEM_COMPRESS); + + assert("edward-846", ctx == NULL); + assert("edward-847", src_len != 0); + + result = lzo_init(); + + if (result != LZO_E_OK) { + warning("edward-848", "lzo_init() failed\n"); + goto out; + } + result = + lzo1x_1_compress(src_first, src_len, dst_first, dst_len, wrkmem); + if (result != LZO_E_OK) { + warning("edward-849", "lzo1x_1_compress failed\n"); + goto out; + } + if (*dst_len >= src_len) + warning("edward-850", + "lzo1x_1_compress: incompressible data\n"); + return; + out: + *dst_len = src_len; + return; +} + +static void +lzo1_decompress(tfm_info_t ctx, __u8 * src_first, unsigned src_len, + __u8 * dst_first, unsigned *dst_len) +{ + int result; + + assert("edward-851", ctx == NULL); + assert("edward-852", src_len != 0); + + result = lzo1x_decompress(src_first, src_len, dst_first, dst_len, NULL); + if (result != LZO_E_OK) + warning("edward-853", "lzo1x_1_decompress failed\n"); + return; +} + +compression_plugin compression_plugins[LAST_COMPRESSION_ID] = { + [NONE_COMPRESSION_ID] = { + .h = { + .type_id = + REISER4_COMPRESSION_PLUGIN_TYPE, + .id = NONE_COMPRESSION_ID, + .pops = NULL, + .label = "none", + .desc = + "absence of any compression transform", + .linkage = TYPE_SAFE_LIST_LINK_ZERO} + , + .overrun = none_overrun, + .alloc = NULL, + .free = NULL, + .compress = NULL, + .decompress = NULL} + , + [NULL_COMPRESSION_ID] = { + .h = { + .type_id = + REISER4_COMPRESSION_PLUGIN_TYPE, + .id = NULL_COMPRESSION_ID, + .pops = NULL, + .label = "null", + .desc = "NONE_NRCOPY times of memcpy", + .linkage = TYPE_SAFE_LIST_LINK_ZERO} + , + .overrun = none_overrun, + .alloc = NULL, + .free = NULL, + .compress = null_compress, + .decompress = null_decompress} + , + [LZO1_COMPRESSION_ID] = { + .h = { + .type_id = + REISER4_COMPRESSION_PLUGIN_TYPE, + .id = LZO1_COMPRESSION_ID, + .pops = NULL, + .label = "lzo1", + .desc = "lzo1 compression transform", + .linkage = TYPE_SAFE_LIST_LINK_ZERO} + , + .overrun = lzo1_overrun, + .alloc = NULL, + .free = NULL, + .compress = lzo1_compress, + .decompress = lzo1_decompress} + , + [GZIP1_COMPRESSION_ID] = { + .h = { + .type_id = + REISER4_COMPRESSION_PLUGIN_TYPE, + .id = GZIP1_COMPRESSION_ID, + .pops = NULL, + .label = "gzip1", + .desc = "gzip1 compression transform", + .linkage = TYPE_SAFE_LIST_LINK_ZERO} + , + .overrun = gzip6_overrun, + .alloc = gzip1_alloc, + .free = gzip1_free, + .compress = gzip1_compress, + .decompress = gzip1_decompress} +}; + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/compress/compress.h 2004-09-03 00:57:05.152256000 -0700 @@ -0,0 +1,25 @@ +#if !defined( __FS_REISER4_COMPRESS_H__ ) +#define REISER4_COMPRESS_H + +#include +#include + +#define NONE_NRCOPY 4 + +typedef enum { + TFM_READ, + TFM_WRITE +} tfm_action; + +#endif /* __FS_REISER4_COMPRESS_H__ */ + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/compress/lzoconf.h 2004-09-03 00:57:05.155255544 -0700 @@ -0,0 +1,456 @@ +/* lzoconf.h -- configuration for the LZO real-time data compression library + adopted for reiser4 compression tramsform plugin + + This file is part of the LZO real-time data compression library. + + Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + The LZO library is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer + + http://www.oberhumer.com/opensource/lzo/ + */ + +#include /* for UINT_MAX, ULONG_MAX - edward */ + +#ifndef __LZOCONF_H +#define __LZOCONF_H + +#define LZO_VERSION 0x1080 +#define LZO_VERSION_STRING "1.08" +#define LZO_VERSION_DATE "Jul 12 2002" + +/* internal Autoconf configuration file - only used when building LZO */ +#if defined(LZO_HAVE_CONFIG_H) +# include +#endif +#ifdef __cplusplus +extern "C" { +#endif + + +/*********************************************************************** +// LZO requires a conforming +************************************************************************/ + +#define CHAR_BIT 8 /* -edward */ +#define USHRT_MAX 0xffff /* -edward */ + +#if 0 /* -edward */ +#if !defined(CHAR_BIT) || (CHAR_BIT != 8) +# error "invalid CHAR_BIT" +#endif +#if !defined(UCHAR_MAX) || !defined(UINT_MAX) || !defined(ULONG_MAX) +# error "check your compiler installation" +#endif +#if (USHRT_MAX < 1) || (UINT_MAX < 1) || (ULONG_MAX < 1) +# error "your limits.h macros are broken" +#endif +#endif /* -edward */ +/* workaround a cpp bug under hpux 10.20 */ +#define LZO_0xffffffffL 4294967295ul + +#if 0 /* -edward */ +#if !defined(LZO_UINT32_C) +# if (UINT_MAX < LZO_0xffffffffL) +# define LZO_UINT32_C(c) c ## UL +# else +# define LZO_UINT32_C(c) c ## U +# endif +#endif +#endif /* -edward */ + +/*********************************************************************** +// architecture defines +************************************************************************/ + +#if !defined(__LZO_WIN) && !defined(__LZO_DOS) && !defined(__LZO_OS2) +# if defined(__WINDOWS__) || defined(_WINDOWS) || defined(_Windows) +# define __LZO_WIN +# elif defined(__WIN32__) || defined(_WIN32) || defined(WIN32) +# define __LZO_WIN +# elif defined(__NT__) || defined(__NT_DLL__) || defined(__WINDOWS_386__) +# define __LZO_WIN +# elif defined(__DOS__) || defined(__MSDOS__) || defined(MSDOS) +# define __LZO_DOS +# elif defined(__OS2__) || defined(__OS2V2__) || defined(OS2) +# define __LZO_OS2 +# elif defined(__palmos__) +# define __LZO_PALMOS +# elif defined(__TOS__) || defined(__atarist__) +# define __LZO_TOS +# endif +#endif + +#if (UINT_MAX < LZO_0xffffffffL) +# if defined(__LZO_WIN) +# define __LZO_WIN16 +# elif defined(__LZO_DOS) +# define __LZO_DOS16 +# elif defined(__LZO_PALMOS) +# define __LZO_PALMOS16 +# elif defined(__LZO_TOS) +# define __LZO_TOS16 +# elif defined(__C166__) +# else + /* porting hint: for pure 16-bit architectures try compiling + * everything with -D__LZO_STRICT_16BIT */ +# error "16-bit target not supported - contact me for porting hints" +# endif +#endif + +#if !defined(__LZO_i386) +# if defined(__LZO_DOS) || defined(__LZO_WIN16) +# define __LZO_i386 +# elif defined(__i386__) || defined(__386__) || defined(_M_IX86) +# define __LZO_i386 +# endif +#endif + +#if defined(__LZO_STRICT_16BIT) +# if (UINT_MAX < LZO_0xffffffffL) +# include +# endif +#endif + +/* memory checkers */ +#if !defined(__LZO_CHECKER) +# if defined(__BOUNDS_CHECKING_ON) +# define __LZO_CHECKER +# elif defined(__CHECKER__) +# define __LZO_CHECKER +# elif defined(__INSURE__) +# define __LZO_CHECKER +# elif defined(__PURIFY__) +# define __LZO_CHECKER +# endif +#endif + + +/*********************************************************************** +// integral and pointer types +************************************************************************/ + +/* Integral types with 32 bits or more */ +#if !defined(LZO_UINT32_MAX) +# if (UINT_MAX >= LZO_0xffffffffL) + typedef unsigned int lzo_uint32; + typedef int lzo_int32; +# define LZO_UINT32_MAX UINT_MAX +# define LZO_INT32_MAX INT_MAX +# define LZO_INT32_MIN INT_MIN +# elif (ULONG_MAX >= LZO_0xffffffffL) + typedef unsigned long lzo_uint32; + typedef long lzo_int32; +# define LZO_UINT32_MAX ULONG_MAX +# define LZO_INT32_MAX LONG_MAX +# define LZO_INT32_MIN LONG_MIN +# else +# error "lzo_uint32" +# endif +#endif + +/* lzo_uint is used like size_t */ +#if !defined(LZO_UINT_MAX) +# if (UINT_MAX >= LZO_0xffffffffL) + typedef unsigned int lzo_uint; + typedef int lzo_int; +# define LZO_UINT_MAX UINT_MAX +# define LZO_INT_MAX INT_MAX +# define LZO_INT_MIN INT_MIN +# elif (ULONG_MAX >= LZO_0xffffffffL) + typedef unsigned long lzo_uint; + typedef long lzo_int; +# define LZO_UINT_MAX ULONG_MAX +# define LZO_INT_MAX LONG_MAX +# define LZO_INT_MIN LONG_MIN +# else +# error "lzo_uint" +# endif +#endif + +typedef int lzo_bool; + + +/*********************************************************************** +// memory models +************************************************************************/ + +/* Memory model for the public code segment. */ +#if !defined(__LZO_CMODEL) +# if defined(__LZO_DOS16) || defined(__LZO_WIN16) +# define __LZO_CMODEL __far +# elif defined(__LZO_i386) && defined(__WATCOMC__) +# define __LZO_CMODEL __near +# else +# define __LZO_CMODEL +# endif +#endif + +/* Memory model for the public data segment. */ +#if !defined(__LZO_DMODEL) +# if defined(__LZO_DOS16) || defined(__LZO_WIN16) +# define __LZO_DMODEL __far +# elif defined(__LZO_i386) && defined(__WATCOMC__) +# define __LZO_DMODEL __near +# else +# define __LZO_DMODEL +# endif +#endif + +/* Memory model that allows to access memory at offsets of lzo_uint. */ +#if !defined(__LZO_MMODEL) +# if (LZO_UINT_MAX <= UINT_MAX) +# define __LZO_MMODEL +# elif defined(__LZO_DOS16) || defined(__LZO_WIN16) +# define __LZO_MMODEL __huge +# define LZO_999_UNSUPPORTED +# elif defined(__LZO_PALMOS16) || defined(__LZO_TOS16) +# define __LZO_MMODEL +# else +# error "__LZO_MMODEL" +# endif +#endif + +/* no typedef here because of const-pointer issues */ +#define lzo_byte unsigned char __LZO_MMODEL +#define lzo_bytep unsigned char __LZO_MMODEL * +#define lzo_charp char __LZO_MMODEL * +#define lzo_voidp void __LZO_MMODEL * +#define lzo_shortp short __LZO_MMODEL * +#define lzo_ushortp unsigned short __LZO_MMODEL * +#define lzo_uint32p lzo_uint32 __LZO_MMODEL * +#define lzo_int32p lzo_int32 __LZO_MMODEL * +#define lzo_uintp lzo_uint __LZO_MMODEL * +#define lzo_intp lzo_int __LZO_MMODEL * +#define lzo_voidpp lzo_voidp __LZO_MMODEL * +#define lzo_bytepp lzo_bytep __LZO_MMODEL * + +#ifndef lzo_sizeof_dict_t +# define lzo_sizeof_dict_t sizeof(lzo_bytep) +#endif + + +/*********************************************************************** +// calling conventions and function types +************************************************************************/ + +/* linkage */ +#if !defined(__LZO_EXTERN_C) +# ifdef __cplusplus +# define __LZO_EXTERN_C extern "C" +# else +# define __LZO_EXTERN_C extern +# endif +#endif + +/* calling convention */ +#if !defined(__LZO_CDECL) +# if defined(__LZO_DOS16) || defined(__LZO_WIN16) +# define __LZO_CDECL __LZO_CMODEL __cdecl +# elif defined(__LZO_i386) && defined(_MSC_VER) +# define __LZO_CDECL __LZO_CMODEL __cdecl +# elif defined(__LZO_i386) && defined(__WATCOMC__) +# define __LZO_CDECL __LZO_CMODEL __cdecl +# else +# define __LZO_CDECL __LZO_CMODEL +# endif +#endif +#if !defined(__LZO_ENTRY) +# define __LZO_ENTRY __LZO_CDECL +#endif + +/* C++ exception specification for extern "C" function types */ +#if !defined(__cplusplus) +# undef LZO_NOTHROW +# define LZO_NOTHROW +#elif !defined(LZO_NOTHROW) +# define LZO_NOTHROW +#endif + + +typedef int +(__LZO_ENTRY *lzo_compress_t) ( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uintp dst_len, + lzo_voidp wrkmem ); + +typedef int +(__LZO_ENTRY *lzo_decompress_t) ( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uintp dst_len, + lzo_voidp wrkmem ); + +typedef int +(__LZO_ENTRY *lzo_optimize_t) ( lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uintp dst_len, + lzo_voidp wrkmem ); + +typedef int +(__LZO_ENTRY *lzo_compress_dict_t)(const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uintp dst_len, + lzo_voidp wrkmem, + const lzo_byte *dict, lzo_uint dict_len ); + +typedef int +(__LZO_ENTRY *lzo_decompress_dict_t)(const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uintp dst_len, + lzo_voidp wrkmem, + const lzo_byte *dict, lzo_uint dict_len ); + + +/* assembler versions always use __cdecl */ +typedef int +(__LZO_CDECL *lzo_compress_asm_t)( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uintp dst_len, + lzo_voidp wrkmem ); + +typedef int +(__LZO_CDECL *lzo_decompress_asm_t)( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uintp dst_len, + lzo_voidp wrkmem ); + + +/* a progress indicator callback function */ +typedef void (__LZO_ENTRY *lzo_progress_callback_t) (lzo_uint, lzo_uint); + + +/*********************************************************************** +// export information +************************************************************************/ + +/* DLL export information */ +#if !defined(__LZO_EXPORT1) +# define __LZO_EXPORT1 +#endif +#if !defined(__LZO_EXPORT2) +# define __LZO_EXPORT2 +#endif + +/* exported calling convention for C functions */ +#if !defined(LZO_PUBLIC) +# define LZO_PUBLIC(_rettype) \ + __LZO_EXPORT1 _rettype __LZO_EXPORT2 __LZO_ENTRY +#endif +#if !defined(LZO_EXTERN) +# define LZO_EXTERN(_rettype) __LZO_EXTERN_C LZO_PUBLIC(_rettype) +#endif +#if !defined(LZO_PRIVATE) +# define LZO_PRIVATE(_rettype) static _rettype __LZO_ENTRY +#endif + +/* exported __cdecl calling convention for assembler functions */ +#if !defined(LZO_PUBLIC_CDECL) +# define LZO_PUBLIC_CDECL(_rettype) \ + __LZO_EXPORT1 _rettype __LZO_EXPORT2 __LZO_CDECL +#endif +#if !defined(LZO_EXTERN_CDECL) +# define LZO_EXTERN_CDECL(_rettype) __LZO_EXTERN_C LZO_PUBLIC_CDECL(_rettype) +#endif + +/* exported global variables (LZO currently uses no static variables and + * is fully thread safe) */ +#if !defined(LZO_PUBLIC_VAR) +# define LZO_PUBLIC_VAR(_type) \ + __LZO_EXPORT1 _type __LZO_EXPORT2 __LZO_DMODEL +#endif +#if !defined(LZO_EXTERN_VAR) +# define LZO_EXTERN_VAR(_type) extern LZO_PUBLIC_VAR(_type) +#endif + + +/*********************************************************************** +// error codes and prototypes +************************************************************************/ + +/* Error codes for the compression/decompression functions. Negative + * values are errors, positive values will be used for special but + * normal events. + */ +#define LZO_E_OK 0 +#define LZO_E_ERROR (-1) +#define LZO_E_OUT_OF_MEMORY (-2) /* not used right now */ +#define LZO_E_NOT_COMPRESSIBLE (-3) /* not used right now */ +#define LZO_E_INPUT_OVERRUN (-4) +#define LZO_E_OUTPUT_OVERRUN (-5) +#define LZO_E_LOOKBEHIND_OVERRUN (-6) +#define LZO_E_EOF_NOT_FOUND (-7) +#define LZO_E_INPUT_NOT_CONSUMED (-8) + + +/* lzo_init() should be the first function you call. + * Check the return code ! + * + * lzo_init() is a macro to allow checking that the library and the + * compiler's view of various types are consistent. + */ +#define lzo_init() __lzo_init2(LZO_VERSION,(int)sizeof(short),(int)sizeof(int),\ + (int)sizeof(long),(int)sizeof(lzo_uint32),(int)sizeof(lzo_uint),\ + (int)lzo_sizeof_dict_t,(int)sizeof(char *),(int)sizeof(lzo_voidp),\ + (int)sizeof(lzo_compress_t)) +LZO_EXTERN(int) __lzo_init2(unsigned,int,int,int,int,int,int,int,int,int); + +/* version functions (useful for shared libraries) */ +LZO_EXTERN(unsigned) lzo_version(void); +LZO_EXTERN(const char *) lzo_version_string(void); +LZO_EXTERN(const char *) lzo_version_date(void); +LZO_EXTERN(const lzo_charp) _lzo_version_string(void); +LZO_EXTERN(const lzo_charp) _lzo_version_date(void); + +/* string functions */ +LZO_EXTERN(int) +lzo_memcmp(const lzo_voidp _s1, const lzo_voidp _s2, lzo_uint _len); +LZO_EXTERN(lzo_voidp) +lzo_memcpy(lzo_voidp _dest, const lzo_voidp _src, lzo_uint _len); +LZO_EXTERN(lzo_voidp) +lzo_memmove(lzo_voidp _dest, const lzo_voidp _src, lzo_uint _len); +LZO_EXTERN(lzo_voidp) +lzo_memset(lzo_voidp _s, int _c, lzo_uint _len); + +/* checksum functions */ +LZO_EXTERN(lzo_uint32) +lzo_adler32(lzo_uint32 _adler, const lzo_byte *_buf, lzo_uint _len); +LZO_EXTERN(lzo_uint32) +lzo_crc32(lzo_uint32 _c, const lzo_byte *_buf, lzo_uint _len); + +/* misc. */ +LZO_EXTERN(lzo_bool) lzo_assert(int _expr); +LZO_EXTERN(int) _lzo_config_check(void); +typedef union { lzo_bytep p; lzo_uint u; } __lzo_pu_u; +typedef union { lzo_bytep p; lzo_uint32 u32; } __lzo_pu32_u; +typedef union { void *vp; lzo_bytep bp; lzo_uint32 u32; long l; } lzo_align_t; + +/* align a char pointer on a boundary that is a multiple of `size' */ +LZO_EXTERN(unsigned) __lzo_align_gap(const lzo_voidp _ptr, lzo_uint _size); +#define LZO_PTR_ALIGN_UP(_ptr,_size) \ + ((_ptr) + (lzo_uint) __lzo_align_gap((const lzo_voidp)(_ptr),(lzo_uint)(_size))) + +/* deprecated - only for backward compatibility */ +#define LZO_ALIGN(_ptr,_size) LZO_PTR_ALIGN_UP(_ptr,_size) + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* already included */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/compress/minilzo.c 2004-09-03 00:57:05.167253720 -0700 @@ -0,0 +1,2947 @@ +/* minilzo.c -- mini subset of the LZO real-time data compression library + Adopted for reiser4 compression transform plugin. + + This file is part of the LZO real-time data compression library. + + Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + The LZO library is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer + + http://www.oberhumer.com/opensource/lzo/ + */ + +/* + * NOTE: + * the full LZO package can be found at + * http://www.oberhumer.com/opensource/lzo/ + */ + +#include "../../debug.h" /* for reiser4 assert macro -edward */ + +#define __LZO_IN_MINILZO +#define LZO_BUILD + +#ifdef MINILZO_HAVE_CONFIG_H +# include +#endif + +#undef LZO_HAVE_CONFIG_H +#include "minilzo.h" + +#if !defined(MINILZO_VERSION) || (MINILZO_VERSION != 0x1080) +# error "version mismatch in miniLZO source files" +#endif + +#ifdef MINILZO_HAVE_CONFIG_H +# define LZO_HAVE_CONFIG_H +#endif + +#if 0 /* -edward */ +#if !defined(LZO_NO_SYS_TYPES_H) +# include +#endif +#include +#endif /* -edward */ + +#ifndef __LZO_CONF_H +#define __LZO_CONF_H + +#if !defined(__LZO_IN_MINILZO) +# ifndef __LZOCONF_H +# include +# endif +#endif + +#if defined(__BOUNDS_CHECKING_ON) +# include +#else +# define BOUNDS_CHECKING_OFF_DURING(stmt) stmt +# define BOUNDS_CHECKING_OFF_IN_EXPR(expr) (expr) +#endif + +#if 0 /* edward */ +#if !defined(LZO_HAVE_CONFIG_H) +# include +# include +# if !defined(NO_STDLIB_H) +# include +# endif +#endif /* edward */ +# define HAVE_MEMCMP +# define HAVE_MEMCPY +# define HAVE_MEMMOVE +# define HAVE_MEMSET +#if 0 /* edward */ +#else +# include +# if defined(HAVE_STDDEF_H) +# include +# endif +# if defined(STDC_HEADERS) +# include +# include +# endif +#endif +#endif /* edward */ + +#if defined(__LZO_DOS16) || defined(__LZO_WIN16) +# define HAVE_MALLOC_H +# define HAVE_HALLOC +#endif + +#undef NDEBUG +#if !defined(LZO_DEBUG) +# define NDEBUG +#endif +#if defined(LZO_DEBUG) || !defined(NDEBUG) +# if !defined(NO_STDIO_H) +# include +# endif +#endif +# if 0 /* edward */ +#include +#endif /* edward */ + +#if !defined(LZO_COMPILE_TIME_ASSERT) +# define LZO_COMPILE_TIME_ASSERT(expr) \ + { typedef int __lzo_compile_time_assert_fail[1 - 2 * !(expr)]; } +#endif + +#if !defined(LZO_UNUSED) +# if 1 +# define LZO_UNUSED(var) ((void)&var) +# elif 0 +# define LZO_UNUSED(var) { typedef int __lzo_unused[sizeof(var) ? 2 : 1]; } +# else +# define LZO_UNUSED(parm) (parm = parm) +# endif +#endif + +#if !defined(__inline__) && !defined(__GNUC__) +# if defined(__cplusplus) +# define __inline__ inline +# else +# define __inline__ +# endif +#endif + +#if defined(NO_MEMCMP) +# undef HAVE_MEMCMP +#endif + +#if !defined(HAVE_MEMCMP) +# undef memcmp +# define memcmp lzo_memcmp +#endif +#if !defined(HAVE_MEMCPY) +# undef memcpy +# define memcpy lzo_memcpy +#endif +#if !defined(HAVE_MEMMOVE) +# undef memmove +# define memmove lzo_memmove +#endif +#if !defined(HAVE_MEMSET) +# undef memset +# define memset lzo_memset +#endif + +#if 0 +# define LZO_BYTE(x) ((unsigned char) (x)) +#else +# define LZO_BYTE(x) ((unsigned char) ((x) & 0xff)) +#endif + +#define LZO_MAX(a,b) ((a) >= (b) ? (a) : (b)) +#define LZO_MIN(a,b) ((a) <= (b) ? (a) : (b)) +#define LZO_MAX3(a,b,c) ((a) >= (b) ? LZO_MAX(a,c) : LZO_MAX(b,c)) +#define LZO_MIN3(a,b,c) ((a) <= (b) ? LZO_MIN(a,c) : LZO_MIN(b,c)) + +#define lzo_sizeof(type) ((lzo_uint) (sizeof(type))) + +#define LZO_HIGH(array) ((lzo_uint) (sizeof(array)/sizeof(*(array)))) + +#define LZO_SIZE(bits) (1u << (bits)) +#define LZO_MASK(bits) (LZO_SIZE(bits) - 1) + +#define LZO_LSIZE(bits) (1ul << (bits)) +#define LZO_LMASK(bits) (LZO_LSIZE(bits) - 1) + +#define LZO_USIZE(bits) ((lzo_uint) 1 << (bits)) +#define LZO_UMASK(bits) (LZO_USIZE(bits) - 1) + +#define LZO_STYPE_MAX(b) (((1l << (8*(b)-2)) - 1l) + (1l << (8*(b)-2))) +#define LZO_UTYPE_MAX(b) (((1ul << (8*(b)-1)) - 1ul) + (1ul << (8*(b)-1))) + +#if !defined(SIZEOF_UNSIGNED) +# if (UINT_MAX == 0xffff) +# define SIZEOF_UNSIGNED 2 +# elif (UINT_MAX == LZO_0xffffffffL) +# define SIZEOF_UNSIGNED 4 +# elif (UINT_MAX >= LZO_0xffffffffL) +# define SIZEOF_UNSIGNED 8 +# else +# error "SIZEOF_UNSIGNED" +# endif +#endif + +#if !defined(SIZEOF_UNSIGNED_LONG) +# if (ULONG_MAX == LZO_0xffffffffL) +# define SIZEOF_UNSIGNED_LONG 4 +# elif (ULONG_MAX >= LZO_0xffffffffL) +# define SIZEOF_UNSIGNED_LONG 8 +# else +# error "SIZEOF_UNSIGNED_LONG" +# endif +#endif + +#if !defined(SIZEOF_SIZE_T) +# define SIZEOF_SIZE_T SIZEOF_UNSIGNED +#endif +#if !defined(SIZE_T_MAX) +# define SIZE_T_MAX LZO_UTYPE_MAX(SIZEOF_SIZE_T) +#endif + +#if 1 && defined(__LZO_i386) && (UINT_MAX == LZO_0xffffffffL) +# if !defined(LZO_UNALIGNED_OK_2) && (USHRT_MAX == 0xffff) +# define LZO_UNALIGNED_OK_2 +# endif +# if !defined(LZO_UNALIGNED_OK_4) && (LZO_UINT32_MAX == LZO_0xffffffffL) +# define LZO_UNALIGNED_OK_4 +# endif +#endif + +#if defined(LZO_UNALIGNED_OK_2) || defined(LZO_UNALIGNED_OK_4) +# if !defined(LZO_UNALIGNED_OK) +# define LZO_UNALIGNED_OK +# endif +#endif + +#if defined(__LZO_NO_UNALIGNED) +# undef LZO_UNALIGNED_OK +# undef LZO_UNALIGNED_OK_2 +# undef LZO_UNALIGNED_OK_4 +#endif + +#if defined(LZO_UNALIGNED_OK_2) && (USHRT_MAX != 0xffff) +# error "LZO_UNALIGNED_OK_2 must not be defined on this system" +#endif +#if defined(LZO_UNALIGNED_OK_4) && (LZO_UINT32_MAX != LZO_0xffffffffL) +# error "LZO_UNALIGNED_OK_4 must not be defined on this system" +#endif + +#if defined(__LZO_NO_ALIGNED) +# undef LZO_ALIGNED_OK_4 +#endif + +#if defined(LZO_ALIGNED_OK_4) && (LZO_UINT32_MAX != LZO_0xffffffffL) +# error "LZO_ALIGNED_OK_4 must not be defined on this system" +#endif + +#define LZO_LITTLE_ENDIAN 1234 +#define LZO_BIG_ENDIAN 4321 +#define LZO_PDP_ENDIAN 3412 + +#if !defined(LZO_BYTE_ORDER) +# if defined(MFX_BYTE_ORDER) +# define LZO_BYTE_ORDER MFX_BYTE_ORDER +# elif defined(__LZO_i386) +# define LZO_BYTE_ORDER LZO_LITTLE_ENDIAN +# elif defined(BYTE_ORDER) +# define LZO_BYTE_ORDER BYTE_ORDER +# elif defined(__BYTE_ORDER) +# define LZO_BYTE_ORDER __BYTE_ORDER +# endif +#endif + +#if defined(LZO_BYTE_ORDER) +# if (LZO_BYTE_ORDER != LZO_LITTLE_ENDIAN) && \ + (LZO_BYTE_ORDER != LZO_BIG_ENDIAN) +# error "invalid LZO_BYTE_ORDER" +# endif +#endif + +#if defined(LZO_UNALIGNED_OK) && !defined(LZO_BYTE_ORDER) +# error "LZO_BYTE_ORDER is not defined" +#endif + +#define LZO_OPTIMIZE_GNUC_i386_IS_BUGGY + +#if defined(NDEBUG) && !defined(LZO_DEBUG) && !defined(__LZO_CHECKER) +# if defined(__GNUC__) && defined(__i386__) +# if !defined(LZO_OPTIMIZE_GNUC_i386_IS_BUGGY) +# define LZO_OPTIMIZE_GNUC_i386 +# endif +# endif +#endif + +__LZO_EXTERN_C int __lzo_init_done; +__LZO_EXTERN_C const lzo_byte __lzo_copyright[]; +LZO_EXTERN(const lzo_byte *) lzo_copyright(void); +__LZO_EXTERN_C const lzo_uint32 _lzo_crc32_table[256]; + +#define _LZO_STRINGIZE(x) #x +#define _LZO_MEXPAND(x) _LZO_STRINGIZE(x) + +#define _LZO_CONCAT2(a,b) a ## b +#define _LZO_CONCAT3(a,b,c) a ## b ## c +#define _LZO_CONCAT4(a,b,c,d) a ## b ## c ## d +#define _LZO_CONCAT5(a,b,c,d,e) a ## b ## c ## d ## e + +#define _LZO_ECONCAT2(a,b) _LZO_CONCAT2(a,b) +#define _LZO_ECONCAT3(a,b,c) _LZO_CONCAT3(a,b,c) +#define _LZO_ECONCAT4(a,b,c,d) _LZO_CONCAT4(a,b,c,d) +#define _LZO_ECONCAT5(a,b,c,d,e) _LZO_CONCAT5(a,b,c,d,e) + +#if 0 + +#define __LZO_IS_COMPRESS_QUERY(i,il,o,ol,w) ((lzo_voidp)(o) == (w)) +#define __LZO_QUERY_COMPRESS(i,il,o,ol,w,n,s) \ + (*ol = (n)*(s), LZO_E_OK) + +#define __LZO_IS_DECOMPRESS_QUERY(i,il,o,ol,w) ((lzo_voidp)(o) == (w)) +#define __LZO_QUERY_DECOMPRESS(i,il,o,ol,w,n,s) \ + (*ol = (n)*(s), LZO_E_OK) + +#define __LZO_IS_OPTIMIZE_QUERY(i,il,o,ol,w) ((lzo_voidp)(o) == (w)) +#define __LZO_QUERY_OPTIMIZE(i,il,o,ol,w,n,s) \ + (*ol = (n)*(s), LZO_E_OK) + +#endif + +#ifndef __LZO_PTR_H +#define __LZO_PTR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__LZO_DOS16) || defined(__LZO_WIN16) +# include +# if 1 && defined(__WATCOMC__) +# include + __LZO_EXTERN_C unsigned char _HShift; +# define __LZO_HShift _HShift +# elif 1 && defined(_MSC_VER) + __LZO_EXTERN_C unsigned short __near _AHSHIFT; +# define __LZO_HShift ((unsigned) &_AHSHIFT) +# elif defined(__LZO_WIN16) +# define __LZO_HShift 3 +# else +# define __LZO_HShift 12 +# endif +# if !defined(_FP_SEG) && defined(FP_SEG) +# define _FP_SEG FP_SEG +# endif +# if !defined(_FP_OFF) && defined(FP_OFF) +# define _FP_OFF FP_OFF +# endif +#endif + +#if !defined(lzo_ptrdiff_t) +# if (UINT_MAX >= LZO_0xffffffffL) + typedef ptrdiff_t lzo_ptrdiff_t; +# else + typedef long lzo_ptrdiff_t; +# endif +#endif + +#if !defined(__LZO_HAVE_PTR_T) +# if defined(lzo_ptr_t) +# define __LZO_HAVE_PTR_T +# endif +#endif +#if !defined(__LZO_HAVE_PTR_T) +# if defined(SIZEOF_CHAR_P) && defined(SIZEOF_UNSIGNED_LONG) +# if (SIZEOF_CHAR_P == SIZEOF_UNSIGNED_LONG) + typedef unsigned long lzo_ptr_t; + typedef long lzo_sptr_t; +# define __LZO_HAVE_PTR_T +# endif +# endif +#endif +#if !defined(__LZO_HAVE_PTR_T) +# if defined(SIZEOF_CHAR_P) && defined(SIZEOF_UNSIGNED) +# if (SIZEOF_CHAR_P == SIZEOF_UNSIGNED) + typedef unsigned int lzo_ptr_t; + typedef int lzo_sptr_t; +# define __LZO_HAVE_PTR_T +# endif +# endif +#endif +#if !defined(__LZO_HAVE_PTR_T) +# if defined(SIZEOF_CHAR_P) && defined(SIZEOF_UNSIGNED_SHORT) +# if (SIZEOF_CHAR_P == SIZEOF_UNSIGNED_SHORT) + typedef unsigned short lzo_ptr_t; + typedef short lzo_sptr_t; +# define __LZO_HAVE_PTR_T +# endif +# endif +#endif +#if !defined(__LZO_HAVE_PTR_T) +# if defined(LZO_HAVE_CONFIG_H) || defined(SIZEOF_CHAR_P) +# error "no suitable type for lzo_ptr_t" +# else + typedef unsigned long lzo_ptr_t; + typedef long lzo_sptr_t; +# define __LZO_HAVE_PTR_T +# endif +#endif + +#if defined(__LZO_DOS16) || defined(__LZO_WIN16) +#define PTR(a) ((lzo_bytep) (a)) +#define PTR_ALIGNED_4(a) ((_FP_OFF(a) & 3) == 0) +#define PTR_ALIGNED2_4(a,b) (((_FP_OFF(a) | _FP_OFF(b)) & 3) == 0) +#else +#define PTR(a) ((lzo_ptr_t) (a)) +#define PTR_LINEAR(a) PTR(a) +#define PTR_ALIGNED_4(a) ((PTR_LINEAR(a) & 3) == 0) +#define PTR_ALIGNED_8(a) ((PTR_LINEAR(a) & 7) == 0) +#define PTR_ALIGNED2_4(a,b) (((PTR_LINEAR(a) | PTR_LINEAR(b)) & 3) == 0) +#define PTR_ALIGNED2_8(a,b) (((PTR_LINEAR(a) | PTR_LINEAR(b)) & 7) == 0) +#endif + +#define PTR_LT(a,b) (PTR(a) < PTR(b)) +#define PTR_GE(a,b) (PTR(a) >= PTR(b)) +#define PTR_DIFF(a,b) ((lzo_ptrdiff_t) (PTR(a) - PTR(b))) +#define pd(a,b) ((lzo_uint) ((a)-(b))) + +LZO_EXTERN(lzo_ptr_t) +__lzo_ptr_linear(const lzo_voidp ptr); + +typedef union +{ + char a_char; + unsigned char a_uchar; + short a_short; + unsigned short a_ushort; + int a_int; + unsigned int a_uint; + long a_long; + unsigned long a_ulong; + lzo_int a_lzo_int; + lzo_uint a_lzo_uint; + lzo_int32 a_lzo_int32; + lzo_uint32 a_lzo_uint32; + ptrdiff_t a_ptrdiff_t; + lzo_ptrdiff_t a_lzo_ptrdiff_t; + lzo_ptr_t a_lzo_ptr_t; + lzo_voidp a_lzo_voidp; + void * a_void_p; + lzo_bytep a_lzo_bytep; + lzo_bytepp a_lzo_bytepp; + lzo_uintp a_lzo_uintp; + lzo_uint * a_lzo_uint_p; + lzo_uint32p a_lzo_uint32p; + lzo_uint32 * a_lzo_uint32_p; + unsigned char * a_uchar_p; + char * a_char_p; +} +lzo_full_align_t; + +#ifdef __cplusplus +} +#endif + +#endif + +#define LZO_DETERMINISTIC + +#define LZO_DICT_USE_PTR +#if defined(__LZO_DOS16) || defined(__LZO_WIN16) || defined(__LZO_STRICT_16BIT) +# undef LZO_DICT_USE_PTR +#endif + +#if defined(LZO_DICT_USE_PTR) +# define lzo_dict_t const lzo_bytep +# define lzo_dict_p lzo_dict_t __LZO_MMODEL * +#else +# define lzo_dict_t lzo_uint +# define lzo_dict_p lzo_dict_t __LZO_MMODEL * +#endif + +#if !defined(lzo_moff_t) +#define lzo_moff_t lzo_uint +#endif + +#endif + +LZO_PUBLIC(lzo_ptr_t) +__lzo_ptr_linear(const lzo_voidp ptr) +{ + lzo_ptr_t p; + +#if defined(__LZO_DOS16) || defined(__LZO_WIN16) + p = (((lzo_ptr_t)(_FP_SEG(ptr))) << (16 - __LZO_HShift)) + (_FP_OFF(ptr)); +#else + p = PTR_LINEAR(ptr); +#endif + + return p; +} + +LZO_PUBLIC(unsigned) +__lzo_align_gap(const lzo_voidp ptr, lzo_uint size) +{ + lzo_ptr_t p, s, n; + + assert("lzo-01", size > 0); + + p = __lzo_ptr_linear(ptr); + s = (lzo_ptr_t) (size - 1); +#if 0 + assert((size & (size - 1)) == 0); + n = ((p + s) & ~s) - p; +#else + n = (((p + s) / size) * size) - p; +#endif + + assert("lzo-02", (long)n >= 0); + assert("lzo-03", n <= s); + + return (unsigned)n; +} + +#ifndef __LZO_UTIL_H +#define __LZO_UTIL_H + +#ifndef __LZO_CONF_H +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if 1 && defined(HAVE_MEMCPY) +#if !defined(__LZO_DOS16) && !defined(__LZO_WIN16) + +#define MEMCPY8_DS(dest,src,len) \ + memcpy(dest,src,len); \ + dest += len; \ + src += len + +#endif +#endif + +#if 0 && !defined(MEMCPY8_DS) + +#define MEMCPY8_DS(dest,src,len) \ + { do { \ + *dest++ = *src++; \ + *dest++ = *src++; \ + *dest++ = *src++; \ + *dest++ = *src++; \ + *dest++ = *src++; \ + *dest++ = *src++; \ + *dest++ = *src++; \ + *dest++ = *src++; \ + len -= 8; \ + } while (len > 0); } + +#endif + +#if !defined(MEMCPY8_DS) + +#define MEMCPY8_DS(dest,src,len) \ + { register lzo_uint __l = (len) / 8; \ + do { \ + *dest++ = *src++; \ + *dest++ = *src++; \ + *dest++ = *src++; \ + *dest++ = *src++; \ + *dest++ = *src++; \ + *dest++ = *src++; \ + *dest++ = *src++; \ + *dest++ = *src++; \ + } while (--__l > 0); } + +#endif + +#define MEMCPY_DS(dest,src,len) \ + do *dest++ = *src++; \ + while (--len > 0) + +#define MEMMOVE_DS(dest,src,len) \ + do *dest++ = *src++; \ + while (--len > 0) + +#if 0 && defined(LZO_OPTIMIZE_GNUC_i386) + +#define BZERO8_PTR(s,l,n) \ +__asm__ __volatile__( \ + "movl %0,%%eax \n" \ + "movl %1,%%edi \n" \ + "movl %2,%%ecx \n" \ + "cld \n" \ + "rep \n" \ + "stosl %%eax,(%%edi) \n" \ + : \ + :"g" (0),"g" (s),"g" (n) \ + :"eax","edi","ecx", "memory", "cc" \ +) + +#elif (LZO_UINT_MAX <= SIZE_T_MAX) && defined(HAVE_MEMSET) + +#if 1 +#define BZERO8_PTR(s,l,n) memset((s),0,(lzo_uint)(l)*(n)) +#else +#define BZERO8_PTR(s,l,n) memset((lzo_voidp)(s),0,(lzo_uint)(l)*(n)) +#endif + +#else + +#define BZERO8_PTR(s,l,n) \ + lzo_memset((lzo_voidp)(s),0,(lzo_uint)(l)*(n)) + +#endif + +#if 0 +#if defined(__GNUC__) && defined(__i386__) + +unsigned char lzo_rotr8(unsigned char value, int shift); +extern __inline__ unsigned char lzo_rotr8(unsigned char value, int shift) +{ + unsigned char result; + + __asm__ __volatile__ ("movb %b1, %b0; rorb %b2, %b0" + : "=a"(result) : "g"(value), "c"(shift)); + return result; +} + +unsigned short lzo_rotr16(unsigned short value, int shift); +extern __inline__ unsigned short lzo_rotr16(unsigned short value, int shift) +{ + unsigned short result; + + __asm__ __volatile__ ("movw %b1, %b0; rorw %b2, %b0" + : "=a"(result) : "g"(value), "c"(shift)); + return result; +} + +#endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +LZO_PUBLIC(lzo_bool) +lzo_assert(int expr) +{ + return (expr) ? 1 : 0; +} + +/* If you use the LZO library in a product, you *must* keep this + * copyright string in the executable of your product. + */ + +const lzo_byte __lzo_copyright[] = +#if !defined(__LZO_IN_MINLZO) + LZO_VERSION_STRING; +#else + "\n\n\n" + "LZO real-time data compression library.\n" + "Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002 Markus Franz Xaver Johannes Oberhumer\n" + "\n" + "http://www.oberhumer.com/opensource/lzo/\n" + "\n" + "LZO version: v" LZO_VERSION_STRING ", " LZO_VERSION_DATE "\n" + "LZO build date: " __DATE__ " " __TIME__ "\n\n" + "LZO special compilation options:\n" +#ifdef __cplusplus + " __cplusplus\n" +#endif +#if defined(__PIC__) + " __PIC__\n" +#elif defined(__pic__) + " __pic__\n" +#endif +#if (UINT_MAX < LZO_0xffffffffL) + " 16BIT\n" +#endif +#if defined(__LZO_STRICT_16BIT) + " __LZO_STRICT_16BIT\n" +#endif +#if (UINT_MAX > LZO_0xffffffffL) + " UINT_MAX=" _LZO_MEXPAND(UINT_MAX) "\n" +#endif +#if (ULONG_MAX > LZO_0xffffffffL) + " ULONG_MAX=" _LZO_MEXPAND(ULONG_MAX) "\n" +#endif +#if defined(LZO_BYTE_ORDER) + " LZO_BYTE_ORDER=" _LZO_MEXPAND(LZO_BYTE_ORDER) "\n" +#endif +#if defined(LZO_UNALIGNED_OK_2) + " LZO_UNALIGNED_OK_2\n" +#endif +#if defined(LZO_UNALIGNED_OK_4) + " LZO_UNALIGNED_OK_4\n" +#endif +#if defined(LZO_ALIGNED_OK_4) + " LZO_ALIGNED_OK_4\n" +#endif +#if defined(LZO_DICT_USE_PTR) + " LZO_DICT_USE_PTR\n" +#endif +#if defined(__LZO_QUERY_COMPRESS) + " __LZO_QUERY_COMPRESS\n" +#endif +#if defined(__LZO_QUERY_DECOMPRESS) + " __LZO_QUERY_DECOMPRESS\n" +#endif +#if defined(__LZO_IN_MINILZO) + " __LZO_IN_MINILZO\n" +#endif + "\n\n" + "$Id: LZO " LZO_VERSION_STRING " built " __DATE__ " " __TIME__ +#if defined(__GNUC__) && defined(__VERSION__) + " by gcc " __VERSION__ +#elif defined(__BORLANDC__) + " by Borland C " _LZO_MEXPAND(__BORLANDC__) +#elif defined(_MSC_VER) + " by Microsoft C " _LZO_MEXPAND(_MSC_VER) +#elif defined(__PUREC__) + " by Pure C " _LZO_MEXPAND(__PUREC__) +#elif defined(__SC__) + " by Symantec C " _LZO_MEXPAND(__SC__) +#elif defined(__TURBOC__) + " by Turbo C " _LZO_MEXPAND(__TURBOC__) +#elif defined(__WATCOMC__) + " by Watcom C " _LZO_MEXPAND(__WATCOMC__) +#endif + " $\n" + "$Copyright: LZO (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002 Markus Franz Xaver Johannes Oberhumer $\n"; +#endif + +LZO_PUBLIC(const lzo_byte *) +lzo_copyright(void) +{ + return __lzo_copyright; +} + +LZO_PUBLIC(unsigned) +lzo_version(void) +{ + return LZO_VERSION; +} + +LZO_PUBLIC(const char *) +lzo_version_string(void) +{ + return LZO_VERSION_STRING; +} + +LZO_PUBLIC(const char *) +lzo_version_date(void) +{ + return LZO_VERSION_DATE; +} + +LZO_PUBLIC(const lzo_charp) +_lzo_version_string(void) +{ + return LZO_VERSION_STRING; +} + +LZO_PUBLIC(const lzo_charp) +_lzo_version_date(void) +{ + return LZO_VERSION_DATE; +} + +#define LZO_BASE 65521u +#define LZO_NMAX 5552 + +#define LZO_DO1(buf,i) {s1 += buf[i]; s2 += s1;} +#define LZO_DO2(buf,i) LZO_DO1(buf,i); LZO_DO1(buf,i+1); +#define LZO_DO4(buf,i) LZO_DO2(buf,i); LZO_DO2(buf,i+2); +#define LZO_DO8(buf,i) LZO_DO4(buf,i); LZO_DO4(buf,i+4); +#define LZO_DO16(buf,i) LZO_DO8(buf,i); LZO_DO8(buf,i+8); + +LZO_PUBLIC(lzo_uint32) +lzo_adler32(lzo_uint32 adler, const lzo_byte *buf, lzo_uint len) +{ + lzo_uint32 s1 = adler & 0xffff; + lzo_uint32 s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == NULL) + return 1; + + while (len > 0) + { + k = len < LZO_NMAX ? (int) len : LZO_NMAX; + len -= k; + if (k >= 16) do + { + LZO_DO16(buf,0); + buf += 16; + k -= 16; + } while (k >= 16); + if (k != 0) do + { + s1 += *buf++; + s2 += s1; + } while (--k > 0); + s1 %= LZO_BASE; + s2 %= LZO_BASE; + } + return (s2 << 16) | s1; +} + +LZO_PUBLIC(int) +lzo_memcmp(const lzo_voidp s1, const lzo_voidp s2, lzo_uint len) +{ +#if (LZO_UINT_MAX <= SIZE_T_MAX) && defined(HAVE_MEMCMP) + return memcmp(s1,s2,len); +#else + const lzo_byte *p1 = (const lzo_byte *) s1; + const lzo_byte *p2 = (const lzo_byte *) s2; + int d; + + if (len > 0) do + { + d = *p1 - *p2; + if (d != 0) + return d; + p1++; + p2++; + } + while (--len > 0); + return 0; +#endif +} + +LZO_PUBLIC(lzo_voidp) +lzo_memcpy(lzo_voidp dest, const lzo_voidp src, lzo_uint len) +{ +#if (LZO_UINT_MAX <= SIZE_T_MAX) && defined(HAVE_MEMCPY) + return memcpy(dest,src,len); +#else + lzo_byte *p1 = (lzo_byte *) dest; + const lzo_byte *p2 = (const lzo_byte *) src; + + if (len <= 0 || p1 == p2) + return dest; + do + *p1++ = *p2++; + while (--len > 0); + return dest; +#endif +} + +LZO_PUBLIC(lzo_voidp) +lzo_memmove(lzo_voidp dest, const lzo_voidp src, lzo_uint len) +{ +#if (LZO_UINT_MAX <= SIZE_T_MAX) && defined(HAVE_MEMMOVE) + return memmove(dest,src,len); +#else + lzo_byte *p1 = (lzo_byte *) dest; + const lzo_byte *p2 = (const lzo_byte *) src; + + if (len <= 0 || p1 == p2) + return dest; + + if (p1 < p2) + { + do + *p1++ = *p2++; + while (--len > 0); + } + else + { + p1 += len; + p2 += len; + do + *--p1 = *--p2; + while (--len > 0); + } + return dest; +#endif +} + +LZO_PUBLIC(lzo_voidp) +lzo_memset(lzo_voidp s, int c, lzo_uint len) +{ +#if (LZO_UINT_MAX <= SIZE_T_MAX) && defined(HAVE_MEMSET) + return memset(s,c,len); +#else + lzo_byte *p = (lzo_byte *) s; + + if (len > 0) do + *p++ = LZO_BYTE(c); + while (--len > 0); + return s; +#endif +} + +#if 0 +# define IS_SIGNED(type) (((type) (1ul << (8 * sizeof(type) - 1))) < 0) +# define IS_UNSIGNED(type) (((type) (1ul << (8 * sizeof(type) - 1))) > 0) +#else +# define IS_SIGNED(type) (((type) (-1)) < ((type) 0)) +# define IS_UNSIGNED(type) (((type) (-1)) > ((type) 0)) +#endif + +#define IS_POWER_OF_2(x) (((x) & ((x) - 1)) == 0) + +static lzo_bool schedule_insns_bug(void); +static lzo_bool strength_reduce_bug(int *); + +#if 0 || defined(LZO_DEBUG) +#include +static lzo_bool __lzo_assert_fail(const char *s, unsigned line) +{ +#if defined(__palmos__) + printf("LZO assertion failed in line %u: '%s'\n",line,s); +#else + fprintf(stderr,"LZO assertion failed in line %u: '%s'\n",line,s); +#endif + return 0; +} +# define __lzo_assert(x) ((x) ? 1 : __lzo_assert_fail(#x,__LINE__)) +#else +# define __lzo_assert(x) ((x) ? 1 : 0) +#endif + +#undef COMPILE_TIME_ASSERT +#if 0 +# define COMPILE_TIME_ASSERT(expr) r &= __lzo_assert(expr) +#else +# define COMPILE_TIME_ASSERT(expr) LZO_COMPILE_TIME_ASSERT(expr) +#endif + +static lzo_bool basic_integral_check(void) +{ + lzo_bool r = 1; + + COMPILE_TIME_ASSERT(CHAR_BIT == 8); + COMPILE_TIME_ASSERT(sizeof(char) == 1); + COMPILE_TIME_ASSERT(sizeof(short) >= 2); + COMPILE_TIME_ASSERT(sizeof(long) >= 4); + COMPILE_TIME_ASSERT(sizeof(int) >= sizeof(short)); + COMPILE_TIME_ASSERT(sizeof(long) >= sizeof(int)); + + COMPILE_TIME_ASSERT(sizeof(lzo_uint) == sizeof(lzo_int)); + COMPILE_TIME_ASSERT(sizeof(lzo_uint32) == sizeof(lzo_int32)); + + COMPILE_TIME_ASSERT(sizeof(lzo_uint32) >= 4); + COMPILE_TIME_ASSERT(sizeof(lzo_uint32) >= sizeof(unsigned)); +#if defined(__LZO_STRICT_16BIT) + COMPILE_TIME_ASSERT(sizeof(lzo_uint) == 2); +#else + COMPILE_TIME_ASSERT(sizeof(lzo_uint) >= 4); + COMPILE_TIME_ASSERT(sizeof(lzo_uint) >= sizeof(unsigned)); +#endif + +#if (USHRT_MAX == 65535u) + COMPILE_TIME_ASSERT(sizeof(short) == 2); +#elif (USHRT_MAX == LZO_0xffffffffL) + COMPILE_TIME_ASSERT(sizeof(short) == 4); +#elif (USHRT_MAX >= LZO_0xffffffffL) + COMPILE_TIME_ASSERT(sizeof(short) > 4); +#endif +#if 0 /* to make gcc happy -edward */ +#if (UINT_MAX == 65535u) + COMPILE_TIME_ASSERT(sizeof(int) == 2); +#elif (UINT_MAX == LZO_0xffffffffL) + COMPILE_TIME_ASSERT(sizeof(int) == 4); +#elif (UINT_MAX >= LZO_0xffffffffL) + COMPILE_TIME_ASSERT(sizeof(int) > 4); +#endif +#if (ULONG_MAX == 65535ul) + COMPILE_TIME_ASSERT(sizeof(long) == 2); +#elif (ULONG_MAX == LZO_0xffffffffL) + COMPILE_TIME_ASSERT(sizeof(long) == 4); +#elif (ULONG_MAX >= LZO_0xffffffffL) + COMPILE_TIME_ASSERT(sizeof(long) > 4); +#endif +#if defined(SIZEOF_UNSIGNED) + COMPILE_TIME_ASSERT(SIZEOF_UNSIGNED == sizeof(unsigned)); +#endif +#if defined(SIZEOF_UNSIGNED_LONG) + COMPILE_TIME_ASSERT(SIZEOF_UNSIGNED_LONG == sizeof(unsigned long)); +#endif +#if defined(SIZEOF_UNSIGNED_SHORT) + COMPILE_TIME_ASSERT(SIZEOF_UNSIGNED_SHORT == sizeof(unsigned short)); +#endif +#if !defined(__LZO_IN_MINILZO) +#if defined(SIZEOF_SIZE_T) + COMPILE_TIME_ASSERT(SIZEOF_SIZE_T == sizeof(size_t)); +#endif +#endif +#endif /* -edward */ + + COMPILE_TIME_ASSERT(IS_UNSIGNED(unsigned char)); + COMPILE_TIME_ASSERT(IS_UNSIGNED(unsigned short)); + COMPILE_TIME_ASSERT(IS_UNSIGNED(unsigned)); + COMPILE_TIME_ASSERT(IS_UNSIGNED(unsigned long)); + COMPILE_TIME_ASSERT(IS_SIGNED(short)); + COMPILE_TIME_ASSERT(IS_SIGNED(int)); + COMPILE_TIME_ASSERT(IS_SIGNED(long)); + + COMPILE_TIME_ASSERT(IS_UNSIGNED(lzo_uint32)); + COMPILE_TIME_ASSERT(IS_UNSIGNED(lzo_uint)); + COMPILE_TIME_ASSERT(IS_SIGNED(lzo_int32)); + COMPILE_TIME_ASSERT(IS_SIGNED(lzo_int)); + + COMPILE_TIME_ASSERT(INT_MAX == LZO_STYPE_MAX(sizeof(int))); + COMPILE_TIME_ASSERT(UINT_MAX == LZO_UTYPE_MAX(sizeof(unsigned))); + COMPILE_TIME_ASSERT(LONG_MAX == LZO_STYPE_MAX(sizeof(long))); + COMPILE_TIME_ASSERT(ULONG_MAX == LZO_UTYPE_MAX(sizeof(unsigned long))); + // COMPILE_TIME_ASSERT(SHRT_MAX == LZO_STYPE_MAX(sizeof(short))); /* edward */ + COMPILE_TIME_ASSERT(USHRT_MAX == LZO_UTYPE_MAX(sizeof(unsigned short))); + COMPILE_TIME_ASSERT(LZO_UINT32_MAX == LZO_UTYPE_MAX(sizeof(lzo_uint32))); + COMPILE_TIME_ASSERT(LZO_UINT_MAX == LZO_UTYPE_MAX(sizeof(lzo_uint))); +#if !defined(__LZO_IN_MINILZO) + COMPILE_TIME_ASSERT(SIZE_T_MAX == LZO_UTYPE_MAX(sizeof(size_t))); +#endif + + r &= __lzo_assert(LZO_BYTE(257) == 1); + + return r; +} + +static lzo_bool basic_ptr_check(void) +{ + lzo_bool r = 1; + + COMPILE_TIME_ASSERT(sizeof(char *) >= sizeof(int)); + COMPILE_TIME_ASSERT(sizeof(lzo_byte *) >= sizeof(char *)); + + COMPILE_TIME_ASSERT(sizeof(lzo_voidp) == sizeof(lzo_byte *)); + COMPILE_TIME_ASSERT(sizeof(lzo_voidp) == sizeof(lzo_voidpp)); + COMPILE_TIME_ASSERT(sizeof(lzo_voidp) == sizeof(lzo_bytepp)); + COMPILE_TIME_ASSERT(sizeof(lzo_voidp) >= sizeof(lzo_uint)); + + COMPILE_TIME_ASSERT(sizeof(lzo_ptr_t) == sizeof(lzo_voidp)); + COMPILE_TIME_ASSERT(sizeof(lzo_ptr_t) == sizeof(lzo_sptr_t)); + COMPILE_TIME_ASSERT(sizeof(lzo_ptr_t) >= sizeof(lzo_uint)); + + COMPILE_TIME_ASSERT(sizeof(lzo_ptrdiff_t) >= 4); + COMPILE_TIME_ASSERT(sizeof(lzo_ptrdiff_t) >= sizeof(ptrdiff_t)); + + COMPILE_TIME_ASSERT(sizeof(ptrdiff_t) >= sizeof(size_t)); + COMPILE_TIME_ASSERT(sizeof(lzo_ptrdiff_t) >= sizeof(lzo_uint)); + +#if defined(SIZEOF_CHAR_P) + COMPILE_TIME_ASSERT(SIZEOF_CHAR_P == sizeof(char *)); +#endif +#if defined(SIZEOF_PTRDIFF_T) + COMPILE_TIME_ASSERT(SIZEOF_PTRDIFF_T == sizeof(ptrdiff_t)); +#endif + + COMPILE_TIME_ASSERT(IS_SIGNED(ptrdiff_t)); + COMPILE_TIME_ASSERT(IS_UNSIGNED(size_t)); + COMPILE_TIME_ASSERT(IS_SIGNED(lzo_ptrdiff_t)); + COMPILE_TIME_ASSERT(IS_SIGNED(lzo_sptr_t)); + COMPILE_TIME_ASSERT(IS_UNSIGNED(lzo_ptr_t)); + COMPILE_TIME_ASSERT(IS_UNSIGNED(lzo_moff_t)); + + return r; +} + +static lzo_bool ptr_check(void) +{ + lzo_bool r = 1; + int i; + char _wrkmem[10 * sizeof(lzo_byte *) + sizeof(lzo_full_align_t)]; + lzo_bytep wrkmem; + lzo_bytepp dict; + unsigned char x[4 * sizeof(lzo_full_align_t)]; + long d; + lzo_full_align_t a; + lzo_full_align_t u; + + for (i = 0; i < (int) sizeof(x); i++) + x[i] = LZO_BYTE(i); + + wrkmem = LZO_PTR_ALIGN_UP((lzo_byte *)_wrkmem,sizeof(lzo_full_align_t)); + +#if 0 + dict = (lzo_bytepp) wrkmem; +#else + + u.a_lzo_bytep = wrkmem; dict = u.a_lzo_bytepp; +#endif + + d = (long) ((const lzo_bytep) dict - (const lzo_bytep) _wrkmem); + r &= __lzo_assert(d >= 0); + r &= __lzo_assert(d < (long) sizeof(lzo_full_align_t)); + + memset(&a,0,sizeof(a)); + r &= __lzo_assert(a.a_lzo_voidp == NULL); + + memset(&a,0xff,sizeof(a)); + r &= __lzo_assert(a.a_ushort == USHRT_MAX); + r &= __lzo_assert(a.a_uint == UINT_MAX); + r &= __lzo_assert(a.a_ulong == ULONG_MAX); + r &= __lzo_assert(a.a_lzo_uint == LZO_UINT_MAX); + r &= __lzo_assert(a.a_lzo_uint32 == LZO_UINT32_MAX); + + if (r == 1) + { + for (i = 0; i < 8; i++) + r &= __lzo_assert((const lzo_voidp) (&dict[i]) == (const lzo_voidp) (&wrkmem[i * sizeof(lzo_byte *)])); + } + + memset(&a,0,sizeof(a)); + r &= __lzo_assert(a.a_char_p == NULL); + r &= __lzo_assert(a.a_lzo_bytep == NULL); + r &= __lzo_assert(NULL == (void *)0); + if (r == 1) + { + for (i = 0; i < 10; i++) + dict[i] = wrkmem; + BZERO8_PTR(dict+1,sizeof(dict[0]),8); + r &= __lzo_assert(dict[0] == wrkmem); + for (i = 1; i < 9; i++) + r &= __lzo_assert(dict[i] == NULL); + r &= __lzo_assert(dict[9] == wrkmem); + } + + if (r == 1) + { + unsigned k = 1; + const unsigned n = (unsigned) sizeof(lzo_uint32); + lzo_byte *p0; + lzo_byte *p1; + + k += __lzo_align_gap(&x[k],n); + p0 = (lzo_bytep) &x[k]; +#if defined(PTR_LINEAR) + r &= __lzo_assert((PTR_LINEAR(p0) & (n-1)) == 0); +#else + r &= __lzo_assert(n == 4); + r &= __lzo_assert(PTR_ALIGNED_4(p0)); +#endif + + r &= __lzo_assert(k >= 1); + p1 = (lzo_bytep) &x[1]; + r &= __lzo_assert(PTR_GE(p0,p1)); + + r &= __lzo_assert(k < 1+n); + p1 = (lzo_bytep) &x[1+n]; + r &= __lzo_assert(PTR_LT(p0,p1)); + + if (r == 1) + { + lzo_uint32 v0, v1; +#if 0 + v0 = * (lzo_uint32 *) &x[k]; + v1 = * (lzo_uint32 *) &x[k+n]; +#else + + u.a_uchar_p = &x[k]; + v0 = *u.a_lzo_uint32_p; + u.a_uchar_p = &x[k+n]; + v1 = *u.a_lzo_uint32_p; +#endif + r &= __lzo_assert(v0 > 0); + r &= __lzo_assert(v1 > 0); + } + } + + return r; +} + +LZO_PUBLIC(int) +_lzo_config_check(void) +{ + lzo_bool r = 1; + int i; + union { + lzo_uint32 a; + unsigned short b; + lzo_uint32 aa[4]; + unsigned char x[4*sizeof(lzo_full_align_t)]; + } u; + + COMPILE_TIME_ASSERT( (int) ((unsigned char) ((signed char) -1)) == 255); + COMPILE_TIME_ASSERT( (((unsigned char)128) << (int)(8*sizeof(int)-8)) < 0); + +#if 0 + r &= __lzo_assert((const void *)&u == (const void *)&u.a); + r &= __lzo_assert((const void *)&u == (const void *)&u.b); + r &= __lzo_assert((const void *)&u == (const void *)&u.x[0]); + r &= __lzo_assert((const void *)&u == (const void *)&u.aa[0]); +#endif + + r &= basic_integral_check(); + r &= basic_ptr_check(); + if (r != 1) + return LZO_E_ERROR; + + u.a = 0; u.b = 0; + for (i = 0; i < (int) sizeof(u.x); i++) + u.x[i] = LZO_BYTE(i); + +#if defined(LZO_BYTE_ORDER) + if (r == 1) + { +# if (LZO_BYTE_ORDER == LZO_LITTLE_ENDIAN) + lzo_uint32 a = (lzo_uint32) (u.a & LZO_0xffffffffL); + unsigned short b = (unsigned short) (u.b & 0xffff); + r &= __lzo_assert(a == 0x03020100L); + r &= __lzo_assert(b == 0x0100); +# elif (LZO_BYTE_ORDER == LZO_BIG_ENDIAN) + lzo_uint32 a = u.a >> (8 * sizeof(u.a) - 32); + unsigned short b = u.b >> (8 * sizeof(u.b) - 16); + r &= __lzo_assert(a == 0x00010203L); + r &= __lzo_assert(b == 0x0001); +# else +# error "invalid LZO_BYTE_ORDER" +# endif + } +#endif + +#if defined(LZO_UNALIGNED_OK_2) + COMPILE_TIME_ASSERT(sizeof(short) == 2); + if (r == 1) + { + unsigned short b[4]; + + for (i = 0; i < 4; i++) + b[i] = * (const unsigned short *) &u.x[i]; + +# if (LZO_BYTE_ORDER == LZO_LITTLE_ENDIAN) + r &= __lzo_assert(b[0] == 0x0100); + r &= __lzo_assert(b[1] == 0x0201); + r &= __lzo_assert(b[2] == 0x0302); + r &= __lzo_assert(b[3] == 0x0403); +# elif (LZO_BYTE_ORDER == LZO_BIG_ENDIAN) + r &= __lzo_assert(b[0] == 0x0001); + r &= __lzo_assert(b[1] == 0x0102); + r &= __lzo_assert(b[2] == 0x0203); + r &= __lzo_assert(b[3] == 0x0304); +# endif + } +#endif + +#if defined(LZO_UNALIGNED_OK_4) + COMPILE_TIME_ASSERT(sizeof(lzo_uint32) == 4); + if (r == 1) + { + lzo_uint32 a[4]; + + for (i = 0; i < 4; i++) + a[i] = * (const lzo_uint32 *) &u.x[i]; + +# if (LZO_BYTE_ORDER == LZO_LITTLE_ENDIAN) + r &= __lzo_assert(a[0] == 0x03020100L); + r &= __lzo_assert(a[1] == 0x04030201L); + r &= __lzo_assert(a[2] == 0x05040302L); + r &= __lzo_assert(a[3] == 0x06050403L); +# elif (LZO_BYTE_ORDER == LZO_BIG_ENDIAN) + r &= __lzo_assert(a[0] == 0x00010203L); + r &= __lzo_assert(a[1] == 0x01020304L); + r &= __lzo_assert(a[2] == 0x02030405L); + r &= __lzo_assert(a[3] == 0x03040506L); +# endif + } +#endif + +#if defined(LZO_ALIGNED_OK_4) + COMPILE_TIME_ASSERT(sizeof(lzo_uint32) == 4); +#endif + + COMPILE_TIME_ASSERT(lzo_sizeof_dict_t == sizeof(lzo_dict_t)); + +#if defined(__LZO_IN_MINLZO) + if (r == 1) + { + lzo_uint32 adler; + adler = lzo_adler32(0, NULL, 0); + adler = lzo_adler32(adler, lzo_copyright(), 200); + r &= __lzo_assert(adler == 0xc76f1751L); + } +#endif + + if (r == 1) + { + r &= __lzo_assert(!schedule_insns_bug()); + } + + if (r == 1) + { + static int x[3]; + static unsigned xn = 3; + register unsigned j; + + for (j = 0; j < xn; j++) + x[j] = (int)j - 3; + r &= __lzo_assert(!strength_reduce_bug(x)); + } + + if (r == 1) + { + r &= ptr_check(); + } + + return r == 1 ? LZO_E_OK : LZO_E_ERROR; +} + +static lzo_bool schedule_insns_bug(void) +{ +#if defined(__LZO_CHECKER) + return 0; +#else + const int clone[] = {1, 2, 0}; + const int *q; + q = clone; + return (*q) ? 0 : 1; +#endif +} + +static lzo_bool strength_reduce_bug(int *x) +{ + return x[0] != -3 || x[1] != -2 || x[2] != -1; +} + +#undef COMPILE_TIME_ASSERT + +int __lzo_init_done = 0; + +LZO_PUBLIC(int) +__lzo_init2(unsigned v, int s1, int s2, int s3, int s4, int s5, + int s6, int s7, int s8, int s9) +{ + int r; + + __lzo_init_done = 1; + + if (v == 0) + return LZO_E_ERROR; + + r = (s1 == -1 || s1 == (int) sizeof(short)) && + (s2 == -1 || s2 == (int) sizeof(int)) && + (s3 == -1 || s3 == (int) sizeof(long)) && + (s4 == -1 || s4 == (int) sizeof(lzo_uint32)) && + (s5 == -1 || s5 == (int) sizeof(lzo_uint)) && + (s6 == -1 || s6 == (int) lzo_sizeof_dict_t) && + (s7 == -1 || s7 == (int) sizeof(char *)) && + (s8 == -1 || s8 == (int) sizeof(lzo_voidp)) && + (s9 == -1 || s9 == (int) sizeof(lzo_compress_t)); + if (!r) + return LZO_E_ERROR; + + r = _lzo_config_check(); + if (r != LZO_E_OK) + return r; + + return r; +} + +#if !defined(__LZO_IN_MINILZO) + +LZO_EXTERN(int) +__lzo_init(unsigned v,int s1,int s2,int s3,int s4,int s5,int s6,int s7); + +LZO_PUBLIC(int) +__lzo_init(unsigned v,int s1,int s2,int s3,int s4,int s5,int s6,int s7) +{ + if (v == 0 || v > 0x1010) + return LZO_E_ERROR; + return __lzo_init2(v,s1,s2,s3,s4,s5,-1,-1,s6,s7); +} + +#endif + +#define do_compress _lzo1x_1_do_compress + +#define LZO_NEED_DICT_H +#define D_BITS 14 +#define D_INDEX1(d,p) d = DM((0x21*DX3(p,5,5,6)) >> 5) +#define D_INDEX2(d,p) d = (d & (D_MASK & 0x7ff)) ^ (D_HIGH | 0x1f) + +#ifndef __LZO_CONFIG1X_H +#define __LZO_CONFIG1X_H + +#if !defined(LZO1X) && !defined(LZO1Y) && !defined(LZO1Z) +# define LZO1X +#endif + +#if !defined(__LZO_IN_MINILZO) +#include +#endif + +#define LZO_EOF_CODE +#undef LZO_DETERMINISTIC + +#define M1_MAX_OFFSET 0x0400 +#ifndef M2_MAX_OFFSET +#define M2_MAX_OFFSET 0x0800 +#endif +#define M3_MAX_OFFSET 0x4000 +#define M4_MAX_OFFSET 0xbfff + +#define MX_MAX_OFFSET (M1_MAX_OFFSET + M2_MAX_OFFSET) + +#define M1_MIN_LEN 2 +#define M1_MAX_LEN 2 +#define M2_MIN_LEN 3 +#ifndef M2_MAX_LEN +#define M2_MAX_LEN 8 +#endif +#define M3_MIN_LEN 3 +#define M3_MAX_LEN 33 +#define M4_MIN_LEN 3 +#define M4_MAX_LEN 9 + +#define M1_MARKER 0 +#define M2_MARKER 64 +#define M3_MARKER 32 +#define M4_MARKER 16 + +#ifndef MIN_LOOKAHEAD +#define MIN_LOOKAHEAD (M2_MAX_LEN + 1) +#endif + +#if defined(LZO_NEED_DICT_H) + +#ifndef LZO_HASH +#define LZO_HASH LZO_HASH_LZO_INCREMENTAL_B +#endif +#define DL_MIN_LEN M2_MIN_LEN + +#ifndef __LZO_DICT_H +#define __LZO_DICT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(D_BITS) && defined(DBITS) +# define D_BITS DBITS +#endif +#if !defined(D_BITS) +# error "D_BITS is not defined" +#endif +#if (D_BITS < 16) +# define D_SIZE LZO_SIZE(D_BITS) +# define D_MASK LZO_MASK(D_BITS) +#else +# define D_SIZE LZO_USIZE(D_BITS) +# define D_MASK LZO_UMASK(D_BITS) +#endif +#define D_HIGH ((D_MASK >> 1) + 1) + +#if !defined(DD_BITS) +# define DD_BITS 0 +#endif +#define DD_SIZE LZO_SIZE(DD_BITS) +#define DD_MASK LZO_MASK(DD_BITS) + +#if !defined(DL_BITS) +# define DL_BITS (D_BITS - DD_BITS) +#endif +#if (DL_BITS < 16) +# define DL_SIZE LZO_SIZE(DL_BITS) +# define DL_MASK LZO_MASK(DL_BITS) +#else +# define DL_SIZE LZO_USIZE(DL_BITS) +# define DL_MASK LZO_UMASK(DL_BITS) +#endif + +#if (D_BITS != DL_BITS + DD_BITS) +# error "D_BITS does not match" +#endif +#if (D_BITS < 8 || D_BITS > 18) +# error "invalid D_BITS" +#endif +#if (DL_BITS < 8 || DL_BITS > 20) +# error "invalid DL_BITS" +#endif +#if (DD_BITS < 0 || DD_BITS > 6) +# error "invalid DD_BITS" +#endif + +#if !defined(DL_MIN_LEN) +# define DL_MIN_LEN 3 +#endif +#if !defined(DL_SHIFT) +# define DL_SHIFT ((DL_BITS + (DL_MIN_LEN - 1)) / DL_MIN_LEN) +#endif + +#define LZO_HASH_GZIP 1 +#define LZO_HASH_GZIP_INCREMENTAL 2 +#define LZO_HASH_LZO_INCREMENTAL_A 3 +#define LZO_HASH_LZO_INCREMENTAL_B 4 + +#if !defined(LZO_HASH) +# error "choose a hashing strategy" +#endif + +#if (DL_MIN_LEN == 3) +# define _DV2_A(p,shift1,shift2) \ + (((( (lzo_uint32)((p)[0]) << shift1) ^ (p)[1]) << shift2) ^ (p)[2]) +# define _DV2_B(p,shift1,shift2) \ + (((( (lzo_uint32)((p)[2]) << shift1) ^ (p)[1]) << shift2) ^ (p)[0]) +# define _DV3_B(p,shift1,shift2,shift3) \ + ((_DV2_B((p)+1,shift1,shift2) << (shift3)) ^ (p)[0]) +#elif (DL_MIN_LEN == 2) +# define _DV2_A(p,shift1,shift2) \ + (( (lzo_uint32)(p[0]) << shift1) ^ p[1]) +# define _DV2_B(p,shift1,shift2) \ + (( (lzo_uint32)(p[1]) << shift1) ^ p[2]) +#else +# error "invalid DL_MIN_LEN" +#endif +#define _DV_A(p,shift) _DV2_A(p,shift,shift) +#define _DV_B(p,shift) _DV2_B(p,shift,shift) +#define DA2(p,s1,s2) \ + (((((lzo_uint32)((p)[2]) << (s2)) + (p)[1]) << (s1)) + (p)[0]) +#define DS2(p,s1,s2) \ + (((((lzo_uint32)((p)[2]) << (s2)) - (p)[1]) << (s1)) - (p)[0]) +#define DX2(p,s1,s2) \ + (((((lzo_uint32)((p)[2]) << (s2)) ^ (p)[1]) << (s1)) ^ (p)[0]) +#define DA3(p,s1,s2,s3) ((DA2((p)+1,s2,s3) << (s1)) + (p)[0]) +#define DS3(p,s1,s2,s3) ((DS2((p)+1,s2,s3) << (s1)) - (p)[0]) +#define DX3(p,s1,s2,s3) ((DX2((p)+1,s2,s3) << (s1)) ^ (p)[0]) +#define DMS(v,s) ((lzo_uint) (((v) & (D_MASK >> (s))) << (s))) +#define DM(v) DMS(v,0) + +#if (LZO_HASH == LZO_HASH_GZIP) +# define _DINDEX(dv,p) (_DV_A((p),DL_SHIFT)) + +#elif (LZO_HASH == LZO_HASH_GZIP_INCREMENTAL) +# define __LZO_HASH_INCREMENTAL +# define DVAL_FIRST(dv,p) dv = _DV_A((p),DL_SHIFT) +# define DVAL_NEXT(dv,p) dv = (((dv) << DL_SHIFT) ^ p[2]) +# define _DINDEX(dv,p) (dv) +# define DVAL_LOOKAHEAD DL_MIN_LEN + +#elif (LZO_HASH == LZO_HASH_LZO_INCREMENTAL_A) +# define __LZO_HASH_INCREMENTAL +# define DVAL_FIRST(dv,p) dv = _DV_A((p),5) +# define DVAL_NEXT(dv,p) \ + dv ^= (lzo_uint32)(p[-1]) << (2*5); dv = (((dv) << 5) ^ p[2]) +# define _DINDEX(dv,p) ((0x9f5f * (dv)) >> 5) +# define DVAL_LOOKAHEAD DL_MIN_LEN + +#elif (LZO_HASH == LZO_HASH_LZO_INCREMENTAL_B) +# define __LZO_HASH_INCREMENTAL +# define DVAL_FIRST(dv,p) dv = _DV_B((p),5) +# define DVAL_NEXT(dv,p) \ + dv ^= p[-1]; dv = (((dv) >> 5) ^ ((lzo_uint32)(p[2]) << (2*5))) +# define _DINDEX(dv,p) ((0x9f5f * (dv)) >> 5) +# define DVAL_LOOKAHEAD DL_MIN_LEN + +#else +# error "choose a hashing strategy" +#endif + +#ifndef DINDEX +#define DINDEX(dv,p) ((lzo_uint)((_DINDEX(dv,p)) & DL_MASK) << DD_BITS) +#endif +#if !defined(DINDEX1) && defined(D_INDEX1) +#define DINDEX1 D_INDEX1 +#endif +#if !defined(DINDEX2) && defined(D_INDEX2) +#define DINDEX2 D_INDEX2 +#endif + +#if !defined(__LZO_HASH_INCREMENTAL) +# define DVAL_FIRST(dv,p) ((void) 0) +# define DVAL_NEXT(dv,p) ((void) 0) +# define DVAL_LOOKAHEAD 0 +#endif + +#if !defined(DVAL_ASSERT) +#if defined(__LZO_HASH_INCREMENTAL) && !defined(NDEBUG) +static void DVAL_ASSERT(lzo_uint32 dv, const lzo_byte *p) +{ + lzo_uint32 df; + DVAL_FIRST(df,(p)); + assert(DINDEX(dv,p) == DINDEX(df,p)); +} +#else +# define DVAL_ASSERT(dv,p) ((void) 0) +#endif +#endif + +#if defined(LZO_DICT_USE_PTR) +# define DENTRY(p,in) (p) +# define GINDEX(m_pos,m_off,dict,dindex,in) m_pos = dict[dindex] +#else +# define DENTRY(p,in) ((lzo_uint) ((p)-(in))) +# define GINDEX(m_pos,m_off,dict,dindex,in) m_off = dict[dindex] +#endif + +#if (DD_BITS == 0) + +# define UPDATE_D(dict,drun,dv,p,in) dict[ DINDEX(dv,p) ] = DENTRY(p,in) +# define UPDATE_I(dict,drun,index,p,in) dict[index] = DENTRY(p,in) +# define UPDATE_P(ptr,drun,p,in) (ptr)[0] = DENTRY(p,in) + +#else + +# define UPDATE_D(dict,drun,dv,p,in) \ + dict[ DINDEX(dv,p) + drun++ ] = DENTRY(p,in); drun &= DD_MASK +# define UPDATE_I(dict,drun,index,p,in) \ + dict[ (index) + drun++ ] = DENTRY(p,in); drun &= DD_MASK +# define UPDATE_P(ptr,drun,p,in) \ + (ptr) [ drun++ ] = DENTRY(p,in); drun &= DD_MASK + +#endif + +#if defined(LZO_DICT_USE_PTR) + +#define LZO_CHECK_MPOS_DET(m_pos,m_off,in,ip,max_offset) \ + (m_pos == NULL || (m_off = (lzo_moff_t) (ip - m_pos)) > max_offset) + +#define LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,max_offset) \ + (BOUNDS_CHECKING_OFF_IN_EXPR( \ + (PTR_LT(m_pos,in) || \ + (m_off = (lzo_moff_t) PTR_DIFF(ip,m_pos)) <= 0 || \ + m_off > max_offset) )) + +#else + +#define LZO_CHECK_MPOS_DET(m_pos,m_off,in,ip,max_offset) \ + (m_off == 0 || \ + ((m_off = (lzo_moff_t) ((ip)-(in)) - m_off) > max_offset) || \ + (m_pos = (ip) - (m_off), 0) ) + +#define LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,max_offset) \ + ((lzo_moff_t) ((ip)-(in)) <= m_off || \ + ((m_off = (lzo_moff_t) ((ip)-(in)) - m_off) > max_offset) || \ + (m_pos = (ip) - (m_off), 0) ) + +#endif + +#if defined(LZO_DETERMINISTIC) +# define LZO_CHECK_MPOS LZO_CHECK_MPOS_DET +#else +# define LZO_CHECK_MPOS LZO_CHECK_MPOS_NON_DET +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +#endif + +#endif + +#define DO_COMPRESS lzo1x_1_compress + +static +lzo_uint do_compress ( const lzo_byte *in , lzo_uint in_len, + lzo_byte *out, lzo_uintp out_len, + lzo_voidp wrkmem ) +{ +#if 0 && defined(__GNUC__) && defined(__i386__) + register const lzo_byte *ip __asm__("%esi"); +#else + register const lzo_byte *ip; +#endif + lzo_byte *op; + const lzo_byte * const in_end = in + in_len; + const lzo_byte * const ip_end = in + in_len - M2_MAX_LEN - 5; + const lzo_byte *ii; + lzo_dict_p const dict = (lzo_dict_p) wrkmem; + + op = out; + ip = in; + ii = ip; + + ip += 4; + for (;;) + { +#if 0 && defined(__GNUC__) && defined(__i386__) + register const lzo_byte *m_pos __asm__("%edi"); +#else + register const lzo_byte *m_pos; +#endif + lzo_moff_t m_off; + lzo_uint m_len; + lzo_uint dindex; + + DINDEX1(dindex,ip); + GINDEX(m_pos,m_off,dict,dindex,in); + if (LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,M4_MAX_OFFSET)) + goto literal; +#if 1 + if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3]) + goto try_match; + DINDEX2(dindex,ip); +#endif + GINDEX(m_pos,m_off,dict,dindex,in); + if (LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,M4_MAX_OFFSET)) + goto literal; + if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3]) + goto try_match; + goto literal; + +try_match: +#if 1 && defined(LZO_UNALIGNED_OK_2) + if (* (const lzo_ushortp) m_pos != * (const lzo_ushortp) ip) +#else + if (m_pos[0] != ip[0] || m_pos[1] != ip[1]) +#endif + { + } + else + { + if (m_pos[2] == ip[2]) + { +#if 0 + if (m_off <= M2_MAX_OFFSET) + goto match; + if (lit <= 3) + goto match; + if (lit == 3) + { + assert(op - 2 > out); op[-2] |= LZO_BYTE(3); + *op++ = *ii++; *op++ = *ii++; *op++ = *ii++; + goto code_match; + } + if (m_pos[3] == ip[3]) +#endif + goto match; + } + else + { +#if 0 +#if 0 + if (m_off <= M1_MAX_OFFSET && lit > 0 && lit <= 3) +#else + if (m_off <= M1_MAX_OFFSET && lit == 3) +#endif + { + register lzo_uint t; + + t = lit; + assert(op - 2 > out); op[-2] |= LZO_BYTE(t); + do *op++ = *ii++; while (--t > 0); + assert(ii == ip); + m_off -= 1; + *op++ = LZO_BYTE(M1_MARKER | ((m_off & 3) << 2)); + *op++ = LZO_BYTE(m_off >> 2); + ip += 2; + goto match_done; + } +#endif + } + } + +literal: + UPDATE_I(dict,0,dindex,ip,in); + ++ip; + if (ip >= ip_end) + break; + continue; + +match: + UPDATE_I(dict,0,dindex,ip,in); + if (pd(ip,ii) > 0) + { + register lzo_uint t = pd(ip,ii); + + if (t <= 3) + { + assert("lzo-04", op - 2 > out); + op[-2] |= LZO_BYTE(t); + } + else if (t <= 18) + *op++ = LZO_BYTE(t - 3); + else + { + register lzo_uint tt = t - 18; + + *op++ = 0; + while (tt > 255) + { + tt -= 255; + *op++ = 0; + } + assert("lzo-05", tt > 0); + *op++ = LZO_BYTE(tt); + } + do *op++ = *ii++; while (--t > 0); + } + + assert("lzo-06", ii == ip); + ip += 3; + if (m_pos[3] != *ip++ || m_pos[4] != *ip++ || m_pos[5] != *ip++ || + m_pos[6] != *ip++ || m_pos[7] != *ip++ || m_pos[8] != *ip++ +#ifdef LZO1Y + || m_pos[ 9] != *ip++ || m_pos[10] != *ip++ || m_pos[11] != *ip++ + || m_pos[12] != *ip++ || m_pos[13] != *ip++ || m_pos[14] != *ip++ +#endif + ) + { + --ip; + m_len = ip - ii; + assert("lzo-07", m_len >= 3); assert("lzo-08", m_len <= M2_MAX_LEN); + + if (m_off <= M2_MAX_OFFSET) + { + m_off -= 1; +#if defined(LZO1X) + *op++ = LZO_BYTE(((m_len - 1) << 5) | ((m_off & 7) << 2)); + *op++ = LZO_BYTE(m_off >> 3); +#elif defined(LZO1Y) + *op++ = LZO_BYTE(((m_len + 1) << 4) | ((m_off & 3) << 2)); + *op++ = LZO_BYTE(m_off >> 2); +#endif + } + else if (m_off <= M3_MAX_OFFSET) + { + m_off -= 1; + *op++ = LZO_BYTE(M3_MARKER | (m_len - 2)); + goto m3_m4_offset; + } + else +#if defined(LZO1X) + { + m_off -= 0x4000; + assert("lzo-09", m_off > 0); assert("lzo-10", m_off <= 0x7fff); + *op++ = LZO_BYTE(M4_MARKER | + ((m_off & 0x4000) >> 11) | (m_len - 2)); + goto m3_m4_offset; + } +#elif defined(LZO1Y) + goto m4_match; +#endif + } + else + { + { + const lzo_byte *end = in_end; + const lzo_byte *m = m_pos + M2_MAX_LEN + 1; + while (ip < end && *m == *ip) + m++, ip++; + m_len = (ip - ii); + } + assert("lzo-11", m_len > M2_MAX_LEN); + + if (m_off <= M3_MAX_OFFSET) + { + m_off -= 1; + if (m_len <= 33) + *op++ = LZO_BYTE(M3_MARKER | (m_len - 2)); + else + { + m_len -= 33; + *op++ = M3_MARKER | 0; + goto m3_m4_len; + } + } + else + { +#if defined(LZO1Y) +m4_match: +#endif + m_off -= 0x4000; + assert("lzo-12", m_off > 0); assert("lzo-13", m_off <= 0x7fff); + if (m_len <= M4_MAX_LEN) + *op++ = LZO_BYTE(M4_MARKER | + ((m_off & 0x4000) >> 11) | (m_len - 2)); + else + { + m_len -= M4_MAX_LEN; + *op++ = LZO_BYTE(M4_MARKER | ((m_off & 0x4000) >> 11)); +m3_m4_len: + while (m_len > 255) + { + m_len -= 255; + *op++ = 0; + } + assert("lzo-14", m_len > 0); + *op++ = LZO_BYTE(m_len); + } + } + +m3_m4_offset: + *op++ = LZO_BYTE((m_off & 63) << 2); + *op++ = LZO_BYTE(m_off >> 6); + } + +#if 0 +match_done: +#endif + ii = ip; + if (ip >= ip_end) + break; + } + + *out_len = op - out; + return pd(in_end,ii); +} + +LZO_PUBLIC(int) +DO_COMPRESS ( const lzo_byte *in , lzo_uint in_len, + lzo_byte *out, lzo_uintp out_len, + lzo_voidp wrkmem ) +{ + lzo_byte *op = out; + lzo_uint t; + +#if defined(__LZO_QUERY_COMPRESS) + if (__LZO_IS_COMPRESS_QUERY(in,in_len,out,out_len,wrkmem)) + return __LZO_QUERY_COMPRESS(in,in_len,out,out_len,wrkmem,D_SIZE,lzo_sizeof(lzo_dict_t)); +#endif + + if (in_len <= M2_MAX_LEN + 5) + t = in_len; + else + { + t = do_compress(in,in_len,op,out_len,wrkmem); + op += *out_len; + } + + if (t > 0) + { + const lzo_byte *ii = in + in_len - t; + + if (op == out && t <= 238) + *op++ = LZO_BYTE(17 + t); + else if (t <= 3) + op[-2] |= LZO_BYTE(t); + else if (t <= 18) + *op++ = LZO_BYTE(t - 3); + else + { + lzo_uint tt = t - 18; + + *op++ = 0; + while (tt > 255) + { + tt -= 255; + *op++ = 0; + } + assert("lzo-15", tt > 0); + *op++ = LZO_BYTE(tt); + } + do *op++ = *ii++; while (--t > 0); + } + + *op++ = M4_MARKER | 1; + *op++ = 0; + *op++ = 0; + + *out_len = op - out; + return LZO_E_OK; +} + +#undef do_compress +#undef DO_COMPRESS +#undef LZO_HASH + +#undef LZO_TEST_DECOMPRESS_OVERRUN +#undef LZO_TEST_DECOMPRESS_OVERRUN_INPUT +#undef LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT +#undef LZO_TEST_DECOMPRESS_OVERRUN_LOOKBEHIND +#undef DO_DECOMPRESS +#define DO_DECOMPRESS lzo1x_decompress + +#if defined(LZO_TEST_DECOMPRESS_OVERRUN) +# if !defined(LZO_TEST_DECOMPRESS_OVERRUN_INPUT) +# define LZO_TEST_DECOMPRESS_OVERRUN_INPUT 2 +# endif +# if !defined(LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT) +# define LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT 2 +# endif +# if !defined(LZO_TEST_DECOMPRESS_OVERRUN_LOOKBEHIND) +# define LZO_TEST_DECOMPRESS_OVERRUN_LOOKBEHIND +# endif +#endif + +#undef TEST_IP +#undef TEST_OP +#undef TEST_LOOKBEHIND +#undef NEED_IP +#undef NEED_OP +#undef HAVE_TEST_IP +#undef HAVE_TEST_OP +#undef HAVE_NEED_IP +#undef HAVE_NEED_OP +#undef HAVE_ANY_IP +#undef HAVE_ANY_OP + +#if defined(LZO_TEST_DECOMPRESS_OVERRUN_INPUT) +# if (LZO_TEST_DECOMPRESS_OVERRUN_INPUT >= 1) +# define TEST_IP (ip < ip_end) +# endif +# if (LZO_TEST_DECOMPRESS_OVERRUN_INPUT >= 2) +# define NEED_IP(x) \ + if ((lzo_uint)(ip_end - ip) < (lzo_uint)(x)) goto input_overrun +# endif +#endif + +#if defined(LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT) +# if (LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT >= 1) +# define TEST_OP (op <= op_end) +# endif +# if (LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT >= 2) +# undef TEST_OP +# define NEED_OP(x) \ + if ((lzo_uint)(op_end - op) < (lzo_uint)(x)) goto output_overrun +# endif +#endif + +#if defined(LZO_TEST_DECOMPRESS_OVERRUN_LOOKBEHIND) +# define TEST_LOOKBEHIND(m_pos,out) if (m_pos < out) goto lookbehind_overrun +#else +# define TEST_LOOKBEHIND(m_pos,op) ((void) 0) +#endif + +#if !defined(LZO_EOF_CODE) && !defined(TEST_IP) +# define TEST_IP (ip < ip_end) +#endif + +#if defined(TEST_IP) +# define HAVE_TEST_IP +#else +# define TEST_IP 1 +#endif +#if defined(TEST_OP) +# define HAVE_TEST_OP +#else +# define TEST_OP 1 +#endif + +#if defined(NEED_IP) +# define HAVE_NEED_IP +#else +# define NEED_IP(x) ((void) 0) +#endif +#if defined(NEED_OP) +# define HAVE_NEED_OP +#else +# define NEED_OP(x) ((void) 0) +#endif + +#if defined(HAVE_TEST_IP) || defined(HAVE_NEED_IP) +# define HAVE_ANY_IP +#endif +#if defined(HAVE_TEST_OP) || defined(HAVE_NEED_OP) +# define HAVE_ANY_OP +#endif + +#undef __COPY4 +#define __COPY4(dst,src) * (lzo_uint32p)(dst) = * (const lzo_uint32p)(src) + +#undef COPY4 +#if defined(LZO_UNALIGNED_OK_4) +# define COPY4(dst,src) __COPY4(dst,src) +#elif defined(LZO_ALIGNED_OK_4) +# define COPY4(dst,src) __COPY4((lzo_ptr_t)(dst),(lzo_ptr_t)(src)) +#endif + +#if defined(DO_DECOMPRESS) +LZO_PUBLIC(int) +DO_DECOMPRESS ( const lzo_byte *in , lzo_uint in_len, + lzo_byte *out, lzo_uintp out_len, + lzo_voidp wrkmem ) +#endif +{ + register lzo_byte *op; + register const lzo_byte *ip; + register lzo_uint t; +#if defined(COPY_DICT) + lzo_uint m_off; + const lzo_byte *dict_end; +#else + register const lzo_byte *m_pos; +#endif + + const lzo_byte * const ip_end = in + in_len; +#if defined(HAVE_ANY_OP) + lzo_byte * const op_end = out + *out_len; +#endif +#if defined(LZO1Z) + lzo_uint last_m_off = 0; +#endif + + LZO_UNUSED(wrkmem); + +#if defined(__LZO_QUERY_DECOMPRESS) + if (__LZO_IS_DECOMPRESS_QUERY(in,in_len,out,out_len,wrkmem)) + return __LZO_QUERY_DECOMPRESS(in,in_len,out,out_len,wrkmem,0,0); +#endif + +#if defined(COPY_DICT) + if (dict) + { + if (dict_len > M4_MAX_OFFSET) + { + dict += dict_len - M4_MAX_OFFSET; + dict_len = M4_MAX_OFFSET; + } + dict_end = dict + dict_len; + } + else + { + dict_len = 0; + dict_end = NULL; + } +#endif + + *out_len = 0; + + op = out; + ip = in; + + if (*ip > 17) + { + t = *ip++ - 17; + if (t < 4) + goto match_next; + assert("lzo-16", t > 0); NEED_OP(t); NEED_IP(t+1); + do *op++ = *ip++; while (--t > 0); + goto first_literal_run; + } + + while (TEST_IP && TEST_OP) + { + t = *ip++; + if (t >= 16) + goto match; + if (t == 0) + { + NEED_IP(1); + while (*ip == 0) + { + t += 255; + ip++; + NEED_IP(1); + } + t += 15 + *ip++; + } + assert("lzo-17", t > 0); NEED_OP(t+3); NEED_IP(t+4); +#if defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4) +#if !defined(LZO_UNALIGNED_OK_4) + if (PTR_ALIGNED2_4(op,ip)) + { +#endif + COPY4(op,ip); + op += 4; ip += 4; + if (--t > 0) + { + if (t >= 4) + { + do { + COPY4(op,ip); + op += 4; ip += 4; t -= 4; + } while (t >= 4); + if (t > 0) do *op++ = *ip++; while (--t > 0); + } + else + do *op++ = *ip++; while (--t > 0); + } +#if !defined(LZO_UNALIGNED_OK_4) + } + else +#endif +#endif +#if !defined(LZO_UNALIGNED_OK_4) + { + *op++ = *ip++; *op++ = *ip++; *op++ = *ip++; + do *op++ = *ip++; while (--t > 0); + } +#endif + +first_literal_run: + + t = *ip++; + if (t >= 16) + goto match; +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2); + last_m_off = m_off; +#else + m_off = (1 + M2_MAX_OFFSET) + (t >> 2) + (*ip++ << 2); +#endif + NEED_OP(3); + t = 3; COPY_DICT(t,m_off) +#else +#if defined(LZO1Z) + t = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2); + m_pos = op - t; + last_m_off = t; +#else + m_pos = op - (1 + M2_MAX_OFFSET); + m_pos -= t >> 2; + m_pos -= *ip++ << 2; +#endif + TEST_LOOKBEHIND(m_pos,out); NEED_OP(3); + *op++ = *m_pos++; *op++ = *m_pos++; *op++ = *m_pos; +#endif + goto match_done; + + while (TEST_IP && TEST_OP) + { +match: + if (t >= 64) + { +#if defined(COPY_DICT) +#if defined(LZO1X) + m_off = 1 + ((t >> 2) & 7) + (*ip++ << 3); + t = (t >> 5) - 1; +#elif defined(LZO1Y) + m_off = 1 + ((t >> 2) & 3) + (*ip++ << 2); + t = (t >> 4) - 3; +#elif defined(LZO1Z) + m_off = t & 0x1f; + if (m_off >= 0x1c) + m_off = last_m_off; + else + { + m_off = 1 + (m_off << 6) + (*ip++ >> 2); + last_m_off = m_off; + } + t = (t >> 5) - 1; +#endif +#else +#if defined(LZO1X) + m_pos = op - 1; + m_pos -= (t >> 2) & 7; + m_pos -= *ip++ << 3; + t = (t >> 5) - 1; +#elif defined(LZO1Y) + m_pos = op - 1; + m_pos -= (t >> 2) & 3; + m_pos -= *ip++ << 2; + t = (t >> 4) - 3; +#elif defined(LZO1Z) + { + lzo_uint off = t & 0x1f; + m_pos = op; + if (off >= 0x1c) + { + assert(last_m_off > 0); + m_pos -= last_m_off; + } + else + { + off = 1 + (off << 6) + (*ip++ >> 2); + m_pos -= off; + last_m_off = off; + } + } + t = (t >> 5) - 1; +#endif + TEST_LOOKBEHIND(m_pos,out); assert("lzo-18", t > 0); NEED_OP(t+3-1); + goto copy_match; +#endif + } + else if (t >= 32) + { + t &= 31; + if (t == 0) + { + NEED_IP(1); + while (*ip == 0) + { + t += 255; + ip++; + NEED_IP(1); + } + t += 31 + *ip++; + } +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off = 1 + (ip[0] << 6) + (ip[1] >> 2); + last_m_off = m_off; +#else + m_off = 1 + (ip[0] >> 2) + (ip[1] << 6); +#endif +#else +#if defined(LZO1Z) + { + lzo_uint off = 1 + (ip[0] << 6) + (ip[1] >> 2); + m_pos = op - off; + last_m_off = off; + } +#elif defined(LZO_UNALIGNED_OK_2) && (LZO_BYTE_ORDER == LZO_LITTLE_ENDIAN) + m_pos = op - 1; + m_pos -= (* (const lzo_ushortp) ip) >> 2; +#else + m_pos = op - 1; + m_pos -= (ip[0] >> 2) + (ip[1] << 6); +#endif +#endif + ip += 2; + } + else if (t >= 16) + { +#if defined(COPY_DICT) + m_off = (t & 8) << 11; +#else + m_pos = op; + m_pos -= (t & 8) << 11; +#endif + t &= 7; + if (t == 0) + { + NEED_IP(1); + while (*ip == 0) + { + t += 255; + ip++; + NEED_IP(1); + } + t += 7 + *ip++; + } +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off += (ip[0] << 6) + (ip[1] >> 2); +#else + m_off += (ip[0] >> 2) + (ip[1] << 6); +#endif + ip += 2; + if (m_off == 0) + goto eof_found; + m_off += 0x4000; +#if defined(LZO1Z) + last_m_off = m_off; +#endif +#else +#if defined(LZO1Z) + m_pos -= (ip[0] << 6) + (ip[1] >> 2); +#elif defined(LZO_UNALIGNED_OK_2) && (LZO_BYTE_ORDER == LZO_LITTLE_ENDIAN) + m_pos -= (* (const lzo_ushortp) ip) >> 2; +#else + m_pos -= (ip[0] >> 2) + (ip[1] << 6); +#endif + ip += 2; + if (m_pos == op) + goto eof_found; + m_pos -= 0x4000; +#if defined(LZO1Z) + last_m_off = op - m_pos; +#endif +#endif + } + else + { +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off = 1 + (t << 6) + (*ip++ >> 2); + last_m_off = m_off; +#else + m_off = 1 + (t >> 2) + (*ip++ << 2); +#endif + NEED_OP(2); + t = 2; COPY_DICT(t,m_off) +#else +#if defined(LZO1Z) + t = 1 + (t << 6) + (*ip++ >> 2); + m_pos = op - t; + last_m_off = t; +#else + m_pos = op - 1; + m_pos -= t >> 2; + m_pos -= *ip++ << 2; +#endif + TEST_LOOKBEHIND(m_pos,out); NEED_OP(2); + *op++ = *m_pos++; *op++ = *m_pos; +#endif + goto match_done; + } + +#if defined(COPY_DICT) + + NEED_OP(t+3-1); + t += 3-1; COPY_DICT(t,m_off) + +#else + + TEST_LOOKBEHIND(m_pos,out); assert("lzo-19", t > 0); NEED_OP(t+3-1); +#if defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4) +#if !defined(LZO_UNALIGNED_OK_4) + if (t >= 2 * 4 - (3 - 1) && PTR_ALIGNED2_4(op,m_pos)) + { + assert((op - m_pos) >= 4); +#else + if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4) + { +#endif + COPY4(op,m_pos); + op += 4; m_pos += 4; t -= 4 - (3 - 1); + do { + COPY4(op,m_pos); + op += 4; m_pos += 4; t -= 4; + } while (t >= 4); + if (t > 0) do *op++ = *m_pos++; while (--t > 0); + } + else +#endif + { +copy_match: + *op++ = *m_pos++; *op++ = *m_pos++; + do *op++ = *m_pos++; while (--t > 0); + } + +#endif + +match_done: +#if defined(LZO1Z) + t = ip[-1] & 3; +#else + t = ip[-2] & 3; +#endif + if (t == 0) + break; + +match_next: + assert("lzo-20", t > 0); NEED_OP(t); NEED_IP(t+1); + do *op++ = *ip++; while (--t > 0); + t = *ip++; + } + } + +#if defined(HAVE_TEST_IP) || defined(HAVE_TEST_OP) + *out_len = op - out; + return LZO_E_EOF_NOT_FOUND; +#endif + +eof_found: + assert("lzo-21", t == 1); + *out_len = op - out; + return (ip == ip_end ? LZO_E_OK : + (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN)); + +#if defined(HAVE_NEED_IP) +input_overrun: + *out_len = op - out; + return LZO_E_INPUT_OVERRUN; +#endif + +#if defined(HAVE_NEED_OP) +output_overrun: + *out_len = op - out; + return LZO_E_OUTPUT_OVERRUN; +#endif + +#if defined(LZO_TEST_DECOMPRESS_OVERRUN_LOOKBEHIND) +lookbehind_overrun: + *out_len = op - out; + return LZO_E_LOOKBEHIND_OVERRUN; +#endif +} + +#define LZO_TEST_DECOMPRESS_OVERRUN +#undef DO_DECOMPRESS +#define DO_DECOMPRESS lzo1x_decompress_safe + +#if defined(LZO_TEST_DECOMPRESS_OVERRUN) +# if !defined(LZO_TEST_DECOMPRESS_OVERRUN_INPUT) +# define LZO_TEST_DECOMPRESS_OVERRUN_INPUT 2 +# endif +# if !defined(LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT) +# define LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT 2 +# endif +# if !defined(LZO_TEST_DECOMPRESS_OVERRUN_LOOKBEHIND) +# define LZO_TEST_DECOMPRESS_OVERRUN_LOOKBEHIND +# endif +#endif + +#undef TEST_IP +#undef TEST_OP +#undef TEST_LOOKBEHIND +#undef NEED_IP +#undef NEED_OP +#undef HAVE_TEST_IP +#undef HAVE_TEST_OP +#undef HAVE_NEED_IP +#undef HAVE_NEED_OP +#undef HAVE_ANY_IP +#undef HAVE_ANY_OP + +#if defined(LZO_TEST_DECOMPRESS_OVERRUN_INPUT) +# if (LZO_TEST_DECOMPRESS_OVERRUN_INPUT >= 1) +# define TEST_IP (ip < ip_end) +# endif +# if (LZO_TEST_DECOMPRESS_OVERRUN_INPUT >= 2) +# define NEED_IP(x) \ + if ((lzo_uint)(ip_end - ip) < (lzo_uint)(x)) goto input_overrun +# endif +#endif + +#if defined(LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT) +# if (LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT >= 1) +# define TEST_OP (op <= op_end) +# endif +# if (LZO_TEST_DECOMPRESS_OVERRUN_OUTPUT >= 2) +# undef TEST_OP +# define NEED_OP(x) \ + if ((lzo_uint)(op_end - op) < (lzo_uint)(x)) goto output_overrun +# endif +#endif + +#if defined(LZO_TEST_DECOMPRESS_OVERRUN_LOOKBEHIND) +# define TEST_LOOKBEHIND(m_pos,out) if (m_pos < out) goto lookbehind_overrun +#else +# define TEST_LOOKBEHIND(m_pos,op) ((void) 0) +#endif + +#if !defined(LZO_EOF_CODE) && !defined(TEST_IP) +# define TEST_IP (ip < ip_end) +#endif + +#if defined(TEST_IP) +# define HAVE_TEST_IP +#else +# define TEST_IP 1 +#endif +#if defined(TEST_OP) +# define HAVE_TEST_OP +#else +# define TEST_OP 1 +#endif + +#if defined(NEED_IP) +# define HAVE_NEED_IP +#else +# define NEED_IP(x) ((void) 0) +#endif +#if defined(NEED_OP) +# define HAVE_NEED_OP +#else +# define NEED_OP(x) ((void) 0) +#endif + +#if defined(HAVE_TEST_IP) || defined(HAVE_NEED_IP) +# define HAVE_ANY_IP +#endif +#if defined(HAVE_TEST_OP) || defined(HAVE_NEED_OP) +# define HAVE_ANY_OP +#endif + +#undef __COPY4 +#define __COPY4(dst,src) * (lzo_uint32p)(dst) = * (const lzo_uint32p)(src) + +#undef COPY4 +#if defined(LZO_UNALIGNED_OK_4) +# define COPY4(dst,src) __COPY4(dst,src) +#elif defined(LZO_ALIGNED_OK_4) +# define COPY4(dst,src) __COPY4((lzo_ptr_t)(dst),(lzo_ptr_t)(src)) +#endif + +#if defined(DO_DECOMPRESS) +LZO_PUBLIC(int) +DO_DECOMPRESS ( const lzo_byte *in , lzo_uint in_len, + lzo_byte *out, lzo_uintp out_len, + lzo_voidp wrkmem ) +#endif +{ + register lzo_byte *op; + register const lzo_byte *ip; + register lzo_uint t; +#if defined(COPY_DICT) + lzo_uint m_off; + const lzo_byte *dict_end; +#else + register const lzo_byte *m_pos; +#endif + + const lzo_byte * const ip_end = in + in_len; +#if defined(HAVE_ANY_OP) + lzo_byte * const op_end = out + *out_len; +#endif +#if defined(LZO1Z) + lzo_uint last_m_off = 0; +#endif + + LZO_UNUSED(wrkmem); + +#if defined(__LZO_QUERY_DECOMPRESS) + if (__LZO_IS_DECOMPRESS_QUERY(in,in_len,out,out_len,wrkmem)) + return __LZO_QUERY_DECOMPRESS(in,in_len,out,out_len,wrkmem,0,0); +#endif + +#if defined(COPY_DICT) + if (dict) + { + if (dict_len > M4_MAX_OFFSET) + { + dict += dict_len - M4_MAX_OFFSET; + dict_len = M4_MAX_OFFSET; + } + dict_end = dict + dict_len; + } + else + { + dict_len = 0; + dict_end = NULL; + } +#endif + + *out_len = 0; + + op = out; + ip = in; + + if (*ip > 17) + { + t = *ip++ - 17; + if (t < 4) + goto match_next; + assert("lzo-22", t > 0); NEED_OP(t); NEED_IP(t+1); + do *op++ = *ip++; while (--t > 0); + goto first_literal_run; + } + + while (TEST_IP && TEST_OP) + { + t = *ip++; + if (t >= 16) + goto match; + if (t == 0) + { + NEED_IP(1); + while (*ip == 0) + { + t += 255; + ip++; + NEED_IP(1); + } + t += 15 + *ip++; + } + assert("lzo-23", t > 0); NEED_OP(t+3); NEED_IP(t+4); +#if defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4) +#if !defined(LZO_UNALIGNED_OK_4) + if (PTR_ALIGNED2_4(op,ip)) + { +#endif + COPY4(op,ip); + op += 4; ip += 4; + if (--t > 0) + { + if (t >= 4) + { + do { + COPY4(op,ip); + op += 4; ip += 4; t -= 4; + } while (t >= 4); + if (t > 0) do *op++ = *ip++; while (--t > 0); + } + else + do *op++ = *ip++; while (--t > 0); + } +#if !defined(LZO_UNALIGNED_OK_4) + } + else +#endif +#endif +#if !defined(LZO_UNALIGNED_OK_4) + { + *op++ = *ip++; *op++ = *ip++; *op++ = *ip++; + do *op++ = *ip++; while (--t > 0); + } +#endif + +first_literal_run: + + t = *ip++; + if (t >= 16) + goto match; +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2); + last_m_off = m_off; +#else + m_off = (1 + M2_MAX_OFFSET) + (t >> 2) + (*ip++ << 2); +#endif + NEED_OP(3); + t = 3; COPY_DICT(t,m_off) +#else +#if defined(LZO1Z) + t = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2); + m_pos = op - t; + last_m_off = t; +#else + m_pos = op - (1 + M2_MAX_OFFSET); + m_pos -= t >> 2; + m_pos -= *ip++ << 2; +#endif + TEST_LOOKBEHIND(m_pos,out); NEED_OP(3); + *op++ = *m_pos++; *op++ = *m_pos++; *op++ = *m_pos; +#endif + goto match_done; + + while (TEST_IP && TEST_OP) + { +match: + if (t >= 64) + { +#if defined(COPY_DICT) +#if defined(LZO1X) + m_off = 1 + ((t >> 2) & 7) + (*ip++ << 3); + t = (t >> 5) - 1; +#elif defined(LZO1Y) + m_off = 1 + ((t >> 2) & 3) + (*ip++ << 2); + t = (t >> 4) - 3; +#elif defined(LZO1Z) + m_off = t & 0x1f; + if (m_off >= 0x1c) + m_off = last_m_off; + else + { + m_off = 1 + (m_off << 6) + (*ip++ >> 2); + last_m_off = m_off; + } + t = (t >> 5) - 1; +#endif +#else +#if defined(LZO1X) + m_pos = op - 1; + m_pos -= (t >> 2) & 7; + m_pos -= *ip++ << 3; + t = (t >> 5) - 1; +#elif defined(LZO1Y) + m_pos = op - 1; + m_pos -= (t >> 2) & 3; + m_pos -= *ip++ << 2; + t = (t >> 4) - 3; +#elif defined(LZO1Z) + { + lzo_uint off = t & 0x1f; + m_pos = op; + if (off >= 0x1c) + { + assert(last_m_off > 0); + m_pos -= last_m_off; + } + else + { + off = 1 + (off << 6) + (*ip++ >> 2); + m_pos -= off; + last_m_off = off; + } + } + t = (t >> 5) - 1; +#endif + TEST_LOOKBEHIND(m_pos,out); assert("lzo-24", t > 0); NEED_OP(t+3-1); + goto copy_match; +#endif + } + else if (t >= 32) + { + t &= 31; + if (t == 0) + { + NEED_IP(1); + while (*ip == 0) + { + t += 255; + ip++; + NEED_IP(1); + } + t += 31 + *ip++; + } +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off = 1 + (ip[0] << 6) + (ip[1] >> 2); + last_m_off = m_off; +#else + m_off = 1 + (ip[0] >> 2) + (ip[1] << 6); +#endif +#else +#if defined(LZO1Z) + { + lzo_uint off = 1 + (ip[0] << 6) + (ip[1] >> 2); + m_pos = op - off; + last_m_off = off; + } +#elif defined(LZO_UNALIGNED_OK_2) && (LZO_BYTE_ORDER == LZO_LITTLE_ENDIAN) + m_pos = op - 1; + m_pos -= (* (const lzo_ushortp) ip) >> 2; +#else + m_pos = op - 1; + m_pos -= (ip[0] >> 2) + (ip[1] << 6); +#endif +#endif + ip += 2; + } + else if (t >= 16) + { +#if defined(COPY_DICT) + m_off = (t & 8) << 11; +#else + m_pos = op; + m_pos -= (t & 8) << 11; +#endif + t &= 7; + if (t == 0) + { + NEED_IP(1); + while (*ip == 0) + { + t += 255; + ip++; + NEED_IP(1); + } + t += 7 + *ip++; + } +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off += (ip[0] << 6) + (ip[1] >> 2); +#else + m_off += (ip[0] >> 2) + (ip[1] << 6); +#endif + ip += 2; + if (m_off == 0) + goto eof_found; + m_off += 0x4000; +#if defined(LZO1Z) + last_m_off = m_off; +#endif +#else +#if defined(LZO1Z) + m_pos -= (ip[0] << 6) + (ip[1] >> 2); +#elif defined(LZO_UNALIGNED_OK_2) && (LZO_BYTE_ORDER == LZO_LITTLE_ENDIAN) + m_pos -= (* (const lzo_ushortp) ip) >> 2; +#else + m_pos -= (ip[0] >> 2) + (ip[1] << 6); +#endif + ip += 2; + if (m_pos == op) + goto eof_found; + m_pos -= 0x4000; +#if defined(LZO1Z) + last_m_off = op - m_pos; +#endif +#endif + } + else + { +#if defined(COPY_DICT) +#if defined(LZO1Z) + m_off = 1 + (t << 6) + (*ip++ >> 2); + last_m_off = m_off; +#else + m_off = 1 + (t >> 2) + (*ip++ << 2); +#endif + NEED_OP(2); + t = 2; COPY_DICT(t,m_off) +#else +#if defined(LZO1Z) + t = 1 + (t << 6) + (*ip++ >> 2); + m_pos = op - t; + last_m_off = t; +#else + m_pos = op - 1; + m_pos -= t >> 2; + m_pos -= *ip++ << 2; +#endif + TEST_LOOKBEHIND(m_pos,out); NEED_OP(2); + *op++ = *m_pos++; *op++ = *m_pos; +#endif + goto match_done; + } + +#if defined(COPY_DICT) + + NEED_OP(t+3-1); + t += 3-1; COPY_DICT(t,m_off) + +#else + + TEST_LOOKBEHIND(m_pos,out); assert("lzo-25", t > 0); NEED_OP(t+3-1); +#if defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4) +#if !defined(LZO_UNALIGNED_OK_4) + if (t >= 2 * 4 - (3 - 1) && PTR_ALIGNED2_4(op,m_pos)) + { + assert((op - m_pos) >= 4); +#else + if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4) + { +#endif + COPY4(op,m_pos); + op += 4; m_pos += 4; t -= 4 - (3 - 1); + do { + COPY4(op,m_pos); + op += 4; m_pos += 4; t -= 4; + } while (t >= 4); + if (t > 0) do *op++ = *m_pos++; while (--t > 0); + } + else +#endif + { +copy_match: + *op++ = *m_pos++; *op++ = *m_pos++; + do *op++ = *m_pos++; while (--t > 0); + } + +#endif + +match_done: +#if defined(LZO1Z) + t = ip[-1] & 3; +#else + t = ip[-2] & 3; +#endif + if (t == 0) + break; + +match_next: + assert("lzo-26", t > 0); NEED_OP(t); NEED_IP(t+1); + do *op++ = *ip++; while (--t > 0); + t = *ip++; + } + } + +#if defined(HAVE_TEST_IP) || defined(HAVE_TEST_OP) + *out_len = op - out; + return LZO_E_EOF_NOT_FOUND; +#endif + +eof_found: + assert("lzo-27", t == 1); + *out_len = op - out; + return (ip == ip_end ? LZO_E_OK : + (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN)); + +#if defined(HAVE_NEED_IP) +input_overrun: + *out_len = op - out; + return LZO_E_INPUT_OVERRUN; +#endif + +#if defined(HAVE_NEED_OP) +output_overrun: + *out_len = op - out; + return LZO_E_OUTPUT_OVERRUN; +#endif + +#if defined(LZO_TEST_DECOMPRESS_OVERRUN_LOOKBEHIND) +lookbehind_overrun: + *out_len = op - out; + return LZO_E_LOOKBEHIND_OVERRUN; +#endif +} + +/***** End of minilzo.c *****/ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/compress/minilzo.h 2004-09-03 00:57:05.168253568 -0700 @@ -0,0 +1,100 @@ +/* minilzo.h -- mini subset of the LZO real-time data compression library + + This file is part of the LZO real-time data compression library. + + Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + The LZO library is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer + + http://www.oberhumer.com/opensource/lzo/ + */ + +/* + * NOTE: + * the full LZO package can be found at + * http://www.oberhumer.com/opensource/lzo/ + */ + + +#ifndef __MINILZO_H +#define __MINILZO_H + +#define MINILZO_VERSION 0x1080 + +#ifdef __LZOCONF_H +# error "you cannot use both LZO and miniLZO" +#endif + +#undef LZO_HAVE_CONFIG_H +#include "lzoconf.h" + +#if !defined(LZO_VERSION) || (LZO_VERSION != MINILZO_VERSION) +# error "version mismatch in header files" +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + + +/*********************************************************************** +// +************************************************************************/ + +/* Memory required for the wrkmem parameter. + * When the required size is 0, you can also pass a NULL pointer. + */ + +#define LZO1X_MEM_COMPRESS LZO1X_1_MEM_COMPRESS +#define LZO1X_1_MEM_COMPRESS ((lzo_uint32) (16384L * lzo_sizeof_dict_t)) +#define LZO1X_MEM_DECOMPRESS (0) + + +/* compression */ +LZO_EXTERN(int) +lzo1x_1_compress ( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uintp dst_len, + lzo_voidp wrkmem ); + +/* decompression */ +LZO_EXTERN(int) +lzo1x_decompress ( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uintp dst_len, + lzo_voidp wrkmem /* NOT USED */ ); + +/* safe decompression with overrun testing */ +LZO_EXTERN(int) +lzo1x_decompress_safe ( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uintp dst_len, + lzo_voidp wrkmem /* NOT USED */ ); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* already included */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/cryptcompress.c 2004-09-03 00:57:05.181251592 -0700 @@ -0,0 +1,3092 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README + +This file contains all cluster operations and methods of the reiser4 +cryptcompress object plugin (see http://www.namesys.com/cryptcompress_design.html +for details). +The list of cryptcompress specific EA: + + Incore inode Disk stat-data +******************************************************************************************** +* data structure * field * data structure * field * +******************************************************************************************** +* plugin_set *file plugin id * reiser4_plugin_stat *file plugin id * +* *crypto plugin id * *crypto plugin id * +* *digest plugin id * *digest plugin id * +* *compression plugin id * *compression plugin id* +******************************************************************************************** +* crypto_stat_t * keysize * reiser4_crypto_stat * keysize * +* * keyid * * keyid * +******************************************************************************************** +* cluster_stat_t * cluster_shift * reiser4_cluster_stat * cluster_shift * +******************************************************************************************** +* cryptcompress_info_t * crypto_tfm * * * +******************************************************************************************** +*/ +#include "../debug.h" +#include "../inode.h" +#include "../jnode.h" +#include "../tree.h" +#include "../page_cache.h" +#include "../readahead.h" +#include "../forward.h" +#include "../super.h" +#include "../context.h" +#include "../cluster.h" +#include "../seal.h" +#include "../vfs_ops.h" +#include "plugin.h" +#include "object.h" +#include "file/funcs.h" + +#include +#include +#include +#include +#include + +int do_readpage_ctail(reiser4_cluster_t *, struct page * page); +int ctail_read_cluster (reiser4_cluster_t *, struct inode *, int); +reiser4_key * append_cluster_key_ctail(const coord_t *, reiser4_key *); +int setattr_reserve(reiser4_tree *); +int reserve_cut_iteration(reiser4_tree *); +int writepage_ctail(struct page *); +int truncate_jnodes_range(struct inode *inode, unsigned long from, int count); +int cut_file_items(struct inode *inode, loff_t new_size, int update_sd, loff_t cur_size, int mode); +int delete_object(struct inode *inode, int mode); +__u8 cluster_shift_by_coord(const coord_t * coord); +int ctail_make_unprepped_cluster(reiser4_cluster_t * clust, struct inode * inode); +unsigned long clust_by_coord(const coord_t * coord); +int hint_is_set(const hint_t *hint); +reiser4_plugin * get_default_plugin(pset_member memb); + +/* get cryptcompress specific portion of inode */ +reiser4_internal cryptcompress_info_t * +cryptcompress_inode_data(const struct inode * inode) +{ + return &reiser4_inode_data(inode)->file_plugin_data.cryptcompress_info; +} + +/* plugin->u.file.init_inode_data */ +reiser4_internal void +init_inode_data_cryptcompress(struct inode *inode, + reiser4_object_create_data *crd, int create) +{ + cryptcompress_info_t * data; + + data = cryptcompress_inode_data(inode); + assert("edward-685", data != NULL); + + xmemset(data, 0, sizeof (*data)); +} + +reiser4_internal int +crc_inode_ok(struct inode * inode) +{ + reiser4_inode * info = reiser4_inode_data(inode); + cryptcompress_info_t * data = cryptcompress_inode_data(inode); + + if ((info->cluster_shift <= MAX_CLUSTER_SHIFT) && + (data->tfm[CRYPTO_TFM] == NULL) && + (data->tfm[DIGEST_TFM] == NULL)) + return 1; + assert("edward-686", 0); + return 0; +} + +reiser4_internal crypto_stat_t * inode_crypto_stat (struct inode * inode) +{ + assert("edward-90", inode != NULL); + assert("edward-91", reiser4_inode_data(inode) != NULL); + return (reiser4_inode_data(inode)->crypt); +} + +/* NOTE-EDWARD: Do not use crypto without digest */ +static int +alloc_crypto_tfm(struct inode * inode, crypto_data_t * data) +{ + int result; + crypto_plugin * cplug = crypto_plugin_by_id(data->cra); + digest_plugin * dplug = digest_plugin_by_id(data->dia); + + assert("edward-414", dplug != NULL); + assert("edward-415", cplug != NULL); + + result = dplug->alloc(inode); + if (result) + return result; + result = cplug->alloc(inode); + if (result) { + dplug->free(inode); + return result; + } + return 0; +} + +static void +free_crypto_tfm(struct inode * inode) +{ + reiser4_inode * info; + + assert("edward-410", inode != NULL); + + info = reiser4_inode_data(inode); + + if (!inode_get_crypto(inode)) + return; + + assert("edward-411", inode_crypto_plugin(inode)); + assert("edward-763", inode_digest_plugin(inode)); + + inode_crypto_plugin(inode)->free(inode); + inode_digest_plugin(inode)->free(inode); +} + +static int +attach_crypto_stat(struct inode * inode, crypto_data_t * data) +{ + __u8 * txt; + + crypto_stat_t * stat; + struct scatterlist sg; + struct crypto_tfm * dtfm; + + assert("edward-690", inode_get_crypto(inode)); + assert("edward-766", inode_get_digest(inode)); + + dtfm = inode_get_digest(inode); + + stat = reiser4_kmalloc(sizeof(*stat), GFP_KERNEL); + if (!stat) + return -ENOMEM; + + stat->keyid = reiser4_kmalloc((size_t)crypto_tfm_alg_digestsize(dtfm), GFP_KERNEL); + if (!stat->keyid) { + reiser4_kfree(stat); + return -ENOMEM; + } + txt = reiser4_kmalloc(data->keyid_size, GFP_KERNEL); + if (!txt) { + reiser4_kfree(stat->keyid); + reiser4_kfree(stat); + return -ENOMEM; + } + xmemcpy(txt, data->keyid, data->keyid_size); + sg.page = virt_to_page (txt); + sg.offset = offset_in_page (txt); + sg.length = data->keyid_size; + + crypto_digest_init (dtfm); + crypto_digest_update (dtfm, &sg, 1); + crypto_digest_final (dtfm, stat->keyid); + + reiser4_inode_data(inode)->crypt = stat; + reiser4_kfree(txt); + + return 0; +} + +static void +detach_crypto_stat(struct inode * object) +{ + crypto_stat_t * stat; + + stat = inode_crypto_stat(object); + + assert("edward-691", crc_inode_ok(object)); + + if (!inode_get_crypto(object)) + return; + + assert("edward-412", stat != NULL); + + reiser4_kfree(stat->keyid); + reiser4_kfree(stat); +} + +static void +init_default_crypto(crypto_data_t * data) +{ + assert("edward-692", data != NULL); + + xmemset(data, 0, sizeof(*data)); + + data->cra = get_default_plugin(PSET_CRYPTO)->h.id; + data->dia = get_default_plugin(PSET_DIGEST)->h.id; + return; +} + +static void +init_default_compression(compression_data_t * data) +{ + assert("edward-693", data != NULL); + + xmemset(data, 0, sizeof(*data)); + + data->coa = get_default_plugin(PSET_COMPRESSION)->h.id; +} + +static void +init_default_cluster(cluster_data_t * data) +{ + assert("edward-694", data != NULL); + + *data = DEFAULT_CLUSTER_SHIFT; +} + +/* 1) fill crypto specific part of inode + 2) set inode crypto stat which is supposed to be saved in stat-data */ +static int +inode_set_crypto(struct inode * object, crypto_data_t * data) +{ + int result; + crypto_data_t def; + struct crypto_tfm * tfm; + crypto_plugin * cplug; + digest_plugin * dplug; + reiser4_inode * info = reiser4_inode_data(object); + + if (!data) { + init_default_crypto(&def); + data = &def; + } + cplug = crypto_plugin_by_id(data->cra); + dplug = digest_plugin_by_id(data->dia); + + plugin_set_crypto(&info->pset, cplug); + plugin_set_digest(&info->pset, dplug); + + result = alloc_crypto_tfm(object, data); + if (!result) + return result; + + if (!inode_get_crypto(object)) + /* nothing to do anymore */ + return 0; + + assert("edward-416", data != NULL); + assert("edward-414", dplug != NULL); + assert("edward-415", cplug != NULL); + assert("edward-417", data->key!= NULL); + assert("edward-88", data->keyid != NULL); + assert("edward-83", data->keyid_size != 0); + assert("edward-89", data->keysize != 0); + + tfm = inode_get_tfm(object, CRYPTO_TFM); + assert("edward-695", tfm != NULL); + + result = cplug->setkey(tfm, data->key, data->keysize); + if (result) { + free_crypto_tfm(object); + return result; + } + assert ("edward-34", !inode_get_flag(object, REISER4_SECRET_KEY_INSTALLED)); + inode_set_flag(object, REISER4_SECRET_KEY_INSTALLED); + + info->extmask |= (1 << CRYPTO_STAT); + + result = attach_crypto_stat(object, data); + if (result) + goto error; + + info->plugin_mask |= (1 << PSET_CRYPTO) | (1 << PSET_DIGEST); + + return 0; + error: + free_crypto_tfm(object); + inode_clr_flag(object, REISER4_SECRET_KEY_INSTALLED); + return result; +} + +static void +inode_set_compression(struct inode * object, compression_data_t * data) +{ + compression_data_t def; + reiser4_inode * info = reiser4_inode_data(object); + + if (!data) { + init_default_compression(&def); + data = &def; + } + plugin_set_compression(&info->pset, compression_plugin_by_id(data->coa)); + info->plugin_mask |= (1 << PSET_COMPRESSION); + + return; +} + +static int +inode_set_cluster(struct inode * object, cluster_data_t * data) +{ + int result = 0; + cluster_data_t def; + reiser4_inode * info; + + assert("edward-696", object != NULL); + + info = reiser4_inode_data(object); + + if(!data) { + /* NOTE-EDWARD: + this is necessary parameter for cryptcompress objects! */ + printk("edward-418, create_cryptcompress: default cluster size (4K) was assigned\n"); + + init_default_cluster(&def); + data = &def; + } + assert("edward-697", *data <= MAX_CLUSTER_SHIFT); + + info->cluster_shift = *data; + info->extmask |= (1 << CLUSTER_STAT); + return result; +} + +/* plugin->create() method for crypto-compressed files + +. install plugins +. attach crypto info if specified +. attach compression info if specified +. attach cluster info +*/ +reiser4_internal int +create_cryptcompress(struct inode *object, struct inode *parent, reiser4_object_create_data * data) +{ + int result; + reiser4_inode * info; + + assert("edward-23", object != NULL); + assert("edward-24", parent != NULL); + assert("edward-30", data != NULL); + assert("edward-26", inode_get_flag(object, REISER4_NO_SD)); + assert("edward-27", data->id == CRC_FILE_PLUGIN_ID); + + info = reiser4_inode_data(object); + + assert("edward-29", info != NULL); + + /* set file bit */ + info->plugin_mask |= (1 << PSET_FILE); + + /* set crypto */ + result = inode_set_crypto(object, data->crypto); + if (result) + goto error; + + /* set compression */ + inode_set_compression(object, data->compression); + + /* set cluster info */ + result = inode_set_cluster(object, data->cluster); + if (result) + goto error; + /* set plugin mask */ + info->extmask |= (1 << PLUGIN_STAT); + + /* save everything in disk stat-data */ + result = write_sd_by_inode_common(object); + if (!result) + return 0; + /* save() method failed, release attached crypto info */ + inode_clr_flag(object, REISER4_CRYPTO_STAT_LOADED); + inode_clr_flag(object, REISER4_CLUSTER_KNOWN); + error: + free_crypto_tfm(object); + detach_crypto_stat(object); + inode_clr_flag(object, REISER4_SECRET_KEY_INSTALLED); + return result; +} + +reiser4_internal int open_cryptcompress(struct inode * inode, struct file * file) +{ + /* FIXME-EDWARD: should be powered by key management */ + assert("edward-698", inode_file_plugin(inode) == file_plugin_by_id(CRC_FILE_PLUGIN_ID)); + return 0; +} + +/* plugin->destroy_inode() */ +reiser4_internal void +destroy_inode_cryptcompress(struct inode * inode) +{ + assert("edward-802", inode_file_plugin(inode) == file_plugin_by_id(SYMLINK_FILE_PLUGIN_ID)); + assert("edward-803", !is_bad_inode(inode) && is_inode_loaded(inode)); + assert("edward-804", inode_get_flag(inode, REISER4_CLUSTER_KNOWN)); + + free_crypto_tfm(inode); + if (inode_get_flag(inode, REISER4_CRYPTO_STAT_LOADED)) + detach_crypto_stat(inode); + inode_clr_flag(inode, REISER4_CLUSTER_KNOWN); + inode_clr_flag(inode, REISER4_CRYPTO_STAT_LOADED); + inode_clr_flag(inode, REISER4_SECRET_KEY_INSTALLED); +} + +static int +save_len_cryptcompress_plugin(struct inode * inode, reiser4_plugin * plugin) +{ + assert("edward-457", inode != NULL); + assert("edward-458", plugin != NULL); + assert("edward-459", plugin->h.id == CRC_FILE_PLUGIN_ID); + return 0; +} + +reiser4_internal int +load_cryptcompress_plugin(struct inode * inode, reiser4_plugin * plugin, char **area, int *len) +{ + assert("edward-455", inode != NULL); + assert("edward-456", (reiser4_inode_data(inode)->pset != NULL)); + + plugin_set_file(&reiser4_inode_data(inode)->pset, file_plugin_by_id(CRC_FILE_PLUGIN_ID)); + return 0; +} + +static int +change_crypto_file(struct inode * inode, reiser4_plugin * plugin) +{ + /* cannot change object plugin of already existing object */ + return RETERR(-EINVAL); +} + +struct reiser4_plugin_ops cryptcompress_plugin_ops = { + .load = load_cryptcompress_plugin, + .save_len = save_len_cryptcompress_plugin, + .save = NULL, + .alignment = 8, + .change = change_crypto_file +}; + +/* returns translated offset */ +reiser4_internal loff_t inode_scaled_offset (struct inode * inode, + const loff_t src_off /* input offset */) +{ + assert("edward-97", inode != NULL); + + if (!inode_get_crypto(inode) || src_off == get_key_offset(max_key())) + return src_off; + + return inode_crypto_plugin(inode)->scale(inode, crypto_blocksize(inode), src_off); +} + +/* returns disk cluster size */ +reiser4_internal size_t +inode_scaled_cluster_size (struct inode * inode) +{ + assert("edward-110", inode != NULL); + assert("edward-111", inode_get_flag(inode, REISER4_CLUSTER_KNOWN)); + + return inode_scaled_offset(inode, inode_cluster_size(inode)); +} + +reiser4_internal void reiser4_cluster_init (reiser4_cluster_t * clust){ + assert("edward-84", clust != NULL); + xmemset(clust, 0, sizeof *clust); + clust->stat = DATA_CLUSTER; +} + +/* release cluster's data */ +reiser4_internal void +release_cluster_buf(reiser4_cluster_t * clust) +{ + assert("edward-121", clust != NULL); + + if (clust->buf) { + assert("edward-615", clust->bsize != 0); + reiser4_kfree(clust->buf); + clust->buf = NULL; + } +} + +reiser4_internal void +put_cluster_data(reiser4_cluster_t * clust) +{ + assert("edward-435", clust != NULL); + + release_cluster_buf(clust); + xmemset(clust, 0, sizeof *clust); +} + +/* returns true if we don't need to read new cluster from disk */ +reiser4_internal int cluster_is_uptodate (reiser4_cluster_t * clust) +{ + assert("edward-126", clust != NULL); + return (clust->buf != NULL); +} + +/* return true if the cluster contains specified page */ +reiser4_internal int +page_of_cluster(struct page * page, reiser4_cluster_t * clust, struct inode * inode) +{ + assert("edward-162", page != NULL); + assert("edward-163", clust != NULL); + assert("edward-164", inode != NULL); + assert("edward-165", inode_get_flag(inode, REISER4_CLUSTER_KNOWN)); + + return (pg_to_clust(page->index, inode) == clust->index); +} + +reiser4_internal int count_to_nrpages(unsigned count) +{ + return (!count ? 0 : off_to_pg(count - 1) + 1); +} + +static int +new_cluster(reiser4_cluster_t * clust, struct inode * inode) +{ + return (clust_to_off(clust->index, inode) >= inode->i_size); +} + +/* set minimal number of cluster pages (start from first one) + which cover hole and users data */ +static void +set_nrpages_by_frame(reiser4_cluster_t * clust) +{ + assert("edward-180", clust != NULL); + + if (clust->count + clust->delta == 0) { + /* nothing to write - nothing to read */ + clust->nr_pages = 0; + return; + } + clust->nr_pages = count_to_nrpages(clust->off + clust->count + clust->delta); +} + +/* cluster index should be valid */ +reiser4_internal void +set_nrpages_by_inode(reiser4_cluster_t * clust, struct inode * inode) +{ + assert("edward-785", clust != NULL); + assert("edward-786", inode != NULL); + + clust->nr_pages = count_to_nrpages(fsize_to_count(clust, inode)); +} + +/* plugin->key_by_inode() */ +/* see plugin/plugin.h for details */ +reiser4_internal int +key_by_inode_cryptcompress(struct inode *inode, loff_t off, reiser4_key * key) +{ + assert("edward-64", inode != 0); + assert("edward-112", ergo(off != get_key_offset(max_key()), !off_to_cloff(off, inode))); + /* don't come here with other offsets */ + + key_by_inode_and_offset_common(inode, 0, key); + set_key_offset(key, (__u64) (!inode_crypto_stat(inode) ? off : inode_scaled_offset(inode, off))); + return 0; +} + +/* plugin->flow_by_inode */ +reiser4_internal int +flow_by_inode_cryptcompress(struct inode *inode /* file to build flow for */ , + char *buf /* user level buffer */ , + int user /* 1 if @buf is of user space, 0 - if it is + kernel space */ , + loff_t size /* buffer size */ , + loff_t off /* offset to start io from */ , + rw_op op /* READ or WRITE */ , + flow_t * f /* resulting flow */) +{ + assert("edward-436", f != NULL); + assert("edward-149", inode != NULL); + assert("edward-150", inode_file_plugin(inode) != NULL); + assert("edward-151", inode_file_plugin(inode)->key_by_inode == key_by_inode_cryptcompress); + + + f->length = size; + f->data = buf; + f->user = user; + f->op = op; + + if (op == WRITE_OP && user == 1) + return 0; + return key_by_inode_cryptcompress(inode, off, &f->key); +} + +reiser4_internal int +hint_prev_cluster(reiser4_cluster_t * clust) +{ + assert("edward-699", clust != NULL); + assert("edward-700", clust->hint != NULL); + + if (!clust->hint->coord.valid) + return 0; + assert("edward-701", clust->file != NULL); + assert("edward-702", clust->file->f_dentry != NULL); + assert("edward-703", clust->file->f_dentry->d_inode != NULL); + + return (clust->index == off_to_clust(clust->hint->offset, clust->file->f_dentry->d_inode)); +} + +static int +crc_hint_validate(hint_t *hint, const reiser4_key *key, int check_key, znode_lock_mode lock_mode) +{ + assert("edward-704", hint != NULL); + + if (hint->coord.valid) { + assert("edward-705", znode_is_any_locked(hint->coord.base_coord.node)); + return 0; + } + assert("edward-706", hint->coord.lh->owner == NULL); + if (!hint || !hint_is_set(hint) || hint->mode != lock_mode) + /* hint either not set or set by different operation */ + return RETERR(-E_REPEAT); + +#if 0 + if (check_key && get_key_offset(key) != hint->offset) + /* hint is set for different key */ + return RETERR(-E_REPEAT); +#endif + assert("edward-707", schedulable()); + + return seal_validate(&hint->seal, &hint->coord.base_coord, key, + hint->level, hint->coord.lh, FIND_MAX_NOT_MORE_THAN, lock_mode, ZNODE_LOCK_LOPRI); +} + +static inline void +crc_validate_extended_coord(uf_coord_t *uf_coord, loff_t offset) +{ + // assert("edward-764", uf_coord->valid == 0); + assert("edward-708", item_plugin_by_coord(&uf_coord->base_coord)->s.file.init_coord_extension); + + item_plugin_by_coord(&uf_coord->base_coord)->s.file.init_coord_extension(uf_coord, offset); +} + +static inline void +crc_invalidate_extended_coord(uf_coord_t *uf_coord) +{ + coord_clear_iplug(&uf_coord->base_coord); + uf_coord->valid = 0; +} + +static int all_but_offset_key_eq(const reiser4_key *k1, const reiser4_key *k2) +{ + return (get_key_locality(k1) == get_key_locality(k2) && + get_key_type(k1) == get_key_type(k2) && + get_key_band(k1) == get_key_band(k2) && + get_key_ordering(k1) == get_key_ordering(k2) && + get_key_objectid(k1) == get_key_objectid(k2)); +} + +/* Search a disk cluster item. + If result is not cbk_errored current znode is locked */ +reiser4_internal int +find_cluster_item(hint_t * hint, /* coord, lh, seal */ + const reiser4_key *key, /* key of next cluster item to read */ + int check_key, + znode_lock_mode lock_mode /* which lock */, + ra_info_t *ra_info, + lookup_bias bias) +{ + int result; + reiser4_key ikey; + coord_t * coord = &hint->coord.base_coord; + + assert("edward-152", hint != NULL); + + if (hint->coord.valid) { + assert("edward-709", znode_is_any_locked(coord->node)); + if (coord->between == BEFORE_ITEM) { + if (equal_to_rdk(coord->node, key)) { + result = goto_right_neighbor(coord, hint->coord.lh); + if (result == -E_NO_NEIGHBOR) { + crc_invalidate_extended_coord(&hint->coord); + return CBK_COORD_NOTFOUND; + } + } + coord->between = AT_UNIT; + result = zload(coord->node); + if (result) + return result; + /* check current item */ + + if(!coord_is_existing_item(coord)) { + /* FIXME-EDWARD: This was the last item + of the object */ + crc_invalidate_extended_coord(&hint->coord); + zrelse(coord->node); + longterm_unlock_znode(hint->coord.lh); + goto traverse_tree; + } + item_key_by_coord(coord, &ikey); + if (!all_but_offset_key_eq(key, &ikey)) { + unset_hint(hint); + zrelse(coord->node); + return CBK_COORD_NOTFOUND; + } + if (get_key_offset(key) == get_key_offset(&ikey)) { + zrelse(coord->node); + return CBK_COORD_FOUND; + } + //assert("edward-765", get_key_offset(key) > get_key_offset(&ikey)); + zrelse(coord->node); + return CBK_COORD_NOTFOUND; + } + else { + assert("edward-710", coord->between == AT_UNIT); + + /* repeat check with updated @key */ + result = zload(coord->node); + if (result) + return result; + item_key_by_coord(coord, &ikey); + assert("edward-711", all_but_offset_key_eq(key, &ikey)); + + if (get_key_offset(key) == get_key_offset(&ikey)) { + zrelse(coord->node); + return CBK_COORD_FOUND; + } + zrelse(coord->node); + /* status is not changed, perhaps this is a hole */ + return CBK_COORD_NOTFOUND; + } + } + else { + /* extended coord is invalid */ + result = crc_hint_validate(hint, key, check_key, lock_mode); + if (result) + goto traverse_tree; + + assert("edward-712", znode_is_any_locked(coord->node)); + + /* hint is valid, extended coord is invalid */ + if (check_key) { + coord->between = AT_UNIT; + return CBK_COORD_FOUND; + } + return CBK_COORD_NOTFOUND; + } + assert("edward-713", hint->coord.lh->owner == NULL); + traverse_tree: + + assert("edward-714", schedulable()); + + coord_init_zero(coord); + hint->coord.valid = 0; + return coord_by_key(current_tree, key, coord, hint->coord.lh, lock_mode, + bias, LEAF_LEVEL, LEAF_LEVEL, CBK_UNIQUE, ra_info); +} + +/* This represent reiser4 crypto alignment policy. + Returns the size > 0 of aligning overhead, if we should align/cut, + returns 0, if we shouldn't (alignment assumes appinding an overhead of the size > 0) */ +static int +crypto_overhead(size_t len /* advised length */, + reiser4_cluster_t * clust, + struct inode * inode, rw_op rw) +{ + size_t size = 0; + int result = 0; + int oh; + + assert("edward-486", clust != 0); + + if (!inode_get_crypto(inode) || !inode_crypto_plugin(inode)->align_cluster) + return 0; + if (!len) + size = clust->len; + + assert("edward-615", size != 0); + assert("edward-489", crypto_blocksize(inode) != 0); + + switch (rw) { + case WRITE_OP: /* align */ + assert("edward-488", size <= inode_cluster_size(inode)); + + oh = size % crypto_blocksize(inode); + + if (!oh && size == fsize_to_count(clust, inode)) + /* cluster don't need alignment and didn't get compressed */ + return 0; + result = (crypto_blocksize(inode) - oh); + break; + case READ_OP: /* cut */ + assert("edward-490", size <= inode_scaled_cluster_size(inode)); + if (size >= inode_scaled_offset(inode, fsize_to_count(clust, inode))) + /* cluster didn't get aligned */ + return 0; + assert("edward-491", clust->buf != NULL); + + result = *(clust->buf + size - 1); + break; + default: + impossible("edward-493", "bad option for getting alignment"); + } + return result; +} + +/* alternating the pairs (@clust->buf, @clust->bsize) and (@buf, @bufsize) */ +static void +alternate_buffers(reiser4_cluster_t * clust, __u8 ** buf, size_t * bufsize) +{ + __u8 * tmp_buf; + size_t tmp_size; + + assert("edward-405", bufsize != NULL); + assert("edward-406", *bufsize != 0); + + tmp_buf = *buf; + tmp_size = *bufsize; + + *buf = clust->buf; + *bufsize = clust->bsize; + + clust->buf = tmp_buf; + clust->bsize = tmp_size; +} + +/* maximal aligning overhead which can be appended + to the flow before encryption if any */ +reiser4_internal unsigned +max_crypto_overhead(struct inode * inode) +{ + if (!inode_get_crypto(inode) || !inode_crypto_plugin(inode)->align_cluster) + return 0; + return crypto_blocksize(inode); +} + +reiser4_internal unsigned +compress_overhead(struct inode * inode, int in_len) +{ + return inode_compression_plugin(inode)->overrun(in_len); +} + +/* The following two functions represent reiser4 compression policy */ +static int +try_compress(reiser4_cluster_t * clust, struct inode * inode) +{ + return (inode_compression_plugin(inode) != compression_plugin_by_id(NONE_COMPRESSION_ID)) && + (clust->count >= MIN_SIZE_FOR_COMPRESSION); +} + +static int +try_encrypt(reiser4_cluster_t * clust, struct inode * inode) +{ + return inode_get_crypto(inode) != NULL; +} + +/* If this is true, we don't use copy on clustering, and page cluster will be + flushed a bit later, during deflate_cluster(). This should be highly useful + when PAGE_CACHE_SIZE is much more then 4K */ +static int +delay_flush_pgcluster(reiser4_cluster_t * clust, struct inode * inode) +{ + return (clust->count <= PAGE_CACHE_SIZE) && (try_compress(clust, inode) || try_encrypt(clust, inode)); +} + +/* Decide by the lengths of compressed and decompressed cluster, should we save or should + we discard the result of compression. The policy is that the length of compressed then + encrypted cluster including _all_ appended infrasrtucture should be _less_ then its lenght + before compression. */ +static int +save_compressed(reiser4_cluster_t * clust, struct inode * inode) +{ + /* NOTE: Actually we use max_crypto_overhead instead of precise overhead + (a bit stronger condition) to avoid divisions */ + return (clust->len + CLUSTER_MAGIC_SIZE + max_crypto_overhead(inode) < clust->count); +} + +/* guess if the cluster was compressed */ +static int +need_decompression(reiser4_cluster_t * clust, struct inode * inode, + int encrypted /* is cluster encrypted */) +{ + assert("edward-142", clust != 0); + assert("edward-143", inode != NULL); + + return (inode_compression_plugin(inode) != compression_plugin_by_id(NONE_COMPRESSION_ID)) && + (clust->len < (encrypted ? inode_scaled_offset(inode, fsize_to_count(clust, inode)) : fsize_to_count(clust, inode))); +} + +reiser4_internal void set_compression_magic(__u8 * magic) +{ + /* FIXME-EDWARD: If crypto_plugin != NULL, this should be private! + Use 4 bytes of decrypted keyid. PARANOID? */ + assert("edward-279", magic != NULL); + xmemset(magic, 0, CLUSTER_MAGIC_SIZE); +} + +/* + Common cluster deflate manager. + + . accept a flow as a single page or cluster of pages assembled into a buffer + of cluster handle @clust + . maybe allocate buffer @bf to store temporary results + . maybe compress accepted flow and attach compression magic if result of + compression is acceptable + . maybe align and encrypt the flow. + . stores the result in the buffer of cluster handle + _ _ _ _ _ _ _ _ + | | + | disk cluster | + |_ _ _ _ _ _ _ _| + ^ + | + _______________ _______|_______ _ _ _ _ _ _ _ _ + | | <---1--- | | | | + | @bf | ----2--> | @clust |<----| page cluster | + |_______________| ----3--> |_______________| |_ _ _ _ _ _ _ _| + ^ ^ + 4 | ______________ | 5 + | | | | + +---- | page | ----+ + |______________| + + + " --n-> " means one of the following operations on a pair of pointers (src, dst) + + 1 - compression or encryption + 2 - encryption + 3 - alternation + 4 - compression + 5 - compression or encryption or copy + + where + . compression is plugin->compress(), + . encryption is plugin->encrypt(), + . alternation is alternate_buffers() (if the final result is contained in temporary buffer @bf, + we should move it to the cluster handle @clust) + . copy is memcpy() + + + FIXME-EDWARD: Currently the only symmetric crypto algorithms with ecb are + supported +*/ + +reiser4_internal int +deflate_cluster(tfm_info_t ctx, /* data for compression plugin, which can be allocated per flush positionn */ + reiser4_cluster_t *clust, /* contains data to process */ + struct inode *inode) +{ + int result = 0; + __u8 * bf = NULL; + __u8 * src = NULL; + __u8 * dst = NULL; + size_t bfsize = clust->count; + struct page * pg = NULL; + + assert("edward-401", inode != NULL); + assert("edward-495", clust != NULL); + assert("edward-496", clust->count != 0); + assert("edward-497", clust->len == 0); + assert("edward-498", clust->buf && clust->bsize); + + if (try_compress(clust, inode)) { + /* try to compress, discard bad results */ + __u32 dst_len; + compression_plugin * cplug = inode_compression_plugin(inode); + + assert("edward-602", cplug != NULL); + + if (try_encrypt(clust, inode) || clust->nr_pages != 1) { + /* [12], [42], [13], tmp buffer is required */ + bfsize += compress_overhead(inode, clust->count); + bf = reiser4_kmalloc(bfsize, GFP_KERNEL); + if (!bf) + return -ENOMEM; + dst = bf; + } + else + /* [5] */ + dst = clust->buf; + if (clust->nr_pages == 1) { + /* [42], [5] */ + assert("edward-619", clust->pages != NULL); + assert("edward-620", PageDirty(*clust->pages)); + + pg = *clust->pages; + lock_page(pg); + assert("edward-621", PageDirty(pg)); + src = kmap(pg); + } + else + /* [12], [13] */ + src = clust->buf; + + dst_len = bfsize; + + cplug->compress(ctx, src, clust->count, dst/* res */, &dst_len); + assert("edward-763", !in_interrupt()); + + clust->len = dst_len; + + assert("edward-603", clust->len <= (bf ? bfsize : clust->bsize)); + + /* estimate compression quality to accept or discard + the results of our efforts */ + if (save_compressed(clust, inode)) { + /* Accepted */ + set_compression_magic(dst + clust->len); + clust->len += CLUSTER_MAGIC_SIZE; + } + else + /* discard */ + clust->len = clust->count; + } + + if (try_encrypt(clust, inode)) { + /* align and encrypt */ + int oh; /* ohhh, the crypto alignment overhead */ + int i, icb, ocb; + __u32 * expkey; + crypto_plugin * cplug = inode_crypto_plugin(inode); + + assert("edward-716", inode_get_crypto(inode) != NULL); + + icb = crypto_blocksize(inode); + ocb = inode_scaled_offset(inode, icb); + + assert("edward-605", icb != 0); + + /* precise crypto-overhead */ + oh = crypto_overhead(0, clust, inode, WRITE_OP); + + if (dst) { + /* compression is specified */ + assert("edward-622", src != NULL); + assert("edward-623", bf != NULL && clust->len != 0); + assert("edward-624", clust->len <= clust->count); + + if (clust->len != clust->count) + /* saved */ + src = dst; + else + /* refused */ + ; + if (pg) { + /* release flushed page */ + assert("edward-625", PageLocked(pg)); + + kunmap(pg); + uncapture_page(pg); + unlock_page(pg); + page_cache_release(pg); + reiser4_kfree(clust->pages); + pg = NULL; + } + } + else { + /* [13], [5], compression wasn't specified */ + + assert("edward-626", !clust->len); + + if (clust->nr_pages != 1) { + /* [13], tmp buffer required */ + assert("edward-627", !bf); + + bfsize += oh; + bf = reiser4_kmalloc(bfsize, GFP_KERNEL); + if (!bf) { + result = -ENOMEM; + goto exit; + } + alternate_buffers(clust, &bf, &bfsize); + src = bf; + } + else { + /* [5] */ + pg = *clust->pages; + lock_page(pg); + assert("edward-628", PageDirty(pg)); + src = kmap(pg); + } + clust->len = clust->count; + } + + dst = clust->buf; + + if (oh) { + /* align the source */ + clust->len += cplug->align_cluster(src + clust->len, clust->len, icb); + + assert("edward-402", clust->len <= (pg ? PAGE_CACHE_SIZE : bfsize)); + + *(src + clust->len - 1) = oh; + } +#if REISER4_DEBUG + if (clust->len % icb) + impossible("edward-403", "bad alignment"); +#endif + + expkey = cryptcompress_inode_data(inode)->expkey; + + assert("edward-404", expkey != NULL); + + for (i=0; i < clust->len/icb; i++) + cplug->encrypt(expkey, clust->buf + i*ocb /* dst */, src + i*icb); + } + + else if (dst && clust->len != clust->count) { + /* [13], [5], saved compression, no encryption */ + if (bf) { + /* [13] */ + assert("edward-635", bf == dst); + assert("edward-636", !clust->pages); + alternate_buffers(clust, &bf, &bfsize); + } + } + else { + /* not specified or discarded compression, no encryption, + [13], [5], [] */ + + if (delay_flush_pgcluster(clust, inode)) { + if (!pg) { + assert("edward-629", !src); + assert("edward-631", !clust->len); + /* -not specified, [5] */ + pg = *clust->pages; + lock_page(pg); + src = kmap(pg); + clust->len = clust->count; + } + else { + /* -discarded, [13] */ + assert("edward-630", src != NULL); + assert("edward-632", clust->len == clust->count); + } + xmemcpy(clust->buf, src, clust->count); + } + if (!clust->len) + /* not specified, [] */ + clust->len = clust->count; + } + exit: + if (bf) + reiser4_kfree(bf); + if (pg) { + assert("edward-621", PageLocked(pg)); + + kunmap(pg); + uncapture_page(pg); + unlock_page(pg); + page_cache_release(pg); + reiser4_kfree(clust->pages); + } + return result; +} + +/* Common inflate cluster manager. Is used in readpage() or readpages() methods of + cryptcompress object plugins. + . maybe allocate temporary buffer (@bf) + . maybe decrypt disk cluster (assembled in united flow of cluster handle) and + cut crypto-alignment overhead (if any) + . maybe check for compression magic and decompress + + The final result is stored in the buffer of the cluster handle (@clust) + (which contained assembled disk cluster at the beginning of this procedure) + and is supposed to be sliced into page cluster by appropriate fillers, but if + cluster size is equal PAGE_SIZE we fill the single page (@pg) right here: + + _ _ _ _ _ _ _ _ + | | + | disk cluster | + |_ _ _ _ _ _ _ _| + | + | + ________________ _______V_______ _ _ _ _ _ _ _ _ + | | <---1--- | | | | + | @bf | ----2--> | @clust |---->| page cluster | + |________________| ----3--> |_______________| |_ _ _ _ _ _ _ _| + | | + 4 | _______________ | 5 + | | | | + +---> | @pg | <---+ + |_______________| + + + " --n-> " means one of the following functions on a pair of pointers (src, dst): + + 1, 5 - decryption or decompression + 2, 4 - decompression + 3 - alternation + + Where: + + decryption is plugin->decrypt(), + decompression is plugin->decompress, + alternation is alternate_buffers() +*/ +reiser4_internal int +inflate_cluster(reiser4_cluster_t *clust, /* cluster handle, contains assembled + disk cluster to process */ + struct inode *inode) +{ + int result = 0; + __u8 * dst = NULL; + __u8 * bf = NULL; /* buffer to handle temporary results */ + size_t bfsize = 0; /* size of the buffer above */ + struct page * pg = NULL; /* pointer to a single page if cluster size + is equal page size */ + if (clust->stat == FAKE_CLUSTER) + /* nothing to inflate */ + return 0; + + assert("edward-407", clust->buf != NULL); + assert("edward-408", clust->len != 0); + + if (inode_get_crypto(inode) != NULL) { + /* decrypt */ + int i; + int oh = 0; + int icb, ocb; + __u32 * expkey; + crypto_plugin * cplug = inode_crypto_plugin(inode); + + assert("edward-617", cplug != 0); + + if (clust->nr_pages == 1) + pg = *clust->pages; + oh = crypto_overhead(0, clust, inode, READ_OP); + + /* input/output crypto blocksizes */ + icb = crypto_blocksize(inode); + ocb = inode_scaled_offset(inode, icb); + + assert("edward-608", clust->len % ocb); + + if (pg && !need_decompression(clust, inode, + 1 /* estimate for encrypted cluster */)) { + /* [5] */ + assert("edward-609", clust->nr_pages == 1); + assert("edward-610", inode_cluster_size(inode) == PAGE_CACHE_SIZE); + + lock_page(pg); + if (PageUptodate(pg)) { + /* races with other read/write */ + goto exit; + } + dst = kmap(pg); + } + else { /* [12] or [13], tmp buffer is needed, estimate its size */ + bfsize = fsize_to_count(clust, inode); + bfsize += crypto_overhead(bfsize, clust, inode, WRITE_OP); + bf = reiser4_kmalloc(bfsize, GFP_KERNEL); + if (!bf) { + result = -ENOMEM; + goto exit; + } + dst = bf; + } + + /* decrypt cluster with the simplest mode + * FIXME-EDWARD: call here stream mode plugin */ + + expkey = cryptcompress_inode_data(inode)->expkey; + + assert("edward-141", expkey != NULL); + + for (i=0; i < clust->len/ocb; i++) + cplug->decrypt(expkey, dst + i*icb /* dst */, clust->buf + i*ocb /* src */); + + /* cut the alignment overhead */ + clust->len -= crypto_overhead(0, clust, inode, READ_OP); + } + if (need_decompression(clust, inode, 0 /* estimate for decrypted cluster */)) { + unsigned dst_len = inode_cluster_size(inode); + compression_plugin * cplug = inode_compression_plugin(inode); + tfm_info_t ctx = NULL; + __u8 * src = bf; + __u8 magic[CLUSTER_MAGIC_SIZE]; + + src = bf; + + if (clust->nr_pages == 1) + pg = *clust->pages; + + if (pg) { + /* [5] or [14] */ + lock_page(pg); + if (PageUptodate(pg)) { + /* races with other read/write */ + goto exit; + } + dst = kmap(pg); + if (!bf) + src = clust->buf; + } + else { + /* [12] or [13] */ + if (!bf) { + /* [13], tmp buffer is needed, estimate its size */ + bfsize = fsize_to_count(clust, inode); + bf = reiser4_kmalloc(bfsize, GFP_KERNEL); + if (!bf) { + result = -ENOMEM; + goto exit; + } + alternate_buffers(clust, &bf, &bfsize); + } + dst = clust->buf; + src = bf; + } + + /* Check compression magic for possible IO errors. + + End-of-cluster format created before encryption: + + data + compression_magic (4) Indicates presence of compression + infrastructure, should be private. + Can be absent. + crypto_overhead Created by ->align() method of crypto-plugin, + Can be absent. + + Crypto overhead format: + + data + tail_size (1) size of aligning tail, + 1 <= tail_size <= blksize + */ + set_compression_magic(magic); + + if (memcmp(src + (clust->len - (size_t)CLUSTER_MAGIC_SIZE), + magic, (size_t)CLUSTER_MAGIC_SIZE)) { + printk("edward-156: wrong compression magic %d (should be %d)\n", + *((int *)(src + (clust->len - (size_t)CLUSTER_MAGIC_SIZE))), *((int *)magic)); + result = -EIO; + goto exit; + } + clust->len -= (size_t)CLUSTER_MAGIC_SIZE; + /* decompress cluster */ + cplug->decompress(ctx, src, clust->len, dst, &dst_len); + + /* check length */ + assert("edward-157", dst_len == fsize_to_count(clust, inode)); + + clust->len = dst_len; + } + exit: + if (bf) + reiser4_kfree(bf); + if (clust->nr_pages == 1) { + + assert("edward-618", clust->len <= PAGE_CACHE_SIZE); + + if (!pg) { + /* no encryption, no compression */ + pg = *clust->pages; + lock_page(pg); + if (PageUptodate(pg)) { + /* races with other read/write */ + unlock_page(pg); + return result; + } + dst = kmap(pg); + xmemcpy(dst, clust->buf, clust->len); + } + + assert("edward-611", PageLocked(pg)); + assert("edward-637", !PageUptodate(pg)); + assert("edward-638", dst != NULL); + + xmemset(dst + clust->len, 0, (size_t)PAGE_CACHE_SIZE - clust->len); + kunmap(pg); + SetPageUptodate(pg); + unlock_page(pg); + } + return result; +} + +/* plugin->read() : + * generic_file_read() + * All key offsets don't make sense in traditional unix semantics unless they + * represent the beginning of clusters, so the only thing we can do is start + * right from mapping to the address space (this is precisely what filemap + * generic method does) */ + +/* plugin->readpage() */ +reiser4_internal int +readpage_cryptcompress(void *vp, struct page *page) +{ + reiser4_cluster_t clust; + struct file * file; + item_plugin * iplug; + int result; + + assert("edward-88", PageLocked(page)); + assert("edward-89", page->mapping && page->mapping->host); + + file = vp; + if (file) + assert("edward-113", page->mapping == file->f_dentry->d_inode->i_mapping); + + if (PageUptodate(page)) { + printk("readpage_cryptcompress: page became already uptodate\n"); + unlock_page(page); + return 0; + } + reiser4_cluster_init(&clust); + + iplug = item_plugin_by_id(CTAIL_ID); + if (!iplug->s.file.readpage) + return -EINVAL; + + result = iplug->s.file.readpage(&clust, page); + + assert("edward-64", ergo(result == 0, (PageLocked(page) || PageUptodate(page)))); + /* if page has jnode - that jnode is mapped + assert("edward-65", ergo(result == 0 && PagePrivate(page), + jnode_mapped(jprivate(page)))); + */ + return result; +} + +/* plugin->readpages() */ +reiser4_internal void +readpages_cryptcompress(struct file *file, struct address_space *mapping, + struct list_head *pages) +{ + item_plugin *iplug; + + iplug = item_plugin_by_id(CTAIL_ID); + iplug->s.file.readpages(file, mapping, pages); + return; +} + +static void +set_cluster_pages_dirty(reiser4_cluster_t * clust) +{ + int i; + struct page * pg; + + for (i=0; i < clust->nr_pages; i++) { + + pg = clust->pages[i]; + + lock_page(pg); + + set_page_dirty_internal(pg, 0); + SetPageUptodate(pg); + mark_page_accessed(pg); + + unlock_page(pg); + + page_cache_release(pg); + } +} + +/* This is the interface to capture cluster nodes via their struct page reference. + Any two blocks of the same cluster contain dependent modification and should + commit at the same time */ +static int +try_capture_cluster(reiser4_cluster_t * clust) +{ + int i; + int result = 0; + + for (i=0; i < clust->nr_pages; i++) { + jnode * node; + struct page *pg; + + pg = clust->pages[i]; + node = jprivate(pg); + + assert("edward-220", node != NULL); + + LOCK_JNODE(node); + + result = try_capture(node, ZNODE_WRITE_LOCK, 0/* not non-blocking */, 0 /* no can_coc */); + if (result) { + UNLOCK_JNODE(node); + jput(node); + break; + } + UNLOCK_JNODE(node); + } + if(result) + /* drop nodes */ + while(i) { + i--; + uncapture_jnode(jprivate(clust->pages[i])); + } + return result; +} + +static void +make_cluster_jnodes_dirty(reiser4_cluster_t * clust) +{ + int i; + jnode * node; + + for (i=0; i < clust->nr_pages; i++) { + node = jprivate(clust->pages[i]); + + assert("edward-221", node != NULL); + + LOCK_JNODE(node); + jnode_make_dirty_locked(node); + UNLOCK_JNODE(node); + + jput(node); + } +} + +/* collect unlocked cluster pages and jnodes */ +static int +grab_cache_cluster(struct inode * inode, reiser4_cluster_t * clust) +{ + int i; + int result = 0; + jnode * node; + + assert("edward-182", clust != NULL); + assert("edward-183", clust->pages != NULL); + assert("edward-437", clust->nr_pages != 0); + assert("edward-184", 0 < clust->nr_pages <= inode_cluster_pages(inode)); + + for (i = 0; i < clust->nr_pages; i++) { + clust->pages[i] = grab_cache_page(inode->i_mapping, clust_to_pg(clust->index, inode) + i); + if (!(clust->pages[i])) { + result = RETERR(-ENOMEM); + break; + } + node = jnode_of_page(clust->pages[i]); + unlock_page(clust->pages[i]); + if (IS_ERR(node)) { + page_cache_release(clust->pages[i]); + result = PTR_ERR(node); + break; + } + LOCK_JNODE(node); + JF_SET(node, JNODE_CLUSTER_PAGE); + UNLOCK_JNODE(node); + } + if (result) { + while(i) { + i--; + page_cache_release(clust->pages[i]); + assert("edward-222", jprivate(clust->pages[i]) != NULL); + jput(jprivate(clust->pages[i])); + } + } + return result; +} + +/* collect unlocked cluster pages */ +reiser4_internal int +grab_cluster_pages(struct inode * inode, reiser4_cluster_t * clust) +{ + int i; + int result = 0; + + assert("edward-787", clust != NULL); + assert("edward-788", clust->pages != NULL); + assert("edward-789", clust->nr_pages != 0); + assert("edward-790", 0 < clust->nr_pages <= inode_cluster_pages(inode)); + + for (i = 0; i < clust->nr_pages; i++) { + clust->pages[i] = grab_cache_page(inode->i_mapping, clust_to_pg(clust->index, inode) + i); + if (!(clust->pages[i])) { + result = RETERR(-ENOMEM); + break; + } + unlock_page(clust->pages[i]); + } + if (result) { + while(i) { + i--; + page_cache_release(clust->pages[i]); + } + } + return result; +} + +UNUSED_ARG static void +set_cluster_unlinked(reiser4_cluster_t * clust, struct inode * inode) +{ + jnode * node; + + node = jprivate(clust->pages[0]); + + assert("edward-640", node); + + LOCK_JNODE(node); + JF_SET(node, JNODE_NEW); + UNLOCK_JNODE(node); +} + +static void +put_cluster_jnodes(reiser4_cluster_t * clust) +{ + int i; + + assert("edward-223", clust != NULL); + + for (i=0; i < clust->nr_pages; i++) { + + assert("edward-208", clust->pages[i] != NULL); + assert("edward-224", jprivate(clust->pages[i]) != NULL); + + jput(jprivate(clust->pages[i])); + } +} + +/* put cluster pages */ +reiser4_internal void +release_cluster_pages(reiser4_cluster_t * clust, int from) +{ + int i; + + assert("edward-447", clust != NULL); + assert("edward-448", from < clust->nr_pages); + + for (i = from; i < clust->nr_pages; i++) { + + assert("edward-449", clust->pages[i] != NULL); + + page_cache_release(clust->pages[i]); + } +} + +static void +release_cluster(reiser4_cluster_t * clust) +{ + int i; + + assert("edward-445", clust != NULL); + + for (i=0; i < clust->nr_pages; i++) { + + assert("edward-446", clust->pages[i] != NULL); + assert("edward-447", jprivate(clust->pages[i]) != NULL); + + page_cache_release(clust->pages[i]); + jput(jprivate(clust->pages[i])); + } +} + +/* debugging purposes */ +#if REISER4_DEBUG +reiser4_internal int +cluster_invariant(reiser4_cluster_t * clust, struct inode * inode) +{ + assert("edward-279", clust != NULL); + + return (clust->pages != NULL && + clust->off < inode_cluster_size(inode) && + ergo(clust->delta != 0, clust->stat == HOLE_CLUSTER) && + clust->off + clust->count + clust->delta <= inode_cluster_size(inode)); +} +#endif + +/* guess next cluster status */ +static inline reiser4_cluster_status +next_cluster_stat(reiser4_cluster_t * clust) +{ + return (clust->stat == HOLE_CLUSTER && clust->delta == 0 /* no non-zero data */ ? HOLE_CLUSTER : DATA_CLUSTER); +} + +/* guess next cluster params */ +static void +update_cluster(struct inode * inode, reiser4_cluster_t * clust, loff_t file_off, loff_t to_file) +{ + assert ("edward-185", clust != NULL); + assert ("edward-438", clust->pages != NULL); + assert ("edward-281", cluster_invariant(clust, inode)); + + switch (clust->stat) { + case DATA_CLUSTER: + /* increment */ + clust->stat = DATA_CLUSTER; + clust->off = 0; + clust->index++; + clust->count = min_count(inode_cluster_size(inode), to_file); + break; + case HOLE_CLUSTER: + switch(next_cluster_stat(clust)) { + case HOLE_CLUSTER: + /* skip */ + clust->stat = HOLE_CLUSTER; + clust->off = 0; + clust->index = off_to_clust(file_off, inode); + clust->count = off_to_cloff(file_off, inode); + clust->delta = min_count(inode_cluster_size(inode) - clust->count, to_file); + break; + case DATA_CLUSTER: + /* keep immovability, off+count+delta=inv */ + clust->stat = DATA_CLUSTER; + clust->off = clust->off + clust->count; + clust->count = clust->delta; + clust->delta = 0; + break; + default: + impossible ("edward-282", "wrong next cluster status"); + } + default: + impossible ("edward-283", "wrong current cluster status"); + } +} + +static int +__reserve4cluster(struct inode * inode, reiser4_cluster_t * clust) +{ + int result = 0; + int reserved = 0; + jnode * j; + + assert("edward-439", inode != NULL); + assert("edward-440", clust != NULL); + assert("edward-441", clust->pages != NULL); + assert("edward-442", jprivate(clust->pages[0]) != NULL); + + j = jprivate(clust->pages[0]); + + LOCK_JNODE(j); + if (JF_ISSET(j, JNODE_CREATED)) { + /* jnode mapped <=> space reserved */ + UNLOCK_JNODE(j); + return 0; + } + reserved = estimate_insert_cluster(inode, 0/* prepped */); + result = reiser4_grab_space_force(reserved, 0); + if (result) + return result; + JF_SET(j, JNODE_CREATED); + + grabbed2cluster_reserved(reserved); + all_grabbed2free(); + +#if REISER4_DEBUG + { + reiser4_context * ctx = get_current_context(); + assert("edward-777", ctx->grabbed_blocks == 0); + } +#endif + UNLOCK_JNODE(j); + return 0; +} + +#if REISER4_TRACE +#define reserve4cluster(inode, clust, msg) __reserve4cluster(inode, clust) +#else +#define reserve4cluster(inode, clust, msg) __reserve4cluster(inode, clust) +#endif + +static void +free_reserved4cluster(struct inode * inode, reiser4_cluster_t * clust) +{ + jnode * j; + + j = jprivate(clust->pages[0]); + + LOCK_JNODE(j); + + assert("edward-443", jnode_is_cluster_page(j)); + assert("edward-444", JF_ISSET(j, JNODE_CREATED)); + + cluster_reserved2free(estimate_insert_cluster(inode, 0)); + JF_CLR(j, JNODE_CREATED); + UNLOCK_JNODE(j); +} + +static int +update_inode_cryptcompress(struct inode *inode, + loff_t new_size, + int update_i_size, int update_times, + int do_update) +{ + int result = 0; + int old_grabbed; + reiser4_context *ctx = get_current_context(); + reiser4_super_info_data * sbinfo = get_super_private(ctx->super); + + old_grabbed = ctx->grabbed_blocks; + + grab_space_enable(); + + result = reiser4_grab_space(/* one for stat data update */ + estimate_update_common(inode), + 0/* flags */); + if (result) + return result; + if (do_update) { + INODE_SET_FIELD(inode, i_size, new_size); + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + result = reiser4_update_sd(inode); + } + grabbed2free(ctx, sbinfo, ctx->grabbed_blocks - old_grabbed); + return result; +} + +/* stick pages into united flow, then release the ones */ +reiser4_internal int +flush_cluster_pages(reiser4_cluster_t * clust, struct inode * inode) +{ + int i; + + assert("edward-236", inode != NULL); + assert("edward-237", clust != NULL); + assert("edward-238", clust->off == 0); + assert("edward-239", clust->count == 0); + assert("edward-240", clust->delta == 0); + assert("edward-241", schedulable()); + assert("edward-718", crc_inode_ok(inode)); + + clust->count = fsize_to_count(clust, inode); + set_nrpages_by_frame(clust); + + cluster_reserved2grabbed(estimate_insert_cluster(inode, 0)); + + /* estimate max size of the cluster after compression and encryption + including all appended infrastructure, and allocate a buffer */ + clust->bsize = clust->count + max_crypto_overhead(inode); + clust->bsize = inode_scaled_offset(inode, clust->bsize); + + if (clust->bsize > inode_scaled_cluster_size(inode)) + clust->bsize = inode_scaled_cluster_size(inode); + if (try_compress(clust, inode)) + clust->bsize += compress_overhead(inode, clust->count); + + clust->buf = reiser4_kmalloc(clust->bsize, GFP_KERNEL); + if (!clust->buf) + return -ENOMEM; + + if (delay_flush_pgcluster(clust, inode)) { + /* delay flushing */ + assert("edward-612", clust->nr_pages == 1); + + clust->pages = reiser4_kmalloc(sizeof(*clust->pages), GFP_KERNEL); + if (!clust->pages) { + reiser4_kfree(clust->buf); + return -ENOMEM; + } + *clust->pages = find_get_page(inode->i_mapping, clust_to_pg(clust->index, inode)); + + assert("edward-613", *clust->pages != NULL); + assert("edward-614", PageDirty(*clust->pages)); + assert("edward-720", crc_inode_ok(inode)); + + return 0; + } + + /* flush more then one page after its assembling into united flow */ + for (i=0; i < clust->nr_pages; i++){ + struct page * page; + char * data; + + page = find_get_page(inode->i_mapping, clust_to_pg(clust->index, inode) + i); + + assert("edward-242", page != NULL); + assert("edward-243", PageDirty(page)); + assert("edward-634", clust->count <= clust->bsize); + /* FIXME_EDWARD: Make sure that jnodes are from the same dirty list */ + + lock_page(page); + data = kmap(page); + xmemcpy(clust->buf + pg_to_off(i), data, off_to_pgcount(clust->count, i)); + kunmap(page); + uncapture_page(page); + unlock_page(page); + page_cache_release(page); + assert("edward-721", crc_inode_ok(inode)); + } + return 0; +} + +static void +set_hint_cluster(struct inode * inode, hint_t * hint, unsigned long index, znode_lock_mode mode) +{ + reiser4_key key; + assert("edward-722", crc_inode_ok(inode)); + assert("edward-723", inode_file_plugin(inode) == file_plugin_by_id(CRC_FILE_PLUGIN_ID)); + + inode_file_plugin(inode)->key_by_inode(inode, clust_to_off(index, inode), &key); + + seal_init(&hint->seal, &hint->coord.base_coord, &key); + hint->offset = get_key_offset(&key); + hint->level = znode_get_level(hint->coord.base_coord.node); + hint->mode = mode; + //set_hint(hint, &key, ZNODE_WRITE_LOCK); +} + +static int +balance_dirty_page_cluster(reiser4_cluster_t * clust, loff_t off, loff_t to_file) +{ + int result; + loff_t new_size; + struct inode * inode; + + assert("edward-724", clust->file != NULL); + + inode = clust->file->f_dentry->d_inode; + + assert("edward-725", crc_inode_ok(inode)); + + new_size = clust_to_off(clust->index, inode) + clust->off + clust->count; + /* set hint for next cluster */ + update_cluster(inode, clust, off, to_file); + set_hint_cluster(inode, clust->hint, clust->index, ZNODE_WRITE_LOCK); + + longterm_unlock_znode(clust->hint->coord.lh); + + result = update_inode_cryptcompress(inode, new_size, (new_size > inode->i_size ? 1 : 0), 1, 1/* update stat data */); + if (result) + return result; + assert("edward-726", clust->hint->coord.lh->owner == NULL); + atomic_inc(&inode->i_count); + reiser4_throttle_write(inode); + + return 0; +} + +/* set zeroes to the cluster, update it, and maybe, try to capture its pages */ +static int +write_hole(struct inode *inode, reiser4_cluster_t * clust, loff_t file_off, loff_t to_file) +{ + char * data; + int result = 0; + unsigned cl_off, cl_count = 0; + unsigned to_pg, pg_off; + + assert ("edward-190", clust != NULL); + assert ("edward-191", inode != NULL); + assert ("edward-727", crc_inode_ok(inode)); + assert ("edward-192", cluster_invariant(clust, inode)); + assert ("edward-201", clust->stat == HOLE_CLUSTER); + + if ((clust->off == 0 && clust->count == inode_cluster_size(inode)) /* fake cluster */ || + (clust->count == 0) /* nothing to write */) { + update_cluster(inode, clust, file_off, to_file); + return 0; + } + cl_count = clust->count; /* number of zeroes to write */ + cl_off = clust->off; + pg_off = off_to_pgoff(clust->off); + + while (cl_count) { + struct page * page; + page = clust->pages[off_to_pg(cl_off)]; + + assert ("edward-284", page != NULL); + + to_pg = min_count(PAGE_CACHE_SIZE - pg_off, cl_count); + lock_page(page); + data = kmap_atomic(page, KM_USER0); + xmemset(data + pg_off, 0, to_pg); + kunmap_atomic(data, KM_USER0); + unlock_page(page); + + cl_off += to_pg; + cl_count -= to_pg; + pg_off = 0; + } + if (!clust->delta) { + /* only zeroes, try to flush */ + + set_cluster_pages_dirty(clust); + result = try_capture_cluster(clust); + if (result) + return result; + make_cluster_jnodes_dirty(clust); + /* hint for updated cluster will be set here */ + result = balance_dirty_page_cluster(clust, file_off, to_file); + } + return result; +} + +/* + The main disk search procedure for cryptcompress plugins, which + . scans all items of disk cluster + . maybe reads each one (if @read != 0) + . maybe makes its znode dirty (if @write != 0) +*/ +reiser4_internal int +find_cluster(reiser4_cluster_t * clust, + struct inode * inode, + int read, + int write) +{ + flow_t f; + hint_t * hint; + int result; + unsigned long cl_idx; + ra_info_t ra_info; + file_plugin * fplug; + item_plugin * iplug; + + assert("edward-138", clust != NULL); + assert("edward-728", clust->hint != NULL); + assert("edward-225", read || write); + assert("edward-226", schedulable()); + assert("edward-137", inode != NULL); + assert("edward-729", crc_inode_ok(inode)); + assert("edward-461", ergo(read, clust->buf != NULL)); + assert("edward-462", ergo(!read, !cluster_is_uptodate(clust))); + assert("edward-474", get_current_context()->grabbed_blocks == 0); + + hint = clust->hint; + cl_idx = clust->index; + fplug = inode_file_plugin(inode); + + /* build flow for the cluster */ + fplug->flow_by_inode(inode, clust->buf, 0 /* kernel space */, + inode_scaled_cluster_size(inode), clust_to_off(cl_idx, inode), READ_OP, &f); + if (write) { + result = reiser4_grab_space_force(estimate_disk_cluster(inode), 0); + if (result) + goto out2; + } + ra_info.key_to_stop = f.key; + set_key_offset(&ra_info.key_to_stop, get_key_offset(max_key())); + + while (f.length) { + result = find_cluster_item(hint, &f.key, 1 /* check key */, (write ? ZNODE_WRITE_LOCK : ZNODE_READ_LOCK), NULL, FIND_EXACT); + switch (result) { + case CBK_COORD_NOTFOUND: + if (inode_scaled_offset(inode, clust_to_off(cl_idx, inode)) == get_key_offset(&f.key)) { + /* first item not found */ + if (read) + /* hole cluster */ + clust->stat = FAKE_CLUSTER; + result = 0; + goto out2; + } + /* we are outside the cluster, stop search here */ + assert("edward-146", f.length != inode_scaled_cluster_size(inode)); + //crc_invalidate_extended_coord(&hint->coord); + goto ok; + case CBK_COORD_FOUND: + assert("edward-148", hint->coord.base_coord.between == AT_UNIT); + assert("edward-460", hint->coord.base_coord.unit_pos == 0); + + coord_clear_iplug(&hint->coord.base_coord); + result = zload_ra(hint->coord.base_coord.node, &ra_info); + if (unlikely(result)) + goto out2; + iplug = item_plugin_by_coord(&hint->coord.base_coord); + assert("edward-147", item_plugin_by_coord(&hint->coord.base_coord) == item_plugin_by_id(CTAIL_ID)); + if (read) { + result = iplug->s.file.read(NULL, &f, hint); + if(result) + goto out; + } + if (write) { + znode_make_dirty(hint->coord.base_coord.node); + znode_set_squeezable(hint->coord.base_coord.node); + if (!read) + move_flow_forward(&f, iplug->b.nr_units(&hint->coord.base_coord)); + } + crc_validate_extended_coord(&hint->coord, get_key_offset(&f.key)); + zrelse(hint->coord.base_coord.node); + break; + default: + goto out2; + } + } + ok: + /* at least one item was found */ + /* NOTE-EDWARD: Callers should handle the case when disk cluster is incomplete (-EIO) */ + clust->len = inode_scaled_cluster_size(inode) - f.length; + set_hint_cluster(inode, clust->hint, clust->index + 1, write ? ZNODE_WRITE_LOCK : ZNODE_READ_LOCK); + all_grabbed2free(); + return 0; + out: + zrelse(hint->coord.base_coord.node); + out2: + all_grabbed2free(); + return result; +} + +static int +get_disk_cluster_locked(reiser4_cluster_t * clust, znode_lock_mode lock_mode) +{ + reiser4_key key; + ra_info_t ra_info; + struct inode * inode; + + assert("edward-730", schedulable()); + assert("edward-731", clust != NULL); + assert("edward-732", clust->file != NULL); + + inode = clust->file->f_dentry->d_inode; + key_by_inode_cryptcompress(inode, clust_to_off(clust->index, inode), &key); + ra_info.key_to_stop = key; + set_key_offset(&ra_info.key_to_stop, get_key_offset(max_key())); + + return find_cluster_item(clust->hint, &key, 0 /* don't check key */, lock_mode, NULL, FIND_MAX_NOT_MORE_THAN); +} + +/* Read before write. + We don't take an interest in how much bytes was written when error occures */ +static int +read_some_cluster_pages(struct inode * inode, reiser4_cluster_t * clust) +{ + int i; + int result = 0; + unsigned to_read; + item_plugin * iplug; + + iplug = item_plugin_by_id(CTAIL_ID); + + assert("edward-733", get_current_context()->grabbed_blocks == 0); + + if (new_cluster(clust, inode)) { + /* new cluster, nothing to read, but anyway find a position in the tree */ + assert("edward-734", schedulable()); + assert("edward-735", clust->hint->coord.lh->owner == NULL); + + /* since we read before write, take a write lock */ + result = get_disk_cluster_locked(clust, ZNODE_WRITE_LOCK); + if (cbk_errored(result)) + return result; + assert("edward-736", clust->hint->coord.base_coord.node == clust->hint->coord.lh->node); + return 0; + } + /* bytes we wanna read starting from the beginning of cluster + to keep first @off ones */ + to_read = clust->off + clust->count + clust->delta; + + assert("edward-298", to_read <= inode_cluster_size(inode)); + + for (i = 0; i < clust->nr_pages; i++) { + struct page * pg = clust->pages[i]; + + if (clust->off <= pg_to_off(i) && pg_to_off(i) <= to_read - 1) + /* page will be completely overwritten */ + continue; + lock_page(pg); + if (PageUptodate(pg)) { + unlock_page(pg); + continue; + } + unlock_page(pg); + + if (!cluster_is_uptodate(clust)) { + /* read cluster and mark its znodes dirty */ + result = ctail_read_cluster(clust, inode, 1 /* write */); + if (result) + goto out; + } + lock_page(pg); + result = do_readpage_ctail(clust, pg); + unlock_page(pg); + if (result) { + impossible("edward-219", "do_readpage_ctail returned crap"); + goto out; + } + } + if (!cluster_is_uptodate(clust)) { + /* disk cluster unclaimed, but we need to make its znodes dirty + to make flush rewrite its content */ + result = find_cluster(clust, inode, 0 /* do not read */, 1 /*write */); + if (!cbk_errored(result)) + result = 0; + } + out: + release_cluster_buf(clust); + return result; +} + +static int +crc_make_unprepped_cluster (reiser4_cluster_t * clust, struct inode * inode) +{ + assert("edward-737", clust != NULL); + assert("edward-738", inode != NULL); + assert("edward-739", crc_inode_ok(inode)); + + return ctail_make_unprepped_cluster(clust, inode); +} + +/* Prepare before write. Called by write, writepage, truncate, etc.. + . grab cluster pages, + . maybe read pages from disk, + . maybe write hole +*/ +static int +prepare_cluster(struct inode *inode, + loff_t file_off /* write position in the file */, + loff_t to_file, /* bytes of users data to write to the file */ + int * nr_pages, /* advised number of pages */ + reiser4_cluster_t *clust, + const char * msg) + +{ + char *data; + int result = 0; + unsigned o_c_d; + + assert("edward-177", inode != NULL); + assert("edward-741", crc_inode_ok(inode)); + assert("edward-280", cluster_invariant(clust, inode)); + + o_c_d = clust->count + clust->delta; + + if (nr_pages != NULL) { + assert("edward-422", *nr_pages <= inode_cluster_pages(inode)); + clust->nr_pages = *nr_pages; + } + else { /* wasn't advised, guess by frame */ + assert("edward-740", clust->pages != NULL); +#if REISER4_DEBUG + xmemset(clust->pages, 0, sizeof(clust->pages) << inode_cluster_shift(inode)); +#endif + set_nrpages_by_frame(clust); + } + if(!clust->nr_pages) + /* do nothing */ + return 0; + /* collect unlocked pages and jnodes */ + result = grab_cache_cluster(inode, clust); + if (result) + return result; + if (clust->off == 0 && inode->i_size <= clust_to_off(clust->index, inode) + o_c_d) { + /* we don't need to read cluster from disk, just + align the current chunk of data up to nr_pages */ + unsigned off = off_to_pgcount(o_c_d, clust->nr_pages - 1); + struct page * pg = clust->pages[clust->nr_pages - 1]; + crypto_plugin * cplug = inode_crypto_plugin(inode); + + assert("edward-285", pg != NULL); + + lock_page(pg); + data = kmap_atomic(pg, KM_USER0); + if (inode_get_crypto(inode) && cplug->align_cluster) + cplug->align_cluster(data + off, off, PAGE_CACHE_SIZE); + else + xmemset(data + off, 0, PAGE_CACHE_SIZE - off); + kunmap_atomic(data, KM_USER0); + unlock_page(pg); + } + result = reserve4cluster(inode, clust, msg); + if (result) + goto exit1; + result = read_some_cluster_pages(inode, clust); + if (result) + goto exit2; + + assert("edward-742", znode_is_write_locked(clust->hint->coord.base_coord.node)); + + switch (clust->stat) { + case HOLE_CLUSTER: + result = write_hole(inode, clust, file_off, to_file); + break; + case DATA_CLUSTER: + if (!new_cluster(clust, inode)) + break; + case FAKE_CLUSTER: + /* page cluster is unprepped */ +#ifdef HANDLE_VIA_FLUSH_SCAN + set_cluster_unlinked(clust, inode); +#else + /* handling via flush squalloc */ + result = crc_make_unprepped_cluster(clust, inode); + assert("edward-743", crc_inode_ok(inode)); + assert("edward-744", znode_is_write_locked(clust->hint->coord.lh->node)); + assert("edward-745", znode_is_dirty(clust->hint->coord.lh->node)); +#endif + break; + default: + impossible("edward-746", "bad cluster status"); + } + if (!result) + return 0; + exit2: + free_reserved4cluster(inode, clust); + exit1: + put_cluster_jnodes(clust); + return result; +} + +/* get cluster handle params by two offsets */ +static void +clust_by_offs(reiser4_cluster_t * clust, struct inode * inode, loff_t o1, loff_t o2) +{ + assert("edward-295", clust != NULL); + assert("edward-296", inode != NULL); + assert("edward-297", o1 <= o2); + + clust->index = off_to_clust(o1, inode); + clust->off = off_to_cloff(o1, inode); + clust->count = min_count(inode_cluster_size(inode) - clust->off, o2 - o1); + clust->delta = 0; +} + +static void +set_cluster_params(struct inode * inode, reiser4_cluster_t * clust, flow_t * f, loff_t file_off) +{ + assert("edward-197", clust != NULL); + assert("edward-286", clust->pages != NULL); + assert("edward-198", inode != NULL); + assert("edward-747", reiser4_inode_data(inode)->cluster_shift <= MAX_CLUSTER_SHIFT); + + xmemset(clust->pages, 0, sizeof(clust->pages) << inode_cluster_shift(inode)); + + if (file_off > inode->i_size) { + /* Uhmm, hole in crypto-file... */ + loff_t hole_size; + hole_size = file_off - inode->i_size; + + printk("edward-176, Warning: Hole of size %llu in " + "cryptocompressed file (inode %llu, offset %llu) \n", + hole_size, get_inode_oid(inode), file_off); + + clust_by_offs(clust, inode, inode->i_size, file_off); + clust->stat = HOLE_CLUSTER; + if (clust->off + hole_size < inode_cluster_size(inode)) + /* besides there is also user's data to write to this cluster */ + clust->delta = min_count(inode_cluster_size(inode) - (clust->off + clust->count), f->length); + return; + } + clust_by_offs(clust, inode, file_off, file_off + f->length); + clust->stat = DATA_CLUSTER; +} + +/* Main write procedure for cryptcompress objects, + this slices user's data into clusters and copies to page cache. + If @buf != NULL, returns number of bytes in successfully written clusters, + otherwise returns error */ +/* FIXME_EDWARD replace flow by something lightweigth */ + +static loff_t +write_cryptcompress_flow(struct file * file , struct inode * inode, const char *buf, size_t count, loff_t pos) +{ + int i; + flow_t f; + hint_t hint; + lock_handle lh; + int result = 0; + size_t to_write = 0; + loff_t file_off; + reiser4_cluster_t clust; + struct page ** pages; + + assert("edward-161", schedulable()); + assert("edward-748", crc_inode_ok(inode)); + assert("edward-159", current_blocksize == PAGE_CACHE_SIZE); + assert("edward-749", reiser4_inode_data(inode)->cluster_shift <= MAX_CLUSTER_SHIFT); + + init_lh(&lh); + result = load_file_hint(file, &hint, &lh); + if (result) + return result; + //coord_init_invalid(&hint.coord.base_coord, 0); + + pages = reiser4_kmalloc(sizeof(*pages) << inode_cluster_shift(inode), GFP_KERNEL); + if (!pages) + return -ENOMEM; + result = flow_by_inode_cryptcompress(inode, (char *)buf, 1 /* user space */, count, pos, WRITE_OP, &f); + if (result) + goto out; + to_write = f.length; + + /* current write position in file */ + file_off = pos; + reiser4_cluster_init(&clust); + clust.file = file; + clust.hint = &hint; + clust.pages = pages; + + set_cluster_params(inode, &clust, &f, file_off); + + if (next_cluster_stat(&clust) == HOLE_CLUSTER) { + result = prepare_cluster(inode, file_off, f.length, NULL, &clust, "write cryptcompress hole"); + if (result) + goto out; + } + do { + char *src; + unsigned page_off, page_count; + + assert("edward-750", schedulable()); + + result = prepare_cluster(inode, file_off, f.length, NULL, &clust, "write cryptcompress flow"); /* jp+ */ + if (result) + goto out; + assert("edward-751", crc_inode_ok(inode)); + assert("edward-204", clust.stat == DATA_CLUSTER); + assert("edward-752", znode_is_write_locked(hint.coord.base_coord.node)); + + /* set write position in page */ + page_off = off_to_pgoff(clust.off); + + /* copy user's data to cluster pages */ + for (i = off_to_pg(clust.off), src = f.data; i < count_to_nrpages(clust.off + clust.count); i++, src += (int)PAGE_CACHE_SIZE) { + page_count = min_count(PAGE_CACHE_SIZE - page_off, clust.count); + + assert("edward-287", pages[i] != NULL); + + lock_page(pages[i]); + result = __copy_from_user((char *)kmap(pages[i]) + page_off, src, page_count); + kunmap(pages[i]); + if (unlikely(result)) { + unlock_page(pages[i]); + result = -EFAULT; + release_cluster(&clust); /* jp- */ + goto err1; + } + unlock_page(pages[i]); + page_off = 0; + } + assert("edward-753", crc_inode_ok(inode)); + + set_cluster_pages_dirty(&clust); /* p- */ + + result = try_capture_cluster(&clust); + if (result) + goto err2; + assert("edward-754", znode_is_dirty(hint.coord.base_coord.node)); + make_cluster_jnodes_dirty(&clust); /* j- */ + move_flow_forward(&f, clust.count); + + /* . update cluster + . set hint for new offset + . unlock znode + . update inode + . balance dirty pages + */ + result = balance_dirty_page_cluster(&clust, 0, f.length); + if(result) + goto err1; + assert("edward-755", hint.coord.lh->owner == NULL); + continue; + err2: + put_cluster_jnodes(&clust); /* j- */ + err1: + free_reserved4cluster(inode, &clust); + break; + } while (f.length); + done_lh(&lh); + out: + if (result == -EEXIST) + printk("write returns EEXIST!\n"); + + reiser4_kfree(pages); + save_file_hint(file, &hint); + if (buf) { + /* if nothing were written - there must be an error */ + assert("edward-195", ergo((to_write == f.length), result < 0)); + return (to_write - f.length) ? (to_write - f.length) : result; + } + return result; +} + +static ssize_t +write_crc_file(struct file * file, /* file to write to */ + struct inode *inode, /* inode */ + const char *buf, /* address of user-space buffer */ + size_t count, /* number of bytes to write */ + loff_t * off /* position to write which */) +{ + + int result; + loff_t pos; + ssize_t written; + + assert("edward-196", crc_inode_ok(inode)); + + result = generic_write_checks(file, off, &count, 0); + if (unlikely(result != 0)) + return result; + + if (unlikely(count == 0)) + return 0; + + /* FIXME-EDWARD: other UNIX features */ + + pos = *off; + written = write_cryptcompress_flow(file, inode, (char *)buf, count, pos); + if (written < 0) { + if (written == -EEXIST) + printk("write_crc_file returns EEXIST!\n"); + return written; + } + + /* update position in a file */ + *off = pos + written; + /* return number of written bytes */ + return written; +} + +/* plugin->u.file.write */ +reiser4_internal ssize_t +write_cryptcompress(struct file * file, /* file to write to */ + const char *buf, /* address of user-space buffer */ + size_t count, /* number of bytes to write */ + loff_t * off /* position to write which */) +{ + ssize_t result; + struct inode *inode; + + inode = file->f_dentry->d_inode; + + down(&inode->i_sem); + + result = write_crc_file(file, inode, buf, count, off); + + up(&inode->i_sem); + return result; +} + +/* Helper function for cryptcompress_truncate */ +static int +find_object_size(struct inode *inode, loff_t * size) +{ + int result; + reiser4_key key; + hint_t hint; + coord_t *coord; + lock_handle lh; + item_plugin *iplug; + file_plugin *fplug = inode_file_plugin(inode); + + assert("edward-95", crc_inode_ok(inode)); + + fplug->key_by_inode(inode, get_key_offset(max_key()), &key); + + hint_init_zero(&hint, &lh); + /* find the last item of this object */ + result = find_cluster_item(&hint, &key, 0, ZNODE_READ_LOCK, 0/* ra_info */, FIND_MAX_NOT_MORE_THAN); + if (result == CBK_COORD_NOTFOUND) { + /* object doesn't have any item */ + done_lh(&lh); + *size = 0; + return 0; + } + if (result != CBK_COORD_FOUND) { + /* error occurred */ + done_lh(&lh); + return result; + } + coord = &hint.coord.base_coord; + + /* there is at least one item */ + coord_clear_iplug(coord); + result = zload(coord->node); + if (unlikely(result)) { + done_lh(&lh); + return result; + } + iplug = item_plugin_by_coord(coord); + assert("edward-277", iplug == item_plugin_by_id(CTAIL_ID)); + assert("edward-659", cluster_shift_by_coord(coord) == inode_cluster_shift(inode)); + + iplug->s.file.append_key(coord, &key); + + *size = get_key_offset(&key); + + zrelse(coord->node); + done_lh(&lh); + + return 0; +} + +UNUSED_ARG static int +cut_items_cryptcompress(struct inode *inode, loff_t new_size, int update_sd) +{ + reiser4_key from_key, to_key; + reiser4_key smallest_removed; + int result = 0; + + assert("edward-293", inode_file_plugin(inode)->key_by_inode == key_by_inode_cryptcompress); + key_by_inode_cryptcompress(inode, off_to_clust_to_off(new_size, inode), &from_key); + to_key = from_key; + set_key_offset(&to_key, get_key_offset(max_key())); + + while (1) { + result = reserve_cut_iteration(tree_by_inode(inode)); + if (result) + break; + + result = cut_tree_object(current_tree, &from_key, &to_key, + &smallest_removed, inode, 0); + if (result == -E_REPEAT) { + /* -E_REPEAT is a signal to interrupt a long file truncation process */ + /* FIXME(Zam) cut_tree does not support that signaling.*/ + result = update_inode_cryptcompress + (inode, get_key_offset(&smallest_removed), 1, 1, update_sd); + if (result) + break; + + all_grabbed2free(); + reiser4_release_reserved(inode->i_sb); + + txn_restart_current(); + continue; + } + if (result) + break; + result = update_inode_cryptcompress + (inode, get_key_offset(&smallest_removed), 1, 1, update_sd); + break; + } + + all_grabbed2free(); + reiser4_release_reserved(inode->i_sb); + return result; +} + +/* The following two procedures are called when truncate decided + to deal with real items */ +static int +cryptcompress_append_hole(struct inode * inode, loff_t new_size) +{ + return write_cryptcompress_flow(0, inode, 0, 0, new_size); +} + +reiser4_internal void +truncate_cluster(struct inode * inode, pgoff_t start, long count) +{ + loff_t from, to; + + from = ((loff_t)start) << PAGE_CACHE_SHIFT; + to = from + (((loff_t)count) << PAGE_CACHE_SHIFT) - 1; + truncate_inode_pages_range(inode->i_mapping, from, to); + truncate_jnodes_range(inode, start, count); +} + +static int +shorten_cryptcompress(struct inode * inode, loff_t new_size, int update_sd, + loff_t asize) +{ + int result; + pgoff_t start, count; + struct page ** pages; + loff_t old_size; + char * kaddr; + unsigned pgoff; + reiser4_cluster_t clust; + crypto_plugin * cplug; + + assert("edward-290", inode->i_size > new_size); + assert("edward-756", crc_inode_ok(inode)); + + pgoff = 0; + start = count = 0; + old_size = inode->i_size; + cplug = inode_crypto_plugin(inode); + + result = cut_file_items(inode, new_size, update_sd, asize, 0); + + if(result) + return result; + + assert("edward-660", ergo(!new_size, + (reiser4_inode_data(inode)->anonymous_eflushed == 0 && + reiser4_inode_data(inode)->captured_eflushed == 0))); + + if (!off_to_cloff(new_size, inode)) + /* truncated to cluster boundary (1) */ + return 0; + /* there is a cluster which should be modified and flushed */ + pages = reiser4_kmalloc(sizeof(*pages) << inode_cluster_shift(inode), GFP_KERNEL); + if (!pages) + return -ENOMEM; + + reiser4_cluster_init(&clust); + clust.pages = pages; + /* set frame */ + clust_by_offs(&clust, inode, new_size, old_size); + + /* read the whole cluster */ + result = prepare_cluster(inode, 0, 0, NULL, &clust, "shorten cryptcompress"); + if (result) { + reiser4_kfree(pages); + return result; + } + /* jp+ */ + /* truncate last cluster pages and jnodes */ + assert("edward-294", clust.stat == DATA_CLUSTER); + assert("edward-661", clust.off > 0); + + pgoff = off_to_pgoff(clust.off); + + /* reduced idx of the first page to release */ + start = off_to_pg(clust.off - 1) + 1; + /* number of pages to release and truncate */ + count = clust.nr_pages - start; + + /* release last pages which won't participate in flush */ + release_cluster_pages(&clust, start); + /* truncate the pages above, also don't forget about jnodes */ + truncate_cluster(inode, clust_to_pg(clust.index, inode) + start, count); + /* update number of cluster pages */ + clust.nr_pages = start; + + /* align last non-truncated page */ + lock_page(pages[clust.nr_pages - 1]); + kaddr = kmap_atomic(pages[clust.nr_pages - 1], KM_USER0); + + if (inode_get_crypto(inode) && cplug->align_cluster) + cplug->align_cluster(kaddr + pgoff, pgoff, PAGE_CACHE_SIZE); + else + xmemset(kaddr + pgoff, 0, PAGE_CACHE_SIZE - pgoff); + unlock_page(pages[clust.nr_pages - 1]); + + set_cluster_pages_dirty(&clust); /* p- */ + result = try_capture_cluster(&clust); + if (result) { + put_cluster_jnodes(&clust); + goto exit; + } + make_cluster_jnodes_dirty(&clust); /* j- */ + + /* FIXME-EDWARD: Update this using balance dirty cluster pages */ + assert("edward-757", 0 /* don't free reserved4cluster when success */); + result = update_inode_cryptcompress(inode, new_size, 1, 1, update_sd); + if(!result) + goto exit; + reiser4_throttle_write(inode); + exit: + free_reserved4cluster(inode, &clust); + reiser4_kfree(pages); + return result; +} + +/* This is called in setattr_cryptcompress when it is used to truncate, + and in delete_cryptcompress */ + +static int +cryptcompress_truncate(struct inode *inode, /* old size */ + loff_t new_size, /* new size */ + int update_sd) +{ + int result; + loff_t old_size = inode->i_size; + loff_t asize; /* actual size */ + + result = find_object_size(inode, &asize); + + if (result) + return result; + if (!asize || + /* no items */ + off_to_clust(asize, inode) < off_to_clust(new_size, inode) + /* truncating up to fake cluster boundary */) { + /* do not touch items */ + assert("edward-662", !off_to_cloff(new_size, inode)); + + INODE_SET_FIELD(inode, i_size, asize); + truncate_cluster(inode, size_to_next_pg(new_size), + size_to_pg(old_size) - size_to_next_pg(new_size) + 1); + assert("edward-663", ergo(!new_size, + reiser4_inode_data(inode)->anonymous_eflushed == 0 && + reiser4_inode_data(inode)->captured_eflushed == 0)); + + if (update_sd) { + result = setattr_reserve_common(tree_by_inode(inode)); + if (!result) + result = update_inode_cryptcompress(inode, new_size, 1, 1, 1); + all_grabbed2free(); + } + return result; + } + result = (old_size < new_size ? cryptcompress_append_hole(inode, new_size) : + shorten_cryptcompress(inode, new_size, update_sd, asize)); + return result; +} + +/* plugin->u.file.truncate */ +reiser4_internal int +truncate_cryptcompress(struct inode *inode, loff_t new_size) +{ + return 0; +} + +#if 0 +static int +cryptcompress_writepage(struct page * page, reiser4_cluster_t * clust) +{ + int result = 0; + int nrpages; + struct inode * inode; + + assert("edward-423", page->mapping && page->mapping->host); + + inode = page->mapping->host; + reiser4_cluster_init(&clust); + + /* read all cluster pages if necessary */ + clust.pages = reiser4_kmalloc(sizeof(*clust.pages) << inode_cluster_shift(inode), GFP_KERNEL); + if (!pages) + return -ENOMEM; + clust.index = pg_to_clust(page->index, inode); + clust.off = pg_to_off_to_cloff(page->index, inode); + clust.count = PAGE_CACHE_SIZE; + nrpages = count_to_nrpages(fsize_to_count(&clust, inode)); + + result = prepare_cluster(page->mapping->host, 0, 0, &nrpages, &clust, "cryptcompress_writepage"); /* jp+ */ + if(result) + goto exit; + + set_cluster_pages_dirty(&clust); /* p- */ + result = try_capture_cluster(&clust); + if (result) { + free_reserved4cluster(inode, &clust); + put_cluster_jnodes(&clust); /* j- */ + goto exit; + } + lock_page(page); + make_cluster_jnodes_dirty(&clust); + put_cluster_jnodes(&clust); /* j- */ + exit: + reiser4_kfree(clust.pages); + return result; +} + +/* make sure for each page the whole cluster was captured */ +static int +writepages_cryptcompress(struct address_space * mapping) +{ + struct list_head *mpages; + int result; + int nr; + int nrpages; + int captured = 0, clean = 0, writeback = 0; + reiser4_cluster_t * clust; + + reiser4_cluster_init(clust); + result = 0; + nr = 0; + + spin_lock (&mapping->page_lock); + + mpages = get_moved_pages(mapping); + while ((result == 0 || result == 1) && !list_empty (mpages) && nr < CAPTURE_APAGE_BURST) { + struct page *pg = list_to_page(mpages); + + assert("edward-481", PageDirty(pg)); + + if (!clust->nr_pages || !page_of_cluster(pg, &clust, inode)) { + /* update cluster handle */ + clust.index = pg_to_clust(pg->index, inode); + clust.off = pg_to_off_to_cloff(pg->index, inode); + clust.count = PAGE_CACHE_SIZE; + /* advice number of pages */ + nrpages = count_to_nrpages(fsize_to_count(&clust, inode)); + + result = prepare_cluster(mapping->host, 0, 0, &nrpages, &clust, + } + result = capture_anonymous_page(pg, 0); + if (result == 1) { + ++ nr; + result = 0; + } + } + spin_unlock(&mapping->page_lock); + + if (result) { + warning("vs-1454", "Cannot capture anon pages: %i (%d %d %d)\n", result, captured, clean, writeback); + return result; + } + + + if (nr >= CAPTURE_APAGE_BURST) + redirty_inode(mapping->host); + + if (result == 0) + result = capture_anonymous_jnodes(mapping->host); + + if (result != 0) + warning("nikita-3328", "Cannot capture anon pages: %i\n", result); + return result; +} + +#endif + +/* plugin->u.file.capture + FIXME: capture method of file plugin is called by reiser4_writepages. It has to capture all + anonymous pages and jnodes of the mapping. See capture_unix_file, for example + */ +reiser4_internal int +capture_cryptcompress(struct inode *inode, const struct writeback_control *wbc, long *captured) +{ + +#if 0 + int result; + struct inode *inode; + + assert("edward-424", PageLocked(page)); + assert("edward-425", PageUptodate(page)); + assert("edward-426", page->mapping && page->mapping->host); + + inode = page->mapping->host; + assert("edward-427", pg_to_off(page->index) < inode->i_size); + + unlock_page(page); + if (pg_to_off(page->index) >= inode->i_size) { + /* race with truncate? */ + lock_page(page); + page_cache_release(page); + return RETERR(-EIO); + } + /* FIXME-EDWARD: Estimate insertion */ + result = cryptcompress_writepage(page); + assert("edward-428", PageLocked(page)); + return result; + + int result; + reiser4_context ctx; + + if (!inode_has_anonymous_pages(inode)) + return 0; + + init_context(&ctx, inode->i_sb); + + ctx.nobalance = 1; + assert("edward-482", lock_stack_isclean(get_current_lock_stack())); + + result = 0; + + do { + result = writepages_cryptcompress(inode->i_mapping); + if (result != 0 || wbc->sync_mode != WB_SYNC_ALL) + break; + result = txnmgr_force_commit_all(inode->i_sb, 0); + } while (result == 0 && inode_has_anonymous_pages(inode)); + + reiser4_exit_context(&ctx); + return result; +#endif + return 0; +} + +static inline void +validate_crc_extended_coord(uf_coord_t *uf_coord, loff_t offset) +{ + assert("edward-418", uf_coord->valid == 0); + assert("edward-419", item_plugin_by_coord(&uf_coord->base_coord)->s.file.init_coord_extension); + + /* FIXME: */ + item_body_by_coord(&uf_coord->base_coord); + item_plugin_by_coord(&uf_coord->base_coord)->s.file.init_coord_extension(uf_coord, offset); +} + +/* plugin->u.file.mmap: + generic_file_mmap */ + +/* plugin->u.file.release */ +/* plugin->u.file.get_block */ +/* This function is used for ->bmap() VFS method in reiser4 address_space_operations */ +reiser4_internal int +get_block_cryptcompress(struct inode *inode, sector_t block, struct buffer_head *bh_result, int create UNUSED_ARG) +{ + if (current_blocksize != inode_cluster_size(inode)) + return RETERR(-EINVAL); + else { + int result; + reiser4_key key; + hint_t hint; + lock_handle lh; + item_plugin *iplug; + + assert("edward-420", create == 0); + key_by_inode_cryptcompress(inode, (loff_t)block * current_blocksize, &key); + hint_init_zero(&hint, &lh); + result = find_cluster_item(&hint, &key, 0, ZNODE_READ_LOCK, 0, FIND_EXACT); + if (result != CBK_COORD_FOUND) { + done_lh(&lh); + return result; + } + result = zload(hint.coord.base_coord.node); + if (unlikely(result)) { + done_lh(&lh); + return result; + } + iplug = item_plugin_by_coord(&hint.coord.base_coord); + + assert("edward-421", iplug == item_plugin_by_id(CTAIL_ID)); + + if (!hint.coord.valid) + validate_crc_extended_coord(&hint.coord, + (loff_t) block << PAGE_CACHE_SHIFT); + if (iplug->s.file.get_block) + result = iplug->s.file.get_block(&hint.coord.base_coord, block, bh_result); + else + result = RETERR(-EINVAL); + + zrelse(hint.coord.base_coord.node); + done_lh(&lh); + return result; + } +} + +/* plugin->u.file.delete */ +/* EDWARD-FIXME-HANS: comment is where? */ +reiser4_internal int +delete_cryptcompress(struct inode *inode) +{ + int result; + + assert("edward-429", inode->i_nlink == 0); + + if (inode->i_size) { + result = cryptcompress_truncate(inode, 0, 0); + if (result) { + warning("edward-430", "cannot truncate cryptcompress file %lli: %i", + get_inode_oid(inode), result); + return result; + } + } + return delete_object(inode, 0); +} + +/* plugin->u.file.init_inode_data */ +/* plugin->u.file.owns_item: + owns_item_common */ +/* plugin->u.file.pre_delete */ +/* EDWARD-FIXME-HANS: comment is where? */ +reiser4_internal int +pre_delete_cryptcompress(struct inode *inode) +{ + return cryptcompress_truncate(inode, 0, 0); +} + +/* plugin->u.file.setattr method */ +reiser4_internal int +setattr_cryptcompress(struct inode *inode, /* Object to change attributes */ + struct iattr *attr /* change description */ ) +{ + int result; + + if (attr->ia_valid & ATTR_SIZE) { + /* EDWARD-FIXME-HANS: VS-FIXME-HANS: + Q: this case occurs when? truncate? + A: yes + + Q: If so, why isn't this code in truncate itself instead of here? + + A: because vfs calls fs's truncate after it has called truncate_inode_pages to get rid of pages + corresponding to part of file being truncated. In reiser4 it may cause existence of unallocated + extents which do not have jnodes. Flush code does not expect that. Solution of this problem is + straightforward. As vfs's truncate is implemented using setattr operation (common implementaion of + which calls truncate_inode_pages and fs's truncate in case when size of file changes) - it seems + reasonable to have reiser4_setattr which will take care of removing pages, jnodes and extents + simultaneously in case of truncate. + */ + + /* truncate does reservation itself and requires exclusive access obtained */ + if (inode->i_size != attr->ia_size) { + loff_t old_size; + + inode_check_scale(inode, inode->i_size, attr->ia_size); + + old_size = inode->i_size; + + result = cryptcompress_truncate(inode, attr->ia_size, 1/* update stat data */); + + if (!result) { + /* items are removed already. inode_setattr will call vmtruncate to invalidate truncated + pages and truncate_cryptcompress which will do nothing. FIXME: is this necessary? */ + INODE_SET_FIELD(inode, i_size, old_size); + result = inode_setattr(inode, attr); + } + } else + result = 0; + } else { + /* FIXME: Edward, please consider calling setattr_common() here */ + result = setattr_reserve_common(tree_by_inode(inode)); + if (!result) { + result = inode_setattr(inode, attr); + if (!result) + /* "capture" inode */ + result = reiser4_mark_inode_dirty(inode); + all_grabbed2free(); + } + } + return result; +} + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/cryptcompress.h 2004-09-03 00:57:05.183251288 -0700 @@ -0,0 +1,170 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ +/* See http://www.namesys.com/cryptcompress_design.html */ + +#if !defined( __FS_REISER4_CRYPTCOMPRESS_H__ ) +#define __FS_REISER4_CRYPTCOMPRESS_H__ + + +#include +#include + +#define MIN_CLUSTER_SIZE PAGE_CACHE_SIZE +#define MAX_CLUSTER_SHIFT 4 +#define DEFAULT_CLUSTER_SHIFT 0 +#define MIN_SIZE_FOR_COMPRESSION 64 +#define MIN_CRYPTO_BLOCKSIZE 8 +#define CLUSTER_MAGIC_SIZE (MIN_CRYPTO_BLOCKSIZE >> 1) + +/* cluster status */ +typedef enum { + DATA_CLUSTER = 0, + HOLE_CLUSTER = 1, /* indicates hole for write ops */ + FAKE_CLUSTER = 2 /* indicates absence of disk cluster for read ops */ +} reiser4_cluster_status; + +/* reiser4 transforms */ +typedef enum { + CRYPTO_TFM, + DIGEST_TFM, + COMPRESS_TFM, + LAST_TFM +} reiser4_tfm; + +/* Write modes for item conversion in flush squeeze phase */ +typedef enum { + CRC_FIRST_ITEM = 1, + CRC_APPEND_ITEM = 2, + CRC_OVERWRITE_ITEM = 3, + CRC_CUT_ITEM = 4 +} crc_write_mode_t; + +/* reiser4 cluster manager transforms page cluster into disk cluster (and back) via + input/output stream of crypto/compression algorithms using copy on clustering. + COC means that page cluster will be assembled into united stream before compression, + and output stream of decompression algorithm will be split into pages. + This manager consists mostly of operations on the following object which represents + one cluster: +*/ +typedef struct reiser4_cluster{ + __u8 * buf; /* pointer to input/output stream of crypto/compression algorithm */ + size_t bsize; /* size of the buffer allocated for the stream */ + size_t len; /* actual length of the stream above */ + int nr_pages; /* number of attached pages */ + struct page ** pages; /* attached pages */ + struct file * file; + hint_t * hint; + reiser4_cluster_status stat; + /* sliding frame of cluster size in loff_t-space to translate main file 'offsets' + like read/write position, size, new size (for truncate), etc.. into number + of pages, cluster status, etc..*/ + unsigned long index; /* cluster index, coord of the frame */ + unsigned off; /* offset we want to read/write/truncate from */ + unsigned count; /* bytes to read/write/truncate */ + unsigned delta; /* bytes of user's data to append to the hole */ +} reiser4_cluster_t; + +/* security attributes supposed to be stored on disk + are loaded by stat-data methods (see plugin/item/static_stat.c */ +typedef struct crypto_stat { + __u8 * keyid; /* pointer to a fingerprint */ + __u16 keysize; /* key size, bits */ +} crypto_stat_t; + +/* cryptcompress specific part of reiser4_inode */ +typedef struct cryptcompress_info { + struct crypto_tfm *tfm[LAST_TFM]; + __u32 * expkey; +} cryptcompress_info_t; + +cryptcompress_info_t *cryptcompress_inode_data(const struct inode * inode); +int equal_to_rdk(znode *, const reiser4_key *); +int equal_to_ldk(znode *, const reiser4_key *); +int goto_right_neighbor(coord_t *, lock_handle *); +int load_file_hint(struct file *, hint_t *, lock_handle *); +void save_file_hint(struct file *, const hint_t *); + +/* declarations of functions implementing methods of cryptcompress object plugin */ +void init_inode_data_cryptcompress(struct inode *inode, reiser4_object_create_data *crd, int create); +int create_cryptcompress(struct inode *, struct inode *, reiser4_object_create_data *); +int open_cryptcompress(struct inode * inode, struct file * file); +int truncate_cryptcompress(struct inode *, loff_t size); +int readpage_cryptcompress(void *, struct page *); +int capture_cryptcompress(struct inode *inode, const struct writeback_control *wbc, long *); +ssize_t write_cryptcompress(struct file *, const char *buf, size_t size, loff_t *off); +int release_cryptcompress(struct inode *inode, struct file *); +int mmap_cryptcompress(struct file *, struct vm_area_struct *vma); +int get_block_cryptcompress(struct inode *, sector_t block, struct buffer_head *bh_result, int create); +int flow_by_inode_cryptcompress(struct inode *, char *buf, int user, loff_t, loff_t, rw_op, flow_t *); +int key_by_inode_cryptcompress(struct inode *, loff_t off, reiser4_key *); +int delete_cryptcompress(struct inode *); +int owns_item_cryptcompress(const struct inode *, const coord_t *); +int setattr_cryptcompress(struct inode *, struct iattr *); +void readpages_cryptcompress(struct file *, struct address_space *, struct list_head *pages); +void init_inode_data_cryptcompress(struct inode *, reiser4_object_create_data *, int create); +int pre_delete_cryptcompress(struct inode *); +void hint_init_zero(hint_t *, lock_handle *); +void destroy_inode_cryptcompress(struct inode * inode); +int crc_inode_ok(struct inode * inode); + +static inline struct crypto_tfm * +inode_get_tfm (struct inode * inode, reiser4_tfm tfm) +{ + return cryptcompress_inode_data(inode)->tfm[tfm]; +} + +static inline struct crypto_tfm * +inode_get_crypto (struct inode * inode) +{ + return (inode_get_tfm(inode, CRYPTO_TFM)); +} + +static inline struct crypto_tfm * +inode_get_digest (struct inode * inode) +{ + return (inode_get_tfm(inode, DIGEST_TFM)); +} + +static inline unsigned int +crypto_blocksize(struct inode * inode) +{ + assert("edward-758", inode_get_tfm(inode, CRYPTO_TFM) != NULL); + return crypto_tfm_alg_blocksize(inode_get_tfm(inode, CRYPTO_TFM)); +} + +#define REGISTER_NONE_ALG(ALG, TFM) \ +static int alloc_none_ ## ALG (struct inode * inode) \ +{ \ + cryptcompress_info_t * info; \ + assert("edward-760", inode != NULL); \ + \ + info = cryptcompress_inode_data(inode); \ + \ + \ + cryptcompress_inode_data(inode)->tfm[TFM ## _TFM] = NULL; \ + return 0; \ + \ +} \ +static void free_none_ ## ALG (struct inode * inode) \ +{ \ + cryptcompress_info_t * info; \ + assert("edward-761", inode != NULL); \ + \ + info = cryptcompress_inode_data(inode); \ + \ + assert("edward-762", info != NULL); \ + \ + info->tfm[TFM ## _TFM] = NULL; \ +} + +#endif /* __FS_REISER4_CRYPTCOMPRESS_H__ */ + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/digest.c 2004-09-03 00:57:05.183251288 -0700 @@ -0,0 +1,32 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* reiser4 digest transform plugin (is used by cryptcompress object plugin) */ + +#include "../debug.h" +#include "plugin_header.h" +#include "plugin.h" +#include "cryptcompress.h" + +#include + +#define NONE_DIGEST_SIZE 0 + +REGISTER_NONE_ALG(digest, DIGEST) + +/* digest plugins */ +digest_plugin digest_plugins[LAST_DIGEST_ID] = { + [NONE_DIGEST_ID] = { + .h = { + .type_id = REISER4_DIGEST_PLUGIN_TYPE, + .id = NONE_DIGEST_ID, + .pops = NULL, + .label = "none", + .desc = "trivial digest", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .dsize = NONE_DIGEST_SIZE, + .alloc = alloc_none_digest, + .free = free_none_digest, + } +}; + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/dir/dir.c 2004-09-03 00:57:05.192249920 -0700 @@ -0,0 +1,1919 @@ +/* Copyright 2001, 2002, 2003, 2004 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Methods of directory plugin. */ + +#include "../../forward.h" +#include "../../debug.h" +#include "../../spin_macros.h" +#include "../plugin_header.h" +#include "../../key.h" +#include "../../kassign.h" +#include "../../coord.h" +#include "../../type_safe_list.h" +#include "../plugin.h" +#include "dir.h" +#include "../item/item.h" +#include "../security/perm.h" +#include "../../jnode.h" +#include "../../znode.h" +#include "../../tap.h" +#include "../../vfs_ops.h" +#include "../../inode.h" +#include "../../super.h" +#include "../../safe_link.h" +#include "../object.h" + +#include "hashed_dir.h" +#include "pseudo_dir.h" + +#include /* for __u?? */ +#include /* for struct file */ +#include +#include /* for struct dentry */ + +/* helper function. Standards require than for many file-system operations + on success ctime and mtime of parent directory is to be updated. */ +reiser4_internal int +reiser4_update_dir(struct inode *dir) +{ + assert("nikita-2525", dir != NULL); + + dir->i_ctime = dir->i_mtime = CURRENT_TIME; + return reiser4_mark_inode_dirty(dir); +} + +/* estimate disk space necessary to add a link from @parent to @object. */ +static reiser4_block_nr common_estimate_link( + struct inode *parent /* parent directory */, + struct inode *object /* object to which new link is being cerated */) +{ + reiser4_block_nr res = 0; + file_plugin *fplug; + dir_plugin *dplug; + + assert("vpf-317", object != NULL); + assert("vpf-318", parent != NULL ); + + fplug = inode_file_plugin(object); + dplug = inode_dir_plugin(parent); + + /* reiser4_add_nlink(object) */ + res += fplug->estimate.update(object); + /* add_entry(parent) */ + res += dplug->estimate.add_entry(parent); + /* reiser4_del_nlink(object) */ + res += fplug->estimate.update(object); + /* update_dir(parent) */ + res += inode_file_plugin(parent)->estimate.update(parent); + /* safe-link */ + res += estimate_one_item_removal(tree_by_inode(object)); + + return res; +} + +/* add link from @parent directory to @existing object. + + . get plugins + . check permissions + . check that "existing" can hold yet another link + . start transaction + . add link to "existing" + . add entry to "parent" + . if last step fails, remove link from "existing" + +*/ +static int +link_common(struct inode *parent /* parent directory */ , + struct dentry *existing /* dentry of object to which + * new link is being + * cerated */ , + struct dentry *newname /* new name */ ) +{ + int result; + struct inode *object; + dir_plugin *parent_dplug; + reiser4_dir_entry_desc entry; + reiser4_object_create_data data; + reiser4_block_nr reserve; + + assert("nikita-1431", existing != NULL); + assert("nikita-1432", parent != NULL); + assert("nikita-1433", newname != NULL); + + object = existing->d_inode; + assert("nikita-1434", object != NULL); + + /* check for race with create_object() */ + if (inode_get_flag(object, REISER4_IMMUTABLE)) + return RETERR(-E_REPEAT); + + /* links to directories are not allowed if file-system + logical name-space should be ADG */ + if (S_ISDIR(object->i_mode) && reiser4_is_set(parent->i_sb, REISER4_ADG)) + return RETERR(-EISDIR); + + /* check permissions */ + result = perm_chk(parent, link, existing, parent, newname); + if (result != 0) + return result; + + parent_dplug = inode_dir_plugin(parent); + + xmemset(&entry, 0, sizeof entry); + entry.obj = object; + + data.mode = object->i_mode; + data.id = inode_file_plugin(object)->h.id; + + reserve = common_estimate_link(parent, existing->d_inode); + if ((__s64)reserve < 0) + return reserve; + + if (reiser4_grab_space(reserve, BA_CAN_COMMIT)) + return RETERR(-ENOSPC); + + /* + * Subtle race handling: sys_link() doesn't take i_sem on @parent. It + * means that link(2) can race against unlink(2) or rename(2), and + * inode is dead (->i_nlink == 0) when reiser4_link() is entered. + * + * For such inode we have to undo special processing done in + * reiser4_unlink() viz. creation of safe-link. + */ + if (unlikely(inode_file_plugin(object)->not_linked(object))) { + result = safe_link_del(object, SAFE_UNLINK); + if (result != 0) + return result; + } + + result = reiser4_add_nlink(object, parent, 1); + if (result == 0) { + /* add entry to the parent */ + result = parent_dplug->add_entry(parent, newname, &data, &entry); + if (result != 0) { + /* failure to add entry to the parent, remove + link from "existing" */ + reiser4_del_nlink(object, parent, 1); + /* now, if this fails, we have a file with too + big nlink---space leak, much better than + directory entry pointing to nowhere */ + /* may be it should be recorded somewhere, but + if addition of link to parent and update of + object's stat data both failed, chances are + that something is going really wrong */ + } + } + if (result == 0) { + atomic_inc(&object->i_count); + /* Upon successful completion, link() shall mark for update + the st_ctime field of the file. Also, the st_ctime and + st_mtime fields of the directory that contains the new + entry shall be marked for update. --SUS + */ + result = reiser4_update_dir(parent); + } + return result; +} + +/* estimate disk space necessary to remove a link between @parent and + * @object. */ +static reiser4_block_nr common_estimate_unlink ( + struct inode *parent /* parent directory */, + struct inode *object /* object to which new link is being cerated */) +{ + reiser4_block_nr res = 0; + file_plugin *fplug; + dir_plugin *dplug; + + assert("vpf-317", object != NULL); + assert("vpf-318", parent != NULL ); + + fplug = inode_file_plugin(object); + dplug = inode_dir_plugin(parent); + + /* rem_entry(parent) */ + res += dplug->estimate.rem_entry(parent); + /* reiser4_del_nlink(object) */ + res += fplug->estimate.update(object); + /* update_dir(parent) */ + res += inode_file_plugin(parent)->estimate.update(parent); + /* fplug->unlink */ + res += fplug->estimate.unlink(object, parent); + /* safe-link */ + res += estimate_one_insert_item(tree_by_inode(object)); + + return res; +} + +/* grab space for unlink. */ +static int +unlink_check_and_grab(struct inode *parent, struct dentry *victim) +{ + file_plugin *fplug; + struct inode *child; + int result; + + result = 0; + child = victim->d_inode; + fplug = inode_file_plugin(child); + + /* check for race with create_object() */ + if (inode_get_flag(child, REISER4_IMMUTABLE)) + return RETERR(-E_REPEAT); + /* object being deleted should have stat data */ + assert("vs-949", !inode_get_flag(child, REISER4_NO_SD)); + + /* check permissions */ + result = perm_chk(parent, unlink, parent, victim); + if (result != 0) + return result; + + /* ask object plugin */ + if (fplug->can_rem_link != NULL && !fplug->can_rem_link(child)) + return RETERR(-ENOTEMPTY); + + result = (int)common_estimate_unlink(parent, child); + if (result < 0) + return result; + + return reiser4_grab_reserved(child->i_sb, result, BA_CAN_COMMIT); +} + +/* remove link from @parent directory to @victim object. + + . get plugins + . find entry in @parent + . check permissions + . decrement nlink on @victim + . if nlink drops to 0, delete object +*/ +static int +unlink_common(struct inode *parent /* parent object */ , + struct dentry *victim /* name being removed from @parent */) +{ + int result; + struct inode *object; + file_plugin *fplug; + + object = victim->d_inode; + fplug = inode_file_plugin(object); + assert("nikita-2882", fplug->detach != NULL); + + result = unlink_check_and_grab(parent, victim); + if (result != 0) + return result; + + result = fplug->detach(object, parent); + if (result == 0) { + dir_plugin *parent_dplug; + reiser4_dir_entry_desc entry; + + parent_dplug = inode_dir_plugin(parent); + xmemset(&entry, 0, sizeof entry); + + /* first, delete directory entry */ + result = parent_dplug->rem_entry(parent, victim, &entry); + if (result == 0) { + /* + * if name was removed successfully, we _have_ to + * return 0 from this function, because upper level + * caller (vfs_{rmdir,unlink}) expect this. + */ + /* now that directory entry is removed, update + * stat-data */ + reiser4_del_nlink(object, parent, 1); + /* Upon successful completion, unlink() shall mark for + update the st_ctime and st_mtime fields of the + parent directory. Also, if the file's link count is + not 0, the st_ctime field of the file shall be + marked for update. --SUS */ + reiser4_update_dir(parent); + /* add safe-link for this file */ + if (fplug->not_linked(object)) + safe_link_add(object, SAFE_UNLINK); + } + } + + if (unlikely(result != 0)) { + if (result != -ENOMEM) + warning("nikita-3398", "Cannot unlink %llu (%i)", + get_inode_oid(object), result); + /* if operation failed commit pending inode modifications to + * the stat-data */ + reiser4_update_sd(object); + reiser4_update_sd(parent); + } + + reiser4_release_reserved(object->i_sb); + + /* @object's i_ctime was updated by ->rem_link() method(). */ + + return result; +} + +/* Estimate the maximum amount of nodes will be allocated or changed for: + - insert an in the parent entry + - update the SD of parent + - estimate child creation +*/ +static reiser4_block_nr common_estimate_create_child( + struct inode *parent, /* parent object */ + struct inode *object /* object */) +{ + assert("vpf-309", parent != NULL); + assert("vpf-307", object != NULL); + + return + /* object creation estimation */ + inode_file_plugin(object)->estimate.create(object) + + /* stat data of parent directory estimation */ + inode_file_plugin(parent)->estimate.update(parent) + + /* adding entry estimation */ + inode_dir_plugin(parent)->estimate.add_entry(parent) + + /* to undo in the case of failure */ + inode_dir_plugin(parent)->estimate.rem_entry(parent); +} + +/* Create child in directory. + + . get object's plugin + . get fresh inode + . initialize inode + . add object's stat-data + . initialize object's directory + . add entry to the parent + . instantiate dentry + +*/ +/* ->create_child method of directory plugin */ +static int +create_child_common(reiser4_object_create_data * data /* parameters + * of new + * object */, + struct inode ** retobj) +{ + int result; + + struct dentry *dentry; /* parent object */ + struct inode *parent; /* new name */ + + dir_plugin *par_dir; /* directory plugin on the parent */ + dir_plugin *obj_dir; /* directory plugin on the new object */ + file_plugin *obj_plug; /* object plugin on the new object */ + struct inode *object; /* new object */ + reiser4_block_nr reserve; + + reiser4_dir_entry_desc entry; /* new directory entry */ + + assert("nikita-1420", data != NULL); + parent = data->parent; + dentry = data->dentry; + + assert("nikita-1418", parent != NULL); + assert("nikita-1419", dentry != NULL); + par_dir = inode_dir_plugin(parent); + /* check permissions */ + result = perm_chk(parent, create, parent, dentry, data); + if (result != 0) + return result; + + /* check, that name is acceptable for parent */ + if (par_dir->is_name_acceptable && + !par_dir->is_name_acceptable(parent, + dentry->d_name.name, + (int) dentry->d_name.len)) + return RETERR(-ENAMETOOLONG); + + result = 0; + obj_plug = file_plugin_by_id((int) data->id); + if (obj_plug == NULL) { + warning("nikita-430", "Cannot find plugin %i", data->id); + return RETERR(-ENOENT); + } + object = new_inode(parent->i_sb); + if (object == NULL) + return RETERR(-ENOMEM); + /* we'll update i_nlink below */ + object->i_nlink = 0; + /* new_inode() initializes i_ino to "arbitrary" value. Reset it to 0, + * to simplify error handling: if some error occurs before i_ino is + * initialized with oid, i_ino should already be set to some + * distinguished value. */ + object->i_ino = 0; + + /* So that on error iput will be called. */ + *retobj = object; + + if (DQUOT_ALLOC_INODE(object)) { + DQUOT_DROP(object); + object->i_flags |= S_NOQUOTA; + return RETERR(-EDQUOT); + } + + xmemset(&entry, 0, sizeof entry); + entry.obj = object; + + plugin_set_file(&reiser4_inode_data(object)->pset, obj_plug); + result = obj_plug->set_plug_in_inode(object, parent, data); + if (result) { + warning("nikita-431", "Cannot install plugin %i on %llx", + data->id, get_inode_oid(object)); + DQUOT_FREE_INODE(object); + object->i_flags |= S_NOQUOTA; + return result; + } + + /* reget plugin after installation */ + obj_plug = inode_file_plugin(object); + + if (obj_plug->create == NULL) { + DQUOT_FREE_INODE(object); + object->i_flags |= S_NOQUOTA; + return RETERR(-EPERM); + } + + /* if any of hash, tail, sd or permission plugins for newly created + object are not set yet set them here inheriting them from parent + directory + */ + assert("nikita-2070", obj_plug->adjust_to_parent != NULL); + result = obj_plug->adjust_to_parent(object, + parent, + object->i_sb->s_root->d_inode); + if (result != 0) { + warning("nikita-432", "Cannot inherit from %llx to %llx", + get_inode_oid(parent), get_inode_oid(object)); + DQUOT_FREE_INODE(object); + object->i_flags |= S_NOQUOTA; + return result; + } + + /* call file plugin's method to initialize plugin specific part of + * inode */ + if (obj_plug->init_inode_data) + obj_plug->init_inode_data(object, data, 1/*create*/); + + /* obtain directory plugin (if any) for new object. */ + obj_dir = inode_dir_plugin(object); + if (obj_dir != NULL && obj_dir->init == NULL) { + DQUOT_FREE_INODE(object); + object->i_flags |= S_NOQUOTA; + return RETERR(-EPERM); + } + + reiser4_inode_data(object)->locality_id = get_inode_oid(parent); + + reserve = common_estimate_create_child(parent, object); + if (reiser4_grab_space(reserve, BA_CAN_COMMIT)) { + DQUOT_FREE_INODE(object); + object->i_flags |= S_NOQUOTA; + return RETERR(-ENOSPC); + } + + /* mark inode `immutable'. We disable changes to the file being + created until valid directory entry for it is inserted. Otherwise, + if file were expanded and insertion of directory entry fails, we + have to remove file, but we only alloted enough space in + transaction to remove _empty_ file. 3.x code used to remove stat + data in different transaction thus possibly leaking disk space on + crash. This all only matters if it's possible to access file + without name, for example, by inode number + */ + inode_set_flag(object, REISER4_IMMUTABLE); + + /* create empty object, this includes allocation of new objectid. For + directories this implies creation of dot and dotdot */ + assert("nikita-2265", inode_get_flag(object, REISER4_NO_SD)); + + /* mark inode as `loaded'. From this point onward + reiser4_delete_inode() will try to remove its stat-data. */ + inode_set_flag(object, REISER4_LOADED); + + result = obj_plug->create(object, parent, data); + if (result != 0) { + inode_clr_flag(object, REISER4_IMMUTABLE); + if (result != -ENAMETOOLONG && result != -ENOMEM) + warning("nikita-2219", + "Failed to create sd for %llu", + get_inode_oid(object)); + DQUOT_FREE_INODE(object); + object->i_flags |= S_NOQUOTA; + return result; + } + + if (obj_dir != NULL) + result = obj_dir->init(object, parent, data); + if (result == 0) { + assert("nikita-434", !inode_get_flag(object, REISER4_NO_SD)); + /* insert inode into VFS hash table */ + insert_inode_hash(object); + /* create entry */ + result = par_dir->add_entry(parent, dentry, data, &entry); + if (result == 0) { + result = reiser4_add_nlink(object, parent, 0); + /* If O_CREAT is set and the file did not previously + exist, upon successful completion, open() shall + mark for update the st_atime, st_ctime, and + st_mtime fields of the file and the st_ctime and + st_mtime fields of the parent directory. --SUS + */ + /* @object times are already updated by + reiser4_add_nlink() */ + if (result == 0) + reiser4_update_dir(parent); + if (result != 0) + /* cleanup failure to add nlink */ + par_dir->rem_entry(parent, dentry, &entry); + } + if (result != 0) + /* cleanup failure to add entry */ + obj_plug->detach(object, parent); + } else if (result != -ENOMEM) + warning("nikita-2219", "Failed to initialize dir for %llu: %i", + get_inode_oid(object), result); + + /* + * update stat-data, committing all pending modifications to the inode + * fields. + */ + reiser4_update_sd(object); + if (result != 0) { + DQUOT_FREE_INODE(object); + object->i_flags |= S_NOQUOTA; + /* if everything was ok (result == 0), parent stat-data is + * already updated above (update_parent_dir()) */ + reiser4_update_sd(parent); + /* failure to create entry, remove object */ + obj_plug->delete(object); + } + + /* file has name now, clear immutable flag */ + inode_clr_flag(object, REISER4_IMMUTABLE); + + /* on error, iput() will call ->delete_inode(). We should keep track + of the existence of stat-data for this inode and avoid attempt to + remove it in reiser4_delete_inode(). This is accomplished through + REISER4_NO_SD bit in inode.u.reiser4_i.plugin.flags + */ + return result; +} + +/* ->is_name_acceptable() method of directory plugin */ +/* Audited by: green(2002.06.15) */ +reiser4_internal int +is_name_acceptable(const struct inode *inode /* directory to check */ , + const char *name UNUSED_ARG /* name to check */ , + int len /* @name's length */ ) +{ + assert("nikita-733", inode != NULL); + assert("nikita-734", name != NULL); + assert("nikita-735", len > 0); + + return len <= reiser4_max_filename_len(inode); +} + +/* return true, iff @coord points to the valid directory item that is part of + * @inode directory. */ +static int +is_valid_dir_coord(struct inode * inode, coord_t * coord) +{ + return + item_type_by_coord(coord) == DIR_ENTRY_ITEM_TYPE && + inode_file_plugin(inode)->owns_item(inode, coord); +} + +/* true if directory is empty (only contains dot and dotdot) */ +reiser4_internal int +is_dir_empty(const struct inode *dir) +{ + assert("nikita-1976", dir != NULL); + + /* rely on our method to maintain directory i_size being equal to the + number of entries. */ + return dir->i_size <= 2 ? 0 : RETERR(-ENOTEMPTY); +} + +/* compare two logical positions within the same directory */ +reiser4_internal cmp_t dir_pos_cmp(const dir_pos * p1, const dir_pos * p2) +{ + cmp_t result; + + assert("nikita-2534", p1 != NULL); + assert("nikita-2535", p2 != NULL); + + result = de_id_cmp(&p1->dir_entry_key, &p2->dir_entry_key); + if (result == EQUAL_TO) { + int diff; + + diff = p1->pos - p2->pos; + result = (diff < 0) ? LESS_THAN : (diff ? GREATER_THAN : EQUAL_TO); + } + return result; +} + +/* true, if file descriptor @f is created by NFS server by "demand" to serve + * one file system operation. This means that there may be "detached state" + * for underlying inode. */ +static inline int +file_is_stateless(struct file *f) +{ + return reiser4_get_dentry_fsdata(f->f_dentry)->stateless; +} + +#define CID_SHIFT (20) +#define CID_MASK (0xfffffull) + +/* calculate ->fpos from user-supplied cookie. Normally it is dir->f_pos, but + * in the case of stateless directory operation (readdir-over-nfs), client id + * was encoded in the high bits of cookie and should me masked off. */ +static loff_t +get_dir_fpos(struct file * dir) +{ + if (file_is_stateless(dir)) + return dir->f_pos & CID_MASK; + else + return dir->f_pos; +} + +/* see comment before readdir_common() for overview of why "adjustment" is + * necessary. */ +static void +adjust_dir_pos(struct file * dir, + readdir_pos * readdir_spot, + const dir_pos * mod_point, + int adj) +{ + dir_pos *pos; + + /* + * new directory entry was added (adj == +1) or removed (adj == -1) at + * the @mod_point. Directory file descriptor @dir is doing readdir and + * is currently positioned at @readdir_spot. Latter has to be updated + * to maintain stable readdir. + */ + + ON_TRACE(TRACE_DIR, "adjust: %s/%i", + dir ? (char *)dir->f_dentry->d_name.name : "(anon)", adj); + ON_TRACE(TRACE_DIR, "\nf_pos: %llu, spot.fpos: %llu entry_no: %llu\n", + dir ? dir->f_pos : 0, readdir_spot->fpos, + readdir_spot->entry_no); + + reiser4_stat_inc(dir.readdir.adjust_pos); + + /* directory is positioned to the beginning. */ + if (readdir_spot->entry_no == 0) + return; + + pos = &readdir_spot->position; + switch (dir_pos_cmp(mod_point, pos)) { + case LESS_THAN: + /* @mod_pos is _before_ @readdir_spot, that is, entry was + * added/removed on the left (in key order) of current + * position. */ + /* logical number of directory entry readdir is "looking" at + * changes */ + readdir_spot->entry_no += adj; + assert("nikita-2577", + ergo(dir != NULL, get_dir_fpos(dir) + adj >= 0)); + if (de_id_cmp(&pos->dir_entry_key, + &mod_point->dir_entry_key) == EQUAL_TO) { + assert("nikita-2575", mod_point->pos < pos->pos); + /* + * if entry added/removed has the same key as current + * for readdir, update counter of duplicate keys in + * @readdir_spot. + */ + pos->pos += adj; + } + reiser4_stat_inc(dir.readdir.adjust_lt); + break; + case GREATER_THAN: + /* directory is modified after @pos: nothing to do. */ + reiser4_stat_inc(dir.readdir.adjust_gt); + break; + case EQUAL_TO: + /* cannot insert an entry readdir is looking at, because it + already exists. */ + assert("nikita-2576", adj < 0); + /* directory entry to which @pos points to is being + removed. + + NOTE-NIKITA: Right thing to do is to update @pos to point + to the next entry. This is complex (we are under spin-lock + for one thing). Just rewind it to the beginning. Next + readdir will have to scan the beginning of + directory. Proper solution is to use semaphore in + spin lock's stead and use rewind_right() here. + + NOTE-NIKITA: now, semaphore is used, so... + */ + xmemset(readdir_spot, 0, sizeof *readdir_spot); + reiser4_stat_inc(dir.readdir.adjust_eq); + } +} + +/* scan all file-descriptors for this directory and adjust their positions + respectively. */ +reiser4_internal void +adjust_dir_file(struct inode *dir, const struct dentry * de, int offset, int adj) +{ + reiser4_file_fsdata *scan; + dir_pos mod_point; + + assert("nikita-2536", dir != NULL); + assert("nikita-2538", de != NULL); + assert("nikita-2539", adj != 0); + + build_de_id(dir, &de->d_name, &mod_point.dir_entry_key); + mod_point.pos = offset; + + spin_lock_inode(dir); + + /* + * new entry was added/removed in directory @dir. Scan all file + * descriptors for @dir that are currently involved into @readdir and + * update them. + */ + + for_all_type_safe_list(readdir, get_readdir_list(dir), scan) + adjust_dir_pos(scan->back, &scan->dir.readdir, &mod_point, adj); + + spin_unlock_inode(dir); +} + +/* + * traverse tree to start/continue readdir from the readdir position @pos. + */ +static int +dir_go_to(struct file *dir, readdir_pos * pos, tap_t * tap) +{ + reiser4_key key; + int result; + struct inode *inode; + + assert("nikita-2554", pos != NULL); + + inode = dir->f_dentry->d_inode; + result = inode_dir_plugin(inode)->build_readdir_key(dir, &key); + if (result != 0) + return result; + result = object_lookup(inode, + &key, + tap->coord, + tap->lh, + tap->mode, + FIND_EXACT, + LEAF_LEVEL, + LEAF_LEVEL, + 0, + &tap->ra_info); + if (result == CBK_COORD_FOUND) + result = rewind_right(tap, (int) pos->position.pos); + else { + tap->coord->node = NULL; + done_lh(tap->lh); + result = RETERR(-EIO); + } + return result; +} + +/* + * handling of non-unique keys: calculate at what ordinal position within + * sequence of directory items with identical keys @pos is. + */ +static int +set_pos(struct inode * inode, readdir_pos * pos, tap_t * tap) +{ + int result; + coord_t coord; + lock_handle lh; + tap_t scan; + de_id *did; + reiser4_key de_key; + + coord_init_zero(&coord); + init_lh(&lh); + tap_init(&scan, &coord, &lh, ZNODE_READ_LOCK); + tap_copy(&scan, tap); + tap_load(&scan); + pos->position.pos = 0; + + did = &pos->position.dir_entry_key; + + if (is_valid_dir_coord(inode, scan.coord)) { + + build_de_id_by_key(unit_key_by_coord(scan.coord, &de_key), did); + + while (1) { + + result = go_prev_unit(&scan); + if (result != 0) + break; + + if (!is_valid_dir_coord(inode, scan.coord)) { + result = -EINVAL; + break; + } + + /* get key of directory entry */ + unit_key_by_coord(scan.coord, &de_key); + if (de_id_key_cmp(did, &de_key) != EQUAL_TO) { + /* duplicate-sequence is over */ + break; + } + pos->position.pos ++; + } + } else + result = RETERR(-ENOENT); + tap_relse(&scan); + tap_done(&scan); + return result; +} + + +/* + * "rewind" directory to @offset, i.e., set @pos and @tap correspondingly. + */ +static int +dir_rewind(struct file *dir, readdir_pos * pos, tap_t * tap) +{ + __u64 destination; + __s64 shift; + int result; + struct inode *inode; + loff_t dirpos; + + assert("nikita-2553", dir != NULL); + assert("nikita-2548", pos != NULL); + assert("nikita-2551", tap->coord != NULL); + assert("nikita-2552", tap->lh != NULL); + + dirpos = get_dir_fpos(dir); + shift = dirpos - pos->fpos; + /* this is logical directory entry within @dir which we are rewinding + * to */ + destination = pos->entry_no + shift; + + inode = dir->f_dentry->d_inode; + if (dirpos < 0) + return RETERR(-EINVAL); + else if (destination == 0ll || dirpos == 0) { + /* rewind to the beginning of directory */ + xmemset(pos, 0, sizeof *pos); + reiser4_stat_inc(dir.readdir.reset); + return dir_go_to(dir, pos, tap); + } else if (destination >= inode->i_size) + return RETERR(-ENOENT); + + if (shift < 0) { + /* I am afraid of negative numbers */ + shift = -shift; + /* rewinding to the left */ + reiser4_stat_inc(dir.readdir.rewind_left); + if (shift <= (int) pos->position.pos) { + /* destination is within sequence of entries with + duplicate keys. */ + reiser4_stat_inc(dir.readdir.left_non_uniq); + result = dir_go_to(dir, pos, tap); + } else { + shift -= pos->position.pos; + while (1) { + /* repetitions: deadlock is possible when + going to the left. */ + result = dir_go_to(dir, pos, tap); + if (result == 0) { + result = rewind_left(tap, shift); + if (result == -E_DEADLOCK) { + tap_done(tap); + reiser4_stat_inc(dir.readdir.left_restart); + continue; + } + } + break; + } + } + } else { + /* rewinding to the right */ + reiser4_stat_inc(dir.readdir.rewind_right); + result = dir_go_to(dir, pos, tap); + if (result == 0) + result = rewind_right(tap, shift); + } + if (result == 0) { + result = set_pos(inode, pos, tap); + if (result == 0) { + /* update pos->position.pos */ + pos->entry_no = destination; + pos->fpos = dirpos; + } + } + return result; +} + +/* + * Function that is called by common_readdir() on each directory entry while + * doing readdir. ->filldir callback may block, so we had to release long term + * lock while calling it. To avoid repeating tree traversal, seal is used. If + * seal is broken, we return -E_REPEAT. Node is unlocked in this case. + * + * Whether node is unlocked in case of any other error is undefined. It is + * guaranteed to be still locked if success (0) is returned. + * + * When ->filldir() wants no more, feed_entry() returns 1, and node is + * unlocked. + */ +static int +feed_entry(struct file *f, + readdir_pos * pos, tap_t *tap, filldir_t filldir, void *dirent) +{ + item_plugin *iplug; + char *name; + reiser4_key sd_key; + int result; + char buf[DE_NAME_BUF_LEN]; + char name_buf[32]; + char *local_name; + unsigned file_type; + seal_t seal; + coord_t *coord; + reiser4_key entry_key; + + coord = tap->coord; + iplug = item_plugin_by_coord(coord); + + /* pointer to name within the node */ + name = iplug->s.dir.extract_name(coord, buf); + assert("nikita-1371", name != NULL); + + /* key of object the entry points to */ + if (iplug->s.dir.extract_key(coord, &sd_key) != 0) + return RETERR(-EIO); + + /* we must release longterm znode lock before calling filldir to avoid + deadlock which may happen if filldir causes page fault. So, copy + name to intermediate buffer */ + if (strlen(name) + 1 > sizeof(name_buf)) { + local_name = kmalloc(strlen(name) + 1, GFP_KERNEL); + if (local_name == NULL) + return RETERR(-ENOMEM); + } else + local_name = name_buf; + + strcpy(local_name, name); + file_type = iplug->s.dir.extract_file_type(coord); + + unit_key_by_coord(coord, &entry_key); + seal_init(&seal, coord, &entry_key); + + longterm_unlock_znode(tap->lh); + + ON_TRACE(TRACE_DIR | TRACE_VFS_OPS, "readdir: %s, %llu, %llu, %llu\n", + name, pos->fpos, pos->entry_no, get_key_objectid(&sd_key)); + + /* + * send information about directory entry to the ->filldir() filler + * supplied to us by caller (VFS). + * + * ->filldir is entitled to do weird things. For example, ->filldir + * supplied by knfsd re-enters file system. Make sure no locks are + * held. + */ + assert("nikita-3436", lock_stack_isclean(get_current_lock_stack())); + + result = filldir(dirent, name, (int) strlen(name), + /* offset of this entry */ + f->f_pos, + /* inode number of object bounden by this entry */ + oid_to_uino(get_key_objectid(&sd_key)), + file_type); + if (local_name != name_buf) + kfree(local_name); + if (result < 0) + /* ->filldir() is satisfied. (no space in buffer, IOW) */ + result = 1; + else + result = seal_validate(&seal, coord, &entry_key, LEAF_LEVEL, + tap->lh, FIND_EXACT, + tap->mode, ZNODE_LOCK_HIPRI); + return result; +} + +static void +move_entry(readdir_pos * pos, coord_t * coord) +{ + reiser4_key de_key; + de_id *did; + + /* update @pos */ + ++pos->entry_no; + did = &pos->position.dir_entry_key; + + /* get key of directory entry */ + unit_key_by_coord(coord, &de_key); + + if (de_id_key_cmp(did, &de_key) == EQUAL_TO) + /* we are within sequence of directory entries + with duplicate keys. */ + ++pos->position.pos; + else { + pos->position.pos = 0; + build_de_id_by_key(&de_key, did); + } + ++pos->fpos; +} + +/* + * STATELESS READDIR + * + * readdir support in reiser4 relies on ability to update readdir_pos embedded + * into reiser4_file_fsdata on each directory modification (name insertion and + * removal), see readdir_common() function below. This obviously doesn't work + * when reiser4 is accessed over NFS, because NFS doesn't keep any state + * across client READDIR requests for the same directory. + * + * To address this we maintain a "pool" of detached reiser4_file_fsdata + * (d_cursor). Whenever NFS readdir request comes, we detect this, and try to + * find detached reiser4_file_fsdata corresponding to previous readdir + * request. In other words, additional state is maintained on the + * server. (This is somewhat contrary to the design goals of NFS protocol.) + * + * To efficiently detect when our ->readdir() method is called by NFS server, + * dentry is marked as "stateless" in reiser4_decode_fh() (this is checked by + * file_is_stateless() function). + * + * To find out d_cursor in the pool, we encode client id (cid) in the highest + * bits of NFS readdir cookie: when first readdir request comes to the given + * directory from the given client, cookie is set to 0. This situation is + * detected, global cid_counter is incremented, and stored in highest bits of + * all direntry offsets returned to the client, including last one. As the + * only valid readdir cookie is one obtained as direntry->offset, we are + * guaranteed that next readdir request (continuing current one) will have + * current cid in the highest bits of starting readdir cookie. All d_cursors + * are hashed into per-super-block hash table by (oid, cid) key. + * + * In addition d_cursors are placed into per-super-block radix tree where they + * are keyed by oid alone. This is necessary to efficiently remove them during + * rmdir. + * + * At last, currently unused d_cursors are linked into special list. This list + * is used d_cursor_shrink to reclaim d_cursors on memory pressure. + * + */ + +TYPE_SAFE_LIST_DECLARE(d_cursor); +TYPE_SAFE_LIST_DECLARE(a_cursor); + +typedef struct { + __u16 cid; + __u64 oid; +} d_cursor_key; + +struct dir_cursor { + int ref; + reiser4_file_fsdata *fsdata; + d_cursor_hash_link hash; + d_cursor_list_link list; + d_cursor_key key; + d_cursor_info *info; + a_cursor_list_link alist; +}; + +static kmem_cache_t *d_cursor_slab; +static struct shrinker *d_cursor_shrinker; +static unsigned long d_cursor_unused = 0; +static spinlock_t d_lock = SPIN_LOCK_UNLOCKED; +static a_cursor_list_head cursor_cache = TYPE_SAFE_LIST_HEAD_INIT(cursor_cache); + +#define D_CURSOR_TABLE_SIZE (256) + +static inline unsigned long +d_cursor_hash(d_cursor_hash_table *table, const d_cursor_key * key) +{ + assert("nikita-3555", IS_POW(D_CURSOR_TABLE_SIZE)); + return (key->oid + key->cid) & (D_CURSOR_TABLE_SIZE - 1); +} + +static inline int +d_cursor_eq(const d_cursor_key * k1, const d_cursor_key * k2) +{ + return k1->cid == k2->cid && k1->oid == k2->oid; +} + +#define KMALLOC(size) kmalloc((size), GFP_KERNEL) +#define KFREE(ptr, size) kfree(ptr) +TYPE_SAFE_HASH_DEFINE(d_cursor, + dir_cursor, + d_cursor_key, + key, + hash, + d_cursor_hash, + d_cursor_eq); +#undef KFREE +#undef KMALLOC + +TYPE_SAFE_LIST_DEFINE(d_cursor, dir_cursor, list); +TYPE_SAFE_LIST_DEFINE(a_cursor, dir_cursor, alist); + +static void kill_cursor(dir_cursor *cursor); + +/* + * shrink d_cursors cache. Scan LRU list of unused cursors, freeing requested + * number. Return number of still freeable cursors. + */ +int d_cursor_shrink(int nr, unsigned int gfp_mask) +{ + if (nr != 0) { + dir_cursor *scan; + int killed; + + killed = 0; + spin_lock(&d_lock); + while (!a_cursor_list_empty(&cursor_cache)) { + scan = a_cursor_list_front(&cursor_cache); + assert("nikita-3567", scan->ref == 0); + kill_cursor(scan); + ++ killed; + -- nr; + if (nr == 0) + break; + } + spin_unlock(&d_lock); + } + return d_cursor_unused; +} + +/* + * perform global initializations for the d_cursor sub-system. + */ +reiser4_internal int +d_cursor_init(void) +{ + d_cursor_slab = kmem_cache_create("d_cursor", sizeof (dir_cursor), 0, + SLAB_HWCACHE_ALIGN, NULL, NULL); + if (d_cursor_slab == NULL) + return RETERR(-ENOMEM); + else { + /* actually, d_cursors are "priceless", because there is no + * way to recover information stored in them. On the other + * hand, we don't want to consume all kernel memory by + * them. As a compromise, just assign higher "seeks" value to + * d_cursor cache, so that it will be shrunk only if system is + * really tight on memory. */ + d_cursor_shrinker = set_shrinker(DEFAULT_SEEKS << 3, + d_cursor_shrink); + if (d_cursor_shrinker == NULL) + return RETERR(-ENOMEM); + else + return 0; + } +} + +/* + * Dual to d_cursor_init(): release global d_cursor resources. + */ +reiser4_internal void +d_cursor_done(void) +{ + if (d_cursor_shrinker != NULL) { + remove_shrinker(d_cursor_shrinker); + d_cursor_shrinker = NULL; + } + if (d_cursor_slab != NULL) { + kmem_cache_destroy(d_cursor_slab); + d_cursor_slab = NULL; + } +} + +/* + * initialize per-super-block d_cursor resources + */ +reiser4_internal int +d_cursor_init_at(struct super_block *s) +{ + d_cursor_info *p; + + p = &get_super_private(s)->d_info; + + INIT_RADIX_TREE(&p->tree, GFP_KERNEL); + return d_cursor_hash_init(&p->table, D_CURSOR_TABLE_SIZE, NULL); +} + +/* + * Dual to d_cursor_init_at: release per-super-block d_cursor resources + */ +reiser4_internal void +d_cursor_done_at(struct super_block *s) +{ + d_cursor_hash_done(&get_super_private(s)->d_info.table); +} + +/* + * return d_cursor data for the file system @inode is in. + */ +static inline d_cursor_info * d_info(struct inode *inode) +{ + return &get_super_private(inode->i_sb)->d_info; +} + +/* + * lookup d_cursor in the per-super-block radix tree. + */ +static inline dir_cursor *lookup(d_cursor_info *info, unsigned long index) +{ + return (dir_cursor *)radix_tree_lookup(&info->tree, index); +} + +/* + * attach @cursor to the radix tree. There may be multiple cursors for the + * same oid, they are chained into circular list. + */ +static void bind_cursor(dir_cursor *cursor, unsigned long index) +{ + dir_cursor *head; + + head = lookup(cursor->info, index); + if (head == NULL) { + /* this is the first cursor for this index */ + d_cursor_list_clean(cursor); + radix_tree_insert(&cursor->info->tree, index, cursor); + } else { + /* some cursor already exists. Chain ours */ + d_cursor_list_insert_after(head, cursor); + } +} + +/* + * remove @cursor from indices and free it + */ +static void +kill_cursor(dir_cursor *cursor) +{ + unsigned long index; + + assert("nikita-3566", cursor->ref == 0); + assert("nikita-3572", cursor->fsdata != NULL); + + index = (unsigned long)cursor->key.oid; + readdir_list_remove_clean(cursor->fsdata); + reiser4_free_fsdata(cursor->fsdata); + cursor->fsdata = NULL; + + if (d_cursor_list_is_clean(cursor)) + /* this is last cursor for a file. Kill radix-tree entry */ + radix_tree_delete(&cursor->info->tree, index); + else { + void **slot; + + /* + * there are other cursors for the same oid. + */ + + /* + * if radix tree point to the cursor being removed, re-target + * radix tree slot to the next cursor in the (non-empty as was + * checked above) element of the circular list of all cursors + * for this oid. + */ + slot = radix_tree_lookup_slot(&cursor->info->tree, index); + assert("nikita-3571", *slot != NULL); + if (*slot == cursor) + *slot = d_cursor_list_next(cursor); + /* remove cursor from circular list */ + d_cursor_list_remove_clean(cursor); + } + /* remove cursor from the list of unused cursors */ + a_cursor_list_remove_clean(cursor); + /* remove cursor from the hash table */ + d_cursor_hash_remove(&cursor->info->table, cursor); + /* and free it */ + kmem_cache_free(d_cursor_slab, cursor); + -- d_cursor_unused; +} + +/* possible actions that can be performed on all cursors for the given file */ +enum cursor_action { + /* load all detached state: this is called when stat-data is loaded + * from the disk to recover information about all pending readdirs */ + CURSOR_LOAD, + /* detach all state from inode, leaving it in the cache. This is + * called when inode is removed form the memory by memory pressure */ + CURSOR_DISPOSE, + /* detach cursors from the inode, and free them. This is called when + * inode is destroyed. */ + CURSOR_KILL +}; + +static void +process_cursors(struct inode *inode, enum cursor_action act) +{ + oid_t oid; + dir_cursor *start; + readdir_list_head *head; + reiser4_context ctx; + d_cursor_info *info; + + /* this can be called by + * + * kswapd->...->prune_icache->..reiser4_destroy_inode + * + * without reiser4_context + */ + init_context(&ctx, inode->i_sb); + + assert("nikita-3558", inode != NULL); + + info = d_info(inode); + oid = get_inode_oid(inode); + spin_lock_inode(inode); + head = get_readdir_list(inode); + spin_lock(&d_lock); + /* find any cursor for this oid: reference to it is hanging of radix + * tree */ + start = lookup(info, (unsigned long)oid); + if (start != NULL) { + dir_cursor *scan; + reiser4_file_fsdata *fsdata; + + /* process circular list of cursors for this oid */ + scan = start; + do { + dir_cursor *next; + + next = d_cursor_list_next(scan); + fsdata = scan->fsdata; + assert("nikita-3557", fsdata != NULL); + if (scan->key.oid == oid) { + switch (act) { + case CURSOR_DISPOSE: + readdir_list_remove_clean(fsdata); + break; + case CURSOR_LOAD: + readdir_list_push_front(head, fsdata); + break; + case CURSOR_KILL: + kill_cursor(scan); + break; + } + } + if (scan == next) + /* last cursor was just killed */ + break; + scan = next; + } while (scan != start); + } + spin_unlock(&d_lock); + /* check that we killed 'em all */ + assert("nikita-3568", ergo(act == CURSOR_KILL, + readdir_list_empty(get_readdir_list(inode)))); + assert("nikita-3569", ergo(act == CURSOR_KILL, + lookup(info, oid) == NULL)); + spin_unlock_inode(inode); + reiser4_exit_context(&ctx); +} + +/* detach all cursors from inode. This is called when inode is removed from + * the memory by memory pressure */ +reiser4_internal void dispose_cursors(struct inode *inode) +{ + process_cursors(inode, CURSOR_DISPOSE); +} + +/* attach all detached cursors to the inode. This is done when inode is loaded + * into memory */ +reiser4_internal void load_cursors(struct inode *inode) +{ + process_cursors(inode, CURSOR_LOAD); +} + +/* free all cursors for this inode. This is called when inode is destroyed. */ +reiser4_internal void kill_cursors(struct inode *inode) +{ + process_cursors(inode, CURSOR_KILL); +} + +/* global counter used to generate "client ids". These ids are encoded into + * high bits of fpos. */ +static __u32 cid_counter = 0; + +/* + * detach fsdata (if detachable) from file descriptor, and put cursor on the + * "unused" list. Called when file descriptor is not longer in active use. + */ +static void +clean_fsdata(struct file *f) +{ + dir_cursor *cursor; + reiser4_file_fsdata *fsdata; + + assert("nikita-3570", file_is_stateless(f)); + + fsdata = (reiser4_file_fsdata *)f->private_data; + if (fsdata != NULL) { + cursor = fsdata->cursor; + if (cursor != NULL) { + spin_lock(&d_lock); + -- cursor->ref; + if (cursor->ref == 0) { + a_cursor_list_push_back(&cursor_cache, cursor); + ++ d_cursor_unused; + } + spin_unlock(&d_lock); + f->private_data = NULL; + } + } +} + +/* add detachable readdir state to the @f */ +static int +insert_cursor(dir_cursor *cursor, struct file *f, struct inode *inode) +{ + int result; + reiser4_file_fsdata *fsdata; + + xmemset(cursor, 0, sizeof *cursor); + + /* this is either first call to readdir, or rewind. Anyway, create new + * cursor. */ + fsdata = create_fsdata(NULL, GFP_KERNEL); + if (fsdata != NULL) { + result = radix_tree_preload(GFP_KERNEL); + if (result == 0) { + d_cursor_info *info; + oid_t oid; + + info = d_info(inode); + oid = get_inode_oid(inode); + /* cid occupies higher 12 bits of f->f_pos. Don't + * allow it to become negative: this confuses + * nfsd_readdir() */ + cursor->key.cid = (++ cid_counter) & 0x7ff; + cursor->key.oid = oid; + cursor->fsdata = fsdata; + cursor->info = info; + cursor->ref = 1; + spin_lock_inode(inode); + /* install cursor as @f's private_data, discarding old + * one if necessary */ + clean_fsdata(f); + reiser4_free_file_fsdata(f); + f->private_data = fsdata; + fsdata->cursor = cursor; + spin_unlock_inode(inode); + spin_lock(&d_lock); + /* insert cursor into hash table */ + d_cursor_hash_insert(&info->table, cursor); + /* and chain it into radix-tree */ + bind_cursor(cursor, (unsigned long)oid); + spin_unlock(&d_lock); + radix_tree_preload_end(); + f->f_pos = ((__u64)cursor->key.cid) << CID_SHIFT; + } + } else + result = RETERR(-ENOMEM); + return result; +} + +/* find or create cursor for readdir-over-nfs */ +static int +try_to_attach_fsdata(struct file *f, struct inode *inode) +{ + loff_t pos; + int result; + dir_cursor *cursor; + + /* + * we are serialized by inode->i_sem + */ + + if (!file_is_stateless(f)) + return 0; + + pos = f->f_pos; + result = 0; + if (pos == 0) { + /* + * first call to readdir (or rewind to the beginning of + * directory) + */ + cursor = kmem_cache_alloc(d_cursor_slab, GFP_KERNEL); + if (cursor != NULL) + result = insert_cursor(cursor, f, inode); + else + result = RETERR(-ENOMEM); + } else { + /* try to find existing cursor */ + d_cursor_key key; + + key.cid = pos >> CID_SHIFT; + key.oid = get_inode_oid(inode); + spin_lock(&d_lock); + cursor = d_cursor_hash_find(&d_info(inode)->table, &key); + if (cursor != NULL) { + /* cursor was found */ + if (cursor->ref == 0) { + /* move it from unused list */ + a_cursor_list_remove_clean(cursor); + -- d_cursor_unused; + } + ++ cursor->ref; + } + spin_unlock(&d_lock); + if (cursor != NULL) { + spin_lock_inode(inode); + assert("nikita-3556", cursor->fsdata->back == NULL); + clean_fsdata(f); + reiser4_free_file_fsdata(f); + f->private_data = cursor->fsdata; + spin_unlock_inode(inode); + } + } + return result; +} + +/* detach fsdata, if necessary */ +static void +detach_fsdata(struct file *f) +{ + struct inode *inode; + + if (!file_is_stateless(f)) + return; + + inode = f->f_dentry->d_inode; + spin_lock_inode(inode); + clean_fsdata(f); + spin_unlock_inode(inode); +} + +/* + * prepare for readdir. + */ +static int +dir_readdir_init(struct file *f, tap_t * tap, readdir_pos ** pos) +{ + struct inode *inode; + reiser4_file_fsdata *fsdata; + int result; + + assert("nikita-1359", f != NULL); + inode = f->f_dentry->d_inode; + assert("nikita-1360", inode != NULL); + + if (!S_ISDIR(inode->i_mode)) + return RETERR(-ENOTDIR); + + /* try to find detached readdir state */ + result = try_to_attach_fsdata(f, inode); + if (result != 0) + return result; + + fsdata = reiser4_get_file_fsdata(f); + assert("nikita-2571", fsdata != NULL); + if (IS_ERR(fsdata)) + return PTR_ERR(fsdata); + + /* add file descriptor to the readdir list hanging of directory + * inode. This list is used to scan "readdirs-in-progress" while + * inserting or removing names in the directory. */ + spin_lock_inode(inode); + if (readdir_list_is_clean(fsdata)) + readdir_list_push_front(get_readdir_list(inode), fsdata); + *pos = &fsdata->dir.readdir; + spin_unlock_inode(inode); + + ON_TRACE(TRACE_DIR, " fpos: %llu entry_no: %llu\n", + (*pos)->entry_no, (*pos)->fpos); + + /* move @tap to the current position */ + return dir_rewind(f, *pos, tap); +} + +/* + * ->readdir method of directory plugin + * + * readdir problems: + * + * Traditional UNIX API for scanning through directory + * (readdir/seekdir/telldir/opendir/closedir/rewindir/getdents) is based + * on the assumption that directory is structured very much like regular + * file, in particular, it is implied that each name within given + * directory (directory entry) can be uniquely identified by scalar offset + * and that such offset is stable across the life-time of the name is + * identifies. + * + * This is manifestly not so for reiser4. In reiser4 the only stable + * unique identifies for the directory entry is its key that doesn't fit + * into seekdir/telldir API. + * + * solution: + * + * Within each file descriptor participating in readdir-ing of directory + * plugin/dir/dir.h:readdir_pos is maintained. This structure keeps track + * of the "current" directory entry that file descriptor looks at. It + * contains a key of directory entry (plus some additional info to deal + * with non-unique keys that we wouldn't dwell onto here) and a logical + * position of this directory entry starting from the beginning of the + * directory, that is ordinal number of this entry in the readdir order. + * + * Obviously this logical position is not stable in the face of directory + * modifications. To work around this, on each addition or removal of + * directory entry all file descriptors for directory inode are scanned + * and their readdir_pos are updated accordingly (adjust_dir_pos()). + * + */ +static int +readdir_common(struct file *f /* directory file being read */ , + void *dirent /* opaque data passed to us by VFS */ , + filldir_t filld /* filler function passed to us + * by VFS */ ) +{ + int result; + struct inode *inode; + coord_t coord; + lock_handle lh; + tap_t tap; + readdir_pos *pos; + + assert("nikita-1359", f != NULL); + inode = f->f_dentry->d_inode; + assert("nikita-1360", inode != NULL); + + reiser4_stat_inc(dir.readdir.calls); + + if (!S_ISDIR(inode->i_mode)) + return RETERR(-ENOTDIR); + + coord_init_zero(&coord); + init_lh(&lh); + tap_init(&tap, &coord, &lh, ZNODE_READ_LOCK); + + reiser4_readdir_readahead_init(inode, &tap); + + ON_TRACE(TRACE_DIR | TRACE_VFS_OPS, + "readdir: inode: %llu offset: %#llx\n", + get_inode_oid(inode), f->f_pos); + + repeat: + result = dir_readdir_init(f, &tap, &pos); + if (result == 0) { + result = tap_load(&tap); + /* scan entries one by one feeding them to @filld */ + while (result == 0) { + coord_t *coord; + + coord = tap.coord; + assert("nikita-2572", coord_is_existing_unit(coord)); + assert("nikita-3227", is_valid_dir_coord(inode, coord)); + + result = feed_entry(f, pos, &tap, filld, dirent); + ON_TRACE(TRACE_DIR | TRACE_VFS_OPS, + "readdir: entry: offset: %#llx\n", f->f_pos); + if (result > 0) { + break; + } else if (result == 0) { + ++ f->f_pos; + result = go_next_unit(&tap); + if (result == -E_NO_NEIGHBOR || + result == -ENOENT) { + result = 0; + break; + } else if (result == 0) { + if (is_valid_dir_coord(inode, coord)) + move_entry(pos, coord); + else + break; + } + } else if (result == -E_REPEAT) { + /* feed_entry() had to restart. */ + ++ f->f_pos; + tap_relse(&tap); + goto repeat; + } else + warning("vs-1617", + "readdir_common: unexpected error %d", + result); + } + tap_relse(&tap); + + if (result >= 0) + f->f_version = inode->i_version; + } else if (result == -E_NO_NEIGHBOR || result == -ENOENT) + result = 0; + tap_done(&tap); + ON_TRACE(TRACE_DIR | TRACE_VFS_OPS, + "readdir_exit: offset: %#llx\n", f->f_pos); + detach_fsdata(f); + return (result <= 0) ? result : 0; +} + +/* + * seek method for directory. See comment before readdir_common() for + * explanation. + */ +loff_t +seek_dir(struct file *file, loff_t off, int origin) +{ + loff_t result; + struct inode *inode; + + inode = file->f_dentry->d_inode; + ON_TRACE(TRACE_DIR | TRACE_VFS_OPS, "seek_dir: %s: %lli -> %lli/%i\n", + file->f_dentry->d_name.name, file->f_pos, off, origin); + down(&inode->i_sem); + + /* update ->f_pos */ + result = default_llseek(file, off, origin); + if (result >= 0) { + int ff; + coord_t coord; + lock_handle lh; + tap_t tap; + readdir_pos *pos; + + coord_init_zero(&coord); + init_lh(&lh); + tap_init(&tap, &coord, &lh, ZNODE_READ_LOCK); + + ff = dir_readdir_init(file, &tap, &pos); + detach_fsdata(file); + if (ff != 0) + result = (loff_t) ff; + tap_done(&tap); + } + detach_fsdata(file); + up(&inode->i_sem); + return result; +} + +/* ->attach method of directory plugin */ +static int +attach_common(struct inode *child UNUSED_ARG, struct inode *parent UNUSED_ARG) +{ + assert("nikita-2647", child != NULL); + assert("nikita-2648", parent != NULL); + + return 0; +} + +/* ->estimate.add_entry method of directory plugin + estimation of adding entry which supposes that entry is inserting a unit into item +*/ +static reiser4_block_nr +estimate_add_entry_common(struct inode *inode) +{ + return estimate_one_insert_into_item(tree_by_inode(inode)); +} + +/* ->estimate.rem_entry method of directory plugin */ +static reiser4_block_nr +estimate_rem_entry_common(struct inode *inode) +{ + return estimate_one_item_removal(tree_by_inode(inode)); +} + +/* placeholder for VFS methods not-applicable to the object */ +static ssize_t +noperm(void) +{ + return RETERR(-EPERM); +} + +#define dir_eperm ((void *)noperm) + +static int +_noop(void) +{ + return 0; +} + +#define enoop ((void *)_noop) + +static int +change_dir(struct inode * inode, reiser4_plugin * plugin) +{ + /* cannot change dir plugin of already existing object */ + return RETERR(-EINVAL); +} + +static reiser4_plugin_ops dir_plugin_ops = { + .init = NULL, + .load = NULL, + .save_len = NULL, + .save = NULL, + .change = change_dir +}; + +/* + * definition of directory plugins + */ + +dir_plugin dir_plugins[LAST_DIR_ID] = { + /* standard hashed directory plugin */ + [HASHED_DIR_PLUGIN_ID] = { + .h = { + .type_id = REISER4_DIR_PLUGIN_TYPE, + .id = HASHED_DIR_PLUGIN_ID, + .pops = &dir_plugin_ops, + .label = "dir", + .desc = "hashed directory", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .get_parent = get_parent_hashed, + .lookup = lookup_hashed, + .unlink = unlink_common, + .link = link_common, + .is_name_acceptable = is_name_acceptable, + .build_entry_key = build_entry_key_common, + .build_readdir_key = build_readdir_key_common, + .add_entry = add_entry_hashed, + .rem_entry = rem_entry_hashed, + .create_child = create_child_common, + .rename = rename_hashed, + .readdir = readdir_common, + .init = init_hashed, + .done = done_hashed, + .attach = attach_common, + .detach = detach_hashed, + .estimate = { + .add_entry = estimate_add_entry_common, + .rem_entry = estimate_rem_entry_common, + .unlink = estimate_unlink_hashed + } + }, + /* hashed directory for which seekdir/telldir are guaranteed to + * work. Brain-damage. */ + [SEEKABLE_HASHED_DIR_PLUGIN_ID] = { + .h = { + .type_id = REISER4_DIR_PLUGIN_TYPE, + .id = SEEKABLE_HASHED_DIR_PLUGIN_ID, + .pops = &dir_plugin_ops, + .label = "dir32", + .desc = "directory hashed with 31 bit hash", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .get_parent = get_parent_hashed, + .lookup = lookup_hashed, + .unlink = unlink_common, + .link = link_common, + .is_name_acceptable = is_name_acceptable, + .build_entry_key = build_entry_key_stable_entry, + .build_readdir_key = build_readdir_key_common, + .add_entry = add_entry_hashed, + .rem_entry = rem_entry_hashed, + .create_child = create_child_common, + .rename = rename_hashed, + .readdir = readdir_common, + .init = init_hashed, + .done = done_hashed, + .attach = attach_common, + .detach = detach_hashed, + .estimate = { + .add_entry = estimate_add_entry_common, + .rem_entry = estimate_rem_entry_common, + .unlink = estimate_unlink_hashed + } + }, + /* pseudo directory. */ + [PSEUDO_DIR_PLUGIN_ID] = { + .h = { + .type_id = REISER4_DIR_PLUGIN_TYPE, + .id = PSEUDO_DIR_PLUGIN_ID, + .pops = &dir_plugin_ops, + .label = "pseudo", + .desc = "pseudo directory", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .get_parent = get_parent_pseudo, + .lookup = lookup_pseudo, + .unlink = dir_eperm, + .link = dir_eperm, + .is_name_acceptable = NULL, + .build_entry_key = NULL, + .build_readdir_key = NULL, + .add_entry = dir_eperm, + .rem_entry = dir_eperm, + .create_child = NULL, + .rename = dir_eperm, + .readdir = readdir_pseudo, + .init = enoop, + .done = enoop, + .attach = enoop, + .detach = enoop, + .estimate = { + .add_entry = NULL, + .rem_entry = NULL, + .unlink = NULL + } + } +}; + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/dir/dir.h 2004-09-03 00:57:05.193249768 -0700 @@ -0,0 +1,106 @@ +/* Copyright 2001, 2002, 2003, 2004 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Directory plugin's methods. See dir.c for more details. */ + +#if !defined( __REISER4_DIR_H__ ) +#define __REISER4_DIR_H__ + +#include "../../forward.h" +#include "../../kassign.h" +#include "../../type_safe_hash.h" + +#include /* for __u?? */ +#include /* for struct file */ +#include + +/* locking: fields of per file descriptor readdir_pos and ->f_pos are + * protected by ->i_sem on inode. Under this lock following invariant + * holds: + * + * file descriptor is "looking" at the entry_no-th directory entry from + * the beginning of directory. This entry has key dir_entry_key and is + * pos-th entry with duplicate-key sequence. + * + */ + +/* logical position within directory */ +typedef struct { + /* key of directory entry (actually, part of a key sufficient to + identify directory entry) */ + de_id dir_entry_key; + /* ordinal number of directory entry among all entries with the same + key. (Starting from 0.) */ + unsigned pos; +} dir_pos; + +typedef struct { + /* f_pos corresponding to this readdir position */ + __u64 fpos; + /* logical position within directory */ + dir_pos position; + /* logical number of directory entry within + directory */ + __u64 entry_no; +} readdir_pos; + +extern void adjust_dir_file(struct inode *dir, const struct dentry *de, + int offset, int adj); +extern loff_t seek_dir(struct file *file, loff_t off, int origin); + +/* description of directory entry being created/destroyed/sought for + + It is passed down to the directory plugin and farther to the + directory item plugin methods. Creation of new directory is done in + several stages: first we search for an entry with the same name, then + create new one. reiser4_dir_entry_desc is used to store some information + collected at some stage of this process and required later: key of + item that we want to insert/delete and pointer to an object that will + be bound by the new directory entry. Probably some more fields will + be added there. + +*/ +struct reiser4_dir_entry_desc { + /* key of directory entry */ + reiser4_key key; + /* object bound by this entry. */ + struct inode *obj; +}; + +int is_name_acceptable(const struct inode *inode, const char *name UNUSED_ARG, int len); +int is_dir_empty(const struct inode *dir); +int reiser4_update_dir(struct inode *dir); + +void dispose_cursors(struct inode *inode); +void load_cursors(struct inode *inode); +void kill_cursors(struct inode *inode); + +typedef struct dir_cursor dir_cursor; + +TYPE_SAFE_HASH_DECLARE(d_cursor, dir_cursor); + +int d_cursor_init_at(struct super_block *s); +void d_cursor_done_at(struct super_block *s); + +/* + * information about d_cursors (detached readdir state) maintained in reiser4 + * specific portion of reiser4 super-block. See dir.c for more information on + * d_cursors. + */ +typedef struct d_cursor_info { + d_cursor_hash_table table; + struct radix_tree_root tree; +} d_cursor_info; + +/* __REISER4_DIR_H__ */ +#endif + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/dir/hashed_dir.c 2004-09-03 00:57:05.200248704 -0700 @@ -0,0 +1,1473 @@ +/* Copyright 2001, 2002, 2003, 2004 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Directory plugin using hashes (see fs/reiser4/plugin/hash.c) to map file + names to the files. */ + +/* See fs/reiser4/doc/directory-service for initial design note. */ + +/* + * Hashed directory logically consists of persistent directory + * entries. Directory entry is a pair of a file name and a key of stat-data of + * a file that has this name in the given directory. + * + * Directory entries are stored in the tree in the form of directory + * items. Directory item should implement dir_entry_ops portion of item plugin + * interface (see plugin/item/item.h). Hashed directory interacts with + * directory item plugin exclusively through dir_entry_ops operations. + * + * Currently there are two implementations of directory items: "simple + * directory item" (plugin/item/sde.[ch]), and "compound directory item" + * (plugin/item/cde.[ch]) with the latter being the default. + * + * There is, however some delicate way through which directory code interferes + * with item plugin: key assignment policy. A key for a directory item is + * chosen by directory code, and as described in kassign.c, this key contains + * a portion of file name. Directory item uses this knowledge to avoid storing + * this portion of file name twice: in the key and in the directory item body. + * + */ + +#include "../../forward.h" +#include "../../debug.h" +#include "../../spin_macros.h" +#include "../../key.h" +#include "../../kassign.h" +#include "../../coord.h" +#include "../../seal.h" +#include "dir.h" +#include "../item/item.h" +#include "../security/perm.h" +#include "../pseudo/pseudo.h" +#include "../plugin.h" +#include "../object.h" +#include "../../jnode.h" +#include "../../znode.h" +#include "../../tree.h" +#include "../../vfs_ops.h" +#include "../../inode.h" +#include "../../reiser4.h" +#include "../../safe_link.h" + +#include /* for struct inode */ +#include /* for struct dentry */ + +static int create_dot_dotdot(struct inode *object, struct inode *parent); +static int find_entry(struct inode *dir, struct dentry *name, + lock_handle * lh, znode_lock_mode mode, + reiser4_dir_entry_desc * entry); +static int check_item(const struct inode *dir, + const coord_t * coord, const char *name); + +reiser4_internal reiser4_block_nr +hashed_estimate_init(struct inode *parent, struct inode *object) +{ + reiser4_block_nr res = 0; + + assert("vpf-321", parent != NULL); + assert("vpf-322", object != NULL); + + /* hashed_add_entry(object) */ + res += inode_dir_plugin(object)->estimate.add_entry(object); + /* reiser4_add_nlink(object) */ + res += inode_file_plugin(object)->estimate.update(object); + /* hashed_add_entry(object) */ + res += inode_dir_plugin(object)->estimate.add_entry(object); + /* reiser4_add_nlink(parent) */ + res += inode_file_plugin(parent)->estimate.update(parent); + + return 0; +} + +/* plugin->u.dir.init + create sd for directory file. Create stat-data, dot, and dotdot. */ +reiser4_internal int +init_hashed(struct inode *object /* new directory */ , + struct inode *parent /* parent directory */ , + reiser4_object_create_data * data UNUSED_ARG /* info passed + * to us, this + * is filled by + * reiser4() + * syscall in + * particular */ ) +{ + reiser4_block_nr reserve; + + assert("nikita-680", object != NULL); + assert("nikita-681", S_ISDIR(object->i_mode)); + assert("nikita-682", parent != NULL); + assert("nikita-684", data != NULL); + assert("nikita-686", data->id == DIRECTORY_FILE_PLUGIN_ID); + assert("nikita-687", object->i_mode & S_IFDIR); + trace_stamp(TRACE_DIR); + + reserve = hashed_estimate_init(parent, object); + if (reiser4_grab_space(reserve, BA_CAN_COMMIT)) + return RETERR(-ENOSPC); + + return create_dot_dotdot(object, parent); +} + +static reiser4_block_nr +hashed_estimate_done(struct inode *object) +{ + reiser4_block_nr res = 0; + + /* hashed_rem_entry(object) */ + res += inode_dir_plugin(object)->estimate.rem_entry(object); + return res; +} + +/* plugin->u.dir.estimate.unlink */ +reiser4_internal reiser4_block_nr +estimate_unlink_hashed(struct inode *parent, struct inode *object) +{ + reiser4_block_nr res = 0; + + /* hashed_rem_entry(object) */ + res += inode_dir_plugin(object)->estimate.rem_entry(object); + /* del_nlink(parent) */ + res += 2 * inode_file_plugin(parent)->estimate.update(parent); + + return res; +} + +/* ->delete() method of directory plugin + plugin->u.dir.done + Delete dot, and call common_file_delete() to delete stat data. +*/ +reiser4_internal int +done_hashed(struct inode *object /* object being deleted */) +{ + int result; + reiser4_block_nr reserve; + struct dentry goodby_dots; + reiser4_dir_entry_desc entry; + + assert("nikita-1449", object != NULL); + + if (inode_get_flag(object, REISER4_NO_SD)) + return 0; + + /* of course, this can be rewritten to sweep everything in one + cut_tree(). */ + xmemset(&entry, 0, sizeof entry); + + /* FIXME: this done method is called from delete_directory_common which + * reserved space already */ + reserve = hashed_estimate_done(object); + if (reiser4_grab_space(reserve, BA_CAN_COMMIT | BA_RESERVED)) + return RETERR(-ENOSPC); + + xmemset(&goodby_dots, 0, sizeof goodby_dots); + entry.obj = goodby_dots.d_inode = object; + goodby_dots.d_name.name = "."; + goodby_dots.d_name.len = 1; + result = rem_entry_hashed(object, &goodby_dots, &entry); + reiser4_free_dentry_fsdata(&goodby_dots); + if (unlikely(result != 0 && result != -ENOMEM && result != -ENOENT)) + /* only worth a warning + + "values of B will give rise to dom!\n" + -- v6src/s2/mv.c:89 + */ + warning("nikita-2252", "Cannot remove dot of %lli: %i", + get_inode_oid(object), result); + return 0; +} + +/* ->detach() method of directory plugin + plugin->u.dir.done + Delete dotdot, decrease nlink on parent +*/ +reiser4_internal int +detach_hashed(struct inode *object, struct inode *parent) +{ + int result; + struct dentry goodby_dots; + reiser4_dir_entry_desc entry; + + assert("nikita-2885", object != NULL); + assert("nikita-2886", !inode_get_flag(object, REISER4_NO_SD)); + + xmemset(&entry, 0, sizeof entry); + + /* NOTE-NIKITA this only works if @parent is -the- parent of + @object, viz. object whose key is stored in dotdot + entry. Wouldn't work with hard-links on directories. */ + xmemset(&goodby_dots, 0, sizeof goodby_dots); + entry.obj = goodby_dots.d_inode = parent; + goodby_dots.d_name.name = ".."; + goodby_dots.d_name.len = 2; + result = rem_entry_hashed(object, &goodby_dots, &entry); + reiser4_free_dentry_fsdata(&goodby_dots); + if (result == 0) { + /* the dot should be the only entry remaining at this time... */ + assert("nikita-3400", object->i_size == 1); + /* and, together with the only name directory can have, they + * provides for the last 2 remaining references. If we get + * here as part of error handling during mkdir, @object + * possibly has no name yet, so its nlink == 1. If we get here + * from rename (targeting empty directory), it has no name + * already, so its nlink == 1. */ + assert("nikita-3401", + object->i_nlink == 2 || object->i_nlink == 1); + + reiser4_del_nlink(parent, object, 0); + } + return result; +} + + +/* ->owns_item() for hashed directory object plugin. */ +reiser4_internal int +owns_item_hashed(const struct inode *inode /* object to check against */ , + const coord_t * coord /* coord of item to check */ ) +{ + reiser4_key item_key; + + assert("nikita-1335", inode != NULL); + assert("nikita-1334", coord != NULL); + + if (item_type_by_coord(coord) == DIR_ENTRY_ITEM_TYPE) + return get_key_locality(item_key_by_coord(coord, &item_key)) == get_inode_oid(inode); + else + return owns_item_common(inode, coord); +} + +/* helper function for directory_file_create(). Create "." and ".." */ +static int +create_dot_dotdot(struct inode *object /* object to create dot and + * dotdot for */ , + struct inode *parent /* parent of @object */ ) +{ + int result; + struct dentry dots_entry; + reiser4_dir_entry_desc entry; + + assert("nikita-688", object != NULL); + assert("nikita-689", S_ISDIR(object->i_mode)); + assert("nikita-691", parent != NULL); + trace_stamp(TRACE_DIR); + + /* We store dot and dotdot as normal directory entries. This is + not necessary, because almost all information stored in them + is already in the stat-data of directory, the only thing + being missed is objectid of grand-parent directory that can + easily be added there as extension. + + But it is done the way it is done, because not storing dot + and dotdot will lead to the following complications: + + . special case handling in ->lookup(). + . addition of another extension to the sd. + . dependency on key allocation policy for stat data. + + */ + + xmemset(&entry, 0, sizeof entry); + xmemset(&dots_entry, 0, sizeof dots_entry); + entry.obj = dots_entry.d_inode = object; + dots_entry.d_name.name = "."; + dots_entry.d_name.len = 1; + result = add_entry_hashed(object, &dots_entry, NULL, &entry); + reiser4_free_dentry_fsdata(&dots_entry); + + if (result == 0) { + result = reiser4_add_nlink(object, object, 0); + if (result == 0) { + entry.obj = dots_entry.d_inode = parent; + dots_entry.d_name.name = ".."; + dots_entry.d_name.len = 2; + result = add_entry_hashed(object, + &dots_entry, NULL, &entry); + reiser4_free_dentry_fsdata(&dots_entry); + /* if creation of ".." failed, iput() will delete + object with ".". */ + if (result == 0) { + result = reiser4_add_nlink(parent, object, 0); + if (result != 0) + /* + * if we failed to bump i_nlink, try + * to remove ".." + */ + detach_hashed(object, parent); + } + } + } + + if (result != 0) { + /* + * in the case of error, at least update stat-data so that, + * ->i_nlink updates are not lingering. + */ + reiser4_update_sd(object); + reiser4_update_sd(parent); + } + + return result; +} + +/* looks for name specified in @dentry in directory @parent and if name is + found - key of object found entry points to is stored in @entry->key */ +static int +lookup_name_hashed(struct inode *parent /* inode of directory to lookup for + * name in */, + struct dentry *dentry /* name to look for */, + reiser4_key *key /* place to store key */) +{ + int result; + coord_t *coord; + lock_handle lh; + const char *name; + int len; + reiser4_dir_entry_desc entry; + reiser4_dentry_fsdata *fsdata; + + assert("nikita-1247", parent != NULL); + assert("nikita-1248", dentry != NULL); + assert("nikita-1123", dentry->d_name.name != NULL); + assert("vs-1486", + dentry->d_op == &get_super_private(parent->i_sb)->ops.dentry); + + result = perm_chk(parent, lookup, parent, dentry); + if (result != 0) + return 0; + + name = dentry->d_name.name; + len = dentry->d_name.len; + + if (!is_name_acceptable(parent, name, len)) + /* some arbitrary error code to return */ + return RETERR(-ENAMETOOLONG); + + fsdata = reiser4_get_dentry_fsdata(dentry); + if (IS_ERR(fsdata)) + return PTR_ERR(fsdata); + + coord = &fsdata->dec.entry_coord; + coord_clear_iplug(coord); + init_lh(&lh); + + ON_TRACE(TRACE_DIR | TRACE_VFS_OPS, "lookup inode: %lli \"%s\"\n", get_inode_oid(parent), dentry->d_name.name); + + /* find entry in a directory. This is plugin method. */ + result = find_entry(parent, dentry, &lh, ZNODE_READ_LOCK, &entry); + if (result == 0) { + /* entry was found, extract object key from it. */ + result = WITH_COORD(coord, item_plugin_by_coord(coord)->s.dir.extract_key(coord, key)); + } + done_lh(&lh); + return result; + +} + +/* + * helper for ->lookup() and ->get_parent() methods: if @inode is a + * light-weight file, setup its credentials that are not stored in the + * stat-data in this case + */ +static void +check_light_weight(struct inode *inode, struct inode *parent) +{ + if (inode_get_flag(inode, REISER4_LIGHT_WEIGHT)) { + inode->i_uid = parent->i_uid; + inode->i_gid = parent->i_gid; + /* clear light-weight flag. If inode would be read by any + other name, [ug]id wouldn't change. */ + inode_clr_flag(inode, REISER4_LIGHT_WEIGHT); + } +} + +/* implementation of ->lookup() method for hashed directories. */ +reiser4_internal int +lookup_hashed(struct inode * parent /* inode of directory to + * lookup into */ , + struct dentry **dentryloc /* name to look for */ ) +{ + int result; + struct inode *inode; + struct dentry *dentry; + reiser4_dir_entry_desc entry; + + dentry = *dentryloc; + /* set up operations on dentry. */ + dentry->d_op = &get_super_private(parent->i_sb)->ops.dentry; + + result = lookup_name_hashed(parent, dentry, &entry.key); + if (result == 0) { + inode = reiser4_iget(parent->i_sb, &entry.key, 0); + if (!IS_ERR(inode)) { + check_light_weight(inode, parent); + /* success */ + *dentryloc = d_splice_alias(inode, dentry); + reiser4_iget_complete(inode); + } else + result = PTR_ERR(inode); + } else if (result == -ENOENT) + result = lookup_pseudo_file(parent, dentryloc); + + return result; +} + +/* + * ->get_parent() method of hashed directory. This is used by NFS kernel + * server to "climb" up directory tree to check permissions. + */ +reiser4_internal struct dentry * +get_parent_hashed(struct inode *child) +{ + struct super_block *s; + struct inode *parent; + struct dentry dotdot; + struct dentry *dentry; + reiser4_key key; + int result; + + /* + * lookup dotdot entry. + */ + + s = child->i_sb; + memset(&dotdot, 0, sizeof(dotdot)); + dotdot.d_name.name = ".."; + dotdot.d_name.len = 2; + dotdot.d_op = &get_super_private(s)->ops.dentry; + + result = lookup_name_hashed(child, &dotdot, &key); + if (result != 0) + return ERR_PTR(result); + + parent = reiser4_iget(s, &key, 1); + if (!IS_ERR(parent)) { + /* + * FIXME-NIKITA dubious: attributes are inherited from @child + * to @parent. But: + * + * (*) this is the only this we can do + * + * (*) attributes of light-weight object are inherited + * from a parent through which object was looked up first, + * so it is ambiguous anyway. + * + */ + check_light_weight(parent, child); + reiser4_iget_complete(parent); + dentry = d_alloc_anon(parent); + if (dentry == NULL) { + iput(parent); + dentry = ERR_PTR(RETERR(-ENOMEM)); + } else + dentry->d_op = &get_super_private(s)->ops.dentry; + } else if (PTR_ERR(parent) == -ENOENT) + dentry = ERR_PTR(RETERR(-ESTALE)); + else + dentry = (void *)parent; + return dentry; +} + +static const char *possible_leak = "Possible disk space leak."; + +/* re-bind existing name at @from_coord in @from_dir to point to @to_inode. + + Helper function called from hashed_rename() */ +static int +replace_name(struct inode *to_inode /* inode where @from_coord is + * to be re-targeted at */ , + struct inode *from_dir /* directory where @from_coord + * lives */ , + struct inode *from_inode /* inode @from_coord + * originally point to */ , + coord_t * from_coord /* where directory entry is in + * the tree */ , + lock_handle * from_lh /* lock handle on @from_coord */ ) +{ + item_plugin *from_item; + int result; + znode *node; + + coord_clear_iplug(from_coord); + node = from_coord->node; + result = zload(node); + if (result != 0) + return result; + from_item = item_plugin_by_coord(from_coord); + if (item_type_by_coord(from_coord) == DIR_ENTRY_ITEM_TYPE) { + reiser4_key to_key; + + build_sd_key(to_inode, &to_key); + + /* everything is found and prepared to change directory entry + at @from_coord to point to @to_inode. + + @to_inode is just about to get new name, so bump its link + counter. + + */ + result = reiser4_add_nlink(to_inode, from_dir, 0); + if (result != 0) { + /* Don't issue warning: this may be plain -EMLINK */ + zrelse(node); + return result; + } + + result = from_item->s.dir.update_key(from_coord, &to_key, from_lh); + if (result != 0) { + reiser4_del_nlink(to_inode, from_dir, 0); + zrelse(node); + return result; + } + + /* @from_inode just lost its name, he-he. + + If @from_inode was directory, it contained dotdot pointing + to @from_dir. @from_dir i_nlink will be decreased when + iput() will be called on @from_inode. + + If file-system is not ADG (hard-links are + supported on directories), iput(from_inode) will not remove + @from_inode, and thus above is incorrect, but hard-links on + directories are problematic in many other respects. + */ + result = reiser4_del_nlink(from_inode, from_dir, 0); + if (result != 0) { + warning("nikita-2330", + "Cannot remove link from source: %i. %s", + result, possible_leak); + } + /* Has to return success, because entry is already + * modified. */ + result = 0; + + /* NOTE-NIKITA consider calling plugin method in stead of + accessing inode fields directly. */ + from_dir->i_mtime = CURRENT_TIME; + } else { + warning("nikita-2326", "Unexpected item type"); + print_plugin("item", item_plugin_to_plugin(from_item)); + result = RETERR(-EIO); + } + zrelse(node); + return result; +} + +/* add new entry pointing to @inode into @dir at @coord, locked by @lh + + Helper function used by hashed_rename(). */ +static int +add_name(struct inode *inode /* inode where @coord is to be + * re-targeted at */ , + struct inode *dir /* directory where @coord lives */ , + struct dentry *name /* new name */ , + coord_t * coord /* where directory entry is in the tree */ , + lock_handle * lh /* lock handle on @coord */ , + int is_dir /* true, if @inode is directory */ ) +{ + int result; + reiser4_dir_entry_desc entry; + + assert("nikita-2333", lh->node == coord->node); + assert("nikita-2334", is_dir == S_ISDIR(inode->i_mode)); + + xmemset(&entry, 0, sizeof entry); + entry.obj = inode; + /* build key of directory entry description */ + inode_dir_plugin(dir)->build_entry_key(dir, &name->d_name, &entry.key); + + /* ext2 does this in different order: first inserts new entry, + then increases directory nlink. We don't want do this, + because reiser4_add_nlink() calls ->add_link() plugin + method that can fail for whatever reason, leaving as with + cleanup problems. + */ + /* @inode is getting new name */ + reiser4_add_nlink(inode, dir, 0); + /* create @new_name in @new_dir pointing to + @old_inode */ + result = WITH_COORD(coord, + inode_dir_item_plugin(dir)->s.dir.add_entry(dir, + coord, + lh, + name, + &entry)); + if (result != 0) { + int result2; + result2 = reiser4_del_nlink(inode, dir, 0); + if (result2 != 0) { + warning("nikita-2327", "Cannot drop link on %lli %i. %s", + get_inode_oid(inode), + result2, possible_leak); + } + } else + INODE_INC_FIELD(dir, i_size); + return result; +} + +static reiser4_block_nr +hashed_estimate_rename( + struct inode *old_dir /* directory where @old is located */, + struct dentry *old_name /* old name */, + struct inode *new_dir /* directory where @new is located */, + struct dentry *new_name /* new name */) +{ + reiser4_block_nr res1, res2; + dir_plugin *p_parent_old, *p_parent_new; + file_plugin *p_child_old, *p_child_new; + + assert("vpf-311", old_dir != NULL); + assert("vpf-312", new_dir != NULL); + assert("vpf-313", old_name != NULL); + assert("vpf-314", new_name != NULL); + + p_parent_old = inode_dir_plugin(old_dir); + p_parent_new = inode_dir_plugin(new_dir); + p_child_old = inode_file_plugin(old_name->d_inode); + if (new_name->d_inode) + p_child_new = inode_file_plugin(new_name->d_inode); + else + p_child_new = 0; + + /* find_entry - can insert one leaf. */ + res1 = res2 = 1; + + /* replace_name */ + { + /* reiser4_add_nlink(p_child_old) and reiser4_del_nlink(p_child_old) */ + res1 += 2 * p_child_old->estimate.update(old_name->d_inode); + /* update key */ + res1 += 1; + /* reiser4_del_nlink(p_child_new) */ + if (p_child_new) + res1 += p_child_new->estimate.update(new_name->d_inode); + } + + /* else add_name */ + { + /* reiser4_add_nlink(p_parent_new) and reiser4_del_nlink(p_parent_new) */ + res2 += 2 * inode_file_plugin(new_dir)->estimate.update(new_dir); + /* reiser4_add_nlink(p_parent_old) */ + res2 += p_child_old->estimate.update(old_name->d_inode); + /* add_entry(p_parent_new) */ + res2 += p_parent_new->estimate.add_entry(new_dir); + /* reiser4_del_nlink(p_parent_old) */ + res2 += p_child_old->estimate.update(old_name->d_inode); + } + + res1 = res1 < res2 ? res2 : res1; + + + /* reiser4_write_sd(p_parent_new) */ + res1 += inode_file_plugin(new_dir)->estimate.update(new_dir); + + /* reiser4_write_sd(p_child_new) */ + if (p_child_new) + res1 += p_child_new->estimate.update(new_name->d_inode); + + /* hashed_rem_entry(p_parent_old) */ + res1 += p_parent_old->estimate.rem_entry(old_dir); + + /* reiser4_del_nlink(p_child_old) */ + res1 += p_child_old->estimate.update(old_name->d_inode); + + /* replace_name */ + { + /* reiser4_add_nlink(p_parent_dir_new) */ + res1 += inode_file_plugin(new_dir)->estimate.update(new_dir); + /* update_key */ + res1 += 1; + /* reiser4_del_nlink(p_parent_new) */ + res1 += inode_file_plugin(new_dir)->estimate.update(new_dir); + /* reiser4_del_nlink(p_parent_old) */ + res1 += inode_file_plugin(old_dir)->estimate.update(old_dir); + } + + /* reiser4_write_sd(p_parent_old) */ + res1 += inode_file_plugin(old_dir)->estimate.update(old_dir); + + /* reiser4_write_sd(p_child_old) */ + res1 += p_child_old->estimate.update(old_name->d_inode); + + return res1; +} + +static int +hashed_rename_estimate_and_grab( + struct inode *old_dir /* directory where @old is located */ , + struct dentry *old_name /* old name */ , + struct inode *new_dir /* directory where @new is located */ , + struct dentry *new_name /* new name */ ) +{ + reiser4_block_nr reserve; + + reserve = hashed_estimate_rename(old_dir, old_name, new_dir, new_name); + + if (reiser4_grab_space(reserve, BA_CAN_COMMIT)) + return RETERR(-ENOSPC); + + return 0; +} + +/* check whether @old_inode and @new_inode can be moved within file system + * tree. This singles out attempts to rename pseudo-files, for example. */ +static int +can_rename(struct inode *old_dir, struct inode *old_inode, + struct inode *new_dir, struct inode *new_inode) +{ + file_plugin *fplug; + dir_plugin *dplug; + + assert("nikita-3370", old_inode != NULL); + + dplug = inode_dir_plugin(new_dir); + fplug = inode_file_plugin(old_inode); + + if (dplug == NULL) + return RETERR(-ENOTDIR); + else if (dplug->create_child == NULL) + return RETERR(-EPERM); + else if (!fplug->can_add_link(old_inode)) + return RETERR(-EMLINK); + else if (new_inode != NULL) { + fplug = inode_file_plugin(new_inode); + if (fplug->can_rem_link != NULL && + !fplug->can_rem_link(new_inode)) + return RETERR(-EBUSY); + } + return 0; +} + +/* ->rename directory plugin method implementation for hashed directories. + plugin->u.dir.rename + See comments in the body. + + It is arguable that this function can be made generic so, that it will be + applicable to any kind of directory plugin that deals with directories + composed out of directory entries. The only obstacle here is that we don't + have any data-type to represent directory entry. This should be + re-considered when more than one different directory plugin will be + implemented. +*/ +reiser4_internal int +rename_hashed(struct inode *old_dir /* directory where @old is located */ , + struct dentry *old_name /* old name */ , + struct inode *new_dir /* directory where @new is located */ , + struct dentry *new_name /* new name */ ) +{ + /* From `The Open Group Base Specifications Issue 6' + + + If either the old or new argument names a symbolic link, rename() + shall operate on the symbolic link itself, and shall not resolve + the last component of the argument. If the old argument and the new + argument resolve to the same existing file, rename() shall return + successfully and perform no other action. + + [this is done by VFS: vfs_rename()] + + + If the old argument points to the pathname of a file that is not a + directory, the new argument shall not point to the pathname of a + directory. + + [checked by VFS: vfs_rename->may_delete()] + + If the link named by the new argument exists, it shall + be removed and old renamed to new. In this case, a link named new + shall remain visible to other processes throughout the renaming + operation and refer either to the file referred to by new or old + before the operation began. + + [we should assure this] + + Write access permission is required for + both the directory containing old and the directory containing new. + + [checked by VFS: vfs_rename->may_delete(), may_create()] + + If the old argument points to the pathname of a directory, the new + argument shall not point to the pathname of a file that is not a + directory. + + [checked by VFS: vfs_rename->may_delete()] + + If the directory named by the new argument exists, it + shall be removed and old renamed to new. In this case, a link named + new shall exist throughout the renaming operation and shall refer + either to the directory referred to by new or old before the + operation began. + + [we should assure this] + + If new names an existing directory, it shall be + required to be an empty directory. + + [we should check this] + + If the old argument points to a pathname of a symbolic link, the + symbolic link shall be renamed. If the new argument points to a + pathname of a symbolic link, the symbolic link shall be removed. + + The new pathname shall not contain a path prefix that names + old. Write access permission is required for the directory + containing old and the directory containing new. If the old + argument points to the pathname of a directory, write access + permission may be required for the directory named by old, and, if + it exists, the directory named by new. + + [checked by VFS: vfs_rename(), vfs_rename_dir()] + + If the link named by the new argument exists and the file's link + count becomes 0 when it is removed and no process has the file + open, the space occupied by the file shall be freed and the file + shall no longer be accessible. If one or more processes have the + file open when the last link is removed, the link shall be removed + before rename() returns, but the removal of the file contents shall + be postponed until all references to the file are closed. + + [iput() handles this, but we can do this manually, a la + reiser4_unlink()] + + Upon successful completion, rename() shall mark for update the + st_ctime and st_mtime fields of the parent directory of each file. + + [N/A] + + */ + + int result; + int is_dir; /* is @old_name directory */ + + struct inode *old_inode; + struct inode *new_inode; + + reiser4_dir_entry_desc old_entry; + reiser4_dir_entry_desc new_entry; + + coord_t *new_coord; + + reiser4_dentry_fsdata *new_fsdata; + + lock_handle new_lh; + + dir_plugin *dplug; + file_plugin *fplug; + + assert("nikita-2318", old_dir != NULL); + assert("nikita-2319", new_dir != NULL); + assert("nikita-2320", old_name != NULL); + assert("nikita-2321", new_name != NULL); + + old_inode = old_name->d_inode; + new_inode = new_name->d_inode; + + dplug = inode_dir_plugin(old_dir); + fplug = NULL; + + new_fsdata = reiser4_get_dentry_fsdata(new_name); + if (IS_ERR(new_fsdata)) + return PTR_ERR(new_fsdata); + + new_coord = &new_fsdata->dec.entry_coord; + coord_clear_iplug(new_coord); + + is_dir = S_ISDIR(old_inode->i_mode); + + assert("nikita-3461", old_inode->i_nlink >= 1 + !!is_dir); + + /* if target is existing directory and it's not empty---return error. + + This check is done specifically, because is_dir_empty() requires + tree traversal and have to be done before locks are taken. + */ + if (is_dir && new_inode != NULL && is_dir_empty(new_inode) != 0) + return RETERR(-ENOTEMPTY); + + result = can_rename(old_dir, old_inode, new_dir, new_inode); + if (result != 0) + return result; + + result = hashed_rename_estimate_and_grab(old_dir, old_name, + new_dir, new_name); + if (result != 0) + return result; + + init_lh(&new_lh); + + /* find entry for @new_name */ + result = find_entry(new_dir, + new_name, &new_lh, ZNODE_WRITE_LOCK, &new_entry); + + if (IS_CBKERR(result)) { + done_lh(&new_lh); + return result; + } + + seal_done(&new_fsdata->dec.entry_seal); + + /* add or replace name for @old_inode as @new_name */ + if (new_inode != NULL) { + /* target (@new_name) exists. */ + /* Not clear what to do with objects that are + both directories and files at the same time. */ + if (result == CBK_COORD_FOUND) { + result = replace_name(old_inode, + new_dir, + new_inode, + new_coord, + &new_lh); + if (result == 0) + fplug = inode_file_plugin(new_inode); + } else if (result == CBK_COORD_NOTFOUND) { + /* VFS told us that @new_name is bound to existing + inode, but we failed to find directory entry. */ + warning("nikita-2324", "Target not found"); + result = RETERR(-ENOENT); + } + } else { + /* target (@new_name) doesn't exists. */ + if (result == CBK_COORD_NOTFOUND) + result = add_name(old_inode, + new_dir, + new_name, + new_coord, + &new_lh, is_dir); + else if (result == CBK_COORD_FOUND) { + /* VFS told us that @new_name is "negative" dentry, + but we found directory entry. */ + warning("nikita-2331", "Target found unexpectedly"); + result = RETERR(-EIO); + } + } + + assert("nikita-3462", ergo(result == 0, + old_inode->i_nlink >= 2 + !!is_dir)); + + /* We are done with all modifications to the @new_dir, release lock on + node. */ + done_lh(&new_lh); + + if (fplug != NULL) { + /* detach @new_inode from name-space */ + result = fplug->detach(new_inode, new_dir); + if (result != 0) + warning("nikita-2330", "Cannot detach %lli: %i. %s", + get_inode_oid(new_inode), result, possible_leak); + } + + if (new_inode != NULL) + reiser4_mark_inode_dirty(new_inode); + + if (result == 0) { + xmemset(&old_entry, 0, sizeof old_entry); + old_entry.obj = old_inode; + + dplug->build_entry_key(old_dir, + &old_name->d_name, &old_entry.key); + + /* At this stage new name was introduced for + @old_inode. @old_inode, @new_dir, and @new_inode i_nlink + counters were updated. + + We want to remove @old_name now. If @old_inode wasn't + directory this is simple. + */ + result = rem_entry_hashed(old_dir, old_name, &old_entry); + if (result != 0 && result != -ENOMEM) { + warning("nikita-2335", + "Cannot remove old name: %i", result); + } else { + result = reiser4_del_nlink(old_inode, old_dir, 0); + if (result != 0 && result != -ENOMEM) { + warning("nikita-2337", + "Cannot drop link on old: %i", result); + } + } + + if (result == 0 && is_dir) { + /* @old_inode is directory. We also have to update + dotdot entry. */ + coord_t *dotdot_coord; + lock_handle dotdot_lh; + struct dentry dotdot_name; + reiser4_dir_entry_desc dotdot_entry; + reiser4_dentry_fsdata dataonstack; + reiser4_dentry_fsdata *fsdata; + + xmemset(&dataonstack, 0, sizeof dataonstack); + xmemset(&dotdot_entry, 0, sizeof dotdot_entry); + dotdot_entry.obj = old_dir; + xmemset(&dotdot_name, 0, sizeof dotdot_name); + dotdot_name.d_name.name = ".."; + dotdot_name.d_name.len = 2; + /* + * allocate ->d_fsdata on the stack to avoid using + * reiser4_get_dentry_fsdata(). Locking is not needed, + * because dentry is private to the current thread. + */ + dotdot_name.d_fsdata = &dataonstack; + init_lh(&dotdot_lh); + + fsdata = &dataonstack; + dotdot_coord = &fsdata->dec.entry_coord; + coord_clear_iplug(dotdot_coord); + + result = find_entry(old_inode, &dotdot_name, &dotdot_lh, + ZNODE_WRITE_LOCK, &dotdot_entry); + if (result == 0) { + /* replace_name() decreases i_nlink on + * @old_dir */ + result = replace_name(new_dir, + old_inode, + old_dir, + dotdot_coord, + &dotdot_lh); + } else + result = RETERR(-EIO); + done_lh(&dotdot_lh); + } + } + reiser4_update_dir(new_dir); + reiser4_update_dir(old_dir); + reiser4_mark_inode_dirty(old_inode); + if (result == 0) { + file_plugin *fplug; + + if (new_inode != NULL) { + /* add safe-link for target file (in case we removed + * last reference to the poor fellow */ + fplug = inode_file_plugin(new_inode); + if (fplug->not_linked(new_inode)) + result = safe_link_add(new_inode, SAFE_UNLINK); + } + } + return result; +} + +/* ->add_entry() method for hashed directory object plugin. + plugin->u.dir.add_entry +*/ +reiser4_internal int +add_entry_hashed(struct inode *object /* directory to add new name + * in */ , + struct dentry *where /* new name */ , + reiser4_object_create_data * data UNUSED_ARG /* parameters + * of new + * object */ , + reiser4_dir_entry_desc * entry /* parameters of new + * directory entry */ ) +{ + int result; + coord_t *coord; + lock_handle lh; + reiser4_dentry_fsdata *fsdata; + reiser4_block_nr reserve; + + assert("nikita-1114", object != NULL); + assert("nikita-1250", where != NULL); + + fsdata = reiser4_get_dentry_fsdata(where); + if (unlikely(IS_ERR(fsdata))) + return PTR_ERR(fsdata); + + reserve = inode_dir_plugin(object)->estimate.add_entry(object); + if (reiser4_grab_space(reserve, BA_CAN_COMMIT)) + return RETERR(-ENOSPC); + + init_lh(&lh); + ON_TRACE(TRACE_DIR, "[%i]: creating \"%s\" in %llu\n", current->pid, where->d_name.name, get_inode_oid(object)); + coord = &fsdata->dec.entry_coord; + coord_clear_iplug(coord); + + /* check for this entry in a directory. This is plugin method. */ + result = find_entry(object, where, &lh, ZNODE_WRITE_LOCK, entry); + if (likely(result == -ENOENT)) { + /* add new entry. Just pass control to the directory + item plugin. */ + assert("nikita-1709", inode_dir_item_plugin(object)); + assert("nikita-2230", coord->node == lh.node); + seal_done(&fsdata->dec.entry_seal); + result = inode_dir_item_plugin(object)->s.dir.add_entry(object, coord, &lh, where, entry); + if (result == 0) { + adjust_dir_file(object, where, fsdata->dec.pos + 1, +1); + INODE_INC_FIELD(object, i_size); + } + } else if (result == 0) { + assert("nikita-2232", coord->node == lh.node); + result = RETERR(-EEXIST); + } + done_lh(&lh); + + return result; +} + +/* ->rem_entry() method for hashed directory object plugin. + plugin->u.dir.rem_entry + */ +reiser4_internal int +rem_entry_hashed(struct inode *object /* directory from which entry + * is begin removed */ , + struct dentry *where /* name that is being + * removed */ , + reiser4_dir_entry_desc * entry /* description of entry being + * removed */ ) +{ + int result; + coord_t *coord; + lock_handle lh; + reiser4_dentry_fsdata *fsdata; + __u64 tograb; + + /* yes, nested function, so what? Sue me. */ + int rem_entry(void) { + item_plugin *iplug; + struct inode *child; + + iplug = inode_dir_item_plugin(object); + child = where->d_inode; + assert("nikita-3399", child != NULL); + + /* check that we are really destroying an entry for @child */ + if (REISER4_DEBUG) { + int result; + reiser4_key key; + + result = iplug->s.dir.extract_key(coord, &key); + if (result != 0) + return result; + if (get_key_objectid(&key) != get_inode_oid(child)) { + warning("nikita-3397", + "rem_entry: %#llx != %#llx\n", + get_key_objectid(&key), + get_inode_oid(child)); + return RETERR(-EIO); + } + } + return iplug->s.dir.rem_entry(object, + &where->d_name, coord, &lh, entry); + } + + assert("nikita-1124", object != NULL); + assert("nikita-1125", where != NULL); + + tograb = inode_dir_plugin(object)->estimate.rem_entry(object); + result = reiser4_grab_space(tograb, BA_CAN_COMMIT | BA_RESERVED); + if (result != 0) + return RETERR(-ENOSPC); + + init_lh(&lh); + + /* check for this entry in a directory. This is plugin method. */ + result = find_entry(object, where, &lh, ZNODE_WRITE_LOCK, entry); + fsdata = reiser4_get_dentry_fsdata(where); + if (IS_ERR(fsdata)) + return PTR_ERR(fsdata); + + coord = &fsdata->dec.entry_coord; + + assert("nikita-3404", + get_inode_oid(where->d_inode) != get_inode_oid(object) || + object->i_size <= 1); + + coord_clear_iplug(coord); + if (result == 0) { + /* remove entry. Just pass control to the directory item + plugin. */ + assert("vs-542", inode_dir_item_plugin(object)); + seal_done(&fsdata->dec.entry_seal); + adjust_dir_file(object, where, fsdata->dec.pos, -1); + result = WITH_COORD(coord, rem_entry()); + if (result == 0) { + if (object->i_size >= 1) + INODE_DEC_FIELD(object, i_size); + else { + warning("nikita-2509", "Dir %llu is runt", + get_inode_oid(object)); + result = RETERR(-EIO); + } + write_current_logf(WRITE_TREE_LOG, + "..de k %#llx %#llx %i %lli", + get_inode_oid(where->d_inode), + get_inode_oid(object), + where->d_inode->i_nlink, + where->d_inode->i_size); + assert("nikita-3405", where->d_inode->i_nlink != 1 || + where->d_inode->i_size != 2 || + inode_dir_plugin(where->d_inode) == NULL); + } + } + done_lh(&lh); + + return result; +} + +static int entry_actor(reiser4_tree * tree /* tree being scanned */ , + coord_t * coord /* current coord */ , + lock_handle * lh /* current lock handle */ , + void *args /* argument to scan */ ); + +/* + * argument package used by entry_actor to scan entries with identical keys. + */ +typedef struct entry_actor_args { + /* name we are looking for */ + const char *name; + /* key of directory entry. entry_actor() scans through sequence of + * items/units having the same key */ + reiser4_key *key; + /* how many entries with duplicate key was scanned so far. */ + int non_uniq; +#if REISER4_USE_COLLISION_LIMIT || REISER4_STATS + /* scan limit */ + int max_non_uniq; +#endif + /* return parameter: set to true, if ->name wasn't found */ + int not_found; + /* what type of lock to take when moving to the next node during + * scan */ + znode_lock_mode mode; + + /* last coord that was visited during scan */ + coord_t last_coord; + /* last node locked during scan */ + lock_handle last_lh; + /* inode of directory */ + const struct inode *inode; +} entry_actor_args; + +static int +check_entry(const struct inode *dir, coord_t *coord, const struct qstr *name) +{ + return WITH_COORD(coord, check_item(dir, coord, name->name)); +} + +/* Look for given @name within directory @dir. + + This is called during lookup, creation and removal of directory + entries. + + First calculate key that directory entry for @name would have. Search + for this key in the tree. If such key is found, scan all items with + the same key, checking name in each directory entry along the way. +*/ +static int +find_entry(struct inode *dir /* directory to scan */, + struct dentry *de /* name to search for */, + lock_handle * lh /* resulting lock handle */, + znode_lock_mode mode /* required lock mode */, + reiser4_dir_entry_desc * entry /* parameters of found directory + * entry */) +{ + const struct qstr *name; + seal_t *seal; + coord_t *coord; + int result; + __u32 flags; + de_location *dec; + reiser4_dentry_fsdata *fsdata; + + assert("nikita-1130", lh != NULL); + assert("nikita-1128", dir != NULL); + + name = &de->d_name; + assert("nikita-1129", name != NULL); + + /* dentry private data don't require lock, because dentry + manipulations are protected by i_sem on parent. + + This is not so for inodes, because there is no -the- parent in + inode case. + */ + fsdata = reiser4_get_dentry_fsdata(de); + if (IS_ERR(fsdata)) + return PTR_ERR(fsdata); + dec = &fsdata->dec; + + coord = &dec->entry_coord; + coord_clear_iplug(coord); + seal = &dec->entry_seal; + /* compose key of directory entry for @name */ + inode_dir_plugin(dir)->build_entry_key(dir, name, &entry->key); + + if (seal_is_set(seal)) { + /* check seal */ + result = seal_validate(seal, coord, &entry->key, LEAF_LEVEL, + lh, FIND_EXACT, mode, ZNODE_LOCK_LOPRI); + if (result == 0) { + /* key was found. Check that it is really item we are + looking for. */ + result = check_entry(dir, coord, name); + if (result == 0) + return 0; + } + } + flags = (mode == ZNODE_WRITE_LOCK) ? CBK_FOR_INSERT : 0; + /* + * find place in the tree where directory item should be located. + */ + result = object_lookup(dir, + &entry->key, + coord, + lh, + mode, + FIND_EXACT, + LEAF_LEVEL, + LEAF_LEVEL, + flags, + 0/*ra_info*/); + + if (result == CBK_COORD_FOUND) { + entry_actor_args arg; + + /* fast path: no hash collisions */ + result = check_entry(dir, coord, name); + if (result == 0) { + seal_init(seal, coord, &entry->key); + dec->pos = 0; + } else if (result > 0) { + /* Iterate through all units with the same keys. */ + arg.name = name->name; + arg.key = &entry->key; + arg.not_found = 0; + arg.non_uniq = 0; +#if REISER4_USE_COLLISION_LIMIT + arg.max_non_uniq = max_hash_collisions(dir); + assert("nikita-2851", arg.max_non_uniq > 1); +#endif + arg.mode = mode; + arg.inode = dir; + coord_init_zero(&arg.last_coord); + init_lh(&arg.last_lh); + + result = iterate_tree(tree_by_inode(dir), coord, lh, + entry_actor, &arg, mode, 1); + /* if end of the tree or extent was reached during + scanning. */ + if (arg.not_found || (result == -E_NO_NEIGHBOR)) { + /* step back */ + done_lh(lh); + + result = zload(arg.last_coord.node); + if (result == 0) { + coord_clear_iplug(&arg.last_coord); + coord_dup(coord, &arg.last_coord); + move_lh(lh, &arg.last_lh); + result = RETERR(-ENOENT); + zrelse(arg.last_coord.node); + --arg.non_uniq; + } + } + + done_lh(&arg.last_lh); + if (result == 0) + seal_init(seal, coord, &entry->key); + + if (result == 0 || result == -ENOENT) { + assert("nikita-2580", arg.non_uniq > 0); + dec->pos = arg.non_uniq - 1; + } + } + } else + dec->pos = -1; + return result; +} + +/* Function called by find_entry() to look for given name in the directory. */ +static int +entry_actor(reiser4_tree * tree UNUSED_ARG /* tree being scanned */ , + coord_t * coord /* current coord */ , + lock_handle * lh /* current lock handle */ , + void *entry_actor_arg /* argument to scan */ ) +{ + reiser4_key unit_key; + entry_actor_args *args; + + assert("nikita-1131", tree != NULL); + assert("nikita-1132", coord != NULL); + assert("nikita-1133", entry_actor_arg != NULL); + + args = entry_actor_arg; + ++args->non_uniq; +#if REISER4_USE_COLLISION_LIMIT + if (args->non_uniq > args->max_non_uniq) { + args->not_found = 1; + /* hash collision overflow. */ + return RETERR(-EBUSY); + } +#endif + + /* + * did we just reach the end of the sequence of items/units with + * identical keys? + */ + if (!keyeq(args->key, unit_key_by_coord(coord, &unit_key))) { + assert("nikita-1791", keylt(args->key, unit_key_by_coord(coord, &unit_key))); + args->not_found = 1; + args->last_coord.between = AFTER_UNIT; + return 0; + } + + coord_dup(&args->last_coord, coord); + /* + * did scan just moved to the next node? + */ + if (args->last_lh.node != lh->node) { + int lock_result; + + /* + * if so, lock new node with the mode requested by the caller + */ + done_lh(&args->last_lh); + assert("nikita-1896", znode_is_any_locked(lh->node)); + lock_result = longterm_lock_znode(&args->last_lh, lh->node, + args->mode, ZNODE_LOCK_HIPRI); + if (lock_result != 0) + return lock_result; + } + return check_item(args->inode, coord, args->name); +} + +/* + * return 0 iff @coord contains a directory entry for the file with the name + * @name. + */ +static int +check_item(const struct inode *dir, const coord_t * coord, const char *name) +{ + item_plugin *iplug; + char buf[DE_NAME_BUF_LEN]; + + iplug = item_plugin_by_coord(coord); + if (iplug == NULL) { + warning("nikita-1135", "Cannot get item plugin"); + print_coord("coord", coord, 1); + return RETERR(-EIO); + } else if (item_id_by_coord(coord) != item_id_by_plugin(inode_dir_item_plugin(dir))) { + /* item id of current item does not match to id of items a + directory is built of */ + warning("nikita-1136", "Wrong item plugin"); + print_coord("coord", coord, 1); + print_plugin("plugin", item_plugin_to_plugin(iplug)); + return RETERR(-EIO); + } + assert("nikita-1137", iplug->s.dir.extract_name); + + ON_TRACE(TRACE_DIR, "[%i]: check_item: \"%s\", \"%s\" in %lli (%lli)\n", + current->pid, name, iplug->s.dir.extract_name(coord, buf), + get_inode_oid(dir), *znode_get_block(coord->node)); + /* Compare name stored in this entry with name we are looking for. + + NOTE-NIKITA Here should go code for support of something like + unicode, code tables, etc. + */ + return !!strcmp(name, iplug->s.dir.extract_name(coord, buf)); +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/dir/hashed_dir.h 2004-09-03 00:57:05.200248704 -0700 @@ -0,0 +1,46 @@ +/* Copyright 2001, 2002, 2003, 2004 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Directory plugin using hashes (see fs/reiser4/plugin/hash.c) to map + file names to to files. See hashed_dir.c */ + +#if !defined( __HASHED_DIR_H__ ) +#define __HASHED_DIR_H__ + +#include "../../forward.h" + +#include /* for struct inode */ +#include /* for struct dentry */ + +/* create sd for directory file. Create stat-data, dot, and dotdot. */ +extern int init_hashed(struct inode *object, struct inode *parent, reiser4_object_create_data *); +extern int done_hashed(struct inode *object); +extern int detach_hashed(struct inode *object, struct inode *parent); +extern int owns_item_hashed(const struct inode *inode, const coord_t * coord); +extern int lookup_hashed(struct inode *inode, struct dentry **dentry); +extern int rename_hashed(struct inode *old_dir, + struct dentry *old_name, struct inode *new_dir, struct dentry *new_name); +extern int add_entry_hashed(struct inode *object, + struct dentry *where, reiser4_object_create_data *, reiser4_dir_entry_desc * entry); +extern int rem_entry_hashed(struct inode *object, struct dentry *where, reiser4_dir_entry_desc * entry); +extern reiser4_block_nr estimate_rename_hashed(struct inode *old_dir, + struct dentry *old_name, + struct inode *new_dir, + struct dentry *new_name); +extern reiser4_block_nr estimate_unlink_hashed(struct inode *parent, + struct inode *object); + +extern struct dentry *get_parent_hashed(struct inode *child); + +/* __HASHED_DIR_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/dir/pseudo_dir.c 2004-09-03 00:57:05.201248552 -0700 @@ -0,0 +1,97 @@ +/* Copyright 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Directory plugin for pseudo files that operate like a directory. */ + +#include "../../debug.h" +#include "../../inode.h" +#include "../pseudo/pseudo.h" +#include "dir.h" + +#include /* for struct inode */ +#include /* for struct dentry */ + +/* implementation of ->lookup() method for pseudo files. */ +reiser4_internal int lookup_pseudo(struct inode * parent, struct dentry **dentry) +{ + pseudo_plugin *pplug; + int result; + + /* + * call ->lookup method of pseudo plugin + */ + + pplug = reiser4_inode_data(parent)->file_plugin_data.pseudo_info.plugin; + assert("nikita-3222", pplug->lookup != NULL); + result = pplug->lookup(parent, dentry); + if (result == -ENOENT) + result = lookup_pseudo_file(parent, dentry); + return result; +} + + +/* ->readdir() method for pseudo file acting like a directory */ +reiser4_internal int +readdir_pseudo(struct file *f, void *dirent, filldir_t filld) +{ + pseudo_plugin *pplug; + struct inode *inode; + struct dentry *dentry; + int result = 0; + + dentry = f->f_dentry; + inode = dentry->d_inode; + pplug = reiser4_inode_data(inode)->file_plugin_data.pseudo_info.plugin; + if (pplug->readdir != NULL) + /* + * if pseudo plugin defines ->readdir() method---call it to do + * actual work. + */ + result = pplug->readdir(f, dirent, filld); + else { + ino_t ino; + int i; + + /* + * if there is no ->readdir() method in the pseudo plugin, + * make sure that at least dot and dotdot are returned to keep + * user-level happy. + */ + + i = f->f_pos; + switch (i) { + case 0: + ino = get_inode_oid(dentry->d_inode); + if (filld(dirent, ".", 1, i, ino, DT_DIR) < 0) + break; + f->f_pos++; + i++; + /* fallthrough */ + case 1: + ino = parent_ino(dentry); + if (filld(dirent, "..", 2, i, ino, DT_DIR) < 0) + break; + f->f_pos++; + i++; + /* fallthrough */ + } + } + return result; +} + +/* pseudo files are not serializable (currently). So, this should just return an + * error. */ +reiser4_internal struct dentry * +get_parent_pseudo(struct inode *child) +{ + return ERR_PTR(RETERR(-ENOTSUPP)); +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/dir/pseudo_dir.h 2004-09-03 00:57:05.201248552 -0700 @@ -0,0 +1,29 @@ +/* Copyright 2003, 2004 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Directory plugin for pseudo files. See pseudo_dir.c for details. */ + +#if !defined( __PSEUDO_DIR_H__ ) +#define __PSEUDO_DIR_H__ + +#include "../../forward.h" + +#include /* for struct inode */ +#include /* for struct dentry */ + +extern int lookup_pseudo(struct inode * parent, struct dentry **dentry); +extern int readdir_pseudo(struct file *f, void *dirent, filldir_t filld); +extern struct dentry *get_parent_pseudo(struct inode *child); + +/* __PSEUDO_DIR_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/disk_format/disk_format40.c 2004-09-03 00:57:05.204248096 -0700 @@ -0,0 +1,549 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#include "../../debug.h" +#include "../../dformat.h" +#include "../../key.h" +#include "../node/node.h" +#include "../space/space_allocator.h" +#include "disk_format40.h" +#include "../plugin.h" +#include "../../txnmgr.h" +#include "../../jnode.h" +#include "../../tree.h" +#include "../../super.h" +#include "../../wander.h" +#include "../../diskmap.h" +#include "../../inode.h" +#include "../../ktxnmgrd.h" +#include "../../status_flags.h" + +#include /* for __u?? */ +#include /* for struct super_block */ +#include + +/* reiser 4.0 default disk layout */ + +/* functions to access fields of format40_disk_super_block */ +static __u64 +get_format40_block_count(const format40_disk_super_block * sb) +{ + return d64tocpu(&sb->block_count); +} + +static __u64 +get_format40_free_blocks(const format40_disk_super_block * sb) +{ + return d64tocpu(&sb->free_blocks); +} + +static __u64 +get_format40_root_block(const format40_disk_super_block * sb) +{ + return d64tocpu(&sb->root_block); +} + +static __u16 +get_format40_tree_height(const format40_disk_super_block * sb) +{ + return d16tocpu(&sb->tree_height); +} + +static __u64 +get_format40_file_count(const format40_disk_super_block * sb) +{ + return d64tocpu(&sb->file_count); +} + +static __u64 +get_format40_oid(const format40_disk_super_block * sb) +{ + return d64tocpu(&sb->oid); +} + +static __u32 +get_format40_mkfs_id(const format40_disk_super_block * sb) +{ + return d32tocpu(&sb->mkfs_id); +} + +static __u64 +get_format40_flags(const format40_disk_super_block * sb) +{ + return d64tocpu(&sb->flags); +} + +static format40_super_info * +get_sb_info(struct super_block *super) +{ + return &get_super_private(super)->u.format40; +} + +static int +consult_diskmap(struct super_block *s) +{ + format40_super_info *info; + journal_location *jloc; + + info = get_sb_info(s); + jloc = &get_super_private(s)->jloc; + /* Default format-specific locations, if there is nothing in + * diskmap */ + jloc->footer = FORMAT40_JOURNAL_FOOTER_BLOCKNR; + jloc->header = FORMAT40_JOURNAL_HEADER_BLOCKNR; + info->loc.super = FORMAT40_OFFSET / s->s_blocksize; +#ifdef CONFIG_REISER4_BADBLOCKS + reiser4_get_diskmap_value(FORMAT40_PLUGIN_DISKMAP_ID, FORMAT40_JF, + &jloc->footer); + reiser4_get_diskmap_value(FORMAT40_PLUGIN_DISKMAP_ID, FORMAT40_JH, + &jloc->header); + reiser4_get_diskmap_value(FORMAT40_PLUGIN_DISKMAP_ID, FORMAT40_SUPER, + &info->loc.super); +#endif + return 0; +} + +/* find any valid super block of disk_format40 (even if the first + super block is destroyed), will change block numbers of actual journal header/footer (jf/jh) + if needed */ +static struct buffer_head * +find_a_disk_format40_super_block(struct super_block *s) +{ + struct buffer_head *super_bh; + format40_disk_super_block *disk_sb; + format40_super_info *info; + + assert("umka-487", s != NULL); + + info = get_sb_info(s); + + super_bh = sb_bread(s, info->loc.super); + if (super_bh == NULL) + return ERR_PTR(RETERR(-EIO)); + + disk_sb = (format40_disk_super_block *) super_bh->b_data; + if (strncmp(disk_sb->magic, FORMAT40_MAGIC, sizeof(FORMAT40_MAGIC))) { + brelse(super_bh); + return ERR_PTR(RETERR(-EINVAL)); + } + + reiser4_set_block_count(s, d64tocpu(&disk_sb->block_count)); + reiser4_set_data_blocks(s, d64tocpu(&disk_sb->block_count) - + d64tocpu(&disk_sb->free_blocks)); + reiser4_set_free_blocks(s, (d64tocpu(&disk_sb->free_blocks))); + + return super_bh; +} + +/* find the most recent version of super block. This is called after journal is + replayed */ +static struct buffer_head * +read_super_block(struct super_block *s UNUSED_ARG) +{ + /* Here the most recent superblock copy has to be read. However, as + journal replay isn't complete, we are using + find_a_disk_format40_super_block() function. */ + return find_a_disk_format40_super_block(s); +} + +static int +get_super_jnode(struct super_block *s) +{ + reiser4_super_info_data *sbinfo = get_super_private(s); + jnode *sb_jnode; + int ret; + + sb_jnode = alloc_io_head(&get_sb_info(s)->loc.super); + + ret = jload(sb_jnode); + + if (ret) { + drop_io_head(sb_jnode); + return ret; + } + + pin_jnode_data(sb_jnode); + jrelse(sb_jnode); + + sbinfo->u.format40.sb_jnode = sb_jnode; + + return 0; +} + +static void +done_super_jnode(struct super_block *s) +{ + jnode *sb_jnode = get_super_private(s)->u.format40.sb_jnode; + + if (sb_jnode) { + unpin_jnode_data(sb_jnode); + drop_io_head(sb_jnode); + } +} + +typedef enum format40_init_stage { + NONE_DONE = 0, + CONSULT_DISKMAP, + FIND_A_SUPER, + INIT_JOURNAL_INFO, + INIT_EFLUSH, + INIT_STATUS, + JOURNAL_REPLAY, + READ_SUPER, + KEY_CHECK, + INIT_OID, + INIT_TREE, + JOURNAL_RECOVER, + INIT_SA, + INIT_JNODE, + ALL_DONE +} format40_init_stage; + +static int +try_init_format40(struct super_block *s, format40_init_stage *stage) +{ + int result; + struct buffer_head *super_bh; + reiser4_super_info_data *sbinfo; + format40_disk_super_block sb; + /* FIXME-NIKITA ugly work-around: keep copy of on-disk super-block */ + format40_disk_super_block *sb_copy = &sb; + tree_level height; + reiser4_block_nr root_block; + node_plugin *nplug; + + cassert(sizeof sb == 512); + + assert("vs-475", s != NULL); + assert("vs-474", get_super_private(s)); + + /* initialize reiser4_super_info_data */ + sbinfo = get_super_private(s); + + *stage = NONE_DONE; + + result = consult_diskmap(s); + if (result) + return result; + *stage = CONSULT_DISKMAP; + + super_bh = find_a_disk_format40_super_block(s); + if (IS_ERR(super_bh)) + return PTR_ERR(super_bh); + brelse(super_bh); + *stage = FIND_A_SUPER; + + /* map jnodes for journal control blocks (header, footer) to disk */ + result = init_journal_info(s); + if (result) + return result; + *stage = INIT_JOURNAL_INFO; + + result = eflush_init_at(s); + if (result) + return result; + *stage = INIT_EFLUSH; + + /* ok, we are sure that filesystem format is a format40 format */ + /* Now check it's state */ + result = reiser4_status_init(FORMAT40_STATUS_BLOCKNR); + if (result != 0 && result != -EINVAL) + /* -EINVAL means there is no magic, so probably just old + * fs. */ + return result; + *stage = INIT_STATUS; + + result = reiser4_status_query(NULL, NULL); + if (result == REISER4_STATUS_MOUNT_WARN) + printk("Warning, mounting filesystem with errors\n"); + if (result == REISER4_STATUS_MOUNT_RO) { + printk("Warning, mounting filesystem with fatal errors, forcing read-only mount\n"); + /* FIXME: here we should actually enforce read-only mount, + * only it is unsupported yet. */ + } + + result = reiser4_journal_replay(s); + if (result) + return result; + *stage = JOURNAL_REPLAY; + + super_bh = read_super_block(s); + if (IS_ERR(super_bh)) + return PTR_ERR(super_bh); + *stage = READ_SUPER; + + xmemcpy(sb_copy, ((format40_disk_super_block *) super_bh->b_data), sizeof (*sb_copy)); + brelse(super_bh); + + if (!equi(REISER4_LARGE_KEY, + get_format40_flags(sb_copy) & (1 << FORMAT40_LARGE_KEYS))) { + warning("nikita-3228", "Key format mismatch. " + "Only %s keys are supported.", + REISER4_LARGE_KEY ? "large" : "small"); + return RETERR(-EINVAL); + } + *stage = KEY_CHECK; + + result = oid_init_allocator(s, get_format40_file_count(sb_copy), get_format40_oid(sb_copy)); + if (result) + return result; + *stage = INIT_OID; + + /* get things necessary to init reiser4_tree */ + root_block = get_format40_root_block(sb_copy); + height = get_format40_tree_height(sb_copy); + nplug = node_plugin_by_id(NODE40_ID); + + sbinfo->tree.super = s; + /* init reiser4_tree for the filesystem */ + result = init_tree(&sbinfo->tree, &root_block, height, nplug); + if (result) + return result; + *stage = INIT_TREE; + + /* initialize reiser4_super_info_data */ + sbinfo->default_uid = 0; + sbinfo->default_gid = 0; + + reiser4_set_mkfs_id(s, get_format40_mkfs_id(sb_copy)); + reiser4_set_block_count(s, get_format40_block_count(sb_copy)); + reiser4_set_free_blocks(s, get_format40_free_blocks(sb_copy)); + + sbinfo->fsuid = 0; + sbinfo->fs_flags |= (1 << REISER4_ADG); /* hard links for directories + * are not supported */ + sbinfo->fs_flags |= (1 << REISER4_ONE_NODE_PLUGIN); /* all nodes in + * layout 40 are + * of one + * plugin */ + /* sbinfo->tmgr is initialized already */ + + /* recover sb data which were logged separately from sb block */ + + /* NOTE-NIKITA: reiser4_journal_recover_sb_data() calls + * oid_init_allocator() and reiser4_set_free_blocks() with new + * data. What's the reason to call them above? */ + result = reiser4_journal_recover_sb_data(s); + if (result != 0) + return result; + *stage = JOURNAL_RECOVER; + + /* Set number of used blocks. The number of used blocks is not stored + neither in on-disk super block nor in the journal footer blocks. At + this moment actual values of total blocks and free block counters are + set in the reiser4 super block (in-memory structure) and we can + calculate number of used blocks from them. */ + reiser4_set_data_blocks(s, + reiser4_block_count(s) - reiser4_free_blocks(s)); + +#if REISER4_DEBUG + sbinfo->min_blocks_used = + 16 /* reserved area */ + + 2 /* super blocks */ + + 2 /* journal footer and header */; +#endif + + /* init disk space allocator */ + result = sa_init_allocator(get_space_allocator(s), s, 0); + if (result) + return result; + *stage = INIT_SA; + + result = get_super_jnode(s); + if (result == 0) + *stage = ALL_DONE; + return result; +} + +/* plugin->u.format.get_ready */ +reiser4_internal int +get_ready_format40(struct super_block *s, void *data UNUSED_ARG) +{ + int result; + format40_init_stage stage; + + result = try_init_format40(s, &stage); + switch (stage) { + case ALL_DONE: + assert("nikita-3458", result == 0); + break; + case INIT_JNODE: + done_super_jnode(s); + case INIT_SA: + sa_destroy_allocator(get_space_allocator(s), s); + case JOURNAL_RECOVER: + case INIT_TREE: + done_tree(&get_super_private(s)->tree); + case INIT_OID: + case KEY_CHECK: + case READ_SUPER: + case JOURNAL_REPLAY: + case INIT_STATUS: + reiser4_status_finish(); + case INIT_EFLUSH: + eflush_done_at(s); + case INIT_JOURNAL_INFO: + done_journal_info(s); + case FIND_A_SUPER: + case CONSULT_DISKMAP: + case NONE_DONE: + break; + default: + impossible("nikita-3457", "init stage: %i", stage); + } + return result; +} + +static void +pack_format40_super(const struct super_block *s, char *data) +{ + format40_disk_super_block *super_data = (format40_disk_super_block *) data; + reiser4_super_info_data *sbinfo = get_super_private(s); + + assert("zam-591", data != NULL); + + cputod64(reiser4_free_committed_blocks(s), &super_data->free_blocks); + cputod64(sbinfo->tree.root_block, &super_data->root_block); + + cputod64(oid_next(s), &super_data->oid); + cputod64(oids_used(s), &super_data->file_count); + + cputod16(sbinfo->tree.height, &super_data->tree_height); +} + +/* plugin->u.format.log_super + return a jnode which should be added to transaction when the super block + gets logged */ +reiser4_internal jnode * +log_super_format40(struct super_block *s) +{ + jnode *sb_jnode; + + sb_jnode = get_super_private(s)->u.format40.sb_jnode; + + jload(sb_jnode); + + pack_format40_super(s, jdata(sb_jnode)); + + jrelse(sb_jnode); + + return sb_jnode; +} + +/* plugin->u.format.release */ +reiser4_internal int +release_format40(struct super_block *s) +{ + int ret; + reiser4_super_info_data *sbinfo; + + sbinfo = get_super_private(s); + assert("zam-579", sbinfo != NULL); + + /* FIXME-UMKA: Should we tell block transaction manager to commit all if + * we will have no space left? */ + if (reiser4_grab_space(1, BA_RESERVED)) + return RETERR(-ENOSPC); + + if (!rofs_super(s)) { + ret = capture_super_block(s); + if (ret != 0) + warning("vs-898", "capture_super_block failed: %d", ret); + + ret = txnmgr_force_commit_all(s, 1); + if (ret != 0) + warning("jmacd-74438", "txn_force failed: %d", ret); + } + if (reiser4_is_debugged(s, REISER4_STATS_ON_UMOUNT)) + print_fs_info("umount ok", s); + + /*done_tree(&sbinfo->tree);*/ + + sa_destroy_allocator(&sbinfo->space_allocator, s); + done_journal_info(s); + eflush_done_at(s); + done_super_jnode(s); + + return 0; +} + +#define FORMAT40_ROOT_LOCALITY 41 +#define FORMAT40_ROOT_OBJECTID 42 + +/* plugin->u.format.root_dir_key */ +reiser4_internal const reiser4_key * +root_dir_key_format40(const struct super_block *super UNUSED_ARG) +{ + static const reiser4_key FORMAT40_ROOT_DIR_KEY = { + .el = {{(FORMAT40_ROOT_LOCALITY << 4) | KEY_SD_MINOR}, +#if REISER4_LARGE_KEY + {0ull}, +#endif + {FORMAT40_ROOT_OBJECTID}, {0ull}} + }; + + return &FORMAT40_ROOT_DIR_KEY; +} + +/* plugin->u.format.print_info */ +reiser4_internal void +print_info_format40(const struct super_block *s) +{ +#if 0 + format40_disk_super_block *sb_copy; + + sb_copy = &get_super_private(s)->u.format40.actual_sb; + + printk("\tblock count %llu\n" + "\tfree blocks %llu\n" + "\troot_block %llu\n" + "\ttail policy %s\n" + "\tmin free oid %llu\n" + "\tfile count %llu\n" + "\ttree height %d\n", + get_format40_block_count(sb_copy), + get_format40_free_blocks(sb_copy), + get_format40_root_block(sb_copy), + formatting_plugin_by_id(get_format40_formatting_policy(sb_copy))->h.label, + get_format40_oid(sb_copy), get_format40_file_count(sb_copy), get_format40_tree_height(sb_copy)); +#endif +} + +/* plugin->u.format.check_open. + Check the opened object for validness. For now it checks for the valid oid & + locality only, can be improved later and it its work may depend on the mount + options. */ +reiser4_internal int +check_open_format40(const struct inode *object) { + oid_t max, oid; + + max = oid_next(object->i_sb) - 1; + + /* Check the oid. */ + oid = get_inode_oid(object); + if (oid > max) { + warning("vpf-1360", "The object with the oid %llu greater then the " + "max used oid %llu found.", oid, max); + return RETERR(-EIO); + } + + /* Check the locality. */ + oid = reiser4_inode_data(object)->locality_id; + if (oid > max) { + warning("vpf-1360", "The object with the locality %llu greater then the " + "max used oid %llu found.", oid, max); + return RETERR(-EIO); + } + + return 0; +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/disk_format/disk_format40.h 2004-09-03 00:57:05.204248096 -0700 @@ -0,0 +1,100 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* this file contains: + - definition of ondisk super block of standart disk layout for + reiser 4.0 (layout 40) + - definition of layout 40 specific portion of in-core super block + - declarations of functions implementing methods of layout plugin + for layout 40 + - declarations of functions used to get/set fields in layout 40 super block +*/ + +#ifndef __DISK_FORMAT40_H__ +#define __DISK_FORMAT40_H__ + +/* magic for default reiser4 layout */ +#define FORMAT40_MAGIC "ReIsEr40FoRmAt" +#define FORMAT40_OFFSET (REISER4_MASTER_OFFSET + PAGE_CACHE_SIZE) + +#include "../../dformat.h" + +#include /* for struct super_block */ + +typedef enum { + FORMAT40_LARGE_KEYS +} format40_flags; + +/* ondisk super block for format 40. It is 512 bytes long */ +typedef struct format40_disk_super_block { + /* 0 */ d64 block_count; + /* number of block in a filesystem */ + /* 8 */ d64 free_blocks; + /* number of free blocks */ + /* 16 */ d64 root_block; + /* filesystem tree root block */ + /* 24 */ d64 oid; + /* smallest free objectid */ + /* 32 */ d64 file_count; + /* number of files in a filesystem */ + /* 40 */ d64 flushes; + /* number of times super block was + flushed. Needed if format 40 + will have few super blocks */ + /* 48 */ d32 mkfs_id; + /* unique identifier of fs */ + /* 52 */ char magic[16]; + /* magic string ReIsEr40FoRmAt */ + /* 68 */ d16 tree_height; + /* height of filesystem tree */ + /* 70 */ d16 formatting_policy; + /* 72 */ d64 flags; + /* 72 */ char not_used[432]; +} format40_disk_super_block; + +/* format 40 specific part of reiser4_super_info_data */ +typedef struct format40_super_info { +/* format40_disk_super_block actual_sb; */ + jnode *sb_jnode; + struct { + reiser4_block_nr super; + } loc; +} format40_super_info; + +/* Defines for journal header and footer respectively. */ +#define FORMAT40_JOURNAL_HEADER_BLOCKNR \ + ((REISER4_MASTER_OFFSET / PAGE_CACHE_SIZE) + 3) + +#define FORMAT40_JOURNAL_FOOTER_BLOCKNR \ + ((REISER4_MASTER_OFFSET / PAGE_CACHE_SIZE) + 4) + +#define FORMAT40_STATUS_BLOCKNR \ + ((REISER4_MASTER_OFFSET / PAGE_CACHE_SIZE) + 5) + +/* Diskmap declarations */ +#define FORMAT40_PLUGIN_DISKMAP_ID ((REISER4_FORMAT_PLUGIN_TYPE<<16) | (FORMAT40_ID)) +#define FORMAT40_SUPER 1 +#define FORMAT40_JH 2 +#define FORMAT40_JF 3 + +/* declarations of functions implementing methods of layout plugin for + format 40. The functions theirself are in disk_format40.c */ +int get_ready_format40(struct super_block *, void *data); +const reiser4_key *root_dir_key_format40(const struct super_block *); +int release_format40(struct super_block *s); +jnode *log_super_format40(struct super_block *s); +void print_info_format40(const struct super_block *s); +int check_open_format40(const struct inode *object); + +/* __DISK_FORMAT40_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/disk_format/disk_format.c 2004-09-03 00:57:05.205247944 -0700 @@ -0,0 +1,38 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#include "../../debug.h" +#include "../plugin_header.h" +#include "disk_format40.h" +#include "disk_format.h" +#include "../plugin.h" + +/* initialization of disk layout plugins */ +disk_format_plugin format_plugins[LAST_FORMAT_ID] = { + [FORMAT40_ID] = { + .h = { + .type_id = REISER4_FORMAT_PLUGIN_TYPE, + .id = FORMAT40_ID, + .pops = NULL, + .label = "reiser40", + .desc = "standard disk layout for reiser40", + .linkage = TYPE_SAFE_LIST_LINK_ZERO, + }, + .get_ready = get_ready_format40, + .root_dir_key = root_dir_key_format40, + .release = release_format40, + .log_super = log_super_format40, + .print_info = print_info_format40, + .check_open = check_open_format40 + } +}; + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/disk_format/disk_format.h 2004-09-03 00:57:05.205247944 -0700 @@ -0,0 +1,41 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* identifiers for disk layouts, they are also used as indexes in array of disk + plugins */ + +#if !defined( __REISER4_DISK_FORMAT_H__ ) +#define __REISER4_DISK_FORMAT_H__ + +typedef enum { + /* standard reiser4 disk layout plugin id */ + FORMAT40_ID, + LAST_FORMAT_ID +} disk_format_id; + +/* __REISER4_DISK_FORMAT_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ + + + + + + + + + + + + + + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/fibration.c 2004-09-03 00:57:05.206247792 -0700 @@ -0,0 +1,173 @@ +/* Copyright 2004 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Directory fibrations */ + +/* + * Suppose we have a directory tree with sources of some project. During + * compilation .o files are created within this tree. This makes access + * to the original source files less efficient, because source files are + * now "diluted" by object files: default directory plugin uses prefix + * of a file name as a part of the key for directory entry (and this + * part is also inherited by the key of file body). This means that + * foo.o will be located close to foo.c and foo.h in the tree. + * + * To avoid this effect directory plugin fill highest 7 (unused + * originally) bits of the second component of the directory entry key + * by bit-pattern depending on the file name (see + * fs/reiser4/kassign.c:build_entry_key_common()). These bits are called + * "fibre". Fibre of the file name key is inherited by key of stat data + * and keys of file body (in the case of REISER4_LARGE_KEY). + * + * Fibre for a given file is chosen by per-directory fibration + * plugin. Names within given fibre are ordered lexicographically. + */ + +#include "../debug.h" +#include "plugin_header.h" +#include "plugin.h" +#include "../super.h" +#include "../inode.h" + +#include + +static const int fibre_shift = 57; + +#define FIBRE_NO(n) (((__u64)(n)) << fibre_shift) + +/* + * Trivial fibration: all files of directory are just ordered + * lexicographically. + */ +static __u64 fibre_trivial(const struct inode *dir, const char *name, int len) +{ + return FIBRE_NO(0); +} + +/* + * dot-o fibration: place .o files after all others. + */ +static __u64 fibre_dot_o(const struct inode *dir, const char *name, int len) +{ + /* special treatment for .*\.o */ + if (len > 2 && name[len - 1] == 'o' && name[len - 2] == '.') + return FIBRE_NO(1); + else + return FIBRE_NO(0); +} + +/* + * ext.1 fibration: subdivide directory into 128 fibrations one for each + * 7bit extension character (file "foo.h" goes into fibre "h"), plus + * default fibre for the rest. + */ +static __u64 fibre_ext_1(const struct inode *dir, const char *name, int len) +{ + if (len > 2 && name[len - 2] == '.') + return FIBRE_NO(name[len - 1]); + else + return FIBRE_NO(0); +} + +/* + * ext.3 fibration: try to separate files with different 3-character + * extensions from each other. + */ +static __u64 fibre_ext_3(const struct inode *dir, const char *name, int len) +{ + if (len > 4 && name[len - 4] == '.') + return FIBRE_NO(name[len - 3] + name[len - 2] + name[len - 1]); + else + return FIBRE_NO(0); +} + +static int +change_fibration(struct inode * inode, reiser4_plugin * plugin) +{ + int result; + + assert("nikita-3503", inode != NULL); + assert("nikita-3504", plugin != NULL); + + assert("nikita-3505", is_reiser4_inode(inode)); + assert("nikita-3506", inode_dir_plugin(inode) != NULL); + assert("nikita-3507", plugin->h.type_id == REISER4_FIBRATION_PLUGIN_TYPE); + + result = 0; + if (inode_fibration_plugin(inode) == NULL || + inode_fibration_plugin(inode)->h.id != plugin->h.id) { + if (is_dir_empty(inode) == 0) + result = plugin_set_fibration(&reiser4_inode_data(inode)->pset, + &plugin->fibration); + else + result = RETERR(-ENOTEMPTY); + + } + return result; +} + +static reiser4_plugin_ops fibration_plugin_ops = { + .init = NULL, + .load = NULL, + .save_len = NULL, + .save = NULL, + .change = change_fibration +}; + +/* fibration plugins */ +fibration_plugin fibration_plugins[LAST_FIBRATION_ID] = { + [FIBRATION_LEXICOGRAPHIC] = { + .h = { + .type_id = REISER4_FIBRATION_PLUGIN_TYPE, + .id = FIBRATION_LEXICOGRAPHIC, + .pops = &fibration_plugin_ops, + .label = "lexicographic", + .desc = "no fibration", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .fibre = fibre_trivial + }, + [FIBRATION_DOT_O] = { + .h = { + .type_id = REISER4_FIBRATION_PLUGIN_TYPE, + .id = FIBRATION_DOT_O, + .pops = &fibration_plugin_ops, + .label = "dot-o", + .desc = "fibrate .o files separately", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .fibre = fibre_dot_o + }, + [FIBRATION_EXT_1] = { + .h = { + .type_id = REISER4_FIBRATION_PLUGIN_TYPE, + .id = FIBRATION_EXT_1, + .pops = &fibration_plugin_ops, + .label = "ext-1", + .desc = "fibrate file by single character extension", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .fibre = fibre_ext_1 + }, + [FIBRATION_EXT_3] = { + .h = { + .type_id = REISER4_FIBRATION_PLUGIN_TYPE, + .id = FIBRATION_EXT_3, + .pops = &fibration_plugin_ops, + .label = "ext-3", + .desc = "fibrate file by three character extension", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .fibre = fibre_ext_3 + } +}; + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/fibration.h 2004-09-03 00:57:05.206247792 -0700 @@ -0,0 +1,37 @@ +/* Copyright 2004 by Hans Reiser, licensing governed by reiser4/README */ + +/* Fibration plugin used by hashed directory plugin to segment content + * of directory. See fs/reiser4/plugin/fibration.c for more on this. */ + +#if !defined( __FS_REISER4_PLUGIN_FIBRATION_H__ ) +#define __FS_REISER4_PLUGIN_FIBRATION_H__ + +#include "plugin_header.h" + +typedef struct fibration_plugin { + /* generic fields */ + plugin_header h; + + __u64 (*fibre)(const struct inode *dir, const char *name, int len); +} fibration_plugin; + +typedef enum { + FIBRATION_LEXICOGRAPHIC, + FIBRATION_DOT_O, + FIBRATION_EXT_1, + FIBRATION_EXT_3, + LAST_FIBRATION_ID +} reiser4_fibration_id; + +/* __FS_REISER4_PLUGIN_FIBRATION_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/file/file.c 2004-09-03 00:57:05.217246120 -0700 @@ -0,0 +1,2531 @@ +/* Copyright 2001, 2002, 2003, 2004 by Hans Reiser, licensing governed by + * reiser4/README */ + +#include "../../inode.h" +#include "../../super.h" +#include "../../tree_walk.h" +#include "../../carry.h" +#include "../../page_cache.h" +#include "../../ioctl.h" +#include "../object.h" +#include "../../prof.h" +#include "../../safe_link.h" +#include "funcs.h" + +#include +#include + +/* this file contains file plugin methods of reiser4 unix files. + + Those files are either built of tail items only (FORMATTING_ID) or of extent + items only (EXTENT_POINTER_ID) or empty (have no items but stat data) */ + +static int unpack(struct inode *inode, int forever); + +/* get unix file plugin specific portion of inode */ +reiser4_internal unix_file_info_t * +unix_file_inode_data(const struct inode * inode) +{ + return &reiser4_inode_data(inode)->file_plugin_data.unix_file_info; +} + +static int +file_is_built_of_tails(const struct inode *inode) +{ + return unix_file_inode_data(inode)->container == UF_CONTAINER_TAILS; +} + +reiser4_internal int +file_is_built_of_extents(const struct inode *inode) +{ + return unix_file_inode_data(inode)->container == UF_CONTAINER_EXTENTS; +} + +reiser4_internal int +file_is_empty(const struct inode *inode) +{ + return unix_file_inode_data(inode)->container == UF_CONTAINER_EMPTY; +} + +reiser4_internal int +file_state_is_unknown(const struct inode *inode) +{ + return unix_file_inode_data(inode)->container == UF_CONTAINER_UNKNOWN; +} + +reiser4_internal void +set_file_state_extents(struct inode *inode) +{ + unix_file_inode_data(inode)->container = UF_CONTAINER_EXTENTS; +} + +reiser4_internal void +set_file_state_tails(struct inode *inode) +{ + unix_file_inode_data(inode)->container = UF_CONTAINER_TAILS; +} + +static void +set_file_state_empty(struct inode *inode) +{ + unix_file_inode_data(inode)->container = UF_CONTAINER_EMPTY; +} + +static void +set_file_state_unknown(struct inode *inode) +{ + unix_file_inode_data(inode)->container = UF_CONTAINER_UNKNOWN; +} +static int +less_than_ldk(znode *node, const reiser4_key *key) +{ + return UNDER_RW(dk, current_tree, read, keylt(key, znode_get_ld_key(node))); +} + +reiser4_internal int +equal_to_rdk(znode *node, const reiser4_key *key) +{ + return UNDER_RW(dk, current_tree, read, keyeq(key, znode_get_rd_key(node))); +} + +#if REISER4_DEBUG + +static int +less_than_rdk(znode *node, const reiser4_key *key) +{ + return UNDER_RW(dk, current_tree, read, keylt(key, znode_get_rd_key(node))); +} + +int +equal_to_ldk(znode *node, const reiser4_key *key) +{ + return UNDER_RW(dk, current_tree, read, keyeq(key, znode_get_ld_key(node))); +} + +/* get key of item next to one @coord is set to */ +static reiser4_key * +get_next_item_key(const coord_t *coord, reiser4_key *next_key) +{ + if (coord->item_pos == node_num_items(coord->node) - 1) { + /* get key of next item if it is in right neighbor */ + UNDER_RW_VOID(dk, znode_get_tree(coord->node), read, + *next_key = *znode_get_rd_key(coord->node)); + } else { + /* get key of next item if it is in the same node */ + coord_t next; + + coord_dup_nocheck(&next, coord); + next.unit_pos = 0; + check_me("vs-730", coord_next_item(&next) == 0); + item_key_by_coord(&next, next_key); + } + return next_key; +} + +static int +item_of_that_file(const coord_t *coord, const reiser4_key *key) +{ + reiser4_key max_possible; + item_plugin *iplug; + + iplug = item_plugin_by_coord(coord); + assert("vs-1011", iplug->b.max_key_inside); + return keylt(key, iplug->b.max_key_inside(coord, &max_possible)); +} + +static int +check_coord(const coord_t *coord, const reiser4_key *key) +{ + coord_t twin; + + if (!REISER4_DEBUG) + return 1; + node_plugin_by_node(coord->node)->lookup(coord->node, key, FIND_MAX_NOT_MORE_THAN, &twin); + return coords_equal(coord, &twin); +} + +#endif /* REISER4_DEBUG */ + +void init_uf_coord(uf_coord_t *uf_coord, lock_handle *lh) +{ + coord_init_zero(&uf_coord->base_coord); + coord_clear_iplug(&uf_coord->base_coord); + uf_coord->lh = lh; + init_lh(lh); + memset(&uf_coord->extension, 0, sizeof(uf_coord->extension)); + uf_coord->valid = 0; +} + +static inline void +invalidate_extended_coord(uf_coord_t *uf_coord) +{ + coord_clear_iplug(&uf_coord->base_coord); + uf_coord->valid = 0; +} + +static inline void +validate_extended_coord(uf_coord_t *uf_coord, loff_t offset) +{ + assert("vs-1333", uf_coord->valid == 0); + assert("vs-1348", item_plugin_by_coord(&uf_coord->base_coord)->s.file.init_coord_extension); + + /* FIXME: */ + item_body_by_coord(&uf_coord->base_coord); + item_plugin_by_coord(&uf_coord->base_coord)->s.file.init_coord_extension(uf_coord, offset); +} + +reiser4_internal write_mode_t +how_to_write(uf_coord_t *uf_coord, const reiser4_key *key) +{ + write_mode_t result; + coord_t *coord; + ON_DEBUG(reiser4_key check); + + coord = &uf_coord->base_coord; + + assert("vs-1252", znode_is_wlocked(coord->node)); + assert("vs-1253", znode_is_loaded(coord->node)); + + if (uf_coord->valid == 1) { + assert("vs-1332", check_coord(coord, key)); + return (coord->between == AFTER_UNIT) ? APPEND_ITEM : OVERWRITE_ITEM; + } + + if (less_than_ldk(coord->node, key)) { + assert("vs-1014", get_key_offset(key) == 0); + + coord_init_before_first_item(coord, coord->node); + uf_coord->valid = 1; + result = FIRST_ITEM; + goto ok; + } + + assert("vs-1335", less_than_rdk(coord->node, key)); + + if (node_is_empty(coord->node)) { + assert("vs-879", znode_get_level(coord->node) == LEAF_LEVEL); + assert("vs-880", get_key_offset(key) == 0); + /* + * Situation that check below tried to handle is follows: some + * other thread writes to (other) file and has to insert empty + * leaf between two adjacent extents. Generally, we are not + * supposed to muck with this node. But it is possible that + * said other thread fails due to some error (out of disk + * space, for example) and leaves empty leaf + * lingering. Nothing prevents us from reusing it. + */ + assert("vs-1000", UNDER_RW(dk, current_tree, read, + keylt(key, znode_get_rd_key(coord->node)))); + assert("vs-1002", coord->between == EMPTY_NODE); + result = FIRST_ITEM; + uf_coord->valid = 1; + goto ok; + } + + assert("vs-1336", coord->item_pos < node_num_items(coord->node)); + assert("vs-1007", ergo(coord->between == AFTER_UNIT || coord->between == AT_UNIT, keyle(item_key_by_coord(coord, &check), key))); + assert("vs-1008", ergo(coord->between == AFTER_UNIT || coord->between == AT_UNIT, keylt(key, get_next_item_key(coord, &check)))); + + switch(coord->between) { + case AFTER_ITEM: + uf_coord->valid = 1; + result = FIRST_ITEM; + break; + case AFTER_UNIT: + assert("vs-1323", (item_is_tail(coord) || item_is_extent(coord)) && item_of_that_file(coord, key)); + assert("vs-1208", keyeq(item_plugin_by_coord(coord)->s.file.append_key(coord, &check), key)); + result = APPEND_ITEM; + validate_extended_coord(uf_coord, get_key_offset(key)); + break; + case AT_UNIT: + /* FIXME: it would be nice to check that coord matches to key */ + assert("vs-1324", (item_is_tail(coord) || item_is_extent(coord)) && item_of_that_file(coord, key)); + validate_extended_coord(uf_coord, get_key_offset(key)); + result = OVERWRITE_ITEM; + break; + default: + assert("vs-1337", 0); + result = OVERWRITE_ITEM; + break; + } + +ok: + assert("vs-1349", uf_coord->valid == 1); + assert("vs-1332", check_coord(coord, key)); + return result; +} + +/* obtain lock on right neighbor and drop lock on current node */ +reiser4_internal int +goto_right_neighbor(coord_t * coord, lock_handle * lh) +{ + int result; + lock_handle lh_right; + + assert("vs-1100", znode_is_locked(coord->node)); + + init_lh(&lh_right); + result = reiser4_get_right_neighbor( + &lh_right, coord->node, + znode_is_wlocked(coord->node) ? ZNODE_WRITE_LOCK : ZNODE_READ_LOCK, + GN_CAN_USE_UPPER_LEVELS); + if (result) { + done_lh(&lh_right); + return result; + } + + done_lh(lh); + + coord_init_first_unit_nocheck(coord, lh_right.node); + move_lh(lh, &lh_right); + + return 0; + +} + +/* this is to be used after find_file_item and in find_file_item_nohint to determine real state of file */ +static void +set_file_state(struct inode *inode, int cbk_result, tree_level level) +{ + assert("vs-1649", inode != NULL); + + if (cbk_errored(cbk_result)) + /* error happened in find_file_item */ + return; + + assert("vs-1164", level == LEAF_LEVEL || level == TWIG_LEVEL); + + if (inode_get_flag(inode, REISER4_PART_CONV)) { + set_file_state_unknown(inode); + return; + } + + if (file_state_is_unknown(inode)) { + if (cbk_result == CBK_COORD_NOTFOUND) + set_file_state_empty(inode); + else if (level == LEAF_LEVEL) + set_file_state_tails(inode); + else + set_file_state_extents(inode); + } else { + /* file state is known, check that it is set correctly */ + assert("vs-1161", ergo(cbk_result == CBK_COORD_NOTFOUND, + file_is_empty(inode))); + assert("vs-1162", ergo(level == LEAF_LEVEL && cbk_result == CBK_COORD_FOUND, + file_is_built_of_tails(inode))); + assert("vs-1165", ergo(level == TWIG_LEVEL && cbk_result == CBK_COORD_FOUND, + file_is_built_of_extents(inode))); + } +} + +reiser4_internal int +find_file_item(hint_t *hint, /* coord, lock handle and seal are here */ + const reiser4_key *key, /* key of position in a file of next read/write */ + znode_lock_mode lock_mode, /* which lock (read/write) to put on returned node */ + ra_info_t *ra_info, + struct inode *inode) +{ + int result; + coord_t *coord; + lock_handle *lh; + __u32 cbk_flags; + + assert("nikita-3030", schedulable()); + + /* collect statistics on the number of calls to this function */ + reiser4_stat_inc(file.find_file_item); + + coord = &hint->coord.base_coord; + lh = hint->coord.lh; + init_lh(lh); + if (hint) { + result = hint_validate(hint, key, 1/*check key*/, lock_mode); + if (!result) { + if (coord->between == AFTER_UNIT && equal_to_rdk(coord->node, key)) { + result = goto_right_neighbor(coord, lh); + if (result == -E_NO_NEIGHBOR) + return RETERR(-EIO); + if (result) + return result; + assert("vs-1152", equal_to_ldk(coord->node, key)); + /* we moved to different node. Invalidate coord extension, zload is necessary to init it + again */ + hint->coord.valid = 0; + reiser4_stat_inc(file.find_file_item_via_right_neighbor); + } else { + reiser4_stat_inc(file.find_file_item_via_seal); + } + + set_file_state(inode, CBK_COORD_FOUND, znode_get_level(coord->node)); + return CBK_COORD_FOUND; + } + } + + /* collect statistics on the number of calls to this function which did not get optimized */ + reiser4_stat_inc(file.find_file_item_via_cbk); + + coord_init_zero(coord); + cbk_flags = (lock_mode == ZNODE_READ_LOCK) ? CBK_UNIQUE : (CBK_UNIQUE | CBK_FOR_INSERT); + if (inode != NULL) { + result = object_lookup(inode, + key, + coord, + lh, + lock_mode, + FIND_MAX_NOT_MORE_THAN, + TWIG_LEVEL, + LEAF_LEVEL, + cbk_flags, + ra_info); + } else { + result = coord_by_key(current_tree, + key, + coord, + lh, + lock_mode, + FIND_MAX_NOT_MORE_THAN, + TWIG_LEVEL, + LEAF_LEVEL, + cbk_flags, + ra_info); + } + + set_file_state(inode, result, znode_get_level(coord->node)); + + /* FIXME: we might already have coord extension initialized */ + hint->coord.valid = 0; + return result; +} + +reiser4_internal int +find_file_item_nohint(coord_t *coord, lock_handle *lh, const reiser4_key *key, + znode_lock_mode lock_mode, struct inode *inode) +{ + int result; + + result = object_lookup(inode, key, coord, lh, lock_mode, + FIND_MAX_NOT_MORE_THAN, + TWIG_LEVEL, LEAF_LEVEL, + (lock_mode == ZNODE_READ_LOCK) ? CBK_UNIQUE : (CBK_UNIQUE | CBK_FOR_INSERT), + NULL /* ra_info */); + set_file_state(inode, result, znode_get_level(coord->node)); + return result; +} + +/* plugin->u.file.write_flowom = NULL + plugin->u.file.read_flow = NULL */ + +reiser4_internal void +hint_init_zero(hint_t *hint, lock_handle *lh) +{ + xmemset(hint, 0, sizeof (*hint)); + hint->coord.lh = lh; +} + +/* find position of last byte of last item of the file plus 1. This is used by truncate and mmap to find real file + size */ +static int +find_file_size(struct inode *inode, loff_t *file_size) +{ + int result; + reiser4_key key; + coord_t coord; + lock_handle lh; + item_plugin *iplug; + + assert("vs-1247", inode_file_plugin(inode)->key_by_inode == key_by_inode_unix_file); + key_by_inode_unix_file(inode, get_key_offset(max_key()), &key); + + init_lh(&lh); + result = find_file_item_nohint(&coord, &lh, &key, ZNODE_READ_LOCK, inode); + if (cbk_errored(result)) { + /* error happened */ + done_lh(&lh); + return result; + } + + if (result == CBK_COORD_NOTFOUND) { + /* empty file */ + done_lh(&lh); + *file_size = 0; + return 0; + } + + /* there are items of this file (at least one) */ + /*coord_clear_iplug(&coord);*/ + result = zload(coord.node); + if (unlikely(result)) { + done_lh(&lh); + return result; + } + iplug = item_plugin_by_coord(&coord); + + assert("vs-853", iplug->s.file.append_key); + iplug->s.file.append_key(&coord, &key); + + *file_size = get_key_offset(&key); + + zrelse(coord.node); + done_lh(&lh); + + return 0; +} + +static int +find_file_state(unix_file_info_t *uf_info) +{ + int result; + + assert("vs-1628", ea_obtained(uf_info)); + + result = 0; + if (uf_info->container == UF_CONTAINER_UNKNOWN) { + loff_t file_size; + + result = find_file_size(unix_file_info_to_inode(uf_info), &file_size); + } + assert("vs-1074", ergo(result == 0, uf_info->container != UF_CONTAINER_UNKNOWN)); + return result; +} + +/* estimate and reserve space needed to truncate page which gets partially truncated: one block for page itself, stat + data update (estimate_one_insert_into_item) and one item insertion (estimate_one_insert_into_item) which may happen + if page corresponds to hole extent and unallocated one will have to be created */ +static int reserve_partial_page(reiser4_tree *tree) +{ + grab_space_enable(); + return reiser4_grab_reserved(reiser4_get_current_sb(), + 1 + + 2 * estimate_one_insert_into_item(tree), + BA_CAN_COMMIT); +} + +/* estimate and reserve space needed to cut one item and update one stat data */ +reiser4_internal int reserve_cut_iteration(reiser4_tree *tree) +{ + __u64 estimate = estimate_one_item_removal(tree) + + estimate_one_insert_into_item(tree); + + assert("nikita-3172", lock_stack_isclean(get_current_lock_stack())); + + grab_space_enable(); + /* We need to double our estimate now that we can delete more than one + node. */ + return reiser4_grab_reserved(reiser4_get_current_sb(), estimate*2, + BA_CAN_COMMIT); +} + +/* cut file items one by one starting from the last one until new file size (inode->i_size) is reached. Reserve space + and update file stat data on every single cut from the tree */ +reiser4_internal int +cut_file_items(struct inode *inode, loff_t new_size, int update_sd, loff_t cur_size, int mode) +{ + reiser4_key from_key, to_key; + reiser4_key smallest_removed; + int result; + + assert("vs-1248", inode_file_plugin(inode)->key_by_inode == key_by_inode_unix_file); + key_by_inode_unix_file(inode, new_size, &from_key); + to_key = from_key; + set_key_offset(&to_key, cur_size - 1/*get_key_offset(max_key())*/); + /* this loop normally runs just once */ + while (1) { + result = reserve_cut_iteration(tree_by_inode(inode)); + if (result) + break; + + result = cut_tree_object(current_tree, &from_key, &to_key, + &smallest_removed, inode, mode); + if (result == -E_REPEAT) { + /* -E_REPEAT is a signal to interrupt a long file truncation process */ + INODE_SET_FIELD(inode, i_size, get_key_offset(&smallest_removed)); + if (update_sd) { + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + result = reiser4_update_sd(inode); + if (result) + break; + } + + all_grabbed2free(); + reiser4_release_reserved(inode->i_sb); + + /* cut_tree_object() was interrupted probably because + * current atom requires commit, we have to release + * transaction handle to allow atom commit. */ + txn_restart_current(); + continue; + } + if (result && !(result == CBK_COORD_NOTFOUND && new_size == 0 && inode->i_size == 0)) + break; + + INODE_SET_FIELD(inode, i_size, new_size); + if (update_sd) { + /* Final sd update after the file gets its correct size */ + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + result = reiser4_update_sd(inode); + } + break; + } + + all_grabbed2free(); + reiser4_release_reserved(inode->i_sb); + + return result; +} + +int find_or_create_extent(struct page *page); + +/* part of unix_file_truncate: it is called when truncate is used to make file shorter */ +static int +shorten_file(struct inode *inode, loff_t new_size) +{ + int result; + struct page *page; + int padd_from; + unsigned long index; + char *kaddr; + + /* all items of ordinary reiser4 file are grouped together. That is why we can use cut_tree. Plan B files (for + instance) can not be truncated that simply */ + result = cut_file_items(inode, new_size, 1/*update_sd*/, get_key_offset(max_key()), 1); + if (result) + return result; + + assert("vs-1105", new_size == inode->i_size); + if (new_size == 0) { + set_file_state_empty(inode); + return 0; + } + + if (file_is_built_of_tails(inode)) + /* No need to worry about zeroing last page after new file end */ + return 0; + + padd_from = inode->i_size & (PAGE_CACHE_SIZE - 1); + if (!padd_from) + /* file is truncated to page boundary */ + return 0; + + result = reserve_partial_page(tree_by_inode(inode)); + if (result) { + reiser4_release_reserved(inode->i_sb); + return result; + } + + /* last page is partially truncated - zero its content */ + index = (inode->i_size >> PAGE_CACHE_SHIFT); + page = read_cache_page(inode->i_mapping, index, readpage_unix_file/*filler*/, 0); + if (IS_ERR(page)) { + all_grabbed2free(); + reiser4_release_reserved(inode->i_sb); + if (likely(PTR_ERR(page) == -EINVAL)) { + /* looks like file is built of tail items */ + return 0; + } + return PTR_ERR(page); + } + wait_on_page_locked(page); + if (!PageUptodate(page)) { + all_grabbed2free(); + page_cache_release(page); + reiser4_release_reserved(inode->i_sb); + return RETERR(-EIO); + } + + /* if page correspons to hole extent unit - unallocated one will be created here. This is not necessary */ + result = find_or_create_extent(page); + + /* FIXME: cut_file_items has already updated inode. Probably it would be better to update it here when file is + really truncated */ + all_grabbed2free(); + if (result) { + page_cache_release(page); + reiser4_release_reserved(inode->i_sb); + return result; + } + + lock_page(page); + assert("vs-1066", PageLocked(page)); + kaddr = kmap_atomic(page, KM_USER0); + memset(kaddr + padd_from, 0, PAGE_CACHE_SIZE - padd_from); + flush_dcache_page(page); + kunmap_atomic(kaddr, KM_USER0); + unlock_page(page); + page_cache_release(page); + reiser4_release_reserved(inode->i_sb); + return 0; +} + +static loff_t +write_flow(struct file *, struct inode *, const char *buf, loff_t count, loff_t pos); + +/* it is called when truncate is used to make file longer and when write position is set past real end of file. It + appends file which has size @cur_size with hole of certain size (@hole_size). It returns 0 on success, error code + otherwise */ +static int +append_hole(struct inode *inode, loff_t new_size) +{ + int result; + loff_t written; + loff_t hole_size; + + assert("vs-1107", inode->i_size < new_size); + + result = 0; + hole_size = new_size - inode->i_size; + written = write_flow(NULL, inode, NULL/*buf*/, hole_size, + inode->i_size); + if (written != hole_size) { + /* return error because file is not expanded as required */ + if (written > 0) + result = RETERR(-ENOSPC); + else + result = written; + } else { + assert("vs-1081", inode->i_size == new_size); + } + return result; +} + +/* this either cuts or add items of/to the file so that items match new_size. It is used in unix_file_setattr when it is + used to truncate +VS-FIXME-HANS: explain that +and in unix_file_delete */ +static int +truncate_file_body(struct inode *inode, loff_t new_size) +{ + int result; + + if (inode->i_size < new_size) + result = append_hole(inode, new_size); + else + result = shorten_file(inode, new_size); + + return result; +} + +/* plugin->u.file.truncate + all the work is done on reiser4_setattr->unix_file_setattr->truncate_file_body +*/ +reiser4_internal int +truncate_unix_file(struct inode *inode, loff_t new_size) +{ + return 0; +} + +/* plugin->u.write_sd_by_inode = write_sd_by_inode_common */ + +/* get access hint (seal, coord, key, level) stored in reiser4 private part of + struct file if it was stored in a previous access to the file */ +reiser4_internal int +load_file_hint(struct file *file, hint_t *hint, lock_handle *lh) +{ + reiser4_file_fsdata *fsdata; + + if (file) { + fsdata = reiser4_get_file_fsdata(file); + if (IS_ERR(fsdata)) + return PTR_ERR(fsdata); + + if (seal_is_set(&fsdata->reg.hint.seal)) { + *hint = fsdata->reg.hint; + hint->coord.lh = lh; + /* force re-validation of the coord on the first + * iteration of the read/write loop. */ + hint->coord.valid = 0; + return 0; + } + xmemset(&fsdata->reg.hint, 0, sizeof(hint_t)); + } + hint_init_zero(hint, lh); + return 0; +} + + +/* this copies hint for future tree accesses back to reiser4 private part of + struct file */ +reiser4_internal void +save_file_hint(struct file *file, const hint_t *hint) +{ + reiser4_file_fsdata *fsdata; + + if (!file || !seal_is_set(&hint->seal)) + return; + + fsdata = reiser4_get_file_fsdata(file); + assert("vs-965", !IS_ERR(fsdata)); + fsdata->reg.hint = *hint; + return; +} + +reiser4_internal void +unset_hint(hint_t *hint) +{ + assert("vs-1315", hint); + seal_done(&hint->seal); +} + +/* coord must be set properly. So, that set_hint has nothing to do */ +reiser4_internal void +set_hint(hint_t *hint, const reiser4_key *key, znode_lock_mode mode) +{ + ON_DEBUG(coord_t *coord = &hint->coord.base_coord); + assert("vs-1207", WITH_DATA(coord->node, check_coord(coord, key))); + + seal_init(&hint->seal, &hint->coord.base_coord, key); + hint->offset = get_key_offset(key); + hint->level = znode_get_level(hint->coord.base_coord.node); + hint->mode = mode; +} + +reiser4_internal int +hint_is_set(const hint_t *hint) +{ + return seal_is_set(&hint->seal); +} + +#if REISER4_DEBUG +static int all_but_offset_key_eq(const reiser4_key *k1, const reiser4_key *k2) +{ + return (get_key_locality(k1) == get_key_locality(k2) && + get_key_type(k1) == get_key_type(k2) && + get_key_band(k1) == get_key_band(k2) && + get_key_ordering(k1) == get_key_ordering(k2) && + get_key_objectid(k1) == get_key_objectid(k2)); +} +#endif + +reiser4_internal int +hint_validate(hint_t *hint, const reiser4_key *key, int check_key, znode_lock_mode lock_mode) +{ + if (!hint || !hint_is_set(hint) || hint->mode != lock_mode) + /* hint either not set or set by different operation */ + return RETERR(-E_REPEAT); + + assert("vs-1277", all_but_offset_key_eq(key, &hint->seal.key)); + + if (check_key && get_key_offset(key) != hint->offset) + /* hint is set for different key */ + return RETERR(-E_REPEAT); + + return seal_validate(&hint->seal, &hint->coord.base_coord, key, + hint->level, hint->coord.lh, + FIND_MAX_NOT_MORE_THAN, + lock_mode, ZNODE_LOCK_LOPRI); +} + +/* look for place at twig level for extent corresponding to page, call extent's writepage method to create + unallocated extent if it does not exist yet, initialize jnode, capture page */ +reiser4_internal int +find_or_create_extent(struct page *page) +{ + int result; + uf_coord_t uf_coord; + coord_t *coord; + lock_handle lh; + reiser4_key key; + item_plugin *iplug; + znode *loaded; + struct inode *inode; + + reiser4_stat_inc(file.page_ops.writepage_calls); + + assert("vs-1065", page->mapping && page->mapping->host); + inode = page->mapping->host; + + /* get key of first byte of the page */ + key_by_inode_unix_file(inode, (loff_t) page->index << PAGE_CACHE_SHIFT, &key); + + init_uf_coord(&uf_coord, &lh); + coord = &uf_coord.base_coord; + + result = find_file_item_nohint(coord, &lh, &key, ZNODE_WRITE_LOCK, inode); + if (IS_CBKERR(result)) { + done_lh(&lh); + return result; + } + + /*coord_clear_iplug(coord);*/ + result = zload(coord->node); + if (result) { + done_lh(&lh); + return result; + } + loaded = coord->node; + + /* get plugin of extent item */ + iplug = item_plugin_by_id(EXTENT_POINTER_ID); + result = iplug->s.file.capture(&key, &uf_coord, page, how_to_write(&uf_coord, &key)); + assert("vs-429378", result != -E_REPEAT); + zrelse(loaded); + done_lh(&lh); + return result; +} + +#if REISER4_USE_EFLUSH +static int inode_has_eflushed_jnodes(struct inode * inode) +{ + reiser4_tree * tree = &get_super_private(inode->i_sb)->tree; + int ret; + + RLOCK_TREE(tree); + ret = (radix_tree_tagged(jnode_tree_by_inode(inode), EFLUSH_TAG_ANONYMOUS) || + radix_tree_tagged(jnode_tree_by_inode(inode), EFLUSH_TAG_CAPTURED)); + RUNLOCK_TREE(tree); + return ret; +} +# else +#define inode_has_eflushed_jnodes(inode) (0) +#endif + +/* Check mapping for existence of not captured dirty pages. This returns !0 if either page tree contains pages tagged + PAGECACHE_TAG_REISER4_MOVED or if eflushed jnode tree is not empty */ +static int +inode_has_anonymous_pages(struct inode *inode) +{ + return (mapping_tagged(inode->i_mapping, PAGECACHE_TAG_REISER4_MOVED) || + inode_has_eflushed_jnodes(inode)); +} + +static int +capture_page_and_create_extent(struct page *page) +{ + int result; + struct inode *inode; + + assert("vs-1084", page->mapping && page->mapping->host); + inode = page->mapping->host; + assert("vs-1139", file_is_built_of_extents(inode)); + /* page belongs to file */ + assert("vs-1393", inode->i_size > ((loff_t) page->index << PAGE_CACHE_SHIFT)); + + /* page capture may require extent creation (if it does not exist yet) and stat data's update (number of blocks + changes on extent creation) */ + grab_space_enable (); + result = reiser4_grab_space(2 * estimate_one_insert_into_item(tree_by_inode(inode)), BA_CAN_COMMIT); + if (likely(!result)) + result = find_or_create_extent(page); + + all_grabbed2free(); + if (result != 0) + SetPageError(page); + return result; +} + +/* plugin->u.file.capturepage handler */ +reiser4_internal int +capturepage_unix_file(struct page * page) { + int result; + + page_cache_get(page); + unlock_page(page); + result = capture_page_and_create_extent(page); + lock_page(page); + page_cache_release(page); + return result; +} + +static void +redirty_inode(struct inode *inode) +{ + spin_lock(&inode_lock); + inode->i_state |= I_DIRTY; + spin_unlock(&inode_lock); +} + +/* + * Support for "anonymous" pages and jnodes. + * + * When file is write-accessed through mmap pages can be dirtied from the user + * level. In this case kernel is not notified until one of following happens: + * + * (1) msync() + * + * (2) truncate() (either explicit or through unlink) + * + * (3) VM scanner starts reclaiming mapped pages, dirtying them before + * starting write-back. + * + * As a result of (3) ->writepage may be called on a dirty page without + * jnode. Such page is called "anonymous" in reiser4. Certain work-loads + * (iozone) generate huge number of anonymous pages. Emergency flush handles + * this situation by creating jnode for anonymous page, starting IO on the + * page, and marking jnode with JNODE_KEEPME bit so that it's not throw out of + * memory. Such jnode is also called anonymous. + * + * reiser4_sync_sb() method tries to insert anonymous pages and jnodes into + * tree. This is done by capture_anonymous_*() functions below. + * + */ + +/* this returns 1 if it captured page */ +static int +capture_anonymous_page(struct page *pg, int keepme) +{ + struct address_space *mapping; + jnode *node; + int result; + + if (PageWriteback(pg)) + /* FIXME: do nothing? */ + return 0; + + mapping = pg->mapping; + + lock_page(pg); + /* page is guaranteed to be in the mapping, because we are operating under rw-semaphore. */ + assert("nikita-3336", pg->mapping == mapping); + node = jnode_of_page(pg); + unlock_page(pg); + if (!IS_ERR(node)) { + result = jload(node); + assert("nikita-3334", result == 0); + assert("nikita-3335", jnode_page(node) == pg); + result = capture_page_and_create_extent(pg); + if (result == 0) { + /* + * node will be captured into atom by + * capture_page_and_create_extent(). Atom + * cannot commit (because we have open + * transaction handle), and node cannot be + * truncated, because we have non-exclusive + * access to the file. + */ + assert("nikita-3327", node->atom != NULL); + JF_CLR(node, JNODE_KEEPME); + result = 1; + } else + warning("nikita-3329", + "Cannot capture anon page: %i", result); + jrelse(node); + jput(node); + } else + result = PTR_ERR(node); + + return result; +} + + +#define CAPTURE_APAGE_BURST (1024) + +static int +capture_anonymous_pages(struct address_space *mapping, pgoff_t *index, long *captured) +{ + int result; + unsigned to_capture; + struct pagevec pvec; + unsigned found_pages; + jnode *jvec[PAGEVEC_SIZE]; + unsigned found_jnodes; + pgoff_t cur, end; + unsigned count; + reiser4_tree * tree; + unsigned i; + + result = 0; + + ON_TRACE(TRACE_CAPTURE_ANONYMOUS, + "capture anonymous: oid %llu: start index %lu\n", + get_inode_oid(mapping->host), *index); + + to_capture = CAPTURE_APAGE_BURST; + found_jnodes = 0; + tree = &get_super_private(mapping->host->i_sb)->tree; + + do { + pagevec_init(&pvec, 0); + + cur = *index; + count = min(pagevec_space(&pvec), to_capture); + + /* find and capture "anonymous" pages */ + found_pages = pagevec_lookup_tag(&pvec, mapping, index, PAGECACHE_TAG_REISER4_MOVED, count); + if (found_pages != 0) { + ON_TRACE(TRACE_CAPTURE_ANONYMOUS, + "oid %llu: found %u moved pages in range starting from (%lu)\n", + get_inode_oid(mapping->host), found_pages, cur); + + for (i = 0; i < pagevec_count(&pvec); i ++) { + /* tag PAGECACHE_TAG_REISER4_MOVED will be cleared by set_page_dirty_internal which is + called when jnode is captured */ + result = capture_anonymous_page(pvec.pages[i], 0); + if (result == 1) { + (*captured) ++; + result = 0; + to_capture --; + } else if (result < 0) { + warning("vs-1454", "failed for moved page: result=%i (captured=%u)\n", + result, CAPTURE_APAGE_BURST - to_capture); + break; + } else { + /* result == 0. capture_anonymous_page returns 0 for Writeback-ed page */ + ; + } + } + pagevec_release(&pvec); + if (result) + return result; + + end = *index; + } else + /* there are no more anonymous pages, continue with anonymous jnodes only */ + end = (pgoff_t)-1; + +#if REISER4_USE_EFLUSH + + /* capture anonymous jnodes between cur and end */ + while (cur < end && to_capture > 0) { + pgoff_t nr_jnodes; + + nr_jnodes = min(to_capture, (unsigned)PAGEVEC_SIZE); + + /* spin_lock_eflush(mapping->host->i_sb); */ + RLOCK_TREE(tree); + + found_jnodes = radix_tree_gang_lookup_tag(jnode_tree_by_inode(mapping->host), + (void **)&jvec, cur, nr_jnodes, + EFLUSH_TAG_ANONYMOUS); + if (found_jnodes != 0) { + for (i = 0; i < found_jnodes; i ++) { + if (index_jnode(jvec[i]) < end) { + jref(jvec[i]); + cur = index_jnode(jvec[i]) + 1; + } else { + found_jnodes = i; + break; + } + } + + if (found_jnodes != 0) { + /* there are anonymous jnodes from given range */ + /* spin_unlock_eflush(mapping->host->i_sb); */ + RUNLOCK_TREE(tree); + + ON_TRACE(TRACE_CAPTURE_ANONYMOUS, + "oid %llu: found %u anonymous jnodes in range (%lu %lu)\n", + get_inode_oid(mapping->host), found_jnodes, cur, end - 1); + + /* start i/o for eflushed nodes */ + for (i = 0; i < found_jnodes; i ++) + jstartio(jvec[i]); + + for (i = 0; i < found_jnodes; i ++) { + result = jload(jvec[i]); + if (result == 0) { + result = capture_anonymous_page(jnode_page(jvec[i]), 0); + if (result == 1) { + (*captured) ++; + result = 0; + to_capture --; + } else if (result < 0) { + jrelse(jvec[i]); + warning("nikita-3328", + "failed for anonymous jnode: result=%i (captured=%u)\n", + result, CAPTURE_APAGE_BURST - to_capture); + break; + } else { + /* result == 0. capture_anonymous_page returns 0 for Writeback-ed page */ + ; + } + jrelse(jvec[i]); + } else { + warning("vs-1454", "jload for anonymous jnode failed: captured %u, result=%i\n", + result, CAPTURE_APAGE_BURST - to_capture); + break; + } + } + for (i = 0; i < found_jnodes; i ++) + jput(jvec[i]); + if (result) + return result; + continue; + } + } + RUNLOCK_TREE(tree); + /* spin_unlock_eflush(mapping->host->i_sb);*/ + ON_TRACE(TRACE_CAPTURE_ANONYMOUS, + "oid %llu: no anonymous jnodes are found\n", get_inode_oid(mapping->host)); + break; + } +#endif /* REISER4_USE_EFLUSH */ + } while (to_capture && (found_pages || found_jnodes) && result == 0); + + if (result) { + warning("vs-1454", "Cannot capture anon pages: result=%i (captured=%d)\n", + result, CAPTURE_APAGE_BURST - to_capture); + return result; + } + + assert("vs-1678", to_capture <= CAPTURE_APAGE_BURST); + if (to_capture == 0) + /* there may be left more pages */ + redirty_inode(mapping->host); + + ON_TRACE(TRACE_CAPTURE_ANONYMOUS, + "capture anonymous: oid %llu: end index %lu, captured %u\n", + get_inode_oid(mapping->host), *index, CAPTURE_APAGE_BURST - to_capture); + + return 0; +} + +/* + * Commit atom of the jnode of a page. + */ +static int +sync_page(struct page *page) +{ + int result; + do { + jnode *node; + txn_atom *atom; + + lock_page(page); + node = jprivate(page); + if (node != NULL) + atom = UNDER_SPIN(jnode, node, jnode_get_atom(node)); + else + atom = NULL; + unlock_page(page); + result = sync_atom(atom); + } while (result == -E_REPEAT); + assert("nikita-3485", ergo(result == 0, + get_current_context()->trans->atom == NULL)); + return result; +} + +/* + * Commit atoms of pages on @pages list. + * call sync_page for each page from mapping's page tree + */ +static int +sync_page_list(struct inode *inode) +{ + int result; + struct address_space *mapping; + unsigned long from; /* start index for radix_tree_gang_lookup */ + unsigned int found; /* return value for radix_tree_gang_lookup */ + + mapping = inode->i_mapping; + from = 0; + result = 0; + read_lock_irq(&mapping->tree_lock); + while (result == 0) { + struct page *page; + + found = radix_tree_gang_lookup(&mapping->page_tree, (void **)&page, from, 1); + assert("", found < 2); + if (found == 0) + break; + + /* page may not leave radix tree because it is protected from truncating by inode->i_sem downed by + sys_fsync */ + page_cache_get(page); + read_unlock_irq(&mapping->tree_lock); + + from = page->index + 1; + + result = sync_page(page); + + page_cache_release(page); + read_lock_irq(&mapping->tree_lock); + } + + read_unlock_irq(&mapping->tree_lock); + return result; +} + +static int +commit_file_atoms(struct inode *inode) +{ + int result; + unix_file_info_t *uf_info; + reiser4_context *ctx; + + /* + * close current transaction + */ + + ctx = get_current_context(); + txn_restart(ctx); + + uf_info = unix_file_inode_data(inode); + + /* + * finish extent<->tail conversion if necessary + */ + + get_exclusive_access(uf_info); + if (inode_get_flag(inode, REISER4_PART_CONV)) { + result = finish_conversion(inode); + if (result != 0) { + drop_exclusive_access(uf_info); + return result; + } + } + + /* + * find what items file is made from + */ + + result = find_file_state(uf_info); + drop_exclusive_access(uf_info); + if (result != 0) + return result; + + /* + * file state cannot change because we are under ->i_sem + */ + + switch(uf_info->container) { + case UF_CONTAINER_EXTENTS: + result = + /* + * when we are called by + * filemap_fdatawrite-> + * do_writepages()-> + * reiser4_writepages() + * + * inode->i_mapping->dirty_pages are spices into + * ->io_pages, leaving ->dirty_pages dirty. + * + * When we are called from + * reiser4_fsync()->sync_unix_file(), we have to + * commit atoms of all pages on the ->dirty_list. + * + * So for simplicity we just commit ->io_pages and + * ->dirty_pages. + */ + sync_page_list(inode); + break; + case UF_CONTAINER_TAILS: + /* + * NOTE-NIKITA probably we can be smarter for tails. For now + * just commit all existing atoms. + */ + result = txnmgr_force_commit_all(inode->i_sb, 0); + break; + case UF_CONTAINER_EMPTY: + result = 0; + break; + case UF_CONTAINER_UNKNOWN: + default: + result = -EIO; + break; + } + + /* + * commit current transaction: there can be captured nodes from + * find_file_state() and finish_conversion(). + */ + txn_restart(ctx); + return result; +} + +/* + * this file plugin method is called to capture into current atom all + * "anonymous pages", that is, pages modified through mmap(2). For each such + * page this function creates jnode, captures this jnode, and creates (or + * modifies) extent. Anonymous pages are kept on the special inode list. Some + * of them can be emergency flushed. To cope with this list of eflushed jnodes + * from this inode is scanned. + */ +reiser4_internal int +capture_unix_file(struct inode *inode, const struct writeback_control *wbc, long *captured) +{ + int result; + unix_file_info_t *uf_info; + pgoff_t index; + + if (!inode_has_anonymous_pages(inode)) + return 0; + + result = 0; + index = 0; + do { + reiser4_context ctx; + + uf_info = unix_file_inode_data(inode); + /* + * locking: creation of extent requires read-semaphore on + * file. _But_, this function can also be called in the + * context of write system call from + * balance_dirty_pages(). So, write keeps semaphore (possible + * in write mode) on file A, and this function tries to + * acquire semaphore on (possibly) different file B. A/B + * deadlock is on a way. To avoid this try-lock is used + * here. When invoked from sys_fsync() and sys_fdatasync(), + * this function is out of reiser4 context and may safely + * sleep on semaphore. + */ + if (is_in_reiser4_context()) { + if (down_read_trylock(&uf_info->latch) == 0) { + result = RETERR(-EBUSY); + break; + } + } else + down_read(&uf_info->latch); + + init_context(&ctx, inode->i_sb); + /* avoid recursive calls to ->sync_inodes */ + ctx.nobalance = 1; + assert("zam-760", lock_stack_isclean(get_current_lock_stack())); + + LOCK_CNT_INC(inode_sem_r); + + result = capture_anonymous_pages(inode->i_mapping, &index, captured); + up_read(&uf_info->latch); + LOCK_CNT_DEC(inode_sem_r); + if (result != 0 || wbc->sync_mode != WB_SYNC_ALL) { + reiser4_exit_context(&ctx); + break; + } + result = commit_file_atoms(inode); + reiser4_exit_context(&ctx); + } while (result == 0 && inode_has_anonymous_pages(inode) /* FIXME: it should be: there are anonymous pages with + page->index >= index */); + + return result; +} + +/* + * ->sync() method for unix file. + * + * We are trying to be smart here. Instead of committing all atoms (original + * solution), we scan dirty pages of this file and commit all atoms they are + * part of. + * + * Situation is complicated by anonymous pages: i.e., extent-less pages + * dirtied through mmap. Fortunately sys_fsync() first calls + * filemap_fdatawrite() that will ultimately call reiser4_writepages(), insert + * all missing extents and capture anonymous pages. + */ +reiser4_internal int +sync_unix_file(struct inode *inode, int datasync) +{ + int result; + reiser4_context *ctx; + + ctx = get_current_context(); + assert("nikita-3486", ctx->trans->atom == NULL); + result = commit_file_atoms(inode); + assert("nikita-3484", ergo(result == 0, ctx->trans->atom == NULL)); + if (result == 0 && !datasync) { + do { + /* commit "meta-data"---stat data in our case */ + lock_handle lh; + coord_t coord; + reiser4_key key; + + coord_init_zero(&coord); + init_lh(&lh); + /* locate stat-data in a tree and return with znode + * locked */ + result = locate_inode_sd(inode, &key, &coord, &lh); + if (result == 0) { + jnode *node; + txn_atom *atom; + + node = jref(ZJNODE(coord.node)); + done_lh(&lh); + txn_restart(ctx); + LOCK_JNODE(node); + atom = jnode_get_atom(node); + UNLOCK_JNODE(node); + result = sync_atom(atom); + jput(node); + } else + done_lh(&lh); + } while (result == -E_REPEAT); + } + return result; +} + +/* plugin->u.file.readpage + page must be not out of file. This is called either via page fault and in that case vp is struct file *file, or on + truncate when last page of a file is to be read to perform its partial truncate and in that case vp is 0 +*/ +reiser4_internal int +readpage_unix_file(void *vp, struct page *page) +{ + int result; + struct inode *inode; + lock_handle lh; + reiser4_key key; + item_plugin *iplug; + hint_t hint; + coord_t *coord; + struct file *file; + + + reiser4_stat_inc(file.page_ops.readpage_calls); + + assert("vs-1062", PageLocked(page)); + assert("vs-1061", page->mapping && page->mapping->host); + assert("vs-1078", (page->mapping->host->i_size > ((loff_t) page->index << PAGE_CACHE_SHIFT))); + + inode = page->mapping->host; + + file = vp; + result = load_file_hint(file, &hint, &lh); + if (result) + return result; + + /* get key of first byte of the page */ + key_by_inode_unix_file(inode, (loff_t) page->index << PAGE_CACHE_SHIFT, &key); + + /* look for file metadata corresponding to first byte of page */ + unlock_page(page); + result = find_file_item(&hint, &key, ZNODE_READ_LOCK, 0/* ra_info */, inode); + lock_page(page); + if (result != CBK_COORD_FOUND) { + /* this indicates file corruption */ + done_lh(&lh); + return result; + } + + if (PageUptodate(page)) { + done_lh(&lh); + unlock_page(page); + return 0; + } + + coord = &hint.coord.base_coord; + result = zload(coord->node); + if (result) { + done_lh(&lh); + return result; + } + if (!hint.coord.valid) + validate_extended_coord(&hint.coord, (loff_t) page->index << PAGE_CACHE_SHIFT); + + if (!coord_is_existing_unit(coord)) { + /* this indicates corruption */ + warning("vs-280", + "Looking for page %lu of file %llu (size %lli). " + "No file items found (%d). " + "File is corrupted?\n", + page->index, get_inode_oid(inode), inode->i_size, result); + zrelse(coord->node); + done_lh(&lh); + return RETERR(-EIO); + } + + /* get plugin of found item or use plugin if extent if there are no + one */ + iplug = item_plugin_by_coord(coord); + if (iplug->s.file.readpage) + result = iplug->s.file.readpage(coord, page); + else + result = RETERR(-EINVAL); + + if (!result) { + set_key_offset(&key, (loff_t) (page->index + 1) << PAGE_CACHE_SHIFT); + /* FIXME should call set_hint() */ + unset_hint(&hint); + } else + unset_hint(&hint); + zrelse(coord->node); + done_lh(&lh); + + save_file_hint(file, &hint); + + assert("vs-979", ergo(result == 0, (PageLocked(page) || PageUptodate(page)))); + + return result; +} + +/* returns 1 if file of that size (@new_size) has to be stored in unformatted + nodes */ +/* Audited by: green(2002.06.15) */ +static int +should_have_notail(const unix_file_info_t *uf_info, loff_t new_size) +{ + if (!uf_info->tplug) + return 1; + return !uf_info->tplug->have_tail(unix_file_info_to_inode(uf_info), + new_size); + +} + +static reiser4_block_nr unix_file_estimate_read(struct inode *inode, + loff_t count UNUSED_ARG) +{ + /* We should reserve one block, because of updating of the stat data + item */ + assert("vs-1249", inode_file_plugin(inode)->estimate.update == estimate_update_common); + return estimate_update_common(inode); +} + +/* plugin->u.file.read + + the read method for the unix_file plugin + +*/ +reiser4_internal ssize_t +read_unix_file(struct file *file, char *buf, size_t read_amount, loff_t *off) +{ + int result; + struct inode *inode; + flow_t f; + lock_handle lh; + hint_t hint; + coord_t *coord; + size_t read; + reiser4_block_nr needed; + int (*read_f) (struct file *, flow_t *, hint_t *); + unix_file_info_t *uf_info; + loff_t size; + + if (unlikely(!read_amount)) + return 0; + + inode = file->f_dentry->d_inode; + assert("vs-972", !inode_get_flag(inode, REISER4_NO_SD)); + + uf_info = unix_file_inode_data(inode); + get_nonexclusive_access(uf_info); + + size = i_size_read(inode); + if (*off >= size) { + /* position to read from is past the end of file */ + drop_access(uf_info); + return 0; + } + + if (*off + read_amount > size) + read_amount = size - *off; + + /* we have nonexclusive access (NA) obtained. File's container may not change until we drop NA. If possible - + calculate read function beforehand */ + switch(uf_info->container) { + case UF_CONTAINER_EXTENTS: + read_f = item_plugin_by_id(EXTENT_POINTER_ID)->s.file.read; + break; + + case UF_CONTAINER_TAILS: + /* this is read-ahead for tails-only files */ + result = reiser4_file_readahead(file, *off, read_amount); + if (result) { + drop_access(uf_info); + return result; + } + + read_f = item_plugin_by_id(FORMATTING_ID)->s.file.read; + break; + + case UF_CONTAINER_UNKNOWN: + read_f = 0; + break; + + case UF_CONTAINER_EMPTY: + default: + warning("vs-1297", "File (ino %llu) has unexpected state: %d\n", get_inode_oid(inode), uf_info->container); + drop_access(uf_info); + return RETERR(-EIO); + } + + needed = unix_file_estimate_read(inode, read_amount); /* FIXME: tree_by_inode(inode)->estimate_one_insert */ + result = reiser4_grab_space(needed, BA_CAN_COMMIT); + if (result != 0) { + drop_access(uf_info); + return result; + } + + /* build flow */ + assert("vs-1250", inode_file_plugin(inode)->flow_by_inode == flow_by_inode_unix_file); + result = flow_by_inode_unix_file(inode, buf, 1 /* user space */ , read_amount, *off, READ_OP, &f); + if (unlikely(result)) { + drop_access(uf_info); + return result; + } + + /* get seal and coord sealed with it from reiser4 private data of struct file. The coord will tell us where our + last read of this file finished, and the seal will help to determine if that location is still valid. + */ + coord = &hint.coord.base_coord; + result = load_file_hint(file, &hint, &lh); + + while (f.length && result == 0) { + result = find_file_item(&hint, &f.key, ZNODE_READ_LOCK, NULL, inode); + if (cbk_errored(result)) + /* error happened */ + break; + + if (coord->between != AT_UNIT) + /* there were no items corresponding to given offset */ + break; + + /*coord_clear_iplug(coord);*/ + hint.coord.valid = 0; + result = zload(coord->node); + if (unlikely(result)) + break; + + validate_extended_coord(&hint.coord, get_key_offset(&f.key)); + + /* call item's read method */ + if (!read_f) + read_f = item_plugin_by_coord(coord)->s.file.read; + result = read_f(file, &f, &hint); + zrelse(coord->node); + done_lh(&lh); + } + + done_lh(&lh); + save_file_hint(file, &hint); + + read = read_amount - f.length; + if (read) + /* something was read. Update stat data */ + update_atime(inode); + + drop_access(uf_info); + + /* update position in a file */ + *off += read; + + /* return number of read bytes or error code if nothing is read */ + return read ?: result; +} + +typedef int (*write_f_t)(struct inode *, flow_t *, hint_t *, int grabbed, write_mode_t); + +/* This searches for write position in the tree and calls write method of + appropriate item to actually copy user data into filesystem. This loops + until all the data from flow @f are written to a file. */ +static loff_t +append_and_or_overwrite(struct file *file, struct inode *inode, flow_t *flow) +{ + int result; + lock_handle lh; + hint_t hint; + loff_t to_write; + write_f_t write_f; + file_container_t cur_container, new_container; + znode *loaded; + unix_file_info_t *uf_info; + + assert("nikita-3031", schedulable()); + assert("vs-1109", get_current_context()->grabbed_blocks == 0); + + /* get seal and coord sealed with it from reiser4 private data of + struct file */ + result = load_file_hint(file, &hint, &lh); + if (result) + return result; + + uf_info = unix_file_inode_data(inode); + + to_write = flow->length; + while (flow->length) { + assert("vs-1123", get_current_context()->grabbed_blocks == 0); + + { + size_t count; + + count = PAGE_CACHE_SIZE; + + if (count > flow->length) + count = flow->length; + fault_in_pages_readable(flow->data, count); + } + + if (to_write == flow->length) { + /* it may happend that find_next_item will have to insert empty node to the tree (empty leaf + node between two extent items) */ + result = reiser4_grab_space_force(1 + estimate_one_insert_item(tree_by_inode(inode)), 0); + if (result) + return result; + } + /* look for file's metadata (extent or tail item) corresponding to position we write to */ + result = find_file_item(&hint, &flow->key, ZNODE_WRITE_LOCK, NULL/* ra_info */, inode); + all_grabbed2free(); + if (IS_CBKERR(result)) { + /* error occurred */ + done_lh(&lh); + return result; + } + + cur_container = uf_info->container; + switch (cur_container) { + case UF_CONTAINER_EMPTY: + assert("vs-1196", get_key_offset(&flow->key) == 0); + if (should_have_notail(uf_info, get_key_offset(&flow->key) + flow->length)) { + new_container = UF_CONTAINER_EXTENTS; + write_f = item_plugin_by_id(EXTENT_POINTER_ID)->s.file.write; + } else { + new_container = UF_CONTAINER_TAILS; + write_f = item_plugin_by_id(FORMATTING_ID)->s.file.write; + } + break; + + case UF_CONTAINER_EXTENTS: + write_f = item_plugin_by_id(EXTENT_POINTER_ID)->s.file.write; + new_container = cur_container; + break; + + case UF_CONTAINER_TAILS: + if (should_have_notail(uf_info, get_key_offset(&flow->key) + flow->length)) { + longterm_unlock_znode(&lh); + if (!ea_obtained(uf_info)) + return RETERR(-E_REPEAT); + result = tail2extent(uf_info); + if (result) + return result; + unset_hint(&hint); + continue; + } + write_f = item_plugin_by_id(FORMATTING_ID)->s.file.write; + new_container = cur_container; + break; + + default: + longterm_unlock_znode(&lh); + return RETERR(-EIO); + } + + result = zload(lh.node); + if (result) { + longterm_unlock_znode(&lh); + return result; + } + loaded = lh.node; + + result = write_f(inode, + flow, + &hint, + 0/* not grabbed */, + how_to_write(&hint.coord, &flow->key)); + + assert("nikita-3142", get_current_context()->grabbed_blocks == 0); + if (cur_container == UF_CONTAINER_EMPTY && to_write != flow->length) { + /* file was empty and we have written something and we are having exclusive access to the file - + change file state */ + assert("vs-1195", (new_container == UF_CONTAINER_TAILS || + new_container == UF_CONTAINER_EXTENTS)); + uf_info->container = new_container; + } + zrelse(loaded); + done_lh(&lh); + if (result && result != -E_REPEAT) + break; + preempt_point(); + } + if (result == -EEXIST) + printk("write returns EEXIST!\n"); + save_file_hint(file, &hint); + + /* if nothing were written - there must be an error */ + assert("vs-951", ergo((to_write == flow->length), result < 0)); + assert("vs-1110", get_current_context()->grabbed_blocks == 0); + + return (to_write - flow->length) ? (to_write - flow->length) : result; +} + +/* make flow and write data (@buf) to the file. If @buf == 0 - hole of size @count will be created. This is called with + uf_info->latch either read- or write-locked */ +static loff_t +write_flow(struct file *file, struct inode *inode, const char *buf, loff_t count, loff_t pos) +{ + int result; + flow_t flow; + + assert("vs-1251", inode_file_plugin(inode)->flow_by_inode == flow_by_inode_unix_file); + + result = flow_by_inode_unix_file(inode, + (char *)buf, 1 /* user space */, count, pos, WRITE_OP, &flow); + if (result) + return result; + + return append_and_or_overwrite(file, inode, &flow); +} + +reiser4_internal void +drop_access(unix_file_info_t *uf_info) +{ + if (uf_info->exclusive_use) + drop_exclusive_access(uf_info); + else + drop_nonexclusive_access(uf_info); +} + +reiser4_internal struct page * +unix_file_filemap_nopage(struct vm_area_struct *area, unsigned long address, int * unused) +{ + struct page *page; + struct inode *inode; + + inode = area->vm_file->f_dentry->d_inode; + + /* block filemap_nopage if copy on capture is processing with a node of this file */ + down_read(&reiser4_inode_data(inode)->coc_sem); + get_nonexclusive_access(unix_file_inode_data(inode)); + + page = filemap_nopage(area, address, 0); + + drop_nonexclusive_access(unix_file_inode_data(inode)); + up_read(&reiser4_inode_data(inode)->coc_sem); + return page; +} + +static struct vm_operations_struct unix_file_vm_ops = { + .nopage = unix_file_filemap_nopage, +}; + +/* This function takes care about @file's pages. First of all it checks if + filesystems readonly and if so gets out. Otherwise, it throws out all + pages of file if it was mapped for read and going to be mapped for write + and consists of tails. This is done in order to not manage few copies + of the data (first in page cache and second one in tails them selves) + for the case of mapping files consisting tails. + + Here also tail2extent conversion is performed if it is allowed and file + is going to be written or mapped for write. This functions may be called + from write_unix_file() or mmap_unix_file(). */ +static int +check_pages_unix_file(struct inode *inode) +{ + reiser4_invalidate_pages(inode->i_mapping, 0, + (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT); + return unpack(inode, 0 /* not forever */); +} + +/* plugin->u.file.mmap + make sure that file is built of extent blocks. An estimation is in tail2extent */ + +/* This sets inode flags: file has mapping. if file is mmaped with VM_MAYWRITE - invalidate pages and convert. */ +reiser4_internal int +mmap_unix_file(struct file *file, struct vm_area_struct *vma) +{ + int result; + struct inode *inode; + unix_file_info_t *uf_info; + + inode = file->f_dentry->d_inode; + uf_info = unix_file_inode_data(inode); + + get_exclusive_access(uf_info); + + if (!IS_RDONLY(inode) && (vma->vm_flags & (VM_MAYWRITE | VM_SHARED))) { + /* we need file built of extent items. If it is still built of tail items we have to convert it. Find + what items the file is built of */ + result = finish_conversion(inode); + if (result) { + drop_exclusive_access(uf_info); + return result; + } + + result = find_file_state(uf_info); + if (result != 0) { + drop_exclusive_access(uf_info); + return result; + } + + assert("vs-1648", (uf_info->container == UF_CONTAINER_TAILS || + uf_info->container == UF_CONTAINER_EXTENTS || + uf_info->container == UF_CONTAINER_EMPTY)); + if (uf_info->container == UF_CONTAINER_TAILS) { + /* invalidate all pages and convert file from tails to extents */ + result = check_pages_unix_file(inode); + if (result) { + drop_exclusive_access(uf_info); + return result; + } + } + } + + result = generic_file_mmap(file, vma); + if (result == 0) { + /* mark file as having mapping. */ + inode_set_flag(inode, REISER4_HAS_MMAP); + vma->vm_ops = &unix_file_vm_ops; + } + + drop_exclusive_access(uf_info); + return result; +} + +static ssize_t +write_file(struct file *file, /* file to write to */ + const char *buf, /* address of user-space buffer */ + size_t count, /* number of bytes to write */ + loff_t *off /* position in file to write to */) +{ + struct inode *inode; + ssize_t written; /* amount actually written so far */ + loff_t pos; /* current location in the file */ + + inode = file->f_dentry->d_inode; + + /* estimation for write is entrusted to write item plugins */ + pos = *off; + + if (inode->i_size < pos) { + /* pos is set past real end of file */ + written = append_hole(inode, pos); + if (written) + return written; + assert("vs-1081", pos == inode->i_size); + } + + /* write user data to the file */ + written = write_flow(file, inode, buf, count, pos); + if (written > 0) + /* update position in a file */ + *off = pos + written; + + /* return number of written bytes, or error code */ + return written; +} + +/* plugin->u.file.write */ +reiser4_internal ssize_t +write_unix_file(struct file *file, /* file to write to */ + const char *buf, /* address of user-space buffer */ + size_t count, /* number of bytes to write */ + loff_t *off /* position in file to write to */) +{ + struct inode *inode; + ssize_t written; /* amount actually written so far */ + int result; + + if (unlikely(count == 0)) + return 0; + + inode = file->f_dentry->d_inode; + assert("vs-947", !inode_get_flag(inode, REISER4_NO_SD)); + + /* linux's VM requires this. See mm/vmscan.c:shrink_list() */ + current->backing_dev_info = inode->i_mapping->backing_dev_info; + + down(&inode->i_sem); + written = generic_write_checks(file, off, &count, 0); + if (written == 0) { + unix_file_info_t *uf_info; + + uf_info = unix_file_inode_data(inode); + + if (inode_get_flag(inode, REISER4_HAS_MMAP)) { + /* file has been mmaped before. If it is built of + tails - invalidate pages created so far and convert + to extents */ + get_exclusive_access(uf_info); + written = finish_conversion(inode); + if (written == 0) + if (uf_info->container == UF_CONTAINER_TAILS) + written = check_pages_unix_file(inode); + + drop_exclusive_access(uf_info); + } + if (written == 0) { + int rep; + int try_free_space = 1; + + for (rep = 0; ; ++ rep) { + if (inode_get_flag(inode, + REISER4_PART_CONV)) { + get_exclusive_access(uf_info); + written = finish_conversion(inode); + if (written != 0) { + drop_access(uf_info); + break; + } + } else if (inode->i_size == 0 || rep) + get_exclusive_access(uf_info); + else + get_nonexclusive_access(uf_info); + + if (rep == 0) { + /* UNIX behavior: clear suid bit on + * file modification. This cannot be + * done earlier, because removing suid + * bit captures blocks into + * transaction, which should be done + * after taking exclusive access on + * the file. */ + written = remove_suid(file->f_dentry); + if (written != 0) { + drop_access(uf_info); + break; + } + grab_space_enable(); + } + + all_grabbed2free(); + written = write_file(file, buf, count, off); + drop_access(uf_info); + + /* With no locks held we can commit atoms in + * attempt to recover free space. */ + if (written == -ENOSPC && try_free_space) { + txnmgr_force_commit_all(inode->i_sb, 0); + try_free_space = 0; + continue; + } + + if (written == -E_REPEAT) + /* write_file required exclusive + * access (for tail2extent). It + * returned E_REPEAT so that we + * restart it with exclusive access */ + txn_restart_current(); + else + break; + } + } + } + + if ((file->f_flags & O_SYNC) || IS_SYNC(inode)) { + txn_restart_current(); + result = sync_unix_file(inode, 0/* data and stat data */); + if (result) + warning("reiser4-7", "failed to sync file %llu", + get_inode_oid(inode)); + } + up(&inode->i_sem); + current->backing_dev_info = 0; + return written; +} + +/* plugin->u.file.release() convert all extent items into tail items if + necessary */ +reiser4_internal int +release_unix_file(struct inode *object, struct file *file) +{ + unix_file_info_t *uf_info; + int result; + + uf_info = unix_file_inode_data(object); + result = 0; + + get_exclusive_access(uf_info); + if (atomic_read(&file->f_dentry->d_count) == 1 && + uf_info->container == UF_CONTAINER_EXTENTS && + !should_have_notail(uf_info, object->i_size) && + !rofs_inode(object)) { + result = extent2tail(uf_info); + if (result != 0) { + warning("nikita-3233", "Failed to convert in %s (%llu)", + __FUNCTION__, get_inode_oid(object)); + print_inode("inode", object); + } + } + drop_exclusive_access(uf_info); + return 0; +} + +static void +set_file_notail(struct inode *inode) +{ + reiser4_inode *state; + formatting_plugin *tplug; + + state = reiser4_inode_data(inode); + tplug = formatting_plugin_by_id(NEVER_TAILS_FORMATTING_ID); + plugin_set_formatting(&state->pset, tplug); + inode_set_plugin(inode, + formatting_plugin_to_plugin(tplug), PSET_FORMATTING); +} + +/* if file is built of tails - convert it to extents */ +static int +unpack(struct inode *inode, int forever) +{ + int result = 0; + unix_file_info_t *uf_info; + + + uf_info = unix_file_inode_data(inode); + assert("vs-1628", ea_obtained(uf_info)); + + result = find_file_state(uf_info); + assert("vs-1074", ergo(result == 0, uf_info->container != UF_CONTAINER_UNKNOWN)); + if (result == 0) { + if (uf_info->container == UF_CONTAINER_TAILS) + result = tail2extent(uf_info); + if (result == 0 && forever) + set_file_notail(inode); + if (result == 0) { + __u64 tograb; + + grab_space_enable(); + tograb = inode_file_plugin(inode)->estimate.update(inode); + result = reiser4_grab_space(tograb, BA_CAN_COMMIT); + if (result == 0) + update_atime(inode); + } + } + + return result; +} + +/* plugin->u.file.ioctl */ +reiser4_internal int +ioctl_unix_file(struct inode *inode, struct file *filp UNUSED_ARG, unsigned int cmd, unsigned long arg UNUSED_ARG) +{ + int result; + + switch (cmd) { + case REISER4_IOC_UNPACK: + get_exclusive_access(unix_file_inode_data(inode)); + result = unpack(inode, 1 /* forever */); + drop_exclusive_access(unix_file_inode_data(inode)); + break; + + default: + result = RETERR(-ENOSYS); + break; + } + return result; +} + +/* plugin->u.file.get_block */ +reiser4_internal int +get_block_unix_file(struct inode *inode, + sector_t block, struct buffer_head *bh_result, int create UNUSED_ARG) +{ + int result; + reiser4_key key; + coord_t coord; + lock_handle lh; + item_plugin *iplug; + + assert("vs-1091", create == 0); + + key_by_inode_unix_file(inode, (loff_t) block * current_blocksize, &key); + + init_lh(&lh); + result = find_file_item_nohint(&coord, &lh, &key, ZNODE_READ_LOCK, inode); + if (cbk_errored(result)) { + done_lh(&lh); + return result; + } + + /*coord_clear_iplug(&coord);*/ + result = zload(coord.node); + if (result) { + done_lh(&lh); + return result; + } + iplug = item_plugin_by_coord(&coord); + if (iplug->s.file.get_block) + result = iplug->s.file.get_block(&coord, block, bh_result); + else + result = RETERR(-EINVAL); + + zrelse(coord.node); + done_lh(&lh); + return result; +} + +/* plugin->u.file.flow_by_inode + initialize flow (key, length, buf, etc) */ +reiser4_internal int +flow_by_inode_unix_file(struct inode *inode /* file to build flow for */ , + char *buf /* user level buffer */ , + int user /* 1 if @buf is of user space, 0 - if it is kernel space */ , + loff_t size /* buffer size */ , + loff_t off /* offset to start operation(read/write) from */ , + rw_op op /* READ or WRITE */ , + flow_t *flow /* resulting flow */ ) +{ + assert("nikita-1100", inode != NULL); + + flow->length = size; + flow->data = buf; + flow->user = user; + flow->op = op; + assert("nikita-1931", inode_file_plugin(inode) != NULL); + assert("nikita-1932", inode_file_plugin(inode)->key_by_inode == key_by_inode_unix_file); + /* calculate key of write position and insert it into flow->key */ + return key_by_inode_unix_file(inode, off, &flow->key); +} + +/* plugin->u.file.key_by_inode */ +reiser4_internal int +key_by_inode_unix_file(struct inode *inode, loff_t off, reiser4_key *key) +{ + return key_by_inode_and_offset_common(inode, off, key); +} + +/* plugin->u.file.set_plug_in_sd = NULL + plugin->u.file.set_plug_in_inode = NULL + plugin->u.file.create_blank_sd = NULL */ +/* plugin->u.file.delete */ +/* + plugin->u.file.add_link = add_link_common + plugin->u.file.rem_link = NULL */ + +/* plugin->u.file.owns_item + this is common_file_owns_item with assertion */ +/* Audited by: green(2002.06.15) */ +reiser4_internal int +owns_item_unix_file(const struct inode *inode /* object to check against */ , + const coord_t *coord /* coord to check */ ) +{ + int result; + + result = owns_item_common(inode, coord); + if (!result) + return 0; + if (item_type_by_coord(coord) != UNIX_FILE_METADATA_ITEM_TYPE) + return 0; + assert("vs-547", + item_id_by_coord(coord) == EXTENT_POINTER_ID || + item_id_by_coord(coord) == FORMATTING_ID); + return 1; +} + +static int +setattr_truncate(struct inode *inode, struct iattr *attr) +{ + int result; + int s_result; + loff_t old_size; + reiser4_tree *tree; + + inode_check_scale(inode, inode->i_size, attr->ia_size); + + old_size = inode->i_size; + tree = tree_by_inode(inode); + + result = safe_link_grab(tree, BA_CAN_COMMIT); + if (result == 0) + result = safe_link_add(inode, SAFE_TRUNCATE); + all_grabbed2free(); + if (result == 0) + result = truncate_file_body(inode, attr->ia_size); + if (result) + warning("vs-1588", "truncate_file failed: oid %lli, old size %lld, new size %lld, retval %d", + get_inode_oid(inode), old_size, attr->ia_size, result); + + s_result = safe_link_grab(tree, BA_CAN_COMMIT); + if (s_result == 0) + s_result = safe_link_del(inode, SAFE_TRUNCATE); + if (s_result != 0) { + warning("nikita-3417", "Cannot kill safelink %lli: %i", + get_inode_oid(inode), s_result); + } + safe_link_release(tree); + all_grabbed2free(); + return result; +} + +/* plugin->u.file.setattr method */ +/* This calls inode_setattr and if truncate is in effect it also takes + exclusive inode access to avoid races */ +reiser4_internal int +setattr_unix_file(struct inode *inode, /* Object to change attributes */ + struct iattr *attr /* change description */ ) +{ + int result; + + if (attr->ia_valid & ATTR_SIZE) { + /* truncate does reservation itself and requires exclusive + * access obtained */ + unix_file_info_t *ufo; + + ufo = unix_file_inode_data(inode); + get_exclusive_access(ufo); + result = setattr_truncate(inode, attr); + drop_exclusive_access(ufo); + } else + result = setattr_common(inode, attr); + + return result; +} + +/* plugin->u.file.can_add_link = common_file_can_add_link */ +/* VS-FIXME-HANS: why does this always resolve to extent pointer? this wrapper serves what purpose? get rid of it. */ +/* plugin->u.file.readpages method */ +reiser4_internal void +readpages_unix_file(struct file *file, struct address_space *mapping, + struct list_head *pages) +{ + reiser4_file_fsdata *fsdata; + item_plugin *iplug; + + /* FIXME: readpages_unix_file() only supports files built of extents. */ + if (unix_file_inode_data(mapping->host)->container != UF_CONTAINER_EXTENTS) + return; + + fsdata = reiser4_get_file_fsdata(file); + iplug = item_plugin_by_id(EXTENT_POINTER_ID); + iplug->s.file.readpages(fsdata->reg.coord, mapping, pages); + return; +} + +/* plugin->u.file.init_inode_data */ +reiser4_internal void +init_inode_data_unix_file(struct inode *inode, + reiser4_object_create_data *crd, int create) +{ + unix_file_info_t *data; + + data = unix_file_inode_data(inode); + data->container = create ? UF_CONTAINER_EMPTY : UF_CONTAINER_UNKNOWN; + init_rwsem(&data->latch); + data->tplug = inode_formatting_plugin(inode); + data->exclusive_use = 0; + +#if REISER4_DEBUG + data->ea_owner = 0; +#endif + init_inode_ordering(inode, crd, create); +} + +/* VS-FIXME-HANS: what is pre deleting all about? */ +/* plugin->u.file.pre_delete */ +reiser4_internal int +pre_delete_unix_file(struct inode *inode) +{ + /* FIXME: put comment here */ + /*if (inode->i_size == 0) + return 0;*/ + return truncate_file_body(inode, 0/* size */); +} + +/* Reads @count bytes from @file and calls @actor for every page read. This is + needed for loop back devices support. */ +reiser4_internal ssize_t sendfile_common ( + struct file *file, loff_t *ppos, size_t count, read_actor_t actor, void __user *target) +{ + file_plugin *fplug; + struct inode *inode; + read_descriptor_t desc; + struct page *page = NULL; + int ret = 0; + + assert("umka-3108", file != NULL); + + inode = file->f_dentry->d_inode; + + desc.error = 0; + desc.written = 0; + desc.arg.data = target; + desc.count = count; + + fplug = inode_file_plugin(inode); + if (fplug->readpage == NULL) + return RETERR(-EINVAL); + + while (desc.count != 0) { + unsigned long read_request_size; + unsigned long index; + unsigned long offset; + loff_t file_size = i_size_read(inode); + + if (*ppos >= file_size) + break; + + index = *ppos >> PAGE_CACHE_SHIFT; + offset = *ppos & ~PAGE_CACHE_MASK; + + page_cache_readahead(inode->i_mapping, &file->f_ra, file, offset); + + /* determine valid read request size. */ + read_request_size = PAGE_CACHE_SIZE - offset; + if (read_request_size > desc.count) + read_request_size = desc.count; + if (*ppos + read_request_size >= file_size) { + read_request_size = file_size - *ppos; + if (read_request_size == 0) + break; + } + page = grab_cache_page(inode->i_mapping, index); + if (unlikely(page == NULL)) { + desc.error = RETERR(-ENOMEM); + break; + } + + if (PageUptodate(page)) + /* process locked, up-to-date page by read actor */ + goto actor; + + ret = fplug->readpage(file, page); + if (ret != 0) { + SetPageError(page); + ClearPageUptodate(page); + desc.error = ret; + goto fail_locked_page; + } + + lock_page(page); + if (!PageUptodate(page)) { + desc.error = RETERR(-EIO); + goto fail_locked_page; + } + + actor: + ret = actor(&desc, page, offset, read_request_size); + unlock_page(page); + page_cache_release(page); + + (*ppos) += ret; + + if (ret != read_request_size) + break; + } + + if (0) { + fail_locked_page: + unlock_page(page); + page_cache_release(page); + } + + update_atime(inode); + + if (desc.written) + return desc.written; + return desc.error; +} + +reiser4_internal ssize_t sendfile_unix_file(struct file *file, loff_t *ppos, size_t count, + read_actor_t actor, void __user *target) +{ + ssize_t ret; + struct inode *inode; + unix_file_info_t *ufo; + + inode = file->f_dentry->d_inode; + ufo = unix_file_inode_data(inode); + + down(&inode->i_sem); + inode_set_flag(inode, REISER4_HAS_MMAP); + up(&inode->i_sem); + + get_nonexclusive_access(ufo); + ret = sendfile_common(file, ppos, count, actor, target); + drop_nonexclusive_access(ufo); + return ret; +} + +reiser4_internal int prepare_write_unix_file(struct file *file, struct page *page, + unsigned from, unsigned to) +{ + unix_file_info_t *uf_info; + int ret; + + uf_info = unix_file_inode_data(file->f_dentry->d_inode); + get_exclusive_access(uf_info); + ret = find_file_state(uf_info); + if (ret == 0) { + if (uf_info->container == UF_CONTAINER_TAILS) + ret = -EINVAL; + else + ret = prepare_write_common(file, page, from, to); + } + drop_exclusive_access(uf_info); + return ret; +} + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/file/file.h 2004-09-03 00:57:05.218245968 -0700 @@ -0,0 +1,138 @@ +/* Copyright 2001, 2002, 2003, 2004 by Hans Reiser, licensing governed by + * reiser4/README */ + +#if !defined( __REISER4_FILE_H__ ) +#define __REISER4_FILE_H__ + +/* declarations of functions implementing file plugin for unix file plugin */ +int truncate_unix_file(struct inode *, loff_t size); +int readpage_unix_file(void *, struct page *); +int capturepage_unix_file(struct page *); +int capture_unix_file(struct inode *, const struct writeback_control *, long *); +ssize_t read_unix_file(struct file *, char *buf, size_t size, loff_t *off); +ssize_t write_unix_file(struct file *, const char *buf, size_t size, loff_t *off); +int release_unix_file(struct inode *inode, struct file *); +int ioctl_unix_file(struct inode *, struct file *, unsigned int cmd, unsigned long arg); +int mmap_unix_file(struct file *, struct vm_area_struct *vma); +int get_block_unix_file(struct inode *, sector_t block, struct buffer_head *bh_result, int create); +int flow_by_inode_unix_file(struct inode *, char *buf, int user, loff_t, loff_t, rw_op, flow_t *); +int key_by_inode_unix_file(struct inode *, loff_t off, reiser4_key *); +int owns_item_unix_file(const struct inode *, const coord_t *); +int setattr_unix_file(struct inode *, struct iattr *); +void readpages_unix_file(struct file *, struct address_space *, struct list_head *pages); +void init_inode_data_unix_file(struct inode *, reiser4_object_create_data *, int create); +int pre_delete_unix_file(struct inode *); + +extern ssize_t sendfile_common ( + struct file *file, loff_t *ppos, size_t count, read_actor_t actor, void __user *target); +extern ssize_t sendfile_unix_file ( + struct file *file, loff_t *ppos, size_t count, read_actor_t actor, void __user *target); +extern int prepare_write_unix_file (struct file *, struct page *, unsigned, unsigned); + +int sync_unix_file(struct inode *, int datasync); + + +/* all the write into unix file is performed by item write method. Write method of unix file plugin only decides which + item plugin (extent or tail) and in which mode (one from the enum below) to call */ +typedef enum { + FIRST_ITEM = 1, + APPEND_ITEM = 2, + OVERWRITE_ITEM = 3 +} write_mode_t; + + +/* unix file may be in one the following states */ +typedef enum { + UF_CONTAINER_UNKNOWN = 0, + UF_CONTAINER_TAILS = 1, + UF_CONTAINER_EXTENTS = 2, + UF_CONTAINER_EMPTY = 3 +} file_container_t; + +struct formatting_plugin; +struct inode; + +/* unix file plugin specific part of reiser4 inode */ +typedef struct unix_file_info { + struct rw_semaphore latch; /* this read-write lock protects file containerization change. Accesses which do not change + file containerization (see file_container_t) (read, readpage, writepage, write (until tail + conversion is involved)) take read-lock. Accesses which modify file containerization + (truncate, conversion from tail to extent and back) take write-lock. */ + file_container_t container; /* this enum specifies which items are used to build the file */ + struct formatting_plugin *tplug; /* plugin which controls when file is to be converted to extents and back to + tail */ + /* if this is set, file is in exclusive use */ + int exclusive_use; +#if REISER4_DEBUG + void *ea_owner; /* pointer to task struct of thread owning exclusive + * access to file */ +#endif +} unix_file_info_t; + +struct unix_file_info *unix_file_inode_data(const struct inode * inode); + +#include "../item/extent.h" +#include "../item/tail.h" + +struct uf_coord { + coord_t base_coord; + lock_handle *lh; + int valid; + union { + extent_coord_extension_t extent; + tail_coord_extension_t tail; + } extension; +}; + +#include "../../seal.h" + +/* structure used to speed up file operations (reads and writes). It contains + * a seal over last file item accessed. */ +struct hint { + seal_t seal; + uf_coord_t coord; + loff_t offset; + tree_level level; + znode_lock_mode mode; +}; + +void set_hint(hint_t *, const reiser4_key *, znode_lock_mode); +void unset_hint(hint_t *); +int hint_validate(hint_t *, const reiser4_key *, int check_key, znode_lock_mode); + + +#if REISER4_DEBUG +static inline struct task_struct * +inode_ea_owner(const unix_file_info_t *uf_info) +{ + return uf_info->ea_owner; +} + +static inline void ea_set(unix_file_info_t *uf_info, void *value) +{ + uf_info->ea_owner = value; +} +#else +#define ea_set(inode, value) noop +#endif + +static inline int ea_obtained(const unix_file_info_t *uf_info) +{ + assert("vs-1167", ergo (inode_ea_owner(uf_info) != NULL, + inode_ea_owner(uf_info) == current)); + return uf_info->exclusive_use; +} + +/* __REISER4_FILE_H__ */ +#endif + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/file/funcs.h 2004-09-03 00:57:05.219245816 -0700 @@ -0,0 +1,28 @@ +/* Copyright 2001, 2002, 2003, 2004 by Hans Reiser, licensing governed by reiser4/README */ + +/* this prototyles functions used by both file.c and tail_conversion.c */ +void get_exclusive_access(unix_file_info_t *); +void drop_exclusive_access(unix_file_info_t *); +void get_nonexclusive_access(unix_file_info_t *); +void drop_nonexclusive_access(unix_file_info_t *); +void drop_access(unix_file_info_t *uf_info); + +int tail2extent(unix_file_info_t *); +int extent2tail(unix_file_info_t *); +int finish_conversion(struct inode *inode); + +void hint_init_zero(hint_t *, lock_handle *); +int find_file_item(hint_t *, const reiser4_key *, znode_lock_mode, + ra_info_t *, struct inode *); +int find_file_item_nohint(coord_t *, lock_handle *, const reiser4_key *, + znode_lock_mode, struct inode *); + +int goto_right_neighbor(coord_t *, lock_handle *); +int find_or_create_extent(struct page *); +write_mode_t how_to_write(uf_coord_t *, const reiser4_key *); + +extern inline int +cbk_errored(int cbk_result) +{ + return (cbk_result != CBK_COORD_NOTFOUND && cbk_result != CBK_COORD_FOUND); +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/file/invert.c 2004-09-03 00:57:05.221245512 -0700 @@ -0,0 +1,511 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Suppose you want to conveniently read and write a large variety of small files conveniently within a single emacs + buffer, without having a separate buffer for each 8 byte or so file. Inverts are the way to do that. An invert + provides you with the contents of a set of subfiles plus its own contents. It is a file which inherits other files + when you read it, and allows you to write to it and through it to the files that it inherits from. In order for it + to know which subfiles each part of your write should go into, there must be delimiters indicating that. It tries to + make that easy for you by providing those delimiters in what you read from it. + + When you read it, an invert performs an inverted assignment. Instead of taking an assignment command and writing a + bunch of files, it takes a bunch of files and composes an assignment command for you to read from it that if executed + would create those files. But which files? Well, that must be specified in the body of the invert using a special + syntax, and that specification is called the invert of the assignment. + + When written to, an invert performs the assignment command that is written + to it, and modifies its own body to contain the invert of that + assignment. + + In other words, writing to an invert file what you have read from it + is the identity operation. + + Malformed assignments cause write errors. Partial writes are not + supported in v4.0, but will be. + + Example: + + If an invert contains: + + /filenameA/<>+"(some text stored in the invert)+/filenameB/<> + +====================== +Each element in this definition should be an invert, and all files +should be called recursively - too. This is bad. If one of the +included files in not a regular or invert file, then we can't read +main file. + +I think to make it is possible easier: + +internal structure of invert file should be like symlink file. But +read and write method should be explitely indicated in i/o operation.. + +By default we read and write (if probably) as symlink and if we +specify ..invert at reading time that too we can specify it at write time. + +example: +/my_invert_file/..invert<- ( (/filenameA<-"(The contents of filenameA))+"(some text stored in the invert)+(/filenameB<-"(The contents of filenameB) ) ) +will create /my_invert_file as invert, and will creat /filenameA and /filenameB with specified body. + +read of /my_invert_file/..invert will be +/filenameA<-"(The contents of filenameA)+"(some text stored in the invert)+/filenameB<-"(The contents of filenameB) + +but read of /my_invert_file/ will be +The contents of filenameAsome text stored in the invertThe contents of filenameB + +we also can creat this file as +/my_invert_file/<-/filenameA+"(some text stored in the invert)+/filenameB +will create /my_invert_file , and use existing files /filenameA and /filenameB. + +and when we will read it will be as previously invert file. + +This is correct? + + vv +DEMIDOV-FIXME-HANS: + +Maybe you are right, but then you must disable writes to /my_invert_file/ and only allow writes to /my_invert_file/..invert + +Do you agree? Discuss it on reiserfs-list.... + +-Hans +======================= + + Then a read will return: + + /filenameA<-"(The contents of filenameA)+"(some text stored in the invert)+/filenameB<-"(The contents of filenameB) + + and a write of the line above to the invert will set the contents of + the invert and filenameA and filenameB to their original values. + + Note that the contents of an invert have no influence on the effect + of a write unless the write is a partial write (and a write of a + shorter file without using truncate first is a partial write). + + truncate() has no effect on filenameA and filenameB, it merely + resets the value of the invert. + + Writes to subfiles via the invert are implemented by preceding them + with truncates. + + Parse failures cause write failures. + + Questions to ponder: should the invert be acted on prior to file + close when writing to an open filedescriptor? + + Example: + + If an invert contains: + + "(This text and a pair of quotes are all that is here.) + +Then a read will return: + + "(This text and a pair of quotes are all that is here.) + +*/ + +/* OPEN method places a struct file in memory associated with invert body + and returns something like file descriptor to the user for the future access + to the invert file. + During opening we parse the body of invert and get a list of the 'entryes' + (that describes all its subfiles) and place pointer on the first struct in + reiserfs-specific part of invert inode (arbitrary decision). + + Each subfile is described by the struct inv_entry that has a pointer @sd on + in-core based stat-data and a pointer on struct file @f (if we find that the + subfile uses more then one unformated node (arbitrary decision), we load + struct file in memory, otherwise we load base stat-data (and maybe 1-2 bytes + of some other information we need) + + Since READ and WRITE methods for inverts were formulated in assignment + language, they don't contain arguments 'size' and 'offset' that make sense + only in ordinary read/write methods. + + READ method is a combination of two methods: + 1) ordinary read method (with offset=0, lenght = @f->...->i_size) for entries + with @f != 0, this method uses pointer on struct file as an argument + 2) read method for inode-less files with @sd != 0, this method uses + in-core based stat-data instead struct file as an argument. + in the first case we don't use pagecache, just copy data that we got after + cbk() into userspace. + + WRITE method for invert files is more complex. + Besides declared WRITE-interface in assignment languageb above we need + to have an opportunity to edit unwrapped body of invert file with some + text editor, it means we need GENERIC WRITE METHOD for invert file: + + my_invert_file/..invert <- "string" + + this method parses "string" and looks for correct subfile signatures, also + the parsing process splits this "string" on the set of flows in accordance + with the set of subfiles specified by this signarure. + The found list of signatures #S is compared with the opened one #I of invert + file. If it doesn't have this one (#I==0, it will be so for instance if we + have just create this invert file) the write method assignes found signature + (#I=#S;) to the invert file. Then if #I==#S, generic write method splits + itself to the some write methods for ordinary or light-weight, or call itself + recursively for invert files with corresponding flows. + I am not sure, but the list of signatures looks like what mr.Demidov means + by 'delimiters'. + + The cases when #S<#I (#I<#S) (in the sense of set-theory) are also available + and cause delete (create new) subfiles (arbitrary decision - it may looks + too complex, but this interface will be the completest). The order of entries + of list #S (#I) and inherited order on #I (#S) must coincide. + The other parsing results give malformed signature that aborts READ method + and releases all resources. + + + Format of subfile (entry) signature: + + "START_MAGIC"<>(TYPE="...",LOOKUP_ARG="...")SUBFILE_BODY"END_MAGIC" + + Legend: + + START_MAGIC - keyword indicates the start of subfile signature; + + <> indicates the start of 'subfile metadata', that is the pair + (TYPE="...",LOOKUP_ARG="...") in parenthesis separated by comma. + + TYPE - the string "type" indicates the start of one of the three words: + - ORDINARY_FILE, + - LIGHT_WEIGHT_FILE, + - INVERT_FILE; + + LOOKUP_ARG - lookup argument depends on previous type: + */ + + /************************************************************/ + /* TYPE * LOOKUP ARGUMENT */ + /************************************************************/ + /* LIGH_WEIGHT_FILE * stat-data key */ + /************************************************************/ + /* ORDINARY_FILE * filename */ + /************************************************************/ + /* INVERT_FILE * filename */ + /************************************************************/ + + /* where: + *stat-data key - the string contains stat data key of this subfile, it will be + passed to fast-access lookup method for light-weight files; + *filename - pathname of this subfile, iyt well be passed to VFS lookup methods + for ordinary and invert files; + + SUBFILE_BODY - data of this subfile (it will go to the flow) + END_MAGIC - the keyword indicates the end of subfile signature. + + The other simbols inside the signature interpreted as 'unformatted content', + which is available with VFS's read_link() (arbitraruy decision). + + NOTE: Parse method for a body of invert file uses mentioned signatures _without_ + subfile bodies. + + Now the only unclear thing is WRITE in regular light-weight subfile A that we + can describe only in assignment language: + + A <- "some_string" + + I guess we don't want to change stat-data and body items of file A + if this file exist, and size(A) != size("some_string") because this operation is + expencive, so we only do the partial write if size(A) > size("some_string") + and do truncate of the "some_string", and then do A <- "truncated string", if + size(A) < size("some_string"). This decision is also arbitrary.. + */ + +/* here is infrastructure for formated flows */ + +#define SUBFILE_HEADER_MAGIC 0x19196605 +#define FLOW_HEADER_MAGIC 0x01194304 + +#include "../plugin.h" +#include "../../debug.h" +#include "../../forward.h" +#include "../object.h" +#include "../item/item.h" +#include "../item/static_stat.h" +#include "../../dformat.h" +#include "../znode.h" +#include "../inode.h" + +#include +#include /* for struct file */ +#include /* for struct list_head */ + +typedef enum { + LIGHT_WEIGHT_FILE, + ORDINARY_FILE, + INVERT_FILE +} inv_entry_type; + +typedef struct flow_header { + d32 fl_magic; + d16 fl_nr; /* number of subfiles in the flow */ +}; + +typedef struct subfile_header { + d32 sh_magic; /* subfile magic */ + d16 sh_type; /* type of subfile: light-weight, ordinary, invert */ + d16 sh_arg_len; /* lenght of lookup argument (filename, key) */ + d32 sh_body_len; /* lenght of subfile body */ +}; + +/* functions to get/set fields of flow header */ + +static void +fl_set_magic(flow_header * fh, __u32 value) +{ + cputod32(value, &fh->fh_magic); +} + +static __u32 +fl_get_magic(flow_header * fh) +{ + return d32tocpu(&fh->fh_magic); +} +static void +fl_set_number(flow_header * fh, __u16 value) +{ + cputod16(value, &fh->fh_nr); +} +static unsigned +fl_get_number(flow_header * fh) +{ + return d16tocpu(&fh->fh_nr); +} + +/* functions to get/set fields of subfile header */ + +static void +sh_set_magic(subfile_header * sh, __u32 value) +{ + cputod32(value, &sh->sh_magic); +} + +static __u32 +sh_get_magic(subfile_header * sh) +{ + return d32tocpu(&sh->sh_magic); +} +static void +sh_set_type(subfile_header * sh, __u16 value) +{ + cputod16(value, &sh->sh_magic); +} +static unsigned +sh_get_type(subfile_header * sh) +{ + return d16tocpu(&sh->sh_magic); +} +static void +sh_set_arg_len(subfile_header * sh, __u16 value) +{ + cputod16(value, &sh->sh_arg_len); +} +static unsigned +sh_get_arg_len(subfile_header * sh) +{ + return d16tocpu(&sh->sh_arg_len); +} +static void +sh_set_body_len(subfile_header * sh, __u32 value) +{ + cputod32(value, &sh->sh_body_len); +} + +static __u32 +sh_get_body_len(subfile_header * sh) +{ + return d32tocpu(&sh->sh_body_len); +} + +/* in-core minimal stat-data, light-weight analog of inode */ + +struct incore_sd_base { + umode_t isd_mode; + nlink_t isd_nlink; + loff_t isd_size; + char *isd_data; /* 'subflow' to write */ +}; + +/* open invert create a list of invert entries, + every entry is represented by structure inv_entry */ + +struct inv_entry { + struct list_head *ie_list; + struct file *ie_file; /* this is NULL if the file doesn't + have unformated nodes */ + struct incore_sd_base *ie_sd; /* inode-less analog of struct file */ +}; + +/* allocate and init invert entry */ + +static struct inv_entry * +allocate_inv_entry(void) +{ + struct inv_entry *inv_entry; + + inv_entry = reiser4_kmalloc(sizeof (struct inv_entry), GFP_KERNEL); + if (!inv_entry) + return ERR_PTR(RETERR(-ENOMEM)); + inv_entry->ie_file = NULL; + inv_entry->ie_sd = NULL; + INIT_LIST_HEAD(&inv_entry->ie_list); + return inv_entry; +} + +static int +put_inv_entry(struct inv_entry *ientry) +{ + int result = 0; + + assert("edward-96", ientry != NULL); + assert("edward-97", ientry->ie_list != NULL); + + list_del(ientry->ie_list); + if (ientry->ie_sd != NULL) { + kfree(ientry->ie_sd); + kfree(ientry); + } + if (ientry->ie_file != NULL) + result = filp_close(ientry->file, NULL); + return result; +} + +static int +allocate_incore_sd_base(struct inv_entry *inv_entry) +{ + struct incore_sd_base *isd_base assert("edward-98", inv_entry != NULL); + assert("edward-99", inv_entry->ie_inode = NULL); + assert("edward-100", inv_entry->ie_sd = NULL); + + isd_base = reiser4_kmalloc(sizeof (struct incore_sd_base), GFP_KERNEL); + if (!isd_base) + return RETERR(-ENOMEM); + inv_entry->ie_sd = isd_base; + return 0; +} + +/* this can be installed as ->init_inv_entry () method of + item_plugins[ STATIC_STAT_DATA_IT ] (fs/reiser4/plugin/item/item.c). + Copies data from on-disk stat-data format into light-weight analog of inode . + Doesn't hanlde stat-data extensions. */ + +static void +sd_base_load(struct inv_entry *inv_entry, char *sd) +{ + reiser4_stat_data_base *sd_base; + + assert("edward-101", inv_entry != NULL); + assert("edward-101", inv_entry->ie_sd != NULL); + assert("edward-102", sd != NULL); + + sd_base = (reiser4_stat_data_base *) sd; + inv_entry->incore_sd_base->isd_mode = d16tocpu(&sd_base->mode); + inv_entry->incore_sd_base->isd_nlink = d32tocpu(&sd_base->nlink); + inv_entry->incore_sd_base->isd_size = d64tocpu(&sd_base->size); + inv_entry->incore_sd_base->isd_data = NULL; +} + +/* initialise incore stat-data */ + +static void +init_incore_sd_base(struct inv_entry *inv_entry, coord_t * coord) +{ + reiser4_plugin *plugin = item_plugin_by_coord(coord); + void *body = item_body_by_coord(coord); + + assert("edward-103", inv_entry != NULL); + assert("edward-104", plugin != NULL); + assert("edward-105", body != NULL); + + sd_base_load(inv_entry, body); +} + +/* takes a key or filename and allocates new invert_entry, + init and adds it into the list, + we use lookup_sd_by_key() for light-weight files and VFS lookup by filename */ + +int +get_inv_entry(struct inode *invert_inode, /* inode of invert's body */ + inv_entry_type type, /* LIGHT-WEIGHT or ORDINARY */ + const reiser4_key * key, /* key of invert entry stat-data */ + char *filename, /* filename of the file to be opened */ + int flags, int mode) +{ + int result; + struct inv_entry *ientry; + + assert("edward-107", invert_inode != NULL); + + ientry = allocate_inv_entry(); + if (IS_ERR(ientry)) + return (PTR_ERR(ientry)); + + if (type == LIGHT_WEIGHT_FILE) { + coord_t coord; + lock_handle lh; + + assert("edward-108", key != NULL); + + init_coord(&coord); + init_lh(&lh); + result = lookup_sd_by_key(tree_by_inode(invert_inode), ZNODE_READ_LOCK, &coord, &lh, key); + if (result == 0) + init_incore_sd_base(ientry, coord); + + done_lh(&lh); + done_coord(&coord); + return (result); + } else { + struct file *file = filp_open(filename, flags, mode); + /* FIXME_EDWARD here we need to check if we + did't follow to any mount point */ + + assert("edward-108", filename != NULL); + + if (IS_ERR(file)) + return (PTR_ERR(file)); + ientry->ie_file = file; + return 0; + } +} + +/* takes inode of invert, reads the body of this invert, parses it, + opens all invert entries and return pointer on the first inv_entry */ + +struct inv_entry * +open_invert(struct file *invert_file) +{ + +} + +ssize_t subfile_read(struct *invert_entry, flow * f) +{ + +} + +ssize_t subfile_write(struct *invert_entry, flow * f) +{ + +} + +ssize_t invert_read(struct *file, flow * f) +{ + +} + +ssize_t invert_write(struct *file, flow * f) +{ + +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/file/pseudo.c 2004-09-03 00:57:05.222245360 -0700 @@ -0,0 +1,182 @@ +/* Copyright 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* + * Pseudo file plugin. This contains helper functions used by pseudo files. + */ + +#include "pseudo.h" +#include "../plugin.h" + +#include "../../inode.h" + +#include +#include + +struct seq_operations pseudo_seq_op; + +/* extract pseudo file plugin, stored in @file */ +static pseudo_plugin * +get_pplug(struct file * file) +{ + struct inode *inode; + + inode = file->f_dentry->d_inode; + return reiser4_inode_data(inode)->file_plugin_data.pseudo_info.plugin; +} + +/* common routine to open pseudo file. */ +reiser4_internal int open_pseudo(struct inode * inode, struct file * file) +{ + int result; + pseudo_plugin *pplug; + + pplug = get_pplug(file); + + /* for pseudo files based on seq_file interface */ + if (pplug->read_type == PSEUDO_READ_SEQ) { + result = seq_open(file, &pplug->read.ops); + if (result == 0) { + struct seq_file *m; + + m = file->private_data; + m->private = file; + } + } else if (pplug->read_type == PSEUDO_READ_SINGLE) + /* for pseudo files containing one record */ + result = single_open(file, pplug->read.single_show, file); + else + result = 0; + + return result; +} + +/* common read method for pseudo files */ +reiser4_internal ssize_t read_pseudo(struct file *file, + char __user *buf, size_t size, loff_t *ppos) +{ + switch (get_pplug(file)->read_type) { + case PSEUDO_READ_SEQ: + case PSEUDO_READ_SINGLE: + /* seq_file behaves like pipe, requiring @ppos to always be + * address of file->f_pos */ + return seq_read(file, buf, size, &file->f_pos); + case PSEUDO_READ_FORWARD: + return get_pplug(file)->read.read(file, buf, size, ppos); + default: + return 0; + } +} + +/* common seek method for pseudo files */ +reiser4_internal loff_t seek_pseudo(struct file *file, loff_t offset, int origin) +{ + switch (get_pplug(file)->read_type) { + case PSEUDO_READ_SEQ: + case PSEUDO_READ_SINGLE: + return seq_lseek(file, offset, origin); + default: + return 0; + } +} + +/* common release method for pseudo files */ +reiser4_internal int release_pseudo(struct inode *inode, struct file *file) +{ + int result; + + switch (get_pplug(file)->read_type) { + case PSEUDO_READ_SEQ: + case PSEUDO_READ_SINGLE: + result = seq_release(inode, file); + file->private_data = NULL; + break; + default: + result = 0; + } + return result; +} + +/* pseudo files need special ->drop() method, because they don't have nlink + * and only exist while host object does. */ +reiser4_internal void drop_pseudo(struct inode * object) +{ + /* pseudo files are not protected from deletion by their ->i_nlink */ + generic_delete_inode(object); +} + +/* common write method for pseudo files */ +reiser4_internal ssize_t +write_pseudo(struct file *file, + const char __user *buf, size_t size, loff_t *ppos) +{ + ssize_t result; + + switch (get_pplug(file)->write_type) { + case PSEUDO_WRITE_STRING: { + char * inkernel; + + inkernel = getname(buf); + if (!IS_ERR(inkernel)) { + result = get_pplug(file)->write.gets(file, inkernel); + putname(inkernel); + if (result == 0) + result = size; + } else + result = PTR_ERR(inkernel); + break; + } + case PSEUDO_WRITE_FORWARD: + result = get_pplug(file)->write.write(file, buf, size, ppos); + break; + default: + result = size; + } + return result; +} + +/* on-wire serialization of pseudo files. */ + +/* this is not implemented so far (and, hence, pseudo files are not accessible + * over NFS, closing remote exploits a fortiori */ + +reiser4_internal int +wire_size_pseudo(struct inode *inode) +{ + return RETERR(-ENOTSUPP); +} + +reiser4_internal char * +wire_write_pseudo(struct inode *inode, char *start) +{ + return ERR_PTR(RETERR(-ENOTSUPP)); +} + +reiser4_internal char * +wire_read_pseudo(char *addr, reiser4_object_on_wire *obj) +{ + return ERR_PTR(RETERR(-ENOTSUPP)); +} + +reiser4_internal void +wire_done_pseudo(reiser4_object_on_wire *obj) +{ + /* nothing to do */ +} + +reiser4_internal struct dentry * +wire_get_pseudo(struct super_block *sb, reiser4_object_on_wire *obj) +{ + return ERR_PTR(RETERR(-ENOTSUPP)); +} + + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/file/pseudo.h 2004-09-03 00:57:05.223245208 -0700 @@ -0,0 +1,39 @@ +/* Copyright 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#if !defined(__REISER4_PSEUDO_FILE_H__) +#define __REISER4_PSEUDO_FILE_H__ + +#include "../plugin.h" + +#include + +extern int open_pseudo(struct inode * inode, struct file * file); +extern ssize_t read_pseudo(struct file *file, + char __user *buf, size_t size, loff_t *ppos); +extern ssize_t write_pseudo(struct file *file, + const char __user *buf, size_t size, loff_t *ppos); +extern loff_t seek_pseudo(struct file *file, loff_t offset, int origin); +extern int release_pseudo(struct inode *inode, struct file *file); +extern void drop_pseudo(struct inode * object); + +extern int wire_size_pseudo(struct inode *inode); +extern char *wire_write_pseudo(struct inode *inode, char *start); +extern char *wire_read_pseudo(char *addr, reiser4_object_on_wire *obj); +extern void wire_done_pseudo(reiser4_object_on_wire *obj); +extern struct dentry *wire_get_pseudo(struct super_block *sb, + reiser4_object_on_wire *obj); + +/* __REISER4_PSEUDO_FILE_H__ */ +#endif + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/file/symfile.c 2004-09-03 00:57:05.223245208 -0700 @@ -0,0 +1,98 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Symfiles are a generalization of Unix symlinks. + + A symfile when read behaves as though you took its contents and + substituted them into the reiser4 naming system as the right hand side + of an assignment, and then read that which you had assigned to it. + + A key issue for symfiles is how to implement writes through to + subfiles. In general, one must have some method of determining what + of that which is written to the symfile is written to what subfile. + This can be done by use of custom plugin methods written by users, or + by using a few general methods we provide for those willing to endure + the insertion of delimiters into what is read. + + Writing to symfiles without delimiters to denote what is written to + what subfile is not supported by any plugins we provide in this + release. Our most sophisticated support for writes is that embodied + by the invert plugin (see invert.c). + + A read only version of the /etc/passwd file might be + constructed as a symfile whose contents are as follows: + + /etc/passwd/userlines/* + + or + + /etc/passwd/userlines/demidov+/etc/passwd/userlines/edward+/etc/passwd/userlines/reiser+/etc/passwd/userlines/root + + or + + /etc/passwd/userlines/(demidov+edward+reiser+root) + + A symfile with contents + + /filenameA+"(some text stored in the uninvertable symfile)+/filenameB + + will return when read + + The contents of filenameAsome text stored in the uninvertable symfileThe contents of filenameB + + and write of what has been read will not be possible to implement as + an identity operation because there are no delimiters denoting the + boundaries of what is to be written to what subfile. + + Note that one could make this a read/write symfile if one specified + delimiters, and the write method understood those delimiters delimited + what was written to subfiles. + + So, specifying the symfile in a manner that allows writes: + + /etc/passwd/userlines/demidov+"( + )+/etc/passwd/userlines/edward+"( + )+/etc/passwd/userlines/reiser+"( + )+/etc/passwd/userlines/root+"( + ) + + or + + /etc/passwd/userlines/(demidov+"( + )+edward+"( + )+reiser+"( + )+root+"( + )) + + and the file demidov might be specified as: + + /etc/passwd/userlines/demidov/username+"(:)+/etc/passwd/userlines/demidov/password+"(:)+/etc/passwd/userlines/demidov/userid+"(:)+/etc/passwd/userlines/demidov/groupid+"(:)+/etc/passwd/userlines/demidov/gecos+"(:)+/etc/passwd/userlines/demidov/home+"(:)+/etc/passwd/userlines/demidov/shell + + or + + /etc/passwd/userlines/demidov/(username+"(:)+password+"(:)+userid+"(:)+groupid+"(:)+gecos+"(:)+home+"(:)+shell) + + Notice that if the file demidov has a carriage return in it, the + parsing fails, but then if you put carriage returns in the wrong place + in a normal /etc/passwd file it breaks things also. + + Note that it is forbidden to have no text between two interpolations + if one wants to be able to define what parts of a write go to what + subfiles referenced in an interpolation. + + If one wants to be able to add new lines by writing to the file, one + must either write a custom plugin for /etc/passwd that knows how to + name an added line, or one must use an invert, or one must use a more + sophisticated symfile syntax that we are not planning to write for + version 4.0. +*/ + + + + + + + + + + + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/file/tail_conversion.c 2004-09-03 00:57:05.227244600 -0700 @@ -0,0 +1,721 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#include "../../inode.h" +#include "../../super.h" +#include "../../page_cache.h" +#include "../../carry.h" +#include "../../lib.h" +#include "../../safe_link.h" +#include "../../vfs_ops.h" +#include "funcs.h" + +#include + +/* this file contains: + tail2extent and extent2tail */ + + +/* exclusive access to a file is acquired when file state changes: tail2extent, empty2tail, extent2tail, etc */ +reiser4_internal void +get_exclusive_access(unix_file_info_t *uf_info) +{ + assert("nikita-3028", schedulable()); + assert("nikita-3047", LOCK_CNT_NIL(inode_sem_w)); + assert("nikita-3048", LOCK_CNT_NIL(inode_sem_r)); + /* + * "deadlock detection": sometimes we commit a transaction under + * rw-semaphore on a file. Such commit can deadlock with another + * thread that captured some block (hence preventing atom from being + * committed) and waits on rw-semaphore. + */ + assert("nikita-3361", get_current_context()->trans->atom == NULL); + BUG_ON(get_current_context()->trans->atom != NULL); + LOCK_CNT_INC(inode_sem_w); + down_write(&uf_info->latch); + assert("nikita-3060", inode_ea_owner(uf_info) == NULL); + assert("vs-1157", !ea_obtained(uf_info)); + ea_set(uf_info, current); + uf_info->exclusive_use = 1; +} + +reiser4_internal void +drop_exclusive_access(unix_file_info_t *uf_info) +{ + assert("nikita-3060", inode_ea_owner(uf_info) == current); + assert("vs-1158", ea_obtained(uf_info)); + ea_set(uf_info, 0); + uf_info->exclusive_use = 0; + up_write(&uf_info->latch); + assert("nikita-3049", LOCK_CNT_NIL(inode_sem_r)); + assert("nikita-3049", LOCK_CNT_GTZ(inode_sem_w)); + LOCK_CNT_DEC(inode_sem_w); +} + +/* nonexclusive access to a file is acquired for read, write, readpage */ +reiser4_internal void +get_nonexclusive_access(unix_file_info_t *uf_info) +{ + assert("nikita-3029", schedulable()); + down_read(&uf_info->latch); + LOCK_CNT_INC(inode_sem_r); + assert("nikita-3060", inode_ea_owner(uf_info) == NULL); + assert("vs-1159", !ea_obtained(uf_info)); +} + +reiser4_internal void +drop_nonexclusive_access(unix_file_info_t *uf_info) +{ + assert("nikita-3060", inode_ea_owner(uf_info) == NULL); + assert("vs-1160", !ea_obtained(uf_info)); + up_read(&uf_info->latch); + LOCK_CNT_DEC(inode_sem_r); +} + +/* part of tail2extent. Cut all items covering @count bytes starting from + @offset */ +/* Audited by: green(2002.06.15) */ +static int +cut_formatting_items(struct inode *inode, loff_t offset, int count) +{ + reiser4_key from, to; + + /* AUDIT: How about putting an assertion here, what would check + all provided range is covered by tail items only? */ + /* key of first byte in the range to be cut */ + key_by_inode_unix_file(inode, offset, &from); + + /* key of last byte in that range */ + to = from; + set_key_offset(&to, (__u64) (offset + count - 1)); + + /* cut everything between those keys */ + return cut_tree(tree_by_inode(inode), &from, &to, inode, 1); +} + +static void +release_all_pages(struct page **pages, unsigned nr_pages) +{ + unsigned i; + + for (i = 0; i < nr_pages; i++) { + if (pages[i] == NULL) { + unsigned j; + for (j = i + 1; j < nr_pages; j ++) + assert("vs-1620", pages[j] == NULL); + break; + } + page_cache_release(pages[i]); + pages[i] = NULL; + } +} + +/* part of tail2extent. replace tail items with extent one. Content of tail + items (@count bytes) being cut are copied already into + pages. extent_writepage method is called to create extents corresponding to + those pages */ +static int +replace(struct inode *inode, struct page **pages, unsigned nr_pages, int count) +{ + int result; + unsigned i; + STORE_COUNTERS; + + if (nr_pages == 0) + return 0; + + assert("vs-596", pages[0]); + + /* cut copied items */ + result = cut_formatting_items(inode, (loff_t) pages[0]->index << PAGE_CACHE_SHIFT, count); + if (result) + return result; + + CHECK_COUNTERS; + + /* put into tree replacement for just removed items: extent item, namely */ + for (i = 0; i < nr_pages; i++) { + result = add_to_page_cache_lru(pages[i], inode->i_mapping, + pages[i]->index, mapping_gfp_mask(inode->i_mapping)); + if (result) + break; + unlock_page(pages[i]); + result = find_or_create_extent(pages[i]); + if (result) + break; + SetPageUptodate(pages[i]); + } + return result; +} + +#define TAIL2EXTENT_PAGE_NUM 3 /* number of pages to fill before cutting tail + * items */ + +static int +reserve_tail2extent_iteration(struct inode *inode) +{ + reiser4_block_nr unformatted_nodes; + reiser4_tree *tree; + + tree = tree_by_inode(inode); + + /* number of unformatted nodes which will be created */ + unformatted_nodes = TAIL2EXTENT_PAGE_NUM; + + /* + * space required for one iteration of extent->tail conversion: + * + * 1. kill N tail items + * + * 2. insert TAIL2EXTENT_PAGE_NUM unformatted nodes + * + * 3. insert TAIL2EXTENT_PAGE_NUM (worst-case single-block + * extents) extent units. + * + * 4. drilling to the leaf level by coord_by_key() + * + * 5. possible update of stat-data + * + */ + grab_space_enable(); + return reiser4_grab_space + (2 * tree->height + + TAIL2EXTENT_PAGE_NUM + + TAIL2EXTENT_PAGE_NUM * estimate_one_insert_into_item(tree) + + 1 + estimate_one_insert_item(tree) + + inode_file_plugin(inode)->estimate.update(inode), + BA_CAN_COMMIT); +} + +/* this is used by tail2extent and extent2tail to detect where previous uncompleted conversion stopped */ +static int +find_start(struct inode *object, reiser4_plugin_id id, __u64 *offset) +{ + int result; + lock_handle lh; + coord_t coord; + unix_file_info_t *ufo; + int found; + reiser4_key key; + + ufo = unix_file_inode_data(object); + init_lh(&lh); + result = 0; + found = 0; + key_by_inode_unix_file(object, *offset, &key); + do { + init_lh(&lh); + result = find_file_item_nohint(&coord, &lh, &key, + ZNODE_READ_LOCK, object); + + if (result == CBK_COORD_FOUND) { + if (coord.between == AT_UNIT) { + /*coord_clear_iplug(&coord);*/ + result = zload(coord.node); + if (result == 0) { + if (item_id_by_coord(&coord) == id) + found = 1; + else + item_plugin_by_coord(&coord)->s.file.append_key(&coord, &key); + zrelse(coord.node); + } + } else + result = RETERR(-ENOENT); + } + done_lh(&lh); + } while (result == 0 && !found); + *offset = get_key_offset(&key); + return result; +} + +/* clear stat data's flag indicating that conversion is being converted */ +static int +complete_conversion(struct inode *inode) +{ + int result; + + all_grabbed2free(); + grab_space_enable(); + result = reiser4_grab_space(inode_file_plugin(inode)->estimate.update(inode), + BA_CAN_COMMIT); + if (result == 0) { + inode_clr_flag(inode, REISER4_PART_CONV); + result = reiser4_update_sd(inode); + } + if (result) + warning("vs-1696", "Failed to clear converting bit of %llu: %i", + get_inode_oid(inode), result); + return 0; +} + +reiser4_internal int +tail2extent(unix_file_info_t *uf_info) +{ + int result; + reiser4_key key; /* key of next byte to be moved to page */ + ON_DEBUG(reiser4_key tmp;) + char *p_data; /* data of page */ + unsigned page_off = 0, /* offset within the page where to copy data */ + count; /* number of bytes of item which can be + * copied to page */ + struct page *pages[TAIL2EXTENT_PAGE_NUM]; + struct page *page; + int done; /* set to 1 when all file is read */ + char *item; + int i; + struct inode *inode; + __u64 offset; + int first_iteration; + int bytes; + + /* collect statistics on the number of tail2extent conversions */ + reiser4_stat_inc(file.tail2extent); + + assert("nikita-3362", ea_obtained(uf_info)); + inode = unix_file_info_to_inode(uf_info); + assert("nikita-3412", !IS_RDONLY(inode)); + assert("vs-1649", uf_info->container != UF_CONTAINER_EXTENTS); + + offset = 0; + if (inode_get_flag(inode, REISER4_PART_CONV)) { + /* find_start() doesn't need block reservation */ + result = find_start(inode, FORMATTING_ID, &offset); + if (result == -ENOENT) + /* no extent found, everything is converted */ + return 0; + else if (result != 0) + /* some other error */ + return result; + } + + /* get key of first byte of a file */ + key_by_inode_unix_file(inode, offset, &key); + + done = 0; + result = 0; + first_iteration = 1; + while (done == 0) { + xmemset(pages, 0, sizeof (pages)); + all_grabbed2free(); + result = reserve_tail2extent_iteration(inode); + if (result != 0) + goto out; + if (first_iteration) { + inode_set_flag(inode, REISER4_PART_CONV); + reiser4_update_sd(inode); + first_iteration = 0; + } + bytes = 0; + for (i = 0; i < sizeof_array(pages) && done == 0; i++) { + assert("vs-598", (get_key_offset(&key) & ~PAGE_CACHE_MASK) == 0); + page = alloc_page(mapping_gfp_mask(inode->i_mapping)); + if (!page) { + result = RETERR(-ENOMEM); + goto error; + } + + page->index = (unsigned long) (get_key_offset(&key) >> PAGE_CACHE_SHIFT); + /* usually when one is going to longterm lock znode (as + find_file_item does, for instance) he must not hold + locked pages. However, there is an exception for + case tail2extent. Pages appearing here are not + reachable to everyone else, they are clean, they do + not have jnodes attached so keeping them locked do + not risk deadlock appearance + */ + assert("vs-983", !PagePrivate(page)); + + for (page_off = 0; page_off < PAGE_CACHE_SIZE;) { + coord_t coord; + lock_handle lh; + + /* get next item */ + /* FIXME: we might want to readahead here */ + init_lh(&lh); + result = find_file_item_nohint(&coord, &lh, &key, ZNODE_READ_LOCK, inode); + if (cbk_errored(result) || result == CBK_COORD_NOTFOUND) { + /* error happened of not items of file were found */ + done_lh(&lh); + page_cache_release(page); + goto error; + } + + if (coord.between == AFTER_UNIT) { + /* this is used to detect end of file when inode->i_size can not be used */ + done_lh(&lh); + done = 1; + p_data = kmap_atomic(page, KM_USER0); + xmemset(p_data + page_off, 0, PAGE_CACHE_SIZE - page_off); + kunmap_atomic(p_data, KM_USER0); + break; + } + + result = zload(coord.node); + if (result) { + page_cache_release(page); + done_lh(&lh); + goto error; + } + assert("vs-562", owns_item_unix_file(inode, &coord)); + assert("vs-856", coord.between == AT_UNIT); + assert("green-11", keyeq(&key, unit_key_by_coord(&coord, &tmp))); + item = ((char *)item_body_by_coord(&coord)) + coord.unit_pos; + + /* how many bytes to copy */ + count = item_length_by_coord(&coord) - coord.unit_pos; + /* limit length of copy to end of page */ + if (count > PAGE_CACHE_SIZE - page_off) + count = PAGE_CACHE_SIZE - page_off; + + /* kmap/kunmap are necessary for pages which are not addressable by direct kernel + virtual addresses */ + p_data = kmap_atomic(page, KM_USER0); + /* copy item (as much as will fit starting from the beginning of the item) into the + page */ + memcpy(p_data + page_off, item, (unsigned) count); + kunmap_atomic(p_data, KM_USER0); + + page_off += count; + bytes += count; + set_key_offset(&key, get_key_offset(&key) + count); + + zrelse(coord.node); + done_lh(&lh); + } /* end of loop which fills one page by content of formatting items */ + + if (page_off) { + /* something was copied into page */ + pages[i] = page; + } else { + page_cache_release(page); + assert("vs-1648", done == 1); + break; + } + } /* end of loop through pages of one conversion iteration */ + + if (i > 0) { + result = replace(inode, pages, i, bytes); + release_all_pages(pages, sizeof_array(pages)); + if (result) + goto error; + /* throttle the conversion */ + reiser4_throttle_write(inode); + } + } + + if (result == 0) { + /* file is converted to extent items */ + assert("vs-1697", inode_get_flag(inode, REISER4_PART_CONV)); + + uf_info->container = UF_CONTAINER_EXTENTS; + complete_conversion(inode); + } else { + /* conversion is not complete. Inode was already marked as + * REISER4_PART_CONV and stat-data were updated at the first + * iteration of the loop above. */ + error: + release_all_pages(pages, sizeof_array(pages)); + warning("nikita-2282", "Partial conversion of %llu: %i", + get_inode_oid(inode), result); + print_inode("inode", inode); + } + + out: + all_grabbed2free(); + return result; +} + + +/* part of extent2tail. Page contains data which are to be put into tree by + tail items. Use tail_write for this. flow is composed like in + unix_file_write. The only difference is that data for writing are in + kernel space */ +/* Audited by: green(2002.06.15) */ +static int +write_page_by_tail(struct inode *inode, struct page *page, unsigned count) +{ + flow_t f; + hint_t hint; + coord_t *coord; + lock_handle lh; + znode *loaded; + item_plugin *iplug; + int result; + + result = 0; + + assert("vs-1089", count); + assert("vs-1647", inode_file_plugin(inode)->flow_by_inode == flow_by_inode_unix_file); + + /* build flow */ + /* FIXME: do not kmap here */ + flow_by_inode_unix_file(inode, kmap(page), 0 /* not user space */ , + count, (loff_t) (page->index << PAGE_CACHE_SHIFT), WRITE_OP, &f); + iplug = item_plugin_by_id(FORMATTING_ID); + init_lh(&lh); + hint_init_zero(&hint, &lh); + coord = &hint.coord.base_coord; + while (f.length) { + result = find_file_item_nohint(coord, &lh, &f.key, ZNODE_WRITE_LOCK, inode); + if (IS_CBKERR(result)) + break; + + assert("vs-957", ergo(result == CBK_COORD_NOTFOUND, get_key_offset(&f.key) == 0)); + assert("vs-958", ergo(result == CBK_COORD_FOUND, get_key_offset(&f.key) != 0)); + + /*coord_clear_iplug(coord);*/ + result = zload(coord->node); + if (result) + break; + loaded = coord->node; + + result = iplug->s.file.write(inode, &f, &hint, 1/*grabbed*/, how_to_write(&hint.coord, &f.key)); + zrelse(loaded); + done_lh(&lh); + + if (result == -E_REPEAT) + result = 0; + else if (result) + break; + } + + done_lh(&lh); + kunmap(page); + + /* result of write is 0 or error */ + assert("vs-589", result <= 0); + /* if result is 0 - all @count bytes is written completely */ + assert("vs-588", ergo(result == 0, f.length == 0)); + return result; +} + +/* flow insertion is limited by CARRY_FLOW_NEW_NODES_LIMIT of new nodes. Therefore, minimal number of bytes of flow + which can be put into tree by one insert_flow is number of bytes contained in CARRY_FLOW_NEW_NODES_LIMIT nodes if + they all are filled completely by one tail item. Fortunately, there is a one to one mapping between bytes of tail + items and bytes of flow. If there were not, we would have to have special item plugin */ +reiser4_internal int min_bytes_per_flow(void) +{ + assert("vs-1103", current_tree->nplug && current_tree->nplug->max_item_size); + return CARRY_FLOW_NEW_NODES_LIMIT * current_tree->nplug->max_item_size(); +} + +static int +reserve_extent2tail_iteration(struct inode *inode) +{ + reiser4_tree *tree; + + tree = tree_by_inode(inode); + /* + * reserve blocks for (in this order): + * + * 1. removal of extent item + * + * 2. insertion of tail by insert_flow() + * + * 3. drilling to the leaf level by coord_by_key() + * + * 4. possible update of stat-data + */ + grab_space_enable(); + return reiser4_grab_space + (estimate_one_item_removal(tree) + + estimate_insert_flow(tree->height) + + 1 + estimate_one_insert_item(tree) + + inode_file_plugin(inode)->estimate.update(inode), + BA_CAN_COMMIT); +} + +/* for every page of file: read page, cut part of extent pointing to this page, + put data of page tree by tail item */ +reiser4_internal int +extent2tail(unix_file_info_t *uf_info) +{ + int result; + struct inode *inode; + struct page *page; + unsigned long num_pages, i; + unsigned long start_page; + reiser4_key from; + reiser4_key to; + unsigned count; + __u64 offset; + + /* collect statistics on the number of extent2tail conversions */ + reiser4_stat_inc(file.extent2tail); + + assert("nikita-3362", ea_obtained(uf_info)); + inode = unix_file_info_to_inode(uf_info); + assert("nikita-3412", !IS_RDONLY(inode)); + assert("vs-1649", uf_info->container != UF_CONTAINER_TAILS); + + offset = 0; + if (inode_get_flag(inode, REISER4_PART_CONV)) { + /* find_start() doesn't need block reservation */ + result = find_start(inode, EXTENT_POINTER_ID, &offset); + if (result == -ENOENT) + /* no extent found, everything is converted */ + return 0; + else if (result != 0) + /* some other error */ + return result; + } + + /* number of pages in the file */ + num_pages = + (inode->i_size - offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + start_page = offset >> PAGE_CACHE_SHIFT; + + key_by_inode_unix_file(inode, offset, &from); + to = from; + + result = 0; + for (i = 0; i < num_pages; i++) { + __u64 start_byte; + + all_grabbed2free(); + result = reserve_extent2tail_iteration(inode); + if (result != 0) + break; + if (i == 0) { + inode_set_flag(inode, REISER4_PART_CONV); + reiser4_update_sd(inode); + } + + page = read_cache_page(inode->i_mapping, + (unsigned) (i + start_page), + readpage_unix_file/*filler*/, 0); + if (IS_ERR(page)) { + result = PTR_ERR(page); + break; + } + + wait_on_page_locked(page); + + if (!PageUptodate(page)) { + page_cache_release(page); + result = RETERR(-EIO); + break; + } + + /* cut part of file we have read */ + start_byte = (__u64) (i << PAGE_CACHE_SHIFT) + offset; + set_key_offset(&from, start_byte); + set_key_offset(&to, start_byte + PAGE_CACHE_SIZE - 1); + /* + * cut_tree_object() returns -E_REPEAT to allow atom + * commits during over-long truncates. But + * extent->tail conversion should be performed in one + * transaction. + */ + result = cut_tree(tree_by_inode(inode), &from, &to, inode, 1); + + if (result) { + page_cache_release(page); + break; + } + + /* put page data into tree via tail_write */ + count = PAGE_CACHE_SIZE; + if (i == num_pages - 1) + count = (inode->i_size & ~PAGE_CACHE_MASK) ? : PAGE_CACHE_SIZE; + result = write_page_by_tail(inode, page, count); + if (result) { + page_cache_release(page); + break; + } + + /* release page */ + lock_page(page); + /* page is already detached from jnode and mapping. */ + assert("vs-1086", page->mapping == NULL); + assert("nikita-2690", (!PagePrivate(page) && page->private == 0)); + /* waiting for writeback completion with page lock held is + * perfectly valid. */ + wait_on_page_writeback(page); + drop_page(page); + /* release reference taken by read_cache_page() above */ + page_cache_release(page); + } + + assert("vs-1260", (reiser4_inode_data(inode)->captured_eflushed == 0 && + reiser4_inode_data(inode)->anonymous_eflushed == 0)); + + if (i == num_pages) { + /* file is converted to formatted items */ + assert("vs-1698", inode_get_flag(inode, REISER4_PART_CONV)); + + uf_info->container = UF_CONTAINER_TAILS; + complete_conversion(inode); + } else { + /* conversion is not complete. Inode was already marked as + * REISER4_PART_CONV and stat-data were updated at the first + * iteration of the loop above. */ + warning("nikita-2282", + "Partial conversion of %llu: %lu of %lu: %i", + get_inode_oid(inode), i, num_pages, result); + print_inode("inode", inode); + } + all_grabbed2free(); + return result; +} + +/* this is used to find which conversion did not complete */ +static int +find_first_item(struct inode *inode) +{ + coord_t coord; + lock_handle lh; + reiser4_key key; + int result; + + coord_init_zero(&coord); + init_lh(&lh); + key_by_inode_unix_file(inode, 0, &key); + result = find_file_item_nohint(&coord, &lh, &key, ZNODE_READ_LOCK, inode); + if (result == CBK_COORD_FOUND) { + if (coord.between == AT_UNIT) { + /*coord_clear_iplug(&coord);*/ + result = zload(coord.node); + if (result == 0) { + result = item_id_by_coord(&coord); + zrelse(coord.node); + if (result != EXTENT_POINTER_ID && result != FORMATTING_ID) + result = RETERR(-EIO); + } + } else + result = RETERR(-EIO); + } + done_lh(&lh); + return result; +} + +/* exclusive access is obtained. File may be "partially converted" - that is file body may have both formatting and + extent items. Find which conversion did not completed and complete */ +reiser4_internal int +finish_conversion(struct inode *inode) +{ + int result; + + if (inode_get_flag(inode, REISER4_PART_CONV)) { + result = find_first_item(inode); + if (result == EXTENT_POINTER_ID) + /* first item is extent, therefore there was incomplete tail2extent conversion. Complete it */ + result = tail2extent(unix_file_inode_data(inode)); + else if (result == FORMATTING_ID) + /* first item is formatting item, therefore there was incomplete extent2tail + conversion. Complete it */ + result = extent2tail(unix_file_inode_data(inode)); + } else + result = 0; + return result; +} + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/flush/flush.alg 2004-09-03 00:57:05.230244144 -0700 @@ -0,0 +1,515 @@ + + + + + + + + + + +/* + +The use of atomic commits dramatically impacts the use of LRU as the +basis for page cleaning (though using it for clean page discarding is +still effective.) + +The use of write clustering dramatically impacts the use of LRU as the +basis for page cleaning. + +ReiserFS v4 uses both. + +We will not use LRU in v4.0 of reiserfs, and then in later versions we +may gradually partially reintroduce it. + +Optimizations to make on flush: + +* block (re)allocation + +* tail conversion + +* extent formation + +* node repacking + +* wandering log definition + + +Memory Pressure: + +There are kinds of memory pressure: + +* general lack of memory for processes requesting it + +* too much dirty memory + +* dirty memory is too old and should be more permanently preserved on disk + +* particular page needs freeing for DMA setup + +[All programmers should understand that I expect strict observance of the +following taboo: you will not add an unnecessary copying of data, while coding +this. I cannot understand why there is resistance to this, but I keep seeing +code which ignores this.] + + +Unlike clean pages, dirty memory must be written to disk before being +freed for other use. It also may require processing that will require +more memory before it can be cleaned by writing it to disk. This +processing makes it vulnerable to deadlocks in extreme cases. +Precisely reserving enough memory to allow that extra processing +without deadlock is often difficult. + +reiser4 limits its usage of dirty pages to 75%, which is enough to +ensure that the extra processing will not cause the system to run out +of memory. More safeguards are possible, including letting commit +choose to swap, but we will wait until this rather simple mechanism +has a problem in practice before elaborating it. + +reiser4 supports the reiserfs_flush(page) command for cleaning pages + +If the Linux VM system was properly designed, it would be based upon +memory sub-managers each reflecting some common principles of +responding to pressure that is in proportion to their size. Linux +used to have multiple caches, these caches had no understand of how +large they were, they made no attempt to proportionalize the pressure +upon each cache, and their management was generally badly designed +without any effective mechanism for ensuring that the caches did not +get out of balance with each other. From this it was concluded that +there should be only one unified cache, rather than designing an +effective mechanism for expressing to each subcache a sense of +pressure in proportion to the size of the subcache, and requiring that +the subcache embody some effective mechanism for responding to that +sense of pressure. + +The unified cache is indeed better than badly designed multiple +caches. It does however perform very poorly at handling caches of +objects that are not page sized. + +Linus says it already has a subcache manager design, we just need to +use writepage. Ok, fine, we will be the first subcache. + +So, understand that in reiserfs, writepage does not write pages, it +pressures the reiserfs memory manager, and understand that the place a +page has on the various mm lists does not determine when it gets +written out, it merely determines when it triggers the next pressure +on the reiserfs memory manager. + +What reiser4 does is interpret pressure on a page as pressure on a +subcache within reiserfs. + +Write clustering, transaction commits, objects whose value to cache is +out of proportion to the number of bytes consumed by them, caches +whose working set size and pattern of access is known to the +application, and those occasions when other factors knowable to the +filesystem or application but not the OS generally are important to +deciding what to eject, and objects much smaller than a page with no +correlation of references for objects on the same page, or larger than +a page with a complete correlation between their pages, are good example of when cache +submanagers should be employed. + + */ + +/* You should read crypt.c and then return. */ +/* You should read block_alloc.c and then return. */ + +current_leaf = find_leftmost_leaf_in_slum(); +/* current_leaf is locked */ +parent = parent(current_leaf); +/* parent is locked */ + +if (is_relocate_set(current_leaf)) +{ + dirty(parent); +} + +if (is_dirty(parent)) +{ + squeeze_level_left(parent); + /* this can create an enormous recursive chain that could overflow + the kernel stack, hmmmm..... */ + flush_all_other_child_slums(parent, min_key(current_leaf)); +} +else +{ + unlock(parent); +} +/* parent is unlocked by squeeze_level_left, and squeezing may have + changed the parent of current_leaf */ + +parent = parent(current_leaf); +/* parent is locked */ +if (leftmost_child(parent) == current_leaf) + allocate(parent); + +/* ok, now we are ready to proceed through slum on the leaf level */ +next_leaf = get_right_neighbor_in_slum_level(current_leaf); +/* next_leaf is locked or null */ + +/* need to review locking in the below */ +while(next_leaf) +{ + if (is_formatted(current_leaf) && is_formatted(next_leaf)) + { + squeeze_left(current_leaf, next_leaf); + if (is_empty(next_leaf)) + { + delete_node(next_leaf); + next_leaf = get_right_neighbor_in_slum_level(current_leaf); + check_and_handle_parent_change(); + continue; + } + } + if (is_unformatted(current_leaf)) + /* allocate or reallocate */ + allocate_extent_in_parent_if_needed(current_leaf); + /* the above may change the parent */ + check_and_handle_parent_change(); + allocate(current_leaf); + next_leaf = get_right_neighbor_in_slum_level(current_leaf); + check_and_handle_parent_change(); + + +} + +/* this means squeeze it as well as allocate it */ +handle_non_leaf_end_of_slum(); + +check_and_handle_parent_change() +{ +if ( (new_parent = parent(current_leaf)) != parent) + squeeze_left(parent, new_parent(parent)); + else + return; + +/* the line above can change who the parent is so retest... */ + if((new_parent = parent(current_leaf)) != parent) + { + parent = new_parent; + if (leftmost_child(parent) != current_leaf) + reiser_panic("reiser-2071: this needs recoding to handle this case"); + allocate_node(parent); + /* allocating other ancestors left for josh */ + + /* our new parent might not be well packed, and we want + it to be well packed even if our slum never reaches its edge + so we... */ + squeeze_left(parent, right_neighbor(parent)); + } +} + + +################################################################################ + + +The Problem: + +We need to know the relocate set in order to perform a left-to-right +parent-first allocate-and-squeeze traversal over a dirty sub-tree. We +could make this decision during the allocate-and-squeeze pass, but in +that case we would discover a node is dirty when we have already +passed over its position in the parent-first ordering. In otherwords, +we would discover this information too late to be useful. + +The Several-Pass Solution: + +It is possible to construct this relocate information at flush time by +scanning the tree, but it means at least two passes over the tree. +Using several passes has an advantage: we can then choose overwrite +when the "optimal location" is part of the atom's own preserve set. +This requires knowing the (partial) set of blocks being flushed before +allocation begins. This strategy was initially proposed by Hans, +before we realized it would require multiple passes. + +The Solution: + +Maintain an active count of dirty children for each node. This allows +us to mark a node dirty whenever its dirty count becomes >= 2 because +at that point overwriting the parent reduces the total number of +blocks written to disk. How much of a counter is needed? In order to +keep track of additions and subtractions to this count, a counter at +the same size as our znode's c_count field is needed. If this value +were only ever incremented, then we could use a single bit (0 = no +dirty children, 1 = single dirty child, otherwise mark dirty & +relocate). But since a node may have dirty children added while +flushes are active (it can happen, right?) this requires more than just +a bit. I worry about the complexity of maintaining this dirty count, +but I fear that the parent-first allocation policy will not succeed +without knowing before-hand all the dirty nodes it must consider. + +The Algorithm: + +Given the above assumption, that nodes are marked dirty whenever they +should be relocated (i.e., that the flush algorithm does not make this +decision during as part of its passing over the tree). + +Starting from some leaf node, find the greatest dirty ancestor, defined as the +least (i.e., lowest-level) ancestor of a node without a dirty parent. The +greatest dirty ancestor will be overwritten, therefore its preceding node in +the parent-first order should not be considered. + + [Dead text: If the greatest dirty ancestor is NOT the leftmost child of + its own parent (and not the root node), there may be a dirty + parent-first-ordered node in a subtree to the left of this one. In those + cases, from the greatest dirty ancestor, find the leftmost in-memory + descendant. If the leftmost descendant is dirty, consider its left + neighbor. If the neighbor is also dirty, repeat the steps of this + paragraph starting at that node (i.e., find the new greatest dirty + ancestor).] + +Pseudo-code for this is: + +/* Starting from a node, find the greatest dirty ancestor. */ +jnode* greatest_dirty_ancestor (jnode *node) +{ + while (! is_root_node (node)) { + znode *parent = get_parent (node); + + if (! znode_is_dirty (parent)) { + break; + } + + node = parent; + } + return node; +} + +Now we have found the greatest dirty ancestor from which to begin allocating +and squeezing. From this point we will traverse all descendants of the +greatest dirty ancestor, in parent-first order, allocating blocks and +squeezing nodes in the following order. Squeezing must be performed in a +bottom-up, left-to-right order, whereas allocation occurs in parent-first +order. The following pseudo-code accomplishes both at once: + +######################################################################## + +Problems with above to be addressed: + +Nikita suggests squeezing all the formatted nodes of a twig before allocating +its extents, thereby increasing room for extents to "inflate". + +######################################################################## + +/* A function to find the parent-first-preceder of a node, although + * there may not be enough nodes in memory to actually compute this. + * In that case, pick something else. If node is leftmost child of + * its parent, return its parent's block number. Otherwise if node + * is a leaf node, return its left neighbor. Finally, return the + * block number of the parent's left neighbor's rightmost descendent + * (which may not be in memory). In the actual implementation of the + * parent-first traversal below, we can optimize this (because we + * know the result most of the time). */ +blocknr parent_first_preceder_of (jnode *node) { ... } + +/* A parent-first recursive tree traversal, allocate and squeeze. + * This is called on the greatest dirty ancestor of a region to be + * flushed. + */ +void allocate_and_squeeze_parent_first (jnode *node) +{ + /* Stop recursion if its not dirty, meaning don't allocate children either. + * Children might be dirty but there is an overwrite below this level + * or else this node would be dirty. */ + if (! is_dirty (node)) { + return; + } + + /* Allocate (parent) first. */ + allocate_node (node, parent_first_preceder_of (node)); + + if (jnode_is_unformatted (node)) { + /* We got here because the parent (twig) of an unformatted node is + * not being relocated. Otherwise this recursion does not descend + * to unformatted nodes. */ + return; + } + + /* Recursive case: */ + if (jnode_get_level (node) > LEAF_LEVEL) { + + for (each_item_left_to_right (node)) { + + if (is_extent_item (item) && extent_item_is_dirty (item)) { + allocate_extent_item (item); + } else if (is_internal_item (item) && jnode_is_dirty (internal_item_child (item))) { + allocate_and_squeeze_parent_first (internal_item_child (item)); + } + } + } + + /* Squeeze a node: note that this makes the "one big memcpy" + * approach somewhat more difficult, but its still possible. */ + while (not_empty (node) && jnode_is_formatted (node->right) && is_dirty (node->right)) { + + item = first_item_of (node->right); + + if (is_extent_item (item) && extent_item_is_dirty (item)) { + allocate_extent_item_into (item, node); + } else if (can_shift_into (item, node)) { + shift_item (item, node); + } + } +} + +######################################################################## +######################################################################## + +######################################################################## +######################################################################## + +Hans says: + +Relocate parent if leftmost child is also relocated + +Relocate if leftmost-child of parent. + +Ignore the "always relocate children if two children of a node are dirty" +idea. + +Rather than scan left at the leaf level, why not jump to parent, check +left-most child dirty, and stop? + +######################################################################## +######################################################################## + +Dead pseudo code, older stuff: + +######################################################################## +######################################################################## + + +Problem: The root of a subtree gets overwritten, so the subtree to the left +will not follow in parent-first order. That would simplify things. Killed +this code: + +/* Starting at a node, find the greatest dirty parent, then see if it + * has a preceding dirty node on the leaf of the subtree to its left. */ +void find_maximal_dirty_ancestor (jnode *node) +{ + repeat: + node = greatest_dirty_ancestor (node) + + /* End search at the root node or if the node is the leftmost child + * of its parent, in which case the left-of-leftmost-descendent does + * not precede it in parent first order, its parent does in that + * case. */ + if (! is_root_node (node) && ! leftmost_child_of_parent (node)) { + jnode *godown = node; + + while (jnode_get_level (godown) > LEAF_LEVEL) { + /* Iterate downward as long as leftmost nodes in memory (note: + * they don't have to be dirty). */ + jnode *child = leftmost_child (godown); + + if (child == NULL) { + return node; + } + + godown = child; + } + + /* Reached the leftmost descendant of the maximal dirty node, + * now see if its left is dirty. Otherwise return. */ + if ((godown = godown->left) == NULL || ! jnode_is_dirty (godown)) { + return node; + } + + /* At this point, "godown" precedes "node" in the parent-first + * traversal, so search for a new maximal dirty node. */ + node = godown; + goto repeat; + } +} + +/* Allocate and squeeze starting at the greatest dirty ancestor + * described above. Repeat in rightward direction for adjacent + * subtrees. + */ +void allocate_and_squeeze_parent_first (jnode *node) +{ + jnode *right; + +repeat: + /* Do one sub-tree */ + allocate_and_squeeze_parent_first_subtree (node); + + /* Now try to repeat to the right. */ + right = get_right_neighbor (node); + + if (right != NULL && jnode_is_dirty (right)) { + node = greatest_dirty_ancestor (right); + goto repeat; + } +} + + +/* The crap below was my first attempt to write this iteratively. */ + + + + + +jnode *maximal_dirty_ancestor = ...; /* Computed using above algorithm */ +jnode *left_edge[MAX_LEVELS]; /* Vertical edge of left-to-right scan */ +int top_edge = jnode_get_level (node) - LEAF_LEVEL; /* Highest index to the left_edge array -- + * by subtracting LEAF_LEVEL it becomes 0-origin */ + +/* Initialize left_edge array entries to NULL, set top edge */ +left_edge[top_edge] = maximal_dirty_ancestor; + +/* For each node above the leaf level, set the child in left_edge */ +for (int level = top_edge; level >= 1; level -= 1) { + + jnode *parent = left_edge[level]; + + /* Find its leftmost dirty child. */ + jnode *child = leftmost_dirty_child (parent); + + /* Its possible that a dirty node could have no dirty children, + * in which case leave the lower edges NULL. */ + if (child == NULL) { break; } + + left_edge[level-1] = child; +} + +/* To store the lowest dirty entry in left_edge[]. */ +int current_level = 0; + +/* Allocate each node in the left edge. */ +for (int level = top_edge; level >= 0 && left_edge[level] != NULL; level -= 1) { + + jnode *node = left_edge[level]; + + /* Allocate this node... */ + allocate_node (node, parent_first_preceder_of (node)); + + current_level = level; +} + +/* Now starting with the current level, squeeze and allocate until finished. */ +while (current_level <= top_level) { + + jnode *current_node = left_edge[current_level]; + + if (jnode_is_formatted (current_node)) { + + do { + + /* Shift as much as possible. */ + while (node_has_room_to_shift_into (current_node)) { + if (is_twig_level (current_node)) { + shift_left (current_node, current_node->right); + } else { + allocate_extents_and_shift_left (current_node, current_node->right); + } + } + + /* Once it has been tightly packed, allocate it. */ + allocate_node (current_node, parent_first_preceder_of (node)); + + current_node = current_node->right; + } + + current_level += 1; +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/hash.c 2004-09-03 00:57:05.231243992 -0700 @@ -0,0 +1,347 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Hash functions */ + +#include "../debug.h" +#include "plugin_header.h" +#include "plugin.h" +#include "../super.h" +#include "../inode.h" +#include "../plugin/dir/dir.h" + +#include + +/* old rupasov (yura) hash */ +static __u64 +hash_rupasov(const unsigned char *name /* name to hash */ , + int len /* @name's length */ ) +{ + int i; + int j; + int pow; + __u64 a; + __u64 c; + + assert("nikita-672", name != NULL); + assert("nikita-673", len >= 0); + + for (pow = 1, i = 1; i < len; ++i) + pow = pow * 10; + + if (len == 1) + a = name[0] - 48; + else + a = (name[0] - 48) * pow; + + for (i = 1; i < len; ++i) { + c = name[i] - 48; + for (pow = 1, j = i; j < len - 1; ++j) + pow = pow * 10; + a = a + c * pow; + } + for (; i < 40; ++i) { + c = '0' - 48; + for (pow = 1, j = i; j < len - 1; ++j) + pow = pow * 10; + a = a + c * pow; + } + + for (; i < 256; ++i) { + c = i; + for (pow = 1, j = i; j < len - 1; ++j) + pow = pow * 10; + a = a + c * pow; + } + + a = a << 7; + return a; +} + +/* r5 hash */ +static __u64 +hash_r5(const unsigned char *name /* name to hash */ , + int len UNUSED_ARG /* @name's length */ ) +{ + __u64 a = 0; + + assert("nikita-674", name != NULL); + assert("nikita-675", len >= 0); + + while (*name) { + a += *name << 4; + a += *name >> 4; + a *= 11; + name++; + } + return a; +} + +/* Keyed 32-bit hash function using TEA in a Davis-Meyer function + H0 = Key + Hi = E Mi(Hi-1) + Hi-1 + + (see Applied Cryptography, 2nd edition, p448). + + Jeremy Fitzhardinge 1998 + + Jeremy has agreed to the contents of reiserfs/README. -Hans + + This code was blindly upgraded to __u64 by s/__u32/__u64/g. +*/ +static __u64 +hash_tea(const unsigned char *name /* name to hash */ , + int len /* @name's length */ ) +{ + __u64 k[] = { 0x9464a485u, 0x542e1a94u, 0x3e846bffu, 0xb75bcfc3u }; + + __u64 h0 = k[0], h1 = k[1]; + __u64 a, b, c, d; + __u64 pad; + int i; + + assert("nikita-676", name != NULL); + assert("nikita-677", len >= 0); + +#define DELTA 0x9E3779B9u +#define FULLROUNDS 10 /* 32 is overkill, 16 is strong crypto */ +#define PARTROUNDS 6 /* 6 gets complete mixing */ + +/* a, b, c, d - data; h0, h1 - accumulated hash */ +#define TEACORE(rounds) \ + do { \ + __u64 sum = 0; \ + int n = rounds; \ + __u64 b0, b1; \ + \ + b0 = h0; \ + b1 = h1; \ + \ + do \ + { \ + sum += DELTA; \ + b0 += ((b1 << 4)+a) ^ (b1+sum) ^ ((b1 >> 5)+b); \ + b1 += ((b0 << 4)+c) ^ (b0+sum) ^ ((b0 >> 5)+d); \ + } while(--n); \ + \ + h0 += b0; \ + h1 += b1; \ + } while(0) + + pad = (__u64) len | ((__u64) len << 8); + pad |= pad << 16; + + while (len >= 16) { + a = (__u64) name[0] | (__u64) name[1] << 8 | (__u64) name[2] << 16 | (__u64) name[3] << 24; + b = (__u64) name[4] | (__u64) name[5] << 8 | (__u64) name[6] << 16 | (__u64) name[7] << 24; + c = (__u64) name[8] | (__u64) name[9] << 8 | (__u64) name[10] << 16 | (__u64) name[11] << 24; + d = (__u64) name[12] | (__u64) name[13] << 8 | (__u64) name[14] << 16 | (__u64) name[15] << 24; + + TEACORE(PARTROUNDS); + + len -= 16; + name += 16; + } + + if (len >= 12) { + //assert(len < 16); + if (len >= 16) + *(int *) 0 = 0; + + a = (__u64) name[0] | (__u64) name[1] << 8 | (__u64) name[2] << 16 | (__u64) name[3] << 24; + b = (__u64) name[4] | (__u64) name[5] << 8 | (__u64) name[6] << 16 | (__u64) name[7] << 24; + c = (__u64) name[8] | (__u64) name[9] << 8 | (__u64) name[10] << 16 | (__u64) name[11] << 24; + + d = pad; + for (i = 12; i < len; i++) { + d <<= 8; + d |= name[i]; + } + } else if (len >= 8) { + //assert(len < 12); + if (len >= 12) + *(int *) 0 = 0; + a = (__u64) name[0] | (__u64) name[1] << 8 | (__u64) name[2] << 16 | (__u64) name[3] << 24; + b = (__u64) name[4] | (__u64) name[5] << 8 | (__u64) name[6] << 16 | (__u64) name[7] << 24; + + c = d = pad; + for (i = 8; i < len; i++) { + c <<= 8; + c |= name[i]; + } + } else if (len >= 4) { + //assert(len < 8); + if (len >= 8) + *(int *) 0 = 0; + a = (__u64) name[0] | (__u64) name[1] << 8 | (__u64) name[2] << 16 | (__u64) name[3] << 24; + + b = c = d = pad; + for (i = 4; i < len; i++) { + b <<= 8; + b |= name[i]; + } + } else { + //assert(len < 4); + if (len >= 4) + *(int *) 0 = 0; + a = b = c = d = pad; + for (i = 0; i < len; i++) { + a <<= 8; + a |= name[i]; + } + } + + TEACORE(FULLROUNDS); + +/* return 0;*/ + return h0 ^ h1; + +} + +/* classical 64 bit Fowler/Noll/Vo-1 (FNV-1) hash. + + See http://www.isthe.com/chongo/tech/comp/fnv/ for details. + + Excerpts: + + FNV hashes are designed to be fast while maintaining a low collision + rate. + + [This version also seems to preserve lexicographical order locally.] + + FNV hash algorithms and source code have been released into the public + domain. + +*/ +static __u64 +hash_fnv1(const unsigned char *name /* name to hash */ , + int len UNUSED_ARG /* @name's length */ ) +{ + unsigned long long a = 0xcbf29ce484222325ull; + const unsigned long long fnv_64_prime = 0x100000001b3ull; + + assert("nikita-678", name != NULL); + assert("nikita-679", len >= 0); + + /* FNV-1 hash each octet in the buffer */ + for (; *name; ++name) { + /* multiply by the 32 bit FNV magic prime mod 2^64 */ + a *= fnv_64_prime; + /* xor the bottom with the current octet */ + a ^= (unsigned long long) (*name); + } + /* return our new hash value */ + return a; +} + +/* degenerate hash function used to simplify testing of non-unique key + handling */ +static __u64 +hash_deg(const unsigned char *name UNUSED_ARG /* name to hash */ , + int len UNUSED_ARG /* @name's length */ ) +{ + ON_TRACE(TRACE_DIR, "Hashing %s\n", name); + return 0xc0c0c0c010101010ull; +} + +static int +change_hash(struct inode * inode, reiser4_plugin * plugin) +{ + int result; + + assert("nikita-3503", inode != NULL); + assert("nikita-3504", plugin != NULL); + + assert("nikita-3505", is_reiser4_inode(inode)); + assert("nikita-3506", inode_dir_plugin(inode) != NULL); + assert("nikita-3507", plugin->h.type_id == REISER4_HASH_PLUGIN_TYPE); + + result = 0; + if (inode_hash_plugin(inode) == NULL || + inode_hash_plugin(inode)->h.id != plugin->h.id) { + if (is_dir_empty(inode) == 0) + result = plugin_set_hash(&reiser4_inode_data(inode)->pset, + &plugin->hash); + else + result = RETERR(-ENOTEMPTY); + + } + return result; +} + +static reiser4_plugin_ops hash_plugin_ops = { + .init = NULL, + .load = NULL, + .save_len = NULL, + .save = NULL, + .change = change_hash +}; + +/* hash plugins */ +hash_plugin hash_plugins[LAST_HASH_ID] = { + [RUPASOV_HASH_ID] = { + .h = { + .type_id = REISER4_HASH_PLUGIN_TYPE, + .id = RUPASOV_HASH_ID, + .pops = &hash_plugin_ops, + .label = "rupasov", + .desc = "Original Yura's hash", + .linkage = TYPE_SAFE_LIST_LINK_ZERO} + , + .hash = hash_rupasov + }, + [R5_HASH_ID] = { + .h = { + .type_id = REISER4_HASH_PLUGIN_TYPE, + .id = R5_HASH_ID, + .pops = &hash_plugin_ops, + .label = "r5", + .desc = "r5 hash", + .linkage = TYPE_SAFE_LIST_LINK_ZERO} + , + .hash = hash_r5 + }, + [TEA_HASH_ID] = { + .h = { + .type_id = REISER4_HASH_PLUGIN_TYPE, + .id = TEA_HASH_ID, + .pops = &hash_plugin_ops, + .label = "tea", + .desc = "tea hash", + .linkage = TYPE_SAFE_LIST_LINK_ZERO} + , + .hash = hash_tea + }, + [FNV1_HASH_ID] = { + .h = { + .type_id = REISER4_HASH_PLUGIN_TYPE, + .id = FNV1_HASH_ID, + .pops = &hash_plugin_ops, + .label = "fnv1", + .desc = "fnv1 hash", + .linkage = TYPE_SAFE_LIST_LINK_ZERO} + , + .hash = hash_fnv1 + }, + [DEGENERATE_HASH_ID] = { + .h = { + .type_id = REISER4_HASH_PLUGIN_TYPE, + .id = DEGENERATE_HASH_ID, + .pops = &hash_plugin_ops, + .label = "degenerate hash", + .desc = "Degenerate hash: only for testing", + .linkage = TYPE_SAFE_LIST_LINK_ZERO} + , + .hash = hash_deg + } +}; + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/item/acl.h 2004-09-03 00:57:05.232243840 -0700 @@ -0,0 +1,64 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Directory entry. */ + +#if !defined( __FS_REISER4_PLUGIN_DIRECTORY_ENTRY_H__ ) +#define __FS_REISER4_PLUGIN_DIRECTORY_ENTRY_H__ + +#include "../../forward.h" +#include "../../dformat.h" +#include "../../kassign.h" +#include "../../key.h" + +#include +#include /* for struct dentry */ + +typedef struct directory_entry_format { + /* key of object stat-data. It's not necessary to store whole + key here, because it's always key of stat-data, so minor + packing locality and offset can be omitted here. But this + relies on particular key allocation scheme for stat-data, so, + for extensibility sake, whole key can be stored here. + + We store key as array of bytes, because we don't want 8-byte + alignment of dir entries. + */ + obj_key_id id; + /* file name. Null terminated string. */ + d8 name[0]; +} directory_entry_format; + +void print_de(const char *prefix, coord_t * coord); +int extract_key_de(const coord_t * coord, reiser4_key * key); +int update_key_de(const coord_t * coord, const reiser4_key * key, lock_handle * lh); +char *extract_name_de(const coord_t * coord, char *buf); +unsigned extract_file_type_de(const coord_t * coord); +int add_entry_de(struct inode *dir, coord_t * coord, + lock_handle * lh, const struct dentry *name, reiser4_dir_entry_desc * entry); +int rem_entry_de(struct inode *dir, const struct qstr * name, coord_t * coord, lock_handle * lh, reiser4_dir_entry_desc * entry); +int max_name_len_de(const struct inode *dir); + + +int de_rem_and_shrink(struct inode *dir, coord_t * coord, int length); + +char *extract_dent_name(const coord_t * coord, + directory_entry_format *dent, char *buf); + +#if REISER4_LARGE_KEY +#define DE_NAME_BUF_LEN (24) +#else +#define DE_NAME_BUF_LEN (16) +#endif + +/* __FS_REISER4_PLUGIN_DIRECTORY_ENTRY_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/item/blackbox.c 2004-09-03 00:57:05.233243688 -0700 @@ -0,0 +1,142 @@ +/* Copyright 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Black box item implementation */ + +#include "../../forward.h" +#include "../../debug.h" +#include "../../dformat.h" +#include "../../kassign.h" +#include "../../coord.h" +#include "../../tree.h" +#include "../../lock.h" + +#include "blackbox.h" +#include "item.h" +#include "../plugin.h" + + +reiser4_internal int +store_black_box(reiser4_tree *tree, + const reiser4_key *key, void *data, int length) +{ + int result; + reiser4_item_data idata; + coord_t coord; + lock_handle lh; + + xmemset(&idata, 0, sizeof idata); + + idata.data = data; + idata.user = 0; + idata.length = length; + idata.iplug = item_plugin_by_id(BLACK_BOX_ID); + + init_lh(&lh); + result = insert_by_key(tree, key, + &idata, &coord, &lh, LEAF_LEVEL, CBK_UNIQUE); + + assert("nikita-3413", + ergo(result == 0, + WITH_COORD(&coord, item_length_by_coord(&coord) == length))); + + done_lh(&lh); + return result; +} + +reiser4_internal int +load_black_box(reiser4_tree *tree, + reiser4_key *key, void *data, int length, int exact) +{ + int result; + coord_t coord; + lock_handle lh; + + init_lh(&lh); + result = coord_by_key(tree, key, + &coord, &lh, ZNODE_READ_LOCK, + exact ? FIND_EXACT : FIND_MAX_NOT_MORE_THAN, + LEAF_LEVEL, LEAF_LEVEL, CBK_UNIQUE, NULL); + + if (result == 0) { + int ilen; + + result = zload(coord.node); + if (result == 0) { + ilen = item_length_by_coord(&coord); + if (ilen <= length) { + xmemcpy(data, item_body_by_coord(&coord), ilen); + unit_key_by_coord(&coord, key); + } else if (exact) { + /* + * item is larger than buffer provided by the + * user. Only issue a warning if @exact is + * set. If @exact is false, we are iterating + * over all safe-links and here we are reaching + * the end of the iteration. + */ + warning("nikita-3415", + "Wrong black box length: %i > %i", + ilen, length); + result = RETERR(-EIO); + } + zrelse(coord.node); + } + } + + done_lh(&lh); + return result; + +} + +reiser4_internal int +update_black_box(reiser4_tree *tree, + const reiser4_key *key, void *data, int length) +{ + int result; + coord_t coord; + lock_handle lh; + + init_lh(&lh); + result = coord_by_key(tree, key, + &coord, &lh, ZNODE_READ_LOCK, + FIND_EXACT, + LEAF_LEVEL, LEAF_LEVEL, CBK_UNIQUE, NULL); + if (result == 0) { + int ilen; + + result = zload(coord.node); + if (result == 0) { + ilen = item_length_by_coord(&coord); + if (length <= ilen) { + xmemcpy(item_body_by_coord(&coord), data, length); + } else { + warning("nikita-3437", + "Wrong black box length: %i < %i", + ilen, length); + result = RETERR(-EIO); + } + zrelse(coord.node); + } + } + + done_lh(&lh); + return result; + +} + +reiser4_internal int kill_black_box(reiser4_tree *tree, const reiser4_key *key) +{ + return cut_tree(tree, key, key, NULL, 1); +} + + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/item/blackbox.h 2004-09-03 00:57:05.233243688 -0700 @@ -0,0 +1,33 @@ +/* Copyright 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* "Black box" entry to fixed-width contain user supplied data */ + +#if !defined( __FS_REISER4_BLACK_BOX_H__ ) +#define __FS_REISER4_BLACK_BOX_H__ + +#include "../../forward.h" +#include "../../dformat.h" +#include "../../kassign.h" +#include "../../key.h" + +extern int store_black_box(reiser4_tree *tree, + const reiser4_key *key, void *data, int length); +extern int load_black_box(reiser4_tree *tree, + reiser4_key *key, void *data, int length, int exact); +extern int kill_black_box(reiser4_tree *tree, const reiser4_key *key); +extern int update_black_box(reiser4_tree *tree, + const reiser4_key *key, void *data, int length); + +/* __FS_REISER4_BLACK_BOX_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/item/cde.c 2004-09-03 00:57:05.238242928 -0700 @@ -0,0 +1,1073 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Directory entry implementation */ + +/* DESCRIPTION: + + This is "compound" directory item plugin implementation. This directory + item type is compound (as opposed to the "simple directory item" in + fs/reiser4/plugin/item/sde.[ch]), because it consists of several directory + entries. + + The reason behind this decision is disk space efficiency: all directory + entries inside the same directory have identical fragment in their + keys. This, of course, depends on key assignment policy. In our default key + assignment policy, all directory entries have the same locality which is + equal to the object id of their directory. + + Composing directory item out of several directory entries for the same + directory allows us to store said key fragment only once. That is, this is + some ad hoc form of key compression (stem compression) that is implemented + here, because general key compression is not supposed to be implemented in + v4.0. + + Another decision that was made regarding all directory item plugins, is + that they will store entry keys unaligned. This is for that sake of disk + space efficiency again. + + In should be noted, that storing keys unaligned increases CPU consumption, + at least on some architectures. + + Internal on-disk structure of the compound directory item is the following: + + HEADER cde_item_format. Here number of entries is stored. + ENTRY_HEADER_0 cde_unit_header. Here part of entry key and + ENTRY_HEADER_1 offset of entry body are stored. + ENTRY_HEADER_2 (basically two last parts of key) + ... + ENTRY_HEADER_N + ENTRY_BODY_0 directory_entry_format. Here part of stat data key and + ENTRY_BODY_1 NUL-terminated name are stored. + ENTRY_BODY_2 (part of statadta key in the + sence that since all SDs have + zero offset, this offset is not + stored on disk). + ... + ENTRY_BODY_N + + When it comes to the balancing, each directory entry in compound directory + item is unit, that is, something that can be cut from one item and pasted + into another item of the same type. Handling of unit cut and paste is major + reason for the complexity of code below. + +*/ + +#include "../../forward.h" +#include "../../debug.h" +#include "../../dformat.h" +#include "../../kassign.h" +#include "../../key.h" +#include "../../coord.h" +#include "sde.h" +#include "cde.h" +#include "item.h" +#include "../node/node.h" +#include "../plugin.h" +#include "../../znode.h" +#include "../../carry.h" +#include "../../tree.h" +#include "../../inode.h" + +#include /* for struct inode */ +#include /* for struct dentry */ +#include + +#if 0 +#define CHECKME(coord) \ +({ \ + const char *message; \ + coord_t dup; \ + \ + coord_dup_nocheck(&dup, (coord)); \ + dup.unit_pos = 0; \ + assert("nikita-2871", cde_check(&dup, &message) == 0); \ +}) +#else +#define CHECKME(coord) noop +#endif + + +/* return body of compound directory item at @coord */ +static inline cde_item_format * +formatted_at(const coord_t * coord) +{ + assert("nikita-1282", coord != NULL); + return item_body_by_coord(coord); +} + +/* return entry header at @coord */ +static inline cde_unit_header * +header_at(const coord_t * coord /* coord of item */ , + int idx /* index of unit */ ) +{ + assert("nikita-1283", coord != NULL); + return &formatted_at(coord)->entry[idx]; +} + +/* return number of units in compound directory item at @coord */ +static int +units(const coord_t * coord /* coord of item */ ) +{ + return d16tocpu(&formatted_at(coord)->num_of_entries); +} + +/* return offset of the body of @idx-th entry in @coord */ +static unsigned int +offset_of(const coord_t * coord /* coord of item */ , + int idx /* index of unit */ ) +{ + if (idx < units(coord)) + return d16tocpu(&header_at(coord, idx)->offset); + else if (idx == units(coord)) + return item_length_by_coord(coord); + else + impossible("nikita-1308", "Wrong idx"); + return 0; +} + +/* set offset of the body of @idx-th entry in @coord */ +static void +set_offset(const coord_t * coord /* coord of item */ , + int idx /* index of unit */ , + unsigned int offset /* new offset */ ) +{ + cputod16((__u16) offset, &header_at(coord, idx)->offset); +} + +static void +adj_offset(const coord_t * coord /* coord of item */ , + int idx /* index of unit */ , + int delta /* offset change */ ) +{ + d16 *doffset; + __u16 offset; + + doffset = &header_at(coord, idx)->offset; + offset = d16tocpu(doffset); + offset += delta; + cputod16((__u16) offset, doffset); +} + +/* return pointer to @offset-th byte from the beginning of @coord */ +static char * +address(const coord_t * coord /* coord of item */ , + int offset) +{ + return ((char *) item_body_by_coord(coord)) + offset; +} + +/* return pointer to the body of @idx-th entry in @coord */ +static directory_entry_format * +entry_at(const coord_t * coord /* coord of + * item */ , + int idx /* index of unit */ ) +{ + return (directory_entry_format *) address(coord, (int) offset_of(coord, idx)); +} + +/* return number of unit referenced by @coord */ +static int +idx_of(const coord_t * coord /* coord of item */ ) +{ + assert("nikita-1285", coord != NULL); + return coord->unit_pos; +} + +/* find position where entry with @entry_key would be inserted into @coord */ +static int +find(const coord_t * coord /* coord of item */ , + const reiser4_key * entry_key /* key to look for */ , + cmp_t * last /* result of last comparison */ ) +{ + int entries; + + int left; + int right; + + cde_unit_header *header; + + assert("nikita-1295", coord != NULL); + assert("nikita-1296", entry_key != NULL); + assert("nikita-1297", last != NULL); + + entries = units(coord); + left = 0; + right = entries - 1; + while (right - left >= REISER4_SEQ_SEARCH_BREAK) { + int median; + + median = (left + right) >> 1; + + header = header_at(coord, median); + *last = de_id_key_cmp(&header->hash, entry_key); + switch (*last) { + case LESS_THAN: + left = median; + break; + case GREATER_THAN: + right = median; + break; + case EQUAL_TO: { + do { + median --; + header --; + } while (median >= 0 && + de_id_key_cmp(&header->hash, + entry_key) == EQUAL_TO); + return median + 1; + } + } + } + header = header_at(coord, left); + for (; left < entries; ++ left, ++ header) { + prefetch(header + 1); + *last = de_id_key_cmp(&header->hash, entry_key); + if (*last != LESS_THAN) + break; + } + if (left < entries) + return left; + else + return RETERR(-ENOENT); + +} + +/* expand @coord as to accommodate for insertion of @no new entries starting + from @pos, with total bodies size @size. */ +static int +expand_item(const coord_t * coord /* coord of item */ , + int pos /* unit position */ , int no /* number of new + * units*/ , + int size /* total size of new units' data */ , + unsigned int data_size /* free space already reserved + * in the item for insertion */ ) +{ + int entries; + cde_unit_header *header; + char *dent; + int i; + + assert("nikita-1310", coord != NULL); + assert("nikita-1311", pos >= 0); + assert("nikita-1312", no > 0); + assert("nikita-1313", data_size >= no * sizeof (directory_entry_format)); + assert("nikita-1343", item_length_by_coord(coord) >= (int) (size + data_size + no * sizeof *header)); + + entries = units(coord); + + if (pos == entries) + dent = address(coord, size); + else + dent = (char *) entry_at(coord, pos); + /* place where new header will be in */ + header = header_at(coord, pos); + /* free space for new entry headers */ + xmemmove(header + no, header, (unsigned) (address(coord, size) - (char *) header)); + /* if adding to the end initialise first new header */ + if (pos == entries) { + set_offset(coord, pos, (unsigned) size); + } + + /* adjust entry pointer and size */ + dent = dent + no * sizeof *header; + size += no * sizeof *header; + /* free space for new entries */ + xmemmove(dent + data_size, dent, (unsigned) (address(coord, size) - dent)); + + /* increase counter */ + entries += no; + cputod16((__u16) entries, &formatted_at(coord)->num_of_entries); + + /* [ 0 ... pos ] entries were shifted by no * ( sizeof *header ) + bytes. */ + for (i = 0; i <= pos; ++i) + adj_offset(coord, i, no * sizeof *header); + /* [ pos + no ... +\infty ) entries were shifted by ( no * + sizeof *header + data_size ) bytes */ + for (i = pos + no; i < entries; ++i) + adj_offset(coord, i, no * sizeof *header + data_size); + return 0; +} + +/* insert new @entry into item */ +static int +expand(const coord_t * coord /* coord of item */ , + cde_entry * entry /* entry to insert */ , + int len /* length of @entry data */ , + int *pos /* position to insert */ , + reiser4_dir_entry_desc * dir_entry /* parameters for new + * entry */ ) +{ + cmp_t cmp_res; + int datasize; + + *pos = find(coord, &dir_entry->key, &cmp_res); + if (*pos < 0) + *pos = units(coord); + + datasize = sizeof (directory_entry_format); + if (is_longname(entry->name->name, entry->name->len)) + datasize += entry->name->len + 1; + + expand_item(coord, *pos, 1, item_length_by_coord(coord) - len, datasize); + return 0; +} + +/* paste body of @entry into item */ +static int +paste_entry(const coord_t * coord /* coord of item */ , + cde_entry * entry /* new entry */ , + int pos /* position to insert */ , + reiser4_dir_entry_desc * dir_entry /* parameters for + * new entry */ ) +{ + cde_unit_header *header; + directory_entry_format *dent; + const char *name; + int len; + + header = header_at(coord, pos); + dent = entry_at(coord, pos); + + build_de_id_by_key(&dir_entry->key, &header->hash); + build_inode_key_id(entry->obj, &dent->id); + /* AUDIT unsafe strcpy() operation! It should be replaced with + much less CPU hungry + memcpy( ( char * ) dent -> name, entry -> name -> name , entry -> name -> len ); + + Also a more major thing is that there should be a way to figure out + amount of space in dent -> name and be able to check that we are + not going to overwrite more than we supposed to */ + name = entry->name->name; + len = entry->name->len; + if (is_longname(name, len)) { + strcpy((unsigned char *) dent->name, name); + cputod8(0, &dent->name[len]); + } + return 0; +} + +/* estimate how much space is necessary in item to insert/paste set of entries + described in @data. */ +reiser4_internal int +estimate_cde(const coord_t * coord /* coord of item */ , + const reiser4_item_data * data /* parameters for new item */ ) +{ + cde_entry_data *e; + int result; + int i; + + e = (cde_entry_data *) data->data; + + assert("nikita-1288", e != NULL); + assert("nikita-1289", e->num_of_entries >= 0); + + if (coord == NULL) + /* insert */ + result = sizeof (cde_item_format); + else + /* paste */ + result = 0; + + result += e->num_of_entries * + (sizeof (cde_unit_header) + sizeof (directory_entry_format)); + for (i = 0; i < e->num_of_entries; ++i) { + const char *name; + int len; + + name = e->entry[i].name->name; + len = e->entry[i].name->len; + assert("nikita-2054", strlen(name) == len); + if (is_longname(name, len)) + result += len + 1; + } + ((reiser4_item_data *) data)->length = result; + return result; +} + +/* ->nr_units() method for this item plugin. */ +reiser4_internal pos_in_node_t +nr_units_cde(const coord_t * coord /* coord of item */ ) +{ + return units(coord); +} + +/* ->unit_key() method for this item plugin. */ +reiser4_internal reiser4_key * +unit_key_cde(const coord_t * coord /* coord of item */ , + reiser4_key * key /* resulting key */ ) +{ + assert("nikita-1452", coord != NULL); + assert("nikita-1345", idx_of(coord) < units(coord)); + assert("nikita-1346", key != NULL); + + item_key_by_coord(coord, key); + extract_key_from_de_id(extract_dir_id_from_key(key), &header_at(coord, idx_of(coord))->hash, key); + return key; +} + +/* mergeable_cde(): implementation of ->mergeable() item method. + + Two directory items are mergeable iff they are from the same + directory. That simple. + +*/ +reiser4_internal int +mergeable_cde(const coord_t * p1 /* coord of first item */ , + const coord_t * p2 /* coord of second item */ ) +{ + reiser4_key k1; + reiser4_key k2; + + assert("nikita-1339", p1 != NULL); + assert("nikita-1340", p2 != NULL); + + return + (item_plugin_by_coord(p1) == item_plugin_by_coord(p2)) && + (extract_dir_id_from_key(item_key_by_coord(p1, &k1)) == + extract_dir_id_from_key(item_key_by_coord(p2, &k2))); + +} + +/* ->max_key_inside() method for this item plugin. */ +reiser4_internal reiser4_key * +max_key_inside_cde(const coord_t * coord /* coord of item */ , + reiser4_key * result /* resulting key */) +{ + assert("nikita-1342", coord != NULL); + + item_key_by_coord(coord, result); + set_key_ordering(result, get_key_ordering(max_key())); + set_key_objectid(result, get_key_objectid(max_key())); + set_key_offset(result, get_key_offset(max_key())); + return result; +} + +/* @data contains data which are to be put into tree */ +reiser4_internal int +can_contain_key_cde(const coord_t * coord /* coord of item */ , + const reiser4_key * key /* key to check */ , + const reiser4_item_data * data /* parameters of new + * item/unit being + * created */ ) +{ + reiser4_key item_key; + + /* FIXME-VS: do not rely on anything but iplug field of @data. Only + data->iplug is initialized */ + assert("vs-457", data && data->iplug); +/* assert( "vs-553", data -> user == 0 );*/ + item_key_by_coord(coord, &item_key); + + return (item_plugin_by_coord(coord) == data->iplug) && + (extract_dir_id_from_key(&item_key) == extract_dir_id_from_key(key)); +} + +#if REISER4_DEBUG_OUTPUT +/* ->print() method for this item plugin. */ +reiser4_internal void +print_cde(const char *prefix /* prefix to print */ , + coord_t * coord /* coord of item to print */ ) +{ + assert("nikita-1077", prefix != NULL); + assert("nikita-1078", coord != NULL); + + if (item_length_by_coord(coord) < (int) sizeof (cde_item_format)) { + printk("%s: wrong size: %i < %i\n", prefix, item_length_by_coord(coord), sizeof (cde_item_format)); + } else { + char *name; + char *end; + char *start; + int i; + oid_t dirid; + reiser4_key key; + + start = address(coord, 0); + end = address(coord, item_length_by_coord(coord)); + item_key_by_coord(coord, &key); + dirid = extract_dir_id_from_key(&key); + + printk("%s: units: %i\n", prefix, nr_units_cde(coord)); + for (i = 0; i < units(coord); ++i) { + cde_unit_header *header; + + header = header_at(coord, i); + indent_znode(coord->node); + printk("\theader %i: ", i); + if ((char *) (header + 1) > end) { + printk("out of bounds: %p [%p, %p]\n", header, start, end); + } else { + extract_key_from_de_id(dirid, &header->hash, &key); + printk("%i: at %i, offset: %i, ", i, i * sizeof (*header), d16tocpu(&header->offset)); + print_key("key", &key); + } + } + for (i = 0; i < units(coord); ++i) { + directory_entry_format *entry; + char buf[DE_NAME_BUF_LEN]; + + entry = entry_at(coord, i); + indent_znode(coord->node); + printk("\tentry: %i: ", i); + if (((char *) (entry + 1) > end) || ((char *) entry < start)) { + printk("out of bounds: %p [%p, %p]\n", entry, start, end); + } else { + coord->unit_pos = i; + extract_key_cde(coord, &key); + name = extract_name_cde(coord, buf); + printk("at %i, name: %s, ", (char *) entry - start, name); + print_key("sdkey", &key); + } + } + } +} +#endif + +#if REISER4_DEBUG +/* cde_check ->check() method for compressed directory items + + used for debugging, every item should have here the most complete + possible check of the consistency of the item that the inventor can + construct +*/ +reiser4_internal int +check_cde(const coord_t * coord /* coord of item to check */ , + const char **error /* where to store error message */ ) +{ + int i; + int result; + char *item_start; + char *item_end; + reiser4_key key; + + coord_t c; + + assert("nikita-1357", coord != NULL); + assert("nikita-1358", error != NULL); + + if (!ergo(coord->item_pos != 0, + is_dot_key(item_key_by_coord(coord, &key)))) { + *error = "CDE doesn't start with dot"; + return -1; + } + item_start = item_body_by_coord(coord); + item_end = item_start + item_length_by_coord(coord); + + coord_dup(&c, coord); + result = 0; + for (i = 0; i < units(coord); ++i) { + directory_entry_format *entry; + + if ((char *) (header_at(coord, i) + 1) > item_end - units(coord) * sizeof *entry) { + *error = "CDE header is out of bounds"; + result = -1; + break; + } + entry = entry_at(coord, i); + if ((char *) entry < item_start + sizeof (cde_item_format)) { + *error = "CDE header is too low"; + result = -1; + break; + } + if ((char *) (entry + 1) > item_end) { + *error = "CDE header is too high"; + result = -1; + break; + } + } + + return result; +} +#endif + +/* ->init() method for this item plugin. */ +reiser4_internal int +init_cde(coord_t * coord /* coord of item */ , + coord_t * from UNUSED_ARG, + reiser4_item_data * data /* structure used for insertion */ + UNUSED_ARG) +{ + cputod16(0u, &formatted_at(coord)->num_of_entries); + return 0; +} + +/* ->lookup() method for this item plugin. */ +reiser4_internal lookup_result +lookup_cde(const reiser4_key * key /* key to search for */ , + lookup_bias bias /* search bias */ , + coord_t * coord /* coord of item to lookup in */ ) +{ + cmp_t last_comp; + int pos; + + reiser4_key utmost_key; + + assert("nikita-1293", coord != NULL); + assert("nikita-1294", key != NULL); + + CHECKME(coord); + + if (keygt(item_key_by_coord(coord, &utmost_key), key)) { + coord->unit_pos = 0; + coord->between = BEFORE_UNIT; + return CBK_COORD_NOTFOUND; + } + pos = find(coord, key, &last_comp); + if (pos >= 0) { + coord->unit_pos = (int) pos; + switch (last_comp) { + case EQUAL_TO: + coord->between = AT_UNIT; + return CBK_COORD_FOUND; + case GREATER_THAN: + coord->between = BEFORE_UNIT; + return RETERR(-ENOENT); + case LESS_THAN: + default: + impossible("nikita-1298", "Broken find"); + return RETERR(-EIO); + } + } else { + coord->unit_pos = units(coord) - 1; + coord->between = AFTER_UNIT; + return (bias == FIND_MAX_NOT_MORE_THAN) ? CBK_COORD_FOUND : CBK_COORD_NOTFOUND; + } +} + +/* ->paste() method for this item plugin. */ +reiser4_internal int +paste_cde(coord_t * coord /* coord of item */ , + reiser4_item_data * data /* parameters of new unit being + * inserted */ , + carry_plugin_info * info UNUSED_ARG /* todo carry queue */ ) +{ + cde_entry_data *e; + int result; + int i; + + CHECKME(coord); + e = (cde_entry_data *) data->data; + + result = 0; + for (i = 0; i < e->num_of_entries; ++i) { + int pos; + int phantom_size; + + phantom_size = data->length; + if (units(coord) == 0) + phantom_size -= sizeof (cde_item_format); + + result = expand(coord, e->entry + i, phantom_size, &pos, data->arg); + if (result != 0) + break; + result = paste_entry(coord, e->entry + i, pos, data->arg); + if (result != 0) + break; + } + CHECKME(coord); + return result; +} + +/* amount of space occupied by all entries starting from @idx both headers and + bodies. */ +static unsigned int +part_size(const coord_t * coord /* coord of item */ , + int idx /* index of unit */ ) +{ + assert("nikita-1299", coord != NULL); + assert("nikita-1300", idx < (int) units(coord)); + + return sizeof (cde_item_format) + + (idx + 1) * sizeof (cde_unit_header) + offset_of(coord, idx + 1) - offset_of(coord, 0); +} + +/* how many but not more than @want units of @source can be merged with + item in @target node. If pend == append - we try to append last item + of @target by first units of @source. If pend == prepend - we try to + "prepend" first item in @target by last units of @source. @target + node has @free_space bytes of free space. Total size of those units + are returned via @size */ +reiser4_internal int +can_shift_cde(unsigned free_space /* free space in item */ , + coord_t * coord /* coord of source item */ , + znode * target /* target node */ , + shift_direction pend /* shift direction */ , + unsigned *size /* resulting number of shifted bytes */ , + unsigned want /* maximal number of bytes to shift */ ) +{ + int shift; + + CHECKME(coord); + if (want == 0) { + *size = 0; + return 0; + } + + /* pend == SHIFT_LEFT <==> shifting to the left */ + if (pend == SHIFT_LEFT) { + for (shift = min((int) want - 1, units(coord)); shift >= 0; --shift) { + *size = part_size(coord, shift); + if (target != NULL) + *size -= sizeof (cde_item_format); + if (*size <= free_space) + break; + } + shift = shift + 1; + } else { + int total_size; + + assert("nikita-1301", pend == SHIFT_RIGHT); + + total_size = item_length_by_coord(coord); + for (shift = units(coord) - want - 1; shift < units(coord) - 1; ++shift) { + *size = total_size - part_size(coord, shift); + if (target == NULL) + *size += sizeof (cde_item_format); + if (*size <= free_space) + break; + } + shift = units(coord) - shift - 1; + } + if (shift == 0) + *size = 0; + CHECKME(coord); + return shift; +} + +/* ->copy_units() method for this item plugin. */ +reiser4_internal void +copy_units_cde(coord_t * target /* coord of target item */ , + coord_t * source /* coord of source item */ , + unsigned from /* starting unit */ , + unsigned count /* how many units to copy */ , + shift_direction where_is_free_space /* shift direction */ , + unsigned free_space /* free space in item */ ) +{ + char *header_from; + char *header_to; + + char *entry_from; + char *entry_to; + + int pos_in_target; + int data_size; + int data_delta; + int i; +#if REISER4_TRACE && REISER4_DEBUG_OUTPUT + reiser4_key debug_key; +#endif + + assert("nikita-1303", target != NULL); + assert("nikita-1304", source != NULL); + assert("nikita-1305", (int) from < units(source)); + assert("nikita-1307", (int) (from + count) <= units(source)); + + IF_TRACE(TRACE_DIR | TRACE_NODES, print_key("cde_copy source", item_key_by_coord(source, &debug_key))); + IF_TRACE(TRACE_DIR | TRACE_NODES, print_key("cde_copy target", item_key_by_coord(target, &debug_key))); + + if (where_is_free_space == SHIFT_LEFT) { + assert("nikita-1453", from == 0); + pos_in_target = units(target); + } else { + assert("nikita-1309", (int) (from + count) == units(source)); + pos_in_target = 0; + xmemmove(item_body_by_coord(target), + (char *) item_body_by_coord(target) + free_space, item_length_by_coord(target) - free_space); + } + + CHECKME(target); + CHECKME(source); + + /* expand @target */ + data_size = offset_of(source, (int) (from + count)) - offset_of(source, (int) from); + + if (units(target) == 0) + free_space -= sizeof (cde_item_format); + + expand_item(target, pos_in_target, (int) count, + (int) (item_length_by_coord(target) - free_space), (unsigned) data_size); + + /* copy first @count units of @source into @target */ + data_delta = offset_of(target, pos_in_target) - offset_of(source, (int) from); + + /* copy entries */ + entry_from = (char *) entry_at(source, (int) from); + entry_to = (char *) entry_at(source, (int) (from + count)); + xmemmove(entry_at(target, pos_in_target), entry_from, (unsigned) (entry_to - entry_from)); + + /* copy headers */ + header_from = (char *) header_at(source, (int) from); + header_to = (char *) header_at(source, (int) (from + count)); + xmemmove(header_at(target, pos_in_target), header_from, (unsigned) (header_to - header_from)); + + /* update offsets */ + for (i = pos_in_target; i < (int) (pos_in_target + count); ++i) + adj_offset(target, i, data_delta); + CHECKME(target); + CHECKME(source); +} + +/* ->cut_units() method for this item plugin. */ +reiser4_internal int +cut_units_cde(coord_t * coord /* coord of item */ , + pos_in_node_t from /* start unit pos */ , + pos_in_node_t to /* stop unit pos */ , + struct carry_cut_data *cdata UNUSED_ARG, reiser4_key *smallest_removed, + reiser4_key *new_first) +{ + char *header_from; + char *header_to; + + char *entry_from; + char *entry_to; + + int size; + int entry_delta; + int header_delta; + int i; + + unsigned count; + + CHECKME(coord); + + count = to - from + 1; + + assert("nikita-1454", coord != NULL); + assert("nikita-1455", (int) (from + count) <= units(coord)); + + if (smallest_removed) + unit_key_by_coord(coord, smallest_removed); + + if (new_first) { + coord_t next; + + /* not everything is cut from item head */ + assert("vs-1527", from == 0); + assert("vs-1528", to < units(coord) - 1); + + coord_dup(&next, coord); + next.unit_pos ++; + unit_key_by_coord(&next, new_first); + } + + size = item_length_by_coord(coord); + if (count == (unsigned) units(coord)) { + return size; + } + + header_from = (char *) header_at(coord, (int) from); + header_to = (char *) header_at(coord, (int) (from + count)); + + entry_from = (char *) entry_at(coord, (int) from); + entry_to = (char *) entry_at(coord, (int) (from + count)); + + /* move headers */ + xmemmove(header_from, header_to, (unsigned) (address(coord, size) - header_to)); + + header_delta = header_to - header_from; + + entry_from -= header_delta; + entry_to -= header_delta; + size -= header_delta; + + /* copy entries */ + xmemmove(entry_from, entry_to, (unsigned) (address(coord, size) - entry_to)); + + entry_delta = entry_to - entry_from; + size -= entry_delta; + + /* update offsets */ + + for (i = 0; i < (int) from; ++i) + adj_offset(coord, i, - header_delta); + + for (i = from; i < units(coord) - (int) count; ++i) + adj_offset(coord, i, - header_delta - entry_delta); + + cputod16((__u16) units(coord) - count, &formatted_at(coord)->num_of_entries); + + if (from == 0) { + /* entries from head was removed - move remaining to right */ + xmemmove((char *) item_body_by_coord(coord) + + header_delta + entry_delta, item_body_by_coord(coord), (unsigned) size); + if (REISER4_DEBUG) + xmemset(item_body_by_coord(coord), 0, (unsigned) header_delta + entry_delta); + } else { + /* freed space is already at the end of item */ + if (REISER4_DEBUG) + xmemset((char *) item_body_by_coord(coord) + size, 0, (unsigned) header_delta + entry_delta); + } + + return header_delta + entry_delta; +} + +reiser4_internal int +kill_units_cde(coord_t * coord /* coord of item */ , + pos_in_node_t from /* start unit pos */ , + pos_in_node_t to /* stop unit pos */ , + struct carry_kill_data *kdata UNUSED_ARG, reiser4_key *smallest_removed, + reiser4_key *new_first) +{ + return cut_units_cde(coord, from, to, 0, smallest_removed, new_first); +} + +/* ->s.dir.extract_key() method for this item plugin. */ +reiser4_internal int +extract_key_cde(const coord_t * coord /* coord of item */ , + reiser4_key * key /* resulting key */ ) +{ + directory_entry_format *dent; + + assert("nikita-1155", coord != NULL); + assert("nikita-1156", key != NULL); + + dent = entry_at(coord, idx_of(coord)); + return extract_key_from_id(&dent->id, key); +} + +reiser4_internal int +update_key_cde(const coord_t * coord, const reiser4_key * key, lock_handle * lh UNUSED_ARG) +{ + directory_entry_format *dent; + obj_key_id obj_id; + int result; + + assert("nikita-2344", coord != NULL); + assert("nikita-2345", key != NULL); + + dent = entry_at(coord, idx_of(coord)); + result = build_obj_key_id(key, &obj_id); + if (result == 0) { + dent->id = obj_id; + znode_make_dirty(coord->node); + } + return 0; +} + +/* ->s.dir.extract_name() method for this item plugin. */ +reiser4_internal char * +extract_name_cde(const coord_t * coord /* coord of item */, char *buf) +{ + directory_entry_format *dent; + + assert("nikita-1157", coord != NULL); + + dent = entry_at(coord, idx_of(coord)); + return extract_dent_name(coord, dent, buf); +} + +static int +cde_bytes(int pasting, const reiser4_item_data * data) +{ + int result; + + result = data->length; + if (!pasting) + result -= sizeof (cde_item_format); + return result; +} + +/* ->s.dir.add_entry() method for this item plugin */ +reiser4_internal int +add_entry_cde(struct inode *dir /* directory object */ , + coord_t * coord /* coord of item */ , + lock_handle * lh /* lock handle for insertion */ , + const struct dentry *name /* name to insert */ , + reiser4_dir_entry_desc * dir_entry /* parameters of new + * directory entry */ ) +{ + reiser4_item_data data; + cde_entry entry; + cde_entry_data edata; + int result; + + assert("nikita-1656", coord->node == lh->node); + assert("nikita-1657", znode_is_write_locked(coord->node)); + + edata.num_of_entries = 1; + edata.entry = &entry; + + entry.dir = dir; + entry.obj = dir_entry->obj; + entry.name = &name->d_name; + + data.data = (char *) &edata; + data.user = 0; /* &edata is not user space */ + data.iplug = item_plugin_by_id(COMPOUND_DIR_ID); + data.arg = dir_entry; + assert("nikita-1302", data.iplug != NULL); + + result = is_dot_key(&dir_entry->key); + data.length = estimate_cde(result ? coord : NULL, &data); + + /* NOTE-NIKITA quota plugin? */ + if (DQUOT_ALLOC_SPACE_NODIRTY(dir, cde_bytes(result, &data))) + return RETERR(-EDQUOT); + + if (result) + result = insert_by_coord(coord, &data, &dir_entry->key, lh, 0); + else + result = resize_item(coord, &data, &dir_entry->key, lh, 0); + return result; +} + +/* ->s.dir.rem_entry() */ +reiser4_internal int +rem_entry_cde(struct inode *dir /* directory of item */ , + const struct qstr * name, + coord_t * coord /* coord of item */ , + lock_handle * lh UNUSED_ARG /* lock handle for + * removal */ , + reiser4_dir_entry_desc * entry UNUSED_ARG /* parameters of + * directory entry + * being removed */ ) +{ + coord_t shadow; + int result; + int length; + ON_DEBUG(char buf[DE_NAME_BUF_LEN]); + + assert("nikita-2870", strlen(name->name) == name->len); + assert("nikita-2869", !strcmp(name->name, extract_name_cde(coord, buf))); + + length = sizeof (directory_entry_format) + sizeof (cde_unit_header); + if (is_longname(name->name, name->len)) + length += name->len + 1; + + if (inode_get_bytes(dir) < length) { + warning("nikita-2628", "Dir is broke: %llu: %llu", get_inode_oid(dir), inode_get_bytes(dir)); + return RETERR(-EIO); + } + + /* cut_node() is supposed to take pointers to _different_ + coords, because it will modify them without respect to + possible aliasing. To work around this, create temporary copy + of @coord. + */ + coord_dup(&shadow, coord); + result = kill_node_content(coord, &shadow, NULL, NULL, NULL, NULL, NULL); + if (result == 0) { + /* NOTE-NIKITA quota plugin? */ + DQUOT_FREE_SPACE_NODIRTY(dir, length); + } + return result; +} + +/* ->s.dir.max_name_len() method for this item plugin */ +reiser4_internal int +max_name_len_cde(const struct inode *dir /* directory */ ) +{ + return + tree_by_inode(dir)->nplug->max_item_size() - + sizeof (directory_entry_format) - sizeof (cde_item_format) - sizeof (cde_unit_header) - 2; +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/item/cde.h 2004-09-03 00:57:05.239242776 -0700 @@ -0,0 +1,78 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Compound directory item. See cde.c for description. */ + +#if !defined( __FS_REISER4_PLUGIN_COMPRESSED_DE_H__ ) +#define __FS_REISER4_PLUGIN_COMPRESSED_DE_H__ + +#include "../../forward.h" +#include "../../kassign.h" +#include "../../dformat.h" + +#include /* for struct inode */ +#include /* for struct dentry, etc */ + +typedef struct cde_unit_header { + de_id hash; + d16 offset; +} cde_unit_header; + +typedef struct cde_item_format { + d16 num_of_entries; + cde_unit_header entry[0]; +} cde_item_format; + +typedef struct cde_entry { + const struct inode *dir; + const struct inode *obj; + const struct qstr *name; +} cde_entry; + +typedef struct cde_entry_data { + int num_of_entries; + cde_entry *entry; +} cde_entry_data; + +/* plugin->item.b.* */ +reiser4_key *max_key_inside_cde(const coord_t * coord, reiser4_key * result); +int can_contain_key_cde(const coord_t * coord, const reiser4_key * key, const reiser4_item_data *); +int mergeable_cde(const coord_t * p1, const coord_t * p2); +pos_in_node_t nr_units_cde(const coord_t * coord); +reiser4_key *unit_key_cde(const coord_t * coord, reiser4_key * key); +int estimate_cde(const coord_t * coord, const reiser4_item_data * data); +void print_cde(const char *prefix, coord_t * coord); +int init_cde(coord_t * coord, coord_t * from, reiser4_item_data * data); +lookup_result lookup_cde(const reiser4_key * key, lookup_bias bias, coord_t * coord); +int paste_cde(coord_t * coord, reiser4_item_data * data, carry_plugin_info * info UNUSED_ARG); +int can_shift_cde(unsigned free_space, coord_t * coord, + znode * target, shift_direction pend, unsigned *size, unsigned want); +void copy_units_cde(coord_t * target, coord_t * source, + unsigned from, unsigned count, shift_direction where_is_free_space, unsigned free_space); +int cut_units_cde(coord_t * coord, pos_in_node_t from, pos_in_node_t to, + struct carry_cut_data *, reiser4_key * smallest_removed, reiser4_key *new_first); +int kill_units_cde(coord_t * coord, pos_in_node_t from, pos_in_node_t to, + struct carry_kill_data *, reiser4_key * smallest_removed, reiser4_key *new_first); +void print_cde(const char *prefix, coord_t * coord); +int check_cde(const coord_t * coord, const char **error); + +/* plugin->u.item.s.dir.* */ +int extract_key_cde(const coord_t * coord, reiser4_key * key); +int update_key_cde(const coord_t * coord, const reiser4_key * key, lock_handle * lh); +char *extract_name_cde(const coord_t * coord, char *buf); +int add_entry_cde(struct inode *dir, coord_t * coord, + lock_handle * lh, const struct dentry *name, reiser4_dir_entry_desc * entry); +int rem_entry_cde(struct inode *dir, const struct qstr * name, coord_t * coord, lock_handle * lh, reiser4_dir_entry_desc * entry); +int max_name_len_cde(const struct inode *dir); + +/* __FS_REISER4_PLUGIN_COMPRESSED_DE_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/item/ctail.c 2004-09-03 00:57:05.245241864 -0700 @@ -0,0 +1,1429 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* ctails (aka "crypto tails") are items for cryptcompress objects */ + +/* DESCRIPTION: + +Each cryptcompress object is stored on disk as a set of clusters sliced +into ctails. + +Internal on-disk structure: + + HEADER (1) Here stored disk cluster shift + BODY +*/ + +#include "../../forward.h" +#include "../../debug.h" +#include "../../dformat.h" +#include "../../kassign.h" +#include "../../key.h" +#include "../../coord.h" +#include "item.h" +#include "../node/node.h" +#include "../plugin.h" +#include "../object.h" +#include "../../znode.h" +#include "../../carry.h" +#include "../../tree.h" +#include "../../inode.h" +#include "../../super.h" +#include "../../context.h" +#include "../../page_cache.h" +#include "../../cluster.h" +#include "../../flush.h" +#include "../file/funcs.h" + +#include +#include + +/* return body of ctail item at @coord */ +static ctail_item_format * +ctail_formatted_at(const coord_t * coord) +{ + assert("edward-60", coord != NULL); + return item_body_by_coord(coord); +} + +reiser4_internal __u8 +cluster_shift_by_coord(const coord_t * coord) +{ + return d8tocpu(&ctail_formatted_at(coord)->cluster_shift); +} + +static unsigned long +pg_by_coord(const coord_t * coord) +{ + reiser4_key key; + + return get_key_offset(item_key_by_coord(coord, &key)) >> PAGE_CACHE_SHIFT; +} + +reiser4_internal unsigned long +clust_by_coord(const coord_t * coord) +{ + return pg_by_coord(coord) >> cluster_shift_by_coord(coord); +} + +#define cluster_key(key, coord) !(get_key_offset(key) & ~(~0ULL << cluster_shift_by_coord(coord) << PAGE_CACHE_SHIFT)) + +static char * +first_unit(coord_t * coord) +{ + /* FIXME: warning: pointer of type `void *' used in arithmetic */ + return (char *)item_body_by_coord(coord) + sizeof (ctail_item_format); +} + +/* plugin->u.item.b.max_key_inside : + tail_max_key_inside */ + +/* plugin->u.item.b.can_contain_key */ +reiser4_internal int +can_contain_key_ctail(const coord_t *coord, const reiser4_key *key, const reiser4_item_data *data) +{ + reiser4_key item_key; + + if (item_plugin_by_coord(coord) != data->iplug) + return 0; + + item_key_by_coord(coord, &item_key); + if (get_key_locality(key) != get_key_locality(&item_key) || + get_key_objectid(key) != get_key_objectid(&item_key)) + return 0; + if (get_key_offset(&item_key) + nr_units_ctail(coord) != get_key_offset(key)) + return 0; + if (cluster_key(key, coord)) + return 0; + return 1; +} + +/* plugin->u.item.b.mergeable + c-tails of different clusters are not mergeable */ +reiser4_internal int +mergeable_ctail(const coord_t * p1, const coord_t * p2) +{ + reiser4_key key1, key2; + + assert("edward-61", item_type_by_coord(p1) == UNIX_FILE_METADATA_ITEM_TYPE); + assert("edward-62", item_id_by_coord(p1) == CTAIL_ID); + + if (item_id_by_coord(p2) != CTAIL_ID) { + /* second item is of another type */ + return 0; + } + + item_key_by_coord(p1, &key1); + item_key_by_coord(p2, &key2); + if (get_key_locality(&key1) != get_key_locality(&key2) || + get_key_objectid(&key1) != get_key_objectid(&key2) || + get_key_type(&key1) != get_key_type(&key2)) { + /* items of different objects */ + return 0; + } + if (get_key_offset(&key1) + nr_units_ctail(p1) != get_key_offset(&key2)) + /* not adjacent items */ + return 0; + if (cluster_key(&key2, p2)) + return 0; + return 1; +} + +/* plugin->u.item.b.nr_units */ +reiser4_internal pos_in_node_t +nr_units_ctail(const coord_t * coord) +{ + return (item_length_by_coord(coord) - sizeof(ctail_formatted_at(coord)->cluster_shift)); +} + +/* plugin->u.item.b.estimate: + estimate how much space is needed to insert/paste @data->length bytes + into ctail at @coord */ +reiser4_internal int +estimate_ctail(const coord_t * coord /* coord of item */, + const reiser4_item_data * data /* parameters for new item */) +{ + if (coord == NULL) + /* insert */ + return (sizeof(ctail_item_format) + data->length); + else + /* paste */ + return data->length; +} + +#if REISER4_DEBUG_OUTPUT +static unsigned +cluster_size_by_coord(const coord_t * coord) +{ + return (PAGE_CACHE_SIZE << cluster_shift_by_coord(coord)); +} + + +/* ->print() method for this item plugin. */ +reiser4_internal void +print_ctail(const char *prefix /* prefix to print */ , + coord_t * coord /* coord of item to print */ ) +{ + assert("edward-63", prefix != NULL); + assert("edward-64", coord != NULL); + + if (item_length_by_coord(coord) < (int) sizeof (ctail_item_format)) + printk("%s: wrong size: %i < %i\n", prefix, item_length_by_coord(coord), sizeof (ctail_item_format)); + else + printk("%s: disk cluster size: %i\n", prefix, cluster_size_by_coord(coord)); +} +#endif + +/* ->init() method for this item plugin. */ +reiser4_internal int +init_ctail(coord_t * to /* coord of item */, + coord_t * from /* old_item */, + reiser4_item_data * data /* structure used for insertion */) +{ + int cluster_shift; /* cpu value to convert */ + + if (data) { + assert("edward-463", data->length > sizeof(ctail_item_format)); + + cluster_shift = (int)(*((char *)(data->arg))); + data->length -= sizeof(ctail_item_format); + } + else { + assert("edward-464", from != NULL); + + cluster_shift = (int)(cluster_shift_by_coord(from)); + } + cputod8(cluster_shift, &ctail_formatted_at(to)->cluster_shift); + + return 0; +} + +/* plugin->u.item.b.lookup: + NULL. (we are looking only for exact keys from item headers) */ + + +/* plugin->u.item.b.check */ + +/* plugin->u.item.b.paste */ +reiser4_internal int +paste_ctail(coord_t * coord, reiser4_item_data * data, carry_plugin_info * info UNUSED_ARG) +{ + unsigned old_nr_units; + + assert("edward-268", data->data != NULL); + /* copy only from kernel space */ + assert("edward-66", data->user == 0); + + old_nr_units = item_length_by_coord(coord) - sizeof(ctail_item_format) - data->length; + + /* ctail items never get pasted in the middle */ + + if (coord->unit_pos == 0 && coord->between == AT_UNIT) { + + /* paste at the beginning when create new item */ + assert("edward-450", item_length_by_coord(coord) == data->length + sizeof(ctail_item_format)); + assert("edward-451", old_nr_units == 0); + } + else if (coord->unit_pos == old_nr_units - 1 && coord->between == AFTER_UNIT) { + + /* paste at the end */ + coord->unit_pos++; + } + else + impossible("edward-453", "bad paste position"); + + xmemcpy(first_unit(coord) + coord->unit_pos, data->data, data->length); + + return 0; +} + +/* plugin->u.item.b.fast_paste */ + +/* plugin->u.item.b.can_shift + number of units is returned via return value, number of bytes via @size. For + ctail items they coincide */ +reiser4_internal int +can_shift_ctail(unsigned free_space, coord_t * source, + znode * target, shift_direction direction UNUSED_ARG, unsigned *size, unsigned want) +{ + /* make sure that that we do not want to shift more than we have */ + assert("edward-68", want > 0 && want <= nr_units_ctail(source)); + + *size = min(want, free_space); + + if (!target) { + /* new item will be created */ + if (*size <= sizeof(ctail_item_format)) { + *size = 0; + return 0; + } + return *size - sizeof(ctail_item_format); + } + return *size; +} + +/* plugin->u.item.b.copy_units */ +reiser4_internal void +copy_units_ctail(coord_t * target, coord_t * source, + unsigned from, unsigned count, shift_direction where_is_free_space, unsigned free_space UNUSED_ARG) +{ + /* make sure that item @target is expanded already */ + assert("edward-69", (unsigned) item_length_by_coord(target) >= count); + assert("edward-70", free_space >= count); + + if (item_length_by_coord(target) == count) { + /* new item has been created */ + assert("edward-465", count > sizeof(ctail_item_format)); + + count--; + } + if (where_is_free_space == SHIFT_LEFT) { + /* append item @target with @count first bytes of @source: + this restriction came from ordinary tails */ + assert("edward-71", from == 0); + + xmemcpy(first_unit(target) + nr_units_ctail(target) - count, first_unit(source), count); + } else { + /* target item is moved to right already */ + reiser4_key key; + + assert("edward-72", nr_units_ctail(source) == from + count); + + xmemcpy(first_unit(target), first_unit(source) + from, count); + + /* new units are inserted before first unit in an item, + therefore, we have to update item key */ + item_key_by_coord(source, &key); + set_key_offset(&key, get_key_offset(&key) + from); + + node_plugin_by_node(target->node)->update_item_key(target, &key, 0 /*info */); + } +} + +/* plugin->u.item.b.create_hook */ +/* plugin->u.item.b.kill_hook */ +reiser4_internal int +kill_hook_ctail(const coord_t *coord, pos_in_node_t from, pos_in_node_t count, carry_kill_data *kdata) +{ + struct inode *inode; + + assert("edward-291", znode_is_write_locked(coord->node)); + + inode = kdata->inode; + if (inode) { + reiser4_key key; + item_key_by_coord(coord, &key); + + if (from == 0 && cluster_key(&key, coord)) { + pgoff_t start = off_to_pg(get_key_offset(&key)); + pgoff_t end = off_to_pg(inode->i_size); + truncate_cluster(inode, start, end - start + 1); + } + } + return 0; +} + +/* for shift_hook_ctail(), + return true if the first disk cluster item has dirty child +*/ +static int +ctail_squeezable (const coord_t *coord) +{ + int result; + reiser4_key key; + jnode * child = NULL; + + assert("edward-477", coord != NULL); + assert("edward-478", item_plugin_by_coord(coord) == item_plugin_by_id(CTAIL_ID)); + + item_key_by_coord(coord, &key); + child = jlookup(current_tree, get_key_objectid(&key), pg_by_coord(coord)); + + if (!child) + return 0; + LOCK_JNODE(child); + if (jnode_is_dirty(child)) + result = 1; + else + result = 0; + UNLOCK_JNODE(child); + jput(child); + return result; +} + +/* plugin->u.item.b.shift_hook */ +reiser4_internal int +shift_hook_ctail(const coord_t * item /* coord of item */ , + unsigned from UNUSED_ARG /* start unit */ , + unsigned count UNUSED_ARG /* stop unit */ , + znode * old_node /* old parent */ ) +{ + assert("edward-479", item != NULL); + assert("edward-480", item->node != old_node); + + if (!znode_squeezable(old_node) || znode_squeezable(item->node)) + return 0; + if (ctail_squeezable(item)) + znode_set_squeezable(item->node); + return 0; +} + +static int +cut_or_kill_ctail_units(coord_t * coord, pos_in_node_t from, pos_in_node_t to, int cut, + void *p, reiser4_key * smallest_removed, reiser4_key *new_first) +{ + pos_in_node_t count; /* number of units to cut */ + char *item; + + count = to - from + 1; + item = item_body_by_coord(coord); + + /* When we cut from the end of item - we have nothing to do */ + assert("edward-73", count < nr_units_ctail(coord)); + assert("edward-74", ergo(from != 0, to == coord_last_unit_pos(coord))); + + if (smallest_removed) { + /* store smallest key removed */ + item_key_by_coord(coord, smallest_removed); + set_key_offset(smallest_removed, get_key_offset(smallest_removed) + from); + } + + if (new_first) { + assert("vs-1531", from == 0); + + item_key_by_coord(coord, new_first); + set_key_offset(new_first, get_key_offset(new_first) + from + count); + } + + if (!cut) + kill_hook_ctail(coord, from, 0, (struct carry_kill_data *)p); + + if (from == 0) { + if (count != nr_units_ctail(coord)) { + /* part of item is removed, so move free space at the beginning + of the item and update item key */ + reiser4_key key; + xmemcpy(item + to + 1, item, sizeof(ctail_item_format)); + item_key_by_coord(coord, &key); + set_key_offset(&key, get_key_offset(&key) + count); + node_plugin_by_node(coord->node)->update_item_key(coord, &key, 0 /*info */ ); + } + else { + impossible("vs-1532", "cut_units should not be called to cut evrything"); + /* whole item is cut, so more then amount of space occupied + by units got freed */ + count += sizeof(ctail_item_format); + } + if (REISER4_DEBUG) + xmemset(item, 0, count); + } + else if (REISER4_DEBUG) + xmemset(item + sizeof(ctail_item_format) + from, 0, count); + return count; +} + +/* plugin->u.item.b.cut_units */ +reiser4_internal int +cut_units_ctail(coord_t *item, pos_in_node_t from, pos_in_node_t to, + carry_cut_data *cdata, reiser4_key *smallest_removed, reiser4_key *new_first) +{ + return cut_or_kill_ctail_units(item, from, to, 1, NULL, smallest_removed, new_first); +} + +/* plugin->u.item.b.kill_units */ +reiser4_internal int +kill_units_ctail(coord_t *item, pos_in_node_t from, pos_in_node_t to, + struct carry_kill_data *kdata, reiser4_key *smallest_removed, reiser4_key *new_first) +{ + return cut_or_kill_ctail_units(item, from, to, 0, kdata, smallest_removed, new_first); +} + +/* plugin->u.item.s.file.read */ +reiser4_internal int +read_ctail(struct file *file UNUSED_ARG, flow_t *f, hint_t *hint) +{ + uf_coord_t *uf_coord; + coord_t *coord; + + uf_coord = &hint->coord; + coord = &uf_coord->base_coord; + assert("edward-127", f->user == 0); + assert("edward-128", f->data); + assert("edward-129", coord && coord->node); + assert("edward-130", coord_is_existing_unit(coord)); + assert("edward-132", znode_is_loaded(coord->node)); + + /* start read only from the beginning of ctail */ + assert("edward-133", coord->unit_pos == 0); + /* read only whole ctails */ + assert("edward-135", nr_units_ctail(coord) <= f->length); + + assert("edward-136", schedulable()); + + memcpy(f->data, (char *)first_unit(coord), (size_t)nr_units_ctail(coord)); + + mark_page_accessed(znode_page(coord->node)); + move_flow_forward(f, nr_units_ctail(coord)); + + coord->item_pos ++; + coord->between = BEFORE_ITEM; + + return 0; +} + +/* this reads one cluster form disk, + attaches buffer with decrypted and decompressed data */ +reiser4_internal int +ctail_read_cluster (reiser4_cluster_t * clust, struct inode * inode, int write) +{ + int result; + + assert("edward-139", clust->buf == NULL); + assert("edward-671", clust->hint != NULL); + assert("edward-140", clust->stat != FAKE_CLUSTER); + assert("edward-672", crc_inode_ok(inode)); + assert("edward-145", inode_get_flag(inode, REISER4_CLUSTER_KNOWN)); + + if (!hint_prev_cluster(clust)) { + done_lh(clust->hint->coord.lh); + unset_hint(clust->hint); + } + /* allocate temporary buffer of disk cluster size */ + + clust->bsize = inode_scaled_offset(inode, fsize_to_count(clust, inode) + + max_crypto_overhead(inode)); + if (clust->bsize > inode_scaled_cluster_size(inode)) + clust->bsize = inode_scaled_cluster_size(inode); + + clust->buf = reiser4_kmalloc(clust->bsize, GFP_KERNEL); + if (!clust->buf) + return -ENOMEM; + + result = find_cluster(clust, inode, 1 /* read */, write); + if (cbk_errored(result)) + goto out; + + assert("edward-673", znode_is_any_locked(clust->hint->coord.lh->node)); + + result = inflate_cluster(clust, inode); + if(result) + goto out; + return 0; + out: + put_cluster_data(clust); + return result; +} + +/* read one locked page */ +reiser4_internal int +do_readpage_ctail(reiser4_cluster_t * clust, struct page *page) +{ + int ret; + unsigned cloff; + struct inode * inode; + char * data; + int release = 0; + size_t pgcnt; + + assert("edward-212", PageLocked(page)); + + if(PageUptodate(page)) + goto exit; + + inode = page->mapping->host; + + if (!cluster_is_uptodate(clust)) { + clust->index = pg_to_clust(page->index, inode); + unlock_page(page); + ret = ctail_read_cluster(clust, inode, 0 /* do not write */); + lock_page(page); + if (ret) + return ret; + /* cluster was uptodated here, release it before exit */ + release = 1; + } + if(PageUptodate(page)) + /* races with another read/write */ + goto exit; + if (clust->stat == FAKE_CLUSTER) { + /* fill page by zeroes */ + char *kaddr = kmap_atomic(page, KM_USER0); + + assert("edward-119", clust->buf == NULL); + + memset(kaddr, 0, PAGE_CACHE_SIZE); + flush_dcache_page(page); + kunmap_atomic(kaddr, KM_USER0); + SetPageUptodate(page); + + ON_TRACE(TRACE_CTAIL, " - hole, OK\n"); + return 0; + } + /* fill page by plain text from cluster handle */ + + assert("edward-120", clust->len <= inode_cluster_size(inode)); + + /* start page offset in the cluster */ + cloff = pg_to_off_to_cloff(page->index, inode); + /* bytes in page */ + pgcnt = off_to_pgcount(inode->i_size, page->index); + assert("edward-620", off_to_pgcount(inode->i_size, page->index) > 0); + + data = kmap(page); + memcpy(data, clust->buf + cloff, pgcnt); + memset(data + pgcnt, 0, (size_t)PAGE_CACHE_SIZE - pgcnt); + kunmap(page); + SetPageUptodate(page); + exit: + if (release) + put_cluster_data(clust); + return 0; +} + +/* plugin->u.item.s.file.readpage */ +reiser4_internal int readpage_ctail(void * vp, struct page * page) +{ + int result; + hint_t hint; + lock_handle lh; + reiser4_cluster_t * clust = vp; + + assert("edward-114", clust != NULL); + assert("edward-115", PageLocked(page)); + assert("edward-116", !PageUptodate(page)); + assert("edward-117", !jprivate(page) && !PagePrivate(page)); + assert("edward-118", page->mapping && page->mapping->host); + + clust->hint = &hint; + init_lh(&lh); + result = load_file_hint(clust->file, &hint, &lh); + if (result) + return result; + + result = do_readpage_ctail(clust, page); + + hint.coord.valid = 0; + save_file_hint(clust->file, &hint); + done_lh(&lh); + put_cluster_data(clust); + + assert("edward-213", PageLocked(page)); + return result; +} + +static int +ctail_read_page_cluster(reiser4_cluster_t * clust, struct inode * inode) +{ + int i; + int result; + assert("edward-779", clust != NULL); + assert("edward-780", inode != NULL); + + set_nrpages_by_inode(clust, inode); + + result = grab_cluster_pages(inode, clust); + if (result) + return result; + result = ctail_read_cluster(clust, inode, 0 /* read */); + if (result) + goto out; + /* stream is attached at this point */ + assert("edward-781", cluster_is_uptodate(clust)); + + for (i=0; i < clust->nr_pages; i++) { + struct page * page = clust->pages[i]; + lock_page(page); + do_readpage_ctail(clust, page); + unlock_page(page); + } + release_cluster_buf(clust); + out: + release_cluster_pages(clust, 0); + return result; +} + +#define check_order(pages) \ +assert("edward-214", ergo(!list_empty(pages) && pages->next != pages->prev, \ + list_to_page(pages)->index < list_to_next_page(pages)->index)) + +/* plugin->s.file.writepage */ + +/* plugin->u.item.s.file.readpages + populate an address space with some pages, and start reads against them. + FIXME_EDWARD: this function should return errors +*/ +reiser4_internal void +readpages_ctail(void *vp, struct address_space *mapping, struct list_head *pages) +{ + int ret = 0; + hint_t hint; + lock_handle lh; + reiser4_cluster_t clust; + struct page *page; + struct pagevec lru_pvec; + struct inode * inode = mapping->host; + + check_order(pages); + pagevec_init(&lru_pvec, 0); + reiser4_cluster_init(&clust); + clust.file = vp; + clust.hint = &hint; + + alloc_clust_pages(&clust, inode); + init_lh(&lh); + + ret = load_file_hint(clust.file, &hint, &lh); + if (ret) + return; + + /* address_space-level file readahead doesn't know about + reiser4 page clustering, so we work around this fact */ + + while (!list_empty(pages)) { + page = list_to_page(pages); + list_del(&page->lru); + if (add_to_page_cache(page, mapping, page->index, GFP_KERNEL)) { + page_cache_release(page); + continue; + } + if (PageUptodate(page)) { + unlock_page(page); + continue; + } + unlock_page(page); + clust.index = pg_to_clust(page->index, inode); + ret = ctail_read_page_cluster(&clust, inode); + if (ret) + goto exit; + assert("edward-782", !cluster_is_uptodate(&clust)); + + lock_page(page); + ret = do_readpage_ctail(&clust, page); + if (!pagevec_add(&lru_pvec, page)) + __pagevec_lru_add(&lru_pvec); + if (ret) { + warning("edward-215", "do_readpage_ctail failed"); + unlock_page(page); + exit: + while (!list_empty(pages)) { + struct page *victim; + + victim = list_to_page(pages); + list_del(&victim->lru); + page_cache_release(victim); + } + break; + } + unlock_page(page); + } + assert("edward-783", !cluster_is_uptodate(&clust)); + hint.coord.valid = 0; + save_file_hint(clust.file, &hint); + + done_lh(&lh); + /* free array */ + free_clust_pages(&clust); + put_cluster_data(&clust); + pagevec_lru_add(&lru_pvec); + return; +} + +/* + plugin->u.item.s.file.append_key +*/ +reiser4_internal reiser4_key * +append_key_ctail(const coord_t *coord, reiser4_key *key) +{ + return NULL; +} + +/* key of the first item of the next cluster */ +reiser4_internal reiser4_key * +append_cluster_key_ctail(const coord_t *coord, reiser4_key *key) +{ + item_key_by_coord(coord, key); + set_key_offset(key, ((__u64)(clust_by_coord(coord)) + 1) << cluster_shift_by_coord(coord) << PAGE_CACHE_SHIFT); + return key; +} + +static int +insert_crc_flow(coord_t * coord, lock_handle * lh, flow_t * f, struct inode * inode) +{ + int result; + carry_pool pool; + carry_level lowest_level; + carry_op *op; + reiser4_item_data data; + __u8 cluster_shift = inode_cluster_shift(inode); + + init_carry_pool(&pool); + init_carry_level(&lowest_level, &pool); + + assert("edward-466", coord->between == AFTER_ITEM || coord->between == AFTER_UNIT || + coord->between == BEFORE_ITEM); + + if (coord->between == AFTER_UNIT) { + coord->unit_pos = 0; + coord->between = AFTER_ITEM; + } + op = post_carry(&lowest_level, COP_INSERT_FLOW, coord->node, 0 /* operate directly on coord -> node */ ); + if (IS_ERR(op) || (op == NULL)) + return RETERR(op ? PTR_ERR(op) : -EIO); + data.user = 0; + data.iplug = item_plugin_by_id(CTAIL_ID); + data.arg = &cluster_shift; + + data.length = 0; + data.data = 0; + + op->u.insert_flow.flags = COPI_DONT_SHIFT_LEFT | COPI_DONT_SHIFT_RIGHT; + op->u.insert_flow.insert_point = coord; + op->u.insert_flow.flow = f; + op->u.insert_flow.data = &data; + op->u.insert_flow.new_nodes = 0; + + lowest_level.track_type = CARRY_TRACK_CHANGE; + lowest_level.tracked = lh; + + ON_STATS(lowest_level.level_no = znode_get_level(coord->node)); + result = carry(&lowest_level, 0); + done_carry_pool(&pool); + + return result; +} + +static int +insert_crc_flow_in_place(coord_t * coord, lock_handle * lh, flow_t * f, struct inode * inode) +{ + int ret; + coord_t point; + lock_handle lock; + + assert("edward-674", f->length <= inode_scaled_cluster_size(inode)); + assert("edward-484", coord->between == AT_UNIT || + coord->between == AFTER_UNIT || coord->between == AFTER_ITEM); + + coord_dup (&point, coord); + + if (coord->between == AT_UNIT) { + coord_prev_item(&point); + + assert("edward-485", item_plugin_by_coord(&point) == item_plugin_by_id(CTAIL_ID)); + + point.between = AFTER_ITEM; + } + + init_lh (&lock); + copy_lh(&lock, lh); + + ret = insert_crc_flow(&point, &lock, f, inode); + done_lh(&lock); + return ret; +} + +/* overwrite tail citem or its part */ +static int +overwrite_ctail(coord_t * coord, flow_t * f) +{ + unsigned count; + + assert("edward-269", f->user == 0); + assert("edward-270", f->data != NULL); + assert("edward-271", f->length > 0); + assert("edward-272", coord_is_existing_unit(coord)); + assert("edward-273", coord->unit_pos == 0); + assert("edward-274", znode_is_write_locked(coord->node)); + assert("edward-275", schedulable()); + assert("edward-467", item_plugin_by_coord(coord) == item_plugin_by_id(CTAIL_ID)); + + count = nr_units_ctail(coord); + + if (count > f->length) + count = f->length; + xmemcpy(first_unit(coord), f->data, count); + move_flow_forward(f, count); + coord->unit_pos += count; + return 0; +} + +/* cut ctail item or its tail subset */ +static int +cut_ctail(coord_t * coord) +{ + coord_t stop; + + assert("edward-435", coord->between == AT_UNIT && + coord->item_pos < coord_num_items(coord) && + coord->unit_pos <= coord_num_units(coord)); + + if(coord->unit_pos == coord_num_units(coord)) { + /* nothing to cut */ + return 0; + } + coord_dup(&stop, coord); + stop.unit_pos = coord_last_unit_pos(coord); + + return cut_node_content(coord, &stop, NULL, NULL, NULL); +} + +#define UNPREPPED_DCLUSTER_LEN 2 + +/* insert minimal disk cluster for unprepped page cluster */ +int ctail_make_unprepped_cluster(reiser4_cluster_t * clust, struct inode * inode) +{ + char buf[UNPREPPED_DCLUSTER_LEN]; + flow_t f; + int result; + + assert("edward-675", get_current_context()->grabbed_blocks == 0); + assert("edward-676", znode_is_wlocked(clust->hint->coord.lh->node)); + + grab_space_enable(); + result = reiser4_grab_space(estimate_insert_cluster(inode, 1 /*unprepped */), 0); + if (result) + return result; + xmemset(buf, 0, UNPREPPED_DCLUSTER_LEN); + + flow_by_inode_cryptcompress(inode, buf, 0 /* kernel space */, UNPREPPED_DCLUSTER_LEN, clust_to_off(clust->index, inode), WRITE_OP, &f); + + result = insert_crc_flow(&clust->hint->coord.base_coord, clust->hint->coord.lh, &f, inode); + all_grabbed2free(); + if (result) + return result; + + assert("edward-677", reiser4_clustered_blocks(reiser4_get_current_sb())); + assert("edward-678", znode_is_dirty(clust->hint->coord.base_coord.node)); + + znode_set_squeezable(clust->hint->coord.base_coord.node); + return result; +} + +static ctail_squeeze_info_t * ctail_squeeze_data(flush_pos_t * pos) +{ + return &pos->sq->itm->u.ctail_info; +} + +/* the following functions are used by flush item methods */ +/* plugin->u.item.s.file.write ? */ +reiser4_internal int +write_ctail(flush_pos_t * pos, crc_write_mode_t mode) +{ + int result; + ctail_squeeze_info_t * info; + + assert("edward-468", pos != NULL); + assert("edward-469", pos->sq != NULL); + assert("edward-845", item_squeeze_data(pos) != NULL); + + info = ctail_squeeze_data(pos); + + switch (mode) { + case CRC_FIRST_ITEM: + case CRC_APPEND_ITEM: + assert("edward-679", info->flow.data != NULL); + result = insert_crc_flow_in_place(&pos->coord, &pos->lock, &info->flow, info->inode); + break; + case CRC_OVERWRITE_ITEM: + overwrite_ctail(&pos->coord, &info->flow); + case CRC_CUT_ITEM: + result = cut_ctail(&pos->coord); + break; + default: + result = RETERR(-EIO); + impossible("edward-244", "wrong ctail write mode"); + } + return result; +} + +reiser4_internal item_plugin * +item_plugin_by_jnode(jnode * node) +{ + assert("edward-302", jnode_is_cluster_page(node)); + return (item_plugin_by_id(CTAIL_ID)); +} + +static jnode * +next_jnode_cluster(jnode * node, struct inode *inode, reiser4_cluster_t * clust) +{ + return jlookup(tree_by_inode(inode), get_inode_oid(inode), clust_to_pg(clust->index + 1, inode)); +} + +/* plugin->u.item.f.scan */ +/* Check if the cluster node we started from is not presented by any items + in the tree. If so, create the link by inserting prosessed cluster into + the tree. Don't care about scan counter since leftward scanning will be + continued from rightmost dirty node. +*/ +reiser4_internal int scan_ctail(flush_scan * scan) +{ + int result; + struct page * page; + struct inode * inode; + reiser4_cluster_t clust; + flow_t f; + jnode * node = scan->node; + file_plugin * fplug; + + reiser4_cluster_init(&clust); + + assert("edward-227", scan->node != NULL); + assert("edward-228", jnode_is_cluster_page(scan->node)); + assert("edward-639", znode_is_write_locked(scan->parent_lock.node)); + + if (!znode_squeezable(scan->parent_lock.node)) { + assert("edward-680", !jnode_is_dirty(scan->node)); + warning("edward-681", "cluster page is already processed"); + return -EAGAIN; + } + + if (get_flush_scan_nstat(scan) == LINKED) { + /* nothing to do */ + return 0; + } + + jref(node); + + do { + LOCK_JNODE(node); + if (!(jnode_is_dirty(node) && + (node->atom == ZJNODE(scan->parent_lock.node)->atom) && + JF_ISSET(node, JNODE_NEW))) { + /* don't touch! */ + UNLOCK_JNODE(node); + jput(node); + break; + } + UNLOCK_JNODE(node); + + reiser4_cluster_init(&clust); + + page = jnode_page(node); + + assert("edward-229", page->mapping != NULL); + assert("edward-230", page->mapping != NULL); + assert("edward-231", page->mapping->host != NULL); + + inode = page->mapping->host; + fplug = inode_file_plugin(inode); + + assert("edward-244", fplug == file_plugin_by_id(CRC_FILE_PLUGIN_ID)); + assert("edward-232", inode_get_flag(inode, REISER4_CLUSTER_KNOWN)); + assert("edward-233", scan->direction == LEFT_SIDE); + + clust.index = pg_to_clust(page->index, inode); + + /* remove jnode cluster from dirty list */ + result = flush_cluster_pages(&clust, inode); + if (result) + return result; + result = deflate_cluster(NULL, &clust, inode); + if (result) + goto error; + + assert("edward-633", clust.len != 0); + + fplug->flow_by_inode(inode, clust.buf, 0, clust.len, clust_to_off(clust.index, inode), WRITE, &f); + /* insert processed data */ + result = insert_crc_flow(&scan->parent_coord, /* insert point */ + &scan->parent_lock, &f, inode); + if (result) + goto error; + assert("edward-234", f.length == 0); + JF_CLR(node, JNODE_NEW); + release_cluster_buf(&clust); + jput(node); + } + while ((node = next_jnode_cluster(node, inode, &clust))); + + /* now the child is linked to its parent, + set appropriate status */ + set_flush_scan_nstat(scan, LINKED); + return 0; + error: + release_cluster_buf(&clust); + return result; +} + +/* If true, this function attaches children */ +static int +should_attach_squeeze_idata(flush_pos_t * pos) +{ + int result; + assert("edward-431", pos != NULL); + assert("edward-432", pos->child == NULL); + assert("edward-619", znode_is_write_locked(pos->coord.node)); + assert("edward-470", item_plugin_by_coord(&pos->coord) == item_plugin_by_id(CTAIL_ID)); + + /* check for leftmost child */ + utmost_child_ctail(&pos->coord, LEFT_SIDE, &pos->child); + + if (!pos->child) + return 0; + LOCK_JNODE(pos->child); + result = jnode_is_dirty(pos->child) && + pos->child->atom == ZJNODE(pos->coord.node)->atom; + UNLOCK_JNODE(pos->child); + if (!result && pos->child) { + /* existing child isn't to attach, clear up this one */ + jput(pos->child); + pos->child = NULL; + } + return result; +} + +static int +alloc_squeeze_tfm_data(squeeze_info_t * sq) +{ + assert("edward-808", sq != NULL); + assert("edward-809", sq->tfm == NULL); + + sq->tfm = reiser4_kmalloc(SQUEEZE_TFM_INFO_SIZE , GFP_KERNEL); + if (!sq->tfm) + return -ENOMEM; + xmemset(sq->tfm, 0, SQUEEZE_TFM_INFO_SIZE); + return 0; +} + +static void +free_squeeze_tfm_data(squeeze_info_t * sq) +{ + reiser4_compression_id i; + compression_plugin * cplug; + + assert("edward-810", sq != NULL); + assert("edward-811", sq->tfm != NULL); + + for(i=0; i < LAST_COMPRESSION_ID; i++) { + if (!sq->tfm[i]) + continue; + cplug = compression_plugin_by_id(i); + assert("edward-812", cplug->free != NULL); + cplug->free(&sq->tfm[i], TFM_WRITE); + } + reiser4_kfree(sq->tfm); + sq->tfm = NULL; + return; +} + +/* plugin->init_squeeze_data() */ +static int +init_squeeze_data_ctail(squeeze_item_info_t * idata, struct inode * inode) +{ + ctail_squeeze_info_t * info; + assert("edward-813", idata != NULL); + assert("edward-814", inode != NULL); + + info = &idata->u.ctail_info; + info->clust = reiser4_kmalloc(sizeof(*info->clust), GFP_KERNEL); + if (!info->clust) + return -ENOMEM; + + reiser4_cluster_init(info->clust); + info->inode = inode; + + return 0; +} + +/* plugin->free_squeeze_data() */ +static void +free_squeeze_data_ctail(squeeze_item_info_t * idata) +{ + ctail_squeeze_info_t * info; + assert("edward-815", idata != NULL); + + info = &idata->u.ctail_info; + if (info->clust) { + release_cluster_buf(info->clust); + reiser4_kfree(info->clust); + } + return; +} + +static int +alloc_item_squeeze_data(squeeze_info_t * sq) +{ + assert("edward-816", sq != NULL); + assert("edward-817", sq->itm == NULL); + + sq->itm = reiser4_kmalloc(sizeof(*sq->itm), GFP_KERNEL); + if (sq->itm == NULL) + return -ENOMEM; + return 0; +} + +static void +free_item_squeeze_data(squeeze_info_t * sq) +{ + assert("edward-818", sq != NULL); + assert("edward-819", sq->itm != NULL); + assert("edward-820", sq->iplug != NULL); + + /* iplug->free(sq->idata); */ + free_squeeze_data_ctail(sq->itm); + + reiser4_kfree(sq->itm); + sq->itm = NULL; + return; +} + +static int +alloc_squeeze_data(flush_pos_t * pos) +{ + assert("edward-821", pos != NULL); + assert("edward-822", pos->sq == NULL); + + pos->sq = reiser4_kmalloc(sizeof(*pos->sq), GFP_KERNEL); + if (!pos->sq) + return -ENOMEM; + xmemset(pos->sq, 0, sizeof(*pos->sq)); + return 0; +} + +reiser4_internal void +free_squeeze_data(flush_pos_t * pos) +{ + squeeze_info_t * sq; + + assert("edward-823", pos != NULL); + assert("edward-824", pos->sq != NULL); + + sq = pos->sq; + if (sq->tfm) + free_squeeze_tfm_data(sq); + if (sq->itm) + free_item_squeeze_data(sq); + reiser4_kfree(pos->sq); + pos->sq = NULL; + return; +} + +static int +init_item_squeeze_data(flush_pos_t * pos, struct inode * inode) +{ + squeeze_info_t * sq; + + assert("edward-825", pos != NULL); + assert("edward-826", pos->sq != NULL); + assert("edward-827", item_squeeze_data(pos) != NULL); + assert("edward-828", inode != NULL); + + sq = pos->sq; + + xmemset(sq->itm, 0, sizeof(*sq->itm)); + + /* iplug->init_squeeze_data() */ + return init_squeeze_data_ctail(sq->itm, inode); +} + +/* create disk cluster info used by 'squeeze' phase of the flush squalloc() */ +static int +attach_squeeze_idata(flush_pos_t * pos, struct inode * inode) +{ + int ret = 0; + ctail_squeeze_info_t * info; + reiser4_cluster_t *clust; + file_plugin * fplug = inode_file_plugin(inode); + compression_plugin * cplug = inode_compression_plugin(inode); + + assert("edward-248", pos != NULL); + assert("edward-249", pos->child != NULL); + assert("edward-251", inode != NULL); + assert("edward-682", crc_inode_ok(inode)); + assert("edward-252", fplug == file_plugin_by_id(CRC_FILE_PLUGIN_ID)); + assert("edward-473", item_plugin_by_coord(&pos->coord) == item_plugin_by_id(CTAIL_ID)); + + if (!pos->sq) { + ret = alloc_squeeze_data(pos); + if (ret) + return ret; + } + if (!tfm_squeeze_data(pos) && cplug->alloc != NULL) { + ret = alloc_squeeze_tfm_data(pos->sq); + if (ret) + goto exit; + } + if (cplug->alloc != NULL && *tfm_squeeze_idx(pos, cplug->h.id) == NULL) { + ret = cplug->alloc(tfm_squeeze_idx(pos, cplug->h.id), TFM_WRITE); + if (ret) + goto exit; + } + assert("edward-829", pos->sq != NULL); + assert("edward-250", item_squeeze_data(pos) == NULL); + + pos->sq->iplug = item_plugin_by_id(CTAIL_ID); + + ret = alloc_item_squeeze_data(pos->sq); + if (ret) + goto exit; + ret = init_item_squeeze_data(pos, inode); + if (ret) + goto exit; + info = ctail_squeeze_data(pos); + clust = info->clust; + clust->index = pg_to_clust(jnode_page(pos->child)->index, inode); + + ret = flush_cluster_pages(clust, inode); + if (ret) + goto exit; + + assert("edward-830", ergo(!tfm_squeeze_pos(pos, cplug->h.id), !cplug->alloc)); + + ret = deflate_cluster(tfm_squeeze_pos(pos, cplug->h.id), clust, inode); + if (ret) + goto exit; + + inc_item_squeeze_count(pos); + + /* make flow by transformed stream */ + fplug->flow_by_inode(info->inode, clust->buf, 0/* kernel space */, + clust->len, clust_to_off(clust->index, inode), + WRITE_OP, &info->flow); + jput(pos->child); + + assert("edward-683", crc_inode_ok(inode)); + return 0; + exit: + jput(pos->child); + free_squeeze_data(pos); + return ret; +} + +/* clear up disk cluster info */ +static void +detach_squeeze_idata(squeeze_info_t * sq) +{ + squeeze_item_info_t * idata; + ctail_squeeze_info_t * info; + struct inode * inode; + + assert("edward-253", sq != NULL); + assert("edward-840", sq->itm != NULL); + + idata = sq->itm; + info = &idata->u.ctail_info; + + assert("edward-254", info->clust != NULL); + assert("edward-255", info->inode != NULL); + + inode = info->inode; + + assert("edward-841", atomic_read(&inode->i_count)); + assert("edward-256", info->clust->buf != NULL); + + atomic_dec(&inode->i_count); + + free_item_squeeze_data(sq); + return; +} + +/* plugin->u.item.f.utmost_child */ + +/* This function sets leftmost child for a first cluster item, + if the child exists, and NULL in other cases. + NOTE-EDWARD: Do not call this for RIGHT_SIDE */ + +reiser4_internal int +utmost_child_ctail(const coord_t * coord, sideof side, jnode ** child) +{ + reiser4_key key; + + item_key_by_coord(coord, &key); + + assert("edward-257", coord != NULL); + assert("edward-258", child != NULL); + assert("edward-259", side == LEFT_SIDE); + assert("edward-260", item_plugin_by_coord(coord) == item_plugin_by_id(CTAIL_ID)); + + if (!cluster_key(&key, coord)) + *child = NULL; + else + *child = jlookup(current_tree, get_key_objectid(item_key_by_coord(coord, &key)), pg_by_coord(coord)); + return 0; +} + +/* plugin->u.item.f.squeeze */ +/* write ctail in guessed mode */ +reiser4_internal int +squeeze_ctail(flush_pos_t * pos) +{ + int result; + ctail_squeeze_info_t * info = NULL; + crc_write_mode_t mode = CRC_OVERWRITE_ITEM; + + assert("edward-261", pos != NULL); + + if (!pos->sq || !item_squeeze_data(pos)) { + if (should_attach_squeeze_idata(pos)) { + /* attach squeeze item info */ + struct inode * inode; + + assert("edward-264", pos->child != NULL); + assert("edward-265", jnode_page(pos->child) != NULL); + assert("edward-266", jnode_page(pos->child)->mapping != NULL); + + inode = jnode_page(pos->child)->mapping->host; + + assert("edward-267", inode != NULL); + + /* attach item squeeze info by child and put the last one */ + result = attach_squeeze_idata(pos, inode); + pos->child = NULL; + if (result != 0) + return result; + info = ctail_squeeze_data(pos); + } + else + /* unsqueezable */ + return 0; + } + else { + /* use old squeeze info */ + + squeeze_item_info_t * idata = item_squeeze_data(pos); + info = ctail_squeeze_data(pos); + + if (info->flow.length) { + /* append or overwrite */ + if (idata->mergeable) { + mode = CRC_OVERWRITE_ITEM; + idata->mergeable = 0; + } + else + mode = CRC_APPEND_ITEM; + } + else { + /* cut or invalidate */ + if (idata->mergeable) { + mode = CRC_CUT_ITEM; + idata->mergeable = 0; + } + else { + detach_squeeze_idata(pos->sq); + return RETERR(-E_REPEAT); + } + } + } + assert("edward-433", info != NULL); + result = write_ctail(pos, mode); + if (result) { + detach_squeeze_idata(pos->sq); + return result; + } + + if (mode == CRC_APPEND_ITEM) { + /* detach squeeze info */ + assert("edward-434", info->flow.length == 0); + detach_squeeze_idata(pos->sq); + return RETERR(-E_REPEAT); + } + return 0; +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/item/ctail.h 2004-09-03 00:57:05.246241712 -0700 @@ -0,0 +1,101 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#if !defined( __FS_REISER4_CTAIL_H__ ) +#define __FS_REISER4_CTAIL_H__ + +/* cryptcompress object item. See ctail.c for description. */ +#include "../cryptcompress.h" + +#include + +typedef struct ctail_item_format { + /* cluster shift */ + d8 cluster_shift; + /* ctail body */ + d8 body[0]; +} __attribute__((packed)) ctail_item_format; + +/* for flush squeeze */ +typedef struct ctail_squeeze_info { + struct inode * inode; + reiser4_cluster_t * clust; + flow_t flow; +} ctail_squeeze_info_t; + +#define CTAIL_MIN_BODY_SIZE MIN_CRYPTO_BLOCKSIZE + +#define list_to_page(head) (list_entry((head)->prev, struct page, lru)) +#define list_to_next_page(head) (list_entry((head)->prev->prev, struct page, lru)) + +struct cut_list; + +/* plugin->item.b.* */ +int can_contain_key_ctail(const coord_t *, const reiser4_key *, const reiser4_item_data *); +int mergeable_ctail(const coord_t * p1, const coord_t * p2); +pos_in_node_t nr_units_ctail(const coord_t * coord); +int estimate_ctail(const coord_t * coord, const reiser4_item_data * data); +void print_ctail(const char *prefix, coord_t * coord); +lookup_result lookup_ctail(const reiser4_key *, lookup_bias, coord_t *); + +int paste_ctail(coord_t * coord, reiser4_item_data * data, carry_plugin_info * info UNUSED_ARG); +int init_ctail(coord_t *, coord_t *, reiser4_item_data *); +int can_shift_ctail(unsigned free_space, coord_t * coord, + znode * target, shift_direction pend, unsigned *size, unsigned want); +void copy_units_ctail(coord_t * target, coord_t * source, + unsigned from, unsigned count, shift_direction where_is_free_space, unsigned free_space); +int cut_units_ctail(coord_t *coord, pos_in_node_t from, pos_in_node_t to, + carry_cut_data *, reiser4_key * smallest_removed, reiser4_key *new_first); +int kill_units_ctail(coord_t * coord, pos_in_node_t from, pos_in_node_t to, + carry_kill_data *, reiser4_key * smallest_removed, reiser4_key *new_first); + +/*int check_check(const coord_t * coord, const char **error);*/ + +/* plugin->u.item.s.* */ +int read_ctail(struct file *, flow_t *, hint_t *); +int readpage_ctail(void *, struct page *); +void readpages_ctail(void *, struct address_space *, struct list_head *); +reiser4_key *append_key_ctail(const coord_t *, reiser4_key *); +int kill_hook_ctail(const coord_t *, pos_in_node_t, pos_in_node_t, carry_kill_data *); +int shift_hook_ctail(const coord_t *, unsigned, unsigned, znode *); + +/* plugin->u.item.f */ +int utmost_child_ctail(const coord_t *, sideof, jnode **); +int scan_ctail(flush_scan *); +int squeeze_ctail(flush_pos_t *); +item_plugin * item_plugin_by_jnode(jnode *); + +crypto_stat_t * inode_crypto_stat(struct inode *); + +void reiser4_cluster_init(reiser4_cluster_t *); +void put_cluster_data(reiser4_cluster_t *); +int cluster_is_uptodate (reiser4_cluster_t *); +void release_cluster_buf(reiser4_cluster_t *); +size_t inode_scaled_cluster_size(struct inode *); +loff_t inode_scaled_offset (struct inode *, const loff_t); +unsigned max_crypto_overhead(struct inode *); + +int inflate_cluster(reiser4_cluster_t *, struct inode *); +int find_cluster_item(hint_t * hint, const reiser4_key *key, + znode_lock_mode lock_mode, ra_info_t *ra_info, + lookup_bias bias); +int page_of_cluster(struct page *, reiser4_cluster_t *, struct inode *); +int find_cluster(reiser4_cluster_t *, struct inode *, int read, int write); +int flush_cluster_pages(reiser4_cluster_t *, struct inode *); +int deflate_cluster(tfm_info_t, reiser4_cluster_t *, struct inode *); +void truncate_cluster(struct inode * inode, pgoff_t start, long count); +int hint_prev_cluster(reiser4_cluster_t * clust); +void set_nrpages_by_inode(reiser4_cluster_t * clust, struct inode * inode); +int grab_cluster_pages(struct inode * inode, reiser4_cluster_t * clust); +void release_cluster_pages(reiser4_cluster_t * clust, int from); + +#endif /* __FS_REISER4_CTAIL_H__ */ + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/item/extent.c 2004-09-03 00:57:05.247241560 -0700 @@ -0,0 +1,196 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#include "item.h" +#include "../../key.h" +#include "../../super.h" +#include "../../carry.h" +#include "../../inode.h" +#include "../../page_cache.h" +#include "../../emergency_flush.h" +#include "../../prof.h" +#include "../../flush.h" +#include "../object.h" + + +/* prepare structure reiser4_item_data. It is used to put one extent unit into tree */ +/* Audited by: green(2002.06.13) */ +reiser4_internal reiser4_item_data * +init_new_extent(reiser4_item_data *data, void *ext_unit, int nr_extents) +{ + if (REISER4_ZERO_NEW_NODE) + memset(data, 0, sizeof(reiser4_item_data)); + + data->data = ext_unit; + /* data->data is kernel space */ + data->user = 0; + data->length = sizeof(reiser4_extent) * nr_extents; + data->arg = 0; + data->iplug = item_plugin_by_id(EXTENT_POINTER_ID); + return data; +} + +/* how many bytes are addressed by @nr first extents of the extent item */ +reiser4_internal reiser4_block_nr +extent_size(const coord_t *coord, pos_in_node_t nr) +{ + pos_in_node_t i; + reiser4_block_nr blocks; + reiser4_extent *ext; + + ext = item_body_by_coord(coord); + assert("vs-263", nr <= nr_units_extent(coord)); + + blocks = 0; + for (i = 0; i < nr; i++, ext++) { + blocks += extent_get_width(ext); + } + + return blocks * current_blocksize; +} + +reiser4_internal extent_state +state_of_extent(reiser4_extent *ext) +{ + switch ((int) extent_get_start(ext)) { + case 0: + return HOLE_EXTENT; + case 1: + return UNALLOCATED_EXTENT; + default: + break; + } + return ALLOCATED_EXTENT; +} + +reiser4_internal int +extent_is_unallocated(const coord_t *item) +{ + assert("jmacd-5133", item_is_extent(item)); + + return state_of_extent(extent_by_coord(item)) == UNALLOCATED_EXTENT; +} + +reiser4_internal int +extent_is_allocated(const coord_t *item) +{ + assert("jmacd-5133", item_is_extent(item)); + + return state_of_extent(extent_by_coord(item)) == ALLOCATED_EXTENT; +} + +/* set extent's start and width */ +reiser4_internal void +set_extent(reiser4_extent *ext, reiser4_block_nr start, reiser4_block_nr width) +{ + extent_set_start(ext, start); + extent_set_width(ext, width); +} + +/* used in split_allocated_extent, conv_extent, plug_hole to insert 1 or 2 extent units (@exts_to_add) after the one + @un_extent is set to. @un_extent itself is changed to @replace */ +reiser4_internal int +replace_extent(coord_t *un_extent, lock_handle *lh, + reiser4_key *key, reiser4_item_data *exts_to_add, const reiser4_extent *replace, unsigned flags UNUSED_ARG, + int return_inserted_position /* if it is 1 - un_extent and lh are returned set to first of newly inserted + units, if it is 0 - un_extent and lh are returned set to unit which was + replaced */) +{ + int result; + coord_t coord_after; + lock_handle lh_after; + tap_t watch; + znode *orig_znode; + ON_DEBUG(reiser4_extent orig_ext); /* this is for debugging */ + + assert("vs-990", coord_is_existing_unit(un_extent)); + assert("vs-1375", znode_is_write_locked(un_extent->node)); + assert("vs-1426", extent_get_width(replace) != 0); + assert("vs-1427", extent_get_width((reiser4_extent *)exts_to_add->data) != 0); + + coord_dup(&coord_after, un_extent); + init_lh(&lh_after); + copy_lh(&lh_after, lh); + tap_init(&watch, &coord_after, &lh_after, ZNODE_WRITE_LOCK); + tap_monitor(&watch); + + ON_DEBUG(orig_ext = *extent_by_coord(un_extent)); + orig_znode = un_extent->node; + + /* make sure that key is set properly */ + if (REISER4_DEBUG) { + reiser4_key tmp; + + unit_key_by_coord(un_extent, &tmp); + set_key_offset(&tmp, get_key_offset(&tmp) + extent_get_width(replace) * current_blocksize); + assert("vs-1080", keyeq(&tmp, key)); + } + + DISABLE_NODE_CHECK; + + /* set insert point after unit to be replaced */ + un_extent->between = AFTER_UNIT; + + result = insert_into_item(un_extent, + return_inserted_position ? lh : 0, + /*(flags == COPI_DONT_SHIFT_LEFT) ? 0 : lh,*/ key, exts_to_add, flags); + if (!result) { + /* now we have to replace the unit after which new units were inserted. Its position is tracked by + @watch */ + reiser4_extent *ext; + + if (coord_after.node != orig_znode) { + coord_clear_iplug(&coord_after); + result = zload(coord_after.node); + } + + if (likely(!result)) { + ext = extent_by_coord(&coord_after); + + assert("vs-987", znode_is_loaded(coord_after.node)); + assert("vs-988", !memcmp(ext, &orig_ext, sizeof (*ext))); + + *ext = *replace; + znode_make_dirty(coord_after.node); + + if (coord_after.node != orig_znode) + zrelse(coord_after.node); + + if (return_inserted_position == 0) { + /* return un_extent and lh set to the same */ + assert("vs-1662", WITH_DATA(coord_after.node, !memcmp(replace, extent_by_coord(&coord_after), sizeof(reiser4_extent)))); + + *un_extent = coord_after; + done_lh(lh); + copy_lh(lh, &lh_after); + } else { + /* return un_extent and lh set to first of inserted units */ + assert("vs-1663", WITH_DATA(un_extent->node, !memcmp(exts_to_add->data, extent_by_coord(un_extent), sizeof(reiser4_extent)))); + assert("vs-1664", lh->node == un_extent->node); + } + } + } + tap_done(&watch); + + ENABLE_NODE_CHECK; + return result; +} + +reiser4_internal lock_handle * +znode_lh(znode *node) +{ + assert("vs-1371", znode_is_write_locked(node)); + assert("vs-1372", znode_is_wlocked_once(node)); + return owners_list_front(&node->lock.owners); +} + + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/item/extent_file_ops.c 2004-09-03 00:57:05.253240648 -0700 @@ -0,0 +1,1401 @@ +/* COPYRIGHT 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#include "item.h" +#include "../../inode.h" +#include "../../page_cache.h" +#include "../../flush.h" /* just for jnode_tostring */ +#include "../object.h" + +#include + +static inline reiser4_extent * +ext_by_offset(const znode *node, int offset) +{ + reiser4_extent *ext; + + ext = (reiser4_extent *)(zdata(node) + offset); + return ext; +} + +static inline reiser4_extent * +ext_by_ext_coord(const uf_coord_t *uf_coord) +{ + reiser4_extent *ext; + + ext = ext_by_offset(uf_coord->base_coord.node, uf_coord->extension.extent.ext_offset); + assert("vs-1650", extent_get_start(ext) == extent_get_start(&uf_coord->extension.extent.extent)); + assert("vs-1651", extent_get_width(ext) == extent_get_width(&uf_coord->extension.extent.extent)); + return ext; +} + +#if REISER4_DEBUG +static int +coord_extension_is_ok(const uf_coord_t *uf_coord) +{ + const coord_t *coord; + const extent_coord_extension_t *ext_coord; + reiser4_extent *ext; + + coord = &uf_coord->base_coord; + ext_coord = &uf_coord->extension.extent; + ext = ext_by_ext_coord(uf_coord); + + return WITH_DATA(coord->node, (uf_coord->valid == 1 && + coord_is_iplug_set(coord) && + item_is_extent(coord) && + ext_coord->nr_units == nr_units_extent(coord) && + ext == extent_by_coord(coord) && + ext_coord->width == extent_get_width(ext) && + coord->unit_pos < ext_coord->nr_units && + ext_coord->pos_in_unit < ext_coord->width && + extent_get_start(ext) == extent_get_start(&ext_coord->extent) && + extent_get_width(ext) == extent_get_width(&ext_coord->extent))); +} + +#endif + +/* @coord is set either to the end of last extent item of a file + (coord->node is a node on the twig level) or to a place where first + item of file has to be inserted to (coord->node is leaf + node). Calculate size of hole to be inserted. If that hole is too + big - only part of it is inserted */ +static int +add_hole(coord_t *coord, lock_handle *lh, const reiser4_key *key /* key of position in a file for write */) +{ + int result; + znode *loaded; + reiser4_extent *ext, new_ext; + reiser4_block_nr hole_width; + reiser4_item_data item; + reiser4_key hole_key; + + /*coord_clear_iplug(coord);*/ + result = zload(coord->node); + if (result) + return result; + loaded = coord->node; + + if (znode_get_level(coord->node) == LEAF_LEVEL) { + /* there are no items of this file yet. First item will be + hole extent inserted here */ + + /* @coord must be set for inserting of new item */ + assert("vs-711", coord_is_between_items(coord)); + + hole_key = *key; + set_key_offset(&hole_key, 0ull); + + hole_width = ((get_key_offset(key) + current_blocksize - 1) >> + current_blocksize_bits); + assert("vs-710", hole_width > 0); + + /* compose body of hole extent */ + set_extent(&new_ext, HOLE_EXTENT_START, hole_width); + + result = insert_extent_by_coord(coord, init_new_extent(&item, &new_ext, 1), &hole_key, lh); + zrelse(loaded); + return result; + } + + /* last item of file may have to be appended with hole */ + assert("vs-708", znode_get_level(coord->node) == TWIG_LEVEL); + assert("vs-714", item_id_by_coord(coord) == EXTENT_POINTER_ID); + + /* make sure we are at proper item */ + assert("vs-918", keylt(key, max_key_inside_extent(coord, &hole_key))); + + /* key of first byte which is not addressed by this extent */ + append_key_extent(coord, &hole_key); + + if (keyle(key, &hole_key)) { + /* there is already extent unit which contains position + specified by @key */ + zrelse(loaded); + return 0; + } + + /* extent item has to be appended with hole. Calculate length of that + hole */ + hole_width = ((get_key_offset(key) - get_key_offset(&hole_key) + + current_blocksize - 1) >> current_blocksize_bits); + assert("vs-954", hole_width > 0); + + /* set coord after last unit */ + coord_init_after_item_end(coord); + + /* get last extent in the item */ + ext = extent_by_coord(coord); + if (state_of_extent(ext) == HOLE_EXTENT) { + /* last extent of a file is hole extent. Widen that extent by + @hole_width blocks. Note that we do not worry about + overflowing - extent width is 64 bits */ + set_extent(ext, HOLE_EXTENT_START, extent_get_width(ext) + hole_width); + znode_make_dirty(coord->node); + zrelse(loaded); + return 0; + } + + /* append item with hole extent unit */ + assert("vs-713", (state_of_extent(ext) == ALLOCATED_EXTENT || state_of_extent(ext) == UNALLOCATED_EXTENT)); + + /* compose body of hole extent */ + set_extent(&new_ext, HOLE_EXTENT_START, hole_width); + + result = insert_into_item(coord, lh, &hole_key, init_new_extent(&item, &new_ext, 1), 0 /*flags */ ); + zrelse(loaded); + return result; +} + +/* insert extent item (containing one unallocated extent of width 1) to place + set by @coord */ +static int +insert_first_block(uf_coord_t *uf_coord, const reiser4_key *key, reiser4_block_nr *block) +{ + int result; + reiser4_extent ext; + reiser4_item_data unit; + + /* make sure that we really write to first block */ + assert("vs-240", get_key_offset(key) == 0); + + /* extent insertion starts at leaf level */ + assert("vs-719", znode_get_level(uf_coord->base_coord.node) == LEAF_LEVEL); + + set_extent(&ext, UNALLOCATED_EXTENT_START, 1); + result = insert_extent_by_coord(&uf_coord->base_coord, init_new_extent(&unit, &ext, 1), key, uf_coord->lh); + if (result) { + /* FIXME-VITALY: this is grabbed at file_write time. */ + /* grabbed2free ((__u64)1); */ + return result; + } + + *block = fake_blocknr_unformatted(); + + /* invalidate coordinate, research must be performed to continue because write will continue on twig level */ + uf_coord->valid = 0; + return 0; +} + +/* @coord is set to the end of extent item. Append it with pointer to one block - either by expanding last unallocated + extent or by appending a new one of width 1 */ +static int +append_one_block(uf_coord_t *uf_coord, reiser4_key *key, reiser4_block_nr *block) +{ + int result; + reiser4_extent new_ext; + reiser4_item_data unit; + coord_t *coord; + extent_coord_extension_t *ext_coord; + reiser4_extent *ext; + + coord = &uf_coord->base_coord; + ext_coord = &uf_coord->extension.extent; + ext = ext_by_ext_coord(uf_coord); + + /* check correctness of position in the item */ + assert("vs-228", coord->unit_pos == coord_last_unit_pos(coord)); + assert("vs-1311", coord->between == AFTER_UNIT); + assert("vs-1302", ext_coord->pos_in_unit == ext_coord->width - 1); + assert("vs-883", + ( { + reiser4_key next; + keyeq(key, append_key_extent(coord, &next)); + })); + + switch (state_of_extent(ext)) { + case UNALLOCATED_EXTENT: + set_extent(ext, UNALLOCATED_EXTENT_START, extent_get_width(ext) + 1); + znode_make_dirty(coord->node); + + /* update coord extension */ + ext_coord->width ++; + ON_DEBUG(extent_set_width(&uf_coord->extension.extent.extent, ext_coord->width)); + break; + + case HOLE_EXTENT: + case ALLOCATED_EXTENT: + /* append one unallocated extent of width 1 */ + set_extent(&new_ext, UNALLOCATED_EXTENT_START, 1); + result = insert_into_item(coord, uf_coord->lh, key, init_new_extent(&unit, &new_ext, 1), 0 /* flags */ ); + /* FIXME: for now */ + uf_coord->valid = 0; + if (result) + return result; + break; + default: + assert("", 0); + } + + *block = fake_blocknr_unformatted(); + return 0; +} + +/* @coord is set to hole unit inside of extent item, replace hole unit with an + unit for unallocated extent of the width 1, and perhaps a hole unit before + the unallocated unit and perhaps a hole unit after the unallocated unit. */ +static int +plug_hole(uf_coord_t *uf_coord, reiser4_key *key) +{ + reiser4_extent *ext, new_exts[2], /* extents which will be added after original + * hole one */ + replace; /* extent original hole extent will be replaced + * with */ + reiser4_block_nr width, pos_in_unit; + reiser4_item_data item; + int count; + coord_t *coord; + extent_coord_extension_t *ext_coord; + reiser4_key tmp_key; + int return_inserted_position; + + coord = &uf_coord->base_coord; + ext_coord = &uf_coord->extension.extent; + ext = ext_by_ext_coord(uf_coord); + + width = ext_coord->width; + pos_in_unit = ext_coord->pos_in_unit; + + if (width == 1) { + set_extent(ext, UNALLOCATED_EXTENT_START, 1); + znode_make_dirty(coord->node); + return 0; + } else if (pos_in_unit == 0) { + if (coord->unit_pos) { + if (state_of_extent(ext - 1) == UNALLOCATED_EXTENT) { + extent_set_width(ext - 1, extent_get_width(ext - 1) + 1); + extent_set_width(ext, width - 1); + znode_make_dirty(coord->node); + + /* update coord extension */ + coord->unit_pos --; + ext_coord->width = extent_get_width(ext - 1); + ext_coord->pos_in_unit = ext_coord->width - 1; + ext_coord->ext_offset -= sizeof(reiser4_extent); + ON_DEBUG(ext_coord->extent = *extent_by_coord(coord)); + return 0; + } + } + /* extent for replace */ + set_extent(&replace, UNALLOCATED_EXTENT_START, 1); + /* extent to be inserted */ + set_extent(&new_exts[0], HOLE_EXTENT_START, width - 1); + + /* have replace_extent to return with @coord and @uf_coord->lh set to unit which was replaced */ + return_inserted_position = 0; + count = 1; + } else if (pos_in_unit == width - 1) { + if (coord->unit_pos < nr_units_extent(coord) - 1) { + if (state_of_extent(ext + 1) == UNALLOCATED_EXTENT) { + extent_set_width(ext + 1, extent_get_width(ext + 1) + 1); + extent_set_width(ext, width - 1); + znode_make_dirty(coord->node); + + /* update coord extension */ + coord->unit_pos ++; + ext_coord->width = extent_get_width(ext + 1); + ext_coord->pos_in_unit = 0; + ext_coord->ext_offset += sizeof(reiser4_extent); + ON_DEBUG(ext_coord->extent = *extent_by_coord(coord)); + return 0; + } + } + /* extent for replace */ + set_extent(&replace, HOLE_EXTENT_START, width - 1); + /* extent to be inserted */ + set_extent(&new_exts[0], UNALLOCATED_EXTENT_START, 1); + + /* have replace_extent to return with @coord and @uf_coord->lh set to unit which was inserted */ + return_inserted_position = 1; + count = 1; + } else { + /* extent for replace */ + set_extent(&replace, HOLE_EXTENT_START, pos_in_unit); + /* extents to be inserted */ + set_extent(&new_exts[0], UNALLOCATED_EXTENT_START, 1); + set_extent(&new_exts[1], HOLE_EXTENT_START, width - pos_in_unit - 1); + + /* have replace_extent to return with @coord and @uf_coord->lh set to first of units which were + inserted */ + return_inserted_position = 1; + count = 2; + } + + /* insert_into_item will insert new units after the one @coord is set + to. So, update key correspondingly */ + unit_key_by_coord(coord, &tmp_key); + set_key_offset(&tmp_key, (get_key_offset(&tmp_key) + extent_get_width(&replace) * current_blocksize)); + + uf_coord->valid = 0; + return replace_extent(coord, uf_coord->lh, &tmp_key, init_new_extent(&item, new_exts, count), &replace, 0 /* flags */, return_inserted_position); +} + +/* make unallocated node pointer in the position @uf_coord is set to */ +static int +overwrite_one_block(uf_coord_t *uf_coord, reiser4_key *key, reiser4_block_nr *block, int *created, + struct inode *inode) +{ + int result; + extent_coord_extension_t *ext_coord; + reiser4_extent *ext; + oid_t oid; + pgoff_t index; + + oid = get_key_objectid(key); + index = get_key_offset(key) >> current_blocksize_bits; + + assert("vs-1312", uf_coord->base_coord.between == AT_UNIT); + + result = 0; + *created = 0; + ext_coord = &uf_coord->extension.extent; + ext = ext_by_ext_coord(uf_coord); + + switch (state_of_extent(ext)) { + case ALLOCATED_EXTENT: + *block = extent_get_start(ext) + ext_coord->pos_in_unit; + break; + + case HOLE_EXTENT: + if (inode != NULL && DQUOT_ALLOC_BLOCK(inode, 1)) + return RETERR(-EDQUOT); + result = plug_hole(uf_coord, key); + if (!result) { + *block = fake_blocknr_unformatted(); + *created = 1; + } else { + if (inode != NULL) + DQUOT_FREE_BLOCK(inode, 1); + } + break; + + case UNALLOCATED_EXTENT: + break; + + default: + impossible("vs-238", "extent of unknown type found"); + result = RETERR(-EIO); + break; + } + + return result; +} + +#if REISER4_DEBUG + +/* after make extent uf_coord's lock handle must be set to node containing unit which was inserted/found */ +static void +check_make_extent_result(int result, write_mode_t mode, const reiser4_key *key, + const lock_handle *lh, reiser4_block_nr block) +{ + coord_t coord; + + if (result != 0) + return; + + assert("vs-960", znode_is_write_locked(lh->node)); + zload(lh->node); + result = lh->node->nplug->lookup(lh->node, key, FIND_EXACT, &coord); + assert("vs-1502", result == NS_FOUND); + assert("vs-1656", coord_is_existing_unit(&coord)); + + if (blocknr_is_fake(&block)) { + assert("vs-1657", state_of_extent(extent_by_coord(&coord)) == UNALLOCATED_EXTENT); + } else if (block == 0) { + assert("vs-1660", mode == OVERWRITE_ITEM); + assert("vs-1657", state_of_extent(extent_by_coord(&coord)) == UNALLOCATED_EXTENT); + } else { + reiser4_key tmp; + reiser4_block_nr pos_in_unit; + + assert("vs-1658", state_of_extent(extent_by_coord(&coord)) == ALLOCATED_EXTENT); + unit_key_by_coord(&coord, &tmp); + pos_in_unit = (get_key_offset(key) - get_key_offset(&tmp)) >> current_blocksize_bits; + assert("vs-1659", block == extent_get_start(extent_by_coord(&coord)) + pos_in_unit); + } + zrelse(lh->node); +} + +#endif + +/* when @inode is not NULL, alloc quota before updating extent item */ +static int +make_extent(reiser4_key *key, uf_coord_t *uf_coord, write_mode_t mode, + reiser4_block_nr *block, int *created, struct inode *inode) +{ + int result; + oid_t oid; + pgoff_t index; + + oid = get_key_objectid(key); + index = get_key_offset(key) >> current_blocksize_bits; + + assert("vs-960", znode_is_write_locked(uf_coord->base_coord.node)); + assert("vs-1334", znode_is_loaded(uf_coord->base_coord.node)); + + DISABLE_NODE_CHECK; + + *block = 0; + switch (mode) { + case FIRST_ITEM: + /* new block will be inserted into file. Check quota */ + if (inode != NULL && DQUOT_ALLOC_BLOCK(inode, 1)) + return RETERR(-EDQUOT); + + /* create first item of the file */ + result = insert_first_block(uf_coord, key, block); + if (result && inode != NULL) + DQUOT_FREE_BLOCK(inode, 1); + *created = 1; + break; + + case APPEND_ITEM: + /* new block will be inserted into file. Check quota */ + if (inode != NULL && DQUOT_ALLOC_BLOCK(inode, 1)) + return RETERR(-EDQUOT); + + /* FIXME: item plugin should be initialized + item_plugin_by_coord(&uf_coord->base_coord);*/ + assert("vs-1316", coord_extension_is_ok(uf_coord)); + result = append_one_block(uf_coord, key, block); + if (result && inode != NULL) + DQUOT_FREE_BLOCK(inode, 1); + *created = 1; + break; + + case OVERWRITE_ITEM: + /* FIXME: item plugin should be initialized + item_plugin_by_coord(&uf_coord->base_coord);*/ + assert("vs-1316", coord_extension_is_ok(uf_coord)); + result = overwrite_one_block(uf_coord, key, block, created, inode); + break; + + default: + assert("vs-1346", 0); + result = RETERR(-E_REPEAT); + break; + } + + ENABLE_NODE_CHECK; + + ON_DEBUG(check_make_extent_result(result, mode, key, uf_coord->lh, *block)); + + return result; +} + +/* drop longterm znode lock before calling balance_dirty_pages. balance_dirty_pages may cause transaction to close, + therefore we have to update stat data if necessary */ +static int +extent_balance_dirty_pages(struct address_space *mapping, const flow_t *f, + hint_t *hint) +{ + return item_balance_dirty_pages(mapping, f, hint, 0, 0/* do not set hint */); +} + +/* estimate and reserve space which may be required for writing one page of file */ +static int +reserve_extent_write_iteration(struct inode *inode, reiser4_tree *tree) +{ + int result; + + grab_space_enable(); + /* one unformatted node and one insertion into tree and one stat data update may be involved */ + result = reiser4_grab_space(1 + /* Hans removed reservation for balancing here. */ + /* if extent items will be ever used by plugins other than unix file plugin - estimate update should instead be taken by + inode_file_plugin(inode)->estimate.update(inode) + */ + estimate_update_common(inode), + 0/* flags */); + return result; +} + +static void +write_move_coord(coord_t *coord, uf_coord_t *uf_coord, write_mode_t mode, int full_page) +{ + extent_coord_extension_t *ext_coord; + + assert("vs-1339", ergo(mode == OVERWRITE_ITEM, coord->between == AT_UNIT)); + assert("vs-1341", ergo(mode == FIRST_ITEM, uf_coord->valid == 0)); + + if (uf_coord->valid == 0) + return; + + ext_coord = &uf_coord->extension.extent; + + if (mode == APPEND_ITEM) { + assert("vs-1340", coord->between == AFTER_UNIT); + assert("vs-1342", coord->unit_pos == ext_coord->nr_units - 1); + assert("vs-1343", ext_coord->pos_in_unit == ext_coord->width - 2); + assert("vs-1344", state_of_extent(ext_by_ext_coord(uf_coord)) == UNALLOCATED_EXTENT); + ON_DEBUG(ext_coord->extent = *ext_by_ext_coord(uf_coord)); + ext_coord->pos_in_unit ++; + if (!full_page) + coord->between = AT_UNIT; + return; + } + + assert("vs-1345", coord->between == AT_UNIT); + + if (!full_page) + return; + if (ext_coord->pos_in_unit == ext_coord->width - 1) { + /* last position in the unit */ + if (coord->unit_pos == ext_coord->nr_units - 1) { + /* last unit in the item */ + uf_coord->valid = 0; + } else { + /* move to the next unit */ + coord->unit_pos ++; + ext_coord->ext_offset += sizeof(reiser4_extent); + ON_DEBUG(ext_coord->extent = *ext_by_offset(coord->node, ext_coord->ext_offset)); + ext_coord->width = extent_get_width(ext_by_offset(coord->node, ext_coord->ext_offset)); + ext_coord->pos_in_unit = 0; + } + } else + ext_coord->pos_in_unit ++; +} + +static void +set_hint_unlock_node(hint_t *hint, flow_t *f, znode_lock_mode mode) +{ + if (hint->coord.valid) { + set_hint(hint, &f->key, mode); + } else { + unset_hint(hint); + } + longterm_unlock_znode(hint->coord.lh); +} + +static int +write_is_partial(struct inode *inode, loff_t file_off, unsigned page_off, unsigned count) +{ + if (count == inode->i_sb->s_blocksize) + return 0; + if (page_off == 0 && file_off + count >= inode->i_size) + return 0; + return 1; +} + +/* this initialize content of page not covered by write */ +static void +zero_around(struct page *page, int from, int count) +{ + char *data; + + data = kmap_atomic(page, KM_USER0); + memset(data, 0, from); + memset(data + from + count, 0, PAGE_CACHE_SIZE - from - count); + flush_dcache_page(page); + kunmap_atomic(data, KM_USER0); +} + +/* write flow's data into file by pages */ +static int +extent_write_flow(struct inode *inode, flow_t *flow, hint_t *hint, + int grabbed, /* 0 if space for operation is not reserved yet, 1 - otherwise */ + write_mode_t mode) +{ + int result; + loff_t file_off; + unsigned long page_nr; + unsigned long page_off, count; + struct page *page; + jnode *j; + uf_coord_t *uf_coord; + coord_t *coord; + oid_t oid; + reiser4_tree *tree; + reiser4_key page_key; + reiser4_block_nr blocknr; + int created; + + + assert("nikita-3139", !inode_get_flag(inode, REISER4_NO_SD)); + assert("vs-885", current_blocksize == PAGE_CACHE_SIZE); + assert("vs-700", flow->user == 1); + assert("vs-1352", flow->length > 0); + + tree = tree_by_inode(inode); + oid = get_inode_oid(inode); + uf_coord = &hint->coord; + coord = &uf_coord->base_coord; + + /* position in a file to start write from */ + file_off = get_key_offset(&flow->key); + /* index of page containing that offset */ + page_nr = (unsigned long)(file_off >> PAGE_CACHE_SHIFT); + /* offset within the page */ + page_off = (unsigned long)(file_off & (PAGE_CACHE_SIZE - 1)); + + /* key of first byte of page */ + page_key = flow->key; + set_key_offset(&page_key, (loff_t)page_nr << PAGE_CACHE_SHIFT); + do { + if (!grabbed) { + result = reserve_extent_write_iteration(inode, tree); + if (result) + break; + } + /* number of bytes to be written to page */ + count = PAGE_CACHE_SIZE - page_off; + if (count > flow->length) + count = flow->length; + + write_page_log(inode->i_mapping, page_nr); + + result = make_extent(&page_key, uf_coord, mode, &blocknr, &created, inode/* check quota */); + if (result) { + goto exit1; + } + + /* look for jnode and create it if it does not exist yet */ + j = find_get_jnode(tree, inode->i_mapping, oid, page_nr); + if (IS_ERR(j)) { + result = PTR_ERR(j); + goto exit1; + } + LOCK_JNODE(j); + if (created) { + /* extent corresponding to this jnode was just created */ + assert("vs-1504", *jnode_get_block(j) == 0); + JF_SET(j, JNODE_CREATED); + /* new block is added to file. Update inode->i_blocks and inode->i_bytes. FIXME: + inode_set/get/add/sub_bytes is used to be called by quota macros */ + /*inode_add_bytes(inode, PAGE_CACHE_SIZE);*/ + } + if (*jnode_get_block(j) == 0) { + jnode_set_block(j, &blocknr); + } else { + assert("vs-1508", !blocknr_is_fake(&blocknr)); + assert("vs-1507", ergo(blocknr, *jnode_get_block(j) == blocknr)); + } + UNLOCK_JNODE(j); + + /* get page looked and attached to jnode */ + page = jnode_get_page_locked(j, GFP_KERNEL); + if (IS_ERR(page)) { + result = PTR_ERR(page); + goto exit2; + } + + page_cache_get(page); + + if (!PageUptodate(page)) { + if (mode == OVERWRITE_ITEM) { + /* this page may be either an anonymous page (a page which was dirtied via mmap, + writepage-ed and for which extent pointer was just created. In this case jnode is + eflushed) or correspod to not page cached block (in which case created == 0). In + either case we have to read this page if it is being overwritten partially */ + if (write_is_partial(inode, file_off, page_off, count) && + (created == 0 || JF_ISSET(j, JNODE_EFLUSH))) { + result = page_io(page, j, READ, GFP_KERNEL); + if (result) + goto exit3; + lock_page(page); + if (!PageUptodate(page)) + goto exit3; + } else { + zero_around(page, page_off, count); + } + } else { + /* new page added to the file. No need to carry about data it might contain. Zero + content of new page around write area */ + assert("vs-1681", !JF_ISSET(j, JNODE_EFLUSH)); + zero_around(page, page_off, count); + } + } + + UNDER_SPIN_VOID(jnode, j, eflush_del(j, 1)); + + move_flow_forward(flow, count); + write_move_coord(coord, uf_coord, mode, page_off + count == PAGE_CACHE_SIZE); + set_hint_unlock_node(hint, flow, ZNODE_WRITE_LOCK); + + assert("vs-1503", UNDER_SPIN(jnode, j, (!JF_ISSET(j, JNODE_EFLUSH) && jnode_page(j) == page))); + assert("nikita-3033", schedulable()); + if (!lock_stack_isclean(get_current_lock_stack())) + print_clog(); + assert("nikita-2104", lock_stack_isclean(get_current_lock_stack())); + + /* copy user data into page */ + result = __copy_from_user((char *)kmap(page) + page_off, flow->data - count, count); + kunmap(page); + if (unlikely(result)) { + /* FIXME: write(fd, 0, 10); to empty file will write no data but file will get increased + size. */ + result = RETERR(-EFAULT); + goto exit3; + } + + set_page_dirty_internal(page, 0); + SetPageUptodate(page); + if (!PageReferenced(page)) + SetPageReferenced(page); + + unlock_page(page); + page_cache_release(page); + + /* FIXME: possible optimization: if jnode is not dirty yet - it gets into clean list in try_capture and + then in jnode_mark_dirty gets moved to dirty list. So, it would be more optimal to put jnode directly + to dirty list */ + LOCK_JNODE(j); + result = try_capture(j, ZNODE_WRITE_LOCK, 0, 1/* can_coc */); + if (result) + goto exit2; + jnode_make_dirty_locked(j); + UNLOCK_JNODE(j); + + jput(j); + + /* throttle the writer */ + result = extent_balance_dirty_pages(inode->i_mapping, flow, hint); + if (!grabbed) + all_grabbed2free(); + if (result) { + reiser4_stat_inc(extent.bdp_caused_repeats); + break; + } + + page_off = 0; + page_nr ++; + file_off += count; + set_key_offset(&page_key, (loff_t)page_nr << PAGE_CACHE_SHIFT); + + continue; + + exit3: + unlock_page(page); + page_cache_release(page); + exit2: + if (created) + inode_sub_bytes(inode, PAGE_CACHE_SIZE); + jput(j); + exit1: + if (!grabbed) + all_grabbed2free(); + break; + + /* hint is unset by make_page_extent when first extent of a + file was inserted: in that case we can not use coord anymore + because we are to continue on twig level but are still at + leaf level + */ + } while (flow->length && uf_coord->valid == 1); + + return result; +} + +/* estimate and reserve space which may be required for appending file with hole stored in extent */ +static int +extent_hole_reserve(reiser4_tree *tree) +{ + /* adding hole may require adding a hole unit into extent item and stat data update */ + grab_space_enable(); + return reiser4_grab_space(estimate_one_insert_into_item(tree) * 2, 0); +} + +static int +extent_write_hole(struct inode *inode, flow_t *flow, hint_t *hint, int grabbed) +{ + int result; + loff_t new_size; + coord_t *coord; + + coord = &hint->coord.base_coord; + if (!grabbed) { + result = extent_hole_reserve(znode_get_tree(coord->node)); + if (result) + return result; + } + + new_size = get_key_offset(&flow->key) + flow->length; + set_key_offset(&flow->key, new_size); + flow->length = 0; + result = add_hole(coord, hint->coord.lh, &flow->key); + hint->coord.valid = 0; + if (!result) { + done_lh(hint->coord.lh); + INODE_SET_FIELD(inode, i_size, new_size); + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + result = reiser4_update_sd(inode); + } + if (!grabbed) + all_grabbed2free(); + return result; +} + +/* + plugin->s.file.write + It can be called in two modes: + 1. real write - to write data from flow to a file (@flow->data != 0) + 2. expanding truncate (@f->data == 0) +*/ +reiser4_internal int +write_extent(struct inode *inode, flow_t *flow, hint_t *hint, + int grabbed, /* extent's write may be called from plain unix file write and from tail conversion. In first + case (grabbed == 0) space is not reserved forehand, so, it must be done here. When it is + being called from tail conversion - space is reserved already for whole operation which may + involve several calls to item write. In this case space reservation will not be done + here */ + write_mode_t mode) +{ + if (flow->data) + /* real write */ + return extent_write_flow(inode, flow, hint, grabbed, mode); + + /* expanding truncate. add_hole requires f->key to be set to new end of file */ + return extent_write_hole(inode, flow, hint, grabbed); +} + +/* move coord one page forward. Return 1 if coord is moved out of item */ +static int +read_move_coord(coord_t *coord, extent_coord_extension_t *ext_coord) +{ + if (ext_coord->pos_in_unit == ext_coord->width - 1) { + /* last position in the unit */ + if (coord->unit_pos == ext_coord->nr_units - 1) { + /* last unit in the item */ + return 1; + } else { + /* move to the next unit */ + coord->unit_pos ++; + ext_coord->ext_offset += sizeof(reiser4_extent); + ON_DEBUG(ext_coord->extent = *ext_by_offset(coord->node, ext_coord->ext_offset)); + ext_coord->width = extent_get_width(ext_by_offset(coord->node, ext_coord->ext_offset)); + ext_coord->pos_in_unit = 0; + } + } else + ext_coord->pos_in_unit ++; + return 0; +} + +static void +call_page_cache_readahead(struct address_space *mapping, struct file *file, unsigned long page_nr, + const uf_coord_t *uf_coord) +{ + reiser4_file_fsdata *fsdata; + uf_coord_t ra_coord; + + fsdata = reiser4_get_file_fsdata(file); + ra_coord = *uf_coord; + ra_coord.extension.extent.expected_page = page_nr; + fsdata->reg.coord = &ra_coord; + + page_cache_readahead(mapping, &file->f_ra, file, page_nr); + fsdata->reg.coord = 0; +} + +#if REISER4_TRACE +static void +print_ext_coord(const char *s, uf_coord_t *uf_coord) +{ + reiser4_key key; + extent_coord_extension_t *ext_coord; + reiser4_extent *ext; + + item_key_by_coord(&uf_coord->base_coord, &key); + ext_coord = &uf_coord->extension.extent; + ext = ext_by_ext_coord(uf_coord); + printk("%s: item key [%llu, %llu], nr_units %d, cur extent [%llu, %llu], unit_pos %d, pos_in_unit %Lu\n", + s, get_key_objectid(&key), get_key_offset(&key), + ext_coord->nr_units, + extent_get_start(ext), extent_get_width(ext), + uf_coord->base_coord.unit_pos, ext_coord->pos_in_unit); +} +#endif + +#if REISER4_DEBUG + +/* return 1 if offset @off is inside of extent unit pointed to by @coord. Set pos_in_unit inside of unit + correspondingly */ +static int +offset_is_in_unit(const coord_t *coord, loff_t off) +{ + reiser4_key unit_key; + __u64 unit_off; + reiser4_extent *ext; + + ext = extent_by_coord(coord); + + unit_key_extent(coord, &unit_key); + unit_off = get_key_offset(&unit_key); + if (off < unit_off) + return 0; + if (off >= (unit_off + (current_blocksize * extent_get_width(ext)))) + return 0; + return 1; +} + +static int +coord_matches_key_extent(const coord_t *coord, const reiser4_key *key) +{ + reiser4_key item_key; + + assert("vs-771", coord_is_existing_unit(coord)); + assert("vs-1258", keylt(key, append_key_extent(coord, &item_key))); + assert("vs-1259", keyge(key, item_key_by_coord(coord, &item_key))); + + return offset_is_in_unit(coord, get_key_offset(key)); +} + +#endif /* REISER4_DEBUG */ + +/* Implements plugin->u.item.s.file.read operation for extent items. */ +reiser4_internal int +read_extent(struct file *file, flow_t *flow, hint_t *hint) +{ + int result; + struct page *page; + unsigned long page_nr; + unsigned long page_off, count; + struct inode *inode; + __u64 file_off; + uf_coord_t *uf_coord; + coord_t *coord; + extent_coord_extension_t *ext_coord; + + uf_coord = &hint->coord; + assert("vs-1318", coord_extension_is_ok(uf_coord)); + + inode = file->f_dentry->d_inode; + coord = &uf_coord->base_coord; + ext_coord = &uf_coord->extension.extent; + + ON_TRACE(TRACE_EXTENTS, "read_extent start: ino %llu, size %llu, offset %llu, count %lld\n", + get_inode_oid(inode), inode->i_size, get_key_offset(&flow->key), flow->length); + IF_TRACE(TRACE_EXTENTS, print_ext_coord("read_extent start", uf_coord)); + + assert("vs-1353", current_blocksize == PAGE_CACHE_SIZE); + assert("vs-572", flow->user == 1); + assert("vs-1351", flow->length > 0); + assert("vs-1119", znode_is_rlocked(coord->node)); + assert("vs-1120", znode_is_loaded(coord->node)); + assert("vs-1256", coord_matches_key_extent(coord, &flow->key)); + + /* offset in a file to start read from */ + file_off = get_key_offset(&flow->key); + /* index of page containing that offset */ + page_nr = (unsigned long)(file_off >> PAGE_CACHE_SHIFT); + /* offset within the page */ + page_off = (unsigned long)(file_off & (PAGE_CACHE_SIZE - 1)); + + count = PAGE_CACHE_SIZE - page_off; + + do { + call_page_cache_readahead(inode->i_mapping, file, page_nr, uf_coord); + + /* this will return page if it exists and is uptodate, otherwise it will allocate page and call + extent_readpage to fill it */ + page = read_cache_page(inode->i_mapping, page_nr, readpage_extent, coord); + if (IS_ERR(page)) + return PTR_ERR(page); + + /* number of bytes which can be read from the page */ + if (count > flow->length) + count = flow->length; + move_flow_forward(flow, count); + if (page_off + count == PAGE_CACHE_SIZE) + if (read_move_coord(coord, ext_coord)) + uf_coord->valid = 0; + set_hint_unlock_node(hint, flow, ZNODE_READ_LOCK); + + wait_on_page_locked(page); + if (!PageUptodate(page)) { + page_detach_jnode(page, inode->i_mapping, page_nr); + page_cache_release(page); + warning("jmacd-97178", "extent_read: page is not up to date"); + return RETERR(-EIO); + } + + /* If users can be writing to this page using arbitrary virtual addresses, take care about potential + aliasing before reading the page on the kernel side. + */ + if (mapping_writably_mapped(inode->i_mapping)) + flush_dcache_page(page); + + assert("nikita-3034", schedulable()); + + + /* AUDIT: We must page-in/prepare user area first to avoid deadlocks */ + result = __copy_to_user(flow->data - count, (char *)kmap(page) + page_off, count); + kunmap(page); + + page_cache_release(page); + if (unlikely(result)) + return RETERR(-EFAULT); + + result = hint_validate(hint, &flow->key, 0/* do not check key */, ZNODE_READ_LOCK); + if (result) + break; + assert("vs-1318", coord_extension_is_ok(uf_coord)); + assert("vs-1263", coord_matches_key_extent(coord, &flow->key)); + page_off = 0; + page_nr ++; + count = PAGE_CACHE_SIZE; + } while (flow->length && uf_coord->valid == 1); + + ON_TRACE(TRACE_EXTENTS, "read_extent done: left %lld\n", flow->length); + IF_TRACE(TRACE_EXTENTS, print_ext_coord("read_extent done", uf_coord)); + + return 0; +} + +static int +move_coord_pages(coord_t *coord, extent_coord_extension_t *ext_coord, unsigned count) +{ + reiser4_extent *ext; + + ext_coord->expected_page += count; + + ext = ext_by_offset(coord->node, ext_coord->ext_offset); + + do { + if (ext_coord->pos_in_unit + count < ext_coord->width) { + ext_coord->pos_in_unit += count; + break; + } + + if (coord->unit_pos == ext_coord->nr_units - 1) { + coord->between = AFTER_UNIT; + return 1; + } + + /* shift to next unit */ + count -= (ext_coord->width - ext_coord->pos_in_unit); + coord->unit_pos ++; + ext_coord->pos_in_unit = 0; + ext_coord->ext_offset += sizeof(reiser4_extent); + ext ++; + ON_DEBUG(ext_coord->extent = *ext); + ext_coord->width = extent_get_width(ext); + } while (1); + + return 0; +} + +static inline void +zero_page(struct page *page) +{ + char *kaddr = kmap_atomic(page, KM_USER0); + + xmemset(kaddr, 0, PAGE_CACHE_SIZE); + flush_dcache_page(page); + kunmap_atomic(kaddr, KM_USER0); + SetPageUptodate(page); + unlock_page(page); +} + +static int +do_readpage_extent(reiser4_extent *ext, reiser4_block_nr pos, struct page *page) +{ + jnode *j; + struct address_space *mapping; + unsigned long index; + oid_t oid; + + mapping = page->mapping; + oid = get_inode_oid(mapping->host); + index = page->index; + + switch (state_of_extent(ext)) { + case HOLE_EXTENT: + /* + * it is possible to have hole page with jnode, if page was + * eflushed previously. + */ + j = jfind(mapping, index); + if (j == NULL) { + zero_page(page); + return 0; + } + LOCK_JNODE(j); + if (!jnode_page(j)) { + jnode_attach_page(j, page); + } else { + BUG_ON(jnode_page(j) != page); + assert("vs-1504", jnode_page(j) == page); + } + + UNLOCK_JNODE(j); + break; + + case ALLOCATED_EXTENT: + j = jnode_of_page(page); + if (IS_ERR(j)) + return PTR_ERR(j); + if (*jnode_get_block(j) == 0) { + reiser4_block_nr blocknr; + + blocknr = extent_get_start(ext) + pos; + jnode_set_block(j, &blocknr); + } else + assert("vs-1403", j->blocknr == extent_get_start(ext) + pos); + break; + + case UNALLOCATED_EXTENT: + j = jfind(mapping, index); + assert("nikita-2688", j); + assert("vs-1426", jnode_page(j) == NULL); + + UNDER_SPIN_VOID(jnode, j, jnode_attach_page(j, page)); + + /* page is locked, it is safe to check JNODE_EFLUSH */ + assert("vs-1668", JF_ISSET(j, JNODE_EFLUSH)); + break; + + default: + warning("vs-957", "extent_readpage: wrong extent\n"); + return RETERR(-EIO); + } + + BUG_ON(j == 0); + page_io(page, j, READ, GFP_NOIO); + jput(j); + return 0; +} + +static int +readahead_readpage_extent(void *vp, struct page *page) +{ + int result; + uf_coord_t *uf_coord; + coord_t *coord; + extent_coord_extension_t *ext_coord; + + uf_coord = vp; + coord = &uf_coord->base_coord; + + if (coord->between != AT_UNIT) { + unlock_page(page); + return RETERR(-EINVAL); + } + + ext_coord = &uf_coord->extension.extent; + if (ext_coord->expected_page != page->index) { + /* read_cache_pages skipped few pages. Try to adjust coord to page */ + assert("vs-1269", page->index > ext_coord->expected_page); + if (move_coord_pages(coord, ext_coord, page->index - ext_coord->expected_page)) { + /* extent pointing to this page is not here */ + unlock_page(page); + return RETERR(-EINVAL); + } + + assert("vs-1274", offset_is_in_unit(coord, + (loff_t)page->index << PAGE_CACHE_SHIFT)); + ext_coord->expected_page = page->index; + } + + assert("vs-1281", page->index == ext_coord->expected_page); + result = do_readpage_extent(ext_by_ext_coord(uf_coord), ext_coord->pos_in_unit, page); + if (!result) + move_coord_pages(coord, ext_coord, 1); + return result; +} + +/* + plugin->u.item.s.file.readpages +*/ +reiser4_internal void +readpages_extent(void *vp, struct address_space *mapping, struct list_head *pages) +{ + if (vp) + read_cache_pages(mapping, pages, readahead_readpage_extent, vp); +} + +/* + plugin->s.file.readpage + reiser4_read->unix_file_read->page_cache_readahead->reiser4_readpage->unix_file_readpage->extent_readpage + or + filemap_nopage->reiser4_readpage->readpage_unix_file->->readpage_extent + + At the beginning: coord->node is read locked, zloaded, page is + locked, coord is set to existing unit inside of extent item (it is not necessary that coord matches to page->index) +*/ +reiser4_internal int +readpage_extent(void *vp, struct page *page) +{ + uf_coord_t *uf_coord = vp; + ON_DEBUG(coord_t *coord = &uf_coord->base_coord); + ON_DEBUG(reiser4_key key); + + assert("vs-1040", PageLocked(page)); + assert("vs-1050", !PageUptodate(page)); + assert("vs-757", !jprivate(page) && !PagePrivate(page)); + assert("vs-1039", page->mapping && page->mapping->host); + + assert("vs-1044", znode_is_loaded(coord->node)); + assert("vs-758", item_is_extent(coord)); + assert("vs-1046", coord_is_existing_unit(coord)); + assert("vs-1045", znode_is_rlocked(coord->node)); + assert("vs-1047", page->mapping->host->i_ino == get_key_objectid(item_key_by_coord(coord, &key))); + assert("vs-1320", coord_extension_is_ok(uf_coord)); + + return do_readpage_extent(ext_by_ext_coord(uf_coord), uf_coord->extension.extent.pos_in_unit, page); +} + +/* + plugin->s.file.capture + + At the beginning: coord.node is write locked, zloaded, page is not locked, coord is set to existing unit inside of + extent item +*/ +reiser4_internal int +capture_extent(reiser4_key *key, uf_coord_t *uf_coord, struct page *page, write_mode_t mode) +{ + jnode *j; + int result; + reiser4_block_nr blocknr; + int created; + int check_quota; + + ON_TRACE(TRACE_EXTENTS, "WP: index %lu, count %d..", page->index, page_count(page)); + + assert("vs-1051", page->mapping && page->mapping->host); + assert("nikita-3139", !inode_get_flag(page->mapping->host, REISER4_NO_SD)); + assert("vs-864", znode_is_wlocked(uf_coord->base_coord.node)); + assert("vs-1398", get_key_objectid(key) == get_inode_oid(page->mapping->host)); + + /* FIXME: assume for now that quota is only checked on write */ + check_quota = 0; + result = make_extent(key, uf_coord, mode, &blocknr, &created, check_quota ? page->mapping->host : NULL); + if (result) { + done_lh(uf_coord->lh); + return result; + } + + lock_page(page); + j = jnode_of_page(page); + if (IS_ERR(j)) { + unlock_page(page); + done_lh(uf_coord->lh); + return PTR_ERR(j); + } + set_page_dirty_internal(page, 0); + unlock_page(page); + + LOCK_JNODE(j); + if (created) { + /* extent corresponding to this jnode was just created */ + assert("vs-1504", *jnode_get_block(j) == 0); + JF_SET(j, JNODE_CREATED); + /* new block is added to file. Update inode->i_blocks and inode->i_bytes. FIXME: + inode_set/get/add/sub_bytes is used to be called by quota macros */ + inode_add_bytes(page->mapping->host, PAGE_CACHE_SIZE); + } + + if (*jnode_get_block(j) == 0) + jnode_set_block(j, &blocknr); + else { + assert("vs-1508", !blocknr_is_fake(&blocknr)); + assert("vs-1507", ergo(blocknr, *jnode_get_block(j) == blocknr)); + } + UNLOCK_JNODE(j); + + done_lh(uf_coord->lh); + + LOCK_JNODE(j); + result = try_capture(j, ZNODE_WRITE_LOCK, 0, 1/* can_coc */); + if (result != 0) + reiser4_panic("nikita-3324", "Cannot capture jnode: %i", result); + jnode_make_dirty_locked(j); + UNLOCK_JNODE(j); + jput(j); + + if (created) + reiser4_update_sd(page->mapping->host); + /* warning about failure of this is issued already */ + + ON_TRACE(TRACE_EXTENTS, "OK\n"); + return 0; +} + +/* + plugin->u.item.s.file.get_block +*/ +reiser4_internal int +get_block_address_extent(const coord_t *coord, sector_t block, struct buffer_head *bh) +{ + reiser4_extent *ext; + + assert("vs-1321", coord_is_existing_unit(coord)); + + ext = extent_by_coord(coord); + + if (state_of_extent(ext) != ALLOCATED_EXTENT) + /* FIXME: bad things may happen if it is unallocated extent */ + bh->b_blocknr = 0; + else { + reiser4_key key; + + unit_key_by_coord(coord, &key); + assert("vs-1645", block >= get_key_offset(&key) >> current_blocksize_bits); + assert("vs-1646", block < (get_key_offset(&key) >> current_blocksize_bits) + extent_get_width(ext)); + bh->b_blocknr = extent_get_start(ext) + (block - (get_key_offset(&key) >> current_blocksize_bits)); + } + return 0; +} + +/* + plugin->u.item.s.file.append_key + key of first byte which is the next to last byte by addressed by this extent +*/ +reiser4_internal reiser4_key * +append_key_extent(const coord_t *coord, reiser4_key *key) +{ + item_key_by_coord(coord, key); + set_key_offset(key, get_key_offset(key) + extent_size(coord, nr_units_extent(coord))); + + assert("vs-610", get_key_offset(key) && (get_key_offset(key) & (current_blocksize - 1)) == 0); + return key; +} + +/* plugin->u.item.s.file.init_coord_extension */ +reiser4_internal void +init_coord_extension_extent(uf_coord_t *uf_coord, loff_t lookuped) +{ + coord_t *coord; + extent_coord_extension_t *ext_coord; + reiser4_key key; + loff_t offset; + + assert("vs-1295", uf_coord->valid == 0); + + coord = &uf_coord->base_coord; + assert("vs-1288", coord_is_iplug_set(coord)); + assert("vs-1327", znode_is_loaded(coord->node)); + + if (coord->between != AFTER_UNIT && coord->between != AT_UNIT) + return; + + ext_coord = &uf_coord->extension.extent; + ext_coord->nr_units = nr_units_extent(coord); + ext_coord->ext_offset = (char *)extent_by_coord(coord) - zdata(coord->node); + ext_coord->width = extent_get_width(extent_by_coord(coord)); + ON_DEBUG(ext_coord->extent = *extent_by_coord(coord)); + uf_coord->valid = 1; + + /* pos_in_unit is the only uninitialized field in extended coord */ + if (coord->between == AFTER_UNIT) { + assert("vs-1330", coord->unit_pos == nr_units_extent(coord) - 1); + + ext_coord->pos_in_unit = ext_coord->width - 1; + } else { + /* AT_UNIT */ + unit_key_by_coord(coord, &key); + offset = get_key_offset(&key); + + assert("vs-1328", offset <= lookuped); + assert("vs-1329", lookuped < offset + ext_coord->width * current_blocksize); + ext_coord->pos_in_unit = ((lookuped - offset) >> current_blocksize_bits); + } +} + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/item/extent_flush_ops.c 2004-09-03 00:57:05.259239736 -0700 @@ -0,0 +1,1096 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#include "item.h" +#include "../../tree.h" +#include "../../jnode.h" +#include "../../super.h" +#include "../../flush.h" +#include "../../carry.h" +#include "../object.h" + +#include + +/* Return either first or last extent (depending on @side) of the item + @coord is set to. Set @pos_in_unit either to first or to last block + of extent. */ +static reiser4_extent * +extent_utmost_ext(const coord_t *coord, sideof side, reiser4_block_nr *pos_in_unit) +{ + reiser4_extent *ext; + + if (side == LEFT_SIDE) { + /* get first extent of item */ + ext = extent_item(coord); + *pos_in_unit = 0; + } else { + /* get last extent of item and last position within it */ + assert("vs-363", side == RIGHT_SIDE); + ext = extent_item(coord) + coord_last_unit_pos(coord); + *pos_in_unit = extent_get_width(ext) - 1; + } + + return ext; +} + +/* item_plugin->f.utmost_child */ +/* Return the child. Coord is set to extent item. Find jnode corresponding + either to first or to last unformatted node pointed by the item */ +reiser4_internal int +utmost_child_extent(const coord_t *coord, sideof side, jnode **childp) +{ + reiser4_extent *ext; + reiser4_block_nr pos_in_unit; + + ext = extent_utmost_ext(coord, side, &pos_in_unit); + + switch (state_of_extent(ext)) { + case HOLE_EXTENT: + *childp = NULL; + return 0; + case ALLOCATED_EXTENT: + case UNALLOCATED_EXTENT: + break; + default: + /* this should never happen */ + assert("vs-1417", 0); + } + + { + reiser4_key key; + reiser4_tree *tree; + unsigned long index; + + if (side == LEFT_SIDE) { + /* get key of first byte addressed by the extent */ + item_key_by_coord(coord, &key); + } else { + /* get key of byte which next after last byte addressed by the extent */ + append_key_extent(coord, &key); + } + + assert("vs-544", (get_key_offset(&key) >> PAGE_CACHE_SHIFT) < ~0ul); + /* index of first or last (depending on @side) page addressed + by the extent */ + index = (unsigned long) (get_key_offset(&key) >> PAGE_CACHE_SHIFT); + if (side == RIGHT_SIDE) + index --; + + tree = coord->node->zjnode.tree; + *childp = jlookup(tree, get_key_objectid(&key), index); + } + + return 0; +} + +/* item_plugin->f.utmost_child_real_block */ +/* Return the child's block, if allocated. */ +reiser4_internal int +utmost_child_real_block_extent(const coord_t *coord, sideof side, reiser4_block_nr *block) +{ + reiser4_extent *ext; + + ext = extent_by_coord(coord); + + switch (state_of_extent(ext)) { + case ALLOCATED_EXTENT: + *block = extent_get_start(ext); + if (side == RIGHT_SIDE) + *block += extent_get_width(ext) - 1; + break; + case HOLE_EXTENT: + case UNALLOCATED_EXTENT: + *block = 0; + break; + default: + /* this should never happen */ + assert("vs-1418", 0); + } + + return 0; +} + +/* item_plugin->f.scan */ +/* Performs leftward scanning starting from an unformatted node and its parent coordinate. + This scan continues, advancing the parent coordinate, until either it encounters a + formatted child or it finishes scanning this node. + + If unallocated, the entire extent must be dirty and in the same atom. (Actually, I'm + not sure this is last property (same atom) is enforced, but it should be the case since + one atom must write the parent and the others must read the parent, thus fusing?). In + any case, the code below asserts this case for unallocated extents. Unallocated + extents are thus optimized because we can skip to the endpoint when scanning. + + It returns control to scan_extent, handles these terminating conditions, e.g., by + loading the next twig. +*/ +reiser4_internal int scan_extent(flush_scan * scan) +{ + coord_t coord; + jnode *neighbor; + unsigned long scan_index, unit_index, unit_width, scan_max, scan_dist; + reiser4_block_nr unit_start; + __u64 oid; + reiser4_key key; + int ret = 0, allocated, incr; + reiser4_tree *tree; + + if (!jnode_check_dirty(scan->node)) { + scan->stop = 1; + return 0; /* Race with truncate, this node is already + * truncated. */ + } + + coord_dup(&coord, &scan->parent_coord); + + assert("jmacd-1404", !scan_finished(scan)); + assert("jmacd-1405", jnode_get_level(scan->node) == LEAF_LEVEL); + assert("jmacd-1406", jnode_is_unformatted(scan->node)); + + /* The scan_index variable corresponds to the current page index of the + unformatted block scan position. */ + scan_index = index_jnode(scan->node); + + assert("jmacd-7889", item_is_extent(&coord)); + + ON_TRACE(TRACE_FLUSH_VERB, "%s scan starts %lu: %s\n", + (scanning_left(scan) ? "left" : "right"), scan_index, jnode_tostring(scan->node)); + +repeat: + /* objectid of file */ + oid = get_key_objectid(item_key_by_coord(&coord, &key)); + + ON_TRACE(TRACE_FLUSH_VERB, "%s scan index %lu: parent %p oid %llu\n", + (scanning_left(scan) ? "left" : "right"), scan_index, coord.node, oid); + + allocated = !extent_is_unallocated(&coord); + /* Get the values of this extent unit: */ + unit_index = extent_unit_index(&coord); + unit_width = extent_unit_width(&coord); + unit_start = extent_unit_start(&coord); + + assert("jmacd-7187", unit_width > 0); + assert("jmacd-7188", scan_index >= unit_index); + assert("jmacd-7189", scan_index <= unit_index + unit_width - 1); + + /* Depending on the scan direction, we set different maximum values for scan_index + (scan_max) and the number of nodes that would be passed if the scan goes the + entire way (scan_dist). Incr is an integer reflecting the incremental + direction of scan_index. */ + if (scanning_left(scan)) { + scan_max = unit_index; + scan_dist = scan_index - unit_index; + incr = -1; + } else { + scan_max = unit_index + unit_width - 1; + scan_dist = scan_max - unit_index; + incr = +1; + } + + tree = coord.node->zjnode.tree; + + /* If the extent is allocated we have to check each of its blocks. If the extent + is unallocated we can skip to the scan_max. */ + if (allocated) { + do { + neighbor = jlookup(tree, oid, scan_index); + if (neighbor == NULL) + goto stop_same_parent; + + ON_TRACE(TRACE_FLUSH_VERB, "alloc scan index %lu: %s\n", + scan_index, jnode_tostring(neighbor)); + + if (scan->node != neighbor && !scan_goto(scan, neighbor)) { + /* @neighbor was jput() by scan_goto(). */ + goto stop_same_parent; + } + + ret = scan_set_current(scan, neighbor, 1, &coord); + if (ret != 0) { + goto exit; + } + + /* reference to @neighbor is stored in @scan, no need + to jput(). */ + scan_index += incr; + + } while (incr + scan_max != scan_index); + + } else { + /* Optimized case for unallocated extents, skip to the end. */ + neighbor = jlookup(tree, oid, scan_max/*index*/); + if (neighbor == NULL) { + /* Race with truncate */ + scan->stop = 1; + ret = 0; + goto exit; + } + + assert ("zam-1043", blocknr_is_fake(jnode_get_block(neighbor))); + + ON_TRACE(TRACE_FLUSH_VERB, "unalloc scan index %lu: %s\n", scan_index, jnode_tostring(neighbor)); + + /* XXX commented assertion out, because it is inherently + * racy */ + /* assert("jmacd-3551", !jnode_check_flushprepped(neighbor) + && same_slum_check(neighbor, scan->node, 0, 0)); */ + + ret = scan_set_current(scan, neighbor, scan_dist, &coord); + if (ret != 0) { + goto exit; + } + } + + if (coord_sideof_unit(&coord, scan->direction) == 0 && item_is_extent(&coord)) { + /* Continue as long as there are more extent units. */ + + scan_index = + extent_unit_index(&coord) + (scanning_left(scan) ? extent_unit_width(&coord) - 1 : 0); + goto repeat; + } + + if (0) { +stop_same_parent: + + /* If we are scanning left and we stop in the middle of an allocated + extent, we know the preceder immediately.. */ + /* middle of extent is (scan_index - unit_index) != 0. */ + if (scanning_left(scan) && (scan_index - unit_index) != 0) { + /* FIXME(B): Someone should step-through and verify that this preceder + calculation is indeed correct. */ + /* @unit_start is starting block (number) of extent + unit. Flush stopped at the @scan_index block from + the beginning of the file, which is (scan_index - + unit_index) block within extent. + */ + if (unit_start) { + /* skip preceder update when we are at hole */ + scan->preceder_blk = unit_start + scan_index - unit_index; + check_preceder(scan->preceder_blk); + } + } + + /* In this case, we leave coord set to the parent of scan->node. */ + scan->stop = 1; + + } else { + /* In this case, we are still scanning, coord is set to the next item which is + either off-the-end of the node or not an extent. */ + assert("jmacd-8912", scan->stop == 0); + assert("jmacd-7812", (coord_is_after_sideof_unit(&coord, scan->direction) + || !item_is_extent(&coord))); + } + + ret = 0; +exit: + return ret; +} + +/* ask block allocator for some blocks */ +static void +extent_allocate_blocks(reiser4_blocknr_hint *preceder, + reiser4_block_nr wanted_count, reiser4_block_nr *first_allocated, reiser4_block_nr *allocated, block_stage_t block_stage) +{ + *allocated = wanted_count; + preceder->max_dist = 0; /* scan whole disk, if needed */ + + /* that number of blocks (wanted_count) is either in UNALLOCATED or in GRABBED */ + preceder->block_stage = block_stage; + + /* FIXME: we do not handle errors here now */ + check_me("vs-420", reiser4_alloc_blocks (preceder, first_allocated, allocated, BA_PERMANENT) == 0); + /* update flush_pos's preceder to last allocated block number */ + preceder->blk = *first_allocated + *allocated - 1; +} + +/* when on flush time unallocated extent is to be replaced with allocated one it may happen that one unallocated extent + will have to be replaced with set of allocated extents. In this case insert_into_item will be called which may have + to add new nodes into tree. Space for that is taken from inviolable reserve (5%). */ +static reiser4_block_nr +reserve_replace(void) +{ + reiser4_block_nr grabbed, needed; + + grabbed = get_current_context()->grabbed_blocks; + needed = estimate_one_insert_into_item(current_tree); + check_me("vpf-340", !reiser4_grab_space_force(needed, BA_RESERVED)); + return grabbed; +} + +static void +free_replace_reserved(reiser4_block_nr grabbed) +{ + reiser4_context *ctx; + + ctx = get_current_context(); + grabbed2free(ctx, get_super_private(ctx->super), + ctx->grabbed_blocks - grabbed); +} + +/* Block offset of first block addressed by unit */ +reiser4_internal __u64 +extent_unit_index(const coord_t *item) +{ + reiser4_key key; + + assert("vs-648", coord_is_existing_unit(item)); + unit_key_by_coord(item, &key); + return get_key_offset(&key) >> current_blocksize_bits; +} + +/* AUDIT shouldn't return value be of reiser4_block_nr type? + Josh's answer: who knows? Is a "number of blocks" the same type as "block offset"? */ +reiser4_internal __u64 +extent_unit_width(const coord_t *item) +{ + assert("vs-649", coord_is_existing_unit(item)); + return width_by_coord(item); +} + +/* Starting block location of this unit */ +reiser4_internal reiser4_block_nr +extent_unit_start(const coord_t *item) +{ + return extent_get_start(extent_by_coord(item)); +} + +/* replace allocated extent with two allocated extents */ +static int +split_allocated_extent(coord_t *coord, reiser4_block_nr pos_in_unit) +{ + int result; + reiser4_extent *ext; + reiser4_extent replace_ext; + reiser4_extent append_ext; + reiser4_key key; + reiser4_item_data item; + reiser4_block_nr grabbed; + + ext = extent_by_coord(coord); + assert("vs-1410", state_of_extent(ext) == ALLOCATED_EXTENT); + assert("vs-1411", extent_get_width(ext) > pos_in_unit); + + set_extent(&replace_ext, extent_get_start(ext), pos_in_unit); + set_extent(&append_ext, extent_get_start(ext) + pos_in_unit, extent_get_width(ext) - pos_in_unit); + + /* insert_into_item will insert new unit after the one @coord is set to. So, update key correspondingly */ + unit_key_by_coord(coord, &key); + set_key_offset(&key, (get_key_offset(&key) + pos_in_unit * current_blocksize)); + + ON_TRACE(TRACE_EXTENT_ALLOC, + "split [%llu %llu] -> [%llu %llu][%llu %llu]\n", + extent_get_start(ext), extent_get_width(ext), + extent_get_start(&replace_ext), extent_get_width(&replace_ext), + extent_get_start(&append_ext), extent_get_width(&append_ext)); + + grabbed = reserve_replace(); + result = replace_extent(coord, znode_lh(coord->node), &key, init_new_extent(&item, &append_ext, 1), + &replace_ext, COPI_DONT_SHIFT_LEFT, 0/* return replaced position */); + free_replace_reserved(grabbed); + return result; +} + +/* clear bit preventing node from being written bypassing extent allocation procedure */ +static inline void +junprotect (jnode * node) +{ + assert("zam-837", !JF_ISSET(node, JNODE_EFLUSH)); + assert("zam-838", JF_ISSET(node, JNODE_EPROTECTED)); + + JF_CLR(node, JNODE_EPROTECTED); +} + +/* this is used to unprotect nodes which were protected before allocating but which will not be allocated either because + space allocator allocates less blocks than were protected and/or if allocation of those nodes failed */ +static void +unprotect_extent_nodes(flush_pos_t *flush_pos, __u64 count, capture_list_head *protected_nodes) +{ + jnode *node, *tmp; + capture_list_head unprotected_nodes; + txn_atom *atom; + + capture_list_init(&unprotected_nodes); + + atom = atom_locked_by_fq(pos_fq(flush_pos)); + assert("vs-1468", atom); + + assert("vs-1469", !capture_list_empty(protected_nodes)); + assert("vs-1474", count > 0); + node = capture_list_back(protected_nodes); + do { + count --; + junprotect(node); + ON_DEBUG( + LOCK_JNODE(node); + count_jnode(atom, node, PROTECT_LIST, DIRTY_LIST, 0); + UNLOCK_JNODE(node); + ); + if (count == 0) { + break; + } + tmp = capture_list_prev(node); + node = tmp; + assert("vs-1470", !capture_list_end(protected_nodes, node)); + } while (1); + + /* move back to dirty list */ + capture_list_split(protected_nodes, &unprotected_nodes, node); + capture_list_splice(ATOM_DIRTY_LIST(atom, LEAF_LEVEL), &unprotected_nodes); + + UNLOCK_ATOM(atom); +} + +extern int getjevent(void); + +/* remove node from atom's list and put to the end of list @jnodes */ +static void +protect_reloc_node(capture_list_head *jnodes, jnode *node) +{ + assert("zam-836", !JF_ISSET(node, JNODE_EPROTECTED)); + assert("vs-1216", jnode_is_unformatted(node)); + assert("vs-1477", spin_atom_is_locked(node->atom)); + assert("nikita-3390", spin_jnode_is_locked(node)); + + JF_SET(node, JNODE_EPROTECTED); + capture_list_remove_clean(node); + capture_list_push_back(jnodes, node); + ON_DEBUG(count_jnode(node->atom, node, DIRTY_LIST, PROTECT_LIST, 0)); +} + +#define JNODES_TO_UNFLUSH (16) + +/* @count nodes of file (objectid @oid) starting from @index are going to be allocated. Protect those nodes from + e-flushing. Nodes which are eflushed already will be un-eflushed. There will be not more than JNODES_TO_UNFLUSH + un-eflushed nodes. If a node is not found or flushprepped - stop protecting */ +/* FIXME: it is likely that not flushprepped jnodes are on dirty capture list in sequential order.. */ +static int +protect_extent_nodes(flush_pos_t *flush_pos, oid_t oid, unsigned long index, reiser4_block_nr count, + reiser4_block_nr *protected, reiser4_extent *ext, + capture_list_head *protected_nodes) +{ + __u64 i; + __u64 j; + int result; + reiser4_tree *tree; + int eflushed; + jnode *buf[JNODES_TO_UNFLUSH]; + txn_atom *atom; + + assert("nikita-3394", capture_list_empty(protected_nodes)); + + tree = current_tree; + + atom = atom_locked_by_fq(pos_fq(flush_pos)); + assert("vs-1468", atom); + + assert("vs-1470", extent_get_width(ext) == count); + eflushed = 0; + *protected = 0; + for (i = 0; i < count; ++i, ++index) { + jnode *node; + + node = jlookup(tree, oid, index); + if (!node) + break; + + if (jnode_check_flushprepped(node)) { + atomic_dec(&node->x_count); + break; + } + + LOCK_JNODE(node); + assert("vs-1476", atomic_read(&node->x_count) > 1); + assert("nikita-3393", !JF_ISSET(node, JNODE_EPROTECTED)); + + if (JF_ISSET(node, JNODE_EFLUSH)) { + if (eflushed == JNODES_TO_UNFLUSH) { + UNLOCK_JNODE(node); + atomic_dec(&node->x_count); + break; + } + buf[eflushed] = node; + eflushed ++; + protect_reloc_node(protected_nodes, node); + UNLOCK_JNODE(node); + } else { + assert("nikita-3384", node->atom == atom); + protect_reloc_node(protected_nodes, node); + assert("nikita-3383", !JF_ISSET(node, JNODE_EFLUSH)); + UNLOCK_JNODE(node); + atomic_dec(&node->x_count); + } + + (*protected) ++; + } + UNLOCK_ATOM(atom); + + /* start io for eflushed nodes */ + for (j = 0; j < eflushed; ++ j) + jstartio(buf[j]); + + result = 0; + for (j = 0; j < eflushed; ++ j) { + if (result == 0) { + result = emergency_unflush(buf[j]); + if (result != 0) { + warning("nikita-3179", + "unflush failed: %i", result); + print_jnode("node", buf[j]); + } + } + jput(buf[j]); + } + if (result != 0) { + /* unprotect all the jnodes we have protected so far */ + unprotect_extent_nodes(flush_pos, i, protected_nodes); + } + return result; +} + +/* replace extent @ext by extent @replace. Try to merge @replace with previous extent of the item (if there is + one). Return 1 if it succeeded, 0 - otherwise */ +static int +try_to_merge_with_left(coord_t *coord, reiser4_extent *ext, reiser4_extent *replace) +{ + assert("vs-1415", extent_by_coord(coord) == ext); + + if (coord->unit_pos == 0 || state_of_extent(ext - 1) != ALLOCATED_EXTENT) + /* @ext either does not exist or is not allocated extent */ + return 0; + if (extent_get_start(ext - 1) + extent_get_width(ext - 1) != extent_get_start(replace)) + return 0; + + /* we can glue, widen previous unit */ + ON_TRACE(TRACE_EXTENT_ALLOC, + "wide previous [%llu %llu] ->", + extent_get_start(ext - 1), extent_get_width(ext - 1)); + + extent_set_width(ext - 1, extent_get_width(ext - 1) + extent_get_width(replace)); + + ON_TRACE(TRACE_EXTENT_ALLOC, " [%llu %llu] -> ", extent_get_start(ext - 1), extent_get_width(ext - 1)); + + if (extent_get_width(ext) != extent_get_width(replace)) { + /* make current extent narrower */ + ON_TRACE(TRACE_EXTENT_ALLOC, "narrow [%llu %llu] -> ", extent_get_start(ext), extent_get_width(ext)); + + if (state_of_extent(ext) == ALLOCATED_EXTENT) + extent_set_start(ext, extent_get_start(ext) + extent_get_width(replace)); + extent_set_width(ext, extent_get_width(ext) - extent_get_width(replace)); + + ON_TRACE(TRACE_EXTENT_ALLOC, "[%llu %llu]\n", extent_get_start(ext), extent_get_width(ext)); + } else { + /* current extent completely glued with its left neighbor, remove it */ + coord_t from, to; + + ON_TRACE(TRACE_EXTENT_ALLOC, "delete [%llu %llu]\n", extent_get_start(ext), extent_get_width(ext)); + + coord_dup(&from, coord); + from.unit_pos = nr_units_extent(coord) - 1; + coord_dup(&to, &from); + + /* currently cut from extent can cut either from the beginning or from the end. Move place which got + freed after unit removal to end of item */ + xmemmove(ext, ext + 1, (from.unit_pos - coord->unit_pos) * sizeof(reiser4_extent)); + /* wipe part of item which is going to be cut, so that node_check will not be confused */ + ON_DEBUG(xmemset(extent_item(coord) + from.unit_pos, 0, sizeof (reiser4_extent))); + cut_node_content(&from, &to, NULL, NULL, NULL); + } + znode_make_dirty(coord->node); + /* move coord back */ + coord->unit_pos --; + return 1; +} + +/* replace extent (unallocated or allocated) pointed by @coord with extent @replace (allocated). If @replace is shorter + than @coord - add padding extent */ +static int +conv_extent(coord_t *coord, reiser4_extent *replace) +{ + int result; + reiser4_extent *ext; + reiser4_extent padd_ext; + reiser4_block_nr start, width, new_width; + reiser4_block_nr grabbed; + reiser4_item_data item; + reiser4_key key; + extent_state state; + + ext = extent_by_coord(coord); + state = state_of_extent(ext); + start = extent_get_start(ext); + width = extent_get_width(ext); + new_width = extent_get_width(replace); + + assert("vs-1458", state == UNALLOCATED_EXTENT || state == ALLOCATED_EXTENT); + assert("vs-1459", width >= new_width); + + if (try_to_merge_with_left(coord, ext, replace)) { + /* merged @replace with left neighbor. Current unit is either removed or narrowed */ + assert("nikita-3563", znode_at_read(coord->node)); + return 0; + } + + if (width == new_width) { + /* replace current extent with @replace */ + ON_TRACE(TRACE_EXTENT_ALLOC, "replace: [%llu %llu]->[%llu %llu]\n", + start, width, + extent_get_start(replace), extent_get_width(replace)); + + *ext = *replace; + znode_make_dirty(coord->node); + assert("nikita-3563", znode_at_read(coord->node)); + return 0; + } + + /* replace @ext with @replace and padding extent */ + set_extent(&padd_ext, state == ALLOCATED_EXTENT ? (start + new_width) : UNALLOCATED_EXTENT_START, + width - new_width); + + /* insert_into_item will insert new units after the one @coord is set to. So, update key correspondingly */ + unit_key_by_coord(coord, &key); + set_key_offset(&key, (get_key_offset(&key) + new_width * current_blocksize)); + + ON_TRACE(TRACE_EXTENT_ALLOC, + "replace: [%llu %llu]->[%llu %llu][%llu %llu]\n", + start, width, + extent_get_start(replace), extent_get_width(replace), + extent_get_start(&padd_ext), extent_get_width(&padd_ext)); + + grabbed = reserve_replace(); + result = replace_extent(coord, znode_lh(coord->node), &key, init_new_extent(&item, &padd_ext, 1), + replace, COPI_DONT_SHIFT_LEFT, 0/* return replaced position */); + + assert("nikita-3563", znode_at_read(coord->node)); + free_replace_reserved(grabbed); + return result; +} + +/* for every jnode from @protected_nodes list assign block number and mark it RELOC and FLUSH_QUEUED. Attach whole + @protected_nodes list to flush queue's prepped list */ +static void +assign_real_blocknrs(flush_pos_t *flush_pos, reiser4_block_nr first, reiser4_block_nr count, + extent_state state, capture_list_head *protected_nodes) +{ + jnode *node; + txn_atom *atom; + flush_queue_t *fq; + int i; + + fq = pos_fq(flush_pos); + atom = atom_locked_by_fq(fq); + assert("vs-1468", atom); + + i = 0; + for_all_type_safe_list(capture, protected_nodes, node) { + LOCK_JNODE(node); + assert("vs-1132", ergo(state == UNALLOCATED_EXTENT, blocknr_is_fake(jnode_get_block(node)))); + assert("vs-1475", node->atom == atom); + assert("vs-1476", atomic_read(&node->x_count) > 0); + JF_CLR(node, JNODE_FLUSH_RESERVED); + jnode_set_block(node, &first); + unformatted_make_reloc(node, fq); + /*XXXX*/ON_DEBUG(count_jnode(node->atom, node, PROTECT_LIST, FQ_LIST, 0)); + junprotect(node); + assert("", NODE_LIST(node) == FQ_LIST); + UNLOCK_JNODE(node); + first ++; + i ++; + } + + capture_list_splice(ATOM_FQ_LIST(fq), protected_nodes); + /*XXX*/ + assert("vs-1687", count == i); + if (state == UNALLOCATED_EXTENT) + dec_unalloc_unfm_ptrs(count); + UNLOCK_ATOM(atom); +} + +static void +make_node_ovrwr(capture_list_head *jnodes, jnode *node) +{ + LOCK_JNODE(node); + + assert ("zam-917", !JF_ISSET(node, JNODE_RELOC)); + assert ("zam-918", !JF_ISSET(node, JNODE_OVRWR)); + + JF_SET(node, JNODE_OVRWR); + capture_list_remove_clean(node); + capture_list_push_back(jnodes, node); + ON_DEBUG(count_jnode(node->atom, node, DIRTY_LIST, OVRWR_LIST, 0)); + + UNLOCK_JNODE(node); +} + +/* put nodes of one extent (file objectid @oid, extent width @width) to overwrite set. Starting from the one with index + @index. If end of slum is detected (node is not found or flushprepped) - stop iterating and set flush position's + state to POS_INVALID */ +static void +mark_jnodes_overwrite(flush_pos_t *flush_pos, oid_t oid, unsigned long index, reiser4_block_nr width) +{ + unsigned long i; + reiser4_tree *tree; + jnode *node; + txn_atom *atom; + capture_list_head jnodes; + + capture_list_init(&jnodes); + + tree = current_tree; + + atom = atom_locked_by_fq(pos_fq(flush_pos)); + assert("vs-1478", atom); + + for (i = flush_pos->pos_in_unit; i < width; i ++, index ++) { + node = jlookup(tree, oid, index); + if (!node) { + flush_pos->state = POS_INVALID; + + ON_TRACE(TRACE_EXTENT_ALLOC, "node not found: (oid %llu, index %lu)\n", oid, index); + + break; + } + if (jnode_check_flushprepped(node)) { + flush_pos->state = POS_INVALID; + atomic_dec(&node->x_count); + + ON_TRACE(TRACE_EXTENT_ALLOC, "flushprepped: (oid %llu, index %lu)\n", oid, index); + + break; + } + make_node_ovrwr(&jnodes, node); + atomic_dec(&node->x_count); + } + + capture_list_splice(ATOM_OVRWR_LIST(atom), &jnodes); + UNLOCK_ATOM(atom); +} + +/* this is called by handle_pos_on_twig to proceed extent unit flush_pos->coord is set to. It is to prepare for flushing + sequence of not flushprepped nodes (slum). It supposes that slum starts at flush_pos->pos_in_unit position within the + extent. Slum gets to relocate set if flush_pos->leaf_relocate is set to 1 and to overwrite set otherwise */ +reiser4_internal int +alloc_extent(flush_pos_t *flush_pos) +{ + coord_t *coord; + reiser4_extent *ext; + reiser4_extent replace_ext; + oid_t oid; + reiser4_block_nr protected; + reiser4_block_nr start; + __u64 index; + __u64 width; + extent_state state; + int result; + reiser4_block_nr first_allocated; + __u64 allocated; + reiser4_key key; + block_stage_t block_stage; + + assert("vs-1468", flush_pos->state == POS_ON_EPOINT); + assert("vs-1469", coord_is_existing_unit(&flush_pos->coord) && item_is_extent(&flush_pos->coord)); + + coord = &flush_pos->coord; + + check_pos(flush_pos); + + ext = extent_by_coord(coord); + state = state_of_extent(ext); + if (state == HOLE_EXTENT) { + flush_pos->state = POS_INVALID; + return 0; + } + + item_key_by_coord(coord, &key); + oid = get_key_objectid(&key); + index = extent_unit_index(coord) + flush_pos->pos_in_unit; + start = extent_get_start(ext); + width = extent_get_width(ext); + + assert("vs-1457", width > flush_pos->pos_in_unit); + + if (flush_pos->leaf_relocate || state == UNALLOCATED_EXTENT) { + protected_jnodes jnodes; + + /* relocate */ + if (flush_pos->pos_in_unit) { + /* split extent unit into two */ + result = split_allocated_extent(coord, flush_pos->pos_in_unit); + check_pos(flush_pos); + flush_pos->pos_in_unit = 0; + return result; + } + ON_TRACE(TRACE_EXTENT_ALLOC, + "ALLOC: relocate: (oid %llu, index %llu) [%llu %llu] - ", + oid, index, start, width); + + /* Prevent nodes from e-flushing before allocating disk space for them. Nodes which were eflushed will be + read from their temporary locations (but not more than certain limit: JNODES_TO_UNFLUSH) and that + disk space will be freed. */ + + protected_jnodes_init(&jnodes); + + result = protect_extent_nodes(flush_pos, oid, index, width, &protected, ext, &jnodes.nodes); + check_pos(flush_pos); + if (result) { + warning("vs-1469", "Failed to protect extent. Should not happen\n"); + protected_jnodes_done(&jnodes); + return result; + } + if (protected == 0) { + ON_TRACE(TRACE_EXTENT_ALLOC, "nothing todo\n"); + flush_pos->state = POS_INVALID; + flush_pos->pos_in_unit = 0; + protected_jnodes_done(&jnodes); + return 0; + } + + if (state == ALLOCATED_EXTENT) + /* all protected nodes are not flushprepped, therefore + * they are counted as flush_reserved */ + block_stage = BLOCK_FLUSH_RESERVED; + else + block_stage = BLOCK_UNALLOCATED; + + /* allocate new block numbers for protected nodes */ + extent_allocate_blocks(pos_hint(flush_pos), protected, &first_allocated, &allocated, block_stage); + check_pos(flush_pos); + + ON_TRACE(TRACE_EXTENT_ALLOC, "allocated: (first %llu, cound %llu) - ", first_allocated, allocated); + + if (allocated != protected) + /* unprotect nodes which will not be + * allocated/relocated on this iteration */ + unprotect_extent_nodes(flush_pos, protected - allocated, + &jnodes.nodes); + check_pos(flush_pos); + if (state == ALLOCATED_EXTENT) { + /* on relocating - free nodes which are going to be + * relocated */ + reiser4_dealloc_blocks(&start, &allocated, BLOCK_ALLOCATED, BA_DEFER); + } + + check_pos(flush_pos); + /* assign new block numbers to protected nodes */ + assign_real_blocknrs(flush_pos, first_allocated, allocated, state, &jnodes.nodes); + + check_pos(flush_pos); + protected_jnodes_done(&jnodes); + + /* send to log information about which blocks were allocated for what */ + write_current_logf(ALLOC_EXTENT_LOG, + "alloc: oid: %llu, index: %llu, state %d, width: %llu. " + "prot: %llu. got [%llu %llu]", + oid, index, state, width, protected, first_allocated, allocated); + + /* prepare extent which will replace current one */ + set_extent(&replace_ext, first_allocated, allocated); + + /* adjust extent item */ + result = conv_extent(coord, &replace_ext); + check_pos(flush_pos); + if (result != 0 && result != -ENOMEM) { + warning("vs-1461", "Failed to allocate extent. Should not happen\n"); + return result; + } + } else { + /* overwrite */ + ON_TRACE(TRACE_EXTENT_ALLOC, + "ALLOC: overwrite: (oid %llu, index %llu) [%llu %llu]\n", + oid, index, start, width); + mark_jnodes_overwrite(flush_pos, oid, index, width); + } + flush_pos->pos_in_unit = 0; + check_pos(flush_pos); + return 0; +} + +/* if @key is glueable to the item @coord is set to */ +static int +must_insert(const coord_t *coord, const reiser4_key *key) +{ + reiser4_key last; + + if (item_id_by_coord(coord) == EXTENT_POINTER_ID && keyeq(append_key_extent(coord, &last), key)) + return 0; + return 1; +} + + /* copy extent @copy to the end of @node. It may have to either insert new item after the last one, or append last item, + or modify last unit of last item to have greater width */ +static int +put_unit_to_end(znode *node, const reiser4_key *key, reiser4_extent *copy_ext) +{ + int result; + coord_t coord; + cop_insert_flag flags; + reiser4_extent *last_ext; + reiser4_item_data data; + + /* set coord after last unit in an item */ + coord_init_last_unit(&coord, node); + coord.between = AFTER_UNIT; + + flags = COPI_DONT_SHIFT_LEFT | COPI_DONT_SHIFT_RIGHT | COPI_DONT_ALLOCATE; + if (must_insert(&coord, key)) { + result = insert_by_coord(&coord, init_new_extent(&data, copy_ext, 1), key, 0 /*lh */ , flags); + + } else { + /* try to glue with last unit */ + last_ext = extent_by_coord(&coord); + if (state_of_extent(last_ext) && + extent_get_start(last_ext) + extent_get_width(last_ext) == extent_get_start(copy_ext)) { + /* widen last unit of node */ + extent_set_width(last_ext, extent_get_width(last_ext) + extent_get_width(copy_ext)); + znode_make_dirty(node); + return 0; + } + + /* FIXME: put an assertion here that we can not merge last unit in @node and new unit */ + result = insert_into_item(&coord, 0 /*lh */ , key, init_new_extent(&data, copy_ext, 1), flags); + } + + assert("vs-438", result == 0 || result == -E_NODE_FULL); + return result; +} + +/* @coord is set to extent unit */ +reiser4_internal squeeze_result +squalloc_extent(znode *left, const coord_t *coord, flush_pos_t *flush_pos, reiser4_key *stop_key) +{ + reiser4_extent *ext; + __u64 index; + __u64 width; + reiser4_block_nr start; + extent_state state; + oid_t oid; + reiser4_block_nr first_allocated; + __u64 allocated; + __u64 protected; + reiser4_extent copy_extent; + reiser4_key key; + int result; + block_stage_t block_stage; + + assert("vs-1457", flush_pos->pos_in_unit == 0); + assert("vs-1467", coord_is_leftmost_unit(coord)); + assert("vs-1467", item_is_extent(coord)); + + ext = extent_by_coord(coord); + index = extent_unit_index(coord); + start = extent_get_start(ext); + width = extent_get_width(ext); + state = state_of_extent(ext); + unit_key_by_coord(coord, &key); + oid = get_key_objectid(&key); + + if (flush_pos->leaf_relocate || state == UNALLOCATED_EXTENT) { + protected_jnodes jnodes; + + ON_TRACE(TRACE_EXTENT_ALLOC, "SQUALLOC: relocate: (oid %llu, index %llu) [%llu %llu] - ", + oid, index, start, width); + + /* relocate */ + protected_jnodes_init(&jnodes); + result = protect_extent_nodes(flush_pos, oid, index, width, &protected, ext, &jnodes.nodes); + if (result) { + warning("vs-1469", "Failed to protect extent. Should not happen\n"); + protected_jnodes_done(&jnodes); + return result; + } + if (protected == 0) { + flush_pos->state = POS_INVALID; + protected_jnodes_done(&jnodes); + return 0; + } + + if (state == ALLOCATED_EXTENT) + /* all protected nodes are not flushprepped, therefore + * they are counted as flush_reserved */ + block_stage = BLOCK_FLUSH_RESERVED; + else + block_stage = BLOCK_UNALLOCATED; + + /* allocate new block numbers for protected nodes */ + extent_allocate_blocks(pos_hint(flush_pos), protected, &first_allocated, &allocated, block_stage); + ON_TRACE(TRACE_EXTENT_ALLOC, "allocated: (first %llu, cound %llu) - ", first_allocated, allocated); + if (allocated != protected) + unprotect_extent_nodes(flush_pos, protected - allocated, + &jnodes.nodes); + + /* prepare extent which will be copied to left */ + set_extent(©_extent, first_allocated, allocated); + + result = put_unit_to_end(left, &key, ©_extent); + if (result == -E_NODE_FULL) { + int target_block_stage; + + /* free blocks which were just allocated */ + ON_TRACE(TRACE_EXTENT_ALLOC, + "left is full, free (first %llu, count %llu)\n", + first_allocated, allocated); + target_block_stage = (state == ALLOCATED_EXTENT) ? BLOCK_FLUSH_RESERVED : BLOCK_UNALLOCATED; + reiser4_dealloc_blocks(&first_allocated, &allocated, target_block_stage, BA_PERMANENT); + unprotect_extent_nodes(flush_pos, allocated, &jnodes.nodes); + + /* rewind the preceder. */ + flush_pos->preceder.blk = first_allocated; + check_preceder(flush_pos->preceder.blk); + + protected_jnodes_done(&jnodes); + return SQUEEZE_TARGET_FULL; + } + + if (state == ALLOCATED_EXTENT) { + /* free nodes which were relocated */ + reiser4_dealloc_blocks(&start, &allocated, BLOCK_ALLOCATED, BA_DEFER); + } + + /* assign new block numbers to protected nodes */ + assign_real_blocknrs(flush_pos, first_allocated, allocated, state, &jnodes.nodes); + protected_jnodes_done(&jnodes); + + set_key_offset(&key, get_key_offset(&key) + (allocated << current_blocksize_bits)); + ON_TRACE(TRACE_EXTENT_ALLOC, + "copied to left: [%llu %llu]\n", first_allocated, allocated); + + /* send to log information about which blocks were allocated for what */ + write_current_logf(ALLOC_EXTENT_LOG, + "sqalloc: oid: %llu, index: %llu, state %d, width: %llu. " + "prot: %llu. got [%llu %llu]", + oid, index, state, width, protected, first_allocated, allocated); + } else { + /* overwrite */ + ON_TRACE(TRACE_EXTENT_ALLOC, + "SQUALLOC: overwrite: (oid %llu, index %llu) [%llu %llu] - ", oid, index, start, width); + + /* overwrite: try to copy unit as it is to left neighbor and make all first not flushprepped nodes + overwrite nodes */ + set_extent(©_extent, start, width); + result = put_unit_to_end(left, &key, ©_extent); + if (result == -E_NODE_FULL) { + ON_TRACE(TRACE_EXTENT_ALLOC, "left is full\n"); + return SQUEEZE_TARGET_FULL; + } + mark_jnodes_overwrite(flush_pos, oid, index, width); + set_key_offset(&key, get_key_offset(&key) + (width << current_blocksize_bits)); + ON_TRACE(TRACE_EXTENT_ALLOC, "copied to left\n"); + } + *stop_key = key; + return SQUEEZE_CONTINUE; +} + +reiser4_internal int +key_by_offset_extent(struct inode *inode, loff_t off, reiser4_key *key) +{ + return key_by_inode_and_offset_common(inode, off, key); +} + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/item/extent.h 2004-09-03 00:57:05.260239584 -0700 @@ -0,0 +1,176 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#ifndef __REISER4_EXTENT_H__ +#define __REISER4_EXTENT_H__ + +/* on disk extent */ +typedef struct { + reiser4_dblock_nr start; + reiser4_dblock_nr width; +} reiser4_extent; + +typedef struct extent_stat { + int unallocated_units; + int unallocated_blocks; + int allocated_units; + int allocated_blocks; + int hole_units; + int hole_blocks; +} extent_stat; + +/* extents in an extent item can be either holes, or unallocated or allocated + extents */ +typedef enum { + HOLE_EXTENT, + UNALLOCATED_EXTENT, + ALLOCATED_EXTENT +} extent_state; + +#define HOLE_EXTENT_START 0 +#define UNALLOCATED_EXTENT_START 1 +#define UNALLOCATED_EXTENT_START2 2 + +typedef struct { + reiser4_block_nr pos_in_unit; + reiser4_block_nr width; /* width of current unit */ + pos_in_node_t nr_units; /* number of units */ + int ext_offset; /* offset from the beginning of zdata() */ + unsigned long expected_page; +#if REISER4_DEBUG + reiser4_extent extent; +#endif +} extent_coord_extension_t; + +/* macros to set/get fields of on-disk extent */ +static inline reiser4_block_nr +extent_get_start(const reiser4_extent * ext) +{ + return dblock_to_cpu(&ext->start); +} + +static inline reiser4_block_nr +extent_get_width(const reiser4_extent * ext) +{ + return dblock_to_cpu(&ext->width); +} + +extern __u64 reiser4_current_block_count(void); + +static inline void +extent_set_start(reiser4_extent * ext, reiser4_block_nr start) +{ + cassert(sizeof (ext->start) == 8); + assert("nikita-2510", ergo(start > 1, start < reiser4_current_block_count())); + cpu_to_dblock(start, &ext->start); +} + +static inline void +extent_set_width(reiser4_extent *ext, reiser4_block_nr width) +{ + cassert(sizeof (ext->width) == 8); + cpu_to_dblock(width, &ext->width); + assert("nikita-2511", + ergo(extent_get_start(ext) > 1, + extent_get_start(ext) + width <= reiser4_current_block_count())); +} + +#define extent_item(coord) \ +({ \ + assert("nikita-3143", item_is_extent(coord)); \ + ((reiser4_extent *)item_body_by_coord (coord)); \ +}) + +#define extent_by_coord(coord) \ +({ \ + assert("nikita-3144", item_is_extent(coord)); \ + (extent_item (coord) + (coord)->unit_pos); \ +}) + +#define width_by_coord(coord) \ +({ \ + assert("nikita-3145", item_is_extent(coord)); \ + extent_get_width (extent_by_coord(coord)); \ +}) + +struct carry_cut_data; +struct carry_kill_data; + +/* plugin->u.item.b.* */ +reiser4_key *max_key_inside_extent(const coord_t *, reiser4_key *); +int can_contain_key_extent(const coord_t * coord, const reiser4_key * key, const reiser4_item_data *); +int mergeable_extent(const coord_t * p1, const coord_t * p2); +pos_in_node_t nr_units_extent(const coord_t *); +lookup_result lookup_extent(const reiser4_key *, lookup_bias, coord_t *); +void init_coord_extent(coord_t *); +int init_extent(coord_t *, reiser4_item_data *); +int paste_extent(coord_t *, reiser4_item_data *, carry_plugin_info *); +int can_shift_extent(unsigned free_space, + coord_t * source, znode * target, shift_direction, unsigned *size, unsigned want); +void copy_units_extent(coord_t * target, + coord_t * source, + unsigned from, unsigned count, shift_direction where_is_free_space, unsigned free_space); +int kill_hook_extent(const coord_t *, pos_in_node_t from, pos_in_node_t count, struct carry_kill_data *); +int create_hook_extent(const coord_t * coord, void *arg); +int cut_units_extent(coord_t *coord, pos_in_node_t from, pos_in_node_t to, + struct carry_cut_data *, reiser4_key *smallest_removed, reiser4_key *new_first); +int kill_units_extent(coord_t *coord, pos_in_node_t from, pos_in_node_t to, + struct carry_kill_data *, reiser4_key *smallest_removed, reiser4_key *new_first); +reiser4_key *unit_key_extent(const coord_t *, reiser4_key *); +reiser4_key *max_unit_key_extent(const coord_t *, reiser4_key *); +void print_extent(const char *, coord_t *); +void show_extent(struct seq_file *m, coord_t *coord); +int utmost_child_extent(const coord_t * coord, sideof side, jnode ** child); +int utmost_child_real_block_extent(const coord_t * coord, sideof side, reiser4_block_nr * block); +void item_stat_extent(const coord_t * coord, void *vp); +int check_extent(const coord_t * coord, const char **error); + +/* plugin->u.item.s.file.* */ +int write_extent(struct inode *, flow_t *, hint_t *, int grabbed, write_mode_t); +int read_extent(struct file *, flow_t *, hint_t *); +int readpage_extent(void *, struct page *); +void readpages_extent(void *, struct address_space *, struct list_head *pages); +int capture_extent(reiser4_key *, uf_coord_t *, struct page *, write_mode_t); +reiser4_key *append_key_extent(const coord_t *, reiser4_key *); +void init_coord_extension_extent(uf_coord_t *, loff_t offset); +int get_block_address_extent(const coord_t *, sector_t block, struct buffer_head *); + + +/* these are used in flush.c + FIXME-VS: should they be somewhere in item_plugin? */ +int allocate_extent_item_in_place(coord_t *, lock_handle *, flush_pos_t * pos); +int allocate_and_copy_extent(znode * left, coord_t * right, flush_pos_t * pos, reiser4_key * stop_key); + +int extent_is_unallocated(const coord_t * item); /* True if this extent is unallocated (i.e., not a hole, not allocated). */ +int extent_is_allocated(const coord_t *); +__u64 extent_unit_index(const coord_t * item); /* Block offset of this unit. */ +__u64 extent_unit_width(const coord_t * item); /* Number of blocks in this unit. */ +reiser4_block_nr extent_unit_start(const coord_t * item); /* Starting block location of this unit. */ + +/* plugin->u.item.f. */ +int scan_extent (flush_scan * scan); +extern int key_by_offset_extent(struct inode *, loff_t, reiser4_key *); + +reiser4_item_data *init_new_extent(reiser4_item_data *data, void *ext_unit, int nr_extents); +reiser4_block_nr extent_size(const coord_t *coord, pos_in_node_t nr); +extent_state state_of_extent(reiser4_extent *ext); +void set_extent(reiser4_extent *ext, reiser4_block_nr start, reiser4_block_nr width); +int replace_extent(coord_t *un_extent, lock_handle *lh, + reiser4_key *key, reiser4_item_data *data, const reiser4_extent *new_ext, unsigned flags, int); +lock_handle *znode_lh(znode *); + +/* the reiser4 repacker support */ +struct repacker_cursor; +extern int process_extent_backward_for_repacking (tap_t *, struct repacker_cursor *); +extern int mark_extent_for_repacking (tap_t *, int); + +/* __REISER4_EXTENT_H__ */ +#endif +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/item/extent_item_ops.c 2004-09-03 00:57:05.264238976 -0700 @@ -0,0 +1,875 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#include "item.h" +#include "../../inode.h" +#include "../../tree_walk.h" /* check_sibling_list() */ +#include "../../page_cache.h" +#include "../../carry.h" + +#include + +/* item_plugin->b.max_key_inside */ +reiser4_internal reiser4_key * +max_key_inside_extent(const coord_t *coord, reiser4_key *key) +{ + item_key_by_coord(coord, key); + set_key_offset(key, get_key_offset(max_key())); + return key; +} + +/* item_plugin->b.can_contain_key + this checks whether @key of @data is matching to position set by @coord */ +reiser4_internal int +can_contain_key_extent(const coord_t *coord, const reiser4_key *key, const reiser4_item_data *data) +{ + reiser4_key item_key; + + if (item_plugin_by_coord(coord) != data->iplug) + return 0; + + item_key_by_coord(coord, &item_key); + if (get_key_locality(key) != get_key_locality(&item_key) || + get_key_objectid(key) != get_key_objectid(&item_key) || + get_key_ordering(key) != get_key_ordering(&item_key)) return 0; + + return 1; +} + +/* item_plugin->b.mergeable + first item is of extent type */ +/* Audited by: green(2002.06.13) */ +reiser4_internal int +mergeable_extent(const coord_t *p1, const coord_t *p2) +{ + reiser4_key key1, key2; + + assert("vs-299", item_id_by_coord(p1) == EXTENT_POINTER_ID); + /* FIXME-VS: Which is it? Assert or return 0 */ + if (item_id_by_coord(p2) != EXTENT_POINTER_ID) { + return 0; + } + + item_key_by_coord(p1, &key1); + item_key_by_coord(p2, &key2); + if (get_key_locality(&key1) != get_key_locality(&key2) || + get_key_objectid(&key1) != get_key_objectid(&key2) || + get_key_ordering(&key1) != get_key_ordering(&key2) || + get_key_type(&key1) != get_key_type(&key2)) + return 0; + if (get_key_offset(&key1) + extent_size(p1, nr_units_extent(p1)) != get_key_offset(&key2)) + return 0; + return 1; +} + +/* item_plugin->b.show */ +reiser4_internal void +show_extent(struct seq_file *m, coord_t *coord) +{ + reiser4_extent *ext; + ext = extent_by_coord(coord); + seq_printf(m, "%Lu %Lu", extent_get_start(ext), extent_get_width(ext)); +} + + +#if REISER4_DEBUG_OUTPUT + +/* Audited by: green(2002.06.13) */ +static const char * +state2label(extent_state state) +{ + const char *label; + + label = 0; + switch (state) { + case HOLE_EXTENT: + label = "hole"; + break; + + case UNALLOCATED_EXTENT: + label = "unalloc"; + break; + + case ALLOCATED_EXTENT: + label = "alloc"; + break; + } + assert("vs-376", label); + return label; +} + +/* item_plugin->b.print */ +reiser4_internal void +print_extent(const char *prefix, coord_t *coord) +{ + reiser4_extent *ext; + unsigned i, nr; + + if (prefix) + printk("%s:", prefix); + + nr = nr_units_extent(coord); + ext = (reiser4_extent *) item_body_by_coord(coord); + + printk("%u: ", nr); + for (i = 0; i < nr; i++, ext++) { + printk("[%Lu (%Lu) %s]", extent_get_start(ext), extent_get_width(ext), state2label(state_of_extent(ext))); + } + printk("\n"); +} + +/* item_plugin->b.item_stat */ +reiser4_internal void +item_stat_extent(const coord_t *coord, void *vp) +{ + reiser4_extent *ext; + struct extent_stat *ex_stat; + unsigned i, nr_units; + + ex_stat = (struct extent_stat *) vp; + + ext = extent_item(coord); + nr_units = nr_units_extent(coord); + + for (i = 0; i < nr_units; i++) { + switch (state_of_extent(ext + i)) { + case ALLOCATED_EXTENT: + ex_stat->allocated_units++; + ex_stat->allocated_blocks += extent_get_width(ext + i); + break; + case UNALLOCATED_EXTENT: + ex_stat->unallocated_units++; + ex_stat->unallocated_blocks += extent_get_width(ext + i); + break; + case HOLE_EXTENT: + ex_stat->hole_units++; + ex_stat->hole_blocks += extent_get_width(ext + i); + break; + default: + assert("vs-1419", 0); + } + } +} + +#endif /* REISER4_DEBUG_OUTPUT */ + +/* item_plugin->b.nr_units */ +reiser4_internal pos_in_node_t +nr_units_extent(const coord_t *coord) +{ + /* length of extent item has to be multiple of extent size */ + assert("vs-1424", (item_length_by_coord(coord) % sizeof (reiser4_extent)) == 0); + return item_length_by_coord(coord) / sizeof (reiser4_extent); +} + +/* item_plugin->b.lookup */ +reiser4_internal lookup_result +lookup_extent(const reiser4_key *key, lookup_bias bias UNUSED_ARG, coord_t *coord) +{ /* znode and item_pos are + set to an extent item to + look through */ + reiser4_key item_key; + reiser4_block_nr lookuped, offset; + unsigned i, nr_units; + reiser4_extent *ext; + unsigned blocksize; + unsigned char blocksize_bits; + + item_key_by_coord(coord, &item_key); + offset = get_key_offset(&item_key); + + /* key we are looking for must be greater than key of item @coord */ + assert("vs-414", keygt(key, &item_key)); + + assert("umka-99945", + !keygt(key, max_key_inside_extent(coord, &item_key))); + + ext = extent_item(coord); + assert("vs-1350", (char *)ext == (zdata(coord->node) + coord->offset)); + + blocksize = current_blocksize; + blocksize_bits = current_blocksize_bits; + + /* offset we are looking for */ + lookuped = get_key_offset(key); + + nr_units = nr_units_extent(coord); + /* go through all extents until the one which address given offset */ + for (i = 0; i < nr_units; i++, ext++) { + offset += (extent_get_width(ext) << blocksize_bits); + if (offset > lookuped) { + /* desired byte is somewhere in this extent */ + coord->unit_pos = i; + coord->between = AT_UNIT; + return CBK_COORD_FOUND; + } + } + + /* set coord after last unit */ + coord->unit_pos = nr_units - 1; + coord->between = AFTER_UNIT; + return CBK_COORD_FOUND; +} + +/* item_plugin->b.paste + item @coord is set to has been appended with @data->length of free + space. data->data contains data to be pasted into the item in position + @coord->in_item.unit_pos. It must fit into that free space. + @coord must be set between units. +*/ +reiser4_internal int +paste_extent(coord_t *coord, reiser4_item_data *data, carry_plugin_info *info UNUSED_ARG) +{ + unsigned old_nr_units; + reiser4_extent *ext; + int item_length; + + ext = extent_item(coord); + item_length = item_length_by_coord(coord); + old_nr_units = (item_length - data->length) / sizeof (reiser4_extent); + + /* this is also used to copy extent into newly created item, so + old_nr_units could be 0 */ + assert("vs-260", item_length >= data->length); + + /* make sure that coord is set properly */ + assert("vs-35", ((!coord_is_existing_unit(coord)) || (!old_nr_units && !coord->unit_pos))); + + /* first unit to be moved */ + switch (coord->between) { + case AFTER_UNIT: + coord->unit_pos++; + case BEFORE_UNIT: + coord->between = AT_UNIT; + break; + case AT_UNIT: + assert("vs-331", !old_nr_units && !coord->unit_pos); + break; + default: + impossible("vs-330", "coord is set improperly"); + } + + /* prepare space for new units */ + xmemmove(ext + coord->unit_pos + data->length / sizeof (reiser4_extent), + ext + coord->unit_pos, (old_nr_units - coord->unit_pos) * sizeof (reiser4_extent)); + + /* copy new data from kernel space */ + assert("vs-556", data->user == 0); + xmemcpy(ext + coord->unit_pos, data->data, (unsigned) data->length); + + /* after paste @coord is set to first of pasted units */ + assert("vs-332", coord_is_existing_unit(coord)); + assert("vs-333", !memcmp(data->data, extent_by_coord(coord), (unsigned) data->length)); + return 0; +} + +/* item_plugin->b.can_shift */ +reiser4_internal int +can_shift_extent(unsigned free_space, coord_t *source, + znode *target UNUSED_ARG, shift_direction pend UNUSED_ARG, unsigned *size, unsigned want) +{ + *size = item_length_by_coord(source); + if (*size > free_space) + /* never split a unit of extent item */ + *size = free_space - free_space % sizeof (reiser4_extent); + + /* we can shift *size bytes, calculate how many do we want to shift */ + if (*size > want * sizeof (reiser4_extent)) + *size = want * sizeof (reiser4_extent); + + if (*size % sizeof (reiser4_extent) != 0) + impossible("vs-119", "Wrong extent size: %i %i", *size, sizeof (reiser4_extent)); + return *size / sizeof (reiser4_extent); + +} + +/* item_plugin->b.copy_units */ +reiser4_internal void +copy_units_extent(coord_t *target, coord_t *source, + unsigned from, unsigned count, shift_direction where_is_free_space, unsigned free_space) +{ + char *from_ext, *to_ext; + + assert("vs-217", free_space == count * sizeof (reiser4_extent)); + + from_ext = item_body_by_coord(source); + to_ext = item_body_by_coord(target); + + if (where_is_free_space == SHIFT_LEFT) { + assert("vs-215", from == 0); + + /* At this moment, item length was already updated in the item + header by shifting code, hence nr_units_extent() will + return "new" number of units---one we obtain after copying + units. + */ + to_ext += (nr_units_extent(target) - count) * sizeof (reiser4_extent); + } else { + reiser4_key key; + coord_t coord; + + assert("vs-216", from + count == coord_last_unit_pos(source) + 1); + + from_ext += item_length_by_coord(source) - free_space; + + /* new units are inserted before first unit in an item, + therefore, we have to update item key */ + coord = *source; + coord.unit_pos = from; + unit_key_extent(&coord, &key); + + node_plugin_by_node(target->node)->update_item_key(target, &key, 0/*info */); + } + + xmemcpy(to_ext, from_ext, free_space); +} + +/* item_plugin->b.create_hook + @arg is znode of leaf node for which we need to update right delimiting key */ +reiser4_internal int +create_hook_extent(const coord_t *coord, void *arg) +{ + coord_t *child_coord; + znode *node; + reiser4_key key; + reiser4_tree *tree; + + if (!arg) + return 0; + + child_coord = arg; + tree = znode_get_tree(coord->node); + + assert("nikita-3246", znode_get_level(child_coord->node) == LEAF_LEVEL); + + WLOCK_DK(tree); + WLOCK_TREE(tree); + /* find a node on the left level for which right delimiting key has to + be updated */ + if (coord_wrt(child_coord) == COORD_ON_THE_LEFT) { + assert("vs-411", znode_is_left_connected(child_coord->node)); + node = child_coord->node->left; + } else { + assert("vs-412", coord_wrt(child_coord) == COORD_ON_THE_RIGHT); + node = child_coord->node; + assert("nikita-3314", node != NULL); + } + + if (node != NULL) { + znode_set_rd_key(node, item_key_by_coord(coord, &key)); + + assert("nikita-3282", check_sibling_list(node)); + /* break sibling links */ + if (ZF_ISSET(node, JNODE_RIGHT_CONNECTED) && node->right) { + node->right->left = NULL; + node->right = NULL; + } + } + WUNLOCK_TREE(tree); + WUNLOCK_DK(tree); + return 0; +} + + +#define ITEM_TAIL_KILLED 0 +#define ITEM_HEAD_KILLED 1 +#define ITEM_KILLED 2 + +/* item_plugin->b.kill_hook + this is called when @count units starting from @from-th one are going to be removed + */ +reiser4_internal int +kill_hook_extent(const coord_t *coord, pos_in_node_t from, pos_in_node_t count, struct carry_kill_data *kdata) +{ + reiser4_extent *ext; + reiser4_block_nr start, length; + reiser4_key min_item_key, max_item_key; + reiser4_key from_key, to_key; + const reiser4_key *pfrom_key, *pto_key; + struct inode *inode; + reiser4_tree *tree; + pgoff_t from_off, to_off, offset, skip; + int retval; + + assert ("zam-811", znode_is_write_locked(coord->node)); + assert("nikita-3315", kdata != NULL); + + item_key_by_coord(coord, &min_item_key); + max_item_key_by_coord(coord, &max_item_key); + + if (kdata->params.from_key) { + pfrom_key = kdata->params.from_key; + pto_key = kdata->params.to_key; + } else { + coord_t dup; + + assert("vs-1549", from == coord->unit_pos); + unit_key_by_coord(coord, &from_key); + pfrom_key = &from_key; + + coord_dup(&dup, coord); + dup.unit_pos = from + count - 1; + max_unit_key_by_coord(&dup, &to_key); + pto_key = &to_key; + } + + if (!keylt(pto_key, &max_item_key)) { + if (!keygt(pfrom_key, &min_item_key)) { + znode *left, *right; + + /* item is to be removed completely */ + assert("nikita-3316", kdata->left != NULL && kdata->right != NULL); + + left = kdata->left->node; + right = kdata->right->node; + + tree = current_tree; + /* we have to do two things: + * + * 1. link left and right formatted neighbors of + * extent being removed, and + * + * 2. update their delimiting keys. + * + * atomicity of these operations is protected by + * taking dk-lock and tree-lock. + */ + WLOCK_DK(tree); + /* if neighbors of item being removed are znodes - + * link them */ + UNDER_RW_VOID(tree, tree, + write, link_left_and_right(left, right)); + + if (left) { + /* update right delimiting key of left + * neighbor of extent item */ + coord_t next; + reiser4_key key; + + coord_dup(&next, coord); + + if (coord_next_item(&next)) + key = *znode_get_rd_key(coord->node); + else + item_key_by_coord(&next, &key); + znode_set_rd_key(left, &key); + } + WUNLOCK_DK(tree); + + from_off = get_key_offset(&min_item_key) >> PAGE_CACHE_SHIFT; + to_off = (get_key_offset(&max_item_key) + 1) >> PAGE_CACHE_SHIFT; + retval = ITEM_KILLED; + } else { + /* tail of item is to be removed */ + from_off = (get_key_offset(pfrom_key) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + to_off = (get_key_offset(&max_item_key) + 1) >> PAGE_CACHE_SHIFT; + retval = ITEM_TAIL_KILLED; + } + } else { + /* head of item is to be removed */ + assert("vs-1571", keyeq(pfrom_key, &min_item_key)); + assert("vs-1572", (get_key_offset(pfrom_key) & (PAGE_CACHE_SIZE - 1)) == 0); + assert("vs-1573", ((get_key_offset(pto_key) + 1) & (PAGE_CACHE_SIZE - 1)) == 0); + + if (kdata->left->node) { + /* update right delimiting key of left neighbor of extent item */ + reiser4_key key; + + key = *pto_key; + set_key_offset(&key, get_key_offset(pto_key) + 1); + + UNDER_RW_VOID(dk, current_tree, write, znode_set_rd_key(kdata->left->node, &key)); + } + + from_off = get_key_offset(pfrom_key) >> PAGE_CACHE_SHIFT; + to_off = (get_key_offset(pto_key) + 1) >> PAGE_CACHE_SHIFT; + retval = ITEM_HEAD_KILLED; + } + + inode = kdata->inode; + assert("vs-1545", inode != NULL); + if (inode != NULL) + /* take care of pages and jnodes corresponding to part of item being killed */ + reiser4_invalidate_pages(inode->i_mapping, from_off, to_off - from_off); + + ext = extent_item(coord) + from; + offset = (get_key_offset(&min_item_key) + extent_size(coord, from)) >> PAGE_CACHE_SHIFT; + + assert("vs-1551", from_off >= offset); + assert("vs-1552", from_off - offset <= extent_get_width(ext)); + skip = from_off - offset; + offset = from_off; + + while (offset < to_off) { + length = extent_get_width(ext) - skip; + if (state_of_extent(ext) == HOLE_EXTENT) { + skip = 0; + offset += length; + ext ++; + continue; + } + + if (offset + length > to_off) { + length = to_off - offset; + } + + DQUOT_FREE_BLOCK(inode, length); + + if (state_of_extent(ext) == UNALLOCATED_EXTENT) { + /* some jnodes corresponding to this unallocated extent */ + fake_allocated2free(length, + 0 /* unformatted */); + + skip = 0; + offset += length; + ext ++; + continue; + } + + assert("vs-1218", state_of_extent(ext) == ALLOCATED_EXTENT); + + if (length != 0) { + start = extent_get_start(ext) + skip; + + /* BA_DEFER bit parameter is turned on because blocks which get freed are not safe to be freed + immediately */ + reiser4_dealloc_blocks(&start, &length, 0 /* not used */, + BA_DEFER/* unformatted with defer */); + } + skip = 0; + offset += length; + ext ++; + } + return retval; +} + +/* item_plugin->b.kill_units */ +reiser4_internal int +kill_units_extent(coord_t *coord, pos_in_node_t from, pos_in_node_t to, struct carry_kill_data *kdata, + reiser4_key *smallest_removed, reiser4_key *new_first) +{ + reiser4_extent *ext; + reiser4_key item_key; + pos_in_node_t count; + reiser4_key from_key, to_key; + const reiser4_key *pfrom_key, *pto_key; + loff_t off; + int result; + + assert("vs-1541", ((kdata->params.from_key == NULL && kdata->params.to_key == NULL) || + (kdata->params.from_key != NULL && kdata->params.to_key != NULL))); + + if (kdata->params.from_key) { + pfrom_key = kdata->params.from_key; + pto_key = kdata->params.to_key; + } else { + coord_t dup; + + /* calculate key range of kill */ + assert("vs-1549", from == coord->unit_pos); + unit_key_by_coord(coord, &from_key); + pfrom_key = &from_key; + + coord_dup(&dup, coord); + dup.unit_pos = to; + max_unit_key_by_coord(&dup, &to_key); + pto_key = &to_key; + } + + item_key_by_coord(coord, &item_key); + +#if REISER4_DEBUG + { + reiser4_key max_item_key; + + max_item_key_by_coord(coord, &max_item_key); + + if (new_first) { + /* head of item is to be cut */ + assert("vs-1542", keyeq(pfrom_key, &item_key)); + assert("vs-1538", keylt(pto_key, &max_item_key)); + } else { + /* tail of item is to be cut */ + assert("vs-1540", keygt(pfrom_key, &item_key)); + assert("vs-1543", !keylt(pto_key, &max_item_key)); + } + } +#endif + + if (smallest_removed) + *smallest_removed = *pfrom_key; + + if (new_first) { + /* item head is cut. Item key will change. This new key is calculated here */ + assert("vs-1556", (get_key_offset(pto_key) & (PAGE_CACHE_SIZE - 1)) == (PAGE_CACHE_SIZE - 1)); + *new_first = *pto_key; + set_key_offset(new_first, get_key_offset(new_first) + 1); + } + + count = to - from + 1; + result = kill_hook_extent(coord, from, count, kdata); + if (result == ITEM_TAIL_KILLED) { + assert("vs-1553", get_key_offset(pfrom_key) >= get_key_offset(&item_key) + extent_size(coord, from)); + off = get_key_offset(pfrom_key) - (get_key_offset(&item_key) + extent_size(coord, from)); + if (off) { + /* unit @from is to be cut partially. Its width decreases */ + ext = extent_item(coord) + from; + extent_set_width(ext, (off + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT); + count --; + } + } else { + __u64 max_to_offset; + __u64 rest; + + assert("vs-1575", result == ITEM_HEAD_KILLED); + assert("", from == 0); + assert("", ((get_key_offset(pto_key) + 1) & (PAGE_CACHE_SIZE - 1)) == 0); + assert("", get_key_offset(pto_key) + 1 > get_key_offset(&item_key) + extent_size(coord, to)); + max_to_offset = get_key_offset(&item_key) + extent_size(coord, to + 1) - 1; + assert("", get_key_offset(pto_key) <= max_to_offset); + + rest = (max_to_offset - get_key_offset(pto_key)) >> PAGE_CACHE_SHIFT; + if (rest) { + /* unit @to is to be cut partially */ + ext = extent_item(coord) + to; + + assert("", extent_get_width(ext) > rest); + + if (state_of_extent(ext) == ALLOCATED_EXTENT) + extent_set_start(ext, extent_get_start(ext) + (extent_get_width(ext) - rest)); + + extent_set_width(ext, rest); + count --; + } + } + return count * sizeof(reiser4_extent); +} + +/* item_plugin->b.cut_units + this is too similar to kill_units_extent */ +reiser4_internal int +cut_units_extent(coord_t *coord, pos_in_node_t from, pos_in_node_t to, struct carry_cut_data *cdata, + reiser4_key *smallest_removed, reiser4_key *new_first) +{ + reiser4_extent *ext; + reiser4_key item_key; + pos_in_node_t count; + reiser4_key from_key, to_key; + const reiser4_key *pfrom_key, *pto_key; + loff_t off; + + assert("vs-1541", ((cdata->params.from_key == NULL && cdata->params.to_key == NULL) || + (cdata->params.from_key != NULL && cdata->params.to_key != NULL))); + + if (cdata->params.from_key) { + pfrom_key = cdata->params.from_key; + pto_key = cdata->params.to_key; + } else { + coord_t dup; + + /* calculate key range of kill */ + coord_dup(&dup, coord); + dup.unit_pos = from; + unit_key_by_coord(&dup, &from_key); + + dup.unit_pos = to; + max_unit_key_by_coord(&dup, &to_key); + + pfrom_key = &from_key; + pto_key = &to_key; + } + + assert("vs-1555", (get_key_offset(pfrom_key) & (PAGE_CACHE_SIZE - 1)) == 0); + assert("vs-1556", (get_key_offset(pto_key) & (PAGE_CACHE_SIZE - 1)) == (PAGE_CACHE_SIZE - 1)); + + item_key_by_coord(coord, &item_key); + +#if REISER4_DEBUG + { + reiser4_key max_item_key; + + assert("vs-1584", get_key_locality(pfrom_key) == get_key_locality(&item_key)); + assert("vs-1585", get_key_type(pfrom_key) == get_key_type(&item_key)); + assert("vs-1586", get_key_objectid(pfrom_key) == get_key_objectid(&item_key)); + assert("vs-1587", get_key_ordering(pfrom_key) == get_key_ordering(&item_key)); + + max_item_key_by_coord(coord, &max_item_key); + + if (new_first != NULL) { + /* head of item is to be cut */ + assert("vs-1542", keyeq(pfrom_key, &item_key)); + assert("vs-1538", keylt(pto_key, &max_item_key)); + } else { + /* tail of item is to be cut */ + assert("vs-1540", keygt(pfrom_key, &item_key)); + assert("vs-1543", keyeq(pto_key, &max_item_key)); + } + } +#endif + + if (smallest_removed) + *smallest_removed = *pfrom_key; + + if (new_first) { + /* item head is cut. Item key will change. This new key is calculated here */ + *new_first = *pto_key; + set_key_offset(new_first, get_key_offset(new_first) + 1); + } + + count = to - from + 1; + + assert("vs-1553", get_key_offset(pfrom_key) >= get_key_offset(&item_key) + extent_size(coord, from)); + off = get_key_offset(pfrom_key) - (get_key_offset(&item_key) + extent_size(coord, from)); + if (off) { + /* tail of unit @from is to be cut partially. Its width decreases */ + assert("vs-1582", new_first == NULL); + ext = extent_item(coord) + from; + extent_set_width(ext, off >> PAGE_CACHE_SHIFT); + count --; + } + + assert("vs-1554", get_key_offset(pto_key) <= get_key_offset(&item_key) + extent_size(coord, to + 1) - 1); + off = (get_key_offset(&item_key) + extent_size(coord, to + 1) - 1) - get_key_offset(pto_key); + if (off) { + /* @to_key is smaller than max key of unit @to. Unit @to will not be removed. It gets start increased + and width decreased. */ + assert("vs-1583", (off & (PAGE_CACHE_SIZE - 1)) == 0); + ext = extent_item(coord) + to; + if (state_of_extent(ext) == ALLOCATED_EXTENT) + extent_set_start(ext, extent_get_start(ext) + (extent_get_width(ext) - (off >> PAGE_CACHE_SHIFT))); + + extent_set_width(ext, (off >> PAGE_CACHE_SHIFT)); + count --; + } + return count * sizeof(reiser4_extent); +} + +/* item_plugin->b.unit_key */ +reiser4_internal reiser4_key * +unit_key_extent(const coord_t *coord, reiser4_key *key) +{ + assert("vs-300", coord_is_existing_unit(coord)); + + item_key_by_coord(coord, key); + set_key_offset(key, (get_key_offset(key) + extent_size(coord, coord->unit_pos))); + + return key; +} + +/* item_plugin->b.max_unit_key */ +reiser4_internal reiser4_key * +max_unit_key_extent(const coord_t *coord, reiser4_key *key) +{ + assert("vs-300", coord_is_existing_unit(coord)); + + item_key_by_coord(coord, key); + set_key_offset(key, (get_key_offset(key) + extent_size(coord, coord->unit_pos + 1) - 1)); + return key; +} + +/* item_plugin->b.estimate + item_plugin->b.item_data_by_flow */ + +#if REISER4_DEBUG + +/* item_plugin->b.check + used for debugging, every item should have here the most complete + possible check of the consistency of the item that the inventor can + construct +*/ +int +check_extent(const coord_t *coord /* coord of item to check */ , + const char **error /* where to store error message */ ) +{ + reiser4_extent *ext, *first; + unsigned i, j; + reiser4_block_nr start, width, blk_cnt; + unsigned num_units; + reiser4_tree *tree; + oid_t oid; + reiser4_key key; + coord_t scan; + + assert("vs-933", REISER4_DEBUG); + + if (znode_get_level(coord->node) != TWIG_LEVEL) { + *error = "Extent on the wrong level"; + return -1; + } + if (item_length_by_coord(coord) % sizeof (reiser4_extent) != 0) { + *error = "Wrong item size"; + return -1; + } + ext = first = extent_item(coord); + blk_cnt = reiser4_block_count(reiser4_get_current_sb()); + num_units = coord_num_units(coord); + tree = znode_get_tree(coord->node); + item_key_by_coord(coord, &key); + oid = get_key_objectid(&key); + coord_dup(&scan, coord); + + for (i = 0; i < num_units; ++i, ++ext) { + __u64 index; + + scan.unit_pos = i; + index = extent_unit_index(&scan); + +#if 0 + /* check that all jnodes are present for the unallocated + * extent */ + if (state_of_extent(ext) == UNALLOCATED_EXTENT) { + for (j = 0; j < extent_get_width(ext); j ++) { + jnode *node; + + node = jlookup(tree, oid, index + j); + if (node == NULL) { + print_coord("scan", &scan, 0); + *error = "Jnode missing"; + return -1; + } + jput(node); + } + } +#endif + + start = extent_get_start(ext); + if (start < 2) + continue; + /* extent is allocated one */ + width = extent_get_width(ext); + if (start >= blk_cnt) { + *error = "Start too large"; + return -1; + } + if (start + width > blk_cnt) { + *error = "End too large"; + return -1; + } + /* make sure that this extent does not overlap with other + allocated extents extents */ + for (j = 0; j < i; j++) { + if (state_of_extent(first + j) != ALLOCATED_EXTENT) + continue; + if (!((extent_get_start(ext) >= extent_get_start(first + j) + extent_get_width(first + j)) + || (extent_get_start(ext) + extent_get_width(ext) <= extent_get_start(first + j)))) { + *error = "Extent overlaps with others"; + return -1; + } + } + + } + + return 0; +} + +#endif /* REISER4_DEBUG */ + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/item/extent_repack_ops.c 2004-09-03 00:57:05.266238672 -0700 @@ -0,0 +1,446 @@ +/* Copyright 2003 by Hans Reiser. */ + +#include "item.h" +#include "../../key.h" +#include "../../super.h" +#include "../../carry.h" +#include "../../inode.h" +#include "../../page_cache.h" +#include "../../emergency_flush.h" +#include "../../prof.h" +#include "../../flush.h" +#include "../../tap.h" +#include "../object.h" + +#include "../../repacker.h" +#include "extent.h" + +static int get_reiser4_inode_by_tap (struct inode ** result, tap_t * tap) +{ + reiser4_key ext_key; + + unit_key_by_coord(tap->coord, &ext_key); + return get_reiser4_inode_by_key(result, &ext_key); +} + +static jnode * get_jnode_by_mapping (struct inode * inode, long index) +{ + struct page * page; + jnode * node; + + page = grab_cache_page(inode->i_mapping, index); + if (page == NULL) + return ERR_PTR(-ENOMEM); + node = jnode_of_page(page); + unlock_page(page); + page_cache_release(page); + return node; +} + +static int mark_jnode_for_repacking (jnode * node) +{ + int ret = 0; + + LOCK_JNODE(node); + ret = try_capture(node, ZNODE_WRITE_LOCK, 0, 0/* no can_coc */); + if (ret) { + UNLOCK_JNODE(node); + return ret; + } + + jnode_make_dirty_locked(node); + UNLOCK_JNODE(node); + JF_SET(node, JNODE_REPACK); + + ret = jload(node); + if (ret == 0) { + struct page * page; + + page = jnode_page(node); + lock_page(page); + set_page_dirty_internal(page, 0); + unlock_page(page); + jrelse(node); + } + + return ret; +} + +/* + Mark jnodes of given extent for repacking. + @tap : lock, coord and load status for the tree traversal position, + @max_nr_marked: a maximum number of nodes which can be marked for repacking, + @return: error code if < 0, number of marked nodes otherwise. +*/ +reiser4_internal int mark_extent_for_repacking (tap_t * tap, int max_nr_marked) +{ + coord_t * coord = tap->coord; + reiser4_extent *ext; + int nr_marked; + struct inode * inode; + unsigned long index, pos_in_extent; + reiser4_block_nr width, start; + int ret; + + ext = extent_by_coord(coord); + + if (state_of_extent(ext) == HOLE_EXTENT) + return 0; + + width = extent_get_width(ext); + start = extent_get_start(ext); + index = extent_unit_index(coord); + + ret = get_reiser4_inode_by_tap(&inode, tap); + if (ret) + return ret; + + for (nr_marked = 0, pos_in_extent = 0; + nr_marked < max_nr_marked && pos_in_extent < width; pos_in_extent ++) + { + jnode * node; + + node = get_jnode_by_mapping(inode, index + pos_in_extent); + if (IS_ERR(node)) { + ret = PTR_ERR(node); + break; + } + + /* Freshly created jnode has no block number set. */ + if (node->blocknr == 0) { + reiser4_block_nr block; + block = start + pos_in_extent; + jnode_set_block(node, &block); + + node->parent_item_id = EXTENT_POINTER_ID; + } + + if (!JF_ISSET(node, JNODE_REPACK)) { + do { + /* Check whether the node is already read. */ + if (!JF_ISSET(node, JNODE_PARSED)) { + ret = jstartio(node); + if (ret) + break; + } + ret = mark_jnode_for_repacking(node); + if (ret) + break; + nr_marked ++; + } while (0); + } + jput(node); + if (ret) + break; + } + + iput(inode); + if (ret) + return ret; + return nr_marked; +} + +/* Check should the repacker relocate this node. */ +static int relocatable (jnode * check) +{ + return !JF_ISSET(check, JNODE_OVRWR) && !JF_ISSET(check, JNODE_RELOC); +} + +static int replace_end_of_extent (coord_t * coord, reiser4_block_nr end_part_start, + reiser4_block_nr end_part_width, int * all_replaced) +{ + reiser4_extent * ext; + reiser4_block_nr ext_start; + reiser4_block_nr ext_width; + + reiser4_item_data item; + reiser4_extent new_ext, replace_ext; + reiser4_block_nr replace_ext_width; + reiser4_key key; + + int ret; + + assert ("zam-959", item_is_extent(coord)); + + ext = extent_by_coord(coord); + ext_start = extent_get_start(ext); + ext_width = extent_get_width(ext); + + assert ("zam-960", end_part_width <= ext_width); + + replace_ext_width = ext_width - end_part_width; + if (replace_ext_width == 0) { + set_extent(ext, end_part_start, end_part_width); + znode_make_dirty(coord->node); + /* End part of extent is equal to the whole extent. */ + * all_replaced = 1; + return 0; + } + + set_extent(&replace_ext, ext_start, replace_ext_width); + set_extent(&new_ext, end_part_start, end_part_width); + + unit_key_by_coord(coord, &key); + set_key_offset(&key, get_key_offset(&key) + replace_ext_width * current_blocksize); + + { + reiser4_context * ctx = get_current_context(); + reiser4_super_info_data * sinfo = get_super_private(ctx->super); + __u64 estimated; + __u64 were_grabbed; + + were_grabbed = ctx->grabbed_blocks; + estimated = estimate_one_insert_item(&get_super_private(ctx->super)->tree); + + /* grab space for operations on internal levels. */ + ret = reiser4_grab_space( + estimated, BA_FORCE | BA_RESERVED | BA_PERMANENT | BA_FORMATTED); + if (ret) + return ret; + + ret = replace_extent( + coord, znode_lh(coord->node), &key, + init_new_extent(&item, &new_ext, 1), &replace_ext, + COPI_DONT_SHIFT_LEFT, 0); + + /* release grabbed space if it was not used. */ + assert ("zam-988", ctx->grabbed_blocks >= were_grabbed); + grabbed2free(ctx, sinfo, ctx->grabbed_blocks - were_grabbed); + } + + return ret; +} + +static int make_new_extent_at_end (coord_t * coord, reiser4_block_nr width, int * all_replaced) +{ + reiser4_extent * ext; + reiser4_block_nr ext_start; + reiser4_block_nr ext_width; + reiser4_block_nr new_ext_start; + + assert ("zam-961", item_is_extent(coord)); + + ext = extent_by_coord(coord); + ext_start = extent_get_start(ext); + ext_width = extent_get_width(ext); + + assert ("zam-962", width < ext_width); + + if (state_of_extent(ext) == ALLOCATED_EXTENT) + new_ext_start = ext_start + ext_width - width; + else + new_ext_start = ext_start; + + return replace_end_of_extent(coord, new_ext_start, width, all_replaced); +} + +static void parse_extent(coord_t * coord, reiser4_block_nr * start, reiser4_block_nr * width, long * ind) +{ + reiser4_extent * ext; + + ext = extent_by_coord(coord); + *start = extent_get_start(ext); + *width = extent_get_width(ext); + *ind = extent_unit_index(coord); +} + +static int skip_not_relocatable_extent(struct inode * inode, coord_t * coord, int * done) +{ + reiser4_block_nr ext_width, ext_start; + long ext_index, reloc_start; + jnode * check = NULL; + int ret = 0; + + assert("zam-985", state_of_extent(extent_by_coord(coord))); + parse_extent(coord, &ext_start, &ext_width, &ext_index); + + for (reloc_start = ext_width - 1; reloc_start >= 0; reloc_start --) { + check = get_jnode_by_mapping(inode, reloc_start + ext_index); + if (IS_ERR(check)) + return PTR_ERR(check); + + if (check->blocknr == 0) { + reiser4_block_nr block; + block = ext_start + reloc_start; + jnode_set_block(check, &block); + + check->parent_item_id = EXTENT_POINTER_ID; + } + + if (relocatable(check)) { + jput(check); + if (reloc_start < ext_width - 1) + ret = make_new_extent_at_end(coord, ext_width - reloc_start - 1, done); + return ret; + } + jput(check); + } + *done = 1; + return 0; +} + + +static int relocate_extent (struct inode * inode, coord_t * coord, reiser4_blocknr_hint * hint, + int *done, reiser4_block_nr * len) +{ + reiser4_block_nr ext_width, ext_start; + long ext_index, reloc_ind; + reiser4_block_nr new_ext_width, new_ext_start, new_block; + int unallocated_flg; + int ret = 0; + + parse_extent(coord, &ext_start, &ext_width, &ext_index); + assert("zam-974", *len != 0); + + unallocated_flg = (state_of_extent(extent_by_coord(coord)) == UNALLOCATED_EXTENT); + hint->block_stage = unallocated_flg ? BLOCK_UNALLOCATED : BLOCK_FLUSH_RESERVED; + + new_ext_width = *len; + ret = reiser4_alloc_blocks(hint, &new_ext_start, &new_ext_width, BA_PERMANENT); + if (ret) + return ret; + + hint->blk = new_ext_start; + if (!unallocated_flg) { + reiser4_block_nr dealloc_ext_start; + + dealloc_ext_start = ext_start + ext_width - new_ext_width; + ret = reiser4_dealloc_blocks(&dealloc_ext_start, &new_ext_width, 0, + BA_DEFER | BA_PERMANENT); + if (ret) + return ret; + } + + new_block = new_ext_start; + for (reloc_ind = ext_width - new_ext_width; reloc_ind < ext_width; reloc_ind ++) + { + jnode * check; + + check = get_jnode_by_mapping(inode, ext_index + reloc_ind); + if (IS_ERR(check)) + return PTR_ERR(check); + + assert("zam-975", relocatable(check)); + assert("zam-986", check->blocknr != 0); + + jnode_set_block(check, &new_block); + check->parent_item_id = EXTENT_POINTER_ID; + new_block ++; + + JF_SET(check, JNODE_RELOC); + JF_SET(check, JNODE_REPACK); + + jput(check); + } + + ret = replace_end_of_extent(coord, new_ext_start, new_ext_width, done); + *len = new_ext_width; + return ret; +} + +static int find_relocatable_extent (struct inode * inode, coord_t * coord, + int * nr_reserved, reiser4_block_nr * len) +{ + reiser4_block_nr ext_width, ext_start; + long ext_index, reloc_end; + jnode * check = NULL; + int ret = 0; + + *len = 0; + parse_extent(coord, &ext_start, &ext_width, &ext_index); + + for (reloc_end = ext_width - 1; + reloc_end >= 0 && *nr_reserved > 0; reloc_end --) + { + assert("zam-980", get_current_context()->grabbed_blocks >= *nr_reserved); + + check = get_jnode_by_mapping(inode, reloc_end + ext_index); + if (IS_ERR(check)) + return PTR_ERR(check); + + if (check->blocknr == 0) { + reiser4_block_nr block; + block = ext_start + reloc_end; + jnode_set_block(check, &block); + } + + if (!relocatable(check)) { + assert("zam-973", reloc_end < ext_width - 1); + goto out; + } + /* add node to transaction. */ + ret = mark_jnode_for_repacking(check); + if (ret) + goto out; ; + jput(check); + + (*len) ++; + (*nr_reserved) --; + } + if (0) { + out: + jput(check); + } + return ret; +} + +static int find_and_relocate_end_of_extent ( + struct inode * inode, coord_t * coord, + struct repacker_cursor * cursor, int * done) +{ + reiser4_block_nr len; + int ret; + + ret = skip_not_relocatable_extent(inode, coord, done); + if (ret || (*done)) + return ret; + + ret = find_relocatable_extent(inode, coord, &cursor->count, &len); + if (ret) + return ret; + if (len == 0) { + *done = 1; + return 0; + } + + ret = relocate_extent(inode, coord, &cursor->hint, done, &len); + if (ret) + return ret; + cursor->stats.jnodes_dirtied += (long)len; + return 0; +} + +/* process (relocate) unformatted nodes in backward direction: from the end of extent to the its start. */ +reiser4_internal int +process_extent_backward_for_repacking (tap_t * tap, struct repacker_cursor * cursor) +{ + coord_t * coord = tap->coord; + reiser4_extent *ext; + struct inode * inode = NULL; + int done = 0; + int ret; + + assert("zam-985", cursor->count > 0); + ext = extent_by_coord(coord); + if (state_of_extent(ext) == HOLE_EXTENT) + return 0; + + ret = get_reiser4_inode_by_tap(&inode, tap); + + while (!ret && !done) + ret = find_and_relocate_end_of_extent(inode, coord, cursor, &done); + + iput(inode); + return ret; +} + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/item/internal.c 2004-09-03 00:57:05.268238368 -0700 @@ -0,0 +1,411 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Implementation of internal-item plugin methods. */ + +#include "../../forward.h" +#include "../../debug.h" +#include "../../dformat.h" +#include "../../key.h" +#include "../../coord.h" +#include "internal.h" +#include "item.h" +#include "../node/node.h" +#include "../plugin.h" +#include "../../jnode.h" +#include "../../znode.h" +#include "../../tree_walk.h" +#include "../../tree_mod.h" +#include "../../tree.h" +#include "../../super.h" +#include "../../block_alloc.h" + +/* see internal.h for explanation */ + +/* plugin->u.item.b.mergeable */ +reiser4_internal int +mergeable_internal(const coord_t * p1 UNUSED_ARG /* first item */ , + const coord_t * p2 UNUSED_ARG /* second item */ ) +{ + /* internal items are not mergeable */ + return 0; +} + +/* ->lookup() method for internal items */ +reiser4_internal lookup_result +lookup_internal(const reiser4_key * key /* key to look up */ , + lookup_bias bias UNUSED_ARG /* lookup bias */ , + coord_t * coord /* coord of item */ ) +{ + reiser4_key ukey; + + switch (keycmp(unit_key_by_coord(coord, &ukey), key)) { + default: + impossible("", "keycmp()?!"); + case LESS_THAN: + /* FIXME-VS: AFTER_ITEM used to be here. But with new coord + item plugin can not be taken using coord set this way */ + assert("vs-681", coord->unit_pos == 0); + coord->between = AFTER_UNIT; + case EQUAL_TO: + return CBK_COORD_FOUND; + case GREATER_THAN: + return CBK_COORD_NOTFOUND; + } +} + +/* return body of internal item at @coord */ +static internal_item_layout * +internal_at(const coord_t * coord /* coord of + * item */ ) +{ + assert("nikita-607", coord != NULL); + assert("nikita-1650", item_plugin_by_coord(coord) == item_plugin_by_id(NODE_POINTER_ID)); + return (internal_item_layout *) item_body_by_coord(coord); +} + +reiser4_internal void +update_internal(const coord_t * coord, const reiser4_block_nr * blocknr) +{ + internal_item_layout *item = internal_at(coord); + assert("nikita-2959", reiser4_blocknr_is_sane(blocknr)); + + cpu_to_dblock(*blocknr, &item->pointer); +} + +/* return child block number stored in the internal item at @coord */ +static reiser4_block_nr +pointer_at(const coord_t * coord /* coord of item */ ) +{ + assert("nikita-608", coord != NULL); + return dblock_to_cpu(&internal_at(coord)->pointer); +} + +/* get znode pointed to by internal @item */ +static znode * +znode_at(const coord_t * item /* coord of item */ , + znode * parent /* parent node */) +{ + return child_znode(item, parent, 1, 0); +} + +/* store pointer from internal item into "block". Implementation of + ->down_link() method */ +reiser4_internal void +down_link_internal(const coord_t * coord /* coord of item */ , + const reiser4_key * key UNUSED_ARG /* key to get + * pointer for */ , + reiser4_block_nr * block /* resulting block number */ ) +{ + ON_DEBUG(reiser4_key item_key); + + assert("nikita-609", coord != NULL); + assert("nikita-611", block != NULL); + assert("nikita-612", (key == NULL) || + /* twig horrors */ + (znode_get_level(coord->node) == TWIG_LEVEL) || keyle(item_key_by_coord(coord, &item_key), key)); + + *block = pointer_at(coord); + assert("nikita-2960", reiser4_blocknr_is_sane(block)); +} + +/* Get the child's block number, or 0 if the block is unallocated. */ +reiser4_internal int +utmost_child_real_block_internal(const coord_t * coord, sideof side UNUSED_ARG, reiser4_block_nr * block) +{ + assert("jmacd-2059", coord != NULL); + + *block = pointer_at(coord); + assert("nikita-2961", reiser4_blocknr_is_sane(block)); + + if (blocknr_is_fake(block)) { + *block = 0; + } + + return 0; +} + +/* Return the child. */ +reiser4_internal int +utmost_child_internal(const coord_t * coord, sideof side UNUSED_ARG, jnode ** childp) +{ + reiser4_block_nr block = pointer_at(coord); + znode *child; + + assert("jmacd-2059", childp != NULL); + assert("nikita-2962", reiser4_blocknr_is_sane(&block)); + + child = zlook(znode_get_tree(coord->node), &block); + + if (IS_ERR(child)) { + return PTR_ERR(child); + } + + *childp = ZJNODE(child); + + return 0; +} + +static void check_link(znode *left, znode *right) +{ + znode *scan; + + for (scan = left; scan != right; scan = scan->right) { + if (ZF_ISSET(scan, JNODE_RIP)) + break; + if (znode_is_right_connected(scan) && scan->right != NULL) { + if (ZF_ISSET(scan->right, JNODE_RIP)) + break; + assert("nikita-3285", + znode_is_left_connected(scan->right)); + assert("nikita-3265", + ergo(scan != left, + ZF_ISSET(scan, JNODE_HEARD_BANSHEE))); + assert("nikita-3284", scan->right->left == scan); + } else + break; + } +} + +reiser4_internal int check__internal(const coord_t * coord, const char **error) +{ + reiser4_block_nr blk; + znode *child; + coord_t cpy; + + blk = pointer_at(coord); + if (!reiser4_blocknr_is_sane(&blk)) { + *error = "Invalid pointer"; + return -1; + } + coord_dup(&cpy, coord); + child = znode_at(&cpy, cpy.node); + if (child != NULL) { + znode *left_child; + znode *right_child; + + left_child = right_child = NULL; + + assert("nikita-3256", znode_invariant(child)); + if (coord_prev_item(&cpy) == 0 && item_is_internal(&cpy)) { + left_child = znode_at(&cpy, cpy.node); + RLOCK_TREE(znode_get_tree(child)); + if (left_child != NULL) + check_link(left_child, child); + RUNLOCK_TREE(znode_get_tree(child)); + if (left_child != NULL) + zput(left_child); + } + coord_dup(&cpy, coord); + if (coord_next_item(&cpy) == 0 && item_is_internal(&cpy)) { + right_child = znode_at(&cpy, cpy.node); + RLOCK_TREE(znode_get_tree(child)); + if (right_child != NULL) + check_link(child, right_child); + RUNLOCK_TREE(znode_get_tree(child)); + if (right_child != NULL) + zput(right_child); + } + zput(child); + } + return 0; +} + +#if REISER4_DEBUG_OUTPUT +/* debugging aid: print human readable information about internal item at + @coord */ +reiser4_internal void +print_internal(const char *prefix /* prefix to print */ , + coord_t * coord /* coord of item to print */ ) +{ + reiser4_block_nr blk; + + blk = pointer_at(coord); + assert("nikita-2963", reiser4_blocknr_is_sane(&blk)); + printk("%s: internal: %s\n", prefix, sprint_address(&blk)); +} +#endif + +/* return true only if this item really points to "block" */ +/* Audited by: green(2002.06.14) */ +reiser4_internal int +has_pointer_to_internal(const coord_t * coord /* coord of item */ , + const reiser4_block_nr * block /* block number to + * check */ ) +{ + assert("nikita-613", coord != NULL); + assert("nikita-614", block != NULL); + + return pointer_at(coord) == *block; +} + +/* hook called by ->create_item() method of node plugin after new internal + item was just created. + + This is point where pointer to new node is inserted into tree. Initialize + parent pointer in child znode, insert child into sibling list and slum. + +*/ +reiser4_internal int +create_hook_internal(const coord_t * item /* coord of item */ , + void *arg /* child's left neighbor, if any */ ) +{ + znode *child; + + assert("nikita-1252", item != NULL); + assert("nikita-1253", item->node != NULL); + assert("nikita-1181", znode_get_level(item->node) > LEAF_LEVEL); + assert("nikita-1450", item->unit_pos == 0); + + child = znode_at(item, item->node); + if (!IS_ERR(child)) { + znode *left; + int result = 0; + reiser4_tree *tree; + + left = arg; + tree = znode_get_tree(item->node); + WLOCK_DK(tree); + WLOCK_TREE(tree); + assert("nikita-1400", (child->in_parent.node == NULL) || (znode_above_root(child->in_parent.node))); + ++ item->node->c_count; + coord_to_parent_coord(item, &child->in_parent); + sibling_list_insert_nolock(child, left); + + assert("nikita-3297", ZF_ISSET(child, JNODE_ORPHAN)); + ZF_CLR(child, JNODE_ORPHAN); + + ON_TRACE(TRACE_ZWEB, "create: %llx: %i [%llx]\n", + *znode_get_block(item->node), item->node->c_count, + *znode_get_block(child)); + + WUNLOCK_TREE(tree); + if ((left != NULL) && !keyeq(znode_get_rd_key(left), + znode_get_rd_key(child))) { + znode_set_rd_key(child, znode_get_rd_key(left)); + } + WUNLOCK_DK(tree); + zput(child); + return result; + } else + return PTR_ERR(child); +} + +/* hook called by ->cut_and_kill() method of node plugin just before internal + item is removed. + + This is point where empty node is removed from the tree. Clear parent + pointer in child, and mark node for pending deletion. + + Node will be actually deleted later and in several installations: + + . when last lock on this node will be released, node will be removed from + the sibling list and its lock will be invalidated + + . when last reference to this node will be dropped, bitmap will be updated + and node will be actually removed from the memory. + + +*/ +reiser4_internal int +kill_hook_internal(const coord_t * item /* coord of item */ , + pos_in_node_t from UNUSED_ARG /* start unit */ , + pos_in_node_t count UNUSED_ARG /* stop unit */, + struct carry_kill_data *p UNUSED_ARG) +{ + znode *child; + + assert("nikita-1222", item != NULL); + assert("nikita-1224", from == 0); + assert("nikita-1225", count == 1); + + child = znode_at(item, item->node); + if (IS_ERR(child)) + return PTR_ERR(child); + else if (node_is_empty(child)) { + reiser4_tree *tree; + + assert("nikita-1397", znode_is_write_locked(child)); + assert("nikita-1398", child->c_count == 0); + assert("nikita-2546", ZF_ISSET(child, JNODE_HEARD_BANSHEE)); + + tree = znode_get_tree(item->node); + WLOCK_TREE(tree); + init_parent_coord(&child->in_parent, NULL); + -- item->node->c_count; + WUNLOCK_TREE(tree); + ON_TRACE(TRACE_ZWEB, "kill: %llx: %i [%llx]\n", + *znode_get_block(item->node), item->node->c_count, + *znode_get_block(child)); + + zput(child); + return 0; + } else { + warning("nikita-1223", "Cowardly refuse to remove link to non-empty node"); + print_znode("parent", item->node); + print_znode("child", child); + zput(child); + return RETERR(-EIO); + } +} + +/* hook called by ->shift() node plugin method when iternal item was just + moved from one node to another. + + Update parent pointer in child and c_counts in old and new parent + +*/ +reiser4_internal int +shift_hook_internal(const coord_t * item /* coord of item */ , + unsigned from UNUSED_ARG /* start unit */ , + unsigned count UNUSED_ARG /* stop unit */ , + znode * old_node /* old parent */ ) +{ + znode *child; + znode *new_node; + reiser4_tree *tree; + + assert("nikita-1276", item != NULL); + assert("nikita-1277", from == 0); + assert("nikita-1278", count == 1); + assert("nikita-1451", item->unit_pos == 0); + + new_node = item->node; + assert("nikita-2132", new_node != old_node); + tree = znode_get_tree(item->node); + child = child_znode(item, old_node, 1, 0); + if (child == NULL) + return 0; + if (!IS_ERR(child)) { + reiser4_stat_inc(tree.reparenting); + WLOCK_TREE(tree); + ++ new_node->c_count; + assert("nikita-1395", znode_parent(child) == old_node); + assert("nikita-1396", old_node->c_count > 0); + coord_to_parent_coord(item, &child->in_parent); + assert("nikita-1781", znode_parent(child) == new_node); + assert("nikita-1782", check_tree_pointer(item, child) == NS_FOUND); + -- old_node->c_count; + WUNLOCK_TREE(tree); + zput(child); + ON_TRACE(TRACE_ZWEB, "shift: %llx: %i -> %lli: %i [%llx]\n", + *znode_get_block(old_node), + old_node->c_count, *znode_get_block(new_node), + new_node->c_count, *znode_get_block(child)); + return 0; + } else + return PTR_ERR(child); +} + +/* plugin->u.item.b.max_key_inside - not defined */ + +/* plugin->u.item.b.nr_units - item.c:single_unit */ + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/item/internal.h 2004-09-03 00:57:05.269238216 -0700 @@ -0,0 +1,51 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ +/* Internal item contains down-link to the child of the internal/twig + node in a tree. It is internal items that are actually used during + tree traversal. */ + +#if !defined( __FS_REISER4_PLUGIN_ITEM_INTERNAL_H__ ) +#define __FS_REISER4_PLUGIN_ITEM_INTERNAL_H__ + +#include "../../forward.h" +#include "../../dformat.h" + +/* on-disk layout of internal item */ +typedef struct internal_item_layout { + /* 0 */ reiser4_dblock_nr pointer; + /* 4 */ +} internal_item_layout; + +struct cut_list; + +int mergeable_internal(const coord_t * p1, const coord_t * p2); +lookup_result lookup_internal(const reiser4_key * key, lookup_bias bias, coord_t * coord); +/* store pointer from internal item into "block". Implementation of + ->down_link() method */ +extern void down_link_internal(const coord_t * coord, const reiser4_key * key, reiser4_block_nr * block); +extern int has_pointer_to_internal(const coord_t * coord, const reiser4_block_nr * block); +extern int create_hook_internal(const coord_t * item, void *arg); +extern int kill_hook_internal(const coord_t * item, pos_in_node_t from, pos_in_node_t count, + struct carry_kill_data *); +extern int shift_hook_internal(const coord_t * item, unsigned from, unsigned count, znode * old_node); +extern void print_internal(const char *prefix, coord_t * coord); + +extern int utmost_child_internal(const coord_t * coord, sideof side, jnode ** child); +int utmost_child_real_block_internal(const coord_t * coord, sideof side, reiser4_block_nr * block); + +extern void update_internal(const coord_t * coord, + const reiser4_block_nr * blocknr); +/* FIXME: reiserfs has check_internal */ +extern int check__internal(const coord_t * coord, const char **error); + +/* __FS_REISER4_PLUGIN_ITEM_INTERNAL_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/item/item.c 2004-09-03 00:57:05.272237760 -0700 @@ -0,0 +1,770 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* definition of item plugins. */ + +#include "../../forward.h" +#include "../../debug.h" +#include "../../key.h" +#include "../../coord.h" +#include "../plugin_header.h" +#include "sde.h" +#include "../cryptcompress.h" +#include "internal.h" +#include "item.h" +#include "static_stat.h" +#include "../plugin.h" +#include "../../znode.h" +#include "../../tree.h" +#include "../../context.h" +#include "ctail.h" + +/* return pointer to item body */ +reiser4_internal void +item_body_by_coord_hard(coord_t * coord /* coord to query */ ) +{ + assert("nikita-324", coord != NULL); + assert("nikita-325", coord->node != NULL); + assert("nikita-326", znode_is_loaded(coord->node)); + assert("nikita-3200", coord->offset == INVALID_OFFSET); + trace_stamp(TRACE_TREE); + + coord->offset = node_plugin_by_node(coord->node)->item_by_coord(coord) - zdata(coord->node); + ON_DEBUG(coord->body_v = coord->node->times_locked); +} + +reiser4_internal void * +item_body_by_coord_easy(const coord_t * coord /* coord to query */ ) +{ + return zdata(coord->node) + coord->offset; +} + +#if REISER4_DEBUG + +reiser4_internal int +item_body_is_valid(const coord_t * coord) +{ + return + coord->offset == + node_plugin_by_node(coord->node)->item_by_coord(coord) - zdata(coord->node); +} + +#endif + +/* return length of item at @coord */ +reiser4_internal pos_in_node_t +item_length_by_coord(const coord_t * coord /* coord to query */ ) +{ + int len; + + assert("nikita-327", coord != NULL); + assert("nikita-328", coord->node != NULL); + assert("nikita-329", znode_is_loaded(coord->node)); + trace_stamp(TRACE_TREE); + + len = node_plugin_by_node(coord->node)->length_by_coord(coord); + check_contexts(); + return len; +} + +reiser4_internal void +obtain_item_plugin(const coord_t * coord) +{ + assert("nikita-330", coord != NULL); + assert("nikita-331", coord->node != NULL); + assert("nikita-332", znode_is_loaded(coord->node)); + trace_stamp(TRACE_TREE); + + coord_set_iplug((coord_t *) coord, + node_plugin_by_node(coord->node)->plugin_by_coord(coord)); + assert("nikita-2479", + coord_iplug(coord) == node_plugin_by_node(coord->node)->plugin_by_coord(coord)); +} + +/* return type of item at @coord */ +reiser4_internal item_type_id +item_type_by_coord(const coord_t * coord /* coord to query */ ) +{ + assert("nikita-333", coord != NULL); + assert("nikita-334", coord->node != NULL); + assert("nikita-335", znode_is_loaded(coord->node)); + assert("nikita-336", item_plugin_by_coord(coord) != NULL); + + trace_stamp(TRACE_TREE); + + return item_plugin_by_coord(coord)->b.item_type; +} + +/* return id of item */ +/* Audited by: green(2002.06.15) */ +reiser4_internal item_id +item_id_by_coord(const coord_t * coord /* coord to query */ ) +{ + assert("vs-539", coord != NULL); + assert("vs-538", coord->node != NULL); + assert("vs-537", znode_is_loaded(coord->node)); + assert("vs-536", item_plugin_by_coord(coord) != NULL); + + trace_stamp(TRACE_TREE); + + assert("vs-540", item_id_by_plugin(item_plugin_by_coord(coord)) < LAST_ITEM_ID); + return item_id_by_plugin(item_plugin_by_coord(coord)); +} + +/* return key of item at @coord */ +/* Audited by: green(2002.06.15) */ +reiser4_internal reiser4_key * +item_key_by_coord(const coord_t * coord /* coord to query */ , + reiser4_key * key /* result */ ) +{ + assert("nikita-338", coord != NULL); + assert("nikita-339", coord->node != NULL); + assert("nikita-340", znode_is_loaded(coord->node)); + trace_stamp(TRACE_TREE); + + return node_plugin_by_node(coord->node)->key_at(coord, key); +} + +/* this returns max key in the item */ +reiser4_internal reiser4_key * +max_item_key_by_coord(const coord_t *coord /* coord to query */ , + reiser4_key *key /* result */ ) +{ + coord_t last; + + assert("nikita-338", coord != NULL); + assert("nikita-339", coord->node != NULL); + assert("nikita-340", znode_is_loaded(coord->node)); + trace_stamp(TRACE_TREE); + + /* make coord pointing to last item's unit */ + coord_dup(&last, coord); + last.unit_pos = coord_num_units(&last) - 1; + assert("vs-1560", coord_is_existing_unit(&last)); + + max_unit_key_by_coord(&last, key); + return key; +} + +/* return key of unit at @coord */ +reiser4_internal reiser4_key * +unit_key_by_coord(const coord_t * coord /* coord to query */ , + reiser4_key * key /* result */ ) +{ + assert("nikita-772", coord != NULL); + assert("nikita-774", coord->node != NULL); + assert("nikita-775", znode_is_loaded(coord->node)); + trace_stamp(TRACE_TREE); + + if (item_plugin_by_coord(coord)->b.unit_key != NULL) + return item_plugin_by_coord(coord)->b.unit_key(coord, key); + else + return item_key_by_coord(coord, key); +} + +/* return the biggest key contained the unit @coord */ +reiser4_internal reiser4_key * +max_unit_key_by_coord(const coord_t * coord /* coord to query */ , + reiser4_key * key /* result */ ) +{ + assert("nikita-772", coord != NULL); + assert("nikita-774", coord->node != NULL); + assert("nikita-775", znode_is_loaded(coord->node)); + trace_stamp(TRACE_TREE); + + if (item_plugin_by_coord(coord)->b.max_unit_key != NULL) + return item_plugin_by_coord(coord)->b.max_unit_key(coord, key); + else + return unit_key_by_coord(coord, key); +} + + +/* ->max_key_inside() method for items consisting of exactly one key (like + stat-data) */ +static reiser4_key * +max_key_inside_single_key(const coord_t * coord /* coord of item */ , + reiser4_key * result /* resulting key */) +{ + assert("nikita-604", coord != NULL); + + /* coord -> key is starting key of this item and it has to be already + filled in */ + return unit_key_by_coord(coord, result); +} + +/* ->nr_units() method for items consisting of exactly one unit always */ +static pos_in_node_t +nr_units_single_unit(const coord_t * coord UNUSED_ARG /* coord of item */ ) +{ + return 1; +} + +static int +paste_no_paste(coord_t * coord UNUSED_ARG, + reiser4_item_data * data UNUSED_ARG, + carry_plugin_info * info UNUSED_ARG) +{ + return 0; +} + +/* default ->fast_paste() method */ +reiser4_internal int +agree_to_fast_op(const coord_t * coord UNUSED_ARG /* coord of item */ ) +{ + return 1; +} + +reiser4_internal int +item_can_contain_key(const coord_t * item /* coord of item */ , + const reiser4_key * key /* key to check */ , + const reiser4_item_data * data /* parameters of item + * being created */ ) +{ + item_plugin *iplug; + reiser4_key min_key_in_item; + reiser4_key max_key_in_item; + + assert("nikita-1658", item != NULL); + assert("nikita-1659", key != NULL); + + iplug = item_plugin_by_coord(item); + if (iplug->b.can_contain_key != NULL) + return iplug->b.can_contain_key(item, key, data); + else { + assert("nikita-1681", iplug->b.max_key_inside != NULL); + item_key_by_coord(item, &min_key_in_item); + iplug->b.max_key_inside(item, &max_key_in_item); + + /* can contain key if + min_key_in_item <= key && + key <= max_key_in_item + */ + return keyle(&min_key_in_item, key) && keyle(key, &max_key_in_item); + } +} + +/* return 0 if @item1 and @item2 are not mergeable, !0 - otherwise */ +reiser4_internal int +are_items_mergeable(const coord_t * i1 /* coord of first item */ , + const coord_t * i2 /* coord of second item */ ) +{ + item_plugin *iplug; + reiser4_key k1; + reiser4_key k2; + + assert("nikita-1336", i1 != NULL); + assert("nikita-1337", i2 != NULL); + + iplug = item_plugin_by_coord(i1); + assert("nikita-1338", iplug != NULL); + + IF_TRACE(TRACE_NODES, print_key("k1", item_key_by_coord(i1, &k1))); + IF_TRACE(TRACE_NODES, print_key("k2", item_key_by_coord(i2, &k2))); + + /* NOTE-NIKITA are_items_mergeable() is also called by assertions in + shifting code when nodes are in "suspended" state. */ + assert("nikita-1663", keyle(item_key_by_coord(i1, &k1), item_key_by_coord(i2, &k2))); + + if (iplug->b.mergeable != NULL) { + return iplug->b.mergeable(i1, i2); + } else if (iplug->b.max_key_inside != NULL) { + iplug->b.max_key_inside(i1, &k1); + item_key_by_coord(i2, &k2); + + /* mergeable if ->max_key_inside() >= key of i2; */ + return keyge(iplug->b.max_key_inside(i1, &k1), item_key_by_coord(i2, &k2)); + } else { + item_key_by_coord(i1, &k1); + item_key_by_coord(i2, &k2); + + return + (get_key_locality(&k1) == get_key_locality(&k2)) && + (get_key_objectid(&k1) == get_key_objectid(&k2)) && (iplug == item_plugin_by_coord(i2)); + } +} + +reiser4_internal int +item_is_extent(const coord_t * item) +{ + assert("vs-482", coord_is_existing_item(item)); + return item_id_by_coord(item) == EXTENT_POINTER_ID; +} + +reiser4_internal int +item_is_tail(const coord_t * item) +{ + assert("vs-482", coord_is_existing_item(item)); + return item_id_by_coord(item) == FORMATTING_ID; +} + +reiser4_internal int +item_is_statdata(const coord_t * item) +{ + assert("vs-516", coord_is_existing_item(item)); + return item_type_by_coord(item) == STAT_DATA_ITEM_TYPE; +} + +static int +change_item(struct inode * inode, reiser4_plugin * plugin) +{ + /* cannot change constituent item (sd, or dir_item) */ + return RETERR(-EINVAL); +} + +static reiser4_plugin_ops item_plugin_ops = { + .init = NULL, + .load = NULL, + .save_len = NULL, + .save = NULL, + .change = change_item +}; + + +item_plugin item_plugins[LAST_ITEM_ID] = { + [STATIC_STAT_DATA_ID] = { + .h = { + .type_id = REISER4_ITEM_PLUGIN_TYPE, + .id = STATIC_STAT_DATA_ID, + .pops = &item_plugin_ops, + .label = "sd", + .desc = "stat-data", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .b = { + .item_type = STAT_DATA_ITEM_TYPE, + .max_key_inside = max_key_inside_single_key, + .can_contain_key = NULL, + .mergeable = NULL, + .nr_units = nr_units_single_unit, + .lookup = NULL, + .init = NULL, + .paste = paste_no_paste, + .fast_paste = NULL, + .can_shift = NULL, + .copy_units = NULL, + .create_hook = NULL, + .kill_hook = NULL, + .shift_hook = NULL, + .cut_units = NULL, + .kill_units = NULL, + .unit_key = NULL, + .max_unit_key = NULL, + .estimate = NULL, + .item_data_by_flow = NULL, +#if REISER4_DEBUG_OUTPUT + .print = print_sd, + .item_stat = item_stat_static_sd, +#endif +#if REISER4_DEBUG + .check = NULL +#endif + }, + .f = { + .utmost_child = NULL, + .utmost_child_real_block = NULL, + .update = NULL, + .scan = NULL, + .squeeze = NULL + }, + .s = { + .sd = { + .init_inode = init_inode_static_sd, + .save_len = save_len_static_sd, + .save = save_static_sd + } + } + }, + [SIMPLE_DIR_ENTRY_ID] = { + .h = { + .type_id = REISER4_ITEM_PLUGIN_TYPE, + .id = SIMPLE_DIR_ENTRY_ID, + .pops = &item_plugin_ops, + .label = "de", + .desc = "directory entry", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .b = { + .item_type = DIR_ENTRY_ITEM_TYPE, + .max_key_inside = max_key_inside_single_key, + .can_contain_key = NULL, + .mergeable = NULL, + .nr_units = nr_units_single_unit, + .lookup = NULL, + .init = NULL, + .paste = NULL, + .fast_paste = NULL, + .can_shift = NULL, + .copy_units = NULL, + .create_hook = NULL, + .kill_hook = NULL, + .shift_hook = NULL, + .cut_units = NULL, + .kill_units = NULL, + .unit_key = NULL, + .max_unit_key = NULL, + .estimate = NULL, + .item_data_by_flow = NULL, +#if REISER4_DEBUG_OUTPUT + .print = print_de, + .item_stat = NULL, +#endif +#if REISER4_DEBUG + .check = NULL +#endif + }, + .f = { + .utmost_child = NULL, + .utmost_child_real_block = NULL, + .update = NULL, + .scan = NULL, + .squeeze = NULL + }, + .s = { + .dir = { + .extract_key = extract_key_de, + .update_key = update_key_de, + .extract_name = extract_name_de, + .extract_file_type = extract_file_type_de, + .add_entry = add_entry_de, + .rem_entry = rem_entry_de, + .max_name_len = max_name_len_de + } + } + }, + [COMPOUND_DIR_ID] = { + .h = { + .type_id = REISER4_ITEM_PLUGIN_TYPE, + .id = COMPOUND_DIR_ID, + .pops = &item_plugin_ops, + .label = "cde", + .desc = "compressed directory entry", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .b = { + .item_type = DIR_ENTRY_ITEM_TYPE, + .max_key_inside = max_key_inside_cde, + .can_contain_key = can_contain_key_cde, + .mergeable = mergeable_cde, + .nr_units = nr_units_cde, + .lookup = lookup_cde, + .init = init_cde, + .paste = paste_cde, + .fast_paste = agree_to_fast_op, + .can_shift = can_shift_cde, + .copy_units = copy_units_cde, + .create_hook = NULL, + .kill_hook = NULL, + .shift_hook = NULL, + .cut_units = cut_units_cde, + .kill_units = kill_units_cde, + .unit_key = unit_key_cde, + .max_unit_key = unit_key_cde, + .estimate = estimate_cde, + .item_data_by_flow = NULL +#if REISER4_DEBUG_OUTPUT + , .print = print_cde, + .item_stat = NULL +#endif +#if REISER4_DEBUG + , .check = check_cde +#endif + }, + .f = { + .utmost_child = NULL, + .utmost_child_real_block = NULL, + .update = NULL, + .scan = NULL, + .squeeze = NULL + }, + .s = { + .dir = { + .extract_key = extract_key_cde, + .update_key = update_key_cde, + .extract_name = extract_name_cde, + .extract_file_type = extract_file_type_de, + .add_entry = add_entry_cde, + .rem_entry = rem_entry_cde, + .max_name_len = max_name_len_cde + } + } + }, + [NODE_POINTER_ID] = { + .h = { + .type_id = REISER4_ITEM_PLUGIN_TYPE, + .id = NODE_POINTER_ID, + .pops = NULL, + .label = "internal", + .desc = "internal item", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .b = { + .item_type = INTERNAL_ITEM_TYPE, + .max_key_inside = NULL, + .can_contain_key = NULL, + .mergeable = mergeable_internal, + .nr_units = nr_units_single_unit, + .lookup = lookup_internal, + .init = NULL, + .paste = NULL, + .fast_paste = NULL, + .can_shift = NULL, + .copy_units = NULL, + .create_hook = create_hook_internal, + .kill_hook = kill_hook_internal, + .shift_hook = shift_hook_internal, + .cut_units = NULL, + .kill_units = NULL, + .unit_key = NULL, + .max_unit_key = NULL, + .estimate = NULL, + .item_data_by_flow = NULL +#if REISER4_DEBUG_OUTPUT + , .print = print_internal, + .item_stat = NULL +#endif +#if REISER4_DEBUG + , .check = check__internal +#endif + }, + .f = { + .utmost_child = utmost_child_internal, + .utmost_child_real_block = utmost_child_real_block_internal, + .update = update_internal, + .scan = NULL, + .squeeze = NULL + }, + .s = { + .internal = { + .down_link = down_link_internal, + .has_pointer_to = has_pointer_to_internal + } + } + }, + [EXTENT_POINTER_ID] = { + .h = { + .type_id = REISER4_ITEM_PLUGIN_TYPE, + .id = EXTENT_POINTER_ID, + .pops = NULL, + .label = "extent", + .desc = "extent item", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .b = { + .item_type = UNIX_FILE_METADATA_ITEM_TYPE, + .max_key_inside = max_key_inside_extent, + .can_contain_key = can_contain_key_extent, + .mergeable = mergeable_extent, + .nr_units = nr_units_extent, + .lookup = lookup_extent, + .init = NULL, + .paste = paste_extent, + .fast_paste = agree_to_fast_op, + .can_shift = can_shift_extent, + .create_hook = create_hook_extent, + .copy_units = copy_units_extent, + .kill_hook = kill_hook_extent, + .shift_hook = NULL, + .cut_units = cut_units_extent, + .kill_units = kill_units_extent, + .unit_key = unit_key_extent, + .max_unit_key = max_unit_key_extent, + .estimate = NULL, + .item_data_by_flow = NULL, + .show = show_extent, +#if REISER4_DEBUG_OUTPUT + .print = print_extent, + .item_stat = item_stat_extent, +#endif +#if REISER4_DEBUG + .check = check_extent +#endif + }, + .f = { + .utmost_child = utmost_child_extent, + .utmost_child_real_block = utmost_child_real_block_extent, + .update = NULL, + .scan = scan_extent, + .squeeze = NULL, + .key_by_offset = key_by_offset_extent + }, + .s = { + .file = { + .write = write_extent, + .read = read_extent, + .readpage = readpage_extent, + .capture = capture_extent, + .get_block = get_block_address_extent, + .readpages = readpages_extent, + .append_key = append_key_extent, + .init_coord_extension = init_coord_extension_extent + } + } + }, + [FORMATTING_ID] = { + .h = { + .type_id = REISER4_ITEM_PLUGIN_TYPE, + .id = FORMATTING_ID, + .pops = NULL, + .label = "body", + .desc = "body (or tail?) item", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .b = { + .item_type = UNIX_FILE_METADATA_ITEM_TYPE, + .max_key_inside = max_key_inside_tail, + .can_contain_key = can_contain_key_tail, + .mergeable = mergeable_tail, + .nr_units = nr_units_tail, + .lookup = lookup_tail, + .init = NULL, + .paste = paste_tail, + .fast_paste = agree_to_fast_op, + .can_shift = can_shift_tail, + .create_hook = NULL, + .copy_units = copy_units_tail, + .kill_hook = kill_hook_tail, + .shift_hook = NULL, + .cut_units = cut_units_tail, + .kill_units = kill_units_tail, + .unit_key = unit_key_tail, + .max_unit_key = unit_key_tail, + .estimate = NULL, + .item_data_by_flow = NULL, + .show = show_tail, +#if REISER4_DEBUG_OUTPUT + .print = NULL, + .item_stat = NULL, +#endif +#if REISER4_DEBUG + .check = NULL +#endif + }, + .f = { + .utmost_child = NULL, + .utmost_child_real_block = NULL, + .update = NULL, + .scan = NULL, + .squeeze = NULL + }, + .s = { + .file = { + .write = write_tail, + .read = read_tail, + .readpage = readpage_tail, + .capture = NULL, + .get_block = NULL, + .readpages = NULL, + .append_key = append_key_tail, + .init_coord_extension = init_coord_extension_tail + } + } + }, + [CTAIL_ID] = { + .h = { + .type_id = REISER4_ITEM_PLUGIN_TYPE, + .id = CTAIL_ID, + .pops = NULL, + .label = "ctail", + .desc = "cryptcompress tail item", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .b = { + .item_type = UNIX_FILE_METADATA_ITEM_TYPE, + .max_key_inside = max_key_inside_tail, + .can_contain_key = can_contain_key_ctail, + .mergeable = mergeable_ctail, + .nr_units = nr_units_ctail, + .lookup = NULL, + .init = init_ctail, + .paste = paste_ctail, + .fast_paste = agree_to_fast_op, + .can_shift = can_shift_ctail, + .create_hook = NULL, + .copy_units = copy_units_ctail, + .kill_hook = kill_hook_ctail, + .shift_hook = shift_hook_ctail, + .cut_units = cut_units_ctail, + .kill_units = kill_units_ctail, + .unit_key = unit_key_tail, + .max_unit_key = unit_key_tail, + .estimate = estimate_ctail, + .item_data_by_flow = NULL +#if REISER4_DEBUG_OUTPUT + , .print = print_ctail, + .item_stat = NULL +#endif +#if REISER4_DEBUG + , .check = NULL +#endif + }, + .f = { + .utmost_child = utmost_child_ctail, + /* FIXME-EDWARD: write this */ + .utmost_child_real_block = NULL, + .update = NULL, + .scan = scan_ctail, + .squeeze = squeeze_ctail + }, + .s = { + .file = { + .write = NULL, + .read = read_ctail, + .readpage = readpage_ctail, + .capture = NULL, + .get_block = get_block_address_tail, + .readpages = readpages_ctail, + .append_key = append_key_ctail, + .init_coord_extension = init_coord_extension_tail + } + } + }, + [BLACK_BOX_ID] = { + .h = { + .type_id = REISER4_ITEM_PLUGIN_TYPE, + .id = BLACK_BOX_ID, + .pops = NULL, + .label = "blackbox", + .desc = "black box item", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .b = { + .item_type = OTHER_ITEM_TYPE, + .max_key_inside = NULL, + .can_contain_key = NULL, + .mergeable = NULL, + .nr_units = nr_units_single_unit, + /* to need for ->lookup method */ + .lookup = NULL, + .init = NULL, + .paste = NULL, + .fast_paste = NULL, + .can_shift = NULL, + .copy_units = NULL, + .create_hook = NULL, + .kill_hook = NULL, + .shift_hook = NULL, + .cut_units = NULL, + .kill_units = NULL, + .unit_key = NULL, + .max_unit_key = NULL, + .estimate = NULL, + .item_data_by_flow = NULL, +#if REISER4_DEBUG_OUTPUT + .print = NULL, + .item_stat = NULL, +#endif +#if REISER4_DEBUG + .check = NULL +#endif + } + } +}; + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/item/item.h 2004-09-03 00:57:05.275237304 -0700 @@ -0,0 +1,396 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* first read balance.c comments before reading this */ + +/* An item_plugin implements all of the operations required for + balancing that are item specific. */ + +/* an item plugin also implements other operations that are specific to that + item. These go into the item specific operations portion of the item + handler, and all of the item specific portions of the item handler are put + into a union. */ + +#if !defined( __REISER4_ITEM_H__ ) +#define __REISER4_ITEM_H__ + +#include "../../forward.h" +#include "../plugin_header.h" +#include "../../dformat.h" +#include "../../seal.h" +#include "../../plugin/file/file.h" + +#include /* for struct file, struct inode */ +#include /* for struct page */ +#include /* for struct dentry */ + +typedef enum { + STAT_DATA_ITEM_TYPE, + DIR_ENTRY_ITEM_TYPE, + INTERNAL_ITEM_TYPE, + UNIX_FILE_METADATA_ITEM_TYPE, + OTHER_ITEM_TYPE +} item_type_id; + + +/* this is the part of each item plugin that all items are expected to + support or at least explicitly fail to support by setting the + pointer to null. */ +typedef struct { + item_type_id item_type; + + /* operations called by balancing + + It is interesting to consider that some of these item + operations could be given sources or targets that are not + really items in nodes. This could be ok/useful. + + */ + /* maximal key that can _possibly_ be occupied by this item + + When inserting, and node ->lookup() method (called by + coord_by_key()) reaches an item after binary search, + the ->max_key_inside() item plugin method is used to determine + whether new item should pasted into existing item + (new_key<=max_key_inside()) or new item has to be created + (new_key>max_key_inside()). + + For items that occupy exactly one key (like stat-data) + this method should return this key. For items that can + grow indefinitely (extent, directory item) this should + return max_key(). + + For example extent with the key + + (LOCALITY,4,OBJID,STARTING-OFFSET), and length BLK blocks, + + ->max_key_inside is (LOCALITY,4,OBJID,0xffffffffffffffff), and + */ + reiser4_key *(*max_key_inside) (const coord_t *, reiser4_key *); + + /* true if item @coord can merge data at @key. */ + int (*can_contain_key) (const coord_t *, const reiser4_key *, const reiser4_item_data *); + /* mergeable() - check items for mergeability + + Optional method. Returns true if two items can be merged. + + */ + int (*mergeable) (const coord_t *, const coord_t *); + + /* number of atomic things in an item */ + pos_in_node_t (*nr_units) (const coord_t *); + + /* search within item for a unit within the item, and return a + pointer to it. This can be used to calculate how many + bytes to shrink an item if you use pointer arithmetic and + compare to the start of the item body if the item's data + are continuous in the node, if the item's data are not + continuous in the node, all sorts of other things are maybe + going to break as well. */ + lookup_result(*lookup) (const reiser4_key *, lookup_bias, coord_t *); + /* method called by ode_plugin->create_item() to initialise new + item */ + int (*init) (coord_t * target, coord_t * from, reiser4_item_data * data); + /* method called (e.g., by resize_item()) to place new data into + item when it grows*/ + int (*paste) (coord_t *, reiser4_item_data *, carry_plugin_info *); + /* return true if paste into @coord is allowed to skip + carry. That is, if such paste would require any changes + at the parent level + */ + int (*fast_paste) (const coord_t *); + /* how many but not more than @want units of @source can be + shifted into @target node. If pend == append - we try to + append last item of @target by first units of @source. If + pend == prepend - we try to "prepend" first item in @target + by last units of @source. @target node has @free_space + bytes of free space. Total size of those units are returned + via @size. + + @target is not NULL if shifting to the mergeable item and + NULL is new item will be created during shifting. + */ + int (*can_shift) (unsigned free_space, coord_t *, + znode *, shift_direction, unsigned *size, unsigned want); + + /* starting off @from-th unit of item @source append or + prepend @count units to @target. @target has been already + expanded by @free_space bytes. That must be exactly what is + needed for those items in @target. If @where_is_free_space + == SHIFT_LEFT - free space is at the end of @target item, + othersize - it is in the beginning of it. */ + void (*copy_units) (coord_t *, coord_t *, + unsigned from, unsigned count, shift_direction where_is_free_space, unsigned free_space); + + int (*create_hook) (const coord_t *, void *); + /* do whatever is necessary to do when @count units starting + from @from-th one are removed from the tree */ + /* FIXME-VS: this is used to be here for, in particular, + extents and items of internal type to free blocks they point + to at the same time with removing items from a + tree. Problems start, however, when dealloc_block fails due + to some reason. Item gets removed, but blocks it pointed to + are not freed. It is not clear how to fix this for items of + internal type because a need to remove internal item may + appear in the middle of balancing, and there is no way to + undo changes made. OTOH, if space allocator involves + balancing to perform dealloc_block - this will probably + break balancing due to deadlock issues + */ + int (*kill_hook) (const coord_t *, pos_in_node_t from, pos_in_node_t count, struct carry_kill_data *); + int (*shift_hook) (const coord_t *, unsigned from, unsigned count, znode *_node); + + /* unit @*from contains @from_key. unit @*to contains @to_key. Cut all keys between @from_key and @to_key + including boundaries. When units are cut from item beginning - move space which gets freed to head of + item. When units are cut from item end - move freed space to item end. When units are cut from the middle of + item - move freed space to item head. Return amount of space which got freed. Save smallest removed key in + @smallest_removed if it is not 0. Save new first item key in @new_first_key if it is not 0 + */ + int (*cut_units) (coord_t *, pos_in_node_t from, pos_in_node_t to, struct carry_cut_data *, + reiser4_key *smallest_removed, reiser4_key *new_first_key); + + /* like cut_units, except that these units are removed from the + tree, not only from a node */ + int (*kill_units) (coord_t *, pos_in_node_t from, pos_in_node_t to, struct carry_kill_data *, + reiser4_key *smallest_removed, reiser4_key *new_first); + + /* if @key_of_coord == 1 - returned key of coord, otherwise - + key of unit is returned. If @coord is not set to certain + unit - ERR_PTR(-ENOENT) is returned */ + reiser4_key *(*unit_key) (const coord_t *, reiser4_key *); + reiser4_key *(*max_unit_key) (const coord_t *, reiser4_key *); + /* estimate how much space is needed for paste @data into item at + @coord. if @coord==0 - estimate insertion, otherwise - estimate + pasting + */ + int (*estimate) (const coord_t *, const reiser4_item_data *); + + /* converts flow @f to item data. @coord == 0 on insert */ + int (*item_data_by_flow) (const coord_t *, const flow_t *, reiser4_item_data *); + + void (*show) (struct seq_file *, coord_t *); + +#if REISER4_DEBUG_OUTPUT + /* used for debugging only, prints an ascii description of the + item contents */ + void (*print) (const char *, coord_t *); + /* gather statistics */ + void (*item_stat) (const coord_t *, void *); +#endif + +#if REISER4_DEBUG + /* used for debugging, every item should have here the most + complete possible check of the consistency of the item that + the inventor can construct */ + int (*check) (const coord_t *, const char **error); +#endif + +} balance_ops; + +typedef struct { + /* return the right or left child of @coord, only if it is in memory */ + int (*utmost_child) (const coord_t *, sideof side, jnode ** child); + + /* return whether the right or left child of @coord has a non-fake + block number. */ + int (*utmost_child_real_block) (const coord_t *, sideof side, reiser4_block_nr *); + /* relocate child at @coord to the @block */ + void (*update) (const coord_t *, const reiser4_block_nr *); + /* count unformatted nodes per item for leave relocation policy, etc.. */ + int (*scan) (flush_scan * scan); + /* squeeze by unformatted child */ + int (*squeeze) (flush_pos_t * pos); + /* backward mapping from jnode offset to a key. */ + int (*key_by_offset) (struct inode *, loff_t, reiser4_key *); +} flush_ops; + +/* operations specific to the directory item */ +typedef struct { + /* extract stat-data key from directory entry at @coord and place it + into @key. */ + int (*extract_key) (const coord_t *, reiser4_key * key); + /* update object key in item. */ + int (*update_key) (const coord_t *, const reiser4_key *, lock_handle *); + /* extract name from directory entry at @coord and return it */ + char *(*extract_name) (const coord_t *, char *buf); + /* extract file type (DT_* stuff) from directory entry at @coord and + return it */ + unsigned (*extract_file_type) (const coord_t *); + int (*add_entry) (struct inode *dir, + coord_t *, lock_handle *, + const struct dentry *name, reiser4_dir_entry_desc *entry); + int (*rem_entry) (struct inode *dir, const struct qstr *name, + coord_t *, lock_handle *, + reiser4_dir_entry_desc *entry); + int (*max_name_len) (const struct inode *dir); +} dir_entry_ops; + +/* operations specific to items regular (unix) file metadata are built of */ +typedef struct { + int (*write)(struct inode *, flow_t *, hint_t *, int grabbed, write_mode_t); + int (*read)(struct file *, flow_t *, hint_t *); + int (*readpage) (void *, struct page *); + int (*capture) (reiser4_key *, uf_coord_t *, struct page *, write_mode_t); + int (*get_block) (const coord_t *, sector_t, struct buffer_head *); + void (*readpages) (void *, struct address_space *, struct list_head *pages); + /* key of first byte which is not addressed by the item @coord is set to + For example extent with the key + + (LOCALITY,4,OBJID,STARTING-OFFSET), and length BLK blocks, + + ->append_key is + + (LOCALITY,4,OBJID,STARTING-OFFSET + BLK * block_size) */ + /* FIXME: could be uf_coord also */ + reiser4_key *(*append_key) (const coord_t *, reiser4_key *); + + void (*init_coord_extension)(uf_coord_t *, loff_t); +} file_ops; + +/* operations specific to items of stat data type */ +typedef struct { + int (*init_inode) (struct inode * inode, char *sd, int len); + int (*save_len) (struct inode * inode); + int (*save) (struct inode * inode, char **area); +} sd_ops; + +/* operations specific to internal item */ +typedef struct { + /* all tree traversal want to know from internal item is where + to go next. */ + void (*down_link) (const coord_t * coord, + const reiser4_key * key, reiser4_block_nr * block); + /* check that given internal item contains given pointer. */ + int (*has_pointer_to) (const coord_t * coord, + const reiser4_block_nr * block); +} internal_item_ops; + +struct item_plugin { + /* generic fields */ + plugin_header h; + + /* methods common for all item types */ + balance_ops b; + /* methods used during flush */ + flush_ops f; + + /* methods specific to particular type of item */ + union { + dir_entry_ops dir; + file_ops file; + sd_ops sd; + internal_item_ops internal; + } s; + +}; + +static inline item_id +item_id_by_plugin(item_plugin * plugin) +{ + return plugin->h.id; +} + +static inline char +get_iplugid(item_plugin *iplug) +{ + assert("nikita-2838", iplug != NULL); + assert("nikita-2839", 0 <= iplug->h.id && iplug->h.id < 0xff); + return (char)item_id_by_plugin(iplug); +} + +extern unsigned long znode_times_locked(const znode *z); + +static inline void +coord_set_iplug(coord_t * coord, item_plugin *iplug) +{ + assert("nikita-2837", coord != NULL); + assert("nikita-2838", iplug != NULL); + coord->iplugid = get_iplugid(iplug); + ON_DEBUG(coord->plug_v = znode_times_locked(coord->node)); +} + +static inline item_plugin * +coord_iplug(const coord_t * coord) +{ + assert("nikita-2833", coord != NULL); + assert("nikita-2834", coord->iplugid != INVALID_PLUGID); + assert("nikita-3549", coord->plug_v == znode_times_locked(coord->node)); + return (item_plugin *)plugin_by_id(REISER4_ITEM_PLUGIN_TYPE, + coord->iplugid); +} + +extern int item_can_contain_key(const coord_t * item, const reiser4_key * key, const reiser4_item_data *); +extern int are_items_mergeable(const coord_t * i1, const coord_t * i2); +extern int item_is_extent(const coord_t *); +extern int item_is_tail(const coord_t *); +extern int item_is_statdata(const coord_t * item); + +extern pos_in_node_t item_length_by_coord(const coord_t * coord); +extern item_type_id item_type_by_coord(const coord_t * coord); +extern item_id item_id_by_coord(const coord_t * coord /* coord to query */ ); +extern reiser4_key *item_key_by_coord(const coord_t * coord, reiser4_key * key); +extern reiser4_key *max_item_key_by_coord(const coord_t *, reiser4_key *); +extern reiser4_key *unit_key_by_coord(const coord_t * coord, reiser4_key * key); +extern reiser4_key *max_unit_key_by_coord(const coord_t * coord, reiser4_key * key); + +extern void obtain_item_plugin(const coord_t * coord); + +#if defined(REISER4_DEBUG) || defined(REISER4_DEBUG_MODIFY) || defined(REISER4_DEBUG_OUTPUT) +extern int znode_is_loaded(const znode * node); +#endif + +/* return plugin of item at @coord */ +static inline item_plugin * +item_plugin_by_coord(const coord_t * coord /* coord to query */ ) +{ + assert("nikita-330", coord != NULL); + assert("nikita-331", coord->node != NULL); + assert("nikita-332", znode_is_loaded(coord->node)); + trace_stamp(TRACE_TREE); + + if (unlikely(!coord_is_iplug_set(coord))) + obtain_item_plugin(coord); + return coord_iplug(coord); +} + +/* this returns true if item is of internal type */ +static inline int +item_is_internal(const coord_t * item) +{ + assert("vs-483", coord_is_existing_item(item)); + return item_type_by_coord(item) == INTERNAL_ITEM_TYPE; +} + +extern void item_body_by_coord_hard(coord_t * coord); +extern void *item_body_by_coord_easy(const coord_t * coord); +#if REISER4_DEBUG +extern int item_body_is_valid(const coord_t * coord); +#endif + +/* return pointer to item body */ +static inline void * +item_body_by_coord(const coord_t * coord /* coord to query */ ) +{ + assert("nikita-324", coord != NULL); + assert("nikita-325", coord->node != NULL); + assert("nikita-326", znode_is_loaded(coord->node)); + trace_stamp(TRACE_TREE); + + if (coord->offset == INVALID_OFFSET) + item_body_by_coord_hard((coord_t *)coord); + assert("nikita-3201", item_body_is_valid(coord)); + assert("nikita-3550", coord->body_v == znode_times_locked(coord->node)); + return item_body_by_coord_easy(coord); +} + +/* __REISER4_ITEM_H__ */ +#endif +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/item/sde.c 2004-09-03 00:57:05.276237152 -0700 @@ -0,0 +1,213 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Directory entry implementation */ +#include "../../forward.h" +#include "../../debug.h" +#include "../../dformat.h" +#include "../../kassign.h" +#include "../../coord.h" +#include "sde.h" +#include "item.h" +#include "../plugin.h" +#include "../../znode.h" +#include "../../carry.h" +#include "../../tree.h" +#include "../../inode.h" + +#include /* for struct inode */ +#include /* for struct dentry */ +#include + +#if REISER4_DEBUG_OUTPUT +reiser4_internal void +print_de(const char *prefix /* prefix to print */ , + coord_t * coord /* item to print */ ) +{ + assert("nikita-1456", prefix != NULL); + assert("nikita-1457", coord != NULL); + + if (item_length_by_coord(coord) < (int) sizeof (directory_entry_format)) { + printk("%s: wrong size: %i < %i\n", prefix, item_length_by_coord(coord), sizeof (directory_entry_format)); + } else { + reiser4_key sdkey; + char *name; + char buf[DE_NAME_BUF_LEN]; + + extract_key_de(coord, &sdkey); + name = extract_name_de(coord, buf); + printk("%s: name: %s\n", prefix, name); + print_key("\tsdkey", &sdkey); + } +} +#endif + +/* ->extract_key() method of simple directory item plugin. */ +reiser4_internal int +extract_key_de(const coord_t * coord /* coord of item */ , + reiser4_key * key /* resulting key */ ) +{ + directory_entry_format *dent; + + assert("nikita-1458", coord != NULL); + assert("nikita-1459", key != NULL); + + dent = (directory_entry_format *) item_body_by_coord(coord); + assert("nikita-1158", item_length_by_coord(coord) >= (int) sizeof *dent); + return extract_key_from_id(&dent->id, key); +} + +reiser4_internal int +update_key_de(const coord_t * coord, const reiser4_key * key, lock_handle * lh UNUSED_ARG) +{ + directory_entry_format *dent; + obj_key_id obj_id; + int result; + + assert("nikita-2342", coord != NULL); + assert("nikita-2343", key != NULL); + + dent = (directory_entry_format *) item_body_by_coord(coord); + result = build_obj_key_id(key, &obj_id); + if (result == 0) { + dent->id = obj_id; + znode_make_dirty(coord->node); + } + return 0; +} + +reiser4_internal char * +extract_dent_name(const coord_t * coord, directory_entry_format *dent, char *buf) +{ + reiser4_key key; + + unit_key_by_coord(coord, &key); + if (get_key_type(&key) != KEY_FILE_NAME_MINOR) + print_address("oops", znode_get_block(coord->node)); + if (!is_longname_key(&key)) { + if (is_dot_key(&key)) + return (char *) "."; + else + return extract_name_from_key(&key, buf); + } else + return (char *) dent->name; +} + +/* ->extract_name() method of simple directory item plugin. */ +reiser4_internal char * +extract_name_de(const coord_t * coord /* coord of item */, char *buf) +{ + directory_entry_format *dent; + + assert("nikita-1460", coord != NULL); + + dent = (directory_entry_format *) item_body_by_coord(coord); + return extract_dent_name(coord, dent, buf); +} + +/* ->extract_file_type() method of simple directory item plugin. */ +reiser4_internal unsigned +extract_file_type_de(const coord_t * coord UNUSED_ARG /* coord of + * item */ ) +{ + assert("nikita-1764", coord != NULL); + /* we don't store file type in the directory entry yet. + + But see comments at kassign.h:obj_key_id + */ + return DT_UNKNOWN; +} + +reiser4_internal int +add_entry_de(struct inode *dir /* directory of item */ , + coord_t * coord /* coord of item */ , + lock_handle * lh /* insertion lock handle */ , + const struct dentry *de /* name to add */ , + reiser4_dir_entry_desc * entry /* parameters of new directory + * entry */ ) +{ + reiser4_item_data data; + directory_entry_format *dent; + int result; + const char *name; + int len; + int longname; + + name = de->d_name.name; + len = de->d_name.len; + assert("nikita-1163", strlen(name) == len); + + longname = is_longname(name, len); + + data.length = sizeof *dent; + if (longname) + data.length += len + 1; + data.data = NULL; + data.user = 0; + data.iplug = item_plugin_by_id(SIMPLE_DIR_ENTRY_ID); + + /* NOTE-NIKITA quota plugin */ + if (DQUOT_ALLOC_SPACE_NODIRTY(dir, data.length)) + return -EDQUOT; + + result = insert_by_coord(coord, &data, &entry->key, lh, 0 /*flags */ ); + if (result != 0) + return result; + + dent = (directory_entry_format *) item_body_by_coord(coord); + build_inode_key_id(entry->obj, &dent->id); + if (longname) { + xmemcpy(dent->name, name, len); + cputod8(0, &dent->name[len]); + } + return 0; +} + +reiser4_internal int +rem_entry_de(struct inode *dir /* directory of item */ , + const struct qstr * name UNUSED_ARG, + coord_t * coord /* coord of item */ , + lock_handle * lh UNUSED_ARG /* lock handle for + * removal */ , + reiser4_dir_entry_desc * entry UNUSED_ARG /* parameters of + * directory entry + * being removed */ ) +{ + coord_t shadow; + int result; + int length; + + length = item_length_by_coord(coord); + if (inode_get_bytes(dir) < length) { + warning("nikita-2627", "Dir is broke: %llu: %llu", get_inode_oid(dir), inode_get_bytes(dir)); + return RETERR(-EIO); + } + + /* cut_node() is supposed to take pointers to _different_ + coords, because it will modify them without respect to + possible aliasing. To work around this, create temporary copy + of @coord. + */ + coord_dup(&shadow, coord); + result = kill_node_content(coord, &shadow, NULL, NULL, NULL, NULL, NULL); + if (result == 0) { + /* NOTE-NIKITA quota plugin */ + DQUOT_FREE_SPACE_NODIRTY(dir, length); + } + return result; +} + +reiser4_internal int +max_name_len_de(const struct inode *dir) +{ + return tree_by_inode(dir)->nplug->max_item_size() - sizeof (directory_entry_format) - 2; +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/item/sde.h 2004-09-03 00:57:05.276237152 -0700 @@ -0,0 +1,64 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Directory entry. */ + +#if !defined( __FS_REISER4_PLUGIN_DIRECTORY_ENTRY_H__ ) +#define __FS_REISER4_PLUGIN_DIRECTORY_ENTRY_H__ + +#include "../../forward.h" +#include "../../dformat.h" +#include "../../kassign.h" +#include "../../key.h" + +#include +#include /* for struct dentry */ + +typedef struct directory_entry_format { + /* key of object stat-data. It's not necessary to store whole + key here, because it's always key of stat-data, so minor + packing locality and offset can be omitted here. But this + relies on particular key allocation scheme for stat-data, so, + for extensibility sake, whole key can be stored here. + + We store key as array of bytes, because we don't want 8-byte + alignment of dir entries. + */ + obj_key_id id; + /* file name. Null terminated string. */ + d8 name[0]; +} directory_entry_format; + +void print_de(const char *prefix, coord_t * coord); +int extract_key_de(const coord_t * coord, reiser4_key * key); +int update_key_de(const coord_t * coord, const reiser4_key * key, lock_handle * lh); +char *extract_name_de(const coord_t * coord, char *buf); +unsigned extract_file_type_de(const coord_t * coord); +int add_entry_de(struct inode *dir, coord_t * coord, + lock_handle * lh, const struct dentry *name, reiser4_dir_entry_desc * entry); +int rem_entry_de(struct inode *dir, const struct qstr * name, coord_t * coord, lock_handle * lh, reiser4_dir_entry_desc * entry); +int max_name_len_de(const struct inode *dir); + + +int de_rem_and_shrink(struct inode *dir, coord_t * coord, int length); + +char *extract_dent_name(const coord_t * coord, + directory_entry_format *dent, char *buf); + +#if REISER4_LARGE_KEY +#define DE_NAME_BUF_LEN (24) +#else +#define DE_NAME_BUF_LEN (16) +#endif + +/* __FS_REISER4_PLUGIN_DIRECTORY_ENTRY_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/item/static_stat.c 2004-09-03 00:57:05.283236088 -0700 @@ -0,0 +1,1306 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* stat data manipulation. */ + +#include "../../forward.h" +#include "../../super.h" +#include "../../vfs_ops.h" +#include "../../inode.h" +#include "../../debug.h" +#include "../../dformat.h" +#include "../object.h" +#include "../plugin.h" +#include "../plugin_header.h" +#include "static_stat.h" +#include "item.h" + +#include +#include + +/* see static_stat.h for explanation */ + +/* helper function used while we are dumping/loading inode/plugin state + to/from the stat-data. */ + +static void +move_on(int *length /* space remaining in stat-data */ , + char **area /* current coord in stat data */ , + int size_of /* how many bytes to move forward */ ) +{ + assert("nikita-615", length != NULL); + assert("nikita-616", area != NULL); + + *length -= size_of; + *area += size_of; + + assert("nikita-617", *length >= 0); +} + +#if REISER4_DEBUG_OUTPUT +/* ->print() method of static sd item. Prints human readable information about + sd at @coord */ +reiser4_internal void +print_sd(const char *prefix /* prefix to print */ , + coord_t * coord /* coord of item */ ) +{ + char *sd; + int len; + int bit; + int chunk; + __u16 mask; + reiser4_stat_data_base *sd_base; + + assert("nikita-1254", prefix != NULL); + assert("nikita-1255", coord != NULL); + + sd = item_body_by_coord(coord); + len = item_length_by_coord(coord); + + sd_base = (reiser4_stat_data_base *) sd; + if (len < (int) sizeof *sd_base) { + printk("%s: wrong size: %i < %i\n", prefix, item_length_by_coord(coord), sizeof *sd_base); + return; + } + + mask = d16tocpu(&sd_base->extmask); + printk("%s: extmask: %x\n", prefix, mask); + + move_on(&len, &sd, sizeof *sd_base); + + for (bit = 0, chunk = 0; mask != 0; ++bit, mask >>= 1) { + if (((bit + 1) % 16) != 0) { + /* handle extension */ + sd_ext_plugin *sdplug; + + sdplug = sd_ext_plugin_by_id(bit); + if (sdplug == NULL) { + continue; + } + if ((mask & 1) && sdplug->print != NULL) { + /* alignment is not supported in node layout + plugin yet. + result = align( inode, &len, &sd, + sdplug -> alignment ); + if( result != 0 ) + return result; */ + sdplug->print(prefix, &sd, &len); + } + } else if (mask & 1) { + /* next portion of bitmask */ + if (len < (int) sizeof (d16)) { + warning("nikita-2708", "No space for bitmap"); + break; + } + mask = d16tocpu((d16 *) sd); + move_on(&len, &sd, sizeof (d16)); + ++chunk; + if (chunk == 3) { + if (!(mask & 0x8000)) { + /* clear last bit */ + mask &= ~0x8000; + continue; + } + /* too much */ + warning("nikita-2709", "Too many extensions"); + break; + } + } else + /* bitmask exhausted */ + break; + } +} +#endif + +reiser4_internal void +item_stat_static_sd(const coord_t * coord, void *vp) +{ + reiser4_stat_data_base *sd; + mode_t mode; + sd_stat *stat; + + stat = (sd_stat *) vp; + sd = (reiser4_stat_data_base *) item_body_by_coord(coord); + mode = 0; // d16tocpu( &sd -> mode ); + + if (S_ISREG(mode)) + stat->files++; + else if (S_ISDIR(mode)) + stat->dirs++; + else + stat->others++; +} + +/* helper function used while loading inode/plugin state from stat-data. + Complain if there is less space in stat-data than was expected. + Can only happen on disk corruption. */ +static int +not_enough_space(struct inode *inode /* object being processed */ , + const char *where /* error message */ ) +{ + assert("nikita-618", inode != NULL); + + warning("nikita-619", "Not enough space in %llu while loading %s", get_inode_oid(inode), where); + return RETERR(-EINVAL); +} + +/* helper function used while loading inode/plugin state from + stat-data. Call it if invalid plugin id was found. */ +static int +unknown_plugin(reiser4_plugin_id id /* invalid id */ , + struct inode *inode /* object being processed */ ) +{ + warning("nikita-620", "Unknown plugin %i in %llu", id, get_inode_oid(inode)); + return RETERR(-EINVAL); +} + +#if 0 /* Item alignment is not yet supported */ + +/* helper function used while storing/loading inode/plugin data to/from + stat-data. Move current coord in stat-data ("area") to position + aligned up to "alignment" bytes. */ +static int +align(struct inode *inode /* object being processed */ , + int *length /* space remaining in stat-data */ , + char **area /* current coord in stat data */ , + int alignment /* required alignment */ ) +{ + int delta; + + assert("nikita-621", inode != NULL); + assert("nikita-622", length != NULL); + assert("nikita-623", area != NULL); + assert("nikita-624", alignment > 0); + + delta = round_up(*area, alignment) - *area; + if (delta > *length) + return not_enough_space(inode, "padding"); + if (delta > 0) + move_on(length, area, delta); + return 0; +} + +#endif /* 0 */ + +/* this is installed as ->init_inode() method of + item_plugins[ STATIC_STAT_DATA_IT ] (fs/reiser4/plugin/item/item.c). + Copies data from on-disk stat-data format into inode. + Handles stat-data extensions. */ +/* was sd_load */ +reiser4_internal int +init_inode_static_sd(struct inode *inode /* object being processed */ , + char *sd /* stat-data body */ , + int len /* length of stat-data */ ) +{ + int result; + int bit; + int chunk; + __u16 mask; + __u64 bigmask; + reiser4_stat_data_base *sd_base; + reiser4_inode *state; + + assert("nikita-625", inode != NULL); + assert("nikita-626", sd != NULL); + + result = 0; + sd_base = (reiser4_stat_data_base *) sd; + state = reiser4_inode_data(inode); + mask = d16tocpu(&sd_base->extmask); + bigmask = mask; + inode_set_flag(inode, REISER4_SDLEN_KNOWN); + + move_on(&len, &sd, sizeof *sd_base); + for (bit = 0, chunk = 0; mask != 0 || bit <= LAST_IMPORTANT_SD_EXTENSION; ++bit, mask >>= 1) { + if (((bit + 1) % 16) != 0) { + /* handle extension */ + sd_ext_plugin *sdplug; + + sdplug = sd_ext_plugin_by_id(bit); + if (sdplug == NULL) { + warning("nikita-627", "No such extension %i in inode %llu", bit, get_inode_oid(inode)); + result = RETERR(-EINVAL); + break; + } + if (mask & 1) { + assert("nikita-628", sdplug->present); + /* alignment is not supported in node layout + plugin yet. + result = align( inode, &len, &sd, + sdplug -> alignment ); + if( result != 0 ) + return result; */ + result = sdplug->present(inode, &sd, &len); + } else if (sdplug->absent != NULL) + result = sdplug->absent(inode); + if (result) + break; + /* else, we are looking at the last bit in 16-bit + portion of bitmask */ + } else if (mask & 1) { + /* next portion of bitmask */ + if (len < (int) sizeof (d16)) { + warning("nikita-629", "No space for bitmap in inode %llu", get_inode_oid(inode)); + result = RETERR(-EINVAL); + break; + } + mask = d16tocpu((d16 *) sd); + bigmask <<= 16; + bigmask |= mask; + move_on(&len, &sd, sizeof (d16)); + ++chunk; + if (chunk == 3) { + if (!(mask & 0x8000)) { + /* clear last bit */ + mask &= ~0x8000; + continue; + } + /* too much */ + warning("nikita-630", "Too many extensions in %llu", get_inode_oid(inode)); + result = RETERR(-EINVAL); + break; + } + } else + /* bitmask exhausted */ + break; + } + state->extmask = bigmask; + /* common initialisations */ + inode->i_blksize = get_super_private(inode->i_sb)->optimal_io_size; + if (len - (sizeof (d16) * bit / 16) > 0) + /* alignment in save_len_static_sd() is taken into account + -edward */ + warning("nikita-631", "unused space in inode %llu", get_inode_oid(inode)); + return result; +} + +/* estimates size of stat-data required to store inode. + Installed as ->save_len() method of + item_plugins[ STATIC_STAT_DATA_IT ] (fs/reiser4/plugin/item/item.c). */ +/* was sd_len */ +reiser4_internal int +save_len_static_sd(struct inode *inode /* object being processed */ ) +{ + unsigned int result; + __u64 mask; + int bit; + + assert("nikita-632", inode != NULL); + + result = sizeof (reiser4_stat_data_base); + mask = reiser4_inode_data(inode)->extmask; + for (bit = 0; mask != 0; ++bit, mask >>= 1) { + if (mask & 1) { + sd_ext_plugin *sdplug; + + sdplug = sd_ext_plugin_by_id(bit); + assert("nikita-633", sdplug != NULL); + /* no aligment support + result += + round_up( result, sdplug -> alignment ) - result; */ + result += sdplug->save_len(inode); + } + } + result += sizeof (d16) * bit / 16; + return result; +} + +/* saves inode into stat-data. + Installed as ->save() method of + item_plugins[ STATIC_STAT_DATA_IT ] (fs/reiser4/plugin/item/item.c). */ +/* was sd_save */ +reiser4_internal int +save_static_sd(struct inode *inode /* object being processed */ , + char **area /* where to save stat-data */ ) +{ + int result; + __u64 emask; + int bit; + unsigned int len; + reiser4_stat_data_base *sd_base; + + assert("nikita-634", inode != NULL); + assert("nikita-635", area != NULL); + + result = 0; + emask = reiser4_inode_data(inode)->extmask; + sd_base = (reiser4_stat_data_base *) * area; + cputod16((unsigned) (emask & 0xffff), &sd_base->extmask); + + *area += sizeof *sd_base; + len = 0xffffffffu; + for (bit = 0; emask != 0; ++bit, emask >>= 1) { + if (emask & 1) { + if ((bit + 1) % 16 != 0) { + sd_ext_plugin *sdplug; + sdplug = sd_ext_plugin_by_id(bit); + assert("nikita-636", sdplug != NULL); + /* no alignment support yet + align( inode, &len, area, + sdplug -> alignment ); */ + result = sdplug->save(inode, area); + if (result) + break; + } else { + cputod16((unsigned) (emask & 0xffff), (d16 *) * area); + *area += sizeof (d16); + } + } + } + return result; +} + +/* stat-data extension handling functions. */ + +static int +present_lw_sd(struct inode *inode /* object being processed */ , + char **area /* position in stat-data */ , + int *len /* remaining length */ ) +{ + if (*len >= (int) sizeof (reiser4_light_weight_stat)) { + reiser4_light_weight_stat *sd_lw; + + sd_lw = (reiser4_light_weight_stat *) * area; + + inode->i_mode = d16tocpu(&sd_lw->mode); + inode->i_nlink = d32tocpu(&sd_lw->nlink); + inode->i_size = d64tocpu(&sd_lw->size); + if ((inode->i_mode & S_IFMT) == (S_IFREG | S_IFIFO)) { + inode->i_mode &= ~S_IFIFO; + inode_set_flag(inode, REISER4_PART_CONV); + } + move_on(len, area, sizeof *sd_lw); + return 0; + } else + return not_enough_space(inode, "lw sd"); +} + +static int +save_len_lw_sd(struct inode *inode UNUSED_ARG /* object being + * processed */ ) +{ + return sizeof (reiser4_light_weight_stat); +} + +static int +save_lw_sd(struct inode *inode /* object being processed */ , + char **area /* position in stat-data */ ) +{ + reiser4_light_weight_stat *sd; + mode_t delta; + + assert("nikita-2705", inode != NULL); + assert("nikita-2706", area != NULL); + assert("nikita-2707", *area != NULL); + + sd = (reiser4_light_weight_stat *) * area; + + delta = inode_get_flag(inode, REISER4_PART_CONV) ? S_IFIFO : 0; + cputod16(inode->i_mode | delta, &sd->mode); + cputod32(inode->i_nlink, &sd->nlink); + cputod64((__u64) inode->i_size, &sd->size); + *area += sizeof *sd; + return 0; +} + +#if REISER4_DEBUG_OUTPUT +static void +print_lw_sd(const char *prefix, char **area /* position in stat-data */ , + int *len /* remaining length */ ) +{ + reiser4_light_weight_stat *sd; + + sd = (reiser4_light_weight_stat *) * area; + printk("%s: mode: %o, nlink: %i, size: %llu\n", prefix, + d16tocpu(&sd->mode), d32tocpu(&sd->nlink), d64tocpu(&sd->size)); + move_on(len, area, sizeof *sd); +} +#endif + +static int +present_unix_sd(struct inode *inode /* object being processed */ , + char **area /* position in stat-data */ , + int *len /* remaining length */ ) +{ + assert("nikita-637", inode != NULL); + assert("nikita-638", area != NULL); + assert("nikita-639", *area != NULL); + assert("nikita-640", len != NULL); + assert("nikita-641", *len > 0); + + if (*len >= (int) sizeof (reiser4_unix_stat)) { + reiser4_unix_stat *sd; + + sd = (reiser4_unix_stat *) * area; + + inode->i_uid = d32tocpu(&sd->uid); + inode->i_gid = d32tocpu(&sd->gid); + inode->i_atime.tv_sec = d32tocpu(&sd->atime); + inode->i_mtime.tv_sec = d32tocpu(&sd->mtime); + inode->i_ctime.tv_sec = d32tocpu(&sd->ctime); + if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) + inode->i_rdev = d64tocpu(&sd->u.rdev); + else + inode_set_bytes(inode, (loff_t) d64tocpu(&sd->u.bytes)); + move_on(len, area, sizeof *sd); + return 0; + } else + return not_enough_space(inode, "unix sd"); +} + +static int +absent_unix_sd(struct inode *inode /* object being processed */ ) +{ + inode->i_uid = get_super_private(inode->i_sb)->default_uid; + inode->i_gid = get_super_private(inode->i_sb)->default_gid; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode_set_bytes(inode, inode->i_size); + /* mark inode as lightweight, so that caller (reiser4_lookup) will + complete initialisation by copying [ug]id from a parent. */ + inode_set_flag(inode, REISER4_LIGHT_WEIGHT); + return 0; +} + +/* Audited by: green(2002.06.14) */ +static int +save_len_unix_sd(struct inode *inode UNUSED_ARG /* object being + * processed */ ) +{ + return sizeof (reiser4_unix_stat); +} + +static int +save_unix_sd(struct inode *inode /* object being processed */ , + char **area /* position in stat-data */ ) +{ + reiser4_unix_stat *sd; + + assert("nikita-642", inode != NULL); + assert("nikita-643", area != NULL); + assert("nikita-644", *area != NULL); + + sd = (reiser4_unix_stat *) * area; + cputod32(inode->i_uid, &sd->uid); + cputod32(inode->i_gid, &sd->gid); + cputod32((__u32) inode->i_atime.tv_sec, &sd->atime); + cputod32((__u32) inode->i_ctime.tv_sec, &sd->ctime); + cputod32((__u32) inode->i_mtime.tv_sec, &sd->mtime); + if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) + cputod64(inode->i_rdev, &sd->u.rdev); + else + cputod64((__u64) inode_get_bytes(inode), &sd->u.bytes); + *area += sizeof *sd; + return 0; +} + +#if REISER4_DEBUG_OUTPUT +static void +print_unix_sd(const char *prefix, char **area /* position in stat-data */ , + int *len /* remaining length */ ) +{ + reiser4_unix_stat *sd; + + sd = (reiser4_unix_stat *) * area; + printk("%s: uid: %i, gid: %i, atime: %i, mtime: %i, ctime: %i, " + "rdev: %llo, bytes: %llu\n", prefix, + d32tocpu(&sd->uid), + d32tocpu(&sd->gid), + d32tocpu(&sd->atime), + d32tocpu(&sd->mtime), d32tocpu(&sd->ctime), d64tocpu(&sd->u.rdev), d64tocpu(&sd->u.bytes)); + move_on(len, area, sizeof *sd); +} +#endif + +static int +present_large_times_sd(struct inode *inode /* object being processed */, + char **area /* position in stat-data */, + int *len /* remaining length */) +{ + if (*len >= (int) sizeof (reiser4_large_times_stat)) { + reiser4_large_times_stat *sd_lt; + + sd_lt = (reiser4_large_times_stat *) * area; + + inode->i_atime.tv_nsec = d32tocpu(&sd_lt->atime); + inode->i_mtime.tv_nsec = d32tocpu(&sd_lt->mtime); + inode->i_ctime.tv_nsec = d32tocpu(&sd_lt->ctime); + + move_on(len, area, sizeof *sd_lt); + return 0; + } else + return not_enough_space(inode, "large times sd"); +} + +static int +save_len_large_times_sd(struct inode *inode UNUSED_ARG /* object being processed */ ) +{ + return sizeof (reiser4_large_times_stat); +} + +static int +save_large_times_sd(struct inode *inode /* object being processed */ , + char **area /* position in stat-data */ ) +{ + reiser4_large_times_stat *sd; + + assert("nikita-2817", inode != NULL); + assert("nikita-2818", area != NULL); + assert("nikita-2819", *area != NULL); + + sd = (reiser4_large_times_stat *) * area; + + cputod32((__u32) inode->i_atime.tv_nsec, &sd->atime); + cputod32((__u32) inode->i_ctime.tv_nsec, &sd->ctime); + cputod32((__u32) inode->i_mtime.tv_nsec, &sd->mtime); + + *area += sizeof *sd; + return 0; +} + +#if REISER4_DEBUG_OUTPUT +static void +print_large_times_sd(const char *prefix, char **area /* position in stat-data */, + int *len /* remaining length */ ) +{ + reiser4_large_times_stat *sd; + + sd = (reiser4_large_times_stat *) * area; + printk("%s: nanotimes: a: %i, m: %i, c: %i\n", prefix, + d32tocpu(&sd->atime), d32tocpu(&sd->mtime), d32tocpu(&sd->ctime)); + move_on(len, area, sizeof *sd); +} +#endif + +/* symlink stat data extension */ + +/* allocate memory for symlink target and attach it to inode->u.generic_ip */ +static int +symlink_target_to_inode(struct inode *inode, const char *target, int len) +{ + assert("vs-845", inode->u.generic_ip == 0); + assert("vs-846", !inode_get_flag(inode, REISER4_GENERIC_PTR_USED)); + + /* FIXME-VS: this is prone to deadlock. Not more than other similar + places, though */ + inode->u.generic_ip = reiser4_kmalloc((size_t) len + 1, GFP_KERNEL); + if (!inode->u.generic_ip) + return RETERR(-ENOMEM); + + xmemcpy((char *) (inode->u.generic_ip), target, (size_t) len); + ((char *) (inode->u.generic_ip))[len] = 0; + inode_set_flag(inode, REISER4_GENERIC_PTR_USED); + return 0; +} + +/* this is called on read_inode. There is nothing to do actually, but some + sanity checks */ +static int +present_symlink_sd(struct inode *inode, char **area, int *len) +{ + int result; + int length; + reiser4_symlink_stat *sd; + + length = (int) inode->i_size; + /* + * *len is number of bytes in stat data item from *area to the end of + * item. It must be not less than size of symlink + 1 for ending 0 + */ + if (length > *len) + return not_enough_space(inode, "symlink"); + + if (*(*area + length) != 0) { + warning("vs-840", "Symlink is not zero terminated"); + return RETERR(-EIO); + } + + sd = (reiser4_symlink_stat *) * area; + result = symlink_target_to_inode(inode, sd->body, length); + + move_on(len, area, length + 1); + return result; +} + +static int +save_len_symlink_sd(struct inode *inode) +{ + return inode->i_size + 1; +} + +/* this is called on create and update stat data. Do nothing on update but + update @area */ +static int +save_symlink_sd(struct inode *inode, char **area) +{ + int result; + int length; + reiser4_symlink_stat *sd; + + length = (int) inode->i_size; + /* inode->i_size must be set already */ + assert("vs-841", length); + + result = 0; + sd = (reiser4_symlink_stat *) * area; + if (!inode_get_flag(inode, REISER4_GENERIC_PTR_USED)) { + const char *target; + + target = (const char *) (inode->u.generic_ip); + inode->u.generic_ip = 0; + + result = symlink_target_to_inode(inode, target, length); + + /* copy symlink to stat data */ + xmemcpy(sd->body, target, (size_t) length); + (*area)[length] = 0; + } else { + /* there is nothing to do in update but move area */ + assert("vs-844", !memcmp(inode->u.generic_ip, sd->body, (size_t) length + 1)); + } + + *area += (length + 1); + return result; +} + +#if REISER4_DEBUG_OUTPUT +static void +print_symlink_sd(const char *prefix, char **area /* position in stat-data */ , + int *len /* remaining length */ ) +{ + reiser4_symlink_stat *sd; + int length; + + sd = (reiser4_symlink_stat *) * area; + length = strlen(sd->body); + printk("%s: \"%s\"\n", prefix, sd->body); + move_on(len, area, length + 1); +} +#endif + +static int +present_flags_sd(struct inode *inode /* object being processed */ , + char **area /* position in stat-data */ , + int *len /* remaining length */ ) +{ + assert("nikita-645", inode != NULL); + assert("nikita-646", area != NULL); + assert("nikita-647", *area != NULL); + assert("nikita-648", len != NULL); + assert("nikita-649", *len > 0); + + if (*len >= (int) sizeof (reiser4_flags_stat)) { + reiser4_flags_stat *sd; + + sd = (reiser4_flags_stat *) * area; + inode->i_flags = d32tocpu(&sd->flags); + move_on(len, area, sizeof *sd); + return 0; + } else + return not_enough_space(inode, "generation and attrs"); +} + +/* Audited by: green(2002.06.14) */ +static int +save_len_flags_sd(struct inode *inode UNUSED_ARG /* object being + * processed */ ) +{ + return sizeof (reiser4_flags_stat); +} + +static int +save_flags_sd(struct inode *inode /* object being processed */ , + char **area /* position in stat-data */ ) +{ + reiser4_flags_stat *sd; + + assert("nikita-650", inode != NULL); + assert("nikita-651", area != NULL); + assert("nikita-652", *area != NULL); + + sd = (reiser4_flags_stat *) * area; + cputod32(inode->i_flags, &sd->flags); + *area += sizeof *sd; + return 0; +} + +static int absent_plugin_sd(struct inode *inode); +static int +present_plugin_sd(struct inode *inode /* object being processed */ , + char **area /* position in stat-data */ , + int *len /* remaining length */ ) +{ + reiser4_plugin_stat *sd; + reiser4_plugin *plugin; + int i; + __u16 mask; + int result; + int num_of_plugins; + + assert("nikita-653", inode != NULL); + assert("nikita-654", area != NULL); + assert("nikita-655", *area != NULL); + assert("nikita-656", len != NULL); + assert("nikita-657", *len > 0); + + if (*len < (int) sizeof (reiser4_plugin_stat)) + return not_enough_space(inode, "plugin"); + + sd = (reiser4_plugin_stat *) * area; + + mask = 0; + num_of_plugins = d16tocpu(&sd->plugins_no); + move_on(len, area, sizeof *sd); + result = 0; + for (i = 0; i < num_of_plugins; ++i) { + reiser4_plugin_slot *slot; + reiser4_plugin_type type; + pset_member memb; + + slot = (reiser4_plugin_slot *) * area; + if (*len < (int) sizeof *slot) + return not_enough_space(inode, "additional plugin"); + + memb = d16tocpu(&slot->pset_memb); + type = pset_member_to_type_unsafe(memb); + if (type == REISER4_PLUGIN_TYPES) { + warning("nikita-3502", "wrong pset member (%i) for %llu", + memb, get_inode_oid(inode)); + return RETERR(-EINVAL); + } + plugin = plugin_by_disk_id(tree_by_inode(inode), + type, &slot->id); + if (plugin == NULL) + return unknown_plugin(d16tocpu(&slot->id), inode); + + /* plugin is loaded into inode, mark this into inode's + bitmask of loaded non-standard plugins */ + if (!(mask & (1 << memb))) { + mask |= (1 << memb); + } else { + warning("nikita-658", "duplicate plugin for %llu", get_inode_oid(inode)); + print_plugin("plugin", plugin); + return RETERR(-EINVAL); + } + move_on(len, area, sizeof *slot); + /* load plugin data, if any */ + if (plugin->h.pops != NULL && plugin->h.pops->load) { + result = plugin->h.pops->load(inode, plugin, area, len); + if (result != 0) + return result; + } else + result = grab_plugin_from(inode, memb, plugin); + } + /* if object plugin wasn't loaded from stat-data, guess it by + mode bits */ + plugin = file_plugin_to_plugin(inode_file_plugin(inode)); + if (plugin == NULL) + result = absent_plugin_sd(inode); + + reiser4_inode_data(inode)->plugin_mask = mask; + return result; +} + +/* Audited by: green(2002.06.14) */ +static int +absent_plugin_sd(struct inode *inode /* object being processed */ ) +{ + int result; + + assert("nikita-659", inode != NULL); + + result = guess_plugin_by_mode(inode); + /* if mode was wrong, guess_plugin_by_mode() returns "regular file", + but setup_inode_ops() will call make_bad_inode(). + Another, more logical but bit more complex solution is to add + "bad-file plugin". */ + /* FIXME-VS: activate was called here */ + return result; +} + +/* helper function for plugin_sd_save_len(): calculate how much space + required to save state of given plugin */ +/* Audited by: green(2002.06.14) */ +static int +len_for(reiser4_plugin * plugin /* plugin to save */ , + struct inode *inode /* object being processed */ , + pset_member memb, int len) +{ + reiser4_inode *info; + assert("nikita-661", inode != NULL); + + info = reiser4_inode_data(inode); + if (plugin != NULL && (info->plugin_mask & (1 << memb))) { + len += sizeof (reiser4_plugin_slot); + if (plugin->h.pops && plugin->h.pops->save_len != NULL) { + /* non-standard plugin, call method */ + /* commented as it is incompatible with alignment + * policy in save_plug() -edward */ + /* len = round_up(len, plugin->h.pops->alignment); */ + len += plugin->h.pops->save_len(inode, plugin); + } + } + return len; +} + +/* calculate how much space is required to save state of all plugins, + associated with inode */ +static int +save_len_plugin_sd(struct inode *inode /* object being processed */ ) +{ + int len; + reiser4_inode *state; + pset_member memb; + + assert("nikita-663", inode != NULL); + + state = reiser4_inode_data(inode); + /* common case: no non-standard plugins */ + if (state->plugin_mask == 0) + return 0; + len = sizeof (reiser4_plugin_stat); + for (memb = 0; memb < PSET_LAST; ++ memb) + len = len_for(pset_get(state->pset, memb), inode, memb, len); + assert("nikita-664", len > (int) sizeof (reiser4_plugin_stat)); + return len; +} + +/* helper function for plugin_sd_save(): save plugin, associated with + inode. */ +static int +save_plug(reiser4_plugin * plugin /* plugin to save */ , + struct inode *inode /* object being processed */ , + pset_member memb /* what element of pset is saved*/, + char **area /* position in stat-data */ , + int *count /* incremented if plugin were actually + * saved. */ ) +{ + reiser4_plugin_slot *slot; + int fake_len; + int result; + + assert("nikita-665", inode != NULL); + assert("nikita-666", area != NULL); + assert("nikita-667", *area != NULL); + + if (plugin == NULL) + return 0; + if (!(reiser4_inode_data(inode)->plugin_mask & (1 << memb))) + return 0; + slot = (reiser4_plugin_slot *) * area; + cputod16(memb, &slot->pset_memb); + cputod16((unsigned) plugin->h.id, &slot->id); + fake_len = (int) 0xffff; + move_on(&fake_len, area, sizeof *slot); + ++*count; + result = 0; + if (plugin->h.pops != NULL) { + if (plugin->h.pops->save != NULL) + result = plugin->h.pops->save(inode, plugin, area); + } + return result; +} + +/* save state of all non-standard plugins associated with inode */ +static int +save_plugin_sd(struct inode *inode /* object being processed */ , + char **area /* position in stat-data */ ) +{ + int result = 0; + int num_of_plugins; + reiser4_plugin_stat *sd; + reiser4_inode *state; + int fake_len; + pset_member memb; + + assert("nikita-669", inode != NULL); + assert("nikita-670", area != NULL); + assert("nikita-671", *area != NULL); + + state = reiser4_inode_data(inode); + if (state->plugin_mask == 0) + return 0; + sd = (reiser4_plugin_stat *) * area; + fake_len = (int) 0xffff; + move_on(&fake_len, area, sizeof *sd); + + num_of_plugins = 0; + for (memb = 0; memb < PSET_LAST; ++ memb) { + result = save_plug(pset_get(state->pset, memb), + inode, memb, area, &num_of_plugins); + if (result != 0) + break; + } + + cputod16((unsigned) num_of_plugins, &sd->plugins_no); + return result; +} + + +/* helper function for crypto_sd_present(), crypto_sd_save. + Allocates memory for crypto stat, keyid and attaches it to the inode */ + +static int crypto_stat_to_inode (struct inode *inode, + crypto_stat_t * tmp, + unsigned int size /* fingerprint size */) +{ + crypto_stat_t * stat; + + assert ("edward-11", (reiser4_inode_data(inode))->crypt == NULL); + assert ("edward-33", !inode_get_flag(inode, REISER4_CRYPTO_STAT_LOADED)); + + stat = reiser4_kmalloc(sizeof(*stat), GFP_KERNEL); + if (!stat) + return RETERR(-ENOMEM); + stat->keyid = reiser4_kmalloc((size_t)size, GFP_KERNEL); + if (!stat->keyid) { + reiser4_kfree(stat); + return RETERR(-ENOMEM); + } + /* load inode crypto-stat */ + stat->keysize = tmp->keysize; + xmemcpy(stat->keyid, tmp->keyid, (size_t)size); + reiser4_inode_data(inode)->crypt = stat; + + inode_set_flag(inode, REISER4_CRYPTO_STAT_LOADED); + return 0; +} + +/* crypto stat-data extension */ + +static int present_crypto_sd(struct inode *inode, char **area, int *len) +{ + int result; + reiser4_crypto_stat *sd; + crypto_stat_t stat; + digest_plugin * dplug = inode_digest_plugin(inode); + + unsigned int keyid_size; + + assert("edward-06", dplug != NULL); + assert("edward-684", dplug->dsize); + assert("edward-07", area != NULL); + assert("edward-08", *area != NULL); + assert("edward-09", len != NULL); + assert("edward-10", *len > 0); + + if (*len < (int) sizeof (reiser4_crypto_stat)) { + return not_enough_space(inode, "crypto-sd"); + } + keyid_size = dplug->dsize; + /* *len is number of bytes in stat data item from *area to the end of + item. It must be not less than size of this extension */ + assert("edward-75", sizeof(*sd) + keyid_size <= *len); + + sd = (reiser4_crypto_stat *) * area; + stat.keysize = d16tocpu(&sd->keysize); + stat.keyid = (__u8 *)sd->keyid; + + result = crypto_stat_to_inode(inode, &stat, keyid_size); + move_on(len, area, sizeof(*sd) + keyid_size); + return result; +} + +static int absent_crypto_sd(struct inode * inode) +{ + return -EIO; +} + +static int save_len_crypto_sd(struct inode *inode) +{ + return (sizeof(reiser4_crypto_stat) + inode_digest_plugin(inode)->dsize); +} + +static int save_crypto_sd(struct inode *inode, char **area) +{ + int result = 0; + reiser4_crypto_stat *sd; + digest_plugin * dplug = inode_digest_plugin(inode); + + assert("edward-12", dplug != NULL); + assert("edward-13", area != NULL); + assert("edward-14", *area != NULL); + assert("edward-76", reiser4_inode_data(inode) != NULL); + + sd = (reiser4_crypto_stat *) *area; + if (!inode_get_flag(inode, REISER4_CRYPTO_STAT_LOADED)) { + /* file is just created */ + crypto_stat_t * stat = reiser4_inode_data(inode)->crypt; + + assert("edward-15", stat != NULL); + + /* copy inode crypto-stat to the disk stat-data */ + cputod16(stat->keysize, &sd->keysize); + xmemcpy(sd->keyid, stat->keyid, (size_t)dplug->dsize); + inode_set_flag(inode, REISER4_CRYPTO_STAT_LOADED); + } else { + /* do nothing */ + } + *area += (sizeof(*sd) + dplug->dsize); + return result; +} + +#if REISER4_DEBUG_OUTPUT +static void +print_crypto_sd(const char *prefix, char **area /* position in stat-data */ , + int *len /* remaining length */ ) +{ + /* FIXME-EDWARD Make sure we debug only with none digest plugin */ + digest_plugin * dplug = digest_plugin_by_id(NONE_DIGEST_ID); + reiser4_crypto_stat *sd = (reiser4_crypto_stat *) * area; + + printk("%s: keysize: %u keyid: \"%llx\"\n", prefix, d16tocpu(&sd->keysize), *(__u64 *)(sd->keyid)); + move_on(len, area, sizeof(*sd) + dplug->dsize); +} +#endif + +/* cluster stat-data extension */ + +static int present_cluster_sd(struct inode *inode, char **area, int *len) +{ + reiser4_inode * info; + + assert("edward-77", inode != NULL); + assert("edward-78", area != NULL); + assert("edward-79", *area != NULL); + assert("edward-80", len != NULL); + assert("edward-81", !inode_get_flag(inode, REISER4_CLUSTER_KNOWN)); + + info = reiser4_inode_data(inode); + + assert("edward-82", info != NULL); + + if (*len >= (int) sizeof (reiser4_cluster_stat)) { + reiser4_cluster_stat *sd; + sd = (reiser4_cluster_stat *) * area; + info->cluster_shift = d8tocpu(&sd->cluster_shift); + inode_set_flag(inode, REISER4_CLUSTER_KNOWN); + move_on(len, area, sizeof *sd); + return 0; + } + else + return not_enough_space(inode, "cluster sd"); +} + +static int absent_cluster_sd(struct inode * inode) +{ + return -EIO; +} + +static int save_len_cluster_sd(struct inode *inode UNUSED_ARG) +{ + return sizeof (reiser4_cluster_stat); +} + +static int save_cluster_sd(struct inode *inode, char **area) +{ + reiser4_cluster_stat *sd; + + assert("edward-106", inode != NULL); + assert("edward-107", area != NULL); + assert("edward-108", *area != NULL); + + sd = (reiser4_cluster_stat *) * area; + if (!inode_get_flag(inode, REISER4_CLUSTER_KNOWN)) { + cputod8(reiser4_inode_data(inode)->cluster_shift, &sd->cluster_shift); + inode_set_flag(inode, REISER4_CLUSTER_KNOWN); + } + else { + /* do nothing */ + } + *area += sizeof *sd; + return 0; +} + +#if REISER4_DEBUG_OUTPUT +static void +print_cluster_sd(const char *prefix, char **area /* position in stat-data */, + int *len /* remaining length */ ) +{ + reiser4_cluster_stat *sd = (reiser4_cluster_stat *) * area; + + printk("%s: %u\n", prefix, d8tocpu(&sd->cluster_shift)); + move_on(len, area, sizeof *sd); +} +#endif + +static int eio(struct inode *inode, char **area, int *len) +{ + return RETERR(-EIO); +} + +sd_ext_plugin sd_ext_plugins[LAST_SD_EXTENSION] = { + [LIGHT_WEIGHT_STAT] = { + .h = { + .type_id = REISER4_SD_EXT_PLUGIN_TYPE, + .id = LIGHT_WEIGHT_STAT, + .pops = NULL, + .label = "light-weight sd", + .desc = "sd for light-weight files", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .present = present_lw_sd, + .absent = NULL, + .save_len = save_len_lw_sd, + .save = save_lw_sd, +#if REISER4_DEBUG_OUTPUT + .print = print_lw_sd, +#endif + .alignment = 8 + }, + [UNIX_STAT] = { + .h = { + .type_id = REISER4_SD_EXT_PLUGIN_TYPE, + .id = UNIX_STAT, + .pops = NULL, + .label = "unix-sd", + .desc = "unix stat-data fields", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .present = present_unix_sd, + .absent = absent_unix_sd, + .save_len = save_len_unix_sd, + .save = save_unix_sd, +#if REISER4_DEBUG_OUTPUT + .print = print_unix_sd, +#endif + .alignment = 8 + }, + [LARGE_TIMES_STAT] = { + .h = { + .type_id = REISER4_SD_EXT_PLUGIN_TYPE, + .id = LARGE_TIMES_STAT, + .pops = NULL, + .label = "64time-sd", + .desc = "nanosecond resolution for times", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .present = present_large_times_sd, + .absent = NULL, + .save_len = save_len_large_times_sd, + .save = save_large_times_sd, +#if REISER4_DEBUG_OUTPUT + .print = print_large_times_sd, +#endif + .alignment = 8 + }, + [SYMLINK_STAT] = { + /* stat data of symlink has this extension */ + .h = { + .type_id = REISER4_SD_EXT_PLUGIN_TYPE, + .id = SYMLINK_STAT, + .pops = NULL, + .label = "symlink-sd", + .desc = "stat data is appended with symlink name", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .present = present_symlink_sd, + .absent = NULL, + .save_len = save_len_symlink_sd, + .save = save_symlink_sd, +#if REISER4_DEBUG_OUTPUT + .print = print_symlink_sd, +#endif + .alignment = 8 + }, + [PLUGIN_STAT] = { + .h = { + .type_id = REISER4_SD_EXT_PLUGIN_TYPE, + .id = PLUGIN_STAT, + .pops = NULL, + .label = "plugin-sd", + .desc = "plugin stat-data fields", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .present = present_plugin_sd, + .absent = absent_plugin_sd, + .save_len = save_len_plugin_sd, + .save = save_plugin_sd, +#if REISER4_DEBUG_OUTPUT + .print = NULL, +#endif + .alignment = 8 + }, + [FLAGS_STAT] = { + .h = { + .type_id = REISER4_SD_EXT_PLUGIN_TYPE, + .id = FLAGS_STAT, + .pops = NULL, + .label = "flags-sd", + .desc = "inode bit flags", + .linkage = TYPE_SAFE_LIST_LINK_ZERO} + , + .present = present_flags_sd, + .absent = NULL, + .save_len = save_len_flags_sd, + .save = save_flags_sd, +#if REISER4_DEBUG_OUTPUT + .print = NULL, +#endif + .alignment = 8 + }, + [CAPABILITIES_STAT] = { + .h = { + .type_id = REISER4_SD_EXT_PLUGIN_TYPE, + .id = CAPABILITIES_STAT, + .pops = NULL, + .label = "capabilities-sd", + .desc = "capabilities", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .present = eio, + .absent = NULL, + .save_len = save_len_flags_sd, + .save = save_flags_sd, +#if REISER4_DEBUG_OUTPUT + .print = NULL, +#endif + .alignment = 8 + }, + [CLUSTER_STAT] = { + .h = { + .type_id = REISER4_SD_EXT_PLUGIN_TYPE, + .id = CLUSTER_STAT, + .pops = NULL, + .label = "cluster-sd", + .desc = "cluster shift", + .linkage = TYPE_SAFE_LIST_LINK_ZERO} + , + .present = present_cluster_sd, + .absent = absent_cluster_sd, + /* return IO_ERROR if smthng is wrong */ + .save_len = save_len_cluster_sd, + .save = save_cluster_sd, +#if REISER4_DEBUG_OUTPUT + .print = print_cluster_sd, +#endif + .alignment = 8 + }, + [CRYPTO_STAT] = { + .h = { + .type_id = REISER4_SD_EXT_PLUGIN_TYPE, + .id = CRYPTO_STAT, + .pops = NULL, + .label = "crypto-sd", + .desc = "secret key size and id", + .linkage = TYPE_SAFE_LIST_LINK_ZERO} + , + .present = present_crypto_sd, + .absent = absent_crypto_sd, + /* return IO_ERROR if smthng is wrong */ + .save_len = save_len_crypto_sd, + .save = save_crypto_sd, +#if REISER4_DEBUG_OUTPUT + .print = print_crypto_sd, +#endif + .alignment = 8 + } +}; + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/item/static_stat.h 2004-09-03 00:57:05.284235936 -0700 @@ -0,0 +1,220 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* This describes the static_stat item, used to hold all information needed by the stat() syscall. + +In the case where each file has not less than the fields needed by the +stat() syscall, it is more compact to store those fields in this +struct. + +If this item does not exist, then all stats are dynamically resolved. +At the moment, we either resolve all stats dynamically or all of them +statically. If you think this is not fully optimal, and the rest of +reiser4 is working, then fix it...:-) + +*/ + +#if !defined( __FS_REISER4_PLUGIN_ITEM_STATIC_STAT_H__ ) +#define __FS_REISER4_PLUGIN_ITEM_STATIC_STAT_H__ + +#include "../../forward.h" +#include "../../dformat.h" + +#include /* for struct inode */ + +/* Stat data layout: goals and implementation. + +We want to be able to have lightweight files which have complete flexibility in what semantic metadata is attached to +them, including not having semantic metadata attached to them. + +There is one problem with doing that, which is that if in fact you have exactly the same metadata for most files you want to store, then it takes more space to store that metadata in a dynamically sized structure than in a statically sized structure because the statically sized structure knows without recording it what the names and lengths of the attributes are. + +This leads to a natural compromise, which is to special case those files which have simply the standard unix file +attributes, and only employ the full dynamic stat data mechanism for those files that differ from the standard unix file +in their use of file attributes. + +Yet this compromise deserves to be compromised a little. + +We accommodate the case where you have no more than the standard unix file attributes by using an "extension bitmask": +each bit in it indicates presence or absence of or particular stat data extension (see sd_ext_bits enum). + + If the first +bit of the extension bitmask bit is 0, we have light-weight file whose attributes are either inherited from parent +directory (as uid, gid) or initialised to some sane values. + + To capitalize on existing code infrastructure, extensions are + implemented as plugins of type REISER4_SD_EXT_PLUGIN_TYPE. + Each stat-data extension plugin implements four methods: + + ->present() called by sd_load() when this extension is found in stat-data + ->absent() called by sd_load() when this extension is not found in stat-data + ->save_len() called by sd_len() to calculate total length of stat-data + ->save() called by sd_save() to store extension data into stat-data + + Implementation is in fs/reiser4/plugin/item/static_stat.c +*/ + +/* stat-data extension. Please order this by presumed frequency of use */ +typedef enum { + /* support for light-weight files */ + LIGHT_WEIGHT_STAT, + /* data required to implement unix stat(2) call. Layout is in + reiser4_unix_stat. If this is not present, file is light-weight */ + UNIX_STAT, + /* this contains additional set of 32bit [anc]time fields to implement + nanosecond resolution. Layout is in reiser4_large_times_stat. Usage + if this extension is governed by 32bittimes mount option. */ + LARGE_TIMES_STAT, + /* stat data has link name included */ + SYMLINK_STAT, + /* if this is present, file is controlled by non-standard + plugin (that is, plugin that cannot be deduced from file + mode bits), for example, aggregation, interpolation etc. */ + PLUGIN_STAT, + /* this extension contains persistent inode flags. These flags are + single bits: immutable, append, only, etc. Layout is in + reiser4_flags_stat. */ + FLAGS_STAT, + /* this extension contains capabilities sets, associated with this + file. Layout is in reiser4_capabilities_stat */ + CAPABILITIES_STAT, + /* this extension contains the information about minimal unit size for + file data processing. Layout is in reiser4_cluster_stat */ + CLUSTER_STAT, + /* this extension contains size and public id of the secret key. + Layout is in reiser4_crypto_stat */ + CRYPTO_STAT, + LAST_SD_EXTENSION, + /* + * init_inode_static_sd() iterates over extension mask until all + * non-zero bits are processed. This means, that neither ->present(), + * nor ->absent() methods will be called for stat-data extensions that + * go after last present extension. But some basic extensions, we want + * either ->absent() or ->present() method to be called, because these + * extensions set up something in inode even when they are not + * present. This is what LAST_IMPORTANT_SD_EXTENSION is for: for all + * extensions before and including LAST_IMPORTANT_SD_EXTENSION either + * ->present(), or ->absent() method will be called, independently of + * what other extensions are present. + */ + LAST_IMPORTANT_SD_EXTENSION = PLUGIN_STAT, +} sd_ext_bits; + +/* minimal stat-data. This allows to support light-weight files. */ +typedef struct reiser4_stat_data_base { + /* 0 */ d16 extmask; + /* 2 */ +} PACKED reiser4_stat_data_base; + +typedef struct reiser4_light_weight_stat { + /* 0 */ d16 mode; + /* 2 */ d32 nlink; + /* 8 */ d64 size; + /* size in bytes */ + /* 16 */ +} PACKED reiser4_light_weight_stat; + +typedef struct reiser4_unix_stat { + /* owner id */ + /* 0 */ d32 uid; + /* group id */ + /* 4 */ d32 gid; + /* access time */ + /* 8 */ d32 atime; + /* modification time */ + /* 12 */ d32 mtime; + /* change time */ + /* 16 */ d32 ctime; + union { + /* minor:major for device files */ + /* 20 */ d64 rdev; + /* bytes used by file */ + /* 20 */ d64 bytes; + } u; + /* 28 */ +} PACKED reiser4_unix_stat; + +/* symlink stored as part of inode */ +typedef struct reiser4_symlink_stat { + char body[0]; +} PACKED reiser4_symlink_stat; + +typedef struct reiser4_plugin_slot { + /* 0 */ d16 pset_memb; + /* 2 */ d16 id; +/* 4 *//* here plugin stores its persistent state */ +} PACKED reiser4_plugin_slot; + +/* stat-data extension for files with non-standard plugin. */ +typedef struct reiser4_plugin_stat { + /* number of additional plugins, associated with this object */ + /* 0 */ d16 plugins_no; + /* 2 */ reiser4_plugin_slot slot[0]; + /* 2 */ +} PACKED reiser4_plugin_stat; + +/* stat-data extension for inode flags. Currently it is just fixed-width 32 + * bit mask. If need arise, this can be replaced with variable width + * bitmask. */ +typedef struct reiser4_flags_stat { + /* 0 */ d32 flags; + /* 4 */ +} PACKED reiser4_flags_stat; + +typedef struct reiser4_capabilities_stat { + /* 0 */ d32 effective; + /* 8 */ d32 permitted; + /* 16 */ +} PACKED reiser4_capabilities_stat; + +typedef struct reiser4_cluster_stat { +/* this defines cluster size (an attribute of cryptcompress objects) as PAGE_SIZE << cluster shift */ + /* 0 */ d8 cluster_shift; + /* 1 */ +} PACKED reiser4_cluster_stat; + +typedef struct reiser4_crypto_stat { + /* secret key size, bits */ + /* 0 */ d16 keysize; + /* secret key id */ + /* 2 */ d8 keyid[0]; + /* 2 */ +} PACKED reiser4_crypto_stat; + +typedef struct reiser4_large_times_stat { + /* access time */ + /* 0 */ d32 atime; + /* modification time */ + /* 8 */ d32 mtime; + /* change time */ + /* 16 */ d32 ctime; + /* 24 */ +} PACKED reiser4_large_times_stat; + +/* this structure is filled by sd_item_stat */ +typedef struct sd_stat { + int dirs; + int files; + int others; +} sd_stat; + +/* plugin->item.common.* */ +extern void print_sd(const char *prefix, coord_t * coord); +extern void item_stat_static_sd(const coord_t * coord, void *vp); + +/* plugin->item.s.sd.* */ +extern int init_inode_static_sd(struct inode *inode, char *sd, int len); +extern int save_len_static_sd(struct inode *inode); +extern int save_static_sd(struct inode *inode, char **area); + +/* __FS_REISER4_PLUGIN_ITEM_STATIC_STAT_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/item/tail.c 2004-09-03 00:57:05.287235480 -0700 @@ -0,0 +1,691 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#include "item.h" +#include "../../inode.h" +#include "../../page_cache.h" +#include "../../carry.h" +#include "../../vfs_ops.h" + +#include +#include +#include +#include + +/* plugin->u.item.b.max_key_inside */ +reiser4_internal reiser4_key * +max_key_inside_tail(const coord_t *coord, reiser4_key *key) +{ + item_key_by_coord(coord, key); + set_key_offset(key, get_key_offset(max_key())); + return key; +} + +/* plugin->u.item.b.can_contain_key */ +reiser4_internal int +can_contain_key_tail(const coord_t *coord, const reiser4_key *key, const reiser4_item_data *data) +{ + reiser4_key item_key; + + if (item_plugin_by_coord(coord) != data->iplug) + return 0; + + item_key_by_coord(coord, &item_key); + if (get_key_locality(key) != get_key_locality(&item_key) || + get_key_objectid(key) != get_key_objectid(&item_key)) return 0; + + return 1; +} + +/* plugin->u.item.b.mergeable + first item is of tail type */ +/* Audited by: green(2002.06.14) */ +reiser4_internal int +mergeable_tail(const coord_t *p1, const coord_t *p2) +{ + reiser4_key key1, key2; + + assert("vs-535", item_type_by_coord(p1) == UNIX_FILE_METADATA_ITEM_TYPE); + assert("vs-365", item_id_by_coord(p1) == FORMATTING_ID); + + if (item_id_by_coord(p2) != FORMATTING_ID) { + /* second item is of another type */ + return 0; + } + + item_key_by_coord(p1, &key1); + item_key_by_coord(p2, &key2); + if (get_key_locality(&key1) != get_key_locality(&key2) || + get_key_objectid(&key1) != get_key_objectid(&key2) || get_key_type(&key1) != get_key_type(&key2)) { + /* items of different objects */ + return 0; + } + if (get_key_offset(&key1) + nr_units_tail(p1) != get_key_offset(&key2)) { + /* not adjacent items */ + return 0; + } + return 1; +} + +reiser4_internal void show_tail(struct seq_file *m, coord_t *coord) +{ + seq_printf(m, "length: %i", item_length_by_coord(coord)); +} + +/* plugin->u.item.b.print + plugin->u.item.b.check */ + +/* plugin->u.item.b.nr_units */ +reiser4_internal pos_in_node_t +nr_units_tail(const coord_t *coord) +{ + return item_length_by_coord(coord); +} + +/* plugin->u.item.b.lookup */ +reiser4_internal lookup_result +lookup_tail(const reiser4_key *key, lookup_bias bias, coord_t *coord) +{ + reiser4_key item_key; + __u64 lookuped, offset; + unsigned nr_units; + + item_key_by_coord(coord, &item_key); + offset = get_key_offset(item_key_by_coord(coord, &item_key)); + nr_units = nr_units_tail(coord); + + /* key we are looking for must be greater than key of item @coord */ + assert("vs-416", keygt(key, &item_key)); + + /* offset we are looking for */ + lookuped = get_key_offset(key); + + if (lookuped >= offset && lookuped < offset + nr_units) { + /* byte we are looking for is in this item */ + coord->unit_pos = lookuped - offset; + coord->between = AT_UNIT; + return CBK_COORD_FOUND; + } + + /* set coord after last unit */ + coord->unit_pos = nr_units - 1; + coord->between = AFTER_UNIT; + return bias == FIND_MAX_NOT_MORE_THAN ? CBK_COORD_FOUND : CBK_COORD_NOTFOUND; +} + +/* plugin->u.item.b.paste */ +reiser4_internal int +paste_tail(coord_t *coord, reiser4_item_data *data, carry_plugin_info *info UNUSED_ARG) +{ + unsigned old_item_length; + char *item; + + /* length the item had before resizing has been performed */ + old_item_length = item_length_by_coord(coord) - data->length; + + /* tail items never get pasted in the middle */ + assert("vs-363", + (coord->unit_pos == 0 && coord->between == BEFORE_UNIT) || + (coord->unit_pos == old_item_length - 1 && + coord->between == AFTER_UNIT) || + (coord->unit_pos == 0 && old_item_length == 0 && coord->between == AT_UNIT)); + + item = item_body_by_coord(coord); + if (coord->unit_pos == 0) + /* make space for pasted data when pasting at the beginning of + the item */ + xmemmove(item + data->length, item, old_item_length); + + if (coord->between == AFTER_UNIT) + coord->unit_pos++; + + if (data->data) { + assert("vs-554", data->user == 0 || data->user == 1); + if (data->user) { + assert("nikita-3035", schedulable()); + /* AUDIT: return result is not checked! */ + /* copy from user space */ + __copy_from_user(item + coord->unit_pos, data->data, (unsigned) data->length); + } else + /* copy from kernel space */ + xmemcpy(item + coord->unit_pos, data->data, (unsigned) data->length); + } else { + xmemset(item + coord->unit_pos, 0, (unsigned) data->length); + } + return 0; +} + +/* plugin->u.item.b.fast_paste */ + +/* plugin->u.item.b.can_shift + number of units is returned via return value, number of bytes via @size. For + tail items they coincide */ +reiser4_internal int +can_shift_tail(unsigned free_space, coord_t *source UNUSED_ARG, + znode *target UNUSED_ARG, shift_direction direction UNUSED_ARG, unsigned *size, unsigned want) +{ + /* make sure that that we do not want to shift more than we have */ + assert("vs-364", want > 0 && want <= (unsigned) item_length_by_coord(source)); + + *size = min(want, free_space); + return *size; +} + +/* plugin->u.item.b.copy_units */ +reiser4_internal void +copy_units_tail(coord_t *target, coord_t *source, + unsigned from, unsigned count, shift_direction where_is_free_space, unsigned free_space UNUSED_ARG) +{ + /* make sure that item @target is expanded already */ + assert("vs-366", (unsigned) item_length_by_coord(target) >= count); + assert("vs-370", free_space >= count); + + if (where_is_free_space == SHIFT_LEFT) { + /* append item @target with @count first bytes of @source */ + assert("vs-365", from == 0); + + xmemcpy((char *) item_body_by_coord(target) + + item_length_by_coord(target) - count, (char *) item_body_by_coord(source), count); + } else { + /* target item is moved to right already */ + reiser4_key key; + + assert("vs-367", (unsigned) item_length_by_coord(source) == from + count); + + xmemcpy((char *) item_body_by_coord(target), (char *) item_body_by_coord(source) + from, count); + + /* new units are inserted before first unit in an item, + therefore, we have to update item key */ + item_key_by_coord(source, &key); + set_key_offset(&key, get_key_offset(&key) + from); + + node_plugin_by_node(target->node)->update_item_key(target, &key, 0 /*info */); + } +} + +/* plugin->u.item.b.create_hook */ + + +/* item_plugin->b.kill_hook + this is called when @count units starting from @from-th one are going to be removed + */ +reiser4_internal int +kill_hook_tail(const coord_t *coord, pos_in_node_t from, + pos_in_node_t count, struct carry_kill_data *kdata) +{ + reiser4_key key; + loff_t start, end; + + assert("vs-1577", kdata); + assert("vs-1579", kdata->inode); + + item_key_by_coord(coord, &key); + start = get_key_offset(&key) + from; + end = start + count; + fake_kill_hook_tail(kdata->inode, start, end); + return 0; +} + +/* plugin->u.item.b.shift_hook */ + +/* helper for kill_units_tail and cut_units_tail */ +static int +do_cut_or_kill(coord_t *coord, pos_in_node_t from, pos_in_node_t to, + reiser4_key *smallest_removed, reiser4_key *new_first) +{ + pos_in_node_t count; + + /* this method is only called to remove part of item */ + assert("vs-374", (to - from + 1) < item_length_by_coord(coord)); + /* tails items are never cut from the middle of an item */ + assert("vs-396", ergo(from != 0, to == coord_last_unit_pos(coord))); + assert("vs-1558", ergo(from == 0, to < coord_last_unit_pos(coord))); + + count = to - from + 1; + + if (smallest_removed) { + /* store smallest key removed */ + item_key_by_coord(coord, smallest_removed); + set_key_offset(smallest_removed, get_key_offset(smallest_removed) + from); + } + if (new_first) { + /* head of item is cut */ + assert("vs-1529", from == 0); + + item_key_by_coord(coord, new_first); + set_key_offset(new_first, get_key_offset(new_first) + from + count); + } + + if (REISER4_DEBUG) + xmemset((char *) item_body_by_coord(coord) + from, 0, count); + return count; +} + +/* plugin->u.item.b.cut_units */ +reiser4_internal int +cut_units_tail(coord_t *coord, pos_in_node_t from, pos_in_node_t to, + struct carry_cut_data *cdata UNUSED_ARG, reiser4_key *smallest_removed, reiser4_key *new_first) +{ + return do_cut_or_kill(coord, from, to, smallest_removed, new_first); +} + +/* plugin->u.item.b.kill_units */ +reiser4_internal int +kill_units_tail(coord_t *coord, pos_in_node_t from, pos_in_node_t to, + struct carry_kill_data *kdata, reiser4_key *smallest_removed, reiser4_key *new_first) +{ + kill_hook_tail(coord, from, to - from + 1, kdata); + return do_cut_or_kill(coord, from, to, smallest_removed, new_first); +} + +/* plugin->u.item.b.unit_key */ +reiser4_internal reiser4_key * +unit_key_tail(const coord_t *coord, reiser4_key *key) +{ + assert("vs-375", coord_is_existing_unit(coord)); + + item_key_by_coord(coord, key); + set_key_offset(key, (get_key_offset(key) + coord->unit_pos)); + + return key; +} + +/* plugin->u.item.b.estimate + plugin->u.item.b.item_data_by_flow */ + +/* overwrite tail item or its part by use data */ +static int +overwrite_tail(coord_t *coord, flow_t *f) +{ + unsigned count; + + assert("vs-570", f->user == 1); + assert("vs-946", f->data); + assert("vs-947", coord_is_existing_unit(coord)); + assert("vs-948", znode_is_write_locked(coord->node)); + assert("nikita-3036", schedulable()); + + count = item_length_by_coord(coord) - coord->unit_pos; + if (count > f->length) + count = f->length; + + if (__copy_from_user((char *) item_body_by_coord(coord) + coord->unit_pos, f->data, count)) + return RETERR(-EFAULT); + + znode_make_dirty(coord->node); + + move_flow_forward(f, count); + return 0; +} + +/* tail redpage function. It is called from readpage_tail(). */ +reiser4_internal int do_readpage_tail(uf_coord_t *uf_coord, struct page *page) { + tap_t tap; + int result; + coord_t coord; + lock_handle lh; + + int count, mapped; + struct inode *inode; + + /* saving passed coord in order to do not move it by tap. */ + init_lh(&lh); + copy_lh(&lh, uf_coord->lh); + inode = page->mapping->host; + coord_dup(&coord, &uf_coord->base_coord); + + tap_init(&tap, &coord, &lh, ZNODE_READ_LOCK); + + if ((result = tap_load(&tap))) + goto out_tap_done; + + /* lookup until page is filled up. */ + for (mapped = 0; mapped < PAGE_CACHE_SIZE; mapped += count) { + void *pagedata; + + /* number of bytes to be copied to page. */ + count = item_length_by_coord(&coord) - coord.unit_pos; + + if (count > PAGE_CACHE_SIZE - mapped) + count = PAGE_CACHE_SIZE - mapped; + + /* attaching @page to address space and getting data address. */ + pagedata = kmap_atomic(page, KM_USER0); + + /* copying tail body to page. */ + xmemcpy((char *)(pagedata + mapped), + ((char *)item_body_by_coord(&coord) + coord.unit_pos), count); + + flush_dcache_page(page); + + /* dettaching page from address space. */ + kunmap_atomic(page, KM_USER0); + + /* Getting next tail item. */ + if (mapped + count < PAGE_CACHE_SIZE) { + + /* unlocking page in order to avoid keep it locked durring tree lookup, + which takes long term locks. */ + unlock_page(page); + + /* getting right neighbour. */ + result = go_dir_el(&tap, RIGHT_SIDE, 0); + + /* lock page back */ + lock_page(page); + + /* page is uptodate due to another thread made it up to date. Getting + out of here. */ + if (PageUptodate(page)) { + result = 0; + goto out_unlock_page; + } + + if (result) { + /* check if there is no neighbour node. */ + if (result == -E_NO_NEIGHBOR) { + result = 0; + goto out_update_page; + } else { + goto out_tap_relse; + } + } else { + /* check if found coord is not owned by file. */ + if (!inode_file_plugin(inode)->owns_item(inode, &coord)) { + result = 0; + goto out_update_page; + } + } + } + } + + /* making page up to date and releasing it. */ + SetPageUptodate(page); + unlock_page(page); + + /* releasing tap */ + tap_relse(&tap); + tap_done(&tap); + + return 0; + + out_update_page: + SetPageUptodate(page); + out_unlock_page: + unlock_page(page); + out_tap_relse: + tap_relse(&tap); + out_tap_done: + tap_done(&tap); + return result; +} + +/* + plugin->s.file.readpage + reiser4_read->unix_file_read->page_cache_readahead->reiser4_readpage->unix_file_readpage->readpage_tail + or + filemap_nopage->reiser4_readpage->readpage_unix_file->->readpage_tail + + At the beginning: coord->node is read locked, zloaded, page is locked, coord is set to existing unit inside of tail + item. */ +reiser4_internal int +readpage_tail(void *vp, struct page *page) +{ + uf_coord_t *uf_coord = vp; + ON_DEBUG(coord_t *coord = &uf_coord->base_coord); + ON_DEBUG(reiser4_key key); + + assert("umka-2515", PageLocked(page)); + assert("umka-2516", !PageUptodate(page)); + assert("umka-2517", !jprivate(page) && !PagePrivate(page)); + assert("umka-2518", page->mapping && page->mapping->host); + + assert("umka-2519", znode_is_loaded(coord->node)); + assert("umka-2520", item_is_tail(coord)); + assert("umka-2521", coord_is_existing_unit(coord)); + assert("umka-2522", znode_is_rlocked(coord->node)); + assert("umka-2523", page->mapping->host->i_ino == get_key_objectid(item_key_by_coord(coord, &key))); + + return do_readpage_tail(uf_coord, page); +} + +reiser4_internal int +item_balance_dirty_pages(struct address_space *mapping, const flow_t *f, + hint_t *hint, int back_to_dirty, int do_set_hint) +{ + int result; + struct inode *inode; + + if (do_set_hint) { + if (hint->coord.valid) + set_hint(hint, &f->key, ZNODE_WRITE_LOCK); + else + unset_hint(hint); + longterm_unlock_znode(hint->coord.lh); + } + + inode = mapping->host; + if (get_key_offset(&f->key) > inode->i_size) { + assert("vs-1649", f->user == 1); + INODE_SET_FIELD(inode, i_size, get_key_offset(&f->key)); + } + if (f->user != 0) { + /* this was writing data from user space. Update timestamps, therefore. Othrewise, this is tail + conversion where we should not update timestamps */ + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + result = reiser4_update_sd(inode); + if (result) + return result; + } + + /* FIXME-VS: this is temporary: the problem is that bdp takes inodes + from sb's dirty list and it looks like nobody puts there inodes of + files which are built of tails */ + if (back_to_dirty) + move_inode_out_from_sync_inodes_loop(mapping); + + reiser4_throttle_write(inode); + return hint_validate(hint, &f->key, 0/* do not check key */, ZNODE_WRITE_LOCK); +} + +/* drop longterm znode lock before calling balance_dirty_pages. balance_dirty_pages may cause transaction to close, + therefore we have to update stat data if necessary */ +static int formatting_balance_dirty_pages(struct address_space *mapping, const flow_t *f, + hint_t *hint) +{ + return item_balance_dirty_pages(mapping, f, hint, 1, 1/* set hint */); +} + +/* calculate number of blocks which can be dirtied/added when flow is inserted and stat data gets updated and grab them. + FIXME-VS: we may want to call grab_space with BA_CAN_COMMIT flag but that would require all that complexity with + sealing coord, releasing long term lock and validating seal later */ +static int +insert_flow_reserve(reiser4_tree *tree) +{ + grab_space_enable(); + return reiser4_grab_space(estimate_insert_flow(tree->height) + estimate_one_insert_into_item(tree), 0); +} + +/* one block gets overwritten and stat data may get updated */ +static int +overwrite_reserve(reiser4_tree *tree) +{ + grab_space_enable(); + return reiser4_grab_space(1 + estimate_one_insert_into_item(tree), 0); +} + +/* plugin->u.item.s.file.write + access to data stored in tails goes directly through formatted nodes */ +reiser4_internal int +write_tail(struct inode *inode, flow_t *f, hint_t *hint, + int grabbed, /* tail's write may be called from plain unix file write and from tail conversion. In first + case (grabbed == 0) space is not reserved forehand, so, it must be done here. When it is + being called from tail conversion - space is reserved already for whole operation which may + involve several calls to item write. In this case space reservation will not be done here */ + write_mode_t mode) +{ + int result; + coord_t *coord; + + assert("vs-1338", hint->coord.valid == 1); + + coord = &hint->coord.base_coord; + result = 0; + while (f->length && hint->coord.valid == 1) { + switch (mode) { + case FIRST_ITEM: + case APPEND_ITEM: + /* check quota before appending data */ + if (DQUOT_ALLOC_SPACE_NODIRTY(inode, f->length)) { + result = RETERR(-EDQUOT); + break; + } + + if (!grabbed) + result = insert_flow_reserve(znode_get_tree(coord->node)); + if (!result) + result = insert_flow(coord, hint->coord.lh, f); + if (f->length) + DQUOT_FREE_SPACE_NODIRTY(inode, f->length); + break; + + case OVERWRITE_ITEM: + if (!grabbed) + result = overwrite_reserve(znode_get_tree(coord->node)); + if (!result) + result = overwrite_tail(coord, f); + break; + + default: + impossible("vs-1031", "does this ever happen?"); + result = RETERR(-EIO); + break; + + } + + if (result) { + if (!grabbed) + all_grabbed2free(); + break; + } + + /* FIXME: do not rely on a coord yet */ + hint->coord.valid = 0; + + /* throttle the writer */ + result = formatting_balance_dirty_pages(inode->i_mapping, f, hint); + if (!grabbed) + all_grabbed2free(); + if (result) { + // reiser4_stat_tail_add(bdp_caused_repeats); + break; + } + } + + return result; +} + +#if REISER4_DEBUG + +static int +coord_matches_key_tail(const coord_t *coord, const reiser4_key *key) +{ + reiser4_key item_key; + + assert("vs-1356", coord_is_existing_unit(coord)); + assert("vs-1354", keylt(key, append_key_tail(coord, &item_key))); + assert("vs-1355", keyge(key, item_key_by_coord(coord, &item_key))); + return get_key_offset(key) == get_key_offset(&item_key) + coord->unit_pos; + +} + +#endif + +/* plugin->u.item.s.file.read */ +reiser4_internal int +read_tail(struct file *file UNUSED_ARG, flow_t *f, hint_t *hint) +{ + unsigned count; + int item_length; + coord_t *coord; + uf_coord_t *uf_coord; + + uf_coord = &hint->coord; + coord = &uf_coord->base_coord; + + assert("vs-571", f->user == 1); + assert("vs-571", f->data); + assert("vs-967", coord && coord->node); + assert("vs-1117", znode_is_rlocked(coord->node)); + assert("vs-1118", znode_is_loaded(coord->node)); + + assert("nikita-3037", schedulable()); + assert("vs-1357", coord_matches_key_tail(coord, &f->key)); + + /* calculate number of bytes to read off the item */ + item_length = item_length_by_coord(coord); + count = item_length_by_coord(coord) - coord->unit_pos; + if (count > f->length) + count = f->length; + + + /* FIXME: unlock long term lock ! */ + + if (__copy_to_user(f->data, ((char *) item_body_by_coord(coord) + coord->unit_pos), count)) + return RETERR(-EFAULT); + + /* probably mark_page_accessed() should only be called if + * coord->unit_pos is zero. */ + mark_page_accessed(znode_page(coord->node)); + move_flow_forward(f, count); + + coord->unit_pos += count; + if (item_length == coord->unit_pos) { + coord->unit_pos --; + coord->between = AFTER_UNIT; + } + + return 0; +} + +/* + plugin->u.item.s.file.append_key + key of first byte which is the next to last byte by addressed by this item +*/ +reiser4_internal reiser4_key * +append_key_tail(const coord_t *coord, reiser4_key *key) +{ + item_key_by_coord(coord, key); + set_key_offset(key, get_key_offset(key) + item_length_by_coord(coord)); + return key; +} + +/* plugin->u.item.s.file.init_coord_extension */ +reiser4_internal void +init_coord_extension_tail(uf_coord_t *uf_coord, loff_t lookuped) +{ + uf_coord->valid = 1; +} + +/* + plugin->u.item.s.file.get_block +*/ +reiser4_internal int +get_block_address_tail(const coord_t *coord, sector_t block, struct buffer_head *bh) +{ + assert("nikita-3252", + znode_get_level(coord->node) == LEAF_LEVEL); + + bh->b_blocknr = *znode_get_block(coord->node); + return 0; +} + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/item/tail.h 2004-09-03 00:57:05.288235328 -0700 @@ -0,0 +1,56 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#if !defined( __REISER4_TAIL_H__ ) +#define __REISER4_TAIL_H__ + +typedef struct { + int not_used; +} tail_coord_extension_t; + +struct cut_list; + + +/* plugin->u.item.b.* */ +reiser4_key *max_key_inside_tail(const coord_t *, reiser4_key *); +int can_contain_key_tail(const coord_t * coord, const reiser4_key * key, const reiser4_item_data *); +int mergeable_tail(const coord_t * p1, const coord_t * p2); +pos_in_node_t nr_units_tail(const coord_t *); +lookup_result lookup_tail(const reiser4_key *, lookup_bias, coord_t *); +int paste_tail(coord_t *, reiser4_item_data *, carry_plugin_info *); +int can_shift_tail(unsigned free_space, coord_t * source, + znode * target, shift_direction, unsigned *size, unsigned want); +void copy_units_tail(coord_t * target, coord_t * source, + unsigned from, unsigned count, shift_direction, unsigned free_space); +int kill_hook_tail(const coord_t *, pos_in_node_t from, pos_in_node_t count, struct carry_kill_data *); +int cut_units_tail(coord_t *, pos_in_node_t from, pos_in_node_t to, + struct carry_cut_data *, reiser4_key *smallest_removed, reiser4_key *new_first); +int kill_units_tail(coord_t *, pos_in_node_t from, pos_in_node_t to, + struct carry_kill_data *, reiser4_key *smallest_removed, reiser4_key *new_first); +reiser4_key *unit_key_tail(const coord_t *, reiser4_key *); + +/* plugin->u.item.s.* */ +int write_tail(struct inode *, flow_t *, hint_t *, int grabbed, write_mode_t); +int read_tail(struct file *, flow_t *, hint_t *); +int readpage_tail(void *vp, struct page *page); +reiser4_key *append_key_tail(const coord_t *, reiser4_key *); +void init_coord_extension_tail(uf_coord_t *, loff_t offset); +int get_block_address_tail(const coord_t *coord, + sector_t block, struct buffer_head *bh); + +void show_tail(struct seq_file *m, coord_t *coord); +int item_balance_dirty_pages(struct address_space *mapping, const flow_t *f, + hint_t *hint, int back_to_dirty, int set_hint); + +/* __REISER4_TAIL_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/name/invterp.c 2004-09-03 00:57:05.288235328 -0700 @@ -0,0 +1,11 @@ +/* Invterp is short for invertable interpolate, and interpolate means to +substitute in. + +Example: + +/filenameA/<> +will resolve to +/filenameA<-`The contents of filenameA' +wherever used. + +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/node/node40.c 2004-09-03 00:57:05.302233200 -0700 @@ -0,0 +1,2860 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/*#include "../../forward.h"*/ +#include "../../debug.h" +#include "../../key.h" +#include "../../coord.h" +#include "../plugin_header.h" +#include "../item/item.h" +#include "node.h" +#include "node40.h" +#include "../plugin.h" +#include "../../jnode.h" +#include "../../znode.h" +#include "../../pool.h" +#include "../../carry.h" +#include "../../tap.h" +#include "../../tree.h" +#include "../../super.h" +#include "../../reiser4.h" + +#include +#include +#include + +/* leaf 40 format: + + [node header | item 0, item 1, .., item N-1 | free space | item_head N-1, .. item_head 1, item head 0 ] + plugin_id (16) key + free_space (16) pluginid (16) + free_space_start (16) offset (16) + level (8) + num_items (16) + magic (32) + flush_time (32) +*/ +/* NIKITA-FIXME-HANS: I told you guys not less than 10 times to not call it r4fs. Change to "ReIs". */ +/* magic number that is stored in ->magic field of node header */ +const __u32 REISER4_NODE_MAGIC = 0x52344653; /* (*(__u32 *)"R4FS"); */ + +static int prepare_for_update(znode * left, znode * right, carry_plugin_info * info); + +/* header of node of reiser40 format is at the beginning of node */ +static inline node40_header * +node40_node_header(const znode * node /* node to + * query */ ) +{ + assert("nikita-567", node != NULL); + assert("nikita-568", znode_page(node) != NULL); + assert("nikita-569", zdata(node) != NULL); + return (node40_header *) zdata(node); +} + +/* functions to get/set fields of node40_header */ + +static __u32 +nh40_get_magic(node40_header * nh) +{ + return d32tocpu(&nh->magic); +} + +static void +nh40_set_magic(node40_header * nh, __u32 magic) +{ + cputod32(magic, &nh->magic); +} + +static void +nh40_set_free_space(node40_header * nh, unsigned value) +{ + cputod16(value, &nh->free_space); + /*node->free_space = value; */ +} + +static inline unsigned +nh40_get_free_space(node40_header * nh) +{ + return d16tocpu(&nh->free_space); +} + +static void +nh40_set_free_space_start(node40_header * nh, unsigned value) +{ + cputod16(value, &nh->free_space_start); +} + +static inline unsigned +nh40_get_free_space_start(node40_header * nh) +{ + return d16tocpu(&nh->free_space_start); +} + +static inline void +nh40_set_level(node40_header * nh, unsigned value) +{ + cputod8(value, &nh->level); +} + +static unsigned +nh40_get_level(node40_header * nh) +{ + return d8tocpu(&nh->level); +} + +static void +nh40_set_num_items(node40_header * nh, unsigned value) +{ + cputod16(value, &nh->nr_items); +} + +static inline unsigned +nh40_get_num_items(node40_header * nh) +{ + return d16tocpu(&nh->nr_items); +} + +static void +nh40_set_mkfs_id(node40_header * nh, __u32 id) +{ + cputod32(id, &nh->mkfs_id); +} + +static inline __u32 +nh40_get_mkfs_id(node40_header * nh) +{ + return d32tocpu(&nh->mkfs_id); +} + +#if 0 +static void +nh40_set_flush_id(node40_header * nh, __u64 id) +{ + cputod64(id, &nh->flush.flush_id); +} +#endif + +static inline __u64 +nh40_get_flush_id(node40_header * nh) +{ + return d64tocpu(&nh->flush_id); +} + +/* plugin field of node header should be read/set by + plugin_by_disk_id/save_disk_plugin */ + +/* array of item headers is at the end of node */ +static inline item_header40 * +node40_ih_at(const znode * node, unsigned pos) +{ + return (item_header40 *) (zdata(node) + znode_size(node)) - pos - 1; +} + +/* ( page_address( node -> pg ) + PAGE_CACHE_SIZE ) - pos - 1 + */ +static inline item_header40 * +node40_ih_at_coord(const coord_t * coord) +{ + return (item_header40 *) (zdata(coord->node) + znode_size(coord->node)) - (coord->item_pos) - 1; +} + +/* functions to get/set fields of item_header40 */ +static void +ih40_set_offset(item_header40 * ih, unsigned offset) +{ + cputod16(offset, &ih->offset); +} + +static inline unsigned +ih40_get_offset(item_header40 * ih) +{ + return d16tocpu(&ih->offset); +} + +/* plugin field of item header should be read/set by + plugin_by_disk_id/save_disk_plugin */ + +/* plugin methods */ + +/* plugin->u.node.item_overhead + look for description of this method in plugin/node/node.h */ +reiser4_internal size_t +item_overhead_node40(const znode * node UNUSED_ARG, flow_t * f UNUSED_ARG) +{ + return sizeof (item_header40); +} + +/* plugin->u.node.free_space + look for description of this method in plugin/node/node.h */ +reiser4_internal size_t free_space_node40(znode * node) +{ + assert("nikita-577", node != NULL); + assert("nikita-578", znode_is_loaded(node)); + assert("nikita-579", zdata(node) != NULL); + trace_stamp(TRACE_NODES); + + return nh40_get_free_space(node40_node_header(node)); +} + +/* private inline version of node40_num_of_items() for use in this file. This + is necessary, because address of node40_num_of_items() is taken and it is + never inlined as a result. */ +static inline short +node40_num_of_items_internal(const znode * node) +{ + trace_stamp(TRACE_NODES); + return nh40_get_num_items(node40_node_header(node)); +} + +#if REISER4_DEBUG +static inline void check_num_items(const znode *node) +{ + assert("nikita-2749", + node40_num_of_items_internal(node) == node->nr_items); + assert("nikita-2746", znode_is_write_locked(node)); +} +#else +#define check_num_items(node) noop +#endif + +/* plugin->u.node.num_of_items + look for description of this method in plugin/node/node.h */ +reiser4_internal int +num_of_items_node40(const znode * node) +{ + trace_stamp(TRACE_NODES); + return node40_num_of_items_internal(node); +} + +static void +node40_set_num_items(znode * node, node40_header * nh, unsigned value) +{ + assert("nikita-2751", node != NULL); + assert("nikita-2750", nh == node40_node_header(node)); + + check_num_items(node); + nh40_set_num_items(nh, value); + node->nr_items = value; + check_num_items(node); +} + +/* plugin->u.node.item_by_coord + look for description of this method in plugin/node/node.h */ +reiser4_internal char * +item_by_coord_node40(const coord_t * coord) +{ + item_header40 *ih; + char *p; + + /* @coord is set to existing item */ + assert("nikita-596", coord != NULL); + assert("vs-255", coord_is_existing_item(coord)); + + ih = node40_ih_at_coord(coord); + p = zdata(coord->node) + ih40_get_offset(ih); + return p; +} + +/* plugin->u.node.length_by_coord + look for description of this method in plugin/node/node.h */ +reiser4_internal int +length_by_coord_node40(const coord_t * coord) +{ + item_header40 *ih; + int result; + + /* @coord is set to existing item */ + assert("vs-256", coord != NULL); + assert("vs-257", coord_is_existing_item(coord)); + + ih = node40_ih_at_coord(coord); + if ((int) coord->item_pos == node40_num_of_items_internal(coord->node) - 1) + result = nh40_get_free_space_start(node40_node_header(coord->node)) - ih40_get_offset(ih); + else + result = ih40_get_offset(ih - 1) - ih40_get_offset(ih); + + return result; +} + +static pos_in_node_t +node40_item_length(const znode *node, pos_in_node_t item_pos) +{ + item_header40 *ih; + pos_in_node_t result; + + /* @coord is set to existing item */ + assert("vs-256", node != NULL); + assert("vs-257", node40_num_of_items_internal(node) > item_pos); + + ih = node40_ih_at(node, item_pos); + if (item_pos == node40_num_of_items_internal(node) - 1) + result = nh40_get_free_space_start(node40_node_header(node)) - ih40_get_offset(ih); + else + result = ih40_get_offset(ih - 1) - ih40_get_offset(ih); + + return result; +} + +/* plugin->u.node.plugin_by_coord + look for description of this method in plugin/node/node.h */ +reiser4_internal item_plugin * +plugin_by_coord_node40(const coord_t * coord) +{ + item_header40 *ih; + item_plugin *result; + + /* @coord is set to existing item */ + assert("vs-258", coord != NULL); + assert("vs-259", coord_is_existing_item(coord)); + + ih = node40_ih_at_coord(coord); + /* pass NULL in stead of current tree. This is time critical call. */ + result = item_plugin_by_disk_id(NULL, &ih->plugin_id); + return result; +} + +/* plugin->u.node.key_at + look for description of this method in plugin/node/node.h */ +reiser4_internal reiser4_key * +key_at_node40(const coord_t * coord, reiser4_key * key) +{ + item_header40 *ih; + + assert("nikita-1765", coord_is_existing_item(coord)); + + /* @coord is set to existing item */ + ih = node40_ih_at_coord(coord); + xmemcpy(key, &ih->key, sizeof (reiser4_key)); + return key; +} + +/* VS-FIXME-HANS: please review whether the below are properly disabled when debugging is disabled */ + +#define NODE_INCSTAT(n, counter) \ + reiser4_stat_inc_at_level(znode_get_level(n), node.lookup.counter) + +#define NODE_ADDSTAT(n, counter, val) \ + reiser4_stat_add_at_level(znode_get_level(n), node.lookup.counter, val) + +/* plugin->u.node.lookup + look for description of this method in plugin/node/node.h */ +reiser4_internal node_search_result +lookup_node40(znode * node /* node to query */ , + const reiser4_key * key /* key to look for */ , + lookup_bias bias /* search bias */ , + coord_t * coord /* resulting coord */ ) +{ + int left; + int right; + int found; + int items; + + item_header40 *lefth; + item_header40 *righth; + + item_plugin *iplug; + item_header40 *bstop; + item_header40 *ih; + cmp_t order; + + assert("nikita-583", node != NULL); + assert("nikita-584", key != NULL); + assert("nikita-585", coord != NULL); + assert("nikita-2693", znode_is_any_locked(node)); + cassert(REISER4_SEQ_SEARCH_BREAK > 2); + + trace_stamp(TRACE_NODES); + + items = node_num_items(node); + NODE_INCSTAT(node, calls); + NODE_ADDSTAT(node, items, items); + + node_check(node, REISER4_NODE_DKEYS); + + if (unlikely(items == 0)) { + coord_init_first_unit(coord, node); + return NS_NOT_FOUND; + } + + /* binary search for item that can contain given key */ + left = 0; + right = items - 1; + coord->node = node; + coord_clear_iplug(coord); + found = 0; + + lefth = node40_ih_at(node, left); + righth = node40_ih_at(node, right); + + /* It is known that for small arrays sequential search is on average + more efficient than binary. This is because sequential search is + coded as tight loop that can be better optimized by compilers and + for small array size gain from this optimization makes sequential + search the winner. Another, maybe more important, reason for this, + is that sequential array is more CPU cache friendly, whereas binary + search effectively destroys CPU caching. + + Critical here is the notion of "smallness". Reasonable value of + REISER4_SEQ_SEARCH_BREAK can be found by playing with code in + fs/reiser4/ulevel/ulevel.c:test_search(). + + Don't try to further optimize sequential search by scanning from + right to left in attempt to use more efficient loop termination + condition (comparison with 0). This doesn't work. + + */ + + while (right - left >= REISER4_SEQ_SEARCH_BREAK) { + int median; + item_header40 *medianh; + + median = (left + right) / 2; + medianh = node40_ih_at(node, median); + + assert("nikita-1084", median >= 0); + assert("nikita-1085", median < items); + NODE_INCSTAT(node, binary); + switch (keycmp(key, &medianh->key)) { + case LESS_THAN: + right = median; + righth = medianh; + break; + default: + wrong_return_value("nikita-586", "keycmp"); + case GREATER_THAN: + left = median; + lefth = medianh; + break; + case EQUAL_TO: + do { + -- median; + /* headers are ordered from right to left */ + ++ medianh; + } while (median >= 0 && keyeq(key, &medianh->key)); + right = left = median + 1; + ih = lefth = righth = medianh - 1; + found = 1; + break; + } + } + /* sequential scan. Item headers, and, therefore, keys are stored at + the rightmost part of a node from right to left. We are trying to + access memory from left to right, and hence, scan in _descending_ + order of item numbers. + */ + if (!found) { + for (left = right, ih = righth; left >= 0; ++ ih, -- left) { + cmp_t comparison; + + NODE_INCSTAT(node, seq); + prefetchkey(&(ih + 1)->key); + comparison = keycmp(&ih->key, key); + if (comparison == GREATER_THAN) + continue; + if (comparison == EQUAL_TO) { + found = 1; + do { + -- left; + ++ ih; + } while (left >= 0 && keyeq(&ih->key, key)); + ++ left; + -- ih; + } else { + assert("nikita-1256", comparison == LESS_THAN); + } + break; + } + if (unlikely(left < 0)) + left = 0; + } + + assert("nikita-3212", right >= left); + assert("nikita-3214", + equi(found, keyeq(&node40_ih_at(node, left)->key, key))); + +#if REISER4_STATS + NODE_ADDSTAT(node, found, !!found); + NODE_ADDSTAT(node, pos, left); + if (items > 1) + NODE_ADDSTAT(node, posrelative, (left << 10) / (items - 1)); + else + NODE_ADDSTAT(node, posrelative, 1 << 10); + if (left == node->last_lookup_pos) + NODE_INCSTAT(node, samepos); + if (left == node->last_lookup_pos + 1) + NODE_INCSTAT(node, nextpos); + node->last_lookup_pos = left; +#endif + + coord_set_item_pos(coord, left); + coord->unit_pos = 0; + coord->between = AT_UNIT; + + /* key < leftmost key in a mode or node is corrupted and keys + are not sorted */ + bstop = node40_ih_at(node, (unsigned) left); + order = keycmp(&bstop->key, key); + if (unlikely(order == GREATER_THAN)) { + if (unlikely(left != 0)) { + /* screw up */ + warning("nikita-587", "Key less than %i key in a node", left); + print_key("key", key); + print_key("min", &bstop->key); + print_znode("node", node); + print_coord_content("coord", coord); + return RETERR(-EIO); + } else { + coord->between = BEFORE_UNIT; + return NS_NOT_FOUND; + } + } + /* left <= key, ok */ + iplug = item_plugin_by_disk_id(znode_get_tree(node), &bstop->plugin_id); + + if (unlikely(iplug == NULL)) { + warning("nikita-588", "Unknown plugin %i", d16tocpu(&bstop->plugin_id)); + print_key("key", key); + print_znode("node", node); + print_coord_content("coord", coord); + return RETERR(-EIO); + } + + coord_set_iplug(coord, iplug); + + /* if exact key from item header was found by binary search, no + further checks are necessary. */ + if (found) { + assert("nikita-1259", order == EQUAL_TO); + return NS_FOUND; + } + if (iplug->b.max_key_inside != NULL) { + reiser4_key max_item_key; + + /* key > max_item_key --- outside of an item */ + if (keygt(key, iplug->b.max_key_inside(coord, &max_item_key))) { + coord->unit_pos = 0; + coord->between = AFTER_ITEM; + /* FIXME-VS: key we are looking for does not fit into + found item. Return NS_NOT_FOUND then. Without that + the following case does not work: there is extent of + file 10000, 10001. File 10000, 10002 has been just + created. When writing to position 0 in that file - + traverse_tree will stop here on twig level. When we + want it to go down to leaf level + */ + return NS_NOT_FOUND; + } + } + + if (iplug->b.lookup != NULL) { + return iplug->b.lookup(key, bias, coord); + } else { + assert("nikita-1260", order == LESS_THAN); + coord->between = AFTER_UNIT; + return (bias == FIND_EXACT) ? NS_NOT_FOUND : NS_FOUND; + } +} + +#undef NODE_ADDSTAT +#undef NODE_INCSTAT + +/* plugin->u.node.estimate + look for description of this method in plugin/node/node.h */ +reiser4_internal size_t estimate_node40(znode * node) +{ + size_t result; + + assert("nikita-597", node != NULL); + + result = free_space_node40(node) - sizeof(item_header40); + + return (result > 0) ? result : 0; +} + +/* plugin->u.node.check + look for description of this method in plugin/node/node.h */ +reiser4_internal int +check_node40(const znode * node /* node to check */ , + __u32 flags /* check flags */ , + const char **error /* where to store error message */ ) +{ + int nr_items; + int i; + reiser4_key prev; + unsigned old_offset; + tree_level level; + coord_t coord; + + assert("nikita-580", node != NULL); + assert("nikita-581", error != NULL); + assert("nikita-2948", znode_is_loaded(node)); + trace_stamp(TRACE_NODES); + + + if (ZF_ISSET(node, JNODE_HEARD_BANSHEE)) + return 0; + + assert("nikita-582", zdata(node) != NULL); + + nr_items = node40_num_of_items_internal(node); + if (nr_items < 0) { + *error = "Negative number of items"; + return -1; + } + + if (flags & REISER4_NODE_DKEYS) + prev = *znode_get_ld_key((znode *)node); + else + prev = *min_key(); + + old_offset = 0; + coord_init_zero(&coord); + coord.node = (znode *) node; + coord.unit_pos = 0; + coord.between = AT_UNIT; + level = znode_get_level(node); + for (i = 0; i < nr_items; i++) { + item_header40 *ih; + reiser4_key unit_key; + unsigned j; + + ih = node40_ih_at(node, (unsigned) i); + coord_set_item_pos(&coord, i); + if ((ih40_get_offset(ih) >= + znode_size(node) - nr_items * sizeof (item_header40)) || + (ih40_get_offset(ih) < sizeof (node40_header))) { + *error = "Offset is out of bounds"; + return -1; + } + if (ih40_get_offset(ih) <= old_offset) { + *error = "Offsets are in wrong order"; + return -1; + } + if ((i == 0) && (ih40_get_offset(ih) != sizeof(node40_header))) { + *error = "Wrong offset of first item"; + return -1; + } + old_offset = ih40_get_offset(ih); + + if (keygt(&prev, &ih->key)) { + *error = "Keys are in wrong order"; + return -1; + } + if (!keyeq(&ih->key, unit_key_by_coord(&coord, &unit_key))) { + *error = "Wrong key of first unit"; + return -1; + } + prev = ih->key; + for (j = 0; j < coord_num_units(&coord); ++j) { + coord.unit_pos = j; + unit_key_by_coord(&coord, &unit_key); + if (keygt(&prev, &unit_key)) { + *error = "Unit keys are in wrong order"; + return -1; + } + prev = unit_key; + } + coord.unit_pos = 0; + if (level != TWIG_LEVEL && + item_is_extent(&coord)) { + *error = "extent on the wrong level"; + return -1; + } + if (level == LEAF_LEVEL && + item_is_internal(&coord)) { + *error = "internal item on the wrong level"; + return -1; + } + if (level != LEAF_LEVEL && + !item_is_internal(&coord) && !item_is_extent(&coord)) { + *error = "wrong item on the internal level"; + return -1; + } + if (level > TWIG_LEVEL && + !item_is_internal(&coord)) { + *error = "non-internal item on the internal level"; + return -1; + } +#if REISER4_DEBUG + if (item_plugin_by_coord(&coord)->b.check && item_plugin_by_coord(&coord)->b.check(&coord, error)) + return -1; +#endif + if (i) { + coord_t prev_coord; + /* two neighboring items can not be mergeable */ + coord_dup(&prev_coord, &coord); + coord_prev_item(&prev_coord); + if (are_items_mergeable(&prev_coord, &coord)) { + *error = "mergeable items in one node"; + return -1; + } + + } + } + + RLOCK_DK(current_tree); + if ((flags & REISER4_NODE_DKEYS) && !node_is_empty(node)) { + coord_t coord; + item_plugin *iplug; + + coord_init_last_unit(&coord, node); + iplug = item_plugin_by_coord(&coord); + if ((item_is_extent(&coord) || item_is_tail(&coord)) && + iplug->s.file.append_key != NULL) { + reiser4_key mkey; + + iplug->s.file.append_key(&coord, &mkey); + set_key_offset(&mkey, get_key_offset(&mkey) - 1); + if (keygt(&mkey, znode_get_rd_key((znode *) node))) { + *error = "key of rightmost item is too large"; + return -1; + } + } + } + if (flags & REISER4_NODE_DKEYS) { + RLOCK_TREE(current_tree); + + flags |= REISER4_NODE_TREE_STABLE; + + if (keygt(&prev, znode_get_rd_key((znode *)node))) { + reiser4_stat_inc(tree.rd_key_skew); + if (flags & REISER4_NODE_TREE_STABLE) { + *error = "Last key is greater than rdkey"; + return -1; + } + } + if (keygt(znode_get_ld_key((znode *)node), znode_get_rd_key((znode *)node))) { + *error = "ldkey is greater than rdkey"; + return -1; + } + if (ZF_ISSET(node, JNODE_LEFT_CONNECTED) && + (node->left != NULL) && + !ZF_ISSET(node->left, JNODE_HEARD_BANSHEE) && + ergo(flags & REISER4_NODE_TREE_STABLE, + !keyeq(znode_get_rd_key(node->left), znode_get_ld_key((znode *)node))) && + ergo(!(flags & REISER4_NODE_TREE_STABLE), keygt(znode_get_rd_key(node->left), znode_get_ld_key((znode *)node)))) { + *error = "left rdkey or ldkey is wrong"; + return -1; + } + if (ZF_ISSET(node, JNODE_RIGHT_CONNECTED) && + (node->right != NULL) && + !ZF_ISSET(node->right, JNODE_HEARD_BANSHEE) && + ergo(flags & REISER4_NODE_TREE_STABLE, + !keyeq(znode_get_rd_key((znode *)node), znode_get_ld_key(node->right))) && + ergo(!(flags & REISER4_NODE_TREE_STABLE), keygt(znode_get_rd_key((znode *)node), znode_get_ld_key(node->right)))) { + *error = "rdkey or right ldkey is wrong"; + return -1; + } + + RUNLOCK_TREE(current_tree); + } + RUNLOCK_DK(current_tree); + + return 0; +} + +/* plugin->u.node.parse + look for description of this method in plugin/node/node.h */ +reiser4_internal int +parse_node40(znode * node /* node to parse */ ) +{ + node40_header *header; + int result; + + header = node40_node_header((znode *) node); + result = -EIO; + if (unlikely(((__u8) znode_get_level(node)) != nh40_get_level(header))) + warning("nikita-494", "Wrong level found in node: %i != %i", + znode_get_level(node), nh40_get_level(header)); + else if (unlikely(nh40_get_magic(header) != REISER4_NODE_MAGIC)) + warning("nikita-495", + "Wrong magic in tree node: want %x, got %x", + REISER4_NODE_MAGIC, nh40_get_magic(header)); + else { + node->nr_items = node40_num_of_items_internal(node); + result = 0; + } + if (unlikely(result != 0)) + /* print_znode("node", node)*/; + return RETERR(result); +} + +/* plugin->u.node.init + look for description of this method in plugin/node/node.h */ +reiser4_internal int +init_node40(znode * node /* node to initialise */ ) +{ + node40_header *header; + + assert("nikita-570", node != NULL); + assert("nikita-572", zdata(node) != NULL); + + header = node40_node_header(node); + if (REISER4_ZERO_NEW_NODE) + xmemset(zdata(node), 0, (unsigned int) znode_size(node)); + else + xmemset(header, 0, sizeof (node40_header)); + nh40_set_free_space(header, znode_size(node) - sizeof (node40_header)); + nh40_set_free_space_start(header, sizeof (node40_header)); + /* sane hypothesis: 0 in CPU format is 0 in disk format */ + /* items: 0 */ + save_plugin_id(node_plugin_to_plugin(node->nplug), &header->common_header.plugin_id); + nh40_set_level(header, znode_get_level(node)); + nh40_set_magic(header, REISER4_NODE_MAGIC); + node->nr_items = 0; + nh40_set_mkfs_id(header, reiser4_mkfs_id(reiser4_get_current_sb())); + + /* flags: 0 */ + return 0; +} + +reiser4_internal int +guess_node40(const znode * node /* node to guess plugin of */ ) +{ + node40_header *nethack; + + assert("nikita-1058", node != NULL); + nethack = node40_node_header(node); + return + (nh40_get_magic(nethack) == REISER4_NODE_MAGIC) && + (plugin_by_disk_id(znode_get_tree(node), + REISER4_NODE_PLUGIN_TYPE, &nethack->common_header.plugin_id)->h.id == NODE40_ID); +} + +#if REISER4_DEBUG_OUTPUT +reiser4_internal void +print_node40(const char *prefix, const znode * node /* node to print */ , + __u32 flags UNUSED_ARG /* print flags */ ) +{ + node40_header *header; + + header = node40_node_header(node); + printk("%s: BLOCKNR %Lu FREE_SPACE %u, LEVEL %u, ITEM_NUMBER %u\n", + prefix, + *znode_get_block(node), nh40_get_free_space(header), nh40_get_level(header), nh40_get_num_items(header)); +} +#endif + +/* plugin->u.node.chage_item_size + look for description of this method in plugin/node/node.h */ +reiser4_internal void +change_item_size_node40(coord_t * coord, int by) +{ + node40_header *nh; + item_header40 *ih; + char *item_data; + int item_length; + unsigned i; + + node_check(coord->node, 0); + + /* make sure that @item is coord of existing item */ + assert("vs-210", coord_is_existing_item(coord)); + + nh = node40_node_header(coord->node); + + item_data = item_by_coord_node40(coord); + item_length = length_by_coord_node40(coord); + + /* move item bodies */ + ih = node40_ih_at_coord(coord); + xmemmove(item_data + item_length + by, item_data + item_length, + nh40_get_free_space_start(node40_node_header(coord->node)) - (ih40_get_offset(ih) + item_length)); + + /* update offsets of moved items */ + for (i = coord->item_pos + 1; i < nh40_get_num_items(nh); i++) { + ih = node40_ih_at(coord->node, i); + ih40_set_offset(ih, ih40_get_offset(ih) + by); + } + + /* update node header */ + nh40_set_free_space(nh, nh40_get_free_space(nh) - by); + nh40_set_free_space_start(nh, nh40_get_free_space_start(nh) + by); +} + +static int +should_notify_parent(const znode * node) +{ + /* FIXME_JMACD This looks equivalent to znode_is_root(), right? -josh */ + return !disk_addr_eq(znode_get_block(node), &znode_get_tree(node)->root_block); +} + +/* plugin->u.node.create_item + look for description of this method in plugin/node/node.h */ +reiser4_internal int +create_item_node40(coord_t * target, const reiser4_key * key, reiser4_item_data * data, carry_plugin_info * info) +{ + node40_header *nh; + item_header40 *ih; + unsigned offset; + unsigned i; + + node_check(target->node, 0); + + nh = node40_node_header(target->node); + + assert("vs-212", coord_is_between_items(target)); + /* node must have enough free space */ + assert("vs-254", free_space_node40(target->node) >= data->length + sizeof(item_header40)); + assert("vs-1410", data->length >= 0); + + if (coord_set_to_right(target)) + /* there are not items to the right of @target, so, new item + will be inserted after last one */ + coord_set_item_pos(target, nh40_get_num_items(nh)); + + if (target->item_pos < nh40_get_num_items(nh)) { + /* there are items to be moved to prepare space for new + item */ + ih = node40_ih_at_coord(target); + /* new item will start at this offset */ + offset = ih40_get_offset(ih); + + xmemmove(zdata(target->node) + offset + data->length, + zdata(target->node) + offset, nh40_get_free_space_start(nh) - offset); + /* update headers of moved items */ + for (i = target->item_pos; i < nh40_get_num_items(nh); i++) { + ih = node40_ih_at(target->node, i); + ih40_set_offset(ih, ih40_get_offset(ih) + data->length); + } + + /* @ih is set to item header of the last item, move item headers */ + xmemmove(ih - 1, ih, sizeof (item_header40) * (nh40_get_num_items(nh) - target->item_pos)); + } else { + /* new item will start at this offset */ + offset = nh40_get_free_space_start(nh); + } + + /* make item header for the new item */ + ih = node40_ih_at_coord(target); + xmemcpy(&ih->key, key, sizeof (reiser4_key)); + ih40_set_offset(ih, offset); + save_plugin_id(item_plugin_to_plugin(data->iplug), &ih->plugin_id); + + /* update node header */ + nh40_set_free_space(nh, nh40_get_free_space(nh) - data->length - sizeof (item_header40)); + nh40_set_free_space_start(nh, nh40_get_free_space_start(nh) + data->length); + node40_set_num_items(target->node, nh, nh40_get_num_items(nh) + 1); + + /* FIXME: check how does create_item work when between is set to BEFORE_UNIT */ + target->unit_pos = 0; + target->between = AT_UNIT; + coord_clear_iplug(target); + + /* initialise item */ + if (data->iplug->b.init != NULL) { + data->iplug->b.init(target, NULL, data); + } + /* copy item body */ + if (data->iplug->b.paste != NULL) { + data->iplug->b.paste(target, data, info); + } else if (data->data != NULL) { + if (data->user) { + /* AUDIT: Are we really should not check that pointer + from userspace was valid and data bytes were + available? How will we return -EFAULT of some kind + without this check? */ + assert("nikita-3038", schedulable()); + /* copy data from user space */ + __copy_from_user(zdata(target->node) + offset, data->data, (unsigned) data->length); + } else + /* copy from kernel space */ + xmemcpy(zdata(target->node) + offset, data->data, (unsigned) data->length); + } + + if (target->item_pos == 0) { + /* left delimiting key has to be updated */ + prepare_for_update(NULL, target->node, info); + } + + if (item_plugin_by_coord(target)->b.create_hook != NULL) { + item_plugin_by_coord(target)->b.create_hook(target, data->arg); + } + + node_check(target->node, 0); + return 0; +} + +/* plugin->u.node.update_item_key + look for description of this method in plugin/node/node.h */ +reiser4_internal void +update_item_key_node40(coord_t * target, const reiser4_key * key, carry_plugin_info * info) +{ + item_header40 *ih; + + ih = node40_ih_at_coord(target); + xmemcpy(&ih->key, key, sizeof (reiser4_key)); + + if (target->item_pos == 0) { + prepare_for_update(NULL, target->node, info); + } +} + +/* this bits encode cut mode */ +#define CMODE_TAIL 1 +#define CMODE_WHOLE 2 +#define CMODE_HEAD 4 + +struct cut40_info { + int mode; + pos_in_node_t tail_removed; /* position of item which gets tail removed */ + pos_in_node_t first_removed; /* position of first the leftmost item among items removed completely */ + pos_in_node_t removed_count; /* number of items removed completely */ + pos_in_node_t head_removed; /* position of item which gets head removed */ + + pos_in_node_t freed_space_start; + pos_in_node_t freed_space_end; + pos_in_node_t first_moved; + pos_in_node_t head_removed_location; +}; + +static void +init_cinfo(struct cut40_info *cinfo) +{ + cinfo->mode = 0; + cinfo->tail_removed = MAX_POS_IN_NODE; + cinfo->first_removed = MAX_POS_IN_NODE; + cinfo->removed_count = MAX_POS_IN_NODE; + cinfo->head_removed = MAX_POS_IN_NODE; + cinfo->freed_space_start = MAX_POS_IN_NODE; + cinfo->freed_space_end = MAX_POS_IN_NODE; + cinfo->first_moved = MAX_POS_IN_NODE; + cinfo->head_removed_location = MAX_POS_IN_NODE; +} + +/* complete cut_node40/kill_node40 content by removing the gap created by */ +static void +compact(znode *node, struct cut40_info *cinfo) +{ + node40_header *nh; + item_header40 *ih; + pos_in_node_t freed; + pos_in_node_t pos, nr_items; + + assert("vs-1526", (cinfo->freed_space_start != MAX_POS_IN_NODE && + cinfo->freed_space_end != MAX_POS_IN_NODE && + cinfo->first_moved != MAX_POS_IN_NODE)); + assert("vs-1523", cinfo->freed_space_end >= cinfo->freed_space_start); + + nh = node40_node_header(node); + nr_items = nh40_get_num_items(nh); + + /* remove gap made up by removal */ + xmemmove(zdata(node) + cinfo->freed_space_start, zdata(node) + cinfo->freed_space_end, + nh40_get_free_space_start(nh) - cinfo->freed_space_end); + + /* update item headers of moved items - change their locations */ + pos = cinfo->first_moved; + ih = node40_ih_at(node, pos); + if (cinfo->head_removed_location != MAX_POS_IN_NODE) { + assert("vs-1580", pos == cinfo->head_removed); + ih40_set_offset(ih, cinfo->head_removed_location); + pos ++; + ih --; + } + + freed = cinfo->freed_space_end - cinfo->freed_space_start; + for (; pos < nr_items; pos ++, ih --) { + assert("vs-1581", ih == node40_ih_at(node, pos)); + ih40_set_offset(ih, ih40_get_offset(ih) - freed); + } + + /* free space start moved to right */ + nh40_set_free_space_start(nh, nh40_get_free_space_start(nh) - freed); + + if (cinfo->removed_count != MAX_POS_IN_NODE) { + /* number of items changed. Remove item headers of those items */ + ih = node40_ih_at(node, nr_items - 1); + xmemmove(ih + cinfo->removed_count, ih, + sizeof (item_header40) * (nr_items - cinfo->removed_count - cinfo->first_removed)); + freed += sizeof (item_header40) * cinfo->removed_count; + node40_set_num_items(node, nh, nr_items - cinfo->removed_count); + } + + /* total amount of free space increased */ + nh40_set_free_space(nh, nh40_get_free_space(nh) + freed); +} + +reiser4_internal int +shrink_item_node40(coord_t *coord, int delta) +{ + node40_header *nh; + item_header40 *ih; + pos_in_node_t pos; + pos_in_node_t nr_items; + char *end; + znode *node; + + assert("nikita-3487", coord != NULL); + assert("nikita-3488", delta >= 0); + + node = coord->node; + node_check(node, 0); + nh = node40_node_header(node); + nr_items = nh40_get_num_items(nh); + + ih = node40_ih_at_coord(coord); + assert("nikita-3489", delta <= length_by_coord_node40(coord)); + end = zdata(node) + ih40_get_offset(ih) + length_by_coord_node40(coord); + + /* remove gap made up by removal */ + xmemmove(end - delta, end, nh40_get_free_space_start(nh) - delta); + + /* update item headers of moved items - change their locations */ + pos = coord->item_pos + 1; + ih = node40_ih_at(node, pos); + for (; pos < nr_items; pos ++, ih --) { + assert("nikita-3490", ih == node40_ih_at(node, pos)); + ih40_set_offset(ih, ih40_get_offset(ih) - delta); + } + + /* free space start moved to left */ + nh40_set_free_space_start(nh, nh40_get_free_space_start(nh) - delta); + /* total amount of free space increased */ + nh40_set_free_space(nh, nh40_get_free_space(nh) + delta); + /* + * This method does _not_ changes number of items. Hence, it cannot + * make node empty. Also it doesn't remove items at all, which means + * that no keys have to be updated either. + */ + return 0; +} + + +/* this is used by cut_node40 and kill_node40. It analyses input parameters and calculates cut mode. There are 2 types + of cut. First is when a unit is removed from the middle of an item. In this case this function returns 1. All the + rest fits into second case: 0 or 1 of items getting tail cut, 0 or more items removed completely and 0 or 1 item + getting head cut. Function returns 0 in this case */ +static int +parse_cut(struct cut40_info *cinfo, const struct cut_kill_params *params) +{ + reiser4_key left_key, right_key; + reiser4_key min_from_key, max_to_key; + const reiser4_key *from_key, *to_key; + + init_cinfo(cinfo); + + /* calculate minimal key stored in first item of items to be cut (params->from) */ + item_key_by_coord(params->from, &min_from_key); + /* and max key stored in last item of items to be cut (params->to) */ + max_item_key_by_coord(params->to, &max_to_key); + + /* if cut key range is not defined in input parameters - define it using cut coord range */ + if (params->from_key == NULL) { + assert("vs-1513", params->to_key == NULL); + unit_key_by_coord(params->from, &left_key); + from_key = &left_key; + max_unit_key_by_coord(params->to, &right_key); + to_key = &right_key; + } else { + from_key = params->from_key; + to_key = params->to_key; + } + + if (params->from->item_pos == params->to->item_pos) { + if (keylt(&min_from_key, from_key) && keylt(to_key, &max_to_key)) + return 1; + + if (keygt(from_key, &min_from_key)) { + /* tail of item is to be cut cut */ + cinfo->tail_removed = params->from->item_pos; + cinfo->mode |= CMODE_TAIL; + } else if (keylt(to_key, &max_to_key)) { + /* head of item is to be cut */ + cinfo->head_removed = params->from->item_pos; + cinfo->mode |= CMODE_HEAD; + } else { + /* item is removed completely */ + cinfo->first_removed = params->from->item_pos; + cinfo->removed_count = 1; + cinfo->mode |= CMODE_WHOLE; + } + } else { + cinfo->first_removed = params->from->item_pos + 1; + cinfo->removed_count = params->to->item_pos - params->from->item_pos - 1; + + if (keygt(from_key, &min_from_key)) { + /* first item is not cut completely */ + cinfo->tail_removed = params->from->item_pos; + cinfo->mode |= CMODE_TAIL; + } else { + cinfo->first_removed --; + cinfo->removed_count ++; + } + if (keylt(to_key, &max_to_key)) { + /* last item is not cut completely */ + cinfo->head_removed = params->to->item_pos; + cinfo->mode |= CMODE_HEAD; + } else { + cinfo->removed_count ++; + } + if (cinfo->removed_count) + cinfo->mode |= CMODE_WHOLE; + } + + return 0; +} + +static void +call_kill_hooks(znode *node, pos_in_node_t from, pos_in_node_t count, carry_kill_data *kdata) +{ + coord_t coord; + item_plugin *iplug; + pos_in_node_t pos; + + coord.node = node; + coord.unit_pos = 0; + coord.between = AT_UNIT; + for (pos = 0; pos < count; pos ++) { + coord_set_item_pos(&coord, from + pos); + coord.unit_pos = 0; + coord.between = AT_UNIT; + iplug = item_plugin_by_coord(&coord); + if (iplug->b.kill_hook) { + iplug->b.kill_hook(&coord, 0, coord_num_units(&coord), kdata); + } + } +} + +/* this is used to kill item partially */ +static pos_in_node_t +kill_units(coord_t *coord, pos_in_node_t from, pos_in_node_t to, void *data, reiser4_key *smallest_removed, + reiser4_key *new_first_key) +{ + struct carry_kill_data *kdata; + item_plugin *iplug; + + kdata = data; + iplug = item_plugin_by_coord(coord); + + assert("vs-1524", iplug->b.kill_units); + return iplug->b.kill_units(coord, from, to, kdata, smallest_removed, new_first_key); +} + +/* call item plugin to cut tail of file */ +static pos_in_node_t +kill_tail(coord_t *coord, void *data, reiser4_key *smallest_removed) +{ + struct carry_kill_data *kdata; + pos_in_node_t to; + + kdata = data; + to = coord_last_unit_pos(coord); + return kill_units(coord, coord->unit_pos, to, kdata, smallest_removed, 0); +} + +/* call item plugin to cut head of item */ +static pos_in_node_t +kill_head(coord_t *coord, void *data, reiser4_key *smallest_removed, reiser4_key *new_first_key) +{ + return kill_units(coord, 0, coord->unit_pos, data, smallest_removed, new_first_key); +} + +/* this is used to cut item partially */ +static pos_in_node_t +cut_units(coord_t *coord, pos_in_node_t from, pos_in_node_t to, void *data, + reiser4_key *smallest_removed, reiser4_key *new_first_key) +{ + carry_cut_data *cdata; + item_plugin *iplug; + + cdata = data; + iplug = item_plugin_by_coord(coord); + assert("vs-302", iplug->b.cut_units); + return iplug->b.cut_units(coord, from, to, cdata, smallest_removed, new_first_key); +} + +/* call item plugin to cut tail of file */ +static pos_in_node_t +cut_tail(coord_t *coord, void *data, reiser4_key *smallest_removed) +{ + carry_cut_data *cdata; + pos_in_node_t to; + + cdata = data; + to = coord_last_unit_pos(cdata->params.from); + return cut_units(coord, coord->unit_pos, to, data, smallest_removed, 0); +} + +/* call item plugin to cut head of item */ +static pos_in_node_t +cut_head(coord_t *coord, void *data, reiser4_key *smallest_removed, reiser4_key *new_first_key) +{ + return cut_units(coord, 0, coord->unit_pos, data, smallest_removed, new_first_key); +} + +/* this returns 1 of key of first item changed, 0 - if it did not */ +static int +prepare_for_compact(struct cut40_info *cinfo, const struct cut_kill_params *params, int is_cut, + void *data, carry_plugin_info *info) +{ + znode *node; + item_header40 *ih; + pos_in_node_t freed; + pos_in_node_t item_pos; + coord_t coord; + reiser4_key new_first_key; + pos_in_node_t (*kill_units_f)(coord_t *, pos_in_node_t, pos_in_node_t, void *, reiser4_key *, reiser4_key *); + pos_in_node_t (*kill_tail_f)(coord_t *, void *, reiser4_key *); + pos_in_node_t (*kill_head_f)(coord_t *, void *, reiser4_key *, reiser4_key *); + int retval; + + retval = 0; + + node = params->from->node; + + assert("vs-184", node == params->to->node); + assert("vs-312", !node_is_empty(node)); + assert("vs-297", coord_compare(params->from, params->to) != COORD_CMP_ON_RIGHT); + + if (is_cut) { + kill_units_f = cut_units; + kill_tail_f = cut_tail; + kill_head_f = cut_head; + } else { + kill_units_f = kill_units; + kill_tail_f = kill_tail; + kill_head_f = kill_head; + } + + if (parse_cut(cinfo, params) == 1) { + /* cut from the middle of item */ + freed = kill_units_f(params->from, params->from->unit_pos, params->to->unit_pos, data, params->smallest_removed, NULL); + + item_pos = params->from->item_pos; + ih = node40_ih_at(node, item_pos); + cinfo->freed_space_start = ih40_get_offset(ih) + node40_item_length(node, item_pos) - freed; + cinfo->freed_space_end = cinfo->freed_space_start + freed; + cinfo->first_moved = item_pos + 1; + } else { + assert("vs-1521", (cinfo->tail_removed != MAX_POS_IN_NODE || + cinfo->first_removed != MAX_POS_IN_NODE || + cinfo->head_removed != MAX_POS_IN_NODE)); + + switch (cinfo->mode) { + case CMODE_TAIL: + /* one item gets cut partially from its end */ + assert("vs-1562", cinfo->tail_removed == params->from->item_pos); + + freed = kill_tail_f(params->from, data, params->smallest_removed); + + item_pos = cinfo->tail_removed; + ih = node40_ih_at(node, item_pos); + cinfo->freed_space_start = ih40_get_offset(ih) + node40_item_length(node, item_pos) - freed; + cinfo->freed_space_end = cinfo->freed_space_start + freed; + cinfo->first_moved = cinfo->tail_removed + 1; + break; + + case CMODE_WHOLE: + /* one or more items get removed completely */ + assert("vs-1563", cinfo->first_removed == params->from->item_pos); + assert("vs-1564", cinfo->removed_count > 0 && cinfo->removed_count != MAX_POS_IN_NODE); + + /* call kill hook for all items removed completely */ + if (is_cut == 0) + call_kill_hooks(node, cinfo->first_removed, cinfo->removed_count, data); + + item_pos = cinfo->first_removed; + ih = node40_ih_at(node, item_pos); + + if (params->smallest_removed) + xmemcpy(params->smallest_removed, &ih->key, sizeof (reiser4_key)); + + cinfo->freed_space_start = ih40_get_offset(ih); + + item_pos += (cinfo->removed_count - 1); + ih -= (cinfo->removed_count - 1); + cinfo->freed_space_end = ih40_get_offset(ih) + node40_item_length(node, item_pos); + cinfo->first_moved = item_pos + 1; + if (cinfo->first_removed == 0) + /* key of first item of the node changes */ + retval = 1; + break; + + case CMODE_HEAD: + /* one item gets cut partially from its head */ + assert("vs-1565", cinfo->head_removed == params->from->item_pos); + + freed = kill_head_f(params->to, data, params->smallest_removed, &new_first_key); + + item_pos = cinfo->head_removed; + ih = node40_ih_at(node, item_pos); + cinfo->freed_space_start = ih40_get_offset(ih); + cinfo->freed_space_end = ih40_get_offset(ih) + freed; + cinfo->first_moved = cinfo->head_removed + 1; + + /* item head is removed, therefore, item key changed */ + coord.node = node; + coord_set_item_pos(&coord, item_pos); + coord.unit_pos = 0; + coord.between = AT_UNIT; + update_item_key_node40(&coord, &new_first_key, 0); + if (item_pos == 0) + /* key of first item of the node changes */ + retval = 1; + break; + + case CMODE_TAIL | CMODE_WHOLE: + /* one item gets cut from its end and one or more items get removed completely */ + assert("vs-1566", cinfo->tail_removed == params->from->item_pos); + assert("vs-1567", cinfo->first_removed == cinfo->tail_removed + 1); + assert("vs-1564", cinfo->removed_count > 0 && cinfo->removed_count != MAX_POS_IN_NODE); + + freed = kill_tail_f(params->from, data, params->smallest_removed); + + item_pos = cinfo->tail_removed; + ih = node40_ih_at(node, item_pos); + cinfo->freed_space_start = ih40_get_offset(ih) + node40_item_length(node, item_pos) - freed; + + /* call kill hook for all items removed completely */ + if (is_cut == 0) + call_kill_hooks(node, cinfo->first_removed, cinfo->removed_count, data); + + item_pos += cinfo->removed_count; + ih -= cinfo->removed_count; + cinfo->freed_space_end = ih40_get_offset(ih) + node40_item_length(node, item_pos); + cinfo->first_moved = item_pos + 1; + break; + + case CMODE_WHOLE | CMODE_HEAD: + /* one or more items get removed completely and one item gets cut partially from its head */ + assert("vs-1568", cinfo->first_removed == params->from->item_pos); + assert("vs-1564", cinfo->removed_count > 0 && cinfo->removed_count != MAX_POS_IN_NODE); + assert("vs-1569", cinfo->head_removed == cinfo->first_removed + cinfo->removed_count); + + /* call kill hook for all items removed completely */ + if (is_cut == 0) + call_kill_hooks(node, cinfo->first_removed, cinfo->removed_count, data); + + item_pos = cinfo->first_removed; + ih = node40_ih_at(node, item_pos); + + if (params->smallest_removed) + xmemcpy(params->smallest_removed, &ih->key, sizeof (reiser4_key)); + + freed = kill_head_f(params->to, data, 0, &new_first_key); + + cinfo->freed_space_start = ih40_get_offset(ih); + + ih = node40_ih_at(node, cinfo->head_removed); + /* this is the most complex case. Item which got head removed and items which are to be moved + intact change their location differently. */ + cinfo->freed_space_end = ih40_get_offset(ih) + freed; + cinfo->first_moved = cinfo->head_removed; + cinfo->head_removed_location = cinfo->freed_space_start; + + /* item head is removed, therefore, item key changed */ + coord.node = node; + coord_set_item_pos(&coord, cinfo->head_removed); + coord.unit_pos = 0; + coord.between = AT_UNIT; + update_item_key_node40(&coord, &new_first_key, 0); + + assert("vs-1579", cinfo->first_removed == 0); + /* key of first item of the node changes */ + retval = 1; + break; + + case CMODE_TAIL | CMODE_HEAD: + /* one item get cut from its end and its neighbor gets cut from its tail */ + impossible("vs-1576", "this can not happen currently"); + break; + + case CMODE_TAIL | CMODE_WHOLE | CMODE_HEAD: + impossible("vs-1577", "this can not happen currently"); + break; + default: + impossible("vs-1578", "unexpected cut mode"); + break; + } + } + return retval; +} + + +/* plugin->u.node.kill + return value is number of items removed completely */ +int +kill_node40(struct carry_kill_data *kdata, carry_plugin_info *info) +{ + znode *node; + struct cut40_info cinfo; + int first_key_changed; + + node = kdata->params.from->node; + node_check(node, 0); + + first_key_changed = prepare_for_compact(&cinfo, &kdata->params, 0/* not cut */, kdata, info); + compact(node, &cinfo); + + if (info) { + /* it is not called by node40_shift, so we have to take care + of changes on upper levels */ + if (node_is_empty(node) && !(kdata->flags & DELETE_RETAIN_EMPTY)) + /* all contents of node is deleted */ + prepare_removal_node40(node, info); + else if (first_key_changed) { + prepare_for_update(NULL, node, info); + } + } + + coord_clear_iplug(kdata->params.from); + coord_clear_iplug(kdata->params.to); + + node_check(node, 0); + znode_make_dirty(node); + return cinfo.removed_count == MAX_POS_IN_NODE ? 0 : cinfo.removed_count; +} + +/* plugin->u.node.cut + return value is number of items removed completely */ +int +cut_node40(struct carry_cut_data *cdata, carry_plugin_info *info) +{ + znode *node; + struct cut40_info cinfo; + int first_key_changed; + + node = cdata->params.from->node; + node_check(node, 0); + + first_key_changed = prepare_for_compact(&cinfo, &cdata->params, 1/* not cut */, cdata, info); + compact(node, &cinfo); + + if (info) { + /* it is not called by node40_shift, so we have to take care + of changes on upper levels */ + if (node_is_empty(node)) + /* all contents of node is deleted */ + prepare_removal_node40(node, info); + else if (first_key_changed) { + prepare_for_update(NULL, node, info); + } + } + + coord_clear_iplug(cdata->params.from); + coord_clear_iplug(cdata->params.to); + + node_check(node, 0); + znode_make_dirty(node); + return cinfo.removed_count == MAX_POS_IN_NODE ? 0 : cinfo.removed_count ; +} + + +/* this structure is used by shift method of node40 plugin */ +struct shift_params { + shift_direction pend; /* when @pend == append - we are shifting to + left, when @pend == prepend - to right */ + coord_t wish_stop; /* when shifting to left this is last unit we + want shifted, when shifting to right - this + is set to unit we want to start shifting + from */ + znode *target; + int everything; /* it is set to 1 if everything we have to shift is + shifted, 0 - otherwise */ + + /* FIXME-VS: get rid of read_stop */ + + /* these are set by estimate_shift */ + coord_t real_stop; /* this will be set to last unit which will be + really shifted */ + + /* coordinate in source node before operation of unit which becomes + first after shift to left of last after shift to right */ + union { + coord_t future_first; + coord_t future_last; + } u; + + unsigned merging_units; /* number of units of first item which have to + be merged with last item of target node */ + unsigned merging_bytes; /* number of bytes in those units */ + + unsigned entire; /* items shifted in their entirety */ + unsigned entire_bytes; /* number of bytes in those items */ + + unsigned part_units; /* number of units of partially copied item */ + unsigned part_bytes; /* number of bytes in those units */ + + unsigned shift_bytes; /* total number of bytes in items shifted (item + headers not included) */ + +}; + +static int +item_creation_overhead(coord_t * item) +{ + return node_plugin_by_coord(item)->item_overhead(item->node, 0); +} + +/* how many units are there in @source starting from source->unit_pos + but not further than @stop_coord */ +static int +wanted_units(coord_t * source, coord_t * stop_coord, shift_direction pend) +{ + if (pend == SHIFT_LEFT) { + assert("vs-181", source->unit_pos == 0); + } else { + assert("vs-182", source->unit_pos == coord_last_unit_pos(source)); + } + + if (source->item_pos != stop_coord->item_pos) { + /* @source and @stop_coord are different items */ + return coord_last_unit_pos(source) + 1; + } + + if (pend == SHIFT_LEFT) { + return stop_coord->unit_pos + 1; + } else { + return source->unit_pos - stop_coord->unit_pos + 1; + } +} + +/* this calculates what can be copied from @shift->wish_stop.node to + @shift->target */ +static void +estimate_shift(struct shift_params *shift, const reiser4_context *ctx) +{ + unsigned target_free_space, size; + pos_in_node_t stop_item; /* item which estimating should not consider */ + unsigned want; /* number of units of item we want shifted */ + coord_t source; /* item being estimated */ + item_plugin *iplug; + + /* shifting to left/right starts from first/last units of + @shift->wish_stop.node */ + if (shift->pend == SHIFT_LEFT) { + coord_init_first_unit(&source, shift->wish_stop.node); + } else { + coord_init_last_unit(&source, shift->wish_stop.node); + } + shift->real_stop = source; + + /* free space in target node and number of items in source */ + target_free_space = znode_free_space(shift->target); + + shift->everything = 0; + if (!node_is_empty(shift->target)) { + /* target node is not empty, check for boundary items + mergeability */ + coord_t to; + + /* item we try to merge @source with */ + if (shift->pend == SHIFT_LEFT) { + coord_init_last_unit(&to, shift->target); + } else { + coord_init_first_unit(&to, shift->target); + } + + if ((shift->pend == SHIFT_LEFT) ? are_items_mergeable(&to, &source) : are_items_mergeable(&source, &to)) { + /* how many units of @source do we want to merge to + item @to */ + want = wanted_units(&source, &shift->wish_stop, shift->pend); + + /* how many units of @source we can merge to item + @to */ + iplug = item_plugin_by_coord(&source); + if (iplug->b.can_shift != NULL) + shift->merging_units = + iplug->b.can_shift(target_free_space, + &source, shift->target, shift->pend, &size, want); + else { + shift->merging_units = 0; + size = 0; + } + shift->merging_bytes = size; + shift->shift_bytes += size; + /* update stop coord to be set to last unit of @source + we can merge to @target */ + if (shift->merging_units) + /* at least one unit can be shifted */ + shift->real_stop.unit_pos = (shift->merging_units - source.unit_pos - 1) * shift->pend; + else { + /* nothing can be shifted */ + if (shift->pend == SHIFT_LEFT) + coord_init_before_first_item(&shift->real_stop, source.node); + else + coord_init_after_last_item(&shift->real_stop, source.node); + } + assert("nikita-2081", shift->real_stop.unit_pos + 1); + + if (shift->merging_units != want) { + /* we could not copy as many as we want, so, + there is no reason for estimating any + longer */ + return; + } + + target_free_space -= size; + coord_add_item_pos(&source, shift->pend); + } + } + + /* number of item nothing of which we want to shift */ + stop_item = shift->wish_stop.item_pos + shift->pend; + + /* calculate how many items can be copied into given free + space as whole */ + for (; source.item_pos != stop_item; coord_add_item_pos(&source, shift->pend)) { + if (shift->pend == SHIFT_RIGHT) + source.unit_pos = coord_last_unit_pos(&source); + + /* how many units of @source do we want to copy */ + want = wanted_units(&source, &shift->wish_stop, shift->pend); + + if (want == coord_last_unit_pos(&source) + 1) { + /* we want this item to be copied entirely */ + size = item_length_by_coord(&source) + item_creation_overhead(&source); + if (size <= target_free_space) { + /* item fits into target node as whole */ + target_free_space -= size; + shift->shift_bytes += size - item_creation_overhead(&source); + shift->entire_bytes += size - item_creation_overhead(&source); + shift->entire++; + + /* update shift->real_stop coord to be set to + last unit of @source we can merge to + @target */ + shift->real_stop = source; + if (shift->pend == SHIFT_LEFT) + shift->real_stop.unit_pos = coord_last_unit_pos(&shift->real_stop); + else + shift->real_stop.unit_pos = 0; + continue; + } + } + + /* we reach here only for an item which does not fit into + target node in its entirety. This item may be either + partially shifted, or not shifted at all. We will have to + create new item in target node, so decrease amout of free + space by an item creation overhead. We can reach here also + if stop coord is in this item */ + if (target_free_space >= (unsigned) item_creation_overhead(&source)) { + target_free_space -= item_creation_overhead(&source); + iplug = item_plugin_by_coord(&source); + if (iplug->b.can_shift) { + shift->part_units = iplug->b.can_shift(target_free_space, &source, 0 /*target */ + , shift->pend, &size, want); + } else { + target_free_space = 0; + shift->part_units = 0; + size = 0; + } + } else { + target_free_space = 0; + shift->part_units = 0; + size = 0; + } + shift->part_bytes = size; + shift->shift_bytes += size; + + /* set @shift->real_stop to last unit of @source we can merge + to @shift->target */ + if (shift->part_units) { + shift->real_stop = source; + shift->real_stop.unit_pos = (shift->part_units - source.unit_pos - 1) * shift->pend; + assert("nikita-2082", shift->real_stop.unit_pos + 1); + } + + if (want != shift->part_units) + /* not everything wanted were shifted */ + return; + break; + } + + shift->everything = 1; +} + +static void +copy_units(coord_t * target, coord_t * source, unsigned from, unsigned count, shift_direction dir, unsigned free_space) +{ + item_plugin *iplug; + + assert("nikita-1463", target != NULL); + assert("nikita-1464", source != NULL); + assert("nikita-1465", from + count <= coord_num_units(source)); + + IF_TRACE(TRACE_COORDS, print_coord("copy_units source:", source, 0)); + + iplug = item_plugin_by_coord(source); + assert("nikita-1468", iplug == item_plugin_by_coord(target)); + iplug->b.copy_units(target, source, from, count, dir, free_space); + + if (dir == SHIFT_RIGHT) { + /* FIXME-VS: this looks not necessary. update_item_key was + called already by copy_units method */ + reiser4_key split_key; + + assert("nikita-1469", target->unit_pos == 0); + + unit_key_by_coord(target, &split_key); + node_plugin_by_coord(target)->update_item_key(target, &split_key, 0); + } +} + +/* copy part of @shift->real_stop.node starting either from its beginning or + from its end and ending at @shift->real_stop to either the end or the + beginning of @shift->target */ +static void +copy(struct shift_params *shift) +{ + node40_header *nh; + coord_t from; + coord_t to; + item_header40 *from_ih, *to_ih; + int free_space_start; + int new_items; + unsigned old_items; + int old_offset; + unsigned i; + + nh = node40_node_header(shift->target); + free_space_start = nh40_get_free_space_start(nh); + old_items = nh40_get_num_items(nh); + new_items = shift->entire + (shift->part_units ? 1 : 0); + assert("vs-185", shift->shift_bytes == shift->merging_bytes + shift->entire_bytes + shift->part_bytes); + + from = shift->wish_stop; + + IF_TRACE(TRACE_COORDS, print_coord("node40_copy from:", &from, 0)); + + coord_init_first_unit(&to, shift->target); + + /* NOTE:NIKITA->VS not sure what I am doing: shift->target is empty, + hence to.between is set to EMPTY_NODE above. Looks like we want it + to be AT_UNIT. + + Oh, wonders of ->betweeness... + + */ + to.between = AT_UNIT; + + if (shift->pend == SHIFT_LEFT) { + /* copying to left */ + + coord_set_item_pos(&from, 0); + from_ih = node40_ih_at(from.node, 0); + + coord_set_item_pos(&to, node40_num_of_items_internal(to.node) - 1); + if (shift->merging_units) { + /* expand last item, so that plugin methods will see + correct data */ + free_space_start += shift->merging_bytes; + nh40_set_free_space_start(nh, (unsigned) free_space_start); + nh40_set_free_space(nh, nh40_get_free_space(nh) - shift->merging_bytes); + + IF_TRACE(TRACE_COORDS, print_coord("before copy_units from:", &from, 0)); + IF_TRACE(TRACE_COORDS, print_coord("before copy_units to:", &to, 0)); + + /* appending last item of @target */ + copy_units(&to, &from, 0, /* starting from 0-th unit */ + shift->merging_units, SHIFT_LEFT, shift->merging_bytes); + coord_inc_item_pos(&from); + from_ih--; + coord_inc_item_pos(&to); + } + + to_ih = node40_ih_at(shift->target, old_items); + if (shift->entire) { + /* copy @entire items entirely */ + + /* copy item headers */ + xmemcpy(to_ih - shift->entire + 1, + from_ih - shift->entire + 1, shift->entire * sizeof (item_header40)); + /* update item header offset */ + old_offset = ih40_get_offset(from_ih); + /* AUDIT: Looks like if we calculate old_offset + free_space_start here instead of just old_offset, we can perform one "add" operation less per each iteration */ + for (i = 0; i < shift->entire; i++, to_ih--, from_ih--) + ih40_set_offset(to_ih, ih40_get_offset(from_ih) - old_offset + free_space_start); + + /* copy item bodies */ + xmemcpy(zdata(shift->target) + free_space_start, zdata(from.node) + old_offset, /*ih40_get_offset (from_ih), */ + shift->entire_bytes); + + coord_add_item_pos(&from, (int) shift->entire); + coord_add_item_pos(&to, (int) shift->entire); + } + + nh40_set_free_space_start(nh, free_space_start + shift->shift_bytes - shift->merging_bytes); + nh40_set_free_space(nh, + nh40_get_free_space(nh) - + (shift->shift_bytes - shift->merging_bytes + sizeof (item_header40) * new_items)); + + /* update node header */ + node40_set_num_items(shift->target, nh, old_items + new_items); + assert("vs-170", nh40_get_free_space(nh) < znode_size(shift->target)); + + if (shift->part_units) { + /* copy heading part (@part units) of @source item as + a new item into @target->node */ + + /* copy item header of partially copied item */ + coord_set_item_pos(&to, node40_num_of_items_internal(to.node) + - 1); + xmemcpy(to_ih, from_ih, sizeof (item_header40)); + ih40_set_offset(to_ih, nh40_get_free_space_start(nh) - shift->part_bytes); + if (item_plugin_by_coord(&to)->b.init) + item_plugin_by_coord(&to)->b.init(&to, &from, 0); + copy_units(&to, &from, 0, shift->part_units, SHIFT_LEFT, shift->part_bytes); + } + + } else { + /* copying to right */ + + coord_set_item_pos(&from, node40_num_of_items_internal(from.node) - 1); + from_ih = node40_ih_at_coord(&from); + + coord_set_item_pos(&to, 0); + + /* prepare space for new items */ + xmemmove(zdata(to.node) + sizeof (node40_header) + + shift->shift_bytes, + zdata(to.node) + sizeof (node40_header), free_space_start - sizeof (node40_header)); + /* update item headers of moved items */ + to_ih = node40_ih_at(to.node, 0); + /* first item gets @merging_bytes longer. free space appears + at its beginning */ + if (!node_is_empty(to.node)) + ih40_set_offset(to_ih, ih40_get_offset(to_ih) + shift->shift_bytes - shift->merging_bytes); + + for (i = 1; i < old_items; i++) + ih40_set_offset(to_ih - i, ih40_get_offset(to_ih - i) + shift->shift_bytes); + + /* move item headers to make space for new items */ + xmemmove(to_ih - old_items + 1 - new_items, to_ih - old_items + 1, sizeof (item_header40) * old_items); + to_ih -= (new_items - 1); + + nh40_set_free_space_start(nh, free_space_start + shift->shift_bytes); + nh40_set_free_space(nh, + nh40_get_free_space(nh) - + (shift->shift_bytes + sizeof (item_header40) * new_items)); + + /* update node header */ + node40_set_num_items(shift->target, nh, old_items + new_items); + assert("vs-170", nh40_get_free_space(nh) < znode_size(shift->target)); + + if (shift->merging_units) { + coord_add_item_pos(&to, new_items); + to.unit_pos = 0; + to.between = AT_UNIT; + /* prepend first item of @to */ + copy_units(&to, &from, + coord_last_unit_pos(&from) - + shift->merging_units + 1, shift->merging_units, SHIFT_RIGHT, shift->merging_bytes); + coord_dec_item_pos(&from); + from_ih++; + } + + if (shift->entire) { + /* copy @entire items entirely */ + + /* copy item headers */ + xmemcpy(to_ih, from_ih, shift->entire * sizeof (item_header40)); + + /* update item header offset */ + old_offset = ih40_get_offset(from_ih + shift->entire - 1); + /* AUDIT: old_offset + sizeof (node40_header) + shift->part_bytes calculation can be taken off the loop. */ + for (i = 0; i < shift->entire; i++, to_ih++, from_ih++) + ih40_set_offset(to_ih, + ih40_get_offset(from_ih) - + old_offset + sizeof (node40_header) + shift->part_bytes); + /* copy item bodies */ + coord_add_item_pos(&from, -(int) (shift->entire - 1)); + xmemcpy(zdata(to.node) + sizeof (node40_header) + + shift->part_bytes, item_by_coord_node40(&from), + shift->entire_bytes); + coord_dec_item_pos(&from); + } + + if (shift->part_units) { + coord_set_item_pos(&to, 0); + to.unit_pos = 0; + to.between = AT_UNIT; + /* copy heading part (@part units) of @source item as + a new item into @target->node */ + + /* copy item header of partially copied item */ + xmemcpy(to_ih, from_ih, sizeof (item_header40)); + ih40_set_offset(to_ih, sizeof (node40_header)); + if (item_plugin_by_coord(&to)->b.init) + item_plugin_by_coord(&to)->b.init(&to, &from, 0); + copy_units(&to, &from, + coord_last_unit_pos(&from) - + shift->part_units + 1, shift->part_units, SHIFT_RIGHT, shift->part_bytes); + } + } +} + +/* remove everything either before or after @fact_stop. Number of items + removed completely is returned */ +static int +delete_copied(struct shift_params *shift) +{ + coord_t from; + coord_t to; + struct carry_cut_data cdata; + + if (shift->pend == SHIFT_LEFT) { + /* we were shifting to left, remove everything from the + beginning of @shift->wish_stop->node upto + @shift->wish_stop */ + coord_init_first_unit(&from, shift->real_stop.node); + to = shift->real_stop; + + /* store old coordinate of unit which will be first after + shift to left */ + shift->u.future_first = to; + coord_next_unit(&shift->u.future_first); + } else { + /* we were shifting to right, remove everything from + @shift->stop_coord upto to end of + @shift->stop_coord->node */ + from = shift->real_stop; + coord_init_last_unit(&to, from.node); + + /* store old coordinate of unit which will be last after + shift to right */ + shift->u.future_last = from; + coord_prev_unit(&shift->u.future_last); + } + + cdata.params.from = &from; + cdata.params.to = &to; + cdata.params.from_key = 0; + cdata.params.to_key = 0; + cdata.params.smallest_removed = 0; + return cut_node40(&cdata, 0); +} + +/* something was moved between @left and @right. Add carry operation to @info + list to have carry to update delimiting key between them */ +static int +prepare_for_update(znode * left, znode * right, carry_plugin_info * info) +{ + carry_op *op; + carry_node *cn; + + if (info == NULL) + /* nowhere to send operation to. */ + return 0; + + if (!should_notify_parent(right)) + return 0; + + op = node_post_carry(info, COP_UPDATE, right, 1); + if (IS_ERR(op) || op == NULL) + return op ? PTR_ERR(op) : -EIO; + + if (left != NULL) { + carry_node *reference; + + if (info->doing) + reference = insert_carry_node(info->doing, + info->todo, left); + else + reference = op->node; + assert("nikita-2992", reference != NULL); + cn = add_carry(info->todo, POOLO_BEFORE, reference); + if (IS_ERR(cn)) + return PTR_ERR(cn); + cn->parent = 1; + cn->node = left; + if (ZF_ISSET(left, JNODE_ORPHAN)) + cn->left_before = 1; + op->u.update.left = cn; + } else + op->u.update.left = NULL; + return 0; +} + +/* plugin->u.node.prepare_removal + to delete a pointer to @empty from the tree add corresponding carry + operation (delete) to @info list */ +reiser4_internal int +prepare_removal_node40(znode * empty, carry_plugin_info * info) +{ + carry_op *op; + + if (!should_notify_parent(empty)) + return 0; + /* already on a road to Styx */ + if (ZF_ISSET(empty, JNODE_HEARD_BANSHEE)) + return 0; + op = node_post_carry(info, COP_DELETE, empty, 1); + if (IS_ERR(op) || op == NULL) + return RETERR(op ? PTR_ERR(op) : -EIO); + + op->u.delete.child = 0; + op->u.delete.flags = 0; + + /* fare thee well */ + ZF_SET(empty, JNODE_HEARD_BANSHEE); + return 0; +} + +/* something were shifted from @insert_coord->node to @shift->target, update + @insert_coord correspondingly */ +static void +adjust_coord(coord_t * insert_coord, struct shift_params *shift, int removed, int including_insert_coord) +{ + /* item plugin was invalidated by shifting */ + coord_clear_iplug(insert_coord); + + if (node_is_empty(shift->wish_stop.node)) { + assert("vs-242", shift->everything); + if (including_insert_coord) { + if (shift->pend == SHIFT_RIGHT) { + /* set @insert_coord before first unit of + @shift->target node */ + coord_init_before_first_item(insert_coord, shift->target); + } else { + /* set @insert_coord after last in target node */ + coord_init_after_last_item(insert_coord, shift->target); + } + } else { + /* set @insert_coord inside of empty node. There is + only one possible coord within an empty + node. init_first_unit will set that coord */ + coord_init_first_unit(insert_coord, shift->wish_stop.node); + } + return; + } + + if (shift->pend == SHIFT_RIGHT) { + /* there was shifting to right */ + if (shift->everything) { + /* everything wanted was shifted */ + if (including_insert_coord) { + /* @insert_coord is set before first unit of + @to node */ + coord_init_before_first_item(insert_coord, shift->target); + insert_coord->between = BEFORE_UNIT; + } else { + /* @insert_coord is set after last unit of + @insert->node */ + coord_init_last_unit(insert_coord, shift->wish_stop.node); + insert_coord->between = AFTER_UNIT; + } + } + return; + } + + /* there was shifting to left */ + if (shift->everything) { + /* everything wanted was shifted */ + if (including_insert_coord) { + /* @insert_coord is set after last unit in @to node */ + coord_init_after_last_item(insert_coord, shift->target); + } else { + /* @insert_coord is set before first unit in the same + node */ + coord_init_before_first_item(insert_coord, shift->wish_stop.node); + } + return; + } + + /* FIXME-VS: the code below is complicated because with between == + AFTER_ITEM unit_pos is set to 0 */ + + if (!removed) { + /* no items were shifted entirely */ + assert("vs-195", shift->merging_units == 0 || shift->part_units == 0); + + if (shift->real_stop.item_pos == insert_coord->item_pos) { + if (shift->merging_units) { + if (insert_coord->between == AFTER_UNIT) { + assert("nikita-1441", insert_coord->unit_pos >= shift->merging_units); + insert_coord->unit_pos -= shift->merging_units; + } else if (insert_coord->between == BEFORE_UNIT) { + assert("nikita-2090", insert_coord->unit_pos > shift->merging_units); + insert_coord->unit_pos -= shift->merging_units; + } + + assert("nikita-2083", insert_coord->unit_pos + 1); + } else { + if (insert_coord->between == AFTER_UNIT) { + assert("nikita-1442", insert_coord->unit_pos >= shift->part_units); + insert_coord->unit_pos -= shift->part_units; + } else if (insert_coord->between == BEFORE_UNIT) { + assert("nikita-2089", insert_coord->unit_pos > shift->part_units); + insert_coord->unit_pos -= shift->part_units; + } + + assert("nikita-2084", insert_coord->unit_pos + 1); + } + } + return; + } + + /* we shifted to left and there was no enough space for everything */ + switch (insert_coord->between) { + case AFTER_UNIT: + case BEFORE_UNIT: + if (shift->real_stop.item_pos == insert_coord->item_pos) + insert_coord->unit_pos -= shift->part_units; + case AFTER_ITEM: + coord_add_item_pos(insert_coord, -removed); + break; + default: + impossible("nikita-2087", "not ready"); + } + assert("nikita-2085", insert_coord->unit_pos + 1); +} + +static int +call_shift_hooks(struct shift_params *shift) +{ + unsigned i, shifted; + coord_t coord; + item_plugin *iplug; + + assert("vs-275", !node_is_empty(shift->target)); + + /* number of items shift touches */ + shifted = shift->entire + (shift->merging_units ? 1 : 0) + (shift->part_units ? 1 : 0); + + if (shift->pend == SHIFT_LEFT) { + /* moved items are at the end */ + coord_init_last_unit(&coord, shift->target); + coord.unit_pos = 0; + + assert("vs-279", shift->pend == 1); + for (i = 0; i < shifted; i++) { + unsigned from, count; + + iplug = item_plugin_by_coord(&coord); + if (i == 0 && shift->part_units) { + assert("vs-277", coord_num_units(&coord) == shift->part_units); + count = shift->part_units; + from = 0; + } else if (i == shifted - 1 && shift->merging_units) { + count = shift->merging_units; + from = coord_num_units(&coord) - count; + } else { + count = coord_num_units(&coord); + from = 0; + } + + if (iplug->b.shift_hook) { + iplug->b.shift_hook(&coord, from, count, shift->wish_stop.node); + } + coord_add_item_pos(&coord, -shift->pend); + } + } else { + /* moved items are at the beginning */ + coord_init_first_unit(&coord, shift->target); + + assert("vs-278", shift->pend == -1); + for (i = 0; i < shifted; i++) { + unsigned from, count; + + iplug = item_plugin_by_coord(&coord); + if (i == 0 && shift->part_units) { + assert("vs-277", coord_num_units(&coord) == shift->part_units); + count = coord_num_units(&coord); + from = 0; + } else if (i == shifted - 1 && shift->merging_units) { + count = shift->merging_units; + from = 0; + } else { + count = coord_num_units(&coord); + from = 0; + } + + if (iplug->b.shift_hook) { + iplug->b.shift_hook(&coord, from, count, shift->wish_stop.node); + } + coord_add_item_pos(&coord, -shift->pend); + } + } + + return 0; +} + +/* shift to left is completed. Return 1 if unit @old was moved to left neighbor */ +static int +unit_moved_left(const struct shift_params *shift, const coord_t * old) +{ + assert("vs-944", shift->real_stop.node == old->node); + + if (shift->real_stop.item_pos < old->item_pos) + return 0; + if (shift->real_stop.item_pos == old->item_pos) { + if (shift->real_stop.unit_pos < old->unit_pos) + return 0; + } + return 1; +} + +/* shift to right is completed. Return 1 if unit @old was moved to right + neighbor */ +static int +unit_moved_right(const struct shift_params *shift, const coord_t * old) +{ + assert("vs-944", shift->real_stop.node == old->node); + + if (shift->real_stop.item_pos > old->item_pos) + return 0; + if (shift->real_stop.item_pos == old->item_pos) { + if (shift->real_stop.unit_pos > old->unit_pos) + return 0; + } + return 1; +} + +/* coord @old was set in node from which shift was performed. What was shifted + is stored in @shift. Update @old correspondingly to performed shift */ +static coord_t * +adjust_coord2(const struct shift_params *shift, const coord_t * old, coord_t * new) +{ + coord_clear_iplug(new); + new->between = old->between; + + coord_clear_iplug(new); + if (old->node == shift->target) { + if (shift->pend == SHIFT_LEFT) { + /* coord which is set inside of left neighbor does not + change during shift to left */ + coord_dup(new, old); + return new; + } + new->node = old->node; + coord_set_item_pos(new, + old->item_pos + shift->entire + + (shift->part_units ? 1 : 0)); + new->unit_pos = old->unit_pos; + if (old->item_pos == 0 && shift->merging_units) + new->unit_pos += shift->merging_units; + return new; + } + + assert("vs-977", old->node == shift->wish_stop.node); + if (shift->pend == SHIFT_LEFT) { + if (unit_moved_left(shift, old)) { + /* unit @old moved to left neighbor. Calculate its + coordinate there */ + new->node = shift->target; + coord_set_item_pos(new, + node_num_items(shift->target) - + shift->entire - + (shift->part_units ? 1 : 0) + + old->item_pos); + + new->unit_pos = old->unit_pos; + if (shift->merging_units) { + coord_dec_item_pos(new); + if (old->item_pos == 0) { + /* unit_pos only changes if item got + merged */ + new->unit_pos = coord_num_units(new) - (shift->merging_units - old->unit_pos); + } + } + } else { + /* unit @old did not move to left neighbor. + + Use _nocheck, because @old is outside of its node. + */ + coord_dup_nocheck(new, old); + coord_add_item_pos(new, -shift->u.future_first.item_pos); + if (new->item_pos == 0) + new->unit_pos -= shift->u.future_first.unit_pos; + } + } else { + if (unit_moved_right(shift, old)) { + /* unit @old moved to right neighbor */ + new->node = shift->target; + coord_set_item_pos(new, + old->item_pos - + shift->real_stop.item_pos); + if (new->item_pos == 0) { + /* unit @old might change unit pos */ + coord_set_item_pos(new, + old->unit_pos - + shift->real_stop.unit_pos); + } + } else { + /* unit @old did not move to right neighbor, therefore + it did not change */ + coord_dup(new, old); + } + } + coord_set_iplug(new, item_plugin_by_coord(new)); + return new; +} + +/* this is called when shift is completed (something of source node is copied + to target and deleted in source) to update all taps set in current + context */ +static void +update_taps(const struct shift_params *shift) +{ + tap_t *tap; + coord_t new; + + for_all_taps(tap) { + /* update only taps set to nodes participating in shift */ + if (tap->coord->node == shift->wish_stop.node || tap->coord->node == shift->target) + tap_to_coord(tap, adjust_coord2(shift, tap->coord, &new)); + } +} + +#if REISER4_DEBUG + +struct shift_check { + reiser4_key key; + __u16 plugin_id; + union { + __u64 bytes; + __u64 entries; + void *unused; + } u; +}; + +void * +shift_check_prepare(const znode *left, const znode *right) +{ + pos_in_node_t i, nr_items; + int mergeable; + struct shift_check *data; + item_header40 *ih; + + + if (node_is_empty(left) || node_is_empty(right)) + mergeable = 0; + else { + coord_t l, r; + + coord_init_last_unit(&l, left); + coord_init_first_unit(&r, right); + mergeable = are_items_mergeable(&l, &r); + } + nr_items = node40_num_of_items_internal(left) + node40_num_of_items_internal(right) - (mergeable ? 1 : 0); + data = reiser4_kmalloc(sizeof(struct shift_check) * nr_items, GFP_KERNEL); + if (data != NULL) { + coord_t coord; + pos_in_node_t item_pos; + + coord_init_first_unit(&coord, left); + i = 0; + + for (item_pos = 0; item_pos < node40_num_of_items_internal(left); item_pos ++) { + + coord_set_item_pos(&coord, item_pos); + ih = node40_ih_at_coord(&coord); + + data[i].key = ih->key; + data[i].plugin_id = d16tocpu(&ih->plugin_id); + switch(data[i].plugin_id) { + case CTAIL_ID: + case FORMATTING_ID: + data[i].u.bytes = coord_num_units(&coord); + break; + case EXTENT_POINTER_ID: + data[i].u.bytes = extent_size(&coord, coord_num_units(&coord)); + break; + case COMPOUND_DIR_ID: + data[i].u.entries = coord_num_units(&coord); + break; + default: + data[i].u.unused = NULL; + break; + } + i ++; + } + + coord_init_first_unit(&coord, right); + + if (mergeable) { + assert("vs-1609", i != 0); + + ih = node40_ih_at_coord(&coord); + + assert("vs-1589", data[i - 1].plugin_id == d16tocpu(&ih->plugin_id)); + switch(data[i - 1].plugin_id) { + case CTAIL_ID: + case FORMATTING_ID: + data[i - 1].u.bytes += coord_num_units(&coord); + break; + case EXTENT_POINTER_ID: + data[i - 1].u.bytes += extent_size(&coord, coord_num_units(&coord)); + break; + case COMPOUND_DIR_ID: + data[i - 1].u.entries += coord_num_units(&coord); + break; + default: + impossible("vs-1605", "wrong mergeable item"); + break; + } + item_pos = 1; + } else + item_pos = 0; + for (; item_pos < node40_num_of_items_internal(right); item_pos ++) { + + assert("vs-1604", i < nr_items); + coord_set_item_pos(&coord, item_pos); + ih = node40_ih_at_coord(&coord); + + data[i].key = ih->key; + data[i].plugin_id = d16tocpu(&ih->plugin_id); + switch(data[i].plugin_id) { + case CTAIL_ID: + case FORMATTING_ID: + data[i].u.bytes = coord_num_units(&coord); + break; + case EXTENT_POINTER_ID: + data[i].u.bytes = extent_size(&coord, coord_num_units(&coord)); + break; + case COMPOUND_DIR_ID: + data[i].u.entries = coord_num_units(&coord); + break; + default: + data[i].u.unused = NULL; + break; + } + i ++; + } + assert("vs-1606", i == nr_items); + } + return data; +} + +void +shift_check(void *vp, const znode *left, const znode *right) +{ + pos_in_node_t i, nr_items; + coord_t coord; + __u64 last_bytes; + int mergeable; + item_header40 *ih; + pos_in_node_t item_pos; + struct shift_check *data; + + data = (struct shift_check *)vp; + + if (data == NULL) + return; + + if (node_is_empty(left) || node_is_empty(right)) + mergeable = 0; + else { + coord_t l, r; + + coord_init_last_unit(&l, left); + coord_init_first_unit(&r, right); + mergeable = are_items_mergeable(&l, &r); + } + + nr_items = node40_num_of_items_internal(left) + node40_num_of_items_internal(right) - (mergeable ? 1 : 0); + + i = 0; + last_bytes = 0; + + coord_init_first_unit(&coord, left); + + for (item_pos = 0; item_pos < node40_num_of_items_internal(left); item_pos ++) { + + coord_set_item_pos(&coord, item_pos); + ih = node40_ih_at_coord(&coord); + + assert("vs-1611", i == item_pos); + assert("vs-1590", keyeq(&ih->key, &data[i].key)); + assert("vs-1591", d16tocpu(&ih->plugin_id) == data[i].plugin_id); + if ((i < (node40_num_of_items_internal(left) - 1)) || !mergeable) { + switch(data[i].plugin_id) { + case CTAIL_ID: + case FORMATTING_ID: + assert("vs-1592", data[i].u.bytes == coord_num_units(&coord)); + break; + case EXTENT_POINTER_ID: + assert("vs-1593", data[i].u.bytes == extent_size(&coord, coord_num_units(&coord))); + break; + case COMPOUND_DIR_ID: + assert("vs-1594", data[i].u.entries == coord_num_units(&coord)); + break; + default: + break; + } + } + if (item_pos == (node40_num_of_items_internal(left) - 1) && mergeable) { + switch(data[i].plugin_id) { + case CTAIL_ID: + case FORMATTING_ID: + last_bytes = coord_num_units(&coord); + break; + case EXTENT_POINTER_ID: + last_bytes = extent_size(&coord, coord_num_units(&coord)); + break; + case COMPOUND_DIR_ID: + last_bytes = coord_num_units(&coord); + break; + default: + impossible("vs-1595", "wrong mergeable item"); + break; + } + } + i ++; + } + + coord_init_first_unit(&coord, right); + if (mergeable) { + ih = node40_ih_at_coord(&coord); + + assert("vs-1589", data[i - 1].plugin_id == d16tocpu(&ih->plugin_id)); + assert("vs-1608", last_bytes != 0); + switch(data[i - 1].plugin_id) { + case CTAIL_ID: + case FORMATTING_ID: + assert("vs-1596", data[i - 1].u.bytes == last_bytes + coord_num_units(&coord)); + break; + + case EXTENT_POINTER_ID: + assert("vs-1597", data[i - 1].u.bytes == last_bytes + extent_size(&coord, coord_num_units(&coord))); + break; + + case COMPOUND_DIR_ID: + assert("vs-1598", data[i - 1].u.bytes == last_bytes + coord_num_units(&coord)); + break; + default: + impossible("vs-1599", "wrong mergeable item"); + break; + } + item_pos = 1; + } else + item_pos = 0; + + for (; item_pos < node40_num_of_items_internal(right); item_pos ++) { + + coord_set_item_pos(&coord, item_pos); + ih = node40_ih_at_coord(&coord); + + assert("vs-1612", keyeq(&ih->key, &data[i].key)); + assert("vs-1613", d16tocpu(&ih->plugin_id) == data[i].plugin_id); + switch(data[i].plugin_id) { + case CTAIL_ID: + case FORMATTING_ID: + assert("vs-1600", data[i].u.bytes == coord_num_units(&coord)); + break; + case EXTENT_POINTER_ID: + assert("vs-1601", data[i].u.bytes == extent_size(&coord, coord_num_units(&coord))); + break; + case COMPOUND_DIR_ID: + assert("vs-1602", data[i].u.entries == coord_num_units(&coord)); + break; + default: + break; + } + i ++; + } + + assert("vs-1603", i == nr_items); + reiser4_kfree(data); +} + +#endif + +ON_DEBUG_MODIFY(extern __u32 znode_checksum(const znode * node);) + +/* plugin->u.node.shift + look for description of this method in plugin/node/node.h */ +reiser4_internal int +shift_node40(coord_t *from, znode *to, shift_direction pend, + int delete_child, /* if @from->node becomes empty - it will be deleted from the tree if this is set to + 1 */ + int including_stop_coord /* */ , + carry_plugin_info *info) +{ + struct shift_params shift; + int result; + znode *left, *right; + znode *source; + int target_empty; +#if REISER4_DEBUG + struct shift_check *check_data; +#endif + + assert("nikita-2161", coord_check(from)); + + ON_DEBUG_MODIFY(znode_set_checksum(ZJNODE(to), 0)); + + xmemset(&shift, 0, sizeof (shift)); + shift.pend = pend; + shift.wish_stop = *from; + shift.target = to; + + assert("nikita-1473", znode_is_write_locked(from->node)); + assert("nikita-1474", znode_is_write_locked(to)); + node_check(from->node, 0); + node_check(to, 0); + + source = from->node; + + /* set @shift.wish_stop to rightmost/leftmost unit among units we want + shifted */ + if (pend == SHIFT_LEFT) { + result = coord_set_to_left(&shift.wish_stop); + left = to; + right = from->node; + } else { + result = coord_set_to_right(&shift.wish_stop); + left = from->node; + right = to; + } + + if (result) { + /* move insertion coord even if there is nothing to move */ + if (including_stop_coord) { + /* move insertion coord (@from) */ + if (pend == SHIFT_LEFT) { + /* after last item in target node */ + coord_init_after_last_item(from, to); + } else { + /* before first item in target node */ + coord_init_before_first_item(from, to); + } + } + + if (delete_child && node_is_empty(shift.wish_stop.node)) + result = prepare_removal_node40(shift.wish_stop.node, info); + else + result = 0; + /* there is nothing to shift */ + assert("nikita-2078", coord_check(from)); + return result; + } + + target_empty = node_is_empty(to); + + ON_DEBUG_MODIFY(assert("nikita-3427", to->cksum == znode_checksum(to))); + + /* when first node plugin with item body compression is implemented, + this must be changed to call node specific plugin */ + + /* shift->stop_coord is updated to last unit which really will be + shifted */ + estimate_shift(&shift, get_current_context()); + if (!shift.shift_bytes) { + /* we could not shift anything */ + assert("nikita-2079", coord_check(from)); + ON_DEBUG_MODIFY(assert("nikita-3433", + to->cksum == znode_checksum(to))); + return 0; + } + + ON_DEBUG(check_data = shift_check_prepare(left, right)); + + IF_TRACE(TRACE_COORDS, print_coord("shift->wish_stop before copy:", &shift.wish_stop, 0)); + + copy(&shift); + + /* result value of this is important. It is used by adjust_coord below */ + result = delete_copied(&shift); + + assert("vs-1610", result >= 0); + assert("vs-1471", ((reiser4_context *) current->journal_info)->magic == context_magic); + + /* item which has been moved from one node to another might want to do + something on that event. This can be done by item's shift_hook + method, which will be now called for every moved items */ + call_shift_hooks(&shift); + + assert("vs-1472", ((reiser4_context *) current->journal_info)->magic == context_magic); + + update_taps(&shift); + + assert("vs-1473", ((reiser4_context *) current->journal_info)->magic == context_magic); + + /* adjust @from pointer in accordance with @including_stop_coord flag + and amount of data which was really shifted */ + adjust_coord(from, &shift, result, including_stop_coord); + + if (target_empty) + /* + * items were shifted into empty node. Update delimiting key. + */ + result = prepare_for_update(NULL, left, info); + + /* add update operation to @info, which is the list of operations to + be performed on a higher level */ + result = prepare_for_update(left, right, info); + if (!result && node_is_empty(source) && delete_child) { + /* all contents of @from->node is moved to @to and @from->node + has to be removed from the tree, so, on higher level we + will be removing the pointer to node @from->node */ + result = prepare_removal_node40(source, info); + } + +#ifdef DEBUGGING_SHIFT + dinfo("SHIFT TO %s: merging %d, entire %d, part %d, size %d\n", + shift.pend == SHIFT_LEFT ? "LEFT" : "RIGHT", + shift.merging_units, shift.entire, shift.part_units, shift.shift_bytes); +#endif + ON_TRACE(TRACE_SHIFT, "shift: [%Li] %s--%s [%Li]: %i\n", + *znode_get_block(left), + (shift.pend == SHIFT_LEFT) ? "<" : "", + (shift.pend == SHIFT_LEFT) ? "" : ">", *znode_get_block(right), shift.shift_bytes); + + node_check(source, 0); + node_check(to, 0); + assert("nikita-2080", coord_check(from)); + + ON_DEBUG(shift_check(check_data, left, right)); + + return result ? result : (int) shift.shift_bytes; +} + +/* plugin->u.node.fast_insert() + look for description of this method in plugin/node/node.h */ +reiser4_internal int +fast_insert_node40(const coord_t * coord UNUSED_ARG /* node to query */ ) +{ + return 1; +} + +/* plugin->u.node.fast_paste() + look for description of this method in plugin/node/node.h */ +reiser4_internal int +fast_paste_node40(const coord_t * coord UNUSED_ARG /* node to query */ ) +{ + return 1; +} + +/* plugin->u.node.fast_cut() + look for description of this method in plugin/node/node.h */ +reiser4_internal int +fast_cut_node40(const coord_t * coord UNUSED_ARG /* node to query */ ) +{ + return 1; +} + +/* plugin->u.node.modify - not defined */ + +/* plugin->u.node.max_item_size */ +reiser4_internal int +max_item_size_node40(void) +{ + return reiser4_get_current_sb()->s_blocksize - sizeof (node40_header) - sizeof (item_header40); +} + +/* plugin->u.node.set_item_plugin */ +reiser4_internal int +set_item_plugin_node40(coord_t *coord, item_id id) +{ + item_header40 *ih; + + ih = node40_ih_at_coord(coord); + cputod16(id, &ih->plugin_id); + coord->iplugid = id; + return 0; +} + + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/node/node40.h 2004-09-03 00:57:05.303233048 -0700 @@ -0,0 +1,118 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#if !defined( __REISER4_NODE40_H__ ) +#define __REISER4_NODE40_H__ + +#include "../../forward.h" +#include "../../dformat.h" +#include "node.h" + +#include + + +/* format of node header for 40 node layouts. Keep bloat out of this struct. */ +typedef struct node40_header { + /* identifier of node plugin. Must be located at the very beginning + of a node. */ + common_node_header common_header; /* this is 16 bits */ + /* number of items. Should be first element in the node header, + because we haven't yet finally decided whether it shouldn't go into + common_header. + */ +/* NIKITA-FIXME-HANS: Create a macro such that if there is only one + * node format at compile time, and it is this one, accesses do not function dereference when + * accessing these fields (and otherwise they do). Probably 80% of users will only have one node format at a time throughout the life of reiser4. */ + d16 nr_items; + /* free space in node measured in bytes */ + d16 free_space; + /* offset to start of free space in node */ + d16 free_space_start; + /* for reiser4_fsck. When information about what is a free + block is corrupted, and we try to recover everything even + if marked as freed, then old versions of data may + duplicate newer versions, and this field allows us to + restore the newer version. Also useful for when users + who don't have the new trashcan installed on their linux distro + delete the wrong files and send us desperate emails + offering $25 for them back. */ + + /* magic field we need to tell formatted nodes NIKITA-FIXME-HANS: improve this comment*/ + d32 magic; + /* flushstamp is made of mk_id and write_counter. mk_id is an + id generated randomly at mkreiserfs time. So we can just + skip all nodes with different mk_id. write_counter is d64 + incrementing counter of writes on disk. It is used for + choosing the newest data at fsck time. NIKITA-FIXME-HANS: why was field name changed but not comment? */ + + d32 mkfs_id; + d64 flush_id; + /* node flags to be used by fsck (reiser4ck or reiser4fsck?) + and repacker NIKITA-FIXME-HANS: say more or reference elsewhere that says more */ + d16 flags; + + /* 1 is leaf level, 2 is twig level, root is the numerically + largest level */ + d8 level; + + d8 pad; +} PACKED node40_header; + +/* item headers are not standard across all node layouts, pass + pos_in_node to functions instead */ +typedef struct item_header40 { + /* key of item */ + /* 0 */ reiser4_key key; + /* offset from start of a node measured in 8-byte chunks */ + /* 24 */ d16 offset; + /* 26 */ d16 flags; + /* 28 */ d16 plugin_id; +} PACKED item_header40; + +size_t item_overhead_node40(const znode * node, flow_t * aflow); +size_t free_space_node40(znode * node); +node_search_result lookup_node40(znode * node, const reiser4_key * key, lookup_bias bias, coord_t * coord); +int num_of_items_node40(const znode * node); +char *item_by_coord_node40(const coord_t * coord); +int length_by_coord_node40(const coord_t * coord); +item_plugin *plugin_by_coord_node40(const coord_t * coord); +reiser4_key *key_at_node40(const coord_t * coord, reiser4_key * key); +size_t estimate_node40(znode * node); +int check_node40(const znode * node, __u32 flags, const char **error); +int parse_node40(znode * node); +#if REISER4_DEBUG_OUTPUT +void print_node40(const char *prefix, const znode * node, __u32 flags); +#endif +int init_node40(znode * node); +int guess_node40(const znode * node); +void change_item_size_node40(coord_t * coord, int by); +int create_item_node40(coord_t * target, const reiser4_key * key, reiser4_item_data * data, carry_plugin_info * info); +void update_item_key_node40(coord_t * target, const reiser4_key * key, carry_plugin_info * info); +int kill_node40(struct carry_kill_data *, carry_plugin_info *); +int cut_node40(struct carry_cut_data *, carry_plugin_info *); +int shift_node40(coord_t * from, znode * to, shift_direction pend, + /* if @from->node becomes + empty - it will be deleted from + the tree if this is set to 1 + */ + int delete_child, int including_stop_coord, carry_plugin_info * info); + +int fast_insert_node40(const coord_t * coord); +int fast_paste_node40(const coord_t * coord); +int fast_cut_node40(const coord_t * coord); +int max_item_size_node40(void); +int prepare_removal_node40(znode * empty, carry_plugin_info * info); +int set_item_plugin_node40(coord_t * coord, item_id id); +int shrink_item_node40(coord_t *coord, int delta); + +/* __REISER4_NODE40_H__ */ +#endif +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/node/node.c 2004-09-03 00:57:05.305232744 -0700 @@ -0,0 +1,375 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Node plugin interface. + + Description: The tree provides the abstraction of flows, which it + internally fragments into items which it stores in nodes. + + A key_atom is a piece of data bound to a single key. + + For reasonable space efficiency to be achieved it is often + necessary to store key_atoms in the nodes in the form of items, where + an item is a sequence of key_atoms of the same or similar type. It is + more space-efficient, because the item can implement (very) + efficient compression of key_atom's bodies using internal knowledge + about their semantics, and it can often avoid having a key for each + key_atom. Each type of item has specific operations implemented by its + item handler (see balance.c). + + Rationale: the rest of the code (specifically balancing routines) + accesses leaf level nodes through this interface. This way we can + implement various block layouts and even combine various layouts + within the same tree. Balancing/allocating algorithms should not + care about peculiarities of splitting/merging specific item types, + but rather should leave that to the item's item handler. + + Items, including those that provide the abstraction of flows, have + the property that if you move them in part or in whole to another + node, the balancing code invokes their is_left_mergeable() + item_operation to determine if they are mergeable with their new + neighbor in the node you have moved them to. For some items the + is_left_mergeable() function always returns null. + + When moving the bodies of items from one node to another: + + if a partial item is shifted to another node the balancing code invokes + an item handler method to handle the item splitting. + + if the balancing code needs to merge with an item in the node it + is shifting to, it will invoke an item handler method to handle + the item merging. + + if it needs to move whole item bodies unchanged, the balancing code uses xmemcpy() + adjusting the item headers after the move is done using the node handler. +*/ + +#include "../../forward.h" +#include "../../debug.h" +#include "../../key.h" +#include "../../coord.h" +#include "../plugin_header.h" +#include "../item/item.h" +#include "node.h" +#include "../plugin.h" +#include "../../znode.h" +#include "../../tree.h" +#include "../../super.h" +#include "../../reiser4.h" + +/* return starting key of the leftmost item in the @node */ +reiser4_internal reiser4_key * +leftmost_key_in_node(const znode * node /* node to query */ , + reiser4_key * key /* resulting key */ ) +{ + assert("nikita-1634", node != NULL); + assert("nikita-1635", key != NULL); + + if (!node_is_empty(node)) { + coord_t first_item; + + coord_init_first_unit(&first_item, (znode *) node); + item_key_by_coord(&first_item, key); + } else + *key = *max_key(); + return key; +} + +#if REISER4_DEBUG_OUTPUT +/* helper function: convert 4 bit integer to its hex representation */ +/* Audited by: green(2002.06.12) */ +static char +hex_to_ascii(const int hex /* hex digit */ ) +{ + assert("nikita-1081", (0 <= hex) && (hex < 0x10)); + + if (hex < 10) + return '0' + hex; + else + return 'a' + hex - 10; +} + +/* helper function used to indent output during recursive tree printing */ +/* Audited by: green(2002.06.12) */ +reiser4_internal void +indent(unsigned indentation) +{ + unsigned i; + + for (i = 0; i < indentation; ++i) + printk("%.1i........", indentation - i); +} + +/* helper function used to indent output for @node during recursive tree + printing */ +reiser4_internal void +indent_znode(const znode * node /* current node */ ) +{ + if (znode_get_tree(node)->height < znode_get_level(node)) + indent(0); + else + indent(znode_get_tree(node)->height - znode_get_level(node)); +} + +/* debugging aid: output human readable information about @node */ +reiser4_internal void +print_node_content(const char *prefix /* output prefix */ , + const znode * node /* node to print */ , + __u32 flags /* print flags */ ) +{ + unsigned short i; + coord_t coord; + item_plugin *iplug; + reiser4_key key; + + if (!znode_is_loaded(node)) { + print_znode("znode is not loaded\n", node); + return; + } + if (node_plugin_by_node(node)->print != NULL) { + indent_znode(node); + node_plugin_by_node(node)->print(prefix, node, flags); + + indent_znode(node); + print_key("LDKEY", &node->ld_key); + + indent_znode(node); + print_key("RDKEY", &node->rd_key); + } + + /*if( flags & REISER4_NODE_SILENT ) {return;} */ + + coord.node = (znode *) node; + coord.unit_pos = 0; + coord.between = AT_UNIT; + /*indent_znode (node); */ + for (i = 0; i < node_num_items(node); i++) { + int j; + int length; + char *data; + + indent_znode(node); + printk("%d: ", i); + + coord_set_item_pos(&coord, i); + + iplug = item_plugin_by_coord(&coord); + print_plugin("\titem plugin", item_plugin_to_plugin(iplug)); + indent_znode(node); + item_key_by_coord(&coord, &key); + print_key("\titem key", &key); + + indent_znode(node); + printk("\tlength %d\n", item_length_by_coord(&coord)); + indent_znode(node); + iplug->b.print("\titem", &coord); + + data = item_body_by_coord(&coord); + length = item_length_by_coord(&coord); + indent_znode(node); + printk("\titem length: %i, offset: %i\n", length, data - zdata(node)); + for (j = 0; j < length; ++j) { + char datum; + + if ((j % 16) == 0) { + /* next 16 bytes */ + if (j == 0) { + indent_znode(node); + printk("\tdata % .2i: ", j); + } else { + printk("\n"); + indent_znode(node); + printk("\t % .2i: ", j); + } + } + datum = data[j]; + printk("%c", hex_to_ascii((datum & 0xf0) >> 4)); + printk("%c ", hex_to_ascii(datum & 0xf)); + } + printk("\n"); + indent_znode(node); + printk("======================\n"); + } + printk("\n"); +} + +/* debugging aid: output human readable information about @node + the same as the above, but items to be printed must be specified */ +reiser4_internal void +print_node_items(const char *prefix /* output prefix */ , + const znode * node /* node to print */ , + __u32 flags /* print flags */ , + unsigned from, unsigned count) +{ + unsigned i; + coord_t coord; + item_plugin *iplug; + reiser4_key key; + + if (!znode_is_loaded(node)) { + print_znode("znode is not loaded\n", node); + return; + } + if (node_plugin_by_node(node)->print != NULL) { + indent_znode(node); + node_plugin_by_node(node)->print(prefix, node, flags); + + indent_znode(node); + print_key("LDKEY", &node->ld_key); + + indent_znode(node); + print_key("RDKEY", &node->rd_key); + } + + /*if( flags & REISER4_NODE_SILENT ) {return;} */ + + coord.node = (znode *) node; + coord.unit_pos = 0; + coord.between = AT_UNIT; + /*indent_znode (node); */ + if (from >= node_num_items(node) || from + count > node_num_items(node)) { + printk("there are no those items (%u-%u) in the node (%u)\n", + from, from + count - 1, node_num_items(node)); + return; + } + + for (i = from; i < from + count; i++) { + int j; + int length; + char *data; + + indent_znode(node); + printk("%d: ", i); + + coord_set_item_pos(&coord, i); + + iplug = item_plugin_by_coord(&coord); + print_plugin("\titem plugin", item_plugin_to_plugin(iplug)); + indent_znode(node); + item_key_by_coord(&coord, &key); + print_key("\titem key", &key); + + if (iplug->b.print) { + indent_znode(node); + printk("\tlength %d\n", item_length_by_coord(&coord)); + indent_znode(node); + iplug->b.print("\titem", &coord); + } + data = item_body_by_coord(&coord); + length = item_length_by_coord(&coord); + indent_znode(node); + printk("\titem length: %i, offset: %i\n", length, data - zdata(node)); + for (j = 0; j < length; ++j) { + char datum; + + if ((j % 16) == 0) { + /* next 16 bytes */ + if (j == 0) { + indent_znode(node); + printk("\tdata % .2i: ", j); + } else { + printk("\n"); + indent_znode(node); + printk("\t % .2i: ", j); + } + } + datum = data[j]; + printk("%c", hex_to_ascii((datum & 0xf0) >> 4)); + printk("%c ", hex_to_ascii(datum & 0xf)); + } + printk("\n"); + indent_znode(node); + printk("======================\n"); + } + printk("\n"); +} +#endif + +#if REISER4_DEBUG_NODE +/* debugging aid: check consistency of @node content */ +void +node_check(znode * node /* node to check */ , + __u32 flags /* check flags */ ) +{ + const char *mes; + int result; + reiser4_tree *tree; + + assert("nikita-3534", schedulable()); + + if (!reiser4_is_debugged(reiser4_get_current_sb(), REISER4_CHECK_NODE)) + return; + + if (get_current_context()->disable_node_check) + return; + tree = znode_get_tree(node); + + if (znode_above_root(node)) + return; + if (znode_just_created(node)) + return; + + zload(node); + result = node_plugin_by_node(node)->check(node, flags, &mes); + if (result != 0) { + printk("%s\n", mes); + print_node_content("check", node, ~0u); + reiser4_panic("vs-273", "node corrupted"); + } + zrelse(node); +} +#endif + +node_plugin node_plugins[LAST_NODE_ID] = { + [NODE40_ID] = { + .h = { + .type_id = REISER4_NODE_PLUGIN_TYPE, + .id = NODE40_ID, + .pops = NULL, + .label = "unified", + .desc = "unified node layout", + .linkage = TYPE_SAFE_LIST_LINK_ZERO, + }, + .item_overhead = item_overhead_node40, + .free_space = free_space_node40, + .lookup = lookup_node40, + .num_of_items = num_of_items_node40, + .item_by_coord = item_by_coord_node40, + .length_by_coord = length_by_coord_node40, + .plugin_by_coord = plugin_by_coord_node40, + .key_at = key_at_node40, + .estimate = estimate_node40, + .check = check_node40, + .parse = parse_node40, + .init = init_node40, +#ifdef GUESS_EXISTS + .guess = guess_node40, +#endif +#if REISER4_DEBUG_OUTPUT + .print = print_node40, +#endif + .change_item_size = change_item_size_node40, + .create_item = create_item_node40, + .update_item_key = update_item_key_node40, + .cut_and_kill = kill_node40, + .cut = cut_node40, + .shift = shift_node40, + .shrink_item = shrink_item_node40, + .fast_insert = fast_insert_node40, + .fast_paste = fast_paste_node40, + .fast_cut = fast_cut_node40, + .max_item_size = max_item_size_node40, + .prepare_removal = prepare_removal_node40, + .set_item_plugin = set_item_plugin_node40 + } +}; + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/node/node.h 2004-09-03 00:57:05.307232440 -0700 @@ -0,0 +1,292 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* We need a definition of the default node layout here. */ + +/* Generally speaking, it is best to have free space in the middle of the + node so that two sets of things can grow towards it, and to have the + item bodies on the left so that the last one of them grows into free + space. We optimize for the case where we append new items to the end + of the node, or grow the last item, because it hurts nothing to so + optimize and it is a common special case to do massive insertions in + increasing key order (and one of cases more likely to have a real user + notice the delay time for). + + formatted leaf default layout: (leaf1) + + |node header:item bodies:free space:key + pluginid + item offset| + + We grow towards the middle, optimizing layout for the case where we + append new items to the end of the node. The node header is fixed + length. Keys, and item offsets plus pluginids for the items + corresponding to them are in increasing key order, and are fixed + length. Item offsets are relative to start of node (16 bits creating + a node size limit of 64k, 12 bits might be a better choice....). Item + bodies are in decreasing key order. Item bodies have a variable size. + There is a one to one to one mapping of keys to item offsets to item + bodies. Item offsets consist of pointers to the zeroth byte of the + item body. Item length equals the start of the next item minus the + start of this item, except the zeroth item whose length equals the end + of the node minus the start of that item (plus a byte). In other + words, the item length is not recorded anywhere, and it does not need + to be since it is computable. + + Leaf variable length items and keys layout : (lvar) + + |node header:key offset + item offset + pluginid triplets:free space:key bodies:item bodies| + + We grow towards the middle, optimizing layout for the case where we + append new items to the end of the node. The node header is fixed + length. Keys and item offsets for the items corresponding to them are + in increasing key order, and keys are variable length. Item offsets + are relative to start of node (16 bits). Item bodies are in + decreasing key order. Item bodies have a variable size. There is a + one to one to one mapping of keys to item offsets to item bodies. + Item offsets consist of pointers to the zeroth byte of the item body. + Item length equals the start of the next item's key minus the start of + this item, except the zeroth item whose length equals the end of the + node minus the start of that item (plus a byte). + + leaf compressed keys layout: (lcomp) + + |node header:key offset + key inherit + item offset pairs:free space:key bodies:item bodies| + + We grow towards the middle, optimizing layout for the case where we + append new items to the end of the node. The node header is fixed + length. Keys and item offsets for the items corresponding to them are + in increasing key order, and keys are variable length. The "key + inherit" field indicates how much of the key prefix is identical to + the previous key (stem compression as described in "Managing + Gigabytes" is used). key_inherit is a one byte integer. The + intra-node searches performed through this layout are linear searches, + and this is theorized to not hurt performance much due to the high + cost of processor stalls on modern CPUs, and the small number of keys + in a single node. Item offsets are relative to start of node (16 + bits). Item bodies are in decreasing key order. Item bodies have a + variable size. There is a one to one to one mapping of keys to item + offsets to item bodies. Item offsets consist of pointers to the + zeroth byte of the item body. Item length equals the start of the + next item minus the start of this item, except the zeroth item whose + length equals the end of the node minus the start of that item (plus a + byte). In other words, item length and key length is not recorded + anywhere, and it does not need to be since it is computable. + + internal node default layout: (idef1) + + just like ldef1 except that item bodies are either blocknrs of + children or extents, and moving them may require updating parent + pointers in the nodes that they point to. +*/ + +/* There is an inherent 3-way tradeoff between optimizing and + exchanging disks between different architectures and code + complexity. This is optimal and simple and inexchangeable. + Someone else can do the code for exchanging disks and make it + complex. It would not be that hard. Using other than the PAGE_SIZE + might be suboptimal. +*/ + +#if !defined( __REISER4_NODE_H__ ) +#define __REISER4_NODE_H__ + +#define LEAF40_NODE_SIZE PAGE_CACHE_SIZE + +#include "../../dformat.h" +#include "../plugin_header.h" + +#include + +typedef enum { + NS_FOUND = 0, + NS_NOT_FOUND = -ENOENT +} node_search_result; + +/* Maximal possible space overhead for creation of new item in a node */ +#define REISER4_NODE_MAX_OVERHEAD ( sizeof( reiser4_key ) + 32 ) + +typedef enum { + REISER4_NODE_DKEYS = (1 << 0), + REISER4_NODE_TREE_STABLE = (1 << 1) +} reiser4_node_check_flag; + +/* cut and cut_and_kill have too long list of parameters. This structure is just to safe some space on stack */ +struct cut_list { + coord_t * from; + coord_t * to; + const reiser4_key * from_key; + const reiser4_key * to_key; + reiser4_key * smallest_removed; + carry_plugin_info * info; + __u32 flags; + struct inode *inode; /* this is to pass list of eflushed jnodes down to extent_kill_hook */ + lock_handle *left; + lock_handle *right; +}; + +struct carry_cut_data; +struct carry_kill_data; + +/* The responsibility of the node plugin is to store and give access + to the sequence of items within the node. */ +typedef struct node_plugin { + /* generic plugin fields */ + plugin_header h; + + /* calculates the amount of space that will be required to store an + item which is in addition to the space consumed by the item body. + (the space consumed by the item body can be gotten by calling + item->estimate) */ + size_t(*item_overhead) (const znode * node, flow_t * f); + + /* returns free space by looking into node (i.e., without using + znode->free_space). */ + size_t(*free_space) (znode * node); + /* search within the node for the one item which might + contain the key, invoking item->search_within to search within + that item to see if it is in there */ + node_search_result(*lookup) (znode * node, const reiser4_key * key, lookup_bias bias, coord_t * coord); + /* number of items in node */ + int (*num_of_items) (const znode * node); + + /* store information about item in @coord in @data */ + /* break into several node ops, don't add any more uses of this before doing so */ + /*int ( *item_at )( const coord_t *coord, reiser4_item_data *data ); */ + char *(*item_by_coord) (const coord_t * coord); + int (*length_by_coord) (const coord_t * coord); + item_plugin *(*plugin_by_coord) (const coord_t * coord); + + /* store item key in @key */ + reiser4_key *(*key_at) (const coord_t * coord, reiser4_key * key); + /* conservatively estimate whether unit of what size can fit + into node. This estimation should be performed without + actually looking into the node's content (free space is saved in + znode). */ + size_t(*estimate) (znode * node); + + /* performs every consistency check the node plugin author could + imagine. Optional. */ + int (*check) (const znode * node, __u32 flags, const char **error); + + /* Called when node is read into memory and node plugin is + already detected. This should read some data into znode (like free + space counter) and, optionally, check data consistency. + */ + int (*parse) (znode * node); + /* This method is called on a new node to initialise plugin specific + data (header, etc.) */ + int (*init) (znode * node); + /* Check whether @node content conforms to this plugin format. + Probably only useful after support for old V3.x formats is added. + Uncomment after 4.0 only. + */ + /* int ( *guess )( const znode *node ); */ +#if REISER4_DEBUG_OUTPUT + void (*print) (const char *prefix, const znode * node, __u32 flags); +#endif + /* change size of @item by @by bytes. @item->node has enough free + space. When @by > 0 - free space is appended to end of item. When + @by < 0 - item is truncated - it is assumed that last @by bytes if + the item are freed already */ + void (*change_item_size) (coord_t * item, int by); + + /* create new item @length bytes long in coord @target */ + int (*create_item) (coord_t * target, const reiser4_key * key, + reiser4_item_data * data, carry_plugin_info * info); + + /* update key of item. */ + void (*update_item_key) (coord_t * target, const reiser4_key * key, carry_plugin_info * info); + + int (*cut_and_kill) (struct carry_kill_data *, carry_plugin_info *); + int (*cut) (struct carry_cut_data *, carry_plugin_info *); + + /* + * shrink item pointed to by @coord by @delta bytes. + */ + int (*shrink_item) (coord_t *coord, int delta); + + /* copy as much as possible but not more than up to @stop from + @stop->node to @target. If (pend == append) then data from beginning of + @stop->node are copied to the end of @target. If (pend == prepend) then + data from the end of @stop->node are copied to the beginning of + @target. Copied data are removed from @stop->node. Information + about what to do on upper level is stored in @todo */ + int (*shift) (coord_t * stop, znode * target, shift_direction pend, + int delete_node, int including_insert_coord, carry_plugin_info * info); + /* return true if this node allows skip carry() in some situations + (see fs/reiser4/tree.c:insert_by_coord()). Reiser3.x format + emulation doesn't. + + This will speedup insertions that doesn't require updates to the + parent, by bypassing initialisation of carry() structures. It's + believed that majority of insertions will fit there. + + */ + int (*fast_insert) (const coord_t * coord); + int (*fast_paste) (const coord_t * coord); + int (*fast_cut) (const coord_t * coord); + /* this limits max size of item which can be inserted into a node and + number of bytes item in a node may be appended with */ + int (*max_item_size) (void); + int (*prepare_removal) (znode * empty, carry_plugin_info * info); + /* change plugin id of items which are in a node already. Currently it is Used in tail conversion for regular + * files */ + int (*set_item_plugin) (coord_t * coord, item_id); +} node_plugin; + +typedef enum { + /* standard unified node layout used for both leaf and internal + nodes */ + NODE40_ID, + LAST_NODE_ID +} reiser4_node_id; + +extern reiser4_key *leftmost_key_in_node(const znode * node, reiser4_key * key); +#if REISER4_DEBUG_OUTPUT +extern void print_node_content(const char *prefix, const znode * node, __u32 flags); +extern void print_node_items(const char *prefix /* output prefix */ , + const znode * node /* node to print */ , + __u32 flags /* print flags */ , + unsigned from, unsigned count); +#else +#define print_node_content(p,n,f) noop +#endif + +extern void indent(unsigned indentation); +extern void indent_znode(const znode * node); + +#if REISER4_DEBUG_NODE +extern void node_check(znode * node, __u32 flags); +#define DISABLE_NODE_CHECK \ +({ \ + ++ get_current_context() -> disable_node_check; \ +}) + +#define ENABLE_NODE_CHECK \ +({ \ + -- get_current_context() -> disable_node_check; \ +}) + +#else +#define node_check( n, f ) noop +#define DISABLE_NODE_CHECK noop +#define ENABLE_NODE_CHECK noop +#endif + +extern void indent_znode(const znode * node); + +typedef struct common_node_header { + /* identifier of node plugin. Must be located at the very beginning + of a node. */ + d16 plugin_id; +} common_node_header; +/* __REISER4_NODE_H__ */ +#endif +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/object.c 2004-09-03 00:57:05.314231376 -0700 @@ -0,0 +1,1650 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Examples of object plugins: file, directory, symlink, special file */ +/* Plugins associated with inode: + + Plugin of inode is plugin referenced by plugin-id field of on-disk + stat-data. How we store this plugin in in-core inode is not + important. Currently pointers are used, another variant is to store + offsets and do array lookup on each access. + + Now, each inode has one selected plugin: object plugin that + determines what type of file this object is: directory, regular etc. + + This main plugin can use other plugins that are thus subordinated to + it. Directory instance of object plugin uses hash; regular file + instance uses tail policy plugin. + + Object plugin is either taken from id in stat-data or guessed from + i_mode bits. Once it is established we ask it to install its + subordinate plugins, by looking again in stat-data or inheriting them + from parent. +*/ +/* How new inode is initialized during ->read_inode(): + 1 read stat-data and initialize inode fields: i_size, i_mode, + i_generation, capabilities etc. + 2 read plugin id from stat data or try to guess plugin id + from inode->i_mode bits if plugin id is missing. + 3 Call ->init_inode() method of stat-data plugin to initialise inode fields. + +NIKITA-FIXME-HANS: can you say a little about 1 being done before 3? What if stat data does contain i_size, etc., due to it being an unusual plugin? + 4 Call ->activate() method of object's plugin. Plugin is either read from + from stat-data or guessed from mode bits + 5 Call ->inherit() method of object plugin to inherit as yet +NIKITA-FIXME-HANS: are you missing an "un" here? +initialized + plugins from parent. + + Easy induction proves that on last step all plugins of inode would be + initialized. + + When creating new object: + 1 obtain object plugin id (see next period) +NIKITA-FIXME-HANS: period? + 2 ->install() this plugin + 3 ->inherit() the rest from the parent + +*/ +/* We need some examples of creating an object with default and + non-default plugin ids. Nikita, please create them. +*/ + +#include "../forward.h" +#include "../debug.h" +#include "../key.h" +#include "../kassign.h" +#include "../coord.h" +#include "../seal.h" +#include "plugin_header.h" +#include "item/static_stat.h" +#include "file/file.h" +#include "file/pseudo.h" +#include "symlink.h" +#include "dir/dir.h" +#include "item/item.h" +#include "plugin.h" +#include "object.h" +#include "../znode.h" +#include "../tap.h" +#include "../tree.h" +#include "../vfs_ops.h" +#include "../inode.h" +#include "../super.h" +#include "../reiser4.h" +#include "../prof.h" +#include "../safe_link.h" + +#include +#include +#include +#include +#include /* security_inode_delete() */ +#include /* wake_up_inode() */ +#include +#include + +/* helper function to print errors */ +static void +key_warning(const reiser4_key * key /* key to print */, + const struct inode *inode, + int code /* error code to print */) +{ + assert("nikita-716", key != NULL); + + if (code != -ENOMEM) { + warning("nikita-717", "Error for inode %llu (%i)", + get_key_objectid(key), code); + print_key("for key", key); + print_inode("inode", inode); + } +} + +/* NIKITA-FIXME-HANS: perhaps this function belongs in another file? */ +#if REISER4_DEBUG +static void +check_inode_seal(const struct inode *inode, + const coord_t *coord, const reiser4_key *key) +{ + reiser4_key unit_key; + + unit_key_by_coord(coord, &unit_key); + assert("nikita-2752", + WITH_DATA_RET(coord->node, 1, keyeq(key, &unit_key))); + assert("nikita-2753", get_inode_oid(inode) == get_key_objectid(key)); +} + +static void +check_sd_coord(coord_t *coord, const reiser4_key *key) +{ + reiser4_key ukey; + + coord_clear_iplug(coord); + if (zload(coord->node)) + return; + + if (!coord_is_existing_unit(coord) || + !item_plugin_by_coord(coord) || + !keyeq(unit_key_by_coord(coord, &ukey), key) || + (znode_get_level(coord->node) != LEAF_LEVEL) || + !item_is_statdata(coord)) { + warning("nikita-1901", "Conspicuous seal"); + print_key("key", key); + print_coord("coord", coord, 1); + impossible("nikita-2877", "no way"); + } + zrelse(coord->node); +} + +#else +#define check_inode_seal(inode, coord, key) noop +#define check_sd_coord(coord, key) noop +#endif + +/* find sd of inode in a tree, deal with errors */ +reiser4_internal int +lookup_sd(struct inode *inode /* inode to look sd for */ , + znode_lock_mode lock_mode /* lock mode */ , + coord_t * coord /* resulting coord */ , + lock_handle * lh /* resulting lock handle */ , + const reiser4_key * key /* resulting key */, + int silent) +{ + int result; + __u32 flags; + + assert("nikita-1692", inode != NULL); + assert("nikita-1693", coord != NULL); + assert("nikita-1694", key != NULL); + + /* look for the object's stat data in a tree. + This returns in "node" pointer to a locked znode and in "pos" + position of an item found in node. Both are only valid if + coord_found is returned. */ + flags = (lock_mode == ZNODE_WRITE_LOCK) ? CBK_FOR_INSERT : 0; + flags |= CBK_UNIQUE; + /* + * traverse tree to find stat data. We cannot use vroot here, because + * it only covers _body_ of the file, and stat data don't belong + * there. + */ + result = coord_by_key(tree_by_inode(inode), + key, + coord, + lh, + lock_mode, + FIND_EXACT, + LEAF_LEVEL, + LEAF_LEVEL, + flags, + 0); + if (REISER4_DEBUG && result == 0) + check_sd_coord(coord, key); + + if (result != 0 && !silent) + key_warning(key, inode, result); + return result; +} + +/* insert new stat-data into tree. Called with inode state + locked. Return inode state locked. */ +static int +insert_new_sd(struct inode *inode /* inode to create sd for */ ) +{ + int result; + reiser4_key key; + coord_t coord; + reiser4_item_data data; + char *area; + reiser4_inode *ref; + lock_handle lh; + oid_t oid; + + assert("nikita-723", inode != NULL); + assert("nikita-3406", inode_get_flag(inode, REISER4_NO_SD)); + + ref = reiser4_inode_data(inode); + spin_lock_inode(inode); + + /* + * prepare specification of new item to be inserted + */ + + data.iplug = inode_sd_plugin(inode); + data.length = data.iplug->s.sd.save_len(inode); + spin_unlock_inode(inode); + + data.data = NULL; + data.user = 0; +/* could be optimized for case where there is only one node format in + * use in the filesystem, probably there are lots of such + * places we could optimize for only one node layout.... -Hans */ + if (data.length > tree_by_inode(inode)->nplug->max_item_size()) { + /* This is silly check, but we don't know actual node where + insertion will go into. */ + return RETERR(-ENAMETOOLONG); + } + oid = oid_allocate(inode->i_sb); +/* NIKITA-FIXME-HANS: what is your opinion on whether this error check should be encapsulated into oid_allocate? */ + if (oid == ABSOLUTE_MAX_OID) + return RETERR(-EOVERFLOW); + + set_inode_oid(inode, oid); + + coord_init_zero(&coord); + init_lh(&lh); + + result = insert_by_key(tree_by_inode(inode), + build_sd_key(inode, &key), + &data, + &coord, + &lh, + /* stat data lives on a leaf level */ + LEAF_LEVEL, + CBK_UNIQUE); + + /* we don't want to re-check that somebody didn't insert + stat-data while we were doing io, because if it did, + insert_by_key() returned error. */ + /* but what _is_ possible is that plugin for inode's stat-data, + list of non-standard plugins or their state would change + during io, so that stat-data wouldn't fit into sd. To avoid + this race we keep inode_state lock. This lock has to be + taken each time you access inode in a way that would cause + changes in sd size: changing plugins etc. + */ + + if (result == IBK_INSERT_OK) { + write_current_logf(WRITE_TREE_LOG, "..sd i %#llx %#llx", + get_inode_oid(inode), ref->locality_id); + + coord_clear_iplug(&coord); + result = zload(coord.node); + if (result == 0) { + /* have we really inserted stat data? */ + assert("nikita-725", item_is_statdata(&coord)); + + /* inode was just created. It is inserted into hash + table, but no directory entry was yet inserted into + parent. So, inode is inaccessible through + ->lookup(). All places that directly grab inode + from hash-table (like old knfsd), should check + IMMUTABLE flag that is set by common_create_child. + */ + assert("nikita-3240", data.iplug != NULL); + assert("nikita-3241", data.iplug->s.sd.save != NULL); + area = item_body_by_coord(&coord); + result = data.iplug->s.sd.save(inode, &area); + znode_make_dirty(coord.node); + if (result == 0) { + /* object has stat-data now */ + inode_clr_flag(inode, REISER4_NO_SD); + inode_set_flag(inode, REISER4_SDLEN_KNOWN); + /* initialise stat-data seal */ + seal_init(&ref->sd_seal, &coord, &key); + ref->sd_coord = coord; + check_inode_seal(inode, &coord, &key); + } else if (result != -ENOMEM) + /* + * convert any other error code to -EIO to + * avoid confusing user level with unexpected + * errors. + */ + result = RETERR(-EIO); + zrelse(coord.node); + } + } + done_lh(&lh); + + if (result != 0) + key_warning(&key, inode, result); + else + oid_count_allocated(); + + return result; +} + + +/* update stat-data at @coord */ +static int +update_sd_at(struct inode * inode, coord_t * coord, reiser4_key * key, + lock_handle * lh) +{ + int result; + reiser4_item_data data; + char *area; + reiser4_inode *state; + znode *loaded; + + state = reiser4_inode_data(inode); + + coord_clear_iplug(coord); + result = zload(coord->node); + if (result != 0) + return result; + loaded = coord->node; + + spin_lock_inode(inode); + assert("nikita-728", inode_sd_plugin(inode) != NULL); + data.iplug = inode_sd_plugin(inode); + + /* if inode has non-standard plugins, add appropriate stat data + * extension */ + if (state->plugin_mask != 0) + inode_set_extension(inode, PLUGIN_STAT); + + /* data.length is how much space to add to (or remove + from if negative) sd */ + if (!inode_get_flag(inode, REISER4_SDLEN_KNOWN)) { + /* recalculate stat-data length */ + data.length = + data.iplug->s.sd.save_len(inode) - + item_length_by_coord(coord); + inode_set_flag(inode, REISER4_SDLEN_KNOWN); + } else + data.length = 0; + spin_unlock_inode(inode); + + /* if on-disk stat data is of different length than required + for this inode, resize it */ + if (data.length != 0) { + data.data = NULL; + data.user = 0; + + /* insertion code requires that insertion point (coord) was + * between units. */ + coord->between = AFTER_UNIT; + result = resize_item(coord, + &data, key, lh, COPI_DONT_SHIFT_LEFT); + if (result != 0) { + key_warning(key, inode, result); + zrelse(loaded); + return result; + } + if (loaded != coord->node) { + /* resize_item moved coord to another node. Zload it */ + zrelse(loaded); + coord_clear_iplug(coord); + result = zload(coord->node); + if (result != 0) + return result; + loaded = coord->node; + } + } + + area = item_body_by_coord(coord); + spin_lock_inode(inode); + result = data.iplug->s.sd.save(inode, &area); + znode_make_dirty(coord->node); + + /* re-initialise stat-data seal */ + + /* + * coord.between was possibly skewed from AT_UNIT when stat-data size + * was changed and new extensions were pasted into item. + */ + coord->between = AT_UNIT; + seal_init(&state->sd_seal, coord, key); + state->sd_coord = *coord; + spin_unlock_inode(inode); + check_inode_seal(inode, coord, key); + zrelse(loaded); + return result; +} + +reiser4_internal int +locate_inode_sd(struct inode *inode, + reiser4_key *key, + coord_t *coord, + lock_handle *lh) +{ + reiser4_inode *state; + seal_t seal; + int result; + + assert("nikita-3483", inode != NULL); + + state = reiser4_inode_data(inode); + spin_lock_inode(inode); + *coord = state->sd_coord; + coord_clear_iplug(coord); + seal = state->sd_seal; + spin_unlock_inode(inode); + + build_sd_key(inode, key); + if (seal_is_set(&seal)) { + /* first, try to use seal */ + result = seal_validate(&seal, + coord, + key, + LEAF_LEVEL, + lh, + FIND_EXACT, + ZNODE_WRITE_LOCK, + ZNODE_LOCK_LOPRI); + if (result == 0) + check_sd_coord(coord, key); + } else + result = -E_REPEAT; + + if (result != 0) { + coord_init_zero(coord); + result = lookup_sd(inode, ZNODE_WRITE_LOCK, coord, lh, key, 0); + } + return result; +} + +/* Update existing stat-data in a tree. Called with inode state locked. Return + inode state locked. */ +static int +update_sd(struct inode *inode /* inode to update sd for */ ) +{ + int result; + reiser4_key key; + coord_t coord; + lock_handle lh; + + assert("nikita-726", inode != NULL); + + /* no stat-data, nothing to update?! */ + assert("nikita-3482", !inode_get_flag(inode, REISER4_NO_SD)); + + init_lh(&lh); + + result = locate_inode_sd(inode, &key, &coord, &lh); + if (result == 0) + result = update_sd_at(inode, &coord, &key, &lh); + done_lh(&lh); + + return result; +} +/* NIKITA-FIXME-HANS: the distinction between writing and updating made in the function names seems muddled, please adopt a better function naming strategy */ +/* save object's stat-data to disk */ +reiser4_internal int +write_sd_by_inode_common(struct inode *inode /* object to save */) +{ + int result; + + assert("nikita-730", inode != NULL); + + mark_inode_update(inode, 1); + + if (inode_get_flag(inode, REISER4_NO_SD)) + /* object doesn't have stat-data yet */ + result = insert_new_sd(inode); + else + result = update_sd(inode); + if (result != 0 && result != -ENAMETOOLONG && result != -ENOMEM) + /* Don't issue warnings about "name is too long" */ + warning("nikita-2221", "Failed to save sd for %llu: %i", + get_inode_oid(inode), result); + return result; +} + +/* checks whether yet another hard links to this object can be added */ +reiser4_internal int +can_add_link_common(const struct inode *object /* object to check */ ) +{ + assert("nikita-732", object != NULL); + + /* inode->i_nlink is unsigned int, so just check for integer + * overflow */ + return object->i_nlink + 1 != 0; +} + +/* remove object stat data. Space for it must be reserved by caller before */ +reiser4_internal int +common_object_delete_no_reserve(struct inode *inode /* object to remove */, + int mode /* cut_mode */) +{ + int result; + + assert("nikita-1477", inode != NULL); + + if (!inode_get_flag(inode, REISER4_NO_SD)) { + reiser4_key sd_key; + + DQUOT_FREE_INODE(inode); + DQUOT_DROP(inode); + + build_sd_key(inode, &sd_key); + write_current_logf(WRITE_TREE_LOG, "..sd k %#llx", get_inode_oid(inode)); + result = cut_tree(tree_by_inode(inode), &sd_key, &sd_key, NULL, mode); + if (result == 0) { + inode_set_flag(inode, REISER4_NO_SD); + result = oid_release(inode->i_sb, get_inode_oid(inode)); + if (result == 0) { + oid_count_released(); + + result = safe_link_del(inode, SAFE_UNLINK); + } + } + } else + result = 0; + return result; +} + +/* delete object stat-data. This is to be used when file deletion turns into stat data removal */ +reiser4_internal int +delete_object(struct inode *inode /* object to remove */, int mode /* cut mode */) +{ + int result; + + assert("nikita-1477", inode != NULL); + assert("nikita-3420", inode->i_size == 0 || S_ISLNK(inode->i_mode)); + assert("nikita-3421", inode->i_nlink == 0); + + if (!inode_get_flag(inode, REISER4_NO_SD)) { + reiser4_block_nr reserve; + + /* grab space which is needed to remove 2 items from the tree: + stat data and safe-link */ + reserve = 2 * estimate_one_item_removal(tree_by_inode(inode)); + if (reiser4_grab_space_force(reserve, + BA_RESERVED | BA_CAN_COMMIT)) + return RETERR(-ENOSPC); + result = common_object_delete_no_reserve(inode, mode); + } else + result = 0; + return result; +} + +reiser4_internal int +delete_file_common(struct inode * inode) +{ + return delete_object(inode, 1); +} + +/* common directory consists of two items: stat data and one item containing "." and ".." */ +static int delete_directory_common(struct inode *inode) +{ + int result; + dir_plugin *dplug; + + dplug = inode_dir_plugin(inode); + assert("vs-1101", dplug && dplug->done); + + /* grab space enough for removing two items */ + if (reiser4_grab_space(2 * estimate_one_item_removal(tree_by_inode(inode)), BA_RESERVED | BA_CAN_COMMIT)) + return RETERR(-ENOSPC); + + result = dplug->done(inode); + if (!result) + result = common_object_delete_no_reserve(inode, 1); + all_grabbed2free(); + return result; +} + +/* ->set_plug_in_inode() default method. */ +static int +set_plug_in_inode_common(struct inode *object /* inode to set plugin on */ , + struct inode *parent /* parent object */ , + reiser4_object_create_data * data /* creational + * data */ ) +{ + __u64 mask; + + object->i_mode = data->mode; + /* this should be plugin decision */ + object->i_uid = current->fsuid; + object->i_mtime = object->i_atime = object->i_ctime = CURRENT_TIME; +/* NIKITA-FIXME-HANS: which is defined as what where? */ + /* support for BSD style group-id assignment. */ + if (reiser4_is_set(object->i_sb, REISER4_BSD_GID)) + object->i_gid = parent->i_gid; + else if (parent->i_mode & S_ISGID) { + /* parent directory has sguid bit */ + object->i_gid = parent->i_gid; + if (S_ISDIR(object->i_mode)) + /* sguid is inherited by sub-directories */ + object->i_mode |= S_ISGID; + } else + object->i_gid = current->fsgid; + + /* this object doesn't have stat-data yet */ + inode_set_flag(object, REISER4_NO_SD); + /* setup inode and file-operations for this inode */ + setup_inode_ops(object, data); + object->i_nlink = 0; + seal_init(&reiser4_inode_data(object)->sd_seal, NULL, NULL); + mask = (1 << UNIX_STAT) | (1 << LIGHT_WEIGHT_STAT); + if (!reiser4_is_set(object->i_sb, REISER4_32_BIT_TIMES)) + mask |= (1 << LARGE_TIMES_STAT); + + reiser4_inode_data(object)->extmask = mask; + return 0; +} + +/* Determine object plugin for @inode based on i_mode. + + Many objects in reiser4 file system are controlled by standard object + plugins that emulate traditional unix objects: unix file, directory, symlink, fifo, and so on. + + For such files we don't explicitly store plugin id in object stat + data. Rather required plugin is guessed from mode bits, where file "type" + is encoded (see stat(2)). +*/ +reiser4_internal int +guess_plugin_by_mode(struct inode *inode /* object to guess plugins + * for */ ) +{ + int fplug_id; + int dplug_id; + reiser4_inode *info; + + assert("nikita-736", inode != NULL); + + dplug_id = fplug_id = -1; + + switch (inode->i_mode & S_IFMT) { + case S_IFSOCK: + case S_IFBLK: + case S_IFCHR: + case S_IFIFO: + fplug_id = SPECIAL_FILE_PLUGIN_ID; + break; + case S_IFLNK: + fplug_id = SYMLINK_FILE_PLUGIN_ID; + break; + case S_IFDIR: + fplug_id = DIRECTORY_FILE_PLUGIN_ID; + dplug_id = HASHED_DIR_PLUGIN_ID; + break; + default: + warning("nikita-737", "wrong file mode: %o", inode->i_mode); + return RETERR(-EIO); + case S_IFREG: + fplug_id = UNIX_FILE_PLUGIN_ID; + break; + } + info = reiser4_inode_data(inode); + plugin_set_file(&info->pset, + (fplug_id >= 0) ? file_plugin_by_id(fplug_id) : NULL); + plugin_set_dir(&info->pset, + (dplug_id >= 0) ? dir_plugin_by_id(dplug_id) : NULL); + return 0; +} + +/* this comon implementation of create estimation function may be used when object creation involves insertion of one item + (usualy stat data) into tree */ +static reiser4_block_nr estimate_create_file_common(struct inode *object) +{ + return estimate_one_insert_item(tree_by_inode(object)); +} + +/* this comon implementation of create directory estimation function may be used when directory creation involves + insertion of two items (usualy stat data and item containing "." and "..") into tree */ +static reiser4_block_nr estimate_create_dir_common(struct inode *object) +{ + return 2 * estimate_one_insert_item(tree_by_inode(object)); +} + +/* ->create method of object plugin */ +static int +create_common(struct inode *object, struct inode *parent UNUSED_ARG, + reiser4_object_create_data * data UNUSED_ARG) +{ + reiser4_block_nr reserve; + assert("nikita-744", object != NULL); + assert("nikita-745", parent != NULL); + assert("nikita-747", data != NULL); + assert("nikita-748", inode_get_flag(object, REISER4_NO_SD)); + + reserve = estimate_create_file_common(object); + if (reiser4_grab_space(reserve, BA_CAN_COMMIT)) + return RETERR(-ENOSPC); + return write_sd_by_inode_common(object); +} + +/* standard implementation of ->owns_item() plugin method: compare objectids + of keys in inode and coord */ +reiser4_internal int +owns_item_common(const struct inode *inode /* object to check + * against */ , + const coord_t * coord /* coord to check */ ) +{ + reiser4_key item_key; + reiser4_key file_key; + + assert("nikita-760", inode != NULL); + assert("nikita-761", coord != NULL); + + return /*coord_is_in_node( coord ) && */ + coord_is_existing_item(coord) && + (get_key_objectid(build_sd_key(inode, &file_key)) == get_key_objectid(item_key_by_coord(coord, &item_key))); +} + +/* @count bytes of flow @f got written, update correspondingly f->length, + f->data and f->key */ +reiser4_internal void +move_flow_forward(flow_t * f, unsigned count) +{ + if (f->data) + f->data += count; + f->length -= count; + set_key_offset(&f->key, get_key_offset(&f->key) + count); +} + +/* default ->add_link() method of file plugin */ +static int +add_link_common(struct inode *object, struct inode *parent UNUSED_ARG) +{ + /* + * increment ->i_nlink and update ->i_ctime + */ + + INODE_INC_FIELD(object, i_nlink); + object->i_ctime = CURRENT_TIME; + return 0; +} + +/* default ->rem_link() method of file plugin */ +static int +rem_link_common(struct inode *object, struct inode *parent UNUSED_ARG) +{ + assert("nikita-2021", object != NULL); + assert("nikita-2163", object->i_nlink > 0); + + /* + * decrement ->i_nlink and update ->i_ctime + */ + + INODE_DEC_FIELD(object, i_nlink); + object->i_ctime = CURRENT_TIME; + return 0; +} + +/* ->not_linked() method for file plugins */ +static int +not_linked_common(const struct inode *inode) +{ + assert("nikita-2007", inode != NULL); + return (inode->i_nlink == 0); +} + +/* ->not_linked() method the for directory file plugin */ +static int +not_linked_dir(const struct inode *inode) +{ + assert("nikita-2008", inode != NULL); + /* one link from dot */ + return (inode->i_nlink == 1); +} + +/* ->adjust_to_parent() method for regular files */ +static int +adjust_to_parent_common(struct inode *object /* new object */ , + struct inode *parent /* parent directory */ , + struct inode *root /* root directory */ ) +{ + assert("nikita-2165", object != NULL); + if (parent == NULL) + parent = root; + assert("nikita-2069", parent != NULL); + + /* + * inherit missing plugins from parent + */ + + grab_plugin(object, parent, PSET_FILE); + grab_plugin(object, parent, PSET_SD); + grab_plugin(object, parent, PSET_FORMATTING); + grab_plugin(object, parent, PSET_PERM); + return 0; +} + +/* ->adjust_to_parent() method for directory files */ +static int +adjust_to_parent_dir(struct inode *object /* new object */ , + struct inode *parent /* parent directory */ , + struct inode *root /* root directory */ ) +{ + int result = 0; + pset_member memb; + + assert("nikita-2166", object != NULL); + if (parent == NULL) + parent = root; + assert("nikita-2167", parent != NULL); + + /* + * inherit missing plugins from parent + */ + for (memb = 0; memb < PSET_LAST; ++ memb) { + result = grab_plugin(object, parent, memb); + if (result != 0) + break; + } + return result; +} + +/* simplest implementation of ->getattr() method. Completely static. */ +static int +getattr_common(struct vfsmount *mnt UNUSED_ARG, struct dentry *dentry, struct kstat *stat) +{ + struct inode *obj; + + assert("nikita-2298", dentry != NULL); + assert("nikita-2299", stat != NULL); + assert("nikita-2300", dentry->d_inode != NULL); + + obj = dentry->d_inode; + + stat->dev = obj->i_sb->s_dev; + stat->ino = oid_to_uino(get_inode_oid(obj)); + stat->mode = obj->i_mode; + /* don't confuse userland with huge nlink. This is not entirely + * correct, because nlink_t is not necessary 16 bit signed. */ + stat->nlink = min(obj->i_nlink, (typeof(obj->i_nlink))0x7fff); + stat->uid = obj->i_uid; + stat->gid = obj->i_gid; + stat->rdev = obj->i_rdev; + stat->atime = obj->i_atime; + stat->mtime = obj->i_mtime; + stat->ctime = obj->i_ctime; + stat->size = obj->i_size; + stat->blocks = (inode_get_bytes(obj) + VFS_BLKSIZE - 1) >> VFS_BLKSIZE_BITS; + /* "preferred" blocksize for efficient file system I/O */ + stat->blksize = get_super_private(obj->i_sb)->optimal_io_size; + + return 0; +} + +/* plugin->u.file.release */ +static int +release_dir(struct inode *inode, struct file *file) +{ + /* this is called when directory file descriptor is closed. */ + spin_lock_inode(inode); + /* remove directory from readddir list. See comment before + * readdir_common() for details. */ + if (file->private_data != NULL) + readdir_list_remove_clean(reiser4_get_file_fsdata(file)); + spin_unlock_inode(inode); + return 0; +} + +/* default implementation of ->bind() method of file plugin */ +static int +bind_common(struct inode *child UNUSED_ARG, struct inode *parent UNUSED_ARG) +{ + return 0; +} + +#define detach_common bind_common +#define cannot ((void *)bind_common) + +static int +detach_dir(struct inode *child, struct inode *parent) +{ + dir_plugin *dplug; + + dplug = inode_dir_plugin(child); + assert("nikita-2883", dplug != NULL); + assert("nikita-2884", dplug->detach != NULL); + return dplug->detach(child, parent); +} + + +/* this common implementation of update estimation function may be used when stat data update does not do more than + inserting a unit into a stat data item which is probably true for most cases */ +reiser4_internal reiser4_block_nr +estimate_update_common(const struct inode *inode) +{ + return estimate_one_insert_into_item(tree_by_inode(inode)); +} + +static reiser4_block_nr +estimate_unlink_common(struct inode *object UNUSED_ARG, + struct inode *parent UNUSED_ARG) +{ + return 0; +} + +static reiser4_block_nr +estimate_unlink_dir_common(struct inode *object, struct inode *parent) +{ + dir_plugin *dplug; + + dplug = inode_dir_plugin(object); + assert("nikita-2888", dplug != NULL); + assert("nikita-2887", dplug->estimate.unlink != NULL); + return dplug->estimate.unlink(object, parent); +} + +/* implementation of ->bind() method for file plugin of directory file */ +static int +bind_dir(struct inode *child, struct inode *parent) +{ + dir_plugin *dplug; + + dplug = inode_dir_plugin(child); + assert("nikita-2646", dplug != NULL); + return dplug->attach(child, parent); +} + +reiser4_internal int +setattr_reserve_common(reiser4_tree *tree) +{ + assert("vs-1096", is_grab_enabled(get_current_context())); + return reiser4_grab_space(estimate_one_insert_into_item(tree), + BA_CAN_COMMIT); +} + +/* ->setattr() method. This is called when inode attribute (including + * ->i_size) is modified. */ +reiser4_internal int +setattr_common(struct inode *inode /* Object to change attributes */, + struct iattr *attr /* change description */) +{ + int result; + + assert("nikita-3119", !(attr->ia_valid & ATTR_SIZE)); + + result = inode_change_ok(inode, attr); + if (result) + return result; + + /* + * grab disk space and call standard inode_setattr(). + */ + result = setattr_reserve_common(tree_by_inode(inode)); + if (!result) { + if ((attr->ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || + (attr->ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) { + result = DQUOT_TRANSFER(inode, attr) ? -EDQUOT : 0; + if (result) { + all_grabbed2free(); + return result; + } + } + result = inode_setattr(inode, attr); + if (!result) + reiser4_update_sd(inode); + } + + all_grabbed2free(); + return result; +} + +/* doesn't seem to be exported in headers. */ +extern spinlock_t inode_lock; + +/* ->delete_inode() method. This is called by + * iput()->iput_final()->drop_inode() when last reference to inode is released + * and inode has no names. */ +static void delete_inode_common(struct inode *object) +{ + /* create context here. + * + * removal of inode from the hash table (done at the very beginning of + * generic_delete_inode(), truncate of pages, and removal of file's + * extents has to be performed in the same atom. Otherwise, it may so + * happen, that twig node with unallocated extent will be flushed to + * the disk. + */ + reiser4_context ctx; + + /* + * FIXME: this resembles generic_delete_inode + */ + list_del_init(&object->i_list); + list_del_init(&object->i_sb_list); + object->i_state |= I_FREEING; + inodes_stat.nr_inodes--; + spin_unlock(&inode_lock); + + init_context(&ctx, object->i_sb); + + kill_cursors(object); + + if (!is_bad_inode(object)) { + file_plugin *fplug; + + /* truncate object body */ + fplug = inode_file_plugin(object); + if (fplug->pre_delete != NULL && fplug->pre_delete(object) != 0) + warning("vs-1216", "Failed to delete file body %llu", + get_inode_oid(object)); + else + assert("vs-1430", + reiser4_inode_data(object)->anonymous_eflushed == 0 && + reiser4_inode_data(object)->captured_eflushed == 0); + } + + if (object->i_data.nrpages) { + warning("vs-1434", "nrpages %ld\n", object->i_data.nrpages); + truncate_inode_pages(&object->i_data, 0); + } + security_inode_delete(object); + if (!is_bad_inode(object)) + DQUOT_INIT(object); + + object->i_sb->s_op->delete_inode(object); + + spin_lock(&inode_lock); + hlist_del_init(&object->i_hash); + spin_unlock(&inode_lock); + wake_up_inode(object); + if (object->i_state != I_CLEAR) + BUG(); + destroy_inode(object); + reiser4_exit_context(&ctx); +} + +/* + * ->forget_inode() method. Called by iput()->iput_final()->drop_inode() when + * last reference to inode with names is released + */ +static void forget_inode_common(struct inode *object) +{ + generic_forget_inode(object); +} + +/* ->drop_inode() method. Called by iput()->iput_final() when last reference + * to inode is released */ +static void drop_common(struct inode * object) +{ + file_plugin *fplug; + + assert("nikita-2643", object != NULL); + + /* -not- creating context in this method, because it is frequently + called and all existing ->not_linked() methods are one liners. */ + + fplug = inode_file_plugin(object); + /* fplug is NULL for fake inode */ + if (fplug != NULL && fplug->not_linked(object)) { + assert("nikita-3231", fplug->delete_inode != NULL); + fplug->delete_inode(object); + } else { + assert("nikita-3232", fplug->forget_inode != NULL); + fplug->forget_inode(object); + } +} + +static ssize_t +isdir(void) +{ + return RETERR(-EISDIR); +} + +#define eisdir ((void *)isdir) + +static ssize_t +perm(void) +{ + return RETERR(-EPERM); +} + +#define eperm ((void *)perm) + +static int +can_rem_dir(const struct inode * inode) +{ + /* is_dir_empty() returns 0 is dir is empty */ + return !is_dir_empty(inode); +} + +static int +process_truncate(struct inode *inode, __u64 size) +{ + int result; + struct iattr attr; + file_plugin *fplug; + reiser4_context ctx; + + init_context(&ctx, inode->i_sb); + + attr.ia_size = size; + attr.ia_valid = ATTR_SIZE | ATTR_CTIME; + fplug = inode_file_plugin(inode); + + down(&inode->i_sem); + result = fplug->setattr(inode, &attr); + up(&inode->i_sem); + + context_set_commit_async(&ctx); + reiser4_exit_context(&ctx); + + return result; +} + +reiser4_internal int +safelink_common(struct inode *object, reiser4_safe_link_t link, __u64 value) +{ + int result; + + if (link == SAFE_UNLINK) + /* nothing to do. iput() in the caller (process_safelink) will + * finish with file */ + result = 0; + else if (link == SAFE_TRUNCATE) + result = process_truncate(object, value); + else { + warning("nikita-3438", "Unrecognized safe-link type: %i", link); + result = RETERR(-EIO); + } + return result; +} + +reiser4_internal int prepare_write_common ( + struct file * file, struct page * page, unsigned from, unsigned to) +{ + int result; + file_plugin *fplug; + struct inode *inode; + + assert("umka-3099", file != NULL); + assert("umka-3100", page != NULL); + assert("umka-3095", PageLocked(page)); + + if (to - from == PAGE_CACHE_SIZE || PageUptodate(page)) + return 0; + + inode = page->mapping->host; + fplug = inode_file_plugin(inode); + + if (fplug->readpage == NULL) + return RETERR(-EINVAL); + + result = fplug->readpage(file, page); + if (result != 0) { + SetPageError(page); + ClearPageUptodate(page); + /* All reiser4 readpage() implementations should return the + * page locked in case of error. */ + assert("nikita-3472", PageLocked(page)); + } else { + /* + * ->readpage() either: + * + * 1. starts IO against @page. @page is locked for IO in + * this case. + * + * 2. doesn't start IO. @page is unlocked. + * + * In either case, page should be locked. + */ + lock_page(page); + /* + * IO (if any) is completed at this point. Check for IO + * errors. + */ + if (!PageUptodate(page)) + result = RETERR(-EIO); + } + assert("umka-3098", PageLocked(page)); + return result; +} + +reiser4_internal int +key_by_inode_and_offset_common(struct inode *inode, loff_t off, reiser4_key *key) +{ + key_init(key); + set_key_locality(key, reiser4_inode_data(inode)->locality_id); + set_key_ordering(key, get_inode_ordering(inode)); + set_key_objectid(key, get_inode_oid(inode));/*FIXME: inode->i_ino */ + set_key_type(key, KEY_BODY_MINOR); + set_key_offset(key, (__u64) off); + return 0; +} + +/* default implementation of ->sync() method: commit all transactions */ +static int +sync_common(struct inode *inode, int datasync) +{ + return txnmgr_force_commit_all(inode->i_sb, 0); +} + +static int +wire_size_common(struct inode *inode) +{ + return inode_onwire_size(inode); +} + +static char * +wire_write_common(struct inode *inode, char *start) +{ + return build_inode_onwire(inode, start); +} + +static char * +wire_read_common(char *addr, reiser4_object_on_wire *obj) +{ + return extract_obj_key_id_from_onwire(addr, &obj->u.std.key_id); +} + +static void +wire_done_common(reiser4_object_on_wire *obj) +{ + /* nothing to do */ +} + +static struct dentry * +wire_get_common(struct super_block *sb, reiser4_object_on_wire *obj) +{ + struct inode *inode; + struct dentry *dentry; + reiser4_key key; + + extract_key_from_id(&obj->u.std.key_id, &key); + inode = reiser4_iget(sb, &key, 1); + if (!IS_ERR(inode)) { + reiser4_iget_complete(inode); + dentry = d_alloc_anon(inode); + if (dentry == NULL) { + iput(inode); + dentry = ERR_PTR(-ENOMEM); + } else + dentry->d_op = &get_super_private(sb)->ops.dentry; + } else if (PTR_ERR(inode) == -ENOENT) + /* + * inode wasn't found at the key encoded in the file + * handle. Hence, file handle is stale. + */ + dentry = ERR_PTR(RETERR(-ESTALE)); + else + dentry = (void *)inode; + return dentry; +} + + +static int +change_file(struct inode * inode, reiser4_plugin * plugin) +{ + /* cannot change object plugin of already existing object */ + return RETERR(-EINVAL); +} + +static reiser4_plugin_ops file_plugin_ops = { + .init = NULL, + .load = NULL, + .save_len = NULL, + .save = NULL, + .change = change_file +}; + + +/* + * Definitions of object plugins. + */ + +file_plugin file_plugins[LAST_FILE_PLUGIN_ID] = { + [UNIX_FILE_PLUGIN_ID] = { + .h = { + .type_id = REISER4_FILE_PLUGIN_TYPE, + .id = UNIX_FILE_PLUGIN_ID, + .pops = &file_plugin_ops, + .label = "reg", + .desc = "regular file", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .open = NULL, + .truncate = truncate_unix_file, + .write_sd_by_inode = write_sd_by_inode_common, + .capturepage = capturepage_unix_file, + .readpage = readpage_unix_file, + .capture = capture_unix_file, + .read = read_unix_file, + .write = write_unix_file, + .release = release_unix_file, + .ioctl = ioctl_unix_file, + .mmap = mmap_unix_file, + .get_block = get_block_unix_file, + .flow_by_inode = flow_by_inode_unix_file, + .key_by_inode = key_by_inode_unix_file, + .set_plug_in_inode = set_plug_in_inode_common, + .adjust_to_parent = adjust_to_parent_common, + .create = create_common, + .delete = delete_file_common, + .sync = sync_unix_file, + .add_link = add_link_common, + .rem_link = rem_link_common, + .owns_item = owns_item_unix_file, + .can_add_link = can_add_link_common, + .can_rem_link = NULL, + .not_linked = not_linked_common, + .setattr = setattr_unix_file, + .getattr = getattr_common, + .seek = NULL, + .detach = detach_common, + .bind = bind_common, + .safelink = safelink_common, + .estimate = { + .create = estimate_create_file_common, + .update = estimate_update_common, + .unlink = estimate_unlink_common + }, + .wire = { + .write = wire_write_common, + .read = wire_read_common, + .get = wire_get_common, + .size = wire_size_common, + .done = wire_done_common + }, + .readpages = readpages_unix_file, + .init_inode_data = init_inode_data_unix_file, + .pre_delete = pre_delete_unix_file, + .drop = drop_common, + .delete_inode = delete_inode_common, + .destroy_inode = NULL, + .forget_inode = forget_inode_common, + .sendfile = sendfile_unix_file, + .prepare_write = prepare_write_unix_file + }, + [DIRECTORY_FILE_PLUGIN_ID] = { + .h = { + .type_id = REISER4_FILE_PLUGIN_TYPE, + .id = DIRECTORY_FILE_PLUGIN_ID, + .pops = &file_plugin_ops, + .label = "dir", + .desc = "directory", + .linkage = TYPE_SAFE_LIST_LINK_ZERO}, + .open = NULL, + .truncate = eisdir, + .write_sd_by_inode = write_sd_by_inode_common, + .capturepage = NULL, + .readpage = eisdir, + .capture = NULL, + .read = eisdir, + .write = eisdir, + .release = release_dir, + .ioctl = eisdir, + .mmap = eisdir, + .get_block = NULL, + .flow_by_inode = NULL, + .key_by_inode = NULL, + .set_plug_in_inode = set_plug_in_inode_common, + .adjust_to_parent = adjust_to_parent_dir, + .create = create_common, + .delete = delete_directory_common, + .sync = sync_common, + .add_link = add_link_common, + .rem_link = rem_link_common, + .owns_item = owns_item_hashed, + .can_add_link = can_add_link_common, + .can_rem_link = can_rem_dir, + .not_linked = not_linked_dir, + .setattr = setattr_common, + .getattr = getattr_common, + .seek = seek_dir, + .detach = detach_dir, + .bind = bind_dir, + .safelink = safelink_common, + .estimate = { + .create = estimate_create_dir_common, + .update = estimate_update_common, + .unlink = estimate_unlink_dir_common + }, + .wire = { + .write = wire_write_common, + .read = wire_read_common, + .get = wire_get_common, + .size = wire_size_common, + .done = wire_done_common + }, + .readpages = NULL, + .init_inode_data = init_inode_ordering, + .pre_delete = NULL, + .drop = drop_common, + .delete_inode = delete_inode_common, + .destroy_inode = NULL, + .forget_inode = forget_inode_common, + }, + [SYMLINK_FILE_PLUGIN_ID] = { + .h = { + .type_id = REISER4_FILE_PLUGIN_TYPE, + .id = SYMLINK_FILE_PLUGIN_ID, + .pops = &file_plugin_ops, + .label = "symlink", + .desc = "symbolic link", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .open = NULL, + .truncate = eperm, + .write_sd_by_inode = write_sd_by_inode_common, + .capturepage = NULL, + .readpage = eperm, + .capture = NULL, + .read = eperm, + .write = eperm, + .release = NULL, + .ioctl = eperm, + .mmap = eperm, + .sync = sync_common, + .get_block = NULL, + .flow_by_inode = NULL, + .key_by_inode = NULL, + .set_plug_in_inode = set_plug_in_inode_common, + .adjust_to_parent = adjust_to_parent_common, + .create = create_symlink, + /* FIXME-VS: symlink should probably have its own destroy + * method */ + .delete = delete_file_common, + .add_link = add_link_common, + .rem_link = rem_link_common, + .owns_item = NULL, + .can_add_link = can_add_link_common, + .can_rem_link = NULL, + .not_linked = not_linked_common, + .setattr = setattr_common, + .getattr = getattr_common, + .seek = NULL, + .detach = detach_common, + .bind = bind_common, + .safelink = safelink_common, + .estimate = { + .create = estimate_create_file_common, + .update = estimate_update_common, + .unlink = estimate_unlink_common + }, + .wire = { + .write = wire_write_common, + .read = wire_read_common, + .get = wire_get_common, + .size = wire_size_common, + .done = wire_done_common + }, + .readpages = NULL, + .init_inode_data = init_inode_ordering, + .pre_delete = NULL, + .drop = drop_common, + .delete_inode = delete_inode_common, + .destroy_inode = destroy_inode_symlink, + .forget_inode = forget_inode_common, + }, + [SPECIAL_FILE_PLUGIN_ID] = { + .h = { + .type_id = REISER4_FILE_PLUGIN_TYPE, + .id = SPECIAL_FILE_PLUGIN_ID, + .pops = &file_plugin_ops, + .label = "special", + .desc = "special: fifo, device or socket", + .linkage = TYPE_SAFE_LIST_LINK_ZERO} + , + .open = NULL, + .truncate = eperm, + .create = create_common, + .write_sd_by_inode = write_sd_by_inode_common, + .capturepage = NULL, + .readpage = eperm, + .capture = NULL, + .read = eperm, + .write = eperm, + .release = NULL, + .ioctl = eperm, + .mmap = eperm, + .sync = sync_common, + .get_block = NULL, + .flow_by_inode = NULL, + .key_by_inode = NULL, + .set_plug_in_inode = set_plug_in_inode_common, + .adjust_to_parent = adjust_to_parent_common, + .delete = delete_file_common, + .add_link = add_link_common, + .rem_link = rem_link_common, + .owns_item = owns_item_common, + .can_add_link = can_add_link_common, + .can_rem_link = NULL, + .not_linked = not_linked_common, + .setattr = setattr_common, + .getattr = getattr_common, + .seek = NULL, + .detach = detach_common, + .bind = bind_common, + .safelink = safelink_common, + .estimate = { + .create = estimate_create_file_common, + .update = estimate_update_common, + .unlink = estimate_unlink_common + }, + .wire = { + .write = wire_write_common, + .read = wire_read_common, + .get = wire_get_common, + .size = wire_size_common, + .done = wire_done_common + }, + .readpages = NULL, + .init_inode_data = init_inode_ordering, + .pre_delete = NULL, + .drop = drop_common, + .delete_inode = delete_inode_common, + .destroy_inode = NULL, + .forget_inode = forget_inode_common, + }, + [PSEUDO_FILE_PLUGIN_ID] = { + .h = { + .type_id = REISER4_FILE_PLUGIN_TYPE, + .id = PSEUDO_FILE_PLUGIN_ID, + .pops = &file_plugin_ops, + .label = "pseudo", + .desc = "pseudo file", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .open = open_pseudo, + .truncate = eperm, + .write_sd_by_inode = eperm, + .readpage = eperm, + .capturepage = NULL, + .capture = NULL, + .read = read_pseudo, + .write = write_pseudo, + .release = release_pseudo, + .ioctl = eperm, + .mmap = eperm, + .sync = sync_common, + .get_block = eperm, + .flow_by_inode = NULL, + .key_by_inode = NULL, + .set_plug_in_inode = set_plug_in_inode_common, + .adjust_to_parent = NULL, + .create = NULL, + .delete = eperm, + .add_link = NULL, + .rem_link = NULL, + .owns_item = NULL, + .can_add_link = cannot, + .can_rem_link = cannot, + .not_linked = NULL, + .setattr = inode_setattr, + .getattr = getattr_common, + .seek = seek_pseudo, + .detach = detach_common, + .bind = bind_common, + .safelink = NULL, + .estimate = { + .create = NULL, + .update = NULL, + .unlink = NULL + }, + .wire = { + .write = wire_write_pseudo, + .read = wire_read_pseudo, + .get = wire_get_pseudo, + .size = wire_size_pseudo, + .done = wire_done_pseudo + }, + .readpages = NULL, + .init_inode_data = NULL, + .pre_delete = NULL, + .drop = drop_pseudo, + .delete_inode = NULL, + .destroy_inode = NULL, + .forget_inode = NULL, + }, + [CRC_FILE_PLUGIN_ID] = { + .h = { + .type_id = REISER4_FILE_PLUGIN_TYPE, + .id = CRC_FILE_PLUGIN_ID, + .pops = &cryptcompress_plugin_ops, + .label = "cryptcompress", + .desc = "cryptcompress file", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + /* FIXME: check which of these are relly needed */ + .open = open_cryptcompress, + .truncate = truncate_cryptcompress, + .write_sd_by_inode = write_sd_by_inode_common, + .readpage = readpage_cryptcompress, + .capturepage = NULL, + .capture = capture_cryptcompress, + .read = generic_file_read, + .write = write_cryptcompress, + .release = NULL, + .ioctl = NULL, + .mmap = generic_file_mmap, + .get_block = get_block_cryptcompress, + .sync = sync_common, + .flow_by_inode = flow_by_inode_cryptcompress, + .key_by_inode = key_by_inode_cryptcompress, + .set_plug_in_inode = set_plug_in_inode_common, + .adjust_to_parent = adjust_to_parent_common, + .create = create_cryptcompress, + .delete = delete_cryptcompress, + .add_link = add_link_common, + .rem_link = rem_link_common, + .owns_item = owns_item_common, + .can_add_link = can_add_link_common, + .can_rem_link = NULL, + .not_linked = not_linked_common, + .setattr = setattr_cryptcompress, + .getattr = getattr_common, + .seek = NULL, + .detach = detach_common, + .bind = bind_common, + .safelink = safelink_common, + .estimate = { + .create = estimate_create_file_common, + .update = estimate_update_common, + .unlink = estimate_unlink_common + }, + .wire = { + .write = wire_write_common, + .read = wire_read_common, + .get = wire_get_common, + .size = wire_size_common, + .done = wire_done_common + }, + .readpages = readpages_cryptcompress, + .init_inode_data = init_inode_data_cryptcompress, + .pre_delete = pre_delete_cryptcompress, + .drop = drop_common, + .delete_inode = delete_inode_common, + .destroy_inode = destroy_inode_cryptcompress, + .forget_inode = forget_inode_common, + .sendfile = sendfile_common, + .prepare_write = prepare_write_common + } +}; + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/object.h 2004-09-03 00:57:05.314231376 -0700 @@ -0,0 +1,45 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Declaration of object plugin functions. */ + +#if !defined( __FS_REISER4_PLUGIN_OBJECT_H__ ) +#define __FS_REISER4_PLUGIN_OBJECT_H__ + +#include "../forward.h" + +#include /* for struct inode */ +#include + +extern int locate_inode_sd(struct inode *inode, + reiser4_key *key, coord_t *coord, lock_handle *lh); +extern int lookup_sd(struct inode *inode, znode_lock_mode lock_mode, + coord_t * coord, lock_handle * lh, const reiser4_key * key, + int silent); +extern int guess_plugin_by_mode(struct inode *inode); + +extern int write_sd_by_inode_common(struct inode *inode); +extern int owns_item_common(const struct inode *inode, + const coord_t * coord); +extern reiser4_block_nr estimate_update_common(const struct inode *inode); +extern int safelink_common(struct inode *object, + reiser4_safe_link_t link, __u64 value); +extern int prepare_write_common (struct file *, struct page *, unsigned, unsigned); +extern int key_by_inode_and_offset_common(struct inode *, loff_t, reiser4_key *); +extern int setattr_reserve_common(reiser4_tree *); +extern int setattr_common(struct inode *, struct iattr *); + +extern reiser4_plugin_ops cryptcompress_plugin_ops; + +/* __FS_REISER4_PLUGIN_OBJECT_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/plugin.c 2004-09-03 00:57:05.318230768 -0700 @@ -0,0 +1,650 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Basic plugin infrastructure, lookup etc. */ + +/* PLUGINS: + + Plugins are internal Reiser4 "modules" or "objects" used to increase + extensibility and allow external users to easily adapt reiser4 to + their needs. + + Plugins are classified into several disjoint "types". Plugins + belonging to the particular plugin type are termed "instances" of + this type. Currently the following types are present: + + . object plugin + . hash plugin + . tail plugin + . perm plugin + . item plugin + . node layout plugin + +NIKITA-FIXME-HANS: update this list, and review this entire comment for currency + + Object (file) plugin determines how given file-system object serves + standard VFS requests for read, write, seek, mmap etc. Instances of + file plugins are: regular file, directory, symlink. Another example + of file plugin is audit plugin, that optionally records accesses to + underlying object and forwards requests to it. + + Hash plugins compute hashes used by reiser4 to store and locate + files within directories. Instances of hash plugin type are: r5, + tea, rupasov. + + Tail plugins (or, more precisely, tail policy plugins) determine + when last part of the file should be stored in a formatted item. + + Perm plugins control permissions granted for a process accessing a file. + + Scope and lookup: + + label such that pair ( type_label, plugin_label ) is unique. This + pair is a globally persistent and user-visible plugin + identifier. Internally kernel maintains plugins and plugin types in + arrays using an index into those arrays as plugin and plugin type + identifiers. File-system in turn, also maintains persistent + "dictionary" which is mapping from plugin label to numerical + identifier which is stored in file-system objects. That is, we + store the offset into the plugin array for that plugin type as the + plugin id in the stat data of the filesystem object. + + plugin_labels have meaning for the user interface that assigns + plugins to files, and may someday have meaning for dynamic loading of + plugins and for copying of plugins from one fs instance to + another by utilities like cp and tar. + + Internal kernel plugin type identifier (index in plugins[] array) is + of type reiser4_plugin_type. Set of available plugin types is + currently static, but dynamic loading doesn't seem to pose + insurmountable problems. + + Within each type plugins are addressed by the identifiers of type + reiser4_plugin_id (indices in + reiser4_plugin_type_data.builtin[]). Such identifiers are only + required to be unique within one type, not globally. + + Thus, plugin in memory is uniquely identified by the pair (type_id, + id). + + Usage: + + There exists only one instance of each plugin instance, but this + single instance can be associated with many entities (file-system + objects, items, nodes, transactions, file-descriptors etc.). Entity + to which plugin of given type is termed (due to the lack of + imagination) "subject" of this plugin type and, by abuse of + terminology, subject of particular instance of this type to which + it's attached currently. For example, inode is subject of object + plugin type. Inode representing directory is subject of directory + plugin, hash plugin type and some particular instance of hash plugin + type. Inode, representing regular file is subject of "regular file" + plugin, tail-policy plugin type etc. + + With each subject the plugin possibly stores some state. For example, + the state of a directory plugin (instance of object plugin type) is pointer + to hash plugin (if directories always use hashing that is). State of + audit plugin is file descriptor (struct file) of log file or some + magic value to do logging through printk(). + + Interface: + + In addition to a scalar identifier, each plugin type and plugin + proper has a "label": short string and a "description"---longer + descriptive string. Labels and descriptions of plugin types are + hard-coded into plugins[] array, declared and defined in + plugin.c. Label and description of plugin are stored in .label and + .desc fields of reiser4_plugin_header respectively. It's possible to + locate plugin by the pair of labels. + + Features: + + . user-level plugin manipulations: + + reiser4("filename/..file_plugin<='audit'"); + + write(open("filename/..file_plugin"), "audit", 8); + + . user level utilities lsplug and chplug to manipulate plugins. + Utilities are not of primary priority. Possibly they will be not + working on v4.0 + +NIKITA-FIXME-HANS: this should be a mkreiserfs option not a mount option, do you agree? I don't think that specifying it at mount time, and then changing it with each mount, is a good model for usage. + + . mount option "plug" to set-up plugins of root-directory. + "plug=foo:bar" will set "bar" as default plugin of type "foo". + + Limitations: + + . each plugin type has to provide at least one builtin + plugin. This is technical limitation and it can be lifted in the + future. + + TODO: + + New plugin types/plugings: + Things we should be able to separately choose to inherit: + + security plugins + + stat data + + file bodies + + file plugins + + dir plugins + + . perm:acl + + d audi---audit plugin intercepting and possibly logging all + accesses to object. Requires to put stub functions in file_operations + in stead of generic_file_*. + +NIKITA-FIXME-HANS: why make overflows a plugin? + . over---handle hash overflows + + . sqnt---handle different access patterns and instruments read-ahead + +NIKITA-FIXME-HANS: describe the line below in more detail. + + . hier---handle inheritance of plugins along file-system hierarchy + + Different kinds of inheritance: on creation vs. on access. + Compatible/incompatible plugins. + Inheritance for multi-linked files. + Layered plugins. + Notion of plugin context is abandoned. + +Each file is associated + with one plugin and dependant plugins (hash, etc.) are stored as + main plugin state. Now, if we have plugins used for regular files + but not for directories, how such plugins would be inherited? + . always store them with directories also + +NIKTIA-FIXME-HANS: Do the line above. It is not exclusive of doing the line below which is also useful. + + . use inheritance hierarchy, independent of file-system namespace + +*/ + +#include "../debug.h" +#include "../dformat.h" +#include "plugin_header.h" +#include "item/static_stat.h" +#include "node/node.h" +#include "security/perm.h" +#include "space/space_allocator.h" +#include "disk_format/disk_format.h" +#include "plugin.h" +#include "../reiser4.h" +#include "../jnode.h" +#include "../inode.h" + +#include /* for struct super_block */ + +/* public interface */ + +/* initialise plugin sub-system. Just call this once on reiser4 startup. */ +int init_plugins(void); +int setup_plugins(struct super_block *super, reiser4_plugin ** area); +reiser4_plugin *lookup_plugin(const char *type_label, const char *plug_label); +reiser4_plugin *lookup_plugin_name(char *plug_label); +int locate_plugin(struct inode *inode, plugin_locator * loc); + +/* internal functions. */ + +static reiser4_plugin_type find_type(const char *label); +static reiser4_plugin *find_plugin(reiser4_plugin_type_data * ptype, const char *label); + +/* initialise plugin sub-system. Just call this once on reiser4 startup. */ +reiser4_internal int +init_plugins(void) +{ + reiser4_plugin_type type_id; + + ON_TRACE(TRACE_PLUGINS, "Builtin plugins:\n"); + for (type_id = 0; type_id < REISER4_PLUGIN_TYPES; ++type_id) { + reiser4_plugin_type_data *ptype; + int i; + + ptype = &plugins[type_id]; + assert("nikita-3508", ptype->label != NULL); + assert("nikita-3509", ptype->type_id == type_id); + + plugin_list_init(&ptype->plugins_list); + ON_TRACE(TRACE_PLUGINS, + "Of type %s (%s):\n", ptype->label, ptype->desc); +/* NIKITA-FIXME-HANS: change builtin_num to some other name lacking the term builtin. */ + for (i = 0; i < ptype->builtin_num; ++i) { + reiser4_plugin *plugin; + + plugin = plugin_at(ptype, i); + + if (plugin->h.label == NULL) + /* uninitialized slot encountered */ + continue; + assert("nikita-3445", plugin->h.type_id == type_id); + plugin->h.id = i; + IF_TRACE(TRACE_PLUGINS, print_plugin("\t", plugin)); + if (plugin->h.pops != NULL && + plugin->h.pops->init != NULL) { + int result; + + result = plugin->h.pops->init(plugin); + if (result != 0) + return result; + } + plugin_list_clean(plugin); + plugin_list_push_back(&ptype->plugins_list, plugin); + } + } + return 0; +} + +/* lookup plugin name by scanning tables */ +reiser4_internal reiser4_plugin * +lookup_plugin_name(char *plug_label /* label to search for */ ) +{ + reiser4_plugin_type type_id; + reiser4_plugin *plugin; + +/* DEMIDOV-FIXME-HANS: did you get Saveliev to agree that his name is not Vova? If not, change to DEMIDOV-001 */ + assert("vova-001", plug_label != NULL); + + plugin = NULL; + + dinfo("lookup_plugin_name: %s\n", plug_label); + + for (type_id = 0; type_id < REISER4_PLUGIN_TYPES; ++type_id) { + plugin = find_plugin(&plugins[type_id], plug_label); + if (plugin != NULL) + break; + } + return plugin; +} + +/* true if plugin type id is valid */ +reiser4_internal int +is_type_id_valid(reiser4_plugin_type type_id /* plugin type id */) +{ + /* "type_id" is unsigned, so no comparison with 0 is + necessary */ + return (type_id < REISER4_PLUGIN_TYPES); +} + +/* true if plugin id is valid */ +reiser4_internal int +is_plugin_id_valid(reiser4_plugin_type type_id /* plugin type id */ , + reiser4_plugin_id id /* plugin id */) +{ + assert("nikita-1653", is_type_id_valid(type_id)); + return ((id < plugins[type_id].builtin_num) && (id >= 0)); +} + +/* lookup plugin by scanning tables */ +reiser4_internal reiser4_plugin * +lookup_plugin(const char *type_label /* plugin type label */ , + const char *plug_label /* plugin label */ ) +{ + reiser4_plugin *result; + reiser4_plugin_type type_id; + + assert("nikita-546", type_label != NULL); + assert("nikita-547", plug_label != NULL); + + type_id = find_type(type_label); + if (is_type_id_valid(type_id)) + result = find_plugin(&plugins[type_id], plug_label); + else + result = NULL; + return result; +} + +/* return plugin by its @type_id and @id. + + Both arguments are checked for validness: this is supposed to be called + from user-level. + +NIKITA-FIXME-HANS: Do you instead mean that this checks ids created in +user space, and passed to the filesystem by use of method files? Your +comment really confused me on the first reading.... + +*/ +reiser4_internal reiser4_plugin * +plugin_by_unsafe_id(reiser4_plugin_type type_id /* plugin + * type id, + * unchecked */ , + reiser4_plugin_id id /* plugin id, + * unchecked */ ) +{ + if (is_type_id_valid(type_id)) { + if (is_plugin_id_valid(type_id, id)) + return plugin_at(&plugins[type_id], id); + else + /* id out of bounds */ + warning("nikita-2913", + "Invalid plugin id: [%i:%i]", type_id, id); + } else + /* type_id out of bounds */ + warning("nikita-2914", "Invalid type_id: %i", type_id); + return NULL; +} + +/* convert plugin id to the disk format */ +reiser4_internal int +save_plugin_id(reiser4_plugin * plugin /* plugin to convert */ , + d16 * area /* where to store result */ ) +{ + assert("nikita-1261", plugin != NULL); + assert("nikita-1262", area != NULL); + + cputod16((__u16) plugin->h.id, area); + return 0; +} + +/* list of all plugins of given type */ +reiser4_internal plugin_list_head * +get_plugin_list(reiser4_plugin_type type_id /* plugin type + * id */ ) +{ + assert("nikita-1056", is_type_id_valid(type_id)); + return &plugins[type_id].plugins_list; +} + +#if REISER4_DEBUG_OUTPUT +/* print human readable plugin information */ +reiser4_internal void +print_plugin(const char *prefix /* prefix to print */ , + reiser4_plugin * plugin /* plugin to print */ ) +{ + if (plugin != NULL) { + printk("%s: %s (%s:%i)\n", prefix, plugin->h.desc, plugin->h.label, plugin->h.id); + } else + printk("%s: (nil)\n", prefix); +} + +#endif + +/* find plugin type by label */ +static reiser4_plugin_type +find_type(const char *label /* plugin type + * label */ ) +{ + reiser4_plugin_type type_id; + + assert("nikita-550", label != NULL); + + for (type_id = 0; type_id < REISER4_PLUGIN_TYPES && + strcmp(label, plugins[type_id].label); ++type_id) { + ; + } + return type_id; +} + +/* given plugin label find it within given plugin type by scanning + array. Used to map user-visible symbolic name to internal kernel + id */ +static reiser4_plugin * +find_plugin(reiser4_plugin_type_data * ptype /* plugin + * type to + * find + * plugin + * within */ , + const char *label /* plugin label */ ) +{ + int i; + reiser4_plugin *result; + + assert("nikita-551", ptype != NULL); + assert("nikita-552", label != NULL); + + for (i = 0; i < ptype->builtin_num; ++i) { + result = plugin_at(ptype, i); + if (result->h.label == NULL) + continue; + if (!strcmp(result->h.label, label)) + return result; + } + return NULL; +} + +int +grab_plugin(struct inode *self, struct inode *ancestor, pset_member memb) +{ + reiser4_plugin *plug; + reiser4_inode *parent; + + parent = reiser4_inode_data(ancestor); + plug = pset_get(parent->hset, memb) ? : pset_get(parent->pset, memb); + return grab_plugin_from(self, memb, plug); +} + +static void +update_plugin_mask(reiser4_inode *info, pset_member memb) +{ + struct dentry *rootdir; + reiser4_inode *root; + + rootdir = inode_by_reiser4_inode(info)->i_sb->s_root; + if (rootdir != NULL) { + root = reiser4_inode_data(rootdir->d_inode); + /* + * if inode is different from the default one, or we are + * changing plugin of root directory, update plugin_mask + */ + if (pset_get(info->pset, memb) != pset_get(root->pset, memb) || + info == root) + info->plugin_mask |= (1 << memb); + } +} + +int +grab_plugin_from(struct inode *self, pset_member memb, reiser4_plugin *plug) +{ + reiser4_inode *info; + int result = 0; + + info = reiser4_inode_data(self); + if (pset_get(info->pset, memb) == NULL) { + result = pset_set(&info->pset, memb, plug); + if (result == 0) + update_plugin_mask(info, memb); + } + return result; +} + +int +force_plugin(struct inode *self, pset_member memb, reiser4_plugin *plug) +{ + reiser4_inode *info; + int result = 0; + + info = reiser4_inode_data(self); + if (plug->h.pops != NULL && plug->h.pops->change != NULL) + result = plug->h.pops->change(self, plug); + else + result = pset_set(&info->pset, memb, plug); + if (result == 0) + update_plugin_mask(info, memb); + return result; +} + +/* defined in fs/reiser4/plugin/file.c */ +extern file_plugin file_plugins[LAST_FILE_PLUGIN_ID]; +/* defined in fs/reiser4/plugin/dir.c */ +extern dir_plugin dir_plugins[LAST_DIR_ID]; +/* defined in fs/reiser4/plugin/item/static_stat.c */ +extern sd_ext_plugin sd_ext_plugins[LAST_SD_EXTENSION]; +/* defined in fs/reiser4/plugin/hash.c */ +extern hash_plugin hash_plugins[LAST_HASH_ID]; +/* defined in fs/reiser4/plugin/fibration.c */ +extern fibration_plugin fibration_plugins[LAST_FIBRATION_ID]; +/* defined in fs/reiser4/plugin/crypt.c */ +extern crypto_plugin crypto_plugins[LAST_CRYPTO_ID]; +/* defined in fs/reiser4/plugin/digest.c */ +extern digest_plugin digest_plugins[LAST_DIGEST_ID]; +/* defined in fs/reiser4/plugin/compress.c */ +extern compression_plugin compression_plugins[LAST_COMPRESSION_ID]; +/* defined in fs/reiser4/plugin/tail.c */ +extern formatting_plugin formatting_plugins[LAST_TAIL_FORMATTING_ID]; +/* defined in fs/reiser4/plugin/security/security.c */ +extern perm_plugin perm_plugins[LAST_PERM_ID]; +/* defined in fs/reiser4/plugin/item/item.c */ +extern item_plugin item_plugins[LAST_ITEM_ID]; +/* defined in fs/reiser4/plugin/node/node.c */ +extern node_plugin node_plugins[LAST_NODE_ID]; +/* defined in fs/reiser4/plugin/disk_format/disk_format.c */ +extern disk_format_plugin format_plugins[LAST_FORMAT_ID]; +/* defined in jnode.c */ +extern jnode_plugin jnode_plugins[LAST_JNODE_TYPE]; +/* defined in plugin/pseudo.c */ +extern pseudo_plugin pseudo_plugins[LAST_PSEUDO_ID]; + +reiser4_plugin_type_data plugins[REISER4_PLUGIN_TYPES] = { + /* C90 initializers */ + [REISER4_FILE_PLUGIN_TYPE] = { + .type_id = REISER4_FILE_PLUGIN_TYPE, + .label = "file", + .desc = "Object plugins", + .builtin_num = sizeof_array(file_plugins), + .builtin = file_plugins, + .plugins_list = TYPE_SAFE_LIST_HEAD_ZERO, + .size = sizeof (file_plugin) + }, + [REISER4_DIR_PLUGIN_TYPE] = { + .type_id = REISER4_DIR_PLUGIN_TYPE, + .label = "dir", + .desc = "Directory plugins", + .builtin_num = sizeof_array(dir_plugins), + .builtin = dir_plugins, + .plugins_list = TYPE_SAFE_LIST_HEAD_ZERO, + .size = sizeof (dir_plugin) + }, + [REISER4_HASH_PLUGIN_TYPE] = { + .type_id = REISER4_HASH_PLUGIN_TYPE, + .label = "hash", + .desc = "Directory hashes", + .builtin_num = sizeof_array(hash_plugins), + .builtin = hash_plugins, + .plugins_list = TYPE_SAFE_LIST_HEAD_ZERO, + .size = sizeof (hash_plugin) + }, + [REISER4_FIBRATION_PLUGIN_TYPE] = { + .type_id = REISER4_FIBRATION_PLUGIN_TYPE, + .label = "fibration", + .desc = "Directory fibrations", + .builtin_num = sizeof_array(fibration_plugins), + .builtin = fibration_plugins, + .plugins_list = TYPE_SAFE_LIST_HEAD_ZERO, + .size = sizeof (fibration_plugin) + }, + [REISER4_CRYPTO_PLUGIN_TYPE] = { + .type_id = REISER4_CRYPTO_PLUGIN_TYPE, + .label = "crypto", + .desc = "Crypto plugins", + .builtin_num = sizeof_array(crypto_plugins), + .builtin = crypto_plugins, + .plugins_list = TYPE_SAFE_LIST_HEAD_ZERO, + .size = sizeof (crypto_plugin) + }, + [REISER4_DIGEST_PLUGIN_TYPE] = { + .type_id = REISER4_DIGEST_PLUGIN_TYPE, + .label = "digest", + .desc = "Digest plugins", + .builtin_num = sizeof_array(digest_plugins), + .builtin = digest_plugins, + .plugins_list = TYPE_SAFE_LIST_HEAD_ZERO, + .size = sizeof (digest_plugin) + }, + [REISER4_COMPRESSION_PLUGIN_TYPE] = { + .type_id = REISER4_COMPRESSION_PLUGIN_TYPE, + .label = "compression", + .desc = "Compression plugins", + .builtin_num = sizeof_array(compression_plugins), + .builtin = compression_plugins, + .plugins_list = TYPE_SAFE_LIST_HEAD_ZERO, + .size = sizeof (compression_plugin) + }, + + [REISER4_FORMATTING_PLUGIN_TYPE] = { + .type_id = REISER4_FORMATTING_PLUGIN_TYPE, + .label = "formatting", + .desc = "Tail inlining policies", + .builtin_num = sizeof_array(formatting_plugins), + .builtin = formatting_plugins, + .plugins_list = TYPE_SAFE_LIST_HEAD_ZERO, + .size = sizeof (formatting_plugin) + }, + [REISER4_PERM_PLUGIN_TYPE] = { + .type_id = REISER4_PERM_PLUGIN_TYPE, + .label = "perm", + .desc = "Permission checks", + .builtin_num = sizeof_array(perm_plugins), + .builtin = perm_plugins, + .plugins_list = TYPE_SAFE_LIST_HEAD_ZERO, + .size = sizeof (perm_plugin) + }, + [REISER4_ITEM_PLUGIN_TYPE] = { + .type_id = REISER4_ITEM_PLUGIN_TYPE, + .label = "item", + .desc = "Item handlers", + .builtin_num = sizeof_array(item_plugins), + .builtin = item_plugins, + .plugins_list = TYPE_SAFE_LIST_HEAD_ZERO, + .size = sizeof (item_plugin) + }, + [REISER4_NODE_PLUGIN_TYPE] = { + .type_id = REISER4_NODE_PLUGIN_TYPE, + .label = "node", + .desc = "node layout handlers", + .builtin_num = sizeof_array(node_plugins), + .builtin = node_plugins, + .plugins_list = TYPE_SAFE_LIST_HEAD_ZERO, + .size = sizeof (node_plugin) + }, + [REISER4_SD_EXT_PLUGIN_TYPE] = { + .type_id = REISER4_SD_EXT_PLUGIN_TYPE, + .label = "sd_ext", + .desc = "Parts of stat-data", + .builtin_num = sizeof_array(sd_ext_plugins), + .builtin = sd_ext_plugins, + .plugins_list = TYPE_SAFE_LIST_HEAD_ZERO, + .size = sizeof (sd_ext_plugin) + }, + [REISER4_FORMAT_PLUGIN_TYPE] = { + .type_id = REISER4_FORMAT_PLUGIN_TYPE, + .label = "disk_layout", + .desc = "defines filesystem on disk layout", + .builtin_num = sizeof_array(format_plugins), + .builtin = format_plugins, + .plugins_list = TYPE_SAFE_LIST_HEAD_ZERO, + .size = sizeof (disk_format_plugin) + }, + [REISER4_JNODE_PLUGIN_TYPE] = { + .type_id = REISER4_JNODE_PLUGIN_TYPE, + .label = "jnode", + .desc = "defines kind of jnode", + .builtin_num = sizeof_array(jnode_plugins), + .builtin = jnode_plugins, + .plugins_list = TYPE_SAFE_LIST_HEAD_ZERO, + .size = sizeof (jnode_plugin) + }, + [REISER4_PSEUDO_PLUGIN_TYPE] = { + .type_id = REISER4_PSEUDO_PLUGIN_TYPE, + .label = "pseudo_file", + .desc = "pseudo file", + .builtin_num = sizeof_array(pseudo_plugins), + .builtin = pseudo_plugins, + .plugins_list = TYPE_SAFE_LIST_HEAD_ZERO, + .size = sizeof (pseudo_plugin) + } +}; + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/plugin.h 2004-09-03 00:57:05.323230008 -0700 @@ -0,0 +1,835 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Basic plugin data-types. + see fs/reiser4/plugin/plugin.c for details */ + +#if !defined( __FS_REISER4_PLUGIN_TYPES_H__ ) +#define __FS_REISER4_PLUGIN_TYPES_H__ + +#include "../forward.h" +#include "../debug.h" +#include "../dformat.h" +#include "../key.h" +#include "../type_safe_list.h" +#include "compress/compress.h" +#include "plugin_header.h" +#include "item/static_stat.h" +#include "item/internal.h" +#include "item/sde.h" +#include "item/cde.h" +/*#include "file/file.h"*/ +#include "pseudo/pseudo.h" +#include "symlink.h" +#include "dir/hashed_dir.h" +#include "dir/dir.h" +#include "item/item.h" +#include "node/node.h" +#include "node/node40.h" +#include "security/perm.h" +#include "fibration.h" + +#include "space/bitmap.h" +#include "space/space_allocator.h" + +#include "disk_format/disk_format40.h" +#include "disk_format/disk_format.h" + +#include /* for struct super_block, address_space */ +#include /* for struct page */ +#include /* for struct buffer_head */ +#include /* for struct dentry */ +#include +#include + +/* a flow is a sequence of bytes being written to or read from the tree. The + tree will slice the flow into items while storing it into nodes, but all of + that is hidden from anything outside the tree. */ + +struct flow { + reiser4_key key; /* key of start of flow's sequence of bytes */ + loff_t length; /* length of flow's sequence of bytes */ + char *data; /* start of flow's sequence of bytes */ + int user; /* if 1 data is user space, 0 - kernel space */ + rw_op op; /* NIKITA-FIXME-HANS: comment is where? */ +}; + +typedef ssize_t(*rw_f_type) (struct file * file, flow_t * a_flow, loff_t * off); + +typedef struct reiser4_object_on_wire reiser4_object_on_wire; + +/* File plugin. Defines the set of methods that file plugins implement, some of which are optional. + + A file plugin offers to the caller an interface for IO ( writing to and/or reading from) to what the caller sees as one + sequence of bytes. An IO to it may affect more than one physical sequence of bytes, or no physical sequence of bytes, + it may affect sequences of bytes offered by other file plugins to the semantic layer, and the file plugin may invoke + other plugins and delegate work to them, but its interface is structured for offering the caller the ability to read + and/or write what the caller sees as being a single sequence of bytes. + + The file plugin must present a sequence of bytes to the caller, but it does not necessarily have to store a sequence of + bytes, it does not necessarily have to support efficient tree traversal to any offset in the sequence of bytes (tail + and extent items, whose keys contain offsets, do however provide efficient non-sequential lookup of any offset in the + sequence of bytes). + + Directory plugins provide methods for selecting file plugins by resolving a name for them. + + The functionality other filesystems call an attribute, and rigidly tie together, we decompose into orthogonal + selectable features of files. Using the terminology we will define next, an attribute is a perhaps constrained, + perhaps static length, file whose parent has a uni-count-intra-link to it, which might be grandparent-major-packed, and + whose parent has a deletion method that deletes it. + + File plugins can implement constraints. + + Files can be of variable length (e.g. regular unix files), or of static length (e.g. static sized attributes). + + An object may have many sequences of bytes, and many file plugins, but, it has exactly one objectid. It is usually + desirable that an object has a deletion method which deletes every item with that objectid. Items cannot in general be + found by just their objectids. This means that an object must have either a method built into its deletion plugin + method for knowing what items need to be deleted, or links stored with the object that provide the plugin with a method + for finding those items. Deleting a file within an object may or may not have the effect of deleting the entire + object, depending on the file plugin's deletion method. + + LINK TAXONOMY: + + Many objects have a reference count, and when the reference count reaches 0 the object's deletion method is invoked. + Some links embody a reference count increase ("countlinks"), and others do not ("nocountlinks"). + + Some links are bi-directional links ("bilinks"), and some are uni-directional("unilinks"). + + Some links are between parts of the same object ("intralinks"), and some are between different objects ("interlinks"). + + PACKING TAXONOMY: + + Some items of an object are stored with a major packing locality based on their object's objectid (e.g. unix directory + items in plan A), and these are called "self-major-packed". + + Some items of an object are stored with a major packing locality based on their semantic parent object's objectid + (e.g. unix file bodies in plan A), and these are called "parent-major-packed". + + Some items of an object are stored with a major packing locality based on their semantic grandparent, and these are + called "grandparent-major-packed". Now carefully notice that we run into trouble with key length if we have to store a + 8 byte major+minor grandparent based packing locality, an 8 byte parent objectid, an 8 byte attribute objectid, and an + 8 byte offset, all in a 24 byte key. One of these fields must be sacrificed if an item is to be + grandparent-major-packed, and which to sacrifice is left to the item author choosing to make the item + grandparent-major-packed. You cannot make tail items and extent items grandparent-major-packed, though you could make + them self-major-packed (usually they are parent-major-packed). + + In the case of ACLs (which are composed of fixed length ACEs which consist of {subject-type, + subject, and permission bitmask} triples), it makes sense to not have an offset field in the ACE item key, and to allow + duplicate keys for ACEs. Thus, the set of ACES for a given file is found by looking for a key consisting of the + objectid of the grandparent (thus grouping all ACLs in a directory together), the minor packing locality of ACE, the + objectid of the file, and 0. + + IO involves moving data from one location to another, which means that two locations must be specified, source and + destination. + + This source and destination can be in the filesystem, or they can be a pointer in the user process address space plus a byte count. + + If both source and destination are in the filesystem, then at least one of them must be representable as a pure stream + of bytes (which we call a flow, and define as a struct containing a key, a data pointer, and a length). This may mean + converting one of them into a flow. We provide a generic cast_into_flow() method, which will work for any plugin + supporting read_flow(), though it is inefficiently implemented in that it temporarily stores the flow in a buffer + (Question: what to do with huge flows that cannot fit into memory? Answer: we must not convert them all at once. ) + + Performing a write requires resolving the write request into a flow defining the source, and a method that performs the write, and + a key that defines where in the tree the write is to go. + + Performing a read requires resolving the read request into a flow defining the target, and a method that performs the + read, and a key that defines where in the tree the read is to come from. + + There will exist file plugins which have no pluginid stored on the disk for them, and which are only invoked by other + plugins. + +*/ +typedef struct file_plugin { + + /* generic fields */ + plugin_header h; + + /* file_operations->open is dispatched here */ + int (*open) (struct inode * inode, struct file * file); + /* NIKITA-FIXME-HANS: comment all fields, even the ones every non-beginner FS developer knows.... */ + int (*truncate) (struct inode * inode, loff_t size); + + /* save inode cached stat-data onto disk. It was called + reiserfs_update_sd() in 3.x */ + int (*write_sd_by_inode) (struct inode * inode); + int (*readpage) (void *, struct page *); + int (*prepare_write) (struct file *, struct page *, unsigned, unsigned); + + /* captures passed page to current atom and takes care about extents handling. + This is needed for loop back devices support and used from ->commit_write() + +*/ /* ZAM-FIXME-HANS: are you writing to yourself or the reader? Bigger comment please. */ + int (*capturepage) (struct page *); + /* + * add pages created through mmap into object. + */ + int (*capture) (struct inode *inode, const struct writeback_control *wbc, long *); + /* these should be implemented using body_read_flow and body_write_flow + builtins */ + ssize_t(*read) (struct file * file, char *buf, size_t size, loff_t * off); + ssize_t(*write) (struct file * file, const char *buf, size_t size, loff_t * off); + + int (*release) (struct inode *inode, struct file * file); + int (*ioctl) (struct inode *, struct file *, unsigned int cmd, unsigned long arg); + int (*mmap) (struct file * file, struct vm_area_struct * vma); + int (*get_block) (struct inode * inode, sector_t block, struct buffer_head * bh_result, int create); +/* private methods: These are optional. If used they will allow you to + minimize the amount of code needed to implement a deviation from some other + method that also uses them. */ + + /* Construct flow into @flow according to user-supplied data. + + This is used by read/write methods to construct a flow to + write/read. ->flow_by_inode() is plugin method, rather than single + global implementation, because key in a flow used by plugin may + depend on data in a @buf. + +NIKITA-FIXME-HANS: please create statistics on what functions are +dereferenced how often for the mongo benchmark. You can supervise +Elena doing this for you if that helps. Email me the list of the top 10, with their counts, and an estimate of the total number of CPU cycles spent dereferencing as a percentage of CPU cycles spent processing (non-idle processing). If the total percent is, say, less than 1%, it will make our coding discussions much easier, and keep me from questioning whether functions like the below are too frequently called to be dereferenced. If the total percent is more than 1%, perhaps private methods should be listed in a "required" comment at the top of each plugin (with stern language about how if the comment is missing it will not be accepted by the maintainer), and implemented using macros not dereferenced functions. How about replacing this whole private methods part of the struct with a thorough documentation of what the standard helper functions are for use in constructing plugins? I think users have been asking for that, though not in so many words. + */ + int (*flow_by_inode) (struct inode *, char *buf, int user, loff_t size, loff_t off, rw_op op, flow_t *); + + /* Return the key used to retrieve an offset of a file. It is used by + default implementation of ->flow_by_inode() method + (common_build_flow()) and, among other things, to get to the extent + from jnode of unformatted node. + */ + int (*key_by_inode) (struct inode * inode, loff_t off, reiser4_key * key); + +/* NIKITA-FIXME-HANS: this comment is not as clear to others as you think.... */ + /* set the plugin for a file. Called during file creation in creat() + but not reiser4() unless an inode already exists for the file. */ + int (*set_plug_in_inode) (struct inode * inode, struct inode * parent, reiser4_object_create_data * data); + +/* NIKITA-FIXME-HANS: comment and name seem to say different things, are you setting up the object itself also or just adjusting the parent?.... */ + /* set up plugins for new @object created in @parent. @root is root + directory. */ + int (*adjust_to_parent) (struct inode * object, struct inode * parent, struct inode * root); + /* this does whatever is necessary to do when object is created. For + instance, for unix files stat data is inserted */ + int (*create) (struct inode * object, struct inode * parent, + reiser4_object_create_data * data); + /* delete empty object. This method should check REISER4_NO_SD + and set REISER4_NO_SD on success. Deletion of empty object + at least includes removal of stat-data if any. For directories this + also includes removal of dot and dot-dot. + */ + int (*delete) (struct inode * object); + + /* method implementing f_op->fsync() */ + int (*sync)(struct inode *, int datasync); + + /* add link from @parent to @object */ + int (*add_link) (struct inode * object, struct inode * parent); + + /* remove link from @parent to @object */ + int (*rem_link) (struct inode * object, struct inode * parent); + + /* return true if item addressed by @coord belongs to @inode. + This is used by read/write to properly slice flow into items + in presence of multiple key assignment policies, because + items of a file are not necessarily contiguous in a key space, + for example, in a plan-b. */ + int (*owns_item) (const struct inode * inode, const coord_t * coord); + + /* checks whether yet another hard links to this object can be + added */ + int (*can_add_link) (const struct inode * inode); + /* checks whether hard links to this object can be removed */ + int (*can_rem_link) (const struct inode * inode); + /* true if there is only one link (aka name) for this file */ + int (*not_linked) (const struct inode * inode); + + /* change inode attributes. */ + int (*setattr) (struct inode * inode, struct iattr * attr); + + /* obtain inode attributes */ + int (*getattr) (struct vfsmount * mnt UNUSED_ARG, struct dentry * dentry, struct kstat * stat); + + /* seek */ + loff_t(*seek) (struct file * f, loff_t offset, int origin); + + int (*detach)(struct inode *child, struct inode *parent); + + /* called when @child was just looked up in the @parent */ + int (*bind) (struct inode * child, struct inode * parent); + + /* process safe-link during mount */ + int (*safelink)(struct inode *object, reiser4_safe_link_t link, + __u64 value); + + /* The couple of estimate methods for all file operations */ + struct { + reiser4_block_nr (*create) (struct inode *); + reiser4_block_nr (*update) (const struct inode *); + reiser4_block_nr (*unlink) (struct inode *, struct inode *); + } estimate; + void (*readpages)(struct file *file, struct address_space *mapping, + struct list_head *pages); + /* reiser4 specific part of inode has a union of structures which are specific to a plugin. This method is + called when inode is read (read_inode) and when file is created (common_create_child) so that file plugin + could initialize its inode data */ + void (*init_inode_data)(struct inode *, reiser4_object_create_data *, int); + + /* truncate file to zero size. called by reiser4_drop_inode before truncate_inode_pages */ + int (*pre_delete)(struct inode *); + + /* called from reiser4_drop_inode() */ + void (*drop)(struct inode *); + + /* called from ->drop() when there are no links, and object should be + * garbage collected. */ + void (*delete_inode)(struct inode *); + + /* called from ->destroy_inode() */ + void (*destroy_inode)(struct inode *); + void (*forget_inode)(struct inode *); + ssize_t (*sendfile)(struct file *, loff_t *, size_t, read_actor_t, void __user *); + /* + * methods to serialize object identify. This is used, for example, by + * reiser4_{en,de}code_fh(). + */ + struct { + /* store object's identity at @area */ + char *(*write)(struct inode *inode, char *area); + /* parse object from wire to the @obj */ + char *(*read)(char *area, reiser4_object_on_wire *obj); + /* given object identity in @obj, find or create its dentry */ + struct dentry *(*get)(struct super_block *s, + reiser4_object_on_wire *obj); + /* how many bytes ->wire.write() consumes */ + int (*size)(struct inode *inode); + /* finish with object identify */ + void (*done)(reiser4_object_on_wire *obj); + } wire; +} file_plugin; + +struct reiser4_object_on_wire { + file_plugin *plugin; + union { + struct { + obj_key_id key_id; + } std; + void *generic; + } u; +}; + +typedef struct dir_plugin { + /* generic fields */ + plugin_header h; + /* for use by open call, based on name supplied will install + appropriate plugin and state information, into the inode such that + subsequent VFS operations that supply a pointer to that inode + operate in a manner appropriate. Note that this may require storing + some state for the plugin, and that this state might even include + the name used by open. */ + int (*lookup) (struct inode * parent_inode, struct dentry **dentry); + /* VFS required/defined operations below this line */ + int (*unlink) (struct inode * parent, struct dentry * victim); + int (*link) (struct inode * parent, struct dentry * existing, struct dentry * where); + /* rename object named by @old entry in @old_dir to be named by @new + entry in @new_dir */ + int (*rename) (struct inode * old_dir, struct dentry * old, struct inode * new_dir, struct dentry * new); + + /* create new object described by @data and add it to the @parent + directory under the name described by @dentry */ + int (*create_child) (reiser4_object_create_data * data, + struct inode ** retobj); + + /* readdir implementation */ + int (*readdir) (struct file * f, void *cookie, filldir_t filldir); + + /* private methods: These are optional. If used they will allow you to + minimize the amount of code needed to implement a deviation from + some other method that uses them. You could logically argue that + they should be a separate type of plugin. */ + + /* check whether "name" is acceptable name to be inserted into + this object. Optionally implemented by directory-like objects. + Can check for maximal length, reserved symbols etc */ + int (*is_name_acceptable) (const struct inode * inode, const char *name, int len); + + void (*build_entry_key) (const struct inode * dir /* directory where + * entry is (or will + * be) in.*/ , + const struct qstr * name /* name of file referenced + * by this entry */ , + reiser4_key * result /* resulting key of directory + * entry */ ); + int (*build_readdir_key) (struct file * dir, reiser4_key * result); + int (*add_entry) (struct inode * object, struct dentry * where, + reiser4_object_create_data * data, reiser4_dir_entry_desc * entry); + + int (*rem_entry) (struct inode * object, struct dentry * where, reiser4_dir_entry_desc * entry); + + /* initialize directory structure for newly created object. For normal + unix directories, insert dot and dotdot. */ + int (*init) (struct inode * object, struct inode * parent, reiser4_object_create_data * data); + /* destroy directory */ + int (*done) (struct inode * child); + + /* called when @subdir was just looked up in the @dir */ + int (*attach) (struct inode * subdir, struct inode * dir); + int (*detach)(struct inode * subdir, struct inode * dir); + + struct dentry *(*get_parent)(struct inode *childdir); + + struct { + reiser4_block_nr (*add_entry) (struct inode *node); + reiser4_block_nr (*rem_entry) (struct inode *node); + reiser4_block_nr (*unlink) (struct inode *, struct inode *); + } estimate; +} dir_plugin; + +typedef struct formatting_plugin { + /* generic fields */ + plugin_header h; + /* returns non-zero iff file's tail has to be stored + in a direct item. */ + int (*have_tail) (const struct inode * inode, loff_t size); +} formatting_plugin; + +typedef struct hash_plugin { + /* generic fields */ + plugin_header h; + /* computes hash of the given name */ + __u64(*hash) (const unsigned char *name, int len); +} hash_plugin; + +typedef struct crypto_plugin { + /* generic fields */ + plugin_header h; + int (*alloc) (struct inode * inode); + void (*free) (struct inode * inode); + /* number of cpu expkey words */ + unsigned nr_keywords; + /* Offset translator. For each offset this returns (k * offset), where + k (k >= 1) is a coefficient of expansion of the crypto algorithm. + For all symmetric algorithms k == 1. For asymmetric algorithms (which + inflate data) offset translation guarantees that all disk cluster's + units will have keys smaller then next cluster's one. + */ + loff_t (*scale)(struct inode * inode, size_t blocksize, loff_t src); + /* Crypto algorithms can accept data only by chunks of crypto block + size. This method is to align any flow up to crypto block size when + we pass it to crypto algorithm. To align means to append padding of + special format specific to the crypto algorithm */ + int (*align_cluster)(__u8 *tail, int clust_size, int blocksize); + /* low-level key manager (check, install, etc..) */ + int (*setkey) (struct crypto_tfm *tfm, const __u8 *key, unsigned int keylen); + /* main text processing procedures */ + void (*encrypt) (__u32 *expkey, __u8 *dst, const __u8 *src); + void (*decrypt) (__u32 *expkey, __u8 *dst, const __u8 *src); +} crypto_plugin; + +typedef struct digest_plugin { + /* generic fields */ + plugin_header h; + /* digest size */ + int dsize; + int (*alloc) (struct inode * inode); + void (*free) (struct inode * inode); +} digest_plugin; + + +typedef void * tfm_info_t; +#define SQUEEZE_TFM_INFO_SIZE (LAST_COMPRESSION_ID * sizeof(tfm_info_t)) + +typedef struct compression_plugin { + /* generic fields */ + plugin_header h; + /* the maximum number of bytes the size of the "compressed" data can + * exceed the uncompressed data. */ + int (*overrun) (unsigned src_len); + int (*alloc) (tfm_info_t * ctx, tfm_action act); + void (*free) (tfm_info_t * ctx, tfm_action act); + /* main text processing procedures */ + void (*compress) (tfm_info_t ctx, __u8 *src_first, unsigned src_len, + __u8 *dst_first, unsigned *dst_len); + void (*decompress) (tfm_info_t ctx, __u8 *src_first, unsigned src_len, + __u8 *dst_first, unsigned *dst_len); +}compression_plugin; + +typedef struct sd_ext_plugin { + /* generic fields */ + plugin_header h; + int (*present) (struct inode * inode, char **area, int *len); + int (*absent) (struct inode * inode); + int (*save_len) (struct inode * inode); + int (*save) (struct inode * inode, char **area); +#if REISER4_DEBUG_OUTPUT + void (*print) (const char *prefix, char **area, int *len); +#endif + /* alignment requirement for this stat-data part */ + int alignment; +} sd_ext_plugin; + +/* this plugin contains methods to allocate objectid for newly created files, + to deallocate objectid when file gets removed, to report number of used and + free objectids */ +typedef struct oid_allocator_plugin { + /* generic fields */ + plugin_header h; + int (*init_oid_allocator) (reiser4_oid_allocator * map, __u64 nr_files, __u64 oids); + /* used to report statfs->f_files */ + __u64(*oids_used) (reiser4_oid_allocator * map); + /* get next oid to use */ + __u64(*next_oid) (reiser4_oid_allocator * map); + /* used to report statfs->f_ffree */ + __u64(*oids_free) (reiser4_oid_allocator * map); + /* allocate new objectid */ + int (*allocate_oid) (reiser4_oid_allocator * map, oid_t *); + /* release objectid */ + int (*release_oid) (reiser4_oid_allocator * map, oid_t); + /* how many pages to reserve in transaction for allocation of new + objectid */ + int (*oid_reserve_allocate) (reiser4_oid_allocator * map); + /* how many pages to reserve in transaction for freeing of an + objectid */ + int (*oid_reserve_release) (reiser4_oid_allocator * map); + void (*print_info) (const char *, reiser4_oid_allocator *); +} oid_allocator_plugin; + +/* disk layout plugin: this specifies super block, journal, bitmap (if there + are any) locations, etc */ +typedef struct disk_format_plugin { + /* generic fields */ + plugin_header h; + /* replay journal, initialize super_info_data, etc */ + int (*get_ready) (struct super_block *, void *data); + + /* key of root directory stat data */ + const reiser4_key *(*root_dir_key) (const struct super_block *); + + int (*release) (struct super_block *); + jnode *(*log_super) (struct super_block *); + void (*print_info) (const struct super_block *); + int (*check_open) (const struct inode *object); +} disk_format_plugin; + +struct jnode_plugin { + /* generic fields */ + plugin_header h; + int (*init) (jnode * node); + int (*parse) (jnode * node); + struct address_space *(*mapping) (const jnode * node); + unsigned long (*index) (const jnode * node); + jnode *(*clone) (jnode * node); +}; + +/* plugin instance. */ +/* */ +/* This is "wrapper" union for all types of plugins. Most of the code uses */ +/* plugins of particular type (file_plugin, dir_plugin, etc.) rather than */ +/* operates with pointers to reiser4_plugin. This union is only used in */ +/* some generic code in plugin/plugin.c that operates on all */ +/* plugins. Technically speaking purpose of this union is to add type */ +/* safety to said generic code: each plugin type (file_plugin, for */ +/* example), contains plugin_header as its first memeber. This first member */ +/* is located at the same place in memory as .h member of */ +/* reiser4_plugin. Generic code, obtains pointer to reiser4_plugin and */ +/* looks in the .h which is header of plugin type located in union. This */ +/* allows to avoid type-casts. */ +union reiser4_plugin { + /* generic fields */ + plugin_header h; + /* file plugin */ + file_plugin file; + /* directory plugin */ + dir_plugin dir; + /* hash plugin, used by directory plugin */ + hash_plugin hash; + /* fibration plugin used by directory plugin */ + fibration_plugin fibration; + /* crypto plugin, used by file plugin */ + crypto_plugin crypto; + /* digest plugin, used by file plugin */ + digest_plugin digest; + /* compression plugin, used by file plugin */ + compression_plugin compression; + /* tail plugin, used by file plugin */ + formatting_plugin formatting; + /* permission plugin */ + perm_plugin perm; + /* node plugin */ + node_plugin node; + /* item plugin */ + item_plugin item; + /* stat-data extension plugin */ + sd_ext_plugin sd_ext; + /* disk layout plugin */ + disk_format_plugin format; + /* object id allocator plugin */ + oid_allocator_plugin oid_allocator; + /* plugin for different jnode types */ + jnode_plugin jnode; + /* plugin for pseudo files */ + pseudo_plugin pseudo; + /* place-holder for new plugin types that can be registered + dynamically, and used by other dynamically loaded plugins. */ + void *generic; +}; + +struct reiser4_plugin_ops { + /* called when plugin is initialized */ + int (*init) (reiser4_plugin * plugin); + /* called when plugin is unloaded */ + int (*done) (reiser4_plugin * plugin); + /* load given plugin from disk */ + int (*load) (struct inode * inode, + reiser4_plugin * plugin, char **area, int *len); + /* how many space is required to store this plugin's state + in stat-data */ + int (*save_len) (struct inode * inode, reiser4_plugin * plugin); + /* save persistent plugin-data to disk */ + int (*save) (struct inode * inode, reiser4_plugin * plugin, char **area); + /* alignment requirement for on-disk state of this plugin + in number of bytes */ + int alignment; + /* install itself into given inode. This can return error + (e.g., you cannot change hash of non-empty directory). */ + int (*change) (struct inode * inode, reiser4_plugin * plugin); + /* install itself into given inode. This can return error + (e.g., you cannot change hash of non-empty directory). */ + int (*inherit) (struct inode * inode, struct inode * parent, + reiser4_plugin * plugin); +}; + +/* functions implemented in fs/reiser4/plugin/plugin.c */ + +/* stores plugin reference in reiser4-specific part of inode */ +extern int set_object_plugin(struct inode *inode, reiser4_plugin_id id); +extern int setup_plugins(struct super_block *super, reiser4_plugin ** area); +extern reiser4_plugin *lookup_plugin(const char *type_label, const char *plug_label); +extern int init_plugins(void); + +/* functions implemented in fs/reiser4/plugin/object.c */ +void move_flow_forward(flow_t * f, unsigned count); + +/* builtin plugins */ + +/* builtin file-plugins */ +typedef enum { + /* regular file */ + UNIX_FILE_PLUGIN_ID, + /* directory */ + DIRECTORY_FILE_PLUGIN_ID, + /* symlink */ + SYMLINK_FILE_PLUGIN_ID, + /* for objects completely handled by the VFS: fifos, devices, + sockets */ + SPECIAL_FILE_PLUGIN_ID, + /* Plugin id for crypto-compression objects */ + CRC_FILE_PLUGIN_ID, + /* pseudo file */ + PSEUDO_FILE_PLUGIN_ID, + /* number of file plugins. Used as size of arrays to hold + file plugins. */ + LAST_FILE_PLUGIN_ID +} reiser4_file_id; + +/* builtin dir-plugins */ +typedef enum { + HASHED_DIR_PLUGIN_ID, + SEEKABLE_HASHED_DIR_PLUGIN_ID, + PSEUDO_DIR_PLUGIN_ID, + LAST_DIR_ID +} reiser4_dir_id; + +/* builtin hash-plugins */ + +typedef enum { + RUPASOV_HASH_ID, + R5_HASH_ID, + TEA_HASH_ID, + FNV1_HASH_ID, + DEGENERATE_HASH_ID, + LAST_HASH_ID +} reiser4_hash_id; + +/* builtin crypto-plugins */ + +typedef enum { + NONE_CRYPTO_ID, + LAST_CRYPTO_ID +} reiser4_crypto_id; + +/* builtin digest plugins */ + +typedef enum { + NONE_DIGEST_ID, + LAST_DIGEST_ID +} reiser4_digest_id; + +/* builtin compression plugins */ + +typedef enum { + NONE_COMPRESSION_ID, + NULL_COMPRESSION_ID, + LZO1_COMPRESSION_ID, + GZIP1_COMPRESSION_ID, + LAST_COMPRESSION_ID +} reiser4_compression_id; + +/* builtin tail-plugins */ + +typedef enum { + NEVER_TAILS_FORMATTING_ID, + ALWAYS_TAILS_FORMATTING_ID, + SMALL_FILE_FORMATTING_ID, + LAST_TAIL_FORMATTING_ID +} reiser4_formatting_id; + +/* Encapsulations of crypto specific data */ +typedef struct crypto_data { + reiser4_crypto_id cra; /* id of the crypto algorithm */ + reiser4_digest_id dia; /* id of the digest algorithm */ + __u8 * key; /* secret key */ + __u16 keysize; /* key size, bits */ + __u8 * keyid; /* keyid */ + __u16 keyid_size; /* keyid size, bytes */ +} crypto_data_t; + +/* compression/clustering specific data */ +typedef struct compression_data { + reiser4_compression_id coa; /* id of the compression algorithm */ +} compression_data_t; + +typedef __u8 cluster_data_t; /* cluster info */ + +/* data type used to pack parameters that we pass to vfs + object creation function create_object() */ +struct reiser4_object_create_data { + /* plugin to control created object */ + reiser4_file_id id; + /* mode of regular file, directory or special file */ +/* what happens if some other sort of perm plugin is in use? */ + int mode; + /* rdev of special file */ + dev_t rdev; + /* symlink target */ + const char *name; + /* add here something for non-standard objects you invent, like + query for interpolation file etc. */ + crypto_data_t * crypto; + compression_data_t * compression; + cluster_data_t * cluster; + + struct inode *parent; + struct dentry *dentry; +}; + +#define MAX_PLUGIN_TYPE_LABEL_LEN 32 +#define MAX_PLUGIN_PLUG_LABEL_LEN 32 + +/* used for interface with user-land: table-driven parsing in + reiser4(). */ +typedef struct plugin_locator { + reiser4_plugin_type type_id; + reiser4_plugin_id id; + char type_label[MAX_PLUGIN_TYPE_LABEL_LEN]; + char plug_label[MAX_PLUGIN_PLUG_LABEL_LEN]; +} plugin_locator; + +extern int locate_plugin(struct inode *inode, plugin_locator * loc); + +static inline reiser4_plugin * +plugin_by_id(reiser4_plugin_type type_id, reiser4_plugin_id id); + +static inline reiser4_plugin * +plugin_by_disk_id(reiser4_tree * tree, reiser4_plugin_type type_id, d16 * did); + +#define PLUGIN_BY_ID(TYPE,ID,FIELD) \ +static inline TYPE *TYPE ## _by_id( reiser4_plugin_id id ) \ +{ \ + reiser4_plugin *plugin = plugin_by_id ( ID, id ); \ + return plugin ? & plugin -> FIELD : NULL; \ +} \ +static inline TYPE *TYPE ## _by_disk_id( reiser4_tree *tree, d16 *id ) \ +{ \ + reiser4_plugin *plugin = plugin_by_disk_id ( tree, ID, id ); \ + return plugin ? & plugin -> FIELD : NULL; \ +} \ +static inline TYPE *TYPE ## _by_unsafe_id( reiser4_plugin_id id ) \ +{ \ + reiser4_plugin *plugin = plugin_by_unsafe_id ( ID, id ); \ + return plugin ? & plugin -> FIELD : NULL; \ +} \ +static inline reiser4_plugin* TYPE ## _to_plugin( TYPE* plugin ) \ +{ \ + return ( reiser4_plugin * ) plugin; \ +} \ +static inline reiser4_plugin_id TYPE ## _id( TYPE* plugin ) \ +{ \ + return TYPE ## _to_plugin (plugin) -> h.id; \ +} \ +typedef struct { int foo; } TYPE ## _plugin_dummy + +PLUGIN_BY_ID(item_plugin, REISER4_ITEM_PLUGIN_TYPE, item); +PLUGIN_BY_ID(file_plugin, REISER4_FILE_PLUGIN_TYPE, file); +PLUGIN_BY_ID(dir_plugin, REISER4_DIR_PLUGIN_TYPE, dir); +PLUGIN_BY_ID(node_plugin, REISER4_NODE_PLUGIN_TYPE, node); +PLUGIN_BY_ID(sd_ext_plugin, REISER4_SD_EXT_PLUGIN_TYPE, sd_ext); +PLUGIN_BY_ID(perm_plugin, REISER4_PERM_PLUGIN_TYPE, perm); +PLUGIN_BY_ID(hash_plugin, REISER4_HASH_PLUGIN_TYPE, hash); +PLUGIN_BY_ID(fibration_plugin, REISER4_FIBRATION_PLUGIN_TYPE, fibration); +PLUGIN_BY_ID(crypto_plugin, REISER4_CRYPTO_PLUGIN_TYPE, crypto); +PLUGIN_BY_ID(digest_plugin, REISER4_DIGEST_PLUGIN_TYPE, digest); +PLUGIN_BY_ID(compression_plugin, REISER4_COMPRESSION_PLUGIN_TYPE, compression); +PLUGIN_BY_ID(formatting_plugin, REISER4_FORMATTING_PLUGIN_TYPE, formatting); +PLUGIN_BY_ID(disk_format_plugin, REISER4_FORMAT_PLUGIN_TYPE, format); +PLUGIN_BY_ID(jnode_plugin, REISER4_JNODE_PLUGIN_TYPE, jnode); +PLUGIN_BY_ID(pseudo_plugin, REISER4_PSEUDO_PLUGIN_TYPE, pseudo); + +extern int save_plugin_id(reiser4_plugin * plugin, d16 * area); + +#if REISER4_DEBUG_OUTPUT +extern void print_plugin(const char *prefix, reiser4_plugin * plugin); +#else +#define print_plugin( pr, pl ) noop +#endif + +TYPE_SAFE_LIST_DEFINE(plugin, reiser4_plugin, h.linkage); + +extern plugin_list_head *get_plugin_list(reiser4_plugin_type type_id); + +#define for_all_plugins( ptype, plugin ) \ +for( plugin = plugin_list_front( get_plugin_list( ptype ) ) ; \ + ! plugin_list_end( get_plugin_list( ptype ), plugin ) ; \ + plugin = plugin_list_next( plugin ) ) + +/* enumeration of fields within plugin_set */ +typedef enum { + PSET_FILE, + PSET_DIR, /* PSET_FILE and PSET_DIR should be first elements: + * inode.c:read_inode() depends on this. */ + PSET_PERM, + PSET_FORMATTING, + PSET_HASH, + PSET_FIBRATION, + PSET_SD, + PSET_DIR_ITEM, + PSET_CRYPTO, + PSET_DIGEST, + PSET_COMPRESSION, + PSET_LAST +} pset_member; + +int grab_plugin(struct inode *self, struct inode *ancestor, pset_member memb); +int grab_plugin_from(struct inode *self, pset_member memb, reiser4_plugin *plug); +int force_plugin(struct inode *self, pset_member memb, reiser4_plugin *plug); + +/* __FS_REISER4_PLUGIN_TYPES_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/plugin_header.h 2004-09-03 00:57:05.324229856 -0700 @@ -0,0 +1,136 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* plugin header. Data structures required by all plugin types. */ + +#if !defined( __PLUGIN_HEADER_H__ ) +#define __PLUGIN_HEADER_H__ + +/* plugin data-types and constants */ + +#include "../type_safe_list.h" +#include "../dformat.h" + +typedef enum { + REISER4_FILE_PLUGIN_TYPE, + REISER4_DIR_PLUGIN_TYPE, + REISER4_ITEM_PLUGIN_TYPE, + REISER4_NODE_PLUGIN_TYPE, + REISER4_HASH_PLUGIN_TYPE, + REISER4_FIBRATION_PLUGIN_TYPE, + REISER4_FORMATTING_PLUGIN_TYPE, + REISER4_PERM_PLUGIN_TYPE, + REISER4_SD_EXT_PLUGIN_TYPE, + REISER4_FORMAT_PLUGIN_TYPE, + REISER4_JNODE_PLUGIN_TYPE, + REISER4_CRYPTO_PLUGIN_TYPE, + REISER4_DIGEST_PLUGIN_TYPE, + REISER4_COMPRESSION_PLUGIN_TYPE, + REISER4_PSEUDO_PLUGIN_TYPE, + REISER4_PLUGIN_TYPES +} reiser4_plugin_type; + +struct reiser4_plugin_ops; +/* generic plugin operations, supported by each + plugin type. */ +typedef struct reiser4_plugin_ops reiser4_plugin_ops; + +TYPE_SAFE_LIST_DECLARE(plugin); + +/* the common part of all plugin instances. */ +typedef struct plugin_header { + /* plugin type */ + reiser4_plugin_type type_id; + /* id of this plugin */ + reiser4_plugin_id id; + /* plugin operations */ + reiser4_plugin_ops *pops; +/* NIKITA-FIXME-HANS: usage of and access to label and desc is not commented and defined. */ + /* short label of this plugin */ + const char *label; + /* descriptive string.. */ + const char *desc; + /* list linkage */ + plugin_list_link linkage; +} plugin_header; + + +/* PRIVATE INTERFACES */ +/* NIKITA-FIXME-HANS: what is this for and why does it duplicate what is in plugin_header? */ +/* plugin type representation. */ +typedef struct reiser4_plugin_type_data { + /* internal plugin type identifier. Should coincide with + index of this item in plugins[] array. */ + reiser4_plugin_type type_id; + /* short symbolic label of this plugin type. Should be no longer + than MAX_PLUGIN_TYPE_LABEL_LEN characters including '\0'. */ + const char *label; + /* plugin type description longer than .label */ + const char *desc; + +/* NIKITA-FIXME-HANS: define built-in */ + /* number of built-in plugin instances of this type */ + int builtin_num; + /* array of built-in plugins */ + void *builtin; + plugin_list_head plugins_list; + size_t size; +} reiser4_plugin_type_data; + +extern reiser4_plugin_type_data plugins[REISER4_PLUGIN_TYPES]; + +int is_type_id_valid(reiser4_plugin_type type_id); +int is_plugin_id_valid(reiser4_plugin_type type_id, reiser4_plugin_id id); + +static inline reiser4_plugin * +plugin_at(reiser4_plugin_type_data * ptype, int i) +{ + char *builtin; + + builtin = ptype->builtin; + return (reiser4_plugin *) (builtin + i * ptype->size); +} + + +/* return plugin by its @type_id and @id */ +static inline reiser4_plugin * +plugin_by_id(reiser4_plugin_type type_id /* plugin type id */ , + reiser4_plugin_id id /* plugin id */ ) +{ + assert("nikita-1651", is_type_id_valid(type_id)); + assert("nikita-1652", is_plugin_id_valid(type_id, id)); + return plugin_at(&plugins[type_id], id); +} + +extern reiser4_plugin * +plugin_by_unsafe_id(reiser4_plugin_type type_id, reiser4_plugin_id id); + +/* get plugin whose id is stored in disk format */ +static inline reiser4_plugin * +plugin_by_disk_id(reiser4_tree * tree UNUSED_ARG /* tree, + * plugin + * belongs + * to */ , + reiser4_plugin_type type_id /* plugin type + * id */ , + d16 * did /* plugin id in disk format */ ) +{ + /* what we should do properly is to maintain within each + file-system a dictionary that maps on-disk plugin ids to + "universal" ids. This dictionary will be resolved on mount + time, so that this function will perform just one additional + array lookup. */ + return plugin_by_unsafe_id(type_id, d16tocpu(did)); +} + +/* __PLUGIN_HEADER_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/plugin_set.c 2004-09-03 00:57:05.326229552 -0700 @@ -0,0 +1,345 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ +/* NIKITA-FIXME-HANS: you didn't discuss this with me before coding it did you? Remove plugin-sets from code by March 15th, 2004 */ +/* plugin-sets */ + +/* + * Each inode comes with a whole set of plugins: file plugin, directory + * plugin, hash plugin, tail policy plugin, security plugin, etc. + * + * Storing them (pointers to them, that is) in inode is a waste of + * space. Especially, given that on average file system plugins of vast + * majority of files will belong to few sets (e.g., one set for regular files, + * another set for standard directory, etc.) + * + * Plugin set (pset) is an object containing pointers to all plugins required + * by inode. Inode only stores a pointer to pset. psets are "interned", that + * is, different inodes with the same set of plugins point to the same + * pset. This is archived by storing psets in global hash table. Races are + * avoided by simple (and efficient so far) solution of never recycling psets, + * even when last inode pointing to it is destroyed. + * + */ + +#include "../debug.h" + +#include "plugin_set.h" + +#include +#include + +/* slab for plugin sets */ +static kmem_cache_t *plugin_set_slab; + +static spinlock_t plugin_set_lock[8] __cacheline_aligned_in_smp = { + [0 ... 7] = SPIN_LOCK_UNLOCKED +}; + +/* hash table support */ + +#define PS_TABLE_SIZE (32) + +static inline plugin_set * +cast_to(const unsigned long * a) +{ + return container_of(a, plugin_set, hashval); +} + +static inline int +pseq(const unsigned long * a1, const unsigned long * a2) +{ + plugin_set *set1; + plugin_set *set2; + + /* make sure fields are not missed in the code below */ + cassert(sizeof *set1 == + + sizeof set1->hashval + + sizeof set1->link + + + sizeof set1->file + + sizeof set1->dir + + sizeof set1->perm + + sizeof set1->formatting + + sizeof set1->hash + + sizeof set1->fibration + + sizeof set1->sd + + sizeof set1->dir_item + + sizeof set1->crypto + + sizeof set1->digest + + sizeof set1->compression); + + set1 = cast_to(a1); + set2 = cast_to(a2); + return + set1->hashval == set2->hashval && + + set1->file == set2->file && + set1->dir == set2->dir && + set1->perm == set2->perm && + set1->formatting == set2->formatting && + set1->hash == set2->hash && + set1->fibration == set2->fibration && + set1->sd == set2->sd && + set1->dir_item == set2->dir_item && + set1->crypto == set2->crypto && + set1->digest == set2->digest && + set1->compression == set2->compression; +} + +#define HASH_FIELD(hash, set, field) \ +({ \ + (hash) += (unsigned long)(set)->field >> 2; \ +}) + +static inline unsigned long calculate_hash(const plugin_set *set) +{ + unsigned long result; + + result = 0; + HASH_FIELD(result, set, file); + HASH_FIELD(result, set, dir); + HASH_FIELD(result, set, perm); + HASH_FIELD(result, set, formatting); + HASH_FIELD(result, set, hash); + HASH_FIELD(result, set, fibration); + HASH_FIELD(result, set, sd); + HASH_FIELD(result, set, dir_item); + HASH_FIELD(result, set, crypto); + HASH_FIELD(result, set, digest); + HASH_FIELD(result, set, compression); + return result & (PS_TABLE_SIZE - 1); +} + +static inline unsigned long +pshash(ps_hash_table *table, const unsigned long * a) +{ + return *a; +} + +/* The hash table definition */ +#define KMALLOC(size) kmalloc((size), GFP_KERNEL) +#define KFREE(ptr, size) kfree(ptr) +TYPE_SAFE_HASH_DEFINE(ps, plugin_set, unsigned long, hashval, link, pshash, pseq); +#undef KFREE +#undef KMALLOC + +static ps_hash_table ps_table; +static plugin_set empty_set = { + .hashval = 0, + .file = NULL, + .dir = NULL, + .perm = NULL, + .formatting = NULL, + .hash = NULL, + .fibration = NULL, + .sd = NULL, + .dir_item = NULL, + .crypto = NULL, + .digest = NULL, + .compression = NULL, + .link = { NULL } +}; + +reiser4_internal plugin_set *plugin_set_get_empty(void) +{ + return &empty_set; +} + +reiser4_internal void plugin_set_put(plugin_set *set) +{ +} + +reiser4_internal plugin_set *plugin_set_clone(plugin_set *set) +{ + return set; +} + +static inline unsigned long * +pset_field(plugin_set *set, int offset) +{ + return (unsigned long *)(((char *)set) + offset); +} + +static int plugin_set_field(plugin_set **set, const unsigned long val, const int offset) +{ + unsigned long *spot; + spinlock_t *lock; + plugin_set replica; + plugin_set *twin; + plugin_set *psal; + plugin_set *orig; + + assert("nikita-2902", set != NULL); + assert("nikita-2904", *set != NULL); + + spot = pset_field(*set, offset); + if (unlikely(*spot == val)) + return 0; + + replica = *(orig = *set); + *pset_field(&replica, offset) = val; + replica.hashval = calculate_hash(&replica); + rcu_read_lock(); + twin = ps_hash_find(&ps_table, &replica.hashval); + if (unlikely(twin == NULL)) { + rcu_read_unlock(); + psal = kmem_cache_alloc(plugin_set_slab, GFP_KERNEL); + if (psal == NULL) + return RETERR(-ENOMEM); + *psal = replica; + lock = &plugin_set_lock[replica.hashval & 7]; + spin_lock(lock); + twin = ps_hash_find(&ps_table, &replica.hashval); + if (likely(twin == NULL)) { + *set = psal; + ps_hash_insert_rcu(&ps_table, psal); + } else { + *set = twin; + kmem_cache_free(plugin_set_slab, psal); + } + spin_unlock(lock); + } else { + rcu_read_unlock(); + *set = twin; + } + return 0; +} + +static struct { + int offset; + reiser4_plugin_type type; +} pset_descr[PSET_LAST] = { + [PSET_FILE] = { + .offset = offsetof(plugin_set, file), + .type = REISER4_FILE_PLUGIN_TYPE + }, + [PSET_DIR] = { + .offset = offsetof(plugin_set, dir), + .type = REISER4_DIR_PLUGIN_TYPE + }, + [PSET_PERM] = { + .offset = offsetof(plugin_set, perm), + .type = REISER4_PERM_PLUGIN_TYPE + }, + [PSET_FORMATTING] = { + .offset = offsetof(plugin_set, formatting), + .type = REISER4_FORMATTING_PLUGIN_TYPE + }, + [PSET_HASH] = { + .offset = offsetof(plugin_set, hash), + .type = REISER4_HASH_PLUGIN_TYPE + }, + [PSET_FIBRATION] = { + .offset = offsetof(plugin_set, fibration), + .type = REISER4_FIBRATION_PLUGIN_TYPE + }, + [PSET_SD] = { + .offset = offsetof(plugin_set, sd), + .type = REISER4_ITEM_PLUGIN_TYPE + }, + [PSET_DIR_ITEM] = { + .offset = offsetof(plugin_set, dir_item), + .type = REISER4_ITEM_PLUGIN_TYPE + }, + [PSET_CRYPTO] = { + .offset = offsetof(plugin_set, crypto), + .type = REISER4_CRYPTO_PLUGIN_TYPE + }, + [PSET_DIGEST] = { + .offset = offsetof(plugin_set, digest), + .type = REISER4_DIGEST_PLUGIN_TYPE + }, + [PSET_COMPRESSION] = { + .offset = offsetof(plugin_set, compression), + .type = REISER4_COMPRESSION_PLUGIN_TYPE + } +}; + +int pset_set(plugin_set **set, pset_member memb, reiser4_plugin *plugin) +{ + assert("nikita-3492", set != NULL); + assert("nikita-3493", *set != NULL); + assert("nikita-3494", plugin != NULL); + assert("nikita-3495", 0 <= memb && memb < PSET_LAST); + assert("nikita-3496", plugin->h.type_id == pset_member_to_type(memb)); + + return plugin_set_field(set, + (unsigned long)plugin, pset_descr[memb].offset); +} + +reiser4_plugin *pset_get(plugin_set *set, pset_member memb) +{ + assert("nikita-3497", set != NULL); + assert("nikita-3498", 0 <= memb && memb < PSET_LAST); + + return *(reiser4_plugin **)(((char *)set) + pset_descr[memb].offset); +} + +reiser4_plugin_type pset_member_to_type(pset_member memb) +{ + assert("nikita-3501", 0 <= memb && memb < PSET_LAST); + return pset_descr[memb].type; +} + +reiser4_plugin_type pset_member_to_type_unsafe(pset_member memb) +{ + if (0 <= memb && memb < PSET_LAST) + return pset_descr[memb].type; + else + return REISER4_PLUGIN_TYPES; +} + +#define DEFINE_PLUGIN_SET(type, field) \ +reiser4_internal int plugin_set_ ## field(plugin_set **set, type *val) \ +{ \ + cassert(sizeof val == sizeof(unsigned long)); \ + return plugin_set_field(set, (unsigned long)val, \ + offsetof(plugin_set, field)); \ +} + +DEFINE_PLUGIN_SET(file_plugin, file) +DEFINE_PLUGIN_SET(dir_plugin, dir) +DEFINE_PLUGIN_SET(perm_plugin, perm) +DEFINE_PLUGIN_SET(formatting_plugin, formatting) +DEFINE_PLUGIN_SET(hash_plugin, hash) +DEFINE_PLUGIN_SET(fibration_plugin, fibration) +DEFINE_PLUGIN_SET(item_plugin, sd) +DEFINE_PLUGIN_SET(item_plugin, dir_item) +DEFINE_PLUGIN_SET(crypto_plugin, crypto) +DEFINE_PLUGIN_SET(digest_plugin, digest) +DEFINE_PLUGIN_SET(compression_plugin, compression) + +reiser4_internal int plugin_set_init(void) +{ + int result; + + result = ps_hash_init(&ps_table, PS_TABLE_SIZE, NULL); + if (result == 0) { + plugin_set_slab = kmem_cache_create("plugin_set", + sizeof (plugin_set), 0, + SLAB_HWCACHE_ALIGN, + NULL, NULL); + if (plugin_set_slab == NULL) + result = RETERR(-ENOMEM); + } + return result; +} + +reiser4_internal void plugin_set_done(void) +{ + /* NOTE: scan hash table and recycle all objects. */ + kmem_cache_destroy(plugin_set_slab); + ps_hash_done(&ps_table); +} + + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/plugin_set.h 2004-09-03 00:57:05.326229552 -0700 @@ -0,0 +1,81 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* plugin-sets. see fs/reiser4/plugin/plugin_set.c for details */ + +#if !defined( __PLUGIN_SET_H__ ) +#define __PLUGIN_SET_H__ + +#include "../type_safe_hash.h" +#include "plugin.h" + +#include + +struct plugin_set; +typedef struct plugin_set plugin_set; + +TYPE_SAFE_HASH_DECLARE(ps, plugin_set); + +struct plugin_set { + unsigned long hashval; + /* plugin of file */ + file_plugin *file; + /* plugin of dir */ + dir_plugin *dir; + /* perm plugin for this file */ + perm_plugin *perm; + /* tail policy plugin. Only meaningful for regular files */ + formatting_plugin *formatting; + /* hash plugin. Only meaningful for directories. */ + hash_plugin *hash; + /* fibration plugin. Only meaningful for directories. */ + fibration_plugin *fibration; + /* plugin of stat-data */ + item_plugin *sd; + /* plugin of items a directory is built of */ + item_plugin *dir_item; + /* crypto plugin */ + crypto_plugin *crypto; + /* digest plugin */ + digest_plugin *digest; + /* compression plugin */ + compression_plugin *compression; + ps_hash_link link; +}; + +extern plugin_set *plugin_set_get_empty(void); +extern plugin_set *plugin_set_clone(plugin_set *set); +extern void plugin_set_put(plugin_set *set); + +extern int plugin_set_file (plugin_set **set, file_plugin *file); +extern int plugin_set_dir (plugin_set **set, dir_plugin *file); +extern int plugin_set_perm (plugin_set **set, perm_plugin *file); +extern int plugin_set_formatting (plugin_set **set, formatting_plugin *file); +extern int plugin_set_hash (plugin_set **set, hash_plugin *file); +extern int plugin_set_fibration (plugin_set **set, fibration_plugin *file); +extern int plugin_set_sd (plugin_set **set, item_plugin *file); +extern int plugin_set_dir_item (plugin_set **set, item_plugin *file); +extern int plugin_set_crypto (plugin_set **set, crypto_plugin *file); +extern int plugin_set_digest (plugin_set **set, digest_plugin *file); +extern int plugin_set_compression(plugin_set **set, compression_plugin *file); + +extern int plugin_set_init(void); +extern void plugin_set_done(void); + +extern int pset_set(plugin_set **set, pset_member memb, reiser4_plugin *plugin); +extern reiser4_plugin *pset_get(plugin_set *set, pset_member memb); + +extern reiser4_plugin_type pset_member_to_type(pset_member memb); +extern reiser4_plugin_type pset_member_to_type_unsafe(pset_member memb); + +/* __PLUGIN_SET_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/pseudo/pseudo.c 2004-09-03 00:57:06.987976928 -0700 @@ -0,0 +1,1804 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Handling of "pseudo" files representing unified access to meta data in + reiser4. */ + +/* + * See http://namesys.com/v4/v4.html, and especially + * http://namesys.com/v4/pseudo.html for basic information about reiser4 + * pseudo files, access to meta-data, reiser4() system call, etc. + * + * Pseudo files should be accessible from both reiser4() system call and + * normal POSIX calls. + * + * OVERVIEW + * + * Pseudo files provide access to various functionality through file + * system name space. As such they are similar to pseudo file systems + * already existing in UNIX and Linux: procfs, sysfs, etc. But pseudo + * files are embedded into name space of Reiser4---real block device based + * file system, and are more tightly integrated with it. In particular, + * some pseudo files are "attached" to other files (either "real" or also + * pseudo), by being accessible through path names of the form + * + * "a/b/c/something" + * + * Here object accessible through "a/b/c/something" is attached to the + * object accessible through "a/b/c" , and the latter is said to be the + * "host" object of the former. + * + * Object can have multiple pseudo files attached to it, distinguished by + * the last component of their names "something", "somethingelse", + * etc. + * + * (Note however, that currently "real" files have only one single pseudo + * file attached to them, viz. pseudo directory "metas". This directory in + * turn contains all other pseudo files pertaining to the real file that + * "metas" is attached to. To avoid referencing "metas" all the time + * "a/b/c" is called a host of "/a/b/c/metas/something". This violates + * definition above, but is convenient.) + * + * Moreover, in addition to the purely pseudo files (that is, file system + * objects whose content (as available through read(2) system call) is not + * backed by any kind of persistent storage), extended file attributes + * (see attr(5) on Linux, and http://acl.bestbits.at/) including security + * attributes such as ACLs are also available through file system name + * space. + * + * As a result each file system object has a sub-name-space rooted at it, + * which is in striking contrast with traditional UNIX file system, where + * only directories has sub-objects and all other types of files (regular, + * FIFO-s, devices, and symlinks) are leaves. + * + * For the sake of objectivity it should be mentioned that this is not + * _completely_ new development in file system design, see + * http://docs.sun.com/db/doc/816-0220/6m6nkorp9?a=view + * + * In particular, as each object has sub-objects, name space tree is + * infinite in both extent (number of reachable objects) and depth. + * + * Some pseudo files are "built-in". They are present as sub-objects in + * each file system object, unless specifically disabled. + * + * Built-in pseudo files are implemented in this file and described at + * http://namesys.com/v4/pseudo.html + * + * IMPLEMENTATION + * + * Pseudo files are implemented as normal inodes, living in the same super + * block as other inodes for reiser4 file system. Their inode numbers are + * generated by fs/inode.c:new_inode() function and are not persistent (in + * the sense that they are not guaranteed to be the same after + * remount). To avoid clashes with "normal" inodes, all pseudo inodes are + * placed into otherwise unused locality (for example, 0), hence allowing + * reiser4_inode_find_actor() to tell them from normal inodes. + * + * All pseudo inodes share the same object plugin + * PSEUDO_FILE_PLUGIN_ID. In pseudo-inode specific part of reiser4_inode + * (pseudo_info), two things are stored: + * + * 1. pointer to the inode of the "host object" (for /a/b/c/metas/acl, + * /a/b/c is the host object) + * + * 2. pointer to pseudo plugin, used by PSEUDO_FILE_PLUGIN_ID to + * implement VFS operations. + * + * This design has following advantages: + * + * 1. provides for ease addition of new pseudo files without going + * through writing whole new object plugin. + * + * 2. allows sys_reiser4() to be implemented by directory invoking + * pseudo plugin methods. + * + */ + +#include "../../inode.h" +#include "../../debug.h" +#include "../plugin.h" + +#include "pseudo.h" + +static int init_pseudo(struct inode *parent, struct inode *pseudo, + pseudo_plugin *pplug, const char *name); + +static struct inode *add_pseudo(struct inode *parent, + pseudo_plugin *pplug, struct dentry **d); + +/* + * helper method: set ->datum field in the pseudo file specific portion of + * reiser4 inode. + */ +static void pseudo_set_datum(struct inode *pseudo, unsigned long datum) +{ + reiser4_inode_data(pseudo)->file_plugin_data.pseudo_info.datum = datum; +} + +/* + * return id of pseudo file plugin for this inode @p + */ +static int pseudo_id(struct inode *p) +{ + return reiser4_inode_data(p)->file_plugin_data.pseudo_info.plugin->h.id; +} + +/* + * helper method used to implement ->lookup() method of pseudo files. + * + * Try to find a pseudo plugin that matches given name (stored in @dentry) and + * has ->parent field equal to @id. + * + * Convention is that ->parent field is set to the id of the pseudo plugin of + * the parent pseudo file in the hierarchy (that is, plugin for + * "a/metas/foo/bar" has ->parent set to the plugin id of "a/metas/foo"), with + * the exception of "a/metas" that uses special reserved value TOP_LEVEL for + * ->parent. + */ +static int +lookup_of_plugin(struct inode *parent, int id, struct dentry **dentry) +{ + const char *name; + struct inode *pseudo; + reiser4_plugin *plugin; + int result; + + name = (*dentry)->d_name.name; + pseudo = ERR_PTR(-ENOENT); + + /* scan all pseudo file plugins and check each */ + for_all_plugins(REISER4_PSEUDO_PLUGIN_TYPE, plugin) { + pseudo_plugin *pplug; + + pplug = &plugin->pseudo; + if (pplug->parent == id && + pplug->try != NULL && pplug->try(pplug, parent, name)) { + pseudo = add_pseudo(parent, pplug, dentry); + break; + } + } + if (!IS_ERR(pseudo)) + result = 0; + else + result = PTR_ERR(pseudo); + return result; +} + +/* + * implement ->lookup() method using convention described in the comment for + * lookup_of_plugin() function. + */ +static int lookup_table(struct inode *parent, struct dentry ** dentry) +{ + assert("nikita-3511", parent != NULL); + assert("nikita-3512", dentry != NULL); + assert("nikita-3513", + inode_file_plugin(parent)->h.id == PSEUDO_FILE_PLUGIN_ID); + + /* + * call lookup_of_plugin() passing id of pseudo plugin for @parent as + * "id" parameter. + */ + return lookup_of_plugin(parent, pseudo_id(parent), dentry); +} + +/* + * helper to implement ->readdir() method for the pseudo files. It uses the + * same convention as lookup_of_plugin() function. + */ +static int +readdir_table(struct file *f, void *dirent, filldir_t filld) +{ + loff_t off; + ino_t ino; + int skip; + int id; + + struct inode *inode; + reiser4_plugin *plugin; + + off = f->f_pos; + if (off < 0) + return 0; + + inode = f->f_dentry->d_inode; + switch ((int)off) { + /* + * first, return dot and dotdot + */ + case 0: + ino = inode->i_ino; + if (filld(dirent, ".", 1, off, ino, DT_DIR) < 0) + break; + ++ off; + /* fallthrough */ + case 1: + ino = parent_ino(f->f_dentry); + if (filld(dirent, "..", 2, off, ino, DT_DIR) < 0) + break; + ++ off; + /* fallthrough */ + default: + skip = off - 2; + id = pseudo_id(inode); + /* then, scan all pseudo plugins, looking for the ones with + * matching ->parent */ + for_all_plugins(REISER4_PSEUDO_PLUGIN_TYPE, plugin) { + pseudo_plugin *pplug; + const char *name; + + pplug = &plugin->pseudo; + if (pplug->parent == id && pplug->readdirable) { + if (skip == 0) { + name = pplug->h.label; + /* + * if match is found---fed @filld with + * it + */ + if (filld(dirent, name, strlen(name), + off, + off + (long)f, DT_REG) < 0) + break; + ++ off; + } else + -- skip; + } + } + } + f->f_pos = off; + return 0; +} + +/* + * special value of ->parent field in pseudo file plugin used by "metas" top + * level pseudo directory. + */ +#define TOP_LEVEL (-1) + +/* + * try to look up built-in pseudo file by its name. + */ +reiser4_internal int +lookup_pseudo_file(struct inode *parent, struct dentry **dentry) +{ + assert("nikita-2999", parent != NULL); + assert("nikita-3000", dentry != NULL); + + /* if pseudo files are disabled for this file system bail out */ + if (reiser4_is_set(parent->i_sb, REISER4_NO_PSEUDO)) + return RETERR(-ENOENT); + else + return lookup_of_plugin(parent, TOP_LEVEL, dentry); +} + +/* create inode for pseudo file with plugin @pplug, and add it to the @parent + * under name @d */ +static struct inode *add_pseudo(struct inode *parent, + pseudo_plugin *pplug, struct dentry **d) +{ + struct inode *pseudo; + + pseudo = new_inode(parent->i_sb); + if (pseudo != NULL) { + int result; + + result = init_pseudo(parent, pseudo, pplug, (*d)->d_name.name); + if (result != 0) + pseudo = ERR_PTR(result); + else + *d = d_splice_alias(pseudo, *d); + } else + pseudo = ERR_PTR(RETERR(-ENOMEM)); + return pseudo; +} + + +/* helper function: return host object of @inode pseudo file */ +static struct inode *get_inode_host(struct inode *inode) +{ + assert("nikita-3510", + inode_file_plugin(inode)->h.id == PSEUDO_FILE_PLUGIN_ID); + return reiser4_inode_data(inode)->file_plugin_data.pseudo_info.host; +} + +/* helper function: return parent object of @inode pseudo file */ +static struct inode *get_inode_parent(struct inode *inode) +{ + assert("nikita-3510", + inode_file_plugin(inode)->h.id == PSEUDO_FILE_PLUGIN_ID); + return reiser4_inode_data(inode)->file_plugin_data.pseudo_info.parent; +} + +/* + * initialize pseudo file @pseudo to be child of @parent, with plugin @pplug + * and name @name. + */ +static int +init_pseudo(struct inode *parent, struct inode *pseudo, + pseudo_plugin *pplug, const char *name) +{ + int result; + struct inode *host; + reiser4_inode *idata; + reiser4_object_create_data data; + static const oid_t pseudo_locality = 0x0ull; + + idata = reiser4_inode_data(pseudo); + /* all pseudo files live in special reserved locality */ + idata->locality_id = pseudo_locality; + + /* + * setup ->parent and ->host fields + */ + if (pplug->parent != TOP_LEVEL) + /* host of "a/metas/b/c" is "a" */ + host = get_inode_host(parent); + else + /* host of "a/metas" is "a" */ + host = parent; + + idata->file_plugin_data.pseudo_info.host = host; + idata->file_plugin_data.pseudo_info.parent = parent; + idata->file_plugin_data.pseudo_info.plugin = pplug; + + data.id = PSEUDO_FILE_PLUGIN_ID; + data.mode = pplug->lookup_mode; + + plugin_set_file(&idata->pset, file_plugin_by_id(PSEUDO_FILE_PLUGIN_ID)); + /* if plugin has a ->lookup method, it means that @pseudo should + * behave like directory. */ + if (pplug->lookup != NULL) + plugin_set_dir(&idata->pset, + dir_plugin_by_id(PSEUDO_DIR_PLUGIN_ID)); + + /* perform standard plugin initialization */ + result = inode_file_plugin(pseudo)->set_plug_in_inode(pseudo, + parent, &data); + if (result != 0) { + warning("nikita-3203", "Cannot install pseudo plugin"); + print_plugin("plugin", pseudo_plugin_to_plugin(pplug)); + return result; + } + + /* inherit permission plugin from parent, */ + grab_plugin(pseudo, parent, PSET_PERM); + /* and credentials... */ + pseudo->i_uid = parent->i_uid; + pseudo->i_gid = parent->i_gid; + + pseudo->i_nlink = 1; + /* insert inode into VFS hash table */ + insert_inode_hash(pseudo); + return 0; +} + +/* helper function: return host object by file descriptor */ +static struct inode *get_pseudo_host(struct file *file) +{ + struct inode *inode; + + inode = file->f_dentry->d_inode; + return get_inode_host(inode); +} + +/* helper function: return host object by seq_file */ +static struct inode *get_seq_pseudo_host(struct seq_file *seq) +{ + struct file *file; + + file = seq->private; + return get_pseudo_host(file); +} + +/* + * implementation of ->try method for pseudo files with fixed names. + */ +static int try_by_label(pseudo_plugin *pplug, + const struct inode *parent, const char *name) +{ + return !strcmp(name, pplug->h.label); +} + +/* + * read method for the "metas/uid" pseudo file. + */ +static int show_uid(struct seq_file *seq, void *cookie) +{ + seq_printf(seq, "%lu", (long unsigned)get_seq_pseudo_host(seq)->i_uid); + return 0; +} + +/* helper: check permissions required to modify metas/[ug]id */ +static int check_perm(struct inode *inode) +{ + if (IS_RDONLY(inode)) + return RETERR(-EROFS); + if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) + return RETERR(-EPERM); + return 0; +} + +/* + * helper function to update [ug]id of @inode. Called by "metas/[ug]id" write + * methods + */ +static int update_ugid(struct dentry *dentry, struct inode *inode, + uid_t uid, gid_t gid) +{ + int result; + + /* logic COPIED from fs/open.c:chown_common() */ + result = check_perm(inode); + if (result == 0) { + struct iattr newattrs; + + newattrs.ia_valid = ATTR_CTIME; + if (uid != (uid_t) -1) { + newattrs.ia_valid |= ATTR_UID; + newattrs.ia_uid = uid; + } + if (gid != (uid_t) -1) { + newattrs.ia_valid |= ATTR_GID; + newattrs.ia_gid = gid; + } + if (!S_ISDIR(inode->i_mode)) + newattrs.ia_valid |= ATTR_KILL_SUID|ATTR_KILL_SGID; + down(&inode->i_sem); + result = notify_change(dentry, &newattrs); + up(&inode->i_sem); + } + return result; +} + +/* + * write method for the "metas/uid": extract uid from user-supplied buffer, + * and update uid + */ +static int store_uid(struct file *file, const char *buf) +{ + uid_t uid; + int result; + + if (sscanf(buf, "%i", &uid) == 1) { + struct inode *host; + + host = get_pseudo_host(file); + result = update_ugid(file->f_dentry->d_parent->d_parent, + host, uid, -1); + } else + result = RETERR(-EINVAL); + return result; +} + +/* + * read method for the "metas/uid" pseudo file. + */ +static int show_gid(struct seq_file *seq, void *cookie) +{ + seq_printf(seq, "%lu", (long unsigned)get_seq_pseudo_host(seq)->i_gid); + return 0; +} + +/* + * write method for the "metas/gid": extract uid from user-supplied buffer, + * and update gid + */ +static int get_gid(struct file *file, const char *buf) +{ + gid_t gid; + int result; + + if (sscanf(buf, "%i", &gid) == 1) { + struct inode *host; + + host = get_pseudo_host(file); + result = update_ugid(file->f_dentry->d_parent->d_parent, + host, -1, gid); + } else + result = RETERR(-EINVAL); + return result; +} + +/* + * read method for the "metas/oid" pseudo file + */ +static int show_oid(struct seq_file *seq, void *cookie) +{ + seq_printf(seq, "%llu", get_inode_oid(get_seq_pseudo_host(seq))); + return 0; +} + +/* + * read method for the "metas/key" pseudo file + */ +static int show_key(struct seq_file *seq, void *cookie) +{ + char buf[KEY_BUF_LEN]; + reiser4_key key; + + sprintf_key(buf, build_sd_key(get_seq_pseudo_host(seq), &key)); + seq_printf(seq, "%s", buf); + return 0; +} + +/* + * read method for the "metas/size" pseudo file + */ +static int show_size(struct seq_file *seq, void *cookie) +{ + seq_printf(seq, "%lli", get_seq_pseudo_host(seq)->i_size); + return 0; +} + +/* + * read method for the "metas/nlink" pseudo file + */ +static int show_nlink(struct seq_file *seq, void *cookie) +{ + seq_printf(seq, "%u", get_seq_pseudo_host(seq)->i_nlink); + return 0; +} + +/* + * read method for the "metas/locality" pseudo file + */ +static int show_locality(struct seq_file *seq, void *cookie) +{ + seq_printf(seq, "%llu", + reiser4_inode_data(get_seq_pseudo_host(seq))->locality_id); + return 0; +} + +/* + * read method for the "metas/rwx" pseudo file + */ +static int show_rwx(struct seq_file *seq, void *cookie) +{ + umode_t m; + + m = get_seq_pseudo_host(seq)->i_mode; + seq_printf(seq, "%#ho %c%c%c%c%c%c%c%c%c%c", + m, + + S_ISREG(m) ? '-' : + S_ISDIR(m) ? 'd' : + S_ISCHR(m) ? 'c' : + S_ISBLK(m) ? 'b' : + S_ISFIFO(m) ? 'p' : + S_ISLNK(m) ? 'l' : + S_ISSOCK(m) ? 's' : '?', + + m & S_IRUSR ? 'r' : '-', + m & S_IWUSR ? 'w' : '-', + m & S_IXUSR ? 'x' : '-', + + m & S_IRGRP ? 'r' : '-', + m & S_IWGRP ? 'w' : '-', + m & S_IXGRP ? 'x' : '-', + + m & S_IROTH ? 'r' : '-', + m & S_IWOTH ? 'w' : '-', + m & S_IXOTH ? 'x' : '-'); + return 0; +} + +/* + * write method for the "metas/rwx" file. Extract permission bits from the + * user supplied buffer and update ->i_mode. + */ +static int get_rwx(struct file *file, const char *buf) +{ + umode_t rwx; + int result; + + if (sscanf(buf, "%hi", &rwx) == 1) { + struct inode *host; + + host = get_pseudo_host(file); + result = check_perm(host); + if (result == 0) { + struct iattr newattrs; + + down(&host->i_sem); + if (rwx == (umode_t)~0) + rwx = host->i_mode; + newattrs.ia_mode = + (rwx & S_IALLUGO) | (host->i_mode & ~S_IALLUGO); + newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; + result = notify_change(file->f_dentry->d_parent->d_parent, + &newattrs); + up(&host->i_sem); + } + } else + result = RETERR(-EINVAL); + return result; +} + +/* + * seq-methods for "metas/pseudo" + */ + +/* + * start iteration over all pseudo files + */ +static void * pseudos_start(struct seq_file *m, loff_t *pos) +{ + if (*pos >= LAST_PSEUDO_ID) + return NULL; + return pseudo_plugin_by_id(*pos); +} + +/* + * stop iteration over all pseudo files + */ +static void pseudos_stop(struct seq_file *m, void *v) +{ +} + +/* + * go to next pseudo file in the sequence + */ +static void * pseudos_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++ (*pos); + return pseudos_start(m, pos); +} + +/* + * output information about particular pseudo file. + */ +static int pseudos_show(struct seq_file *m, void *v) +{ + pseudo_plugin *pplug; + + pplug = v; + if (pplug->try != NULL) + seq_printf(m, "%s\n", pplug->h.label); + return 0; +} + +/* + * seq-methods for "metas/bmap" + */ + +/* + * start iteration over all blocks allocated for the host file + */ +static void * bmap_start(struct seq_file *m, loff_t *pos) +{ + struct inode *host; + + host = get_seq_pseudo_host(m); + if (*pos << host->i_blkbits >= host->i_size) + return NULL; + else + return (void *)((unsigned long)*pos + 1); +} + +/* + * stop iteration over all blocks allocated for the host file + */ +static void bmap_stop(struct seq_file *m, void *v) +{ +} + +/* + * go to the next block in the sequence of blocks allocated for the host + * file. + */ +static void * bmap_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++ (*pos); + return bmap_start(m, pos); +} + +extern int reiser4_lblock_to_blocknr(struct address_space *mapping, + sector_t lblock, reiser4_block_nr *blocknr); + +/* + * output information about single block number allocated for the host file + * into user supplied buffer + */ +static int bmap_show(struct seq_file *m, void *v) +{ + sector_t lblock; + int result; + reiser4_block_nr blocknr; + + lblock = ((sector_t)(unsigned long)v) - 1; + result = reiser4_lblock_to_blocknr(get_seq_pseudo_host(m)->i_mapping, + lblock, &blocknr); + if (result == 0) { + if (blocknr_is_fake(&blocknr)) + seq_printf(m, "%#llx\n", blocknr); + else + seq_printf(m, "%llu\n", blocknr); + } + return result; +} + +/* + * seq-methods for the "metas/readdir" + */ + +/* "cursor" used to iterate over all directory entries for the host file */ +typedef struct readdir_cookie { + /* position within the tree */ + tap_t tap; + /* coord used by ->tap */ + coord_t coord; + /* lock handle used by ->tap */ + lock_handle lh; +} readdir_cookie; + +/* true if @coord stores directory entries for @host */ +static int is_host_item(struct inode *host, coord_t *coord) +{ + if (item_type_by_coord(coord) != DIR_ENTRY_ITEM_TYPE) + return 0; + if (!inode_file_plugin(host)->owns_item(host, coord)) + return 0; + return 1; +} + +/* helper function to release resources allocated to iterate over directory + * entries for the host file */ +static void finish(readdir_cookie *c) +{ + if (c != NULL && !IS_ERR(c)) { + /* release c->tap->lh long term lock... */ + tap_done(&c->tap); + /* ... and free cursor itself */ + kfree(c); + } +} + +/* + * start iterating over directory entries for the host file + */ +static void * readdir_start(struct seq_file *m, loff_t *pos) +{ + struct inode *host; + readdir_cookie *c; + dir_plugin *dplug; + reiser4_key dotkey; + struct qstr dotname; + int result; + loff_t entryno; + + /* + * first, lookup item containing dot of the host + */ + + host = get_seq_pseudo_host(m); + dplug = inode_dir_plugin(host); + + dotname.name = "."; + dotname.len = 1; + + down(&host->i_sem); + if (dplug == NULL || dplug->build_entry_key == NULL) { + finish(NULL); + return NULL; + } + + /* build key of dot */ + dplug->build_entry_key(host, &dotname, &dotkey); + + /* allocate cursor */ + c = kmalloc(sizeof *c, GFP_KERNEL); + if (c == NULL) { + finish(NULL); + return ERR_PTR(RETERR(-ENOMEM)); + } + + /* tree lookup */ + result = object_lookup(host, + &dotkey, + &c->coord, + &c->lh, + ZNODE_READ_LOCK, + FIND_EXACT, + LEAF_LEVEL, + LEAF_LEVEL, + CBK_READDIR_RA, + NULL); + + tap_init(&c->tap, &c->coord, &c->lh, ZNODE_READ_LOCK); + if (result == 0) + /* + * ok, now c->tap is positioned at the dot. We are requested + * to start readdir from the offset *pos. Skip that number of + * entries. That's not very efficient for the large + * directories. + */ + result = tap_load(&c->tap); { + if (result == 0) { + for (entryno = 0; entryno != *pos; ++ entryno) { + result = go_next_unit(&c->tap); + if (result == -E_NO_NEIGHBOR) { + finish(c); + return NULL; + } + if (result != 0) + break; + if (!is_host_item(host, c->tap.coord)) { + finish(c); + return NULL; + } + } + } + } + if (result != 0) { + finish(c); + return ERR_PTR(result); + } else + return c; +} + +/* + * stop iterating over directory entries for the host file + */ +static void readdir_stop(struct seq_file *m, void *v) +{ + up(&get_seq_pseudo_host(m)->i_sem); + finish(v); +} + +/* + * go to the next entry in the host directory + */ +static void * readdir_next(struct seq_file *m, void *v, loff_t *pos) +{ + readdir_cookie *c; + struct inode *host; + int result; + + c = v; + ++ (*pos); + host = get_seq_pseudo_host(m); + /* next entry is in the next unit */ + result = go_next_unit(&c->tap); + if (result == 0) { + /* check whether end of the directory was reached. */ + if (!is_host_item(host, c->tap.coord)) { + finish(c); + return NULL; + } else + return v; + } else { + finish(c); + return ERR_PTR(result); + } +} + +/* + * output information about single directory entry in the host directory + */ +static int readdir_show(struct seq_file *m, void *v) +{ + readdir_cookie *c; + item_plugin *iplug; + char *name; + char buf[DE_NAME_BUF_LEN]; + + c = v; + iplug = item_plugin_by_coord(&c->coord); + + name = iplug->s.dir.extract_name(&c->coord, buf); + assert("nikita-3221", name != NULL); + /* entries are separated by the "/" in the user buffer, because this + * is the only symbol (besides NUL) that is not allowed in file + * names. */ + seq_printf(m, "%s/", name); + return 0; +} + +/* + * methods for "metas/plugin" + */ + +/* + * entry in the table mapping plugin pseudo file name to the corresponding + * pset member. + */ +typedef struct plugin_entry { + const char *name; + pset_member memb; +} plugin_entry; + +/* initializer for plugin_entry */ +#define PLUGIN_ENTRY(field, ind) \ +{ \ + .name = #field, \ + .memb = ind \ +} + +#define PSEUDO_ARRAY_ENTRY(idx, aname) \ +[idx] = { \ + .name = aname \ +} + +/* + * initialize array defining files available under "metas/plugin". + */ +static plugin_entry pentry[] = { + /* "a/metas/plugin/file" corresponds to the PSET_FILE plugin of its + * host file (that is, "a"), etc. */ + PLUGIN_ENTRY(file, PSET_FILE), + PLUGIN_ENTRY(dir, PSET_DIR), + PLUGIN_ENTRY(perm, PSET_PERM), + PLUGIN_ENTRY(formatting, PSET_FORMATTING), + PLUGIN_ENTRY(hash, PSET_HASH), + PLUGIN_ENTRY(fibration, PSET_FIBRATION), + PLUGIN_ENTRY(sd, PSET_SD), + PLUGIN_ENTRY(dir_item, PSET_DIR_ITEM), + PLUGIN_ENTRY(crypto, PSET_CRYPTO), + PLUGIN_ENTRY(digest, PSET_DIGEST), + PLUGIN_ENTRY(compression, PSET_COMPRESSION), + { + .name = NULL, + } +}; + +/* + * enumeration of files available under "a/metas/plugin/foo" + */ +typedef enum { + PFIELD_TYPEID, /* "a/metas/plugin/foo/type_id" contains type id of the + * plugin foo */ + PFIELD_ID, /* "a/metas/plugin/foo/id" contains id of the plugin + * foo */ + PFIELD_LABEL, /* "a/metas/plugin/foo/label" contains label of the + * plugin foo */ + PFIELD_DESC /* "a/metas/plugin/foo/desc" contains description of + * the plugin foo */ +} plugin_field; + +/* map pseudo files under "a/metas/plugin/foo" to their names */ +static plugin_entry fentry[] = { + PSEUDO_ARRAY_ENTRY(PFIELD_TYPEID, "type_id"), + PSEUDO_ARRAY_ENTRY(PFIELD_ID, "id"), + PSEUDO_ARRAY_ENTRY(PFIELD_LABEL, "label"), + PSEUDO_ARRAY_ENTRY(PFIELD_DESC, "desc"), + { + .name = NULL + }, +}; + +/* read method for "a/metas/plugin/foo" */ +static int show_plugin(struct seq_file *seq, void *cookie) +{ + struct inode *host; + struct file *file; + struct inode *inode; + reiser4_plugin *plug; + plugin_entry *entry; + int idx; + plugin_set *pset; + + file = seq->private; + inode = file->f_dentry->d_inode; + + host = get_inode_host(inode); + idx = reiser4_inode_data(inode)->file_plugin_data.pseudo_info.datum; + entry = &pentry[idx]; + pset = reiser4_inode_data(host)->pset; + plug = pset_get(pset, entry->memb); + + if (plug != NULL) + seq_printf(seq, "%i %s %s", + plug->h.id, plug->h.label, plug->h.desc); + return 0; +} + +/* + * write method for "a/metas/plugin/foo": extract plugin label from the user + * supplied buffer @buf and update plugin foo, if possible. + */ +static int set_plugin(struct file *file, const char *buf) +{ + struct inode *host; + struct inode *inode; + reiser4_plugin *plug; + plugin_entry *entry; + int idx; + plugin_set *pset; + int result; + reiser4_context ctx; + + inode = file->f_dentry->d_inode; + init_context(&ctx, inode->i_sb); + + host = get_inode_host(inode); + idx = reiser4_inode_data(inode)->file_plugin_data.pseudo_info.datum; + entry = &pentry[idx]; + pset = reiser4_inode_data(host)->pset; + + plug = lookup_plugin(entry->name, buf); + if (plug != NULL) { + result = force_plugin(host, entry->memb, plug); + if (result == 0) { + __u64 tograb; + + /* + * if plugin was updated successfully, save changes in + * the stat-data + */ + tograb = inode_file_plugin(host)->estimate.update(host); + result = reiser4_grab_space(tograb, BA_CAN_COMMIT); + if (result == 0) + result = reiser4_mark_inode_dirty(host); + } + } else + result = RETERR(-ENOENT); + context_set_commit_async(&ctx); + reiser4_exit_context(&ctx); + return result; +} + +/* + * helper function to implement ->lookup() method of pseudo directory plugin + * for the file that contains multiple similar children pseudo files. + * + * For example, "a/metas/plugin/" directory contains files for each plugin + * associated with the host file "a". Handling of read/write for these file is + * exactly the same, the only difference being the pset member id for the + * corresponding plugin. Similarly, "a/metas/plugin/foo/" itself contains + * files that are used to provide user access to the corresponding fields of + * the "foo" plugin, and all such fields can be handled similarly (see + * show_plugin_field()) + * + * To avoid code duplication in such situation, an array is constructed that + * is used as a map from the name of "child" object to the corresponding + * "datum". All child objects are handled by the same pseudo plugin, and are + * differentiated by the datum installed into pseudo file inode. + */ +static int array_lookup_pseudo(struct inode *parent, struct dentry ** dentry, + plugin_entry *array, pseudo_plugin *pplug) +{ + int result; + int idx; + struct inode *pseudo; + + pseudo = ERR_PTR(-ENOENT); + /* search for the given name in the array */ + for (idx = 0; array[idx].name != NULL; ++ idx) { + if (!strcmp((*dentry)->d_name.name, array[idx].name)) { + pseudo = add_pseudo(parent, pplug, dentry); + break; + } + } + if (IS_ERR(pseudo)) + result = PTR_ERR(pseudo); + else { + result = 0; + /* if name was found, set datum in the inode */ + pseudo_set_datum(pseudo, idx); + } + return result; +} + +/* + * helper method to implement array for the situation when we have multiple + * child pseudo files with similar functionality. See comment for + * array_lookup_pseudo(). + */ +static int array_readdir_pseudo(struct file *f, void *dirent, filldir_t filld, + plugin_entry *array, int size) +{ + loff_t off; + ino_t ino; + + off = f->f_pos; + if (off < 0) + return 0; + + /* for god's sake, why switch(loff_t) requires __cmpdi2? */ + switch ((int)off) { + case 0: + ino = f->f_dentry->d_inode->i_ino; + if (filld(dirent, ".", 1, off, ino, DT_DIR) < 0) + break; + ++ off; + /* fallthrough */ + case 1: + ino = parent_ino(f->f_dentry); + if (filld(dirent, "..", 2, off, ino, DT_DIR) < 0) + break; + ++ off; + /* fallthrough */ + default: + /* scan array for the names */ + for (; off < size + 1; ++ off) { + const char *name; + + name = array[off - 2].name; + if (filld(dirent, name, strlen(name), + off, off + (long)f, DT_REG) < 0) + break; + } + } + f->f_pos = off; + return 0; +} + + +/* + * ->lookup() method for the "a/metas/plugin/foo/" directory. It uses array + * representation of child objects, described in the comment for + * array_lookup_pseudo(). + */ +static int lookup_plugin_field(struct inode *parent, struct dentry ** dentry) +{ + return array_lookup_pseudo(parent, dentry, fentry, + pseudo_plugin_by_id(PSEUDO_PLUGIN_FIELD_ID)); +} + +/* + * read method for "a/metas/plugin/foo/field" + */ +static int show_plugin_field(struct seq_file *seq, void *cookie) +{ + struct inode *parent; + struct inode *host; + struct file *file; + struct inode *inode; + reiser4_plugin *plug; + plugin_entry *entry; + int pidx; + int idx; + plugin_set *pset; + + file = seq->private; + inode = file->f_dentry->d_inode; + + parent = get_inode_parent(inode); + host = get_inode_host(inode); + pidx = reiser4_inode_data(parent)->file_plugin_data.pseudo_info.datum; + idx = reiser4_inode_data(inode)->file_plugin_data.pseudo_info.datum; + entry = &pentry[pidx]; + pset = reiser4_inode_data(host)->pset; + plug = pset_get(pset, entry->memb); + + if (plug != NULL) { + switch (idx) { + case PFIELD_TYPEID: + seq_printf(seq, "%i", plug->h.type_id); + break; + case PFIELD_ID: + seq_printf(seq, "%i", plug->h.id); + break; + case PFIELD_LABEL: + seq_printf(seq, "%s", plug->h.label); + break; + case PFIELD_DESC: + seq_printf(seq, "%s", plug->h.desc); + break; + } + } + + return 0; +} + +/* + * ->readdir() method for "a/metas/plugin/foo/". It uses array representation of + * child objects, described in the comment for array_lookup_pseudo(). + */ +static int readdir_plugin_field(struct file *f, void *dirent, filldir_t filld) +{ + return array_readdir_pseudo(f, dirent, filld, + fentry, sizeof_array(fentry)); +} + +/* + * ->lookup() method for the "a/metas/plugin/" directory. It uses array + * representation of child objects, described in the comment for + * array_lookup_pseudo(). + */ +static int lookup_plugins(struct inode *parent, struct dentry ** dentry) +{ + return array_lookup_pseudo(parent, dentry, pentry, + pseudo_plugin_by_id(PSEUDO_PLUGIN_ID)); +} + +/* + * ->readdir() method for "a/metas/plugin/". It uses array representation of + * child objects, described in the comment for array_lookup_pseudo(). + */ +static int readdir_plugins(struct file *f, void *dirent, filldir_t filld) +{ + return array_readdir_pseudo(f, dirent, filld, + pentry, sizeof_array(pentry)); +} + +/* + * seq-methods for the "a/metas/items" + */ + +/* + * start iteration over a sequence of items for the host file. This iterator + * uses the same cursor as a readdir iterator above. + */ +static void * items_start(struct seq_file *m, loff_t *pos) +{ + struct inode *host; + readdir_cookie *c; + file_plugin *fplug; + reiser4_key headkey; + int result; + loff_t entryno; + + /* + * first, find first item in the file, then, scan to the *pos-th one. + */ + + host = get_seq_pseudo_host(m); + fplug = inode_file_plugin(host); + + down(&host->i_sem); + if (fplug->key_by_inode == NULL) { + finish(NULL); + return NULL; + } + + /* construct a key of the first item */ + fplug->key_by_inode(host, 0, &headkey); + + c = kmalloc(sizeof *c, GFP_KERNEL); + if (c == NULL) { + finish(NULL); + return ERR_PTR(RETERR(-ENOMEM)); + } + + /* find first item */ + result = object_lookup(host, + &headkey, + &c->coord, + &c->lh, + ZNODE_READ_LOCK, + FIND_MAX_NOT_MORE_THAN, + TWIG_LEVEL, + LEAF_LEVEL, + 0, + NULL); + + tap_init(&c->tap, &c->coord, &c->lh, ZNODE_READ_LOCK); + if (result == 0) + result = tap_load(&c->tap); { + if (result == 0) { + /* + * skip @pos items + */ + for (entryno = 0; entryno != *pos; ++ entryno) { + result = go_next_unit(&c->tap); + if (result == -E_NO_NEIGHBOR) { + finish(c); + return NULL; + } + if (result != 0) + break; + if (!fplug->owns_item(host, c->tap.coord)) { + finish(c); + return NULL; + } + } + } + } + if (result != 0) { + finish(c); + return ERR_PTR(result); + } else + return c; +} + +/* + * stop iteration over a sequence of items for the host file + */ +static void items_stop(struct seq_file *m, void *v) +{ + up(&get_seq_pseudo_host(m)->i_sem); + finish(v); +} + +/* go to the next item in the host file */ +static void * items_next(struct seq_file *m, void *v, loff_t *pos) +{ + readdir_cookie *c; + struct inode *host; + int result; + + c = v; + ++ (*pos); + host = get_seq_pseudo_host(m); + result = go_next_unit(&c->tap); + if (result == 0) { + if (!inode_file_plugin(host)->owns_item(host, c->tap.coord)) { + finish(c); + return NULL; + } else + return v; + } else { + finish(c); + return ERR_PTR(result); + } +} + +/* output information about single item of the host file */ +static int items_show(struct seq_file *m, void *v) +{ + readdir_cookie *c; + item_plugin *iplug; + char buf[KEY_BUF_LEN]; + reiser4_key key; + + + c = v; + iplug = item_plugin_by_coord(&c->coord); + + /* output key... */ + sprintf_key(buf, unit_key_by_coord(&c->coord, &key)); + /* ... and item plugin label... */ + seq_printf(m, "%s %s ", buf, iplug->h.label); + if (iplug->b.show != NULL) + /* ... and call ->b.show() method of item plugin, if any, to + * do the rest */ + iplug->b.show(m, &c->coord); + seq_printf(m, "\n"); + return 0; +} + +extern int +invoke_create_method(struct inode *, struct dentry *, + reiser4_object_create_data *); + +/* + * write method for the "a/metas/new" file. Extract file name from the user + * supplied buffer @buf, and create regular file with that name within host + * file (that is better to be a directory). + */ +static int get_new(struct file *file, const char *buf) +{ + int result; + + /* check that @buf contains no slashes */ + if (strchr(buf, '/') == NULL) { + struct dentry *d; + struct qstr name; + unsigned int c; + unsigned long hash; + + reiser4_object_create_data data; + xmemset(&data, 0, sizeof data); + + data.mode = S_IFREG | 0 /* mode */; + data.id = UNIX_FILE_PLUGIN_ID; + + name.name = buf; + c = *(const unsigned char *)buf; + + /* build hash of the name */ + hash = init_name_hash(); + do { + buf++; + hash = partial_name_hash(c, hash); + c = *(const unsigned char *)buf; + } while (c); + name.len = buf - (const char *) name.name; + name.hash = end_name_hash(hash); + + /* allocate dentry */ + d = d_alloc(file->f_dentry->d_parent->d_parent, &name); + if (d == NULL) + result = RETERR(-ENOMEM); + else { + /* call ->create() method of the host directory */ + result = invoke_create_method(get_pseudo_host(file), + d, &data); + reiser4_free_dentry_fsdata(d); + } + } else + result = RETERR(-EINVAL); + return result; +} + +/* + * initialize pseudo plugins. + */ +pseudo_plugin pseudo_plugins[LAST_PSEUDO_ID] = { + [PSEUDO_METAS_ID] = { + .h = { + .type_id = REISER4_PSEUDO_PLUGIN_TYPE, + .id = PSEUDO_METAS_ID, + .pops = NULL, + .label = "metas", + .desc = "meta-files", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .parent = TOP_LEVEL, + .try = try_by_label, + .readdirable = 0, + .lookup = lookup_table, + .lookup_mode = S_IFDIR | S_IRUGO | S_IXUGO, + .read_type = PSEUDO_READ_NONE, + .write_type = PSEUDO_WRITE_NONE, + .readdir = readdir_table + }, + [PSEUDO_UID_ID] = { + .h = { + .type_id = REISER4_PSEUDO_PLUGIN_TYPE, + .id = PSEUDO_UID_ID, + .pops = NULL, + .label = "uid", + .desc = "returns owner", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .parent = PSEUDO_METAS_ID, + .try = try_by_label, + .readdirable = 1, + .lookup = NULL, + .lookup_mode = S_IFREG | S_IRUGO | S_IWUSR, + .read_type = PSEUDO_READ_SINGLE, + .read = { + .single_show = show_uid + }, + .write_type = PSEUDO_WRITE_STRING, + .write = { + .gets = store_uid + } + }, + [PSEUDO_GID_ID] = { + .h = { + .type_id = REISER4_PSEUDO_PLUGIN_TYPE, + .id = PSEUDO_GID_ID, + .pops = NULL, + .label = "gid", + .desc = "returns group", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .parent = PSEUDO_METAS_ID, + .try = try_by_label, + .readdirable = 1, + .lookup = NULL, + .lookup_mode = S_IFREG | S_IRUGO | S_IWUSR, + .read_type = PSEUDO_READ_SINGLE, + .read = { + .single_show = show_gid + }, + .write_type = PSEUDO_WRITE_STRING, + .write = { + .gets = get_gid + } + }, + [PSEUDO_RWX_ID] = { + .h = { + .type_id = REISER4_PSEUDO_PLUGIN_TYPE, + .id = PSEUDO_RWX_ID, + .pops = NULL, + .label = "rwx", + .desc = "returns rwx permissions", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .parent = PSEUDO_METAS_ID, + .try = try_by_label, + .readdirable = 1, + .lookup = NULL, + .lookup_mode = S_IFREG | S_IRUGO | S_IWUSR, + .read_type = PSEUDO_READ_SINGLE, + .read = { + .single_show = show_rwx + }, + .write_type = PSEUDO_WRITE_STRING, + .write = { + .gets = get_rwx + } + }, + [PSEUDO_OID_ID] = { + .h = { + .type_id = REISER4_PSEUDO_PLUGIN_TYPE, + .id = PSEUDO_OID_ID, + .pops = NULL, + .label = "oid", + .desc = "returns object id", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .parent = PSEUDO_METAS_ID, + .try = try_by_label, + .readdirable = 1, + .lookup = NULL, + .lookup_mode = S_IFREG | S_IRUGO, + .read_type = PSEUDO_READ_SINGLE, + .read = { + .single_show = show_oid + }, + .write_type = PSEUDO_WRITE_NONE + }, + [PSEUDO_KEY_ID] = { + .h = { + .type_id = REISER4_PSEUDO_PLUGIN_TYPE, + .id = PSEUDO_KEY_ID, + .pops = NULL, + .label = "key", + .desc = "returns object's key", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .parent = PSEUDO_METAS_ID, + .try = try_by_label, + .readdirable = 1, + .lookup = NULL, + .lookup_mode = S_IFREG | S_IRUGO, + .read_type = PSEUDO_READ_SINGLE, + .read = { + .single_show = show_key + }, + .write_type = PSEUDO_WRITE_NONE + }, + [PSEUDO_SIZE_ID] = { + .h = { + .type_id = REISER4_PSEUDO_PLUGIN_TYPE, + .id = PSEUDO_SIZE_ID, + .pops = NULL, + .label = "size", + .desc = "returns object's size", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .parent = PSEUDO_METAS_ID, + .try = try_by_label, + .readdirable = 1, + .lookup = NULL, + .lookup_mode = S_IFREG | S_IRUGO, + .read_type = PSEUDO_READ_SINGLE, + .read = { + .single_show = show_size + }, + .write_type = PSEUDO_WRITE_NONE + }, + [PSEUDO_NLINK_ID] = { + .h = { + .type_id = REISER4_PSEUDO_PLUGIN_TYPE, + .id = PSEUDO_NLINK_ID, + .pops = NULL, + .label = "nlink", + .desc = "returns nlink count", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .parent = PSEUDO_METAS_ID, + .try = try_by_label, + .readdirable = 1, + .lookup = NULL, + .lookup_mode = S_IFREG | S_IRUGO, + .read_type = PSEUDO_READ_SINGLE, + .read = { + .single_show = show_nlink + }, + .write_type = PSEUDO_WRITE_NONE + }, + [PSEUDO_LOCALITY_ID] = { + .h = { + .type_id = REISER4_PSEUDO_PLUGIN_TYPE, + .id = PSEUDO_LOCALITY_ID, + .pops = NULL, + .label = "locality", + .desc = "returns object's locality", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .parent = PSEUDO_METAS_ID, + .try = try_by_label, + .readdirable = 1, + .lookup = NULL, + .lookup_mode = S_IFREG | S_IRUGO, + .read_type = PSEUDO_READ_SINGLE, + .read = { + .single_show = show_locality + }, + .write_type = PSEUDO_WRITE_NONE + }, + [PSEUDO_PSEUDOS_ID] = { + .h = { + .type_id = REISER4_PSEUDO_PLUGIN_TYPE, + .id = PSEUDO_PSEUDOS_ID, + .pops = NULL, + .label = "pseudo", + .desc = "returns a list of pseudo files", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .parent = PSEUDO_METAS_ID, + .try = try_by_label, + .readdirable = 1, + .lookup = NULL, + .lookup_mode = S_IFREG | S_IRUGO, + .read_type = PSEUDO_READ_SEQ, + .read = { + .ops = { + .start = pseudos_start, + .stop = pseudos_stop, + .next = pseudos_next, + .show = pseudos_show + } + }, + .write_type = PSEUDO_WRITE_NONE + }, + [PSEUDO_BMAP_ID] = { + .h = { + .type_id = REISER4_PSEUDO_PLUGIN_TYPE, + .id = PSEUDO_BMAP_ID, + .pops = NULL, + .label = "bmap", + .desc = "returns a list blocks for this file", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .parent = PSEUDO_METAS_ID, + .try = try_by_label, + .readdirable = 1, + .lookup = NULL, + .lookup_mode = S_IFREG | S_IRUGO, + .read_type = PSEUDO_READ_SEQ, + .read = { + .ops = { + .start = bmap_start, + .stop = bmap_stop, + .next = bmap_next, + .show = bmap_show + } + }, + .write_type = PSEUDO_WRITE_NONE + }, + [PSEUDO_READDIR_ID] = { + .h = { + .type_id = REISER4_PSEUDO_PLUGIN_TYPE, + .id = PSEUDO_READDIR_ID, + .pops = NULL, + .label = "readdir", + .desc = "returns a list of names in the dir", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .parent = PSEUDO_METAS_ID, + .try = try_by_label, + .readdirable = 1, + .lookup = NULL, + .lookup_mode = S_IFREG | S_IRUGO, + .read_type = PSEUDO_READ_SEQ, + .read = { + .ops = { + .start = readdir_start, + .stop = readdir_stop, + .next = readdir_next, + .show = readdir_show + } + }, + .write_type = PSEUDO_WRITE_NONE + }, + [PSEUDO_PLUGIN_ID] = { + .h = { + .type_id = REISER4_PSEUDO_PLUGIN_TYPE, + .id = PSEUDO_PLUGIN_ID, + .pops = NULL, + .label = "plugin", + .desc = "plugin", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .parent = PSEUDO_PLUGINS_ID, + .try = NULL, + .readdirable = 0, + .lookup = lookup_plugin_field, + /* + * foo/metas/plugin/bar is much like a directory. So, why + * there is no S_IFDIR term in the .lookup_mode, you ask? + * + * fs/namei.c:may_open(): + * + * if (S_ISDIR(inode->i_mode) && (flag & FMODE_WRITE)) + * return -EISDIR; + * + * Directory cannot be opened for write. How smart. + */ + .lookup_mode = S_IFREG | S_IRUGO | S_IWUSR | S_IXUGO, + .read_type = PSEUDO_READ_SINGLE, + .read = { + .single_show = show_plugin + }, + .write_type = PSEUDO_WRITE_STRING, + .write = { + .gets = set_plugin + }, + .readdir = readdir_plugin_field + }, + [PSEUDO_PLUGINS_ID] = { + .h = { + .type_id = REISER4_PSEUDO_PLUGIN_TYPE, + .id = PSEUDO_PLUGINS_ID, + .pops = NULL, + .label = "plugin", + .desc = "list of plugins", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .parent = PSEUDO_METAS_ID, + .try = try_by_label, + .readdirable = 1, + .lookup = lookup_plugins, + .lookup_mode = S_IFDIR | S_IRUGO | S_IXUGO, + .read_type = PSEUDO_READ_NONE, + .write_type = PSEUDO_WRITE_NONE, + .readdir = readdir_plugins + }, + [PSEUDO_PLUGIN_FIELD_ID] = { + .h = { + .type_id = REISER4_PSEUDO_PLUGIN_TYPE, + .id = PSEUDO_PLUGIN_ID, + .pops = NULL, + .label = "plugin-field", + .desc = "plugin field", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .parent = PSEUDO_PLUGIN_ID, + .try = NULL, + .readdirable = 0, + .lookup = NULL, + .lookup_mode = S_IFREG | S_IRUGO, + .read_type = PSEUDO_READ_SINGLE, + .read = { + .single_show = show_plugin_field + }, + .write_type = PSEUDO_WRITE_NONE, + .readdir = NULL + }, + [PSEUDO_ITEMS_ID] = { + .h = { + .type_id = REISER4_PSEUDO_PLUGIN_TYPE, + .id = PSEUDO_ITEMS_ID, + .pops = NULL, + .label = "items", + .desc = "returns a list of items for this file", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .parent = PSEUDO_METAS_ID, + .try = try_by_label, + .readdirable = 1, + .lookup = NULL, + .lookup_mode = S_IFREG | S_IRUGO, + .read_type = PSEUDO_READ_SEQ, + .read = { + .ops = { + .start = items_start, + .stop = items_stop, + .next = items_next, + .show = items_show + } + }, + .write_type = PSEUDO_WRITE_NONE + }, + [PSEUDO_NEW_ID] = { + .h = { + .type_id = REISER4_PSEUDO_PLUGIN_TYPE, + .id = PSEUDO_NEW_ID, + .pops = NULL, + .label = "new", + .desc = "creates new file in the host", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .parent = PSEUDO_METAS_ID, + .try = try_by_label, + .readdirable = 1, + .lookup = NULL, + .lookup_mode = S_IFREG | S_IWUSR, + .read_type = PSEUDO_READ_NONE, + .read = { + .single_show = show_rwx + }, + .write_type = PSEUDO_WRITE_STRING, + .write = { + .gets = get_new + } + }, +}; + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/pseudo/pseudo.h 2004-09-03 00:57:05.335228184 -0700 @@ -0,0 +1,176 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Handling of "pseudo" files representing unified access to meta data in + reiser4. See pseudo.c for more comments. */ + +#if !defined( __REISER4_PSEUDO_H__ ) +#define __REISER4_PSEUDO_H__ + +#include "../plugin_header.h" +#include "../../key.h" + +#include +#include + +/* + * tag used by wrappers in plugin/file/pseudo.c to perform actions for the + * particular flavor of pseudo file. + */ +typedef enum { + /* this pseudo file cannot be read */ + PSEUDO_READ_NONE, + /* this pseudo file used seq_* functions (fs/seq_file.c) to generate + * it's content */ + PSEUDO_READ_SEQ, + /* this pseudo file contains single value */ + PSEUDO_READ_SINGLE, + /* this pseudo file has some special ->read() method that should be + * called */ + PSEUDO_READ_FORWARD +} pseudo_read_type; + +typedef enum { + /* this pseudo file cannot be written into */ + PSEUDO_WRITE_NONE, + /* this pseudo file's content is generated by sprintf() */ + PSEUDO_WRITE_STRING, + /* this pseudo file has some special ->write() method that should be + * called */ + PSEUDO_WRITE_FORWARD +} pseudo_write_type; + +/* low level operations on the pseudo files. + + Methods from this interface are directly callable by reiser4 system call. + + This operation structure looks suspiciously like yet another plugin + type. Doing so would simplify some things. For example, there are already + functions to look up plugin by name, etc. + +*/ +struct pseudo_plugin; +typedef struct pseudo_plugin pseudo_plugin; +struct pseudo_plugin { + + /* common fields */ + plugin_header h; + + /* + * id of plugin of the parent pseudo file in the directory + * hierarchy. See comment for + * plugin/pseudo/pseudo.c:lookup_of_plugin(). + */ + int parent; + + /* + * check whether this pseudo file matches name @name within @parent + */ + int (*try) (pseudo_plugin *pplug, + const struct inode *parent, const char *name); + /* + * true if this pseudo file is visible in readdir. + */ + int readdirable; + /* lookup method applicable to this pseudo file by method name. + + This is for something like "foo/..acl/dup", here "../acl" is the + name of a pseudo file, and "dup" is name of an operation (method) + applicable to "../acl". Once "..acl" is resolved to ACL object, + ->lookup( "dup" ) can be called to get operation. + + */ + int (*lookup)(struct inode *parent, struct dentry ** dentry); + + /* + * rwx bits returned by stat(2) for this pseudo file + */ + umode_t lookup_mode; + + /* NOTE-NIKITA some other operations. Reiser4 syntax people should + add something here. */ + + /* + * how content of this pseudo file is generated + */ + pseudo_read_type read_type; + union { + /* for PSEUDO_READ_SEQ */ + struct seq_operations ops; + /* for PSEUDO_READ_SINGLE */ + int (*single_show) (struct seq_file *, void *); + /* for PSEUDO_READ_FORWARD */ + ssize_t (*read)(struct file *, char __user *, size_t , loff_t *); + } read; + + /* + * how this pseudo file reacts to write(2) calls + */ + pseudo_write_type write_type; + union { + /* for PSEUDO_WRITE_STRING */ + int (*gets)(struct file *, const char *); + /* for PSEUDO_WRITE_FORWARD */ + ssize_t (*write)(struct file *, + const char __user *, size_t , loff_t *); + } write; + /* + * ->readdir method + */ + int (*readdir)(struct file *f, void *dirent, filldir_t filld); +}; + +/* portion of reiser4_inode specific for pseudo files */ +typedef struct pseudo_info { + /* pseudo file plugin controlling this file */ + pseudo_plugin *plugin; + /* host object, for /etc/passwd/..oid, this is pointer to inode of + * /etc/passwd */ + struct inode *host; + /* immediate parent object. This is different from ->host for deeply + * nested pseudo files like foo/..plugin/foo */ + struct inode *parent; + /* for private use of pseudo file plugin */ + unsigned long datum; +} pseudo_info_t; + +extern int lookup_pseudo_file(struct inode *parent, struct dentry **dentry); + +/* + * ids of pseudo files. See plugin/pseudo/pseudo.c for more details on each + * particular pseudo file. + */ +typedef enum { + PSEUDO_METAS_ID, + PSEUDO_UID_ID, + PSEUDO_GID_ID, + PSEUDO_RWX_ID, + PSEUDO_OID_ID, + PSEUDO_KEY_ID, + PSEUDO_SIZE_ID, + PSEUDO_NLINK_ID, + PSEUDO_LOCALITY_ID, + PSEUDO_PSEUDOS_ID, + PSEUDO_BMAP_ID, + PSEUDO_READDIR_ID, + PSEUDO_PLUGIN_ID, + PSEUDO_PLUGINS_ID, + PSEUDO_PLUGIN_FIELD_ID, + PSEUDO_ITEMS_ID, + PSEUDO_NEW_ID, + LAST_PSEUDO_ID +} reiser4_pseudo_id; + +/* __REISER4_PSEUDO_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/security/perm.c 2004-09-03 00:57:05.335228184 -0700 @@ -0,0 +1,76 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ +/* NIKITA-FIXME-HANS: this comment describes what code? */ +/* definition of item plugins. */ + +#include "../plugin.h" +#include "../plugin_header.h" +#include "../../debug.h" + +#include +#include /* for struct dentry */ +#include + +static int +mask_ok_common(struct inode *inode, int mask) +{ + return vfs_permission(inode, mask); +} + +static int +setattr_ok_common(struct dentry *dentry, struct iattr *attr) +{ + int result; + struct inode *inode; + + assert("nikita-2272", dentry != NULL); + assert("nikita-2273", attr != NULL); + + inode = dentry->d_inode; + assert("nikita-2274", inode != NULL); + + result = inode_change_ok(inode, attr); + if (result == 0) { + unsigned int valid; + + valid = attr->ia_valid; + if ((valid & ATTR_UID && attr->ia_uid != inode->i_uid) || + (valid & ATTR_GID && attr->ia_gid != inode->i_gid)) + result = DQUOT_TRANSFER(inode, attr) ? -EDQUOT : 0; + } + return result; +} + +perm_plugin perm_plugins[LAST_PERM_ID] = { +/* NIKITA-FIXME-HANS: what file contains rwx permissions methods code? */ + [RWX_PERM_ID] = { + .h = { + .type_id = REISER4_PERM_PLUGIN_TYPE, + .id = RWX_PERM_ID, + .pops = NULL, + .label = "rwx", + .desc = "standard UNIX permissions", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .read_ok = NULL, + .write_ok = NULL, + .lookup_ok = NULL, + .create_ok = NULL, + .link_ok = NULL, + .unlink_ok = NULL, + .delete_ok = NULL, + .mask_ok = mask_ok_common, + .setattr_ok = setattr_ok_common, + .getattr_ok = NULL, + .rename_ok = NULL, + }, +}; + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/security/perm.h 2004-09-03 00:57:05.336228032 -0700 @@ -0,0 +1,88 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Perm (short for "permissions") plugins common stuff. */ + +#if !defined( __REISER4_PERM_H__ ) +#define __REISER4_PERM_H__ + +#include "../../forward.h" +#include "../plugin_header.h" + +#include +#include /* for struct file */ +#include /* for struct dentry */ + +/* interface for perm plugin. + + Perm plugin method can be implemented through: + + 1. consulting ->i_mode bits in stat data + + 2. obtaining acl from the tree and inspecting it + + 3. asking some kernel module or user-level program to authorize access. + + This allows for integration with things like capabilities, SELinux-style + secutiry contexts, etc. + +*/ +/* NIKITA-FIXME-HANS: define what this is targeted for. It does not seem to be intended for use with sys_reiser4. Explain. */ +typedef struct perm_plugin { + /* generic plugin fields */ + plugin_header h; + + /* check permissions for read/write */ + int (*read_ok) (struct file * file, const char *buf, size_t size, loff_t * off); + int (*write_ok) (struct file * file, const char *buf, size_t size, loff_t * off); + + /* check permissions for lookup */ + int (*lookup_ok) (struct inode * parent, struct dentry * dentry); + + /* check permissions for create */ + int (*create_ok) (struct inode * parent, struct dentry * dentry, reiser4_object_create_data * data); + + /* check permissions for linking @where to @existing */ + int (*link_ok) (struct dentry * existing, struct inode * parent, struct dentry * where); + + /* check permissions for unlinking @victim from @parent */ + int (*unlink_ok) (struct inode * parent, struct dentry * victim); + + /* check permissions for deletion of @object whose last reference is + by @parent */ + int (*delete_ok) (struct inode * parent, struct dentry * victim); + int (*mask_ok) (struct inode * inode, int mask); + /* check whether attribute change is acceptable */ + int (*setattr_ok) (struct dentry * dentry, struct iattr * attr); + + /* check whether stat(2) is allowed */ + int (*getattr_ok) (struct vfsmount * mnt UNUSED_ARG, struct dentry * dentry, struct kstat * stat); + /* check whether rename(2) is allowed */ + int (*rename_ok) (struct inode * old_dir, struct dentry * old, + struct inode * new_dir, struct dentry * new); +} perm_plugin; +/* NIKITA-FIXME-HANS: I really hate things like this that kill the ability of Meta-. to work. Please eliminate this macro, exce */ +/* call ->check_ok method of perm plugin for inode */ +#define perm_chk(inode, check, ...) \ +({ \ + perm_plugin *perm; \ + \ + perm = inode_perm_plugin(inode); \ + (perm == NULL || perm->check ## _ok == NULL) ? \ + 0 : \ + perm->check ## _ok(__VA_ARGS__); \ +}) + +typedef enum { RWX_PERM_ID, LAST_PERM_ID } reiser4_perm_id; + +/* __REISER4_PERM_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/space/bitmap.c 2004-09-03 00:57:05.344226816 -0700 @@ -0,0 +1,1602 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#include "../../debug.h" +#include "../../dformat.h" +#include "../../txnmgr.h" +#include "../../jnode.h" +#include "../../block_alloc.h" +#include "../../tree.h" +#include "../../super.h" +#include "../../lib.h" + +#include "../plugin.h" +#include "../../diskmap.h" + +#include "space_allocator.h" +#include "bitmap.h" + +#include +#include /* for struct super_block */ +#include +#include + +/* Proposed (but discarded) optimization: dynamic loading/unloading of bitmap + * blocks + + A useful optimization of reiser4 bitmap handling would be dynamic bitmap + blocks loading/unloading which is different from v3.x where all bitmap + blocks are loaded at mount time. + + To implement bitmap blocks unloading we need to count bitmap block usage + and detect currently unused blocks allowing them to be unloaded. It is not + a simple task since we allow several threads to modify one bitmap block + simultaneously. + + Briefly speaking, the following schema is proposed: we count in special + variable associated with each bitmap block. That is for counting of block + alloc/dealloc operations on that bitmap block. With a deferred block + deallocation feature of reiser4 all those operation will be represented in + atom dirty/deleted lists as jnodes for freshly allocated or deleted + nodes. + + So, we increment usage counter for each new node allocated or deleted, and + decrement it at atom commit one time for each node from the dirty/deleted + atom's list. Of course, freshly allocated node deletion and node reusing + from atom deleted (if we do so) list should decrement bitmap usage counter + also. + + This schema seems to be working but that reference counting is + not easy to debug. I think we should agree with Hans and do not implement + it in v4.0. Current code implements "on-demand" bitmap blocks loading only. + + For simplicity all bitmap nodes (both commit and working bitmap blocks) are + loaded into memory on fs mount time or each bitmap nodes are loaded at the + first access to it, the "dont_load_bitmap" mount option controls whether + bimtap nodes should be loaded at mount time. Dynamic unloading of bitmap + nodes currently is not supported. */ + +#define CHECKSUM_SIZE 4 + +#define BYTES_PER_LONG (sizeof(long)) + +#if BITS_PER_LONG == 64 +# define LONG_INT_SHIFT (6) +#else +# define LONG_INT_SHIFT (5) +#endif + +#define LONG_INT_MASK (BITS_PER_LONG - 1) + +typedef unsigned long ulong_t; + + +#define bmap_size(blocksize) ((blocksize) - CHECKSUM_SIZE) +#define bmap_bit_count(blocksize) (bmap_size(blocksize) << 3) + +/* Block allocation/deallocation are done through special bitmap objects which + are allocated in an array at fs mount. */ +struct bitmap_node { + struct semaphore sema; /* long term lock object */ + + jnode *wjnode; /* j-nodes for WORKING ... */ + jnode *cjnode; /* ... and COMMIT bitmap blocks */ + + bmap_off_t first_zero_bit; /* for skip_busy option implementation */ + + atomic_t loaded; /* a flag which shows that bnode is loaded + * already */ +}; + +static inline char * +bnode_working_data(struct bitmap_node *bnode) +{ + char *data; + + data = jdata(bnode->wjnode); + assert("zam-429", data != NULL); + + return data + CHECKSUM_SIZE; +} + +static inline char * +bnode_commit_data(const struct bitmap_node *bnode) +{ + char *data; + + data = jdata(bnode->cjnode); + assert("zam-430", data != NULL); + + return data + CHECKSUM_SIZE; +} + +static inline __u32 +bnode_commit_crc(const struct bitmap_node *bnode) +{ + char *data; + + data = jdata(bnode->cjnode); + assert("vpf-261", data != NULL); + + return d32tocpu((d32 *) data); +} + +static inline void +bnode_set_commit_crc(struct bitmap_node *bnode, __u32 crc) +{ + char *data; + + data = jdata(bnode->cjnode); + assert("vpf-261", data != NULL); + + cputod32(crc, (d32 *) data); +} + +/* ZAM-FIXME-HANS: is the idea that this might be a union someday? having + * written the code, does this added abstraction still have */ +/* ANSWER(Zam): No, the abstractions is in the level above (exact place is the + * reiser4_space_allocator structure) */ +/* ZAM-FIXME-HANS: I don't understand your english in comment above. */ +/* FIXME-HANS(Zam): I don't understand the questions like "might be a union + * someday?". What they about? If there is a reason to have a union, it should + * be a union, if not, it should not be a union. "..might be someday" means no + * reason. */ +struct bitmap_allocator_data { + /* an array for bitmap blocks direct access */ + struct bitmap_node *bitmap; +}; + +#define get_barray(super) \ +(((struct bitmap_allocator_data *)(get_super_private(super)->space_allocator.u.generic)) -> bitmap) + +#define get_bnode(super, i) (get_barray(super) + i) + +/* allocate and initialize jnode with JNODE_BITMAP type */ +static jnode * +bnew(void) +{ + jnode *jal = jalloc(); + + if (jal) + jnode_init(jal, current_tree, JNODE_BITMAP); + + return jal; +} + +/* this file contains: + - bitmap based implementation of space allocation plugin + - all the helper functions like set bit, find_first_zero_bit, etc */ + +/* Audited by: green(2002.06.12) */ +static int +find_next_zero_bit_in_word(ulong_t word, int start_bit) +{ + unsigned int mask = 1 << start_bit; + int i = start_bit; + + while ((word & mask) != 0) { + mask <<= 1; + if (++i >= BITS_PER_LONG) + break; + } + + return i; +} + +#include + +#define reiser4_set_bit(nr, addr) ext2_set_bit(nr, addr) +#define reiser4_clear_bit(nr, addr) ext2_clear_bit(nr, addr) +#define reiser4_test_bit(nr, addr) ext2_test_bit(nr, addr) + +#define reiser4_find_next_zero_bit(addr, maxoffset, offset) \ +ext2_find_next_zero_bit(addr, maxoffset, offset) + +/* Search for a set bit in the bit array [@start_offset, @max_offset[, offsets + * are counted from @addr, return the offset of the first bit if it is found, + * @maxoffset otherwise. */ +static bmap_off_t reiser4_find_next_set_bit( + void *addr, bmap_off_t max_offset, bmap_off_t start_offset) +{ + ulong_t *base = addr; + /* start_offset is in bits, convert it to byte offset within bitmap. */ + int word_nr = start_offset >> LONG_INT_SHIFT; + /* bit number within the byte. */ + int bit_nr = start_offset & LONG_INT_MASK; + int max_word_nr = (max_offset - 1) >> LONG_INT_SHIFT; + + assert("zam-387", max_offset != 0); + + /* Unaligned @start_offset case. */ + if (bit_nr != 0) { + bmap_nr_t nr; + + nr = find_next_zero_bit_in_word(~(base[word_nr]), bit_nr); + + if (nr < BITS_PER_LONG) + return (word_nr << LONG_INT_SHIFT) + nr; + + ++word_nr; + } + + /* Fast scan trough aligned words. */ + while (word_nr <= max_word_nr) { + if (base[word_nr] != 0) { + return (word_nr << LONG_INT_SHIFT) + + find_next_zero_bit_in_word(~(base[word_nr]), 0); + } + + ++word_nr; + } + + return max_offset; +} + +/* search for the first set bit in single word. */ +static int find_last_set_bit_in_word (ulong_t word, int start_bit) +{ + unsigned bit_mask; + int nr = start_bit; + + assert ("zam-965", start_bit < BITS_PER_LONG); + assert ("zam-966", start_bit >= 0); + + bit_mask = (1 << nr); + + while (bit_mask != 0) { + if (bit_mask & word) + return nr; + bit_mask >>= 1; + nr --; + } + return BITS_PER_LONG; +} + +/* Search bitmap for a set bit in backward direction from the end to the + * beginning of given region + * + * @result: result offset of the last set bit + * @addr: base memory address, + * @low_off: low end of the search region, edge bit included into the region, + * @high_off: high end of the search region, edge bit included into the region, + * + * @return: 0 - set bit was found, -1 otherwise. + */ +static int +reiser4_find_last_set_bit (bmap_off_t * result, void * addr, bmap_off_t low_off, bmap_off_t high_off) +{ + ulong_t * base = addr; + int last_word; + int first_word; + int last_bit; + int nr; + + assert ("zam-961", high_off >= 0); + assert ("zam-962", high_off >= low_off); + + last_word = high_off >> LONG_INT_SHIFT; + last_bit = high_off & LONG_INT_MASK; + first_word = low_off >> LONG_INT_SHIFT; + + if (last_bit < BITS_PER_LONG) { + nr = find_last_set_bit_in_word(base[last_word], last_bit); + if (nr < BITS_PER_LONG) { + *result = (last_word << LONG_INT_SHIFT) + nr; + return 0; + } + -- last_word; + } + while (last_word >= first_word) { + if (base[last_word] != 0x0) { + last_bit = find_last_set_bit_in_word(base[last_word], BITS_PER_LONG - 1); + assert ("zam-972", last_bit < BITS_PER_LONG); + *result = (last_word << LONG_INT_SHIFT) + last_bit; + return 0; + } + -- last_word; + } + + return -1; /* set bit not found */ +} + +/* Search bitmap for a clear bit in backward direction from the end to the + * beginning of given region */ +static int +reiser4_find_last_zero_bit (bmap_off_t * result, void * addr, bmap_off_t low_off, bmap_off_t high_off) +{ + ulong_t * base = addr; + int last_word; + int first_word; + int last_bit; + int nr; + + last_word = high_off >> LONG_INT_SHIFT; + last_bit = high_off & LONG_INT_MASK; + first_word = low_off >> LONG_INT_SHIFT; + + if (last_bit < BITS_PER_LONG) { + nr = find_last_set_bit_in_word(~base[last_word], last_bit); + if (nr < BITS_PER_LONG) { + *result = (last_word << LONG_INT_SHIFT) + nr; + return 0; + } + -- last_word; + } + while (last_word >= first_word) { + if (base[last_word] != (ulong_t)(-1)) { + *result = (last_word << LONG_INT_SHIFT) + + find_last_set_bit_in_word(~base[last_word], BITS_PER_LONG - 1); + return 0; + } + -- last_word; + } + + return -1; /* zero bit not found */ +} + +/* Audited by: green(2002.06.12) */ +static void +reiser4_clear_bits(char *addr, bmap_off_t start, bmap_off_t end) +{ + int first_byte; + int last_byte; + + unsigned char first_byte_mask = 0xFF; + unsigned char last_byte_mask = 0xFF; + + assert("zam-410", start < end); + + first_byte = start >> 3; + last_byte = (end - 1) >> 3; + + if (last_byte > first_byte + 1) + xmemset(addr + first_byte + 1, 0, (size_t) (last_byte - first_byte - 1)); + + first_byte_mask >>= 8 - (start & 0x7); + last_byte_mask <<= ((end - 1) & 0x7) + 1; + + if (first_byte == last_byte) { + addr[first_byte] &= (first_byte_mask | last_byte_mask); + } else { + addr[first_byte] &= first_byte_mask; + addr[last_byte] &= last_byte_mask; + } +} + +/* Audited by: green(2002.06.12) */ +/* ZAM-FIXME-HANS: comment this */ +static void +reiser4_set_bits(char *addr, bmap_off_t start, bmap_off_t end) +{ + int first_byte; + int last_byte; + + unsigned char first_byte_mask = 0xFF; + unsigned char last_byte_mask = 0xFF; + + assert("zam-386", start < end); + + first_byte = start >> 3; + last_byte = (end - 1) >> 3; + + if (last_byte > first_byte + 1) + xmemset(addr + first_byte + 1, 0xFF, (size_t) (last_byte - first_byte - 1)); + + first_byte_mask <<= start & 0x7; + last_byte_mask >>= 7 - ((end - 1) & 0x7); + + if (first_byte == last_byte) { + addr[first_byte] |= (first_byte_mask & last_byte_mask); + } else { + addr[first_byte] |= first_byte_mask; + addr[last_byte] |= last_byte_mask; + } +} + +#define ADLER_BASE 65521 +#define ADLER_NMAX 5552 + +/* Calculates the adler32 checksum for the data pointed by `data` of the + length `len`. This function was originally taken from zlib, version 1.1.3, + July 9th, 1998. + + Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + The above comment applies only to the adler32 function. +*/ + +static __u32 +adler32(char *data, __u32 len) +{ + unsigned char *t = data; + __u32 s1 = 1; + __u32 s2 = 0; + int k; + + while (len > 0) { + k = len < ADLER_NMAX ? len : ADLER_NMAX; + len -= k; + + while (k--) { + s1 += *t++; + s2 += s1; + } + + s1 %= ADLER_BASE; + s2 %= ADLER_BASE; + } + return (s2 << 16) | s1; +} + +static __u32 +bnode_calc_crc(const struct bitmap_node *bnode) +{ + struct super_block *super; + + super = jnode_get_tree(bnode->wjnode)->super; + return adler32(bnode_commit_data(bnode), bmap_size(super->s_blocksize)); +} + +#define REISER4_CHECK_BMAP_CRC (0) + +#if REISER4_CHECK_BMAP_CRC +static int +bnode_check_crc(const struct bitmap_node *bnode) +{ + if (bnode_calc_crc(bnode) != bnode_commit_crc (bnode)) { + bmap_nr_t bmap; + struct super_block *super; + + super = jnode_get_tree(bnode->wjnode)->super; + bmap = bnode - get_bnode(super, 0) + + warning("vpf-263", + "Checksum for the bitmap block %llu is incorrect", bmap); + return RETERR(-EIO); + } else + return 0; +} + +/* REISER4_CHECK_BMAP_CRC */ +#else + +#define bnode_check_crc(bnode) (0) + +/* REISER4_CHECK_BMAP_CRC */ +#endif + +/* Recalculates the adler32 checksum for only 1 byte change. + adler - previous adler checksum + old_data, data - old, new byte values. + tail == (chunk - offset) : length, checksum was calculated for, - offset of + the changed byte within this chunk. + This function can be used for checksum calculation optimisation. +*/ + +static __u32 +adler32_recalc(__u32 adler, unsigned char old_data, unsigned char data, __u32 tail) +{ + __u32 delta = data - old_data + 2 * ADLER_BASE; + __u32 s1 = adler & 0xffff; + __u32 s2 = (adler >> 16) & 0xffff; + + s1 = (delta + s1) % ADLER_BASE; + s2 = (delta * tail + s2) % ADLER_BASE; + + return (s2 << 16) | s1; +} + + +#define LIMIT(val, boundary) ((val) > (boundary) ? (boundary) : (val)) + +/* A number of bitmap blocks for given fs. This number can be stored on disk + or calculated on fly; it depends on disk format. +VS-FIXME-HANS: explain calculation, using device with block count of 8 * 4096 blocks as an example. + FIXME-VS: number of blocks in a filesystem is taken from reiser4 + super private data */ +/* Audited by: green(2002.06.12) */ +static bmap_nr_t +get_nr_bmap(const struct super_block *super) +{ + assert("zam-393", reiser4_block_count(super) != 0); + + return div64_32(reiser4_block_count(super) - 1, bmap_bit_count(super->s_blocksize), NULL) + 1; + +} + +/* calculate bitmap block number and offset within that bitmap block */ +static void +parse_blocknr(const reiser4_block_nr * block, bmap_nr_t * bmap, bmap_off_t * offset) +{ + struct super_block *super = get_current_context()->super; + + *bmap = div64_32(*block, bmap_bit_count(super->s_blocksize), offset); + + assert("zam-433", *bmap < get_nr_bmap(super)); +} + +#if REISER4_DEBUG +/* Audited by: green(2002.06.12) */ +static void +check_block_range(const reiser4_block_nr * start, const reiser4_block_nr * len) +{ + struct super_block *sb = reiser4_get_current_sb(); + + assert("zam-436", sb != NULL); + + assert("zam-455", start != NULL); + assert("zam-437", *start != 0); + assert("zam-541", !blocknr_is_fake(start)); + assert("zam-441", *start < reiser4_block_count(sb)); + + if (len != NULL) { + assert("zam-438", *len != 0); + assert("zam-442", *start + *len <= reiser4_block_count(sb)); + } +} + +static void +check_bnode_loaded(const struct bitmap_node *bnode) +{ + assert("zam-485", bnode != NULL); + assert("zam-483", jnode_page(bnode->wjnode) != NULL); + assert("zam-484", jnode_page(bnode->cjnode) != NULL); + assert("nikita-2820", jnode_is_loaded(bnode->wjnode)); + assert("nikita-2821", jnode_is_loaded(bnode->cjnode)); +} + +#else + +# define check_block_range(start, len) do { /* nothing */} while(0) +# define check_bnode_loaded(bnode) do { /* nothing */} while(0) + +#endif + +/* modify bnode->first_zero_bit (if we free bits before); bnode should be + spin-locked */ +static inline void +adjust_first_zero_bit(struct bitmap_node *bnode, bmap_off_t offset) +{ + if (offset < bnode->first_zero_bit) + bnode->first_zero_bit = offset; +} + +/* return a physical disk address for logical bitmap number @bmap */ +/* FIXME-VS: this is somehow related to disk layout? */ +/* ZAM-FIXME-HANS: your answer is? Use not more than one function dereference + * per block allocation so that performance is not affected. Probably this + * whole file should be considered part of the disk layout plugin, and other + * disk layouts can use other defines and efficiency will not be significantly + * affected. */ + +#define REISER4_FIRST_BITMAP_BLOCK \ + ((REISER4_MASTER_OFFSET / PAGE_CACHE_SIZE) + 2) + +/* Audited by: green(2002.06.12) */ +reiser4_internal void +get_bitmap_blocknr(struct super_block *super, bmap_nr_t bmap, reiser4_block_nr * bnr) +{ + + assert("zam-390", bmap < get_nr_bmap(super)); + +#ifdef CONFIG_REISER4_BADBLOCKS +#define BITMAP_PLUGIN_DISKMAP_ID ((0xc0e1<<16) | (0xe0ff)) + /* Check if the diskmap have this already, first. */ + if ( reiser4_get_diskmap_value( BITMAP_PLUGIN_DISKMAP_ID, bmap, bnr) == 0 ) + return; /* Found it in diskmap */ +#endif + /* FIXME_ZAM: before discussing of disk layouts and disk format + plugins I implement bitmap location scheme which is close to scheme + used in reiser 3.6 */ + if (bmap == 0) { + *bnr = REISER4_FIRST_BITMAP_BLOCK; + } else { + *bnr = bmap * bmap_bit_count(super->s_blocksize); + } +} + +/* construct a fake block number for shadow bitmap (WORKING BITMAP) block */ +/* Audited by: green(2002.06.12) */ +reiser4_internal void +get_working_bitmap_blocknr(bmap_nr_t bmap, reiser4_block_nr * bnr) +{ + *bnr = (reiser4_block_nr) ((bmap & ~REISER4_BLOCKNR_STATUS_BIT_MASK) | REISER4_BITMAP_BLOCKS_STATUS_VALUE); +} + +/* bnode structure initialization */ +static void +init_bnode(struct bitmap_node *bnode, + struct super_block *super UNUSED_ARG, bmap_nr_t bmap UNUSED_ARG) +{ + xmemset(bnode, 0, sizeof (struct bitmap_node)); + + sema_init(&bnode->sema, 1); + atomic_set(&bnode->loaded, 0); +} + +static void +release(jnode *node) +{ + jrelse(node); + JF_SET(node, JNODE_HEARD_BANSHEE); + jput(node); +} + +/* This function is for internal bitmap.c use because it assumes that jnode is + in under full control of this thread */ +static void +done_bnode(struct bitmap_node *bnode) +{ + if (bnode) { + atomic_set(&bnode->loaded, 0); + if (bnode->wjnode != NULL) + release(bnode->wjnode); + if (bnode->cjnode != NULL) + release(bnode->cjnode); + bnode->wjnode = bnode->cjnode = NULL; + } +} + +/* ZAM-FIXME-HANS: comment this. Called only by load_and_lock_bnode()*/ +static int +prepare_bnode(struct bitmap_node *bnode, jnode **cjnode_ret, jnode **wjnode_ret) +{ + struct super_block *super; + jnode *cjnode; + jnode *wjnode; + bmap_nr_t bmap; + int ret; + + super = reiser4_get_current_sb(); + + *wjnode_ret = wjnode = bnew(); + if (wjnode == NULL) + return RETERR(-ENOMEM); + + *cjnode_ret = cjnode = bnew(); + if (cjnode == NULL) + return RETERR(-ENOMEM); + + bmap = bnode - get_bnode(super, 0); + + get_working_bitmap_blocknr(bmap, &wjnode->blocknr); + get_bitmap_blocknr(super, bmap, &cjnode->blocknr); + + jref(cjnode); + jref(wjnode); + + /* load commit bitmap */ + ret = jload_gfp(cjnode, GFP_NOFS, 1); + + if (ret) + goto error; + + /* allocate memory for working bitmap block. Note that for + * bitmaps jinit_new() doesn't actually modifies node content, + * so parallel calls to this are ok. */ + ret = jinit_new(wjnode, GFP_NOFS); + + if (ret != 0) { + jrelse(cjnode); + goto error; + } + + return 0; + + error: + jput(cjnode); + jput(wjnode); + *wjnode_ret = *cjnode_ret = NULL; + return ret; + +} + + static int +check_adler32_jnode(jnode *jnode, unsigned long size) { + return (adler32(jdata(jnode) + CHECKSUM_SIZE, size) != *(__u32 *)jdata(jnode)); +} + +/* Check the bnode data on read. */ +static int check_struct_bnode(struct bitmap_node *bnode, __u32 blksize) { + void *data; + + /* Check CRC */ + if (check_adler32_jnode(bnode->cjnode, bmap_size(blksize))) { + warning("vpf-1361", "Checksum for the bitmap block %llu " + "is incorrect", bnode->cjnode->blocknr); + + return -EINVAL; + } + + data = jdata(bnode->cjnode) + CHECKSUM_SIZE; + + /* Check the very first bit -- it must be busy. */ + if (!reiser4_test_bit(0, data)) { + warning("vpf-1362", "The allocator block %llu is not marked as used.", + bnode->cjnode->blocknr); + + return -EINVAL; + } + + return 0; +} + +/* load bitmap blocks "on-demand" */ +static int +load_and_lock_bnode(struct bitmap_node *bnode) +{ + int ret; + + jnode *cjnode; + jnode *wjnode; + + assert("nikita-3040", schedulable()); + +/* ZAM-FIXME-HANS: since bitmaps are never unloaded, this does not + * need to be atomic, right? Just leave a comment that if bitmaps were + * unloadable, this would need to be atomic. */ + if (atomic_read(&bnode->loaded)) { + /* bitmap is already loaded, nothing to do */ + check_bnode_loaded(bnode); + down(&bnode->sema); + assert("nikita-2827", atomic_read(&bnode->loaded)); + return 0; + } + + ret = prepare_bnode(bnode, &cjnode, &wjnode); + if (ret == 0) { + down(&bnode->sema); + + if (!atomic_read(&bnode->loaded)) { + assert("nikita-2822", cjnode != NULL); + assert("nikita-2823", wjnode != NULL); + assert("nikita-2824", jnode_is_loaded(cjnode)); + assert("nikita-2825", jnode_is_loaded(wjnode)); + + bnode->wjnode = wjnode; + bnode->cjnode = cjnode; + + ret = check_struct_bnode(bnode, current_blocksize); + if (!ret) { + cjnode = wjnode = NULL; + atomic_set(&bnode->loaded, 1); + /* working bitmap is initialized by on-disk + * commit bitmap. This should be performed + * under semaphore. */ + xmemcpy(bnode_working_data(bnode), + bnode_commit_data(bnode), + bmap_size(current_blocksize)); + } else { + up(&bnode->sema); + } + } else + /* race: someone already loaded bitmap while we were + * busy initializing data. */ + check_bnode_loaded(bnode); + } + + if (wjnode != NULL) + release(wjnode); + if (cjnode != NULL) + release(cjnode); + + return ret; +} + +static void +release_and_unlock_bnode(struct bitmap_node *bnode) +{ + check_bnode_loaded(bnode); + up(&bnode->sema); +} + +/* This function does all block allocation work but only for one bitmap + block.*/ +/* FIXME_ZAM: It does not allow us to allocate block ranges across bitmap + block responsibility zone boundaries. This had no sense in v3.6 but may + have it in v4.x */ +/* ZAM-FIXME-HANS: do you mean search one bitmap block forward? */ +static int +search_one_bitmap_forward(bmap_nr_t bmap, bmap_off_t * offset, bmap_off_t max_offset, + int min_len, int max_len) +{ + struct super_block *super = get_current_context()->super; + struct bitmap_node *bnode = get_bnode(super, bmap); + + char *data; + + bmap_off_t search_end; + bmap_off_t start; + bmap_off_t end; + + int set_first_zero_bit = 0; + + int ret; + + assert("zam-364", min_len > 0); + assert("zam-365", max_len >= min_len); + assert("zam-366", *offset < max_offset); + + ret = load_and_lock_bnode(bnode); + + if (ret) + return ret; + + data = bnode_working_data(bnode); + + start = *offset; + + if (bnode->first_zero_bit >= start) { + start = bnode->first_zero_bit; + set_first_zero_bit = 1; + } + + while (start + min_len < max_offset) { + + start = reiser4_find_next_zero_bit((long *) data, max_offset, start); + if (set_first_zero_bit) { + bnode->first_zero_bit = start; + set_first_zero_bit = 0; + } + if (start >= max_offset) + break; + + search_end = LIMIT(start + max_len, max_offset); + end = reiser4_find_next_set_bit((long *) data, search_end, start); + if (end >= start + min_len) { + /* we can't trust find_next_set_bit result if set bit + was not fount, result may be bigger than + max_offset */ + if (end > search_end) + end = search_end; + + ret = end - start; + *offset = start; + + reiser4_set_bits(data, start, end); + + /* FIXME: we may advance first_zero_bit if [start, + end] region overlaps the first_zero_bit point */ + + break; + } + + start = end + 1; + } + + release_and_unlock_bnode(bnode); + + return ret; +} + +static int +search_one_bitmap_backward (bmap_nr_t bmap, bmap_off_t * start_offset, bmap_off_t end_offset, + int min_len, int max_len) +{ + struct super_block *super = get_current_context()->super; + struct bitmap_node *bnode = get_bnode(super, bmap); + char *data; + bmap_off_t start; + int ret; + + assert("zam-958", min_len > 0); + assert("zam-959", max_len >= min_len); + assert("zam-960", *start_offset >= end_offset); + + ret = load_and_lock_bnode(bnode); + if (ret) + return ret; + + data = bnode_working_data(bnode); + start = *start_offset; + + while (1) { + bmap_off_t end, search_end; + + /* Find the beginning of the zero filled region */ + if (reiser4_find_last_zero_bit(&start, data, end_offset, start)) + break; + /* Is there more than `min_len' bits from `start' to + * `end_offset'? */ + if (start < end_offset + min_len - 1) + break; + + /* Do not search to `end_offset' if we need to find less than + * `max_len' zero bits. */ + if (end_offset + max_len - 1 < start) + search_end = start - max_len + 1; + else + search_end = end_offset; + + if (reiser4_find_last_set_bit(&end, data, search_end, start)) + end = search_end; + else + end ++; + + if (end + min_len <= start + 1) { + if (end < search_end) + end = search_end; + ret = start - end + 1; + *start_offset = end; /* `end' is lowest offset */ + assert ("zam-987", reiser4_find_next_set_bit(data, start + 1, end) >= start + 1); + reiser4_set_bits(data, end, start + 1); + break; + } + + if (end <= end_offset) + /* left search boundary reached. */ + break; + start = end - 1; + } + + release_and_unlock_bnode(bnode); + return ret; +} + +/* allocate contiguous range of blocks in bitmap */ +static int bitmap_alloc_forward(reiser4_block_nr * start, const reiser4_block_nr * end, + int min_len, int max_len) +{ + bmap_nr_t bmap, end_bmap; + bmap_off_t offset, end_offset; + int len; + + reiser4_block_nr tmp; + + struct super_block *super = get_current_context()->super; + const bmap_off_t max_offset = bmap_bit_count(super->s_blocksize); + + parse_blocknr(start, &bmap, &offset); + + tmp = *end - 1; + parse_blocknr(&tmp, &end_bmap, &end_offset); + ++end_offset; + + assert("zam-358", end_bmap >= bmap); + assert("zam-359", ergo(end_bmap == bmap, end_offset > offset)); + + for (; bmap < end_bmap; bmap++, offset = 0) { + len = search_one_bitmap_forward(bmap, &offset, max_offset, min_len, max_len); + if (len != 0) + goto out; + } + + len = search_one_bitmap_forward(bmap, &offset, end_offset, min_len, max_len); +out: + *start = bmap * max_offset + offset; + return len; +} + +/* allocate contiguous range of blocks in bitmap (from @start to @end in + * backward direction) */ +static int bitmap_alloc_backward(reiser4_block_nr * start, const reiser4_block_nr * end, + int min_len, int max_len) +{ + bmap_nr_t bmap, end_bmap; + bmap_off_t offset, end_offset; + int len; + struct super_block *super = get_current_context()->super; + const bmap_off_t max_offset = bmap_bit_count(super->s_blocksize); + + parse_blocknr(start, &bmap, &offset); + parse_blocknr(end, &end_bmap, &end_offset); + + assert("zam-961", end_bmap <= bmap); + assert("zam-962", ergo(end_bmap == bmap, end_offset <= offset)); + + for (; bmap > end_bmap; bmap --, offset = max_offset - 1) { + len = search_one_bitmap_backward(bmap, &offset, 0, min_len, max_len); + if (len != 0) + goto out; + } + + len = search_one_bitmap_backward(bmap, &offset, end_offset, min_len, max_len); + out: + *start = bmap * max_offset + offset; + return len; +} + +/* plugin->u.space_allocator.alloc_blocks() */ +reiser4_internal int +alloc_blocks_forward(reiser4_blocknr_hint * hint, int needed, + reiser4_block_nr * start, reiser4_block_nr * len) +{ + struct super_block *super = get_current_context()->super; + int actual_len; + + reiser4_block_nr search_start; + reiser4_block_nr search_end; + + assert("zam-398", super != NULL); + assert("zam-412", hint != NULL); + assert("zam-397", hint->blk < reiser4_block_count(super)); + + if (hint->max_dist == 0) + search_end = reiser4_block_count(super); + else + search_end = LIMIT(hint->blk + hint->max_dist, reiser4_block_count(super)); + + /* We use @hint -> blk as a search start and search from it to the end + of the disk or in given region if @hint -> max_dist is not zero */ + search_start = hint->blk; + + actual_len = bitmap_alloc_forward(&search_start, &search_end, 1, needed); + + /* There is only one bitmap search if max_dist was specified or first + pass was from the beginning of the bitmap. We also do one pass for + scanning bitmap in backward direction. */ + if (!(actual_len != 0 || hint->max_dist != 0 || search_start == 0)) { + /* next step is a scanning from 0 to search_start */ + search_end = search_start; + search_start = 0; + actual_len = bitmap_alloc_forward(&search_start, &search_end, 1, needed); + } + if (actual_len == 0) + return RETERR(-ENOSPC); + if (actual_len < 0) + return RETERR(actual_len); + *len = actual_len; + *start = search_start; + return 0; +} + +static int alloc_blocks_backward (reiser4_blocknr_hint * hint, int needed, + reiser4_block_nr * start, reiser4_block_nr * len) +{ + reiser4_block_nr search_start; + reiser4_block_nr search_end; + int actual_len; + + ON_DEBUG(struct super_block * super = reiser4_get_current_sb()); + + assert ("zam-969", super != NULL); + assert ("zam-970", hint != NULL); + assert ("zam-971", hint->blk < reiser4_block_count(super)); + + search_start = hint->blk; + if (hint->max_dist == 0 || search_start <= hint->max_dist) + search_end = 0; + else + search_end = search_start - hint->max_dist; + + actual_len = bitmap_alloc_backward(&search_start, &search_end, 1, needed); + if (actual_len == 0) + return RETERR(-ENOSPC); + if (actual_len < 0) + return RETERR(actual_len); + *len = actual_len; + *start = search_start; + return 0; +} + +/* plugin->u.space_allocator.alloc_blocks() */ +reiser4_internal int +alloc_blocks_bitmap(reiser4_space_allocator * allocator UNUSED_ARG, + reiser4_blocknr_hint * hint, int needed, + reiser4_block_nr * start, reiser4_block_nr * len) +{ + if (hint->backward) + return alloc_blocks_backward(hint, needed, start, len); + return alloc_blocks_forward(hint, needed, start, len); +} + +/* plugin->u.space_allocator.dealloc_blocks(). */ +/* It just frees blocks in WORKING BITMAP. Usually formatted an unformatted + nodes deletion is deferred until transaction commit. However, deallocation + of temporary objects like wandered blocks and transaction commit records + requires immediate node deletion from WORKING BITMAP.*/ +reiser4_internal void +dealloc_blocks_bitmap(reiser4_space_allocator * allocator UNUSED_ARG, reiser4_block_nr start, reiser4_block_nr len) +{ + struct super_block *super = reiser4_get_current_sb(); + + bmap_nr_t bmap; + bmap_off_t offset; + + struct bitmap_node *bnode; + int ret; + + assert("zam-468", len != 0); + check_block_range(&start, &len); + + parse_blocknr(&start, &bmap, &offset); + + assert("zam-469", offset + len <= bmap_bit_count(super->s_blocksize)); + + bnode = get_bnode(super, bmap); + + assert("zam-470", bnode != NULL); + + ret = load_and_lock_bnode(bnode); + assert("zam-481", ret == 0); + + reiser4_clear_bits(bnode_working_data(bnode), offset, (bmap_off_t) (offset + len)); + + adjust_first_zero_bit(bnode, offset); + + release_and_unlock_bnode(bnode); +} + + +/* plugin->u.space_allocator.check_blocks(). */ +reiser4_internal void +check_blocks_bitmap(const reiser4_block_nr * start, const reiser4_block_nr * len, int desired) +{ +#if REISER4_DEBUG + struct super_block *super = reiser4_get_current_sb(); + + bmap_nr_t bmap; + bmap_off_t start_offset; + bmap_off_t end_offset; + + struct bitmap_node *bnode; + int ret; + + assert("zam-622", len != NULL); + check_block_range(start, len); + parse_blocknr(start, &bmap, &start_offset); + + end_offset = start_offset + *len; + assert("nikita-2214", end_offset <= bmap_bit_count(super->s_blocksize)); + + bnode = get_bnode(super, bmap); + + assert("nikita-2215", bnode != NULL); + + ret = load_and_lock_bnode(bnode); + assert("zam-626", ret == 0); + + assert("nikita-2216", jnode_is_loaded(bnode->wjnode)); + + if (desired) { + assert("zam-623", reiser4_find_next_zero_bit(bnode_working_data(bnode), end_offset, start_offset) + >= end_offset); + } else { + assert("zam-624", reiser4_find_next_set_bit(bnode_working_data(bnode), end_offset, start_offset) + >= end_offset); + } + + release_and_unlock_bnode(bnode); +#endif +} + +/* conditional insertion of @node into atom's overwrite set if it was not there */ +static void +cond_add_to_overwrite_set (txn_atom * atom, jnode * node) +{ + assert("zam-546", atom != NULL); + assert("zam-547", atom->stage == ASTAGE_PRE_COMMIT); + assert("zam-548", node != NULL); + + LOCK_ATOM(atom); + LOCK_JNODE(node); + + if (node->atom == NULL) { + JF_SET(node, JNODE_OVRWR); + insert_into_atom_ovrwr_list(atom, node); + } else { + assert("zam-549", node->atom == atom); + } + + UNLOCK_JNODE(node); + UNLOCK_ATOM(atom); +} + +/* an actor which applies delete set to COMMIT bitmap pages and link modified + pages in a single-linked list */ +static int +apply_dset_to_commit_bmap(txn_atom * atom, const reiser4_block_nr * start, const reiser4_block_nr * len, void *data) +{ + + bmap_nr_t bmap; + bmap_off_t offset; + int ret; + + long long *blocks_freed_p = data; + + struct bitmap_node *bnode; + + struct super_block *sb = reiser4_get_current_sb(); + + check_block_range(start, len); + + parse_blocknr(start, &bmap, &offset); + + /* FIXME-ZAM: we assume that all block ranges are allocated by this + bitmap-based allocator and each block range can't go over a zone of + responsibility of one bitmap block; same assumption is used in + other journal hooks in bitmap code. */ + bnode = get_bnode(sb, bmap); + assert("zam-448", bnode != NULL); + + /* it is safe to unlock atom with is in ASTAGE_PRE_COMMIT */ + assert ("zam-767", atom->stage == ASTAGE_PRE_COMMIT); + ret = load_and_lock_bnode(bnode); + if (ret) + return ret; + + /* put bnode into atom's overwrite set */ + cond_add_to_overwrite_set (atom, bnode->cjnode); + + data = bnode_commit_data(bnode); + + ret = bnode_check_crc(bnode); + if (ret != 0) + return ret; + + if (len != NULL) { + /* FIXME-ZAM: a check that all bits are set should be there */ + assert("zam-443", offset + *len <= bmap_bit_count(sb->s_blocksize)); + reiser4_clear_bits(data, offset, (bmap_off_t) (offset + *len)); + + (*blocks_freed_p) += *len; + } else { + reiser4_clear_bit(offset, data); + (*blocks_freed_p)++; + } + + bnode_set_commit_crc(bnode, bnode_calc_crc(bnode)); + + release_and_unlock_bnode(bnode); + + return 0; +} + +/* plugin->u.space_allocator.pre_commit_hook(). */ +/* It just applies transaction changes to fs-wide COMMIT BITMAP, hoping the + rest is done by transaction manager (allocate wandered locations for COMMIT + BITMAP blocks, copy COMMIT BITMAP blocks data). */ +/* Only one instance of this function can be running at one given time, because + only one transaction can be committed a time, therefore it is safe to access + some global variables without any locking */ + +#if REISER4_COPY_ON_CAPTURE + +extern spinlock_t scan_lock; + +reiser4_internal int +pre_commit_hook_bitmap(void) +{ + struct super_block * super = reiser4_get_current_sb(); + txn_atom *atom; + + long long blocks_freed = 0; + + atom = get_current_atom_locked (); + BUG_ON(atom->stage != ASTAGE_PRE_COMMIT); + assert ("zam-876", atom->stage == ASTAGE_PRE_COMMIT); + spin_unlock_atom(atom); + + + + { /* scan atom's captured list and find all freshly allocated nodes, + * mark corresponded bits in COMMIT BITMAP as used */ + /* how cpu significant is this scan, should we someday have a freshly_allocated list? -Hans */ + capture_list_head *head = ATOM_CLEAN_LIST(atom); + jnode *node; + + spin_lock(&scan_lock); + node = capture_list_front(head); + + while (!capture_list_end(head, node)) { + int ret; + + assert("vs-1445", NODE_LIST(node) == CLEAN_LIST); + BUG_ON(node->atom != atom); + JF_SET(node, JNODE_SCANNED); + spin_unlock(&scan_lock); + + /* we detect freshly allocated jnodes */ + if (JF_ISSET(node, JNODE_RELOC)) { + bmap_nr_t bmap; + + bmap_off_t offset; + bmap_off_t index; + struct bitmap_node *bn; + __u32 size = bmap_size(super->s_blocksize); + char byte; + __u32 crc; + + assert("zam-559", !JF_ISSET(node, JNODE_OVRWR)); + assert("zam-460", !blocknr_is_fake(&node->blocknr)); + + parse_blocknr(&node->blocknr, &bmap, &offset); + bn = get_bnode(super, bmap); + + index = offset >> 3; + assert("vpf-276", index < size); + + ret = bnode_check_crc(bnode); + if (ret != 0) + return ret; + + check_bnode_loaded(bn); + load_and_lock_bnode(bn); + + byte = *(bnode_commit_data(bn) + index); + reiser4_set_bit(offset, bnode_commit_data(bn)); + + crc = adler32_recalc(bnode_commit_crc(bn), byte, + *(bnode_commit_data(bn) + + index), + size - index), + + bnode_set_commit_crc(bn, crc); + + release_and_unlock_bnode(bn); + + ret = bnode_check_crc(bnode); + if (ret != 0) + return ret; + + /* working of this depends on how it inserts + new j-node into clean list, because we are + scanning the same list now. It is OK, if + insertion is done to the list front */ + cond_add_to_overwrite_set (atom, bn->cjnode); + } + + spin_lock(&scan_lock); + JF_CLR(node, JNODE_SCANNED); + node = capture_list_next(node); + } + spin_unlock(&scan_lock); + } + + blocknr_set_iterator(atom, &atom->delete_set, apply_dset_to_commit_bmap, &blocks_freed, 0); + + blocks_freed -= atom->nr_blocks_allocated; + + { + reiser4_super_info_data *sbinfo; + + sbinfo = get_super_private(super); + + reiser4_spin_lock_sb(sbinfo); + sbinfo->blocks_free_committed += blocks_freed; + reiser4_spin_unlock_sb(sbinfo); + } + + return 0; +} + +#else /* ! REISER4_COPY_ON_CAPTURE */ + +reiser4_internal int +pre_commit_hook_bitmap(void) +{ + struct super_block * super = reiser4_get_current_sb(); + txn_atom *atom; + + long long blocks_freed = 0; + + atom = get_current_atom_locked (); + assert ("zam-876", atom->stage == ASTAGE_PRE_COMMIT); + spin_unlock_atom(atom); + + { /* scan atom's captured list and find all freshly allocated nodes, + * mark corresponded bits in COMMIT BITMAP as used */ + capture_list_head *head = ATOM_CLEAN_LIST(atom); + jnode *node = capture_list_front(head); + + while (!capture_list_end(head, node)) { + /* we detect freshly allocated jnodes */ + if (JF_ISSET(node, JNODE_RELOC)) { + int ret; + bmap_nr_t bmap; + + bmap_off_t offset; + bmap_off_t index; + struct bitmap_node *bn; + __u32 size = bmap_size(super->s_blocksize); + __u32 crc; + char byte; + + assert("zam-559", !JF_ISSET(node, JNODE_OVRWR)); + assert("zam-460", !blocknr_is_fake(&node->blocknr)); + + parse_blocknr(&node->blocknr, &bmap, &offset); + bn = get_bnode(super, bmap); + + index = offset >> 3; + assert("vpf-276", index < size); + + ret = bnode_check_crc(bnode); + if (ret != 0) + return ret; + + check_bnode_loaded(bn); + load_and_lock_bnode(bn); + + byte = *(bnode_commit_data(bn) + index); + reiser4_set_bit(offset, bnode_commit_data(bn)); + + crc = adler32_recalc(bnode_commit_crc(bn), byte, + *(bnode_commit_data(bn) + + index), + size - index), + + bnode_set_commit_crc(bn, crc); + + release_and_unlock_bnode(bn); + + ret = bnode_check_crc(bn); + if (ret != 0) + return ret; + + /* working of this depends on how it inserts + new j-node into clean list, because we are + scanning the same list now. It is OK, if + insertion is done to the list front */ + cond_add_to_overwrite_set (atom, bn->cjnode); + } + + node = capture_list_next(node); + } + } + + blocknr_set_iterator(atom, &atom->delete_set, apply_dset_to_commit_bmap, &blocks_freed, 0); + + blocks_freed -= atom->nr_blocks_allocated; + + { + reiser4_super_info_data *sbinfo; + + sbinfo = get_super_private(super); + + reiser4_spin_lock_sb(sbinfo); + sbinfo->blocks_free_committed += blocks_freed; + reiser4_spin_unlock_sb(sbinfo); + } + + return 0; +} +#endif /* ! REISER4_COPY_ON_CAPTURE */ + +/* plugin->u.space_allocator.init_allocator + constructor of reiser4_space_allocator object. It is called on fs mount */ +reiser4_internal int +init_allocator_bitmap(reiser4_space_allocator * allocator, struct super_block *super, void *arg UNUSED_ARG) +{ + struct bitmap_allocator_data *data = NULL; + bmap_nr_t bitmap_blocks_nr; + bmap_nr_t i; + + assert("nikita-3039", schedulable()); + + /* getting memory for bitmap allocator private data holder */ + data = reiser4_kmalloc(sizeof (struct bitmap_allocator_data), GFP_KERNEL); + + if (data == NULL) + return RETERR(-ENOMEM); + + /* allocation and initialization for the array of bnodes */ + bitmap_blocks_nr = get_nr_bmap(super); + + /* FIXME-ZAM: it is not clear what to do with huge number of bitmaps + which is bigger than 2^32 (= 8 * 4096 * 4096 * 2^32 bytes = 5.76e+17, + may I never meet someone who still uses the ia32 architecture when + storage devices of that size enter the market, and wants to use ia32 + with that storage device, much less reiser4. ;-) -Hans). Kmalloc is not possible and, + probably, another dynamic data structure should replace a static + array of bnodes. */ + /*data->bitmap = reiser4_kmalloc((size_t) (sizeof (struct bitmap_node) * bitmap_blocks_nr), GFP_KERNEL);*/ + data->bitmap = vmalloc(sizeof (struct bitmap_node) * bitmap_blocks_nr); + if (data->bitmap == NULL) { + reiser4_kfree(data); + return RETERR(-ENOMEM); + } + + for (i = 0; i < bitmap_blocks_nr; i++) + init_bnode(data->bitmap + i, super, i); + + allocator->u.generic = data; + +#if REISER4_DEBUG + get_super_private(super)->min_blocks_used += bitmap_blocks_nr; +#endif + + /* Load all bitmap blocks at mount time. */ + if (!test_bit(REISER4_DONT_LOAD_BITMAP, &get_super_private(super)->fs_flags)) { + __u64 start_time, elapsed_time; + struct bitmap_node * bnode; + int ret; + + if (REISER4_DEBUG) + printk(KERN_INFO "loading reiser4 bitmap..."); + start_time = jiffies; + + for (i = 0; i < bitmap_blocks_nr; i++) { + bnode = data->bitmap + i; + ret = load_and_lock_bnode(bnode); + if (ret) { + destroy_allocator_bitmap(allocator, super); + return ret; + } + release_and_unlock_bnode(bnode); + } + + elapsed_time = jiffies - start_time; + if (REISER4_DEBUG) + printk("...done (%llu jiffies)\n", + (unsigned long long)elapsed_time); + } + + return 0; +} + +/* plugin->u.space_allocator.destroy_allocator + destructor. It is called on fs unmount */ +reiser4_internal int +destroy_allocator_bitmap(reiser4_space_allocator * allocator, struct super_block *super) +{ + bmap_nr_t bitmap_blocks_nr; + bmap_nr_t i; + + struct bitmap_allocator_data *data = allocator->u.generic; + + assert("zam-414", data != NULL); + assert("zam-376", data->bitmap != NULL); + + bitmap_blocks_nr = get_nr_bmap(super); + + for (i = 0; i < bitmap_blocks_nr; i++) { + struct bitmap_node *bnode = data->bitmap + i; + + down(&bnode->sema); + +#if REISER4_DEBUG + if (atomic_read(&bnode->loaded)) { + jnode *wj = bnode->wjnode; + jnode *cj = bnode->cjnode; + + assert("zam-480", jnode_page(cj) != NULL); + assert("zam-633", jnode_page(wj) != NULL); + + assert("zam-634", + memcmp(jdata(wj), jdata(wj), + bmap_size(super->s_blocksize)) == 0); + + } +#endif + done_bnode(bnode); + up(&bnode->sema); + } + + /*reiser4_kfree(data->bitmap);*/ + vfree(data->bitmap); + reiser4_kfree(data); + + allocator->u.generic = NULL; + + return 0; +} + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 80 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/space/bitmap.h 2004-09-03 00:57:05.344226816 -0700 @@ -0,0 +1,44 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#if !defined (__REISER4_PLUGIN_SPACE_BITMAP_H__) +#define __REISER4_PLUGIN_SPACE_BITMAP_H__ + +#include "../../dformat.h" +#include "../../block_alloc.h" + +#include /* for __u?? */ +#include /* for struct super_block */ +/* EDWARD-FIXME-HANS: write something as informative as the below for every .h file lacking it. */ +/* declarations of functions implementing methods of space allocator plugin for + bitmap based allocator. The functions themselves are in bitmap.c */ +extern int init_allocator_bitmap(reiser4_space_allocator *, struct super_block *, void *); +extern int destroy_allocator_bitmap(reiser4_space_allocator *, struct super_block *); +extern int alloc_blocks_bitmap(reiser4_space_allocator *, + reiser4_blocknr_hint *, int needed, reiser4_block_nr * start, reiser4_block_nr * len); +extern void check_blocks_bitmap(const reiser4_block_nr *, const reiser4_block_nr *, int); + +extern void dealloc_blocks_bitmap(reiser4_space_allocator *, reiser4_block_nr, reiser4_block_nr); +extern int pre_commit_hook_bitmap(void); + +#define post_commit_hook_bitmap() do{}while(0) +#define post_write_back_hook_bitmap() do{}while(0) +#define print_info_bitmap(pref, al) do{}while(0) + +typedef __u64 bmap_nr_t; +typedef __u32 bmap_off_t; + +/* exported for user-level simulator */ +extern void get_bitmap_blocknr(struct super_block *, bmap_nr_t, reiser4_block_nr *); + +#endif /* __REISER4_PLUGIN_SPACE_BITMAP_H__ */ + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/space/space_allocator.h 2004-09-03 00:57:05.345226664 -0700 @@ -0,0 +1,80 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#ifndef __SPACE_ALLOCATOR_H__ +#define __SPACE_ALLOCATOR_H__ + +#include "../../forward.h" +#include "bitmap.h" +/* NIKITA-FIXME-HANS: surely this could use a comment. Something about how bitmap is the only space allocator for now, + * but... */ +#define DEF_SPACE_ALLOCATOR(allocator) \ + \ +static inline int sa_init_allocator (reiser4_space_allocator * al, struct super_block *s, void * opaque) \ +{ \ + return init_allocator_##allocator (al, s, opaque); \ +} \ + \ +static inline void sa_destroy_allocator (reiser4_space_allocator *al, struct super_block *s) \ +{ \ + destroy_allocator_##allocator (al, s); \ +} \ + \ +static inline int sa_alloc_blocks (reiser4_space_allocator *al, reiser4_blocknr_hint * hint, \ + int needed, reiser4_block_nr * start, reiser4_block_nr * len) \ +{ \ + return alloc_blocks_##allocator (al, hint, needed, start, len); \ +} \ +static inline void sa_dealloc_blocks (reiser4_space_allocator * al, reiser4_block_nr start, reiser4_block_nr len) \ +{ \ + dealloc_blocks_##allocator (al, start, len); \ +} \ + \ +static inline void sa_check_blocks (const reiser4_block_nr * start, const reiser4_block_nr * end, int desired) \ +{ \ + check_blocks_##allocator (start, end, desired); \ +} \ + \ +static inline void sa_pre_commit_hook (void) \ +{ \ + pre_commit_hook_##allocator (); \ +} \ + \ +static inline void sa_post_commit_hook (void) \ +{ \ + post_commit_hook_##allocator (); \ +} \ + \ +static inline void sa_post_write_back_hook (void) \ +{ \ + post_write_back_hook_##allocator(); \ +} \ + \ +static inline void sa_print_info(const char * prefix, reiser4_space_allocator * al) \ +{ \ + print_info_##allocator (prefix, al); \ +} + +DEF_SPACE_ALLOCATOR(bitmap) + +/* this object is part of reiser4 private in-core super block */ +struct reiser4_space_allocator { + union { + /* space allocators might use this pointer to reference their + * data. */ + void *generic; + } u; +}; + +/* __SPACE_ALLOCATOR_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/symlink.c 2004-09-03 00:57:05.346226512 -0700 @@ -0,0 +1,85 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#include "../forward.h" +#include "../debug.h" +#include "item/static_stat.h" +#include "plugin.h" +#include "../tree.h" +#include "../vfs_ops.h" +#include "../inode.h" +#include "object.h" + +#include +#include /* for struct inode */ + +/* symlink plugin's specific functions */ + +reiser4_internal int +create_symlink(struct inode *symlink, /* inode of symlink */ + struct inode *dir UNUSED_ARG, /* parent directory */ + reiser4_object_create_data * data /* info passed + * to us, this + * is filled by + * reiser4() + * syscall in + * particular */ ) +{ + int result; + + assert("nikita-680", symlink != NULL); + assert("nikita-681", S_ISLNK(symlink->i_mode)); + assert("nikita-685", inode_get_flag(symlink, REISER4_NO_SD)); + assert("nikita-682", dir != NULL); + assert("nikita-684", data != NULL); + assert("nikita-686", data->id == SYMLINK_FILE_PLUGIN_ID); + + /* + * stat data of symlink has symlink extension in which we store + * symlink content, that is, path symlink is pointing to. + */ + reiser4_inode_data(symlink)->extmask |= (1 << SYMLINK_STAT); + + assert("vs-838", symlink->u.generic_ip == 0); + symlink->u.generic_ip = (void *) data->name; + + assert("vs-843", symlink->i_size == 0); + INODE_SET_FIELD(symlink, i_size, strlen(data->name)); + + /* insert stat data appended with data->name */ + result = write_sd_by_inode_common(symlink); + if (result) { + /* FIXME-VS: Make sure that symlink->u.generic_ip is not attached + to kmalloced data */ + INODE_SET_FIELD(symlink, i_size, 0); + } else { + assert("vs-849", symlink->u.generic_ip && inode_get_flag(symlink, REISER4_GENERIC_PTR_USED)); + assert("vs-850", !memcmp((char *) symlink->u.generic_ip, data->name, (size_t) symlink->i_size + 1)); + } + return result; +} + +/* plugin->destroy_inode() */ +reiser4_internal void +destroy_inode_symlink(struct inode * inode) +{ + assert("edward-799", inode_file_plugin(inode) == file_plugin_by_id(SYMLINK_FILE_PLUGIN_ID)); + assert("edward-800", !is_bad_inode(inode) && is_inode_loaded(inode)); + assert("edward-801", inode_get_flag(inode, REISER4_GENERIC_PTR_USED)); + assert("vs-839", S_ISLNK(inode->i_mode)); + + reiser4_kfree_in_sb(inode->u.generic_ip, inode->i_sb); + inode->u.generic_ip = 0; + inode_clr_flag(inode, REISER4_GENERIC_PTR_USED); +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/symlink.h 2004-09-03 00:57:05.346226512 -0700 @@ -0,0 +1,24 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#if !defined( __REISER4_SYMLINK_H__ ) +#define __REISER4_SYMLINK_H__ + +#include "../forward.h" +#include /* for struct inode */ + +int create_symlink(struct inode *symlink, struct inode *dir, reiser4_object_create_data * data); +void destroy_inode_symlink(struct inode * inode); + +/* __REISER4_SYMLINK_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/plugin/tail_policy.c 2004-09-03 00:57:05.347226360 -0700 @@ -0,0 +1,109 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Tail policy plugins */ + +/* Tail policy is used by object plugin (of regular file) to convert file + between two representations. TO BE CONTINUED. +NIKITA-FIXME-HANS: the "TO BE CONTINUED" means what? +GREV-FIXME-HANS: why the references to tails above? fix comments and website.... tail implies it is less than the whole file that is formatted, and it is not.... not in v4.... + + Currently following policies are implemented: + + never tail + + always tail + + only tail if file is smaller than 4 blocks (default). +*/ + +#include "../tree.h" +#include "../inode.h" +#include "../super.h" +#include "object.h" +#include "plugin.h" +#include "node/node.h" +#include "plugin_header.h" +#include "../lib.h" + +#include +#include /* For struct inode */ + +/* Never store file's tail as direct item */ +/* Audited by: green(2002.06.12) */ +static int +have_formatting_never(const struct inode *inode UNUSED_ARG /* inode to operate on */ , + loff_t size UNUSED_ARG /* new object size */ ) +{ + return 0; +} + +/* Always store file's tail as direct item */ +/* Audited by: green(2002.06.12) */ +static int +have_formatting_always(const struct inode *inode UNUSED_ARG /* inode to operate on */ , + loff_t size UNUSED_ARG /* new object size */ ) +{ + return 1; +} + +/* This function makes test if we should store file denoted @inode as tails only or + as extents only. */ +static int +have_formatting_default(const struct inode *inode UNUSED_ARG /* inode to operate on */ , + loff_t size /* new object size */ ) +{ + assert("umka-1253", inode != NULL); + + if (size > inode->i_sb->s_blocksize * 4) + return 0; + + return 1; +} + +/* tail plugins */ +formatting_plugin formatting_plugins[LAST_TAIL_FORMATTING_ID] = { + [NEVER_TAILS_FORMATTING_ID] = { + .h = { + .type_id = REISER4_FORMATTING_PLUGIN_TYPE, + .id = NEVER_TAILS_FORMATTING_ID, + .pops = NULL, + .label = "never", + .desc = "Never store file's tail", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .have_tail = have_formatting_never + }, + [ALWAYS_TAILS_FORMATTING_ID] = { + .h = { + .type_id = REISER4_FORMATTING_PLUGIN_TYPE, + .id = ALWAYS_TAILS_FORMATTING_ID, + .pops = NULL, + .label = "always", + .desc = "Always store file's tail", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .have_tail = have_formatting_always + }, + [SMALL_FILE_FORMATTING_ID] = { + .h = { + .type_id = REISER4_FORMATTING_PLUGIN_TYPE, + .id = SMALL_FILE_FORMATTING_ID, + .pops = NULL, + .label = "4blocks", + .desc = "store files shorter than 4 blocks in tail items", + .linkage = TYPE_SAFE_LIST_LINK_ZERO + }, + .have_tail = have_formatting_default + } +}; + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/pool.c 2004-09-03 00:57:05.348226208 -0700 @@ -0,0 +1,232 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Fast pool allocation. + + There are situations when some sub-system normally asks memory allocator + for only few objects, but under some circumstances could require much + more. Typical and actually motivating example is tree balancing. It needs + to keep track of nodes that were involved into it, and it is well-known + that in reasonable packed balanced tree most (92.938121%) percent of all + balancings end up after working with only few nodes (3.141592 on + average). But in rare cases balancing can involve much more nodes + (3*tree_height+1 in extremal situation). + + On the one hand, we don't want to resort to dynamic allocation (slab, + malloc(), etc.) to allocate data structures required to keep track of + nodes during balancing. On the other hand, we cannot statically allocate + required amount of space on the stack, because first: it is useless wastage + of precious resource, and second: this amount is unknown in advance (tree + height can change). + + Pools, implemented in this file are solution for this problem: + + - some configurable amount of objects is statically preallocated on the + stack + + - if this preallocated pool is exhausted and more objects is requested + they are allocated dynamically. + + Pools encapsulate distinction between statically and dynamically allocated + objects. Both allocation and recycling look exactly the same. + + To keep track of dynamically allocated objects, pool adds its own linkage + to each object. + + NOTE-NIKITA This linkage also contains some balancing-specific data. This + is not perfect. On the other hand, balancing is currently the only client + of pool code. + + NOTE-NIKITA Another desirable feature is to rewrite all pool manipulation + functions in the style of tslist/tshash, i.e., make them unreadable, but + type-safe. + + +*/ + +#include "debug.h" +#include "pool.h" +#include "super.h" + +#include +#include + +/* initialise new pool object */ +static void +reiser4_init_pool_obj(reiser4_pool_header * h /* pool object to + * initialise */ ) +{ + pool_usage_list_clean(h); + pool_level_list_clean(h); + pool_extra_list_clean(h); +} + +/* initialise new pool */ +reiser4_internal void +reiser4_init_pool(reiser4_pool * pool /* pool to initialise */ , + size_t obj_size /* size of objects in @pool */ , + int num_of_objs /* number of preallocated objects */ , + char *data /* area for preallocated objects */ ) +{ + reiser4_pool_header *h; + int i; + + assert("nikita-955", pool != NULL); + assert("nikita-1044", obj_size > 0); + assert("nikita-956", num_of_objs >= 0); + assert("nikita-957", data != NULL); + + xmemset(pool, 0, sizeof *pool); + pool->obj_size = obj_size; + pool->data = data; + pool_usage_list_init(&pool->free); + pool_usage_list_init(&pool->used); + pool_extra_list_init(&pool->extra); + xmemset(data, 0, obj_size * num_of_objs); + for (i = 0; i < num_of_objs; ++i) { + h = (reiser4_pool_header *) (data + i * obj_size); + reiser4_init_pool_obj(h); + pool_usage_list_push_back(&pool->free, h); + } +} + +/* release pool resources + + Release all resources acquired by this pool, specifically, dynamically + allocated objects. + +*/ +reiser4_internal void +reiser4_done_pool(reiser4_pool * pool UNUSED_ARG /* pool to destroy */ ) +{ +} + +/* allocate carry object from pool + + First, try to get preallocated object. If this fails, resort to dynamic + allocation. + +*/ +reiser4_internal void * +reiser4_pool_alloc(reiser4_pool * pool /* pool to allocate object + * from */ ) +{ + reiser4_pool_header *result; + + assert("nikita-959", pool != NULL); + trace_stamp(TRACE_CARRY); + reiser4_stat_inc(pool.alloc); + + if (!pool_usage_list_empty(&pool->free)) { + result = pool_usage_list_pop_front(&pool->free); + pool_usage_list_clean(result); + assert("nikita-965", pool_extra_list_is_clean(result)); + } else { + reiser4_stat_inc(pool.kmalloc); + /* pool is empty. Extra allocations don't deserve dedicated + slab to be served from, as they are expected to be rare. */ + result = reiser4_kmalloc(pool->obj_size, GFP_KERNEL); + if (result != 0) { + reiser4_init_pool_obj(result); + pool_extra_list_push_front(&pool->extra, result); + } else + return ERR_PTR(RETERR(-ENOMEM)); + } + ++pool->objs; + pool_level_list_clean(result); + pool_usage_list_push_front(&pool->used, result); + xmemset(result + 1, 0, pool->obj_size - sizeof *result); + return result; +} + +/* return object back to the pool */ +reiser4_internal void +reiser4_pool_free(reiser4_pool * pool, + reiser4_pool_header * h /* pool to return object back + * into */ ) +{ + assert("nikita-961", h != NULL); + assert("nikita-962", pool != NULL); + trace_stamp(TRACE_CARRY); + + -- pool->objs; + assert("nikita-963", pool->objs >= 0); + + pool_usage_list_remove_clean(h); + pool_level_list_remove_clean(h); + if (pool_extra_list_is_clean(h)) + pool_usage_list_push_front(&pool->free, h); + else { + pool_extra_list_remove_clean(h); + reiser4_kfree(h); + } +} + +/* add new object to the carry level list + + Carry level is FIFO most of the time, but not always. Complications arise + when make_space() function tries to go to the left neighbor and thus adds + carry node before existing nodes, and also, when updating delimiting keys + after moving data between two nodes, we want left node to be locked before + right node. + + Latter case is confusing at the first glance. Problem is that COP_UPDATE + opration that updates delimiting keys is sometimes called with two nodes + (when data are moved between two nodes) and sometimes with only one node + (when leftmost item is deleted in a node). In any case operation is + supplied with at least node whose left delimiting key is to be updated + (that is "right" node). + +*/ +reiser4_internal reiser4_pool_header * +add_obj(reiser4_pool * pool /* pool from which to + * allocate new object */ , + pool_level_list_head * list /* list where to add + * object */ , + pool_ordering order /* where to add */ , + reiser4_pool_header * reference /* after (or + * before) which + * existing + * object to + * add */ ) +{ + reiser4_pool_header *result; + + assert("nikita-972", pool != NULL); + + trace_stamp(TRACE_CARRY); + + result = reiser4_pool_alloc(pool); + if (IS_ERR(result)) + return result; + + assert("nikita-973", result != NULL); + + switch (order) { + case POOLO_BEFORE: + pool_level_list_insert_before(reference, result); + break; + case POOLO_AFTER: + pool_level_list_insert_after(reference, result); + break; + case POOLO_LAST: + pool_level_list_push_back(list, result); + break; + case POOLO_FIRST: + pool_level_list_push_front(list, result); + break; + default: + wrong_return_value("nikita-927", "order"); + } + return result; +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/pool.h 2004-09-03 00:57:05.348226208 -0700 @@ -0,0 +1,70 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Fast pool allocation */ + +#ifndef __REISER4_POOL_H__ +#define __REISER4_POOL_H__ + +#include "type_safe_list.h" +#include + +/* each pool object is either on a "used" or "free" list. */ +TYPE_SAFE_LIST_DECLARE(pool_usage); + +/* list of extra pool objects */ +TYPE_SAFE_LIST_DECLARE(pool_extra); + +/* list of pool objects on a given level */ +TYPE_SAFE_LIST_DECLARE(pool_level); + +typedef struct reiser4_pool { + size_t obj_size; + int objs; + char *data; + pool_usage_list_head free; + pool_usage_list_head used; + pool_extra_list_head extra; +} reiser4_pool; + +typedef struct reiser4_pool_header { + /* object is either on free or "used" lists */ + pool_usage_list_link usage_linkage; + pool_level_list_link level_linkage; + pool_extra_list_link extra_linkage; +} reiser4_pool_header; + +typedef enum { + POOLO_BEFORE, + POOLO_AFTER, + POOLO_LAST, + POOLO_FIRST +} pool_ordering; + +/* each pool object is either on a "used" or "free" list. */ +TYPE_SAFE_LIST_DEFINE(pool_usage, reiser4_pool_header, usage_linkage); +/* list of extra pool objects */ +TYPE_SAFE_LIST_DEFINE(pool_extra, reiser4_pool_header, extra_linkage); +/* list of pool objects on a given level */ +TYPE_SAFE_LIST_DEFINE(pool_level, reiser4_pool_header, level_linkage); + +/* pool manipulation functions */ + +extern void reiser4_init_pool(reiser4_pool * pool, size_t obj_size, int num_of_objs, char *data); +extern void reiser4_done_pool(reiser4_pool * pool); +extern void *reiser4_pool_alloc(reiser4_pool * pool); +extern void reiser4_pool_free(reiser4_pool * pool, reiser4_pool_header * h); +reiser4_pool_header *add_obj(reiser4_pool * pool, pool_level_list_head * list, + pool_ordering order, reiser4_pool_header * reference); + +/* __REISER4_POOL_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/prof.c 2004-09-03 00:57:05.350225904 -0700 @@ -0,0 +1,273 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* profiling facilities. */ + +/* + * This code is used to collect statistics about how many times particular + * function (or part of function) was called, and how long average call + * took. In addition (or, in the first place, depending on one's needs), it + * also keep track of through what call-chain profiled piece of code was + * entered. Latter is done by having a list of call-chains. Call-chains are + * obtained by series of calls to __builtin_return_address() (hence, this + * functionality requires kernel to be compiled with frame pointers). Whenever + * profiled region is just about to be left, call-chain is constructed and + * then compared against all chains already in the list. If match is found + * (cache hit!), its statistics are updated, otherwise (cache miss), entry + * with smallest hit count is selected and re-used to new call-chain. + * + * NOTE: this replacement policy has obvious deficiencies: after some time + * entries in the list accumulate high hit counts and will effectively prevent + * any new call-chain from finding a place in the list, even is this + * call-chain is frequently activated. Probably LRU should be used instead + * (this is not that hard, /proc//sleep patch does this), but nobody + * complained so far. + * + */ + + +#include "kattr.h" +#include "reiser4.h" +#include "context.h" +#include "super.h" +#include "prof.h" + +#include +#include +#include +#include +#include +#include + +#if REISER4_PROF + +#ifdef CONFIG_FRAME_POINTER +static void +update_prof_trace(reiser4_prof_cnt *cnt, int depth, int shift) +{ + int i; + int minind; + __u64 minhit; + unsigned long hash; + backtrace_path bt; + + fill_backtrace(&bt, depth, shift); + + for (i = 0, hash = 0 ; i < REISER4_BACKTRACE_DEPTH ; ++ i) { + hash += (unsigned long)bt.trace[i]; + } + minhit = ~0ull; + minind = 0; + for (i = 0 ; i < REISER4_PROF_TRACE_NUM ; ++ i) { + if (hash == cnt->bt[i].hash) { + ++ cnt->bt[i].hits; + return; + } + if (cnt->bt[i].hits < minhit) { + minhit = cnt->bt[i].hits; + minind = i; + } + } + cnt->bt[minind].path = bt; + cnt->bt[minind].hash = hash; + cnt->bt[minind].hits = 1; +} +#else +#define update_prof_trace(cnt, depth, shift) noop +#endif + +void update_prof_cnt(reiser4_prof_cnt *cnt, __u64 then, __u64 now, + unsigned long swtch_mark, __u64 start_jif, + int depth, int shift) +{ + __u64 delta; + + delta = now - then; + cnt->nr ++; + cnt->total += delta; + cnt->max = max(cnt->max, delta); + if (swtch_mark == nr_context_switches()) { + cnt->noswtch_nr ++; + cnt->noswtch_total += delta; + cnt->noswtch_max = max(cnt->noswtch_max, delta); + } + update_prof_trace(cnt, depth, shift); +} + +struct prof_attr_entry { + struct attribute attr; + char name[10]; +}; + +static struct prof_attr_entry prof_attr[REISER4_PROF_TRACE_NUM]; + +static ssize_t +show_prof_attr(struct kobject *kobj, struct attribute *attr, char *buf) +{ + char *p; + reiser4_prof_entry *entry; + reiser4_prof_cnt *val; +#ifdef CONFIG_FRAME_POINTER + int pos; + int j; + + pos = ((struct prof_attr_entry *)attr) - prof_attr; +#endif + entry = container_of(kobj, reiser4_prof_entry, kobj); + val = &entry->cnt; + p = buf; + KATTR_PRINT(p, buf, "%llu %llu %llu %llu %llu %llu\n", + val->nr, val->total, val->max, + val->noswtch_nr, val->noswtch_total, val->noswtch_max); +#ifdef CONFIG_FRAME_POINTER + if (val->bt[pos].hash != 0) { + KATTR_PRINT(p, buf, "\t%llu: ", val->bt[pos].hits); + for (j = 0 ; j < REISER4_BACKTRACE_DEPTH ; ++ j) { + char *module; + const char *name; + char namebuf[128]; + unsigned long address; + unsigned long offset; + unsigned long size; + + address = (unsigned long) val->bt[pos].path.trace[j]; + name = kallsyms_lookup(address, &size, + &offset, &module, namebuf); + KATTR_PRINT(p, buf, "\n\t\t%#lx ", address); + if (name != NULL) + KATTR_PRINT(p, buf, "%s+%#lx/%#lx", + name, offset, size); + } + KATTR_PRINT(p, buf, "\n"); + } +#endif + return (p - buf); +} + +/* zero a prof entry corresponding to @attr */ +static ssize_t +store_prof_attr(struct kobject *kobj, struct attribute *attr, const char *buf, size_t size) +{ + reiser4_prof_entry *entry; + + entry = container_of(kobj, reiser4_prof_entry, kobj); + memset(&entry->cnt, 0, sizeof(reiser4_prof_cnt)); + return sizeof(reiser4_prof_cnt); +} + +static struct sysfs_ops prof_attr_ops = { + .show = show_prof_attr, + .store = store_prof_attr +}; + +static struct kobj_type ktype_reiser4_prof = { + .sysfs_ops = &prof_attr_ops, + .default_attrs = NULL +}; + +static decl_subsys(prof, &ktype_reiser4_prof, NULL); + +static struct kobject cpu_prof; + +#define DEFINE_PROF_ENTRY_0(attr_name,field_name) \ + .field_name = { \ + .kobj = { \ + .name = attr_name \ + } \ + } + + +#define DEFINE_PROF_ENTRY(name) \ + DEFINE_PROF_ENTRY_0(#name,name) + +reiser4_prof reiser4_prof_defs = { + DEFINE_PROF_ENTRY(fuse_wait), +#if 0 + DEFINE_PROF_ENTRY(cbk), + DEFINE_PROF_ENTRY(init_context), + DEFINE_PROF_ENTRY(jlook), + DEFINE_PROF_ENTRY(writepage), + DEFINE_PROF_ENTRY(jload), + DEFINE_PROF_ENTRY(jrelse), + DEFINE_PROF_ENTRY(flush_alloc), + DEFINE_PROF_ENTRY(forward_squalloc), + DEFINE_PROF_ENTRY(atom_wait_event), + DEFINE_PROF_ENTRY(zget), + /* write profiling */ + DEFINE_PROF_ENTRY(extent_write), + /* read profiling */ + DEFINE_PROF_ENTRY(file_read) +#endif +}; + +void calibrate_prof(void) +{ + __u64 start; + __u64 end; + + rdtscll(start); + schedule_timeout(HZ/100); + rdtscll(end); + warning("nikita-2923", "1 sec. == %llu rdtsc.", (end - start) * 100); +} + + +int init_prof_kobject(void) +{ + int result; + int i; + reiser4_prof_entry *array; + + for (i = 0; i < REISER4_PROF_TRACE_NUM; ++ i) { + sprintf(prof_attr[i].name, "%i", i); + prof_attr[i].attr.name = prof_attr[i].name; + prof_attr[i].attr.mode = 0644; + } + + result = subsystem_register(&prof_subsys); + if (result != 0) + return result; + + cpu_prof.kset = &prof_subsys.kset; + snprintf(cpu_prof.name, KOBJ_NAME_LEN, "cpu_prof"); + result = kobject_register(&cpu_prof); + if (result != 0) + return result; + + /* populate */ + array = (reiser4_prof_entry *)&reiser4_prof_defs; + for(i = 0 ; i < sizeof(reiser4_prof_defs)/sizeof(reiser4_prof_entry); + ++ i) { + struct kobject *kobj; + int j; + + kobj = &array[i].kobj; + kobj->ktype = &ktype_reiser4_prof; + kobj->parent = kobject_get(&cpu_prof); + + result = kobject_register(kobj); + if (result != 0) + break; + + for (j = 0; j < REISER4_PROF_TRACE_NUM; ++ j) { + result = sysfs_create_file(kobj, &prof_attr[j].attr); + if (result != 0) + break; + } + } + if (result != 0) + kobject_unregister(&cpu_prof); + return result; +} + +void done_prof_kobject(void) +{ + kobject_unregister(&cpu_prof); + subsystem_unregister(&prof_subsys); +} + +/* REISER4_PROF */ +#else + +/* REISER4_PROF */ +#endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/prof.h 2004-09-03 00:57:05.350225904 -0700 @@ -0,0 +1,130 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* profiling. This is i386, rdtsc-based profiling. See prof.c for comments. */ + +#if !defined( __REISER4_PROF_H__ ) +#define __REISER4_PROF_H__ + +#include "kattr.h" + +#if (defined(__i386__) || defined(CONFIG_USERMODE)) && defined(CONFIG_REISER4_PROF) +#define REISER4_PROF (1) +#else +#define REISER4_PROF (0) +#endif + +#if REISER4_PROF + +#include + +#define REISER4_PROF_TRACE_NUM (30) + +/* data structure to keep call trace */ +typedef struct { + /* hash of trace---used for fast comparison */ + unsigned long hash; + /* call trace proper---return addresses collected by + * __builtin_return_address() */ + backtrace_path path; + /* number of times profiled code was entered through this call + * chain */ + __u64 hits; +} reiser4_trace; + +/* statistics for profiled region of code */ +typedef struct { + /* number of times region was entered */ + __u64 nr; + /* total time spent in this region */ + __u64 total; + /* maximal time per enter */ + __u64 max; + /* number of times region was executed without context switch */ + __u64 noswtch_nr; + /* total time spent in executions without context switch */ + __u64 noswtch_total; + /* maximal time of execution without context switch */ + __u64 noswtch_max; + /* array of back traces */ + reiser4_trace bt[REISER4_PROF_TRACE_NUM]; +} reiser4_prof_cnt; + +/* profiler entry. */ +typedef struct { + /* sysfs placeholder */ + struct kobject kobj; + /* statistics, see above */ + reiser4_prof_cnt cnt; +} reiser4_prof_entry; + +typedef struct { + reiser4_prof_entry fuse_wait; +#if 0 + reiser4_prof_entry cbk; + reiser4_prof_entry init_context; + reiser4_prof_entry jlook; + reiser4_prof_entry writepage; + reiser4_prof_entry jload; + reiser4_prof_entry jrelse; + reiser4_prof_entry flush_alloc; + reiser4_prof_entry forward_squalloc; + reiser4_prof_entry atom_wait_event; + reiser4_prof_entry zget; + /* write profiling */ + reiser4_prof_entry extent_write; + /* read profiling */ + reiser4_prof_entry file_read; +#endif +} reiser4_prof; + +extern reiser4_prof reiser4_prof_defs; + +extern unsigned long nr_context_switches(void); +void update_prof_cnt(reiser4_prof_cnt *cnt, __u64 then, __u64 now, + unsigned long swtch_mark, __u64 start_jif, + int delta, int shift); +void calibrate_prof(void); + +#define PROF_BEGIN(aname) \ + unsigned long __swtch_mark__ ## aname = nr_context_switches(); \ + __u64 __prof_jiffies ## aname = jiffies; \ + __u64 __prof_cnt__ ## aname = ({ __u64 __tmp_prof ; \ + rdtscll(__tmp_prof) ; __tmp_prof; }) + +#define PROF_END(aname) __PROF_END(aname, REISER4_BACKTRACE_DEPTH, 0) + +#define __PROF_END(aname, depth, shift) \ +({ \ + __u64 __prof_end; \ + \ + rdtscll(__prof_end); \ + update_prof_cnt(&reiser4_prof_defs.aname.cnt, \ + __prof_cnt__ ## aname, \ + __prof_end, \ + __swtch_mark__ ## aname, \ + __prof_jiffies ## aname, \ + depth, shift ); \ +}) + +extern int init_prof_kobject(void); +extern void done_prof_kobject(void); + +/* REISER4_PROF */ +#else + +typedef struct reiser4_prof_cnt {} reiser4_prof_cnt; +typedef struct reiser4_prof {} reiser4_prof; + +#define PROF_BEGIN(aname) noop +#define PROF_END(aname) noop +#define __PROF_END(aname, depth, shift) noop +#define calibrate_prof() noop + +#define init_prof_kobject() (0) +#define done_prof_kobject() noop + +#endif + +/* __REISER4_PROF_H__ */ +#endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/readahead.c 2004-09-03 00:57:05.352225600 -0700 @@ -0,0 +1,382 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +#include "forward.h" +#include "tree.h" +#include "tree_walk.h" +#include "super.h" +#include "inode.h" +#include "key.h" +#include "znode.h" + +#include /* for totalram_pages */ + +reiser4_internal void init_ra_info(ra_info_t * rai) +{ + rai->key_to_stop = *min_key(); +} + +/* global formatted node readahead parameter. It can be set by mount option -o readahead:NUM:1 */ +static inline int ra_adjacent_only(int flags) +{ + return flags & RA_ADJACENT_ONLY; +} + +/* this is used by formatted_readahead to decide whether read for right neighbor of node is to be issued. It returns 1 + if right neighbor's first key is less or equal to readahead's stop key */ +static int +should_readahead_neighbor(znode *node, ra_info_t *info) +{ + return (UNDER_RW(dk, ZJNODE(node)->tree, read, + keyle(znode_get_rd_key(node), &info->key_to_stop))); +} + +#define LOW_MEM_PERCENTAGE (5) + +static int +low_on_memory(void) +{ + unsigned int freepages; + + freepages = nr_free_pages(); + return freepages < (totalram_pages * LOW_MEM_PERCENTAGE / 100); +} + +/* start read for @node and for a few of its right neighbors */ +reiser4_internal void +formatted_readahead(znode *node, ra_info_t *info) +{ + ra_params_t *ra_params; + znode *cur; + int i; + int grn_flags; + lock_handle next_lh; + + /* do nothing if node block number has not been assigned to node (which means it is still in cache). */ + if (blocknr_is_fake(znode_get_block(node))) + return; + + ra_params = get_current_super_ra_params(); + + if (znode_page(node) == NULL) + jstartio(ZJNODE(node)); + + if (znode_get_level(node) != LEAF_LEVEL) + return; + + /* don't waste memory for read-ahead when low on memory */ + if (low_on_memory()) + return; + + write_current_logf(READAHEAD_LOG, "...readahead\n"); + + /* We can have locked nodes on upper tree levels, in this situation lock + priorities do not help to resolve deadlocks, we have to use TRY_LOCK + here. */ + grn_flags = (GN_CAN_USE_UPPER_LEVELS | GN_TRY_LOCK); + + i = 0; + cur = zref(node); + init_lh(&next_lh); + while (i < ra_params->max) { + const reiser4_block_nr *nextblk; + + if (!should_readahead_neighbor(cur, info)) + break; + + if (reiser4_get_right_neighbor(&next_lh, cur, ZNODE_READ_LOCK, grn_flags)) + break; + + if (JF_ISSET(ZJNODE(next_lh.node), JNODE_EFLUSH)) { + /* emergency flushed znode is encountered. That means we are low on memory. Do not readahead + then */ + break; + } + + nextblk = znode_get_block(next_lh.node); + if (blocknr_is_fake(nextblk) || + (ra_adjacent_only(ra_params->flags) && *nextblk != *znode_get_block(cur) + 1)) { + break; + } + + zput(cur); + cur = zref(next_lh.node); + done_lh(&next_lh); + if (znode_page(cur) == NULL) + jstartio(ZJNODE(cur)); + else + /* Do not scan read-ahead window if pages already + * allocated (and i/o already started). */ + break; + + i ++; + } + zput(cur); + done_lh(&next_lh); + + write_current_logf(READAHEAD_LOG, "...readahead exits\n"); +} + +static inline loff_t get_max_readahead(struct reiser4_file_ra_state *ra) +{ + /* NOTE: ra->max_window_size is initialized in + * reiser4_get_file_fsdata(). */ + return ra->max_window_size; +} + +static inline loff_t get_min_readahead(struct reiser4_file_ra_state *ra) +{ + return VM_MIN_READAHEAD * 1024; +} + + +/* Start read for the given window. */ +static loff_t do_reiser4_file_readahead (struct inode * inode, loff_t offset, loff_t size) +{ + reiser4_tree * tree = current_tree; + reiser4_inode * object; + reiser4_key start_key; + reiser4_key stop_key; + + lock_handle lock; + lock_handle next_lock; + + coord_t coord; + tap_t tap; + + loff_t result; + + assert("zam-994", lock_stack_isclean(get_current_lock_stack())); + + object = reiser4_inode_data(inode); + key_by_inode_unix_file(inode, offset, &start_key); + key_by_inode_unix_file(inode, offset + size, &stop_key); + + init_lh(&lock); + init_lh(&next_lock); + + /* Stop on twig level */ + result = coord_by_key( + current_tree, &start_key, &coord, &lock, ZNODE_READ_LOCK, + FIND_EXACT, TWIG_LEVEL, TWIG_LEVEL, 0, NULL); + if (result < 0) + goto error; + if (result != CBK_COORD_FOUND) { + result = 0; + goto error; + } + + tap_init(&tap, &coord, &lock, ZNODE_WRITE_LOCK); + result = tap_load(&tap); + if (result) + goto error0; + + /* Advance coord to right (even across node boundaries) while coord key + * less than stop_key. */ + while (1) { + reiser4_key key; + znode * child; + reiser4_block_nr blk; + + /* Currently this read-ahead is for formatted nodes only */ + if (!item_is_internal(&coord)) + break; + + item_key_by_coord(&coord, &key); + if (keyge(&key, &stop_key)) + break; + + result = item_utmost_child_real_block(&coord, LEFT_SIDE, &blk); + if (result || blk == 0) + break; + + child = zget(tree, &blk, lock.node, LEAF_LEVEL, GFP_KERNEL); + + if (IS_ERR(child)) { + result = PTR_ERR(child); + break; + } + + /* If znode's page is present that usually means that i/o was + * already started for the page. */ + if (znode_page(child) == NULL) { + result = jstartio(ZJNODE(child)); + if (result) { + zput(child); + break; + } + } + zput(child); + + /* Advance coord by one unit ... */ + result = coord_next_unit(&coord); + if (result == 0) + continue; + + /* ... and continue on the right neighbor if needed. */ + result = reiser4_get_right_neighbor ( + &next_lock, lock.node, ZNODE_READ_LOCK, + GN_CAN_USE_UPPER_LEVELS); + if (result) + break; + + if (znode_page(next_lock.node) == NULL) { + loff_t end_offset; + + result = jstartio(ZJNODE(next_lock.node)); + if (result) + break; + + read_lock_dk(tree); + end_offset = get_key_offset(znode_get_ld_key(next_lock.node)); + read_unlock_dk(tree); + + result = end_offset - offset; + break; + } + + result = tap_move(&tap, &next_lock); + if (result) + break; + + done_lh(&next_lock); + coord_init_first_unit(&coord, lock.node); + } + + if (! result || result == -E_NO_NEIGHBOR) + result = size; + error0: + tap_done(&tap); + error: + done_lh(&lock); + done_lh(&next_lock); + return result; +} + +typedef unsigned long long int ull_t; +#define PRINTK(...) noop +/* This is derived from the linux original read-ahead code (mm/readahead.c), and + * cannot be licensed from Namesys in its current state. */ +int reiser4_file_readahead (struct file * file, loff_t offset, size_t size) +{ + loff_t min; + loff_t max; + loff_t orig_next_size; + loff_t actual; + struct reiser4_file_ra_state * ra; + struct inode * inode = file->f_dentry->d_inode; + + assert ("zam-995", inode != NULL); + + PRINTK ("R/A REQ: off=%llu, size=%llu\n", (ull_t)offset, (ull_t)size); + ra = &reiser4_get_file_fsdata(file)->ra; + + max = get_max_readahead(ra); + if (max == 0) + goto out; + + min = get_min_readahead(ra); + orig_next_size = ra->next_size; + + if (!ra->slow_start) { + ra->slow_start = 1; + /* + * Special case - first read from first page. + * We'll assume it's a whole-file read, and + * grow the window fast. + */ + ra->next_size = max / 2; + goto do_io; + + } + + /* + * Is this request outside the current window? + */ + if (offset < ra->start || offset > (ra->start + ra->size)) { + /* R/A miss. */ + + /* next r/a window size is shrunk by fixed offset and enlarged + * by 2 * size of read request. This makes r/a window smaller + * for small unordered requests and larger for big read + * requests. */ + ra->next_size += -2 * PAGE_CACHE_SIZE + 2 * size ; + if (ra->next_size < 0) + ra->next_size = 0; +do_io: + ra->start = offset; + ra->size = size + orig_next_size; + actual = do_reiser4_file_readahead(inode, offset, ra->size); + if (actual > 0) + ra->size = actual; + + ra->ahead_start = ra->start + ra->size; + ra->ahead_size = ra->next_size; + + actual = do_reiser4_file_readahead(inode, ra->ahead_start, ra->ahead_size); + if (actual > 0) + ra->ahead_size = actual; + + PRINTK ("R/A MISS: cur = [%llu, +%llu[, ahead = [%llu, +%llu[\n", + (ull_t)ra->start, (ull_t)ra->size, + (ull_t)ra->ahead_start, (ull_t)ra->ahead_size); + } else { + /* R/A hit. */ + + /* Enlarge r/a window size. */ + ra->next_size += 2 * size; + if (ra->next_size > max) + ra->next_size = max; + + PRINTK("R/A HIT\n"); + while (offset + size >= ra->ahead_start) { + ra->start = ra->ahead_start; + ra->size = ra->ahead_size; + + ra->ahead_start = ra->start + ra->size; + ra->ahead_size = ra->next_size; + + actual = do_reiser4_file_readahead( + inode, ra->ahead_start, ra->ahead_size); + if (actual > 0) { + ra->ahead_size = actual; + } + + PRINTK ("R/A ADVANCE: cur = [%llu, +%llu[, ahead = [%llu, +%llu[\n", + (ull_t)ra->start, (ull_t)ra->size, + (ull_t)ra->ahead_start, (ull_t)ra->ahead_size); + + } + } + +out: + return 0; +} + +reiser4_internal void +reiser4_readdir_readahead_init(struct inode *dir, tap_t *tap) +{ + reiser4_key *stop_key; + + assert("nikita-3542", dir != NULL); + assert("nikita-3543", tap != NULL); + + stop_key = &tap->ra_info.key_to_stop; + /* initialize readdir readahead information: include into readahead + * stat data of all files of the directory */ + set_key_locality(stop_key, get_inode_oid(dir)); + set_key_type(stop_key, KEY_SD_MINOR); + set_key_ordering(stop_key, get_key_ordering(max_key())); + set_key_objectid(stop_key, get_key_objectid(max_key())); + set_key_offset(stop_key, get_key_offset(max_key())); +} + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 80 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/readahead.h 2004-09-03 00:57:05.353225448 -0700 @@ -0,0 +1,50 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#ifndef __READAHEAD_H__ +#define __READAHEAD_H__ + +#include "key.h" + +typedef enum { + RA_ADJACENT_ONLY = 1, /* only requests nodes which are adjacent. Default is NO (not only adjacent) */ +} ra_global_flags; + +/* reiser4 super block has a field of this type. It controls readahead during tree traversals */ +typedef struct formatted_read_ahead_params { + unsigned long max; /* request not more than this amount of nodes. Default is totalram_pages / 4 */ + int flags; +} ra_params_t; + + +typedef struct { + reiser4_key key_to_stop; +} ra_info_t; + +void formatted_readahead(znode *, ra_info_t *); +void init_ra_info(ra_info_t * rai); + +struct reiser4_file_ra_state { + loff_t start; /* Current window */ + loff_t size; + loff_t next_size; /* Next window size */ + loff_t ahead_start; /* Ahead window */ + loff_t ahead_size; + loff_t max_window_size; /* Maximum readahead window */ + loff_t slow_start; /* enlarging r/a size algorithm. */ +}; + +extern int reiser4_file_readahead(struct file *, loff_t, size_t); +extern void reiser4_readdir_readahead_init(struct inode *dir, tap_t *tap); + +/* __READAHEAD_H__ */ +#endif + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/README 2004-09-03 00:57:05.354225296 -0700 @@ -0,0 +1,125 @@ +[LICENSING] + +Reiser4 is hereby licensed under the GNU General +Public License version 2. + +Source code files that contain the phrase "licensing governed by +reiser4/README" are "governed files" throughout this file. Governed +files are licensed under the GPL. The portions of them owned by Hans +Reiser, or authorized to be licensed by him, have been in the past, +and likely will be in the future, licensed to other parties under +other licenses. If you add your code to governed files, and don't +want it to be owned by Hans Reiser, put your copyright label on that +code so the poor blight and his customers can keep things straight. +All portions of governed files not labeled otherwise are owned by Hans +Reiser, and by adding your code to it, widely distributing it to +others or sending us a patch, and leaving the sentence in stating that +licensing is governed by the statement in this file, you accept this. +It will be a kindness if you identify whether Hans Reiser is allowed +to license code labeled as owned by you on your behalf other than +under the GPL, because he wants to know if it is okay to do so and put +a check in the mail to you (for non-trivial improvements) when he +makes his next sale. He makes no guarantees as to the amount if any, +though he feels motivated to motivate contributors, and you can surely +discuss this with him before or after contributing. You have the +right to decline to allow him to license your code contribution other +than under the GPL. + +Further licensing options are available for commercial and/or other +interests directly from Hans Reiser: reiser@namesys.com. If you interpret +the GPL as not allowing those additional licensing options, you read +it wrongly, and Richard Stallman agrees with me, when carefully read +you can see that those restrictions on additional terms do not apply +to the owner of the copyright, and my interpretation of this shall +govern for this license. + +[END LICENSING] + +Reiser4 is a file system based on dancing tree algorithms, and is +described at http://www.namesys.com + +mkfs.reiser4 and other utilities are on our webpage or wherever your +Linux provider put them. You really want to be running the latest +version off the website if you use fsck. + +Yes, if you update your reiser4 kernel module you do have to +recompile your kernel, most of the time. The errors you get will be +quite cryptic if your forget to do so. + +Hideous Commercial Pitch: Spread your development costs across other OS +vendors. Select from the best in the world, not the best in your +building, by buying from third party OS component suppliers. Leverage +the software component development power of the internet. Be the most +aggressive in taking advantage of the commercial possibilities of +decentralized internet development, and add value through your branded +integration that you sell as an operating system. Let your competitors +be the ones to compete against the entire internet by themselves. Be +hip, get with the new economic trend, before your competitors do. Send +email to reiser@namesys.com + +Hans Reiser was the primary architect of Reiser4, but a whole team +chipped their ideas in. He invested everything he had into Namesys +for 5.5 dark years of no money before Reiser3 finally started to work well +enough to bring in money. He owns the copyright. + +DARPA was the primary sponsor of Reiser4. DARPA does not endorse +Reiser4, it merely sponsors it. DARPA is, in solely Hans's personal +opinion, unique in its willingness to invest into things more +theoretical than the VC community can readily understand, and more +longterm than allows them to be sure that they will be the ones to +extract the economic benefits from. DARPA also integrated us into a +security community that transformed our security worldview. + +Vladimir Saveliev is our lead programmer, with us from the beginning, +and he worked long hours writing the cleanest code. This is why he is +now the lead programmer after years of commitment to our work. He +always made the effort to be the best he could be, and to make his +code the best that it could be. What resulted was quite remarkable. I +don't think that money can ever motivate someone to work the way he +did, he is one of the most selfless men I know. + +Alexander Lyamin was our sysadmin, and helped to educate us in +security issues. Moscow State University and IMT were very generous +in the internet access they provided us, and in lots of other little +ways that a generous institution can be. + +Alexander Zarochentcev (sometimes known as zam, or sasha), wrote the +locking code, the block allocator, and finished the flushing code. +His code is always crystal clean and well structured. + +Nikita Danilov wrote the core of the balancing code, the core of the +plugins code, and the directory code. He worked a steady pace of long +hours that produced a whole lot of well abstracted code. He is our +senior computer scientist. + +Vladimir Demidov wrote the parser. Writing an in kernel parser is +something very few persons have the skills for, and it is thanks to +him that we can say that the parser is really not so big compared to +various bits of our other code, and making a parser work in the kernel +was not so complicated as everyone would imagine mainly because it was +him doing it... + +Joshua McDonald wrote the transaction manager, and the flush code. +The flush code unexpectedly turned out be extremely hairy for reasons +you can read about on our web page, and he did a great job on an +extremely difficult task. + +Nina Reiser handled our accounting, government relations, and much +more. + +Ramon Reiser developed our website. + +Beverly Palmer drew our graphics. + +Vitaly Fertman developed librepair, userspace plugins repair code, fsck +and worked with Umka on developing libreiser4 and userspace plugins. + +Yury Umanets (aka Umka) developed libreiser4, userspace plugins and +userspace tools (reiser4progs). + +Oleg Drokin (aka Green) is the release manager who fixes everything. +It is so nice to have someone like that on the team. He (plus Chris +and Jeff) make it possible for the entire rest of the Namesys team to +focus on Reiser4, and he fixed a whole lot of Reiser4 bugs also. It +is just amazing to watch his talent for spotting bugs in action. + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/regression.sh 2004-09-03 00:57:05.354225296 -0700 @@ -0,0 +1,70 @@ +#! /bin/sh + +PROGRAM=./a.out + +printf "REISER4_TRACE_FLAGS: %x\n" $REISER4_TRACE_FLAGS +printf "REISER4_BLOCK_SIZE: %i\n" ${REISER4_BLOCK_SIZE:-256} +echo "REISER4_MOUNT: " $REISER4_MOUNT +echo "REISER4_MOUNT_OPTS: " $REISER4_MOUNT_OPTS + +ROUNDS=${1:-1} + +function run() +{ +# do_mkfs + echo -n $* "..." + ( /usr/bin/time -f " T: %e/%S/%U F: %F/%R" $* >/dev/null ) || exit 1 +# shift +# shift + RNAME=`echo $* | sed 's/[ \/]/./g'`.$r +# echo $RNAME + mv gmon.out gmon.out.$RNAME 2>/dev/null +} + +function do_mkfs() +{ + echo "mkfs $REISER4_MOUNT tail" | ${PROGRAM} sh >/dev/null +} + +export REISER4_PRINT_STATS=1 +export REISER4_CRASH_MODE=suspend +export REISER4_TRAP=1 +#export REISER4_SWAPD=1 + +rm -f gmon.out.* + +#ORDER='000' +ORDER=${2:-''} + +do_mkfs || exit 1 + +if [ -z $REISER4_MOUNT ] +then + echo 'Set $REISER4_MOUNT' + exit 3 +fi + +for r in `seq 1 $ROUNDS` +do +echo Round $r + +run ${PROGRAM} nikita ibk 30${ORDER} +# run ${PROGRAM} jmacd build 3 1000 1000 +run ${PROGRAM} nikita dir 1 100${ORDER} 0 +run ${PROGRAM} nikita dir 1 100${ORDER} 1 +run ${PROGRAM} nikita dir 4 7${ORDER} 0 +run ${PROGRAM} nikita dir 4 7${ORDER} 1 +run ${PROGRAM} nikita mongo 3 20${ORDER} 0 +run ${PROGRAM} nikita mongo 3 20${ORDER} 1 +run ${PROGRAM} nikita rm 6 10${ORDER} 0 +run ${PROGRAM} nikita rm 6 10${ORDER} 1 +run ${PROGRAM} nikita unlink 15${ORDER} +#run ${PROGRAM} nikita queue 30 10${ORDER} 10000 +run ${PROGRAM} nikita mongo 30 1${ORDER} 0 +run ${PROGRAM} nikita mongo 30 1${ORDER} 1 +run ${PROGRAM} nikita bobber 100${ORDER} 300 +#run ulevel/cp-r plugin +#( find /tmp | ${PROGRAM} vs copydir ) + +echo Round $r done. +done --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/reiser4.h 2004-09-03 00:57:05.357224840 -0700 @@ -0,0 +1,501 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* definitions of common constants used by reiser4 */ + +#if !defined( __REISER4_H__ ) +#define __REISER4_H__ + +#include +#include /* for HZ */ +#include +#include +#include +#include +#include + +#if defined(CONFIG_4KSTACKS) +#error "Please turn 4k stack off" +#endif + +/* + * reiser4 compilation options. + */ + +#if defined(CONFIG_REISER4_DEBUG) +/* turn on assertion checks */ +#define REISER4_DEBUG (1) +#else +#define REISER4_DEBUG (0) +#endif + +#if defined(CONFIG_REISER4_DEBUG_MODIFY) +/* + * Turn on "znode modification checks". In this mode znode check-sum is + * maintained in special field added to znode. Check-sum is updated during + * znode_make_dirty() (or, during zload()). It is checked that check-sum is + * only if ever updated between acquiring write lock on znode and calling + * znode_make_dirty(). This significantly slows down testing, but we should + * run our test-suite through with this every once in a while. */ +#define REISER4_DEBUG_MODIFY (1) +#else +#define REISER4_DEBUG_MODIFY (0) +#endif + +#if defined(CONFIG_REISER4_DEBUG_MEMCPY) +/* Provide our own memcpy/memmove to profile shifts. Reiser4 code uses + * xmem{cpy,move,set}() functions in stead of mem{cpy,move,set}(). When + * REISER4_DEBUG_MEMCPY is on, care is taken to uninline xmem* functions so + * that they show up in CPU profiling (/proc/profile, for example) separately + * from calling functions. This is done to estimate CPU consumption of memory + * shifts. When this mode is off, xmem* functions are preprocessed into their + * mem* analogs. */ +#define REISER4_DEBUG_MEMCPY (1) +#else +#define REISER4_DEBUG_MEMCPY (0) +#endif + +#if defined(CONFIG_REISER4_DEBUG_NODE) +/* + * Check consistency of internal node structures. When this mode is on, node + * consistency check (implemented by plugin/node/node.c:node_check() function) + * are invoked in many places (including start and end of most node plugin + * methods). node_check() includes a lot of checks, see it for details. + * + * Node consistency checking (which is off by default) has to be activated by + * setting REISER4_CHECK_NODE bit in ->debug_flags field of + * reiser4_super_info_data. This can be done with debug_flags mount option. + */ +#define REISER4_DEBUG_NODE (1) +#else +#define REISER4_DEBUG_NODE (0) +#endif + +#if defined(CONFIG_REISER4_ZERO_NEW_NODE) +/* If this is non-zero, clear content of new node, otherwise leave whatever + may happen to be here */ +#define REISER4_ZERO_NEW_NODE (1) +#else +#define REISER4_ZERO_NEW_NODE (0) +#endif + +#if defined(CONFIG_REISER4_TRACE) +/* tracing facility. When this is on, {ON,IF}_TRACE statements are + * activated. Thy print (through printk()) information about control flow + * (what function is called, with what arguments, etc.). {ON,IF}_TRACE + * statements contain "trace mask" and output is done only when this mask + * matches current trace mask, calculated by get_current_trace_flags() + * function. Current trace mask is combined from per-thread context mask + * (stored in reiser4_context), and per-super-block mask (stored in + * ->trace_flags field of reiser4_super_info_data). Per-super-block trace mask + * can be adjusted through: + * + * 1. mount option "trace_flags" + * + * 2. /sys/fs/reiser4//trace_flags file. + * + */ +#define REISER4_TRACE (1) +#else +#define REISER4_TRACE (0) +#endif + +#if defined(CONFIG_REISER4_EVENT_LOG) +/* + * Collect event logs. When this is on, logging macros/functions declared in + * fs/reiser4/log.h are activated. Event-logging facility is designed to cope + * with large amount of output data. To this end, event descriptions are + * buffered in the internal buffer (of REISER4_TRACE_BUF_SIZE bytes) and then + * written into user-visible log file. Log file is specified through log_file + * mount option. + * + * Events which are logged are specified through log_flags mount option (or + * /sys/fs/reiser4//log_flags file). See + * fs/reiser4/debug.h:reiser4_log_flags for possible values. + * + * Note that event-logging is for gathering statistics (as opposed to tracing, + * which is for debugging). + * + * When running experiments with event-logging on, it's important to minimize + * an impact of event-logging to the system. It was found that one of the most + * disturbing effects of event-logging is continuous generation of dirty + * memory that triggers premature write-back and, generally, affects system + * behavior in various ways. To work around this set log file to named pipe, + * and use netcat(1) to dump log through over network. + * + */ +#define REISER4_LOG (1) +#else +#define REISER4_LOG (0) +#endif + +#if defined(CONFIG_REISER4_STATS) +/* + * Collect statistics. In this mode reiser4 collects a lot of statistical + * information in the form if "stat-counters". There are global counters + * (per-super-block) and per-level counters collected separately for each + * level of the internal reiser4 tree. See fs/reiser4/stats.[ch] for the list + * of counters. Counters are exported under /sys/fs/reiser4//stats/ + * + * Note: this option consumes quite a bit of kernel memory. + */ +#define REISER4_STATS (1) +#else +#define REISER4_STATS (0) +#endif + +#if defined(CONFIG_REISER4_DEBUG_OUTPUT) +/* + * In this mode various "debugging output" functions are compiled in. These + * functions output human readable representation of various reiser4 kernel + * data-structures (keys, tree nodes, items, etc.), which are used in error + * messages. + */ +#define REISER4_DEBUG_OUTPUT (1) +#else +#define REISER4_DEBUG_OUTPUT (0) +#endif + +#if defined(CONFIG_REISER4_COPY_ON_CAPTURE) +/* + * Turns on copy-on-capture (COC) optimization. See + * http://www.namesys.com/v4/v4.html#cp_on_capture + */ +#define REISER4_COPY_ON_CAPTURE (1) +#else +#define REISER4_COPY_ON_CAPTURE (0) +#endif + +#if defined(CONFIG_REISER4_LOCKPROF) +/* + * Turns on lock profiling mode. In this mode reiser4 spin-locks are + * instrumented to collect information about their contention and + * utilization. See fs/reiser4/spinprof.[ch] for details. + * + * Lock profiling results are exported as /sys/profregion/ + */ +#define REISER4_LOCKPROF (1) +#else +#define REISER4_LOCKPROF (0) +#endif + +#if defined(CONFIG_REISER4_LARGE_KEY) +/* + * Turn on large keys mode. In his mode (which is default), reiser4 key has 4 + * 8-byte components. In the old "small key" mode, it's 3 8-byte + * components. Additional component, referred to as "ordering" is used to + * order items from which given object is composed of. As such, ordering is + * placed between locality and objectid. For directory item ordering contains + * initial prefix of the file name this item is for. This sorts all directory + * items within given directory lexicographically (but see + * fibration.[ch]). For file body and stat-data, ordering contains initial + * prefix of the name file was initially created with. In the common case + * (files with single name) this allows to order file bodies and stat-datas in + * the same order as their respective directory entries, thus speeding up + * readdir. + * + * Note, that kernel can only mount file system with the same key size as one + * it is compiled for, so flipping this option may render your data + * inaccessible. + */ +#define REISER4_LARGE_KEY (1) +#else +#define REISER4_LARGE_KEY (0) +#endif + +#if defined(CONFIG_REISER4_ALL_IN_ONE) +/* + * Turn on all-on-one compilation mode. In this mode reiser4 is compiled as + * one single source file all-in-one.c that includes all other sources. This + * is supposed to result in better code, because compiler is free to perform + * all optimizations within the same compilation unit. To achieve this, + * (almost) all reiser4 functions are prefixed with reiser4_internal + * specifier. In normal compilation mode it expands to nothing, in all-in-one + * mode, it expands to "static", thus telling compiler that function is only + * used in this compilation unit (that is, in whole reiser4). + * + * Note-1: compilation in this mode would result in large number of warnings, + * because header files weren't updated. + * + * Note-2: in addition to generating better code this mode can be used to + * detect declared but not used functions, or declarations without definition. + * + * Note-3: this should be tried with -funit-at-a-time option of gcc 3.4 + */ +#define REISER4_ALL_IN_ONE (1) +#else +#define REISER4_ALL_IN_ONE (0) +#endif + +#if defined (CONFIG_REISER4_DEBUG_NODE_INVARIANT) +/* + * In this mode [zj]node invariants are checked. This mode is not usually on, + * because it consumes a lot of CPU. See [zj]node_invariant() and + * doc/lock-ordering for description of invariants checked. + */ +#define REISER4_DEBUG_NODE_INVARIANT (1) +#else +#define REISER4_DEBUG_NODE_INVARIANT (0) +#endif + +#if defined(CONFIG_REISER4_DEBUG_SPIN_LOCKS) && defined(CONFIG_REISER4_DEBUG) +/* + * Turns on spin-lock debugging. Many (but not all) spin-locks used by reiser4 + * are accessed through special wrapper macros defined in spin_macros.h. These + * macros allow, among other things, to specify for a given spin-lock type its + * "lock ordering predicate" that specifies what other locks may or may not be + * held simultaneously with this one. Spin-lock debugging checks for these + * ordering constraints along with trivial checks for proper lock/unlock + * nesting, etc. Note, that spin_macros.h also support spin-lock profiling + * described above (CONFIG_REISER4_LOCKPROF). + * + * Note: this is not available through fs/Kconfig. Adjust manually. + */ +#define REISER4_DEBUG_SPIN_LOCKS (1) +#else +#define REISER4_DEBUG_SPIN_LOCKS (0) +#endif + +#define CONFIG_REISER4_DEBUG_CONTEXTS y +#if defined(CONFIG_REISER4_DEBUG_CONTEXTS) && defined(CONFIG_REISER4_DEBUG) +/* + * In this mode reiser4_context debugging is activated. reiser4_context is a + * data-structure created on stack at the beginning of reiser4 entry. In this + * mode, list of all "active" contexts is maintained, and periodically + * checked. This is to catch various hard-to-debug bugs like exiting without + * destroying context, or stack overflowing. + * + * Note: this is not available through fs/Kconfig. Adjust manually. + */ +#define REISER4_DEBUG_CONTEXTS (1) +#else +#define REISER4_DEBUG_CONTEXTS (0) +#endif + +#if defined(CONFIG_REISER4_DEBUG_SIBLING_LIST) && defined(CONFIG_REISER4_DEBUG) +/* + * Turn on sibling-list debugging. In this mode consistency of sibling lists + * of reiser4 internal tree is checked. + * + * Note: this is not available through fs/Kconfig. Adjust manually. + */ +#define REISER4_DEBUG_SIBLING_LIST (1) +#else +#define REISER4_DEBUG_SIBLING_LIST (0) +#endif + +#if defined(CONFIG_CRYPTO_DEFLATE) +#define REISER4_GZIP_TFM (1) +#else +#define REISER4_GZIP_TFM (0) +#endif + +/* + * PLEASE update fs/reiser4/kattr.c:show_options() when adding new compilation + * option + */ + + +extern const char *REISER4_SUPER_MAGIC_STRING; +extern const int REISER4_MAGIC_OFFSET; /* offset to magic string from the + * beginning of device */ + +/* here go tunable parameters that are not worth special entry in kernel + configuration */ + +/* default number of slots in coord-by-key caches */ +#define CBK_CACHE_SLOTS (16) +/* how many elementary tree operation to carry on the next level */ +#define CARRIES_POOL_SIZE (5) +/* size of pool of preallocated nodes for carry process. */ +#define NODES_LOCKED_POOL_SIZE (5) + +#define REISER4_NEW_NODE_FLAGS (COPI_LOAD_LEFT | COPI_LOAD_RIGHT | COPI_GO_LEFT) +#define REISER4_NEW_EXTENT_FLAGS (COPI_LOAD_LEFT | COPI_LOAD_RIGHT | COPI_GO_LEFT) +#define REISER4_PASTE_FLAGS (COPI_GO_LEFT) +#define REISER4_INSERT_FLAGS (COPI_GO_LEFT) + +/* we are supporting reservation of disk space on uid basis */ +#define REISER4_SUPPORT_UID_SPACE_RESERVATION (0) +/* we are supporting reservation of disk space for groups */ +#define REISER4_SUPPORT_GID_SPACE_RESERVATION (0) +/* we are supporting reservation of disk space for root */ +#define REISER4_SUPPORT_ROOT_SPACE_RESERVATION (0) +/* we use rapid flush mode, see flush.c for comments. */ +#define REISER4_USE_RAPID_FLUSH (1) + +/* + * set this to 0 if you don't want to use wait-for-flush in ->writepage(). + */ +#define REISER4_USE_ENTD (1) + +/* Using of emergency flush is an option. */ +#define REISER4_USE_EFLUSH (1) + +/* key allocation is Plan-A */ +#define REISER4_PLANA_KEY_ALLOCATION (1) +/* key allocation follows good old 3.x scheme */ +#define REISER4_3_5_KEY_ALLOCATION (0) + +/* size of hash-table for znodes */ +#define REISER4_ZNODE_HASH_TABLE_SIZE (1 << 13) + +/* number of buckets in lnode hash-table */ +#define LNODE_HTABLE_BUCKETS (1024) + +/* some ridiculously high maximal limit on height of znode tree. This + is used in declaration of various per level arrays and + to allocate stattistics gathering array for per-level stats. */ +#define REISER4_MAX_ZTREE_HEIGHT (8) + +#define REISER4_PANIC_MSG_BUFFER_SIZE (1024) + +/* If array contains less than REISER4_SEQ_SEARCH_BREAK elements then, + sequential search is on average faster than binary. This is because + of better optimization and because sequential search is more CPU + cache friendly. This number (25) was found by experiments on dual AMD + Athlon(tm), 1400MHz. + + NOTE: testing in kernel has shown that binary search is more effective than + implied by results of the user level benchmarking. Probably because in the + node keys are separated by other data. So value was adjusted after few + tests. More thorough tuning is needed. +*/ +#define REISER4_SEQ_SEARCH_BREAK (3) + +/* don't allow tree to be lower than this */ +#define REISER4_MIN_TREE_HEIGHT (TWIG_LEVEL) + +/* NOTE NIKITA this is no longer used: maximal atom size is auto-adjusted to + * available memory. */ +/* Default value of maximal atom size. Can be ovewritten by + tmgr.atom_max_size mount option. By default infinity. */ +#define REISER4_ATOM_MAX_SIZE ((unsigned)(~0)) + +/* Default value of maximal atom age (in jiffies). After reaching this age + atom will be forced to commit, either synchronously or asynchronously. Can + be overwritten by tmgr.atom_max_age mount option. */ +#define REISER4_ATOM_MAX_AGE (600 * HZ) + +/* sleeping period for ktxnmrgd */ +#define REISER4_TXNMGR_TIMEOUT (5 * HZ) + +/* timeout to wait for ent thread in writepage. Default: 3 milliseconds. */ +#define REISER4_ENTD_TIMEOUT (3 * HZ / 1000) + +/* start complaining after that many restarts in coord_by_key(). + + This either means incredibly heavy contention for this part of a tree, or + some corruption or bug. +*/ +#define REISER4_CBK_ITERATIONS_LIMIT (100) + +/* return -EIO after that many iterations in coord_by_key(). + + I have witnessed more than 800 iterations (in 30 thread test) before cbk + finished. --nikita +*/ +#define REISER4_MAX_CBK_ITERATIONS ((unsigned)~0) + +/* put a per-inode limit on maximal number of directory entries with identical + keys in hashed directory. + + Disable this until inheritance interfaces stabilize: we need some way to + set per directory limit. +*/ +#define REISER4_USE_COLLISION_LIMIT (0) + +/* If flush finds more than FLUSH_RELOCATE_THRESHOLD adjacent dirty leaf-level blocks it + will force them to be relocated. */ +#define FLUSH_RELOCATE_THRESHOLD 64 +/* If flush finds can find a block allocation closer than at most FLUSH_RELOCATE_DISTANCE + from the preceder it will relocate to that position. */ +#define FLUSH_RELOCATE_DISTANCE 64 + +/* If we have written this much or more blocks before encountering busy jnode + in flush list - abort flushing hoping that next time we get called + this jnode will be clean already, and we will save some seeks. */ +#define FLUSH_WRITTEN_THRESHOLD 50 + +/* The maximum number of nodes to scan left on a level during flush. */ +#define FLUSH_SCAN_MAXNODES 10000 + +/* default tracing buffer size */ +#define REISER4_TRACE_BUF_SIZE (1 << 15) + +/* what size units of IO we would like cp, etc., to use, in writing to + reiser4. In bytes. + + Can be overwritten by optimal_io_size mount option. +*/ +#define REISER4_OPTIMAL_IO_SIZE (64 * 1024) + +/* see comments in inode.c:oid_to_uino() */ +#define REISER4_UINO_SHIFT (1 << 30) + +/* Mark function argument as unused to avoid compiler warnings. */ +#define UNUSED_ARG __attribute__((unused)) + +#if ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3) +#define NONNULL __attribute__((nonnull)) +#else +#define NONNULL +#endif + +/* master super block offset in bytes.*/ +#define REISER4_MASTER_OFFSET 65536 + +/* size of VFS block */ +#define VFS_BLKSIZE 512 +/* number of bits in size of VFS block (512==2^9) */ +#define VFS_BLKSIZE_BITS 9 + +#define REISER4_I reiser4_inode_data + +/* implication */ +#define ergo( antecedent, consequent ) ( !( antecedent ) || ( consequent ) ) +/* logical equivalence */ +#define equi( p1, p2 ) ( ergo( ( p1 ), ( p2 ) ) && ergo( ( p2 ), ( p1 ) ) ) + +#define sizeof_array(x) ((int) (sizeof(x) / sizeof(x[0]))) + + +#define NOT_YET (0) + +/** Reiser4 specific error codes **/ + +#define REISER4_ERROR_CODE_BASE 500 + +/* Neighbor is not available (side neighbor or parent) */ +#define E_NO_NEIGHBOR (REISER4_ERROR_CODE_BASE) + +/* Node was not found in cache */ +#define E_NOT_IN_CACHE (REISER4_ERROR_CODE_BASE + 1) + +/* node has no free space enough for completion of balancing operation */ +#define E_NODE_FULL (REISER4_ERROR_CODE_BASE + 2) + +/* repeat operation */ +#define E_REPEAT (REISER4_ERROR_CODE_BASE + 3) + +/* deadlock happens */ +#define E_DEADLOCK (REISER4_ERROR_CODE_BASE + 4) + +/* operation cannot be performed, because it would block and non-blocking mode + * was requested. */ +#define E_BLOCK (REISER4_ERROR_CODE_BASE + 5) + +/* wait some event (depends on context), then repeat */ +#define E_WAIT (REISER4_ERROR_CODE_BASE + 6) + +#endif /* __REISER4_H__ */ + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/reiser4_roadmap.html 2004-09-03 00:57:05.365223624 -0700 @@ -0,0 +1,1120 @@ +

Reiser4 (Version 4 of ReiserFS)

+ +

Primary sponsor www.DARPA.mil, regular sponsors applianceware.com and +bigstorage.com. DARPA does not endorse this project, it merely +sponsors it.

+

Table of Contents: + +

+New Extensibility Infrastructure +
   + +File Plugins +
     + +Directory Plugins +
   + +Hash Plugins +
   + +Security Plugins +
   + +Putting Your New Plugin To Work Will Mean Recompiling +
   + +Item Plugins +
   + +Key Assignment Plugins +
   + +Node Search and Item Search Plugins +
   + +Backup +
   + +Without Plugins We Will Drown +
   + +Steps For Creating A Security Attribute +
   + +Plugins: FS Programming For The Lazy +

+ +New Functionality +
   + +Why Linux Needs To Be Secure +
   + +Fine Graining Security +
     + +Good Security Requires Precision In Specification Of Security +
     + +Space Efficiency Concerns Motivate Imprecise Security +
     + +Security Definition Units And Data Access Patterns Sometimes Inherently Don't Align +
     + +/etc/passwd As Example +
     + +Aggregating Files Can Improve The User Interface To Them +
     + +How Do We Write Modifications To An Aggregation +
     + +Aggregation Is Best Implemented As Inheritance +
     + +Constraints +
     + +Auditing +
     + +Increasing the Allowed Granularity of Security +
     + +Files That Are Also Directories +
     + +Hidden Directory Entries +
   + +New Security Attributes and Set Theoretic Semantic Purity +
     + +Minimizing Number Of Primitives Is Important In Abstract Constructions +
     + +Can We Get By Using Just Files and Directories (Composing Streams And Attributes From Files And Directories)? +
     + +List Of Features Needed To Get Attribute And Stream Functionality From Files And Directories +
     + +Mounting FS Flavors +
   + +API Suitable For Accessing Files That Store Security Attributes +
     + +Flaws In Traditional File API When Applied To Security Attributes +
     + +The Usual Resolution Of These Flaws Is A One-Off Solution +
     + +One-Off Solutions Are A Lot of Work To Do A Lot Of +
     + +reiser4() System Call Description +
     + +Transactions and Transcrashes: +
       + +Transactions Are Necessary Safeguard Against Certain Race Condition Exploits +
       + +Transcrashes +
+ +Performance Enhancements +
   + +Dancing Trees Are Faster Than Balanced Trees +
     + +If It Is In RAM, Dirty, and Contiguous, Then Squeeze It ALL +Together Just Before Writing +
   + +Repacker +
   + +Encryption On Commit +
     + +Wandering Logs +
       + +(More detailed treatment soon to be available at www.namesys.com/transactions.html by Joshua MacDonald.) +
     + +Conclusion + + +

+ +

New Extensibility Infrastructure

+

+It takes more than a license to make source code open, it takes a design. + +

Reiser4 will focus on extensibility. Plugins ala photoshop but +for files and directories. This is necessary if we are to enable +vendors to DARPA (including ourselves) to cost effectively add +substantial numbers of new security features to Reiser4. + +

+Imagine that you were an experimental physicist who had spent his life +using only the tools that were in his local hardware store. Then one +day you joined a major research lab with a machine shop, and a whole +bunch of other physicists. All of a sudden you are not using just +whatever tools the large tool companies who have never heard of you +have made for you, you are part of a cooperative of physicists all +making your own tools, swapping tools with each other, suddenly +empowered to have tools that are exactly what you want them to be, or +even merely exactly what your colleagues want them to be, rather than +what some big tool company wants them to be. That is the transition +you will make when you go from version 3 to version 4 of ReiserFS. +The tools your colleagues and sysadmins (your machinists) make are +going to be much better for what you need. + +

File Plugins

+

+Every object (file or directory) will possess a plugin id. This +plugin id will identify a set of methods. The set of methods will +embody all of the different possible interactions with the object that +come from sources external to reiserfs. It is a layer of indirection +added between the external interface to reiserfs, and the rest of +reiserfs. Each method will have a methodid. It will be usual to mix +and match methods from other plugins when composing plugins. + + +

Directory Plugins

Reiser4 will implement a plugin for +traditional directories, and it will implement directory style access +to file attributes as part of the plugin for regular files. Later we +will describe why this is useful. Other directory plugins we will +leave for later versions. There is no deep reason for this +deferra. It is simply the randomness of what features attract sponsors +and make into a release specification, and there are no sponsors at +the moment for additional directory plugins. I have no doubt that +they will appear later; new directory plugins will be too much fun to +miss out on.:-) + +

+ +

Hash Plugins

+ +

Hash plugins already exist in version 3, and if you know what they +are this paragraph says nothing new. To coexist with NFS we must be +able to hand out 64 bit "cookies" that can be used to resume a +readdir. Cookies are implemented in most filesystems as byte offsets +within a directory (which means they cannot shrink directories), and +in reiserfs as hashes of filenames plus a generation counter. We +order directory entries in reiserfs by their cookies. This costs us +performance compared to ordering lexicographically (but is immensely +faster than the linear searching employed by most other Unix +filesystems), and depending on the hash and its match to the +application usage pattern there may be more or less performance +lossage. Hash plugins will probably remain until version 5 or so, +when directory plugins and ordering function plugins will obsolete +them, and directory entries will be ordered by filenames like they +should be (and possibly stem compressed as well). + + +

Security Plugins

+

+Security plugins handle all security checks. They are normally +invoked by file and directory plugins. + +

+

    Example of reading a file: +
  • Access the pluginid for the file. +
  • Invoke the read method for the plugin. +
  • The read method determines the security plugin for the file. +
  • That security plugin invokes its read check method for determining whether to permit the read. +
  • The read check method for the security plugin reads file/attributes containing the permissions on the file +
  • Since file/attributes are also files, this means invoking the plugin for reading the file/attribute. +
  • The pluginid for this particular file/attribute for this file happens to be inherited (saving space and centralizing control of it). + +
  • The read method for the file/attribute is coded such that it does +not check permissions when called by a sec plug method. (Endless +recursion is thereby avoided.) + +
  • The file/attribute plugin employs a decompression algorithm +specially designed for efficient decompression of our encoding of +ACLs. + +
  • The security plugin determines that the read should be permitted. +
  • The read method continues and completes. +
+ + +

Putting Your New Plugin To Work Will Mean Recompiling

+

If you want to add a new plugin, we think your having to ask the sysadmin to +recompile the kernel with your new plugin added to it will be +acceptable for version 4.0. We will initially code plugin-id lookup +as an in-kernel fixed length array lookup, methodids as function +pointers, and make no provision for post-compilation loading of +plugins. Performance, and coding cost, motivates this. + +

Item Plugins

+

+The balancing code will be able to balance an item iff it has an item +plugin implemented for it. The item plugin will implement each of +the methods the balancing code needs (methods such as splitting items, +estimating how large the split pieces will be, overwriting, appending +to, cutting from, or inserting into the item, etc.) +

+In addition to all of the balancing operations, item plugins will also +implement intra-item search plugins. +

+Our current code understands the structure of the items it balances. +This makes adding new types of items storing such new security +attributes as other researchers develop too expensive in coding time, +greatly inhibiting the addition of them to ReiserFS. We anticipate +that there will be a great proliferation in the types of security +attributes in ReiserFS if and only if we are able to make it a matter +requiring not a modification of the balancing code by our most +experienced programmers, but the writing of an item handler. This is +necessary if we are to achieve our goal of making the adding of each +new security attribute an order of magnitude or more easier to perform +than it is now. + + +

Key Assignment Plugins

+

+ +When assigning the key to an item, the key assignment plugin will be +invoked, and it will have a key assignment method for each item type. +A single key assignment plugin is defined for the whole FS at FS +creation time. We know from experience that there is no "correct" key +assignment policy, squid has very different needs from average user +home directories. Yes, there could be value in varying it more +flexibly than just at FS creation time, but we have to draw the line +somewhere when deciding what goes into each release.... + + +

Node Search and Item Search Plugins

+

+Every node layout will have a search method for that layout, and every +item that is searched through will have a search method for that item. +(When doing searches, we search through a node to find an item, and +then search within the item for those items that contain multiple +things to find.) + +

Backup

+

+We need to modify tar to record plugin ids. Some plugins may require special treatment. + +

Without Plugins We Will Drown

+

+People often ask, as ReiserFS grows in features, how will we keep the +design from being drowned under the weight of the added complexity, +and from reaching the point where it is difficult to work on the code? +

+The infrastructure to support security attributes implemented as files +also enables lots of features not necessarily security related. The +plugins we are choosing to implement in v4.0 are all security related +because of our funding source, but users will add other sorts of +plugins just as they took DARPA's TCP/IP and used it for non-military +computers. Only requiring that all features be implemented in the +manner that maximizes code reuse keeps ReiserFS coding complexity down +to where we can manage it over the long term. + + +

Steps For Creating A Security Attribute

+

+Once this infrastructure has been created, you will be able to create a new security attribute by: +

    +
  • defining a pluginid +
  • composing a set of methods for the plugin from ones you create or reuse from other existing plugins +
  • defining a set of items that act as the storage containers of the object, or reusing existing items from other plugins (e.g. regular files) +
  • implementing item handlers for all of the new items you create +
  • creating a key assignment algorithm for all of the new items +
  • implementing a search handler for every item you create that requires searching within it (perhaps search methods should be considered part of the item handler, we shall see while implementing it) +
+ + +

Plugins: FS Programming For The Lazy

+

+The important feature here is that in practice most plugins will have +only a very few of these features unique to them, and the rest of the +plugin will be reused code. This is how we will reduce adding new +security attributes to a task requiring a few weeks work: by first +creating the right tools for the job, and only then starting work on +it. Our ambition is to have two orders of magnitude more security +features than we otherwise would in 5 years, by first making it an +order of magnitude less work to add them to reiser4, and then +attracting an order of magnitude more security attribute developers +because of that. What DARPA is paying for here, is primarily not a +suite of security plugins from Namesys, though it is getting that, but +an architectural (not just the license) enabling of lots of outside vendors +to efficiently create lots of innovative security plugins that Namesys +would never have imagined if working by itself as a supplier. + + +

New Functionality

+ +

Why Linux Needs To Be Secure

+

+The world is sadly changing. It used to be that there was no spam, +because it was not socially acceptable. Now there is spam. It used +to be that security attacks on civilian computers were infrequent, +because only unbalanced teenage boys had nothing better to do. This +is changing in much the same way. +

+The communist government of China has attacking US information +infrastructure as part of its military doctrine. Linux computers are +the bricks the US (and global) civilian information infrastructure is +being built from. It is in the US (and global) interest that Linux +become SECURELY neutral, so that when large US (or elsewhere) banks +use Linux, and the US (or anyone else) experiences an attack, the +infrastructure does not go down. Chinese crackers are known to have +compromised a computer forming a part of the California power grid.... +

+It used to be that most casualties in wars were to combatants. Now +they are mostly to civilians. In future information infrastructure +attacks, who will take more damage, civilian or military +installations? DARPA is funding us to make all Linux computers more +resistant to attack. +

+ +

Fine Graining Security

+

+ +

Good Security Requires Precision In Specification Of Security

+

+Suppose you have a large file, and this file has many components. One +of the themes of SE Linux is that Unix security is insufficiently fine +grained. This is a general principle of security, that good security +requires precision of permissions. When security lacks precision, it +increases the burden of being secure, and the extent to which users +adhere to security requirements in practice is a function of the +burden of adhering to it. +

+ +

Space Efficiency Concerns Motivate Imprecise Security

+

+Many filesystems make it space usage ineffective to store small components as +separate files for various reasons. Not being separate +files means that they cannot have separate permissions. One of the +reasons for using overly aggregated units of security is space +efficiency. ReiserFS currently improves this by an order of magnitude +over most of the existing alternative art. Space efficiency is the +hardest of the reasons to eliminate, and its elimination makes it that +much more enticing to attempt to eliminate the other reasons. + + +

Security Definition Units And Data Access Patterns Sometimes Inherently Don't Align

+

+Applications sometimes want to operate on a collection of components +as a single aggregated stream. (Note that commonly two different +applications want to operate on data with different levels of +aggregation, and the infrastructure for solving this as a security +issue will also solve that problem as well.) + + +

/etc/passwd As Example

+

+I am going to use the /etc/passwd file as an example, not because I +think that other aspects of SE Linux won't solve its problems better, +but because the implementation of it as a single flat file in the +early Unixes is a wonderful illustrative example of poorly +granularized security that the readers may share my personal +experiences with, and then I hope they will be able to imagine that +other data files less famous could have similar problems. +

+Have you ever tried to figure out just exactly what part of the +/etc/passwd file changed near the time of a break-in? Have you ever +wished that you could have a modification time on each field in it? +Have you ever wished the users could change part of it, such as the +gecos field, themselves (setuid utilities have been written to allow +this, but this is a pedagogical not a practical example), but not have +the power to change it for other users? +

+There were good reasons why +/etc/passwd was first implemented as a single file with one single +permission governing the entire file. If we can eliminate them one by +one, the same techniques for making finer grained security effective +will be of value to other highly secure data files. + + +

Aggregating Files Can Improve The User Interface To Them

+

+Consider the use of emacs on a collection of a thousand small 8-32 +byte files like you might have if you deconstructed /etc/passwd into +small files with separable acls for every field. It is more +convenient in screen real estate, buffer management, and other user +interface considerations, to operate on them as an aggregation all +placed into a single buffer rather than as a thousand 8-32 byte +buffers. + + +

How Do We Write Modifications To An Aggregation

+

+Suppose we create a plugin that aggregates all of the files in a +directory into a single stream. How does one handle writes to that +aggregation that change the length of the components of that +aggregation? + +

Richard Stallman pointed out to me that if we separate the +aggregated files with delimiters, then emacs need not be changed at +all to acquire an effective interface for large numbers of small files +accessed via an aggregation plugin. If +/new_syntax_access_path/big_directory_of_small_files/.glued is a +plugin that aggregates every file in big_directory_of_small_files with +a delimiter separating every file within the aggregation, then one can +simply type emacs +/new_syntax_access_path/big_directory_of_small_files/.glued, and the +filesystem has done all the work emacs needs to be effective at this. +Not a line of emacs needs to be changed. +

+One needs to be able to choose different delimiting syntax for +different aggregation plugins so that one can, for say the passwd +file, aggregate subdirectories into lines, and files within those +subdirectories into colon separate fields within the line. XML would +benefit from yet other delimiter construction rules. (We have been +told by one XML company (need link to testimonial here) that ReiserFS is +higher performance than any other "database" for storing XML.) + + +

Aggregation Is Best Implemented As Inheritance

+ +In summary, to be able to achieve precision in security we need to +have inheritance with specifiable delimiters, and we need whole file +inheritance to support ACLs. + + +

Constraints

+ +

+Another way security may be insufficiently fine grained is in values: +it can be useful to allow persons to change data but only within +certain constraints. For this project we will implement plugins, and +one type of plugin will be write constraints. Write-constraints are +invoked upon write to a file, and if they return non-error then the +write is allowed. We will implement two trivial sample +write-constraint plugins, one in the form of a kernel function +loadable as a kernel module which returns non-error (thus allowing the +write) if the file consists of the strings "secret" or "sensitive" but +not "top-secret", and another in the form of a perl program residing +in a file and is executed in user-space which does exactly the same. +Use of kernel functions will have performance advantages, particularly +for small functions, but severe disadvantages in power of scripting, +flexibility, and ability to be installed by non-secure sources. Both +types of plugins will have their place. + +

Note that ACLs will also embody write constraints. + +

+We will implement constraints that are compiled into the kernel, and +constraints that are implemented as user space processes. +Specifically, we will implement a plugin that executes an arbitrary +constraint contained in an arbitary named file as a user space +process, passes the proposed new file contents to that process as +standard input, and iff the process exits without error allows the +write to occur. + +

+It can be useful to have read constraints as well as write constraints. +

+ +

Auditing

+

+We will implement a plugin that notifies administrators by email when +access is made to files, e.g. read access. + +

With each plugin implemented, creating additional plugins becomes +easier as the available toolkit is enriched. Auditing constitutes a +major additional security feature, yet it will be easy to implement +once the infrastructure to support it exists (and it would be +substantial work to implement it without that infrastructure). +

+The scope of this project is not the creation of plugins themselves, +but the creation of the infrastructure that plugin authors would find +useful. We want to enable future contractors to the DoD (and US +financial institutions, PGP Security developers working on SE Linux, +etc.), to implement more secure systems on the Linux platform, not +implement them ourselves. By laying a proper foundation and creating +a toolkit for them, we hope to reduce the cost of coding new security +attributes by an order of magnitude for those who follow us. +Employing a proper set of well orthogonalized primitives also changes +the addition of these attributes from being a complexity burden upon +the architecture into being an empowering extension of the +architecture, which greatly increases their acceptability for +ReiserFS. + + +

Increasing the Allowed Granularity of Security

+

+Inheritance of security attributes is important to providing +flexibility in their administration. We have spoken about making +security more fine grained, but sometimes it needs to be larger +grained. Sometimes a large number of files are logically one unit in +regards to their security, and it is desirable to have a single point +of control over their security. Inheritance of attributes is the +mechanism for implementing that. Security administrators should have +the power to choose whatever units of security they desire, without +having to distort them to make them correspond to semantic units. +Inheritance of file bodies using aggregation plugins allows the units +of security to be smaller than files, inheritance of attributes allows +them to be larger than files. + + +

Files That Are Also Directories

+

+In Reiser4 (but not ReiserFS 3) an object can be both a file and a +directory at the same time. If you access it as a file, you obtain +the named sequence of bytes, and if you use it as a directory you can +obtain files within it, directory listings, etc. There was a lengthy +discussion on the Linux kernel about whether this was technically +feasible to do which I won't reproduce here except to summarize that +Linus showed that it was feasible. +

+Allowing an object to be both a file and a directory is one of the +features necessary to to compose the functionality present in streams +and attributes using files and directories. +

+ +

Hidden Directory Entries

+

+A file can exist, but not be visible when using readdir in the usual +way. WAFL does this with the .snapshots directory, and it works well +for them without disturbing users. This is useful for adding access +to a variety of new features without disturbing the user and +applications with them when they are not relevant. An interesting +question is whether we should have all of these hidden files have the +same name prefix (e.g. '..' at the start of the hidden name), or not. +I am still soliciting input on this. Note that this feature should be +used for special files that one does not want to be backed up. +

+ +

New Security Attributes and +Set Theoretic Semantic Purity

+

+ +

Minimizing Number Of Primitives Is Important In Abstract Constructions

+

+To a theoretician, it is extremely important to minimize the number of +primitives with which one achieves the desired functionality in an +abstract construction. It is a bit hard to explain why this is so, +but it is well accepted that breaking an abstract model into more +basic primitives is very important. A not very precise explanation of +why, is to say that if you have complex primitives, and you break them +into more basic primitives, then by combining those basic primitives +differently from how they were originally combined in the complex +primitives, you can usually express new things that the complex +primitives did not express. Let's follow this grand tradition of +theoreticians, and see what happens if we apply it to Linux files and +directories. + +

Can We Get By Using Just Files and Directories (Composing Streams And Attributes From Files And Directories)?

+

+In Linux we have files, directories, and attributes. In NTFS they +have streams also. Since Samba is important to Linux, there are +frequently requests that we add streams to ReiserFS. There are also +requests that we add more and more different kinds of attributes using +more and more different APIs. Can we do everything that can be done +with {files, directories, attributes, streams} using just {files, +directories}? I say yes, if we make files and directories more powerful and flexible, and I hope that by the end of reading this you +will agree. +

Let us have two basic objects. A file is a sequence of bytes that has a name. A directory is a namespace mapping names to a set of objects "within" the directory. We connect these directory namespaces such that one can use compound names whose subcomponents are separated by a delimiter '/'. +What is missing from files and directories now that attributes and streams offer? +

In ReiserFS 3, there exist file attributes. File attributes are out-of-band data describing the sequence of bytes which is the file. For example, the permissions defining who can access a file, or the last modification time, are file attributes. +File attributes have their own API, and creating new file attributes creates new code complexity and compatibility issues galore. ACLs are one example of new file attributes users want. +

+Since files can also be directories in Reiser4, then we can implement +traditional file attributes as simply files. To access a file +attribute, one need merely name the file, followed by a '/', followed +by an attribute name. That is, a traditional file will be implemented +to possess some of the features of a directory, it will contains files +within the directory corresponding to file attributes which you can +access by their names, and it will contain a file body which is what +you access when you name the "directory" not the file. +

+Unix currently has a variety of attributes that are distinct from +files (ACLS, permissions, timestamps, other mostly security related +attributes....). This is because a variety of persons needed this +feature and that, and there was no infrastructure that would allow +implementing the features as fully orthogonal features that could be +applied to any file. Reiser4 will create that infrastructure. + +

List Of Features Needed To Get Attribute And Stream Functionality From Files And Directories

+
    +
  • api efficient for small files +
  • efficient storage for small files +
  • plugins, including plugins that can compress a file servings as an attribute into a single bit +
  • files that also act as directories when accessed as directories +
  • inheritance (includes file aggregation) +
  • constraints +
  • transactions +
  • hidden directory entries +
+

+The reader is asked to note that each of these additional features is a feature that the filesystem would benefit by the addition of anyway. So we add them in v4. + +

Mounting FS Flavors

+

+Making these attributes accessible via filenames implies a slight +deviation from Unix tradition. If we create a way for this deviation +to not be visible to those who don't want it, it paradoxically gives +us more freedom to deviate without getting paranoid about the effects +on existing applications. + +

+A strict POSIX filesystem API will be implemented as a restricted +functionality namespace obtained when mounting with --POSIX-only, and +it will be possible, and even usual, to mount the filesystem both with +and without --rich-semantics simultaneously each at different mount +points. Note that Al Viro has done work in VFS to make this more +feasible, which is nice. +

"reiser4" will be a distinct filesystem type from "reiserfs" in +the eyes of the mount command. Upon the completion of reiser4, we +will evaluate the relative costs of implementing a conversion script, +or supporting mounting "reiserfs" format filesystems using "reiser4". +Under no circumstance will we make it impossible to mount an old +"reiserfs" formatted filesystem, though users may or may not be able +to mount them as type "reiser4" --- this is not yet determined or +funded. + + +

API Suitable For Accessing Files That Store Security Attributes

+ +

A new system call reiser4() will be implemented to support +applications that don't have to be fooled into thinking that they are +using POSIX, and through this entry point a richer set of semantics +will access the same files that are also accessible using POSIX calls. +reiser4() will not implement more than hierarchical names, a full set +theoretic naming system as described on our future vision page will +not be implemented before reiser5() is implemented. reiser4() will +implement all features necessary to access ACLs as files/directories +rather than as something neither file nor directory. This includes +opening and closing transactions, performing a sequence of I/Os in one +system call, and accessing files without use of file descriptors +(necessary for efficient small I/O). It will do it with a syntax +suitable for evolving into reiser5() syntax with its set theoretic +naming. + + +

Flaws In Traditional File API When Applied To Security Attributes

+Security related attributes tend to be small. The traditional filesystem API for reading and writing files has these flaws in the context of accessing security attributes: +
    +
  • Creating a file descriptor is excessive overhead and not useful when accessing an 8 byte attribute. +
  • A system call for every attribute accessed is too much overhead when accessing lots of little attributes. +
  • Lacking constraints: it is important to constrain what is written to the attribute, often in complex ways. +
  • Lacking transactional semantics: Often one needs to update multiple attributes as one action that is guaranteed to either fully succeed or fully fail. +
+ +

The Usual Resolution Of These Flaws Is A One-Off Solution

+

+The usual response to these flaws is that persons adding security +related and other attributes create a set of methods unique to their +attributes, plus non-reusable code to implement those methods in which +their particular attributes are accessed and stored not using the +methods for files, but using their particular methods for that +attribute. Their particular API for that attribute typically does a +one-off instantiation of a lightweight single system call write +constrained atomic access with no code being reusable by those who +want to modify file bodies. It is very basic and crucial to system +design to decompose desired functionality into reusable orthogonal +separated components. Persons designing security attributes are +typically doing it without the filesystem that they want to add them +to offering them a proper foundation and toolkit. They need more help +from us the core FS developers. Linus said that we can have a system +call to use as our experimental plaything in this, and with what I +have in mind for the API, one rather flexible system call is all we +want for creating transactional lightweight batched constrained +accesses to files, with each of those adjectives to accesses being an +orthogonal optional feature that may or may not be invoked in a +particular instance of the new system call. + + +

One-Off Solutions Are A Lot of Work To Do A Lot Of

+

Looking at the coin from the other side, we want to make it an +order of magnitude less work to add features to ReiserFS, so that both +users and Namesys can add at least an order of magnitude more of them. +To verify that it is truly more extensible you have to do some +extending, and our DARPA funding motivates us to instantiate most of +those extensions as new security features. + +

This system call's syntax enables attributes to be implemented as a +particular type of file --- it avoids uglifying the semantics with two +APIs for two supposedly but not needfully different kinds of objects. +All of its special features that are useful for accessing particular +attributes are all available for use on files also. It has symmetry, +and its features have been fully orthogonalized. There will be +nothing particularly interesting about this system call to a languages +specialist (it's ideas are decades old except to filesystem +developers) until Reiser6, when we will further evolve it into a set theoretic +syntax that deconstructs tuple structured names into ordered set, and +unordered set, name components. That is described at +www.namesys.com/future_vision.html + +

reiser4() System Call Description

+

The reiser4() system call will contain a sequence of commands +separated by a separator ( comma only for now). + +

+Assignment, and transaction, will be the commands supported in +reiser4(), more commands will appear in reiser5. => and <= will be +the assignment operators. + +

    lhs +(assignment target) values: + +
  • /process/buffer/first_byte/last_byte/bytes_written assigns +(writes) to the buffer starting at address first_byte in the process +address space, ending at last_byte, with the number of bytes actually +written (assignment source may be smaller or larger than assignment +target) being written to address bytes_written. Representation of +first_byte,last_byte, and bytes_written is left to the coder to +determine, as it is an issue that will be of much dispute and little +importance. Notice how / is used to indicate that the order of the +operands matters, see www.namesys.com/future_vision.html for details +of why this is appropriate syntax design. Notice the lack of a file +descriptor + +
  • /filename assigns to the file named filename, wholly obliterating +its body with what is assigned. + +
  • /filename/..range/first_byte/last_byte/bytes_written writes to the +body, starting at first_byte, ending not past last_byte, recording +number of bytes written in bytes_written + +
  • /filename/..offset/offset_byte writes to the body starting at offset + +
/..process/..range/first_byte/last_byte/bytes_written writes to +the process address space, starting at first_byte, ending not past +last_byte, recording number of bytes actually written in bytes_written + +
    rhs (assignment source) values: + +
  • /process/buffer/first_byte/last_byte/bytes_read reads from the +buffer starting at address first_byte in the process address space, +ending at last_byte, with the number of bytes actually read +(assignment source may be smaller or larger than assignment target) +being written to address bytes_read. Representation of first_byte, +last_byte, and bytes_read is left to the coder to determine, as it is +an issue that will be of much dispute and little importance. + +
  • /filename reads the entirety of the file named filename. + +
  • /filename/..range/first_byte/last_byte/bytes_read reads from the +body, starting at first_byte, ending not past last_byte, recording +number of bytes read in bytes_read + +
  • /filename/..offset/offset_byte/bytes_read reads from the body +starting at offset until the end + +
  • /filename/..stat/owner reads from the ownership field of the stat +data (stat data is that which is returned by the stat() system call (owner, permissions, +etc.) +and stored on a per file basis by the FS) + +
  • /filename/..nonbody returns a delimiter separated aggregation of +all parts of the file except the body of the file (owner, permissions, +ACLs, etc.). + +
+ + +

Transactions and Transcrashes:

+ +

Transactions Are Necessary Safeguard Against Certain Race Condition Exploits

+ +(This section to be replaced with link to Josh MacDonald paper when that is complete.) +

+Recently, a security exploit was discovered in all versions of the MIT Kerberos secure authentication system due to unsafe handling of temporary files [Bugtraq, 3/7/2001]. +http://www.linuxsecurity.net/advisories/other_advisory-1204.html +

+During the process of generating a new ticket, the attacker creates a symbolic link that redirects the ticket file being written to an arbitrary location. This kind of vulnerability is quite common, unfortunately, due to inherent weaknesses of the traditional POSIX file system interface. There is no primitive support for an operation that atomically tests for the existence of a symbolic link prior to opening that location, not without vulnerability to races. The solution posted in the Kerberos incident does not completely eliminate the vulnerability. Instead, vulnerability is greatly reduced through programmer vigilance (provided a few assumptions). The existing file system interface leaves open potential vulnerabilities such as this, by default, due to the fact that it is a stateless interface. In general, lacking transactions the result of a file system read cannot be trusted for security decisions; the instant a value is returned it may be out of date. +

+When security is a concern and the application is sufficiently important that it can be modified to conform with more secure interfaces, there is an easy solution to these problems --- transactions. Transactions provide the framework for strict, fine-grained locking that is used to extend the atomicity of individual operations into an atomic sequence of operations. In the Kerberos example, the ticket-writing application would instead issue a sequence of operations to: +

    +
  • lookup the file to be written, +
  • make security checks on that file, +
  • open the file for writing +
  • output data. +
+

The transaction framework provides a context for ensuring that a security check remains consistent throughout the resulting operation. +

+Transactions also provide critical support for extensibility (i.e., +plugins), since the system is able to automatically recover from +partial component failures, and transactions are necessary to support +consistent operations on multiple parts of an "aggregate" file. [For +example: you wish to perform a complex edit over /etc/passwd that +requires the addition of one user and the deletion of another (e.g., +rename user). To perform that operation consistently you must have +transactions to preserve the invariant.] +

+There is a close relationship between version control and transaction +isolation, which is why the same programmer on our team (Josh +McDonald) does both. + + +

Transcrashes

+

+There is a reason why filesystems implemented on top of databases have +not performed well. Traditional database transactions do more work +than the filesystems needs them to do. It is not that database +transactions are done wrong (far from it, we will take great pride in +adding database style transactions to reiser4), it is that in some +circumstances they are doing more work than is needed by traditional +filesystem usage requirements, and good performance requires making +the aspects of consistency independently selectable. In particular, +filesystems often need to be able to guarantee that an operation will +be atomic with respect to surviving a crash, and DON'T need to +guarantee isolation with respect to other concurrent operations. This +has profound performance import, and it affects not just buffering in +RAM, but also dramatically impacts the size of logs. +

+[J.n> Gray] models transactions as having 4 degrees of consistency. I +find it more appropriate to model not degrees of consistency, which +implies that the features have ranked levels and one cannot have a +higher level feature without also getting the lower level features +with it, but aspects of consistency, each potentially fully orthogonal +to the other. +

+There are three aspects of consistency we will support initially, and +you'll note that they are decoupled and independently specifiable. +

    +
  • "Transcrashes", which guarantee that either all or none of the +transcrash will survive a crash. +
  • Branches, which guarantee isolation but not exclusion. +
  • Locks, which guarantee exclusion but not isolation. +
+

+There is necessarily a performance cost to implementing an isolated +transaction. This cost can be reduced for transcrashes which are not +also branched or locked. Very frequently the application better knows +whether it needs to branch or lock, knows that its structure of +operation is such that it does not need the protection of branching +and locking, and it can depend on itself to do the right thing without +the significant unnecessary performance cost of asking the filesystem +to protect it from itself. +

+A "limited transcrash" has the property that it can be told to finish up +and either commit or abort within MAX_LIMITED_TRANSCRASH_DELAY time, and +it also has the property that the filesystem doesn't have to know how to +rollback if it chooses to abort but rather the user space process must +track how to do rollbacks. Most such transcrashes will be implemented +to not ever rollback, but more simply to instead take responsibility +for ensuring that they can commit quickly enough. If they fail to do +so, the commit will be imposed upon them before they have completed +the transcrash. This approach is particularly useful for high +performance short running transcrashes. +

+For instance, suppose you want to do two simple updates to two files +as an atomic transaction, and these updates will not require longer +than MAX_TRANSCRASH_DELAY to be done, and you want to be able to do +many of these in parallel with high performance, and the application +process running in user space is able to handle worrying about +enforcing isolation through selective locking. In that case, a common +view of the filesystem state involving many other such limited +transcrashes can be batched together and committed as one commit. +(This is necessarily higher performance.) When memory pressure +triggers commit, all new transcrashes will hang while all outstanding +transcrashes are signalled to complete their transcrash, and given +MAX_TRANSCRASH_DELAY time in which they can be a running process if +they choose to be. Carefully note that the delay allowed has to be +measured as time during which the process has priority if it chooses +to be runnable, not as absolute time. (Nikita, please phrase this +more precisely for me, you know the scheduler better than I.) +

+A particular source of concern is high concurrency of semantically +unrelated data that has common metadata. For instance, the super block +and the internal nodes of the tree. Where the application can +track and self-ensure the isolation of itself from concurrent +processes rather than requiring the OS to give it its own atomically +merged and committed view, performance is very likely going to be +higher, and perhaps order of magnitude higher. +

+Reiser4 will implement limited transcrashes first, and whether it will +implement branching in v4.0 or 4.1 will depend on how fast +Josh works. +

+Why are limited transcrashs the priority for us? We need to ensure that +the infrastructure we create is performance efficient for what +filesystems currently do before we enable new functionality based on +strong transactions. In other words, we have gotten addicted to being +the fastest FS in town, and we don't want to lose that. Reiser4 needs +transactional semantics for some of its internal housekeeping +(implementing rename), and only limited transcrashs are a high enough +performance architecture for those needs. +

+When any grouping delimiter ([] is the only one for 4.0) is preceded +by tw/transcrash_name (e.g. tw/transcrash_33[ /home/reiser/a <= +/home/reiser/b, /home/reiser/c <= /home/reiser/d]), then it delimits a +transcrash. We leave unspecified for now how to have multipart +specifications of a transcrash (I am getting pretty shameless in +deferring things for v4.1, yes...? ). Transactions logically batch +not nest, extent that the interpreter will error check the nesting to +make sure that it has not been passed confused garbage. +

+To anyone who has worked in databases or any other aspect of language +design, this design surely seems exceedingly simple and modest. To +many filesystem and OS folks, this seems like something extraordinary, +commands that are parsed, oh no! The complexity will be +extraordinary, oh no! Sigh. Namesys, determined to bring radical new +1960's technology from other areas of computer science into the file +systems field no matter how crazy our competitors think we are! Sigh. +Reiser4 will be smaller than XFS much less VxFS.... + + +

Performance Enhancements

+ + +

Dancing Trees Are Faster Than Balanced Trees

+ +

ReiserFS V4 will also add innovations in the fundamental tree +technology. We will employ not balanced trees, but "dancing trees". +Dancing trees merge insufficiently full nodes not with every +modification to the tree, but instead: + +

    + +
  • in response to memory pressure +triggering a commit. + +
  • when an insertion into an internal node presents a danger of +needing to split the internal node, and the immediate children are not +sufficiently space efficient (sufficiently being a configurable +value), and reducing the number of children could avoid the split. +
+ +

If It Is In RAM, Dirty, and Contiguous, Then Squeeze It ALL +Together Just Before Writing

+ +

Let a slum be defined as a maximal sequence of contiguous in the +tree order, and dirty in this transaction, nodes. A dancing tree, +when presented with memory pressure, responds to it by committing the +transaction, and the commit in turn triggers a repacking of all slums +involved in the transaction which it estimates can be squeezed into +fewer nodes than they currently occupy. +

+Balanced trees have an +inherent tradeoff between balancing cost and space efficiency. If +they consider more neighboring nodes, for the purpose of merging them +to save a node, with every change to the tree, then they can pack the +tree more tightly at the cost of moving more data with every change to +the tree. +

+By contrast, with a dancing tree, you simply take a large slum, shove +everything in it as far to the left as it will go, and then free all +the nodes in the slum that are left with nothing remaining in them, at +the time of committing the slum's contents to disk in response to +memory pressure. This gives you extreme space efficiency when slums +are large, at a cost in data movement that is lower than it would be +with an invariant balancing criterion because it is done less often. +By compressing at the time one flushes to disk, one compresses less +often, and that means one can afford to do it more thoroughly. + + +

Repacker

+

+Another way of escaping from the balancing time vs. space efficiency +tradeoff is to use a repacker. 80% of files on the disk remain +unchanged for long periods of time. It is efficient to pack them +perfectly, by using a repacker that runs much less often than every +write to disk. This repacker goes through the entire tree ordering, +from left to right and then from right to left, alternating each time +it runs. When it goes from left to right in the tree ordering, it +shoves everything as far to the left as it will go, and when it goes +from right to left it shoves everything as far to the right as it will +go. (Left means small in key or in block number:-) ). In the absence +of FS activity the effect of this over time is to sort by tree order +(defragment), and to pack with perfect efficiency. +

+Reiser4.1 will modify the repacker to insert controlled "airholes", as +it is well known that insertion efficiency is harmed by overly tight +packing. +

+I hypothesize that it is more efficient to periodically run a repacker +that systematically repacks using large IOs, than to perform lots of 1 +block reads of neighboring nodes of the modification points, so as to +preserve a balancing invariant in the face of poorly localized +modifications to the tree. + +

Encryption On Commit

+

+Currently, encrypted files suffer severely in their write performance +when implemented using schemes that encrypt at every write() rather +than at every commit to disk. We will implement encrypt on flush, +such that a file with an encryption plugin id is encrypted not at the +time of write, but at the time of commit to disk. This is both +non-trivial to implement, and important to performance. It requires +implementing a memory pressure manager for ReiserFS. That memory +pressure manager would receive a request to either reduce memory +consumed, reduce dirty memory (dirty memory needs special treatment +for deadlock avoidance reasons), or verify that nothing overly old has +been kept in memory for too long. It would respond by selecting what +to commit, and preparing it for writing to disk. That preparation +will consist of encrypting it for those files that implement the +encryption plugin. (It can also consist of allocating optimal block +numbers and repacking formatted nodes and compressing data, but that +is not of such concern here.) I suspect you will want us to +coordinate with the PGP developers you are also contracting with. + +

Encryption is implemented as a special form of repacking, and it +occurs for any node which has its CONTAINS_ENCRYPTED_DATA state flag +set on it regardless of space usage. With the dancing tree +infrastructure in place, it should be only a moderate amount of work +to implement encryption as a variant on repacking on commit. +

+ +

Wandering Logs

+ +

(More detailed treatment soon to be available at www.namesys.com/transactions.html by Joshua MacDonald.)

+

+Traditional fixed location logs have a problem in that data gets +written twice, once to the log, and once to the rest of the +filesystem. +

+Instead of moving data out of the log, wandering logs redefine what +blocks compose the log. There is no fixed location for where the log +is, though there are fixed locations for where the definition of what +blocks compose the log is. +

+This approach has two principle disadvantages: +

    +
  • blocks which contain data that must be retained if the transcrash +fails to complete cannot be written in the place of the block +containing the old data, and their location may not be as optimal as +that of the new data (it may also be more optimal though) + +
  • it does not support undo/redo for isolated transactions +
+

+This means that in addition to wandering block logs, we also need +wandering logical logs. +

+Wandering logical logs log for every transaction enough information to +either redo or undo each isolated transaction. +

+They have the disadvantage that first they write the data into the log +(though it can go anywhere convenient to define as part of the log), +and then they write the data again after the transaction commits. +

+They have the advantage that for small updates (when not logging a 100 +megabyte file) their log is smaller. This is potentially useful for +distributed filesystems which operate by transmitting the log. +

+The compelling reason for supporting them is that they are needed for +supporting isolated transactions, and while isolated transactions are +expected to be only a small fraction of total disk IO, they are quite +important functionally. (How many bytes does it take to make your +system not secure.... ) + + +

Conclusion

+

+Reiser4 will offer a dramatically better infrastructure for creating +new filesystem features. Files and directories will have all of the +features needed to make it not necessary to have file attributes be +something different from files. The effectiveness of this new +infrastructure will be tested using a variety of new security +features. Performance will be greatly improved by the use of dancing +trees, wandering logs, allocate on flush, a repacker, and encryption +on commit. --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/repacker.c 2004-09-03 00:57:05.368223168 -0700 @@ -0,0 +1,661 @@ +/* Copyright 2003 by Hans Reiser */ + +/* + The reiser4 repacker. + + It walks the reiser4 tree and marks all nodes (reads them if it is + necessary) for repacking by setting JNODE_REPACK bit. Also, all nodes which + have no JNODE_REPACK bit set nodes added to a transaction and marked dirty. +*/ + +#include +#include +#include +#include +#include +#include + +#include + +#include "reiser4.h" +#include "kattr.h" +#include "super.h" +#include "tree.h" +#include "tree_walk.h" +#include "jnode.h" +#include "znode.h" +#include "block_alloc.h" + +#include "plugin/item/extent.h" + +#include +#include "kcond.h" + +#include "repacker.h" + +/* The reiser4 repacker process nodes by chunks of REPACKER_CHUNK_SIZE + * size. */ +#define REPACKER_DEFAULT_CHUNK_SIZE 512 + +enum repacker_state_bits { + REPACKER_RUNNING = 0x1, + REPACKER_STOP = 0x2, + REPACKER_DESTROY = 0x4, + REPACKER_GOES_BACKWARD = 0x8 +}; + +/* Per super block repacker structure for */ +struct repacker { + /* Back reference to a super block. */ + struct super_block * super; + /* Repacker thread state */ + enum repacker_state_bits state; + /* A spin lock to protect state */ + spinlock_t guard; + /* A conditional variable to wait repacker state change. */ + kcond_t cond; +#if REISER4_USE_SYSFS + /* An object (kobject), externally visible through SysFS. */ + struct kobject kobj; +#endif + struct { + reiser4_key start_key; + reiser4_block_nr chunk_size; + reiser4_block_nr count; + } params; +}; + +/* A thread-safe repacker check state bit routine. */ +static inline int check_repacker_state_bit(struct repacker *repacker, enum repacker_state_bits bits) +{ + int result; + + spin_lock(&repacker->guard); + result = !!(repacker->state & bits); + spin_unlock(&repacker->guard); + + return result; +} + +static int check_repacker_state (struct repacker * repacker) +{ + if (check_repacker_state_bit( + get_current_super_private()->repacker, REPACKER_STOP)) + return -EINTR; + if (current->flags & PF_FREEZE) + return -E_REPEAT; + if (current_atom_should_commit()) + return -E_REPEAT; + return 0; +} + +static void repacker_cursor_init (struct repacker_cursor * cursor, struct repacker * repacker) +{ + int backward = check_repacker_state_bit(repacker, REPACKER_GOES_BACKWARD); + + xmemset(cursor, 0, sizeof (struct repacker_cursor)); + + blocknr_hint_init(&cursor->hint); + cursor->hint.backward = backward; + + if (backward) + cursor->hint.blk = get_current_super_private()->block_count - 1; + else + cursor->hint.blk = 0; +} + +static void repacker_cursor_done (struct repacker_cursor * cursor) +{ + blocknr_hint_done(&cursor->hint); +} + +/* routines for closing current transaction and beginning new one */ + +static int end_work (void) +{ + reiser4_context * ctx = get_current_context(); + + txn_end(ctx); + return 0; +} +static void begin_work (void) +{ + reiser4_context * ctx = get_current_context(); + preempt_point(); + txn_begin(ctx); +} + +/* Processing of a formatted node when the repacker goes forward. */ +static int process_znode_forward (tap_t * tap, void * arg) +{ + struct repacker_cursor * cursor = arg; + znode * node = tap->lh->node; + int ret; + + assert("zam-954", cursor->count > 0); + + ret = check_repacker_state(get_current_super_private()->repacker); + if (ret) + return ret; + + if (ZF_ISSET(node, JNODE_REPACK)) + return 0; + + if (current_atom_should_commit()) + return -E_REPEAT; + + znode_make_dirty(node); + ZF_SET(node, JNODE_REPACK); + + cursor->stats.znodes_dirtied ++; + + if (-- cursor->count <= 0) + return -E_REPEAT; + return 0; +} + +/* Processing of unformatted nodes (of one extent unit) when the repacker goes + * forward. */ +static int process_extent_forward (tap_t *tap, void * arg) +{ + int ret; + struct repacker_cursor * cursor = arg; + + ret = check_repacker_state(get_current_super_private()->repacker); + if (ret) + return ret; + + ret = mark_extent_for_repacking(tap, cursor->count); + if (ret > 0) { + cursor->stats.jnodes_dirtied += ret; + cursor->count -= ret; + if (cursor->count <= 0) + return -E_REPEAT; + return 0; + } + + return ret; +} + + +/* It is for calling by tree walker before taking any locks. */ +static int prepare_repacking_session (void * arg) +{ + struct repacker_cursor * cursor = arg; + int ret; + + assert("zam-951", schedulable()); + + all_grabbed2free(); + ret = end_work(); + if (ret) + return ret; + + if (current->flags & PF_FREEZE) + refrigerator(PF_FREEZE); + + begin_work(); + balance_dirty_pages_ratelimited(get_current_super_private()->fake->i_mapping); + cursor->count = get_current_super_private()->repacker->params.chunk_size; + return reiser4_grab_space((__u64)cursor->count, + BA_CAN_COMMIT | BA_FORCE); +} + +/* When the repacker goes backward (from the rightmost key to the leftmost + * one), it does relocation of all processed nodes to the end of disk. Thus + * repacker does what usually the reiser4 flush does but in backward direction + * and node squeezing is not supported. */ +static int process_znode_backward (tap_t * tap, void * arg) +{ + lock_handle parent_lock; + load_count parent_load; + znode * child = tap->lh->node; + struct repacker_cursor * cursor = arg; + __u64 new_blocknr; + int ret; + + assert("zam-977", (unsigned)(cursor->count) <= get_current_context()->grabbed_blocks); + + /* Add node to current transaction like in processing forward. */ + ret = process_znode_forward(tap, arg); + if (ret) + return ret; + + init_lh(&parent_lock); + ret = reiser4_get_parent(&parent_lock, child, ZNODE_WRITE_LOCK, 0); + if (ret) + goto out; + + init_load_count(&parent_load); + + /* Do not relocate nodes which were processed by flush already. */ + if (ZF_ISSET(child, JNODE_RELOC) || ZF_ISSET(child, JNODE_OVRWR)) + goto out; + + if (ZF_ISSET(child, JNODE_CREATED)) { + assert("zam-962", blocknr_is_fake(znode_get_block(child))); + cursor->hint.block_stage = BLOCK_UNALLOCATED; + } else { + if (znode_get_level(child) == LEAF_LEVEL) + cursor->hint.block_stage = BLOCK_FLUSH_RESERVED; + else { + ret = reiser4_grab_space((__u64)1, + BA_FORCE | + BA_RESERVED | + BA_PERMANENT | + BA_FORMATTED); + if (ret) + goto out; + + cursor->hint.block_stage = BLOCK_GRABBED; + } + } + + { + __u64 len = 1UL; + + ret = reiser4_alloc_blocks(&cursor->hint, &new_blocknr, &len, + BA_PERMANENT | BA_FORMATTED); + if (ret) + goto out; + + cursor->hint.blk = new_blocknr; + } + + if (!ZF_ISSET(child, JNODE_CREATED)) { + ret = reiser4_dealloc_block(znode_get_block(child), 0, + BA_DEFER | BA_PERMANENT | BA_FORMATTED); + if (ret) + goto out; + } + + /* Flush doesn't process nodes twice, it will not discard this block + * relocation. */ + ZF_SET(child, JNODE_RELOC); + + /* Update parent reference. */ + if (unlikely(znode_above_root(parent_lock.node))) { + reiser4_tree * tree = current_tree; + UNDER_RW_VOID(tree, tree, write, tree->root_block = new_blocknr); + } else { + coord_t parent_coord; + item_plugin *iplug; + + ret = incr_load_count_znode(&parent_load, parent_lock.node); + if (ret) + goto out; + + ret = find_child_ptr(parent_lock.node, child, &parent_coord); + if (ret) + goto out; + + assert ("zam-960", item_is_internal(&parent_coord)); + assert ("zam-961", znode_is_loaded(child)); + iplug = item_plugin_by_coord(&parent_coord); + assert("zam-964", iplug->f.update != NULL); + iplug->f.update(&parent_coord, &new_blocknr); + } + + znode_make_dirty(parent_lock.node); + ret = znode_rehash(child, &new_blocknr); + + out: + done_load_count(&parent_load); + done_lh(&parent_lock); + assert("zam-982", (unsigned)(cursor->count) <= get_current_context()->grabbed_blocks); + return ret; +} + +/* Processing of unformatted nodes when the repacker goes backward. */ +static int process_extent_backward (tap_t * tap, void * arg) +{ + struct repacker_cursor * cursor = arg; + int ret; + + assert("zam-978", (unsigned)(cursor->count) <= get_current_context()->grabbed_blocks); + + ret = check_repacker_state(get_current_super_private()->repacker); + if (ret) + return ret; + + ret = process_extent_backward_for_repacking(tap, cursor); + if (ret) + return ret; + if (cursor->count <= 0) + return -E_REPEAT; + + return 0; +} +/* A set of functions to be called by tree_walk in repacker forward pass. */ +static struct tree_walk_actor forward_actor = { + .process_znode = process_znode_forward, + .process_extent = process_extent_forward, + .before = prepare_repacking_session +}; + +/* A set of functions to be called by tree_walk in repacker backward pass. */ +static struct tree_walk_actor backward_actor = { + .process_znode = process_znode_backward, + .process_extent = process_extent_backward, + .before = prepare_repacking_session +}; + + +reiser4_internal int reiser4_repacker (struct repacker * repacker) +{ + struct repacker_cursor cursor; + int backward; + struct tree_walk_actor * actor; + int ret; + + repacker_cursor_init(&cursor, repacker); + + backward = check_repacker_state_bit(repacker, REPACKER_GOES_BACKWARD); + actor = backward ? &backward_actor : &forward_actor; + ret = tree_walk(NULL, backward, actor, &cursor); + printk(KERN_INFO "reiser4 repacker: " + "%lu formatted node(s) processed, %lu unformatted node(s) processed, ret = %d\n", + cursor.stats.znodes_dirtied, cursor.stats.jnodes_dirtied, ret); + + repacker_cursor_done(&cursor); + return ret; +} + +/* The repacker kernel thread code. */ +reiser4_internal int repacker_d(void *arg) +{ + struct repacker * repacker = arg; + struct task_struct * me = current; + int ret; + + reiser4_context ctx; + + daemonize("k_reiser4_repacker_d"); + + /* block all signals */ + spin_lock_irq(&me->sighand->siglock); + siginitsetinv(&me->blocked, 0); + recalc_sigpending(); + spin_unlock_irq(&me->sighand->siglock); + + /* zeroing the fs_context copied form parent process' task struct. */ + me->journal_info = NULL; + + printk(KERN_INFO "Repacker: I am alive, pid = %u\n", me->pid); + ret = init_context(&ctx, repacker->super); + if (!ret) { + ret = reiser4_repacker(repacker); + reiser4_exit_context(&ctx); + } + + spin_lock(&repacker->guard); + repacker->state &= ~REPACKER_RUNNING; + kcond_broadcast(&repacker->cond); + spin_unlock(&repacker->guard); + + return ret; +} + +static void wait_repacker_completion(struct repacker * repacker) +{ + if (repacker->state & REPACKER_RUNNING) { + kcond_wait(&repacker->cond, &repacker->guard, 0); + assert("zam-956", !(repacker->state & REPACKER_RUNNING)); + } +} + +#if REISER4_USE_SYSFS + +static int start_repacker(struct repacker * repacker) +{ + spin_lock(&repacker->guard); + if (!(repacker->state & REPACKER_DESTROY)) { + repacker->state &= ~REPACKER_STOP; + if (!(repacker->state & REPACKER_RUNNING)) { + repacker->state |= REPACKER_RUNNING; + spin_unlock(&repacker->guard); + kernel_thread(repacker_d, repacker, CLONE_VM | CLONE_FS | CLONE_FILES); + return 0; + } + } + spin_unlock(&repacker->guard); + return 0; +} + +static void stop_repacker(struct repacker * repacker) +{ + spin_lock(&repacker->guard); + repacker->state |= REPACKER_STOP; + spin_unlock(&repacker->guard); +} + +struct repacker_attr { + struct attribute attr; + ssize_t (*show)(struct repacker *, char * buf); + ssize_t (*store)(struct repacker *, const char * buf, size_t size); +}; + +static ssize_t start_attr_show (struct repacker * repacker, char * buf) +{ + return snprintf(buf, PAGE_SIZE , "%d", check_repacker_state_bit(repacker, REPACKER_RUNNING)); +} + +static ssize_t start_attr_store (struct repacker * repacker, const char *buf, size_t size) +{ + int start_stop = 0; + + sscanf(buf, "%d", &start_stop); + if (start_stop) + start_repacker(repacker); + else + stop_repacker(repacker); + + return size; +} + +static ssize_t direction_attr_show (struct repacker * repacker, char * buf) +{ + return snprintf(buf, PAGE_SIZE , "%d", check_repacker_state_bit(repacker, REPACKER_GOES_BACKWARD)); +} + +static ssize_t direction_attr_store (struct repacker * repacker, const char *buf, size_t size) +{ + int go_left = 0; + + sscanf(buf, "%d", &go_left); + + spin_lock(&repacker->guard); + if (!(repacker->state & REPACKER_RUNNING)) { + if (go_left) + repacker->state |= REPACKER_GOES_BACKWARD; + else + repacker->state &= ~REPACKER_GOES_BACKWARD; + } + spin_unlock(&repacker->guard); + return size; +} + +static ssize_t start_key_attr_show (struct repacker * repacker, char * buf) +{ + spin_lock(&repacker->guard); + spin_unlock(&repacker->guard); + + return 0; +} + +static ssize_t start_key_attr_store (struct repacker * repacker, const char *buf, size_t size) +{ + spin_lock(&repacker->guard); + spin_unlock(&repacker->guard); + + return (ssize_t)size; +} + +static ssize_t count_attr_show (struct repacker * repacker, char * buf) +{ + __u64 count; + + spin_lock(&repacker->guard); + count = repacker->params.count; + spin_unlock(&repacker->guard); + + return snprintf(buf, PAGE_SIZE, "%llu", (unsigned long long)count); +} + +static ssize_t count_attr_store (struct repacker * repacker, const char *buf, size_t size) +{ + unsigned long long count; + + sscanf(buf, "%Lu", &count); + + spin_lock(&repacker->guard); + repacker->params.count = (__u64)count; + spin_unlock(&repacker->guard); + + return (ssize_t)size; +} + +static ssize_t chunk_size_attr_show (struct repacker * repacker, char * buf) +{ + __u64 chunk_size; + + spin_lock(&repacker->guard); + chunk_size = repacker->params.chunk_size; + spin_unlock(&repacker->guard); + + return snprintf(buf, PAGE_SIZE, "%Lu", (unsigned long long)chunk_size); +} + +static ssize_t chunk_size_attr_store (struct repacker * repacker, const char *buf, size_t size) +{ + unsigned long long chunk_size; + + sscanf(buf, "%Lu", &chunk_size); + + spin_lock(&repacker->guard); + repacker->params.chunk_size = (__u64)chunk_size; + spin_unlock(&repacker->guard); + + return (ssize_t)size; +} + +#define REPACKER_ATTR(attr_name, perm) \ +static struct repacker_attr attr_name ## _attr = { \ + .attr = { \ + .name = # attr_name, \ + .mode = perm \ + }, \ + .show = attr_name ## _attr_show, \ + .store = attr_name ## _attr_store, \ +} + +REPACKER_ATTR(start, 0644); +REPACKER_ATTR(direction, 0644); +REPACKER_ATTR(start_key, 0644); +REPACKER_ATTR(count, 0644); +REPACKER_ATTR(chunk_size, 0644); + +static struct attribute * repacker_def_attrs[] = { + &start_attr.attr, + &direction_attr.attr, + &start_key_attr.attr, + &count_attr.attr, + &chunk_size_attr.attr, + NULL +}; + +static ssize_t repacker_attr_show (struct kobject *kobj, struct attribute *attr, char *buf) +{ + struct repacker_attr * r_attr = container_of(attr, struct repacker_attr, attr); + struct repacker * repacker = container_of(kobj, struct repacker, kobj); + + return r_attr->show(repacker, buf); +} + +static ssize_t repacker_attr_store (struct kobject *kobj, struct attribute *attr, const char *buf, size_t size) +{ + struct repacker_attr * r_attr = container_of(attr, struct repacker_attr, attr); + struct repacker * repacker = container_of(kobj, struct repacker, kobj); + + return r_attr->store(repacker, buf, size); +} + +static struct sysfs_ops repacker_sysfs_ops = { + .show = repacker_attr_show, + .store = repacker_attr_store +}; + +static struct kobj_type repacker_ktype = { + .sysfs_ops = &repacker_sysfs_ops, + .default_attrs = repacker_def_attrs, + .release = NULL +}; + +static int init_repacker_sysfs_interface (struct super_block * s) +{ + int ret = 0; + reiser4_super_info_data * sinfo = get_super_private(s); + struct kobject * root = &sinfo->kobj.kobj; + struct repacker * repacker = sinfo->repacker; + + assert("zam-947", repacker != NULL); + + snprintf(repacker->kobj.name, KOBJ_NAME_LEN, "repacker"); + repacker->kobj.parent = kobject_get(root); + repacker->kobj.ktype = &repacker_ktype; + ret = kobject_register(&repacker->kobj); + + return ret; +} + +static void done_repacker_sysfs_interface (struct super_block * s) +{ + reiser4_super_info_data * sinfo = get_super_private(s); + + kobject_unregister(&sinfo->repacker->kobj); +} + +#else /* REISER4_USE_SYSFS */ + +#define init_repacker_sysfs_interface(s) (0) +#define done_repacker_sysfs_interface(s) do{}while(0) + +#endif /* REISER4_USE_SYSFS */ + +reiser4_internal int init_reiser4_repacker (struct super_block *super) +{ + reiser4_super_info_data * sinfo = get_super_private(super); + + assert ("zam-946", sinfo->repacker == NULL); + sinfo->repacker = kmalloc(sizeof (struct repacker), GFP_KERNEL); + if (sinfo->repacker == NULL) + return -ENOMEM; + xmemset(sinfo->repacker, 0, sizeof(struct repacker)); + sinfo->repacker->super = super; + + /* set repacker parameters by default values */ + sinfo->repacker->params.chunk_size = REPACKER_DEFAULT_CHUNK_SIZE; + + spin_lock_init(&sinfo->repacker->guard); + kcond_init(&sinfo->repacker->cond); + + return init_repacker_sysfs_interface(super); +} + +reiser4_internal void done_reiser4_repacker (struct super_block *super) +{ + reiser4_super_info_data * sinfo = get_super_private(super); + struct repacker * repacker; + + repacker = sinfo->repacker; + assert("zam-945", repacker != NULL); + done_repacker_sysfs_interface(super); + + spin_lock(&repacker->guard); + repacker->state |= (REPACKER_STOP | REPACKER_DESTROY); + wait_repacker_completion(repacker); + spin_unlock(&repacker->guard); + + kfree(repacker); + sinfo->repacker = NULL; +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/repacker.h 2004-09-03 00:57:05.369223016 -0700 @@ -0,0 +1,22 @@ +/* Copyright 2003 by Hans Reiser */ + +#ifndef __FS_REISER4_REPACKER_H__ +#define __FS_REISER4_REPACKER_H__ + +/* Repacker per tread state and statistics. */ +struct repacker_cursor { + reiser4_blocknr_hint hint; + int count; + struct { + long znodes_dirtied; + long jnodes_dirtied; + } stats; +}; + +extern int init_reiser4_repacker(struct super_block *); +extern void done_reiser4_repacker(struct super_block *); + +extern int reiser4_repacker (struct repacker * repacker); +extern int repacker_d(void *arg); + +#endif /* __FS_REISER4_REPACKER_H__ */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/safe_link.c 2004-09-03 00:57:07.759859584 -0700 @@ -0,0 +1,348 @@ +/* Copyright 2003, 2004 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Safe-links. */ + +/* + * Safe-links are used to maintain file system consistency during operations + * that spawns multiple transactions. For example: + * + * 1. Unlink. UNIX supports "open-but-unlinked" files, that is files + * without user-visible names in the file system, but still opened by some + * active process. What happens here is that unlink proper (i.e., removal + * of the last file name) and file deletion (truncate of file body to zero + * and deletion of stat-data, that happens when last file descriptor is + * closed), may belong to different transactions T1 and T2. If a crash + * happens after T1 commit, but before T2 commit, on-disk file system has + * a file without name, that is, disk space leak. + * + * 2. Truncate. Truncate of large file may spawn multiple transactions. If + * system crashes while truncate was in-progress, file is left partially + * truncated, which violates "atomicity guarantees" of reiser4, viz. that + * every system is atomic. + * + * Safe-links address both above cases. Basically, safe-link is a way post + * some operation to be executed during commit of some other transaction than + * current one. (Another way to look at the safe-link is to interpret it as a + * logical logging.) + * + * Specifically, at the beginning of unlink safe-link in inserted in the + * tree. This safe-link is normally removed by file deletion code (during + * transaction T2 in the above terms). Truncate also inserts safe-link that is + * normally removed when truncate operation is finished. + * + * This means, that in the case of "clean umount" there are no safe-links in + * the tree. If safe-links are observed during mount, it means that (a) system + * was terminated abnormally, and (b) safe-link correspond to the "pending" + * (i.e., not finished) operations that were in-progress during system + * termination. Each safe-link record enough information to complete + * corresponding operation, and mount simply "replays" them (hence, the + * analogy with the logical logging). + * + * Safe-links are implemented as blackbox items (see + * plugin/item/blackbox.[ch]). + * + * For the reference: ext3 also has similar mechanism, it's called "an orphan + * list" there. + */ + +#include "safe_link.h" +#include "debug.h" +#include "inode.h" + +#include "plugin/item/blackbox.h" + +#include + +/* + * On-disk format of safe-link. + */ +typedef struct safelink { + reiser4_key sdkey; /* key of stat-data for the file safe-link is + * for */ + d64 size; /* size to which file should be truncated */ +} safelink_t; + +/* + * locality where safe-link items are stored. Next to the locality of root + * directory. + */ +static oid_t +safe_link_locality(reiser4_tree *tree) +{ + return get_inode_oid(tree->super->s_root->d_inode) + 1; +} + +/* + Construct a key for the safe-link. Key has the following format: + +| 60 | 4 | 64 | 4 | 60 | 64 | ++---------------+---+------------------+---+---------------+------------------+ +| locality | 0 | 0 | 0 | objectid | link type | ++---------------+---+------------------+---+---------------+------------------+ +| | | | | +| 8 bytes | 8 bytes | 8 bytes | 8 bytes | + + This is in large keys format. In small keys format second 8 byte chunk is + out. Locality is a constant returned by safe_link_locality(). objectid is + an oid of a file on which operation protected by this safe-link is + performed. link-type is used to distinguish safe-links for different + operations. + + */ +static reiser4_key * +build_link_key(struct inode *inode, reiser4_safe_link_t link, reiser4_key *key) +{ + key_init(key); + set_key_locality(key, safe_link_locality(tree_by_inode(inode))); + set_key_objectid(key, get_inode_oid(inode)); + set_key_offset(key, link); + return key; +} + +/* + * how much disk space is necessary to insert and remove (in the + * error-handling path) safe-link. + */ +reiser4_internal __u64 safe_link_tograb(reiser4_tree *tree) +{ + return + /* insert safe link */ + estimate_one_insert_item(tree) + + /* remove safe link */ + estimate_one_item_removal(tree) + + /* drill to the leaf level during insertion */ + 1 + estimate_one_insert_item(tree) + + /* + * possible update of existing safe-link. Actually, if + * safe-link existed already (we failed to remove it), then no + * insertion is necessary, so this term is already "covered", + * but for simplicity let's left it. + */ + 1; +} + +/* + * grab enough disk space to insert and remove (in the error-handling path) + * safe-link. + */ +reiser4_internal int safe_link_grab(reiser4_tree *tree, reiser4_ba_flags_t flags) +{ + int result; + + grab_space_enable(); + /* The sbinfo->delete semaphore can be taken here. + * safe_link_release() should be called before leaving reiser4 + * context. */ + result = reiser4_grab_reserved(tree->super, safe_link_tograb(tree), flags); + grab_space_enable(); + return result; +} + +/* + * release unused disk space reserved by safe_link_grab(). + */ +reiser4_internal void safe_link_release(reiser4_tree * tree) +{ + reiser4_release_reserved(tree->super); +} + +/* + * insert into tree safe-link for operation @link on inode @inode. + */ +reiser4_internal int safe_link_add(struct inode *inode, reiser4_safe_link_t link) +{ + reiser4_key key; + safelink_t sl; + int length; + int result; + reiser4_tree *tree; + + build_sd_key(inode, &sl.sdkey); + length = sizeof sl.sdkey; + + if (link == SAFE_TRUNCATE) { + /* + * for truncate we have to store final file length also, + * expand item. + */ + length += sizeof(sl.size); + cputod64(inode->i_size, &sl.size); + } + tree = tree_by_inode(inode); + build_link_key(inode, link, &key); + + result = store_black_box(tree, &key, &sl, length); + if (result == -EEXIST) + result = update_black_box(tree, &key, &sl, length); + return result; +} + +/* + * remove safe-link corresponding to the operation @link on inode @inode from + * the tree. + */ +reiser4_internal int safe_link_del(struct inode *inode, reiser4_safe_link_t link) +{ + reiser4_key key; + + return kill_black_box(tree_by_inode(inode), + build_link_key(inode, link, &key)); +} + +/* + * in-memory structure to keep information extracted from safe-link. This is + * used to iterate over all safe-links. + */ +typedef struct { + reiser4_tree *tree; /* internal tree */ + reiser4_key key; /* safe-link key*/ + reiser4_key sdkey; /* key of object stat-data */ + reiser4_safe_link_t link; /* safe-link type */ + oid_t oid; /* object oid */ + __u64 size; /* final size for truncate */ +} safe_link_context; + +/* + * start iterating over all safe-links. + */ +static void safe_link_iter_begin(reiser4_tree *tree, safe_link_context *ctx) +{ + ctx->tree = tree; + key_init(&ctx->key); + set_key_locality(&ctx->key, safe_link_locality(tree)); + set_key_objectid(&ctx->key, get_key_objectid(max_key())); + set_key_offset(&ctx->key, get_key_offset(max_key())); +} + +/* + * return next safe-link. + */ +static int safe_link_iter_next(safe_link_context *ctx) +{ + int result; + safelink_t sl; + + result = load_black_box(ctx->tree, + &ctx->key, &sl, sizeof sl, 0); + if (result == 0) { + ctx->oid = get_key_objectid(&ctx->key); + ctx->link = get_key_offset(&ctx->key); + ctx->sdkey = sl.sdkey; + if (ctx->link == SAFE_TRUNCATE) + ctx->size = d64tocpu(&sl.size); + } + return result; +} + +/* + * check are there any more safe-links left in the tree. + */ +static int safe_link_iter_finished(safe_link_context *ctx) +{ + return get_key_locality(&ctx->key) != safe_link_locality(ctx->tree); +} + + +/* + * finish safe-link iteration. + */ +static void safe_link_iter_end(safe_link_context *ctx) +{ + /* nothing special */ +} + +/* + * process single safe-link. + */ +static int process_safelink(struct super_block *super, reiser4_safe_link_t link, + reiser4_key *sdkey, oid_t oid, __u64 size) +{ + struct inode *inode; + int result; + + /* + * obtain object inode by reiser4_iget(), then call object plugin + * ->safelink() method to do actual work, then delete safe-link on + * success. + */ + + inode = reiser4_iget(super, sdkey, 1); + if (!IS_ERR(inode)) { + file_plugin *fplug; + + fplug = inode_file_plugin(inode); + assert("nikita-3428", fplug != NULL); + if (fplug->safelink != NULL) + result = fplug->safelink(inode, link, size); + else { + warning("nikita-3430", + "Cannot handle safelink for %lli", + (unsigned long long)oid); + print_key("key", sdkey); + print_inode("inode", inode); + result = 0; + } + if (result != 0) { + warning("nikita-3431", + "Error processing safelink for %lli: %i", + (unsigned long long)oid, result); + } + reiser4_iget_complete(inode); + iput(inode); + if (result == 0) { + result = safe_link_grab(tree_by_inode(inode), + BA_CAN_COMMIT); + if (result == 0) + result = safe_link_del(inode, link); + safe_link_release(tree_by_inode(inode)); + /* + * restart transaction: if there was large number of + * safe-links, their processing may fail to fit into + * single transaction. + */ + if (result == 0) + txn_restart_current(); + } + } else + result = PTR_ERR(inode); + return result; +} + +/* + * iterate over all safe-links in the file-system processing them one by one. + */ +reiser4_internal int process_safelinks(struct super_block *super) +{ + safe_link_context ctx; + int result; + + if (rofs_super(super)) + /* do nothing on the read-only file system */ + return 0; + safe_link_iter_begin(&get_super_private(super)->tree, &ctx); + result = 0; + do { + result = safe_link_iter_next(&ctx); + if (safe_link_iter_finished(&ctx) || result == -ENOENT) { + result = 0; + break; + } + if (result == 0) + result = process_safelink(super, ctx.link, + &ctx.sdkey, ctx.oid, ctx.size); + } while (result == 0); + safe_link_iter_end(&ctx); + return result; +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/safe_link.h 2004-09-03 00:57:05.371222712 -0700 @@ -0,0 +1,33 @@ +/* Copyright 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Safe-links. See safe_link.c for details. */ + +#if !defined( __FS_SAFE_LINK_H__ ) +#define __FS_SAFE_LINK_H__ + +#include "tree.h" +#include "tap.h" + +struct inode; + +__u64 safe_link_tograb(reiser4_tree *tree); +int safe_link_grab(reiser4_tree *tree, reiser4_ba_flags_t flags); +void safe_link_release(reiser4_tree *tree); +int safe_link_add(struct inode *inode, reiser4_safe_link_t link); +int safe_link_del(struct inode *inode, reiser4_safe_link_t link); + +int process_safelinks(struct super_block *super); + +/* __FS_SAFE_LINK_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/seal.c 2004-09-03 00:57:05.372222560 -0700 @@ -0,0 +1,234 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ +/* Seals implementation. */ +/* Seals are "weak" tree pointers. They are analogous to tree coords in + allowing to bypass tree traversal. But normal usage of coords implies that + node pointed to by coord is locked, whereas seals don't keep a lock (or + even a reference) to znode. In stead, each znode contains a version number, + increased on each znode modification. This version number is copied into a + seal when seal is created. Later, one can "validate" seal by calling + seal_validate(). If znode is in cache and its version number is still the + same, seal is "pristine" and coord associated with it can be re-used + immediately. + + If, on the other hand, znode is out of cache, or it is obviously different + one from the znode seal was initially attached to (for example, it is on + the different level, or is being removed from the tree), seal is + irreparably invalid ("burned") and tree traversal has to be repeated. + + Otherwise, there is some hope, that while znode was modified (and seal was + "broken" as a result), key attached to the seal is still in the node. This + is checked by first comparing this key with delimiting keys of node and, if + key is ok, doing intra-node lookup. + + Znode version is maintained in the following way: + + there is reiser4_tree.znode_epoch counter. Whenever new znode is created, + znode_epoch is incremented and its new value is stored in ->version field + of new znode. Whenever znode is dirtied (which means it was probably + modified), znode_epoch is also incremented and its new value is stored in + znode->version. This is done so, because just incrementing znode->version + on each update is not enough: it may so happen, that znode get deleted, new + znode is allocated for the same disk block and gets the same version + counter, tricking seal code into false positive. +*/ + +#include "forward.h" +#include "debug.h" +#include "key.h" +#include "coord.h" +#include "seal.h" +#include "plugin/item/item.h" +#include "plugin/node/node.h" +#include "jnode.h" +#include "znode.h" +#include "super.h" + +static znode *seal_node(const seal_t * seal); +static int seal_matches(const seal_t * seal, znode * node); + +/* initialise seal. This can be called several times on the same seal. @coord + and @key can be NULL. */ +reiser4_internal void +seal_init(seal_t * seal /* seal to initialise */ , + const coord_t * coord /* coord @seal will be attached to */ , + const reiser4_key * key UNUSED_ARG /* key @seal will be + * attached to */ ) +{ + assert("nikita-1886", seal != NULL); + xmemset(seal, 0, sizeof *seal); + if (coord != NULL) { + znode *node; + + node = coord->node; + assert("nikita-1987", node != NULL); + spin_lock_znode(node); + seal->version = node->version; + assert("nikita-1988", seal->version != 0); + seal->block = *znode_get_block(node); +#if REISER4_DEBUG + seal->coord = *coord; + if (key != NULL) + seal->key = *key; +#endif + spin_unlock_znode(node); + } +} + +/* finish with seal */ +reiser4_internal void +seal_done(seal_t * seal /* seal to clear */) +{ + assert("nikita-1887", seal != NULL); + seal->version = 0; +} + +/* true if seal was initialised */ +reiser4_internal int +seal_is_set(const seal_t * seal /* seal to query */ ) +{ + assert("nikita-1890", seal != NULL); + return seal->version != 0; +} + +#if REISER4_DEBUG +/* helper function for seal_validate(). It checks that item at @coord has + * expected key. This is to detect cases where node was modified but wasn't + * marked dirty. */ +static inline int +check_seal_match(const coord_t * coord /* coord to check */, + const reiser4_key * k /* expected key */) +{ + reiser4_key ukey; + + return (coord->between != AT_UNIT) || + /* FIXME-VS: we only can compare keys for items whose units + represent exactly one key */ + (coord_is_existing_unit(coord) && (item_is_extent(coord) || keyeq(k, unit_key_by_coord(coord, &ukey)))); +} +#endif + + +/* this is used by seal_validate. It accepts return value of + * longterm_lock_znode and returns 1 if it can be interpreted as seal + * validation failure. For instance, when longterm_lock_znode returns -EINVAL, + * seal_validate returns -E_REPEAT and caller will call tre search. We cannot + * do this in longterm_lock_znode(), because sometimes we want to distinguish + * between -EINVAL and -E_REPEAT. */ +static int +should_repeat(int return_code) +{ + return return_code == -EINVAL; +} + +/* (re-)validate seal. + + Checks whether seal is pristine, and try to revalidate it if possible. + + If seal was burned, or broken irreparably, return -E_REPEAT. + + NOTE-NIKITA currently seal_validate() returns -E_REPEAT if key we are + looking for is in range of keys covered by the sealed node, but item wasn't + found by node ->lookup() method. Alternative is to return -ENOENT in this + case, but this would complicate callers logic. + +*/ +reiser4_internal int +seal_validate(seal_t * seal /* seal to validate */ , + coord_t * coord /* coord to validate against */ , + const reiser4_key * key /* key to validate against */ , + tree_level level /* level of node */ , + lock_handle * lh /* resulting lock handle */ , + lookup_bias bias /* search bias */ , + znode_lock_mode mode /* lock node */ , + znode_lock_request request /* locking priority */ ) +{ + znode *node; + int result; + + assert("nikita-1889", seal != NULL); + assert("nikita-1881", seal_is_set(seal)); + assert("nikita-1882", key != NULL); + assert("nikita-1883", coord != NULL); + assert("nikita-1884", lh != NULL); + assert("nikita-1885", keyeq(&seal->key, key)); + assert("nikita-1989", coords_equal(&seal->coord, coord)); + + /* obtain znode by block number */ + node = seal_node(seal); + if (node != NULL) { + /* znode was in cache, lock it */ + result = longterm_lock_znode(lh, node, mode, request); + zput(node); + if (result == 0) { + if (seal_matches(seal, node)) { + /* if seal version and znode version + coincide */ + ON_DEBUG(coord_update_v(coord)); + assert("nikita-1990", node == seal->coord.node); + assert("nikita-1898", WITH_DATA_RET(coord->node, 1, check_seal_match(coord, key))); + reiser4_stat_inc(seal.perfect_match); + } else + result = RETERR(-E_REPEAT); + } + if (result != 0) { + if (should_repeat(result)) + result = RETERR(-E_REPEAT); + /* unlock node on failure */ + done_lh(lh); + } + } else { + /* znode wasn't in cache */ + reiser4_stat_inc(seal.out_of_cache); + result = RETERR(-E_REPEAT); + } + return result; +} + +/* helpers functions */ + +/* obtain reference to znode seal points to, if in cache */ +static znode * +seal_node(const seal_t * seal /* seal to query */ ) +{ + assert("nikita-1891", seal != NULL); + return zlook(current_tree, &seal->block); +} + +/* true if @seal version and @node version coincide */ +static int +seal_matches(const seal_t * seal /* seal to check */ , + znode * node /* node to check */ ) +{ + assert("nikita-1991", seal != NULL); + assert("nikita-1993", node != NULL); + + return UNDER_SPIN(jnode, ZJNODE(node), (seal->version == node->version)); +} + +#if REISER4_DEBUG_OUTPUT +/* debugging function: print human readable form of @seal. */ +reiser4_internal void +print_seal(const char *prefix, const seal_t * seal) +{ + if (seal == NULL) { + printk("%s: null seal\n", prefix); + } else { + printk("%s: version: %llu, block: %llu\n", prefix, seal->version, seal->block); +#if REISER4_DEBUG + print_key("seal key", &seal->key); + print_coord("seal coord", &seal->coord, 0); +#endif + } +} +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/seal.h 2004-09-03 00:57:05.373222408 -0700 @@ -0,0 +1,59 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Declaration of seals: "weak" tree pointers. See seal.c for comments. */ + +#ifndef __SEAL_H__ +#define __SEAL_H__ + +#include "forward.h" +#include "debug.h" +#include "dformat.h" +#include "key.h" +#include "coord.h" + +/* for __u?? types */ +#include + +/* seal. See comment at the top of seal.c */ +typedef struct seal_s { + /* version of znode recorder at the time of seal creation */ + __u64 version; + /* block number of znode attached to this seal */ + reiser4_block_nr block; +#if REISER4_DEBUG + /* coord this seal is attached to. For debugging. */ + coord_t coord; + /* key this seal is attached to. For debugging. */ + reiser4_key key; +#endif +} seal_t; + +extern void seal_init(seal_t * seal, const coord_t * coord, const reiser4_key * key); +extern void seal_done(seal_t * seal); + +extern int seal_is_set(const seal_t * seal); + +extern int seal_validate(seal_t * seal, + coord_t * coord, + const reiser4_key * key, + tree_level level, + lock_handle * lh, lookup_bias bias, znode_lock_mode mode, znode_lock_request request); + +#if REISER4_DEBUG_OUTPUT +extern void print_seal(const char *prefix, const seal_t * seal); +#else +#define print_seal( prefix, seal ) noop +#endif + +/* __SEAL_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/search.c 2004-09-03 00:57:07.760859432 -0700 @@ -0,0 +1,1689 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +#include "forward.h" +#include "debug.h" +#include "dformat.h" +#include "key.h" +#include "coord.h" +#include "seal.h" +#include "plugin/item/item.h" +#include "plugin/node/node.h" +#include "plugin/plugin.h" +#include "jnode.h" +#include "znode.h" +#include "block_alloc.h" +#include "tree_walk.h" +#include "tree.h" +#include "log.h" +#include "reiser4.h" +#include "super.h" +#include "prof.h" +#include "inode.h" + +#include + +/* tree searching algorithm, intranode searching algorithms are in + plugin/node/ */ + +/* tree lookup cache + * + * The coord by key cache consists of small list of recently accessed nodes + * maintained according to the LRU discipline. Before doing real top-to-down + * tree traversal this cache is scanned for nodes that can contain key + * requested. + * + * The efficiency of coord cache depends heavily on locality of reference for + * tree accesses. Our user level simulations show reasonably good hit ratios + * for coord cache under most loads so far. + */ + +/* Initialise coord cache slot */ +static void +cbk_cache_init_slot(cbk_cache_slot * slot) +{ + assert("nikita-345", slot != NULL); + + cbk_cache_list_clean(slot); + slot->node = NULL; +} + +/* Initialise coord cache */ +reiser4_internal int +cbk_cache_init(cbk_cache * cache /* cache to init */ ) +{ + int i; + + assert("nikita-346", cache != NULL); + + cache->slot = kmalloc(sizeof (cbk_cache_slot) * cache->nr_slots, GFP_KERNEL); + if (cache->slot == NULL) + return RETERR(-ENOMEM); + + cbk_cache_list_init(&cache->lru); + for (i = 0; i < cache->nr_slots; ++i) { + cbk_cache_init_slot(cache->slot + i); + cbk_cache_list_push_back(&cache->lru, cache->slot + i); + } + rw_cbk_cache_init(cache); + return 0; +} + +/* free cbk cache data */ +reiser4_internal void +cbk_cache_done(cbk_cache * cache /* cache to release */ ) +{ + assert("nikita-2493", cache != NULL); + if (cache->slot != NULL) { + kfree(cache->slot); + cache->slot = NULL; + } +} + +/* macro to iterate over all cbk cache slots */ +#define for_all_slots( cache, slot ) \ + for( ( slot ) = cbk_cache_list_front( &( cache ) -> lru ) ; \ + !cbk_cache_list_end( &( cache ) -> lru, ( slot ) ) ; \ + ( slot ) = cbk_cache_list_next( slot ) ) + +#if REISER4_DEBUG_OUTPUT +/* Debugging aid: print human readable information about @slot */ +reiser4_internal void +print_cbk_slot(const char *prefix /* prefix to print */ , + const cbk_cache_slot * slot /* slot to print */ ) +{ + if (slot == NULL) + printk("%s: null slot\n", prefix); + else + print_znode("node", slot->node); +} + +/* Debugging aid: print human readable information about @cache */ +reiser4_internal void +print_cbk_cache(const char *prefix /* prefix to print */ , + const cbk_cache * cache /* cache to print */ ) +{ + if (cache == NULL) + printk("%s: null cache\n", prefix); + else { + cbk_cache_slot *scan; + + printk("%s: cache: %p\n", prefix, cache); + for_all_slots(cache, scan) + print_cbk_slot("slot", scan); + } +} +#endif + +#if REISER4_DEBUG +/* this function assures that [cbk-cache-invariant] invariant holds */ +static int +cbk_cache_invariant(const cbk_cache * cache) +{ + cbk_cache_slot *slot; + int result; + int unused; + + if (cache->nr_slots == 0) + return 1; + + assert("nikita-2469", cache != NULL); + unused = 0; + result = 1; + read_lock_cbk_cache((cbk_cache *) cache); + for_all_slots(cache, slot) { + /* in LRU first go all `used' slots followed by `unused' */ + if (unused && (slot->node != NULL)) + result = 0; + if (slot->node == NULL) + unused = 1; + else { + cbk_cache_slot *scan; + + /* all cached nodes are different */ + scan = slot; + while (result) { + scan = cbk_cache_list_next(scan); + if (cbk_cache_list_end(&cache->lru, scan)) + break; + if (slot->node == scan->node) + result = 0; + } + } + if (!result) + break; + } + read_unlock_cbk_cache((cbk_cache *) cache); + return result; +} + +#endif + +/* Remove references, if any, to @node from coord cache */ +reiser4_internal void +cbk_cache_invalidate(const znode * node /* node to remove from cache */ , + reiser4_tree * tree /* tree to remove node from */ ) +{ + cbk_cache_slot *slot; + cbk_cache *cache; + int i; + + assert("nikita-350", node != NULL); + assert("nikita-1479", LOCK_CNT_GTZ(rw_locked_tree)); + + cache = &tree->cbk_cache; + assert("nikita-2470", cbk_cache_invariant(cache)); + + write_lock_cbk_cache(cache); + for (i = 0, slot = cache->slot; i < cache->nr_slots; ++ i, ++ slot) { + if (slot->node == node) { + cbk_cache_list_remove(slot); + cbk_cache_list_push_back(&cache->lru, slot); + slot->node = NULL; + break; + } + } + write_unlock_cbk_cache(cache); + assert("nikita-2471", cbk_cache_invariant(cache)); +} + +/* add to the cbk-cache in the "tree" information about "node". This + can actually be update of existing slot in a cache. */ +reiser4_internal void +cbk_cache_add(const znode * node /* node to add to the cache */ ) +{ + cbk_cache *cache; + cbk_cache_slot *slot; + int i; + + assert("nikita-352", node != NULL); + + cache = &znode_get_tree(node)->cbk_cache; + assert("nikita-2472", cbk_cache_invariant(cache)); + + if (cache->nr_slots == 0) + return; + + write_lock_cbk_cache(cache); + /* find slot to update/add */ + for (i = 0, slot = cache->slot; i < cache->nr_slots; ++ i, ++ slot) { + /* oops, this node is already in a cache */ + if (slot->node == node) + break; + } + /* if all slots are used, reuse least recently used one */ + if (i == cache->nr_slots) { + slot = cbk_cache_list_back(&cache->lru); + slot->node = (znode *) node; + } + cbk_cache_list_remove(slot); + cbk_cache_list_push_front(&cache->lru, slot); + write_unlock_cbk_cache(cache); + assert("nikita-2473", cbk_cache_invariant(cache)); +} + +static int setup_delimiting_keys(cbk_handle * h); +static lookup_result coord_by_handle(cbk_handle * handle); +static lookup_result traverse_tree(cbk_handle * h); +static int cbk_cache_search(cbk_handle * h); + +static level_lookup_result cbk_level_lookup(cbk_handle * h); +static level_lookup_result cbk_node_lookup(cbk_handle * h); + +/* helper functions */ + +static void update_stale_dk(reiser4_tree *tree, znode *node); + +/* release parent node during traversal */ +static void put_parent(cbk_handle * h); +/* check consistency of fields */ +static int sanity_check(cbk_handle * h); +/* release resources in handle */ +static void hput(cbk_handle * h); + +static level_lookup_result search_to_left(cbk_handle * h); + +/* pack numerous (numberous I should say) arguments of coord_by_key() into + * cbk_handle */ +reiser4_internal cbk_handle *cbk_pack(cbk_handle *handle, + reiser4_tree * tree, + const reiser4_key * key, + coord_t * coord, + lock_handle * active_lh, + lock_handle * parent_lh, + znode_lock_mode lock_mode, + lookup_bias bias, + tree_level lock_level, + tree_level stop_level, + __u32 flags, + ra_info_t *info) +{ + xmemset(handle, 0, sizeof *handle); + + handle->tree = tree; + handle->key = key; + handle->lock_mode = lock_mode; + handle->bias = bias; + handle->lock_level = lock_level; + handle->stop_level = stop_level; + handle->coord = coord; + /* set flags. See comment in tree.h:cbk_flags */ + handle->flags = flags | CBK_TRUST_DK | CBK_USE_CRABLOCK; + + handle->active_lh = active_lh; + handle->parent_lh = parent_lh; + handle->ra_info = info; + return handle; +} + +/* main tree lookup procedure + + Check coord cache. If key we are looking for is not found there, call cbk() + to do real tree traversal. + + As we have extents on the twig level, @lock_level and @stop_level can + be different from LEAF_LEVEL and each other. + + Thread cannot keep any reiser4 locks (tree, znode, dk spin-locks, or znode + long term locks) while calling this. +*/ +reiser4_internal lookup_result +coord_by_key(reiser4_tree * tree /* tree to perform search + * in. Usually this tree is + * part of file-system + * super-block */ , + const reiser4_key * key /* key to look for */ , + coord_t * coord /* where to store found + * position in a tree. Fields + * in "coord" are only valid if + * coord_by_key() returned + * "CBK_COORD_FOUND" */ , + lock_handle * lh, /* resulting lock handle */ + znode_lock_mode lock_mode /* type of lookup we + * want on node. Pass + * ZNODE_READ_LOCK here + * if you only want to + * read item found and + * ZNODE_WRITE_LOCK if + * you want to modify + * it */ , + lookup_bias bias /* what to return if coord + * with exactly the @key is + * not in the tree */ , + tree_level lock_level /* tree level where to start + * taking @lock type of + * locks */ , + tree_level stop_level /* tree level to stop. Pass + * LEAF_LEVEL or TWIG_LEVEL + * here Item being looked + * for has to be between + * @lock_level and + * @stop_level, inclusive */ , + __u32 flags /* search flags */, + ra_info_t *info /* information about desired tree traversal readahead */) +{ + cbk_handle handle; + lock_handle parent_lh; + lookup_result result; + + init_lh(lh); + init_lh(&parent_lh); + + assert("nikita-3023", schedulable()); + + assert("nikita-353", tree != NULL); + assert("nikita-354", key != NULL); + assert("nikita-355", coord != NULL); + assert("nikita-356", (bias == FIND_EXACT) || (bias == FIND_MAX_NOT_MORE_THAN)); + assert("nikita-357", stop_level >= LEAF_LEVEL); + + if (!lock_stack_isclean(get_current_lock_stack())) + print_clog(); + + /* no locks can be held during tree traversal */ + assert("nikita-2104", lock_stack_isclean(get_current_lock_stack())); + trace_stamp(TRACE_TREE); + + cbk_pack(&handle, + tree, + key, + coord, + lh, + &parent_lh, + lock_mode, + bias, + lock_level, + stop_level, + flags, + info); + + result = coord_by_handle(&handle); + assert("nikita-3247", ergo(!IS_CBKERR(result), coord->node == lh->node)); + return result; +} + +/* like coord_by_key(), but starts traversal from vroot of @object rather than + * from tree root. */ +reiser4_internal lookup_result +object_lookup(struct inode *object, + const reiser4_key * key, + coord_t * coord, + lock_handle * lh, + znode_lock_mode lock_mode, + lookup_bias bias, + tree_level lock_level, + tree_level stop_level, + __u32 flags, + ra_info_t *info) +{ + cbk_handle handle; + lock_handle parent_lh; + lookup_result result; + + init_lh(lh); + init_lh(&parent_lh); + + assert("nikita-3023", schedulable()); + + assert("nikita-354", key != NULL); + assert("nikita-355", coord != NULL); + assert("nikita-356", (bias == FIND_EXACT) || (bias == FIND_MAX_NOT_MORE_THAN)); + assert("nikita-357", stop_level >= LEAF_LEVEL); + + if (!lock_stack_isclean(get_current_lock_stack())) + print_clog(); + + /* no locks can be held during tree search by key */ + assert("nikita-2104", lock_stack_isclean(get_current_lock_stack())); + trace_stamp(TRACE_TREE); + + cbk_pack(&handle, + object != NULL ? tree_by_inode(object) : current_tree, + key, + coord, + lh, + &parent_lh, + lock_mode, + bias, + lock_level, + stop_level, + flags, + info); + handle.object = object; + + result = coord_by_handle(&handle); + assert("nikita-3247", ergo(!IS_CBKERR(result), coord->node == lh->node)); + return result; +} + +/* lookup by cbk_handle. Common part of coord_by_key() and object_lookup(). */ +static lookup_result +coord_by_handle(cbk_handle * handle) +{ + /* + * first check cbk_cache (which is look-aside cache for our tree) and + * of this fails, start traversal. + */ + + write_tree_log(handle->tree, tree_lookup, handle->key); + + /* first check whether "key" is in cache of recent lookups. */ + if (cbk_cache_search(handle) == 0) + return handle->result; + else + return traverse_tree(handle); +} + +/* Execute actor for each item (or unit, depending on @through_units_p), + starting from @coord, right-ward, until either: + + - end of the tree is reached + - unformatted node is met + - error occurred + - @actor returns 0 or less + + Error code, or last actor return value is returned. + + This is used by plugin/dir/hashe_dir.c:find_entry() to move through + sequence of entries with identical keys and alikes. +*/ +reiser4_internal int +iterate_tree(reiser4_tree * tree /* tree to scan */ , + coord_t * coord /* coord to start from */ , + lock_handle * lh /* lock handle to start with and to + * update along the way */ , + tree_iterate_actor_t actor /* function to call on each + * item/unit */ , + void *arg /* argument to pass to @actor */ , + znode_lock_mode mode /* lock mode on scanned nodes */ , + int through_units_p /* call @actor on each item or on each + * unit */ ) +{ + int result; + + assert("nikita-1143", tree != NULL); + assert("nikita-1145", coord != NULL); + assert("nikita-1146", lh != NULL); + assert("nikita-1147", actor != NULL); + + result = zload(coord->node); + coord_clear_iplug(coord); + if (result != 0) + return result; + if (!coord_is_existing_unit(coord)) { + zrelse(coord->node); + return -ENOENT; + } + while ((result = actor(tree, coord, lh, arg)) > 0) { + /* move further */ + if ((through_units_p && coord_next_unit(coord)) || + (!through_units_p && coord_next_item(coord))) { + do { + lock_handle couple; + + /* move to the next node */ + init_lh(&couple); + result = reiser4_get_right_neighbor( + &couple, coord->node, (int) mode, GN_CAN_USE_UPPER_LEVELS); + zrelse(coord->node); + if (result == 0) { + + result = zload(couple.node); + if (result != 0) { + done_lh(&couple); + return result; + } + + coord_init_first_unit(coord, couple.node); + done_lh(lh); + move_lh(lh, &couple); + } else + return result; + } while (node_is_empty(coord->node)); + } + + assert("nikita-1149", coord_is_existing_unit(coord)); + } + zrelse(coord->node); + return result; +} + +/* return locked uber znode for @tree */ +reiser4_internal int get_uber_znode(reiser4_tree * tree, znode_lock_mode mode, + znode_lock_request pri, lock_handle *lh) +{ + int result; + + result = longterm_lock_znode(lh, tree->uber, mode, pri); + return result; +} + +/* true if @key is strictly within @node + + we are looking for possibly non-unique key and it is item is at the edge of + @node. May be it is in the neighbor. +*/ +static int +znode_contains_key_strict(znode * node /* node to check key + * against */ , + const reiser4_key * key /* key to check */, + int isunique) +{ + int answer; + + assert("nikita-1760", node != NULL); + assert("nikita-1722", key != NULL); + + if (keyge(key, &node->rd_key)) + return 0; + + answer = keycmp(&node->ld_key, key); + + if (isunique) + return answer != GREATER_THAN; + else + return answer == LESS_THAN; +} + +/* + * Virtual Root (vroot) code. + * + * For given file system object (e.g., regular file or directory) let's + * define its "virtual root" as lowest in the tree (that is, furtherest + * from the tree root) node such that all body items of said object are + * located in a tree rooted at this node. + * + * Once vroot of object is found all tree lookups for items within body of + * this object ("object lookups") can be started from its vroot rather + * than from real root. This has following advantages: + * + * 1. amount of nodes traversed during lookup (and, hence, amount of + * key comparisons made) decreases, and + * + * 2. contention on tree root is decreased. This latter was actually + * motivating reason behind vroot, because spin lock of root node, + * which is taken when acquiring long-term lock on root node is the + * hottest lock in the reiser4. + * + * How to find vroot. + * + * When vroot of object F is not yet determined, all object lookups start + * from the root of the tree. At each tree level during traversal we have + * a node N such that a key we are looking for (which is the key inside + * object's body) is located within N. In function handle_vroot() called + * from cbk_level_lookup() we check whether N is possible vroot for + * F. Check is trivial---if neither leftmost nor rightmost item of N + * belongs to F (and we already have helpful ->owns_item() method of + * object plugin for this), then N is possible vroot of F. This, of + * course, relies on the assumption that each object occupies contiguous + * range of keys in the tree. + * + * Thus, traversing tree downward and checking each node as we go, we can + * find lowest such node, which, by definition, is vroot. + * + * How to track vroot. + * + * Nohow. If actual vroot changes, next object lookup will just restart + * from the actual tree root, refreshing object's vroot along the way. + * + */ + +/* + * Check whether @node is possible vroot of @object. + */ +static void +handle_vroot(struct inode *object, znode *node) +{ + file_plugin *fplug; + coord_t coord; + + fplug = inode_file_plugin(object); + assert("nikita-3353", fplug != NULL); + assert("nikita-3354", fplug->owns_item != NULL); + + if (unlikely(node_is_empty(node))) + return; + + coord_init_first_unit(&coord, node); + /* + * if leftmost item of @node belongs to @object, we cannot be sure + * that @node is vroot of @object, because, some items of @object are + * probably in the sub-tree rooted at the left neighbor of @node. + */ + if (fplug->owns_item(object, &coord)) + return; + coord_init_last_unit(&coord, node); + /* mutatis mutandis for the rightmost item */ + if (fplug->owns_item(object, &coord)) + return; + /* otherwise, @node is possible vroot of @object */ + inode_set_vroot(object, node); +} + +/* + * helper function used by traverse tree to start tree traversal not from the + * tree root, but from @h->object's vroot, if possible. + */ +static int +prepare_object_lookup(cbk_handle * h) +{ + znode *vroot; + int result; + + vroot = inode_get_vroot(h->object); + if (vroot == NULL) { + /* + * object doesn't have known vroot, start from real tree root. + */ + reiser4_stat_inc(tree.object_lookup_novroot); + return LOOKUP_CONT; + } + + h->level = znode_get_level(vroot); + /* take a long-term lock on vroot */ + h->result = longterm_lock_znode(h->active_lh, vroot, + cbk_lock_mode(h->level, h), + ZNODE_LOCK_LOPRI); + result = LOOKUP_REST; + if (h->result == 0) { + int isunique; + int inside; + + isunique = h->flags & CBK_UNIQUE; + /* check that key is inside vroot */ + inside = + UNDER_RW(dk, h->tree, read, + znode_contains_key_strict(vroot, + h->key, + isunique)) && + !ZF_ISSET(vroot, JNODE_HEARD_BANSHEE); + if (inside) { + h->result = zload(vroot); + if (h->result == 0) { + /* search for key in vroot. */ + result = cbk_node_lookup(h); + zrelse(vroot);/*h->active_lh->node);*/ + if (h->active_lh->node != vroot) { + result = LOOKUP_REST; + reiser4_stat_inc(tree.object_lookup_moved); + } else if (result == LOOKUP_CONT) { + move_lh(h->parent_lh, h->active_lh); + h->flags &= ~CBK_DKSET; + } + } + } else + /* vroot is not up-to-date. Restart. */ + reiser4_stat_inc(tree.object_lookup_outside); + } else + /* long-term locking failed. Restart. */ + reiser4_stat_inc(tree.object_lookup_cannotlock); + + zput(vroot); + + if (IS_CBKERR(h->result) || result == LOOKUP_REST) + hput(h); + if (result != LOOKUP_REST) + reiser4_stat_inc_at_level(h->level, object_lookup_start); + return result; +} + +/* main function that handles common parts of tree traversal: starting + (fake znode handling), restarts, error handling, completion */ +static lookup_result +traverse_tree(cbk_handle * h /* search handle */ ) +{ + int done; + int iterations; + int vroot_used; + + assert("nikita-365", h != NULL); + assert("nikita-366", h->tree != NULL); + assert("nikita-367", h->key != NULL); + assert("nikita-368", h->coord != NULL); + assert("nikita-369", (h->bias == FIND_EXACT) || (h->bias == FIND_MAX_NOT_MORE_THAN)); + assert("nikita-370", h->stop_level >= LEAF_LEVEL); + assert("nikita-2949", !(h->flags & CBK_DKSET)); + assert("zam-355", lock_stack_isclean(get_current_lock_stack())); + + trace_stamp(TRACE_TREE); + reiser4_stat_inc(tree.cbk); + + done = 0; + iterations = 0; + vroot_used = 0; + + /* loop for restarts */ +restart: + + assert("nikita-3024", schedulable()); + + h->result = CBK_COORD_FOUND; + /* connect_znode() needs it */ + h->ld_key = *min_key(); + h->rd_key = *max_key(); + h->flags |= CBK_DKSET; + h->error = NULL; + + if (!vroot_used && h->object != NULL) { + vroot_used = 1; + done = prepare_object_lookup(h); + if (done == LOOKUP_REST) { + reiser4_stat_inc(tree.object_lookup_restart); + goto restart; + } else if (done == LOOKUP_DONE) + return h->result; + } + if (h->parent_lh->node == NULL) { + done = get_uber_znode(h->tree, ZNODE_READ_LOCK, ZNODE_LOCK_LOPRI, + h->parent_lh); + + assert("nikita-1637", done != -E_DEADLOCK); + + h->block = h->tree->root_block; + h->level = h->tree->height; + h->coord->node = h->parent_lh->node; + + if (done != 0) + return done; + } + + /* loop descending a tree */ + while (!done) { + + if (unlikely((iterations > REISER4_CBK_ITERATIONS_LIMIT) && + IS_POW(iterations))) { + warning("nikita-1481", "Too many iterations: %i", iterations); + print_key("key", h->key); + ++iterations; + } else if (unlikely(iterations > REISER4_MAX_CBK_ITERATIONS)) { + h->error = + "reiser-2018: Too many iterations. Tree corrupted, or (less likely) starvation occurring."; + h->result = RETERR(-EIO); + break; + } + switch (cbk_level_lookup(h)) { + case LOOKUP_CONT: + move_lh(h->parent_lh, h->active_lh); + continue; + default: + wrong_return_value("nikita-372", "cbk_level"); + case LOOKUP_DONE: + done = 1; + break; + case LOOKUP_REST: + reiser4_stat_inc(tree.cbk_restart); + hput(h); + /* deadlock avoidance is normal case. */ + if (h->result != -E_DEADLOCK) + ++iterations; + preempt_point(); + goto restart; + } + } + /* that's all. The rest is error handling */ + if (unlikely(h->error != NULL)) { + warning("nikita-373", "%s: level: %i, " + "lock_level: %i, stop_level: %i " + "lock_mode: %s, bias: %s", + h->error, h->level, h->lock_level, h->stop_level, + lock_mode_name(h->lock_mode), bias_name(h->bias)); + print_address("block", &h->block); + print_key("key", h->key); + print_coord_content("coord", h->coord); + print_znode("active", h->active_lh->node); + print_znode("parent", h->parent_lh->node); + } + /* `unlikely' error case */ + if (unlikely(IS_CBKERR(h->result))) { + /* failure. do cleanup */ + hput(h); + } else { + assert("nikita-1605", WITH_DATA_RET + (h->coord->node, 1, + ergo((h->result == CBK_COORD_FOUND) && + (h->bias == FIND_EXACT) && + (!node_is_empty(h->coord->node)), coord_is_existing_item(h->coord)))); + } + write_tree_log(h->tree, tree_exit); + return h->result; +} + +/* find delimiting keys of child + + Determine left and right delimiting keys for child pointed to by + @parent_coord. + +*/ +static void +find_child_delimiting_keys(znode * parent /* parent znode, passed + * locked */ , + const coord_t * parent_coord /* coord where + * pointer to + * child is + * stored */ , + reiser4_key * ld /* where to store left + * delimiting key */ , + reiser4_key * rd /* where to store right + * delimiting key */ ) +{ + coord_t neighbor; + + assert("nikita-1484", parent != NULL); + assert("nikita-1485", rw_dk_is_locked(znode_get_tree(parent))); + + coord_dup(&neighbor, parent_coord); + + if (neighbor.between == AT_UNIT) + /* imitate item ->lookup() behavior. */ + neighbor.between = AFTER_UNIT; + + if (coord_is_existing_unit(&neighbor) || + coord_set_to_left(&neighbor) == 0) + unit_key_by_coord(&neighbor, ld); + else + *ld = *znode_get_ld_key(parent); + + coord_dup(&neighbor, parent_coord); + if (neighbor.between == AT_UNIT) + neighbor.between = AFTER_UNIT; + if (coord_set_to_right(&neighbor) == 0) + unit_key_by_coord(&neighbor, rd); + else + *rd = *znode_get_rd_key(parent); +} + +/* + * setup delimiting keys for a child + * + * @parent parent node + * + * @coord location in @parent where pointer to @child is + * + * @child child node + */ +reiser4_internal int +set_child_delimiting_keys(znode * parent, + const coord_t * coord, znode * child) +{ + reiser4_tree *tree; + int result; + + assert("nikita-2952", + znode_get_level(parent) == znode_get_level(coord->node)); + + tree = znode_get_tree(parent); + result = 0; + /* fast check without taking dk lock. This is safe, because + * JNODE_DKSET is never cleared once set. */ + if (!ZF_ISSET(child, JNODE_DKSET)) { + WLOCK_DK(tree); + if (likely(!ZF_ISSET(child, JNODE_DKSET))) { + find_child_delimiting_keys(parent, coord, + znode_get_ld_key(child), + znode_get_rd_key(child)); + ZF_SET(child, JNODE_DKSET); + result = 1; + } + WUNLOCK_DK(tree); + } + return result; +} + +/* Perform tree lookup at one level. This is called from cbk_traverse() + function that drives lookup through tree and calls cbk_node_lookup() to + perform lookup within one node. + + See comments in a code. +*/ +static level_lookup_result +cbk_level_lookup(cbk_handle * h /* search handle */ ) +{ + int ret; + int setdk; + int ldkeyset = 0; + reiser4_key ldkey; + reiser4_key key; + znode *active; + + assert("nikita-3025", schedulable()); + + /* acquire reference to @active node */ + active = zget(h->tree, &h->block, h->parent_lh->node, h->level, GFP_KERNEL); + + if (IS_ERR(active)) { + h->result = PTR_ERR(active); + return LOOKUP_DONE; + } + + /* lock @active */ + h->result = longterm_lock_znode(h->active_lh, + active, + cbk_lock_mode(h->level, h), + ZNODE_LOCK_LOPRI); + /* longterm_lock_znode() acquires additional reference to znode (which + will be later released by longterm_unlock_znode()). Release + reference acquired by zget(). + */ + zput(active); + if (unlikely(h->result != 0)) + goto fail_or_restart; + + setdk = 0; + /* if @active is accessed for the first time, setup delimiting keys on + it. Delimiting keys are taken from the parent node. See + setup_delimiting_keys() for details. + */ + if (h->flags & CBK_DKSET) { + setdk = setup_delimiting_keys(h); + h->flags &= ~CBK_DKSET; + } else { + znode *parent; + + parent = h->parent_lh->node; + h->result = zload(parent); + if (unlikely(h->result != 0)) + goto fail_or_restart; + + if (!ZF_ISSET(active, JNODE_DKSET)) + setdk = set_child_delimiting_keys(parent, + h->coord, active); + else { + UNDER_RW_VOID(dk, h->tree, read, + find_child_delimiting_keys(parent, + h->coord, + &ldkey, &key)); + ldkeyset = 1; + } + zrelse(parent); + } + + /* this is ugly kludge. Reminder: this is necessary, because + ->lookup() method returns coord with ->between field probably set + to something different from AT_UNIT. + */ + h->coord->between = AT_UNIT; + + if (znode_just_created(active) && (h->coord->node != NULL)) { + WLOCK_TREE(h->tree); + /* if we are going to load znode right now, setup + ->in_parent: coord where pointer to this node is stored in + parent. + */ + coord_to_parent_coord(h->coord, &active->in_parent); + WUNLOCK_TREE(h->tree); + } + + /* check connectedness without holding tree lock---false negatives + * will be re-checked by connect_znode(), and false positives are + * impossible---@active cannot suddenly turn into unconnected + * state. */ + if (!znode_is_connected(active)) { + h->result = connect_znode(h->coord, active); + if (unlikely(h->result != 0)) { + put_parent(h); + goto fail_or_restart; + } + } + + jload_prefetch(ZJNODE(active)); + + if (setdk) + update_stale_dk(h->tree, active); + + /* put_parent() cannot be called earlier, because connect_znode() + assumes parent node is referenced; */ + put_parent(h); + + if ((!znode_contains_key_lock(active, h->key) && + (h->flags & CBK_TRUST_DK)) || ZF_ISSET(active, JNODE_HEARD_BANSHEE)) { + /* 1. key was moved out of this node while this thread was + waiting for the lock. Restart. More elaborate solution is + to determine where key moved (to the left, or to the right) + and try to follow it through sibling pointers. + + 2. or, node itself is going to be removed from the + tree. Release lock and restart. + */ + if (REISER4_STATS) { + if (znode_contains_key_lock(active, h->key)) + reiser4_stat_inc_at_level(h->level, cbk_met_ghost); + else + reiser4_stat_inc_at_level(h->level, cbk_key_moved); + } + h->result = -E_REPEAT; + } + if (h->result == -E_REPEAT) + return LOOKUP_REST; + + h->result = zload_ra(active, h->ra_info); + if (h->result) { + return LOOKUP_DONE; + } + + /* sanity checks */ + if (sanity_check(h)) { + zrelse(active); + return LOOKUP_DONE; + } + + /* check that key of leftmost item in the @active is the same as in + * its parent */ + if (ldkeyset && !node_is_empty(active) && + !keyeq(leftmost_key_in_node(active, &key), &ldkey)) { + warning("vs-3533", "Keys are inconsistent. Fsck?"); + print_node_content("child", active, ~0); + print_key("inparent", &ldkey); + print_key("inchild", &key); + h->result = RETERR(-EIO); + zrelse(active); + return LOOKUP_DONE; + } + + if (h->object != NULL) + handle_vroot(h->object, active); + + ret = cbk_node_lookup(h); + + /* reget @active from handle, because it can change in + cbk_node_lookup() */ + /*active = h->active_lh->node;*/ + zrelse(active); + + return ret; + +fail_or_restart: + if (h->result == -E_DEADLOCK) + return LOOKUP_REST; + return LOOKUP_DONE; +} + +#if REISER4_DEBUG +/* check left and right delimiting keys of a znode */ +void +check_dkeys(const znode *node) +{ + znode *left; + znode *right; + + RLOCK_DK(current_tree); + RLOCK_TREE(current_tree); + + assert("vs-1197", !keygt(&node->ld_key, &node->rd_key)); + + left = node->left; + right = node->right; + + if (ZF_ISSET(node, JNODE_LEFT_CONNECTED) && + left != NULL && ZF_ISSET(left, JNODE_DKSET)) + /* check left neighbor */ + assert("vs-1198", keyeq(&left->rd_key, &node->ld_key)); + + if (ZF_ISSET(node, JNODE_RIGHT_CONNECTED) && right != NULL && + ZF_ISSET(right, JNODE_DKSET)) + /* check right neighbor */ + assert("vs-1199", keyeq(&node->rd_key, &right->ld_key)); + + RUNLOCK_TREE(current_tree); + RUNLOCK_DK(current_tree); +} +#endif + +/* Process one node during tree traversal. + + This is called by cbk_level_lookup(). */ +static level_lookup_result +cbk_node_lookup(cbk_handle * h /* search handle */ ) +{ + /* node plugin of @active */ + node_plugin *nplug; + /* item plugin of item that was found */ + item_plugin *iplug; + /* search bias */ + lookup_bias node_bias; + /* node we are operating upon */ + znode *active; + /* tree we are searching in */ + reiser4_tree *tree; + /* result */ + int result; + + /* true if @key is left delimiting key of @node */ + static int key_is_ld(znode * node, const reiser4_key * key) { + int ld; + + assert("nikita-1716", node != NULL); + assert("nikita-1758", key != NULL); + + RLOCK_DK(znode_get_tree(node)); + assert("nikita-1759", znode_contains_key(node, key)); + ld = keyeq(znode_get_ld_key(node), key); + RUNLOCK_DK(znode_get_tree(node)); + return ld; + } + assert("nikita-379", h != NULL); + + active = h->active_lh->node; + tree = h->tree; + + nplug = active->nplug; + assert("nikita-380", nplug != NULL); + + ON_DEBUG(check_dkeys(active)); + + /* return item from "active" node with maximal key not greater than + "key" */ + node_bias = h->bias; + result = nplug->lookup(active, h->key, node_bias, h->coord); + if (unlikely(result != NS_FOUND && result != NS_NOT_FOUND)) { + /* error occurred */ + h->result = result; + return LOOKUP_DONE; + } + if (h->level == h->stop_level) { + /* welcome to the stop level */ + assert("nikita-381", h->coord->node == active); + if (result == NS_FOUND) { + /* success of tree lookup */ + if (!(h->flags & CBK_UNIQUE) && key_is_ld(active, h->key)) { + return search_to_left(h); + } else + h->result = CBK_COORD_FOUND; + reiser4_stat_inc(tree.cbk_found); + } else { + h->result = CBK_COORD_NOTFOUND; + reiser4_stat_inc(tree.cbk_notfound); + } + if (!(h->flags & CBK_IN_CACHE)) + cbk_cache_add(active); + return LOOKUP_DONE; + } + + if (h->level > TWIG_LEVEL && result == NS_NOT_FOUND) { + h->error = "not found on internal node"; + h->result = result; + return LOOKUP_DONE; + } + + assert("vs-361", h->level > h->stop_level); + + if (handle_eottl(h, &result)) { + /**/ + assert("vs-1674", result == LOOKUP_DONE || result == LOOKUP_REST); + return result; + } + + assert("nikita-2116", item_is_internal(h->coord)); + iplug = item_plugin_by_coord(h->coord); + + /* go down to next level */ + assert("vs-515", item_is_internal(h->coord)); + iplug->s.internal.down_link(h->coord, h->key, &h->block); + --h->level; + return LOOKUP_CONT; /* continue */ +} + +/* scan cbk_cache slots looking for a match for @h */ +static int +cbk_cache_scan_slots(cbk_handle * h /* cbk handle */ ) +{ + level_lookup_result llr; + znode *node; + reiser4_tree *tree; + cbk_cache_slot *slot; + cbk_cache *cache; + tree_level level; + int isunique; + const reiser4_key *key; + int result; + + assert("nikita-1317", h != NULL); + assert("nikita-1315", h->tree != NULL); + assert("nikita-1316", h->key != NULL); + + tree = h->tree; + cache = &tree->cbk_cache; + if (cache->nr_slots == 0) + /* size of cbk cache was set to 0 by mount time option. */ + return RETERR(-ENOENT); + + assert("nikita-2474", cbk_cache_invariant(cache)); + node = NULL; /* to keep gcc happy */ + level = h->level; + key = h->key; + isunique = h->flags & CBK_UNIQUE; + result = RETERR(-ENOENT); + + /* + * this is time-critical function and dragons had, hence, been settled + * here. + * + * Loop below scans cbk cache slots trying to find matching node with + * suitable range of delimiting keys and located at the h->level. + * + * Scan is done under cbk cache spin lock that protects slot->node + * pointers. If suitable node is found we want to pin it in + * memory. But slot->node can point to the node with x_count 0 + * (unreferenced). Such node can be recycled at any moment, or can + * already be in the process of being recycled (within jput()). + * + * As we found node in the cbk cache, it means that jput() hasn't yet + * called cbk_cache_invalidate(). + * + * We acquire reference to the node without holding tree lock, and + * later, check node's RIP bit. This avoids races with jput(). + * + */ + + rcu_read_lock(); + read_lock_cbk_cache(cache); + slot = cbk_cache_list_prev(cbk_cache_list_front(&cache->lru)); + while (1) { + + slot = cbk_cache_list_next(slot); + + if (!cbk_cache_list_end(&cache->lru, slot)) + node = slot->node; + else + node = NULL; + + if (unlikely(node == NULL)) + break; + + /* + * this is (hopefully) the only place in the code where we are + * working with delimiting keys without holding dk lock. This + * is fine here, because this is only "guess" anyway---keys + * are rechecked under dk lock below. + */ + if (znode_get_level(node) == level && + /* min_key < key < max_key */ + znode_contains_key_strict(node, key, isunique)) { + zref(node); + result = 0; + spin_lock_prefetch(&tree->tree_lock.lock); + break; + } + } + read_unlock_cbk_cache(cache); + + assert("nikita-2475", cbk_cache_invariant(cache)); + + if (unlikely(result == 0 && ZF_ISSET(node, JNODE_RIP))) + result = -ENOENT; + + rcu_read_unlock(); + + if (result != 0) { + h->result = CBK_COORD_NOTFOUND; + return RETERR(-ENOENT); + } + + result = longterm_lock_znode(h->active_lh, node, cbk_lock_mode(level, h), ZNODE_LOCK_LOPRI); + zput(node); + if (result != 0) + return result; + result = zload(node); + if (result != 0) + return result; + + /* recheck keys */ + result = + UNDER_RW(dk, tree, read, + znode_contains_key_strict(node, key, isunique)) && + !ZF_ISSET(node, JNODE_HEARD_BANSHEE); + + if (result) { + /* do lookup inside node */ + llr = cbk_node_lookup(h); + /* if cbk_node_lookup() wandered to another node (due to eottl + or non-unique keys), adjust @node */ + /*node = h->active_lh->node;*/ + + if (llr != LOOKUP_DONE) { + /* restart or continue on the next level */ + reiser4_stat_inc(tree.cbk_cache_wrong_node); + result = RETERR(-ENOENT); + } else if (IS_CBKERR(h->result)) + /* io or oom */ + result = RETERR(-ENOENT); + else { + /* good. Either item found or definitely not found. */ + result = 0; + + write_lock_cbk_cache(cache); + if (slot->node == h->active_lh->node/*node*/) { + /* if this node is still in cbk cache---move + its slot to the head of the LRU list. */ + cbk_cache_list_remove(slot); + cbk_cache_list_push_front(&cache->lru, slot); + } + write_unlock_cbk_cache(cache); + } + } else { + /* race. While this thread was waiting for the lock, node was + rebalanced and item we are looking for, shifted out of it + (if it ever was here). + + Continuing scanning is almost hopeless: node key range was + moved to, is almost certainly at the beginning of the LRU + list at this time, because it's hot, but restarting + scanning from the very beginning is complex. Just return, + so that cbk() will be performed. This is not that + important, because such races should be rare. Are they? + */ + reiser4_stat_inc(tree.cbk_cache_race); + result = RETERR(-ENOENT); /* -ERAUGHT */ + } + zrelse(node); + assert("nikita-2476", cbk_cache_invariant(cache)); + return result; +} + +/* look for item with given key in the coord cache + + This function, called by coord_by_key(), scans "coord cache" (&cbk_cache) + which is a small LRU list of znodes accessed lately. For each znode in + znode in this list, it checks whether key we are looking for fits into key + range covered by this node. If so, and in addition, node lies at allowed + level (this is to handle extents on a twig level), node is locked, and + lookup inside it is performed. + + we need a measurement of the cost of this cache search compared to the cost + of coord_by_key. + +*/ +static int +cbk_cache_search(cbk_handle * h /* cbk handle */ ) +{ + int result = 0; + tree_level level; + + /* add CBK_IN_CACHE to the handle flags. This means that + * cbk_node_lookup() assumes that cbk_cache is scanned and would add + * found node to the cache. */ + h->flags |= CBK_IN_CACHE; + for (level = h->stop_level; level <= h->lock_level; ++level) { + h->level = level; + result = cbk_cache_scan_slots(h); + if (result != 0) { + done_lh(h->active_lh); + done_lh(h->parent_lh); + reiser4_stat_inc(tree.cbk_cache_miss); + } else { + assert("nikita-1319", !IS_CBKERR(h->result)); + reiser4_stat_inc(tree.cbk_cache_hit); + write_tree_log(h->tree, tree_cached); + break; + } + } + h->flags &= ~CBK_IN_CACHE; + return result; +} + +/* type of lock we want to obtain during tree traversal. On stop level + we want type of lock user asked for, on upper levels: read lock. */ +reiser4_internal znode_lock_mode cbk_lock_mode(tree_level level, cbk_handle * h) +{ + assert("nikita-382", h != NULL); + + return (level <= h->lock_level) ? h->lock_mode : ZNODE_READ_LOCK; +} + +/* update outdated delimiting keys */ +static void stale_dk(reiser4_tree *tree, znode *node) +{ + znode *right; + + WLOCK_DK(tree); + RLOCK_TREE(tree); + right = node->right; + + if (ZF_ISSET(node, JNODE_RIGHT_CONNECTED) && right && + !keyeq(znode_get_rd_key(node), znode_get_ld_key(right))) + znode_set_rd_key(node, znode_get_ld_key(right)); + + RUNLOCK_TREE(tree); + WUNLOCK_DK(tree); +} + +/* check for possibly outdated delimiting keys, and update them if + * necessary. */ +static void update_stale_dk(reiser4_tree *tree, znode *node) +{ + znode *right; + reiser4_key rd; + + RLOCK_DK(tree); + rd = *znode_get_rd_key(node); + RLOCK_TREE(tree); + right = node->right; + if (unlikely(ZF_ISSET(node, JNODE_RIGHT_CONNECTED) && right && + !keyeq(&rd, znode_get_ld_key(right)))) { + RUNLOCK_TREE(tree); + RUNLOCK_DK(tree); + stale_dk(tree, node); + return; + } + RUNLOCK_TREE(tree); + RUNLOCK_DK(tree); +} + +/* + * handle searches a the non-unique key. + * + * Suppose that we are looking for an item with possibly non-unique key 100. + * + * Root node contains two pointers: one to a node with left delimiting key 0, + * and another to a node with left delimiting key 100. Item we interested in + * may well happen in the sub-tree rooted at the first pointer. + * + * To handle this search_to_left() is called when search reaches stop + * level. This function checks it is _possible_ that item we are looking for + * is in the left neighbor (this can be done by comparing delimiting keys) and + * if so, tries to lock left neighbor (this is low priority lock, so it can + * deadlock, tree traversal is just restarted if it did) and then checks + * whether left neighbor actually contains items with our key. + * + * Note that this is done on the stop level only. It is possible to try such + * left-check on each level, but as duplicate keys are supposed to be rare + * (very unlikely that more than one node is completely filled with items with + * duplicate keys), it sis cheaper to scan to the left on the stop level once. + * + */ +static level_lookup_result +search_to_left(cbk_handle * h /* search handle */ ) +{ + level_lookup_result result; + coord_t *coord; + znode *node; + znode *neighbor; + + lock_handle lh; + + assert("nikita-1761", h != NULL); + assert("nikita-1762", h->level == h->stop_level); + + init_lh(&lh); + coord = h->coord; + node = h->active_lh->node; + assert("nikita-1763", coord_is_leftmost_unit(coord)); + + reiser4_stat_inc(tree.check_left_nonuniq); + h->result = reiser4_get_left_neighbor( + &lh, node, (int) h->lock_mode, GN_CAN_USE_UPPER_LEVELS); + neighbor = NULL; + switch (h->result) { + case -E_DEADLOCK: + result = LOOKUP_REST; + break; + case 0:{ + node_plugin *nplug; + coord_t crd; + lookup_bias bias; + + neighbor = lh.node; + h->result = zload(neighbor); + if (h->result != 0) { + result = LOOKUP_DONE; + break; + } + + nplug = neighbor->nplug; + + coord_init_zero(&crd); + bias = h->bias; + h->bias = FIND_EXACT; + h->result = nplug->lookup(neighbor, h->key, h->bias, &crd); + h->bias = bias; + + if (h->result == NS_NOT_FOUND) { + case -E_NO_NEIGHBOR: + h->result = CBK_COORD_FOUND; + reiser4_stat_inc(tree.cbk_found); + if (!(h->flags & CBK_IN_CACHE)) + cbk_cache_add(node); + default: /* some other error */ + result = LOOKUP_DONE; + } else if (h->result == NS_FOUND) { + reiser4_stat_inc(tree.left_nonuniq_found); + + RLOCK_DK(znode_get_tree(neighbor)); + h->rd_key = *znode_get_ld_key(node); + leftmost_key_in_node(neighbor, &h->ld_key); + RUNLOCK_DK(znode_get_tree(neighbor)); + h->flags |= CBK_DKSET; + + h->block = *znode_get_block(neighbor); + /* clear coord -> node so that cbk_level_lookup() + wouldn't overwrite parent hint in neighbor. + + Parent hint was set up by + reiser4_get_left_neighbor() + */ + UNDER_RW_VOID(tree, znode_get_tree(neighbor), write, + h->coord->node = NULL); + result = LOOKUP_CONT; + } else { + result = LOOKUP_DONE; + } + if (neighbor != NULL) + zrelse(neighbor); + } + } + done_lh(&lh); + return result; +} + +/* debugging aid: return symbolic name of search bias */ +reiser4_internal const char * +bias_name(lookup_bias bias /* bias to get name of */ ) +{ + if (bias == FIND_EXACT) + return "exact"; + else if (bias == FIND_MAX_NOT_MORE_THAN) + return "left-slant"; +/* else if( bias == RIGHT_SLANT_BIAS ) */ +/* return "right-bias"; */ + else { + static char buf[30]; + + sprintf(buf, "unknown: %i", bias); + return buf; + } +} + +#if REISER4_DEBUG_OUTPUT +/* debugging aid: print human readable information about @p */ +reiser4_internal void +print_coord_content(const char *prefix /* prefix to print */ , + coord_t * p /* coord to print */ ) +{ + reiser4_key key; + + if (p == NULL) { + printk("%s: null\n", prefix); + return; + } + if ((p->node != NULL) && znode_is_loaded(p->node) && coord_is_existing_item(p)) + printk("%s: data: %p, length: %i\n", prefix, item_body_by_coord(p), item_length_by_coord(p)); + print_znode(prefix, p->node); + if (znode_is_loaded(p->node)) { + item_key_by_coord(p, &key); + print_key(prefix, &key); + print_plugin(prefix, item_plugin_to_plugin(item_plugin_by_coord(p))); + } +} + +/* debugging aid: print human readable information about @block */ +reiser4_internal void +print_address(const char *prefix /* prefix to print */ , + const reiser4_block_nr * block /* block number to print */ ) +{ + printk("%s: %s\n", prefix, sprint_address(block)); +} +#endif + +/* return string containing human readable representation of @block */ +reiser4_internal char * +sprint_address(const reiser4_block_nr * block /* block number to print */ ) +{ + static char address[30]; + + if (block == NULL) + sprintf(address, "null"); + else if (blocknr_is_fake(block)) + sprintf(address, "%llx", (unsigned long long)(*block)); + else + sprintf(address, "%llu", (unsigned long long)(*block)); + return address; +} + +/* release parent node during traversal */ +static void +put_parent(cbk_handle * h /* search handle */ ) +{ + assert("nikita-383", h != NULL); + if (h->parent_lh->node != NULL) { + longterm_unlock_znode(h->parent_lh); + } +} + +/* helper function used by coord_by_key(): release reference to parent znode + stored in handle before processing its child. */ +static void +hput(cbk_handle * h /* search handle */ ) +{ + assert("nikita-385", h != NULL); + done_lh(h->parent_lh); + done_lh(h->active_lh); +} + +/* Helper function used by cbk(): update delimiting keys of child node (stored + in h->active_lh->node) using key taken from parent on the parent level. */ +static int +setup_delimiting_keys(cbk_handle * h /* search handle */) +{ + znode *active; + reiser4_tree *tree; + + assert("nikita-1088", h != NULL); + + active = h->active_lh->node; + tree = znode_get_tree(active); + /* fast check without taking dk lock. This is safe, because + * JNODE_DKSET is never cleared once set. */ + if (!ZF_ISSET(active, JNODE_DKSET)) { + WLOCK_DK(tree); + if (!ZF_ISSET(active, JNODE_DKSET)) { + znode_set_ld_key(active, &h->ld_key); + znode_set_rd_key(active, &h->rd_key); + ZF_SET(active, JNODE_DKSET); + } + WUNLOCK_DK(tree); + return 1; + } + return 0; +} + +/* true if @block makes sense for the @tree. Used to detect corrupted node + * pointers */ +static int +block_nr_is_correct(reiser4_block_nr * block /* block number to check */ , + reiser4_tree * tree /* tree to check against */ ) +{ + assert("nikita-757", block != NULL); + assert("nikita-758", tree != NULL); + + /* check to see if it exceeds the size of the device. */ + return reiser4_blocknr_is_sane_for(tree->super, block); +} + +/* check consistency of fields */ +static int +sanity_check(cbk_handle * h /* search handle */ ) +{ + assert("nikita-384", h != NULL); + + if (h->level < h->stop_level) { + h->error = "Buried under leaves"; + h->result = RETERR(-EIO); + return LOOKUP_DONE; + } else if (!block_nr_is_correct(&h->block, h->tree)) { + h->error = "bad block number"; + h->result = RETERR(-EIO); + return LOOKUP_DONE; + } else + return 0; +} + + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/spin_macros.h 2004-09-03 00:57:07.372918408 -0700 @@ -0,0 +1,831 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Wrapper functions/macros for spin locks. */ + +/* + * This file implements wrapper functions and macros to work with spin locks + * and read write locks embedded into kernel objects. Wrapper functions + * provide following functionality: + * + * (1) encapsulation of locks: in stead of writing spin_lock(&obj->lock), + * where obj is object of type foo, one writes spin_lock_foo(obj). + * + * (2) optional keeping (in per-thread reiser4_context->locks) information + * about number of locks of particular type currently held by thread. This + * is done if REISER4_DEBUG is on. + * + * (3) optional checking of lock ordering. For object type foo, it is + * possible to provide "lock ordering predicate" (possibly using + * information stored in reiser4_context->locks) checking that locks are + * acquired in the proper order. This is done if REISER4_DEBUG is on. + * + * (4) optional collection of spin lock contention statistics. In this mode + * two sysfs objects (located in /sys/profregion) are associated with each + * spin lock type. One object (foo_t) shows how much time was spent trying + * to acquire spin locks of foo type. Another (foo_h) shows how much time + * spin locks of the type foo were held locked. See spinprof.h for more + * details on this. + * + */ + +#ifndef __SPIN_MACROS_H__ +#define __SPIN_MACROS_H__ + +#include +#include + +#include "debug.h" +#include "spinprof.h" + +/* Checks that read write lock @s is locked (or not) by the -current- + * thread. not yet implemented */ +#define check_is_write_locked(s) ((void)(s), 1) +#define check_is_read_locked(s) ((void)(s), 1) +#define check_is_not_read_locked(s) ((void)(s), 1) +#define check_is_not_write_locked(s) ((void)(s), 1) + +/* Checks that spin lock @s is locked (or not) by the -current- thread. */ +#define check_spin_is_not_locked(s) ((void)(s), 1) +#define spin_is_not_locked(s) ((void)(s), 1) +#if defined(CONFIG_SMP) +# define check_spin_is_locked(s) spin_is_locked(s) +#else +# define check_spin_is_locked(s) ((void)(s), 1) +#endif + +#if REISER4_DEBUG_SPIN_LOCKS +#define __ODCA(l, e) ON_DEBUG_CONTEXT(assert(l, e)) +#else +#define __ODCA(l, e) noop +#endif + +#define REISER4_LOCKPROF_OBJECTS (0) + +#if REISER4_LOCKPROF + +/* + * If spin lock profiling is on, define profregions (see spinprof.[ch]) + * exporting through sysfs information about spin lock contention. With each + * spin lock type two profregions are associated: "held" region (exported as + * /sys/profregion/foo_h), and "trying" region (exported as + * /sys/profregion/foo_t). + */ + +/* + * This macro, given spin lock type, defines corresponding profregions and + * functions to register and unregister them. + */ +#define DEFINE_SPIN_PROFREGIONS(aname) \ +struct profregion pregion_spin_ ## aname ## _held = { \ + .kobj = { \ + .name = #aname "_h" \ + } \ +}; \ + \ +struct profregion pregion_spin_ ## aname ## _trying = { \ + .kobj = { \ + .name = #aname "_t" \ + } \ +}; \ + \ +static inline int register_ ## aname ## _profregion(void) \ +{ \ + int result; \ + \ + result = profregion_register(&pregion_spin_ ## aname ## _held); \ + if (result != 0) \ + return result; \ + result = profregion_register(&pregion_spin_ ## aname ## _trying); \ + return result; \ +} \ + \ +static inline void unregister_ ## aname ## _profregion(void) \ +{ \ + profregion_unregister(&pregion_spin_ ## aname ## _held); \ + profregion_unregister(&pregion_spin_ ## aname ## _trying); \ +} \ + \ +typedef struct { int foo; } aname ## _spin_dummy_profregion + +#define DECLARE_SPIN_PROFREGIONS(NAME) \ +extern struct profregion pregion_spin_ ## NAME ## _held; \ +extern struct profregion pregion_spin_ ## NAME ## _trying; + +/* + * If spin lock profiling is on, define profregions (see spinprof.[ch]) + * exporting through sysfs information about read write lock contention. With + * each read write lock type four profregions are associated: "read held" and + * "write held" regions, and "read trying" and "write trying" regions, + * exported as /sys/profregion/foo_{r,w}_{t,h}. + */ + + +/* + * This macro, given read write lock type, defines corresponding profregions + * and functions to register and unregister them. + */ +#define DEFINE_RW_PROFREGIONS(aname) \ +struct profregion pregion_rw_ ## aname ## _r_held = { \ + .kobj = { \ + .name = #aname "_r_h" \ + } \ +}; \ + \ +struct profregion pregion_rw_ ## aname ## _w_held = { \ + .kobj = { \ + .name = #aname "_w_h" \ + } \ +}; \ + \ +struct profregion pregion_rw_ ## aname ## _r_trying = { \ + .kobj = { \ + .name = #aname "_r_t" \ + } \ +}; \ + \ +struct profregion pregion_rw_ ## aname ## _w_trying = { \ + .kobj = { \ + .name = #aname "_w_t" \ + } \ +}; \ + \ +static inline int register_ ## aname ## _profregion(void) \ +{ \ + int result; \ + \ + result = profregion_register(&pregion_rw_ ## aname ## _r_held); \ + if (result != 0) \ + return result; \ + result = profregion_register(&pregion_rw_ ## aname ## _w_held); \ + if (result != 0) \ + return result; \ + result = profregion_register(&pregion_rw_ ## aname ## _r_trying); \ + if (result != 0) \ + return result; \ + result = profregion_register(&pregion_rw_ ## aname ## _w_trying); \ + return result; \ +} \ + \ +static inline void unregister_ ## aname ## _profregion(void) \ +{ \ + profregion_unregister(&pregion_rw_ ## aname ## _r_held); \ + profregion_unregister(&pregion_rw_ ## aname ## _w_held); \ + profregion_unregister(&pregion_rw_ ## aname ## _r_trying); \ + profregion_unregister(&pregion_rw_ ## aname ## _w_trying); \ +} \ + \ +typedef struct { int foo; } aname ## _rw_dummy_profregion + +#define DECLARE_RW_PROFREGIONS(NAME) \ +extern struct profregion pregion_rw_ ## NAME ## _r_held; \ +extern struct profregion pregion_rw_ ## NAME ## _w_held; \ +extern struct profregion pregion_rw_ ## NAME ## _r_trying; \ +extern struct profregion pregion_rw_ ## NAME ## _w_trying; + +#if REISER4_LOCKPROF_OBJECTS +#define OBJCNT(field) field +#else +#define OBJCNT(field) (NULL) +#endif + +/* + * Helper macros to enter and leave profiling regions. + */ + +#define GETCPU(cpu) \ + int cpu = get_cpu() + +#define PUTCPU(cpu) put_cpu() + +#define PREG_IN(cpu, preg, objloc, codeloc) \ + profregion_in(cpu, preg, OBJCNT(objloc), codeloc) + +#define PREG_REPLACE(cpu, preg, objloc, codeloc) \ + profregion_replace(cpu, preg, OBJCNT(objloc), codeloc) + +#define PREG_EX(cpu, preg) profregion_ex(cpu, preg) + +/* REISER4_LOCKPROF */ +#else + +/* + * If spin lock profiling is disabled, declare everything to noops. + */ + +#define DEFINE_SPIN_PROFREGIONS(aname) \ +static inline int register_ ## aname ## _profregion(void) \ +{ \ + return 0; \ +} \ + \ +static inline void unregister_ ## aname ## _profregion(void) \ +{ \ +} + +#define DECLARE_SPIN_PROFREGIONS(NAME) + +#define DEFINE_RW_PROFREGIONS(aname) \ +static inline int register_ ## aname ## _profregion(void) \ +{ \ + return 0; \ +} \ + \ +static inline void unregister_ ## aname ## _profregion(void) \ +{ \ +} + +#define DECLARE_RW_PROFREGIONS(NAME) + +#define GETCPU(cpu) +#define PUTCPU(cpu) +#define PREG_IN(cpu, preg, objloc, codeloc) +#define PREG_REPLACE(cpu, preg, objloc, codeloc) +#define PREG_EX(cpu, preg) + +/* REISER4_LOCKPROF */ +#endif + +/* + * Data structure embedded into kernel objects together with spin lock. + */ +typedef struct reiser4_spin_data { + /* spin lock proper */ + spinlock_t lock; +#if REISER4_LOCKPROF && REISER4_LOCKPROF_OBJECTS + /* number of times clock interrupt found spin lock of this objects to + * be held */ + int held; + /* number of times clock interrupt found that current thread is trying + * to acquire this spin lock */ + int trying; +#endif +} reiser4_spin_data; + +/* + * Data structure embedded into kernel objects together with read write lock. + */ +typedef struct reiser4_rw_data { + /* read write lock proper */ + rwlock_t lock; +#if REISER4_LOCKPROF && REISER4_LOCKPROF_OBJECTS + /* number of times clock interrupt found read write lock of this + * objects to be read held */ + int r_held; + /* number of times clock interrupt found that current thread is trying + * to acquire this lock for read */ + int r_trying; + /* number of times clock interrupt found read write lock of this + * objects to be write held */ + int w_held; + /* number of times clock interrupt found that current thread is trying + * to acquire this lock for write */ + int w_trying; +#endif +} reiser4_rw_data; + +/* Define several inline functions for each type of spinlock. This is long + * monster macro definition. */ +#define SPIN_LOCK_FUNCTIONS(NAME,TYPE,FIELD) \ + \ +DECLARE_SPIN_PROFREGIONS(NAME) \ + \ +/* Initialize spin lock embedded in @x */ \ +static inline void spin_ ## NAME ## _init(TYPE *x) \ +{ \ + __ODCA("nikita-2987", x != NULL); \ + memset(& x->FIELD, 0, sizeof x->FIELD); \ + spin_lock_init(& x->FIELD.lock); \ +} \ + \ +/* Increment per-thread lock counter for this lock type and total counter */ \ +/* of acquired spin locks. This is helper function used by spin lock */ \ +/* acquiring functions below */ \ +static inline void spin_ ## NAME ## _inc(void) \ +{ \ + LOCK_CNT_INC(spin_locked_ ## NAME); \ + LOCK_CNT_INC(spin_locked); \ +} \ + \ +/* Decrement per-thread lock counter and total counter of acquired spin */ \ +/* locks. This is helper function used by spin lock releasing functions */ \ +/* below. */ \ +static inline void spin_ ## NAME ## _dec(void) \ +{ \ + LOCK_CNT_DEC(spin_locked_ ## NAME); \ + LOCK_CNT_DEC(spin_locked); \ +} \ + \ +/* Return true of spin lock embedded in @x is acquired by -current- */ \ +/* thread */ \ +static inline int spin_ ## NAME ## _is_locked (const TYPE *x) \ +{ \ + return check_spin_is_locked (& x->FIELD.lock) && \ + LOCK_CNT_GTZ(spin_locked_ ## NAME); \ +} \ + \ +/* Return true of spin lock embedded in @x is not acquired by -current- */ \ +/* thread */ \ +static inline int spin_ ## NAME ## _is_not_locked (TYPE *x) \ +{ \ + return check_spin_is_not_locked (& x->FIELD.lock); \ +} \ + \ +/* Acquire spin lock embedded in @x without checking lock ordering. */ \ +/* This is useful when, for example, locking just created object. */ \ +static inline void spin_lock_ ## NAME ## _no_ord (TYPE *x, \ + locksite *t, locksite *h) \ +{ \ + GETCPU(cpu); \ + __ODCA("nikita-2703", spin_ ## NAME ## _is_not_locked(x)); \ + PREG_IN(cpu, &pregion_spin_ ## NAME ## _trying, &x->FIELD.trying, t); \ + spin_lock(&x->FIELD.lock); \ + PREG_REPLACE(cpu, \ + &pregion_spin_ ## NAME ## _held, &x->FIELD.held, h); \ + PUTCPU(cpu); \ + spin_ ## NAME ## _inc(); \ +} \ + \ +/* Account for spin lock acquired by some other means. For example */ \ +/* through atomic_dec_and_lock() or similar. */ \ +static inline void spin_lock_ ## NAME ## _acc (TYPE *x, locksite *h) \ +{ \ + GETCPU(cpu); \ + PREG_IN(cpu, &pregion_spin_ ## NAME ## _held, &x->FIELD.held, h); \ + PUTCPU(cpu); \ + spin_ ## NAME ## _inc(); \ +} \ + \ +/* Lock @x with explicit indication of spin lock profiling "sites". */ \ +/* Locksite is used by spin lock profiling code (spinprof.[ch]) to */ \ +/* identify fragment of code that locks @x. */ \ +/* */ \ +/* If clock interrupt finds that current thread is spinning waiting for */ \ +/* the lock on @x, counters in @t will be incremented. */ \ +/* */ \ +/* If clock interrupt finds that current thread holds the lock on @x, */ \ +/* counters in @h will be incremented. */ \ +/* */ \ +static inline void spin_lock_ ## NAME ## _at (TYPE *x, \ + locksite *t, locksite *h) \ +{ \ + __ODCA("nikita-1383", spin_ordering_pred_ ## NAME(x)); \ + spin_lock_ ## NAME ## _no_ord(x, t, h); \ +} \ + \ +/* Lock @x. */ \ +static inline void spin_lock_ ## NAME (TYPE *x) \ +{ \ + __ODCA("nikita-1383", spin_ordering_pred_ ## NAME(x)); \ + spin_lock_ ## NAME ## _no_ord(x, 0, 0); \ +} \ + \ +/* Try to obtain lock @x. On success, returns 1 with @x locked. */ \ +/* If @x is already locked, return 0 immediately. */ \ +static inline int spin_trylock_ ## NAME (TYPE *x) \ +{ \ + if (spin_trylock (& x->FIELD.lock)) { \ + GETCPU(cpu); \ + spin_ ## NAME ## _inc(); \ + PREG_IN(cpu, \ + &pregion_spin_ ## NAME ## _held, &x->FIELD.held, 0); \ + PUTCPU(cpu); \ + return 1; \ + } \ + return 0; \ +} \ + \ +/* Unlock @x. */ \ +static inline void spin_unlock_ ## NAME (TYPE *x) \ +{ \ + __ODCA("nikita-1375", LOCK_CNT_GTZ(spin_locked_ ## NAME)); \ + __ODCA("nikita-1376", LOCK_CNT_GTZ(spin_locked > 0)); \ + __ODCA("nikita-2703", spin_ ## NAME ## _is_locked(x)); \ + \ + spin_ ## NAME ## _dec(); \ + spin_unlock (& x->FIELD.lock); \ + PREG_EX(get_cpu(), &pregion_spin_ ## NAME ## _held); \ +} \ + \ +typedef struct { int foo; } NAME ## _spin_dummy + +/* + * Helper macro to perform a simple operation that requires taking of spin + * lock. + * + * 1. Acquire spin lock on object @obj of type @obj_type. + * + * 2. Execute @exp under spin lock, and store result. + * + * 3. Release spin lock. + * + * 4. Return result of @exp. + * + * Example: + * + * right_delimiting_key = UNDER_SPIN(dk, current_tree, *znode_get_rd_key(node)); + * + */ +#define UNDER_SPIN(obj_type, obj, exp) \ +({ \ + typeof (obj) __obj; \ + typeof (exp) __result; \ + LOCKSITE_INIT(__hits_trying); \ + LOCKSITE_INIT(__hits_held); \ + \ + __obj = (obj); \ + __ODCA("nikita-2492", __obj != NULL); \ + spin_lock_ ## obj_type ## _at (__obj, &__hits_trying, &__hits_held); \ + __result = exp; \ + spin_unlock_ ## obj_type (__obj); \ + __result; \ +}) + +/* + * The same as UNDER_SPIN, but without storing and returning @exp's result. + */ +#define UNDER_SPIN_VOID(obj_type, obj, exp) \ +({ \ + typeof (obj) __obj; \ + LOCKSITE_INIT(__hits_trying); \ + LOCKSITE_INIT(__hits_held); \ + \ + __obj = (obj); \ + __ODCA("nikita-2492", __obj != NULL); \ + spin_lock_ ## obj_type ## _at (__obj, &__hits_trying, &__hits_held); \ + exp; \ + spin_unlock_ ## obj_type (__obj); \ +}) + + +/* Define several inline functions for each type of read write lock. This is + * insanely long macro definition. */ +#define RW_LOCK_FUNCTIONS(NAME,TYPE,FIELD) \ + \ +DECLARE_RW_PROFREGIONS(NAME) \ + \ +/* Initialize read write lock embedded into @x. */ \ +static inline void rw_ ## NAME ## _init(TYPE *x) \ +{ \ + __ODCA("nikita-2988", x != NULL); \ + memset(& x->FIELD, 0, sizeof x->FIELD); \ + rwlock_init(& x->FIELD.lock); \ +} \ + \ +/* True, if @x is read locked by the -current- thread. */ \ +static inline int rw_ ## NAME ## _is_read_locked (const TYPE *x) \ +{ \ + return check_is_read_locked (& x->FIELD.lock); \ +} \ + \ +/* True, if @x is write locked by the -current- thread. */ \ +static inline int rw_ ## NAME ## _is_write_locked (const TYPE *x) \ +{ \ + return check_is_write_locked (& x->FIELD.lock); \ +} \ + \ +/* True, if @x is not read locked by the -current- thread. */ \ +static inline int rw_ ## NAME ## _is_not_read_locked (TYPE *x) \ +{ \ + return check_is_not_read_locked (& x->FIELD.lock); \ +} \ + \ +/* True, if @x is not write locked by the -current- thread. */ \ +static inline int rw_ ## NAME ## _is_not_write_locked (TYPE *x) \ +{ \ + return check_is_not_write_locked (& x->FIELD.lock); \ +} \ + \ +/* True, if @x is either read or write locked by the -current- thread. */ \ +static inline int rw_ ## NAME ## _is_locked (const TYPE *x) \ +{ \ + return check_is_read_locked (& x->FIELD.lock) || \ + check_is_write_locked (& x->FIELD.lock); \ +} \ + \ +/* True, if @x is neither read nor write locked by the -current- thread. */ \ +static inline int rw_ ## NAME ## _is_not_locked (const TYPE *x) \ +{ \ + return check_is_not_read_locked (& x->FIELD.lock) && \ + check_is_not_write_locked (& x->FIELD.lock); \ +} \ + \ +/* This is helper function used by lock acquiring functions below */ \ +static inline void read_ ## NAME ## _inc(void) \ +{ \ + LOCK_CNT_INC(read_locked_ ## NAME); \ + LOCK_CNT_INC(rw_locked_ ## NAME); \ + LOCK_CNT_INC(spin_locked); \ +} \ + \ +/* This is helper function used by lock acquiring functions below */ \ +static inline void read_ ## NAME ## _dec(void) \ +{ \ + LOCK_CNT_DEC(read_locked_ ## NAME); \ + LOCK_CNT_DEC(rw_locked_ ## NAME); \ + LOCK_CNT_DEC(spin_locked); \ +} \ + \ +/* This is helper function used by lock acquiring functions below */ \ +static inline void write_ ## NAME ## _inc(void) \ +{ \ + LOCK_CNT_INC(write_locked_ ## NAME); \ + LOCK_CNT_INC(rw_locked_ ## NAME); \ + LOCK_CNT_INC(spin_locked); \ +} \ + \ +/* This is helper function used by lock acquiring functions below */ \ +static inline void write_ ## NAME ## _dec(void) \ +{ \ + LOCK_CNT_DEC(write_locked_ ## NAME); \ + LOCK_CNT_DEC(rw_locked_ ## NAME); \ + LOCK_CNT_DEC(spin_locked); \ +} \ + \ +/* Acquire read lock on @x without checking lock ordering predicates. */ \ +/* This is useful when, for example, locking just created object. */ \ +static inline void read_lock_ ## NAME ## _no_ord (TYPE *x, \ + locksite *t, locksite *h) \ +{ \ + GETCPU(cpu); \ + __ODCA("nikita-2976", rw_ ## NAME ## _is_not_read_locked(x)); \ + PREG_IN(cpu, &pregion_rw_ ## NAME ## _r_trying, &x->FIELD.r_trying, t); \ + read_lock(&x->FIELD.lock); \ + PREG_REPLACE(cpu, &pregion_rw_ ## NAME ## _r_held, \ + &x->FIELD.r_held, h); \ + PUTCPU(cpu); \ + read_ ## NAME ## _inc(); \ +} \ + \ +/* Acquire write lock on @x without checking lock ordering predicates. */ \ +/* This is useful when, for example, locking just created object. */ \ +static inline void write_lock_ ## NAME ## _no_ord (TYPE *x, \ + locksite *t, locksite *h) \ +{ \ + GETCPU(cpu); \ + __ODCA("nikita-2977", rw_ ## NAME ## _is_not_write_locked(x)); \ + PREG_IN(cpu, &pregion_rw_ ## NAME ## _w_trying, &x->FIELD.w_trying, t); \ + write_lock(&x->FIELD.lock); \ + PREG_REPLACE(cpu, &pregion_rw_ ## NAME ## _w_held, \ + &x->FIELD.w_held, h); \ + PUTCPU(cpu); \ + write_ ## NAME ## _inc(); \ +} \ + \ +/* Read lock @x with explicit indication of spin lock profiling "sites". */ \ +/* See spin_lock_foo_at() above for more information. */ \ +static inline void read_lock_ ## NAME ## _at (TYPE *x, \ + locksite *t, locksite *h) \ +{ \ + __ODCA("nikita-2975", rw_ordering_pred_ ## NAME(x)); \ + read_lock_ ## NAME ## _no_ord(x, t, h); \ +} \ + \ +/* Write lock @x with explicit indication of spin lock profiling "sites". */ \ +/* See spin_lock_foo_at() above for more information. */ \ +static inline void write_lock_ ## NAME ## _at (TYPE *x, \ + locksite *t, locksite *h) \ +{ \ + __ODCA("nikita-2978", rw_ordering_pred_ ## NAME(x)); \ + write_lock_ ## NAME ## _no_ord(x, t, h); \ +} \ + \ +/* Read lock @x. */ \ +static inline void read_lock_ ## NAME (TYPE *x) \ +{ \ + __ODCA("nikita-2975", rw_ordering_pred_ ## NAME(x)); \ + read_lock_ ## NAME ## _no_ord(x, 0, 0); \ +} \ + \ +/* Write lock @x. */ \ +static inline void write_lock_ ## NAME (TYPE *x) \ +{ \ + __ODCA("nikita-2978", rw_ordering_pred_ ## NAME(x)); \ + write_lock_ ## NAME ## _no_ord(x, 0, 0); \ +} \ + \ +/* Release read lock on @x. */ \ +static inline void read_unlock_ ## NAME (TYPE *x) \ +{ \ + __ODCA("nikita-2979", LOCK_CNT_GTZ(read_locked_ ## NAME)); \ + __ODCA("nikita-2980", LOCK_CNT_GTZ(rw_locked_ ## NAME)); \ + __ODCA("nikita-2980", LOCK_CNT_GTZ(spin_locked)); \ + read_ ## NAME ## _dec(); \ + __ODCA("nikita-2703", rw_ ## NAME ## _is_read_locked(x)); \ + read_unlock (& x->FIELD.lock); \ + PREG_EX(get_cpu(), &pregion_rw_ ## NAME ## _r_held); \ +} \ + \ +/* Release write lock on @x. */ \ +static inline void write_unlock_ ## NAME (TYPE *x) \ +{ \ + __ODCA("nikita-2979", LOCK_CNT_GTZ(write_locked_ ## NAME)); \ + __ODCA("nikita-2980", LOCK_CNT_GTZ(rw_locked_ ## NAME)); \ + __ODCA("nikita-2980", LOCK_CNT_GTZ(spin_locked)); \ + write_ ## NAME ## _dec(); \ + __ODCA("nikita-2703", rw_ ## NAME ## _is_write_locked(x)); \ + write_unlock (& x->FIELD.lock); \ + PREG_EX(get_cpu(), &pregion_rw_ ## NAME ## _w_held); \ +} \ + \ +/* Try to obtain write lock on @x. On success, returns 1 with @x locked. */ \ +/* If @x is already locked, return 0 immediately. */ \ +static inline int write_trylock_ ## NAME (TYPE *x) \ +{ \ + if (write_trylock (& x->FIELD.lock)) { \ + GETCPU(cpu); \ + PREG_IN(cpu, &pregion_rw_ ## NAME ## _w_held, \ + &x->FIELD.w_held, 0); \ + PUTCPU(cpu); \ + write_ ## NAME ## _inc(); \ + return 1; \ + } \ + return 0; \ +} \ + \ + \ +typedef struct { int foo; } NAME ## _rw_dummy + +/* + * Helper macro to perform a simple operation that requires taking of read + * write lock. + * + * 1. Acquire read or write (depending on @rw parameter) lock on object @obj + * of type @obj_type. + * + * 2. Execute @exp under lock, and store result. + * + * 3. Release lock. + * + * 4. Return result of @exp. + * + * Example: + * + * tree_height = UNDER_RW(tree, current_tree, read, current_tree->height); + */ +#define UNDER_RW(obj_type, obj, rw, exp) \ +({ \ + typeof (obj) __obj; \ + typeof (exp) __result; \ + LOCKSITE_INIT(__hits_t); \ + LOCKSITE_INIT(__hits_h); \ + \ + __obj = (obj); \ + __ODCA("nikita-2981", __obj != NULL); \ + rw ## _lock_ ## obj_type ## _at (__obj, &__hits_t, &__hits_h); \ + __result = exp; \ + rw ## _unlock_ ## obj_type (__obj); \ + __result; \ +}) + +/* + * The same as UNDER_RW, but without storing and returning @exp's result. + */ +#define UNDER_RW_VOID(obj_type, obj, rw, exp) \ +({ \ + typeof (obj) __obj; \ + LOCKSITE_INIT(__hits_t); \ + LOCKSITE_INIT(__hits_h); \ + \ + __obj = (obj); \ + __ODCA("nikita-2982", __obj != NULL); \ + rw ## _lock_ ## obj_type ## _at (__obj, &__hits_t, &__hits_h); \ + exp; \ + rw ## _unlock_ ## obj_type (__obj); \ +}) + +#if REISER4_LOCKPROF + +/* + * Wrapper function to work with locks of certain reiser4 objects. These + * functions allows to track where in code locks are held (or tried) for the + * longest time. + */ + +#define LOCK_JNODE(node) \ +({ \ + LOCKSITE_INIT(__hits_t); \ + LOCKSITE_INIT(__hits_h); \ + \ + spin_lock_jnode_at(node, &__hits_t, &__hits_h); \ +}) + +#define LOCK_JLOAD(node) \ +({ \ + LOCKSITE_INIT(__hits_t); \ + LOCKSITE_INIT(__hits_h); \ + \ + spin_lock_jload_at(node, &__hits_t, &__hits_h); \ +}) + +#define LOCK_ATOM(atom) \ +({ \ + LOCKSITE_INIT(__hits_t); \ + LOCKSITE_INIT(__hits_h); \ + \ + spin_lock_atom_at(atom, &__hits_t, &__hits_h); \ +}) + +#define LOCK_TXNH(txnh) \ +({ \ + LOCKSITE_INIT(__hits_t); \ + LOCKSITE_INIT(__hits_h); \ + \ + spin_lock_txnh_at(txnh, &__hits_t, &__hits_h); \ +}) + +#define LOCK_INODE(inode) \ +({ \ + LOCKSITE_INIT(__hits_t); \ + LOCKSITE_INIT(__hits_h); \ + \ + spin_lock_inode_object_at(inode, &__hits_t, &__hits_h); \ +}) + +#define RLOCK_TREE(tree) \ +({ \ + LOCKSITE_INIT(__hits_t); \ + LOCKSITE_INIT(__hits_h); \ + \ + read_lock_tree_at(tree, &__hits_t, &__hits_h); \ +}) + +#define WLOCK_TREE(tree) \ +({ \ + LOCKSITE_INIT(__hits_t); \ + LOCKSITE_INIT(__hits_h); \ + \ + write_lock_tree_at(tree, &__hits_t, &__hits_h); \ +}) + +#define RLOCK_DK(tree) \ +({ \ + LOCKSITE_INIT(__hits_t); \ + LOCKSITE_INIT(__hits_h); \ + \ + read_lock_dk_at(tree, &__hits_t, &__hits_h); \ +}) + +#define WLOCK_DK(tree) \ +({ \ + LOCKSITE_INIT(__hits_t); \ + LOCKSITE_INIT(__hits_h); \ + \ + write_lock_dk_at(tree, &__hits_t, &__hits_h); \ +}) + +#define RLOCK_ZLOCK(lock) \ +({ \ + LOCKSITE_INIT(__hits_t); \ + LOCKSITE_INIT(__hits_h); \ + \ + read_lock_zlock_at(lock, &__hits_t, &__hits_h); \ +}) + +#define WLOCK_ZLOCK(lock) \ +({ \ + LOCKSITE_INIT(__hits_t); \ + LOCKSITE_INIT(__hits_h); \ + \ + write_lock_zlock_at(lock, &__hits_t, &__hits_h); \ +}) + + +#else +#define LOCK_JNODE(node) spin_lock_jnode(node) +#define LOCK_JLOAD(node) spin_lock_jload(node) +#define LOCK_ATOM(atom) spin_lock_atom(atom) +#define LOCK_TXNH(txnh) spin_lock_txnh(txnh) +#define LOCK_INODE(inode) spin_lock_inode_object(inode) +#define RLOCK_TREE(tree) read_lock_tree(tree) +#define WLOCK_TREE(tree) write_lock_tree(tree) +#define RLOCK_DK(tree) read_lock_dk(tree) +#define WLOCK_DK(tree) write_lock_dk(tree) +#define RLOCK_ZLOCK(lock) read_lock_zlock(lock) +#define WLOCK_ZLOCK(lock) write_lock_zlock(lock) +#endif + +#define UNLOCK_JNODE(node) spin_unlock_jnode(node) +#define UNLOCK_JLOAD(node) spin_unlock_jload(node) +#define UNLOCK_ATOM(atom) spin_unlock_atom(atom) +#define UNLOCK_TXNH(txnh) spin_unlock_txnh(txnh) +#define UNLOCK_INODE(inode) spin_unlock_inode_object(inode) +#define RUNLOCK_TREE(tree) read_unlock_tree(tree) +#define WUNLOCK_TREE(tree) write_unlock_tree(tree) +#define RUNLOCK_DK(tree) read_unlock_dk(tree) +#define WUNLOCK_DK(tree) write_unlock_dk(tree) +#define RUNLOCK_ZLOCK(lock) read_unlock_zlock(lock) +#define WUNLOCK_ZLOCK(lock) write_unlock_zlock(lock) + +/* __SPIN_MACROS_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/spinprof.c 2004-09-03 00:57:05.387220280 -0700 @@ -0,0 +1,567 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* spin lock profiling */ + +/* + * Spin-lock profiling code. + * + * Basic notion in our profiling code is "profiling region" (struct + * profregion). Profiling region is entered and left by calling + * profregion_in() and profregion_ex() function correspondingly. It is invalid + * to be preempted (voluntary or not) while inside profiling region. Profiling + * regions can be entered recursively, and it is not necessary nest then + * properly, that is + * + * profregion_in(&A); + * profregion_in(&B); + * profregion_ex(&A); + * profregion_ex(&B)) + * + * is valid sequence of operations. Each CPU maintains an array of currently + * active profiling regions. This array is consulted by clock interrupt + * handler, and counters in the profiling regions found active by handler are + * incremented. This allows one to estimate for how long region has been + * active on average. Spin-locking code in spin_macros.h uses this to measure + * spin-lock contention. Specifically two profiling regions are defined for + * each spin-lock type: one is activate while thread is trying to acquire + * lock, and another when it holds the lock. + * + * Profiling regions export their statistics in the sysfs, under special + * directory /sys/profregion. + * + * Each profregion is represented as a child directory + * /sys/profregion/foo. Internally it's represented as a struct kobject (viz + * one embedded into struct profregion, see spinprof.h). + * + * Each /sys/profregion/foo directory contains files representing fields in + * profregion: + * + * hits + * busy + * obj + * objhit + * code + * codehit + * + * See spinprof.h for details. + * + * + */ + +#include "kattr.h" +#include "spinprof.h" +#include "debug.h" + +#include +#include +#include + +#include +#include /* for instruction_pointer() */ + +#if REISER4_LOCKPROF + +/* + * helper macro: how many bytes left in the PAGE_SIZE buffer, starting at @buf + * and used up to and including @p. + */ +#define LEFT(p, buf) (PAGE_SIZE - ((p) - (buf)) - 1) + +void profregion_functions_start_here(void); +void profregion_functions_end_here(void); + +static locksite none = { + .hits = STATCNT_INIT, + .func = "", + .line = 0 +}; + +/* + * sysfs holder. + */ +struct profregion_attr { + struct attribute attr; + ssize_t (*show)(struct profregion *pregion, char *buf); +}; + +/* + * macro to define profregion_attr for the given profregion + */ +#define PROFREGION_ATTR(aname) \ +static struct profregion_attr aname = { \ + .attr = { \ + .name = (char *)#aname, \ + .mode = 0666 \ + }, \ + .show = aname ## _show \ +} + +/* + * ->show() method for the "hits" attribute. + */ +static ssize_t hits_show(struct profregion *pregion, char *buf) +{ + char *p = buf; + KATTR_PRINT(p, buf, "%li\n", statcnt_get(&pregion->hits)); + return (p - buf); +} + +/* + * ->show() method for the "busy" attribute. + */ +static ssize_t busy_show(struct profregion *pregion, char *buf) +{ + char *p = buf; + KATTR_PRINT(p, buf, "%li\n", statcnt_get(&pregion->busy)); + return (p - buf); +} + +/* + * ->show() method for the "obj" attribute. + */ +static ssize_t obj_show(struct profregion *pregion, char *buf) +{ + char *p = buf; + KATTR_PRINT(p, buf, "%p\n", pregion->obj); + return (p - buf); +} + +/* + * ->show() method for the "objhit" attribute. + */ +static ssize_t objhit_show(struct profregion *pregion, char *buf) +{ + char *p = buf; + KATTR_PRINT(p, buf, "%i\n", pregion->objhit); + return (p - buf); +} + +/* + * ->show() method for the "code" attribute. + */ +static ssize_t code_show(struct profregion *pregion, char *buf) +{ + char *p = buf; + locksite *site; + + site = pregion->code ? : &none; + KATTR_PRINT(p, buf, "%s:%i\n", site->func, site->line); + return (p - buf); +} + +/* + * ->show() method for the "codehit" attribute. + */ +static ssize_t codehit_show(struct profregion *pregion, char *buf) +{ + char *p = buf; + KATTR_PRINT(p, buf, "%i\n", pregion->codehit); + return (p - buf); +} + +PROFREGION_ATTR(hits); +PROFREGION_ATTR(busy); +PROFREGION_ATTR(obj); +PROFREGION_ATTR(objhit); +PROFREGION_ATTR(code); +PROFREGION_ATTR(codehit); + +/* + * wrapper to call attribute ->show() methods (defined above). This is called + * by sysfs. + */ +static ssize_t +profregion_show(struct kobject * kobj, struct attribute *attr, char *buf) +{ + struct profregion *pregion; + struct profregion_attr *pattr; + + pregion = container_of(kobj, struct profregion, kobj); + pattr = container_of(attr, struct profregion_attr, attr); + + return pattr->show(pregion, buf); +} + +/* + * ->store() method for profregion sysfs object. Any write to this object, + * just resets profregion stats. + */ +static ssize_t profregion_store(struct kobject * kobj, + struct attribute * attr UNUSED_ARG, + const char * buf UNUSED_ARG, + size_t size) +{ + struct profregion *pregion; + + pregion = container_of(kobj, struct profregion, kobj); + statcnt_reset(&pregion->hits); + statcnt_reset(&pregion->busy); + pregion->obj = 0; + pregion->objhit = 0; + pregion->code = 0; + pregion->codehit = 0; + return size; +} + +/* + * sysfs attribute operations vector... + */ +static struct sysfs_ops profregion_attr_ops = { + .show = profregion_show, + .store = profregion_store +}; + +/* + * ...and attributes themselves. + */ +static struct attribute * def_attrs[] = { + &hits.attr, + &busy.attr, + &obj.attr, + &objhit.attr, + &code.attr, + &codehit.attr, + NULL +}; + +/* + * ktype for kobjects representing profregions. + */ +static struct kobj_type ktype_profregion = { + .sysfs_ops = &profregion_attr_ops, + .default_attrs = def_attrs, +}; + +/* + * sysfs object for /sys/profregion + */ +static decl_subsys(profregion, &ktype_profregion, NULL); + +/* + * profregionstack for each CPU + */ +DEFINE_PER_CPU(struct profregionstack, inregion) = {0}; + +/* + * profregion meaning "no other profregion is active" + */ +struct profregion outside = { + .hits = STATCNT_INIT, + .kobj = { + .name = "outside" + } +}; + +/* + * profregion meaning "we are in reiser4 context, but no locks are held" + */ +struct profregion incontext = { + .hits = STATCNT_INIT, + .kobj = { + .name = "incontext" + } +}; + +/* + * profregion meaning "we are profregion handling code". This is to estimate + * profregion overhead. + */ +struct profregion overhead = { + .hits = STATCNT_INIT, + .kobj = { + .name = "overhead" + } +}; + +extern struct profregion pregion_spin_jnode_held; +extern struct profregion pregion_spin_jnode_trying; + +/* + * This is main profregion handling function. It is called from clock + * interrupt handler on each tick (HZ times per second). + * + * It determines what profregions are active at the moment of call, and + * updates their fields correspondingly. + */ +static int callback(struct notifier_block *self UNUSED_ARG, + unsigned long val UNUSED_ARG, void *p) +{ + struct profregionstack *stack; + struct pt_regs *regs; + unsigned long pc; + int ntop; + + regs = p; + /* instruction pointer at which interrupt happened */ + pc = instruction_pointer(regs); + + if (pc > (unsigned long)profregion_functions_start_here && + pc < (unsigned long)profregion_functions_end_here) { + /* if @pc lies in this file---count it as overhead */ + statcnt_inc(&overhead.hits); + return 0; + } + + stack = &get_cpu_var(inregion); + ntop = stack->top; + if (unlikely(ntop != 0)) { + struct pregactivation *act; + struct profregion *preg; + int hits; + + act = &stack->stack[ntop - 1]; + preg = act->preg; + statcnt_inc(&preg->hits); + + hits = 0; + if (act->objloc != NULL) { + BUG_ON(*act->objloc == 0x6b6b6b6b); + BUG_ON(*act->objloc == 0x5a5a5a5a); + hits = ++ (*act->objloc); + } + if (unlikely(hits > preg->objhit)) { + if (preg->obj != act->objloc) { + preg->objhit = hits; + preg->obj = act->objloc; + if (preg->champion != NULL) + preg->champion(preg); + } + } + + hits = 0; + if (act->codeloc != NULL) { + statcnt_inc(&act->codeloc->hits); + hits = statcnt_get(&act->codeloc->hits); + } + if (unlikely(hits > preg->codehit)) { + preg->codehit = hits; + preg->code = act->codeloc; + } + for (; ntop > 0 ; --ntop) { + preg = stack->stack[ntop - 1].preg; + if (preg != NULL) + statcnt_inc(&preg->busy); + } + } else if (is_in_reiser4_context()) + statcnt_inc(&incontext.hits); + else + statcnt_inc(&outside.hits); + put_cpu_var(inregion); + return 0; +} + +/* + * notifier block used to register our callback for clock interrupt handler. + */ +static struct notifier_block profregionnotifier = { + .notifier_call = callback +}; + +/* different architectures tend to declare register_profile_notifier() in + * different places */ +extern int register_profile_notifier(struct notifier_block * nb); + +/* + * profregion initialization: setup sysfs things. + */ +int __init +profregion_init(void) +{ + int result; + + /* register /sys/profregion */ + result = subsystem_register(&profregion_subsys); + if (result != 0) + return result; + + /* register /sys/profregion/outside */ + result = profregion_register(&outside); + if (result != 0) + return result; + + /* register /sys/profregion/incontext */ + result = profregion_register(&incontext); + if (result != 0) + return result; + + /* register /sys/profregion/overhead */ + result = profregion_register(&overhead); + if (result != 0) + return result; + + /* register our callback function to be called on each clock tick */ + return register_profile_notifier(&profregionnotifier); +} +subsys_initcall(profregion_init); + +/* + * undo profregion_init() actions. + */ +static void __exit +profregion_exit(void) +{ + profregion_unregister(&overhead); + profregion_unregister(&incontext); + profregion_unregister(&outside); + subsystem_unregister(&profregion_subsys); +} +__exitcall(profregion_exit); + +/* + * register profregion + */ +int profregion_register(struct profregion *pregion) +{ + /* tell sysfs that @pregion is part of /sys/profregion "subsystem" */ + kobj_set_kset_s(pregion, profregion_subsys); + /* and register /sys/profregion/ */ + return kobject_register(&pregion->kobj); +} + +/* + * dual to profregion_register(): unregister profregion + */ +void profregion_unregister(struct profregion *pregion) +{ + kobject_register(&pregion->kobj); +} + +void profregion_functions_start_here(void) { } + +/* + * search for @pregion in the stack of currently active profregions on this + * cpu. Return its index if found, 0 otherwise. + */ +int profregion_find(struct profregionstack *stack, struct profregion *pregion) +{ + int i; + + for (i = stack->top - 2 ; i >= 0 ; -- i) { + if (stack->stack[i].preg == pregion) { + return i; + } + } + BUG(); + return 0; +} + +/* + * Fill @act slot with information + */ +void profregfill(struct pregactivation *act, + struct profregion *pregion, + void *objloc, locksite *codeloc) +{ + act->objloc = NULL; + act->codeloc = NULL; + /* barrier is needed here, because clock interrupt can come at any + * point, and we want our callback to see consistent data */ + barrier(); + act->preg = pregion; + act->objloc = objloc; + act->codeloc = codeloc; +} + +/* + * activate profregion @pregion on processor @cpu. + */ +void profregion_in(int cpu, struct profregion *pregion, + void *objloc, locksite *codeloc) +{ + struct profregionstack *stack; + int ntop; + + preempt_disable(); + stack = &per_cpu(inregion, cpu); + ntop = stack->top; + /* check for stack overflow */ + BUG_ON(ntop == PROFREGION_MAX_DEPTH); + /* store information about @pregion in the next free slot on the + * stack */ + profregfill(&stack->stack[ntop], pregion, objloc, codeloc); + /* put optimization barrier here */ + /* barrier is needed here, because clock interrupt can come at any + * point, and we want our callback to see consistent data */ + barrier(); + ++ stack->top; +} + +/* + * deactivate (leave) @pregion at processor @cpu. + */ +void profregion_ex(int cpu, struct profregion *pregion) +{ + struct profregionstack *stack; + int ntop; + + stack = &per_cpu(inregion, cpu); + ntop = stack->top; + BUG_ON(ntop == 0); + /* + * in the usual case (when locks nest properly), @pregion uses top + * slot of the stack. Free it. + */ + if(likely(stack->stack[ntop - 1].preg == pregion)) { + do { + -- ntop; + } while (ntop > 0 && + stack->stack[ntop - 1].preg == NULL); + /* put optimization barrier here */ + barrier(); + stack->top = ntop; + } else + /* + * Otherwise (locks are not nested), find slot used by + * @prefion and free it. + */ + stack->stack[profregion_find(stack, pregion)].preg = NULL; + preempt_enable(); + put_cpu(); +} + +/* + * simultaneously deactivate top-level profregion in the stack, and activate + * @pregion. This is optimization to serve common case, when profregion + * covering "trying to take lock X" is immediately followed by profregion + * covering "holding lock X". + */ +void profregion_replace(int cpu, struct profregion *pregion, + void *objloc, void *codeloc) +{ + struct profregionstack *stack; + int ntop; + + stack = &per_cpu(inregion, cpu); + ntop = stack->top; + BUG_ON(ntop == 0); + profregfill(&stack->stack[ntop - 1], pregion, objloc, codeloc); +} + +void profregion_functions_end_here(void) { } + +/* REISER4_LOCKPROF */ +#else + +#if defined(CONFIG_REISER4_NOOPT) || defined(CONFIG_KGDB) + +locksite __hits; +locksite __hits_h; +locksite __hits_t; +locksite __hits_held; +locksite __hits_trying; + +#endif /* CONFIG_REISER4_NOOPT */ + +/* REISER4_LOCKPROF */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/spinprof.h 2004-09-03 00:57:05.388220128 -0700 @@ -0,0 +1,131 @@ +/* Copyright 2002, 2003, 2004 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* spin lock profiling. See spinprof.c for comments. */ + +#ifndef __SPINPROF_H__ +#define __SPINPROF_H__ + +#include "debug.h" +#include "spin_macros.h" +#include "statcnt.h" + +#include +#include +#include + +#if REISER4_LOCKPROF + +/* maximal number of profiling regions that can be active at the same time */ +#define PROFREGION_MAX_DEPTH (12) + +typedef struct percpu_counter scnt_t; + +/* spin-locking code uses this to identify place in the code, where particular + * call to locking function is made. */ +typedef struct locksite { + statcnt_t hits; /* number of times profiling region that is + * entered at this place of code was found active + * my clock interrupt handler. */ + const char *func; /* function */ + int line; /* line in the source file */ +} locksite; + +/* macro to initialize locksite */ +#define LOCKSITE_INIT(name) \ + static locksite name = { \ + .hits = STATCNT_INIT, \ + .func = __FUNCTION__, \ + .line = __LINE__ \ + } + +/* profiling region */ +struct profregion { + /* how many times clock interrupt handler found this profiling region + * to be at the top of array of active regions. */ + statcnt_t hits; + /* how many times clock interrupt handler found this profiling region + * in active array */ + statcnt_t busy; + /* sysfs handle */ + struct kobject kobj; + /* object that (so far) was observed to be locked/contended most + * through this region */ + void *obj; + /* number of times ->obj's lock was requested/held while in this + * region */ + int objhit; + /* place in code that (so far) was most active user of this + * profregion */ + locksite *code; + /* number of times clock interrupt handler observed that ->code was in + * this profregion */ + int codehit; + /* + * optional method called when ->obj is changed. Can be used to output + * information about most contended objects. + */ + void (*champion)(struct profregion * preg); +}; + +/* + * slot in profregionstack used when profregion is activated (that is, + * entered). + */ +struct pregactivation { + /* profiling region */ + struct profregion *preg; + /* pointer to hits counter, embedded into object */ + int *objloc; + /* current lock site */ + locksite *codeloc; +}; + +/* + * Stack recording currently active profregion activations. Strictly speaking + * this is not a stack at all, because locks (and profregions) do not + * necessary nest properly. + */ +struct profregionstack { + /* index of next free slot */ + int top; + /* array of slots for profregion activations */ + struct pregactivation stack[PROFREGION_MAX_DEPTH]; +}; + +DECLARE_PER_CPU(struct profregionstack, inregion); + +extern int profregion_register(struct profregion *pregion); +extern void profregion_unregister(struct profregion *pregion); + +extern void profregion_in(int cpu, struct profregion *pregion, + void *objloc, locksite *codeloc); +extern void profregion_ex(int cpu, struct profregion *pregion); +extern void profregion_replace(int cpu, struct profregion *pregion, + void *objloc, void *codeloc); + +/* REISER4_LOCKPROF */ +#else + +struct profregionstack {}; +#define profregion_register(pregion) (0) +#define profregion_unregister(pregion) noop + +typedef struct locksite {} locksite; +#define LOCKSITE_INIT(name) extern locksite name + +/* REISER4_LOCKPROF */ +#endif + +/* __SPINPROF_H__ */ +#endif +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/statcnt.h 2004-09-03 00:57:05.389219976 -0700 @@ -0,0 +1,113 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Efficient counter for statistics collection. */ + +/* + * This is write-optimized statistical counter. There is stock counter of such + * kind (include/linux/percpu_counter.h), but it is read-optimized, that is + * reads are cheap, updates are relatively expensive. We need data-structure + * for our statistical counters (stats.[ch]) that are write-mostly, that is, + * they are updated much more frequently than read. + */ + +#ifndef __STATCNT_H__ +#define __STATCNT_H__ + +#include +#include +#include +#include +#include + +#ifdef CONFIG_SMP + +struct __statcnt_slot { + long count; +} ____cacheline_aligned; + +/* + * counter. + * + * This is an array of integer counters, one per each processor. Each + * individual counter is cacheline aligned, so that processor don't fight for + * cache-lines when accessing this. Such alignment makes this counter _huge_. + * + * To update counter, thread just modifies an element in array corresponding + * to its current processor. + * + * To read value of counter array is scanned and all elements are summed + * up. This means that read value can be slightly inaccurate, because scan is + * not protected by any lock, but that is it. + */ +typedef struct statcnt { + struct __statcnt_slot counters[NR_CPUS]; +} statcnt_t; + +#define STATCNT_INIT \ +{ \ + .counters = { [ 0 ... NR_CPUS - 1 ] = { .count = 0 } } \ +} + +static inline void statcnt_add(statcnt_t *cnt, int val) +{ + int cpu; + + cpu = get_cpu(); + cnt->counters[cpu].count += val; + put_cpu(); +} + +static inline long statcnt_get(statcnt_t *cnt) +{ + long result; + int i; + + for (i = 0, result = 0; i < NR_CPUS; ++i) + result += cnt->counters[i].count; + return result; +} + +/* CONFIG_SMP */ +#else + +typedef struct statcnt { + long count; +} statcnt_t; + +#define STATCNT_INIT { .count = 0 } + +static inline void statcnt_add(statcnt_t *cnt, int val) +{ + cnt->count += val; +} + +static inline long statcnt_get(statcnt_t *cnt) +{ + return cnt->count; +} + +/* CONFIG_SMP */ +#endif + +static inline void statcnt_init(statcnt_t *cnt) +{ + xmemset(cnt, 0, sizeof *cnt); +} + +#define statcnt_reset(cnt) statcnt_init(cnt) + +#define statcnt_inc(cnt) statcnt_add((cnt), +1) +#define statcnt_dec(cnt) statcnt_add((cnt), -1) + +/* __STATCNT_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/stats.c 2004-09-03 00:57:05.392219520 -0700 @@ -0,0 +1,639 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Statistical facilities. */ + +/* + * Reiser4 has special REISER4_STATS compilation option (flippable through + * kernel configuration process). When it is on, code to collect statistics is + * compiled in. When option is off, code is preprocessed to zilch. + * + * Statistics are ["statistics" as singular is a name of a science, used as + * plural it refers to "classified facts respecting ... any particular class + * or interest" (Webster)] collected in the form of "statistical + * counters". Each counter has type statcnt_t (see statcnt.h). Counters are + * organized into per-super block reiser4_statistics structure. This structure + * contains sub-structures used to group counters. This grouping is only for + * cleanness, it has no effect on execution. + * + * In addition to sub-structures reiser4_statistics also contains array of + * reiser4_level_stat structures used to collect statistics attributable to + * particular level in reiser4 internal tree. + * + * As explained in statcnt.h, statcnt_t is large, hence, reiser4_statistics, + * containing fewscores of counters is _huge_. It is so huge, that it cannot + * be allocated with kmalloc() and vmalloc() is used for this. + * + * reiser4_stat_inc() and reiser4_stat_add() macros defined in stats.h are + * main means of updating statistical counters. Note, that due to the + * construction of statcnt_t counters said macros are completely lock-less + * and, no than less, almost accurate. + * + * Each statistical counter is exported through sysfs (see kattr.c for more + * details). + * + * If you adding new stat-counter, you should add it into appropriate struct + * definition in stats.h (reiser4_level_statistics or reiser4_statistics), and + * add initialization of counter to the reiser4_stat_defs[] or + * reiser4_stat_level_defs[] arrays in this file. + * + * Note: it is probably possible to avoid this duplication by placing some + * description of counters into third file and using preprocessor to generate + * definitions of structs and arrays. + * + */ + +#include "kattr.h" +#include "reiser4.h" +#include "stats.h" +#include "statcnt.h" +#include "super.h" +#include "debug.h" + +#include +#include + +/* + * Data-type describing how to export stat-counter through sysfs. + */ +typedef struct reiser4_stats_cnt { + /* standard object to interact with sysfs */ + reiser4_kattr kattr; + /* offset to the counter from the beginning of embedding data-type + * (reiser4_level_statistics or reiser4_statistics) */ + ptrdiff_t offset; + /* counter size in bytes */ + size_t size; + /* printf(3) format to output counter value */ + const char *format; +} reiser4_stats_cnt; + +/* + * helper macro: return value of counter (of type @type) located at the offset + * @offset (in bytes) from the data-structure located at @ptr. + */ +#define getptrat(type, ptr, offset) ((type *)(((char *)(ptr)) + (offset))) + +/* + * helper macro to define reiser4_stats_cnt describing particular + * stat-counter. + */ +#define DEFINE_STATCNT_0(aname, /* name under which counter will be exported \ + * in sysfs */ \ + afield, /* name of field in the embedding type */ \ + atype, /* data-type of counter */ \ + afmt, /* output printf(3) format */ \ + ashow, /* show method */ \ + astore /* store method */) \ +{ \ + .kattr = { \ + .attr = { \ + .kattr = { \ + .name = (char *)aname, \ + .mode = 0666 /* rw-rw-rw- */ \ + }, \ + .show = ashow, \ + .store = astore \ + }, \ + .cookie = 0, \ + }, \ + .format = afmt "\n", \ + .offset = offsetof(atype, afield), \ + .size = sizeof(((atype *)0)->afield) \ +} + +#if REISER4_STATS + +/* + * return counter corresponding to the @fskattr. struct fs_kattr is embedded + * into reiser4_kattr, and reiser4_kattr is embedded into reiser4_stats_cnt. + */ +static inline reiser4_stats_cnt *getcnt(struct fs_kattr * fskattr) +{ + return container_of(fskattr, reiser4_stats_cnt, kattr.attr); +} + +/* + * ->show() method for "global" --that is, not per-level-- stat-counter. + */ +static ssize_t +show_stat_attr(struct super_block * s, struct fs_kobject * fskobj, + struct fs_kattr * fskattr, char * buf) +{ + char *p; + reiser4_stats_cnt *cnt; + statcnt_t *val; + + /* obtain counter description */ + cnt = getcnt(fskattr); + /* use byte offset stored in description to obtain counter value */ + val = getptrat(statcnt_t, get_super_private(s)->stats, cnt->offset); + p = buf; + KATTR_PRINT(p, buf, cnt->format, statcnt_get(val)); + return (p - buf); +} + +/* + * ->store() method for "global" --that is, not per-level-- stat-counter. + */ +static ssize_t +store_stat_attr(struct super_block * s, struct fs_kobject * fskobj, + struct fs_kattr * fskattr, const char * buf, size_t size) +{ + reiser4_stats_cnt *cnt; + statcnt_t *val; + + /* + * any write into file representing stat-counter in sysfs, resets + * counter to zero + */ + + cnt = getcnt(fskattr); + val = getptrat(statcnt_t, get_super_private(s)->stats, cnt->offset); + statcnt_reset(val); + return size; +} + +/* + * ->show() method for per-level stat-counter + */ +static ssize_t +show_stat_level_attr(struct super_block * s, struct fs_kobject * fskobj, + struct fs_kattr * fskattr, char * buf) +{ + char *p; + reiser4_stats_cnt *cnt; + statcnt_t *val; + int level; + + /* obtain level from reiser4_level_stats_kobj */ + level = container_of(fskobj, reiser4_level_stats_kobj, kobj)->level; + /* obtain counter description */ + cnt = getcnt(fskattr); + /* obtain counter value, using level and byte-offset from the + * beginning of per-level counter struct */ + val = getptrat(statcnt_t, &get_super_private(s)->stats->level[level], + cnt->offset); + p = buf; + KATTR_PRINT(p, buf, cnt->format, statcnt_get(val)); + return (p - buf); +} + +/* + * ->store() method for per-level stat-counter + */ +static ssize_t +store_stat_level_attr(struct super_block * s, struct fs_kobject * fskobj, + struct fs_kattr * fskattr, const char * buf, size_t size) +{ + reiser4_stats_cnt *cnt; + statcnt_t *val; + int level; + + /* + * any write into file representing stat-counter in sysfs, resets + * counter to zero + */ + + level = container_of(fskobj, reiser4_level_stats_kobj, kobj)->level; + cnt = getcnt(fskattr); + val = getptrat(statcnt_t, &get_super_private(s)->stats->level[level], + cnt->offset); + statcnt_reset(val); + return size; +} + +/* + * macro defining reiser4_stats_cnt instance describing particular global + * stat-counter + */ +#define DEFINE_STATCNT(field) \ + DEFINE_STATCNT_0(#field, field, reiser4_stat, "%lu", \ + show_stat_attr, store_stat_attr) + +reiser4_stats_cnt reiser4_stat_defs[] = { + DEFINE_STATCNT(tree.cbk), + DEFINE_STATCNT(tree.cbk_found), + DEFINE_STATCNT(tree.cbk_notfound), + DEFINE_STATCNT(tree.cbk_restart), + DEFINE_STATCNT(tree.cbk_cache_hit), + DEFINE_STATCNT(tree.cbk_cache_miss), + DEFINE_STATCNT(tree.cbk_cache_wrong_node), + DEFINE_STATCNT(tree.cbk_cache_race), + DEFINE_STATCNT(tree.object_lookup_novroot), + DEFINE_STATCNT(tree.object_lookup_moved), + DEFINE_STATCNT(tree.object_lookup_outside), + DEFINE_STATCNT(tree.object_lookup_cannotlock), + DEFINE_STATCNT(tree.object_lookup_restart), + DEFINE_STATCNT(tree.pos_in_parent_hit), + DEFINE_STATCNT(tree.pos_in_parent_miss), + DEFINE_STATCNT(tree.pos_in_parent_set), + DEFINE_STATCNT(tree.fast_insert), + DEFINE_STATCNT(tree.fast_paste), + DEFINE_STATCNT(tree.fast_cut), + DEFINE_STATCNT(tree.reparenting), + DEFINE_STATCNT(tree.rd_key_skew), + DEFINE_STATCNT(tree.check_left_nonuniq), + DEFINE_STATCNT(tree.left_nonuniq_found), + + DEFINE_STATCNT(vfs_calls.open), + DEFINE_STATCNT(vfs_calls.lookup), + DEFINE_STATCNT(vfs_calls.create), + DEFINE_STATCNT(vfs_calls.mkdir), + DEFINE_STATCNT(vfs_calls.symlink), + DEFINE_STATCNT(vfs_calls.mknod), + DEFINE_STATCNT(vfs_calls.rename), + DEFINE_STATCNT(vfs_calls.readlink), + DEFINE_STATCNT(vfs_calls.follow_link), + DEFINE_STATCNT(vfs_calls.setattr), + DEFINE_STATCNT(vfs_calls.getattr), + DEFINE_STATCNT(vfs_calls.read), + DEFINE_STATCNT(vfs_calls.write), + DEFINE_STATCNT(vfs_calls.truncate), + DEFINE_STATCNT(vfs_calls.statfs), + DEFINE_STATCNT(vfs_calls.bmap), + DEFINE_STATCNT(vfs_calls.link), + DEFINE_STATCNT(vfs_calls.llseek), + DEFINE_STATCNT(vfs_calls.readdir), + DEFINE_STATCNT(vfs_calls.ioctl), + DEFINE_STATCNT(vfs_calls.mmap), + DEFINE_STATCNT(vfs_calls.unlink), + DEFINE_STATCNT(vfs_calls.rmdir), + DEFINE_STATCNT(vfs_calls.alloc_inode), + DEFINE_STATCNT(vfs_calls.destroy_inode), + DEFINE_STATCNT(vfs_calls.delete_inode), + DEFINE_STATCNT(vfs_calls.write_super), + DEFINE_STATCNT(vfs_calls.private_data_alloc), + + DEFINE_STATCNT(dir.readdir.calls), + DEFINE_STATCNT(dir.readdir.reset), + DEFINE_STATCNT(dir.readdir.rewind_left), + DEFINE_STATCNT(dir.readdir.left_non_uniq), + DEFINE_STATCNT(dir.readdir.left_restart), + DEFINE_STATCNT(dir.readdir.rewind_right), + DEFINE_STATCNT(dir.readdir.adjust_pos), + DEFINE_STATCNT(dir.readdir.adjust_lt), + DEFINE_STATCNT(dir.readdir.adjust_gt), + DEFINE_STATCNT(dir.readdir.adjust_eq), + + DEFINE_STATCNT(file.page_ops.readpage_calls), + DEFINE_STATCNT(file.page_ops.writepage_calls), + DEFINE_STATCNT(file.tail2extent), + DEFINE_STATCNT(file.extent2tail), + DEFINE_STATCNT(file.find_file_item), + DEFINE_STATCNT(file.find_file_item_via_seal), + DEFINE_STATCNT(file.find_file_item_via_right_neighbor), + DEFINE_STATCNT(file.find_file_item_via_cbk), + + DEFINE_STATCNT(extent.unfm_block_reads), + DEFINE_STATCNT(extent.broken_seals), + DEFINE_STATCNT(extent.bdp_caused_repeats), + + DEFINE_STATCNT(tail.bdp_caused_repeats), + + DEFINE_STATCNT(txnmgr.slept_in_wait_atom), + DEFINE_STATCNT(txnmgr.slept_in_wait_event), + DEFINE_STATCNT(txnmgr.commits), + DEFINE_STATCNT(txnmgr.post_commit_writes), + DEFINE_STATCNT(txnmgr.time_spent_in_commits), + DEFINE_STATCNT(txnmgr.empty_bio), + DEFINE_STATCNT(txnmgr.commit_from_writepage), + DEFINE_STATCNT(txnmgr.capture_equal), + DEFINE_STATCNT(txnmgr.capture_both), + DEFINE_STATCNT(txnmgr.capture_block), + DEFINE_STATCNT(txnmgr.capture_txnh), + DEFINE_STATCNT(txnmgr.capture_none), + DEFINE_STATCNT(txnmgr.restart.atom_begin), + DEFINE_STATCNT(txnmgr.restart.cannot_commit), + DEFINE_STATCNT(txnmgr.restart.should_wait), + DEFINE_STATCNT(txnmgr.restart.flush), + DEFINE_STATCNT(txnmgr.restart.fuse_lock_owners_fused), + DEFINE_STATCNT(txnmgr.restart.fuse_lock_owners), + DEFINE_STATCNT(txnmgr.restart.trylock_throttle), + DEFINE_STATCNT(txnmgr.restart.assign_block), + DEFINE_STATCNT(txnmgr.restart.assign_txnh), + DEFINE_STATCNT(txnmgr.restart.fuse_wait_nonblock), + DEFINE_STATCNT(txnmgr.restart.fuse_wait_slept), + DEFINE_STATCNT(txnmgr.restart.init_fusion_atomf), + DEFINE_STATCNT(txnmgr.restart.init_fusion_atomh), + DEFINE_STATCNT(txnmgr.restart.init_fusion_fused), + + DEFINE_STATCNT(flush.squeezed_completely), + DEFINE_STATCNT(flush.flushed_with_unallocated), + DEFINE_STATCNT(flush.squeezed_leaves), + DEFINE_STATCNT(flush.squeezed_leaf_items), + DEFINE_STATCNT(flush.squeezed_leaf_bytes), + DEFINE_STATCNT(flush.flush), + DEFINE_STATCNT(flush.left), + DEFINE_STATCNT(flush.right), + DEFINE_STATCNT(flush.slept_in_mtflush_sem), + + DEFINE_STATCNT(pool.alloc), + DEFINE_STATCNT(pool.kmalloc), + + DEFINE_STATCNT(seal.perfect_match), + DEFINE_STATCNT(seal.out_of_cache), + + DEFINE_STATCNT(hashes.znode.lookup), + DEFINE_STATCNT(hashes.znode.insert), + DEFINE_STATCNT(hashes.znode.remove), + DEFINE_STATCNT(hashes.znode.scanned), + DEFINE_STATCNT(hashes.zfake.lookup), + DEFINE_STATCNT(hashes.zfake.insert), + DEFINE_STATCNT(hashes.zfake.remove), + DEFINE_STATCNT(hashes.zfake.scanned), + DEFINE_STATCNT(hashes.jnode.lookup), + DEFINE_STATCNT(hashes.jnode.insert), + DEFINE_STATCNT(hashes.jnode.remove), + DEFINE_STATCNT(hashes.jnode.scanned), + DEFINE_STATCNT(hashes.lnode.lookup), + DEFINE_STATCNT(hashes.lnode.insert), + DEFINE_STATCNT(hashes.lnode.remove), + DEFINE_STATCNT(hashes.lnode.scanned), + DEFINE_STATCNT(hashes.eflush.lookup), + DEFINE_STATCNT(hashes.eflush.insert), + DEFINE_STATCNT(hashes.eflush.remove), + DEFINE_STATCNT(hashes.eflush.scanned), + + DEFINE_STATCNT(block_alloc.nohint), + + DEFINE_STATCNT(non_uniq), + + /* pcwb - page common write back */ + DEFINE_STATCNT(pcwb.calls), + DEFINE_STATCNT(pcwb.no_jnode), + DEFINE_STATCNT(pcwb.written), + DEFINE_STATCNT(pcwb.not_written), + + /* cop on capture stats */ + DEFINE_STATCNT(coc.calls), + /* satisfied requests */ + DEFINE_STATCNT(coc.ok_uber), + DEFINE_STATCNT(coc.ok_nopage), + DEFINE_STATCNT(coc.ok_clean), + DEFINE_STATCNT(coc.ok_ovrwr), + DEFINE_STATCNT(coc.ok_reloc), + /* refused copy on capture requests */ + DEFINE_STATCNT(coc.forbidden), + DEFINE_STATCNT(coc.writeback), + DEFINE_STATCNT(coc.flush_queued), + DEFINE_STATCNT(coc.dirty), + DEFINE_STATCNT(coc.eflush), + DEFINE_STATCNT(coc.scan_race), + DEFINE_STATCNT(coc.atom_changed), + DEFINE_STATCNT(coc.coc_race), + DEFINE_STATCNT(coc.coc_wait), +}; + +/* + * macro defining reiser4_stats_cnt instance describing particular per-level + * stat-counter + */ +#define DEFINE_STAT_LEVEL_CNT(field) \ + DEFINE_STATCNT_0(#field, field, \ + reiser4_level_stat, "%lu", \ + show_stat_level_attr, store_stat_level_attr) + +reiser4_stats_cnt reiser4_stat_level_defs[] = { + DEFINE_STAT_LEVEL_CNT(carry_restart), + DEFINE_STAT_LEVEL_CNT(carry_done), + DEFINE_STAT_LEVEL_CNT(carry_left_in_carry), + DEFINE_STAT_LEVEL_CNT(carry_left_in_cache), + DEFINE_STAT_LEVEL_CNT(carry_left_missed), + DEFINE_STAT_LEVEL_CNT(carry_left_not_avail), + DEFINE_STAT_LEVEL_CNT(carry_left_refuse), + DEFINE_STAT_LEVEL_CNT(carry_right_in_carry), + DEFINE_STAT_LEVEL_CNT(carry_right_in_cache), + DEFINE_STAT_LEVEL_CNT(carry_right_missed), + DEFINE_STAT_LEVEL_CNT(carry_right_not_avail), + DEFINE_STAT_LEVEL_CNT(insert_looking_left), + DEFINE_STAT_LEVEL_CNT(insert_looking_right), + DEFINE_STAT_LEVEL_CNT(insert_alloc_new), + DEFINE_STAT_LEVEL_CNT(insert_alloc_many), + DEFINE_STAT_LEVEL_CNT(insert), + DEFINE_STAT_LEVEL_CNT(delete), + DEFINE_STAT_LEVEL_CNT(cut), + DEFINE_STAT_LEVEL_CNT(paste), + DEFINE_STAT_LEVEL_CNT(extent), + DEFINE_STAT_LEVEL_CNT(paste_restarted), + DEFINE_STAT_LEVEL_CNT(update), + DEFINE_STAT_LEVEL_CNT(modify), + DEFINE_STAT_LEVEL_CNT(half_split_race), + DEFINE_STAT_LEVEL_CNT(dk_vs_create_race), + DEFINE_STAT_LEVEL_CNT(track_lh), + DEFINE_STAT_LEVEL_CNT(sibling_search), + DEFINE_STAT_LEVEL_CNT(cbk_key_moved), + DEFINE_STAT_LEVEL_CNT(cbk_met_ghost), + DEFINE_STAT_LEVEL_CNT(object_lookup_start), + + DEFINE_STAT_LEVEL_CNT(jnode.jload), + DEFINE_STAT_LEVEL_CNT(jnode.jload_already), + DEFINE_STAT_LEVEL_CNT(jnode.jload_page), + DEFINE_STAT_LEVEL_CNT(jnode.jload_async), + DEFINE_STAT_LEVEL_CNT(jnode.jload_read), + DEFINE_STAT_LEVEL_CNT(jnode.jput), + DEFINE_STAT_LEVEL_CNT(jnode.jputlast), + + DEFINE_STAT_LEVEL_CNT(znode.lock), + DEFINE_STAT_LEVEL_CNT(znode.lock_iteration), + DEFINE_STAT_LEVEL_CNT(znode.lock_neighbor), + DEFINE_STAT_LEVEL_CNT(znode.lock_neighbor_iteration), + DEFINE_STAT_LEVEL_CNT(znode.lock_read), + DEFINE_STAT_LEVEL_CNT(znode.lock_write), + DEFINE_STAT_LEVEL_CNT(znode.lock_lopri), + DEFINE_STAT_LEVEL_CNT(znode.lock_hipri), + DEFINE_STAT_LEVEL_CNT(znode.lock_contented), + DEFINE_STAT_LEVEL_CNT(znode.lock_uncontented), + DEFINE_STAT_LEVEL_CNT(znode.lock_dying), + DEFINE_STAT_LEVEL_CNT(znode.lock_cannot_lock), + DEFINE_STAT_LEVEL_CNT(znode.lock_can_lock), + DEFINE_STAT_LEVEL_CNT(znode.lock_no_capture), + DEFINE_STAT_LEVEL_CNT(znode.unlock), + DEFINE_STAT_LEVEL_CNT(znode.wakeup), + DEFINE_STAT_LEVEL_CNT(znode.wakeup_found), + DEFINE_STAT_LEVEL_CNT(znode.wakeup_found_read), + DEFINE_STAT_LEVEL_CNT(znode.wakeup_scan), + DEFINE_STAT_LEVEL_CNT(znode.wakeup_convoy), + DEFINE_STAT_LEVEL_CNT(node.lookup.calls), + DEFINE_STAT_LEVEL_CNT(node.lookup.items), + DEFINE_STAT_LEVEL_CNT(node.lookup.binary), + DEFINE_STAT_LEVEL_CNT(node.lookup.seq), + DEFINE_STAT_LEVEL_CNT(node.lookup.found), + DEFINE_STAT_LEVEL_CNT(node.lookup.pos), + DEFINE_STAT_LEVEL_CNT(node.lookup.posrelative), + DEFINE_STAT_LEVEL_CNT(node.lookup.samepos), + DEFINE_STAT_LEVEL_CNT(node.lookup.nextpos), + + DEFINE_STAT_LEVEL_CNT(vm.release.try), + DEFINE_STAT_LEVEL_CNT(vm.release.ok), + DEFINE_STAT_LEVEL_CNT(vm.release.loaded), + DEFINE_STAT_LEVEL_CNT(vm.release.copy), + DEFINE_STAT_LEVEL_CNT(vm.release.eflushed), + DEFINE_STAT_LEVEL_CNT(vm.release.fake), + DEFINE_STAT_LEVEL_CNT(vm.release.dirty), + DEFINE_STAT_LEVEL_CNT(vm.release.ovrwr), + DEFINE_STAT_LEVEL_CNT(vm.release.writeback), + DEFINE_STAT_LEVEL_CNT(vm.release.keepme), + DEFINE_STAT_LEVEL_CNT(vm.release.bitmap), + + DEFINE_STAT_LEVEL_CNT(vm.eflush.called), + DEFINE_STAT_LEVEL_CNT(vm.eflush.ok), + DEFINE_STAT_LEVEL_CNT(vm.eflush.nolonger), + DEFINE_STAT_LEVEL_CNT(vm.eflush.needs_block), + DEFINE_STAT_LEVEL_CNT(vm.eflush.loaded), + DEFINE_STAT_LEVEL_CNT(vm.eflush.queued), + DEFINE_STAT_LEVEL_CNT(vm.eflush.protected), + DEFINE_STAT_LEVEL_CNT(vm.eflush.heard_banshee), + DEFINE_STAT_LEVEL_CNT(vm.eflush.nopage), + DEFINE_STAT_LEVEL_CNT(vm.eflush.writeback), + DEFINE_STAT_LEVEL_CNT(vm.eflush.bitmap), + DEFINE_STAT_LEVEL_CNT(vm.eflush.clustered), + DEFINE_STAT_LEVEL_CNT(vm.eflush.eflushed), + + DEFINE_STAT_LEVEL_CNT(time_slept), + DEFINE_STAT_LEVEL_CNT(total_hits_at_level) +}; + +/* + * print single stat-counter. + */ +static void +print_cnt(reiser4_stats_cnt * cnt, const char * prefix, void * base) +{ + printk("%s%s:\t ", prefix, cnt->kattr.attr.kattr.name); + printk(cnt->format, + statcnt_get(getptrat(statcnt_t, base, cnt->offset))); +} + +/* + * Print statistical data accumulated so far. + * + * This is done during umount if REISER4_STATS_ON_UMOUNT flag is set in + * ->debug_flags. + */ +reiser4_internal void +reiser4_print_stats(void) +{ + reiser4_stat *s; + int i; + + s = get_current_super_private()->stats; + for(i = 0 ; i < sizeof_array(reiser4_stat_defs) ; ++ i) + print_cnt(&reiser4_stat_defs[i], "", s); + + for (i = 0; i < REAL_MAX_ZTREE_HEIGHT; ++i) { + int j; + + if (statcnt_get(&s->level[i].total_hits_at_level) <= 0) + continue; + printk("tree: at level: %i\n", i + LEAF_LEVEL); + for(j = 0 ; j < sizeof_array(reiser4_stat_level_defs) ; ++ j) + print_cnt(&reiser4_stat_level_defs[j], "\t", &s->level[i]); + } +} + +/* + * add all defined per-level stat-counters as attributes to the @kobj. This is + * the way stat-counters are exported through sysfs. + */ +int +reiser4_populate_kattr_level_dir(struct kobject * kobj) +{ + int result; + int i; + + result = 0; + for(i = 0 ; i < sizeof_array(reiser4_stat_level_defs) && !result ; ++ i){ + struct attribute *a; + + a = &reiser4_stat_level_defs[i].kattr.attr.kattr; + result = sysfs_create_file(kobj, a); + } + if (result != 0) + warning("nikita-2921", "Failed to add sysfs level attr: %i, %i", + result, i); + return result; +} + +/* + * initialize stat-counters for a super block. Called during mount. + */ +int reiser4_stat_init(reiser4_stat ** storehere) +{ + reiser4_stat *stats; + statcnt_t *cnt; + int num, i; + + /* sanity check: @stats size is multiple of statcnt_t size */ + cassert((sizeof *stats) / (sizeof *cnt) * (sizeof *cnt) == (sizeof *stats)); + + /* + * allocate @stats via vmalloc_32: it's too large for kmalloc. + */ + stats = vmalloc_32(sizeof *stats); + if (stats == NULL) + return -ENOMEM; + + *storehere = stats; + + num = (sizeof *stats) / (sizeof *cnt); + cnt = (statcnt_t *)stats; + /* + * initialize all counters + */ + for (i = 0; i < num ; ++i, ++cnt) + statcnt_init(cnt); + return 0; +} + +/* + * release resources used by stat-counters. Called during umount. Dual to + * reiser4_stat_init. + */ +void reiser4_stat_done(reiser4_stat ** stats) +{ + vfree(*stats); + *stats = NULL; +} + +#else +void +reiser4_print_stats() +{ +} +#endif + +/* + * populate /sys/fs/reiser4//stats with sub-objects. + */ +reiser4_internal int +reiser4_populate_kattr_dir(struct kobject * kobj UNUSED_ARG) +{ + int result; + ON_STATS(int i); + + result = 0; +#if REISER4_STATS + for(i = 0 ; i < sizeof_array(reiser4_stat_defs) && !result ; ++ i) { + struct attribute *a; + + a = &reiser4_stat_defs[i].kattr.attr.kattr; + result = sysfs_create_file(kobj, a); + } + if (result != 0) + warning("nikita-2920", "Failed to add sysfs attr: %i, %i", + result, i); +#endif + return result; +} + + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/stats.h 2004-09-03 00:57:05.396218912 -0700 @@ -0,0 +1,769 @@ +/* Copyright 2001, 2002, 2003, 2004 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Statistics gathering. See stats.c for comments. */ + +#if !defined( __FS_REISER4_STATS_H__ ) +#define __FS_REISER4_STATS_H__ + +#include "forward.h" +#include "reiser4.h" +#include "debug.h" +#include "statcnt.h" + +/* for __u?? types */ +#include +/* for struct super_block, etc */ +#include +/* for in_interrupt() */ +#include + +#include + +#if REISER4_STATS + +/* following macros update counters from &reiser4_stat below, which + see */ + +#define ON_STATS(e) e +/* statistics gathering features. */ + +#define REISER4_STATS_STRICT (0) + +/* statistical counters collected on each level of internal tree */ +typedef struct reiser4_level_statistics { + /* carries restarted due to deadlock avoidance algorithm */ + statcnt_t carry_restart; + /* carries performed */ + statcnt_t carry_done; + /* how many times carry, trying to find left neighbor of a given node, + found it already in a carry set. */ + statcnt_t carry_left_in_carry; + /* how many times carry, trying to find left neighbor of a given node, + found it already in a memory. */ + statcnt_t carry_left_in_cache; + /* how many times carry, trying to find left neighbor of a given node, + found it is not in a memory. */ + statcnt_t carry_left_missed; + /* how many times carry, trying to find left neighbor of a given node, + found that left neighbor either doesn't exist (we are at the left + border of the tree already), or that there is extent on the left. + */ + statcnt_t carry_left_not_avail; + /* how many times carry, trying to find left neighbor of a given node, + gave this up to avoid deadlock */ + statcnt_t carry_left_refuse; + /* how many times carry, trying to find right neighbor of a given + node, found it already in a carry set. */ + statcnt_t carry_right_in_carry; + /* how many times carry, trying to find right neighbor of a given + node, found it already in a memory. */ + statcnt_t carry_right_in_cache; + /* how many times carry, trying to find right neighbor of a given + node, found it is not in a memory. */ + statcnt_t carry_right_missed; + /* how many times carry, trying to find right neighbor of a given + node, found that right neighbor either doesn't exist (we are at the + right border of the tree already), or that there is extent on the + right. + */ + statcnt_t carry_right_not_avail; + /* how many times insertion has to look into the left neighbor, + searching for the free space. */ + statcnt_t insert_looking_left; + /* how many times insertion has to look into the right neighbor, + searching for the free space. */ + statcnt_t insert_looking_right; + /* how many times insertion has to allocate new node, searching for + the free space. */ + statcnt_t insert_alloc_new; + /* how many times insertion has to allocate several new nodes in a + row, searching for the free space. */ + statcnt_t insert_alloc_many; + /* how many insertions were performed by carry. */ + statcnt_t insert; + /* how many deletions were performed by carry. */ + statcnt_t delete; + /* how many cuts were performed by carry. */ + statcnt_t cut; + /* how many pastes (insertions into existing items) were performed by + carry. */ + statcnt_t paste; + /* how many extent insertions were done by carry. */ + statcnt_t extent; + /* how many paste operations were restarted as insert. */ + statcnt_t paste_restarted; + /* how many updates of delimiting keys were performed by carry. */ + statcnt_t update; + /* how many times carry notified parent node about updates in its + child. */ + statcnt_t modify; + /* how many times node was found reparented at the time when its + parent has to be updated. */ + statcnt_t half_split_race; + /* how many times new node was inserted into sibling list after + concurrent balancing modified right delimiting key if its left + neighbor. + */ + statcnt_t dk_vs_create_race; + /* how many times insert or paste ultimately went into node different + from original target */ + statcnt_t track_lh; + /* how many times sibling lookup required getting that high in a + tree */ + statcnt_t sibling_search; + /* key was moved out of node while thread was waiting for the lock */ + statcnt_t cbk_key_moved; + /* node was moved out of tree while thread was waiting for the lock */ + statcnt_t cbk_met_ghost; + /* how many times vroot ("virtual root") optimization was used during + * tree lookup */ + statcnt_t object_lookup_start; + struct { + /* calls to jload() */ + statcnt_t jload; + /* calls to jload() that found jnode already loaded */ + statcnt_t jload_already; + /* calls to jload() that found page already in memory */ + statcnt_t jload_page; + /* calls to jload() that found jnode with asynchronous io + * started */ + statcnt_t jload_async; + /* calls to jload() that actually had to read data */ + statcnt_t jload_read; + /* calls to jput() */ + statcnt_t jput; + /* calls to jput() that released last reference */ + statcnt_t jputlast; + } jnode; + struct { + /* calls to lock_znode() */ + statcnt_t lock; + /* number of times loop inside lock_znode() was executed */ + statcnt_t lock_iteration; + /* calls to lock_neighbor() */ + statcnt_t lock_neighbor; + /* number of times loop inside lock_neighbor() was executed */ + statcnt_t lock_neighbor_iteration; + /* read locks taken */ + statcnt_t lock_read; + /* write locks taken */ + statcnt_t lock_write; + /* low priority locks taken */ + statcnt_t lock_lopri; + /* high priority locks taken */ + statcnt_t lock_hipri; + /* how many requests for znode long term lock couldn't succeed + * immediately. */ + statcnt_t lock_contented; + /* how many requests for znode long term lock managed to + * succeed immediately. */ + statcnt_t lock_uncontented; + /* attempt to acquire a lock failed, because target node was + * dying */ + statcnt_t lock_dying; + /* lock wasn't immediately available, due to incompatible lock + * mode */ + statcnt_t lock_cannot_lock; + /* lock was immediately available (i.e., without wait) */ + statcnt_t lock_can_lock; + /* no node capture was necessary when acquiring a lock */ + statcnt_t lock_no_capture; + /* number of unlocks */ + statcnt_t unlock; + /* number of times unlock decided to wake up sleeping + * requestors */ + statcnt_t wakeup; + /* number of times requestors were actually found during wake + * up */ + statcnt_t wakeup_found; + /* number of read-mode requestors found */ + statcnt_t wakeup_found_read; + /* number of requestor queue items scanned during wake-up + * processing */ + statcnt_t wakeup_scan; + /* number of requestors bundled into convoys */ + statcnt_t wakeup_convoy; + } znode; + struct { + /* node lookup stats */ + struct { + /* ->lookup() calls */ + statcnt_t calls; + /* items in all nodes */ + statcnt_t items; + /* "hops" of binary search */ + statcnt_t binary; + /* iterations of sequential search */ + statcnt_t seq; + /* how many times key sought for was found */ + statcnt_t found; + /* average position where key was found */ + statcnt_t pos; + /* average position where key was found relative to + * total number of items */ + statcnt_t posrelative; + /* number of times key was found in the same position + * as in the previous lookup in this node */ + statcnt_t samepos; + /* number of times key was found in the next position + * relative to the previous lookup in this node */ + statcnt_t nextpos; + } lookup; + } node; + struct { + /* reiser4_releasepage() stats */ + struct { + /* for how many pages on this level ->releasepage() + * was called. */ + statcnt_t try; + /* how many pages were released on this level */ + statcnt_t ok; + /* + * how many times we failed to release a page, + * because... + */ + /* jnode pinned it in memory */ + statcnt_t loaded; + /* it's coced page */ + statcnt_t copy; + /* it has fake block number */ + statcnt_t fake; + /* it is dirty */ + statcnt_t dirty; + /* it is in the overwrite set */ + statcnt_t ovrwr; + /* it is under writeback */ + statcnt_t writeback; + /* it's anonymous page, and jnode is not yet captured + * into atom. */ + statcnt_t keepme; + /* it's bitmap */ + statcnt_t bitmap; + + /* emergency flush was performed on this page/jnode, + * so it's ok to release */ + statcnt_t eflushed; + } release; + /* emergency flush statistics */ + struct { + /* how many times emergency flush was invoked on this + * level */ + statcnt_t called; + /* eflush was successful */ + statcnt_t ok; + /* jnode ceased to be flushable after lock release */ + statcnt_t nolonger; + /* new block number was needed for eflush */ + statcnt_t needs_block; + /* + * eflush failed, because... + */ + /* jnode is loaded */ + statcnt_t loaded; + /* jnode is in the flush queue */ + statcnt_t queued; + /* jnode is protected (JNODE_PROTECTED bit is on) */ + statcnt_t protected; + /* jnode heard banshee already */ + statcnt_t heard_banshee; + /* jnode has no page */ + statcnt_t nopage; + /* jnode is under writeback */ + statcnt_t writeback; + /* jnode is bitmap */ + statcnt_t bitmap; + /* jnode is crypto-compress cluster */ + statcnt_t clustered; + /* jnode is already eflushed */ + statcnt_t eflushed; + } eflush; + } vm; + /* + * non zero, if there is some other non-zero counter at this tree + * level. Used to suppress processing of higher tree levels, that + * don't exist on the underlying file system. + */ + statcnt_t total_hits_at_level; + /* total time (in jiffies) threads sleep for the longterm locks on + * this level */ + statcnt_t time_slept; +} reiser4_level_stat; + +/* + * hash table statistics. Such object is added to each type safe hash table + * instance (see fs/reiser4/type_safe_hash.h). + */ +typedef struct tshash_stat { + statcnt_t lookup; /* number of lookup calls */ + statcnt_t insert; /* number of insert calls */ + statcnt_t remove; /* number of remove calls */ + statcnt_t scanned; /* total number of items inspected during all + * operations. This can be used to estimate average + * hash-chain depth. */ +} tshash_stat; + +#define TSHASH_LOOKUP(stat) ({ if(stat) statcnt_inc(&stat->lookup); }) +#define TSHASH_INSERT(stat) ({ if(stat) statcnt_inc(&stat->insert); }) +#define TSHASH_REMOVE(stat) ({ if(stat) statcnt_inc(&stat->remove); }) +#define TSHASH_SCANNED(stat) ({ if(stat) statcnt_inc(&stat->scanned); }) + +/* set of statistics counter. This is embedded into super-block when + REISER4_STATS is on. */ +typedef struct reiser4_statistics { + struct { + /* calls to coord_by_key */ + statcnt_t cbk; + /* calls to coord_by_key that found requested key */ + statcnt_t cbk_found; + /* calls to coord_by_key that didn't find requested key */ + statcnt_t cbk_notfound; + /* number of times calls to coord_by_key restarted */ + statcnt_t cbk_restart; + /* calls to coord_by_key that found key in coord cache */ + statcnt_t cbk_cache_hit; + /* calls to coord_by_key that didn't find key in coord + cache */ + statcnt_t cbk_cache_miss; + /* cbk cache search found wrong node */ + statcnt_t cbk_cache_wrong_node; + /* search for key in coord cache raced against parallel + balancing and lose. This should be rare. If not, + update cbk_cache_search() according to comment + therewithin. + */ + statcnt_t cbk_cache_race; + /* + * statistics for vroot ("virtual root") optimization of tree + * lookup. + */ + /* + * vroot usage failed, because... + */ + /* given object has no vroot set */ + statcnt_t object_lookup_novroot; + /* vroot changed due to race with balancing */ + statcnt_t object_lookup_moved; + /* object is not fitted into its vroot any longer */ + statcnt_t object_lookup_outside; + /* failed to lock vroot */ + statcnt_t object_lookup_cannotlock; + + /* tree traversal had to be re-started due to vroot failure */ + statcnt_t object_lookup_restart; + + /* number of times coord of child in its parent, cached + in a former, was reused. */ + statcnt_t pos_in_parent_hit; + /* number of time binary search for child position in + its parent had to be redone. */ + statcnt_t pos_in_parent_miss; + /* number of times position of child in its parent was + cached in the former */ + statcnt_t pos_in_parent_set; + /* how many times carry() was skipped by doing "fast + insertion path". See + fs/reiser4/plugin/node/node.h:->fast_insert() method. + */ + statcnt_t fast_insert; + /* how many times carry() was skipped by doing "fast + paste path". See + fs/reiser4/plugin/node/node.h:->fast_paste() method. + */ + statcnt_t fast_paste; + /* how many times carry() was skipped by doing "fast + cut path". See + fs/reiser4/plugin/node/node.h:->cut_insert() method. + */ + statcnt_t fast_cut; + /* children reparented due to shifts at the parent level */ + statcnt_t reparenting; + /* right delimiting key is not exact */ + statcnt_t rd_key_skew; + statcnt_t check_left_nonuniq; + statcnt_t left_nonuniq_found; + } tree; + reiser4_level_stat level[REISER4_MAX_ZTREE_HEIGHT]; + /* system call statistics. Indicates how many times given system (or, + * sometimes, internal kernel function) was + * invoked. Self-explanatory. */ + struct { + statcnt_t open; + statcnt_t lookup; + statcnt_t create; + statcnt_t mkdir; + statcnt_t symlink; + statcnt_t mknod; + statcnt_t rename; + statcnt_t readlink; + statcnt_t follow_link; + statcnt_t setattr; + statcnt_t getattr; + statcnt_t read; + statcnt_t write; + statcnt_t truncate; + statcnt_t statfs; + statcnt_t bmap; + statcnt_t link; + statcnt_t llseek; + statcnt_t readdir; + statcnt_t ioctl; + statcnt_t mmap; + statcnt_t unlink; + statcnt_t rmdir; + statcnt_t alloc_inode; + statcnt_t destroy_inode; + statcnt_t delete_inode; + statcnt_t write_super; + statcnt_t private_data_alloc; /* allocations of either per + * struct dentry or per struct + * file data */ + } vfs_calls; + struct { + /* readdir stats */ + struct { + /* calls to readdir */ + statcnt_t calls; + /* rewinds to the beginning of directory */ + statcnt_t reset; + /* partial rewinds to the left */ + statcnt_t rewind_left; + /* rewind to left that was completely within sequence + * of duplicate keys */ + statcnt_t left_non_uniq; + /* restarts of rewinds to the left due to hi/lo + * priority locking */ + statcnt_t left_restart; + /* rewinds to the right */ + statcnt_t rewind_right; + /* how many times readdir position has to be adjusted + * due to directory modification. Large readdir + * comment in plugin/dir/dir.c */ + statcnt_t adjust_pos; + /* how many times adjustment was on the left of + * current readdir position */ + statcnt_t adjust_lt; + /* how many times adjustment was on the right of + * current readdir position */ + statcnt_t adjust_gt; + /* how many times adjustment was exactly on the + * current readdir position */ + statcnt_t adjust_eq; + } readdir; + } dir; + + /* statistics of unix file plugin */ + struct { + + struct { + statcnt_t readpage_calls; + statcnt_t writepage_calls; + } page_ops; + + /* number of tail conversions */ + statcnt_t tail2extent; + statcnt_t extent2tail; + + /* find_next_item statistic */ + statcnt_t find_file_item; + statcnt_t find_file_item_via_seal; + statcnt_t find_file_item_via_right_neighbor; + statcnt_t find_file_item_via_cbk; + + } file; + struct { + /* how many unformatted nodes were read */ + statcnt_t unfm_block_reads; + + /* extent_write seals and unlock znode before locking/capturing + page which is to be modified. After page is locked/captured + it validates a seal. Number of found broken seals is stored + here + */ + statcnt_t broken_seals; + + /* extent_write calls balance_dirty_pages after it modifies + every page. Before that it seals node it currently holds + and uses seal_validate to lock it again. This field stores + how many times balance_dirty_pages broke that seal and + caused to repease search tree traversal + */ + statcnt_t bdp_caused_repeats; + /* how many times extent_write could not write a coord and had + * to ask for research */ + statcnt_t repeats; + } extent; + struct { /* stats on tail items */ + /* tail_write calls balance_dirty_pages after every call to + insert_flow. Before that it seals node it currently holds + and uses seal_validate to lock it again. This field stores + how many times balance_dirty_pages broke that seal and + caused to repease search tree traversal + */ + statcnt_t bdp_caused_repeats; + } tail; + /* transaction manager stats */ + struct { + /* jiffies, spent in atom_wait_event() */ + statcnt_t slept_in_wait_event; + /* jiffies, spent in capture_fuse_wait (wait for atom state + * change) */ + statcnt_t slept_in_wait_atom; + /* number of commits */ + statcnt_t commits; + /*number of post commit writes */ + statcnt_t post_commit_writes; + /* jiffies, spent in commits and post commit writes */ + statcnt_t time_spent_in_commits; + /* how many times attempt to write a flush queue ended up with + * an empty bio */ + statcnt_t empty_bio; + /* how many times ->writepage kicked ktxnmged to start commit + * of an atom */ + statcnt_t commit_from_writepage; + + /* + * fs/txnmgrd.c:try_capture_block() stats + */ + + /* atoms of node and transaction handle are the same + * already */ + statcnt_t capture_equal; + /* node and handle both belong to atoms */ + statcnt_t capture_both; + /* only node belongs to atom */ + statcnt_t capture_block; + /* only handle belongs to atom */ + statcnt_t capture_txnh; + /* neither node nor handle belong to atom */ + statcnt_t capture_none; + + /* + * how many times some transaction manager activity had to be + * re-started, because... + */ + struct { + /* new atom was created */ + statcnt_t atom_begin; + /* commit_current_atom() found atom in use */ + statcnt_t cannot_commit; + /* committer had to wait */ + statcnt_t should_wait; + /* jnode_flush was invoked several times in a row */ + statcnt_t flush; + /* fuse_not_fused_lock_owners() fused atoms */ + statcnt_t fuse_lock_owners_fused; + /* fuse_not_fused_lock_owners() has to restart */ + statcnt_t fuse_lock_owners; + /* trylock failed on atom */ + statcnt_t trylock_throttle; + /* atom trylock failed in capture_assign_block() */ + statcnt_t assign_block; + /* atom trylock failed in capture_assign_txnh() */ + statcnt_t assign_txnh; + /* capture_fuse_wait() was called in non-blocking + * mode */ + statcnt_t fuse_wait_nonblock; + /* capture_fuse_wait() had to sleep */ + statcnt_t fuse_wait_slept; + /* capture_init_fusion() failed to try-lock node + * atom */ + statcnt_t init_fusion_atomf; + /* capture_init_fusion() failed to try-lock handle + * atom */ + statcnt_t init_fusion_atomh; + /* capture_init_fusion_locked() slept during fusion */ + statcnt_t init_fusion_fused; + } restart; + } txnmgr; + struct { + /* how many nodes were squeezed to left neighbor completely */ + statcnt_t squeezed_completely; + /* how many times nodes with unallocated children are written */ + statcnt_t flushed_with_unallocated; + /* how many leaves were squeezed to left */ + statcnt_t squeezed_leaves; + /* how many items were squeezed on leaf level */ + statcnt_t squeezed_leaf_items; + /* how mnay bytes were squeezed on leaf level */ + statcnt_t squeezed_leaf_bytes; + /* how many times jnode_flush was called */ + statcnt_t flush; + /* how many nodes were scanned by scan_left() */ + statcnt_t left; + /* how many nodes were scanned by scan_right() */ + statcnt_t right; + /* an overhead of MTFLUSH semaphore */ + statcnt_t slept_in_mtflush_sem; + } flush; + struct { + /* how many carry objects were allocated */ + statcnt_t alloc; + /* how many "extra" carry objects were allocated by + kmalloc. */ + statcnt_t kmalloc; + } pool; + struct { + /* seals that were found pristine */ + statcnt_t perfect_match; + /* how many times node under seal was out of cache */ + statcnt_t out_of_cache; + } seal; + /* hash tables stats. See tshash_stat above. */ + struct { + /* for the hash table of znodes with real block numbers */ + tshash_stat znode; + /* for the hash table of znodes with fake block numbers */ + tshash_stat zfake; + /* for the hash table of jnodes */ + tshash_stat jnode; + /* for the hash table of lnodes */ + tshash_stat lnode; + /* for the hash table of eflush_node_t's */ + tshash_stat eflush; + } hashes; + struct { + /* how many times block was allocated without having valid + * preceder. */ + statcnt_t nohint; + } block_alloc; + /* how many non-unique keys were scanned into tree */ + statcnt_t non_uniq; + + /* page_common_writeback stats */ + struct { + /* calls to ->writepage() */ + statcnt_t calls; + /* ->writepage() failed to allocate jnode for the page */ + statcnt_t no_jnode; + /* emergency flush succeed */ + statcnt_t written; + /* emergency flush failed */ + statcnt_t not_written; + } pcwb; + + /* stat of copy on capture requests */ + struct { + statcnt_t calls; + /* satisfied requests */ + statcnt_t ok_uber; + statcnt_t ok_nopage; + statcnt_t ok_clean; + statcnt_t ok_ovrwr; + statcnt_t ok_reloc; + /* refused copy on capture requests */ + statcnt_t forbidden; + statcnt_t writeback; + statcnt_t flush_queued; + statcnt_t dirty; + statcnt_t eflush; + statcnt_t scan_race; + statcnt_t atom_changed; + statcnt_t coc_race; + statcnt_t coc_wait; + } coc; + + statcnt_t pages_dirty; + statcnt_t pages_clean; +} reiser4_stat; + + +#define get_current_stat() \ + (get_super_private_nocheck(reiser4_get_current_sb())->stats) + +/* Macros to gather statistical data. If REISER4_STATS is disabled, they + are preprocessed to nothing. +*/ + +#define reiser4_stat(sb, cnt) (&get_super_private_nocheck(sb)->stats->cnt) + +#define reiser4_stat_inc_at(sb, counter) \ + statcnt_inc(&get_super_private_nocheck(sb)->stats->counter) + +#define reiser4_stat_inc(counter) \ + ON_CONTEXT(statcnt_inc(&get_current_stat()->counter)) + +#define reiser4_stat_add(counter, delta) \ + ON_CONTEXT(statcnt_add(&get_current_stat()->counter, delta)) + +#define reiser4_stat_inc_at_level(lev, stat) \ +({ \ + int __level; \ + \ + __level = (lev); \ + if (__level >= 0) { \ + if(__level < REISER4_MAX_ZTREE_HEIGHT) { \ + reiser4_stat_inc(level[__level]. stat); \ + reiser4_stat_inc(level[__level]. total_hits_at_level); \ + } \ + } \ +}) + +#define reiser4_stat_add_at_level(lev, stat, value) \ +({ \ + int level; \ + \ + level = (lev); \ + if (level >= 0) { \ + if(level < REISER4_MAX_ZTREE_HEIGHT) { \ + reiser4_stat_add(level[level]. stat , value ); \ + reiser4_stat_inc(level[level]. total_hits_at_level); \ + } \ + } \ +}) + +#define reiser4_stat_level_inc(l, stat) \ + reiser4_stat_inc_at_level((l)->level_no, stat) + + +struct kobject; +extern int reiser4_populate_kattr_level_dir(struct kobject * kobj); +extern int reiser4_stat_init(reiser4_stat ** stats); +extern void reiser4_stat_done(reiser4_stat ** stats); + +/* REISER4_STATS */ +#else + +#define ON_STATS(e) noop + +#define reiser4_stat(sb, cnt) ((void *)NULL) +#define reiser4_stat_inc(counter) noop +#define reiser4_stat_add(counter, delta) noop + +#define reiser4_stat_inc_at(sb, counter) noop +#define reiser4_stat_inc_at_level(lev, stat) noop +#define reiser4_stat_add_at_level(lev, stat, cnt) noop +#define reiser4_stat_level_inc(l, stat) noop + +typedef struct { +} reiser4_stat; + +typedef struct tshash_stat { +} tshash_stat; + +#define TSHASH_LOOKUP(stat) noop +#define TSHASH_INSERT(stat) noop +#define TSHASH_REMOVE(stat) noop +#define TSHASH_SCANNED(stat) noop + +#define reiser4_populate_kattr_level_dir(kobj, i) (0) +#define reiser4_stat_init(s) (0) +#define reiser4_stat_done(s) noop + +#endif + +extern int reiser4_populate_kattr_dir(struct kobject * kobj); + + +/* __FS_REISER4_STATS_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/status_flags.c 2004-09-03 00:57:05.397218760 -0700 @@ -0,0 +1,193 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Functions that deal with reiser4 status block, query status and update it, if needed */ + +#include +#include +#include +#include +#include +#include "debug.h" +#include "dformat.h" +#include "status_flags.h" +#include "super.h" + +/* This is our end I/O handler that marks page uptodate if IO was successful. It also + unconditionally unlocks the page, so we can see that io was done. + We do not free bio, because we hope to reuse that. */ +static int reiser4_status_endio(struct bio *bio, unsigned int bytes_done, int err) +{ + if (bio->bi_size) + return 1; + + if (test_bit(BIO_UPTODATE, &bio->bi_flags)) { + SetPageUptodate(bio->bi_io_vec->bv_page); + } else { + ClearPageUptodate(bio->bi_io_vec->bv_page); + SetPageError(bio->bi_io_vec->bv_page); + } + unlock_page(bio->bi_io_vec->bv_page); +// bio_put(bio); + return 0; +} + +/* Initialise status code. This is expected to be called from the disk format + code. block paremeter is where status block lives. */ +reiser4_internal int reiser4_status_init(reiser4_block_nr block) +{ + struct super_block *sb = reiser4_get_current_sb(); + struct reiser4_status *statuspage; + struct bio *bio; + struct page *page; + + get_super_private(sb)->status_page = NULL; + get_super_private(sb)->status_bio = NULL; + + page = alloc_pages(GFP_KERNEL, 0); + if (!page) + return -ENOMEM; + + bio = bio_alloc(GFP_KERNEL, 1); + if (bio != NULL) { + bio->bi_sector = block * (sb->s_blocksize >> 9); + bio->bi_bdev = sb->s_bdev; + bio->bi_io_vec[0].bv_page = page; + bio->bi_io_vec[0].bv_len = sb->s_blocksize; + bio->bi_io_vec[0].bv_offset = 0; + bio->bi_vcnt = 1; + bio->bi_size = sb->s_blocksize; + bio->bi_end_io = reiser4_status_endio; + } else { + __free_pages(page, 0); + return -ENOMEM; + } + lock_page(page); + submit_bio(READ, bio); + blk_run_address_space(get_super_fake(sb)->i_mapping); + /*blk_run_queues();*/ + wait_on_page_locked(page); + if ( !PageUptodate(page) ) { + warning("green-2007", "I/O error while tried to read status page\n"); + return -EIO; + } + + statuspage = kmap_atomic(page, KM_USER0); + if ( memcmp( statuspage->magic, REISER4_STATUS_MAGIC, sizeof(REISER4_STATUS_MAGIC)) ) { + /* Magic does not match. */ + kunmap_atomic(page, KM_USER0); + warning("green-2008", "Wrong magic in status block\n"); + __free_pages(page, 0); + bio_put(bio); + return -EINVAL; + } + kunmap_atomic(page, KM_USER0); + + get_super_private(sb)->status_page = page; + get_super_private(sb)->status_bio = bio; + return 0; +} + +/* Query the status of fs. Returns if the FS can be safely mounted. + Also if "status" and "extended" parameters are given, it will fill + actual parts of status from disk there. */ +reiser4_internal int reiser4_status_query(u64 *status, u64 *extended) +{ + struct super_block *sb = reiser4_get_current_sb(); + struct reiser4_status *statuspage; + int retval; + + if ( !get_super_private(sb)->status_page ) { // No status page? + return REISER4_STATUS_MOUNT_UNKNOWN; + } + statuspage = kmap_atomic(get_super_private(sb)->status_page, KM_USER0); + switch ( (long)d64tocpu(&statuspage->status) ) { // FIXME: this cast is a hack for 32 bit arches to work. + case REISER4_STATUS_OK: + retval = REISER4_STATUS_MOUNT_OK; + break; + case REISER4_STATUS_CORRUPTED: + retval = REISER4_STATUS_MOUNT_WARN; + break; + case REISER4_STATUS_DAMAGED: + case REISER4_STATUS_DESTROYED: + case REISER4_STATUS_IOERROR: + retval = REISER4_STATUS_MOUNT_RO; + break; + default: + retval = REISER4_STATUS_MOUNT_UNKNOWN; + break; + } + + if ( status ) + *status = d64tocpu(&statuspage->status); + if ( extended ) + *extended = d64tocpu(&statuspage->extended_status); + + kunmap_atomic(get_super_private(sb)->status_page, KM_USER0); + return retval; +} + +/* This function should be called when something bad happens (e.g. from reiser4_panic). + It fills the status structure and tries to push it to disk. */ +reiser4_internal int +reiser4_status_write(u64 status, u64 extended_status, char *message) +{ + struct super_block *sb = reiser4_get_current_sb(); + struct reiser4_status *statuspage; + struct bio *bio = get_super_private(sb)->status_bio; + + if ( !get_super_private(sb)->status_page ) { // No status page? + return -1; + } + statuspage = kmap_atomic(get_super_private(sb)->status_page, KM_USER0); + + cputod64(status, &statuspage->status); + cputod64(extended_status, &statuspage->extended_status); + strncpy(statuspage->texterror, message, REISER4_TEXTERROR_LEN); + +#ifdef CONFIG_FRAME_POINTER +#define GETFRAME(no) \ + cputod64((unsigned long)__builtin_return_address(no), \ + &statuspage->stacktrace[no]) + + GETFRAME(0); + GETFRAME(1); + GETFRAME(2); + GETFRAME(3); + GETFRAME(4); + GETFRAME(5); + GETFRAME(6); + GETFRAME(7); + GETFRAME(8); + GETFRAME(9); + +#undef GETFRAME +#endif + kunmap_atomic(get_super_private(sb)->status_page, KM_USER0); + bio->bi_bdev = sb->s_bdev; + bio->bi_io_vec[0].bv_page = get_super_private(sb)->status_page; + bio->bi_io_vec[0].bv_len = sb->s_blocksize; + bio->bi_io_vec[0].bv_offset = 0; + bio->bi_vcnt = 1; + bio->bi_size = sb->s_blocksize; + bio->bi_end_io = reiser4_status_endio; + lock_page(get_super_private(sb)->status_page); // Safe as nobody should touch our page. + /* We can block now, but we have no other choice anyway */ + submit_bio(WRITE, bio); + blk_run_address_space(get_super_fake(sb)->i_mapping); + /*blk_run_queues();*/ // Now start the i/o. + return 0; // We do not wait for io to finish. +} + +/* Frees the page with status and bio structure. Should be called by disk format at umount time */ +reiser4_internal int reiser4_status_finish(void) +{ + struct super_block *sb = reiser4_get_current_sb(); + + __free_pages(get_super_private(sb)->status_page, 0); + get_super_private(sb)->status_page = NULL; + bio_put(get_super_private(sb)->status_bio); + get_super_private(sb)->status_bio = NULL; + return 0; +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/status_flags.h 2004-09-03 00:57:05.397218760 -0700 @@ -0,0 +1,43 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Here we declare structures and flags that store reiser4 status on disk. + The status that helps us to find out if the filesystem is valid or if it + contains some critical, or not so critical errors */ + +#if !defined( __REISER4_STATUS_FLAGS_H__ ) +#define __REISER4_STATUS_FLAGS_H__ + +#include "dformat.h" +/* These are major status flags */ +#define REISER4_STATUS_OK 0 +#define REISER4_STATUS_CORRUPTED 0x1 +#define REISER4_STATUS_DAMAGED 0x2 +#define REISER4_STATUS_DESTROYED 0x4 +#define REISER4_STATUS_IOERROR 0x8 + +/* Return values for reiser4_status_query() */ +#define REISER4_STATUS_MOUNT_OK 0 +#define REISER4_STATUS_MOUNT_WARN 1 +#define REISER4_STATUS_MOUNT_RO 2 +#define REISER4_STATUS_MOUNT_UNKNOWN -1 + +#define REISER4_TEXTERROR_LEN 256 + +#define REISER4_STATUS_MAGIC "ReiSeR4StATusBl" +/* We probably need to keep its size under sector size which is 512 bytes */ +struct reiser4_status { + char magic[16]; + d64 status; /* Current FS state */ + d64 extended_status; /* Any additional info that might have sense in addition to "status". E.g. + last sector where io error happened if status is "io error encountered" */ + d64 stacktrace[10]; /* Last ten functional calls made (addresses)*/ + char texterror[REISER4_TEXTERROR_LEN]; /* Any error message if appropriate, otherwise filled with zeroes */ +}; + +int reiser4_status_init(reiser4_block_nr block); +int reiser4_status_query(u64 *status, u64 *extended); +int reiser4_status_write(u64 status, u64 extended_status, char *message); +int reiser4_status_finish(void); + +#endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/super.c 2004-09-03 00:57:05.400218304 -0700 @@ -0,0 +1,552 @@ +/* Copyright 2001, 2002, 2003, 2004 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Super-block manipulations. */ + +#include "debug.h" +#include "dformat.h" +#include "key.h" +#include "plugin/security/perm.h" +#include "plugin/space/space_allocator.h" +#include "plugin/plugin.h" +#include "tree.h" +#include "vfs_ops.h" +#include "super.h" +#include "reiser4.h" + +#include /* for __u?? */ +#include /* for struct super_block */ + +/*const __u32 REISER4_SUPER_MAGIC = 0x52345362;*/ /* (*(__u32 *)"R4Sb"); */ + +static __u64 reserved_for_gid(const struct super_block *super, gid_t gid); +static __u64 reserved_for_uid(const struct super_block *super, uid_t uid); +static __u64 reserved_for_root(const struct super_block *super); + +/* Return reiser4-specific part of super block */ +reiser4_internal reiser4_super_info_data * +get_super_private_nocheck(const struct super_block *super /* super block + * queried */ ) +{ + return (reiser4_super_info_data *) super->s_fs_info; +} + + +/* Return reiser4 fstype: value that is returned in ->f_type field by statfs() */ +reiser4_internal long +statfs_type(const struct super_block *super UNUSED_ARG /* super block + * queried */ ) +{ + assert("nikita-448", super != NULL); + assert("nikita-449", is_reiser4_super(super)); + return (long) REISER4_SUPER_MAGIC; +} + +/* block size used by file system corresponding to @super */ +reiser4_internal int +reiser4_blksize(const struct super_block *super /* super block queried */ ) +{ + assert("nikita-450", super != NULL); + assert("nikita-451", is_reiser4_super(super)); + /* FIXME-VS: blocksize has to be 512, 1024, 2048, etc */ + assert("zam-391", super->s_blocksize > 0); + return super->s_blocksize; +} + +/* functions to read/modify fields of reiser4_super_info_data */ + +/* get number of blocks in file system */ +reiser4_internal __u64 +reiser4_block_count(const struct super_block * super /* super block + queried */ ) +{ + assert("vs-494", super != NULL); + assert("vs-495", is_reiser4_super(super)); + return get_super_private(super)->block_count; +} + +/* + * number of blocks in the current file system + */ +reiser4_internal __u64 reiser4_current_block_count(void) +{ + return get_current_super_private()->block_count; +} + + +/* set number of block in filesystem */ +reiser4_internal void +reiser4_set_block_count(const struct super_block *super, __u64 nr) +{ + assert("vs-501", super != NULL); + assert("vs-502", is_reiser4_super(super)); + get_super_private(super)->block_count = nr; + /* The proper calculation of the reserved space counter (%5 of device + block counter) we need a 64 bit division which is missing in Linux on + i386 platform. Because we do not need a precise calculation here we + can replace a div64 operation by this combination of multiplication + and shift: 51. / (2^10) == .0498 .*/ + get_super_private(super)->blocks_reserved = ((nr * 51) >> 10); +} + +/* amount of blocks used (allocated for data) in file system */ +reiser4_internal __u64 +reiser4_data_blocks(const struct super_block *super /* super block + queried */ ) +{ + assert("nikita-452", super != NULL); + assert("nikita-453", is_reiser4_super(super)); + return get_super_private(super)->blocks_used; +} + +/* set number of block used in filesystem */ +reiser4_internal void +reiser4_set_data_blocks(const struct super_block *super, __u64 nr) +{ + assert("vs-503", super != NULL); + assert("vs-504", is_reiser4_super(super)); + get_super_private(super)->blocks_used = nr; +} + +/* amount of free blocks in file system */ +reiser4_internal __u64 +reiser4_free_blocks(const struct super_block *super /* super block + queried */ ) +{ + assert("nikita-454", super != NULL); + assert("nikita-455", is_reiser4_super(super)); + return get_super_private(super)->blocks_free; +} + +/* set number of blocks free in filesystem */ +reiser4_internal void +reiser4_set_free_blocks(const struct super_block *super, __u64 nr) +{ + assert("vs-505", super != NULL); + assert("vs-506", is_reiser4_super(super)); + get_super_private(super)->blocks_free = nr; +} + +/* increment reiser4_super_info_data's counter of free blocks */ +reiser4_internal void +reiser4_inc_free_blocks(const struct super_block *super) +{ + assert("vs-496", reiser4_free_blocks(super) < reiser4_block_count(super)); + get_super_private(super)->blocks_free++; +} + +/* get mkfs unique identifier */ +reiser4_internal __u32 +reiser4_mkfs_id(const struct super_block *super /* super block + queried */ ) +{ + assert("vpf-221", super != NULL); + assert("vpf-222", is_reiser4_super(super)); + return get_super_private(super)->mkfs_id; +} + +/* set mkfs unique identifier */ +reiser4_internal void +reiser4_set_mkfs_id(const struct super_block *super, __u32 id) +{ + assert("vpf-223", super != NULL); + assert("vpf-224", is_reiser4_super(super)); + get_super_private(super)->mkfs_id = id; +} + +/* amount of free blocks in file system */ +reiser4_internal __u64 +reiser4_free_committed_blocks(const struct super_block *super) +{ + assert("vs-497", super != NULL); + assert("vs-498", is_reiser4_super(super)); + return get_super_private(super)->blocks_free_committed; +} + +/* this is only used once on mount time to number of free blocks in + filesystem */ +reiser4_internal void +reiser4_set_free_committed_blocks(const struct super_block *super, __u64 nr) +{ + assert("vs-507", super != NULL); + assert("vs-508", is_reiser4_super(super)); + get_super_private(super)->blocks_free_committed = nr; +} + +/* amount of blocks in the file system reserved for @uid and @gid */ +reiser4_internal long +reiser4_reserved_blocks(const struct super_block *super /* super block + queried */ , + uid_t uid /* user id */ , gid_t gid /* group id */ ) +{ + long reserved; + + assert("nikita-456", super != NULL); + assert("nikita-457", is_reiser4_super(super)); + + reserved = 0; + if (REISER4_SUPPORT_GID_SPACE_RESERVATION) + reserved += reserved_for_gid(super, gid); + if (REISER4_SUPPORT_UID_SPACE_RESERVATION) + reserved += reserved_for_uid(super, uid); + if (REISER4_SUPPORT_ROOT_SPACE_RESERVATION && (uid == 0)) + reserved += reserved_for_root(super); + return reserved; +} + +/* get/set value of/to grabbed blocks counter */ +reiser4_internal __u64 reiser4_grabbed_blocks(const struct super_block * super) +{ + assert("zam-512", super != NULL); + assert("zam-513", is_reiser4_super(super)); + + return get_super_private(super)->blocks_grabbed; +} + +reiser4_internal void +reiser4_set_grabbed_blocks(const struct super_block *super, __u64 nr) +{ + assert("zam-514", super != NULL); + assert("zam-515", is_reiser4_super(super)); + + get_super_private(super)->blocks_grabbed = nr; +} + +reiser4_internal __u64 flush_reserved (const struct super_block *super) +{ + assert ("vpf-285", super != NULL); + assert ("vpf-286", is_reiser4_super (super)); + + return get_super_private(super)->blocks_flush_reserved; +} + +reiser4_internal void +set_flush_reserved (const struct super_block *super, __u64 nr) +{ + assert ("vpf-282", super != NULL); + assert ("vpf-283", is_reiser4_super (super)); + + get_super_private(super)->blocks_flush_reserved = nr; +} + +/* get/set value of/to counter of fake allocated formatted blocks */ +reiser4_internal __u64 reiser4_fake_allocated(const struct super_block *super) +{ + assert("zam-516", super != NULL); + assert("zam-517", is_reiser4_super(super)); + + return get_super_private(super)->blocks_fake_allocated; +} + +reiser4_internal void +reiser4_set_fake_allocated(const struct super_block *super, __u64 nr) +{ + assert("zam-518", super != NULL); + assert("zam-519", is_reiser4_super(super)); + + get_super_private(super)->blocks_fake_allocated = nr; +} + +/* get/set value of/to counter of fake allocated unformatted blocks */ +reiser4_internal __u64 +reiser4_fake_allocated_unformatted(const struct super_block *super) +{ + assert("zam-516", super != NULL); + assert("zam-517", is_reiser4_super(super)); + + return get_super_private(super)->blocks_fake_allocated_unformatted; +} + +reiser4_internal void +reiser4_set_fake_allocated_unformatted(const struct super_block *super, __u64 nr) +{ + assert("zam-518", super != NULL); + assert("zam-519", is_reiser4_super(super)); + + get_super_private(super)->blocks_fake_allocated_unformatted = nr; +} + + +/* get/set value of/to counter of clustered blocks */ +reiser4_internal __u64 reiser4_clustered_blocks(const struct super_block *super) +{ + assert("edward-601", super != NULL); + assert("edward-602", is_reiser4_super(super)); + + return get_super_private(super)->blocks_clustered; +} + +reiser4_internal void +reiser4_set_clustered_blocks(const struct super_block *super, __u64 nr) +{ + assert("edward-603", super != NULL); + assert("edward-604", is_reiser4_super(super)); + + get_super_private(super)->blocks_clustered = nr; +} + +/* space allocator used by this file system */ +reiser4_internal reiser4_space_allocator * +get_space_allocator(const struct super_block * super) +{ + assert("nikita-1965", super != NULL); + assert("nikita-1966", is_reiser4_super(super)); + return &get_super_private(super)->space_allocator; +} + +/* return fake inode used to bind formatted nodes in the page cache */ +reiser4_internal struct inode * +get_super_fake(const struct super_block *super /* super block + queried */ ) +{ + assert("nikita-1757", super != NULL); + return get_super_private(super)->fake; +} + +/* return fake inode used to bind copied on capture nodes in the page cache */ +reiser4_internal struct inode * +get_cc_fake(const struct super_block *super /* super block + queried */ ) +{ + assert("nikita-1757", super != NULL); + return get_super_private(super)->cc; +} + +/* tree used by this file system */ +reiser4_internal reiser4_tree * +get_tree(const struct super_block * super /* super block + * queried */ ) +{ + assert("nikita-460", super != NULL); + assert("nikita-461", is_reiser4_super(super)); + return &get_super_private(super)->tree; +} + +/* Check that @super is (looks like) reiser4 super block. This is mainly for + use in assertions. */ +reiser4_internal int +is_reiser4_super(const struct super_block *super /* super block + * queried */ ) +{ + return + super != NULL && + get_super_private(super) != NULL && + super->s_op == &get_super_private(super)->ops.super; +} + +reiser4_internal int +reiser4_is_set(const struct super_block *super, reiser4_fs_flag f) +{ + return test_bit((int) f, &get_super_private(super)->fs_flags); +} + +/* amount of blocks reserved for given group in file system */ +static __u64 +reserved_for_gid(const struct super_block *super UNUSED_ARG /* super + * block + * queried */ , + gid_t gid UNUSED_ARG /* group id */ ) +{ + return 0; +} + +/* amount of blocks reserved for given user in file system */ +static __u64 +reserved_for_uid(const struct super_block *super UNUSED_ARG /* super + block + queried */ , + uid_t uid UNUSED_ARG /* user id */ ) +{ + return 0; +} + +/* amount of blocks reserved for super user in file system */ +static __u64 +reserved_for_root(const struct super_block *super UNUSED_ARG /* super + block + queried */ ) +{ + return 0; +} + +/* + * true if block number @blk makes sense for the file system at @super. + */ +reiser4_internal int +reiser4_blocknr_is_sane_for(const struct super_block *super, + const reiser4_block_nr *blk) +{ + reiser4_super_info_data *sbinfo; + + assert("nikita-2957", super != NULL); + assert("nikita-2958", blk != NULL); + + if (blocknr_is_fake(blk)) + return 1; + + sbinfo = get_super_private(super); + return *blk < sbinfo->block_count; +} + +/* + * true, if block number @blk makes sense for the current file system + */ +reiser4_internal int +reiser4_blocknr_is_sane(const reiser4_block_nr *blk) +{ + return reiser4_blocknr_is_sane_for(reiser4_get_current_sb(), blk); +} + +/* + * construct various VFS related operation vectors that are embedded into @ops + * inside of @super. + */ +reiser4_internal void +build_object_ops(struct super_block *super, object_ops *ops) +{ + struct inode_operations iops; + + assert("nikita-3248", super != NULL); + assert("nikita-3249", ops != NULL); + + iops = reiser4_inode_operations; + + /* setup super_operations... */ + ops->super = reiser4_super_operations; + /* ...and export operations for NFS */ + ops->export = reiser4_export_operations; + + /* install pointers to the per-super-block vectors into super-block + * fields */ + super->s_op = &ops->super; + super->s_export_op = &ops->export; + + /* cleanup XATTR related fields in inode operations---we don't support + * Linux xattr API... */ + iops.setxattr = NULL; + iops.getxattr = NULL; + iops.listxattr = NULL; + iops.removexattr = NULL; + + /* ...and we don't need ->clear_inode, because its only user was + * xattrs */ + /*ops->super.clear_inode = NULL;*/ + + ops->regular = iops; + ops->dir = iops; + + ops->file = reiser4_file_operations; + ops->symlink = reiser4_symlink_inode_operations; + ops->special = reiser4_special_inode_operations; + ops->dentry = reiser4_dentry_operations; + ops->as = reiser4_as_operations; + + if (reiser4_is_set(super, REISER4_NO_PSEUDO)) { + /* if we don't support pseudo files, we need neither ->open, + * nor ->lookup on regular files */ + ops->regular.lookup = NULL; + ops->file.open = NULL; + } + +} + +#if REISER4_DEBUG_OUTPUT +/* + * debugging function: output human readable information about file system + * parameters + */ +reiser4_internal void +print_fs_info(const char *prefix, const struct super_block *s) +{ + reiser4_super_info_data *sbinfo; + + sbinfo = get_super_private(s); + + printk("================ fs info (%s) =================\n", prefix); + printk("root block: %lli\ntree height: %i\n", sbinfo->tree.root_block, sbinfo->tree.height); + sa_print_info("", get_space_allocator(s)); + + printk("Oids: next to use %llu, in use %llu\n", sbinfo->next_to_use, sbinfo->oids_in_use); + printk("Block counters:\n\tblock count\t%llu\n\tfree blocks\t%llu\n" + "\tused blocks\t%llu\n\tgrabbed\t%llu\n\tfake allocated formatted\t%llu\n" + "\tfake allocated unformatted\t%llu\n", + reiser4_block_count(s), reiser4_free_blocks(s), + reiser4_data_blocks(s), reiser4_grabbed_blocks(s), + reiser4_fake_allocated(s), reiser4_fake_allocated_unformatted(s)); + print_key("Root directory key", sbinfo->df_plug->root_dir_key(s)); + + if ( sbinfo->diskmap_block) + printk("Diskmap is present in %llu block\n", sbinfo->diskmap_block); + else + printk("Diskmap is not present\n"); + + if (sbinfo->df_plug->print_info) { + printk("=========== disk format info (%s) =============\n", sbinfo->df_plug->h.label); + sbinfo->df_plug->print_info(s); + } + +} +#endif + + +#if REISER4_DEBUG + +/* this is caller when unallocated extent pointer is added */ +void +inc_unalloc_unfm_ptr(void) +{ + reiser4_super_info_data *sbinfo; + + sbinfo = get_super_private(get_current_context()->super); + reiser4_spin_lock_sb(sbinfo); + sbinfo->unalloc_extent_pointers ++; + reiser4_spin_unlock_sb(sbinfo); +} + +/* this is called when unallocated extent is converted to allocated */ +void +dec_unalloc_unfm_ptrs(int nr) +{ + reiser4_super_info_data *sbinfo; + + sbinfo = get_super_private(get_current_context()->super); + reiser4_spin_lock_sb(sbinfo); + BUG_ON(sbinfo->unalloc_extent_pointers < nr); + sbinfo->unalloc_extent_pointers -= nr; + reiser4_spin_unlock_sb(sbinfo); +} + +void +inc_unfm_ef(void) +{ + reiser4_super_info_data *sbinfo; + + sbinfo = get_super_private(get_current_context()->super); + reiser4_spin_lock_sb(sbinfo); + sbinfo->eflushed_unformatted ++; + reiser4_spin_unlock_sb(sbinfo); +} + +void +dec_unfm_ef(void) +{ + reiser4_super_info_data *sbinfo; + + sbinfo = get_super_private(get_current_context()->super); + reiser4_spin_lock_sb(sbinfo); + BUG_ON(sbinfo->eflushed_unformatted == 0); + sbinfo->eflushed_unformatted --; + reiser4_spin_unlock_sb(sbinfo); +} + +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/super.h 2004-09-03 00:57:05.403217848 -0700 @@ -0,0 +1,591 @@ +/* Copyright 2001, 2002, 2003, 2004 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Super-block functions. See super.c for details. */ + +#if !defined( __REISER4_SUPER_H__ ) +#define __REISER4_SUPER_H__ + +#include "forward.h" +#include "debug.h" +#include "tree.h" +#include "context.h" +#include "log.h" +#include "lnode.h" +#include "entd.h" +#include "plugin/plugin.h" +#include "prof.h" +#include "wander.h" + +#include "plugin/space/space_allocator.h" + +#include "plugin/disk_format/disk_format40.h" +#include "plugin/security/perm.h" +#include "plugin/dir/dir.h" + +#include "emergency_flush.h" + +#include +#include /* for __u??, etc. */ +#include /* for struct super_block, etc. */ +#include /* for struct list_head */ +#include /* for kobject */ + +/* + * Flush algorithms parameters. + */ +typedef struct { + unsigned relocate_threshold; + unsigned relocate_distance; + unsigned written_threshold; + unsigned scan_maxnodes; +} flush_params; + +typedef enum { + /* True if this file system doesn't support hard-links (multiple + names) for directories: this is default UNIX behavior. + + If hard-links on directoires are not allowed, file system is + Acyclic Directed Graph (modulo dot, and dotdot, of course). + + This is used by reiser4_link(). + */ + REISER4_ADG = 0, + /* set if all nodes in internal tree have the same node layout plugin. + If so, znode_guess_plugin() will return tree->node_plugin in stead + of guessing plugin by plugin id stored in the node. + */ + REISER4_ONE_NODE_PLUGIN = 1, + /* if set, bsd gid assignment is supported. */ + REISER4_BSD_GID = 2, + /* [mac]_time are 32 bit in inode */ + REISER4_32_BIT_TIMES = 3, + /* allow concurrent flushes */ + REISER4_MTFLUSH = 4, + /* disable support for pseudo files. Don't treat regular files as + * directories. */ + REISER4_NO_PSEUDO = 5, + /* load all bitmap blocks at mount time */ + REISER4_DONT_LOAD_BITMAP = 6 +} reiser4_fs_flag; + +#if REISER4_STATS + +/* + * Object to export per-level stat-counters through sysfs. See stats.[ch] and + * kattr.[ch] + */ +typedef struct reiser4_level_stats_kobj { + struct fs_kobject kobj; /* file system kobject exported for each + * level */ + int level; /* tree level */ +} reiser4_level_stats_kobj; + +#endif + +/* + * VFS related operation vectors. + * + * Usually file system has one instance of those, but in reiser4 we sometimes + * want to be able to modify vectors on per-mount basis. For example, reiser4 + * needs ->open method to handle pseudo files correctly, but if file system is + * mounted with "nopseudo" mount option, it's better to have ->open set to + * NULL, as this makes sys_open() a little bit more efficient. + * + */ +typedef struct object_ops { + struct super_operations super; + struct file_operations file; + struct dentry_operations dentry; + struct address_space_operations as; + + struct inode_operations regular; + struct inode_operations dir; + struct inode_operations symlink; + struct inode_operations special; + + struct export_operations export; +} object_ops; + +/* reiser4-specific part of super block + + Locking + + Fields immutable after mount: + + ->oid* + ->space* + ->default_[ug]id + ->mkfs_id + ->trace_flags + ->debug_flags + ->fs_flags + ->df_plug + ->optimal_io_size + ->plug + ->flush + ->u (bad name) + ->txnmgr + ->ra_params + ->fsuid + ->journal_header + ->journal_footer + + Fields protected by ->lnode_guard + + ->lnode_htable + + Fields protected by per-super block spin lock + + ->block_count + ->blocks_used + ->blocks_free + ->blocks_free_committed + ->blocks_grabbed + ->blocks_fake_allocated_unformatted + ->blocks_fake_allocated + ->blocks_flush_reserved + ->eflushed + ->blocknr_hint_default + + After journal replaying during mount, + + ->last_committed_tx + + is protected by ->tmgr.commit_semaphore + + Invariants involving this data-type: + + [sb-block-counts] + [sb-grabbed] + [sb-fake-allocated] +*/ +struct reiser4_super_info_data { + /* guard spinlock which protects reiser4 super + block fields (currently blocks_free, + blocks_free_committed) + */ + reiser4_spin_data guard; + + /* + * object id manager + */ + /* next oid that will be returned by oid_allocate() */ + oid_t next_to_use; + /* total number of used oids */ + oid_t oids_in_use; + + /* space manager plugin */ + reiser4_space_allocator space_allocator; + + /* reiser4 internal tree */ + reiser4_tree tree; + + /* default user id used for light-weight files without their own + stat-data. */ + uid_t default_uid; + + /* default group id used for light-weight files without their own + stat-data. */ + gid_t default_gid; + + /* mkfs identifier generated at mkfs time. */ + __u32 mkfs_id; + /* amount of blocks in a file system */ + __u64 block_count; + + /* inviolable reserve */ + __u64 blocks_reserved; + + /* amount of blocks used by file system data and meta-data. */ + __u64 blocks_used; + + /* amount of free blocks. This is "working" free blocks counter. It is + like "working" bitmap, please see block_alloc.c for description. */ + __u64 blocks_free; + + /* free block count for fs committed state. This is "commit" version + of free block counter. */ + __u64 blocks_free_committed; + + /* number of blocks reserved for further allocation, for all threads. */ + __u64 blocks_grabbed; + + /* number of fake allocated unformatted blocks in tree. */ + __u64 blocks_fake_allocated_unformatted; + + /* number of fake allocated formatted blocks in tree. */ + __u64 blocks_fake_allocated; + + /* number of blocks reserved for flush operations. */ + __u64 blocks_flush_reserved; + + /* number of blocks reserved for cluster operations. */ + __u64 blocks_clustered; + + /* unique file-system identifier */ + /* does this conform to Andreas Dilger UUID stuff? */ + __u32 fsuid; + + /* per-fs tracing flags. Use reiser4_trace_flags enum to set + bits in it. */ + __u32 trace_flags; + + /* per-fs log flags. Use reiser4_log_flags enum to set + bits in it. */ + __u32 log_flags; + __u32 oid_to_log; + + /* file where tracing goes (if enabled). */ + reiser4_log_file log_file; + + /* per-fs debugging flags. This is bitmask populated from + reiser4_debug_flags enum. */ + __u32 debug_flags; + + /* super block flags */ + + /* file-system wide flags. See reiser4_fs_flag enum */ + unsigned long fs_flags; + + /* transaction manager */ + txn_mgr tmgr; + + /* ent thread */ + entd_context entd; + + /* fake inode used to bind formatted nodes */ + struct inode *fake; + /* inode used to bind bitmaps (and journal heads) */ + struct inode *bitmap; + /* inode used to bind copied on capture nodes */ + struct inode *cc; + + /* disk layout plugin */ + disk_format_plugin *df_plug; + + /* disk layout specific part of reiser4 super info data */ + union { + format40_super_info format40; + } u; + + /* + * value we return in st_blksize on stat(2). + */ + unsigned long optimal_io_size; + + /* parameters for the flush algorithm */ + flush_params flush; + + /* see emergency_flush.c for details */ + reiser4_spin_data eflush_guard; + /* number of emergency flushed nodes */ + int eflushed; +#if REISER4_USE_EFLUSH + /* hash table used by emergency flush. Protected by ->eflush_guard */ + ef_hash_table efhash_table; +#endif + /* pointers to jnodes for journal header and footer */ + jnode *journal_header; + jnode *journal_footer; + + journal_location jloc; + + /* head block number of last committed transaction */ + __u64 last_committed_tx; + + /* we remember last written location for using as a hint for + new block allocation */ + __u64 blocknr_hint_default; + + /* committed number of files (oid allocator state variable ) */ + __u64 nr_files_committed; + + ra_params_t ra_params; + + /* A semaphore for serializing cut tree operation if + out-of-free-space: the only one cut_tree thread is allowed to grab + space from reserved area (it is 5% of disk space) */ + struct semaphore delete_sema; + /* task owning ->delete_sema */ + struct task_struct *delete_sema_owner; + + /* serialize semaphore */ + struct semaphore flush_sema; + + /* Diskmap's blocknumber */ + __u64 diskmap_block; + + /* What to do in case of error */ + int onerror; + + /* operations for objects on this file system */ + object_ops ops; + + /* dir_cursor_info see plugin/dir/dir.[ch] for more details */ + d_cursor_info d_info; + +#if REISER4_USE_SYSFS + /* kobject representing this file system. It is visible as + * /sys/fs/reiser4/. All other kobjects for this file system + * (statistical counters, tunables, etc.) are below it in sysfs + * hierarchy. */ + struct fs_kobject kobj; +#endif +#if REISER4_STATS + /* Statistical counters. reiser4_stat is empty data-type unless + REISER4_STATS is set. See stats.[ch] for details. */ + reiser4_stat *stats; + /* kobject for statistical counters. Visible as + * /sys/fs/reiser4//stats */ + struct fs_kobject stats_kobj; + /* kobjects for per-level statistical counters. Each level is visible + as /sys/fs/reiser4//stats-NN */ + reiser4_level_stats_kobj level[REISER4_MAX_ZTREE_HEIGHT]; +#endif +#ifdef CONFIG_REISER4_BADBLOCKS + /* Alternative master superblock offset (in bytes) */ + unsigned long altsuper; +#endif +#if REISER4_LOG + /* last disk block IO was performed against by this file system. Used + * by tree tracing code to track seeks. */ + reiser4_block_nr last_touched; +#endif +#if REISER4_DEBUG + /* minimum used blocks value (includes super blocks, bitmap blocks and + * other fs reserved areas), depends on fs format and fs size. */ + __u64 min_blocks_used; + /* amount of space allocated by kmalloc. For debugging. */ + int kmalloc_allocated; + + /* + * when debugging is on, all jnodes (including znodes, bitmaps, etc.) + * are kept on a list anchored at sbinfo->all_jnodes. This list is + * protected by sbinfo->all_guard spin lock. This lock should be taken + * with _irq modifier, because it is also modified from interrupt + * contexts (by RCU). + */ + + spinlock_t all_guard; + /* list of all jnodes */ + struct list_head all_jnodes; + + /*XXX debugging code */ + __u64 eflushed_unformatted; /* number of eflushed unformatted nodes */ + __u64 unalloc_extent_pointers; /* number of unallocated extent pointers in the tree */ +#endif + struct repacker * repacker; + struct page * status_page; + struct bio * status_bio; +}; + + + +extern reiser4_super_info_data *get_super_private_nocheck(const struct + super_block *super); + +extern struct super_operations reiser4_super_operations; + +/* Return reiser4-specific part of super block */ +static inline reiser4_super_info_data * +get_super_private(const struct super_block * super) +{ + assert("nikita-447", super != NULL); + + return (reiser4_super_info_data *) super->s_fs_info; +} + +/* "Current" super-block: main super block used during current system + call. Reference to this super block is stored in reiser4_context. */ +static inline struct super_block * +reiser4_get_current_sb(void) +{ + return get_current_context()->super; +} + +/* Reiser4-specific part of "current" super-block: main super block used + during current system call. Reference to this super block is stored in + reiser4_context. */ +static inline reiser4_super_info_data * +get_current_super_private(void) +{ + return get_super_private(reiser4_get_current_sb()); +} + +static inline ra_params_t * +get_current_super_ra_params(void) +{ + return &(get_current_super_private()->ra_params); +} + +/* + * true, if file system on @super is read-only + */ +static inline int rofs_super(struct super_block *super) +{ + return super->s_flags & MS_RDONLY; +} + +/* + * true, if @tree represents read-only file system + */ +static inline int rofs_tree(reiser4_tree *tree) +{ + return rofs_super(tree->super); +} + +/* + * true, if file system where @inode lives on, is read-only + */ +static inline int rofs_inode(struct inode *inode) +{ + return rofs_super(inode->i_sb); +} + +/* + * true, if file system where @node lives on, is read-only + */ +static inline int rofs_jnode(jnode *node) +{ + return rofs_tree(jnode_get_tree(node)); +} + +extern __u64 reiser4_current_block_count(void); + +extern void build_object_ops(struct super_block *super, object_ops *ops); + +#define REISER4_SUPER_MAGIC 0x52345362 /* (*(__u32 *)"R4Sb"); */ + +#define spin_ordering_pred_super(private) (1) +SPIN_LOCK_FUNCTIONS(super, reiser4_super_info_data, guard); + +#define spin_ordering_pred_super_eflush(private) (1) +SPIN_LOCK_FUNCTIONS(super_eflush, reiser4_super_info_data, eflush_guard); + +/* + * lock reiser4-specific part of super block + */ +static inline void reiser4_spin_lock_sb(reiser4_super_info_data *sbinfo) +{ + spin_lock_super(sbinfo); +} + +/* + * unlock reiser4-specific part of super block + */ +static inline void reiser4_spin_unlock_sb(reiser4_super_info_data *sbinfo) +{ + spin_unlock_super(sbinfo); +} + +/* + * lock emergency flush data-structures for super block @s + */ +static inline void spin_lock_eflush(const struct super_block * s) +{ + reiser4_super_info_data * sbinfo = get_super_private (s); + spin_lock_super_eflush(sbinfo); +} + +/* + * unlock emergency flush data-structures for super block @s + */ +static inline void spin_unlock_eflush(const struct super_block * s) +{ + reiser4_super_info_data * sbinfo = get_super_private (s); + spin_unlock_super_eflush(sbinfo); +} + + +extern __u64 flush_reserved ( const struct super_block*); +extern void set_flush_reserved ( const struct super_block*, __u64 nr ); +extern int reiser4_is_set(const struct super_block *super, reiser4_fs_flag f); +extern long statfs_type(const struct super_block *super); +extern int reiser4_blksize(const struct super_block *super); +extern __u64 reiser4_block_count(const struct super_block *super); +extern void reiser4_set_block_count(const struct super_block *super, __u64 nr); +extern __u64 reiser4_data_blocks(const struct super_block *super); +extern void reiser4_set_data_blocks(const struct super_block *super, __u64 nr); +extern __u64 reiser4_free_blocks(const struct super_block *super); +extern void reiser4_set_free_blocks(const struct super_block *super, __u64 nr); +extern void reiser4_inc_free_blocks(const struct super_block *super); +extern __u32 reiser4_mkfs_id(const struct super_block *super); +extern void reiser4_set_mkfs_id(const struct super_block *super, __u32 id); + +extern __u64 reiser4_free_committed_blocks(const struct super_block *super); +extern void reiser4_set_free_committed_blocks(const struct super_block *super, __u64 nr); + +extern __u64 reiser4_grabbed_blocks(const struct super_block *); +extern void reiser4_set_grabbed_blocks(const struct super_block *, __u64 nr); +extern __u64 reiser4_fake_allocated(const struct super_block *); +extern void reiser4_set_fake_allocated(const struct super_block *, __u64 nr); +extern __u64 reiser4_fake_allocated_unformatted(const struct super_block *); +extern void reiser4_set_fake_allocated_unformatted(const struct super_block *, __u64 nr); +extern __u64 reiser4_clustered_blocks(const struct super_block *); +extern void reiser4_set_clustered_blocks(const struct super_block *, __u64 nr); + +extern long reiser4_reserved_blocks(const struct super_block *super, uid_t uid, gid_t gid); + +extern reiser4_space_allocator *get_space_allocator(const struct super_block + *super); +extern reiser4_oid_allocator *get_oid_allocator(const struct super_block + *super); +extern struct inode *get_super_fake(const struct super_block *super); +extern struct inode *get_cc_fake(const struct super_block *super); +extern reiser4_tree *get_tree(const struct super_block *super); +extern int is_reiser4_super(const struct super_block *super); + +extern int reiser4_blocknr_is_sane(const reiser4_block_nr *blk); +extern int reiser4_blocknr_is_sane_for(const struct super_block *super, + const reiser4_block_nr *blk); + +/* Maximal possible object id. */ +#define ABSOLUTE_MAX_OID ((oid_t)~0) + +#define OIDS_RESERVED ( 1 << 16 ) +int oid_init_allocator(struct super_block *, oid_t nr_files, oid_t next); +oid_t oid_allocate(struct super_block *); +int oid_release(struct super_block *, oid_t); +oid_t oid_next(const struct super_block *); +void oid_count_allocated(void); +void oid_count_released(void); +long oids_used(const struct super_block *); +long oids_free(const struct super_block *); + + +#if REISER4_DEBUG_OUTPUT +void print_fs_info(const char *prefix, const struct super_block *); +#else +#define print_fs_info(p,s) noop +#endif + +#if REISER4_DEBUG + +void inc_unalloc_unfm_ptr(void); +void dec_unalloc_unfm_ptrs(int nr); +void inc_unfm_ef(void); +void dec_unfm_ef(void); + +#else + +#define inc_unalloc_unfm_ptr() noop +#define dec_unalloc_unfm_ptrs(nr) noop +#define inc_unfm_ef() noop +#define dec_unfm_ef() noop + +#endif + + +/* __REISER4_SUPER_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/syntax.alg 2004-09-03 00:57:05.404217696 -0700 @@ -0,0 +1,152 @@ +Reiser4() Syntax In All Its Obscurities + +

Introduction

+ +

We define a file operations API, that allows accessing files and +performing operations on them. These same files are also accessible +by the usual Linux conventional VFS API. + +

The existing VFS API is appropriate for accessing streams of data +that are larger than the buffers used to hold them, by using simple +hierarchical names. + +

+There exist other needs. + +

The new API provides a very few of them, but more significantly, +makes it easy to add more of them later. Much of what we do in v4 to +expand the existing semantics can be done by accessing special +pseudo-files, and so we implement that. Some of it, particularly +efficiently accessing multiple small files (or "attributes" which +should be implemented as files) with transactions in a single system +call, cannot be done via existing system calls, and requires a new +API. + + +

Why Not Change The VFS All Filesystems Use?

+ +

It is usual to ask, why don't we implement all of the new +functionality in VFS, changing all of the other filesystems, and +getting all of the authors of all of the other filesystems to agree +with the changes and participate in making them. + +

The programming community has a different customary process, for +several reasons. Too many cooks spoil the soup, regardless of how +good they are. Only after the soup is made, can others know whether +our recipe is desirable or to be avoided. Then, using the GPL license +with its restrictions, or obtaining another license from us if +proprietary, they can take what was shown to work well from our code, +and improve it from there. Also, stability is very important to VFS, +and by making our experimental changes to separate code accessed by a +separate system call, we make change less disruptive. Standards are +for when consensus has emerged, not for the tumult of active research. +It is not realistic to think that such a consensus will be quickly +reached, and a working implementation will propell such a consensus +faster than any other form of persuasion. + + +

Library vs. Kernel

+ +

We have no deep reason for not adopting an exo-kernel style +approach. We developed for Linux, and Linux is not an exo-kernel. It +is not appropriate for us to differ from the rest of Linux in this +aspect. I am far from religious about the micro/exo/macro kernel +debates. Micro kernels (e.g. HURD) seem to have substantially lower +performance while being easier to debug. It won't surprise me if +somebody cures the lowered performance someday by being clever. Many +of the developers on our team think we should put as much of our +functionality into the libraries as possible. The danger in doing +that blindly is the danger of experiencing micro-kernel performance +problems. Putting the functionality either all in the kernel +(monolithic kernel (Linux)), or all out of the kernel (exo-kernel) +seems likely to result in the best performance. Selecting certain +functionality which lends itself naturally to being layered above our +interface, and putting it into libraries, is also reasonable, though +there might not be much of it frankly. Functionality that initially +seems likely to someday evolve to access functions not exposed outside +the kernel should be inside the kernel for optimal efficiency, and for +simplicity of kernel interface. If the applications don't see whether +it is in the kernel or in the library, then one can change the +location later. For now our first pass at the problem is to put the +functionality into the kernel part of Reiser4. Perhaps during the +debugging phase we will even pull certain pieces out of the kernel.... + +

Creating Objects

+ +

Every object must have at least one name, though that name might +be just its key. Every object has a key. + +

Objects With Only Keys For Names

+ +Objects with only keys for names can be accessed by the name +"..key/KEY" where KEY is of type key. "..key" is not implemented as a +tradition directory: it has no directory entries, and performs lookups +by looking in the tree for them. Objects that may have only keys for +names must have a flag so indicating in their stat data, or else fsck +will assume they are lost files and put them in lost+found. +Permission to access by key must be explicitly granted, and by default +is not granted to any user even root. + +

Object Plugin Determined At Object Creation Time

+ +

There are various non-symmetries of object creation. Plugins can +only be specified at object creation time (this might change in the +future for some plugins, but it is universally true in v4.0), and +changing plugins requires creating a new file and copying the old file +to the new file. + +

The implication of these non-symmetries is that the syntax for +object creation necessarily must allow for multi-value assignment. +Each plugin must specify a set of assignments to look for, and provide +defaults for the event that they are ommitted. There is even a +default packing locality for objects created without specified names +or packing localities. + +

The object creation method is determined by the name.

+ +

Packing Locality Is Determined At Object Creation Time

+ +

Keys determine what an object is to be packed near. If the packing +locality for an object is specified at creation time, then we use the +specified packing locality. If not, then if a name for the object is +specified at object creation time, then we let the parent directory +present in the name determine its packing locality, which by default +means to set it equal to the packing locality of its parent directory.. +

+The implication of this is that specifying a name for an object at +creation time has an effect in addition to that of specifying a name +at some later time if the packing locality will be inherited from the +parent directory in the name. Also, objects whose packing localities +are equal to the parent directory for their name can have their keys +better compressed. + +

If an object is created without name and without specified packing +locality, then it is assigned to the default packing locality. Since +the next field after packing locality within a key is the objectid, +and object creation time proximity correlates strongly with objectid +proximity due to the algorithms used, this has the effect of causing +objects with creation time proximity to be created near each other in +tree order and thus near each other in disk geometry. This is an +intended effect. Interleaved deletes may complicate this, but some +correlation will remain. There is to be a special directory "/..keys" +which allows listing of all objects in the FS by key. + +

We Offer An Object, Not Block, Storage Interface

+ +

Allowing applications to reserve and control specific blocks ala +Franz's exo-kernel is a feature not yet supported, though it would +have advantages for boot loaders and other applications. Our current +mechanism of allowing tails to be turned off, and then allowing +querying of what blocks a file occupies, can be thought of as a poorly +abstracted version of this (and the linux boot loaders currently +support this poor abstraction we offer.) + +

+ascii_command = +

'/home/teletubbies/(..new/(..name<-glove_location,..object_t<-(audit|encrypted), ..perm_t<-acl ); +

glove_location/..acl/(uid=357, access<-denied)); +

/home/teletubbies/glove_location/..audit/..new/(mailto<-"(teletubbies@pbs.org)); +

/home/teletubbies/glove_location<-"(we stole it quite some number of years ago,and put it in the very first loot pile (in the hole near the purple flower).);' + + + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/sys_reiser4_2.c 2004-09-03 00:57:05.408217088 -0700 @@ -0,0 +1,1174 @@ + +/* + * Below is an implementation of hand crafted "recursive descent" parser. It + * is not based on the yacc toolkit. + * + * This parser was initially supposed to be a prototype implementation created + * to develop and test back end methods to be used by the yacc parser. For + * this reason, parser's functions are placed into proto_* name space. + * + * Parser is actually almost non-recursive. That is, it all information + * required during the parsing is kept in a special stack, allocated by + * kmalloc, rather than on the native C stack. Parser uses C recursion, but + * only for the simplicity of prototyping---the only information communicated + * via C stack is integer return codes. It should be trivial to maintain it in + * the allocated stack and to re-write parser in the iterative manner. + * + * Grammar: + * + * expression ::= binary_exp { ; binary_exp } + * + * binary_exp ::= path | path binop binary_exp + * + * path ::= literal | rel_path | / rel_path + * + * literal ::= "string" | #number + * + * rel_path ::= name { / name } + * + * name ::= name_token | ( expression ) + * + * binop ::= <- + * + * + * Examples: + * + * (1) a/b <- /etc/passwd + * + * (2) "foo" + * + * (3) #3 + * + * + * Implementation: + * + * parsing is done in the context represented by data type proto_ctx_t. + * + * Types of terminal tokens are represented by values of proto_token_type_t + * enumeration. Particular tokens, met during parsing are represented by + * instances of proto_token_t. + * + * proto_ctx_t contains a stack used to recursively parse + * sub-expressions. Each subexpression has a value represented by instance of + * proto_val_t. Values have types, types are represented by proto_val_type_t + * enumeration. + * + * Each non-terminal token is parsed by special function, and all terminals + * are parsed by next_token(). + * + * TO BE DONE: + * + * 1. more sophisticated fs_point_t with support for lnodes + * + * 2. hub-based assignment + * + * 3. locking during name resolutions + * + * + * + * + */ + + +#include "debug.h" +#include "lnode.h" + +#include +#include /* mnt{get,put}() */ + +/* maximal recursion depth */ +#define PROTO_LEVELS (100) + +/* types of terminal tokens */ +typedef enum proto_token_type { + TOKEN_NAME, /* file name, part of a pathname */ + TOKEN_SLASH, /* / */ + TOKEN_ASSIGNMENT, /* <- */ + TOKEN_LPAREN, /* ( */ + TOKEN_RPAREN, /* ) */ + TOKEN_STRING, /* "foo" string literal */ + TOKEN_NUMBER, /* #100 decimal number */ + TOKEN_LESS_THAN, /* < */ + TOKEN_GREATER_THAN, /* > */ + TOKEN_EQUAL_TO, /* = */ + TOKEN_SEMICOLON, /* ; */ + TOKEN_COMMA, /* , */ + TOKEN_EOF, /* eof-of-file reached */ + TOKEN_INVALID /* syntax-error */ +} proto_token_type_t; + +/* terminal token */ +typedef struct proto_token { + /* type of the token */ + proto_token_type_t type; + /* position within command, where this token starts */ + int pos; + /* union of data associated with this token */ + union { + struct { + /* for name and string literal: token length */ + int len; + /* offset from ->pos to position where actual token + * content starts */ + int delta; + } name, string; + struct { + /* for number---its value */ + long val; + } number; + } u; +} proto_token_t; + +/* types of values that expressions can result in */ +typedef enum proto_val_type { + /* file system object---pathname results in this */ + VAL_FSOBJ, + /* number---number literal and assignment result in this */ + VAL_NUMBER, + /* string---string literal results in this */ + VAL_STRING, + /* error---ill-formed expression, and execution error result in + * this */ + VAL_ERROR, + /* no value */ + VAL_VOID +} proto_val_type_t; + +/* file system object representation. This is needed to interface with VFS */ +typedef struct fs_point { + struct dentry *dentry; + struct vfsmount *mnt; +} fs_point_t; + +/* value of expression */ +typedef struct proto_val { + /* value type */ + proto_val_type_t type; + /* value itself. Union by various value types. */ + union { + /* VAL_FSOBJ */ + fs_point_t fsobj; + /* VAL_NUMBER */ + long number; + /* VAL_STRING */ + struct { + char *string; + int len; + } string; + /* VAL_ERROR */ + struct { + /* error message */ + char *error; + /* position in a command, where error occurred */ + int error_pos; + } error; + } u; +} proto_val_t; + +/* data maintained for each recursion level. */ +typedef struct proto_level { + /* error message, if error occurred at this level */ + const char *error; + /* error position within command, if error occurred at this level */ + int error_pos; + /* value of expression, calculated at this level */ + proto_val_t val; + /* point in a file system from which relative names are resolved at + * this level */ + fs_point_t cur; +} proto_level_t; + +/* global parsing flags */ +typedef enum proto_flags { + /* set whenever syntax error is detected */ + CTX_PARSE_ERROR = (1 << 0) +} proto_flags_t; + +/* parsing context. */ +typedef struct proto_ctx { + /* global flags */ + __u32 flags; + /* command being parsed and executed */ + const char *command; + /* length of ->command */ + int len; + /* current parsing position within ->command */ + int pos; + /* recursion depth */ + int depth; + /* array of levels */ + proto_level_t *level; + /* where to resolve relative pathnames from */ + fs_point_t cwd; + /* where to resolve absolute pathnames from */ + fs_point_t root; +} proto_ctx_t; + +static int parse_exp(proto_ctx_t *ctx); + +#define PTRACE(ctx, format, ... ) \ +({ \ + ON_TRACE(TRACE_PARSE, "parse: %02i at %i[%c]: %s: " format "\n", \ + ctx->depth, \ + ctx->pos, char_at(ctx, ctx->pos) ? : '.', \ + __FUNCTION__ , __VA_ARGS__); \ +}) + +/* methods to manipulate fs_point_t objects */ + +/* acquire a reference to @fsobj */ +static fs_point_t *fsget(fs_point_t *fsobj) +{ + dget(fsobj->dentry); + mntget(fsobj->mnt); + return fsobj; +} + +/* release a reference to @fsobj */ +static void fsput(fs_point_t *fsobj) +{ + if (fsobj->dentry != NULL) { + dput(fsobj->dentry); + fsobj->dentry = NULL; + } + if (fsobj->mnt != NULL) { + mntput(fsobj->mnt); + fsobj->mnt = NULL; + } +} + +/* duplicate a reference to @src in @dst */ +static fs_point_t *fscpy(fs_point_t *dst, fs_point_t *src) +{ + *dst = *src; + return fsget(dst); +} + +/* current character in a command */ +static char char_at(proto_ctx_t *ctx, int pos) +{ + if (pos < ctx->len) + return ctx->command[pos]; + else + return 0; +} + +/* current level */ +static proto_level_t *get_level(proto_ctx_t *ctx) +{ + assert("nikita-3233", ctx->depth < PROTO_LEVELS); + return &ctx->level[ctx->depth]; +} + +/* current value---value stored in the current level */ +static proto_val_t *get_val(proto_ctx_t *ctx) +{ + return &get_level(ctx)->val; +} + +/* from where relative names should be resolved */ +static fs_point_t *get_cur(proto_ctx_t *ctx) +{ + int i; + + for (i = ctx->depth; i >= 0; -- i) { + if (ctx->level[i].cur.dentry != NULL) + return &ctx->level[i].cur; + } + return &ctx->cwd; +} + +/* move typed value from one location to another */ +static void proto_val_move(proto_val_t *dst, proto_val_t *src) +{ + xmemmove(dst, src, sizeof *dst); + src->type = VAL_VOID; +} + +/* finish with value */ +static void proto_val_put(proto_val_t *val) +{ + switch(val->type) { + case VAL_FSOBJ: + fsput(&val->u.fsobj); + break; + case VAL_STRING: + if (val->u.string.string != NULL) { + kfree(val->u.string.string); + val->u.string.string = NULL; + } + break; + case VAL_NUMBER: + case VAL_ERROR: + case VAL_VOID: + break; + } + val->type = VAL_VOID; +} + +/* move value one level up. Useful when value produced by an expression is the + * value of its sub-expression. */ +static void proto_val_up(proto_ctx_t *ctx) +{ + assert("nikita-3236", ctx->depth > 0); + proto_val_move(&ctx->level[ctx->depth - 1].val, get_val(ctx)); +} + +/* signal an error */ +static void post_error(proto_ctx_t *ctx, char *error) +{ + proto_val_t *val; + + PTRACE(ctx, "%s", error); + + get_level(ctx)->error = error; + get_level(ctx)->error_pos = ctx->pos; + ctx->flags |= CTX_PARSE_ERROR; + val = get_val(ctx); + proto_val_put(val); + val->type = VAL_ERROR; + val->u.error.error = error; + val->u.error.error_pos = ctx->pos; +} + +/* parse string literal */ +static proto_token_type_t extract_string(proto_ctx_t *ctx, int *outpos, + proto_token_t *token) +{ + int len; + int pos; + + /* simplistic string literal---no escape handling. Feel free to + * improve. */ + pos = *outpos; + for (len = 0; ; ++ len, ++ pos) { + char ch; + + ch = char_at(ctx, pos); + if (ch == '"') { + token->type = TOKEN_STRING; + token->u.string.len = len; + /* string literal start with a quote that should be + * skipped */ + token->u.string.delta = 1; + *outpos = pos + 1; + PTRACE(ctx, "%i", len); + break; + } else if (ch == 0) { + token->type = TOKEN_INVALID; + post_error(ctx, "eof in string"); + break; + } + } + return token->type; +} + +static int unhex(char ch) +{ + ch = tolower(ch); + + if (ch >= '0' && ch <= '9') + return ch - '0'; + else if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 0xa; + return 0xff; +} + +/* construct zero number */ +static proto_token_type_t number_zero(proto_token_t *token) +{ + token->type = TOKEN_NUMBER; + token->u.number.val = 0; + return TOKEN_NUMBER; +} + +/* parse number literal */ +static proto_token_type_t extract_number(proto_ctx_t *ctx, int *pos, + proto_token_t *token) +{ + char ch; + int sign; + int base; + long val; + + ch = char_at(ctx, *pos); + + sign = +1; + + if (ch == '+') + ++ *pos; + else if (ch == '-') { + ++ *pos; + sign = -1; + } else if (!isdigit(ch)) { + token->type = TOKEN_INVALID; + *pos = token->pos; + return TOKEN_INVALID; + } + + val = (ch - '0'); + base = 10; + ++ *pos; + if (val == 0) { + base = 010; + if (!isxdigit(char_at(ctx, *pos)) && + isxdigit(char_at(ctx, *pos + 1))) { + /* 0[xXoOdDtT] */ + switch (char_at(ctx, *pos)) { + case 'x': + case 'X': + base = 0x10; + break; + case 'o': + case 'O': + base = 010; + break; + case 'd': + case 'D': + base = 10; + break; + case 't': + case 'T': + base = 2; + break; + default: + return number_zero(token); + } + if (unhex(char_at(ctx, *pos + 1)) >= base) + return number_zero(token); + ++ *pos; + } + } + for (;; ++ *pos) { + int digit; + long newval; + + ch = char_at(ctx, *pos); + if (!isxdigit(ch)) + break; + digit = unhex(ch); + if (digit < 0 || digit >= base) + break; + newval = val * base + digit; + if (newval > val || (val == newval && digit == 0)) + val = newval; + else { + token->type = TOKEN_INVALID; + post_error(ctx, "integer overflow"); + *pos = token->pos; + return TOKEN_INVALID; + } + } + token->type = TOKEN_NUMBER; + PTRACE(ctx, "%li", val); + token->u.number.val = sign * val; + return token->type; +} + +/* parse name token */ +static proto_token_type_t extract_name(proto_ctx_t *ctx, int *pos, + proto_token_t *token) +{ + int len; + + /* name is sequence of any characters save for /, <, and + */ + for (len = 0; ; ++ *pos, ++ len) { + char ch; + + ch = char_at(ctx, *pos); + if (isspace(ch)) + break; + if (ch == 0) + break; + if (strchr("/+-=()[]<>;,", ch) != NULL) + break; + } + if (len == 0) { + token->type = TOKEN_INVALID; + } else { + token->type = TOKEN_NAME; + token->u.name.len = len; + token->u.name.delta = 0; + PTRACE(ctx, "%i", len); + } + return token->type; +} + +static proto_token_type_t extract_extended_string(proto_ctx_t *ctx, int *pos, + proto_token_t *token, + proto_token_type_t ttype) +{ + proto_token_t width; + + /* s:bytes */ + token->type = TOKEN_INVALID; + ++ *pos; + /* :bytes */ + if (extract_number(ctx, pos, &width) == TOKEN_NUMBER) { + /* :bytes */ + if (char_at(ctx, *pos) == ':') { + ++ *pos; + /* bytes */ + token->type = ttype; + token->u.string.len = width.u.number.val; + token->u.string.delta = *pos - token->pos; + *pos += token->u.string.len; + } + } + if (token->type == TOKEN_INVALID) + *pos = token->pos; + return token->type; +} + +/* parse #-literal */ +static proto_token_type_t extract_extended_literal(proto_ctx_t *ctx, int *pos, + proto_token_t *token) +{ + char ch; + + ch = char_at(ctx, *pos); + if (isdigit(ch)) + return extract_number(ctx, pos, token); + + /* "#s:bytes" */ + if (ch == 's') + return extract_extended_string(ctx, pos, token, TOKEN_STRING); + if (ch == 'n') + return extract_extended_string(ctx, pos, token, TOKEN_NAME); + /* put "#" back */ + -- *pos; + token->type = TOKEN_INVALID; + return TOKEN_INVALID; +} + +/* return next token */ +static proto_token_type_t next_token(proto_ctx_t *ctx, + proto_token_t *token) +{ + proto_token_type_t ttype; + int pos; + + /* skip white spaces */ + for (; isspace(char_at(ctx, ctx->pos)) ; ++ ctx->pos) + {;} + + pos = token->pos = ctx->pos; + switch (char_at(ctx, pos ++)) { + case '/': + ttype = TOKEN_SLASH; + break; + case '(': + ttype = TOKEN_LPAREN; + break; + case ')': + ttype = TOKEN_RPAREN; + break; + case ';': + ttype = TOKEN_SEMICOLON; + break; + case ',': + ttype = TOKEN_COMMA; + break; + case '"': + ttype = extract_string(ctx, &pos, token); + break; + case '<': + if (char_at(ctx, pos) == '-') { + ttype = TOKEN_ASSIGNMENT; + ++ pos; + } else + ttype = TOKEN_LESS_THAN; + break; + case 0: + ttype = TOKEN_EOF; + -- pos; + break; + case '#': + ttype = extract_extended_literal(ctx, &pos, token); + break; + default: + -- pos; + ttype = extract_name(ctx, &pos, token); + break; + } + token->type = ttype; + ctx->pos = pos; + PTRACE(ctx, "%i", ttype); + return ttype; +} + +/* push token back into command, so that next_token() will return @token + * again */ +static void back_token(proto_ctx_t *ctx, proto_token_t *token) +{ + assert("nikita-3237", ctx->pos >= token->pos); + /* it is -that- simple */ + ctx->pos = token->pos; +} + +/* finish with context, release all resources */ +static void ctx_done(proto_ctx_t *ctx) +{ + if (ctx->level != NULL) { + kfree(ctx->level); + ctx->level = NULL; + } + fsput(&ctx->cwd); + fsput(&ctx->root); +} + +/* initialize context for parsing and executing @command */ +static int ctx_init(proto_ctx_t *ctx, const char *command) +{ + int result; + + xmemset(ctx, 0, sizeof *ctx); + ctx->command = command; + ctx->len = strlen(command); + ctx->level = kmalloc(sizeof (ctx->level[0]) * PROTO_LEVELS, + GFP_KERNEL); + xmemset(ctx->level, 0, sizeof (ctx->level[0]) * PROTO_LEVELS); + if (ctx->level != NULL) { + + read_lock(¤t->fs->lock); + ctx->cwd.dentry = dget(current->fs->pwd); + ctx->cwd.mnt = mntget(current->fs->pwdmnt); + ctx->root.dentry = dget(current->fs->root); + ctx->root.mnt = mntget(current->fs->rootmnt); + read_unlock(¤t->fs->lock); + + result = 0; + } else + result = -ENOMEM; + if (result != 0) + ctx_done(ctx); + return result; +} + +/* go one level deeper to parse and execute sub-expression */ +static int inlevel(proto_ctx_t *ctx) +{ + if (ctx->depth >= PROTO_LEVELS - 1) { + /* handle stack overflow */ + post_error(ctx, "stack overflow"); + return -EOVERFLOW; + } + ++ ctx->depth; + xmemset(get_level(ctx), 0, sizeof *get_level(ctx)); + get_val(ctx)->type = VAL_VOID; + return 0; +} + +/* go one level up */ +static void exlevel(proto_ctx_t *ctx) +{ + assert("nikita-3235", ctx->depth > 0); + proto_val_put(get_val(ctx)); + fsput(&get_level(ctx)->cur); + -- ctx->depth; +} + +/* given @token which should be token for string literal, produce string + * value */ +static void build_string_val(proto_ctx_t *ctx, + proto_token_t *token, proto_val_t *val) +{ + int len; + + assert("nikita-3238", + token->type == TOKEN_STRING || token->type == TOKEN_NAME); + + len = token->u.string.len; + val->type = VAL_STRING; + val->u.string.string = kmalloc(len + 1, GFP_KERNEL); + if (val->u.string.string != NULL) { + strncpy(val->u.string.string, + ctx->command + token->pos + token->u.string.delta, len); + val->u.string.string[len] = 0; + val->u.string.len = len; + } +} + +/* given @token which should be token for a number literal, produce number + * value */ +static void build_number_val(proto_ctx_t *ctx, + proto_token_t *token, proto_val_t *val) +{ + assert("nikita-3245", token->type == TOKEN_NUMBER); + + val->type = VAL_NUMBER; + val->u.number = token->u.number.val; +} + +/* follow mount points. COPIED from fs/namei.c */ +static void follow_mount(fs_point_t * fsobj) +{ + while (d_mountpoint(fsobj->dentry)) { + struct vfsmount *mounted; + + spin_lock(&dcache_lock); + mounted = lookup_mnt(fsobj->mnt, fsobj->dentry); + if (!mounted) { + spin_unlock(&dcache_lock); + break; + } + fsobj->mnt = mntget(mounted); + spin_unlock(&dcache_lock); + dput(fsobj->dentry); + mntput(mounted->mnt_parent); + fsobj->dentry = dget(mounted->mnt_root); + } +} + +/* resolve @name within @parent and return resulting object in @fsobj. + * COPIED from fs/namei.c, fs/dcache.c */ +static int lookup(fs_point_t * parent, const char * name, fs_point_t * fsobj) +{ + unsigned long hash; + struct qstr qname; + int result; + unsigned int c; + + qname.name = name; + c = *(const unsigned char *)name; + + hash = init_name_hash(); + do { + name++; + hash = partial_name_hash(c, hash); + c = *(const unsigned char *)name; + } while (c != 0); + qname.len = name - (const char *) qname.name; + qname.hash = end_name_hash(hash); + + result = 0; + fsobj->dentry = __d_lookup(parent->dentry, &qname); + if (fsobj->dentry == NULL) { + struct inode *dir; + + dir = parent->dentry->d_inode; + down(&dir->i_sem); + fsobj->dentry = d_lookup(parent->dentry, &qname); + if (fsobj->dentry == NULL) { + struct dentry * new; + + new = d_alloc(parent->dentry, &qname); + if (new != NULL) { + fsobj->dentry = dir->i_op->lookup(dir, new); + if (fsobj->dentry != NULL) { + dput(new); + result = PTR_ERR(fsobj->dentry); + } else if (new->d_inode != NULL) + fsobj->dentry = new; + else { + dput(new); + result = RETERR(-ENOENT); + } + } else + result = RETERR(-ENOMEM); + } + up(&dir->i_sem); + } + if (result == 0) { + fsobj->mnt = parent->mnt; + follow_mount(fsobj); + } + return result; +} + +#define START_KERNEL_IO \ + { \ + mm_segment_t __ski_old_fs; \ + \ + __ski_old_fs = get_fs(); \ + set_fs(KERNEL_DS) + +#define END_KERNEL_IO \ + set_fs(__ski_old_fs); \ + } + +#define PUMP_BUF_SIZE (PAGE_CACHE_SIZE) + +/* perform actual assignment (copying) from @righthand to @lefthand */ +static int pump(fs_point_t *lefthand, fs_point_t *righthand) +{ + int result; + char *buf; + loff_t readoff; + loff_t writeoff; + struct file *dst; + struct file *src; + + buf = kmalloc(PUMP_BUF_SIZE, GFP_KERNEL); + if (buf == NULL) + return RETERR(-ENOMEM); + + src = dentry_open(righthand->dentry, righthand->mnt, O_RDONLY); + if (!IS_ERR(src)) { + mntget(righthand->mnt); /* simulate open_namei() */ + dget(righthand->dentry); + dst = dentry_open(lefthand->dentry, lefthand->mnt, O_WRONLY); + if (!IS_ERR(dst)) { + mntget(lefthand->mnt); /* simulate open_namei() */ + dget(lefthand->dentry); + readoff = writeoff = 0; + result = 0; + START_KERNEL_IO; + while (result >= 0) { + result = vfs_read(src, + buf, PUMP_BUF_SIZE, &readoff); + if (result <= 0) + break; + /* give other threads chance to run */ + preempt_point(); + result = vfs_write(dst, buf, result, &writeoff); + } + END_KERNEL_IO; + if (result == 0) + result = writeoff; + filp_close(dst, current->files); + } else + result = PTR_ERR(dst); + filp_close(src, current->files); + } else + result = PTR_ERR(src); + + kfree(buf); + return result; +} + +/* perform actual assignment (copying) from buffer to @lefthand */ +static int pump_buf(fs_point_t *lefthand, char *buf, int len) +{ + int result; + loff_t writeoff; + struct file *dst; + + dst = dentry_open(lefthand->dentry, lefthand->mnt, O_WRONLY); + if (!IS_ERR(dst)) { + writeoff = 0; + result = 0; + START_KERNEL_IO; + while (len > writeoff && result >= 0) + result = vfs_write(dst, buf + writeoff, + len - writeoff, &writeoff); + END_KERNEL_IO; + if (result == 0) + result = writeoff; + filp_close(dst, current->files); + } else + result = PTR_ERR(dst); + + return result; +} + +/* prepare and perform assignment, store result at the level */ +static int proto_assign(proto_ctx_t *ctx, proto_val_t *lhs, proto_val_t *rhs) +{ + int result; + fs_point_t dst; + + if (lhs->type != VAL_FSOBJ) { + post_error(ctx, "cannot assign"); + return -EINVAL; + } + + fscpy(&dst, &lhs->u.fsobj); + switch (rhs->type) { + case VAL_FSOBJ: { + result = pump(&dst, &rhs->u.fsobj); + break; + } + case VAL_NUMBER: { + char buf[20]; + + snprintf(buf, sizeof buf, "%li", rhs->u.number); + result = pump_buf(&dst, buf, strlen(buf)); + break; + } + case VAL_STRING: { + result = pump_buf(&dst, rhs->u.string.string, rhs->u.string.len); + break; + } + default: + post_error(ctx, "lnode expected"); + result = -EINVAL; + } + fsput(&dst); + if (result >= 0) { + proto_val_t *ret; + + ret = get_val(ctx); + proto_val_put(ret); + ret->type = VAL_NUMBER; + ret->u.number = result; + result = 0; + } + return result; +} + +/* parse "name" token */ +static int parse_name(proto_ctx_t *ctx) +{ + int result; + proto_token_t token; + + /* name ::= name_token | ( expression ) */ + + next_token(ctx, &token); + PTRACE(ctx, "%i", token.type); + + result = 0; + switch (token.type) { + case TOKEN_NAME: { + proto_val_t name; + fs_point_t child; + + build_string_val(ctx, &token, &name); + result = lookup(get_cur(ctx), name.u.string.string, &child); + if (result == -ENOENT || child.dentry->d_inode == NULL) { + post_error(ctx, "not found"); + result = -ENOENT; + } else if (result == 0) { + proto_val_put(get_val(ctx)); + get_val(ctx)->type = VAL_FSOBJ; + fscpy(&get_val(ctx)->u.fsobj, &child); + } else + post_error(ctx, "lookup failure"); + proto_val_put(&name); + break; + } + case TOKEN_LPAREN: { + proto_token_t rparen; + + result = inlevel(ctx); + if (result == 0) { + result = parse_exp(ctx); + proto_val_up(ctx); + exlevel(ctx); + if (next_token(ctx, &rparen) != TOKEN_RPAREN) { + post_error(ctx, "expecting `)'"); + result = -EINVAL; + } + } + break; + } + case TOKEN_INVALID: + post_error(ctx, "huh"); + result = -EINVAL; + default: + back_token(ctx, &token); + break; + } + return result; +} + +/* parse "path" token */ +static int parse_rel_path(proto_ctx_t *ctx, fs_point_t *start) +{ + int result; + + /* rel_path ::= name { / name } */ + + result = inlevel(ctx); + if (result != 0) + return result; + + fscpy(&get_level(ctx)->cur, start); + + while (1) { + proto_token_t token; + proto_val_t *val; + + result = parse_name(ctx); + if (result != 0) + break; + + val = get_val(ctx); + if (val->type != VAL_FSOBJ) { + post_error(ctx, "name is not an file system object"); + break; + } + + fsput(&get_level(ctx)->cur); + fscpy(&get_level(ctx)->cur, &val->u.fsobj); + + next_token(ctx, &token); + PTRACE(ctx, "%i", token.type); + + if (token.type != TOKEN_SLASH) { + back_token(ctx, &token); + break; + } + } + proto_val_up(ctx); + exlevel(ctx); + return result; +} + +/* parse "path" token */ +static int parse_path(proto_ctx_t *ctx) +{ + int result; + proto_token_t token; + + /* path ::= literal | rel_path | / rel_path */ + + next_token(ctx, &token); + PTRACE(ctx, "%i", token.type); + + result = 0; + switch (token.type) { + case TOKEN_STRING: + build_string_val(ctx, &token, get_val(ctx)); + break; + case TOKEN_NUMBER: + build_number_val(ctx, &token, get_val(ctx)); + break; + case TOKEN_SLASH: + result = parse_rel_path(ctx, &ctx->root); + break; + default: + back_token(ctx, &token); + result = parse_rel_path(ctx, get_cur(ctx)); + break; + case TOKEN_INVALID: + post_error(ctx, "cannot parse path"); + result = -EINVAL; + back_token(ctx, &token); + break; + } + return result; +} + +/* parse "binary_exp" token */ +static int parse_binary_exp(proto_ctx_t *ctx) +{ + int result; + proto_val_t *lhs; + + /* binary_exp ::= path | path binop binary_exp */ + + result = inlevel(ctx); + if (result != 0) + return result; + + result = parse_path(ctx); + if (result == 0) { + proto_token_t token; + + lhs = get_val(ctx); + + next_token(ctx, &token); + PTRACE(ctx, "%i", token.type); + + if (token.type == TOKEN_ASSIGNMENT) { + result = inlevel(ctx); + if (result == 0) { + result = parse_binary_exp(ctx); + if (result == 0) { + proto_val_t *rhs; + + rhs = get_val(ctx); + result = proto_assign(ctx, lhs, rhs); + } + proto_val_up(ctx); + exlevel(ctx); + } + } else + back_token(ctx, &token); + } + proto_val_up(ctx); + exlevel(ctx); + return result; +} + +/* parse "expression" token */ +static int parse_exp(proto_ctx_t *ctx) +{ + int result; + + /* expression ::= binary_exp { ; binary_exp } */ + + result = inlevel(ctx); + if (result != 0) + return result; + + while (1) { + proto_token_t token; + + result = parse_binary_exp(ctx); + proto_val_up(ctx); + if (result != 0) + break; + + next_token(ctx, &token); + PTRACE(ctx, "%i", token.type); + + if (token.type != TOKEN_SEMICOLON) { + back_token(ctx, &token); + break; + } + /* discard value */ + proto_val_put(get_val(ctx)); + } + exlevel(ctx); + return result; +} + +/* execute @command */ +static int execute(proto_ctx_t *ctx) +{ + int result; + + inlevel(ctx); + fscpy(&get_level(ctx)->cur, &ctx->cwd); + result = parse_exp(ctx); + if (get_val(ctx)->type == VAL_NUMBER) + result = get_val(ctx)->u.number; + exlevel(ctx); + assert("nikita-3234", ctx->depth == 0); + if (char_at(ctx, ctx->pos) != 0) { + post_error(ctx, "garbage after expression"); + if (result == 0) + result = -EINVAL; + } + + if (ctx->flags & CTX_PARSE_ERROR) { + int i; + + printk("Syntax error in ``%s''\n", ctx->command); + for (i = PROTO_LEVELS - 1; i >= 0; --i) { + proto_level_t *level; + + level = &ctx->level[i]; + if (level->error != NULL) { + printk(" %02i: %s at %i\n", + i, level->error, level->error_pos); + } + } + result = -EINVAL; + } + return result; +} + +/* entry point */ +asmlinkage long sys_reiser4(const char __user * command) +{ + int result; + char * inkernel; + + inkernel = getname(command); + if (!IS_ERR(inkernel)) { + proto_ctx_t ctx; + + result = ctx_init(&ctx, inkernel); + if (result == 0) { + result = execute(&ctx); + ctx_done(&ctx); + } + putname(inkernel); + } else + result = PTR_ERR(inkernel); + return result; +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/sys_reiser4.c 2004-09-03 00:57:07.618881016 -0700 @@ -0,0 +1,101 @@ +/* System call for accessing enhanced semantics of the Reiser Filesystem Version 4 (reiser4). */ + +/* This system call feeds a string to parser.c, parser.c converts the + string into a set of commands which are executed, and then this + system call returns after the completion of those commands. */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_REISER4_FS_SYSCALL) + +#include "forward.h" +#include "debug.h" +#include "key.h" +#include "kassign.h" +#include "coord.h" +#include "seal.h" +#include "plugin/item/item.h" +#include "plugin/security/perm.h" +#include "plugin/plugin.h" +#include "plugin/object.h" +#include "znode.h" +#include "vfs_ops.h" +#include "inode.h" +#include "super.h" +#include "reiser4.h" + +#include "lnode.h" + +#include "parser/parser.h" + +#define YYREISER4_DEF + +#include "parser/parser.code.c" + + +/* @p_string is a command string for parsing +this function allocates work area for yacc, +initializes fields, calls yacc, free space +and call for execute the generated code */ + +asmlinkage long +sys_reiser4(char *p_string) +{ + long ret; + int *Gencode; + char * str; + struct reiser4_syscall_w_space * work_space ; + str=getname(p_string); + if (!IS_ERR(str)) { + /* allocate work space for parser + working variables, attached to this call */ + if ( (work_space = reiser4_pars_init() ) == NULL ) { + return -ENOMEM; + } + /* initialize fields */ + /* this field used for parsing string, one (inline) stay on begin of string*/ + work_space->ws_pline = str; + work_space->ws_inline = work_space->ws_pline; + PTRACE(work_space, "%s", "begin parsing"); + ret = yyparse(work_space); /* parse command */ + reiser4_pars_free(work_space); + putname(str); + } + else { + ret = PTR_ERR(str); + } + return ret; +} + +#else +asmlinkage long +sys_reiser4(void *p_string) +{ + return -ENOSYS; +} +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/tap.c 2004-09-03 00:57:05.411216632 -0700 @@ -0,0 +1,392 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* + Tree Access Pointer (tap). + + tap is data structure combining coord and lock handle (mostly). It is + useful when one has to scan tree nodes (for example, in readdir, or flush), + for tap functions allow to move tap in either direction transparently + crossing unit/item/node borders. + + Tap doesn't provide automatic synchronization of its fields as it is + supposed to be per-thread object. +*/ + +#include "forward.h" +#include "debug.h" +#include "coord.h" +#include "tree.h" +#include "context.h" +#include "tap.h" +#include "znode.h" +#include "tree_walk.h" + +#if REISER4_DEBUG +static int tap_invariant(const tap_t * tap); +static void tap_check(const tap_t * tap); +#else +#define tap_check(tap) noop +#endif + +/** load node tap is pointing to, if not loaded already */ +reiser4_internal int +tap_load(tap_t * tap) +{ + tap_check(tap); + if (tap->loaded == 0) { + int result; + + result = zload_ra(tap->coord->node, &tap->ra_info); + if (result != 0) + return result; + coord_clear_iplug(tap->coord); + } + ++tap->loaded; + tap_check(tap); + return 0; +} + +/** release node tap is pointing to. Dual to tap_load() */ +reiser4_internal void +tap_relse(tap_t * tap) +{ + tap_check(tap); + if (tap->loaded > 0) { + --tap->loaded; + if (tap->loaded == 0) { + zrelse(tap->coord->node); + } + } + tap_check(tap); +} + +/** + * init tap to consist of @coord and @lh. Locks on nodes will be acquired with + * @mode + */ +reiser4_internal void +tap_init(tap_t * tap, coord_t * coord, lock_handle * lh, znode_lock_mode mode) +{ + tap->coord = coord; + tap->lh = lh; + tap->mode = mode; + tap->loaded = 0; + tap_list_clean(tap); + init_ra_info(&tap->ra_info); +} + +/** add @tap to the per-thread list of all taps */ +reiser4_internal void +tap_monitor(tap_t * tap) +{ + assert("nikita-2623", tap != NULL); + tap_check(tap); + tap_list_push_front(taps_list(), tap); + tap_check(tap); +} + +/* duplicate @src into @dst. Copy lock handle. @dst is not initially + * loaded. */ +reiser4_internal void +tap_copy(tap_t * dst, tap_t * src) +{ + assert("nikita-3193", src != NULL); + assert("nikita-3194", dst != NULL); + + *dst->coord = *src->coord; + if (src->lh->node) + copy_lh(dst->lh, src->lh); + dst->mode = src->mode; + dst->loaded = 0; + tap_list_clean(dst); + dst->ra_info = src->ra_info; +} + +/** finish with @tap */ +reiser4_internal void +tap_done(tap_t * tap) +{ + assert("nikita-2565", tap != NULL); + tap_check(tap); + if (tap->loaded > 0) + zrelse(tap->coord->node); + done_lh(tap->lh); + tap->loaded = 0; + tap_list_remove_clean(tap); + tap->coord->node = NULL; +} + +/** + * move @tap to the new node, locked with @target. Load @target, if @tap was + * already loaded. + */ +reiser4_internal int +tap_move(tap_t * tap, lock_handle * target) +{ + int result = 0; + + assert("nikita-2567", tap != NULL); + assert("nikita-2568", target != NULL); + assert("nikita-2570", target->node != NULL); + assert("nikita-2569", tap->coord->node == tap->lh->node); + + tap_check(tap); + if (tap->loaded > 0) + result = zload_ra(target->node, &tap->ra_info); + + if (result == 0) { + if (tap->loaded > 0) + zrelse(tap->coord->node); + done_lh(tap->lh); + copy_lh(tap->lh, target); + tap->coord->node = target->node; + coord_clear_iplug(tap->coord); + } + tap_check(tap); + return result; +} + +/** + * move @tap to @target. Acquire lock on @target, if @tap was already + * loaded. + */ +reiser4_internal int +tap_to(tap_t * tap, znode * target) +{ + int result; + + assert("nikita-2624", tap != NULL); + assert("nikita-2625", target != NULL); + + tap_check(tap); + result = 0; + if (tap->coord->node != target) { + lock_handle here; + + init_lh(&here); + result = longterm_lock_znode(&here, target, + tap->mode, ZNODE_LOCK_HIPRI); + if (result == 0) { + result = tap_move(tap, &here); + done_lh(&here); + } + } + tap_check(tap); + return result; +} + +/** + * move @tap to given @target, loading and locking @target->node if + * necessary + */ +reiser4_internal int +tap_to_coord(tap_t * tap, coord_t * target) +{ + int result; + + tap_check(tap); + result = tap_to(tap, target->node); + if (result == 0) + coord_dup(tap->coord, target); + tap_check(tap); + return result; +} + +/** return list of all taps */ +reiser4_internal tap_list_head * +taps_list(void) +{ + return &get_current_context()->taps; +} + +/** helper function for go_{next,prev}_{item,unit,node}() */ +reiser4_internal int +go_dir_el(tap_t * tap, sideof dir, int units_p) +{ + coord_t dup; + coord_t *coord; + int result; + + int (*coord_dir) (coord_t *); + int (*get_dir_neighbor) (lock_handle *, znode *, int, int); + void (*coord_init) (coord_t *, const znode *); + ON_DEBUG(int (*coord_check) (const coord_t *)); + + assert("nikita-2556", tap != NULL); + assert("nikita-2557", tap->coord != NULL); + assert("nikita-2558", tap->lh != NULL); + assert("nikita-2559", tap->coord->node != NULL); + + tap_check(tap); + if (dir == LEFT_SIDE) { + coord_dir = units_p ? coord_prev_unit : coord_prev_item; + get_dir_neighbor = reiser4_get_left_neighbor; + coord_init = coord_init_last_unit; + } else { + coord_dir = units_p ? coord_next_unit : coord_next_item; + get_dir_neighbor = reiser4_get_right_neighbor; + coord_init = coord_init_first_unit; + } + ON_DEBUG(coord_check = units_p ? coord_is_existing_unit : coord_is_existing_item); + assert("nikita-2560", coord_check(tap->coord)); + + coord = tap->coord; + coord_dup(&dup, coord); + if (coord_dir(&dup) != 0) { + do { + /* move to the left neighboring node */ + lock_handle dup; + + init_lh(&dup); + result = get_dir_neighbor( + &dup, coord->node, (int) tap->mode, GN_CAN_USE_UPPER_LEVELS); + if (result == 0) { + result = tap_move(tap, &dup); + if (result == 0) + coord_init(tap->coord, dup.node); + done_lh(&dup); + } + /* skip empty nodes */ + } while ((result == 0) && node_is_empty(coord->node)); + } else { + result = 0; + coord_dup(coord, &dup); + } + assert("nikita-2564", ergo(!result, coord_check(tap->coord))); + tap_check(tap); + return result; +} + +/** + * move @tap to the next unit, transparently crossing item and node + * boundaries + */ +reiser4_internal int +go_next_unit(tap_t * tap) +{ + return go_dir_el(tap, RIGHT_SIDE, 1); +} + +/** + * move @tap to the previous unit, transparently crossing item and node + * boundaries + */ +reiser4_internal int +go_prev_unit(tap_t * tap) +{ + return go_dir_el(tap, LEFT_SIDE, 1); +} + +/** + * @shift times apply @actor to the @tap. This is used to move @tap by + * @shift units (or items, or nodes) in either direction. + */ +reiser4_internal int +rewind_to(tap_t * tap, go_actor_t actor, int shift) +{ + int result; + + assert("nikita-2555", shift >= 0); + assert("nikita-2562", tap->coord->node == tap->lh->node); + + tap_check(tap); + result = tap_load(tap); + if (result != 0) + return result; + + for (; shift > 0; --shift) { + result = actor(tap); + assert("nikita-2563", tap->coord->node == tap->lh->node); + if (result != 0) + break; + } + tap_relse(tap); + tap_check(tap); + return result; +} + +/** move @tap @shift units rightward */ +reiser4_internal int +rewind_right(tap_t * tap, int shift) +{ + return rewind_to(tap, go_next_unit, shift); +} + +/** move @tap @shift units leftward */ +reiser4_internal int +rewind_left(tap_t * tap, int shift) +{ + return rewind_to(tap, go_prev_unit, shift); +} + +#if REISER4_DEBUG_OUTPUT +/** debugging function: print @tap content in human readable form */ +reiser4_internal void print_tap(const char * prefix, const tap_t * tap) +{ + if (tap == NULL) { + printk("%s: null tap\n", prefix); + return; + } + printk("%s: loaded: %i, in-list: %i, node: %p, mode: %s\n", prefix, + tap->loaded, tap_list_is_clean(tap), tap->lh->node, + lock_mode_name(tap->mode)); + print_coord("\tcoord", tap->coord, 0); +} +#else +#define print_tap(prefix, tap) noop +#endif + +#if REISER4_DEBUG +/** check [tap-sane] invariant */ +static int tap_invariant(const tap_t * tap) +{ + /* [tap-sane] invariant */ + + if (tap == NULL) + return 1; + /* tap->mode is one of + * + * {ZNODE_NO_LOCK, ZNODE_READ_LOCK, ZNODE_WRITE_LOCK}, and + */ + if (tap->mode != ZNODE_NO_LOCK && + tap->mode != ZNODE_READ_LOCK && tap->mode != ZNODE_WRITE_LOCK) + return 2; + /* tap->coord != NULL, and */ + if (tap->coord == NULL) + return 3; + /* tap->lh != NULL, and */ + if (tap->lh == NULL) + return 4; + /* tap->loaded > 0 => znode_is_loaded(tap->coord->node), and */ + if (!ergo(tap->loaded, znode_is_loaded(tap->coord->node))) + return 5; + /* tap->coord->node == tap->lh->node if tap->lh->node is not 0 */ + if (tap->lh->node != NULL && tap->coord->node != tap->lh->node) + return 6; + return 0; +} + +/** debugging function: check internal @tap consistency */ +static void tap_check(const tap_t * tap) +{ + int result; + + result = tap_invariant(tap); + if (result != 0) { + print_tap("broken", tap); + reiser4_panic("nikita-2831", "tap broken: %i\n", result); + } +} +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/tap.h 2004-09-03 00:57:05.411216632 -0700 @@ -0,0 +1,75 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* Tree Access Pointers. See tap.c for more details. */ + +#if !defined( __REISER4_TAP_H__ ) +#define __REISER4_TAP_H__ + +#include "forward.h" +#include "type_safe_list.h" +#include "readahead.h" + +TYPE_SAFE_LIST_DECLARE(tap); + +/** + tree_access_pointer aka tap. Data structure combining coord_t and lock + handle. + Invariants involving this data-type, see doc/lock-ordering for details: + + [tap-sane] + */ +struct tree_access_pointer { + /* coord tap is at */ + coord_t *coord; + /* lock handle on ->coord->node */ + lock_handle *lh; + /* mode of lock acquired by this tap */ + znode_lock_mode mode; + /* incremented by tap_load(). Decremented by tap_relse(). */ + int loaded; + /* list of taps */ + tap_list_link linkage; + /* read-ahead hint */ + ra_info_t ra_info; +}; + +TYPE_SAFE_LIST_DEFINE(tap, tap_t, linkage); + +typedef int (*go_actor_t) (tap_t * tap); + +extern int tap_load(tap_t * tap); +extern void tap_relse(tap_t * tap); +extern void tap_init(tap_t * tap, coord_t * coord, lock_handle * lh, znode_lock_mode mode); +extern void tap_monitor(tap_t * tap); +extern void tap_copy(tap_t * dst, tap_t * src); +extern void tap_done(tap_t * tap); +extern int tap_move(tap_t * tap, lock_handle * target); +extern int tap_to(tap_t * tap, znode * target); +extern int tap_to_coord(tap_t * tap, coord_t * target); + +extern int go_dir_el(tap_t * tap, sideof dir, int units_p); +extern int go_next_unit(tap_t * tap); +extern int go_prev_unit(tap_t * tap); +extern int rewind_to(tap_t * tap, go_actor_t actor, int shift); +extern int rewind_right(tap_t * tap, int shift); +extern int rewind_left(tap_t * tap, int shift); + +extern tap_list_head *taps_list(void); + +#define for_all_taps( tap ) \ + for (tap = tap_list_front ( taps_list() ); \ + ! tap_list_end ( taps_list(), tap ); \ + tap = tap_list_next ( tap ) ) + +/* __REISER4_TAP_H__ */ +#endif +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/tree.c 2004-09-03 00:57:05.420215264 -0700 @@ -0,0 +1,1829 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* + * KEYS IN A TREE. + * + * The tree consists of nodes located on the disk. Node in the tree is either + * formatted or unformatted. Formatted node is one that has structure + * understood by the tree balancing and traversal code. Formatted nodes are + * further classified into leaf and internal nodes. Latter distinctions is + * (almost) of only historical importance: general structure of leaves and + * internal nodes is the same in Reiser4. Unformatted nodes contain raw data + * that are part of bodies of ordinary files and attributes. + * + * Each node in the tree spawns some interval in the key space. Key ranges for + * all nodes in the tree are disjoint. Actually, this only holds in some weak + * sense, because of the non-unique keys: intersection of key ranges for + * different nodes is either empty, or consists of exactly one key. + * + * Formatted node consists of a sequence of items. Each item spawns some + * interval in key space. Key ranges for all items in a tree are disjoint, + * modulo non-unique keys again. Items within nodes are ordered in the key + * order of the smallest key in a item. + * + * Particular type of item can be further split into units. Unit is piece of + * item that can be cut from item and moved into another item of the same + * time. Units are used by balancing code to repack data during balancing. + * + * Unit can be further split into smaller entities (for example, extent unit + * represents several pages, and it is natural for extent code to operate on + * particular pages and even bytes within one unit), but this is of no + * relevance to the generic balancing and lookup code. + * + * Although item is said to "spawn" range or interval of keys, it is not + * necessary that item contains piece of data addressable by each and every + * key in this range. For example, compound directory item, consisting of + * units corresponding to directory entries and keyed by hashes of file names, + * looks more as having "discrete spectrum": only some disjoint keys inside + * range occupied by this item really address data. + * + * No than less, each item always has well-defined least (minimal) key, that + * is recorded in item header, stored in the node this item is in. Also, item + * plugin can optionally define method ->max_key_inside() returning maximal + * key that can _possibly_ be located within this item. This method is used + * (mainly) to determine when given piece of data should be merged into + * existing item, in stead of creating new one. Because of this, even though + * ->max_key_inside() can be larger that any key actually located in the item, + * intervals + * + * [ min_key( item ), ->max_key_inside( item ) ] + * + * are still disjoint for all items within the _same_ node. + * + * In memory node is represented by znode. It plays several roles: + * + * . something locks are taken on + * + * . something tracked by transaction manager (this is going to change) + * + * . something used to access node data + * + * . something used to maintain tree structure in memory: sibling and + * parental linkage. + * + * . something used to organize nodes into "slums" + * + * More on znodes see in znode.[ch] + * + * DELIMITING KEYS + * + * To simplify balancing, allow some flexibility in locking and speed up + * important coord cache optimization, we keep delimiting keys of nodes in + * memory. Depending on disk format (implemented by appropriate node plugin) + * node on disk can record both left and right delimiting key, only one of + * them, or none. Still, our balancing and tree traversal code keep both + * delimiting keys for a node that is in memory stored in the znode. When + * node is first brought into memory during tree traversal, its left + * delimiting key is taken from its parent, and its right delimiting key is + * either next key in its parent, or is right delimiting key of parent if + * node is the rightmost child of parent. + * + * Physical consistency of delimiting key is protected by special dk + * read-write lock. That is, delimiting keys can only be inspected or + * modified under this lock. But dk lock is only sufficient for fast + * "pessimistic" check, because to simplify code and to decrease lock + * contention, balancing (carry) only updates delimiting keys right before + * unlocking all locked nodes on the given tree level. For example, + * coord-by-key cache scans LRU list of recently accessed znodes. For each + * node it first does fast check under dk spin lock. If key looked for is + * not between delimiting keys for this node, next node is inspected and so + * on. If key is inside of the key range, long term lock is taken on node + * and key range is rechecked. + * + * COORDINATES + * + * To find something in the tree, you supply a key, and the key is resolved + * by coord_by_key() into a coord (coordinate) that is valid as long as the + * node the coord points to remains locked. As mentioned above trees + * consist of nodes that consist of items that consist of units. A unit is + * the smallest and indivisible piece of tree as far as balancing and tree + * search are concerned. Each node, item, and unit can be addressed by + * giving its level in the tree and the key occupied by this entity. A node + * knows what the key ranges are of the items within it, and how to find its + * items and invoke their item handlers, but it does not know how to access + * individual units within its items except through the item handlers. + * coord is a structure containing a pointer to the node, the ordinal number + * of the item within this node (a sort of item offset), and the ordinal + * number of the unit within this item. + * + * TREE LOOKUP + * + * There are two types of access to the tree: lookup and modification. + * + * Lookup is a search for the key in the tree. Search can look for either + * exactly the key given to it, or for the largest key that is not greater + * than the key given to it. This distinction is determined by "bias" + * parameter of search routine (coord_by_key()). coord_by_key() either + * returns error (key is not in the tree, or some kind of external error + * occurred), or successfully resolves key into coord. + * + * This resolution is done by traversing tree top-to-bottom from root level + * to the desired level. On levels above twig level (level one above the + * leaf level) nodes consist exclusively of internal items. Internal item is + * nothing more than pointer to the tree node on the child level. On twig + * level nodes consist of internal items intermixed with extent + * items. Internal items form normal search tree structure used by traversal + * to descent through the tree. + * + * TREE LOOKUP OPTIMIZATIONS + * + * Tree lookup described above is expensive even if all nodes traversed are + * already in the memory: for each node binary search within it has to be + * performed and binary searches are CPU consuming and tend to destroy CPU + * caches. + * + * Several optimizations are used to work around this: + * + * . cbk_cache (look-aside cache for tree traversals, see search.c for + * details) + * + * . seals (see seal.[ch]) + * + * . vroot (see search.c) + * + * General search-by-key is layered thusly: + * + * [check seal, if any] --ok--> done + * | + * failed + * | + * V + * [vroot defined] --no--> node = tree_root + * | | + * yes | + * | | + * V | + * node = vroot | + * | | + * | | + * | | + * V V + * [check cbk_cache for key] --ok--> done + * | + * failed + * | + * V + * [start tree traversal from node] + * + */ + +#include "forward.h" +#include "debug.h" +#include "dformat.h" +#include "key.h" +#include "coord.h" +#include "plugin/item/static_stat.h" +#include "plugin/item/item.h" +#include "plugin/node/node.h" +#include "plugin/plugin.h" +#include "txnmgr.h" +#include "jnode.h" +#include "znode.h" +#include "block_alloc.h" +#include "tree_walk.h" +#include "carry.h" +#include "carry_ops.h" +#include "tap.h" +#include "tree.h" +#include "log.h" +#include "vfs_ops.h" +#include "page_cache.h" +#include "super.h" +#include "reiser4.h" +#include "inode.h" + +#include /* for struct super_block */ +#include + +/* Disk address (block number) never ever used for any real tree node. This is + used as block number of "uber" znode. + + Invalid block addresses are 0 by tradition. + +*/ +const reiser4_block_nr UBER_TREE_ADDR = 0ull; + +#define CUT_TREE_MIN_ITERATIONS 64 + +/* return node plugin of coord->node */ +reiser4_internal node_plugin * +node_plugin_by_coord(const coord_t * coord) +{ + assert("vs-1", coord != NULL); + assert("vs-2", coord->node != NULL); + + return coord->node->nplug; +} + +/* insert item into tree. Fields of @coord are updated so that they can be + * used by consequent insert operation. */ +reiser4_internal insert_result +insert_by_key(reiser4_tree * tree /* tree to insert new item + * into */ , + const reiser4_key * key /* key of new item */ , + reiser4_item_data * data /* parameters for item + * creation */ , + coord_t * coord /* resulting insertion coord */ , + lock_handle * lh /* resulting lock + * handle */ , + tree_level stop_level /** level where to insert */ , + __u32 flags /* insertion flags */ ) +{ + int result; + + assert("nikita-358", tree != NULL); + assert("nikita-360", coord != NULL); + + result = coord_by_key(tree, key, coord, lh, ZNODE_WRITE_LOCK, + FIND_EXACT, stop_level, stop_level, flags | CBK_FOR_INSERT, 0/*ra_info*/); + switch (result) { + default: + break; + case CBK_COORD_FOUND: + result = IBK_ALREADY_EXISTS; + break; + case CBK_COORD_NOTFOUND: + assert("nikita-2017", coord->node != NULL); + result = insert_by_coord(coord, data, key, lh, 0 /*flags */ ); + break; + } + return result; +} + +/* insert item by calling carry. Helper function called if short-cut + insertion failed */ +static insert_result +insert_with_carry_by_coord(coord_t * coord /* coord where to insert */ , + lock_handle * lh /* lock handle of insertion + * node */ , + reiser4_item_data * data /* parameters of new + * item */ , + const reiser4_key * key /* key of new item */ , + carry_opcode cop /* carry operation to perform */ , + cop_insert_flag flags /* carry flags */) +{ + int result; + carry_pool pool; + carry_level lowest_level; + carry_op *op; + carry_insert_data cdata; + + assert("umka-314", coord != NULL); + + init_carry_pool(&pool); + init_carry_level(&lowest_level, &pool); + + op = post_carry(&lowest_level, cop, coord->node, 0); + if (IS_ERR(op) || (op == NULL)) + return RETERR(op ? PTR_ERR(op) : -EIO); + cdata.coord = coord; + cdata.data = data; + cdata.key = key; + op->u.insert.d = &cdata; + if (flags == 0) + flags = znode_get_tree(coord->node)->carry.insert_flags; + op->u.insert.flags = flags; + op->u.insert.type = COPT_ITEM_DATA; + op->u.insert.child = 0; + if (lh != NULL) { + assert("nikita-3245", lh->node == coord->node); + lowest_level.track_type = CARRY_TRACK_CHANGE; + lowest_level.tracked = lh; + } + + ON_STATS(lowest_level.level_no = znode_get_level(coord->node)); + result = carry(&lowest_level, 0); + done_carry_pool(&pool); + + return result; +} + +/* form carry queue to perform paste of @data with @key at @coord, and launch + its execution by calling carry(). + + Instruct carry to update @lh it after balancing insertion coord moves into + different block. + +*/ +static int +paste_with_carry(coord_t * coord /* coord of paste */ , + lock_handle * lh /* lock handle of node + * where item is + * pasted */ , + reiser4_item_data * data /* parameters of new + * item */ , + const reiser4_key * key /* key of new item */ , + unsigned flags /* paste flags */ ) +{ + int result; + carry_pool pool; + carry_level lowest_level; + carry_op *op; + carry_insert_data cdata; + + assert("umka-315", coord != NULL); + assert("umka-316", key != NULL); + + init_carry_pool(&pool); + init_carry_level(&lowest_level, &pool); + + op = post_carry(&lowest_level, COP_PASTE, coord->node, 0); + if (IS_ERR(op) || (op == NULL)) + return RETERR(op ? PTR_ERR(op) : -EIO); + cdata.coord = coord; + cdata.data = data; + cdata.key = key; + op->u.paste.d = &cdata; + if (flags == 0) + flags = znode_get_tree(coord->node)->carry.paste_flags; + op->u.paste.flags = flags; + op->u.paste.type = COPT_ITEM_DATA; + if (lh != NULL) { + lowest_level.track_type = CARRY_TRACK_CHANGE; + lowest_level.tracked = lh; + } + + ON_STATS(lowest_level.level_no = znode_get_level(coord->node)); + result = carry(&lowest_level, 0); + done_carry_pool(&pool); + + return result; +} + +/* insert item at the given coord. + + First try to skip carry by directly calling ->create_item() method of node + plugin. If this is impossible (there is not enough free space in the node, + or leftmost item in the node is created), call insert_with_carry_by_coord() + that will do full carry(). + +*/ +reiser4_internal insert_result +insert_by_coord(coord_t * coord /* coord where to + * insert. coord->node has + * to be write locked by + * caller */ , + reiser4_item_data * data /* data to be + * inserted */ , + const reiser4_key * key /* key of new item */ , + lock_handle * lh /* lock handle of write + * lock on node */ , + __u32 flags /* insertion flags */ ) +{ + unsigned item_size; + int result; + znode *node; + + assert("vs-247", coord != NULL); + assert("vs-248", data != NULL); + assert("vs-249", data->length >= 0); + assert("nikita-1191", znode_is_write_locked(coord->node)); + + write_tree_log(znode_get_tree(coord->node), tree_insert, key, data, coord, flags); + + node = coord->node; + coord_clear_iplug(coord); + result = zload(node); + if (result != 0) + return result; + + item_size = space_needed(node, NULL, data, 1); + if (item_size > znode_free_space(node) && + (flags & COPI_DONT_SHIFT_LEFT) && (flags & COPI_DONT_SHIFT_RIGHT) && (flags & COPI_DONT_ALLOCATE)) { + /* we are forced to use free space of coord->node and new item + does not fit into it. + + Currently we get here only when we allocate and copy units + of extent item from a node to its left neighbor during + "squalloc"-ing. If @node (this is left neighbor) does not + have enough free space - we do not want to attempt any + shifting and allocations because we are in squeezing and + everything to the left of @node is tightly packed. + */ + result = -E_NODE_FULL; + } else if ((item_size <= znode_free_space(node)) && + !coord_is_before_leftmost(coord) && + (node_plugin_by_node(node)->fast_insert != NULL) && node_plugin_by_node(node)->fast_insert(coord)) { + /* shortcut insertion without carry() overhead. + + Only possible if: + + - there is enough free space + + - insertion is not into the leftmost position in a node + (otherwise it would require updating of delimiting key in a + parent) + + - node plugin agrees with this + + */ + reiser4_stat_inc(tree.fast_insert); + result = node_plugin_by_node(node)->create_item(coord, key, data, NULL); + znode_make_dirty(node); + } else { + /* otherwise do full-fledged carry(). */ + result = insert_with_carry_by_coord(coord, lh, data, key, COP_INSERT, flags); + } + zrelse(node); + return result; +} + +/* @coord is set to leaf level and @data is to be inserted to twig level */ +reiser4_internal insert_result +insert_extent_by_coord(coord_t * coord /* coord where to insert. coord->node * has to be write * locked by caller */ , + reiser4_item_data * data /* data to be inserted */ , + const reiser4_key * key /* key of new item */ , + lock_handle * lh /* lock handle of write lock on * node */) +{ + assert("vs-405", coord != NULL); + assert("vs-406", data != NULL); + assert("vs-407", data->length > 0); + assert("vs-408", znode_is_write_locked(coord->node)); + assert("vs-409", znode_get_level(coord->node) == LEAF_LEVEL); + + return insert_with_carry_by_coord(coord, lh, data, key, COP_EXTENT, 0 /*flags */ ); +} + +/* Insert into the item at the given coord. + + First try to skip carry by directly calling ->paste() method of item + plugin. If this is impossible (there is not enough free space in the node, + or we are pasting into leftmost position in the node), call + paste_with_carry() that will do full carry(). + +*/ +/* paste_into_item */ +reiser4_internal int +insert_into_item(coord_t * coord /* coord of pasting */ , + lock_handle * lh /* lock handle on node involved */ , + const reiser4_key * key /* key of unit being pasted */ , + reiser4_item_data * data /* parameters for new unit */ , + unsigned flags /* insert/paste flags */ ) +{ + int result; + int size_change; + node_plugin *nplug; + item_plugin *iplug; + + assert("umka-317", coord != NULL); + assert("umka-318", key != NULL); + + iplug = item_plugin_by_coord(coord); + nplug = node_plugin_by_coord(coord); + + assert("nikita-1480", iplug == data->iplug); + + write_tree_log(znode_get_tree(coord->node), tree_paste, key, data, coord, flags); + + size_change = space_needed(coord->node, coord, data, 0); + if (size_change > (int) znode_free_space(coord->node) && + (flags & COPI_DONT_SHIFT_LEFT) && (flags & COPI_DONT_SHIFT_RIGHT) && (flags & COPI_DONT_ALLOCATE)) { + /* we are forced to use free space of coord->node and new data + does not fit into it. */ + return -E_NODE_FULL; + } + + /* shortcut paste without carry() overhead. + + Only possible if: + + - there is enough free space + + - paste is not into the leftmost unit in a node (otherwise + it would require updating of delimiting key in a parent) + + - node plugin agrees with this + + - item plugin agrees with us + */ + if (size_change <= (int) znode_free_space(coord->node) && + (coord->item_pos != 0 || + coord->unit_pos != 0 || coord->between == AFTER_UNIT) && + coord->unit_pos != 0 && nplug->fast_paste != NULL && + nplug->fast_paste(coord) && + iplug->b.fast_paste != NULL && iplug->b.fast_paste(coord)) { + reiser4_stat_inc(tree.fast_paste); + if (size_change > 0) + nplug->change_item_size(coord, size_change); + /* NOTE-NIKITA: huh? where @key is used? */ + result = iplug->b.paste(coord, data, NULL); + if (size_change < 0) + nplug->change_item_size(coord, size_change); + znode_make_dirty(coord->node); + } else + /* otherwise do full-fledged carry(). */ + result = paste_with_carry(coord, lh, data, key, flags); + return result; +} + +/* this either appends or truncates item @coord */ +reiser4_internal int +resize_item(coord_t * coord /* coord of item being resized */ , + reiser4_item_data * data /* parameters of resize */ , + reiser4_key * key /* key of new unit */ , + lock_handle * lh /* lock handle of node + * being modified */ , + cop_insert_flag flags /* carry flags */ ) +{ + int result; + carry_pool pool; + carry_level lowest_level; + znode *node; + + assert("nikita-362", coord != NULL); + assert("nikita-363", data != NULL); + assert("vs-245", data->length != 0); + + node = coord->node; + coord_clear_iplug(coord); + result = zload(node); + if (result != 0) + return result; + + init_carry_pool(&pool); + init_carry_level(&lowest_level, &pool); + + if (data->length < 0) + result = node_plugin_by_coord(coord)->shrink_item(coord, + data->length); + else + result = insert_into_item(coord, lh, key, data, flags); + + zrelse(node); + return result; +} + +/* insert flow @f */ +reiser4_internal int +insert_flow(coord_t * coord, lock_handle * lh, flow_t * f) +{ + int result; + carry_pool pool; + carry_level lowest_level; + carry_op *op; + reiser4_item_data data; + + init_carry_pool(&pool); + init_carry_level(&lowest_level, &pool); + + op = post_carry(&lowest_level, COP_INSERT_FLOW, coord->node, 0 /* operate directly on coord -> node */ ); + if (IS_ERR(op) || (op == NULL)) + return RETERR(op ? PTR_ERR(op) : -EIO); + + /* these are permanent during insert_flow */ + data.user = 1; + data.iplug = item_plugin_by_id(FORMATTING_ID); + data.arg = 0; + /* data.length and data.data will be set before calling paste or + insert */ + data.length = 0; + data.data = 0; + + op->u.insert_flow.flags = 0; + op->u.insert_flow.insert_point = coord; + op->u.insert_flow.flow = f; + op->u.insert_flow.data = &data; + op->u.insert_flow.new_nodes = 0; + + lowest_level.track_type = CARRY_TRACK_CHANGE; + lowest_level.tracked = lh; + + ON_STATS(lowest_level.level_no = znode_get_level(coord->node)); + result = carry(&lowest_level, 0); + done_carry_pool(&pool); + + return result; +} + +/* Given a coord in parent node, obtain a znode for the corresponding child */ +reiser4_internal znode * +child_znode(const coord_t * parent_coord /* coord of pointer to + * child */ , + znode * parent /* parent of child */ , + int incore_p /* if !0 only return child if already in + * memory */ , + int setup_dkeys_p /* if !0 update delimiting keys of + * child */ ) +{ + znode *child; + + assert("nikita-1374", parent_coord != NULL); + assert("nikita-1482", parent != NULL); + assert("nikita-1384", ergo(setup_dkeys_p, + rw_dk_is_not_locked(znode_get_tree(parent)))); + assert("nikita-2947", znode_is_any_locked(parent)); + + if (znode_get_level(parent) <= LEAF_LEVEL) { + /* trying to get child of leaf node */ + warning("nikita-1217", "Child of maize?"); + print_znode("node", parent); + return ERR_PTR(RETERR(-EIO)); + } + if (item_is_internal(parent_coord)) { + reiser4_block_nr addr; + item_plugin *iplug; + reiser4_tree *tree; + + iplug = item_plugin_by_coord(parent_coord); + assert("vs-512", iplug->s.internal.down_link); + iplug->s.internal.down_link(parent_coord, NULL, &addr); + + tree = znode_get_tree(parent); + if (incore_p) + child = zlook(tree, &addr); + else + child = zget(tree, &addr, parent, znode_get_level(parent) - 1, GFP_KERNEL); + if ((child != NULL) && !IS_ERR(child) && setup_dkeys_p) + set_child_delimiting_keys(parent, parent_coord, child); + } else { + warning("nikita-1483", "Internal item expected"); + print_znode("node", parent); + child = ERR_PTR(RETERR(-EIO)); + } + return child; +} + +/* remove znode from transaction */ +static void uncapture_znode (znode * node) +{ + struct page * page; + + assert ("zam-1001", ZF_ISSET(node, JNODE_HEARD_BANSHEE)); + + /* Get e-flush block allocation back before deallocating node's + * block number. */ + spin_lock_znode(node); + if (ZF_ISSET(node, JNODE_EFLUSH)) + eflush_del(ZJNODE(node), 0); + spin_unlock_znode(node); + + if (!blocknr_is_fake(znode_get_block(node))) { + int ret; + + /* An already allocated block goes right to the atom's delete set. */ + ret = reiser4_dealloc_block( + znode_get_block(node), 0, BA_DEFER | BA_FORMATTED); + if (ret) + warning("zam-942", "can\'t add a block (%llu) number to atom's delete set\n", + (unsigned long long)(*znode_get_block(node))); + + spin_lock_znode(node); + /* Here we return flush reserved block which was reserved at the + * moment when this allocated node was marked dirty and still + * not used by flush in node relocation procedure. */ + if (ZF_ISSET(node, JNODE_FLUSH_RESERVED)) { + txn_atom * atom ; + + atom = jnode_get_atom(ZJNODE(node)); + assert("zam-939", atom != NULL); + spin_unlock_znode(node); + flush_reserved2grabbed(atom, (__u64)1); + UNLOCK_ATOM(atom); + } else + spin_unlock_znode(node); + } else { + /* znode has assigned block which is counted as "fake + allocated". Return it back to "free blocks") */ + fake_allocated2free((__u64) 1, BA_FORMATTED); + } + + /* + * uncapture page from transaction. There is a possibility of a race + * with ->releasepage(): reiser4_releasepage() detaches page from this + * jnode and we have nothing to uncapture. To avoid this, get + * reference of node->pg under jnode spin lock. uncapture_page() will + * deal with released page itself. + */ + spin_lock_znode(node); + page = znode_page(node); + if (likely(page != NULL)) { + /* + * uncapture_page() can only be called when we are sure that + * znode is pinned in memory, which we are, because + * forget_znode() is only called from longterm_unlock_znode(). + */ + page_cache_get(page); + spin_unlock_znode(node); + lock_page(page); + uncapture_page(page); + unlock_page(page); + page_cache_release(page); + } else { + txn_atom * atom; + + /* handle "flush queued" znodes */ + while (1) { + atom = jnode_get_atom(ZJNODE(node)); + assert("zam-943", atom != NULL); + + if (!ZF_ISSET(node, JNODE_FLUSH_QUEUED) || !atom->nr_running_queues) + break; + + spin_unlock_znode(node); + atom_wait_event(atom); + spin_lock_znode(node); + } + + uncapture_block(ZJNODE(node)); + UNLOCK_ATOM(atom); + zput(node); + } +} + +/* This is called from longterm_unlock_znode() when last lock is released from + the node that has been removed from the tree. At this point node is removed + from sibling list and its lock is invalidated. */ +reiser4_internal void +forget_znode(lock_handle * handle) +{ + znode *node; + reiser4_tree *tree; + + assert("umka-319", handle != NULL); + + node = handle->node; + tree = znode_get_tree(node); + + assert("vs-164", znode_is_write_locked(node)); + assert("nikita-1280", ZF_ISSET(node, JNODE_HEARD_BANSHEE)); + assert("nikita-3337", rw_zlock_is_locked(&node->lock)); + + /* We assume that this node was detached from its parent before + * unlocking, it gives no way to reach this node from parent through a + * down link. The node should have no children and, thereby, can't be + * reached from them by their parent pointers. The only way to obtain a + * reference to the node is to use sibling pointers from its left and + * right neighbors. In the next several lines we remove the node from + * the sibling list. */ + + WLOCK_TREE(tree); + sibling_list_remove(node); + znode_remove(node, tree); + WUNLOCK_TREE(tree); + + /* Here we set JNODE_DYING and cancel all pending lock requests. It + * forces all lock requestor threads to repeat iterations of getting + * lock on a child, neighbor or parent node. But, those threads can't + * come to this node again, because this node is no longer a child, + * neighbor or parent of any other node. This order of znode + * invalidation does not allow other threads to waste cpu time is a busy + * loop, trying to lock dying object. The exception is in the flush + * code when we take node directly from atom's capture list.*/ + + write_unlock_zlock(&node->lock); + /* and, remove from atom's capture list. */ + uncapture_znode(node); + write_lock_zlock(&node->lock); + + invalidate_lock(handle); +} + +/* Check that internal item at @pointer really contains pointer to @child. */ +reiser4_internal int +check_tree_pointer(const coord_t * pointer /* would-be pointer to + * @child */ , + const znode * child /* child znode */ ) +{ + assert("nikita-1016", pointer != NULL); + assert("nikita-1017", child != NULL); + assert("nikita-1018", pointer->node != NULL); + + assert("nikita-1325", znode_is_any_locked(pointer->node)); + + assert("nikita-2985", + znode_get_level(pointer->node) == znode_get_level(child) + 1); + + coord_clear_iplug((coord_t *) pointer); + + if (coord_is_existing_unit(pointer)) { + item_plugin *iplug; + reiser4_block_nr addr; + + if (item_is_internal(pointer)) { + iplug = item_plugin_by_coord(pointer); + assert("vs-513", iplug->s.internal.down_link); + iplug->s.internal.down_link(pointer, NULL, &addr); + /* check that cached value is correct */ + if (disk_addr_eq(&addr, znode_get_block(child))) { + reiser4_stat_inc(tree.pos_in_parent_hit); + return NS_FOUND; + } + } + } + /* warning ("jmacd-1002", "tree pointer incorrect"); */ + return NS_NOT_FOUND; +} + +/* find coord of pointer to new @child in @parent. + + Find the &coord_t in the @parent where pointer to a given @child will + be in. + +*/ +reiser4_internal int +find_new_child_ptr(znode * parent /* parent znode, passed locked */ , + znode * child UNUSED_ARG /* child znode, passed locked */ , + znode * left /* left brother of new node */ , + coord_t * result /* where result is stored in */ ) +{ + int ret; + + assert("nikita-1486", parent != NULL); + assert("nikita-1487", child != NULL); + assert("nikita-1488", result != NULL); + + ret = find_child_ptr(parent, left, result); + if (ret != NS_FOUND) { + warning("nikita-1489", "Cannot find brother position: %i", ret); + return RETERR(-EIO); + } else { + result->between = AFTER_UNIT; + return RETERR(NS_NOT_FOUND); + } +} + +/* find coord of pointer to @child in @parent. + + Find the &coord_t in the @parent where pointer to a given @child is in. + +*/ +reiser4_internal int +find_child_ptr(znode * parent /* parent znode, passed locked */ , + znode * child /* child znode, passed locked */ , + coord_t * result /* where result is stored in */ ) +{ + int lookup_res; + node_plugin *nplug; + /* left delimiting key of a child */ + reiser4_key ld; + reiser4_tree *tree; + + assert("nikita-934", parent != NULL); + assert("nikita-935", child != NULL); + assert("nikita-936", result != NULL); + assert("zam-356", znode_is_loaded(parent)); + + coord_init_zero(result); + result->node = parent; + + nplug = parent->nplug; + assert("nikita-939", nplug != NULL); + + tree = znode_get_tree(parent); + /* NOTE-NIKITA taking read-lock on tree here assumes that @result is + * not aliased to ->in_parent of some znode. Otherwise, + * parent_coord_to_coord() below would modify data protected by tree + * lock. */ + RLOCK_TREE(tree); + /* fast path. Try to use cached value. Lock tree to keep + node->pos_in_parent and pos->*_blocknr consistent. */ + if (child->in_parent.item_pos + 1 != 0) { + reiser4_stat_inc(tree.pos_in_parent_set); + parent_coord_to_coord(&child->in_parent, result); + if (check_tree_pointer(result, child) == NS_FOUND) { + RUNLOCK_TREE(tree); + return NS_FOUND; + } + + reiser4_stat_inc(tree.pos_in_parent_miss); + child->in_parent.item_pos = (unsigned short)~0; + } + RUNLOCK_TREE(tree); + + /* is above failed, find some key from @child. We are looking for the + least key in a child. */ + UNDER_RW_VOID(dk, tree, read, ld = *znode_get_ld_key(child)); + /* + * now, lookup parent with key just found. Note, that left delimiting + * key doesn't identify node uniquely, because (in extremely rare + * case) two nodes can have equal left delimiting keys, if one of them + * is completely filled with directory entries that all happened to be + * hash collision. But, we check block number in check_tree_pointer() + * and, so, are safe. + */ + lookup_res = nplug->lookup(parent, &ld, FIND_EXACT, result); + /* update cached pos_in_node */ + if (lookup_res == NS_FOUND) { + WLOCK_TREE(tree); + coord_to_parent_coord(result, &child->in_parent); + WUNLOCK_TREE(tree); + lookup_res = check_tree_pointer(result, child); + } + if (lookup_res == NS_NOT_FOUND) + lookup_res = find_child_by_addr(parent, child, result); + return lookup_res; +} + +/* find coord of pointer to @child in @parent by scanning + + Find the &coord_t in the @parent where pointer to a given @child + is in by scanning all internal items in @parent and comparing block + numbers in them with that of @child. + +*/ +reiser4_internal int +find_child_by_addr(znode * parent /* parent znode, passed locked */ , + znode * child /* child znode, passed locked */ , + coord_t * result /* where result is stored in */ ) +{ + int ret; + + assert("nikita-1320", parent != NULL); + assert("nikita-1321", child != NULL); + assert("nikita-1322", result != NULL); + + ret = NS_NOT_FOUND; + + for_all_units(result, parent) { + if (check_tree_pointer(result, child) == NS_FOUND) { + UNDER_RW_VOID(tree, znode_get_tree(parent), write, + coord_to_parent_coord(result, + &child->in_parent)); + ret = NS_FOUND; + break; + } + } + return ret; +} + +/* true, if @addr is "unallocated block number", which is just address, with + highest bit set. */ +reiser4_internal int +is_disk_addr_unallocated(const reiser4_block_nr * addr /* address to + * check */ ) +{ + assert("nikita-1766", addr != NULL); + cassert(sizeof (reiser4_block_nr) == 8); + return (*addr & REISER4_BLOCKNR_STATUS_BIT_MASK) == REISER4_UNALLOCATED_STATUS_VALUE; +} + +/* convert unallocated disk address to the memory address + + FIXME: This needs a big comment. */ +reiser4_internal void * +unallocated_disk_addr_to_ptr(const reiser4_block_nr * addr /* address to + * convert */ ) +{ + assert("nikita-1688", addr != NULL); + assert("nikita-1689", is_disk_addr_unallocated(addr)); + return (void *) (long) (*addr << 1); +} + +/* returns true if removing bytes of given range of key [from_key, to_key] + causes removing of whole item @from */ +static int +item_removed_completely(coord_t * from, const reiser4_key * from_key, const reiser4_key * to_key) +{ + item_plugin *iplug; + reiser4_key key_in_item; + + assert("umka-325", from != NULL); + assert("", item_is_extent(from)); + + /* check first key just for case */ + item_key_by_coord(from, &key_in_item); + if (keygt(from_key, &key_in_item)) + return 0; + + /* check last key */ + iplug = item_plugin_by_coord(from); + assert("vs-611", iplug && iplug->s.file.append_key); + + iplug->s.file.append_key(from, &key_in_item); + set_key_offset(&key_in_item, get_key_offset(&key_in_item) - 1); + + if (keylt(to_key, &key_in_item)) + /* last byte is not removed */ + return 0; + return 1; +} + +/* helper function for prepare_twig_kill(): @left and @right are formatted + * neighbors of extent item being completely removed. Load and lock neighbors + * and store lock handles into @cdata for later use by kill_hook_extent() */ +static int +prepare_children(znode *left, znode *right, carry_kill_data *kdata) +{ + int result; + int left_loaded; + int right_loaded; + + result = 0; + left_loaded = right_loaded = 0; + + if (left != NULL) { + result = zload(left); + if (result == 0) { + left_loaded = 1; + result = longterm_lock_znode(kdata->left, left, + ZNODE_READ_LOCK, + ZNODE_LOCK_LOPRI); + } + } + if (result == 0 && right != NULL) { + result = zload(right); + if (result == 0) { + right_loaded = 1; + result = longterm_lock_znode(kdata->right, right, + ZNODE_READ_LOCK, + ZNODE_LOCK_HIPRI | ZNODE_LOCK_NONBLOCK); + } + } + if (result != 0) { + done_lh(kdata->left); + done_lh(kdata->right); + if (left_loaded != 0) + zrelse(left); + if (right_loaded != 0) + zrelse(right); + } + return result; +} + +static void +done_children(carry_kill_data *kdata) +{ + if (kdata->left != NULL && kdata->left->node != NULL) { + zrelse(kdata->left->node); + done_lh(kdata->left); + } + if (kdata->right != NULL && kdata->right->node != NULL) { + zrelse(kdata->right->node); + done_lh(kdata->right); + } +} + +/* part of cut_node. It is called when cut_node is called to remove or cut part + of extent item. When head of that item is removed - we have to update right + delimiting of left neighbor of extent. When item is removed completely - we + have to set sibling link between left and right neighbor of removed + extent. This may return -E_DEADLOCK because of trying to get left neighbor + locked. So, caller should repeat an attempt +*/ +/* Audited by: umka (2002.06.16) */ +static int +prepare_twig_kill(carry_kill_data *kdata, znode * locked_left_neighbor) +{ + int result; + reiser4_key key; + lock_handle left_lh; + lock_handle right_lh; + coord_t left_coord; + coord_t *from; + znode *left_child; + znode *right_child; + reiser4_tree *tree; + int left_zloaded_here, right_zloaded_here; + + from = kdata->params.from; + assert("umka-326", from != NULL); + assert("umka-327", kdata->params.to != NULL); + + /* for one extent item only yet */ + assert("vs-591", item_is_extent(from)); + assert ("vs-592", from->item_pos == kdata->params.to->item_pos); + + if ((kdata->params.from_key && keygt(kdata->params.from_key, item_key_by_coord(from, &key))) || + from->unit_pos != 0) { + /* head of item @from is not removed, there is nothing to + worry about */ + return 0; + } + + result = 0; + left_zloaded_here = 0; + right_zloaded_here = 0; + + left_child = right_child = NULL; + + coord_dup(&left_coord, from); + init_lh(&left_lh); + init_lh(&right_lh); + if (coord_prev_unit(&left_coord)) { + /* @from is leftmost item in its node */ + if (!locked_left_neighbor) { + result = reiser4_get_left_neighbor(&left_lh, from->node, ZNODE_READ_LOCK, GN_CAN_USE_UPPER_LEVELS); + switch (result) { + case 0: + break; + case -E_NO_NEIGHBOR: + /* there is no formatted node to the left of + from->node */ + warning("vs-605", + "extent item has smallest key in " "the tree and it is about to be removed"); + return 0; + case -E_DEADLOCK: + /* need to restart */ + default: + return result; + } + + /* we have acquired left neighbor of from->node */ + result = zload(left_lh.node); + if (result) + goto done; + + locked_left_neighbor = left_lh.node; + } else { + /* squalloc_right_twig_cut should have supplied locked + * left neighbor */ + assert("vs-834", znode_is_write_locked(locked_left_neighbor)); + result = zload(locked_left_neighbor); + if (result) + return result; + } + + left_zloaded_here = 1; + coord_init_last_unit(&left_coord, locked_left_neighbor); + } + + if (!item_is_internal(&left_coord)) { + /* what else but extent can be on twig level */ + assert("vs-606", item_is_extent(&left_coord)); + + /* there is no left formatted child */ + if (left_zloaded_here) + zrelse(locked_left_neighbor); + done_lh(&left_lh); + return 0; + } + + tree = znode_get_tree(left_coord.node); + left_child = child_znode(&left_coord, left_coord.node, 1, 0); + + if (IS_ERR(left_child)) { + result = PTR_ERR(left_child); + goto done; + } + + /* left child is acquired, calculate new right delimiting key for it + and get right child if it is necessary */ + if (item_removed_completely(from, kdata->params.from_key, kdata->params.to_key)) { + /* try to get right child of removed item */ + coord_t right_coord; + + assert("vs-607", kdata->params.to->unit_pos == coord_last_unit_pos(kdata->params.to)); + coord_dup(&right_coord, kdata->params.to); + if (coord_next_unit(&right_coord)) { + /* @to is rightmost unit in the node */ + result = reiser4_get_right_neighbor(&right_lh, from->node, ZNODE_READ_LOCK, GN_CAN_USE_UPPER_LEVELS); + switch (result) { + case 0: + result = zload(right_lh.node); + if (result) + goto done; + + right_zloaded_here = 1; + coord_init_first_unit(&right_coord, right_lh.node); + item_key_by_coord(&right_coord, &key); + break; + + case -E_NO_NEIGHBOR: + /* there is no formatted node to the right of + from->node */ + UNDER_RW_VOID(dk, tree, read, + key = *znode_get_rd_key(from->node)); + right_coord.node = 0; + result = 0; + break; + default: + /* real error */ + goto done; + } + } else { + /* there is an item to the right of @from - take its key */ + item_key_by_coord(&right_coord, &key); + } + + /* try to get right child of @from */ + if (right_coord.node && /* there is right neighbor of @from */ + item_is_internal(&right_coord)) { /* it is internal item */ + right_child = child_znode(&right_coord, + right_coord.node, 1, 0); + + if (IS_ERR(right_child)) { + result = PTR_ERR(right_child); + goto done; + } + + } + /* whole extent is removed between znodes left_child and right_child. Prepare them for linking and + update of right delimiting key of left_child */ + result = prepare_children(left_child, right_child, kdata); + } else { + /* head of item @to is removed. left_child has to get right delimting key update. Prepare it for that */ + result = prepare_children(left_child, NULL, kdata); + } + + done: + if (right_child) + zput(right_child); + if (right_zloaded_here) + zrelse(right_lh.node); + done_lh(&right_lh); + + if (left_child) + zput(left_child); + if (left_zloaded_here) + zrelse(locked_left_neighbor); + done_lh(&left_lh); + return result; +} + +/* this is used to remove part of node content between coordinates @from and @to. Units to which @from and @to are set + are to be cut completely */ +/* for try_to_merge_with_left, delete_copied, delete_node */ +reiser4_internal int +cut_node_content(coord_t *from, coord_t *to, + const reiser4_key * from_key /* first key to be removed */ , + const reiser4_key * to_key /* last key to be removed */ , + reiser4_key * smallest_removed /* smallest key actually removed */) +{ + carry_pool pool; + carry_level lowest_level; + carry_op *op; + carry_cut_data cut_data; + int result; + + assert("", coord_compare(from, to) != COORD_CMP_ON_RIGHT); + + init_carry_pool(&pool); + init_carry_level(&lowest_level, &pool); + + op = post_carry(&lowest_level, COP_CUT, from->node, 0); + assert("vs-1509", op != 0); + if (IS_ERR(op)) + return PTR_ERR(op); + + cut_data.params.from = from; + cut_data.params.to = to; + cut_data.params.from_key = from_key; + cut_data.params.to_key = to_key; + cut_data.params.smallest_removed = smallest_removed; + + op->u.cut_or_kill.is_cut = 1; + op->u.cut_or_kill.u.cut = &cut_data; + + ON_STATS(lowest_level.level_no = znode_get_level(from->node)); + result = carry(&lowest_level, 0); + done_carry_pool(&pool); + + return result; +} + +/* cut part of the node + + Cut part or whole content of node. + + cut data between @from and @to of @from->node and call carry() to make + corresponding changes in the tree. @from->node may become empty. If so - + pointer to it will be removed. Neighboring nodes are not changed. Smallest + removed key is stored in @smallest_removed + +*/ +reiser4_internal int +kill_node_content(coord_t * from /* coord of the first unit/item that will be + * eliminated */ , + coord_t * to /* coord of the last unit/item that will be + * eliminated */ , + const reiser4_key * from_key /* first key to be removed */ , + const reiser4_key * to_key /* last key to be removed */ , + reiser4_key * smallest_removed /* smallest key actually + * removed */ , + znode * locked_left_neighbor, /* this is set when kill_node_content is called with left neighbor + * locked (in squalloc_right_twig_cut, namely) */ + struct inode *inode /* inode of file whose item (or its part) is to be killed. This is necessary to + invalidate pages together with item pointing to them */) +{ + int result; + carry_pool pool; + carry_level lowest_level; + carry_op *op; + carry_kill_data kdata; + lock_handle left_child; + lock_handle right_child; + + assert("umka-328", from != NULL); + assert("vs-316", !node_is_empty(from->node)); + assert("nikita-1812", coord_is_existing_unit(from) && coord_is_existing_unit(to)); + + init_lh(&left_child); + init_lh(&right_child); + + kdata.params.from = from; + kdata.params.to = to; + kdata.params.from_key = from_key; + kdata.params.to_key = to_key; + kdata.params.smallest_removed = smallest_removed; + kdata.flags = 0; + kdata.inode = inode; + kdata.left = &left_child; + kdata.right = &right_child; + + if (znode_get_level(from->node) == TWIG_LEVEL && item_is_extent(from)) { + /* left child of extent item may have to get updated right + delimiting key and to get linked with right child of extent + @from if it will be removed completely */ + result = prepare_twig_kill(&kdata, locked_left_neighbor); + if (result) { + done_children(&kdata); + return result; + } + } + + init_carry_pool(&pool); + init_carry_level(&lowest_level, &pool); + + op = post_carry(&lowest_level, COP_CUT, from->node, 0); + if (IS_ERR(op) || (op == NULL)) { + done_children(&kdata); + return RETERR(op ? PTR_ERR(op) : -EIO); + } + + op->u.cut_or_kill.is_cut = 0; + op->u.cut_or_kill.u.kill = &kdata; + + ON_STATS(lowest_level.level_no = znode_get_level(from->node)); + result = carry(&lowest_level, 0); + done_carry_pool(&pool); + + done_children(&kdata); + return result; +} + +void +fake_kill_hook_tail(struct inode *inode, loff_t start, loff_t end) +{ + if (inode_get_flag(inode, REISER4_HAS_MMAP)) { + pgoff_t start_pg, end_pg; + + start_pg = start >> PAGE_CACHE_SHIFT; + end_pg = (end - 1) >> PAGE_CACHE_SHIFT; + + if ((start & (PAGE_CACHE_SIZE - 1)) == 0) { + /* + * kill up to the page boundary. + */ + assert("vs-123456", start_pg == end_pg); + reiser4_invalidate_pages(inode->i_mapping, start_pg, 1); + } else if (start_pg != end_pg) { + /* + * page boundary is within killed portion of node. + */ + assert("vs-654321", end_pg - start_pg == 1); + reiser4_invalidate_pages(inode->i_mapping, end_pg, end_pg - start_pg); + } + } + inode_sub_bytes(inode, end - start); +} + +/** + * Delete whole @node from the reiser4 tree without loading it. + * + * @left: locked left neighbor, + * @node: node to be deleted, + * @smallest_removed: leftmost key of deleted node, + * @object: inode pointer, if we truncate a file body. + * + * @return: 0 if success, error code otherwise. + * + * NOTE: if @object!=NULL we assume that @smallest_removed != NULL and it + * contains the right value of the smallest removed key from the previous + * cut_worker() iteration. This is needed for proper accounting of + * "i_blocks" and "i_bytes" fields of the @object. + */ +reiser4_internal int delete_node (znode * node, reiser4_key * smallest_removed, + struct inode * object) +{ + lock_handle parent_lock; + coord_t cut_from; + coord_t cut_to; + reiser4_tree * tree; + int ret; + + assert ("zam-937", node != NULL); + assert ("zam-933", znode_is_write_locked(node)); + assert ("zam-999", smallest_removed != NULL); + + init_lh(&parent_lock); + + ret = reiser4_get_parent(&parent_lock, node, ZNODE_WRITE_LOCK, 0); + if (ret) + return ret; + + assert("zam-934", !znode_above_root(parent_lock.node)); + + ret = zload(parent_lock.node); + if (ret) + goto failed_nozrelse; + + ret = find_child_ptr(parent_lock.node, node, &cut_from); + if (ret) + goto failed; + + /* decrement child counter and set parent pointer to NULL before + deleting the list from parent node because of checks in + internal_kill_item_hook (we can delete the last item from the parent + node, the parent node is going to be deleted and its c_count should + be zero). */ + + tree = znode_get_tree(node); + WLOCK_TREE(tree); + init_parent_coord(&node->in_parent, NULL); + -- parent_lock.node->c_count; + WUNLOCK_TREE(tree); + + assert("zam-989", item_is_internal(&cut_from)); + + /* @node should be deleted after unlocking. */ + ZF_SET(node, JNODE_HEARD_BANSHEE); + + /* remove a pointer from the parent node to the node being deleted. */ + coord_dup(&cut_to, &cut_from); + /* FIXME: shouldn't this be kill_node_content */ + ret = cut_node_content(&cut_from, &cut_to, NULL, NULL, NULL); + if (ret) + /* FIXME(Zam): Should we re-connect the node to its parent if + * cut_node fails? */ + goto failed; + + { + reiser4_tree * tree = current_tree; + __u64 start_offset = 0, end_offset = 0; + + WLOCK_DK(tree); + if (object) { + /* We use @smallest_removed and the left delimiting of + * the current node for @object->i_blocks, i_bytes + * calculation. We assume that the items after the + * *@smallest_removed key have been deleted from the + * file body. */ + start_offset = get_key_offset(znode_get_ld_key(node)); + end_offset = get_key_offset(smallest_removed); + } + + RLOCK_TREE(tree); + assert("zam-1021", znode_is_connected(node)); + if (node->left) + znode_set_rd_key(node->left, znode_get_rd_key(node)); + RUNLOCK_TREE(tree); + + *smallest_removed = *znode_get_ld_key(node); + + WUNLOCK_DK(tree); + + if (object) { + /* we used to perform actions which are to be performed on items on their removal from tree in + special item method - kill_hook. Here for optimization reasons we avoid reading node + containing item we remove and can not call item's kill hook. Instead we call function which + does exactly the same things as tail kill hook in assumption that node we avoid reading + contains only one item and that item is a tail one. */ + fake_kill_hook_tail(object, start_offset, end_offset); + } + } + failed: + zrelse(parent_lock.node); + failed_nozrelse: + done_lh(&parent_lock); + + return ret; +} + +/** + * The cut_tree subroutine which does progressive deletion of items and whole + * nodes from right to left (which is not optimal but implementation seems to + * be easier). + * + * @tap: the point deletion process begins from, + * @from_key: the beginning of the deleted key range, + * @to_key: the end of the deleted key range, + * @smallest_removed: the smallest removed key, + * + * @return: 0 if success, error code otherwise, -E_REPEAT means that long cut_tree + * operation was interrupted for allowing atom commit . + */ +static int cut_tree_worker (tap_t * tap, const reiser4_key * from_key, + const reiser4_key * to_key, reiser4_key * smallest_removed, + struct inode * object, + int lazy) +{ + lock_handle next_node_lock; + coord_t left_coord; + int result; + long iterations = 0; + + assert("zam-931", tap->coord->node != NULL); + assert("zam-932", znode_is_write_locked(tap->coord->node)); + + init_lh(&next_node_lock); + + while (1) { + znode *node; /* node from which items are cut */ + node_plugin *nplug; /* node plugin for @node */ + + node = tap->coord->node; + + /* Move next_node_lock to the next node on the left. */ + result = reiser4_get_left_neighbor( + &next_node_lock, node, ZNODE_WRITE_LOCK, GN_CAN_USE_UPPER_LEVELS); + if (result != 0 && result != -E_NO_NEIGHBOR) + break; + /* Check can we delete the node as a whole. */ + if (lazy && iterations && znode_get_level(node) == LEAF_LEVEL && + UNDER_RW(dk, current_tree, read, keyle(from_key, znode_get_ld_key(node)))) + { + result = delete_node(node, smallest_removed, object); + } else { + result = tap_load(tap); + if (result) + return result; + + /* Prepare the second (right) point for cut_node() */ + if (iterations) + coord_init_last_unit(tap->coord, node); + + else if (item_plugin_by_coord(tap->coord)->b.lookup == NULL) + /* set rightmost unit for the items without lookup method */ + tap->coord->unit_pos = coord_last_unit_pos(tap->coord); + + nplug = node->nplug; + + assert("vs-686", nplug); + assert("vs-687", nplug->lookup); + + /* left_coord is leftmost unit cut from @node */ + result = nplug->lookup(node, from_key, + FIND_MAX_NOT_MORE_THAN, &left_coord); + + if (IS_CBKERR(result)) + break; + + /* adjust coordinates so that they are set to existing units */ + if (coord_set_to_right(&left_coord) || coord_set_to_left(tap->coord)) { + result = 0; + break; + } + + if (coord_compare(&left_coord, tap->coord) == COORD_CMP_ON_RIGHT) { + /* keys from @from_key to @to_key are not in the tree */ + result = 0; + break; + } + + if (left_coord.item_pos != tap->coord->item_pos) { + /* do not allow to cut more than one item. It is added to solve problem of truncating + partially converted files. If file is partially converted there may exist a twig node + containing both internal item or items pointing to leaf nodes with formatting items + and extent item. We do not want to kill internal items being at twig node here + because cut_tree_worker assumes killing them from level level */ + coord_dup(&left_coord, tap->coord); + assert("vs-1652", coord_is_existing_unit(&left_coord)); + left_coord.unit_pos = 0; + } + + /* cut data from one node */ + *smallest_removed = *min_key(); + result = kill_node_content(&left_coord, + tap->coord, + from_key, + to_key, + smallest_removed, + next_node_lock.node, + object); + tap_relse(tap); + } + if (result) + break; + + /* Check whether all items with keys >= from_key were removed + * from the tree. */ + if (keyle(smallest_removed, from_key)) + /* result = 0;*/ + break; + + if (next_node_lock.node == NULL) + break; + + result = tap_move(tap, &next_node_lock); + done_lh(&next_node_lock); + if (result) + break; + + /* Break long cut_tree operation (deletion of a large file) if + * atom requires commit. */ + if (iterations > CUT_TREE_MIN_ITERATIONS + && current_atom_should_commit()) + { + result = -E_REPEAT; + break; + } + + + ++ iterations; + } + done_lh(&next_node_lock); + // assert("vs-301", !keyeq(&smallest_removed, min_key())); + return result; +} + + +/* there is a fundamental problem with optimizing deletes: VFS does it + one file at a time. Another problem is that if an item can be + anything, then deleting items must be done one at a time. It just + seems clean to writes this to specify a from and a to key, and cut + everything between them though. */ + +/* use this function with care if deleting more than what is part of a single file. */ +/* do not use this when cutting a single item, it is suboptimal for that */ + +/* You are encouraged to write plugin specific versions of this. It + cannot be optimal for all plugins because it works item at a time, + and some plugins could sometimes work node at a time. Regular files + however are not optimizable to work node at a time because of + extents needing to free the blocks they point to. + + Optimizations compared to v3 code: + + It does not balance (that task is left to memory pressure code). + + Nodes are deleted only if empty. + + Uses extents. + + Performs read-ahead of formatted nodes whose contents are part of + the deletion. +*/ + + +/** + * Delete everything from the reiser4 tree between two keys: @from_key and + * @to_key. + * + * @from_key: the beginning of the deleted key range, + * @to_key: the end of the deleted key range, + * @smallest_removed: the smallest removed key, + * @object: owner of cutting items. + * + * @return: 0 if success, error code otherwise, -E_REPEAT means that long cut_tree + * operation was interrupted for allowing atom commit . + * + * FIXME(Zam): the cut_tree interruption is not implemented. + */ + +reiser4_internal int +cut_tree_object(reiser4_tree * tree, const reiser4_key * from_key, + const reiser4_key * to_key, reiser4_key * smallest_removed_p, + struct inode * object, int lazy) +{ + lock_handle lock; + int result; + tap_t tap; + coord_t right_coord; + reiser4_key smallest_removed; + STORE_COUNTERS; + + assert("umka-329", tree != NULL); + assert("umka-330", from_key != NULL); + assert("umka-331", to_key != NULL); + assert("zam-936", keyle(from_key, to_key)); + + if (smallest_removed_p == NULL) + smallest_removed_p = &smallest_removed; + + write_tree_log(tree, tree_cut, from_key, to_key); + init_lh(&lock); + + do { + /* Find rightmost item to cut away from the tree. */ + result = object_lookup( + object, to_key, &right_coord, &lock, + ZNODE_WRITE_LOCK, FIND_MAX_NOT_MORE_THAN, TWIG_LEVEL, + LEAF_LEVEL, CBK_UNIQUE, 0/*ra_info*/); + if (result != CBK_COORD_FOUND) + break; + + tap_init(&tap, &right_coord, &lock, ZNODE_WRITE_LOCK); + result = cut_tree_worker( + &tap, from_key, to_key, smallest_removed_p, object, lazy); + tap_done(&tap); + + preempt_point(); + + } while (0); + + done_lh(&lock); + + if (result) { + switch (result) { + case -E_NO_NEIGHBOR: + result = 0; + break; + case -E_DEADLOCK: + result = -E_REPEAT; + case -E_REPEAT: + case -ENOMEM: + case -ENOENT: + break; + default: + warning("nikita-2861", "failure: %i", result); + } + } + + CHECK_COUNTERS; + return result; +} + +/* repeat cut_tree_object until everything is deleted. unlike cut_file_items, it + * does not end current transaction if -E_REPEAT is returned by + * cut_tree_object. */ +reiser4_internal int +cut_tree(reiser4_tree *tree, const reiser4_key *from, const reiser4_key *to, + struct inode *inode, int mode) +{ + int result; + + do { + result = cut_tree_object(tree, from, to, NULL, inode, mode); + } while (result == -E_REPEAT); + + return result; +} + + +/* first step of reiser4 tree initialization */ +reiser4_internal void +init_tree_0(reiser4_tree * tree) +{ + assert("zam-683", tree != NULL); + rw_tree_init(tree); + spin_epoch_init(tree); +} + +/* finishing reiser4 initialization */ +reiser4_internal int +init_tree(reiser4_tree * tree /* pointer to structure being + * initialized */ , + const reiser4_block_nr * root_block /* address of a root block + * on a disk */ , + tree_level height /* height of a tree */ , + node_plugin * nplug /* default node plugin */ ) +{ + int result; + + assert("nikita-306", tree != NULL); + assert("nikita-307", root_block != NULL); + assert("nikita-308", height > 0); + assert("nikita-309", nplug != NULL); + assert("zam-587", tree->super != NULL); + + /* someone might not call init_tree_0 before calling init_tree. */ + init_tree_0(tree); + + tree->root_block = *root_block; + tree->height = height; + tree->estimate_one_insert = calc_estimate_one_insert(height); + tree->nplug = nplug; + + tree->znode_epoch = 1ull; + + cbk_cache_init(&tree->cbk_cache); + + result = znodes_tree_init(tree); + if (result == 0) + result = jnodes_tree_init(tree); + if (result == 0) { + tree->uber = zget(tree, &UBER_TREE_ADDR, NULL, 0, GFP_KERNEL); + if (IS_ERR(tree->uber)) { + result = PTR_ERR(tree->uber); + tree->uber = NULL; + } + } + return result; +} + +/* release resources associated with @tree */ +reiser4_internal void +done_tree(reiser4_tree * tree /* tree to release */ ) +{ + assert("nikita-311", tree != NULL); + + if (tree->uber != NULL) { + zput(tree->uber); + tree->uber = NULL; + } + znodes_tree_done(tree); + jnodes_tree_done(tree); + cbk_cache_done(&tree->cbk_cache); +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/tree.h 2004-09-03 00:57:05.423214808 -0700 @@ -0,0 +1,556 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Tree operations. See fs/reiser4/tree.c for comments */ + +#if !defined( __REISER4_TREE_H__ ) +#define __REISER4_TREE_H__ + +#include "forward.h" +#include "debug.h" +#include "spin_macros.h" +#include "dformat.h" +#include "type_safe_list.h" +#include "plugin/node/node.h" +#include "plugin/plugin.h" +#include "jnode.h" +#include "znode.h" +#include "tap.h" + +#include /* for __u?? */ +#include /* for struct super_block */ +#include +#include /* for struct task_struct */ + +/* fictive block number never actually used */ +extern const reiser4_block_nr UBER_TREE_ADDR; + +/* define typed list for cbk_cache lru */ +TYPE_SAFE_LIST_DECLARE(cbk_cache); + +/* &cbk_cache_slot - entry in a coord cache. + + This is entry in a coord_by_key (cbk) cache, represented by + &cbk_cache. + +*/ +typedef struct cbk_cache_slot { + /* cached node */ + znode *node; + /* linkage to the next cbk cache slot in a LRU order */ + cbk_cache_list_link lru; +} cbk_cache_slot; + +/* &cbk_cache - coord cache. This is part of reiser4_tree. + + cbk_cache is supposed to speed up tree lookups by caching results of recent + successful lookups (we don't cache negative results as dentry cache + does). Cache consists of relatively small number of entries kept in a LRU + order. Each entry (&cbk_cache_slot) contains a pointer to znode, from + which we can obtain a range of keys that covered by this znode. Before + embarking into real tree traversal we scan cbk_cache slot by slot and for + each slot check whether key we are looking for is between minimal and + maximal keys for node pointed to by this slot. If no match is found, real + tree traversal is performed and if result is successful, appropriate entry + is inserted into cache, possibly pulling least recently used entry out of + it. + + Tree spin lock is used to protect coord cache. If contention for this + lock proves to be too high, more finer grained locking can be added. + + Invariants involving parts of this data-type: + + [cbk-cache-invariant] +*/ +typedef struct cbk_cache { + /* serializator */ + reiser4_rw_data guard; + int nr_slots; + /* head of LRU list of cache slots */ + cbk_cache_list_head lru; + /* actual array of slots */ + cbk_cache_slot *slot; +} cbk_cache; + +#define rw_ordering_pred_cbk_cache(cache) (1) + +/* defined read-write locking functions for cbk_cache */ +RW_LOCK_FUNCTIONS(cbk_cache, cbk_cache, guard); + +/* define list manipulation functions for cbk_cache LRU list */ +TYPE_SAFE_LIST_DEFINE(cbk_cache, cbk_cache_slot, lru); + +/* level_lookup_result - possible outcome of looking up key at some level. + This is used by coord_by_key when traversing tree downward. */ +typedef enum { + /* continue to the next level */ + LOOKUP_CONT, + /* done. Either required item was found, or we can prove it + doesn't exist, or some error occurred. */ + LOOKUP_DONE, + /* restart traversal from the root. Infamous "repetition". */ + LOOKUP_REST +} level_lookup_result; + +/* This is representation of internal reiser4 tree where all file-system + data and meta-data are stored. This structure is passed to all tree + manipulation functions. It's different from the super block because: + we don't want to limit ourselves to strictly one to one mapping + between super blocks and trees, and, because they are logically + different: there are things in a super block that have no relation to + the tree (bitmaps, journalling area, mount options, etc.) and there + are things in a tree that bear no relation to the super block, like + tree of znodes. + + At this time, there is only one tree + per filesystem, and this struct is part of the super block. We only + call the super block the super block for historical reasons (most + other filesystems call the per filesystem metadata the super block). +*/ + +struct reiser4_tree { + /* block_nr == 0 is fake znode. Write lock it, while changing + tree height. */ + /* disk address of root node of a tree */ + reiser4_block_nr root_block; + + /* level of the root node. If this is 1, tree consists of root + node only */ + tree_level height; + + /* + * this is cached here avoid calling plugins through function + * dereference all the time. + */ + __u64 estimate_one_insert; + + /* cache of recent tree lookup results */ + cbk_cache cbk_cache; + + /* hash table to look up znodes by block number. */ + z_hash_table zhash_table; + z_hash_table zfake_table; + /* hash table to look up jnodes by inode and offset. */ + j_hash_table jhash_table; + + /* lock protecting: + - parent pointers, + - sibling pointers, + - znode hash table + - coord cache + */ + /* NOTE: The "giant" tree lock can be replaced by more spin locks, + hoping they will be less contented. We can use one spin lock per one + znode hash bucket. With adding of some code complexity, sibling + pointers can be protected by both znode spin locks. However it looks + more SMP scalable we should test this locking change on n-ways (n > + 4) SMP machines. Current 4-ways machine test does not show that tree + lock is contented and it is a bottleneck (2003.07.25). */ + + reiser4_rw_data tree_lock; + + /* lock protecting delimiting keys */ + reiser4_rw_data dk_lock; + + /* spin lock protecting znode_epoch */ + reiser4_spin_data epoch_lock; + /* version stamp used to mark znode updates. See seal.[ch] for more + * information. */ + __u64 znode_epoch; + + znode *uber; + node_plugin *nplug; + struct super_block *super; + struct { + /* carry flags used for insertion of new nodes */ + __u32 new_node_flags; + /* carry flags used for insertion of new extents */ + __u32 new_extent_flags; + /* carry flags used for paste operations */ + __u32 paste_flags; + /* carry flags used for insert operations */ + __u32 insert_flags; + } carry; +}; + +#define spin_ordering_pred_epoch(tree) (1) +SPIN_LOCK_FUNCTIONS(epoch, reiser4_tree, epoch_lock); + +extern void init_tree_0(reiser4_tree *); + +extern int init_tree(reiser4_tree * tree, + const reiser4_block_nr * root_block, tree_level height, node_plugin * default_plugin); +extern void done_tree(reiser4_tree * tree); + +/* &reiser4_item_data - description of data to be inserted or pasted + + Q: articulate the reasons for the difference between this and flow. + + A: Becides flow we insert into tree other things: stat data, directory + entry, etc. To insert them into tree one has to provide this structure. If + one is going to insert flow - he can use insert_flow, where this structure + does not have to be created +*/ +struct reiser4_item_data { + /* actual data to be inserted. If NULL, ->create_item() will not + do xmemcpy itself, leaving this up to the caller. This can + save some amount of unnecessary memory copying, for example, + during insertion of stat data. + + */ + char *data; + /* 1 if 'char * data' contains pointer to user space and 0 if it is + kernel space */ + int user; + /* amount of data we are going to insert or paste */ + int length; + /* "Arg" is opaque data that is passed down to the + ->create_item() method of node layout, which in turn + hands it to the ->create_hook() of item being created. This + arg is currently used by: + + . ->create_hook() of internal item + (fs/reiser4/plugin/item/internal.c:internal_create_hook()), + . ->paste() method of directory item. + . ->create_hook() of extent item + + For internal item, this is left "brother" of new node being + inserted and it is used to add new node into sibling list + after parent to it was just inserted into parent. + + While ->arg does look somewhat of unnecessary compication, + it actually saves a lot of headache in many places, because + all data necessary to insert or paste new data into tree are + collected in one place, and this eliminates a lot of extra + argument passing and storing everywhere. + + */ + void *arg; + /* plugin of item we are inserting */ + item_plugin *iplug; +}; + +/* cbk flags: options for coord_by_key() */ +typedef enum { + /* coord_by_key() is called for insertion. This is necessary because + of extents being located at the twig level. For explanation, see + comment just above is_next_item_internal(). + */ + CBK_FOR_INSERT = (1 << 0), + /* coord_by_key() is called with key that is known to be unique */ + CBK_UNIQUE = (1 << 1), + /* coord_by_key() can trust delimiting keys. This options is not user + accessible. coord_by_key() will set it automatically. It will be + only cleared by special-case in extents-on-the-twig-level handling + where it is necessary to insert item with a key smaller than + leftmost key in a node. This is necessary because of extents being + located at the twig level. For explanation, see comment just above + is_next_item_internal(). + */ + CBK_TRUST_DK = (1 << 2), + CBK_READA = (1 << 3), /* original: readahead leaves which contain items of certain file */ + CBK_READDIR_RA = (1 << 4), /* readdir: readahead whole directory and all its stat datas */ + CBK_DKSET = (1 << 5), + CBK_EXTENDED_COORD = (1 << 6), /* coord_t is actually */ + CBK_IN_CACHE = (1 << 7), /* node is already in cache */ + CBK_USE_CRABLOCK = (1 << 8) /* use crab_lock in stead of long term + * lock */ +} cbk_flags; + +/* insertion outcome. IBK = insert by key */ +typedef enum { + IBK_INSERT_OK = 0, + IBK_ALREADY_EXISTS = -EEXIST, + IBK_IO_ERROR = -EIO, + IBK_NO_SPACE = -E_NODE_FULL, + IBK_OOM = -ENOMEM +} insert_result; + +#define IS_CBKERR(err) ((err) != CBK_COORD_FOUND && (err) != CBK_COORD_NOTFOUND) + +typedef int (*tree_iterate_actor_t) (reiser4_tree * tree, coord_t * coord, lock_handle * lh, void *arg); +extern int iterate_tree(reiser4_tree * tree, coord_t * coord, lock_handle * lh, + tree_iterate_actor_t actor, void *arg, znode_lock_mode mode, int through_units_p); +extern int get_uber_znode(reiser4_tree * tree, znode_lock_mode mode, + znode_lock_request pri, lock_handle *lh); + +/* return node plugin of @node */ +static inline node_plugin * +node_plugin_by_node(const znode * node /* node to query */ ) +{ + assert("vs-213", node != NULL); + assert("vs-214", znode_is_loaded(node)); + + return node->nplug; +} + +/* number of items in @node */ +static inline pos_in_node_t +node_num_items(const znode * node) +{ + assert("nikita-2754", znode_is_loaded(node)); + assert("nikita-2468", + node_plugin_by_node(node)->num_of_items(node) == node->nr_items); + + return node->nr_items; +} + +/* Return the number of items at the present node. Asserts coord->node != + NULL. */ +static inline unsigned +coord_num_items(const coord_t * coord) +{ + assert("jmacd-9805", coord->node != NULL); + + return node_num_items(coord->node); +} + +/* true if @node is empty */ +static inline int +node_is_empty(const znode * node) +{ + return node_num_items(node) == 0; +} + +typedef enum { + SHIFTED_SOMETHING = 0, + SHIFT_NO_SPACE = -E_NODE_FULL, + SHIFT_IO_ERROR = -EIO, + SHIFT_OOM = -ENOMEM, +} shift_result; + +extern node_plugin *node_plugin_by_coord(const coord_t * coord); +extern int is_coord_in_node(const coord_t * coord); +extern int key_in_node(const reiser4_key *, const coord_t *); +extern void coord_item_move_to(coord_t * coord, int items); +extern void coord_unit_move_to(coord_t * coord, int units); + +/* there are two types of repetitive accesses (ra): intra-syscall + (local) and inter-syscall (global). Local ra is used when + during single syscall we add/delete several items and units in the + same place in a tree. Note that plan-A fragments local ra by + separating stat-data and file body in key-space. Global ra is + used when user does repetitive modifications in the same place in a + tree. + + Our ra implementation serves following purposes: + 1 it affects balancing decisions so that next operation in a row + can be performed faster; + 2 it affects lower-level read-ahead in page-cache; + 3 it allows to avoid unnecessary lookups by maintaining some state + across several operations (this is only for local ra); + 4 it leaves room for lazy-micro-balancing: when we start a sequence of + operations they are performed without actually doing any intra-node + shifts, until we finish sequence or scope of sequence leaves + current node, only then we really pack node (local ra only). +*/ + +/* another thing that can be useful is to keep per-tree and/or + per-process cache of recent lookups. This cache can be organised as a + list of block numbers of formatted nodes sorted by starting key in + this node. Balancings should invalidate appropriate parts of this + cache. +*/ + +lookup_result coord_by_key(reiser4_tree * tree, const reiser4_key * key, + coord_t * coord, lock_handle * handle, + znode_lock_mode lock, lookup_bias bias, + tree_level lock_level, tree_level stop_level, __u32 flags, + ra_info_t *); + +lookup_result object_lookup(struct inode *object, + const reiser4_key * key, + coord_t * coord, + lock_handle * lh, + znode_lock_mode lock_mode, + lookup_bias bias, + tree_level lock_level, + tree_level stop_level, + __u32 flags, + ra_info_t *info); + +insert_result insert_by_key(reiser4_tree * tree, const reiser4_key * key, + reiser4_item_data * data, coord_t * coord, + lock_handle * lh, + tree_level stop_level, __u32 flags); +insert_result insert_by_coord(coord_t * coord, + reiser4_item_data * data, const reiser4_key * key, + lock_handle * lh, + __u32); +insert_result insert_extent_by_coord(coord_t * coord, + reiser4_item_data * data, const reiser4_key * key, lock_handle * lh); +int cut_node_content(coord_t *from, coord_t *to, + const reiser4_key *from_key, const reiser4_key *to_key, + reiser4_key *smallest_removed); +int kill_node_content(coord_t *from, coord_t *to, + const reiser4_key *from_key, const reiser4_key *to_key, + reiser4_key *smallest_removed, + znode *locked_left_neighbor, + struct inode *inode); + +int resize_item(coord_t * coord, reiser4_item_data * data, + reiser4_key * key, lock_handle * lh, cop_insert_flag); +int insert_into_item(coord_t * coord, lock_handle * lh, const reiser4_key * key, reiser4_item_data * data, unsigned); +int insert_flow(coord_t * coord, lock_handle * lh, flow_t * f); +int find_new_child_ptr(znode * parent, znode * child, znode * left, coord_t * result); + +int shift_right_of_but_excluding_insert_coord(coord_t * insert_coord); +int shift_left_of_and_including_insert_coord(coord_t * insert_coord); + +void fake_kill_hook_tail(struct inode *, loff_t start, loff_t end); + +extern int cut_tree_object(reiser4_tree*, const reiser4_key*, const reiser4_key*, reiser4_key*, struct inode*, int lazy); +extern int cut_tree(reiser4_tree *tree, const reiser4_key *from, const reiser4_key *to, struct inode*, int lazy); + +extern int delete_node(znode * node, reiser4_key *, struct inode *); +extern int check_tree_pointer(const coord_t * pointer, const znode * child); +extern int find_new_child_ptr(znode * parent, znode * child UNUSED_ARG, znode * left, coord_t * result); +extern int find_child_ptr(znode * parent, znode * child, coord_t * result); +extern int find_child_by_addr(znode * parent, znode * child, coord_t * result); +extern int set_child_delimiting_keys(znode * parent, const coord_t * in_parent, znode *child); +extern znode *child_znode(const coord_t * in_parent, znode * parent, int incore_p, int setup_dkeys_p); + +extern int cbk_cache_init(cbk_cache * cache); +extern void cbk_cache_done(cbk_cache * cache); +extern void cbk_cache_invalidate(const znode * node, reiser4_tree * tree); +extern void cbk_cache_add(const znode * node); + +extern const char *bias_name(lookup_bias bias); +extern char *sprint_address(const reiser4_block_nr * block); + +#if REISER4_DEBUG_OUTPUT +extern void print_coord_content(const char *prefix, coord_t * p); +extern void print_address(const char *prefix, const reiser4_block_nr * block); +extern void print_tree_rec(const char *prefix, reiser4_tree * tree, __u32 flags); +extern void print_cbk_slot(const char *prefix, const cbk_cache_slot * slot); +extern void print_cbk_cache(const char *prefix, const cbk_cache * cache); +#else +#define print_coord_content(p, c) noop +#define print_address(p, b) noop +#define print_tree_rec(p, f, t) noop +#define print_cbk_slot(p, s) noop +#define print_cbk_cache(p, c) noop +#endif + +extern void forget_znode(lock_handle * handle); +extern int deallocate_znode(znode * node); + +extern int is_disk_addr_unallocated(const reiser4_block_nr * addr); +extern void *unallocated_disk_addr_to_ptr(const reiser4_block_nr * addr); + +/* struct used internally to pack all numerous arguments of tree lookup. + Used to avoid passing a lot of arguments to helper functions. */ +typedef struct cbk_handle { + /* tree we are in */ + reiser4_tree *tree; + /* key we are going after */ + const reiser4_key *key; + /* coord we will store result in */ + coord_t *coord; + /* type of lock to take on target node */ + znode_lock_mode lock_mode; + /* lookup bias. See comments at the declaration of lookup_bias */ + lookup_bias bias; + /* lock level: level starting from which tree traversal starts taking + * write locks. */ + tree_level lock_level; + /* level where search will stop. Either item will be found between + lock_level and stop_level, or CBK_COORD_NOTFOUND will be + returned. + */ + tree_level stop_level; + /* level we are currently at */ + tree_level level; + /* block number of @active node. Tree traversal operates on two + nodes: active and parent. */ + reiser4_block_nr block; + /* put here error message to be printed by caller */ + const char *error; + /* result passed back to caller */ + lookup_result result; + /* lock handles for active and parent */ + lock_handle *parent_lh; + lock_handle *active_lh; + reiser4_key ld_key; + reiser4_key rd_key; + /* flags, passed to the cbk routine. Bits of this bitmask are defined + in tree.h:cbk_flags enum. */ + __u32 flags; + ra_info_t *ra_info; + struct inode *object; +} cbk_handle; + +extern znode_lock_mode cbk_lock_mode(tree_level level, cbk_handle * h); + +/* eottl.c */ +extern int handle_eottl(cbk_handle * h, int *outcome); + +int lookup_multikey(cbk_handle * handle, int nr_keys); +int lookup_couple(reiser4_tree * tree, + const reiser4_key * key1, const reiser4_key * key2, + coord_t * coord1, coord_t * coord2, + lock_handle * lh1, lock_handle * lh2, + znode_lock_mode lock_mode, lookup_bias bias, + tree_level lock_level, tree_level stop_level, __u32 flags, int *result1, int *result2); + +/* ordering constraint for tree spin lock: tree lock is "strongest" */ +#define rw_ordering_pred_tree(tree) \ + (lock_counters()->spin_locked_txnh == 0) && \ + (lock_counters()->rw_locked_tree == 0) + +/* Define spin_lock_tree, spin_unlock_tree, and spin_tree_is_locked: + spin lock protecting znode hash, and parent and sibling pointers. */ +RW_LOCK_FUNCTIONS(tree, reiser4_tree, tree_lock); + +/* ordering constraint for delimiting key spin lock: dk lock is weaker than + tree lock */ +#define rw_ordering_pred_dk( tree ) \ + (lock_counters()->rw_locked_tree == 0) && \ + (lock_counters()->spin_locked_jnode == 0) && \ + (lock_counters()->rw_locked_zlock == 0) && \ + (lock_counters()->spin_locked_txnh == 0) && \ + (lock_counters()->spin_locked_atom == 0) && \ + (lock_counters()->spin_locked_inode_object == 0) && \ + (lock_counters()->spin_locked_txnmgr == 0) + +/* Define spin_lock_dk(), spin_unlock_dk(), etc: locking for delimiting + keys. */ +RW_LOCK_FUNCTIONS(dk, reiser4_tree, dk_lock); + +#if REISER4_DEBUG +#define check_tree() print_tree_rec( "", current_tree, REISER4_TREE_CHECK ) +#else +#define check_tree() noop +#endif + +/* estimate api. Implementation is in estimate.c */ +reiser4_block_nr estimate_internal_amount(reiser4_block_nr childen, tree_level); +reiser4_block_nr estimate_one_insert_item(reiser4_tree *); +reiser4_block_nr estimate_one_insert_into_item(reiser4_tree *); +reiser4_block_nr estimate_insert_flow(tree_level); +reiser4_block_nr estimate_one_item_removal(reiser4_tree *); +reiser4_block_nr calc_estimate_one_insert(tree_level); +reiser4_block_nr estimate_disk_cluster(struct inode *); +reiser4_block_nr estimate_insert_cluster(struct inode *, int); + +/* take read or write tree lock, depending on @takeread argument */ +#define XLOCK_TREE(tree, takeread) \ + (takeread ? RLOCK_TREE(tree) : WLOCK_TREE(tree)) + +/* release read or write tree lock, depending on @takeread argument */ +#define XUNLOCK_TREE(tree, takeread) \ + (takeread ? RUNLOCK_TREE(tree) : WUNLOCK_TREE(tree)) + +/* __REISER4_TREE_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/tree_mod.c 2004-09-03 00:57:07.761859280 -0700 @@ -0,0 +1,364 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* + * Functions to add/delete new nodes to/from the tree. + * + * Functions from this file are used by carry (see carry*) to handle: + * + * . insertion of new formatted node into tree + * + * . addition of new tree root, increasing tree height + * + * . removing tree root, decreasing tree height + * + */ + +#include "forward.h" +#include "debug.h" +#include "dformat.h" +#include "key.h" +#include "coord.h" +#include "plugin/plugin.h" +#include "jnode.h" +#include "znode.h" +#include "tree_mod.h" +#include "block_alloc.h" +#include "tree_walk.h" +#include "tree.h" +#include "super.h" + +#include + +static int add_child_ptr(znode * parent, znode * child); +/* warning only issued if error is not -E_REPEAT */ +#define ewarning( error, ... ) \ + if( ( error ) != -E_REPEAT ) \ + warning( __VA_ARGS__ ) + +/* allocate new node on the @level and immediately on the right of @brother. */ +reiser4_internal znode * +new_node(znode * brother /* existing left neighbor of new node */ , + tree_level level /* tree level at which new node is to + * be allocated */ ) +{ + znode *result; + int retcode; + reiser4_block_nr blocknr; + + assert("nikita-930", brother != NULL); + assert("umka-264", level < REAL_MAX_ZTREE_HEIGHT); + + retcode = assign_fake_blocknr_formatted(&blocknr); + if (retcode == 0) { + result = zget(znode_get_tree(brother), &blocknr, NULL, level, GFP_KERNEL); + if (IS_ERR(result)) { + ewarning(PTR_ERR(result), "nikita-929", + "Cannot allocate znode for carry: %li", PTR_ERR(result)); + return result; + } + /* cheap test, can be executed even when debugging is off */ + if (!znode_just_created(result)) { + warning("nikita-2213", "Allocated already existing block: %llu", + (unsigned long long)blocknr); + zput(result); + return ERR_PTR(RETERR(-EIO)); + } + + assert("nikita-931", result != NULL); + result->nplug = znode_get_tree(brother)->nplug; + assert("nikita-933", result->nplug != NULL); + + retcode = zinit_new(result, GFP_KERNEL); + if (retcode == 0) { + ZF_SET(result, JNODE_CREATED); + zrelse(result); + } else { + zput(result); + result = ERR_PTR(retcode); + } + } else { + /* failure to allocate new node during balancing. + This should never happen. Ever. Returning -E_REPEAT + is not viable solution, because "out of disk space" + is not transient error that will go away by itself. + */ + ewarning(retcode, "nikita-928", + "Cannot allocate block for carry: %i", retcode); + result = ERR_PTR(retcode); + } + assert("nikita-1071", result != NULL); + return result; +} + +/* allocate new root and add it to the tree + + This helper function is called by add_new_root(). + +*/ +reiser4_internal znode * +add_tree_root(znode * old_root /* existing tree root */ , + znode * fake /* "fake" znode */ ) +{ + reiser4_tree *tree = znode_get_tree(old_root); + znode *new_root = NULL; /* to shut gcc up */ + int result; + + assert("nikita-1069", old_root != NULL); + assert("umka-262", fake != NULL); + assert("umka-263", tree != NULL); + + /* "fake" znode---one always hanging just above current root. This + node is locked when new root is created or existing root is + deleted. Downward tree traversal takes lock on it before taking + lock on a root node. This avoids race conditions with root + manipulations. + + */ + assert("nikita-1348", znode_above_root(fake)); + assert("nikita-1211", znode_is_root(old_root)); + + result = 0; + if (tree->height >= REAL_MAX_ZTREE_HEIGHT) { + warning("nikita-1344", "Tree is too tall: %i", tree->height); + /* ext2 returns -ENOSPC when it runs out of free inodes with a + following comment (fs/ext2/ialloc.c:441): Is it really + ENOSPC? + + -EXFULL? -EINVAL? + */ + result = RETERR(-ENOSPC); + } else { + /* Allocate block for new root. It's not that + important where it will be allocated, as root is + almost always in memory. Moreover, allocate on + flush can be going here. + */ + assert("nikita-1448", znode_is_root(old_root)); + new_root = new_node(fake, tree->height + 1); + if (!IS_ERR(new_root) && (result = zload(new_root)) == 0) { + lock_handle rlh; + + init_lh(&rlh); + result = longterm_lock_znode(&rlh, new_root, ZNODE_WRITE_LOCK, ZNODE_LOCK_LOPRI); + if (result == 0) { + parent_coord_t *in_parent; + + znode_make_dirty(fake); + + /* new root is a child of "fake" node */ + WLOCK_TREE(tree); + + ++tree->height; + + /* recalculate max balance overhead */ + tree->estimate_one_insert = estimate_one_insert_item(tree); + + tree->root_block = *znode_get_block(new_root); + in_parent = &new_root->in_parent; + init_parent_coord(in_parent, fake); + /* manually insert new root into sibling + * list. With this all nodes involved into + * balancing are connected after balancing is + * done---useful invariant to check. */ + sibling_list_insert_nolock(new_root, NULL); + WUNLOCK_TREE(tree); + + /* insert into new root pointer to the + @old_root. */ + assert("nikita-1110", WITH_DATA(new_root, node_is_empty(new_root))); + WLOCK_DK(tree); + znode_set_ld_key(new_root, min_key()); + znode_set_rd_key(new_root, max_key()); + WUNLOCK_DK(tree); + if (REISER4_DEBUG) { + ZF_CLR(old_root, JNODE_LEFT_CONNECTED); + ZF_CLR(old_root, JNODE_RIGHT_CONNECTED); + ZF_SET(old_root, JNODE_ORPHAN); + } + result = add_child_ptr(new_root, old_root); + done_lh(&rlh); + } + zrelse(new_root); + } + } + if (result != 0) + new_root = ERR_PTR(result); + return new_root; +} + +/* build &reiser4_item_data for inserting child pointer + + Build &reiser4_item_data that can be later used to insert pointer to @child + in its parent. + +*/ +reiser4_internal void +build_child_ptr_data(znode * child /* node pointer to which will be + * inserted */ , + reiser4_item_data * data /* where to store result */ ) +{ + assert("nikita-1116", child != NULL); + assert("nikita-1117", data != NULL); + + /* this is subtle assignment to meditate upon */ + data->data = (char *) znode_get_block(child); + /* data -> data is kernel space */ + data->user = 0; + data->length = sizeof (reiser4_block_nr); + /* FIXME-VS: hardcoded internal item? */ + + /* AUDIT: Is it possible that "item_plugin_by_id" may find nothing? */ + data->iplug = item_plugin_by_id(NODE_POINTER_ID); +} + +/* add pointer to @child into empty @parent. + + This is used when pointer to old root is inserted into new root which is + empty. +*/ +static int +add_child_ptr(znode * parent, znode * child) +{ + coord_t coord; + reiser4_item_data data; + int result; + reiser4_key *key; + + assert("nikita-1111", parent != NULL); + assert("nikita-1112", child != NULL); + assert("nikita-1115", znode_get_level(parent) == znode_get_level(child) + 1); + + result = zload(parent); + if (result != 0) + return result; + assert("nikita-1113", node_is_empty(parent)); + coord_init_first_unit(&coord, parent); + + build_child_ptr_data(child, &data); + data.arg = NULL; + + key = UNDER_RW(dk, znode_get_tree(parent), read, znode_get_ld_key(child)); + result = node_plugin_by_node(parent)->create_item(&coord, key, &data, NULL); + znode_make_dirty(parent); + zrelse(parent); + return result; +} + +/* actually remove tree root */ +static int +kill_root(reiser4_tree * tree /* tree from which root is being + * removed */ , + znode * old_root /* root node that is being removed */ , + znode * new_root /* new root---sole child of * + * @old_root */ , + const reiser4_block_nr * new_root_blk /* disk address of + * @new_root */ ) +{ + znode *uber; + int result; + lock_handle handle_for_uber; + + assert("umka-265", tree != NULL); + assert("nikita-1198", new_root != NULL); + assert("nikita-1199", znode_get_level(new_root) + 1 == znode_get_level(old_root)); + + assert("nikita-1201", znode_is_write_locked(old_root)); + + assert("nikita-1203", disk_addr_eq(new_root_blk, znode_get_block(new_root))); + + init_lh(&handle_for_uber); + /* obtain and lock "fake" znode protecting changes in tree height. */ + result = get_uber_znode(tree, ZNODE_WRITE_LOCK, ZNODE_LOCK_HIPRI, + &handle_for_uber); + if (result == 0) { + uber = handle_for_uber.node; + + znode_make_dirty(uber); + + /* don't take long term lock a @new_root. Take spinlock. */ + + WLOCK_TREE(tree); + + tree->root_block = *new_root_blk; + --tree->height; + + /* recalculate max balance overhead */ + tree->estimate_one_insert = estimate_one_insert_item(tree); + + assert("nikita-1202", tree->height == znode_get_level(new_root)); + + /* new root is child on "fake" node */ + init_parent_coord(&new_root->in_parent, uber); + ++ uber->c_count; + + /* sibling_list_insert_nolock(new_root, NULL); */ + WUNLOCK_TREE(tree); + + /* reinitialise old root. */ + result = node_plugin_by_node(old_root)->init(old_root); + znode_make_dirty(old_root); + if (result == 0) { + assert("nikita-1279", node_is_empty(old_root)); + ZF_SET(old_root, JNODE_HEARD_BANSHEE); + old_root->c_count = 0; + } + } + done_lh(&handle_for_uber); + + return result; +} + +/* remove tree root + + This function removes tree root, decreasing tree height by one. Tree root + and its only child (that is going to become new tree root) are write locked + at the entry. + + To remove tree root we need to take lock on special "fake" znode that + protects changes of tree height. See comments in add_tree_root() for more + on this. + + Also parent pointers have to be updated in + old and new root. To simplify code, function is split into two parts: outer + kill_tree_root() collects all necessary arguments and calls kill_root() + to do the actual job. + +*/ +reiser4_internal int +kill_tree_root(znode * old_root /* tree root that we are removing */ ) +{ + int result; + coord_t down_link; + znode *new_root; + reiser4_tree *tree; + + assert("umka-266", current_tree != NULL); + assert("nikita-1194", old_root != NULL); + assert("nikita-1196", znode_is_root(old_root)); + assert("nikita-1200", node_num_items(old_root) == 1); + assert("nikita-1401", znode_is_write_locked(old_root)); + + coord_init_first_unit(&down_link, old_root); + + tree = znode_get_tree(old_root); + new_root = child_znode(&down_link, old_root, 0, 1); + if (!IS_ERR(new_root)) { + result = kill_root(tree, old_root, new_root, znode_get_block(new_root)); + zput(new_root); + } else + result = PTR_ERR(new_root); + + return result; +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/tree_mod.h 2004-09-03 00:57:05.426214352 -0700 @@ -0,0 +1,29 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Functions to add/delete new nodes to/from the tree. See tree_mod.c for + * comments. */ + +#if !defined( __REISER4_TREE_MOD_H__ ) +#define __REISER4_TREE_MOD_H__ + +#include "forward.h" + +znode *new_node(znode * brother, tree_level level); +znode *add_tree_root(znode * old_root, znode * fake); +int kill_tree_root(znode * old_root); +void build_child_ptr_data(znode * child, reiser4_item_data * data); + +/* __REISER4_TREE_MOD_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/tree_walk.c 2004-09-03 00:57:05.431213592 -0700 @@ -0,0 +1,1175 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Routines and macros to: + + get_left_neighbor() + + get_right_neighbor() + + get_parent() + + get_first_child() + + get_last_child() + + various routines to walk the whole tree and do things to it like + repack it, or move it to tertiary storage. Please make them as + generic as is reasonable. + +*/ + +#include "forward.h" +#include "debug.h" +#include "dformat.h" +#include "coord.h" +#include "plugin/item/item.h" +#include "jnode.h" +#include "znode.h" +#include "tree_walk.h" +#include "tree.h" +#include "super.h" + +/* These macros are used internally in tree_walk.c in attempt to make + lock_neighbor() code usable to build lock_parent(), lock_right_neighbor, + lock_left_neighbor */ +#define GET_NODE_BY_PTR_OFFSET(node, off) (*(znode**)(((unsigned long)(node)) + (off))) +#define FIELD_OFFSET(name) offsetof(znode, name) +#define PARENT_PTR_OFFSET FIELD_OFFSET(in_parent.node) +#define LEFT_PTR_OFFSET FIELD_OFFSET(left) +#define RIGHT_PTR_OFFSET FIELD_OFFSET(right) + +/* This is the generic procedure to get and lock `generic' neighbor (left or + right neighbor or parent). It implements common algorithm for all cases of + getting lock on neighbor node, only znode structure field is different in + each case. This is parameterized by ptr_offset argument, which is byte + offset for the pointer to the desired neighbor within the current node's + znode structure. This function should be called with the tree lock held */ +static int +lock_neighbor( + /* resulting lock handle*/ + lock_handle * result, + /* znode to lock */ + znode * node, + /* pointer to neighbor (or parent) znode field offset, in bytes from + the base address of znode structure */ + int ptr_offset, + /* lock mode for longterm_lock_znode call */ + znode_lock_mode mode, + /* lock request for longterm_lock_znode call */ + znode_lock_request req, + /* GN_* flags */ + int flags, + int rlocked) +{ + reiser4_tree *tree = znode_get_tree(node); + znode *neighbor; + int ret; + + assert("umka-236", node != NULL); + assert("umka-237", tree != NULL); + assert("umka-301", rw_tree_is_locked(tree)); + + reiser4_stat_inc_at_level(znode_get_level(node), znode.lock_neighbor); + + if (flags & GN_TRY_LOCK) + req |= ZNODE_LOCK_NONBLOCK; + if (flags & GN_SAME_ATOM) + req |= ZNODE_LOCK_DONT_FUSE; + + /* get neighbor's address by using of sibling link, quit while loop + (and return) if link is not available. */ + while (1) { + reiser4_stat_inc_at_level(znode_get_level(node), + znode.lock_neighbor_iteration); + neighbor = GET_NODE_BY_PTR_OFFSET(node, ptr_offset); + + /* return -E_NO_NEIGHBOR if parent or side pointer is NULL or if + * node pointed by it is not connected. + * + * However, GN_ALLOW_NOT_CONNECTED option masks "connected" + * check and allows passing reference to not connected znode to + * subsequent longterm_lock_znode() call. This kills possible + * busy loop if we are trying to get longterm lock on locked but + * not yet connected parent node. */ + if (neighbor == NULL || !((flags & GN_ALLOW_NOT_CONNECTED) + || znode_is_connected(neighbor))) { + return RETERR(-E_NO_NEIGHBOR); + } + + /* protect it from deletion. */ + zref(neighbor); + + XUNLOCK_TREE(tree, rlocked); + + ret = longterm_lock_znode(result, neighbor, mode, req); + + /* The lock handle obtains its own reference, release the one from above. */ + zput(neighbor); + + XLOCK_TREE(tree, rlocked); + + /* restart if node we got reference to is being + invalidated. we should not get reference to this node + again.*/ + if (ret == -EINVAL) + continue; + if (ret) + return ret; + + /* check if neighbor link still points to just locked znode; + the link could have been changed while the process slept. */ + if (neighbor == GET_NODE_BY_PTR_OFFSET(node, ptr_offset)) + return 0; + + /* znode was locked by mistake; unlock it and restart locking + process from beginning. */ + XUNLOCK_TREE(tree, rlocked); + longterm_unlock_znode(result); + XLOCK_TREE(tree, rlocked); + } +} +/* get parent node with longterm lock, accepts GN* flags. */ +reiser4_internal int +reiser4_get_parent_flags(lock_handle * result /* resulting lock handle */, + znode * node /* child node */, + znode_lock_mode mode /* type of lock: read or write */, + int flags /* GN_* flags */) +{ + return UNDER_RW(tree, znode_get_tree(node), read, + lock_neighbor(result, node, PARENT_PTR_OFFSET, mode, + ZNODE_LOCK_HIPRI, flags, 1)); +} + +/* A wrapper for reiser4_get_parent_flags(). */ +reiser4_internal int +reiser4_get_parent(lock_handle * result /* resulting lock + * handle */ , + znode * node /* child node */ , + znode_lock_mode mode /* type of lock: read or write */ , + int only_connected_p /* if this is true, parent is + * only returned when it is + * connected. If parent is + * unconnected, -E_NO_NEIGHBOR is + * returned. Normal users should + * pass 1 here. Only during carry + * we want to access still + * unconnected parents. */ ) +{ + assert("umka-238", znode_get_tree(node) != NULL); + + return reiser4_get_parent_flags(result, node, mode, + only_connected_p ? 0 : GN_ALLOW_NOT_CONNECTED); +} + +/* wrapper function to lock right or left neighbor depending on GN_GO_LEFT + bit in @flags parameter */ +/* Audited by: umka (2002.06.14) */ +static inline int +lock_side_neighbor(lock_handle * result, + znode * node, + znode_lock_mode mode, + int flags, int rlocked) +{ + int ret; + int ptr_offset; + znode_lock_request req; + + if (flags & GN_GO_LEFT) { + ptr_offset = LEFT_PTR_OFFSET; + req = ZNODE_LOCK_LOPRI; + } else { + ptr_offset = RIGHT_PTR_OFFSET; + req = ZNODE_LOCK_HIPRI; + } + + ret = lock_neighbor(result, node, ptr_offset, mode, req, flags, rlocked); + + if (ret == -E_NO_NEIGHBOR) /* if we walk left or right -E_NO_NEIGHBOR does not + * guarantee that neighbor is absent in the + * tree; in this case we return -ENOENT -- + * means neighbor at least not found in + * cache */ + return RETERR(-ENOENT); + + return ret; +} + +#if REISER4_DEBUG_SIBLING_LIST +int check_sibling_list(znode * node) +{ + znode *scan; + znode *next; + + assert("nikita-3283", LOCK_CNT_GTZ(write_locked_tree)); + + if (node == NULL) + return 1; + + if (ZF_ISSET(node, JNODE_RIP)) + return 1; + + assert("nikita-3270", node != NULL); + assert("nikita-3269", rw_tree_is_write_locked(znode_get_tree(node))); + + for (scan = node; znode_is_left_connected(scan); scan = next) { + next = scan->left; + if (next != NULL && !ZF_ISSET(next, JNODE_RIP)) { + assert("nikita-3271", znode_is_right_connected(next)); + assert("nikita-3272", next->right == scan); + } else + break; + } + for (scan = node; znode_is_right_connected(scan); scan = next) { + next = scan->right; + if (next != NULL && !ZF_ISSET(next, JNODE_RIP)) { + assert("nikita-3273", znode_is_left_connected(next)); + assert("nikita-3274", next->left == scan); + } else + break; + } + return 1; +} +#endif + +/* Znode sibling pointers maintenence. */ + +/* Znode sibling pointers are established between any neighbored nodes which are + in cache. There are two znode state bits (JNODE_LEFT_CONNECTED, + JNODE_RIGHT_CONNECTED), if left or right sibling pointer contains actual + value (even NULL), corresponded JNODE_*_CONNECTED bit is set. + + Reiser4 tree operations which may allocate new znodes (CBK, tree balancing) + take care about searching (hash table lookup may be required) of znode + neighbors, establishing sibling pointers between them and setting + JNODE_*_CONNECTED state bits. */ + +/* adjusting of sibling pointers and `connected' states for two + neighbors; works if one neighbor is NULL (was not found). */ + +/* FIXME-VS: this is unstatic-ed to use in tree.c in prepare_twig_cut */ +reiser4_internal void +link_left_and_right(znode * left, znode * right) +{ + assert("nikita-3275", check_sibling_list(left)); + assert("nikita-3275", check_sibling_list(right)); + + if (left != NULL) { + if (left->right == NULL) { + left->right = right; + ZF_SET(left, JNODE_RIGHT_CONNECTED); + } else if (ZF_ISSET(left->right, JNODE_HEARD_BANSHEE)) { + left->right->left = NULL; + left->right = right; + ZF_SET(left, JNODE_RIGHT_CONNECTED); + } else + /* + * there is a race condition in renew_sibling_link() + * and assertions below check that it is only one + * there. Thread T1 calls renew_sibling_link() without + * GN_NO_ALLOC flag. zlook() doesn't find neighbor + * node, but before T1 gets to the + * link_left_and_right(), another thread T2 creates + * neighbor node and connects it. check for + * left->right == NULL above protects T1 from + * overwriting correct left->right pointer installed + * by T2. + */ + assert("nikita-3302", + right == NULL || left->right == right); + } + if (right != NULL) { + if (right->left == NULL) { + right->left = left; + ZF_SET(right, JNODE_LEFT_CONNECTED); + } else if (ZF_ISSET(right->left, JNODE_HEARD_BANSHEE)) { + right->left->right = NULL; + right->left = left; + ZF_SET(right, JNODE_LEFT_CONNECTED); + } else + assert("nikita-3303", + left == NULL || right->left == left); + } + assert("nikita-3275", check_sibling_list(left)); + assert("nikita-3275", check_sibling_list(right)); +} + +/* Audited by: umka (2002.06.14) */ +static void +link_znodes(znode * first, znode * second, int to_left) +{ + if (to_left) + link_left_and_right(second, first); + else + link_left_and_right(first, second); +} + +/* getting of next (to left or to right, depend on gn_to_left bit in flags) + coord's unit position in horizontal direction, even across node + boundary. Should be called under tree lock, it protects nonexistence of + sibling link on parent level, if lock_side_neighbor() fails with + -ENOENT. */ +static int +far_next_coord(coord_t * coord, lock_handle * handle, int flags) +{ + int ret; + znode *node; + reiser4_tree *tree; + + assert("umka-243", coord != NULL); + assert("umka-244", handle != NULL); + + handle->owner = NULL; /* mark lock handle as unused */ + + ret = (flags & GN_GO_LEFT) ? coord_prev_unit(coord) : coord_next_unit(coord); + if (!ret) + return 0; + + ret = lock_side_neighbor(handle, coord->node, ZNODE_READ_LOCK, flags, 0); + if (ret) + return ret; + + node = handle->node; + tree = znode_get_tree(node); + WUNLOCK_TREE(tree); + + coord_init_zero(coord); + + /* We avoid synchronous read here if it is specified by flag. */ + if ((flags & GN_ASYNC) && znode_page(handle->node) == NULL) { + ret = jstartio(ZJNODE(handle->node)); + if (!ret) + ret = -E_REPEAT; + goto error_locked; + } + + /* corresponded zrelse() should be called by the clients of + far_next_coord(), in place when this node gets unlocked. */ + ret = zload(handle->node); + if (ret) + goto error_locked; + + if (flags & GN_GO_LEFT) + coord_init_last_unit(coord, node); + else + coord_init_first_unit(coord, node); + + if (0) { + error_locked: + longterm_unlock_znode(handle); + } + WLOCK_TREE(tree); + return ret; +} + +/* Very significant function which performs a step in horizontal direction + when sibling pointer is not available. Actually, it is only function which + does it. + Note: this function does not restore locking status at exit, + caller should does care about proper unlocking and zrelsing */ +static int +renew_sibling_link(coord_t * coord, lock_handle * handle, znode * child, tree_level level, int flags, int *nr_locked) +{ + int ret; + int to_left = flags & GN_GO_LEFT; + reiser4_block_nr da; + /* parent of the neighbor node; we set it to parent until not sharing + of one parent between child and neighbor node is detected */ + znode *side_parent = coord->node; + reiser4_tree *tree = znode_get_tree(child); + znode *neighbor = NULL; + + assert("umka-245", coord != NULL); + assert("umka-246", handle != NULL); + assert("umka-247", child != NULL); + assert("umka-303", tree != NULL); + + WLOCK_TREE(tree); + ret = far_next_coord(coord, handle, flags); + + if (ret) { + if (ret != -ENOENT) { + WUNLOCK_TREE(tree); + return ret; + } + } else { + item_plugin *iplug; + + if (handle->owner != NULL) { + (*nr_locked)++; + side_parent = handle->node; + } + + /* does coord object points to internal item? We do not + support sibling pointers between znode for formatted and + unformatted nodes and return -E_NO_NEIGHBOR in that case. */ + iplug = item_plugin_by_coord(coord); + if (!item_is_internal(coord)) { + link_znodes(child, NULL, to_left); + WUNLOCK_TREE(tree); + /* we know there can't be formatted neighbor */ + return RETERR(-E_NO_NEIGHBOR); + } + WUNLOCK_TREE(tree); + + iplug->s.internal.down_link(coord, NULL, &da); + + if (flags & GN_NO_ALLOC) { + neighbor = zlook(tree, &da); + } else { + neighbor = zget(tree, &da, side_parent, level, GFP_KERNEL); + } + + if (IS_ERR(neighbor)) { + ret = PTR_ERR(neighbor); + return ret; + } + + if (neighbor) + /* update delimiting keys */ + set_child_delimiting_keys(coord->node, coord, neighbor); + + WLOCK_TREE(tree); + } + + if (likely(neighbor == NULL || + (znode_get_level(child) == znode_get_level(neighbor) && child != neighbor))) + link_znodes(child, neighbor, to_left); + else { + warning("nikita-3532", + "Sibling nodes on the different levels: %i != %i\n", + znode_get_level(child), znode_get_level(neighbor)); + ret = RETERR(-EIO); + } + + WUNLOCK_TREE(tree); + + /* if GN_NO_ALLOC isn't set we keep reference to neighbor znode */ + if (neighbor != NULL && (flags & GN_NO_ALLOC)) + /* atomic_dec(&ZJNODE(neighbor)->x_count); */ + zput(neighbor); + + return ret; +} + +/* This function is for establishing of one side relation. */ +/* Audited by: umka (2002.06.14) */ +static int +connect_one_side(coord_t * coord, znode * node, int flags) +{ + coord_t local; + lock_handle handle; + int nr_locked; + int ret; + + assert("umka-248", coord != NULL); + assert("umka-249", node != NULL); + + coord_dup_nocheck(&local, coord); + + init_lh(&handle); + + ret = renew_sibling_link(&local, &handle, node, znode_get_level(node), flags | GN_NO_ALLOC, &nr_locked); + + if (handle.owner != NULL) { + /* complementary operations for zload() and lock() in far_next_coord() */ + zrelse(handle.node); + longterm_unlock_znode(&handle); + } + + /* we catch error codes which are not interesting for us because we + run renew_sibling_link() only for znode connection. */ + if (ret == -ENOENT || ret == -E_NO_NEIGHBOR) + return 0; + + return ret; +} + +/* if @child is not in `connected' state, performs hash searches for left and + right neighbor nodes and establishes horizontal sibling links */ +/* Audited by: umka (2002.06.14), umka (2002.06.15) */ +reiser4_internal int +connect_znode(coord_t * parent_coord, znode * child) +{ + reiser4_tree *tree = znode_get_tree(child); + int ret = 0; + + assert("zam-330", parent_coord != NULL); + assert("zam-331", child != NULL); + assert("zam-332", parent_coord->node != NULL); + assert("umka-305", tree != NULL); + + /* it is trivial to `connect' root znode because it can't have + neighbors */ + if (znode_above_root(parent_coord->node)) { + child->left = NULL; + child->right = NULL; + ZF_SET(child, JNODE_LEFT_CONNECTED); + ZF_SET(child, JNODE_RIGHT_CONNECTED); + return 0; + } + + /* load parent node */ + coord_clear_iplug(parent_coord); + ret = zload(parent_coord->node); + + if (ret != 0) + return ret; + + /* protect `connected' state check by tree_lock */ + RLOCK_TREE(tree); + + if (!znode_is_right_connected(child)) { + RUNLOCK_TREE(tree); + /* connect right (default is right) */ + ret = connect_one_side(parent_coord, child, GN_NO_ALLOC); + if (ret) + goto zrelse_and_ret; + + RLOCK_TREE(tree); + } + + ret = znode_is_left_connected(child); + + RUNLOCK_TREE(tree); + + if (!ret) { + ret = connect_one_side(parent_coord, child, GN_NO_ALLOC | GN_GO_LEFT); + } else + ret = 0; + +zrelse_and_ret: + zrelse(parent_coord->node); + + return ret; +} + +/* this function is like renew_sibling_link() but allocates neighbor node if + it doesn't exist and `connects' it. It may require making two steps in + horizontal direction, first one for neighbor node finding/allocation, + second one is for finding neighbor of neighbor to connect freshly allocated + znode. */ +/* Audited by: umka (2002.06.14), umka (2002.06.15) */ +static int +renew_neighbor(coord_t * coord, znode * node, tree_level level, int flags) +{ + coord_t local; + lock_handle empty[2]; + reiser4_tree *tree = znode_get_tree(node); + znode *neighbor = NULL; + int nr_locked = 0; + int ret; + + assert("umka-250", coord != NULL); + assert("umka-251", node != NULL); + assert("umka-307", tree != NULL); + assert("umka-308", level <= tree->height); + + /* umka (2002.06.14) + Here probably should be a check for given "level" validness. + Something like assert("xxx-yyy", level < REAL_MAX_ZTREE_HEIGHT); + */ + + coord_dup(&local, coord); + + ret = renew_sibling_link(&local, &empty[0], node, level, flags & ~GN_NO_ALLOC, &nr_locked); + if (ret) + goto out; + + /* tree lock is not needed here because we keep parent node(s) locked + and reference to neighbor znode incremented */ + neighbor = (flags & GN_GO_LEFT) ? node->left : node->right; + + ret = UNDER_RW(tree, tree, read, znode_is_connected(neighbor)); + + if (ret) { + ret = 0; + goto out; + } + + ret = renew_sibling_link(&local, &empty[nr_locked], neighbor, level, flags | GN_NO_ALLOC, &nr_locked); + /* second renew_sibling_link() call is used for znode connection only, + so we can live with these errors */ + if (-ENOENT == ret || -E_NO_NEIGHBOR == ret) + ret = 0; + +out: + + for (--nr_locked; nr_locked >= 0; --nr_locked) { + zrelse(empty[nr_locked].node); + longterm_unlock_znode(&empty[nr_locked]); + } + + if (neighbor != NULL) + /* decrement znode reference counter without actually + releasing it. */ + atomic_dec(&ZJNODE(neighbor)->x_count); + + return ret; +} + +/* + reiser4_get_neighbor() -- lock node's neighbor. + + reiser4_get_neighbor() locks node's neighbor (left or right one, depends on + given parameter) using sibling link to it. If sibling link is not available + (i.e. neighbor znode is not in cache) and flags allow read blocks, we go one + level up for information about neighbor's disk address. We lock node's + parent, if it is common parent for both 'node' and its neighbor, neighbor's + disk address is in next (to left or to right) down link from link that points + to original node. If not, we need to lock parent's neighbor, read its content + and take first(last) downlink with neighbor's disk address. That locking + could be done by using sibling link and lock_neighbor() function, if sibling + link exists. In another case we have to go level up again until we find + common parent or valid sibling link. Then go down + allocating/connecting/locking/reading nodes until neighbor of first one is + locked. + + @neighbor: result lock handle, + @node: a node which we lock neighbor of, + @lock_mode: lock mode {LM_READ, LM_WRITE}, + @flags: logical OR of {GN_*} (see description above) subset. + + @return: 0 if success, negative value if lock was impossible due to an error + or lack of neighbor node. +*/ + +/* Audited by: umka (2002.06.14), umka (2002.06.15) */ +reiser4_internal int +reiser4_get_neighbor ( + lock_handle * neighbor, znode * node, znode_lock_mode lock_mode, int flags) +{ + reiser4_tree *tree = znode_get_tree(node); + lock_handle path[REAL_MAX_ZTREE_HEIGHT]; + + coord_t coord; + + tree_level base_level; + tree_level h = 0; + int ret; + + assert("umka-252", tree != NULL); + assert("umka-253", neighbor != NULL); + assert("umka-254", node != NULL); + + base_level = znode_get_level(node); + + assert("umka-310", base_level <= tree->height); + + coord_init_zero(&coord); + +again: + /* first, we try to use simple lock_neighbor() which requires sibling + link existence */ + ret = UNDER_RW(tree, tree, read, + lock_side_neighbor(neighbor, node, lock_mode, flags, 1)); + + if (!ret) { + /* load znode content if it was specified */ + if (flags & GN_LOAD_NEIGHBOR) { + ret = zload(node); + if (ret) + longterm_unlock_znode(neighbor); + } + return ret; + } + + /* only -ENOENT means we may look upward and try to connect + @node with its neighbor (if @flags allow us to do it) */ + if (ret != -ENOENT || !(flags & GN_CAN_USE_UPPER_LEVELS)) + return ret; + + /* before establishing of sibling link we lock parent node; it is + required by renew_neighbor() to work. */ + init_lh(&path[0]); + ret = reiser4_get_parent(&path[0], node, ZNODE_READ_LOCK, 1); + if (ret) + return ret; + if (znode_above_root(path[0].node)) { + longterm_unlock_znode(&path[0]); + return RETERR(-E_NO_NEIGHBOR); + } + + while (1) { + znode *child = (h == 0) ? node : path[h - 1].node; + znode *parent = path[h].node; + + reiser4_stat_inc_at_level(h + LEAF_LEVEL, sibling_search); + ret = zload(parent); + if (ret) + break; + + ret = find_child_ptr(parent, child, &coord); + + if (ret) { + zrelse(parent); + break; + } + + /* try to establish missing sibling link */ + ret = renew_neighbor(&coord, child, h + base_level, flags); + + zrelse(parent); + + switch (ret) { + case 0: + /* unlocking of parent znode prevents simple + deadlock situation */ + done_lh(&path[h]); + + /* depend on tree level we stay on we repeat first + locking attempt ... */ + if (h == 0) + goto again; + + /* ... or repeat establishing of sibling link at + one level below. */ + --h; + break; + + case -ENOENT: + /* sibling link is not available -- we go + upward. */ + init_lh(&path[h + 1]); + ret = reiser4_get_parent(&path[h + 1], parent, ZNODE_READ_LOCK, 1); + if (ret) + goto fail; + ++h; + if (znode_above_root(path[h].node)) { + ret = RETERR(-E_NO_NEIGHBOR); + goto fail; + } + break; + + case -E_DEADLOCK: + /* there was lock request from hi-pri locker. if + it is possible we unlock last parent node and + re-lock it again. */ + while (check_deadlock()) { + if (h == 0) + goto fail; + + done_lh(&path[--h]); + } + + break; + + default: /* other errors. */ + goto fail; + } + } +fail: + ON_DEBUG(check_lock_node_data(node)); + ON_DEBUG(check_lock_data()); + + /* unlock path */ + do { + longterm_unlock_znode(&path[h]); + --h; + } while (h + 1 != 0); + + return ret; +} + +/* remove node from sibling list */ +/* Audited by: umka (2002.06.14) */ +reiser4_internal void +sibling_list_remove(znode * node) +{ + assert("umka-255", node != NULL); + assert("zam-878", rw_tree_is_write_locked(znode_get_tree(node))); + assert("nikita-3275", check_sibling_list(node)); + + if (znode_is_right_connected(node) && node->right != NULL) { + assert("zam-322", znode_is_left_connected(node->right)); + node->right->left = node->left; + } + if (znode_is_left_connected(node) && node->left != NULL) { + assert("zam-323", znode_is_right_connected(node->left)); + node->left->right = node->right; + } + ZF_CLR(node, JNODE_LEFT_CONNECTED); + ZF_CLR(node, JNODE_RIGHT_CONNECTED); + ON_DEBUG(node->left = node->right = NULL); + assert("nikita-3276", check_sibling_list(node)); +} + +/* disconnect node from sibling list */ +reiser4_internal void +sibling_list_drop(znode * node) +{ + znode *right; + znode *left; + + assert("nikita-2464", node != NULL); + assert("nikita-3277", check_sibling_list(node)); + + right = node->right; + if (right != NULL) { + assert("nikita-2465", znode_is_left_connected(right)); + right->left = NULL; + } + left = node->left; + if (left != NULL) { + assert("zam-323", znode_is_right_connected(left)); + left->right = NULL; + } + ZF_CLR(node, JNODE_LEFT_CONNECTED); + ZF_CLR(node, JNODE_RIGHT_CONNECTED); + ON_DEBUG(node->left = node->right = NULL); +} + +/* Insert new node into sibling list. Regular balancing inserts new node + after (at right side) existing and locked node (@before), except one case + of adding new tree root node. @before should be NULL in that case. */ +reiser4_internal void +sibling_list_insert_nolock(znode * new, znode * before) +{ + assert("zam-334", new != NULL); + assert("nikita-3298", !znode_is_left_connected(new)); + assert("nikita-3299", !znode_is_right_connected(new)); + assert("nikita-3300", new->left == NULL); + assert("nikita-3301", new->right == NULL); + assert("nikita-3278", check_sibling_list(new)); + assert("nikita-3279", check_sibling_list(before)); + + if (before != NULL) { + assert("zam-333", znode_is_connected(before)); + new->right = before->right; + new->left = before; + if (before->right != NULL) + before->right->left = new; + before->right = new; + } else { + new->right = NULL; + new->left = NULL; + } + ZF_SET(new, JNODE_LEFT_CONNECTED); + ZF_SET(new, JNODE_RIGHT_CONNECTED); + assert("nikita-3280", check_sibling_list(new)); + assert("nikita-3281", check_sibling_list(before)); +} + +struct tw_handle { + /* A key for tree walking (re)start, updated after each successful tree + * node processing */ + reiser4_key start_key; + /* A tree traversal current position. */ + tap_t tap; + /* An externally supplied pair of functions for formatted and + * unformatted nodes processing. */ + struct tree_walk_actor * actor; + /* It is passed to actor functions as is. */ + void * opaque; + /* A direction of a tree traversal: 1 if going from right to left. */ + int go_left:1; + /* "Done" flag */ + int done:1; + /* Current node was processed completely */ + int node_completed:1; +}; + +/* it locks the root node, handles the restarts inside */ +static int lock_tree_root (lock_handle * lock, znode_lock_mode mode) +{ + int ret; + + reiser4_tree * tree = current_tree; + lock_handle uber_znode_lock; + znode * root; + + init_lh(&uber_znode_lock); + again: + + ret = get_uber_znode(tree, mode, ZNODE_LOCK_HIPRI, &uber_znode_lock); + if (ret) + return ret; + + root = zget(tree, &tree->root_block, uber_znode_lock.node, tree->height, GFP_KERNEL); + if (IS_ERR(root)) { + done_lh(&uber_znode_lock); + return PTR_ERR(root); + } + + ret = longterm_lock_znode(lock, root, ZNODE_WRITE_LOCK, ZNODE_LOCK_HIPRI); + + zput(root); + done_lh(&uber_znode_lock); + + if (ret == -E_DEADLOCK) + goto again; + + return ret; +} + +/* Update the handle->start_key by the first key of the node is being + * processed. */ +static int update_start_key(struct tw_handle * h) +{ + int ret; + + ret = tap_load(&h->tap); + if (ret == 0) { + unit_key_by_coord(h->tap.coord, &h->start_key); + tap_relse(&h->tap); + } + return ret; +} + +/* Move tap to the next node, load it. */ +static int go_next_node (struct tw_handle * h, lock_handle * lock, const coord_t * coord) +{ + int ret; + + assert ("zam-948", ergo (coord != NULL, lock->node == coord->node)); + + tap_relse(&h->tap); + + ret = tap_move(&h->tap, lock); + if (ret) + return ret; + + ret = tap_load(&h->tap); + if (ret) + goto error; + + if (coord) + coord_dup(h->tap.coord, coord); + else { + if (h->go_left) + coord_init_last_unit(h->tap.coord, lock->node); + else + coord_init_first_unit(h->tap.coord, lock->node); + } + + if (h->actor->process_znode != NULL) { + ret = (h->actor->process_znode)(&h->tap, h->opaque); + if (ret) + goto error; + } + + ret = update_start_key(h); + + error: + done_lh(lock); + return ret; +} + +static void next_unit (struct tw_handle * h) +{ + if (h->go_left) + h->node_completed = coord_prev_unit(h->tap.coord); + else + h->node_completed = coord_next_unit(h->tap.coord); +} + + +/* Move tree traversal position (which is embedded into tree_walk_handle) to the + * parent of current node (h->lh.node). */ +static int tw_up (struct tw_handle * h) +{ + coord_t coord; + lock_handle lock; + load_count load; + int ret; + + init_lh(&lock); + init_load_count(&load); + + do { + ret = reiser4_get_parent(&lock, h->tap.lh->node, ZNODE_WRITE_LOCK, 0); + if (ret) + break; + if (znode_above_root(lock.node)) { + h->done = 1; + break; + } + ret = incr_load_count_znode(&load, lock.node); + if (ret) + break; + ret = find_child_ptr(lock.node, h->tap.lh->node, &coord); + if (ret) + break; + ret = go_next_node(h, &lock, &coord); + if (ret) + break; + next_unit(h); + } while (0); + + done_load_count(&load); + done_lh(&lock); + + return ret; +} + +/* Move tree traversal position to the child of current node pointed by + * h->tap.coord. */ +static int tw_down(struct tw_handle * h) +{ + reiser4_block_nr block; + lock_handle lock; + znode * child; + item_plugin * iplug; + tree_level level = znode_get_level(h->tap.lh->node); + int ret; + + assert ("zam-943", item_is_internal(h->tap.coord)); + + iplug = item_plugin_by_coord(h->tap.coord); + iplug->s.internal.down_link(h->tap.coord, NULL, &block); + init_lh(&lock); + + do { + child = zget(current_tree, &block, h->tap.lh->node, level - 1, GFP_KERNEL); + if (IS_ERR(child)) + return PTR_ERR(child); + ret = connect_znode(h->tap.coord, child); + if (ret) + break; + ret = longterm_lock_znode(&lock, child, ZNODE_WRITE_LOCK, 0); + if (ret) + break; + set_child_delimiting_keys(h->tap.coord->node, h->tap.coord, child); + ret = go_next_node (h, &lock, NULL); + } while(0); + + zput(child); + done_lh(&lock); + return ret; +} +/* Traverse the reiser4 tree until either all tree traversing is done or an + * error encountered (including recoverable ones as -E_DEADLOCK or -E_REPEAT). The + * @actor function is able to stop tree traversal by returning an appropriate + * error code. */ +static int tw_by_handle (struct tw_handle * h) +{ + int ret; + lock_handle next_lock; + + ret = tap_load(&h->tap); + if (ret) + return ret; + + init_lh (&next_lock); + + while (!h->done) { + tree_level level; + + if (h->node_completed) { + h->node_completed = 0; + ret = tw_up(h); + if (ret) + break; + continue; + } + + assert ("zam-944", coord_is_existing_unit(h->tap.coord)); + level = znode_get_level(h->tap.lh->node); + + if (level == LEAF_LEVEL) { + h->node_completed = 1; + continue; + } + + if (item_is_extent(h->tap.coord)) { + if (h->actor->process_extent != NULL) { + ret = (h->actor->process_extent)(&h->tap, h->opaque); + if (ret) + break; + } + next_unit(h); + continue; + } + + ret = tw_down(h); + if (ret) + break; + } + + done_lh(&next_lock); + return ret; +} + +/* Walk the reiser4 tree in parent-first order */ +reiser4_internal int +tree_walk (const reiser4_key *start_key, int go_left, struct tree_walk_actor * actor, void * opaque) +{ + coord_t coord; + lock_handle lock; + struct tw_handle handle; + + int ret; + + assert ("zam-950", actor != NULL); + + handle.actor = actor; + handle.opaque = opaque; + handle.go_left = !!go_left; + handle.done = 0; + handle.node_completed = 0; + + init_lh(&lock); + + if (start_key == NULL) { + if (actor->before) { + ret = actor->before(opaque); + if (ret) + return ret; + } + + ret = lock_tree_root(&lock, ZNODE_WRITE_LOCK); + if (ret) + return ret; + ret = zload(lock.node); + if (ret) + goto done; + + if (go_left) + coord_init_last_unit(&coord, lock.node); + else + coord_init_first_unit_nocheck(&coord, lock.node); + + zrelse(lock.node); + goto no_start_key; + } else + handle.start_key = *start_key; + + do { + if (actor->before) { + ret = actor->before(opaque); + if (ret) + return ret; + } + + ret = coord_by_key(current_tree, &handle.start_key, &coord, &lock, ZNODE_WRITE_LOCK, + FIND_MAX_NOT_MORE_THAN, TWIG_LEVEL, LEAF_LEVEL, 0, NULL); + if (ret != CBK_COORD_FOUND) + break; + no_start_key: + tap_init(&handle.tap, &coord, &lock, ZNODE_WRITE_LOCK); + + ret = update_start_key(&handle); + if (ret) { + tap_done(&handle.tap); + break; + } + ret = tw_by_handle(&handle); + tap_done (&handle.tap); + + } while (!handle.done && (ret == -E_DEADLOCK || ret == -E_REPEAT)); + + done: + done_lh(&lock); + return ret; +} + + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 80 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/tree_walk.h 2004-09-03 00:57:05.432213440 -0700 @@ -0,0 +1,117 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +/* definitions of reiser4 tree walk functions */ + +#ifndef __FS_REISER4_TREE_WALK_H__ +#define __FS_REISER4_TREE_WALK_H__ + +#include "debug.h" +#include "forward.h" + +/* establishes horizontal links between cached znodes */ +int connect_znode(coord_t * coord, znode * node); + +/* tree traversal functions (reiser4_get_parent(), reiser4_get_neighbor()) + have the following common arguments: + + return codes: + + @return : 0 - OK, + +ZAM-FIXME-HANS: wrong return code name. Change them all. + -ENOENT - neighbor is not in cache, what is detected by sibling + link absence. + + -E_NO_NEIGHBOR - we are sure that neighbor (or parent) node cannot be + found (because we are left-/right- most node of the + tree, for example). Also, this return code is for + reiser4_get_parent() when we see no parent link -- it + means that our node is root node. + + -E_DEADLOCK - deadlock detected (request from high-priority process + received), other error codes are conformed to + /usr/include/asm/errno.h . +*/ + +int +reiser4_get_parent_flags(lock_handle * result, znode * node, + znode_lock_mode mode, int flags); + +int reiser4_get_parent(lock_handle * result, znode * node, znode_lock_mode mode, int only_connected_p); + +/* bits definition for reiser4_get_neighbor function `flags' arg. */ +typedef enum { + /* If sibling pointer is NULL, this flag allows get_neighbor() to try to + * find not allocated not connected neigbor by going though upper + * levels */ + GN_CAN_USE_UPPER_LEVELS = 0x1, + /* locking left neighbor instead of right one */ + GN_GO_LEFT = 0x2, + /* automatically load neighbor node content */ + GN_LOAD_NEIGHBOR = 0x4, + /* return -E_REPEAT if can't lock */ + GN_TRY_LOCK = 0x8, + /* used internally in tree_walk.c, causes renew_sibling to not + allocate neighbor znode, but only search for it in znode cache */ + GN_NO_ALLOC = 0x10, + /* do not go across atom boundaries */ + GN_SAME_ATOM = 0x20, + /* allow to lock not connected nodes */ + GN_ALLOW_NOT_CONNECTED = 0x40, + /* Avoid synchronous jload, instead, call jstartio() and return -E_REPEAT. */ + GN_ASYNC = 0x80 +} znode_get_neigbor_flags; + +int reiser4_get_neighbor(lock_handle * neighbor, znode * node, znode_lock_mode lock_mode, int flags); + +/* there are wrappers for most common usages of reiser4_get_neighbor() */ +static inline int +reiser4_get_left_neighbor(lock_handle * result, znode * node, int lock_mode, int flags) +{ + return reiser4_get_neighbor(result, node, lock_mode, flags | GN_GO_LEFT); +} + +static inline int +reiser4_get_right_neighbor(lock_handle * result, znode * node, int lock_mode, int flags) +{ + ON_DEBUG(check_lock_node_data(node)); + ON_DEBUG(check_lock_data()); + return reiser4_get_neighbor(result, node, lock_mode, flags & (~GN_GO_LEFT)); +} + +extern void invalidate_lock(lock_handle * _link); + +extern void sibling_list_remove(znode * node); +extern void sibling_list_drop(znode * node); +extern void sibling_list_insert_nolock(znode * new, znode * before); +extern void link_left_and_right(znode * left, znode * right); + +/* Functions called by tree_walk() when tree_walk() ... */ +struct tree_walk_actor { + /* ... meets a formatted node, */ + int (*process_znode)(tap_t* , void*); + /* ... meets an extent, */ + int (*process_extent)(tap_t*, void*); + /* ... begins tree traversal or repeats it after -E_REPEAT was returned by + * node or extent processing functions. */ + int (*before)(void *); +}; +extern int tree_walk(const reiser4_key *, int, struct tree_walk_actor *, void *); + +#if REISER4_DEBUG_SIBLING_LIST +int check_sibling_list(znode * node); +#else +#define check_sibling_list(n) (1) +#endif + +#endif /* __FS_REISER4_TREE_WALK_H__ */ + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/txnmgr.c 2004-09-03 00:57:05.452210400 -0700 @@ -0,0 +1,4368 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Joshua MacDonald wrote the first draft of this code. */ + +/* ZAM-LONGTERM-FIXME-HANS: The locking in this file is badly designed, and a +filesystem scales only as well as its worst locking design. You need to +substantially restructure this code. Josh was not as experienced a programmer +as you. Particularly review how the locking style differs from what you did +for znodes usingt hi-lo priority locking, and present to me an opinion on +whether the differences are well founded. */ + +/* I cannot help but to disagree with the sentiment above. Locking of + * transaction manager is _not_ badly designed, and, at the very least, is not + * the scaling bottleneck. Scaling bottleneck is _exactly_ hi-lo priority + * locking on znodes, especially on the root node of the tree. --nikita, + * 2003.10.13 */ + +/* The txnmgr is a set of interfaces that keep track of atoms and transcrash handles. The + txnmgr processes capture_block requests and manages the relationship between jnodes and + atoms through the various stages of a transcrash, and it also oversees the fusion and + capture-on-copy processes. The main difficulty with this task is maintaining a + deadlock-free lock ordering between atoms and jnodes/handles. The reason for the + difficulty is that jnodes, handles, and atoms contain pointer circles, and the cycle + must be broken. The main requirement is that atom-fusion be deadlock free, so once you + hold the atom_lock you may then wait to acquire any jnode or handle lock. This implies + that any time you check the atom-pointer of a jnode or handle and then try to lock that + atom, you must use trylock() and possibly reverse the order. + + This code implements the design documented at: + + http://namesys.com/txn-doc.html + +ZAM-FIXME-HANS: update v4.html to contain all of the information present in the above (but updated), and then remove the +above document and reference the new. Be sure to provide some credit to Josh. I already have some writings on this +topic in v4.html, but they are lacking in details present in the above. Cure that. Remember to write for the bright 12 +year old --- define all technical terms used. + +*/ + +/* Thoughts on the external transaction interface: + + In the current code, a TRANSCRASH handle is created implicitly by init_context() (which + creates state that lasts for the duration of a system call and is called at the start + of ReiserFS methods implementing VFS operations), and closed by reiser4_exit_context(), + occupying the scope of a single system call. We wish to give certain applications an + interface to begin and close (commit) transactions. Since our implementation of + transactions does not yet support isolation, allowing an application to open a + transaction implies trusting it to later close the transaction. Part of the + transaction interface will be aimed at enabling that trust, but the interface for + actually using transactions is fairly narrow. + + BEGIN_TRANSCRASH: Returns a transcrash identifier. It should be possible to translate + this identifier into a string that a shell-script could use, allowing you to start a + transaction by issuing a command. Once open, the transcrash should be set in the task + structure, and there should be options (I suppose) to allow it to be carried across + fork/exec. A transcrash has several options: + + - READ_FUSING or WRITE_FUSING: The default policy is for txn-capture to capture only + on writes (WRITE_FUSING) and allow "dirty reads". If the application wishes to + capture on reads as well, it should set READ_FUSING. + + - TIMEOUT: Since a non-isolated transcrash cannot be undone, every transcrash must + eventually close (or else the machine must crash). If the application dies an + unexpected death with an open transcrash, for example, or if it hangs for a long + duration, one solution (to avoid crashing the machine) is to simply close it anyway. + This is a dangerous option, but it is one way to solve the problem until isolated + transcrashes are available for untrusted applications. + + It seems to be what databases do, though it is unclear how one avoids a DoS attack + creating a vulnerability based on resource starvation. Guaranteeing that some + minimum amount of computational resources are made available would seem more correct + than guaranteeing some amount of time. When we again have someone to code the work, + this issue should be considered carefully. -Hans + + RESERVE_BLOCKS: A running transcrash should indicate to the transaction manager how + many dirty blocks it expects. The reserve_blocks interface should be called at a point + where it is safe for the application to fail, because the system may not be able to + grant the allocation and the application must be able to back-out. For this reason, + the number of reserve-blocks can also be passed as an argument to BEGIN_TRANSCRASH, but + the application may also wish to extend the allocation after beginning its transcrash. + + CLOSE_TRANSCRASH: The application closes the transcrash when it is finished making + modifications that require transaction protection. When isolated transactions are + supported the CLOSE operation is replaced by either COMMIT or ABORT. For example, if a + RESERVE_BLOCKS call fails for the application, it should "abort" by calling + CLOSE_TRANSCRASH, even though it really commits any changes that were made (which is + why, for safety, the application should call RESERVE_BLOCKS before making any changes). + + For actually implementing these out-of-system-call-scopped transcrashes, the + reiser4_context has a "txn_handle *trans" pointer that may be set to an open + transcrash. Currently there are no dynamically-allocated transcrashes, but there is a + "kmem_cache_t *_txnh_slab" created for that purpose in this file. +*/ + +/* Extending the other system call interfaces for future transaction features: + + Specialized applications may benefit from passing flags to the ordinary system call + interface such as read(), write(), or stat(). For example, the application specifies + WRITE_FUSING by default but wishes to add that a certain read() command should be + treated as READ_FUSING. But which read? Is it the directory-entry read, the stat-data + read, or the file-data read? These issues are straight-forward, but there are a lot of + them and adding the necessary flags-passing code will be tedious. + + When supporting isolated transactions, there is a corresponding READ_MODIFY_WRITE (RMW) + flag, which specifies that although it is a read operation being requested, a + write-lock should be taken. The reason is that read-locks are shared while write-locks + are exclusive, so taking a read-lock when a later-write is known in advance will often + leads to deadlock. If a reader knows it will write later, it should issue read + requests with the RMW flag set. +*/ + +/* + The znode/atom deadlock avoidance. + + FIXME(Zam): writing of this comment is in progress. + + The atom's special stage ASTAGE_CAPTURE_WAIT introduces a kind of atom's + long-term locking, which makes reiser4 locking scheme more complex. It had + deadlocks until we implement deadlock avoidance algorithms. That deadlocks + looked as the following: one stopped thread waits for a long-term lock on + znode, the thread who owns that lock waits when fusion with another atom will + be allowed. + + The source of the deadlocks is an optimization of not capturing index nodes + for read. Let's prove it. Suppose we have dumb node capturing scheme which + unconditionally captures each block before locking it. + + That scheme has no deadlocks. Let's begin with the thread which stage is + ASTAGE_CAPTURE_WAIT and it waits for a znode lock. The thread can't wait for + a capture because it's stage allows fusion with any atom except which are + being committed currently. A process of atom commit can't deadlock because + atom commit procedure does not acquire locks and does not fuse with other + atoms. Reiser4 does capturing right before going to sleep inside the + longtertm_lock_znode() function, it means the znode which we want to lock is + already captured and its atom is in ASTAGE_CAPTURE_WAIT stage. If we + continue the analysis we understand that no one process in the sequence may + waits atom fusion. Thereby there are no deadlocks of described kind. + + The capturing optimization makes the deadlocks possible. A thread can wait a + lock which owner did not captured that node. The lock owner's current atom + is not fused with the first atom and it does not get a ASTAGE_CAPTURE_WAIT + state. A deadlock is possible when that atom meets another one which is in + ASTAGE_CAPTURE_WAIT already. + + The deadlock avoidance scheme includes two algorithms: + + First algorithm is used when a thread captures a node which is locked but not + captured by another thread. Those nodes are marked MISSED_IN_CAPTURE at the + moment we skip their capturing. If such a node (marked MISSED_IN_CAPTURE) is + being captured by a thread with current atom is in ASTAGE_CAPTURE_WAIT, the + routine which forces all lock owners to join with current atom is executed. + + Second algorithm does not allow to skip capturing of already captured nodes. + + Both algorithms together prevent waiting a longterm lock without atom fusion + with atoms of all lock owners, which is a key thing for getting atom/znode + locking deadlocks. +*/ + +/* + * Transactions and mmap(2). + * + * 1. Transactions are not supported for accesses through mmap(2), because + * this would effectively amount to user-level transactions whose duration + * is beyond control of the kernel. + * + * 2. That said, we still want to preserve some decency with regard to + * mmap(2). During normal write(2) call, following sequence of events + * happens: + * + * 1. page is created; + * + * 2. jnode is created, dirtied and captured into current atom. + * + * 3. extent is inserted and modified. + * + * Steps (2) and (3) take place under long term lock on the twig node. + * + * When file is accessed through mmap(2) page is always created during + * page fault. After this (in reiser4_readpage()->readpage_extent()): + * + * 1. if access is made to non-hole page new jnode is created, (if + * necessary) + * + * 2. if access is made to the hole page, jnode is not created (XXX + * not clear why). + * + * Also, even if page is created by write page fault it is not marked + * dirty immediately by handle_mm_fault(). Probably this is to avoid races + * with page write-out. + * + * Dirty bit installed by hardware is only transferred to the struct page + * later, when page is unmapped (in zap_pte_range(), or + * try_to_unmap_one()). + * + * So, with mmap(2) we have to handle following irksome situations: + * + * 1. there exists modified page (clean or dirty) without jnode + * + * 2. there exists modified page (clean or dirty) with clean jnode + * + * 3. clean page which is a part of atom can be transparently modified + * at any moment through mapping without becoming dirty. + * + * (1) and (2) can lead to the out-of-memory situation: ->writepage() + * doesn't know what to do with such pages and ->sync_sb()/->writepages() + * don't see them, because these methods operate on atoms. + * + * (3) can lead to the loss of data: suppose we have dirty page with dirty + * captured jnode captured by some atom. As part of early flush (for + * example) page was written out. Dirty bit was cleared on both page and + * jnode. After this page is modified through mapping, but kernel doesn't + * notice and just discards page and jnode as part of commit. (XXX + * actually it doesn't, because to reclaim page ->releasepage() has to be + * called and before this dirty bit will be transferred to the struct + * page). + * + */ + +#include "debug.h" +#include "type_safe_list.h" +#include "txnmgr.h" +#include "jnode.h" +#include "znode.h" +#include "block_alloc.h" +#include "tree.h" +#include "wander.h" +#include "ktxnmgrd.h" +#include "super.h" +#include "page_cache.h" +#include "reiser4.h" +#include "vfs_ops.h" +#include "inode.h" +#include "prof.h" +#include "flush.h" + +#include +#include +#include +#include +#include +#include +#include +#include /* for totalram_pages */ + +static void atom_free(txn_atom * atom); + +static long commit_txnh(txn_handle * txnh); + +static void wakeup_atom_waitfor_list(txn_atom * atom); +static void wakeup_atom_waiting_list(txn_atom * atom); + +static void capture_assign_txnh_nolock(txn_atom * atom, txn_handle * txnh); + +static void capture_assign_block_nolock(txn_atom * atom, jnode * node); + +static int capture_assign_block(txn_handle * txnh, jnode * node); + +static int capture_assign_txnh(jnode * node, txn_handle * txnh, txn_capture mode, int can_coc); + +static int fuse_not_fused_lock_owners(txn_handle * txnh, znode * node); + +static int capture_init_fusion(jnode * node, txn_handle * txnh, txn_capture mode, int can_coc); + +static int capture_fuse_wait(jnode * node, txn_handle * txnh, txn_atom * atomf, txn_atom * atomh, txn_capture mode); + +static void capture_fuse_into(txn_atom * small, txn_atom * large); + +static int capture_copy(jnode * node, txn_handle * txnh, txn_atom * atomf, txn_atom * atomh, txn_capture mode, int can_coc); + +void invalidate_list(capture_list_head *); + +/* GENERIC STRUCTURES */ + +typedef struct _txn_wait_links txn_wait_links; + +struct _txn_wait_links { + lock_stack *_lock_stack; + fwaitfor_list_link _fwaitfor_link; + fwaiting_list_link _fwaiting_link; + int (*waitfor_cb)(txn_atom *atom, struct _txn_wait_links *wlinks); + int (*waiting_cb)(txn_atom *atom, struct _txn_wait_links *wlinks); +}; + +TYPE_SAFE_LIST_DEFINE(txnh, txn_handle, txnh_link); + +TYPE_SAFE_LIST_DEFINE(fwaitfor, txn_wait_links, _fwaitfor_link); +TYPE_SAFE_LIST_DEFINE(fwaiting, txn_wait_links, _fwaiting_link); + +/* FIXME: In theory, we should be using the slab cache init & destructor + methods instead of, e.g., jnode_init, etc. */ +static kmem_cache_t *_atom_slab = NULL; +/* this is for user-visible, cross system-call transactions. */ +static kmem_cache_t *_txnh_slab = NULL; + +ON_DEBUG(extern atomic_t flush_cnt;) + +/* TXN_INIT */ +/* Initialize static variables in this file. */ +reiser4_internal int +txnmgr_init_static(void) +{ + assert("jmacd-600", _atom_slab == NULL); + assert("jmacd-601", _txnh_slab == NULL); + + ON_DEBUG(atomic_set(&flush_cnt, 0)); + + _atom_slab = kmem_cache_create("txn_atom", sizeof (txn_atom), 0, + SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + NULL, NULL); + + if (_atom_slab == NULL) { + goto error; + } + + _txnh_slab = kmem_cache_create("txn_handle", sizeof (txn_handle), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + + if (_txnh_slab == NULL) { + goto error; + } + + return 0; + +error: + + if (_atom_slab != NULL) { + kmem_cache_destroy(_atom_slab); + } + if (_txnh_slab != NULL) { + kmem_cache_destroy(_txnh_slab); + } + return RETERR(-ENOMEM); +} + +/* Un-initialize static variables in this file. */ +reiser4_internal int +txnmgr_done_static(void) +{ + int ret1, ret2, ret3; + + ret1 = ret2 = ret3 = 0; + + if (_atom_slab != NULL) { + ret1 = kmem_cache_destroy(_atom_slab); + _atom_slab = NULL; + } + + if (_txnh_slab != NULL) { + ret2 = kmem_cache_destroy(_txnh_slab); + _txnh_slab = NULL; + } + + return ret1 ? : ret2; +} + +/* Initialize a new transaction manager. Called when the super_block is initialized. */ +reiser4_internal void +txnmgr_init(txn_mgr * mgr) +{ + assert("umka-169", mgr != NULL); + + mgr->atom_count = 0; + mgr->id_count = 1; + + atom_list_init(&mgr->atoms_list); + spin_txnmgr_init(mgr); + + sema_init(&mgr->commit_semaphore, 1); +} + +/* Free transaction manager. */ +reiser4_internal int +txnmgr_done(txn_mgr * mgr UNUSED_ARG) +{ + assert("umka-170", mgr != NULL); + + return 0; +} + +/* Initialize a transaction handle. */ +/* Audited by: umka (2002.06.13) */ +static void +txnh_init(txn_handle * txnh, txn_mode mode) +{ + assert("umka-171", txnh != NULL); + + txnh->mode = mode; + txnh->atom = NULL; + txnh->flags = 0; + + spin_txnh_init(txnh); + + txnh_list_clean(txnh); +} + +#if REISER4_DEBUG +/* Check if a transaction handle is clean. */ +static int +txnh_isclean(txn_handle * txnh) +{ + assert("umka-172", txnh != NULL); + return txnh->atom == NULL && spin_txnh_is_not_locked(txnh); +} +#endif + +/* Initialize an atom. */ +static void +atom_init(txn_atom * atom) +{ + int level; + + assert("umka-173", atom != NULL); + + xmemset(atom, 0, sizeof (txn_atom)); + + atom->stage = ASTAGE_FREE; + atom->start_time = jiffies; + + for (level = 0; level < REAL_MAX_ZTREE_HEIGHT + 1; level += 1) + capture_list_init(ATOM_DIRTY_LIST(atom, level)); + + capture_list_init(ATOM_CLEAN_LIST(atom)); + capture_list_init(ATOM_OVRWR_LIST(atom)); + capture_list_init(ATOM_WB_LIST(atom)); + capture_list_init(&atom->inodes); + spin_atom_init(atom); + txnh_list_init(&atom->txnh_list); + atom_list_clean(atom); + fwaitfor_list_init(&atom->fwaitfor_list); + fwaiting_list_init(&atom->fwaiting_list); + prot_list_init(&atom->protected); + blocknr_set_init(&atom->delete_set); + blocknr_set_init(&atom->wandered_map); + + init_atom_fq_parts(atom); +} + +#if REISER4_DEBUG +/* Check if an atom is clean. */ +static int +atom_isclean(txn_atom * atom) +{ + int level; + + assert("umka-174", atom != NULL); + + for (level = 0; level < REAL_MAX_ZTREE_HEIGHT + 1; level += 1) { + if (!capture_list_empty(ATOM_DIRTY_LIST(atom, level))) { + return 0; + } + } + + return + atom->stage == ASTAGE_FREE && + atom->txnh_count == 0 && + atom->capture_count == 0 && + atomic_read(&atom->refcount) == 0 && + atom_list_is_clean(atom) && + txnh_list_empty(&atom->txnh_list) && + capture_list_empty(ATOM_CLEAN_LIST(atom)) && + capture_list_empty(ATOM_OVRWR_LIST(atom)) && + capture_list_empty(ATOM_WB_LIST(atom)) && + fwaitfor_list_empty(&atom->fwaitfor_list) && + fwaiting_list_empty(&atom->fwaiting_list) && + prot_list_empty(&atom->protected) && + atom_fq_parts_are_clean(atom); +} +#endif + +/* Begin a transaction in this context. Currently this uses the reiser4_context's + trans_in_ctx, which means that transaction handles are stack-allocated. Eventually + this will be extended to allow transaction handles to span several contexts. */ +/* Audited by: umka (2002.06.13) */ +reiser4_internal void +txn_begin(reiser4_context * context) +{ + assert("jmacd-544", context->trans == NULL); + + context->trans = &context->trans_in_ctx; + + /* FIXME_LATER_JMACD Currently there's no way to begin a TXN_READ_FUSING + transcrash. Default should be TXN_WRITE_FUSING. Also, the _trans variable is + stack allocated right now, but we would like to allow for dynamically allocated + transcrashes that span multiple system calls. + */ + txnh_init(context->trans, TXN_WRITE_FUSING); +} + +/* Finish a transaction handle context. */ +reiser4_internal long +txn_end(reiser4_context * context) +{ + long ret = 0; + txn_handle *txnh; + + assert("umka-283", context != NULL); + assert("nikita-3012", schedulable()); + + /* closing non top-level context---nothing to do */ + if (context != context->parent) + return 0; + + assert("nikita-2967", lock_stack_isclean(get_current_lock_stack())); + + txnh = context->trans; + + if (txnh != NULL) { + /* The txnh's field "atom" can be checked for NULL w/o holding a + lock because txnh->atom could be set by this thread's call to + try_capture or the deadlock prevention code in + fuse_not_fused_lock_owners(). But that code may assign an + atom to this transaction handle only if there are locked and + not yet fused nodes. It cannot happen because lock stack + should be clean at this moment. */ + if (txnh->atom != NULL) + ret = commit_txnh(txnh); + + assert("jmacd-633", txnh_isclean(txnh)); + + context->trans = NULL; + } + + return ret; +} + +reiser4_internal void +txn_restart(reiser4_context * context) +{ + txn_end(context); + preempt_point(); + txn_begin(context); +} + +reiser4_internal void +txn_restart_current(void) +{ + txn_restart(get_current_context()); +} + +/* TXN_ATOM */ + +/* Get the atom belonging to a txnh, which is not locked. Return txnh locked. Locks atom, if atom + is not NULL. This performs the necessary spin_trylock to break the lock-ordering cycle. May + return NULL. */ +reiser4_internal txn_atom * +txnh_get_atom(txn_handle * txnh) +{ + txn_atom *atom; + + assert("umka-180", txnh != NULL); + assert("jmacd-5108", spin_txnh_is_not_locked(txnh)); + + while (1) { + LOCK_TXNH(txnh); + atom = txnh->atom; + + if (atom == NULL) + break; + + if (spin_trylock_atom(atom)) + break; + + atomic_inc(&atom->refcount); + + UNLOCK_TXNH(txnh); + LOCK_ATOM(atom); + LOCK_TXNH(txnh); + + if (txnh->atom == atom) { + atomic_dec(&atom->refcount); + break; + } + + UNLOCK_TXNH(txnh); + atom_dec_and_unlock(atom); + } + + return atom; +} + +/* Get the current atom and spinlock it if current atom present. May return NULL */ +reiser4_internal txn_atom * +get_current_atom_locked_nocheck(void) +{ + reiser4_context *cx; + txn_atom *atom; + txn_handle *txnh; + + cx = get_current_context(); + assert("zam-437", cx != NULL); + + txnh = cx->trans; + assert("zam-435", txnh != NULL); + + atom = txnh_get_atom(txnh); + + UNLOCK_TXNH(txnh); + return atom; +} + +/* Get the atom belonging to a jnode, which is initially locked. Return with + both jnode and atom locked. This performs the necessary spin_trylock to + break the lock-ordering cycle. Assumes the jnode is already locked, and + returns NULL if atom is not set. */ +reiser4_internal txn_atom * +jnode_get_atom(jnode * node) +{ + txn_atom *atom; + + assert("umka-181", node != NULL); + + while (1) { + assert("jmacd-5108", spin_jnode_is_locked(node)); + + atom = node->atom; + /* node is not in any atom */ + if (atom == NULL) + break; + + /* If atom is not locked, grab the lock and return */ + if (spin_trylock_atom(atom)) + break; + + /* At least one jnode belongs to this atom it guarantees that + * atom->refcount > 0, we can safely increment refcount. */ + atomic_inc(&atom->refcount); + UNLOCK_JNODE(node); + + /* re-acquire spin locks in the right order */ + LOCK_ATOM(atom); + LOCK_JNODE(node); + + /* check if node still points to the same atom. */ + if (node->atom == atom) { + atomic_dec(&atom->refcount); + break; + } + + /* releasing of atom lock and reference requires not holding + * locks on jnodes. */ + UNLOCK_JNODE(node); + + /* We do not sure that this atom has extra references except our + * one, so we should call proper function which may free atom if + * last reference is released. */ + atom_dec_and_unlock(atom); + + /* lock jnode again for getting valid node->atom pointer + * value. */ + LOCK_JNODE(node); + } + + return atom; +} + +/* Returns true if @node is dirty and part of the same atom as one of its neighbors. Used + by flush code to indicate whether the next node (in some direction) is suitable for + flushing. */ +reiser4_internal int +same_slum_check(jnode * node, jnode * check, int alloc_check, int alloc_value) +{ + int compat; + txn_atom *atom; + + assert("umka-182", node != NULL); + assert("umka-183", check != NULL); + + /* Not sure what this function is supposed to do if supplied with @check that is + neither formatted nor unformatted (bitmap or so). */ + assert("nikita-2373", jnode_is_znode(check) || jnode_is_unformatted(check)); + + /* Need a lock on CHECK to get its atom and to check various state bits. + Don't need a lock on NODE once we get the atom lock. */ + /* It is not enough to lock two nodes and check (node->atom == + check->atom) because atom could be locked and being fused at that + moment, jnodes of the atom of that state (being fused) can point to + different objects, but the atom is the same.*/ + LOCK_JNODE(check); + + atom = jnode_get_atom(check); + + if (atom == NULL) { + compat = 0; + } else { + compat = (node->atom == atom && jnode_is_dirty(check)); + + if (compat && jnode_is_znode(check)) { + compat &= znode_is_connected(JZNODE(check)); + } + + if (compat && alloc_check) { + compat &= (alloc_value == jnode_is_flushprepped(check)); + } + + UNLOCK_ATOM(atom); + } + + UNLOCK_JNODE(check); + + return compat; +} + +/* Decrement the atom's reference count and if it falls to zero, free it. */ +reiser4_internal void +atom_dec_and_unlock(txn_atom * atom) +{ + txn_mgr *mgr = &get_super_private(reiser4_get_current_sb())->tmgr; + + assert("umka-186", atom != NULL); + assert("jmacd-1071", spin_atom_is_locked(atom)); + assert("zam-1039", atomic_read(&atom->refcount) > 0); + + if (atomic_dec_and_test(&atom->refcount)) { + /* take txnmgr lock and atom lock in proper order. */ + if (!spin_trylock_txnmgr(mgr)) { + /* This atom should exist after we re-acquire its + * spinlock, so we increment its reference counter. */ + atomic_inc(&atom->refcount); + UNLOCK_ATOM(atom); + spin_lock_txnmgr(mgr); + LOCK_ATOM(atom); + + if (!atomic_dec_and_test(&atom->refcount)) { + UNLOCK_ATOM(atom); + spin_unlock_txnmgr(mgr); + return; + } + } + assert("nikita-2656", spin_txnmgr_is_locked(mgr)); + atom_free(atom); + spin_unlock_txnmgr(mgr); + } else + UNLOCK_ATOM(atom); +} + +/* Return a new atom, locked. This adds the atom to the transaction manager's list and + sets its reference count to 1, an artificial reference which is kept until it + commits. We play strange games to avoid allocation under jnode & txnh spinlocks.*/ + +/* ZAM-FIXME-HANS: should we set node->atom and txnh->atom here also? */ +/* ANSWER(ZAM): there are special functions, capture_assign_txnh_nolock() and + capture_assign_block_nolock(), they are called right after calling + atom_begin_and_lock(). It could be done here, but, for understandability, it + is better to keep those calls inside try_capture_block main routine where all + assignments are made. */ +static txn_atom * +atom_begin_andlock(txn_atom ** atom_alloc, jnode * node, txn_handle * txnh) +{ + txn_atom *atom; + txn_mgr *mgr; + + assert("jmacd-43228", spin_jnode_is_locked(node)); + assert("jmacd-43227", spin_txnh_is_locked(txnh)); + assert("jmacd-43226", node->atom == NULL); + assert("jmacd-43225", txnh->atom == NULL); + + if (REISER4_DEBUG && rofs_jnode(node)) { + warning("nikita-3366", "Creating atom on rofs"); + dump_stack(); + } + + /* A memory allocation may schedule we have to release those spinlocks + * before kmem_cache_alloc() call. */ + UNLOCK_JNODE(node); + UNLOCK_TXNH(txnh); + + if (*atom_alloc == NULL) { + (*atom_alloc) = kmem_cache_alloc(_atom_slab, GFP_KERNEL); + + if (*atom_alloc == NULL) + return ERR_PTR(RETERR(-ENOMEM)); + } + + /* and, also, txnmgr spin lock should be taken before jnode and txnh + locks. */ + mgr = &get_super_private(reiser4_get_current_sb())->tmgr; + spin_lock_txnmgr(mgr); + + LOCK_JNODE(node); + LOCK_TXNH(txnh); + + /* Check if both atom pointers are still NULL... */ + if (node->atom != NULL || txnh->atom != NULL) { + ON_TRACE(TRACE_TXN, "alloc atom race\n"); + /* NOTE-NIKITA probably it is rather better to free + * atom_alloc here than thread it up to try_capture(). */ + + UNLOCK_TXNH(txnh); + UNLOCK_JNODE(node); + spin_unlock_txnmgr(mgr); + + reiser4_stat_inc(txnmgr.restart.atom_begin); + return ERR_PTR(-E_REPEAT); + } + + atom = *atom_alloc; + *atom_alloc = NULL; + + atom_init(atom); + + assert("jmacd-17", atom_isclean(atom)); + + /* Take the atom and txnmgr lock. No checks for lock ordering, because + @atom is new and inaccessible for others. */ + spin_lock_atom_no_ord(atom, 0, 0); + + atom_list_push_back(&mgr->atoms_list, atom); + atom->atom_id = mgr->id_count++; + mgr->atom_count += 1; + + /* Release txnmgr lock */ + spin_unlock_txnmgr(mgr); + + /* One reference until it commits. */ + atomic_inc(&atom->refcount); + + atom->stage = ASTAGE_CAPTURE_FUSE; + + ON_TRACE(TRACE_TXN, "begin atom %u\n", atom->atom_id); + + return atom; +} + +#if REISER4_DEBUG +/* Return true if an atom is currently "open". */ +static int atom_isopen(const txn_atom * atom) +{ + assert("umka-185", atom != NULL); + + return atom->stage > 0 && atom->stage < ASTAGE_PRE_COMMIT; +} +#endif + +/* Return the number of pointers to this atom that must be updated during fusion. This + approximates the amount of work to be done. Fusion chooses the atom with fewer + pointers to fuse into the atom with more pointers. */ +static int +atom_pointer_count(const txn_atom * atom) +{ + assert("umka-187", atom != NULL); + + /* This is a measure of the amount of work needed to fuse this atom + * into another. */ + return atom->txnh_count + atom->capture_count; +} + +/* Called holding the atom lock, this removes the atom from the transaction manager list + and frees it. */ +static void +atom_free(txn_atom * atom) +{ + txn_mgr *mgr = &get_super_private(reiser4_get_current_sb())->tmgr; + + assert("umka-188", atom != NULL); + + ON_TRACE(TRACE_TXN, "free atom %u\n", atom->atom_id); + + assert("jmacd-18", spin_atom_is_locked(atom)); + + /* Remove from the txn_mgr's atom list */ + assert("nikita-2657", spin_txnmgr_is_locked(mgr)); + mgr->atom_count -= 1; + atom_list_remove_clean(atom); + + /* Clean the atom */ + assert("jmacd-16", (atom->stage == ASTAGE_INVALID || atom->stage == ASTAGE_DONE)); + atom->stage = ASTAGE_FREE; + + blocknr_set_destroy(&atom->delete_set); + blocknr_set_destroy(&atom->wandered_map); + + assert("jmacd-16", atom_isclean(atom)); + + UNLOCK_ATOM(atom); + + kmem_cache_free(_atom_slab, atom); +} + +static int +atom_is_dotard(const txn_atom * atom) +{ + return time_after(jiffies, atom->start_time + + get_current_super_private()->tmgr.atom_max_age); +} + +static int atom_can_be_committed (txn_atom * atom) +{ + assert ("zam-884", spin_atom_is_locked(atom)); + assert ("zam-885", atom->txnh_count > atom->nr_waiters); + return atom->txnh_count == atom->nr_waiters + 1; +} + +/* Return true if an atom should commit now. This is determined by aging, atom + size or atom flags. */ +static int +atom_should_commit(const txn_atom * atom) +{ + assert("umka-189", atom != NULL); + return + (atom->flags & ATOM_FORCE_COMMIT) || + ((unsigned) atom_pointer_count(atom) > get_current_super_private()->tmgr.atom_max_size) || + atom_is_dotard(atom); +} + +/* return 1 if current atom exists and requires commit. */ +reiser4_internal int current_atom_should_commit(void) +{ + txn_atom * atom; + int result = 0; + + atom = get_current_atom_locked_nocheck(); + if (atom) { + result = atom_should_commit(atom); + UNLOCK_ATOM(atom); + } + return result; +} + +static int +atom_should_commit_asap(const txn_atom * atom) +{ + unsigned int captured; + unsigned int pinnedpages; + + assert("nikita-3309", atom != NULL); + + captured = (unsigned) atom->capture_count; + pinnedpages = (captured >> PAGE_CACHE_SHIFT) * sizeof(znode); + + return + (pinnedpages > (totalram_pages >> 3)) || + (atom->flushed > 100); +} + +static jnode * find_first_dirty_in_list (capture_list_head * head, int flags) +{ + jnode * first_dirty; + + for_all_type_safe_list(capture, head, first_dirty) { + if (!(flags & JNODE_FLUSH_COMMIT)) { + if ( + /* skip jnodes which have "heard banshee" */ + JF_ISSET(first_dirty, JNODE_HEARD_BANSHEE) || + /* and with active I/O */ + JF_ISSET(first_dirty, JNODE_WRITEBACK)) + continue; + } + return first_dirty; + } + return NULL; +} + +/* Get first dirty node from the atom's dirty_nodes[n] lists; return NULL if atom has no dirty + nodes on atom's lists */ +reiser4_internal jnode * find_first_dirty_jnode (txn_atom * atom, int flags) +{ + jnode *first_dirty; + tree_level level; + + assert("zam-753", spin_atom_is_locked(atom)); + + /* The flush starts from LEAF_LEVEL (=1). */ + for (level = 1; level < REAL_MAX_ZTREE_HEIGHT + 1; level += 1) { + if (capture_list_empty(ATOM_DIRTY_LIST(atom, level))) + continue; + + first_dirty = find_first_dirty_in_list(ATOM_DIRTY_LIST(atom, level), flags); + if (first_dirty) + return first_dirty; + } + + /* znode-above-root is on the list #0. */ + return find_first_dirty_in_list(ATOM_DIRTY_LIST(atom, 0), flags); +} + +#if REISER4_COPY_ON_CAPTURE + +/* this spin lock is used to prevent races during steal on capture. + FIXME: should be per filesystem or even per atom */ +spinlock_t scan_lock = SPIN_LOCK_UNLOCKED; + +/* Scan atom->writeback_nodes list and dispatch jnodes according to their state: + * move dirty and !writeback jnodes to @fq, clean jnodes to atom's clean + * list. */ +/* NOTE: doing that in end IO handler requires using of special spinlocks which + * disables interrupts in all places except IO handler. That is expensive. */ +static void dispatch_wb_list (txn_atom * atom, flush_queue_t * fq) +{ + jnode * cur; + int total, moved; + + assert("zam-905", spin_atom_is_locked(atom)); + + total = 0; + moved = 0; + + spin_lock(&scan_lock); + cur = capture_list_front(ATOM_WB_LIST(atom)); + while (!capture_list_end(ATOM_WB_LIST(atom), cur)) { + jnode * next; + + total ++; + JF_SET(cur, JNODE_SCANNED); + next = capture_list_next(cur); + if (!capture_list_end(ATOM_WB_LIST(atom), next)) + JF_SET(next, JNODE_SCANNED); + + spin_unlock(&scan_lock); + + LOCK_JNODE(cur); + assert("vs-1441", NODE_LIST(cur) == WB_LIST); + if (!JF_ISSET(cur, JNODE_WRITEBACK)) { + moved ++; + if (JF_ISSET(cur, JNODE_DIRTY)) { + queue_jnode(fq, cur); + } else { + /* move from writeback list to clean list */ + capture_list_remove(cur); + capture_list_push_back(ATOM_CLEAN_LIST(atom), cur); + ON_DEBUG(count_jnode(atom, cur, WB_LIST, CLEAN_LIST, 1)); + } + } + UNLOCK_JNODE(cur); + + spin_lock(&scan_lock); + JF_CLR(cur, JNODE_SCANNED); + cur = next; + assert("vs-1450", ergo(!capture_list_end(ATOM_WB_LIST(atom), cur), + JF_ISSET(cur, JNODE_SCANNED) && NODE_LIST(cur) == WB_LIST)); + } + spin_unlock(&scan_lock); +} + +#else + +static void dispatch_wb_list (txn_atom * atom, flush_queue_t * fq) +{ + jnode * cur; + + assert("zam-905", atom_is_protected(atom)); + + cur = capture_list_front(ATOM_WB_LIST(atom)); + while (!capture_list_end(ATOM_WB_LIST(atom), cur)) { + jnode * next = capture_list_next(cur); + + LOCK_JNODE(cur); + if (!JF_ISSET(cur, JNODE_WRITEBACK)) { + if (JF_ISSET(cur, JNODE_DIRTY)) { + queue_jnode(fq, cur); + } else { + capture_list_remove(cur); + capture_list_push_back(ATOM_CLEAN_LIST(atom), cur); + } + } + UNLOCK_JNODE(cur); + + cur = next; + } +} + +#endif + +/* Scan current atom->writeback_nodes list, re-submit dirty and !writeback + * jnodes to disk. */ +static int submit_wb_list (void) +{ + int ret; + flush_queue_t * fq; + + fq = get_fq_for_current_atom(); + if (IS_ERR(fq)) + return PTR_ERR(fq); + + dispatch_wb_list(fq->atom, fq); + UNLOCK_ATOM(fq->atom); + + /* trace_mark(flush); */ + write_current_logf(WRITE_IO_LOG, "mark=flush"); + + ret = write_fq(fq, NULL, 1); + fq_put(fq); + + return ret; +} + +#if 0 + +/* when during system call inode is "captured" (by reiser4_mark_inode_dirty) - blocks grabbed for stat data update are + moved to atom's flush_reserved bucket. On commit time (right before updating stat datas of all captured inodes) those + blocks are moved to grabbed. This function is used to calculate number of blocks reserved for stat data update when + those blocks get moved back and forwward between buckets of grabbed and flush_reserved blocks */ +static reiser4_block_nr reserved_for_sd_update(struct inode *inode) +{ + return inode_file_plugin(inode)->estimate.update(inode); +} + +static void atom_update_stat_data(txn_atom **atom) +{ + jnode *j; + + assert("vs-1241", spin_atom_is_locked(*atom)); + assert("vs-1616", capture_list_empty(&(*atom)->inodes)); + + while (!capture_list_empty(&(*atom)->inodes)) { + struct inode *inode; + + j = capture_list_front(&((*atom)->inodes)); + + inode = inode_by_reiser4_inode(container_of(j, reiser4_inode, inode_jnode)); + + /* move blocks grabbed for stat data update back from atom's + * flush_reserved to grabbed */ + flush_reserved2grabbed(*atom, reserved_for_sd_update(inode)); + + capture_list_remove_clean(j); + capture_list_push_back(ATOM_CLEAN_LIST(*atom), j); + UNLOCK_ATOM(*atom); + + /* FIXME: it is not clear what to do if update sd fails. A warning will be issued (nikita-2221) */ + reiser4_update_sd(inode); + *atom = get_current_atom_locked(); + } + + assert("vs-1231", capture_list_empty(&((*atom)->inodes))); +} +#endif + +/* Wait completion of all writes, re-submit atom writeback list if needed. */ +static int current_atom_complete_writes (void) +{ + int ret; + + /* Each jnode from that list was modified and dirtied when it had i/o + * request running already. After i/o completion we have to resubmit + * them to disk again.*/ + ret = submit_wb_list(); + if (ret < 0) + return ret; + + /* Wait all i/o completion */ + ret = current_atom_finish_all_fq(); + if (ret) + return ret; + + /* Scan wb list again; all i/o should be completed, we re-submit dirty + * nodes to disk */ + ret = submit_wb_list(); + if(ret < 0) + return ret; + + /* Wait all nodes we just submitted */ + return current_atom_finish_all_fq(); +} + +#define TOOMANYFLUSHES (1 << 13) + +/* Called with the atom locked and no open "active" transaction handlers except + ours, this function calls flush_current_atom() until all dirty nodes are + processed. Then it initiates commit processing. + + Called by the single remaining open "active" txnh, which is closing. Other + open txnhs belong to processes which wait atom commit in commit_txnh() + routine. They are counted as "waiters" in atom->nr_waiters. Therefore as + long as we hold the atom lock none of the jnodes can be captured and/or + locked. + + Return value is an error code if commit fails. +*/ +static int commit_current_atom (long *nr_submitted, txn_atom ** atom) +{ + reiser4_super_info_data * sbinfo = get_current_super_private (); + long ret; + /* how many times jnode_flush() was called as a part of attempt to + * commit this atom. */ + int flushiters; + + assert ("zam-888", atom != NULL && *atom != NULL); + assert ("zam-886", spin_atom_is_locked(*atom)); + assert ("zam-887", get_current_context()->trans->atom == *atom); + assert("jmacd-151", atom_isopen(*atom)); + + /* lock ordering: delete_sema and commit_sema are unordered */ + assert("nikita-3184", + get_current_super_private()->delete_sema_owner != current); + + ON_TRACE(TRACE_TXN, "atom %u trying to commit %u: CAPTURE_WAIT\n", + (*atom)->atom_id, current->pid); + + /* call reiser4_update_sd for all atom's inodes */ + /*atom_update_stat_data(atom);*/ + + for (flushiters = 0 ;; ++ flushiters) { + ret = flush_current_atom(JNODE_FLUSH_WRITE_BLOCKS | JNODE_FLUSH_COMMIT, nr_submitted, atom); + if (ret != -E_REPEAT) + break; + + /* if atom's dirty list contains one znode which is + HEARD_BANSHEE and is locked we have to allow lock owner to + continue and uncapture that znode */ + preempt_point(); + + *atom = get_current_atom_locked(); + if (flushiters > TOOMANYFLUSHES && IS_POW(flushiters)) { + warning("nikita-3176", + "Flushing like mad: %i", flushiters); + info_atom("atom", *atom); + DEBUGON(flushiters > (1 << 20)); + } + } + + if (ret) + return ret; + + assert ("zam-882", spin_atom_is_locked(*atom)); + + if (!atom_can_be_committed(*atom)) { + UNLOCK_ATOM(*atom); + reiser4_stat_inc(txnmgr.restart.cannot_commit); + return RETERR(-E_REPEAT); + } + + /* Up to this point we have been flushing and after flush is called we + return -E_REPEAT. Now we can commit. We cannot return -E_REPEAT + at this point, commit should be successful. */ + atom_set_stage(*atom, ASTAGE_PRE_COMMIT); + ON_DEBUG(((*atom)->committer = current)); + + ON_TRACE(TRACE_TXN, "commit atom %u: PRE_COMMIT\n", (*atom)->atom_id); + ON_TRACE(TRACE_FLUSH, "everything flushed atom %u: PRE_COMMIT\n", (*atom)->atom_id); + + UNLOCK_ATOM(*atom); + + ret = current_atom_complete_writes(); + if (ret) + return ret; + + assert ("zam-906", capture_list_empty(ATOM_WB_LIST(*atom))); + + ON_TRACE(TRACE_FLUSH, "everything written back atom %u\n", + (*atom)->atom_id); + + /* isolate critical code path which should be executed by only one + * thread using tmgr semaphore */ + down(&sbinfo->tmgr.commit_semaphore); + + ret = reiser4_write_logs(nr_submitted); + if (ret < 0) + reiser4_panic("zam-597", "write log failed (%ld)\n", ret); + + /* The atom->ovrwr_nodes list is processed under commit semaphore held + because of bitmap nodes which are captured by special way in + bitmap_pre_commit_hook(), that way does not include + capture_fuse_wait() as a capturing of other nodes does -- the commit + semaphore is used for transaction isolation instead. */ + invalidate_list(ATOM_OVRWR_LIST(*atom)); + up(&sbinfo->tmgr.commit_semaphore); + + invalidate_list(ATOM_CLEAN_LIST(*atom)); + invalidate_list(ATOM_WB_LIST(*atom)); + assert("zam-927", capture_list_empty(&(*atom)->inodes)); + + LOCK_ATOM(*atom); + atom_set_stage(*atom, ASTAGE_DONE); + ON_DEBUG((*atom)->committer = 0); + + /* Atom's state changes, so wake up everybody waiting for this + event. */ + wakeup_atom_waiting_list(*atom); + + /* Decrement the "until commit" reference, at least one txnh (the caller) is + still open. */ + atomic_dec(&(*atom)->refcount); + + assert("jmacd-1070", atomic_read(&(*atom)->refcount) > 0); + assert("jmacd-1062", (*atom)->capture_count == 0); + BUG_ON((*atom)->capture_count != 0); + assert("jmacd-1071", spin_atom_is_locked(*atom)); + + ON_TRACE(TRACE_TXN, "commit atom finished %u refcount %d\n", + (*atom)->atom_id, atomic_read(&(*atom)->refcount)); + return ret; +} + +/* TXN_TXNH */ + +/* commit current atom and wait commit completion; atom and txn_handle should be + * locked before call, this function unlocks them on exit. */ +static int force_commit_atom_nolock (txn_handle * txnh) +{ + txn_atom * atom; + + assert ("zam-837", txnh != NULL); + assert ("zam-835", spin_txnh_is_locked(txnh)); + assert ("nikita-2966", lock_stack_isclean(get_current_lock_stack())); + + atom = txnh->atom; + + assert ("zam-834", atom != NULL); + assert ("zam-836", spin_atom_is_locked(atom)); + + /* Set flags for atom and txnh: forcing atom commit and waiting for + * commit completion */ + txnh->flags |= TXNH_WAIT_COMMIT; + atom->flags |= ATOM_FORCE_COMMIT; + + UNLOCK_TXNH(txnh); + UNLOCK_ATOM(atom); + + txn_restart_current(); + return 0; +} + +/* externally visible function which takes all necessary locks and commits + * current atom */ +reiser4_internal int txnmgr_force_commit_current_atom (void) +{ + txn_handle * txnh = get_current_context()->trans; + txn_atom * atom; + + atom = txnh_get_atom(txnh); + + if (atom == NULL) { + UNLOCK_TXNH(txnh); + return 0; + } + + return force_commit_atom_nolock(txnh); +} + +/* Called to force commit of any outstanding atoms. @commit_all_atoms controls + * should we commit all atoms including new ones which are created after this + * functions is called. */ +reiser4_internal int +txnmgr_force_commit_all (struct super_block *super, int commit_all_atoms) +{ + int ret; + txn_atom *atom; + txn_mgr *mgr; + txn_handle *txnh; + unsigned long start_time = jiffies; + reiser4_context * ctx = get_current_context(); + + assert("nikita-2965", lock_stack_isclean(get_current_lock_stack())); + assert("nikita-3058", commit_check_locks()); + + txn_restart(ctx); + + mgr = &get_super_private(super)->tmgr; + + txnh = ctx->trans; + +again: + + spin_lock_txnmgr(mgr); + + for_all_type_safe_list(atom, &mgr->atoms_list, atom) { + LOCK_ATOM(atom); + + /* Commit any atom which can be committed. If @commit_new_atoms + * is not set we commit only atoms which were created before + * this call is started. */ + if (commit_all_atoms || time_before_eq(atom->start_time, start_time)) { + if (atom->stage <= ASTAGE_POST_COMMIT) { + spin_unlock_txnmgr(mgr); + + if (atom->stage < ASTAGE_PRE_COMMIT) { + LOCK_TXNH(txnh); + /* Add force-context txnh */ + capture_assign_txnh_nolock(atom, txnh); + ret = force_commit_atom_nolock(txnh); + if(ret) + return ret; + } else + /* wait atom commit */ + atom_wait_event(atom); + + goto again; + } + } + + UNLOCK_ATOM(atom); + } + +#if REISER4_DEBUG + if (commit_all_atoms) { + reiser4_super_info_data * sbinfo = get_super_private(super); + reiser4_spin_lock_sb(sbinfo); + assert("zam-813", sbinfo->blocks_fake_allocated_unformatted == 0); + assert("zam-812", sbinfo->blocks_fake_allocated == 0); + reiser4_spin_unlock_sb(sbinfo); + } +#endif + + spin_unlock_txnmgr(mgr); + + return 0; +} + +/* check whether commit_some_atoms() can commit @atom. Locking is up to the + * caller */ +static int atom_is_committable(txn_atom *atom) +{ + return + atom->stage < ASTAGE_PRE_COMMIT && + atom->txnh_count == atom->nr_waiters && + atom_should_commit(atom); +} + +/* called periodically from ktxnmgrd to commit old atoms. Releases ktxnmgrd spin + * lock at exit */ +reiser4_internal int +commit_some_atoms(txn_mgr * mgr) +{ + int ret = 0; + txn_atom *atom; + txn_atom *next_atom; + txn_handle *txnh; + reiser4_context *ctx; + + ctx = get_current_context(); + assert("nikita-2444", ctx != NULL); + + txnh = ctx->trans; + spin_lock_txnmgr(mgr); + + /* look for atom to commit */ + for_all_type_safe_list_safe(atom, &mgr->atoms_list, atom, next_atom) { + /* first test without taking atom spin lock, whether it is + * eligible for committing at all */ + if (atom_is_committable(atom)) { + /* now, take spin lock and re-check */ + LOCK_ATOM(atom); + if (atom_is_committable(atom)) + break; + UNLOCK_ATOM(atom); + } + } + + ret = atom_list_end(&mgr->atoms_list, atom); + spin_unlock_txnmgr(mgr); + + if (ret) { + /* nothing found */ + spin_unlock(&mgr->daemon->guard); + return 0; + } + + LOCK_TXNH(txnh); + + /* Set the atom to force committing */ + atom->flags |= ATOM_FORCE_COMMIT; + + /* Add force-context txnh */ + capture_assign_txnh_nolock(atom, txnh); + + UNLOCK_TXNH(txnh); + UNLOCK_ATOM(atom); + + /* we are about to release daemon spin lock, notify daemon it + has to rescan atoms */ + mgr->daemon->rescan = 1; + spin_unlock(&mgr->daemon->guard); + txn_restart(ctx); + return 0; +} + +/* Calls jnode_flush for current atom if it exists; if not, just take another + atom and call jnode_flush() for him. If current transaction handle has + already assigned atom (current atom) we have to close current transaction + prior to switch to another atom or do something with current atom. This + code tries to flush current atom. + + flush_some_atom() is called as part of memory clearing process. It is + invoked from balance_dirty_pages(), pdflushd, and entd. + + If we can flush no nodes, atom is committed, because this frees memory. + + If atom is too large or too old it is committed also. +*/ +reiser4_internal int +flush_some_atom(long *nr_submitted, const struct writeback_control *wbc, int flags) +{ + reiser4_context *ctx = get_current_context(); + txn_handle *txnh = ctx->trans; + txn_atom *atom; + int ret; + int ret1; + + assert("zam-1042", txnh != NULL); + repeat: + if (txnh->atom == NULL) { + /* current atom is available, take first from txnmgr */ + txn_mgr *tmgr = &get_super_private(ctx->super)->tmgr; + + spin_lock_txnmgr(tmgr); + + /* traverse the list of all atoms */ + for_all_type_safe_list(atom, &tmgr->atoms_list, atom) { + /* lock atom before checking its state */ + LOCK_ATOM(atom); + + /* we need an atom which is not being committed and which has no + * flushers (jnode_flush() add one flusher at the beginning and + * subtract one at the end). */ + if (atom->stage < ASTAGE_PRE_COMMIT && atom->nr_flushers == 0) { + LOCK_TXNH(txnh); + capture_assign_txnh_nolock(atom, txnh); + UNLOCK_TXNH(txnh); + + goto found; + } + + UNLOCK_ATOM(atom); + } + + /* Write throttling is case of no one atom can be + * flushed/committed. */ + if (!current_is_pdflush() && !wbc->nonblocking) { + for_all_type_safe_list(atom, &tmgr->atoms_list, atom) { + LOCK_ATOM(atom); + /* Repeat the check from the above. */ + if (atom->stage < ASTAGE_PRE_COMMIT && atom->nr_flushers == 0) { + LOCK_TXNH(txnh); + capture_assign_txnh_nolock(atom, txnh); + UNLOCK_TXNH(txnh); + + goto found; + } + if (atom->stage <= ASTAGE_POST_COMMIT) { + spin_unlock_txnmgr(tmgr); + /* we just wait until atom's flusher + makes a progress in flushing or + committing the atom */ + atom_wait_event(atom); + goto repeat; + } + UNLOCK_ATOM(atom); + } + } + spin_unlock_txnmgr(tmgr); + return 0; + found: + spin_unlock_txnmgr(tmgr); + } else + atom = get_current_atom_locked(); + + ret = flush_current_atom(flags, nr_submitted, &atom); + if (ret == 0) { + if (*nr_submitted == 0 || atom_should_commit_asap(atom)) { + /* if early flushing could not make more nodes clean, + * or atom is too old/large, + * we force current atom to commit */ + /* wait for commit completion but only if this + * wouldn't stall pdflushd and ent thread. */ + if (!wbc->nonblocking && !ctx->entd) + txnh->flags |= TXNH_WAIT_COMMIT; + atom->flags |= ATOM_FORCE_COMMIT; + } + UNLOCK_ATOM(atom); + } else if (ret == -E_REPEAT) { + if (*nr_submitted == 0) + goto repeat; + ret = 0; + } + + ret1 = txn_end(ctx); + assert("vs-1692", ret1 == 0); + if (ret1 > 0) + *nr_submitted += ret1; + txn_begin(ctx); + + return ret; +} + +#if REISER4_COPY_ON_CAPTURE + +/* Remove processed nodes from atom's clean list (thereby remove them from transaction). */ +void +invalidate_list(capture_list_head * head) +{ + txn_atom *atom; + + spin_lock(&scan_lock); + while (!capture_list_empty(head)) { + jnode *node; + + node = capture_list_front(head); + JF_SET(node, JNODE_SCANNED); + spin_unlock(&scan_lock); + + atom = node->atom; + LOCK_ATOM(atom); + LOCK_JNODE(node); + if (JF_ISSET(node, JNODE_CC) && node->pg) { + /* corresponding page_cache_get is in swap_jnode_pages */ + assert("vs-1448", test_and_clear_bit(PG_arch_1, &node->pg->flags)); + page_cache_release(node->pg); + } + uncapture_block(node); + UNLOCK_ATOM(atom); + JF_CLR(node, JNODE_SCANNED); + jput(node); + + spin_lock(&scan_lock); + } + spin_unlock(&scan_lock); +} + +#else + +/* Remove processed nodes from atom's clean list (thereby remove them from transaction). */ +void +invalidate_list(capture_list_head * head) +{ + while (!capture_list_empty(head)) { + jnode *node; + + node = capture_list_front(head); + LOCK_JNODE(node); + uncapture_block(node); + jput(node); + } +} + +#endif + +static void +init_wlinks(txn_wait_links * wlinks) +{ + wlinks->_lock_stack = get_current_lock_stack(); + fwaitfor_list_clean(wlinks); + fwaiting_list_clean(wlinks); + wlinks->waitfor_cb = NULL; + wlinks->waiting_cb = NULL; +} + +/* Add atom to the atom's waitfor list and wait for somebody to wake us up; */ +reiser4_internal void atom_wait_event(txn_atom * atom) +{ + txn_wait_links _wlinks; + + assert("zam-744", spin_atom_is_locked(atom)); + assert("nikita-3156", + lock_stack_isclean(get_current_lock_stack()) || + atom->nr_running_queues > 0); + + init_wlinks(&_wlinks); + fwaitfor_list_push_back(&atom->fwaitfor_list, &_wlinks); + atomic_inc(&atom->refcount); + UNLOCK_ATOM(atom); + + /* assert("nikita-3056", commit_check_locks()); */ + prepare_to_sleep(_wlinks._lock_stack); + go_to_sleep(_wlinks._lock_stack, ADD_TO_SLEPT_IN_WAIT_EVENT); + + LOCK_ATOM (atom); + fwaitfor_list_remove(&_wlinks); + atom_dec_and_unlock (atom); +} + +reiser4_internal void +atom_set_stage(txn_atom *atom, txn_stage stage) +{ + assert("nikita-3535", atom != NULL); + assert("nikita-3538", spin_atom_is_locked(atom)); + assert("nikita-3536", ASTAGE_FREE <= stage && stage <= ASTAGE_INVALID); + /* Excelsior! */ + assert("nikita-3537", stage >= atom->stage); + if (atom->stage != stage) { + atom->stage = stage; + atom_send_event(atom); + } +} + +/* wake all threads which wait for an event */ +reiser4_internal void +atom_send_event(txn_atom * atom) +{ + assert("zam-745", spin_atom_is_locked(atom)); + wakeup_atom_waitfor_list(atom); +} + +/* Informs txn manager code that owner of this txn_handle should wait atom commit completion (for + example, because it does fsync(2)) */ +static int +should_wait_commit(txn_handle * h) +{ + return h->flags & TXNH_WAIT_COMMIT; +} + +typedef struct commit_data { + txn_atom *atom; + txn_handle *txnh; + long nr_written; + /* as an optimization we start committing atom by first trying to + * flush it few times without switching into ASTAGE_CAPTURE_WAIT. This + * allows to reduce stalls due to other threads waiting for atom in + * ASTAGE_CAPTURE_WAIT stage. ->preflush is counter of these + * preliminary flushes. */ + int preflush; + /* have we waited on atom. */ + int wait; + int failed; + int wake_ktxnmgrd_up; +} commit_data; + +/* + * Called from commit_txnh() repeatedly, until either error happens, or atom + * commits successfully. + */ +static int +try_commit_txnh(commit_data *cd) +{ + int result; + + assert("nikita-2968", lock_stack_isclean(get_current_lock_stack())); + + /* Get the atom and txnh locked. */ + cd->atom = txnh_get_atom(cd->txnh); + assert("jmacd-309", cd->atom != NULL); + UNLOCK_TXNH(cd->txnh); + + if (cd->wait) { + cd->atom->nr_waiters --; + cd->wait = 0; + } + + if (cd->atom->stage == ASTAGE_DONE) + return 0; + + ON_TRACE(TRACE_TXN, + "commit_txnh: atom %u failed %u; txnh_count %u; should_commit %u\n", + cd->atom->atom_id, cd->failed, cd->atom->txnh_count, + atom_should_commit(cd->atom)); + + if (cd->failed) + return 0; + + if (atom_should_commit(cd->atom)) { + /* if atom is _very_ large schedule it for common as soon as + * possible. */ + if (atom_should_commit_asap(cd->atom)) { + /* + * When atom is in PRE_COMMIT or later stage following + * invariant (encoded in atom_can_be_committed()) + * holds: there is exactly one non-waiter transaction + * handle opened on this atom. When thread wants to + * wait until atom commits (for example sync()) it + * waits on atom event after increasing + * atom->nr_waiters (see blow in this function). It + * cannot be guaranteed that atom is already committed + * after receiving event, so loop has to be + * re-started. But if atom switched into PRE_COMMIT + * stage and became too large, we cannot change its + * state back to CAPTURE_WAIT (atom stage can only + * increase monotonically), hence this check. + */ + if (cd->atom->stage < ASTAGE_CAPTURE_WAIT) + atom_set_stage(cd->atom, ASTAGE_CAPTURE_WAIT); + cd->atom->flags |= ATOM_FORCE_COMMIT; + } + if (cd->txnh->flags & TXNH_DONT_COMMIT) { + /* + * this thread (transaction handle that is) doesn't + * want to commit atom. Notify waiters that handle is + * closed. This can happen, for example, when we are + * under VFS directory lock and don't want to commit + * atom right now to avoid stalling other threads + * working in the same directory. + */ + + /* Wake the ktxnmgrd up if the ktxnmgrd is needed to + * commit this atom: no atom waiters and only one + * (our) open transaction handle. */ + cd->wake_ktxnmgrd_up = + cd->atom->txnh_count == 1 && + cd->atom->nr_waiters == 0; + atom_send_event(cd->atom); + result = 0; + } else if (!atom_can_be_committed(cd->atom)) { + if (should_wait_commit(cd->txnh)) { + /* sync(): wait for commit */ + cd->atom->nr_waiters++; + cd->wait = 1; + atom_wait_event(cd->atom); + reiser4_stat_inc(txnmgr.restart.should_wait); + result = RETERR(-E_REPEAT); + } else { + result = 0; + } + } else if (cd->preflush > 0 && !is_current_ktxnmgrd()) { + /* + * optimization: flush atom without switching it into + * ASTAGE_CAPTURE_WAIT. + * + * But don't do this for ktxnmgrd, because ktxnmgrd + * should never block on atom fusion. + */ + result = flush_current_atom(JNODE_FLUSH_WRITE_BLOCKS, + &cd->nr_written, &cd->atom); + if (result == 0) { + UNLOCK_ATOM(cd->atom); + cd->preflush = 0; + reiser4_stat_inc(txnmgr.restart.flush); + result = RETERR(-E_REPEAT); + } else /* Atoms wasn't flushed + * completely. Rinse. Repeat. */ + -- cd->preflush; + } else { + /* We change atom state to ASTAGE_CAPTURE_WAIT to + prevent atom fusion and count ourself as an active + flusher */ + atom_set_stage(cd->atom, ASTAGE_CAPTURE_WAIT); + cd->atom->flags |= ATOM_FORCE_COMMIT; + + result = commit_current_atom(&cd->nr_written, &cd->atom); + if (result != 0 && result != -E_REPEAT) + cd->failed = 1; + } + } else + result = 0; + + assert("jmacd-1027", ergo(result == 0, spin_atom_is_locked(cd->atom))); + /* perfectly valid assertion, except that when atom/txnh is not locked + * fusion can take place, and cd->atom points nowhere. */ + /* + assert("jmacd-1028", ergo(result != 0, spin_atom_is_not_locked(cd->atom))); + */ + return result; +} + +/* Called to commit a transaction handle. This decrements the atom's number of open + handles and if it is the last handle to commit and the atom should commit, initiates + atom commit. if commit does not fail, return number of written blocks */ +static long +commit_txnh(txn_handle * txnh) +{ + commit_data cd; + assert("umka-192", txnh != NULL); + + xmemset(&cd, 0, sizeof cd); + cd.txnh = txnh; + cd.preflush = 10; + + /* calls try_commit_txnh() until either atom commits, or error + * happens */ + while (try_commit_txnh(&cd) != 0) + preempt_point(); + + assert("nikita-3171", spin_txnh_is_not_locked(txnh)); + LOCK_TXNH(txnh); + + cd.atom->txnh_count -= 1; + txnh->atom = NULL; + + txnh_list_remove(txnh); + + ON_TRACE(TRACE_TXN, "close txnh atom %u refcount %d\n", + cd.atom->atom_id, atomic_read(&cd.atom->refcount)); + + UNLOCK_TXNH(txnh); + atom_dec_and_unlock(cd.atom); + /* if we don't want to do a commit (TXNH_DONT_COMMIT is set, probably + * because it takes time) by current thread, we do that work + * asynchronously by ktxnmgrd daemon. */ + if (cd.wake_ktxnmgrd_up) + ktxnmgrd_kick(&get_current_super_private()->tmgr); + + return 0; +} + +/* TRY_CAPTURE */ + +/* This routine attempts a single block-capture request. It may return -E_REPEAT if some + condition indicates that the request should be retried, and it may block if the + txn_capture mode does not include the TXN_CAPTURE_NONBLOCKING request flag. + + This routine encodes the basic logic of block capturing described by: + + http://namesys.com/v4/v4.html + + Our goal here is to ensure that any two blocks that contain dependent modifications + should commit at the same time. This function enforces this discipline by initiating + fusion whenever a transaction handle belonging to one atom requests to read or write a + block belonging to another atom (TXN_CAPTURE_WRITE or TXN_CAPTURE_READ_ATOMIC). + + In addition, this routine handles the initial assignment of atoms to blocks and + transaction handles. These are possible outcomes of this function: + + 1. The block and handle are already part of the same atom: return immediate success + + 2. The block is assigned but the handle is not: call capture_assign_txnh to assign + the handle to the block's atom. + + 3. The handle is assigned but the block is not: call capture_assign_block to assign + the block to the handle's atom. + + 4. Both handle and block are assigned, but to different atoms: call capture_init_fusion + to fuse atoms. + + 5. Neither block nor handle are assigned: create a new atom and assign them both. + + 6. A read request for a non-captured block: return immediate success. + + This function acquires and releases the handle's spinlock. This function is called + under the jnode lock and if the return value is 0, it returns with the jnode lock still + held. If the return is -E_REPEAT or some other error condition, the jnode lock is + released. The external interface (try_capture) manages re-aquiring the jnode lock + in the failure case. +*/ + +static int +try_capture_block(txn_handle * txnh, jnode * node, txn_capture mode, txn_atom ** atom_alloc, int can_coc) +{ + int ret; + txn_atom *block_atom; + txn_atom *txnh_atom; + + /* Should not call capture for READ_NONCOM requests, handled in try_capture. */ + assert("jmacd-567", CAPTURE_TYPE(mode) != TXN_CAPTURE_READ_NONCOM); + + /* FIXME-ZAM-HANS: FIXME_LATER_JMACD Should assert that atom->tree == node->tree somewhere. */ + + assert("umka-194", txnh != NULL); + assert("umka-195", node != NULL); + + /* The jnode is already locked! Being called from try_capture(). */ + assert("jmacd-567", spin_jnode_is_locked(node)); + + block_atom = node->atom; + + /* Get txnh spinlock, this allows us to compare txn_atom pointers but it doesn't + let us touch the atoms themselves. */ + LOCK_TXNH(txnh); + + txnh_atom = txnh->atom; + + if (REISER4_STATS) { + if (block_atom != NULL && txnh_atom != NULL) + if (block_atom == txnh_atom) + reiser4_stat_inc(txnmgr.capture_equal); + else + reiser4_stat_inc(txnmgr.capture_both); + else if (block_atom != NULL && txnh_atom == NULL) + reiser4_stat_inc(txnmgr.capture_block); + else if (block_atom == NULL && txnh_atom != NULL) + reiser4_stat_inc(txnmgr.capture_txnh); + else + reiser4_stat_inc(txnmgr.capture_none); + } + + if (txnh_atom != NULL && block_atom == txnh_atom) { + UNLOCK_TXNH(txnh); + return 0; + } + /* NIKITA-HANS: nothing */ + if (txnh_atom != NULL) { + /* It is time to perform deadlock prevention check over the + node we want to capture. It is possible this node was + locked for read without capturing it. The optimization + which allows to do it helps us in keeping atoms independent + as long as possible but it may cause lock/fuse deadlock + problems. + + A number of similar deadlock situations with locked but not + captured nodes were found. In each situation there are two + or more threads: one of them does flushing while another + one does routine balancing or tree lookup. The flushing + thread (F) sleeps in long term locking request for node + (N), another thread (A) sleeps in trying to capture some + node already belonging the atom F, F has a state which + prevents immediately fusion . + + Deadlocks of this kind cannot happen if node N was properly + captured by thread A. The F thread fuse atoms before + locking therefore current atom of thread F and current atom + of thread A became the same atom and thread A may proceed. + This does not work if node N was not captured because the + fusion of atom does not happens. + + The following scheme solves the deadlock: If + longterm_lock_znode locks and does not capture a znode, + that znode is marked as MISSED_IN_CAPTURE. A node marked + this way is processed by the code below which restores the + missed capture and fuses current atoms of all the node lock + owners by calling the fuse_not_fused_lock_owners() + function. + */ + + if ( // txnh_atom->stage >= ASTAGE_CAPTURE_WAIT && + jnode_is_znode(node) && znode_is_locked(JZNODE(node)) + && JF_ISSET(node, JNODE_MISSED_IN_CAPTURE)) { + JF_CLR(node, JNODE_MISSED_IN_CAPTURE); + + ret = fuse_not_fused_lock_owners(txnh, JZNODE(node)); + + if (ret) { + JF_SET(node, JNODE_MISSED_IN_CAPTURE); + + assert("zam-687", spin_txnh_is_not_locked(txnh)); + assert("zam-688", spin_jnode_is_not_locked(node)); + + return ret; + } + + assert("zam-701", spin_txnh_is_locked(txnh)); + assert("zam-702", spin_jnode_is_locked(node)); + } + } + + if (block_atom != NULL) { + /* The block has already been assigned to an atom. */ + + /* case (block_atom == txnh_atom) is already handled above */ + if (txnh_atom == NULL) { + + /* The txnh is unassigned, try to assign it. */ + ret = capture_assign_txnh(node, txnh, mode, can_coc); + if (ret != 0) { + /* E_REPEAT or otherwise */ + assert("jmacd-6129", spin_txnh_is_not_locked(txnh)); + assert("jmacd-6130", spin_jnode_is_not_locked(node)); + return ret; + } + + /* Either the txnh is now assigned to the block's atom or the read-request was + granted because the block is committing. Locks still held. */ + } else { + if (mode & TXN_CAPTURE_DONT_FUSE) { + UNLOCK_TXNH(txnh); + UNLOCK_JNODE(node); + /* we are in a "no-fusion" mode and @node is + * already part of transaction. */ + return RETERR(-E_NO_NEIGHBOR); + } + /* In this case, both txnh and node belong to different atoms. This function + returns -E_REPEAT on successful fusion, 0 on the fall-through case. */ + ret = capture_init_fusion(node, txnh, mode, can_coc); + if (ret != 0) { + assert("jmacd-6131", spin_txnh_is_not_locked(txnh)); + assert("jmacd-6132", spin_jnode_is_not_locked(node)); + return ret; + } + + /* The fall-through case is read request for committing block. Locks still + held. */ + } + + } else if ((mode & TXN_CAPTURE_WTYPES) != 0) { + + /* In this case, the page is unlocked and the txnh wishes exclusive access. */ + + if (txnh_atom != NULL) { + /* The txnh is already assigned: add the page to its atom. */ + ret = capture_assign_block(txnh, node); + if (ret != 0) { + /* E_REPEAT or otherwise */ + assert("jmacd-6133", spin_txnh_is_not_locked(txnh)); + assert("jmacd-6134", spin_jnode_is_not_locked(node)); + return ret; + } + + /* Success: Locks are still held. */ + + } else { + + /* In this case, neither txnh nor page are assigned to an atom. */ + block_atom = atom_begin_andlock(atom_alloc, node, txnh); + + if (!IS_ERR(block_atom)) { + /* Assign both, release atom lock. */ + assert("jmacd-125", block_atom->stage == ASTAGE_CAPTURE_FUSE); + + capture_assign_txnh_nolock(block_atom, txnh); + capture_assign_block_nolock(block_atom, node); + + UNLOCK_ATOM(block_atom); + } else { + /* all locks are released already */ + return PTR_ERR(block_atom); + } + + /* Success: Locks are still held. */ + } + + } else { + /* The jnode is uncaptured and its a read request -- fine. */ + assert("jmacd-411", CAPTURE_TYPE(mode) == TXN_CAPTURE_READ_ATOMIC); + } + + /* Successful case: both jnode and txnh are still locked. */ + assert("jmacd-740", spin_txnh_is_locked(txnh)); + assert("jmacd-741", spin_jnode_is_locked(node)); + + /* Release txnh lock, return with the jnode still locked. */ + UNLOCK_TXNH(txnh); + + return 0; +} + +reiser4_internal txn_capture +build_capture_mode(jnode * node, znode_lock_mode lock_mode, txn_capture flags) +{ + txn_capture cap_mode; + + assert("nikita-3187", spin_jnode_is_locked(node)); + + /* FIXME_JMACD No way to set TXN_CAPTURE_READ_MODIFY yet. */ + + if (lock_mode == ZNODE_WRITE_LOCK) { + cap_mode = TXN_CAPTURE_WRITE; + } else if (node->atom != NULL) { + cap_mode = TXN_CAPTURE_WRITE; + } else if (0 && /* txnh->mode == TXN_READ_FUSING && */ + jnode_get_level(node) == LEAF_LEVEL) { + /* NOTE-NIKITA TXN_READ_FUSING is not currently used */ + /* We only need a READ_FUSING capture at the leaf level. This + is because the internal levels of the tree (twigs included) + are redundant from the point of the user that asked for a + read-fusing transcrash. The user only wants to read-fuse + atoms due to reading uncommitted data that another user has + written. It is the file system that reads/writes the + internal tree levels, the user only reads/writes leaves. */ + cap_mode = TXN_CAPTURE_READ_ATOMIC; + } else { + /* In this case (read lock at a non-leaf) there's no reason to + * capture. */ + /* cap_mode = TXN_CAPTURE_READ_NONCOM; */ + + /* Mark this node as "MISSED". It helps in further deadlock + * analysis */ + JF_SET(node, JNODE_MISSED_IN_CAPTURE); + return 0; + } + + cap_mode |= (flags & (TXN_CAPTURE_NONBLOCKING | + TXN_CAPTURE_DONT_FUSE)); + assert("nikita-3186", cap_mode != 0); + return cap_mode; +} + +/* This is an external interface to try_capture_block(), it calls + try_capture_block() repeatedly as long as -E_REPEAT is returned. + + @node: node to capture, + @lock_mode: read or write lock is used in capture mode calculation, + @flags: see txn_capture flags enumeration, + @can_coc : can copy-on-capture + + @return: 0 - node was successfully captured, -E_REPEAT - capture request + cannot be processed immediately as it was requested in flags, + < 0 - other errors. +*/ +reiser4_internal int +try_capture(jnode * node, znode_lock_mode lock_mode, + txn_capture flags, int can_coc) +{ + txn_atom *atom_alloc = NULL; + txn_capture cap_mode; + txn_handle * txnh = get_current_context()->trans; +#if REISER4_COPY_ON_CAPTURE + int coc_enabled = 1; +#endif + int ret; + + assert("jmacd-604", spin_jnode_is_locked(node)); + +repeat: + cap_mode = build_capture_mode(node, lock_mode, flags); + if (cap_mode == 0) + return 0; + + /* Repeat try_capture as long as -E_REPEAT is returned. */ +#if REISER4_COPY_ON_CAPTURE + ret = try_capture_block(txnh, node, cap_mode, &atom_alloc, can_coc && coc_enabled); + coc_enabled = 1; +#else + ret = try_capture_block(txnh, node, cap_mode, &atom_alloc, can_coc); +#endif + /* Regardless of non_blocking: + + If ret == 0 then jnode is still locked. + If ret != 0 then jnode is unlocked. + */ + assert("nikita-2674", ergo(ret == 0, spin_jnode_is_locked(node))); + assert("nikita-2675", ergo(ret != 0, spin_jnode_is_not_locked(node))); + + assert("nikita-2974", spin_txnh_is_not_locked(txnh)); + + if (ret == -E_REPEAT) { + /* E_REPEAT implies all locks were released, therefore we need + to take the jnode's lock again. */ + LOCK_JNODE(node); + + /* Although this may appear to be a busy loop, it is not. + There are several conditions that cause E_REPEAT to be + returned by the call to try_capture_block, all cases + indicating some kind of state change that means you should + retry the request and will get a different result. In some + cases this could be avoided with some extra code, but + generally it is done because the necessary locks were + released as a result of the operation and repeating is the + simplest thing to do (less bug potential). The cases are: + atom fusion returns E_REPEAT after it completes (jnode and + txnh were unlocked); race conditions in assign_block, + assign_txnh, and init_fusion return E_REPEAT (trylock + failure); after going to sleep in capture_fuse_wait + (request was blocked but may now succeed). I'm not quite + sure how capture_copy works yet, but it may also return + E_REPEAT. When the request is legitimately blocked, the + requestor goes to sleep in fuse_wait, so this is not a busy + loop. */ + /* NOTE-NIKITA: still don't understand: + + try_capture_block->capture_assign_txnh->spin_trylock_atom->E_REPEAT + + looks like busy loop? + */ + goto repeat; + } + +#if REISER4_COPY_ON_CAPTURE + if (ret == -E_WAIT) { + reiser4_stat_inc(coc.coc_wait); + /* disable COC for the next loop iteration */ + coc_enabled = 0; + LOCK_JNODE(node); + goto repeat; + } +#endif + + /* free extra atom object that was possibly allocated by + try_capture_block(). + + Do this before acquiring jnode spin lock to + minimize time spent under lock. --nikita */ + if (atom_alloc != NULL) { + kmem_cache_free(_atom_slab, atom_alloc); + } + + if (ret != 0) { + if (ret == -E_BLOCK) { + assert("nikita-3360", cap_mode & TXN_CAPTURE_NONBLOCKING); + ret = -E_REPEAT; + } + + /* Failure means jnode is not locked. FIXME_LATER_JMACD May + want to fix the above code to avoid releasing the lock and + re-acquiring it, but there are cases were failure occurs + when the lock is not held, and those cases would need to be + modified to re-take the lock. */ + LOCK_JNODE(node); + } + + /* Jnode is still locked. */ + assert("jmacd-760", spin_jnode_is_locked(node)); + return ret; +} + +/* This function sets up a call to try_capture_block and repeats as long as -E_REPEAT is + returned by that routine. The txn_capture request mode is computed here depending on + the transaction handle's type and the lock request. This is called from the depths of + the lock manager with the jnode lock held and it always returns with the jnode lock + held. +*/ + +/* fuse all 'active' atoms of lock owners of given node. */ +static int +fuse_not_fused_lock_owners(txn_handle * txnh, znode * node) +{ + lock_handle *lh; + int repeat = 0; + txn_atom *atomh = txnh->atom; + +/* assert ("zam-689", znode_is_rlocked (node));*/ + assert("zam-690", spin_znode_is_locked(node)); + assert("zam-691", spin_txnh_is_locked(txnh)); + assert("zam-692", atomh != NULL); + + RLOCK_ZLOCK(&node->lock); + + if (!spin_trylock_atom(atomh)) { + repeat = 1; + goto fail; + } + + /* inspect list of lock owners */ + for_all_type_safe_list(owners, &node->lock.owners, lh) { + reiser4_context *ctx; + txn_atom *atomf; + + ctx = get_context_by_lock_stack(lh->owner); + + if (ctx == get_current_context()) + continue; + + if (!spin_trylock_txnh(ctx->trans)) { + repeat = 1; + continue; + } + + atomf = ctx->trans->atom; + + if (atomf == NULL) { + capture_assign_txnh_nolock(atomh, ctx->trans); + UNLOCK_TXNH(ctx->trans); + + reiser4_wake_up(lh->owner); + continue; + } + + if (atomf == atomh) { + UNLOCK_TXNH(ctx->trans); + continue; + } + + if (!spin_trylock_atom(atomf)) { + UNLOCK_TXNH(ctx->trans); + repeat = 1; + continue; + } + + UNLOCK_TXNH(ctx->trans); + + if (atomf == atomh || atomf->stage > ASTAGE_CAPTURE_WAIT) { + UNLOCK_ATOM(atomf); + continue; + } + // repeat = 1; + + reiser4_wake_up(lh->owner); + + UNLOCK_TXNH(txnh); + RUNLOCK_ZLOCK(&node->lock); + spin_unlock_znode(node); + + /* @atomf is "small" and @atomh is "large", by + definition. Small atom is destroyed and large is unlocked + inside capture_fuse_into() + */ + capture_fuse_into(atomf, atomh); + + reiser4_stat_inc(txnmgr.restart.fuse_lock_owners_fused); + return RETERR(-E_REPEAT); + } + + UNLOCK_ATOM(atomh); + + if (repeat) { +fail: + UNLOCK_TXNH(txnh); + RUNLOCK_ZLOCK(&node->lock); + spin_unlock_znode(node); + reiser4_stat_inc(txnmgr.restart.fuse_lock_owners); + return RETERR(-E_REPEAT); + } + + RUNLOCK_ZLOCK(&node->lock); + return 0; +} + +/* This is the interface to capture unformatted nodes via their struct page + reference. Currently it is only used in reiser4_invalidatepage */ +reiser4_internal int +try_capture_page_to_invalidate(struct page *pg) +{ + int ret; + jnode *node; + + assert("umka-292", pg != NULL); + assert("nikita-2597", PageLocked(pg)); + + if (IS_ERR(node = jnode_of_page(pg))) { + return PTR_ERR(node); + } + + LOCK_JNODE(node); + unlock_page(pg); + + ret = try_capture(node, ZNODE_WRITE_LOCK, 0, 0/* no copy on capture */); + UNLOCK_JNODE(node); + jput(node); + lock_page(pg); + return ret; +} + +/* This informs the transaction manager when a node is deleted. Add the block to the + atom's delete set and uncapture the block. + +VS-FIXME-HANS: this E_REPEAT paradigm clutters the code and creates a need for +explanations. find all the functions that use it, and unless there is some very +good reason to use it (I have not noticed one so far and I doubt it exists, but maybe somewhere somehow....), +move the loop to inside the function. + +VS-FIXME-HANS: can this code be at all streamlined? In particular, can you lock and unlock the jnode fewer times? + */ +reiser4_internal void +uncapture_page(struct page *pg) +{ + jnode *node; + txn_atom *atom; + + assert("umka-199", pg != NULL); + assert("nikita-3155", PageLocked(pg)); + + reiser4_clear_page_dirty(pg); + + reiser4_wait_page_writeback(pg); + + node = (jnode *) (pg->private); + if (node == NULL) + return; + + LOCK_JNODE(node); + + eflush_del(node, 1/* page is locked */); + /*assert ("zam-815", !JF_ISSET(node, JNODE_EFLUSH));*/ + + atom = jnode_get_atom(node); + if (atom == NULL) { + assert("jmacd-7111", !jnode_is_dirty(node)); + UNLOCK_JNODE (node); + return; + } + + /* We can remove jnode from transaction even if it is on flush queue + * prepped list, we only need to be sure that flush queue is not being + * written by write_fq(). write_fq() does not use atom spin lock for + * protection of the prepped nodes list, instead write_fq() increments + * atom's nr_running_queues counters for the time when prepped list is + * not protected by spin lock. Here we check this counter if we want + * to remove jnode from flush queue and, if the counter is not zero, + * wait all write_fq() for this atom to complete. This is not + * significant overhead. */ + while (JF_ISSET(node, JNODE_FLUSH_QUEUED) && atom->nr_running_queues) { + UNLOCK_JNODE(node); + /* + * at this moment we want to wait for "atom event", viz. wait + * until @node can be removed from flush queue. But + * atom_wait_event() cannot be called with page locked, because + * it deadlocks with jnode_extent_write(). Unlock page, after + * making sure (through page_cache_get()) that it cannot be + * released from memory. + */ + page_cache_get(pg); + unlock_page(pg); + atom_wait_event(atom); + lock_page(pg); + /* + * page may has been detached by ->writepage()->releasepage(). + */ + reiser4_wait_page_writeback(pg); + LOCK_JNODE(node); + eflush_del(node, 1); + page_cache_release(pg); + atom = jnode_get_atom(node); +/* VS-FIXME-HANS: improve the commenting in this function */ + if (atom == NULL) { + UNLOCK_JNODE(node); + return; + } + } + uncapture_block(node); + UNLOCK_ATOM(atom); + jput(node); +} + +/* this is used in extent's kill hook to uncapture and unhash jnodes attached to inode's tree of jnodes */ +reiser4_internal void +uncapture_jnode(jnode *node) +{ + txn_atom *atom; + + assert("vs-1462", spin_jnode_is_locked(node)); + assert("", node->pg == 0); + + eflush_del(node, 0); + /*jnode_make_clean(node);*/ + atom = jnode_get_atom(node); + if (atom == NULL) { + assert("jmacd-7111", !jnode_is_dirty(node)); + UNLOCK_JNODE (node); + return; + } + + uncapture_block(node); + UNLOCK_ATOM(atom); + jput(node); +} + +/* No-locking version of assign_txnh. Sets the transaction handle's atom pointer, + increases atom refcount and txnh_count, adds to txnh_list. */ +static void +capture_assign_txnh_nolock(txn_atom * atom, txn_handle * txnh) +{ + assert("umka-200", atom != NULL); + assert("umka-201", txnh != NULL); + + assert("jmacd-822", spin_txnh_is_locked(txnh)); + assert("jmacd-823", spin_atom_is_locked(atom)); + assert("jmacd-824", txnh->atom == NULL); + assert("nikita-3540", atom_isopen(atom)); + + atomic_inc(&atom->refcount); + + ON_TRACE(TRACE_TXN, "assign txnh atom %u refcount %d\n", atom->atom_id, atomic_read(&atom->refcount)); + + txnh->atom = atom; + txnh_list_push_back(&atom->txnh_list, txnh); + atom->txnh_count += 1; +} + +/* No-locking version of assign_block. Sets the block's atom pointer, references the + block, adds it to the clean or dirty capture_jnode list, increments capture_count. */ +static void +capture_assign_block_nolock(txn_atom * atom, jnode * node) +{ + assert("umka-202", atom != NULL); + assert("umka-203", node != NULL); + assert("jmacd-321", spin_jnode_is_locked(node)); + assert("umka-295", spin_atom_is_locked(atom)); + assert("jmacd-323", node->atom == NULL); + BUG_ON(!capture_list_is_clean(node)); + assert("nikita-3470", !jnode_is_dirty(node)); + + /* Pointer from jnode to atom is not counted in atom->refcount. */ + node->atom = atom; + + capture_list_push_back(ATOM_CLEAN_LIST(atom), node); + atom->capture_count += 1; + /* reference to jnode is acquired by atom. */ + jref(node); + + ON_DEBUG(count_jnode(atom, node, NOT_CAPTURED, CLEAN_LIST, 1)); + + LOCK_CNT_INC(t_refs); + + ON_TRACE(TRACE_TXN, "capture %p for atom %u (captured %u)\n", node, atom->atom_id, atom->capture_count); +} + +#if REISER4_COPY_ON_CAPTURE +static void +set_cced_bit(jnode *node) +{ + BUG_ON(JF_ISSET(node, JNODE_CCED)); + JF_SET(node, JNODE_CCED); +} +#endif + +static void +clear_cced_bits(jnode *node) +{ + JF_CLR(node, JNODE_CCED); +} + +int +is_cced(const jnode *node) +{ + return JF_ISSET(node, JNODE_CCED); +} + +/* common code for dirtying both unformatted jnodes and formatted znodes. */ +static void +do_jnode_make_dirty(jnode * node, txn_atom * atom) +{ + assert("zam-748", spin_jnode_is_locked(node)); + assert("zam-750", spin_atom_is_locked(atom)); + assert("jmacd-3981", !jnode_is_dirty(node)); + + JF_SET(node, JNODE_DIRTY); + + get_current_context()->nr_marked_dirty ++; + + /* We grab2flush_reserve one additional block only if node was + not CREATED and jnode_flush did not sort it into neither + relocate set nor overwrite one. If node is in overwrite or + relocate set we assume that atom's flush reserved counter was + already adjusted. */ + if (!JF_ISSET(node, JNODE_CREATED) && !JF_ISSET(node, JNODE_RELOC) + && !JF_ISSET(node, JNODE_OVRWR) && jnode_is_leaf(node) + && !jnode_is_cluster_page(node)) { + assert("vs-1093", !blocknr_is_fake(&node->blocknr)); + assert("vs-1506", *jnode_get_block(node) != 0); + grabbed2flush_reserved_nolock(atom, (__u64)1); + JF_SET(node, JNODE_FLUSH_RESERVED); + } + + if (!JF_ISSET(node, JNODE_FLUSH_QUEUED)) { + /* If the atom is not set yet, it will be added to the appropriate list in + capture_assign_block_nolock. */ + /* Sometimes a node is set dirty before being captured -- the case for new + jnodes. In that case the jnode will be added to the appropriate list + in capture_assign_block_nolock. Another reason not to re-link jnode is + that jnode is on a flush queue (see flush.c for details) */ + + int level = jnode_get_level(node); + + assert("nikita-3152", !JF_ISSET(node, JNODE_OVRWR)); + assert("zam-654", atom->stage < ASTAGE_PRE_COMMIT); + assert("nikita-2607", 0 <= level); + assert("nikita-2606", level <= REAL_MAX_ZTREE_HEIGHT); + + capture_list_remove(node); + capture_list_push_back(ATOM_DIRTY_LIST(atom, level), node); + ON_DEBUG(count_jnode(atom, node, NODE_LIST(node), DIRTY_LIST, 1)); + + /* + * JNODE_CCED bit protects clean copy (page created by + * copy-on-capture) from being evicted from the memory. This + * is necessary, because otherwise jload() would load obsolete + * disk block (up-to-date original is still in memory). But + * once jnode is dirtied, it cannot be released without + * storing its content on the disk, so protection is no longer + * necessary. + */ + clear_cced_bits(node); + } +} + +/* Set the dirty status for this (spin locked) jnode. */ +reiser4_internal void +jnode_make_dirty_locked(jnode * node) +{ + assert("umka-204", node != NULL); + assert("zam-7481", spin_jnode_is_locked(node)); + + if (REISER4_DEBUG && rofs_jnode(node)) { + warning("nikita-3365", "Dirtying jnode on rofs"); + dump_stack(); + } + + /* Fast check for already dirty node */ + if (!jnode_is_dirty(node)) { + txn_atom * atom; + + atom = jnode_get_atom (node); + assert("vs-1094", atom); + /* Check jnode dirty status again because node spin lock might + * be released inside jnode_get_atom(). */ + if (likely(!jnode_is_dirty(node))) + do_jnode_make_dirty(node, atom); + UNLOCK_ATOM (atom); + } +} + +/* Set the dirty status for this znode. */ +reiser4_internal void +znode_make_dirty(znode * z) +{ + jnode *node; + struct page *page; + + assert("umka-204", z != NULL); + assert("nikita-3290", znode_above_root(z) || znode_is_loaded(z)); + assert("nikita-3291", !ZF_ISSET(z, JNODE_EFLUSH)); + assert("nikita-3560", znode_is_write_locked(z)); + + node = ZJNODE(z); + + LOCK_JNODE(node); + jnode_make_dirty_locked(node); + page = jnode_page(node); + if (page != NULL) { + /* this is useful assertion (allows one to check that no + * modifications are lost due to update of in-flight page), + * but it requires locking on page to check PG_writeback + * bit. */ + /* assert("nikita-3292", + !PageWriteback(page) || ZF_ISSET(z, JNODE_WRITEBACK)); */ + page_cache_get(page); + ON_DEBUG_MODIFY(znode_set_checksum(ZJNODE(z), 1)); + /* jnode lock is not needed for the rest of + * znode_set_dirty(). */ + UNLOCK_JNODE(node); + /* reiser4 file write code calls set_page_dirty for + * unformatted nodes, for formatted nodes we do it here. */ + set_page_dirty_internal(page, 0); + page_cache_release(page); + /* bump version counter in znode */ + z->version = znode_build_version(jnode_get_tree(node)); + } else { + assert("zam-596", znode_above_root(JZNODE(node))); + UNLOCK_JNODE(node); + } + + assert("nikita-1900", znode_is_write_locked(z)); + assert("jmacd-9777", node->atom != NULL); +} + +reiser4_internal int +sync_atom(txn_atom *atom) +{ + int result; + txn_handle *txnh; + + txnh = get_current_context()->trans; + + result = 0; + if (atom != NULL) { + if (atom->stage < ASTAGE_PRE_COMMIT) { + LOCK_TXNH(txnh); + capture_assign_txnh_nolock(atom, txnh); + result = force_commit_atom_nolock(txnh); + } else if (atom->stage < ASTAGE_POST_COMMIT) { + /* wait atom commit */ + atom_wait_event(atom); + /* try once more */ + result = RETERR(-E_REPEAT); + } else + UNLOCK_ATOM(atom); + } + return result; +} + +#if REISER4_DEBUG + +void check_fq(const txn_atom *atom); + +/* move jnode form one list to another + call this after atom->capture_count is updated */ +void +count_jnode(txn_atom *atom, jnode *node, atom_list old_list, atom_list new_list, int check_lists) +{ +#if REISER4_COPY_ON_CAPTURE + assert("", spin_atom_is_locked(atom)); +#else + assert("zam-1018", atom_is_protected(atom)); +#endif + assert("", spin_jnode_is_locked(node)); + assert("", NODE_LIST(node) == old_list); + + switch(NODE_LIST(node)) { + case NOT_CAPTURED: + break; + case DIRTY_LIST: + assert("", atom->dirty > 0); + atom->dirty --; + break; + case CLEAN_LIST: + assert("", atom->clean > 0); + atom->clean --; + break; + case FQ_LIST: + assert("", atom->fq > 0); + atom->fq --; + break; + case WB_LIST: + assert("", atom->wb > 0); + atom->wb --; + break; + case OVRWR_LIST: + assert("", atom->ovrwr > 0); + atom->ovrwr --; + break; + case PROTECT_LIST: + /* protect list is an intermediate atom's list to which jnodes + get put from dirty list before disk space is allocated for + them. From this list jnodes can either go to flush queue list + or back to dirty list */ + assert("", atom->protect > 0); + assert("", new_list == FQ_LIST || new_list == DIRTY_LIST); + atom->protect --; + break; + default: + impossible("", ""); + } + + switch(new_list) { + case NOT_CAPTURED: + break; + case DIRTY_LIST: + atom->dirty ++; + break; + case CLEAN_LIST: + atom->clean ++; + break; + case FQ_LIST: + atom->fq ++; + break; + case WB_LIST: + atom->wb ++; + break; + case OVRWR_LIST: + atom->ovrwr ++; + break; + case PROTECT_LIST: + assert("", old_list == DIRTY_LIST); + atom->protect ++; + break; + default: + impossible("", ""); + } + ASSIGN_NODE_LIST(node, new_list); + if (0 && check_lists) { + int count; + tree_level level; + jnode *node; + + count = 0; + + /* flush queue list */ + /*check_fq(atom);*/ + + /* dirty list */ + count = 0; + for (level = 0; level < REAL_MAX_ZTREE_HEIGHT + 1; level += 1) { + for_all_type_safe_list(capture, ATOM_DIRTY_LIST(atom, level), node) + count ++; + } + if (count != atom->dirty) + warning("", "dirty counter %d, real %d\n", atom->dirty, count); + + /* clean list */ + count = 0; + for_all_type_safe_list(capture, ATOM_CLEAN_LIST(atom), node) + count ++; + if (count != atom->clean) + warning("", "clean counter %d, real %d\n", atom->clean, count); + + /* wb list */ + count = 0; + for_all_type_safe_list(capture, ATOM_WB_LIST(atom), node) + count ++; + if (count != atom->wb) + warning("", "wb counter %d, real %d\n", atom->wb, count); + + /* overwrite list */ + count = 0; + for_all_type_safe_list(capture, ATOM_OVRWR_LIST(atom), node) + count ++; + + if (count != atom->ovrwr) + warning("", "ovrwr counter %d, real %d\n", atom->ovrwr, count); + } + assert("vs-1624", atom->num_queued == atom->fq); + if (atom->capture_count != atom->dirty + atom->clean + atom->ovrwr + atom->wb + atom->fq + atom->protect) { + printk("count %d, dirty %d clean %d ovrwr %d wb %d fq %d protect %d\n", atom->capture_count, atom->dirty, atom->clean, atom->ovrwr, atom->wb, atom->fq, atom->protect); + assert("vs-1622", + atom->capture_count == atom->dirty + atom->clean + atom->ovrwr + atom->wb + atom->fq + atom->protect); + } +} + +#endif + +/* Make node OVRWR and put it on atom->overwrite_nodes list, atom lock and jnode + * lock should be taken before calling this function. */ +reiser4_internal void jnode_make_wander_nolock (jnode * node) +{ + txn_atom * atom; + + assert("nikita-2431", node != NULL); + assert("nikita-2432", !JF_ISSET(node, JNODE_RELOC)); + assert("nikita-3153", jnode_is_dirty(node)); + assert("zam-897", !JF_ISSET(node, JNODE_FLUSH_QUEUED)); + assert("nikita-3367", !blocknr_is_fake(jnode_get_block(node))); + + atom = node->atom; + + assert("zam-895", atom != NULL); + assert("zam-894", atom_is_protected(atom)); + + JF_SET(node, JNODE_OVRWR); + capture_list_remove_clean(node); + capture_list_push_back(ATOM_OVRWR_LIST(atom), node); + /*XXXX*/ON_DEBUG(count_jnode(atom, node, DIRTY_LIST, OVRWR_LIST, 1)); +} + +/* Same as jnode_make_wander_nolock, but all necessary locks are taken inside + * this function. */ +reiser4_internal void jnode_make_wander (jnode * node) +{ + txn_atom * atom; + + LOCK_JNODE(node); + atom = jnode_get_atom(node); + assert ("zam-913", atom != NULL); + assert ("zam-914", !JF_ISSET(node, JNODE_RELOC)); + + jnode_make_wander_nolock(node); + UNLOCK_ATOM(atom); + UNLOCK_JNODE(node); +} + +/* this just sets RELOC bit */ +static void +jnode_make_reloc_nolock(flush_queue_t *fq, jnode *node) +{ + assert("vs-1480", spin_jnode_is_locked(node)); + assert ("zam-916", jnode_is_dirty(node)); + assert ("zam-917", !JF_ISSET(node, JNODE_RELOC)); + assert ("zam-918", !JF_ISSET(node, JNODE_OVRWR)); + assert ("zam-920", !JF_ISSET(node, JNODE_FLUSH_QUEUED)); + assert ("nikita-3367", !blocknr_is_fake(jnode_get_block(node))); + + jnode_set_reloc(node); +} + +/* Make znode RELOC and put it on flush queue */ +reiser4_internal void znode_make_reloc (znode *z, flush_queue_t * fq) +{ + jnode *node; + txn_atom * atom; + + node = ZJNODE(z); + LOCK_JNODE(node); + + atom = jnode_get_atom(node); + assert ("zam-919", atom != NULL); + + jnode_make_reloc_nolock(fq, node); + queue_jnode(fq, node); + + UNLOCK_ATOM(atom); + UNLOCK_JNODE(node); + +} + +/* Make unformatted node RELOC and put it on flush queue */ +reiser4_internal void +unformatted_make_reloc(jnode *node, flush_queue_t * fq) +{ + assert("vs-1479", jnode_is_unformatted(node)); + + jnode_make_reloc_nolock(fq, node); + mark_jnode_queued(fq, node); +} + +static int +trylock_wait(txn_atom *atom, txn_handle * txnh, jnode * node) +{ + if (unlikely(!spin_trylock_atom(atom))) { + atomic_inc(&atom->refcount); + + UNLOCK_JNODE(node); + UNLOCK_TXNH(txnh); + + LOCK_ATOM(atom); + /* caller should eliminate extra reference by calling + * atom_dec_and_unlock() for this atom. */ + return 1; + } else + return 0; +} + +/* + * in transaction manager jnode spin lock and transaction handle spin lock + * nest within atom spin lock. During capturing we are in a situation when + * jnode and transaction handle spin locks are held and we want to manipulate + * atom's data (capture lists, and txnh list) to add node and/or handle to the + * atom. Releasing jnode (or txnh) spin lock at this point is unsafe, because + * concurrent fusion can render assumption made by capture so far (about + * ->atom pointers in jnode and txnh) invalid. Initial code used try-lock and + * if atom was busy returned -E_REPEAT to the top level. This can lead to the + * busy loop if atom is locked for long enough time. Function below tries to + * throttle this loop. + * + */ +/* ZAM-FIXME-HANS: how feasible would it be to use our hi-lo priority locking + mechanisms/code for this as well? Does that make any sense? */ +/* ANSWER(Zam): I am not sure that I understand you proposal right, but the idea + might be in inventing spin_lock_lopri() which should be a complex loop with + "release lock" messages check like we have in the znode locking. I think we + should not substitute spin locks by more complex busy loops. Once it was + done that way in try_capture_block() where spin lock waiting was spread in a + busy loop through several functions. The proper solution should be in + making spin lock contention rare. */ +static int +trylock_throttle(txn_atom *atom, txn_handle * txnh, jnode * node) +{ + assert("nikita-3224", atom != NULL); + assert("nikita-3225", txnh != NULL); + assert("nikita-3226", node != NULL); + + assert("nikita-3227", spin_txnh_is_locked(txnh)); + assert("nikita-3229", spin_jnode_is_locked(node)); + + if (unlikely(trylock_wait(atom, txnh, node) != 0)) { + atom_dec_and_unlock(atom); + reiser4_stat_inc(txnmgr.restart.trylock_throttle); + return RETERR(-E_REPEAT); + } else + return 0; +} + +/* This function assigns a block to an atom, but first it must obtain the atom lock. If + the atom lock is busy, it returns -E_REPEAT to avoid deadlock with a fusing atom. Since + the transaction handle is currently open, we know the atom must also be open. */ +static int +capture_assign_block(txn_handle * txnh, jnode * node) +{ + txn_atom *atom; + int result; + + assert("umka-206", txnh != NULL); + assert("umka-207", node != NULL); + + atom = txnh->atom; + + assert("umka-297", atom != NULL); + + result = trylock_throttle(atom, txnh, node); + if (result != 0) { + /* this avoid busy loop, but we return -E_REPEAT anyway to + * simplify things. */ + reiser4_stat_inc(txnmgr.restart.assign_block); + return result; + } else { + assert("jmacd-19", atom_isopen(atom)); + + /* Add page to capture list. */ + capture_assign_block_nolock(atom, node); + + /* Success holds onto jnode & txnh locks. Unlock atom. */ + UNLOCK_ATOM(atom); + return 0; + } +} + +/* This function assigns a handle to an atom, but first it must obtain the atom lock. If + the atom is busy, it returns -E_REPEAT to avoid deadlock with a fusing atom. Unlike + capture_assign_block, the atom may be closed but we cannot know this until the atom is + locked. If the atom is closed and the request is to read, it is as if the block is + unmodified and the request is satisified without actually assigning the transaction + handle. If the atom is closed and the handle requests to write the block, then + initiate copy-on-capture. +*/ +static int +capture_assign_txnh(jnode * node, txn_handle * txnh, txn_capture mode, int can_coc) +{ + txn_atom *atom; + + assert("umka-208", node != NULL); + assert("umka-209", txnh != NULL); + + atom = node->atom; + + assert("umka-298", atom != NULL); + + /* + * optimization: this code went through three evolution stages. Main + * driving force of evolution here is lock ordering: + * + * at the entry to this function following pre-conditions are met: + * + * 1. txnh and node are both spin locked, + * + * 2. node belongs to atom, and + * + * 3. txnh don't. + * + * What we want to do here is to acquire spin lock on node's atom and + * modify it somehow depending on its ->stage. In the simplest case, + * where ->stage is ASTAGE_CAPTURE_FUSE, txnh should be added to + * atom's list. Problem is that atom spin lock nests outside of jnode + * and transaction handle ones. So, we cannot just LOCK_ATOM here. + * + * Solutions tried here: + * + * 1. spin_trylock(atom), return -E_REPEAT on failure. + * + * 2. spin_trylock(atom). On failure to acquire lock, increment + * atom->refcount, release all locks, and spin on atom lock. Then + * decrement ->refcount, unlock atom and return -E_REPEAT. + * + * 3. like previous one, but before unlocking atom, re-acquire + * spin locks on node and txnh and re-check whether function + * pre-condition are still met. Continue boldly if they are. + * + */ + if (trylock_wait(atom, txnh, node) != 0) { + LOCK_JNODE(node); + LOCK_TXNH(txnh); + /* NOTE-NIKITA is it at all possible that current txnh + * spontaneously changes ->atom from NULL to non-NULL? */ + if (node->atom == NULL || + txnh->atom != NULL || atom != node->atom) { + /* something changed. Caller have to re-decide */ + UNLOCK_TXNH(txnh); + UNLOCK_JNODE(node); + atom_dec_and_unlock(atom); + reiser4_stat_inc(txnmgr.restart.assign_txnh); + return RETERR(-E_REPEAT); + } else { + /* atom still has a jnode on its list (node->atom == + * atom), it means atom is not fused or finished + * (committed), we can safely decrement its refcount + * because it is not a last reference. */ + atomic_dec(&atom->refcount); + assert("zam-990", atomic_read(&atom->refcount) > 0); + } + } + + if (atom->stage == ASTAGE_CAPTURE_WAIT && + (atom->txnh_count != 0 || + atom_should_commit(atom) || atom_should_commit_asap(atom))) { + /* We don't fuse with the atom in ASTAGE_CAPTURE_WAIT only if + * there is open transaction handler. It makes sense: those + * atoms should not wait ktxnmgrd to flush and commit them. + * And, it solves deadlocks with loop back devices (reiser4 over + * loopback over reiser4), when ktxnmrgd is busy committing one + * atom (above the loop back device) and can't flush an atom + * below the loopback. */ + + /* The atom could be blocking requests--this is the first chance we've had + to test it. Since this txnh is not yet assigned, the fuse_wait logic + is not to avoid deadlock, its just waiting. Releases all three locks + and returns E_REPEAT. */ + + return capture_fuse_wait(node, txnh, atom, NULL, mode); + + } else if (atom->stage > ASTAGE_CAPTURE_WAIT) { + + /* The block is involved with a committing atom. */ + if (CAPTURE_TYPE(mode) == TXN_CAPTURE_READ_ATOMIC) { + + /* A read request for a committing block can be satisfied w/o + COPY-ON-CAPTURE. */ + + /* Success holds onto the jnode & txnh lock. Continue to unlock + atom below. */ + + } else { + + /* Perform COPY-ON-CAPTURE. Copy and try again. This function + releases all three locks. */ + return capture_copy(node, txnh, atom, NULL, mode, can_coc); + } + + } else { + + assert("jmacd-160", atom->stage == ASTAGE_CAPTURE_FUSE || + (atom->stage == ASTAGE_CAPTURE_WAIT && atom->txnh_count == 0)); + + /* Add txnh to active list. */ + capture_assign_txnh_nolock(atom, txnh); + + /* Success holds onto the jnode & txnh lock. Continue to unlock atom + below. */ + } + + /* Unlock the atom */ + UNLOCK_ATOM(atom); + return 0; +} + +reiser4_internal int +capture_super_block(struct super_block *s) +{ + int result; + znode *uber; + lock_handle lh; + + init_lh(&lh); + result = get_uber_znode(get_tree(s), + ZNODE_WRITE_LOCK, ZNODE_LOCK_LOPRI, &lh); + if (result) + return result; + + uber = lh.node; + /* Grabbing one block for superblock */ + result = reiser4_grab_space_force((__u64)1, BA_RESERVED); + if (result != 0) + return result; + + znode_make_dirty(uber); + + done_lh(&lh); + return 0; +} + +/* Wakeup every handle on the atom's WAITFOR list */ +static void +wakeup_atom_waitfor_list(txn_atom * atom) +{ + txn_wait_links *wlinks; + + assert("umka-210", atom != NULL); + + /* atom is locked */ + for_all_type_safe_list(fwaitfor, &atom->fwaitfor_list, wlinks) { + if (wlinks->waitfor_cb == NULL || + wlinks->waitfor_cb(atom, wlinks)) + /* Wake up. */ + reiser4_wake_up(wlinks->_lock_stack); + } +} + +/* Wakeup every handle on the atom's WAITING list */ +static void +wakeup_atom_waiting_list(txn_atom * atom) +{ + txn_wait_links *wlinks; + + assert("umka-211", atom != NULL); + + /* atom is locked */ + for_all_type_safe_list(fwaiting, &atom->fwaiting_list, wlinks) { + if (wlinks->waiting_cb == NULL || + wlinks->waiting_cb(atom, wlinks)) + /* Wake up. */ + reiser4_wake_up(wlinks->_lock_stack); + } +} + +/* helper function used by capture_fuse_wait() to avoid "spurious wake-ups" */ +static int wait_for_fusion(txn_atom * atom, txn_wait_links * wlinks) +{ + assert("nikita-3330", atom != NULL); + assert("nikita-3331", spin_atom_is_locked(atom)); + + + /* atom->txnh_count == 1 is for waking waiters up if we are releasing + * last transaction handle. */ + return atom->stage != ASTAGE_CAPTURE_WAIT || atom->txnh_count == 1; +} + +/* The general purpose of this function is to wait on the first of two possible events. + The situation is that a handle (and its atom atomh) is blocked trying to capture a + block (i.e., node) but the node's atom (atomf) is in the CAPTURE_WAIT state. The + handle's atom (atomh) is not in the CAPTURE_WAIT state. However, atomh could fuse with + another atom or, due to age, enter the CAPTURE_WAIT state itself, at which point it + needs to unblock the handle to avoid deadlock. When the txnh is unblocked it will + proceed and fuse the two atoms in the CAPTURE_WAIT state. + + In other words, if either atomh or atomf change state, the handle will be awakened, + thus there are two lists per atom: WAITING and WAITFOR. + + This is also called by capture_assign_txnh with (atomh == NULL) to wait for atomf to + close but it is not assigned to an atom of its own. + + Lock ordering in this method: all four locks are held: JNODE_LOCK, TXNH_LOCK, + BOTH_ATOM_LOCKS. Result: all four locks are released. +*/ +static int +capture_fuse_wait(jnode * node, txn_handle * txnh, txn_atom * atomf, txn_atom * atomh, txn_capture mode) +{ + int ret; + + /* Initialize the waiting list links. */ + txn_wait_links wlinks; + + assert("umka-212", node != NULL); + assert("umka-213", txnh != NULL); + assert("umka-214", atomf != NULL); + + /* We do not need the node lock. */ + UNLOCK_JNODE(node); + + if ((mode & TXN_CAPTURE_NONBLOCKING) != 0) { + UNLOCK_TXNH(txnh); + UNLOCK_ATOM(atomf); + + if (atomh) { + UNLOCK_ATOM(atomh); + } + + ON_TRACE(TRACE_TXN, "thread %u nonblocking on atom %u\n", current->pid, atomf->atom_id); + + reiser4_stat_inc(txnmgr.restart.fuse_wait_nonblock); + return RETERR(-E_BLOCK); + } + + init_wlinks(&wlinks); + + /* Add txnh to atomf's waitfor list, unlock atomf. */ + fwaitfor_list_push_back(&atomf->fwaitfor_list, &wlinks); + wlinks.waitfor_cb = wait_for_fusion; + atomic_inc(&atomf->refcount); + UNLOCK_ATOM(atomf); + + if (atomh) { + /* Add txnh to atomh's waiting list, unlock atomh. */ + fwaiting_list_push_back(&atomh->fwaiting_list, &wlinks); + atomic_inc(&atomh->refcount); + UNLOCK_ATOM(atomh); + } + + ON_TRACE(TRACE_TXN, "thread %u waitfor %u waiting %u\n", current->pid, + atomf->atom_id, atomh ? atomh->atom_id : 0); + + /* Go to sleep. */ + UNLOCK_TXNH(txnh); + + ret = prepare_to_sleep(wlinks._lock_stack); + if (ret != 0) { + ON_TRACE(TRACE_TXN, "thread %u deadlock blocking on atom %u\n", current->pid, atomf->atom_id); + } else { + go_to_sleep(wlinks._lock_stack, ADD_TO_SLEPT_IN_WAIT_ATOM); + + reiser4_stat_inc(txnmgr.restart.fuse_wait_slept); + ret = RETERR(-E_REPEAT); + ON_TRACE(TRACE_TXN, "thread %u wakeup %u waiting %u\n", + current->pid, atomf->atom_id, atomh ? atomh->atom_id : 0); + } + + /* Remove from the waitfor list. */ + LOCK_ATOM(atomf); + fwaitfor_list_remove(&wlinks); + atom_dec_and_unlock(atomf); + + if (atomh) { + /* Remove from the waiting list. */ + LOCK_ATOM(atomh); + fwaiting_list_remove(&wlinks); + atom_dec_and_unlock(atomh); + } + + assert("nikita-2186", ergo(ret, spin_jnode_is_not_locked(node))); + return ret; +} + +static inline int +capture_init_fusion_locked(jnode * node, txn_handle * txnh, txn_capture mode, int can_coc) +{ + txn_atom *atomf; + txn_atom *atomh; + + assert("umka-216", txnh != NULL); + assert("umka-217", node != NULL); + + atomh = txnh->atom; + atomf = node->atom; + + /* The txnh atom must still be open (since the txnh is active)... the node atom may + be in some later stage (checked next). */ + assert("jmacd-20", atom_isopen(atomh)); + + /* If the node atom is in the FUSE_WAIT state then we should wait, except to + avoid deadlock we still must fuse if the txnh atom is also in FUSE_WAIT. */ + if (atomf->stage == ASTAGE_CAPTURE_WAIT && + atomh->stage != ASTAGE_CAPTURE_WAIT && + (atomf->txnh_count != 0 || + atom_should_commit(atomf) || atom_should_commit_asap(atomf))) { + /* see comment in capture_assign_txnh() about the + * "atomf->txnh_count != 0" condition. */ + /* This unlocks all four locks and returns E_REPEAT. */ + return capture_fuse_wait(node, txnh, atomf, atomh, mode); + + } else if (atomf->stage > ASTAGE_CAPTURE_WAIT) { + + /* The block is involved with a comitting atom. */ + if (CAPTURE_TYPE(mode) == TXN_CAPTURE_READ_ATOMIC) { + /* A read request for a committing block can be satisfied w/o + COPY-ON-CAPTURE. Success holds onto the jnode & txnh + locks. */ + UNLOCK_ATOM(atomf); + UNLOCK_ATOM(atomh); + return 0; + } else { + /* Perform COPY-ON-CAPTURE. Copy and try again. This function + releases all four locks. */ + return capture_copy(node, txnh, atomf, atomh, mode, can_coc); + } + } + + /* Because atomf's stage <= CAPTURE_WAIT */ + assert("jmacd-175", atom_isopen(atomf)); + + /* If we got here its either because the atomh is in CAPTURE_WAIT or because the + atomf is not in CAPTURE_WAIT. */ + assert("jmacd-176", (atomh->stage == ASTAGE_CAPTURE_WAIT || atomf->stage != ASTAGE_CAPTURE_WAIT) || atomf->txnh_count == 0); + + /* Now release the txnh lock: only holding the atoms at this point. */ + UNLOCK_TXNH(txnh); + UNLOCK_JNODE(node); + + /* Decide which should be kept and which should be merged. */ + if (atom_pointer_count(atomf) < atom_pointer_count(atomh)) { + capture_fuse_into(atomf, atomh); + } else { + capture_fuse_into(atomh, atomf); + } + + /* Atoms are unlocked in capture_fuse_into. No locks held. */ + reiser4_stat_inc(txnmgr.restart.init_fusion_fused); + return RETERR(-E_REPEAT); +} + +/* Perform the necessary work to prepare for fusing two atoms, which involves + * acquiring two atom locks in the proper order. If one of the node's atom is + * blocking fusion (i.e., it is in the CAPTURE_WAIT stage) and the handle's + * atom is not then the handle's request is put to sleep. If the node's atom + * is committing, then the node can be copy-on-captured. Otherwise, pick the + * atom with fewer pointers to be fused into the atom with more pointer and + * call capture_fuse_into. + */ +static int +capture_init_fusion(jnode * node, txn_handle * txnh, txn_capture mode, int can_coc) +{ + /* Have to perform two trylocks here. */ + if (likely(spin_trylock_atom(node->atom))) + if (likely(spin_trylock_atom(txnh->atom))) + return capture_init_fusion_locked(node, txnh, mode, can_coc); + else { + UNLOCK_ATOM(node->atom); + reiser4_stat_inc(txnmgr.restart.init_fusion_atomh); + } + else { + reiser4_stat_inc(txnmgr.restart.init_fusion_atomf); + } + + UNLOCK_JNODE(node); + UNLOCK_TXNH(txnh); + return RETERR(-E_REPEAT); +} +/* This function splices together two jnode lists (small and large) and sets all jnodes in + the small list to point to the large atom. Returns the length of the list. */ +static int +capture_fuse_jnode_lists(txn_atom * large, capture_list_head * large_head, capture_list_head * small_head) +{ + int count = 0; + jnode *node; + + assert("umka-218", large != NULL); + assert("umka-219", large_head != NULL); + assert("umka-220", small_head != NULL); + /* small atom should be locked also. */ + assert("zam-968", spin_atom_is_locked(large)); + + /* For every jnode on small's capture list... */ + for_all_type_safe_list(capture, small_head, node) { + count += 1; + + /* With the jnode lock held, update atom pointer. */ + UNDER_SPIN_VOID(jnode, node, node->atom = large); + } + + /* Splice the lists. */ + capture_list_splice(large_head, small_head); + + return count; +} + +/* This function splices together two txnh lists (small and large) and sets all txn handles in + the small list to point to the large atom. Returns the length of the list. */ +/* Audited by: umka (2002.06.13) */ +static int +capture_fuse_txnh_lists(txn_atom * large, txnh_list_head * large_head, txnh_list_head * small_head) +{ + int count = 0; + txn_handle *txnh; + + assert("umka-221", large != NULL); + assert("umka-222", large_head != NULL); + assert("umka-223", small_head != NULL); + + /* Adjust every txnh to the new atom. */ + for_all_type_safe_list(txnh, small_head, txnh) { + count += 1; + + /* With the txnh lock held, update atom pointer. */ + UNDER_SPIN_VOID(txnh, txnh, txnh->atom = large); + } + + /* Splice the txn_handle list. */ + txnh_list_splice(large_head, small_head); + + return count; +} + +/* This function fuses two atoms. The captured nodes and handles belonging to SMALL are + added to LARGE and their ->atom pointers are all updated. The associated counts are + updated as well, and any waiting handles belonging to either are awakened. Finally the + smaller atom's refcount is decremented. +*/ +static void +capture_fuse_into(txn_atom * small, txn_atom * large) +{ + int level; + unsigned zcount = 0; + unsigned tcount = 0; + protected_jnodes *prot_list; + + assert("umka-224", small != NULL); + assert("umka-225", small != NULL); + + assert("umka-299", spin_atom_is_locked(large)); + assert("umka-300", spin_atom_is_locked(small)); + + assert("jmacd-201", atom_isopen(small)); + assert("jmacd-202", atom_isopen(large)); + + ON_TRACE(TRACE_TXN, "fuse atom %u into %u\n", small->atom_id, large->atom_id); + + /* Splice and update the per-level dirty jnode lists */ + for (level = 0; level < REAL_MAX_ZTREE_HEIGHT + 1; level += 1) { + zcount += capture_fuse_jnode_lists(large, ATOM_DIRTY_LIST(large, level), ATOM_DIRTY_LIST(small, level)); + } + + /* Splice and update the [clean,dirty] jnode and txnh lists */ + zcount += capture_fuse_jnode_lists(large, ATOM_CLEAN_LIST(large), ATOM_CLEAN_LIST(small)); + zcount += capture_fuse_jnode_lists(large, ATOM_OVRWR_LIST(large), ATOM_OVRWR_LIST(small)); + zcount += capture_fuse_jnode_lists(large, ATOM_WB_LIST(large), ATOM_WB_LIST(small)); + zcount += capture_fuse_jnode_lists(large, &large->inodes, &small->inodes); + tcount += capture_fuse_txnh_lists(large, &large->txnh_list, &small->txnh_list); + + for_all_type_safe_list(prot, &small->protected, prot_list) { + jnode *node; + + for_all_type_safe_list(capture, &prot_list->nodes, node) { + zcount += 1; + + LOCK_JNODE(node); + assert("nikita-3375", node->atom == small); + /* With the jnode lock held, update atom pointer. */ + node->atom = large; + UNLOCK_JNODE(node); + } + } + /* Splice the lists of lists. */ + prot_list_splice(&large->protected, &small->protected); + + /* Check our accounting. */ + assert("jmacd-1063", zcount + small->num_queued == small->capture_count); + assert("jmacd-1065", tcount == small->txnh_count); + + /* sum numbers of waiters threads */ + large->nr_waiters += small->nr_waiters; + small->nr_waiters = 0; + + /* splice flush queues */ + fuse_fq(large, small); + + /* update counter of jnode on every atom' list */ + ON_DEBUG(large->dirty += small->dirty; + small->dirty = 0; + large->clean += small->clean; + small->clean = 0; + large->ovrwr += small->ovrwr; + small->ovrwr = 0; + large->wb += small->wb; + small->wb = 0; + large->fq += small->fq; + small->fq = 0; + large->protect += small->protect; + small->protect = 0; + ); + + /* count flushers in result atom */ + large->nr_flushers += small->nr_flushers; + small->nr_flushers = 0; + + /* update counts of flushed nodes */ + large->flushed += small->flushed; + small->flushed = 0; + + /* Transfer list counts to large. */ + large->txnh_count += small->txnh_count; + large->capture_count += small->capture_count; + + /* Add all txnh references to large. */ + atomic_add(small->txnh_count, &large->refcount); + atomic_sub(small->txnh_count, &small->refcount); + + /* Reset small counts */ + small->txnh_count = 0; + small->capture_count = 0; + + /* Assign the oldest start_time, merge flags. */ + large->start_time = min(large->start_time, small->start_time); + large->flags |= small->flags; + + /* Merge blocknr sets. */ + blocknr_set_merge(&small->delete_set, &large->delete_set); + blocknr_set_merge(&small->wandered_map, &large->wandered_map); + + /* Merge allocated/deleted file counts */ + large->nr_objects_deleted += small->nr_objects_deleted; + large->nr_objects_created += small->nr_objects_created; + + small->nr_objects_deleted = 0; + small->nr_objects_created = 0; + + /* Merge allocated blocks counts */ + large->nr_blocks_allocated += small->nr_blocks_allocated; + + large->nr_running_queues += small->nr_running_queues; + small->nr_running_queues = 0; + + /* Merge blocks reserved for overwrite set. */ + large->flush_reserved += small->flush_reserved; + small->flush_reserved = 0; + + if (large->stage < small->stage) { + /* Large only needs to notify if it has changed state. */ + atom_set_stage(large, small->stage); + wakeup_atom_waiting_list(large); + } + + atom_set_stage(small, ASTAGE_INVALID); + + /* Notify any waiters--small needs to unload its wait lists. Waiters + actually remove themselves from the list before returning from the + fuse_wait function. */ + wakeup_atom_waiting_list(small); + + /* Unlock atoms */ + UNLOCK_ATOM(large); + atom_dec_and_unlock(small); +} + +reiser4_internal void +protected_jnodes_init(protected_jnodes *list) +{ + txn_atom *atom; + + assert("nikita-3376", list != NULL); + + atom = get_current_atom_locked(); + prot_list_push_front(&atom->protected, list); + capture_list_init(&list->nodes); + UNLOCK_ATOM(atom); +} + +reiser4_internal void +protected_jnodes_done(protected_jnodes *list) +{ + txn_atom *atom; + + assert("nikita-3379", capture_list_empty(&list->nodes)); + + atom = get_current_atom_locked(); + prot_list_remove(list); + UNLOCK_ATOM(atom); +} + +/* TXNMGR STUFF */ + +#if REISER4_COPY_ON_CAPTURE + +/* copy on capture steals jnode (J) from capture list. It may replace (J) with + special newly created jnode (CCJ) to which J's page gets attached. J in its + turn gets newly created copy of page. + Or, it may merely take J from capture list if J was never dirtied + + The problem with this replacement is that capture lists are being contiguously + scanned. + Race between replacement and scanning are avoided with one global spin lock + (scan_lock) and JNODE_SCANNED state of jnode. Replacement (in capture copy) + goes under scan_lock locked only if jnode is not in JNODE_SCANNED state. This + state gets set under scan_lock locked whenever scanning is working with that + jnode. +*/ + +/* remove jnode page from mapping's tree and insert new page with the same index */ +static void +replace_page_in_mapping(jnode *node, struct page *new_page) +{ + struct address_space *mapping; + unsigned long index; + + mapping = jnode_get_mapping(node); + index = jnode_get_index(node); + + spin_lock(&mapping->page_lock); + + /* delete old page from. This resembles __remove_from_page_cache */ + assert("vs-1416", radix_tree_lookup(&mapping->page_tree, index) == node->pg); + assert("vs-1428", node->pg->mapping == mapping); + __remove_from_page_cache(node->pg); + + /* insert new page into mapping */ + check_me("vs-1411", + radix_tree_insert(&mapping->page_tree, index, new_page) == 0); + + /* this resembles add_to_page_cache */ + page_cache_get(new_page); + ___add_to_page_cache(new_page, mapping, index); + + spin_unlock(&mapping->page_lock); + lru_cache_add(new_page); +} + +/* attach page of @node to @copy, @new_page to @node */ +static void +swap_jnode_pages(jnode *node, jnode *copy, struct page *new_page) +{ + /* attach old page to new jnode */ + assert("vs-1414", jnode_by_page(node->pg) == node); + copy->pg = node->pg; + copy->data = page_address(copy->pg); + jnode_set_block(copy, jnode_get_block(node)); + copy->pg->private = (unsigned long)copy; + + /* attach new page to jnode */ + assert("vs-1412", !PagePrivate(new_page)); + page_cache_get(new_page); + node->pg = new_page; + node->data = page_address(new_page); + new_page->private = (unsigned long)node; + SetPagePrivate(new_page); + + { + /* insert old page to new mapping */ + struct address_space *mapping; + unsigned long index; + + mapping = get_current_super_private()->cc->i_mapping; + index = (unsigned long)copy; + spin_lock(&mapping->page_lock); + + /* insert old page into new (fake) mapping. No page_cache_get + because page reference counter was not decreased on removing + it from old mapping */ + assert("vs-1416", radix_tree_lookup(&mapping->page_tree, index) == NULL); + check_me("vs-1418", radix_tree_insert(&mapping->page_tree, index, copy->pg) == 0); + ___add_to_page_cache(copy->pg, mapping, index); + ON_DEBUG(set_bit(PG_arch_1, &(copy->pg)->flags)); + + /* corresponding page_cache_release is in invalidate_list */ + page_cache_get(copy->pg); + spin_unlock(&mapping->page_lock); + } +} + +/* this is to make capture copied jnode looking like if there were jload called for it */ +static void +fake_jload(jnode *node) +{ + jref(node); + atomic_inc(&node->d_count); + JF_SET(node, JNODE_PARSED); +} + +/* for now - refuse to copy-on-capture any suspicious nodes (WRITEBACK, DIRTY, FLUSH_QUEUED) */ +static int +check_capturable(const jnode *node, const txn_atom *atom) +{ + assert("vs-1429", spin_jnode_is_locked(node)); + assert("vs-1487", check_spin_is_locked(&scan_lock)); + + if (JF_ISSET(node, JNODE_WRITEBACK)) { + reiser4_stat_inc(coc.writeback); + return RETERR(-E_WAIT); + } + if (JF_ISSET(node, JNODE_FLUSH_QUEUED)) { + reiser4_stat_inc(coc.flush_queued); + return RETERR(-E_WAIT); + } + if (JF_ISSET(node, JNODE_DIRTY)) { + reiser4_stat_inc(coc.dirty); + return RETERR(-E_WAIT); + } + if (JF_ISSET(node, JNODE_SCANNED)) { + reiser4_stat_inc(coc.scan_race); + return RETERR(-E_REPEAT); + } + if (node->atom != atom) { + reiser4_stat_inc(coc.atom_changed); + return RETERR(-E_WAIT); + } + return 0; /* OK */ +} + +static void +remove_from_capture_list(jnode *node) +{ + ON_DEBUG_MODIFY(znode_set_checksum(node, 1)); + JF_CLR(node, JNODE_DIRTY); + JF_CLR(node, JNODE_RELOC); + JF_CLR(node, JNODE_OVRWR); + JF_CLR(node, JNODE_CREATED); + JF_CLR(node, JNODE_WRITEBACK); + JF_CLR(node, JNODE_REPACK); + + capture_list_remove_clean(node); + node->atom->capture_count --; + atomic_dec(&node->x_count); + /*XXXX*/ON_DEBUG(count_jnode(node->atom, node, NODE_LIST(node), NOT_CAPTURED, 1)); + node->atom = 0; +} + +/* insert new jnode (copy) to capture list instead of old one */ +static void +replace_on_capture_list(jnode *node, jnode *copy) +{ + assert("vs-1415", node->atom); + assert("vs-1489", !capture_list_is_clean(node)); + assert("vs-1493", JF_ISSET(copy, JNODE_CC) && JF_ISSET(copy, JNODE_HEARD_BANSHEE)); + + copy->state |= node->state; + + /* insert cc-jnode @copy into capture list before old jnode @node */ + capture_list_insert_before(node, copy); + jref(copy); + copy->atom = node->atom; + node->atom->capture_count ++; + /*XXXX*/ON_DEBUG(count_jnode(node->atom, copy, NODE_LIST(copy), NODE_LIST(node), 1)); + + /* remove old jnode from capture list */ + remove_from_capture_list(node); +} + +/* when capture request is made for a node which is captured but was never + dirtied copy on capture will merely uncapture it */ +static int +copy_on_capture_clean(jnode *node, txn_atom *atom) +{ + int result; + + assert("vs-1625", spin_atom_is_locked(atom)); + assert("vs-1432", spin_jnode_is_locked(node)); + assert("vs-1627", !JF_ISSET(node, JNODE_WRITEBACK)); + + spin_lock(&scan_lock); + result = check_capturable(node, atom); + if (result == 0) { + /* remove jnode from capture list */ + remove_from_capture_list(node); + reiser4_stat_inc(coc.ok_clean); + } + spin_unlock(&scan_lock); + UNLOCK_JNODE(node); + UNLOCK_ATOM(atom); + + return result; +} + +static void +lock_two_nodes(jnode *node1, jnode *node2) +{ + if (node1 > node2) { + LOCK_JNODE(node2); + LOCK_JNODE(node1); + } else { + LOCK_JNODE(node1); + LOCK_JNODE(node2); + } +} + +/* capture request is made for node which does not have page. In most cases this + is "uber" znode */ +static int +copy_on_capture_nopage(jnode *node, txn_atom *atom) +{ + int result; + jnode *copy; + + assert("vs-1432", spin_atom_is_locked(atom)); + assert("vs-1432", spin_jnode_is_locked(node)); + + jref(node); + UNLOCK_JNODE(node); + UNLOCK_ATOM(atom); + assert("nikita-3475", schedulable()); + copy = jclone(node); + if (IS_ERR(copy)) { + jput(node); + return PTR_ERR(copy); + } + + LOCK_ATOM(atom); + lock_two_nodes(node, copy); + spin_lock(&scan_lock); + + result = check_capturable(node, atom); + if (result == 0) { + if (jnode_page(node) == NULL) { + replace_on_capture_list(node, copy); +#if REISER4_STATS + if (znode_above_root(JZNODE(node))) + reiser4_stat_inc(coc.ok_uber); + else + reiser4_stat_inc(coc.ok_nopage); +#endif + } else + result = RETERR(-E_REPEAT); + } + + spin_unlock(&scan_lock); + UNLOCK_JNODE(node); + UNLOCK_JNODE(copy); + UNLOCK_ATOM(atom); + assert("nikita-3476", schedulable()); + jput(copy); + assert("nikita-3477", schedulable()); + jput(node); + assert("nikita-3478", schedulable()); + ON_TRACE(TRACE_CAPTURE_COPY, "nopage\n"); + return result; +} + +static int +handle_coc(jnode *node, jnode *copy, struct page *page, struct page *new_page, + txn_atom *atom) +{ + char *to; + char *from; + int result; + + to = kmap(new_page); + lock_page(page); + from = kmap(page); + /* + * FIXME(zam): one preloaded radix tree node may be not enough for two + * insertions, one insertion is in replace_page_in_mapping(), another + * one is in swap_jnode_pages(). The radix_tree_delete() call might + * not help, because an empty radix tree node is freed and the node's + * free space may not be re-used in insertion. + */ + radix_tree_preload(GFP_KERNEL); + LOCK_ATOM(atom); + lock_two_nodes(node, copy); + spin_lock(&scan_lock); + + result = check_capturable(node, atom); + if (result == 0) { + /* if node was jloaded by get_overwrite_set, we have to jrelse + it here, because we remove jnode from atom's capture list - + put_overwrite_set will not jrelse it */ + int was_jloaded; + + was_jloaded = JF_ISSET(node, JNODE_JLOADED_BY_GET_OVERWRITE_SET); + + replace_page_in_mapping(node, new_page); + swap_jnode_pages(node, copy, new_page); + replace_on_capture_list(node, copy); + /* statistics */ + if (JF_ISSET(copy, JNODE_RELOC)) { + reiser4_stat_inc(coc.ok_reloc); + } else if (JF_ISSET(copy, JNODE_OVRWR)) { + reiser4_stat_inc(coc.ok_ovrwr); + } else + impossible("", ""); + + memcpy(to, from, PAGE_CACHE_SIZE); + SetPageUptodate(new_page); + if (was_jloaded) + fake_jload(copy); + else + kunmap(page); + + assert("vs-1419", page_count(new_page) >= 3); + spin_unlock(&scan_lock); + UNLOCK_JNODE(node); + UNLOCK_JNODE(copy); + UNLOCK_ATOM(atom); + radix_tree_preload_end(); + unlock_page(page); + + if (was_jloaded) { + jrelse_tail(node); + assert("vs-1494", JF_ISSET(node, JNODE_JLOADED_BY_GET_OVERWRITE_SET)); + JF_CLR(node, JNODE_JLOADED_BY_GET_OVERWRITE_SET); + } else + kunmap(new_page); + + jput(copy); + jrelse(node); + jput(node); + page_cache_release(page); + page_cache_release(new_page); + ON_TRACE(TRACE_CAPTURE_COPY, "copy on capture done\n"); + } else { + spin_unlock(&scan_lock); + UNLOCK_JNODE(node); + UNLOCK_JNODE(copy); + UNLOCK_ATOM(atom); + radix_tree_preload_end(); + kunmap(page); + unlock_page(page); + kunmap(new_page); + page_cache_release(new_page); + } + return result; +} + +static int +real_copy_on_capture(jnode *node, txn_atom *atom) +{ + int result; + jnode *copy; + struct page *page; + struct page *new_page; + + assert("vs-1432", spin_jnode_is_locked(node)); + assert("vs-1490", !JF_ISSET(node, JNODE_EFLUSH)); + assert("vs-1491", node->pg); + assert("vs-1492", jprivate(node->pg) == node); + + page = node->pg; + page_cache_get(page); + jref(node); + UNLOCK_JNODE(node); + UNLOCK_ATOM(atom); + + /* prevent node from eflushing */ + result = jload(node); + if (!result) { + copy = jclone(node); + if (likely(!IS_ERR(copy))) { + new_page = alloc_page(GFP_KERNEL); + if (new_page) { + result = handle_coc(node, + copy, page, new_page, atom); + if (result == 0) + return 0; + } else + result = RETERR(-ENOMEM); + jput(copy); + } + jrelse(node); + } + + jput(node); + page_cache_release(page); + return result; +} + +/* create new jnode, create new page, jload old jnode, copy data, detach old + page from old jnode, attach new page to old jnode, attach old page to new + jnode this returns 0 if copy on capture succeeded, E_REPEAT to have + capture_fuse_wait to be called */ +static int +create_copy_and_replace(jnode *node, txn_atom *atom) +{ + int result; + struct inode *inode; /* inode for which filemap_nopage is blocked */ + + assert("jmacd-321", spin_jnode_is_locked(node)); + assert("umka-295", spin_atom_is_locked(atom)); + assert("vs-1381", node->atom == atom); + assert("vs-1409", atom->stage > ASTAGE_CAPTURE_WAIT && atom->stage < ASTAGE_DONE); + assert("vs-1410", jnode_is_znode(node) || jnode_is_unformatted(node)); + + + if (JF_ISSET(node, JNODE_CCED)) { + /* node is under copy on capture already */ + reiser4_stat_inc(coc.coc_race); + UNLOCK_JNODE(node); + UNLOCK_ATOM(atom); + return RETERR(-E_WAIT); + } + + /* measure how often suspicious (WRITEBACK, DIRTY, FLUSH_QUEUED) appear + here. For most often case we can return EAGAIN right here and avoid + all the preparations made for copy on capture */ + ON_TRACE(TRACE_CAPTURE_COPY, "copy_on_capture: node %p, atom %p..", node, atom); + if (JF_ISSET(node, JNODE_EFLUSH)) { + UNLOCK_JNODE(node); + UNLOCK_ATOM(atom); + + reiser4_stat_inc(coc.eflush); + ON_TRACE(TRACE_CAPTURE_COPY, "eflushed\n"); + result = jload(node); + if (result) + return RETERR(result); + jrelse(node); + return RETERR(-E_REPEAT); + } + + set_cced_bit(node); + + if (jnode_is_unformatted(node)) { + /* to capture_copy unformatted node we have to take care of its + page mappings. Page gets unmapped here and concurrent + mappings are blocked on reiser4 inodes's coc_sem in reiser4's + filemap_nopage */ + struct page *page; + + inode = mapping_jnode(node)->host; + page = jnode_page(node); + assert("vs-1640", inode != NULL); + assert("vs-1641", page != NULL); + assert("vs-1642", page->mapping != NULL); + UNLOCK_JNODE(node); + UNLOCK_ATOM(atom); + + down_write(&reiser4_inode_data(inode)->coc_sem); + lock_page(page); + pte_chain_lock(page); + + if (page_mapped(page)) { + result = try_to_unmap(page); + if (result == SWAP_AGAIN) { + result = RETERR(-E_REPEAT); + + } else if (result == SWAP_FAIL) + result = RETERR(-E_WAIT); + else { + assert("vs-1643", result == SWAP_SUCCESS); + result = 0; + } + if (result != 0) { + unlock_page(page); + pte_chain_unlock(page); + up_write(&reiser4_inode_data(inode)->coc_sem); + return result; + } + } + pte_chain_unlock(page); + unlock_page(page); + LOCK_ATOM(atom); + LOCK_JNODE(node); + } else + inode = NULL; + + if (!JF_ISSET(node, JNODE_OVRWR) && !JF_ISSET(node, JNODE_RELOC)) { + /* clean node can be made available for capturing. Just take + care to preserve atom list during uncapturing */ + ON_TRACE(TRACE_CAPTURE_COPY, "clean\n"); + result = copy_on_capture_clean(node, atom); + } else if (!node->pg) { + ON_TRACE(TRACE_CAPTURE_COPY, "uber\n"); + result = copy_on_capture_nopage(node, atom); + } else + result = real_copy_on_capture(node, atom); + if (result != 0) + clear_cced_bits(node); + assert("vs-1626", spin_atom_is_not_locked(atom)); + + if (inode != NULL) + up_write(&reiser4_inode_data(inode)->coc_sem); + + return result; +} +#endif /* REISER4_COPY_ON_CAPTURE */ + +/* Perform copy-on-capture of a block. */ +static int +capture_copy(jnode * node, txn_handle * txnh, txn_atom * atomf, txn_atom * atomh, txn_capture mode, int can_coc) +{ +#if REISER4_COPY_ON_CAPTURE + reiser4_stat_inc(coc.calls); + + /* do not copy on capture in ent thread to avoid deadlock on coc semaphore */ + if (can_coc && get_current_context()->entd == 0) { + int result; + + ON_TRACE(TRACE_TXN, "capture_copy\n"); + + /* The txnh and its (possibly NULL) atom's locks are not needed + at this point. */ + UNLOCK_TXNH(txnh); + if (atomh != NULL) + UNLOCK_ATOM(atomh); + + /* create a copy of node, detach node from atom and attach its copy + instead */ + atomic_inc(&atomf->refcount); + result = create_copy_and_replace(node, atomf); + assert("nikita-3474", schedulable()); + preempt_point(); + LOCK_ATOM(atomf); + atom_dec_and_unlock(atomf); + preempt_point(); + + if (result == 0) { + if (jnode_is_znode(node)) { + znode *z; + + z = JZNODE(node); + z->version = znode_build_version(jnode_get_tree(node)); + } + result = RETERR(-E_REPEAT); + } + return result; + } + + reiser4_stat_inc(coc.forbidden); + return capture_fuse_wait(node, txnh, atomf, atomh, mode); +#else + ON_TRACE(TRACE_TXN, "capture_copy: fuse wait\n"); + + return capture_fuse_wait(node, txnh, atomf, atomh, mode); + +#endif +} + +/* Release a block from the atom, reversing the effects of being captured, + do not release atom's reference to jnode due to holding spin-locks. + Currently this is only called when the atom commits. + + NOTE: this function does not release a (journal) reference to jnode + due to locking optimizations, you should call jput() somewhere after + calling uncapture_block(). */ +reiser4_internal void uncapture_block(jnode * node) +{ + txn_atom * atom; + + assert("umka-226", node != NULL); + atom = node->atom; + assert("umka-228", atom != NULL); + + assert("jmacd-1021", node->atom == atom); + assert("jmacd-1022", spin_jnode_is_locked(node)); +#if REISER4_COPY_ON_CAPTURE + assert("jmacd-1023", spin_atom_is_locked(atom)); +#else + assert("jmacd-1023", atom_is_protected(atom)); +#endif + + /*ON_TRACE (TRACE_TXN, "un-capture %p from atom %u (captured %u)\n", + * node, atom->atom_id, atom->capture_count); */ + + ON_DEBUG_MODIFY(znode_set_checksum(node, 1)); + JF_CLR(node, JNODE_DIRTY); + JF_CLR(node, JNODE_RELOC); + JF_CLR(node, JNODE_OVRWR); + JF_CLR(node, JNODE_CREATED); + JF_CLR(node, JNODE_WRITEBACK); + JF_CLR(node, JNODE_REPACK); + clear_cced_bits(node); +#if REISER4_DEBUG + node->written = 0; +#endif + + capture_list_remove_clean(node); + if (JF_ISSET(node, JNODE_FLUSH_QUEUED)) { + assert("zam-925", atom_isopen(atom)); + assert("vs-1623", NODE_LIST(node) == FQ_LIST); + ON_DEBUG(atom->num_queued --); + JF_CLR(node, JNODE_FLUSH_QUEUED); + } + atom->capture_count -= 1; + ON_DEBUG(count_jnode(atom, node, NODE_LIST(node), NOT_CAPTURED, 1)); + node->atom = NULL; + + UNLOCK_JNODE(node); + LOCK_CNT_DEC(t_refs); +} + +/* Unconditional insert of jnode into atom's overwrite list. Currently used in + bitmap-based allocator code for adding modified bitmap blocks the + transaction. @atom and @node are spin locked */ +reiser4_internal void +insert_into_atom_ovrwr_list(txn_atom * atom, jnode * node) +{ + assert("zam-538", spin_atom_is_locked(atom) || atom->stage >= ASTAGE_PRE_COMMIT); + assert("zam-539", spin_jnode_is_locked(node)); + assert("zam-899", JF_ISSET(node, JNODE_OVRWR)); + assert("zam-543", node->atom == NULL); + assert("vs-1433", !jnode_is_unformatted(node) && !jnode_is_znode(node)); + + capture_list_push_front(ATOM_OVRWR_LIST(atom), node); + jref(node); + node->atom = atom; + atom->capture_count++; + ON_DEBUG(count_jnode(atom, node, NODE_LIST(node), OVRWR_LIST, 1)); +} + +/* return 1 if two dirty jnodes belong to one atom, 0 - otherwise */ +reiser4_internal int +jnodes_of_one_atom(jnode * j1, jnode * j2) +{ + int ret = 0; + int finish = 0; + + assert("zam-9003", j1 != j2); + /*assert ("zam-9004", jnode_check_dirty (j1)); */ + assert("zam-9005", jnode_check_dirty(j2)); + + do { + LOCK_JNODE(j1); + assert("zam-9001", j1->atom != NULL); + if (spin_trylock_jnode(j2)) { + assert("zam-9002", j2->atom != NULL); + ret = (j2->atom == j1->atom); + finish = 1; + + UNLOCK_JNODE(j2); + } + UNLOCK_JNODE(j1); + } while (!finish); + + return ret; +} + +/* when atom becomes that big, commit it as soon as possible. This was found + * to be most effective by testing. */ +reiser4_internal unsigned int +txnmgr_get_max_atom_size(struct super_block *super UNUSED_ARG) +{ + return totalram_pages / 4; +} + + +#if REISER4_DEBUG_OUTPUT + +reiser4_internal void +info_atom(const char *prefix, const txn_atom * atom) +{ + if (atom == NULL) { + printk("%s: no atom\n", prefix); + return; + } + + printk("%s: refcount: %i id: %i flags: %x txnh_count: %i" + " capture_count: %i stage: %x start: %lu, flushed: %i\n", prefix, + atomic_read(&atom->refcount), atom->atom_id, atom->flags, atom->txnh_count, + atom->capture_count, atom->stage, atom->start_time, atom->flushed); +} + + +reiser4_internal void +print_atom(const char *prefix, txn_atom * atom) +{ + jnode *pos_in_atom; + char list[32]; + int level; + + assert("umka-229", atom != NULL); + + info_atom(prefix, atom); + + for (level = 0; level < REAL_MAX_ZTREE_HEIGHT + 1; level += 1) { + + sprintf(list, "capture level %d", level); + + for (pos_in_atom = capture_list_front(ATOM_DIRTY_LIST(atom, level)); + !capture_list_end(ATOM_DIRTY_LIST(atom, level), pos_in_atom); + pos_in_atom = capture_list_next(pos_in_atom)) { + + info_jnode(list, pos_in_atom); + printk("\n"); + } + } + + for_all_type_safe_list(capture, ATOM_CLEAN_LIST(atom), pos_in_atom) { + info_jnode("clean", pos_in_atom); + printk("\n"); + } +} +#endif + +static int count_deleted_blocks_actor ( + txn_atom *atom, const reiser4_block_nr * a, const reiser4_block_nr *b, void * data) +{ + reiser4_block_nr *counter = data; + + assert ("zam-995", data != NULL); + assert ("zam-996", a != NULL); + if (b == NULL) + *counter += 1; + else + *counter += *b; + return 0; +} +reiser4_internal reiser4_block_nr txnmgr_count_deleted_blocks (void) +{ + reiser4_block_nr result; + txn_mgr *tmgr = &get_super_private(reiser4_get_current_sb())->tmgr; + txn_atom * atom; + + result = 0; + + spin_lock_txnmgr(tmgr); + for_all_type_safe_list(atom, &tmgr->atoms_list, atom) { + LOCK_ATOM(atom); + blocknr_set_iterator(atom, &atom->delete_set, + count_deleted_blocks_actor, &result, 0); + UNLOCK_ATOM(atom); + } + spin_unlock_txnmgr(tmgr); + + return result; +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 80 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/txnmgr.h 2004-09-03 00:57:05.456209792 -0700 @@ -0,0 +1,659 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* data-types and function declarations for transaction manager. See txnmgr.c + * for details. */ + +#ifndef __REISER4_TXNMGR_H__ +#define __REISER4_TXNMGR_H__ + +#include "forward.h" +#include "spin_macros.h" +#include "dformat.h" +#include "type_safe_list.h" + +#include +#include +#include +#include +#include +#include + +/* LIST TYPES */ + +/* list of all atoms controlled by single transaction manager (that is, file + * system) */ +TYPE_SAFE_LIST_DECLARE(atom); +/* list of transaction handles attached to given atom */ +TYPE_SAFE_LIST_DECLARE(txnh); + +/* + * ->fwaitfor and ->fwaiting lists. + * + * Each atom has one of these lists: one for its own handles waiting on + * another atom and one for reverse mapping. Used to prevent deadlock in the + * ASTAGE_CAPTURE_WAIT state. + * + * Thread that needs to wait for a given atom, attaches itself to the atom's + * ->fwaitfor list. This is done in atom_wait_event() (and, in + * capture_fuse_wait()). All threads waiting on this list are waked up + * whenever "event" occurs for this atom: it changes stage, commits, flush + * queue is released, etc. This is used, in particular, to implement sync(), + * where thread has to wait until atom commits. + */ +TYPE_SAFE_LIST_DECLARE(fwaitfor); + +/* + * This list is used to wait for atom fusion (in capture_fuse_wait()). Threads + * waiting on this list are waked up if atom commits or is fused into another. + * + * This is used in capture_fuse_wait() which see for more comments. + */ +TYPE_SAFE_LIST_DECLARE(fwaiting); + +/* The transaction's list of captured jnodes */ +TYPE_SAFE_LIST_DECLARE(capture); +#if REISER4_DEBUG +TYPE_SAFE_LIST_DECLARE(inode_jnodes); +#endif + +TYPE_SAFE_LIST_DECLARE(blocknr_set); /* Used for the transaction's delete set + * and wandered mapping. */ + +/* list of flush queues attached to a given atom */ +TYPE_SAFE_LIST_DECLARE(fq); + +/* list of lists of jnodes that threads take into exclusive ownership during + * allocate-on-flush.*/ +TYPE_SAFE_LIST_DECLARE(prot); + +/* TYPE DECLARATIONS */ + +/* This enumeration describes the possible types of a capture request (try_capture). + A capture request dynamically assigns a block to the calling thread's transaction + handle. */ +typedef enum { + /* A READ_ATOMIC request indicates that a block will be read and that the caller's + atom should fuse in order to ensure that the block commits atomically with the + caller. */ + TXN_CAPTURE_READ_ATOMIC = (1 << 0), + + /* A READ_NONCOM request indicates that a block will be read and that the caller is + willing to read a non-committed block without causing atoms to fuse. */ + TXN_CAPTURE_READ_NONCOM = (1 << 1), + + /* A READ_MODIFY request indicates that a block will be read but that the caller + wishes for the block to be captured as it will be written. This capture request + mode is not currently used, but eventually it will be useful for preventing + deadlock in read-modify-write cycles. */ + TXN_CAPTURE_READ_MODIFY = (1 << 2), + + /* A WRITE capture request indicates that a block will be modified and that atoms + should fuse to make the commit atomic. */ + TXN_CAPTURE_WRITE = (1 << 3), + + /* CAPTURE_TYPES is a mask of the four above capture types, used to separate the + exclusive type designation from extra bits that may be supplied -- see + below. */ + TXN_CAPTURE_TYPES = (TXN_CAPTURE_READ_ATOMIC | + TXN_CAPTURE_READ_NONCOM | TXN_CAPTURE_READ_MODIFY | TXN_CAPTURE_WRITE), + + /* A subset of CAPTURE_TYPES, CAPTURE_WTYPES is a mask of request types that + indicate modification will occur. */ + TXN_CAPTURE_WTYPES = (TXN_CAPTURE_READ_MODIFY | TXN_CAPTURE_WRITE), + + /* An option to try_capture, NONBLOCKING indicates that the caller would + prefer not to sleep waiting for an aging atom to commit. */ + TXN_CAPTURE_NONBLOCKING = (1 << 4), + + /* An option to try_capture to prevent atom fusion, just simple capturing is allowed */ + TXN_CAPTURE_DONT_FUSE = (1 << 5), + + /* if it is set - copy on capture is allowed */ + /*TXN_CAPTURE_CAN_COC = (1 << 6)*/ + + /* This macro selects only the exclusive capture request types, stripping out any + options that were supplied (i.e., NONBLOCKING). */ +#define CAPTURE_TYPE(x) ((x) & TXN_CAPTURE_TYPES) +} txn_capture; + +/* There are two kinds of transaction handle: WRITE_FUSING and READ_FUSING, the only + difference is in the handling of read requests. A WRITE_FUSING transaction handle + defaults read capture requests to TXN_CAPTURE_READ_NONCOM whereas a READ_FUSIONG + transaction handle defaults to TXN_CAPTURE_READ_ATOMIC. */ +typedef enum { + TXN_WRITE_FUSING = (1 << 0), + TXN_READ_FUSING = (1 << 1) | TXN_WRITE_FUSING, /* READ implies WRITE */ +} txn_mode; + +/* Every atom has a stage, which is one of these exclusive values: */ +typedef enum { + /* Initially an atom is free. */ + ASTAGE_FREE = 0, + + /* An atom begins by entering the CAPTURE_FUSE stage, where it proceeds to capture + blocks and fuse with other atoms. */ + ASTAGE_CAPTURE_FUSE = 1, + + /* We need to have a ASTAGE_CAPTURE_SLOW in which an atom fuses with one node for every X nodes it flushes to disk where X > 1. */ + + /* When an atom reaches a certain age it must do all it can to commit. An atom in + the CAPTURE_WAIT stage refuses new transaction handles and prevents fusion from + atoms in the CAPTURE_FUSE stage. */ + ASTAGE_CAPTURE_WAIT = 2, + + /* Waiting for I/O before commit. Copy-on-capture (see + http://namesys.com/v4/v4.html). */ + ASTAGE_PRE_COMMIT = 3, + + /* Post-commit overwrite I/O. Steal-on-capture. */ + ASTAGE_POST_COMMIT = 4, + + /* Atom which waits for the removal of the last reference to (it? ) to + * be deleted from memory */ + ASTAGE_DONE = 5, + + /* invalid atom. */ + ASTAGE_INVALID = 6, + +} txn_stage; + +/* Certain flags may be set in the txn_atom->flags field. */ +typedef enum { + /* Indicates that the atom should commit as soon as possible. */ + ATOM_FORCE_COMMIT = (1 << 0) +} txn_flags; + +/* Flags for controlling commit_txnh */ +typedef enum { + /* Wait commit atom completion in commit_txnh */ + TXNH_WAIT_COMMIT = 0x2, + /* Don't commit atom when this handle is closed */ + TXNH_DONT_COMMIT = 0x4 +} txn_handle_flags_t; + +/* TYPE DEFINITIONS */ + +/* A note on lock ordering: the handle & jnode spinlock protects reading of their ->atom + fields, so typically an operation on the atom through either of these objects must (1) + lock the object, (2) read the atom pointer, (3) lock the atom. + + During atom fusion, the process holds locks on both atoms at once. Then, it iterates + through the list of handles and pages held by the smaller of the two atoms. For each + handle and page referencing the smaller atom, the fusing process must: (1) lock the + object, and (2) update the atom pointer. + + You can see that there is a conflict of lock ordering here, so the more-complex + procedure should have priority, i.e., the fusing process has priority so that it is + guaranteed to make progress and to avoid restarts. + + This decision, however, means additional complexity for aquiring the atom lock in the + first place. + + The general original procedure followed in the code was: + + TXN_OBJECT *obj = ...; + TXN_ATOM *atom; + + spin_lock (& obj->_lock); + + atom = obj->_atom; + + if (! spin_trylock_atom (atom)) + { + spin_unlock (& obj->_lock); + RESTART OPERATION, THERE WAS A RACE; + } + + ELSE YOU HAVE BOTH ATOM AND OBJ LOCKED + + + It has however been found that this wastes CPU a lot in a manner that is + hard to profile. So, proper refcounting was added to atoms, and new + standard locking sequence is like following: + + TXN_OBJECT *obj = ...; + TXN_ATOM *atom; + + spin_lock (& obj->_lock); + + atom = obj->_atom; + + if (! spin_trylock_atom (atom)) + { + atomic_inc (& atom->refcount); + spin_unlock (& obj->_lock); + spin_lock (&atom->_lock); + atomic_dec (& atom->refcount); + // HERE atom is locked + spin_unlock (&atom->_lock); + RESTART OPERATION, THERE WAS A RACE; + } + + ELSE YOU HAVE BOTH ATOM AND OBJ LOCKED + + (core of this is implemented in trylock_throttle() function) + + See the jnode_get_atom() function for a common case. + + As an additional (and important) optimization allowing to avoid restarts, + it is possible to re-check required pre-conditions at the HERE point in + code above and proceed without restarting if they are still satisfied. +*/ + +/* A block number set consists of only the list head. */ +struct blocknr_set { + blocknr_set_list_head entries; /* blocknr_set_list_head defined from a template from tslist.h */ +}; + +/* An atomic transaction: this is the underlying system representation + of a transaction, not the one seen by clients. + + Invariants involving this data-type: + + [sb-fake-allocated] +*/ +struct txn_atom { + /* The spinlock protecting the atom, held during fusion and various other state + changes. */ + reiser4_spin_data alock; + + /* The atom's reference counter, increasing (in case of a duplication + of an existing reference or when we are sure that some other + reference exists) may be done without taking spinlock, decrementing + of the ref. counter requires a spinlock to be held. + + Each transaction handle counts in ->refcount. All jnodes count as + one reference acquired in atom_begin_andlock(), released in + commit_current_atom(). + */ + atomic_t refcount; + + /* The atom_id identifies the atom in persistent records such as the log. */ + __u32 atom_id; + + /* Flags holding any of the txn_flags enumerated values (e.g., + ATOM_FORCE_COMMIT). */ + __u32 flags; + + /* Number of open handles. */ + __u32 txnh_count; + + /* The number of znodes captured by this atom. Equal to the sum of lengths of the + dirty_nodes[level] and clean_nodes lists. */ + __u32 capture_count; + +#if REISER4_DEBUG + int clean; + int dirty; + int ovrwr; + int wb; + int fq; + int protect; +#endif + + __u32 flushed; + + /* Current transaction stage. */ + txn_stage stage; + + /* Start time. */ + unsigned long start_time; + + /* The atom's delete set. It collects block numbers of the nodes + which were deleted during the transaction. */ + blocknr_set delete_set; + + /* The atom's wandered_block mapping. */ + blocknr_set wandered_map; + + /* The transaction's list of dirty captured nodes--per level. Index + by (level). dirty_nodes[0] is for znode-above-root */ + capture_list_head dirty_nodes1[REAL_MAX_ZTREE_HEIGHT + 1]; + + /* The transaction's list of clean captured nodes. */ + capture_list_head clean_nodes1; + + /* The atom's overwrite set */ + capture_list_head ovrwr_nodes1; + + /* nodes which are being written to disk */ + capture_list_head writeback_nodes1; + + /* list of inodes */ + capture_list_head inodes; + + /* List of handles associated with this atom. */ + txnh_list_head txnh_list; + + /* Transaction list link: list of atoms in the transaction manager. */ + atom_list_link atom_link; + + /* List of handles waiting FOR this atom: see 'capture_fuse_wait' comment. */ + fwaitfor_list_head fwaitfor_list; + + /* List of this atom's handles that are waiting: see 'capture_fuse_wait' comment. */ + fwaiting_list_head fwaiting_list; + + prot_list_head protected; + + /* Numbers of objects which were deleted/created in this transaction + thereby numbers of objects IDs which were released/deallocated. */ + int nr_objects_deleted; + int nr_objects_created; + /* number of blocks allocated during the transaction */ + __u64 nr_blocks_allocated; + /* All atom's flush queue objects are on this list */ + fq_list_head flush_queues; +#if REISER4_DEBUG + /* number of flush queues for this atom. */ + int nr_flush_queues; + /* Number of jnodes which were removed from atom's lists and put + on flush_queue */ + int num_queued; +#endif + /* number of threads who wait for this atom to complete commit */ + int nr_waiters; + /* number of threads which do jnode_flush() over this atom */ + int nr_flushers; + /* number of flush queues which are IN_USE and jnodes from fq->prepped + are submitted to disk by the write_fq() routine. */ + int nr_running_queues; + /* A counter of grabbed unformatted nodes, see a description of the + * reiser4 space reservation scheme at block_alloc.c */ + reiser4_block_nr flush_reserved; +#if REISER4_DEBUG + void *committer; +#endif +}; + +#define ATOM_DIRTY_LIST(atom, level) (&(atom)->dirty_nodes1[level]) +#define ATOM_CLEAN_LIST(atom) (&(atom)->clean_nodes1) +#define ATOM_OVRWR_LIST(atom) (&(atom)->ovrwr_nodes1) +#define ATOM_WB_LIST(atom) (&(atom)->writeback_nodes1) +#define ATOM_FQ_LIST(fq) (&(fq)->prepped1) + +#define NODE_LIST(node) (node)->list1 +#define ASSIGN_NODE_LIST(node, list) ON_DEBUG(NODE_LIST(node) = list) +ON_DEBUG(void count_jnode(txn_atom *, jnode *, atom_list old_list, atom_list new_list, int check_lists)); + +typedef struct protected_jnodes { + prot_list_link inatom; + capture_list_head nodes; +} protected_jnodes; + +TYPE_SAFE_LIST_DEFINE(prot, protected_jnodes, inatom); + +TYPE_SAFE_LIST_DEFINE(atom, txn_atom, atom_link); + +/* A transaction handle: the client obtains and commits this handle which is assigned by + the system to a txn_atom. */ +struct txn_handle { + /* Spinlock protecting ->atom pointer */ + reiser4_spin_data hlock; + + /* Flags for controlling commit_txnh() behavior */ + /* from txn_handle_flags_t */ + txn_handle_flags_t flags; + + /* Whether it is READ_FUSING or WRITE_FUSING. */ + txn_mode mode; + + /* If assigned, the atom it is part of. */ + txn_atom *atom; + + /* Transaction list link. */ + txnh_list_link txnh_link; +}; + +TYPE_SAFE_LIST_DECLARE(txn_mgrs); + +/* The transaction manager: one is contained in the reiser4_super_info_data */ +struct txn_mgr { + /* A spinlock protecting the atom list, id_count, flush_control */ + reiser4_spin_data tmgr_lock; + + /* List of atoms. */ + atom_list_head atoms_list; + + /* Number of atoms. */ + int atom_count; + + /* A counter used to assign atom->atom_id values. */ + __u32 id_count; + + /* a semaphore object for commit serialization */ + struct semaphore commit_semaphore; + + /* a list of all txnmrgs served by particular daemon. */ + txn_mgrs_list_link linkage; + + /* description of daemon for this txnmgr */ + ktxnmgrd_context *daemon; + + /* parameters. Adjustable through mount options. */ + unsigned int atom_max_size; + unsigned int atom_max_age; + /* max number of concurrent flushers for one atom, 0 - unlimited. */ + unsigned int atom_max_flushers; +}; + +/* list of all transaction managers in a system */ +TYPE_SAFE_LIST_DEFINE(txn_mgrs, txn_mgr, linkage); + +/* FUNCTION DECLARATIONS */ + +/* These are the externally (within Reiser4) visible transaction functions, therefore they + are prefixed with "txn_". For comments, see txnmgr.c. */ + +extern int txnmgr_init_static(void); +extern void txnmgr_init(txn_mgr * mgr); + +extern int txnmgr_done_static(void); +extern int txnmgr_done(txn_mgr * mgr); + +extern int txn_reserve(int reserved); + +extern void txn_begin(reiser4_context * context); +extern long txn_end(reiser4_context * context); + +extern void txn_restart(reiser4_context * context); +extern void txn_restart_current(void); + +extern int txnmgr_force_commit_current_atom(void); +extern int txnmgr_force_commit_all(struct super_block *, int); +extern int current_atom_should_commit(void); + +extern jnode * find_first_dirty_jnode (txn_atom *, int); + +extern int commit_some_atoms(txn_mgr *); +extern int flush_current_atom (int, long *, txn_atom **); + +extern int flush_some_atom(long *, const struct writeback_control *, int); + +extern void atom_set_stage(txn_atom *atom, txn_stage stage); + +extern int same_slum_check(jnode * base, jnode * check, int alloc_check, int alloc_value); +extern void atom_dec_and_unlock(txn_atom * atom); + +extern txn_capture build_capture_mode(jnode * node, + znode_lock_mode lock_mode, + txn_capture flags); + +extern int try_capture(jnode * node, znode_lock_mode mode, txn_capture flags, int can_coc); +extern int try_capture_page_to_invalidate(struct page *pg); + +extern void uncapture_page(struct page *pg); +extern void uncapture_block(jnode *); +extern void uncapture_jnode(jnode *); + +extern int capture_inode(struct inode *); +extern int uncapture_inode(struct inode *); + +extern txn_atom *txnh_get_atom(txn_handle * txnh); +extern txn_atom *get_current_atom_locked_nocheck(void); + +#define atom_is_protected(atom) (spin_atom_is_locked(atom) || (atom)->stage >= ASTAGE_PRE_COMMIT) + +/* Get the current atom and spinlock it if current atom present. May not return NULL */ +static inline txn_atom * +get_current_atom_locked(void) +{ + txn_atom *atom; + + atom = get_current_atom_locked_nocheck(); + assert("zam-761", atom != NULL); + + return atom; +} + +extern txn_atom *jnode_get_atom(jnode *); + +extern void atom_wait_event(txn_atom *); +extern void atom_send_event(txn_atom *); + +extern void insert_into_atom_ovrwr_list(txn_atom * atom, jnode * node); +extern int capture_super_block(struct super_block *s); + +extern int jnodes_of_one_atom(jnode *, jnode *); + +/* See the comment on the function blocknrset.c:blocknr_set_add for the + calling convention of these three routines. */ +extern void blocknr_set_init(blocknr_set * bset); +extern void blocknr_set_destroy(blocknr_set * bset); +extern void blocknr_set_merge(blocknr_set * from, blocknr_set * into); +extern int blocknr_set_add_extent(txn_atom * atom, + blocknr_set * bset, + blocknr_set_entry ** new_bsep, + const reiser4_block_nr * start, const reiser4_block_nr * len); +extern int blocknr_set_add_pair(txn_atom * atom, + blocknr_set * bset, + blocknr_set_entry ** new_bsep, const reiser4_block_nr * a, const reiser4_block_nr * b); + +typedef int (*blocknr_set_actor_f) (txn_atom *, const reiser4_block_nr *, const reiser4_block_nr *, void *); + +extern int blocknr_set_iterator(txn_atom * atom, blocknr_set * bset, blocknr_set_actor_f actor, void *data, int delete); + +/* flush code takes care about how to fuse flush queues */ +extern void flush_init_atom(txn_atom * atom); +extern void flush_fuse_queues(txn_atom * large, txn_atom * small); + +/* INLINE FUNCTIONS */ + +#define spin_ordering_pred_atom(atom) \ + ( ( lock_counters() -> spin_locked_txnh == 0 ) && \ + ( lock_counters() -> spin_locked_jnode == 0 ) && \ + ( lock_counters() -> rw_locked_zlock == 0 ) && \ + ( lock_counters() -> rw_locked_dk == 0 ) && \ + ( lock_counters() -> rw_locked_tree == 0 ) ) + +#define spin_ordering_pred_txnh(txnh) \ + ( ( lock_counters() -> rw_locked_dk == 0 ) && \ + ( lock_counters() -> rw_locked_zlock == 0 ) && \ + ( lock_counters() -> rw_locked_tree == 0 ) ) + +#define spin_ordering_pred_txnmgr(tmgr) \ + ( ( lock_counters() -> spin_locked_atom == 0 ) && \ + ( lock_counters() -> spin_locked_txnh == 0 ) && \ + ( lock_counters() -> spin_locked_jnode == 0 ) && \ + ( lock_counters() -> rw_locked_zlock == 0 ) && \ + ( lock_counters() -> rw_locked_dk == 0 ) && \ + ( lock_counters() -> rw_locked_tree == 0 ) ) + +SPIN_LOCK_FUNCTIONS(atom, txn_atom, alock); +SPIN_LOCK_FUNCTIONS(txnh, txn_handle, hlock); +SPIN_LOCK_FUNCTIONS(txnmgr, txn_mgr, tmgr_lock); + +typedef enum { + FQ_IN_USE = 0x1 +} flush_queue_state_t; + +typedef struct flush_queue flush_queue_t; + +/* This is an accumulator for jnodes prepared for writing to disk. A flush queue + is filled by the jnode_flush() routine, and written to disk under memory + pressure or at atom commit time. */ +/* LOCKING: fq state and fq->atom are protected by guard spinlock, fq->nr_queued + field and fq->prepped list can be modified if atom is spin-locked and fq + object is "in-use" state. For read-only traversal of the fq->prepped list + and reading of the fq->nr_queued field it is enough to keep fq "in-use" or + only have atom spin-locked. */ +struct flush_queue { + /* linkage element is the first in this structure to make debugging + easier. See field in atom struct for description of list. */ + fq_list_link alink; + /* A spinlock to protect changes of fq state and fq->atom pointer */ + reiser4_spin_data guard; + /* flush_queue state: [in_use | ready] */ + flush_queue_state_t state; + /* A list which contains queued nodes, queued nodes are removed from any + * atom's list and put on this ->prepped one. */ + capture_list_head prepped1; + /* number of submitted i/o requests */ + atomic_t nr_submitted; + /* number of i/o errors */ + atomic_t nr_errors; + /* An atom this flush queue is attached to */ + txn_atom *atom; + /* A semaphore for waiting on i/o completion */ + struct semaphore io_sem; +#if REISER4_DEBUG + /* A thread which took this fq in exclusive use, NULL if fq is free, + * used for debugging. */ + struct task_struct *owner; +#endif +}; + +extern int fq_by_atom(txn_atom *, flush_queue_t **); +extern int fq_by_atom_gfp(txn_atom *, flush_queue_t **, int); +extern int fq_by_jnode(jnode *, flush_queue_t **); +extern int fq_by_jnode_gfp(jnode *, flush_queue_t **, int); +extern void fq_put_nolock(flush_queue_t *); +extern void fq_put(flush_queue_t *); +extern void fuse_fq(txn_atom * to, txn_atom * from); +extern void queue_jnode(flush_queue_t *, jnode *); +extern void mark_jnode_queued(flush_queue_t *, jnode *); + +extern int write_fq(flush_queue_t *, long *, int); +extern int current_atom_finish_all_fq(void); +extern void init_atom_fq_parts(txn_atom *); + +extern unsigned int txnmgr_get_max_atom_size(struct super_block *super); +extern reiser4_block_nr txnmgr_count_deleted_blocks (void); + +extern void znode_make_dirty(znode * node); +extern void jnode_make_dirty_locked(jnode * node); + +extern int sync_atom(txn_atom *atom); + +#if REISER4_DEBUG +extern int atom_fq_parts_are_clean (txn_atom *); +#endif + +extern void add_fq_to_bio(flush_queue_t *, struct bio *); +extern flush_queue_t *get_fq_for_current_atom(void); + +void protected_jnodes_init(protected_jnodes *list); +void protected_jnodes_done(protected_jnodes *list); +void invalidate_list(capture_list_head * head); + +/* Debugging */ +#if REISER4_DEBUG_OUTPUT +void print_atom(const char *prefix, txn_atom * atom); +void info_atom(const char *prefix, const txn_atom * atom); +#else +#define print_atom(p,a) noop +#define info_atom(p,a) noop +#endif + +# endif /* __REISER4_TXNMGR_H__ */ + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/type_safe_hash.h 2004-09-03 00:57:05.459209336 -0700 @@ -0,0 +1,332 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* A hash table class that uses hash chains (singly-linked) and is + parametrized to provide type safety. */ + +#ifndef __REISER4_TYPE_SAFE_HASH_H__ +#define __REISER4_TYPE_SAFE_HASH_H__ + +#include "debug.h" +#include "stats.h" + +#include +/* Step 1: Use TYPE_SAFE_HASH_DECLARE() to define the TABLE and LINK objects + based on the object type. You need to declare the item type before + this definition, define it after this definition. */ +#define TYPE_SAFE_HASH_DECLARE(PREFIX,ITEM_TYPE) \ + \ +typedef struct PREFIX##_hash_table_ PREFIX##_hash_table; \ +typedef struct PREFIX##_hash_link_ PREFIX##_hash_link; \ + \ +struct PREFIX##_hash_table_ \ +{ \ + ITEM_TYPE **_table; \ + __u32 _buckets; \ + tshash_stat *_stats; \ +}; \ + \ +struct PREFIX##_hash_link_ \ +{ \ + ITEM_TYPE *_next; \ +} + +/* Step 2: Define the object type of the hash: give it field of type + PREFIX_hash_link. */ + +/* Step 3: Use TYPE_SAFE_HASH_DEFINE to define the hash table interface using + the type and field name used in step 3. The arguments are: + + ITEM_TYPE The item type being hashed + KEY_TYPE The type of key being hashed + KEY_NAME The name of the key field within the item + LINK_NAME The name of the link field within the item, which you must make type PREFIX_hash_link) + HASH_FUNC The name of the hash function (or macro, takes const pointer to key) + EQ_FUNC The name of the equality function (or macro, takes const pointer to two keys) + + It implements these functions: + + prefix_hash_init Initialize the table given its size. + prefix_hash_insert Insert an item + prefix_hash_insert_index Insert an item w/ precomputed hash_index + prefix_hash_find Find an item by key + prefix_hash_find_index Find an item w/ precomputed hash_index + prefix_hash_remove Remove an item, returns 1 if found, 0 if not found + prefix_hash_remove_index Remove an item w/ precomputed hash_index + + If you'd like something to be done differently, feel free to ask me + for modifications. Additional features that could be added but + have not been: + + prefix_hash_remove_key Find and remove an item by key + prefix_hash_remove_key_index Find and remove an item by key w/ precomputed hash_index + + The hash_function currently receives only the key as an argument, + meaning it must somehow know the number of buckets. If this is a + problem let me know. + + This hash table uses a single-linked hash chain. This means + insertion is fast but deletion requires searching the chain. + + There is also the doubly-linked hash chain approach, under which + deletion requires no search but the code is longer and it takes two + pointers per item. + + The circularly-linked approach has the shortest code but requires + two pointers per bucket, doubling the size of the bucket array (in + addition to two pointers per item). +*/ +#define TYPE_SAFE_HASH_DEFINE(PREFIX,ITEM_TYPE,KEY_TYPE,KEY_NAME,LINK_NAME,HASH_FUNC,EQ_FUNC) \ + \ +static __inline__ void \ +PREFIX##_check_hash (PREFIX##_hash_table *table UNUSED_ARG, \ + __u32 hash UNUSED_ARG) \ +{ \ + assert("nikita-2780", hash < table->_buckets); \ +} \ + \ +static __inline__ int \ +PREFIX##_hash_init (PREFIX##_hash_table *hash, \ + __u32 buckets, \ + tshash_stat *stats) \ +{ \ + hash->_table = (ITEM_TYPE**) KMALLOC (sizeof (ITEM_TYPE*) * buckets); \ + hash->_buckets = buckets; \ + hash->_stats = stats; \ + if (hash->_table == NULL) \ + { \ + return RETERR(-ENOMEM); \ + } \ + xmemset (hash->_table, 0, sizeof (ITEM_TYPE*) * buckets); \ + ON_DEBUG(printk(#PREFIX "_hash_table: %i buckets\n", buckets)); \ + return 0; \ +} \ + \ +static __inline__ void \ +PREFIX##_hash_done (PREFIX##_hash_table *hash) \ +{ \ + if (REISER4_DEBUG && hash->_table != NULL) { \ + __u32 i; \ + for (i = 0 ; i < hash->_buckets ; ++ i) \ + assert("nikita-2905", hash->_table[i] == NULL); \ + } \ + if (hash->_table != NULL) \ + KFREE (hash->_table, sizeof (ITEM_TYPE*) * hash->_buckets); \ + hash->_table = NULL; \ +} \ + \ +static __inline__ void \ +PREFIX##_hash_prefetch_next (ITEM_TYPE *item) \ +{ \ + prefetch(item->LINK_NAME._next); \ +} \ + \ +static __inline__ void \ +PREFIX##_hash_prefetch_bucket (PREFIX##_hash_table *hash, \ + __u32 index) \ +{ \ + prefetch(hash->_table[index]); \ +} \ + \ +static __inline__ ITEM_TYPE* \ +PREFIX##_hash_find_index (PREFIX##_hash_table *hash, \ + __u32 hash_index, \ + KEY_TYPE const *find_key) \ +{ \ + ITEM_TYPE *item; \ + \ + PREFIX##_check_hash(hash, hash_index); \ + TSHASH_LOOKUP(hash->_stats); \ + \ + for (item = hash->_table[hash_index]; \ + item != NULL; \ + item = item->LINK_NAME._next) \ + { \ + TSHASH_SCANNED(hash->_stats); \ + prefetch(item->LINK_NAME._next); \ + prefetch(item->LINK_NAME._next + offsetof(ITEM_TYPE, KEY_NAME)); \ + if (EQ_FUNC (& item->KEY_NAME, find_key)) \ + { \ + return item; \ + } \ + } \ + \ + return NULL; \ +} \ + \ +static __inline__ ITEM_TYPE* \ +PREFIX##_hash_find_index_lru (PREFIX##_hash_table *hash, \ + __u32 hash_index, \ + KEY_TYPE const *find_key) \ +{ \ + ITEM_TYPE ** item = &hash->_table[hash_index]; \ + \ + PREFIX##_check_hash(hash, hash_index); \ + TSHASH_LOOKUP(hash->_stats); \ + \ + while (*item != NULL) { \ + TSHASH_SCANNED(hash->_stats); \ + prefetch(&(*item)->LINK_NAME._next); \ + if (EQ_FUNC (&(*item)->KEY_NAME, find_key)) { \ + ITEM_TYPE *found; \ + \ + found = *item; \ + *item = found->LINK_NAME._next; \ + found->LINK_NAME._next = hash->_table[hash_index]; \ + hash->_table[hash_index] = found; \ + return found; \ + } \ + item = &(*item)->LINK_NAME._next; \ + } \ + return NULL; \ +} \ + \ +static __inline__ int \ +PREFIX##_hash_remove_index (PREFIX##_hash_table *hash, \ + __u32 hash_index, \ + ITEM_TYPE *del_item) \ +{ \ + ITEM_TYPE ** hash_item_p = &hash->_table[hash_index]; \ + \ + PREFIX##_check_hash(hash, hash_index); \ + TSHASH_REMOVE(hash->_stats); \ + \ + while (*hash_item_p != NULL) { \ + TSHASH_SCANNED(hash->_stats); \ + prefetch(&(*hash_item_p)->LINK_NAME._next); \ + if (*hash_item_p == del_item) { \ + *hash_item_p = (*hash_item_p)->LINK_NAME._next; \ + return 1; \ + } \ + hash_item_p = &(*hash_item_p)->LINK_NAME._next; \ + } \ + return 0; \ +} \ + \ +static __inline__ void \ +PREFIX##_hash_insert_index (PREFIX##_hash_table *hash, \ + __u32 hash_index, \ + ITEM_TYPE *ins_item) \ +{ \ + PREFIX##_check_hash(hash, hash_index); \ + TSHASH_INSERT(hash->_stats); \ + \ + ins_item->LINK_NAME._next = hash->_table[hash_index]; \ + hash->_table[hash_index] = ins_item; \ +} \ + \ +static __inline__ void \ +PREFIX##_hash_insert_index_rcu (PREFIX##_hash_table *hash, \ + __u32 hash_index, \ + ITEM_TYPE *ins_item) \ +{ \ + PREFIX##_check_hash(hash, hash_index); \ + TSHASH_INSERT(hash->_stats); \ + \ + ins_item->LINK_NAME._next = hash->_table[hash_index]; \ + smp_wmb(); \ + hash->_table[hash_index] = ins_item; \ +} \ + \ +static __inline__ ITEM_TYPE* \ +PREFIX##_hash_find (PREFIX##_hash_table *hash, \ + KEY_TYPE const *find_key) \ +{ \ + return PREFIX##_hash_find_index (hash, HASH_FUNC(hash, find_key), find_key); \ +} \ + \ +static __inline__ ITEM_TYPE* \ +PREFIX##_hash_find_lru (PREFIX##_hash_table *hash, \ + KEY_TYPE const *find_key) \ +{ \ + return PREFIX##_hash_find_index_lru (hash, HASH_FUNC(hash, find_key), find_key); \ +} \ + \ +static __inline__ int \ +PREFIX##_hash_remove (PREFIX##_hash_table *hash, \ + ITEM_TYPE *del_item) \ +{ \ + return PREFIX##_hash_remove_index (hash, \ + HASH_FUNC(hash, &del_item->KEY_NAME), del_item); \ +} \ + \ +static __inline__ int \ +PREFIX##_hash_remove_rcu (PREFIX##_hash_table *hash, \ + ITEM_TYPE *del_item) \ +{ \ + return PREFIX##_hash_remove (hash, del_item); \ +} \ + \ +static __inline__ void \ +PREFIX##_hash_insert (PREFIX##_hash_table *hash, \ + ITEM_TYPE *ins_item) \ +{ \ + return PREFIX##_hash_insert_index (hash, \ + HASH_FUNC(hash, &ins_item->KEY_NAME), ins_item); \ +} \ + \ +static __inline__ void \ +PREFIX##_hash_insert_rcu (PREFIX##_hash_table *hash, \ + ITEM_TYPE *ins_item) \ +{ \ + return PREFIX##_hash_insert_index_rcu (hash, HASH_FUNC(hash, &ins_item->KEY_NAME), \ + ins_item); \ +} \ + \ +static __inline__ ITEM_TYPE * \ +PREFIX##_hash_first (PREFIX##_hash_table *hash, __u32 ind) \ +{ \ + ITEM_TYPE *first; \ + \ + for (first = NULL; ind < hash->_buckets; ++ ind) { \ + first = hash->_table[ind]; \ + if (first != NULL) \ + break; \ + } \ + return first; \ +} \ + \ +static __inline__ ITEM_TYPE * \ +PREFIX##_hash_next (PREFIX##_hash_table *hash, \ + ITEM_TYPE *item) \ +{ \ + ITEM_TYPE *next; \ + \ + if (item == NULL) \ + return NULL; \ + next = item->LINK_NAME._next; \ + if (next == NULL) \ + next = PREFIX##_hash_first (hash, HASH_FUNC(hash, &item->KEY_NAME) + 1); \ + return next; \ +} \ + \ +typedef struct {} PREFIX##_hash_dummy + +#define for_all_ht_buckets(table, head) \ +for ((head) = &(table) -> _table[ 0 ] ; \ + (head) != &(table) -> _table[ (table) -> _buckets ] ; ++ (head)) + +#define for_all_in_bucket(bucket, item, next, field) \ +for ((item) = *(bucket), (next) = (item) ? (item) -> field._next : NULL ; \ + (item) != NULL ; \ + (item) = (next), (next) = (item) ? (item) -> field._next : NULL ) + +#define for_all_in_htable(table, prefix, item, next) \ +for ((item) = prefix ## _hash_first ((table), 0), \ + (next) = prefix ## _hash_next ((table), (item)) ; \ + (item) != NULL ; \ + (item) = (next), \ + (next) = prefix ## _hash_next ((table), (item))) + +/* __REISER4_TYPE_SAFE_HASH_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/type_safe_list.h 2004-09-03 00:57:05.463208728 -0700 @@ -0,0 +1,436 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#ifndef __REISER4_TYPE_SAFE_LIST_H__ +#define __REISER4_TYPE_SAFE_LIST_H__ + +#include "debug.h" +/* A circular doubly linked list that differs from the previous + implementation because it is parametrized to provide + type safety. This data structure is also useful as a queue or stack. + + The "list template" consists of a set of types and methods for + implementing list operations. All of the types and methods + associated with a single list class are assigned unique names and + type signatures, thus allowing the compiler to verify correct + usage. + + The first parameter of a list class is the item type being stored + in the list. The list class maintains two pointers within each + item structure for its "next" and "prev" pointers. + + There are two structures associated with the list, in addition to + the item type itself. The "list link" contains the two pointers + that are embedded within the item itself. The "list head" also + contains two pointers which refer to the first item ("front") and + last item ("back") of the list. + + The list maintains a "circular" invariant, in that you can always + begin at the front and follow "next" pointers until eventually you + reach the same point. The "list head" is included within the + cycle, even though it does not have the correct item type. The + "list head" and "list link" types are different objects from the + user's perspective, but the core algorithms that operate on this + style of list treat the "list head" and "list link" as identical + types. That is why these algorithms are so simple. + + The implementation uses the same algorithms as those + in this file but uses only a single type "struct list_head". There + are two problems with this approach. First, there are no type + distinctions made between the two objects despite their distinct + types, which greatly increases the possibility for mistakes. For + example, the list_add function takes two "struct + list_head" arguments: the first is the item being inserted and the + second is the "struct list_head" which should precede the new + insertion to the list. You can use this function to insert at any + point in the list, but by far the most common list operations are + to insert at the front or back of the list. This common case + should accept two different argument types: a "list head" and an + "item", this allows for no confusion. + + The second problem with using a single "struct list_head" is that + it does not distinguish between list objects of distinct list + classes. If a single item can belong to two separate lists, there + is easily the possibility of a mistake being made that causes the + item to be added to a "list head" using the wrong "list link". By + using a parametrized list class we can statically detect such + mistakes, detecting mistakes as soon as they happen. + + To create a new list class takes several steps which are described + below. Suppose for this example that you would like to link + together items of type "rx_event". You should decide on + prefix-name to be used on all list functions and structures. For + example, the string "rx_event" can be as a prefix for all the list + operations, resulting in a "list head" named rx_event_list_head and + a "list link" named rx_event_list_link. The list operations on + this list class would be named "rx_event_list_empty", + "rx_event_list_init", "rx_event_list_push_front", + "rx_event_list_push_back", and so on. +*/ + +#define TYPE_SAFE_LIST_LINK_INIT(name) { &(name), &(name) } +#define TYPE_SAFE_LIST_HEAD_INIT(name) { (void *)&(name), (void *)&(name) } +#define TYPE_SAFE_LIST_LINK_ZERO { NULL, NULL } +#define TYPE_SAFE_LIST_HEAD_ZERO { NULL, NULL } + +#define TS_LINK_TO_ITEM(ITEM_TYPE,LINK_NAME,LINK) \ + ((ITEM_TYPE *)((char *)(LINK)-(unsigned long)(&((ITEM_TYPE *)0)->LINK_NAME))) + +/* Step 1: Use the TYPE_SAFE_LIST_DECLARE() macro to define the "list head" + and "list link" objects. This macro takes one arguments, the + prefix-name, which is prepended to every structure and function + name of the list class. Following the example, this will create + types named rx_event_list_head and rx_event_list_link. In the + example you would write: + + TYPE_SAFE_LIST_DECLARE(rx_event); + +*/ +#define TYPE_SAFE_LIST_DECLARE(PREFIX) \ + \ +typedef struct _##PREFIX##_list_head PREFIX##_list_head; \ +typedef struct _##PREFIX##_list_link PREFIX##_list_link; \ + \ +struct _##PREFIX##_list_link \ +{ \ + PREFIX##_list_link *_next; \ + PREFIX##_list_link *_prev; \ +}; \ + \ +struct _##PREFIX##_list_head \ +{ \ + PREFIX##_list_link *_next; \ + PREFIX##_list_link *_prev; \ +} + +/* Step 2: Once you have defined the two list classes, you should + define the item type you intend to use. The list classes must be + declared before the item type because the item type must contain an + embedded "list link" object. Following the example, you might define + rx_event as follows: + + typedef struct _rx_event rx_event; + + struct _rx_event + { + ... other members ... + + rx_event_list_link _link; + }; + + In this case we have given the rx_event a field named "_link" of + the appropriate type. +*/ + +/* Step 3: The final step will define the list-functions for a + specific list class using the macro TYPE_SAFE_LIST_DEFINE. There are + three arguments to the TYPE_SAFE_LIST_DEFINE macro: the prefix-name, the + item type name, and field name of the "list link" element within + the item type. In the above example you would supply "rx_event" as + the type name and "_link" as the field name (without quotes). + E.g., + + TYPE_SAFE_LIST_DEFINE(rx_event,rx_event,_link) + + The list class you define is now complete with the functions: + + rx_event_list_init Initialize a list_head + rx_event_list_clean Initialize a list_link + rx_event_list_is_clean True if list_link is not in a list + rx_event_list_push_front Insert to the front of the list + rx_event_list_push_back Insert to the back of the list + rx_event_list_insert_before Insert just before given item in the list + rx_event_list_insert_after Insert just after given item in the list + rx_event_list_remove Remove an item from anywhere in the list + rx_event_list_remove_clean Remove an item from anywhere in the list and clean link_item + rx_event_list_remove_get_next Remove an item from anywhere in the list and return the next element + rx_event_list_remove_get_prev Remove an item from anywhere in the list and return the prev element + rx_event_list_pop_front Remove and return the front of the list, cannot be empty + rx_event_list_pop_back Remove and return the back of the list, cannot be empty + rx_event_list_front Get the front of the list + rx_event_list_back Get the back of the list + rx_event_list_next Iterate front-to-back through the list + rx_event_list_prev Iterate back-to-front through the list + rx_event_list_end Test to end an iteration, either direction + rx_event_list_splice Join two lists at the head + rx_event_list_empty True if the list is empty + rx_event_list_object_ok Check that list element satisfies double + list invariants. For debugging. + + To iterate over such a list use a for-loop such as: + + rx_event_list_head *head = ...; + rx_event *item; + + for (item = rx_event_list_front (head); + ! rx_event_list_end (head, item); + item = rx_event_list_next (item)) + {...} +*/ +#define TYPE_SAFE_LIST_DEFINE(PREFIX,ITEM_TYPE,LINK_NAME) \ + \ +static __inline__ int \ +PREFIX##_list_link_invariant (const PREFIX##_list_link *_link) \ +{ \ + return (_link != NULL) && \ + (_link->_prev != NULL) && (_link->_next != NULL ) && \ + (_link->_prev->_next == _link) && \ + (_link->_next->_prev == _link); \ +} \ + \ +static __inline__ void \ +PREFIX##_list_link_ok (const PREFIX##_list_link *_link UNUSED_ARG) \ +{ \ + assert ("nikita-1054", PREFIX##_list_link_invariant (_link)); \ +} \ + \ +static __inline__ void \ +PREFIX##_list_object_ok (const ITEM_TYPE *item) \ +{ \ + PREFIX##_list_link_ok (&item->LINK_NAME); \ +} \ + \ +static __inline__ void \ +PREFIX##_list_init (PREFIX##_list_head *head) \ +{ \ + head->_next = (PREFIX##_list_link*) head; \ + head->_prev = (PREFIX##_list_link*) head; \ +} \ + \ +static __inline__ void \ +PREFIX##_list_clean (ITEM_TYPE *item) \ +{ \ + PREFIX##_list_link *_link = &item->LINK_NAME; \ + \ + _link->_next = _link; \ + _link->_prev = _link; \ +} \ + \ +static __inline__ int \ +PREFIX##_list_is_clean (const ITEM_TYPE *item) \ +{ \ + const PREFIX##_list_link *_link = &item->LINK_NAME; \ + \ + PREFIX##_list_link_ok (_link); \ + return (_link == _link->_next) && (_link == _link->_prev); \ +} \ + \ +static __inline__ void \ +PREFIX##_list_insert_int (PREFIX##_list_link *next, \ + PREFIX##_list_link *item) \ +{ \ + PREFIX##_list_link *prev = next->_prev; \ + PREFIX##_list_link_ok (next); \ + PREFIX##_list_link_ok (prev); \ + next->_prev = item; \ + item->_next = next; \ + item->_prev = prev; \ + prev->_next = item; \ + PREFIX##_list_link_ok (next); \ + PREFIX##_list_link_ok (prev); \ + PREFIX##_list_link_ok (item); \ +} \ + \ +static __inline__ void \ +PREFIX##_list_push_front (PREFIX##_list_head *head, \ + ITEM_TYPE *item) \ +{ \ + PREFIX##_list_insert_int (head->_next, & item->LINK_NAME); \ +} \ + \ +static __inline__ void \ +PREFIX##_list_push_back (PREFIX##_list_head *head, \ + ITEM_TYPE *item) \ +{ \ + PREFIX##_list_insert_int ((PREFIX##_list_link *) head, & item->LINK_NAME); \ +} \ + \ +static __inline__ void \ +PREFIX##_list_insert_before (ITEM_TYPE *reference, \ + ITEM_TYPE *item) \ +{ \ + PREFIX##_list_insert_int (& reference->LINK_NAME, & item->LINK_NAME); \ +} \ + \ +static __inline__ void \ +PREFIX##_list_insert_after (ITEM_TYPE *reference, \ + ITEM_TYPE *item) \ +{ \ + PREFIX##_list_insert_int (reference->LINK_NAME._next, & item->LINK_NAME); \ +} \ + \ +static __inline__ PREFIX##_list_link* \ +PREFIX##_list_remove_int (PREFIX##_list_link *list_link) \ +{ \ + PREFIX##_list_link *next = list_link->_next; \ + PREFIX##_list_link *prev = list_link->_prev; \ + PREFIX##_list_link_ok (list_link); \ + PREFIX##_list_link_ok (next); \ + PREFIX##_list_link_ok (prev); \ + next->_prev = prev; \ + prev->_next = next; \ + PREFIX##_list_link_ok (next); \ + PREFIX##_list_link_ok (prev); \ + return list_link; \ +} \ + \ +static __inline__ void \ +PREFIX##_list_remove (ITEM_TYPE *item) \ +{ \ + PREFIX##_list_remove_int (& item->LINK_NAME); \ +} \ + \ +static __inline__ void \ +PREFIX##_list_remove_clean (ITEM_TYPE *item) \ +{ \ + PREFIX##_list_remove_int (& item->LINK_NAME); \ + PREFIX##_list_clean (item); \ +} \ + \ +static __inline__ ITEM_TYPE* \ +PREFIX##_list_remove_get_next (ITEM_TYPE *item) \ +{ \ + PREFIX##_list_link *next = item->LINK_NAME._next; \ + PREFIX##_list_remove_int (& item->LINK_NAME); \ + return TS_LINK_TO_ITEM(ITEM_TYPE,LINK_NAME,next); \ +} \ + \ +static __inline__ ITEM_TYPE* \ +PREFIX##_list_remove_get_prev (ITEM_TYPE *item) \ +{ \ + PREFIX##_list_link *prev = item->LINK_NAME._prev; \ + PREFIX##_list_remove_int (& item->LINK_NAME); \ + return TS_LINK_TO_ITEM(ITEM_TYPE,LINK_NAME,prev); \ +} \ + \ +static __inline__ int \ +PREFIX##_list_empty (const PREFIX##_list_head *head) \ +{ \ + return head == (PREFIX##_list_head*) head->_next; \ +} \ + \ +static __inline__ ITEM_TYPE* \ +PREFIX##_list_pop_front (PREFIX##_list_head *head) \ +{ \ + assert ("nikita-1913", ! PREFIX##_list_empty (head)); \ + return TS_LINK_TO_ITEM(ITEM_TYPE,LINK_NAME,PREFIX##_list_remove_int (head->_next)); \ +} \ + \ +static __inline__ ITEM_TYPE* \ +PREFIX##_list_pop_back (PREFIX##_list_head *head) \ +{ \ + assert ("nikita-1914", ! PREFIX##_list_empty (head)); /* WWI started */ \ + return TS_LINK_TO_ITEM(ITEM_TYPE,LINK_NAME,PREFIX##_list_remove_int (head->_prev)); \ +} \ + \ +static __inline__ ITEM_TYPE* \ +PREFIX##_list_front (const PREFIX##_list_head *head) \ +{ \ + return TS_LINK_TO_ITEM(ITEM_TYPE,LINK_NAME,head->_next); \ +} \ + \ +static __inline__ ITEM_TYPE* \ +PREFIX##_list_back (const PREFIX##_list_head *head) \ +{ \ + return TS_LINK_TO_ITEM(ITEM_TYPE,LINK_NAME,head->_prev); \ +} \ + \ +static __inline__ ITEM_TYPE* \ +PREFIX##_list_next (const ITEM_TYPE *item) \ +{ \ + return TS_LINK_TO_ITEM(ITEM_TYPE,LINK_NAME,item->LINK_NAME._next); \ +} \ + \ +static __inline__ ITEM_TYPE* \ +PREFIX##_list_prev (const ITEM_TYPE *item) \ +{ \ + return TS_LINK_TO_ITEM(ITEM_TYPE,LINK_NAME,item->LINK_NAME._prev); \ +} \ + \ +static __inline__ int \ +PREFIX##_list_end (const PREFIX##_list_head *head, \ + const ITEM_TYPE *item) \ +{ \ + return ((PREFIX##_list_link *) head) == (& item->LINK_NAME); \ +} \ + \ +static __inline__ void \ +PREFIX##_list_splice (PREFIX##_list_head *head_join, \ + PREFIX##_list_head *head_empty) \ +{ \ + if (PREFIX##_list_empty (head_empty)) { \ + return; \ + } \ + \ + head_empty->_prev->_next = (PREFIX##_list_link*) head_join; \ + head_empty->_next->_prev = head_join->_prev; \ + \ + head_join->_prev->_next = head_empty->_next; \ + head_join->_prev = head_empty->_prev; \ + \ + PREFIX##_list_link_ok ((PREFIX##_list_link*) head_join); \ + PREFIX##_list_link_ok (head_join->_prev); \ + PREFIX##_list_link_ok (head_join->_next); \ + \ + PREFIX##_list_init (head_empty); \ +} \ + \ +static __inline__ void \ +PREFIX##_list_split(PREFIX##_list_head *head_split, \ + PREFIX##_list_head *head_new, \ + ITEM_TYPE *item) \ +{ \ + assert("vs-1471", PREFIX##_list_empty(head_new)); \ + \ + /* attach to new list */ \ + head_new->_next = (& item->LINK_NAME); \ + head_new->_prev = head_split->_prev; \ + \ + /* cut from old list */ \ + item->LINK_NAME._prev->_next = (PREFIX##_list_link*)head_split; \ + head_split->_prev = item->LINK_NAME._prev; \ + \ + /* link new list */ \ + head_new->_next->_prev = (PREFIX##_list_link*)head_new; \ + head_new->_prev->_next = (PREFIX##_list_link*)head_new; \ +} \ + \ +static __inline__ void \ +PREFIX##_list_check (const PREFIX##_list_head *head) \ +{ \ + const PREFIX##_list_link *link; \ + \ + for (link = head->_next ; link != ((PREFIX##_list_link *) head) ; link = link->_next) \ + PREFIX##_list_link_ok (link); \ +} \ + \ +typedef struct { int foo; } PREFIX##_list_dummy_decl + +/* The final typedef is to allow a semicolon at the end of + * TYPE_SAFE_LIST_DEFINE(); */ + +#define for_all_type_safe_list(prefix, head, item) \ + for(item = prefix ## _list_front(head), \ + prefetch(prefix ## _list_next(item)); \ + !prefix ## _list_end(head, item) ; \ + item = prefix ## _list_next(item), \ + prefetch(prefix ## _list_next(item))) + +#define for_all_type_safe_list_safe(prefix, head, item, next) \ + for(item = prefix ## _list_front(head), \ + next = prefix ## _list_next(item); \ + !prefix ## _list_end(head, item) ; \ + item = next, \ + next = prefix ## _list_next(item)) + +/* __REISER4_TYPE_SAFE_LIST_H__ */ +#endif + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/vfs_ops.c 2004-09-03 00:57:05.471207512 -0700 @@ -0,0 +1,1715 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Interface to VFS. Reiser4 {super|export|dentry}_operations are defined + here. */ + +#include "forward.h" +#include "debug.h" +#include "dformat.h" +#include "coord.h" +#include "plugin/item/item.h" +#include "plugin/file/file.h" +#include "plugin/security/perm.h" +#include "plugin/disk_format/disk_format.h" +#include "plugin/dir/dir.h" +#include "plugin/plugin.h" +#include "plugin/plugin_set.h" +#include "plugin/object.h" +#include "txnmgr.h" +#include "jnode.h" +#include "znode.h" +#include "block_alloc.h" +#include "tree.h" +#include "log.h" +#include "vfs_ops.h" +#include "inode.h" +#include "page_cache.h" +#include "ktxnmgrd.h" +#include "super.h" +#include "reiser4.h" +#include "kattr.h" +#include "entd.h" +#include "emergency_flush.h" +#include "prof.h" +#include "repacker.h" +#include "init_super.h" +#include "status_flags.h" +#include "flush.h" +#include "dscale.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* super operations */ + +static struct inode *reiser4_alloc_inode(struct super_block *super); +static void reiser4_destroy_inode(struct inode *inode); +static void reiser4_drop_inode(struct inode *); +static void reiser4_delete_inode(struct inode *); +static void reiser4_write_super(struct super_block *); +static int reiser4_statfs(struct super_block *, struct kstatfs *); +static int reiser4_show_options(struct seq_file *m, struct vfsmount *mnt); +static void reiser4_sync_inodes(struct super_block *s, struct writeback_control * wbc); + +extern struct dentry_operations reiser4_dentry_operation; + +struct file_system_type reiser4_fs_type; + +/* ->statfs() VFS method in reiser4 super_operations */ +static int +reiser4_statfs(struct super_block *super /* super block of file + * system in queried */ , + struct kstatfs *statfs /* buffer to fill with + * statistics */ ) +{ + sector_t total; + sector_t reserved; + sector_t free; + sector_t forroot; + sector_t deleted; + reiser4_context ctx; + + assert("nikita-408", super != NULL); + assert("nikita-409", statfs != NULL); + + init_context(&ctx, super); + reiser4_stat_inc(vfs_calls.statfs); + + statfs->f_type = statfs_type(super); + statfs->f_bsize = super->s_blocksize; + + /* + * 5% of total block space is reserved. This is needed for flush and + * for truncates (so that we are able to perform truncate/unlink even + * on the otherwise completely full file system). If this reservation + * is hidden from statfs(2), users will mistakenly guess that they + * have enough free space to complete some operation, which is + * frustrating. + * + * Another possible solution is to subtract ->blocks_reserved from + * ->f_bfree, but changing available space seems less intrusive than + * letting user to see 5% of disk space to be used directly after + * mkfs. + */ + total = reiser4_block_count(super); + reserved = get_super_private(super)->blocks_reserved; + deleted = txnmgr_count_deleted_blocks(); + free = reiser4_free_blocks(super) + deleted; + forroot = reiser4_reserved_blocks(super, 0, 0); + + /* These counters may be in inconsistent state because we take the + * values without keeping any global spinlock. Here we do a sanity + * check that free block counter does not exceed the number of all + * blocks. */ + if (free > total) + free = total; + statfs->f_blocks = total - reserved; + /* make sure statfs->f_bfree is never larger than statfs->f_blocks */ + if (free > reserved) + free -= reserved; + else + free = 0; + statfs->f_bfree = free; + + if (free > forroot) + free -= forroot; + else + free = 0; + statfs->f_bavail = free; + +/* FIXME: Seems that various df implementations are way unhappy by such big numbers. + So we will leave those as zeroes. + statfs->f_files = oids_used(super) + oids_free(super); + statfs->f_ffree = oids_free(super); +*/ + + /* maximal acceptable name length depends on directory plugin. */ + assert("nikita-3351", super->s_root->d_inode != NULL); + statfs->f_namelen = reiser4_max_filename_len(super->s_root->d_inode); + reiser4_exit_context(&ctx); + return 0; +} + +/* this is called whenever mark_inode_dirty is to be called. Stat-data are + * updated in the tree. */ +reiser4_internal int +reiser4_mark_inode_dirty(struct inode *inode) +{ + assert("vs-1207", is_in_reiser4_context()); + return reiser4_update_sd(inode); +} + +/* update inode stat-data by calling plugin */ +reiser4_internal int +reiser4_update_sd(struct inode *object) +{ + file_plugin *fplug; + + assert("nikita-2338", object != NULL); + /* check for read-only file system. */ + if (IS_RDONLY(object)) + return 0; + + fplug = inode_file_plugin(object); + assert("nikita-2339", fplug != NULL); + return fplug->write_sd_by_inode(object); +} + +/* helper function: increase inode nlink count and call plugin method to save + updated stat-data. + + Used by link/create and during creation of dot and dotdot in mkdir +*/ +reiser4_internal int +reiser4_add_nlink(struct inode *object /* object to which link is added */ , + struct inode *parent /* parent where new entry will be */ , + int write_sd_p /* true if stat-data has to be + * updated */ ) +{ + file_plugin *fplug; + int result; + + assert("nikita-1351", object != NULL); + + fplug = inode_file_plugin(object); + assert("nikita-1445", fplug != NULL); + + /* ask plugin whether it can add yet another link to this + object */ + if (!fplug->can_add_link(object)) + return RETERR(-EMLINK); + + assert("nikita-2211", fplug->add_link != NULL); + /* call plugin to do actual addition of link */ + result = fplug->add_link(object, parent); + + mark_inode_update(object, write_sd_p); + + /* optionally update stat data */ + if (result == 0 && write_sd_p) + result = fplug->write_sd_by_inode(object); + return result; +} + +/* helper function: decrease inode nlink count and call plugin method to save + updated stat-data. + + Used by unlink/create +*/ +reiser4_internal int +reiser4_del_nlink(struct inode *object /* object from which link is + * removed */ , + struct inode *parent /* parent where entry was */ , + int write_sd_p /* true is stat-data has to be + * updated */ ) +{ + file_plugin *fplug; + int result; + + assert("nikita-1349", object != NULL); + + fplug = inode_file_plugin(object); + assert("nikita-1350", fplug != NULL); + assert("nikita-1446", object->i_nlink > 0); + assert("nikita-2210", fplug->rem_link != NULL); + + /* call plugin to do actual deletion of link */ + result = fplug->rem_link(object, parent); + mark_inode_update(object, write_sd_p); + /* optionally update stat data */ + if (result == 0 && write_sd_p) + result = fplug->write_sd_by_inode(object); + return result; +} + +/* slab for reiser4_dentry_fsdata */ +static kmem_cache_t *dentry_fsdata_slab; + +/* + * initializer for dentry_fsdata_slab called during boot or module load. + */ +reiser4_internal int init_dentry_fsdata(void) +{ + dentry_fsdata_slab = kmem_cache_create("dentry_fsdata", + sizeof (reiser4_dentry_fsdata), + 0, + SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + NULL, + NULL); + return (dentry_fsdata_slab == NULL) ? RETERR(-ENOMEM) : 0; +} + +/* + * dual to init_dentry_fsdata(). Called on module unload. + */ +reiser4_internal void done_dentry_fsdata(void) +{ + kmem_cache_destroy(dentry_fsdata_slab); +} + + +/* Return and lazily allocate if necessary per-dentry data that we + attach to each dentry. */ +reiser4_internal reiser4_dentry_fsdata * +reiser4_get_dentry_fsdata(struct dentry *dentry /* dentry + * queried */ ) +{ + assert("nikita-1365", dentry != NULL); + + if (dentry->d_fsdata == NULL) { + reiser4_stat_inc(vfs_calls.private_data_alloc); + dentry->d_fsdata = kmem_cache_alloc(dentry_fsdata_slab, + GFP_KERNEL); + if (dentry->d_fsdata == NULL) + return ERR_PTR(RETERR(-ENOMEM)); + xmemset(dentry->d_fsdata, 0, sizeof (reiser4_dentry_fsdata)); + } + return dentry->d_fsdata; +} + +/* opposite to reiser4_get_dentry_fsdata(), returns per-dentry data into slab + * allocator */ +reiser4_internal void +reiser4_free_dentry_fsdata(struct dentry *dentry /* dentry released */ ) +{ + if (dentry->d_fsdata != NULL) { + kmem_cache_free(dentry_fsdata_slab, dentry->d_fsdata); + dentry->d_fsdata = NULL; + } +} + +/* Release reiser4 dentry. This is d_op->d_release() method. */ +static void +reiser4_d_release(struct dentry *dentry /* dentry released */ ) +{ + reiser4_free_dentry_fsdata(dentry); +} + +/* slab for reiser4_dentry_fsdata */ +static kmem_cache_t *file_fsdata_slab; + +/* + * initialize file_fsdata_slab. This is called during boot or module load. + */ +reiser4_internal int init_file_fsdata(void) +{ + file_fsdata_slab = kmem_cache_create("file_fsdata", + sizeof (reiser4_file_fsdata), + 0, + SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + NULL, + NULL); + return (file_fsdata_slab == NULL) ? RETERR(-ENOMEM) : 0; +} + +/* + * dual to init_file_fsdata(). Called during module unload. + */ +reiser4_internal void done_file_fsdata(void) +{ + kmem_cache_destroy(file_fsdata_slab); +} + +/* + * Create reiser4 specific per-file data: reiser4_file_fsdata. + */ +reiser4_internal reiser4_file_fsdata * +create_fsdata(struct file *file, int gfp) +{ + reiser4_file_fsdata *fsdata; + + fsdata = kmem_cache_alloc(file_fsdata_slab, gfp); + if (fsdata != NULL) { + xmemset(fsdata, 0, sizeof *fsdata); + fsdata->ra.max_window_size = VM_MAX_READAHEAD * 1024; + fsdata->back = file; + readdir_list_clean(fsdata); + } + return fsdata; +} + +/* Return and lazily allocate if necessary per-file data that we attach + to each struct file. */ +reiser4_internal reiser4_file_fsdata * +reiser4_get_file_fsdata(struct file *f /* file + * queried */ ) +{ + assert("nikita-1603", f != NULL); + + if (f->private_data == NULL) { + reiser4_file_fsdata *fsdata; + struct inode *inode; + + reiser4_stat_inc(vfs_calls.private_data_alloc); + fsdata = create_fsdata(f, GFP_KERNEL); + if (fsdata == NULL) + return ERR_PTR(RETERR(-ENOMEM)); + + inode = f->f_dentry->d_inode; + spin_lock_inode(inode); + if (f->private_data == NULL) { + f->private_data = fsdata; + fsdata = NULL; + } + spin_unlock_inode(inode); + if (fsdata != NULL) + /* other thread initialized ->fsdata */ + kmem_cache_free(file_fsdata_slab, fsdata); + } + assert("nikita-2665", f->private_data != NULL); + return f->private_data; +} + +/* + * Dual to create_fsdata(). Free reiser4_file_fsdata. + */ +reiser4_internal void +reiser4_free_fsdata(reiser4_file_fsdata *fsdata) +{ + if (fsdata != NULL) + kmem_cache_free(file_fsdata_slab, fsdata); +} + +/* + * Dual to reiser4_get_file_fsdata(). + */ +reiser4_internal void +reiser4_free_file_fsdata(struct file *f) +{ + reiser4_file_fsdata *fsdata; + fsdata = f->private_data; + if (fsdata != NULL) { + readdir_list_remove_clean(fsdata); + if (fsdata->cursor == NULL) + reiser4_free_fsdata(fsdata); + } + f->private_data = NULL; +} + +/* our ->read_inode() is no-op. Reiser4 inodes should be loaded + through fs/reiser4/inode.c:reiser4_iget() */ +static void +noop_read_inode(struct inode *inode UNUSED_ARG) +{ +} + +/* initialization and shutdown */ + +/* slab cache for inodes */ +static kmem_cache_t *inode_cache; + +/* initalisation function passed to the kmem_cache_create() to init new pages + grabbed by our inodecache. */ +static void +init_once(void *obj /* pointer to new inode */ , + kmem_cache_t * cache UNUSED_ARG /* slab cache */ , + unsigned long flags /* cache flags */ ) +{ + reiser4_inode_object *info; + + info = obj; + + if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) { + /* NOTE-NIKITA add here initializations for locks, list heads, + etc. that will be added to our private inode part. */ + inode_init_once(&info->vfs_inode); + readdir_list_init(get_readdir_list(&info->vfs_inode)); + init_rwsem(&info->p.coc_sem); + sema_init(&info->p.loading, 1); + ON_DEBUG(info->p.nr_jnodes = 0); + INIT_RADIX_TREE(jnode_tree_by_reiser4_inode(&info->p), GFP_ATOMIC); + ON_DEBUG(info->p.captured_eflushed = 0); + ON_DEBUG(info->p.anonymous_eflushed = 0); + ON_DEBUG(inode_jnodes_list_init(&info->p.jnodes_list)); + } +} + +/* initialize slab cache where reiser4 inodes will live */ +reiser4_internal int +init_inodecache(void) +{ + inode_cache = kmem_cache_create("reiser4_inode", + sizeof (reiser4_inode_object), + 0, + SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + init_once, + NULL); + return (inode_cache != NULL) ? 0 : RETERR(-ENOMEM); +} + +/* initialize slab cache where reiser4 inodes lived */ +static void +destroy_inodecache(void) +{ + if (kmem_cache_destroy(inode_cache) != 0) + warning("nikita-1695", "not all inodes were freed"); +} + +/* ->alloc_inode() super operation: allocate new inode */ +static struct inode * +reiser4_alloc_inode(struct super_block *super UNUSED_ARG /* super block new + * inode is + * allocated for */ ) +{ + reiser4_inode_object *obj; + + assert("nikita-1696", super != NULL); + reiser4_stat_inc_at(super, vfs_calls.alloc_inode); + obj = kmem_cache_alloc(inode_cache, SLAB_KERNEL); + if (obj != NULL) { + reiser4_inode *info; + + info = &obj->p; + + info->hset = info->pset = plugin_set_get_empty(); + info->extmask = 0; + info->locality_id = 0ull; + info->plugin_mask = 0; +#if !REISER4_INO_IS_OID + info->oid_hi = 0; +#endif + seal_init(&info->sd_seal, NULL, NULL); + coord_init_invalid(&info->sd_coord, NULL); + info->cluster_shift = 0; + info->crypt = NULL; + info->flags = 0; + spin_inode_object_init(info); + + /* initizalize inode's jnode */ + /*jnode_init(&info->inode_jnode, current_tree, JNODE_INODE); + atomic_set(&info->inode_jnode.x_count, 1);*/ + info->vroot = UBER_TREE_ADDR; + return &obj->vfs_inode; + } else + return NULL; +} + +/* ->destroy_inode() super operation: recycle inode */ +static void +reiser4_destroy_inode(struct inode *inode /* inode being destroyed */) +{ + reiser4_inode *info; + + reiser4_stat_inc_at(inode->i_sb, vfs_calls.destroy_inode); + + info = reiser4_inode_data(inode); + + assert("vs-1220", jnode_tree_by_reiser4_inode(info)->rnode == NULL); + assert("vs-1222", info->captured_eflushed == 0); + assert("vs-1428", info->anonymous_eflushed == 0); + assert("zam-1050", info->nr_jnodes == 0); + + if (!is_bad_inode(inode) && is_inode_loaded(inode)) { + file_plugin * fplug = inode_file_plugin(inode); + if (fplug->destroy_inode != NULL) + fplug->destroy_inode(inode); + } + dispose_cursors(inode); + if (info->pset) + plugin_set_put(info->pset); + + /* FIXME: assert that info's page radix tree is empty */ + /*assert("nikita-2872", list_empty(&info->moved_pages));*/ + + /* cannot add similar assertion about ->i_list as prune_icache return + * inode into slab with dangling ->list.{next,prev}. This is safe, + * because they are re-initialized in the new_inode(). */ + assert("nikita-2895", list_empty(&inode->i_dentry)); + assert("nikita-2896", hlist_unhashed(&inode->i_hash)); + assert("nikita-2898", readdir_list_empty(get_readdir_list(inode))); + kmem_cache_free(inode_cache, container_of(info, reiser4_inode_object, p)); +} + +/* our ->drop_inode() method. This is called by iput_final() when last + * reference on inode is released */ +static void +reiser4_drop_inode(struct inode *object) +{ + file_plugin *fplug; + + assert("nikita-2643", object != NULL); + + /* -not- creating context in this method, because it is frequently + called and all existing ->not_linked() methods are one liners. */ + + fplug = inode_file_plugin(object); + /* fplug is NULL for fake inode */ + if (fplug != NULL) { + assert("nikita-3251", fplug->drop != NULL); + fplug->drop(object); + } else + generic_forget_inode(object); +} + +/* + * Called by reiser4_sync_inodes(), during speculative write-back (through + * pdflush, or balance_dirty_pages()). + */ +static void +writeout(struct super_block *sb, struct writeback_control *wbc) +{ + long written = 0; + int repeats = 0; + + /* + * Performs early flushing, trying to free some memory. If there is + * nothing to flush, commits some atoms. + */ + + /* reiser4 has its own means of periodical write-out */ + if (wbc->for_kupdate) + return; + + /* Commit all atoms if reiser4_writepages() is called from sys_sync() or + sys_fsync(). */ + if (wbc->sync_mode != WB_SYNC_NONE) { + txnmgr_force_commit_all(sb, 1); + return; + } + + do { + long nr_submitted = 0; + struct inode *fake; + + fake = get_super_fake(sb); + if (fake != NULL) { + struct address_space *mapping; + + mapping = fake->i_mapping; + /* do not put more requests to overload write queue */ + if (wbc->nonblocking && + bdi_write_congested(mapping->backing_dev_info)) { + + blk_run_address_space(mapping); + /*blk_run_queues();*/ + wbc->encountered_congestion = 1; + break; + } + } + repeats ++; + flush_some_atom(&nr_submitted, wbc, JNODE_FLUSH_WRITE_BLOCKS); + if (!nr_submitted) + break; + + wbc->nr_to_write -= nr_submitted; + + written += nr_submitted; + + } while (wbc->nr_to_write > 0); + +} + +/* ->sync_inodes() method. This is called by pdflush, and synchronous + * writeback (throttling by balance_dirty_pages()). */ +static void +reiser4_sync_inodes(struct super_block * sb, struct writeback_control * wbc) +{ + reiser4_context ctx; + + init_context(&ctx, sb); + wbc->older_than_this = NULL; + + /* + * What we are trying to do here is to capture all "anonymous" pages. + */ + capture_reiser4_inodes(sb, wbc); + spin_unlock(&inode_lock); + writeout(sb, wbc); + + /* avoid recursive calls to ->sync_inodes */ + context_set_commit_async(&ctx); + reiser4_exit_context(&ctx); + spin_lock(&inode_lock); +} + +void reiser4_throttle_write(struct inode * inode) +{ + txn_restart_current(); + balance_dirty_pages_ratelimited(inode->i_mapping); +} + +/* ->delete_inode() super operation */ +static void +reiser4_delete_inode(struct inode *object) +{ + reiser4_context ctx; + + init_context(&ctx, object->i_sb); + reiser4_stat_inc(vfs_calls.delete_inode); + if (is_inode_loaded(object)) { + file_plugin *fplug; + + fplug = inode_file_plugin(object); + if (fplug != NULL && fplug->delete != NULL) + fplug->delete(object); + } + + object->i_blocks = 0; + clear_inode(object); + reiser4_exit_context(&ctx); +} + +/* ->delete_inode() super operation */ +static void +reiser4_clear_inode(struct inode *object) +{ + reiser4_inode *r4_inode; + + r4_inode = reiser4_inode_data(object); + assert("vs-1688", (r4_inode->anonymous_eflushed == 0 && + r4_inode->captured_eflushed == 0 && + r4_inode->nr_jnodes == 0)); +} + +const char *REISER4_SUPER_MAGIC_STRING = "ReIsEr4"; +const int REISER4_MAGIC_OFFSET = 16 * 4096; /* offset to magic string from the + * beginning of device */ + +/* type of option parseable by parse_option() */ +typedef enum { + /* value of option is arbitrary string */ + OPT_STRING, + /* option specifies bit in a bitmask */ + OPT_BIT, + /* value of option should conform to sprintf() format */ + OPT_FORMAT, + /* option can take one of predefined values */ + OPT_ONEOF, +} opt_type_t; + +typedef struct opt_bitmask_bit { + const char *bit_name; + int bit_nr; +} opt_bitmask_bit; + +/* description of option parseable by parse_option() */ +typedef struct opt_desc { + /* option name. + + parsed portion of string has a form "name=value". + */ + const char *name; + /* type of option */ + opt_type_t type; + union { + /* where to store value of string option (type == OPT_STRING) */ + char **string; + /* description of bits for bit option (type == OPT_BIT) */ + struct { + int nr; + void *addr; + } bit; + /* description of format and targets for format option (type + == OPT_FORMAT) */ + struct { + const char *format; + int nr_args; + void *arg1; + void *arg2; + void *arg3; + void *arg4; + } f; + struct { + int *result; + const char *list[10]; + } oneof; + struct { + void *addr; + int nr_bits; + opt_bitmask_bit *bits; + } bitmask; + } u; +} opt_desc_t; + +/* parse one option */ +static int +parse_option(char *opt_string /* starting point of parsing */ , + opt_desc_t * opt /* option description */ ) +{ + /* foo=bar, + ^ ^ ^ + | | +-- replaced to '\0' + | +-- val_start + +-- opt_string + */ + char *val_start; + int result; + const char *err_msg; + + /* NOTE-NIKITA think about using lib/cmdline.c functions here. */ + + val_start = strchr(opt_string, '='); + if (val_start != NULL) { + *val_start = '\0'; + ++val_start; + } + + err_msg = NULL; + result = 0; + switch (opt->type) { + case OPT_STRING: + if (val_start == NULL) { + err_msg = "String arg missing"; + result = RETERR(-EINVAL); + } else + *opt->u.string = val_start; + break; + case OPT_BIT: + if (val_start != NULL) + err_msg = "Value ignored"; + else + set_bit(opt->u.bit.nr, opt->u.bit.addr); + break; + case OPT_FORMAT: + if (val_start == NULL) { + err_msg = "Formatted arg missing"; + result = RETERR(-EINVAL); + break; + } + if (sscanf(val_start, opt->u.f.format, + opt->u.f.arg1, opt->u.f.arg2, opt->u.f.arg3, opt->u.f.arg4) != opt->u.f.nr_args) { + err_msg = "Wrong conversion"; + result = RETERR(-EINVAL); + } + break; + case OPT_ONEOF:{ + int i = 0; + err_msg = "Wrong option value"; + result = RETERR(-EINVAL); + while ( opt->u.oneof.list[i] ) { + if ( !strcmp(opt->u.oneof.list[i], val_start) ) { + result = 0; + *opt->u.oneof.result = i; +printk("%s choice is %d\n",opt->name, i); + break; + } + i++; + } + break; + } + default: + wrong_return_value("nikita-2100", "opt -> type"); + break; + } + if (err_msg != NULL) { + warning("nikita-2496", "%s when parsing option \"%s%s%s\"", + err_msg, opt->name, val_start ? "=" : "", val_start ? : ""); + } + return result; +} + +/* parse options */ +static int +parse_options(char *opt_string /* starting point */ , + opt_desc_t * opts /* array with option description */ , + int nr_opts /* number of elements in @opts */ ) +{ + int result; + + result = 0; + while ((result == 0) && opt_string && *opt_string) { + int j; + char *next; + + next = strchr(opt_string, ','); + if (next != NULL) { + *next = '\0'; + ++next; + } + for (j = 0; j < nr_opts; ++j) { + if (!strncmp(opt_string, opts[j].name, strlen(opts[j].name))) { + result = parse_option(opt_string, &opts[j]); + break; + } + } + if (j == nr_opts) { + warning("nikita-2307", "Unrecognized option: \"%s\"", opt_string); + /* traditionally, -EINVAL is returned on wrong mount + option */ + result = RETERR(-EINVAL); + } + opt_string = next; + } + return result; +} + +#define NUM_OPT( label, fmt, addr ) \ + { \ + .name = ( label ), \ + .type = OPT_FORMAT, \ + .u = { \ + .f = { \ + .format = ( fmt ), \ + .nr_args = 1, \ + .arg1 = ( addr ), \ + .arg2 = NULL, \ + .arg3 = NULL, \ + .arg4 = NULL \ + } \ + } \ + } + +#define SB_FIELD_OPT( field, fmt ) NUM_OPT( #field, fmt, &sbinfo -> field ) + +#define BIT_OPT(label, bitnr) \ + { \ + .name = label, \ + .type = OPT_BIT, \ + .u = { \ + .bit = { \ + .nr = bitnr, \ + .addr = &sbinfo->fs_flags \ + } \ + } \ + } + +/* parse options during mount */ +reiser4_internal int +reiser4_parse_options(struct super_block *s, char *opt_string) +{ + int result; + reiser4_super_info_data *sbinfo = get_super_private(s); + char *log_file_name; + + opt_desc_t opts[] = { + /* trace_flags=N + + set trace flags to be N for this mount. N can be C numeric + literal recognized by %i scanf specifier. It is treated as + bitfield filled by values of debug.h:reiser4_trace_flags + enum + */ + SB_FIELD_OPT(trace_flags, "%i"), + /* log_flags=N + + set log flags to be N for this mount. N can be C numeric + literal recognized by %i scanf specifier. It is treated as + bitfield filled by values of debug.h:reiser4_log_flags + enum + */ + SB_FIELD_OPT(log_flags, "%i"), + /* debug_flags=N + + set debug flags to be N for this mount. N can be C numeric + literal recognized by %i scanf specifier. It is treated as + bitfield filled by values of debug.h:reiser4_debug_flags + enum + */ + SB_FIELD_OPT(debug_flags, "%i"), + /* tmgr.atom_max_size=N + + Atoms containing more than N blocks will be forced to + commit. N is decimal. + */ + SB_FIELD_OPT(tmgr.atom_max_size, "%u"), + /* tmgr.atom_max_age=N + + Atoms older than N seconds will be forced to commit. N is + decimal. + */ + SB_FIELD_OPT(tmgr.atom_max_age, "%u"), + /* tmgr.atom_max_flushers=N + + limit of concurrent flushers for one atom. 0 means no limit. + */ + SB_FIELD_OPT(tmgr.atom_max_flushers, "%u"), + /* tree.cbk_cache_slots=N + + Number of slots in the cbk cache. + */ + SB_FIELD_OPT(tree.cbk_cache.nr_slots, "%u"), + + /* If flush finds more than FLUSH_RELOCATE_THRESHOLD adjacent + dirty leaf-level blocks it will force them to be + relocated. */ + SB_FIELD_OPT(flush.relocate_threshold, "%u"), + /* If flush finds can find a block allocation closer than at + most FLUSH_RELOCATE_DISTANCE from the preceder it will + relocate to that position. */ + SB_FIELD_OPT(flush.relocate_distance, "%u"), + /* If we have written this much or more blocks before + encountering busy jnode in flush list - abort flushing + hoping that next time we get called this jnode will be + clean already, and we will save some seeks. */ + SB_FIELD_OPT(flush.written_threshold, "%u"), + /* The maximum number of nodes to scan left on a level during + flush. */ + SB_FIELD_OPT(flush.scan_maxnodes, "%u"), + + /* preferred IO size */ + SB_FIELD_OPT(optimal_io_size, "%u"), + + /* carry flags used for insertion of new nodes */ + SB_FIELD_OPT(tree.carry.new_node_flags, "%u"), + /* carry flags used for insertion of new extents */ + SB_FIELD_OPT(tree.carry.new_extent_flags, "%u"), + /* carry flags used for paste operations */ + SB_FIELD_OPT(tree.carry.paste_flags, "%u"), + /* carry flags used for insert operations */ + SB_FIELD_OPT(tree.carry.insert_flags, "%u"), + +#ifdef CONFIG_REISER4_BADBLOCKS + /* Alternative master superblock location in case if it's original + location is not writeable/accessable. This is offset in BYTES. */ + SB_FIELD_OPT(altsuper, "%lu"), +#endif + + /* turn on BSD-style gid assignment */ + BIT_OPT("bsdgroups", REISER4_BSD_GID), + /* turn on 32 bit times */ + BIT_OPT("32bittimes", REISER4_32_BIT_TIMES), + /* turn off concurrent flushing */ + BIT_OPT("mtflush", REISER4_MTFLUSH), + /* disable pseudo files support */ + BIT_OPT("nopseudo", REISER4_NO_PSEUDO), + /* Don't load all bitmap blocks at mount time, it is useful + for machines with tiny RAM and large disks. */ + BIT_OPT("dont_load_bitmap", REISER4_DONT_LOAD_BITMAP), + + { + /* tree traversal readahead parameters: + -o readahead:MAXNUM:FLAGS + MAXNUM - max number fo nodes to request readahead for: -1UL will set it to max_sane_readahead() + FLAGS - combination of bits: RA_ADJCENT_ONLY, RA_ALL_LEVELS, CONTINUE_ON_PRESENT + */ + .name = "readahead", + .type = OPT_FORMAT, + .u = { + .f = { + .format = "%u:%u", + .nr_args = 2, + .arg1 = &sbinfo->ra_params.max, + .arg2 = &sbinfo->ra_params.flags, + .arg3 = NULL, + .arg4 = NULL + } + } + }, + /* What to do in case of fs error */ + { + .name = "onerror", + .type = OPT_ONEOF, + .u = { + .oneof = { + .result = &sbinfo->onerror, + .list = {"panic", "remount-ro", "reboot", NULL}, + } + } + }, + +#if REISER4_LOG + { + .name = "log_file", + .type = OPT_STRING, + .u = { + .string = &log_file_name + } + }, +#endif + }; + + sbinfo->tmgr.atom_max_size = txnmgr_get_max_atom_size(s); + sbinfo->tmgr.atom_max_age = REISER4_ATOM_MAX_AGE / HZ; + + sbinfo->tree.cbk_cache.nr_slots = CBK_CACHE_SLOTS; + + sbinfo->flush.relocate_threshold = FLUSH_RELOCATE_THRESHOLD; + sbinfo->flush.relocate_distance = FLUSH_RELOCATE_DISTANCE; + sbinfo->flush.written_threshold = FLUSH_WRITTEN_THRESHOLD; + sbinfo->flush.scan_maxnodes = FLUSH_SCAN_MAXNODES; + + + sbinfo->optimal_io_size = REISER4_OPTIMAL_IO_SIZE; + + sbinfo->tree.carry.new_node_flags = REISER4_NEW_NODE_FLAGS; + sbinfo->tree.carry.new_extent_flags = REISER4_NEW_EXTENT_FLAGS; + sbinfo->tree.carry.paste_flags = REISER4_PASTE_FLAGS; + sbinfo->tree.carry.insert_flags = REISER4_INSERT_FLAGS; + + log_file_name = NULL; + + /* + init default readahead params + */ + sbinfo->ra_params.max = num_physpages / 4; + sbinfo->ra_params.flags = 0; + + result = parse_options(opt_string, opts, sizeof_array(opts)); + if (result != 0) + return result; + + sbinfo->tmgr.atom_max_age *= HZ; + if (sbinfo->tmgr.atom_max_age <= 0) + /* overflow */ + sbinfo->tmgr.atom_max_age = REISER4_ATOM_MAX_AGE; + + /* round optimal io size up to 512 bytes */ + sbinfo->optimal_io_size >>= VFS_BLKSIZE_BITS; + sbinfo->optimal_io_size <<= VFS_BLKSIZE_BITS; + if (sbinfo->optimal_io_size == 0) { + warning("nikita-2497", "optimal_io_size is too small"); + return RETERR(-EINVAL); + } +#if REISER4_LOG + if (log_file_name != NULL) + result = open_log_file(s, log_file_name, REISER4_TRACE_BUF_SIZE, &sbinfo->log_file); + else + sbinfo->log_file.type = log_to_bucket; +#endif + + /* disable single-threaded flush as it leads to deadlock */ + sbinfo->fs_flags |= (1 << REISER4_MTFLUSH); + return result; +} + +/* show mount options in /proc/mounts */ +static int +reiser4_show_options(struct seq_file *m, struct vfsmount *mnt) +{ + struct super_block *super; + reiser4_super_info_data *sbinfo; + + super = mnt->mnt_sb; + sbinfo = get_super_private(super); + + seq_printf(m, ",trace=0x%x", sbinfo->trace_flags); + seq_printf(m, ",log=0x%x", sbinfo->log_flags); + seq_printf(m, ",debug=0x%x", sbinfo->debug_flags); + seq_printf(m, ",atom_max_size=0x%x", sbinfo->tmgr.atom_max_size); + + return 0; +} + +/* + * Lock profiling code. + * + * see spin_macros.h and spinprof.[ch] + * + */ + +/* defined profiling regions for spin lock types */ +DEFINE_SPIN_PROFREGIONS(epoch); +DEFINE_SPIN_PROFREGIONS(jnode); +DEFINE_SPIN_PROFREGIONS(jload); +DEFINE_SPIN_PROFREGIONS(stack); +DEFINE_SPIN_PROFREGIONS(super); +DEFINE_SPIN_PROFREGIONS(atom); +DEFINE_SPIN_PROFREGIONS(txnh); +DEFINE_SPIN_PROFREGIONS(txnmgr); +DEFINE_SPIN_PROFREGIONS(ktxnmgrd); +DEFINE_SPIN_PROFREGIONS(inode_object); +DEFINE_SPIN_PROFREGIONS(fq); +DEFINE_SPIN_PROFREGIONS(super_eflush); + +/* define profiling regions for read-write locks */ +DEFINE_RW_PROFREGIONS(zlock); +DEFINE_RW_PROFREGIONS(dk); +DEFINE_RW_PROFREGIONS(tree); +DEFINE_RW_PROFREGIONS(cbk_cache); + +/* register profiling regions defined above */ +static int register_profregions(void) +{ + register_super_eflush_profregion(); + register_epoch_profregion(); + register_jnode_profregion(); + register_jload_profregion(); + register_stack_profregion(); + register_super_profregion(); + register_atom_profregion(); + register_txnh_profregion(); + register_txnmgr_profregion(); + register_ktxnmgrd_profregion(); + register_inode_object_profregion(); + register_fq_profregion(); + + register_zlock_profregion(); + register_cbk_cache_profregion(); + register_dk_profregion(); + register_tree_profregion(); + + return 0; +} + +/* unregister profiling regions defined above */ +static void unregister_profregions(void) +{ + unregister_super_eflush_profregion(); + unregister_epoch_profregion(); + unregister_jload_profregion(); + unregister_jnode_profregion(); + unregister_stack_profregion(); + unregister_super_profregion(); + unregister_atom_profregion(); + unregister_txnh_profregion(); + unregister_txnmgr_profregion(); + unregister_ktxnmgrd_profregion(); + unregister_inode_object_profregion(); + unregister_fq_profregion(); + + unregister_zlock_profregion(); + unregister_cbk_cache_profregion(); + unregister_dk_profregion(); + unregister_tree_profregion(); +} + +/* ->write_super() method. Called by sync(2). */ +static void +reiser4_write_super(struct super_block *s) +{ + int ret; + reiser4_context ctx; + + assert("vs-1700", !rofs_super(s)); + + init_context(&ctx, s); + reiser4_stat_inc(vfs_calls.write_super); + + ret = capture_super_block(s); + if (ret != 0) + warning("vs-1701", + "capture_super_block failed in write_super: %d", ret); + ret = txnmgr_force_commit_all(s, 1); + if (ret != 0) + warning("jmacd-77113", + "txn_force failed in write_super: %d", ret); + + s->s_dirt = 0; + + reiser4_exit_context(&ctx); +} + +static void +reiser4_put_super(struct super_block *s) +{ + reiser4_super_info_data *sbinfo; + reiser4_context context; + + sbinfo = get_super_private(s); + assert("vs-1699", sbinfo); + + init_context(&context, s); + done_reiser4_repacker(s); + stop_ktxnmgrd(&sbinfo->tmgr); + reiser4_sysfs_done(s); + + /* have disk format plugin to free its resources */ + if (get_super_private(s)->df_plug->release) + get_super_private(s)->df_plug->release(s); + + done_ktxnmgrd_context(&sbinfo->tmgr); + done_entd_context(s); + + check_block_counters(s); + + rcu_barrier(); + /* done_formatted_fake just has finished with last jnodes (bitmap + * ones) */ + done_tree(&sbinfo->tree); + /* call finish_rcu(), because some znode were "released" in + * done_tree(). */ + rcu_barrier(); + done_formatted_fake(s); + + /* no assertions below this line */ + reiser4_exit_context(&context); + + reiser4_stat_done(&sbinfo->stats); + + kfree(sbinfo); + s->s_fs_info = NULL; +} + +/* ->get_sb() method of file_system operations. */ +static struct super_block * +reiser4_get_sb(struct file_system_type *fs_type /* file + * system + * type */ , + int flags /* flags */ , + const char *dev_name /* device name */ , + void *data /* mount options */ ) +{ + return get_sb_bdev(fs_type, flags, dev_name, data, reiser4_fill_super); +} + +int d_cursor_init(void); +void d_cursor_done(void); + +/* + * Reiser4 initialization/shutdown. + * + * Code below performs global reiser4 initialization that is done either as + * part of kernel initialization (when reiser4 is statically built-in), or + * during reiser4 module load (when compiled as module). + */ + +/* + * Initialization stages for reiser4. + * + * These enumerate various things that have to be done during reiser4 + * startup. Initialization code (init_reiser4()) keeps track of what stage was + * reached, so that proper undo can be done if error occurs during + * initialization. + */ +typedef enum { + INIT_NONE, /* nothing is initialized yet */ + INIT_INODECACHE, /* inode cache created */ + INIT_CONTEXT_MGR, /* list of active contexts created */ + INIT_ZNODES, /* znode slab created */ + INIT_PLUGINS, /* plugins initialized */ + INIT_PLUGIN_SET, /* psets initialized */ + INIT_TXN, /* transaction manager initialized */ + INIT_FAKES, /* fake inode initialized */ + INIT_JNODES, /* jnode slab initialized */ + INIT_EFLUSH, /* emergency flush initialized */ + INIT_SPINPROF, /* spin lock profiling initialized */ + INIT_SYSFS, /* sysfs exports initialized */ + INIT_LNODES, /* lnodes initialized */ + INIT_FQS, /* flush queues initialized */ + INIT_DENTRY_FSDATA, /* dentry_fsdata slab initialized */ + INIT_FILE_FSDATA, /* file_fsdata slab initialized */ + INIT_D_CURSOR, /* d_cursor suport initialized */ + INIT_FS_REGISTERED, /* reiser4 file system type registered */ +} reiser4_init_stage; + +static reiser4_init_stage init_stage; + +/* finish with reiser4: this is called either at shutdown or at module unload. */ +static void +shutdown_reiser4(void) +{ +#define DONE_IF( stage, exp ) \ + if( init_stage == ( stage ) ) { \ + exp; \ + -- init_stage; \ + } + + /* + * undo initializations already done by init_reiser4(). + */ + + DONE_IF(INIT_FS_REGISTERED, unregister_filesystem(&reiser4_fs_type)); + DONE_IF(INIT_D_CURSOR, d_cursor_done()); + DONE_IF(INIT_FILE_FSDATA, done_file_fsdata()); + DONE_IF(INIT_DENTRY_FSDATA, done_dentry_fsdata()); + DONE_IF(INIT_FQS, done_fqs()); + DONE_IF(INIT_LNODES, lnodes_done()); + DONE_IF(INIT_SYSFS, reiser4_sysfs_done_once()); + DONE_IF(INIT_SPINPROF, unregister_profregions()); + DONE_IF(INIT_EFLUSH, eflush_done()); + DONE_IF(INIT_JNODES, jnode_done_static()); + DONE_IF(INIT_FAKES,;); + DONE_IF(INIT_TXN, txnmgr_done_static()); + DONE_IF(INIT_PLUGIN_SET,plugin_set_done()); + DONE_IF(INIT_PLUGINS,;); + DONE_IF(INIT_ZNODES, znodes_done()); + DONE_IF(INIT_CONTEXT_MGR,;); + DONE_IF(INIT_INODECACHE, destroy_inodecache()); + assert("nikita-2516", init_stage == INIT_NONE); + +#undef DONE_IF +} + +/* initialize reiser4: this is called either at bootup or at module load. */ +static int __init +init_reiser4(void) +{ +#define CHECK_INIT_RESULT( exp ) \ +({ \ + result = exp; \ + if( result == 0 ) \ + ++ init_stage; \ + else { \ + shutdown_reiser4(); \ + return result; \ + } \ +}) + + int result; + /* + printk(KERN_INFO + "Loading Reiser4. " + "See www.namesys.com for a description of Reiser4.\n"); + */ + init_stage = INIT_NONE; + + CHECK_INIT_RESULT(init_inodecache()); + CHECK_INIT_RESULT(init_context_mgr()); + CHECK_INIT_RESULT(znodes_init()); + CHECK_INIT_RESULT(init_plugins()); + CHECK_INIT_RESULT(plugin_set_init()); + CHECK_INIT_RESULT(txnmgr_init_static()); + CHECK_INIT_RESULT(init_fakes()); + CHECK_INIT_RESULT(jnode_init_static()); + CHECK_INIT_RESULT(eflush_init()); + CHECK_INIT_RESULT(register_profregions()); + CHECK_INIT_RESULT(reiser4_sysfs_init_once()); + CHECK_INIT_RESULT(lnodes_init()); + CHECK_INIT_RESULT(init_fqs()); + CHECK_INIT_RESULT(init_dentry_fsdata()); + CHECK_INIT_RESULT(init_file_fsdata()); + CHECK_INIT_RESULT(d_cursor_init()); + CHECK_INIT_RESULT(register_filesystem(&reiser4_fs_type)); + + calibrate_prof(); + + assert("nikita-2515", init_stage == INIT_FS_REGISTERED); + return 0; +#undef CHECK_INIT_RESULT +} + +static void __exit +done_reiser4(void) +{ + shutdown_reiser4(); +} + +reiser4_internal void reiser4_handle_error(void) +{ + struct super_block *sb = reiser4_get_current_sb(); + + if ( !sb ) + return; + reiser4_status_write(REISER4_STATUS_DAMAGED, 0, "Filesystem error occured"); + switch ( get_super_private(sb)->onerror ) { + case 0: + reiser4_panic("foobar-42", "Filesystem error occured\n"); + case 1: + if ( sb->s_flags & MS_RDONLY ) + return; + sb->s_flags |= MS_RDONLY; + break; + case 2: + machine_restart(NULL); + } +} + +module_init(init_reiser4); +module_exit(done_reiser4); + +MODULE_DESCRIPTION("Reiser4 filesystem"); +MODULE_AUTHOR("Hans Reiser "); + +MODULE_LICENSE("GPL"); + +/* description of the reiser4 file system type in the VFS eyes. */ +struct file_system_type reiser4_fs_type = { + .owner = THIS_MODULE, + .name = "reiser4", + .fs_flags = FS_REQUIRES_DEV, + .get_sb = reiser4_get_sb, + .kill_sb = kill_block_super,/*reiser4_kill_super,*/ + .next = NULL +}; + +struct super_operations reiser4_super_operations = { + .alloc_inode = reiser4_alloc_inode, + .destroy_inode = reiser4_destroy_inode, + .read_inode = noop_read_inode, + .dirty_inode = NULL, + .write_inode = NULL, + .put_inode = NULL, + .drop_inode = reiser4_drop_inode, + .delete_inode = reiser4_delete_inode, + .put_super = reiser4_put_super, + .write_super = reiser4_write_super, + .sync_fs = NULL, + .write_super_lockfs = NULL, + .unlockfs = NULL, + .statfs = reiser4_statfs, + .remount_fs = NULL, + .clear_inode = reiser4_clear_inode, + .umount_begin = NULL, + .sync_inodes = reiser4_sync_inodes, + .show_options = reiser4_show_options +}; + +/* + * Object serialization support. + * + * To support knfsd file system provides export_operations that are used to + * construct and interpret NFS file handles. As a generalization of this, + * reiser4 object plugins have serialization support: it provides methods to + * create on-wire representation of identity of reiser4 object, and + * re-create/locate object given its on-wire identity. + * + */ + +/* + * return number of bytes that on-wire representation of @inode's identity + * consumes. + */ +static int +encode_inode_size(struct inode *inode) +{ + assert("nikita-3514", inode != NULL); + assert("nikita-3515", inode_file_plugin(inode) != NULL); + assert("nikita-3516", inode_file_plugin(inode)->wire.size != NULL); + + return inode_file_plugin(inode)->wire.size(inode) + sizeof(d16); +} + +/* + * store on-wire representation of @inode's identity at the area beginning at + * @start. + */ +static char * +encode_inode(struct inode *inode, char *start) +{ + assert("nikita-3517", inode != NULL); + assert("nikita-3518", inode_file_plugin(inode) != NULL); + assert("nikita-3519", inode_file_plugin(inode)->wire.write != NULL); + + /* + * first, store two-byte identifier of object plugin, then + */ + save_plugin_id(file_plugin_to_plugin(inode_file_plugin(inode)), + (d16 *)start); + start += sizeof(d16); + /* + * call plugin to serialize object's identity + */ + return inode_file_plugin(inode)->wire.write(inode, start); +} + +/* + * Supported file-handle types + */ +typedef enum { + FH_WITH_PARENT = 0x10, /* file handle with parent */ + FH_WITHOUT_PARENT = 0x11 /* file handle without parent */ +} reiser4_fhtype; + +#define NFSERROR (255) + +/* this returns number of 32 bit long numbers encoded in @lenp. 255 is + * returned if file handle can not be stored */ +static int +reiser4_encode_fh(struct dentry *dentry, __u32 *data, int *lenp, int need_parent) +{ + struct inode *inode; + struct inode *parent; + char *addr; + int need; + int delta; + int result; + reiser4_context context; + + /* + * knfsd asks as to serialize object in @dentry, and, optionally its + * parent (if need_parent != 0). + * + * encode_inode() and encode_inode_size() is used to build + * representation of object and its parent. All hard work is done by + * object plugins. + */ + + inode = dentry->d_inode; + parent = dentry->d_parent->d_inode; + + addr = (char *)data; + + need = encode_inode_size(inode); + if (need < 0) + return NFSERROR; + if (need_parent) { + delta = encode_inode_size(parent); + if (delta < 0) + return NFSERROR; + need += delta; + } + + init_context(&context, dentry->d_inode->i_sb); + + if (need <= sizeof(__u32) * (*lenp)) { + addr = encode_inode(inode, addr); + if (need_parent) + addr = encode_inode(parent, addr); + + /* store in lenp number of 32bit words required for file + * handle. */ + *lenp = (need + sizeof(__u32) - 1) >> 2; + result = need_parent ? FH_WITH_PARENT : FH_WITHOUT_PARENT; + } else + /* no enough space in file handle */ + result = NFSERROR; + reiser4_exit_context(&context); + return result; +} + +/* + * read serialized object identity from @addr and store information about + * object in @obj. This is dual to encode_inode(). + */ +static char * +decode_inode(struct super_block *s, char *addr, reiser4_object_on_wire *obj) +{ + file_plugin *fplug; + + /* identifier of object plugin is stored in the first two bytes, + * followed by... */ + fplug = file_plugin_by_disk_id(get_tree(s), (d16 *)addr); + if (fplug != NULL) { + addr += sizeof(d16); + obj->plugin = fplug; + assert("nikita-3520", fplug->wire.read != NULL); + /* plugin specific encoding of object identity. */ + addr = fplug->wire.read(addr, obj); + } else + addr = ERR_PTR(RETERR(-EINVAL)); + return addr; +} + +/* initialize place-holder for object */ +static void +object_on_wire_init(reiser4_object_on_wire *o) +{ + o->plugin = NULL; +} + +/* finish with @o */ +static void +object_on_wire_done(reiser4_object_on_wire *o) +{ + if (o->plugin != NULL) + o->plugin->wire.done(o); +} + +/* decode knfsd file handle. This is dual to reiser4_encode_fh() */ +static struct dentry * +reiser4_decode_fh(struct super_block *s, __u32 *data, + int len, int fhtype, + int (*acceptable)(void *context, struct dentry *de), + void *context) +{ + reiser4_context ctx; + reiser4_object_on_wire object; + reiser4_object_on_wire parent; + char *addr; + int with_parent; + + init_context(&ctx, s); + + assert("vs-1482", + fhtype == FH_WITH_PARENT || fhtype == FH_WITHOUT_PARENT); + + with_parent = (fhtype == FH_WITH_PARENT); + + addr = (char *)data; + + object_on_wire_init(&object); + object_on_wire_init(&parent); + + addr = decode_inode(s, addr, &object); + if (!IS_ERR(addr)) { + if (with_parent) + addr = decode_inode(s, addr, &parent); + if (!IS_ERR(addr)) { + struct dentry *d; + typeof(s->s_export_op->find_exported_dentry) fn; + + fn = s->s_export_op->find_exported_dentry; + assert("nikita-3521", fn != NULL); + d = fn(s, &object, with_parent ? &parent : NULL, + acceptable, context); + if (d != NULL && !IS_ERR(d)) + /* FIXME check for -ENOMEM */ + reiser4_get_dentry_fsdata(d)->stateless = 1; + addr = (char *)d; + } + } + + object_on_wire_done(&object); + object_on_wire_done(&parent); + + reiser4_exit_context(&ctx); + return (void *)addr; +} + +static struct dentry * +reiser4_get_dentry(struct super_block *sb, void *data) +{ + reiser4_object_on_wire *o; + + assert("nikita-3522", sb != NULL); + assert("nikita-3523", data != NULL); + /* + * this is only supposed to be called by + * + * reiser4_decode_fh->find_exported_dentry + * + * so, reiser4_context should be here already. + * + */ + assert("nikita-3526", is_in_reiser4_context()); + + o = (reiser4_object_on_wire *)data; + assert("nikita-3524", o->plugin != NULL); + assert("nikita-3525", o->plugin->wire.get != NULL); + + return o->plugin->wire.get(sb, o); +} + +static struct dentry * +reiser4_get_dentry_parent(struct dentry *child) +{ + struct inode *dir; + dir_plugin *dplug; + + assert("nikita-3527", child != NULL); + /* see comment in reiser4_get_dentry() about following assertion */ + assert("nikita-3528", is_in_reiser4_context()); + + dir = child->d_inode; + assert("nikita-3529", dir != NULL); + dplug = inode_dir_plugin(dir); + assert("nikita-3531", ergo(dplug != NULL, dplug->get_parent != NULL)); + if (dplug != NULL) + return dplug->get_parent(dir); + else + return ERR_PTR(RETERR(-ENOTDIR)); +} + +struct export_operations reiser4_export_operations = { + .encode_fh = reiser4_encode_fh, + .decode_fh = reiser4_decode_fh, + .get_parent = reiser4_get_dentry_parent, + .get_dentry = reiser4_get_dentry +}; + +struct dentry_operations reiser4_dentry_operations = { + .d_revalidate = NULL, + .d_hash = NULL, + .d_compare = NULL, + .d_delete = NULL, + .d_release = reiser4_d_release, + .d_iput = NULL, +}; + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/vfs_ops.h 2004-09-03 00:57:05.472207360 -0700 @@ -0,0 +1,131 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* vfs_ops.c's exported symbols */ + +#if !defined( __FS_REISER4_VFS_OPS_H__ ) +#define __FS_REISER4_VFS_OPS_H__ + +#include "forward.h" +#include "coord.h" +#include "seal.h" +#include "type_safe_list.h" +#include "plugin/dir/dir.h" +#include "plugin/file/file.h" +#include "super.h" +#include "readahead.h" + +#include /* for loff_t */ +#include /* for struct address_space */ +#include /* for struct dentry */ +#include +#include + +extern int reiser4_mark_inode_dirty(struct inode *object); +extern int reiser4_update_sd(struct inode *object); +extern int reiser4_add_nlink(struct inode *, struct inode *, int); +extern int reiser4_del_nlink(struct inode *, struct inode *, int); + +extern struct file_operations reiser4_file_operations; +extern struct inode_operations reiser4_inode_operations; +extern struct inode_operations reiser4_symlink_inode_operations; +extern struct inode_operations reiser4_special_inode_operations; +extern struct super_operations reiser4_super_operations; +extern struct export_operations reiser4_export_operations; +extern struct address_space_operations reiser4_as_operations; +extern struct dentry_operations reiser4_dentry_operations; +extern int reiser4_invalidatepage(struct page *page, unsigned long offset); +extern int reiser4_releasepage(struct page *page, int gfp); +extern int reiser4_writepages(struct address_space *, struct writeback_control *wbc); +extern int reiser4_start_up_io(struct page *page); +extern void move_inode_out_from_sync_inodes_loop(struct address_space * mapping); +extern void reiser4_clear_page_dirty(struct page *); +extern void reiser4_throttle_write(struct inode*); +/* + * this is used to speed up lookups for directory entry: on initial call to + * ->lookup() seal and coord of directory entry (if found, that is) are stored + * in struct dentry and reused later to avoid tree traversals. + */ +typedef struct de_location { + /* seal covering directory entry */ + seal_t entry_seal; + /* coord of directory entry */ + coord_t entry_coord; + /* ordinal number of directory entry among all entries with the same + key. (Starting from 0.) */ + int pos; +} de_location; + +/* &reiser4_dentry_fsdata - reiser4-specific data attached to dentries. + + This is allocated dynamically and released in d_op->d_release() + + Currently it only contains cached location (hint) of directory entry, but + it is expected that other information will be accumulated here. +*/ +typedef struct reiser4_dentry_fsdata { + /* here will go fields filled by ->lookup() to speedup next + create/unlink, like blocknr of znode with stat-data, or key + of stat-data. + */ + de_location dec; + int stateless; /* created through reiser4_decode_fh, needs special + * treatment in readdir. */ +} reiser4_dentry_fsdata; + +/* declare data types and manipulation functions for readdir list. */ +TYPE_SAFE_LIST_DECLARE(readdir); + +struct dir_cursor; + +/* &reiser4_dentry_fsdata - reiser4-specific data attached to files. + + This is allocated dynamically and released in reiser4_release() */ +struct reiser4_file_fsdata { + /* pointer back to the struct file which this reiser4_file_fsdata is + * part of */ + struct file *back; + /* detached cursor for stateless readdir. */ + struct dir_cursor *cursor; + /* We need both directory and regular file parts here, because there + are file system objects that are files and directories. */ + struct { + readdir_pos readdir; + readdir_list_link linkage; + } dir; + /* hints to speed up operations with regular files: read and write. */ + struct { + hint_t hint; + /* this is set by read_extent before calling + * page_cache_readahead */ + void *coord; + } reg; + struct reiser4_file_ra_state ra; +}; + +TYPE_SAFE_LIST_DEFINE(readdir, reiser4_file_fsdata, dir.linkage); + +extern reiser4_dentry_fsdata *reiser4_get_dentry_fsdata(struct dentry *dentry); +extern reiser4_file_fsdata *reiser4_get_file_fsdata(struct file *f); +extern void reiser4_free_dentry_fsdata(struct dentry *dentry); +extern void reiser4_free_file_fsdata(struct file *f); +extern void reiser4_free_fsdata(reiser4_file_fsdata *fsdata); + +extern reiser4_file_fsdata *create_fsdata(struct file *file, int gfp); + +extern void reiser4_handle_error(void); +extern int reiser4_parse_options (struct super_block *, char *); + +/* __FS_REISER4_VFS_OPS_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/wander.c 2004-09-03 00:57:05.482205840 -0700 @@ -0,0 +1,2239 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Reiser4 Wandering Log */ + +/* You should read http://www.namesys.com/txn-doc.html + + That describes how filesystem operations are performed as atomic + transactions, and how we try to arrange it so that we can write most of the + data only once while performing the operation atomically. + + For the purposes of this code, it is enough for it to understand that it + has been told a given block should be written either once, or twice (if + twice then once to the wandered location and once to the real location). + + This code guarantees that those blocks that are defined to be part of an + atom either all take effect or none of them take effect. + + Relocate set nodes are submitted to write by the jnode_flush() routine, and + the overwrite set is submitted by reiser4_write_log(). This is because with + the overwrite set we seek to optimize writes, and with the relocate set we + seek to cause disk order to correlate with the parent first pre-order. + + reiser4_write_log() allocates and writes wandered blocks and maintains + additional on-disk structures of the atom as wander records (each wander + record occupies one block) for storing of the "wandered map" (a table which + contains a relation between wandered and real block numbers) and other + information which might be needed at transaction recovery time. + + The wander records are unidirectionally linked into a circle: each wander + record contains a block number of the next wander record, the last wander + record points to the first one. + + One wander record (named "tx head" in this file) has a format which is + different from the other wander records. The "tx head" has a reference to the + "tx head" block of the previously committed atom. Also, "tx head" contains + fs information (the free blocks counter, and the oid allocator state) which + is logged in a special way . + + There are two journal control blocks, named journal header and journal + footer which have fixed on-disk locations. The journal header has a + reference to the "tx head" block of the last committed atom. The journal + footer points to the "tx head" of the last flushed atom. The atom is + "played" when all blocks from its overwrite set are written to disk the + second time (i.e. written to their real locations). + + NOTE: People who know reiserfs internals and its journal structure might be + confused with these terms journal footer and journal header. There is a table + with terms of similar semantics in reiserfs (reiser3) and reiser4: + + REISER3 TERM | REISER4 TERM | DESCRIPTION + --------------------+-----------------------+---------------------------- + commit record | journal header | atomic write of this record + | | ends transaction commit + --------------------+-----------------------+---------------------------- + journal header | journal footer | atomic write of this record + | | ends post-commit writes. + | | After successful + | | writing of this journal + | | blocks (in reiser3) or + | | wandered blocks/records are + | | free for re-use. + --------------------+-----------------------+---------------------------- + + The atom commit process is the following: + + 1. The overwrite set is taken from atom's clean list, and its size is + counted. + + 2. The number of necessary wander records (including tx head) is calculated, + and the wander record blocks are allocated. + + 3. Allocate wandered blocks and populate wander records by wandered map. + + 4. submit write requests for wander records and wandered blocks. + + 5. wait until submitted write requests complete. + + 6. update journal header: change the pointer to the block number of just + written tx head, submit an i/o for modified journal header block and wait + for i/o completion. + + NOTE: The special logging for bitmap blocks and some reiser4 super block + fields makes processes of atom commit, flush and recovering a bit more + complex (see comments in the source code for details). + + The atom playing process is the following: + + 1. Write atom's overwrite set in-place. + + 2. Wait on i/o. + + 3. Update journal footer: change the pointer to block number of tx head + block of the atom we currently flushing, submit an i/o, wait on i/o + completion. + + 4. Free disk space which was used for wandered blocks and wander records. + + After the freeing of wandered blocks and wander records we have that journal + footer points to the on-disk structure which might be overwritten soon. + Neither the log writer nor the journal recovery procedure use that pointer + for accessing the data. When the journal recovery procedure finds the oldest + transaction it compares the journal footer pointer value with the "prev_tx" + pointer value in tx head, if values are equal the oldest not flushed + transaction is found. + + NOTE on disk space leakage: the information about of what blocks and how many + blocks are allocated for wandered blocks, wandered records is not written to + the disk because of special logging for bitmaps and some super blocks + counters. After a system crash we the reiser4 does not remember those + objects allocation, thus we have no such a kind of disk space leakage. +*/ + +/* Special logging of reiser4 super block fields. */ + +/* There are some reiser4 super block fields (free block count and OID allocator + state (number of files and next free OID) which are logged separately from + super block to avoid unnecessary atom fusion. + + So, the reiser4 super block can be not captured by a transaction with + allocates/deallocates disk blocks or create/delete file objects. Moreover, + the reiser4 on-disk super block is not touched when such a transaction is + committed and flushed. Those "counters logged specially" are logged in "tx + head" blocks and in the journal footer block. + + A step-by-step description of special logging: + + 0. The per-atom information about deleted or created files and allocated or + freed blocks is collected during the transaction. The atom's + ->nr_objects_created and ->nr_objects_deleted are for object + deletion/creation tracking, the numbers of allocated and freed blocks are + calculated using atom's delete set and atom's capture list -- all new and + relocated nodes should be on atom's clean list and should have JNODE_RELOC + bit set. + + 1. The "logged specially" reiser4 super block fields have their "committed" + versions in the reiser4 in-memory super block. They get modified only at + atom commit time. The atom's commit thread has an exclusive access to those + "committed" fields because the log writer implementation supports only one + atom commit a time (there is a per-fs "commit" semaphore). At + that time "committed" counters are modified using per-atom information + collected during the transaction. These counters are stored on disk as a + part of tx head block when atom is committed. + + 2. When the atom is flushed the value of the free block counter and the OID + allocator state get written to the journal footer block. A special journal + procedure (journal_recover_sb_data()) takes those values from the journal + footer and updates the reiser4 in-memory super block. + + NOTE: That means free block count and OID allocator state are logged + separately from the reiser4 super block regardless of the fact that the + reiser4 super block has fields to store both the free block counter and the + OID allocator. + + Writing the whole super block at commit time requires knowing true values of + all its fields without changes made by not yet committed transactions. It is + possible by having their "committed" version of the super block like the + reiser4 bitmap blocks have "committed" and "working" versions. However, + another scheme was implemented which stores special logged values in the + unused free space inside transaction head block. In my opinion it has an + advantage of not writing whole super block when only part of it was + modified. */ + +#include "debug.h" +#include "dformat.h" +#include "txnmgr.h" +#include "jnode.h" +#include "znode.h" +#include "block_alloc.h" +#include "page_cache.h" +#include "wander.h" +#include "reiser4.h" +#include "super.h" +#include "vfs_ops.h" +#include "writeout.h" +#include "inode.h" +#include "entd.h" + +#include +#include /* for struct super_block */ +#include /* for struct page */ +#include +#include /* for struct bio */ +#include + +static int write_jnodes_to_disk_extent( + capture_list_head * head, jnode *, int, const reiser4_block_nr *, flush_queue_t *, int ); + +/* The commit_handle is a container for objects needed at atom commit time */ +struct commit_handle { + /* A pointer to the list of OVRWR nodes */ + capture_list_head * overwrite_set; + /* atom's overwrite set size */ + int overwrite_set_size; + /* jnodes for wander record blocks */ + capture_list_head tx_list; + /* number of wander records */ + int tx_size; + /* 'committed' sb counters are saved here until atom is completely + flushed */ + __u64 free_blocks; + __u64 nr_files; + __u64 next_oid; + /* A pointer to the atom which is being committed */ + txn_atom *atom; + /* A pointer to current super block */ + struct super_block *super; + /* The counter of modified bitmaps */ + reiser4_block_nr nr_bitmap; +}; + +static void +init_commit_handle(struct commit_handle *ch, txn_atom * atom) +{ + xmemset(ch, 0, sizeof (struct commit_handle)); + capture_list_init(&ch->tx_list); + + ch->atom = atom; + ch->super = reiser4_get_current_sb(); +} + +static void +done_commit_handle(struct commit_handle *ch UNUSED_ARG) +{ + assert("zam-690", capture_list_empty(&ch->tx_list)); +} + +/* fill journal header block data */ +static void +format_journal_header(struct commit_handle *ch) +{ + struct reiser4_super_info_data *sbinfo; + struct journal_header *header; + jnode *txhead; + + sbinfo = get_super_private(ch->super); + assert("zam-479", sbinfo != NULL); + assert("zam-480", sbinfo->journal_header != NULL); + + txhead = capture_list_front(&ch->tx_list); + + jload(sbinfo->journal_header); + + header = (struct journal_header *) jdata(sbinfo->journal_header); + assert("zam-484", header != NULL); + + cputod64(*jnode_get_block(txhead), &header->last_committed_tx); + + jrelse(sbinfo->journal_header); +} + +/* fill journal footer block data */ +static void +format_journal_footer(struct commit_handle *ch) +{ + struct reiser4_super_info_data *sbinfo; + struct journal_footer *footer; + + jnode *tx_head; + + sbinfo = get_super_private(ch->super); + + tx_head = capture_list_front(&ch->tx_list); + + assert("zam-493", sbinfo != NULL); + assert("zam-494", sbinfo->journal_header != NULL); + + check_me("zam-691", jload(sbinfo->journal_footer) == 0); + + footer = (struct journal_footer *) jdata(sbinfo->journal_footer); + assert("zam-495", footer != NULL); + + cputod64(*jnode_get_block(tx_head), &footer->last_flushed_tx); + cputod64(ch->free_blocks, &footer->free_blocks); + + cputod64(ch->nr_files, &footer->nr_files); + cputod64(ch->next_oid, &footer->next_oid); + + jrelse(sbinfo->journal_footer); +} + +/* wander record capacity depends on current block size */ +static int +wander_record_capacity(const struct super_block *super) +{ + return (super->s_blocksize - sizeof (struct wander_record_header)) / sizeof (struct wander_entry); +} + +/* Fill first wander record (tx head) in accordance with supplied given data */ +static void +format_tx_head(struct commit_handle *ch) +{ + jnode *tx_head; + jnode *next; + struct tx_header *header; + + tx_head = capture_list_front(&ch->tx_list); + assert("zam-692", !capture_list_end(&ch->tx_list, tx_head)); + + next = capture_list_next(tx_head); + if (capture_list_end(&ch->tx_list, next)) + next = tx_head; + + header = (struct tx_header *) jdata(tx_head); + + assert("zam-460", header != NULL); + assert("zam-462", ch->super->s_blocksize >= sizeof (struct tx_header)); + + xmemset(jdata(tx_head), 0, (size_t) ch->super->s_blocksize); + xmemcpy(jdata(tx_head), TX_HEADER_MAGIC, TX_HEADER_MAGIC_SIZE); + + cputod32((__u32) ch->tx_size, &header->total); + cputod64(get_super_private(ch->super)->last_committed_tx, &header->prev_tx); + cputod64(*jnode_get_block(next), &header->next_block); + + cputod64(ch->free_blocks, &header->free_blocks); + cputod64(ch->nr_files, &header->nr_files); + cputod64(ch->next_oid, &header->next_oid); +} + +/* prepare ordinary wander record block (fill all service fields) */ +static void +format_wander_record(struct commit_handle *ch, jnode * node, int serial) +{ + struct wander_record_header *LRH; + jnode *next; + + assert("zam-464", node != NULL); + + LRH = (struct wander_record_header *) jdata(node); + next = capture_list_next(node); + + if (capture_list_end(&ch->tx_list, next)) + next = capture_list_front(&ch->tx_list); + + assert("zam-465", LRH != NULL); + assert("zam-463", ch->super->s_blocksize > sizeof (struct wander_record_header)); + + xmemset(jdata(node), 0, (size_t) ch->super->s_blocksize); + xmemcpy(jdata(node), WANDER_RECORD_MAGIC, WANDER_RECORD_MAGIC_SIZE); + +// cputod64((__u64)reiser4_trans_id(super), &h->id); + cputod32((__u32) ch->tx_size, &LRH->total); + cputod32((__u32) serial, &LRH->serial); + cputod64((__u64) * jnode_get_block(next), &LRH->next_block); +} + +/* add one wandered map entry to formatted wander record */ +static void +store_entry(jnode * node, int index, const reiser4_block_nr * a, const reiser4_block_nr * b) +{ + char *data; + struct wander_entry *pairs; + + data = jdata(node); + assert("zam-451", data != NULL); + + pairs = (struct wander_entry *) (data + sizeof (struct wander_record_header)); + + cputod64(*a, &pairs[index].original); + cputod64(*b, &pairs[index].wandered); +} + +/* currently, wander records contains contain only wandered map, which depend on + overwrite set size */ +static void +get_tx_size(struct commit_handle *ch) +{ + assert("zam-440", ch->overwrite_set_size != 0); + assert("zam-695", ch->tx_size == 0); + + /* count all ordinary wander records + ( - 1) / + 1 and add one + for tx head block */ + ch->tx_size = (ch->overwrite_set_size - 1) / wander_record_capacity(ch->super) + 2; +} + +/* A special structure for using in store_wmap_actor() for saving its state + between calls */ +struct store_wmap_params { + jnode *cur; /* jnode of current wander record to fill */ + int idx; /* free element index in wander record */ + int capacity; /* capacity */ + +#if REISER4_DEBUG + capture_list_head *tx_list; +#endif +}; + +/* an actor for use in blocknr_set_iterator routine which populates the list + of pre-formatted wander records by wandered map info */ +static int +store_wmap_actor(txn_atom * atom UNUSED_ARG, const reiser4_block_nr * a, const reiser4_block_nr * b, void *data) +{ + struct store_wmap_params *params = data; + + if (params->idx >= params->capacity) { + /* a new wander record should be taken from the tx_list */ + params->cur = capture_list_next(params->cur); + assert("zam-454", !capture_list_end(params->tx_list, params->cur)); + + params->idx = 0; + } + + store_entry(params->cur, params->idx, a, b); + params->idx++; + + return 0; +} + +/* This function is called after Relocate set gets written to disk, Overwrite + set is written to wandered locations and all wander records are written + also. Updated journal header blocks contains a pointer (block number) to + first wander record of the just written transaction */ +static int +update_journal_header(struct commit_handle *ch) +{ + struct reiser4_super_info_data *sbinfo = get_super_private(ch->super); + + jnode *jh = sbinfo->journal_header; + jnode *head = capture_list_front(&ch->tx_list); + + int ret; + + format_journal_header(ch); + + ret = write_jnodes_to_disk_extent(&ch->tx_list, jh, 1, jnode_get_block(jh), NULL, 0); + + if (ret) + return ret; + + blk_run_address_space(sbinfo->fake->i_mapping); + /*blk_run_queues();*/ + + ret = jwait_io(jh, WRITE); + + if (ret) + return ret; + + sbinfo->last_committed_tx = *jnode_get_block(head); + + return 0; +} + +/* This function is called after write-back is finished. We update journal + footer block and free blocks which were occupied by wandered blocks and + transaction wander records */ +static int +update_journal_footer(struct commit_handle *ch) +{ + reiser4_super_info_data *sbinfo = get_super_private(ch->super); + + jnode *jf = sbinfo->journal_footer; + + int ret; + + format_journal_footer(ch); + + ret = write_jnodes_to_disk_extent(&ch->tx_list, jf, 1, jnode_get_block(jf), NULL, 0); + if (ret) + return ret; + + blk_run_address_space(sbinfo->fake->i_mapping); + /*blk_run_queue();*/ + + ret = jwait_io(jf, WRITE); + if (ret) + return ret; + + return 0; +} + +/* free block numbers of wander records of already written in place transaction */ +static void +dealloc_tx_list(struct commit_handle *ch) +{ + while (!capture_list_empty(&ch->tx_list)) { + jnode *cur = capture_list_pop_front(&ch->tx_list); + + ON_DEBUG(capture_list_clean(cur)); + reiser4_dealloc_block(jnode_get_block(cur), BLOCK_NOT_COUNTED, BA_FORMATTED); + + unpin_jnode_data(cur); + drop_io_head(cur); + } +} + +/* An actor for use in block_nr_iterator() routine which frees wandered blocks + from atom's overwrite set. */ +static int +dealloc_wmap_actor(txn_atom * atom UNUSED_ARG, + const reiser4_block_nr * a UNUSED_ARG, const reiser4_block_nr * b, void *data UNUSED_ARG) +{ + + assert("zam-499", b != NULL); + assert("zam-500", *b != 0); + assert("zam-501", !blocknr_is_fake(b)); + + reiser4_dealloc_block(b, BLOCK_NOT_COUNTED, BA_FORMATTED); + return 0; +} + +/* free wandered block locations of already written in place transaction */ +static void +dealloc_wmap(struct commit_handle *ch) +{ + assert("zam-696", ch->atom != NULL); + + blocknr_set_iterator(ch->atom, &ch->atom->wandered_map, dealloc_wmap_actor, NULL, 1); +} + +/* helper function for alloc wandered blocks, which refill set of block + numbers needed for wandered blocks */ +static int +get_more_wandered_blocks(int count, reiser4_block_nr * start, int *len) +{ + reiser4_blocknr_hint hint; + int ret; + + reiser4_block_nr wide_len = count; + + /* FIXME-ZAM: A special policy needed for allocation of wandered blocks + ZAM-FIXME-HANS: yes, what happened to our discussion of using a fixed + reserved allocation area so as to get the best qualities of fixed + journals? */ + blocknr_hint_init(&hint); + hint.block_stage = BLOCK_GRABBED; + + ret = reiser4_alloc_blocks(&hint, start, &wide_len, + BA_FORMATTED | BA_USE_DEFAULT_SEARCH_START); + + *len = (int) wide_len; + + return ret; +} + +/* + * roll back changes made before issuing BIO in the case of IO error. + */ +static void +undo_bio(struct bio *bio) +{ + int i; + + for (i = 0; i < bio->bi_vcnt; ++i) { + struct page *pg; + jnode *node; + + pg = bio->bi_io_vec[i].bv_page; + ClearPageWriteback(pg); + node = jprivate(pg); + LOCK_JNODE(node); + JF_CLR(node, JNODE_WRITEBACK); + JF_SET(node, JNODE_DIRTY); + UNLOCK_JNODE(node); + } + bio_put(bio); +} + +#if REISER4_COPY_ON_CAPTURE + +extern spinlock_t scan_lock; + +/* put overwrite set back to atom's clean list */ +static void put_overwrite_set(struct commit_handle * ch) +{ + jnode * cur; + + spin_lock(&scan_lock); + cur = capture_list_front(ch->overwrite_set); + while (!capture_list_end(ch->overwrite_set, cur)) { + assert("vs-1443", NODE_LIST(cur) == OVRWR_LIST); + JF_SET(cur, JNODE_SCANNED); + spin_unlock(&scan_lock); + JF_CLR(cur, JNODE_JLOADED_BY_GET_OVERWRITE_SET); + jrelse_tail(cur); + spin_lock(&scan_lock); + JF_CLR(cur, JNODE_SCANNED); + cur = capture_list_next(cur); + } + spin_unlock(&scan_lock); +} + +/* Count overwrite set size, grab disk space for wandered blocks allocation. + Since we have a separate list for atom's overwrite set we just scan the list, + count bitmap and other not leaf nodes which wandered blocks allocation we + have to grab space for. */ +static int +get_overwrite_set(struct commit_handle *ch) +{ + int ret; + jnode *cur; + __u64 nr_not_leaves = 0; +#if REISER4_DEBUG + __u64 nr_formatted_leaves = 0; + __u64 nr_unformatted_leaves = 0; +#endif + + + assert("zam-697", ch->overwrite_set_size == 0); + + ch->overwrite_set = ATOM_OVRWR_LIST(ch->atom); + + spin_lock(&scan_lock); + cur = capture_list_front(ch->overwrite_set); + + while (!capture_list_end(ch->overwrite_set, cur)) { + jnode *next; + + /* FIXME: for all but first this bit is set already */ + assert("vs-1444", NODE_LIST(cur) == OVRWR_LIST); + JF_SET(cur, JNODE_SCANNED); + next = capture_list_next(cur); + if (!capture_list_end(ch->overwrite_set, next)) + JF_SET(next, JNODE_SCANNED); + spin_unlock(&scan_lock); + + if (jnode_is_znode(cur) && znode_above_root(JZNODE(cur))) { + ON_TRACE(TRACE_LOG, "fake znode found , WANDER=(%d)\n", JF_ISSET(cur, JNODE_OVRWR)); + } + + /* Count bitmap locks for getting correct statistics what number + * of blocks were cleared by the transaction commit. */ + if (jnode_get_type(cur) == JNODE_BITMAP) + ch->nr_bitmap ++; + + assert("zam-939", JF_ISSET(cur, JNODE_OVRWR) || jnode_get_type(cur) == JNODE_BITMAP); + + if (jnode_is_znode(cur) && znode_above_root(JZNODE(cur))) { + /* we replace fake znode by another (real) + znode which is suggested by disk_layout + plugin */ + + /* FIXME: it looks like fake znode should be + replaced by jnode supplied by + disk_layout. */ + + struct super_block *s = reiser4_get_current_sb(); + reiser4_super_info_data *sbinfo = get_current_super_private(); + + if (sbinfo->df_plug->log_super) { + jnode *sj = sbinfo->df_plug->log_super(s); + + assert("zam-593", sj != NULL); + + if (IS_ERR(sj)) + return PTR_ERR(sj); + + LOCK_ATOM(ch->atom); + LOCK_JNODE(sj); + JF_SET(sj, JNODE_OVRWR); + insert_into_atom_ovrwr_list(ch->atom, sj); + UNLOCK_JNODE(sj); + UNLOCK_ATOM(ch->atom); + + /* jload it as the rest of overwrite set */ + jload_gfp(sj, GFP_KERNEL, 0); + + ch->overwrite_set_size++; + } + LOCK_ATOM(ch->atom); + LOCK_JNODE(cur); + uncapture_block(cur); + UNLOCK_ATOM(ch->atom); + jput(cur); + + spin_lock(&scan_lock); + JF_CLR(cur, JNODE_SCANNED); + cur = next; + nr_not_leaves ++; + } else { + int ret; + ch->overwrite_set_size++; + ret = jload_gfp(cur, GFP_KERNEL, 0); + if (ret) + reiser4_panic("zam-783", "cannot load e-flushed jnode back (ret = %d)\n", ret); + + /* Count not leaves here because we have to grab disk + * space for wandered blocks. They were not counted as + * "flush reserved". This should be done after doing + * jload() to avoid races with emergency + * flush. Counting should be done _after_ nodes are + * pinned * into memory by jload(). */ + if (!jnode_is_leaf(cur)) + nr_not_leaves ++; + /* this is to check atom's flush reserved space for + * overwritten leaves */ + else { +#if REISER4_DEBUG + /* at this point @cur either has + * JNODE_FLUSH_RESERVED or is + * eflushed. Locking is not strong enough to + * write an assertion checking for this. */ + if (jnode_is_znode(cur)) + nr_formatted_leaves ++; + else + nr_unformatted_leaves ++; +#endif + JF_CLR(cur, JNODE_FLUSH_RESERVED); + } + spin_lock(&scan_lock); + JF_SET(cur, JNODE_JLOADED_BY_GET_OVERWRITE_SET); + assert("", cur->pg); + JF_CLR(cur, JNODE_SCANNED); + cur = next; + } + + } + spin_unlock(&scan_lock); + + /* Grab space for writing (wandered blocks) of not leaves found in + * overwrite set. */ + ret = reiser4_grab_space_force(nr_not_leaves, BA_RESERVED); + if (ret) + return ret; + + /* Disk space for allocation of wandered blocks of leaf nodes already + * reserved as "flush reserved", move it to grabbed space counter. */ + spin_lock_atom(ch->atom); + assert("zam-940", nr_formatted_leaves + nr_unformatted_leaves <= ch->atom->flush_reserved); + flush_reserved2grabbed(ch->atom, ch->atom->flush_reserved); + spin_unlock_atom(ch->atom); + + return ch->overwrite_set_size; +} + +/* Submit a write request for @nr jnodes beginning from the @first, other + jnodes are after the @first on the double-linked "capture" list. All + jnodes will be written to the disk region of @nr blocks starting with + @block_p block number. If @fq is not NULL it means that waiting for i/o + completion will be done more efficiently by using flush_queue_t objects + +ZAM-FIXME-HANS: brief me on why this function exists, and why bios are +aggregated in this function instead of being left to the layers below + +FIXME: ZAM->HANS: What layer are you talking about? Can you point me to that? +Why that layer needed? Why BIOs cannot be constructed here? +*/ +static int +write_jnodes_to_disk_extent(capture_list_head * head, jnode * first, int nr, + const reiser4_block_nr * block_p, flush_queue_t * fq, int flags) +{ + struct super_block *super = reiser4_get_current_sb(); + int for_reclaim = flags & WRITEOUT_FOR_PAGE_RECLAIM; + int max_blocks; + jnode *cur = first; + reiser4_block_nr block; + + assert("zam-571", first != NULL); + assert("zam-572", block_p != NULL); + assert("zam-570", nr > 0); + + block = *block_p; + + ON_TRACE (TRACE_IO_W, "write of %d blocks starting from %llu\n", + nr, (unsigned long long)block); + + max_blocks = bdev_get_queue(super->s_bdev)->max_sectors >> (super->s_blocksize_bits - 9); + + while (nr > 0) { + struct bio *bio; + int nr_blocks = min(nr, max_blocks); + int i; + int nr_used; + + bio = bio_alloc(GFP_NOIO, nr_blocks); + if (!bio) + return RETERR(-ENOMEM); + + bio->bi_bdev = super->s_bdev; + bio->bi_sector = block * (super->s_blocksize >> 9); + for (nr_used = 0, i = 0; i < nr_blocks; i++) { + struct page *pg; + ON_DEBUG(int jnode_is_releasable(jnode *)); + + assert("vs-1423", ergo(jnode_is_znode(cur) || jnode_is_unformatted(cur), JF_ISSET(cur, JNODE_SCANNED))); + pg = jnode_page(cur); + assert("zam-573", pg != NULL); + + page_cache_get(pg); + + lock_and_wait_page_writeback(pg); + + LOCK_JNODE(cur); + assert("nikita-3553", jnode_page(cur) == pg); + assert("nikita-3554", jprivate(pg) == cur); + + assert("nikita-3166", + ergo(!JF_ISSET(cur, JNODE_CC), pg->mapping == jnode_get_mapping(cur))); + if (!JF_ISSET(cur, JNODE_WRITEBACK)) { + assert("nikita-3165", !jnode_is_releasable(cur)); + UNLOCK_JNODE(cur); + if (!bio_add_page(bio, + pg, super->s_blocksize, 0)) { + /* + * underlying device is satiated. Stop + * adding pages to the bio. + */ + unlock_page(pg); + page_cache_release(pg); + break; + } + + LOCK_JNODE(cur); + JF_SET(cur, JNODE_WRITEBACK); + JF_CLR(cur, JNODE_DIRTY); + UNLOCK_JNODE(cur); + + SetPageWriteback(pg); + if (for_reclaim) + ent_writes_page(super, pg); + spin_lock(&pg->mapping->page_lock); + + if (REISER4_STATS && !PageDirty(pg)) + reiser4_stat_inc(pages_clean); + + /* don't check return value: submit page even if + it wasn't dirty. */ + test_clear_page_dirty(pg); + + list_del(&pg->list); + list_add(&pg->list, &pg->mapping->locked_pages); + + spin_unlock(&pg->mapping->page_lock); + + nr_used ++; + } else { + /* jnode being WRITEBACK might be replaced on + ovrwr_nodes list with jnode CC. We just + encountered this CC jnode. Do not submit i/o + for it */ + assert("zam-912", JF_ISSET(cur, JNODE_CC)); + UNLOCK_JNODE(cur); + } + unlock_page(pg); + + nr --; + cur = capture_list_next(cur); + } + if (nr_used > 0) { + assert("nikita-3455", + bio->bi_size == super->s_blocksize * nr_used); + assert("nikita-3456", bio->bi_vcnt == nr_used); + + /* Check if we are allowed to write at all */ + if (super->s_flags & MS_RDONLY) + undo_bio(bio); + else { + add_fq_to_bio(fq, bio); + reiser4_submit_bio(WRITE, bio); + } + + block += nr_used - 1; + update_blocknr_hint_default (super, &block); + block += 1; + } else { + reiser4_stat_inc(txnmgr.empty_bio); + bio_put(bio); + } + } + return 0; +} + +/* @nr jnodes starting from @j are marked as JNODE_SCANNED. Clear this bit for + all those jnodes */ +static void +unscan_sequence_nolock(jnode *j, int nr) +{ + int i; + + for (i = 0; i < nr; i ++) { + assert("vs-1631", JF_ISSET(j, JNODE_SCANNED)); + JF_CLR(j, JNODE_SCANNED); + j = capture_list_next(j); + } +} + +static void +unscan_sequence(jnode *j, int nr) +{ + spin_lock(&scan_lock); + unscan_sequence_nolock(j, nr); + spin_unlock(&scan_lock); +} + +/* This is a procedure which recovers a contiguous sequences of disk block + numbers in the given list of j-nodes and submits write requests on this + per-sequence basis */ +reiser4_internal int +write_jnode_list(capture_list_head *head, flush_queue_t *fq, long *nr_submitted, int flags) +{ + int ret; + jnode *beg, *end; + + spin_lock(&scan_lock); + beg = capture_list_front(head); + while (!capture_list_end(head, beg)) { + int nr = 1; + jnode *cur; + + JF_SET(beg, JNODE_SCANNED); + end = beg; + cur = capture_list_next(beg); + + while (!capture_list_end(head, cur)) { + if (*jnode_get_block(cur) != *jnode_get_block(beg) + nr) + /* jnode from which next sequence of blocks starts */ + break; + + JF_SET(cur, JNODE_SCANNED); + ++ nr; + end = cur; + cur = capture_list_next(cur); + } + spin_unlock(&scan_lock); + + ret = write_jnodes_to_disk_extent(head, beg, nr, jnode_get_block(beg), fq, flags); + if (ret) { + unscan_sequence(beg, nr); + return ret; + } + + if (nr_submitted) + *nr_submitted += nr; + + spin_lock(&scan_lock); + unscan_sequence_nolock(beg, nr); + beg = capture_list_next(end); + } + + spin_unlock(&scan_lock); + return 0; +} + +/* add given wandered mapping to atom's wandered map + this starts from jnode which is in JNODE_SCANNED state. */ +static int +add_region_to_wmap(jnode * cur, int len, const reiser4_block_nr * block_p) +{ + int ret; + blocknr_set_entry *new_bsep = NULL; + reiser4_block_nr block; + int first; + txn_atom *atom; + + assert("zam-568", block_p != NULL); + block = *block_p; + assert("zam-569", len > 0); + + while ((len--) > 0) { + assert("vs-1422", JF_ISSET(cur, JNODE_SCANNED)); + + do { + atom = get_current_atom_locked(); + assert("zam-536", !blocknr_is_fake(jnode_get_block(cur))); + ret = blocknr_set_add_pair(atom, &atom->wandered_map, &new_bsep, jnode_get_block(cur), &block); + } while (ret == -E_REPEAT); + + if (ret) { + /* deallocate blocks which were not added to wandered + map */ + reiser4_block_nr wide_len = len; + + reiser4_dealloc_blocks(&block, &wide_len, BLOCK_NOT_COUNTED, + BA_FORMATTED/* formatted, without defer */); + + return ret; + } + + UNLOCK_ATOM(atom); + + cur = capture_list_next(cur); + ++block; + first = 0; + } + + return 0; +} + +/* Allocate wandered blocks for current atom's OVERWRITE SET and immediately + submit IO for allocated blocks. We assume that current atom is in a stage + when any atom fusion is impossible and atom is unlocked and it is safe. */ +static int +alloc_wandered_blocks(struct commit_handle *ch, flush_queue_t * fq) +{ + reiser4_block_nr block; + + int rest; + int len, prev_len = 0, i; + int ret; + jnode *cur, *beg, *end; + + assert("zam-534", ch->overwrite_set_size > 0); + + cur = beg = end = NULL; + + for (rest = ch->overwrite_set_size; rest > 0; rest -= len) { + ret = get_more_wandered_blocks(rest, &block, &len); + if (ret) { + if (beg != NULL) + unscan_sequence_nolock(beg, prev_len); + return ret; + } + + spin_lock(&scan_lock); + if (beg == NULL) + cur = capture_list_front(ch->overwrite_set); + else { + unscan_sequence_nolock(beg, prev_len); + cur = capture_list_next(end); + } + beg = cur; + + /* mark @len jnodes starting from @cur as scanned */ + for (i = 0; i < len; i ++) { + assert("vs-1633", !capture_list_end(ch->overwrite_set, cur)); + assert("vs-1632", !JF_ISSET(cur, JNODE_SCANNED)); + JF_SET(cur, JNODE_SCANNED); + end = cur; + cur = capture_list_next(cur); + } + prev_len = len; + spin_unlock(&scan_lock); + + ret = add_region_to_wmap(beg, len, &block); + if (ret) { + unscan_sequence(beg, len); + return ret; + } + ret = write_jnodes_to_disk_extent(ch->overwrite_set, beg, len, &block, fq, 0); + if (ret) { + unscan_sequence(beg, len); + return ret; + } + assert("vs-1638", rest >= len); + } + + assert("vs-1634", rest == 0); + assert("vs-1635", beg != NULL && end != NULL); + assert("vs-1639", cur == capture_list_next(end)); + assert("vs-1636", capture_list_end(ch->overwrite_set, cur)); + unscan_sequence(beg, len); + + return 0; +} + +#else /* !REISER4_COPY_ON_CAPTURE */ + +/* put overwrite set back to atom's clean list */ +static void put_overwrite_set(struct commit_handle * ch) +{ + jnode * cur; + + for_all_type_safe_list(capture, ch->overwrite_set, cur) + jrelse_tail(cur); +} + +/* Count overwrite set size, grab disk space for wandered blocks allocation. + Since we have a separate list for atom's overwrite set we just scan the list, + count bitmap and other not leaf nodes which wandered blocks allocation we + have to grab space for. */ +static int +get_overwrite_set(struct commit_handle *ch) +{ + int ret; + jnode *cur; + __u64 nr_not_leaves = 0; +#if REISER4_DEBUG + __u64 nr_formatted_leaves = 0; + __u64 nr_unformatted_leaves = 0; +#endif + + + assert("zam-697", ch->overwrite_set_size == 0); + + ch->overwrite_set = ATOM_OVRWR_LIST(ch->atom); + cur = capture_list_front(ch->overwrite_set); + + while (!capture_list_end(ch->overwrite_set, cur)) { + jnode *next = capture_list_next(cur); + + if (jnode_is_znode(cur) && znode_above_root(JZNODE(cur))) { + ON_TRACE(TRACE_LOG, "fake znode found , WANDER=(%d)\n", JF_ISSET(cur, JNODE_OVRWR)); + } + + /* Count bitmap locks for getting correct statistics what number + * of blocks were cleared by the transaction commit. */ + if (jnode_get_type(cur) == JNODE_BITMAP) + ch->nr_bitmap ++; + + assert("zam-939", JF_ISSET(cur, JNODE_OVRWR) || jnode_get_type(cur) == JNODE_BITMAP); + + if (jnode_is_znode(cur) && znode_above_root(JZNODE(cur))) { + /* we replace fake znode by another (real) + znode which is suggested by disk_layout + plugin */ + + /* FIXME: it looks like fake znode should be + replaced by jnode supplied by + disk_layout. */ + + struct super_block *s = reiser4_get_current_sb(); + reiser4_super_info_data *sbinfo = get_current_super_private(); + + if (sbinfo->df_plug->log_super) { + jnode *sj = sbinfo->df_plug->log_super(s); + + assert("zam-593", sj != NULL); + + if (IS_ERR(sj)) + return PTR_ERR(sj); + + LOCK_JNODE(sj); + JF_SET(sj, JNODE_OVRWR); + insert_into_atom_ovrwr_list(ch->atom, sj); + UNLOCK_JNODE(sj); + + /* jload it as the rest of overwrite set */ + jload_gfp(sj, GFP_KERNEL, 0); + + ch->overwrite_set_size++; + } + LOCK_JNODE(cur); + uncapture_block(cur); + jput(cur); + + } else { + int ret; + ch->overwrite_set_size++; + ret = jload_gfp(cur, GFP_KERNEL, 0); + if (ret) + reiser4_panic("zam-783", "cannot load e-flushed jnode back (ret = %d)\n", ret); + } + + /* Count not leaves here because we have to grab disk space + * for wandered blocks. They were not counted as "flush + * reserved". Counting should be done _after_ nodes are pinned + * into memory by jload(). */ + if (!jnode_is_leaf(cur)) + nr_not_leaves ++; + else { +#if REISER4_DEBUG + /* at this point @cur either has JNODE_FLUSH_RESERVED + * or is eflushed. Locking is not strong enough to + * write an assertion checking for this. */ + if (jnode_is_znode(cur)) + nr_formatted_leaves ++; + else + nr_unformatted_leaves ++; +#endif + JF_CLR(cur, JNODE_FLUSH_RESERVED); + } + + cur = next; + } + + /* Grab space for writing (wandered blocks) of not leaves found in + * overwrite set. */ + ret = reiser4_grab_space_force(nr_not_leaves, BA_RESERVED); + if (ret) + return ret; + + /* Disk space for allocation of wandered blocks of leaf nodes already + * reserved as "flush reserved", move it to grabbed space counter. */ + spin_lock_atom(ch->atom); + assert("zam-940", nr_formatted_leaves + nr_unformatted_leaves <= ch->atom->flush_reserved); + flush_reserved2grabbed(ch->atom, ch->atom->flush_reserved); + spin_unlock_atom(ch->atom); + + return ch->overwrite_set_size; +} + +/* Submit a write request for @nr jnodes beginning from the @first, other jnodes + are after the @first on the double-linked "capture" list. All jnodes will be + written to the disk region of @nr blocks starting with @block_p block number. + If @fq is not NULL it means that waiting for i/o completion will be done more + efficiently by using flush_queue_t objects. + + This function is the one which writes list of jnodes in batch mode. It does + all low-level things as bio construction and page states manipulation. +*/ +static int +write_jnodes_to_disk_extent(capture_list_head * head, jnode * first, int nr, + const reiser4_block_nr * block_p, flush_queue_t * fq, int flags) +{ + struct super_block *super = reiser4_get_current_sb(); + int for_reclaim = flags & WRITEOUT_FOR_PAGE_RECLAIM; + int max_blocks; + jnode *cur = first; + reiser4_block_nr block; + + assert("zam-571", first != NULL); + assert("zam-572", block_p != NULL); + assert("zam-570", nr > 0); + + block = *block_p; + + ON_TRACE (TRACE_IO_W, "write of %d blocks starting from %llu\n", + nr, (unsigned long long)block); + + max_blocks = bdev_get_queue(super->s_bdev)->max_sectors >> (super->s_blocksize_bits - 9); + + while (nr > 0) { + struct bio *bio; + int nr_blocks = min(nr, max_blocks); + int i; + int nr_used; + + bio = bio_alloc(GFP_NOIO, nr_blocks); + if (!bio) + return RETERR(-ENOMEM); + + bio->bi_bdev = super->s_bdev; + bio->bi_sector = block * (super->s_blocksize >> 9); + for (nr_used = 0, i = 0; i < nr_blocks; i++) { + struct page *pg; + ON_DEBUG(int jnode_is_releasable(jnode *)); + + pg = jnode_page(cur); + assert("zam-573", pg != NULL); + + page_cache_get(pg); + + lock_and_wait_page_writeback(pg); + + if (!bio_add_page(bio, pg, super->s_blocksize, 0)) { + /* + * underlying device is satiated. Stop adding + * pages to the bio. + */ + unlock_page(pg); + page_cache_release(pg); + break; + } + + LOCK_JNODE(cur); + ON_DEBUG_MODIFY(znode_set_checksum(cur, 1)); + assert("nikita-3166", + pg->mapping == jnode_get_mapping(cur)); + assert("zam-912", !JF_ISSET(cur, JNODE_WRITEBACK)); + assert("nikita-3165", !jnode_is_releasable(cur)); + JF_SET(cur, JNODE_WRITEBACK); + JF_CLR(cur, JNODE_DIRTY); + UNLOCK_JNODE(cur); + + if (REISER4_STATS && !PageDirty(pg)) + reiser4_stat_inc(pages_clean); + + set_page_writeback(pg); + if (for_reclaim) + ent_writes_page(super, pg); + /* clear DIRTY or REISER4_MOVED tag if it is set */ + reiser4_clear_page_dirty(pg); + + unlock_page(pg); + + cur = capture_list_next(cur); + nr_used ++; + } + if (nr_used > 0) { + assert("nikita-3453", + bio->bi_size == super->s_blocksize * nr_used); + assert("nikita-3454", bio->bi_vcnt == nr_used); + + /* Check if we are allowed to write at all */ + if (super->s_flags & MS_RDONLY) + undo_bio(bio); + else { + add_fq_to_bio(fq, bio); + reiser4_submit_bio(WRITE, bio); + } + + block += nr_used - 1; + update_blocknr_hint_default (super, &block); + block += 1; + } else { + reiser4_stat_inc(txnmgr.empty_bio); + bio_put(bio); + } + nr -= nr_used; + } + + return 0; +} + +/* This is a procedure which recovers a contiguous sequences of disk block + numbers in the given list of j-nodes and submits write requests on this + per-sequence basis */ +reiser4_internal int +write_jnode_list (capture_list_head * head, flush_queue_t * fq, long *nr_submitted, int flags) +{ + int ret; + jnode *beg = capture_list_front(head); + + while (!capture_list_end(head, beg)) { + int nr = 1; + jnode *cur = capture_list_next(beg); + + while (!capture_list_end(head, cur)) { + if (*jnode_get_block(cur) != *jnode_get_block(beg) + nr) + break; + ++nr; + cur = capture_list_next(cur); + } + + ret = write_jnodes_to_disk_extent(head, beg, nr, jnode_get_block(beg), fq, flags); + if (ret) + return ret; + + if (nr_submitted) + *nr_submitted += nr; + + beg = cur; + } + + return 0; +} + +/* add given wandered mapping to atom's wandered map */ +static int +add_region_to_wmap(jnode * cur, int len, const reiser4_block_nr * block_p) +{ + int ret; + blocknr_set_entry *new_bsep = NULL; + reiser4_block_nr block; + + txn_atom *atom; + + assert("zam-568", block_p != NULL); + block = *block_p; + assert("zam-569", len > 0); + + while ((len--) > 0) { + do { + atom = get_current_atom_locked(); + assert("zam-536", !blocknr_is_fake(jnode_get_block(cur))); + ret = blocknr_set_add_pair(atom, &atom->wandered_map, &new_bsep, jnode_get_block(cur), &block); + } while (ret == -E_REPEAT); + + if (ret) { + /* deallocate blocks which were not added to wandered + map */ + reiser4_block_nr wide_len = len; + + reiser4_dealloc_blocks(&block, &wide_len, BLOCK_NOT_COUNTED, + BA_FORMATTED/* formatted, without defer */); + + return ret; + } + + UNLOCK_ATOM(atom); + + cur = capture_list_next(cur); + ++block; + } + + return 0; +} + +/* Allocate wandered blocks for current atom's OVERWRITE SET and immediately + submit IO for allocated blocks. We assume that current atom is in a stage + when any atom fusion is impossible and atom is unlocked and it is safe. */ +reiser4_internal int +alloc_wandered_blocks(struct commit_handle *ch, flush_queue_t * fq) +{ + reiser4_block_nr block; + + int rest; + int len; + int ret; + + jnode *cur; + + assert("zam-534", ch->overwrite_set_size > 0); + + rest = ch->overwrite_set_size; + + cur = capture_list_front(ch->overwrite_set); + while (!capture_list_end(ch->overwrite_set, cur)) { + assert("zam-567", JF_ISSET(cur, JNODE_OVRWR)); + + ret = get_more_wandered_blocks(rest, &block, &len); + if (ret) + return ret; + + rest -= len; + + ret = add_region_to_wmap(cur, len, &block); + if (ret) + return ret; + + ret = write_jnodes_to_disk_extent(ch->overwrite_set, cur, len, &block, fq, 0); + if (ret) + return ret; + + while ((len--) > 0) { + assert("zam-604", !capture_list_end(ch->overwrite_set, cur)); + cur = capture_list_next(cur); + } + } + + return 0; +} + +#endif /* ! REISER4_COPY_ON_CAPTURE */ + +/* allocate given number of nodes over the journal area and link them into a + list, return pointer to the first jnode in the list */ +static int +alloc_tx(struct commit_handle *ch, flush_queue_t * fq) +{ + reiser4_blocknr_hint hint; + + reiser4_block_nr allocated = 0; + reiser4_block_nr first, len; + + jnode *cur; + jnode *txhead; + int ret; + + assert("zam-698", ch->tx_size > 0); + assert("zam-699", capture_list_empty(&ch->tx_list)); + + while (allocated < (unsigned) ch->tx_size) { + len = (ch->tx_size - allocated); + + blocknr_hint_init(&hint); + + hint.block_stage = BLOCK_GRABBED; + + /* FIXME: there should be some block allocation policy for + nodes which contain wander records */ + + /* We assume that disk space for wandered record blocks can be + * taken from reserved area. */ + ret = reiser4_alloc_blocks (&hint, &first, &len, + BA_FORMATTED | BA_RESERVED | BA_USE_DEFAULT_SEARCH_START); + + blocknr_hint_done(&hint); + + if (ret) + return ret; + + allocated += len; + + /* create jnodes for all wander records */ + while (len--) { + cur = alloc_io_head(&first); + + if (cur == NULL) { + ret = RETERR(-ENOMEM); + goto free_not_assigned; + } + + ret = jinit_new(cur, GFP_KERNEL); + + if (ret != 0) { + jfree(cur); + goto free_not_assigned; + } + + pin_jnode_data(cur); + + capture_list_push_back(&ch->tx_list, cur); + + first++; + } + } + + { /* format a on-disk linked list of wander records */ + int serial = 1; + + txhead = capture_list_front(&ch->tx_list); + format_tx_head(ch); + + cur = capture_list_next(txhead); + while (!capture_list_end(&ch->tx_list, cur)) { + format_wander_record(ch, cur, serial++); + cur = capture_list_next(cur); + } + + } + + { /* Fill wander records with Wandered Set */ + struct store_wmap_params params; + txn_atom *atom; + + params.cur = capture_list_next(txhead); + + params.idx = 0; + params.capacity = wander_record_capacity(reiser4_get_current_sb()); + + atom = get_current_atom_locked(); + blocknr_set_iterator(atom, &atom->wandered_map, &store_wmap_actor, ¶ms, 0); + UNLOCK_ATOM(atom); + } + + { /* relse all jnodes from tx_list */ + cur = capture_list_front(&ch->tx_list); + while (!capture_list_end(&ch->tx_list, cur)) { + jrelse(cur); + cur = capture_list_next(cur); + } + } + + ret = write_jnode_list(&ch->tx_list, fq, NULL, 0); + + return ret; + +free_not_assigned: + /* We deallocate blocks not yet assigned to jnodes on tx_list. The + caller takes care about invalidating of tx list */ + reiser4_dealloc_blocks(&first, &len, BLOCK_NOT_COUNTED, BA_FORMATTED); + + return ret; +} + +/* We assume that at this moment all captured blocks are marked as RELOC or + WANDER (belong to Relocate o Overwrite set), all nodes from Relocate set + are submitted to write. +*/ + +reiser4_internal int reiser4_write_logs(long * nr_submitted) +{ + txn_atom *atom; + + struct super_block *super = reiser4_get_current_sb(); + reiser4_super_info_data *sbinfo = get_super_private(super); + + struct commit_handle ch; + + int ret; + +#if REISER4_STATS + unsigned long commit_start_time = jiffies; +#endif + writeout_mode_enable(); + + /* block allocator may add j-nodes to the clean_list */ + ret = pre_commit_hook(); + if (ret) + return ret; + + /* No locks are required if we take atom which stage >= + * ASTAGE_PRE_COMMIT */ + atom = get_current_context()->trans->atom; + assert("zam-965", atom != NULL); + + /* relocate set is on the atom->clean_nodes list after + * current_atom_complete_writes() finishes. It can be safely + * uncaptured after commit_semaphore is taken, because any atom that + * captures these nodes is guaranteed to commit after current one. + * + * This can only be done after pre_commit_hook(), because it is where + * early flushed jnodes with CREATED bit are transferred to the + * overwrite list. */ + invalidate_list(ATOM_CLEAN_LIST(atom)); + LOCK_ATOM(atom); + /* There might be waiters for the relocate nodes which we have + * released, wake them up. */ + atom_send_event(atom); + UNLOCK_ATOM(atom); + + /* trace_mark(wander); */ + write_current_logf(WRITE_IO_LOG, "mark=wander\n"); + + if (REISER4_DEBUG) { + int level; + + for (level = 0; level < REAL_MAX_ZTREE_HEIGHT + 1; ++ level) + assert("nikita-3352", + capture_list_empty(ATOM_DIRTY_LIST(atom, level))); + } + + sbinfo->nr_files_committed += (unsigned) atom->nr_objects_created; + sbinfo->nr_files_committed -= (unsigned) atom->nr_objects_deleted; + + init_commit_handle(&ch, atom); + + ch.free_blocks = sbinfo->blocks_free_committed; + ch.nr_files = sbinfo->nr_files_committed; + /* ZAM-FIXME-HANS: email me what the contention level is for the super + * lock. */ + ch.next_oid = oid_next(super); + + /* count overwrite set and place it in a separate list */ + ret = get_overwrite_set(&ch); + + if (ret <= 0) { + /* It is possible that overwrite set is empty here, it means + all captured nodes are clean */ + goto up_and_ret; + } + + /* Inform the caller about what number of dirty pages will be + * submitted to disk. */ + *nr_submitted += ch.overwrite_set_size - ch.nr_bitmap; + + ON_TRACE(TRACE_LOG, "commit atom (id = %u, count = %u)\n", atom->atom_id, atom->capture_count); + + /* count all records needed for storing of the wandered set */ + get_tx_size(&ch); + + /* Grab more space for wandered records. */ + ret = reiser4_grab_space_force((__u64)(ch.tx_size), BA_RESERVED); + if (ret) + goto up_and_ret; + + { + flush_queue_t *fq; + + fq = get_fq_for_current_atom(); + + if (IS_ERR(fq)) { + ret = PTR_ERR(fq); + goto up_and_ret; + } + + UNLOCK_ATOM(fq->atom); + + do { + ret = alloc_wandered_blocks(&ch, fq); + if (ret) + break; + + ret = alloc_tx(&ch, fq); + if (ret) + break; + } while (0); + + + /* Release all grabbed space if it was not fully used for + * wandered blocks/records allocation. */ + all_grabbed2free(); + + fq_put(fq); + if (ret) + goto up_and_ret; + } + + ret = current_atom_finish_all_fq(); + if (ret) + goto up_and_ret; + + ON_TRACE(TRACE_LOG, "overwrite set (%u blocks) written to wandered locations\n", ch.overwrite_set_size); + + if ((ret = update_journal_header(&ch))) + goto up_and_ret; + + ON_TRACE(TRACE_LOG, + "journal header updated (tx head at block %s)\n", + sprint_address(jnode_get_block(capture_list_front(&ch.tx_list)))); + + reiser4_stat_inc(txnmgr.commits); + + UNDER_SPIN_VOID(atom, atom, atom_set_stage(atom, ASTAGE_POST_COMMIT)); + + /* trace_mark(ovrwr); */ + write_current_logf(WRITE_IO_LOG, "mark=ovrwr\n"); + + post_commit_hook(); + + { + /* force j-nodes write back */ + + flush_queue_t *fq; + + fq = get_fq_for_current_atom(); + + if (IS_ERR(fq)) { + ret = PTR_ERR(fq); + goto up_and_ret; + } + + UNLOCK_ATOM(fq->atom); + + ret = write_jnode_list(ch.overwrite_set, fq, NULL, WRITEOUT_FOR_PAGE_RECLAIM); + + fq_put(fq); + + if (ret) + goto up_and_ret; + } + + ret = current_atom_finish_all_fq(); + + if (ret) + goto up_and_ret; + + ON_TRACE(TRACE_LOG, "overwrite set written in place\n"); + + if ((ret = update_journal_footer(&ch))) + goto up_and_ret; + + ON_TRACE(TRACE_LOG, + "journal footer updated (tx head at block %s)\n", + sprint_address(jnode_get_block(capture_list_front(&ch.tx_list)))); + + post_write_back_hook(); + + reiser4_stat_inc(txnmgr.post_commit_writes); + reiser4_stat_add(txnmgr.time_spent_in_commits, jiffies - commit_start_time); + +up_and_ret: + if (ret) { + /* there could be fq attached to current atom; the only way to + remove them is: */ + current_atom_finish_all_fq(); + } + + /* free blocks of flushed transaction */ + dealloc_tx_list(&ch); + dealloc_wmap(&ch); + + put_overwrite_set(&ch); + + done_commit_handle(&ch); + + writeout_mode_disable(); + + return ret; +} + +/* consistency checks for journal data/control blocks: header, footer, log + records, transactions head blocks. All functions return zero on success. */ + +static int +check_journal_header(const jnode * node UNUSED_ARG) +{ + /* FIXME: journal header has no magic field yet. */ + return 0; +} + +/* wait for write completion for all jnodes from given list */ +static int +wait_on_jnode_list(capture_list_head * head) +{ + jnode *scan; + int ret = 0; + + for_all_type_safe_list(capture, head, scan) { + struct page *pg = jnode_page(scan); + + if (pg) { + if (PageWriteback(pg)) + wait_on_page_writeback(pg); + + if (PageError(pg)) + ret++; + } + } + + return ret; +} + +static int +check_journal_footer(const jnode * node UNUSED_ARG) +{ + /* FIXME: journal footer has no magic field yet. */ + return 0; +} + +static int +check_tx_head(const jnode * node) +{ + struct tx_header *header = (struct tx_header *) jdata(node); + + if (memcmp(&header->magic, TX_HEADER_MAGIC, TX_HEADER_MAGIC_SIZE) != 0) { + warning("zam-627", "tx head at block %s corrupted\n", sprint_address(jnode_get_block(node))); + return RETERR(-EIO); + } + + return 0; +} + +static int +check_wander_record(const jnode * node) +{ + struct wander_record_header *RH = (struct wander_record_header *) jdata(node); + + if (memcmp(&RH->magic, WANDER_RECORD_MAGIC, WANDER_RECORD_MAGIC_SIZE) != 0) { + warning("zam-628", "wander record at block %s corrupted\n", sprint_address(jnode_get_block(node))); + return RETERR(-EIO); + } + + return 0; +} + +/* fill commit_handler structure by everything what is needed for update_journal_footer */ +static int +restore_commit_handle(struct commit_handle *ch, jnode * tx_head) +{ + struct tx_header *TXH; + int ret; + + ret = jload(tx_head); + + if (ret) + return ret; + + TXH = (struct tx_header *) jdata(tx_head); + + ch->free_blocks = d64tocpu(&TXH->free_blocks); + ch->nr_files = d64tocpu(&TXH->nr_files); + ch->next_oid = d64tocpu(&TXH->next_oid); + + jrelse(tx_head); + + capture_list_push_front(&ch->tx_list, tx_head); + + return 0; +} + +/* replay one transaction: restore and write overwrite set in place */ +static int +replay_transaction(const struct super_block *s, + jnode * tx_head, + const reiser4_block_nr * log_rec_block_p, + const reiser4_block_nr * end_block, unsigned int nr_wander_records) +{ + reiser4_block_nr log_rec_block = *log_rec_block_p; + struct commit_handle ch; + capture_list_head overwrite_set; + jnode *log; + int ret; + + init_commit_handle(&ch, NULL); + capture_list_init(&overwrite_set); + ch.overwrite_set = &overwrite_set; + + restore_commit_handle(&ch, tx_head); + + while (log_rec_block != *end_block) { + struct wander_record_header *header; + struct wander_entry *entry; + + int i; + + if (nr_wander_records == 0) { + warning("zam-631", + "number of wander records in the linked list" " greater than number stored in tx head.\n"); + ret = RETERR(-EIO); + goto free_ow_set; + } + + log = alloc_io_head(&log_rec_block); + if (log == NULL) + return RETERR(-ENOMEM); + + ret = jload(log); + if (ret < 0) { + drop_io_head(log); + return ret; + } + + ret = check_wander_record(log); + if (ret) { + jrelse(log); + drop_io_head(log); + return ret; + } + + header = (struct wander_record_header *) jdata(log); + log_rec_block = d64tocpu(&header->next_block); + + entry = (struct wander_entry *) (header + 1); + + /* restore overwrite set from wander record content */ + for (i = 0; i < wander_record_capacity(s); i++) { + reiser4_block_nr block; + jnode *node; + + block = d64tocpu(&entry->wandered); + + if (block == 0) + break; + + node = alloc_io_head(&block); + if (node == NULL) { + ret = RETERR(-ENOMEM); + /* + * FIXME-VS:??? + */ + jrelse(log); + drop_io_head(log); + goto free_ow_set; + } + + ret = jload(node); + + if (ret < 0) { + drop_io_head(node); + /* + * FIXME-VS:??? + */ + jrelse(log); + drop_io_head(log); + goto free_ow_set; + } + + block = d64tocpu(&entry->original); + + assert("zam-603", block != 0); + + jnode_set_block(node, &block); + + capture_list_push_back(ch.overwrite_set, node); + + ++entry; + } + + jrelse(log); + drop_io_head(log); + + --nr_wander_records; + } + + if (nr_wander_records != 0) { + warning("zam-632", "number of wander records in the linked list" + " less than number stored in tx head.\n"); + ret = RETERR(-EIO); + goto free_ow_set; + } + + { /* write wandered set in place */ + write_jnode_list(ch.overwrite_set, 0, NULL, 0); + ret = wait_on_jnode_list(ch.overwrite_set); + + if (ret) { + ret = RETERR(-EIO); + goto free_ow_set; + } + } + + ret = update_journal_footer(&ch); + +free_ow_set: + + while (!capture_list_empty(ch.overwrite_set)) { + jnode *cur = capture_list_front(ch.overwrite_set); + capture_list_remove_clean (cur); + jrelse(cur); + drop_io_head(cur); + } + + capture_list_remove_clean (tx_head); + + done_commit_handle(&ch); + + return ret; +} + +/* find oldest committed and not played transaction and play it. The transaction + * was committed and journal header block was updated but the blocks from the + * process of writing the atom's overwrite set in-place and updating of journal + * footer block were not completed. This function completes the process by + * recovering the atom's overwrite set from their wandered locations and writes + * them in-place and updating the journal footer. */ +static int +replay_oldest_transaction(struct super_block *s) +{ + reiser4_super_info_data *sbinfo = get_super_private(s); + jnode *jf = sbinfo->journal_footer; + unsigned int total; + struct journal_footer *F; + struct tx_header *T; + + reiser4_block_nr prev_tx; + reiser4_block_nr last_flushed_tx; + reiser4_block_nr log_rec_block = 0; + + jnode *tx_head; + + int ret; + + if ((ret = jload(jf)) < 0) + return ret; + + F = (struct journal_footer *) jdata(jf); + + last_flushed_tx = d64tocpu(&F->last_flushed_tx); + + jrelse(jf); + + if (sbinfo->last_committed_tx == last_flushed_tx) { + /* all transactions are replayed */ + return 0; + } + + ON_TRACE(TRACE_REPLAY, "not flushed transactions found."); + + prev_tx = sbinfo->last_committed_tx; + + /* searching for oldest not flushed transaction */ + while (1) { + tx_head = alloc_io_head(&prev_tx); + if (!tx_head) + return RETERR(-ENOMEM); + + ret = jload(tx_head); + if (ret < 0) { + drop_io_head(tx_head); + return ret; + } + + ret = check_tx_head(tx_head); + if (ret) { + jrelse(tx_head); + drop_io_head(tx_head); + return ret; + } + + T = (struct tx_header *) jdata(tx_head); + + prev_tx = d64tocpu(&T->prev_tx); + + if (prev_tx == last_flushed_tx) + break; + + jrelse(tx_head); + drop_io_head(tx_head); + } + + total = d32tocpu(&T->total); + log_rec_block = d64tocpu(&T->next_block); + + ON_TRACE(TRACE_REPLAY, + "not flushed transaction found (head block %s, %u wander records)\n", + sprint_address(jnode_get_block(tx_head)), total); + + pin_jnode_data(tx_head); + jrelse(tx_head); + + ret = replay_transaction(s, tx_head, &log_rec_block, jnode_get_block(tx_head), total - 1); + + unpin_jnode_data(tx_head); + drop_io_head(tx_head); + + if (ret) + return ret; + return -E_REPEAT; +} + +/* The reiser4 journal current implementation was optimized to not to capture + super block if certain super blocks fields are modified. Currently, the set + is (, ). These fields are logged by + special way which includes storing them in each transaction head block at + atom commit time and writing that information to journal footer block at + atom flush time. For getting info from journal footer block to the + in-memory super block there is a special function + reiser4_journal_recover_sb_data() which should be called after disk format + plugin re-reads super block after journal replaying. +*/ + +/* get the information from journal footer in-memory super block */ +reiser4_internal int +reiser4_journal_recover_sb_data(struct super_block *s) +{ + reiser4_super_info_data *sbinfo = get_super_private(s); + struct journal_footer *jf; + int ret; + + assert("zam-673", sbinfo->journal_footer != NULL); + + ret = jload(sbinfo->journal_footer); + if (ret != 0) + return ret; + + ret = check_journal_footer(sbinfo->journal_footer); + if (ret != 0) + goto out; + + jf = (struct journal_footer *) jdata(sbinfo->journal_footer); + + /* was there at least one flushed transaction? */ + if (d64tocpu(&jf->last_flushed_tx)) { + + /* restore free block counter logged in this transaction */ + reiser4_set_free_blocks(s, d64tocpu(&jf->free_blocks)); + + /* restore oid allocator state */ + oid_init_allocator(s, + d64tocpu(&jf->nr_files), + d64tocpu(&jf->next_oid)); + } +out: + jrelse(sbinfo->journal_footer); + return ret; +} + +/* reiser4 replay journal procedure */ +reiser4_internal int +reiser4_journal_replay(struct super_block *s) +{ + reiser4_super_info_data *sbinfo = get_super_private(s); + jnode *jh, *jf; + + struct journal_header *header; + int nr_tx_replayed = 0; + + int ret; + + assert("zam-582", sbinfo != NULL); + + jh = sbinfo->journal_header; + jf = sbinfo->journal_footer; + + if (!jh || !jf) { + /* it is possible that disk layout does not support journal + structures, we just warn about this */ + warning("zam-583", + "journal control blocks were not loaded by disk layout plugin. " + "journal replaying is not possible.\n"); + return 0; + } + + /* Take free block count from journal footer block. The free block + counter value corresponds the last flushed transaction state */ + ret = jload(jf); + if (ret < 0) + return ret; + + ret = check_journal_footer(jf); + if (ret) { + jrelse(jf); + return ret; + } + + jrelse(jf); + + /* store last committed transaction info in reiser4 in-memory super + block */ + ret = jload(jh); + if (ret < 0) + return ret; + + ret = check_journal_header(jh); + if (ret) { + jrelse(jh); + return ret; + } + + header = (struct journal_header *) jdata(jh); + sbinfo->last_committed_tx = d64tocpu(&header->last_committed_tx); + + jrelse(jh); + + /* replay committed transactions */ + while ((ret = replay_oldest_transaction(s)) == -E_REPEAT) + nr_tx_replayed++; + + ON_TRACE(TRACE_REPLAY, "%d transactions replayed ret = %d", nr_tx_replayed, ret); + + return ret; +} +/* load journal control block (either journal header or journal footer block) */ +static int +load_journal_control_block(jnode ** node, const reiser4_block_nr * block) +{ + int ret; + + *node = alloc_io_head(block); + if (!(*node)) + return RETERR(-ENOMEM); + + ret = jload(*node); + + if (ret) { + drop_io_head(*node); + *node = NULL; + return ret; + } + + pin_jnode_data(*node); + jrelse(*node); + + return 0; +} + +/* unload journal header or footer and free jnode */ +static void +unload_journal_control_block(jnode ** node) +{ + if (*node) { + unpin_jnode_data(*node); + drop_io_head(*node); + *node = NULL; + } +} + +/* release journal control blocks */ +reiser4_internal void +done_journal_info(struct super_block *s) +{ + reiser4_super_info_data *sbinfo = get_super_private(s); + + assert("zam-476", sbinfo != NULL); + + unload_journal_control_block(&sbinfo->journal_header); + unload_journal_control_block(&sbinfo->journal_footer); +} + +/* load journal control blocks */ +reiser4_internal int +init_journal_info(struct super_block *s) +{ + reiser4_super_info_data *sbinfo = get_super_private(s); + journal_location *loc; + int ret; + + loc = &sbinfo->jloc; + + assert("zam-651", loc != NULL); + assert("zam-652", loc->header != 0); + assert("zam-653", loc->footer != 0); + + ret = load_journal_control_block(&sbinfo->journal_header, &loc->header); + + if (ret) + return ret; + + ret = load_journal_control_block(&sbinfo->journal_footer, &loc->footer); + + if (ret) { + unload_journal_control_block(&sbinfo->journal_header); + } + + return ret; +} + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 80 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/wander.h 2004-09-03 00:57:05.483205688 -0700 @@ -0,0 +1,135 @@ +/* Copyright 2002, 2003 by Hans Reiser, licensing governed by reiser4/README */ + +#if !defined (__FS_REISER4_WANDER_H__) +#define __FS_REISER4_WANDER_H__ + +#include "dformat.h" + +#include /* for struct super_block */ + +/* REISER4 JOURNAL ON-DISK DATA STRUCTURES */ + +#define TX_HEADER_MAGIC "TxMagic4" +#define WANDER_RECORD_MAGIC "LogMagc4" + +#define TX_HEADER_MAGIC_SIZE (8) +#define WANDER_RECORD_MAGIC_SIZE (8) + +/* journal header block format */ +struct journal_header { + /* last written transaction head location */ + d64 last_committed_tx; +}; + +typedef struct journal_location { + reiser4_block_nr footer; + reiser4_block_nr header; +} journal_location; + +/* The wander.c head comment describes usage and semantic of all these structures */ +/* journal footer block format */ +struct journal_footer { + /* last flushed transaction location. */ + /* This block number is no more valid after the transaction it points + to gets flushed, this number is used only at journal replaying time + for detection of the end of on-disk list of committed transactions + which were not flushed completely */ + d64 last_flushed_tx; + + /* free block counter is written in journal footer at transaction + flushing , not in super block because free blocks counter is logged + by another way than super block fields (root pointer, for + example). */ + d64 free_blocks; + + /* number of used OIDs and maximal used OID are logged separately from + super block */ + d64 nr_files; + d64 next_oid; +}; + +/* Each wander record (except the first one) has unified format with wander + record header followed by an array of log entries */ +struct wander_record_header { + /* when there is no predefined location for wander records, this magic + string should help reiser4fsck. */ + char magic[WANDER_RECORD_MAGIC_SIZE]; + + /* transaction id */ + d64 id; + + /* total number of wander records in current transaction */ + d32 total; + + /* this block number in transaction */ + d32 serial; + + /* number of previous block in commit */ + d64 next_block; +}; + +/* The first wander record (transaction head) of written transaction has the + special format */ +struct tx_header { + /* magic string makes first block in transaction different from other + logged blocks, it should help fsck. */ + char magic[TX_HEADER_MAGIC_SIZE]; + + /* transaction id */ + d64 id; + + /* total number of records (including this first tx head) in the + transaction */ + d32 total; + + /* align next field to 8-byte boundary; this field always is zero */ + d32 padding; + + /* block number of previous transaction head */ + d64 prev_tx; + + /* next wander record location */ + d64 next_block; + + /* committed versions of free blocks counter */ + d64 free_blocks; + + /* number of used OIDs (nr_files) and maximal used OID are logged + separately from super block */ + d64 nr_files; + d64 next_oid; +}; + +/* A transaction gets written to disk as a set of wander records (each wander + record size is fs block) */ + +/* As it was told above a wander The rest of wander record is filled by these log entries, unused space filled + by zeroes */ +struct wander_entry { + d64 original; /* block original location */ + d64 wandered; /* block wandered location */ +}; + +/* REISER4 JOURNAL WRITER FUNCTIONS */ + +extern int reiser4_write_logs(long *); +extern int reiser4_journal_replay(struct super_block *); +extern int reiser4_journal_recover_sb_data(struct super_block *); + +extern int init_journal_info(struct super_block *); +extern void done_journal_info(struct super_block *); + +extern int write_jnode_list (capture_list_head*, flush_queue_t*, long*, int); + +#endif /* __FS_REISER4_WANDER_H__ */ + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 80 + scroll-step: 1 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/writeout.h 2004-09-03 00:57:05.483205688 -0700 @@ -0,0 +1,21 @@ +/* Copyright 2002, 2003, 2004 by Hans Reiser, licensing governed by reiser4/README */ + +#if !defined (__FS_REISER4_WRITEOUT_H__) + +#define WRITEOUT_SINGLE_STREAM (0x1) +#define WRITEOUT_FOR_PAGE_RECLAIM (0x2) + +extern int get_writeout_flags(void); + +#endif /* __FS_REISER4_WRITEOUT_H__ */ + + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 80 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/znode.c 2004-09-03 00:57:07.762859128 -0700 @@ -0,0 +1,1360 @@ +/* Copyright 2001, 2002, 2003 by Hans Reiser, licensing governed by + * reiser4/README */ +/* Znode manipulation functions. */ +/* Znode is the in-memory header for a tree node. It is stored + separately from the node itself so that it does not get written to + disk. In this respect znode is like buffer head or page head. We + also use znodes for additional reiser4 specific purposes: + + . they are organized into tree structure which is a part of whole + reiser4 tree. + . they are used to implement node grained locking + . they are used to keep additional state associated with a + node + . they contain links to lists used by the transaction manager + + Znode is attached to some variable "block number" which is instance of + fs/reiser4/tree.h:reiser4_block_nr type. Znode can exist without + appropriate node being actually loaded in memory. Existence of znode itself + is regulated by reference count (->x_count) in it. Each time thread + acquires reference to znode through call to zget(), ->x_count is + incremented and decremented on call to zput(). Data (content of node) are + brought in memory through call to zload(), which also increments ->d_count + reference counter. zload can block waiting on IO. Call to zrelse() + decreases this counter. Also, ->c_count keeps track of number of child + znodes and prevents parent znode from being recycled until all of its + children are. ->c_count is decremented whenever child goes out of existence + (being actually recycled in zdestroy()) which can be some time after last + reference to this child dies if we support some form of LRU cache for + znodes. + +*/ +/* EVERY ZNODE'S STORY + + 1. His infancy. + + Once upon a time, the znode was born deep inside of zget() by call to + zalloc(). At the return from zget() znode had: + + . reference counter (x_count) of 1 + . assigned block number, marked as used in bitmap + . pointer to parent znode. Root znode parent pointer points + to its father: "fake" znode. This, in turn, has NULL parent pointer. + . hash table linkage + . no data loaded from disk + . no node plugin + . no sibling linkage + + 2. His childhood + + Each node is either brought into memory as a result of tree traversal, or + created afresh, creation of the root being a special case of the latter. In + either case it's inserted into sibling list. This will typically require + some ancillary tree traversing, but ultimately both sibling pointers will + exist and JNODE_LEFT_CONNECTED and JNODE_RIGHT_CONNECTED will be true in + zjnode.state. + + 3. His youth. + + If znode is bound to already existing node in a tree, its content is read + from the disk by call to zload(). At that moment, JNODE_LOADED bit is set + in zjnode.state and zdata() function starts to return non null for this + znode. zload() further calls zparse() that determines which node layout + this node is rendered in, and sets ->nplug on success. + + If znode is for new node just created, memory for it is allocated and + zinit_new() function is called to initialise data, according to selected + node layout. + + 4. His maturity. + + After this point, znode lingers in memory for some time. Threads can + acquire references to znode either by blocknr through call to zget(), or by + following a pointer to unallocated znode from internal item. Each time + reference to znode is obtained, x_count is increased. Thread can read/write + lock znode. Znode data can be loaded through calls to zload(), d_count will + be increased appropriately. If all references to znode are released + (x_count drops to 0), znode is not recycled immediately. Rather, it is + still cached in the hash table in the hope that it will be accessed + shortly. + + There are two ways in which znode existence can be terminated: + + . sudden death: node bound to this znode is removed from the tree + . overpopulation: znode is purged out of memory due to memory pressure + + 5. His death. + + Death is complex process. + + When we irrevocably commit ourselves to decision to remove node from the + tree, JNODE_HEARD_BANSHEE bit is set in zjnode.state of corresponding + znode. This is done either in ->kill_hook() of internal item or in + kill_root() function when tree root is removed. + + At this moment znode still has: + + . locks held on it, necessary write ones + . references to it + . disk block assigned to it + . data loaded from the disk + . pending requests for lock + + But once JNODE_HEARD_BANSHEE bit set, last call to unlock_znode() does node + deletion. Node deletion includes two phases. First all ways to get + references to that znode (sibling and parent links and hash lookup using + block number stored in parent node) should be deleted -- it is done through + sibling_list_remove(), also we assume that nobody uses down link from + parent node due to its nonexistence or proper parent node locking and + nobody uses parent pointers from children due to absence of them. Second we + invalidate all pending lock requests which still are on znode's lock + request queue, this is done by invalidate_lock(). Another JNODE_IS_DYING + znode status bit is used to invalidate pending lock requests. Once it set + all requesters are forced to return -EINVAL from + longterm_lock_znode(). Future locking attempts are not possible because all + ways to get references to that znode are removed already. Last, node is + uncaptured from transaction. + + When last reference to the dying znode is just about to be released, + block number for this lock is released and znode is removed from the + hash table. + + Now znode can be recycled. + + [it's possible to free bitmap block and remove znode from the hash + table when last lock is released. This will result in having + referenced but completely orphaned znode] + + 6. Limbo + + As have been mentioned above znodes with reference counter 0 are + still cached in a hash table. Once memory pressure increases they are + purged out of there [this requires something like LRU list for + efficient implementation. LRU list would also greatly simplify + implementation of coord cache that would in this case morph to just + scanning some initial segment of LRU list]. Data loaded into + unreferenced znode are flushed back to the durable storage if + necessary and memory is freed. Znodes themselves can be recycled at + this point too. + +*/ + +#include "debug.h" +#include "dformat.h" +#include "key.h" +#include "coord.h" +#include "plugin/plugin_header.h" +#include "plugin/node/node.h" +#include "plugin/plugin.h" +#include "txnmgr.h" +#include "jnode.h" +#include "znode.h" +#include "block_alloc.h" +#include "tree.h" +#include "tree_walk.h" +#include "super.h" +#include "reiser4.h" +#include "prof.h" + +#include +#include +#include +#include + +/* hash table support */ + +/* compare two block numbers for equality. Used by hash-table macros */ +static inline int +blknreq(const reiser4_block_nr * b1, const reiser4_block_nr * b2) +{ + assert("nikita-534", b1 != NULL); + assert("nikita-535", b2 != NULL); + + return *b1 == *b2; +} + +/* Hash znode by block number. Used by hash-table macros */ +/* Audited by: umka (2002.06.11) */ +static inline __u32 +blknrhashfn(z_hash_table *table, const reiser4_block_nr * b) +{ + assert("nikita-536", b != NULL); + + return *b & (REISER4_ZNODE_HASH_TABLE_SIZE - 1); +} + +/* The hash table definition */ +#define KMALLOC(size) reiser4_kmalloc((size), GFP_KERNEL) +#define KFREE(ptr, size) reiser4_kfree(ptr) +TYPE_SAFE_HASH_DEFINE(z, znode, reiser4_block_nr, zjnode.key.z, zjnode.link.z, blknrhashfn, blknreq); +#undef KFREE +#undef KMALLOC + +/* slab for znodes */ +static kmem_cache_t *znode_slab; + +int znode_shift_order; + +/* ZNODE INITIALIZATION */ + +/* call this once on reiser4 initialisation */ +reiser4_internal int +znodes_init(void) +{ + znode_slab = kmem_cache_create("znode", sizeof (znode), 0, + SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + NULL, NULL); + if (znode_slab == NULL) { + return RETERR(-ENOMEM); + } else { + for (znode_shift_order = 0; + (1 << znode_shift_order) < sizeof(znode); + ++ znode_shift_order) + ; + -- znode_shift_order; + return 0; + } +} + +/* call this before unloading reiser4 */ +reiser4_internal int +znodes_done(void) +{ + return kmem_cache_destroy(znode_slab); +} + +/* call this to initialise tree of znodes */ +reiser4_internal int +znodes_tree_init(reiser4_tree * tree /* tree to initialise znodes for */ ) +{ + int result; + assert("umka-050", tree != NULL); + + rw_dk_init(tree); + + result = z_hash_init(&tree->zhash_table, REISER4_ZNODE_HASH_TABLE_SIZE, + reiser4_stat(tree->super, hashes.znode)); + if (result != 0) + return result; + result = z_hash_init(&tree->zfake_table, REISER4_ZNODE_HASH_TABLE_SIZE, + reiser4_stat(tree->super, hashes.zfake)); + return result; +} + +#if REISER4_DEBUG +extern void jnode_done(jnode * node, reiser4_tree * tree); +#endif + +/* free this znode */ +reiser4_internal void +zfree(znode * node /* znode to free */ ) +{ + trace_stamp(TRACE_ZNODES); + assert("nikita-465", node != NULL); + assert("nikita-2120", znode_page(node) == NULL); + assert("nikita-2301", owners_list_empty(&node->lock.owners)); + assert("nikita-2302", requestors_list_empty(&node->lock.requestors)); + assert("nikita-2663", capture_list_is_clean(ZJNODE(node)) && NODE_LIST(ZJNODE(node)) == NOT_CAPTURED); + assert("nikita-2773", !JF_ISSET(ZJNODE(node), JNODE_EFLUSH)); + assert("nikita-3220", list_empty(&ZJNODE(node)->jnodes)); + assert("nikita-3293", !znode_is_right_connected(node)); + assert("nikita-3294", !znode_is_left_connected(node)); + assert("nikita-3295", node->left == NULL); + assert("nikita-3296", node->right == NULL); + + + /* not yet phash_jnode_destroy(ZJNODE(node)); */ + + /* poison memory. */ + ON_DEBUG(xmemset(node, 0xde, sizeof *node)); + kmem_cache_free(znode_slab, node); +} + +/* call this to free tree of znodes */ +reiser4_internal void +znodes_tree_done(reiser4_tree * tree /* tree to finish with znodes of */ ) +{ + znode *node; + znode *next; + z_hash_table *ztable; + + /* scan znode hash-tables and kill all znodes, then free hash tables + * themselves. */ + + assert("nikita-795", tree != NULL); + + IF_TRACE(TRACE_ZWEB, UNDER_RW_VOID(tree, tree, read, + print_znodes("umount", tree))); + + ztable = &tree->zhash_table; + + for_all_in_htable(ztable, z, node, next) { + node->c_count = 0; + node->in_parent.node = NULL; + assert("nikita-2179", atomic_read(&ZJNODE(node)->x_count) == 0); + zdrop(node); + } + + z_hash_done(&tree->zhash_table); + + ztable = &tree->zfake_table; + + for_all_in_htable(ztable, z, node, next) { + node->c_count = 0; + node->in_parent.node = NULL; + assert("nikita-2179", atomic_read(&ZJNODE(node)->x_count) == 0); + zdrop(node); + } + + z_hash_done(&tree->zfake_table); +} + +/* ZNODE STRUCTURES */ + +/* allocate fresh znode */ +reiser4_internal znode * +zalloc(int gfp_flag /* allocation flag */ ) +{ + znode *node; + + trace_stamp(TRACE_ZNODES); + node = kmem_cache_alloc(znode_slab, gfp_flag); + return node; +} + +/* Initialize fields of znode + @node: znode to initialize; + @parent: parent znode; + @tree: tree we are in. */ +reiser4_internal void +zinit(znode * node, const znode * parent, reiser4_tree * tree) +{ + assert("nikita-466", node != NULL); + assert("umka-268", current_tree != NULL); + + xmemset(node, 0, sizeof *node); + + assert("umka-051", tree != NULL); + + jnode_init(&node->zjnode, tree, JNODE_FORMATTED_BLOCK); + reiser4_init_lock(&node->lock); + init_parent_coord(&node->in_parent, parent); + ON_DEBUG_MODIFY(node->cksum = 0); +} + +/* + * remove znode from indices. This is called jput() when last reference on + * znode is released. + */ +reiser4_internal void +znode_remove(znode * node /* znode to remove */ , reiser4_tree * tree) +{ + assert("nikita-2108", node != NULL); + assert("nikita-470", node->c_count == 0); + assert("zam-879", rw_tree_is_write_locked(tree)); + + /* remove reference to this znode from cbk cache */ + cbk_cache_invalidate(node, tree); + + /* update c_count of parent */ + if (znode_parent(node) != NULL) { + assert("nikita-472", znode_parent(node)->c_count > 0); + /* father, onto your hands I forward my spirit... */ + znode_parent(node)->c_count --; + node->in_parent.node = NULL; + } else { + /* orphaned znode?! Root? */ + } + + /* remove znode from hash-table */ + z_hash_remove_rcu(znode_get_htable(node), node); +} + +/* zdrop() -- Remove znode from the tree. + + This is called when znode is removed from the memory. */ +reiser4_internal void +zdrop(znode * node /* znode to finish with */ ) +{ + jdrop(ZJNODE(node)); +} + +/* + * put znode into right place in the hash table. This is called by relocate + * code. + */ +reiser4_internal int +znode_rehash(znode * node /* node to rehash */ , + const reiser4_block_nr * new_block_nr /* new block number */ ) +{ + z_hash_table *oldtable; + z_hash_table *newtable; + reiser4_tree *tree; + + assert("nikita-2018", node != NULL); + + tree = znode_get_tree(node); + oldtable = znode_get_htable(node); + newtable = get_htable(tree, new_block_nr); + + WLOCK_TREE(tree); + /* remove znode from hash-table */ + z_hash_remove_rcu(oldtable, node); + + /* assertion no longer valid due to RCU */ + /* assert("nikita-2019", z_hash_find(newtable, new_block_nr) == NULL); */ + + /* update blocknr */ + znode_set_block(node, new_block_nr); + node->zjnode.key.z = *new_block_nr; + + /* insert it into hash */ + z_hash_insert_rcu(newtable, node); + WUNLOCK_TREE(tree); + return 0; +} + +/* ZNODE LOOKUP, GET, PUT */ + +/* zlook() - get znode with given block_nr in a hash table or return NULL + + If result is non-NULL then the znode's x_count is incremented. Internal version + accepts pre-computed hash index. The hash table is accessed under caller's + tree->hash_lock. +*/ +reiser4_internal znode * +zlook(reiser4_tree * tree, const reiser4_block_nr * const blocknr) +{ + znode *result; + __u32 hash; + z_hash_table *htable; + + trace_stamp(TRACE_ZNODES); + + assert("jmacd-506", tree != NULL); + assert("jmacd-507", blocknr != NULL); + + htable = get_htable(tree, blocknr); + hash = blknrhashfn(htable, blocknr); + + rcu_read_lock(); + result = z_hash_find_index(htable, hash, blocknr); + + if (result != NULL) { + add_x_ref(ZJNODE(result)); + result = znode_rip_check(tree, result); + } + rcu_read_unlock(); + + return result; +} + +/* return hash table where znode with block @blocknr is (or should be) + * stored */ +reiser4_internal z_hash_table * +get_htable(reiser4_tree * tree, const reiser4_block_nr * const blocknr) +{ + z_hash_table *table; + if (is_disk_addr_unallocated(blocknr)) + table = &tree->zfake_table; + else + table = &tree->zhash_table; + return table; +} + +/* return hash table where znode @node is (or should be) stored */ +reiser4_internal z_hash_table * +znode_get_htable(const znode *node) +{ + return get_htable(znode_get_tree(node), znode_get_block(node)); +} + +/* zget() - get znode from hash table, allocating it if necessary. + + First a call to zlook, locating a x-referenced znode if one + exists. If znode is not found, allocate new one and return. Result + is returned with x_count reference increased. + + LOCKS TAKEN: TREE_LOCK, ZNODE_LOCK + LOCK ORDERING: NONE +*/ +reiser4_internal znode * +zget(reiser4_tree * tree, + const reiser4_block_nr * const blocknr, + znode * parent, + tree_level level, + int gfp_flag) +{ + znode *result; + __u32 hashi; + + z_hash_table *zth; + + trace_stamp(TRACE_ZNODES); + + assert("jmacd-512", tree != NULL); + assert("jmacd-513", blocknr != NULL); + assert("jmacd-514", level < REISER4_MAX_ZTREE_HEIGHT); + + zth = get_htable(tree, blocknr); + hashi = blknrhashfn(zth, blocknr); + + /* NOTE-NIKITA address-as-unallocated-blocknr still is not + implemented. */ + + z_hash_prefetch_bucket(zth, hashi); + + rcu_read_lock(); + /* Find a matching BLOCKNR in the hash table. If the znode is found, + we obtain an reference (x_count) but the znode remains unlocked. + Have to worry about race conditions later. */ + result = z_hash_find_index(zth, hashi, blocknr); + /* According to the current design, the hash table lock protects new + znode references. */ + if (result != NULL) { + add_x_ref(ZJNODE(result)); + /* NOTE-NIKITA it should be so, but special case during + creation of new root makes such assertion highly + complicated. */ + assert("nikita-2131", 1 || znode_parent(result) == parent || + (ZF_ISSET(result, JNODE_ORPHAN) && (znode_parent(result) == NULL))); + result = znode_rip_check(tree, result); + } + + rcu_read_unlock(); + + if (!result) { + znode * shadow; + + result = zalloc(gfp_flag); + if (!result) { + return ERR_PTR(RETERR(-ENOMEM)); + } + + zinit(result, parent, tree); + ZJNODE(result)->blocknr = *blocknr; + ZJNODE(result)->key.z = *blocknr; + result->level = level; + + WLOCK_TREE(tree); + + shadow = z_hash_find_index(zth, hashi, blocknr); + if (unlikely(shadow != NULL && !ZF_ISSET(shadow, JNODE_RIP))) { + jnode_list_remove(ZJNODE(result)); + zfree(result); + result = shadow; + } else { + result->version = znode_build_version(tree); + z_hash_insert_index_rcu(zth, hashi, result); + + if (parent != NULL) + ++ parent->c_count; + } + + add_x_ref(ZJNODE(result)); + + WUNLOCK_TREE(tree); + } + +#if REISER4_DEBUG + if (!blocknr_is_fake(blocknr) && *blocknr != 0) + reiser4_check_block(blocknr, 1); +#endif + /* Check for invalid tree level, return -EIO */ + if (unlikely(znode_get_level(result) != level)) { + warning("jmacd-504", + "Wrong level for cached block %llu: %i expecting %i", + (unsigned long long)(*blocknr), znode_get_level(result), level); + zput(result); + return ERR_PTR(RETERR(-EIO)); + } + + assert("nikita-1227", znode_invariant(result)); + + return result; +} + +/* ZNODE PLUGINS/DATA */ + +/* "guess" plugin for node loaded from the disk. Plugin id of node plugin is + stored at the fixed offset from the beginning of the node. */ +static node_plugin * +znode_guess_plugin(const znode * node /* znode to guess + * plugin of */ ) +{ + reiser4_tree * tree; + + assert("nikita-1053", node != NULL); + assert("nikita-1055", zdata(node) != NULL); + + tree = znode_get_tree(node); + assert("umka-053", tree != NULL); + + if (reiser4_is_set(tree->super, REISER4_ONE_NODE_PLUGIN)) { + return tree->nplug; + } else { + return node_plugin_by_disk_id + (tree, &((common_node_header *) zdata(node))->plugin_id); +#ifdef GUESS_EXISTS + reiser4_plugin *plugin; + + /* NOTE-NIKITA add locking here when dynamic plugins will be + * implemented */ + for_all_plugins(REISER4_NODE_PLUGIN_TYPE, plugin) { + if ((plugin->u.node.guess != NULL) && plugin->u.node.guess(node)) + return plugin; + } +#endif + warning("nikita-1057", "Cannot guess node plugin"); + print_znode("node", node); + return NULL; + } +} + +/* parse node header and install ->node_plugin */ +reiser4_internal int +zparse(znode * node /* znode to parse */ ) +{ + int result; + + assert("nikita-1233", node != NULL); + assert("nikita-2370", zdata(node) != NULL); + + if (node->nplug == NULL) { + node_plugin *nplug; + + nplug = znode_guess_plugin(node); + if (likely(nplug != NULL)) { + result = nplug->parse(node); + if (likely(result == 0)) + node->nplug = nplug; + } else { + result = RETERR(-EIO); + } + } else + result = 0; + return result; +} + +/* zload with readahead */ +reiser4_internal int +zload_ra(znode * node /* znode to load */, ra_info_t *info) +{ + int result; + + assert("nikita-484", node != NULL); + assert("nikita-1377", znode_invariant(node)); + assert("jmacd-7771", !znode_above_root(node)); + assert("nikita-2125", atomic_read(&ZJNODE(node)->x_count) > 0); + assert("nikita-3016", schedulable()); + + if (info) + formatted_readahead(node, info); + + result = jload(ZJNODE(node)); + ON_DEBUG_MODIFY(znode_pre_write(node)); + assert("nikita-1378", znode_invariant(node)); + return result; +} + +/* load content of node into memory */ +reiser4_internal int zload(znode * node) +{ + return zload_ra(node, 0); +} + +/* call node plugin to initialise newly allocated node. */ +reiser4_internal int +zinit_new(znode * node /* znode to initialise */, int gfp_flags ) +{ + return jinit_new(ZJNODE(node), gfp_flags); +} + +/* drop reference to node data. When last reference is dropped, data are + unloaded. */ +reiser4_internal void +zrelse(znode * node /* znode to release references to */ ) +{ + assert("nikita-1381", znode_invariant(node)); + + jrelse(ZJNODE(node)); +} + +/* returns free space in node */ +reiser4_internal unsigned +znode_free_space(znode * node /* znode to query */ ) +{ + assert("nikita-852", node != NULL); + return node_plugin_by_node(node)->free_space(node); +} + +/* left delimiting key of znode */ +reiser4_internal reiser4_key * +znode_get_rd_key(znode * node /* znode to query */ ) +{ + assert("nikita-958", node != NULL); + assert("nikita-1661", rw_dk_is_locked(znode_get_tree(node))); + assert("nikita-3067", LOCK_CNT_GTZ(rw_locked_dk)); + + return &node->rd_key; +} + +/* right delimiting key of znode */ +reiser4_internal reiser4_key * +znode_get_ld_key(znode * node /* znode to query */ ) +{ + assert("nikita-974", node != NULL); + assert("nikita-1662", rw_dk_is_locked(znode_get_tree(node))); + assert("nikita-3068", LOCK_CNT_GTZ(rw_locked_dk)); + + return &node->ld_key; +} + +/* update right-delimiting key of @node */ +reiser4_internal reiser4_key * +znode_set_rd_key(znode * node, const reiser4_key * key) +{ + assert("nikita-2937", node != NULL); + assert("nikita-2939", key != NULL); + assert("nikita-2938", rw_dk_is_write_locked(znode_get_tree(node))); + assert("nikita-3069", LOCK_CNT_GTZ(write_locked_dk)); + assert("nikita-2944", + znode_is_any_locked(node) || + znode_get_level(node) != LEAF_LEVEL || + keyge(key, znode_get_rd_key(node)) || + keyeq(znode_get_rd_key(node), min_key())); + + node->rd_key = *key; + return &node->rd_key; +} + +/* update left-delimiting key of @node */ +reiser4_internal reiser4_key * +znode_set_ld_key(znode * node, const reiser4_key * key) +{ + assert("nikita-2940", node != NULL); + assert("nikita-2941", key != NULL); + assert("nikita-2942", rw_dk_is_write_locked(znode_get_tree(node))); + assert("nikita-3070", LOCK_CNT_GTZ(write_locked_dk > 0)); + assert("nikita-2943", + znode_is_any_locked(node) || + keyeq(znode_get_ld_key(node), min_key())); + + node->ld_key = *key; + return &node->ld_key; +} + +/* true if @key is inside key range for @node */ +reiser4_internal int +znode_contains_key(znode * node /* znode to look in */ , + const reiser4_key * key /* key to look for */ ) +{ + assert("nikita-1237", node != NULL); + assert("nikita-1238", key != NULL); + + /* left_delimiting_key <= key <= right_delimiting_key */ + return keyle(znode_get_ld_key(node), key) && keyle(key, znode_get_rd_key(node)); +} + +/* same as znode_contains_key(), but lock dk lock */ +reiser4_internal int +znode_contains_key_lock(znode * node /* znode to look in */ , + const reiser4_key * key /* key to look for */ ) +{ + assert("umka-056", node != NULL); + assert("umka-057", key != NULL); + + return UNDER_RW(dk, znode_get_tree(node), + read, znode_contains_key(node, key)); +} + +/* get parent pointer, assuming tree is not locked */ +reiser4_internal znode * +znode_parent_nolock(const znode * node /* child znode */ ) +{ + assert("nikita-1444", node != NULL); + return node->in_parent.node; +} + +/* get parent pointer of znode */ +reiser4_internal znode * +znode_parent(const znode * node /* child znode */ ) +{ + assert("nikita-1226", node != NULL); + assert("nikita-1406", LOCK_CNT_GTZ(rw_locked_tree)); + return znode_parent_nolock(node); +} + +/* detect uber znode used to protect in-superblock tree root pointer */ +reiser4_internal int +znode_above_root(const znode * node /* znode to query */ ) +{ + assert("umka-059", node != NULL); + + return disk_addr_eq(&ZJNODE(node)->blocknr, &UBER_TREE_ADDR); +} + +/* check that @node is root---that its block number is recorder in the tree as + that of root node */ +reiser4_internal int +znode_is_true_root(const znode * node /* znode to query */ ) +{ + assert("umka-060", node != NULL); + assert("umka-061", current_tree != NULL); + + return disk_addr_eq(znode_get_block(node), &znode_get_tree(node)->root_block); +} + +/* check that @node is root */ +reiser4_internal int +znode_is_root(const znode * node /* znode to query */ ) +{ + assert("nikita-1206", node != NULL); + + return znode_get_level(node) == znode_get_tree(node)->height; +} + +/* Returns true is @node was just created by zget() and wasn't ever loaded + into memory. */ +/* NIKITA-HANS: yes */ +reiser4_internal int +znode_just_created(const znode * node) +{ + assert("nikita-2188", node != NULL); + return (znode_page(node) == NULL); +} + +/* obtain updated ->znode_epoch. See seal.c for description. */ +reiser4_internal __u64 +znode_build_version(reiser4_tree * tree) +{ + return UNDER_SPIN(epoch, tree, ++tree->znode_epoch); +} + +/* + * relocate znode to the new block number @blk. Caller keeps @node and @parent + * long-term locked, and loaded. + */ +static int +relocate_locked(znode * node, znode * parent, reiser4_block_nr * blk) +{ + coord_t inparent; + int result; + + assert("nikita-3127", node != NULL); + assert("nikita-3128", parent != NULL); + assert("nikita-3129", blk != NULL); + assert("nikita-3130", znode_is_any_locked(node)); + assert("nikita-3131", znode_is_any_locked(parent)); + assert("nikita-3132", znode_is_loaded(node)); + assert("nikita-3133", znode_is_loaded(parent)); + + result = find_child_ptr(parent, node, &inparent); + if (result == NS_FOUND) { + int grabbed; + + grabbed = get_current_context()->grabbed_blocks; + /* for a node and its parent */ + result = reiser4_grab_space_force((__u64)2, BA_RESERVED); + if (result == 0) { + item_plugin *iplug; + + iplug = item_plugin_by_coord(&inparent); + assert("nikita-3126", iplug->f.update != NULL); + iplug->f.update(&inparent, blk); + znode_make_dirty(inparent.node); + result = znode_rehash(node, blk); + } + grabbed2free_mark(grabbed); + } else + result = RETERR(-EIO); + return result; +} + +/* + * relocate znode to the new block number @blk. Used for speculative + * relocation of bad blocks. + */ +reiser4_internal int +znode_relocate(znode * node, reiser4_block_nr * blk) +{ + lock_handle lh; + int result; + + assert("nikita-3120", node != NULL); + assert("nikita-3121", atomic_read(&ZJNODE(node)->x_count) > 0); + assert("nikita-3122", blk != NULL); + assert("nikita-3123", lock_stack_isclean(get_current_lock_stack())); + assert("nikita-3124", schedulable()); + assert("nikita-3125", !znode_is_root(node)); + + init_lh(&lh); + result = longterm_lock_znode(&lh, node, + ZNODE_READ_LOCK, ZNODE_LOCK_LOPRI); + if (result == 0) { + lock_handle parent; + + result = reiser4_get_parent(&parent, node, ZNODE_READ_LOCK, 1); + if (result == 0) { + result = zload(node); + if (result == 0) { + result = zload(parent.node); + if (result == 0) { + result = relocate_locked(node, + parent.node, + blk); + zrelse(parent.node); + } + zrelse(node); + } + done_lh(&parent); + } + done_lh(&lh); + } + return result; +} + +reiser4_internal void +init_load_count(load_count * dh) +{ + assert("nikita-2105", dh != NULL); + xmemset(dh, 0, sizeof *dh); +} + +reiser4_internal void +done_load_count(load_count * dh) +{ + assert("nikita-2106", dh != NULL); + if (dh->node != NULL) { + for (; dh->d_ref > 0; --dh->d_ref) + zrelse(dh->node); + dh->node = NULL; + } +} + +reiser4_internal int +incr_load_count_znode(load_count * dh, znode * node) +{ + assert("nikita-2107", dh != NULL); + assert("nikita-2158", node != NULL); + assert("nikita-2109", ergo(dh->node != NULL, (dh->node == node) || (dh->d_ref == 0))); + + dh->node = node; + return incr_load_count(dh); +} + +reiser4_internal int +incr_load_count(load_count * dh) +{ + int result; + + assert("nikita-2110", dh != NULL); + assert("nikita-2111", dh->node != NULL); + + result = zload(dh->node); + if (result == 0) + ++dh->d_ref; + return result; +} + +reiser4_internal int +incr_load_count_jnode(load_count * dh, jnode * node) +{ + if (jnode_is_znode(node)) { + return incr_load_count_znode(dh, JZNODE(node)); + } + return 0; +} + +reiser4_internal void +copy_load_count(load_count * new, load_count * old) +{ + int ret = 0; + done_load_count(new); + new->node = old->node; + new->d_ref = 0; + + while ((new->d_ref < old->d_ref) && (ret = incr_load_count(new)) == 0) { + } + + assert("jmacd-87589", ret == 0); +} + +reiser4_internal void +move_load_count(load_count * new, load_count * old) +{ + done_load_count(new); + new->node = old->node; + new->d_ref = old->d_ref; + old->node = NULL; + old->d_ref = 0; +} + +/* convert parent pointer into coord */ +reiser4_internal void +parent_coord_to_coord(const parent_coord_t * pcoord, coord_t * coord) +{ + assert("nikita-3204", pcoord != NULL); + assert("nikita-3205", coord != NULL); + + coord_init_first_unit_nocheck(coord, pcoord->node); + coord_set_item_pos(coord, pcoord->item_pos); + coord->between = AT_UNIT; +} + +/* pack coord into parent_coord_t */ +reiser4_internal void +coord_to_parent_coord(const coord_t * coord, parent_coord_t * pcoord) +{ + assert("nikita-3206", pcoord != NULL); + assert("nikita-3207", coord != NULL); + + pcoord->node = coord->node; + pcoord->item_pos = coord->item_pos; +} + +/* Initialize a parent hint pointer. (parent hint pointer is a field in znode, + look for comments there) */ +reiser4_internal void +init_parent_coord(parent_coord_t * pcoord, const znode * node) +{ + pcoord->node = (znode *) node; + pcoord->item_pos = (unsigned short)~0; +} + + +#if REISER4_DEBUG_NODE_INVARIANT +int jnode_invariant_f(const jnode * node, char const **msg); + +/* debugging aid: znode invariant */ +static int +znode_invariant_f(const znode * node /* znode to check */ , + char const **msg /* where to store error + * message, if any */ ) +{ +#define _ergo(ant, con) \ + ((*msg) = "{" #ant "} ergo {" #con "}", ergo((ant), (con))) + +#define _equi(e1, e2) \ + ((*msg) = "{" #e1 "} <=> {" #e2 "}", equi((e1), (e2))) + +#define _check(exp) ((*msg) = #exp, (exp)) + + return + jnode_invariant_f(ZJNODE(node), msg) && + + /* [znode-fake] invariant */ + + /* fake znode doesn't have a parent, and */ + _ergo(znode_get_level(node) == 0, znode_parent(node) == NULL) && + /* there is another way to express this very check, and */ + _ergo(znode_above_root(node), + znode_parent(node) == NULL) && + /* it has special block number, and */ + _ergo(znode_get_level(node) == 0, + disk_addr_eq(znode_get_block(node), &UBER_TREE_ADDR)) && + /* it is the only znode with such block number, and */ + _ergo(!znode_above_root(node) && znode_is_loaded(node), + !disk_addr_eq(znode_get_block(node), &UBER_TREE_ADDR)) && + /* it is parent of the tree root node */ + _ergo(znode_is_true_root(node), znode_above_root(znode_parent(node))) && + + /* [znode-level] invariant */ + + /* level of parent znode is one larger than that of child, + except for the fake znode, and */ + _ergo(znode_parent(node) && !znode_above_root(znode_parent(node)), + znode_get_level(znode_parent(node)) == + znode_get_level(node) + 1) && + /* left neighbor is at the same level, and */ + _ergo(znode_is_left_connected(node) && node->left != NULL, + znode_get_level(node) == znode_get_level(node->left)) && + /* right neighbor is at the same level */ + _ergo(znode_is_right_connected(node) && node->right != NULL, + znode_get_level(node) == znode_get_level(node->right)) && + + /* [znode-connected] invariant */ + + _ergo(node->left != NULL, znode_is_left_connected(node)) && + _ergo(node->right != NULL, znode_is_right_connected(node)) && + _ergo(!znode_is_root(node) && node->left != NULL, + znode_is_right_connected(node->left) && + node->left->right == node) && + _ergo(!znode_is_root(node) && node->right != NULL, + znode_is_left_connected(node->right) && + node->right->left == node) && + + /* [znode-c_count] invariant */ + + /* for any znode, c_count of its parent is greater than 0 */ + _ergo(znode_parent(node) != NULL && + !znode_above_root(znode_parent(node)), + znode_parent(node)->c_count > 0) && + /* leaves don't have children */ + _ergo(znode_get_level(node) == LEAF_LEVEL, + node->c_count == 0) && + + _check(node->zjnode.jnodes.prev != NULL) && + _check(node->zjnode.jnodes.next != NULL) && + /* orphan doesn't have a parent */ + _ergo(ZF_ISSET(node, JNODE_ORPHAN), znode_parent(node) == 0) && + + /* [znode-modify] invariant */ + + /* if znode is not write-locked, its checksum remains + * invariant */ + /* unfortunately, zlock is unordered w.r.t. jnode_lock, so we + * cannot check this. */ + /* + UNDER_RW(zlock, (zlock *)&node->lock, + read, _ergo(!znode_is_wlocked(node), + znode_at_read(node))) && + */ + /* [znode-refs] invariant */ + + /* only referenced znode can be long-term locked */ + _ergo(znode_is_locked(node), + atomic_read(&ZJNODE(node)->x_count) != 0); +} + +/* debugging aid: check znode invariant and panic if it doesn't hold */ +int +znode_invariant(const znode * node /* znode to check */ ) +{ + char const *failed_msg; + int result; + + assert("umka-063", node != NULL); + assert("umka-064", current_tree != NULL); + + spin_lock_znode((znode *) node); + RLOCK_TREE(znode_get_tree(node)); + result = znode_invariant_f(node, &failed_msg); + if (!result) { + /* print_znode("corrupted node", node); */ + warning("jmacd-555", "Condition %s failed", failed_msg); + } + RUNLOCK_TREE(znode_get_tree(node)); + spin_unlock_znode((znode *) node); + return result; +} +/* REISER4_DEBUG_NODE_INVARIANT */ +#endif + +/* + * Node dirtying debug. + * + * Whenever formatted node is modified, it should be marked dirty (through + * call to znode_make_dirty()) before exclusive long term lock (necessary to + * modify node) is released. This is critical for correct operation of seal.c + * code. + * + * As this is an error easy to make, special debugging mode was implemented to + * catch it. + * + * In this mode new field ->cksum is added to znode. This field contains + * checksum (adler32) of znode content calculated when znode is loaded into + * memory and re-calculated whenever znode_make_dirty() is called on it. + * + * Whenever long term lock on znode is released, and znode wasn't marked + * dirty, checksum of its content is calculated and compared with value stored + * in ->cksum. If they differ, call to znode_make_dirty() is missing. + * + * This debugging mode (tunable though fs/Kconfig) is very CPU consuming and + * hence, unsuitable for normal operation. + * + */ + +#if REISER4_DEBUG_MODIFY +__u32 znode_checksum(const znode * node) +{ + int i, size = znode_size(node); + __u32 l = 0; + __u32 h = 0; + const char *data = page_address(znode_page(node)); + + /* Checksum is similar to adler32... */ + for (i = 0; i < size; i += 1) { + l += data[i]; + h += l; + } + + return (h << 16) | (l & 0xffff); +} + +static inline int znode_has_data(const znode *z) +{ + return znode_page(z) != NULL && page_address(znode_page(z)) == zdata(z); +} + +void znode_set_checksum(jnode * node, int locked_p) +{ + if (jnode_is_znode(node)) { + znode *z; + + z = JZNODE(node); + + if (!locked_p) + LOCK_JNODE(node); + if (znode_has_data(z)) + z->cksum = znode_checksum(z); + else + z->cksum = 0; + if (!locked_p) + UNLOCK_JNODE(node); + } +} + +void +znode_pre_write(znode * node) +{ + assert("umka-066", node != NULL); + + spin_lock_znode(node); + if (znode_has_data(node)) { + if (node->cksum == 0 && !znode_is_dirty(node)) + node->cksum = znode_checksum(node); + } + spin_unlock_znode(node); +} + +void +znode_post_write(znode * node) +{ + __u32 cksum; + + assert("umka-067", node != NULL); + + if (znode_has_data(node)) { + cksum = znode_checksum(node); + + if (cksum != node->cksum && node->cksum != 0) + reiser4_panic("jmacd-1081", + "changed znode is not dirty: %llu", + node->zjnode.blocknr); + } +} + +int +znode_at_read(const znode * node) +{ + __u32 cksum; + + assert("umka-067", node != NULL); + + if (znode_has_data(node)) { + cksum = znode_checksum((znode *)node); + + if (cksum != node->cksum && node->cksum != 0) { + reiser4_panic("nikita-3561", + "znode is changed: %llu", + node->zjnode.blocknr); + return 0; + } + } + return 1; +} +#endif + +#if REISER4_DEBUG_OUTPUT + +/* debugging aid: output more human readable information about @node that + info_znode(). */ +reiser4_internal void +print_znode(const char *prefix /* prefix to print */ , + const znode * node /* node to print */ ) +{ + if (node == NULL) { + printk("%s: null\n", prefix); + return; + } + + info_znode(prefix, node); + if (!jnode_is_znode(ZJNODE(node))) + return; + info_znode("\tparent", znode_parent_nolock(node)); + info_znode("\tleft", node->left); + info_znode("\tright", node->right); + print_key("\tld", &node->ld_key); + print_key("\trd", &node->rd_key); + printk("\n"); +} + +/* debugging aid: output human readable information about @node */ +reiser4_internal void +info_znode(const char *prefix /* prefix to print */ , + const znode * node /* node to print */ ) +{ + if (node == NULL) { + return; + } + info_jnode(prefix, ZJNODE(node)); + if (!jnode_is_znode(ZJNODE(node))) + return; + + printk("c_count: %i, readers: %i, items: %i\n", + node->c_count, node->lock.nr_readers, node->nr_items); +} + +/* print all znodes in @tree */ +reiser4_internal void +print_znodes(const char *prefix, reiser4_tree * tree) +{ + znode *node; + znode *next; + z_hash_table *htable; + int tree_lock_taken; + + if (tree == NULL) + tree = current_tree; + + /* this is debugging function. It can be called by reiser4_panic() + with tree spin-lock already held. Trylock is not exactly what we + want here, but it is passable. + */ + tree_lock_taken = write_trylock_tree(tree); + + htable = &tree->zhash_table; + for_all_in_htable(htable, z, node, next) { + info_znode(prefix, node); + } + + htable = &tree->zfake_table; + for_all_in_htable(htable, z, node, next) { + info_znode(prefix, node); + } + + if (tree_lock_taken) + WUNLOCK_TREE(tree); +} +#endif + +#if defined(REISER4_DEBUG) || defined(REISER4_DEBUG_MODIFY) || defined(REISER4_DEBUG_OUTPUT) + +/* return non-0 iff data are loaded into znode */ +reiser4_internal int +znode_is_loaded(const znode * node /* znode to query */ ) +{ + assert("nikita-497", node != NULL); + return jnode_is_loaded(ZJNODE(node)); +} + +#endif + +#if REISER4_DEBUG +reiser4_internal unsigned long +znode_times_locked(const znode *z) +{ + return z->times_locked; +} +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/fs/reiser4/znode.h 2004-09-03 00:57:05.492204320 -0700 @@ -0,0 +1,473 @@ +/* Copyright 2001, 2002, 2003, 2004 by Hans Reiser, licensing governed by + * reiser4/README */ + +/* Declaration of znode (Zam's node). See znode.c for more details. */ + +#ifndef __ZNODE_H__ +#define __ZNODE_H__ + +#include "forward.h" +#include "debug.h" +#include "dformat.h" +#include "spin_macros.h" +#include "key.h" +#include "coord.h" +#include "type_safe_list.h" +#include "plugin/node/node.h" +#include "jnode.h" +#include "lock.h" +#include "readahead.h" + +#include +#include +#include /* for PAGE_CACHE_SIZE */ +#include +#include + +/* znode tracks its position within parent (internal item in a parent node, + * that contains znode's block number). */ +typedef struct parent_coord { + znode *node; + pos_in_node_t item_pos; +} parent_coord_t; + +/* &znode - node in a reiser4 tree. + + NOTE-NIKITA fields in this struct have to be rearranged (later) to reduce + cacheline pressure. + + Locking: + + Long term: data in a disk node attached to this znode are protected + by long term, deadlock aware lock ->lock; + + Spin lock: the following fields are protected by the spin lock: + + ->lock + + Following fields are protected by the global tree lock: + + ->left + ->right + ->in_parent + ->c_count + + Following fields are protected by the global delimiting key lock (dk_lock): + + ->ld_key (to update ->ld_key long-term lock on the node is also required) + ->rd_key + + Following fields are protected by the long term lock: + + ->nr_items + + ->node_plugin is never changed once set. This means that after code made + itself sure that field is valid it can be accessed without any additional + locking. + + ->level is immutable. + + Invariants involving this data-type: + + [znode-fake] + [znode-level] + [znode-connected] + [znode-c_count] + [znode-refs] + [jnode-refs] + [jnode-queued] + [znode-modify] + + For this to be made into a clustering or NUMA filesystem, we would want to eliminate all of the global locks. + Suggestions for how to do that are desired.*/ +struct znode { + /* Embedded jnode. */ + jnode zjnode; + + /* contains three subfields, node, pos_in_node, and pos_in_unit. + + pos_in_node and pos_in_unit are only hints that are cached to + speed up lookups during balancing. They are not required to be up to + date. Synched in find_child_ptr(). + + This value allows us to avoid expensive binary searches. + + in_parent->node points to the parent of this node, and is NOT a + hint. + */ + parent_coord_t in_parent; + + /* + * sibling list pointers + */ + + /* left-neighbor */ + znode *left; + /* right-neighbor */ + znode *right; + /* long term lock on node content. This lock supports deadlock + detection. See lock.c + */ + zlock lock; + + /* You cannot remove from memory a node that has children in + memory. This is because we rely on the fact that parent of given + node can always be reached without blocking for io. When reading a + node into memory you must increase the c_count of its parent, when + removing it from memory you must decrease the c_count. This makes + the code simpler, and the cases where it is suboptimal are truly + obscure. + */ + int c_count; + + /* plugin of node attached to this znode. NULL if znode is not + loaded. */ + node_plugin *nplug; + + /* version of znode data. This is increased on each modification. This + * is necessary to implement seals (see seal.[ch]) efficiently. */ + __u64 version; + + /* left delimiting key. Necessary to efficiently perform + balancing with node-level locking. Kept in memory only. */ + reiser4_key ld_key; + /* right delimiting key. */ + reiser4_key rd_key; + + /* znode's tree level */ + __u16 level; + /* number of items in this node. This field is modified by node + * plugin. */ + __u16 nr_items; +#if REISER4_DEBUG_MODIFY + /* In debugging mode, used to detect loss of znode_set_dirty() + notification. */ + spinlock_t cksum_lock; + __u32 cksum; +#endif + +#if REISER4_DEBUG + void *creator; + reiser4_key first_key; + unsigned long times_locked; +#endif +#if REISER4_STATS + int last_lookup_pos; +#endif +} __attribute__((aligned(16))); + +/* In general I think these macros should not be exposed. */ +#define znode_is_locked(node) (lock_is_locked(&node->lock)) +#define znode_is_rlocked(node) (lock_is_rlocked(&node->lock)) +#define znode_is_wlocked(node) (lock_is_wlocked(&node->lock)) +#define znode_is_wlocked_once(node) (lock_is_wlocked_once(&node->lock)) +#define znode_can_be_rlocked(node) (lock_can_be_rlocked(&node->lock)) +#define is_lock_compatible(node, mode) (lock_mode_compatible(&node->lock, mode)) + +/* Macros for accessing the znode state. */ +#define ZF_CLR(p,f) JF_CLR (ZJNODE(p), (f)) +#define ZF_ISSET(p,f) JF_ISSET(ZJNODE(p), (f)) +#define ZF_SET(p,f) JF_SET (ZJNODE(p), (f)) + +extern znode *zget(reiser4_tree * tree, const reiser4_block_nr * const block, + znode * parent, tree_level level, int gfp_flag); +extern znode *zlook(reiser4_tree * tree, const reiser4_block_nr * const block); +extern int zload(znode * node); +extern int zload_ra(znode * node, ra_info_t *info); +extern int zinit_new(znode * node, int gfp_flags); +extern void zrelse(znode * node); +extern void znode_change_parent(znode * new_parent, reiser4_block_nr * block); + +/* size of data in znode */ +static inline unsigned +znode_size(const znode * node UNUSED_ARG /* znode to query */ ) +{ + assert("nikita-1416", node != NULL); + return PAGE_CACHE_SIZE; +} + +extern void parent_coord_to_coord(const parent_coord_t *pcoord, coord_t *coord); +extern void coord_to_parent_coord(const coord_t *coord, parent_coord_t *pcoord); +extern void init_parent_coord(parent_coord_t * pcoord, const znode * node); + +extern unsigned znode_free_space(znode * node); + +extern reiser4_key *znode_get_rd_key(znode * node); +extern reiser4_key *znode_get_ld_key(znode * node); + +extern reiser4_key *znode_set_rd_key(znode * node, const reiser4_key * key); +extern reiser4_key *znode_set_ld_key(znode * node, const reiser4_key * key); + +/* `connected' state checks */ +static inline int +znode_is_right_connected(const znode * node) +{ + return ZF_ISSET(node, JNODE_RIGHT_CONNECTED); +} + +static inline int +znode_is_left_connected(const znode * node) +{ + return ZF_ISSET(node, JNODE_LEFT_CONNECTED); +} + +static inline int +znode_is_connected(const znode * node) +{ + return znode_is_right_connected(node) && znode_is_left_connected(node); +} + +extern int znode_rehash(znode * node, const reiser4_block_nr * new_block_nr); +extern void znode_remove(znode *, reiser4_tree *); +extern znode *znode_parent(const znode * node); +extern znode *znode_parent_nolock(const znode * node); +extern int znode_above_root(const znode * node); +extern int znode_is_true_root(const znode * node); +extern void zdrop(znode * node); +extern int znodes_init(void); +extern int znodes_done(void); +extern int znodes_tree_init(reiser4_tree * ztree); +extern void znodes_tree_done(reiser4_tree * ztree); +extern int znode_contains_key(znode * node, const reiser4_key * key); +extern int znode_contains_key_lock(znode * node, const reiser4_key * key); +extern unsigned znode_save_free_space(znode * node); +extern unsigned znode_recover_free_space(znode * node); + +extern int znode_just_created(const znode * node); + +extern void zfree(znode * node); + +#if REISER4_DEBUG_MODIFY +extern void znode_pre_write(znode * node); +extern void znode_post_write(znode * node); +extern void znode_set_checksum(jnode * node, int locked_p); +extern int znode_at_read(const znode * node); +#else +#define znode_pre_write(n) noop +#define znode_post_write(n) noop +#define znode_set_checksum(n, l) noop +#define znode_at_read(n) (1) +#endif + +#if REISER4_DEBUG_OUTPUT +extern void print_znode(const char *prefix, const znode * node); +extern void info_znode(const char *prefix, const znode * node); +extern void print_znodes(const char *prefix, reiser4_tree * tree); +extern void print_lock_stack(const char *prefix, lock_stack * owner); +#else +#define print_znode( p, n ) noop +#define info_znode( p, n ) noop +#define print_znodes( p, t ) noop +#define print_lock_stack( p, o ) noop +#endif + +/* Make it look like various znode functions exist instead of treating znodes as + jnodes in znode-specific code. */ +#define znode_page(x) jnode_page ( ZJNODE(x) ) +#define zdata(x) jdata ( ZJNODE(x) ) +#define znode_get_block(x) jnode_get_block ( ZJNODE(x) ) +#define znode_created(x) jnode_created ( ZJNODE(x) ) +#define znode_set_created(x) jnode_set_created ( ZJNODE(x) ) +#define znode_squeezable(x) jnode_squeezable (ZJNODE(x)) +#define znode_set_squeezable(x) jnode_set_squeezable (ZJNODE(x)) + +#define znode_is_dirty(x) jnode_is_dirty ( ZJNODE(x) ) +#define znode_check_dirty(x) jnode_check_dirty ( ZJNODE(x) ) +#define znode_make_clean(x) jnode_make_clean ( ZJNODE(x) ) +#define znode_set_block(x, b) jnode_set_block ( ZJNODE(x), (b) ) + +#define spin_lock_znode(x) LOCK_JNODE ( ZJNODE(x) ) +#define spin_unlock_znode(x) UNLOCK_JNODE ( ZJNODE(x) ) +#define spin_trylock_znode(x) spin_trylock_jnode ( ZJNODE(x) ) +#define spin_znode_is_locked(x) spin_jnode_is_locked ( ZJNODE(x) ) +#define spin_znode_is_not_locked(x) spin_jnode_is_not_locked ( ZJNODE(x) ) + +#if REISER4_DEBUG +extern int znode_x_count_is_protected(const znode * node); +#endif + +#if REISER4_DEBUG_NODE_INVARIANT +extern int znode_invariant(const znode * node); +#else +#define znode_invariant(n) (1) +#endif + +/* acquire reference to @node */ +static inline znode * +zref(znode * node) +{ + /* change of x_count from 0 to 1 is protected by tree spin-lock */ + return JZNODE(jref(ZJNODE(node))); +} + +/* release reference to @node */ +static inline void +zput(znode * node) +{ + assert("nikita-3564", znode_invariant(node)); + jput(ZJNODE(node)); +} + +/* get the level field for a znode */ +static inline tree_level +znode_get_level(const znode * node) +{ + return node->level; +} + +/* get the level field for a jnode */ +static inline tree_level +jnode_get_level(const jnode * node) +{ + if (jnode_is_znode(node)) + return znode_get_level(JZNODE(node)); + else + /* unformatted nodes are all at the LEAF_LEVEL and for + "semi-formatted" nodes like bitmaps, level doesn't matter. */ + return LEAF_LEVEL; +} + +/* true if jnode is on leaf level */ +static inline int jnode_is_leaf(const jnode * node) +{ + if (jnode_is_znode(node)) + return (znode_get_level(JZNODE(node)) == LEAF_LEVEL); + if (jnode_get_type(node) == JNODE_UNFORMATTED_BLOCK) + return 1; + return 0; +} + +/* return znode's tree */ +static inline reiser4_tree * +znode_get_tree(const znode * node) +{ + assert("nikita-2692", node != NULL); + return jnode_get_tree(ZJNODE(node)); +} + +/* resolve race with zput */ +static inline znode * +znode_rip_check(reiser4_tree *tree, znode * node) +{ + jnode *j; + + j = jnode_rip_sync(tree, ZJNODE(node)); + if (likely(j != NULL)) + node = JZNODE(j); + else + node = NULL; + return node; +} + +#if defined(REISER4_DEBUG) || defined(REISER4_DEBUG_MODIFY) || defined(REISER4_DEBUG_OUTPUT) +int znode_is_loaded(const znode * node /* znode to query */ ); +#endif + +extern z_hash_table *get_htable(reiser4_tree * tree, + const reiser4_block_nr * const blocknr); +extern z_hash_table *znode_get_htable(const znode *node); + +extern __u64 znode_build_version(reiser4_tree * tree); + +extern int znode_relocate(znode * node, reiser4_block_nr * blk); + +/* Data-handles. A data handle object manages pairing calls to zload() and zrelse(). We + must load the data for a node in many places. We could do this by simply calling + zload() everywhere, the difficulty arises when we must release the loaded data by + calling zrelse. In a function with many possible error/return paths, it requires extra + work to figure out which exit paths must call zrelse and those which do not. The data + handle automatically calls zrelse for every zload that it is responsible for. In that + sense, it acts much like a lock_handle. +*/ +typedef struct load_count { + znode *node; + int d_ref; +} load_count; + +extern void init_load_count(load_count * lc); /* Initialize a load_count set the current node to NULL. */ +extern void done_load_count(load_count * dh); /* Finalize a load_count: call zrelse() if necessary */ +extern int incr_load_count(load_count * dh); /* Call zload() on the current node. */ +extern int incr_load_count_znode(load_count * dh, znode * node); /* Set the argument znode to the current node, call zload(). */ +extern int incr_load_count_jnode(load_count * dh, jnode * node); /* If the argument jnode is formatted, do the same as + * incr_load_count_znode, otherwise do nothing (unformatted nodes + * don't require zload/zrelse treatment). */ +extern void move_load_count(load_count * new, load_count * old); /* Move the contents of a load_count. Old handle is released. */ +extern void copy_load_count(load_count * new, load_count * old); /* Copy the contents of a load_count. Old handle remains held. */ + +/* Variable initializers for load_count. */ +#define INIT_LOAD_COUNT ( load_count * ){ .node = NULL, .d_ref = 0 } +#define INIT_LOAD_COUNT_NODE( n ) ( load_count ){ .node = ( n ), .d_ref = 0 } +/* A convenience macro for use in assertions or debug-only code, where loaded + data is only required to perform the debugging check. This macro + encapsulates an expression inside a pair of calls to zload()/zrelse(). */ +#define WITH_DATA( node, exp ) \ +({ \ + long __with_dh_result; \ + znode *__with_dh_node; \ + \ + __with_dh_node = ( node ); \ + __with_dh_result = zload( __with_dh_node ); \ + if( __with_dh_result == 0 ) { \ + __with_dh_result = ( long )( exp ); \ + zrelse( __with_dh_node ); \ + } \ + __with_dh_result; \ +}) + +/* Same as above, but accepts a return value in case zload fails. */ +#define WITH_DATA_RET( node, ret, exp ) \ +({ \ + int __with_dh_result; \ + znode *__with_dh_node; \ + \ + __with_dh_node = ( node ); \ + __with_dh_result = zload( __with_dh_node ); \ + if( __with_dh_result == 0 ) { \ + __with_dh_result = ( int )( exp ); \ + zrelse( __with_dh_node ); \ + } else \ + __with_dh_result = ( ret ); \ + __with_dh_result; \ +}) + +#define WITH_COORD(coord, exp) \ +({ \ + coord_t *__coord; \ + \ + __coord = (coord); \ + coord_clear_iplug(__coord); \ + WITH_DATA(__coord->node, exp); \ +}) + + +#if REISER4_DEBUG_SPIN_LOCKS +#define STORE_COUNTERS \ + lock_counters_info __entry_counters = *lock_counters() +#define CHECK_COUNTERS \ +ON_DEBUG_CONTEXT( \ +({ \ + __entry_counters.x_refs = lock_counters() -> x_refs; \ + __entry_counters.t_refs = lock_counters() -> t_refs; \ + __entry_counters.d_refs = lock_counters() -> d_refs; \ + assert("nikita-2159", \ + !memcmp(&__entry_counters, lock_counters(), \ + sizeof __entry_counters)); \ +}) ) + +#else +#define STORE_COUNTERS +#define CHECK_COUNTERS noop +#endif + +/* __ZNODE_H__ */ +#endif + +/* Make Linus happy. + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 120 + End: +*/ --- linux-2.6.9-rc1/fs/reiserfs/super.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/fs/reiserfs/super.c 2004-09-02 20:51:52.000000000 -0700 @@ -444,7 +444,7 @@ static int init_inodecache(void) { reiserfs_inode_cachep = kmem_cache_create("reiser_inode_cache", sizeof(struct reiserfs_inode_info), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (reiserfs_inode_cachep == NULL) return -ENOMEM; --- linux-2.6.9-rc1/fs/reiserfs/xattr_acl.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/fs/reiserfs/xattr_acl.c 2004-09-02 20:51:52.000000000 -0700 @@ -289,8 +289,14 @@ reiserfs_set_acl(struct inode *inode, in error = reiserfs_xattr_set(inode, name, value, size, 0); } else { error = reiserfs_xattr_del (inode, name); - if (error == -ENODATA) + if (error == -ENODATA) { + /* This may seem odd here, but it means that the ACL was set + * with a value representable with mode bits. If there was + * an ACL before, reiserfs_xattr_del already dirtied the inode. + */ + mark_inode_dirty (inode); error = 0; + } } if (value) --- linux-2.6.9-rc1/fs/reiserfs/xattr.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/fs/reiserfs/xattr.c 2004-09-02 20:51:52.000000000 -0700 @@ -761,6 +761,11 @@ reiserfs_xattr_del (struct inode *inode, err = __reiserfs_xattr_del (dir, name, strlen (name)); dput (dir); + if (!err) { + inode->i_ctime = CURRENT_TIME; + mark_inode_dirty (inode); + } + out: return err; } @@ -1240,8 +1245,10 @@ xattr_lookup_poison (struct dentry *dent name->hash == priv_root->d_name.hash && !memcmp (name->name, priv_root->d_name.name, name->len)) { return -ENOENT; - } - return 0; + } else if (q1->len == name->len && + !memcmp(q1->name, name->name, name->len)) + return 0; + return 1; } static struct dentry_operations xattr_lookup_poison_ops = { --- linux-2.6.9-rc1/fs/romfs/inode.c 2004-08-24 00:03:14.000000000 -0700 +++ 25/fs/romfs/inode.c 2004-09-02 20:51:52.000000000 -0700 @@ -579,7 +579,7 @@ static int init_inodecache(void) { romfs_inode_cachep = kmem_cache_create("romfs_inode_cache", sizeof(struct romfs_inode_info), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (romfs_inode_cachep == NULL) return -ENOMEM; --- linux-2.6.9-rc1/fs/select.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/fs/select.c 2004-09-02 20:51:52.000000000 -0700 @@ -118,15 +118,11 @@ void __pollwait(struct file *filp, wait_ } } +#define FDS_IN(fds, n) (fds->in + n) +#define FDS_OUT(fds, n) (fds->out + n) +#define FDS_EX(fds, n) (fds->ex + n) -#define __IN(fds, n) (fds->in + n) -#define __OUT(fds, n) (fds->out + n) -#define __EX(fds, n) (fds->ex + n) -#define __RES_IN(fds, n) (fds->res_in + n) -#define __RES_OUT(fds, n) (fds->res_out + n) -#define __RES_EX(fds, n) (fds->res_ex + n) - -#define BITS(fds, n) (*__IN(fds, n)|*__OUT(fds, n)|*__EX(fds, n)) +#define BITS(fds, n) (*FDS_IN(fds, n)|*FDS_OUT(fds, n)|*FDS_EX(fds, n)) static int max_select_fd(unsigned long n, fd_set_bits *fds) { @@ -377,9 +373,10 @@ sys_select(int n, fd_set __user *inp, fd ret = 0; } - set_fd_set(n, inp, fds.res_in); - set_fd_set(n, outp, fds.res_out); - set_fd_set(n, exp, fds.res_ex); + if (set_fd_set(n, inp, fds.res_in) || + set_fd_set(n, outp, fds.res_out) || + set_fd_set(n, exp, fds.res_ex)) + ret = -EFAULT; out: select_bits_free(bits, size); --- linux-2.6.9-rc1/fs/smbfs/inode.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/fs/smbfs/inode.c 2004-09-02 20:51:52.000000000 -0700 @@ -80,7 +80,7 @@ static int init_inodecache(void) { smb_inode_cachep = kmem_cache_create("smb_inode_cache", sizeof(struct smb_inode_info), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (smb_inode_cachep == NULL) return -ENOMEM; --- linux-2.6.9-rc1/fs/super.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/fs/super.c 2004-09-03 00:57:14.973762904 -0700 @@ -35,6 +35,7 @@ #include #include /* for the emergency remount stuff */ #include +#include #include @@ -67,7 +68,9 @@ static struct super_block *alloc_super(v INIT_LIST_HEAD(&s->s_io); INIT_LIST_HEAD(&s->s_files); INIT_LIST_HEAD(&s->s_instances); + INIT_LIST_HEAD(&s->s_entries); INIT_HLIST_HEAD(&s->s_anon); + INIT_LIST_HEAD(&s->s_inodes); init_rwsem(&s->s_umount); sema_init(&s->s_lock, 1); down_write(&s->s_umount); @@ -99,6 +102,205 @@ static inline void destroy_super(struct kfree(s); } +/* struct sb_entry obmanipulations. */ + +/** + * register_sb_entry - attach sb_entry to a superblock + * @s: superblock to attach to + * @entry: entry to attach to the @s + * + * Attaches @entry to the superblock, marks it as live. Called under + * write-taken @s->s_umount. + */ +static void register_sb_entry(struct super_block *s, struct sb_entry *entry) +{ + entry->live = 1; + list_add(&entry->linkage, &s->s_entries); + entry->type = s->s_type; + get_filesystem(entry->type); + entry->s = s; +} + +/** + * unregister_sb_entry - detach sb_entry from superblock + * @entry: entry to detach + * + * Detaches @entry from the superblock, and marks it as dead. Called + * under write-taken @entry->s->s_umount. + */ +static void unregister_sb_entry(struct sb_entry *entry) +{ + entry->live = 0; + list_del_init(&entry->linkage); + if (entry->type != NULL) { + put_filesystem(entry->type); + entry->type = NULL; + } +} + +/* helper (*test) callback for get_sb_entry. Should probably be just declared + * local in get_sb_entry(). */ +static int test_sb(struct super_block *sb, void *data) +{ + return data == sb; +} + +/* helper (*set) callback for get_sb_entry. */ +static int set_sb(struct super_block *sb, void *data) +{ + return -ENOENT; +} + +/** + * get_sb_entry - acquire a reference to sb_entry + * @entry: sb_entry to acquire reference to + * + * Acquires a reference to the @entry. This means that after successful + * return from this function @entry (and @entry->s) are guaranteed to + * exist until corresponding call to @put_sb_entry. This function is used + * to avoid races between sysfs/procfs methods and umount. Fails if + * sget() failed or @entry->s is already unmounted. + */ +static int get_sb_entry(struct sb_entry *entry) +{ + int result; + struct super_block *s; + + s = sget(entry->type, test_sb, set_sb, entry->s); + if (IS_ERR(s)) + return PTR_ERR(s); + /* at this point @s is pinned, and s->s_umount is write-taken */ + result = entry->live ? 0 : -ENOENT; + up_write(&s->s_umount); + if (result != 0) + deactivate_super(s); + return result; +} + +/** + * put_sb_entry - release a reference to sb_entry + * @entry: sb_entry to release reference to + * + * Dual to get_sb_entry(), should always be called if former returned + * success. + */ +static void put_sb_entry(struct sb_entry *entry) +{ + /* release reference acquired by get_sb_entry() */ + deactivate_super(entry->s); +} + +/* helper to deactivate_super(): detach all sb_entries from @s */ +static void kill_sb_entries(struct super_block *s) +{ + while (!list_empty(&s->s_entries)) + unregister_sb_entry(container_of(s->s_entries.next, + struct sb_entry, linkage)); +} + +/** + * fs_kobject_register - register file-system kobject + * @s: superblock @fskobj belongs to + * @fskobj: file-system kobject to register + * + * Registers kobject and sb_entry parts of + * @fskobj. Cf. include/linux/fs.h:struct fs_kobject + */ +int fs_kobject_register(struct super_block *s, struct fs_kobject * fskobj) +{ + int result; + + result = kobject_register(&fskobj->kobj); + if (result == 0) + register_sb_entry(s, &fskobj->entry); + return result; +} +EXPORT_SYMBOL(fs_kobject_register); + +/** + * fs_kobject_register - register file-system kobject + * @fskobj: file-system kobject to register + * + * Unregisters kobject and sb_entry parts of @fskobj. + */ +void fs_kobject_unregister(struct fs_kobject * fskobj) +{ + unregister_sb_entry(&fskobj->entry); + kobject_unregister(&fskobj->kobj); +} +EXPORT_SYMBOL(fs_kobject_unregister); + +/** + * fs_attr_show - ->show method for fs_attr_ops. + * @kobj: kobject embedded into struct fs_kobject + * @kattr: kattr embedded into struct fs_kattr + * + * Calls ->show() method of fs_kattr into which @kattr is + * embedded. Avoids races with umount. + */ +static ssize_t +fs_attr_show(struct kobject *kobj, struct attribute *kattr, char *buf) +{ + struct fs_kobject *fskobj; + struct fs_kattr *fskattr; + struct sb_entry *entry; + int result; + + fskobj = container_of(kobj, struct fs_kobject, kobj); + fskattr = container_of(kattr, struct fs_kattr, kattr); + entry = &fskobj->entry; + + result = get_sb_entry(entry); + if (result == 0) { + if (fskattr->show != NULL) + result = fskattr->show(entry->s, fskobj, fskattr, buf); + else + result = 0; + put_sb_entry(entry); + } + return result; +} + +/** + * fs_attr_store - ->store method for fs_attr_ops. + * @kobj: kobject embedded into struct fs_kobject + * @kattr: kattr embedded into struct fs_kattr + * + * Calls ->store() method of fs_kattr into which @kattr is + * embedded. Avoids races with umount. + */ +static ssize_t +fs_attr_store(struct kobject *kobj, struct attribute *kattr, + const char *buf, size_t size) +{ + struct fs_kobject *fskobj; + struct fs_kattr *fskattr; + struct sb_entry *entry; + int result; + + fskobj = container_of(kobj, struct fs_kobject, kobj); + fskattr = container_of(kattr, struct fs_kattr, kattr); + entry = &fskobj->entry; + + result = get_sb_entry(entry); + if (result == 0) { + if (fskattr->store != NULL) + result = fskattr->store(entry->s, + fskobj, fskattr, buf, size); + else + result = 0; + put_sb_entry(entry); + } + return result; +} + +/* sysfs operations for file systems exporting information in /sys/fs */ +struct sysfs_ops fs_attr_ops = { + .show = fs_attr_show, + .store = fs_attr_store +}; +EXPORT_SYMBOL(fs_attr_ops); + /* Superblock refcounting */ /* @@ -147,6 +349,7 @@ void deactivate_super(struct super_block s->s_count -= S_BIAS-1; spin_unlock(&sb_lock); down_write(&s->s_umount); + kill_sb_entries(s); fs->kill_sb(s); put_filesystem(fs); put_super(s); @@ -234,7 +437,6 @@ void generic_shutdown_super(struct super spin_unlock(&sb_lock); up_write(&sb->s_umount); } - EXPORT_SYMBOL(generic_shutdown_super); /** @@ -416,9 +618,8 @@ rescan: spin_unlock(&sb_lock); return NULL; } - EXPORT_SYMBOL(get_super); - + struct super_block * user_get_super(dev_t dev) { struct list_head *p; @@ -675,8 +876,12 @@ struct super_block *get_sb_bdev(struct f up_write(&s->s_umount); deactivate_super(s); s = ERR_PTR(error); - } else + } else { s->s_flags |= MS_ACTIVE; + if (bdev->bd_disk) + send_kevent(KEVENT_FS, NULL, + &bdev->bd_disk->kobj, "mount"); + } } return s; @@ -691,6 +896,10 @@ EXPORT_SYMBOL(get_sb_bdev); void kill_block_super(struct super_block *sb) { struct block_device *bdev = sb->s_bdev; + + if (bdev->bd_disk) + send_kevent(KEVENT_FS, NULL, &bdev->bd_disk->kobj, "umount"); + generic_shutdown_super(sb); set_blocksize(bdev, sb->s_old_blocksize); close_bdev_excl(bdev); --- linux-2.6.9-rc1/fs/sysfs/bin.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/fs/sysfs/bin.c 2004-09-03 00:56:29.877618560 -0700 @@ -17,8 +17,8 @@ static int fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count) { - struct bin_attribute * attr = dentry->d_fsdata; - struct kobject * kobj = dentry->d_parent->d_fsdata; + struct bin_attribute * attr = to_bin_attr(dentry); + struct kobject * kobj = to_kobj(dentry->d_parent); return attr->read(kobj, buffer, off, count); } @@ -60,8 +60,8 @@ read(struct file * file, char __user * u static int flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count) { - struct bin_attribute *attr = dentry->d_fsdata; - struct kobject *kobj = dentry->d_parent->d_fsdata; + struct bin_attribute *attr = to_bin_attr(dentry); + struct kobject *kobj = to_kobj(dentry->d_parent); return attr->write(kobj, buffer, offset, count); } @@ -95,7 +95,7 @@ static ssize_t write(struct file * file, static int open(struct inode * inode, struct file * file) { struct kobject *kobj = sysfs_get_kobject(file->f_dentry->d_parent); - struct bin_attribute * attr = file->f_dentry->d_fsdata; + struct bin_attribute * attr = to_bin_attr(file->f_dentry); int error = -EINVAL; if (!kobj || !attr) @@ -130,8 +130,8 @@ static int open(struct inode * inode, st static int release(struct inode * inode, struct file * file) { - struct kobject * kobj = file->f_dentry->d_parent->d_fsdata; - struct bin_attribute * attr = file->f_dentry->d_fsdata; + struct kobject * kobj = to_kobj(file->f_dentry->d_parent); + struct bin_attribute * attr = to_bin_attr(file->f_dentry); u8 * buffer = file->private_data; if (kobj) @@ -141,7 +141,7 @@ static int release(struct inode * inode, return 0; } -static struct file_operations bin_fops = { +struct file_operations bin_fops = { .read = read, .write = write, .llseek = generic_file_llseek, @@ -158,31 +158,9 @@ static struct file_operations bin_fops = int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr) { - struct dentry * dentry; - struct dentry * parent; - int error = 0; + BUG_ON(!kobj || !kobj->dentry || !attr); - if (!kobj || !attr) - return -EINVAL; - - parent = kobj->dentry; - - down(&parent->d_inode->i_sem); - dentry = sysfs_get_dentry(parent,attr->attr.name); - if (!IS_ERR(dentry)) { - dentry->d_fsdata = (void *)attr; - error = sysfs_create(dentry, - (attr->attr.mode & S_IALLUGO) | S_IFREG, - NULL); - if (!error) { - dentry->d_inode->i_size = attr->size; - dentry->d_inode->i_fop = &bin_fops; - } - dput(dentry); - } else - error = PTR_ERR(dentry); - up(&parent->d_inode->i_sem); - return error; + return sysfs_add_file(kobj->dentry, &attr->attr, SYSFS_KOBJ_BIN_ATTR); } --- linux-2.6.9-rc1/fs/sysfs/dir.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/fs/sysfs/dir.c 2004-09-03 00:56:29.878618408 -0700 @@ -12,32 +12,107 @@ DECLARE_RWSEM(sysfs_rename_sem); +static void sysfs_d_iput(struct dentry * dentry, struct inode * inode) +{ + struct sysfs_dirent * sd = dentry->d_fsdata; + + if (sd) { + BUG_ON(sd->s_dentry != dentry); + sd->s_dentry = NULL; + sysfs_put(sd); + } + iput(inode); +} + +static struct dentry_operations sysfs_dentry_ops = { + .d_iput = sysfs_d_iput, +}; + +/* + * Allocates a new sysfs_dirent and links it to the parent sysfs_dirent + */ +static struct sysfs_dirent * sysfs_new_dirent(struct sysfs_dirent * parent_sd, + void * element) +{ + struct sysfs_dirent * sd; + + sd = kmalloc(sizeof(*sd), GFP_KERNEL); + if (!sd) + return ERR_PTR(-ENOMEM); + + memset(sd, 0, sizeof(*sd)); + atomic_set(&sd->s_count, 1); + INIT_LIST_HEAD(&sd->s_children); + list_add(&sd->s_sibling, &parent_sd->s_children); + sd->s_element = element; + + return sd; +} + +int sysfs_make_dirent(struct sysfs_dirent * parent_sd, struct dentry * dentry, + void * element, umode_t mode, int type) +{ + struct sysfs_dirent * sd; + + sd = sysfs_new_dirent(parent_sd, element); + if (!sd) + return -ENOMEM; + + sd->s_mode = mode; + sd->s_type = type; + sd->s_dentry = dentry; + if (dentry) { + dentry->d_fsdata = sysfs_get(sd); + dentry->d_op = &sysfs_dentry_ops; + } + + return 0; +} + static int init_dir(struct inode * inode) { - inode->i_op = &simple_dir_inode_operations; - inode->i_fop = &simple_dir_operations; + inode->i_op = &sysfs_dir_inode_operations; + inode->i_fop = &sysfs_dir_operations; /* directory inodes start off with i_nlink == 2 (for "." entry) */ inode->i_nlink++; return 0; } +static int init_file(struct inode * inode) +{ + inode->i_size = PAGE_SIZE; + inode->i_fop = &sysfs_file_operations; + return 0; +} + +static int init_symlink(struct inode * inode) +{ + inode->i_op = &sysfs_symlink_inode_operations; + return 0; +} static int create_dir(struct kobject * k, struct dentry * p, const char * n, struct dentry ** d) { int error; + umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO; down(&p->d_inode->i_sem); *d = sysfs_get_dentry(p,n); if (!IS_ERR(*d)) { - error = sysfs_create(*d, - S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO, - init_dir); + error = sysfs_create(*d, mode, init_dir); if (!error) { - (*d)->d_fsdata = k; - p->d_inode->i_nlink++; + error = sysfs_make_dirent(p->d_fsdata, *d, k, mode, + SYSFS_DIR); + if (!error) { + p->d_inode->i_nlink++; + (*d)->d_op = &sysfs_dentry_ops; + d_rehash(*d); + } } + if (error) + d_drop(*d); dput(*d); } else error = PTR_ERR(*d); @@ -63,8 +138,7 @@ int sysfs_create_dir(struct kobject * ko struct dentry * parent; int error = 0; - if (!kobj) - return -EINVAL; + BUG_ON(!kobj); if (kobj->parent) parent = kobj->parent->dentry; @@ -79,12 +153,93 @@ int sysfs_create_dir(struct kobject * ko return error; } +/* attaches attribute's sysfs_dirent to the dentry corresponding to the + * attribute file + */ +static int sysfs_attach_attr(struct sysfs_dirent * sd, struct dentry * dentry) +{ + struct attribute * attr = NULL; + struct bin_attribute * bin_attr = NULL; + int (* init) (struct inode *) = NULL; + int error = 0; + + if (sd->s_type & SYSFS_KOBJ_BIN_ATTR) { + bin_attr = sd->s_element; + attr = &bin_attr->attr; + } else { + attr = sd->s_element; + init = init_file; + } + + error = sysfs_create(dentry, (attr->mode & S_IALLUGO) | S_IFREG, init); + if (error) + return error; + + if (bin_attr) { + dentry->d_inode->i_size = bin_attr->size; + dentry->d_inode->i_fop = &bin_fops; + } + dentry->d_op = &sysfs_dentry_ops; + dentry->d_fsdata = sysfs_get(sd); + sd->s_dentry = dentry; + d_rehash(dentry); + + return 0; +} + +static int sysfs_attach_link(struct sysfs_dirent * sd, struct dentry * dentry) +{ + int err = 0; + + err = sysfs_create(dentry, S_IFLNK|S_IRWXUGO, init_symlink); + if (!err) { + dentry->d_op = &sysfs_dentry_ops; + dentry->d_fsdata = sysfs_get(sd); + sd->s_dentry = dentry; + d_rehash(dentry); + } + return err; +} + +struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +{ + struct sysfs_dirent * parent_sd = dentry->d_parent->d_fsdata; + struct sysfs_dirent * sd; + int err = 0; + + list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { + if (sd->s_type & SYSFS_NOT_PINNED) { + const unsigned char * name = sysfs_get_name(sd); + + if (strcmp(name, dentry->d_name.name)) + continue; + + if (sd->s_type & SYSFS_KOBJ_LINK) + err = sysfs_attach_link(sd, dentry); + else + err = sysfs_attach_attr(sd, dentry); + break; + } + } + + return ERR_PTR(err); +} + +struct inode_operations sysfs_dir_inode_operations = { + .lookup = sysfs_lookup, +}; static void remove_dir(struct dentry * d) { struct dentry * parent = dget(d->d_parent); + struct sysfs_dirent * sd; + down(&parent->d_inode->i_sem); d_delete(d); + sd = d->d_fsdata; + list_del_init(&sd->s_sibling); + sysfs_put(sd); if (d->d_inode) simple_rmdir(parent->d_inode,d); @@ -112,47 +267,22 @@ void sysfs_remove_subdir(struct dentry * void sysfs_remove_dir(struct kobject * kobj) { - struct list_head * node; struct dentry * dentry = dget(kobj->dentry); + struct sysfs_dirent * parent_sd = dentry->d_fsdata; + struct sysfs_dirent * sd, * tmp; if (!dentry) return; pr_debug("sysfs %s: removing dir\n",dentry->d_name.name); down(&dentry->d_inode->i_sem); - - spin_lock(&dcache_lock); -restart: - node = dentry->d_subdirs.next; - while (node != &dentry->d_subdirs) { - struct dentry * d = list_entry(node,struct dentry,d_child); - - node = node->next; - pr_debug(" o %s (%d): ",d->d_name.name,atomic_read(&d->d_count)); - if (!d_unhashed(d) && (d->d_inode)) { - d = dget_locked(d); - pr_debug("removing"); - - /** - * Unlink and unhash. - */ - __d_drop(d); - spin_unlock(&dcache_lock); - /* release the target kobject in case of - * a symlink - */ - if (S_ISLNK(d->d_inode->i_mode)) - kobject_put(d->d_fsdata); - - simple_unlink(dentry->d_inode,d); - dput(d); - pr_debug(" done\n"); - spin_lock(&dcache_lock); - /* re-acquired dcache_lock, need to restart */ - goto restart; - } + list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) { + if (!sd->s_element) + continue; + list_del_init(&sd->s_sibling); + sysfs_drop_dentry(sd, dentry); + sysfs_put(sd); } - spin_unlock(&dcache_lock); up(&dentry->d_inode->i_sem); remove_dir(dentry); @@ -182,9 +312,14 @@ int sysfs_rename_dir(struct kobject * ko if (!IS_ERR(new_dentry)) { if (!new_dentry->d_inode) { error = kobject_set_name(kobj,new_name); - if (!error) + if (!error) { + d_add(new_dentry, NULL); d_move(kobj->dentry, new_dentry); - } + } + else + d_drop(new_dentry); + } else + error = -EEXIST; dput(new_dentry); } up(&parent->d_inode->i_sem); @@ -193,6 +328,144 @@ int sysfs_rename_dir(struct kobject * ko return error; } +static int sysfs_dir_open(struct inode *inode, struct file *file) +{ + struct dentry * dentry = file->f_dentry; + struct sysfs_dirent * parent_sd = dentry->d_fsdata; + + down(&dentry->d_inode->i_sem); + file->private_data = sysfs_new_dirent(parent_sd, NULL); + up(&dentry->d_inode->i_sem); + + return file->private_data ? 0 : -ENOMEM; + +} + +static int sysfs_dir_close(struct inode *inode, struct file *file) +{ + struct dentry * dentry = file->f_dentry; + struct sysfs_dirent * cursor = file->private_data; + + down(&dentry->d_inode->i_sem); + list_del_init(&cursor->s_sibling); + up(&dentry->d_inode->i_sem); + + return 0; +} + +/* Relationship between s_mode and the DT_xxx types */ +static inline unsigned char dt_type(struct sysfs_dirent *sd) +{ + return (sd->s_mode >> 12) & 15; +} + +static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) +{ + struct dentry *dentry = filp->f_dentry; + struct sysfs_dirent * parent_sd = dentry->d_fsdata; + struct sysfs_dirent *cursor = filp->private_data; + struct list_head *p, *q = &cursor->s_sibling; + ino_t ino; + int i = filp->f_pos; + + switch (i) { + case 0: + ino = dentry->d_inode->i_ino; + if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0) + break; + filp->f_pos++; + i++; + /* fallthrough */ + case 1: + ino = parent_ino(dentry); + if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0) + break; + filp->f_pos++; + i++; + /* fallthrough */ + default: + if (filp->f_pos == 2) { + list_del(q); + list_add(q, &parent_sd->s_children); + } + for (p=q->next; p!= &parent_sd->s_children; p=p->next) { + struct sysfs_dirent *next; + const char * name; + int len; + + next = list_entry(p, struct sysfs_dirent, + s_sibling); + if (!next->s_element) + continue; + + name = sysfs_get_name(next); + len = strlen(name); + if (next->s_dentry) + ino = next->s_dentry->d_inode->i_ino; + else + ino = iunique(sysfs_sb, 2); + + if (filldir(dirent, name, len, filp->f_pos, ino, + dt_type(next)) < 0) + return 0; + + list_del(q); + list_add(q, p); + p = q; + filp->f_pos++; + } + } + return 0; +} + +static loff_t sysfs_dir_lseek(struct file * file, loff_t offset, int origin) +{ + struct dentry * dentry = file->f_dentry; + + down(&dentry->d_inode->i_sem); + switch (origin) { + case 1: + offset += file->f_pos; + case 0: + if (offset >= 0) + break; + default: + up(&file->f_dentry->d_inode->i_sem); + return -EINVAL; + } + if (offset != file->f_pos) { + file->f_pos = offset; + if (file->f_pos >= 2) { + struct sysfs_dirent *sd = dentry->d_fsdata; + struct sysfs_dirent *cursor = file->private_data; + struct list_head *p; + loff_t n = file->f_pos - 2; + + list_del(&cursor->s_sibling); + p = sd->s_children.next; + while (n && p != &sd->s_children) { + struct sysfs_dirent *next; + next = list_entry(p, struct sysfs_dirent, + s_sibling); + if (next->s_element) + n--; + p = p->next; + } + list_add_tail(&cursor->s_sibling, p); + } + } + up(&dentry->d_inode->i_sem); + return offset; +} + +struct file_operations sysfs_dir_operations = { + .open = sysfs_dir_open, + .release = sysfs_dir_close, + .llseek = sysfs_dir_lseek, + .read = generic_read_dir, + .readdir = sysfs_readdir, +}; + EXPORT_SYMBOL(sysfs_create_dir); EXPORT_SYMBOL(sysfs_remove_dir); EXPORT_SYMBOL(sysfs_rename_dir); --- linux-2.6.9-rc1/fs/sysfs/file.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/fs/sysfs/file.c 2004-09-03 00:57:25.415175568 -0700 @@ -6,18 +6,10 @@ #include #include #include +#include #include "sysfs.h" -static struct file_operations sysfs_file_operations; - -static int init_file(struct inode * inode) -{ - inode->i_size = PAGE_SIZE; - inode->i_fop = &sysfs_file_operations; - return 0; -} - #define to_subsys(k) container_of(k,struct subsystem,kset.kobj) #define to_sattr(a) container_of(a,struct subsys_attribute,attr) @@ -62,12 +54,13 @@ struct sysfs_buffer { loff_t pos; char * page; struct sysfs_ops * ops; + struct semaphore sem; }; /** * fill_read_buffer - allocate and fill buffer from object. - * @file: file pointer. + * @dentry: dentry pointer. * @buffer: data buffer for file. * * Allocate @buffer->page, if it hasn't been already, then call the @@ -75,10 +68,10 @@ struct sysfs_buffer { * data. * This is called only once, on the file's first read. */ -static int fill_read_buffer(struct file * file, struct sysfs_buffer * buffer) +static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer) { - struct attribute * attr = file->f_dentry->d_fsdata; - struct kobject * kobj = file->f_dentry->d_parent->d_fsdata; + struct attribute * attr = to_attr(dentry); + struct kobject * kobj = to_kobj(dentry->d_parent); struct sysfs_ops * ops = buffer->ops; int ret = 0; ssize_t count; @@ -115,6 +108,9 @@ static int flush_read_buffer(struct sysf { int error; + if (*ppos > buffer->count) + return 0; + if (count > (buffer->count - *ppos)) count = buffer->count - *ppos; @@ -149,13 +145,17 @@ sysfs_read_file(struct file *file, char struct sysfs_buffer * buffer = file->private_data; ssize_t retval = 0; - if (!*ppos) { - if ((retval = fill_read_buffer(file,buffer))) - return retval; + down(&buffer->sem); + if ((!*ppos) || (!buffer->page)) { + if ((retval = fill_read_buffer(file->f_dentry,buffer))) + goto out; } pr_debug("%s: count = %d, ppos = %lld, buf = %s\n", __FUNCTION__,count,*ppos,buffer->page); - return flush_read_buffer(buffer,buf,count,ppos); + retval = flush_read_buffer(buffer,buf,count,ppos); +out: + up(&buffer->sem); + return retval; } @@ -197,10 +197,10 @@ fill_write_buffer(struct sysfs_buffer * */ static int -flush_write_buffer(struct file * file, struct sysfs_buffer * buffer, size_t count) +flush_write_buffer(struct dentry * dentry, struct sysfs_buffer * buffer, size_t count) { - struct attribute * attr = file->f_dentry->d_fsdata; - struct kobject * kobj = file->f_dentry->d_parent->d_fsdata; + struct attribute * attr = to_attr(dentry); + struct kobject * kobj = to_kobj(dentry->d_parent); struct sysfs_ops * ops = buffer->ops; return ops->store(kobj,attr,buffer->page,count); @@ -229,18 +229,20 @@ sysfs_write_file(struct file *file, cons { struct sysfs_buffer * buffer = file->private_data; + down(&buffer->sem); count = fill_write_buffer(buffer,buf,count); if (count > 0) - count = flush_write_buffer(file,buffer,count); + count = flush_write_buffer(file->f_dentry,buffer,count); if (count > 0) *ppos += count; + up(&buffer->sem); return count; } static int check_perm(struct inode * inode, struct file * file) { struct kobject *kobj = sysfs_get_kobject(file->f_dentry->d_parent); - struct attribute * attr = file->f_dentry->d_fsdata; + struct attribute * attr = to_attr(file->f_dentry); struct sysfs_buffer * buffer; struct sysfs_ops * ops = NULL; int error = 0; @@ -296,6 +298,7 @@ static int check_perm(struct inode * ino buffer = kmalloc(sizeof(struct sysfs_buffer),GFP_KERNEL); if (buffer) { memset(buffer,0,sizeof(struct sysfs_buffer)); + init_MUTEX(&buffer->sem); buffer->ops = ops; file->private_data = buffer; } else @@ -321,8 +324,8 @@ static int sysfs_open_file(struct inode static int sysfs_release(struct inode * inode, struct file * filp) { - struct kobject * kobj = filp->f_dentry->d_parent->d_fsdata; - struct attribute * attr = filp->f_dentry->d_fsdata; + struct kobject * kobj = to_kobj(filp->f_dentry->d_parent); + struct attribute * attr = to_attr(filp->f_dentry); struct sysfs_buffer * buffer = filp->private_data; if (kobj) @@ -337,7 +340,7 @@ static int sysfs_release(struct inode * return 0; } -static struct file_operations sysfs_file_operations = { +struct file_operations sysfs_file_operations = { .read = sysfs_read_file, .write = sysfs_write_file, .llseek = generic_file_llseek, @@ -346,23 +349,16 @@ static struct file_operations sysfs_file }; -int sysfs_add_file(struct dentry * dir, const struct attribute * attr) +int sysfs_add_file(struct dentry * dir, const struct attribute * attr, int type) { - struct dentry * dentry; - int error; + struct sysfs_dirent * parent_sd = dir->d_fsdata; + umode_t mode = (attr->mode & S_IALLUGO) | S_IFREG; + int error = 0; down(&dir->d_inode->i_sem); - dentry = sysfs_get_dentry(dir,attr->name); - if (!IS_ERR(dentry)) { - error = sysfs_create(dentry, - (attr->mode & S_IALLUGO) | S_IFREG, - init_file); - if (!error) - dentry->d_fsdata = (void *)attr; - dput(dentry); - } else - error = PTR_ERR(dentry); + error = sysfs_make_dirent(parent_sd, NULL, (void *) attr, mode, type); up(&dir->d_inode->i_sem); + return error; } @@ -375,9 +371,10 @@ int sysfs_add_file(struct dentry * dir, int sysfs_create_file(struct kobject * kobj, const struct attribute * attr) { - if (kobj && attr) - return sysfs_add_file(kobj->dentry,attr); - return -EINVAL; + BUG_ON(!kobj || !kobj->dentry || !attr); + + return sysfs_add_file(kobj->dentry, attr, SYSFS_KOBJ_ATTR); + } @@ -409,7 +406,8 @@ int sysfs_update_file(struct kobject * k */ dput(victim); res = 0; - } + } else + d_drop(victim); /** * Drop the reference acquired from sysfs_get_dentry() above. --- linux-2.6.9-rc1/fs/sysfs/group.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/fs/sysfs/group.c 2004-09-03 00:56:29.370695624 -0700 @@ -31,7 +31,7 @@ static int create_files(struct dentry * int error = 0; for (attr = grp->attrs; *attr && !error; attr++) { - error = sysfs_add_file(dir,*attr); + error = sysfs_add_file(dir, *attr, SYSFS_KOBJ_ATTR); } if (error) remove_files(dir,grp); @@ -45,6 +45,8 @@ int sysfs_create_group(struct kobject * struct dentry * dir; int error; + BUG_ON(!kobj || !kobj->dentry); + if (grp->name) { error = sysfs_create_subdir(kobj,grp->name,&dir); if (error) --- linux-2.6.9-rc1/fs/sysfs/inode.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/fs/sysfs/inode.c 2004-09-03 00:56:29.591662032 -0700 @@ -11,6 +11,8 @@ #include #include #include +#include "sysfs.h" + extern struct super_block * sysfs_sb; static struct address_space_operations sysfs_aops = { @@ -29,8 +31,8 @@ struct inode * sysfs_new_inode(mode_t mo struct inode * inode = new_inode(sysfs_sb); if (inode) { inode->i_mode = mode; - inode->i_uid = current->fsuid; - inode->i_gid = current->fsgid; + inode->i_uid = 0; + inode->i_gid = 0; inode->i_blksize = PAGE_CACHE_SIZE; inode->i_blocks = 0; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; @@ -66,7 +68,8 @@ int sysfs_create(struct dentry * dentry, error = init(inode); if (!error) { d_instantiate(dentry, inode); - dget(dentry); /* Extra count - pin the dentry in core */ + if (S_ISDIR(mode)) + dget(dentry); /* pin only directory dentry in core */ } else iput(inode); Done: @@ -88,31 +91,74 @@ struct dentry * sysfs_get_dentry(struct return lookup_hash(&qstr,parent); } +/* + * Get the name for corresponding element represented by the given sysfs_dirent + */ +const unsigned char * sysfs_get_name(struct sysfs_dirent *sd) +{ + struct attribute * attr; + struct bin_attribute * bin_attr; + struct sysfs_symlink * sl; + + if (!sd || !sd->s_element) + BUG(); + + switch (sd->s_type) { + case SYSFS_DIR: + /* Always have a dentry so use that */ + return sd->s_dentry->d_name.name; + + case SYSFS_KOBJ_ATTR: + attr = sd->s_element; + return attr->name; + + case SYSFS_KOBJ_BIN_ATTR: + bin_attr = sd->s_element; + return bin_attr->attr.name; + + case SYSFS_KOBJ_LINK: + sl = sd->s_element; + return sl->link_name; + } + return NULL; +} + + +/* + * Unhashes the dentry corresponding to given sysfs_dirent + * Called with parent inode's i_sem held. + */ +void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent) +{ + struct dentry * dentry = sd->s_dentry; + + if (dentry) { + spin_lock(&dcache_lock); + if (!(d_unhashed(dentry) && dentry->d_inode)) { + dget_locked(dentry); + __d_drop(dentry); + spin_unlock(&dcache_lock); + simple_unlink(parent->d_inode, dentry); + } else + spin_unlock(&dcache_lock); + } +} + void sysfs_hash_and_remove(struct dentry * dir, const char * name) { - struct dentry * victim; + struct sysfs_dirent * sd; + struct sysfs_dirent * parent_sd = dir->d_fsdata; down(&dir->d_inode->i_sem); - victim = sysfs_get_dentry(dir,name); - if (!IS_ERR(victim)) { - /* make sure dentry is really there */ - if (victim->d_inode && - (victim->d_parent->d_inode == dir->d_inode)) { - pr_debug("sysfs: Removing %s (%d)\n", victim->d_name.name, - atomic_read(&victim->d_count)); - - d_drop(victim); - /* release the target kobject in case of - * a symlink - */ - if (S_ISLNK(victim->d_inode->i_mode)) - kobject_put(victim->d_fsdata); - simple_unlink(dir->d_inode,victim); + list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { + if (!sd->s_element) + continue; + if (!strcmp(sysfs_get_name(sd), name)) { + list_del_init(&sd->s_sibling); + sysfs_drop_dentry(sd, dir); + sysfs_put(sd); + break; } - /* - * Drop reference from sysfs_get_dentry() above. - */ - dput(victim); } up(&dir->d_inode->i_sem); } --- linux-2.6.9-rc1/fs/sysfs/mount.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/fs/sysfs/mount.c 2004-09-03 00:56:29.879618256 -0700 @@ -22,6 +22,13 @@ static struct super_operations sysfs_ops .drop_inode = generic_delete_inode, }; +struct sysfs_dirent sysfs_root = { + .s_sibling = LIST_HEAD_INIT(sysfs_root.s_sibling), + .s_children = LIST_HEAD_INIT(sysfs_root.s_children), + .s_element = NULL, + .s_type = SYSFS_ROOT, +}; + static int sysfs_fill_super(struct super_block *sb, void *data, int silent) { struct inode *inode; @@ -35,8 +42,8 @@ static int sysfs_fill_super(struct super inode = sysfs_new_inode(S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO); if (inode) { - inode->i_op = &simple_dir_inode_operations; - inode->i_fop = &simple_dir_operations; + inode->i_op = &sysfs_dir_inode_operations; + inode->i_fop = &sysfs_dir_operations; /* directory inodes start off with i_nlink == 2 (for "." entry) */ inode->i_nlink++; } else { @@ -50,6 +57,7 @@ static int sysfs_fill_super(struct super iput(inode); return -ENOMEM; } + root->d_fsdata = &sysfs_root; sb->s_root = root; return 0; } --- linux-2.6.9-rc1/fs/sysfs/symlink.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/fs/sysfs/symlink.c 2004-09-03 00:56:29.879618256 -0700 @@ -5,20 +5,16 @@ #include #include #include +#include #include "sysfs.h" -static struct inode_operations sysfs_symlink_inode_operations = { - .readlink = sysfs_readlink, +struct inode_operations sysfs_symlink_inode_operations = { + .readlink = generic_readlink, .follow_link = sysfs_follow_link, + .put_link = sysfs_put_link, }; -static int init_symlink(struct inode * inode) -{ - inode->i_op = &sysfs_symlink_inode_operations; - return 0; -} - static int object_depth(struct kobject * kobj) { struct kobject * p = kobj; @@ -53,6 +49,36 @@ static void fill_object_path(struct kobj } } +static int sysfs_add_link(struct dentry * parent, char * name, struct kobject * target) +{ + struct sysfs_dirent * parent_sd = parent->d_fsdata; + struct sysfs_symlink * sl; + int error = 0; + + error = -ENOMEM; + sl = kmalloc(sizeof(*sl), GFP_KERNEL); + if (!sl) + goto exit1; + + sl->link_name = kmalloc(strlen(name) + 1, GFP_KERNEL); + if (!sl->link_name) + goto exit2; + + strcpy(sl->link_name, name); + sl->target_kobj = kobject_get(target); + + error = sysfs_make_dirent(parent_sd, NULL, sl, S_IFLNK|S_IRWXUGO, + SYSFS_KOBJ_LINK); + if (!error) + return 0; + + kfree(sl->link_name); +exit2: + kfree(sl); +exit1: + return error; +} + /** * sysfs_create_link - create symlink between two objects. * @kobj: object whose directory we're creating the link in. @@ -62,21 +88,12 @@ static void fill_object_path(struct kobj int sysfs_create_link(struct kobject * kobj, struct kobject * target, char * name) { struct dentry * dentry = kobj->dentry; - struct dentry * d; int error = 0; + BUG_ON(!kobj || !kobj->dentry || !name); + down(&dentry->d_inode->i_sem); - d = sysfs_get_dentry(dentry,name); - if (!IS_ERR(d)) { - error = sysfs_create(d, S_IFLNK|S_IRWXUGO, init_symlink); - if (!error) - /* - * associate the link dentry with the target kobject - */ - d->d_fsdata = kobject_get(target); - dput(d); - } else - error = PTR_ERR(d); + error = sysfs_add_link(dentry, name, target); up(&dentry->d_inode->i_sem); return error; } @@ -140,38 +157,21 @@ static int sysfs_getlink(struct dentry * } -int sysfs_readlink(struct dentry *dentry, char __user *buffer, int buflen) +int sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) { - int error = 0; + int error = -ENOMEM; unsigned long page = get_zeroed_page(GFP_KERNEL); - - if (!page) - return -ENOMEM; - - error = sysfs_getlink(dentry, (char *) page); - if (!error) - error = vfs_readlink(dentry, buffer, buflen, (char *) page); - - free_page(page); - - return error; + if (page) + error = sysfs_getlink(dentry, (char *) page); + nd_set_link(nd, error ? ERR_PTR(error) : (char *)page); + return 0; } -int sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) +void sysfs_put_link(struct dentry *dentry, struct nameidata *nd) { - int error = 0; - unsigned long page = get_zeroed_page(GFP_KERNEL); - - if (!page) - return -ENOMEM; - - error = sysfs_getlink(dentry, (char *) page); - if (!error) - error = vfs_follow_link(nd, (char *) page); - - free_page(page); - - return error; + char *page = nd_get_link(nd); + if (!IS_ERR(page)) + free_page((unsigned long)page); } EXPORT_SYMBOL(sysfs_create_link); --- linux-2.6.9-rc1/fs/sysfs/sysfs.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/fs/sysfs/sysfs.h 2004-09-03 00:56:29.880618104 -0700 @@ -4,26 +4,93 @@ extern struct vfsmount * sysfs_mount; extern struct inode * sysfs_new_inode(mode_t mode); extern int sysfs_create(struct dentry *, int mode, int (*init)(struct inode *)); +extern int sysfs_make_dirent(struct sysfs_dirent *, struct dentry *, void *, + umode_t, int); extern struct dentry * sysfs_get_dentry(struct dentry *, const char *); -extern int sysfs_add_file(struct dentry * dir, const struct attribute * attr); +extern int sysfs_add_file(struct dentry *, const struct attribute *, int); extern void sysfs_hash_and_remove(struct dentry * dir, const char * name); extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **); extern void sysfs_remove_subdir(struct dentry *); -extern int sysfs_readlink(struct dentry *, char __user *, int ); +extern const unsigned char * sysfs_get_name(struct sysfs_dirent *sd); +extern void sysfs_drop_dentry(struct sysfs_dirent *sd, struct dentry *parent); + extern int sysfs_follow_link(struct dentry *, struct nameidata *); +extern void sysfs_put_link(struct dentry *, struct nameidata *); extern struct rw_semaphore sysfs_rename_sem; +extern struct super_block * sysfs_sb; +extern struct file_operations sysfs_dir_operations; +extern struct file_operations sysfs_file_operations; +extern struct file_operations bin_fops; +extern struct inode_operations sysfs_dir_inode_operations; +extern struct inode_operations sysfs_symlink_inode_operations; + +struct sysfs_symlink { + char * link_name; + struct kobject * target_kobj; +}; + +static inline struct kobject * to_kobj(struct dentry * dentry) +{ + struct sysfs_dirent * sd = dentry->d_fsdata; + return ((struct kobject *) sd->s_element); +} + +static inline struct attribute * to_attr(struct dentry * dentry) +{ + struct sysfs_dirent * sd = dentry->d_fsdata; + return ((struct attribute *) sd->s_element); +} + +static inline struct bin_attribute * to_bin_attr(struct dentry * dentry) +{ + struct sysfs_dirent * sd = dentry->d_fsdata; + return ((struct bin_attribute *) sd->s_element); +} static inline struct kobject *sysfs_get_kobject(struct dentry *dentry) { struct kobject * kobj = NULL; spin_lock(&dcache_lock); - if (!d_unhashed(dentry)) - kobj = kobject_get(dentry->d_fsdata); + if (!d_unhashed(dentry)) { + struct sysfs_dirent * sd = dentry->d_fsdata; + if (sd->s_type & SYSFS_KOBJ_LINK) { + struct sysfs_symlink * sl = sd->s_element; + kobj = kobject_get(sl->target_kobj); + } else + kobj = kobject_get(sd->s_element); + } spin_unlock(&dcache_lock); return kobj; } + +static inline void release_sysfs_dirent(struct sysfs_dirent * sd) +{ + if (sd->s_type & SYSFS_KOBJ_LINK) { + struct sysfs_symlink * sl = sd->s_element; + kfree(sl->link_name); + kobject_put(sl->target_kobj); + kfree(sl); + } + kfree(sd); +} + +static inline struct sysfs_dirent * sysfs_get(struct sysfs_dirent * sd) +{ + if (sd) { + WARN_ON(!atomic_read(&sd->s_count)); + atomic_inc(&sd->s_count); + } + return sd; +} + +static inline void sysfs_put(struct sysfs_dirent * sd) +{ + if (atomic_dec_and_test(&sd->s_count)) + release_sysfs_dirent(sd); +} + --- linux-2.6.9-rc1/fs/sysv/inode.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/fs/sysv/inode.c 2004-09-02 20:51:52.000000000 -0700 @@ -340,7 +340,7 @@ int __init sysv_init_icache(void) { sysv_inode_cachep = kmem_cache_create("sysv_inode_cache", sizeof(struct sysv_inode_info), 0, - SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (!sysv_inode_cachep) return -ENOMEM; --- linux-2.6.9-rc1/fs/udf/super.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/fs/udf/super.c 2004-09-02 20:51:52.000000000 -0700 @@ -145,7 +145,7 @@ static int init_inodecache(void) { udf_inode_cachep = kmem_cache_create("udf_inode_cache", sizeof(struct udf_inode_info), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (udf_inode_cachep == NULL) return -ENOMEM; --- linux-2.6.9-rc1/fs/ufs/super.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/fs/ufs/super.c 2004-09-02 20:51:52.000000000 -0700 @@ -1183,7 +1183,7 @@ static int init_inodecache(void) { ufs_inode_cachep = kmem_cache_create("ufs_inode_cache", sizeof(struct ufs_inode_info), - 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + 0, SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (ufs_inode_cachep == NULL) return -ENOMEM; --- linux-2.6.9-rc1/fs/vfat/namei.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/fs/vfat/namei.c 2004-09-03 00:57:15.786639328 -0700 @@ -73,33 +73,6 @@ static int vfat_revalidate(struct dentry return ret; } -static inline unsigned char -vfat_tolower(struct nls_table *t, unsigned char c) -{ - unsigned char nc = t->charset2lower[c]; - - return nc ? nc : c; -} - -static inline unsigned char -vfat_toupper(struct nls_table *t, unsigned char c) -{ - unsigned char nc = t->charset2upper[c]; - - return nc ? nc : c; -} - -static inline int -vfat_strnicmp(struct nls_table *t, const unsigned char *s1, - const unsigned char *s2, int len) -{ - while(len--) - if (vfat_tolower(t, *s1++) != vfat_tolower(t, *s2++)) - return 1; - - return 0; -} - /* returns the length of a struct qstr, ignoring trailing dots */ static unsigned int vfat_striptail_len(struct qstr *qstr) { @@ -142,7 +115,7 @@ static int vfat_hashi(struct dentry *den hash = init_name_hash(); while (len--) - hash = partial_name_hash(vfat_tolower(t, *name++), hash); + hash = partial_name_hash(nls_tolower(t, *name++), hash); qstr->hash = end_name_hash(hash); return 0; @@ -160,7 +133,7 @@ static int vfat_cmpi(struct dentry *dent alen = vfat_striptail_len(a); blen = vfat_striptail_len(b); if (alen == blen) { - if (vfat_strnicmp(t, a->name, b->name, alen) == 0) + if (nls_strnicmp(t, a->name, b->name, alen) == 0) return 0; } return 1; @@ -341,7 +314,7 @@ static inline int to_shortname_char(stru info->upper = 0; } - buf[0] = vfat_toupper(nls, buf[0]); + buf[0] = nls_toupper(nls, buf[0]); if (isalpha(buf[0])) { if (buf[0] == prev) info->lower = 0; --- linux-2.6.9-rc1/fs/xfs/linux-2.6/xfs_file.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/fs/xfs/linux-2.6/xfs_file.c 2004-09-02 20:51:52.000000000 -0700 @@ -116,17 +116,10 @@ __linvfs_write( ssize_t rval; BUG_ON(iocb->ki_pos != pos); - if (unlikely(file->f_flags & O_DIRECT)) { + if (unlikely(file->f_flags & O_DIRECT)) ioflags |= IO_ISDIRECT; - VOP_WRITE(vp, iocb, &iov, 1, &iocb->ki_pos, - ioflags, NULL, rval); - } else { - down(&inode->i_sem); - VOP_WRITE(vp, iocb, &iov, 1, &iocb->ki_pos, - ioflags, NULL, rval); - up(&inode->i_sem); - } + VOP_WRITE(vp, iocb, &iov, 1, &iocb->ki_pos, ioflags, NULL, rval); return rval; } @@ -214,17 +207,10 @@ __linvfs_writev( init_sync_kiocb(&kiocb, file); kiocb.ki_pos = *ppos; - if (unlikely(file->f_flags & O_DIRECT)) { + if (unlikely(file->f_flags & O_DIRECT)) ioflags |= IO_ISDIRECT; - VOP_WRITE(vp, &kiocb, iov, nr_segs, &kiocb.ki_pos, - ioflags, NULL, rval); - } else { - down(&inode->i_sem); - VOP_WRITE(vp, &kiocb, iov, nr_segs, &kiocb.ki_pos, - ioflags, NULL, rval); - up(&inode->i_sem); - } + VOP_WRITE(vp, &kiocb, iov, nr_segs, &kiocb.ki_pos, ioflags, NULL, rval); if (rval == -EIOCBQUEUED) rval = wait_on_sync_kiocb(&kiocb); @@ -259,7 +245,7 @@ linvfs_sendfile( loff_t *ppos, size_t count, read_actor_t actor, - void __user *target) + void *target) { vnode_t *vp = LINVFS_GET_VP(filp->f_dentry->d_inode); ssize_t rval; --- linux-2.6.9-rc1/fs/xfs/linux-2.6/xfs_ioctl32.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/fs/xfs/linux-2.6/xfs_ioctl32.c 2004-09-02 20:51:52.000000000 -0700 @@ -45,8 +45,7 @@ #if defined(CONFIG_IA64) || defined(CONFIG_X86_64) #define BROKEN_X86_ALIGNMENT -#endif - +#else typedef struct xfs_fsop_bulkreq32 { compat_uptr_t lastip; /* last inode # pointer */ @@ -77,6 +76,7 @@ xfs_ioctl32_bulkstat( return sys_ioctl(fd, cmd, (unsigned long)p); } +#endif struct ioctl_trans xfs_ioctl32_trans[] = { { XFS_IOC_DIOINFO, }, --- linux-2.6.9-rc1/fs/xfs/linux-2.6/xfs_lrw.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/fs/xfs/linux-2.6/xfs_lrw.c 2004-09-02 20:51:52.000000000 -0700 @@ -74,6 +74,7 @@ #include "xfs_iomap.h" #include +#include #if defined(XFS_RW_TRACE) @@ -225,40 +226,11 @@ xfs_inval_cached_pages( int write, int relock) { - xfs_mount_t *mp; - - if (!VN_CACHED(vp)) { - return; - } - - mp = io->io_mount; - - /* - * We need to get the I/O lock exclusively in order - * to safely invalidate pages and mappings. - */ - if (relock) { - XFS_IUNLOCK(mp, io, XFS_IOLOCK_SHARED); - XFS_ILOCK(mp, io, XFS_IOLOCK_EXCL); + if (VN_CACHED(vp)) { + xfs_inval_cached_trace(io, offset, -1, ctooff(offtoct(offset)), -1); + VOP_FLUSHINVAL_PAGES(vp, ctooff(offtoct(offset)), -1, FI_REMAPF_LOCKED); } - /* Writing beyond EOF creates a hole that must be zeroed */ - if (write && (offset > XFS_SIZE(mp, io))) { - xfs_fsize_t isize; - - XFS_ILOCK(mp, io, XFS_ILOCK_EXCL|XFS_EXTSIZE_RD); - isize = XFS_SIZE(mp, io); - if (offset > isize) { - xfs_zero_eof(vp, io, offset, isize, offset); - } - XFS_IUNLOCK(mp, io, XFS_ILOCK_EXCL|XFS_EXTSIZE_RD); - } - - xfs_inval_cached_trace(io, offset, -1, ctooff(offtoct(offset)), -1); - VOP_FLUSHINVAL_PAGES(vp, ctooff(offtoct(offset)), -1, FI_REMAPF_LOCKED); - if (relock) { - XFS_ILOCK_DEMOTE(mp, io, XFS_IOLOCK_EXCL); - } } ssize_t /* bytes read, or (-) error */ @@ -366,7 +338,7 @@ xfs_sendfile( int ioflags, size_t count, read_actor_t actor, - void __user *target, + void *target, cred_t *credp) { ssize_t ret; @@ -637,32 +609,34 @@ xfs_write( bhv_desc_t *bdp, struct kiocb *iocb, const struct iovec *iovp, - unsigned int segs, + unsigned int nsegs, loff_t *offset, int ioflags, cred_t *credp) { struct file *file = iocb->ki_filp; - size_t size = 0; + struct address_space *mapping = file->f_mapping; + struct inode *inode = mapping->host; + unsigned long segs = nsegs; xfs_inode_t *xip; xfs_mount_t *mp; - ssize_t ret; - int error = 0; + ssize_t ret = 0, error = 0; xfs_fsize_t isize, new_size; - xfs_fsize_t n, limit; xfs_iocore_t *io; vnode_t *vp; unsigned long seg; int iolock; int eventsent = 0; vrwlock_t locktype; + size_t ocount = 0, count; + loff_t pos; + int need_isem = 1, need_flush = 0; XFS_STATS_INC(xs_write_calls); vp = BHV_TO_VNODE(bdp); xip = XFS_BHVTOI(bdp); - /* START copy & waste from filemap.c */ for (seg = 0; seg < segs; seg++) { const struct iovec *iv = &iovp[seg]; @@ -670,73 +644,90 @@ xfs_write( * If any segment has a negative length, or the cumulative * length ever wraps negative then return -EINVAL. */ - size += iv->iov_len; - if (unlikely((ssize_t)(size|iv->iov_len) < 0)) - return XFS_ERROR(-EINVAL); + ocount += iv->iov_len; + if (unlikely((ssize_t)(ocount|iv->iov_len) < 0)) + return -EINVAL; + if (access_ok(VERIFY_READ, iv->iov_base, iv->iov_len)) + continue; + if (seg == 0) + return -EFAULT; + segs = seg; + ocount -= iv->iov_len; /* This segment is no good */ + break; } - /* END copy & waste from filemap.c */ - if (size == 0) + count = ocount; + pos = *offset; + + if (count == 0) return 0; io = &xip->i_iocore; mp = io->io_mount; - if (XFS_FORCED_SHUTDOWN(mp)) { + if (XFS_FORCED_SHUTDOWN(mp)) return -EIO; - } if (ioflags & IO_ISDIRECT) { xfs_buftarg_t *target = (xip->i_d.di_flags & XFS_DIFLAG_REALTIME) ? mp->m_rtdev_targp : mp->m_ddev_targp; - if ((*offset & target->pbr_smask) || - (size & target->pbr_smask)) { + if ((pos & target->pbr_smask) || (count & target->pbr_smask)) return XFS_ERROR(-EINVAL); - } - iolock = XFS_IOLOCK_SHARED; - locktype = VRWLOCK_WRITE_DIRECT; - } else { + + if (!VN_CACHED(vp) && pos < i_size_read(inode)) + need_isem = 0; + + if (VN_CACHED(vp)) + need_flush = 1; + } + +relock: + if (need_isem) { iolock = XFS_IOLOCK_EXCL; locktype = VRWLOCK_WRITE; + + down(&inode->i_sem); + } else { + iolock = XFS_IOLOCK_SHARED; + locktype = VRWLOCK_WRITE_DIRECT; } xfs_ilock(xip, XFS_ILOCK_EXCL|iolock); - isize = xip->i_d.di_size; - limit = XFS_MAXIOFFSET(mp); + isize = i_size_read(inode); if (file->f_flags & O_APPEND) *offset = isize; start: - n = limit - *offset; - if (n <= 0) { + error = -generic_write_checks(file, &pos, &count, + S_ISBLK(inode->i_mode)); + if (error) { xfs_iunlock(xip, XFS_ILOCK_EXCL|iolock); - return -EFBIG; + goto out_unlock_isem; } - if (n < size) - size = n; - - new_size = *offset + size; - if (new_size > isize) { + new_size = pos + count; + if (new_size > isize) io->io_new_size = new_size; - } if ((DM_EVENT_ENABLED(vp->v_vfsp, xip, DM_EVENT_WRITE) && !(ioflags & IO_INVIS) && !eventsent)) { - loff_t savedsize = *offset; - int dmflags = FILP_DELAY_FLAG(file) | DM_SEM_FLAG_RD(ioflags); + loff_t savedsize = pos; + int dmflags = FILP_DELAY_FLAG(file); + + if (need_isem) + dmflags |= DM_FLAGS_ISEM; xfs_iunlock(xip, XFS_ILOCK_EXCL); error = XFS_SEND_DATA(xip->i_mount, DM_EVENT_WRITE, vp, - *offset, size, + pos, count, dmflags, &locktype); if (error) { xfs_iunlock(xip, iolock); - return -error; + goto out_unlock_isem; } xfs_ilock(xip, XFS_ILOCK_EXCL); eventsent = 1; @@ -748,9 +739,8 @@ start: * event prevents another call to XFS_SEND_DATA, which is * what allows the size to change in the first place. */ - if ((file->f_flags & O_APPEND) && - savedsize != xip->i_d.di_size) { - *offset = isize = xip->i_d.di_size; + if ((file->f_flags & O_APPEND) && savedsize != isize) { + pos = isize = xip->i_d.di_size; goto start; } } @@ -761,8 +751,10 @@ start: * * We must update xfs' times since revalidate will overcopy xfs. */ - if (size && !(ioflags & IO_INVIS)) + if (!(ioflags & IO_INVIS)) { xfs_ichgtime(xip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); + inode_update_time(inode, 1); + } /* * If the offset is beyond the size of the file, we have a couple @@ -773,12 +765,12 @@ start: * to zero it out up to the new size. */ - if (!(ioflags & IO_ISDIRECT) && (*offset > isize && isize)) { - error = xfs_zero_eof(BHV_TO_VNODE(bdp), io, *offset, - isize, *offset + size); + if (pos > isize) { + error = xfs_zero_eof(BHV_TO_VNODE(bdp), io, pos, + isize, pos + count); if (error) { xfs_iunlock(xip, XFS_ILOCK_EXCL|iolock); - return(-error); + goto out_unlock_isem; } } xfs_iunlock(xip, XFS_ILOCK_EXCL); @@ -795,22 +787,64 @@ start: (S_ISGID | S_IXGRP))) && !capable(CAP_FSETID)) { error = xfs_write_clear_setuid(xip); - if (error) { + if (likely(!error)) + error = -remove_suid(file->f_dentry); + if (unlikely(error)) { xfs_iunlock(xip, iolock); - return -error; + goto out_unlock_isem; } } retry: - if (ioflags & IO_ISDIRECT) { - xfs_inval_cached_pages(vp, io, *offset, 1, 1); - xfs_rw_enter_trace(XFS_DIOWR_ENTER, - io, (void *)iovp, segs, *offset, ioflags); + /* We can write back this queue in page reclaim */ + current->backing_dev_info = mapping->backing_dev_info; + + if ((ioflags & IO_ISDIRECT)) { + if (need_flush) { + xfs_inval_cached_trace(io, pos, -1, + ctooff(offtoct(pos)), -1); + VOP_FLUSHINVAL_PAGES(vp, ctooff(offtoct(pos)), + -1, FI_REMAPF_LOCKED); + } + + if (need_isem) { + /* demote the lock now the cached pages are gone */ + XFS_ILOCK_DEMOTE(mp, io, XFS_IOLOCK_EXCL); + up(&inode->i_sem); + + iolock = XFS_IOLOCK_SHARED; + locktype = VRWLOCK_WRITE_DIRECT; + need_isem = 0; + } + + xfs_rw_enter_trace(XFS_DIOWR_ENTER, io, (void *)iovp, segs, + *offset, ioflags); + ret = generic_file_direct_write(iocb, iovp, + &segs, pos, offset, count, ocount); + + /* + * direct-io write to a hole: fall through to buffered I/O + * for completing the rest of the request. + */ + if (ret >= 0 && ret != count) { + XFS_STATS_ADD(xs_write_bytes, ret); + + pos += ret; + count -= ret; + + need_isem = 1; + ioflags &= ~IO_ISDIRECT; + xfs_iunlock(xip, iolock); + goto relock; + } } else { - xfs_rw_enter_trace(XFS_WRITE_ENTER, - io, (void *)iovp, segs, *offset, ioflags); + xfs_rw_enter_trace(XFS_WRITE_ENTER, io, (void *)iovp, segs, + *offset, ioflags); + ret = generic_file_buffered_write(iocb, iovp, segs, + pos, offset, count, ret); } - ret = generic_file_aio_write_nolock(iocb, iovp, segs, offset); + + current->backing_dev_info = NULL; if ((ret == -ENOSPC) && DM_EVENT_ENABLED(vp->v_vfsp, xip, DM_EVENT_NOSPACE) && @@ -821,17 +855,15 @@ retry: DM_RIGHT_NULL, vp, DM_RIGHT_NULL, NULL, NULL, 0, 0, 0); /* Delay flag intentionally unused */ if (error) - return -error; + goto out_unlock_isem; xfs_rwlock(bdp, locktype); - *offset = xip->i_d.di_size; + pos = xip->i_d.di_size; goto retry; } if (*offset > xip->i_d.di_size) { xfs_ilock(xip, XFS_ILOCK_EXCL); if (*offset > xip->i_d.di_size) { - struct inode *inode = LINVFS_GET_IP(vp); - xip->i_d.di_size = *offset; i_size_write(inode, *offset); xip->i_update_core = 1; @@ -840,23 +872,22 @@ retry: xfs_iunlock(xip, XFS_ILOCK_EXCL); } - if (ret <= 0) { - xfs_rwunlock(bdp, locktype); - return ret; - } + error = -ret; + if (ret <= 0) + goto out_unlock_internal; XFS_STATS_ADD(xs_write_bytes, ret); /* Handle various SYNC-type writes */ - if ((file->f_flags & O_SYNC) || IS_SYNC(file->f_dentry->d_inode)) { - + if ((file->f_flags & O_SYNC) || IS_SYNC(inode)) { /* * If we're treating this as O_DSYNC and we have not updated the * size, force the log. */ + if (!(mp->m_flags & XFS_MOUNT_OSYNCISOSYNC) && + !(xip->i_update_size)) { + xfs_inode_log_item_t *iip = xip->i_itemp; - if (!(mp->m_flags & XFS_MOUNT_OSYNCISOSYNC) - && !(xip->i_update_size)) { /* * If an allocation transaction occurred * without extending the size, then we have to force @@ -876,14 +907,8 @@ retry: * all changes affecting the inode are permanent * when we return. */ - - xfs_inode_log_item_t *iip; - xfs_lsn_t lsn; - - iip = xip->i_itemp; if (iip && iip->ili_last_lsn) { - lsn = iip->ili_last_lsn; - xfs_log_force(mp, lsn, + xfs_log_force(mp, iip->ili_last_lsn, XFS_LOG_FORCE | XFS_LOG_SYNC); } else if (xfs_ipincount(xip) > 0) { xfs_log_force(mp, (xfs_lsn_t)0, @@ -924,12 +949,25 @@ retry: xfs_trans_set_sync(tp); error = xfs_trans_commit(tp, 0, NULL); xfs_iunlock(xip, XFS_ILOCK_EXCL); + if (error) + goto out_unlock_internal; } } - } /* (ioflags & O_SYNC) */ + xfs_rwunlock(bdp, locktype); + + error = sync_page_range(inode, mapping, pos, ret); + if (!error) + error = -ret; + goto out_unlock_isem; + } + + out_unlock_internal: xfs_rwunlock(bdp, locktype); - return(ret); + out_unlock_isem: + if (need_isem) + up(&inode->i_sem); + return -error; } /* --- linux-2.6.9-rc1/fs/xfs/linux-2.6/xfs_lrw.h 2004-08-24 00:01:53.000000000 -0700 +++ 25/fs/xfs/linux-2.6/xfs_lrw.h 2004-09-02 20:51:52.000000000 -0700 @@ -104,7 +104,7 @@ extern ssize_t xfs_write(struct bhv_desc loff_t *, int, struct cred *); extern ssize_t xfs_sendfile(struct bhv_desc *, struct file *, loff_t *, int, size_t, read_actor_t, - void __user *, struct cred *); + void *, struct cred *); extern int xfs_dev_is_read_only(struct xfs_mount *, char *); --- linux-2.6.9-rc1/fs/xfs/linux-2.6/xfs_super.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/fs/xfs/linux-2.6/xfs_super.c 2004-09-02 20:51:52.000000000 -0700 @@ -335,8 +335,7 @@ STATIC int init_inodecache( void ) { linvfs_inode_zone = kmem_cache_create("linvfs_icache", - sizeof(vnode_t), 0, - SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, + sizeof(vnode_t), 0, SLAB_RECLAIM_ACCOUNT, init_once, NULL); if (linvfs_inode_zone == NULL) --- linux-2.6.9-rc1/fs/xfs/linux-2.6/xfs_vnode.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/fs/xfs/linux-2.6/xfs_vnode.h 2004-09-02 20:51:52.000000000 -0700 @@ -192,7 +192,7 @@ typedef ssize_t (*vop_write_t)(bhv_desc_ loff_t *, int, struct cred *); typedef ssize_t (*vop_sendfile_t)(bhv_desc_t *, struct file *, loff_t *, int, size_t, read_actor_t, - void __user *, struct cred *); + void *, struct cred *); typedef int (*vop_ioctl_t)(bhv_desc_t *, struct inode *, struct file *, int, unsigned int, void __user *); typedef int (*vop_getattr_t)(bhv_desc_t *, struct vattr *, int, --- linux-2.6.9-rc1/fs/xfs/quota/xfs_dquot.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/fs/xfs/quota/xfs_dquot.c 2004-09-02 20:51:52.000000000 -0700 @@ -187,9 +187,9 @@ xfs_qm_dqdestroy( */ STATIC void xfs_qm_dqinit_core( - xfs_dqid_t id, - uint type, - xfs_dqblk_t *d) + xfs_dqid_t id, + uint type, + xfs_dqblk_t *d) { /* * Caller has zero'd the entire dquot 'chunk' already. @@ -250,6 +250,36 @@ __xfs_dqtrace_entry( /* + * If default limits are in force, push them into the dquot now. + * We overwrite the dquot limits only if they are zero and this + * is not the root dquot. + */ +void +xfs_qm_adjust_dqlimits( + xfs_mount_t *mp, + xfs_disk_dquot_t *d) +{ + xfs_quotainfo_t *q = mp->m_quotainfo; + + ASSERT(!INT_ISZERO(d->d_id, ARCH_CONVERT)); + + if (q->qi_bsoftlimit && INT_ISZERO(d->d_blk_softlimit, ARCH_CONVERT)) + INT_SET(d->d_blk_softlimit, ARCH_CONVERT, q->qi_bsoftlimit); + if (q->qi_bhardlimit && INT_ISZERO(d->d_blk_hardlimit, ARCH_CONVERT)) + INT_SET(d->d_blk_hardlimit, ARCH_CONVERT, q->qi_bhardlimit); + if (q->qi_isoftlimit && INT_ISZERO(d->d_ino_softlimit, ARCH_CONVERT)) + INT_SET(d->d_ino_softlimit, ARCH_CONVERT, q->qi_isoftlimit); + if (q->qi_ihardlimit && INT_ISZERO(d->d_ino_hardlimit, ARCH_CONVERT)) + INT_SET(d->d_ino_hardlimit, ARCH_CONVERT, q->qi_ihardlimit); + if (q->qi_rtbsoftlimit && + INT_ISZERO(d->d_rtb_softlimit, ARCH_CONVERT)) + INT_SET(d->d_rtb_softlimit, ARCH_CONVERT, q->qi_rtbsoftlimit); + if (q->qi_rtbhardlimit && + INT_ISZERO(d->d_rtb_hardlimit, ARCH_CONVERT)) + INT_SET(d->d_rtb_hardlimit, ARCH_CONVERT, q->qi_rtbhardlimit); +} + +/* * Check the limits and timers of a dquot and start or reset timers * if necessary. * This gets called even when quota enforcement is OFF, which makes our @@ -265,53 +295,81 @@ xfs_qm_adjust_dqtimers( xfs_mount_t *mp, xfs_disk_dquot_t *d) { - /* - * The dquot had better be locked. We are modifying it here. - */ - - /* - * root's limits are not real limits. - */ - if (INT_ISZERO(d->d_id, ARCH_CONVERT)) - return; + ASSERT(!INT_ISZERO(d->d_id, ARCH_CONVERT)); #ifdef QUOTADEBUG if (INT_GET(d->d_blk_hardlimit, ARCH_CONVERT)) - ASSERT(INT_GET(d->d_blk_softlimit, ARCH_CONVERT) <= INT_GET(d->d_blk_hardlimit, ARCH_CONVERT)); + ASSERT(INT_GET(d->d_blk_softlimit, ARCH_CONVERT) <= + INT_GET(d->d_blk_hardlimit, ARCH_CONVERT)); if (INT_GET(d->d_ino_hardlimit, ARCH_CONVERT)) - ASSERT(INT_GET(d->d_ino_softlimit, ARCH_CONVERT) <= INT_GET(d->d_ino_hardlimit, ARCH_CONVERT)); + ASSERT(INT_GET(d->d_ino_softlimit, ARCH_CONVERT) <= + INT_GET(d->d_ino_hardlimit, ARCH_CONVERT)); + if (INT_GET(d->d_rtb_hardlimit, ARCH_CONVERT)) + ASSERT(INT_GET(d->d_rtb_softlimit, ARCH_CONVERT) <= + INT_GET(d->d_rtb_hardlimit, ARCH_CONVERT)); #endif if (INT_ISZERO(d->d_btimer, ARCH_CONVERT)) { if ((INT_GET(d->d_blk_softlimit, ARCH_CONVERT) && - (INT_GET(d->d_bcount, ARCH_CONVERT) >= INT_GET(d->d_blk_softlimit, ARCH_CONVERT))) || + (INT_GET(d->d_bcount, ARCH_CONVERT) >= + INT_GET(d->d_blk_softlimit, ARCH_CONVERT))) || (INT_GET(d->d_blk_hardlimit, ARCH_CONVERT) && - (INT_GET(d->d_bcount, ARCH_CONVERT) >= INT_GET(d->d_blk_hardlimit, ARCH_CONVERT)))) { - INT_SET(d->d_btimer, ARCH_CONVERT, get_seconds() + XFS_QI_BTIMELIMIT(mp)); + (INT_GET(d->d_bcount, ARCH_CONVERT) >= + INT_GET(d->d_blk_hardlimit, ARCH_CONVERT)))) { + INT_SET(d->d_btimer, ARCH_CONVERT, + get_seconds() + XFS_QI_BTIMELIMIT(mp)); } } else { if ((INT_ISZERO(d->d_blk_softlimit, ARCH_CONVERT) || - (INT_GET(d->d_bcount, ARCH_CONVERT) < INT_GET(d->d_blk_softlimit, ARCH_CONVERT))) && + (INT_GET(d->d_bcount, ARCH_CONVERT) < + INT_GET(d->d_blk_softlimit, ARCH_CONVERT))) && (INT_ISZERO(d->d_blk_hardlimit, ARCH_CONVERT) || - (INT_GET(d->d_bcount, ARCH_CONVERT) < INT_GET(d->d_blk_hardlimit, ARCH_CONVERT)))) { + (INT_GET(d->d_bcount, ARCH_CONVERT) < + INT_GET(d->d_blk_hardlimit, ARCH_CONVERT)))) { INT_ZERO(d->d_btimer, ARCH_CONVERT); } } if (INT_ISZERO(d->d_itimer, ARCH_CONVERT)) { if ((INT_GET(d->d_ino_softlimit, ARCH_CONVERT) && - (INT_GET(d->d_icount, ARCH_CONVERT) >= INT_GET(d->d_ino_softlimit, ARCH_CONVERT))) || + (INT_GET(d->d_icount, ARCH_CONVERT) >= + INT_GET(d->d_ino_softlimit, ARCH_CONVERT))) || (INT_GET(d->d_ino_hardlimit, ARCH_CONVERT) && - (INT_GET(d->d_icount, ARCH_CONVERT) >= INT_GET(d->d_ino_hardlimit, ARCH_CONVERT)))) { - INT_SET(d->d_itimer, ARCH_CONVERT, get_seconds() + XFS_QI_ITIMELIMIT(mp)); + (INT_GET(d->d_icount, ARCH_CONVERT) >= + INT_GET(d->d_ino_hardlimit, ARCH_CONVERT)))) { + INT_SET(d->d_itimer, ARCH_CONVERT, + get_seconds() + XFS_QI_ITIMELIMIT(mp)); } } else { if ((INT_ISZERO(d->d_ino_softlimit, ARCH_CONVERT) || - (INT_GET(d->d_icount, ARCH_CONVERT) < INT_GET(d->d_ino_softlimit, ARCH_CONVERT))) && + (INT_GET(d->d_icount, ARCH_CONVERT) < + INT_GET(d->d_ino_softlimit, ARCH_CONVERT))) && (INT_ISZERO(d->d_ino_hardlimit, ARCH_CONVERT) || - (INT_GET(d->d_icount, ARCH_CONVERT) < INT_GET(d->d_ino_hardlimit, ARCH_CONVERT)))) { + (INT_GET(d->d_icount, ARCH_CONVERT) < + INT_GET(d->d_ino_hardlimit, ARCH_CONVERT)))) { INT_ZERO(d->d_itimer, ARCH_CONVERT); } } + + if (INT_ISZERO(d->d_rtbtimer, ARCH_CONVERT)) { + if ((INT_GET(d->d_rtb_softlimit, ARCH_CONVERT) && + (INT_GET(d->d_rtbcount, ARCH_CONVERT) >= + INT_GET(d->d_rtb_softlimit, ARCH_CONVERT))) || + (INT_GET(d->d_rtb_hardlimit, ARCH_CONVERT) && + (INT_GET(d->d_rtbcount, ARCH_CONVERT) >= + INT_GET(d->d_rtb_hardlimit, ARCH_CONVERT)))) { + INT_SET(d->d_rtbtimer, ARCH_CONVERT, + get_seconds() + XFS_QI_RTBTIMELIMIT(mp)); + } + } else { + if ((INT_ISZERO(d->d_rtb_softlimit, ARCH_CONVERT) || + (INT_GET(d->d_rtbcount, ARCH_CONVERT) < + INT_GET(d->d_rtb_softlimit, ARCH_CONVERT))) && + (INT_ISZERO(d->d_rtb_hardlimit, ARCH_CONVERT) || + (INT_GET(d->d_rtbcount, ARCH_CONVERT) < + INT_GET(d->d_rtb_hardlimit, ARCH_CONVERT)))) { + INT_ZERO(d->d_rtbtimer, ARCH_CONVERT); + } + } } /* --- linux-2.6.9-rc1/fs/xfs/quota/xfs_dquot.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/fs/xfs/quota/xfs_dquot.h 2004-09-02 20:51:52.000000000 -0700 @@ -209,6 +209,8 @@ extern int xfs_qm_dqflock_nowait(xfs_dq extern void xfs_qm_dqflock_pushbuf_wait(xfs_dquot_t *dqp); extern void xfs_qm_adjust_dqtimers(xfs_mount_t *, xfs_disk_dquot_t *); +extern void xfs_qm_adjust_dqlimits(xfs_mount_t *, + xfs_disk_dquot_t *); extern int xfs_qm_dqwarn(xfs_disk_dquot_t *, uint); extern int xfs_qm_dqget(xfs_mount_t *, xfs_inode_t *, xfs_dqid_t, uint, uint, xfs_dquot_t **); --- linux-2.6.9-rc1/fs/xfs/quota/xfs_qm.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/fs/xfs/quota/xfs_qm.c 2004-09-02 20:51:52.000000000 -0700 @@ -1213,22 +1213,46 @@ xfs_qm_init_quotainfo( XFS_QMOPT_DQSUSER|XFS_QMOPT_DOWARN, &dqp); if (! error) { + xfs_disk_dquot_t *ddqp = &dqp->q_core; + /* * The warnings and timers set the grace period given to * a user or group before he or she can not perform any * more writing. If it is zero, a default is used. */ - qinf->qi_btimelimit = INT_GET(dqp->q_core.d_btimer, ARCH_CONVERT) ? - INT_GET(dqp->q_core.d_btimer, ARCH_CONVERT) : XFS_QM_BTIMELIMIT; - qinf->qi_itimelimit = INT_GET(dqp->q_core.d_itimer, ARCH_CONVERT) ? - INT_GET(dqp->q_core.d_itimer, ARCH_CONVERT) : XFS_QM_ITIMELIMIT; - qinf->qi_rtbtimelimit = INT_GET(dqp->q_core.d_rtbtimer, ARCH_CONVERT) ? - INT_GET(dqp->q_core.d_rtbtimer, ARCH_CONVERT) : XFS_QM_RTBTIMELIMIT; - qinf->qi_bwarnlimit = INT_GET(dqp->q_core.d_bwarns, ARCH_CONVERT) ? - INT_GET(dqp->q_core.d_bwarns, ARCH_CONVERT) : XFS_QM_BWARNLIMIT; - qinf->qi_iwarnlimit = INT_GET(dqp->q_core.d_iwarns, ARCH_CONVERT) ? - INT_GET(dqp->q_core.d_iwarns, ARCH_CONVERT) : XFS_QM_IWARNLIMIT; - + qinf->qi_btimelimit = + INT_GET(ddqp->d_btimer, ARCH_CONVERT) ? + INT_GET(ddqp->d_btimer, ARCH_CONVERT) : + XFS_QM_BTIMELIMIT; + qinf->qi_itimelimit = + INT_GET(ddqp->d_itimer, ARCH_CONVERT) ? + INT_GET(ddqp->d_itimer, ARCH_CONVERT) : + XFS_QM_ITIMELIMIT; + qinf->qi_rtbtimelimit = + INT_GET(ddqp->d_rtbtimer, ARCH_CONVERT) ? + INT_GET(ddqp->d_rtbtimer, ARCH_CONVERT) : + XFS_QM_RTBTIMELIMIT; + qinf->qi_bwarnlimit = + INT_GET(ddqp->d_bwarns, ARCH_CONVERT) ? + INT_GET(ddqp->d_bwarns, ARCH_CONVERT) : + XFS_QM_BWARNLIMIT; + qinf->qi_iwarnlimit = + INT_GET(ddqp->d_iwarns, ARCH_CONVERT) ? + INT_GET(ddqp->d_iwarns, ARCH_CONVERT) : + XFS_QM_IWARNLIMIT; + qinf->qi_bhardlimit = + INT_GET(ddqp->d_blk_hardlimit, ARCH_CONVERT); + qinf->qi_bsoftlimit = + INT_GET(ddqp->d_blk_softlimit, ARCH_CONVERT); + qinf->qi_ihardlimit = + INT_GET(ddqp->d_ino_hardlimit, ARCH_CONVERT); + qinf->qi_isoftlimit = + INT_GET(ddqp->d_ino_softlimit, ARCH_CONVERT); + qinf->qi_rtbhardlimit = + INT_GET(ddqp->d_rtb_hardlimit, ARCH_CONVERT); + qinf->qi_rtbsoftlimit = + INT_GET(ddqp->d_rtb_softlimit, ARCH_CONVERT); + /* * We sent the XFS_QMOPT_DQSUSER flag to dqget because * we don't want this dquot cached. We haven't done a @@ -1688,10 +1712,12 @@ xfs_qm_quotacheck_dqadjust( } /* - * Adjust the timers since we just changed usages + * Set default limits, adjust timers (since we changed usages) */ - if (! XFS_IS_SUSER_DQUOT(dqp)) + if (! XFS_IS_SUSER_DQUOT(dqp)) { + xfs_qm_adjust_dqlimits(dqp->q_mount, &dqp->q_core); xfs_qm_adjust_dqtimers(dqp->q_mount, &dqp->q_core); + } dqp->dq_flags |= XFS_DQ_DIRTY; } --- linux-2.6.9-rc1/fs/xfs/quota/xfs_qm.h 2004-08-24 00:02:25.000000000 -0700 +++ 25/fs/xfs/quota/xfs_qm.h 2004-09-02 20:51:52.000000000 -0700 @@ -136,9 +136,14 @@ typedef struct xfs_quotainfo { xfs_qwarncnt_t qi_bwarnlimit; /* limit for num warnings */ xfs_qwarncnt_t qi_iwarnlimit; /* limit for num warnings */ mutex_t qi_quotaofflock;/* to serialize quotaoff */ - /* Some useful precalculated constants */ xfs_filblks_t qi_dqchunklen; /* # BBs in a chunk of dqs */ uint qi_dqperchunk; /* # ondisk dqs in above chunk */ + xfs_qcnt_t qi_bhardlimit; /* default data blk hard limit */ + xfs_qcnt_t qi_bsoftlimit; /* default data blk soft limit */ + xfs_qcnt_t qi_ihardlimit; /* default inode count hard limit */ + xfs_qcnt_t qi_isoftlimit; /* default inode count soft limit */ + xfs_qcnt_t qi_rtbhardlimit;/* default realtime blk hard limit */ + xfs_qcnt_t qi_rtbsoftlimit;/* default realtime blk soft limit */ } xfs_quotainfo_t; --- linux-2.6.9-rc1/fs/xfs/quota/xfs_qm_syscalls.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/fs/xfs/quota/xfs_qm_syscalls.c 2004-09-02 20:51:52.000000000 -0700 @@ -648,8 +648,11 @@ xfs_qm_scall_setqlim( if (hard == 0 || hard >= soft) { INT_SET(ddq->d_blk_hardlimit, ARCH_CONVERT, hard); INT_SET(ddq->d_blk_softlimit, ARCH_CONVERT, soft); - } - else { + if (id == 0) { + mp->m_quotainfo->qi_bhardlimit = hard; + mp->m_quotainfo->qi_bsoftlimit = soft; + } + } else { qdprintk("blkhard %Ld < blksoft %Ld\n", hard, soft); } hard = (newlim->d_fieldmask & FS_DQ_RTBHARD) ? @@ -661,40 +664,49 @@ xfs_qm_scall_setqlim( if (hard == 0 || hard >= soft) { INT_SET(ddq->d_rtb_hardlimit, ARCH_CONVERT, hard); INT_SET(ddq->d_rtb_softlimit, ARCH_CONVERT, soft); - } - else + if (id == 0) { + mp->m_quotainfo->qi_rtbhardlimit = hard; + mp->m_quotainfo->qi_rtbsoftlimit = soft; + } + } else { qdprintk("rtbhard %Ld < rtbsoft %Ld\n", hard, soft); + } hard = (newlim->d_fieldmask & FS_DQ_IHARD) ? (xfs_qcnt_t) newlim->d_ino_hardlimit : - INT_GET(ddq->d_ino_hardlimit, ARCH_CONVERT); + INT_GET(ddq->d_ino_hardlimit, ARCH_CONVERT); soft = (newlim->d_fieldmask & FS_DQ_ISOFT) ? (xfs_qcnt_t) newlim->d_ino_softlimit : - INT_GET(ddq->d_ino_softlimit, ARCH_CONVERT); + INT_GET(ddq->d_ino_softlimit, ARCH_CONVERT); if (hard == 0 || hard >= soft) { INT_SET(ddq->d_ino_hardlimit, ARCH_CONVERT, hard); INT_SET(ddq->d_ino_softlimit, ARCH_CONVERT, soft); - } - else + if (id == 0) { + mp->m_quotainfo->qi_ihardlimit = hard; + mp->m_quotainfo->qi_isoftlimit = soft; + } + } else { qdprintk("ihard %Ld < isoft %Ld\n", hard, soft); + } if (id == 0) { /* * Timelimits for the super user set the relative time * the other users can be over quota for this file system. - * If it is zero a default is used. + * If it is zero a default is used. Ditto for the default + * soft and hard limit values (already done, above). */ if (newlim->d_fieldmask & FS_DQ_BTIMER) { mp->m_quotainfo->qi_btimelimit = newlim->d_btimer; - INT_SET(dqp->q_core.d_btimer, ARCH_CONVERT, newlim->d_btimer); + INT_SET(ddq->d_btimer, ARCH_CONVERT, newlim->d_btimer); } if (newlim->d_fieldmask & FS_DQ_ITIMER) { mp->m_quotainfo->qi_itimelimit = newlim->d_itimer; - INT_SET(dqp->q_core.d_itimer, ARCH_CONVERT, newlim->d_itimer); + INT_SET(ddq->d_itimer, ARCH_CONVERT, newlim->d_itimer); } if (newlim->d_fieldmask & FS_DQ_RTBTIMER) { mp->m_quotainfo->qi_rtbtimelimit = newlim->d_rtbtimer; - INT_SET(dqp->q_core.d_rtbtimer, ARCH_CONVERT, newlim->d_rtbtimer); + INT_SET(ddq->d_rtbtimer, ARCH_CONVERT, newlim->d_rtbtimer); } } else /* if (XFS_IS_QUOTA_ENFORCED(mp)) */ { /* --- linux-2.6.9-rc1/fs/xfs/quota/xfs_trans_dquot.c 2004-08-24 00:02:22.000000000 -0700 +++ 25/fs/xfs/quota/xfs_trans_dquot.c 2004-09-02 20:51:52.000000000 -0700 @@ -452,9 +452,13 @@ xfs_trans_apply_dquot_deltas( INT_MOD(d->d_rtbcount, ARCH_CONVERT, (xfs_qcnt_t)totalrtbdelta); /* + * Get any default limits in use. * Start/reset the timer(s) if needed. */ - xfs_qm_adjust_dqtimers(tp->t_mountp, d); + if (!INT_ISZERO(d->d_id, ARCH_CONVERT)) { + xfs_qm_adjust_dqlimits(tp->t_mountp, d); + xfs_qm_adjust_dqtimers(tp->t_mountp, d); + } dqp->dq_flags |= XFS_DQ_DIRTY; /* @@ -625,6 +629,7 @@ xfs_trans_unreserve_and_mod_dquots( STATIC int xfs_trans_dqresv( xfs_trans_t *tp, + xfs_mount_t *mp, xfs_dquot_t *dqp, long nblks, long ninos, @@ -635,6 +640,7 @@ xfs_trans_dqresv( xfs_qcnt_t softlimit; time_t btimer; xfs_qcnt_t *resbcountp; + xfs_quotainfo_t *q = mp->m_quotainfo; if (! (flags & XFS_QMOPT_DQLOCK)) { xfs_dqlock(dqp); @@ -642,13 +648,21 @@ xfs_trans_dqresv( ASSERT(XFS_DQ_IS_LOCKED(dqp)); if (flags & XFS_TRANS_DQ_RES_BLKS) { hardlimit = INT_GET(dqp->q_core.d_blk_hardlimit, ARCH_CONVERT); + if (!hardlimit) + hardlimit = q->qi_bhardlimit; softlimit = INT_GET(dqp->q_core.d_blk_softlimit, ARCH_CONVERT); + if (!softlimit) + softlimit = q->qi_bsoftlimit; btimer = INT_GET(dqp->q_core.d_btimer, ARCH_CONVERT); resbcountp = &dqp->q_res_bcount; } else { ASSERT(flags & XFS_TRANS_DQ_RES_RTBLKS); hardlimit = INT_GET(dqp->q_core.d_rtb_hardlimit, ARCH_CONVERT); + if (!hardlimit) + hardlimit = q->qi_rtbhardlimit; softlimit = INT_GET(dqp->q_core.d_rtb_softlimit, ARCH_CONVERT); + if (!softlimit) + softlimit = q->qi_rtbsoftlimit; btimer = INT_GET(dqp->q_core.d_rtbtimer, ARCH_CONVERT); resbcountp = &dqp->q_res_rtbcount; } @@ -689,14 +703,18 @@ xfs_trans_dqresv( } } if (ninos > 0) { - if (INT_GET(dqp->q_core.d_ino_hardlimit, ARCH_CONVERT) > 0ULL && - INT_GET(dqp->q_core.d_icount, ARCH_CONVERT) >= - INT_GET(dqp->q_core.d_ino_hardlimit, ARCH_CONVERT)) { + hardlimit = INT_GET(dqp->q_core.d_ino_hardlimit, ARCH_CONVERT); + if (!hardlimit) + hardlimit = q->qi_ihardlimit; + softlimit = INT_GET(dqp->q_core.d_ino_softlimit, ARCH_CONVERT); + if (!softlimit) + softlimit = q->qi_isoftlimit; + if (hardlimit > 0ULL && + INT_GET(dqp->q_core.d_icount, ARCH_CONVERT) >= hardlimit) { error = EDQUOT; goto error_return; - } else if (INT_GET(dqp->q_core.d_ino_softlimit, ARCH_CONVERT) > 0ULL && - INT_GET(dqp->q_core.d_icount, ARCH_CONVERT) >= - INT_GET(dqp->q_core.d_ino_softlimit, ARCH_CONVERT)) { + } else if (softlimit > 0ULL && + INT_GET(dqp->q_core.d_icount, ARCH_CONVERT) >= softlimit) { /* * If timer or warnings has expired, * return EDQUOT @@ -786,19 +804,20 @@ xfs_trans_reserve_quota_bydquots( resvd = 0; if (udqp) { - if (xfs_trans_dqresv(tp, udqp, nblks, ninos, flags)) + if (xfs_trans_dqresv(tp, mp, udqp, nblks, ninos, flags)) return (EDQUOT); resvd = 1; } if (gdqp) { - if (xfs_trans_dqresv(tp, gdqp, nblks, ninos, flags)) { + if (xfs_trans_dqresv(tp, mp, gdqp, nblks, ninos, flags)) { /* * can't do it, so backout previous reservation */ if (resvd) { - xfs_trans_dqresv(tp, udqp, -nblks, -ninos, - flags); + flags |= XFS_QMOPT_FORCE_RES; + xfs_trans_dqresv(tp, mp, udqp, + -nblks, -ninos, flags); } return (EDQUOT); } --- linux-2.6.9-rc1/fs/xfs/xfs_bmap.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/fs/xfs/xfs_bmap.c 2004-09-02 20:51:52.000000000 -0700 @@ -3427,19 +3427,20 @@ xfs_bmap_do_search_extents( int high; /* high index of binary search */ int low; /* low index of binary search */ - /* Initialize the extent entry structure to catch access to - * uninitialized br_startblock field. - */ - - got.br_startoff = 0xffa5a5a5a5a5a5a5; - got.br_blockcount = 0xa55a5a5a5a5a5a5a; - got.br_state = XFS_EXT_INVALID; - - #if XFS_BIG_BLKNOS - got.br_startblock = 0xffffa5a5a5a5a5a5; - #else - got.br_startblock = 0xffffa5a5; - #endif + /* + * Initialize the extent entry structure to catch access to + * uninitialized br_startblock field. + */ + got.br_startoff = 0xffa5a5a5a5a5a5a5LL; + got.br_blockcount = 0xa55a5a5a5a5a5a5aLL; + got.br_state = XFS_EXT_INVALID; + +#if XFS_BIG_BLKNOS + got.br_startblock = 0xffffa5a5a5a5a5a5LL; +#else + got.br_startblock = 0xffffa5a5; +#endif + if (lastx != NULLEXTNUM && lastx < nextents) ep = base + lastx; else --- linux-2.6.9-rc1/fs/xfs/xfs_fsops.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/fs/xfs/xfs_fsops.c 2004-09-02 20:51:52.000000000 -0700 @@ -142,6 +142,7 @@ xfs_growfs_data_private( int dpct; int error; xfs_agnumber_t nagcount; + xfs_agnumber_t nagimax = 0; xfs_rfsblock_t nb, nb_mod; xfs_rfsblock_t new; xfs_rfsblock_t nfree; @@ -183,7 +184,7 @@ xfs_growfs_data_private( memset(&mp->m_perag[oagcount], 0, (nagcount - oagcount) * sizeof(xfs_perag_t)); mp->m_flags |= XFS_MOUNT_32BITINODES; - xfs_initialize_perag(mp, nagcount); + nagimax = xfs_initialize_perag(mp, nagcount); up_write(&mp->m_peraglock); } tp = xfs_trans_alloc(mp, XFS_TRANS_GROWFS); @@ -372,6 +373,9 @@ xfs_growfs_data_private( if (error) { return error; } + /* New allocation groups fully initialized, so update mount struct */ + if (nagimax) + mp->m_maxagi = nagimax; if (mp->m_sb.sb_imax_pct) { __uint64_t icount = mp->m_sb.sb_dblocks * mp->m_sb.sb_imax_pct; do_div(icount, 100); --- linux-2.6.9-rc1/fs/xfs/xfs_log_recover.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/fs/xfs/xfs_log_recover.c 2004-09-02 20:51:52.000000000 -0700 @@ -2047,12 +2047,11 @@ xfs_qm_dqcheck( errs++; } - if (! errs) { + if (! errs && !INT_ISZERO(ddq->d_id, ARCH_CONVERT)) { if (INT_GET(ddq->d_blk_softlimit, ARCH_CONVERT) && INT_GET(ddq->d_bcount, ARCH_CONVERT) >= INT_GET(ddq->d_blk_softlimit, ARCH_CONVERT)) { - if (INT_ISZERO(ddq->d_btimer, ARCH_CONVERT) && - !INT_ISZERO(ddq->d_id, ARCH_CONVERT)) { + if (INT_ISZERO(ddq->d_btimer, ARCH_CONVERT)) { if (flags & XFS_QMOPT_DOWARN) cmn_err(CE_ALERT, "%s : Dquot ID 0x%x (0x%p) " @@ -2065,8 +2064,7 @@ xfs_qm_dqcheck( if (INT_GET(ddq->d_ino_softlimit, ARCH_CONVERT) && INT_GET(ddq->d_icount, ARCH_CONVERT) >= INT_GET(ddq->d_ino_softlimit, ARCH_CONVERT)) { - if (INT_ISZERO(ddq->d_itimer, ARCH_CONVERT) && - !INT_ISZERO(ddq->d_id, ARCH_CONVERT)) { + if (INT_ISZERO(ddq->d_itimer, ARCH_CONVERT)) { if (flags & XFS_QMOPT_DOWARN) cmn_err(CE_ALERT, "%s : Dquot ID 0x%x (0x%p) " @@ -2076,6 +2074,19 @@ xfs_qm_dqcheck( errs++; } } + if (INT_GET(ddq->d_rtb_softlimit, ARCH_CONVERT) && + INT_GET(ddq->d_rtbcount, ARCH_CONVERT) >= + INT_GET(ddq->d_rtb_softlimit, ARCH_CONVERT)) { + if (INT_ISZERO(ddq->d_rtbtimer, ARCH_CONVERT)) { + if (flags & XFS_QMOPT_DOWARN) + cmn_err(CE_ALERT, + "%s : Dquot ID 0x%x (0x%p) " + "RTBLK TIMER NOT STARTED", + str, (int) + INT_GET(ddq->d_id, ARCH_CONVERT), ddq); + errs++; + } + } } if (!errs || !(flags & XFS_QMOPT_DQREPAIR)) --- linux-2.6.9-rc1/fs/xfs/xfs_mount.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/fs/xfs/xfs_mount.c 2004-09-02 20:51:52.000000000 -0700 @@ -318,10 +318,10 @@ xfs_mount_validate_sb( return 0; } -void -xfs_initialize_perag(xfs_mount_t *mp, int agcount) +xfs_agnumber_t +xfs_initialize_perag(xfs_mount_t *mp, xfs_agnumber_t agcount) { - int index, max_metadata; + xfs_agnumber_t index, max_metadata; xfs_perag_t *pag; xfs_agino_t agino; xfs_ino_t ino; @@ -377,7 +377,7 @@ xfs_initialize_perag(xfs_mount_t *mp, in pag->pagi_inodeok = 1; } } - mp->m_maxagi = index; + return index; } /* @@ -951,7 +951,7 @@ xfs_mountfs( mp->m_perag = kmem_zalloc(sbp->sb_agcount * sizeof(xfs_perag_t), KM_SLEEP); - xfs_initialize_perag(mp, sbp->sb_agcount); + mp->m_maxagi = xfs_initialize_perag(mp, sbp->sb_agcount); /* * log's mount-time initialization. Perform 1st part recovery if needed --- linux-2.6.9-rc1/fs/xfs/xfs_mount.h 2004-08-24 00:01:50.000000000 -0700 +++ 25/fs/xfs/xfs_mount.h 2004-09-02 20:51:52.000000000 -0700 @@ -551,7 +551,7 @@ extern int xfs_readsb(xfs_mount_t *mp); extern void xfs_freesb(xfs_mount_t *); extern void xfs_do_force_shutdown(bhv_desc_t *, int, char *, int); extern int xfs_syncsub(xfs_mount_t *, int, int, int *); -extern void xfs_initialize_perag(xfs_mount_t *, int); +extern xfs_agnumber_t xfs_initialize_perag(xfs_mount_t *, xfs_agnumber_t); extern void xfs_xlatesb(void *, struct xfs_sb *, int, xfs_arch_t, __int64_t); --- linux-2.6.9-rc1/fs/xfs/xfs_vnodeops.c 2004-08-24 00:03:49.000000000 -0700 +++ 25/fs/xfs/xfs_vnodeops.c 2004-09-02 20:51:52.000000000 -0700 @@ -283,7 +283,7 @@ xfs_getattr( /* * xfs_setattr */ -STATIC int +int xfs_setattr( bhv_desc_t *bdp, vattr_t *vap, @@ -305,6 +305,7 @@ xfs_setattr( int mandlock_before, mandlock_after; struct xfs_dquot *udqp, *gdqp, *olddquot1, *olddquot2; int file_owner; + int need_iolock = (flags & ATTR_DMI) == 0; vp = BHV_TO_VNODE(bdp); vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address); @@ -406,7 +407,8 @@ xfs_setattr( goto error_return; } } - lock_flags |= XFS_IOLOCK_EXCL; + if (need_iolock) + lock_flags |= XFS_IOLOCK_EXCL; } xfs_ilock(ip, lock_flags); @@ -678,7 +680,8 @@ xfs_setattr( XFS_TRANS_PERM_LOG_RES, XFS_ITRUNCATE_LOG_COUNT))) { xfs_trans_cancel(tp, 0); - xfs_iunlock(ip, XFS_IOLOCK_EXCL); + if (need_iolock) + xfs_iunlock(ip, XFS_IOLOCK_EXCL); return code; } commit_flags = XFS_TRANS_RELEASE_LOG_RES; @@ -4297,6 +4300,7 @@ xfs_free_file_space( int rt; xfs_fileoff_t startoffset_fsb; xfs_trans_t *tp; + int need_iolock = (attr_flags & ATTR_DMI) == 0; vn_trace_entry(XFS_ITOV(ip), __FUNCTION__, (inst_t *)__return_address); mp = ip->i_mount; @@ -4324,7 +4328,8 @@ xfs_free_file_space( return(error); } - xfs_ilock(ip, XFS_IOLOCK_EXCL); + if (need_iolock) + xfs_ilock(ip, XFS_IOLOCK_EXCL); rounding = MAX((__uint8_t)(1 << mp->m_sb.sb_blocklog), (__uint8_t)NBPP); ilen = len + (offset & (rounding - 1)); @@ -4342,10 +4347,8 @@ xfs_free_file_space( nimap = 1; error = xfs_bmapi(NULL, ip, startoffset_fsb, 1, 0, NULL, 0, &imap, &nimap, NULL); - if (error) { - xfs_iunlock(ip, XFS_IOLOCK_EXCL); - return error; - } + if (error) + goto out_unlock_iolock; ASSERT(nimap == 0 || nimap == 1); if (nimap && imap.br_startblock != HOLESTARTBLOCK) { xfs_daddr_t block; @@ -4359,10 +4362,8 @@ xfs_free_file_space( nimap = 1; error = xfs_bmapi(NULL, ip, endoffset_fsb - 1, 1, 0, NULL, 0, &imap, &nimap, NULL); - if (error) { - xfs_iunlock(ip, XFS_IOLOCK_EXCL); - return error; - } + if (error) + goto out_unlock_iolock; ASSERT(nimap == 0 || nimap == 1); if (nimap && imap.br_startblock != HOLESTARTBLOCK) { ASSERT(imap.br_startblock != DELAYSTARTBLOCK); @@ -4451,14 +4452,17 @@ xfs_free_file_space( xfs_iunlock(ip, XFS_ILOCK_EXCL); } - xfs_iunlock(ip, XFS_IOLOCK_EXCL); + out_unlock_iolock: + if (need_iolock) + xfs_iunlock(ip, XFS_IOLOCK_EXCL); return error; error0: xfs_bmap_cancel(&free_list); error1: xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT); - xfs_iunlock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); + xfs_iunlock(ip, need_iolock ? (XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL) : + XFS_ILOCK_EXCL); return error; } @@ -4615,20 +4619,21 @@ xfs_change_file_space( xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); xfs_trans_ihold(tp, ip); - ip->i_d.di_mode &= ~S_ISUID; - - /* - * Note that we don't have to worry about mandatory - * file locking being disabled here because we only - * clear the S_ISGID bit if the Group execute bit is - * on, but if it was on then mandatory locking wouldn't - * have been enabled. - */ - if (ip->i_d.di_mode & S_IXGRP) - ip->i_d.di_mode &= ~S_ISGID; + if ((attr_flags & ATTR_DMI) == 0) { + ip->i_d.di_mode &= ~S_ISUID; - xfs_ichgtime(ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); + /* + * Note that we don't have to worry about mandatory + * file locking being disabled here because we only + * clear the S_ISGID bit if the Group execute bit is + * on, but if it was on then mandatory locking wouldn't + * have been enabled. + */ + if (ip->i_d.di_mode & S_IXGRP) + ip->i_d.di_mode &= ~S_ISGID; + xfs_ichgtime(ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); + } if (setprealloc) ip->i_d.di_flags |= XFS_DIFLAG_PREALLOC; else if (clrprealloc) --- linux-2.6.9-rc1/include/acpi/acconfig.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/include/acpi/acconfig.h 2004-09-03 00:56:30.134579496 -0700 @@ -64,7 +64,7 @@ /* Version string */ -#define ACPI_CA_VERSION 0x20040715 +#define ACPI_CA_VERSION 0x20040816 /* * OS name, used for the _OS object. The _OS object is essentially obsolete, --- linux-2.6.9-rc1/include/acpi/acexcep.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/include/acpi/acexcep.h 2004-09-03 00:56:30.135579344 -0700 @@ -166,7 +166,7 @@ #define AE_AML_CIRCULAR_REFERENCE (acpi_status) (0x0020 | AE_CODE_AML) #define AE_AML_BAD_RESOURCE_LENGTH (acpi_status) (0x0021 | AE_CODE_AML) -#define AE_CODE_AML_MAX 0x0020 +#define AE_CODE_AML_MAX 0x0021 /* * Internal exceptions used for control --- linux-2.6.9-rc1/include/acpi/acglobal.h 2004-08-24 00:01:50.000000000 -0700 +++ 25/include/acpi/acglobal.h 2004-09-03 00:56:30.135579344 -0700 @@ -94,7 +94,7 @@ extern u32 * interpreter strictly follows the ACPI specification. Setting to TRUE * allows the interpreter to forgive certain bad AML constructs. */ -ACPI_EXTERN u8 ACPI_INIT_GLOBAL (acpi_gbl_enable_interpeter_slack, FALSE); +ACPI_EXTERN u8 ACPI_INIT_GLOBAL (acpi_gbl_enable_interpreter_slack, FALSE); /* * Automatically serialize ALL control methods? Default is FALSE, meaning --- linux-2.6.9-rc1/include/acpi/acmacros.h 2004-08-24 00:02:48.000000000 -0700 +++ 25/include/acpi/acmacros.h 2004-09-03 00:56:30.136579192 -0700 @@ -364,24 +364,6 @@ #define ACPI_IS_OCTAL_DIGIT(d) (((char)(d) >= '0') && ((char)(d) <= '7')) -/* Macros for GAS addressing */ - -#if ACPI_MACHINE_WIDTH != 16 - -#define ACPI_PCI_DEVICE(a) (u16) ((ACPI_HIDWORD ((a))) & 0x0000FFFF) -#define ACPI_PCI_FUNCTION(a) (u16) ((ACPI_LODWORD ((a))) >> 16) -#define ACPI_PCI_REGISTER(a) (u16) ((ACPI_LODWORD ((a))) & 0x0000FFFF) - -#else - -/* No support for GAS and PCI IDs in 16-bit mode */ - -#define ACPI_PCI_FUNCTION(a) (u16) ((a) & 0xFFFF0000) -#define ACPI_PCI_DEVICE(a) (u16) ((a) & 0x0000FFFF) -#define ACPI_PCI_REGISTER(a) (u16) ((a) & 0x0000FFFF) - -#endif - /* Bitfields within ACPI registers */ --- linux-2.6.9-rc1/include/acpi/acpi_drivers.h 2004-08-24 00:03:19.000000000 -0700 +++ 25/include/acpi/acpi_drivers.h 2004-09-03 00:56:30.137579040 -0700 @@ -106,59 +106,4 @@ int acpi_ec_ecdt_probe (void); int acpi_processor_set_thermal_limit(acpi_handle handle, int type); -/* -------------------------------------------------------------------------- - Debug Support - -------------------------------------------------------------------------- */ - -#define ACPI_DEBUG_RESTORE 0 -#define ACPI_DEBUG_LOW 1 -#define ACPI_DEBUG_MEDIUM 2 -#define ACPI_DEBUG_HIGH 3 -#define ACPI_DEBUG_DRIVERS 4 - -extern u32 acpi_dbg_level; -extern u32 acpi_dbg_layer; - -static inline void -acpi_set_debug ( - u32 flag) -{ - static u32 layer_save; - static u32 level_save; - - switch (flag) { - case ACPI_DEBUG_RESTORE: - acpi_dbg_layer = layer_save; - acpi_dbg_level = level_save; - break; - case ACPI_DEBUG_LOW: - case ACPI_DEBUG_MEDIUM: - case ACPI_DEBUG_HIGH: - case ACPI_DEBUG_DRIVERS: - layer_save = acpi_dbg_layer; - level_save = acpi_dbg_level; - break; - } - - switch (flag) { - case ACPI_DEBUG_LOW: - acpi_dbg_layer = ACPI_COMPONENT_DEFAULT | ACPI_ALL_DRIVERS; - acpi_dbg_level = ACPI_DEBUG_DEFAULT; - break; - case ACPI_DEBUG_MEDIUM: - acpi_dbg_layer = ACPI_COMPONENT_DEFAULT | ACPI_ALL_DRIVERS; - acpi_dbg_level = ACPI_LV_FUNCTIONS | ACPI_LV_ALL_EXCEPTIONS; - break; - case ACPI_DEBUG_HIGH: - acpi_dbg_layer = 0xFFFFFFFF; - acpi_dbg_level = 0xFFFFFFFF; - break; - case ACPI_DEBUG_DRIVERS: - acpi_dbg_layer = ACPI_ALL_DRIVERS; - acpi_dbg_level = 0xFFFFFFFF; - break; - } -} - - #endif /*__ACPI_DRIVERS_H__*/ --- linux-2.6.9-rc1/include/asm-alpha/a.out.h 2004-08-24 00:03:19.000000000 -0700 +++ 25/include/asm-alpha/a.out.h 2004-09-02 20:51:52.000000000 -0700 @@ -95,7 +95,7 @@ struct exec Worse, we have to notice the start address before swapping to use /sbin/loader, which of course is _not_ a TASO application. */ #define SET_AOUT_PERSONALITY(BFPM, EX) \ - set_personality (((BFPM->sh_bang || EX.ah.entry < 0x100000000 \ + set_personality (((BFPM->sh_bang || EX.ah.entry < 0x100000000L \ ? ADDR_LIMIT_32BIT : 0) | PER_OSF4)) #define STACK_TOP \ --- linux-2.6.9-rc1/include/asm-alpha/cache.h 2004-08-24 00:02:32.000000000 -0700 +++ 25/include/asm-alpha/cache.h 2004-09-02 20:51:52.000000000 -0700 @@ -20,6 +20,6 @@ #define L1_CACHE_ALIGN(x) (((x)+(L1_CACHE_BYTES-1))&~(L1_CACHE_BYTES-1)) #define SMP_CACHE_BYTES L1_CACHE_BYTES -#define L1_CACHE_SHIFT_MAX 6 /* largest L1 which this arch supports */ +#define L1_CACHE_SHIFT_MAX L1_CACHE_SHIFT #endif --- linux-2.6.9-rc1/include/asm-alpha/dma-mapping.h 2004-08-24 00:02:26.000000000 -0700 +++ 25/include/asm-alpha/dma-mapping.h 2004-09-02 20:51:52.000000000 -0700 @@ -1 +1,63 @@ -#include +#ifndef _ALPHA_DMA_MAPPING_H +#define _ALPHA_DMA_MAPPING_H + +#include + +#ifdef CONFIG_PCI + +#include + +#define dma_map_single(dev, va, size, dir) \ + pci_map_single(alpha_gendev_to_pci(dev), va, size, dir) +#define dma_unmap_single(dev, addr, size, dir) \ + pci_unmap_single(alpha_gendev_to_pci(dev), addr, size, dir) +#define dma_alloc_coherent(dev, size, addr, gfp) \ + pci_alloc_consistent(alpha_gendev_to_pci(dev), size, addr) +#define dma_free_coherent(dev, size, va, addr) \ + pci_free_consistent(alpha_gendev_to_pci(dev), size, va, addr) +#define dma_map_page(dev, page, off, size, dir) \ + pci_map_single(alpha_gendev_to_pci(dev), page, off, size, dir) +#define dma_unmap_page(dev, addr, size, dir) \ + pci_unmap_page(alpha_gendev_to_pci(dev), addr, size, dir) +#define dma_map_sg(dev, sg, nents, dir) \ + pci_map_sg(alpha_gendev_to_pci(dev), sg, nents, dir) +#define dma_unmap_sg(dev, sg, nents, dir) \ + pci_unmap_sg(alpha_gendev_to_pci(dev), sg, nents, dir) +#define dma_supported(dev, mask) \ + pci_dma_supported(alpha_gendev_to_pci(dev), mask) + +#else /* no PCI - no IOMMU. */ + +void *dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, int gfp); +int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, + enum dma_data_direction direction); + +#define dma_free_coherent(dev, size, va, addr) \ + free_pages((unsigned long)va, get_order(size)) +#define dma_supported(dev, mask) (mask < 0x00ffffffUL ? 0 : 1) +#define dma_map_single(dev, va, size, dir) virt_to_phys(va) +#define dma_map_page(dev, page, off, size, dir) (page_to_pa(page) + off) + +#define dma_unmap_single(dev, addr, size, dir) do { } while (0) +#define dma_unmap_page(dev, addr, size, dir) do { } while (0) +#define dma_unmap_sg(dev, sg, nents, dir) do { } while (0) + +#endif /* !CONFIG_PCI */ + +#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f) +#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h) +#define dma_is_consistent(dev) (1) + +int dma_set_mask(struct device *dev, u64 mask); + +#define dma_sync_single_for_cpu(dev, addr, size, dir) do { } while (0) +#define dma_sync_single_for_device(dev, addr, size, dir) do { } while (0) +#define dma_sync_single_range(dev, addr, off, size, dir) do { } while (0) +#define dma_sync_sg_for_cpu(dev, sg, nents, dir) do { } while (0) +#define dma_sync_sg_for_device(dev, sg, nents, dir) do { } while (0) +#define dma_cache_sync(va, size, dir) do { } while (0) + +#define dma_get_cache_alignment() L1_CACHE_BYTES + +#endif /* _ALPHA_DMA_MAPPING_H */ --- linux-2.6.9-rc1/include/asm-alpha/errno.h 2004-08-24 00:02:26.000000000 -0700 +++ 25/include/asm-alpha/errno.h 2004-09-03 00:56:54.492876472 -0700 @@ -110,5 +110,9 @@ #define ENOMEDIUM 129 /* No medium found */ #define EMEDIUMTYPE 130 /* Wrong medium type */ +#define ENOKEY 131 /* Required key not available */ +#define EKEYEXPIRED 132 /* Key has expired */ +#define EKEYREVOKED 133 /* Key has been revoked */ +#define EKEYREJECTED 134 /* Key was rejected by service */ #endif --- linux-2.6.9-rc1/include/asm-alpha/hardirq.h 2004-08-24 00:02:59.000000000 -0700 +++ 25/include/asm-alpha/hardirq.h 2004-09-03 00:56:41.016925128 -0700 @@ -39,20 +39,6 @@ typedef struct { #define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) #define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) -#define __MASK(x) ((1UL << (x))-1) - -#define PREEMPT_MASK (__MASK(PREEMPT_BITS) << PREEMPT_SHIFT) -#define HARDIRQ_MASK (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) -#define SOFTIRQ_MASK (__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) - -#define hardirq_count() (preempt_count() & HARDIRQ_MASK) -#define softirq_count() (preempt_count() & SOFTIRQ_MASK) -#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) - -#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) -#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) -#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) - /* * The hardirq mask has to be large enough to have * space for potentially nestable IRQ sources in the system @@ -64,28 +50,7 @@ typedef struct { #error HARDIRQ_BITS is too low! #endif -/* - * Are we doing bottom half or hardware interrupt processing? - * Are we in a softirq context? Interrupt context? - */ -#define in_irq() (hardirq_count()) -#define in_softirq() (softirq_count()) -#define in_interrupt() (irq_count()) - - -#define hardirq_trylock() (!in_interrupt()) -#define hardirq_endlock() do { } while (0) - #define irq_enter() (preempt_count() += HARDIRQ_OFFSET) - - -#ifdef CONFIG_PREEMPT -#define in_atomic() (preempt_count() != kernel_locked()) -# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) -#else -#define in_atomic() (preempt_count() != 0) -#define IRQ_EXIT_OFFSET HARDIRQ_OFFSET -# endif #define irq_exit() \ do { \ preempt_count() -= IRQ_EXIT_OFFSET; \ @@ -95,10 +60,4 @@ do { \ preempt_enable_no_resched(); \ } while (0) -#ifndef CONFIG_SMP -# define synchronize_irq(irq) barrier() -#else - extern void synchronize_irq(unsigned int irq); -#endif /* CONFIG_SMP */ - #endif /* _ALPHA_HARDIRQ_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-alpha/lockmeter.h 2004-09-03 00:56:42.827649856 -0700 @@ -0,0 +1,84 @@ +/* + * Written by John Hawkes (hawkes@sgi.com) + * Based on klstat.h by Jack Steiner (steiner@sgi.com) + * + * Modified by Peter Rival (frival@zk3.dec.com) + */ + +#ifndef _ALPHA_LOCKMETER_H +#define _ALPHA_LOCKMETER_H + +#include +#define CPU_CYCLE_FREQUENCY hwrpb->cycle_freq + +#define get_cycles64() get_cycles() + +#define THIS_CPU_NUMBER smp_processor_id() + +#include + +#define SPINLOCK_MAGIC_INIT /**/ + +/* + * Macros to cache and retrieve an index value inside of a lock + * these macros assume that there are less than 65536 simultaneous + * (read mode) holders of a rwlock. + * We also assume that the hash table has less than 32767 entries. + * the high order bit is used for write locking a rw_lock + * Note: although these defines and macros are the same as what is being used + * in include/asm-i386/lockmeter.h, they are present here to easily + * allow an alternate Alpha implementation. + */ +/* + * instrumented spinlock structure -- never used to allocate storage + * only used in macros below to overlay a spinlock_t + */ +typedef struct inst_spinlock_s { + /* remember, Alpha is little endian */ + unsigned short lock; + unsigned short index; +} inst_spinlock_t; +#define PUT_INDEX(lock_ptr,indexv) ((inst_spinlock_t *)(lock_ptr))->index = indexv +#define GET_INDEX(lock_ptr) ((inst_spinlock_t *)(lock_ptr))->index + +/* + * macros to cache and retrieve an index value in a read/write lock + * as well as the cpu where a reader busy period started + * we use the 2nd word (the debug word) for this, so require the + * debug word to be present + */ +/* + * instrumented rwlock structure -- never used to allocate storage + * only used in macros below to overlay a rwlock_t + */ +typedef struct inst_rwlock_s { + volatile int lock; + unsigned short index; + unsigned short cpu; +} inst_rwlock_t; +#define PUT_RWINDEX(rwlock_ptr,indexv) ((inst_rwlock_t *)(rwlock_ptr))->index = indexv +#define GET_RWINDEX(rwlock_ptr) ((inst_rwlock_t *)(rwlock_ptr))->index +#define PUT_RW_CPU(rwlock_ptr,cpuv) ((inst_rwlock_t *)(rwlock_ptr))->cpu = cpuv +#define GET_RW_CPU(rwlock_ptr) ((inst_rwlock_t *)(rwlock_ptr))->cpu + +/* + * return true if rwlock is write locked + * (note that other lock attempts can cause the lock value to be negative) + */ +#define RWLOCK_IS_WRITE_LOCKED(rwlock_ptr) (((inst_rwlock_t *)rwlock_ptr)->lock & 1) +#define IABS(x) ((x) > 0 ? (x) : -(x)) + +#define RWLOCK_READERS(rwlock_ptr) rwlock_readers(rwlock_ptr) +extern inline int rwlock_readers(rwlock_t *rwlock_ptr) +{ + int tmp = (int) ((inst_rwlock_t *)rwlock_ptr)->lock; + /* readers subtract 2, so we have to: */ + /* - andnot off a possible writer (bit 0) */ + /* - get the absolute value */ + /* - divide by 2 (right shift by one) */ + /* to find the number of readers */ + if (tmp == 0) return(0); + else return(IABS(tmp & ~1)>>1); +} + +#endif /* _ALPHA_LOCKMETER_H */ --- linux-2.6.9-rc1/include/asm-alpha/page.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/include/asm-alpha/page.h 2004-09-03 00:56:41.910789240 -0700 @@ -1,6 +1,7 @@ #ifndef _ALPHA_PAGE_H #define _ALPHA_PAGE_H +#include #include /* PAGE_SHIFT determines the page size */ @@ -106,6 +107,7 @@ extern __inline__ int get_order(unsigned #define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) +#define devmem_is_allowed(x) 1 #endif /* __KERNEL__ */ #endif /* _ALPHA_PAGE_H */ --- linux-2.6.9-rc1/include/asm-alpha/pci.h 2004-08-24 00:01:51.000000000 -0700 +++ 25/include/asm-alpha/pci.h 2004-09-02 20:51:52.000000000 -0700 @@ -169,7 +169,7 @@ pci_dma_sync_single_for_cpu(struct pci_d static inline void pci_dma_sync_single_for_device(struct pci_dev *dev, dma_addr_t dma_addr, - long size, int direction) + size_t size, int direction) { /* Nothing to do. */ } @@ -246,6 +246,8 @@ pcibios_add_platform_entries(struct pci_ { } +struct pci_dev *alpha_gendev_to_pci(struct device *dev); + #endif /* __KERNEL__ */ /* Values for the `which' argument to sys_pciconfig_iobase. */ --- linux-2.6.9-rc1/include/asm-alpha/pgtable.h 2004-08-24 00:02:25.000000000 -0700 +++ 25/include/asm-alpha/pgtable.h 2004-09-02 20:51:52.000000000 -0700 @@ -328,7 +328,7 @@ extern inline pte_t mk_swap_pte(unsigned #endif #define io_remap_page_range(vma, start, busaddr, size, prot) \ - remap_page_range(vma, start, virt_to_phys(__ioremap(busaddr, size)), size, prot) + remap_page_range(vma, start, virt_to_phys((void *)__ioremap(busaddr, size)), size, prot) #define pte_ERROR(e) \ printk("%s:%d: bad pte %016lx.\n", __FILE__, __LINE__, pte_val(e)) --- linux-2.6.9-rc1/include/asm-alpha/ptrace.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/include/asm-alpha/ptrace.h 2004-09-02 20:51:52.000000000 -0700 @@ -69,6 +69,7 @@ struct switch_stack { #ifdef __KERNEL__ #define user_mode(regs) (((regs)->ps & 8) != 0) #define instruction_pointer(regs) ((regs)->pc) +#define profile_pc(regs) instruction_pointer(regs) extern void show_regs(struct pt_regs *); #define alpha_task_regs(task) \ --- linux-2.6.9-rc1/include/asm-alpha/semaphore.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/include/asm-alpha/semaphore.h 2004-09-02 20:51:52.000000000 -0700 @@ -18,21 +18,13 @@ struct semaphore { atomic_t count; wait_queue_head_t wait; -#ifdef WAITQUEUE_DEBUG - long __magic; -#endif }; -#ifdef WAITQUEUE_DEBUG -# define __SEM_DEBUG_INIT(name) , (long)&(name).__magic -#else -# define __SEM_DEBUG_INIT(name) -#endif - -#define __SEMAPHORE_INITIALIZER(name,count) \ - { ATOMIC_INIT(count), \ - __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ - __SEM_DEBUG_INIT(name) } +#define __SEMAPHORE_INITIALIZER(name, n) \ +{ \ + .count = ATOMIC_INIT(n), \ + .wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait), \ +} #define __MUTEX_INITIALIZER(name) \ __SEMAPHORE_INITIALIZER(name,1) @@ -53,9 +45,6 @@ static inline void sema_init(struct sema atomic_set(&sem->count, val); init_waitqueue_head(&sem->wait); -#ifdef WAITQUEUE_DEBUG - sem->__magic = (long)&sem->__magic; -#endif } static inline void init_MUTEX (struct semaphore *sem) @@ -142,7 +131,7 @@ static inline void __up(struct semaphore __up_wakeup(sem); } -#if !defined(WAITQUEUE_DEBUG) && !defined(CONFIG_DEBUG_SEMAPHORE) +#if !defined(CONFIG_DEBUG_SEMAPHORE) extern inline void down(struct semaphore *sem) { __down(sem); --- linux-2.6.9-rc1/include/asm-alpha/spinlock.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/include/asm-alpha/spinlock.h 2004-09-03 00:56:42.827649856 -0700 @@ -6,6 +6,10 @@ #include #include +#ifdef CONFIG_LOCKMETER +#undef DEBUG_SPINLOCK +#undef DEBUG_RWLOCK +#endif /* * Simple spin lock operations. There are two variants, one clears IRQ's @@ -96,9 +100,18 @@ static inline int _raw_spin_trylock(spin typedef struct { volatile int write_lock:1, read_counter:31; +#ifdef CONFIG_LOCKMETER + /* required for LOCKMETER since all bits in lock are used */ + /* need this storage for CPU and lock INDEX ............. */ + unsigned magic; +#endif } /*__attribute__((aligned(32)))*/ rwlock_t; +#ifdef CONFIG_LOCKMETER +#define RW_LOCK_UNLOCKED (rwlock_t) { 0, 0, 0 } +#else #define RW_LOCK_UNLOCKED (rwlock_t) { 0, 0 } +#endif #define rwlock_init(x) do { *(x) = RW_LOCK_UNLOCKED; } while(0) #define rwlock_is_locked(x) (*(volatile int *)(x) != 0) @@ -170,4 +183,41 @@ static inline void _raw_read_unlock(rwlo : "m" (*lock) : "memory"); } +#ifdef CONFIG_LOCKMETER +static inline int _raw_write_trylock(rwlock_t *lock) +{ + long temp,result; + + __asm__ __volatile__( + " ldl_l %1,%0\n" + " mov $31,%2\n" + " bne %1,1f\n" + " or $31,1,%2\n" + " stl_c %2,%0\n" + "1: mb\n" + : "=m" (*(volatile int *)lock), "=&r" (temp), "=&r" (result) + : "m" (*(volatile int *)lock) + ); + + return (result); +} + +static inline int _raw_read_trylock(rwlock_t *lock) +{ + unsigned long temp,result; + + __asm__ __volatile__( + " ldl_l %1,%0\n" + " mov $31,%2\n" + " blbs %1,1f\n" + " subl %1,2,%2\n" + " stl_c %2,%0\n" + "1: mb\n" + : "=m" (*(volatile int *)lock), "=&r" (temp), "=&r" (result) + : "m" (*(volatile int *)lock) + ); + return (result); +} +#endif /* CONFIG_LOCKMETER */ + #endif /* _ALPHA_SPINLOCK_H */ --- linux-2.6.9-rc1/include/asm-alpha/thread_info.h 2004-08-24 00:03:32.000000000 -0700 +++ 25/include/asm-alpha/thread_info.h 2004-09-02 20:51:52.000000000 -0700 @@ -19,7 +19,7 @@ struct thread_info { struct exec_domain *exec_domain; /* execution domain */ mm_segment_t addr_limit; /* thread address space */ - long cpu; /* current CPU */ + unsigned cpu; /* current CPU */ int preempt_count; /* 0 => preemptable, <0 => BUG */ int bpt_nsaved; --- linux-2.6.9-rc1/include/asm-alpha/uaccess.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/include/asm-alpha/uaccess.h 2004-09-02 20:51:52.000000000 -0700 @@ -108,7 +108,6 @@ extern void __get_user_unknown(void); ({ \ long __gu_err = -EFAULT, __gu_val = 0; \ const __typeof__(*(ptr)) __user *__gu_addr = (ptr); \ - __chk_user_ptr(ptr); \ if (__access_ok((unsigned long)__gu_addr,size,segment)) { \ __gu_err = 0; \ switch (size) { \ @@ -223,7 +222,6 @@ extern void __put_user_unknown(void); ({ \ long __pu_err = -EFAULT; \ __typeof__(*(ptr)) __user *__pu_addr = (ptr); \ - __chk_user_ptr(ptr); \ if (__access_ok((unsigned long)__pu_addr,size,segment)) { \ __pu_err = 0; \ switch (size) { \ @@ -395,6 +393,10 @@ __copy_tofrom_user(void *to, const void __copy_tofrom_user_nocheck((to),(__force void *)(from),(n)); \ }) +#define __copy_to_user_inatomic __copy_to_user +#define __copy_from_user_inatomic __copy_from_user + + extern inline long copy_to_user(void __user *to, const void *from, long n) { --- linux-2.6.9-rc1/include/asm-arm26/hardirq.h 2004-08-24 00:02:32.000000000 -0700 +++ 25/include/asm-arm26/hardirq.h 2004-09-03 00:56:41.017924976 -0700 @@ -32,20 +32,6 @@ typedef struct { #define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) #define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) -#define __MASK(x) ((1UL << (x))-1) - -#define PREEMPT_MASK (__MASK(PREEMPT_BITS) << PREEMPT_SHIFT) -#define HARDIRQ_MASK (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) -#define SOFTIRQ_MASK (__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) - -#define hardirq_count() (preempt_count() & HARDIRQ_MASK) -#define softirq_count() (preempt_count() & SOFTIRQ_MASK) -#define irq_count() (preempt_count() & (HARDIRQ_MASK|SOFTIRQ_MASK)) - -#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) -#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) -#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) - /* * The hardirq mask has to be large enough to have space * for potentially all IRQ sources in the system nesting @@ -55,26 +41,8 @@ typedef struct { # error HARDIRQ_BITS is too low! #endif -/* - * Are we doing bottom half or hardware interrupt processing? - * Are we in a softirq context? Interrupt context? - */ -#define in_irq() (hardirq_count()) -#define in_softirq() (softirq_count()) -#define in_interrupt() (irq_count()) - -#define hardirq_trylock() (!in_interrupt()) -#define hardirq_endlock() do { } while (0) - #define irq_enter() (preempt_count() += HARDIRQ_OFFSET) -#ifdef CONFIG_PREEMPT -# define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != kernel_locked())# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) -#else -# define in_atomic() (preempt_count() != 0) -# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET -#endif - #ifndef CONFIG_SMP #define irq_exit() \ do { \ @@ -84,9 +52,6 @@ typedef struct { preempt_enable_no_resched(); \ } while (0) -#define synchronize_irq(irq) barrier() -#else -#error SMP not supported #endif #endif /* __ASM_HARDIRQ_H */ --- linux-2.6.9-rc1/include/asm-arm26/page.h 2004-08-24 00:03:18.000000000 -0700 +++ 25/include/asm-arm26/page.h 2004-09-03 00:56:41.910789240 -0700 @@ -110,6 +110,8 @@ static inline int get_order(unsigned lon #define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) +#define devmem_is_allowed(x) 1 + #endif /* __KERNEL__ */ #endif --- linux-2.6.9-rc1/include/asm-arm26/ptrace.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/include/asm-arm26/ptrace.h 2004-09-02 20:51:52.000000000 -0700 @@ -30,6 +30,7 @@ #define pc_pointer(v) ((v) & ~PCMASK) /* convert v to pc type address */ #define instruction_pointer(regs) (pc_pointer((regs)->ARM_pc)) /* get pc */ +#define profile_pc(regs) instruction_pointer(regs) /* this struct defines the way the registers are stored on the stack during a system call. */ --- linux-2.6.9-rc1/include/asm-arm26/semaphore.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/include/asm-arm26/semaphore.h 2004-09-02 20:51:52.000000000 -0700 @@ -16,22 +16,14 @@ struct semaphore { atomic_t count; int sleepers; wait_queue_head_t wait; -#if WAITQUEUE_DEBUG - long __magic; -#endif }; -#if WAITQUEUE_DEBUG -# define __SEM_DEBUG_INIT(name) \ - , (long)&(name).__magic -#else -# define __SEM_DEBUG_INIT(name) -#endif - -#define __SEMAPHORE_INIT(name,count) \ - { ATOMIC_INIT(count), 0, \ - __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ - __SEM_DEBUG_INIT(name) } +#define __SEMAPHORE_INIT(name, n) \ +{ \ + .count = ATOMIC_INIT(n), \ + .sleepers = 0, \ + .wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait), \ +} #define __MUTEX_INITIALIZER(name) \ __SEMAPHORE_INIT(name,1) @@ -47,9 +39,6 @@ static inline void sema_init(struct sema atomic_set(&sem->count, val); sem->sleepers = 0; init_waitqueue_head(&sem->wait); -#if WAITQUEUE_DEBUG - sem->__magic = (long)&sem->__magic; -#endif } static inline void init_MUTEX(struct semaphore *sem) @@ -81,9 +70,6 @@ extern void __up(struct semaphore * sem) */ static inline void down(struct semaphore * sem) { -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); __down_op(sem, __down_failed); } @@ -94,19 +80,12 @@ static inline void down(struct semaphore */ static inline int down_interruptible (struct semaphore * sem) { -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); return __down_op_ret(sem, __down_interruptible_failed); } static inline int down_trylock(struct semaphore *sem) { -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - return __down_op_ret(sem, __down_trylock_failed); } @@ -118,10 +97,6 @@ static inline int down_trylock(struct se */ static inline void up(struct semaphore * sem) { -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - __up_op(sem, __up_wakeup); } --- linux-2.6.9-rc1/include/asm-arm26/uaccess.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/include/asm-arm26/uaccess.h 2004-09-02 20:51:52.000000000 -0700 @@ -217,6 +217,9 @@ static __inline__ unsigned long __copy_t return n; } +#define __copy_to_user_inatomic __copy_to_user +#define __copy_from_user_inatomic __copy_from_user + static __inline__ unsigned long clear_user (void *to, unsigned long n) { if (access_ok(VERIFY_WRITE, to, n)) --- linux-2.6.9-rc1/include/asm-arm/apm.h 2004-08-24 00:03:38.000000000 -0700 +++ 25/include/asm-arm/apm.h 2004-09-02 20:51:52.000000000 -0700 @@ -15,7 +15,7 @@ #include -#ifdef CONFIG_APM +#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) #define APM_AC_OFFLINE 0 --- linux-2.6.9-rc1/include/asm-arm/arch-rpc/io.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/include/asm-arm/arch-rpc/io.h 2004-09-02 20:51:52.000000000 -0700 @@ -22,40 +22,40 @@ */ #define __arch_base_getb(b,o) \ ({ \ - unsigned int v, r = (b); \ + unsigned int __v, __r = (b); \ __asm__ __volatile__( \ "ldrb %0, [%1, %2]" \ - : "=r" (v) \ - : "r" (r), "Ir" (o)); \ - v; \ + : "=r" (__v) \ + : "r" (__r), "Ir" (o)); \ + __v; \ }) #define __arch_base_getl(b,o) \ ({ \ - unsigned int v, r = (b); \ + unsigned int __v, __r = (b); \ __asm__ __volatile__( \ "ldr %0, [%1, %2]" \ - : "=r" (v) \ - : "r" (r), "Ir" (o)); \ - v; \ + : "=r" (__v) \ + : "r" (__r), "Ir" (o)); \ + __v; \ }) #define __arch_base_putb(v,b,o) \ ({ \ - unsigned int r = (b); \ + unsigned int __r = (b); \ __asm__ __volatile__( \ "strb %0, [%1, %2]" \ : \ - : "r" (v), "r" (r), "Ir" (o)); \ + : "r" (v), "r" (__r), "Ir" (o));\ }) #define __arch_base_putl(v,b,o) \ ({ \ - unsigned int r = (b); \ + unsigned int __r = (b); \ __asm__ __volatile__( \ "str %0, [%1, %2]" \ : \ - : "r" (v), "r" (r), "Ir" (o)); \ + : "r" (v), "r" (__r), "Ir" (o));\ }) /* @@ -176,15 +176,15 @@ DECLARE_IO(int,l,"") #define __outwc(value,port) \ ({ \ - unsigned long v = value; \ + unsigned long __v = value; \ if (__PORT_PCIO((port))) \ __asm__ __volatile__( \ "str %0, [%1, %2] @ outwc" \ - : : "r" (v|v<<16), "r" (PCIO_BASE), "Jr" ((port) << 2)); \ + : : "r" (__v|__v<<16), "r" (PCIO_BASE), "Jr" ((port) << 2)); \ else \ __asm__ __volatile__( \ "str %0, [%1, %2] @ outwc" \ - : : "r" (v|v<<16), "r" (IO_BASE), "r" ((port) << 2)); \ + : : "r" (__v|__v<<16), "r" (IO_BASE), "r" ((port) << 2)); \ }) #define __inwc(port) \ @@ -203,15 +203,15 @@ DECLARE_IO(int,l,"") #define __outlc(value,port) \ ({ \ - unsigned long v = value; \ + unsigned long __v = value; \ if (__PORT_PCIO((port))) \ __asm__ __volatile__( \ "str %0, [%1, %2] @ outlc" \ - : : "r" (v), "r" (PCIO_BASE), "Jr" ((port) << 2)); \ + : : "r" (__v), "r" (PCIO_BASE), "Jr" ((port) << 2)); \ else \ __asm__ __volatile__( \ "str %0, [%1, %2] @ outlc" \ - : : "r" (v), "r" (IO_BASE), "r" ((port) << 2)); \ + : : "r" (__v), "r" (IO_BASE), "r" ((port) << 2)); \ }) #define __inlc(port) \ @@ -250,6 +250,6 @@ DECLARE_IO(int,l,"") /* * 1:1 mapping for ioremapped regions. */ -#define __mem_pci(x) (x) +#define __mem_pci(x) ((unsigned long)(x)) #endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-arm/arch-s3c2410/regs-dsc.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,183 @@ +/* linux/include/asm/hardware/s3c2410/regs-dsc.h + * + * Copyright (c) 2004 Simtec Electronics + * http://www.simtec.co.uk/products/SWLINUX/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * S3C2440 Signal Drive Strength Control + * + * Changelog: + * 11-Aug-2004 BJD Created file + * 25-Aug-2004 BJD Added the _SELECT_* defs for using with functions +*/ + + +#ifndef __ASM_ARCH_REGS_DSC_H +#define __ASM_ARCH_REGS_DSC_H "2440-dsc" + +#ifdef CONFIG_CPU_S3C2440 + +#define S3C2440_DSC0 S3C2410_GPIOREG(0xc0) +#define S3C2440_DSC1 S3C2410_GPIOREG(0xc4) + +#define S3C2440_SELECT_DSC0 (0) +#define S3C2440_SELECT_DSC1 (1<<31) + +#define S3C2440_DSC_GETSHIFT(x) ((x) & 31) + +#define S3C2440_DSC0_ENABLE (1<<31) + +#define S3C2440_DSC0_ADDR (S3C2440_SELECT_DSC0 | 8) +#define S3C2440_DSC0_ADDR_12mA (0<<8) +#define S3C2440_DSC0_ADDR_10mA (1<<8) +#define S3C2440_DSC0_ADDR_8mA (2<<8) +#define S3C2440_DSC0_ADDR_6mA (3<<8) +#define S3C2440_DSC0_ADDR_MASK (3<<8) + +/* D24..D31 */ +#define S3C2440_DSC0_DATA3 (S3C2440_SELECT_DSC0 | 6) +#define S3C2440_DSC0_DATA3_12mA (0<<6) +#define S3C2440_DSC0_DATA3_10mA (1<<6) +#define S3C2440_DSC0_DATA3_8mA (2<<6) +#define S3C2440_DSC0_DATA3_6mA (3<<6) +#define S3C2440_DSC0_DATA3_MASK (3<<6) + +/* D16..D23 */ +#define S3C2440_DSC0_DATA2 (S3C2440_SELECT_DSC0 | 4) +#define S3C2440_DSC0_DATA2_12mA (0<<4) +#define S3C2440_DSC0_DATA2_10mA (1<<4) +#define S3C2440_DSC0_DATA2_8mA (2<<4) +#define S3C2440_DSC0_DATA2_6mA (3<<4) +#define S3C2440_DSC0_DATA2_MASK (3<<4) + +/* D8..D15 */ +#define S3C2440_DSC0_DATA1 (S3C2440_SELECT_DSC0 | 2) +#define S3C2440_DSC0_DATA1_12mA (0<<2) +#define S3C2440_DSC0_DATA1_10mA (1<<2) +#define S3C2440_DSC0_DATA1_8mA (2<<2) +#define S3C2440_DSC0_DATA1_6mA (3<<2) +#define S3C2440_DSC0_DATA1_MASK (3<<2) + +/* D0..D7 */ +#define S3C2440_DSC0_DATA0 (S3C2440_SELECT_DSC0 | 0) +#define S3C2440_DSC0_DATA0_12mA (0<<0) +#define S3C2440_DSC0_DATA0_10mA (1<<0) +#define S3C2440_DSC0_DATA0_8mA (2<<0) +#define S3C2440_DSC0_DATA0_6mA (3<<0) +#define S3C2440_DSC0_DATA0_MASK (3<<0) + +#define S3C2440_DSC1_SCK0 (S3C2440_SELECT_DSC1 | 28) +#define S3C2440_DSC1_SCK0_12mA (0<<28) +#define S3C2440_DSC1_SCK0_10mA (1<<28) +#define S3C2440_DSC1_SCK0_8mA (2<<28) +#define S3C2440_DSC1_SCK0_6mA (3<<28) +#define S3C2440_DSC1_SCK0_MASK (3<<28) + +#define S3C2440_DSC1_SCK1 (S3C2440_SELECT_DSC1 | 26) +#define S3C2440_DSC1_SCK1_12mA (0<<26) +#define S3C2440_DSC1_SCK1_10mA (1<<26) +#define S3C2440_DSC1_SCK1_8mA (2<<26) +#define S3C2440_DSC1_SCK1_6mA (3<<26) +#define S3C2440_DSC1_SCK1_MASK (3<<26) + +#define S3C2440_DSC1_SCKE (S3C2440_SELECT_DSC1 | 24) +#define S3C2440_DSC1_SCKE_10mA (0<<24) +#define S3C2440_DSC1_SCKE_8mA (1<<24) +#define S3C2440_DSC1_SCKE_6mA (2<<24) +#define S3C2440_DSC1_SCKE_4mA (3<<24) +#define S3C2440_DSC1_SCKE_MASK (3<<24) + +/* SDRAM nRAS/nCAS */ +#define S3C2440_DSC1_SDR (S3C2440_SELECT_DSC1 | 22) +#define S3C2440_DSC1_SDR_10mA (0<<22) +#define S3C2440_DSC1_SDR_8mA (1<<22) +#define S3C2440_DSC1_SDR_6mA (2<<22) +#define S3C2440_DSC1_SDR_4mA (3<<22) +#define S3C2440_DSC1_SDR_MASK (3<<22) + +/* NAND Flash Controller */ +#define S3C2440_DSC1_NFC (S3C2440_SELECT_DSC1 | 20) +#define S3C2440_DSC1_NFC_10mA (0<<20) +#define S3C2440_DSC1_NFC_8mA (1<<20) +#define S3C2440_DSC1_NFC_6mA (2<<20) +#define S3C2440_DSC1_NFC_4mA (3<<20) +#define S3C2440_DSC1_NFC_MASK (3<<20) + +/* nBE[0..3] */ +#define S3C2440_DSC1_nBE (S3C2440_SELECT_DSC1 | 18) +#define S3C2440_DSC1_nBE_10mA (0<<18) +#define S3C2440_DSC1_nBE_8mA (1<<18) +#define S3C2440_DSC1_nBE_6mA (2<<18) +#define S3C2440_DSC1_nBE_4mA (3<<18) +#define S3C2440_DSC1_nBE_MASK (3<<18) + +#define S3C2440_DSC1_WOE (S3C2440_SELECT_DSC1 | 16) +#define S3C2440_DSC1_WOE_10mA (0<<16) +#define S3C2440_DSC1_WOE_8mA (1<<16) +#define S3C2440_DSC1_WOE_6mA (2<<16) +#define S3C2440_DSC1_WOE_4mA (3<<16) +#define S3C2440_DSC1_WOE_MASK (3<<16) + +#define S3C2440_DSC1_CS7 (S3C2440_SELECT_DSC1 | 14) +#define S3C2440_DSC1_CS7_10mA (0<<14) +#define S3C2440_DSC1_CS7_8mA (1<<14) +#define S3C2440_DSC1_CS7_6mA (2<<14) +#define S3C2440_DSC1_CS7_4mA (3<<14) +#define S3C2440_DSC1_CS7_MASK (3<<14) + +#define S3C2440_DSC1_CS6 (S3C2440_SELECT_DSC1 | 12) +#define S3C2440_DSC1_CS6_10mA (0<<12) +#define S3C2440_DSC1_CS6_8mA (1<<12) +#define S3C2440_DSC1_CS6_6mA (2<<12) +#define S3C2440_DSC1_CS6_4mA (3<<12) +#define S3C2440_DSC1_CS6_MASK (3<<12) + +#define S3C2440_DSC1_CS5 (S3C2440_SELECT_DSC1 | 10) +#define S3C2440_DSC1_CS5_10mA (0<<10) +#define S3C2440_DSC1_CS5_8mA (1<<10) +#define S3C2440_DSC1_CS5_6mA (2<<10) +#define S3C2440_DSC1_CS5_4mA (3<<10) +#define S3C2440_DSC1_CS5_MASK (3<<10) + +#define S3C2440_DSC1_CS4 (S3C2440_SELECT_DSC1 | 8) +#define S3C2440_DSC1_CS4_10mA (0<<8) +#define S3C2440_DSC1_CS4_8mA (1<<8) +#define S3C2440_DSC1_CS4_6mA (2<<8) +#define S3C2440_DSC1_CS4_4mA (3<<8) +#define S3C2440_DSC1_CS4_MASK (3<<8) + +#define S3C2440_DSC1_CS3 (S3C2440_SELECT_DSC1 | 6) +#define S3C2440_DSC1_CS3_10mA (0<<6) +#define S3C2440_DSC1_CS3_8mA (1<<6) +#define S3C2440_DSC1_CS3_6mA (2<<6) +#define S3C2440_DSC1_CS3_4mA (3<<6) +#define S3C2440_DSC1_CS3_MASK (3<<6) + +#define S3C2440_DSC1_CS2 (S3C2440_SELECT_DSC1 | 4) +#define S3C2440_DSC1_CS2_10mA (0<<4) +#define S3C2440_DSC1_CS2_8mA (1<<4) +#define S3C2440_DSC1_CS2_6mA (2<<4) +#define S3C2440_DSC1_CS2_4mA (3<<4) +#define S3C2440_DSC1_CS2_MASK (3<<4) + +#define S3C2440_DSC1_CS1 (S3C2440_SELECT_DSC1 | 2) +#define S3C2440_DSC1_CS1_10mA (0<<2) +#define S3C2440_DSC1_CS1_8mA (1<<2) +#define S3C2440_DSC1_CS1_6mA (2<<2) +#define S3C2440_DSC1_CS1_4mA (3<<2) +#define S3C2440_DSC1_CS1_MASK (3<<2) + +#define S3C2440_DSC1_CS0 (S3C2440_SELECT_DSC1 | 0 +#define S3C2440_DSC1_CS0_10mA (0<<0) +#define S3C2440_DSC1_CS0_8mA (1<<0) +#define S3C2440_DSC1_CS0_6mA (2<<0) +#define S3C2440_DSC1_CS0_4mA (3<<0) +#define S3C2440_DSC1_CS0_MASK (3<<0) + +#endif /* CONFIG_CPU_S3C2440 */ + +#endif /* __ASM_ARCH_REGS_DSC_H */ + --- linux-2.6.9-rc1/include/asm-arm/arch-s3c2410/regs-timer.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/include/asm-arm/arch-s3c2410/regs-timer.h 2004-09-02 20:51:52.000000000 -0700 @@ -29,6 +29,8 @@ #define S3C2410_TCFG_PRESCALER0_MASK (255<<0) #define S3C2410_TCFG_PRESCALER1_MASK (255<<8) #define S3C2410_TCFG_PRESCALER1_SHIFT (8) +#define S3C2410_TCFG_DEADZONE_MASK (255<<16) +#define S3C2410_TCFG_DEADZONE_SHIFT (16) #define S3C2410_TCFG1_MUX4_DIV2 (0<<16) #define S3C2410_TCFG1_MUX4_DIV4 (1<<16) --- linux-2.6.9-rc1/include/asm-arm/cacheflush.h 2004-08-24 00:03:32.000000000 -0700 +++ 25/include/asm-arm/cacheflush.h 2004-09-03 00:56:37.635439192 -0700 @@ -294,9 +294,9 @@ flush_cache_page(struct vm_area_struct * extern void flush_dcache_page(struct page *); #define flush_dcache_mmap_lock(mapping) \ - spin_lock_irq(&(mapping)->tree_lock) + write_lock_irq(&(mapping)->tree_lock) #define flush_dcache_mmap_unlock(mapping) \ - spin_unlock_irq(&(mapping)->tree_lock) + write_unlock_irq(&(mapping)->tree_lock) #define flush_icache_user_range(vma,page,addr,len) \ flush_dcache_page(page) --- linux-2.6.9-rc1/include/asm-arm/checksum.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/include/asm-arm/checksum.h 2004-09-02 20:51:52.000000000 -0700 @@ -37,7 +37,7 @@ unsigned int csum_partial_copy_nocheck(const char *src, char *dst, int len, int sum); unsigned int -csum_partial_copy_from_user(const char *src, char *dst, int len, int sum, int *err_ptr); +csum_partial_copy_from_user(const char __user *src, char *dst, int len, int sum, int *err_ptr); /* * This is the old (and unsafe) way of doing checksums, a warning message will --- linux-2.6.9-rc1/include/asm-arm/hardirq.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/include/asm-arm/hardirq.h 2004-09-03 00:56:41.018924824 -0700 @@ -22,31 +22,25 @@ typedef struct { * * - bits 0-7 are the preemption count (max depth: 256) * - bits 8-15 are the softirq count (max # of softirqs: 256) - * - bits 16-23 are the hardirq count (max # of hardirqs: 256) + * - bits 16-24 are the hardirq count (max # of hardirqs: 512) * - bit 26 is the PREEMPT_ACTIVE flag + * + * We optimize HARDIRQ_BITS for immediate constant, and only + * increase it if really needed. */ #define PREEMPT_BITS 8 #define SOFTIRQ_BITS 8 + +#if NR_IRQS > 256 +#define HARDIRQ_BITS 9 +#else #define HARDIRQ_BITS 8 +#endif #define PREEMPT_SHIFT 0 #define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) #define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) -#define __MASK(x) ((1UL << (x))-1) - -#define PREEMPT_MASK (__MASK(PREEMPT_BITS) << PREEMPT_SHIFT) -#define HARDIRQ_MASK (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) -#define SOFTIRQ_MASK (__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) - -#define hardirq_count() (preempt_count() & HARDIRQ_MASK) -#define softirq_count() (preempt_count() & SOFTIRQ_MASK) -#define irq_count() (preempt_count() & (HARDIRQ_MASK|SOFTIRQ_MASK)) - -#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) -#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) -#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) - /* * The hardirq mask has to be large enough to have space * for potentially all IRQ sources in the system nesting @@ -56,29 +50,9 @@ typedef struct { # error HARDIRQ_BITS is too low! #endif -/* - * Are we doing bottom half or hardware interrupt processing? - * Are we in a softirq context? Interrupt context? - */ -#define in_irq() (hardirq_count()) -#define in_softirq() (softirq_count()) -#define in_interrupt() (irq_count()) - -#define hardirq_trylock() (!in_interrupt()) -#define hardirq_endlock() do { } while (0) - #define irq_enter() (preempt_count() += HARDIRQ_OFFSET) -#ifdef CONFIG_PREEMPT -# define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != kernel_locked()) -# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) -#else -# define in_atomic() (preempt_count() != 0) -# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET -#endif - #ifndef CONFIG_SMP - extern asmlinkage void __do_softirq(void); #define irq_exit() \ @@ -88,10 +62,6 @@ extern asmlinkage void __do_softirq(void __do_softirq(); \ preempt_enable_no_resched(); \ } while (0) - -#define synchronize_irq(irq) barrier() -#else -extern void synchronize_irq(unsigned int irq); #endif #endif /* __ASM_HARDIRQ_H */ --- linux-2.6.9-rc1/include/asm-arm/memory.h 2004-08-24 00:03:32.000000000 -0700 +++ 25/include/asm-arm/memory.h 2004-09-02 20:51:52.000000000 -0700 @@ -13,6 +13,7 @@ #define __ASM_ARM_MEMORY_H #include +#include #include #ifndef TASK_SIZE @@ -186,8 +187,8 @@ static inline __deprecated void *bus_to_ */ #ifndef __arch_page_to_dma #define page_to_dma(dev, page) ((dma_addr_t)__virt_to_bus((unsigned long)page_address(page))) -#define dma_to_virt(dev, addr) (__bus_to_virt(addr)) -#define virt_to_dma(dev, addr) (__virt_to_bus((unsigned long)(addr))) +#define dma_to_virt(dev, addr) ((void *)__bus_to_virt(addr)) +#define virt_to_dma(dev, addr) ((dma_addr_t)__virt_to_bus((unsigned long)(addr))) #else #define page_to_dma(dev, page) (__arch_page_to_dma(dev, page)) #define dma_to_virt(dev, addr) (__arch_dma_to_virt(dev, addr)) --- linux-2.6.9-rc1/include/asm-arm/page.h 2004-08-24 00:02:20.000000000 -0700 +++ 25/include/asm-arm/page.h 2004-09-03 00:56:41.911789088 -0700 @@ -196,6 +196,8 @@ static inline int get_order(unsigned lon #define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) +#define devmem_is_allowed(x) 1 + #endif /* __KERNEL__ */ #endif --- linux-2.6.9-rc1/include/asm-arm/ptrace.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/include/asm-arm/ptrace.h 2004-09-02 20:51:52.000000000 -0700 @@ -130,6 +130,7 @@ static inline int valid_user_regs(struct #define instruction_pointer(regs) \ (pc_pointer((regs)->ARM_pc)) +#define profile_pc(regs) instruction_pointer(regs) #ifdef __KERNEL__ extern void show_regs(struct pt_regs *); --- linux-2.6.9-rc1/include/asm-arm/semaphore.h 2004-08-24 00:02:47.000000000 -0700 +++ 25/include/asm-arm/semaphore.h 2004-09-02 20:51:52.000000000 -0700 @@ -16,21 +16,12 @@ struct semaphore { atomic_t count; int sleepers; wait_queue_head_t wait; -#ifdef WAITQUEUE_DEBUG - long __magic; -#endif }; -#ifdef WAITQUEUE_DEBUG -# define __SEM_DEBUG_INIT(name) .__magic = (long)&(name).__magic -#else -# define __SEM_DEBUG_INIT(name) -#endif - -#define __SEMAPHORE_INIT(name,cnt) { \ +#define __SEMAPHORE_INIT(name, cnt) \ +{ \ .count = ATOMIC_INIT(cnt), \ .wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait), \ - __SEM_DEBUG_INIT(name) \ } #define __MUTEX_INITIALIZER(name) __SEMAPHORE_INIT(name,1) @@ -46,9 +37,6 @@ static inline void sema_init(struct sema atomic_set(&sem->count, val); sem->sleepers = 0; init_waitqueue_head(&sem->wait); -#ifdef WAITQUEUE_DEBUG - sem->__magic = (long)&sem->__magic; -#endif } static inline void init_MUTEX(struct semaphore *sem) @@ -85,9 +73,6 @@ extern void __up(struct semaphore * sem) */ static inline void down(struct semaphore * sem) { -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); __down_op(sem, __down_failed); } @@ -98,19 +83,12 @@ static inline void down(struct semaphore */ static inline int down_interruptible (struct semaphore * sem) { -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); return __down_op_ret(sem, __down_interruptible_failed); } static inline int down_trylock(struct semaphore *sem) { -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - return __down_op_ret(sem, __down_trylock_failed); } @@ -122,10 +100,6 @@ static inline int down_trylock(struct se */ static inline void up(struct semaphore * sem) { -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - __up_op(sem, __up_wakeup); } --- linux-2.6.9-rc1/include/asm-arm/uaccess.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/include/asm-arm/uaccess.h 2004-09-02 20:51:52.000000000 -0700 @@ -394,6 +394,9 @@ static inline unsigned long __copy_to_us return __arch_copy_to_user(to, from, n); } +#define __copy_to_user_inatomic __copy_to_user +#define __copy_from_user_inatomic __copy_from_user + static inline unsigned long clear_user (void __user *to, unsigned long n) { if (access_ok(VERIFY_WRITE, to, n)) --- linux-2.6.9-rc1/include/asm-cris/arch-v10/ptrace.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/include/asm-cris/arch-v10/ptrace.h 2004-09-02 20:51:52.000000000 -0700 @@ -109,6 +109,7 @@ struct switch_stack { /* bit 8 is user-mode flag */ #define user_mode(regs) (((regs)->dccr & 0x100) != 0) #define instruction_pointer(regs) ((regs)->irp) +#define profile_pc(regs) instruction_pointer(regs) extern void show_regs(struct pt_regs *); #endif --- linux-2.6.9-rc1/include/asm-cris/hardirq.h 2004-08-24 00:02:25.000000000 -0700 +++ 25/include/asm-cris/hardirq.h 2004-09-03 00:56:41.018924824 -0700 @@ -40,20 +40,6 @@ typedef struct { #define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) #define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) -#define __MASK(x) ((1UL << (x))-1) - -#define PREEMPT_MASK (__MASK(PREEMPT_BITS) << PREEMPT_SHIFT) -#define HARDIRQ_MASK (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) -#define SOFTIRQ_MASK (__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) - -#define hardirq_count() (preempt_count() & HARDIRQ_MASK) -#define softirq_count() (preempt_count() & SOFTIRQ_MASK) -#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) - -#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) -#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) -#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) - /* * The hardirq mask has to be large enough to have * space for potentially all IRQ sources in the system @@ -63,27 +49,7 @@ typedef struct { # error HARDIRQ_BITS is too low! #endif -/* - * Are we doing bottom half or hardware interrupt processing? - * Are we in a softirq context? Interrupt context? - */ -#define in_irq() (hardirq_count()) -#define in_softirq() (softirq_count()) -#define in_interrupt() (irq_count()) - - -#define hardirq_trylock() (!in_interrupt()) -#define hardirq_endlock() do { } while (0) - #define irq_enter() (preempt_count() += HARDIRQ_OFFSET) - -#ifdef CONFIG_PREEMPT -# define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != kernel_locked()) -# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) -#else -# define in_atomic() (preempt_count() != 0) -# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET -#endif #define irq_exit() \ do { \ preempt_count() -= IRQ_EXIT_OFFSET; \ @@ -92,6 +58,4 @@ do { \ preempt_enable_no_resched(); \ } while (0) -#define synchronize_irq(irq) barrier() - #endif /* __ASM_HARDIRQ_H */ --- linux-2.6.9-rc1/include/asm-cris/page.h 2004-08-24 00:02:25.000000000 -0700 +++ 25/include/asm-cris/page.h 2004-09-03 00:56:41.911789088 -0700 @@ -96,6 +96,8 @@ static inline int get_order(unsigned lon #define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) +#define devmem_is_allowed(x) 1 + #endif /* __KERNEL__ */ #endif /* _CRIS_PAGE_H */ --- linux-2.6.9-rc1/include/asm-cris/semaphore.h 2004-08-24 00:02:26.000000000 -0700 +++ 25/include/asm-cris/semaphore.h 2004-09-02 20:51:52.000000000 -0700 @@ -24,21 +24,14 @@ struct semaphore { atomic_t count; atomic_t waking; wait_queue_head_t wait; -#if WAITQUEUE_DEBUG - long __magic; -#endif }; -#if WAITQUEUE_DEBUG -# define __SEM_DEBUG_INIT(name) , (long)&(name).__magic -#else -# define __SEM_DEBUG_INIT(name) -#endif - -#define __SEMAPHORE_INITIALIZER(name,count) \ - { ATOMIC_INIT(count), ATOMIC_INIT(0), \ - __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ - __SEM_DEBUG_INIT(name) } +#define __SEMAPHORE_INITIALIZER(name, n) \ +{ \ + .count = ATOMIC_INIT(n), \ + .waking = ATOMIC_INIT(0), \ + .wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ +} #define __MUTEX_INITIALIZER(name) \ __SEMAPHORE_INITIALIZER(name,1) @@ -76,9 +69,6 @@ extern inline void down(struct semaphore unsigned long flags; int failed; -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); /* atomically decrement the semaphores count, and if its negative, we wait */ @@ -102,9 +92,6 @@ extern inline int down_interruptible(str unsigned long flags; int failed; -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); /* atomically decrement the semaphores count, and if its negative, we wait */ @@ -122,10 +109,6 @@ extern inline int down_trylock(struct se unsigned long flags; int failed; -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - local_save_flags(flags); local_irq_disable(); failed = --(sem->count.counter) < 0; @@ -146,10 +129,6 @@ extern inline void up(struct semaphore * unsigned long flags; int wakeup; -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - /* atomically increment the semaphores count, and if it was negative, we wake people */ local_save_flags(flags); local_irq_disable(); --- linux-2.6.9-rc1/include/asm-cris/uaccess.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/include/asm-cris/uaccess.h 2004-09-02 20:51:52.000000000 -0700 @@ -434,6 +434,8 @@ __generic_clear_user_nocheck(void *to, u #define __copy_to_user(to,from,n) __generic_copy_to_user_nocheck((to),(from),(n)) #define __copy_from_user(to,from,n) __generic_copy_from_user_nocheck((to),(from),(n)) +#define __copy_to_user_inatomic __copy_to_user +#define __copy_from_user_inatomic __copy_from_user #define __clear_user(to,n) __generic_clear_user_nocheck((to),(n)) #define strlen_user(str) strnlen_user((str), 0x7ffffffe) --- linux-2.6.9-rc1/include/asm-generic/errno.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/include/asm-generic/errno.h 2004-09-03 00:56:54.362896232 -0700 @@ -96,5 +96,9 @@ #define ENOMEDIUM 123 /* No medium found */ #define EMEDIUMTYPE 124 /* Wrong medium type */ +#define ENOKEY 125 /* Required key not available */ +#define EKEYEXPIRED 126 /* Key has expired */ +#define EKEYREVOKED 127 /* Key has been revoked */ +#define EKEYREJECTED 128 /* Key was rejected by service */ #endif --- linux-2.6.9-rc1/include/asm-generic/local.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/include/asm-generic/local.h 2004-09-03 00:56:41.018924824 -0700 @@ -3,8 +3,8 @@ #include #include +#include #include -#include /* An unsigned long type for operations which are atomic for a single * CPU. Usually used in combination with per-cpu variables. */ --- linux-2.6.9-rc1/include/asm-generic/siginfo.h 2004-08-24 00:02:25.000000000 -0700 +++ 25/include/asm-generic/siginfo.h 2004-09-02 20:51:52.000000000 -0700 @@ -3,6 +3,7 @@ #include #include +#include typedef union sigval { int sival_int; @@ -74,6 +75,7 @@ typedef struct siginfo { int _status; /* exit code */ clock_t _utime; clock_t _stime; + struct rusage _rusage; } _sigchld; /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ @@ -105,6 +107,7 @@ typedef struct siginfo { #define si_status _sifields._sigchld._status #define si_utime _sifields._sigchld._utime #define si_stime _sifields._sigchld._stime +#define si_rusage _sifields._sigchld._rusage #define si_value _sifields._rt._sigval #define si_int _sifields._rt._sigval.sival_int #define si_ptr _sifields._rt._sigval.sival_ptr --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-generic/uaccess.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,26 @@ +#ifndef _ASM_GENERIC_UACCESS_H_ +#define _ASM_GENERIC_UACCESS_H_ + +/* + * This macro should be used instead of __get_user() when accessing + * values at locations that are not known to be aligned. + */ +#define __get_user_unaligned(x, ptr) \ +({ \ + __typeof__ (*(ptr)) __x; \ + __copy_from_user(&__x, (ptr), sizeof(*(ptr))) ? -EFAULT : 0; \ + (x) = __x; \ +}) + + +/* + * This macro should be used instead of __put_user() when accessing + * values at locations that are not known to be aligned. + */ +#define __put_user_unaligned(x, ptr) \ +({ \ + __typeof__ (*(ptr)) __x = (x); \ + __copy_to_user((ptr), &__x, sizeof(*(ptr))) ? -EFAULT : 0; \ +}) + +#endif /* _ASM_GENERIC_UACCESS_H */ --- linux-2.6.9-rc1/include/asm-generic/vmlinux.lds.h 2004-08-24 00:01:52.000000000 -0700 +++ 25/include/asm-generic/vmlinux.lds.h 2004-09-03 00:56:53.808980440 -0700 @@ -24,6 +24,9 @@ VMLINUX_SYMBOL(__start_pci_fixups_final) = .; \ *(.pci_fixup_final) \ VMLINUX_SYMBOL(__end_pci_fixups_final) = .; \ + VMLINUX_SYMBOL(__start_pci_fixups_enable) = .; \ + *(.pci_fixup_enable) \ + VMLINUX_SYMBOL(__end_pci_fixups_enable) = .; \ } \ \ /* Kernel symbol table: Normal symbols */ \ @@ -57,6 +60,13 @@ /* Kernel symbol table: strings */ \ __ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) { \ *(__ksymtab_strings) \ + } \ + \ + /* Built-in module parameters. */ \ + __param : AT(ADDR(__param) - LOAD_OFFSET) { \ + VMLINUX_SYMBOL(__start___param) = .; \ + *(__param) \ + VMLINUX_SYMBOL(__stop___param) = .; \ } #define SECURITY_INIT \ --- linux-2.6.9-rc1/include/asm-h8300/hardirq.h 2004-08-24 00:02:48.000000000 -0700 +++ 25/include/asm-h8300/hardirq.h 2004-09-03 00:56:41.019924672 -0700 @@ -38,20 +38,6 @@ typedef struct { #define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) #define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) -#define __MASK(x) ((1UL << (x))-1) - -#define PREEMPT_MASK (__MASK(PREEMPT_BITS) << PREEMPT_SHIFT) -#define HARDIRQ_MASK (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) -#define SOFTIRQ_MASK (__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) - -#define hardirq_count() (preempt_count() & HARDIRQ_MASK) -#define softirq_count() (preempt_count() & SOFTIRQ_MASK) -#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) - -#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) -#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) -#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) - /* * The hardirq mask has to be large enough to have * space for potentially all IRQ sources in the system @@ -61,27 +47,7 @@ typedef struct { # error HARDIRQ_BITS is too low! #endif -/* - * Are we doing bottom half or hardware interrupt processing? - * Are we in a softirq context? Interrupt context? - */ -#define in_irq() (hardirq_count()) -#define in_softirq() (softirq_count()) -#define in_interrupt() (irq_count()) - -#define hardirq_trylock() (!in_interrupt()) -#define hardirq_endlock() do { } while (0) - #define irq_enter() (preempt_count() += HARDIRQ_OFFSET) - -#ifdef CONFIG_PREEMPT -# define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != kernel_locked()) -# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) -#else -# define in_atomic() (preempt_count() != 0) -# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET -#endif - #define irq_exit() \ do { \ preempt_count() -= IRQ_EXIT_OFFSET; \ @@ -90,10 +56,4 @@ do { \ preempt_enable_no_resched(); \ } while (0) -#ifndef CONFIG_SMP -# define synchronize_irq(irq) barrier() -#else -# error h8300 SMP is not available -#endif /* CONFIG_SMP */ - #endif --- linux-2.6.9-rc1/include/asm-h8300/page.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/include/asm-h8300/page.h 2004-09-03 00:56:41.911789088 -0700 @@ -96,6 +96,8 @@ extern unsigned long memory_end; #endif /* __ASSEMBLY__ */ +#define devmem_is_allowed(x) 1 + #endif /* __KERNEL__ */ #endif /* _H8300_PAGE_H */ --- linux-2.6.9-rc1/include/asm-h8300/ptrace.h 2004-08-24 00:03:32.000000000 -0700 +++ 25/include/asm-h8300/ptrace.h 2004-09-02 20:51:52.000000000 -0700 @@ -57,6 +57,7 @@ struct pt_regs { #define user_mode(regs) (!((regs)->ccr & PS_S)) #define instruction_pointer(regs) ((regs)->pc) +#define profile_pc(regs) instruction_pointer(regs) extern void show_regs(struct pt_regs *); #endif /* __KERNEL__ */ #endif /* __ASSEMBLY__ */ --- linux-2.6.9-rc1/include/asm-h8300/semaphore.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/include/asm-h8300/semaphore.h 2004-09-02 20:51:52.000000000 -0700 @@ -26,21 +26,14 @@ struct semaphore { atomic_t count; int sleepers; wait_queue_head_t wait; -#if WAITQUEUE_DEBUG - long __magic; -#endif }; -#if WAITQUEUE_DEBUG -# define __SEM_DEBUG_INIT(name) \ - , (long)&(name).__magic -#else -# define __SEM_DEBUG_INIT(name) -#endif - -#define __SEMAPHORE_INITIALIZER(name,count) \ -{ ATOMIC_INIT(count), 0, __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ - __SEM_DEBUG_INIT(name) } +#define __SEMAPHORE_INITIALIZER(name, n) \ +{ \ + .count = ATOMIC_INIT(n), \ + .sleepers = 0, \ + .wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ +} #define __MUTEX_INITIALIZER(name) \ __SEMAPHORE_INITIALIZER(name,1) @@ -87,9 +80,6 @@ static inline void down(struct semaphore { register atomic_t *count asm("er0"); -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); count = &(sem->count); @@ -116,9 +106,6 @@ static inline int down_interruptible(str { register atomic_t *count asm("er0"); -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); count = &(sem->count); @@ -147,10 +134,6 @@ static inline int down_trylock(struct se { register atomic_t *count asm("er0"); -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - count = &(sem->count); __asm__ __volatile__( "stc ccr,r3l\n\t" @@ -187,10 +170,6 @@ static inline void up(struct semaphore * { register atomic_t *count asm("er0"); -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - count = &(sem->count); __asm__ __volatile__( "stc ccr,r3l\n\t" --- linux-2.6.9-rc1/include/asm-h8300/uaccess.h 2004-08-24 00:02:25.000000000 -0700 +++ 25/include/asm-h8300/uaccess.h 2004-09-02 20:51:52.000000000 -0700 @@ -123,6 +123,8 @@ extern int __get_user_bad(void); #define __copy_from_user(to, from, n) copy_from_user(to, from, n) #define __copy_to_user(to, from, n) copy_to_user(to, from, n) +#define __copy_to_user_inatomic __copy_to_user +#define __copy_from_user_inatomic __copy_from_user #define copy_to_user_ret(to,from,n,retval) ({ if (copy_to_user(to,from,n)) return retval; }) --- linux-2.6.9-rc1/include/asm-i386/apicdef.h 2004-08-24 00:02:48.000000000 -0700 +++ 25/include/asm-i386/apicdef.h 2004-09-03 00:56:57.969347968 -0700 @@ -86,6 +86,7 @@ #define APIC_LVT_REMOTE_IRR (1<<14) #define APIC_INPUT_POLARITY (1<<13) #define APIC_SEND_PENDING (1<<12) +#define APIC_MODE_MASK 0x700 #define GET_APIC_DELIVERY_MODE(x) (((x)>>8)&0x7) #define SET_APIC_DELIVERY_MODE(x,y) (((x)&~0x700)|((y)<<8)) #define APIC_MODE_FIXED 0x0 --- linux-2.6.9-rc1/include/asm-i386/bitops.h 2004-08-24 00:02:59.000000000 -0700 +++ 25/include/asm-i386/bitops.h 2004-09-02 20:51:52.000000000 -0700 @@ -31,6 +31,11 @@ * * This function is atomic and may not be reordered. See __set_bit() * if you do not require the atomic guarantees. + * + * Note: there are no guarantees that this function will not be reordered + * on non x86 architectures, so if you are writting portable code, + * make sure not to rely on its reordering guarantees. + * * Note that @nr may be almost arbitrarily large; this function is not * restricted to acting on a single-word quantity. */ @@ -109,7 +114,8 @@ static inline void __change_bit(int nr, * @nr: Bit to change * @addr: Address to start counting from * - * change_bit() is atomic and may not be reordered. + * change_bit() is atomic and may not be reordered. It may be + * reordered on other architectures than x86. * Note that @nr may be almost arbitrarily large; this function is not * restricted to acting on a single-word quantity. */ @@ -127,6 +133,7 @@ static inline void change_bit(int nr, vo * @addr: Address to count from * * This operation is atomic and cannot be reordered. + * It may be reordered on other architectures than x86. * It also implies a memory barrier. */ static inline int test_and_set_bit(int nr, volatile unsigned long * addr) @@ -165,7 +172,8 @@ static inline int __test_and_set_bit(int * @nr: Bit to clear * @addr: Address to count from * - * This operation is atomic and cannot be reordered. + * This operation is atomic and cannot be reordered. + * It can be reorderdered on other architectures other than x86. * It also implies a memory barrier. */ static inline int test_and_clear_bit(int nr, volatile unsigned long * addr) --- linux-2.6.9-rc1/include/asm-i386/bugs.h 2004-08-24 00:02:16.000000000 -0700 +++ 25/include/asm-i386/bugs.h 2004-09-03 00:56:35.781721000 -0700 @@ -1,11 +1,11 @@ /* * include/asm-i386/bugs.h * - * Copyright (C) 1994 Linus Torvalds + * Copyright (C) 1994 Linus Torvalds * * Cyrix stuff, June 1998 by: * - Rafael R. Reilova (moved everything from head.S), - * + * * - Channing Corn (tests & fixes), * - Andrew D. Balsa (code cleanup). * @@ -25,7 +25,20 @@ #include #include #include - +#ifdef CONFIG_KGDB +/* + * Provied the command line "gdb" initial break + */ +int __init kgdb_initial_break(char * str) +{ + if (*str == '\0'){ + breakpoint(); + return 1; + } + return 0; +} +__setup("gdb",kgdb_initial_break); +#endif static int __init no_halt(char *s) { boot_cpu_data.hlt_works_ok = 0; @@ -140,7 +153,7 @@ static void __init check_popad(void) : "ecx", "edi" ); /* If this fails, it means that any user program may lock the CPU hard. Too bad. */ if (res != 12345678) printk( "Buggy.\n" ); - else printk( "OK.\n" ); + else printk( "OK.\n" ); #endif } --- linux-2.6.9-rc1/include/asm-i386/checksum.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/include/asm-i386/checksum.h 2004-09-02 20:51:52.000000000 -0700 @@ -43,10 +43,12 @@ unsigned int csum_partial_copy_nocheck ( } static __inline__ -unsigned int csum_partial_copy_from_user ( const char __user *src, char *dst, +unsigned int csum_partial_copy_from_user(const char __user *src, char *dst, int len, int sum, int *err_ptr) { - return csum_partial_copy_generic ( (__force char *)src, dst, len, sum, err_ptr, NULL); + might_sleep(); + return csum_partial_copy_generic((__force char *)src, dst, + len, sum, err_ptr, NULL); } /* @@ -177,6 +179,7 @@ static __inline__ unsigned int csum_and_ int len, int sum, int *err_ptr) { + might_sleep(); if (access_ok(VERIFY_WRITE, dst, len)) return csum_partial_copy_generic(src, (__force char *)dst, len, sum, NULL, err_ptr); --- linux-2.6.9-rc1/include/asm-i386/cpufeature.h 2004-08-24 00:03:19.000000000 -0700 +++ 25/include/asm-i386/cpufeature.h 2004-09-02 20:51:52.000000000 -0700 @@ -71,9 +71,13 @@ #define X86_FEATURE_P4 (3*32+ 7) /* P4 */ /* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */ -#define X86_FEATURE_EST (4*32+ 7) /* Enhanced SpeedStep */ +#define X86_FEATURE_XMM3 (4*32+ 0) /* Streaming SIMD Extensions-3 */ #define X86_FEATURE_MWAIT (4*32+ 3) /* Monitor/Mwait support */ - +#define X86_FEATURE_DSCPL (4*32+ 4) /* CPL Qualified Debug Store */ +#define X86_FEATURE_EST (4*32+ 7) /* Enhanced SpeedStep */ +#define X86_FEATURE_TM2 (4*32+ 8) /* Thermal Monitor 2 */ +#define X86_FEATURE_CID (4*32+10) /* Context ID */ +#define X86_FEATURE_XTPR (4*32+14) /* Send Task Priority Messages */ /* VIA/Cyrix/Centaur-defined CPU features, CPUID level 0xC0000001, word 5 */ #define X86_FEATURE_XSTORE (5*32+ 2) /* on-CPU RNG present (xstore insn) */ @@ -92,13 +96,14 @@ #define cpu_has_tsc boot_cpu_has(X86_FEATURE_TSC) #define cpu_has_pae boot_cpu_has(X86_FEATURE_PAE) #define cpu_has_pge boot_cpu_has(X86_FEATURE_PGE) -#define cpu_has_sse2 boot_cpu_has(X86_FEATURE_XMM2) #define cpu_has_apic boot_cpu_has(X86_FEATURE_APIC) #define cpu_has_sep boot_cpu_has(X86_FEATURE_SEP) #define cpu_has_mtrr boot_cpu_has(X86_FEATURE_MTRR) #define cpu_has_mmx boot_cpu_has(X86_FEATURE_MMX) #define cpu_has_fxsr boot_cpu_has(X86_FEATURE_FXSR) #define cpu_has_xmm boot_cpu_has(X86_FEATURE_XMM) +#define cpu_has_xmm2 boot_cpu_has(X86_FEATURE_XMM2) +#define cpu_has_xmm3 boot_cpu_has(X86_FEATURE_XMM3) #define cpu_has_ht boot_cpu_has(X86_FEATURE_HT) #define cpu_has_mp boot_cpu_has(X86_FEATURE_MP) #define cpu_has_nx boot_cpu_has(X86_FEATURE_NX) @@ -106,7 +111,9 @@ #define cpu_has_cyrix_arr boot_cpu_has(X86_FEATURE_CYRIX_ARR) #define cpu_has_centaur_mcr boot_cpu_has(X86_FEATURE_CENTAUR_MCR) #define cpu_has_xstore boot_cpu_has(X86_FEATURE_XSTORE) +#define cpu_has_xstore_enabled boot_cpu_has(X86_FEATURE_XSTORE_EN) #define cpu_has_xcrypt boot_cpu_has(X86_FEATURE_XCRYPT) +#define cpu_has_xcrypt_enabled boot_cpu_has(X86_FEATURE_XCRYPT_EN) #endif /* __ASM_I386_CPUFEATURE_H */ --- linux-2.6.9-rc1/include/asm-i386/cpu.h 2004-08-24 00:03:14.000000000 -0700 +++ 25/include/asm-i386/cpu.h 2004-09-03 00:56:52.585166488 -0700 @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include @@ -25,4 +27,5 @@ static inline int arch_register_cpu(int return register_cpu(&cpu_devices[num].cpu, num, parent); } +DECLARE_PER_CPU(int, cpu_state); #endif /* _ASM_I386_CPU_H_ */ --- linux-2.6.9-rc1/include/asm-i386/dma-mapping.h 2004-08-24 00:03:49.000000000 -0700 +++ 25/include/asm-i386/dma-mapping.h 2004-09-02 20:51:52.000000000 -0700 @@ -163,4 +163,16 @@ dma_cache_sync(void *vaddr, size_t size, flush_write_buffers(); } +#define ARCH_HAS_DMA_DECLARE_COHERENT_MEMORY +extern int +dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, + dma_addr_t device_addr, size_t size, int flags); + +extern void +dma_release_declared_memory(struct device *dev); + +extern void * +dma_mark_declared_memory_occupied(struct device *dev, + dma_addr_t device_addr, size_t size); + #endif --- linux-2.6.9-rc1/include/asm-i386/fixmap.h 2004-08-24 00:01:53.000000000 -0700 +++ 25/include/asm-i386/fixmap.h 2004-09-02 20:51:52.000000000 -0700 @@ -14,6 +14,15 @@ #define _ASM_FIXMAP_H #include + +/* used by vmalloc.c, vsyscall.lds.S. + * + * Leave one empty page between vmalloc'ed areas and + * the start of the fixmap. + */ +#define __FIXADDR_TOP 0xfffff000 + +#ifndef __ASSEMBLY__ #include #include #include @@ -97,13 +106,8 @@ extern void __set_fixmap (enum fixed_add #define clear_fixmap(idx) \ __set_fixmap(idx, 0, __pgprot(0)) -/* - * used by vmalloc.c. - * - * Leave one empty page between vmalloc'ed areas and - * the start of the fixmap. - */ -#define FIXADDR_TOP (0xfffff000UL) +#define FIXADDR_TOP ((unsigned long)__FIXADDR_TOP) + #define __FIXADDR_SIZE (__end_of_permanent_fixed_addresses << PAGE_SHIFT) #define FIXADDR_START (FIXADDR_TOP - __FIXADDR_SIZE) @@ -148,4 +152,5 @@ static inline unsigned long virt_to_fix( return __virt_to_fix(vaddr); } +#endif /* !__ASSEMBLY__ */ #endif --- linux-2.6.9-rc1/include/asm-i386/hardirq.h 2004-08-24 00:03:24.000000000 -0700 +++ 25/include/asm-i386/hardirq.h 2004-09-03 00:56:41.020924520 -0700 @@ -37,20 +37,6 @@ typedef struct { #define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) #define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) -#define __MASK(x) ((1UL << (x))-1) - -#define PREEMPT_MASK (__MASK(PREEMPT_BITS) << PREEMPT_SHIFT) -#define HARDIRQ_MASK (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) -#define SOFTIRQ_MASK (__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) - -#define hardirq_count() (preempt_count() & HARDIRQ_MASK) -#define softirq_count() (preempt_count() & SOFTIRQ_MASK) -#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) - -#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) -#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) -#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) - /* * The hardirq mask has to be large enough to have * space for potentially all IRQ sources in the system @@ -60,30 +46,10 @@ typedef struct { # error HARDIRQ_BITS is too low! #endif -/* - * Are we doing bottom half or hardware interrupt processing? - * Are we in a softirq context? Interrupt context? - */ -#define in_irq() (hardirq_count()) -#define in_softirq() (softirq_count()) -#define in_interrupt() (irq_count()) - - -#define hardirq_trylock() (!in_interrupt()) -#define hardirq_endlock() do { } while (0) - -#define irq_enter() (preempt_count() += HARDIRQ_OFFSET) #define nmi_enter() (irq_enter()) #define nmi_exit() (preempt_count() -= HARDIRQ_OFFSET) -#ifdef CONFIG_PREEMPT -# include -# define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != kernel_locked()) -# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) -#else -# define in_atomic() (preempt_count() != 0) -# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET -#endif +#define irq_enter() (preempt_count() += HARDIRQ_OFFSET) #define irq_exit() \ do { \ preempt_count() -= IRQ_EXIT_OFFSET; \ @@ -92,10 +58,4 @@ do { \ preempt_enable_no_resched(); \ } while (0) -#ifndef CONFIG_SMP -# define synchronize_irq(irq) barrier() -#else - extern void synchronize_irq(unsigned int irq); -#endif /* CONFIG_SMP */ - #endif /* __ASM_HARDIRQ_H */ --- linux-2.6.9-rc1/include/asm-i386/hw_irq.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/include/asm-i386/hw_irq.h 2004-09-02 20:51:52.000000000 -0700 @@ -68,46 +68,6 @@ extern atomic_t irq_mis_count; #define IO_APIC_IRQ(x) (((x) >= 16) || ((1<<(x)) & io_apic_irqs)) -/* - * The profiling function is SMP safe. (nothing can mess - * around with "current", and the profiling counters are - * updated with atomic operations). This is especially - * useful with a profiling multiplier != 1 - */ -static inline void x86_do_profile(struct pt_regs * regs) -{ - unsigned long eip; - extern unsigned long prof_cpu_mask; - - profile_hook(regs); - - if (user_mode(regs)) - return; - - if (!prof_buffer) - return; - - eip = regs->eip; - - /* - * Only measure the CPUs specified by /proc/irq/prof_cpu_mask. - * (default is all CPUs.) - */ - if (!((1<>= prof_shift; - /* - * Don't ignore out-of-bounds EIP values silently, - * put them into the last histogram slot, so if - * present, they will show up as a sharp peak. - */ - if (eip > prof_len-1) - eip = prof_len-1; - atomic_inc((atomic_t *)&prof_buffer[eip]); -} - #if defined(CONFIG_X86_IO_APIC) static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) { --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-i386/kdebug.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,50 @@ +#ifndef _I386_KDEBUG_H +#define _I386_KDEBUG_H 1 + +/* + * Aug-05 2004 Ported by Prasanna S Panchamukhi + * from x86_64 architecture. + */ +#include + +struct pt_regs; + +struct die_args { + struct pt_regs *regs; + const char *str; + long err; + int trapnr; + int signr; +}; + +/* Note - you should never unregister because that can race with NMIs. + If you really want to do it first unregister - then synchronize_kernel - then free. + */ +int register_die_notifier(struct notifier_block *nb); +extern struct notifier_block *i386die_chain; + + +/* Grossly misnamed. */ +enum die_val { + DIE_OOPS = 1, + DIE_INT3, + DIE_DEBUG, + DIE_PANIC, + DIE_NMI, + DIE_DIE, + DIE_NMIWATCHDOG, + DIE_KERNELDEBUG, + DIE_TRAP, + DIE_GPF, + DIE_CALL, + DIE_NMI_IPI, + DIE_PAGE_FAULT, +}; + +static inline int notify_die(enum die_val val,char *str,struct pt_regs *regs,long err,int trap, int sig) +{ + struct die_args args = { .regs=regs, .str=str, .err=err, .trapnr=trap,.signr=sig }; + return notifier_call_chain(&i386die_chain, val, &args); +} + +#endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-i386/kexec.h 2004-09-03 00:56:59.180163896 -0700 @@ -0,0 +1,25 @@ +#ifndef _I386_KEXEC_H +#define _I386_KEXEC_H + +#include + +/* + * KEXEC_SOURCE_MEMORY_LIMIT maximum page get_free_page can return. + * I.e. Maximum page that is mapped directly into kernel memory, + * and kmap is not required. + * + * Someone correct me if FIXADDR_START - PAGEOFFSET is not the correct + * calculation for the amount of memory directly mappable into the + * kernel memory space. + */ + +/* Maximum physical address we can use pages from */ +#define KEXEC_SOURCE_MEMORY_LIMIT (-1UL) +/* Maximum address we can reach in physical address mode */ +#define KEXEC_DESTINATION_MEMORY_LIMIT (-1UL) +/* Maximum address we can use for the control code buffer */ +#define KEXEC_CONTROL_MEMORY_LIMIT TASK_SIZE + +#define KEXEC_CONTROL_CODE_SIZE 4096 + +#endif /* _I386_KEXEC_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-i386/kgdb.h 2004-09-03 00:56:36.524608064 -0700 @@ -0,0 +1,69 @@ +#ifndef __KGDB +#define __KGDB + +/* + * This file should not include ANY others. This makes it usable + * most anywhere without the fear of include order or inclusion. + * Make it so! + * + * This file may be included all the time. It is only active if + * CONFIG_KGDB is defined, otherwise it stubs out all the macros + * and entry points. + */ +#if defined(CONFIG_KGDB) && !defined(__ASSEMBLY__) + +extern void breakpoint(void); +#define INIT_KGDB_INTS kgdb_enable_ints() + +#ifndef BREAKPOINT +#define BREAKPOINT asm(" int $3") +#endif + +extern void kgdb_schedule_breakpoint(void); +extern void kgdb_process_breakpoint(void); + +extern int kgdb_tty_hook(void); +extern int kgdb_eth_hook(void); +extern int kgdboe; + +/* + * GDB debug stub (or any debug stub) can point the 'linux_debug_hook' + * pointer to its routine and it will be entered as the first thing + * when a trap occurs. + * + * Return values are, at present, undefined. + * + * The debug hook routine does not necessarily return to its caller. + * It has the register image and thus may choose to resume execution + * anywhere it pleases. + */ +struct pt_regs; + +extern int kgdb_handle_exception(int trapno, + int signo, int err_code, struct pt_regs *regs); +extern int in_kgdb(struct pt_regs *regs); + +#ifdef CONFIG_KGDB_TS +void kgdb_tstamp(int line, char *source, int data0, int data1); +/* + * This is the time stamp function. The macro adds the source info and + * does a cast on the data to allow most any 32-bit value. + */ + +#define kgdb_ts(data0,data1) kgdb_tstamp(__LINE__,__FILE__,(int)data0,(int)data1) +#else +#define kgdb_ts(data0,data1) +#endif +#else /* CONFIG_KGDB && ! __ASSEMBLY__ ,stubs follow... */ +#ifndef BREAKPOINT +#define BREAKPOINT +#endif +#define kgdb_ts(data0,data1) +#define in_kgdb +#define kgdb_handle_exception +#define breakpoint +#define INIT_KGDB_INTS +#define kgdb_process_breakpoint() do {} while(0) + +#endif +#endif /* __KGDB */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-i386/kgdb_local.h 2004-09-03 00:56:35.782720848 -0700 @@ -0,0 +1,102 @@ +#ifndef __KGDB_LOCAL +#define ___KGDB_LOCAL +#include +#include +#include +#include +#include +#include +#include +#include + +#define PORT 0x3f8 +#ifdef CONFIG_KGDB_PORT +#undef PORT +#define PORT CONFIG_KGDB_PORT +#endif +#define IRQ 4 +#ifdef CONFIG_KGDB_IRQ +#undef IRQ +#define IRQ CONFIG_KGDB_IRQ +#endif +#define SB_CLOCK 1843200 +#define SB_BASE (SB_CLOCK/16) +#define SB_BAUD9600 SB_BASE/9600 +#define SB_BAUD192 SB_BASE/19200 +#define SB_BAUD384 SB_BASE/38400 +#define SB_BAUD576 SB_BASE/57600 +#define SB_BAUD1152 SB_BASE/115200 +#ifdef CONFIG_KGDB_9600BAUD +#define SB_BAUD SB_BAUD9600 +#endif +#ifdef CONFIG_KGDB_19200BAUD +#define SB_BAUD SB_BAUD192 +#endif +#ifdef CONFIG_KGDB_38400BAUD +#define SB_BAUD SB_BAUD384 +#endif +#ifdef CONFIG_KGDB_57600BAUD +#define SB_BAUD SB_BAUD576 +#endif +#ifdef CONFIG_KGDB_115200BAUD +#define SB_BAUD SB_BAUD1152 +#endif +#ifndef SB_BAUD +#define SB_BAUD SB_BAUD1152 /* Start with this if not given */ +#endif + +#ifndef CONFIG_X86_TSC +#undef rdtsc +#define rdtsc(a,b) if (a++ > 10000){a = 0; b++;} +#undef rdtscll +#define rdtscll(s) s++ +#endif + +#ifdef _raw_read_unlock /* must use a name that is "define"ed, not an inline */ +#undef spin_lock +#undef spin_trylock +#undef spin_unlock +#define spin_lock _raw_spin_lock +#define spin_trylock _raw_spin_trylock +#define spin_unlock _raw_spin_unlock +#else +#endif +#undef spin_unlock_wait +#define spin_unlock_wait(x) do { cpu_relax(); barrier();} \ + while(spin_is_locked(x)) + +#define SB_IER 1 +#define SB_MCR UART_MCR_OUT2 | UART_MCR_DTR | UART_MCR_RTS + +#define FLAGS 0 +#define SB_STATE { \ + magic: SSTATE_MAGIC, \ + baud_base: SB_BASE, \ + port: PORT, \ + irq: IRQ, \ + flags: FLAGS, \ + custom_divisor:SB_BAUD} +#define SB_INFO { \ + magic: SERIAL_MAGIC, \ + port: PORT,0,FLAGS, \ + state: &state, \ + tty: (struct tty_struct *)&state, \ + IER: SB_IER, \ + MCR: SB_MCR} +extern void putDebugChar(int); +/* RTAI support needs us to really stop/start interrupts */ + +#define kgdb_sti() __asm__ __volatile__("sti": : :"memory") +#define kgdb_cli() __asm__ __volatile__("cli": : :"memory") +#define kgdb_local_save_flags(x) __asm__ __volatile__(\ + "pushfl ; popl %0":"=g" (x): /* no input */) +#define kgdb_local_irq_restore(x) __asm__ __volatile__(\ + "pushl %0 ; popfl": \ + /* no output */ :"g" (x):"memory", "cc") +#define kgdb_local_irq_save(x) kgdb_local_save_flags(x); kgdb_cli() + +#ifdef CONFIG_SERIAL +extern void shutdown_for_kgdb(struct async_struct *info); +#endif +#define INIT_KDEBUG putDebugChar("+"); +#endif /* __KGDB_LOCAL */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-i386/kprobes.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,60 @@ +#ifndef _ASM_KPROBES_H +#define _ASM_KPROBES_H +/* + * Kernel Probes (KProbes) + * include/asm-i386/kprobes.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2002, 2004 + * + * 2002-Oct Created by Vamsi Krishna S Kernel + * Probes initial implementation ( includes suggestions from + * Rusty Russell). + */ +#include +#include + +struct pt_regs; + +typedef u8 kprobe_opcode_t; +#define BREAKPOINT_INSTRUCTION 0xcc +#define MAX_INSN_SIZE 16 +#define MAX_STACK_SIZE 64 +#define MIN_STACK_SIZE(ADDR) (((MAX_STACK_SIZE) < \ + (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR))) \ + ? (MAX_STACK_SIZE) \ + : (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR))) + +/* trap3/1 are intr gates for kprobes. So, restore the status of IF, + * if necessary, before executing the original int3/1 (trap) handler. + */ +static inline void restore_interrupts(struct pt_regs *regs) +{ + if (regs->eflags & IF_MASK) + local_irq_enable(); +} + +#ifdef CONFIG_KPROBES +extern int kprobe_exceptions_notify(struct notifier_block *self, + unsigned long val, void *data); +#else /* !CONFIG_KPROBES */ +static inline int kprobe_exceptions_notify(struct notifier_block *self, + unsigned long val, void *data) +{ + return 0; +} +#endif +#endif /* _ASM_KPROBES_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-i386/lockmeter.h 2004-09-03 00:56:42.828649704 -0700 @@ -0,0 +1,115 @@ +/* + * Copyright (C) 1999,2000 Silicon Graphics, Inc. + * + * Written by John Hawkes (hawkes@sgi.com) + * Based on klstat.h by Jack Steiner (steiner@sgi.com) + * + * Modified by Ray Bryant (raybry@us.ibm.com) + * Changes Copyright (C) 2000 IBM, Inc. + * Added save of index in spinlock_t to improve efficiency + * of "hold" time reporting for spinlocks. + * Added support for hold time statistics for read and write + * locks. + * Moved machine dependent code here from include/lockmeter.h. + * + */ + +#ifndef _I386_LOCKMETER_H +#define _I386_LOCKMETER_H + +#include +#include + +#include + +#ifdef __KERNEL__ +extern unsigned long cpu_khz; +#define CPU_CYCLE_FREQUENCY (cpu_khz * 1000) +#else +#define CPU_CYCLE_FREQUENCY 450000000 +#endif + +#define THIS_CPU_NUMBER smp_processor_id() + +/* + * macros to cache and retrieve an index value inside of a spin lock + * these macros assume that there are less than 65536 simultaneous + * (read mode) holders of a rwlock. Not normally a problem!! + * we also assume that the hash table has less than 65535 entries. + */ +/* + * instrumented spinlock structure -- never used to allocate storage + * only used in macros below to overlay a spinlock_t + */ +typedef struct inst_spinlock_s { + /* remember, Intel is little endian */ + unsigned short lock; + unsigned short index; +} inst_spinlock_t; +#define PUT_INDEX(lock_ptr,indexv) ((inst_spinlock_t *)(lock_ptr))->index = indexv +#define GET_INDEX(lock_ptr) ((inst_spinlock_t *)(lock_ptr))->index + +/* + * macros to cache and retrieve an index value in a read/write lock + * as well as the cpu where a reader busy period started + * we use the 2nd word (the debug word) for this, so require the + * debug word to be present + */ +/* + * instrumented rwlock structure -- never used to allocate storage + * only used in macros below to overlay a rwlock_t + */ +typedef struct inst_rwlock_s { + volatile int lock; + unsigned short index; + unsigned short cpu; +} inst_rwlock_t; +#define PUT_RWINDEX(rwlock_ptr,indexv) ((inst_rwlock_t *)(rwlock_ptr))->index = indexv +#define GET_RWINDEX(rwlock_ptr) ((inst_rwlock_t *)(rwlock_ptr))->index +#define PUT_RW_CPU(rwlock_ptr,cpuv) ((inst_rwlock_t *)(rwlock_ptr))->cpu = cpuv +#define GET_RW_CPU(rwlock_ptr) ((inst_rwlock_t *)(rwlock_ptr))->cpu + +/* + * return the number of readers for a rwlock_t + */ +#define RWLOCK_READERS(rwlock_ptr) rwlock_readers(rwlock_ptr) + +extern inline int rwlock_readers(rwlock_t *rwlock_ptr) +{ + int tmp = (int) rwlock_ptr->lock; + /* read and write lock attempts may cause the lock value to temporarily */ + /* be negative. Until it is >= 0 we know nothing (i. e. can't tell if */ + /* is -1 because it was write locked and somebody tried to read lock it */ + /* or if it is -1 because it was read locked and somebody tried to write*/ + /* lock it. ........................................................... */ + do { + tmp = (int) rwlock_ptr->lock; + } while (tmp < 0); + if (tmp == 0) return(0); + else return(RW_LOCK_BIAS-tmp); +} + +/* + * return true if rwlock is write locked + * (note that other lock attempts can cause the lock value to be negative) + */ +#define RWLOCK_IS_WRITE_LOCKED(rwlock_ptr) ((rwlock_ptr)->lock <= 0) +#define IABS(x) ((x) > 0 ? (x) : -(x)) +#define RWLOCK_IS_READ_LOCKED(rwlock_ptr) ((IABS((rwlock_ptr)->lock) % RW_LOCK_BIAS) != 0) + +/* this is a lot of typing just to get gcc to emit "rdtsc" */ +static inline long long get_cycles64 (void) +{ + union longlong_u { + long long intlong; + struct intint_s { + uint32_t eax; + uint32_t edx; + } intint; + } longlong; + + rdtsc(longlong.intint.eax,longlong.intint.edx); + return longlong.intlong; +} + +#endif /* _I386_LOCKMETER_H */ --- linux-2.6.9-rc1/include/asm-i386/mach-bigsmp/mach_ipi.h 2004-08-24 00:02:32.000000000 -0700 +++ 25/include/asm-i386/mach-bigsmp/mach_ipi.h 2004-09-02 20:51:52.000000000 -0700 @@ -1,7 +1,7 @@ #ifndef __ASM_MACH_IPI_H #define __ASM_MACH_IPI_H -inline void send_IPI_mask_sequence(cpumask_t mask, int vector); +void send_IPI_mask_sequence(cpumask_t mask, int vector); static inline void send_IPI_mask(cpumask_t mask, int vector) { --- linux-2.6.9-rc1/include/asm-i386/mach-default/do_timer.h 2004-08-24 00:03:32.000000000 -0700 +++ 25/include/asm-i386/mach-default/do_timer.h 2004-09-02 20:51:52.000000000 -0700 @@ -22,7 +22,7 @@ static inline void do_timer_interrupt_ho * system, in that case we have to call the local interrupt handler. */ #ifndef CONFIG_X86_LOCAL_APIC - x86_do_profile(regs); + profile_tick(CPU_PROFILING, regs); #else if (!using_apic_timer) smp_local_timer_interrupt(regs); --- linux-2.6.9-rc1/include/asm-i386/mach-default/irq_vectors.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/include/asm-i386/mach-default/irq_vectors.h 2004-09-03 00:56:44.878338104 -0700 @@ -56,14 +56,15 @@ * sources per level' errata. */ #define LOCAL_TIMER_VECTOR 0xef +#define LOCAL_PERFCTR_VECTOR 0xee /* - * First APIC vector available to drivers: (vectors 0x30-0xee) + * First APIC vector available to drivers: (vectors 0x30-0xed) * we start at 0x31 to spread out vectors evenly between priority * levels. (0x80 is the syscall vector) */ #define FIRST_DEVICE_VECTOR 0x31 -#define FIRST_SYSTEM_VECTOR 0xef +#define FIRST_SYSTEM_VECTOR 0xee #define TIMER_IRQ 0 --- linux-2.6.9-rc1/include/asm-i386/mach-es7000/mach_ipi.h 2004-08-24 00:02:25.000000000 -0700 +++ 25/include/asm-i386/mach-es7000/mach_ipi.h 2004-09-02 20:51:52.000000000 -0700 @@ -1,7 +1,7 @@ #ifndef __ASM_MACH_IPI_H #define __ASM_MACH_IPI_H -inline void send_IPI_mask_sequence(cpumask_t mask, int vector); +void send_IPI_mask_sequence(cpumask_t mask, int vector); static inline void send_IPI_mask(cpumask_t mask, int vector) { --- linux-2.6.9-rc1/include/asm-i386/mach-es7000/mach_mpparse.h 2004-08-24 00:01:51.000000000 -0700 +++ 25/include/asm-i386/mach-es7000/mach_mpparse.h 2004-09-02 20:51:52.000000000 -0700 @@ -21,7 +21,8 @@ static inline int mps_oem_check(struct m if (mpc->mpc_oemptr) { struct mp_config_oemtable *oem_table = (struct mp_config_oemtable *)mpc->mpc_oemptr; - return parse_unisys_oem((char *)oem_table, oem_table->oem_length); + if (!strncmp(oem, "UNISYS", 6)) + return parse_unisys_oem((char *)oem_table, oem_table->oem_length); } return 0; } --- linux-2.6.9-rc1/include/asm-i386/mach-es7000/mach_mpspec.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/include/asm-i386/mach-es7000/mach_mpspec.h 2004-09-02 23:57:37.282654624 -0700 @@ -3,6 +3,6 @@ #define MAX_IRQ_SOURCES 256 -#define MAX_MP_BUSSES 32 +#define MAX_MP_BUSSES 256 #endif /* __ASM_MACH_MPSPEC_H */ --- linux-2.6.9-rc1/include/asm-i386/mach-summit/mach_apic.h 2004-08-24 00:02:59.000000000 -0700 +++ 25/include/asm-i386/mach-summit/mach_apic.h 2004-09-02 23:57:37.539615560 -0700 @@ -19,11 +19,15 @@ static inline cpumask_t target_cpus(void) { - return CPU_MASK_ALL; + /* CPU_MASK_ALL (0xff) has undefined behaviour with + * dest_LowestPrio mode logical clustered apic interrupt routing + * Just start on cpu 0. IRQ balancing will spread load + */ + return cpumask_of_cpu(0); } #define TARGET_CPUS (target_cpus()) -#define INT_DELIVERY_MODE (dest_Fixed) +#define INT_DELIVERY_MODE (dest_LowestPrio) #define INT_DEST_MODE 1 /* logical delivery broadcast to all procs */ static inline unsigned long check_apicid_used(physid_mask_t bitmap, int apicid) --- linux-2.6.9-rc1/include/asm-i386/mach-summit/mach_ipi.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/include/asm-i386/mach-summit/mach_ipi.h 2004-09-02 20:51:52.000000000 -0700 @@ -1,7 +1,7 @@ #ifndef __ASM_MACH_IPI_H #define __ASM_MACH_IPI_H -inline void send_IPI_mask_sequence(cpumask_t mask, int vector); +void send_IPI_mask_sequence(cpumask_t mask, int vector); static inline void send_IPI_mask(cpumask_t mask, int vector) { --- linux-2.6.9-rc1/include/asm-i386/mach-visws/do_timer.h 2004-08-24 00:02:25.000000000 -0700 +++ 25/include/asm-i386/mach-visws/do_timer.h 2004-09-02 20:51:52.000000000 -0700 @@ -15,7 +15,7 @@ static inline void do_timer_interrupt_ho * system, in that case we have to call the local interrupt handler. */ #ifndef CONFIG_X86_LOCAL_APIC - x86_do_profile(regs); + profile_tick(CPU_PROFILING, regs); #else if (!using_apic_timer) smp_local_timer_interrupt(regs); --- linux-2.6.9-rc1/include/asm-i386/mach-visws/irq_vectors.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/include/asm-i386/mach-visws/irq_vectors.h 2004-09-03 00:56:44.878338104 -0700 @@ -35,14 +35,15 @@ * sources per level' errata. */ #define LOCAL_TIMER_VECTOR 0xef +#define LOCAL_PERFCTR_VECTOR 0xee /* - * First APIC vector available to drivers: (vectors 0x30-0xee) + * First APIC vector available to drivers: (vectors 0x30-0xed) * we start at 0x31 to spread out vectors evenly between priority * levels. (0x80 is the syscall vector) */ #define FIRST_DEVICE_VECTOR 0x31 -#define FIRST_SYSTEM_VECTOR 0xef +#define FIRST_SYSTEM_VECTOR 0xee #define TIMER_IRQ 0 --- linux-2.6.9-rc1/include/asm-i386/node.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/include/asm-i386/node.h 2004-09-03 00:56:51.389348280 -0700 @@ -5,6 +5,7 @@ #include #include #include +#include struct i386_node { struct node node; --- linux-2.6.9-rc1/include/asm-i386/page.h 2004-08-24 00:02:19.000000000 -0700 +++ 25/include/asm-i386/page.h 2004-09-03 00:56:41.912788936 -0700 @@ -116,6 +116,10 @@ static __inline__ int get_order(unsigned return order; } +extern int sysctl_legacy_va_layout; + +extern int devmem_is_allowed(unsigned long pagenr); + #endif /* __ASSEMBLY__ */ #ifdef __ASSEMBLY__ @@ -145,6 +149,8 @@ static __inline__ int get_order(unsigned ((current->personality & READ_IMPLIES_EXEC) ? VM_EXEC : 0 ) | \ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) + + #endif /* __KERNEL__ */ #endif /* _I386_PAGE_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-i386/perfctr.h 2004-09-03 00:56:46.052159656 -0700 @@ -0,0 +1,195 @@ +/* $Id: perfctr.h,v 1.52 2004/05/23 22:36:34 mikpe Exp $ + * x86/x86_64 Performance-Monitoring Counters driver + * + * Copyright (C) 1999-2004 Mikael Pettersson + */ +#ifndef _ASM_I386_PERFCTR_H +#define _ASM_I386_PERFCTR_H + +/* cpu_type values */ +#define PERFCTR_X86_GENERIC 0 /* any x86 with rdtsc */ +#define PERFCTR_X86_INTEL_P5 1 /* no rdpmc */ +#define PERFCTR_X86_INTEL_P5MMX 2 +#define PERFCTR_X86_INTEL_P6 3 +#define PERFCTR_X86_INTEL_PII 4 +#define PERFCTR_X86_INTEL_PIII 5 +#define PERFCTR_X86_CYRIX_MII 6 +#define PERFCTR_X86_WINCHIP_C6 7 /* no rdtsc */ +#define PERFCTR_X86_WINCHIP_2 8 /* no rdtsc */ +#define PERFCTR_X86_AMD_K7 9 +#define PERFCTR_X86_VIA_C3 10 /* no pmc0 */ +#define PERFCTR_X86_INTEL_P4 11 /* model 0 and 1 */ +#define PERFCTR_X86_INTEL_P4M2 12 /* model 2 */ +#define PERFCTR_X86_AMD_K8 13 +#define PERFCTR_X86_INTEL_PENTM 14 /* Pentium M */ +#define PERFCTR_X86_AMD_K8C 15 /* Revision C */ +#define PERFCTR_X86_INTEL_P4M3 16 /* model 3 and above */ + +struct perfctr_sum_ctrs { + unsigned long long tsc; + unsigned long long pmc[18]; +}; + +struct perfctr_cpu_control { + unsigned int tsc_on; + unsigned int nractrs; /* # of a-mode counters */ + unsigned int nrictrs; /* # of i-mode counters */ + unsigned int pmc_map[18]; + unsigned int evntsel[18]; /* one per counter, even on P5 */ + struct { + unsigned int escr[18]; + unsigned int pebs_enable; /* for replay tagging */ + unsigned int pebs_matrix_vert; /* for replay tagging */ + } p4; + int ireset[18]; /* < 0, for i-mode counters */ + unsigned int _reserved1; + unsigned int _reserved2; + unsigned int _reserved3; + unsigned int _reserved4; +}; + +struct perfctr_cpu_state { + unsigned int cstatus; + struct { /* k1 is opaque in the user ABI */ + unsigned int id; + int isuspend_cpu; + } k1; + /* The two tsc fields must be inlined. Placing them in a + sub-struct causes unwanted internal padding on x86-64. */ + unsigned int tsc_start; + unsigned long long tsc_sum; + struct { + unsigned int map; + unsigned int start; + unsigned long long sum; + } pmc[18]; /* the size is not part of the user ABI */ +#ifdef __KERNEL__ + struct perfctr_cpu_control control; + unsigned int p4_escr_map[18]; +#endif +}; + +/* cstatus is a re-encoding of control.tsc_on/nractrs/nrictrs + which should have less overhead in most cases */ + +static inline +unsigned int __perfctr_mk_cstatus(unsigned int tsc_on, unsigned int have_ictrs, + unsigned int nrictrs, unsigned int nractrs) +{ + return (tsc_on<<31) | (have_ictrs<<16) | ((nractrs+nrictrs)<<8) | nractrs; +} + +static inline +unsigned int perfctr_mk_cstatus(unsigned int tsc_on, unsigned int nractrs, + unsigned int nrictrs) +{ + return __perfctr_mk_cstatus(tsc_on, nrictrs, nrictrs, nractrs); +} + +static inline unsigned int perfctr_cstatus_enabled(unsigned int cstatus) +{ + return cstatus; +} + +static inline int perfctr_cstatus_has_tsc(unsigned int cstatus) +{ + return (int)cstatus < 0; /* test and jump on sign */ +} + +static inline unsigned int perfctr_cstatus_nractrs(unsigned int cstatus) +{ + return cstatus & 0x7F; /* and with imm8 */ +} + +static inline unsigned int perfctr_cstatus_nrctrs(unsigned int cstatus) +{ + return (cstatus >> 8) & 0x7F; +} + +static inline unsigned int perfctr_cstatus_has_ictrs(unsigned int cstatus) +{ + return cstatus & (0x7F << 16); +} + +/* + * 'struct siginfo' support for perfctr overflow signals. + * In unbuffered mode, si_code is set to SI_PMC_OVF and a bitmask + * describing which perfctrs overflowed is put in si_pmc_ovf_mask. + * A bitmask is used since more than one perfctr can have overflowed + * by the time the interrupt handler runs. + * + * glibc's doesn't seem to define __SI_FAULT or __SI_CODE(), + * and including as well may cause redefinition errors, + * so the user and kernel values are different #defines here. + */ +#ifdef __KERNEL__ +#define SI_PMC_OVF (__SI_FAULT|'P') +#else +#define SI_PMC_OVF ('P') +#endif +#define si_pmc_ovf_mask _sifields._pad[0] /* XXX: use an unsigned field later */ + +/* version number for user-visible CPU-specific data */ +#define PERFCTR_CPU_VERSION 0x0500 /* 5.0 */ + +#ifdef __KERNEL__ + +#if defined(CONFIG_PERFCTR) + +/* Driver init/exit. */ +extern int perfctr_cpu_init(void); +extern void perfctr_cpu_exit(void); + +/* CPU type name. */ +extern char *perfctr_cpu_name; + +/* Hardware reservation. */ +extern const char *perfctr_cpu_reserve(const char *service); +extern void perfctr_cpu_release(const char *service); + +/* PRE: state has no running interrupt-mode counters. + Check that the new control data is valid. + Update the driver's private control data. + is_global should be zero for per-process counters and non-zero + for global-mode counters. This matters for HT P4s, alas. + Returns a negative error code if the control data is invalid. */ +extern int perfctr_cpu_update_control(struct perfctr_cpu_state *state, int is_global); + +/* Read a-mode counters. Subtract from start and accumulate into sums. + Must be called with preemption disabled. */ +extern void perfctr_cpu_suspend(struct perfctr_cpu_state *state); + +/* Write control registers. Read a-mode counters into start. + Must be called with preemption disabled. */ +extern void perfctr_cpu_resume(struct perfctr_cpu_state *state); + +/* Perform an efficient combined suspend/resume operation. + Must be called with preemption disabled. */ +extern void perfctr_cpu_sample(struct perfctr_cpu_state *state); + +/* The type of a perfctr overflow interrupt handler. + It will be called in IRQ context, with preemption disabled. */ +typedef void (*perfctr_ihandler_t)(unsigned long pc); + +/* Operations related to overflow interrupt handling. */ +#ifdef CONFIG_X86_LOCAL_APIC +extern void perfctr_cpu_set_ihandler(perfctr_ihandler_t); +extern void perfctr_cpu_ireload(struct perfctr_cpu_state*); +extern unsigned int perfctr_cpu_identify_overflow(struct perfctr_cpu_state*); +#else +static inline void perfctr_cpu_set_ihandler(perfctr_ihandler_t x) { } +#endif + +#endif /* CONFIG_PERFCTR */ + +#if defined(CONFIG_PERFCTR) && defined(CONFIG_X86_LOCAL_APIC) +asmlinkage void perfctr_interrupt(struct pt_regs*); +#define perfctr_vector_init() \ + set_intr_gate(LOCAL_PERFCTR_VECTOR, perfctr_interrupt) +#else +#define perfctr_vector_init() do{}while(0) +#endif + +#endif /* __KERNEL__ */ + +#endif /* _ASM_I386_PERFCTR_H */ --- linux-2.6.9-rc1/include/asm-i386/pgtable-2level.h 2004-08-24 00:02:48.000000000 -0700 +++ 25/include/asm-i386/pgtable-2level.h 2004-09-02 20:51:52.000000000 -0700 @@ -75,4 +75,11 @@ static inline int pte_exec_kernel(pte_t #define pgoff_to_pte(off) \ ((pte_t) { (((off) & 0x1f) << 1) + (((off) >> 5) << 8) + _PAGE_FILE }) +/* Encode and de-code a swap entry */ +#define __swp_type(x) (((x).val >> 1) & 0x1f) +#define __swp_offset(x) ((x).val >> 8) +#define __swp_entry(type, offset) ((swp_entry_t) { ((type) << 1) | ((offset) << 8) }) +#define __pte_to_swp_entry(pte) ((swp_entry_t) { (pte).pte_low }) +#define __swp_entry_to_pte(x) ((pte_t) { (x).val }) + #endif /* _I386_PGTABLE_2LEVEL_H */ --- linux-2.6.9-rc1/include/asm-i386/pgtable-3level.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/include/asm-i386/pgtable-3level.h 2004-09-02 20:51:52.000000000 -0700 @@ -134,4 +134,11 @@ static inline pmd_t pfn_pmd(unsigned lon #define pgoff_to_pte(off) ((pte_t) { _PAGE_FILE, (off) }) #define PTE_FILE_MAX_BITS 32 +/* Encode and de-code a swap entry */ +#define __swp_type(x) (((x).val) & 0x1f) +#define __swp_offset(x) ((x).val >> 5) +#define __swp_entry(type, offset) ((swp_entry_t){(type) | (offset) << 5}) +#define __pte_to_swp_entry(pte) ((swp_entry_t){ (pte).pte_high }) +#define __swp_entry_to_pte(x) ((pte_t){ 0, (x).val }) + #endif /* _I386_PGTABLE_3LEVEL_H */ --- linux-2.6.9-rc1/include/asm-i386/pgtable.h 2004-08-24 00:03:28.000000000 -0700 +++ 25/include/asm-i386/pgtable.h 2004-09-02 20:51:52.000000000 -0700 @@ -398,13 +398,6 @@ extern pte_t *lookup_address(unsigned lo } \ } while (0) -/* Encode and de-code a swap entry */ -#define __swp_type(x) (((x).val >> 1) & 0x1f) -#define __swp_offset(x) ((x).val >> 8) -#define __swp_entry(type, offset) ((swp_entry_t) { ((type) << 1) | ((offset) << 8) }) -#define __pte_to_swp_entry(pte) ((swp_entry_t) { (pte).pte_low }) -#define __swp_entry_to_pte(x) ((pte_t) { (x).val }) - #endif /* !__ASSEMBLY__ */ #ifndef CONFIG_DISCONTIGMEM --- linux-2.6.9-rc1/include/asm-i386/processor.h 2004-08-24 00:01:52.000000000 -0700 +++ 25/include/asm-i386/processor.h 2004-09-03 00:56:44.880337800 -0700 @@ -297,6 +297,8 @@ extern unsigned int mca_pentium_flag; */ #define TASK_UNMAPPED_BASE (PAGE_ALIGN(TASK_SIZE / 3)) +#define HAVE_ARCH_PICK_MMAP_LAYOUT + /* * Size of io_bitmap. */ @@ -425,6 +427,8 @@ struct thread_struct { unsigned long *io_bitmap_ptr; /* max allowed port in the bitmap, in bytes: */ unsigned int io_bitmap_max; +/* performance counters */ + struct vperfctr *perfctr; }; #define INIT_THREAD { \ @@ -647,9 +651,4 @@ extern void select_idle_routine(const st #define cache_line_size() (boot_cpu_data.x86_cache_alignment) -#ifdef CONFIG_SCHED_SMT -#define ARCH_HAS_SCHED_DOMAIN -#define ARCH_HAS_SCHED_WAKE_IDLE -#endif - #endif /* __ASM_I386_PROCESSOR_H */ --- linux-2.6.9-rc1/include/asm-i386/ptrace.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/include/asm-i386/ptrace.h 2004-09-02 20:51:52.000000000 -0700 @@ -57,6 +57,7 @@ struct pt_regs { #ifdef __KERNEL__ #define user_mode(regs) ((VM_MASK & (regs)->eflags) || (3 & (regs)->xcs)) #define instruction_pointer(regs) ((regs)->eip) +#define profile_pc(regs) instruction_pointer(regs) #endif #endif --- linux-2.6.9-rc1/include/asm-i386/semaphore.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/include/asm-i386/semaphore.h 2004-09-02 20:51:52.000000000 -0700 @@ -45,21 +45,15 @@ struct semaphore { atomic_t count; int sleepers; wait_queue_head_t wait; -#ifdef WAITQUEUE_DEBUG - long __magic; -#endif }; -#ifdef WAITQUEUE_DEBUG -# define __SEM_DEBUG_INIT(name) \ - , (int)&(name).__magic -#else -# define __SEM_DEBUG_INIT(name) -#endif -#define __SEMAPHORE_INITIALIZER(name,count) \ -{ ATOMIC_INIT(count), 0, __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ - __SEM_DEBUG_INIT(name) } +#define __SEMAPHORE_INITIALIZER(name, n) \ +{ \ + .count = ATOMIC_INIT(n), \ + .sleepers = 0, \ + .wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ +} #define __MUTEX_INITIALIZER(name) \ __SEMAPHORE_INITIALIZER(name,1) @@ -81,9 +75,6 @@ static inline void sema_init (struct sem atomic_set(&sem->count, val); sem->sleepers = 0; init_waitqueue_head(&sem->wait); -#ifdef WAITQUEUE_DEBUG - sem->__magic = (int)&sem->__magic; -#endif } static inline void init_MUTEX (struct semaphore *sem) @@ -113,9 +104,6 @@ asmlinkage void __up(struct semaphore * */ static inline void down(struct semaphore * sem) { -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); __asm__ __volatile__( "# atomic down operation\n\t" @@ -139,9 +127,6 @@ static inline int down_interruptible(str { int result; -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); __asm__ __volatile__( "# atomic interruptible down operation\n\t" @@ -167,10 +152,6 @@ static inline int down_trylock(struct se { int result; -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - __asm__ __volatile__( "# atomic interruptible down operation\n\t" LOCK "decl %1\n\t" /* --sem->count */ @@ -195,9 +176,6 @@ static inline int down_trylock(struct se */ static inline void up(struct semaphore * sem) { -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif __asm__ __volatile__( "# atomic up operation\n\t" LOCK "incl %0\n\t" /* ++sem->count */ --- linux-2.6.9-rc1/include/asm-i386/smp.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/include/asm-i386/smp.h 2004-09-03 00:56:52.586166336 -0700 @@ -84,6 +84,9 @@ static __inline int logical_smp_processo } #endif + +extern int __cpu_disable(void); +extern void __cpu_die(unsigned int cpu); #endif /* !__ASSEMBLY__ */ #define NO_PROC_ID 0xFF /* No processor magic marker */ --- linux-2.6.9-rc1/include/asm-i386/spinlock.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/include/asm-i386/spinlock.h 2004-09-03 00:56:42.829649552 -0700 @@ -169,6 +169,11 @@ here: */ typedef struct { volatile unsigned int lock; +#ifdef CONFIG_LOCKMETER + /* required for LOCKMETER since all bits in lock are used */ + /* and we need this storage for CPU and lock INDEX */ + unsigned lockmeter_magic; +#endif #ifdef CONFIG_DEBUG_SPINLOCK unsigned magic; #endif @@ -176,11 +181,19 @@ typedef struct { #define RWLOCK_MAGIC 0xdeaf1eed +#ifdef CONFIG_LOCKMETER +#ifdef CONFIG_DEBUG_SPINLOCK +#define RWLOCK_MAGIC_INIT , 0, RWLOCK_MAGIC +#else +#define RWLOCK_MAGIC_INIT , 0 +#endif +#else /* !CONFIG_LOCKMETER */ #ifdef CONFIG_DEBUG_SPINLOCK #define RWLOCK_MAGIC_INIT , RWLOCK_MAGIC #else #define RWLOCK_MAGIC_INIT /* */ #endif +#endif /* !CONFIG_LOCKMETER */ #define RW_LOCK_UNLOCKED (rwlock_t) { RW_LOCK_BIAS RWLOCK_MAGIC_INIT } @@ -227,4 +240,60 @@ static inline int _raw_write_trylock(rwl return 0; } +#ifdef CONFIG_LOCKMETER +static inline int _raw_read_trylock(rwlock_t *lock) +{ +/* FIXME -- replace with assembler */ + atomic_t *count = (atomic_t *)lock; + atomic_dec(count); + if (count->counter > 0) + return 1; + atomic_inc(count); + return 0; +} +#endif + +#if defined(CONFIG_LOCKMETER) && defined(CONFIG_HAVE_DEC_LOCK) +extern void _metered_spin_lock (spinlock_t *lock); +extern void _metered_spin_unlock(spinlock_t *lock); + +/* + * Matches what is in arch/i386/lib/dec_and_lock.c, except this one is + * "static inline" so that the spin_lock(), if actually invoked, is charged + * against the real caller, not against the catch-all atomic_dec_and_lock + */ +static inline int atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock) +{ + int counter; + int newcount; + +repeat: + counter = atomic_read(atomic); + newcount = counter-1; + + if (!newcount) + goto slow_path; + + asm volatile("lock; cmpxchgl %1,%2" + :"=a" (newcount) + :"r" (newcount), "m" (atomic->counter), "0" (counter)); + + /* If the above failed, "eax" will have changed */ + if (newcount != counter) + goto repeat; + return 0; + +slow_path: + preempt_disable(); + _metered_spin_lock(lock); + if (atomic_dec_and_test(atomic)) + return 1; + _metered_spin_unlock(lock); + preempt_enable(); + return 0; +} + +#define ATOMIC_DEC_AND_LOCK +#endif + #endif /* __ASM_SPINLOCK_H */ --- linux-2.6.9-rc1/include/asm-i386/timex.h 2004-08-24 00:02:25.000000000 -0700 +++ 25/include/asm-i386/timex.h 2004-09-02 20:51:52.000000000 -0700 @@ -7,7 +7,7 @@ #define _ASMi386_TIMEX_H #include -#include +#include #ifdef CONFIG_X86_ELAN # define CLOCK_TICK_RATE 1189200 /* AMD Elan has different frequency! */ @@ -40,14 +40,17 @@ extern cycles_t cacheflush_time; static inline cycles_t get_cycles (void) { + unsigned long long ret=0; + #ifndef CONFIG_X86_TSC - return 0; -#else - unsigned long long ret; + if (!cpu_has_tsc) + return 0; +#endif +#if defined(CONFIG_X86_GENERIC) || defined(CONFIG_X86_TSC) rdtscll(ret); - return ret; #endif + return ret; } extern unsigned long cpu_khz; --- linux-2.6.9-rc1/include/asm-i386/topology.h 2004-08-24 00:02:20.000000000 -0700 +++ 25/include/asm-i386/topology.h 2004-09-02 20:51:52.000000000 -0700 @@ -67,7 +67,7 @@ static inline cpumask_t pcibus_to_cpumas } /* Node-to-Node distance */ -#define node_distance(from, to) (from != to) +#define node_distance(from, to) ((from) != (to)) /* Cross-node load balancing interval. */ #define NODE_BALANCE_RATE 100 --- linux-2.6.9-rc1/include/asm-i386/uaccess.h 2004-08-24 00:01:55.000000000 -0700 +++ 25/include/asm-i386/uaccess.h 2004-09-02 20:51:52.000000000 -0700 @@ -375,8 +375,10 @@ do { \ : "m"(__m(addr)), "i"(errret), "0"(err)) -unsigned long __copy_to_user_ll(void __user *to, const void *from, unsigned long n); -unsigned long __copy_from_user_ll(void *to, const void __user *from, unsigned long n); +unsigned long __must_check __copy_to_user_ll(void __user *to, + const void *from, unsigned long n); +unsigned long __must_check __copy_from_user_ll(void *to, + const void __user *from, unsigned long n); /* * Here we special-case 1, 2 and 4-byte copy_*_user invocations. On a fault @@ -399,8 +401,8 @@ unsigned long __copy_from_user_ll(void * * Returns number of bytes that could not be copied. * On success, this will be zero. */ -static inline unsigned long -__copy_to_user(void __user *to, const void *from, unsigned long n) +static inline unsigned long __must_check +__copy_to_user_inatomic(void __user *to, const void *from, unsigned long n) { if (__builtin_constant_p(n)) { unsigned long ret; @@ -420,6 +422,13 @@ __copy_to_user(void __user *to, const vo return __copy_to_user_ll(to, from, n); } +static inline unsigned long __must_check +__copy_to_user(void __user *to, const void *from, unsigned long n) +{ + might_sleep(); + return __copy_to_user_inatomic(to, from, n); +} + /** * __copy_from_user: - Copy a block of data from user space, with less checking. * @to: Destination address, in kernel space. @@ -438,7 +447,7 @@ __copy_to_user(void __user *to, const vo * data to the requested size using zero bytes. */ static inline unsigned long -__copy_from_user(void *to, const void __user *from, unsigned long n) +__copy_from_user_inatomic(void *to, const void __user *from, unsigned long n) { if (__builtin_constant_p(n)) { unsigned long ret; @@ -458,11 +467,20 @@ __copy_from_user(void *to, const void __ return __copy_from_user_ll(to, from, n); } -unsigned long copy_to_user(void __user *to, const void *from, unsigned long n); -unsigned long copy_from_user(void *to, - const void __user *from, unsigned long n); -long strncpy_from_user(char *dst, const char __user *src, long count); -long __strncpy_from_user(char *dst, const char __user *src, long count); +static inline unsigned long +__copy_from_user(void *to, const void __user *from, unsigned long n) +{ + might_sleep(); + return __copy_from_user_inatomic(to, from, n); +} +unsigned long __must_check copy_to_user(void __user *to, + const void *from, unsigned long n); +unsigned long __must_check copy_from_user(void *to, + const void __user *from, unsigned long n); +long __must_check strncpy_from_user(char *dst, const char __user *src, + long count); +long __must_check __strncpy_from_user(char *dst, + const char __user *src, long count); /** * strlen_user: - Get the size of a string in user space. @@ -481,7 +499,7 @@ long __strncpy_from_user(char *dst, cons #define strlen_user(str) strnlen_user(str, ~0UL >> 1) long strnlen_user(const char __user *str, long n); -unsigned long clear_user(void __user *mem, unsigned long len); -unsigned long __clear_user(void __user *mem, unsigned long len); +unsigned long __must_check clear_user(void __user *mem, unsigned long len); +unsigned long __must_check __clear_user(void __user *mem, unsigned long len); #endif /* __i386_UACCESS_H */ --- linux-2.6.9-rc1/include/asm-i386/unistd.h 2004-08-24 00:02:59.000000000 -0700 +++ 25/include/asm-i386/unistd.h 2004-09-03 00:56:44.881337648 -0700 @@ -289,8 +289,15 @@ #define __NR_mq_notify (__NR_mq_open+4) #define __NR_mq_getsetattr (__NR_mq_open+5) #define __NR_sys_kexec_load 283 +#define __NR_waitid 284 +#define __NR_perfctr_info 285 +#define __NR_vperfctr_open (__NR_perfctr_info+1) +#define __NR_vperfctr_control (__NR_perfctr_info+2) +#define __NR_vperfctr_unlink (__NR_perfctr_info+3) +#define __NR_vperfctr_iresume (__NR_perfctr_info+4) +#define __NR_vperfctr_read (__NR_perfctr_info+5) -#define NR_syscalls 284 +#define NR_syscalls 291 /* user-visible error numbers are in the range -1 - -124: see */ --- linux-2.6.9-rc1/include/asm-ia64/acpi.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/include/asm-ia64/acpi.h 2004-09-02 20:51:52.000000000 -0700 @@ -96,7 +96,6 @@ static inline void disable_acpi(void) { const char *acpi_get_sysname (void); int acpi_request_vector (u32 int_type); -int acpi_register_irq (u32 gsi, u32 polarity, u32 trigger); int acpi_gsi_to_irq (u32 gsi, unsigned int *irq); #ifdef CONFIG_ACPI_NUMA --- linux-2.6.9-rc1/include/asm-ia64/dma-mapping.h 2004-08-24 00:03:19.000000000 -0700 +++ 25/include/asm-ia64/dma-mapping.h 2004-09-02 20:51:52.000000000 -0700 @@ -5,7 +5,8 @@ * Copyright (C) 2003-2004 Hewlett-Packard Co * David Mosberger-Tang */ - +#include +#include #include #define dma_alloc_coherent platform_dma_alloc_coherent --- linux-2.6.9-rc1/include/asm-ia64/hardirq.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/include/asm-ia64/hardirq.h 2004-09-03 00:56:41.020924520 -0700 @@ -52,20 +52,6 @@ #define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) #define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) -#define __MASK(x) ((1UL << (x))-1) - -#define PREEMPT_MASK (__MASK(PREEMPT_BITS) << PREEMPT_SHIFT) -#define HARDIRQ_MASK (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) -#define SOFTIRQ_MASK (__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) - -#define hardirq_count() (preempt_count() & HARDIRQ_MASK) -#define softirq_count() (preempt_count() & SOFTIRQ_MASK) -#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) - -#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) -#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) -#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) - /* * The hardirq mask has to be large enough to have space for potentially all IRQ sources * in the system nesting on a single CPU: @@ -74,31 +60,4 @@ # error HARDIRQ_BITS is too low! #endif -/* - * Are we doing bottom half or hardware interrupt processing? - * Are we in a softirq context? - * Interrupt context? - */ -#define in_irq() (hardirq_count()) -#define in_softirq() (softirq_count()) -#define in_interrupt() (irq_count()) - -#define hardirq_trylock() (!in_interrupt()) -#define hardirq_endlock() do { } while (0) - -#ifdef CONFIG_PREEMPT -# include -# define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != kernel_locked()) -# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) -#else -# define in_atomic() (preempt_count() != 0) -# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET -#endif - -#ifdef CONFIG_SMP - extern void synchronize_irq (unsigned int irq); -#else -# define synchronize_irq(irq) barrier() -#endif /* CONFIG_SMP */ - #endif /* _ASM_IA64_HARDIRQ_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-ia64/kgdb.h 2004-09-03 00:56:37.256496800 -0700 @@ -0,0 +1,69 @@ +#ifndef __KGDB +#define __KGDB + +/* + * This file should not include ANY others. This makes it usable + * most anywhere without the fear of include order or inclusion. + * Make it so! + * + * This file may be included all the time. It is only active if + * CONFIG_KGDB is defined, otherwise it stubs out all the macros + * and entry points. + */ +#if defined(CONFIG_KGDB) && !defined(__ASSEMBLY__) + +extern void breakpoint(void); +#define INIT_KGDB_INTS kgdb_enable_ints() + +#ifndef BREAKPOINT +#define BREAKPOINT asm volatile ("break.m 0x6665") +#endif + +extern void kgdb_schedule_breakpoint(void); +extern void kgdb_process_breakpoint(void); + +extern int kgdb_tty_hook(void); +extern int kgdb_eth_hook(void); +extern int kgdboe; + +struct kgdb_serial { + unsigned long iobase; + unsigned long shift; + int line; + int iotype; + int claimed; +}; + +/* + * GDB debug stub (or any debug stub) can point the 'linux_debug_hook' + * pointer to its routine and it will be entered as the first thing + * when a trap occurs. + * + * Return values are, at present, undefined. + * + * The debug hook routine does not necessarily return to its caller. + * It has the register image and thus may choose to resume execution + * anywhere it pleases. + */ +struct pt_regs; + +extern int kgdb_handle_exception(int trapno, + int signo, unsigned long err_code, struct pt_regs *regs); +struct unw_frame_info; +extern int in_kgdb(struct pt_regs *regs, struct unw_frame_info *); +#ifdef CONFIG_KGDB_EARLY +extern void __init kgdb_serial_init(void); +#endif + +#else /* CONFIG_KGDB && ! __ASSEMBLY__ ,stubs follow... */ +#ifndef BREAKPOINT +#define BREAKPOINT +#endif +#define in_kgdb +#define kgdb_handle_exception +#define breakpoint +#define INIT_KGDB_INTS +#define kgdb_process_breakpoint() do {} while(0) + +#endif +#endif /* __KGDB */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-ia64/kgdb_local.h 2004-09-03 00:56:37.256496800 -0700 @@ -0,0 +1,114 @@ +#ifndef __KGDB_LOCAL +#define ___KGDB_LOCAL +#include +#include +#include +#include +#include +#include +#include + +#define PORT 0x0 +#ifdef CONFIG_KGDB_PORT +#undef PORT +#define PORT CONFIG_KGDB_PORT +#define IOTYPE SERIAL_IO_PORT +#endif + +#define IOMEM 0x0 +#ifdef CONFIG_KGDB_IOMEM +#undef IOMEM +#define IOMEM CONFIG_KGDB_IOMEM +#define IOTYPE SERIAL_IO_MEM +#endif + +#define IRQ 4 +#ifdef CONFIG_KGDB_IRQ +#undef IRQ +#define IRQ CONFIG_KGDB_IRQ +#endif + +#define IOMEM_REG_SHIFT 0 +#ifdef CONFIG_IOMEM_REG_SHIFT +#undef IOMEM_REG_SHIFT +#define IOMEM_REG_SHIFT CONFIG_IOMEM_REG_SHIFT +#endif + + +#define SB_CLOCK 1843200 +#define SB_BASE (SB_CLOCK/16) +#define SB_BAUD9600 SB_BASE/9600 +#define SB_BAUD192 SB_BASE/19200 +#define SB_BAUD384 SB_BASE/38400 +#define SB_BAUD576 SB_BASE/57600 +#define SB_BAUD1152 SB_BASE/115200 +#ifdef CONFIG_KGDB_9600BAUD +#define SB_BAUD SB_BAUD9600 +#endif +#ifdef CONFIG_KGDB_19200BAUD +#define SB_BAUD SB_BAUD192 +#endif +#ifdef CONFIG_KGDB_38400BAUD +#define SB_BAUD SB_BAUD384 +#endif +#ifdef CONFIG_KGDB_57600BAUD +#define SB_BAUD SB_BAUD576 +#endif +#ifdef CONFIG_KGDB_115200BAUD +#define SB_BAUD SB_BAUD1152 +#endif +#ifndef SB_BAUD +#define SB_BAUD SB_BAUD1152 /* Start with this if not given */ +#endif + + +#ifdef _raw_read_unlock /* must use a name that is "define"ed, not an inline */ +#undef spin_lock +#undef spin_trylock +#undef spin_unlock +#define spin_lock _raw_spin_lock +#define spin_trylock _raw_spin_trylock +#define spin_unlock _raw_spin_unlock +#else +#endif +#undef spin_unlock_wait +#define spin_unlock_wait(x) do { cpu_relax(); barrier();} \ + while(spin_is_locked(x)) + +#define SB_IER 1 +#define SB_MCR UART_MCR_OUT2 | UART_MCR_DTR | UART_MCR_RTS + +#define FLAGS 0 +#define SB_STATE { \ + magic: SSTATE_MAGIC, \ + baud_base: SB_BASE, \ + port: PORT, \ + iomem_base:(u8 *) IOMEM, \ + iomem_reg_shift: IOMEM_REG_SHIFT,\ + io_type: IOTYPE, \ + irq: IRQ, \ + flags: FLAGS, \ + custom_divisor:SB_BAUD} +#define SB_INFO { \ + magic: SERIAL_MAGIC, \ + port: PORT,0,FLAGS, \ + io_type: IOTYPE, \ + iomem_base: (u8 *) IOMEM, \ + iomem_reg_shift: IOMEM_REG_SHIFT, \ + state: &state, \ + tty: (struct tty_struct *)&state, \ + IER: SB_IER, \ + MCR: SB_MCR} +extern void putDebugChar(int); +/* RTAI support needs us to really stop/start interrupts */ + +#define kgdb_local_save_flags(x) local_save_flags(x) +#define kgdb_local_irq_restore(x) local_irq_restore(x) +#define kgdb_local_irq_save(x) local_irq_save(x) + +#ifdef CONFIG_SERIAL +extern void shutdown_for_kgdb(struct async_struct *info); +extern void kgdb_serial_setup(struct uart_port *); +#endif +#define INIT_KDEBUG putDebugChar("+"); +#endif /* __KGDB_LOCAL */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-ia64/lockmeter.h 2004-09-03 00:56:42.829649552 -0700 @@ -0,0 +1,72 @@ +/* + * Copyright (C) 1999,2000 Silicon Graphics, Inc. + * + * Written by John Hawkes (hawkes@sgi.com) + * Based on klstat.h by Jack Steiner (steiner@sgi.com) + */ + +#ifndef _IA64_LOCKMETER_H +#define _IA64_LOCKMETER_H + +#ifdef local_cpu_data +#define CPU_CYCLE_FREQUENCY local_cpu_data->itc_freq +#else +#define CPU_CYCLE_FREQUENCY my_cpu_data.itc_freq +#endif +#define get_cycles64() get_cycles() + +#define THIS_CPU_NUMBER smp_processor_id() + +/* + * macros to cache and retrieve an index value inside of a lock + * these macros assume that there are less than 65536 simultaneous + * (read mode) holders of a rwlock. + * we also assume that the hash table has less than 32767 entries. + */ +/* + * instrumented spinlock structure -- never used to allocate storage + * only used in macros below to overlay a spinlock_t + */ +typedef struct inst_spinlock_s { + /* remember, Intel is little endian */ + volatile unsigned short lock; + volatile unsigned short index; +} inst_spinlock_t; +#define PUT_INDEX(lock_ptr,indexv) ((inst_spinlock_t *)(lock_ptr))->index = indexv +#define GET_INDEX(lock_ptr) ((inst_spinlock_t *)(lock_ptr))->index + +/* + * macros to cache and retrieve an index value in a read/write lock + * as well as the cpu where a reader busy period started + * we use the 2nd word (the debug word) for this, so require the + * debug word to be present + */ +/* + * instrumented rwlock structure -- never used to allocate storage + * only used in macros below to overlay a rwlock_t + */ +typedef struct inst_rwlock_s { + volatile int read_counter:31; + volatile int write_lock:1; + volatile unsigned short index; + volatile unsigned short cpu; +} inst_rwlock_t; +#define PUT_RWINDEX(rwlock_ptr,indexv) ((inst_rwlock_t *)(rwlock_ptr))->index = indexv +#define GET_RWINDEX(rwlock_ptr) ((inst_rwlock_t *)(rwlock_ptr))->index +#define PUT_RW_CPU(rwlock_ptr,cpuv) ((inst_rwlock_t *)(rwlock_ptr))->cpu = cpuv +#define GET_RW_CPU(rwlock_ptr) ((inst_rwlock_t *)(rwlock_ptr))->cpu + +/* + * return the number of readers for a rwlock_t + */ +#define RWLOCK_READERS(rwlock_ptr) ((rwlock_ptr)->read_counter) + +/* + * return true if rwlock is write locked + * (note that other lock attempts can cause the lock value to be negative) + */ +#define RWLOCK_IS_WRITE_LOCKED(rwlock_ptr) ((rwlock_ptr)->write_lock) +#define RWLOCK_IS_READ_LOCKED(rwlock_ptr) ((rwlock_ptr)->read_counter) + +#endif /* _IA64_LOCKMETER_H */ + --- linux-2.6.9-rc1/include/asm-ia64/mmu_context.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/include/asm-ia64/mmu_context.h 2004-09-03 00:56:30.922459720 -0700 @@ -28,36 +28,6 @@ #include -#define MMU_CONTEXT_DEBUG 0 - -#if MMU_CONTEXT_DEBUG - -#include - -extern struct mmu_trace_entry { - char op; - u8 cpu; - u32 context; - void *mm; -} mmu_tbuf[1024]; - -extern volatile int mmu_tbuf_index; - -# define MMU_TRACE(_op,_cpu,_mm,_ctx) \ -do { \ - int i = __sync_fetch_and_add(&mmu_tbuf_index, 1) % ARRAY_SIZE(mmu_tbuf); \ - struct mmu_trace_entry e; \ - e.op = (_op); \ - e.cpu = (_cpu); \ - e.mm = (_mm); \ - e.context = (_ctx); \ - mmu_tbuf[i] = e; \ -} while (0) - -#else -# define MMU_TRACE(op,cpu,mm,ctx) do { ; } while (0) -#endif - struct ia64_ctx { spinlock_t lock; unsigned int next; /* next context number to use */ @@ -123,7 +93,6 @@ get_mmu_context (struct mm_struct *mm) static inline int init_new_context (struct task_struct *p, struct mm_struct *mm) { - MMU_TRACE('N', smp_processor_id(), mm, 0); mm->context = 0; return 0; } @@ -132,7 +101,6 @@ static inline void destroy_context (struct mm_struct *mm) { /* Nothing to do. */ - MMU_TRACE('D', smp_processor_id(), mm, mm->context); } static inline void @@ -171,19 +139,14 @@ activate_context (struct mm_struct *mm) do { context = get_mmu_context(mm); - MMU_TRACE('A', smp_processor_id(), mm, context); if (!cpu_isset(smp_processor_id(), mm->cpu_vm_mask)) cpu_set(smp_processor_id(), mm->cpu_vm_mask); reload_context(context); - MMU_TRACE('a', smp_processor_id(), mm, context); /* in the unlikely event of a TLB-flush by another thread, redo the load: */ } while (unlikely(context != mm->context)); } -#define deactivate_mm(tsk,mm) \ -do { \ - MMU_TRACE('d', smp_processor_id(), mm, mm->context); \ -} while (0) +#define deactivate_mm(tsk,mm) do { } while (0) /* * Switch from address space PREV to address space NEXT. --- linux-2.6.9-rc1/include/asm-ia64/numa.h 2004-08-24 00:02:26.000000000 -0700 +++ 25/include/asm-ia64/numa.h 2004-09-02 20:51:52.000000000 -0700 @@ -59,7 +59,7 @@ extern struct node_cpuid_s node_cpuid[NR */ extern u8 numa_slit[MAX_NUMNODES * MAX_NUMNODES]; -#define node_distance(from,to) (numa_slit[from * numnodes + to]) +#define node_distance(from,to) (numa_slit[(from) * numnodes + (to)]) extern int paddr_to_nid(unsigned long paddr); --- linux-2.6.9-rc1/include/asm-ia64/page.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/include/asm-ia64/page.h 2004-09-03 00:56:41.912788936 -0700 @@ -187,4 +187,6 @@ get_order (unsigned long size) (((current->personality & READ_IMPLIES_EXEC) != 0) \ ? VM_EXEC : 0)) +#define devmem_is_allowed(x) 1 + #endif /* _ASM_IA64_PAGE_H */ --- linux-2.6.9-rc1/include/asm-ia64/pgtable.h 2004-08-24 00:02:25.000000000 -0700 +++ 25/include/asm-ia64/pgtable.h 2004-09-02 20:51:52.000000000 -0700 @@ -520,7 +520,7 @@ do { \ # ifdef CONFIG_VIRTUAL_MEM_MAP /* arch mem_map init routine is needed due to holes in a virtual mem_map */ # define __HAVE_ARCH_MEMMAP_INIT - extern void memmap_init (struct page *start, unsigned long size, int nid, unsigned long zone, + extern void memmap_init (unsigned long size, int nid, unsigned long zone, unsigned long start_pfn); # endif /* CONFIG_VIRTUAL_MEM_MAP */ # endif /* !__ASSEMBLY__ */ --- linux-2.6.9-rc1/include/asm-ia64/processor.h 2004-08-24 00:02:26.000000000 -0700 +++ 25/include/asm-ia64/processor.h 2004-09-02 20:51:52.000000000 -0700 @@ -334,6 +334,26 @@ struct task_struct; /* Prepare to copy thread state - unlazy all lazy status */ #define prepare_to_copy(tsk) do { } while (0) +#ifdef CONFIG_NUMA +#define SD_NODE_INIT (struct sched_domain) { \ + .span = CPU_MASK_NONE, \ + .parent = NULL, \ + .groups = NULL, \ + .min_interval = 80, \ + .max_interval = 320, \ + .busy_factor = 320, \ + .imbalance_pct = 125, \ + .cache_hot_time = (10*1000000), \ + .cache_nice_tries = 1, \ + .per_cpu_gain = 100, \ + .flags = SD_BALANCE_EXEC \ + | SD_WAKE_BALANCE, \ + .last_balance = jiffies, \ + .balance_interval = 10, \ + .nr_balance_failed = 0, \ +} +#endif + /* * This is the mechanism for creating a new kernel thread. * --- linux-2.6.9-rc1/include/asm-ia64/ptrace.h 2004-08-24 00:03:09.000000000 -0700 +++ 25/include/asm-ia64/ptrace.h 2004-09-02 20:51:52.000000000 -0700 @@ -229,6 +229,15 @@ struct switch_stack { * the canonical representation by adding to instruction pointer. */ # define instruction_pointer(regs) ((regs)->cr_iip + ia64_psr(regs)->ri) +/* Conserve space in histogram by encoding slot bits in address + * bits 2 and 3 rather than bits 0 and 1. + */ +#define profile_pc(regs) \ +({ \ + unsigned long __ip = instruction_pointer(regs); \ + (__ip & ~3UL) + ((__ip & 3UL) << 2); \ +}) + /* given a pointer to a task_struct, return the user's pt_regs */ # define ia64_task_regs(t) (((struct pt_regs *) ((char *) (t) + IA64_STK_OFFSET)) - 1) # define ia64_psr(regs) ((struct ia64_psr *) &(regs)->cr_ipsr) --- linux-2.6.9-rc1/include/asm-ia64/sal.h 2004-08-24 00:03:38.000000000 -0700 +++ 25/include/asm-ia64/sal.h 2004-09-03 00:56:30.922459720 -0700 @@ -364,7 +364,7 @@ typedef struct sal_processor_static_info u64 cr[128]; u64 ar[128]; u64 rr[8]; - struct ia64_fpreg fr[128]; + struct ia64_fpreg __attribute__ ((packed)) fr[128]; } sal_processor_static_info_t; struct sal_cpuid_info { @@ -819,6 +819,16 @@ struct sal_ret_values { long r8; long r9; long r10; long r11; }; +#define IA64_SAL_OEMFUNC_MIN 0x02000000 +#define IA64_SAL_OEMFUNC_MAX 0x03ffffff + +extern int ia64_sal_oemcall(struct ia64_sal_retval *, u64, u64, u64, u64, u64, + u64, u64, u64); +extern int ia64_sal_oemcall_nolock(struct ia64_sal_retval *, u64, u64, u64, + u64, u64, u64, u64, u64); +extern int ia64_sal_oemcall_reentrant(struct ia64_sal_retval *, u64, u64, u64, + u64, u64, u64, u64, u64); + #endif /* __ASSEMBLY__ */ #endif /* _ASM_IA64_SAL_H */ --- linux-2.6.9-rc1/include/asm-ia64/semaphore.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/include/asm-ia64/semaphore.h 2004-09-02 20:51:52.000000000 -0700 @@ -15,21 +15,13 @@ struct semaphore { atomic_t count; int sleepers; wait_queue_head_t wait; -#if WAITQUEUE_DEBUG - long __magic; /* initialized by __SEM_DEBUG_INIT() */ -#endif }; -#if WAITQUEUE_DEBUG -# define __SEM_DEBUG_INIT(name) , (long) &(name).__magic -#else -# define __SEM_DEBUG_INIT(name) -#endif - -#define __SEMAPHORE_INITIALIZER(name,count) \ -{ \ - ATOMIC_INIT(count), 0, __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ - __SEM_DEBUG_INIT(name) \ +#define __SEMAPHORE_INITIALIZER(name, n) \ +{ \ + .count = ATOMIC_INIT(n), \ + .sleepers = 0, \ + .wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ } #define __MUTEX_INITIALIZER(name) __SEMAPHORE_INITIALIZER(name,1) @@ -70,9 +62,6 @@ extern void __up (struct semaphore * sem static inline void down (struct semaphore *sem) { -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); if (atomic_dec_return(&sem->count) < 0) __down(sem); @@ -87,9 +76,6 @@ down_interruptible (struct semaphore * s { int ret = 0; -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); if (atomic_dec_return(&sem->count) < 0) ret = __down_interruptible(sem); @@ -101,9 +87,6 @@ down_trylock (struct semaphore *sem) { int ret = 0; -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif if (atomic_dec_return(&sem->count) < 0) ret = __down_trylock(sem); return ret; @@ -112,9 +95,6 @@ down_trylock (struct semaphore *sem) static inline void up (struct semaphore * sem) { -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif if (atomic_inc_return(&sem->count) <= 0) __up(sem); } --- linux-2.6.9-rc1/include/asm-ia64/siginfo.h 2004-08-24 00:03:32.000000000 -0700 +++ 25/include/asm-ia64/siginfo.h 2004-09-02 20:51:52.000000000 -0700 @@ -56,6 +56,7 @@ typedef struct siginfo { int _status; /* exit code */ clock_t _utime; clock_t _stime; + struct rusage _rusage; } _sigchld; /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ --- linux-2.6.9-rc1/include/asm-ia64/sn/sn2/shubio.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/include/asm-ia64/sn/sn2/shubio.h 2004-09-02 20:51:52.000000000 -0700 @@ -3384,7 +3384,7 @@ typedef ii_icrb0_e_u_t icrbe_t; #define IO_PERF_SETS 32 -#if __KERNEL__ +#ifdef __KERNEL__ #include #include #include --- linux-2.6.9-rc1/include/asm-ia64/sn/sn_cpuid.h 2004-08-24 00:01:55.000000000 -0700 +++ 25/include/asm-ia64/sn/sn_cpuid.h 2004-09-02 20:51:52.000000000 -0700 @@ -4,7 +4,7 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 2000-2003 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2000-2004 Silicon Graphics, Inc. All rights reserved. */ @@ -93,6 +93,7 @@ */ #define cpu_physical_id_to_nasid(cpi) ((cpi) &0xfff) #define cpu_physical_id_to_slice(cpi) ((cpi>>12) & 3) +#define cpu_physical_id_to_coherence_id(cpi) (cpu_physical_id_to_nasid(cpi) >> 9) #define get_nasid() ((ia64_getreg(_IA64_REG_CR_LID) >> 16) & 0xfff) #define get_slice() ((ia64_getreg(_IA64_REG_CR_LID) >> 28) & 0xf) #define get_node_number(addr) (((unsigned long)(addr)>>38) & 0x7ff) @@ -172,6 +173,11 @@ extern short physical_node_map[]; /* i #define smp_physical_node_id() (cpuid_to_nasid(smp_processor_id())) +/* + * cpuid_to_coherence_id - convert a cpuid to the coherence domain id it + * resides on + */ +#define cpuid_to_coherence_id(cpuid) cpu_physical_id_to_coherence_id(cpu_physical_id(cpuid)) #endif /* _ASM_IA64_SN_SN_CPUID_H */ --- linux-2.6.9-rc1/include/asm-ia64/sn/xtalk/xtalk.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/include/asm-ia64/sn/xtalk/xtalk.h 2004-09-02 20:51:52.000000000 -0700 @@ -55,7 +55,7 @@ typedef struct xtalk_piomap_s *xtalk_pio /* * Kernel/driver only definitions */ -#if __KERNEL__ +#ifdef __KERNEL__ #include #include --- linux-2.6.9-rc1/include/asm-ia64/sn/xtalk/xwidget.h 2004-08-24 00:01:50.000000000 -0700 +++ 25/include/asm-ia64/sn/xtalk/xwidget.h 2004-09-02 20:51:52.000000000 -0700 @@ -183,7 +183,7 @@ typedef struct xwidget_hwid_s { /* Generic crosstalk widget initialization interface */ -#if __KERNEL__ +#ifdef __KERNEL__ extern int xwidget_driver_register(xwidget_part_num_t part_num, xwidget_mfg_num_t mfg_num, --- linux-2.6.9-rc1/include/asm-ia64/spinlock.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/include/asm-ia64/spinlock.h 2004-09-03 00:56:42.830649400 -0700 @@ -116,8 +116,18 @@ do { \ typedef struct { volatile int read_counter : 31; volatile int write_lock : 1; +#ifdef CONFIG_LOCKMETER + /* required for LOCKMETER since all bits in lock are used */ + /* and we need this storage for CPU and lock INDEX */ + unsigned lockmeter_magic; +#endif } rwlock_t; + +#ifdef CONFIG_LOCKMETER +#define RW_LOCK_UNLOCKED (rwlock_t) { 0, 0, 0 } +#else #define RW_LOCK_UNLOCKED (rwlock_t) { 0, 0 } +#endif #define rwlock_init(x) do { *(x) = RW_LOCK_UNLOCKED; } while(0) #define rwlock_is_locked(x) (*(volatile int *) (x) != 0) @@ -133,6 +143,48 @@ do { \ } \ } while (0) +#ifdef CONFIG_LOCKMETER +/* + * HACK: This works, but still have a timing window that affects performance: + * we see that no one owns the Write lock, then someone * else grabs for Write + * lock before we do a read_lock(). + * This means that on rare occasions our read_lock() will stall and spin-wait + * until we acquire for Read, instead of simply returning a trylock failure. + */ +static inline int _raw_read_trylock(rwlock_t *rw) +{ + if (rw->write_lock) { + return 0; + } else { + _raw_read_lock(rw); + return 1; + } +} + +static inline int _raw_write_trylock(rwlock_t *rw) +{ + if (!(rw->write_lock)) { + /* isn't currently write-locked... that looks promising... */ + if (test_and_set_bit(31, rw) == 0) { + /* now it is write-locked by me... */ + if (rw->read_counter) { + /* really read-locked, so release write-lock and fail */ + clear_bit(31, rw); + } else { + /* we've the the write-lock, no read-lockers... success! */ + barrier(); + return 1; + } + + } + } + + /* falls through ... fails to write-lock */ + barrier(); + return 0; +} +#endif + #define _raw_read_unlock(rw) \ do { \ rwlock_t *__read_lock_ptr = (rw); \ @@ -196,4 +248,25 @@ do { \ clear_bit(31, (x)); \ }) +#ifdef CONFIG_LOCKMETER +extern void _metered_spin_lock (spinlock_t *lock); +extern void _metered_spin_unlock(spinlock_t *lock); + +/* + * Use a less efficient, and inline, atomic_dec_and_lock() if lockmetering + * so we can see the callerPC of who is actually doing the spin_lock(). + * Otherwise, all we see is the generic rollup of all locks done by + * atomic_dec_and_lock(). + */ +static inline int atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock) +{ + _metered_spin_lock(lock); + if (atomic_dec_and_test(atomic)) + return 1; + _metered_spin_unlock(lock); + return 0; +} +#define ATOMIC_DEC_AND_LOCK +#endif + #endif /* _ASM_IA64_SPINLOCK_H */ --- linux-2.6.9-rc1/include/asm-ia64/tlbflush.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/include/asm-ia64/tlbflush.h 2004-09-03 00:56:30.923459568 -0700 @@ -48,22 +48,19 @@ local_finish_flush_tlb_mm (struct mm_str static inline void flush_tlb_mm (struct mm_struct *mm) { - MMU_TRACE('F', smp_processor_id(), mm, mm->context); if (!mm) - goto out; + return; mm->context = 0; if (atomic_read(&mm->mm_users) == 0) - goto out; /* happens as a result of exit_mmap() */ + return; /* happens as a result of exit_mmap() */ #ifdef CONFIG_SMP smp_flush_tlb_mm(mm); #else local_finish_flush_tlb_mm(mm); #endif - out: - MMU_TRACE('f', smp_processor_id(), mm, mm->context); } extern void flush_tlb_range (struct vm_area_struct *vma, unsigned long start, unsigned long end); --- linux-2.6.9-rc1/include/asm-ia64/tlb.h 2004-08-24 00:02:32.000000000 -0700 +++ 25/include/asm-ia64/tlb.h 2004-09-03 00:56:30.923459568 -0700 @@ -39,6 +39,7 @@ */ #include #include +#include #include #include --- linux-2.6.9-rc1/include/asm-ia64/uaccess.h 2004-08-24 00:02:25.000000000 -0700 +++ 25/include/asm-ia64/uaccess.h 2004-09-02 20:51:52.000000000 -0700 @@ -91,6 +91,42 @@ verify_area (int type, const void *addr, #define __put_user(x, ptr) __put_user_nocheck((__typeof__(*(ptr))) (x), (ptr), sizeof(*(ptr))) #define __get_user(x, ptr) __get_user_nocheck((x), (ptr), sizeof(*(ptr))) +extern long __put_user_unaligned_unknown (void); + +#define __put_user_unaligned(x, ptr) \ +({ \ + long __ret; \ + switch (sizeof(*(ptr))) { \ + case 1: __ret = __put_user((x), (ptr)); break; \ + case 2: __ret = (__put_user((x), (u8 __user *)(ptr))) \ + | (__put_user((x) >> 8, ((u8 __user *)(ptr) + 1))); break; \ + case 4: __ret = (__put_user((x), (u16 __user *)(ptr))) \ + | (__put_user((x) >> 16, ((u16 __user *)(ptr) + 1))); break; \ + case 8: __ret = (__put_user((x), (u32 __user *)(ptr))) \ + | (__put_user((x) >> 32, ((u32 __user *)(ptr) + 1))); break; \ + default: __ret = __put_user_unaligned_unknown(); \ + } \ + __ret; \ +}) + +extern long __get_user_unaligned_unknown (void); + +#define __get_user_unaligned(x, ptr) \ +({ \ + long __ret; \ + switch (sizeof(*(ptr))) { \ + case 1: __ret = __get_user((x), (ptr)); break; \ + case 2: __ret = (__get_user((x), (u8 __user *)(ptr))) \ + | (__get_user((x) >> 8, ((u8 __user *)(ptr) + 1))); break; \ + case 4: __ret = (__get_user((x), (u16 __user *)(ptr))) \ + | (__get_user((x) >> 16, ((u16 __user *)(ptr) + 1))); break; \ + case 8: __ret = (__get_user((x), (u32 __user *)(ptr))) \ + | (__get_user((x) >> 32, ((u32 __user *)(ptr) + 1))); break; \ + default: __ret = __get_user_unaligned_unknown(); \ + } \ + __ret; \ +}) + #ifdef ASM_SUPPORTED struct __large_struct { unsigned long buf[100]; }; # define __m(x) (*(struct __large_struct *)(x)) @@ -202,7 +238,8 @@ extern unsigned long __copy_user (void * #define __copy_to_user(to, from, n) __copy_user((to), (from), (n)) #define __copy_from_user(to, from, n) __copy_user((to), (from), (n)) - +#define __copy_to_user_inatomic __copy_to_user +#define __copy_from_user_inatomic __copy_from_user #define copy_to_user(to, from, n) __copy_tofrom_user((to), (from), (n), 1) #define copy_from_user(to, from, n) __copy_tofrom_user((to), (from), (n), 0) --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/addrspace.h 2004-09-03 00:57:17.404393392 -0700 @@ -0,0 +1,58 @@ +/* $Id$ */ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2001 by Hiroyuki Kondo + * + * Defitions for the address spaces of the M32R CPUs. + */ +#ifndef __ASM_M32R_ADDRSPACE_H +#define __ASM_M32R_ADDRSPACE_H + +/* + * Memory segments (32bit kernel mode addresses) + */ +#define KUSEG 0x00000000 +#define KSEG0 0x80000000 +#define KSEG1 0xa0000000 +#define KSEG2 0xc0000000 +#define KSEG3 0xe0000000 + +#define K0BASE KSEG0 + +/* + * Returns the kernel segment base of a given address + */ +#ifndef __ASSEMBLY__ +#define KSEGX(a) (((unsigned long)(a)) & 0xe0000000) +#else +#define KSEGX(a) ((a) & 0xe0000000) +#endif + +/* + * Returns the physical address of a KSEG0/KSEG1 address + */ +#ifndef __ASSEMBLY__ +#define PHYSADDR(a) (((unsigned long)(a)) & 0x1fffffff) +#else +#define PHYSADDR(a) ((a) & 0x1fffffff) +#endif + +/* + * Map an address to a certain kernel segment + */ +#ifndef __ASSEMBLY__ +#define KSEG0ADDR(a) ((__typeof__(a))(((unsigned long)(a) & 0x1fffffff) | KSEG0)) +#define KSEG1ADDR(a) ((__typeof__(a))(((unsigned long)(a) & 0x1fffffff) | KSEG1)) +#define KSEG2ADDR(a) ((__typeof__(a))(((unsigned long)(a) & 0x1fffffff) | KSEG2)) +#define KSEG3ADDR(a) ((__typeof__(a))(((unsigned long)(a) & 0x1fffffff) | KSEG3)) +#else +#define KSEG0ADDR(a) (((a) & 0x1fffffff) | KSEG0) +#define KSEG1ADDR(a) (((a) & 0x1fffffff) | KSEG1) +#define KSEG2ADDR(a) (((a) & 0x1fffffff) | KSEG2) +#define KSEG3ADDR(a) (((a) & 0x1fffffff) | KSEG3) +#endif + +#endif /* __ASM_M32R_ADDRSPACE_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/a.out.h 2004-09-03 00:57:17.404393392 -0700 @@ -0,0 +1,28 @@ +#ifndef _ASM_M32R_A_OUT_H +#define _ASM_M32R_A_OUT_H + +/* orig : i386 2.4.18 */ + +struct exec +{ + unsigned long a_info; /* Use macros N_MAGIC, etc for access */ + unsigned a_text; /* length of text, in bytes */ + unsigned a_data; /* length of data, in bytes */ + unsigned a_bss; /* length of uninitialized data area for file, in bytes */ + unsigned a_syms; /* length of symbol table data in file, in bytes */ + unsigned a_entry; /* start address */ + unsigned a_trsize; /* length of relocation info for text, in bytes */ + unsigned a_drsize; /* length of relocation info for data, in bytes */ +}; + +#define N_TRSIZE(a) ((a).a_trsize) +#define N_DRSIZE(a) ((a).a_drsize) +#define N_SYMSIZE(a) ((a).a_syms) + +#ifdef __KERNEL__ + +#define STACK_TOP TASK_SIZE + +#endif + +#endif /* _ASM_M32R_A_OUT_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/assembler.h 2004-09-03 00:57:17.405393240 -0700 @@ -0,0 +1,212 @@ +#ifndef _ASM_M32R_ASSEMBLER_H +#define _ASM_M32R_ASSEMBLER_H + +/* $Id$ */ + +/* + * linux/asm-m32r/assembler.h + * + * This file contains M32R architecture specific defines. + * + * Do not include any C declarations in this file - it is included by + * assembler source. + */ + +#include + + +#undef ENTRY +#define ENTRY(name) ENTRY_M name + .macro ENTRY_M name + .global \name + ALIGN +\name: + .endm + +/* + * LDIMM: load immediate value + * + * STI: enable interruption + * CLI: disable interruption + */ + +#ifdef __ASSEMBLY__ + +#define LDIMM(reg,x) LDIMM reg x + .macro LDIMM reg x + seth \reg, #high(\x) + or3 \reg, \reg, #low(\x) + .endm + +#if !defined(CONFIG_CHIP_M32102) +#define STI(reg) STI_M reg + .macro STI_M reg + setpsw #0x40 -> nop + ; WORKAROUND: "-> nop" is a workaround for the M32700(TS1). + .endm + +#define CLI(reg) CLI_M reg + .macro CLI_M reg + clrpsw #0x40 -> nop + ; WORKAROUND: "-> nop" is a workaround for the M32700(TS1). + .endm +#else /* CONFIG_CHIP_M32102 */ +#define STI(reg) STI_M reg + .macro STI_M reg + mvfc \reg, psw + or3 \reg, \reg, #0x0040 + mvtc \reg, psw + .endm + +#define CLI(reg) CLI_M reg + .macro CLI_M reg + mvfc \reg, psw + and3 \reg, \reg, #0xffbf + mvtc \reg, psw + .endm +#endif /* CONFIG_CHIP_M32102 */ + + .macro SAVE_ALL + push r0 ; orig_r0 + push sp ; spi (r15) + push lr ; r14 + push r13 + mvfc r13, cr3 ; spu + push r13 + mvfc r13, bbpc + push r13 + mvfc r13, bbpsw + push r13 + mvfc r13, bpc + push r13 + mvfc r13, psw + push r13 +#if defined(CONFIG_ISA_M32R2) && defined(CONFIG_ISA_DSP_LEVEL2) + mvfaclo r13, a1 + push r13 + mvfachi r13, a1 + push r13 + mvfaclo r13, a0 + push r13 + mvfachi r13, a0 + push r13 +#elif defined(CONFIG_ISA_M32R2) || defined(CONFIG_ISA_M32R) + mvfaclo r13 + push r13 + mvfachi r13 + push r13 +#else +#error unknown isa configuration +#endif + ldi r13, #-1 + push r13 ; syscall_nr (default: -1) + push r12 + push r11 + push r10 + push r9 + push r8 + push r7 + push r3 + push r2 + push r1 + push r0 + addi sp, #-4 ; room for implicit pt_regs parameter + push r6 + push r5 + push r4 + .endm + + .macro RESTORE_ALL + pop r4 + pop r5 + pop r6 + addi sp, #4 + pop r0 + pop r1 + pop r2 + pop r3 + pop r7 + pop r8 + pop r9 + pop r10 + pop r11 + pop r12 + addi r15, #4 ; Skip syscall number +#if defined(CONFIG_ISA_M32R2) && defined(CONFIG_ISA_DSP_LEVEL2) + pop r13 + mvtachi r13, a0 + pop r13 + mvtaclo r13, a0 + pop r13 + mvtachi r13, a1 + pop r13 + mvtaclo r13, a1 +#elif defined(CONFIG_ISA_M32R2) || defined(CONFIG_ISA_M32R) + pop r13 + mvtachi r13 + pop r13 + mvtaclo r13 +#else +#error unknown isa configuration +#endif + pop r14 + mvtc r14, psw + pop r14 + mvtc r14, bpc + addi sp, #8 ; Skip bbpsw, bbpc + pop r14 + mvtc r14, cr3 ; spu + pop r13 + pop lr ; r14 + pop sp ; spi (r15) + addi sp, #4 ; Skip orig_r0 + .fillinsn +1: rte + .section .fixup,"ax" +2: bl do_exit + .previous + .section __ex_table,"a" + ALIGN + .long 1b, 2b + .previous + .endm + +#define GET_CURRENT(reg) get_current reg + .macro get_current reg + ldi \reg, #-8192 + and \reg, sp + .endm + +#if !defined(CONFIG_CHIP_M32102) + .macro SWITCH_TO_KERNEL_STACK + ; switch to kernel stack (spi) + clrpsw #0x80 -> nop + .endm +#else /* CONFIG_CHIP_M32102 */ + .macro SWITCH_TO_KERNEL_STACK + push r0 ; save r0 for working + mvfc r0, psw + and3 r0, r0, #0x00ff7f + mvtc r0, psw + slli r0, #16 + bltz r0, 1f ; check BSM-bit +; + ;; called from kernel context: previous stack = spi + pop r0 ; retrieve r0 + bra 2f + .fillinsn +1: + ;; called from user context: previous stack = spu + mvfc r0, cr3 ; spu + addi r0, #4 + mvtc r0, cr3 ; spu + ld r0, @(-4,r0) ; retrieve r0 + .fillinsn +2: + .endm +#endif /* CONFIG_CHIP_M32102 */ + +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_M32R_ASSEMBLER_H */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/atomic.h 2004-09-03 00:57:17.407392936 -0700 @@ -0,0 +1,337 @@ +#ifndef _ASM_M32R_ATOMIC_H +#define _ASM_M32R_ATOMIC_H + +/* $Id$ */ + +/* + * linux/include/asm-m32r/atomic.h + * orig : i386 2.4.10 + * + * M32R version: + * Copyright (C) 2001, 2002 Hitoshi Yamamoto + */ + +#include +#include + +/* + * Atomic operations that C can't guarantee us. Useful for + * resource counting etc.. + */ + +#undef LOAD +#undef STORE +#ifdef CONFIG_SMP +#define LOAD "lock" +#define STORE "unlock" +#else +#define LOAD "ld" +#define STORE "st" +#endif + +/* + * Make sure gcc doesn't try to be clever and move things around + * on us. We need to use _exactly_ the address the user gave us, + * not some alias that contains the same information. + */ +typedef struct { volatile int counter; } atomic_t; + +#define ATOMIC_INIT(i) { (i) } + +/** + * atomic_read - read atomic variable + * @v: pointer of type atomic_t + * + * Atomically reads the value of @v. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define atomic_read(v) ((v)->counter) + +/** + * atomic_set - set atomic variable + * @v: pointer of type atomic_t + * @i: required value + * + * Atomically sets the value of @v to @i. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define atomic_set(v,i) (((v)->counter) = (i)) + +/** + * atomic_add - add integer to atomic variable + * @i: integer value to add + * @v: pointer of type atomic_t + * + * Atomically adds @i to @v. Note that the guaranteed useful range + * of an atomic_t is only 24 bits. + */ +static __inline__ void atomic_add(int i, atomic_t *v) +{ + unsigned long flags; + + local_irq_save(flags); + __asm__ __volatile__ ( + "# atomic_add \n\t" + DCACHE_CLEAR("r4", "r5", "%0") + LOAD" r4, @%0; \n\t" + "add r4, %1; \n\t" + STORE" r4, @%0; \n\t" + : /* no outputs */ + : "r" (&v->counter), "r" (i) + : "memory", "r4" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r5" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + local_irq_restore(flags); +} + +/** + * atomic_sub - subtract the atomic variable + * @i: integer value to subtract + * @v: pointer of type atomic_t + * + * Atomically subtracts @i from @v. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static __inline__ void atomic_sub(int i, atomic_t *v) +{ + unsigned long flags; + + local_irq_save(flags); + __asm__ __volatile__ ( + "# atomic_sub \n\t" + DCACHE_CLEAR("r4", "r5", "%0") + LOAD" r4, @%0; \n\t" + "sub r4, %1; \n\t" + STORE" r4, @%0; \n\t" + : /* no outputs */ + : "r" (&v->counter), "r" (i) + : "memory", "r4" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r5" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + local_irq_restore(flags); +} + +/** + * atomic_sub_and_test - subtract value from variable and test result + * @i: integer value to subtract + * @v: pointer of type atomic_t + * + * Atomically subtracts @i from @v and returns + * true if the result is zero, or false for all + * other cases. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static __inline__ int atomic_sub_and_test(int i, atomic_t *v) +{ + unsigned long flags; + int result; + + local_irq_save(flags); + __asm__ __volatile__ ( + "# atomic_sub_and_test \n\t" + DCACHE_CLEAR("%0", "r4", "%1") + LOAD" %0, @%1; \n\t" + "sub %0, %2; \n\t" + STORE" %0, @%1; \n\t" + : "=&r" (result) + : "r" (&v->counter), "r" (i) + : "memory" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r4" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + local_irq_restore(flags); + + return (result == 0); +} + +/** + * atomic_inc - increment atomic variable + * @v: pointer of type atomic_t + * + * Atomically increments @v by 1. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static __inline__ void atomic_inc(atomic_t *v) +{ + unsigned long flags; + + local_irq_save(flags); + __asm__ __volatile__ ( + "# atomic_inc \n\t" + DCACHE_CLEAR("r4", "r5", "%0") + LOAD" r4, @%0; \n\t" + "addi r4, #1; \n\t" + STORE" r4, @%0; \n\t" + : /* no outputs */ + : "r" (&v->counter) + : "memory", "r4" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r5" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + local_irq_restore(flags); +} + +/** + * atomic_dec - decrement atomic variable + * @v: pointer of type atomic_t + * + * Atomically decrements @v by 1. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static __inline__ void atomic_dec(atomic_t *v) +{ + unsigned long flags; + + local_irq_save(flags); + __asm__ __volatile__ ( + "# atomic_dec \n\t" + DCACHE_CLEAR("r4", "r5", "%0") + LOAD" r4, @%0; \n\t" + "addi r4, #-1; \n\t" + STORE" r4, @%0; \n\t" + : /* no outputs */ + : "r" (&v->counter) + : "memory", "r4" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r5" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + local_irq_restore(flags); +} + +/** + * atomic_dec_and_test - decrement and test + * @v: pointer of type atomic_t + * + * Atomically decrements @v by 1 and + * returns true if the result is 0, or false for all other + * cases. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static __inline__ int atomic_dec_and_test(atomic_t *v) +{ + unsigned long flags; + int result; + + local_irq_save(flags); + __asm__ __volatile__ ( + "# atomic_dec_and_test \n\t" + DCACHE_CLEAR("%0", "r4", "%1") + LOAD" %0, @%1; \n\t" + "addi %0, #-1; \n\t" + STORE" %0, @%1; \n\t" + : "=&r" (result) + : "r" (&v->counter) + : "memory" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r4" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + local_irq_restore(flags); + + return (result == 0); +} + +/** + * atomic_inc_and_test - increment and test + * @v: pointer of type atomic_t + * + * Atomically increments @v by 1 + * and returns true if the result is zero, or false for all + * other cases. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static __inline__ int atomic_inc_and_test(atomic_t *v) +{ + unsigned long flags; + int result; + + local_irq_save(flags); + __asm__ __volatile__ ( + "# atomic_dec_and_test \n\t" + DCACHE_CLEAR("%0", "r4", "%1") + LOAD" %0, @%1; \n\t" + "addi %0, #1; \n\t" + STORE" %0, @%1; \n\t" + : "=&r" (result) + : "r" (&v->counter) + : "memory" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r4" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + local_irq_restore(flags); + + return (result == 0); +} + +/** + * atomic_add_negative - add and test if negative + * @v: pointer of type atomic_t + * @i: integer value to add + * + * Atomically adds @i to @v and returns true + * if the result is negative, or false when + * result is greater than or equal to zero. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static __inline__ int atomic_add_negative(int i, atomic_t *v) +{ + unsigned long flags; + int result; + + local_irq_save(flags); + __asm__ __volatile__ ( + "# atomic_add_negative \n\t" + DCACHE_CLEAR("%0", "r4", "%1") + LOAD" %0, @%1; \n\t" + "add %0, %2; \n\t" + STORE" %0, @%1; \n\t" + : "=&r" (result) + : "r" (&v->counter), "r" (i) + : "memory" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r4" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + local_irq_restore(flags); + + return (result < 0); +} + +/* These are x86-specific, used by some header files */ +static __inline__ void atomic_set_mask(unsigned long mask, atomic_t *addr) +{ + unsigned long flags; + + local_irq_save(flags); + __asm__ __volatile__ ( + "# atomic_set_mask \n\t" + DCACHE_CLEAR("r4", "r5", "%0") + LOAD" r4, @%0; \n\t" + "or r4, %1; \n\t" + STORE" r4, @%0; \n\t" + : /* no outputs */ + : "r" (addr), "r" (mask) + : "memory", "r4" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r5" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + local_irq_restore(flags); +} + +/* Atomic operations are already serializing on x86 */ +#define smp_mb__before_atomic_dec() barrier() +#define smp_mb__after_atomic_dec() barrier() +#define smp_mb__before_atomic_inc() barrier() +#define smp_mb__after_atomic_inc() barrier() + +#endif /* _ASM_M32R_ATOMIC_H */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/bitops.h 2004-09-03 00:57:18.736190928 -0700 @@ -0,0 +1,712 @@ +#ifndef _ASM_M32R_BITOPS_H +#define _ASM_M32R_BITOPS_H + +/* $Id$ */ + +/* + * linux/include/asm-m32r/bitops.h + * orig : i386 2.4.10 + * + * Copyright 1992, Linus Torvalds. + * + * M32R version: + * Copyright (C) 2001, 2002 Hitoshi Yamamoto + * Copyright (C) 2004 Hirokazu Takata + */ + +#include +#include +#include +#include +#include + +/* + * These have to be done with inline assembly: that way the bit-setting + * is guaranteed to be atomic. All bit operations return 0 if the bit + * was cleared before the operation and != 0 if it was not. + * + * bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1). + */ + +#undef LOAD +#undef STORE +#ifdef CONFIG_SMP +#define LOAD "lock" +#define STORE "unlock" +#else +#define LOAD "ld" +#define STORE "st" +#endif + +/* #define ADDR (*(volatile long *) addr) */ + +/** + * set_bit - Atomically set a bit in memory + * @nr: the bit to set + * @addr: the address to start counting from + * + * This function is atomic and may not be reordered. See __set_bit() + * if you do not require the atomic guarantees. + * Note that @nr may be almost arbitrarily large; this function is not + * restricted to acting on a single-word quantity. + */ +static __inline__ void set_bit(int nr, volatile void * addr) +{ + __u32 mask; + volatile __u32 *a = addr; + unsigned long flags; + + a += (nr >> 5); + mask = (1 << (nr & 0x1F)); + + local_irq_save(flags); + __asm__ __volatile__ ( + DCACHE_CLEAR("r4", "r6", "%0") + LOAD" r4, @%0; \n\t" + "or r4, %1; \n\t" + STORE" r4, @%0; \n\t" + : /* no outputs */ + : "r" (a), "r" (mask) + : "memory", "r4" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r6" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + local_irq_restore(flags); +} + +/** + * __set_bit - Set a bit in memory + * @nr: the bit to set + * @addr: the address to start counting from + * + * Unlike set_bit(), this function is non-atomic and may be reordered. + * If it's called on the same region of memory simultaneously, the effect + * may be that only one operation succeeds. + */ +static __inline__ void __set_bit(int nr, volatile void * addr) +{ + __u32 mask; + volatile __u32 *a = addr; + + a += (nr >> 5); + mask = (1 << (nr & 0x1F)); + *a |= mask; +} + +/** + * clear_bit - Clears a bit in memory + * @nr: Bit to clear + * @addr: Address to start counting from + * + * clear_bit() is atomic and may not be reordered. However, it does + * not contain a memory barrier, so if it is used for locking purposes, + * you should call smp_mb__before_clear_bit() and/or smp_mb__after_clear_bit() + * in order to ensure changes are visible on other processors. + */ +static __inline__ void clear_bit(int nr, volatile void * addr) +{ + __u32 mask; + volatile __u32 *a = addr; + unsigned long flags; + + a += (nr >> 5); + mask = (1 << (nr & 0x1F)); + + local_irq_save(flags); + + __asm__ __volatile__ ( + DCACHE_CLEAR("r4", "r6", "%0") + LOAD" r4, @%0; \n\t" + "and r4, %1; \n\t" + STORE" r4, @%0; \n\t" + : /* no outputs */ + : "r" (a), "r" (~mask) + : "memory", "r4" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r6" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + local_irq_restore(flags); +} + +static __inline__ void __clear_bit(int nr, volatile unsigned long * addr) +{ + unsigned long mask; + volatile unsigned long *a = addr; + + a += (nr >> 5); + mask = (1 << (nr & 0x1F)); + *a &= ~mask; +} + +#define smp_mb__before_clear_bit() barrier() +#define smp_mb__after_clear_bit() barrier() + +/** + * __change_bit - Toggle a bit in memory + * @nr: the bit to set + * @addr: the address to start counting from + * + * Unlike change_bit(), this function is non-atomic and may be reordered. + * If it's called on the same region of memory simultaneously, the effect + * may be that only one operation succeeds. + */ +static __inline__ void __change_bit(int nr, volatile void * addr) +{ + __u32 mask; + volatile __u32 *a = addr; + + a += (nr >> 5); + mask = (1 << (nr & 0x1F)); + *a ^= mask; +} + +/** + * change_bit - Toggle a bit in memory + * @nr: Bit to clear + * @addr: Address to start counting from + * + * change_bit() is atomic and may not be reordered. + * Note that @nr may be almost arbitrarily large; this function is not + * restricted to acting on a single-word quantity. + */ +static __inline__ void change_bit(int nr, volatile void * addr) +{ + __u32 mask; + volatile __u32 *a = addr; + unsigned long flags; + + a += (nr >> 5); + mask = (1 << (nr & 0x1F)); + + local_irq_save(flags); + __asm__ __volatile__ ( + DCACHE_CLEAR("r4", "r6", "%0") + LOAD" r4, @%0; \n\t" + "xor r4, %1; \n\t" + STORE" r4, @%0; \n\t" + : /* no outputs */ + : "r" (a), "r" (mask) + : "memory", "r4" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r6" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + local_irq_restore(flags); +} + +/** + * test_and_set_bit - Set a bit and return its old value + * @nr: Bit to set + * @addr: Address to count from + * + * This operation is atomic and cannot be reordered. + * It also implies a memory barrier. + */ +static __inline__ int test_and_set_bit(int nr, volatile void * addr) +{ + __u32 mask, oldbit; + volatile __u32 *a = addr; + unsigned long flags; + + a += (nr >> 5); + mask = (1 << (nr & 0x1F)); + + local_irq_save(flags); + __asm__ __volatile__ ( + DCACHE_CLEAR("%0", "r4", "%1") + LOAD" %0, @%1; \n\t" + "mv r4, %0; \n\t" + "and %0, %2; \n\t" + "or r4, %2; \n\t" + STORE" r4, @%1; \n\t" + : "=&r" (oldbit) + : "r" (a), "r" (mask) + : "memory", "r4" + ); + local_irq_restore(flags); + return (oldbit != 0); +} + +/** + * __test_and_set_bit - Set a bit and return its old value + * @nr: Bit to set + * @addr: Address to count from + * + * This operation is non-atomic and can be reordered. + * If two examples of this operation race, one can appear to succeed + * but actually fail. You must protect multiple accesses with a lock. + */ +static __inline__ int __test_and_set_bit(int nr, volatile void * addr) +{ + __u32 mask, oldbit; + volatile __u32 *a = addr; + + a += (nr >> 5); + mask = (1 << (nr & 0x1F)); + oldbit = (*a & mask); + *a |= mask; + + return (oldbit != 0); +} + +/** + * test_and_clear_bit - Clear a bit and return its old value + * @nr: Bit to set + * @addr: Address to count from + * + * This operation is atomic and cannot be reordered. + * It also implies a memory barrier. + */ +static __inline__ int test_and_clear_bit(int nr, volatile void * addr) +{ + __u32 mask, oldbit; + volatile __u32 *a = addr; + unsigned long flags; + + a += (nr >> 5); + mask = (1 << (nr & 0x1F)); + + local_irq_save(flags); + + __asm__ __volatile__ ( + DCACHE_CLEAR("%0", "r4", "%2") + LOAD" %0, @%2; \n\t" + "mv r4, %0; \n\t" + "and %0, %1; \n\t" + "not %1, %1; \n\t" + "and r4, %1; \n\t" + STORE" r4, @%2; \n\t" + : "=&r" (oldbit), "+r" (mask) + : "r" (a) + : "memory", "r4" + ); + local_irq_restore(flags); + + return (oldbit != 0); +} + +/** + * __test_and_clear_bit - Clear a bit and return its old value + * @nr: Bit to set + * @addr: Address to count from + * + * This operation is non-atomic and can be reordered. + * If two examples of this operation race, one can appear to succeed + * but actually fail. You must protect multiple accesses with a lock. + */ +static __inline__ int __test_and_clear_bit(int nr, volatile void * addr) +{ + __u32 mask, oldbit; + volatile __u32 *a = addr; + + a += (nr >> 5); + mask = (1 << (nr & 0x1F)); + oldbit = (*a & mask); + *a &= ~mask; + + return (oldbit != 0); +} + +/* WARNING: non atomic and it can be reordered! */ +static __inline__ int __test_and_change_bit(int nr, volatile void * addr) +{ + __u32 mask, oldbit; + volatile __u32 *a = addr; + + a += (nr >> 5); + mask = (1 << (nr & 0x1F)); + oldbit = (*a & mask); + *a ^= mask; + + return (oldbit != 0); +} + +/** + * test_and_change_bit - Change a bit and return its old value + * @nr: Bit to set + * @addr: Address to count from + * + * This operation is atomic and cannot be reordered. + * It also implies a memory barrier. + */ +static __inline__ int test_and_change_bit(int nr, volatile void * addr) +{ + __u32 mask, oldbit; + volatile __u32 *a = addr; + unsigned long flags; + + a += (nr >> 5); + mask = (1 << (nr & 0x1F)); + + local_irq_save(flags); + __asm__ __volatile__ ( + DCACHE_CLEAR("%0", "r4", "%1") + LOAD" %0, @%1; \n\t" + "mv r4, %0; \n\t" + "and %0, %2; \n\t" + "xor r4, %2; \n\t" + STORE" r4, @%1; \n\t" + : "=&r" (oldbit) + : "r" (a), "r" (mask) + : "memory", "r4" + ); + local_irq_restore(flags); + return (oldbit != 0); +} + +#if 0 /* Fool kernel-doc since it doesn't do macros yet */ +/** + * test_bit - Determine whether a bit is set + * @nr: bit number to test + * @addr: Address to start counting from + */ +static int test_bit(int nr, const volatile void * addr); +#endif + +static __inline__ int test_bit(int nr, const volatile void * addr) +{ + __u32 mask; + const volatile __u32 *a = addr; + + a += (nr >> 5); + mask = (1 << (nr & 0x1F)); + + return ((*a & mask) != 0); +} + +/** + * ffz - find first zero in word. + * @word: The word to search + * + * Undefined if no zero exists, so code should check against ~0UL first. + */ +static __inline__ unsigned long ffz(unsigned long word) +{ + int k; + + word = ~word; + k = 0; + if (!(word & 0x0000ffff)) { k += 16; word >>= 16; } + if (!(word & 0x000000ff)) { k += 8; word >>= 8; } + if (!(word & 0x0000000f)) { k += 4; word >>= 4; } + if (!(word & 0x00000003)) { k += 2; word >>= 2; } + if (!(word & 0x00000001)) { k += 1; } + + return k; +} + +/** + * find_first_zero_bit - find the first zero bit in a memory region + * @addr: The address to start the search at + * @size: The maximum size to search + * + * Returns the bit-number of the first zero bit, not the number of the byte + * containing a bit. + */ + +#define find_first_zero_bit(addr, size) \ + find_next_zero_bit((addr), (size), 0) + +/** + * find_next_zero_bit - find the first zero bit in a memory region + * @addr: The address to base the search on + * @offset: The bitnumber to start searching at + * @size: The maximum size to search + */ +static __inline__ int find_next_zero_bit(void *addr, int size, int offset) +{ + unsigned long *p = ((unsigned long *) addr) + (offset >> 5); + unsigned long result = offset & ~31UL; + unsigned long tmp; + + if (offset >= size) + return size; + size -= result; + offset &= 31UL; + if (offset) { + tmp = *(p++); + tmp |= ~0UL >> (32-offset); + if (size < 32) + goto found_first; + if (~tmp) + goto found_middle; + size -= 32; + result += 32; + } + while (size & ~31UL) { + if (~(tmp = *(p++))) + goto found_middle; + result += 32; + size -= 32; + } + if (!size) + return result; + tmp = *p; + +found_first: + tmp |= ~0UL << size; +found_middle: + return result + ffz(tmp); +} + +/** + * __ffs - find first bit in word. + * @word: The word to search + * + * Undefined if no bit exists, so code should check against 0 first. + */ +static __inline__ unsigned long __ffs(unsigned long word) +{ + int k = 0; + + if (!(word & 0x0000ffff)) { k += 16; word >>= 16; } + if (!(word & 0x000000ff)) { k += 8; word >>= 8; } + if (!(word & 0x0000000f)) { k += 4; word >>= 4; } + if (!(word & 0x00000003)) { k += 2; word >>= 2; } + if (!(word & 0x00000001)) { k += 1;} + + return k; +} + +/* + * fls: find last bit set. + */ +#define fls(x) generic_fls(x) + +#ifdef __KERNEL__ + +/* + * Every architecture must define this function. It's the fastest + * way of searching a 140-bit bitmap where the first 100 bits are + * unlikely to be set. It's guaranteed that at least one of the 140 + * bits is cleared. + */ +static __inline__ int sched_find_first_bit(unsigned long *b) +{ + if (unlikely(b[0])) + return __ffs(b[0]); + if (unlikely(b[1])) + return __ffs(b[1]) + 32; + if (unlikely(b[2])) + return __ffs(b[2]) + 64; + if (b[3]) + return __ffs(b[3]) + 96; + return __ffs(b[4]) + 128; +} + +/** + * find_next_bit - find the first set bit in a memory region + * @addr: The address to base the search on + * @offset: The bitnumber to start searching at + * @size: The maximum size to search + */ +static __inline__ unsigned long find_next_bit(const unsigned long *addr, + unsigned long size, unsigned long offset) +{ + unsigned int *p = ((unsigned int *) addr) + (offset >> 5); + unsigned int result = offset & ~31UL; + unsigned int tmp; + + if (offset >= size) + return size; + size -= result; + offset &= 31UL; + if (offset) { + tmp = *p++; + tmp &= ~0UL << offset; + if (size < 32) + goto found_first; + if (tmp) + goto found_middle; + size -= 32; + result += 32; + } + while (size >= 32) { + if ((tmp = *p++) != 0) + goto found_middle; + result += 32; + size -= 32; + } + if (!size) + return result; + tmp = *p; + +found_first: + tmp &= ~0UL >> (32 - size); + if (tmp == 0UL) /* Are any bits set? */ + return result + size; /* Nope. */ +found_middle: + return result + __ffs(tmp); +} + +/** + * find_first_bit - find the first set bit in a memory region + * @addr: The address to start the search at + * @size: The maximum size to search + * + * Returns the bit-number of the first set bit, not the number of the byte + * containing a bit. + */ +#define find_first_bit(addr, size) \ + find_next_bit((addr), (size), 0) + +/** + * ffs - find first bit set + * @x: the word to search + * + * This is defined the same way as + * the libc and compiler builtin ffs routines, therefore + * differs in spirit from the above ffz (man ffs). + */ +#define ffs(x) generic_ffs(x) + +/** + * hweightN - returns the hamming weight of a N-bit word + * @x: the word to weigh + * + * The Hamming Weight of a number is the total number of bits set in it. + */ + +#define hweight32(x) generic_hweight32(x) +#define hweight16(x) generic_hweight16(x) +#define hweight8(x) generic_hweight8(x) + +#endif /* __KERNEL__ */ + +#ifdef __KERNEL__ + +/* + * ext2_XXXX function + * orig: include/asm-sh/bitops.h + */ + +#ifdef __LITTLE_ENDIAN__ +#define ext2_set_bit test_and_set_bit +#define ext2_clear_bit __test_and_clear_bit +#define ext2_test_bit test_bit +#define ext2_find_first_zero_bit find_first_zero_bit +#define ext2_find_next_zero_bit find_next_zero_bit +#else +static __inline__ int ext2_set_bit(int nr, volatile void * addr) +{ + __u8 mask, oldbit; + volatile __u8 *a = addr; + + a += (nr >> 3); + mask = (1 << (nr & 0x07)); + oldbit = (*a & mask); + *a |= mask; + + return (oldbit != 0); +} + +static __inline__ int ext2_clear_bit(int nr, volatile void * addr) +{ + __u8 mask, oldbit; + volatile __u8 *a = addr; + + a += (nr >> 3); + mask = (1 << (nr & 0x07)); + oldbit = (*a & mask); + *a &= ~mask; + + return (oldbit != 0); +} + +static __inline__ int ext2_test_bit(int nr, const volatile void * addr) +{ + __u32 mask; + const volatile __u8 *a = addr; + + a += (nr >> 3); + mask = (1 << (nr & 0x07)); + + return ((mask & *a) != 0); +} + +#define ext2_find_first_zero_bit(addr, size) \ + ext2_find_next_zero_bit((addr), (size), 0) + +static __inline__ unsigned long ext2_find_next_zero_bit(void *addr, + unsigned long size, unsigned long offset) +{ + unsigned long *p = ((unsigned long *) addr) + (offset >> 5); + unsigned long result = offset & ~31UL; + unsigned long tmp; + + if (offset >= size) + return size; + size -= result; + offset &= 31UL; + if(offset) { + /* We hold the little endian value in tmp, but then the + * shift is illegal. So we could keep a big endian value + * in tmp, like this: + * + * tmp = __swab32(*(p++)); + * tmp |= ~0UL >> (32-offset); + * + * but this would decrease preformance, so we change the + * shift: + */ + tmp = *(p++); + tmp |= __swab32(~0UL >> (32-offset)); + if(size < 32) + goto found_first; + if(~tmp) + goto found_middle; + size -= 32; + result += 32; + } + while(size & ~31UL) { + if(~(tmp = *(p++))) + goto found_middle; + result += 32; + size -= 32; + } + if(!size) + return result; + tmp = *p; + +found_first: + /* tmp is little endian, so we would have to swab the shift, + * see above. But then we have to swab tmp below for ffz, so + * we might as well do this here. + */ + return result + ffz(__swab32(tmp) | (~0UL << size)); +found_middle: + return result + ffz(__swab32(tmp)); +} +#endif + +#define ext2_set_bit_atomic(lock, nr, addr) \ + ({ \ + int ret; \ + spin_lock(lock); \ + ret = ext2_set_bit((nr), (addr)); \ + spin_unlock(lock); \ + ret; \ + }) + +#define ext2_clear_bit_atomic(lock, nr, addr) \ + ({ \ + int ret; \ + spin_lock(lock); \ + ret = ext2_clear_bit((nr), (addr)); \ + spin_unlock(lock); \ + ret; \ + }) + +/* Bitmap functions for the minix filesystem. */ +#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr) +#define minix_set_bit(nr,addr) __set_bit(nr,addr) +#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr) +#define minix_test_bit(nr,addr) test_bit(nr,addr) +#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size) + +#endif /* __KERNEL__ */ + +#endif /* _ASM_M32R_BITOPS_H */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/bug.h 2004-09-03 00:57:17.410392480 -0700 @@ -0,0 +1,22 @@ +#ifndef _M32R_BUG_H +#define _M32R_BUG_H + +#define BUG() do { \ + printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ +} while (0) + +#define PAGE_BUG(page) do { BUG(); } while (0) + +#define BUG_ON(condition) \ + do { if (unlikely((condition)!=0)) BUG(); } while(0) + +#define WARN_ON(condition) do { \ + if (unlikely((condition)!=0)) { \ + printk("Badness in %s at %s:%d\n", __FUNCTION__, \ + __FILE__, __LINE__); \ + dump_stack(); \ + } \ +} while (0) + +#endif /* _M32R_BUG_H */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/bugs.h 2004-09-03 00:57:17.410392480 -0700 @@ -0,0 +1,21 @@ +#ifndef _ASM_M32R_BUGS_H +#define _ASM_M32R_BUGS_H + +/* $Id$ */ + +/* + * This is included by init/main.c to check for architecture-dependent bugs. + * + * Needs: + * void check_bugs(void); + */ +#include + +static void __init check_bugs(void) +{ + extern unsigned long loops_per_jiffy; + + current_cpu_data.loops_per_jiffy = loops_per_jiffy; +} + +#endif /* _ASM_M32R_BUGS_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/byteorder.h 2004-09-03 00:57:17.410392480 -0700 @@ -0,0 +1,19 @@ +#ifndef _ASM_M32R_BYTEORDER_H +#define _ASM_M32R_BYTEORDER_H + +/* $Id$ */ + +#include + +#if !defined(__STRICT_ANSI__) || defined(__KERNEL__) +# define __BYTEORDER_HAS_U64__ +# define __SWAB_64_THRU_32__ +#endif + +#if defined(__LITTLE_ENDIAN__) +# include +#else +# include +#endif + +#endif /* _ASM_M32R_BYTEORDER_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/cachectl.h 2004-09-03 00:57:17.411392328 -0700 @@ -0,0 +1,26 @@ +/* + * cachectl.h -- defines for M32R cache control system calls + * + * Copyright (C) 2003 by Kazuhiro Inaoka + */ +#ifndef __ASM_M32R_CACHECTL +#define __ASM_M32R_CACHECTL + +/* + * Options for cacheflush system call + * + * cacheflush() is currently fluch_cache_all(). + */ +#define ICACHE (1<<0) /* flush instruction cache */ +#define DCACHE (1<<1) /* writeback and flush data cache */ +#define BCACHE (ICACHE|DCACHE) /* flush both caches */ + +/* + * Caching modes for the cachectl(2) call + * + * cachectl(2) is currently not supported and returns ENOSYS. + */ +#define CACHEABLE 0 /* make pages cacheable */ +#define UNCACHEABLE 1 /* make pages uncacheable */ + +#endif /* __ASM_M32R_CACHECTL */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/cacheflush.h 2004-09-03 00:57:17.411392328 -0700 @@ -0,0 +1,68 @@ +#ifndef _ASM_M32R_CACHEFLUSH_H +#define _ASM_M32R_CACHEFLUSH_H + +#include +#include + +extern void _flush_cache_all(void); +extern void _flush_cache_copyback_all(void); + +#if defined(CONFIG_CHIP_M32700) || defined(CONFIG_CHIP_OPSP) +#define flush_cache_all() do { } while (0) +#define flush_cache_mm(mm) do { } while (0) +#define flush_cache_range(vma, start, end) do { } while (0) +#define flush_cache_page(vma, vmaddr) do { } while (0) +#define flush_dcache_page(page) do { } while (0) +#define flush_dcache_mmap_lock(mapping) do { } while (0) +#define flush_dcache_mmap_unlock(mapping) do { } while (0) +#ifndef CONFIG_SMP +#define flush_icache_range(start, end) _flush_cache_copyback_all() +#define flush_icache_page(vma,pg) _flush_cache_copyback_all() +#define flush_icache_user_range(vma,pg,adr,len) _flush_cache_copyback_all() +#define flush_cache_sigtramp(addr) _flush_cache_copyback_all() +#else /* CONFIG_SMP */ +extern void smp_flush_cache_all(void); +#define flush_icache_range(start, end) smp_flush_cache_all() +#define flush_icache_page(vma,pg) smp_flush_cache_all() +#define flush_icache_user_range(vma,pg,adr,len) smp_flush_cache_all() +#define flush_cache_sigtramp(addr) _flush_cache_copyback_all() +#endif /* CONFIG_SMP */ +#elif defined(CONFIG_CHIP_M32102) +#define flush_cache_all() do { } while (0) +#define flush_cache_mm(mm) do { } while (0) +#define flush_cache_range(vma, start, end) do { } while (0) +#define flush_cache_page(vma, vmaddr) do { } while (0) +#define flush_dcache_page(page) do { } while (0) +#define flush_dcache_mmap_lock(mapping) do { } while (0) +#define flush_dcache_mmap_unlock(mapping) do { } while (0) +#define flush_icache_range(start, end) _flush_cache_all() +#define flush_icache_page(vma,pg) _flush_cache_all() +#define flush_icache_user_range(vma,pg,adr,len) _flush_cache_all() +#define flush_cache_sigtramp(addr) _flush_cache_all() +#else +#define flush_cache_all() do { } while (0) +#define flush_cache_mm(mm) do { } while (0) +#define flush_cache_range(vma, start, end) do { } while (0) +#define flush_cache_page(vma, vmaddr) do { } while (0) +#define flush_dcache_page(page) do { } while (0) +#define flush_dcache_mmap_lock(mapping) do { } while (0) +#define flush_dcache_mmap_unlock(mapping) do { } while (0) +#define flush_icache_range(start, end) do { } while (0) +#define flush_icache_page(vma,pg) do { } while (0) +#define flush_icache_user_range(vma,pg,adr,len) do { } while (0) +#define flush_cache_sigtramp(addr) do { } while (0) +#endif /* CONFIG_CHIP_* */ + +#define flush_cache_vmap(start, end) do { } while (0) +#define flush_cache_vunmap(start, end) do { } while (0) + +#define copy_to_user_page(vma, page, vaddr, dst, src, len) \ +do { \ + memcpy(dst, src, len); \ + flush_icache_user_range(vma, page, vaddr, len); \ +} while (0) +#define copy_from_user_page(vma, page, vaddr, dst, src, len) \ + memcpy(dst, src, len) + +#endif /* _ASM_M32R_CACHEFLUSH_H */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/cache.h 2004-09-03 00:57:17.412392176 -0700 @@ -0,0 +1,12 @@ +#ifndef _ASM_M32R_CACHE_H +#define _ASM_M32R_CACHE_H + +/* $Id$ */ + +/* L1 cache line size */ +#define L1_CACHE_SHIFT 4 +#define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT) + +#define L1_CACHE_SHIFT_MAX 4 + +#endif /* _ASM_M32R_CACHE_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/checksum.h 2004-09-03 00:57:18.737190776 -0700 @@ -0,0 +1,258 @@ +#ifndef _ASM_M32R_CHECKSUM_H +#define _ASM_M32R_CHECKSUM_H + +/* $Id$ */ + +/* + * linux/include/asm-m32r/atomic.h + * orig : i386 2.4.10 + * + * M32R version: + * Copyright (C) 2001, 2002 Hiroyuki Kondo, Hirokazu Takata + */ + +#include + +/* + * computes the checksum of a memory block at buff, length len, + * and adds in "sum" (32-bit) + * + * returns a 32-bit number suitable for feeding into itself + * or csum_tcpudp_magic + * + * this function must be called with even lengths, except + * for the last fragment, which may be odd + * + * it's best to have buff aligned on a 32-bit boundary + */ +asmlinkage unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum); + +/* + * the same as csum_partial, but copies from src while it + * checksums, and handles user-space pointer exceptions correctly, when needed. + * + * here even more important to align src and dst on a 32-bit (or even + * better 64-bit) boundary + */ + +asmlinkage unsigned int csum_partial_copy_generic(const char *src, char *dst, + int len, int sum, int *src_err_ptr, int *dst_err_ptr); + +/* + * Note: when you get a NULL pointer exception here this means someone + * passed in an incorrect kernel address to one of these functions. + * + * If you use these functions directly please don't forget the + * verify_area(). + */ + +extern unsigned int csum_partial_copy(const char *src, char *dst, + int len, int sum); +extern unsigned int csum_partial_copy_generic_from(const char *src, + char *dst, int len, unsigned int sum, int *err_ptr); +extern unsigned int csum_partial_copy_generic_to (const char *src, + char *dst, int len, unsigned int sum, int *err_ptr); + +extern __inline__ +unsigned int csum_partial_copy_nocheck ( const char *src, char *dst, + int len, int sum) +{ +#if 0 + return csum_partial_copy_generic ( src, dst, len, sum, NULL, NULL); +#else + return csum_partial_copy( src, dst, len, sum); +#endif +} + +extern __inline__ +unsigned int csum_partial_copy_from_user ( const char __user *src, char *dst, + int len, int sum, int *err_ptr) +{ +#if 0 + return csum_partial_copy_generic ( src, dst, len, sum, err_ptr, NULL); +#else + return csum_partial_copy_generic_from ( src, dst, len, sum, err_ptr); +#endif +} + +/* + * These are the old (and unsafe) way of doing checksums, a warning message will be + * printed if they are used and an exeption occurs. + * + * these functions should go away after some time. + */ + +#define csum_partial_copy_fromuser csum_partial_copy +unsigned int csum_partial_copy( const char *src, char *dst, int len, int sum); + +/* + * Fold a partial checksum + */ + +static __inline__ unsigned int csum_fold(unsigned int sum) +{ + unsigned long tmpreg; + __asm__( + " sll3 %1, %0, #16 \n" + " cmp %0, %0 \n" + " addx %0, %1 \n" + " ldi %1, #0 \n" + " srli %0, #16 \n" + " addx %0, %1 \n" + " xor3 %0, %0, #0x0000ffff \n" + : "=r" (sum), "=&r" (tmpreg) + : "0" (sum) + : "cbit" + ); + return sum; +} + +/* + * This is a version of ip_compute_csum() optimized for IP headers, + * which always checksum on 4 octet boundaries. + * + * By Jorge Cwik , adapted for linux by + * Arnt Gulbrandsen. + */ +static __inline__ unsigned short ip_fast_csum(unsigned char * iph, + unsigned int ihl) { + unsigned long sum, tmpreg0, tmpreg1; + + __asm__ __volatile__( + " ld %0, @%1+ \n" + " addi %2, #-4 \n" + "# bgez %2, 2f \n" + " cmp %0, %0 \n" + " ld %3, @%1+ \n" + " ld %4, @%1+ \n" + " addx %0, %3 \n" + " ld %3, @%1+ \n" + " addx %0, %4 \n" + " addx %0, %3 \n" + " .fillinsn\n" + "1: \n" + " ld %4, @%1+ \n" + " addi %2, #-1 \n" + " addx %0, %4 \n" + " bgtz %2, 1b \n" + "\n" + " ldi %3, #0 \n" + " addx %0, %3 \n" + " .fillinsn\n" + "2: \n" + /* Since the input registers which are loaded with iph and ipl + are modified, we must also specify them as outputs, or gcc + will assume they contain their original values. */ + : "=&r" (sum), "=r" (iph), "=r" (ihl), "=&r" (tmpreg0), "=&r" (tmpreg1) + : "1" (iph), "2" (ihl) + : "cbit", "memory"); + + return csum_fold(sum); +} + +static __inline__ unsigned long csum_tcpudp_nofold(unsigned long saddr, + unsigned long daddr, + unsigned short len, + unsigned short proto, + unsigned int sum) +{ +#if defined(__LITTLE_ENDIAN) + unsigned long len_proto = (ntohs(len)<<16)+proto*256; +#else + unsigned long len_proto = (proto<<16)+len; +#endif + unsigned long tmpreg; + + __asm__( + " cmp %0, %0 \n" + " addx %0, %2 \n" + " addx %0, %3 \n" + " addx %0, %4 \n" + " ldi %1, #0 \n" + " addx %0, %1 \n" + : "=r" (sum), "=&r" (tmpreg) + : "r" (daddr), "r" (saddr), "r" (len_proto), "0" (sum) + : "cbit" + ); + + return sum; +} + +/* + * computes the checksum of the TCP/UDP pseudo-header + * returns a 16-bit checksum, already complemented + */ +static __inline__ unsigned short int csum_tcpudp_magic(unsigned long saddr, + unsigned long daddr, + unsigned short len, + unsigned short proto, + unsigned int sum) +{ + return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum)); +} + +/* + * this routine is used for miscellaneous IP-like checksums, mainly + * in icmp.c + */ + +static __inline__ unsigned short ip_compute_csum(unsigned char * buff, int len) { + return csum_fold (csum_partial(buff, len, 0)); +} + +#define _HAVE_ARCH_IPV6_CSUM +static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr, + struct in6_addr *daddr, + __u16 len, + unsigned short proto, + unsigned int sum) +{ + unsigned long tmpreg0, tmpreg1, tmpreg2, tmpreg3; + __asm__( + " ld %1, @(%5) \n" + " ld %2, @(4,%5) \n" + " ld %3, @(8,%5) \n" + " ld %4, @(12,%5) \n" + " add %0, %1 \n" + " addx %0, %2 \n" + " addx %0, %3 \n" + " addx %0, %4 \n" + " ld %1, @(%6) \n" + " ld %2, @(4,%6) \n" + " ld %3, @(8,%6) \n" + " ld %4, @(12,%6) \n" + " addx %0, %1 \n" + " addx %0, %2 \n" + " addx %0, %3 \n" + " addx %0, %4 \n" + " addx %0, %7 \n" + " addx %0, %8 \n" + " ldi %1, #0 \n" + " addx %0, %1 \n" + : "=&r" (sum), "=&r" (tmpreg0), "=&r" (tmpreg1), + "=&r" (tmpreg2), "=&r" (tmpreg3) + : "r" (saddr), "r" (daddr), + "r" (htonl((__u32) (len))), "r" (htonl(proto)), "0" (sum) + : "cbit" + ); + + return csum_fold(sum); +} + +/* + * Copy and checksum to user + */ +#define HAVE_CSUM_COPY_USER +static __inline__ unsigned int csum_and_copy_to_user (const char *src, char *dst, + int len, int sum, int *err_ptr) +{ + if (access_ok(VERIFY_WRITE, dst, len)) + return csum_partial_copy_generic_to(src, dst, len, sum, err_ptr); + + if (len) + *err_ptr = -EFAULT; + + return -1; /* invalid checksum */ +} + +#endif /* _ASM_M32R_CHECKSUM_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/current.h 2004-09-03 00:57:17.413392024 -0700 @@ -0,0 +1,18 @@ +#ifndef _ASM_M32R_CURRENT_H +#define _ASM_M32R_CURRENT_H + +/* $Id$ */ + +#include + +struct task_struct; + +static __inline__ struct task_struct *get_current(void) +{ + return current_thread_info()->task; +} + +#define current (get_current()) + +#endif /* _ASM_M32R_CURRENT_H */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/delay.h 2004-09-03 00:57:18.739190472 -0700 @@ -0,0 +1,28 @@ +#ifndef _ASM_M32R_DELAY_H +#define _ASM_M32R_DELAY_H + +/* $Id$ */ + +/* + * Copyright (C) 1993 Linus Torvalds + * + * Delay routines calling functions in arch/m32r/lib/delay.c + */ + +extern void __bad_udelay(void); +extern void __bad_ndelay(void); + +extern void __udelay(unsigned long usecs); +extern void __ndelay(unsigned long nsecs); +extern void __const_udelay(unsigned long usecs); +extern void __delay(unsigned long loops); + +#define udelay(n) (__builtin_constant_p(n) ? \ + ((n) > 20000 ? __bad_udelay() : __const_udelay((n) * 0x10c7ul)) : \ + __udelay(n)) + +#define ndelay(n) (__builtin_constant_p(n) ? \ + ((n) > 20000 ? __bad_ndelay() : __const_udelay((n) * 5ul)) : \ + __ndelay(n)) + +#endif /* _ASM_M32R_DELAY_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/div64.h 2004-09-03 00:57:17.414391872 -0700 @@ -0,0 +1,38 @@ +#ifndef _ASM_M32R_DIV64 +#define _ASM_M32R_DIV64 + +/* $Id$ */ + +/* unsigned long long division. + * Input: + * unsigned long long n + * unsigned long base + * Output: + * n = n / base; + * return value = n % base; + */ +#define do_div(n, base) \ +({ \ + unsigned long _res, _high, _mid, _low; \ + \ + _low = (n) & 0xffffffffUL; \ + _high = (n) >> 32; \ + if (_high) { \ + _mid = (_high % (unsigned long)(base)) << 16; \ + _high = _high / (unsigned long)(base); \ + _mid += _low >> 16; \ + _low &= 0x0000ffffUL; \ + _low += (_mid % (unsigned long)(base)) << 16; \ + _mid = _mid / (unsigned long)(base); \ + _res = _low % (unsigned long)(base); \ + _low = _low / (unsigned long)(base); \ + n = _low + ((long long)_mid << 16) + \ + ((long long)_high << 32); \ + } else { \ + _res = _low % (unsigned long)(base); \ + n = (_low / (unsigned long)(base)); \ + } \ + _res; \ +}) + +#endif /* _ASM_M32R_DIV64 */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/dma.h 2004-09-03 00:57:17.414391872 -0700 @@ -0,0 +1,14 @@ +#ifndef _ASM_M32R_DMA_H +#define _ASM_M32R_DMA_H + +/* $Id$ */ + +#include + +/* + * The maximum address that we can perform a DMA transfer + * to on this platform + */ +#define MAX_DMA_ADDRESS (PAGE_OFFSET+0x20000000) + +#endif /* _ASM_M32R_DMA_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/dma-mapping.h 2004-09-03 00:57:17.415391720 -0700 @@ -0,0 +1,23 @@ +#ifndef _ASM_M32R_DMA_MAPPING_H +#define _ASM_M32R_DMA_MAPPING_H + +/* + * NOTE: Do not include + * Because it requires PCI stuffs, but current M32R don't provide these. + */ + +static inline void * +dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, + int flag) +{ + return (void *)NULL; +} + +static inline void +dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, + dma_addr_t dma_handle) +{ + return; +} + +#endif /* _ASM_M32R_DMA_MAPPING_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/elf.h 2004-09-03 00:57:17.416391568 -0700 @@ -0,0 +1,177 @@ +#ifndef _ASM_M32R__ELF_H +#define _ASM_M32R__ELF_H + +/* $Id$ */ + +/* + * ELF register definitions.. + */ + +#include +#include + +#include + +/* M32R relocation types */ +#define R_M32R_NONE 0 +#define R_M32R_16 1 +#define R_M32R_32 2 +#define R_M32R_24 3 +#define R_M32R_10_PCREL 4 +#define R_M32R_18_PCREL 5 +#define R_M32R_26_PCREL 6 +#define R_M32R_HI16_ULO 7 +#define R_M32R_HI16_SLO 8 +#define R_M32R_LO16 9 +#define R_M32R_SDA16 10 +#ifdef OLD_TYPE +#define R_M32R_GOT24 11 +#define R_M32R_26_PLTREL 12 +#define R_M32R_GOT16_HI_ULO 13 +#define R_M32R_GOT16_HI_SLO 14 +#define R_M32R_GOT16_LO 15 +#define R_M32R_GOTPC24 16 +#define R_M32R_COPY 17 +#define R_M32R_GLOB_DAT 18 +#define R_M32R_JMP_SLOT 19 +#define R_M32R_RELATIVE 20 +#define R_M32R_GNU_VTINHERIT 21 +#define R_M32R_GNU_VTENTRY 22 + +#define R_M32R_16_RELA R_M32R_16 +#define R_M32R_32_RELA R_M32R_32 +#define R_M32R_24_RELA R_M32R_24 +#define R_M32R_10_PCREL_RELA R_M32R_10_PCREL +#define R_M32R_18_PCREL_RELA R_M32R_18_PCREL +#define R_M32R_26_PCREL_RELA R_M32R_26_PCREL +#define R_M32R_HI16_ULO_RELA R_M32R_HI16_ULO +#define R_M32R_HI16_SLO_RELA R_M32R_HI16_SLO +#define R_M32R_LO16_RELA R_M32R_LO16 +#define R_M32R_SDA16_RELA R_M32R_SDA16 +#else /* OLD_TYPE */ +#define R_M32R_GNU_VTINHERIT 11 +#define R_M32R_GNU_VTENTRY 12 + +#define R_M32R_GOT24_SAMPLE 11 /* comflict */ +#define R_M32R_26_PLTREL_SAMPLE 12 /* comflict */ +#define R_M32R_GOT16_HI_ULO_SAMPLE 13 +#define R_M32R_GOT16_HI_SLO_SAMPLE 14 +#define R_M32R_GOT16_LO_SAMPLE 15 +#define R_M32R_GOTPC24_SAMPLE 16 +#define R_M32R_COPY_SAMPLE 17 +#define R_M32R_GLOB_DAT_SAMPLE 18 +#define R_M32R_JMP_SLOT_SAMPLE 19 +#define R_M32R_RELATIVE_SAMPLE 20 +#define R_M32R_GNU_VTINHERIT_SAMPLE 21 +#define R_M32R_GNU_VTENTRY_SAMPLE 22 + +#define R_M32R_16_RELA 33 +#define R_M32R_32_RELA 34 +#define R_M32R_24_RELA 35 +#define R_M32R_10_PCREL_RELA 36 +#define R_M32R_18_PCREL_RELA 37 +#define R_M32R_26_PCREL_RELA 38 +#define R_M32R_HI16_ULO_RELA 39 +#define R_M32R_HI16_SLO_RELA 40 +#define R_M32R_LO16_RELA 41 +#define R_M32R_SDA16_RELA 42 +#define R_M32R_RELA_GNU_VTINHERIT 43 +#define R_M32R_RELA_GNU_VTENTRY 44 + +#define R_M32R_GOT24 48 +#define R_M32R_26_PLTREL 49 +#define R_M32R_COPY 50 +#define R_M32R_GLOB_DAT 51 +#define R_M32R_JMP_SLOT 52 +#define R_M32R_RELATIVE 53 +#define R_M32R_GOTOFF 54 +#define R_M32R_GOTPC24 55 +#define R_M32R_GOT16_HI_ULO 56 +#define R_M32R_GOT16_HI_SLO 57 +#define R_M32R_GOT16_LO 58 +#define R_M32R_GOTPC_HI_ULO 59 +#define R_M32R_GOTPC_HI_SLO 60 +#define R_M32R_GOTPC_LO 61 + +#endif /* OLD_TYPE */ + +#define R_M32R_NUM 256 + +typedef unsigned long elf_greg_t; + +#define ELF_NGREG (sizeof (struct pt_regs) / sizeof(elf_greg_t)) +typedef elf_greg_t elf_gregset_t[ELF_NGREG]; + +/* We have no FP mumumu. */ +typedef double elf_fpreg_t; +typedef elf_fpreg_t elf_fpregset_t; + +/* + * This is used to ensure we don't load something for the wrong architecture. + */ +#define elf_check_arch(x) \ + (((x)->e_machine == EM_M32R) || ((x)->e_machine == EM_CYGNUS_M32R)) + +/* + * These are used to set parameters in the core dumps. + */ +#define ELF_CLASS ELFCLASS32 +#if defined(__LITTLE_ENDIAN) +#define ELF_DATA ELFDATA2LSB +#elif defined(__BIG_ENDIAN) +#define ELF_DATA ELFDATA2MSB +#else +#error no endian defined +#endif +#define ELF_ARCH EM_M32R + +/* SVR4/i386 ABI (pages 3-31, 3-32) says that when the program starts %edx + contains a pointer to a function which might be registered using `atexit'. + This provides a mean for the dynamic linker to call DT_FINI functions for + shared libraries that have been loaded before the code runs. + + A value of 0 tells we have no such handler. + + We might as well make sure everything else is cleared too (except for %esp), + just to make things more deterministic. + */ +#define ELF_PLAT_INIT(_r, load_addr) do { \ + _r->r0 = 0; \ +} while (0) + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 4096 + +/* This is the location that an ET_DYN program is loaded if exec'ed. Typical + use of this is to invoke "./ld.so someprog" to test out a new version of + the loader. We need to make sure that it is out of the way of the program + that it will "exec", and that there is sufficient room for the brk. */ + +#define ELF_ET_DYN_BASE (TASK_SIZE / 3 * 2) + +/* regs is struct pt_regs, pr_reg is elf_gregset_t (which is + now struct_user_regs, they are different) */ + +#define ELF_CORE_COPY_REGS(pr_reg, regs) \ + memcpy((char *)&pr_reg, (char *)®s, sizeof (struct pt_regs)); + +/* This yields a mask that user programs can use to figure out what + instruction set this CPU supports. This could be done in user space, + but it's not easy, and we've already done it here. */ + +#define ELF_HWCAP (0) + +/* This yields a string that ld.so will use to load implementation + specific libraries for optimization. This is more specific in + intent than poking at uname or /proc/cpuinfo. + + For the moment, we have only optimizations for the Intel generations, + but that could change... */ + +#define ELF_PLATFORM (NULL) + +#ifdef __KERNEL__ +#define SET_PERSONALITY(ex, ibcs2) set_personality((ibcs2)?PER_SVR4:PER_LINUX) +#endif + +#endif /* _ASM_M32R__ELF_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/errno.h 2004-09-03 00:57:17.416391568 -0700 @@ -0,0 +1,9 @@ +#ifndef _ASM_M32R_ERRNO_H +#define _ASM_M32R_ERRNO_H + +/* $Id$ */ + +#include + +#endif /* _ASM_M32R_ERRNO_H */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/fcntl.h 2004-09-03 00:57:18.739190472 -0700 @@ -0,0 +1,92 @@ +#ifndef _ASM_M32R_FCNTL_H +#define _ASM_M32R_FCNTL_H + +/* $Id$ */ + +/* orig : i386 2.4.18 */ + +/* open/fcntl - O_SYNC is only implemented on blocks devices and on files + located on an ext2 file system */ +#define O_ACCMODE 0003 +#define O_RDONLY 00 +#define O_WRONLY 01 +#define O_RDWR 02 +#define O_CREAT 0100 /* not fcntl */ +#define O_EXCL 0200 /* not fcntl */ +#define O_NOCTTY 0400 /* not fcntl */ +#define O_TRUNC 01000 /* not fcntl */ +#define O_APPEND 02000 +#define O_NONBLOCK 04000 +#define O_NDELAY O_NONBLOCK +#define O_SYNC 010000 +#define FASYNC 020000 /* fcntl, for BSD compatibility */ +#define O_DIRECT 040000 /* direct disk access hint */ +#define O_LARGEFILE 0100000 +#define O_DIRECTORY 0200000 /* must be a directory */ +#define O_NOFOLLOW 0400000 /* don't follow links */ +#define O_NOATIME 01000000 + +#define F_DUPFD 0 /* dup */ +#define F_GETFD 1 /* get close_on_exec */ +#define F_SETFD 2 /* set/clear close_on_exec */ +#define F_GETFL 3 /* get file->f_flags */ +#define F_SETFL 4 /* set file->f_flags */ +#define F_GETLK 5 +#define F_SETLK 6 +#define F_SETLKW 7 + +#define F_SETOWN 8 /* for sockets. */ +#define F_GETOWN 9 /* for sockets. */ +#define F_SETSIG 10 /* for sockets. */ +#define F_GETSIG 11 /* for sockets. */ + +#define F_GETLK64 12 /* using 'struct flock64' */ +#define F_SETLK64 13 +#define F_SETLKW64 14 + +/* for F_[GET|SET]FL */ +#define FD_CLOEXEC 1 /* actually anything with low bit set goes */ + +/* for posix fcntl() and lockf() */ +#define F_RDLCK 0 +#define F_WRLCK 1 +#define F_UNLCK 2 + +/* for old implementation of bsd flock () */ +#define F_EXLCK 4 /* or 3 */ +#define F_SHLCK 8 /* or 4 */ + +/* for leases */ +#define F_INPROGRESS 16 + +/* operations for bsd flock(), also used by the kernel implementation */ +#define LOCK_SH 1 /* shared lock */ +#define LOCK_EX 2 /* exclusive lock */ +#define LOCK_NB 4 /* or'd with one of the above to prevent + blocking */ +#define LOCK_UN 8 /* remove lock */ + +#define LOCK_MAND 32 /* This is a mandatory flock */ +#define LOCK_READ 64 /* ... Which allows concurrent read operations */ +#define LOCK_WRITE 128 /* ... Which allows concurrent write operations */ +#define LOCK_RW 192 /* ... Which allows concurrent read & write ops */ + +struct flock { + short l_type; + short l_whence; + off_t l_start; + off_t l_len; + pid_t l_pid; +}; + +struct flock64 { + short l_type; + short l_whence; + loff_t l_start; + loff_t l_len; + pid_t l_pid; +}; + +#define F_LINUX_SPECIFIC_BASE 1024 + +#endif /* _ASM_M32R_FCNTL_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/flat.h 2004-09-03 00:57:17.417391416 -0700 @@ -0,0 +1,145 @@ +/* + * include/asm-m32r/flat.h + * + * uClinux flat-format executables + * + * Copyright (C) 2004 Kazuhiro Inaoka + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive for + * more details. + */ +#ifndef __ASM_M32R_FLAT_H +#define __ASM_M32R_FLAT_H + +#define flat_stack_align(sp) (*sp += (*sp & 3 ? (4 - (*sp & 3)): 0)) +#define flat_argvp_envp_on_stack() 0 +#define flat_old_ram_flag(flags) (flags) +#define flat_reloc_valid(reloc, size) \ + (((reloc) - textlen_for_m32r_lo16_data) <= (size)) +#define flat_get_addr_from_rp(rp, relval, flags) \ + m32r_flat_get_addr_from_rp(rp, relval, (text_len) ) + +#define flat_put_addr_at_rp(rp, addr, relval) \ + m32r_flat_put_addr_at_rp(rp, addr, relval) + +/* Convert a relocation entry into an address. */ +static inline unsigned long +flat_get_relocate_addr (unsigned long relval) +{ + return relval & 0x00ffffff; /* Mask out top 8-bits */ +} + +#define flat_m32r_get_reloc_type(relval) ((relval) >> 24) + +#define M32R_SETH_OPCODE 0xd0c00000 /* SETH instruction code */ + +#define FLAT_M32R_32 0x00 /* 32bits reloc */ +#define FLAT_M32R_24 0x01 /* unsigned 24bits reloc */ +#define FLAT_M32R_16 0x02 /* 16bits reloc */ +#define FLAT_M32R_LO16 0x03 /* signed low 16bits reloc (low()) */ +#define FLAT_M32R_LO16_DATA 0x04 /* signed low 16bits reloc (low()) + for a symbol in .data section */ + /* High 16bits of an address used + when the lower 16bbits are treated + as unsigned. + To create SETH instruction only. + 0x1X: X means a number of register. + 0x10 - 0x3F are reserved. */ +#define FLAT_M32R_HI16_ULO 0x10 /* reloc for SETH Rn,#high(imm16) */ + /* High 16bits of an address used + when the lower 16bbits are treated + as signed. + To create SETH instruction only. + 0x2X: X means a number of register. + 0x20 - 0x4F are reserved. */ +#define FLAT_M32R_HI16_SLO 0x20 /* reloc for SETH Rn,#shigh(imm16) */ + +static unsigned long textlen_for_m32r_lo16_data = 0; + +static inline unsigned long m32r_flat_get_addr_from_rp (unsigned long *rp, + unsigned long relval, + unsigned long textlen) +{ + unsigned int reloc = flat_m32r_get_reloc_type (relval); + textlen_for_m32r_lo16_data = 0; + if (reloc & 0xf0) { + unsigned long addr = htonl(*rp); + switch (reloc & 0xf0) + { + case FLAT_M32R_HI16_ULO: + case FLAT_M32R_HI16_SLO: + if (addr == 0) { + /* put "seth Rn,#0x0" instead of 0 (addr). */ + *rp = (M32R_SETH_OPCODE | ((reloc & 0x0f)<<24)); + } + return addr; + default: + break; + } + } else { + switch (reloc) + { + case FLAT_M32R_LO16: + return htonl(*rp) & 0xFFFF; + case FLAT_M32R_LO16_DATA: + /* FIXME: The return value will decrease by textlen + at m32r_flat_put_addr_at_rp () */ + textlen_for_m32r_lo16_data = textlen; + return (htonl(*rp) & 0xFFFF) + textlen; + case FLAT_M32R_16: + return htons(*(unsigned short *)rp) & 0xFFFF; + case FLAT_M32R_24: + return htonl(*rp) & 0xFFFFFF; + case FLAT_M32R_32: + return htonl(*rp); + default: + break; + } + } + return ~0; /* bogus value */ +} + +static inline void m32r_flat_put_addr_at_rp (unsigned long *rp, + unsigned long addr, + unsigned long relval) +{ + unsigned int reloc = flat_m32r_get_reloc_type (relval); + if (reloc & 0xf0) { + unsigned long Rn = reloc & 0x0f; /* get a number of register */ + Rn <<= 24; /* 0x0R000000 */ + reloc &= 0xf0; + switch (reloc) + { + case FLAT_M32R_HI16_ULO: /* To create SETH Rn,#high(imm16) */ + *rp = (M32R_SETH_OPCODE | Rn + | ((addr >> 16) & 0xFFFF)); + break; + case FLAT_M32R_HI16_SLO: /* To create SETH Rn,#shigh(imm16) */ + *rp = (M32R_SETH_OPCODE | Rn + | (((addr >> 16) + ((addr & 0x8000) ? 1 : 0)) + & 0xFFFF)); + break; + } + } else { + switch (reloc) { + case FLAT_M32R_LO16_DATA: + addr -= textlen_for_m32r_lo16_data; + textlen_for_m32r_lo16_data = 0; + case FLAT_M32R_LO16: + *rp = (htonl(*rp) & 0xFFFF0000) | (addr & 0xFFFF); + break; + case FLAT_M32R_16: + *(unsigned short *)rp = addr & 0xFFFF; + break; + case FLAT_M32R_24: + *rp = (htonl(*rp) & 0xFF000000) | (addr & 0xFFFFFF); + break; + case FLAT_M32R_32: + *rp = addr; + break; + } + } +} + +#endif /* __ASM_M32R_FLAT_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/hardirq.h 2004-09-03 00:57:17.418391264 -0700 @@ -0,0 +1,103 @@ +#ifndef __ASM_HARDIRQ_H +#define __ASM_HARDIRQ_H + +/* $Id$ */ + +/* orig : i386 2.5.67 */ + +#include +#include +#include + +typedef struct { + unsigned int __softirq_pending; + unsigned int __syscall_count; + struct task_struct * __ksoftirqd_task; /* waitqueue is too large */ +} ____cacheline_aligned irq_cpustat_t; + +#include /* Standard mappings for irq_cpustat_t above */ + +/* + * We put the hardirq and softirq counter into the preemption + * counter. The bitmask has the following meaning: + * + * - bits 0-7 are the preemption count (max preemption depth: 256) + * - bits 8-15 are the softirq count (max # of softirqs: 256) + * - bits 16-23 are the hardirq count (max # of hardirqs: 256) + * + * - ( bit 26 is the PREEMPT_ACTIVE flag. ) + * + * PREEMPT_MASK: 0x000000ff + * SOFTIRQ_MASK: 0x0000ff00 + * HARDIRQ_MASK: 0x00ff0000 + */ + +#define PREEMPT_BITS 8 +#define SOFTIRQ_BITS 8 +#define HARDIRQ_BITS 8 + +#define PREEMPT_SHIFT 0 +#define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) +#define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) + +#define __MASK(x) ((1UL << (x))-1) + +#define PREEMPT_MASK (__MASK(PREEMPT_BITS) << PREEMPT_SHIFT) +#define HARDIRQ_MASK (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) +#define SOFTIRQ_MASK (__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) + +#define hardirq_count() (preempt_count() & HARDIRQ_MASK) +#define softirq_count() (preempt_count() & SOFTIRQ_MASK) +#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) + +#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) +#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) +#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) + +/* + * The hardirq mask has to be large enough to have + * space for potentially all IRQ sources in the system + * nesting on a single CPU: + */ +#if (1 << HARDIRQ_BITS) < NR_IRQS +# error HARDIRQ_BITS is too low! +#endif + +/* + * Are we doing bottom half or hardware interrupt processing? + * Are we in a softirq context? Interrupt context? + */ +#define in_irq() (hardirq_count()) +#define in_softirq() (softirq_count()) +#define in_interrupt() (irq_count()) + + +#define hardirq_trylock() (!in_interrupt()) +#define hardirq_endlock() do { } while (0) + +#define irq_enter() (preempt_count() += HARDIRQ_OFFSET) +#define nmi_enter() (irq_enter()) +#define nmi_exit() (preempt_count() -= HARDIRQ_OFFSET) + +#ifdef CONFIG_PREEMPT +# define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != kernel_locked()) +# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) +#else +# define in_atomic() (preempt_count() != 0) +# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET +#endif +#define irq_exit() \ +do { \ + preempt_count() -= IRQ_EXIT_OFFSET; \ + if (!in_interrupt() && softirq_pending(smp_processor_id())) \ + do_softirq(); \ + preempt_enable_no_resched(); \ +} while (0) + +#ifndef CONFIG_SMP +# define synchronize_irq(irq) barrier() +#else + extern void synchronize_irq(unsigned int irq); +#endif /* CONFIG_SMP */ + +#endif /* __ASM_HARDIRQ_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/hdreg.h 2004-09-03 00:57:17.418391264 -0700 @@ -0,0 +1 @@ +#include --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/hw_irq.h 2004-09-03 00:57:17.419391112 -0700 @@ -0,0 +1,40 @@ +#ifndef _ASM_M32R_HW_IRQ_H +#define _ASM_M32R_HW_IRQ_H + +/* $Id$ */ + +#include +#include +#include + +static __inline__ void hw_resend_irq(struct hw_interrupt_type *h, + unsigned int i) +{ + /* Nothing to do */ +} + +static __inline__ void m32r_do_profile (struct pt_regs *regs) +{ + unsigned long pc = regs->bpc; + + profile_hook(regs); + + if (user_mode(regs)) + return; + + if (!prof_buffer) + return; + + pc -= (unsigned long) &_stext; + pc >>= prof_shift; + /* + * Don't ignore out-of-bounds PC values silently, + * put them into the last histogram slot, so if + * present, they will show up as a sharp peak. + */ + if (pc > prof_len - 1) + pc = prof_len - 1; + atomic_inc((atomic_t *)&prof_buffer[pc]); +} + +#endif /* _ASM_M32R_HW_IRQ_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/ide.h 2004-09-03 00:57:19.066140768 -0700 @@ -0,0 +1,82 @@ +#ifndef _ASM_M32R_IDE_H +#define _ASM_M32R_IDE_H + +/* $Id$ */ + +/* + * linux/include/asm-m32r/ide.h + * + * Copyright (C) 1994-1996 Linus Torvalds & authors + */ + +/* + * This file contains the i386 architecture specific IDE code. + */ + +#ifdef __KERNEL__ + +#include + +#ifndef MAX_HWIFS +# ifdef CONFIG_BLK_DEV_IDEPCI +#define MAX_HWIFS 10 +# else +#define MAX_HWIFS 2 +# endif +#endif + +#if defined(CONFIG_PLAT_M32700UT) +#include +#include +#endif + +#define IDE_ARCH_OBSOLETE_DEFAULTS + +static __inline__ int ide_default_irq(unsigned long base) +{ + switch (base) { +#if defined(CONFIG_PLAT_M32700UT) + case 0x1f0: return PLD_IRQ_CFIREQ; + default: + return 0; +#else + case 0x1f0: return 14; + case 0x170: return 15; + case 0x1e8: return 11; + case 0x168: return 10; + case 0x1e0: return 8; + case 0x160: return 12; + default: + return 0; +#endif + } +} + +static __inline__ unsigned long ide_default_io_base(int index) +{ + switch (index) { + case 0: return 0x1f0; + case 1: return 0x170; + case 2: return 0x1e8; + case 3: return 0x168; + case 4: return 0x1e0; + case 5: return 0x160; + default: + return 0; + } +} + +#define IDE_ARCH_OBSOLETE_INIT +#define ide_default_io_ctl(base) ((base) + 0x206) /* obsolete */ + +#ifdef CONFIG_BLK_DEV_IDEPCI +#define ide_init_default_irq(base) (0) +#else +#define ide_init_default_irq(base) ide_default_irq(base) +#endif + +#include + +#endif /* __KERNEL__ */ + +#endif /* _ASM_M32R_IDE_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/ioctl.h 2004-09-03 00:57:17.420390960 -0700 @@ -0,0 +1,78 @@ +#ifndef _ASM_M32R_IOCTL_H +#define _ASM_M32R_IOCTL_H + +/* $Id$ */ + +/* orig : i386 2.4.18 */ + +/* + * linux/ioctl.h for Linux by H.H. Bergman. + */ + +/* ioctl command encoding: 32 bits total, command in lower 16 bits, + * size of the parameter structure in the lower 14 bits of the + * upper 16 bits. + * Encoding the size of the parameter structure in the ioctl request + * is useful for catching programs compiled with old versions + * and to avoid overwriting user space outside the user buffer area. + * The highest 2 bits are reserved for indicating the ``access mode''. + * NOTE: This limits the max parameter size to 16kB -1 ! + */ + +/* + * The following is for compatibility across the various Linux + * platforms. The i386 ioctl numbering scheme doesn't really enforce + * a type field. De facto, however, the top 8 bits of the lower 16 + * bits are indeed used as a type field, so we might just as well make + * this explicit here. Please be sure to use the decoding macros + * below from now on. + */ +#define _IOC_NRBITS 8 +#define _IOC_TYPEBITS 8 +#define _IOC_SIZEBITS 14 +#define _IOC_DIRBITS 2 + +#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1) +#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1) +#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1) +#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1) + +#define _IOC_NRSHIFT 0 +#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) +#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) +#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) + +/* + * Direction bits. + */ +#define _IOC_NONE 0U +#define _IOC_WRITE 1U +#define _IOC_READ 2U + +#define _IOC(dir,type,nr,size) \ + (((dir) << _IOC_DIRSHIFT) | \ + ((type) << _IOC_TYPESHIFT) | \ + ((nr) << _IOC_NRSHIFT) | \ + ((size) << _IOC_SIZESHIFT)) + +/* used to create numbers */ +#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0) +#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size)) +#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size)) +#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size)) + +/* used to decode ioctl numbers.. */ +#define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK) +#define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK) +#define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK) +#define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK) + +/* ...and for the drivers/sound files... */ + +#define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT) +#define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT) +#define IOC_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT) +#define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT) +#define IOCSIZE_SHIFT (_IOC_SIZESHIFT) + +#endif /* _ASM_M32R_IOCTL_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/ioctls.h 2004-09-03 00:57:17.420390960 -0700 @@ -0,0 +1,88 @@ +#ifndef __ARCH_M32R_IOCTLS_H__ +#define __ARCH_M32R_IOCTLS_H__ + +/* $Id$ */ + +/* orig : i386 2.5.67 */ + +#include + +/* 0x54 is just a magic number to make these relatively unique ('T') */ + +#define TCGETS 0x5401 +#define TCSETS 0x5402 /* Clashes with SNDCTL_TMR_START sound ioctl */ +#define TCSETSW 0x5403 +#define TCSETSF 0x5404 +#define TCGETA 0x5405 +#define TCSETA 0x5406 +#define TCSETAW 0x5407 +#define TCSETAF 0x5408 +#define TCSBRK 0x5409 +#define TCXONC 0x540A +#define TCFLSH 0x540B +#define TIOCEXCL 0x540C +#define TIOCNXCL 0x540D +#define TIOCSCTTY 0x540E +#define TIOCGPGRP 0x540F +#define TIOCSPGRP 0x5410 +#define TIOCOUTQ 0x5411 +#define TIOCSTI 0x5412 +#define TIOCGWINSZ 0x5413 +#define TIOCSWINSZ 0x5414 +#define TIOCMGET 0x5415 +#define TIOCMBIS 0x5416 +#define TIOCMBIC 0x5417 +#define TIOCMSET 0x5418 +#define TIOCGSOFTCAR 0x5419 +#define TIOCSSOFTCAR 0x541A +#define FIONREAD 0x541B +#define TIOCINQ FIONREAD +#define TIOCLINUX 0x541C +#define TIOCCONS 0x541D +#define TIOCGSERIAL 0x541E +#define TIOCSSERIAL 0x541F +#define TIOCPKT 0x5420 +#define FIONBIO 0x5421 +#define TIOCNOTTY 0x5422 +#define TIOCSETD 0x5423 +#define TIOCGETD 0x5424 +#define TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */ +/* #define TIOCTTYGSTRUCT 0x5426 - Former debugging-only ioctl */ +#define TIOCSBRK 0x5427 /* BSD compatibility */ +#define TIOCCBRK 0x5428 /* BSD compatibility */ +#define TIOCGSID 0x5429 /* Return the session ID of FD */ +#define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ +#define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ + +#define FIONCLEX 0x5450 +#define FIOCLEX 0x5451 +#define FIOASYNC 0x5452 +#define TIOCSERCONFIG 0x5453 +#define TIOCSERGWILD 0x5454 +#define TIOCSERSWILD 0x5455 +#define TIOCGLCKTRMIOS 0x5456 +#define TIOCSLCKTRMIOS 0x5457 +#define TIOCSERGSTRUCT 0x5458 /* For debugging only */ +#define TIOCSERGETLSR 0x5459 /* Get line status register */ +#define TIOCSERGETMULTI 0x545A /* Get multiport config */ +#define TIOCSERSETMULTI 0x545B /* Set multiport config */ + +#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */ +#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ +#define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */ +#define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */ +#define FIOQSIZE 0x5460 + +/* Used for packet mode */ +#define TIOCPKT_DATA 0 +#define TIOCPKT_FLUSHREAD 1 +#define TIOCPKT_FLUSHWRITE 2 +#define TIOCPKT_STOP 4 +#define TIOCPKT_START 8 +#define TIOCPKT_NOSTOP 16 +#define TIOCPKT_DOSTOP 32 + +#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ + +#endif /* __ARCH_M32R_IOCTLS_H__ */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/io.h 2004-09-03 00:57:17.421390808 -0700 @@ -0,0 +1,201 @@ +#ifndef _ASM_M32R_IO_H +#define _ASM_M32R_IO_H + +/* $Id$ */ + +#include /* __va */ + +#ifdef __KERNEL__ + +#define IO_SPACE_LIMIT 0xFFFFFFFF + +/** + * virt_to_phys - map virtual addresses to physical + * @address: address to remap + * + * The returned physical address is the physical (CPU) mapping for + * the memory address given. It is only valid to use this function on + * addresses directly mapped or allocated via kmalloc. + * + * This function does not give bus mappings for DMA transfers. In + * almost all conceivable cases a device driver should not be using + * this function + */ + +static __inline__ unsigned long virt_to_phys(volatile void * address) +{ + return __pa(address); +} + +/** + * phys_to_virt - map physical address to virtual + * @address: address to remap + * + * The returned virtual address is a current CPU mapping for + * the memory address given. It is only valid to use this function on + * addresses that have a kernel mapping + * + * This function does not handle bus mappings for DMA transfers. In + * almost all conceivable cases a device driver should not be using + * this function + */ + +static __inline__ void *phys_to_virt(unsigned long address) +{ + return __va(address); +} + +extern void * __ioremap(unsigned long offset, unsigned long size, unsigned long flags); + +/** + * ioremap - map bus memory into CPU space + * @offset: bus address of the memory + * @size: size of the resource to map + * + * ioremap performs a platform specific sequence of operations to + * make bus memory CPU accessible via the readb/readw/readl/writeb/ + * writew/writel functions and the other mmio helpers. The returned + * address is not guaranteed to be usable directly as a virtual + * address. + */ + +static __inline__ void * ioremap(unsigned long offset, unsigned long size) +{ + return __ioremap(offset, size, 0); +} + +extern void iounmap(void *addr); +#define ioremap_nocache(off,size) ioremap(off,size) + +/* + * IO bus memory addresses are also 1:1 with the physical address + */ +#define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT) +#define page_to_bus page_to_phys +#define virt_to_bus virt_to_phys + +extern unsigned char _inb(unsigned long); +extern unsigned short _inw(unsigned long); +extern unsigned long _inl(unsigned long); +extern unsigned char _inb_p(unsigned long); +extern unsigned short _inw_p(unsigned long); +extern unsigned long _inl_p(unsigned long); +extern void _outb(unsigned char, unsigned long); +extern void _outw(unsigned short, unsigned long); +extern void _outl(unsigned long, unsigned long); +extern void _outb_p(unsigned char, unsigned long); +extern void _outw_p(unsigned short, unsigned long); +extern void _outl_p(unsigned long, unsigned long); +extern void _insb(unsigned int, void *, unsigned long); +extern void _insw(unsigned int, void *, unsigned long); +extern void _insl(unsigned int, void *, unsigned long); +extern void _outsb(unsigned int, const void *, unsigned long); +extern void _outsw(unsigned int, const void *, unsigned long); +extern void _outsl(unsigned int, const void *, unsigned long); + +static inline unsigned char _readb(unsigned long addr) +{ + return *(volatile unsigned char *)addr; +} + +static inline unsigned short _readw(unsigned long addr) +{ + return *(volatile unsigned short *)addr; +} + +static inline unsigned long _readl(unsigned long addr) +{ + return *(volatile unsigned long *)addr; +} + +static inline void _writeb(unsigned char b, unsigned long addr) +{ + *(volatile unsigned char *)addr = b; +} + +static inline void _writew(unsigned short w, unsigned long addr) +{ + *(volatile unsigned short *)addr = w; +} + +static inline void _writel(unsigned long l, unsigned long addr) +{ + *(volatile unsigned long *)addr = l; +} + +#define inb _inb +#define inw _inw +#define inl _inl +#define outb _outb +#define outw _outw +#define outl _outl + +#define inb_p _inb_p +#define inw_p _inw_p +#define inl_p _inl_p +#define outb_p _outb_p +#define outw_p _outw_p +#define outl_p _outl_p + +#define insb _insb +#define insw _insw +#define insl _insl +#define outsb _outsb +#define outsw _outsw +#define outsl _outsl + +#define readb(addr) _readb((unsigned long)(addr)) +#define readw(addr) _readw((unsigned long)(addr)) +#define readl(addr) _readl((unsigned long)(addr)) +#define __raw_readb readb +#define __raw_readw readw +#define __raw_readl readl + +#define writeb(val, addr) _writeb((val), (unsigned long)(addr)) +#define writew(val, addr) _writew((val), (unsigned long)(addr)) +#define writel(val, addr) _writel((val), (unsigned long)(addr)) +#define __raw_writeb writeb +#define __raw_writew writew +#define __raw_writel writel + +#define flush_write_buffers() do { } while (0) /* M32R_FIXME */ + +/** + * isa_check_signature - find BIOS signatures + * @io_addr: mmio address to check + * @signature: signature block + * @length: length of signature + * + * Perform a signature comparison with the ISA mmio address io_addr. + * Returns 1 on a match. + * + * This function is deprecated. New drivers should use ioremap and + * check_signature. + */ + +static inline int isa_check_signature(unsigned long io_addr, + const unsigned char *signature, int length) +{ + int retval = 0; +#if 0 +printk("isa_check_signature\n"); + do { + if (isa_readb(io_addr) != *signature) + goto out; + io_addr++; + signature++; + length--; + } while (length); + retval = 1; +out: +#endif + return retval; +} + +#define memset_io(a, b, c) memset((void *)(a), (b), (c)) +#define memcpy_fromio(a, b, c) memcpy((a), (void *)(b), (c)) +#define memcpy_toio(a, b, c) memcpy((void *)(a), (b), (c)) + +#endif /* __KERNEL__ */ + +#endif /* _ASM_M32R_IO_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/ipcbuf.h 2004-09-03 00:57:17.422390656 -0700 @@ -0,0 +1,33 @@ +#ifndef _ASM_M32R_IPCBUF_H +#define _ASM_M32R_IPCBUF_H + +/* $Id$ */ + +/* orig : i386 2.4.18 */ + +/* + * The ipc64_perm structure for m32r architecture. + * Note extra padding because this structure is passed back and forth + * between kernel and user space. + * + * Pad space is left for: + * - 32-bit mode_t and seq + * - 2 miscellaneous 32-bit values + */ + +struct ipc64_perm +{ + __kernel_key_t key; + __kernel_uid32_t uid; + __kernel_gid32_t gid; + __kernel_uid32_t cuid; + __kernel_gid32_t cgid; + __kernel_mode_t mode; + unsigned short __pad1; + unsigned short seq; + unsigned short __pad2; + unsigned long __unused1; + unsigned long __unused2; +}; + +#endif /* _ASM_M32R_IPCBUF_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/ipc.h 2004-09-03 00:57:17.422390656 -0700 @@ -0,0 +1,35 @@ +#ifndef __M32R_IPC_H__ +#define __M32R_IPC_H__ + +/* orig : i386/ipc.h 2.6.0-test3 */ + +/* + * These are used to wrap system calls on x86. + * + * See arch/i386/kernel/sys_i386.c for ugly details.. + */ +struct ipc_kludge { + struct msgbuf __user *msgp; + long msgtyp; +}; + +#define SEMOP 1 +#define SEMGET 2 +#define SEMCTL 3 +#define SEMTIMEDOP 4 +#define MSGSND 11 +#define MSGRCV 12 +#define MSGGET 13 +#define MSGCTL 14 +#define SHMAT 21 +#define SHMDT 22 +#define SHMGET 23 +#define SHMCTL 24 + +/* Used by the DIPC package, try and avoid reusing it */ +#define DIPC 25 + +#define IPCCALL(version,op) ((version)<<16 | (op)) + +#endif /* __M32R_IPC_H__ */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/irq.h 2004-09-03 00:57:17.423390504 -0700 @@ -0,0 +1,86 @@ +#ifndef _ASM_M32R_IRQ_H +#define _ASM_M32R_IRQ_H + +/* $Id$ */ + +#include + +#if defined(CONFIG_PLAT_M32700UT_Alpha) || defined(CONFIG_PLAT_USRV) +/* + * IRQ definitions for M32700UT + * M32700 Chip: 64 interrupts + * ICU of M32700UT-on-board PLD: 32 interrupts cascaded to INT1# chip pin + */ +#define M32700UT_NUM_CPU_IRQ (64) +#define M32700UT_NUM_PLD_IRQ (32) +#define M32700UT_IRQ_BASE 0 +#define M32700UT_CPU_IRQ_BASE M32700UT_IRQ_BASE +#define M32700UT_PLD_IRQ_BASE (M32700UT_CPU_IRQ_BASE + M32700UT_NUM_CPU_IRQ) + +#define NR_IRQS (M32700UT_NUM_CPU_IRQ + M32700UT_NUM_PLD_IRQ) +#elif defined(CONFIG_PLAT_M32700UT) +/* + * IRQ definitions for M32700UT(Rev.C) + M32R-LAN + * M32700 Chip: 64 interrupts + * ICU of M32700UT-on-board PLD: 32 interrupts cascaded to INT1# chip pin + * ICU of M32R-LCD-on-board PLD: 32 interrupts cascaded to INT2# chip pin + * ICU of M32R-LAN-on-board PLD: 32 interrupts cascaded to INT0# chip pin + */ +#define M32700UT_NUM_CPU_IRQ (64) +#define M32700UT_NUM_PLD_IRQ (32) +#define M32700UT_NUM_LCD_PLD_IRQ (32) +#define M32700UT_NUM_LAN_PLD_IRQ (32) +#define M32700UT_IRQ_BASE 0 +#define M32700UT_CPU_IRQ_BASE (M32700UT_IRQ_BASE) +#define M32700UT_PLD_IRQ_BASE \ + (M32700UT_CPU_IRQ_BASE + M32700UT_NUM_CPU_IRQ) +#define M32700UT_LCD_PLD_IRQ_BASE \ + (M32700UT_PLD_IRQ_BASE + M32700UT_NUM_PLD_IRQ) +#define M32700UT_LAN_PLD_IRQ_BASE \ + (M32700UT_LCD_PLD_IRQ_BASE + M32700UT_NUM_LCD_PLD_IRQ) + +#define NR_IRQS \ + (M32700UT_NUM_CPU_IRQ + M32700UT_NUM_PLD_IRQ \ + + M32700UT_NUM_LCD_PLD_IRQ + M32700UT_NUM_LAN_PLD_IRQ) +#elif defined(CONFIG_PLAT_OPSPUT) +/* + * IRQ definitions for OPSPUT + M32R-LAN + * OPSP Chip: 64 interrupts + * ICU of OPSPUT-on-board PLD: 32 interrupts cascaded to INT1# chip pin + * ICU of M32R-LCD-on-board PLD: 32 interrupts cascaded to INT2# chip pin + * ICU of M32R-LAN-on-board PLD: 32 interrupts cascaded to INT0# chip pin + */ +#define OPSPUT_NUM_CPU_IRQ (64) +#define OPSPUT_NUM_PLD_IRQ (32) +#define OPSPUT_NUM_LCD_PLD_IRQ (32) +#define OPSPUT_NUM_LAN_PLD_IRQ (32) +#define OPSPUT_IRQ_BASE 0 +#define OPSPUT_CPU_IRQ_BASE (OPSPUT_IRQ_BASE) +#define OPSPUT_PLD_IRQ_BASE \ + (OPSPUT_CPU_IRQ_BASE + OPSPUT_NUM_CPU_IRQ) +#define OPSPUT_LCD_PLD_IRQ_BASE \ + (OPSPUT_PLD_IRQ_BASE + OPSPUT_NUM_PLD_IRQ) +#define OPSPUT_LAN_PLD_IRQ_BASE \ + (OPSPUT_LCD_PLD_IRQ_BASE + OPSPUT_NUM_LCD_PLD_IRQ) + +#define NR_IRQS \ + (OPSPUT_NUM_CPU_IRQ + OPSPUT_NUM_PLD_IRQ \ + + OPSPUT_NUM_LCD_PLD_IRQ + OPSPUT_NUM_LAN_PLD_IRQ) +#else +#define NR_IRQS 64 +#endif + +#define irq_canonicalize(irq) (irq) + +#ifndef __ASSEMBLY__ +extern void disable_irq(unsigned int); +extern void disable_irq_nosync(unsigned int); +extern void enable_irq(unsigned int); + +struct irqaction; +struct pt_regs; +int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); +#endif + +#endif /* _ASM_M32R_IRQ_H */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/kmap_types.h 2004-09-03 00:57:17.423390504 -0700 @@ -0,0 +1,34 @@ +#ifndef __M32R_KMAP_TYPES_H +#define __M32R_KMAP_TYPES_H + +/* Dummy header just to define km_type. */ + +#include + +#ifdef CONFIG_DEBUG_HIGHMEM +# define D(n) __KM_FENCE_##n , +#else +# define D(n) +#endif + +enum km_type { +D(0) KM_BOUNCE_READ, +D(1) KM_SKB_SUNRPC_DATA, +D(2) KM_SKB_DATA_SOFTIRQ, +D(3) KM_USER0, +D(4) KM_USER1, +D(5) KM_BIO_SRC_IRQ, +D(6) KM_BIO_DST_IRQ, +D(7) KM_PTE0, +D(8) KM_PTE1, +D(9) KM_IRQ0, +D(10) KM_IRQ1, +D(11) KM_SOFTIRQ0, +D(12) KM_SOFTIRQ1, +D(13) KM_TYPE_NR +}; + +#undef D + +#endif /* __M32R_KMAP_TYPES_H */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/linkage.h 2004-09-03 00:57:17.423390504 -0700 @@ -0,0 +1,7 @@ +#ifndef __ASM_LINKAGE_H +#define __ASM_LINKAGE_H + +#define __ALIGN .balign 4 +#define __ALIGN_STR ".balign 4" + +#endif /* __ASM_LINKAGE_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/local.h 2004-09-03 00:57:17.423390504 -0700 @@ -0,0 +1,6 @@ +#ifndef __M32R_LOCAL_H +#define __M32R_LOCAL_H + +#include + +#endif /* __M32R_LOCAL_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/m32102.h 2004-09-03 00:57:17.425390200 -0700 @@ -0,0 +1,265 @@ +#ifndef _M32102_H_ +#define _M32102_H_ + +/* + * Mitsubishi M32R 32102 group + * Copyright (c) 2001 [Hitoshi Yamamoto] All rights reserved. + */ +/* $Id$ */ + +/*======================================================================* + * Special Function Register + *======================================================================*/ +#define M32R_SFR_OFFSET (0x00E00000) /* 0x00E00000-0x00EFFFFF 1[MB] */ + +/* + * Clock and Power Management registers. + */ +#define M32R_CPM_OFFSET (0x000F4000+M32R_SFR_OFFSET) + +#define M32R_CPM_CPUCLKCR_PORTL (0x00+M32R_CPM_OFFSET) +#define M32R_CPM_CLKMOD_PORTL (0x04+M32R_CPM_OFFSET) +#define M32R_CPM_PLLCR_PORTL (0x08+M32R_CPM_OFFSET) + +/* + * Multi Function Timer registers. + */ +#define M32R_MFT_OFFSET (0x000FC000+M32R_SFR_OFFSET) + +#define M32R_MFTCR_PORTL (0x000+M32R_MFT_OFFSET) /* MFT control */ +#define M32R_MFTRPR_PORTL (0x004+M32R_MFT_OFFSET) /* MFT real port */ + +#define M32R_MFT0_OFFSET (0x100+M32R_MFT_OFFSET) +#define M32R_MFT0MOD_PORTL (0x00+M32R_MFT0_OFFSET) /* MFT0 mode */ +#define M32R_MFT0BOS_PORTL (0x04+M32R_MFT0_OFFSET) /* MFT0 b-port output status */ +#define M32R_MFT0CUT_PORTL (0x08+M32R_MFT0_OFFSET) /* MFT0 count */ +#define M32R_MFT0RLD_PORTL (0x0C+M32R_MFT0_OFFSET) /* MFT0 reload */ +#define M32R_MFT0CMPRLD_PORTL (0x10+M32R_MFT0_OFFSET) /* MFT0 compare reload */ + +#define M32R_MFT1_OFFSET (0x200+M32R_MFT_OFFSET) +#define M32R_MFT1MOD_PORTL (0x00+M32R_MFT1_OFFSET) /* MFT1 mode */ +#define M32R_MFT1BOS_PORTL (0x04+M32R_MFT1_OFFSET) /* MFT1 b-port output status */ +#define M32R_MFT1CUT_PORTL (0x08+M32R_MFT1_OFFSET) /* MFT1 count */ +#define M32R_MFT1RLD_PORTL (0x0C+M32R_MFT1_OFFSET) /* MFT1 reload */ +#define M32R_MFT1CMPRLD_PORTL (0x10+M32R_MFT1_OFFSET) /* MFT1 compare reload */ + +#define M32R_MFT2_OFFSET (0x300+M32R_MFT_OFFSET) +#define M32R_MFT2MOD_PORTL (0x00+M32R_MFT2_OFFSET) /* MFT2 mode */ +#define M32R_MFT2BOS_PORTL (0x04+M32R_MFT2_OFFSET) /* MFT2 b-port output status */ +#define M32R_MFT2CUT_PORTL (0x08+M32R_MFT2_OFFSET) /* MFT2 count */ +#define M32R_MFT2RLD_PORTL (0x0C+M32R_MFT2_OFFSET) /* MFT2 reload */ +#define M32R_MFT2CMPRLD_PORTL (0x10+M32R_MFT2_OFFSET) /* MFT2 compare reload */ + +#define M32R_MFT3_OFFSET (0x400+M32R_MFT_OFFSET) +#define M32R_MFT3MOD_PORTL (0x00+M32R_MFT3_OFFSET) /* MFT3 mode */ +#define M32R_MFT3BOS_PORTL (0x04+M32R_MFT3_OFFSET) /* MFT3 b-port output status */ +#define M32R_MFT3CUT_PORTL (0x08+M32R_MFT3_OFFSET) /* MFT3 count */ +#define M32R_MFT3RLD_PORTL (0x0C+M32R_MFT3_OFFSET) /* MFT3 reload */ +#define M32R_MFT3CMPRLD_PORTL (0x10+M32R_MFT3_OFFSET) /* MFT3 compare reload */ + +#define M32R_MFT4_OFFSET (0x500+M32R_MFT_OFFSET) +#define M32R_MFT4MOD_PORTL (0x00+M32R_MFT4_OFFSET) /* MFT4 mode */ +#define M32R_MFT4BOS_PORTL (0x04+M32R_MFT4_OFFSET) /* MFT4 b-port output status */ +#define M32R_MFT4CUT_PORTL (0x08+M32R_MFT4_OFFSET) /* MFT4 count */ +#define M32R_MFT4RLD_PORTL (0x0C+M32R_MFT4_OFFSET) /* MFT4 reload */ +#define M32R_MFT4CMPRLD_PORTL (0x10+M32R_MFT4_OFFSET) /* MFT4 compare reload */ + +#define M32R_MFT5_OFFSET (0x600+M32R_MFT_OFFSET) +#define M32R_MFT5MOD_PORTL (0x00+M32R_MFT5_OFFSET) /* MFT4 mode */ +#define M32R_MFT5BOS_PORTL (0x04+M32R_MFT5_OFFSET) /* MFT4 b-port output status */ +#define M32R_MFT5CUT_PORTL (0x08+M32R_MFT5_OFFSET) /* MFT4 count */ +#define M32R_MFT5RLD_PORTL (0x0C+M32R_MFT5_OFFSET) /* MFT4 reload */ +#define M32R_MFT5CMPRLD_PORTL (0x10+M32R_MFT5_OFFSET) /* MFT4 compare reload */ + +#ifdef CONFIG_CHIP_M32700 +#define M32R_MFTCR_MFT0MSK (1UL<<31) /* b0 */ +#define M32R_MFTCR_MFT1MSK (1UL<<30) /* b1 */ +#define M32R_MFTCR_MFT2MSK (1UL<<29) /* b2 */ +#define M32R_MFTCR_MFT3MSK (1UL<<28) /* b3 */ +#define M32R_MFTCR_MFT4MSK (1UL<<27) /* b4 */ +#define M32R_MFTCR_MFT5MSK (1UL<<26) /* b5 */ +#define M32R_MFTCR_MFT0EN (1UL<<23) /* b8 */ +#define M32R_MFTCR_MFT1EN (1UL<<22) /* b9 */ +#define M32R_MFTCR_MFT2EN (1UL<<21) /* b10 */ +#define M32R_MFTCR_MFT3EN (1UL<<20) /* b11 */ +#define M32R_MFTCR_MFT4EN (1UL<<19) /* b12 */ +#define M32R_MFTCR_MFT5EN (1UL<<18) /* b13 */ +#else /* not CONFIG_CHIP_M32700 */ +#define M32R_MFTCR_MFT0MSK (1UL<<15) /* b16 */ +#define M32R_MFTCR_MFT1MSK (1UL<<14) /* b17 */ +#define M32R_MFTCR_MFT2MSK (1UL<<13) /* b18 */ +#define M32R_MFTCR_MFT3MSK (1UL<<12) /* b19 */ +#define M32R_MFTCR_MFT4MSK (1UL<<11) /* b20 */ +#define M32R_MFTCR_MFT5MSK (1UL<<10) /* b21 */ +#define M32R_MFTCR_MFT0EN (1UL<<7) /* b24 */ +#define M32R_MFTCR_MFT1EN (1UL<<6) /* b25 */ +#define M32R_MFTCR_MFT2EN (1UL<<5) /* b26 */ +#define M32R_MFTCR_MFT3EN (1UL<<4) /* b27 */ +#define M32R_MFTCR_MFT4EN (1UL<<3) /* b28 */ +#define M32R_MFTCR_MFT5EN (1UL<<2) /* b29 */ +#endif /* not CONFIG_CHIP_M32700 */ + +#define M32R_MFTMOD_CC_MASK (1UL<<15) /* b16 */ +#define M32R_MFTMOD_TCCR (1UL<<13) /* b18 */ +#define M32R_MFTMOD_GTSEL000 (0UL<<8) /* b21-23 : 000 */ +#define M32R_MFTMOD_GTSEL001 (1UL<<8) /* b21-23 : 001 */ +#define M32R_MFTMOD_GTSEL010 (2UL<<8) /* b21-23 : 010 */ +#define M32R_MFTMOD_GTSEL011 (3UL<<8) /* b21-23 : 011 */ +#define M32R_MFTMOD_GTSEL110 (6UL<<8) /* b21-23 : 110 */ +#define M32R_MFTMOD_GTSEL111 (7UL<<8) /* b21-23 : 111 */ +#define M32R_MFTMOD_CMSEL (1UL<<3) /* b28 */ +#define M32R_MFTMOD_CSSEL000 (0UL<<0) /* b29-b31 : 000 */ +#define M32R_MFTMOD_CSSEL001 (1UL<<0) /* b29-b31 : 001 */ +#define M32R_MFTMOD_CSSEL010 (2UL<<0) /* b29-b31 : 010 */ +#define M32R_MFTMOD_CSSEL011 (3UL<<0) /* b29-b31 : 011 */ +#define M32R_MFTMOD_CSSEL100 (4UL<<0) /* b29-b31 : 100 */ +#define M32R_MFTMOD_CSSEL110 (6UL<<0) /* b29-b31 : 110 */ + +/* + * Serial I/O registers. + */ +#define M32R_SIO_OFFSET (0x000FD000+M32R_SFR_OFFSET) + +#define M32R_SIO0_CR_PORTL (0x000+M32R_SIO_OFFSET) +#define M32R_SIO0_MOD0_PORTL (0x004+M32R_SIO_OFFSET) +#define M32R_SIO0_MOD1_PORTL (0x008+M32R_SIO_OFFSET) +#define M32R_SIO0_STS_PORTL (0x00C+M32R_SIO_OFFSET) +#define M32R_SIO0_TRCR_PORTL (0x010+M32R_SIO_OFFSET) +#define M32R_SIO0_BAUR_PORTL (0x014+M32R_SIO_OFFSET) +#define M32R_SIO0_RBAUR_PORTL (0x018+M32R_SIO_OFFSET) +#define M32R_SIO0_TXB_PORTL (0x01C+M32R_SIO_OFFSET) +#define M32R_SIO0_RXB_PORTL (0x020+M32R_SIO_OFFSET) + +/* + * Interrupt Control Unit registers. + */ +#define M32R_ICU_OFFSET (0x000FF000+M32R_SFR_OFFSET) +#define M32R_ICU_ISTS_PORTL (0x004+M32R_ICU_OFFSET) +#define M32R_ICU_IREQ0_PORTL (0x008+M32R_ICU_OFFSET) +#define M32R_ICU_IREQ1_PORTL (0x00C+M32R_ICU_OFFSET) +#define M32R_ICU_SBICR_PORTL (0x018+M32R_ICU_OFFSET) +#define M32R_ICU_IMASK_PORTL (0x01C+M32R_ICU_OFFSET) +#define M32R_ICU_CR1_PORTL (0x200+M32R_ICU_OFFSET) /* INT0 */ +#define M32R_ICU_CR2_PORTL (0x204+M32R_ICU_OFFSET) /* INT1 */ +#define M32R_ICU_CR3_PORTL (0x208+M32R_ICU_OFFSET) /* INT2 */ +#define M32R_ICU_CR4_PORTL (0x20C+M32R_ICU_OFFSET) /* INT3 */ +#define M32R_ICU_CR5_PORTL (0x210+M32R_ICU_OFFSET) /* INT4 */ +#define M32R_ICU_CR6_PORTL (0x214+M32R_ICU_OFFSET) /* INT5 */ +#define M32R_ICU_CR7_PORTL (0x218+M32R_ICU_OFFSET) /* INT6 */ +#define M32R_ICU_CR16_PORTL (0x23C+M32R_ICU_OFFSET) /* MFT0 */ +#define M32R_ICU_CR17_PORTL (0x240+M32R_ICU_OFFSET) /* MFT1 */ +#define M32R_ICU_CR18_PORTL (0x244+M32R_ICU_OFFSET) /* MFT2 */ +#define M32R_ICU_CR19_PORTL (0x248+M32R_ICU_OFFSET) /* MFT3 */ +#define M32R_ICU_CR20_PORTL (0x24C+M32R_ICU_OFFSET) /* MFT4 */ +#define M32R_ICU_CR21_PORTL (0x250+M32R_ICU_OFFSET) /* MFT5 */ +#define M32R_ICU_CR32_PORTL (0x27C+M32R_ICU_OFFSET) /* DMA0 */ +#define M32R_ICU_CR33_PORTL (0x280+M32R_ICU_OFFSET) /* DMA1 */ +#define M32R_ICU_CR48_PORTL (0x2BC+M32R_ICU_OFFSET) /* SIO0 */ +#define M32R_ICU_CR49_PORTL (0x2C0+M32R_ICU_OFFSET) /* SIO0 */ +#define M32R_ICU_CR50_PORTL (0x2C4+M32R_ICU_OFFSET) /* SIO1 */ +#define M32R_ICU_CR51_PORTL (0x2C8+M32R_ICU_OFFSET) /* SIO1 */ +#define M32R_ICU_CR52_PORTL (0x2CC+M32R_ICU_OFFSET) /* SIO2 */ +#define M32R_ICU_CR53_PORTL (0x2D0+M32R_ICU_OFFSET) /* SIO2 */ +#define M32R_ICU_CR54_PORTL (0x2D4+M32R_ICU_OFFSET) /* SIO3 */ +#define M32R_ICU_CR55_PORTL (0x2D8+M32R_ICU_OFFSET) /* SIO3 */ +#define M32R_ICU_CR56_PORTL (0x2DC+M32R_ICU_OFFSET) /* SIO4 */ +#define M32R_ICU_CR57_PORTL (0x2E0+M32R_ICU_OFFSET) /* SIO4 */ + +#ifdef CONFIG_SMP +#define M32R_ICU_IPICR0_PORTL (0x2dc+M32R_ICU_OFFSET) /* IPI0 */ +#define M32R_ICU_IPICR1_PORTL (0x2e0+M32R_ICU_OFFSET) /* IPI1 */ +#define M32R_ICU_IPICR2_PORTL (0x2e4+M32R_ICU_OFFSET) /* IPI2 */ +#define M32R_ICU_IPICR3_PORTL (0x2e8+M32R_ICU_OFFSET) /* IPI3 */ +#define M32R_ICU_IPICR4_PORTL (0x2ec+M32R_ICU_OFFSET) /* IPI4 */ +#define M32R_ICU_IPICR5_PORTL (0x2f0+M32R_ICU_OFFSET) /* IPI5 */ +#define M32R_ICU_IPICR6_PORTL (0x2f4+M32R_ICU_OFFSET) /* IPI6 */ +#define M32R_ICU_IPICR7_PORTL (0x2f8+M32R_ICU_OFFSET) /* IPI7 */ +#endif /* CONFIG_SMP */ + +#define M32R_ICUIMASK_IMSK0 (0UL<<16) /* b13-b15: Disable interrupt */ +#define M32R_ICUIMASK_IMSK1 (1UL<<16) /* b13-b15: Enable level 0 interrupt */ +#define M32R_ICUIMASK_IMSK2 (2UL<<16) /* b13-b15: Enable level 0,1 interrupt */ +#define M32R_ICUIMASK_IMSK3 (3UL<<16) /* b13-b15: Enable level 0-2 interrupt */ +#define M32R_ICUIMASK_IMSK4 (4UL<<16) /* b13-b15: Enable level 0-3 interrupt */ +#define M32R_ICUIMASK_IMSK5 (5UL<<16) /* b13-b15: Enable level 0-4 interrupt */ +#define M32R_ICUIMASK_IMSK6 (6UL<<16) /* b13-b15: Enable level 0-5 interrupt */ +#define M32R_ICUIMASK_IMSK7 (7UL<<16) /* b13-b15: Enable level 0-6 interrupt */ + +#define M32R_ICUCR_IEN (1UL<<12) /* b19: Interrupt enable */ +#define M32R_ICUCR_IRQ (1UL<<8) /* b23: Interrupt request */ +#define M32R_ICUCR_ISMOD00 (0UL<<4) /* b26-b27: Interrupt sense mode Edge HtoL */ +#define M32R_ICUCR_ISMOD01 (1UL<<4) /* b26-b27: Interrupt sense mode Level L */ +#define M32R_ICUCR_ISMOD10 (2UL<<4) /* b26-b27: Interrupt sense mode Edge LtoH*/ +#define M32R_ICUCR_ISMOD11 (3UL<<4) /* b26-b27: Interrupt sense mode Level H */ +#define M32R_ICUCR_ILEVEL0 (0UL<<0) /* b29-b31: Interrupt priority level 0 */ +#define M32R_ICUCR_ILEVEL1 (1UL<<0) /* b29-b31: Interrupt priority level 1 */ +#define M32R_ICUCR_ILEVEL2 (2UL<<0) /* b29-b31: Interrupt priority level 2 */ +#define M32R_ICUCR_ILEVEL3 (3UL<<0) /* b29-b31: Interrupt priority level 3 */ +#define M32R_ICUCR_ILEVEL4 (4UL<<0) /* b29-b31: Interrupt priority level 4 */ +#define M32R_ICUCR_ILEVEL5 (5UL<<0) /* b29-b31: Interrupt priority level 5 */ +#define M32R_ICUCR_ILEVEL6 (6UL<<0) /* b29-b31: Interrupt priority level 6 */ +#define M32R_ICUCR_ILEVEL7 (7UL<<0) /* b29-b31: Disable interrupt */ + +#define M32R_IRQ_INT0 (1) /* INT0 */ +#define M32R_IRQ_INT1 (2) /* INT1 */ +#define M32R_IRQ_INT2 (3) /* INT2 */ +#define M32R_IRQ_INT3 (4) /* INT3 */ +#define M32R_IRQ_INT4 (5) /* INT4 */ +#define M32R_IRQ_INT5 (6) /* INT5 */ +#define M32R_IRQ_INT6 (7) /* INT6 */ +#define M32R_IRQ_MFT0 (16) /* MFT0 */ +#define M32R_IRQ_MFT1 (17) /* MFT1 */ +#define M32R_IRQ_MFT2 (18) /* MFT2 */ +#define M32R_IRQ_MFT3 (19) /* MFT3 */ +#define M32R_IRQ_MFT4 (20) /* MFT4 */ +#define M32R_IRQ_MFT5 (21) /* MFT5 */ +#define M32R_IRQ_DMA0 (32) /* DMA0 */ +#define M32R_IRQ_DMA1 (33) /* DMA1 */ +#define M32R_IRQ_SIO0_R (48) /* SIO0 send */ +#define M32R_IRQ_SIO0_S (49) /* SIO0 receive */ +#define M32R_IRQ_SIO1_R (50) /* SIO1 send */ +#define M32R_IRQ_SIO1_S (51) /* SIO1 receive */ +#define M32R_IRQ_SIO2_R (52) /* SIO2 send */ +#define M32R_IRQ_SIO2_S (53) /* SIO2 receive */ +#define M32R_IRQ_SIO3_R (54) /* SIO3 send */ +#define M32R_IRQ_SIO3_S (55) /* SIO3 receive */ +#define M32R_IRQ_SIO4_R (56) /* SIO4 send */ +#define M32R_IRQ_SIO4_S (57) /* SIO4 receive */ + +#ifdef CONFIG_SMP +#define M32R_IRQ_IPI0 (56) +#define M32R_IRQ_IPI1 (57) +#define M32R_IRQ_IPI2 (58) +#define M32R_IRQ_IPI3 (59) +#define M32R_IRQ_IPI4 (60) +#define M32R_IRQ_IPI5 (61) +#define M32R_IRQ_IPI6 (62) +#define M32R_IRQ_IPI7 (63) +#define M32R_CPUID_PORTL (0xffffffe0) + +#define M32R_FPGA_TOP (0x000F0000+M32R_SFR_OFFSET) + +#define M32R_FPGA_NUM_OF_CPUS_PORTL (0x00+M32R_FPGA_TOP) +#define M32R_FPGA_CPU_NAME0_PORTL (0x10+M32R_FPGA_TOP) +#define M32R_FPGA_CPU_NAME1_PORTL (0x14+M32R_FPGA_TOP) +#define M32R_FPGA_CPU_NAME2_PORTL (0x18+M32R_FPGA_TOP) +#define M32R_FPGA_CPU_NAME3_PORTL (0x1c+M32R_FPGA_TOP) +#define M32R_FPGA_MODEL_ID0_PORTL (0x20+M32R_FPGA_TOP) +#define M32R_FPGA_MODEL_ID1_PORTL (0x24+M32R_FPGA_TOP) +#define M32R_FPGA_MODEL_ID2_PORTL (0x28+M32R_FPGA_TOP) +#define M32R_FPGA_MODEL_ID3_PORTL (0x2c+M32R_FPGA_TOP) +#define M32R_FPGA_VERSION0_PORTL (0x30+M32R_FPGA_TOP) +#define M32R_FPGA_VERSION1_PORTL (0x34+M32R_FPGA_TOP) + +#ifndef __ASSEMBLY__ +/* For NETDEV WATCHDOG */ +typedef struct { + unsigned long icucr; /* ICU Control Register */ +} icu_data_t; + +extern icu_data_t icu_data[]; +#endif + +#endif /* CONFIG_SMP */ + +#endif /* _M32102_H_ */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/m32102peri.h 2004-09-03 00:57:17.428389744 -0700 @@ -0,0 +1,468 @@ +/* $Id$ + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2000,2001 by Hiroyuki Kondo + */ + +#ifndef __ASSEMBLY__ + +typedef void V; +typedef char B; +typedef short S; +typedef int W; +typedef long L; +typedef float F; +typedef double D; +typedef unsigned char UB; +typedef unsigned short US; +typedef unsigned int UW; +typedef unsigned long UL; +typedef const unsigned int CUW; + +/********************************* + +M32102 ICU + +*********************************/ +#define ICUISTS (UW *)0xa0EFF004 +#define ICUIREQ0 (UW *)0xa0EFF008 +#define ICUIREQ1 (UW *)0xa0EFF00C + +#define ICUSBICR (UW *)0xa0EFF018 +#define ICUIMASK (UW *)0xa0EFF01C + +#define ICUCR1 (UW *)0xa0EFF200 /* INT0 */ +#define ICUCR2 (UW *)0xa0EFF204 /* INT1 */ +#define ICUCR3 (UW *)0xa0EFF208 /* INT2 */ +#define ICUCR4 (UW *)0xa0EFF20C /* INT3 */ +#define ICUCR5 (UW *)0xa0EFF210 /* INT4 */ +#define ICUCR6 (UW *)0xa0EFF214 /* INT5 */ +#define ICUCR7 (UW *)0xa0EFF218 /* INT6 */ + +#define ICUCR16 (UW *)0xa0EFF23C /* MFT0 */ +#define ICUCR17 (UW *)0xa0EFF240 /* MFT1 */ +#define ICUCR18 (UW *)0xa0EFF244 /* MFT2 */ +#define ICUCR19 (UW *)0xa0EFF248 /* MFT3 */ +#define ICUCR20 (UW *)0xa0EFF24C /* MFT4 */ +#define ICUCR21 (UW *)0xa0EFF250 /* MFT5 */ + +#define ICUCR32 (UW *)0xa0EFF27C /* DMA0 */ +#define ICUCR33 (UW *)0xa0EFF280 /* DMA1 */ + +#define ICUCR48 (UW *)0xa0EFF2BC /* SIO0R */ +#define ICUCR49 (UW *)0xa0EFF2C0 /* SIO0S */ +#define ICUCR50 (UW *)0xa0EFF2C4 /* SIO1R */ +#define ICUCR51 (UW *)0xa0EFF2C8 /* SIO1S */ +#define ICUCR52 (UW *)0xa0EFF2CC /* SIO2R */ +#define ICUCR53 (UW *)0xa0EFF2D0 /* SIO2S */ +#define ICUCR54 (UW *)0xa0EFF2D4 /* SIO3R */ +#define ICUCR55 (UW *)0xa0EFF2D8 /* SIO3S */ +#define ICUCR56 (UW *)0xa0EFF2DC /* SIO4R */ +#define ICUCR57 (UW *)0xa0EFF2E0 /* SIO4S */ + +/********************************* + +M32102 MFT + +*********************************/ +#define MFTCR (US *)0xa0EFC002 +#define MFTRPR (UB *)0xa0EFC006 + +#define MFT0MOD (US *)0xa0EFC102 +#define MFT0BOS (US *)0xa0EFC106 +#define MFT0CUT (US *)0xa0EFC10A +#define MFT0RLD (US *)0xa0EFC10E +#define MFT0CRLD (US *)0xa0EFC112 + +#define MFT1MOD (US *)0xa0EFC202 +#define MFT1BOS (US *)0xa0EFC206 +#define MFT1CUT (US *)0xa0EFC20A +#define MFT1RLD (US *)0xa0EFC20E +#define MFT1CRLD (US *)0xa0EFC212 + +#define MFT2MOD (US *)0xa0EFC302 +#define MFT2BOS (US *)0xa0EFC306 +#define MFT2CUT (US *)0xa0EFC30A +#define MFT2RLD (US *)0xa0EFC30E +#define MFT2CRLD (US *)0xa0EFC312 + +#define MFT3MOD (US *)0xa0EFC402 +#define MFT3CUT (US *)0xa0EFC40A +#define MFT3RLD (US *)0xa0EFC40E +#define MFT3CRLD (US *)0xa0EFC412 + +#define MFT4MOD (US *)0xa0EFC502 +#define MFT4CUT (US *)0xa0EFC50A +#define MFT4RLD (US *)0xa0EFC50E +#define MFT4CRLD (US *)0xa0EFC512 + +#define MFT5MOD (US *)0xa0EFC602 +#define MFT5CUT (US *)0xa0EFC60A +#define MFT5RLD (US *)0xa0EFC60E +#define MFT5CRLD (US *)0xa0EFC612 + +/********************************* + +M32102 SIO + +*********************************/ + +#define SIO0CR (volatile int *)0xa0efd000 +#define SIO0MOD0 (volatile int *)0xa0efd004 +#define SIO0MOD1 (volatile int *)0xa0efd008 +#define SIO0STS (volatile int *)0xa0efd00c +#define SIO0IMASK (volatile int *)0xa0efd010 +#define SIO0BAUR (volatile int *)0xa0efd014 +#define SIO0RBAUR (volatile int *)0xa0efd018 +#define SIO0TXB (volatile int *)0xa0efd01c +#define SIO0RXB (volatile int *)0xa0efd020 + +#define SIO1CR (volatile int *)0xa0efd100 +#define SIO1MOD0 (volatile int *)0xa0efd104 +#define SIO1MOD1 (volatile int *)0xa0efd108 +#define SIO1STS (volatile int *)0xa0efd10c +#define SIO1IMASK (volatile int *)0xa0efd110 +#define SIO1BAUR (volatile int *)0xa0efd114 +#define SIO1RBAUR (volatile int *)0xa0efd118 +#define SIO1TXB (volatile int *)0xa0efd11c +#define SIO1RXB (volatile int *)0xa0efd120 +/********************************* + +M32102 PORT + +*********************************/ +#define PIEN (UB *)0xa0EF1003 /* input enable */ + +#define P0DATA (UB *)0xa0EF1020 /* data */ +#define P1DATA (UB *)0xa0EF1021 +#define P2DATA (UB *)0xa0EF1022 +#define P3DATA (UB *)0xa0EF1023 +#define P4DATA (UB *)0xa0EF1024 +#define P5DATA (UB *)0xa0EF1025 +#define P6DATA (UB *)0xa0EF1026 +#define P7DATA (UB *)0xa0EF1027 + +#define P0DIR (UB *)0xa0EF1040 /* direction */ +#define P1DIR (UB *)0xa0EF1041 +#define P2DIR (UB *)0xa0EF1042 +#define P3DIR (UB *)0xa0EF1043 +#define P4DIR (UB *)0xa0EF1044 +#define P5DIR (UB *)0xa0EF1045 +#define P6DIR (UB *)0xa0EF1046 +#define P7DIR (UB *)0xa0EF1047 + +#define P0MOD (US *)0xa0EF1060 /* mode control */ +#define P1MOD (US *)0xa0EF1062 +#define P2MOD (US *)0xa0EF1064 +#define P3MOD (US *)0xa0EF1066 +#define P4MOD (US *)0xa0EF1068 +#define P5MOD (US *)0xa0EF106A +#define P6MOD (US *)0xa0EF106C +#define P7MOD (US *)0xa0EF106E + +#define P0ODCR (UB *)0xa0EF1080 /* open-drain control */ +#define P1ODCR (UB *)0xa0EF1081 +#define P2ODCR (UB *)0xa0EF1082 +#define P3ODCR (UB *)0xa0EF1083 +#define P4ODCR (UB *)0xa0EF1084 +#define P5ODCR (UB *)0xa0EF1085 +#define P6ODCR (UB *)0xa0EF1086 +#define P7ODCR (UB *)0xa0EF1087 + +/********************************* + +M32102 Cache + +********************************/ + +#define MCCR (US *)0xFFFFFFFE + + +#else /* __ASSEMBLY__ */ + +;; +;; PIO 0x80ef1000 +;; + +#define PIEN 0xa0ef1000 + +#define P0DATA 0xa0ef1020 +#define P1DATA 0xa0ef1021 +#define P2DATA 0xa0ef1022 +#define P3DATA 0xa0ef1023 +#define P4DATA 0xa0ef1024 +#define P5DATA 0xa0ef1025 +#define P6DATA 0xa0ef1026 +#define P7DATA 0xa0ef1027 + +#define P0DIR 0xa0ef1040 +#define P1DIR 0xa0ef1041 +#define P2DIR 0xa0ef1042 +#define P3DIR 0xa0ef1043 +#define P4DIR 0xa0ef1044 +#define P5DIR 0xa0ef1045 +#define P6DIR 0xa0ef1046 +#define P7DIR 0xa0ef1047 + +#define P0MOD 0xa0ef1060 +#define P1MOD 0xa0ef1062 +#define P2MOD 0xa0ef1064 +#define P3MOD 0xa0ef1066 +#define P4MOD 0xa0ef1068 +#define P5MOD 0xa0ef106a +#define P6MOD 0xa0ef106c +#define P7MOD 0xa0ef106e +; +#define P0ODCR 0xa0ef1080 +#define P1ODCR 0xa0ef1081 +#define P2ODCR 0xa0ef1082 +#define P3ODCR 0xa0ef1083 +#define P4ODCR 0xa0ef1084 +#define P5ODCR 0xa0ef1085 +#define P6ODCR 0xa0ef1086 +#define P7ODCR 0xa0ef1087 + +;; +;; WDT 0xa0ef2000 +;; + +#define WDTCR 0xa0ef2000 + + +;; +;; CLK 0xa0ef4000 +;; + +#define CPUCLKCR 0xa0ef4000 +#define CLKMOD 0xa0ef4004 +#define PLLCR 0xa0ef4008 + + +;; +;; BSEL 0xa0ef5000 +;; + +#define BSEL0CR 0xa0ef5000 +#define BSEL1CR 0xa0ef5004 +#define BSEL2CR 0xa0ef5008 +#define BSEL3CR 0xa0ef500c +#define BSEL4CR 0xa0ef5010 +#define BSEL5CR 0xa0ef5014 + + +;; +;; SDRAMC 0xa0ef6000 +;; + +#define SDRF0 0xa0ef6000 +#define SDRF1 0xa0ef6004 +#define SDIR0 0xa0ef6008 +#define SDIR1 0xa0ef600c +#define SDBR 0xa0ef6010 + +;; CH0 +#define SD0ADR 0xa0ef6020 +#define SD0SZ 0xa0ef6022 +#define SD0ER 0xa0ef6024 +#define SD0TR 0xa0ef6028 +#define SD0MOD 0xa0ef602c + +;; CH1 +#define SD1ADR 0xa0ef6040 +#define SD1SZ 0xa0ef6042 +#define SD1ER 0xa0ef6044 +#define SD1TR 0xa0ef6048 +#define SD1MOD 0xa0ef604c + + +;; +;; DMAC 0xa0ef8000 +;; + +#define DMAEN 0xa0ef8000 +#define DMAISTS 0xa0ef8004 +#define DMAEDET 0xa0ef8008 +#define DMAASTS 0xa0ef800c + +;; CH0 +#define DMA0CR0 0xa0ef8100 +#define DMA0CR1 0xa0ef8104 +#define DMA0CSA 0xa0ef8108 +#define DMA0RSA 0xa0ef810c +#define DMA0CDA 0xa0ef8110 +#define DMA0RDA 0xa0ef8114 +#define DMA0CBCUT 0xa0ef8118 +#define DMA0RBCUT 0xa0ef811c + +;; CH1 +#define DMA1CR0 0xa0ef8200 +#define DMA1CR1 0xa0ef8204 +#define DMA1CSA 0xa0ef8208 +#define DMA1RSA 0xa0ef820c +#define DMA1CDA 0xa0ef8210 +#define DMA1RDA 0xa0ef8214 +#define DMA1CBCUT 0xa0ef8218 +#define DMA1RBCUT 0xa0ef821c + + +;; +;; MFT 0xa0efc000 +;; + +#define MFTCR 0xa0efc000 +#define MFTRPR 0xa0efc004 + +;; CH0 +#define MFT0MOD 0xa0efc100 +#define MFT0BOS 0xa0efc104 +#define MFT0CUT 0xa0efc108 +#define MFT0RLD 0xa0efc10c +#define MFT0CMPRLD 0xa0efc110 + +;; CH1 +#define MFT1MOD 0xa0efc200 +#define MFT1BOS 0xa0efc204 +#define MFT1CUT 0xa0efc208 +#define MFT1RLD 0xa0efc20c +#define MFT1CMPRLD 0xa0efc210 + +;; CH2 +#define MFT2MOD 0xa0efc300 +#define MFT2BOS 0xa0efc304 +#define MFT2CUT 0xa0efc308 +#define MFT2RLD 0xa0efc30c +#define MFT2CMPRLD 0xa0efc310 + +;; CH3 +#define MFT3MOD 0xa0efc400 +#define MFT3BOS 0xa0efc404 +#define MFT3CUT 0xa0efc408 +#define MFT3RLD 0xa0efc40c +#define MFT3CMPRLD 0xa0efc410 + +;; CH4 +#define MFT4MOD 0xa0efc500 +#define MFT4BOS 0xa0efc504 +#define MFT4CUT 0xa0efc508 +#define MFT4RLD 0xa0efc50c +#define MFT4CMPRLD 0xa0efc510 + +;; CH5 +#define MFT5MOD 0xa0efc600 +#define MFT5BOS 0xa0efc604 +#define MFT5CUT 0xa0efc608 +#define MFT5RLD 0xa0efc60c +#define MFT5CMPRLD 0xa0efc610 + + +;; +;; SIO 0xa0efd000 +;; + +;; CH0 +#define SIO0CR 0xa0efd000 +#define SIO0MOD0 0xa0efd004 +#define SIO0MOD1 0xa0efd008 +#define SIO0STS 0xa0efd00c +#define SIO0IMASK 0xa0efd010 +#define SIO0BAUR 0xa0efd014 +#define SIO0RBAUR 0xa0efd018 +#define SIO0TXB 0xa0efd01c +#define SIO0RXB 0xa0efd020 + +;; CH1 +#define SIO1CR 0xa0efd100 +#define SIO1MOD0 0xa0efd104 +#define SIO1MOD1 0xa0efd108 +#define SIO1STS 0xa0efd10c +#define SIO1IMASK 0xa0efd110 +#define SIO1BAUR 0xa0efd114 +#define SIO1RBAUR 0xa0efd118 +#define SIO1TXB 0xa0efd11c +#define SIO1RXB 0xa0efd120 + +;; CH2 +#define SIO2CR 0xa0efd200 +#define SIO2MOD0 0xa0efd204 +#define SIO2MOD1 0xa0efd208 +#define SIO2STS 0xa0efd20c +#define SIO2IMASK 0xa0efd210 +#define SIO2BAUR 0xa0efd214 +#define SIO2RBAUR 0xa0efd218 +#define SIO2TXB 0xa0efd21c +#define SIO2RXB 0xa0efd220 + +;; CH3 +#define SIO3CR 0xa0efd300 +#define SIO3MOD0 0xa0efd304 +#define SIO3MOD1 0xa0efd308 +#define SIO3STS 0xa0efd30c +#define SIO3IMASK 0xa0efd310 +#define SIO3BAUR 0xa0efd314 +#define SIO3RBAUR 0xa0efd318 +#define SIO3TXB 0xa0efd31c +#define SIO3RXB 0xa0efd320 + +;; CH4 +#define SIO4CR 0xa0efd400 +#define SIO4MOD0 0xa0efd404 +#define SIO4MOD1 0xa0efd408 +#define SIO4STS 0xa0efd40c +#define SIO4IMASK 0xa0efd410 +#define SIO4BAUR 0xa0efd414 +#define SIO4RBAUR 0xa0efd418 +#define SIO4TXB 0xa0efd41c +#define SIO4RXB 0xa0efd420 + + +;; +;; ICU 0xa0eff000 +;; + +#define ICUISTS 0xa0eff004 +#define ICUIREQ0 0xa0eff008 +#define ICUIREQ1 0xa0eff00c + +#define ICUSBICR 0xa0eff018 +#define ICUIMASK 0xa0eff01c + +#define ICUCR1 0xa0eff200 +#define ICUCR2 0xa0eff204 +#define ICUCR3 0xa0eff208 +#define ICUCR4 0xa0eff20c +#define ICUCR5 0xa0eff210 +#define ICUCR6 0xa0eff214 +#define ICUCR7 0xa0eff218 + +#define ICUCR16 0xa0eff23c +#define ICUCR17 0xa0eff240 +#define ICUCR18 0xa0eff244 +#define ICUCR19 0xa0eff248 +#define ICUCR20 0xa0eff24c +#define ICUCR21 0xa0eff250 + +#define ICUCR32 0xa0eff27c +#define ICUCR33 0xa0eff280 + +#define ICUCR48 0xa0eff2bc +#define ICUCR49 0xa0eff2c0 +#define ICUCR50 0xa0eff2c4 +#define ICUCR51 0xa0eff2c8 +#define ICUCR52 0xa0eff2cc +#define ICUCR53 0xa0eff2d0 +#define ICUCR54 0xa0eff2d4 +#define ICUCR55 0xa0eff2d8 +#define ICUCR56 0xa0eff2dc +#define ICUCR57 0xa0eff2e0 + +;; +;; CACHE +;; + +#define MCCR 0xfffffffc + + +#endif /* __ASSEMBLY__ */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/m32700ut/m32700ut_lan.h 2004-09-03 00:57:17.428389744 -0700 @@ -0,0 +1,107 @@ +/* + * include/asm/m32700ut_lan.h + * + * M32700UT-LAN board + * + * Copyright (c) 2002 Takeo Takahashi + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of + * this archive for more details. + * + * $Id$ + */ + +#ifndef _M32700UT_M32700UT_LAN_H +#define _M32700UT_M32700UT_LAN_H + +#include + +#ifndef __ASSEMBLY__ +/* + * C functions use non-cache address. + */ +#define M32700UT_LAN_BASE (0x10000000 /* + NONCACHE_OFFSET */) +#else +#define M32700UT_LAN_BASE (0x10000000 + NONCACHE_OFFSET) +#endif /* __ASSEMBLY__ */ + +/* ICU + * ICUISTS: status register + * ICUIREQ0: request register + * ICUIREQ1: request register + * ICUCR3: control register for CFIREQ# interrupt + * ICUCR4: control register for CFC Card insert interrupt + * ICUCR5: control register for CFC Card eject interrupt + * ICUCR6: control register for external interrupt + * ICUCR11: control register for MMC Card insert/eject interrupt + * ICUCR13: control register for SC error interrupt + * ICUCR14: control register for SC receive interrupt + * ICUCR15: control register for SC send interrupt + * ICUCR16: control register for SIO0 receive interrupt + * ICUCR17: control register for SIO0 send interrupt + */ +#define M32700UT_LAN_IRQ_LAN (M32700UT_LAN_PLD_IRQ_BASE + 1) /* LAN */ +#define M32700UT_LAN_IRQ_I2C (M32700UT_LAN_PLD_IRQ_BASE + 3) /* I2C */ + +#define M32700UT_LAN_ICUISTS __reg16(M32700UT_LAN_BASE + 0xc0002) +#define M32700UT_LAN_ICUISTS_VECB_MASK (0xf000) +#define M32700UT_LAN_VECB(x) ((x) & M32700UT_LAN_ICUISTS_VECB_MASK) +#define M32700UT_LAN_ICUISTS_ISN_MASK (0x07c0) +#define M32700UT_LAN_ICUISTS_ISN(x) ((x) & M32700UT_LAN_ICUISTS_ISN_MASK) +#define M32700UT_LAN_ICUIREQ0 __reg16(M32700UT_LAN_BASE + 0xc0004) +#define M32700UT_LAN_ICUCR1 __reg16(M32700UT_LAN_BASE + 0xc0010) +#define M32700UT_LAN_ICUCR3 __reg16(M32700UT_LAN_BASE + 0xc0014) + +/* + * AR register on PLD + */ +#define ARVCR0 __reg32(M32700UT_LAN_BASE + 0x40000) +#define ARVCR0_VDS 0x00080000 +#define ARVCR0_RST 0x00010000 +#define ARVCR1 __reg32(M32700UT_LAN_BASE + 0x40004) +#define ARVCR1_QVGA 0x02000000 +#define ARVCR1_NORMAL 0x01000000 +#define ARVCR1_HIEN 0x00010000 +#define ARVHCOUNT __reg32(M32700UT_LAN_BASE + 0x40008) +#define ARDATA __reg32(M32700UT_LAN_BASE + 0x40010) +#define ARINTSEL __reg32(M32700UT_LAN_BASE + 0x40014) +#define ARINTSEL_INT3 0x10000000 /* CPU INT3 */ +#define ARDATA32 __reg32(M32700UT_LAN_BASE + 0x04040010) // Block 5 +/* +#define ARINTSEL_SEL2 0x00002000 +#define ARINTSEL_SEL3 0x00001000 +#define ARINTSEL_SEL6 0x00000200 +#define ARINTSEL_SEL7 0x00000100 +#define ARINTSEL_SEL9 0x00000040 +#define ARINTSEL_SEL10 0x00000020 +#define ARINTSEL_SEL11 0x00000010 +#define ARINTSEL_SEL12 0x00000008 +*/ + +/* + * I2C register on PLD + */ +#define PLDI2CCR __reg32(M32700UT_LAN_BASE + 0x40040) +#define PLDI2CCR_ES0 0x00000001 /* enable I2C interface */ +#define PLDI2CMOD __reg32(M32700UT_LAN_BASE + 0x40044) +#define PLDI2CMOD_ACKCLK 0x00000200 +#define PLDI2CMOD_DTWD 0x00000100 +#define PLDI2CMOD_10BT 0x00000004 +#define PLDI2CMOD_ATM_NORMAL 0x00000000 +#define PLDI2CMOD_ATM_AUTO 0x00000003 +#define PLDI2CACK __reg32(M32700UT_LAN_BASE + 0x40048) +#define PLDI2CACK_ACK 0x00000001 +#define PLDI2CFREQ __reg32(M32700UT_LAN_BASE + 0x4004c) +#define PLDI2CCND __reg32(M32700UT_LAN_BASE + 0x40050) +#define PLDI2CCND_START 0x00000001 +#define PLDI2CCND_STOP 0x00000002 +#define PLDI2CSTEN __reg32(M32700UT_LAN_BASE + 0x40054) +#define PLDI2CSTEN_STEN 0x00000001 +#define PLDI2CDATA __reg32(M32700UT_LAN_BASE + 0x40060) +#define PLDI2CSTS __reg32(M32700UT_LAN_BASE + 0x40064) +#define PLDI2CSTS_TRX 0x00000020 +#define PLDI2CSTS_BB 0x00000010 +#define PLDI2CSTS_NOACK 0x00000001 /* 0:ack, 1:noack */ + +#endif /* _M32700UT_M32700UT_LAN_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/m32700ut/m32700ut_lcd.h 2004-09-03 00:57:17.429389592 -0700 @@ -0,0 +1,59 @@ +/* + * include/asm/m32700ut_lcd.h + * + * M32700UT-LCD board + * + * Copyright (c) 2002 Takeo Takahashi + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of + * this archive for more details. + * + * $Id$ + */ + +#ifndef _M32700UT_M32700UT_LCD_H +#define _M32700UT_M32700UT_LCD_H + +#include + +#ifndef __ASSEMBLY__ +/* + * C functions use non-cache address. + */ +#define M32700UT_LCD_BASE (0x10000000 /* + NONCACHE_OFFSET */) +#else +#define M32700UT_LCD_BASE (0x10000000 + NONCACHE_OFFSET) +#endif /* __ASSEMBLY__ */ + +/* + * ICU + */ +#define M32700UT_LCD_IRQ_BAT_INT (M32700UT_LCD_PLD_IRQ_BASE + 1) +#define M32700UT_LCD_IRQ_USB_INT1 (M32700UT_LCD_PLD_IRQ_BASE + 2) +#define M32700UT_LCD_IRQ_AUDT0 (M32700UT_LCD_PLD_IRQ_BASE + 3) +#define M32700UT_LCD_IRQ_AUDT2 (M32700UT_LCD_PLD_IRQ_BASE + 4) +#define M32700UT_LCD_IRQ_BATSIO_RCV (M32700UT_LCD_PLD_IRQ_BASE + 16) +#define M32700UT_LCD_IRQ_BATSIO_SND (M32700UT_LCD_PLD_IRQ_BASE + 17) +#define M32700UT_LCD_IRQ_ASNDSIO_RCV (M32700UT_LCD_PLD_IRQ_BASE + 18) +#define M32700UT_LCD_IRQ_ASNDSIO_SND (M32700UT_LCD_PLD_IRQ_BASE + 19) +#define M32700UT_LCD_IRQ_ACNLSIO_SND (M32700UT_LCD_PLD_IRQ_BASE + 21) + +#define M32700UT_LCD_ICUISTS __reg16(M32700UT_LCD_BASE + 0x300002) +#define M32700UT_LCD_ICUISTS_VECB_MASK (0xf000) +#define M32700UT_LCD_VECB(x) ((x) & M32700UT_LCD_ICUISTS_VECB_MASK) +#define M32700UT_LCD_ICUISTS_ISN_MASK (0x07c0) +#define M32700UT_LCD_ICUISTS_ISN(x) ((x) & M32700UT_LCD_ICUISTS_ISN_MASK) +#define M32700UT_LCD_ICUIREQ0 __reg16(M32700UT_LCD_BASE + 0x300004) +#define M32700UT_LCD_ICUIREQ1 __reg16(M32700UT_LCD_BASE + 0x300006) +#define M32700UT_LCD_ICUCR1 __reg16(M32700UT_LCD_BASE + 0x300020) +#define M32700UT_LCD_ICUCR2 __reg16(M32700UT_LCD_BASE + 0x300022) +#define M32700UT_LCD_ICUCR3 __reg16(M32700UT_LCD_BASE + 0x300024) +#define M32700UT_LCD_ICUCR4 __reg16(M32700UT_LCD_BASE + 0x300026) +#define M32700UT_LCD_ICUCR16 __reg16(M32700UT_LCD_BASE + 0x300030) +#define M32700UT_LCD_ICUCR17 __reg16(M32700UT_LCD_BASE + 0x300032) +#define M32700UT_LCD_ICUCR18 __reg16(M32700UT_LCD_BASE + 0x300034) +#define M32700UT_LCD_ICUCR19 __reg16(M32700UT_LCD_BASE + 0x300036) +#define M32700UT_LCD_ICUCR21 __reg16(M32700UT_LCD_BASE + 0x30003a) + +#endif /* _M32700UT_M32700UT_LCD_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/m32700ut/m32700ut_pld.h 2004-09-03 00:57:17.431389288 -0700 @@ -0,0 +1,265 @@ +/* + * include/asm/m32700ut/m32700ut_pld.h + * + * Definitions for Programable Logic Device(PLD) on M32700UT board. + * + * Copyright (c) 2002 Takeo Takahashi + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of + * this archive for more details. + * + * $Id$ + */ + +#ifndef _M32700UT_M32700UT_PLD_H +#define _M32700UT_M32700UT_PLD_H + +#include + +#if defined(CONFIG_PLAT_M32700UT_Alpha) +#define PLD_PLAT_BASE 0x08c00000 +#elif defined(CONFIG_PLAT_M32700UT) || defined(CONFIG_PLAT_USRV) +#define PLD_PLAT_BASE 0x04c00000 +#else +#error "no platform configuration" +#endif + +#ifndef __ASSEMBLY__ +/* + * C functions use non-cache address. + */ +#define PLD_BASE (PLD_PLAT_BASE /* + NONCACHE_OFFSET */) +#define __reg8 (volatile unsigned char *) +#define __reg16 (volatile unsigned short *) +#define __reg32 (volatile unsigned int *) +#else +#define PLD_BASE (PLD_PLAT_BASE + NONCACHE_OFFSET) +#define __reg8 +#define __reg16 +#define __reg32 +#endif /* __ASSEMBLY__ */ + +/* CFC */ +#define PLD_CFRSTCR __reg16(PLD_BASE + 0x0000) +#define PLD_CFSTS __reg16(PLD_BASE + 0x0002) +#define PLD_CFIMASK __reg16(PLD_BASE + 0x0004) +#define PLD_CFBUFCR __reg16(PLD_BASE + 0x0006) +#define PLD_CFVENCR __reg16(PLD_BASE + 0x0008) +#define PLD_CFCR0 __reg16(PLD_BASE + 0x000a) +#define PLD_CFCR1 __reg16(PLD_BASE + 0x000c) +#define PLD_IDERSTCR __reg16(PLD_BASE + 0x0010) + +/* MMC */ +#define PLD_MMCCR __reg16(PLD_BASE + 0x4000) +#define PLD_MMCMOD __reg16(PLD_BASE + 0x4002) +#define PLD_MMCSTS __reg16(PLD_BASE + 0x4006) +#define PLD_MMCBAUR __reg16(PLD_BASE + 0x400a) +#define PLD_MMCCMDBCUT __reg16(PLD_BASE + 0x400c) +#define PLD_MMCCDTBCUT __reg16(PLD_BASE + 0x400e) +#define PLD_MMCDET __reg16(PLD_BASE + 0x4010) +#define PLD_MMCWP __reg16(PLD_BASE + 0x4012) +#define PLD_MMCWDATA __reg16(PLD_BASE + 0x5000) +#define PLD_MMCRDATA __reg16(PLD_BASE + 0x6000) +#define PLD_MMCCMDDATA __reg16(PLD_BASE + 0x7000) +#define PLD_MMCRSPDATA __reg16(PLD_BASE + 0x7006) + +/* ICU + * ICUISTS: status register + * ICUIREQ0: request register + * ICUIREQ1: request register + * ICUCR3: control register for CFIREQ# interrupt + * ICUCR4: control register for CFC Card insert interrupt + * ICUCR5: control register for CFC Card eject interrupt + * ICUCR6: control register for external interrupt + * ICUCR11: control register for MMC Card insert/eject interrupt + * ICUCR13: control register for SC error interrupt + * ICUCR14: control register for SC receive interrupt + * ICUCR15: control register for SC send interrupt + * ICUCR16: control register for SIO0 receive interrupt + * ICUCR17: control register for SIO0 send interrupt + */ +#if !defined(CONFIG_PLAT_USRV) +#define PLD_IRQ_INT0 (M32700UT_PLD_IRQ_BASE + 0) /* None */ +#define PLD_IRQ_INT1 (M32700UT_PLD_IRQ_BASE + 1) /* reserved */ +#define PLD_IRQ_INT2 (M32700UT_PLD_IRQ_BASE + 2) /* reserved */ +#define PLD_IRQ_CFIREQ (M32700UT_PLD_IRQ_BASE + 3) /* CF IREQ */ +#define PLD_IRQ_CFC_INSERT (M32700UT_PLD_IRQ_BASE + 4) /* CF Insert */ +#define PLD_IRQ_CFC_EJECT (M32700UT_PLD_IRQ_BASE + 5) /* CF Eject */ +#define PLD_IRQ_EXINT (M32700UT_PLD_IRQ_BASE + 6) /* EXINT */ +#define PLD_IRQ_INT7 (M32700UT_PLD_IRQ_BASE + 7) /* reserved */ +#define PLD_IRQ_INT8 (M32700UT_PLD_IRQ_BASE + 8) /* reserved */ +#define PLD_IRQ_INT9 (M32700UT_PLD_IRQ_BASE + 9) /* reserved */ +#define PLD_IRQ_INT10 (M32700UT_PLD_IRQ_BASE + 10) /* reserved */ +#define PLD_IRQ_MMCCARD (M32700UT_PLD_IRQ_BASE + 11) /* MMC Insert/Eject */ +#define PLD_IRQ_INT12 (M32700UT_PLD_IRQ_BASE + 12) /* reserved */ +#define PLD_IRQ_SC_ERROR (M32700UT_PLD_IRQ_BASE + 13) /* SC error */ +#define PLD_IRQ_SC_RCV (M32700UT_PLD_IRQ_BASE + 14) /* SC receive */ +#define PLD_IRQ_SC_SND (M32700UT_PLD_IRQ_BASE + 15) /* SC send */ +#define PLD_IRQ_SIO0_RCV (M32700UT_PLD_IRQ_BASE + 16) /* SIO receive */ +#define PLD_IRQ_SIO0_SND (M32700UT_PLD_IRQ_BASE + 17) /* SIO send */ +#define PLD_IRQ_INT18 (M32700UT_PLD_IRQ_BASE + 18) /* reserved */ +#define PLD_IRQ_INT19 (M32700UT_PLD_IRQ_BASE + 19) /* reserved */ +#define PLD_IRQ_INT20 (M32700UT_PLD_IRQ_BASE + 20) /* reserved */ +#define PLD_IRQ_INT21 (M32700UT_PLD_IRQ_BASE + 21) /* reserved */ +#define PLD_IRQ_INT22 (M32700UT_PLD_IRQ_BASE + 22) /* reserved */ +#define PLD_IRQ_INT23 (M32700UT_PLD_IRQ_BASE + 23) /* reserved */ +#define PLD_IRQ_INT24 (M32700UT_PLD_IRQ_BASE + 24) /* reserved */ +#define PLD_IRQ_INT25 (M32700UT_PLD_IRQ_BASE + 25) /* reserved */ +#define PLD_IRQ_INT26 (M32700UT_PLD_IRQ_BASE + 26) /* reserved */ +#define PLD_IRQ_INT27 (M32700UT_PLD_IRQ_BASE + 27) /* reserved */ +#define PLD_IRQ_INT28 (M32700UT_PLD_IRQ_BASE + 28) /* reserved */ +#define PLD_IRQ_INT29 (M32700UT_PLD_IRQ_BASE + 29) /* reserved */ +#define PLD_IRQ_INT30 (M32700UT_PLD_IRQ_BASE + 30) /* reserved */ +#define PLD_IRQ_INT31 (M32700UT_PLD_IRQ_BASE + 31) /* reserved */ + +#else /* CONFIG_PLAT_USRV */ + +#define PLD_IRQ_INT0 (M32700UT_PLD_IRQ_BASE + 0) /* None */ +#define PLD_IRQ_INT1 (M32700UT_PLD_IRQ_BASE + 1) /* reserved */ +#define PLD_IRQ_INT2 (M32700UT_PLD_IRQ_BASE + 2) /* reserved */ +#define PLD_IRQ_CF0 (M32700UT_PLD_IRQ_BASE + 3) /* CF0# */ +#define PLD_IRQ_CF1 (M32700UT_PLD_IRQ_BASE + 4) /* CF1# */ +#define PLD_IRQ_CF2 (M32700UT_PLD_IRQ_BASE + 5) /* CF2# */ +#define PLD_IRQ_CF3 (M32700UT_PLD_IRQ_BASE + 6) /* CF3# */ +#define PLD_IRQ_CF4 (M32700UT_PLD_IRQ_BASE + 7) /* CF4# */ +#define PLD_IRQ_INT8 (M32700UT_PLD_IRQ_BASE + 8) /* reserved */ +#define PLD_IRQ_INT9 (M32700UT_PLD_IRQ_BASE + 9) /* reserved */ +#define PLD_IRQ_INT10 (M32700UT_PLD_IRQ_BASE + 10) /* reserved */ +#define PLD_IRQ_INT11 (M32700UT_PLD_IRQ_BASE + 11) /* reserved */ +#define PLD_IRQ_UART0 (M32700UT_PLD_IRQ_BASE + 12) /* UARTIRQ0 */ +#define PLD_IRQ_UART1 (M32700UT_PLD_IRQ_BASE + 13) /* UARTIRQ1 */ +#define PLD_IRQ_INT14 (M32700UT_PLD_IRQ_BASE + 14) /* reserved */ +#define PLD_IRQ_INT15 (M32700UT_PLD_IRQ_BASE + 15) /* reserved */ +#define PLD_IRQ_SNDINT (M32700UT_PLD_IRQ_BASE + 16) /* SNDINT# */ +#define PLD_IRQ_INT17 (M32700UT_PLD_IRQ_BASE + 17) /* reserved */ +#define PLD_IRQ_INT18 (M32700UT_PLD_IRQ_BASE + 18) /* reserved */ +#define PLD_IRQ_INT19 (M32700UT_PLD_IRQ_BASE + 19) /* reserved */ +#define PLD_IRQ_INT20 (M32700UT_PLD_IRQ_BASE + 20) /* reserved */ +#define PLD_IRQ_INT21 (M32700UT_PLD_IRQ_BASE + 21) /* reserved */ +#define PLD_IRQ_INT22 (M32700UT_PLD_IRQ_BASE + 22) /* reserved */ +#define PLD_IRQ_INT23 (M32700UT_PLD_IRQ_BASE + 23) /* reserved */ +#define PLD_IRQ_INT24 (M32700UT_PLD_IRQ_BASE + 24) /* reserved */ +#define PLD_IRQ_INT25 (M32700UT_PLD_IRQ_BASE + 25) /* reserved */ +#define PLD_IRQ_INT26 (M32700UT_PLD_IRQ_BASE + 26) /* reserved */ +#define PLD_IRQ_INT27 (M32700UT_PLD_IRQ_BASE + 27) /* reserved */ +#define PLD_IRQ_INT28 (M32700UT_PLD_IRQ_BASE + 28) /* reserved */ +#define PLD_IRQ_INT29 (M32700UT_PLD_IRQ_BASE + 29) /* reserved */ +#define PLD_IRQ_INT30 (M32700UT_PLD_IRQ_BASE + 30) /* reserved */ + +#endif /* CONFIG_PLAT_USRV */ + +#define PLD_ICUISTS __reg16(PLD_BASE + 0x8002) +#define PLD_ICUISTS_VECB_MASK (0xf000) +#define PLD_ICUISTS_VECB(x) ((x) & PLD_ICUISTS_VECB_MASK) +#define PLD_ICUISTS_ISN_MASK (0x07c0) +#define PLD_ICUISTS_ISN(x) ((x) & PLD_ICUISTS_ISN_MASK) +#define PLD_ICUIREQ0 __reg16(PLD_BASE + 0x8004) +#define PLD_ICUIREQ1 __reg16(PLD_BASE + 0x8006) +#define PLD_ICUCR1 __reg16(PLD_BASE + 0x8100) +#define PLD_ICUCR2 __reg16(PLD_BASE + 0x8102) +#define PLD_ICUCR3 __reg16(PLD_BASE + 0x8104) +#define PLD_ICUCR4 __reg16(PLD_BASE + 0x8106) +#define PLD_ICUCR5 __reg16(PLD_BASE + 0x8108) +#define PLD_ICUCR6 __reg16(PLD_BASE + 0x810a) +#define PLD_ICUCR7 __reg16(PLD_BASE + 0x810c) +#define PLD_ICUCR8 __reg16(PLD_BASE + 0x810e) +#define PLD_ICUCR9 __reg16(PLD_BASE + 0x8110) +#define PLD_ICUCR10 __reg16(PLD_BASE + 0x8112) +#define PLD_ICUCR11 __reg16(PLD_BASE + 0x8114) +#define PLD_ICUCR12 __reg16(PLD_BASE + 0x8116) +#define PLD_ICUCR13 __reg16(PLD_BASE + 0x8118) +#define PLD_ICUCR14 __reg16(PLD_BASE + 0x811a) +#define PLD_ICUCR15 __reg16(PLD_BASE + 0x811c) +#define PLD_ICUCR16 __reg16(PLD_BASE + 0x811e) +#define PLD_ICUCR17 __reg16(PLD_BASE + 0x8120) +#define PLD_ICUCR_IEN (0x1000) +#define PLD_ICUCR_IREQ (0x0100) +#define PLD_ICUCR_ISMOD00 (0x0000) /* Low edge */ +#define PLD_ICUCR_ISMOD01 (0x0010) /* Low level */ +#define PLD_ICUCR_ISMOD02 (0x0020) /* High edge */ +#define PLD_ICUCR_ISMOD03 (0x0030) /* High level */ +#define PLD_ICUCR_ILEVEL0 (0x0000) +#define PLD_ICUCR_ILEVEL1 (0x0001) +#define PLD_ICUCR_ILEVEL2 (0x0002) +#define PLD_ICUCR_ILEVEL3 (0x0003) +#define PLD_ICUCR_ILEVEL4 (0x0004) +#define PLD_ICUCR_ILEVEL5 (0x0005) +#define PLD_ICUCR_ILEVEL6 (0x0006) +#define PLD_ICUCR_ILEVEL7 (0x0007) + +/* Power Control of MMC and CF */ +#define PLD_CPCR __reg16(PLD_BASE + 0x14000) +#define PLD_CPCR_CF 0x0001 +#define PLD_CPCR_MMC 0x0002 + +/* LED Control + * + * 1: DIP swich side + * 2: Reset switch side + */ +#define PLD_IOLEDCR __reg16(PLD_BASE + 0x14002) +#define PLD_IOLED_1_ON 0x001 +#define PLD_IOLED_1_OFF 0x000 +#define PLD_IOLED_2_ON 0x002 +#define PLD_IOLED_2_OFF 0x000 + +/* DIP Switch + * 0: Write-protect of Flash Memory (0:protected, 1:non-protected) + * 1: - + * 2: - + * 3: - + */ +#define PLD_IOSWSTS __reg16(PLD_BASE + 0x14004) +#define PLD_IOSWSTS_IOSW2 0x0200 +#define PLD_IOSWSTS_IOSW1 0x0100 +#define PLD_IOSWSTS_IOWP0 0x0001 + +/* CRC */ +#define PLD_CRC7DATA __reg16(PLD_BASE + 0x18000) +#define PLD_CRC7INDATA __reg16(PLD_BASE + 0x18002) +#define PLD_CRC16DATA __reg16(PLD_BASE + 0x18004) +#define PLD_CRC16INDATA __reg16(PLD_BASE + 0x18006) +#define PLD_CRC16ADATA __reg16(PLD_BASE + 0x18008) +#define PLD_CRC16AINDATA __reg16(PLD_BASE + 0x1800a) + +/* RTC */ +#define PLD_RTCCR __reg16(PLD_BASE + 0x1c000) +#define PLD_RTCBAUR __reg16(PLD_BASE + 0x1c002) +#define PLD_RTCWRDATA __reg16(PLD_BASE + 0x1c004) +#define PLD_RTCRDDATA __reg16(PLD_BASE + 0x1c006) +#define PLD_RTCRSTODT __reg16(PLD_BASE + 0x1c008) + +/* SIO0 */ +#define PLD_ESIO0CR __reg16(PLD_BASE + 0x20000) +#define PLD_ESIO0CR_TXEN 0x0001 +#define PLD_ESIO0CR_RXEN 0x0002 +#define PLD_ESIO0MOD0 __reg16(PLD_BASE + 0x20002) +#define PLD_ESIO0MOD0_CTSS 0x0040 +#define PLD_ESIO0MOD0_RTSS 0x0080 +#define PLD_ESIO0MOD1 __reg16(PLD_BASE + 0x20004) +#define PLD_ESIO0MOD1_LMFS 0x0010 +#define PLD_ESIO0STS __reg16(PLD_BASE + 0x20006) +#define PLD_ESIO0STS_TEMP 0x0001 +#define PLD_ESIO0STS_TXCP 0x0002 +#define PLD_ESIO0STS_RXCP 0x0004 +#define PLD_ESIO0STS_TXSC 0x0100 +#define PLD_ESIO0STS_RXSC 0x0200 +#define PLD_ESIO0STS_TXREADY (PLD_ESIO0STS_TXCP | PLD_ESIO0STS_TEMP) +#define PLD_ESIO0INTCR __reg16(PLD_BASE + 0x20008) +#define PLD_ESIO0INTCR_TXIEN 0x0002 +#define PLD_ESIO0INTCR_RXCEN 0x0004 +#define PLD_ESIO0BAUR __reg16(PLD_BASE + 0x2000a) +#define PLD_ESIO0TXB __reg16(PLD_BASE + 0x2000c) +#define PLD_ESIO0RXB __reg16(PLD_BASE + 0x2000e) + +/* SIM Card */ +#define PLD_SCCR __reg16(PLD_BASE + 0x38000) +#define PLD_SCMOD __reg16(PLD_BASE + 0x38004) +#define PLD_SCSTS __reg16(PLD_BASE + 0x38006) +#define PLD_SCINTCR __reg16(PLD_BASE + 0x38008) +#define PLD_SCBAUR __reg16(PLD_BASE + 0x3800a) +#define PLD_SCTXB __reg16(PLD_BASE + 0x3800c) +#define PLD_SCRXB __reg16(PLD_BASE + 0x3800e) + +#endif /* _M32700UT_M32700UT_PLD.H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/m32r.h 2004-09-03 00:57:17.432389136 -0700 @@ -0,0 +1,135 @@ +#ifndef _ASM_M32R_M32R_H_ +#define _ASM_M32R_M32R_H_ + +/* + * Mitsubishi M32R processor + * Copyright (C) 1997-2002, Mitsubishi Electric Corporation + */ + +/* $Id$ */ + +#include + +/* Chip type */ +#if defined(CONFIG_CHIP_XNUX_MP) || defined(CONFIG_CHIP_XNUX2_MP) +#include +#elif defined(CONFIG_CHIP_VDEC2) || defined(CONFIG_CHIP_XNUX2) \ + || defined(CONFIG_CHIP_M32700) || defined(CONFIG_CHIP_M32102) \ + || defined(CONFIG_CHIP_OPSP) +#include +#include +#endif + +/* Platform type */ +#if defined(CONFIG_PLAT_M32700UT) +#include +#include +#include +#endif /* CONFIG_PLAT_M32700UT */ + +#if defined(CONFIG_PLAT_OPSPUT) +#include +#include +#include +#endif /* CONFIG_PLAT_OPSPUT */ + +#if defined(CONFIG_PLAT_MAPPI2) +#include +#endif /* CONFIG_PLAT_MAPPI2 */ + +#if defined(CONFIG_PLAT_USRV) +#include +#endif + +/* + * M32R Register + */ + +/* + * MMU Register + */ + +#define MMU_REG_BASE (0xffff0000) +#define ITLB_BASE (0xfe000000) +#define DTLB_BASE (0xfe000800) + +#define NR_TLB_ENTRIES CONFIG_TLB_ENTRIES + +#define MATM MMU_REG_BASE /* MMU Address Translation Mode + Register */ +#define MPSZ (0x04 + MMU_REG_BASE) /* MMU Page Size Designation Register */ +#define MASID (0x08 + MMU_REG_BASE) /* MMU Address Space ID Register */ +#define MESTS (0x0c + MMU_REG_BASE) /* MMU Exception Status Register */ +#define MDEVA (0x10 + MMU_REG_BASE) /* MMU Operand Exception Virtual + Address Register */ +#define MDEVP (0x14 + MMU_REG_BASE) /* MMU Operand Exception Virtual Page + Number Register */ +#define MPTB (0x18 + MMU_REG_BASE) /* MMU Page Table Base Register */ +#define MSVA (0x20 + MMU_REG_BASE) /* MMU Search Virtual Address + Register */ +#define MTOP (0x24 + MMU_REG_BASE) /* MMU TLB Operation Register */ +#define MIDXI (0x28 + MMU_REG_BASE) /* MMU Index Register for + Instruciton */ +#define MIDXD (0x2c + MMU_REG_BASE) /* MMU Index Register for Operand */ + +#define MATM_offset (MATM - MMU_REG_BASE) +#define MPSZ_offset (MPSZ - MMU_REG_BASE) +#define MASID_offset (MASID - MMU_REG_BASE) +#define MESTS_offset (MESTS - MMU_REG_BASE) +#define MDEVA_offset (MDEVA - MMU_REG_BASE) +#define MDEVP_offset (MDEVP - MMU_REG_BASE) +#define MPTB_offset (MPTB - MMU_REG_BASE) +#define MSVA_offset (MSVA - MMU_REG_BASE) +#define MTOP_offset (MTOP - MMU_REG_BASE) +#define MIDXI_offset (MIDXI - MMU_REG_BASE) +#define MIDXD_offset (MIDXD - MMU_REG_BASE) + +#define MESTS_IT (1 << 0) /* Instruction TLB miss */ +#define MESTS_IA (1 << 1) /* Instruction Access Exception */ +#define MESTS_DT (1 << 4) /* Operand TLB miss */ +#define MESTS_DA (1 << 5) /* Operand Access Exception */ +#define MESTS_DRW (1 << 6) /* Operand Write Exception Flag */ + +/* + * PSW (Processor Status Word) + */ + +/* PSW bit */ +#define M32R_PSW_BIT_SM (7) /* Stack Mode */ +#define M32R_PSW_BIT_IE (6) /* Interrupt Enable */ +#define M32R_PSW_BIT_PM (3) /* Processor Mode [0:Supervisor,1:User] */ +#define M32R_PSW_BIT_C (0) /* Condition */ +#define M32R_PSW_BIT_BSM (7+8) /* Backup Stack Mode */ +#define M32R_PSW_BIT_BIE (6+8) /* Backup Interrupt Enable */ +#define M32R_PSW_BIT_BPM (3+8) /* Backup Processor Mode */ +#define M32R_PSW_BIT_BC (0+8) /* Backup Condition */ + +/* PSW bit map */ +#define M32R_PSW_SM (1UL<< M32R_PSW_BIT_SM) /* Stack Mode */ +#define M32R_PSW_IE (1UL<< M32R_PSW_BIT_IE) /* Interrupt Enable */ +#define M32R_PSW_PM (1UL<< M32R_PSW_BIT_PM) /* Processor Mode */ +#define M32R_PSW_C (1UL<< M32R_PSW_BIT_C) /* Condition */ +#define M32R_PSW_BSM (1UL<< M32R_PSW_BIT_BSM) /* Backup Stack Mode */ +#define M32R_PSW_BIE (1UL<< M32R_PSW_BIT_BIE) /* Backup Interrupt Enable */ +#define M32R_PSW_BPM (1UL<< M32R_PSW_BIT_BPM) /* Backup Processor Mode */ +#define M32R_PSW_BC (1UL<< M32R_PSW_BIT_BC) /* Backup Condition */ + +/* + * Direct address to SFR + */ + +#include +#ifdef CONFIG_MMU +#define NONCACHE_OFFSET __PAGE_OFFSET+0x20000000 +#else +#define NONCACHE_OFFSET __PAGE_OFFSET +#endif /* CONFIG_MMU */ + +#define M32R_ICU_ISTS_ADDR M32R_ICU_ISTS_PORTL+NONCACHE_OFFSET +#define M32R_ICU_IPICR_ADDR M32R_ICU_IPICR0_PORTL+NONCACHE_OFFSET +#define M32R_ICU_IMASK_ADDR M32R_ICU_IMASK_PORTL+NONCACHE_OFFSET +#define M32R_FPGA_CPU_NAME_ADDR M32R_FPGA_CPU_NAME0_PORTL+NONCACHE_OFFSET +#define M32R_FPGA_MODEL_ID_ADDR M32R_FPGA_MODEL_ID0_PORTL+NONCACHE_OFFSET +#define M32R_FPGA_VERSION_ADDR M32R_FPGA_VERSION0_PORTL+NONCACHE_OFFSET + +#endif /* _ASM_M32R_M32R_H_ */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/m32r_mp_fpga.h 2004-09-03 00:57:17.434388832 -0700 @@ -0,0 +1,313 @@ +#ifndef _ASM_M32R_M32R_MP_FPGA_ +#define _ASM_M32R_M32R_MP_FPGA_ + +/* + * Mitsubishi M32R-MP-FPGA + * Copyright (c) 2002 [Hitoshi Yamamoto] All rights reserved. + */ + +/* $Id$ */ + +/* + * ======================================================== + * M32R-MP-FPGA Memory Map + * ======================================================== + * 0x00000000 : Block#0 : 64[MB] + * 0x03E00000 : SFR + * 0x03E00000 : reserved + * 0x03EF0000 : FPGA + * 0x03EF1000 : reserved + * 0x03EF4000 : CKM + * 0x03EF4000 : BSELC + * 0x03EF5000 : reserved + * 0x03EFC000 : MFT + * 0x03EFD000 : SIO + * 0x03EFE000 : reserved + * 0x03EFF000 : ICU + * 0x03F00000 : Internal SRAM 64[KB] + * 0x03F10000 : reserved + * -------------------------------------------------------- + * 0x04000000 : Block#1 : 64[MB] + * 0x04000000 : Debug board SRAM 4[MB] + * 0x04400000 : reserved + * -------------------------------------------------------- + * 0x08000000 : Block#2 : 64[MB] + * -------------------------------------------------------- + * 0x0C000000 : Block#3 : 64[MB] + * -------------------------------------------------------- + * 0x10000000 : Block#4 : 64[MB] + * -------------------------------------------------------- + * 0x14000000 : Block#5 : 64[MB] + * -------------------------------------------------------- + * 0x18000000 : Block#6 : 64[MB] + * -------------------------------------------------------- + * 0x1C000000 : Block#7 : 64[MB] + * -------------------------------------------------------- + * 0xFE000000 : TLB + * 0xFE000000 : ITLB + * 0xFE000080 : reserved + * 0xFE000800 : DTLB + * 0xFE000880 : reserved + * -------------------------------------------------------- + * 0xFF000000 : System area + * 0xFFFF0000 : MMU + * 0xFFFF0030 : reserved + * 0xFFFF8000 : Debug function + * 0xFFFFA000 : reserved + * 0xFFFFC000 : CPU control + * 0xFFFFFFFF + * ======================================================== + */ + +/*======================================================================* + * Special Function Register + *======================================================================*/ +#define M32R_SFR_OFFSET (0x00E00000) /* 0x03E00000-0x03EFFFFF 1[MB] */ + +/* + * FPGA registers. + */ +#define M32R_FPGA_TOP (0x000F0000+M32R_SFR_OFFSET) + +#define M32R_FPGA_NUM_OF_CPUS_PORTL (0x00+M32R_FPGA_TOP) +#define M32R_FPGA_CPU_NAME0_PORTL (0x10+M32R_FPGA_TOP) +#define M32R_FPGA_CPU_NAME1_PORTL (0x14+M32R_FPGA_TOP) +#define M32R_FPGA_CPU_NAME2_PORTL (0x18+M32R_FPGA_TOP) +#define M32R_FPGA_CPU_NAME3_PORTL (0x1C+M32R_FPGA_TOP) +#define M32R_FPGA_MODEL_ID0_PORTL (0x20+M32R_FPGA_TOP) +#define M32R_FPGA_MODEL_ID1_PORTL (0x24+M32R_FPGA_TOP) +#define M32R_FPGA_MODEL_ID2_PORTL (0x28+M32R_FPGA_TOP) +#define M32R_FPGA_MODEL_ID3_PORTL (0x2C+M32R_FPGA_TOP) +#define M32R_FPGA_VERSION0_PORTL (0x30+M32R_FPGA_TOP) +#define M32R_FPGA_VERSION1_PORTL (0x34+M32R_FPGA_TOP) + +/* + * Clock and Power Manager registers. + */ +#define M32R_CPM_OFFSET (0x000F4000+M32R_SFR_OFFSET) + +#define M32R_CPM_CPUCLKCR_PORTL (0x00+M32R_CPM_OFFSET) +#define M32R_CPM_CLKMOD_PORTL (0x04+M32R_CPM_OFFSET) +#define M32R_CPM_PLLCR_PORTL (0x08+M32R_CPM_OFFSET) + +/* + * Block SELect Controller registers. + */ +#define M32R_BSELC_OFFSET (0x000F5000+M32R_SFR_OFFSET) + +#define M32R_BSEL0_CR0_PORTL (0x000+M32R_BSELC_OFFSET) +#define M32R_BSEL0_CR1_PORTL (0x004+M32R_BSELC_OFFSET) +#define M32R_BSEL1_CR0_PORTL (0x100+M32R_BSELC_OFFSET) +#define M32R_BSEL1_CR1_PORTL (0x104+M32R_BSELC_OFFSET) +#define M32R_BSEL2_CR0_PORTL (0x200+M32R_BSELC_OFFSET) +#define M32R_BSEL2_CR1_PORTL (0x204+M32R_BSELC_OFFSET) +#define M32R_BSEL3_CR0_PORTL (0x300+M32R_BSELC_OFFSET) +#define M32R_BSEL3_CR1_PORTL (0x304+M32R_BSELC_OFFSET) +#define M32R_BSEL4_CR0_PORTL (0x400+M32R_BSELC_OFFSET) +#define M32R_BSEL4_CR1_PORTL (0x404+M32R_BSELC_OFFSET) +#define M32R_BSEL5_CR0_PORTL (0x500+M32R_BSELC_OFFSET) +#define M32R_BSEL5_CR1_PORTL (0x504+M32R_BSELC_OFFSET) +#define M32R_BSEL6_CR0_PORTL (0x600+M32R_BSELC_OFFSET) +#define M32R_BSEL6_CR1_PORTL (0x604+M32R_BSELC_OFFSET) +#define M32R_BSEL7_CR0_PORTL (0x700+M32R_BSELC_OFFSET) +#define M32R_BSEL7_CR1_PORTL (0x704+M32R_BSELC_OFFSET) + +/* + * Multi Function Timer registers. + */ +#define M32R_MFT_OFFSET (0x000FC000+M32R_SFR_OFFSET) + +#define M32R_MFTCR_PORTL (0x000+M32R_MFT_OFFSET) /* MFT control */ +#define M32R_MFTRPR_PORTL (0x004+M32R_MFT_OFFSET) /* MFT real port */ + +#define M32R_MFT0_OFFSET (0x100+M32R_MFT_OFFSET) +#define M32R_MFT0MOD_PORTL (0x00+M32R_MFT0_OFFSET) /* MFT0 mode */ +#define M32R_MFT0BOS_PORTL (0x04+M32R_MFT0_OFFSET) /* MFT0 b-port output status */ +#define M32R_MFT0CUT_PORTL (0x08+M32R_MFT0_OFFSET) /* MFT0 count */ +#define M32R_MFT0RLD_PORTL (0x0C+M32R_MFT0_OFFSET) /* MFT0 reload */ +#define M32R_MFT0CMPRLD_PORTL (0x10+M32R_MFT0_OFFSET) /* MFT0 compare reload */ + +#define M32R_MFT1_OFFSET (0x200+M32R_MFT_OFFSET) +#define M32R_MFT1MOD_PORTL (0x00+M32R_MFT1_OFFSET) /* MFT1 mode */ +#define M32R_MFT1BOS_PORTL (0x04+M32R_MFT1_OFFSET) /* MFT1 b-port output status */ +#define M32R_MFT1CUT_PORTL (0x08+M32R_MFT1_OFFSET) /* MFT1 count */ +#define M32R_MFT1RLD_PORTL (0x0C+M32R_MFT1_OFFSET) /* MFT1 reload */ +#define M32R_MFT1CMPRLD_PORTL (0x10+M32R_MFT1_OFFSET) /* MFT1 compare reload */ + +#define M32R_MFT2_OFFSET (0x300+M32R_MFT_OFFSET) +#define M32R_MFT2MOD_PORTL (0x00+M32R_MFT2_OFFSET) /* MFT2 mode */ +#define M32R_MFT2BOS_PORTL (0x04+M32R_MFT2_OFFSET) /* MFT2 b-port output status */ +#define M32R_MFT2CUT_PORTL (0x08+M32R_MFT2_OFFSET) /* MFT2 count */ +#define M32R_MFT2RLD_PORTL (0x0C+M32R_MFT2_OFFSET) /* MFT2 reload */ +#define M32R_MFT2CMPRLD_PORTL (0x10+M32R_MFT2_OFFSET) /* MFT2 compare reload */ + +#define M32R_MFT3_OFFSET (0x400+M32R_MFT_OFFSET) +#define M32R_MFT3MOD_PORTL (0x00+M32R_MFT3_OFFSET) /* MFT3 mode */ +#define M32R_MFT3BOS_PORTL (0x04+M32R_MFT3_OFFSET) /* MFT3 b-port output status */ +#define M32R_MFT3CUT_PORTL (0x08+M32R_MFT3_OFFSET) /* MFT3 count */ +#define M32R_MFT3RLD_PORTL (0x0C+M32R_MFT3_OFFSET) /* MFT3 reload */ +#define M32R_MFT3CMPRLD_PORTL (0x10+M32R_MFT3_OFFSET) /* MFT3 compare reload */ + +#define M32R_MFT4_OFFSET (0x500+M32R_MFT_OFFSET) +#define M32R_MFT4MOD_PORTL (0x00+M32R_MFT4_OFFSET) /* MFT4 mode */ +#define M32R_MFT4BOS_PORTL (0x04+M32R_MFT4_OFFSET) /* MFT4 b-port output status */ +#define M32R_MFT4CUT_PORTL (0x08+M32R_MFT4_OFFSET) /* MFT4 count */ +#define M32R_MFT4RLD_PORTL (0x0C+M32R_MFT4_OFFSET) /* MFT4 reload */ +#define M32R_MFT4CMPRLD_PORTL (0x10+M32R_MFT4_OFFSET) /* MFT4 compare reload */ + +#define M32R_MFT5_OFFSET (0x600+M32R_MFT_OFFSET) +#define M32R_MFT5MOD_PORTL (0x00+M32R_MFT5_OFFSET) /* MFT4 mode */ +#define M32R_MFT5BOS_PORTL (0x04+M32R_MFT5_OFFSET) /* MFT4 b-port output status */ +#define M32R_MFT5CUT_PORTL (0x08+M32R_MFT5_OFFSET) /* MFT4 count */ +#define M32R_MFT5RLD_PORTL (0x0C+M32R_MFT5_OFFSET) /* MFT4 reload */ +#define M32R_MFT5CMPRLD_PORTL (0x10+M32R_MFT5_OFFSET) /* MFT4 compare reload */ + +#define M32R_MFTCR_MFT0MSK (1UL<<15) /* b16 */ +#define M32R_MFTCR_MFT1MSK (1UL<<14) /* b17 */ +#define M32R_MFTCR_MFT2MSK (1UL<<13) /* b18 */ +#define M32R_MFTCR_MFT3MSK (1UL<<12) /* b19 */ +#define M32R_MFTCR_MFT4MSK (1UL<<11) /* b20 */ +#define M32R_MFTCR_MFT5MSK (1UL<<10) /* b21 */ +#define M32R_MFTCR_MFT0EN (1UL<<7) /* b24 */ +#define M32R_MFTCR_MFT1EN (1UL<<6) /* b25 */ +#define M32R_MFTCR_MFT2EN (1UL<<5) /* b26 */ +#define M32R_MFTCR_MFT3EN (1UL<<4) /* b27 */ +#define M32R_MFTCR_MFT4EN (1UL<<3) /* b28 */ +#define M32R_MFTCR_MFT5EN (1UL<<2) /* b29 */ + +#define M32R_MFTMOD_CC_MASK (1UL<<15) /* b16 */ +#define M32R_MFTMOD_TCCR (1UL<<13) /* b18 */ +#define M32R_MFTMOD_GTSEL000 (0UL<<8) /* b21-23 : 000 */ +#define M32R_MFTMOD_GTSEL001 (1UL<<8) /* b21-23 : 001 */ +#define M32R_MFTMOD_GTSEL010 (2UL<<8) /* b21-23 : 010 */ +#define M32R_MFTMOD_GTSEL011 (3UL<<8) /* b21-23 : 011 */ +#define M32R_MFTMOD_GTSEL110 (6UL<<8) /* b21-23 : 110 */ +#define M32R_MFTMOD_GTSEL111 (7UL<<8) /* b21-23 : 111 */ +#define M32R_MFTMOD_CMSEL (1UL<<3) /* b28 */ +#define M32R_MFTMOD_CSSEL000 (0UL<<0) /* b29-b31 : 000 */ +#define M32R_MFTMOD_CSSEL001 (1UL<<0) /* b29-b31 : 001 */ +#define M32R_MFTMOD_CSSEL010 (2UL<<0) /* b29-b31 : 010 */ +#define M32R_MFTMOD_CSSEL011 (3UL<<0) /* b29-b31 : 011 */ +#define M32R_MFTMOD_CSSEL100 (4UL<<0) /* b29-b31 : 100 */ +#define M32R_MFTMOD_CSSEL110 (6UL<<0) /* b29-b31 : 110 */ + +/* + * Serial I/O registers. + */ +#define M32R_SIO_OFFSET (0x000FD000+M32R_SFR_OFFSET) + +#define M32R_SIO0_CR_PORTL (0x000+M32R_SIO_OFFSET) +#define M32R_SIO0_MOD0_PORTL (0x004+M32R_SIO_OFFSET) +#define M32R_SIO0_MOD1_PORTL (0x008+M32R_SIO_OFFSET) +#define M32R_SIO0_STS_PORTL (0x00C+M32R_SIO_OFFSET) +#define M32R_SIO0_TRCR_PORTL (0x010+M32R_SIO_OFFSET) +#define M32R_SIO0_BAUR_PORTL (0x014+M32R_SIO_OFFSET) +#define M32R_SIO0_RBAUR_PORTL (0x018+M32R_SIO_OFFSET) +#define M32R_SIO0_TXB_PORTL (0x01C+M32R_SIO_OFFSET) +#define M32R_SIO0_RXB_PORTL (0x020+M32R_SIO_OFFSET) + +/* + * Interrupt Control Unit registers. + */ +#define M32R_ICU_OFFSET (0x000FF000+M32R_SFR_OFFSET) + +#define M32R_ICU_ISTS_PORTL (0x004+M32R_ICU_OFFSET) +#define M32R_ICU_IREQ0_PORTL (0x008+M32R_ICU_OFFSET) +#define M32R_ICU_IREQ1_PORTL (0x00C+M32R_ICU_OFFSET) +#define M32R_ICU_SBICR_PORTL (0x018+M32R_ICU_OFFSET) +#define M32R_ICU_IMASK_PORTL (0x01C+M32R_ICU_OFFSET) +#define M32R_ICU_CR1_PORTL (0x200+M32R_ICU_OFFSET) /* INT0 */ +#define M32R_ICU_CR2_PORTL (0x204+M32R_ICU_OFFSET) /* INT1 */ +#define M32R_ICU_CR3_PORTL (0x208+M32R_ICU_OFFSET) /* INT2 */ +#define M32R_ICU_CR4_PORTL (0x20C+M32R_ICU_OFFSET) /* INT3 */ +#define M32R_ICU_CR5_PORTL (0x210+M32R_ICU_OFFSET) /* INT4 */ +#define M32R_ICU_CR6_PORTL (0x214+M32R_ICU_OFFSET) /* INT5 */ +#define M32R_ICU_CR7_PORTL (0x218+M32R_ICU_OFFSET) /* INT6 */ +#define M32R_ICU_CR8_PORTL (0x218+M32R_ICU_OFFSET) /* INT7 */ +#define M32R_ICU_CR32_PORTL (0x27C+M32R_ICU_OFFSET) /* SIO0 RX */ +#define M32R_ICU_CR33_PORTL (0x280+M32R_ICU_OFFSET) /* SIO0 TX */ +#define M32R_ICU_CR40_PORTL (0x29C+M32R_ICU_OFFSET) /* DMAC0 */ +#define M32R_ICU_CR41_PORTL (0x2A0+M32R_ICU_OFFSET) /* DMAC1 */ +#define M32R_ICU_CR48_PORTL (0x2BC+M32R_ICU_OFFSET) /* MFT0 */ +#define M32R_ICU_CR49_PORTL (0x2C0+M32R_ICU_OFFSET) /* MFT1 */ +#define M32R_ICU_CR50_PORTL (0x2C4+M32R_ICU_OFFSET) /* MFT2 */ +#define M32R_ICU_CR51_PORTL (0x2C8+M32R_ICU_OFFSET) /* MFT3 */ +#define M32R_ICU_CR52_PORTL (0x2CC+M32R_ICU_OFFSET) /* MFT4 */ +#define M32R_ICU_CR53_PORTL (0x2D0+M32R_ICU_OFFSET) /* MFT5 */ +#define M32R_ICU_IPICR0_PORTL (0x2DC+M32R_ICU_OFFSET) /* IPI0 */ +#define M32R_ICU_IPICR1_PORTL (0x2E0+M32R_ICU_OFFSET) /* IPI1 */ +#define M32R_ICU_IPICR2_PORTL (0x2E4+M32R_ICU_OFFSET) /* IPI2 */ +#define M32R_ICU_IPICR3_PORTL (0x2E8+M32R_ICU_OFFSET) /* IPI3 */ +#define M32R_ICU_IPICR4_PORTL (0x2EC+M32R_ICU_OFFSET) /* IPI4 */ +#define M32R_ICU_IPICR5_PORTL (0x2F0+M32R_ICU_OFFSET) /* IPI5 */ +#define M32R_ICU_IPICR6_PORTL (0x2F4+M32R_ICU_OFFSET) /* IPI6 */ +#define M32R_ICU_IPICR7_PORTL (0x2FC+M32R_ICU_OFFSET) /* IPI7 */ + +#define M32R_ICUISTS_VECB(val) ((val>>28) & 0xF) +#define M32R_ICUISTS_ISN(val) ((val>>22) & 0x3F) +#define M32R_ICUISTS_PIML(val) ((val>>16) & 0x7) + +#define M32R_ICUIMASK_IMSK0 (0UL<<16) /* b13-b15: Disable interrupt */ +#define M32R_ICUIMASK_IMSK1 (1UL<<16) /* b13-b15: Enable level 0 interrupt */ +#define M32R_ICUIMASK_IMSK2 (2UL<<16) /* b13-b15: Enable level 0,1 interrupt */ +#define M32R_ICUIMASK_IMSK3 (3UL<<16) /* b13-b15: Enable level 0-2 interrupt */ +#define M32R_ICUIMASK_IMSK4 (4UL<<16) /* b13-b15: Enable level 0-3 interrupt */ +#define M32R_ICUIMASK_IMSK5 (5UL<<16) /* b13-b15: Enable level 0-4 interrupt */ +#define M32R_ICUIMASK_IMSK6 (6UL<<16) /* b13-b15: Enable level 0-5 interrupt */ +#define M32R_ICUIMASK_IMSK7 (7UL<<16) /* b13-b15: Enable level 0-6 interrupt */ + +#define M32R_ICUCR_IEN (1UL<<12) /* b19: Interrupt enable */ +#define M32R_ICUCR_IRQ (1UL<<8) /* b23: Interrupt request */ +#define M32R_ICUCR_ISMOD00 (0UL<<4) /* b26-b27: Interrupt sense mode Edge HtoL */ +#define M32R_ICUCR_ISMOD01 (1UL<<4) /* b26-b27: Interrupt sense mode Level L */ +#define M32R_ICUCR_ISMOD10 (2UL<<4) /* b26-b27: Interrupt sense mode Edge LtoH*/ +#define M32R_ICUCR_ISMOD11 (3UL<<4) /* b26-b27: Interrupt sense mode Level H */ +#define M32R_ICUCR_ILEVEL0 (0UL<<0) /* b29-b31: Interrupt priority level 0 */ +#define M32R_ICUCR_ILEVEL1 (1UL<<0) /* b29-b31: Interrupt priority level 1 */ +#define M32R_ICUCR_ILEVEL2 (2UL<<0) /* b29-b31: Interrupt priority level 2 */ +#define M32R_ICUCR_ILEVEL3 (3UL<<0) /* b29-b31: Interrupt priority level 3 */ +#define M32R_ICUCR_ILEVEL4 (4UL<<0) /* b29-b31: Interrupt priority level 4 */ +#define M32R_ICUCR_ILEVEL5 (5UL<<0) /* b29-b31: Interrupt priority level 5 */ +#define M32R_ICUCR_ILEVEL6 (6UL<<0) /* b29-b31: Interrupt priority level 6 */ +#define M32R_ICUCR_ILEVEL7 (7UL<<0) /* b29-b31: Disable interrupt */ +#define M32R_ICUCR_ILEVEL_MASK (7UL) + +#define M32R_IRQ_INT0 (1) /* INT0 */ +#define M32R_IRQ_INT1 (2) /* INT1 */ +#define M32R_IRQ_INT2 (3) /* INT2 */ +#define M32R_IRQ_INT3 (4) /* INT3 */ +#define M32R_IRQ_INT4 (5) /* INT4 */ +#define M32R_IRQ_INT5 (6) /* INT5 */ +#define M32R_IRQ_INT6 (7) /* INT6 */ +#define M32R_IRQ_INT7 (8) /* INT7 */ +#define M32R_IRQ_MFT0 (16) /* MFT0 */ +#define M32R_IRQ_MFT1 (17) /* MFT1 */ +#define M32R_IRQ_MFT2 (18) /* MFT2 */ +#define M32R_IRQ_MFT3 (19) /* MFT3 */ +#define M32R_IRQ_MFT4 (20) /* MFT4 */ +#define M32R_IRQ_MFT5 (21) /* MFT5 */ +#define M32R_IRQ_DMAC0 (32) /* DMAC0 */ +#define M32R_IRQ_DMAC1 (33) /* DMAC1 */ +#define M32R_IRQ_SIO0_R (48) /* SIO0 receive */ +#define M32R_IRQ_SIO0_S (49) /* SIO0 send */ +#define M32R_IRQ_SIO1_R (50) /* SIO1 send */ +#define M32R_IRQ_SIO1_S (51) /* SIO1 receive */ +#define M32R_IRQ_IPI0 (56) /* IPI0 */ +#define M32R_IRQ_IPI1 (57) /* IPI1 */ +#define M32R_IRQ_IPI2 (58) /* IPI2 */ +#define M32R_IRQ_IPI3 (59) /* IPI3 */ +#define M32R_IRQ_IPI4 (60) /* IPI4 */ +#define M32R_IRQ_IPI5 (61) /* IPI5 */ +#define M32R_IRQ_IPI6 (62) /* IPI6 */ +#define M32R_IRQ_IPI7 (63) /* IPI7 */ + +/*======================================================================* + * CPU + *======================================================================*/ + +#define M32R_CPUID_PORTL (0xFFFFFFE0) +#define M32R_MCICAR_PORTL (0xFFFFFFF0) +#define M32R_MCDCAR_PORTL (0xFFFFFFF4) +#define M32R_MCCR_PORTL (0xFFFFFFFC) + +#endif /* _ASM_M32R_M32R_MP_FPGA_ */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/mappi2/mappi2_pld.h 2004-09-03 00:57:17.435388680 -0700 @@ -0,0 +1,151 @@ +/* + * include/asm/mappi2/mappi2_pld.h + * + * Definitions for Extended IO Logic on MAPPI2 board. + * based on m32700ut_pld.h by + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of + * this archive for more details. + * + */ + +#ifndef _MAPPI2_PLD_H +#define _MAPPI2_PLD_H + +#ifndef __ASSEMBLY__ +/* FIXME: + * Some C functions use non-cache address, so can't define non-cache address. + */ +#define PLD_BASE (0x10c00000 /* + NONCACHE_OFFSET */) +#define __reg8 (volatile unsigned char *) +#define __reg16 (volatile unsigned short *) +#define __reg32 (volatile unsigned int *) +#else +#define PLD_BASE (0x10c00000 + NONCACHE_OFFSET) +#define __reg8 +#define __reg16 +#define __reg32 +#endif /* __ASSEMBLY__ */ + +/* CFC */ +#define PLD_CFRSTCR __reg16(PLD_BASE + 0x0000) +#define PLD_CFSTS __reg16(PLD_BASE + 0x0002) +#define PLD_CFIMASK __reg16(PLD_BASE + 0x0004) +#define PLD_CFBUFCR __reg16(PLD_BASE + 0x0006) +#define PLD_CFCR0 __reg16(PLD_BASE + 0x000a) +#define PLD_CFCR1 __reg16(PLD_BASE + 0x000c) + +/* MMC */ +#define PLD_MMCCR __reg16(PLD_BASE + 0x4000) +#define PLD_MMCMOD __reg16(PLD_BASE + 0x4002) +#define PLD_MMCSTS __reg16(PLD_BASE + 0x4006) +#define PLD_MMCBAUR __reg16(PLD_BASE + 0x400a) +#define PLD_MMCCMDBCUT __reg16(PLD_BASE + 0x400c) +#define PLD_MMCCDTBCUT __reg16(PLD_BASE + 0x400e) +#define PLD_MMCDET __reg16(PLD_BASE + 0x4010) +#define PLD_MMCWP __reg16(PLD_BASE + 0x4012) +#define PLD_MMCWDATA __reg16(PLD_BASE + 0x5000) +#define PLD_MMCRDATA __reg16(PLD_BASE + 0x6000) +#define PLD_MMCCMDDATA __reg16(PLD_BASE + 0x7000) +#define PLD_MMCRSPDATA __reg16(PLD_BASE + 0x7006) + +/* Power Control of MMC and CF */ +#define PLD_CPCR __reg16(PLD_BASE + 0x14000) + + +/*==== ICU ====*/ +#define M32R_IRQ_PC104 (5) /* INT4(PC/104) */ +#define M32R_IRQ_I2C (28) /* I2C-BUS */ +#if 1 +#define PLD_IRQ_CFIREQ (40) /* CFC Card Interrupt */ +#define PLD_IRQ_CFC_INSERT (41) /* CFC Card Insert */ +#define PLD_IRQ_CFC_EJECT (42) /* CFC Card Eject */ +#define PLD_IRQ_MMCCARD (43) /* MMC Card Insert */ +#define PLD_IRQ_MMCIRQ (44) /* MMC Transfer Done */ +#else +#define PLD_IRQ_CFIREQ (34) /* CFC Card Interrupt */ +#define PLD_IRQ_CFC_INSERT (35) /* CFC Card Insert */ +#define PLD_IRQ_CFC_EJECT (36) /* CFC Card Eject */ +#define PLD_IRQ_MMCCARD (37) /* MMC Card Insert */ +#define PLD_IRQ_MMCIRQ (38) /* MMC Transfer Done */ +#endif + + +#if 0 +/* LED Control + * + * 1: DIP swich side + * 2: Reset switch side + */ +#define PLD_IOLEDCR __reg16(PLD_BASE + 0x14002) +#define PLD_IOLED_1_ON 0x001 +#define PLD_IOLED_1_OFF 0x000 +#define PLD_IOLED_2_ON 0x002 +#define PLD_IOLED_2_OFF 0x000 + +/* DIP Switch + * 0: Write-protect of Flash Memory (0:protected, 1:non-protected) + * 1: - + * 2: - + * 3: - + */ +#define PLD_IOSWSTS __reg16(PLD_BASE + 0x14004) +#define PLD_IOSWSTS_IOSW2 0x0200 +#define PLD_IOSWSTS_IOSW1 0x0100 +#define PLD_IOSWSTS_IOWP0 0x0001 + +#endif + +/* CRC */ +#define PLD_CRC7DATA __reg16(PLD_BASE + 0x18000) +#define PLD_CRC7INDATA __reg16(PLD_BASE + 0x18002) +#define PLD_CRC16DATA __reg16(PLD_BASE + 0x18004) +#define PLD_CRC16INDATA __reg16(PLD_BASE + 0x18006) +#define PLD_CRC16ADATA __reg16(PLD_BASE + 0x18008) +#define PLD_CRC16AINDATA __reg16(PLD_BASE + 0x1800a) + + +#if 0 +/* RTC */ +#define PLD_RTCCR __reg16(PLD_BASE + 0x1c000) +#define PLD_RTCBAUR __reg16(PLD_BASE + 0x1c002) +#define PLD_RTCWRDATA __reg16(PLD_BASE + 0x1c004) +#define PLD_RTCRDDATA __reg16(PLD_BASE + 0x1c006) +#define PLD_RTCRSTODT __reg16(PLD_BASE + 0x1c008) + +/* SIO0 */ +#define PLD_ESIO0CR __reg16(PLD_BASE + 0x20000) +#define PLD_ESIO0CR_TXEN 0x0001 +#define PLD_ESIO0CR_RXEN 0x0002 +#define PLD_ESIO0MOD0 __reg16(PLD_BASE + 0x20002) +#define PLD_ESIO0MOD0_CTSS 0x0040 +#define PLD_ESIO0MOD0_RTSS 0x0080 +#define PLD_ESIO0MOD1 __reg16(PLD_BASE + 0x20004) +#define PLD_ESIO0MOD1_LMFS 0x0010 +#define PLD_ESIO0STS __reg16(PLD_BASE + 0x20006) +#define PLD_ESIO0STS_TEMP 0x0001 +#define PLD_ESIO0STS_TXCP 0x0002 +#define PLD_ESIO0STS_RXCP 0x0004 +#define PLD_ESIO0STS_TXSC 0x0100 +#define PLD_ESIO0STS_RXSC 0x0200 +#define PLD_ESIO0STS_TXREADY (PLD_ESIO0STS_TXCP | PLD_ESIO0STS_TEMP) +#define PLD_ESIO0INTCR __reg16(PLD_BASE + 0x20008) +#define PLD_ESIO0INTCR_TXIEN 0x0002 +#define PLD_ESIO0INTCR_RXCEN 0x0004 +#define PLD_ESIO0BAUR __reg16(PLD_BASE + 0x2000a) +#define PLD_ESIO0TXB __reg16(PLD_BASE + 0x2000c) +#define PLD_ESIO0RXB __reg16(PLD_BASE + 0x2000e) + +/* SIM Card */ +#define PLD_SCCR __reg16(PLD_BASE + 0x38000) +#define PLD_SCMOD __reg16(PLD_BASE + 0x38004) +#define PLD_SCSTS __reg16(PLD_BASE + 0x38006) +#define PLD_SCINTCR __reg16(PLD_BASE + 0x38008) +#define PLD_SCBAUR __reg16(PLD_BASE + 0x3800a) +#define PLD_SCTXB __reg16(PLD_BASE + 0x3800c) +#define PLD_SCRXB __reg16(PLD_BASE + 0x3800e) + +#endif + +#endif /* _MAPPI2_PLD.H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/mc146818rtc.h 2004-09-03 00:57:17.436388528 -0700 @@ -0,0 +1,32 @@ +/* + * Machine dependent access functions for RTC registers. + */ +#ifndef _ASM_MC146818RTC_H +#define _ASM_MC146818RTC_H + +#include + +#ifndef RTC_PORT +// #define RTC_PORT(x) (0x70 + (x)) +#define RTC_PORT(x) ((x)) +#define RTC_ALWAYS_BCD 1 /* RTC operates in binary mode */ +#endif + +/* + * The yet supported machines all access the RTC index register via + * an ISA port access but the way to access the date register differs ... + */ +#define CMOS_READ(addr) ({ \ +outb_p((addr),RTC_PORT(0)); \ +inb_p(RTC_PORT(1)); \ +}) +#define CMOS_WRITE(val, addr) ({ \ +outb_p((addr),RTC_PORT(0)); \ +outb_p((val),RTC_PORT(1)); \ +}) + +#define RTC_IRQ 8 +#if 0 +#endif + +#endif /* _ASM_MC146818RTC_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/mman.h 2004-09-03 00:57:17.436388528 -0700 @@ -0,0 +1,45 @@ +#ifndef __M32R_MMAN_H__ +#define __M32R_MMAN_H__ + +/* orig : i386 2.6.0-test6 */ + +#define PROT_READ 0x1 /* page can be read */ +#define PROT_WRITE 0x2 /* page can be written */ +#define PROT_EXEC 0x4 /* page can be executed */ +#define PROT_SEM 0x8 /* page may be used for atomic ops */ +#define PROT_NONE 0x0 /* page can not be accessed */ +#define PROT_GROWSDOWN 0x01000000 /* mprotect flag: extend change to start of growsdown vma */ +#define PROT_GROWSUP 0x02000000 /* mprotect flag: extend change to end of growsup vma */ + +#define MAP_SHARED 0x01 /* Share changes */ +#define MAP_PRIVATE 0x02 /* Changes are private */ +#define MAP_TYPE 0x0f /* Mask for type of mapping */ +#define MAP_FIXED 0x10 /* Interpret addr exactly */ +#define MAP_ANONYMOUS 0x20 /* don't use a file */ + +#define MAP_GROWSDOWN 0x0100 /* stack-like segment */ +#define MAP_DENYWRITE 0x0800 /* ETXTBSY */ +#define MAP_EXECUTABLE 0x1000 /* mark it as an executable */ +#define MAP_LOCKED 0x2000 /* pages are locked */ +#define MAP_NORESERVE 0x4000 /* don't check for reservations */ +#define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */ +#define MAP_NONBLOCK 0x10000 /* do not block on IO */ + +#define MS_ASYNC 1 /* sync memory asynchronously */ +#define MS_INVALIDATE 2 /* invalidate the caches */ +#define MS_SYNC 4 /* synchronous memory sync */ + +#define MCL_CURRENT 1 /* lock all current mappings */ +#define MCL_FUTURE 2 /* lock all future mappings */ + +#define MADV_NORMAL 0x0 /* default page-in behavior */ +#define MADV_RANDOM 0x1 /* page-in minimum required */ +#define MADV_SEQUENTIAL 0x2 /* read-ahead aggressively */ +#define MADV_WILLNEED 0x3 /* pre-fault pages */ +#define MADV_DONTNEED 0x4 /* discard these pages */ + +/* compatibility flags */ +#define MAP_ANON MAP_ANONYMOUS +#define MAP_FILE 0 + +#endif /* __M32R_MMAN_H__ */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/mmu_context.h 2004-09-03 00:57:17.437388376 -0700 @@ -0,0 +1,169 @@ +#ifndef _ASM_M32R_MMU_CONTEXT_H +#define _ASM_M32R_MMU_CONTEXT_H + +/* $Id */ + +#include + +#include + +#define MMU_CONTEXT_ASID_MASK (0x000000FF) +#define MMU_CONTEXT_VERSION_MASK (0xFFFFFF00) +#define MMU_CONTEXT_FIRST_VERSION (0x00000100) +#define NO_CONTEXT (0x00000000) + + +#ifndef __ASSEMBLY__ + +#include +#include +#include +#include +#include + +/* + * Cache of MMU context last used. + */ +#ifndef CONFIG_SMP +extern unsigned long mmu_context_cache_dat; +#define mmu_context_cache mmu_context_cache_dat +#define mm_context(mm) mm->context +#else /* not CONFIG_SMP */ +extern unsigned long mmu_context_cache_dat[]; +#define mmu_context_cache mmu_context_cache_dat[smp_processor_id()] +#define mm_context(mm) mm->context[smp_processor_id()] +#endif /* not CONFIG_SMP */ + +#define set_tlb_tag(entry, tag) (*entry = (tag & PAGE_MASK)|get_asid()) +#define set_tlb_data(entry, data) (*entry = (data | _PAGE_PRESENT)) + +#ifdef CONFIG_MMU +#define enter_lazy_tlb(mm, tsk) do { } while (0) + +static __inline__ void get_new_mmu_context(struct mm_struct *mm) +{ + unsigned long mc = ++mmu_context_cache; + + if (!(mc & MMU_CONTEXT_ASID_MASK)) { + /* We exhaust ASID of this version. + Flush all TLB and start new cycle. */ + local_flush_tlb_all(); + /* Fix version if needed. + Note that we avoid version #0 to distingush NO_CONTEXT. */ + if (!mc) + mmu_context_cache = mc = MMU_CONTEXT_FIRST_VERSION; + } + mm_context(mm) = mc; +} + +/* + * Get MMU context if needed. + */ +static __inline__ void get_mmu_context(struct mm_struct *mm) +{ + if (mm) { + unsigned long mc = mmu_context_cache; + + /* Check if we have old version of context. + If it's old, we need to get new context with new version. */ + if ((mm_context(mm) ^ mc) & MMU_CONTEXT_VERSION_MASK) + get_new_mmu_context(mm); + } +} + +/* + * Initialize the context related info for a new mm_struct + * instance. + */ +static __inline__ int init_new_context(struct task_struct *tsk, + struct mm_struct *mm) +{ +#ifndef CONFIG_SMP + mm->context = NO_CONTEXT; +#else /* CONFIG_SMP */ + int num_cpus = num_online_cpus(); + int i; + + for (i = 0 ; i < num_cpus ; i++) + mm->context[i] = NO_CONTEXT; +#endif /* CONFIG_SMP */ + + return 0; +} + +/* + * Destroy context related info for an mm_struct that is about + * to be put to rest. + */ +#define destroy_context(mm) do { } while (0) + +static __inline__ void set_asid(unsigned long asid) +{ + *(volatile unsigned long *)MASID = (asid & MMU_CONTEXT_ASID_MASK); +} + +static __inline__ unsigned long get_asid(void) +{ + unsigned long asid; + + asid = *(volatile long *)MASID; + asid &= MMU_CONTEXT_ASID_MASK; + + return asid; +} + +/* + * After we have set current->mm to a new value, this activates + * the context for the new mm so we see the new mappings. + */ +static __inline__ void activate_context(struct mm_struct *mm) +{ + get_mmu_context(mm); + set_asid(mm_context(mm) & MMU_CONTEXT_ASID_MASK); +} + +static __inline__ void switch_mm(struct mm_struct *prev, + struct mm_struct *next, struct task_struct *tsk) +{ +#ifdef CONFIG_SMP + int cpu = smp_processor_id(); +#endif /* CONFIG_SMP */ + + if (prev != next) { +#ifdef CONFIG_SMP + cpu_set(cpu, next->cpu_vm_mask); +#endif /* CONFIG_SMP */ + /* Set MPTB = next->pgd */ + *(volatile unsigned long *)MPTB = (unsigned long)next->pgd; + activate_context(next); + } +#ifdef CONFIG_SMP + else + if (!cpu_test_and_set(cpu, next->cpu_vm_mask)) + activate_context(next); +#endif /* CONFIG_SMP */ +} + +#define deactivate_mm(tsk, mm) do { } while (0) + +#define activate_mm(prev, next) \ + switch_mm((prev), (next), NULL) + +#else +#define get_mmu_context(mm) do { } while (0) +#define init_new_context(tsk,mm) (0) +#define destroy_context(mm) do { } while (0) +#define set_asid(asid) do { } while (0) +#define get_asid() (0) +#define activate_context(mm) do { } while (0) +#define switch_mm(prev,next,tsk) do { } while (0) +#define deactivate_mm(mm,tsk) do { } while (0) +#define activate_mm(prev,next) do { } while (0) +#define enter_lazy_tlb(mm,tsk) do { } while (0) +#endif /* CONFIG_MMU */ + + +#endif /* not __ASSEMBLY__ */ + +#endif /* _ASM_M32R_MMU_CONTEXT_H */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/mmu.h 2004-09-03 00:57:17.437388376 -0700 @@ -0,0 +1,35 @@ +#ifndef _ASM_M32R_MMU_H +#define _ASM_M32R_MMU_H + +/* $Id$ */ + +#include + +#if !defined(CONFIG_MMU) +struct mm_rblock_struct { + int size; + int refcount; + void *kblock; +}; + +struct mm_tblock_struct { + struct mm_rblock_struct *rblock; + struct mm_tblock_struct *next; +}; + +typedef struct { + struct mm_tblock_struct tblock; + unsigned long end_brk; +} mm_context_t; +#else + +/* Default "unsigned long" context */ +#ifndef CONFIG_SMP +typedef unsigned long mm_context_t; +#else +typedef unsigned long mm_context_t[NR_CPUS]; +#endif + +#endif /* CONFIG_MMU */ +#endif /* _ASM_M32R_MMU_H */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/mmzone.h 2004-09-03 00:57:17.438388224 -0700 @@ -0,0 +1,80 @@ +/* + * Written by Pat Gaughen (gone@us.ibm.com) Mar 2002 + * + */ + +#ifndef _ASM_MMZONE_H_ +#define _ASM_MMZONE_H_ + +#include + +#ifdef CONFIG_DISCONTIGMEM + +extern struct pglist_data *node_data[]; +#define NODE_DATA(nid) (node_data[nid]) + +#define node_localnr(pfn, nid) ((pfn) - NODE_DATA(nid)->node_start_pfn) +#define node_mem_map(nid) (NODE_DATA(nid)->node_mem_map) +#define node_start_pfn(nid) (NODE_DATA(nid)->node_start_pfn) +#define node_end_pfn(nid) \ +({ \ + pg_data_t *__pgdat = NODE_DATA(nid); \ + __pgdat->node_start_pfn + __pgdat->node_spanned_pages - 1; \ +}) + +#define local_mapnr(kvaddr) \ +({ \ + unsigned long __pfn = __pa(kvaddr) >> PAGE_SHIFT; \ + (__pfn - node_start_pfn(pfn_to_nid(__pfn))); \ +}) + +#define pfn_to_page(pfn) \ +({ \ + unsigned long __pfn = pfn; \ + int __node = pfn_to_nid(__pfn); \ + &node_mem_map(__node)[node_localnr(__pfn,__node)]; \ +}) + +#define page_to_pfn(pg) \ +({ \ + struct page *__page = pg; \ + struct zone *__zone = page_zone(__page); \ + (unsigned long)(__page - __zone->zone_mem_map) \ + + __zone->zone_start_pfn; \ +}) +#define pmd_page(pmd) (pfn_to_page(pmd_val(pmd) >> PAGE_SHIFT)) +/* + * pfn_valid should be made as fast as possible, and the current definition + * is valid for machines that are NUMA, but still contiguous, which is what + * is currently supported. A more generalised, but slower definition would + * be something like this - mbligh: + * ( pfn_to_pgdat(pfn) && ((pfn) < node_end_pfn(pfn_to_nid(pfn))) ) + */ +#if 1 /* M32R_FIXME */ +#define pfn_valid(pfn) (1) +#else +#define pfn_valid(pfn) ((pfn) < num_physpages) +#endif + +/* + * generic node memory support, the following assumptions apply: + */ + +static __inline__ int pfn_to_nid(unsigned long pfn) +{ + int node; + + for (node = 0 ; node < MAX_NUMNODES ; node++) + if (pfn >= node_start_pfn(node) && pfn <= node_end_pfn(node)) + break; + + return node; +} + +static __inline__ struct pglist_data *pfn_to_pgdat(unsigned long pfn) +{ + return(NODE_DATA(pfn_to_nid(pfn))); +} + +#endif /* CONFIG_DISCONTIGMEM */ +#endif /* _ASM_MMZONE_H_ */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/module.h 2004-09-03 00:57:17.438388224 -0700 @@ -0,0 +1,13 @@ +#ifndef _ASM_M32R_MODULE_H +#define _ASM_M32R_MODULE_H + +/* $Id$ */ + +struct mod_arch_specific { }; + +#define Elf_Shdr Elf32_Shdr +#define Elf_Sym Elf32_Sym +#define Elf_Ehdr Elf32_Ehdr + +#endif /* _ASM_M32R_MODULE_H */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/msgbuf.h 2004-09-03 00:57:17.438388224 -0700 @@ -0,0 +1,35 @@ +#ifndef _ASM_M32R_MSGBUF_H +#define _ASM_M32R_MSGBUF_H + +/* $Id$ */ + +/* orig : i386 2.4.18 */ + +/* + * The msqid64_ds structure for m32r architecture. + * Note extra padding because this structure is passed back and forth + * between kernel and user space. + * + * Pad space is left for: + * - 64-bit time_t to solve y2038 problem + * - 2 miscellaneous 32-bit values + */ + +struct msqid64_ds { + struct ipc64_perm msg_perm; + __kernel_time_t msg_stime; /* last msgsnd time */ + unsigned long __unused1; + __kernel_time_t msg_rtime; /* last msgrcv time */ + unsigned long __unused2; + __kernel_time_t msg_ctime; /* last change time */ + unsigned long __unused3; + unsigned long msg_cbytes; /* current number of bytes on queue */ + unsigned long msg_qnum; /* number of messages in queue */ + unsigned long msg_qbytes; /* max number of bytes on queue */ + __kernel_pid_t msg_lspid; /* pid of last msgsnd */ + __kernel_pid_t msg_lrpid; /* last receive pid */ + unsigned long __unused4; + unsigned long __unused5; +}; + +#endif /* _ASM_M32R_MSGBUF_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/namei.h 2004-09-03 00:57:17.439388072 -0700 @@ -0,0 +1,21 @@ +#ifndef _ASM_M32R_NAMEI_H +#define _ASM_M32R_NAMEI_H + +/* $Id$ */ + +/* orig : i386 2.4.18 */ + +/* + * linux/include/asm-m32r/namei.h + * + * Included from linux/fs/namei.c + */ + +/* This dummy routine maybe changed to something useful + * for /usr/gnemul/ emulation stuff. + * Look at asm-sparc/namei.h for details. + */ + +#define __emul_prefix() NULL + +#endif /* _ASM_M32R_NAMEI_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/numnodes.h 2004-09-03 00:57:17.439388072 -0700 @@ -0,0 +1,15 @@ +#ifndef _ASM_NUMNODES_H_ +#define _ASM_NUMNODES_H_ + +#include + +#ifdef CONFIG_DISCONTIGMEM + +#if defined(CONFIG_CHIP_M32700) +#define NODES_SHIFT 1 /* Max 2 Nodes */ +#endif /* CONFIG_CHIP_M32700 */ + +#endif /* CONFIG_DISCONTIGMEM */ + +#endif /* _ASM_NUMNODES_H_ */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/opsput/opsput_lan.h 2004-09-03 00:57:17.439388072 -0700 @@ -0,0 +1,56 @@ +/* + * include/asm/opsput_lan.h + * + * OPSPUT-LAN board + * + * Copyright (c) 2002-2004 Takeo Takahashi, Mamoru Sakugawa + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of + * this archive for more details. + * + * $Id: opsput_lan.h,v 1.1 2004/07/27 06:54:20 sakugawa Exp $ + */ + +#ifndef _OPSPUT_OPSPUT_LAN_H +#define _OPSPUT_OPSPUT_LAN_H + +#include + +#ifndef __ASSEMBLY__ +/* + * C functions use non-cache address. + */ +#define OPSPUT_LAN_BASE (0x10000000 /* + NONCACHE_OFFSET */) +#else +#define OPSPUT_LAN_BASE (0x10000000 + NONCACHE_OFFSET) +#endif /* __ASSEMBLY__ */ + +/* ICU + * ICUISTS: status register + * ICUIREQ0: request register + * ICUIREQ1: request register + * ICUCR3: control register for CFIREQ# interrupt + * ICUCR4: control register for CFC Card insert interrupt + * ICUCR5: control register for CFC Card eject interrupt + * ICUCR6: control register for external interrupt + * ICUCR11: control register for MMC Card insert/eject interrupt + * ICUCR13: control register for SC error interrupt + * ICUCR14: control register for SC receive interrupt + * ICUCR15: control register for SC send interrupt + * ICUCR16: control register for SIO0 receive interrupt + * ICUCR17: control register for SIO0 send interrupt + */ +#define OPSPUT_LAN_IRQ_LAN (OPSPUT_LAN_PLD_IRQ_BASE + 1) /* LAN */ +#define OPSPUT_LAN_IRQ_I2C (OPSPUT_LAN_PLD_IRQ_BASE + 3) /* I2C */ + +#define OPSPUT_LAN_ICUISTS __reg16(OPSPUT_LAN_BASE + 0xc0002) +#define OPSPUT_LAN_ICUISTS_VECB_MASK (0xf000) +#define OPSPUT_LAN_VECB(x) ((x) & OPSPUT_LAN_ICUISTS_VECB_MASK) +#define OPSPUT_LAN_ICUISTS_ISN_MASK (0x07c0) +#define OPSPUT_LAN_ICUISTS_ISN(x) ((x) & OPSPUT_LAN_ICUISTS_ISN_MASK) +#define OPSPUT_LAN_ICUIREQ0 __reg16(OPSPUT_LAN_BASE + 0xc0004) +#define OPSPUT_LAN_ICUCR1 __reg16(OPSPUT_LAN_BASE + 0xc0010) +#define OPSPUT_LAN_ICUCR3 __reg16(OPSPUT_LAN_BASE + 0xc0014) + +#endif /* _OPSPUT_OPSPUT_LAN_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/opsput/opsput_lcd.h 2004-09-03 00:57:17.440387920 -0700 @@ -0,0 +1,59 @@ +/* + * include/asm/opsput_lcd.h + * + * OPSPUT-LCD board + * + * Copyright (c) 2002 Takeo Takahashi + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of + * this archive for more details. + * + * $Id: opsput_lcd.h,v 1.1 2004/07/27 06:54:20 sakugawa Exp $ + */ + +#ifndef _OPSPUT_OPSPUT_LCD_H +#define _OPSPUT_OPSPUT_LCD_H + +#include + +#ifndef __ASSEMBLY__ +/* + * C functions use non-cache address. + */ +#define OPSPUT_LCD_BASE (0x10000000 /* + NONCACHE_OFFSET */) +#else +#define OPSPUT_LCD_BASE (0x10000000 + NONCACHE_OFFSET) +#endif /* __ASSEMBLY__ */ + +/* + * ICU + */ +#define OPSPUT_LCD_IRQ_BAT_INT (OPSPUT_LCD_PLD_IRQ_BASE + 1) +#define OPSPUT_LCD_IRQ_USB_INT1 (OPSPUT_LCD_PLD_IRQ_BASE + 2) +#define OPSPUT_LCD_IRQ_AUDT0 (OPSPUT_LCD_PLD_IRQ_BASE + 3) +#define OPSPUT_LCD_IRQ_AUDT2 (OPSPUT_LCD_PLD_IRQ_BASE + 4) +#define OPSPUT_LCD_IRQ_BATSIO_RCV (OPSPUT_LCD_PLD_IRQ_BASE + 16) +#define OPSPUT_LCD_IRQ_BATSIO_SND (OPSPUT_LCD_PLD_IRQ_BASE + 17) +#define OPSPUT_LCD_IRQ_ASNDSIO_RCV (OPSPUT_LCD_PLD_IRQ_BASE + 18) +#define OPSPUT_LCD_IRQ_ASNDSIO_SND (OPSPUT_LCD_PLD_IRQ_BASE + 19) +#define OPSPUT_LCD_IRQ_ACNLSIO_SND (OPSPUT_LCD_PLD_IRQ_BASE + 21) + +#define OPSPUT_LCD_ICUISTS __reg16(OPSPUT_LCD_BASE + 0x300002) +#define OPSPUT_LCD_ICUISTS_VECB_MASK (0xf000) +#define OPSPUT_LCD_VECB(x) ((x) & OPSPUT_LCD_ICUISTS_VECB_MASK) +#define OPSPUT_LCD_ICUISTS_ISN_MASK (0x07c0) +#define OPSPUT_LCD_ICUISTS_ISN(x) ((x) & OPSPUT_LCD_ICUISTS_ISN_MASK) +#define OPSPUT_LCD_ICUIREQ0 __reg16(OPSPUT_LCD_BASE + 0x300004) +#define OPSPUT_LCD_ICUIREQ1 __reg16(OPSPUT_LCD_BASE + 0x300006) +#define OPSPUT_LCD_ICUCR1 __reg16(OPSPUT_LCD_BASE + 0x300020) +#define OPSPUT_LCD_ICUCR2 __reg16(OPSPUT_LCD_BASE + 0x300022) +#define OPSPUT_LCD_ICUCR3 __reg16(OPSPUT_LCD_BASE + 0x300024) +#define OPSPUT_LCD_ICUCR4 __reg16(OPSPUT_LCD_BASE + 0x300026) +#define OPSPUT_LCD_ICUCR16 __reg16(OPSPUT_LCD_BASE + 0x300030) +#define OPSPUT_LCD_ICUCR17 __reg16(OPSPUT_LCD_BASE + 0x300032) +#define OPSPUT_LCD_ICUCR18 __reg16(OPSPUT_LCD_BASE + 0x300034) +#define OPSPUT_LCD_ICUCR19 __reg16(OPSPUT_LCD_BASE + 0x300036) +#define OPSPUT_LCD_ICUCR21 __reg16(OPSPUT_LCD_BASE + 0x30003a) + +#endif /* _OPSPUT_OPSPUT_LCD_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/opsput/opsput_pld.h 2004-09-03 00:57:17.442387616 -0700 @@ -0,0 +1,259 @@ +/* + * include/asm/opsput/opsput_pld.h + * + * Definitions for Programable Logic Device(PLD) on OPSPUT board. + * + * Copyright (c) 2002 Takeo Takahashi + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of + * this archive for more details. + * + * $Id: opsput_pld.h,v 1.1 2004/07/27 06:54:20 sakugawa Exp $ + */ + +#ifndef _OPSPUT_OPSPUT_PLD_H +#define _OPSPUT_OPSPUT_PLD_H + +#include + +#define PLD_PLAT_BASE 0x1cc00000 + +#ifndef __ASSEMBLY__ +/* + * C functions use non-cache address. + */ +#define PLD_BASE (PLD_PLAT_BASE /* + NONCACHE_OFFSET */) +#define __reg8 (volatile unsigned char *) +#define __reg16 (volatile unsigned short *) +#define __reg32 (volatile unsigned int *) +#else +#define PLD_BASE (PLD_PLAT_BASE + NONCACHE_OFFSET) +#define __reg8 +#define __reg16 +#define __reg32 +#endif /* __ASSEMBLY__ */ + +/* CFC */ +#define PLD_CFRSTCR __reg16(PLD_BASE + 0x0000) +#define PLD_CFSTS __reg16(PLD_BASE + 0x0002) +#define PLD_CFIMASK __reg16(PLD_BASE + 0x0004) +#define PLD_CFBUFCR __reg16(PLD_BASE + 0x0006) +#define PLD_CFVENCR __reg16(PLD_BASE + 0x0008) +#define PLD_CFCR0 __reg16(PLD_BASE + 0x000a) +#define PLD_CFCR1 __reg16(PLD_BASE + 0x000c) +#define PLD_IDERSTCR __reg16(PLD_BASE + 0x0010) + +/* MMC */ +#define PLD_MMCCR __reg16(PLD_BASE + 0x4000) +#define PLD_MMCMOD __reg16(PLD_BASE + 0x4002) +#define PLD_MMCSTS __reg16(PLD_BASE + 0x4006) +#define PLD_MMCBAUR __reg16(PLD_BASE + 0x400a) +#define PLD_MMCCMDBCUT __reg16(PLD_BASE + 0x400c) +#define PLD_MMCCDTBCUT __reg16(PLD_BASE + 0x400e) +#define PLD_MMCDET __reg16(PLD_BASE + 0x4010) +#define PLD_MMCWP __reg16(PLD_BASE + 0x4012) +#define PLD_MMCWDATA __reg16(PLD_BASE + 0x5000) +#define PLD_MMCRDATA __reg16(PLD_BASE + 0x6000) +#define PLD_MMCCMDDATA __reg16(PLD_BASE + 0x7000) +#define PLD_MMCRSPDATA __reg16(PLD_BASE + 0x7006) + +/* ICU + * ICUISTS: status register + * ICUIREQ0: request register + * ICUIREQ1: request register + * ICUCR3: control register for CFIREQ# interrupt + * ICUCR4: control register for CFC Card insert interrupt + * ICUCR5: control register for CFC Card eject interrupt + * ICUCR6: control register for external interrupt + * ICUCR11: control register for MMC Card insert/eject interrupt + * ICUCR13: control register for SC error interrupt + * ICUCR14: control register for SC receive interrupt + * ICUCR15: control register for SC send interrupt + * ICUCR16: control register for SIO0 receive interrupt + * ICUCR17: control register for SIO0 send interrupt + */ +#if !defined(CONFIG_PLAT_USRV) +#define PLD_IRQ_INT0 (OPSPUT_PLD_IRQ_BASE + 0) /* None */ +#define PLD_IRQ_INT1 (OPSPUT_PLD_IRQ_BASE + 1) /* reserved */ +#define PLD_IRQ_INT2 (OPSPUT_PLD_IRQ_BASE + 2) /* reserved */ +#define PLD_IRQ_CFIREQ (OPSPUT_PLD_IRQ_BASE + 3) /* CF IREQ */ +#define PLD_IRQ_CFC_INSERT (OPSPUT_PLD_IRQ_BASE + 4) /* CF Insert */ +#define PLD_IRQ_CFC_EJECT (OPSPUT_PLD_IRQ_BASE + 5) /* CF Eject */ +#define PLD_IRQ_EXINT (OPSPUT_PLD_IRQ_BASE + 6) /* EXINT */ +#define PLD_IRQ_INT7 (OPSPUT_PLD_IRQ_BASE + 7) /* reserved */ +#define PLD_IRQ_INT8 (OPSPUT_PLD_IRQ_BASE + 8) /* reserved */ +#define PLD_IRQ_INT9 (OPSPUT_PLD_IRQ_BASE + 9) /* reserved */ +#define PLD_IRQ_INT10 (OPSPUT_PLD_IRQ_BASE + 10) /* reserved */ +#define PLD_IRQ_MMCCARD (OPSPUT_PLD_IRQ_BASE + 11) /* MMC Insert/Eject */ +#define PLD_IRQ_INT12 (OPSPUT_PLD_IRQ_BASE + 12) /* reserved */ +#define PLD_IRQ_SC_ERROR (OPSPUT_PLD_IRQ_BASE + 13) /* SC error */ +#define PLD_IRQ_SC_RCV (OPSPUT_PLD_IRQ_BASE + 14) /* SC receive */ +#define PLD_IRQ_SC_SND (OPSPUT_PLD_IRQ_BASE + 15) /* SC send */ +#define PLD_IRQ_SIO0_RCV (OPSPUT_PLD_IRQ_BASE + 16) /* SIO receive */ +#define PLD_IRQ_SIO0_SND (OPSPUT_PLD_IRQ_BASE + 17) /* SIO send */ +#define PLD_IRQ_INT18 (OPSPUT_PLD_IRQ_BASE + 18) /* reserved */ +#define PLD_IRQ_INT19 (OPSPUT_PLD_IRQ_BASE + 19) /* reserved */ +#define PLD_IRQ_INT20 (OPSPUT_PLD_IRQ_BASE + 20) /* reserved */ +#define PLD_IRQ_INT21 (OPSPUT_PLD_IRQ_BASE + 21) /* reserved */ +#define PLD_IRQ_INT22 (OPSPUT_PLD_IRQ_BASE + 22) /* reserved */ +#define PLD_IRQ_INT23 (OPSPUT_PLD_IRQ_BASE + 23) /* reserved */ +#define PLD_IRQ_INT24 (OPSPUT_PLD_IRQ_BASE + 24) /* reserved */ +#define PLD_IRQ_INT25 (OPSPUT_PLD_IRQ_BASE + 25) /* reserved */ +#define PLD_IRQ_INT26 (OPSPUT_PLD_IRQ_BASE + 26) /* reserved */ +#define PLD_IRQ_INT27 (OPSPUT_PLD_IRQ_BASE + 27) /* reserved */ +#define PLD_IRQ_INT28 (OPSPUT_PLD_IRQ_BASE + 28) /* reserved */ +#define PLD_IRQ_INT29 (OPSPUT_PLD_IRQ_BASE + 29) /* reserved */ +#define PLD_IRQ_INT30 (OPSPUT_PLD_IRQ_BASE + 30) /* reserved */ +#define PLD_IRQ_INT31 (OPSPUT_PLD_IRQ_BASE + 31) /* reserved */ + +#else /* CONFIG_PLAT_USRV */ + +#define PLD_IRQ_INT0 (OPSPUT_PLD_IRQ_BASE + 0) /* None */ +#define PLD_IRQ_INT1 (OPSPUT_PLD_IRQ_BASE + 1) /* reserved */ +#define PLD_IRQ_INT2 (OPSPUT_PLD_IRQ_BASE + 2) /* reserved */ +#define PLD_IRQ_CF0 (OPSPUT_PLD_IRQ_BASE + 3) /* CF0# */ +#define PLD_IRQ_CF1 (OPSPUT_PLD_IRQ_BASE + 4) /* CF1# */ +#define PLD_IRQ_CF2 (OPSPUT_PLD_IRQ_BASE + 5) /* CF2# */ +#define PLD_IRQ_CF3 (OPSPUT_PLD_IRQ_BASE + 6) /* CF3# */ +#define PLD_IRQ_CF4 (OPSPUT_PLD_IRQ_BASE + 7) /* CF4# */ +#define PLD_IRQ_INT8 (OPSPUT_PLD_IRQ_BASE + 8) /* reserved */ +#define PLD_IRQ_INT9 (OPSPUT_PLD_IRQ_BASE + 9) /* reserved */ +#define PLD_IRQ_INT10 (OPSPUT_PLD_IRQ_BASE + 10) /* reserved */ +#define PLD_IRQ_INT11 (OPSPUT_PLD_IRQ_BASE + 11) /* reserved */ +#define PLD_IRQ_UART0 (OPSPUT_PLD_IRQ_BASE + 12) /* UARTIRQ0 */ +#define PLD_IRQ_UART1 (OPSPUT_PLD_IRQ_BASE + 13) /* UARTIRQ1 */ +#define PLD_IRQ_INT14 (OPSPUT_PLD_IRQ_BASE + 14) /* reserved */ +#define PLD_IRQ_INT15 (OPSPUT_PLD_IRQ_BASE + 15) /* reserved */ +#define PLD_IRQ_SNDINT (OPSPUT_PLD_IRQ_BASE + 16) /* SNDINT# */ +#define PLD_IRQ_INT17 (OPSPUT_PLD_IRQ_BASE + 17) /* reserved */ +#define PLD_IRQ_INT18 (OPSPUT_PLD_IRQ_BASE + 18) /* reserved */ +#define PLD_IRQ_INT19 (OPSPUT_PLD_IRQ_BASE + 19) /* reserved */ +#define PLD_IRQ_INT20 (OPSPUT_PLD_IRQ_BASE + 20) /* reserved */ +#define PLD_IRQ_INT21 (OPSPUT_PLD_IRQ_BASE + 21) /* reserved */ +#define PLD_IRQ_INT22 (OPSPUT_PLD_IRQ_BASE + 22) /* reserved */ +#define PLD_IRQ_INT23 (OPSPUT_PLD_IRQ_BASE + 23) /* reserved */ +#define PLD_IRQ_INT24 (OPSPUT_PLD_IRQ_BASE + 24) /* reserved */ +#define PLD_IRQ_INT25 (OPSPUT_PLD_IRQ_BASE + 25) /* reserved */ +#define PLD_IRQ_INT26 (OPSPUT_PLD_IRQ_BASE + 26) /* reserved */ +#define PLD_IRQ_INT27 (OPSPUT_PLD_IRQ_BASE + 27) /* reserved */ +#define PLD_IRQ_INT28 (OPSPUT_PLD_IRQ_BASE + 28) /* reserved */ +#define PLD_IRQ_INT29 (OPSPUT_PLD_IRQ_BASE + 29) /* reserved */ +#define PLD_IRQ_INT30 (OPSPUT_PLD_IRQ_BASE + 30) /* reserved */ + +#endif /* CONFIG_PLAT_USRV */ + +#define PLD_ICUISTS __reg16(PLD_BASE + 0x8002) +#define PLD_ICUISTS_VECB_MASK (0xf000) +#define PLD_ICUISTS_VECB(x) ((x) & PLD_ICUISTS_VECB_MASK) +#define PLD_ICUISTS_ISN_MASK (0x07c0) +#define PLD_ICUISTS_ISN(x) ((x) & PLD_ICUISTS_ISN_MASK) +#define PLD_ICUIREQ0 __reg16(PLD_BASE + 0x8004) +#define PLD_ICUIREQ1 __reg16(PLD_BASE + 0x8006) +#define PLD_ICUCR1 __reg16(PLD_BASE + 0x8100) +#define PLD_ICUCR2 __reg16(PLD_BASE + 0x8102) +#define PLD_ICUCR3 __reg16(PLD_BASE + 0x8104) +#define PLD_ICUCR4 __reg16(PLD_BASE + 0x8106) +#define PLD_ICUCR5 __reg16(PLD_BASE + 0x8108) +#define PLD_ICUCR6 __reg16(PLD_BASE + 0x810a) +#define PLD_ICUCR7 __reg16(PLD_BASE + 0x810c) +#define PLD_ICUCR8 __reg16(PLD_BASE + 0x810e) +#define PLD_ICUCR9 __reg16(PLD_BASE + 0x8110) +#define PLD_ICUCR10 __reg16(PLD_BASE + 0x8112) +#define PLD_ICUCR11 __reg16(PLD_BASE + 0x8114) +#define PLD_ICUCR12 __reg16(PLD_BASE + 0x8116) +#define PLD_ICUCR13 __reg16(PLD_BASE + 0x8118) +#define PLD_ICUCR14 __reg16(PLD_BASE + 0x811a) +#define PLD_ICUCR15 __reg16(PLD_BASE + 0x811c) +#define PLD_ICUCR16 __reg16(PLD_BASE + 0x811e) +#define PLD_ICUCR17 __reg16(PLD_BASE + 0x8120) +#define PLD_ICUCR_IEN (0x1000) +#define PLD_ICUCR_IREQ (0x0100) +#define PLD_ICUCR_ISMOD00 (0x0000) /* Low edge */ +#define PLD_ICUCR_ISMOD01 (0x0010) /* Low level */ +#define PLD_ICUCR_ISMOD02 (0x0020) /* High edge */ +#define PLD_ICUCR_ISMOD03 (0x0030) /* High level */ +#define PLD_ICUCR_ILEVEL0 (0x0000) +#define PLD_ICUCR_ILEVEL1 (0x0001) +#define PLD_ICUCR_ILEVEL2 (0x0002) +#define PLD_ICUCR_ILEVEL3 (0x0003) +#define PLD_ICUCR_ILEVEL4 (0x0004) +#define PLD_ICUCR_ILEVEL5 (0x0005) +#define PLD_ICUCR_ILEVEL6 (0x0006) +#define PLD_ICUCR_ILEVEL7 (0x0007) + +/* Power Control of MMC and CF */ +#define PLD_CPCR __reg16(PLD_BASE + 0x14000) +#define PLD_CPCR_CF 0x0001 +#define PLD_CPCR_MMC 0x0002 + +/* LED Control + * + * 1: DIP swich side + * 2: Reset switch side + */ +#define PLD_IOLEDCR __reg16(PLD_BASE + 0x14002) +#define PLD_IOLED_1_ON 0x001 +#define PLD_IOLED_1_OFF 0x000 +#define PLD_IOLED_2_ON 0x002 +#define PLD_IOLED_2_OFF 0x000 + +/* DIP Switch + * 0: Write-protect of Flash Memory (0:protected, 1:non-protected) + * 1: - + * 2: - + * 3: - + */ +#define PLD_IOSWSTS __reg16(PLD_BASE + 0x14004) +#define PLD_IOSWSTS_IOSW2 0x0200 +#define PLD_IOSWSTS_IOSW1 0x0100 +#define PLD_IOSWSTS_IOWP0 0x0001 + +/* CRC */ +#define PLD_CRC7DATA __reg16(PLD_BASE + 0x18000) +#define PLD_CRC7INDATA __reg16(PLD_BASE + 0x18002) +#define PLD_CRC16DATA __reg16(PLD_BASE + 0x18004) +#define PLD_CRC16INDATA __reg16(PLD_BASE + 0x18006) +#define PLD_CRC16ADATA __reg16(PLD_BASE + 0x18008) +#define PLD_CRC16AINDATA __reg16(PLD_BASE + 0x1800a) + +/* RTC */ +#define PLD_RTCCR __reg16(PLD_BASE + 0x1c000) +#define PLD_RTCBAUR __reg16(PLD_BASE + 0x1c002) +#define PLD_RTCWRDATA __reg16(PLD_BASE + 0x1c004) +#define PLD_RTCRDDATA __reg16(PLD_BASE + 0x1c006) +#define PLD_RTCRSTODT __reg16(PLD_BASE + 0x1c008) + +/* SIO0 */ +#define PLD_ESIO0CR __reg16(PLD_BASE + 0x20000) +#define PLD_ESIO0CR_TXEN 0x0001 +#define PLD_ESIO0CR_RXEN 0x0002 +#define PLD_ESIO0MOD0 __reg16(PLD_BASE + 0x20002) +#define PLD_ESIO0MOD0_CTSS 0x0040 +#define PLD_ESIO0MOD0_RTSS 0x0080 +#define PLD_ESIO0MOD1 __reg16(PLD_BASE + 0x20004) +#define PLD_ESIO0MOD1_LMFS 0x0010 +#define PLD_ESIO0STS __reg16(PLD_BASE + 0x20006) +#define PLD_ESIO0STS_TEMP 0x0001 +#define PLD_ESIO0STS_TXCP 0x0002 +#define PLD_ESIO0STS_RXCP 0x0004 +#define PLD_ESIO0STS_TXSC 0x0100 +#define PLD_ESIO0STS_RXSC 0x0200 +#define PLD_ESIO0STS_TXREADY (PLD_ESIO0STS_TXCP | PLD_ESIO0STS_TEMP) +#define PLD_ESIO0INTCR __reg16(PLD_BASE + 0x20008) +#define PLD_ESIO0INTCR_TXIEN 0x0002 +#define PLD_ESIO0INTCR_RXCEN 0x0004 +#define PLD_ESIO0BAUR __reg16(PLD_BASE + 0x2000a) +#define PLD_ESIO0TXB __reg16(PLD_BASE + 0x2000c) +#define PLD_ESIO0RXB __reg16(PLD_BASE + 0x2000e) + +/* SIM Card */ +#define PLD_SCCR __reg16(PLD_BASE + 0x38000) +#define PLD_SCMOD __reg16(PLD_BASE + 0x38004) +#define PLD_SCSTS __reg16(PLD_BASE + 0x38006) +#define PLD_SCINTCR __reg16(PLD_BASE + 0x38008) +#define PLD_SCBAUR __reg16(PLD_BASE + 0x3800a) +#define PLD_SCTXB __reg16(PLD_BASE + 0x3800c) +#define PLD_SCRXB __reg16(PLD_BASE + 0x3800e) + +#endif /* _OPSPUT_OPSPUT_PLD.H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/page.h 2004-09-03 00:57:17.442387616 -0700 @@ -0,0 +1,112 @@ +#ifndef _ASM_M32R_PAGE_H +#define _ASM_M32R_PAGE_H + +/* $Id$ */ + +#include + +/* PAGE_SHIFT determines the page size */ +#define PAGE_SHIFT 12 +#define PAGE_SIZE (1UL << PAGE_SHIFT) +#define PAGE_MASK (~(PAGE_SIZE-1)) + +#ifdef __KERNEL__ +#ifndef __ASSEMBLY__ + +extern void clear_page(void *to); +extern void copy_page(void *to, void *from); + +#define clear_user_page(page, vaddr, pg) clear_page(page) +#define copy_user_page(to, from, vaddr, pg) copy_page(to, from) + +/* + * These are used to make use of C type-checking.. + */ +typedef struct { unsigned long pte; } pte_t; +typedef struct { unsigned long pmd; } pmd_t; +typedef struct { unsigned long pgd; } pgd_t; +#define pte_val(x) ((x).pte) +#define PTE_MASK PAGE_MASK + +typedef struct { unsigned long pgprot; } pgprot_t; + +#define pmd_val(x) ((x).pmd) +#define pgd_val(x) ((x).pgd) +#define pgprot_val(x) ((x).pgprot) + +#define __pte(x) ((pte_t) { (x) } ) +#define __pmd(x) ((pmd_t) { (x) } ) +#define __pgd(x) ((pgd_t) { (x) } ) +#define __pgprot(x) ((pgprot_t) { (x) } ) + +#endif /* !__ASSEMBLY__ */ + +/* to align the pointer to the (next) page boundary */ +#define PAGE_ALIGN(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK) + +/* + * This handles the memory map.. We could make this a config + * option, but too many people screw it up, and too few need + * it. + * + * A __PAGE_OFFSET of 0xC0000000 means that the kernel has + * a virtual address space of one gigabyte, which limits the + * amount of physical memory you can use to about 950MB. + * + * If you want more physical memory than this then see the CONFIG_HIGHMEM4G + * and CONFIG_HIGHMEM64G options in the kernel configuration. + */ + + +/* This handles the memory map.. */ + +#ifndef __ASSEMBLY__ + +/* Pure 2^n version of get_order */ +static __inline__ int get_order(unsigned long size) +{ + int order; + + size = (size - 1) >> (PAGE_SHIFT - 1); + order = -1; + do { + size >>= 1; + order++; + } while (size); + + return order; +} + +#endif /* __ASSEMBLY__ */ + +#define __MEMORY_START CONFIG_MEMORY_START +#define __MEMORY_SIZE CONFIG_MEMORY_SIZE + +#ifdef CONFIG_MMU +#define __PAGE_OFFSET (0x80000000) +#else +#define __PAGE_OFFSET (0x00000000) +#endif + +#define PAGE_OFFSET ((unsigned long)__PAGE_OFFSET) +#define __pa(x) ((unsigned long)(x) - PAGE_OFFSET) +#define __va(x) ((void *)((unsigned long)(x) + PAGE_OFFSET)) + +#ifndef CONFIG_DISCONTIGMEM +#define PFN_BASE (CONFIG_MEMORY_START >> PAGE_SHIFT) +#define pfn_to_page(pfn) (mem_map + ((pfn) - PFN_BASE)) +#define page_to_pfn(page) \ + ((unsigned long)((page) - mem_map) + PFN_BASE) +#define pfn_valid(pfn) (((pfn) - PFN_BASE) < max_mapnr) +#endif /* !CONFIG_DISCONTIGMEM */ + +#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) +#define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT) + +#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ + VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC ) + +#endif /* __KERNEL__ */ + +#endif /* _ASM_M32R_PAGE_H */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/param.h 2004-09-03 00:57:17.443387464 -0700 @@ -0,0 +1,27 @@ +#ifndef _ASM_M32R_PARAM_H +#define _ASM_M32R_PARAM_H + +/* $Id$ */ + +/* orig : i386 2.5.67 */ + +#ifdef __KERNEL__ +# define HZ 100 /* Internal kernel timer frequency */ +# define USER_HZ 100 /* .. some user interfaces are in "ticks" */ +# define CLOCKS_PER_SEC (USER_HZ) /* like times() */ +#endif + +#ifndef HZ +#define HZ 100 +#endif + +#define EXEC_PAGESIZE 4096 + +#ifndef NOGROUP +#define NOGROUP (-1) +#endif + +#define MAXHOSTNAMELEN 64 /* max length of hostname */ + +#endif /* _ASM_M32R_PARAM_H */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/pci.h 2004-09-03 00:57:17.443387464 -0700 @@ -0,0 +1,10 @@ +#ifndef _ASM_M32R_PCI_H +#define _ASM_M32R_PCI_H + +/* $Id$ */ + +#include + +#define PCI_DMA_BUS_IS_PHYS (1) + +#endif /* _ASM_M32R_PCI_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/percpu.h 2004-09-03 00:57:17.443387464 -0700 @@ -0,0 +1,6 @@ +#ifndef __ARCH_M32R_PERCPU__ +#define __ARCH_M32R_PERCPU__ + +#include + +#endif /* __ARCH_M32R_PERCPU__ */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/pgalloc.h 2004-09-03 00:57:17.443387464 -0700 @@ -0,0 +1,87 @@ +#ifndef _ASM_M32R_PGALLOC_H +#define _ASM_M32R_PGALLOC_H + +/* $Id$ */ + +#include +#include + +#include +#include + +#define pmd_populate_kernel(mm, pmd, pte) \ + set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(pte))) + +static __inline__ void pmd_populate(struct mm_struct *mm, pmd_t *pmd, + struct page *pte) +{ + set_pmd(pmd, __pmd(_PAGE_TABLE + page_to_phys(pte))); +} + +/* + * Allocate and free page tables. + */ +static __inline__ pgd_t *pgd_alloc(struct mm_struct *mm) +{ + pgd_t *pgd = (pgd_t *)__get_free_page(GFP_KERNEL); + + if (pgd) + clear_page(pgd); + + return pgd; +} + +static __inline__ void pgd_free(pgd_t *pgd) +{ + free_page((unsigned long)pgd); +} + +static __inline__ pte_t *pte_alloc_one_kernel(struct mm_struct *mm, + unsigned long address) +{ + pte_t *pte = (pte_t *)__get_free_page(GFP_KERNEL); + + if (pte) + clear_page(pte); + + return pte; +} + +static __inline__ struct page *pte_alloc_one(struct mm_struct *mm, + unsigned long address) +{ + struct page *pte = alloc_page(GFP_KERNEL); + + if (pte) + clear_page(page_address(pte)); + + return pte; +} + +static __inline__ void pte_free_kernel(pte_t *pte) +{ + free_page((unsigned long)pte); +} + +static __inline__ void pte_free(struct page *pte) +{ + __free_page(pte); +} + +#define __pte_free_tlb(tlb, pte) pte_free((pte)) + +/* + * allocating and freeing a pmd is trivial: the 1-entry pmd is + * inside the pgd, so has no extra memory associated with it. + * (In the PAE case we free the pmds as part of the pgd.) + */ + +#define pmd_alloc_one(mm, addr) ({ BUG(); ((pmd_t *)2); }) +#define pmd_free(x) do { } while (0) +#define __pmd_free_tlb(tlb, x) do { } while (0) +#define pgd_populate(mm, pmd, pte) BUG() + +#define check_pgt_cache() do { } while (0) + +#endif /* _ASM_M32R_PGALLOC_H */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/pgtable-2level.h 2004-09-03 00:57:17.444387312 -0700 @@ -0,0 +1,77 @@ +#ifndef _ASM_M32R_PGTABLE_2LEVEL_H +#define _ASM_M32R_PGTABLE_2LEVEL_H + +/* $Id$ */ + +#include + +/* + * traditional M32R two-level paging structure: + */ + +#define PGDIR_SHIFT 22 +#define PTRS_PER_PGD 1024 + +/* + * the M32R is two-level, so we don't really have any + * PMD directory physically. + */ +#define PMD_SHIFT 22 +#define PTRS_PER_PMD 1 + +#define PTRS_PER_PTE 1024 + +#define pte_ERROR(e) \ + printk("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, pte_val(e)) +#define pmd_ERROR(e) \ + printk("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, pmd_val(e)) +#define pgd_ERROR(e) \ + printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e)) + +/* + * The "pgd_xxx()" functions here are trivial for a folded two-level + * setup: the pgd is never bad, and a pmd always exists (as it's folded + * into the pgd entry) + */ +static __inline__ int pgd_none(pgd_t pgd) { return 0; } +static __inline__ int pgd_bad(pgd_t pgd) { return 0; } +static __inline__ int pgd_present(pgd_t pgd) { return 1; } +#define pgd_clear(xp) do { } while (0) + +/* + * Certain architectures need to do special things when PTEs + * within a page table are directly modified. Thus, the following + * hook is made available. + */ +#define set_pte(pteptr, pteval) (*(pteptr) = pteval) +#define set_pte_atomic(pteptr, pteval) set_pte(pteptr, pteval) +/* + * (pmds are folded into pgds so this doesnt get actually called, + * but the define is needed for a generic inline function.) + */ +#define set_pmd(pmdptr, pmdval) (*(pmdptr) = pmdval) +#define set_pgd(pgdptr, pgdval) (*(pgdptr) = pgdval) + +#define pgd_page(pgd) \ +((unsigned long) __va(pgd_val(pgd) & PAGE_MASK)) + +static __inline__ pmd_t *pmd_offset(pgd_t * dir, unsigned long address) +{ + return (pmd_t *) dir; +} + +#define ptep_get_and_clear(xp) __pte(xchg(&(xp)->pte, 0)) +#define pte_same(a, b) (pte_val(a) == pte_val(b)) +#define pte_page(x) pfn_to_page(pte_pfn(x)) +#define pte_none(x) (!pte_val(x)) +#define pte_pfn(x) (pte_val(x) >> PAGE_SHIFT) +#define pfn_pte(pfn, prot) __pte(((pfn) << PAGE_SHIFT) | pgprot_val(prot)) +#define pfn_pmd(pfn, prot) __pmd(((pfn) << PAGE_SHIFT) | pgprot_val(prot)) + +/* M32R_FIXME : PTE_FILE_MAX_BITS, pte_to_pgoff, pgoff_to_pte */ +#define PTE_FILE_MAX_BITS 31 +#define pte_to_pgoff(pte) (pte_val(pte) >> 1) +#define pgoff_to_pte(off) ((pte_t) { ((off) << 1) | _PAGE_FILE }) + +#endif /* _ASM_M32R_PGTABLE_2LEVEL_H */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/pgtable.h 2004-09-03 00:57:17.446387008 -0700 @@ -0,0 +1,422 @@ +#ifndef _ASM_M32R_PGTABLE_H +#define _ASM_M32R_PGTABLE_H + +/* $Id$ */ + +/* + * The Linux memory management assumes a three-level page table setup. On + * the M32R, we use that, but "fold" the mid level into the top-level page + * table, so that we physically have the same two-level page table as the + * M32R mmu expects. + * + * This file contains the functions and defines necessary to modify and use + * the M32R page table tree. + */ + +/* CAUTION!: If you change macro definitions in this file, you might have to + * change arch/m32r/mmu.S manually. + */ + +#ifndef __ASSEMBLY__ + +#include +#include +#include +#include +#include +#include + +extern pgd_t swapper_pg_dir[1024]; +extern void paging_init(void); + +/* + * ZERO_PAGE is a global shared page that is always zero: used + * for zero-mapped memory areas etc.. + */ +extern unsigned long empty_zero_page[1024]; +#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page)) + +#endif /* !__ASSEMBLY__ */ + +/* + * The Linux x86 paging architecture is 'compile-time dual-mode', it + * implements both the traditional 2-level x86 page tables and the + * newer 3-level PAE-mode page tables. + */ +#ifndef __ASSEMBLY__ +#include +#endif + +#define pgtable_cache_init() do { } while (0) + +#define PMD_SIZE (1UL << PMD_SHIFT) +#define PMD_MASK (~(PMD_SIZE - 1)) +#define PGDIR_SIZE (1UL << PGDIR_SHIFT) +#define PGDIR_MASK (~(PGDIR_SIZE - 1)) + +#define USER_PTRS_PER_PGD (TASK_SIZE / PGDIR_SIZE) +#define FIRST_USER_PGD_NR 0 + +#ifndef __ASSEMBLY__ +/* Just any arbitrary offset to the start of the vmalloc VM area: the + * current 8MB value just means that there will be a 8MB "hole" after the + * physical memory until the kernel virtual memory starts. That means that + * any out-of-bounds memory accesses will hopefully be caught. + * The vmalloc() routines leaves a hole of 4kB between each vmalloced + * area for the same reason. ;) + */ +#define VMALLOC_START KSEG2 +#define VMALLOC_END KSEG3 + +/* + * The 4MB page is guessing.. Detailed in the infamous "Chapter H" + * of the Pentium details, but assuming intel did the straightforward + * thing, this bit set in the page directory entry just means that + * the page directory entry points directly to a 4MB-aligned block of + * memory. + */ + +/* + * M32R TLB format + * + * [0] [1:19] [20:23] [24:31] + * +-----------------------+----+-------------+ + * | VPN |0000| ASID | + * +-----------------------+----+-------------+ + * +-+---------------------+----+-+---+-+-+-+-+ + * |0 PPN |0000|N|AC |L|G|V| | + * +-+---------------------+----+-+---+-+-+-+-+ + * RWX + */ + +#define _PAGE_BIT_DIRTY 0 /* software */ +#define _PAGE_BIT_FILE 0 /* when !present: nonlinear file + mapping */ +#define _PAGE_BIT_PRESENT 1 /* Valid */ +#define _PAGE_BIT_GLOBAL 2 /* Global */ +#define _PAGE_BIT_LARGE 3 /* Large */ +#define _PAGE_BIT_EXEC 4 /* Execute */ +#define _PAGE_BIT_WRITE 5 /* Write */ +#define _PAGE_BIT_READ 6 /* Read */ +#define _PAGE_BIT_NONCACHABLE 7 /* Non cachable */ +#define _PAGE_BIT_USER 8 /* software */ +#define _PAGE_BIT_ACCESSED 9 /* software */ + +#define _PAGE_DIRTY \ + (1UL << _PAGE_BIT_DIRTY) /* software : page changed */ +#define _PAGE_FILE \ + (1UL << _PAGE_BIT_FILE) /* when !present: nonlinear file + mapping */ +#define _PAGE_PRESENT \ + (1UL << _PAGE_BIT_PRESENT) /* Valid : Page is Valid */ +#define _PAGE_GLOBAL \ + (1UL << _PAGE_BIT_GLOBAL) /* Global */ +#define _PAGE_LARGE \ + (1UL << _PAGE_BIT_LARGE) /* Large */ +#define _PAGE_EXEC \ + (1UL << _PAGE_BIT_EXEC) /* Execute */ +#define _PAGE_WRITE \ + (1UL << _PAGE_BIT_WRITE) /* Write */ +#define _PAGE_READ \ + (1UL << _PAGE_BIT_READ) /* Read */ +#define _PAGE_NONCACHABLE \ + (1UL<<_PAGE_BIT_NONCACHABLE) /* Non cachable */ +#define _PAGE_USER \ + (1UL << _PAGE_BIT_USER) /* software : user space access + allowed */ +#define _PAGE_ACCESSED \ + (1UL << _PAGE_BIT_ACCESSED) /* software : page referenced */ + +#define _PAGE_TABLE \ + ( _PAGE_PRESENT | _PAGE_WRITE | _PAGE_READ | _PAGE_USER \ + | _PAGE_ACCESSED | _PAGE_DIRTY ) +#define _KERNPG_TABLE \ + ( _PAGE_PRESENT | _PAGE_WRITE | _PAGE_READ | _PAGE_ACCESSED \ + | _PAGE_DIRTY ) +#define _PAGE_CHG_MASK \ + ( PTE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY ) + +#ifdef CONFIG_MMU +#define PAGE_NONE \ + __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED) +#define PAGE_SHARED \ + __pgprot(_PAGE_PRESENT | _PAGE_WRITE | _PAGE_READ | _PAGE_USER \ + | _PAGE_ACCESSED) +#define PAGE_SHARED_X \ + __pgprot(_PAGE_PRESENT | _PAGE_EXEC | _PAGE_WRITE | _PAGE_READ \ + | _PAGE_USER | _PAGE_ACCESSED) +#define PAGE_COPY \ + __pgprot(_PAGE_PRESENT | _PAGE_EXEC | _PAGE_READ | _PAGE_USER \ + | _PAGE_ACCESSED) +#define PAGE_COPY_X \ + __pgprot(_PAGE_PRESENT | _PAGE_EXEC | _PAGE_READ | _PAGE_USER \ + | _PAGE_ACCESSED) +#define PAGE_READONLY \ + __pgprot(_PAGE_PRESENT | _PAGE_READ | _PAGE_USER | _PAGE_ACCESSED) +#define PAGE_READONLY_X \ + __pgprot(_PAGE_PRESENT | _PAGE_EXEC | _PAGE_READ | _PAGE_USER \ + | _PAGE_ACCESSED) + +#define __PAGE_KERNEL \ + ( _PAGE_PRESENT | _PAGE_EXEC | _PAGE_WRITE | _PAGE_READ | _PAGE_DIRTY \ + | _PAGE_ACCESSED ) +#define __PAGE_KERNEL_RO ( __PAGE_KERNEL & ~_PAGE_WRITE ) +#define __PAGE_KERNEL_NOCACHE ( __PAGE_KERNEL | _PAGE_NONCACHABLE) + +#define MAKE_GLOBAL(x) __pgprot((x) | _PAGE_GLOBAL) + +#define PAGE_KERNEL MAKE_GLOBAL(__PAGE_KERNEL) +#define PAGE_KERNEL_RO MAKE_GLOBAL(__PAGE_KERNEL_RO) +#define PAGE_KERNEL_NOCACHE MAKE_GLOBAL(__PAGE_KERNEL_NOCACHE) + +#else +#define PAGE_NONE __pgprot(0) +#define PAGE_SHARED __pgprot(0) +#define PAGE_SHARED_X __pgprot(0) +#define PAGE_COPY __pgprot(0) +#define PAGE_COPY_X __pgprot(0) +#define PAGE_READONLY __pgprot(0) +#define PAGE_READONLY_X __pgprot(0) + +#define PAGE_KERNEL __pgprot(0) +#define PAGE_KERNEL_RO __pgprot(0) +#define PAGE_KERNEL_NOCACHE __pgprot(0) +#endif /* CONFIG_MMU */ + +/* + * The i386 can't do page protection for execute, and considers that + * the same are read. Also, write permissions imply read permissions. + * This is the closest we can get.. + */ + /* rwx */ +#define __P000 PAGE_NONE +#define __P001 PAGE_READONLY_X +#define __P010 PAGE_COPY_X +#define __P011 PAGE_COPY_X +#define __P100 PAGE_READONLY +#define __P101 PAGE_READONLY_X +#define __P110 PAGE_COPY_X +#define __P111 PAGE_COPY_X + +#define __S000 PAGE_NONE +#define __S001 PAGE_READONLY_X +#define __S010 PAGE_SHARED +#define __S011 PAGE_SHARED_X +#define __S100 PAGE_READONLY +#define __S101 PAGE_READONLY_X +#define __S110 PAGE_SHARED +#define __S111 PAGE_SHARED_X + +/* page table for 0-4MB for everybody */ + +#define pte_present(x) (pte_val(x) & _PAGE_PRESENT) +#define pte_clear(xp) do { set_pte(xp, __pte(0)); } while (0) + +#define pmd_none(x) (!pmd_val(x)) +#define pmd_present(x) (pmd_val(x) & _PAGE_PRESENT) +#define pmd_clear(xp) do { set_pmd(xp, __pmd(0)); } while (0) +#define pmd_bad(x) ((pmd_val(x) & (~PAGE_MASK & ~_PAGE_USER)) \ + != _KERNPG_TABLE) + +#define pages_to_mb(x) ((x) >> (20 - PAGE_SHIFT)) + +/* + * The following only work if pte_present() is true. + * Undefined behaviour if not.. + */ +static __inline__ int pte_user(pte_t pte) +{ + return pte_val(pte) & _PAGE_USER; +} + +static __inline__ int pte_read(pte_t pte) +{ + return pte_val(pte) & _PAGE_READ; +} + +static __inline__ int pte_exec(pte_t pte) +{ + return pte_val(pte) & _PAGE_EXEC; +} + +static __inline__ int pte_dirty(pte_t pte) +{ + return pte_val(pte) & _PAGE_DIRTY; +} + +static __inline__ int pte_young(pte_t pte) +{ + return pte_val(pte) & _PAGE_ACCESSED; +} + +static __inline__ int pte_write(pte_t pte) +{ + return pte_val(pte) & _PAGE_WRITE; +} + +/* + * The following only works if pte_present() is not true. + */ +static __inline__ int pte_file(pte_t pte) +{ + return pte_val(pte) & _PAGE_FILE; +} + +static __inline__ pte_t pte_rdprotect(pte_t pte) +{ + pte_val(pte) &= ~_PAGE_READ; + return pte; +} + +static __inline__ pte_t pte_exprotect(pte_t pte) +{ + pte_val(pte) &= ~_PAGE_EXEC; + return pte; +} + +static __inline__ pte_t pte_mkclean(pte_t pte) +{ + pte_val(pte) &= ~_PAGE_DIRTY; + return pte; +} + +static __inline__ pte_t pte_mkold(pte_t pte) +{ + pte_val(pte) &= ~_PAGE_ACCESSED;return pte;} + +static __inline__ pte_t pte_wrprotect(pte_t pte) +{ + pte_val(pte) &= ~_PAGE_WRITE; + return pte; +} + +static __inline__ pte_t pte_mkread(pte_t pte) +{ + pte_val(pte) |= _PAGE_READ; + return pte; +} + +static __inline__ pte_t pte_mkexec(pte_t pte) +{ + pte_val(pte) |= _PAGE_EXEC; + return pte; +} + +static __inline__ pte_t pte_mkdirty(pte_t pte) +{ + pte_val(pte) |= _PAGE_DIRTY; + return pte; +} + +static __inline__ pte_t pte_mkyoung(pte_t pte) +{ + pte_val(pte) |= _PAGE_ACCESSED; + return pte; +} + +static __inline__ pte_t pte_mkwrite(pte_t pte) +{ + pte_val(pte) |= _PAGE_WRITE; + return pte; +} + +static __inline__ int ptep_test_and_clear_dirty(pte_t *ptep) +{ + return test_and_clear_bit(_PAGE_BIT_DIRTY, ptep); +} + +static __inline__ int ptep_test_and_clear_young(pte_t *ptep) +{ + return test_and_clear_bit(_PAGE_BIT_ACCESSED, ptep); +} + +static __inline__ void ptep_set_wrprotect(pte_t *ptep) +{ + clear_bit(_PAGE_BIT_WRITE, ptep); +} + +static __inline__ void ptep_mkdirty(pte_t *ptep) +{ + set_bit(_PAGE_BIT_DIRTY, ptep); +} + +/* + * Conversion functions: convert a page and protection to a page entry, + * and a page entry and page directory to the page they refer to. + */ +#define mk_pte(page, pgprot) pfn_pte(page_to_pfn(page), pgprot) + +static __inline__ pte_t pte_modify(pte_t pte, pgprot_t newprot) +{ + set_pte(&pte, __pte((pte_val(pte) & _PAGE_CHG_MASK) \ + | pgprot_val(newprot))); + + return pte; +} + +#define page_pte(page) page_pte_prot(page, __pgprot(0)) + +/* + * Conversion functions: convert a page and protection to a page entry, + * and a page entry and page directory to the page they refer to. + */ + +static __inline__ void pmd_set(pmd_t * pmdp, pte_t * ptep) +{ + pmd_val(*pmdp) = (((unsigned long) ptep) & PAGE_MASK); +} + +#define pmd_page_kernel(pmd) \ + ((unsigned long) __va(pmd_val(pmd) & PAGE_MASK)) + +#ifndef CONFIG_DISCONTIGMEM +#define pmd_page(pmd) (mem_map + ((pmd_val(pmd) >> PAGE_SHIFT) - PFN_BASE)) +#endif /* !CONFIG_DISCONTIGMEM */ + +/* to find an entry in a page-table-directory. */ +#define pgd_index(address) \ + (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1)) + +#define pgd_offset(mm, address) ((mm)->pgd + pgd_index(address)) + +/* to find an entry in a kernel page-table-directory */ +#define pgd_offset_k(address) pgd_offset(&init_mm, address) + +#define pmd_index(address) \ + (((address) >> PMD_SHIFT) & (PTRS_PER_PMD - 1)) + +#define pte_index(address) \ + (((address) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) +#define pte_offset_kernel(dir, address) \ + ((pte_t *)pmd_page_kernel(*(dir)) + pte_index(address)) +#define pte_offset_map(dir, address) \ + ((pte_t *)page_address(pmd_page(*(dir))) + pte_index(address)) +#define pte_offset_map_nested(dir, address) pte_offset_map(dir, address) +#define pte_unmap(pte) do { } while (0) +#define pte_unmap_nested(pte) do { } while (0) + +/* Encode and de-code a swap entry */ +#define __swp_type(x) (((x).val >> 1) & 0x3f) +#define __swp_offset(x) ((x).val >> 8) +#define __swp_entry(type, offset) \ + ((swp_entry_t) { ((type) << 1) | ((offset) << 8) }) +#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) +#define __swp_entry_to_pte(x) ((pte_t) { (x).val }) + +#endif /* !__ASSEMBLY__ */ + +/* Needs to be defined here and not in linux/mm.h, as it is arch dependent */ +#define kern_addr_valid(addr) (1) + +#define io_remap_page_range remap_page_range + +#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG +#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_DIRTY +#define __HAVE_ARCH_PTEP_GET_AND_CLEAR +#define __HAVE_ARCH_PTEP_SET_WRPROTECT +#define __HAVE_ARCH_PTEP_MKDIRTY +#define __HAVE_ARCH_PTE_SAME +#include + +#endif /* _ASM_M32R_PGTABLE_H */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/poll.h 2004-09-03 00:57:17.446387008 -0700 @@ -0,0 +1,29 @@ +#ifndef _ASM_M32R_POLL_H +#define _ASM_M32R_POLL_H + +/* $Id$ */ + +/* orig : i386 2.4.18 */ + +/* These are specified by iBCS2 */ +#define POLLIN 0x0001 +#define POLLPRI 0x0002 +#define POLLOUT 0x0004 +#define POLLERR 0x0008 +#define POLLHUP 0x0010 +#define POLLNVAL 0x0020 + +/* The rest seem to be more-or-less nonstandard. Check them! */ +#define POLLRDNORM 0x0040 +#define POLLRDBAND 0x0080 +#define POLLWRNORM 0x0100 +#define POLLWRBAND 0x0200 +#define POLLMSG 0x0400 + +struct pollfd { + int fd; + short events; + short revents; +}; + +#endif /* _ASM_M32R_POLL_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/posix_types.h 2004-09-03 00:57:17.447386856 -0700 @@ -0,0 +1,126 @@ +#ifndef _ASM_M32R_POSIX_TYPES_H +#define _ASM_M32R_POSIX_TYPES_H + +/* $Id$ */ + +/* orig : i386, sh 2.4.18 */ + +/* + * This file is generally used by user-level software, so you need to + * be a little careful about namespace pollution etc. Also, we cannot + * assume GCC is being used. + */ + +typedef unsigned long __kernel_ino_t; +typedef unsigned short __kernel_mode_t; +typedef unsigned short __kernel_nlink_t; +typedef long __kernel_off_t; +typedef int __kernel_pid_t; +typedef unsigned short __kernel_ipc_pid_t; +typedef unsigned short __kernel_uid_t; +typedef unsigned short __kernel_gid_t; +typedef unsigned int __kernel_size_t; +typedef int __kernel_ssize_t; +typedef int __kernel_ptrdiff_t; +typedef long __kernel_time_t; +typedef long __kernel_suseconds_t; +typedef long __kernel_clock_t; +typedef int __kernel_timer_t; +typedef int __kernel_clockid_t; +typedef int __kernel_daddr_t; +typedef char * __kernel_caddr_t; +typedef unsigned short __kernel_uid16_t; +typedef unsigned short __kernel_gid16_t; +typedef unsigned int __kernel_uid32_t; +typedef unsigned int __kernel_gid32_t; + +typedef unsigned short __kernel_old_uid_t; +typedef unsigned short __kernel_old_gid_t; +typedef unsigned short __kernel_old_dev_t; + +#ifdef __GNUC__ +typedef long long __kernel_loff_t; +#endif + +typedef struct { +#if defined(__KERNEL__) || defined(__USE_ALL) + int val[2]; +#else /* !defined(__KERNEL__) && !defined(__USE_ALL) */ + int __val[2]; +#endif /* !defined(__KERNEL__) && !defined(__USE_ALL) */ +} __kernel_fsid_t; + +#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2) + +#undef __FD_SET +static __inline__ void __FD_SET(unsigned long __fd, __kernel_fd_set *__fdsetp) +{ + unsigned long __tmp = __fd / __NFDBITS; + unsigned long __rem = __fd % __NFDBITS; + __fdsetp->fds_bits[__tmp] |= (1UL<<__rem); +} + +#undef __FD_CLR +static __inline__ void __FD_CLR(unsigned long __fd, __kernel_fd_set *__fdsetp) +{ + unsigned long __tmp = __fd / __NFDBITS; + unsigned long __rem = __fd % __NFDBITS; + __fdsetp->fds_bits[__tmp] &= ~(1UL<<__rem); +} + + +#undef __FD_ISSET +static __inline__ int __FD_ISSET(unsigned long __fd, const __kernel_fd_set *__p) +{ + unsigned long __tmp = __fd / __NFDBITS; + unsigned long __rem = __fd % __NFDBITS; + return (__p->fds_bits[__tmp] & (1UL<<__rem)) != 0; +} + +/* + * This will unroll the loop for the normal constant case (8 ints, + * for a 256-bit fd_set) + */ +#undef __FD_ZERO +static __inline__ void __FD_ZERO(__kernel_fd_set *__p) +{ + unsigned long *__tmp = __p->fds_bits; + int __i; + + if (__builtin_constant_p(__FDSET_LONGS)) { + switch (__FDSET_LONGS) { + case 16: + __tmp[ 0] = 0; __tmp[ 1] = 0; + __tmp[ 2] = 0; __tmp[ 3] = 0; + __tmp[ 4] = 0; __tmp[ 5] = 0; + __tmp[ 6] = 0; __tmp[ 7] = 0; + __tmp[ 8] = 0; __tmp[ 9] = 0; + __tmp[10] = 0; __tmp[11] = 0; + __tmp[12] = 0; __tmp[13] = 0; + __tmp[14] = 0; __tmp[15] = 0; + return; + + case 8: + __tmp[ 0] = 0; __tmp[ 1] = 0; + __tmp[ 2] = 0; __tmp[ 3] = 0; + __tmp[ 4] = 0; __tmp[ 5] = 0; + __tmp[ 6] = 0; __tmp[ 7] = 0; + return; + + case 4: + __tmp[ 0] = 0; __tmp[ 1] = 0; + __tmp[ 2] = 0; __tmp[ 3] = 0; + return; + } + } + __i = __FDSET_LONGS; + while (__i) { + __i--; + *__tmp = 0; + __tmp++; + } +} + +#endif /* defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2) */ + +#endif /* _ASM_M32R_POSIX_TYPES_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/processor.h 2004-09-03 00:57:17.448386704 -0700 @@ -0,0 +1,157 @@ +#ifndef _ASM_M32R_PROCESSOR_H +#define _ASM_M32R_PROCESSOR_H + +/* $Id$ */ + +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2001 by Hiroyuki Kondo, Hirokazu Takata, and Hitoshi Yamamoto + */ + +/* + * include/asm-m32r/processor.h + * + * Copyright (C) 1994 Linus Torvalds + */ +#include +#include +#include +#include /* pt_regs */ + +#include + +/* + * Default implementation of macro that returns current + * instruction pointer ("program counter"). + */ +#define current_text_addr() ({ __label__ _l; _l: &&_l; }) + +/* + * CPU type and hardware bug flags. Kept separately for each CPU. + * Members of this structure are referenced in head.S, so think twice + * before touching them. [mj] + */ + +struct cpuinfo_m32r { + unsigned long pgtable_cache_sz; + unsigned long cpu_clock; + unsigned long bus_clock; + unsigned long timer_divide; + unsigned long loops_per_jiffy; +}; + +/* + * capabilities of CPUs + */ + +extern struct cpuinfo_m32r boot_cpu_data; + +#ifdef CONFIG_SMP +extern struct cpuinfo_m32r cpu_data[]; +#define current_cpu_data cpu_data[smp_processor_id()] +#else +#define cpu_data &boot_cpu_data +#define current_cpu_data boot_cpu_data +#endif + +/* + * User space process size: 2GB (default). + */ +#ifdef CONFIG_MMU +#define TASK_SIZE (0x80000000UL) +#else +#define TASK_SIZE (0x00400000UL) +#endif + +/* This decides where the kernel will search for a free chunk of vm + * space during mmap's. + */ +#define TASK_UNMAPPED_BASE PAGE_ALIGN(TASK_SIZE / 3) + +typedef struct { + unsigned long seg; +} mm_segment_t; + +struct debug_trap { + int nr_trap; + unsigned long addr; + unsigned long insn; +}; + +struct thread_struct { + unsigned long address; + unsigned long trap_no; /* Trap number */ + unsigned long error_code; /* Error code of trap */ + unsigned long lr; /* saved pc */ + unsigned long sp; /* user stack pointer */ + struct debug_trap debug_trap; +}; + +#define INIT_SP (sizeof(init_stack) + (unsigned long) &init_stack) + +#define INIT_THREAD { \ + .sp = INIT_SP, \ +} + +/* + * Do necessary setup to start up a newly executed thread. + */ + +/* User process Backup PSW */ +#define USERPS_BPSW (M32R_PSW_BSM|M32R_PSW_BIE|M32R_PSW_BPM) + +#define start_thread(regs, new_pc, new_spu) \ + do { \ + set_fs(USER_DS); \ + regs->psw = (regs->psw | USERPS_BPSW) & 0x0000FFFFUL; \ + regs->bpc = new_pc; \ + regs->spu = new_spu; \ + } while (0) + +/* Forward declaration, a strange C thing */ +struct task_struct; +struct mm_struct; + +/* Free all resources held by a thread. */ +extern void release_thread(struct task_struct *); + +#define prepare_to_copy(tsk) do { } while (0) + +/* + * create a kernel thread without removing it from tasklists + */ +extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); + +/* Copy and release all segment info associated with a VM */ +extern void copy_segments(struct task_struct *p, struct mm_struct * mm); +extern void release_segments(struct mm_struct * mm); + +extern unsigned long thread_saved_pc(struct task_struct *); + +/* Copy and release all segment info associated with a VM */ +#define copy_segments(p, mm) do { } while (0) +#define release_segments(mm) do { } while (0) + +unsigned long get_wchan(struct task_struct *p); +#define KSTK_EIP(tsk) ((tsk)->thread.lr) +#define KSTK_ESP(tsk) ((tsk)->thread.sp) + +#define THREAD_SIZE (2*PAGE_SIZE) + +/* REP NOP (PAUSE) is a good thing to insert into busy-wait loops. */ +static __inline__ void rep_nop(void) +{ + __asm__ __volatile__( + "nop \n\t" + "nop \n\t" + : + : + : "memory"); +} + +#define cpu_relax() rep_nop() + +#endif /* _ASM_M32R_PROCESSOR_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/ptrace.h 2004-09-03 00:57:17.449386552 -0700 @@ -0,0 +1,163 @@ +#ifndef _M32R_PTRACE_H +#define _M32R_PTRACE_H + +/* $Id$ */ + +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2001, 2002, 2004 Hirokazu Takata + */ + +#include +#include /* M32R_PSW_BSM, M32R_PSW_BPM */ + +/* 0 - 13 are integer registers (general purpose registers). */ +#define PT_R4 0 +#define PT_R5 1 +#define PT_R6 2 +#define PT_REGS 3 +#define PT_R0 4 +#define PT_R1 5 +#define PT_R2 6 +#define PT_R3 7 +#define PT_R7 8 +#define PT_R8 9 +#define PT_R9 10 +#define PT_R10 11 +#define PT_R11 12 +#define PT_R12 13 +#define PT_SYSCNR 14 +#define PT_R13 PT_FP +#define PT_R14 PT_LR +#define PT_R15 PT_SP + +/* processor status and miscellaneous context registers. */ +#if defined(CONFIG_ISA_M32R2) && defined(CONFIG_ISA_DSP_LEVEL2) +#define PT_ACC0H 15 +#define PT_ACC0L 16 +#define PT_ACC1H 17 +#define PT_ACC1L 18 +#define PT_ACCH PT_ACC0H +#define PT_ACCL PT_ACC0L +#define PT_PSW 19 +#define PT_BPC 20 +#define PT_BBPSW 21 +#define PT_BBPC 22 +#define PT_SPU 23 +#define PT_FP 24 +#define PT_LR 25 +#define PT_SPI 26 +#define PT_ORIGR0 27 +#elif defined(CONFIG_ISA_M32R2) || defined(CONFIG_ISA_M32R) +#define PT_ACCH 15 +#define PT_ACCL 16 +#define PT_PSW 17 +#define PT_BPC 18 +#define PT_BBPSW 19 +#define PT_BBPC 20 +#define PT_SPU 21 +#define PT_FP 22 +#define PT_LR 23 +#define PT_SPI 24 +#define PT_ORIGR0 25 +#else +#error unknown isa conifiguration +#endif + +/* virtual pt_reg entry for gdb */ +#define PT_PC 30 +#define PT_CBR 31 +#define PT_EVB 32 + + +/* Control registers. */ +#define SPR_CR0 PT_PSW +#define SPR_CR1 PT_CBR /* read only */ +#define SPR_CR2 PT_SPI +#define SPR_CR3 PT_SPU +#define SPR_CR4 +#define SPR_CR5 PT_EVB /* part of M32R/E, M32R/I core only */ +#define SPR_CR6 PT_BPC +#define SPR_CR7 +#define SPR_CR8 PT_BBPSW +#define SPR_CR9 +#define SPR_CR10 +#define SPR_CR11 +#define SPR_CR12 +#define SPR_CR13 PT_WR +#define SPR_CR14 PT_BBPC +#define SPR_CR15 + +/* this struct defines the way the registers are stored on the + stack during a system call. */ +struct pt_regs { + /* Saved main processor registers. */ + unsigned long r4; + unsigned long r5; + unsigned long r6; + struct pt_regs *pt_regs; + unsigned long r0; + unsigned long r1; + unsigned long r2; + unsigned long r3; + unsigned long r7; + unsigned long r8; + unsigned long r9; + unsigned long r10; + unsigned long r11; + unsigned long r12; + long syscall_nr; + + /* Saved main processor status and miscellaneous context registers. */ +#if defined(CONFIG_ISA_M32R2) && defined(CONFIG_ISA_DSP_LEVEL2) + unsigned long acc0h; + unsigned long acc0l; + unsigned long acc1h; + unsigned long acc1l; +#elif defined(CONFIG_ISA_M32R2) || defined(CONFIG_ISA_M32R) + unsigned long acch; + unsigned long accl; +#else +#error unknown isa configuration +#endif + unsigned long psw; + unsigned long bpc; /* saved PC for TRAP syscalls */ + unsigned long bbpsw; + unsigned long bbpc; + unsigned long spu; /* saved user stack */ + unsigned long fp; + unsigned long lr; /* saved PC for JL syscalls */ + unsigned long spi; /* saved kernel stack */ + unsigned long orig_r0; +}; + +/* Arbitrarily choose the same ptrace numbers as used by the Sparc code. */ +#define PTRACE_GETREGS 12 +#define PTRACE_SETREGS 13 + +#define PTRACE_OLDSETOPTIONS 21 + +/* options set using PTRACE_SETOPTIONS */ +#define PTRACE_O_TRACESYSGOOD 0x00000001 + +#ifdef __KERNEL__ +#if defined(CONFIG_ISA_M32R2) || defined(CONFIG_CHIP_VDEC2) +#define user_mode(regs) ((M32R_PSW_BPM & (regs)->psw) != 0) +#elif defined(CONFIG_ISA_M32R) +#define user_mode(regs) ((M32R_PSW_BSM & (regs)->psw) != 0) +#else +#error unknown isa configuration +#endif + +#define instruction_pointer(regs) ((regs)->bpc) + +extern void show_regs(struct pt_regs *); + +extern void withdraw_debug_trap(struct pt_regs *regs); + +#endif /* __KERNEL */ + +#endif /* _M32R_PTRACE_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/resource.h 2004-09-03 00:57:18.740190320 -0700 @@ -0,0 +1,55 @@ +#ifndef _ASM_M32R_RESOURCE_H +#define _ASM_M32R_RESOURCE_H + +/* $Id$ */ + +/* orig : i386 2.4.18 */ + +/* + * Resource limits + */ + +#define RLIMIT_CPU 0 /* CPU time in ms */ +#define RLIMIT_FSIZE 1 /* Maximum filesize */ +#define RLIMIT_DATA 2 /* max data size */ +#define RLIMIT_STACK 3 /* max stack size */ +#define RLIMIT_CORE 4 /* max core file size */ +#define RLIMIT_RSS 5 /* max resident set size */ +#define RLIMIT_NPROC 6 /* max number of processes */ +#define RLIMIT_NOFILE 7 /* max number of open files */ +#define RLIMIT_MEMLOCK 8 /* max locked-in-memory address space */ +#define RLIMIT_AS 9 /* address space limit */ +#define RLIMIT_LOCKS 10 /* maximum file locks held */ +#define RLIMIT_SIGPENDING 11 /* max number of pending signals */ +#define RLIMIT_MSGQUEUE 12 /* maximum bytes in POSIX mqueues */ + +#define RLIM_NLIMITS 13 + +/* + * SuS says limits have to be unsigned. + * Which makes a ton more sense anyway. + */ +#define RLIM_INFINITY (~0UL) + +#ifdef __KERNEL__ + +#define INIT_RLIMITS \ +{ \ + { RLIM_INFINITY, RLIM_INFINITY }, \ + { RLIM_INFINITY, RLIM_INFINITY }, \ + { RLIM_INFINITY, RLIM_INFINITY }, \ + { _STK_LIM, RLIM_INFINITY }, \ + { 0, RLIM_INFINITY }, \ + { RLIM_INFINITY, RLIM_INFINITY }, \ + { 0, 0 }, \ + { INR_OPEN, INR_OPEN }, \ + { RLIM_INFINITY, RLIM_INFINITY }, \ + { RLIM_INFINITY, RLIM_INFINITY }, \ + { RLIM_INFINITY, RLIM_INFINITY }, \ + { MAX_SIGPENDING, MAX_SIGPENDING }, \ + { MQ_BYTES_MAX, MQ_BYTES_MAX }, \ +} + +#endif /* __KERNEL__ */ + +#endif /* _ASM_M32R_RESOURCE_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/rtc.h 2004-09-03 00:57:17.450386400 -0700 @@ -0,0 +1,70 @@ +/* $Id: rtc.h,v 1.1.1.1 2004/03/25 04:29:22 hitoshiy Exp $ */ + +#ifndef __RTC_H__ +#define __RTC_H__ + + +#include + + /* Dallas DS1302 clock/calendar register numbers. */ +# define RTC_SECONDS 0 +# define RTC_MINUTES 1 +# define RTC_HOURS 2 +# define RTC_DAY_OF_MONTH 3 +# define RTC_MONTH 4 +# define RTC_WEEKDAY 5 +# define RTC_YEAR 6 +# define RTC_CONTROL 7 + + /* Bits in CONTROL register. */ +# define RTC_CONTROL_WRITEPROTECT 0x80 +# define RTC_TRICKLECHARGER 8 + + /* Bits in TRICKLECHARGER register TCS TCS TCS TCS DS DS RS RS. */ +# define RTC_TCR_PATTERN 0xA0 /* 1010xxxx */ +# define RTC_TCR_1DIOD 0x04 /* xxxx01xx */ +# define RTC_TCR_2DIOD 0x08 /* xxxx10xx */ +# define RTC_TCR_DISABLED 0x00 /* xxxxxx00 Disabled */ +# define RTC_TCR_2KOHM 0x01 /* xxxxxx01 2KOhm */ +# define RTC_TCR_4KOHM 0x02 /* xxxxxx10 4kOhm */ +# define RTC_TCR_8KOHM 0x03 /* xxxxxx11 8kOhm */ + +#ifdef CONFIG_M32700UT_DS1302 +extern unsigned char ds1302_readreg(int reg); +extern void ds1302_writereg(int reg, unsigned char val); +extern int ds1302_init(void); +# define CMOS_READ(x) ds1302_readreg(x) +# define CMOS_WRITE(val,reg) ds1302_writereg(reg,val) +# define RTC_INIT() ds1302_init() +#else + /* No RTC configured so we shouldn't try to access any. */ +# define CMOS_READ(x) 42 +# define CMOS_WRITE(x,y) +# define RTC_INIT() (-1) +#endif + +/* + * The struct used to pass data via the following ioctl. Similar to the + * struct tm in , but it needs to be here so that the kernel + * source is self contained, allowing cross-compiles, etc. etc. + */ +struct rtc_time { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; +}; + +/* ioctl() calls that are permitted to the /dev/rtc interface. */ +#define RTC_MAGIC 'p' +#define RTC_RD_TIME _IOR(RTC_MAGIC, 0x09, struct rtc_time) /* Read RTC time. */ +#define RTC_SET_TIME _IOW(RTC_MAGIC, 0x0a, struct rtc_time) /* Set RTC time. */ +#define RTC_SET_CHARGE _IOW(RTC_MAGIC, 0x0b, int) +#define RTC_MAX_IOCTL 0x0b + +#endif /* __RTC_H__ */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/scatterlist.h 2004-09-03 00:57:17.450386400 -0700 @@ -0,0 +1,18 @@ +#ifndef _ASM_M32R_SCATTERLIST_H +#define _ASM_M32R_SCATTERLIST_H + +/* $Id$ */ + +struct scatterlist { + char * address; /* Location data is to be transferred to, NULL for + * highmem page */ + struct page * page; /* Location for highmem page, if any */ + unsigned int offset;/* for highmem, page offset */ + + dma_addr_t dma_address; + unsigned int length; +}; + +#define ISA_DMA_THRESHOLD (0x1fffffff) + +#endif /* _ASM_M32R_SCATTERLIST_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/sections.h 2004-09-03 00:57:17.450386400 -0700 @@ -0,0 +1,8 @@ +#ifndef _M32R_SECTIONS_H +#define _M32R_SECTIONS_H + +/* nothing to see, move along */ +#include + +#endif /* _M32R_SECTIONS_H */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/segment.h 2004-09-03 00:57:17.450386400 -0700 @@ -0,0 +1,14 @@ +#ifndef _ASM_M32R_SEGMENT_H +#define _ASM_M32R_SEGMENT_H + +/* $Id$ */ + +/* orig : i386 (2.4.18) */ + +#define __KERNEL_CS 0x10 +#define __KERNEL_DS 0x18 + +#define __USER_CS 0x23 +#define __USER_DS 0x2B + +#endif /* _ASM_M32R_SEGMENT_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/semaphore.h 2004-09-03 00:57:17.452386096 -0700 @@ -0,0 +1,266 @@ +#ifndef _ASM_M32R_SEMAPHORE_H +#define _ASM_M32R_SEMAPHORE_H + +/* $Id$ */ + +#include + +#ifdef __KERNEL__ + +/* + * SMP- and interrupt-safe semaphores.. + * + * (C) Copyright 1996 Linus Torvalds + * + * Modified 1996-12-23 by Dave Grothe to fix bugs in + * the original code and to make semaphore waits + * interruptible so that processes waiting on + * semaphores can be killed. + * Modified 1999-02-14 by Andrea Arcangeli, split the sched.c helper + * functions in asm/sempahore-helper.h while fixing a + * potential and subtle race discovered by Ulrich Schmid + * in down_interruptible(). Since I started to play here I + * also implemented the `trylock' semaphore operation. + * 1999-07-02 Artur Skawina + * Optimized "0(ecx)" -> "(ecx)" (the assembler does not + * do this). Changed calling sequences from push/jmp to + * traditional call/ret. + * Modified 2001-01-01 Andreas Franck + * Some hacks to ensure compatibility with recent + * GCC snapshots, to avoid stack corruption when compiling + * with -fomit-frame-pointer. It's not sure if this will + * be fixed in GCC, as our previous implementation was a + * bit dubious. + * + * If you would like to see an analysis of this implementation, please + * ftp to gcom.com and download the file + * /pub/linux/src/semaphore/semaphore-2.0.24.tar.gz. + * + */ + +#include +#include +#include +#include +#include + +#undef LOAD +#undef STORE +#ifdef CONFIG_SMP +#define LOAD "lock" +#define STORE "unlock" +#else +#define LOAD "ld" +#define STORE "st" +#endif + +struct semaphore { + atomic_t count; + int sleepers; + wait_queue_head_t wait; +#ifdef WAITQUEUE_DEBUG + long __magic; +#endif +}; + +#ifdef WAITQUEUE_DEBUG +# define __SEM_DEBUG_INIT(name) \ + , (int)&(name).__magic +#else +# define __SEM_DEBUG_INIT(name) +#endif + +#define __SEMAPHORE_INITIALIZER(name,count) \ +{ ATOMIC_INIT(count), 0, __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ + __SEM_DEBUG_INIT(name) } + +#define __MUTEX_INITIALIZER(name) \ + __SEMAPHORE_INITIALIZER(name,1) + +#define __DECLARE_SEMAPHORE_GENERIC(name,count) \ + struct semaphore name = __SEMAPHORE_INITIALIZER(name,count) + +#define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name,1) +#define DECLARE_MUTEX_LOCKED(name) __DECLARE_SEMAPHORE_GENERIC(name,0) + +static __inline__ void sema_init (struct semaphore *sem, int val) +{ +/* + * *sem = (struct semaphore)__SEMAPHORE_INITIALIZER((*sem),val); + * + * i'd rather use the more flexible initialization above, but sadly + * GCC 2.7.2.3 emits a bogus warning. EGCS doesnt. Oh well. + */ + atomic_set(&sem->count, val); + sem->sleepers = 0; + init_waitqueue_head(&sem->wait); +#ifdef WAITQUEUE_DEBUG + sem->__magic = (int)&sem->__magic; +#endif +} + +static __inline__ void init_MUTEX (struct semaphore *sem) +{ + sema_init(sem, 1); +} + +static __inline__ void init_MUTEX_LOCKED (struct semaphore *sem) +{ + sema_init(sem, 0); +} + +asmlinkage void __down_failed(void /* special register calling convention */); +asmlinkage int __down_failed_interruptible(void /* params in registers */); +asmlinkage int __down_failed_trylock(void /* params in registers */); +asmlinkage void __up_wakeup(void /* special register calling convention */); + +asmlinkage void __down(struct semaphore * sem); +asmlinkage int __down_interruptible(struct semaphore * sem); +asmlinkage int __down_trylock(struct semaphore * sem); +asmlinkage void __up(struct semaphore * sem); + +/* + * This is ugly, but we want the default case to fall through. + * "__down_failed" is a special asm handler that calls the C + * routine that actually waits. See arch/i386/kernel/semaphore.c + */ +static __inline__ void down(struct semaphore * sem) +{ + unsigned long flags; + int temp; + +#ifdef WAITQUEUE_DEBUG + CHECK_MAGIC(sem->__magic); +#endif + + local_irq_save(flags); + __asm__ __volatile__ ( + "# down \n\t" + DCACHE_CLEAR("%0", "r4", "%1") + LOAD" %0, @%1; \n\t" + "addi %0, #-1; \n\t" + STORE" %0, @%1; \n\t" + : "=&r" (temp) + : "r" (&sem->count) + : "memory" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r4" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + local_irq_restore(flags); + + if (temp < 0) + __down(sem); +} + +/* + * Interruptible try to acquire a semaphore. If we obtained + * it, return zero. If we were interrupted, returns -EINTR + */ +static __inline__ int down_interruptible(struct semaphore * sem) +{ + unsigned long flags; + int temp; + int result = 0; + +#ifdef WAITQUEUE_DEBUG + CHECK_MAGIC(sem->__magic); +#endif + + local_irq_save(flags); + __asm__ __volatile__ ( + "# down_interruptible \n\t" + DCACHE_CLEAR("%0", "r4", "%1") + LOAD" %0, @%1; \n\t" + "addi %0, #-1; \n\t" + STORE" %0, @%1; \n\t" + : "=&r" (temp) + : "r" (&sem->count) + : "memory" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r4" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + local_irq_restore(flags); + + if (temp < 0) + result = __down_interruptible(sem); + + return result; +} + +/* + * Non-blockingly attempt to down() a semaphore. + * Returns zero if we acquired it + */ +static __inline__ int down_trylock(struct semaphore * sem) +{ + unsigned long flags; + int temp; + int result = 0; + +#ifdef WAITQUEUE_DEBUG + CHECK_MAGIC(sem->__magic); +#endif + + local_irq_save(flags); + __asm__ __volatile__ ( + "# down_trylock \n\t" + DCACHE_CLEAR("%0", "r4", "%1") + LOAD" %0, @%1; \n\t" + "addi %0, #-1; \n\t" + STORE" %0, @%1; \n\t" + : "=&r" (temp) + : "r" (&sem->count) + : "memory" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r4" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + local_irq_restore(flags); + + if (temp < 0) + result = __down_trylock(sem); + + return result; +} + +/* + * Note! This is subtle. We jump to wake people up only if + * the semaphore was negative (== somebody was waiting on it). + * The default case (no contention) will result in NO + * jumps for both down() and up(). + */ +static __inline__ void up(struct semaphore * sem) +{ + unsigned long flags; + int temp; + +#ifdef WAITQUEUE_DEBUG + CHECK_MAGIC(sem->__magic); +#endif + + local_irq_save(flags); + __asm__ __volatile__ ( + "# up \n\t" + DCACHE_CLEAR("%0", "r4", "%1") + LOAD" %0, @%1; \n\t" + "addi %0, #1; \n\t" + STORE" %0, @%1; \n\t" + : "=&r" (temp) + : "r" (&sem->count) + : "memory" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r4" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + local_irq_restore(flags); + + if (temp <= 0) + __up(sem); +} + +#endif /* __KERNEL__ */ + +#endif /* _ASM_M32R_SEMAPHORE_H */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/sembuf.h 2004-09-03 00:57:17.452386096 -0700 @@ -0,0 +1,29 @@ +#ifndef _ASM_M32R_SEMBUF_H +#define _ASM_M32R_SEMBUF_H + +/* $Id$ */ + +/* orig : i386 2.4.18 */ + +/* + * The semid64_ds structure for m32r architecture. + * Note extra padding because this structure is passed back and forth + * between kernel and user space. + * + * Pad space is left for: + * - 64-bit time_t to solve y2038 problem + * - 2 miscellaneous 32-bit values + */ + +struct semid64_ds { + struct ipc64_perm sem_perm; /* permissions .. see ipc.h */ + __kernel_time_t sem_otime; /* last semop time */ + unsigned long __unused1; + __kernel_time_t sem_ctime; /* last change time */ + unsigned long __unused2; + unsigned long sem_nsems; /* no. of semaphores in array */ + unsigned long __unused3; + unsigned long __unused4; +}; + +#endif /* _ASM_M32R_SEMBUF_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/serial.h 2004-09-03 00:57:17.453385944 -0700 @@ -0,0 +1,151 @@ +#ifndef _ASM_M32R_SERIAL_H +#define _ASM_M32R_SERIAL_H + +/* $Id$ */ + +/* orig : i386 2.4.18 */ + +/* + * include/asm-m32r/serial.h + */ + +#include +#include + +/* + * This assumes you have a 1.8432 MHz clock for your UART. + * + * It'd be nice if someone built a serial card with a 24.576 MHz + * clock, since the 16550A is capable of handling a top speed of 1.5 + * megabits/second; but this requires the faster clock. + */ +#define BASE_BAUD ( 1843200 / 16 ) + +/* Standard COM flags (except for COM4, because of the 8514 problem) */ +#ifdef CONFIG_SERIAL_DETECT_IRQ +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ) +#define STD_COM4_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_AUTO_IRQ) +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) +#define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF +#endif + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define FOURPORT_FLAGS ASYNC_FOURPORT +#define ACCENT_FLAGS 0 +#define BOCA_FLAGS 0 +#define HUB6_FLAGS 0 +#define RS_TABLE_SIZE 64 +#else +#define RS_TABLE_SIZE +#endif + +#define MCA_COM_FLAGS (STD_COM_FLAGS|ASYNC_BOOT_ONLYMCA) + +/* + * The following define the access methods for the HUB6 card. All + * access is through two ports for all 24 possible chips. The card is + * selected through the high 2 bits, the port on that card with the + * "middle" 3 bits, and the register on that port with the bottom + * 3 bits. + * + * While the access port and interrupt is configurable, the default + * port locations are 0x302 for the port control register, and 0x303 + * for the data read/write register. Normally, the interrupt is at irq3 + * but can be anything from 3 to 7 inclusive. Note that using 3 will + * require disabling com2. + */ + +#define C_P(card,port) (((card)<<6|(port)<<3) + 1) + +#define STD_SERIAL_PORT_DEFNS \ + /* UART CLK PORT IRQ FLAGS */ \ + { 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS0 */ \ + { 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }, /* ttyS1 */ \ + { 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS }, /* ttyS2 */ \ + { 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS }, /* ttyS3 */ + + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define EXTRA_SERIAL_PORT_DEFNS \ + { 0, BASE_BAUD, 0x1A0, 9, FOURPORT_FLAGS }, /* ttyS4 */ \ + { 0, BASE_BAUD, 0x1A8, 9, FOURPORT_FLAGS }, /* ttyS5 */ \ + { 0, BASE_BAUD, 0x1B0, 9, FOURPORT_FLAGS }, /* ttyS6 */ \ + { 0, BASE_BAUD, 0x1B8, 9, FOURPORT_FLAGS }, /* ttyS7 */ \ + { 0, BASE_BAUD, 0x2A0, 5, FOURPORT_FLAGS }, /* ttyS8 */ \ + { 0, BASE_BAUD, 0x2A8, 5, FOURPORT_FLAGS }, /* ttyS9 */ \ + { 0, BASE_BAUD, 0x2B0, 5, FOURPORT_FLAGS }, /* ttyS10 */ \ + { 0, BASE_BAUD, 0x2B8, 5, FOURPORT_FLAGS }, /* ttyS11 */ \ + { 0, BASE_BAUD, 0x330, 4, ACCENT_FLAGS }, /* ttyS12 */ \ + { 0, BASE_BAUD, 0x338, 4, ACCENT_FLAGS }, /* ttyS13 */ \ + { 0, BASE_BAUD, 0x000, 0, 0 }, /* ttyS14 (spare) */ \ + { 0, BASE_BAUD, 0x000, 0, 0 }, /* ttyS15 (spare) */ \ + { 0, BASE_BAUD, 0x100, 12, BOCA_FLAGS }, /* ttyS16 */ \ + { 0, BASE_BAUD, 0x108, 12, BOCA_FLAGS }, /* ttyS17 */ \ + { 0, BASE_BAUD, 0x110, 12, BOCA_FLAGS }, /* ttyS18 */ \ + { 0, BASE_BAUD, 0x118, 12, BOCA_FLAGS }, /* ttyS19 */ \ + { 0, BASE_BAUD, 0x120, 12, BOCA_FLAGS }, /* ttyS20 */ \ + { 0, BASE_BAUD, 0x128, 12, BOCA_FLAGS }, /* ttyS21 */ \ + { 0, BASE_BAUD, 0x130, 12, BOCA_FLAGS }, /* ttyS22 */ \ + { 0, BASE_BAUD, 0x138, 12, BOCA_FLAGS }, /* ttyS23 */ \ + { 0, BASE_BAUD, 0x140, 12, BOCA_FLAGS }, /* ttyS24 */ \ + { 0, BASE_BAUD, 0x148, 12, BOCA_FLAGS }, /* ttyS25 */ \ + { 0, BASE_BAUD, 0x150, 12, BOCA_FLAGS }, /* ttyS26 */ \ + { 0, BASE_BAUD, 0x158, 12, BOCA_FLAGS }, /* ttyS27 */ \ + { 0, BASE_BAUD, 0x160, 12, BOCA_FLAGS }, /* ttyS28 */ \ + { 0, BASE_BAUD, 0x168, 12, BOCA_FLAGS }, /* ttyS29 */ \ + { 0, BASE_BAUD, 0x170, 12, BOCA_FLAGS }, /* ttyS30 */ \ + { 0, BASE_BAUD, 0x178, 12, BOCA_FLAGS }, /* ttyS31 */ +#else +#define EXTRA_SERIAL_PORT_DEFNS +#endif + +/* You can have up to four HUB6's in the system, but I've only + * included two cards here for a total of twelve ports. + */ +#if (defined(CONFIG_HUB6) && defined(CONFIG_SERIAL_MANY_PORTS)) +#define HUB6_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,0) }, /* ttyS32 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,1) }, /* ttyS33 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,2) }, /* ttyS34 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,3) }, /* ttyS35 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,4) }, /* ttyS36 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(0,5) }, /* ttyS37 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,0) }, /* ttyS38 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,1) }, /* ttyS39 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,2) }, /* ttyS40 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,3) }, /* ttyS41 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) }, /* ttyS42 */ \ + { 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) }, /* ttyS43 */ +#else +#define HUB6_SERIAL_PORT_DFNS +#endif + +#ifdef CONFIG_MCA +#define MCA_SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, 0x3220, 3, MCA_COM_FLAGS }, \ + { 0, BASE_BAUD, 0x3228, 3, MCA_COM_FLAGS }, \ + { 0, BASE_BAUD, 0x4220, 3, MCA_COM_FLAGS }, \ + { 0, BASE_BAUD, 0x4228, 3, MCA_COM_FLAGS }, \ + { 0, BASE_BAUD, 0x5220, 3, MCA_COM_FLAGS }, \ + { 0, BASE_BAUD, 0x5228, 3, MCA_COM_FLAGS }, +#else +#define MCA_SERIAL_PORT_DFNS +#endif + +#ifndef CONFIG_PLAT_USRV +#define SERIAL_PORT_DFNS \ + STD_SERIAL_PORT_DEFNS \ + EXTRA_SERIAL_PORT_DEFNS \ + HUB6_SERIAL_PORT_DFNS \ + MCA_SERIAL_PORT_DFNS + +#else /* CONFIG_PLAT_USRV */ + +#define SERIAL_PORT_DFNS \ + /* UART CLK PORT IRQ FLAGS */ \ + { 0, BASE_BAUD, 0x3F8, PLD_IRQ_UART0, STD_COM_FLAGS }, /* ttyS0 */ \ + { 0, BASE_BAUD, 0x2F8, PLD_IRQ_UART1, STD_COM_FLAGS }, /* ttyS1 */ +#endif /* CONFIG_PLAT_USRV */ + +#endif /* _ASM_M32R_SERIAL_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/setup.h 2004-09-03 00:57:18.740190320 -0700 @@ -0,0 +1,33 @@ +/* + * This is set up by the setup-routine at boot-time + */ +#define PARAM ((unsigned char *)empty_zero_page) + +#define MOUNT_ROOT_RDONLY (*(unsigned long *) (PARAM+0x000)) +#define RAMDISK_FLAGS (*(unsigned long *) (PARAM+0x004)) +#define ORIG_ROOT_DEV (*(unsigned long *) (PARAM+0x008)) +#define LOADER_TYPE (*(unsigned long *) (PARAM+0x00c)) +#define INITRD_START (*(unsigned long *) (PARAM+0x010)) +#define INITRD_SIZE (*(unsigned long *) (PARAM+0x014)) + +#define M32R_CPUCLK (*(unsigned long *) (PARAM+0x018)) +#define M32R_BUSCLK (*(unsigned long *) (PARAM+0x01c)) +#define M32R_TIMER_DIVIDE (*(unsigned long *) (PARAM+0x020)) + +#define COMMAND_LINE ((char *) (PARAM+0x100)) + +#define SCREEN_INFO (*(struct screen_info *) (PARAM+0x200)) + +#define COMMAND_LINE_SIZE (512) + +#define RAMDISK_IMAGE_START_MASK (0x07FF) +#define RAMDISK_PROMPT_FLAG (0x8000) +#define RAMDISK_LOAD_FLAG (0x4000) + +#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) +#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) +#define PFN_PHYS(x) ((x) << PAGE_SHIFT) + +extern unsigned long memory_start; +extern unsigned long memory_end; + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/shmbuf.h 2004-09-03 00:57:17.454385792 -0700 @@ -0,0 +1,46 @@ +#ifndef _ASM_M32R_SHMBUF_H +#define _ASM_M32R_SHMBUF_H + +/* $Id$ */ + +/* orig : i386 2.4.18 */ + +/* + * The shmid64_ds structure for M32R architecture. + * Note extra padding because this structure is passed back and forth + * between kernel and user space. + * + * Pad space is left for: + * - 64-bit time_t to solve y2038 problem + * - 2 miscellaneous 32-bit values + */ + +struct shmid64_ds { + struct ipc64_perm shm_perm; /* operation perms */ + size_t shm_segsz; /* size of segment (bytes) */ + __kernel_time_t shm_atime; /* last attach time */ + unsigned long __unused1; + __kernel_time_t shm_dtime; /* last detach time */ + unsigned long __unused2; + __kernel_time_t shm_ctime; /* last change time */ + unsigned long __unused3; + __kernel_pid_t shm_cpid; /* pid of creator */ + __kernel_pid_t shm_lpid; /* pid of last operator */ + unsigned long shm_nattch; /* no. of current attaches */ + unsigned long __unused4; + unsigned long __unused5; +}; + +struct shminfo64 { + unsigned long shmmax; + unsigned long shmmin; + unsigned long shmmni; + unsigned long shmseg; + unsigned long shmall; + unsigned long __unused1; + unsigned long __unused2; + unsigned long __unused3; + unsigned long __unused4; +}; + +#endif /* _ASM_M32R_SHMBUF_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/shmparam.h 2004-09-03 00:57:17.454385792 -0700 @@ -0,0 +1,8 @@ +#ifndef _ASM_M32R_SHMPARAM_H +#define _ASM_M32R_SHMPARAM_H + +/* $Id$ */ + +#define SHMLBA PAGE_SIZE /* attach addr a multiple of this */ + +#endif /* _ASM_M32R_SHMPARAM_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/sigcontext.h 2004-09-03 00:57:17.455385640 -0700 @@ -0,0 +1,50 @@ +#ifndef _ASM_M32R_SIGCONTEXT_H +#define _ASM_M32R_SIGCONTEXT_H + +/* $Id$ */ + +#include + +struct sigcontext { + /* CPU registers */ + /* Saved main processor registers. */ + unsigned long sc_r4; + unsigned long sc_r5; + unsigned long sc_r6; + struct pt_regs *sc_pt_regs; + unsigned long sc_r0; + unsigned long sc_r1; + unsigned long sc_r2; + unsigned long sc_r3; + unsigned long sc_r7; + unsigned long sc_r8; + unsigned long sc_r9; + unsigned long sc_r10; + unsigned long sc_r11; + unsigned long sc_r12; + + /* Saved main processor status and miscellaneous context registers. */ +#if defined(CONFIG_ISA_M32R2) && defined(CONFIG_ISA_DSP_LEVEL2) + unsigned long sc_acc0h; + unsigned long sc_acc0l; + unsigned long sc_acc1h; + unsigned long sc_acc1l; +#elif defined(CONFIG_ISA_M32R2) || defined(CONFIG_ISA_M32R) + unsigned long sc_acch; + unsigned long sc_accl; +#else +#error unknown isa configuration +#endif + unsigned long sc_psw; + unsigned long sc_bpc; /* saved PC for TRAP syscalls */ + unsigned long sc_bbpsw; + unsigned long sc_bbpc; + unsigned long sc_spu; /* saved user stack */ + unsigned long sc_fp; + unsigned long sc_lr; /* saved PC for JL syscalls */ + unsigned long sc_spi; /* saved kernel stack */ + + unsigned long oldmask; +}; + +#endif /* _ASM_M32R_SIGCONTEXT_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/siginfo.h 2004-09-03 00:57:17.455385640 -0700 @@ -0,0 +1,8 @@ +#ifndef _M32R_SIGINFO_H +#define _M32R_SIGINFO_H + +/* $Id$ */ + +#include + +#endif /* _M32R_SIGINFO_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/signal.h 2004-09-03 00:57:18.741190168 -0700 @@ -0,0 +1,200 @@ +#ifndef _ASM_M32R_SIGNAL_H +#define _ASM_M32R_SIGNAL_H + +/* $Id$ */ + +/* orig : i386 2.4.18 */ + +#include +#include +#include +#include + +/* Avoid too many header ordering problems. */ +struct siginfo; + +#ifdef __KERNEL__ +/* Most things should be clean enough to redefine this at will, if care + is taken to make libc match. */ + +#define _NSIG 64 +#define _NSIG_BPW 32 +#define _NSIG_WORDS (_NSIG / _NSIG_BPW) + +typedef unsigned long old_sigset_t; /* at least 32 bits */ + +typedef struct { + unsigned long sig[_NSIG_WORDS]; +} sigset_t; + +#else +/* Here we must cater to libcs that poke about in kernel headers. */ + +#define NSIG 32 +typedef unsigned long sigset_t; + +#endif /* __KERNEL__ */ + +#define SIGHUP 1 +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGABRT 6 +#define SIGIOT 6 +#define SIGBUS 7 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGUSR1 10 +#define SIGSEGV 11 +#define SIGUSR2 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGTERM 15 +#define SIGSTKFLT 16 +#define SIGCHLD 17 +#define SIGCONT 18 +#define SIGSTOP 19 +#define SIGTSTP 20 +#define SIGTTIN 21 +#define SIGTTOU 22 +#define SIGURG 23 +#define SIGXCPU 24 +#define SIGXFSZ 25 +#define SIGVTALRM 26 +#define SIGPROF 27 +#define SIGWINCH 28 +#define SIGIO 29 +#define SIGPOLL SIGIO +/* +#define SIGLOST 29 +*/ +#define SIGPWR 30 +#define SIGSYS 31 +#define SIGUNUSED 31 + +/* These should not be considered constants from userland. */ +#define SIGRTMIN 32 +#define SIGRTMAX _NSIG + +/* + * SA_FLAGS values: + * + * SA_ONSTACK indicates that a registered stack_t will be used. + * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the + * SA_RESTART flag to get restarting signals (which were the default long ago) + * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. + * SA_RESETHAND clears the handler when the signal is delivered. + * SA_NOCLDWAIT flag on SIGCHLD to inhibit zombies. + * SA_NODEFER prevents the current signal from being masked in the handler. + * + * SA_ONESHOT and SA_NOMASK are the historical Linux names for the Single + * Unix names RESETHAND and NODEFER respectively. + */ +#define SA_NOCLDSTOP 0x00000001u +#define SA_NOCLDWAIT 0x00000002u +#define SA_SIGINFO 0x00000004u +#define SA_ONSTACK 0x08000000u +#define SA_RESTART 0x10000000u +#define SA_NODEFER 0x40000000u +#define SA_RESETHAND 0x80000000u + +#define SA_NOMASK SA_NODEFER +#define SA_ONESHOT SA_RESETHAND +#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ + +#define SA_RESTORER 0x04000000 + +/* + * sigaltstack controls + */ +#define SS_ONSTACK 1 +#define SS_DISABLE 2 + +#define MINSIGSTKSZ 2048 +#define SIGSTKSZ 8192 + +#ifdef __KERNEL__ + +/* + * These values of sa_flags are used only by the kernel as part of the + * irq handling routines. + * + * SA_INTERRUPT is also used by the irq handling routines. + * SA_SHIRQ is for shared interrupt support on PCI and EISA. + */ +#define SA_PROBE SA_ONESHOT +#define SA_SAMPLE_RANDOM SA_RESTART +#define SA_SHIRQ 0x04000000 +#endif + +#define SIG_BLOCK 0 /* for blocking signals */ +#define SIG_UNBLOCK 1 /* for unblocking signals */ +#define SIG_SETMASK 2 /* for setting the signal mask */ + +/* Type of a signal handler. */ +typedef void __signalfn_t(int); +typedef __signalfn_t __user *__sighandler_t; + +typedef void __restorefn_t(void); +typedef __restorefn_t __user *__sigrestore_t; + +#define SIG_DFL ((__sighandler_t)0) /* default signal handling */ +#define SIG_IGN ((__sighandler_t)1) /* ignore signal */ +#define SIG_ERR ((__sighandler_t)-1) /* error return from signal */ + +#ifdef __KERNEL__ +struct old_sigaction { + __sighandler_t sa_handler; + old_sigset_t sa_mask; + unsigned long sa_flags; + __sigrestore_t sa_restorer; +}; + +struct sigaction { + __sighandler_t sa_handler; + unsigned long sa_flags; + __sigrestore_t sa_restorer; + sigset_t sa_mask; /* mask last for extensibility */ +}; + +struct k_sigaction { + struct sigaction sa; +}; +#else +/* Here we must cater to libcs that poke about in kernel headers. */ + +struct sigaction { + union { + __sighandler_t _sa_handler; + void (*_sa_sigaction)(int, struct siginfo *, void *); + } _u; + sigset_t sa_mask; + unsigned long sa_flags; + void (*sa_restorer)(void); +}; + +#define sa_handler _u._sa_handler +#define sa_sigaction _u._sa_sigaction + +#endif /* __KERNEL__ */ + +typedef struct sigaltstack { + void __user *ss_sp; + int ss_flags; + size_t ss_size; +} stack_t; + +#ifdef __KERNEL__ +#include + +#undef __HAVE_ARCH_SIG_BITOPS + +struct pt_regs; +extern int FASTCALL(do_signal(struct pt_regs *regs, sigset_t *oldset)); + +#define ptrace_signal_deliver(regs, cookie) do { } while (0) + +#endif /* __KERNEL__ */ + +#endif /* _ASM_M32R_SIGNAL_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/smp.h 2004-09-03 00:57:18.741190168 -0700 @@ -0,0 +1,119 @@ +#ifndef _ASM_M32R_SMP_H +#define _ASM_M32R_SMP_H + +/* $Id$ */ + +#include + +#ifdef CONFIG_SMP +#ifndef __ASSEMBLY__ + +#include +#include +#include +#include + +#define PHYSID_ARRAY_SIZE 1 + +struct physid_mask +{ + unsigned long mask[PHYSID_ARRAY_SIZE]; +}; + +typedef struct physid_mask physid_mask_t; + +#define physid_set(physid, map) set_bit(physid, (map).mask) +#define physid_clear(physid, map) clear_bit(physid, (map).mask) +#define physid_isset(physid, map) test_bit(physid, (map).mask) +#define physid_test_and_set(physid, map) test_and_set_bit(physid, (map).mask) + +#define physids_and(dst, src1, src2) bitmap_and((dst).mask, (src1).mask, (src2).mask, MAX_APICS) +#define physids_or(dst, src1, src2) bitmap_or((dst).mask, (src1).mask, (src2).mask, MAX_APICS) +#define physids_clear(map) bitmap_zero((map).mask, MAX_APICS) +#define physids_complement(dst, src) bitmap_complement((dst).mask,(src).mask, MAX_APICS) +#define physids_empty(map) bitmap_empty((map).mask, MAX_APICS) +#define physids_equal(map1, map2) bitmap_equal((map1).mask, (map2).mask, MAX_APICS) +#define physids_weight(map) bitmap_weight((map).mask, MAX_APICS) +#define physids_shift_right(d, s, n) bitmap_shift_right((d).mask, (s).mask, n, MAX_APICS) +#define physids_shift_left(d, s, n) bitmap_shift_left((d).mask, (s).mask, n, MAX_APICS) +#define physids_coerce(map) ((map).mask[0]) + +#define physids_promote(physids) \ + ({ \ + physid_mask_t __physid_mask = PHYSID_MASK_NONE; \ + __physid_mask.mask[0] = physids; \ + __physid_mask; \ + }) + +#define physid_mask_of_physid(physid) \ + ({ \ + physid_mask_t __physid_mask = PHYSID_MASK_NONE; \ + physid_set(physid, __physid_mask); \ + __physid_mask; \ + }) + +#define PHYSID_MASK_ALL { {[0 ... PHYSID_ARRAY_SIZE-1] = ~0UL} } +#define PHYSID_MASK_NONE { {[0 ... PHYSID_ARRAY_SIZE-1] = 0UL} } + +extern physid_mask_t phys_cpu_present_map; + +/* + * Some lowlevel functions might want to know about + * the real CPU ID <-> CPU # mapping. + */ +extern volatile int physid_2_cpu[NR_CPUS]; +extern volatile int cpu_2_physid[NR_CPUS]; +#define physid_to_cpu(physid) physid_2_cpu[physid] +#define cpu_to_physid(cpu_id) cpu_2_physid[cpu_id] + +#define smp_processor_id() (current_thread_info()->cpu) + +extern cpumask_t cpu_callout_map; +#define cpu_possible_map cpu_callout_map + +static __inline__ int hard_smp_processor_id(void) +{ + return (int)*(volatile long *)M32R_CPUID_PORTL; +} + +static __inline__ int cpu_logical_map(int cpu) +{ + return cpu; +} + +static __inline__ int cpu_number_map(int cpu) +{ + return cpu; +} + +static __inline__ unsigned int num_booting_cpus(void) +{ + return cpus_weight(cpu_callout_map); +} + +extern void smp_send_timer(void); +extern void calibrate_delay(void); +extern unsigned long send_IPI_mask_phys(cpumask_t, int, int); + +#endif /* not __ASSEMBLY__ */ + +#define NO_PROC_ID (0xff) /* No processor magic marker */ + +#define PROC_CHANGE_PENALTY (15) /* Schedule penalty */ + +/* + * M32R-mp IPI + */ +#define RESCHEDULE_IPI (M32R_IRQ_IPI0-M32R_IRQ_IPI0) +#define INVALIDATE_TLB_IPI (M32R_IRQ_IPI1-M32R_IRQ_IPI0) +#define CALL_FUNCTION_IPI (M32R_IRQ_IPI2-M32R_IRQ_IPI0) +#define LOCAL_TIMER_IPI (M32R_IRQ_IPI3-M32R_IRQ_IPI0) +#define INVALIDATE_CACHE_IPI (M32R_IRQ_IPI4-M32R_IRQ_IPI0) +#define CPU_BOOT_IPI (M32R_IRQ_IPI5-M32R_IRQ_IPI0) + +#define IPI_SHIFT (0) +#define NR_IPIS (8) + +#endif /* CONFIG_SMP */ + +#endif /* _ASM_M32R_SMP_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/socket.h 2004-09-03 00:57:17.457385336 -0700 @@ -0,0 +1,70 @@ +#ifndef _ASM_M32R_SOCKET_H +#define _ASM_M32R_SOCKET_H + +/* $Id$ */ + +/* orig : i386 2.4.18 */ + +#include + +/* For setsockoptions(2) */ +#define SOL_SOCKET 1 + +#define SO_DEBUG 1 +#define SO_REUSEADDR 2 +#define SO_TYPE 3 +#define SO_ERROR 4 +#define SO_DONTROUTE 5 +#define SO_BROADCAST 6 +#define SO_SNDBUF 7 +#define SO_RCVBUF 8 +#define SO_KEEPALIVE 9 +#define SO_OOBINLINE 10 +#define SO_NO_CHECK 11 +#define SO_PRIORITY 12 +#define SO_LINGER 13 +#define SO_BSDCOMPAT 14 +/* To add :#define SO_REUSEPORT 15 */ +#define SO_PASSCRED 16 +#define SO_PEERCRED 17 +#define SO_RCVLOWAT 18 +#define SO_SNDLOWAT 19 +#define SO_RCVTIMEO 20 +#define SO_SNDTIMEO 21 + +/* Security levels - as per NRL IPv6 - don't actually do anything */ +#define SO_SECURITY_AUTHENTICATION 22 +#define SO_SECURITY_ENCRYPTION_TRANSPORT 23 +#define SO_SECURITY_ENCRYPTION_NETWORK 24 + +#define SO_BINDTODEVICE 25 + +/* Socket filtering */ +#define SO_ATTACH_FILTER 26 +#define SO_DETACH_FILTER 27 + +#define SO_PEERNAME 28 +#define SO_TIMESTAMP 29 +#define SCM_TIMESTAMP SO_TIMESTAMP + +#define SO_ACCEPTCONN 30 + +#define SO_PEERSEC 31 + +/* Nasty libc5 fixup - bletch */ +#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2) +/* Socket types. */ +#define SOCK_STREAM 1 /* stream (connection) socket */ +#define SOCK_DGRAM 2 /* datagram (conn.less) socket */ +#define SOCK_RAW 3 /* raw socket */ +#define SOCK_RDM 4 /* reliably-delivered message */ +#define SOCK_SEQPACKET 5 /* sequential packet socket */ +#define SOCK_PACKET 10 /* linux specific way of */ + /* getting packets at the dev */ + /* level. For writing rarp and */ + /* other similar things on the */ + /* user level. */ +#define SOCK_MAX (SOCK_PACKET+1) +#endif + +#endif /* _ASM_M32R_SOCKET_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/sockios.h 2004-09-03 00:57:17.457385336 -0700 @@ -0,0 +1,14 @@ +#ifndef _ASM_M32R_SOCKIOS_H +#define _ASM_M32R_SOCKIOS_H + +/* $Id$ */ + +/* Socket-level I/O control calls. */ +#define FIOSETOWN 0x8901 +#define SIOCSPGRP 0x8902 +#define FIOGETOWN 0x8903 +#define SIOCGPGRP 0x8904 +#define SIOCATMARK 0x8905 +#define SIOCGSTAMP 0x8906 /* Get stamp */ + +#endif /* _ASM_M32R_SOCKIOS_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/spinlock.h 2004-09-03 00:57:17.459385032 -0700 @@ -0,0 +1,371 @@ +#ifndef _ASM_M32R_SPINLOCK_H +#define _ASM_M32R_SPINLOCK_H + +/* $Id$ */ + +/* + * linux/include/asm-m32r/spinlock.h + * orig : i386 2.4.10 + * + * M32R version: + * Copyright (C) 2001, 2002 Hitoshi Yamamoto + */ + +#include /* CONFIG_DEBUG_SPINLOCK, CONFIG_SMP */ +#include +#include +#include + +extern int printk(const char * fmt, ...) + __attribute__ ((format (printf, 1, 2))); + +#define RW_LOCK_BIAS 0x01000000 +#define RW_LOCK_BIAS_STR "0x01000000" + +/* It seems that people are forgetting to + * initialize their spinlocks properly, tsk tsk. + * Remember to turn this off in 2.4. -ben + */ +#if defined(CONFIG_DEBUG_SPINLOCK) +#define SPINLOCK_DEBUG 1 +#else +#define SPINLOCK_DEBUG 0 +#endif + +/* + * Your basic SMP spinlocks, allowing only a single CPU anywhere + */ + +typedef struct { + volatile int lock; +#if SPINLOCK_DEBUG + unsigned magic; +#endif +} spinlock_t; + +#define SPINLOCK_MAGIC 0xdead4ead + +#if SPINLOCK_DEBUG +#define SPINLOCK_MAGIC_INIT , SPINLOCK_MAGIC +#else +#define SPINLOCK_MAGIC_INIT /* */ +#endif + +#define SPIN_LOCK_UNLOCKED (spinlock_t) { 1 SPINLOCK_MAGIC_INIT } + +#define spin_lock_init(x) do { *(x) = SPIN_LOCK_UNLOCKED; } while(0) + +/* + * Simple spin lock operations. There are two variants, one clears IRQ's + * on the local processor, one does not. + * + * We make no fairness assumptions. They have a cost. + */ + +#define spin_is_locked(x) (*(volatile int *)(&(x)->lock) <= 0) +#define spin_unlock_wait(x) do { barrier(); } while(spin_is_locked(x)) +#define _raw_spin_lock_flags(lock, flags) _raw_spin_lock(lock) + +/* + * This works. Despite all the confusion. + */ + +/*======================================================================* + * Try spin lock + *======================================================================* + * Argument: + * arg0: lock + * Return value: + * =1: Success + * =0: Failure + *======================================================================*/ +static __inline__ int _raw_spin_trylock(spinlock_t *lock) +{ + int oldval; + + /* + * lock->lock : =1 : unlock + * : <=0 : lock + * { + * oldval = lock->lock; <--+ need atomic operation + * lock->lock = 0; <--+ + * } + */ + __asm__ __volatile__ ( + "# spin_trylock \n\t" + "ldi r4, #0; \n\t" + "mvfc r5, psw; \n\t" + "clrpsw #0x40 -> nop; \n\t" + DCACHE_CLEAR("%0", "r6", "%1") + "lock %0, @%1; \n\t" + "unlock r4, @%1; \n\t" + "mvtc r5, psw; \n\t" + : "=&r" (oldval) + : "r" (&lock->lock) + : "memory", "r4", "r5" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r6" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + + return (oldval > 0); +} + +static __inline__ void _raw_spin_lock(spinlock_t *lock) +{ +#if SPINLOCK_DEBUG + __label__ here; +here: + if (lock->magic != SPINLOCK_MAGIC) { + printk("eip: %p\n", &&here); + BUG(); + } +#endif + /* + * lock->lock : =1 : unlock + * : <=0 : lock + * + * for ( ; ; ) { + * lock->lock -= 1; <-- need atomic operation + * if (lock->lock == 0) break; + * for ( ; lock->lock <= 0 ; ); + * } + */ + __asm__ __volatile__ ( + "# spin_lock \n\t" + ".fillinsn \n" + "1: \n\t" + "mvfc r5, psw; \n\t" + "clrpsw #0x40 -> nop; \n\t" + DCACHE_CLEAR("r4", "r6", "%0") + "lock r4, @%0; \n\t" + "addi r4, #-1; \n\t" + "unlock r4, @%0; \n\t" + "mvtc r5, psw; \n\t" + "bltz r4, 2f; \n\t" + LOCK_SECTION_START(".balign 4 \n\t") + ".fillinsn \n" + "2: \n\t" + "ld r4, @%0; \n\t" + "bgtz r4, 1b; \n\t" + "bra 2b; \n\t" + LOCK_SECTION_END + : /* no outputs */ + : "r" (&lock->lock) + : "memory", "r4", "r5" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r6" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); +} + +static __inline__ void _raw_spin_unlock(spinlock_t *lock) +{ +#if SPINLOCK_DEBUG + BUG_ON(lock->magic != SPINLOCK_MAGIC); + BUG_ON(!spin_is_locked(lock)); +#endif + mb(); + lock->lock = 1; +} + +/* + * Read-write spinlocks, allowing multiple readers + * but only one writer. + * + * NOTE! it is quite common to have readers in interrupts + * but no interrupt writers. For those circumstances we + * can "mix" irq-safe locks - any writer needs to get a + * irq-safe write-lock, but readers can get non-irqsafe + * read-locks. + */ +typedef struct { + volatile int lock; +#if SPINLOCK_DEBUG + unsigned magic; +#endif +} rwlock_t; + +#define RWLOCK_MAGIC 0xdeaf1eed + +#if SPINLOCK_DEBUG +#define RWLOCK_MAGIC_INIT , RWLOCK_MAGIC +#else +#define RWLOCK_MAGIC_INIT /* */ +#endif + +#define RW_LOCK_UNLOCKED (rwlock_t) { RW_LOCK_BIAS RWLOCK_MAGIC_INIT } + +#define rwlock_init(x) do { *(x) = RW_LOCK_UNLOCKED; } while(0) + +#define rwlock_is_locked(x) ((x)->lock != RW_LOCK_BIAS) + +/* + * On x86, we implement read-write locks as a 32-bit counter + * with the high bit (sign) being the "contended" bit. + * + * The inline assembly is non-obvious. Think about it. + * + * Changed to use the same technique as rw semaphores. See + * semaphore.h for details. -ben + */ +/* the spinlock helpers are in arch/i386/kernel/semaphore.c */ + +static __inline__ void _raw_read_lock(rwlock_t *rw) +{ +#if SPINLOCK_DEBUG + BUG_ON(rw->magic != RWLOCK_MAGIC); +#endif + /* + * rw->lock : >0 : unlock + * : <=0 : lock + * + * for ( ; ; ) { + * rw->lock -= 1; <-- need atomic operation + * if (rw->lock >= 0) break; + * rw->lock += 1; <-- need atomic operation + * for ( ; rw->lock <= 0 ; ); + * } + */ + __asm__ __volatile__ ( + "# read_lock \n\t" + ".fillinsn \n" + "1: \n\t" + "mvfc r5, psw; \n\t" + "clrpsw #0x40 -> nop; \n\t" + DCACHE_CLEAR("r4", "r6", "%0") + "lock r4, @%0; \n\t" + "addi r4, #-1; \n\t" + "unlock r4, @%0; \n\t" + "mvtc r5, psw; \n\t" + "bltz r4, 2f; \n\t" + LOCK_SECTION_START(".balign 4 \n\t") + ".fillinsn \n" + "2: \n\t" + "clrpsw #0x40 -> nop; \n\t" + DCACHE_CLEAR("r4", "r6", "%0") + "lock r4, @%0; \n\t" + "addi r4, #1; \n\t" + "unlock r4, @%0; \n\t" + "mvtc r5, psw; \n\t" + ".fillinsn \n" + "3: \n\t" + "ld r4, @%0; \n\t" + "bgtz r4, 1b; \n\t" + "bra 3b; \n\t" + LOCK_SECTION_END + : /* no outputs */ + : "r" (&rw->lock) + : "memory", "r4", "r5" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r6" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); +} + +static __inline__ void _raw_write_lock(rwlock_t *rw) +{ +#if SPINLOCK_DEBUG + BUG_ON(rw->magic != RWLOCK_MAGIC); +#endif + /* + * rw->lock : =RW_LOCK_BIAS_STR : unlock + * : !=RW_LOCK_BIAS_STR : lock + * + * for ( ; ; ) { + * rw->lock -= RW_LOCK_BIAS_STR; <-- need atomic operation + * if (rw->lock == 0) break; + * rw->lock += RW_LOCK_BIAS_STR; <-- need atomic operation + * for ( ; rw->lock != RW_LOCK_BIAS_STR ; ) ; + * } + */ + __asm__ __volatile__ ( + "# write_lock \n\t" + "seth r5, #high(" RW_LOCK_BIAS_STR "); \n\t" + "or3 r5, r5, #low(" RW_LOCK_BIAS_STR "); \n\t" + ".fillinsn \n" + "1: \n\t" + "mvfc r6, psw; \n\t" + "clrpsw #0x40 -> nop; \n\t" + DCACHE_CLEAR("r4", "r7", "%0") + "lock r4, @%0; \n\t" + "sub r4, r5; \n\t" + "unlock r4, @%0; \n\t" + "mvtc r6, psw; \n\t" + "bnez r4, 2f; \n\t" + LOCK_SECTION_START(".balign 4 \n\t") + ".fillinsn \n" + "2: \n\t" + "clrpsw #0x40 -> nop; \n\t" + DCACHE_CLEAR("r4", "r7", "%0") + "lock r4, @%0; \n\t" + "add r4, r5; \n\t" + "unlock r4, @%0; \n\t" + "mvtc r6, psw; \n\t" + ".fillinsn \n" + "3: \n\t" + "ld r4, @%0; \n\t" + "beq r4, r5, 1b; \n\t" + "bra 3b; \n\t" + LOCK_SECTION_END + : /* no outputs */ + : "r" (&rw->lock) + : "memory", "r4", "r5", "r6" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r7" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); +} + +static __inline__ void _raw_read_unlock(rwlock_t *rw) +{ + __asm__ __volatile__ ( + "# read_unlock \n\t" + "mvfc r5, psw; \n\t" + "clrpsw #0x40 -> nop; \n\t" + DCACHE_CLEAR("r4", "r6", "%0") + "lock r4, @%0; \n\t" + "addi r4, #1; \n\t" + "unlock r4, @%0; \n\t" + "mvtc r5, psw; \n\t" + : /* no outputs */ + : "r" (&rw->lock) + : "memory", "r4", "r5" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r6" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); +} + +static __inline__ void _raw_write_unlock(rwlock_t *rw) +{ + __asm__ __volatile__ ( + "# write_unlock \n\t" + "seth r5, #high(" RW_LOCK_BIAS_STR "); \n\t" + "or3 r5, r5, #low(" RW_LOCK_BIAS_STR "); \n\t" + "mvfc r6, psw; \n\t" + "clrpsw #0x40 -> nop; \n\t" + DCACHE_CLEAR("r4", "r7", "%0") + "lock r4, @%0; \n\t" + "add r4, r5; \n\t" + "unlock r4, @%0; \n\t" + "mvtc r6, psw; \n\t" + : /* no outputs */ + : "r" (&rw->lock) + : "memory", "r4", "r5", "r6" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r7" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); +} + +static __inline__ int _raw_write_trylock(rwlock_t *lock) +{ + atomic_t *count = (atomic_t *)lock; + if (atomic_sub_and_test(RW_LOCK_BIAS, count)) + return 1; + atomic_add(RW_LOCK_BIAS, count); + return 0; +} + +#endif /* _ASM_M32R_SPINLOCK_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/statfs.h 2004-09-03 00:57:17.459385032 -0700 @@ -0,0 +1,6 @@ +#ifndef _ASM_M32R_STATFS_H +#define _ASM_M32R_STATFS_H + +#include + +#endif /* _ASM_M32R_STATFS_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/stat.h 2004-09-03 00:57:17.460384880 -0700 @@ -0,0 +1,91 @@ +#ifndef _ASM_M32R_STAT_H +#define _ASM_M32R_STAT_H + +/* $Id$ */ + +/* orig : i386 2.4.18 */ + +#include + +struct __old_kernel_stat { + unsigned short st_dev; + unsigned short st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + unsigned long st_size; + unsigned long st_atime; + unsigned long st_mtime; + unsigned long st_ctime; +}; + +#define STAT_HAVE_NSEC 1 + +struct stat { + unsigned short st_dev; + unsigned short __pad1; + unsigned long st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + unsigned short __pad2; + unsigned long st_size; + unsigned long st_blksize; + unsigned long st_blocks; + unsigned long st_atime; + unsigned long st_atime_nsec; + unsigned long st_mtime; + unsigned long st_mtime_nsec; + unsigned long st_ctime; + unsigned long st_ctime_nsec; + unsigned long __unused4; + unsigned long __unused5; +}; + +/* This matches struct stat64 in glibc2.1, hence the absolutely + * insane amounts of padding around dev_t's. + */ +struct stat64 { + unsigned long long st_dev; + unsigned char __pad0[4]; +#define STAT64_HAS_BROKEN_ST_INO + unsigned long __st_ino; + + unsigned int st_mode; + unsigned int st_nlink; + + unsigned long st_uid; + unsigned long st_gid; + + unsigned long long st_rdev; + unsigned char __pad3[4]; + + long long st_size; + unsigned long st_blksize; + +#if defined(__BIG_ENDIAN) + unsigned long __pad4; /* future possible st_blocks high bits */ + unsigned long st_blocks; /* Number 512-byte blocks allocated. */ +#elif defined(__LITTLE_ENDIAN) + unsigned long st_blocks; /* Number 512-byte blocks allocated. */ + unsigned long __pad4; /* future possible st_blocks high bits */ +#else +#error no endian defined +#endif + unsigned long st_atime; + unsigned long st_atime_nsec; + + unsigned long st_mtime; + unsigned long st_mtime_nsec; + + unsigned long st_ctime; + unsigned long st_ctime_nsec; + + unsigned long long st_ino; +}; + +#endif /* _ASM_M32R_STAT_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/string.h 2004-09-03 00:57:17.460384880 -0700 @@ -0,0 +1,15 @@ +#ifndef _ASM_M32R_STRING_H +#define _ASM_M32R_STRING_H + +/* $Id$ */ + +#define __HAVE_ARCH_STRLEN +extern size_t strlen(const char * s); + +#define __HAVE_ARCH_MEMCPY +extern void *memcpy(void *__to, __const__ void *__from, size_t __n); + +#define __HAVE_ARCH_MEMSET +extern void *memset(void *__s, int __c, size_t __count); + +#endif /* _ASM_M32R_STRING_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/syscall.h 2004-09-03 00:57:17.460384880 -0700 @@ -0,0 +1,11 @@ +#ifndef _ASM_M32R_SYSCALL_H +#define _ASM_M32R_SYSCALL_H + +/* $Id$ */ + +/* Definitions for the system call vector. */ +#define SYSCALL_VECTOR "2" +#define SYSCALL_VECTOR_ADDRESS "0xa0" + +#endif /* _ASM_M32R_SYSCALL_H */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/system.h 2004-09-03 00:57:17.462384576 -0700 @@ -0,0 +1,301 @@ +#ifndef _ASM_M32R_SYSTEM_H +#define _ASM_M32R_SYSTEM_H + +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2001 by Hiroyuki Kondo, Hirokazu Takata, and Hitoshi Yamamoto + */ + +#include + +#ifdef __KERNEL__ + +/* + * switch_to(prev, next) should switch from task `prev' to `next' + * `prev' will never be the same as `next'. + * + * `next' and `prev' should be struct task_struct, but it isn't always defined + */ + +#ifndef CONFIG_SMP +#define prepare_to_switch() do { } while(0) +#endif /* not CONFIG_SMP */ + +#define switch_to(prev, next, last) do { \ + register unsigned long arg0 __asm__ ("r0") = (unsigned long)prev; \ + register unsigned long arg1 __asm__ ("r1") = (unsigned long)next; \ + register unsigned long *oldsp __asm__ ("r2") = &(prev->thread.sp); \ + register unsigned long *newsp __asm__ ("r3") = &(next->thread.sp); \ + register unsigned long *oldlr __asm__ ("r4") = &(prev->thread.lr); \ + register unsigned long *newlr __asm__ ("r5") = &(next->thread.lr); \ + register struct task_struct *__last __asm__ ("r6"); \ + __asm__ __volatile__ ( \ + "st r8, @-r15 \n\t" \ + "st r9, @-r15 \n\t" \ + "st r10, @-r15 \n\t" \ + "st r11, @-r15 \n\t" \ + "st r12, @-r15 \n\t" \ + "st r13, @-r15 \n\t" \ + "st r14, @-r15 \n\t" \ + "seth r14, #high(1f) \n\t" \ + "or3 r14, r14, #low(1f) \n\t" \ + "st r14, @r4 ; store old LR \n\t" \ + "st r15, @r2 ; store old SP \n\t" \ + "ld r15, @r3 ; load new SP \n\t" \ + "st r0, @-r15 ; store 'prev' onto new stack \n\t" \ + "ld r14, @r5 ; load new LR \n\t" \ + "jmp r14 \n\t" \ + ".fillinsn \n " \ + "1: \n\t" \ + "ld r6, @r15+ ; load 'prev' from new stack \n\t" \ + "ld r14, @r15+ \n\t" \ + "ld r13, @r15+ \n\t" \ + "ld r12, @r15+ \n\t" \ + "ld r11, @r15+ \n\t" \ + "ld r10, @r15+ \n\t" \ + "ld r9, @r15+ \n\t" \ + "ld r8, @r15+ \n\t" \ + : "=&r" (__last) \ + : "r" (arg0), "r" (arg1), "r" (oldsp), "r" (newsp), \ + "r" (oldlr), "r" (newlr) \ + : "memory" \ + ); \ + last = __last; \ +} while(0) + +/* Interrupt Control */ +#if !defined(CONFIG_CHIP_M32102) +#define local_irq_enable() \ + __asm__ __volatile__ ("setpsw #0x40 -> nop": : :"memory") +#define local_irq_disable() \ + __asm__ __volatile__ ("clrpsw #0x40 -> nop": : :"memory") +#else /* CONFIG_CHIP_M32102 */ +static __inline__ void local_irq_enable(void) +{ + unsigned long tmpreg; + __asm__ __volatile__( + "mvfc %0, psw; \n\t" + "or3 %0, %0, #0x0040; \n\t" + "mvtc %0, psw; \n\t" + : "=&r" (tmpreg) : : "cbit", "memory"); +} + +static __inline__ void local_irq_disable(void) +{ + unsigned long tmpreg0, tmpreg1; + __asm__ __volatile__( + "ld24 %0, #0 ; Use 32-bit insn. \n\t" + "mvfc %1, psw ; No interrupt can be accepted here. \n\t" + "mvtc %0, psw \n\t" + "and3 %0, %1, #0xffbf \n\t" + "mvtc %0, psw \n\t" + : "=&r" (tmpreg0), "=&r" (tmpreg1) : : "cbit", "memory"); +} +#endif /* CONFIG_CHIP_M32102 */ + +#define local_save_flags(x) \ + __asm__ __volatile__("mvfc %0,psw" : "=r"(x) : /* no input */) + +#define local_irq_restore(x) \ + __asm__ __volatile__("mvtc %0,psw" : /* no outputs */ \ + : "r" (x) : "cbit", "memory") + +#if !defined(CONFIG_CHIP_M32102) +#define local_irq_save(x) \ + __asm__ __volatile__( \ + "mvfc %0, psw; \n\t" \ + "clrpsw #0x40 -> nop; \n\t" \ + : "=r" (x) : /* no input */ : "memory") +#else /* CONFIG_CHIP_M32102 */ +#define local_irq_save(x) \ + ({ \ + unsigned long tmpreg; \ + __asm__ __volatile__( \ + "ld24 %1, #0 \n\t" \ + "mvfc %0, psw \n\t" \ + "mvtc %1, psw \n\t" \ + "and3 %1, %0, #0xffbf \n\t" \ + "mvtc %1, psw \n\t" \ + : "=r" (x), "=&r" (tmpreg) \ + : : "cbit", "memory"); \ + }) +#endif /* CONFIG_CHIP_M32102 */ + +#define irqs_disabled() \ + ({ \ + unsigned long flags; \ + local_save_flags(flags); \ + !(flags & 0x40); \ + }) + +#endif /* __KERNEL__ */ + +#define nop() __asm__ __volatile__ ("nop" : : ) + +#define xchg(ptr,x) \ + ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr)))) + +#define tas(ptr) (xchg((ptr),1)) + +#ifdef CONFIG_SMP +extern void __xchg_called_with_bad_pointer(void); +#endif + +#ifdef CONFIG_CHIP_M32700_TS1 +#define DCACHE_CLEAR(reg0, reg1, addr) \ + "seth "reg1", #high(dcache_dummy); \n\t" \ + "or3 "reg1", "reg1", #low(dcache_dummy); \n\t" \ + "lock "reg0", @"reg1"; \n\t" \ + "add3 "reg0", "addr", #0x1000; \n\t" \ + "ld "reg0", @"reg0"; \n\t" \ + "add3 "reg0", "addr", #0x2000; \n\t" \ + "ld "reg0", @"reg0"; \n\t" \ + "unlock "reg0", @"reg1"; \n\t" + /* FIXME: This workaround code cannot handle kenrel modules + * correctly under SMP environment. + */ +#else /* CONFIG_CHIP_M32700_TS1 */ +#define DCACHE_CLEAR(reg0, reg1, addr) +#endif /* CONFIG_CHIP_M32700_TS1 */ + +static __inline__ unsigned long __xchg(unsigned long x, volatile void * ptr, + int size) +{ + unsigned long flags; + unsigned long tmp = 0; + + local_irq_save(flags); + + switch (size) { +#ifndef CONFIG_SMP + case 1: + __asm__ __volatile__ ( + "ldb %0, @%2 \n\t" + "stb %1, @%2 \n\t" + : "=&r" (tmp) : "r" (x), "r" (ptr) : "memory"); + break; + case 2: + __asm__ __volatile__ ( + "ldh %0, @%2 \n\t" + "sth %1, @%2 \n\t" + : "=&r" (tmp) : "r" (x), "r" (ptr) : "memory"); + break; + case 4: + __asm__ __volatile__ ( + "ld %0, @%2 \n\t" + "st %1, @%2 \n\t" + : "=&r" (tmp) : "r" (x), "r" (ptr) : "memory"); + break; +#else /* CONFIG_SMP */ + case 4: + __asm__ __volatile__ ( + DCACHE_CLEAR("%0", "r4", "%2") + "lock %0, @%2; \n\t" + "unlock %1, @%2; \n\t" + : "=&r" (tmp) : "r" (x), "r" (ptr) + : "memory" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r4" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + break; + default: + __xchg_called_with_bad_pointer(); +#endif /* CONFIG_SMP */ + } + + local_irq_restore(flags); + + return (tmp); +} + +/* + * Memory barrier. + * + * mb() prevents loads and stores being reordered across this point. + * rmb() prevents loads being reordered across this point. + * wmb() prevents stores being reordered across this point. + */ +#if 0 +#define mb() __asm__ __volatile__ ("push r0; \n\t pop r0;" : : : "memory") +#else +#define mb() __asm__ __volatile__ ("" : : : "memory") +#endif +#define rmb() mb() +#define wmb() mb() + +/** + * read_barrier_depends - Flush all pending reads that subsequents reads + * depend on. + * + * No data-dependent reads from memory-like regions are ever reordered + * over this barrier. All reads preceding this primitive are guaranteed + * to access memory (but not necessarily other CPUs' caches) before any + * reads following this primitive that depend on the data return by + * any of the preceding reads. This primitive is much lighter weight than + * rmb() on most CPUs, and is never heavier weight than is + * rmb(). + * + * These ordering constraints are respected by both the local CPU + * and the compiler. + * + * Ordering is not guaranteed by anything other than these primitives, + * not even by data dependencies. See the documentation for + * memory_barrier() for examples and URLs to more information. + * + * For example, the following code would force ordering (the initial + * value of "a" is zero, "b" is one, and "p" is "&a"): + * + * + * CPU 0 CPU 1 + * + * b = 2; + * memory_barrier(); + * p = &b; q = p; + * read_barrier_depends(); + * d = *q; + * + * + * + * because the read of "*q" depends on the read of "p" and these + * two reads are separated by a read_barrier_depends(). However, + * the following code, with the same initial values for "a" and "b": + * + * + * CPU 0 CPU 1 + * + * a = 2; + * memory_barrier(); + * b = 3; y = b; + * read_barrier_depends(); + * x = a; + * + * + * does not enforce ordering, since there is no data dependency between + * the read of "a" and the read of "b". Therefore, on some CPUs, such + * as Alpha, "y" could be set to 3 and "x" to 0. Use rmb() + * in cases like thiswhere there are no data dependencies. + **/ + +#define read_barrier_depends() do { } while (0) + +#ifdef CONFIG_SMP +#define smp_mb() mb() +#define smp_rmb() rmb() +#define smp_wmb() wmb() +#define smp_read_barrier_depends() read_barrier_depends() +#else +#define smp_mb() barrier() +#define smp_rmb() barrier() +#define smp_wmb() barrier() +#define smp_read_barrier_depends() do { } while (0) +#endif + +#define set_mb(var, value) do { xchg(&var, value); } while (0) +#define set_wmb(var, value) do { var = value; wmb(); } while (0) + +#endif /* _ASM_M32R_SYSTEM_H */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/termbits.h 2004-09-03 00:57:17.463384424 -0700 @@ -0,0 +1,175 @@ +#ifndef _ASM_M32R_TERMBITS_H +#define _ASM_M32R_TERMBITS_H + +/* $Id$ */ + +#include + +typedef unsigned char cc_t; +typedef unsigned int speed_t; +typedef unsigned int tcflag_t; + +#define NCCS 19 +struct termios { + tcflag_t c_iflag; /* input mode flags */ + tcflag_t c_oflag; /* output mode flags */ + tcflag_t c_cflag; /* control mode flags */ + tcflag_t c_lflag; /* local mode flags */ + cc_t c_line; /* line discipline */ + cc_t c_cc[NCCS]; /* control characters */ +}; + +/* c_cc characters */ +#define VINTR 0 +#define VQUIT 1 +#define VERASE 2 +#define VKILL 3 +#define VEOF 4 +#define VTIME 5 +#define VMIN 6 +#define VSWTC 7 +#define VSTART 8 +#define VSTOP 9 +#define VSUSP 10 +#define VEOL 11 +#define VREPRINT 12 +#define VDISCARD 13 +#define VWERASE 14 +#define VLNEXT 15 +#define VEOL2 16 + +/* c_iflag bits */ +#define IGNBRK 0000001 +#define BRKINT 0000002 +#define IGNPAR 0000004 +#define PARMRK 0000010 +#define INPCK 0000020 +#define ISTRIP 0000040 +#define INLCR 0000100 +#define IGNCR 0000200 +#define ICRNL 0000400 +#define IUCLC 0001000 +#define IXON 0002000 +#define IXANY 0004000 +#define IXOFF 0010000 +#define IMAXBEL 0020000 +#define IUTF8 0040000 + +/* c_oflag bits */ +#define OPOST 0000001 +#define OLCUC 0000002 +#define ONLCR 0000004 +#define OCRNL 0000010 +#define ONOCR 0000020 +#define ONLRET 0000040 +#define OFILL 0000100 +#define OFDEL 0000200 +#define NLDLY 0000400 +#define NL0 0000000 +#define NL1 0000400 +#define CRDLY 0003000 +#define CR0 0000000 +#define CR1 0001000 +#define CR2 0002000 +#define CR3 0003000 +#define TABDLY 0014000 +#define TAB0 0000000 +#define TAB1 0004000 +#define TAB2 0010000 +#define TAB3 0014000 +#define XTABS 0014000 +#define BSDLY 0020000 +#define BS0 0000000 +#define BS1 0020000 +#define VTDLY 0040000 +#define VT0 0000000 +#define VT1 0040000 +#define FFDLY 0100000 +#define FF0 0000000 +#define FF1 0100000 + +/* c_cflag bit meaning */ +#define CBAUD 0010017 +#define B0 0000000 /* hang up */ +#define B50 0000001 +#define B75 0000002 +#define B110 0000003 +#define B134 0000004 +#define B150 0000005 +#define B200 0000006 +#define B300 0000007 +#define B600 0000010 +#define B1200 0000011 +#define B1800 0000012 +#define B2400 0000013 +#define B4800 0000014 +#define B9600 0000015 +#define B19200 0000016 +#define B38400 0000017 +#define EXTA B19200 +#define EXTB B38400 +#define CSIZE 0000060 +#define CS5 0000000 +#define CS6 0000020 +#define CS7 0000040 +#define CS8 0000060 +#define CSTOPB 0000100 +#define CREAD 0000200 +#define PARENB 0000400 +#define PARODD 0001000 +#define HUPCL 0002000 +#define CLOCAL 0004000 +#define CBAUDEX 0010000 +#define B57600 0010001 +#define B115200 0010002 +#define B230400 0010003 +#define B460800 0010004 +#define B500000 0010005 +#define B576000 0010006 +#define B921600 0010007 +#define B1000000 0010010 +#define B1152000 0010011 +#define B1500000 0010012 +#define B2000000 0010013 +#define B2500000 0010014 +#define B3000000 0010015 +#define B3500000 0010016 +#define B4000000 0010017 +#define CIBAUD 002003600000 /* input baud rate (not used) */ +#define CMSPAR 010000000000 /* mark or space (stick) parity */ +#define CRTSCTS 020000000000 /* flow control */ + +/* c_lflag bits */ +#define ISIG 0000001 +#define ICANON 0000002 +#define XCASE 0000004 +#define ECHO 0000010 +#define ECHOE 0000020 +#define ECHOK 0000040 +#define ECHONL 0000100 +#define NOFLSH 0000200 +#define TOSTOP 0000400 +#define ECHOCTL 0001000 +#define ECHOPRT 0002000 +#define ECHOKE 0004000 +#define FLUSHO 0010000 +#define PENDIN 0040000 +#define IEXTEN 0100000 + +/* tcflow() and TCXONC use these */ +#define TCOOFF 0 +#define TCOON 1 +#define TCIOFF 2 +#define TCION 3 + +/* tcflush() and TCFLSH use these */ +#define TCIFLUSH 0 +#define TCOFLUSH 1 +#define TCIOFLUSH 2 + +/* tcsetattr uses these */ +#define TCSANOW 0 +#define TCSADRAIN 1 +#define TCSAFLUSH 2 + +#endif /* _ASM_M32R_TERMBITS_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/termios.h 2004-09-03 00:57:17.463384424 -0700 @@ -0,0 +1,109 @@ +#ifndef _M32R_TERMIOS_H +#define _M32R_TERMIOS_H + +/* orig : i386 2.6.0-test5 */ + +#include +#include + +struct winsize { + unsigned short ws_row; + unsigned short ws_col; + unsigned short ws_xpixel; + unsigned short ws_ypixel; +}; + +#define NCC 8 +struct termio { + unsigned short c_iflag; /* input mode flags */ + unsigned short c_oflag; /* output mode flags */ + unsigned short c_cflag; /* control mode flags */ + unsigned short c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[NCC]; /* control characters */ +}; + +/* modem lines */ +#define TIOCM_LE 0x001 +#define TIOCM_DTR 0x002 +#define TIOCM_RTS 0x004 +#define TIOCM_ST 0x008 +#define TIOCM_SR 0x010 +#define TIOCM_CTS 0x020 +#define TIOCM_CAR 0x040 +#define TIOCM_RNG 0x080 +#define TIOCM_DSR 0x100 +#define TIOCM_CD TIOCM_CAR +#define TIOCM_RI TIOCM_RNG +#define TIOCM_OUT1 0x2000 +#define TIOCM_OUT2 0x4000 +#define TIOCM_LOOP 0x8000 + +/* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */ + +/* line disciplines */ +#define N_TTY 0 +#define N_SLIP 1 +#define N_MOUSE 2 +#define N_PPP 3 +#define N_STRIP 4 +#define N_AX25 5 +#define N_X25 6 /* X.25 async */ +#define N_6PACK 7 +#define N_MASC 8 /* Reserved for Mobitex module */ +#define N_R3964 9 /* Reserved for Simatic R3964 module */ +#define N_PROFIBUS_FDL 10 /* Reserved for Profibus */ +#define N_IRDA 11 /* Linux IR - http://irda.sourceforge.net/ */ +#define N_SMSBLOCK 12 /* SMS block mode - for talking to GSM data cards about SMS messages */ +#define N_HDLC 13 /* synchronous HDLC */ +#define N_SYNC_PPP 14 /* synchronous PPP */ +#define N_HCI 15 /* Bluetooth HCI UART */ + +#ifdef __KERNEL__ +#include + +/* intr=^C quit=^\ erase=del kill=^U + eof=^D vtime=\0 vmin=\1 sxtc=\0 + start=^Q stop=^S susp=^Z eol=\0 + reprint=^R discard=^U werase=^W lnext=^V + eol2=\0 +*/ +#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0" + +/* + * Translate a "termio" structure into a "termios". Ugh. + */ +#define SET_LOW_TERMIOS_BITS(termios, termio, x) { \ + unsigned short __tmp; \ + get_user(__tmp,&(termio)->x); \ + *(unsigned short *) &(termios)->x = __tmp; \ +} + +#define user_termio_to_kernel_termios(termios, termio) \ +({ \ + SET_LOW_TERMIOS_BITS(termios, termio, c_iflag); \ + SET_LOW_TERMIOS_BITS(termios, termio, c_oflag); \ + SET_LOW_TERMIOS_BITS(termios, termio, c_cflag); \ + SET_LOW_TERMIOS_BITS(termios, termio, c_lflag); \ + copy_from_user((termios)->c_cc, (termio)->c_cc, NCC); \ +}) + +/* + * Translate a "termios" structure into a "termio". Ugh. + */ +#define kernel_termios_to_user_termio(termio, termios) \ +({ \ + put_user((termios)->c_iflag, &(termio)->c_iflag); \ + put_user((termios)->c_oflag, &(termio)->c_oflag); \ + put_user((termios)->c_cflag, &(termio)->c_cflag); \ + put_user((termios)->c_lflag, &(termio)->c_lflag); \ + put_user((termios)->c_line, &(termio)->c_line); \ + copy_to_user((termio)->c_cc, (termios)->c_cc, NCC); \ +}) + +#define user_termios_to_kernel_termios(k, u) copy_from_user(k, u, sizeof(struct termios)) +#define kernel_termios_to_user_termios(u, k) copy_to_user(u, k, sizeof(struct termios)) + +#endif /* __KERNEL__ */ + +#endif /* _M32R_TERMIOS_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/thread_info.h 2004-09-03 00:57:17.464384272 -0700 @@ -0,0 +1,149 @@ +/* thread_info.h: i386 low-level thread information + * + * Copyright (C) 2002 David Howells (dhowells@redhat.com) + * - Incorporating suggestions made by Linus Torvalds and Dave Miller + */ + +#ifndef _ASM_THREAD_INFO_H +#define _ASM_THREAD_INFO_H + +#ifdef __KERNEL__ + +#ifndef __ASSEMBLY__ +#include +#endif + +/* + * low level task data that entry.S needs immediate access to + * - this struct should fit entirely inside of one cache line + * - this struct shares the supervisor stack pages + * - if the contents of this structure are changed, the assembly constants must also be changed + */ +#ifndef __ASSEMBLY__ + +struct thread_info { + struct task_struct *task; /* main task structure */ + struct exec_domain *exec_domain; /* execution domain */ + unsigned long flags; /* low level flags */ + unsigned long status; /* thread-synchronous flags */ + __u32 cpu; /* current CPU */ + __s32 preempt_count; /* 0 => preemptable, <0 => BUG */ + + mm_segment_t addr_limit; /* thread address space: + 0-0xBFFFFFFF for user-thead + 0-0xFFFFFFFF for kernel-thread + */ + struct restart_block restart_block; + + __u8 supervisor_stack[0]; +}; + +#else /* !__ASSEMBLY__ */ + +/* offsets into the thread_info struct for assembly code access */ +#define TI_TASK 0x00000000 +#define TI_EXEC_DOMAIN 0x00000004 +#define TI_FLAGS 0x00000008 +#define TI_STATUS 0x0000000C +#define TI_CPU 0x00000010 +#define TI_PRE_COUNT 0x00000014 +#define TI_ADDR_LIMIT 0x00000018 +#define TI_RESTART_BLOCK 0x000001C + +#endif + +#define PREEMPT_ACTIVE 0x4000000 + +/* + * macros/functions for gaining access to the thread information structure + * + * preempt_count needs to be 1 initially, until the scheduler is functional. + */ +#ifndef __ASSEMBLY__ + +#define INIT_THREAD_INFO(tsk) \ +{ \ + .task = &tsk, \ + .exec_domain = &default_exec_domain, \ + .flags = 0, \ + .cpu = 0, \ + .preempt_count = 1, \ + .addr_limit = KERNEL_DS, \ + .restart_block = { \ + .fn = do_no_restart_syscall, \ + }, \ +} + +#define init_thread_info (init_thread_union.thread_info) +#define init_stack (init_thread_union.stack) + +/* how to get the thread information struct from C */ +static inline struct thread_info *current_thread_info(void) +{ + struct thread_info *ti; + + __asm__ __volatile__ ( + "ldi %0, #0xffffe000; \n\t" + "and %0, sp; \n\t" + : "=r" (ti) + ); + + return ti; +} + +/* thread information allocation */ +#define THREAD_SIZE (2*PAGE_SIZE) +#define alloc_thread_info(task) \ + ((struct thread_info *) __get_free_pages(GFP_KERNEL,1)) +#define free_thread_info(ti) free_pages((unsigned long) (ti), 1) +#define get_thread_info(ti) get_task_struct((ti)->task) +#define put_thread_info(ti) put_task_struct((ti)->task) + +#else /* !__ASSEMBLY__ */ + +/* how to get the thread information struct from ASM */ +#define GET_THREAD_INFO(reg) GET_THREAD_INFO reg + .macro GET_THREAD_INFO reg + ldi \reg, #0xffffe000 + and \reg, sp + .endm + +#endif + +/* + * thread information flags + * - these are process state flags that various assembly files may need to access + * - pending work-to-be-done flags are in LSW + * - other flags in MSW + */ +#define TIF_SYSCALL_TRACE 0 /* syscall trace active */ +#define TIF_NOTIFY_RESUME 1 /* resumption notification requested */ +#define TIF_SIGPENDING 2 /* signal pending */ +#define TIF_NEED_RESCHED 3 /* rescheduling necessary */ +#define TIF_SINGLESTEP 4 /* restore singlestep on return to user mode */ +#define TIF_IRET 5 /* return with iret */ +#define TIF_POLLING_NRFLAG 16 /* true if poll_idle() is polling TIF_NEED_RESCHED */ + +#define _TIF_SYSCALL_TRACE (1< + +#define CLOCK_TICK_RATE (CONFIG_BUS_CLOCK / CONFIG_TIMER_DIVIDE) +#define CLOCK_TICK_FACTOR 20 /* Factor of both 1000000 and CLOCK_TICK_RATE */ +#define FINETUNE ((((((long)LATCH * HZ - CLOCK_TICK_RATE) << SHIFT_HZ) * \ + (1000000/CLOCK_TICK_FACTOR) / (CLOCK_TICK_RATE/CLOCK_TICK_FACTOR)) \ + << (SHIFT_SCALE-SHIFT_HZ)) / HZ) + +#ifdef __KERNEL__ +/* + * Standard way to access the cycle counter. + * Currently only used on SMP. + */ + +typedef unsigned long long cycles_t; + +extern cycles_t cacheflush_time; + +static __inline__ cycles_t get_cycles (void) +{ + return 0; +} +#endif /* __KERNEL__ */ + +#endif /* _ASM_M32R_TIMEX_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/tlbflush.h 2004-09-03 00:57:17.465384120 -0700 @@ -0,0 +1,102 @@ +#ifndef _ASM_M32R_TLBFLUSH_H +#define _ASM_M32R_TLBFLUSH_H + +#include +#include + +/* + * TLB flushing: + * + * - flush_tlb() flushes the current mm struct TLBs + * - flush_tlb_all() flushes all processes TLBs + * - flush_tlb_mm(mm) flushes the specified mm context TLB's + * - flush_tlb_page(vma, vmaddr) flushes one page + * - flush_tlb_range(vma, start, end) flushes a range of pages + * - flush_tlb_kernel_range(start, end) flushes a range of kernel pages + * - flush_tlb_pgtables(mm, start, end) flushes a range of page tables + */ + +extern void local_flush_tlb_all(void); +extern void local_flush_tlb_mm(struct mm_struct *); +extern void local_flush_tlb_page(struct vm_area_struct *, unsigned long); +extern void local_flush_tlb_range(struct vm_area_struct *, unsigned long, + unsigned long); + +#ifndef CONFIG_SMP +#ifdef CONFIG_MMU +#define flush_tlb_all() local_flush_tlb_all() +#define flush_tlb_mm(mm) local_flush_tlb_mm(mm) +#define flush_tlb_page(vma, page) local_flush_tlb_page(vma, page) +#define flush_tlb_range(vma, start, end) \ + local_flush_tlb_range(vma, start, end) +#define flush_tlb_kernel_range(start, end) local_flush_tlb_all() +#else /* CONFIG_MMU */ +#define flush_tlb_all() do { } while (0) +#define flush_tlb_mm(mm) do { } while (0) +#define flush_tlb_page(vma, vmaddr) do { } while (0) +#define flush_tlb_range(vma, start, end) do { } while (0) +#endif /* CONFIG_MMU */ +#else /* CONFIG_SMP */ +extern void smp_flush_tlb_all(void); +extern void smp_flush_tlb_mm(struct mm_struct *); +extern void smp_flush_tlb_page(struct vm_area_struct *, unsigned long); +extern void smp_flush_tlb_range(struct vm_area_struct *, unsigned long, + unsigned long); + +#define flush_tlb_all() smp_flush_tlb_all() +#define flush_tlb_mm(mm) smp_flush_tlb_mm(mm) +#define flush_tlb_page(vma, page) smp_flush_tlb_page(vma, page) +#define flush_tlb_range(vma, start, end) \ + smp_flush_tlb_range(vma, start, end) +#define flush_tlb_kernel_range(start, end) smp_flush_tlb_all() +#endif /* CONFIG_SMP */ + +static __inline__ void __flush_tlb_page(unsigned long page) +{ + unsigned int tmpreg0, tmpreg1, tmpreg2; + + __asm__ __volatile__ ( + "seth %0, #high(%4) \n\t" + "st %3, @(%5, %0) \n\t" + "ldi %1, #1 \n\t" + "st %1, @(%6, %0) \n\t" + "add3 %1, %0, %7 \n\t" + ".fillinsn \n" + "1: \n\t" + "ld %2, @(%6, %0) \n\t" + "bnez %2, 1b \n\t" + "ld %0, @%1+ \n\t" + "ld %1, @%1 \n\t" + "st %2, @+%0 \n\t" + "st %2, @+%1 \n\t" + : "=&r" (tmpreg0), "=&r" (tmpreg1), "=&r" (tmpreg2) + : "r" (page), "i" (MMU_REG_BASE), "i" (MSVA_offset), + "i" (MTOP_offset), "i" (MIDXI_offset) + : "memory" + ); +} + +static __inline__ void __flush_tlb_all(void) +{ + unsigned int tmpreg0, tmpreg1; + + __asm__ __volatile__ ( + "seth %0, #high(%2) \n\t" + "or3 %0, %0, #low(%2) \n\t" + "ldi %1, #0xc \n\t" + "st %1, @%0 \n\t" + ".fillinsn \n" + "1: \n\t" + "ld %1, @%0 \n\t" + "bnez %1, 1b \n\t" + : "=&r" (tmpreg0), "=&r" (tmpreg1) + : "i" (MTOP) : "memory" + ); +} + +#define flush_tlb_pgtables(mm, start, end) do { } while (0) + +extern void update_mmu_cache(struct vm_area_struct *, unsigned long, pte_t); + +#endif /* _ASM_M32R_TLBFLUSH_H */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/tlb.h 2004-09-03 00:57:17.465384120 -0700 @@ -0,0 +1,20 @@ +#ifndef _M32R_TLB_H +#define _M32R_TLB_H + +/* + * x86 doesn't need any special per-pte or + * per-vma handling.. + */ +#define tlb_start_vma(tlb, vma) do { } while (0) +#define tlb_end_vma(tlb, vma) do { } while (0) +#define __tlb_remove_tlb_entry(tlb, pte, address) do { } while (0) + +/* + * .. because we flush the whole mm when it + * fills up. + */ +#define tlb_flush(tlb) flush_tlb_mm((tlb)->mm) + +#include + +#endif /* _M32R_TLB_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/topology.h 2004-09-03 00:57:17.466383968 -0700 @@ -0,0 +1,53 @@ +/* + * linux/include/asm-generic/topology.h + * + * Written by: Matthew Dobson, IBM Corporation + * + * Copyright (C) 2002, IBM Corp. + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to + */ +#ifndef _ASM_M32R_TOPOLOGY_H +#define _ASM_M32R_TOPOLOGY_H + +/* Other architectures wishing to use this simple topology API should fill + in the below functions as appropriate in their own file. */ + +#define cpu_to_node(cpu) (0) + +#ifndef parent_node +#define parent_node(node) (0) +#endif +#ifndef node_to_cpumask +#define node_to_cpumask(node) (cpu_online_map) +#endif +#ifndef node_to_first_cpu +#define node_to_first_cpu(node) (0) +#endif +#ifndef pcibus_to_cpumask +#define pcibus_to_cpumask(bus) (cpu_online_map) +#endif + +/* Cross-node load balancing interval. */ +#ifndef NODE_BALANCE_RATE +#define NODE_BALANCE_RATE 10 +#endif + +#endif /* _ASM_M32R_TOPOLOGY_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/types.h 2004-09-03 00:57:19.066140768 -0700 @@ -0,0 +1,64 @@ +#ifndef _ASM_M32R_TYPES_H +#define _ASM_M32R_TYPES_H + +#ifndef __ASSEMBLY__ + +/* $Id$ */ + +/* orig : i386 2.4.18 */ + +typedef unsigned short umode_t; + +/* + * __xx is ok: it doesn't pollute the POSIX namespace. Use these in the + * header files exported to user space + */ + +typedef __signed__ char __s8; +typedef unsigned char __u8; + +typedef __signed__ short __s16; +typedef unsigned short __u16; + +typedef __signed__ int __s32; +typedef unsigned int __u32; + +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +typedef __signed__ long long __s64; +typedef unsigned long long __u64; +#endif +#endif /* __ASSEMBLY__ */ + +/* + * These aren't exported outside the kernel to avoid name space clashes + */ +#ifdef __KERNEL__ + +#define BITS_PER_LONG 32 + +#ifndef __ASSEMBLY__ + +typedef signed char s8; +typedef unsigned char u8; + +typedef signed short s16; +typedef unsigned short u16; + +typedef signed int s32; +typedef unsigned int u32; + +typedef signed long long s64; +typedef unsigned long long u64; + +/* DMA addresses are 32-bits wide. */ + +typedef u32 dma_addr_t; +typedef u64 dma64_addr_t; + +typedef unsigned short kmem_bufctl_t; + +#endif /* __ASSEMBLY__ */ + +#endif /* __KERNEL__ */ + +#endif /* _ASM_M32R_TYPES_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/uaccess.h 2004-09-03 00:57:19.067140616 -0700 @@ -0,0 +1,521 @@ +#ifndef _ASM_M32R_UACCESS_H +#define _ASM_M32R_UACCESS_H + +/* $Id$ */ + +#undef UACCESS_DEBUG + +#ifdef UACCESS_DEBUG +#define UAPRINTK(args...) printk(args) +#else +#define UAPRINTK(args...) +#endif /* UACCESS_DEBUG */ + +/* + * User space memory access functions + */ +#include +#include +#include +#include + +#define VERIFY_READ 0 +#define VERIFY_WRITE 1 + +/* + * The fs value determines whether argument validity checking should be + * performed or not. If get_fs() == USER_DS, checking is performed, with + * get_fs() == KERNEL_DS, checking is bypassed. + * + * For historical reasons, these macros are grossly misnamed. + */ + +#define MAKE_MM_SEG(s) ((mm_segment_t) { (s) }) + +#ifdef CONFIG_MMU +#define KERNEL_DS MAKE_MM_SEG(0xFFFFFFFF) +#define USER_DS MAKE_MM_SEG(PAGE_OFFSET) +#else +#define KERNEL_DS MAKE_MM_SEG(0xFFFFFFFF) +#define USER_DS MAKE_MM_SEG(0xFFFFFFFF) +#endif /* CONFIG_MMU */ + +#define get_ds() (KERNEL_DS) +#ifdef CONFIG_MMU +#define get_fs() (current_thread_info()->addr_limit) +#define set_fs(x) (current_thread_info()->addr_limit = (x)) +#else +static inline mm_segment_t get_fs(void) +{ + return USER_DS; +} + +static inline void set_fs(mm_segment_t s) +{ +} +#endif /* CONFIG_MMU */ + +#define segment_eq(a,b) ((a).seg == (b).seg) + +#define __addr_ok(addr) \ + ((unsigned long)(addr) < (current_thread_info()->addr_limit.seg)) + +/* + * Uhhuh, this needs 33-bit arithmetic. We have a carry.. + */ +#define __range_ok(addr,size) ({ \ + unsigned long flag, sum; \ + __chk_user_ptr(addr); \ + asm ( \ + " cmpu %1, %1 ; clear cbit\n" \ + " addx %1, %3 ; set cbit if overflow\n" \ + " subx %0, %0\n" \ + " cmpu %4, %1\n" \ + " subx %0, %5\n" \ + : "=&r"(flag), "=r"(sum) \ + : "1"(addr), "r"((int)(size)), \ + "r"(current_thread_info()->addr_limit.seg), "r"(0) \ + : "cbit" ); \ + flag; }) + +#ifdef CONFIG_MMU +#define access_ok(type,addr,size) (__range_ok(addr,size) == 0) +#else +static inline int access_ok(int type, const void *addr, unsigned long size) +{ + extern unsigned long memory_start, memory_end; + unsigned long val = (unsigned long)addr; + + return ((val >= memory_start) && ((val + size) < memory_end)); +} +#endif /* CONFIG_MMU */ + +static __inline__ int verify_area(int type, const void __user *addr, + unsigned long size) +{ + return access_ok(type, addr, size) ? 0 : -EFAULT; +} + + +/* + * The exception table consists of pairs of addresses: the first is the + * address of an instruction that is allowed to fault, and the second is + * the address at which the program should continue. No registers are + * modified, so it is entirely up to the continuation code to figure out + * what to do. + * + * All the routines below use bits of fixup code that are out of line + * with the main instruction path. This means when everything is well, + * we don't even have to jump over them. Further, they do not intrude + * on our cache or tlb entries. + */ + +struct exception_table_entry +{ + unsigned long insn, fixup; +}; + +extern int fixup_exception(struct pt_regs *regs); + +/* + * These are the main single-value transfer routines. They automatically + * use the right size if we just have the right pointer type. + * + * This gets kind of ugly. We want to return _two_ values in "get_user()" + * and yet we don't want to do any pointers, because that is too much + * of a performance impact. Thus we have a few rather ugly macros here, + * and hide all the uglyness from the user. + * + * The "__xxx" versions of the user access functions are versions that + * do not verify the address space, that must have been done previously + * with a separate "access_ok()" call (this is used when we do multiple + * accesses to the same area of user memory). + */ + +extern void __get_user_1(void); +extern void __get_user_2(void); +extern void __get_user_4(void); + +#ifndef MODULE +#define __get_user_x(size,ret,x,ptr) \ + __asm__ __volatile__( \ + " mv r0, %0\n" \ + " mv r1, %1\n" \ + " bl __get_user_" #size "\n" \ + " mv %0, r0\n" \ + " mv %1, r1\n" \ + : "=r"(ret), "=r"(x) \ + : "0"(ptr) \ + : "r0", "r1", "r14" ) +#else /* MODULE */ +/* + * Use "jl" instead of "bl" for MODULE + */ +#define __get_user_x(size,ret,x,ptr) \ + __asm__ __volatile__( \ + " mv r0, %0\n" \ + " mv r1, %1\n" \ + " seth lr, #high(__get_user_" #size ")\n" \ + " or3 lr, lr, #low(__get_user_" #size ")\n" \ + " jl lr\n" \ + " mv %0, r0\n" \ + " mv %1, r1\n" \ + : "=r"(ret), "=r"(x) \ + : "0"(ptr) \ + : "r0", "r1", "r14" ) +#endif + +/* Careful: we have to cast the result to the type of the pointer for sign + reasons */ +#define get_user(x,ptr) \ +({ int __ret_gu,__val_gu; \ + __chk_user_ptr(ptr); \ + switch(sizeof (*(ptr))) { \ + case 1: __get_user_x(1,__ret_gu,__val_gu,ptr); break; \ + case 2: __get_user_x(2,__ret_gu,__val_gu,ptr); break; \ + case 4: __get_user_x(4,__ret_gu,__val_gu,ptr); break; \ + default: __get_user_x(X,__ret_gu,__val_gu,ptr); break; \ + } \ + (x) = (__typeof__(*(ptr)))__val_gu; \ + __ret_gu; \ +}) + +extern void __put_user_bad(void); + +#define put_user(x,ptr) \ + __put_user_check((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr))) + +#define __get_user(x,ptr) \ + __get_user_nocheck((x),(ptr),sizeof(*(ptr))) +#define __put_user(x,ptr) \ + __put_user_nocheck((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr))) + +#define __put_user_nocheck(x,ptr,size) \ +({ \ + long __pu_err; \ + __put_user_size((x),(ptr),(size),__pu_err); \ + __pu_err; \ +}) + + +#define __put_user_check(x,ptr,size) \ +({ \ + long __pu_err = -EFAULT; \ + __typeof__(*(ptr)) __user *__pu_addr = (ptr); \ + might_sleep(); \ + if (access_ok(VERIFY_WRITE,__pu_addr,size)) \ + __put_user_size((x),__pu_addr,(size),__pu_err); \ + __pu_err; \ +}) + +#define __put_user_size(x,ptr,size,retval) \ +do { \ + retval = 0; \ + __chk_user_ptr(ptr); \ + switch (size) { \ + case 1: __put_user_asm(x,ptr,retval,"b"); break; \ + case 2: __put_user_asm(x,ptr,retval,"h"); break; \ + case 4: __put_user_asm(x,ptr,retval,""); break; \ + case 8: __put_user_u64((__typeof__(*ptr))(x),ptr,retval); break;\ + default: __put_user_bad(); \ + } \ +} while (0) + +struct __large_struct { unsigned long buf[100]; }; +#define __m(x) (*(struct __large_struct *)(x)) + +/* + * Tell gcc we read from memory instead of writing: this is because + * we do not write to any memory gcc knows about, so there are no + * aliasing issues. + */ +#define __put_user_asm(x, addr, err, itype) \ + __asm__ __volatile__( \ + " .fillinsn\n" \ + "1: st"itype" %1,@%2\n" \ + " .fillinsn\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + " .balign 4\n" \ + "3: ldi %0,%3\n" \ + " seth r14,#high(2b)\n" \ + " or3 r14,r14,#low(2b)\n" \ + " jmp r14\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 1b,3b\n" \ + ".previous" \ + : "=r"(err) \ + : "r"(x), "r"(addr), "i"(-EFAULT), "0"(err) \ + : "r14", "memory") + +#if defined(__LITTLE_ENDIAN__) +#define __put_user_u64(x, addr, err) \ + __asm__ __volatile__( \ + " .fillinsn\n" \ + "1: st %L1,@%2\n" \ + " .fillinsn\n" \ + "2: st %H1,@(4,%2)\n" \ + " .fillinsn\n" \ + "3:\n" \ + ".section .fixup,\"ax\"\n" \ + " .balign 4\n" \ + "4: ldi %0,%3\n" \ + " seth r14,#high(3b)\n" \ + " or3 r14,r14,#low(3b)\n" \ + " jmp r14\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 1b,4b\n" \ + " .long 2b,4b\n" \ + ".previous" \ + : "=r"(err) \ + : "r"(x), "r"(addr), "i"(-EFAULT), "0"(err) \ + : "r14", "memory") + +#elif defined(__BIG_ENDIAN__) +#define __put_user_u64(x, addr, err) \ + __asm__ __volatile__( \ + " .fillinsn\n" \ + "1: st %H1,@%2\n" \ + " .fillinsn\n" \ + "2: st %L1,@(4,%2)\n" \ + " .fillinsn\n" \ + "3:\n" \ + ".section .fixup,\"ax\"\n" \ + " .balign 4\n" \ + "4: ldi %0,%3\n" \ + " seth r14,#high(3b)\n" \ + " or3 r14,r14,#low(3b)\n" \ + " jmp r14\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 1b,4b\n" \ + " .long 2b,4b\n" \ + ".previous" \ + : "=r"(err) \ + : "r"(x), "r"(addr), "i"(-EFAULT), "0"(err) \ + : "r14", "memory") +#else +#error no endian defined +#endif + +#define __get_user_nocheck(x,ptr,size) \ +({ \ + long __gu_err, __gu_val; \ + __get_user_size(__gu_val,(ptr),(size),__gu_err); \ + (x) = (__typeof__(*(ptr)))__gu_val; \ + __gu_err; \ +}) + +extern long __get_user_bad(void); + +#define __get_user_size(x,ptr,size,retval) \ +do { \ + retval = 0; \ + __chk_user_ptr(ptr); \ + switch (size) { \ + case 1: __get_user_asm(x,ptr,retval,"ub"); break; \ + case 2: __get_user_asm(x,ptr,retval,"uh"); break; \ + case 4: __get_user_asm(x,ptr,retval,""); break; \ + default: (x) = __get_user_bad(); \ + } \ +} while (0) + +#define __get_user_asm(x, addr, err, itype) \ + __asm__ __volatile__( \ + " .fillinsn\n" \ + "1: ld"itype" %1,@%2\n" \ + " .fillinsn\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + " .balign 4\n" \ + "3: ldi %0,%3\n" \ + " seth r14,#high(2b)\n" \ + " or3 r14,r14,#low(2b)\n" \ + " jmp r14\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 1b,3b\n" \ + ".previous" \ + : "=r"(err), "=&r"(x) \ + : "r"(addr), "i"(-EFAULT), "0"(err) \ + : "r14", "memory") + + +/* + * Copy To/From Userspace + */ + +/* Generic arbitrary sized copy. */ +/* Return the number of bytes NOT copied. */ +#define __copy_user(to,from,size) \ +do { \ + unsigned long __dst, __src, __c; \ + __asm__ __volatile__ ( \ + " mv r14, %0\n" \ + " or r14, %1\n" \ + " beq %0, %1, 9f\n" \ + " beqz %2, 9f\n" \ + " and3 r14, r14, #3\n" \ + " bnez r14, 2f\n" \ + " and3 %2, %2, #3\n" \ + " beqz %3, 2f\n" \ + " addi %0, #-4 ; word_copy \n" \ + " .fillinsn\n" \ + "0: ld r14, @%1+\n" \ + " addi %3, #-1\n" \ + " .fillinsn\n" \ + "1: st r14, @+%0\n" \ + " bnez %3, 0b\n" \ + " beqz %2, 9f\n" \ + " addi %0, #4\n" \ + " .fillinsn\n" \ + "2: ldb r14, @%1 ; byte_copy \n" \ + " .fillinsn\n" \ + "3: stb r14, @%0\n" \ + " addi %1, #1\n" \ + " addi %2, #-1\n" \ + " addi %0, #1\n" \ + " bnez %2, 2b\n" \ + " .fillinsn\n" \ + "9:\n" \ + ".section .fixup,\"ax\"\n" \ + " .balign 4\n" \ + "5: addi %3, #1\n" \ + " addi %1, #-4\n" \ + " .fillinsn\n" \ + "6: slli %3, #2\n" \ + " add %2, %3\n" \ + " addi %0, #4\n" \ + " .fillinsn\n" \ + "7: seth r14, #high(9b)\n" \ + " or3 r14, r14, #low(9b)\n" \ + " jmp r14\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 0b,6b\n" \ + " .long 1b,5b\n" \ + " .long 2b,9b\n" \ + " .long 3b,9b\n" \ + ".previous\n" \ + : "=&r"(__dst), "=&r"(__src), "=&r"(size), "=&r"(__c) \ + : "0"(to), "1"(from), "2"(size), "3"(size / 4) \ + : "r14", "memory"); \ +} while (0) + +#define __copy_user_zeroing(to,from,size) \ +do { \ + unsigned long __dst, __src, __c; \ + __asm__ __volatile__ ( \ + " mv r14, %0\n" \ + " or r14, %1\n" \ + " beq %0, %1, 9f\n" \ + " beqz %2, 9f\n" \ + " and3 r14, r14, #3\n" \ + " bnez r14, 2f\n" \ + " and3 %2, %2, #3\n" \ + " beqz %3, 2f\n" \ + " addi %0, #-4 ; word_copy \n" \ + " .fillinsn\n" \ + "0: ld r14, @%1+\n" \ + " addi %3, #-1\n" \ + " .fillinsn\n" \ + "1: st r14, @+%0\n" \ + " bnez %3, 0b\n" \ + " beqz %2, 9f\n" \ + " addi %0, #4\n" \ + " .fillinsn\n" \ + "2: ldb r14, @%1 ; byte_copy \n" \ + " .fillinsn\n" \ + "3: stb r14, @%0\n" \ + " addi %1, #1\n" \ + " addi %2, #-1\n" \ + " addi %0, #1\n" \ + " bnez %2, 2b\n" \ + " .fillinsn\n" \ + "9:\n" \ + ".section .fixup,\"ax\"\n" \ + " .balign 4\n" \ + "5: addi %3, #1\n" \ + " addi %1, #-4\n" \ + " .fillinsn\n" \ + "6: slli %3, #2\n" \ + " add %2, %3\n" \ + " addi %0, #4\n" \ + " .fillinsn\n" \ + "7: ldi r14, #0 ; store zero \n" \ + " .fillinsn\n" \ + "8: addi %2, #-1\n" \ + " stb r14, @%0 ; ACE? \n" \ + " addi %0, #1\n" \ + " bnez %2, 8b\n" \ + " seth r14, #high(9b)\n" \ + " or3 r14, r14, #low(9b)\n" \ + " jmp r14\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 0b,6b\n" \ + " .long 1b,5b\n" \ + " .long 2b,7b\n" \ + " .long 3b,7b\n" \ + ".previous\n" \ + : "=&r"(__dst), "=&r"(__src), "=&r"(size), "=&r"(__c) \ + : "0"(to), "1"(from), "2"(size), "3"(size / 4) \ + : "r14", "memory"); \ +} while (0) + + +/* We let the __ versions of copy_from/to_user inline, because they're often + * used in fast paths and have only a small space overhead. + */ +static __inline__ unsigned long __generic_copy_from_user_nocheck(void *to, + const void __user *from, unsigned long n) +{ + __copy_user_zeroing(to,from,n); + return n; +} + +static __inline__ unsigned long __generic_copy_to_user_nocheck(void __user *to, + const void *from, unsigned long n) +{ + __copy_user(to,from,n); + return n; +} + +unsigned long __generic_copy_to_user(void *, const void *, unsigned long); +unsigned long __generic_copy_from_user(void *, const void *, unsigned long); + +#define copy_to_user(to,from,n) \ +({ \ + might_sleep(); \ + __generic_copy_to_user((to),(from),(n)); \ +}) + +#define copy_from_user(to,from,n) \ +({ \ + might_sleep(); \ + __generic_copy_from_user((to),(from),(n)); \ +}) + +#define __copy_to_user(to,from,n) \ + __generic_copy_to_user_nocheck((to),(from),(n)) + +#define __copy_from_user(to,from,n) \ + __generic_copy_from_user_nocheck((to),(from),(n)) + +long strncpy_from_user(char *dst, const char __user *src, long count); +long __strncpy_from_user(char *dst, const char __user *src, long count); +#define strlen_user(str) strnlen_user(str, ~0UL >> 1) +long strnlen_user(const char __user *str, long n); +unsigned long clear_user(void __user *mem, unsigned long len); +unsigned long __clear_user(void __user *mem, unsigned long len); + +#endif /* _ASM_M32R_UACCESS_H */ + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/ucontext.h 2004-09-03 00:57:17.469383512 -0700 @@ -0,0 +1,14 @@ +#ifndef _ASM_M32R_UCONTEXT_H +#define _ASM_M32R_UCONTEXT_H + +/* orig : i386 2.4.18 */ + +struct ucontext { + unsigned long uc_flags; + struct ucontext *uc_link; + stack_t uc_stack; + struct sigcontext uc_mcontext; + sigset_t uc_sigmask; /* mask last for extensibility */ +}; + +#endif /* _ASM_M32R_UCONTEXT_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/unaligned.h 2004-09-03 00:57:17.469383512 -0700 @@ -0,0 +1,25 @@ +#ifndef _ASM_M32R_UNALIGNED_H +#define _ASM_M32R_UNALIGNED_H + +/* $Id$ */ + +/* orig : generic 2.4.18 */ + +/* + * For the benefit of those who are trying to port Linux to another + * architecture, here are some C-language equivalents. + */ + +#include + + +#define get_unaligned(ptr) \ + ({ __typeof__(*(ptr)) __tmp; memmove(&__tmp, (ptr), sizeof(*(ptr))); __tmp; }) + +#define put_unaligned(val, ptr) \ + ({ __typeof__(*(ptr)) __tmp = (val); \ + memmove((ptr), &__tmp, sizeof(*(ptr))); \ + (void)0; }) + + +#endif /* _ASM_M32R_UNALIGNED_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/unistd.h 2004-09-03 00:57:18.743189864 -0700 @@ -0,0 +1,480 @@ +#ifndef _ASM_M32R_UNISTD_H +#define _ASM_M32R_UNISTD_H + +/* $Id$ */ + +#include /* SYSCALL_* */ + +/* + * This file contains the system call numbers. + */ + +#define __NR_restart_syscall 0 +#define __NR_exit 1 +#define __NR_fork 2 +#define __NR_read 3 +#define __NR_write 4 +#define __NR_open 5 +#define __NR_close 6 +#define __NR_waitpid 7 +#define __NR_creat 8 +#define __NR_link 9 +#define __NR_unlink 10 +#define __NR_execve 11 +#define __NR_chdir 12 +#define __NR_time 13 +#define __NR_mknod 14 +#define __NR_chmod 15 +#define __NR_lchown 16 +#define __NR_break 17 +#define __NR_oldstat 18 +#define __NR_lseek 19 +#define __NR_getpid 20 +#define __NR_mount 21 +#define __NR_umount 22 +#define __NR_setuid 23 +#define __NR_getuid 24 +#define __NR_stime 25 +#define __NR_ptrace 26 +#define __NR_alarm 27 +#define __NR_oldfstat 28 +#define __NR_pause 29 +#define __NR_utime 30 +#define __NR_cacheflush 31 /* old #define __NR_stty 31*/ +#define __NR_cachectl 32 /* old #define __NR_gtty 32*/ +#define __NR_access 33 +#define __NR_nice 34 +#define __NR_ftime 35 +#define __NR_sync 36 +#define __NR_kill 37 +#define __NR_rename 38 +#define __NR_mkdir 39 +#define __NR_rmdir 40 +#define __NR_dup 41 +#define __NR_pipe 42 +#define __NR_times 43 +#define __NR_prof 44 +#define __NR_brk 45 +#define __NR_setgid 46 +#define __NR_getgid 47 +#define __NR_signal 48 +#define __NR_geteuid 49 +#define __NR_getegid 50 +#define __NR_acct 51 +#define __NR_umount2 52 +#define __NR_lock 53 +#define __NR_ioctl 54 +#define __NR_fcntl 55 +#define __NR_mpx 56 +#define __NR_setpgid 57 +#define __NR_ulimit 58 +#define __NR_oldolduname 59 +#define __NR_umask 60 +#define __NR_chroot 61 +#define __NR_ustat 62 +#define __NR_dup2 63 +#define __NR_getppid 64 +#define __NR_getpgrp 65 +#define __NR_setsid 66 +#define __NR_sigaction 67 +#define __NR_sgetmask 68 +#define __NR_ssetmask 69 +#define __NR_setreuid 70 +#define __NR_setregid 71 +#define __NR_sigsuspend 72 +#define __NR_sigpending 73 +#define __NR_sethostname 74 +#define __NR_setrlimit 75 +#define __NR_getrlimit 76 /* Back compatible 2Gig limited rlimit */ +#define __NR_getrusage 77 +#define __NR_gettimeofday 78 +#define __NR_settimeofday 79 +#define __NR_getgroups 80 +#define __NR_setgroups 81 +#define __NR_select 82 +#define __NR_symlink 83 +#define __NR_oldlstat 84 +#define __NR_readlink 85 +#define __NR_uselib 86 +#define __NR_swapon 87 +#define __NR_reboot 88 +#define __NR_readdir 89 +#define __NR_mmap 90 +#define __NR_munmap 91 +#define __NR_truncate 92 +#define __NR_ftruncate 93 +#define __NR_fchmod 94 +#define __NR_fchown 95 +#define __NR_getpriority 96 +#define __NR_setpriority 97 +#define __NR_profil 98 +#define __NR_statfs 99 +#define __NR_fstatfs 100 +#define __NR_ioperm 101 +#define __NR_socketcall 102 +#define __NR_syslog 103 +#define __NR_setitimer 104 +#define __NR_getitimer 105 +#define __NR_stat 106 +#define __NR_lstat 107 +#define __NR_fstat 108 +#define __NR_olduname 109 +#define __NR_iopl 110 +#define __NR_vhangup 111 +#define __NR_idle 112 +#define __NR_vm86old 113 +#define __NR_wait4 114 +#define __NR_swapoff 115 +#define __NR_sysinfo 116 +#define __NR_ipc 117 +#define __NR_fsync 118 +#define __NR_sigreturn 119 +#define __NR_clone 120 +#define __NR_setdomainname 121 +#define __NR_uname 122 +#define __NR_modify_ldt 123 +#define __NR_adjtimex 124 +#define __NR_mprotect 125 +#define __NR_sigprocmask 126 +#define __NR_create_module 127 +#define __NR_init_module 128 +#define __NR_delete_module 129 +#define __NR_get_kernel_syms 130 +#define __NR_quotactl 131 +#define __NR_getpgid 132 +#define __NR_fchdir 133 +#define __NR_bdflush 134 +#define __NR_sysfs 135 +#define __NR_personality 136 +#define __NR_afs_syscall 137 /* Syscall for Andrew File System */ +#define __NR_setfsuid 138 +#define __NR_setfsgid 139 +#define __NR__llseek 140 +#define __NR_getdents 141 +#define __NR__newselect 142 +#define __NR_flock 143 +#define __NR_msync 144 +#define __NR_readv 145 +#define __NR_writev 146 +#define __NR_getsid 147 +#define __NR_fdatasync 148 +#define __NR__sysctl 149 +#define __NR_mlock 150 +#define __NR_munlock 151 +#define __NR_mlockall 152 +#define __NR_munlockall 153 +#define __NR_sched_setparam 154 +#define __NR_sched_getparam 155 +#define __NR_sched_setscheduler 156 +#define __NR_sched_getscheduler 157 +#define __NR_sched_yield 158 +#define __NR_sched_get_priority_max 159 +#define __NR_sched_get_priority_min 160 +#define __NR_sched_rr_get_interval 161 +#define __NR_nanosleep 162 +#define __NR_mremap 163 +#define __NR_setresuid 164 +#define __NR_getresuid 165 +#define __NR_tas 166 +#define __NR_query_module 167 +#define __NR_poll 168 +#define __NR_nfsservctl 169 +#define __NR_setresgid 170 +#define __NR_getresgid 171 +#define __NR_prctl 172 +#define __NR_rt_sigreturn 173 +#define __NR_rt_sigaction 174 +#define __NR_rt_sigprocmask 175 +#define __NR_rt_sigpending 176 +#define __NR_rt_sigtimedwait 177 +#define __NR_rt_sigqueueinfo 178 +#define __NR_rt_sigsuspend 179 +#define __NR_pread64 180 +#define __NR_pwrite64 181 +#define __NR_chown 182 +#define __NR_getcwd 183 +#define __NR_capget 184 +#define __NR_capset 185 +#define __NR_sigaltstack 186 +#define __NR_sendfile 187 +#define __NR_getpmsg 188 /* some people actually want streams */ +#define __NR_putpmsg 189 /* some people actually want streams */ +#define __NR_vfork 190 +#define __NR_ugetrlimit 191 /* SuS compliant getrlimit */ +#define __NR_mmap2 192 +#define __NR_truncate64 193 +#define __NR_ftruncate64 194 +#define __NR_stat64 195 +#define __NR_lstat64 196 +#define __NR_fstat64 197 +#define __NR_lchown32 198 +#define __NR_getuid32 199 +#define __NR_getgid32 200 +#define __NR_geteuid32 201 +#define __NR_getegid32 202 +#define __NR_setreuid32 203 +#define __NR_setregid32 204 +#define __NR_getgroups32 205 +#define __NR_setgroups32 206 +#define __NR_fchown32 207 +#define __NR_setresuid32 208 +#define __NR_getresuid32 209 +#define __NR_setresgid32 210 +#define __NR_getresgid32 211 +#define __NR_chown32 212 +#define __NR_setuid32 213 +#define __NR_setgid32 214 +#define __NR_setfsuid32 215 +#define __NR_setfsgid32 216 +#define __NR_pivot_root 217 +#define __NR_mincore 218 +#define __NR_madvise 219 +#define __NR_madvise1 219 /* delete when C lib stub is removed */ +#define __NR_getdents64 220 +#define __NR_fcntl64 221 +#define __NR_security 223 /* syscall for security modules */ +#define __NR_gettid 224 +#define __NR_readahead 225 +#define __NR_setxattr 226 +#define __NR_lsetxattr 227 +#define __NR_fsetxattr 228 +#define __NR_getxattr 229 +#define __NR_lgetxattr 230 +#define __NR_fgetxattr 231 +#define __NR_listxattr 232 +#define __NR_llistxattr 233 +#define __NR_flistxattr 234 +#define __NR_removexattr 235 +#define __NR_lremovexattr 236 +#define __NR_fremovexattr 237 +#define __NR_tkill 238 +#define __NR_sendfile64 239 +#define __NR_futex 240 +#define __NR_sched_setaffinity 241 +#define __NR_sched_getaffinity 242 +#define __NR_set_thread_area 243 +#define __NR_get_thread_area 244 +#define __NR_io_setup 245 +#define __NR_io_destroy 246 +#define __NR_io_getevents 247 +#define __NR_io_submit 248 +#define __NR_io_cancel 249 +#define __NR_fadvise64 250 + +#define __NR_exit_group 252 +#define __NR_lookup_dcookie 253 +#define __NR_epoll_create 254 +#define __NR_epoll_ctl 255 +#define __NR_epoll_wait 256 +#define __NR_remap_file_pages 257 +#define __NR_set_tid_address 258 +#define __NR_timer_create 259 +#define __NR_timer_settime (__NR_timer_create+1) +#define __NR_timer_gettime (__NR_timer_create+2) +#define __NR_timer_getoverrun (__NR_timer_create+3) +#define __NR_timer_delete (__NR_timer_create+4) +#define __NR_clock_settime (__NR_timer_create+5) +#define __NR_clock_gettime (__NR_timer_create+6) +#define __NR_clock_getres (__NR_timer_create+7) +#define __NR_clock_nanosleep (__NR_timer_create+8) +#define __NR_statfs64 268 +#define __NR_fstatfs64 269 +#define __NR_tgkill 270 +#define __NR_utimes 271 +#define __NR_fadvise64_64 272 +#define __NR_vserver 273 +#define __NR_mbind 274 +#define __NR_get_mempolicy 275 +#define __NR_set_mempolicy 276 +#define __NR_mq_open 277 +#define __NR_mq_unlink (__NR_mq_open+1) +#define __NR_mq_timedsend (__NR_mq_open+2) +#define __NR_mq_timedreceive (__NR_mq_open+3) +#define __NR_mq_notify (__NR_mq_open+4) +#define __NR_mq_getsetattr (__NR_mq_open+5) +#define __NR_kexec_load 283 + +#define NR_syscalls 284 + +/* user-visible error numbers are in the range -1 - -124: see */ + +#define __syscall_return(type, res) \ +do { \ + if ((unsigned long)(res) >= (unsigned long)(-125)) { \ + /* Avoid using "res" which is declared to be in register r0; \ + errno might expand to a function call and clobber it. */ \ + int __err = -(res); \ + errno = __err; \ + res = -1; \ + } \ + return (type) (res); \ +} while (0) + +#define _syscall0(type,name) \ +type name(void) \ +{ \ +register long __scno __asm__ ("r7") = __NR_##name; \ +register long __res __asm__("r0"); \ +__asm__ __volatile__ (\ + "trap #" SYSCALL_VECTOR \ + : "=r" (__res) \ + : "r" (__scno) \ + : "memory"); \ +__syscall_return(type,__res); \ +} + +#define _syscall1(type,name,type1,arg1) \ +type name(type1 arg1) \ +{ \ +register long __scno __asm__ ("r7") = __NR_##name; \ +register long __res __asm__ ("r0") = (long)(arg1); \ +__asm__ __volatile__ (\ + "trap #" SYSCALL_VECTOR \ + : "=r" (__res) \ + : "r" (__scno), "0" (__res) \ + : "memory"); \ +__syscall_return(type,__res); \ +} + +#define _syscall2(type,name,type1,arg1,type2,arg2) \ +type name(type1 arg1,type2 arg2) \ +{ \ +register long __scno __asm__ ("r7") = __NR_##name; \ +register long __arg2 __asm__ ("r1") = (long)(arg2); \ +register long __res __asm__ ("r0") = (long)(arg1); \ +__asm__ __volatile__ (\ + "trap #" SYSCALL_VECTOR \ + : "=r" (__res) \ + : "r" (__scno), "0" (__res), "r" (__arg2) \ + : "memory"); \ +__syscall_return(type,__res); \ +} + +#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ +type name(type1 arg1,type2 arg2,type3 arg3) \ +{ \ +register long __scno __asm__ ("r7") = __NR_##name; \ +register long __arg3 __asm__ ("r2") = (long)(arg3); \ +register long __arg2 __asm__ ("r1") = (long)(arg2); \ +register long __res __asm__ ("r0") = (long)(arg1); \ +__asm__ __volatile__ (\ + "trap #" SYSCALL_VECTOR \ + : "=r" (__res) \ + : "r" (__scno), "0" (__res), "r" (__arg2), \ + "r" (__arg3) \ + : "memory"); \ +__syscall_return(type,__res); \ +} + +#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ +type name(type1 arg1,type2 arg2,type3 arg3,type4 arg4) \ +{ \ +register long __scno __asm__ ("r7") = __NR_##name; \ +register long __arg4 __asm__ ("r3") = (long)(arg4); \ +register long __arg3 __asm__ ("r2") = (long)(arg3); \ +register long __arg2 __asm__ ("r1") = (long)(arg2); \ +register long __res __asm__ ("r0") = (long)(arg1); \ +__asm__ __volatile__ (\ + "trap #" SYSCALL_VECTOR \ + : "=r" (__res) \ + : "r" (__scno), "0" (__res), "r" (__arg2), \ + "r" (__arg3), "r" (__arg4) \ + : "memory"); \ +__syscall_return(type,__res); \ +} + +#define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ +type name(type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5) \ +{ \ +register long __scno __asm__ ("r7") = __NR_##name; \ +register long __arg5 __asm__ ("r4") = (long)(arg5); \ +register long __arg4 __asm__ ("r3") = (long)(arg4); \ +register long __arg3 __asm__ ("r2") = (long)(arg3); \ +register long __arg2 __asm__ ("r1") = (long)(arg2); \ +register long __res __asm__ ("r0") = (long)(arg1); \ +__asm__ __volatile__ (\ + "trap #" SYSCALL_VECTOR \ + : "=r" (__res) \ + : "r" (__scno), "0" (__res), "r" (__arg2), \ + "r" (__arg3), "r" (__arg4), "r" (__arg5) \ + : "memory"); \ +__syscall_return(type,__res); \ +} + +#ifdef __KERNEL__ +#define __ARCH_WANT_IPC_PARSE_VERSION +#define __ARCH_WANT_OLD_READDIR +#define __ARCH_WANT_OLD_STAT +#define __ARCH_WANT_STAT64 +#define __ARCH_WANT_SYS_ALARM +#define __ARCH_WANT_SYS_GETHOSTNAME +#define __ARCH_WANT_SYS_PAUSE +#define __ARCH_WANT_SYS_SGETMASK +#define __ARCH_WANT_SYS_SIGNAL +#define __ARCH_WANT_SYS_TIME +#define __ARCH_WANT_SYS_UTIME +#define __ARCH_WANT_SYS_WAITPID +#define __ARCH_WANT_SYS_SOCKETCALL +#define __ARCH_WANT_SYS_FADVISE64 +#define __ARCH_WANT_SYS_GETPGRP +#define __ARCH_WANT_SYS_LLSEEK +#define __ARCH_WANT_SYS_NICE +#define __ARCH_WANT_SYS_OLD_GETRLIMIT +#define __ARCH_WANT_SYS_OLDUMOUNT +#define __ARCH_WANT_SYS_SIGPENDING +#define __ARCH_WANT_SYS_SIGPROCMASK +#define __ARCH_WANT_SYS_RT_SIGACTION +#endif + +#ifdef __KERNEL_SYSCALLS__ + +#include +#include +#include +#include + +/* + * we need this inline - forking from kernel space will result + * in NO COPY ON WRITE (!!!), until an execve is executed. This + * is no problem, but for the stack. This is handled by not letting + * main() use the stack at all after fork(). Thus, no function + * calls - which means inline code for fork too, as otherwise we + * would use the stack upon exit from 'fork()'. + * + * Actually only pause and fork are needed inline, so that there + * won't be any messing with the stack from main(), but we define + * some others too. + */ +static __inline__ _syscall3(int,execve,const char *,file,char **,argv,char **,envp) + +asmlinkage int sys_modify_ldt(int func, void __user *ptr, unsigned long bytecount); +asmlinkage long sys_mmap2(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff); +asmlinkage int sys_execve(struct pt_regs regs); +asmlinkage int sys_clone(struct pt_regs regs); +asmlinkage int sys_fork(struct pt_regs regs); +asmlinkage int sys_vfork(struct pt_regs regs); +asmlinkage int sys_pipe(unsigned long __user *fildes); +asmlinkage int sys_ptrace(long request, long pid, long addr, long data); +asmlinkage long sys_iopl(unsigned long unused); +struct sigaction; +asmlinkage long sys_rt_sigaction(int sig, + const struct sigaction __user *act, + struct sigaction __user *oact, + size_t sigsetsize); + +#endif /* __KERNEL_SYSCALLS__ */ + +/* + * "Conditional" syscalls + * + * What we want is __attribute__((weak,alias("sys_ni_syscall"))), + * but it doesn't work on all toolchains, so we just do it by hand + */ +#ifndef cond_syscall +#define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall"); +#endif + +#endif /* _ASM_M32R_UNISTD_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/user.h 2004-09-03 00:57:17.473382904 -0700 @@ -0,0 +1,59 @@ +#ifndef _ASM_M32R_USER_H +#define _ASM_M32R_USER_H + +/* $Id$ */ + +/* orig : sh 2.4.18 + * mod : remove fpu registers + */ + +#include +#include +#include +#include + +/* + * Core file format: The core file is written in such a way that gdb + * can understand it and provide useful information to the user (under + * linux we use the `trad-core' bfd). + * + * The actual file contents are as follows: + * UPAGE: 1 page consisting of a user struct that tells gdb + * what is present in the file. Directly after this is a + * copy of the task_struct, which is currently not used by gdb, + * but it may come in handy at some point. All of the registers + * are stored as part of the upage. The upage should always be + * only one page. + * DATA: The data area is stored. We use current->end_text to + * current->brk to pick up all of the user variables, plus any memory + * that may have been sbrk'ed. No attempt is made to determine if a + * page is demand-zero or if a page is totally unused, we just cover + * the entire range. All of the addresses are rounded in such a way + * that an integral number of pages is written. + * STACK: We need the stack information in order to get a meaningful + * backtrace. We need to write the data from usp to + * current->start_stack, so we round each of these off in order to be + * able to write an integer number of pages. + */ + +struct user { + struct pt_regs regs; /* entire machine state */ + size_t u_tsize; /* text size (pages) */ + size_t u_dsize; /* data size (pages) */ + size_t u_ssize; /* stack size (pages) */ + unsigned long start_code; /* text starting address */ + unsigned long start_data; /* data starting address */ + unsigned long start_stack; /* stack starting address */ + long int signal; /* signal causing core dump */ + struct regs * u_ar0; /* help gdb find registers */ + unsigned long magic; /* identifies a core file */ + char u_comm[32]; /* user command name */ +}; + +#define NBPG PAGE_SIZE +#define UPAGES 1 +#define HOST_TEXT_START_ADDR (u.start_code) +#define HOST_DATA_START_ADDR (u.start_data) +#define HOST_STACK_END_ADDR (u.start_stack + u.u_ssize * NBPG) + +#endif /* _ASM_M32R_USER_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/vga.h 2004-09-03 00:57:17.473382904 -0700 @@ -0,0 +1,22 @@ +#ifndef _ASM_M32R_VGA_H +#define _ASM_M32R_VGA_H + +/* $Id$ */ + +/* + * Access to VGA videoram + * + * (c) 1998 Martin Mares + */ + +/* + * On the PC, we can just recalculate addresses and then + * access the videoram directly without any black magic. + */ + +#define VGA_MAP_MEM(x) (unsigned long)phys_to_virt(x) + +#define vga_readb(x) (*(x)) +#define vga_writeb(x,y) (*(y) = (x)) + +#endif /* _ASM_M32R_VGA_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-m32r/xor.h 2004-09-03 00:57:17.473382904 -0700 @@ -0,0 +1,8 @@ +#ifndef _ASM_M32R_XOR_H +#define _ASM_M32R_XOR_H + +/* $Id$ */ + +#include + +#endif /* _ASM_M32R_XOR_H */ --- linux-2.6.9-rc1/include/asm-m68k/hardirq.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/include/asm-m68k/hardirq.h 2004-09-03 00:56:41.021924368 -0700 @@ -35,20 +35,6 @@ typedef struct { #define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) #define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) -#define __MASK(x) ((1UL << (x))-1) - -#define PREEMPT_MASK (__MASK(PREEMPT_BITS) << PREEMPT_SHIFT) -#define HARDIRQ_MASK (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) -#define SOFTIRQ_MASK (__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) - -#define hardirq_count() (preempt_count() & HARDIRQ_MASK) -#define softirq_count() (preempt_count() & SOFTIRQ_MASK) -#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) - -#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) -#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) -#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) - /* * The hardirq mask has to be large enough to have * space for potentially all IRQ sources in the system @@ -58,27 +44,7 @@ typedef struct { # error HARDIRQ_BITS is too low! #endif -/* - * Are we doing bottom half or hardware interrupt processing? - * Are we in a softirq context? Interrupt context? - */ -#define in_irq() (hardirq_count()) -#define in_softirq() (softirq_count()) -#define in_interrupt() (irq_count()) - - -#define hardirq_trylock() (!in_interrupt()) -#define hardirq_endlock() do { } while (0) - #define irq_enter() (preempt_count() += HARDIRQ_OFFSET) - -#ifdef CONFIG_PREEMPT -# define in_atomic() (preempt_count() != kernel_locked()) -# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) -#else -# define in_atomic() (preempt_count() != 0) -# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET -#endif #define irq_exit() \ do { \ preempt_count() -= IRQ_EXIT_OFFSET; \ @@ -87,6 +53,4 @@ do { \ preempt_enable_no_resched(); \ } while (0) -#define synchronize_irq(irq) barrier() - #endif --- linux-2.6.9-rc1/include/asm-m68knommu/hardirq.h 2004-08-24 00:01:53.000000000 -0700 +++ 25/include/asm-m68knommu/hardirq.h 2004-09-03 00:56:41.021924368 -0700 @@ -36,20 +36,6 @@ typedef struct { #define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) #define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) -#define __MASK(x) ((1UL << (x))-1) - -#define PREEMPT_MASK (__MASK(PREEMPT_BITS) << PREEMPT_SHIFT) -#define HARDIRQ_MASK (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) -#define SOFTIRQ_MASK (__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) - -#define hardirq_count() (preempt_count() & HARDIRQ_MASK) -#define softirq_count() (preempt_count() & SOFTIRQ_MASK) -#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) - -#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) -#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) -#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) - /* * The hardirq mask has to be large enough to have * space for potentially all IRQ sources in the system @@ -59,33 +45,7 @@ typedef struct { # error HARDIRQ_BITS is too low! #endif -/* - * Are we doing bottom half or hardware interrupt processing? - * Are we in a softirq context? Interrupt context? - */ -#define in_irq() (hardirq_count()) -#define in_softirq() (softirq_count()) -#define in_interrupt() (irq_count()) - -#define hardirq_trylock() (!in_interrupt()) -#define hardirq_endlock() do { } while (0) - #define irq_enter() (preempt_count() += HARDIRQ_OFFSET) - -#ifdef CONFIG_PREEMPT -# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) -#else -# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET -#endif - -#ifdef CONFIG_PREEMPT -# define in_atomic() (preempt_count() != kernel_locked()) -# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) -#else -# define in_atomic() (preempt_count() != 0) -# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET -#endif - #define irq_exit() \ do { \ preempt_count() -= IRQ_EXIT_OFFSET; \ @@ -94,10 +54,4 @@ do { \ preempt_enable_no_resched(); \ } while (0) -#ifndef CONFIG_SMP -# define synchronize_irq(irq) barrier() -#else -# error m68knommu SMP is not available -#endif /* CONFIG_SMP */ - #endif /* __M68K_HARDIRQ_H */ --- linux-2.6.9-rc1/include/asm-m68knommu/page.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/include/asm-m68knommu/page.h 2004-09-03 00:56:41.913788784 -0700 @@ -96,6 +96,8 @@ extern unsigned long memory_end; #endif /* __ASSEMBLY__ */ +#define devmem_is_allowed(x) 1 + #endif /* __KERNEL__ */ #endif /* _M68KNOMMU_PAGE_H */ --- linux-2.6.9-rc1/include/asm-m68knommu/ptrace.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/include/asm-m68knommu/ptrace.h 2004-09-02 20:51:52.000000000 -0700 @@ -84,6 +84,7 @@ struct switch_stack { #define user_mode(regs) (!((regs)->sr & PS_S)) #define instruction_pointer(regs) ((regs)->pc) +#define profile_pc(regs) instruction_pointer(regs) extern void show_regs(struct pt_regs *); #endif /* __KERNEL__ */ #endif /* __ASSEMBLY__ */ --- linux-2.6.9-rc1/include/asm-m68knommu/semaphore.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/include/asm-m68knommu/semaphore.h 2004-09-02 20:51:52.000000000 -0700 @@ -26,21 +26,14 @@ struct semaphore { atomic_t count; atomic_t waking; wait_queue_head_t wait; -#if WAITQUEUE_DEBUG - long __magic; -#endif }; -#if WAITQUEUE_DEBUG -# define __SEM_DEBUG_INIT(name) \ - , (long)&(name).__magic -#else -# define __SEM_DEBUG_INIT(name) -#endif - -#define __SEMAPHORE_INITIALIZER(name,count) \ -{ ATOMIC_INIT(count), ATOMIC_INIT(0), __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ - __SEM_DEBUG_INIT(name) } +#define __SEMAPHORE_INITIALIZER(name, n) \ +{ \ + .count = ATOMIC_INIT(n), \ + .waking = ATOMIC_INIT(0), \ + .wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ +} #define __MUTEX_INITIALIZER(name) \ __SEMAPHORE_INITIALIZER(name,1) @@ -85,9 +78,6 @@ extern spinlock_t semaphore_wake_lock; */ extern inline void down(struct semaphore * sem) { -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); __asm__ __volatile__( "| atomic down operation\n\t" @@ -105,9 +95,6 @@ extern inline int down_interruptible(str { int ret; -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); __asm__ __volatile__( "| atomic down operation\n\t" @@ -128,10 +115,6 @@ extern inline int down_trylock(struct se register struct semaphore *sem1 __asm__ ("%a1") = sem; register int result __asm__ ("%d0"); -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - __asm__ __volatile__( "| atomic down trylock operation\n\t" "subql #1,%1@\n\t" @@ -157,10 +140,6 @@ extern inline int down_trylock(struct se */ extern inline void up(struct semaphore * sem) { -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - __asm__ __volatile__( "| atomic up operation\n\t" "movel %0, %%a1\n\t" --- linux-2.6.9-rc1/include/asm-m68knommu/uaccess.h 2004-08-24 00:03:19.000000000 -0700 +++ 25/include/asm-m68knommu/uaccess.h 2004-09-02 20:51:52.000000000 -0700 @@ -134,6 +134,8 @@ extern int __get_user_bad(void); #define __copy_from_user(to, from, n) copy_from_user(to, from, n) #define __copy_to_user(to, from, n) copy_to_user(to, from, n) +#define __copy_to_user_inatomic __copy_to_user +#define __copy_from_user_inatomic __copy_from_user #define copy_to_user_ret(to,from,n,retval) ({ if (copy_to_user(to,from,n)) return retval; }) --- linux-2.6.9-rc1/include/asm-m68k/page.h 2004-08-24 00:03:49.000000000 -0700 +++ 25/include/asm-m68k/page.h 2004-09-03 00:56:41.913788784 -0700 @@ -190,6 +190,8 @@ static inline void *__va(unsigned long x #define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) +#define devmem_is_allowed(x) 1 + #endif /* __KERNEL__ */ #endif /* _M68K_PAGE_H */ --- linux-2.6.9-rc1/include/asm-m68k/ptrace.h 2004-08-24 00:03:49.000000000 -0700 +++ 25/include/asm-m68k/ptrace.h 2004-09-02 20:51:52.000000000 -0700 @@ -73,6 +73,7 @@ struct switch_stack { #define user_mode(regs) (!((regs)->sr & PS_S)) #define instruction_pointer(regs) ((regs)->pc) +#define profile_pc(regs) instruction_pointer(regs) extern void show_regs(struct pt_regs *); #endif /* __KERNEL__ */ #endif /* __ASSEMBLY__ */ --- linux-2.6.9-rc1/include/asm-m68k/semaphore.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/include/asm-m68k/semaphore.h 2004-09-02 20:51:52.000000000 -0700 @@ -27,21 +27,14 @@ struct semaphore { atomic_t count; atomic_t waking; wait_queue_head_t wait; -#ifdef WAITQUEUE_DEBUG - long __magic; -#endif }; -#ifdef WAITQUEUE_DEBUG -# define __SEM_DEBUG_INIT(name) \ - , (long)&(name).__magic -#else -# define __SEM_DEBUG_INIT(name) -#endif - -#define __SEMAPHORE_INITIALIZER(name,count) \ -{ ATOMIC_INIT(count), ATOMIC_INIT(0), __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ - __SEM_DEBUG_INIT(name) } +#define __SEMAPHORE_INITIALIZER(name, n) \ +{ \ + .count = ATOMIC_INIT(n), \ + .waking = ATOMIC_INIT(0), \ + .wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ +} #define __MUTEX_INITIALIZER(name) \ __SEMAPHORE_INITIALIZER(name,1) @@ -86,9 +79,6 @@ static inline void down(struct semaphore { register struct semaphore *sem1 __asm__ ("%a1") = sem; -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); __asm__ __volatile__( "| atomic down operation\n\t" @@ -109,9 +99,6 @@ static inline int down_interruptible(str register struct semaphore *sem1 __asm__ ("%a1") = sem; register int result __asm__ ("%d0"); -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); __asm__ __volatile__( "| atomic interruptible down operation\n\t" @@ -134,10 +121,6 @@ static inline int down_trylock(struct se register struct semaphore *sem1 __asm__ ("%a1") = sem; register int result __asm__ ("%d0"); -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - __asm__ __volatile__( "| atomic down trylock operation\n\t" "subql #1,%1@\n\t" @@ -164,10 +147,6 @@ static inline void up(struct semaphore * { register struct semaphore *sem1 __asm__ ("%a1") = sem; -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - __asm__ __volatile__( "| atomic up operation\n\t" "addql #1,%0@\n\t" --- linux-2.6.9-rc1/include/asm-m68k/system.h 2004-08-24 00:03:19.000000000 -0700 +++ 25/include/asm-m68k/system.h 2004-09-03 00:56:41.022924216 -0700 @@ -51,7 +51,7 @@ asmlinkage void resume(void); #if 0 #define local_irq_enable() asm volatile ("andiw %0,%%sr": : "i" (ALLOWINT) : "memory") #else -#include +#include #define local_irq_enable() ({ \ if (MACH_IS_Q40 || !hardirq_count()) \ asm volatile ("andiw %0,%%sr": : "i" (ALLOWINT) : "memory"); \ --- linux-2.6.9-rc1/include/asm-m68k/uaccess.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/include/asm-m68k/uaccess.h 2004-09-02 20:51:52.000000000 -0700 @@ -521,6 +521,9 @@ __constant_copy_from_user(void *to, cons : "0"(to), "1"(from), "2"(n/4) \ : "d0", "memory") +#define __copy_to_user_inatomic __copy_to_user +#define __copy_from_user_inatomic __copy_from_user + static inline unsigned long __constant_copy_to_user(void *to, const void *from, unsigned long n) { --- linux-2.6.9-rc1/include/asm-mips/errno.h 2004-08-24 00:02:48.000000000 -0700 +++ 25/include/asm-mips/errno.h 2004-09-03 00:56:54.492876472 -0700 @@ -110,6 +110,10 @@ */ #define ENOMEDIUM 159 /* No medium found */ #define EMEDIUMTYPE 160 /* Wrong medium type */ +#define ENOKEY 161 /* Required key not available */ +#define EKEYEXPIRED 162 /* Key has expired */ +#define EKEYREVOKED 163 /* Key has been revoked */ +#define EKEYREJECTED 164 /* Key was rejected by service */ #define EDQUOT 1133 /* Quota exceeded */ --- linux-2.6.9-rc1/include/asm-mips/hardirq.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/include/asm-mips/hardirq.h 2004-09-03 00:56:41.022924216 -0700 @@ -43,20 +43,6 @@ typedef struct { #define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) #define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) -#define __MASK(x) ((1UL << (x))-1) - -#define PREEMPT_MASK (__MASK(PREEMPT_BITS) << PREEMPT_SHIFT) -#define HARDIRQ_MASK (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) -#define SOFTIRQ_MASK (__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) - -#define hardirq_count() (preempt_count() & HARDIRQ_MASK) -#define softirq_count() (preempt_count() & SOFTIRQ_MASK) -#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) - -#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) -#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) -#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) - /* * The hardirq mask has to be large enough to have * space for potentially all IRQ sources in the system @@ -66,27 +52,7 @@ typedef struct { # error HARDIRQ_BITS is too low! #endif -/* - * Are we doing bottom half or hardware interrupt processing? - * Are we in a softirq context? Interrupt context? - */ -#define in_irq() (hardirq_count()) -#define in_softirq() (softirq_count()) -#define in_interrupt() (irq_count()) - -#define hardirq_trylock() (!in_interrupt()) -#define hardirq_endlock() do { } while (0) - #define irq_enter() (preempt_count() += HARDIRQ_OFFSET) - -#ifdef CONFIG_PREEMPT -# include -# define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != kernel_locked()) -# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) -#else -# define in_atomic() (preempt_count() != 0) -# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET -#endif #define irq_exit() \ do { \ preempt_count() -= IRQ_EXIT_OFFSET; \ @@ -95,10 +61,4 @@ do { preempt_enable_no_resched(); \ } while (0) -#ifndef CONFIG_SMP -# define synchronize_irq(irq) barrier() -#else - extern void synchronize_irq(unsigned int irq); -#endif /* CONFIG_SMP */ - #endif /* _ASM_HARDIRQ_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-mips/lockmeter.h 2004-09-03 00:56:42.831649248 -0700 @@ -0,0 +1,126 @@ +/* + * Copyright (C) 1999,2000 Silicon Graphics, Inc. + * + * Written by John Hawkes (hawkes@sgi.com) + * Based on klstat.h by Jack Steiner (steiner@sgi.com) + * Ported to mips32 for Asita Technologies + * by D.J. Barrow ( dj.barrow@asitatechnologies.com ) + */ +#ifndef _ASM_LOCKMETER_H +#define _ASM_LOCKMETER_H + +/* do_gettimeoffset is a function pointer on mips */ +/* & it is not included by */ +#include +#include +#include + +#define SPINLOCK_MAGIC_INIT /* */ + +#define CPU_CYCLE_FREQUENCY get_cpu_cycle_frequency() + +#define THIS_CPU_NUMBER smp_processor_id() + +static uint32_t cpu_cycle_frequency = 0; + +static uint32_t get_cpu_cycle_frequency(void) +{ + /* a total hack, slow and invasive, but ... it works */ + int sec; + uint32_t start_cycles; + struct timeval tv; + + if (cpu_cycle_frequency == 0) { /* uninitialized */ + do_gettimeofday(&tv); + sec = tv.tv_sec; /* set up to catch the tv_sec rollover */ + while (sec == tv.tv_sec) { do_gettimeofday(&tv); } + sec = tv.tv_sec; /* rolled over to a new sec value */ + start_cycles = get_cycles(); + while (sec == tv.tv_sec) { do_gettimeofday(&tv); } + cpu_cycle_frequency = get_cycles() - start_cycles; + } + + return cpu_cycle_frequency; +} + +extern struct timeval xtime; + +static uint64_t get_cycles64(void) +{ + static uint64_t last_get_cycles64 = 0; + uint64_t ret; + unsigned long sec; + unsigned long usec, usec_offset; + +again: + sec = xtime.tv_sec; + usec = xtime.tv_usec; + usec_offset = do_gettimeoffset(); + if ((xtime.tv_sec != sec) || + (xtime.tv_usec != usec)|| + (usec_offset >= 20000)) + goto again; + + ret = ((uint64_t)(usec + usec_offset) * cpu_cycle_frequency); + /* We can't do a normal 64 bit division on mips without libgcc.a */ + do_div(ret,1000000); + ret += ((uint64_t)sec * cpu_cycle_frequency); + + /* XXX why does time go backwards? do_gettimeoffset? general time adj? */ + if (ret <= last_get_cycles64) + ret = last_get_cycles64+1; + last_get_cycles64 = ret; + + return ret; +} + +/* + * macros to cache and retrieve an index value inside of a lock + * these macros assume that there are less than 65536 simultaneous + * (read mode) holders of a rwlock. + * we also assume that the hash table has less than 32767 entries. + * the high order bit is used for write locking a rw_lock + */ +#define INDEX_MASK 0x7FFF0000 +#define READERS_MASK 0x0000FFFF +#define INDEX_SHIFT 16 +#define PUT_INDEX(lockp,index) \ + lockp->lock = (((lockp->lock) & ~INDEX_MASK) | (index) << INDEX_SHIFT) +#define GET_INDEX(lockp) \ + (((lockp->lock) & INDEX_MASK) >> INDEX_SHIFT) + +/* + * macros to cache and retrieve an index value in a read/write lock + * as well as the cpu where a reader busy period started + * we use the 2nd word (the debug word) for this, so require the + * debug word to be present + */ +/* + * instrumented rwlock structure -- never used to allocate storage + * only used in macros below to overlay a rwlock_t + */ +typedef struct inst_rwlock_s { + volatile int lock; + unsigned short index; + unsigned short cpu; +} inst_rwlock_t; +#define PUT_RWINDEX(rwlock_ptr,indexv) ((inst_rwlock_t *)(rwlock_ptr))->index = indexv +#define GET_RWINDEX(rwlock_ptr) ((inst_rwlock_t *)(rwlock_ptr))->index +#define PUT_RW_CPU(rwlock_ptr,cpuv) ((inst_rwlock_t *)(rwlock_ptr))->cpu = cpuv +#define GET_RW_CPU(rwlock_ptr) ((inst_rwlock_t *)(rwlock_ptr))->cpu + +/* + * return the number of readers for a rwlock_t + */ +#define RWLOCK_READERS(rwlock_ptr) rwlock_readers(rwlock_ptr) + +extern inline int rwlock_readers(rwlock_t *rwlock_ptr) +{ + int tmp = (int) rwlock_ptr->lock; + return (tmp >= 0) ? tmp : 0; +} + +#define RWLOCK_IS_WRITE_LOCKED(rwlock_ptr) ((rwlock_ptr)->lock < 0) +#define RWLOCK_IS_READ_LOCKED(rwlock_ptr) ((rwlock_ptr)->lock > 0) + +#endif /* _ASM_LOCKMETER_H */ --- linux-2.6.9-rc1/include/asm-mips/page.h 2004-08-24 00:02:26.000000000 -0700 +++ 25/include/asm-mips/page.h 2004-09-03 00:56:41.913788784 -0700 @@ -137,4 +137,6 @@ static __inline__ int get_order(unsigned #define WANT_PAGE_VIRTUAL #endif +#define devmem_is_allowed(x) 1 + #endif /* _ASM_PAGE_H */ --- linux-2.6.9-rc1/include/asm-mips/ptrace.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/include/asm-mips/ptrace.h 2004-09-02 20:51:52.000000000 -0700 @@ -66,6 +66,7 @@ struct pt_regs { #define user_mode(regs) (((regs)->cp0_status & KU_MASK) == KU_USER) #define instruction_pointer(regs) ((regs)->cp0_epc) +#define profile_pc(regs) instruction_pointer(regs) extern void show_regs(struct pt_regs *); --- linux-2.6.9-rc1/include/asm-mips/semaphore.h 2004-08-24 00:02:32.000000000 -0700 +++ 25/include/asm-mips/semaphore.h 2004-09-02 20:51:52.000000000 -0700 @@ -37,22 +37,13 @@ struct semaphore { */ atomic_t count; wait_queue_head_t wait; -#ifdef WAITQUEUE_DEBUG - long __magic; -#endif }; -#ifdef WAITQUEUE_DEBUG -# define __SEM_DEBUG_INIT(name) \ - , (long)&(name).__magic -#else -# define __SEM_DEBUG_INIT(name) -#endif - -#define __SEMAPHORE_INITIALIZER(name, count) \ - { ATOMIC_INIT(count), \ - __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ - __SEM_DEBUG_INIT(name) } +#define __SEMAPHORE_INITIALIZER(name, n) \ +{ \ + .count = ATOMIC_INIT(n), \ + .wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ +} #define __MUTEX_INITIALIZER(name) \ __SEMAPHORE_INITIALIZER(name, 1) @@ -67,9 +58,6 @@ static inline void sema_init (struct sem { atomic_set(&sem->count, val); init_waitqueue_head(&sem->wait); -#ifdef WAITQUEUE_DEBUG - sem->__magic = (long)&sem->__magic; -#endif } static inline void init_MUTEX (struct semaphore *sem) @@ -88,9 +76,6 @@ extern void __up(struct semaphore * sem) static inline void down(struct semaphore * sem) { -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); /* @@ -104,9 +89,6 @@ static inline int down_interruptible(str { int ret = 0; -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); if (unlikely(atomic_dec_return(&sem->count) < 0)) @@ -116,19 +98,11 @@ static inline int down_interruptible(str static inline int down_trylock(struct semaphore * sem) { -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - return atomic_dec_if_positive(&sem->count) < 0; } static inline void up(struct semaphore * sem) { -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - if (unlikely(atomic_inc_return(&sem->count) <= 0)) __up(sem); } --- linux-2.6.9-rc1/include/asm-mips/spinlock.h 2004-08-24 00:03:38.000000000 -0700 +++ 25/include/asm-mips/spinlock.h 2004-09-03 00:56:42.831649248 -0700 @@ -92,9 +92,18 @@ static inline unsigned int _raw_spin_try typedef struct { volatile unsigned int lock; +#ifdef CONFIG_LOCKMETER + /* required for LOCKMETER since all bits in lock are used */ + /* and we need this storage for CPU and lock INDEX */ + unsigned lockmeter_magic; +#endif } rwlock_t; +#ifdef CONFIG_LOCKMETER +#define RW_LOCK_UNLOCKED (rwlock_t) { 0, 0 } +#else #define RW_LOCK_UNLOCKED (rwlock_t) { 0 } +#endif #define rwlock_init(x) do { *(x) = RW_LOCK_UNLOCKED; } while(0) --- linux-2.6.9-rc1/include/asm-mips/uaccess.h 2004-08-24 00:01:52.000000000 -0700 +++ 25/include/asm-mips/uaccess.h 2004-09-02 20:51:52.000000000 -0700 @@ -13,6 +13,7 @@ #include #include #include +#include /* * The fs value determines whether argument validity checking should be @@ -463,6 +464,9 @@ extern size_t __copy_user(void *__to, co __cu_len; \ }) +#define __copy_to_user_inatomic __copy_to_user +#define __copy_from_user_inatomic __copy_from_user + /* * copy_to_user: - Copy a block of data into user space. * @to: Destination address, in user space. --- linux-2.6.9-rc1/include/asm-parisc/cacheflush.h 2004-08-24 00:03:38.000000000 -0700 +++ 25/include/asm-parisc/cacheflush.h 2004-09-03 00:56:37.635439192 -0700 @@ -68,9 +68,9 @@ flush_user_icache_range(unsigned long st extern void flush_dcache_page(struct page *page); #define flush_dcache_mmap_lock(mapping) \ - spin_lock_irq(&(mapping)->tree_lock) + write_lock_irq(&(mapping)->tree_lock) #define flush_dcache_mmap_unlock(mapping) \ - spin_unlock_irq(&(mapping)->tree_lock) + write_unlock_irq(&(mapping)->tree_lock) #define flush_icache_page(vma,page) do { flush_kernel_dcache_page(page_address(page)); flush_kernel_icache_page(page_address(page)); } while (0) --- linux-2.6.9-rc1/include/asm-parisc/errno.h 2004-08-24 00:03:32.000000000 -0700 +++ 25/include/asm-parisc/errno.h 2004-09-03 00:56:54.493876320 -0700 @@ -67,6 +67,10 @@ #define EREMOTEIO 181 /* Remote I/O error */ #define ENOMEDIUM 182 /* No medium found */ #define EMEDIUMTYPE 183 /* Wrong medium type */ +#define ENOKEY 184 /* Required key not available */ +#define EKEYEXPIRED 185 /* Key has expired */ +#define EKEYREVOKED 186 /* Key has been revoked */ +#define EKEYREJECTED 187 /* Key was rejected by service */ /* We now return you to your regularly scheduled HPUX. */ --- linux-2.6.9-rc1/include/asm-parisc/hardirq.h 2004-08-24 00:03:32.000000000 -0700 +++ 25/include/asm-parisc/hardirq.h 2004-09-03 00:56:41.023924064 -0700 @@ -51,20 +51,6 @@ typedef struct { #define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) #define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) -#define __MASK(x) ((1UL << (x))-1) - -#define PREEMPT_MASK (__MASK(PREEMPT_BITS) << PREEMPT_SHIFT) -#define HARDIRQ_MASK (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) -#define SOFTIRQ_MASK (__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) - -#define hardirq_count() (preempt_count() & HARDIRQ_MASK) -#define softirq_count() (preempt_count() & SOFTIRQ_MASK) -#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) - -#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) -#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) -#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) - /* * The hardirq mask has to be large enough to have space for potentially all IRQ sources * in the system nesting on a single CPU: @@ -73,29 +59,7 @@ typedef struct { # error HARDIRQ_BITS is too low! #endif -/* - * Are we doing bottom half or hardware interrupt processing? - * Are we in a softirq context? - * Interrupt context? - */ -#define in_irq() (hardirq_count()) -#define in_softirq() (softirq_count()) -#define in_interrupt() (irq_count()) - -#define hardirq_trylock() (!in_interrupt()) -#define hardirq_endlock() do { } while (0) - #define irq_enter() (preempt_count() += HARDIRQ_OFFSET) - -#ifdef CONFIG_PREEMPT -# error CONFIG_PREEMT currently not supported. -# define in_atomic() BUG() -# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) -#else -# define in_atomic() (preempt_count() != 0) -# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET -#endif - #define irq_exit() \ do { \ preempt_count() -= IRQ_EXIT_OFFSET; \ @@ -104,10 +68,4 @@ do { \ preempt_enable_no_resched(); \ } while (0) -#ifdef CONFIG_SMP - extern void synchronize_irq (unsigned int irq); -#else -# define synchronize_irq(irq) barrier() -#endif /* CONFIG_SMP */ - #endif /* _PARISC_HARDIRQ_H */ --- linux-2.6.9-rc1/include/asm-parisc/page.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/include/asm-parisc/page.h 2004-09-03 00:56:41.914788632 -0700 @@ -157,6 +157,8 @@ extern int npmem_ranges; #define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) +#define devmem_is_allowed(x) 1 + #endif /* __KERNEL__ */ #endif /* _PARISC_PAGE_H */ --- linux-2.6.9-rc1/include/asm-parisc/ptrace.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/include/asm-parisc/ptrace.h 2004-09-02 20:51:52.000000000 -0700 @@ -48,6 +48,7 @@ struct pt_regs { /* XXX should we use iaoq[1] or iaoq[0] ? */ #define user_mode(regs) (((regs)->iaoq[0] & 3) ? 1 : 0) #define instruction_pointer(regs) ((regs)->iaoq[0] & ~3) +#define profile_pc(regs) instruction_pointer(regs) extern void show_regs(struct pt_regs *); #endif --- linux-2.6.9-rc1/include/asm-parisc/semaphore.h 2004-08-24 00:01:51.000000000 -0700 +++ 25/include/asm-parisc/semaphore.h 2004-09-02 20:51:52.000000000 -0700 @@ -40,21 +40,14 @@ struct semaphore { spinlock_t sentry; int count; wait_queue_head_t wait; -#if WAITQUEUE_DEBUG - long __magic; -#endif }; -#if WAITQUEUE_DEBUG -# define __SEM_DEBUG_INIT(name) \ - , (long)&(name).__magic -#else -# define __SEM_DEBUG_INIT(name) -#endif - -#define __SEMAPHORE_INITIALIZER(name,count) \ -{ SPIN_LOCK_UNLOCKED, count, __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ - __SEM_DEBUG_INIT(name) } +#define __SEMAPHORE_INITIALIZER(name, n) \ +{ \ + .sentry = SPIN_LOCK_UNLOCKED, \ + .count = n, \ + .wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ +} #define __MUTEX_INITIALIZER(name) \ __SEMAPHORE_INITIALIZER(name,1) @@ -95,9 +88,6 @@ asmlinkage void __up(struct semaphore * extern __inline__ void down(struct semaphore * sem) { -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); spin_lock_irq(&sem->sentry); if (sem->count > 0) { @@ -111,9 +101,6 @@ extern __inline__ void down(struct semap extern __inline__ int down_interruptible(struct semaphore * sem) { int ret = 0; -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); spin_lock_irq(&sem->sentry); if (sem->count > 0) { @@ -132,9 +119,6 @@ extern __inline__ int down_interruptible extern __inline__ int down_trylock(struct semaphore * sem) { int flags, count; -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif spin_lock_irqsave(&sem->sentry, flags); count = sem->count - 1; @@ -151,9 +135,6 @@ extern __inline__ int down_trylock(struc extern __inline__ void up(struct semaphore * sem) { int flags; -#if WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif spin_lock_irqsave(&sem->sentry, flags); if (sem->count < 0) { __up(sem); --- linux-2.6.9-rc1/include/asm-parisc/uaccess.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/include/asm-parisc/uaccess.h 2004-09-02 20:51:52.000000000 -0700 @@ -279,5 +279,7 @@ extern long lstrnlen_user(const char __u #define __copy_to_user lcopy_to_user #define copy_in_user lcopy_in_user #define __copy_in_user lcopy_in_user +#define __copy_to_user_inatomic __copy_to_user +#define __copy_from_user_inatomic __copy_from_user #endif /* __PARISC_UACCESS_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-ppc64/8253pit.h 2004-09-03 00:56:31.592357880 -0700 @@ -0,0 +1,10 @@ +/* + * 8253/8254 Programmable Interval Timer + */ + +#ifndef _8253PIT_H +#define _8253PIT_H + +#define PIT_TICK_RATE 1193182UL + +#endif --- linux-2.6.9-rc1/include/asm-ppc64/eeh.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/include/asm-ppc64/eeh.h 2004-09-03 00:56:38.376326560 -0700 @@ -44,6 +44,7 @@ struct device_node; extern void __init eeh_init(void); unsigned long eeh_check_failure(void *token, unsigned long val); +int eeh_dn_check_failure (struct device_node *dn, struct pci_dev *dev); void *eeh_ioremap(unsigned long addr, void *vaddr); void __init pci_addr_cache_build(void); @@ -89,7 +90,15 @@ int eeh_set_option(struct pci_dev *dev, */ #define EEH_POSSIBLE_IO_ERROR(val, type) ((val) == (type)~0) -/* The vaddr will equal the addr if EEH checking is disabled for +/* + * Reads from a device which has been isolated by EEH will return + * all 1s. This macro gives an all-1s value of the given size (in + * bytes: 1, 2, or 4) for comparing with the result of a read. + */ +#define EEH_IO_ERROR_VALUE(size) (~0U >> ((4 - (size)) * 8)) + +/* + * The vaddr will equal the addr if EEH checking is disabled for * this device. This is because eeh_ioremap() will not have * remapped to 0xA0, and thus both vaddr and addr will be 0xE0... */ --- linux-2.6.9-rc1/include/asm-ppc64/hardirq.h 2004-08-24 00:02:22.000000000 -0700 +++ 25/include/asm-ppc64/hardirq.h 2004-09-03 00:56:41.023924064 -0700 @@ -1,4 +1,3 @@ -#ifdef __KERNEL__ #ifndef __ASM_HARDIRQ_H #define __ASM_HARDIRQ_H @@ -43,20 +42,6 @@ typedef struct { #define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) #define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) -#define __HARDIRQ_MASK(x) ((1UL << (x))-1) - -#define PREEMPT_MASK (__HARDIRQ_MASK(PREEMPT_BITS) << PREEMPT_SHIFT) -#define SOFTIRQ_MASK (__HARDIRQ_MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) -#define HARDIRQ_MASK (__HARDIRQ_MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) - -#define hardirq_count() (preempt_count() & HARDIRQ_MASK) -#define softirq_count() (preempt_count() & SOFTIRQ_MASK) -#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) - -#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) -#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) -#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) - /* * The hardirq mask has to be large enough to have * space for potentially all IRQ sources in the system @@ -66,29 +51,7 @@ typedef struct { # error HARDIRQ_BITS is too low! #endif -/* - * Are we doing bottom half or hardware interrupt processing? - * Are we in a softirq context? Interrupt context? - */ -#define in_irq() (hardirq_count()) -#define in_softirq() (softirq_count()) -#define in_interrupt() (irq_count()) - - -#define hardirq_trylock() (!in_interrupt()) -#define hardirq_endlock() do { } while (0) - #define irq_enter() (preempt_count() += HARDIRQ_OFFSET) - -#ifdef CONFIG_PREEMPT -# define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != kernel_locked()) -# define preemptible() (preempt_count() == 0 && !irqs_disabled()) -# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) -#else -# define in_atomic() (preempt_count() != 0) -# define preemptible() 0 -# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET -#endif #define irq_exit() \ do { \ preempt_count() -= IRQ_EXIT_OFFSET; \ @@ -97,12 +60,4 @@ do { \ preempt_enable_no_resched(); \ } while (0) -#ifndef CONFIG_SMP -# define synchronize_irq(irq) barrier() -#else - extern void synchronize_irq(unsigned int irq); -#endif /* CONFIG_SMP */ - -#endif /* __KERNEL__ */ - #endif /* __ASM_HARDIRQ_H */ --- linux-2.6.9-rc1/include/asm-ppc64/hvcall.h 2004-08-24 00:01:51.000000000 -0700 +++ 25/include/asm-ppc64/hvcall.h 2004-09-03 00:56:39.031227000 -0700 @@ -101,10 +101,12 @@ #define H_VIO_SIGNAL 0x104 #define H_SEND_CRQ 0x108 #define H_COPY_RDMA 0x110 -#define H_POLL_PENDING 0x1D8 +#define H_STUFF_TCE 0x138 +#define H_PUT_TCE_INDIRECT 0x13C #define H_VTERM_PARTNER_INFO 0x150 -#define H_REGISTER_VTERM 0x154 -#define H_FREE_VTERM 0x158 +#define H_REGISTER_VTERM 0x154 +#define H_FREE_VTERM 0x158 +#define H_POLL_PENDING 0x1D8 /* plpar_hcall() -- Generic call interface using above opcodes * --- linux-2.6.9-rc1/include/asm-ppc64/hvconsole.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/include/asm-ppc64/hvconsole.h 2004-09-02 20:51:52.000000000 -0700 @@ -22,9 +22,19 @@ #ifndef _PPC64_HVCONSOLE_H #define _PPC64_HVCONSOLE_H -extern int hvc_get_chars(int index, char *buf, int count); -extern int hvc_put_chars(int index, const char *buf, int count); -extern int hvc_count(int *start_termno); +/* + * This is the max number of console adapters that can/will be found as + * console devices on first stage console init. Any number beyond this range + * can't be used as a console device but is still a valid tty device. + */ +#define MAX_NR_HVC_CONSOLES 16 -#endif /* _PPC64_HVCONSOLE_H */ +extern int hvc_get_chars(uint32_t vtermno, char *buf, int count); +extern int hvc_put_chars(uint32_t vtermno, const char *buf, int count); + +/* Early discovery of console adapters. */ +extern int hvc_find_vtys(void); +/* Implemented by a console driver */ +extern int hvc_instantiate(uint32_t vtermno, int index); +#endif /* _PPC64_HVCONSOLE_H */ --- linux-2.6.9-rc1/include/asm-ppc64/lmb.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/include/asm-ppc64/lmb.h 2004-09-03 00:56:40.095065272 -0700 @@ -47,7 +47,7 @@ struct lmb { struct lmb_region reserved; }; -extern struct lmb lmb __initdata; +extern struct lmb lmb; extern void __init lmb_init(void); extern void __init lmb_analyze(void); --- linux-2.6.9-rc1/include/asm-ppc64/mmu.h 2004-08-24 00:03:17.000000000 -0700 +++ 25/include/asm-ppc64/mmu.h 2004-09-03 00:56:38.092369728 -0700 @@ -27,16 +27,6 @@ typedef struct { #endif } mm_context_t; -#ifdef CONFIG_HUGETLB_PAGE -#define KERNEL_LOW_HPAGES .htlb_segs = 0, -#else -#define KERNEL_LOW_HPAGES -#endif - -#define KERNEL_CONTEXT(ea) ({ \ - mm_context_t ctx = { .id = REGION_ID(ea), KERNEL_LOW_HPAGES}; \ - ctx; }) - #define STE_ESID_V 0x80 #define STE_ESID_KS 0x20 #define STE_ESID_KP 0x10 @@ -208,7 +198,7 @@ extern void htab_finish_init(void); #define STAB0_PHYS_ADDR (STAB0_PAGE<node_start_pfn) --- linux-2.6.9-rc1/include/asm-ppc64/paca.h 2004-08-24 00:02:32.000000000 -0700 +++ 25/include/asm-ppc64/paca.h 2004-09-02 20:51:52.000000000 -0700 @@ -105,21 +105,6 @@ struct paca_struct { */ struct ItLpPaca lppaca __attribute__((aligned(0x80))); struct ItLpRegSave reg_save; - - /* - * iSeries profiling support - * - * FIXME: do we still want this, or can we ditch it in favour - * of oprofile? - */ - u32 *prof_buffer; /* iSeries profiling buffer */ - u32 *prof_stext; /* iSeries start of kernel text */ - u32 prof_multiplier; - u32 prof_counter; - u32 prof_shift; /* iSeries shift for profile - * bucket size */ - u32 prof_len; /* iSeries length of profile */ - u8 prof_enabled; /* 1=iSeries profiling enabled */ }; #endif /* _PPC64_PACA_H */ --- linux-2.6.9-rc1/include/asm-ppc64/page.h 2004-08-24 00:01:51.000000000 -0700 +++ 25/include/asm-ppc64/page.h 2004-09-03 00:56:41.914788632 -0700 @@ -181,8 +181,7 @@ static inline int get_order(unsigned lon #define __pa(x) ((unsigned long)(x)-PAGE_OFFSET) -/* Not 100% correct, for use by /dev/mem only */ -extern int page_is_ram(unsigned long physaddr); +extern int page_is_ram(unsigned long pfn); #endif /* __ASSEMBLY__ */ @@ -240,11 +239,14 @@ extern int page_is_ram(unsigned long phy #endif #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) +#define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) #define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT) #define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) +#define devmem_is_allowed(x) 1 + #endif /* __KERNEL__ */ #endif /* _PPC64_PAGE_H */ --- linux-2.6.9-rc1/include/asm-ppc64/pgalloc.h 2004-08-24 00:01:52.000000000 -0700 +++ 25/include/asm-ppc64/pgalloc.h 2004-09-02 20:51:52.000000000 -0700 @@ -7,6 +7,7 @@ #include #include #include +#include extern kmem_cache_t *zero_cache; --- linux-2.6.9-rc1/include/asm-ppc64/pgtable.h 2004-08-24 00:03:38.000000000 -0700 +++ 25/include/asm-ppc64/pgtable.h 2004-09-03 00:56:38.765267432 -0700 @@ -167,6 +167,7 @@ int hash_huge_page(struct mm_struct *mm, #endif /* __ASSEMBLY__ */ #define HAVE_ARCH_UNMAPPED_AREA +#define HAVE_ARCH_UNMAPPED_AREA_TOPDOWN #else #define hash_huge_page(mm,a,ea,vsid,local) -1 --- linux-2.6.9-rc1/include/asm-ppc64/processor.h 2004-08-24 00:01:53.000000000 -0700 +++ 25/include/asm-ppc64/processor.h 2004-09-03 00:56:39.961085640 -0700 @@ -20,12 +20,6 @@ #include #include -/* - * Default implementation of macro that returns current - * instruction pointer ("program counter"). - */ -#define current_text_addr() ({ __label__ _l; _l: &&_l;}) - /* Machine State Register (MSR) Fields */ #define MSR_SF_LG 63 /* Enable 64 bit mode */ #define MSR_ISF_LG 61 /* Interrupt 64b mode valid on 630 */ @@ -410,6 +404,12 @@ #define XGLUE(a,b) a##b #define GLUE(a,b) XGLUE(a,b) +/* iSeries CTRL register (for runlatch) */ + +#define CTRLT 0x098 +#define CTRLF 0x088 +#define RUNLATCH 0x0001 + #ifdef __ASSEMBLY__ #define _GLOBAL(name) \ @@ -438,8 +438,13 @@ name: \ .type GLUE(.,name),@function; \ GLUE(.,name): -#endif /* __ASSEMBLY__ */ +#else /* __ASSEMBLY__ */ +/* + * Default implementation of macro that returns current + * instruction pointer ("program counter"). + */ +#define current_text_addr() ({ __label__ _l; _l: &&_l;}) /* Macros for setting and retrieving special purpose registers */ @@ -461,20 +466,9 @@ GLUE(.,name): #define mttbl(v) asm volatile("mttbl %0":: "r"(v)) #define mttbu(v) asm volatile("mttbu %0":: "r"(v)) -/* iSeries CTRL register (for runlatch) */ - -#define CTRLT 0x098 -#define CTRLF 0x088 -#define RUNLATCH 0x0001 - -/* Size of an exception stack frame contained in the paca. */ -#define EXC_FRAME_SIZE 64 - #define mfasr() ({unsigned long rval; \ asm volatile("mfasr %0" : "=r" (rval)); rval;}) -#ifndef __ASSEMBLY__ - static inline void set_tb(unsigned int upper, unsigned int lower) { mttbl(0); @@ -485,6 +479,8 @@ static inline void set_tb(unsigned int u #define __get_SP() ({unsigned long sp; \ asm volatile("mr %0,1": "=r" (sp)); sp;}) +#ifdef __KERNEL__ + extern int have_of; struct task_struct; @@ -507,8 +503,6 @@ extern long kernel_thread(int (*fn)(void extern struct task_struct *last_task_used_math; extern struct task_struct *last_task_used_altivec; - -#ifdef __KERNEL__ /* 64-bit user address space is 41-bits (2TBs user VM) */ #define TASK_SIZE_USER64 (0x0000020000000000UL) @@ -520,8 +514,6 @@ extern struct task_struct *last_task_use #define TASK_SIZE (test_thread_flag(TIF_32BIT) ? \ TASK_SIZE_USER32 : TASK_SIZE_USER64) -#endif /* __KERNEL__ */ - /* This decides where the kernel will search for a free chunk of vm * space during mmap's. @@ -538,6 +530,7 @@ typedef struct { struct thread_struct { unsigned long ksp; /* Kernel stack pointer */ + unsigned long ksp_vsid; struct pt_regs *regs; /* Pointer to saved register state */ mm_segment_t fs; /* for get_fs() validation */ double fpr[32]; /* Complete floating point set */ @@ -626,12 +619,11 @@ static inline void prefetchw(const void #define spin_lock_prefetch(x) prefetchw(x) -#ifdef CONFIG_SCHED_SMT -#define ARCH_HAS_SCHED_DOMAIN -#define ARCH_HAS_SCHED_WAKE_IDLE -#endif +#define HAVE_ARCH_PICK_MMAP_LAYOUT + +#endif /* __KERNEL__ */ -#endif /* ASSEMBLY */ +#endif /* __ASSEMBLY__ */ /* * Number of entries in the SLB. If this ever changes we should handle --- linux-2.6.9-rc1/include/asm-ppc64/ptrace.h 2004-08-24 00:03:18.000000000 -0700 +++ 25/include/asm-ppc64/ptrace.h 2004-09-02 20:51:52.000000000 -0700 @@ -69,6 +69,7 @@ struct pt_regs32 { #define __SIGNAL_FRAMESIZE32 64 #define instruction_pointer(regs) ((regs)->nip) +#define profile_pc(regs) instruction_pointer(regs) #define user_mode(regs) ((((regs)->msr) >> MSR_PR_LG) & 0x1) #define force_successful_syscall_return() \ --- linux-2.6.9-rc1/include/asm-ppc64/rtas.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/include/asm-ppc64/rtas.h 2004-09-03 00:56:39.167206328 -0700 @@ -211,8 +211,14 @@ extern void pSeries_log_error(char *buf, #define RTAS_DEBUG KERN_DEBUG "RTAS: " #define RTAS_ERROR_LOG_MAX 2048 - - + +/* + * Return the firmware-specified size of the error log buffer + * for all rtas calls that require an error buffer argument. + * This includes 'check-exception' and 'rtas-last-error'. + */ +extern int rtas_get_error_log_max(void); + /* Event Scan Parameters */ #define EVENT_SCAN_ALL_EVENTS 0xf0000000 #define SURVEILLANCE_TOKEN 9000 --- linux-2.6.9-rc1/include/asm-ppc64/semaphore.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/include/asm-ppc64/semaphore.h 2004-09-02 20:51:52.000000000 -0700 @@ -23,22 +23,13 @@ struct semaphore { */ atomic_t count; wait_queue_head_t wait; -#ifdef WAITQUEUE_DEBUG - long __magic; -#endif }; -#ifdef WAITQUEUE_DEBUG -# define __SEM_DEBUG_INIT(name) \ - , (long)&(name).__magic -#else -# define __SEM_DEBUG_INIT(name) -#endif - -#define __SEMAPHORE_INITIALIZER(name, count) \ - { ATOMIC_INIT(count), \ - __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ - __SEM_DEBUG_INIT(name) } +#define __SEMAPHORE_INITIALIZER(name, n) \ +{ \ + .count = ATOMIC_INIT(n), \ + .wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ +} #define __MUTEX_INITIALIZER(name) \ __SEMAPHORE_INITIALIZER(name, 1) @@ -53,9 +44,6 @@ static inline void sema_init (struct sem { atomic_set(&sem->count, val); init_waitqueue_head(&sem->wait); -#ifdef WAITQUEUE_DEBUG - sem->__magic = (long)&sem->__magic; -#endif } static inline void init_MUTEX (struct semaphore *sem) @@ -74,9 +62,6 @@ extern void __up(struct semaphore * sem) static inline void down(struct semaphore * sem) { -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); /* @@ -90,9 +75,6 @@ static inline int down_interruptible(str { int ret = 0; -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); if (unlikely(atomic_dec_return(&sem->count) < 0)) @@ -102,19 +84,11 @@ static inline int down_interruptible(str static inline int down_trylock(struct semaphore * sem) { -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - return atomic_dec_if_positive(&sem->count) < 0; } static inline void up(struct semaphore * sem) { -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - if (unlikely(atomic_inc_return(&sem->count) <= 0)) __up(sem); } --- linux-2.6.9-rc1/include/asm-ppc64/smp.h 2004-08-24 00:01:50.000000000 -0700 +++ 25/include/asm-ppc64/smp.h 2004-09-02 20:51:52.000000000 -0700 @@ -36,6 +36,8 @@ extern void smp_message_recv(int, struct #define smp_processor_id() (get_paca()->paca_index) #define hard_smp_processor_id() (get_paca()->hw_cpu_id) +extern cpumask_t cpu_sibling_map[NR_CPUS]; + /* Since OpenPIC has only 4 IPIs, we use slightly different message numbers. * * Make sure this matches openpic_request_IPIs in open_pic.c, or what shows up @@ -61,7 +63,7 @@ extern int query_cpu_stopped(unsigned in #define get_hard_smp_processor_id(CPU) (paca[(CPU)].hw_cpu_id) #define set_hard_smp_processor_id(CPU, VAL) \ - do { (paca[(CPU)].hw_proc_num = (VAL)); } while (0) + do { (paca[(CPU)].hw_cpu_id = (VAL)); } while (0) #endif /* __ASSEMBLY__ */ --- linux-2.6.9-rc1/include/asm-ppc64/system.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/include/asm-ppc64/system.h 2004-09-02 20:51:52.000000000 -0700 @@ -105,6 +105,7 @@ extern int fix_alignment(struct pt_regs extern void bad_page_fault(struct pt_regs *regs, unsigned long address, int sig); extern void show_regs(struct pt_regs * regs); +extern void low_hash_fault(struct pt_regs *regs, unsigned long address); extern int die(const char *str, struct pt_regs *regs, long err); extern void flush_instruction_cache(void); --- linux-2.6.9-rc1/include/asm-ppc64/uaccess.h 2004-08-24 00:02:47.000000000 -0700 +++ 25/include/asm-ppc64/uaccess.h 2004-09-02 20:51:52.000000000 -0700 @@ -111,6 +111,9 @@ extern unsigned long search_exception_ta #define __put_user(x,ptr) \ __put_user_nocheck((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr))) +#define __get_user_unaligned __get_user +#define __put_user_unaligned __put_user + extern long __put_user_bad(void); #define __put_user_nocheck(x,ptr,size) \ @@ -281,6 +284,9 @@ extern unsigned long copy_in_user(void _ extern unsigned long __clear_user(void __user *addr, unsigned long size); +#define __copy_to_user_inatomic __copy_to_user +#define __copy_from_user_inatomic __copy_from_user + static inline unsigned long clear_user(void __user *addr, unsigned long size) { --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-ppc/8253pit.h 2004-09-03 00:56:31.591358032 -0700 @@ -0,0 +1,10 @@ +/* + * 8253/8254 Programmable Interval Timer + */ + +#ifndef _8253PIT_H +#define _8253PIT_H + +#define PIT_TICK_RATE 1193182UL + +#endif --- linux-2.6.9-rc1/include/asm-ppc/checksum.h 2004-08-24 00:02:25.000000000 -0700 +++ 25/include/asm-ppc/checksum.h 2004-09-02 20:51:52.000000000 -0700 @@ -33,7 +33,7 @@ extern unsigned int csum_partial_copy_ge int *src_err, int *dst_err); #define csum_partial_copy_from_user(src, dst, len, sum, errp) \ - csum_partial_copy_generic((src), (dst), (len), (sum), (errp), NULL) + csum_partial_copy_generic((__force void *)(src), (dst), (len), (sum), (errp), NULL) /* FIXME: this needs to be written to really do no check -- Cort */ #define csum_partial_copy_nocheck(src, dst, len, sum) \ --- linux-2.6.9-rc1/include/asm-ppc/hardirq.h 2004-08-24 00:01:51.000000000 -0700 +++ 25/include/asm-ppc/hardirq.h 2004-09-03 00:56:41.024923912 -0700 @@ -44,20 +44,6 @@ typedef struct { #define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) #define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) -#define __MASK(x) ((1UL << (x))-1) - -#define PREEMPT_MASK (__MASK(PREEMPT_BITS) << PREEMPT_SHIFT) -#define HARDIRQ_MASK (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) -#define SOFTIRQ_MASK (__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) - -#define hardirq_count() (preempt_count() & HARDIRQ_MASK) -#define softirq_count() (preempt_count() & SOFTIRQ_MASK) -#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) - -#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) -#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) -#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) - /* * The hardirq mask has to be large enough to have * space for potentially all IRQ sources in the system @@ -67,31 +53,7 @@ typedef struct { # error HARDIRQ_BITS is too low! #endif -/* - * Are we doing bottom half or hardware interrupt processing? - * Are we in a softirq context? Interrupt context? - */ -#define in_irq() (hardirq_count()) -#define in_softirq() (softirq_count()) -#define in_interrupt() (irq_count()) - - -#define hardirq_trylock() (!in_interrupt()) -#define hardirq_endlock() do { } while (0) - #define irq_enter() (preempt_count() += HARDIRQ_OFFSET) - -#ifdef CONFIG_PREEMPT -# define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != kernel_locked()) -# define preemptible() (preempt_count() == 0 && !irqs_disabled()) -# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) - -#else -# define in_atomic() (preempt_count() != 0) -# define preemptible() 0 -# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET -#endif - #define irq_exit() \ do { \ preempt_count() -= IRQ_EXIT_OFFSET; \ @@ -100,11 +62,5 @@ do { \ preempt_enable_no_resched(); \ } while (0) -#ifndef CONFIG_SMP -# define synchronize_irq(irq) barrier() -#else - extern void synchronize_irq(unsigned int irq); -#endif /* CONFIG_SMP */ - #endif /* __ASM_HARDIRQ_H */ #endif /* __KERNEL__ */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-ppc/kexec.h 2004-09-03 00:56:59.460121336 -0700 @@ -0,0 +1,26 @@ +#ifndef _PPC_KEXEC_H +#define _PPC_KEXEC_H + +#ifdef CONFIG_KEXEC +/* + * KEXEC_SOURCE_MEMORY_LIMIT maximum page get_free_page can return. + * I.e. Maximum page that is mapped directly into kernel memory, + * and kmap is not required. + * + * Someone correct me if FIXADDR_START - PAGEOFFSET is not the correct + * calculation for the amount of memory directly mappable into the + * kernel memory space. + */ + +/* Maximum physical address we can use pages from */ +#define KEXEC_SOURCE_MEMORY_LIMIT (-1UL) +/* Maximum address we can reach in physical address mode */ +#define KEXEC_DESTINATION_MEMORY_LIMIT (-1UL) +/* Maximum address we can use for the control code buffer */ +#define KEXEC_CONTROL_MEMORY_LIMIT TASK_SIZE + +#define KEXEC_CONTROL_CODE_SIZE 4096 + +#endif /* CONFIG_KEXEC */ + +#endif /* _PPC_KEXEC_H */ --- linux-2.6.9-rc1/include/asm-ppc/mpc8260.h 2004-08-24 00:01:51.000000000 -0700 +++ 25/include/asm-ppc/mpc8260.h 2004-09-02 20:51:52.000000000 -0700 @@ -40,6 +40,10 @@ #include #endif +#ifdef CONFIG_PCI_8260 +#include +#endif + /* Make sure the memory translation stuff is there if PCI not used. */ #ifndef _IO_BASE --- linux-2.6.9-rc1/include/asm-ppc/open_pic.h 2004-08-24 00:02:25.000000000 -0700 +++ 25/include/asm-ppc/open_pic.h 2004-09-02 20:51:52.000000000 -0700 @@ -23,7 +23,7 @@ #define OPENPIC_VEC_TIMER 110 /* and up */ #define OPENPIC_VEC_IPI 118 /* and up */ -#define OPENPIC_VEC_SPURIOUS 127 +#define OPENPIC_VEC_SPURIOUS 239 /* OpenPIC IRQ controller structure */ extern struct hw_interrupt_type open_pic; --- linux-2.6.9-rc1/include/asm-ppc/page.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/include/asm-ppc/page.h 2004-09-03 00:56:41.914788632 -0700 @@ -163,5 +163,7 @@ extern __inline__ int get_order(unsigned #define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) +#define devmem_is_allowed(x) 1 + #endif /* __KERNEL__ */ #endif /* _PPC_PAGE_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-ppc/perfctr.h 2004-09-03 00:56:46.052159656 -0700 @@ -0,0 +1,170 @@ +/* $Id: perfctr.h,v 1.6 2004/05/30 14:47:34 mikpe Exp $ + * PPC32 Performance-Monitoring Counters driver + * + * Copyright (C) 2004 Mikael Pettersson + */ +#ifndef _ASM_PPC_PERFCTR_H +#define _ASM_PPC_PERFCTR_H + +/* perfctr_info.cpu_type values */ +#define PERFCTR_PPC_GENERIC 0 +#define PERFCTR_PPC_604 1 +#define PERFCTR_PPC_604e 2 +#define PERFCTR_PPC_750 3 +#define PERFCTR_PPC_7400 4 +#define PERFCTR_PPC_7450 5 + +struct perfctr_sum_ctrs { + unsigned long long tsc; + unsigned long long pmc[8]; +}; + +struct perfctr_cpu_control { + unsigned int tsc_on; + unsigned int nractrs; /* # of a-mode counters */ + unsigned int nrictrs; /* # of i-mode counters */ + unsigned int pmc_map[8]; + unsigned int evntsel[8]; /* one per counter, even on P5 */ + int ireset[8]; /* [0,0x7fffffff], for i-mode counters */ + struct { + unsigned int mmcr0; /* sans PMC{1,2}SEL */ + unsigned int mmcr2; /* only THRESHMULT */ + /* IABR/DABR/BAMR not supported */ + } ppc; + unsigned int _reserved1; + unsigned int _reserved2; + unsigned int _reserved3; + unsigned int _reserved4; +}; + +struct perfctr_cpu_state { + unsigned int cstatus; + struct { /* k1 is opaque in the user ABI */ + unsigned int id; + int isuspend_cpu; + } k1; + /* The two tsc fields must be inlined. Placing them in a + sub-struct causes unwanted internal padding on x86-64. */ + unsigned int tsc_start; + unsigned long long tsc_sum; + struct { + unsigned int map; + unsigned int start; + unsigned long long sum; + } pmc[8]; /* the size is not part of the user ABI */ +#ifdef __KERNEL__ + unsigned int ppc_mmcr[3]; + struct perfctr_cpu_control control; +#endif +}; + +/* cstatus is a re-encoding of control.tsc_on/nractrs/nrictrs + which should have less overhead in most cases */ +/* XXX: ppc driver internally also uses cstatus&(1<<30) */ + +static inline +unsigned int perfctr_mk_cstatus(unsigned int tsc_on, unsigned int nractrs, + unsigned int nrictrs) +{ + return (tsc_on<<31) | (nrictrs<<16) | ((nractrs+nrictrs)<<8) | nractrs; +} + +static inline unsigned int perfctr_cstatus_enabled(unsigned int cstatus) +{ + return cstatus; +} + +static inline int perfctr_cstatus_has_tsc(unsigned int cstatus) +{ + return (int)cstatus < 0; /* test and jump on sign */ +} + +static inline unsigned int perfctr_cstatus_nractrs(unsigned int cstatus) +{ + return cstatus & 0x7F; /* and with imm8 */ +} + +static inline unsigned int perfctr_cstatus_nrctrs(unsigned int cstatus) +{ + return (cstatus >> 8) & 0x7F; +} + +static inline unsigned int perfctr_cstatus_has_ictrs(unsigned int cstatus) +{ + return cstatus & (0x7F << 16); +} + +/* + * 'struct siginfo' support for perfctr overflow signals. + * In unbuffered mode, si_code is set to SI_PMC_OVF and a bitmask + * describing which perfctrs overflowed is put in si_pmc_ovf_mask. + * A bitmask is used since more than one perfctr can have overflowed + * by the time the interrupt handler runs. + * + * glibc's doesn't seem to define __SI_FAULT or __SI_CODE(), + * and including as well may cause redefinition errors, + * so the user and kernel values are different #defines here. + */ +#ifdef __KERNEL__ +#define SI_PMC_OVF (__SI_FAULT|'P') +#else +#define SI_PMC_OVF ('P') +#endif +#define si_pmc_ovf_mask _sifields._pad[0] /* XXX: use an unsigned field later */ + +/* version number for user-visible CPU-specific data */ +#define PERFCTR_CPU_VERSION 0 /* XXX: not yet cast in stone */ + +#ifdef __KERNEL__ + +#if defined(CONFIG_PERFCTR) + +/* Driver init/exit. */ +extern int perfctr_cpu_init(void); +extern void perfctr_cpu_exit(void); + +/* CPU type name. */ +extern char *perfctr_cpu_name; + +/* Hardware reservation. */ +extern const char *perfctr_cpu_reserve(const char *service); +extern void perfctr_cpu_release(const char *service); + +/* PRE: state has no running interrupt-mode counters. + Check that the new control data is valid. + Update the driver's private control data. + Returns a negative error code if the control data is invalid. */ +extern int perfctr_cpu_update_control(struct perfctr_cpu_state *state, int is_global); + +/* Read a-mode counters. Subtract from start and accumulate into sums. + Must be called with preemption disabled. */ +extern void perfctr_cpu_suspend(struct perfctr_cpu_state *state); + +/* Write control registers. Read a-mode counters into start. + Must be called with preemption disabled. */ +extern void perfctr_cpu_resume(struct perfctr_cpu_state *state); + +/* Perform an efficient combined suspend/resume operation. + Must be called with preemption disabled. */ +extern void perfctr_cpu_sample(struct perfctr_cpu_state *state); + +/* The type of a perfctr overflow interrupt handler. + It will be called in IRQ context, with preemption disabled. */ +typedef void (*perfctr_ihandler_t)(unsigned long pc); + +/* Operations related to overflow interrupt handling. + XXX: The hardware supports overflow interrupts, but the driver + does not yet enable this due to an erratum in 750/7400/7410. */ +#ifdef CONFIG_PERFCTR_INTERRUPT_SUPPORT +extern void perfctr_cpu_set_ihandler(perfctr_ihandler_t); +extern void perfctr_cpu_ireload(struct perfctr_cpu_state*); +extern unsigned int perfctr_cpu_identify_overflow(struct perfctr_cpu_state*); +#else +static inline void perfctr_cpu_set_ihandler(perfctr_ihandler_t x) { } +#endif + +#endif /* CONFIG_PERFCTR */ + +#endif /* __KERNEL__ */ + +#endif /* _ASM_PPC_PERFCTR_H */ --- linux-2.6.9-rc1/include/asm-ppc/processor.h 2004-08-24 00:01:51.000000000 -0700 +++ 25/include/asm-ppc/processor.h 2004-09-03 00:56:45.412256936 -0700 @@ -126,6 +126,7 @@ struct thread_struct { unsigned long spefscr; /* SPE & eFP status */ int used_spe; /* set if process has used spe */ #endif /* CONFIG_SPE */ + struct vperfctr *perfctr; /* performance counters */ }; #define ARCH_MIN_TASKALIGN 16 --- linux-2.6.9-rc1/include/asm-ppc/ptrace.h 2004-08-24 00:02:47.000000000 -0700 +++ 25/include/asm-ppc/ptrace.h 2004-09-02 20:51:52.000000000 -0700 @@ -47,6 +47,7 @@ struct pt_regs { #ifndef __ASSEMBLY__ #define instruction_pointer(regs) ((regs)->nip) +#define profile_pc(regs) instruction_pointer(regs) #define user_mode(regs) (((regs)->msr & MSR_PR) != 0) #define force_successful_syscall_return() \ --- linux-2.6.9-rc1/include/asm-ppc/reg.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/include/asm-ppc/reg.h 2004-09-03 00:56:45.413256784 -0700 @@ -269,22 +269,14 @@ #define SPRN_LDSTCR 0x3f8 /* Load/Store control register */ #define SPRN_LDSTDB 0x3f4 /* */ #define SPRN_LR 0x008 /* Link Register */ -#define SPRN_MMCR0 0x3B8 /* Monitor Mode Control Register 0 */ -#define SPRN_MMCR1 0x3BC /* Monitor Mode Control Register 1 */ #ifndef SPRN_PIR #define SPRN_PIR 0x3FF /* Processor Identification Register */ #endif -#define SPRN_PMC1 0x3B9 /* Performance Counter Register 1 */ -#define SPRN_PMC2 0x3BA /* Performance Counter Register 2 */ -#define SPRN_PMC3 0x3BD /* Performance Counter Register 3 */ -#define SPRN_PMC4 0x3BE /* Performance Counter Register 4 */ #define SPRN_PTEHI 0x3D5 /* 981 7450 PTE HI word (S/W TLB load) */ #define SPRN_PTELO 0x3D6 /* 982 7450 PTE LO word (S/W TLB load) */ #define SPRN_PVR 0x11F /* Processor Version Register */ #define SPRN_RPA 0x3D6 /* Required Physical Address Register */ -#define SPRN_SDA 0x3BF /* Sampled Data Address Register */ #define SPRN_SDR1 0x019 /* MMU Hash Base Register */ -#define SPRN_SIA 0x3BB /* Sampled Instruction Address Register */ #define SPRN_SPRG0 0x110 /* Special Purpose Register General 0 */ #define SPRN_SPRG1 0x111 /* Special Purpose Register General 1 */ #define SPRN_SPRG2 0x112 /* Special Purpose Register General 2 */ @@ -311,16 +303,79 @@ #define SPRN_THRM3 0x3FE /* Thermal Management Register 3 */ #define THRM3_E (1<<0) #define SPRN_TLBMISS 0x3D4 /* 980 7450 TLB Miss Register */ -#define SPRN_UMMCR0 0x3A8 /* User Monitor Mode Control Register 0 */ -#define SPRN_UMMCR1 0x3AC /* User Monitor Mode Control Register 0 */ -#define SPRN_UPMC1 0x3A9 /* User Performance Counter Register 1 */ -#define SPRN_UPMC2 0x3AA /* User Performance Counter Register 2 */ -#define SPRN_UPMC3 0x3AD /* User Performance Counter Register 3 */ -#define SPRN_UPMC4 0x3AE /* User Performance Counter Register 4 */ -#define SPRN_USIA 0x3AB /* User Sampled Instruction Address Register */ #define SPRN_VRSAVE 0x100 /* Vector Register Save Register */ #define SPRN_XER 0x001 /* Fixed Point Exception Register */ +/* Performance-monitoring control and counter registers */ +#define SPRN_MMCR0 0x3B8 /* Monitor Mode Control Register 0 (604 and up) */ +#define SPRN_MMCR1 0x3BC /* Monitor Mode Control Register 1 (604e and up) */ +#define SPRN_MMCR2 0x3B0 /* Monitor Mode Control Register 2 (7400 and up) */ +#define SPRN_PMC1 0x3B9 /* Performance Counter Register 1 (604 and up) */ +#define SPRN_PMC2 0x3BA /* Performance Counter Register 2 (604 and up) */ +#define SPRN_PMC3 0x3BD /* Performance Counter Register 3 (604e and up) */ +#define SPRN_PMC4 0x3BE /* Performance Counter Register 4 (604e and up) */ +#define SPRN_PMC5 0x3B1 /* Performance Counter Register 5 (7450 and up) */ +#define SPRN_PMC6 0x3B2 /* Performance Counter Register 6 (7450 and up) */ +#define SPRN_SIA 0x3BB /* Sampled Instruction Address Register (604 and up) */ +#define SPRN_SDA 0x3BF /* Sampled Data Address Register (604/604e only) */ +#define SPRN_BAMR 0x3B7 /* Breakpoint Address Mask Register (7400 and up) */ + +#define SPRN_UMMCR0 0x3A8 /* User Monitor Mode Control Register 0 (750 and up) */ +#define SPRN_UMMCR1 0x3AC /* User Monitor Mode Control Register 0 (750 and up) */ +#define SPRN_UMMCR2 0x3A0 /* User Monitor Mode Control Register 0 (7400 and up) */ +#define SPRN_UPMC1 0x3A9 /* User Performance Counter Register 1 (750 and up) */ +#define SPRN_UPMC2 0x3AA /* User Performance Counter Register 2 (750 and up) */ +#define SPRN_UPMC3 0x3AD /* User Performance Counter Register 3 (750 and up) */ +#define SPRN_UPMC4 0x3AE /* User Performance Counter Register 4 (750 and up) */ +#define SPRN_UPMC5 0x3A1 /* User Performance Counter Register 5 (7450 and up) */ +#define SPRN_UPMC6 0x3A2 /* User Performance Counter Register 5 (7450 and up) */ +#define SPRN_USIA 0x3AB /* User Sampled Instruction Address Register (750 and up) */ +#define SPRN_UBAMR 0x3A7 /* User Breakpoint Address Mask Register (7400 and up) */ + +/* MMCR0 layout (74xx terminology) */ +#define MMCR0_FC 0x80000000 /* Freeze counters unconditionally. */ +#define MMCR0_FCS 0x40000000 /* Freeze counters while MSR[PR]=0 (supervisor mode). */ +#define MMCR0_FCP 0x20000000 /* Freeze counters while MSR[PR]=1 (user mode). */ +#define MMCR0_FCM1 0x10000000 /* Freeze counters while MSR[PM]=1. */ +#define MMCR0_FCM0 0x08000000 /* Freeze counters while MSR[PM]=0. */ +#define MMCR0_PMXE 0x04000000 /* Enable performance monitor exceptions. + * Cleared by hardware when a PM exception occurs. + * 604: PMXE is not cleared by hardware. + */ +#define MMCR0_FCECE 0x02000000 /* Freeze counters on enabled condition or event. + * FCECE is treated as 0 if TRIGGER is 1. + * 74xx: FC is set when the event occurs. + * 604/750: ineffective when PMXE=0. + */ +#define MMCR0_TBSEL 0x01800000 /* Time base lower (TBL) bit selector. + * 00: bit 31, 01: bit 23, 10: bit 19, 11: bit 15. + */ +#define MMCR0_TBEE 0x00400000 /* Enable event on TBL bit transition from 0 to 1. */ +#define MMCR0_THRESHOLD 0x003F0000 /* Threshold value for certain events. */ +#define MMCR0_PMC1CE 0x00008000 /* Enable event on PMC1 overflow. */ +#define MMCR0_PMCjCE 0x00004000 /* Enable event on PMC2-PMC6 overflow. + * 604/750: Overrides FCECE (DISCOUNT). + */ +#define MMCR0_TRIGGER 0x00002000 /* Disable PMC2-PMC6 until PMC1 overflow or other event. + * 74xx: cleared by hardware when the event occurs. + */ +#define MMCR0_PMC1SEL 0x00001FB0 /* PMC1 event selector, 7 bits. */ +#define MMCR0_PMC2SEL 0x0000003F /* PMC2 event selector, 6 bits. */ + +/* MMCR1 layout (604e-7457) */ +#define MMCR1_PMC3SEL 0xF8000000 /* PMC3 event selector, 5 bits. */ +#define MMCR1_PMC4SEL 0x07B00000 /* PMC4 event selector, 5 bits. */ +#define MMCR1_PMC5SEL 0x003E0000 /* PMC5 event selector, 5 bits. (745x only) */ +#define MMCR1_PMC6SEL 0x0001F800 /* PMC6 event selector, 6 bits. (745x only) */ +#define MMCR1__RESERVED 0x000007FF /* should be zero */ + +/* MMCR2 layout (7400-7457) */ +#define MMCR2_THRESHMULT 0x80000000 /* MMCR0[THRESHOLD] multiplier. */ +#define MMCR2_SMCNTEN 0x40000000 /* 7400/7410 only, should be zero. */ +#define MMCR2_SMINTEN 0x20000000 /* 7400/7410 only, should be zero. */ +#define MMCR2__RESERVED 0x1FFFFFFF /* should be zero */ +#define MMCR2_RESERVED (MMCR2_SMCNTEN | MMCR2_SMINTEN | MMCR2__RESERVED) + /* Bit definitions for MMCR0 and PMC1 / PMC2. */ #define MMCR0_PMC1_CYCLES (1 << 7) #define MMCR0_PMC1_ICACHEMISS (5 << 7) --- linux-2.6.9-rc1/include/asm-ppc/residual.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/include/asm-ppc/residual.h 2004-09-02 20:51:52.000000000 -0700 @@ -315,11 +315,20 @@ typedef struct _RESIDUAL { } RESIDUAL; +/* + * Forward declaration - we can't include because it + * breaks the boot loader + */ +struct pci_dev; + extern RESIDUAL *res; extern void print_residual_device_info(void); extern PPC_DEVICE *residual_find_device(unsigned long BusMask, unsigned char * DevID, int BaseType, int SubType, int Interface, int n); +extern int residual_pcidev_irq(struct pci_dev *dev); +extern void residual_irq_mask(char *irq_edge_mask_lo, char *irq_edge_mask_hi); +extern unsigned int residual_isapic_addr(void); extern PnP_TAG_PACKET *PnP_find_packet(unsigned char *p, unsigned packet_tag, int n); extern PnP_TAG_PACKET *PnP_find_small_vendor_packet(unsigned char *p, @@ -328,6 +337,13 @@ extern PnP_TAG_PACKET *PnP_find_small_ve extern PnP_TAG_PACKET *PnP_find_large_vendor_packet(unsigned char *p, unsigned packet_type, int n); + +#ifdef CONFIG_PREP_RESIDUAL +#define have_residual_data (res && res->ResidualLength) +#else +#define have_residual_data 0 +#endif + #endif /* __ASSEMBLY__ */ #endif /* ndef _RESIDUAL_ */ --- linux-2.6.9-rc1/include/asm-ppc/semaphore.h 2004-08-24 00:02:47.000000000 -0700 +++ 25/include/asm-ppc/semaphore.h 2004-09-02 20:51:52.000000000 -0700 @@ -29,22 +29,13 @@ struct semaphore { */ atomic_t count; wait_queue_head_t wait; -#ifdef WAITQUEUE_DEBUG - long __magic; -#endif }; -#ifdef WAITQUEUE_DEBUG -# define __SEM_DEBUG_INIT(name) \ - , (long)&(name).__magic -#else -# define __SEM_DEBUG_INIT(name) -#endif - -#define __SEMAPHORE_INITIALIZER(name, count) \ - { ATOMIC_INIT(count), \ - __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ - __SEM_DEBUG_INIT(name) } +#define __SEMAPHORE_INITIALIZER(name, n) \ +{ \ + .count = ATOMIC_INIT(n), \ + .wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ +} #define __MUTEX_INITIALIZER(name) \ __SEMAPHORE_INITIALIZER(name, 1) @@ -59,9 +50,6 @@ static inline void sema_init (struct sem { atomic_set(&sem->count, val); init_waitqueue_head(&sem->wait); -#ifdef WAITQUEUE_DEBUG - sem->__magic = (long)&sem->__magic; -#endif } static inline void init_MUTEX (struct semaphore *sem) @@ -80,9 +68,6 @@ extern void __up(struct semaphore * sem) extern inline void down(struct semaphore * sem) { -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); /* @@ -97,9 +82,6 @@ extern inline int down_interruptible(str { int ret = 0; -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); if (atomic_dec_return(&sem->count) < 0) @@ -112,10 +94,6 @@ extern inline int down_trylock(struct se { int ret; -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - ret = atomic_dec_if_positive(&sem->count) < 0; smp_wmb(); return ret; @@ -123,10 +101,6 @@ extern inline int down_trylock(struct se extern inline void up(struct semaphore * sem) { -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - smp_wmb(); if (atomic_inc_return(&sem->count) <= 0) __up(sem); --- linux-2.6.9-rc1/include/asm-ppc/serial.h 2004-08-24 00:03:38.000000000 -0700 +++ 25/include/asm-ppc/serial.h 2004-09-02 20:51:52.000000000 -0700 @@ -13,17 +13,17 @@ #elif defined(CONFIG_GEMINI) #include #elif defined(CONFIG_POWERPMC250) -#include +#include #elif defined(CONFIG_LOPEC) -#include +#include #elif defined(CONFIG_MCPN765) -#include +#include #elif defined(CONFIG_MVME5100) -#include +#include #elif defined(CONFIG_PRPMC750) -#include +#include #elif defined(CONFIG_PRPMC800) -#include +#include #elif defined(CONFIG_SANDPOINT) #include #elif defined(CONFIG_SPRUCE) --- linux-2.6.9-rc1/include/asm-ppc/uaccess.h 2004-08-24 00:01:51.000000000 -0700 +++ 25/include/asm-ppc/uaccess.h 2004-09-02 20:51:52.000000000 -0700 @@ -331,6 +331,8 @@ copy_to_user(void __user *to, const void __copy_tofrom_user((void __user *)(to), (from), (size)) #define __copy_to_user(to, from, size) \ __copy_tofrom_user((to), (void __user *)(from), (size)) +#define __copy_to_user_inatomic __copy_to_user +#define __copy_from_user_inatomic __copy_from_user extern unsigned long __clear_user(void __user *addr, unsigned long size); --- linux-2.6.9-rc1/include/asm-ppc/unistd.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/include/asm-ppc/unistd.h 2004-09-03 00:56:45.414256632 -0700 @@ -273,8 +273,14 @@ #define __NR_mq_notify 266 #define __NR_mq_getsetattr 267 #define __NR_kexec_load 268 +#define __NR_perfctr_info 269 +#define __NR_vperfctr_open (__NR_perfctr_info+1) +#define __NR_vperfctr_control (__NR_perfctr_info+2) +#define __NR_vperfctr_unlink (__NR_perfctr_info+3) +#define __NR_vperfctr_iresume (__NR_perfctr_info+4) +#define __NR_vperfctr_read (__NR_perfctr_info+5) -#define __NR_syscalls 269 +#define __NR_syscalls 275 #define __NR(n) #n --- linux-2.6.9-rc1/include/asm-s390/hardirq.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/include/asm-s390/hardirq.h 2004-09-03 00:56:41.025923760 -0700 @@ -61,51 +61,15 @@ softirq_pending(unsigned int cpu) #define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) #define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) -#define __MASK(x) ((1UL << (x))-1) - -#define PREEMPT_MASK (__MASK(PREEMPT_BITS) << PREEMPT_SHIFT) -#define SOFTIRQ_MASK (__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) -#define HARDIRQ_MASK (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) - -#define hardirq_count() (preempt_count() & HARDIRQ_MASK) -#define softirq_count() (preempt_count() & SOFTIRQ_MASK) -#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) - -#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) -#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) -#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) - -/* - * Are we doing bottom half or hardware interrupt processing? - * Are we in a softirq context? Interrupt context? - */ -#define in_irq() (hardirq_count()) -#define in_softirq() (softirq_count()) -#define in_interrupt() (irq_count()) - +extern void do_call_softirq(void); +extern void account_ticks(struct pt_regs *); -#define hardirq_trylock() (!in_interrupt()) -#define hardirq_endlock() do { } while (0) +#define invoke_softirq() do_call_softirq() #define irq_enter() \ do { \ (preempt_count() += HARDIRQ_OFFSET); \ } while(0) - - -extern void do_call_softirq(void); -extern void account_ticks(struct pt_regs *); - -#define invoke_softirq() do_call_softirq() - -#ifdef CONFIG_PREEMPT -# define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != kernel_locked()) -# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) -#else -# define in_atomic() (preempt_count() != 0) -# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET -#endif - #define irq_exit() \ do { \ preempt_count() -= IRQ_EXIT_OFFSET; \ --- linux-2.6.9-rc1/include/asm-s390/idals.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/include/asm-s390/idals.h 2004-09-02 20:51:52.000000000 -0700 @@ -35,7 +35,7 @@ static inline int idal_is_needed(void *vaddr, unsigned int length) { #ifdef __s390x__ - return ((__pa(vaddr) + length) >> 31) != 0; + return ((__pa(vaddr) + length - 1) >> 31) != 0; #else return 0; #endif --- linux-2.6.9-rc1/include/asm-s390/irq.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/include/asm-s390/irq.h 2004-09-03 00:56:41.025923760 -0700 @@ -2,7 +2,7 @@ #define _ASM_IRQ_H #ifdef __KERNEL__ -#include +#include /* * the definition of irqs has changed in 2.5.46: --- linux-2.6.9-rc1/include/asm-s390/lowcore.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/include/asm-s390/lowcore.h 2004-09-02 20:51:52.000000000 -0700 @@ -68,6 +68,7 @@ #define __LC_ASYNC_STACK 0xC48 #define __LC_KERNEL_ASCE 0xC4C #define __LC_USER_ASCE 0xC50 +#define __LC_PANIC_STACK 0xC54 #define __LC_CPUID 0xC60 #define __LC_CPUADDR 0xC68 #define __LC_IPLDEV 0xC7C @@ -80,6 +81,7 @@ #define __LC_ASYNC_STACK 0xD50 #define __LC_KERNEL_ASCE 0xD58 #define __LC_USER_ASCE 0xD60 +#define __LC_PANIC_STACK 0xD68 #define __LC_CPUID 0xD90 #define __LC_CPUADDR 0xD98 #define __LC_IPLDEV 0xDB8 @@ -176,7 +178,8 @@ struct _lowcore __u32 async_stack; /* 0xc48 */ __u32 kernel_asce; /* 0xc4c */ __u32 user_asce; /* 0xc50 */ - __u8 pad10[0xc60-0xc54]; /* 0xc54 */ + __u32 panic_stack; /* 0xc54 */ + __u8 pad10[0xc60-0xc58]; /* 0xc58 */ /* entry.S sensitive area start */ struct cpuinfo_S390 cpu_data; /* 0xc60 */ __u32 ipl_device; /* 0xc7c */ @@ -257,7 +260,8 @@ struct _lowcore __u64 async_stack; /* 0xd50 */ __u64 kernel_asce; /* 0xd58 */ __u64 user_asce; /* 0xd60 */ - __u8 pad10[0xd80-0xd68]; /* 0xd68 */ + __u64 panic_stack; /* 0xd68 */ + __u8 pad10[0xd80-0xd70]; /* 0xd70 */ /* entry.S sensitive area start */ struct cpuinfo_S390 cpu_data; /* 0xd80 */ __u32 ipl_device; /* 0xdb8 */ --- linux-2.6.9-rc1/include/asm-s390/page.h 2004-08-24 00:02:20.000000000 -0700 +++ 25/include/asm-s390/page.h 2004-09-03 00:56:41.915788480 -0700 @@ -181,6 +181,8 @@ typedef struct { unsigned long pgd; } pg #define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) +#define devmem_is_allowed(x) 1 + #endif /* __KERNEL__ */ #endif /* _S390_PAGE_H */ --- linux-2.6.9-rc1/include/asm-s390/processor.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/include/asm-s390/processor.h 2004-09-02 20:51:52.000000000 -0700 @@ -76,6 +76,8 @@ extern struct task_struct *last_task_use #define MM_VM_SIZE(mm) DEFAULT_TASK_SIZE +#define HAVE_ARCH_PICK_MMAP_LAYOUT + typedef struct { __u32 ar4; } mm_segment_t; @@ -101,6 +103,25 @@ struct thread_struct { typedef struct thread_struct thread_struct; +/* + * Stack layout of a C stack frame. + */ +#ifndef __PACK_STACK +struct stack_frame { + unsigned long back_chain; + unsigned long empty1[5]; + unsigned long gprs[10]; + unsigned int empty2[8]; +}; +#else +struct stack_frame { + unsigned long empty1[5]; + unsigned int empty2[8]; + unsigned long gprs[10]; + unsigned long back_chain; +}; +#endif + #define ARCH_MIN_TASKALIGN 8 #ifndef __s390x__ --- linux-2.6.9-rc1/include/asm-s390/ptrace.h 2004-08-24 00:03:18.000000000 -0700 +++ 25/include/asm-s390/ptrace.h 2004-09-02 20:51:52.000000000 -0700 @@ -466,6 +466,7 @@ struct user_regs_struct #ifdef __KERNEL__ #define user_mode(regs) (((regs)->psw.mask & PSW_MASK_PSTATE) != 0) #define instruction_pointer(regs) ((regs)->psw.addr & PSW_ADDR_INSN) +#define profile_pc(regs) instruction_pointer(regs) extern void show_regs(struct pt_regs * regs); #endif --- linux-2.6.9-rc1/include/asm-s390/thread_info.h 2004-08-24 00:03:17.000000000 -0700 +++ 25/include/asm-s390/thread_info.h 2004-09-02 20:51:52.000000000 -0700 @@ -11,6 +11,30 @@ #ifdef __KERNEL__ +/* + * Size of kernel stack for each process + */ +#ifndef __s390x__ +#ifndef __SMALL_STACK +#define THREAD_ORDER 1 +#define ASYNC_ORDER 1 +#else +#define THREAD_ORDER 0 +#define ASYNC_ORDER 0 +#endif +#else /* __s390x__ */ +#ifndef __SMALL_STACK_STACK +#define THREAD_ORDER 2 +#define ASYNC_ORDER 2 +#else +#define THREAD_ORDER 1 +#define ASYNC_ORDER 1 +#endif +#endif /* __s390x__ */ + +#define THREAD_SIZE (PAGE_SIZE << THREAD_ORDER) +#define ASYNC_SIZE (PAGE_SIZE << ASYNC_ORDER) + #ifndef __ASSEMBLY__ #include #include @@ -47,20 +71,6 @@ struct thread_info { #define init_thread_info (init_thread_union.thread_info) #define init_stack (init_thread_union.stack) -/* - * Size of kernel stack for each process - */ -#ifndef __s390x__ -#define THREAD_ORDER 1 -#define ASYNC_ORDER 1 -#else /* __s390x__ */ -#define THREAD_ORDER 2 -#define ASYNC_ORDER 2 -#endif /* __s390x__ */ - -#define THREAD_SIZE (PAGE_SIZE << THREAD_ORDER) -#define ASYNC_SIZE (PAGE_SIZE << ASYNC_ORDER) - /* how to get the thread information struct from C */ static inline struct thread_info *current_thread_info(void) { --- linux-2.6.9-rc1/include/asm-s390/timer.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/include/asm-s390/timer.h 2004-09-02 20:51:52.000000000 -0700 @@ -45,6 +45,4 @@ extern void add_virt_timer_periodic(void extern int mod_virt_timer(struct vtimer_list *timer, __u64 expires); extern int del_virt_timer(struct vtimer_list *timer); -int stop_timers(void); - #endif --- linux-2.6.9-rc1/include/asm-s390/uaccess.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/include/asm-s390/uaccess.h 2004-09-02 20:51:52.000000000 -0700 @@ -250,6 +250,9 @@ extern int __put_user_bad(void); extern int __get_user_bad(void); +#define __put_user_unaligned __put_user +#define __get_user_unaligned __get_user + extern long __copy_to_user_asm(const void *from, long n, void __user *to); /** @@ -272,6 +275,9 @@ __copy_to_user(void __user *to, const vo return __copy_to_user_asm(from, n, to); } +#define __copy_to_user_inatomic __copy_to_user +#define __copy_from_user_inatomic __copy_from_user + /** * copy_to_user: - Copy a block of data into user space. * @to: Destination address, in user space. --- linux-2.6.9-rc1/include/asm-sh64/page.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/include/asm-sh64/page.h 2004-09-03 00:56:41.915788480 -0700 @@ -132,6 +132,8 @@ extern __inline__ int get_order(unsigned #endif +#define devmem_is_allowed(x) 1 + #endif /* __KERNEL__ */ #endif /* __ASM_SH64_PAGE_H */ --- linux-2.6.9-rc1/include/asm-sh64/ptrace.h 2004-08-24 00:03:18.000000000 -0700 +++ 25/include/asm-sh64/ptrace.h 2004-09-02 20:51:52.000000000 -0700 @@ -28,6 +28,7 @@ struct pt_regs { #ifdef __KERNEL__ #define user_mode(regs) (((regs)->sr & 0x40000000)==0) #define instruction_pointer(regs) ((regs)->pc) +#define profile_pc(regs) instruction_pointer(regs) extern void show_regs(struct pt_regs *); #endif --- linux-2.6.9-rc1/include/asm-sh64/semaphore.h 2004-08-24 00:02:25.000000000 -0700 +++ 25/include/asm-sh64/semaphore.h 2004-09-02 20:51:52.000000000 -0700 @@ -31,21 +31,14 @@ struct semaphore { atomic_t count; int sleepers; wait_queue_head_t wait; -#ifdef WAITQUEUE_DEBUG - long __magic; -#endif }; -#ifdef WAITQUEUE_DEBUG -# define __SEM_DEBUG_INIT(name) \ - , (int)&(name).__magic -#else -# define __SEM_DEBUG_INIT(name) -#endif - -#define __SEMAPHORE_INITIALIZER(name,count) \ -{ ATOMIC_INIT(count), 0, __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ - __SEM_DEBUG_INIT(name) } +#define __SEMAPHORE_INITIALIZER(name, n) \ +{ \ + .count = ATOMIC_INIT(n), \ + .sleepers = 0, \ + .wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ +} #define __MUTEX_INITIALIZER(name) \ __SEMAPHORE_INITIALIZER(name,1) @@ -67,9 +60,6 @@ static inline void sema_init (struct sem atomic_set(&sem->count, val); sem->sleepers = 0; init_waitqueue_head(&sem->wait); -#ifdef WAITQUEUE_DEBUG - sem->__magic = (int)&sem->__magic; -#endif } static inline void init_MUTEX (struct semaphore *sem) @@ -98,10 +88,6 @@ extern spinlock_t semaphore_wake_lock; static inline void down(struct semaphore * sem) { -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - if (atomic_dec_return(&sem->count) < 0) __down(sem); } @@ -109,9 +95,6 @@ static inline void down(struct semaphore static inline int down_interruptible(struct semaphore * sem) { int ret = 0; -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif if (atomic_dec_return(&sem->count) < 0) ret = __down_interruptible(sem); @@ -121,9 +104,6 @@ static inline int down_interruptible(str static inline int down_trylock(struct semaphore * sem) { int ret = 0; -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif if (atomic_dec_return(&sem->count) < 0) ret = __down_trylock(sem); @@ -136,9 +116,6 @@ static inline int down_trylock(struct se */ static inline void up(struct semaphore * sem) { -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif if (atomic_inc_return(&sem->count) <= 0) __up(sem); } --- linux-2.6.9-rc1/include/asm-sh64/uaccess.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/include/asm-sh64/uaccess.h 2004-09-02 20:51:52.000000000 -0700 @@ -261,6 +261,9 @@ if (__copy_from_user(to,from,n)) \ return retval; \ }) +#define __copy_to_user_inatomic __copy_to_user +#define __copy_from_user_inatomic __copy_from_user + /* XXX: Not sure it works well.. should be such that: 4byte clear and the rest. */ extern __kernel_size_t __clear_user(void *addr, __kernel_size_t size); --- linux-2.6.9-rc1/include/asm-sh/hardirq.h 2004-08-24 00:02:32.000000000 -0700 +++ 25/include/asm-sh/hardirq.h 2004-09-03 00:56:41.026923608 -0700 @@ -35,20 +35,6 @@ typedef struct { #define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) #define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) -#define __MASK(x) ((1UL << (x))-1) - -#define PREEMPT_MASK (__MASK(PREEMPT_BITS) << PREEMPT_SHIFT) -#define HARDIRQ_MASK (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) -#define SOFTIRQ_MASK (__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) - -#define hardirq_count() (preempt_count() & HARDIRQ_MASK) -#define softirq_count() (preempt_count() & SOFTIRQ_MASK) -#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) - -#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) -#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) -#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) - /* * The hardirq mask has to be large enough to have * space for potentially all IRQ sources in the system @@ -58,29 +44,10 @@ typedef struct { # error HARDIRQ_BITS is too low! #endif -/* - * Are we in an interrupt context? Either doing bottom half - * or hardware interrupt processing? - */ -#define in_irq() (hardirq_count()) -#define in_softirq() (softirq_count()) -#define in_interrupt() (irq_count()) - - -#define hardirq_trylock() (!in_interrupt()) -#define hardirq_endlock() do { } while (0) - -#define irq_enter() (preempt_count() += HARDIRQ_OFFSET) #define nmi_enter() (irq_enter()) #define nmi_exit() (preempt_count() -= HARDIRQ_OFFSET) -#ifdef CONFIG_PREEMPT -# define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != kernel_locked()) -# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) -#else -# define in_atomic() (preempt_count() != 0) -# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET -#endif +#define irq_enter() (preempt_count() += HARDIRQ_OFFSET) #define irq_exit() \ do { \ preempt_count() -= IRQ_EXIT_OFFSET; \ @@ -89,10 +56,4 @@ do { \ preempt_enable_no_resched(); \ } while (0) -#ifndef CONFIG_SMP -# define synchronize_irq(irq) barrier() -#else -extern void synchronize_irq(unsigned int irq); -#endif /* CONFIG_SMP */ - #endif /* __ASM_SH_HARDIRQ_H */ --- linux-2.6.9-rc1/include/asm-sh/page.h 2004-08-24 00:03:32.000000000 -0700 +++ 25/include/asm-sh/page.h 2004-09-03 00:56:41.915788480 -0700 @@ -133,6 +133,8 @@ static __inline__ int get_order(unsigned #endif +#define devmem_is_allowed(x) 1 + #endif /* __KERNEL__ */ #endif /* __ASM_SH_PAGE_H */ --- linux-2.6.9-rc1/include/asm-sh/ptrace.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/include/asm-sh/ptrace.h 2004-09-02 20:51:52.000000000 -0700 @@ -90,6 +90,15 @@ struct pt_dspregs { #define user_mode(regs) (((regs)->sr & 0x40000000)==0) #define instruction_pointer(regs) ((regs)->pc) extern void show_regs(struct pt_regs *); + +static inline unsigned long profile_pc(struct pt_regs *regs) +{ + unsigned long pc = instruction_pointer(regs); + + if (pc >= 0xa0000000UL && pc < 0xc0000000UL) + pc -= 0x20000000; + return pc; +} #endif #endif /* __ASM_SH_PTRACE_H */ --- linux-2.6.9-rc1/include/asm-sh/semaphore.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/include/asm-sh/semaphore.h 2004-09-02 20:51:52.000000000 -0700 @@ -24,21 +24,14 @@ struct semaphore { atomic_t count; int sleepers; wait_queue_head_t wait; -#ifdef WAITQUEUE_DEBUG - long __magic; -#endif }; -#ifdef WAITQUEUE_DEBUG -# define __SEM_DEBUG_INIT(name) \ - , (int)&(name).__magic -#else -# define __SEM_DEBUG_INIT(name) -#endif - -#define __SEMAPHORE_INITIALIZER(name,count) \ -{ ATOMIC_INIT(count), 0, __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ - __SEM_DEBUG_INIT(name) } +#define __SEMAPHORE_INITIALIZER(name, n) \ +{ \ + .count = ATOMIC_INIT(n), \ + .sleepers = 0, \ + .wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ +} #define __MUTEX_INITIALIZER(name) \ __SEMAPHORE_INITIALIZER(name,1) @@ -60,9 +53,6 @@ static inline void sema_init (struct sem atomic_set(&sem->count, val); sem->sleepers = 0; init_waitqueue_head(&sem->wait); -#ifdef WAITQUEUE_DEBUG - sem->__magic = (int)&sem->__magic; -#endif } static inline void init_MUTEX (struct semaphore *sem) @@ -91,10 +81,6 @@ extern spinlock_t semaphore_wake_lock; static inline void down(struct semaphore * sem) { -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - might_sleep(); if (atomic_dec_return(&sem->count) < 0) __down(sem); @@ -103,9 +89,6 @@ static inline void down(struct semaphore static inline int down_interruptible(struct semaphore * sem) { int ret = 0; -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); if (atomic_dec_return(&sem->count) < 0) @@ -116,9 +99,6 @@ static inline int down_interruptible(str static inline int down_trylock(struct semaphore * sem) { int ret = 0; -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif if (atomic_dec_return(&sem->count) < 0) ret = __down_trylock(sem); @@ -131,9 +111,6 @@ static inline int down_trylock(struct se */ static inline void up(struct semaphore * sem) { -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif if (atomic_inc_return(&sem->count) <= 0) __up(sem); } --- linux-2.6.9-rc1/include/asm-sh/uaccess.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/include/asm-sh/uaccess.h 2004-09-02 20:51:52.000000000 -0700 @@ -446,6 +446,10 @@ __copy_res; }) __copy_user((void *)(to), \ (void *)(from), n) +#define __copy_to_user_inatomic __copy_to_user +#define __copy_from_user_inatomic __copy_from_user + + #define copy_from_user(to,from,n) ({ \ void *__copy_to = (void *) (to); \ void *__copy_from = (void *) (from); \ --- linux-2.6.9-rc1/include/asm-sparc64/delay.h 2004-08-24 00:03:17.000000000 -0700 +++ 25/include/asm-sparc64/delay.h 2004-09-02 20:51:52.000000000 -0700 @@ -1,7 +1,11 @@ -/* $Id: delay.h,v 1.13 2002/02/02 03:33:48 kanoj Exp $ - * delay.h: Linux delay routines on the V9. +/* delay.h: Linux delay routines on sparc64. * - * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu). + * Copyright (C) 1996, 2004 David S. Miller (davem@davemloft.net). + * + * Based heavily upon x86 variant which is: + * Copyright (C) 1993 Linus Torvalds + * + * Delay routines calling functions in arch/sparc64/lib/delay.c */ #ifndef __SPARC64_DELAY_H @@ -13,50 +17,21 @@ #ifndef __ASSEMBLY__ -static __inline__ void __delay(unsigned long loops) -{ - __asm__ __volatile__( -" b,pt %%xcc, 1f\n" -" cmp %0, 0\n" -" .align 32\n" -"1:\n" -" bne,pt %%xcc, 1b\n" -" subcc %0, 1, %0\n" - : "=&r" (loops) - : "0" (loops) - : "cc"); -} - -static __inline__ void __udelay(unsigned long usecs, unsigned long lps) -{ - usecs *= 0x00000000000010c6UL; /* 2**32 / 1000000 */ - - __asm__ __volatile__( -" mulx %1, %2, %0\n" -" srlx %0, 32, %0\n" - : "=r" (usecs) - : "r" (usecs), "r" (lps)); - - __delay(usecs * HZ); -} - -extern __inline__ void __ndelay(unsigned long usecs, unsigned long lps) -{ - usecs *= 0x0000000000000005UL; /* 2**32 / 10000 */ - - __asm__ __volatile__( -" mulx %1, %2, %0\n" -" srlx %0, 32, %0\n" - : "=r" (usecs) - : "r" (usecs), "r" (lps)); - - __delay(usecs * HZ); -} - -#define __udelay_val cpu_data(smp_processor_id()).udelay_val +extern void __bad_udelay(void); +extern void __bad_ndelay(void); -#define udelay(usecs) __udelay((usecs),__udelay_val) -#define ndelay(usecs) __ndelay((usecs),__udelay_val) +extern void __udelay(unsigned long usecs); +extern void __ndelay(unsigned long nsecs); +extern void __const_udelay(unsigned long usecs); +extern void __delay(unsigned long loops); + +#define udelay(n) (__builtin_constant_p(n) ? \ + ((n) > 20000 ? __bad_udelay() : __const_udelay((n) * 0x10c7ul)) : \ + __udelay(n)) + +#define ndelay(n) (__builtin_constant_p(n) ? \ + ((n) > 20000 ? __bad_ndelay() : __const_udelay((n) * 5ul)) : \ + __ndelay(n)) #endif /* !__ASSEMBLY__ */ --- linux-2.6.9-rc1/include/asm-sparc64/errno.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/include/asm-sparc64/errno.h 2004-09-03 00:56:54.493876320 -0700 @@ -101,5 +101,9 @@ #define ENOMEDIUM 125 /* No medium found */ #define EMEDIUMTYPE 126 /* Wrong medium type */ +#define ENOKEY 127 /* Required key not available */ +#define EKEYEXPIRED 128 /* Key has expired */ +#define EKEYREVOKED 129 /* Key has been revoked */ +#define EKEYREJECTED 130 /* Key was rejected by service */ #endif /* !(_SPARC64_ERRNO_H) */ --- linux-2.6.9-rc1/include/asm-sparc64/hardirq.h 2004-08-24 00:03:18.000000000 -0700 +++ 25/include/asm-sparc64/hardirq.h 2004-09-03 00:56:41.027923456 -0700 @@ -41,42 +41,7 @@ typedef struct { #define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) #define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) -#define __MASK(x) ((1UL << (x))-1) - -#define PREEMPT_MASK (__MASK(PREEMPT_BITS) << PREEMPT_SHIFT) -#define HARDIRQ_MASK (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) -#define SOFTIRQ_MASK (__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) - -#define hardirq_count() (preempt_count() & HARDIRQ_MASK) -#define softirq_count() (preempt_count() & SOFTIRQ_MASK) -#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) - -#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) -#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) -#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) - -/* - * Are we doing bottom half or hardware interrupt processing? - * Are we in a softirq context? Interrupt context? - */ -#define in_irq() (hardirq_count()) -#define in_softirq() (softirq_count()) -#define in_interrupt() (irq_count()) - - -#define hardirq_trylock() (!in_interrupt()) -#define hardirq_endlock() do { } while (0) - #define irq_enter() (preempt_count() += HARDIRQ_OFFSET) - -#ifdef CONFIG_PREEMPT -# include -# define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != kernel_locked()) -# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) -#else -# define in_atomic() (preempt_count() != 0) -# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET -#endif #define irq_exit() \ do { \ preempt_count() -= IRQ_EXIT_OFFSET; \ @@ -85,10 +50,4 @@ do { \ preempt_enable_no_resched(); \ } while (0) -#ifndef CONFIG_SMP -# define synchronize_irq(irq) barrier() -#else - extern void synchronize_irq(unsigned int irq); -#endif /* CONFIG_SMP */ - #endif /* !(__SPARC64_HARDIRQ_H) */ --- linux-2.6.9-rc1/include/asm-sparc64/kdebug.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/include/asm-sparc64/kdebug.h 2004-09-02 20:51:52.000000000 -0700 @@ -1,9 +1,52 @@ #ifndef _SPARC64_KDEBUG_H #define _SPARC64_KDEBUG_H -/* - * No kernel debugger on sparc64. Kept here because drivers/sbus/char/ - * includes it for sparc32 sake. +/* Nearly identical to x86_64/i386 code. */ + +#include + +struct pt_regs; + +struct die_args { + struct pt_regs *regs; + const char *str; + long err; + int trapnr; + int signr; +}; + +/* Note - you should never unregister because that can race with NMIs. + * If you really want to do it first unregister - then synchronize_kernel + * - then free. */ +int register_die_notifier(struct notifier_block *nb); +extern struct notifier_block *sparc64die_chain; + +extern void bad_trap(struct pt_regs *, long); + +/* Grossly misnamed. */ +enum die_val { + DIE_OOPS = 1, + DIE_DEBUG, /* ta 0x70 */ + DIE_DEBUG_2, /* ta 0x71 */ + DIE_DIE, + DIE_TRAP, + DIE_TRAP_TL1, + DIE_GPF, + DIE_CALL, + DIE_PAGE_FAULT, +}; + +static inline int notify_die(enum die_val val,char *str, struct pt_regs *regs, + long err, int trap, int sig) +{ + struct die_args args = { .regs = regs, + .str = str, + .err = err, + .trapnr = trap, + .signr = sig }; + + return notifier_call_chain(&sparc64die_chain, val, &args); +} #endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-sparc64/kprobes.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,24 @@ +#ifndef _SPARC64_KPROBES_H +#define _SPARC64_KPROBES_H + +#include +#include + +typedef u32 kprobe_opcode_t; + +#define BREAKPOINT_INSTRUCTION 0x91d02070 /* ta 0x70 */ +#define BREAKPOINT_INSTRUCTION_2 0x91d02071 /* ta 0x71 */ +#define MAX_INSN_SIZE 2 + +#ifdef CONFIG_KPROBES +extern int kprobe_exceptions_notify(struct notifier_block *self, + unsigned long val, void *data); +#else /* !CONFIG_KPROBES */ +static inline int kprobe_exceptions_notify(struct notifier_block *self, + unsigned long val, void *data) +{ + return 0; +} +#endif + +#endif /* _SPARC64_KPROBES_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-sparc64/lockmeter.h 2004-09-03 00:56:42.832649096 -0700 @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2000 Anton Blanchard (anton@linuxcare.com) + * Copyright (C) 2003 David S. Miller (davem@redhat.com) + */ + +#ifndef _SPARC64_LOCKMETER_H +#define _SPARC64_LOCKMETER_H + +#include +#include +#include +#include + +/* Actually, this is not the CPU frequency by the system tick + * frequency which is good enough for lock metering. + */ +#define CPU_CYCLE_FREQUENCY (timer_tick_offset * HZ) +#define THIS_CPU_NUMBER smp_processor_id() + +#define PUT_INDEX(lock_ptr,indexv) (lock_ptr)->index = (indexv) +#define GET_INDEX(lock_ptr) (lock_ptr)->index + +#define PUT_RWINDEX(rwlock_ptr,indexv) (rwlock_ptr)->index = (indexv) +#define GET_RWINDEX(rwlock_ptr) (rwlock_ptr)->index +#define PUT_RW_CPU(rwlock_ptr,cpuv) (rwlock_ptr)->cpu = (cpuv) +#define GET_RW_CPU(rwlock_ptr) (rwlock_ptr)->cpu + +#define RWLOCK_READERS(rwlock_ptr) rwlock_readers(rwlock_ptr) + +extern inline int rwlock_readers(rwlock_t *rwlock_ptr) +{ + signed int tmp = rwlock_ptr->lock; + + if (tmp > 0) + return tmp; + else + return 0; +} + +#define RWLOCK_IS_WRITE_LOCKED(rwlock_ptr) ((signed int)((rwlock_ptr)->lock) < 0) +#define RWLOCK_IS_READ_LOCKED(rwlock_ptr) ((signed int)((rwlock_ptr)->lock) > 0) + +#define get_cycles64() get_cycles() + +#endif /* _SPARC64_LOCKMETER_H */ --- linux-2.6.9-rc1/include/asm-sparc64/page.h 2004-08-24 00:02:32.000000000 -0700 +++ 25/include/asm-sparc64/page.h 2004-09-03 00:56:41.916788328 -0700 @@ -165,6 +165,8 @@ static __inline__ int get_order(unsigned #define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) +#define devmem_is_allowed(x) 1 + #endif /* !(__KERNEL__) */ #endif /* !(_SPARC64_PAGE_H) */ --- linux-2.6.9-rc1/include/asm-sparc64/ptrace.h 2004-08-24 00:02:59.000000000 -0700 +++ 25/include/asm-sparc64/ptrace.h 2004-09-02 20:51:52.000000000 -0700 @@ -98,6 +98,7 @@ struct sparc_trapf { set_thread_flag(TIF_SYSCALL_SUCCESS) #define user_mode(regs) (!((regs)->tstate & TSTATE_PRIV)) #define instruction_pointer(regs) ((regs)->tpc) +#define profile_pc(regs) instruction_pointer(regs) extern void show_regs(struct pt_regs *); #endif --- linux-2.6.9-rc1/include/asm-sparc64/sigcontext.h 2004-08-24 00:02:25.000000000 -0700 +++ 25/include/asm-sparc64/sigcontext.h 2004-09-02 20:51:52.000000000 -0700 @@ -83,18 +83,6 @@ struct sigcontext { unsigned long sigc_mask; }; -#ifdef __KERNEL__ - -/* This magic should be in g_upper[0] for all upper parts - to be valid. */ -#define SIGINFO_EXTRA_V8PLUS_MAGIC 0x130e269 -typedef struct { - unsigned int g_upper[8]; - unsigned int o_upper[8]; -} siginfo_extra_v8plus_t; - -#endif - #endif /* !(__ASSEMBLY__) */ #endif /* !(__SPARC64_SIGCONTEXT_H) */ --- linux-2.6.9-rc1/include/asm-sparc64/siginfo.h 2004-08-24 00:03:23.000000000 -0700 +++ 25/include/asm-sparc64/siginfo.h 2004-09-02 20:51:52.000000000 -0700 @@ -24,57 +24,8 @@ typedef union sigval32 { u32 sival_ptr; } sigval_t32; -typedef struct siginfo32 { - int si_signo; - int si_errno; - int si_code; +struct siginfo32; - union { - int _pad[SI_PAD_SIZE32]; - - /* kill() */ - struct { - compat_pid_t _pid; /* sender's pid */ - unsigned int _uid; /* sender's uid */ - } _kill; - - /* POSIX.1b timers */ - struct { - timer_t _tid; /* timer id */ - int _overrun; /* overrun count */ - sigval_t32 _sigval; /* same as below */ - int _sys_private; /* not to be passed to user */ - } _timer; - - /* POSIX.1b signals */ - struct { - compat_pid_t _pid; /* sender's pid */ - unsigned int _uid; /* sender's uid */ - sigval_t32 _sigval; - } _rt; - - /* SIGCHLD */ - struct { - compat_pid_t _pid; /* which child */ - unsigned int _uid; /* sender's uid */ - int _status; /* exit code */ - compat_clock_t _utime; - compat_clock_t _stime; - } _sigchld; - - /* SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGEMT */ - struct { - u32 _addr; /* faulting insn/memory ref. */ - int _trapno; - } _sigfault; - - /* SIGPOLL */ - struct { - int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ - int _fd; - } _sigpoll; - } _sifields; -} siginfo_t32; #endif /* CONFIG_COMPAT */ #endif /* __KERNEL__ */ @@ -105,7 +56,8 @@ typedef struct sigevent32 { } _sigev_un; } sigevent_t32; -extern int copy_siginfo_to_user32(siginfo_t32 __user *to, siginfo_t *from); +extern int copy_siginfo_to_user32(struct siginfo32 __user *to, siginfo_t *from); +extern int copy_siginfo_to_kernel32(siginfo_t *to, struct siginfo32 __user *from); #endif /* CONFIG_COMPAT */ --- linux-2.6.9-rc1/include/asm-sparc64/spinlock.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/include/asm-sparc64/spinlock.h 2004-09-03 00:56:42.833648944 -0700 @@ -31,15 +31,23 @@ #ifndef CONFIG_DEBUG_SPINLOCK -typedef unsigned char spinlock_t; -#define SPIN_LOCK_UNLOCKED 0 +typedef struct { + unsigned char lock; + unsigned int index; +} spinlock_t; -#define spin_lock_init(lock) (*((unsigned char *)(lock)) = 0) -#define spin_is_locked(lock) (*((volatile unsigned char *)(lock)) != 0) +#ifdef CONFIG_LOCKMETER +#define SPIN_LOCK_UNLOCKED (spinlock_t) {0, 0} +#else +#define SPIN_LOCK_UNLOCKED (spinlock_t) { 0 } +#endif -#define spin_unlock_wait(lock) \ +#define spin_lock_init(__lock) do { *(__lock) = SPIN_LOCK_UNLOCKED; } while(0) +#define spin_is_locked(__lock) (*((volatile unsigned char *)(&((__lock)->lock))) != 0) + +#define spin_unlock_wait(__lock) \ do { membar("#LoadLoad"); \ -} while(*((volatile unsigned char *)lock)) +} while(*((volatile unsigned char *)(&(((spinlock_t *)__lock)->lock)))) /* arch/sparc64/lib/spinlock.S */ extern void _raw_spin_lock(spinlock_t *lock); @@ -99,17 +107,31 @@ extern int _spin_trylock (spinlock_t *lo #ifndef CONFIG_DEBUG_SPINLOCK -typedef unsigned int rwlock_t; -#define RW_LOCK_UNLOCKED 0 -#define rwlock_init(lp) do { *(lp) = RW_LOCK_UNLOCKED; } while(0) -#define rwlock_is_locked(x) (*(x) != RW_LOCK_UNLOCKED) +#ifdef CONFIG_LOCKMETER +typedef struct { + unsigned int lock; + unsigned int index; + unsigned int cpu; +} rwlock_t; +#define RW_LOCK_UNLOCKED (rwlock_t) { 0, 0, 0xff } +#else +typedef struct { + unsigned int lock; +} rwlock_t; +#define RW_LOCK_UNLOCKED (rwlock_t) { 0 } +#endif + +#define rwlock_init(lp) do { *(lp) = RW_LOCK_UNLOCKED; } while(0) +#define rwlock_is_locked(x) ((x)->lock != 0) +extern int __read_trylock(rwlock_t *); extern void __read_lock(rwlock_t *); extern void __read_unlock(rwlock_t *); extern void __write_lock(rwlock_t *); extern void __write_unlock(rwlock_t *); extern int __write_trylock(rwlock_t *); +#define _raw_read_trylock(p) __read_trylock(p) #define _raw_read_lock(p) __read_lock(p) #define _raw_read_unlock(p) __read_unlock(p) #define _raw_write_lock(p) __write_lock(p) --- linux-2.6.9-rc1/include/asm-sparc64/ttable.h 2004-08-24 00:01:52.000000000 -0700 +++ 25/include/asm-sparc64/ttable.h 2004-09-02 20:51:52.000000000 -0700 @@ -176,6 +176,12 @@ ba,pt %xcc, rtrap_clr_l6; \ stx %l2, [%sp + PTREGS_OFF + PT_V9_TNPC]; +#ifdef CONFIG_KPROBES +#define KPROBES_TRAP(lvl) TRAP_IRQ(kprobe_trap, lvl) +#else +#define KPROBES_TRAP(lvl) TRAP_ARG(bad_trap, lvl) +#endif + /* Before touching these macros, you owe it to yourself to go and * see how arch/sparc64/kernel/winfixup.S works... -DaveM * --- linux-2.6.9-rc1/include/asm-sparc64/uaccess.h 2004-08-24 00:03:50.000000000 -0700 +++ 25/include/asm-sparc64/uaccess.h 2004-09-02 20:51:52.000000000 -0700 @@ -14,6 +14,7 @@ #include #include #include +#include #endif #ifndef __ASSEMBLY__ @@ -316,6 +317,8 @@ extern long __strnlen_user(const char __ #define strlen_user __strlen_user #define strnlen_user __strnlen_user +#define __copy_to_user_inatomic __copy_to_user +#define __copy_from_user_inatomic __copy_from_user #endif /* __ASSEMBLY__ */ --- linux-2.6.9-rc1/include/asm-sparc64/unistd.h 2004-08-24 00:02:20.000000000 -0700 +++ 25/include/asm-sparc64/unistd.h 2004-09-02 20:51:52.000000000 -0700 @@ -292,11 +292,13 @@ #define __NR_io_cancel 271 #define __NR_io_getevents 272 #define __NR_mq_open 273 -#define __NR_mq_unlink (__NR_mq_open+1) -#define __NR_mq_timedsend (__NR_mq_open+2) -#define __NR_mq_timedreceive (__NR_mq_open+3) -#define __NR_mq_notify (__NR_mq_open+4) -#define __NR_mq_getsetattr (__NR_mq_open+5) +#define __NR_mq_unlink 274 +#define __NR_mq_timedsend 275 +#define __NR_mq_timedreceive 276 +#define __NR_mq_notify 277 +#define __NR_mq_getsetattr 278 +#define __NR_waitid 279 + /* WARNING: You MAY NOT add syscall numbers larger than 282, since * all of the syscall tables in the Sparc kernel are * sized to have 283 entries (starting at zero). Therefore --- linux-2.6.9-rc1/include/asm-sparc64/vga.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/include/asm-sparc64/vga.h 2004-09-02 20:51:52.000000000 -0700 @@ -11,6 +11,9 @@ #define VT_BUF_HAVE_RW +#undef scr_writew +#undef scr_readw + static inline void scr_writew(u16 val, u16 *addr) { BUG_ON((long) addr >= 0); --- linux-2.6.9-rc1/include/asm-sparc/errno.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/include/asm-sparc/errno.h 2004-09-03 00:56:54.493876320 -0700 @@ -101,5 +101,9 @@ #define ENOMEDIUM 125 /* No medium found */ #define EMEDIUMTYPE 126 /* Wrong medium type */ +#define ENOKEY 127 /* Required key not available */ +#define EKEYEXPIRED 128 /* Key has expired */ +#define EKEYREVOKED 129 /* Key has been revoked */ +#define EKEYREJECTED 130 /* Key was rejected by service */ #endif --- linux-2.6.9-rc1/include/asm-sparc/hardirq.h 2004-08-24 00:03:49.000000000 -0700 +++ 25/include/asm-sparc/hardirq.h 2004-09-03 00:56:41.027923456 -0700 @@ -42,42 +42,7 @@ typedef struct { #define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) #define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) -#define __MASK(x) ((1UL << (x))-1) - -#define PREEMPT_MASK (__MASK(PREEMPT_BITS) << PREEMPT_SHIFT) -#define HARDIRQ_MASK (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) -#define SOFTIRQ_MASK (__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) - -#define hardirq_count() (preempt_count() & HARDIRQ_MASK) -#define softirq_count() (preempt_count() & SOFTIRQ_MASK) -#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) - -#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) -#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) -#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) - -/* - * Are we doing bottom half or hardware interrupt processing? - * Are we in a softirq context? Interrupt context? - */ -#define in_irq() (hardirq_count()) -#define in_softirq() (softirq_count()) -#define in_interrupt() (irq_count()) - - -#define hardirq_trylock() (!in_interrupt()) -#define hardirq_endlock() do { } while (0) - #define irq_enter() (preempt_count() += HARDIRQ_OFFSET) - -#ifdef CONFIG_PREEMPT -#include -# define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != kernel_locked()) -# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) -#else -# define in_atomic() (preempt_count() != 0) -# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET -#endif #define irq_exit() \ do { \ preempt_count() -= IRQ_EXIT_OFFSET; \ @@ -86,10 +51,4 @@ do { preempt_enable_no_resched(); \ } while (0) -#ifndef CONFIG_SMP -# define synchronize_irq(irq) barrier() -#else /* SMP */ -extern void synchronize_irq(unsigned int irq); -#endif /* SMP */ - #endif /* __SPARC_HARDIRQ_H */ --- linux-2.6.9-rc1/include/asm-sparc/page.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/include/asm-sparc/page.h 2004-09-03 00:56:41.916788328 -0700 @@ -176,6 +176,8 @@ extern unsigned long pfn_base; #define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) +#define devmem_is_allowed(x) 1 + #endif /* __KERNEL__ */ #endif /* _SPARC_PAGE_H */ --- linux-2.6.9-rc1/include/asm-sparc/ptrace.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/include/asm-sparc/ptrace.h 2004-09-02 20:51:52.000000000 -0700 @@ -62,6 +62,7 @@ struct sparc_stackf { #ifdef __KERNEL__ #define user_mode(regs) (!((regs)->psr & PSR_PS)) #define instruction_pointer(regs) ((regs)->pc) +unsigned long profile_pc(struct pt_regs *); extern void show_regs(struct pt_regs *); #endif --- linux-2.6.9-rc1/include/asm-sparc/semaphore.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/include/asm-sparc/semaphore.h 2004-09-02 20:51:52.000000000 -0700 @@ -13,21 +13,14 @@ struct semaphore { atomic24_t count; int sleepers; wait_queue_head_t wait; -#ifdef WAITQUEUE_DEBUG - long __magic; -#endif }; -#ifdef WAITQUEUE_DEBUG -# define __SEM_DEBUG_INIT(name) \ - , (long)&(name).__magic -#else -# define __SEM_DEBUG_INIT(name) -#endif - -#define __SEMAPHORE_INITIALIZER(name,count) \ -{ ATOMIC24_INIT(count), 0, __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ - __SEM_DEBUG_INIT(name) } +#define __SEMAPHORE_INITIALIZER(name, n) \ +{ \ + .count = ATOMIC24_INIT(n), \ + .sleepers = 0, \ + .wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ +} #define __MUTEX_INITIALIZER(name) \ __SEMAPHORE_INITIALIZER(name,1) @@ -43,9 +36,6 @@ static inline void sema_init (struct sem atomic24_set(&sem->count, val); sem->sleepers = 0; init_waitqueue_head(&sem->wait); -#ifdef WAITQUEUE_DEBUG - sem->__magic = (long)&sem->__magic; -#endif } static inline void init_MUTEX (struct semaphore *sem) @@ -68,9 +58,6 @@ static inline void down(struct semaphore register volatile int *ptr asm("g1"); register int increment asm("g2"); -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); ptr = &(sem->count.counter); @@ -105,9 +92,6 @@ static inline int down_interruptible(str register volatile int *ptr asm("g1"); register int increment asm("g2"); -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); ptr = &(sem->count.counter); @@ -145,10 +129,6 @@ static inline int down_trylock(struct se register volatile int *ptr asm("g1"); register int increment asm("g2"); -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - ptr = &(sem->count.counter); increment = 1; @@ -184,10 +164,6 @@ static inline void up(struct semaphore * register volatile int *ptr asm("g1"); register int increment asm("g2"); -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - ptr = &(sem->count.counter); increment = 1; --- linux-2.6.9-rc1/include/asm-sparc/sigcontext.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/include/asm-sparc/sigcontext.h 2004-09-02 20:51:52.000000000 -0700 @@ -57,20 +57,6 @@ typedef struct { } si_fpqueue [16]; } __siginfo_fpu_t; -#ifdef __KERNEL__ - -/* This magic should be in g_upper[0] for all upper parts - to be valid. - This is generated by sparc64 only, but for 32bit processes, - so we define it here as well. */ -#define SIGINFO_EXTRA_V8PLUS_MAGIC 0x130e269 -typedef struct { - unsigned int g_upper[8]; - unsigned int o_upper[8]; -} siginfo_extra_v8plus_t; - -#endif - #endif /* !(__ASSEMBLY__) */ #endif /* !(__SPARC_SIGCONTEXT_H) */ --- linux-2.6.9-rc1/include/asm-sparc/uaccess.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/include/asm-sparc/uaccess.h 2004-09-02 20:51:52.000000000 -0700 @@ -322,6 +322,9 @@ static inline unsigned long __copy_from_ return __copy_user((void __user *) to, from, n); } +#define __copy_to_user_inatomic __copy_to_user +#define __copy_from_user_inatomic __copy_from_user + static inline unsigned long __clear_user(void __user *addr, unsigned long size) { unsigned long ret; --- linux-2.6.9-rc1/include/asm-sparc/unistd.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/include/asm-sparc/unistd.h 2004-09-02 20:51:52.000000000 -0700 @@ -290,11 +290,12 @@ #define __NR_io_cancel 271 #define __NR_io_getevents 272 #define __NR_mq_open 273 -#define __NR_mq_unlink (__NR_mq_open+1) -#define __NR_mq_timedsend (__NR_mq_open+2) -#define __NR_mq_timedreceive (__NR_mq_open+3) -#define __NR_mq_notify (__NR_mq_open+4) -#define __NR_mq_getsetattr (__NR_mq_open+5) +#define __NR_mq_unlink 274 +#define __NR_mq_timedsend 275 +#define __NR_mq_timedreceive 276 +#define __NR_mq_notify 277 +#define __NR_mq_getsetattr 278 +#define __NR_waitid 279 /* WARNING: You MAY NOT add syscall numbers larger than 282, since * all of the syscall tables in the Sparc kernel are --- linux-2.6.9-rc1/include/asm-um/archparam-i386.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/include/asm-um/archparam-i386.h 2004-09-02 20:51:52.000000000 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) * Licensed under the GPL */ @@ -56,6 +56,93 @@ typedef elf_greg_t elf_gregset_t[ELF_NGR pr_reg[16] = PT_REGS_SS(regs); \ } while(0); +#if 0 /* Turn this back on when UML has VSYSCALL working */ +#define VSYSCALL_BASE (__fix_to_virt(FIX_VSYSCALL)) +#else +#define VSYSCALL_BASE 0 +#endif + +#define VSYSCALL_EHDR ((const struct elfhdr *) VSYSCALL_BASE) +#define VSYSCALL_ENTRY ((unsigned long) &__kernel_vsyscall) +extern void *__kernel_vsyscall; + +/* + * Architecture-neutral AT_ values in 0-17, leave some room + * for more of them, start the x86-specific ones at 32. + */ +#define AT_SYSINFO 32 +#define AT_SYSINFO_EHDR 33 + +#define ARCH_DLINFO \ +do { \ + NEW_AUX_ENT(AT_SYSINFO, VSYSCALL_ENTRY); \ + NEW_AUX_ENT(AT_SYSINFO_EHDR, VSYSCALL_BASE); \ +} while (0) + +/* + * These macros parameterize elf_core_dump in fs/binfmt_elf.c to write out + * extra segments containing the vsyscall DSO contents. Dumping its + * contents makes post-mortem fully interpretable later without matching up + * the same kernel and hardware config to see what PC values meant. + * Dumping its extra ELF program headers includes all the other information + * a debugger needs to easily find how the vsyscall DSO was being used. + */ +#if 0 +#define ELF_CORE_EXTRA_PHDRS (VSYSCALL_EHDR->e_phnum) +#endif + +#undef ELF_CORE_EXTRA_PHDRS + +#if 0 +#define ELF_CORE_WRITE_EXTRA_PHDRS \ +do { \ + const struct elf_phdr *const vsyscall_phdrs = \ + (const struct elf_phdr *) (VSYSCALL_BASE \ + + VSYSCALL_EHDR->e_phoff); \ + int i; \ + Elf32_Off ofs = 0; \ + for (i = 0; i < VSYSCALL_EHDR->e_phnum; ++i) { \ + struct elf_phdr phdr = vsyscall_phdrs[i]; \ + if (phdr.p_type == PT_LOAD) { \ + ofs = phdr.p_offset = offset; \ + offset += phdr.p_filesz; \ + } \ + else \ + phdr.p_offset += ofs; \ + phdr.p_paddr = 0; /* match other core phdrs */ \ + DUMP_WRITE(&phdr, sizeof(phdr)); \ + } \ +} while (0) +#define ELF_CORE_WRITE_EXTRA_DATA \ +do { \ + const struct elf_phdr *const vsyscall_phdrs = \ + (const struct elf_phdr *) (VSYSCALL_BASE \ + + VSYSCALL_EHDR->e_phoff); \ + int i; \ + for (i = 0; i < VSYSCALL_EHDR->e_phnum; ++i) { \ + if (vsyscall_phdrs[i].p_type == PT_LOAD) \ + DUMP_WRITE((void *) vsyscall_phdrs[i].p_vaddr, \ + vsyscall_phdrs[i].p_filesz); \ + } \ +} while (0) +#endif + +#undef ELF_CORE_WRITE_EXTRA_PHDRS +#undef ELF_CORE_WRITE_EXTRA_DATA + +#define R_386_NONE 0 +#define R_386_32 1 +#define R_386_PC32 2 +#define R_386_GOT32 3 +#define R_386_PLT32 4 +#define R_386_COPY 5 +#define R_386_GLOB_DAT 6 +#define R_386_JMP_SLOT 7 +#define R_386_RELATIVE 8 +#define R_386_GOTOFF 9 +#define R_386_GOTPC 10 +#define R_386_NUM 11 + /********* Bits for asm-um/delay.h **********/ typedef unsigned long um_udelay_t; --- linux-2.6.9-rc1/include/asm-um/common.lds.S 2004-08-24 00:02:23.000000000 -0700 +++ 25/include/asm-um/common.lds.S 2004-09-02 20:51:52.000000000 -0700 @@ -1,3 +1,5 @@ +#include + .fini : { *(.fini) } =0x9090 _etext = .; PROVIDE (etext = .); @@ -13,18 +15,6 @@ RODATA - __start___ksymtab = .; /* Kernel symbol table */ - __ksymtab : { *(__ksymtab) } - __stop___ksymtab = .; - - __start___gpl_ksymtab = .; /* Kernel symbol table: GPL-only symbols */ - __gpl_ksymtab : { *(__gpl_ksymtab) } - __stop___gpl_ksymtab = .; - - __start___kallsyms = .; /* All kernel symbols */ - __kallsyms : { *(__kallsyms) } - __stop___kallsyms = .; - .unprotected : { *(.unprotected) } . = ALIGN(4096); PROVIDE (_unprotected_end = .); @@ -46,10 +36,6 @@ .init.setup : { *(.init.setup) } __setup_end = .; - __start___param = .; - __param : { *(__param) } - __stop___param = .; - . = ALIGN(32); __per_cpu_start = . ; .data.percpu : { *(.data.percpu) } @@ -67,11 +53,17 @@ } __initcall_end = .; + __con_initcall_start = .; + .con_initcall.init : { *(.con_initcall.init) } + __con_initcall_end = .; + __uml_initcall_start = .; .uml.initcall.init : { *(.uml.initcall.init) } __uml_initcall_end = .; __init_end = .; + SECURITY_INIT + __exitcall_begin = .; .exitcall : { *(.exitcall.exit) } __exitcall_end = .; @@ -80,7 +72,33 @@ .uml.exitcall : { *(.uml.exitcall.exit) } __uml_exitcall_end = .; - . = ALIGN(4096); + . = ALIGN(4); + __alt_instructions = .; + .altinstructions : { *(.altinstructions) } + __alt_instructions_end = .; + .altinstr_replacement : { *(.altinstr_replacement) } + /* .exit.text is discard at runtime, not link time, to deal with references + from .altinstructions and .eh_frame */ + .exit.text : { *(.exit.text) } + .exit.data : { *(.exit.data) } + + __preinit_array_start = .; + .preinit_array : { *(.preinit_array) } + __preinit_array_end = .; + __init_array_start = .; + .init_array : { *(.init_array) } + __init_array_end = .; + __fini_array_start = .; + .fini_array : { *(.fini_array) } + __fini_array_end = .; + + . = ALIGN(4096); __initramfs_start = .; .init.ramfs : { *(.init.ramfs) } __initramfs_end = .; + + /* Sections to be discarded */ + /DISCARD/ : { + *(.exitcall.exit) + } + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-um/cpufeature.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,6 @@ +#ifndef __UM_CPUFEATURE_H +#define __UM_CPUFEATURE_H + +#include "asm/arch/cpufeature.h" + +#endif --- linux-2.6.9-rc1/include/asm-um/current.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/include/asm-um/current.h 2004-09-02 20:51:52.000000000 -0700 @@ -16,8 +16,10 @@ struct thread_info; #define CURRENT_THREAD(dummy) (((unsigned long) &dummy) & \ (PAGE_MASK << CONFIG_KERNEL_STACK_ORDER)) -#define current ({ int dummy; \ - ((struct thread_info *) CURRENT_THREAD(dummy))->task; }) +#define current_thread \ + ({ int dummy; ((struct thread_info *) CURRENT_THREAD(dummy)); }) + +#define current (current_thread->task) #endif /* __ASSEMBLY__ */ --- linux-2.6.9-rc1/include/asm-um/dma-mapping.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/include/asm-um/dma-mapping.h 2004-09-02 20:51:52.000000000 -0700 @@ -1 +1,119 @@ -#include +#ifndef _ASM_DMA_MAPPING_H +#define _ASM_DMA_MAPPING_H + +static inline int +dma_supported(struct device *dev, u64 mask) +{ + BUG(); + return(0); +} + +static inline int +dma_set_mask(struct device *dev, u64 dma_mask) +{ + BUG(); + return(0); +} + +static inline void * +dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, + int flag) +{ + BUG(); + return((void *) 0); +} + +static inline void +dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, + dma_addr_t dma_handle) +{ + BUG(); +} + +static inline dma_addr_t +dma_map_single(struct device *dev, void *cpu_addr, size_t size, + enum dma_data_direction direction) +{ + BUG(); + return(0); +} + +static inline void +dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, + enum dma_data_direction direction) +{ + BUG(); +} + +static inline dma_addr_t +dma_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + BUG(); + return(0); +} + +static inline void +dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size, + enum dma_data_direction direction) +{ + BUG(); +} + +static inline int +dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, + enum dma_data_direction direction) +{ + BUG(); + return(0); +} + +static inline void +dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries, + enum dma_data_direction direction) +{ + BUG(); +} + +static inline void +dma_sync_single(struct device *dev, dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction) +{ + BUG(); +} + +static inline void +dma_sync_sg(struct device *dev, struct scatterlist *sg, int nelems, + enum dma_data_direction direction) +{ + BUG(); +} + +#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f) +#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h) +#define dma_is_consistent(d) (1) + +static inline int +dma_get_cache_alignment(void) +{ + BUG(); + return(0); +} + +static inline void +dma_sync_single_range(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + BUG(); +} + +static inline void +dma_cache_sync(void *vaddr, size_t size, + enum dma_data_direction direction) +{ + BUG(); +} + +#endif --- linux-2.6.9-rc1/include/asm-um/elf.h 2004-08-24 00:02:59.000000000 -0700 +++ 25/include/asm-um/elf.h 2004-09-02 20:51:52.000000000 -0700 @@ -15,4 +15,17 @@ #define USE_ELF_CORE_DUMP +#define R_386_NONE 0 +#define R_386_32 1 +#define R_386_PC32 2 +#define R_386_GOT32 3 +#define R_386_PLT32 4 +#define R_386_COPY 5 +#define R_386_GLOB_DAT 6 +#define R_386_JMP_SLOT 7 +#define R_386_RELATIVE 8 +#define R_386_GOTOFF 9 +#define R_386_GOTPC 10 +#define R_386_NUM 11 + #endif --- linux-2.6.9-rc1/include/asm-um/fixmap.h 2004-08-24 00:03:32.000000000 -0700 +++ 25/include/asm-um/fixmap.h 2004-09-02 20:51:52.000000000 -0700 @@ -34,6 +34,7 @@ enum fixed_addresses { FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */ FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1, #endif + FIX_VSYSCALL, __end_of_fixed_addresses }; @@ -63,6 +64,13 @@ extern unsigned long get_kmem_end(void); #define __fix_to_virt(x) (FIXADDR_TOP - ((x) << PAGE_SHIFT)) #define __virt_to_fix(x) ((FIXADDR_TOP - ((x)&PAGE_MASK)) >> PAGE_SHIFT) +/* + * This is the range that is readable by user mode, and things + * acting like user mode such as get_user_pages. + */ +#define FIXADDR_USER_START (__fix_to_virt(FIX_VSYSCALL)) +#define FIXADDR_USER_END (FIXADDR_USER_START + PAGE_SIZE) + extern void __this_fixmap_does_not_exist(void); /* --- linux-2.6.9-rc1/include/asm-um/irq.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/include/asm-um/irq.h 2004-09-02 20:51:52.000000000 -0700 @@ -1,15 +1,6 @@ #ifndef __UM_IRQ_H #define __UM_IRQ_H -/* The i386 irq.h has a struct task_struct in a prototype without including - * sched.h. This forward declaration kills the resulting warning. - */ -struct task_struct; - -#include "asm/ptrace.h" - -#undef NR_IRQS - #define TIMER_IRQ 0 #define UMN_IRQ 1 #define CONSOLE_IRQ 2 @@ -28,13 +19,4 @@ struct task_struct; #define LAST_IRQ XTERM_IRQ #define NR_IRQS (LAST_IRQ + 1) -extern int um_request_irq(unsigned int irq, int fd, int type, - void (*handler)(int, void *, struct pt_regs *), - unsigned long irqflags, const char * devname, - void *dev_id); - -struct irqaction; -struct pt_regs; -int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); - #endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-um/local.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,6 @@ +#ifndef __UM_LOCAL_H +#define __UM_LOCAL_H + +#include "asm/arch/local.h" + +#endif --- linux-2.6.9-rc1/include/asm-um/mmu_context.h 2004-08-24 00:02:47.000000000 -0700 +++ 25/include/asm-um/mmu_context.h 2004-09-02 20:51:52.000000000 -0700 @@ -26,8 +26,8 @@ static inline void switch_mm(struct mm_s unsigned cpu = smp_processor_id(); if(prev != next){ - clear_bit(cpu, &prev->cpu_vm_mask); - set_bit(cpu, &next->cpu_vm_mask); + cpu_clear(cpu, prev->cpu_vm_mask); + cpu_set(cpu, next->cpu_vm_mask); if(next != &init_mm) CHOOSE_MODE((void) 0, switch_mm_skas(next->context.skas.mm_fd)); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-um/module-generic.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,6 @@ +#ifndef __UM_MODULE_GENERIC_H +#define __UM_MODULE_GENERIC_H + +#include "asm/arch/module.h" + +#endif --- linux-2.6.9-rc1/include/asm-um/module.h 2004-08-24 00:03:10.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,13 +0,0 @@ -#ifndef __UM_MODULE_H -#define __UM_MODULE_H - -/* UML is simple */ -struct mod_arch_specific -{ -}; - -#define Elf_Shdr Elf32_Shdr -#define Elf_Sym Elf32_Sym -#define Elf_Ehdr Elf32_Ehdr - -#endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-um/module-i386.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,13 @@ +#ifndef __UM_MODULE_I386_H +#define __UM_MODULE_I386_H + +/* UML is simple */ +struct mod_arch_specific +{ +}; + +#define Elf_Shdr Elf32_Shdr +#define Elf_Sym Elf32_Sym +#define Elf_Ehdr Elf32_Ehdr + +#endif --- linux-2.6.9-rc1/include/asm-um/page.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/include/asm-um/page.h 2004-09-03 00:56:41.917788176 -0700 @@ -1,10 +1,14 @@ +/* + * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + #ifndef __UM_PAGE_H #define __UM_PAGE_H struct page; #include "asm/arch/page.h" -#include "asm/bug.h" #undef __pa #undef __va @@ -24,25 +28,26 @@ extern unsigned long uml_physmem; #define __va_space (8*1024*1024) -extern unsigned long region_pa(void *virt); -extern void *region_va(unsigned long phys); - -#define __pa(virt) region_pa((void *) (virt)) -#define __va(phys) region_va((unsigned long) (phys)) - -extern unsigned long page_to_pfn(struct page *page); -extern struct page *pfn_to_page(unsigned long pfn); +extern unsigned long to_phys(void *virt); +extern void *to_virt(unsigned long phys); -extern struct page *phys_to_page(unsigned long phys); +#define __pa(virt) to_phys((void *) virt) +#define __va(phys) to_virt((unsigned long) phys) -#define virt_to_page(v) (phys_to_page(__pa(v))) +#define page_to_pfn(page) ((page) - mem_map) +#define pfn_to_page(pfn) (mem_map + (pfn)) -extern struct page *page_mem_map(struct page *page); - -#define pfn_valid(pfn) (page_mem_map(pfn_to_page(pfn)) != NULL) -#define virt_addr_valid(v) pfn_valid(__pa(v) >> PAGE_SHIFT) +#define phys_to_pfn(p) ((p) >> PAGE_SHIFT) +#define pfn_to_phys(pfn) ((pfn) << PAGE_SHIFT) +#define pfn_valid(pfn) ((pfn) < max_mapnr) +#define virt_addr_valid(v) pfn_valid(phys_to_pfn(__pa(v))) + extern struct page *arch_validate(struct page *page, int mask, int order); #define HAVE_ARCH_VALIDATE +#define devmem_is_allowed(x) 1 + +extern void arch_free_page(struct page *page, int order); +#define HAVE_ARCH_FREE_PAGE #endif --- linux-2.6.9-rc1/include/asm-um/pgtable.h 2004-08-24 00:03:50.000000000 -0700 +++ 25/include/asm-um/pgtable.h 2004-09-02 20:51:52.000000000 -0700 @@ -12,8 +12,6 @@ #include "asm/page.h" #include "asm/fixmap.h" -extern pgd_t swapper_pg_dir[1024]; - extern void *um_virt_to_phys(struct task_struct *task, unsigned long virt, pte_t *pte_out); @@ -49,6 +47,8 @@ extern unsigned long *empty_zero_page; #define pgd_ERROR(e) \ printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e)) +extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; + /* * pgd entries used up by user/kernel: */ @@ -65,10 +65,10 @@ extern unsigned long *empty_zero_page; * area for the same reason. ;) */ -extern unsigned long high_physmem; +extern unsigned long end_iomem; #define VMALLOC_OFFSET (__va_space) -#define VMALLOC_START (((unsigned long) high_physmem + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) +#define VMALLOC_START ((end_iomem + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) #ifdef CONFIG_HIGHMEM # define VMALLOC_END (PKMAP_BASE-2*PAGE_SIZE) @@ -78,12 +78,13 @@ extern unsigned long high_physmem; #define _PAGE_PRESENT 0x001 #define _PAGE_NEWPAGE 0x002 -#define _PAGE_PROTNONE 0x004 /* If not present */ -#define _PAGE_RW 0x008 -#define _PAGE_USER 0x010 -#define _PAGE_ACCESSED 0x020 -#define _PAGE_DIRTY 0x040 -#define _PAGE_NEWPROT 0x080 +#define _PAGE_NEWPROT 0x004 +#define _PAGE_FILE 0x008 /* set:pagecache unset:swap */ +#define _PAGE_PROTNONE 0x010 /* If not present */ +#define _PAGE_RW 0x020 +#define _PAGE_USER 0x040 +#define _PAGE_ACCESSED 0x080 +#define _PAGE_DIRTY 0x100 #define REGION_MASK 0xf0000000 #define REGION_SHIFT 28 @@ -143,7 +144,8 @@ extern pte_t * __bad_pagetable(void); #define BAD_PAGETABLE __bad_pagetable() #define BAD_PAGE __bad_page() -#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page)) + +#define ZERO_PAGE(vaddr) virt_to_page(empty_zero_page) /* number of bits that fit into a memory pointer */ #define BITS_PER_PTR (8*sizeof(unsigned long)) @@ -164,9 +166,6 @@ extern pte_t * __bad_pagetable(void); #define pte_clear(xp) do { pte_val(*(xp)) = _PAGE_NEWPAGE; } while (0) -#define phys_region_index(x) (((x) & REGION_MASK) >> REGION_SHIFT) -#define pte_region_index(x) phys_region_index(pte_val(x)) - #define pmd_none(x) (!(pmd_val(x) & ~_PAGE_NEWPAGE)) #define pmd_bad(x) ((pmd_val(x) & (~PAGE_MASK & ~_PAGE_USER)) != _KERNPG_TABLE) #define pmd_present(x) (pmd_val(x) & _PAGE_PRESENT) @@ -188,19 +187,25 @@ static inline void pgd_clear(pgd_t * pgd #define pages_to_mb(x) ((x) >> (20-PAGE_SHIFT)) -extern struct page *pte_mem_map(pte_t pte); -extern struct page *phys_mem_map(unsigned long phys); -extern unsigned long phys_to_pfn(unsigned long p); -extern unsigned long pfn_to_phys(unsigned long pfn); - -#define pte_page(x) pfn_to_page(pte_pfn(x)) -#define pte_address(x) (__va(pte_val(x) & PAGE_MASK)) -#define mk_phys(a, r) ((a) + (r << REGION_SHIFT)) -#define phys_addr(p) ((p) & ~REGION_MASK) -#define phys_page(p) (phys_mem_map(p) + ((phys_addr(p)) >> PAGE_SHIFT)) +#define pte_page(pte) phys_to_page(pte_val(pte)) +#define pmd_page(pmd) phys_to_page(pmd_val(pmd) & PAGE_MASK) + #define pte_pfn(x) phys_to_pfn(pte_val(x)) #define pfn_pte(pfn, prot) __pte(pfn_to_phys(pfn) | pgprot_val(prot)) -#define pfn_pmd(pfn, prot) __pmd(pfn_to_phys(pfn) | pgprot_val(prot)) + +extern struct page *phys_to_page(const unsigned long phys); +extern struct page *__virt_to_page(const unsigned long virt); +#define virt_to_page(addr) __virt_to_page((const unsigned long) addr) + +/* + * Bits 0 through 3 are taken + */ +#define PTE_FILE_MAX_BITS 28 + +#define pte_to_pgoff(pte) ((pte).pte_low >> 4) + +#define pgoff_to_pte(off) \ + ((pte_t) { ((off) << 4) + _PAGE_FILE }) static inline pte_t pte_mknewprot(pte_t pte) { @@ -235,6 +240,12 @@ static inline void set_pte(pte_t *pteptr * The following only work if pte_present() is true. * Undefined behaviour if not.. */ +static inline int pte_user(pte_t pte) +{ + return((pte_val(pte) & _PAGE_USER) && + !(pte_val(pte) & _PAGE_PROTNONE)); +} + static inline int pte_read(pte_t pte) { return((pte_val(pte) & _PAGE_USER) && @@ -252,6 +263,14 @@ static inline int pte_write(pte_t pte) !(pte_val(pte) & _PAGE_PROTNONE)); } +/* + * The following only works if pte_present() is not true. + */ +static inline int pte_file(pte_t pte) +{ + return (pte).pte_low & _PAGE_FILE; +} + static inline int pte_dirty(pte_t pte) { return pte_val(pte) & _PAGE_DIRTY; } static inline int pte_young(pte_t pte) { return pte_val(pte) & _PAGE_ACCESSED; } static inline int pte_newpage(pte_t pte) { return pte_val(pte) & _PAGE_NEWPAGE; } @@ -334,14 +353,7 @@ extern unsigned long page_to_phys(struct * and a page entry and page directory to the page they refer to. */ -#define mk_pte(page, pgprot) \ -({ \ - pte_t __pte; \ - \ - pte_val(__pte) = page_to_phys(page) + pgprot_val(pgprot);\ - if(pte_present(__pte)) pte_mknewprot(pte_mknewpage(__pte)); \ - __pte; \ -}) +extern pte_t mk_pte(struct page *page, pgprot_t pgprot); static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) { @@ -351,17 +363,27 @@ static inline pte_t pte_modify(pte_t pte } #define pmd_page_kernel(pmd) ((unsigned long) __va(pmd_val(pmd) & PAGE_MASK)) -#define pmd_page(pmd) (phys_mem_map(pmd_val(pmd) & PAGE_MASK) + \ - ((phys_addr(pmd_val(pmd)) >> PAGE_SHIFT))) -/* to find an entry in a page-table-directory. */ +/* + * the pgd page can be thought of an array like this: pgd_t[PTRS_PER_PGD] + * + * this macro returns the index of the entry in the pgd page which would + * control the given virtual address + */ #define pgd_index(address) ((address >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) -/* to find an entry in a page-table-directory */ +/* + * pgd_offset() returns a (pgd_t *) + * pgd_index() is used get the offset into the pgd page's array of pgd_t's; + */ #define pgd_offset(mm, address) \ ((mm)->pgd + ((address) >> PGDIR_SHIFT)) -/* to find an entry in a kernel page-table-directory */ + +/* + * a shortcut which implies the use of the kernel's pgd, instead + * of a process's + */ #define pgd_offset_k(address) pgd_offset(&init_mm, address) #define pmd_index(address) \ @@ -373,7 +395,12 @@ static inline pmd_t * pmd_offset(pgd_t * return (pmd_t *) dir; } -/* Find an entry in the third-level page table.. */ +/* + * the pte page can be thought of an array like this: pte_t[PTRS_PER_PTE] + * + * this macro returns the index of the entry in the pte page which would + * control the given virtual address + */ #define pte_index(address) (((address) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) #define pte_offset_kernel(dir, address) \ ((pte_t *) pmd_page_kernel(*(dir)) + pte_index(address)) @@ -387,11 +414,11 @@ static inline pmd_t * pmd_offset(pgd_t * #define update_mmu_cache(vma,address,pte) do ; while (0) /* Encode and de-code a swap entry */ -#define __swp_type(x) (((x).val >> 3) & 0x7f) -#define __swp_offset(x) ((x).val >> 10) +#define __swp_type(x) (((x).val >> 4) & 0x3f) +#define __swp_offset(x) ((x).val >> 11) #define __swp_entry(type, offset) \ - ((swp_entry_t) { ((type) << 3) | ((offset) << 10) }) + ((swp_entry_t) { ((type) << 4) | ((offset) << 11) }) #define __pte_to_swp_entry(pte) \ ((swp_entry_t) { pte_val(pte_mkuptodate(pte)) }) #define __swp_entry_to_pte(x) ((pte_t) { (x).val }) --- linux-2.6.9-rc1/include/asm-um/processor-generic.h 2004-08-24 00:02:22.000000000 -0700 +++ 25/include/asm-um/processor-generic.h 2004-09-02 20:51:52.000000000 -0700 @@ -11,33 +11,14 @@ struct pt_regs; struct task_struct; #include "linux/config.h" -#include "linux/signal.h" #include "asm/ptrace.h" -#include "asm/siginfo.h" #include "choose-mode.h" struct mm_struct; #define current_text_addr() ((void *) 0) -#define cpu_relax() do ; while (0) - -#ifdef CONFIG_MODE_TT -struct proc_tt_mode { - int extern_pid; - int tracing; - int switch_pipe[2]; - int singlestep_syscall; - int vm_seq; -}; -#endif - -#ifdef CONFIG_MODE_SKAS -struct proc_skas_mode { - void *switch_buf; - void *fork_buf; -}; -#endif +#define cpu_relax() barrier() struct thread_struct { int forking; @@ -46,6 +27,7 @@ struct thread_struct { struct pt_regs regs; unsigned long cr2; int err; + unsigned long trap_no; void *fault_addr; void *fault_catcher; struct task_struct *prev_sched; @@ -54,10 +36,20 @@ struct thread_struct { struct arch_thread arch; union { #ifdef CONFIG_MODE_TT - struct proc_tt_mode tt; + struct { + int extern_pid; + int tracing; + int switch_pipe[2]; + int singlestep_syscall; + int vm_seq; + } tt; #endif #ifdef CONFIG_MODE_SKAS - struct proc_skas_mode skas; + struct { + void *switch_buf; + void *fork_buf; + int mm_count; + } skas; #endif } mode; struct { @@ -99,14 +91,19 @@ typedef struct { } mm_segment_t; extern struct task_struct *alloc_task_struct(void); -extern void free_task_struct(struct task_struct *task); extern void release_thread(struct task_struct *); extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); extern void dump_thread(struct pt_regs *regs, struct user *u); +extern void prepare_to_copy(struct task_struct *tsk); extern unsigned long thread_saved_pc(struct task_struct *t); +static inline void mm_copy_segments(struct mm_struct *from_mm, + struct mm_struct *new_mm) +{ +} + #define init_stack (init_thread_union.stack) /* --- linux-2.6.9-rc1/include/asm-um/processor-i386.h 2004-08-24 00:01:53.000000000 -0700 +++ 25/include/asm-um/processor-i386.h 2004-09-02 20:51:52.000000000 -0700 @@ -6,8 +6,8 @@ #ifndef __UM_PROCESSOR_I386_H #define __UM_PROCESSOR_I386_H -extern int cpu_has_xmm; -extern int cpu_has_cmov; +extern int host_has_xmm; +extern int host_has_cmov; struct arch_thread { unsigned long debugregs[8]; --- linux-2.6.9-rc1/include/asm-um/ptrace-generic.h 2004-08-24 00:03:26.000000000 -0700 +++ 25/include/asm-um/ptrace-generic.h 2004-09-02 20:51:52.000000000 -0700 @@ -45,6 +45,8 @@ struct pt_regs { #define PT_REGS_SC(r) UPT_SC(&(r)->regs) +#define instruction_pointer(regs) PT_REGS_IP(regs) + struct task_struct; extern unsigned long getreg(struct task_struct *child, int regno); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-um/sections.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,7 @@ +#ifndef _UM_SECTIONS_H +#define _UM_SECTIONS_H + +/* nothing to see, move along */ +#include + +#endif --- linux-2.6.9-rc1/include/asm-um/smp.h 2004-08-24 00:01:51.000000000 -0700 +++ 25/include/asm-um/smp.h 2004-09-02 20:51:52.000000000 -0700 @@ -10,7 +10,7 @@ extern cpumask_t cpu_online_map; -#define smp_processor_id() (current->thread_info->cpu) +#define smp_processor_id() (current_thread->cpu) #define cpu_logical_map(n) (n) #define cpu_number_map(n) (n) #define PROC_CHANGE_PENALTY 15 /* Pick a number, any number */ --- linux-2.6.9-rc1/include/asm-um/smplock.h 2004-08-24 00:02:19.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,6 +0,0 @@ -#ifndef __UM_SMPLOCK_H -#define __UM_SMPLOCK_H - -#include "asm/arch/smplock.h" - -#endif --- linux-2.6.9-rc1/include/asm-um/spinlock.h 2004-08-24 00:03:31.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,10 +0,0 @@ -#ifndef __UM_SPINLOCK_H -#define __UM_SPINLOCK_H - -#include "linux/config.h" - -#ifdef CONFIG_SMP -#include "asm/arch/spinlock.h" -#endif - -#endif --- linux-2.6.9-rc1/include/asm-um/system-generic.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/include/asm-um/system-generic.h 2004-09-02 20:51:52.000000000 -0700 @@ -23,8 +23,10 @@ extern int get_signals(void); extern void block_signals(void); extern void unblock_signals(void); -#define local_save_flags(flags) do { (flags) = get_signals(); } while(0) -#define local_irq_restore(flags) do { set_signals(flags); } while(0) +#define local_save_flags(flags) do { typecheck(unsigned long, flags); \ + (flags) = get_signals(); } while(0) +#define local_irq_restore(flags) do { typecheck(unsigned long, flags); \ + set_signals(flags); } while(0) #define local_irq_save(flags) do { local_save_flags(flags); \ local_irq_disable(); } while(0) @@ -39,4 +41,7 @@ extern void unblock_signals(void); (flags == 0); \ }) +extern void *_switch_to(void *prev, void *next, void *last); +#define switch_to(prev, next, last) prev = _switch_to(prev, next, last) + #endif --- linux-2.6.9-rc1/include/asm-um/system-i386.h 2004-08-24 00:02:26.000000000 -0700 +++ 25/include/asm-um/system-i386.h 2004-09-02 20:51:52.000000000 -0700 @@ -2,36 +2,5 @@ #define __UM_SYSTEM_I386_H #include "asm/system-generic.h" - -static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, - unsigned long new, int size) -{ - unsigned long prev; - switch (size) { - case 1: - __asm__ __volatile__(LOCK_PREFIX "cmpxchgb %b1,%2" - : "=a"(prev) - : "q"(new), "m"(*__xg(ptr)), "0"(old) - : "memory"); - return prev; - case 2: - __asm__ __volatile__(LOCK_PREFIX "cmpxchgw %w1,%2" - : "=a"(prev) - : "q"(new), "m"(*__xg(ptr)), "0"(old) - : "memory"); - return prev; - case 4: - __asm__ __volatile__(LOCK_PREFIX "cmpxchgl %1,%2" - : "=a"(prev) - : "q"(new), "m"(*__xg(ptr)), "0"(old) - : "memory"); - return prev; - } - return old; -} - -#define cmpxchg(ptr,o,n)\ - ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o),\ - (unsigned long)(n),sizeof(*(ptr)))) #endif --- linux-2.6.9-rc1/include/asm-um/thread_info.h 2004-08-24 00:02:25.000000000 -0700 +++ 25/include/asm-um/thread_info.h 2004-09-02 20:51:52.000000000 -0700 @@ -9,6 +9,7 @@ #ifndef __ASSEMBLY__ #include +#include struct thread_info { struct task_struct *task; /* main task structure */ @@ -43,15 +44,18 @@ struct thread_info { static inline struct thread_info *current_thread_info(void) { struct thread_info *ti; - __asm__("andl %%esp,%0; ":"=r" (ti) : "0" (~16383UL)); + unsigned long mask = PAGE_SIZE * + (1 << CONFIG_KERNEL_STACK_ORDER) - 1; + __asm__("andl %%esp,%0; ":"=r" (ti) : "0" (~mask)); return ti; } /* thread information allocation */ -#define THREAD_SIZE (4*PAGE_SIZE) -#define alloc_thread_info(tsk) ((struct thread_info *) \ - __get_free_pages(GFP_KERNEL,2)) -#define free_thread_info(ti) free_pages((unsigned long) (ti), 2) +#define THREAD_SIZE ((1 << CONFIG_KERNEL_STACK_ORDER) * PAGE_SIZE) +#define alloc_thread_info(tsk) \ + ((struct thread_info *) kmalloc(THREAD_SIZE, GFP_KERNEL)) +#define free_thread_info(ti) kfree(ti) + #define get_thread_info(ti) get_task_struct((ti)->task) #define put_thread_info(ti) put_task_struct((ti)->task) @@ -65,11 +69,13 @@ static inline struct thread_info *curren #define TIF_POLLING_NRFLAG 3 /* true if poll_idle() is polling * TIF_NEED_RESCHED */ +#define TIF_RESTART_BLOCK 4 #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) #define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG) +#define _TIF_RESTART_BLOCK (1 << TIF_RESTART_BLOCK) #endif --- linux-2.6.9-rc1/include/asm-um/timex.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/include/asm-um/timex.h 2004-09-02 20:51:52.000000000 -0700 @@ -1,8 +1,6 @@ #ifndef __UM_TIMEX_H #define __UM_TIMEX_H -#include "linux/time.h" - typedef unsigned long cycles_t; #define cacheflush_time (0) --- linux-2.6.9-rc1/include/asm-um/uaccess.h 2004-08-24 00:03:17.000000000 -0700 +++ 25/include/asm-um/uaccess.h 2004-09-02 20:51:52.000000000 -0700 @@ -6,6 +6,8 @@ #ifndef __UM_UACCESS_H #define __UM_UACCESS_H +#include "linux/sched.h" + #define VERIFY_READ 0 #define VERIFY_WRITE 1 @@ -34,6 +36,9 @@ #define __copy_to_user(to, from, n) copy_to_user(to, from, n) +#define __copy_to_user_inatomic __copy_to_user +#define __copy_from_user_inatomic __copy_from_user + #define __get_user(x, ptr) \ ({ \ const __typeof__(ptr) __private_ptr = ptr; \ --- linux-2.6.9-rc1/include/asm-um/unistd.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/include/asm-um/unistd.h 2004-09-02 20:51:52.000000000 -0700 @@ -48,7 +48,10 @@ extern int um_execve(const char *file, c set_fs(KERNEL_DS); \ ret = sys(args); \ set_fs(fs); \ - return ret; + if (ret >= 0) \ + return ret; \ + errno = -(long)ret; \ + return -1; static inline long open(const char *pathname, int flags, int mode) { --- linux-2.6.9-rc1/include/asm-v850/hardirq.h 2004-08-24 00:01:55.000000000 -0700 +++ 25/include/asm-v850/hardirq.h 2004-09-03 00:56:41.028923304 -0700 @@ -36,20 +36,6 @@ typedef struct { #define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) #define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) -#define __MASK(x) ((1UL << (x))-1) - -#define PREEMPT_MASK (__MASK(PREEMPT_BITS) << PREEMPT_SHIFT) -#define HARDIRQ_MASK (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) -#define SOFTIRQ_MASK (__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) - -#define hardirq_count() (preempt_count() & HARDIRQ_MASK) -#define softirq_count() (preempt_count() & SOFTIRQ_MASK) -#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) - -#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) -#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) -#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) - /* * The hardirq mask has to be large enough to have * space for potentially all IRQ sources in the system @@ -59,27 +45,7 @@ typedef struct { # error HARDIRQ_BITS is too low! #endif -/* - * Are we doing bottom half or hardware interrupt processing? - * Are we in a softirq context? Interrupt context? - */ -#define in_irq() (hardirq_count()) -#define in_softirq() (softirq_count()) -#define in_interrupt() (irq_count()) - -#define hardirq_trylock() (!in_interrupt()) -#define hardirq_endlock() do { } while (0) - #define irq_enter() (preempt_count() += HARDIRQ_OFFSET) - -#ifdef CONFIG_PREEMPT -# define in_atomic() (preempt_count() != kernel_locked()) -# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) -#else -# define in_atomic() (preempt_count() != 0) -# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET -#endif - #define irq_exit() \ do { \ preempt_count() -= IRQ_EXIT_OFFSET; \ @@ -88,10 +54,4 @@ do { \ preempt_enable_no_resched(); \ } while (0) -#ifndef CONFIG_SMP -# define synchronize_irq(irq) barrier() -#else -# error v850nommu SMP is not available -#endif /* CONFIG_SMP */ - #endif /* __V850_HARDIRQ_H__ */ --- linux-2.6.9-rc1/include/asm-v850/page.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/include/asm-v850/page.h 2004-09-03 00:56:41.917788176 -0700 @@ -141,6 +141,8 @@ extern __inline__ int get_order (unsigne #define __va(x) ((void *)__phys_to_virt ((unsigned long)(x))) +#define devmem_is_allowed(x) 1 + #endif /* KERNEL */ #endif /* __V850_PAGE_H__ */ --- linux-2.6.9-rc1/include/asm-v850/ptrace.h 2004-08-24 00:03:49.000000000 -0700 +++ 25/include/asm-v850/ptrace.h 2004-09-02 20:51:52.000000000 -0700 @@ -76,6 +76,7 @@ struct pt_regs #define instruction_pointer(regs) ((regs)->pc) +#define profile_pc(regs) instruction_pointer(regs) #define user_mode(regs) (!(regs)->kernel_mode) /* When a struct pt_regs is used to save user state for a system call in --- linux-2.6.9-rc1/include/asm-v850/uaccess.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/include/asm-v850/uaccess.h 2004-09-02 20:51:52.000000000 -0700 @@ -112,6 +112,9 @@ extern int bad_user_access_length (void) #define __copy_from_user(to, from, n) (memcpy (to, from, n), 0) #define __copy_to_user(to, from, n) (memcpy(to, from, n), 0) +#define __copy_to_user_inatomic __copy_to_user +#define __copy_from_user_inatomic __copy_from_user + #define copy_from_user(to, from, n) __copy_from_user (to, from, n) #define copy_to_user(to, from, n) __copy_to_user(to, from, n) --- linux-2.6.9-rc1/include/asm-x86_64/acpi.h 2004-08-24 00:03:14.000000000 -0700 +++ 25/include/asm-x86_64/acpi.h 2004-09-02 20:51:52.000000000 -0700 @@ -99,6 +99,11 @@ __acpi_release_global_lock (unsigned int :"=r"(n_hi), "=r"(n_lo) \ :"0"(n_hi), "1"(n_lo)) +/* + * Refer Intel ACPI _PDC support document for bit definitions + */ +#define ACPI_PDC_EST_CAPABILITY_SMP 0xa +#define ACPI_PDC_EST_CAPABILITY_MSR 0x1 #ifdef CONFIG_ACPI_BOOT extern int acpi_lapic; --- linux-2.6.9-rc1/include/asm-x86_64/apicdef.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/include/asm-x86_64/apicdef.h 2004-09-03 00:56:58.102327752 -0700 @@ -32,6 +32,8 @@ #define SET_APIC_LOGICAL_ID(x) (((x)<<24)) #define APIC_ALL_CPUS 0xFF #define APIC_DFR 0xE0 +#define APIC_DFR_CLUSTER 0x0FFFFFFFul +#define APIC_DFR_FLAT 0xFFFFFFFFul #define APIC_SPIV 0xF0 #define APIC_SPIV_FOCUS_DISABLED (1<<9) #define APIC_SPIV_APIC_ENABLED (1<<8) @@ -86,6 +88,7 @@ #define APIC_LVT_REMOTE_IRR (1<<14) #define APIC_INPUT_POLARITY (1<<13) #define APIC_SEND_PENDING (1<<12) +#define APIC_MODE_MASK 0x700 #define GET_APIC_DELIVERY_MODE(x) (((x)>>8)&0x7) #define SET_APIC_DELIVERY_MODE(x,y) (((x)&~0x700)|((y)<<8)) #define APIC_MODE_FIXED 0x0 @@ -108,7 +111,7 @@ #define APIC_BASE (fix_to_virt(FIX_APIC_BASE)) -#define MAX_IO_APICS 16 +#define MAX_IO_APICS 32 /* * the local APIC register structure, memory mapped. Not terribly well --- linux-2.6.9-rc1/include/asm-x86_64/bitops.h 2004-08-24 00:02:32.000000000 -0700 +++ 25/include/asm-x86_64/bitops.h 2004-09-02 20:51:52.000000000 -0700 @@ -25,10 +25,10 @@ * Note that @nr may be almost arbitrarily large; this function is not * restricted to acting on a single-word quantity. */ -static __inline__ void set_bit(long nr, volatile void * addr) +static __inline__ void set_bit(int nr, volatile void * addr) { __asm__ __volatile__( LOCK_PREFIX - "btsq %1,%0" + "btsl %1,%0" :"=m" (ADDR) :"dIr" (nr) : "memory"); } @@ -254,128 +254,37 @@ static __inline__ int variable_test_bit( #undef ADDR -/** - * find_first_zero_bit - find the first zero bit in a memory region - * @addr: The address to start the search at - * @size: The maximum size to search - * - * Returns the bit-number of the first zero bit, not the number of the byte - * containing a bit. - */ -static __inline__ int find_first_zero_bit(const unsigned long * addr, unsigned size) -{ - int d0, d1, d2; - int res; - - if (!size) - return 0; - __asm__ __volatile__( - "movl $-1,%%eax\n\t" - "xorl %%edx,%%edx\n\t" - "repe; scasl\n\t" - "je 1f\n\t" - "xorl -4(%%rdi),%%eax\n\t" - "subq $4,%%rdi\n\t" - "bsfl %%eax,%%edx\n" - "1:\tsubq %%rbx,%%rdi\n\t" - "shlq $3,%%rdi\n\t" - "addq %%rdi,%%rdx" - :"=d" (res), "=&c" (d0), "=&D" (d1), "=&a" (d2) - :"1" ((size + 31) >> 5), "2" (addr), "b" (addr) : "memory"); - return res; -} - -/** - * find_next_zero_bit - find the first zero bit in a memory region - * @addr: The address to base the search on - * @offset: The bitnumber to start searching at - * @size: The maximum size to search - */ -static __inline__ int find_next_zero_bit (const unsigned long * addr, int size, int offset) -{ - unsigned long * p = ((unsigned long *) addr) + (offset >> 6); - unsigned long set = 0; - unsigned long res, bit = offset&63; - - if (bit) { - /* - * Look for zero in first word - */ - __asm__("bsfq %1,%0\n\t" - "cmoveq %2,%0" - : "=r" (set) - : "r" (~(*p >> bit)), "r"(64L)); - if (set < (64 - bit)) - return set + offset; - set = 64 - bit; - p++; - } - /* - * No zero yet, search remaining full words for a zero - */ - res = find_first_zero_bit ((const unsigned long *)p, size - 64 * (p - (unsigned long *) addr)); - return (offset + set + res); -} - - -/** - * find_first_bit - find the first set bit in a memory region - * @addr: The address to start the search at - * @size: The maximum size to search - * - * Returns the bit-number of the first set bit, not the number of the byte - * containing a bit. - */ -static __inline__ int find_first_bit(const unsigned long * addr, unsigned size) -{ - int d0, d1; - int res; - - /* This looks at memory. Mark it volatile to tell gcc not to move it around */ - __asm__ __volatile__( - "xorl %%eax,%%eax\n\t" - "repe; scasl\n\t" - "jz 1f\n\t" - "leaq -4(%%rdi),%%rdi\n\t" - "bsfl (%%rdi),%%eax\n" - "1:\tsubq %%rbx,%%rdi\n\t" - "shll $3,%%edi\n\t" - "addl %%edi,%%eax" - :"=a" (res), "=&c" (d0), "=&D" (d1) - :"1" ((size + 31) >> 5), "2" (addr), "b" (addr) : "memory"); - return res; -} - -/** - * find_next_bit - find the first set bit in a memory region - * @addr: The address to base the search on - * @offset: The bitnumber to start searching at - * @size: The maximum size to search - */ -static __inline__ int find_next_bit(const unsigned long * addr, int size, int offset) -{ - const unsigned long * p = addr + (offset >> 6); - unsigned long set = 0, bit = offset & 63, res; +extern long find_first_zero_bit(const unsigned long * addr, unsigned long size); +extern long find_next_zero_bit (const unsigned long * addr, long size, long offset); +extern long find_first_bit(const unsigned long * addr, unsigned long size); +extern long find_next_bit(const unsigned long * addr, long size, long offset); + +/* return index of first bet set in val or max when no bit is set */ +static inline unsigned long __scanbit(unsigned long val, unsigned long max) +{ + asm("bsfq %1,%0 ; cmovz %2,%0" : "=&r" (val) : "r" (val), "r" (max)); + return val; +} + +#define find_first_bit(addr,size) \ +((__builtin_constant_p(size) && size <= BITS_PER_LONG ? \ + (__scanbit(*(unsigned long *)addr,(size))) : \ + find_first_bit(addr,size))) + +#define find_next_bit(addr,size,off) \ +((__builtin_constant_p(size) && size <= BITS_PER_LONG ? \ + ((off) + (__scanbit((*(unsigned long *)addr) >> (off),(size)-(off)))) : \ + find_next_bit(addr,size,off))) + +#define find_first_zero_bit(addr,size) \ +((__builtin_constant_p(size) && size <= BITS_PER_LONG ? \ + (__scanbit(~*(unsigned long *)addr,(size))) : \ + find_first_zero_bit(addr,size))) - if (bit) { - /* - * Look for nonzero in the first 64 bits: - */ - __asm__("bsfq %1,%0\n\t" - "cmoveq %2,%0\n\t" - : "=r" (set) - : "r" (*p >> bit), "r" (64L)); - if (set < (64 - bit)) - return set + offset; - set = 64 - bit; - p++; - } - /* - * No set bit yet, search remaining full words for a bit - */ - res = find_first_bit (p, size - 64 * (p - addr)); - return (offset + set + res); -} +#define find_next_zero_bit(addr,size,off) \ +((__builtin_constant_p(size) && size <= BITS_PER_LONG ? \ + ((off)+(__scanbit(~(((*(unsigned long *)addr)) >> (off)),(size)-(off)))) : \ + find_next_zero_bit(addr,size,off))) /* * Find string of zero bits in a bitmap. -1 when not found. --- linux-2.6.9-rc1/include/asm-x86_64/cpufeature.h 2004-08-24 00:02:48.000000000 -0700 +++ 25/include/asm-x86_64/cpufeature.h 2004-09-03 00:56:40.253041256 -0700 @@ -7,7 +7,7 @@ #ifndef __ASM_X8664_CPUFEATURE_H #define __ASM_X8664_CPUFEATURE_H -#define NCAPINTS 5 /* Currently we have 4 32-bit words worth of info */ +#define NCAPINTS 6 /* Intel-defined CPU features, CPUID level 0x00000001, word 0 */ #define X86_FEATURE_FPU (0*32+ 0) /* Onboard FPU */ @@ -63,8 +63,17 @@ #define X86_FEATURE_K8_C (3*32+ 4) /* C stepping K8 */ /* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */ -#define X86_FEATURE_EST (4*32+ 7) /* Enhanced SpeedStep */ +#define X86_FEATURE_XMM3 (4*32+ 0) /* Streaming SIMD Extensions-3 */ #define X86_FEATURE_MWAIT (4*32+ 3) /* Monitor/Mwait support */ +#define X86_FEATURE_DSCPL (4*32+ 4) /* CPL Qualified Debug Store */ +#define X86_FEATURE_EST (4*32+ 7) /* Enhanced SpeedStep */ +#define X86_FEATURE_TM2 (4*32+ 8) /* Thermal Monitor 2 */ +#define X86_FEATURE_CID (4*32+10) /* Context ID */ +#define X86_FEATURE_CX16 (4*32+13) /* CMPXCHG16B */ +#define X86_FEATURE_XTPR (4*32+14) /* Send Task Priority Messages */ + +/* More extended AMD flags: CPUID level 0x80000001, ecx, word 5 */ +#define X86_FEATURE_HTVALID (5*32+ 0) /* HyperThreading valid, otherwise CMP */ #define cpu_has(c, bit) test_bit(bit, (c)->x86_capability) #define boot_cpu_has(bit) test_bit(bit, boot_cpu_data.x86_capability) @@ -81,6 +90,8 @@ #define cpu_has_mmx 1 #define cpu_has_fxsr 1 #define cpu_has_xmm 1 +#define cpu_has_xmm2 1 +#define cpu_has_xmm3 boot_cpu_has(X86_FEATURE_XMM3) #define cpu_has_ht boot_cpu_has(X86_FEATURE_HT) #define cpu_has_mp 1 /* XXX */ #define cpu_has_k6_mtrr 0 --- linux-2.6.9-rc1/include/asm-x86_64/dma-mapping.h 2004-08-24 00:02:48.000000000 -0700 +++ 25/include/asm-x86_64/dma-mapping.h 2004-09-02 20:51:52.000000000 -0700 @@ -1,6 +1,139 @@ #ifndef _X8664_DMA_MAPPING_H #define _X8664_DMA_MAPPING_H 1 -#include +/* + * IOMMU interface. See Documentation/DMA-mapping.txt and DMA-API.txt for + * documentation. + */ + +#include +#include + +#include +#include +#include + +extern dma_addr_t bad_dma_address; +#define dma_mapping_error(x) \ + (swiotlb ? swiotlb_dma_mapping_error(x) : ((x) == bad_dma_address)) + +void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, + unsigned gfp); +void dma_free_coherent(struct device *dev, size_t size, void *vaddr, + dma_addr_t dma_handle); + +#ifdef CONFIG_GART_IOMMU + +extern dma_addr_t dma_map_single(struct device *hwdev, void *ptr, size_t size, + int direction); +extern void dma_unmap_single(struct device *dev, dma_addr_t addr,size_t size, + int direction); + +#else + +/* No IOMMU */ + +static inline dma_addr_t dma_map_single(struct device *hwdev, void *ptr, + size_t size, int direction) +{ + dma_addr_t addr; + + if (direction == DMA_NONE) + out_of_line_bug(); + addr = virt_to_bus(ptr); + + if ((addr+size) & ~*hwdev->dma_mask) + out_of_line_bug(); + return addr; +} + +static inline void dma_unmap_single(struct device *hwdev, dma_addr_t dma_addr, + size_t size, int direction) +{ + if (direction == DMA_NONE) + out_of_line_bug(); + /* Nothing to do */ +} + +#endif + +#define dma_map_page(dev,page,offset,size,dir) \ + dma_map_single((dev), page_address(page)+(offset), (size), (dir)) + +static inline void dma_sync_single_for_cpu(struct device *hwdev, + dma_addr_t dma_handle, + size_t size, int direction) +{ + if (direction == DMA_NONE) + out_of_line_bug(); + + if (swiotlb) + return swiotlb_sync_single_for_cpu(hwdev,dma_handle,size,direction); + + flush_write_buffers(); +} + +static inline void dma_sync_single_for_device(struct device *hwdev, + dma_addr_t dma_handle, + size_t size, int direction) +{ + if (direction == DMA_NONE) + out_of_line_bug(); + + if (swiotlb) + return swiotlb_sync_single_for_device(hwdev,dma_handle,size,direction); + + flush_write_buffers(); +} + +static inline void dma_sync_sg_for_cpu(struct device *hwdev, + struct scatterlist *sg, + int nelems, int direction) +{ + if (direction == DMA_NONE) + out_of_line_bug(); + + if (swiotlb) + return swiotlb_sync_sg_for_cpu(hwdev,sg,nelems,direction); + + flush_write_buffers(); +} + +static inline void dma_sync_sg_for_device(struct device *hwdev, + struct scatterlist *sg, + int nelems, int direction) +{ + if (direction == DMA_NONE) + out_of_line_bug(); + + if (swiotlb) + return swiotlb_sync_sg_for_device(hwdev,sg,nelems,direction); + + flush_write_buffers(); +} + +extern int dma_map_sg(struct device *hwdev, struct scatterlist *sg, + int nents, int direction); +extern void dma_unmap_sg(struct device *hwdev, struct scatterlist *sg, + int nents, int direction); + +#define dma_unmap_page dma_unmap_single + +extern int dma_supported(struct device *hwdev, u64 mask); +extern int dma_get_cache_alignment(void); +#define dma_is_consistent(h) 1 + +static inline int dma_set_mask(struct device *dev, u64 mask) +{ + if (!dev->dma_mask || !dma_supported(dev, mask)) + return -EIO; + *dev->dma_mask = mask; + return 0; +} + +static inline void dma_cache_sync(void *vaddr, size_t size, enum dma_data_direction dir) +{ + flush_write_buffers(); +} #endif --- linux-2.6.9-rc1/include/asm-x86_64/hardirq.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/include/asm-x86_64/hardirq.h 2004-09-03 00:56:41.029923152 -0700 @@ -37,20 +37,6 @@ #define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) #define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) -#define __MASK(x) ((1UL << (x))-1) - -#define PREEMPT_MASK (__MASK(PREEMPT_BITS) << PREEMPT_SHIFT) -#define HARDIRQ_MASK (__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) -#define SOFTIRQ_MASK (__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) - -#define hardirq_count() (preempt_count() & HARDIRQ_MASK) -#define softirq_count() (preempt_count() & SOFTIRQ_MASK) -#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) - -#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) -#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) -#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) - /* * The hardirq mask has to be large enough to have * space for potentially all IRQ sources in the system @@ -60,31 +46,10 @@ # error HARDIRQ_BITS is too low! #endif -/* - * Are we doing bottom half or hardware interrupt processing? - * Are we in a softirq context? Interrupt context? - */ -#define in_irq() (hardirq_count()) -#define in_softirq() (softirq_count()) -#define in_interrupt() (irq_count()) - - -#define hardirq_trylock() (!in_interrupt()) -#define hardirq_endlock() do { } while (0) - -#define irq_enter() (preempt_count() += HARDIRQ_OFFSET) #define nmi_enter() (irq_enter()) #define nmi_exit() (preempt_count() -= HARDIRQ_OFFSET) - -#ifdef CONFIG_PREEMPT -# include -# define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != kernel_locked()) -# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) -#else -# define in_atomic() (preempt_count() != 0) -# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET -#endif +#define irq_enter() (preempt_count() += HARDIRQ_OFFSET) #define irq_exit() \ do { \ preempt_count() -= IRQ_EXIT_OFFSET; \ @@ -93,10 +58,4 @@ do { \ preempt_enable_no_resched(); \ } while (0) -#ifndef CONFIG_SMP -# define synchronize_irq(irq) barrier() -#else - extern void synchronize_irq(unsigned int irq); -#endif /* CONFIG_SMP */ - #endif /* __ASM_HARDIRQ_H */ --- linux-2.6.9-rc1/include/asm-x86_64/hw_irq.h 2004-08-24 00:03:38.000000000 -0700 +++ 25/include/asm-x86_64/hw_irq.h 2004-09-03 00:56:45.207288096 -0700 @@ -65,14 +65,15 @@ struct hw_interrupt_type; * sources per level' errata. */ #define LOCAL_TIMER_VECTOR 0xef +#define LOCAL_PERFCTR_VECTOR 0xee /* - * First APIC vector available to drivers: (vectors 0x30-0xee) + * First APIC vector available to drivers: (vectors 0x30-0xed) * we start at 0x31 to spread out vectors evenly between priority * levels. (0x80 is the syscall vector) */ #define FIRST_DEVICE_VECTOR 0x31 -#define FIRST_SYSTEM_VECTOR 0xef /* duplicated in irq.h */ +#define FIRST_SYSTEM_VECTOR 0xee /* duplicated in irq.h */ #ifndef __ASSEMBLY__ @@ -129,40 +130,6 @@ __asm__( \ "push $" #nr "-256 ; " \ "jmp common_interrupt"); -static inline void x86_do_profile (struct pt_regs *regs) -{ - unsigned long rip; - extern unsigned long prof_cpu_mask; - extern char _stext[]; - - profile_hook(regs); - - if (user_mode(regs)) - return; - if (!prof_buffer) - return; - - rip = regs->rip; - - /* - * Only measure the CPUs specified by /proc/irq/prof_cpu_mask. - * (default is all CPUs.) - */ - if (!((1<>= prof_shift; - /* - * Don't ignore out-of-bounds EIP values silently, - * put them into the last histogram slot, so if - * present, they will show up as a sharp peak. - */ - if (rip > prof_len-1) - rip = prof_len-1; - atomic_inc((atomic_t *)&prof_buffer[rip]); -} - #if defined(CONFIG_X86_IO_APIC) && defined(CONFIG_SMP) static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) { if (IO_APIC_IRQ(i)) --- linux-2.6.9-rc1/include/asm-x86_64/i387.h 2004-08-24 00:02:47.000000000 -0700 +++ 25/include/asm-x86_64/i387.h 2004-09-02 20:51:52.000000000 -0700 @@ -39,16 +39,25 @@ static inline int need_signal_i387(struc * FPU lazy state save handling... */ -#define kernel_fpu_end() stts() - #define unlazy_fpu(tsk) do { \ if ((tsk)->thread_info->status & TS_USEDFPU) \ save_init_fpu(tsk); \ } while (0) +/* Ignore delayed exceptions from user space */ +static inline void tolerant_fwait(void) +{ + asm volatile("1: fwait\n" + "2:\n" + " .section __ex_table,\"a\"\n" + " .align 8\n" + " .quad 1b,2b\n" + " .previous\n"); +} + #define clear_fpu(tsk) do { \ if ((tsk)->thread_info->status & TS_USEDFPU) { \ - asm volatile("fnclex ; fwait"); \ + tolerant_fwait(); \ (tsk)->thread_info->status &= ~TS_USEDFPU; \ stts(); \ } \ @@ -116,6 +125,7 @@ static inline int save_i387_checking(str static inline void kernel_fpu_begin(void) { struct thread_info *me = current_thread_info(); + preempt_disable(); if (me->status & TS_USEDFPU) { asm volatile("rex64 ; fxsave %0 ; fnclex" : "=m" (me->task->thread.i387.fxsave)); @@ -125,9 +135,15 @@ static inline void kernel_fpu_begin(void clts(); } +static inline void kernel_fpu_end(void) +{ + stts(); + preempt_enable(); +} + static inline void save_init_fpu( struct task_struct *tsk ) { - asm volatile( "fxsave %0 ; fnclex" + asm volatile( "rex64 ; fxsave %0 ; fnclex" : "=m" (tsk->thread.i387.fxsave)); tsk->thread_info->status &= ~TS_USEDFPU; stts(); --- linux-2.6.9-rc1/include/asm-x86_64/ia32.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/include/asm-x86_64/ia32.h 2004-09-02 20:51:52.000000000 -0700 @@ -78,12 +78,6 @@ struct stat64 { unsigned long long st_ino; } __attribute__((packed)); - -typedef union sigval32 { - int sival_int; - unsigned int sival_ptr; -} sigval_t32; - typedef struct siginfo32 { int si_signo; int si_errno; @@ -102,7 +96,7 @@ typedef struct siginfo32 { struct { int _tid; /* timer id */ int _overrun; /* overrun count */ - sigval_t32 _sigval; /* same as below */ + compat_sigval_t _sigval; /* same as below */ int _sys_private; /* not to be passed to user */ int _overrun_incr; /* amount to add to overrun */ } _timer; @@ -111,7 +105,7 @@ typedef struct siginfo32 { struct { unsigned int _pid; /* sender's pid */ unsigned int _uid; /* sender's uid */ - sigval_t32 _sigval; + compat_sigval_t _sigval; } _rt; /* SIGCHLD */ @@ -121,6 +115,7 @@ typedef struct siginfo32 { int _status; /* exit code */ compat_clock_t _utime; compat_clock_t _stime; + struct compat_rusage _rusage; } _sigchld; /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ --- linux-2.6.9-rc1/include/asm-x86_64/ia32_unistd.h 2004-08-24 00:02:59.000000000 -0700 +++ 25/include/asm-x86_64/ia32_unistd.h 2004-09-03 00:56:45.208287944 -0700 @@ -289,7 +289,14 @@ #define __NR_ia32_mq_notify (__NR_ia32_mq_open+4) #define __NR_ia32_mq_getsetattr (__NR_ia32_mq_open+5) #define __NR_ia32_kexec 283 +#define __NR_ia32_waitid 284 +#define __NR_ia32_perfctr_info 285 +#define __NR_ia32_vperfctr_open (__NR_ia32_perfctr_info+1) +#define __NR_ia32_vperfctr_control (__NR_ia32_perfctr_info+2) +#define __NR_ia32_vperfctr_unlink (__NR_ia32_perfctr_info+3) +#define __NR_ia32_vperfctr_iresume (__NR_ia32_perfctr_info+4) +#define __NR_ia32_vperfctr_read (__NR_ia32_perfctr_info+5) -#define IA32_NR_syscalls 287 /* must be > than biggest syscall! */ +#define IA32_NR_syscalls 290 /* must be > than biggest syscall! */ #endif /* _ASM_X86_64_IA32_UNISTD_H_ */ --- linux-2.6.9-rc1/include/asm-x86_64/io.h 2004-08-24 00:02:32.000000000 -0700 +++ 25/include/asm-x86_64/io.h 2004-09-02 20:51:52.000000000 -0700 @@ -186,10 +186,30 @@ extern void iounmap(void *addr); #define __raw_readl readl #define __raw_readq readq -#define writeb(b,addr) (*(volatile unsigned char *) (addr) = (b)) -#define writew(b,addr) (*(volatile unsigned short *) (addr) = (b)) +#ifdef CONFIG_UNORDERED_IO +static inline void __writel(u32 val, void *addr) +{ + volatile u32 *target = addr; + asm volatile("movnti %1,%0" + : "=m" (*target) + : "r" (val) : "memory"); +} + +static inline void __writeq(u64 val, void *addr) +{ + volatile u64 *target = addr; + asm volatile("movnti %1,%0" + : "=m" (*target) + : "r" (val) : "memory"); +} +#define writeq(val,addr) __writeq((val),(void *)(addr)) +#define writel(val,addr) __writel((val),(void *)(addr)) +#else #define writel(b,addr) (*(volatile unsigned int *) (addr) = (b)) #define writeq(b,addr) (*(volatile unsigned long *) (addr) = (b)) +#endif +#define writeb(b,addr) (*(volatile unsigned char *) (addr) = (b)) +#define writew(b,addr) (*(volatile unsigned short *) (addr) = (b)) #define __raw_writeb writeb #define __raw_writew writew #define __raw_writel writel @@ -299,11 +319,8 @@ out: #define flush_write_buffers() -/* Disable vmerge for now. Need to fix the block layer code - to check for non iommu addresses first. - When the IOMMU is force it is safe to enable. */ -extern int iommu_merge; -#define BIO_VMERGE_BOUNDARY (iommu_merge ? 4096 : 0) +extern int iommu_bio_merge; +#define BIO_VMERGE_BOUNDARY iommu_bio_merge #endif /* __KERNEL__ */ --- linux-2.6.9-rc1/include/asm-x86_64/irq.h 2004-08-24 00:01:50.000000000 -0700 +++ 25/include/asm-x86_64/irq.h 2004-09-03 00:56:45.208287944 -0700 @@ -29,7 +29,7 @@ */ #define NR_VECTORS 256 -#define FIRST_SYSTEM_VECTOR 0xef /* duplicated in hw_irq.h */ +#define FIRST_SYSTEM_VECTOR 0xee /* duplicated in hw_irq.h */ #ifdef CONFIG_PCI_MSI #define NR_IRQS FIRST_SYSTEM_VECTOR --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-x86_64/kexec.h 2004-09-03 00:56:58.897206912 -0700 @@ -0,0 +1,25 @@ +#ifndef _X86_64_KEXEC_H +#define _X86_64_KEXEC_H + +#include +#include + +/* + * KEXEC_SOURCE_MEMORY_LIMIT maximum page get_free_page can return. + * I.e. Maximum page that is mapped directly into kernel memory, + * and kmap is not required. + * + * So far x86_64 is limited to 40 physical address bits. + */ + +/* Maximum physical address we can use pages from */ +#define KEXEC_SOURCE_MEMORY_LIMIT (0xFFFFFFFFFFUL) +/* Maximum address we can reach in physical address mode */ +#define KEXEC_DESTINATION_MEMORY_LIMIT (0xFFFFFFFFFFUL) +/* Maximum address we can use for the control pages */ +#define KEXEC_CONTROL_MEMORY_LIMIT (0xFFFFFFFFFFUL) + +/* Allocate one page for the pdp and the second for the code */ +#define KEXEC_CONTROL_CODE_SIZE (4096UL + 4096UL) + +#endif /* _X86_64_KEXEC_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-x86_64/kgdb.h 2004-09-03 00:56:36.979538904 -0700 @@ -0,0 +1,71 @@ +#ifndef __KGDB +#define __KGDB + +/* + * This file should not include ANY others. This makes it usable + * most anywhere without the fear of include order or inclusion. + * Make it so! + * + * This file may be included all the time. It is only active if + * CONFIG_KGDB is defined, otherwise it stubs out all the macros + * and entry points. + */ +#if defined(CONFIG_KGDB) && !defined(__ASSEMBLY__) + +extern void breakpoint(void); +#define INIT_KGDB_INTS kgdb_enable_ints() + +#ifndef BREAKPOINT +#define BREAKPOINT asm(" int $3") +#endif + +extern void kgdb_schedule_breakpoint(void); +extern void kgdb_process_breakpoint(void); + +extern int kgdb_tty_hook(void); +extern int kgdb_eth_hook(void); +extern int kgdboe; + +/* + * GDB debug stub (or any debug stub) can point the 'linux_debug_hook' + * pointer to its routine and it will be entered as the first thing + * when a trap occurs. + * + * Return values are, at present, undefined. + * + * The debug hook routine does not necessarily return to its caller. + * It has the register image and thus may choose to resume execution + * anywhere it pleases. + */ +struct pt_regs; + +extern int kgdb_handle_exception(int trapno, + int signo, int err_code, struct pt_regs *regs); +extern int in_kgdb(struct pt_regs *regs); + +extern void set_debug_traps(void); + +#ifdef CONFIG_KGDB_TS +void kgdb_tstamp(int line, char *source, int data0, int data1); +/* + * This is the time stamp function. The macro adds the source info and + * does a cast on the data to allow most any 32-bit value. + */ + +#define kgdb_ts(data0,data1) kgdb_tstamp(__LINE__,__FILE__,(int)data0,(int)data1) +#else +#define kgdb_ts(data0,data1) +#endif +#else /* CONFIG_KGDB && ! __ASSEMBLY__ ,stubs follow... */ +#ifndef BREAKPOINT +#define BREAKPOINT +#endif +#define kgdb_ts(data0,data1) +#define in_kgdb (0) +#define kgdb_handle_exception +#define breakpoint +#define INIT_KGDB_INTS +#define kgdb_process_breakpoint() do {} while(0) + +#endif +#endif /* __KGDB */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-x86_64/kgdb_local.h 2004-09-03 00:56:36.979538904 -0700 @@ -0,0 +1,102 @@ +#ifndef __KGDB_LOCAL +#define ___KGDB_LOCAL +#include +#include +#include +#include +#include +#include +#include +#include + +#define PORT 0x3f8 +#ifdef CONFIG_KGDB_PORT +#undef PORT +#define PORT CONFIG_KGDB_PORT +#endif +#define IRQ 4 +#ifdef CONFIG_KGDB_IRQ +#undef IRQ +#define IRQ CONFIG_KGDB_IRQ +#endif +#define SB_CLOCK 1843200 +#define SB_BASE (SB_CLOCK/16) +#define SB_BAUD9600 SB_BASE/9600 +#define SB_BAUD192 SB_BASE/19200 +#define SB_BAUD384 SB_BASE/38400 +#define SB_BAUD576 SB_BASE/57600 +#define SB_BAUD1152 SB_BASE/115200 +#ifdef CONFIG_KGDB_9600BAUD +#define SB_BAUD SB_BAUD9600 +#endif +#ifdef CONFIG_KGDB_19200BAUD +#define SB_BAUD SB_BAUD192 +#endif +#ifdef CONFIG_KGDB_38400BAUD +#define SB_BAUD SB_BAUD384 +#endif +#ifdef CONFIG_KGDB_57600BAUD +#define SB_BAUD SB_BAUD576 +#endif +#ifdef CONFIG_KGDB_115200BAUD +#define SB_BAUD SB_BAUD1152 +#endif +#ifndef SB_BAUD +#define SB_BAUD SB_BAUD1152 /* Start with this if not given */ +#endif + +#ifndef CONFIG_X86_TSC +#undef rdtsc +#define rdtsc(a,b) if (a++ > 10000){a = 0; b++;} +#undef rdtscll +#define rdtscll(s) s++ +#endif + +#ifdef _raw_read_unlock /* must use a name that is "define"ed, not an inline */ +#undef spin_lock +#undef spin_trylock +#undef spin_unlock +#define spin_lock _raw_spin_lock +#define spin_trylock _raw_spin_trylock +#define spin_unlock _raw_spin_unlock +#else +#endif +#undef spin_unlock_wait +#define spin_unlock_wait(x) do { cpu_relax(); barrier();} \ + while(spin_is_locked(x)) + +#define SB_IER 1 +#define SB_MCR UART_MCR_OUT2 | UART_MCR_DTR | UART_MCR_RTS + +#define FLAGS 0 +#define SB_STATE { \ + magic: SSTATE_MAGIC, \ + baud_base: SB_BASE, \ + port: PORT, \ + irq: IRQ, \ + flags: FLAGS, \ + custom_divisor:SB_BAUD} +#define SB_INFO { \ + magic: SERIAL_MAGIC, \ + port: PORT,0,FLAGS, \ + state: &state, \ + tty: (struct tty_struct *)&state, \ + IER: SB_IER, \ + MCR: SB_MCR} +extern void putDebugChar(int); +/* RTAI support needs us to really stop/start interrupts */ + +#define kgdb_sti() __asm__ __volatile__("sti": : :"memory") +#define kgdb_cli() __asm__ __volatile__("cli": : :"memory") +#define kgdb_local_save_flags(x) __asm__ __volatile__(\ + "pushfl ; popl %0":"=g" (x): /* no input */) +#define kgdb_local_irq_restore(x) __asm__ __volatile__(\ + "pushl %0 ; popfl": \ + /* no output */ :"g" (x):"memory", "cc") +#define kgdb_local_irq_save(x) kgdb_local_save_flags(x); kgdb_cli() + +#ifdef CONFIG_SERIAL +extern void shutdown_for_kgdb(struct async_struct *info); +#endif +#define INIT_KDEBUG putDebugChar("+"); +#endif /* __KGDB_LOCAL */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-x86_64/lockmeter.h 2004-09-03 00:56:43.208591944 -0700 @@ -0,0 +1,102 @@ +/* + * Copyright (C) 1999,2000 Silicon Graphics, Inc. + * + * Written by John Hawkes (hawkes@sgi.com) + * Based on klstat.h by Jack Steiner (steiner@sgi.com) + * + * Modified by Ray Bryant (raybry@us.ibm.com) + * Changes Copyright (C) 2000 IBM, Inc. + * Added save of index in spinlock_t to improve efficiency + * of "hold" time reporting for spinlocks. + * Added support for hold time statistics for read and write + * locks. + * Moved machine dependent code here from include/lockmeter.h. + * + */ + +#ifndef _X8664_LOCKMETER_H +#define _X8664_LOCKMETER_H + +#include +#include + +#include + +#ifdef __KERNEL__ +extern unsigned int cpu_khz; +#define CPU_CYCLE_FREQUENCY (cpu_khz * 1000) +#else +#define CPU_CYCLE_FREQUENCY 450000000 +#endif + +#define THIS_CPU_NUMBER smp_processor_id() + +/* + * macros to cache and retrieve an index value inside of a spin lock + * these macros assume that there are less than 65536 simultaneous + * (read mode) holders of a rwlock. Not normally a problem!! + * we also assume that the hash table has less than 65535 entries. + */ +/* + * instrumented spinlock structure -- never used to allocate storage + * only used in macros below to overlay a spinlock_t + */ +typedef struct inst_spinlock_s { + /* remember, Intel is little endian */ + unsigned short lock; + unsigned short index; +} inst_spinlock_t; +#define PUT_INDEX(lock_ptr,indexv) ((inst_spinlock_t *)(lock_ptr))->index = indexv +#define GET_INDEX(lock_ptr) ((inst_spinlock_t *)(lock_ptr))->index + +/* + * macros to cache and retrieve an index value in a read/write lock + * as well as the cpu where a reader busy period started + * we use the 2nd word (the debug word) for this, so require the + * debug word to be present + */ +/* + * instrumented rwlock structure -- never used to allocate storage + * only used in macros below to overlay a rwlock_t + */ +typedef struct inst_rwlock_s { + volatile int lock; + unsigned short index; + unsigned short cpu; +} inst_rwlock_t; +#define PUT_RWINDEX(rwlock_ptr,indexv) ((inst_rwlock_t *)(rwlock_ptr))->index = indexv +#define GET_RWINDEX(rwlock_ptr) ((inst_rwlock_t *)(rwlock_ptr))->index +#define PUT_RW_CPU(rwlock_ptr,cpuv) ((inst_rwlock_t *)(rwlock_ptr))->cpu = cpuv +#define GET_RW_CPU(rwlock_ptr) ((inst_rwlock_t *)(rwlock_ptr))->cpu + +/* + * return the number of readers for a rwlock_t + */ +#define RWLOCK_READERS(rwlock_ptr) rwlock_readers(rwlock_ptr) + +extern inline int rwlock_readers(rwlock_t *rwlock_ptr) +{ + int tmp = (int) rwlock_ptr->lock; + /* read and write lock attempts may cause the lock value to temporarily */ + /* be negative. Until it is >= 0 we know nothing (i. e. can't tell if */ + /* is -1 because it was write locked and somebody tried to read lock it */ + /* or if it is -1 because it was read locked and somebody tried to write*/ + /* lock it. ........................................................... */ + do { + tmp = (int) rwlock_ptr->lock; + } while (tmp < 0); + if (tmp == 0) return(0); + else return(RW_LOCK_BIAS-tmp); +} + +/* + * return true if rwlock is write locked + * (note that other lock attempts can cause the lock value to be negative) + */ +#define RWLOCK_IS_WRITE_LOCKED(rwlock_ptr) ((rwlock_ptr)->lock <= 0) +#define IABS(x) ((x) > 0 ? (x) : -(x)) +#define RWLOCK_IS_READ_LOCKED(rwlock_ptr) ((IABS((rwlock_ptr)->lock) % RW_LOCK_BIAS) != 0) + +#define get_cycles64() get_cycles() + +#endif /* _X8664_LOCKMETER_H */ --- linux-2.6.9-rc1/include/asm-x86_64/mpspec.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/include/asm-x86_64/mpspec.h 2004-09-03 00:56:40.518000976 -0700 @@ -156,8 +156,8 @@ struct mpc_config_lintsrc * 7 2 CPU MCA+PCI */ +#define MAX_MP_BUSSES 270 #define MAX_IRQ_SOURCES 256 -#define MAX_MP_BUSSES 32 enum mp_bustype { MP_BUS_ISA = 1, MP_BUS_EISA, @@ -166,7 +166,6 @@ enum mp_bustype { }; extern unsigned char mp_bus_id_to_type [MAX_MP_BUSSES]; extern int mp_bus_id_to_pci_bus [MAX_MP_BUSSES]; -extern cpumask_t pci_bus_to_cpumask [256]; extern unsigned int boot_cpu_physical_apicid; extern int smp_found_config; --- linux-2.6.9-rc1/include/asm-x86_64/mtrr.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/include/asm-x86_64/mtrr.h 2004-09-02 20:51:52.000000000 -0700 @@ -71,8 +71,6 @@ struct mtrr_gentry #ifdef __KERNEL__ -extern char *mtrr_strings[MTRR_NUM_TYPES]; - /* The following functions are for use by other drivers */ # ifdef CONFIG_MTRR extern int mtrr_add (unsigned long base, unsigned long size, --- linux-2.6.9-rc1/include/asm-x86_64/numa.h 2004-08-24 00:01:51.000000000 -0700 +++ 25/include/asm-x86_64/numa.h 2004-09-03 00:56:51.389348280 -0700 @@ -1,6 +1,8 @@ #ifndef _ASM_X8664_NUMA_H #define _ASM_X8664_NUMA_H 1 +#include + #define MAXNODE 8 #define NODEMASK 0xff --- linux-2.6.9-rc1/include/asm-x86_64/page.h 2004-08-24 00:01:52.000000000 -0700 +++ 25/include/asm-x86_64/page.h 2004-09-03 00:56:41.917788176 -0700 @@ -148,6 +148,7 @@ extern __inline__ int get_order(unsigned struct task_struct; struct vm_area_struct *get_gate_vma(struct task_struct *tsk); int in_gate_area(struct task_struct *task, unsigned long addr); +extern int devmem_is_allowed(unsigned long pagenr); #endif #endif /* __KERNEL__ */ --- linux-2.6.9-rc1/include/asm-x86_64/pci.h 2004-08-24 00:02:32.000000000 -0700 +++ 25/include/asm-x86_64/pci.h 2004-09-02 20:51:52.000000000 -0700 @@ -44,81 +44,25 @@ int pcibios_set_irq_routing(struct pci_d #include #include -struct pci_dev; - extern int iommu_setup(char *opt); -extern dma_addr_t bad_dma_address; -#define pci_dma_mapping_error(x) ((x) == bad_dma_address) - -/* Allocate and map kernel buffer using consistent mode DMA for a device. - * hwdev should be valid struct pci_dev pointer for PCI devices, - * NULL for PCI-like buses (ISA, EISA). - * Returns non-NULL cpu-view pointer to the buffer if successful and - * sets *dma_addrp to the pci side dma address as well, else *dma_addrp - * is undefined. - */ -extern void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, - dma_addr_t *dma_handle); - -/* Free and unmap a consistent DMA buffer. - * cpu_addr is what was returned from pci_alloc_consistent, - * size must be the same as what as passed into pci_alloc_consistent, - * and likewise dma_addr must be the same as what *dma_addrp was set to. - * - * References to the memory and mappings associated with cpu_addr/dma_addr - * past this call are illegal. - */ -extern void pci_free_consistent(struct pci_dev *hwdev, size_t size, - void *vaddr, dma_addr_t dma_handle); - -#ifdef CONFIG_SWIOTLB -extern int swiotlb; -extern dma_addr_t swiotlb_map_single (struct device *hwdev, void *ptr, size_t size, - int dir); -extern void swiotlb_unmap_single (struct device *hwdev, dma_addr_t dev_addr, - size_t size, int dir); -extern void swiotlb_sync_single_for_cpu (struct device *hwdev, - dma_addr_t dev_addr, - size_t size, int dir); -extern void swiotlb_sync_single_for_device (struct device *hwdev, - dma_addr_t dev_addr, - size_t size, int dir); -extern void swiotlb_sync_sg_for_cpu (struct device *hwdev, - struct scatterlist *sg, int nelems, - int dir); -extern void swiotlb_sync_sg_for_device (struct device *hwdev, - struct scatterlist *sg, int nelems, - int dir); -extern int swiotlb_map_sg(struct device *hwdev, struct scatterlist *sg, - int nents, int direction); -extern void swiotlb_unmap_sg(struct device *hwdev, struct scatterlist *sg, - int nents, int direction); - -#endif - #ifdef CONFIG_GART_IOMMU - -/* Map a single buffer of the indicated size for DMA in streaming mode. - * The 32-bit bus address to use is returned. +/* The PCI address space does equal the physical memory + * address space. The networking and block device layers use + * this boolean for bounce buffer decisions * - * Once the device is given the dma address, the device owns this memory - * until either pci_unmap_single or pci_dma_sync_single_for_cpu is performed. + * On AMD64 it mostly equals, but we set it to zero to tell some subsystems + * that an IOMMU is available. */ -extern dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, size_t size, - int direction); - - -void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t addr, - size_t size, int direction); +#define PCI_DMA_BUS_IS_PHYS (no_iommu ? 1 : 0) /* - * pci_{map,unmap}_single_page maps a kernel page to a dma_addr_t. identical - * to pci_map_single, but takes a struct page instead of a virtual address + * x86-64 always supports DAC, but sometimes it is useful to force + * devices through the IOMMU to get automatic sg list merging. + * Optional right now. */ - -#define pci_map_page(dev,page,offset,size,dir) \ - pci_map_single((dev), page_address(page)+(offset), (size), (dir)) +extern int iommu_sac_force; +#define pci_dac_dma_supported(pci_dev, mask) (!iommu_sac_force) #define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME) \ dma_addr_t ADDR_NAME; @@ -133,113 +77,12 @@ void pci_unmap_single(struct pci_dev *hw #define pci_unmap_len_set(PTR, LEN_NAME, VAL) \ (((PTR)->LEN_NAME) = (VAL)) -static inline void pci_dma_sync_single_for_cpu(struct pci_dev *hwdev, - dma_addr_t dma_handle, - size_t size, int direction) -{ - BUG_ON(direction == PCI_DMA_NONE); - -#ifdef CONFIG_SWIOTLB - if (swiotlb) - return swiotlb_sync_single_for_cpu(&hwdev->dev,dma_handle,size,direction); -#endif - - flush_write_buffers(); -} - -static inline void pci_dma_sync_single_for_device(struct pci_dev *hwdev, - dma_addr_t dma_handle, - size_t size, int direction) -{ - BUG_ON(direction == PCI_DMA_NONE); - -#ifdef CONFIG_SWIOTLB - if (swiotlb) - return swiotlb_sync_single_for_device(&hwdev->dev,dma_handle,size,direction); -#endif - - flush_write_buffers(); -} - -static inline void pci_dma_sync_sg_for_cpu(struct pci_dev *hwdev, - struct scatterlist *sg, - int nelems, int direction) -{ - BUG_ON(direction == PCI_DMA_NONE); - -#ifdef CONFIG_SWIOTLB - if (swiotlb) - return swiotlb_sync_sg_for_cpu(&hwdev->dev,sg,nelems,direction); -#endif - flush_write_buffers(); -} - -static inline void pci_dma_sync_sg_for_device(struct pci_dev *hwdev, - struct scatterlist *sg, - int nelems, int direction) -{ - BUG_ON(direction == PCI_DMA_NONE); - -#ifdef CONFIG_SWIOTLB - if (swiotlb) - return swiotlb_sync_sg_for_device(&hwdev->dev,sg,nelems,direction); -#endif - flush_write_buffers(); -} - -/* The PCI address space does equal the physical memory - * address space. The networking and block device layers use - * this boolean for bounce buffer decisions - * - * On AMD64 it mostly equals, but we set it to zero to tell some subsystems - * that an IOMMU is available. - */ -#define PCI_DMA_BUS_IS_PHYS (no_iommu ? 1 : 0) - -/* We lie slightly when the IOMMU is forced to get the device to - use SAC instead of DAC. */ -#define pci_dac_dma_supported(pci_dev, mask) (force_iommu ? 0 : 1) - #else -static inline dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, - size_t size, int direction) -{ - dma_addr_t addr; +/* No IOMMU */ - if (direction == PCI_DMA_NONE) - out_of_line_bug(); - addr = virt_to_bus(ptr); - - /* - * This is gross, but what should I do. - * Unfortunately drivers do not test the return value of this. - */ - if ((addr+size) & ~hwdev->dma_mask) - out_of_line_bug(); - return addr; -} - -static inline void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, - size_t size, int direction) -{ - if (direction == PCI_DMA_NONE) - out_of_line_bug(); - /* Nothing to do */ -} - -static inline dma_addr_t pci_map_page(struct pci_dev *hwdev, struct page *page, - unsigned long offset, size_t size, int direction) -{ - dma_addr_t addr; - if (direction == PCI_DMA_NONE) - out_of_line_bug(); - addr = page_to_pfn(page) * PAGE_SIZE + offset; - if ((addr+size) & ~hwdev->dma_mask) - out_of_line_bug(); - return addr; -} +#define PCI_DMA_BUS_IS_PHYS 1 +#define pci_dac_dma_supported(pci_dev, mask) 1 -/* pci_unmap_{page,single} is a nop so... */ #define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME) #define DECLARE_PCI_UNMAP_LEN(LEN_NAME) #define pci_unmap_addr(PTR, ADDR_NAME) (0) @@ -247,74 +90,9 @@ static inline dma_addr_t pci_map_page(st #define pci_unmap_len(PTR, LEN_NAME) (0) #define pci_unmap_len_set(PTR, LEN_NAME, VAL) do { } while (0) -/* Make physical memory consistent for a single - * streaming mode DMA translation after a transfer. - * - * If you perform a pci_map_single() but wish to interrogate the - * buffer using the cpu, yet do not wish to teardown the PCI dma - * mapping, you must call this function before doing so. At the - * next point you give the PCI dma address back to the card, you - * must first perform a pci_dma_sync_for_device, and then the - * device again owns the buffer. - */ -static inline void pci_dma_sync_single_for_cpu(struct pci_dev *hwdev, - dma_addr_t dma_handle, - size_t size, int direction) -{ - if (direction == PCI_DMA_NONE) - out_of_line_bug(); -} - -static inline void pci_dma_sync_single_for_device(struct pci_dev *hwdev, - dma_addr_t dma_handle, - size_t size, int direction) -{ - if (direction == PCI_DMA_NONE) - out_of_line_bug(); - flush_write_buffers(); -} - -/* Make physical memory consistent for a set of streaming - * mode DMA translations after a transfer. - * - * The same as pci_dma_sync_single_* but for a scatter-gather list, - * same rules and usage. - */ -static inline void pci_dma_sync_sg_for_cpu(struct pci_dev *hwdev, - struct scatterlist *sg, - int nelems, int direction) -{ - if (direction == PCI_DMA_NONE) - out_of_line_bug(); -} - -static inline void pci_dma_sync_sg_for_device(struct pci_dev *hwdev, - struct scatterlist *sg, - int nelems, int direction) -{ - if (direction == PCI_DMA_NONE) - out_of_line_bug(); - flush_write_buffers(); -} - -#define PCI_DMA_BUS_IS_PHYS 1 - -#define pci_dac_dma_supported(pci_dev, mask) 1 #endif -extern int pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, - int nents, int direction); -extern void pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, - int nents, int direction); - -#define pci_unmap_page pci_unmap_single - -/* Return whether the given PCI device DMA address mask can - * be supported properly. For example, if your device can - * only drive the low 24-bits during PCI bus mastering, then - * you would pass 0x00ffffff as the mask to this function. - */ -extern int pci_dma_supported(struct pci_dev *hwdev, u64 mask); +#include static inline dma64_addr_t pci_dac_page_to_dma(struct pci_dev *pdev, struct page *page, unsigned long offset, int direction) @@ -359,7 +137,6 @@ static inline void pcibios_add_platform_ /* generic pci stuff */ #ifdef CONFIG_PCI #include -#include #endif #endif /* __x8664_PCI_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-x86_64/perfctr.h 2004-09-03 00:56:45.208287944 -0700 @@ -0,0 +1 @@ +#include --- linux-2.6.9-rc1/include/asm-x86_64/processor.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/include/asm-x86_64/processor.h 2004-09-03 00:56:45.209287792 -0700 @@ -60,6 +60,8 @@ struct cpuinfo_x86 { int x86_cache_alignment; int x86_tlbsize; /* number of 4K pages in DTLB/ITLB combined(in pages)*/ __u8 x86_virt_bits, x86_phys_bits; + __u8 x86_num_cores; + __u8 x86_apicid; __u32 x86_power; unsigned long loops_per_jiffy; } ____cacheline_aligned; @@ -82,7 +84,7 @@ extern struct tss_struct init_tss[NR_CPU extern struct cpuinfo_x86 cpu_data[]; #define current_cpu_data cpu_data[smp_processor_id()] #else -#define cpu_data &boot_cpu_data +#define cpu_data (&boot_cpu_data) #define current_cpu_data boot_cpu_data #endif @@ -253,6 +255,8 @@ struct thread_struct { unsigned long *io_bitmap_ptr; /* cached TLS descriptors. */ u64 tls_array[GDT_ENTRY_TLS_ENTRIES]; +/* performance counters */ + struct vperfctr *perfctr; } __attribute__((aligned(16))); #define INIT_THREAD {} @@ -456,9 +460,4 @@ static inline void __mwait(unsigned long #define cache_line_size() (boot_cpu_data.x86_cache_alignment) -#ifdef CONFIG_SCHED_SMT -#define ARCH_HAS_SCHED_DOMAIN -#define ARCH_HAS_SCHED_WAKE_IDLE -#endif - #endif /* __ASM_X86_64_PROCESSOR_H */ --- linux-2.6.9-rc1/include/asm-x86_64/proto.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/include/asm-x86_64/proto.h 2004-09-02 20:51:52.000000000 -0700 @@ -82,7 +82,6 @@ extern int unhandled_signal(struct task_ extern void select_idle_routine(const struct cpuinfo_x86 *c); extern void swiotlb_init(void); -extern int swiotlb; extern unsigned long max_mapnr; extern unsigned long end_pfn; @@ -103,6 +102,8 @@ extern int fallback_aper_force; extern int iommu_aperture; extern int iommu_aperture_disabled; extern int iommu_aperture_allowed; +extern int fix_aperture; +extern int force_iommu; extern void smp_local_timer_interrupt(struct pt_regs * regs); --- linux-2.6.9-rc1/include/asm-x86_64/ptrace.h 2004-08-24 00:02:25.000000000 -0700 +++ 25/include/asm-x86_64/ptrace.h 2004-09-02 20:51:52.000000000 -0700 @@ -83,6 +83,7 @@ struct pt_regs { #if defined(__KERNEL__) && !defined(__ASSEMBLY__) #define user_mode(regs) (!!((regs)->cs & 3)) #define instruction_pointer(regs) ((regs)->rip) +#define profile_pc(regs) instruction_pointer(regs) void signal_fault(struct pt_regs *regs, void __user *frame, char *where); enum { --- linux-2.6.9-rc1/include/asm-x86_64/semaphore.h 2004-08-24 00:01:51.000000000 -0700 +++ 25/include/asm-x86_64/semaphore.h 2004-09-02 20:51:52.000000000 -0700 @@ -47,21 +47,14 @@ struct semaphore { atomic_t count; int sleepers; wait_queue_head_t wait; -#ifdef WAITQUEUE_DEBUG - long __magic; -#endif }; -#ifdef WAITQUEUE_DEBUG -# define __SEM_DEBUG_INIT(name) \ - , (int)&(name).__magic -#else -# define __SEM_DEBUG_INIT(name) -#endif - -#define __SEMAPHORE_INITIALIZER(name,count) \ -{ ATOMIC_INIT(count), 0, __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ - __SEM_DEBUG_INIT(name) } +#define __SEMAPHORE_INITIALIZER(name, n) \ +{ \ + .count = ATOMIC_INIT(n), \ + .sleepers = 0, \ + .wait = __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \ +} #define __MUTEX_INITIALIZER(name) \ __SEMAPHORE_INITIALIZER(name,1) @@ -83,9 +76,6 @@ static inline void sema_init (struct sem atomic_set(&sem->count, val); sem->sleepers = 0; init_waitqueue_head(&sem->wait); -#ifdef WAITQUEUE_DEBUG - sem->__magic = (int)&sem->__magic; -#endif } static inline void init_MUTEX (struct semaphore *sem) @@ -115,9 +105,6 @@ asmlinkage void __up(struct semaphore * */ static inline void down(struct semaphore * sem) { -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); __asm__ __volatile__( @@ -142,9 +129,6 @@ static inline int down_interruptible(str { int result; -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif might_sleep(); __asm__ __volatile__( @@ -171,10 +155,6 @@ static inline int down_trylock(struct se { int result; -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif - __asm__ __volatile__( "# atomic interruptible down operation\n\t" LOCK "decl %1\n\t" /* --sem->count */ @@ -199,9 +179,6 @@ static inline int down_trylock(struct se */ static inline void up(struct semaphore * sem) { -#ifdef WAITQUEUE_DEBUG - CHECK_MAGIC(sem->__magic); -#endif __asm__ __volatile__( "# atomic up operation\n\t" LOCK "incl %0\n\t" /* ++sem->count */ --- linux-2.6.9-rc1/include/asm-x86_64/spinlock.h 2004-08-24 00:03:38.000000000 -0700 +++ 25/include/asm-x86_64/spinlock.h 2004-09-03 00:56:51.885272888 -0700 @@ -41,7 +41,6 @@ typedef struct { #define spin_is_locked(x) (*(volatile signed char *)(&(x)->lock) <= 0) #define spin_unlock_wait(x) do { barrier(); } while(spin_is_locked(x)) -#define _raw_spin_lock_flags(lock, flags) _raw_spin_lock(lock) #define spin_lock_string \ "\n1:\t" \ @@ -55,6 +54,23 @@ typedef struct { "jmp 1b\n" \ LOCK_SECTION_END +#define spin_lock_string_flags \ + "\n1:\t" \ + "lock ; decb %0\n\t" \ + "js 2f\n\t" \ + LOCK_SECTION_START("") \ + "2:\t" \ + "test $0x200, %1\n\t" \ + "jz 3f\n\t" \ + "sti\n\t" \ + "3:\t" \ + "rep;nop\n\t" \ + "cmpb $0, %0\n\t" \ + "jle 3b\n\t" \ + "cli\n\t" \ + "jmp 1b\n" \ + LOCK_SECTION_END + /* * This works. Despite all the confusion. * (except on PPro SMP or if we are using OOSTORE) @@ -125,6 +141,20 @@ printk("eip: %p\n", &&here); :"=m" (lock->lock) : : "memory"); } +static inline void _raw_spin_lock_flags (spinlock_t *lock, unsigned long flags) +{ +#ifdef CONFIG_DEBUG_SPINLOCK + __label__ here; +here: + if (unlikely(lock->magic != SPINLOCK_MAGIC)) { + printk("eip: %p\n", &&here); + BUG(); + } +#endif + __asm__ __volatile__(spin_lock_string_flags + :"=m" (lock->lock) : "r" (flags) : "memory"); +} + /* * Read-write spinlocks, allowing multiple readers @@ -138,6 +168,11 @@ printk("eip: %p\n", &&here); */ typedef struct { volatile unsigned int lock; +#ifdef CONFIG_LOCKMETER + /* required for LOCKMETER since all bits in lock are used */ + /* and we need this storage for CPU and lock INDEX */ + unsigned lockmeter_magic; +#endif #ifdef CONFIG_DEBUG_SPINLOCK unsigned magic; #endif @@ -145,11 +180,19 @@ typedef struct { #define RWLOCK_MAGIC 0xdeaf1eed +#ifdef CONFIG_LOCKMETER +#ifdef CONFIG_DEBUG_SPINLOCK +#define RWLOCK_MAGIC_INIT , 0, RWLOCK_MAGIC +#else +#define RWLOCK_MAGIC_INIT , 0 +#endif +#else /* !CONFIG_LOCKMETER */ #ifdef CONFIG_DEBUG_SPINLOCK #define RWLOCK_MAGIC_INIT , RWLOCK_MAGIC #else #define RWLOCK_MAGIC_INIT /* */ #endif +#endif /* !CONFIG_LOCKMETER */ #define RW_LOCK_UNLOCKED (rwlock_t) { RW_LOCK_BIAS RWLOCK_MAGIC_INIT } @@ -196,4 +239,60 @@ static inline int _raw_write_trylock(rwl return 0; } +#ifdef CONFIG_LOCKMETER +static inline int _raw_read_trylock(rwlock_t *lock) +{ +/* FIXME -- replace with assembler */ + atomic_t *count = (atomic_t *)lock; + atomic_dec(count); + if (count->counter > 0) + return 1; + atomic_inc(count); + return 0; +} +#endif + +#if defined(CONFIG_LOCKMETER) && defined(CONFIG_HAVE_DEC_LOCK) +extern void _metered_spin_lock (spinlock_t *lock); +extern void _metered_spin_unlock(spinlock_t *lock); + +/* + * Matches what is in arch/x86_64/lib/dec_and_lock.c, except this one is + * "static inline" so that the spin_lock(), if actually invoked, is charged + * against the real caller, not against the catch-all atomic_dec_and_lock + */ +static inline int atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock) +{ + int counter; + int newcount; + +repeat: + counter = atomic_read(atomic); + newcount = counter-1; + + if (!newcount) + goto slow_path; + + asm volatile("lock; cmpxchgl %1,%2" + :"=a" (newcount) + :"r" (newcount), "m" (atomic->counter), "0" (counter)); + + /* If the above failed, "eax" will have changed */ + if (newcount != counter) + goto repeat; + return 0; + +slow_path: + preempt_disable(); + _metered_spin_lock(lock); + if (atomic_dec_and_test(atomic)) + return 1; + _metered_spin_unlock(lock); + preempt_enable(); + return 0; +} + +#define ATOMIC_DEC_AND_LOCK +#endif + #endif /* __ASM_SPINLOCK_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/asm-x86_64/swiotlb.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,36 @@ +#ifndef _ASM_SWIOTLB_H +#define _ASM_SWTIOLB_H 1 + +#include + +/* SWIOTLB interface */ + +extern dma_addr_t swiotlb_map_single(struct device *hwdev, void *ptr, size_t size, + int dir); +extern void swiotlb_unmap_single(struct device *hwdev, dma_addr_t dev_addr, + size_t size, int dir); +extern void swiotlb_sync_single_for_cpu(struct device *hwdev, + dma_addr_t dev_addr, + size_t size, int dir); +extern void swiotlb_sync_single_for_device(struct device *hwdev, + dma_addr_t dev_addr, + size_t size, int dir); +extern void swiotlb_sync_sg_for_cpu(struct device *hwdev, + struct scatterlist *sg, int nelems, + int dir); +extern void swiotlb_sync_sg_for_device(struct device *hwdev, + struct scatterlist *sg, int nelems, + int dir); +extern int swiotlb_map_sg(struct device *hwdev, struct scatterlist *sg, + int nents, int direction); +extern void swiotlb_unmap_sg(struct device *hwdev, struct scatterlist *sg, + int nents, int direction); +extern int swiotlb_dma_mapping_error(dma_addr_t dma_addr); + +#ifdef CONFIG_SWIOTLB +extern int swiotlb; +#else +#define swiotlb 0 +#endif + +#endif --- linux-2.6.9-rc1/include/asm-x86_64/system.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/include/asm-x86_64/system.h 2004-09-02 20:51:52.000000000 -0700 @@ -297,11 +297,11 @@ static inline unsigned long __cmpxchg(vo #define mb() asm volatile("mfence":::"memory") #define rmb() asm volatile("lfence":::"memory") -/* could use SFENCE here, but it would be only needed for unordered SSE - store instructions and we always do an explicit sfence with them currently. - the ordering of normal stores is serialized enough. Just make it a compile - barrier. */ +#ifdef CONFIG_UNORDERED_IO +#define wmb() asm volatile("sfence" ::: "memory") +#else #define wmb() asm volatile("" ::: "memory") +#endif #define read_barrier_depends() do {} while(0) #define set_mb(var, value) do { xchg(&var, value); } while (0) #define set_wmb(var, value) do { var = value; wmb(); } while (0) --- linux-2.6.9-rc1/include/asm-x86_64/topology.h 2004-08-24 00:03:18.000000000 -0700 +++ 25/include/asm-x86_64/topology.h 2004-09-02 20:51:52.000000000 -0700 @@ -14,18 +14,23 @@ extern cpumask_t cpu_online_map; extern unsigned char cpu_to_node[]; extern cpumask_t node_to_cpumask[]; +extern cpumask_t pci_bus_to_cpumask[]; #define cpu_to_node(cpu) (cpu_to_node[cpu]) #define parent_node(node) (node) #define node_to_first_cpu(node) (__ffs(node_to_cpumask[node])) #define node_to_cpumask(node) (node_to_cpumask[node]) -static inline cpumask_t pcibus_to_cpumask(int bus) +static inline cpumask_t __pcibus_to_cpumask(int bus) { + cpumask_t busmask = pci_bus_to_cpumask[bus]; + cpumask_t online = cpu_online_map; cpumask_t res; - cpus_and(res, pci_bus_to_cpumask[bus], cpu_online_map); + cpus_and(res, busmask, online); return res; } +/* broken generic file uses #ifndef later on this */ +#define pcibus_to_cpumask(bus) __pcibus_to_cpumask(bus) #define NODE_BALANCE_RATE 30 /* CHECKME */ --- linux-2.6.9-rc1/include/asm-x86_64/uaccess.h 2004-08-24 00:03:50.000000000 -0700 +++ 25/include/asm-x86_64/uaccess.h 2004-09-02 20:51:52.000000000 -0700 @@ -137,6 +137,9 @@ extern void __put_user_bad(void); #define __put_user(x,ptr) \ __put_user_nocheck((__typeof__(*(ptr)))(x),(ptr),sizeof(*(ptr))) +#define __get_user_unaligned __get_user +#define __put_user_unaligned __put_user + #define __put_user_nocheck(x,ptr,size) \ ({ \ int __pu_err; \ @@ -351,4 +354,7 @@ long strlen_user(const char __user *str) unsigned long clear_user(void __user *mem, unsigned long len); unsigned long __clear_user(void __user *mem, unsigned long len); +#define __copy_to_user_inatomic __copy_to_user +#define __copy_from_user_inatomic __copy_from_user + #endif /* __X86_64_UACCESS_H */ --- linux-2.6.9-rc1/include/asm-x86_64/unistd.h 2004-08-24 00:03:17.000000000 -0700 +++ 25/include/asm-x86_64/unistd.h 2004-09-03 00:56:58.898206760 -0700 @@ -553,9 +553,23 @@ __SYSCALL(__NR_mq_notify, sys_mq_notify) #define __NR_mq_getsetattr 245 __SYSCALL(__NR_mq_getsetattr, sys_mq_getsetattr) #define __NR_kexec_load 246 -__SYSCALL(__NR_kexec_load, sys_ni_syscall) +__SYSCALL(__NR_kexec_load, sys_kexec_load) +#define __NR_waitid 247 +__SYSCALL(__NR_waitid, sys_waitid) +#define __NR_perfctr_info 248 +__SYSCALL(__NR_perfctr_info, sys_perfctr_info) +#define __NR_vperfctr_open (__NR_perfctr_info+1) +__SYSCALL(__NR_vperfctr_open, sys_vperfctr_open) +#define __NR_vperfctr_control (__NR_perfctr_info+2) +__SYSCALL(__NR_vperfctr_control, sys_vperfctr_control) +#define __NR_vperfctr_unlink (__NR_perfctr_info+3) +__SYSCALL(__NR_vperfctr_unlink, sys_vperfctr_unlink) +#define __NR_vperfctr_iresume (__NR_perfctr_info+4) +__SYSCALL(__NR_vperfctr_iresume, sys_vperfctr_iresume) +#define __NR_vperfctr_read (__NR_perfctr_info+5) +__SYSCALL(__NR_vperfctr_read, sys_vperfctr_read) -#define __NR_syscall_max __NR_kexec_load +#define __NR_syscall_max __NR_vperfctr_read #ifndef __NO_STUBS /* user-visible error numbers are in the range -1 - -4095 */ --- linux-2.6.9-rc1/include/linux/acct.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/include/linux/acct.h 2004-09-03 00:57:09.239634624 -0700 @@ -172,17 +172,24 @@ static inline u32 jiffies_to_AHZ(unsigne #endif } -static inline u64 jiffies_64_to_AHZ(u64 x) +static inline u64 nsec_to_AHZ(u64 x) { -#if (TICK_NSEC % (NSEC_PER_SEC / AHZ)) == 0 -#if HZ != AHZ - do_div(x, HZ / AHZ); -#endif -#else - x *= TICK_NSEC; +#if (NSEC_PER_SEC % AHZ) == 0 do_div(x, (NSEC_PER_SEC / AHZ)); +#elif (AHZ % 512) == 0 + x *= AHZ/512; + do_div(x, (NSEC_PER_SEC / 512)); +#else + /* + * max relative error 5.7e-8 (1.8s per year) for AHZ <= 1024, + * overflow after 64.99 years. + * exact for AHZ=60, 72, 90, 120, 144, 180, 300, 600, 900, ... + */ + x *= 9; + do_div(x, (unsigned long)((9ull * NSEC_PER_SEC + (AHZ/2)) + / AHZ)); #endif - return x; + return x; } #endif /* __KERNEL */ --- linux-2.6.9-rc1/include/linux/acpi.h 2004-08-24 00:01:51.000000000 -0700 +++ 25/include/linux/acpi.h 2004-09-03 00:56:30.137579040 -0700 @@ -453,14 +453,15 @@ void acpi_pci_unregister_driver(struct a #ifdef CONFIG_ACPI_EC -int ec_read(u8 addr, u8 *val); -int ec_write(u8 addr, u8 val); +extern int ec_read(u8 addr, u8 *val); +extern int ec_write(u8 addr, u8 val); #endif /*CONFIG_ACPI_EC*/ #ifdef CONFIG_ACPI_INTERPRETER -int acpi_blacklisted(void); +extern int acpi_blacklisted(void); +extern void acpi_bios_year(char *s); #else /*!CONFIG_ACPI_INTERPRETER*/ --- linux-2.6.9-rc1/include/linux/adfs_fs_i.h 2004-08-24 00:02:26.000000000 -0700 +++ 25/include/linux/adfs_fs_i.h 2004-09-02 20:51:52.000000000 -0700 @@ -17,7 +17,7 @@ struct adfs_inode_info { __u32 execaddr; /* RISC OS exec address */ unsigned int filetype; /* RISC OS file type */ unsigned int attr; /* RISC OS permissions */ - int stamped:1; /* RISC OS file has date/time */ + unsigned int stamped:1; /* RISC OS file has date/time */ struct inode vfs_inode; }; --- linux-2.6.9-rc1/include/linux/aio.h 2004-08-24 00:03:50.000000000 -0700 +++ 25/include/linux/aio.h 2004-09-02 20:51:52.000000000 -0700 @@ -52,7 +52,7 @@ struct kiocb { struct file *ki_filp; struct kioctx *ki_ctx; /* may be NULL for sync ops */ int (*ki_cancel)(struct kiocb *, struct io_event *); - long (*ki_retry)(struct kiocb *); + ssize_t (*ki_retry)(struct kiocb *); void (*ki_dtor)(struct kiocb *); struct list_head ki_list; /* the aio core uses this @@ -64,6 +64,16 @@ struct kiocb { } ki_obj; __u64 ki_user_data; /* user's data for completion */ loff_t ki_pos; + /* State that we remember to be able to restart/retry */ + unsigned short ki_opcode; + size_t ki_nbytes; /* copy of iocb->aio_nbytes */ + char __user *ki_buf; /* remaining iocb->aio_buf */ + size_t ki_left; /* remaining bytes */ + wait_queue_t ki_wait; + long ki_retried; /* just for testing */ + long ki_kicked; /* just for testing */ + long ki_queued; /* just for testing */ + void *private; }; @@ -79,6 +89,8 @@ struct kiocb { (x)->ki_cancel = NULL; \ (x)->ki_dtor = NULL; \ (x)->ki_obj.tsk = tsk; \ + (x)->ki_user_data = 0; \ + init_wait((&(x)->ki_wait)); \ } while (0) #define AIO_RING_MAGIC 0xa10a10a1 @@ -161,6 +173,20 @@ int FASTCALL(io_submit_one(struct kioctx #define get_ioctx(kioctx) do { if (unlikely(atomic_read(&(kioctx)->users) <= 0)) BUG(); atomic_inc(&(kioctx)->users); } while (0) #define put_ioctx(kioctx) do { if (unlikely(atomic_dec_and_test(&(kioctx)->users))) __put_ioctx(kioctx); else if (unlikely(atomic_read(&(kioctx)->users) < 0)) BUG(); } while (0) +#define in_aio() !is_sync_wait(current->io_wait) +/* may be used for debugging */ +#define warn_if_async() \ +do { \ + if (in_aio()) { \ + printk(KERN_ERR "%s(%s:%d) called in async context!\n", \ + __FUNCTION__, __FILE__, __LINE__); \ + dump_stack(); \ + } \ +} while (0) + +#define io_wait_to_kiocb(wait) container_of(wait, struct kiocb, ki_wait) +#define is_retried_kiocb(iocb) ((iocb)->ki_retried > 1) + #include static inline struct kiocb *list_kiocb(struct list_head *h) --- linux-2.6.9-rc1/include/linux/bitmap.h 2004-08-24 00:03:49.000000000 -0700 +++ 25/include/linux/bitmap.h 2004-09-03 00:56:59.740078776 -0700 @@ -41,7 +41,9 @@ * bitmap_shift_right(dst, src, n, nbits) *dst = *src >> n * bitmap_shift_left(dst, src, n, nbits) *dst = *src << n * bitmap_scnprintf(buf, len, src, nbits) Print bitmap src to buf - * bitmap_parse(ubuf, ulen, dst, nbits) Parse bitmap dst from buf + * bitmap_parse(ubuf, ulen, dst, nbits) Parse bitmap dst from user buf + * bitmap_scnlistprintf(buf, len, src, nbits) Print bitmap src as list to buf + * bitmap_parselist(buf, dst, nbits) Parse bitmap dst from list */ /* @@ -98,6 +100,13 @@ extern int bitmap_scnprintf(char *buf, u const unsigned long *src, int nbits); extern int bitmap_parse(const char __user *ubuf, unsigned int ulen, unsigned long *dst, int nbits); +extern int bitmap_scnlistprintf(char *buf, unsigned int len, + const unsigned long *src, int nbits); +extern int bitmap_parselist(const char *buf, unsigned long *maskp, + int nmaskbits); +extern int bitmap_find_free_region(unsigned long *bitmap, int bits, int order); +extern void bitmap_release_region(unsigned long *bitmap, int pos, int order); +extern int bitmap_allocate_region(unsigned long *bitmap, int pos, int order); #define BITMAP_LAST_WORD_MASK(nbits) \ ( \ --- linux-2.6.9-rc1/include/linux/bootmem.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/include/linux/bootmem.h 2004-09-02 20:51:52.000000000 -0700 @@ -67,6 +67,9 @@ extern void * __init __alloc_bootmem_nod __alloc_bootmem_node((pgdat), (x), PAGE_SIZE, 0) #endif /* !CONFIG_HAVE_ARCH_BOOTMEM_NODE */ +extern unsigned long __initdata nr_kernel_pages; +extern unsigned long __initdata nr_all_pages; + extern void *__init alloc_large_system_hash(const char *tablename, unsigned long bucketsize, unsigned long numentries, --- linux-2.6.9-rc1/include/linux/buffer_head.h 2004-08-24 00:01:52.000000000 -0700 +++ 25/include/linux/buffer_head.h 2004-09-02 20:51:52.000000000 -0700 @@ -49,12 +49,12 @@ typedef void (bh_end_io_t)(struct buffer struct buffer_head { /* First cache line: */ unsigned long b_state; /* buffer state bitmap (see above) */ - atomic_t b_count; /* users using this block */ struct buffer_head *b_this_page;/* circular list of page's buffers */ struct page *b_page; /* the page this bh is mapped to */ + atomic_t b_count; /* users using this block */ + u32 b_size; /* block size */ sector_t b_blocknr; /* block number */ - u32 b_size; /* block size */ char *b_data; /* pointer to data block */ struct block_device *b_bdev; @@ -272,12 +272,14 @@ map_bh(struct buffer_head *bh, struct su */ static inline void wait_on_buffer(struct buffer_head *bh) { + might_sleep(); if (buffer_locked(bh) || atomic_read(&bh->b_count) == 0) __wait_on_buffer(bh); } static inline void lock_buffer(struct buffer_head *bh) { + might_sleep(); if (test_set_buffer_locked(bh)) __lock_buffer(bh); } --- linux-2.6.9-rc1/include/linux/cdrom.h 2004-08-24 00:01:51.000000000 -0700 +++ 25/include/linux/cdrom.h 2004-09-03 00:56:49.238675232 -0700 @@ -499,6 +499,7 @@ struct cdrom_generic_command #define GPMODE_VENDOR_PAGE 0x00 #define GPMODE_R_W_ERROR_PAGE 0x01 #define GPMODE_WRITE_PARMS_PAGE 0x05 +#define GPMODE_WCACHING_PAGE 0x08 #define GPMODE_AUDIO_CTL_PAGE 0x0e #define GPMODE_POWER_PAGE 0x1a #define GPMODE_FAULT_FAIL_PAGE 0x1c @@ -947,6 +948,8 @@ struct cdrom_device_info { __u8 reserved : 6; /* not used yet */ int cdda_method; /* see flags */ __u8 last_sense; + __u8 media_written; /* dirty flag, DVD+RW bookkeeping */ + unsigned short mmc3_profile; /* current MMC3 profile */ int for_data; int (*exit)(struct cdrom_device_info *); int mrw_mode_page; --- linux-2.6.9-rc1/include/linux/coda.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/include/linux/coda.h 2004-09-03 00:57:22.850565448 -0700 @@ -200,7 +200,7 @@ typedef u_int32_t vuid_t; typedef u_int32_t vgid_t; #endif /*_VUID_T_ */ -#ifdef CODA_FS_OLD_API +#ifdef CONFIG_CODA_FS_OLD_API struct CodaFid { u_int32_t opaque[3]; }; @@ -220,7 +220,7 @@ struct coda_cred { vgid_t cr_groupid, cr_egid, cr_sgid, cr_fsgid; /* same for groups */ }; -#else /* not defined(CODA_FS_OLD_API) */ +#else /* not defined(CONFIG_CODA_FS_OLD_API) */ struct CodaFid { u_int32_t opaque[4]; @@ -318,7 +318,7 @@ struct coda_statfs { #define CODA_KERNEL_VERSION 0 /* don't care about kernel version number */ #define CODA_KERNEL_VERSION 1 /* The old venus 4.6 compatible interface */ #endif -#ifdef CODA_FS_OLD_API +#ifdef CONFIG_CODA_FS_OLD_API #define CODA_KERNEL_VERSION 2 /* venus_lookup got an extra parameter */ #else #define CODA_KERNEL_VERSION 3 /* 128-bit file identifiers */ @@ -330,7 +330,7 @@ struct coda_statfs { struct coda_in_hdr { u_int32_t opcode; u_int32_t unique; /* Keep multiple outstanding msgs distinct */ -#ifdef CODA_FS_OLD_API +#ifdef CONFIG_CODA_FS_OLD_API u_int16_t pid; /* Common to all */ u_int16_t pgid; /* Common to all */ u_int16_t sid; /* Common to all */ @@ -614,7 +614,7 @@ struct coda_vget_out { /* CODA_PURGEUSER is a venus->kernel call */ struct coda_purgeuser_out { struct coda_out_hdr oh; -#ifdef CODA_FS_OLD_API +#ifdef CONFIG_CODA_FS_OLD_API struct coda_cred cred; #else vuid_t uid; --- linux-2.6.9-rc1/include/linux/compat.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/include/linux/compat.h 2004-09-02 20:51:52.000000000 -0700 @@ -79,6 +79,8 @@ struct compat_rusage { compat_long_t ru_nivcsw; }; +extern int put_compat_rusage(const struct rusage *, struct compat_rusage __user *); + struct compat_dirent { u32 d_ino; compat_off_t d_off; --- linux-2.6.9-rc1/include/linux/compat_ioctl.h 2004-08-24 00:03:38.000000000 -0700 +++ 25/include/linux/compat_ioctl.h 2004-09-03 00:56:49.703604552 -0700 @@ -382,6 +382,8 @@ COMPATIBLE_IOCTL(CDROMREADALL) COMPATIBLE_IOCTL(DVD_READ_STRUCT) COMPATIBLE_IOCTL(DVD_WRITE_STRUCT) COMPATIBLE_IOCTL(DVD_AUTH) +/* pktcdvd */ +COMPATIBLE_IOCTL(PACKET_CTRL_CMD) /* Big L */ ULONG_IOCTL(LOOP_SET_FD) ULONG_IOCTL(LOOP_CHANGE_FD) @@ -735,3 +737,20 @@ COMPATIBLE_IOCTL(SIOCSIWRETRY) COMPATIBLE_IOCTL(SIOCGIWRETRY) COMPATIBLE_IOCTL(SIOCSIWPOWER) COMPATIBLE_IOCTL(SIOCGIWPOWER) +/* hiddev */ +COMPATIBLE_IOCTL(HIDIOCGVERSION) +COMPATIBLE_IOCTL(HIDIOCAPPLICATION) +COMPATIBLE_IOCTL(HIDIOCGDEVINFO) +COMPATIBLE_IOCTL(HIDIOCGSTRING) +COMPATIBLE_IOCTL(HIDIOCINITREPORT) +COMPATIBLE_IOCTL(HIDIOCGREPORT) +COMPATIBLE_IOCTL(HIDIOCSREPORT) +COMPATIBLE_IOCTL(HIDIOCGREPORTINFO) +COMPATIBLE_IOCTL(HIDIOCGFIELDINFO) +COMPATIBLE_IOCTL(HIDIOCGUSAGE) +COMPATIBLE_IOCTL(HIDIOCSUSAGE) +COMPATIBLE_IOCTL(HIDIOCGUCODE) +COMPATIBLE_IOCTL(HIDIOCGFLAG) +COMPATIBLE_IOCTL(HIDIOCSFLAG) +COMPATIBLE_IOCTL(HIDIOCGCOLLECTIONINDEX) +COMPATIBLE_IOCTL(HIDIOCGCOLLECTIONINFO) --- linux-2.6.9-rc1/include/linux/compiler-gcc.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/include/linux/compiler-gcc.h 2004-09-03 00:56:40.646981368 -0700 @@ -13,5 +13,5 @@ shouldn't recognize the original var, and make assumptions about it */ #define RELOC_HIDE(ptr, off) \ ({ unsigned long __ptr; \ - __asm__ ("" : "=g"(__ptr) : "0"(ptr)); \ + __asm__ ("" : "=r"(__ptr) : "0"(ptr)); \ (typeof(ptr)) (__ptr + (off)); }) --- linux-2.6.9-rc1/include/linux/config.h 2004-08-24 00:03:38.000000000 -0700 +++ 25/include/linux/config.h 2004-09-03 00:56:36.980538752 -0700 @@ -2,5 +2,8 @@ #define _LINUX_CONFIG_H #include +#ifdef CONFIG_X86 +#include +#endif #endif --- linux-2.6.9-rc1/include/linux/cpufreq.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/include/linux/cpufreq.h 2004-09-03 00:56:30.568513528 -0700 @@ -209,13 +209,6 @@ struct cpufreq_driver { #define CPUFREQ_CONST_LOOPS 0x02 /* loops_per_jiffy or other kernel * "constants" aren't affected by * frequency transitions */ -#define CPUFREQ_PANIC_OUTOFSYNC 0x04 /* panic if cpufreq's opinion of - * current frequency differs from - * actual frequency */ -#define CPUFREQ_PANIC_RESUME_OUTOFSYNC 0x08 /* panic if cpufreq's opinion of - * current frequency differs from - * actual frequency on resume - * from sleep. */ int cpufreq_register_driver(struct cpufreq_driver *driver_data); --- linux-2.6.9-rc1/include/linux/cpumask.h 2004-08-24 00:02:26.000000000 -0700 +++ 25/include/linux/cpumask.h 2004-09-03 00:56:59.741078624 -0700 @@ -10,6 +10,8 @@ * * For details of cpumask_scnprintf() and cpumask_parse(), * see bitmap_scnprintf() and bitmap_parse() in lib/bitmap.c. + * For details of cpulist_scnprintf() and cpulist_parse(), see + * bitmap_scnlistprintf() and bitmap_parselist(), also in bitmap.c. * * The available cpumask operations are: * @@ -46,6 +48,8 @@ * * int cpumask_scnprintf(buf, len, mask) Format cpumask for printing * int cpumask_parse(ubuf, ulen, mask) Parse ascii string as cpumask + * int cpulist_scnprintf(buf, len, mask) Format cpumask as list for printing + * int cpulist_parse(buf, map) Parse ascii string as cpulist * * for_each_cpu_mask(cpu, mask) for-loop cpu over mask * @@ -73,6 +77,7 @@ * inside a macro, the way we do the other calls. */ +#include #include #include #include @@ -207,13 +212,13 @@ static inline void __cpus_shift_left(cpu #define first_cpu(src) __first_cpu(&(src), NR_CPUS) static inline int __first_cpu(const cpumask_t *srcp, int nbits) { - return find_first_bit(srcp->bits, nbits); + return min_t(int, nbits, find_first_bit(srcp->bits, nbits)); } #define next_cpu(n, src) __next_cpu((n), &(src), NR_CPUS) static inline int __next_cpu(int n, const cpumask_t *srcp, int nbits) { - return find_next_bit(srcp->bits, nbits, n+1); + return min_t(int, nbits, find_next_bit(srcp->bits, nbits, n+1)); } #define cpumask_of_cpu(cpu) \ @@ -267,14 +272,28 @@ static inline int __cpumask_scnprintf(ch return bitmap_scnprintf(buf, len, srcp->bits, nbits); } -#define cpumask_parse(ubuf, ulen, src) \ - __cpumask_parse((ubuf), (ulen), &(src), NR_CPUS) +#define cpumask_parse(ubuf, ulen, dst) \ + __cpumask_parse((ubuf), (ulen), &(dst), NR_CPUS) static inline int __cpumask_parse(const char __user *buf, int len, cpumask_t *dstp, int nbits) { return bitmap_parse(buf, len, dstp->bits, nbits); } +#define cpulist_scnprintf(buf, len, src) \ + __cpulist_scnprintf((buf), (len), &(src), NR_CPUS) +static inline int __cpulist_scnprintf(char *buf, int len, + const cpumask_t *srcp, int nbits) +{ + return bitmap_scnlistprintf(buf, len, srcp->bits, nbits); +} + +#define cpulist_parse(buf, dst) __cpulist_parse((buf), &(dst), NR_CPUS) +static inline int __cpulist_parse(const char *buf, cpumask_t *dstp, int nbits) +{ + return bitmap_parselist(buf, dstp->bits, nbits); +} + #if NR_CPUS > 1 #define for_each_cpu_mask(cpu, mask) \ for ((cpu) = first_cpu(mask); \ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/linux/cpuset.h 2004-09-03 00:57:00.770922064 -0700 @@ -0,0 +1,57 @@ +#ifndef _LINUX_CPUSET_H +#define _LINUX_CPUSET_H +/* + * cpuset interface + * + * Copyright (C) 2003 BULL SA + * Copyright (C) 2004 Silicon Graphics, Inc. + * + */ + +#include +#include +#include + +#ifdef CONFIG_CPUSETS + +extern int cpuset_init(void); +extern void cpuset_init_smp(void); +extern void cpuset_fork(struct task_struct *p); +extern void cpuset_exit(struct task_struct *p); +extern const cpumask_t cpuset_cpus_allowed(const struct task_struct *p); +void cpuset_init_current_mems_allowed(void); +void cpuset_update_current_mems_allowed(void); +void cpuset_restrict_to_mems_allowed(unsigned long *nodes); +int cpuset_zonelist_valid_mems_allowed(struct zonelist *zl); +int cpuset_zone_allowed(struct zone *z); +extern struct file_operations proc_cpuset_operations; + +#else /* !CONFIG_CPUSETS */ + +static inline int cpuset_init(void) { return 0; } +static inline void cpuset_init_smp(void) {} +static inline void cpuset_fork(struct task_struct *p) {} +static inline void cpuset_exit(struct task_struct *p) {} + +static inline const cpumask_t cpuset_cpus_allowed(struct task_struct *p) +{ + return cpu_possible_map; +} + +static inline void cpuset_init_current_mems_allowed(void) {} +static inline void cpuset_update_current_mems_allowed(void) {} +static inline void cpuset_restrict_to_mems_allowed(unsigned long *nodes) {} + +static inline int cpuset_zonelist_valid_mems_allowed(struct zonelist *zl) +{ + return 1; +} + +static inline int cpuset_zone_allowed(struct zone *z) +{ + return 1; +} + +#endif /* !CONFIG_CPUSETS */ + +#endif /* _LINUX_CPUSET_H */ --- linux-2.6.9-rc1/include/linux/dcache.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/include/linux/dcache.h 2004-09-02 20:51:52.000000000 -0700 @@ -33,8 +33,8 @@ struct vfsmount; */ struct qstr { unsigned int hash; - const unsigned char *name; unsigned int len; + const unsigned char *name; }; struct dentry_stat_t { @@ -101,11 +101,11 @@ struct dentry { unsigned long d_time; /* used by d_revalidate */ struct dentry_operations *d_op; struct super_block *d_sb; /* The root of the dentry tree */ - int d_mounted; void *d_fsdata; /* fs-specific data */ struct rcu_head d_rcu; struct dcookie_struct *d_cookie; /* cookie, if any */ struct hlist_node d_hash; /* lookup hash list */ + int d_mounted; unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* small names */ }; --- linux-2.6.9-rc1/include/linux/delay.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/include/linux/delay.h 2004-09-03 00:57:22.710586728 -0700 @@ -39,5 +39,11 @@ extern unsigned long loops_per_jiffy; #endif void msleep(unsigned int msecs); +unsigned long msleep_interruptible(unsigned int msecs); + +static inline void ssleep(unsigned int seconds) +{ + msleep(seconds * 1000); +} #endif /* defined(_LINUX_DELAY_H) */ --- linux-2.6.9-rc1/include/linux/device.h 2004-08-24 00:03:32.000000000 -0700 +++ 25/include/linux/device.h 2004-09-02 20:51:52.000000000 -0700 @@ -59,7 +59,6 @@ struct bus_type { struct driver_attribute * drv_attrs; int (*match)(struct device * dev, struct device_driver * drv); - struct device * (*add) (struct device * parent, char * bus_id); int (*hotplug) (struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size); int (*suspend)(struct device * dev, u32 state); @@ -285,6 +284,9 @@ struct device { struct list_head dma_pools; /* dma pools (if dma'ble) */ + struct dma_coherent_mem *dma_mem; /* internal for coherent mem + override */ + void (*release)(struct device * dev); }; --- linux-2.6.9-rc1/include/linux/dma-mapping.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/include/linux/dma-mapping.h 2004-09-02 20:51:52.000000000 -0700 @@ -1,6 +1,8 @@ #ifndef _ASM_LINUX_DMA_MAPPING_H #define _ASM_LINUX_DMA_MAPPING_H +#include + /* These definitions mirror those in pci.h, so they can be used * interchangeably with their PCI_ counterparts */ enum dma_data_direction { @@ -21,6 +23,33 @@ enum dma_data_direction { extern u64 dma_get_required_mask(struct device *dev); +/* flags for the coherent memory api */ +#define DMA_MEMORY_MAP 0x01 +#define DMA_MEMORY_IO 0x02 +#define DMA_MEMORY_INCLUDES_CHILDREN 0x04 +#define DMA_MEMORY_EXCLUSIVE 0x08 + +#ifndef ARCH_HAS_DMA_DECLARE_COHERENT_MEMORY +static inline int +dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, + dma_addr_t device_addr, size_t size, int flags) +{ + return 0; +} + +static inline void +dma_release_declared_memory(struct device *dev) +{ +} + +static inline void * +dma_mark_declared_memory_occupied(struct device *dev, + dma_addr_t device_addr, size_t size) +{ + return ERR_PTR(-EBUSY); +} +#endif + #endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/linux/dwarf2.h 2004-09-03 00:56:35.786720240 -0700 @@ -0,0 +1,738 @@ +/* Declarations and definitions of codes relating to the DWARF2 symbolic + debugging information format. + Copyright (C) 1992, 1993, 1995, 1996, 1997, 1999, 2000, 2001, 2002 + Free Software Foundation, Inc. + + Written by Gary Funck (gary@intrepid.com) The Ada Joint Program + Office (AJPO), Florida State Unviversity and Silicon Graphics Inc. + provided support for this effort -- June 21, 1995. + + Derived from the DWARF 1 implementation written by Ron Guilmette + (rfg@netcom.com), November 1990. + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2, or (at your option) any later + version. + + GCC is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with GCC; see the file COPYING. If not, write to the Free + Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +/* This file is derived from the DWARF specification (a public document) + Revision 2.0.0 (July 27, 1993) developed by the UNIX International + Programming Languages Special Interest Group (UI/PLSIG) and distributed + by UNIX International. Copies of this specification are available from + UNIX International, 20 Waterview Boulevard, Parsippany, NJ, 07054. + + This file also now contains definitions from the DWARF 3 specification. */ + +/* This file is shared between GCC and GDB, and should not contain + prototypes. */ + +#ifndef _ELF_DWARF2_H +#define _ELF_DWARF2_H + +/* Structure found in the .debug_line section. */ +#ifndef __ASSEMBLY__ +typedef struct +{ + unsigned char li_length [4]; + unsigned char li_version [2]; + unsigned char li_prologue_length [4]; + unsigned char li_min_insn_length [1]; + unsigned char li_default_is_stmt [1]; + unsigned char li_line_base [1]; + unsigned char li_line_range [1]; + unsigned char li_opcode_base [1]; +} +DWARF2_External_LineInfo; + +typedef struct +{ + unsigned long li_length; + unsigned short li_version; + unsigned int li_prologue_length; + unsigned char li_min_insn_length; + unsigned char li_default_is_stmt; + int li_line_base; + unsigned char li_line_range; + unsigned char li_opcode_base; +} +DWARF2_Internal_LineInfo; + +/* Structure found in .debug_pubnames section. */ +typedef struct +{ + unsigned char pn_length [4]; + unsigned char pn_version [2]; + unsigned char pn_offset [4]; + unsigned char pn_size [4]; +} +DWARF2_External_PubNames; + +typedef struct +{ + unsigned long pn_length; + unsigned short pn_version; + unsigned long pn_offset; + unsigned long pn_size; +} +DWARF2_Internal_PubNames; + +/* Structure found in .debug_info section. */ +typedef struct +{ + unsigned char cu_length [4]; + unsigned char cu_version [2]; + unsigned char cu_abbrev_offset [4]; + unsigned char cu_pointer_size [1]; +} +DWARF2_External_CompUnit; + +typedef struct +{ + unsigned long cu_length; + unsigned short cu_version; + unsigned long cu_abbrev_offset; + unsigned char cu_pointer_size; +} +DWARF2_Internal_CompUnit; + +typedef struct +{ + unsigned char ar_length [4]; + unsigned char ar_version [2]; + unsigned char ar_info_offset [4]; + unsigned char ar_pointer_size [1]; + unsigned char ar_segment_size [1]; +} +DWARF2_External_ARange; + +typedef struct +{ + unsigned long ar_length; + unsigned short ar_version; + unsigned long ar_info_offset; + unsigned char ar_pointer_size; + unsigned char ar_segment_size; +} +DWARF2_Internal_ARange; + +#define ENUM(name) enum name { +#define IF_NOT_ASM(a) a +#define COMMA , +#else +#define ENUM(name) +#define IF_NOT_ASM(a) +#define COMMA + +#endif + +/* Tag names and codes. */ +ENUM(dwarf_tag) + + DW_TAG_padding = 0x00 COMMA + DW_TAG_array_type = 0x01 COMMA + DW_TAG_class_type = 0x02 COMMA + DW_TAG_entry_point = 0x03 COMMA + DW_TAG_enumeration_type = 0x04 COMMA + DW_TAG_formal_parameter = 0x05 COMMA + DW_TAG_imported_declaration = 0x08 COMMA + DW_TAG_label = 0x0a COMMA + DW_TAG_lexical_block = 0x0b COMMA + DW_TAG_member = 0x0d COMMA + DW_TAG_pointer_type = 0x0f COMMA + DW_TAG_reference_type = 0x10 COMMA + DW_TAG_compile_unit = 0x11 COMMA + DW_TAG_string_type = 0x12 COMMA + DW_TAG_structure_type = 0x13 COMMA + DW_TAG_subroutine_type = 0x15 COMMA + DW_TAG_typedef = 0x16 COMMA + DW_TAG_union_type = 0x17 COMMA + DW_TAG_unspecified_parameters = 0x18 COMMA + DW_TAG_variant = 0x19 COMMA + DW_TAG_common_block = 0x1a COMMA + DW_TAG_common_inclusion = 0x1b COMMA + DW_TAG_inheritance = 0x1c COMMA + DW_TAG_inlined_subroutine = 0x1d COMMA + DW_TAG_module = 0x1e COMMA + DW_TAG_ptr_to_member_type = 0x1f COMMA + DW_TAG_set_type = 0x20 COMMA + DW_TAG_subrange_type = 0x21 COMMA + DW_TAG_with_stmt = 0x22 COMMA + DW_TAG_access_declaration = 0x23 COMMA + DW_TAG_base_type = 0x24 COMMA + DW_TAG_catch_block = 0x25 COMMA + DW_TAG_const_type = 0x26 COMMA + DW_TAG_constant = 0x27 COMMA + DW_TAG_enumerator = 0x28 COMMA + DW_TAG_file_type = 0x29 COMMA + DW_TAG_friend = 0x2a COMMA + DW_TAG_namelist = 0x2b COMMA + DW_TAG_namelist_item = 0x2c COMMA + DW_TAG_packed_type = 0x2d COMMA + DW_TAG_subprogram = 0x2e COMMA + DW_TAG_template_type_param = 0x2f COMMA + DW_TAG_template_value_param = 0x30 COMMA + DW_TAG_thrown_type = 0x31 COMMA + DW_TAG_try_block = 0x32 COMMA + DW_TAG_variant_part = 0x33 COMMA + DW_TAG_variable = 0x34 COMMA + DW_TAG_volatile_type = 0x35 COMMA + /* DWARF 3. */ + DW_TAG_dwarf_procedure = 0x36 COMMA + DW_TAG_restrict_type = 0x37 COMMA + DW_TAG_interface_type = 0x38 COMMA + DW_TAG_namespace = 0x39 COMMA + DW_TAG_imported_module = 0x3a COMMA + DW_TAG_unspecified_type = 0x3b COMMA + DW_TAG_partial_unit = 0x3c COMMA + DW_TAG_imported_unit = 0x3d COMMA + /* SGI/MIPS Extensions. */ + DW_TAG_MIPS_loop = 0x4081 COMMA + /* GNU extensions. */ + DW_TAG_format_label = 0x4101 COMMA /* For FORTRAN 77 and Fortran 90. */ + DW_TAG_function_template = 0x4102 COMMA /* For C++. */ + DW_TAG_class_template = 0x4103 COMMA /* For C++. */ + DW_TAG_GNU_BINCL = 0x4104 COMMA + DW_TAG_GNU_EINCL = 0x4105 COMMA + /* Extensions for UPC. See: http://upc.gwu.edu/~upc. */ + DW_TAG_upc_shared_type = 0x8765 COMMA + DW_TAG_upc_strict_type = 0x8766 COMMA + DW_TAG_upc_relaxed_type = 0x8767 +IF_NOT_ASM(};) + +#define DW_TAG_lo_user 0x4080 +#define DW_TAG_hi_user 0xffff + +/* Flag that tells whether entry has a child or not. */ +#define DW_children_no 0 +#define DW_children_yes 1 + +/* Form names and codes. */ +ENUM(dwarf_form) + + DW_FORM_addr = 0x01 COMMA + DW_FORM_block2 = 0x03 COMMA + DW_FORM_block4 = 0x04 COMMA + DW_FORM_data2 = 0x05 COMMA + DW_FORM_data4 = 0x06 COMMA + DW_FORM_data8 = 0x07 COMMA + DW_FORM_string = 0x08 COMMA + DW_FORM_block = 0x09 COMMA + DW_FORM_block1 = 0x0a COMMA + DW_FORM_data1 = 0x0b COMMA + DW_FORM_flag = 0x0c COMMA + DW_FORM_sdata = 0x0d COMMA + DW_FORM_strp = 0x0e COMMA + DW_FORM_udata = 0x0f COMMA + DW_FORM_ref_addr = 0x10 COMMA + DW_FORM_ref1 = 0x11 COMMA + DW_FORM_ref2 = 0x12 COMMA + DW_FORM_ref4 = 0x13 COMMA + DW_FORM_ref8 = 0x14 COMMA + DW_FORM_ref_udata = 0x15 COMMA + DW_FORM_indirect = 0x16 +IF_NOT_ASM(};) + +/* Attribute names and codes. */ + +ENUM(dwarf_attribute) + + DW_AT_sibling = 0x01 COMMA + DW_AT_location = 0x02 COMMA + DW_AT_name = 0x03 COMMA + DW_AT_ordering = 0x09 COMMA + DW_AT_subscr_data = 0x0a COMMA + DW_AT_byte_size = 0x0b COMMA + DW_AT_bit_offset = 0x0c COMMA + DW_AT_bit_size = 0x0d COMMA + DW_AT_element_list = 0x0f COMMA + DW_AT_stmt_list = 0x10 COMMA + DW_AT_low_pc = 0x11 COMMA + DW_AT_high_pc = 0x12 COMMA + DW_AT_language = 0x13 COMMA + DW_AT_member = 0x14 COMMA + DW_AT_discr = 0x15 COMMA + DW_AT_discr_value = 0x16 COMMA + DW_AT_visibility = 0x17 COMMA + DW_AT_import = 0x18 COMMA + DW_AT_string_length = 0x19 COMMA + DW_AT_common_reference = 0x1a COMMA + DW_AT_comp_dir = 0x1b COMMA + DW_AT_const_value = 0x1c COMMA + DW_AT_containing_type = 0x1d COMMA + DW_AT_default_value = 0x1e COMMA + DW_AT_inline = 0x20 COMMA + DW_AT_is_optional = 0x21 COMMA + DW_AT_lower_bound = 0x22 COMMA + DW_AT_producer = 0x25 COMMA + DW_AT_prototyped = 0x27 COMMA + DW_AT_return_addr = 0x2a COMMA + DW_AT_start_scope = 0x2c COMMA + DW_AT_stride_size = 0x2e COMMA + DW_AT_upper_bound = 0x2f COMMA + DW_AT_abstract_origin = 0x31 COMMA + DW_AT_accessibility = 0x32 COMMA + DW_AT_address_class = 0x33 COMMA + DW_AT_artificial = 0x34 COMMA + DW_AT_base_types = 0x35 COMMA + DW_AT_calling_convention = 0x36 COMMA + DW_AT_count = 0x37 COMMA + DW_AT_data_member_location = 0x38 COMMA + DW_AT_decl_column = 0x39 COMMA + DW_AT_decl_file = 0x3a COMMA + DW_AT_decl_line = 0x3b COMMA + DW_AT_declaration = 0x3c COMMA + DW_AT_discr_list = 0x3d COMMA + DW_AT_encoding = 0x3e COMMA + DW_AT_external = 0x3f COMMA + DW_AT_frame_base = 0x40 COMMA + DW_AT_friend = 0x41 COMMA + DW_AT_identifier_case = 0x42 COMMA + DW_AT_macro_info = 0x43 COMMA + DW_AT_namelist_items = 0x44 COMMA + DW_AT_priority = 0x45 COMMA + DW_AT_segment = 0x46 COMMA + DW_AT_specification = 0x47 COMMA + DW_AT_static_link = 0x48 COMMA + DW_AT_type = 0x49 COMMA + DW_AT_use_location = 0x4a COMMA + DW_AT_variable_parameter = 0x4b COMMA + DW_AT_virtuality = 0x4c COMMA + DW_AT_vtable_elem_location = 0x4d COMMA + /* DWARF 3 values. */ + DW_AT_allocated = 0x4e COMMA + DW_AT_associated = 0x4f COMMA + DW_AT_data_location = 0x50 COMMA + DW_AT_stride = 0x51 COMMA + DW_AT_entry_pc = 0x52 COMMA + DW_AT_use_UTF8 = 0x53 COMMA + DW_AT_extension = 0x54 COMMA + DW_AT_ranges = 0x55 COMMA + DW_AT_trampoline = 0x56 COMMA + DW_AT_call_column = 0x57 COMMA + DW_AT_call_file = 0x58 COMMA + DW_AT_call_line = 0x59 COMMA + /* SGI/MIPS extensions. */ + DW_AT_MIPS_fde = 0x2001 COMMA + DW_AT_MIPS_loop_begin = 0x2002 COMMA + DW_AT_MIPS_tail_loop_begin = 0x2003 COMMA + DW_AT_MIPS_epilog_begin = 0x2004 COMMA + DW_AT_MIPS_loop_unroll_factor = 0x2005 COMMA + DW_AT_MIPS_software_pipeline_depth = 0x2006 COMMA + DW_AT_MIPS_linkage_name = 0x2007 COMMA + DW_AT_MIPS_stride = 0x2008 COMMA + DW_AT_MIPS_abstract_name = 0x2009 COMMA + DW_AT_MIPS_clone_origin = 0x200a COMMA + DW_AT_MIPS_has_inlines = 0x200b COMMA + /* GNU extensions. */ + DW_AT_sf_names = 0x2101 COMMA + DW_AT_src_info = 0x2102 COMMA + DW_AT_mac_info = 0x2103 COMMA + DW_AT_src_coords = 0x2104 COMMA + DW_AT_body_begin = 0x2105 COMMA + DW_AT_body_end = 0x2106 COMMA + DW_AT_GNU_vector = 0x2107 COMMA + /* VMS extensions. */ + DW_AT_VMS_rtnbeg_pd_address = 0x2201 COMMA + /* UPC extension. */ + DW_AT_upc_threads_scaled = 0x3210 +IF_NOT_ASM(};) + +#define DW_AT_lo_user 0x2000 /* Implementation-defined range start. */ +#define DW_AT_hi_user 0x3ff0 /* Implementation-defined range end. */ + +/* Location atom names and codes. */ +ENUM(dwarf_location_atom) + + DW_OP_addr = 0x03 COMMA + DW_OP_deref = 0x06 COMMA + DW_OP_const1u = 0x08 COMMA + DW_OP_const1s = 0x09 COMMA + DW_OP_const2u = 0x0a COMMA + DW_OP_const2s = 0x0b COMMA + DW_OP_const4u = 0x0c COMMA + DW_OP_const4s = 0x0d COMMA + DW_OP_const8u = 0x0e COMMA + DW_OP_const8s = 0x0f COMMA + DW_OP_constu = 0x10 COMMA + DW_OP_consts = 0x11 COMMA + DW_OP_dup = 0x12 COMMA + DW_OP_drop = 0x13 COMMA + DW_OP_over = 0x14 COMMA + DW_OP_pick = 0x15 COMMA + DW_OP_swap = 0x16 COMMA + DW_OP_rot = 0x17 COMMA + DW_OP_xderef = 0x18 COMMA + DW_OP_abs = 0x19 COMMA + DW_OP_and = 0x1a COMMA + DW_OP_div = 0x1b COMMA + DW_OP_minus = 0x1c COMMA + DW_OP_mod = 0x1d COMMA + DW_OP_mul = 0x1e COMMA + DW_OP_neg = 0x1f COMMA + DW_OP_not = 0x20 COMMA + DW_OP_or = 0x21 COMMA + DW_OP_plus = 0x22 COMMA + DW_OP_plus_uconst = 0x23 COMMA + DW_OP_shl = 0x24 COMMA + DW_OP_shr = 0x25 COMMA + DW_OP_shra = 0x26 COMMA + DW_OP_xor = 0x27 COMMA + DW_OP_bra = 0x28 COMMA + DW_OP_eq = 0x29 COMMA + DW_OP_ge = 0x2a COMMA + DW_OP_gt = 0x2b COMMA + DW_OP_le = 0x2c COMMA + DW_OP_lt = 0x2d COMMA + DW_OP_ne = 0x2e COMMA + DW_OP_skip = 0x2f COMMA + DW_OP_lit0 = 0x30 COMMA + DW_OP_lit1 = 0x31 COMMA + DW_OP_lit2 = 0x32 COMMA + DW_OP_lit3 = 0x33 COMMA + DW_OP_lit4 = 0x34 COMMA + DW_OP_lit5 = 0x35 COMMA + DW_OP_lit6 = 0x36 COMMA + DW_OP_lit7 = 0x37 COMMA + DW_OP_lit8 = 0x38 COMMA + DW_OP_lit9 = 0x39 COMMA + DW_OP_lit10 = 0x3a COMMA + DW_OP_lit11 = 0x3b COMMA + DW_OP_lit12 = 0x3c COMMA + DW_OP_lit13 = 0x3d COMMA + DW_OP_lit14 = 0x3e COMMA + DW_OP_lit15 = 0x3f COMMA + DW_OP_lit16 = 0x40 COMMA + DW_OP_lit17 = 0x41 COMMA + DW_OP_lit18 = 0x42 COMMA + DW_OP_lit19 = 0x43 COMMA + DW_OP_lit20 = 0x44 COMMA + DW_OP_lit21 = 0x45 COMMA + DW_OP_lit22 = 0x46 COMMA + DW_OP_lit23 = 0x47 COMMA + DW_OP_lit24 = 0x48 COMMA + DW_OP_lit25 = 0x49 COMMA + DW_OP_lit26 = 0x4a COMMA + DW_OP_lit27 = 0x4b COMMA + DW_OP_lit28 = 0x4c COMMA + DW_OP_lit29 = 0x4d COMMA + DW_OP_lit30 = 0x4e COMMA + DW_OP_lit31 = 0x4f COMMA + DW_OP_reg0 = 0x50 COMMA + DW_OP_reg1 = 0x51 COMMA + DW_OP_reg2 = 0x52 COMMA + DW_OP_reg3 = 0x53 COMMA + DW_OP_reg4 = 0x54 COMMA + DW_OP_reg5 = 0x55 COMMA + DW_OP_reg6 = 0x56 COMMA + DW_OP_reg7 = 0x57 COMMA + DW_OP_reg8 = 0x58 COMMA + DW_OP_reg9 = 0x59 COMMA + DW_OP_reg10 = 0x5a COMMA + DW_OP_reg11 = 0x5b COMMA + DW_OP_reg12 = 0x5c COMMA + DW_OP_reg13 = 0x5d COMMA + DW_OP_reg14 = 0x5e COMMA + DW_OP_reg15 = 0x5f COMMA + DW_OP_reg16 = 0x60 COMMA + DW_OP_reg17 = 0x61 COMMA + DW_OP_reg18 = 0x62 COMMA + DW_OP_reg19 = 0x63 COMMA + DW_OP_reg20 = 0x64 COMMA + DW_OP_reg21 = 0x65 COMMA + DW_OP_reg22 = 0x66 COMMA + DW_OP_reg23 = 0x67 COMMA + DW_OP_reg24 = 0x68 COMMA + DW_OP_reg25 = 0x69 COMMA + DW_OP_reg26 = 0x6a COMMA + DW_OP_reg27 = 0x6b COMMA + DW_OP_reg28 = 0x6c COMMA + DW_OP_reg29 = 0x6d COMMA + DW_OP_reg30 = 0x6e COMMA + DW_OP_reg31 = 0x6f COMMA + DW_OP_breg0 = 0x70 COMMA + DW_OP_breg1 = 0x71 COMMA + DW_OP_breg2 = 0x72 COMMA + DW_OP_breg3 = 0x73 COMMA + DW_OP_breg4 = 0x74 COMMA + DW_OP_breg5 = 0x75 COMMA + DW_OP_breg6 = 0x76 COMMA + DW_OP_breg7 = 0x77 COMMA + DW_OP_breg8 = 0x78 COMMA + DW_OP_breg9 = 0x79 COMMA + DW_OP_breg10 = 0x7a COMMA + DW_OP_breg11 = 0x7b COMMA + DW_OP_breg12 = 0x7c COMMA + DW_OP_breg13 = 0x7d COMMA + DW_OP_breg14 = 0x7e COMMA + DW_OP_breg15 = 0x7f COMMA + DW_OP_breg16 = 0x80 COMMA + DW_OP_breg17 = 0x81 COMMA + DW_OP_breg18 = 0x82 COMMA + DW_OP_breg19 = 0x83 COMMA + DW_OP_breg20 = 0x84 COMMA + DW_OP_breg21 = 0x85 COMMA + DW_OP_breg22 = 0x86 COMMA + DW_OP_breg23 = 0x87 COMMA + DW_OP_breg24 = 0x88 COMMA + DW_OP_breg25 = 0x89 COMMA + DW_OP_breg26 = 0x8a COMMA + DW_OP_breg27 = 0x8b COMMA + DW_OP_breg28 = 0x8c COMMA + DW_OP_breg29 = 0x8d COMMA + DW_OP_breg30 = 0x8e COMMA + DW_OP_breg31 = 0x8f COMMA + DW_OP_regx = 0x90 COMMA + DW_OP_fbreg = 0x91 COMMA + DW_OP_bregx = 0x92 COMMA + DW_OP_piece = 0x93 COMMA + DW_OP_deref_size = 0x94 COMMA + DW_OP_xderef_size = 0x95 COMMA + DW_OP_nop = 0x96 COMMA + /* DWARF 3 extensions. */ + DW_OP_push_object_address = 0x97 COMMA + DW_OP_call2 = 0x98 COMMA + DW_OP_call4 = 0x99 COMMA + DW_OP_call_ref = 0x9a COMMA + /* GNU extensions. */ + DW_OP_GNU_push_tls_address = 0xe0 +IF_NOT_ASM(};) + +#define DW_OP_lo_user 0xe0 /* Implementation-defined range start. */ +#define DW_OP_hi_user 0xff /* Implementation-defined range end. */ + +/* Type encodings. */ +ENUM(dwarf_type) + + DW_ATE_void = 0x0 COMMA + DW_ATE_address = 0x1 COMMA + DW_ATE_boolean = 0x2 COMMA + DW_ATE_complex_float = 0x3 COMMA + DW_ATE_float = 0x4 COMMA + DW_ATE_signed = 0x5 COMMA + DW_ATE_signed_char = 0x6 COMMA + DW_ATE_unsigned = 0x7 COMMA + DW_ATE_unsigned_char = 0x8 COMMA + /* DWARF 3. */ + DW_ATE_imaginary_float = 0x9 +IF_NOT_ASM(};) + +#define DW_ATE_lo_user 0x80 +#define DW_ATE_hi_user 0xff + +/* Array ordering names and codes. */ +ENUM(dwarf_array_dim_ordering) + + DW_ORD_row_major = 0 COMMA + DW_ORD_col_major = 1 +IF_NOT_ASM(};) + +/* Access attribute. */ +ENUM(dwarf_access_attribute) + + DW_ACCESS_public = 1 COMMA + DW_ACCESS_protected = 2 COMMA + DW_ACCESS_private = 3 +IF_NOT_ASM(};) + +/* Visibility. */ +ENUM(dwarf_visibility_attribute) + + DW_VIS_local = 1 COMMA + DW_VIS_exported = 2 COMMA + DW_VIS_qualified = 3 +IF_NOT_ASM(};) + +/* Virtuality. */ +ENUM(dwarf_virtuality_attribute) + + DW_VIRTUALITY_none = 0 COMMA + DW_VIRTUALITY_virtual = 1 COMMA + DW_VIRTUALITY_pure_virtual = 2 +IF_NOT_ASM(};) + +/* Case sensitivity. */ +ENUM(dwarf_id_case) + + DW_ID_case_sensitive = 0 COMMA + DW_ID_up_case = 1 COMMA + DW_ID_down_case = 2 COMMA + DW_ID_case_insensitive = 3 +IF_NOT_ASM(};) + +/* Calling convention. */ +ENUM(dwarf_calling_convention) + + DW_CC_normal = 0x1 COMMA + DW_CC_program = 0x2 COMMA + DW_CC_nocall = 0x3 +IF_NOT_ASM(};) + +#define DW_CC_lo_user 0x40 +#define DW_CC_hi_user 0xff + +/* Inline attribute. */ +ENUM(dwarf_inline_attribute) + + DW_INL_not_inlined = 0 COMMA + DW_INL_inlined = 1 COMMA + DW_INL_declared_not_inlined = 2 COMMA + DW_INL_declared_inlined = 3 +IF_NOT_ASM(};) + +/* Discriminant lists. */ +ENUM(dwarf_discrim_list) + + DW_DSC_label = 0 COMMA + DW_DSC_range = 1 +IF_NOT_ASM(};) + +/* Line number opcodes. */ +ENUM(dwarf_line_number_ops) + + DW_LNS_extended_op = 0 COMMA + DW_LNS_copy = 1 COMMA + DW_LNS_advance_pc = 2 COMMA + DW_LNS_advance_line = 3 COMMA + DW_LNS_set_file = 4 COMMA + DW_LNS_set_column = 5 COMMA + DW_LNS_negate_stmt = 6 COMMA + DW_LNS_set_basic_block = 7 COMMA + DW_LNS_const_add_pc = 8 COMMA + DW_LNS_fixed_advance_pc = 9 COMMA + /* DWARF 3. */ + DW_LNS_set_prologue_end = 10 COMMA + DW_LNS_set_epilogue_begin = 11 COMMA + DW_LNS_set_isa = 12 +IF_NOT_ASM(};) + +/* Line number extended opcodes. */ +ENUM(dwarf_line_number_x_ops) + + DW_LNE_end_sequence = 1 COMMA + DW_LNE_set_address = 2 COMMA + DW_LNE_define_file = 3 +IF_NOT_ASM(};) + +/* Call frame information. */ +ENUM(dwarf_call_frame_info) + + DW_CFA_advance_loc = 0x40 COMMA + DW_CFA_offset = 0x80 COMMA + DW_CFA_restore = 0xc0 COMMA + DW_CFA_nop = 0x00 COMMA + DW_CFA_set_loc = 0x01 COMMA + DW_CFA_advance_loc1 = 0x02 COMMA + DW_CFA_advance_loc2 = 0x03 COMMA + DW_CFA_advance_loc4 = 0x04 COMMA + DW_CFA_offset_extended = 0x05 COMMA + DW_CFA_restore_extended = 0x06 COMMA + DW_CFA_undefined = 0x07 COMMA + DW_CFA_same_value = 0x08 COMMA + DW_CFA_register = 0x09 COMMA + DW_CFA_remember_state = 0x0a COMMA + DW_CFA_restore_state = 0x0b COMMA + DW_CFA_def_cfa = 0x0c COMMA + DW_CFA_def_cfa_register = 0x0d COMMA + DW_CFA_def_cfa_offset = 0x0e COMMA + + /* DWARF 3. */ + DW_CFA_def_cfa_expression = 0x0f COMMA + DW_CFA_expression = 0x10 COMMA + DW_CFA_offset_extended_sf = 0x11 COMMA + DW_CFA_def_cfa_sf = 0x12 COMMA + DW_CFA_def_cfa_offset_sf = 0x13 COMMA + + /* SGI/MIPS specific. */ + DW_CFA_MIPS_advance_loc8 = 0x1d COMMA + + /* GNU extensions. */ + DW_CFA_GNU_window_save = 0x2d COMMA + DW_CFA_GNU_args_size = 0x2e COMMA + DW_CFA_GNU_negative_offset_extended = 0x2f +IF_NOT_ASM(};) + +#define DW_CIE_ID 0xffffffff +#define DW_CIE_VERSION 1 + +#define DW_CFA_extended 0 +#define DW_CFA_lo_user 0x1c +#define DW_CFA_hi_user 0x3f + +#define DW_CHILDREN_no 0x00 +#define DW_CHILDREN_yes 0x01 + +#define DW_ADDR_none 0 + +/* Source language names and codes. */ +ENUM(dwarf_source_language) + + DW_LANG_C89 = 0x0001 COMMA + DW_LANG_C = 0x0002 COMMA + DW_LANG_Ada83 = 0x0003 COMMA + DW_LANG_C_plus_plus = 0x0004 COMMA + DW_LANG_Cobol74 = 0x0005 COMMA + DW_LANG_Cobol85 = 0x0006 COMMA + DW_LANG_Fortran77 = 0x0007 COMMA + DW_LANG_Fortran90 = 0x0008 COMMA + DW_LANG_Pascal83 = 0x0009 COMMA + DW_LANG_Modula2 = 0x000a COMMA + DW_LANG_Java = 0x000b COMMA + /* DWARF 3. */ + DW_LANG_C99 = 0x000c COMMA + DW_LANG_Ada95 = 0x000d COMMA + DW_LANG_Fortran95 = 0x000e COMMA + /* MIPS. */ + DW_LANG_Mips_Assembler = 0x8001 COMMA + /* UPC. */ + DW_LANG_Upc = 0x8765 +IF_NOT_ASM(};) + +#define DW_LANG_lo_user 0x8000 /* Implementation-defined range start. */ +#define DW_LANG_hi_user 0xffff /* Implementation-defined range start. */ + +/* Names and codes for macro information. */ +ENUM(dwarf_macinfo_record_type) + + DW_MACINFO_define = 1 COMMA + DW_MACINFO_undef = 2 COMMA + DW_MACINFO_start_file = 3 COMMA + DW_MACINFO_end_file = 4 COMMA + DW_MACINFO_vendor_ext = 255 +IF_NOT_ASM(};) + +/* @@@ For use with GNU frame unwind information. */ + +#define DW_EH_PE_absptr 0x00 +#define DW_EH_PE_omit 0xff + +#define DW_EH_PE_uleb128 0x01 +#define DW_EH_PE_udata2 0x02 +#define DW_EH_PE_udata4 0x03 +#define DW_EH_PE_udata8 0x04 +#define DW_EH_PE_sleb128 0x09 +#define DW_EH_PE_sdata2 0x0A +#define DW_EH_PE_sdata4 0x0B +#define DW_EH_PE_sdata8 0x0C +#define DW_EH_PE_signed 0x08 + +#define DW_EH_PE_pcrel 0x10 +#define DW_EH_PE_textrel 0x20 +#define DW_EH_PE_datarel 0x30 +#define DW_EH_PE_funcrel 0x40 +#define DW_EH_PE_aligned 0x50 + +#define DW_EH_PE_indirect 0x80 + +#endif /* _ELF_DWARF2_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/linux/dwarf2-lang.h 2004-09-03 00:56:35.787720088 -0700 @@ -0,0 +1,132 @@ +#ifndef DWARF2_LANG +#define DWARF2_LANG +#include + +/* + * This is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2, or (at your option) any later + * version. + */ +/* + * This file defines macros that allow generation of DWARF debug records + * for asm files. This file is platform independent. Register numbers + * (which are about the only thing that is platform dependent) are to be + * supplied by a platform defined file. + */ +#define DWARF_preamble() .section .debug_frame,"",@progbits +/* + * This macro starts a debug frame section. The debug_frame describes + * where to find the registers that the enclosing function saved on + * entry. + * + * ORD is use by the label generator and should be the same as what is + * passed to CFI_postamble. + * + * pc, pc register gdb ordinal. + * + * code_align this is the factor used to define locations or regions + * where the given definitions apply. If you use labels to define these + * this should be 1. + * + * data_align this is the factor used to define register offsets. If + * you use struct offset, this should be the size of the register in + * bytes or the negative of that. This is how it is used: you will + * define a register as the reference register, say the stack pointer, + * then you will say where a register is located relative to this + * reference registers value, say 40 for register 3 (the gdb register + * number). The <40> will be multiplied by to define the + * byte offset of the given register (3, in this example). So if your + * <40> is the byte offset and the reference register points at the + * begining, you would want 1 for the data_offset. If <40> was the 40th + * 4-byte element in that structure you would want 4. And if your + * reference register points at the end of the structure you would want + * a negative data_align value(and you would have to do other math as + * well). + */ + +#define CFI_preamble(ORD, pc, code_align, data_align) \ +.section .debug_frame,"",@progbits ; \ +frame/**/_/**/ORD: \ + .long end/**/_/**/ORD-start/**/_/**/ORD; \ +start/**/_/**/ORD: \ + .long DW_CIE_ID; \ + .byte DW_CIE_VERSION; \ + .byte 0 ; \ + .uleb128 code_align; \ + .sleb128 data_align; \ + .byte pc; + +/* + * After the above macro and prior to the CFI_postamble, you need to + * define the initial state. This starts with defining the reference + * register and, usually the pc. Here are some helper macros: + */ + +#define CFA_define_reference(reg, offset) \ + .byte DW_CFA_def_cfa; \ + .uleb128 reg; \ + .uleb128 (offset); + +#define CFA_define_offset(reg, offset) \ + .byte (DW_CFA_offset + reg); \ + .uleb128 (offset); + +#define CFI_postamble(ORD) \ + .align 4; \ +end/**/_/**/ORD: +/* + * So now your code pushs stuff on the stack, you need a new location + * and the rules for what to do. This starts a running description of + * the call frame. You need to describe what changes with respect to + * the call registers as the location of the pc moves through the code. + * The following builds an FDE (fram descriptor entry?). Like the + * above, it has a preamble and a postamble. It also is tied to the CFI + * above. + * The first entry after the preamble must be the location in the code + * that the call frame is being described for. + */ +#define FDE_preamble(ORD, fde_no, initial_address, length) \ + .long FDE_end/**/_/**/fde_no-FDE_start/**/_/**/fde_no; \ +FDE_start/**/_/**/fde_no: \ + .long frame/**/_/**/ORD; \ + .long initial_address; \ + .long length; + +#define FDE_postamble(fde_no) \ + .align 4; \ +FDE_end/**/_/**/fde_no: +/* + * That done, you can now add registers, subtract registers, move the + * reference and even change the reference. You can also define a new + * area of code the info applies to. For discontinuous bits you should + * start a new FDE. You may have as many as you like. + */ + +/* + * To advance the address by + */ + +#define FDE_advance(bytes) \ + .byte DW_CFA_advance_loc4 \ + .long bytes + + + +/* + * With the above you can define all the register locations. But + * suppose the reference register moves... Takes the new offset NOT an + * increment. This is how esp is tracked if it is not saved. + */ + +#define CFA_define_cfa_offset(offset) \ + .byte $DW_CFA_def_cfa_offset; \ + .uleb128 (offset); +/* + * Or suppose you want to use a different reference register... + */ +#define CFA_define_cfa_register(reg) \ + .byte DW_CFA_def_cfa_register; \ + .uleb128 reg; + +#endif --- linux-2.6.9-rc1/include/linux/errno.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/include/linux/errno.h 2004-09-02 20:51:52.000000000 -0700 @@ -22,6 +22,7 @@ #define EBADTYPE 527 /* Type not supported by server */ #define EJUKEBOX 528 /* Request initiated, but will not complete before timeout */ #define EIOCBQUEUED 529 /* iocb queued, will get completion event */ +#define EIOCBRETRY 530 /* iocb queued, will trigger a retry */ #endif --- linux-2.6.9-rc1/include/linux/ext3_fs.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/include/linux/ext3_fs.h 2004-09-03 00:56:47.092001576 -0700 @@ -33,11 +33,10 @@ struct statfs; #undef EXT3FS_DEBUG /* - * Define EXT3_PREALLOCATE to preallocate data blocks for expanding files + * Define EXT3_RESERVATION to reserve data blocks for expanding files */ -#undef EXT3_PREALLOCATE /* @@@ Fix this! */ -#define EXT3_DEFAULT_PREALLOC_BLOCKS 8 - +#define EXT3_DEFAULT_RESERVE_BLOCKS 8 +#define EXT3_MAX_RESERVE_BLOCKS 1024 /* * Always enable hashed directories */ @@ -195,6 +194,32 @@ struct ext3_group_desc */ #define EXT3_STATE_JDATA 0x00000001 /* journaled data exists */ #define EXT3_STATE_NEW 0x00000002 /* inode is newly created */ +#define EXT3_STATE_RESIZE 0x00000004 /* fake inode for resizing */ + + +/* Used to pass group descriptor data when online resize is done */ +struct ext3_new_group_input { + __u32 group; /* Group number for this data */ + __u32 block_bitmap; /* Absolute block number of block bitmap */ + __u32 inode_bitmap; /* Absolute block number of inode bitmap */ + __u32 inode_table; /* Absolute block number of inode table start */ + __u32 blocks_count; /* Total number of blocks in this group */ + __u16 reserved_blocks; /* Number of reserved blocks in this group */ + __u16 unused; +}; + +/* The struct ext3_new_group_input in kernel space, with free_blocks_count */ +struct ext3_new_group_data { + __u32 group; + __u32 block_bitmap; + __u32 inode_bitmap; + __u32 inode_table; + __u32 blocks_count; + __u16 reserved_blocks; + __u16 unused; + __u32 free_blocks_count; +}; + /* * ioctl commands @@ -203,11 +228,15 @@ struct ext3_group_desc #define EXT3_IOC_SETFLAGS _IOW('f', 2, long) #define EXT3_IOC_GETVERSION _IOR('f', 3, long) #define EXT3_IOC_SETVERSION _IOW('f', 4, long) +#define EXT3_IOC_GROUP_EXTEND _IOW('f', 7, unsigned long) +#define EXT3_IOC_GROUP_ADD _IOW('f', 8,struct ext3_new_group_input) #define EXT3_IOC_GETVERSION_OLD _IOR('v', 1, long) #define EXT3_IOC_SETVERSION_OLD _IOW('v', 2, long) #ifdef CONFIG_JBD_DEBUG #define EXT3_IOC_WAIT_FOR_READONLY _IOR('f', 99, long) #endif +#define EXT3_IOC_GETRSVSZ _IOR('r', 1, long) +#define EXT3_IOC_SETRSVSZ _IOW('r', 2, long) /* * Structure of an inode on the disk @@ -306,25 +335,26 @@ struct ext3_inode { /* * Mount flags */ -#define EXT3_MOUNT_CHECK 0x0001 /* Do mount-time checks */ -#define EXT3_MOUNT_OLDALLOC 0x0002 /* Don't use the new Orlov allocator */ -#define EXT3_MOUNT_GRPID 0x0004 /* Create files with directory's group */ -#define EXT3_MOUNT_DEBUG 0x0008 /* Some debugging messages */ -#define EXT3_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */ -#define EXT3_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */ -#define EXT3_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */ -#define EXT3_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */ -#define EXT3_MOUNT_NOLOAD 0x0100 /* Don't use existing journal*/ -#define EXT3_MOUNT_ABORT 0x0200 /* Fatal error detected */ -#define EXT3_MOUNT_DATA_FLAGS 0x0C00 /* Mode for data writes: */ - #define EXT3_MOUNT_JOURNAL_DATA 0x0400 /* Write data to journal */ - #define EXT3_MOUNT_ORDERED_DATA 0x0800 /* Flush data before commit */ - #define EXT3_MOUNT_WRITEBACK_DATA 0x0C00 /* No data ordering */ -#define EXT3_MOUNT_UPDATE_JOURNAL 0x1000 /* Update the journal format */ -#define EXT3_MOUNT_NO_UID32 0x2000 /* Disable 32-bit UIDs */ -#define EXT3_MOUNT_XATTR_USER 0x4000 /* Extended user attributes */ -#define EXT3_MOUNT_POSIX_ACL 0x8000 /* POSIX Access Control Lists */ -#define EXT3_MOUNT_BARRIER 0x10000 /* Use block barriers */ +#define EXT3_MOUNT_CHECK 0x00001 /* Do mount-time checks */ +#define EXT3_MOUNT_OLDALLOC 0x00002 /* Don't use the new Orlov allocator */ +#define EXT3_MOUNT_GRPID 0x00004 /* Create files with directory's group */ +#define EXT3_MOUNT_DEBUG 0x00008 /* Some debugging messages */ +#define EXT3_MOUNT_ERRORS_CONT 0x00010 /* Continue on errors */ +#define EXT3_MOUNT_ERRORS_RO 0x00020 /* Remount fs ro on errors */ +#define EXT3_MOUNT_ERRORS_PANIC 0x00040 /* Panic on errors */ +#define EXT3_MOUNT_MINIX_DF 0x00080 /* Mimics the Minix statfs */ +#define EXT3_MOUNT_NOLOAD 0x00100 /* Don't use existing journal*/ +#define EXT3_MOUNT_ABORT 0x00200 /* Fatal error detected */ +#define EXT3_MOUNT_DATA_FLAGS 0x00C00 /* Mode for data writes: */ +#define EXT3_MOUNT_JOURNAL_DATA 0x00400 /* Write data to journal */ +#define EXT3_MOUNT_ORDERED_DATA 0x00800 /* Flush data before commit */ +#define EXT3_MOUNT_WRITEBACK_DATA 0x00C00 /* No data ordering */ +#define EXT3_MOUNT_UPDATE_JOURNAL 0x01000 /* Update the journal format */ +#define EXT3_MOUNT_NO_UID32 0x02000 /* Disable 32-bit UIDs */ +#define EXT3_MOUNT_XATTR_USER 0x04000 /* Extended user attributes */ +#define EXT3_MOUNT_POSIX_ACL 0x08000 /* POSIX Access Control Lists */ +#define EXT3_MOUNT_RESERVATION 0x10000 /* Preallocation */ +#define EXT3_MOUNT_BARRIER 0x20000 /* Use block barriers */ /* Compatibility, for having both ext2_fs.h and ext3_fs.h included at once */ #ifndef _LINUX_EXT2_FS_H @@ -418,7 +448,7 @@ struct ext3_super_block { */ __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ - __u16 s_padding1; + __u16 s_reserved_gdt_blocks; /* Per group desc for online growth */ /* * Journaling support valid if EXT3_FEATURE_COMPAT_HAS_JOURNAL set. */ @@ -681,8 +711,7 @@ struct dir_private_info { /* balloc.c */ extern int ext3_bg_has_super(struct super_block *sb, int group); extern unsigned long ext3_bg_num_gdb(struct super_block *sb, int group); -extern int ext3_new_block (handle_t *, struct inode *, unsigned long, - __u32 *, __u32 *, int *); +extern int ext3_new_block (handle_t *, struct inode *, unsigned long, int *); extern void ext3_free_blocks (handle_t *, struct inode *, unsigned long, unsigned long); extern unsigned long ext3_count_free_blocks (struct super_block *); @@ -730,6 +759,7 @@ extern void ext3_put_inode (struct inode extern void ext3_delete_inode (struct inode *); extern int ext3_sync_inode (handle_t *, struct inode *); extern void ext3_discard_prealloc (struct inode *); +extern void ext3_discard_reservation (struct inode *); extern void ext3_dirty_inode(struct inode *); extern int ext3_change_inode_journal_flag(struct inode *, int); extern void ext3_truncate (struct inode *); @@ -746,6 +776,13 @@ extern int ext3_orphan_del(handle_t *, s extern int ext3_htree_fill_tree(struct file *dir_file, __u32 start_hash, __u32 start_minor_hash, __u32 *next_hash); +/* resize.c */ +extern int ext3_group_add(struct super_block *sb, + struct ext3_new_group_data *input); +extern int ext3_group_extend(struct super_block *sb, + struct ext3_super_block *es, + unsigned long n_blocks_count); + /* super.c */ extern void ext3_error (struct super_block *, const char *, const char *, ...) __attribute__ ((format (printf, 3, 4))); --- linux-2.6.9-rc1/include/linux/ext3_fs_i.h 2004-08-24 00:01:52.000000000 -0700 +++ 25/include/linux/ext3_fs_i.h 2004-09-03 00:56:43.803501504 -0700 @@ -18,8 +18,16 @@ #include +struct reserve_window { + struct list_head rsv_list; + __u32 rsv_start; + __u32 rsv_end; + atomic_t rsv_goal_size; + __u32 rsv_alloc_hit; +}; + /* - * second extended file system inode data in memory + * third extended file system inode data in memory */ struct ext3_inode_info { __u32 i_data[15]; @@ -57,10 +65,9 @@ struct ext3_inode_info { * allocation when we detect linearly ascending requests. */ __u32 i_next_alloc_goal; -#ifdef EXT3_PREALLOCATE - __u32 i_prealloc_block; - __u32 i_prealloc_count; -#endif + /* block reservation window */ + struct reserve_window i_rsv_window; + __u32 i_dir_start_lookup; #ifdef CONFIG_EXT3_FS_XATTR /* --- linux-2.6.9-rc1/include/linux/ext3_fs_sb.h 2004-08-24 00:03:32.000000000 -0700 +++ 25/include/linux/ext3_fs_sb.h 2004-09-03 00:56:43.803501504 -0700 @@ -59,6 +59,10 @@ struct ext3_sb_info { struct percpu_counter s_dirs_counter; struct blockgroup_lock s_blockgroup_lock; + /* head of the per fs reservation window tree */ + spinlock_t s_rsv_window_lock; + struct reserve_window s_rsv_window_head; + /* Journaling */ struct inode * s_journal_inode; struct journal_s * s_journal; --- linux-2.6.9-rc1/include/linux/fs.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/include/linux/fs.h 2004-09-03 00:57:11.861236080 -0700 @@ -334,15 +334,15 @@ struct backing_dev_info; struct address_space { struct inode *host; /* owner: inode, block_device */ struct radix_tree_root page_tree; /* radix tree of all pages */ - spinlock_t tree_lock; /* and spinlock protecting it */ - unsigned long nrpages; /* number of total pages */ - pgoff_t writeback_index;/* writeback starts here */ - struct address_space_operations *a_ops; /* methods */ - struct prio_tree_root i_mmap; /* tree of private mappings */ + rwlock_t tree_lock; /* and rwlock protecting it */ unsigned int i_mmap_writable;/* count VM_SHARED mappings */ + struct prio_tree_root i_mmap; /* tree of private mappings */ struct list_head i_mmap_nonlinear;/*list VM_NONLINEAR mappings */ spinlock_t i_mmap_lock; /* protect tree, count, list */ atomic_t truncate_count; /* Cover race condition with truncate */ + unsigned long nrpages; /* number of total pages */ + pgoff_t writeback_index;/* writeback starts here */ + struct address_space_operations *a_ops; /* methods */ unsigned long flags; /* error bits/gfp mask */ struct backing_dev_info *backing_dev_info; /* device readahead, etc */ spinlock_t private_lock; /* for use by the address_space */ @@ -379,10 +379,11 @@ struct block_device { /* * Radix-tree tags, for tagging dirty and writeback pages within the pagecache - * radix trees + * radix trees. One tag is for using for specific filesystem needs */ #define PAGECACHE_TAG_DIRTY 0 #define PAGECACHE_TAG_WRITEBACK 1 +#define PAGECACHE_TAG_FS_SPECIFIC 2 int mapping_tagged(struct address_space *mapping, int tag); @@ -420,6 +421,7 @@ static inline int mapping_writably_mappe struct inode { struct hlist_node i_hash; struct list_head i_list; + struct list_head i_sb_list; struct list_head i_dentry; unsigned long i_ino; atomic_t i_count; @@ -437,6 +439,7 @@ struct inode { unsigned long i_version; unsigned long i_blocks; unsigned short i_bytes; + unsigned char i_sock; spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */ struct semaphore i_sem; struct rw_semaphore i_alloc_sem; @@ -456,6 +459,8 @@ struct inode { struct cdev *i_cdev; int i_cindex; + __u32 i_generation; + unsigned long i_dnotify_mask; /* Directory notify events */ struct dnotify_struct *i_dnotify; /* for directory notifications */ @@ -463,11 +468,9 @@ struct inode { unsigned long dirtied_when; /* jiffies of first dirtying */ unsigned int i_flags; - unsigned char i_sock; atomic_t i_writecount; void *i_security; - __u32 i_generation; union { void *generic_ip; } u; @@ -694,6 +697,7 @@ extern int posix_lock_file_wait(struct f extern void posix_block_lock(struct file_lock *, struct file_lock *); extern void posix_unblock_lock(struct file *, struct file_lock *); extern int posix_locks_deadlock(struct file_lock *, struct file_lock *); +extern int flock_lock_file_wait(struct file *filp, struct file_lock *fl); extern int __break_lease(struct inode *inode, unsigned int flags); extern void lease_get_mtime(struct inode *, struct timespec *time); extern int lock_may_read(struct inode *, loff_t start, unsigned long count); @@ -731,6 +735,28 @@ extern int send_sigurg(struct fown_struc extern struct list_head super_blocks; extern spinlock_t sb_lock; +/* + * sb_entry is attached to some super block (register_sb_entry()). It + * can be detached manually (unregister_sb_entry()), or automatically by + * umount (see deactivate_super()). {get,put}_sb_entry() can be used to + * guarantee liveness of entry. Typical usage is to have sb_entry along + * with kobject or proc_dir_entry; see struct fs_kobject. + */ + +struct sb_entry { + /* linkage into &s->entries */ + struct list_head linkage; + /* true is entry is attached to @s */ + int live; + /* file system type for this entry. This is necessary, because + * entry liveness should be checkable in the situation when ->s + * may already be invalid. */ + struct file_system_type *type; + /* superblock this entry is attached to. Can only be inspected + * after successful call to get_sb_entry() */ + struct super_block *s; +}; + #define sb_entry(list) list_entry((list), struct super_block, s_list) #define S_BIAS (1<<30) struct super_block { @@ -757,6 +783,7 @@ struct super_block { atomic_t s_active; void *s_security; + struct list_head s_inodes; /* all inodes */ struct list_head s_dirty; /* dirty inodes */ struct list_head s_io; /* parked for writeback */ struct hlist_head s_anon; /* anonymous dentries for (nfs) exporting */ @@ -778,9 +805,54 @@ struct super_block { * even looking at it. You had been warned. */ struct semaphore s_vfs_rename_sem; /* Kludge */ + struct list_head s_entries; /* list of + * sb_entries. Protected + * by ->s_umount. */ +}; + +/* + * file system kobject. + * + * fs_kobject is used to export per-super-block information in sysfs + * while providing synchronization against concurrent umount. To this + * end it includes struct sb_entry that is attached to the super block + * by fs_kobject_register() and detached by fs_kobject_unregister(). + * + * We need these wrappers (see fs/super.c:fs_kattr_{show,store}()), + * because it's impossible to handle module unloading races properly + * from within file-system code. Viz get_sb_entry() avoids umount races + * by acquiring reference to the super block (through sget()), but this + * may very well be the _last_ reference to the file-system, and + * put_sb_entry() will trigger module unload, which means that + * put_sb_entry() should be called from the generic code rather than + * from file-system module. + */ +struct fs_kobject { + struct sb_entry entry; + struct kobject kobj; /* it should be kobj for compatibility + * with silly kobject.h macros */ }; /* + * file system kattr. See struct fs_kobject. + */ +struct fs_kattr { + struct attribute kattr; + ssize_t (*show)(struct super_block *sb, struct fs_kobject *fs_kobj, + struct fs_kattr *fs_kattr, char *buf); + ssize_t (*store)(struct super_block *sb, struct fs_kobject *fs_kobj, + struct fs_kattr *fs_kattr, const char *buf, + size_t size); +}; + +/* in fs/super.c */ + +int fs_kobject_register(struct super_block *s, struct fs_kobject * fskobj); +void fs_kobject_unregister(struct fs_kobject * fskobj); + +extern struct sysfs_ops fs_attr_ops; + +/* * Snapshotting support. */ enum { @@ -911,6 +983,7 @@ struct file_operations { unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); int (*dir_notify)(struct file *filp, unsigned long arg); + int (*flock) (struct file *, int, struct file_lock *); }; struct inode_operations { @@ -971,6 +1044,8 @@ struct super_operations { void (*clear_inode) (struct inode *); void (*umount_begin) (struct super_block *); + void (*sync_inodes) (struct super_block *sb, + struct writeback_control *wbc); int (*show_options)(struct seq_file *, struct vfsmount *); }; @@ -978,7 +1053,8 @@ struct super_operations { #define I_DIRTY_SYNC 1 /* Not dirty enough for O_DATASYNC */ #define I_DIRTY_DATASYNC 2 /* Data-related inode changes pending */ #define I_DIRTY_PAGES 4 /* Data-related inode changes pending */ -#define I_LOCK 8 +#define __I_LOCK 3 +#define I_LOCK (1 << __I_LOCK) #define I_FREEING 16 #define I_CLEAR 32 #define I_NEW 64 @@ -1372,6 +1448,7 @@ extern struct inode * igrab(struct inode extern ino_t iunique(struct super_block *, ino_t); extern int inode_needs_sync(struct inode *inode); extern void generic_delete_inode(struct inode *inode); +extern void generic_forget_inode(struct inode *inode); extern struct inode *ilookup5(struct super_block *sb, unsigned long hashval, int (*test)(struct inode *, void *), void *data); @@ -1571,6 +1648,39 @@ static inline ino_t parent_ino(struct de /* kernel/fork.c */ extern int unshare_files(void); +/* Transaction based IO helpers */ + +/* + * An argresp is stored in an allocated page and holds the + * size of the argument or response, along with its content + */ +struct simple_transaction_argresp { + ssize_t size; + char data[0]; +}; + +#define SIMPLE_TRANSACTION_LIMIT (PAGE_SIZE - sizeof(struct simple_transaction_argresp)) + +char *simple_transaction_get(struct file *file, const char __user *buf, + size_t size); +ssize_t simple_transaction_read(struct file *file, char __user *buf, + size_t size, loff_t *pos); +int simple_transaction_release(struct inode *inode, struct file *file); + +static inline void simple_transaction_set(struct file *file, size_t n) +{ + struct simple_transaction_argresp *ar = file->private_data; + + BUG_ON(n > SIMPLE_TRANSACTION_LIMIT); + + /* + * The barrier ensures that ar->size will really remain zero until + * ar->data is ready for reading. + */ + smp_mb(); + ar->size = n; +} + #ifdef CONFIG_SECURITY static inline char *alloc_secdata(void) { --- linux-2.6.9-rc1/include/linux/genhd.h 2004-08-24 00:01:55.000000000 -0700 +++ 25/include/linux/genhd.h 2004-09-02 20:51:52.000000000 -0700 @@ -100,7 +100,7 @@ struct gendisk { struct timer_rand_state *random; int policy; - unsigned sync_io; /* RAID */ + atomic_t sync_io; /* RAID */ unsigned long stamp, stamp_idle; int in_flight; #ifdef CONFIG_SMP --- linux-2.6.9-rc1/include/linux/gfp.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/include/linux/gfp.h 2004-09-03 00:57:03.610490384 -0700 @@ -73,6 +73,11 @@ struct vm_area_struct; * For the normal case of non-DISCONTIGMEM systems the NODE_DATA() gets * optimized to &contig_page_data at compile-time. */ + +#ifndef HAVE_ARCH_FREE_PAGE +static inline void arch_free_page(struct page *page, int order) { } +#endif + extern struct page * FASTCALL(__alloc_pages(unsigned int, unsigned int, struct zonelist *)); @@ -125,4 +130,8 @@ extern void FASTCALL(free_cold_page(stru void page_alloc_init(void); +int perthread_pages_reserve(int nrpages, int gfp); +void perthread_pages_release(int nrpages); +int perthread_pages_count(void); + #endif /* __LINUX_GFP_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/linux/ghash.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,236 @@ +/* + * include/linux/ghash.h -- generic hashing with fuzzy retrieval + * + * (C) 1997 Thomas Schoebel-Theuer + * + * The algorithms implemented here seem to be a completely new invention, + * and I'll publish the fundamentals in a paper. + */ + +#ifndef _GHASH_H +#define _GHASH_H +/* HASHSIZE _must_ be a power of two!!! */ + + +#define DEF_HASH_FUZZY_STRUCTS(NAME,HASHSIZE,TYPE) \ +\ +struct NAME##_table {\ + TYPE * hashtable[HASHSIZE];\ + TYPE * sorted_list;\ + int nr_entries;\ +};\ +\ +struct NAME##_ptrs {\ + TYPE * next_hash;\ + TYPE * prev_hash;\ + TYPE * next_sorted;\ + TYPE * prev_sorted;\ +}; + +#define DEF_HASH_FUZZY(LINKAGE,NAME,HASHSIZE,TYPE,PTRS,KEYTYPE,KEY,KEYCMP,KEYEQ,HASHFN)\ +\ +LINKAGE void insert_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\ +{\ + int ix = HASHFN(elem->KEY);\ + TYPE ** base = &tbl->hashtable[ix];\ + TYPE * ptr = *base;\ + TYPE * prev = NULL;\ +\ + tbl->nr_entries++;\ + while(ptr && KEYCMP(ptr->KEY, elem->KEY)) {\ + base = &ptr->PTRS.next_hash;\ + prev = ptr;\ + ptr = *base;\ + }\ + elem->PTRS.next_hash = ptr;\ + elem->PTRS.prev_hash = prev;\ + if(ptr) {\ + ptr->PTRS.prev_hash = elem;\ + }\ + *base = elem;\ +\ + ptr = prev;\ + if(!ptr) {\ + ptr = tbl->sorted_list;\ + prev = NULL;\ + } else {\ + prev = ptr->PTRS.prev_sorted;\ + }\ + while(ptr) {\ + TYPE * next = ptr->PTRS.next_hash;\ + if(next && KEYCMP(next->KEY, elem->KEY)) {\ + prev = ptr;\ + ptr = next;\ + } else if(KEYCMP(ptr->KEY, elem->KEY)) {\ + prev = ptr;\ + ptr = ptr->PTRS.next_sorted;\ + } else\ + break;\ + }\ + elem->PTRS.next_sorted = ptr;\ + elem->PTRS.prev_sorted = prev;\ + if(ptr) {\ + ptr->PTRS.prev_sorted = elem;\ + }\ + if(prev) {\ + prev->PTRS.next_sorted = elem;\ + } else {\ + tbl->sorted_list = elem;\ + }\ +}\ +\ +LINKAGE void remove_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\ +{\ + TYPE * next = elem->PTRS.next_hash;\ + TYPE * prev = elem->PTRS.prev_hash;\ +\ + tbl->nr_entries--;\ + if(next)\ + next->PTRS.prev_hash = prev;\ + if(prev)\ + prev->PTRS.next_hash = next;\ + else {\ + int ix = HASHFN(elem->KEY);\ + tbl->hashtable[ix] = next;\ + }\ +\ + next = elem->PTRS.next_sorted;\ + prev = elem->PTRS.prev_sorted;\ + if(next)\ + next->PTRS.prev_sorted = prev;\ + if(prev)\ + prev->PTRS.next_sorted = next;\ + else\ + tbl->sorted_list = next;\ +}\ +\ +LINKAGE TYPE * find_##NAME##_hash(struct NAME##_table * tbl, KEYTYPE pos)\ +{\ + int ix = hashfn(pos);\ + TYPE * ptr = tbl->hashtable[ix];\ + while(ptr && KEYCMP(ptr->KEY, pos))\ + ptr = ptr->PTRS.next_hash;\ + if(ptr && !KEYEQ(ptr->KEY, pos))\ + ptr = NULL;\ + return ptr;\ +}\ +\ +LINKAGE TYPE * find_##NAME##_hash_fuzzy(struct NAME##_table * tbl, KEYTYPE pos)\ +{\ + int ix;\ + int offset;\ + TYPE * ptr;\ + TYPE * next;\ +\ + ptr = tbl->sorted_list;\ + if(!ptr || KEYCMP(pos, ptr->KEY))\ + return NULL;\ + ix = HASHFN(pos);\ + offset = HASHSIZE;\ + do {\ + offset >>= 1;\ + next = tbl->hashtable[(ix+offset) & ((HASHSIZE)-1)];\ + if(next && (KEYCMP(next->KEY, pos) || KEYEQ(next->KEY, pos))\ + && KEYCMP(ptr->KEY, next->KEY))\ + ptr = next;\ + } while(offset);\ +\ + for(;;) {\ + next = ptr->PTRS.next_hash;\ + if(next) {\ + if(KEYCMP(next->KEY, pos)) {\ + ptr = next;\ + continue;\ + }\ + }\ + next = ptr->PTRS.next_sorted;\ + if(next && KEYCMP(next->KEY, pos)) {\ + ptr = next;\ + continue;\ + }\ + return ptr;\ + }\ + return NULL;\ +} + +/* LINKAGE - empty or "static", depending on whether you want the definitions to + * be public or not + * NAME - a string to stick in names to make this hash table type distinct from + * any others + * HASHSIZE - number of buckets + * TYPE - type of data contained in the buckets - must be a structure, one + * field is of type NAME_ptrs, another is the hash key + * PTRS - TYPE must contain a field of type NAME_ptrs, PTRS is the name of that + * field + * KEYTYPE - type of the key field within TYPE + * KEY - name of the key field within TYPE + * KEYCMP - pointer to function that compares KEYTYPEs to each other - the + * prototype is int KEYCMP(KEYTYPE, KEYTYPE), it returns zero for equal, + * non-zero for not equal + * HASHFN - the hash function - the prototype is int HASHFN(KEYTYPE), + * it returns a number in the range 0 ... HASHSIZE - 1 + * Call DEF_HASH_STRUCTS, define your hash table as a NAME_table, then call + * DEF_HASH. + */ + +#define DEF_HASH_STRUCTS(NAME,HASHSIZE,TYPE) \ +\ +struct NAME##_table {\ + TYPE * hashtable[HASHSIZE];\ + int nr_entries;\ +};\ +\ +struct NAME##_ptrs {\ + TYPE * next_hash;\ + TYPE * prev_hash;\ +}; + +#define DEF_HASH(LINKAGE,NAME,TYPE,PTRS,KEYTYPE,KEY,KEYCMP,HASHFN)\ +\ +LINKAGE void insert_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\ +{\ + int ix = HASHFN(elem->KEY);\ + TYPE ** base = &tbl->hashtable[ix];\ + TYPE * ptr = *base;\ + TYPE * prev = NULL;\ +\ + tbl->nr_entries++;\ + while(ptr && KEYCMP(ptr->KEY, elem->KEY)) {\ + base = &ptr->PTRS.next_hash;\ + prev = ptr;\ + ptr = *base;\ + }\ + elem->PTRS.next_hash = ptr;\ + elem->PTRS.prev_hash = prev;\ + if(ptr) {\ + ptr->PTRS.prev_hash = elem;\ + }\ + *base = elem;\ +}\ +\ +LINKAGE void remove_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\ +{\ + TYPE * next = elem->PTRS.next_hash;\ + TYPE * prev = elem->PTRS.prev_hash;\ +\ + tbl->nr_entries--;\ + if(next)\ + next->PTRS.prev_hash = prev;\ + if(prev)\ + prev->PTRS.next_hash = next;\ + else {\ + int ix = HASHFN(elem->KEY);\ + tbl->hashtable[ix] = next;\ + }\ +}\ +\ +LINKAGE TYPE * find_##NAME##_hash(struct NAME##_table * tbl, KEYTYPE pos)\ +{\ + int ix = HASHFN(pos);\ + TYPE * ptr = tbl->hashtable[ix];\ + while(ptr && KEYCMP(ptr->KEY, pos))\ + ptr = ptr->PTRS.next_hash;\ + return ptr;\ +} + +#endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/linux/hardirq.h 2004-09-03 00:56:41.029923152 -0700 @@ -0,0 +1,51 @@ +#ifndef LINUX_HARDIRQ_H +#define LINUX_HARDIRQ_H + +#include +#ifdef CONFIG_PREEPT +#include +#endif +#include + +#define __IRQ_MASK(x) ((1UL << (x))-1) + +#define PREEMPT_MASK (__IRQ_MASK(PREEMPT_BITS) << PREEMPT_SHIFT) +#define HARDIRQ_MASK (__IRQ_MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) +#define SOFTIRQ_MASK (__IRQ_MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) + +#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) +#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) +#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) + +#define hardirq_count() (preempt_count() & HARDIRQ_MASK) +#define softirq_count() (preempt_count() & SOFTIRQ_MASK) +#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) + +/* + * Are we doing bottom half or hardware interrupt processing? + * Are we in a softirq context? Interrupt context? + */ +#define in_irq() (hardirq_count()) +#define in_softirq() (softirq_count()) +#define in_interrupt() (irq_count()) + +#define hardirq_trylock() (!in_interrupt()) +#define hardirq_endlock() do { } while (0) + +#ifdef CONFIG_PREEMPT +# define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != kernel_locked()) +# define preemptible() (preempt_count() == 0 && !irqs_disabled()) +# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1) +#else +# define in_atomic() (preempt_count() != 0) +# define preemptible() 0 +# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET +#endif + +#ifdef CONFIG_SMP +extern void synchronize_irq(unsigned int irq); +#else +# define synchronize_irq(irq) barrier() +#endif + +#endif /* LINUX_HARDIRQ_H */ --- linux-2.6.9-rc1/include/linux/hdreg.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/include/linux/hdreg.h 2004-09-02 20:51:52.000000000 -0700 @@ -449,9 +449,8 @@ enum { /* hd/ide ctl's that pass (arg) ptrs to user space are numbered 0x033n/0x033n */ /* 0x330 is reserved - used to be HDIO_GETGEO_BIG */ /* 0x331 is reserved - used to be HDIO_GETGEO_BIG_RAW */ - -#define HDIO_SET_IDE_SCSI 0x0338 -#define HDIO_SET_SCSI_IDE 0x0339 +/* 0x338 is reserved - used to be HDIO_SET_IDE_SCSI */ +/* 0x339 is reserved - used to be HDIO_SET_SCSI_IDE */ #define __NEW_HD_DRIVE_ID --- linux-2.6.9-rc1/include/linux/hiddev.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/include/linux/hiddev.h 2004-09-03 00:56:31.593357728 -0700 @@ -128,10 +128,11 @@ struct hiddev_usage_ref { /* hiddev_usage_ref_multi is used for sending multiple bytes to a control. * It really manifests itself as setting the value of consecutive usages */ +#define HID_MAX_MULTI_USAGES 1024 struct hiddev_usage_ref_multi { struct hiddev_usage_ref uref; __u32 num_values; - __s32 values[HID_MAX_USAGES]; + __s32 values[HID_MAX_MULTI_USAGES]; }; /* FIELD_INDEX_NONE is returned in read() data from the kernel when flags @@ -212,6 +213,11 @@ struct hiddev_usage_ref_multi { * In-kernel definitions. */ +struct hid_device; +struct hid_usage; +struct hid_field; +struct hid_report; + #ifdef CONFIG_USB_HIDDEV int hiddev_connect(struct hid_device *); void hiddev_disconnect(struct hid_device *); --- linux-2.6.9-rc1/include/linux/highuid.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/include/linux/highuid.h 2004-09-02 20:51:52.000000000 -0700 @@ -44,8 +44,8 @@ extern void __bad_gid(void); #ifdef CONFIG_UID16 /* prevent uid mod 65536 effect by returning a default value for high UIDs */ -#define high2lowuid(uid) ((uid) > 65535 ? (old_uid_t)overflowuid : (old_uid_t)(uid)) -#define high2lowgid(gid) ((gid) > 65535 ? (old_gid_t)overflowgid : (old_gid_t)(gid)) +#define high2lowuid(uid) ((uid) & ~0xFFFF ? (old_uid_t)overflowuid : (old_uid_t)(uid)) +#define high2lowgid(gid) ((gid) & ~0xFFFF ? (old_gid_t)overflowgid : (old_gid_t)(gid)) /* * -1 is different in 16 bits than it is in 32 bits * these macros are used by chown(), setreuid(), ..., @@ -89,8 +89,8 @@ extern int fs_overflowgid; * Since these macros are used in architectures that only need limited * 16-bit UID back compatibility, we won't use old_uid_t and old_gid_t */ -#define fs_high2lowuid(uid) ((uid) > 65535 ? (uid16_t)fs_overflowuid : (uid16_t)(uid)) -#define fs_high2lowgid(gid) ((gid) > 65535 ? (gid16_t)fs_overflowgid : (gid16_t)(gid)) +#define fs_high2lowuid(uid) ((uid) & ~0xFFFF ? (uid16_t)fs_overflowuid : (uid16_t)(uid)) +#define fs_high2lowgid(gid) ((gid) & ~0xFFFF ? (gid16_t)fs_overflowgid : (gid16_t)(gid)) #define low_16_bits(x) ((x) & 0xFFFF) #define high_16_bits(x) (((x) & 0xFFFF0000) >> 16) --- linux-2.6.9-rc1/include/linux/i2o-dev.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/include/linux/i2o-dev.h 2004-09-02 20:51:52.000000000 -0700 @@ -1,13 +1,13 @@ /* * I2O user space accessible structures/APIs - * + * * (c) Copyright 1999, 2000 Red Hat Software * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * ************************************************************************* * * This header file defines the I2O APIs that are available to both @@ -23,7 +23,7 @@ /* How many controllers are we allowing */ #define MAX_I2O_CONTROLLERS 32 -#include +//#include /* * I2O Control IOCTLs and structures @@ -42,22 +42,25 @@ #define I2OEVTREG _IOW(I2O_MAGIC_NUMBER,10,struct i2o_evt_id) #define I2OEVTGET _IOR(I2O_MAGIC_NUMBER,11,struct i2o_evt_info) #define I2OPASSTHRU _IOR(I2O_MAGIC_NUMBER,12,struct i2o_cmd_passthru) +#define I2OPASSTHRU32 _IOR(I2O_MAGIC_NUMBER,12,struct i2o_cmd_passthru32) + +struct i2o_cmd_passthru32 { + unsigned int iop; /* IOP unit number */ + u32 msg; /* message */ +}; -struct i2o_cmd_passthru -{ +struct i2o_cmd_passthru { unsigned int iop; /* IOP unit number */ void __user *msg; /* message */ }; -struct i2o_cmd_hrtlct -{ +struct i2o_cmd_hrtlct { unsigned int iop; /* IOP unit number */ void __user *resbuf; /* Buffer for result */ unsigned int __user *reslen; /* Buffer length in bytes */ }; -struct i2o_cmd_psetget -{ +struct i2o_cmd_psetget { unsigned int iop; /* IOP unit number */ unsigned int tid; /* Target device TID */ void __user *opbuf; /* Operation List buffer */ @@ -66,8 +69,7 @@ struct i2o_cmd_psetget unsigned int __user *reslen; /* Result List buffer length in bytes */ }; -struct i2o_sw_xfer -{ +struct i2o_sw_xfer { unsigned int iop; /* IOP unit number */ unsigned char flags; /* Flags field */ unsigned char sw_type; /* Software type */ @@ -78,21 +80,19 @@ struct i2o_sw_xfer unsigned int __user *curfrag; /* Current fragment count */ }; -struct i2o_html -{ +struct i2o_html { unsigned int iop; /* IOP unit number */ unsigned int tid; /* Target device ID */ unsigned int page; /* HTML page */ - void __user *resbuf; /* Buffer for reply HTML page */ + void __user *resbuf; /* Buffer for reply HTML page */ unsigned int __user *reslen; /* Length in bytes of reply buffer */ - void __user *qbuf; /* Pointer to HTTP query string */ + void __user *qbuf; /* Pointer to HTTP query string */ unsigned int qlen; /* Length in bytes of query string buffer */ }; #define I2O_EVT_Q_LEN 32 -struct i2o_evt_id -{ +struct i2o_evt_id { unsigned int iop; unsigned int tid; unsigned int evt_mask; @@ -101,21 +101,18 @@ struct i2o_evt_id /* Event data size = frame size - message header + evt indicator */ #define I2O_EVT_DATA_SIZE 88 -struct i2o_evt_info -{ +struct i2o_evt_info { struct i2o_evt_id id; unsigned char evt_data[I2O_EVT_DATA_SIZE]; unsigned int data_size; }; -struct i2o_evt_get -{ +struct i2o_evt_get { struct i2o_evt_info info; int pending; int lost; }; - /************************************************************************** * HRT related constants and structures **************************************************************************/ @@ -135,139 +132,127 @@ typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; -#endif /* __KERNEL__ */ +#endif /* __KERNEL__ */ -typedef struct _i2o_pci_bus -{ - u8 PciFunctionNumber; - u8 PciDeviceNumber; - u8 PciBusNumber; - u8 reserved; - u16 PciVendorID; - u16 PciDeviceID; +typedef struct _i2o_pci_bus { + u8 PciFunctionNumber; + u8 PciDeviceNumber; + u8 PciBusNumber; + u8 reserved; + u16 PciVendorID; + u16 PciDeviceID; } i2o_pci_bus; -typedef struct _i2o_local_bus -{ - u16 LbBaseIOPort; - u16 reserved; - u32 LbBaseMemoryAddress; +typedef struct _i2o_local_bus { + u16 LbBaseIOPort; + u16 reserved; + u32 LbBaseMemoryAddress; } i2o_local_bus; -typedef struct _i2o_isa_bus -{ - u16 IsaBaseIOPort; - u8 CSN; - u8 reserved; - u32 IsaBaseMemoryAddress; +typedef struct _i2o_isa_bus { + u16 IsaBaseIOPort; + u8 CSN; + u8 reserved; + u32 IsaBaseMemoryAddress; } i2o_isa_bus; -typedef struct _i2o_eisa_bus_info -{ - u16 EisaBaseIOPort; - u8 reserved; - u8 EisaSlotNumber; - u32 EisaBaseMemoryAddress; +typedef struct _i2o_eisa_bus_info { + u16 EisaBaseIOPort; + u8 reserved; + u8 EisaSlotNumber; + u32 EisaBaseMemoryAddress; } i2o_eisa_bus; -typedef struct _i2o_mca_bus -{ - u16 McaBaseIOPort; - u8 reserved; - u8 McaSlotNumber; - u32 McaBaseMemoryAddress; +typedef struct _i2o_mca_bus { + u16 McaBaseIOPort; + u8 reserved; + u8 McaSlotNumber; + u32 McaBaseMemoryAddress; } i2o_mca_bus; -typedef struct _i2o_other_bus -{ +typedef struct _i2o_other_bus { u16 BaseIOPort; u16 reserved; u32 BaseMemoryAddress; } i2o_other_bus; -typedef struct _i2o_hrt_entry -{ - u32 adapter_id; - u32 parent_tid:12; - u32 state:4; - u32 bus_num:8; - u32 bus_type:8; - union - { - i2o_pci_bus pci_bus; - i2o_local_bus local_bus; - i2o_isa_bus isa_bus; - i2o_eisa_bus eisa_bus; - i2o_mca_bus mca_bus; - i2o_other_bus other_bus; +typedef struct _i2o_hrt_entry { + u32 adapter_id; + u32 parent_tid:12; + u32 state:4; + u32 bus_num:8; + u32 bus_type:8; + union { + i2o_pci_bus pci_bus; + i2o_local_bus local_bus; + i2o_isa_bus isa_bus; + i2o_eisa_bus eisa_bus; + i2o_mca_bus mca_bus; + i2o_other_bus other_bus; } bus; } i2o_hrt_entry; -typedef struct _i2o_hrt -{ - u16 num_entries; - u8 entry_len; - u8 hrt_version; - u32 change_ind; +typedef struct _i2o_hrt { + u16 num_entries; + u8 entry_len; + u8 hrt_version; + u32 change_ind; i2o_hrt_entry hrt_entry[1]; } i2o_hrt; -typedef struct _i2o_lct_entry -{ - u32 entry_size:16; - u32 tid:12; - u32 reserved:4; - u32 change_ind; - u32 device_flags; - u32 class_id:12; - u32 version:4; - u32 vendor_id:16; - u32 sub_class; - u32 user_tid:12; - u32 parent_tid:12; - u32 bios_info:8; - u8 identity_tag[8]; - u32 event_capabilities; +typedef struct _i2o_lct_entry { + u32 entry_size:16; + u32 tid:12; + u32 reserved:4; + u32 change_ind; + u32 device_flags; + u32 class_id:12; + u32 version:4; + u32 vendor_id:16; + u32 sub_class; + u32 user_tid:12; + u32 parent_tid:12; + u32 bios_info:8; + u8 identity_tag[8]; + u32 event_capabilities; } i2o_lct_entry; -typedef struct _i2o_lct -{ - u32 table_size:16; - u32 boot_tid:12; - u32 lct_ver:4; - u32 iop_flags; - u32 change_ind; +typedef struct _i2o_lct { + u32 table_size:16; + u32 boot_tid:12; + u32 lct_ver:4; + u32 iop_flags; + u32 change_ind; i2o_lct_entry lct_entry[1]; } i2o_lct; -typedef struct _i2o_status_block -{ - u16 org_id; - u16 reserved; - u16 iop_id:12; - u16 reserved1:4; - u16 host_unit_id; - u16 segment_number:12; - u16 i2o_version:4; - u8 iop_state; - u8 msg_type; - u16 inbound_frame_size; - u8 init_code; - u8 reserved2; - u32 max_inbound_frames; - u32 cur_inbound_frames; - u32 max_outbound_frames; - char product_id[24]; - u32 expected_lct_size; - u32 iop_capabilities; - u32 desired_mem_size; - u32 current_mem_size; - u32 current_mem_base; - u32 desired_io_size; - u32 current_io_size; - u32 current_io_base; - u32 reserved3:24; - u32 cmd_status:8; +typedef struct _i2o_status_block { + u16 org_id; + u16 reserved; + u16 iop_id:12; + u16 reserved1:4; + u16 host_unit_id; + u16 segment_number:12; + u16 i2o_version:4; + u8 iop_state; + u8 msg_type; + u16 inbound_frame_size; + u8 init_code; + u8 reserved2; + u32 max_inbound_frames; + u32 cur_inbound_frames; + u32 max_outbound_frames; + char product_id[24]; + u32 expected_lct_size; + u32 iop_capabilities; + u32 desired_mem_size; + u32 current_mem_size; + u32 current_mem_base; + u32 desired_io_size; + u32 current_io_size; + u32 current_io_base; + u32 reserved3:24; + u32 cmd_status:8; } i2o_status_block; /* Event indicator mask flags */ @@ -351,14 +336,15 @@ typedef struct _i2o_status_block #define I2O_CLASS_BUS_ADAPTER_PORT 0x080 #define I2O_CLASS_PEER_TRANSPORT_AGENT 0x090 #define I2O_CLASS_PEER_TRANSPORT 0x091 +#define I2O_CLASS_END 0xfff -/* +/* * Rest of 0x092 - 0x09f reserved for peer-to-peer classes */ #define I2O_CLASS_MATCH_ANYCLASS 0xffffffff -/* +/* * Subclasses */ @@ -380,7 +366,7 @@ typedef struct _i2o_status_block #define I2O_PARAMS_TABLE_CLEAR 0x000A /* - * I2O serial number conventions / formats + * I2O serial number conventions / formats * (circa v1.5) */ @@ -391,7 +377,7 @@ typedef struct _i2o_status_block #define I2O_SNFORMAT_LAN48_MAC 4 #define I2O_SNFORMAT_WAN 5 -/* +/* * Plus new in v2.0 (Yellowstone pdf doc) */ @@ -402,7 +388,7 @@ typedef struct _i2o_status_block #define I2O_SNFORMAT_UNKNOWN2 0xff /* - * I2O Get Status State values + * I2O Get Status State values */ #define ADAPTER_STATE_INITIALIZING 0x01 @@ -413,4 +399,4 @@ typedef struct _i2o_status_block #define ADAPTER_STATE_FAILED 0x10 #define ADAPTER_STATE_FAULTED 0x11 -#endif /* _I2O_DEV_H */ +#endif /* _I2O_DEV_H */ --- linux-2.6.9-rc1/include/linux/i2o.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/include/linux/i2o.h 2004-09-02 20:51:52.000000000 -0700 @@ -1,16 +1,16 @@ /* * I2O kernel space accessible structures/APIs - * + * * (c) Copyright 1999, 2000 Red Hat Software * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * ************************************************************************* * - * This header file defined the I2O APIs/structures for use by + * This header file defined the I2O APIs/structures for use by * the I2O kernel modules. * */ @@ -18,309 +18,586 @@ #ifndef _I2O_H #define _I2O_H -#ifdef __KERNEL__ /* This file to be included by kernel only */ +#ifdef __KERNEL__ /* This file to be included by kernel only */ #include /* How many different OSM's are we allowing */ -#define MAX_I2O_MODULES 4 - -/* How many OSMs can register themselves for device status updates? */ -#define I2O_MAX_MANAGERS 4 +#define I2O_MAX_DRIVERS 4 +#include #include /* Needed for MUTEX init macros */ -#include -#include -#include +#include +#include + +/* message queue empty */ +#define I2O_QUEUE_EMPTY 0xffffffff /* * Message structures */ -struct i2o_message -{ - u8 version_offset; - u8 flags; - u16 size; - u32 target_tid:12; - u32 init_tid:12; - u32 function:8; - u32 initiator_context; +struct i2o_message { + union { + struct { + u8 version_offset; + u8 flags; + u16 size; + u32 target_tid:12; + u32 init_tid:12; + u32 function:8; + u32 icntxt; /* initiator context */ + u32 tcntxt; /* transaction context */ + } s; + u32 head[4]; + } u; /* List follows */ + u32 body[0]; }; /* - * Each I2O device entity has one or more of these. There is one - * per device. + * Each I2O device entity has one of these. There is one per device. */ -struct i2o_device -{ - i2o_lct_entry lct_data; /* Device LCT information */ - u32 flags; - int i2oversion; /* I2O version supported. Actually - * there should be high and low - * version */ +struct i2o_device { + i2o_lct_entry lct_data; /* Device LCT information */ - struct proc_dir_entry *proc_entry; /* /proc dir */ + struct i2o_controller *iop; /* Controlling IOP */ + struct list_head list; /* node in IOP devices list */ + + struct device device; - /* Primary user */ - struct i2o_handler *owner; + struct semaphore lock; /* device lock */ - /* Management users */ - struct i2o_handler *managers[I2O_MAX_MANAGERS]; - int num_managers; + struct class_device classdev; /* i2o device class */ +}; - struct i2o_controller *controller; /* Controlling IOP */ - struct i2o_device *next; /* Chain */ - struct i2o_device *prev; - char dev_name[8]; /* linux /dev name if available */ +/* + * Event structure provided to the event handling function + */ +struct i2o_event { + struct work_struct work; + struct i2o_device *i2o_dev; /* I2O device pointer from which the + event reply was initiated */ + u16 size; /* Size of data in 32-bit words */ + u32 tcntxt; /* Transaction context used at + registration */ + u32 event_indicator; /* Event indicator from reply */ + u32 data[0]; /* Event data from reply */ }; /* - * context queue entry, used for 32-bit context on 64-bit systems + * I2O classes which could be handled by the OSM + */ +struct i2o_class_id { + u16 class_id:12; +}; + +/* + * I2O driver structure for OSMs + */ +struct i2o_driver { + char *name; /* OSM name */ + int context; /* Low 8 bits of the transaction info */ + struct i2o_class_id *classes; /* I2O classes that this OSM handles */ + + /* Message reply handler */ + int (*reply) (struct i2o_controller *, u32, struct i2o_message *); + + /* Event handler */ + void (*event) (struct i2o_event *); + + struct workqueue_struct *event_queue; /* Event queue */ + + struct device_driver driver; + + /* notification of changes */ + void (*notify_controller_add) (struct i2o_controller *); + void (*notify_controller_remove) (struct i2o_controller *); + void (*notify_device_add) (struct i2o_device *); + void (*notify_device_remove) (struct i2o_device *); + + struct semaphore lock; +}; + +/* + * Contains all information which are necessary for DMA operations + */ +struct i2o_dma { + void *virt; + dma_addr_t phys; + u32 len; +}; + +/* + * Context queue entry, used for 32-bit context on 64-bit systems */ struct i2o_context_list_element { - struct i2o_context_list_element *next; + struct list_head list; u32 context; void *ptr; - unsigned int flags; + unsigned long timestamp; }; /* * Each I2O controller has one of these objects */ -struct i2o_controller -{ +struct i2o_controller { char name[16]; int unit; int type; - int enabled; - - struct pci_dev *pdev; /* PCI device */ - int irq; - int short_req:1; /* Use small block sizes */ - int dpt:1; /* Don't quiesce */ - int raptor:1; /* split bar */ - int promise:1; /* Promise controller */ + + struct pci_dev *pdev; /* PCI device */ + + int short_req:1; /* use small block sizes */ + int no_quiesce:1; /* dont quiesce before reset */ + int raptor:1; /* split bar */ + int promise:1; /* Promise controller */ + #ifdef CONFIG_MTRR - int mtrr_reg0; - int mtrr_reg1; + int mtrr_reg0; + int mtrr_reg1; #endif + struct list_head devices; /* list of I2O devices */ + struct notifier_block *event_notifer; /* Events */ atomic_t users; - struct i2o_device *devices; /* I2O device chain */ - struct i2o_controller *next; /* Controller chain */ - void *post_port; /* Inbout port address */ - void *reply_port; /* Outbound port address */ - void *irq_mask; /* Interrupt register address */ + struct list_head list; /* Controller list */ + void *post_port; /* Inbout port address */ + void *reply_port; /* Outbound port address */ + void *irq_mask; /* Interrupt register address */ /* Dynamic LCT related data */ - struct semaphore lct_sem; - int lct_pid; - int lct_running; - - i2o_status_block *status_block; /* IOP status block */ - dma_addr_t status_block_phys; - i2o_lct *lct; /* Logical Config Table */ - dma_addr_t lct_phys; - i2o_lct *dlct; /* Temp LCT */ - dma_addr_t dlct_phys; - i2o_hrt *hrt; /* HW Resource Table */ - dma_addr_t hrt_phys; - u32 hrt_len; - - void *base_virt; /* base virtual address */ - unsigned long base_phys; /* base physical address */ - - void *msg_virt; /* messages virtual address */ - unsigned long msg_phys; /* messages physical address */ - - int battery:1; /* Has a battery backup */ - int io_alloc:1; /* An I/O resource was allocated */ - int mem_alloc:1; /* A memory resource was allocated */ - struct resource io_resource; /* I/O resource allocated to the IOP */ - struct resource mem_resource; /* Mem resource allocated to the IOP */ + struct i2o_dma status; /* status of IOP */ - struct proc_dir_entry *proc_entry; /* /proc dir */ + struct i2o_dma hrt; /* HW Resource Table */ + i2o_lct *lct; /* Logical Config Table */ + struct i2o_dma dlct; /* Temp LCT */ + struct semaphore lct_lock; /* Lock for LCT updates */ + struct i2o_dma status_block; /* IOP status block */ + + struct i2o_dma base; /* controller messaging unit */ + struct i2o_dma in_queue; /* inbound message queue Host->IOP */ + struct i2o_dma out_queue; /* outbound message queue IOP->Host */ + + int battery:1; /* Has a battery backup */ + int io_alloc:1; /* An I/O resource was allocated */ + int mem_alloc:1; /* A memory resource was allocated */ + + struct resource io_resource; /* I/O resource allocated to the IOP */ + struct resource mem_resource; /* Mem resource allocated to the IOP */ + struct proc_dir_entry *proc_entry; /* /proc dir */ - void *page_frame; /* Message buffers */ - dma_addr_t page_frame_map; /* Cache map */ + struct list_head bus_list; /* list of busses on IOP */ + struct device device; + struct i2o_device *exec; /* Executive */ #if BITS_PER_LONG == 64 - spinlock_t context_list_lock; /* lock for context_list */ - struct i2o_context_list_element *context_list; /* list of context id's - and pointers */ + spinlock_t context_list_lock; /* lock for context_list */ + atomic_t context_list_counter; /* needed for unique contexts */ + struct list_head context_list; /* list of context id's + and pointers */ #endif + spinlock_t lock; /* lock for controller + configuration */ + + void *driver_data[I2O_MAX_DRIVERS]; /* storage for drivers */ }; /* - * OSM resgistration block + * I2O System table entry * - * Each OSM creates at least one of these and registers it with the - * I2O core through i2o_register_handler. An OSM may want to - * register more than one if it wants a fast path to a reply - * handler by having a separate initiator context for each - * class function. + * The system table contains information about all the IOPs in the + * system. It is sent to all IOPs so that they can create peer2peer + * connections between them. */ -struct i2o_handler +struct i2o_sys_tbl_entry { + u16 org_id; + u16 reserved1; + u32 iop_id:12; + u32 reserved2:20; + u16 seg_num:12; + u16 i2o_version:4; + u8 iop_state; + u8 msg_type; + u16 frame_size; + u16 reserved3; + u32 last_changed; + u32 iop_capabilities; + u32 inbound_low; + u32 inbound_high; +}; + +struct i2o_sys_tbl { + u8 num_entries; + u8 version; + u16 reserved1; + u32 change_ind; + u32 reserved2; + u32 reserved3; + struct i2o_sys_tbl_entry iops[0]; +}; + +extern struct list_head i2o_controllers; + +/* Message functions */ +static inline u32 i2o_msg_get(struct i2o_controller *, struct i2o_message **); +extern u32 i2o_msg_get_wait(struct i2o_controller *, struct i2o_message **, + int); +static inline void i2o_msg_post(struct i2o_controller *, u32); +static inline int i2o_msg_post_wait(struct i2o_controller *, u32, + unsigned long); +extern int i2o_msg_post_wait_mem(struct i2o_controller *, u32, unsigned long, + struct i2o_dma *); +extern void i2o_msg_nop(struct i2o_controller *, u32); +static inline void i2o_flush_reply(struct i2o_controller *, u32); + +/* DMA handling functions */ +static inline int i2o_dma_alloc(struct device *, struct i2o_dma *, size_t, + unsigned int); +static inline void i2o_dma_free(struct device *, struct i2o_dma *); +int i2o_dma_realloc(struct device *, struct i2o_dma *, size_t, unsigned int); + +static inline int i2o_dma_map(struct device *, struct i2o_dma *); +static inline void i2o_dma_unmap(struct device *, struct i2o_dma *); + +/* IOP functions */ +extern int i2o_status_get(struct i2o_controller *); +extern int i2o_hrt_get(struct i2o_controller *); + +extern int i2o_event_register(struct i2o_device *, struct i2o_driver *, int, + u32); +extern struct i2o_device *i2o_iop_find_device(struct i2o_controller *, u16); +extern struct i2o_controller *i2o_find_iop(int); + +/* Functions needed for handling 64-bit pointers in 32-bit context */ +#if BITS_PER_LONG == 64 +extern u32 i2o_cntxt_list_add(struct i2o_controller *, void *); +extern void *i2o_cntxt_list_get(struct i2o_controller *, u32); +extern u32 i2o_cntxt_list_remove(struct i2o_controller *, void *); +extern u32 i2o_cntxt_list_get_ptr(struct i2o_controller *, void *); + +static inline u32 i2o_ptr_low(void *ptr) { - /* Message reply handler */ - void (*reply)(struct i2o_handler *, struct i2o_controller *, - struct i2o_message *); + return (u32) (u64) ptr; +}; + +static inline u32 i2o_ptr_high(void *ptr) +{ + return (u32) ((u64) ptr >> 32); +}; +#else +static inline u32 i2o_cntxt_list_add(struct i2o_controller *c, void *ptr) +{ + return (u32) ptr; +}; - /* New device notification handler */ - void (*new_dev_notify)(struct i2o_controller *, struct i2o_device *); +static inline void *i2o_cntxt_list_get(struct i2o_controller *c, u32 context) +{ + return (void *)context; +}; - /* Device deltion handler */ - void (*dev_del_notify)(struct i2o_controller *, struct i2o_device *); +static inline u32 i2o_cntxt_list_remove(struct i2o_controller *c, void *ptr) +{ + return (u32) ptr; +}; - /* Reboot notification handler */ - void (*reboot_notify)(void); +static inline u32 i2o_cntxt_list_get_ptr(struct i2o_controller *c, void *ptr) +{ + return (u32) ptr; +}; - char *name; /* OSM name */ - int context; /* Low 8 bits of the transaction info */ - u32 class; /* I2O classes that this driver handles */ - /* User data follows */ +static inline u32 i2o_ptr_low(void *ptr) +{ + return (u32) ptr; }; -#ifdef MODULE -/* - * Used by bus specific modules to communicate with the core +static inline u32 i2o_ptr_high(void *ptr) +{ + return 0; +}; +#endif + +/* I2O driver (OSM) functions */ +extern int i2o_driver_register(struct i2o_driver *); +extern void i2o_driver_unregister(struct i2o_driver *); + +/** + * i2o_driver_notify_controller_add - Send notification of added controller + * to a single I2O driver * - * This is needed because the bus modules cannot make direct - * calls to the core as this results in the i2o_bus_specific_module - * being dependent on the core, not the otherway around. - * In that case, a 'modprobe i2o_lan' loads i2o_core & i2o_lan, - * but _not_ i2o_pci...which makes the whole thing pretty useless :) + * Send notification of added controller to a single registered driver. + */ +static inline void i2o_driver_notify_controller_add(struct i2o_driver *drv, + struct i2o_controller *c) +{ + if (drv->notify_controller_add) + drv->notify_controller_add(c); +}; + +/** + * i2o_driver_notify_controller_remove - Send notification of removed + * controller to a single I2O driver * + * Send notification of removed controller to a single registered driver. */ -struct i2o_core_func_table +static inline void i2o_driver_notify_controller_remove(struct i2o_driver *drv, + struct i2o_controller *c) { - int (*install)(struct i2o_controller *); - int (*activate)(struct i2o_controller *); - struct i2o_controller *(*find)(int); - void (*unlock)(struct i2o_controller *); - void (*run_queue)(struct i2o_controller * c); - int (*delete)(struct i2o_controller *); + if (drv->notify_controller_remove) + drv->notify_controller_remove(c); }; -#endif /* MODULE */ -/* - * I2O System table entry +/** + * i2o_driver_notify_device_add - Send notification of added device to a + * single I2O driver * - * The system table contains information about all the IOPs in the - * system. It is sent to all IOPs so that they can create peer2peer - * connections between them. + * Send notification of added device to a single registered driver. */ -struct i2o_sys_tbl_entry +static inline void i2o_driver_notify_device_add(struct i2o_driver *drv, + struct i2o_device *i2o_dev) { - u16 org_id; - u16 reserved1; - u32 iop_id:12; - u32 reserved2:20; - u16 seg_num:12; - u16 i2o_version:4; - u8 iop_state; - u8 msg_type; - u16 frame_size; - u16 reserved3; - u32 last_changed; - u32 iop_capabilities; - u32 inbound_low; - u32 inbound_high; -}; - -struct i2o_sys_tbl -{ - u8 num_entries; - u8 version; - u16 reserved1; - u32 change_ind; - u32 reserved2; - u32 reserved3; - struct i2o_sys_tbl_entry iops[0]; + if (drv->notify_device_add) + drv->notify_device_add(i2o_dev); +}; + +/** + * i2o_driver_notify_device_remove - Send notification of removed device + * to a single I2O driver + * + * Send notification of removed device to a single registered driver. + */ +static inline void i2o_driver_notify_device_remove(struct i2o_driver *drv, + struct i2o_device *i2o_dev) +{ + if (drv->notify_device_remove) + drv->notify_device_remove(i2o_dev); }; +extern void i2o_driver_notify_controller_add_all(struct i2o_controller *); +extern void i2o_driver_notify_controller_remove_all(struct i2o_controller *); +extern void i2o_driver_notify_device_add_all(struct i2o_device *); +extern void i2o_driver_notify_device_remove_all(struct i2o_device *); + +/* I2O device functions */ +extern int i2o_device_claim(struct i2o_device *); +extern int i2o_device_claim_release(struct i2o_device *); + +/* Exec OSM functions */ +extern int i2o_exec_lct_get(struct i2o_controller *); +extern int i2o_exec_lct_notify(struct i2o_controller *, u32); + +/* device to i2o_device and driver to i2o_driver convertion functions */ +#define to_i2o_driver(drv) container_of(drv,struct i2o_driver, driver) +#define to_i2o_device(dev) container_of(dev, struct i2o_device, device) + /* * Messenger inlines */ static inline u32 I2O_POST_READ32(struct i2o_controller *c) { + rmb(); return readl(c->post_port); -} +}; static inline void I2O_POST_WRITE32(struct i2o_controller *c, u32 val) { + wmb(); writel(val, c->post_port); -} - +}; static inline u32 I2O_REPLY_READ32(struct i2o_controller *c) { + rmb(); return readl(c->reply_port); -} +}; static inline void I2O_REPLY_WRITE32(struct i2o_controller *c, u32 val) { + wmb(); writel(val, c->reply_port); -} - +}; static inline u32 I2O_IRQ_READ32(struct i2o_controller *c) { + rmb(); return readl(c->irq_mask); -} +}; static inline void I2O_IRQ_WRITE32(struct i2o_controller *c, u32 val) { + wmb(); writel(val, c->irq_mask); -} + wmb(); +}; +/** + * i2o_msg_get - obtain an I2O message from the IOP + * @c: I2O controller + * @msg: pointer to a I2O message pointer + * + * This function tries to get a message slot. If no message slot is + * available do not wait until one is availabe (see also i2o_msg_get_wait). + * + * On a success the message is returned and the pointer to the message is + * set in msg. The returned message is the physical page frame offset + * address from the read port (see the i2o spec). If no message is + * available returns I2O_QUEUE_EMPTY and msg is leaved untouched. + */ +static inline u32 i2o_msg_get(struct i2o_controller *c, + struct i2o_message **msg) +{ + u32 m; -static inline void i2o_post_message(struct i2o_controller *c, u32 m) + if ((m = I2O_POST_READ32(c)) != I2O_QUEUE_EMPTY) + *msg = c->in_queue.virt + m; + + return m; +}; + +/** + * i2o_msg_post - Post I2O message to I2O controller + * @c: I2O controller to which the message should be send + * @m: the message identifier + * + * Post the message to the I2O controller. + */ +static inline void i2o_msg_post(struct i2o_controller *c, u32 m) { - /* The second line isnt spurious - thats forcing PCI posting */ I2O_POST_WRITE32(c, m); - (void) I2O_IRQ_READ32(c); -} +}; +/** + * i2o_msg_post_wait - Post and wait a message and wait until return + * @c: controller + * @m: message to post + * @timeout: time in seconds to wait + * + * This API allows an OSM to post a message and then be told whether or + * not the system received a successful reply. If the message times out + * then the value '-ETIMEDOUT' is returned. + * + * Returns 0 on success or negative error code on failure. + */ +static inline int i2o_msg_post_wait(struct i2o_controller *c, u32 m, + unsigned long timeout) +{ + return i2o_msg_post_wait_mem(c, m, timeout, NULL); +}; + +/** + * i2o_flush_reply - Flush reply from I2O controller + * @c: I2O controller + * @m: the message identifier + * + * The I2O controller must be informed that the reply message is not needed + * anymore. If you forget to flush the reply, the message frame can't be + * used by the controller anymore and is therefore lost. + * + * FIXME: is there a timeout after which the controller reuse the message? + */ static inline void i2o_flush_reply(struct i2o_controller *c, u32 m) { I2O_REPLY_WRITE32(c, m); -} +}; + +/** + * i2o_dma_alloc - Allocate DMA memory + * @dev: struct device pointer to the PCI device of the I2O controller + * @addr: i2o_dma struct which should get the DMA buffer + * @len: length of the new DMA memory + * @gfp_mask: GFP mask + * + * Allocate a coherent DMA memory and write the pointers into addr. + * + * Returns 0 on success or -ENOMEM on failure. + */ +static inline int i2o_dma_alloc(struct device *dev, struct i2o_dma *addr, + size_t len, unsigned int gfp_mask) +{ + addr->virt = dma_alloc_coherent(dev, len, &addr->phys, gfp_mask); + if (!addr->virt) + return -ENOMEM; + + memset(addr->virt, 0, len); + addr->len = len; + + return 0; +}; + +/** + * i2o_dma_free - Free DMA memory + * @dev: struct device pointer to the PCI device of the I2O controller + * @addr: i2o_dma struct which contains the DMA buffer + * + * Free a coherent DMA memory and set virtual address of addr to NULL. + */ +static inline void i2o_dma_free(struct device *dev, struct i2o_dma *addr) +{ + if (addr->virt) { + if (addr->phys) + dma_free_coherent(dev, addr->len, addr->virt, + addr->phys); + else + kfree(addr->virt); + addr->virt = NULL; + } +}; + +/** + * i2o_dma_map - Map the memory to DMA + * @dev: struct device pointer to the PCI device of the I2O controller + * @addr: i2o_dma struct which should be mapped + * + * Map the memory in addr->virt to coherent DMA memory and write the + * physical address into addr->phys. + * + * Returns 0 on success or -ENOMEM on failure. + */ +static inline int i2o_dma_map(struct device *dev, struct i2o_dma *addr) +{ + if (!addr->virt) + return -EFAULT; + + if (!addr->phys) + addr->phys = dma_map_single(dev, addr->virt, addr->len, + DMA_BIDIRECTIONAL); + if (!addr->phys) + return -ENOMEM; + + return 0; +}; + +/** + * i2o_dma_unmap - Unmap the DMA memory + * @dev: struct device pointer to the PCI device of the I2O controller + * @addr: i2o_dma struct which should be unmapped + * + * Unmap the memory in addr->virt from DMA memory. + */ +static inline void i2o_dma_unmap(struct device *dev, struct i2o_dma *addr) +{ + if (!addr->virt) + return; + + if (addr->phys) { + dma_unmap_single(dev, addr->phys, addr->len, DMA_BIDIRECTIONAL); + addr->phys = 0; + } +}; /* * Endian handling wrapped into the macro - keeps the core code * cleaner. */ - -#define i2o_raw_writel(val, mem) __raw_writel(cpu_to_le32(val), mem) - -extern struct i2o_controller *i2o_find_controller(int); -extern void i2o_unlock_controller(struct i2o_controller *); -extern struct i2o_controller *i2o_controller_chain; -extern int i2o_num_controllers; -extern int i2o_status_get(struct i2o_controller *); -extern int i2o_install_handler(struct i2o_handler *); -extern int i2o_remove_handler(struct i2o_handler *); - -extern int i2o_claim_device(struct i2o_device *, struct i2o_handler *); -extern int i2o_release_device(struct i2o_device *, struct i2o_handler *); -extern int i2o_device_notify_on(struct i2o_device *, struct i2o_handler *); -extern int i2o_device_notify_off(struct i2o_device *, - struct i2o_handler *); - -extern int i2o_post_this(struct i2o_controller *, u32 *, int); -extern int i2o_post_wait(struct i2o_controller *, u32 *, int, int); -extern int i2o_post_wait_mem(struct i2o_controller *, u32 *, int, int, - void *, void *, dma_addr_t, dma_addr_t, int, int); +#define i2o_raw_writel(val, mem) __raw_writel(cpu_to_le32(val), mem) -extern int i2o_query_scalar(struct i2o_controller *, int, int, int, void *, - int); -extern int i2o_set_scalar(struct i2o_controller *, int, int, int, void *, - int); +extern int i2o_parm_field_get(struct i2o_device *, int, int, void *, int); +extern int i2o_parm_field_set(struct i2o_device *, int, int, void *, int); +extern int i2o_parm_table_get(struct i2o_device *, int, int, int, void *, int, + void *, int); +/* FIXME: remove extern int i2o_query_table(int, struct i2o_controller *, int, int, int, void *, int, void *, int); extern int i2o_clear_table(struct i2o_controller *, int, int); @@ -328,51 +605,24 @@ extern int i2o_row_add_table(struct i2o_ void *, int); extern int i2o_issue_params(int, struct i2o_controller *, int, void *, int, void *, int); +*/ -extern int i2o_event_register(struct i2o_controller *, u32, u32, u32, u32); -extern int i2o_event_ack(struct i2o_controller *, u32 *); - -extern void i2o_report_status(const char *, const char *, u32 *); -extern void i2o_dump_message(u32 *); -extern const char *i2o_get_class_name(int); - -extern int i2o_install_controller(struct i2o_controller *); -extern int i2o_activate_controller(struct i2o_controller *); -extern void i2o_run_queue(struct i2o_controller *); -extern int i2o_delete_controller(struct i2o_controller *); - -#if BITS_PER_LONG == 64 -extern u32 i2o_context_list_add(void *, struct i2o_controller *); -extern void *i2o_context_list_get(u32, struct i2o_controller *); -extern u32 i2o_context_list_remove(void *, struct i2o_controller *); -#else -static inline u32 i2o_context_list_add(void *ptr, struct i2o_controller *c) -{ - return (u32)ptr; -} - -static inline void *i2o_context_list_get(u32 context, struct i2o_controller *c) -{ - return (void *)context; -} - -static inline u32 i2o_context_list_remove(void *ptr, struct i2o_controller *c) -{ - return (u32)ptr; -} -#endif +/* debugging functions */ +extern void i2o_report_status(const char *, const char *, struct i2o_message *); +extern void i2o_dump_message(struct i2o_message *); +extern void i2o_dump_hrt(struct i2o_controller *c); +extern void i2o_debug_state(struct i2o_controller *c); /* * Cache strategies */ - - + /* The NULL strategy leaves everything up to the controller. This tends to be a * pessimal but functional choice. */ #define CACHE_NULL 0 /* Prefetch data when reading. We continually attempt to load the next 32 sectors - * into the controller cache. + * into the controller cache. */ #define CACHE_PREFETCH 1 /* Prefetch data when reading. We sometimes attempt to load the next 32 sectors @@ -406,15 +656,11 @@ static inline u32 i2o_context_list_remov /* * Ioctl structures */ - - -#define BLKI2OGRSTRAT _IOR('2', 1, int) -#define BLKI2OGWSTRAT _IOR('2', 2, int) -#define BLKI2OSRSTRAT _IOW('2', 3, int) -#define BLKI2OSWSTRAT _IOW('2', 4, int) - - +#define BLKI2OGRSTRAT _IOR('2', 1, int) +#define BLKI2OGWSTRAT _IOR('2', 2, int) +#define BLKI2OSRSTRAT _IOW('2', 3, int) +#define BLKI2OSWSTRAT _IOW('2', 4, int) /* * I2O Function codes @@ -652,7 +898,6 @@ static inline u32 i2o_context_list_remov #define TRL_SINGLE_VARIABLE_LENGTH 0x40 #define TRL_MULTIPLE_FIXED_LENGTH 0x80 - /* msg header defines for MsgFlags */ #define MSG_STATIC 0x0100 #define MSG_64BIT_CNTXT 0x0200 @@ -673,13 +918,12 @@ static inline u32 i2o_context_list_remov #define ELEVEN_WORD_MSG_SIZE 0x000B0000 #define I2O_MESSAGE_SIZE(x) ((x)<<16) - /* Special TID Assignments */ #define ADAPTER_TID 0 #define HOST_TID 1 -#define MSG_FRAME_SIZE 64 /* i2o_scsi assumes >= 32 */ +#define MSG_FRAME_SIZE 128 /* i2o_scsi assumes >= 32 */ #define REPLY_FRAME_SIZE 17 #define SG_TABLESIZE 30 #define NMBR_MSG_FRAMES 128 @@ -693,5 +937,23 @@ static inline u32 i2o_context_list_remov #define I2O_CONTEXT_LIST_USED 0x01 #define I2O_CONTEXT_LIST_DELETED 0x02 -#endif /* __KERNEL__ */ -#endif /* _I2O_H */ +/* timeouts */ +#define I2O_TIMEOUT_INIT_OUTBOUND_QUEUE 15 +#define I2O_TIMEOUT_MESSAGE_GET 5 +#define I2O_TIMEOUT_RESET 30 +#define I2O_TIMEOUT_STATUS_GET 5 +#define I2O_TIMEOUT_LCT_GET 20 +#define I2O_TIMEOUT_SCSI_SCB_ABORT 240 + +/* retries */ +#define I2O_HRT_GET_TRIES 3 +#define I2O_LCT_GET_TRIES 3 + +/* request queue sizes */ +#define I2O_MAX_SECTORS 1024 +#define I2O_MAX_SEGMENTS 128 + +#define I2O_REQ_MEMPOOL_SIZE 32 + +#endif /* __KERNEL__ */ +#endif /* _I2O_H */ --- linux-2.6.9-rc1/include/linux/ide.h 2004-08-24 00:02:26.000000000 -0700 +++ 25/include/linux/ide.h 2004-09-03 00:56:53.517024824 -0700 @@ -756,8 +756,8 @@ typedef struct ide_drive_s { * 2=48-bit doing 28-bit * 3=64-bit */ + unsigned scsi : 1; /* 0=default, 1=ide-scsi emulation */ - u8 scsi; /* 0=default, 1=skip current ide-subdriver for ide-scsi emulation */ u8 quirk_list; /* considered quirky, set for a specific host */ u8 suspend_reset; /* drive suspend mode flag, soft-reset recovers */ u8 init_speed; /* transfer rate set at boot */ @@ -1497,6 +1497,9 @@ extern irqreturn_t ide_intr(int irq, voi extern void do_ide_request(request_queue_t *); extern void ide_init_subdrivers(void); +extern void ide_pin_hwgroup(ide_drive_t *); +extern void ide_unpin_hwgroup(ide_drive_t *); + extern struct block_device_operations ide_fops[]; extern ide_proc_entry_t generic_subdriver_entries[]; --- linux-2.6.9-rc1/include/linux/if_bridge.h 2004-08-24 00:02:47.000000000 -0700 +++ 25/include/linux/if_bridge.h 2004-09-02 20:51:52.000000000 -0700 @@ -105,7 +105,7 @@ struct __fdb_entry #include extern void brioctl_set(int (*ioctl_hook)(unsigned int, void __user *)); -extern int (*br_handle_frame_hook)(struct sk_buff *skb); +extern int (*br_handle_frame_hook)(struct net_bridge_port *p, struct sk_buff **pskb); extern int (*br_should_route_hook)(struct sk_buff **pskb); #endif --- linux-2.6.9-rc1/include/linux/init_task.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/include/linux/init_task.h 2004-09-03 00:57:03.610490384 -0700 @@ -71,14 +71,13 @@ extern struct group_info init_groups; .usage = ATOMIC_INIT(2), \ .flags = 0, \ .lock_depth = -1, \ - .prio = MAX_PRIO-20, \ - .static_prio = MAX_PRIO-20, \ + .prio = MAX_PRIO-29, \ + .static_prio = MAX_PRIO-29, \ .policy = SCHED_NORMAL, \ .cpus_allowed = CPU_MASK_ALL, \ .mm = NULL, \ .active_mm = &init_mm, \ .run_list = LIST_HEAD_INIT(tsk.run_list), \ - .time_slice = HZ, \ .tasks = LIST_HEAD_INIT(tsk.tasks), \ .ptrace_children= LIST_HEAD_INIT(tsk.ptrace_children), \ .ptrace_list = LIST_HEAD_INIT(tsk.ptrace_list), \ @@ -112,8 +111,8 @@ extern struct group_info init_groups; .proc_lock = SPIN_LOCK_UNLOCKED, \ .switch_lock = SPIN_LOCK_UNLOCKED, \ .journal_info = NULL, \ + .private_pages = LIST_HEAD_INIT(tsk.private_pages), \ + .private_pages_count = 0, \ } - - #endif --- linux-2.6.9-rc1/include/linux/input.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/include/linux/input.h 2004-09-03 00:56:31.594357576 -0700 @@ -527,6 +527,8 @@ struct input_absinfo { #define MSC_SERIAL 0x00 #define MSC_PULSELED 0x01 #define MSC_GESTURE 0x02 +#define MSC_RAW 0x03 +#define MSC_SCAN 0x04 #define MSC_MAX 0x07 /* --- linux-2.6.9-rc1/include/linux/interrupt.h 2004-08-24 00:02:48.000000000 -0700 +++ 25/include/linux/interrupt.h 2004-09-03 00:56:41.029923152 -0700 @@ -8,8 +8,8 @@ #include #include #include +#include #include -#include #include #include --- linux-2.6.9-rc1/include/linux/ioctl32.h 2004-08-24 00:01:51.000000000 -0700 +++ 25/include/linux/ioctl32.h 2004-09-02 20:51:52.000000000 -0700 @@ -3,6 +3,15 @@ struct file; +typedef int (*ioctl_trans_handler_t)(unsigned int, unsigned int, + unsigned long, struct file *); + +struct ioctl_trans { + unsigned long cmd; + ioctl_trans_handler_t handler; + struct ioctl_trans *next; +}; + /* * Register an 32bit ioctl translation handler for ioctl cmd. * @@ -13,16 +22,16 @@ struct file; * struct file *file: file descriptor pointer. */ -extern int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *)); - +#ifdef CONFIG_COMPAT +extern int register_ioctl32_conversion(unsigned int cmd, + ioctl_trans_handler_t handler); extern int unregister_ioctl32_conversion(unsigned int cmd); -typedef int (*ioctl_trans_handler_t)(unsigned int, unsigned int, unsigned long, struct file *); +#else -struct ioctl_trans { - unsigned long cmd; - ioctl_trans_handler_t handler; - struct ioctl_trans *next; -}; +#define register_ioctl32_conversion(cmd, handler) ({ 0; }) +#define unregister_ioctl32_conversion(cmd) ({ 0; }) + +#endif #endif --- linux-2.6.9-rc1/include/linux/isicom.h 2004-08-24 00:02:32.000000000 -0700 +++ 25/include/linux/isicom.h 2004-09-03 00:57:21.549763200 -0700 @@ -102,7 +102,6 @@ typedef struct { #define ClearInterrupt(base) (inw((base)+0x0a)) #define BOARD(line) (((line) >> 4) & 0x3) -#define MIN(a, b) ( (a) < (b) ? (a) : (b) ) /* isi kill queue bitmap */ --- linux-2.6.9-rc1/include/linux/kernel.h 2004-08-24 00:01:50.000000000 -0700 +++ 25/include/linux/kernel.h 2004-09-03 00:56:34.828865856 -0700 @@ -136,6 +136,9 @@ extern const char *print_tainted(void); extern enum system_states { SYSTEM_BOOTING, SYSTEM_RUNNING, + SYSTEM_SNAPSHOT, + SYSTEM_DISK_SUSPEND, + SYSTEM_RAM_SUSPEND, SYSTEM_HALT, SYSTEM_POWER_OFF, SYSTEM_RESTART, --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/linux/kevent.h 2004-09-03 00:57:14.973762904 -0700 @@ -0,0 +1,42 @@ +#ifndef _LINUX_KEVENT_H +#define _LINUX_KEVENT_H + +#include +#include + +/* kevent types - these are used as the multicast group */ +enum kevent { + KEVENT_GENERAL = 0, + KEVENT_STORAGE = 1, + KEVENT_POWER = 2, + KEVENT_FS = 3, + KEVENT_HOTPLUG = 4, +}; + +#ifdef __KERNEL__ +#ifdef CONFIG_KERNEL_EVENTS + +int send_kevent(enum kevent type, struct kset *kset, + struct kobject *kobj, const char *signal); + +int send_kevent_atomic(enum kevent type, struct kset *kset, + struct kobject *kobj, const char *signal); + +#else + +static inline int send_kevent(enum kevent type, struct kset *kset, + struct kobject *kobj, const char *signal) +{ + return 0; +} + +static inline int send_kevent_atomic(enum kevent type, struct kset *kset, + struct kobject *kobj, const char *signal) +{ + return 0; +} + +#endif /* CONFIG_KERNEL_EVENTS */ +#endif /* __KERNEL__ */ + +#endif /* _LINUX_KEVENT_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/linux/kexec.h 2004-09-03 00:56:58.613250080 -0700 @@ -0,0 +1,56 @@ +#ifndef LINUX_KEXEC_H +#define LINUX_KEXEC_H + +#if CONFIG_KEXEC +#include +#include +#include + +/* + * This structure is used to hold the arguments that are used when loading + * kernel binaries. + */ + +typedef unsigned long kimage_entry_t; +#define IND_DESTINATION 0x1 +#define IND_INDIRECTION 0x2 +#define IND_DONE 0x4 +#define IND_SOURCE 0x8 + +#define KEXEC_SEGMENT_MAX 8 +struct kexec_segment { + void *buf; + size_t bufsz; + void *mem; + size_t memsz; +}; + +struct kimage { + kimage_entry_t head; + kimage_entry_t *entry; + kimage_entry_t *last_entry; + + unsigned long destination; + + unsigned long start; + struct page *control_code_page; + + unsigned long nr_segments; + struct kexec_segment segment[KEXEC_SEGMENT_MAX]; + + struct list_head control_pages; + struct list_head dest_pages; + struct list_head unuseable_pages; +}; + + +/* kexec interface functions */ +extern void machine_kexec(struct kimage *image); +extern int machine_kexec_prepare(struct kimage *image); +extern void machine_kexec_cleanup(struct kimage *image); +extern asmlinkage long sys_kexec(unsigned long entry, long nr_segments, + struct kexec_segment *segments); +extern struct page *kimage_alloc_control_pages(struct kimage *image, unsigned int order); +extern struct kimage *kexec_image; +#endif +#endif /* LINUX_KEXEC_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/linux/key.h 2004-09-03 00:56:55.752684952 -0700 @@ -0,0 +1,276 @@ +/* key.h: authentication token and access key management + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * + * See Documentation/keys.txt for information on keys/keyrings. + */ + +#ifndef _LINUX_KEY_H +#define _LINUX_KEY_H + +#include +#include +#include +#include +#include + +#ifdef __KERNEL__ +#ifdef CONFIG_KEYS + +#undef KEY_DEBUGGING + +/* key handle serial number */ +typedef int32_t key_serial_t; + +/* key handle permissions mask */ +typedef uint32_t key_perm_t; +#define KEY_USR_VIEW 0x00010000 /* user can view a key's attributes */ +#define KEY_USR_READ 0x00020000 /* user can read key payload / view keyring */ +#define KEY_USR_WRITE 0x00040000 /* user can update key payload / add link to keyring */ +#define KEY_USR_SEARCH 0x00080000 /* user can find a key in search / search a keyring */ +#define KEY_USR_LINK 0x00100000 /* user can create a link to a key/keyring */ +#define KEY_USR_ALL 0x001f0000 + +#define KEY_GRP_VIEW 0x00000100 /* group permissions... */ +#define KEY_GRP_READ 0x00000200 +#define KEY_GRP_WRITE 0x00000400 +#define KEY_GRP_SEARCH 0x00000800 +#define KEY_GRP_LINK 0x00001000 +#define KEY_GRP_ALL 0x00001f00 + +#define KEY_OTH_VIEW 0x00000001 /* third party permissions... */ +#define KEY_OTH_READ 0x00000002 +#define KEY_OTH_WRITE 0x00000004 +#define KEY_OTH_SEARCH 0x00000008 +#define KEY_OTH_LINK 0x00000010 +#define KEY_OTH_ALL 0x0000001f + +struct seq_file; + +struct key; +struct key_type; +struct key_owner; +struct keyring_list; +struct keyring_name; + +/*****************************************************************************/ +/* + * authentication token / access credential / keyring + * - types of key include: + * - keyrings + * - disk encryption IDs + * - Kerberos TGTs and tickets + */ +struct key { + atomic_t usage; /* number of references */ + key_serial_t serial; /* key serial number */ + struct rb_node serial_node; + struct key_type *type; /* type of key */ + rwlock_t lock; /* examination vs change lock */ + struct rw_semaphore sem; /* change vs change sem */ + struct key_user *user; /* owner of this key */ + time_t expiry; /* time at which key expires (or 0) */ + uid_t uid; + gid_t gid; + key_perm_t perm; /* access permissions */ + unsigned short quotalen; /* length added to quota */ + unsigned short datalen; /* payload data length */ + unsigned short flags; /* status flags (change with lock writelocked) */ +#define KEY_FLAG_INSTANTIATED 0x00000001 /* set if key has been instantiated */ +#define KEY_FLAG_DEAD 0x00000002 /* set if key type has been deleted */ +#define KEY_FLAG_REVOKED 0x00000004 /* set if key had been revoked */ +#define KEY_FLAG_IN_QUOTA 0x00000008 /* set if key consumes quota */ +#define KEY_FLAG_USER_CONSTRUCT 0x00000010 /* set if key is being constructed in userspace */ +#define KEY_FLAG_NEGATIVE 0x00000020 /* set if key is negative */ + +#ifdef KEY_DEBUGGING + unsigned magic; +#define KEY_DEBUG_MAGIC 0x18273645u +#define KEY_DEBUG_MAGIC_X 0xf8e9dacbu +#endif + + /* the description string + * - this is used to match a key against search criteria + * - this should be a printable string + * - eg: for krb5 AFS, this might be "afs@REDHAT.COM" + */ + char *description; + + /* type specific data + * - this is used by the keyring type to index the name + */ + union { + struct list_head link; + } type_data; + + /* key data + * - this is used to hold the data actually used in cryptography or + * whatever + */ + union { + unsigned long value; + void *data; + struct keyring_list *subscriptions; + } payload; +}; + +/*****************************************************************************/ +/* + * kernel managed key type definition + */ +struct key_type { + /* name of the type */ + const char *name; + + /* default payload length for quota precalculation (optional) + * - this can be used instead of calling key_payload_reserve(), that + * function only needs to be called if the real datalen is different + */ + size_t def_datalen; + + /* instantiate a key of this type + * - this method should call key_payload_reserve() to determine if the + * user's quota will hold the payload + */ + int (*instantiate)(struct key *key, const void *data, size_t datalen); + + /* duplicate a key of this type (optional) + * - the source key will be locked against change + * - the new description will be attached + * - the quota will have been adjusted automatically from + * source->quotalen + */ + int (*duplicate)(struct key *key, const struct key *source); + + /* update a key of this type (optional) + * - this method should call key_payload_reserve() to recalculate the + * quota consumption + * - the key must be locked against read when modifying + */ + int (*update)(struct key *key, const void *data, size_t datalen); + + /* match a key against a description */ + int (*match)(const struct key *key, const void *desc); + + /* clear the data from a key (optional) */ + void (*destroy)(struct key *key); + + /* describe a key */ + void (*describe)(const struct key *key, struct seq_file *p); + + /* read a key's data (optional) + * - permission checks will be done by the caller + * - the key's semaphore will be readlocked by the caller + * - should return the amount of data that could be read, no matter how + * much is copied into the buffer + * - shouldn't do the copy if the buffer is NULL + */ + long (*read)(const struct key *key, char __user *buffer, size_t buflen); + + /* internal fields */ + struct list_head link; /* link in types list */ +}; + +extern struct key_type key_type_keyring; + +extern int register_key_type(struct key_type *ktype); +extern void unregister_key_type(struct key_type *ktype); + +extern struct key *key_alloc(struct key_type *type, + const char *desc, + uid_t uid, gid_t gid, key_perm_t perm, + int not_in_quota); +extern int key_payload_reserve(struct key *key, size_t datalen); +extern int key_instantiate_and_link(struct key *key, + const void *data, + size_t datalen, + struct key *keyring); +extern int key_negate_and_link(struct key *key, + unsigned timeout, + struct key *keyring); +extern void key_revoke(struct key *key); +extern void key_put(struct key *key); + +extern struct key *request_key(struct key_type *type, + const char *description, + const char *callout_info); + +extern int key_validate(struct key *key); + +extern struct key *key_create_or_update(struct key *keyring, + const char *type, + const char *description, + const void *payload, + size_t plen, + int not_in_quota); + +extern int key_update(struct key *key, + const void *payload, + size_t plen); + +extern int key_link(struct key *keyring, + struct key *key); + +extern int key_unlink(struct key *keyring, + struct key *key); + +extern struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, + int not_in_quota, struct key *dest); + +extern int keyring_clear(struct key *keyring); + +extern struct key *keyring_search(struct key *keyring, + struct key_type *type, + const char *description); + +extern struct key *search_process_keyrings(struct key_type *type, + const char *description); + +extern int keyring_add_key(struct key *keyring, + struct key *key); + +extern struct key *key_lookup(key_serial_t id); + +/* + * the userspace interface + */ +extern struct key root_user_keyring, root_session_keyring; +extern int alloc_uid_keyring(struct user_struct *user); +extern void switch_uid_keyring(struct user_struct *new_user); +extern int copy_keys(unsigned long clone_flags, struct task_struct *tsk); +extern void exit_keys(struct task_struct *tsk); +extern int suid_keys(struct task_struct *tsk); +extern int exec_keys(struct task_struct *tsk); +extern void key_fsuid_changed(struct task_struct *tsk); +extern void key_fsgid_changed(struct task_struct *tsk); +extern long get_process_keyring_ID(key_serial_t id, int create); +extern long join_session_keyring_user(const char __user *name); + +asmlinkage long sys_keyctl(int option, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5); + +#else /* CONFIG_KEYS */ + +#define key_put(k) do { } while(0) +#define alloc_uid_keyring(u) 0 +#define switch_uid_keyring(u) do { } while(0) +#define copy_keys(f,t) 0 +#define exit_keys(t) do { } while(0) +#define suid_keys(t) do { } while(0) +#define exec_keys(t) do { } while(0) +#define key_fsuid_changed(t) do { } while(0) +#define key_fsgid_changed(t) do { } while(0) +#define get_process_keyring_ID(s,c) (-EINVAL) +#define join_session_keyring_user(n) (-EINVAL) +#define sys_keyctl(o,b,c,d,e) (-EINVAL) + +#endif /* CONFIG_KEYS */ +#endif /* __KERNEL__ */ +#endif /* _LINUX_KEY_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/linux/key-ui.h 2004-09-03 00:56:54.720841816 -0700 @@ -0,0 +1,97 @@ +/* key-ui.h: key userspace interface stuff for use by keyfs + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_KEY_UI_H +#define _LINUX_KEY_UI_H + +#include + +/* the key tree */ +extern struct rb_root key_serial_tree; +extern spinlock_t key_serial_lock; + +/* required permissions */ +#define KEY_VIEW 0x01 /* require permission to view attributes */ +#define KEY_READ 0x02 /* require permission to read content */ +#define KEY_WRITE 0x04 /* require permission to update / modify */ +#define KEY_SEARCH 0x08 /* require permission to search (keyring) or find (key) */ +#define KEY_LINK 0x10 /* require permission to link */ +#define KEY_ALL 0x1f /* all the above permissions */ + +/* + * the keyring payload contains a list of the keys to which the keyring is + * subscribed + */ +struct keyring_list { + unsigned maxkeys; /* max keys this list can hold */ + unsigned nkeys; /* number of keys currently held */ + struct key *keys[0]; +}; + + +/* + * check to see whether permission is granted to use a key in the desired way + */ +static inline int key_permission(const struct key *key, key_perm_t perm) +{ + key_perm_t kperm; + + if (key->uid == current->fsuid) + kperm = key->perm >> 16; + else if (key->gid != -1 && + key->perm & KEY_GRP_ALL && + in_group_p(key->gid) + ) + kperm = key->perm >> 8; + else + kperm = key->perm; + + kperm = kperm & perm & KEY_ALL; + + return kperm == perm; +} + +/* + * check to see whether permission is granted to use a key in at least one of + * the desired ways + */ +static inline int key_any_permission(const struct key *key, key_perm_t perm) +{ + key_perm_t kperm; + + if (key->uid == current->fsuid) + kperm = key->perm >> 16; + else if (key->gid != -1 && + key->perm & KEY_GRP_ALL && + in_group_p(key->gid) + ) + kperm = key->perm >> 8; + else + kperm = key->perm; + + kperm = kperm & perm & KEY_ALL; + + return kperm != 0; +} + + +extern struct key *lookup_user_key(key_serial_t id, int create, int part, + key_perm_t perm); + +extern long join_session_keyring(const char *name); + +extern struct key_type *key_type_lookup(const char *type); +extern void key_type_put(struct key_type *ktype); + +#define key_negative_timeout 60 /* default timeout on a negative key's existence */ + + +#endif /* _LINUX_KEY_UI_H */ --- linux-2.6.9-rc1/include/linux/kobject.h 2004-08-24 00:01:52.000000000 -0700 +++ 25/include/linux/kobject.h 2004-09-02 20:51:52.000000000 -0700 @@ -12,13 +12,16 @@ * destructors. */ -#if defined(__KERNEL__) && !defined(_KOBJECT_H_) +#ifndef _KOBJECT_H_ #define _KOBJECT_H_ +#ifdef __KERNEL__ + #include #include #include #include +#include #include #define KOBJ_NAME_LEN 20 @@ -26,7 +29,7 @@ struct kobject { char * k_name; char name[KOBJ_NAME_LEN]; - atomic_t refcount; + struct kref kref; struct list_head entry; struct kobject * parent; struct kset * kset; @@ -58,6 +61,8 @@ extern void kobject_put(struct kobject * extern void kobject_hotplug(const char *action, struct kobject *); +extern char * kobject_get_path(struct kset *, struct kobject *, int); + struct kobj_type { void (*release)(struct kobject *); struct sysfs_ops * sysfs_ops; @@ -229,5 +234,5 @@ struct subsys_attribute { extern int subsys_create_file(struct subsystem * , struct subsys_attribute *); extern void subsys_remove_file(struct subsystem * , struct subsys_attribute *); - +#endif /* __KERNEL__ */ #endif /* _KOBJECT_H_ */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/linux/kprobes.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,134 @@ +#ifndef _LINUX_KPROBES_H +#define _LINUX_KPROBES_H +/* + * Kernel Probes (KProbes) + * include/linux/kprobes.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2002, 2004 + * + * 2002-Oct Created by Vamsi Krishna S Kernel + * Probes initial implementation ( includes suggestions from + * Rusty Russell). + * 2004-July Suparna Bhattacharya added jumper probes + * interface to access function arguments. + */ +#include +#include +#include +#include +#include + +struct kprobe; +struct pt_regs; +typedef int (*kprobe_pre_handler_t) (struct kprobe *, struct pt_regs *); +typedef int (*kprobe_break_handler_t) (struct kprobe *, struct pt_regs *); +typedef void (*kprobe_post_handler_t) (struct kprobe *, struct pt_regs *, + unsigned long flags); +typedef int (*kprobe_fault_handler_t) (struct kprobe *, struct pt_regs *, + int trapnr); +struct kprobe { + struct hlist_node hlist; + + /* location of the probe point */ + kprobe_opcode_t *addr; + + /* Called before addr is executed. */ + kprobe_pre_handler_t pre_handler; + + /* Called after addr is executed, unless... */ + kprobe_post_handler_t post_handler; + + /* ... called if executing addr causes a fault (eg. page fault). + * Return 1 if it handled fault, otherwise kernel will see it. */ + kprobe_fault_handler_t fault_handler; + + /* ... called if breakpoint trap occurs in probe handler. + * Return 1 if it handled break, otherwise kernel will see it. */ + kprobe_break_handler_t break_handler; + + /* Saved opcode (which has been replaced with breakpoint) */ + kprobe_opcode_t opcode; + + /* copy of the original instruction */ + kprobe_opcode_t insn[MAX_INSN_SIZE]; +}; + +/* + * Special probe type that uses setjmp-longjmp type tricks to resume + * execution at a specified entry with a matching prototype corresponding + * to the probed function - a trick to enable arguments to become + * accessible seamlessly by probe handling logic. + * Note: + * Because of the way compilers allocate stack space for local variables + * etc upfront, regardless of sub-scopes within a function, this mirroring + * principle currently works only for probes placed on function entry points. + */ +struct jprobe { + struct kprobe kp; + kprobe_opcode_t *entry; /* probe handling code to jump to */ +}; + +#ifdef CONFIG_KPROBES +/* Locks kprobe: irq must be disabled */ +void lock_kprobes(void); +void unlock_kprobes(void); + +/* kprobe running now on this CPU? */ +static inline int kprobe_running(void) +{ + extern unsigned int kprobe_cpu; + return kprobe_cpu == smp_processor_id(); +} + +extern void arch_prepare_kprobe(struct kprobe *p); +extern void show_registers(struct pt_regs *regs); + +/* Get the kprobe at this addr (if any). Must have called lock_kprobes */ +struct kprobe *get_kprobe(void *addr); + +int register_kprobe(struct kprobe *p); +void unregister_kprobe(struct kprobe *p); +int setjmp_pre_handler(struct kprobe *, struct pt_regs *); +int longjmp_break_handler(struct kprobe *, struct pt_regs *); +int register_jprobe(struct jprobe *p); +void unregister_jprobe(struct jprobe *p); +void jprobe_return(void); + +#else +static inline int kprobe_running(void) +{ + return 0; +} +static inline int register_kprobe(struct kprobe *p) +{ + return -ENOSYS; +} +static inline void unregister_kprobe(struct kprobe *p) +{ +} +static inline int register_jprobe(struct jprobe *p) +{ + return -ENOSYS; +} +static inline void unregister_jprobe(struct jprobe *p) +{ +} +static inline void jprobe_return(void) +{ +} +#endif +#endif /* _LINUX_KPROBES_H */ --- linux-2.6.9-rc1/include/linux/kref.h 2004-08-24 00:01:55.000000000 -0700 +++ 25/include/linux/kref.h 2004-09-02 20:51:52.000000000 -0700 @@ -12,21 +12,21 @@ * */ -#if defined(__KERNEL__) && !defined(_KREF_H_) +#ifndef _KREF_H_ #define _KREF_H_ +#ifdef __KERNEL__ + #include #include - struct kref { atomic_t refcount; - void (*release)(struct kref *kref); }; -void kref_init(struct kref *kref, void (*release)(struct kref *)); -struct kref *kref_get(struct kref *kref); -void kref_put(struct kref *kref); - +void kref_init(struct kref *kref); +void kref_get(struct kref *kref); +void kref_put(struct kref *kref, void (*release) (struct kref *kref)); +#endif /* __KERNEL__ */ #endif /* _KREF_H_ */ --- linux-2.6.9-rc1/include/linux/list.h 2004-08-24 00:02:48.000000000 -0700 +++ 25/include/linux/list.h 2004-09-03 00:56:42.664674632 -0700 @@ -6,6 +6,7 @@ #include #include #include +#include /* * These are non-NULL pointers that will result in page faults @@ -160,6 +161,8 @@ static inline void __list_del(struct lis */ static inline void list_del(struct list_head *entry) { + BUG_ON(entry->prev->next != entry); + BUG_ON(entry->next->prev != entry); __list_del(entry->prev, entry->next); entry->next = LIST_POISON1; entry->prev = LIST_POISON2; --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/linux/lockmeter.h 2004-09-03 00:56:42.835648640 -0700 @@ -0,0 +1,320 @@ +/* + * Copyright (C) 1999-2002 Silicon Graphics, Inc. + * + * Written by John Hawkes (hawkes@sgi.com) + * Based on klstat.h by Jack Steiner (steiner@sgi.com) + * + * Modified by Ray Bryant (raybry@us.ibm.com) Feb-Apr 2000 + * Changes Copyright (C) 2000 IBM, Inc. + * Added save of index in spinlock_t to improve efficiency + * of "hold" time reporting for spinlocks + * Added support for hold time statistics for read and write + * locks. + * Moved machine dependent code to include/asm/lockmeter.h. + * + */ + +#ifndef _LINUX_LOCKMETER_H +#define _LINUX_LOCKMETER_H + + +/*--------------------------------------------------- + * architecture-independent lockmeter.h + *-------------------------------------------------*/ + +/* + * raybry -- version 2: added efficient hold time statistics + * requires lstat recompile, so flagged as new version + * raybry -- version 3: added global reader lock data + * hawkes -- version 4: removed some unnecessary fields to simplify mips64 port + */ +#define LSTAT_VERSION 5 + +int lstat_update(void*, void*, int); +int lstat_update_time(void*, void*, int, uint32_t); + +/* + * Currently, the mips64 and sparc64 kernels talk to a 32-bit lockstat, so we + * need to force compatibility in the inter-communication data structure. + */ + +#if defined(CONFIG_MIPS32_COMPAT) +#define TIME_T uint32_t +#elif defined(CONFIG_SPARC) || defined(CONFIG_SPARC64) +#define TIME_T uint64_t +#else +#define TIME_T time_t +#endif + +#if defined(__KERNEL__) || (!defined(CONFIG_MIPS32_COMPAT) && !defined(CONFIG_SPARC) && !defined(CONFIG_SPARC64)) || (_MIPS_SZLONG==32) +#define POINTER void * +#else +#define POINTER int64_t +#endif + +/* + * Values for the "action" parameter passed to lstat_update. + * ZZZ - do we want a try-success status here??? + */ +#define LSTAT_ACT_NO_WAIT 0 +#define LSTAT_ACT_SPIN 1 +#define LSTAT_ACT_REJECT 2 +#define LSTAT_ACT_WW_SPIN 3 +#define LSTAT_ACT_SLEPT 4 /* UNUSED */ + +#define LSTAT_ACT_MAX_VALUES 4 /* NOTE: Increase to 5 if use ACT_SLEPT */ + +/* + * Special values for the low 2 bits of an RA passed to + * lstat_update. + */ +/* we use these values to figure out what kind of lock data */ +/* is stored in the statistics table entry at index ....... */ +#define LSTAT_RA_SPIN 0 /* spin lock data */ +#define LSTAT_RA_READ 1 /* read lock statistics */ +#define LSTAT_RA_SEMA 2 /* RESERVED */ +#define LSTAT_RA_WRITE 3 /* write lock statistics*/ + +#define LSTAT_RA(n) \ + ((void*)( ((unsigned long)__builtin_return_address(0) & ~3) | n) ) + +/* + * Constants used for lock addresses in the lstat_directory + * to indicate special values of the lock address. + */ +#define LSTAT_MULTI_LOCK_ADDRESS NULL + +/* + * Maximum size of the lockstats tables. Increase this value + * if its not big enough. (Nothing bad happens if its not + * big enough although some locks will not be monitored.) + * We record overflows of this quantity in lstat_control.dir_overflows + * + * Note: The max value here must fit into the field set + * and obtained by the macro's PUT_INDEX() and GET_INDEX(). + * This value depends on how many bits are available in the + * lock word in the particular machine implementation we are on. + */ +#define LSTAT_MAX_STAT_INDEX 2000 + +/* + * Size and mask for the hash table into the directory. + */ +#define LSTAT_HASH_TABLE_SIZE 4096 /* must be 2**N */ +#define LSTAT_HASH_TABLE_MASK (LSTAT_HASH_TABLE_SIZE-1) + +#define DIRHASH(ra) ((unsigned long)(ra)>>2 & LSTAT_HASH_TABLE_MASK) + +/* + * This defines an entry in the lockstat directory. It contains + * information about a lock being monitored. + * A directory entry only contains the lock identification - + * counts on usage of the lock are kept elsewhere in a per-cpu + * data structure to minimize cache line pinging. + */ +typedef struct { + POINTER caller_ra; /* RA of code that set lock */ + POINTER lock_ptr; /* lock address */ + ushort next_stat_index; /* Used to link multiple locks that have the same hash table value */ +} lstat_directory_entry_t; + +/* + * A multi-dimensioned array used to contain counts for lock accesses. + * The array is 3-dimensional: + * - CPU number. Keep from thrashing cache lines between CPUs + * - Directory entry index. Identifies the lock + * - Action. Indicates what kind of contention occurred on an + * access to the lock. + * + * The index of an entry in the directory is the same as the 2nd index + * of the entry in the counts array. + */ +/* + * This table contains data for spin_locks, write locks, and read locks + * Not all data is used for all cases. In particular, the hold time + * information is not stored here for read locks since that is a global + * (e. g. cannot be separated out by return address) quantity. + * See the lstat_read_lock_counts_t structure for the global read lock + * hold time. + */ +typedef struct { + uint64_t cum_wait_ticks; /* sum of wait times */ + /* for write locks, sum of time a */ + /* writer is waiting for a reader */ + int64_t cum_hold_ticks; /* cumulative sum of holds */ + /* not used for read mode locks */ + /* must be signed. ............... */ + uint32_t max_wait_ticks; /* max waiting time */ + uint32_t max_hold_ticks; /* max holding time */ + uint64_t cum_wait_ww_ticks; /* sum times writer waits on writer*/ + uint32_t max_wait_ww_ticks; /* max wait time writer vs writer */ + /* prev 2 only used for write locks*/ + uint32_t acquire_time; /* time lock acquired this CPU */ + uint32_t count[LSTAT_ACT_MAX_VALUES]; +} lstat_lock_counts_t; + +typedef lstat_lock_counts_t lstat_cpu_counts_t[LSTAT_MAX_STAT_INDEX]; + +/* + * User request to: + * - turn statistic collection on/off, or to reset + */ +#define LSTAT_OFF 0 +#define LSTAT_ON 1 +#define LSTAT_RESET 2 +#define LSTAT_RELEASE 3 + +#define LSTAT_MAX_READ_LOCK_INDEX 1000 +typedef struct { + POINTER lock_ptr; /* address of lock for output stats */ + uint32_t read_lock_count; + int64_t cum_hold_ticks; /* sum of read lock hold times over */ + /* all callers. ....................*/ + uint32_t write_index; /* last write lock hash table index */ + uint32_t busy_periods; /* count of busy periods ended this */ + uint64_t start_busy; /* time this busy period started. ..*/ + uint64_t busy_ticks; /* sum of busy periods this lock. ..*/ + uint64_t max_busy; /* longest busy period for this lock*/ + uint32_t max_readers; /* maximum number of readers ...... */ +#ifdef USER_MODE_TESTING + rwlock_t entry_lock; /* lock for this read lock entry... */ + /* avoid having more than one rdr at*/ + /* needed for user space testing... */ + /* not needed for kernel 'cause it */ + /* is non-preemptive. ............. */ +#endif +} lstat_read_lock_counts_t; +typedef lstat_read_lock_counts_t lstat_read_lock_cpu_counts_t[LSTAT_MAX_READ_LOCK_INDEX]; + +#if defined(__KERNEL__) || defined(USER_MODE_TESTING) + +#ifndef USER_MODE_TESTING +#include +#else +#include "asm_newlockmeter.h" +#endif + +/* + * Size and mask for the hash table into the directory. + */ +#define LSTAT_HASH_TABLE_SIZE 4096 /* must be 2**N */ +#define LSTAT_HASH_TABLE_MASK (LSTAT_HASH_TABLE_SIZE-1) + +#define DIRHASH(ra) ((unsigned long)(ra)>>2 & LSTAT_HASH_TABLE_MASK) + +/* + * This version eliminates the per processor lock stack. What we do is to + * store the index of the lock hash structure in unused bits in the lock + * itself. Then on unlock we can find the statistics record without doing + * any additional hash or lock stack lookup. This works for spin_locks. + * Hold time reporting is now basically as cheap as wait time reporting + * so we ignore the difference between LSTAT_ON_HOLD and LSTAT_ON_WAIT + * as in version 1.1.* of lockmeter. + * + * For rw_locks, we store the index of a global reader stats structure in + * the lock and the writer index is stored in the latter structure. + * For read mode locks we hash at the time of the lock to find an entry + * in the directory for reader wait time and the like. + * At unlock time for read mode locks, we update just the global structure + * so we don't need to know the reader directory index value at unlock time. + * + */ + +/* + * Protocol to change lstat_control.state + * This is complicated because we don't want the cum_hold_time for + * a rw_lock to be decremented in _read_lock_ without making sure it + * is incremented in _read_lock_ and vice versa. So here is the + * way we change the state of lstat_control.state: + * I. To Turn Statistics On + * After allocating storage, set lstat_control.state non-zero. + * This works because we don't start updating statistics for in use + * locks until the reader lock count goes to zero. + * II. To Turn Statistics Off: + * (0) Disable interrupts on this CPU + * (1) Seize the lstat_control.directory_lock + * (2) Obtain the current value of lstat_control.next_free_read_lock_index + * (3) Store a zero in lstat_control.state. + * (4) Release the lstat_control.directory_lock + * (5) For each lock in the read lock list up to the saved value + * (well, -1) of the next_free_read_lock_index, do the following: + * (a) Check validity of the stored lock address + * by making sure that the word at the saved addr + * has an index that matches this entry. If not + * valid, then skip this entry. + * (b) If there is a write lock already set on this lock, + * skip to (d) below. + * (c) Set a non-metered write lock on the lock + * (d) set the cached INDEX in the lock to zero + * (e) Release the non-metered write lock. + * (6) Re-enable interrupts + * + * These rules ensure that a read lock will not have its statistics + * partially updated even though the global lock recording state has + * changed. See put_lockmeter_info() for implementation. + * + * The reason for (b) is that there may be write locks set on the + * syscall path to put_lockmeter_info() from user space. If we do + * not do this check, then we can deadlock. A similar problem would + * occur if the lock was read locked by the current CPU. At the + * moment this does not appear to happen. + */ + +/* + * Main control structure for lockstat. Used to turn statistics on/off + * and to maintain directory info. + */ +typedef struct { + int state; + spinlock_t control_lock; /* used to serialize turning statistics on/off */ + spinlock_t directory_lock; /* for serialize adding entries to directory */ + volatile int next_free_dir_index;/* next free entry in the directory */ + /* FIXME not all of these fields are used / needed .............. */ + /* the following fields represent data since */ + /* first "lstat on" or most recent "lstat reset" */ + TIME_T first_started_time; /* time when measurement first enabled */ + TIME_T started_time; /* time when measurement last started */ + TIME_T ending_time; /* time when measurement last disabled */ + uint64_t started_cycles64; /* cycles when measurement last started */ + uint64_t ending_cycles64; /* cycles when measurement last disabled */ + uint64_t enabled_cycles64; /* total cycles with measurement enabled */ + int intervals; /* number of measurement intervals recorded */ + /* i. e. number of times did lstat on;lstat off */ + lstat_directory_entry_t *dir; /* directory */ + int dir_overflow; /* count of times ran out of space in directory */ + int rwlock_overflow; /* count of times we couldn't allocate a rw block*/ + ushort *hashtab; /* hash table for quick dir scans */ + lstat_cpu_counts_t *counts[NR_CPUS]; /* Array of pointers to per-cpu stats */ + int next_free_read_lock_index; /* next rwlock reader (global) stats block */ + lstat_read_lock_cpu_counts_t *read_lock_counts[NR_CPUS]; /* per cpu read lock stats */ +} lstat_control_t; + +#endif /* defined(__KERNEL__) || defined(USER_MODE_TESTING) */ + +typedef struct { + short lstat_version; /* version of the data */ + short state; /* the current state is returned */ + int maxcpus; /* Number of cpus present */ + int next_free_dir_index; /* index of the next free directory entry */ + TIME_T first_started_time; /* when measurement enabled for first time */ + TIME_T started_time; /* time in secs since 1969 when stats last turned on */ + TIME_T ending_time; /* time in secs since 1969 when stats last turned off */ + uint32_t cycleval; /* cycles per second */ +#ifdef notyet + void *kernel_magic_addr; /* address of kernel_magic */ + void *kernel_end_addr; /* contents of kernel magic (points to "end") */ +#endif + int next_free_read_lock_index; /* index of next (global) read lock stats struct */ + uint64_t started_cycles64; /* cycles when measurement last started */ + uint64_t ending_cycles64; /* cycles when stats last turned off */ + uint64_t enabled_cycles64; /* total cycles with measurement enabled */ + int intervals; /* number of measurement intervals recorded */ + /* i.e. number of times we did lstat on;lstat off*/ + int dir_overflow; /* number of times we wanted more space in directory */ + int rwlock_overflow; /* # of times we wanted more space in read_locks_count */ + struct new_utsname uts; /* info about machine where stats are measured */ + /* -T option of lockstat allows data to be */ + /* moved to another machine. ................. */ +} lstat_user_request_t; + +#endif /* _LINUX_LOCKMETER_H */ --- linux-2.6.9-rc1/include/linux/mmc/host.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/include/linux/mmc/host.h 2004-09-03 00:56:32.169270176 -0700 @@ -64,12 +64,18 @@ struct device; struct mmc_host { struct device *dev; struct mmc_host_ops *ops; - void *priv; unsigned int f_min; unsigned int f_max; u32 ocr_avail; char host_name[8]; + /* host specific block data */ + unsigned int max_seg_size; /* see blk_queue_max_segment_size */ + unsigned short max_hw_segs; /* see blk_queue_max_hw_segments */ + unsigned short max_phys_segs; /* see blk_queue_max_phys_segments */ + unsigned short max_sectors; /* see blk_queue_max_sectors */ + unsigned short unused; + /* private data */ struct mmc_ios ios; /* current io bus settings */ u32 ocr; /* the current OCR setting */ --- linux-2.6.9-rc1/include/linux/mm.h 2004-08-24 00:01:52.000000000 -0700 +++ 25/include/linux/mm.h 2004-09-03 00:57:01.427822200 -0700 @@ -26,6 +26,12 @@ extern void * high_memory; extern unsigned long vmalloc_earlyreserve; extern int page_cluster; +#ifdef CONFIG_SYSCTL +extern int sysctl_legacy_va_layout; +#else +#define sysctl_legacy_va_layout 0 +#endif + #include #include #include @@ -195,21 +201,21 @@ struct page { page_flags_t flags; /* Atomic flags, some possibly * updated asynchronously */ atomic_t _count; /* Usage count, see below. */ - unsigned int mapcount; /* Count of ptes mapped in mms, + atomic_t _mapcount; /* Count of ptes mapped in mms, * to show when page is mapped - * & limit reverse map searches, - * protected by PG_maplock. + * & limit reverse map searches. */ unsigned long private; /* Mapping-private opaque data: * usually used for buffer_heads * if PagePrivate set; used for * swp_entry_t if PageSwapCache */ - struct address_space *mapping; /* If PG_anon clear, points to + struct address_space *mapping; /* If low bit clear, points to * inode address_space, or NULL. * If page mapped as anonymous - * memory, PG_anon is set, and - * it points to anon_vma object. + * memory, low bit is set, and + * it points to anon_vma object: + * see PAGE_MAPPING_ANON below. */ pgoff_t index; /* Our offset within mapping. */ struct list_head lru; /* Pageout list, eg. active_list @@ -433,24 +439,32 @@ void page_address_init(void); /* * On an anonymous page mapped into a user virtual memory area, - * page->mapping points to its anon_vma, not to a struct address_space. + * page->mapping points to its anon_vma, not to a struct address_space; + * with the PAGE_MAPPING_ANON bit set to distinguish it. * * Please note that, confusingly, "page_mapping" refers to the inode * address_space which maps the page from disk; whereas "page_mapped" * refers to user virtual address space into which the page is mapped. */ +#define PAGE_MAPPING_ANON 1 + extern struct address_space swapper_space; static inline struct address_space *page_mapping(struct page *page) { - struct address_space *mapping = NULL; + struct address_space *mapping = page->mapping; if (unlikely(PageSwapCache(page))) mapping = &swapper_space; - else if (likely(!PageAnon(page))) - mapping = page->mapping; + else if (unlikely((unsigned long)mapping & PAGE_MAPPING_ANON)) + mapping = NULL; return mapping; } +static inline int PageAnon(struct page *page) +{ + return ((unsigned long)page->mapping & PAGE_MAPPING_ANON) != 0; +} + /* * Return the pagecache index of the passed page. Regular pagecache pages * use ->index whereas swapcache pages use ->private @@ -463,11 +477,26 @@ static inline pgoff_t page_index(struct } /* + * The atomic page->_mapcount, like _count, starts from -1: + * so that transitions both from it and to it can be tracked, + * using atomic_inc_and_test and atomic_add_negative(-1). + */ +static inline void reset_page_mapcount(struct page *page) +{ + atomic_set(&(page)->_mapcount, -1); +} + +static inline int page_mapcount(struct page *page) +{ + return atomic_read(&(page)->_mapcount) + 1; +} + +/* * Return true if this page is mapped into pagetables. */ static inline int page_mapped(struct page *page) { - return page->mapcount != 0; + return atomic_read(&(page)->_mapcount) >= 0; } /* @@ -490,13 +519,21 @@ static inline int page_mapped(struct pag extern void show_free_areas(void); -struct page *shmem_nopage(struct vm_area_struct * vma, +#ifdef CONFIG_SHMEM +struct page *shmem_nopage(struct vm_area_struct *vma, unsigned long address, int *type); int shmem_set_policy(struct vm_area_struct *vma, struct mempolicy *new); struct mempolicy *shmem_get_policy(struct vm_area_struct *vma, unsigned long addr); -struct file *shmem_file_setup(char * name, loff_t size, unsigned long flags); int shmem_lock(struct file *file, int lock, struct user_struct *user); +#else +#define shmem_nopage filemap_nopage +#define shmem_lock(a, b, c) ({0;}) /* always in memory, no need to lock */ +#define shmem_set_policy(a, b) (0) +#define shmem_get_policy(a, b) (NULL) +#endif +struct file *shmem_file_setup(char *name, loff_t size, unsigned long flags); + int shmem_zero_setup(struct vm_area_struct *); static inline int can_do_mlock(void) @@ -599,11 +636,10 @@ static inline pmd_t *pmd_alloc(struct mm } extern void free_area_init(unsigned long * zones_size); -extern void free_area_init_node(int nid, pg_data_t *pgdat, struct page *pmap, +extern void free_area_init_node(int nid, pg_data_t *pgdat, unsigned long * zones_size, unsigned long zone_start_pfn, unsigned long *zholes_size); -extern void memmap_init_zone(struct page *, unsigned long, int, - unsigned long, unsigned long); +extern void memmap_init_zone(unsigned long, int, unsigned long, unsigned long); extern void mem_init(void); extern void show_mem(void); extern void si_meminfo(struct sysinfo * val); @@ -670,6 +706,8 @@ extern unsigned long do_brk(unsigned lon /* filemap.c */ extern unsigned long page_unuse(struct page *); extern void truncate_inode_pages(struct address_space *, loff_t); +extern void truncate_inode_pages_range(struct address_space *, + loff_t lstart, loff_t lend); /* generic vm_area_ops exported for stackable file systems */ struct page *filemap_nopage(struct vm_area_struct *, unsigned long, int *); @@ -725,6 +763,27 @@ extern struct page * follow_page(struct extern int remap_page_range(struct vm_area_struct *vma, unsigned long from, unsigned long to, unsigned long size, pgprot_t prot); +#ifdef CONFIG_PROC_FS +void __vm_stat_account(struct mm_struct *, unsigned long, struct file *, long); +#else +static inline void __vm_stat_account(struct mm_struct *mm, + unsigned long flags, struct file *file, long pages) +{ +} +#endif /* CONFIG_PROC_FS */ + +static inline void vm_stat_account(struct vm_area_struct *vma) +{ + __vm_stat_account(vma->vm_mm, vma->vm_flags, vma->vm_file, + vma_pages(vma)); +} + +static inline void vm_stat_unaccount(struct vm_area_struct *vma) +{ + __vm_stat_account(vma->vm_mm, vma->vm_flags, vma->vm_file, + -vma_pages(vma)); +} + #ifndef CONFIG_DEBUG_PAGEALLOC static inline void kernel_map_pages(struct page *page, int numpages, int enable) --- linux-2.6.9-rc1/include/linux/mmzone.h 2004-08-24 00:02:48.000000000 -0700 +++ 25/include/linux/mmzone.h 2004-09-03 00:56:51.390348128 -0700 @@ -272,6 +272,8 @@ typedef struct pglist_data { extern int numnodes; extern struct pglist_data *pgdat_list; +void __get_zone_counts(unsigned long *active, unsigned long *inactive, + unsigned long *free, struct pglist_data *pgdat); void get_zone_counts(unsigned long *active, unsigned long *inactive, unsigned long *free); void build_all_zonelists(void); @@ -408,35 +410,6 @@ extern struct pglist_data contig_page_da #error ZONES_SHIFT > MAX_ZONES_SHIFT #endif -extern DECLARE_BITMAP(node_online_map, MAX_NUMNODES); - -#if defined(CONFIG_DISCONTIGMEM) || defined(CONFIG_NUMA) - -#define node_online(node) test_bit(node, node_online_map) -#define node_set_online(node) set_bit(node, node_online_map) -#define node_set_offline(node) clear_bit(node, node_online_map) -static inline unsigned int num_online_nodes(void) -{ - int i, num = 0; - - for(i = 0; i < MAX_NUMNODES; i++){ - if (node_online(i)) - num++; - } - return num; -} - -#else /* !CONFIG_DISCONTIGMEM && !CONFIG_NUMA */ - -#define node_online(node) \ - ({ BUG_ON((node) != 0); test_bit(node, node_online_map); }) -#define node_set_online(node) \ - ({ BUG_ON((node) != 0); set_bit(node, node_online_map); }) -#define node_set_offline(node) \ - ({ BUG_ON((node) != 0); clear_bit(node, node_online_map); }) -#define num_online_nodes() 1 - -#endif /* CONFIG_DISCONTIGMEM || CONFIG_NUMA */ #endif /* !__ASSEMBLY__ */ #endif /* __KERNEL__ */ #endif /* _LINUX_MMZONE_H */ --- linux-2.6.9-rc1/include/linux/netdevice.h 2004-08-24 00:03:32.000000000 -0700 +++ 25/include/linux/netdevice.h 2004-09-03 00:56:32.634199496 -0700 @@ -304,7 +304,9 @@ struct net_device /* List of functions to handle Wireless Extensions (instead of ioctl). * See for details. Jean II */ - struct iw_handler_def * wireless_handlers; + const struct iw_handler_def * wireless_handlers; + /* Instance data managed by the core of Wireless Extensions. */ + struct iw_public_data * wireless_data; struct ethtool_ops *ethtool_ops; @@ -462,7 +464,7 @@ struct net_device unsigned char *haddr); int (*neigh_setup)(struct net_device *dev, struct neigh_parms *); int (*accept_fastpath)(struct net_device *, struct dst_entry*); -#ifdef CONFIG_NETPOLL_RX +#ifdef CONFIG_NETPOLL int netpoll_rx; #endif #ifdef CONFIG_NET_POLL_CONTROLLER --- linux-2.6.9-rc1/include/linux/netfilter_ipv4/ip_conntrack.h 2004-08-24 00:03:32.000000000 -0700 +++ 25/include/linux/netfilter_ipv4/ip_conntrack.h 2004-09-02 20:51:52.000000000 -0700 @@ -275,6 +275,7 @@ extern void (*ip_conntrack_destroyed)(st /* Fake conntrack entry for untracked connections */ extern struct ip_conntrack ip_conntrack_untracked; +extern int ip_ct_no_defrag; /* Returns new sk_buff, or NULL */ struct sk_buff * ip_ct_gather_frags(struct sk_buff *skb); --- linux-2.6.9-rc1/include/linux/netfilter_ipv4/ip_nat_helper.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/include/linux/netfilter_ipv4/ip_nat_helper.h 2004-09-02 20:51:52.000000000 -0700 @@ -44,6 +44,9 @@ extern void ip_nat_helper_unregister(str extern struct ip_nat_helper * ip_nat_find_helper(const struct ip_conntrack_tuple *tuple); +extern struct ip_nat_helper * +__ip_nat_find_helper(const struct ip_conntrack_tuple *tuple); + /* These return true or false. */ extern int ip_nat_mangle_tcp_packet(struct sk_buff **skb, struct ip_conntrack *ct, --- linux-2.6.9-rc1/include/linux/netfilter_ipv4/ipt_LOG.h 2004-08-24 00:03:32.000000000 -0700 +++ 25/include/linux/netfilter_ipv4/ipt_LOG.h 2004-09-03 00:56:35.508762496 -0700 @@ -11,5 +11,7 @@ struct ipt_log_info { unsigned char logflags; char prefix[30]; }; +void dump_packet(const struct ipt_log_info *info, const struct sk_buff *skb, + unsigned int iphoff); #endif /*_IPT_LOG_H*/ --- linux-2.6.9-rc1/include/linux/netfilter_ipv6.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/include/linux/netfilter_ipv6.h 2004-09-02 20:51:52.000000000 -0700 @@ -58,8 +58,10 @@ enum nf_ip6_hook_priorities { NF_IP6_PRI_FIRST = INT_MIN, NF_IP6_PRI_SELINUX_FIRST = -225, NF_IP6_PRI_CONNTRACK = -200, + NF_IP6_PRI_BRIDGE_SABOTAGE_FORWARD = -175, NF_IP6_PRI_MANGLE = -150, NF_IP6_PRI_NAT_DST = -100, + NF_IP6_PRI_BRIDGE_SABOTAGE_LOCAL_OUT = -50, NF_IP6_PRI_FILTER = 0, NF_IP6_PRI_NAT_SRC = 100, NF_IP6_PRI_SELINUX_LAST = 225, --- linux-2.6.9-rc1/include/linux/netfilter_ipv6/ip6t_REJECT.h 2004-08-24 00:01:51.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,16 +0,0 @@ -#ifndef _IP6T_REJECT_H -#define _IP6T_REJECT_H - -enum ip6t_reject_with { - IP6T_ICMP_NET_UNREACHABLE, - IP6T_ICMP_HOST_UNREACHABLE, - IP6T_ICMP_PROT_UNREACHABLE, - IP6T_ICMP_PORT_UNREACHABLE, - IP6T_ICMP_ECHOREPLY -}; - -struct ip6t_reject_info { - enum ip6t_reject_with with; /* reject type */ -}; - -#endif /*_IPT_REJECT_H*/ --- linux-2.6.9-rc1/include/linux/netlink.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/include/linux/netlink.h 2004-09-03 00:57:14.973762904 -0700 @@ -17,6 +17,7 @@ #define NETLINK_ROUTE6 11 /* af_inet6 route comm channel */ #define NETLINK_IP6_FW 13 #define NETLINK_DNRTMSG 14 /* DECnet routing messages */ +#define NETLINK_KEVENT 15 /* Kernel messages to userspace */ #define NETLINK_TAPBASE 16 /* 16 to 31 are ethertap */ #define MAX_LINKS 32 @@ -134,13 +135,6 @@ int netlink_attachskb(struct sock *sk, s void netlink_detachskb(struct sock *sk, struct sk_buff *skb); int netlink_sendskb(struct sock *sk, struct sk_buff *skb, int protocol); -/* finegrained unicast helpers: */ -struct sock *netlink_getsockbypid(struct sock *ssk, u32 pid); -struct sock *netlink_getsockbyfilp(struct file *filp); -int netlink_attachskb(struct sock *sk, struct sk_buff *skb, int nonblock, long timeo); -void netlink_detachskb(struct sock *sk, struct sk_buff *skb); -int netlink_sendskb(struct sock *sk, struct sk_buff *skb, int protocol); - /* * skb should fit one page. This choice is good for headerless malloc. * --- linux-2.6.9-rc1/include/linux/nfs_xdr.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/include/linux/nfs_xdr.h 2004-09-02 20:51:52.000000000 -0700 @@ -361,7 +361,8 @@ struct nfs_diropok { struct nfs_readlinkargs { struct nfs_fh * fh; - unsigned int count; + unsigned int pgbase; + unsigned int pglen; struct page ** pages; }; @@ -455,7 +456,8 @@ struct nfs3_accessres { struct nfs3_readlinkargs { struct nfs_fh * fh; - unsigned int count; + unsigned int pgbase; + unsigned int pglen; struct page ** pages; }; @@ -570,7 +572,8 @@ struct nfs4_readdir_res { struct nfs4_readlink { const struct nfs_fh * fh; - u32 count; /* zero-copy data */ + unsigned int pgbase; + unsigned int pglen; /* zero-copy data */ struct page ** pages; /* zero-copy data */ }; @@ -673,7 +676,8 @@ struct nfs_rpc_ops { int (*lookup) (struct inode *, struct qstr *, struct nfs_fh *, struct nfs_fattr *); int (*access) (struct inode *, struct nfs_access_entry *); - int (*readlink)(struct inode *, struct page *); + int (*readlink)(struct inode *, struct page *, unsigned int, + unsigned int); int (*read) (struct nfs_read_data *); int (*write) (struct nfs_write_data *); int (*commit) (struct nfs_write_data *); --- linux-2.6.9-rc1/include/linux/nls.h 2004-08-24 00:02:25.000000000 -0700 +++ 25/include/linux/nls.h 2004-09-03 00:57:15.787639176 -0700 @@ -33,6 +33,31 @@ extern int utf8_mbstowcs(wchar_t *, cons extern int utf8_wctomb(__u8 *, wchar_t, int); extern int utf8_wcstombs(__u8 *, const wchar_t *, int); +static inline unsigned char nls_tolower(struct nls_table *t, unsigned char c) +{ + unsigned char nc = t->charset2lower[c]; + + return nc ? nc : c; +} + +static inline unsigned char nls_toupper(struct nls_table *t, unsigned char c) +{ + unsigned char nc = t->charset2upper[c]; + + return nc ? nc : c; +} + +static inline int nls_strnicmp(struct nls_table *t, const unsigned char *s1, + const unsigned char *s2, int len) +{ + while (len--) { + if (nls_tolower(t, *s1++) != nls_tolower(t, *s2++)) + return 1; + } + + return 0; +} + #define MODULE_ALIAS_NLS(name) MODULE_ALIAS("nls_" __stringify(name)) #endif /* _LINUX_NLS_H */ --- linux-2.6.9-rc1/include/linux/node.h 2004-08-24 00:03:38.000000000 -0700 +++ 25/include/linux/node.h 2004-09-02 20:51:52.000000000 -0700 @@ -23,7 +23,6 @@ #include struct node { - cpumask_t cpumap; /* Bitmap of CPUs on the Node */ struct sys_device sysdev; }; --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/linux/nodemask.h 2004-09-03 00:57:09.395610912 -0700 @@ -0,0 +1,344 @@ +#ifndef __LINUX_NODEMASK_H +#define __LINUX_NODEMASK_H + +/* + * Nodemasks provide a bitmap suitable for representing the + * set of Node's in a system, one bit position per Node number. + * + * See detailed comments in the file linux/bitmap.h describing the + * data type on which these nodemasks are based. + * + * For details of nodemask_scnprintf() and nodemask_parse(), + * see bitmap_scnprintf() and bitmap_parse() in lib/bitmap.c. + * For details of nodelist_scnprintf() and nodelist_parse(), see + * bitmap_scnlistprintf() and bitmap_parselist(), also in bitmap.c. + * + * The available nodemask operations are: + * + * void node_set(node, mask) turn on bit 'node' in mask + * void node_clear(node, mask) turn off bit 'node' in mask + * void nodes_setall(mask) set all bits + * void nodes_clear(mask) clear all bits + * int node_isset(node, mask) true iff bit 'node' set in mask + * int node_test_and_set(node, mask) test and set bit 'node' in mask + * + * void nodes_and(dst, src1, src2) dst = src1 & src2 [intersection] + * void nodes_or(dst, src1, src2) dst = src1 | src2 [union] + * void nodes_xor(dst, src1, src2) dst = src1 ^ src2 + * void nodes_andnot(dst, src1, src2) dst = src1 & ~src2 + * void nodes_complement(dst, src) dst = ~src + * + * int nodes_equal(mask1, mask2) Does mask1 == mask2? + * int nodes_intersects(mask1, mask2) Do mask1 and mask2 intersect? + * int nodes_subset(mask1, mask2) Is mask1 a subset of mask2? + * int nodes_empty(mask) Is mask empty (no bits sets)? + * int nodes_full(mask) Is mask full (all bits sets)? + * int nodes_weight(mask) Hamming weight - number of set bits + * + * void nodes_shift_right(dst, src, n) Shift right + * void nodes_shift_left(dst, src, n) Shift left + * + * int first_node(mask) Number lowest set bit, or MAX_NUMNODES + * int next_node(node, mask) Next node past 'node', or MAX_NUMNODES + * + * nodemask_t nodemask_of_node(node) Return nodemask with bit 'node' set + * NODE_MASK_ALL Initializer - all bits set + * NODE_MASK_NONE Initializer - no bits set + * unsigned long *nodes_addr(mask) Array of unsigned long's in mask + * + * int nodemask_scnprintf(buf, len, mask) Format nodemask for printing + * int nodemask_parse(ubuf, ulen, mask) Parse ascii string as nodemask + * int nodelist_scnprintf(buf, len, mask) Format nodemask as list for printing + * int nodelist_parse(buf, map) Parse ascii string as nodelist + * + * for_each_node_mask(node, mask) for-loop node over mask + * + * int num_online_nodes() Number of online Nodes + * int num_possible_nodes() Number of all possible Nodes + * + * int node_online(node) Is some node online? + * int node_possible(node) Is some node possible? + * + * int any_online_node(mask) First online node in mask + * + * node_set_online(node) set bit 'node' in node_online_map + * node_set_offline(node) clear bit 'node' in node_online_map + * + * for_each_node(node) for-loop node over node_possible_map + * for_each_online_node(node) for-loop node over node_online_map + * + * Subtlety: + * 1) The 'type-checked' form of node_isset() causes gcc (3.3.2, anyway) + * to generate slightly worse code. So use a simple one-line #define + * for node_isset(), instead of wrapping an inline inside a macro, the + * way we do the other calls. + */ + +#include +#include +#include +#include +#include + +typedef struct { DECLARE_BITMAP(bits, MAX_NUMNODES); } nodemask_t; +extern nodemask_t _unused_nodemask_arg_; + +#define node_set(node, dst) __node_set((node), &(dst)) +static inline void __node_set(int node, volatile nodemask_t *dstp) +{ + set_bit(node, dstp->bits); +} + +#define node_clear(node, dst) __node_clear((node), &(dst)) +static inline void __node_clear(int node, volatile nodemask_t *dstp) +{ + clear_bit(node, dstp->bits); +} + +#define nodes_setall(dst) __nodes_setall(&(dst), MAX_NUMNODES) +static inline void __nodes_setall(nodemask_t *dstp, int nbits) +{ + bitmap_fill(dstp->bits, nbits); +} + +#define nodes_clear(dst) __nodes_clear(&(dst), MAX_NUMNODES) +static inline void __nodes_clear(nodemask_t *dstp, int nbits) +{ + bitmap_zero(dstp->bits, nbits); +} + +/* No static inline type checking - see Subtlety (1) above. */ +#define node_isset(node, nodemask) test_bit((node), (nodemask).bits) + +#define node_test_and_set(node, nodemask) \ + __node_test_and_set((node), &(nodemask)) +static inline int __node_test_and_set(int node, nodemask_t *addr) +{ + return test_and_set_bit(node, addr->bits); +} + +#define nodes_and(dst, src1, src2) \ + __nodes_and(&(dst), &(src1), &(src2), MAX_NUMNODES) +static inline void __nodes_and(nodemask_t *dstp, const nodemask_t *src1p, + const nodemask_t *src2p, int nbits) +{ + bitmap_and(dstp->bits, src1p->bits, src2p->bits, nbits); +} + +#define nodes_or(dst, src1, src2) \ + __nodes_or(&(dst), &(src1), &(src2), MAX_NUMNODES) +static inline void __nodes_or(nodemask_t *dstp, const nodemask_t *src1p, + const nodemask_t *src2p, int nbits) +{ + bitmap_or(dstp->bits, src1p->bits, src2p->bits, nbits); +} + +#define nodes_xor(dst, src1, src2) \ + __nodes_xor(&(dst), &(src1), &(src2), MAX_NUMNODES) +static inline void __nodes_xor(nodemask_t *dstp, const nodemask_t *src1p, + const nodemask_t *src2p, int nbits) +{ + bitmap_xor(dstp->bits, src1p->bits, src2p->bits, nbits); +} + +#define nodes_andnot(dst, src1, src2) \ + __nodes_andnot(&(dst), &(src1), &(src2), MAX_NUMNODES) +static inline void __nodes_andnot(nodemask_t *dstp, const nodemask_t *src1p, + const nodemask_t *src2p, int nbits) +{ + bitmap_andnot(dstp->bits, src1p->bits, src2p->bits, nbits); +} + +#define nodes_complement(dst, src) \ + __nodes_complement(&(dst), &(src), MAX_NUMNODES) +static inline void __nodes_complement(nodemask_t *dstp, + const nodemask_t *srcp, int nbits) +{ + bitmap_complement(dstp->bits, srcp->bits, nbits); +} + +#define nodes_equal(src1, src2) \ + __nodes_equal(&(src1), &(src2), MAX_NUMNODES) +static inline int __nodes_equal(const nodemask_t *src1p, + const nodemask_t *src2p, int nbits) +{ + return bitmap_equal(src1p->bits, src2p->bits, nbits); +} + +#define nodes_intersects(src1, src2) \ + __nodes_intersects(&(src1), &(src2), MAX_NUMNODES) +static inline int __nodes_intersects(const nodemask_t *src1p, + const nodemask_t *src2p, int nbits) +{ + return bitmap_intersects(src1p->bits, src2p->bits, nbits); +} + +#define nodes_subset(src1, src2) \ + __nodes_subset(&(src1), &(src2), MAX_NUMNODES) +static inline int __nodes_subset(const nodemask_t *src1p, + const nodemask_t *src2p, int nbits) +{ + return bitmap_subset(src1p->bits, src2p->bits, nbits); +} + +#define nodes_empty(src) __nodes_empty(&(src), MAX_NUMNODES) +static inline int __nodes_empty(const nodemask_t *srcp, int nbits) +{ + return bitmap_empty(srcp->bits, nbits); +} + +#define nodes_full(nodemask) __nodes_full(&(nodemask), MAX_NUMNODES) +static inline int __nodes_full(const nodemask_t *srcp, int nbits) +{ + return bitmap_full(srcp->bits, nbits); +} + +#define nodes_weight(nodemask) __nodes_weight(&(nodemask), MAX_NUMNODES) +static inline int __nodes_weight(const nodemask_t *srcp, int nbits) +{ + return bitmap_weight(srcp->bits, nbits); +} + +#define nodes_shift_right(dst, src, n) \ + __nodes_shift_right(&(dst), &(src), (n), MAX_NUMNODES) +static inline void __nodes_shift_right(nodemask_t *dstp, + const nodemask_t *srcp, int n, int nbits) +{ + bitmap_shift_right(dstp->bits, srcp->bits, n, nbits); +} + +#define nodes_shift_left(dst, src, n) \ + __nodes_shift_left(&(dst), &(src), (n), MAX_NUMNODES) +static inline void __nodes_shift_left(nodemask_t *dstp, + const nodemask_t *srcp, int n, int nbits) +{ + bitmap_shift_left(dstp->bits, srcp->bits, n, nbits); +} + +#define first_node(src) __first_node(&(src), MAX_NUMNODES) +static inline int __first_node(const nodemask_t *srcp, int nbits) +{ + return min_t(int, nbits, find_first_bit(srcp->bits, nbits)); +} + +#define next_node(n, src) __next_node((n), &(src), MAX_NUMNODES) +static inline int __next_node(int n, const nodemask_t *srcp, int nbits) +{ + return min_t(int, nbits, find_next_bit(srcp->bits, nbits, n+1)); +} + +#define nodemask_of_node(node) \ +({ \ + typeof(_unused_nodemask_arg_) m; \ + if (sizeof(m) == sizeof(unsigned long)) { \ + m.bits[0] = 1UL<<(node); \ + } else { \ + nodes_clear(m); \ + node_set((node), m); \ + } \ + m; \ +}) + +#define NODE_MASK_LAST_WORD BITMAP_LAST_WORD_MASK(MAX_NUMNODES) + +#if MAX_NUMNODES <= BITS_PER_LONG + +#define NODE_MASK_ALL \ +((nodemask_t) { { \ + [BITS_TO_LONGS(MAX_NUMNODES)-1] = NODE_MASK_LAST_WORD \ +} }) + +#else + +#define NODE_MASK_ALL \ +((nodemask_t) { { \ + [0 ... BITS_TO_LONGS(MAX_NUMNODES)-2] = ~0UL, \ + [BITS_TO_LONGS(MAX_NUMNODES)-1] = NODE_MASK_LAST_WORD \ +} }) + +#endif + +#define NODE_MASK_NONE \ +((nodemask_t) { { \ + [0 ... BITS_TO_LONGS(MAX_NUMNODES)-1] = 0UL \ +} }) + +#define nodes_addr(src) ((src).bits) + +#define nodemask_scnprintf(buf, len, src) \ + __nodemask_scnprintf((buf), (len), &(src), MAX_NUMNODES) +static inline int __nodemask_scnprintf(char *buf, int len, + const nodemask_t *srcp, int nbits) +{ + return bitmap_scnprintf(buf, len, srcp->bits, nbits); +} + +#define nodemask_parse(ubuf, ulen, dst) \ + __nodemask_parse((ubuf), (ulen), &(dst), MAX_NUMNODES) +static inline int __nodemask_parse(const char __user *buf, int len, + nodemask_t *dstp, int nbits) +{ + return bitmap_parse(buf, len, dstp->bits, nbits); +} + +#define nodelist_scnprintf(buf, len, src) \ + __nodelist_scnprintf((buf), (len), &(src), MAX_NUMNODES) +static inline int __nodelist_scnprintf(char *buf, int len, + const nodemask_t *srcp, int nbits) +{ + return bitmap_scnlistprintf(buf, len, srcp->bits, nbits); +} + +#define nodelist_parse(buf, dst) __nodelist_parse((buf), &(dst), MAX_NUMNODES) +static inline int __nodelist_parse(const char *buf, nodemask_t *dstp, int nbits) +{ + return bitmap_parselist(buf, dstp->bits, nbits); +} + +#if MAX_NUMNODES > 1 +#define for_each_node_mask(node, mask) \ + for ((node) = first_node(mask); \ + (node) < MAX_NUMNODES; \ + (node) = next_node((node), (mask))) +#else /* MAX_NUMNODES == 1 */ +#define for_each_node_mask(node, mask) \ + if (!nodes_empty(mask)) \ + for ((node) = 0; (node) < 1; (node)++) +#endif /* MAX_NUMNODES */ + +/* + * The following particular system nodemasks and operations + * on them manage all possible and online nodes. + */ + +extern nodemask_t node_online_map; +extern nodemask_t node_possible_map; + +#if MAX_NUMNODES > 1 +#define num_online_nodes() nodes_weight(node_online_map) +#define num_possible_nodes() nodes_weight(node_possible_map) +#define node_online(node) node_isset((node), node_online_map) +#define node_possible(node) node_isset((node), node_possible_map) +#else +#define num_online_nodes() 1 +#define num_possible_nodes() 1 +#define node_online(node) ((node) == 0) +#define node_possible(node) ((node) == 0) +#endif + +#define any_online_node(mask) \ +({ \ + int node; \ + for_each_node_mask(node, (mask)) \ + if (node_online(node)) \ + break; \ + node; \ +}) + +#define node_set_online(node) set_bit((node), node_online_map.bits) +#define node_set_offline(node) clear_bit((node), node_online_map.bits) + +#define for_each_node(node) for_each_node_mask((node), node_possible_map) +#define for_each_online_node(node) for_each_node_mask((node), node_online_map) + +#endif /* __LINUX_NODEMASK_H */ --- linux-2.6.9-rc1/include/linux/page-flags.h 2004-08-24 00:03:18.000000000 -0700 +++ 25/include/linux/page-flags.h 2004-09-02 20:51:52.000000000 -0700 @@ -69,14 +69,11 @@ #define PG_private 12 /* Has something at ->private */ #define PG_writeback 13 /* Page is under writeback */ #define PG_nosave 14 /* Used for system suspend/resume */ -#define PG_maplock 15 /* Lock bit for rmap to ptes */ +#define PG_compound 15 /* Part of a compound page */ #define PG_swapcache 16 /* Swap page: swp_entry_t in private */ #define PG_mappedtodisk 17 /* Has blocks allocated on-disk */ #define PG_reclaim 18 /* To be reclaimed asap */ -#define PG_compound 19 /* Part of a compound page */ - -#define PG_anon 20 /* Anonymous: anon_vma in mapping */ /* @@ -236,6 +233,7 @@ extern unsigned long __read_page_state(u #define PageReserved(page) test_bit(PG_reserved, &(page)->flags) #define SetPageReserved(page) set_bit(PG_reserved, &(page)->flags) #define ClearPageReserved(page) clear_bit(PG_reserved, &(page)->flags) +#define __ClearPageReserved(page) __clear_bit(PG_reserved, &(page)->flags) #define SetPagePrivate(page) set_bit(PG_private, &(page)->flags) #define ClearPagePrivate(page) clear_bit(PG_private, &(page)->flags) @@ -292,10 +290,6 @@ extern unsigned long __read_page_state(u #define SetPageCompound(page) set_bit(PG_compound, &(page)->flags) #define ClearPageCompound(page) clear_bit(PG_compound, &(page)->flags) -#define PageAnon(page) test_bit(PG_anon, &(page)->flags) -#define SetPageAnon(page) set_bit(PG_anon, &(page)->flags) -#define ClearPageAnon(page) clear_bit(PG_anon, &(page)->flags) - #ifdef CONFIG_SWAP #define PageSwapCache(page) test_bit(PG_swapcache, &(page)->flags) #define SetPageSwapCache(page) set_bit(PG_swapcache, &(page)->flags) --- linux-2.6.9-rc1/include/linux/pagemap.h 2004-08-24 00:01:51.000000000 -0700 +++ 25/include/linux/pagemap.h 2004-09-02 20:51:52.000000000 -0700 @@ -156,6 +156,7 @@ extern void FASTCALL(unlock_page(struct static inline void lock_page(struct page *page) { + might_sleep(); if (TestSetPageLocked(page)) __lock_page(page); } --- linux-2.6.9-rc1/include/linux/pci.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/include/linux/pci.h 2004-09-03 00:56:53.809980288 -0700 @@ -712,17 +712,13 @@ struct resource *pci_find_parent_resourc int pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge); extern struct pci_dev *pci_dev_get(struct pci_dev *dev); extern void pci_dev_put(struct pci_dev *dev); - +extern void pci_remove_bus(struct pci_bus *b); extern void pci_remove_bus_device(struct pci_dev *dev); /* Generic PCI functions exported to card drivers */ struct pci_dev *pci_find_device (unsigned int vendor, unsigned int device, const struct pci_dev *from); struct pci_dev *pci_find_device_reverse (unsigned int vendor, unsigned int device, const struct pci_dev *from); -struct pci_dev *pci_find_subsys (unsigned int vendor, unsigned int device, - unsigned int ss_vendor, unsigned int ss_device, - const struct pci_dev *from); -struct pci_dev *pci_find_class (unsigned int class, const struct pci_dev *from); struct pci_dev *pci_find_slot (unsigned int bus, unsigned int devfn); int pci_find_capability (struct pci_dev *dev, int cap); int pci_find_ext_capability (struct pci_dev *dev, int cap); @@ -733,6 +729,7 @@ struct pci_dev *pci_get_subsys (unsigned unsigned int ss_vendor, unsigned int ss_device, struct pci_dev *from); struct pci_dev *pci_get_slot (struct pci_bus *bus, unsigned int devfn); +struct pci_dev *pci_get_class (unsigned int class, struct pci_dev *from); int pci_bus_read_config_byte (struct pci_bus *bus, unsigned int devfn, int where, u8 *val); int pci_bus_read_config_word (struct pci_bus *bus, unsigned int devfn, int where, u16 *val); @@ -882,16 +879,9 @@ _PCI_NOP_ALL(write,) static inline struct pci_dev *pci_find_device(unsigned int vendor, unsigned int device, const struct pci_dev *from) { return NULL; } -static inline struct pci_dev *pci_find_class(unsigned int class, const struct pci_dev *from) -{ return NULL; } - static inline struct pci_dev *pci_find_slot(unsigned int bus, unsigned int devfn) { return NULL; } -static inline struct pci_dev *pci_find_subsys(unsigned int vendor, unsigned int device, -unsigned int ss_vendor, unsigned int ss_device, const struct pci_dev *from) -{ return NULL; } - static inline struct pci_dev *pci_get_device (unsigned int vendor, unsigned int device, struct pci_dev *from) { return NULL; } @@ -899,6 +889,9 @@ static inline struct pci_dev *pci_get_su unsigned int ss_vendor, unsigned int ss_device, struct pci_dev *from) { return NULL; } +static inline struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from) +{ return NULL; } + static inline void pci_set_master(struct pci_dev *dev) { } static inline int pci_enable_device(struct pci_dev *dev) { return -EIO; } static inline void pci_disable_device(struct pci_dev *dev) { } @@ -1008,6 +1001,7 @@ struct pci_fixup { enum pci_fixup_pass { pci_fixup_header, /* Called immediately after reading configuration header */ pci_fixup_final, /* Final phase of device fixups */ + pci_fixup_enable, /* pci_enable_device() time */ }; /* Anonymous variables would be nice... */ @@ -1021,6 +1015,12 @@ enum pci_fixup_pass { __attribute__((__section__(".pci_fixup_final"))) = { \ vendor, device, hook }; +#define DECLARE_PCI_FIXUP_ENABLE(vendor, device, hook) \ + static struct pci_fixup __pci_fixup_##vendor##device##hook __attribute_used__ \ + __attribute__((__section__(".pci_fixup_enable"))) = { \ + vendor, device, hook }; + + void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev); extern int pci_pci_problems; --- linux-2.6.9-rc1/include/linux/pci_ids.h 2004-08-24 00:03:50.000000000 -0700 +++ 25/include/linux/pci_ids.h 2004-09-03 00:56:52.414192480 -0700 @@ -438,6 +438,7 @@ #define PCI_DEVICE_ID_IBM_405GP 0x0156 #define PCI_DEVICE_ID_IBM_SNIPE 0x0180 #define PCI_DEVICE_ID_IBM_SERVERAIDI960 0x01bd +#define PCI_DEVICE_ID_IBM_CITRINE 0x028C #define PCI_DEVICE_ID_IBM_GEMSTONE 0xB166 #define PCI_DEVICE_ID_IBM_MPIC_2 0xffff #define PCI_DEVICE_ID_IBM_ICOM_DEV_ID_1 0x0031 @@ -1077,6 +1078,7 @@ #define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2 0x0055 #define PCI_DEVICE_ID_NVIDIA_NVENET_8 0x0056 #define PCI_DEVICE_ID_NVIDIA_NVENET_9 0x0057 +#define PCI_DEVICE_ID_NVIDIA_CK804_AUDIO 0x0059 #define PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE 0x0065 #define PCI_DEVICE_ID_NVIDIA_NVENET_2 0x0066 #define PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO 0x006a @@ -1645,6 +1647,7 @@ #define PCI_VENDOR_ID_ITE 0x1283 #define PCI_DEVICE_ID_ITE_IT8172G 0x8172 #define PCI_DEVICE_ID_ITE_IT8172G_AUDIO 0x0801 +#define PCI_DEVICE_ID_ITE_8212 0x8212 #define PCI_DEVICE_ID_ITE_8872 0x8872 #define PCI_DEVICE_ID_ITE_IT8330G_0 0xe886 @@ -1918,6 +1921,7 @@ #define PCI_DEVICE_ID_BCM4401 0x4401 #define PCI_DEVICE_ID_BCM4401B0 0x4402 #define PCI_DEVICE_ID_BCM4401B1 0x170c +#define PCI_DEVICE_ID_BCM4713 0x4713 #define PCI_VENDOR_ID_ENE 0x1524 #define PCI_DEVICE_ID_ENE_1211 0x1211 @@ -2313,6 +2317,8 @@ #define PCI_DEVICE_ID_NETMOS_9815 0x9815 #define PCI_DEVICE_ID_NETMOS_9835 0x9835 #define PCI_DEVICE_ID_NETMOS_9855 0x9855 +#define PCI_DEVICE_ID_NETMOS_9755 0x9755 +#define PCI_DEVICE_ID_NETMOS_9715 0x9715 #define PCI_SUBVENDOR_ID_EXSYS 0xd84d #define PCI_SUBDEVICE_ID_EXSYS_4014 0x4014 --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/linux/perfctr.h 2004-09-03 00:56:46.515089280 -0700 @@ -0,0 +1,171 @@ +/* $Id: perfctr.h,v 1.78 2004/05/31 20:45:51 mikpe Exp $ + * Performance-Monitoring Counters driver + * + * Copyright (C) 1999-2004 Mikael Pettersson + */ +#ifndef _LINUX_PERFCTR_H +#define _LINUX_PERFCTR_H + +#ifdef CONFIG_PERFCTR /* don't break archs without */ + +#include + +struct perfctr_info { + unsigned int abi_version; + char driver_version[32]; + unsigned int cpu_type; + unsigned int cpu_features; + unsigned int cpu_khz; + unsigned int tsc_to_cpu_mult; + unsigned int _reserved2; + unsigned int _reserved3; + unsigned int _reserved4; +}; + +struct perfctr_cpu_mask { + unsigned int nrwords; + unsigned int mask[1]; /* actually 'nrwords' */ +}; + +/* abi_version values: Lower 16 bits contain the CPU data version, upper + 16 bits contain the API version. Each half has a major version in its + upper 8 bits, and a minor version in its lower 8 bits. */ +#define PERFCTR_API_VERSION 0x0600 /* 6.0 */ +#define PERFCTR_ABI_VERSION ((PERFCTR_API_VERSION<<16)|PERFCTR_CPU_VERSION) + +/* cpu_features flag bits */ +#define PERFCTR_FEATURE_RDPMC 0x01 +#define PERFCTR_FEATURE_RDTSC 0x02 +#define PERFCTR_FEATURE_PCINT 0x04 + +/* user's view of mmap:ed virtual perfctr */ +struct vperfctr_state { + struct perfctr_cpu_state cpu_state; +}; + +/* virtual perfctr control object */ +struct vperfctr_control { + int si_signo; + struct perfctr_cpu_control cpu_control; + unsigned int preserve; + unsigned int _reserved1; + unsigned int _reserved2; + unsigned int _reserved3; + unsigned int _reserved4; +}; + +#else +struct perfctr_info; +struct perfctr_cpu_mask; +struct perfctr_sum_ctrs; +struct vperfctr_control; +#endif /* CONFIG_PERFCTR */ + +#ifdef __KERNEL__ + +/* + * The perfctr system calls. + */ +asmlinkage long sys_perfctr_info(struct perfctr_info __user*, + struct perfctr_cpu_mask __user*, + struct perfctr_cpu_mask __user*); +asmlinkage long sys_vperfctr_open(int tid, int creat); +asmlinkage long sys_vperfctr_control(int fd, + const struct vperfctr_control __user*); +asmlinkage long sys_vperfctr_unlink(int fd); +asmlinkage long sys_vperfctr_iresume(int fd); +asmlinkage long sys_vperfctr_read(int fd, + struct perfctr_sum_ctrs __user*, + struct vperfctr_control __user*, + struct perfctr_sum_ctrs __user*); + +extern struct perfctr_info perfctr_info; + +#ifdef CONFIG_PERFCTR_VIRTUAL + +/* + * Virtual per-process performance-monitoring counters. + */ +struct vperfctr; /* opaque */ + +/* process management operations */ +extern void __vperfctr_copy(struct task_struct*, struct pt_regs*); +extern void __vperfctr_release(struct task_struct*); +extern void __vperfctr_exit(struct vperfctr*); +extern void __vperfctr_suspend(struct vperfctr*); +extern void __vperfctr_resume(struct vperfctr*); +extern void __vperfctr_sample(struct vperfctr*); +extern void __vperfctr_set_cpus_allowed(struct task_struct*, struct vperfctr*, cpumask_t); + +static inline void perfctr_copy_task(struct task_struct *tsk, struct pt_regs *regs) +{ + if (tsk->thread.perfctr) + __vperfctr_copy(tsk, regs); +} + +static inline void perfctr_release_task(struct task_struct *tsk) +{ + if (tsk->thread.perfctr) + __vperfctr_release(tsk); +} + +static inline void perfctr_exit_thread(struct thread_struct *thread) +{ + struct vperfctr *perfctr; + perfctr = thread->perfctr; + if (perfctr) + __vperfctr_exit(perfctr); +} + +static inline void perfctr_suspend_thread(struct thread_struct *prev) +{ + struct vperfctr *perfctr; + perfctr = prev->perfctr; + if (perfctr) + __vperfctr_suspend(perfctr); +} + +static inline void perfctr_resume_thread(struct thread_struct *next) +{ + struct vperfctr *perfctr; + perfctr = next->perfctr; + if (perfctr) + __vperfctr_resume(perfctr); +} + +static inline void perfctr_sample_thread(struct thread_struct *thread) +{ + struct vperfctr *perfctr; + perfctr = thread->perfctr; + if (perfctr) + __vperfctr_sample(perfctr); +} + +static inline void perfctr_set_cpus_allowed(struct task_struct *p, cpumask_t new_mask) +{ +#ifdef CONFIG_PERFCTR_CPUS_FORBIDDEN_MASK + struct vperfctr *perfctr; + + task_lock(p); + perfctr = p->thread.perfctr; + if (perfctr) + __vperfctr_set_cpus_allowed(p, perfctr, new_mask); + task_unlock(p); +#endif +} + +#else /* !CONFIG_PERFCTR_VIRTUAL */ + +static inline void perfctr_copy_task(struct task_struct *p, struct pt_regs *r) { } +static inline void perfctr_release_task(struct task_struct *p) { } +static inline void perfctr_exit_thread(struct thread_struct *t) { } +static inline void perfctr_suspend_thread(struct thread_struct *t) { } +static inline void perfctr_resume_thread(struct thread_struct *t) { } +static inline void perfctr_sample_thread(struct thread_struct *t) { } +static inline void perfctr_set_cpus_allowed(struct task_struct *p, cpumask_t m) { } + +#endif /* CONFIG_PERFCTR_VIRTUAL */ + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_PERFCTR_H */ --- linux-2.6.9-rc1/include/linux/personality.h 2004-08-24 00:02:47.000000000 -0700 +++ 25/include/linux/personality.h 2004-09-03 00:57:16.181579288 -0700 @@ -12,17 +12,6 @@ extern int register_exec_domain(struct extern int unregister_exec_domain(struct exec_domain *); extern int __set_personality(unsigned long); - -/* - * Sysctl variables related to binary emulation. - */ -extern unsigned long abi_defhandler_coff; -extern unsigned long abi_defhandler_elf; -extern unsigned long abi_defhandler_lcall7; -extern unsigned long abi_defhandler_libcso; -extern int abi_fake_utsname; - - /* * Flags for bug emulation. * @@ -30,6 +19,7 @@ extern int abi_fake_utsname; */ enum { MMAP_PAGE_ZERO = 0x0100000, + ADDR_COMPAT_LAYOUT = 0x0200000, READ_IMPLIES_EXEC = 0x0400000, ADDR_LIMIT_32BIT = 0x0800000, SHORT_INODE = 0x1000000, --- linux-2.6.9-rc1/include/linux/pid.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/include/linux/pid.h 2004-09-02 20:51:52.000000000 -0700 @@ -12,34 +12,22 @@ enum pid_type struct pid { + /* Try to keep pid_chain in the same cacheline as nr for find_pid */ int nr; - atomic_t count; - struct task_struct *task; - struct list_head task_list; - struct list_head hash_chain; -}; - -struct pid_link -{ - struct list_head pid_chain; - struct pid *pidptr; - struct pid pid; + struct hlist_node pid_chain; + /* list of pids with the same nr, only one of them is in the hash */ + struct list_head pid_list; }; #define pid_task(elem, type) \ - list_entry(elem, struct task_struct, pids[type].pid_chain) + list_entry(elem, struct task_struct, pids[type].pid_list) /* - * attach_pid() and link_pid() must be called with the tasklist_lock + * attach_pid() and detach_pid() must be called with the tasklist_lock * write-held. */ extern int FASTCALL(attach_pid(struct task_struct *task, enum pid_type type, int nr)); -extern void FASTCALL(link_pid(struct task_struct *task, struct pid_link *link, struct pid *pid)); - -/* - * detach_pid() must be called with the tasklist_lock write-held. - */ extern void FASTCALL(detach_pid(struct task_struct *task, enum pid_type)); /* @@ -52,13 +40,16 @@ extern int alloc_pidmap(void); extern void FASTCALL(free_pidmap(int)); extern void switch_exec_pids(struct task_struct *leader, struct task_struct *thread); -#define for_each_task_pid(who, type, task, elem, pid) \ - if ((pid = find_pid(type, who))) \ - for (elem = pid->task_list.next, \ - prefetch(elem->next), \ - task = pid_task(elem, type); \ - elem != &pid->task_list; \ - elem = elem->next, prefetch(elem->next), \ - task = pid_task(elem, type)) +#define do_each_task_pid(who, type, task) \ + if ((task = find_task_by_pid_type(type, who))) { \ + prefetch((task)->pids[type].pid_list.next); \ + do { + +#define while_each_task_pid(who, type, task) \ + task = pid_task((task)->pids[type].pid_list.next,\ + type); \ + prefetch((task)->pids[type].pid_list.next); \ + } while (hlist_unhashed(&(task)->pids[type].pid_chain));\ + } \ #endif /* _LINUX_PID_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/linux/pktcdvd.h 2004-09-03 00:56:50.407497544 -0700 @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2000 Jens Axboe + * Copyright (C) 2001-2004 Peter Osterlund + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * Packet writing layer for ATAPI and SCSI CD-R, CD-RW, DVD-R, and + * DVD-RW devices. + * + */ +#ifndef __PKTCDVD_H +#define __PKTCDVD_H + +#include + +/* + * 1 for normal debug messages, 2 is very verbose. 0 to turn it off. + */ +#define PACKET_DEBUG 1 + +#define MAX_WRITERS 8 + +#define PKT_RB_POOL_SIZE 512 + +/* + * How long we should hold a non-full packet before starting data gathering. + */ +#define PACKET_WAIT_TIME (HZ * 5 / 1000) + +/* + * use drive write caching -- we need deferred error handling to be + * able to sucessfully recover with this option (drive will return good + * status as soon as the cdb is validated). + */ +#if defined(CONFIG_CDROM_PKTCDVD_WCACHE) +#define USE_WCACHING 1 +#else +#define USE_WCACHING 0 +#endif + +/* + * No user-servicable parts beyond this point -> + */ + +/* + * device types + */ +#define PACKET_CDR 1 +#define PACKET_CDRW 2 +#define PACKET_DVDR 3 +#define PACKET_DVDRW 4 + +/* + * flags + */ +#define PACKET_WRITABLE 1 /* pd is writable */ +#define PACKET_NWA_VALID 2 /* next writable address valid */ +#define PACKET_LRA_VALID 3 /* last recorded address valid */ +#define PACKET_MERGE_SEGS 4 /* perform segment merging to keep */ + /* underlying cdrom device happy */ + +/* + * Disc status -- from READ_DISC_INFO + */ +#define PACKET_DISC_EMPTY 0 +#define PACKET_DISC_INCOMPLETE 1 +#define PACKET_DISC_COMPLETE 2 +#define PACKET_DISC_OTHER 3 + +/* + * write type, and corresponding data block type + */ +#define PACKET_MODE1 1 +#define PACKET_MODE2 2 +#define PACKET_BLOCK_MODE1 8 +#define PACKET_BLOCK_MODE2 10 + +/* + * Last session/border status + */ +#define PACKET_SESSION_EMPTY 0 +#define PACKET_SESSION_INCOMPLETE 1 +#define PACKET_SESSION_RESERVED 2 +#define PACKET_SESSION_COMPLETE 3 + +#define PACKET_MCN "4a656e734178626f65323030300000" + +#undef PACKET_USE_LS + +#define PKT_CTRL_CMD_SETUP 0 +#define PKT_CTRL_CMD_TEARDOWN 1 +#define PKT_CTRL_CMD_STATUS 2 + +struct pkt_ctrl_command { + __u32 command; /* in: Setup, teardown, status */ + __u32 dev_index; /* in/out: Device index */ + __u32 dev; /* in/out: Device nr for cdrw device */ + __u32 pkt_dev; /* in/out: Device nr for packet device */ + __u32 num_devices; /* out: Largest device index + 1 */ + __u32 padding; /* Not used */ +}; + +/* + * packet ioctls + */ +#define PACKET_IOCTL_MAGIC ('X') +#define PACKET_CTRL_CMD _IOWR(PACKET_IOCTL_MAGIC, 1, struct pkt_ctrl_command) + +#ifdef __KERNEL__ +#include +#include +#include + +struct packet_settings +{ + __u8 size; /* packet size in (512 byte) sectors */ + __u8 fp; /* fixed packets */ + __u8 link_loss; /* the rest is specified + * as per Mt Fuji */ + __u8 write_type; + __u8 track_mode; + __u8 block_mode; +}; + +/* + * Very crude stats for now + */ +struct packet_stats +{ + unsigned long pkt_started; + unsigned long pkt_ended; + unsigned long secs_w; + unsigned long secs_rg; + unsigned long secs_r; +}; + +struct packet_cdrw +{ + struct list_head pkt_free_list; + struct list_head pkt_active_list; + spinlock_t active_list_lock; /* Serialize access to pkt_active_list */ + struct task_struct *thread; + atomic_t pending_bios; +}; + +/* + * Switch to high speed reading after reading this many kilobytes + * with no interspersed writes. + */ +#define HI_SPEED_SWITCH 512 + +struct packet_iosched +{ + atomic_t attention; /* Set to non-zero when queue processing is needed */ + int writing; /* Non-zero when writing, zero when reading */ + spinlock_t lock; /* Protecting read/write queue manipulations */ + struct bio *read_queue; + struct bio *read_queue_tail; + struct bio *write_queue; + struct bio *write_queue_tail; + int high_prio_read; /* An important read request has been queued */ + int successive_reads; +}; + +/* + * 32 buffers of 2048 bytes + */ +#define PACKET_MAX_SIZE 32 +#define PAGES_PER_PACKET (PACKET_MAX_SIZE * CD_FRAMESIZE / PAGE_SIZE) +#define PACKET_MAX_SECTORS (PACKET_MAX_SIZE * CD_FRAMESIZE >> 9) + +enum packet_data_state { + PACKET_IDLE_STATE, /* Not used at the moment */ + PACKET_WAITING_STATE, /* Waiting for more bios to arrive, so */ + /* we don't have to do as much */ + /* data gathering */ + PACKET_READ_WAIT_STATE, /* Waiting for reads to fill in holes */ + PACKET_WRITE_WAIT_STATE, /* Waiting for the write to complete */ + PACKET_RECOVERY_STATE, /* Recover after read/write errors */ + PACKET_FINISHED_STATE, /* After write has finished */ + + PACKET_NUM_STATES /* Number of possible states */ +}; + +/* + * Information needed for writing a single packet + */ +struct pktcdvd_device; + +struct packet_data +{ + struct list_head list; + + spinlock_t lock; /* Lock protecting state transitions and */ + /* orig_bios list */ + + struct bio *orig_bios; /* Original bios passed to pkt_make_request */ + struct bio *orig_bios_tail;/* that will be handled by this packet */ + int write_size; /* Total size of all bios in the orig_bios */ + /* list, measured in number of frames */ + + struct bio *w_bio; /* The bio we will send to the real CD */ + /* device once we have all data for the */ + /* packet we are going to write */ + sector_t sector; /* First sector in this packet */ + int frames; /* Number of frames in this packet */ + + enum packet_data_state state; /* Current state */ + atomic_t run_sm; /* Incremented whenever the state */ + /* machine needs to be run */ + long sleep_time; /* Set this to non-zero to make the state */ + /* machine run after this many jiffies. */ + + atomic_t io_wait; /* Number of pending IO operations */ + atomic_t io_errors; /* Number of read/write errors during IO */ + + struct bio *r_bios[PACKET_MAX_SIZE]; /* bios to use during data gathering */ + struct page *pages[PAGES_PER_PACKET]; + + int cache_valid; /* If non-zero, the data for the zone defined */ + /* by the sector variable is completely cached */ + /* in the pages[] vector. */ + + int id; /* ID number for debugging */ + struct pktcdvd_device *pd; +}; + +struct pkt_rb_node { + struct rb_node rb_node; + struct bio *bio; +}; + +struct packet_stacked_data +{ + struct bio *bio; /* Original read request bio */ + struct pktcdvd_device *pd; +}; +#define PSD_POOL_SIZE 64 + +struct pktcdvd_device +{ + struct block_device *bdev; /* dev attached */ + dev_t pkt_dev; /* our dev */ + char name[20]; + struct packet_settings settings; + struct packet_stats stats; + int refcnt; /* Open count */ + int write_speed; /* current write speed, kB/s */ + int read_speed; /* current read speed, kB/s */ + unsigned long offset; /* start offset */ + __u8 mode_offset; /* 0 / 8 */ + __u8 type; + unsigned long flags; + __u16 mmc3_profile; + __u32 nwa; /* next writable address */ + __u32 lra; /* last recorded address */ + struct packet_cdrw cdrw; + wait_queue_head_t wqueue; + + spinlock_t lock; /* Serialize access to bio_queue */ + struct rb_root bio_queue; /* Work queue of bios we need to handle */ + int bio_queue_size; /* Number of nodes in bio_queue */ + sector_t current_sector; /* Keep track of where the elevator is */ + atomic_t scan_queue; /* Set to non-zero when pkt_handle_queue */ + /* needs to be run. */ + mempool_t *rb_pool; /* mempool for pkt_rb_node allocations */ + + struct packet_iosched iosched; + struct gendisk *disk; +}; + +#endif /* __KERNEL__ */ + +#endif /* __PKTCDVD_H */ --- linux-2.6.9-rc1/include/linux/pkt_sched.h 2004-08-24 00:03:19.000000000 -0700 +++ 25/include/linux/pkt_sched.h 2004-09-02 20:51:52.000000000 -0700 @@ -402,6 +402,16 @@ enum { #define TCA_ATM_MAX TCA_ATM_STATE /* Network emulator */ + +enum +{ + TCA_NETEM_UNSPEC, + TCA_NETEM_CORR, + TCA_NETEM_DELAY_DIST, +}; + +#define TCA_NETEM_MAX TCA_NETEM_DELAY_DIST + struct tc_netem_qopt { __u32 latency; /* added delay (us) */ @@ -411,4 +421,14 @@ struct tc_netem_qopt __u32 duplicate; /* random packet dup (0=none ~0=100%) */ __u32 jitter; /* random jitter in latency (us) */ }; + +struct tc_netem_corr +{ + __u32 delay_corr; /* delay correlation */ + __u32 loss_corr; /* packet loss correlation */ + __u32 dup_corr; /* duplicate correlation */ +}; + +#define NETEM_DIST_SCALE 8192 + #endif --- linux-2.6.9-rc1/include/linux/pm.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/include/linux/pm.h 2004-09-03 00:56:34.829865704 -0700 @@ -28,33 +28,20 @@ #include /* - * Power management requests + * Power management requests... these are passed to pm_send_all() and friends. + * + * these functions are old and deprecated, see below. */ -enum +typedef enum pm_request { PM_SUSPEND, /* enter D1-D3 */ PM_RESUME, /* enter D0 */ - - PM_SAVE_STATE, /* save device's state */ - - /* enable wake-on */ - PM_SET_WAKEUP, - - /* bus resource management */ - PM_GET_RESOURCES, - PM_SET_RESOURCES, - - /* base station management */ - PM_EJECT, - PM_LOCK, -}; - -typedef int pm_request_t; +} pm_request_t; /* - * Device types + * Device types... these are passed to pm_register */ -enum +typedef enum pm_dev_type { PM_UNKNOWN_DEV = 0, /* generic */ PM_SYS_DEV, /* system device (fan, KB controller, ...) */ @@ -63,9 +50,7 @@ enum PM_SCSI_DEV, /* SCSI device */ PM_ISA_DEV, /* ISA device */ PM_MTD_DEV, /* Memory Technology Device */ -}; - -typedef int pm_dev_t; +} pm_dev_t; /* * System device hardware ID (PnP) values @@ -186,6 +171,8 @@ static inline void pm_dev_idle(struct pm #endif /* CONFIG_PM */ +/* Functions above this comment are list-based old-style power + * managment. Please avoid using them. */ /* * Callbacks for platform drivers to implement. @@ -193,15 +180,15 @@ static inline void pm_dev_idle(struct pm extern void (*pm_idle)(void); extern void (*pm_power_off)(void); -enum { - PM_SUSPEND_ON, - PM_SUSPEND_STANDBY, - PM_SUSPEND_MEM, - PM_SUSPEND_DISK, +enum suspend_state { + PM_SUSPEND_ON = 0, + PM_SUSPEND_STANDBY = 1, + PM_SUSPEND_MEM = 2, + PM_SUSPEND_DISK = 3, PM_SUSPEND_MAX, }; -enum { +enum suspend_disk_method { PM_DISK_FIRMWARE = 1, PM_DISK_PLATFORM, PM_DISK_SHUTDOWN, @@ -211,15 +198,15 @@ enum { struct pm_ops { - u32 pm_disk_mode; - int (*prepare)(u32 state); - int (*enter)(u32 state); - int (*finish)(u32 state); + enum suspend_disk_method pm_disk_mode; + int (*prepare)(enum suspend_state state); + int (*enter)(enum suspend_state state); + int (*finish)(enum suspend_state state); }; extern void pm_set_ops(struct pm_ops *); -extern int pm_suspend(u32 state); +extern int pm_suspend(enum suspend_state state); /* --- linux-2.6.9-rc1/include/linux/pnp.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/include/linux/pnp.h 2004-09-03 00:56:33.271102672 -0700 @@ -292,6 +292,7 @@ struct pnp_driver { unsigned int flags; int (*probe) (struct pnp_dev *dev, const struct pnp_device_id *dev_id); void (*remove) (struct pnp_dev *dev); + int (*match) (struct pnp_dev *dev); struct device_driver driver; }; --- linux-2.6.9-rc1/include/linux/poll.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/include/linux/poll.h 2004-09-02 20:51:52.000000000 -0700 @@ -5,6 +5,7 @@ #ifdef __KERNEL__ +#include #include #include #include @@ -81,11 +82,12 @@ int get_fd_set(unsigned long nr, void __ return 0; } -static inline -void set_fd_set(unsigned long nr, void __user *ufdset, unsigned long *fdset) +static inline unsigned long __must_check +set_fd_set(unsigned long nr, void __user *ufdset, unsigned long *fdset) { if (ufdset) - __copy_to_user(ufdset, fdset, FDS_BYTES(nr)); + return __copy_to_user(ufdset, fdset, FDS_BYTES(nr)); + return 0; } static inline --- linux-2.6.9-rc1/include/linux/prctl.h 2004-08-24 00:02:48.000000000 -0700 +++ 25/include/linux/prctl.h 2004-09-03 00:56:54.693845920 -0700 @@ -49,5 +49,30 @@ # define PR_TIMING_TIMESTAMP 1 /* Accurate timestamp based process timing */ +/* Manage a process's keyrings */ +#define PR_SPEC_THREAD_KEYRING -1 /* - key ID for thread-specific keyring */ +#define PR_SPEC_PROCESS_KEYRING -2 /* - key ID for process-specific keyring */ +#define PR_SPEC_SESSION_KEYRING -3 /* - key ID for session-specific keyring */ +#define PR_SPEC_USER_KEYRING -4 /* - key ID for UID-specific keyring */ +#define PR_SPEC_USER_SESSION_KEYRING -5 /* - key ID for UID-session keyring */ +#define PR_SPEC_GROUP_KEYRING -6 /* - key ID for GID-specific keyring */ + +#define PR_GET_KEYRING_ID 15 /* ask for a keyring's ID */ +#define PR_JOIN_SESSION_KEYRING 16 /* join or start named session keyring */ +#define KEYCTL_NEW 0x100 /* create a key and add to specified keyring */ +#define KEYCTL_UPDATE 0x101 /* update a key */ +#define KEYCTL_REVOKE 0x102 /* revoke a key */ +#define KEYCTL_CHOWN 0x103 /* set ownership of a key */ +#define KEYCTL_SETPERM 0x104 /* set perms on a key */ +#define KEYCTL_DESCRIBE 0x105 /* describe a key */ +#define KEYCTL_CLEAR 0x106 /* clear contents of a keyring */ +#define KEYCTL_LINK 0x107 /* link a key into a keyring */ +#define KEYCTL_UNLINK 0x108 /* unlink a key from a keyring */ +#define KEYCTL_SEARCH 0x109 /* search for a key in a keyring */ +#define KEYCTL_READ 0x10a /* read a key or keyring's contents */ +#define KEYCTL_REQUEST_KEY 0x10b /* request a key from the process's keyrings */ +#define KEYCTL_INSTANTIATE 0x10c /* instantiate a partially constructed key */ +#define KEYCTL_NEGATE 0x10d /* negate a partially constructed key */ +#define __KEYCTL_LAST 0x10d #endif /* _LINUX_PRCTL_H */ --- linux-2.6.9-rc1/include/linux/proc_fs.h 2004-08-24 00:03:50.000000000 -0700 +++ 25/include/linux/proc_fs.h 2004-09-03 00:56:56.926506504 -0700 @@ -86,10 +86,15 @@ extern struct proc_dir_entry *proc_root_ extern void proc_root_init(void); extern void proc_misc_init(void); +struct mm_struct; + struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *); struct dentry *proc_pid_unhash(struct task_struct *p); void proc_pid_flush(struct dentry *proc_dentry); int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir); +unsigned long task_vsize(struct mm_struct *); +int task_statm(struct mm_struct *, int *, int *, int *, int *); +char *task_mem(struct mm_struct *, char *); extern struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent); @@ -201,14 +206,13 @@ static inline struct proc_dir_entry *cre #define remove_proc_entry(name, parent) do {} while (0) static inline struct proc_dir_entry *proc_symlink(const char *name, - struct proc_dir_entry *parent,char *dest) {return NULL;} + struct proc_dir_entry *parent,const char *dest) {return NULL;} static inline struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent) {return NULL;} static inline struct proc_dir_entry *create_proc_read_entry(const char *name, mode_t mode, struct proc_dir_entry *base, - int (*read_proc)(char *, char **, off_t, int, int *, void *), - void * data) { return NULL; } + read_proc_t *read_proc, void * data) { return NULL; } static inline struct proc_dir_entry *create_proc_info_entry(const char *name, mode_t mode, struct proc_dir_entry *base, get_info_t *get_info) { return NULL; } --- linux-2.6.9-rc1/include/linux/profile.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/include/linux/profile.h 2004-09-02 20:51:52.000000000 -0700 @@ -6,24 +6,28 @@ #include #include #include +#include #include -/* parse command line */ -int __init profile_setup(char * str); +#define CPU_PROFILING 1 +#define SCHED_PROFILING 2 + +struct proc_dir_entry; +struct pt_regs; /* init basic kernel profiler */ void __init profile_init(void); - -extern unsigned int * prof_buffer; -extern unsigned long prof_len; -extern unsigned long prof_shift; -extern int prof_on; - +void profile_tick(int, struct pt_regs *); +void profile_hit(int, void *); +#ifdef CONFIG_PROC_FS +void create_prof_cpu_mask(struct proc_dir_entry *); +#else +#define create_prof_cpu_mask(x) do { (void)(x); } while (0) +#endif enum profile_type { - EXIT_TASK, - EXIT_MMAP, - EXEC_UNMAP + PROFILE_TASK_EXIT, + PROFILE_MUNMAP }; #ifdef CONFIG_PROFILING @@ -33,16 +37,20 @@ struct task_struct; struct mm_struct; /* task is in do_exit() */ -void profile_exit_task(struct task_struct * task); +void profile_task_exit(struct task_struct * task); -/* change of vma mappings */ -void profile_exec_unmap(struct mm_struct * mm); +/* task is dead, free task struct ? Returns 1 if + * the task was taken, 0 if the task should be freed. + */ +int profile_handoff_task(struct task_struct * task); -/* exit of all vmas for a task */ -void profile_exit_mmap(struct mm_struct * mm); +/* sys_munmap */ +void profile_munmap(unsigned long addr); -int profile_event_register(enum profile_type, struct notifier_block * n); +int task_handoff_register(struct notifier_block * n); +int task_handoff_unregister(struct notifier_block * n); +int profile_event_register(enum profile_type, struct notifier_block * n); int profile_event_unregister(enum profile_type, struct notifier_block * n); int register_profile_notifier(struct notifier_block * nb); @@ -55,6 +63,16 @@ void profile_hook(struct pt_regs * regs) #else +static inline int task_handoff_register(struct notifier_block * n) +{ + return -ENOSYS; +} + +static inline int task_handoff_unregister(struct notifier_block * n) +{ + return -ENOSYS; +} + static inline int profile_event_register(enum profile_type t, struct notifier_block * n) { return -ENOSYS; @@ -65,9 +83,9 @@ static inline int profile_event_unregist return -ENOSYS; } -#define profile_exit_task(a) do { } while (0) -#define profile_exec_unmap(a) do { } while (0) -#define profile_exit_mmap(a) do { } while (0) +#define profile_task_exit(a) do { } while (0) +#define profile_handoff_task(a) (0) +#define profile_munmap(a) do { } while (0) static inline int register_profile_notifier(struct notifier_block * nb) { --- linux-2.6.9-rc1/include/linux/radix-tree.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/include/linux/radix-tree.h 2004-09-03 00:57:03.076571552 -0700 @@ -46,6 +46,7 @@ do { \ int radix_tree_insert(struct radix_tree_root *, unsigned long, void *); void *radix_tree_lookup(struct radix_tree_root *, unsigned long); +void **radix_tree_lookup_slot(struct radix_tree_root *, unsigned long); void *radix_tree_delete(struct radix_tree_root *, unsigned long); unsigned int radix_tree_gang_lookup(struct radix_tree_root *root, void **results, --- linux-2.6.9-rc1/include/linux/raid/md.h 2004-08-24 00:01:53.000000000 -0700 +++ 25/include/linux/raid/md.h 2004-09-02 20:51:52.000000000 -0700 @@ -74,7 +74,6 @@ extern void md_write_start(mddev_t *mdde extern void md_write_end(mddev_t *mddev); extern void md_handle_safemode(mddev_t *mddev); extern void md_done_sync(mddev_t *mddev, int blocks, int ok); -extern void md_sync_acct(mdk_rdev_t *rdev, unsigned long nr_sectors); extern void md_error (mddev_t *mddev, mdk_rdev_t *rdev); extern void md_unplug_mddev(mddev_t *mddev); --- linux-2.6.9-rc1/include/linux/raid/md_k.h 2004-08-24 00:01:52.000000000 -0700 +++ 25/include/linux/raid/md_k.h 2004-09-03 00:57:25.802116744 -0700 @@ -24,7 +24,8 @@ #define HSM 6UL #define MULTIPATH 7UL #define RAID6 8UL -#define MAX_PERSONALITY 9UL +#define RAID10 9UL +#define MAX_PERSONALITY 10UL #define LEVEL_MULTIPATH (-4) #define LEVEL_LINEAR (-1) @@ -43,6 +44,7 @@ static inline int pers_to_level (int per case RAID1: return 1; case RAID5: return 5; case RAID6: return 6; + case RAID10: return 10; } BUG(); return MD_RESERVED; @@ -60,6 +62,7 @@ static inline int level_to_pers (int lev case 4: case 5: return RAID5; case 6: return RAID6; + case 10: return RAID10; } return MD_RESERVED; } @@ -216,6 +219,7 @@ struct mddev_s unsigned long resync_mark; /* a recent timestamp */ sector_t resync_mark_cnt;/* blocks written at resync_mark */ + sector_t resync_max_sectors; /* may be set by personality */ /* recovery/resync flags * NEEDED: we might need to start a resync/recover * RUNNING: a thread is running, or about to be started @@ -263,6 +267,11 @@ static inline void rdev_dec_pending(mdk_ set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); } +static inline void md_sync_acct(struct block_device *bdev, unsigned long nr_sectors) +{ + atomic_add(nr_sectors, &bdev->bd_contains->bd_disk->sync_io); +} + struct mdk_personality_s { char *name; @@ -351,5 +360,9 @@ do { \ __wait_event_lock_irq(wq, condition, lock, cmd); \ } while (0) +extern int md_new_event(void); +extern wait_queue_head_t md_event_waiters; +extern int md_event_reached(unsigned long ev); + #endif --- linux-2.6.9-rc1/include/linux/raid/multipath.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/include/linux/raid/multipath.h 2004-09-03 00:57:25.802116744 -0700 @@ -13,6 +13,7 @@ struct multipath_private_data { int raid_disks; int working_disks; spinlock_t device_lock; + unsigned long last_fail_event; mempool_t *pool; }; --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/linux/raid/raid10.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,103 @@ +#ifndef _RAID10_H +#define _RAID10_H + +#include + +typedef struct mirror_info mirror_info_t; + +struct mirror_info { + mdk_rdev_t *rdev; + sector_t head_position; +}; + +typedef struct r10bio_s r10bio_t; + +struct r10_private_data_s { + mddev_t *mddev; + mirror_info_t *mirrors; + int raid_disks; + int working_disks; + spinlock_t device_lock; + + /* geometry */ + int near_copies; /* number of copies layed out raid0 style */ + int far_copies; /* number of copies layed out + * at large strides across drives + */ + int copies; /* near_copies * far_copies. + * must be <= raid_disks + */ + sector_t stride; /* distance between far copies. + * This is size / far_copies + */ + + int chunk_shift; /* shift from chunks to sectors */ + sector_t chunk_mask; + + struct list_head retry_list; + /* for use when syncing mirrors: */ + + spinlock_t resync_lock; + int nr_pending; + int barrier; + sector_t next_resync; + + wait_queue_head_t wait_idle; + wait_queue_head_t wait_resume; + + mempool_t *r10bio_pool; + mempool_t *r10buf_pool; +}; + +typedef struct r10_private_data_s conf_t; + +/* + * this is the only point in the RAID code where we violate + * C type safety. mddev->private is an 'opaque' pointer. + */ +#define mddev_to_conf(mddev) ((conf_t *) mddev->private) + +/* + * this is our 'private' RAID10 bio. + * + * it contains information about what kind of IO operations were started + * for this RAID10 operation, and about their status: + */ + +struct r10bio_s { + atomic_t remaining; /* 'have we finished' count, + * used from IRQ handlers + */ + sector_t sector; /* virtual sector number */ + int sectors; + unsigned long state; + mddev_t *mddev; + /* + * original bio going to /dev/mdx + */ + struct bio *master_bio; + /* + * if the IO is in READ direction, then this is where we read + */ + int read_slot; + + struct list_head retry_list; + /* + * if the IO is in WRITE direction, then multiple bios are used, + * one for each copy. + * When resyncing we also use one for each copy. + * When reconstructing, we use 2 bios, one for read, one for write. + * We choose the number when they are allocated. + */ + struct { + struct bio *bio; + sector_t addr; + int devnum; + } devs[0]; +}; + +/* bits for r10bio.state */ +#define R10BIO_Uptodate 0 +#define R10BIO_IsSync 1 +#define R10BIO_IsRecover 2 +#endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/linux/ramfs.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,11 @@ +#ifndef _LINUX_RAMFS_H +#define _LINUX_RAMFS_H + +struct inode *ramfs_get_inode(struct super_block *sb, int mode, dev_t dev); +struct super_block *ramfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data); + +extern struct file_operations ramfs_file_operations; +extern struct vm_operations_struct generic_file_vm_ops; + +#endif --- linux-2.6.9-rc1/include/linux/rcupdate.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/include/linux/rcupdate.h 2004-09-03 00:57:02.071724312 -0700 @@ -102,6 +102,7 @@ struct rcu_data { struct rcu_head *donelist; struct rcu_head **donetail; int cpu; + struct rcu_head barrier; }; DECLARE_PER_CPU(struct rcu_data, rcu_data); @@ -248,6 +249,7 @@ extern void FASTCALL(call_rcu(struct rcu extern void FASTCALL(call_rcu_bh(struct rcu_head *head, void (*func)(struct rcu_head *head))); extern void synchronize_kernel(void); +extern void rcu_barrier(void); #endif /* __KERNEL__ */ #endif /* __LINUX_RCUPDATE_H */ --- linux-2.6.9-rc1/include/linux/reboot.h 2004-08-24 00:03:49.000000000 -0700 +++ 25/include/linux/reboot.h 2004-09-03 00:56:58.614249928 -0700 @@ -51,6 +51,8 @@ extern void machine_restart(char *cmd); extern void machine_halt(void); extern void machine_power_off(void); +extern void machine_shutdown(void); + #endif #endif /* _LINUX_REBOOT_H */ --- linux-2.6.9-rc1/include/linux/resource.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/include/linux/resource.h 2004-09-03 00:56:56.674544808 -0700 @@ -59,7 +59,7 @@ struct rlimit { * GPG wants 32kB of mlocked memory, to make sure pass phrases * and other sensitive information are never written to disk. */ -#define MLOCK_LIMIT (32*1024) +#define MLOCK_LIMIT (8 * PAGE_SIZE) /* * Due to binary compatibility, the actual resource numbers --- linux-2.6.9-rc1/include/linux/rmap.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/include/linux/rmap.h 2004-09-02 20:51:52.000000000 -0700 @@ -9,11 +9,6 @@ #include #include -#define page_map_lock(page) \ - bit_spin_lock(PG_maplock, (unsigned long *)&(page)->flags) -#define page_map_unlock(page) \ - bit_spin_unlock(PG_maplock, (unsigned long *)&(page)->flags) - /* * The anon_vma heads a list of private "related" vmas, to scan if * an anonymous page pointing to this anon_vma needs to be unmapped: @@ -87,24 +82,27 @@ void page_remove_rmap(struct page *); */ static inline void page_dup_rmap(struct page *page) { - page_map_lock(page); - page->mapcount++; - page_map_unlock(page); + atomic_inc(&page->_mapcount); } /* * Called from mm/vmscan.c to handle paging out */ -int page_referenced(struct page *); +int page_referenced(struct page *, int is_locked); int try_to_unmap(struct page *); +/* + * Used by swapoff to help locate where page is expected in vma. + */ +unsigned long page_address_in_vma(struct page *, struct vm_area_struct *); + #else /* !CONFIG_MMU */ #define anon_vma_init() do {} while (0) #define anon_vma_prepare(vma) (0) #define anon_vma_link(vma) do {} while (0) -#define page_referenced(page) TestClearPageReferenced(page) +#define page_referenced(page,l) TestClearPageReferenced(page) #define try_to_unmap(page) SWAP_FAIL #endif /* CONFIG_MMU */ --- linux-2.6.9-rc1/include/linux/rtnetlink.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/include/linux/rtnetlink.h 2004-09-02 20:51:52.000000000 -0700 @@ -561,6 +561,12 @@ enum #define IFLA_WIRELESS IFLA_WIRELESS IFLA_PROTINFO, /* Protocol specific information for a link */ #define IFLA_PROTINFO IFLA_PROTINFO + IFLA_TXQLEN, +#define IFLA_TXQLEN IFLA_TXQLEN + IFLA_MAP, +#define IFLA_MAP IFLA_MAP + IFLA_WEIGHT, +#define IFLA_WEIGHT IFLA_WEIGHT __IFLA_MAX }; --- linux-2.6.9-rc1/include/linux/sched.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/include/linux/sched.h 2004-09-03 00:57:09.240634472 -0700 @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -40,7 +41,6 @@ struct exec_domain; #define CLONE_FS 0x00000200 /* set if fs info shared between processes */ #define CLONE_FILES 0x00000400 /* set if open files shared between processes */ #define CLONE_SIGHAND 0x00000800 /* set if signal handlers and blocked signals shared */ -#define CLONE_IDLETASK 0x00001000 /* set if new pid should be 0 (kernel only)*/ #define CLONE_PTRACE 0x00002000 /* set if we want to let tracing continue on the child too */ #define CLONE_VFORK 0x00004000 /* set if the parent wants the child to wake it up on mm_release */ #define CLONE_PARENT 0x00008000 /* set if we want to have the same parent as the cloner */ @@ -107,8 +107,9 @@ extern unsigned long nr_iowait(void); #define TASK_INTERRUPTIBLE 1 #define TASK_UNINTERRUPTIBLE 2 #define TASK_STOPPED 4 -#define TASK_ZOMBIE 8 -#define TASK_DEAD 16 +#define TASK_TRACED 8 +#define TASK_ZOMBIE 16 +#define TASK_DEAD 32 #define __set_task_state(tsk, state_value) \ do { (tsk)->state = (state_value); } while (0) @@ -189,10 +190,26 @@ extern int sysctl_max_map_count; #include +extern unsigned long +arch_get_unmapped_area(struct file *, unsigned long, unsigned long, + unsigned long, unsigned long); +extern unsigned long +arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr, + unsigned long len, unsigned long pgoff, + unsigned long flags); +extern void arch_unmap_area(struct vm_area_struct *area); +extern void arch_unmap_area_topdown(struct vm_area_struct *area); + + struct mm_struct { struct vm_area_struct * mmap; /* list of VMAs */ struct rb_root mm_rb; struct vm_area_struct * mmap_cache; /* last find_vma result */ + unsigned long (*get_unmapped_area) (struct file *filp, + unsigned long addr, unsigned long len, + unsigned long pgoff, unsigned long flags); + void (*unmap_area) (struct vm_area_struct *area); + unsigned long mmap_base; /* base of mmap area */ unsigned long free_area_cache; /* first hole */ pgd_t * pgd; atomic_t mm_users; /* How many users with user space? */ @@ -209,10 +226,10 @@ struct mm_struct { unsigned long start_code, end_code, start_data, end_data; unsigned long start_brk, brk, start_stack; unsigned long arg_start, arg_end, env_start, env_end; - unsigned long rss, total_vm, locked_vm; - unsigned long def_flags; + unsigned long rss, total_vm, locked_vm, shared_vm; + unsigned long exec_vm, stack_vm, reserved_vm, def_flags; - unsigned long saved_auxv[40]; /* for /proc/PID/auxv */ + unsigned long saved_auxv[42]; /* for /proc/PID/auxv */ unsigned dumpable:1; cpumask_t cpu_vm_mask; @@ -272,6 +289,8 @@ struct signal_struct { /* thread group stop support, overloads group_exit_code too */ int group_stop_count; + /* 1 if group stopped since last SIGCONT, -1 if SIGCONT since report */ + int stop_state; /* POSIX.1b Interval Timers */ struct list_head posix_timers; @@ -284,6 +303,16 @@ struct signal_struct { int leader; struct tty_struct *tty; /* NULL if no tty */ + + /* + * Cumulative resource counters for dead threads in the group, + * and for reaped dead child processes forked by this group. + * Live threads maintain their own counters and add to these + * in __exit_signal, except for the group leader. + */ + unsigned long utime, stime, cutime, cstime; + unsigned long nvcsw, nivcsw, cnvcsw, cnivcsw; + unsigned long min_flt, maj_flt, cmin_flt, cmaj_flt; }; /* @@ -302,9 +331,9 @@ struct signal_struct { #define MAX_USER_RT_PRIO 100 #define MAX_RT_PRIO MAX_USER_RT_PRIO -#define MAX_PRIO (MAX_RT_PRIO + 40) +#define MAX_PRIO (MAX_RT_PRIO + 59) -#define rt_task(p) ((p)->prio < MAX_RT_PRIO) +#define rt_task(p) (unlikely((p)->prio < MAX_RT_PRIO)) /* * Some day this will be a full-fledged user tracking system.. @@ -318,6 +347,11 @@ struct user_struct { unsigned long mq_bytes; /* How many bytes can be allocated to mqueue? */ unsigned long locked_shm; /* How many pages of mlocked shm ? */ +#ifdef CONFIG_KEYS + struct key *uid_keyring; /* UID specific keyring */ + struct key *session_keyring; /* UID's default session keyring */ +#endif + /* Hash table maintenance information */ struct list_head uidhash_list; uid_t uid; @@ -352,9 +386,24 @@ struct k_itimer { struct timespec wall_to_prev; /* wall_to_monotonic used when set */ }; +#ifdef CONFIG_SCHEDSTATS +struct sched_info { + /* cumulative counters */ + unsigned long cpu_time, /* time spent on the cpu */ + run_delay, /* time spent waiting on a runqueue */ + pcnt; /* # of timeslices run on this cpu */ + + /* timestamps */ + unsigned long last_arrival, /* when we last ran on a cpu */ + last_queued; /* when we were last queued to run */ +}; + +extern struct file_operations proc_schedstat_operations; +#endif struct io_context; /* See blkdev.h */ void exit_io_context(void); +struct cpuset; #define NGROUPS_SMALL 32 #define NGROUPS_PER_BLOCK ((int)(PAGE_SIZE / sizeof(gid_t))) @@ -405,14 +454,19 @@ struct task_struct { struct list_head run_list; prio_array_t *array; - unsigned long sleep_avg; - long interactive_credit; + /* Scheduler variables follow. kernel/sched.c */ + unsigned long array_sequence; unsigned long long timestamp; - int activated; + int used_slice; + + unsigned long total_time, sleep_time; unsigned long policy; cpumask_t cpus_allowed; - unsigned int time_slice, first_time_slice; + +#ifdef CONFIG_SCHEDSTATS + struct sched_info sched_info; +#endif struct list_head tasks; /* @@ -449,7 +503,7 @@ struct task_struct { struct task_struct *group_leader; /* threadgroup leader */ /* PID/PID hash table linkage. */ - struct pid_link pids[PIDTYPE_MAX]; + struct pid pids[PIDTYPE_MAX]; wait_queue_head_t wait_chldexit; /* for wait4() */ struct completion *vfork_done; /* for vfork() */ @@ -460,11 +514,11 @@ struct task_struct { unsigned long it_real_value, it_prof_value, it_virt_value; unsigned long it_real_incr, it_prof_incr, it_virt_incr; struct timer_list real_timer; - unsigned long utime, stime, cutime, cstime; - unsigned long nvcsw, nivcsw, cnvcsw, cnivcsw; /* context switch counts */ - u64 start_time; + unsigned long utime, stime; + unsigned long nvcsw, nivcsw; /* context switch counts */ + struct timespec start_time; /* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */ - unsigned long min_flt, maj_flt, cmin_flt, cmaj_flt; + unsigned long min_flt, maj_flt; /* process credentials */ uid_t uid,euid,suid,fsuid; gid_t gid,egid,sgid,fsgid; @@ -472,6 +526,11 @@ struct task_struct { kernel_cap_t cap_effective, cap_inheritable, cap_permitted; unsigned keep_capabilities:1; struct user_struct *user; +#ifdef CONFIG_KEYS + struct key *session_keyring; /* keyring inherited over fork */ + struct key *process_keyring; /* keyring private to this process (CLONE_THREAD) */ + struct key *thread_keyring; /* keyring private to this thread */ +#endif /* limits */ struct rlimit rlim[RLIM_NLIMITS]; unsigned short used_math; @@ -507,7 +566,7 @@ struct task_struct { /* Thread group tracking */ u32 parent_exec_id; u32 self_exec_id; -/* Protection of (de-)allocation: mm, files, fs, tty */ +/* Protection of (de-)allocation: mm, files, fs, tty, keyrings */ spinlock_t alloc_lock; /* Protection of proc_dentry: nesting proc_lock, dcache_lock, write_lock_irq(&tasklist_lock); */ spinlock_t proc_lock; @@ -527,11 +586,25 @@ struct task_struct { unsigned long ptrace_message; siginfo_t *last_siginfo; /* For ptrace use. */ - +/* + * current io wait handle: wait queue entry to use for io waits + * If this thread is processing aio, this points at the waitqueue + * inside the currently handled kiocb. It may be NULL (i.e. default + * to a stack based synchronous wait) if its doing sync IO. + */ + wait_queue_t *io_wait; #ifdef CONFIG_NUMA struct mempolicy *mempolicy; short il_next; /* could be shared with used_math */ #endif +#ifdef CONFIG_CPUSETS + struct cpuset *cpuset; + nodemask_t mems_allowed; + int cpuset_mems_generation; +#endif + + struct list_head private_pages; /* per-process private pages */ + int private_pages_count; }; static inline pid_t process_group(struct task_struct *tsk) @@ -539,6 +612,7 @@ static inline pid_t process_group(struct return tsk->signal->pgrp; } +extern void free_task(struct task_struct *tsk); extern void __put_task_struct(struct task_struct *tsk); #define get_task_struct(tsk) do { atomic_inc(&(tsk)->usage); } while(0) #define put_task_struct(tsk) \ @@ -570,118 +644,6 @@ do { if (atomic_dec_and_test(&(tsk)->usa #define PF_SYNCWRITE 0x00200000 /* I am doing a sync write */ #ifdef CONFIG_SMP -#define SCHED_LOAD_SCALE 128UL /* increase resolution of load */ - -#define SD_BALANCE_NEWIDLE 1 /* Balance when about to become idle */ -#define SD_BALANCE_EXEC 2 /* Balance on exec */ -#define SD_BALANCE_CLONE 4 /* Balance on clone */ -#define SD_WAKE_IDLE 8 /* Wake to idle CPU on task wakeup */ -#define SD_WAKE_AFFINE 16 /* Wake task to waking CPU */ -#define SD_WAKE_BALANCE 32 /* Perform balancing at task wakeup */ -#define SD_SHARE_CPUPOWER 64 /* Domain members share cpu power */ - -struct sched_group { - struct sched_group *next; /* Must be a circular list */ - cpumask_t cpumask; - - /* - * CPU power of this group, SCHED_LOAD_SCALE being max power for a - * single CPU. This should be read only (except for setup). Although - * it will need to be written to at cpu hot(un)plug time, perhaps the - * cpucontrol semaphore will provide enough exclusion? - */ - unsigned long cpu_power; -}; - -struct sched_domain { - /* These fields must be setup */ - struct sched_domain *parent; /* top domain must be null terminated */ - struct sched_group *groups; /* the balancing groups of the domain */ - cpumask_t span; /* span of all CPUs in this domain */ - unsigned long min_interval; /* Minimum balance interval ms */ - unsigned long max_interval; /* Maximum balance interval ms */ - unsigned int busy_factor; /* less balancing by factor if busy */ - unsigned int imbalance_pct; /* No balance until over watermark */ - unsigned long long cache_hot_time; /* Task considered cache hot (ns) */ - unsigned int cache_nice_tries; /* Leave cache hot tasks for # tries */ - unsigned int per_cpu_gain; /* CPU % gained by adding domain cpus */ - int flags; /* See SD_* */ - - /* Runtime fields. */ - unsigned long last_balance; /* init to jiffies. units in jiffies */ - unsigned int balance_interval; /* initialise to 1. units in ms. */ - unsigned int nr_balance_failed; /* initialise to 0 */ -}; - -/* Common values for SMT siblings */ -#define SD_SIBLING_INIT (struct sched_domain) { \ - .span = CPU_MASK_NONE, \ - .parent = NULL, \ - .groups = NULL, \ - .min_interval = 1, \ - .max_interval = 2, \ - .busy_factor = 8, \ - .imbalance_pct = 110, \ - .cache_hot_time = 0, \ - .cache_nice_tries = 0, \ - .per_cpu_gain = 15, \ - .flags = SD_BALANCE_NEWIDLE \ - | SD_BALANCE_EXEC \ - | SD_BALANCE_CLONE \ - | SD_WAKE_AFFINE \ - | SD_WAKE_IDLE \ - | SD_SHARE_CPUPOWER, \ - .last_balance = jiffies, \ - .balance_interval = 1, \ - .nr_balance_failed = 0, \ -} - -/* Common values for CPUs */ -#define SD_CPU_INIT (struct sched_domain) { \ - .span = CPU_MASK_NONE, \ - .parent = NULL, \ - .groups = NULL, \ - .min_interval = 1, \ - .max_interval = 4, \ - .busy_factor = 64, \ - .imbalance_pct = 125, \ - .cache_hot_time = (5*1000000/2), \ - .cache_nice_tries = 1, \ - .per_cpu_gain = 100, \ - .flags = SD_BALANCE_NEWIDLE \ - | SD_BALANCE_EXEC \ - | SD_BALANCE_CLONE \ - | SD_WAKE_AFFINE \ - | SD_WAKE_BALANCE, \ - .last_balance = jiffies, \ - .balance_interval = 1, \ - .nr_balance_failed = 0, \ -} - -#ifdef CONFIG_NUMA -/* Common values for NUMA nodes */ -#define SD_NODE_INIT (struct sched_domain) { \ - .span = CPU_MASK_NONE, \ - .parent = NULL, \ - .groups = NULL, \ - .min_interval = 8, \ - .max_interval = 32, \ - .busy_factor = 32, \ - .imbalance_pct = 125, \ - .cache_hot_time = (10*1000000), \ - .cache_nice_tries = 1, \ - .per_cpu_gain = 100, \ - .flags = SD_BALANCE_EXEC \ - | SD_BALANCE_CLONE \ - | SD_WAKE_BALANCE, \ - .last_balance = jiffies, \ - .balance_interval = 1, \ - .nr_balance_failed = 0, \ -} -#endif - -extern void cpu_attach_domain(struct sched_domain *sd, int cpu); - extern int set_cpus_allowed(task_t *p, cpumask_t new_mask); #else static inline int set_cpus_allowed(task_t *p, cpumask_t new_mask) @@ -692,10 +654,11 @@ static inline int set_cpus_allowed(task_ extern unsigned long long sched_clock(void); +/* sched_exec is called by processes performing an exec */ #ifdef CONFIG_SMP -extern void sched_balance_exec(void); +extern void sched_exec(void); #else -#define sched_balance_exec() {} +#define sched_exec() {} #endif extern void sched_idle_next(void); @@ -732,7 +695,8 @@ extern struct task_struct init_task; extern struct mm_struct init_mm; -extern struct task_struct *find_task_by_pid(int pid); +#define find_task_by_pid(nr) find_task_by_pid_type(PIDTYPE_PID, nr) +extern struct task_struct *find_task_by_pid_type(int type, int pid); extern void set_special_pids(pid_t session, pid_t pgrp); extern void __set_special_pids(pid_t session, pid_t pgrp); @@ -754,16 +718,12 @@ extern void do_timer(struct pt_regs *); extern int FASTCALL(wake_up_state(struct task_struct * tsk, unsigned int state)); extern int FASTCALL(wake_up_process(struct task_struct * tsk)); -extern void FASTCALL(wake_up_forked_process(struct task_struct * tsk)); +extern void FASTCALL(wake_up_new_task(struct task_struct * tsk, + unsigned long clone_flags)); #ifdef CONFIG_SMP extern void kick_process(struct task_struct *tsk); - extern void FASTCALL(wake_up_forked_thread(struct task_struct * tsk)); #else static inline void kick_process(struct task_struct *tsk) { } - static inline void wake_up_forked_thread(struct task_struct * tsk) - { - wake_up_forked_process(tsk); - } #endif extern void FASTCALL(sched_fork(task_t * p)); extern void FASTCALL(sched_exit(task_t * p)); @@ -794,12 +754,12 @@ extern void unblock_all_signals(void); extern void release_task(struct task_struct * p); extern int send_sig_info(int, struct siginfo *, struct task_struct *); extern int send_group_sig_info(int, struct siginfo *, struct task_struct *); +extern int force_sigsegv(int, struct task_struct *); extern int force_sig_info(int, struct siginfo *, struct task_struct *); extern int __kill_pg_info(int sig, struct siginfo *info, pid_t pgrp); extern int kill_pg_info(int, struct siginfo *, pid_t); extern int kill_sl_info(int, struct siginfo *, pid_t); extern int kill_proc_info(int, struct siginfo *, pid_t); -extern void notify_parent(struct task_struct *, int); extern void do_notify_parent(struct task_struct *, int); extern void force_sig(int, struct task_struct *); extern void force_sig_specific(int, struct task_struct *); @@ -863,8 +823,8 @@ static inline void mmdrop(struct mm_stru /* mmput gets rid of the mappings and all user-space */ extern void mmput(struct mm_struct *); -/* Grab a reference to the mm if its not already going away */ -extern struct mm_struct *mmgrab(struct mm_struct *); +/* Grab a reference to a task's mm, if it is not already going away */ +extern struct mm_struct *get_task_mm(struct task_struct *task); /* Remove the current tasks stale references to the old mm_struct */ extern void mm_release(struct task_struct *, struct mm_struct *); @@ -890,7 +850,7 @@ extern task_t *child_reaper; extern int do_execve(char *, char __user * __user *, char __user * __user *, struct pt_regs *); extern long do_fork(unsigned long, unsigned long, struct pt_regs *, unsigned long, int __user *, int __user *); -extern struct task_struct * copy_process(unsigned long, unsigned long, struct pt_regs *, unsigned long, int __user *, int __user *); +task_t *fork_idle(int); extern void set_task_comm(struct task_struct *tsk, char *from); extern void get_task_comm(char *to, struct task_struct *tsk); @@ -938,9 +898,7 @@ extern task_t * FASTCALL(next_thread(con static inline int thread_group_empty(task_t *p) { - struct pid *pid = p->pids[PIDTYPE_TGID].pidptr; - - return pid->task_list.next->next == &pid->task_list; + return list_empty(&p->pids[PIDTYPE_TGID].pid_list); } #define delay_group_leader(p) \ @@ -949,8 +907,11 @@ static inline int thread_group_empty(tas extern void unhash_process(struct task_struct *p); /* - * Protects ->fs, ->files, ->mm, ->ptrace, ->group_info, ->comm and - * synchronises with wait4(). + * Protects ->fs, ->files, ->mm, ->ptrace, ->group_info, ->comm, keyring + * subscriptions and synchronises with wait4(). Also used in procfs. + * + * Synchronises set_cpus_allowed(), unlink, and creat of ->thread.perfctr. + * [if CONFIG_PERFCTR_VIRTUAL] * * Nests both inside and outside of read_lock(&tasklist_lock). * It must not be nested with write_lock_irq(&tasklist_lock), @@ -965,27 +926,7 @@ static inline void task_unlock(struct ta { spin_unlock(&p->alloc_lock); } - -/** - * get_task_mm - acquire a reference to the task's mm - * - * Returns %NULL if the task has no mm. User must release - * the mm via mmput() after use. - */ -static inline struct mm_struct * get_task_mm(struct task_struct * task) -{ - struct mm_struct * mm; - - task_lock(task); - mm = task->mm; - if (mm) - mm = mmgrab(mm); - task_unlock(task); - return mm; -} - - /* set thread flags in other task's structures * - see asm/thread_info.h for TIF_xxxx flags available */ @@ -1096,6 +1037,17 @@ static inline void set_task_cpu(struct t #endif /* CONFIG_SMP */ +#ifdef HAVE_ARCH_PICK_MMAP_LAYOUT +extern void arch_pick_mmap_layout(struct mm_struct *mm); +#else +static inline void arch_pick_mmap_layout(struct mm_struct *mm) +{ + mm->mmap_base = TASK_UNMAPPED_BASE; + mm->get_unmapped_area = arch_get_unmapped_area; + mm->unmap_area = arch_unmap_area; +} +#endif + #endif /* __KERNEL__ */ #endif --- linux-2.6.9-rc1/include/linux/serial_core.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/include/linux/serial_core.h 2004-09-03 00:56:57.200464856 -0700 @@ -37,7 +37,8 @@ #define PORT_RSA 13 #define PORT_NS16550A 14 #define PORT_XSCALE 15 -#define PORT_MAX_8250 15 /* max port ID */ +#define PORT_OMAP 16 +#define PORT_MAX_8250 16 /* max port ID */ /* * ARM specific type numbers. These are not currently guaranteed @@ -172,7 +173,9 @@ struct uart_port { unsigned char x_char; /* xon/xoff char */ unsigned char regshift; /* reg offset shift */ unsigned char iotype; /* io access style */ - +#ifdef CONFIG_KGDB + int kgdb; /* in use by kgdb */ +#endif #define UPIO_PORT (0) #define UPIO_HUB6 (1) #define UPIO_MEM (2) --- linux-2.6.9-rc1/include/linux/serial_reg.h 2004-08-24 00:03:25.000000000 -0700 +++ 25/include/linux/serial_reg.h 2004-09-03 00:56:57.201464704 -0700 @@ -294,5 +294,21 @@ #define SERIAL_RSA_BAUD_BASE (921600) #define SERIAL_RSA_BAUD_BASE_LO (SERIAL_RSA_BAUD_BASE / 8) +/* + * Extra serial register definitions for the internal UARTs + * in TI OMAP processors. + */ +#define UART_OMAP_TCR 0x06 /* Transmission control register */ +#define UART_OMAP_TLR 0x07 /* Trigger level register */ +#define UART_OMAP_MDR1 0x08 /* Mode definition register */ +#define UART_OMAP_MDR2 0x09 /* Mode definition register 2 */ +#define UART_OMAP_SCR 0x10 /* Supplementary control register */ +#define UART_OMAP_SSR 0x11 /* Supplementary status register */ +#define UART_OMAP_EBLR 0x12 /* BOF length register */ +#define UART_OMAP_OSC_12M_SEL 0x13 /* OMAP1510 12MHz osc select */ +#define UART_OMAP_MVER 0x14 /* Module version register */ +#define UART_OMAP_SYSC 0x15 /* System configuration register */ +#define UART_OMAP_SYSS 0x16 /* System status register */ + #endif /* _LINUX_SERIAL_REG_H */ --- linux-2.6.9-rc1/include/linux/serio.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/include/linux/serio.h 2004-09-03 00:56:31.595357424 -0700 @@ -17,12 +17,17 @@ #ifdef __KERNEL__ #include +#include +#include struct serio { void *private; - void *driver; - char *name; - char *phys; + void *port_data; + + char name[32]; + char phys[32]; + + unsigned int manual_bind; unsigned short idbus; unsigned short idvendor; @@ -32,31 +37,43 @@ struct serio { unsigned long type; unsigned long event; + spinlock_t lock; /* protects critical sections from port's interrupt handler */ + int (*write)(struct serio *, unsigned char); int (*open)(struct serio *); void (*close)(struct serio *); - struct serio_dev *dev; + struct serio *parent, *child; + + struct serio_driver *drv; /* accessed from interrupt, must be protected by serio->lock */ + + struct device dev; struct list_head node; }; +#define to_serio_port(d) container_of(d, struct serio, dev) -struct serio_dev { +struct serio_driver { void *private; - char *name; + char *description; + + unsigned int manual_bind; void (*write_wakeup)(struct serio *); irqreturn_t (*interrupt)(struct serio *, unsigned char, unsigned int, struct pt_regs *); - void (*connect)(struct serio *, struct serio_dev *dev); + void (*connect)(struct serio *, struct serio_driver *drv); int (*reconnect)(struct serio *); void (*disconnect)(struct serio *); void (*cleanup)(struct serio *); + struct device_driver driver; + struct list_head node; }; +#define to_serio_driver(d) container_of(d, struct serio_driver, driver) -int serio_open(struct serio *serio, struct serio_dev *dev); +int serio_open(struct serio *serio, struct serio_driver *drv); void serio_close(struct serio *serio); void serio_rescan(struct serio *serio); void serio_reconnect(struct serio *serio); @@ -64,12 +81,11 @@ irqreturn_t serio_interrupt(struct serio void serio_register_port(struct serio *serio); void serio_register_port_delayed(struct serio *serio); -void __serio_register_port(struct serio *serio); void serio_unregister_port(struct serio *serio); void serio_unregister_port_delayed(struct serio *serio); -void __serio_unregister_port(struct serio *serio); -void serio_register_device(struct serio_dev *dev); -void serio_unregister_device(struct serio_dev *dev); + +void serio_register_driver(struct serio_driver *drv); +void serio_unregister_driver(struct serio_driver *drv); static __inline__ int serio_write(struct serio *serio, unsigned char data) { @@ -79,18 +95,34 @@ static __inline__ int serio_write(struct return -1; } -static __inline__ void serio_dev_write_wakeup(struct serio *serio) +static __inline__ void serio_drv_write_wakeup(struct serio *serio) { - if (serio->dev && serio->dev->write_wakeup) - serio->dev->write_wakeup(serio); + if (serio->drv && serio->drv->write_wakeup) + serio->drv->write_wakeup(serio); } static __inline__ void serio_cleanup(struct serio *serio) { - if (serio->dev && serio->dev->cleanup) - serio->dev->cleanup(serio); + if (serio->drv && serio->drv->cleanup) + serio->drv->cleanup(serio); +} + + +/* + * Use the following fucntions to protect critical sections in + * driver code from port's interrupt handler + */ +static __inline__ void serio_pause_rx(struct serio *serio) +{ + spin_lock_irq(&serio->lock); } +static __inline__ void serio_continue_rx(struct serio *serio) +{ + spin_unlock_irq(&serio->lock); +} + + #endif /* --- linux-2.6.9-rc1/include/linux/shm.h 2004-08-24 00:03:19.000000000 -0700 +++ 25/include/linux/shm.h 2004-09-02 20:51:52.000000000 -0700 @@ -44,6 +44,7 @@ struct shmid_ds { #define SHM_RDONLY 010000 /* read-only access */ #define SHM_RND 020000 /* round attach address to SHMLBA boundary */ #define SHM_REMAP 040000 /* take-over region on attach */ +#define SHM_EXEC 0100000 /* execution access */ /* super user shmctl commands */ #define SHM_LOCK 11 --- linux-2.6.9-rc1/include/linux/signal.h 2004-08-24 00:02:22.000000000 -0700 +++ 25/include/linux/signal.h 2004-09-02 20:51:52.000000000 -0700 @@ -217,7 +217,7 @@ extern int sigprocmask(int, sigset_t *, #ifndef HAVE_ARCH_GET_SIGNAL_TO_DELIVER struct pt_regs; -extern int get_signal_to_deliver(siginfo_t *info, struct pt_regs *regs, void *cookie); +extern int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka, struct pt_regs *regs, void *cookie); #endif #endif /* __KERNEL__ */ --- linux-2.6.9-rc1/include/linux/slab.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/include/linux/slab.h 2004-09-03 00:57:16.586517728 -0700 @@ -4,7 +4,7 @@ * (markhe@nextd.demon.co.uk) */ -#if !defined(_LINUX_SLAB_H) +#ifndef _LINUX_SLAB_H #define _LINUX_SLAB_H #if defined(__KERNEL__) @@ -45,6 +45,7 @@ typedef struct kmem_cache_s kmem_cache_t #define SLAB_RECLAIM_ACCOUNT 0x00020000UL /* track pages allocated to indicate what is reclaimable later*/ #define SLAB_PANIC 0x00040000UL /* panic if kmem_cache_create() fails */ +#define SLAB_DESTROY_BY_RCU 0x00080000UL /* defer freeing pages to RCU */ /* flags passed to a constructor func */ #define SLAB_CTOR_CONSTRUCTOR 0x001UL /* if not set, then deconstructor */ @@ -97,6 +98,7 @@ found: return __kmalloc(size, flags); } +extern void *kcalloc(size_t, size_t, int); extern void kfree(const void *); extern unsigned int ksize(const void *); @@ -115,8 +117,6 @@ extern kmem_cache_t *signal_cachep; extern kmem_cache_t *sighand_cachep; extern kmem_cache_t *bio_cachep; -void ptrinfo(unsigned long addr); - extern atomic_t slab_reclaim_pages; #endif /* __KERNEL__ */ --- linux-2.6.9-rc1/include/linux/spinlock.h 2004-08-24 00:02:47.000000000 -0700 +++ 25/include/linux/spinlock.h 2004-09-03 00:56:42.836648488 -0700 @@ -15,6 +15,12 @@ #include /* for cpu relax */ #include +#ifdef CONFIG_KGDB +#include +#define SET_WHO(x, him) (x)->who = him; +#else +#define SET_WHO(x, him) +#endif /* * Must define these before including other files, inline functions need them @@ -57,6 +63,9 @@ typedef struct { const char *module; char *owner; int oline; +#ifdef CONFIG_KGDB + struct task_struct *who; +#endif } spinlock_t; #define SPIN_LOCK_UNLOCKED (spinlock_t) { SPINLOCK_MAGIC, 0, 10, __FILE__ , NULL, 0} @@ -68,6 +77,7 @@ typedef struct { (x)->module = __FILE__; \ (x)->owner = NULL; \ (x)->oline = 0; \ + SET_WHO(x, NULL) \ } while (0) #define CHECK_LOCK(x) \ @@ -90,6 +100,7 @@ typedef struct { (x)->lock = 1; \ (x)->owner = __FILE__; \ (x)->oline = __LINE__; \ + SET_WHO(x, current) \ } while (0) /* without debugging, spin_is_locked on UP always says @@ -120,6 +131,7 @@ typedef struct { (x)->lock = 1; \ (x)->owner = __FILE__; \ (x)->oline = __LINE__; \ + SET_WHO(x, current) \ 1; \ }) @@ -186,6 +198,17 @@ typedef struct { #endif /* !SMP */ +#ifdef CONFIG_LOCKMETER +extern void _metered_spin_lock (spinlock_t *lock); +extern void _metered_spin_unlock (spinlock_t *lock); +extern int _metered_spin_trylock(spinlock_t *lock); +extern void _metered_read_lock (rwlock_t *lock); +extern void _metered_read_unlock (rwlock_t *lock); +extern void _metered_write_lock (rwlock_t *lock); +extern void _metered_write_unlock (rwlock_t *lock); +extern int _metered_write_trylock(rwlock_t *lock); +#endif + /* * Define the various spin_lock and rw_lock methods. Note we define these * regardless of whether CONFIG_SMP or CONFIG_PREEMPT are set. The various @@ -391,6 +414,141 @@ do { \ _raw_spin_trylock(lock) ? 1 : \ ({preempt_enable(); local_bh_enable(); 0;});}) +#ifdef CONFIG_LOCKMETER +#undef spin_lock +#undef spin_trylock +#undef spin_unlock +#undef spin_lock_irqsave +#undef spin_lock_irq +#undef spin_lock_bh +#undef read_lock +#undef read_unlock +#undef write_lock +#undef write_unlock +#undef write_trylock +#undef spin_unlock_bh +#undef read_lock_irqsave +#undef read_lock_irq +#undef read_lock_bh +#undef read_unlock_bh +#undef write_lock_irqsave +#undef write_lock_irq +#undef write_lock_bh +#undef write_unlock_bh + +#define spin_lock(lock) \ +do { \ + preempt_disable(); \ + _metered_spin_lock(lock); \ +} while(0) + +#define spin_trylock(lock) ({preempt_disable(); _metered_spin_trylock(lock) ? \ + 1 : ({preempt_enable(); 0;});}) +#define spin_unlock(lock) \ +do { \ + _metered_spin_unlock(lock); \ + preempt_enable(); \ +} while (0) + +#define spin_lock_irqsave(lock, flags) \ +do { \ + local_irq_save(flags); \ + preempt_disable(); \ + _metered_spin_lock(lock); \ +} while (0) + +#define spin_lock_irq(lock) \ +do { \ + local_irq_disable(); \ + preempt_disable(); \ + _metered_spin_lock(lock); \ +} while (0) + +#define spin_lock_bh(lock) \ +do { \ + local_bh_disable(); \ + preempt_disable(); \ + _metered_spin_lock(lock); \ +} while (0) + +#define spin_unlock_bh(lock) \ +do { \ + _metered_spin_unlock(lock); \ + preempt_enable(); \ + local_bh_enable(); \ +} while (0) + + +#define read_lock(lock) ({preempt_disable(); _metered_read_lock(lock);}) +#define read_unlock(lock) ({_metered_read_unlock(lock); preempt_enable();}) +#define write_lock(lock) ({preempt_disable(); _metered_write_lock(lock);}) +#define write_unlock(lock) ({_metered_write_unlock(lock); preempt_enable();}) +#define write_trylock(lock) ({preempt_disable();_metered_write_trylock(lock) ? \ + 1 : ({preempt_enable(); 0;});}) +#define spin_unlock_no_resched(lock) \ +do { \ + _metered_spin_unlock(lock); \ + preempt_enable_no_resched(); \ +} while (0) + +#define read_lock_irqsave(lock, flags) \ +do { \ + local_irq_save(flags); \ + preempt_disable(); \ + _metered_read_lock(lock); \ +} while (0) + +#define read_lock_irq(lock) \ +do { \ + local_irq_disable(); \ + preempt_disable(); \ + _metered_read_lock(lock); \ +} while (0) + +#define read_lock_bh(lock) \ +do { \ + local_bh_disable(); \ + preempt_disable(); \ + _metered_read_lock(lock); \ +} while (0) + +#define read_unlock_bh(lock) \ +do { \ + _metered_read_unlock(lock); \ + preempt_enable(); \ + local_bh_enable(); \ +} while (0) + +#define write_lock_irqsave(lock, flags) \ +do { \ + local_irq_save(flags); \ + preempt_disable(); \ + _metered_write_lock(lock); \ +} while (0) + +#define write_lock_irq(lock) \ +do { \ + local_irq_disable(); \ + preempt_disable(); \ + _metered_write_lock(lock); \ +} while (0) + +#define write_lock_bh(lock) \ +do { \ + local_bh_disable(); \ + preempt_disable(); \ + _metered_write_lock(lock); \ +} while (0) + +#define write_unlock_bh(lock) \ +do { \ + _metered_write_unlock(lock); \ + preempt_enable(); \ + local_bh_enable(); \ +} while (0) + +#endif /* !CONFIG_LOCKMETER */ + /* "lock on reference count zero" */ #ifndef ATOMIC_DEC_AND_LOCK #include --- linux-2.6.9-rc1/include/linux/sunrpc/sched.h 2004-08-24 00:03:14.000000000 -0700 +++ 25/include/linux/sunrpc/sched.h 2004-09-03 00:56:48.779745000 -0700 @@ -11,7 +11,9 @@ #include #include +#include #include +#include #include /* @@ -25,11 +27,18 @@ struct rpc_message { struct rpc_cred * rpc_cred; /* Credentials */ }; +struct rpc_wait_queue; +struct rpc_wait { + struct list_head list; /* wait queue links */ + struct list_head links; /* Links to related tasks */ + wait_queue_head_t waitq; /* sync: sleep on this q */ + struct rpc_wait_queue * rpc_waitq; /* RPC wait queue we're on */ +}; + /* * This is the RPC task struct */ struct rpc_task { - struct list_head tk_list; /* wait queue links */ #ifdef RPC_DEBUG unsigned long tk_magic; /* 0xf00baa */ #endif @@ -37,7 +46,6 @@ struct rpc_task { struct rpc_clnt * tk_client; /* RPC client */ struct rpc_rqst * tk_rqstp; /* RPC request */ int tk_status; /* result of last operation */ - struct rpc_wait_queue * tk_rpcwait; /* RPC wait queue we're on */ /* * RPC call state @@ -70,13 +78,18 @@ struct rpc_task { * you have a pathological interest in kernel oopses. */ struct timer_list tk_timer; /* kernel timer */ - wait_queue_head_t tk_wait; /* sync: sleep on this q */ unsigned long tk_timeout; /* timeout for rpc_sleep() */ unsigned short tk_flags; /* misc flags */ unsigned char tk_active : 1;/* Task has been activated */ unsigned char tk_priority : 2;/* Task priority */ unsigned long tk_runstate; /* Task run status */ - struct list_head tk_links; /* links to related tasks */ + struct workqueue_struct *tk_workqueue; /* Normally rpciod, but could + * be any workqueue + */ + union { + struct work_struct tk_work; /* Async task work queue */ + struct rpc_wait tk_wait; /* RPC wait */ + } u; #ifdef RPC_DEBUG unsigned short tk_pid; /* debugging aid */ #endif @@ -87,11 +100,11 @@ struct rpc_task { /* support walking a list of tasks on a wait queue */ #define task_for_each(task, pos, head) \ list_for_each(pos, head) \ - if ((task=list_entry(pos, struct rpc_task, tk_list)),1) + if ((task=list_entry(pos, struct rpc_task, u.tk_wait.list)),1) #define task_for_first(task, head) \ if (!list_empty(head) && \ - ((task=list_entry((head)->next, struct rpc_task, tk_list)),1)) + ((task=list_entry((head)->next, struct rpc_task, u.tk_wait.list)),1)) /* .. and walking list of all tasks */ #define alltask_for_each(task, pos, head) \ @@ -126,22 +139,38 @@ typedef void (*rpc_action)(struct rpc_ #define RPC_IS_SOFT(t) ((t)->tk_flags & RPC_TASK_SOFT) #define RPC_TASK_UNINTERRUPTIBLE(t) ((t)->tk_flags & RPC_TASK_NOINTR) -#define RPC_TASK_SLEEPING 0 -#define RPC_TASK_RUNNING 1 -#define RPC_IS_SLEEPING(t) (test_bit(RPC_TASK_SLEEPING, &(t)->tk_runstate)) -#define RPC_IS_RUNNING(t) (test_bit(RPC_TASK_RUNNING, &(t)->tk_runstate)) +#define RPC_TASK_RUNNING 0 +#define RPC_TASK_QUEUED 1 +#define RPC_TASK_WAKEUP 2 +#define RPC_IS_RUNNING(t) (test_bit(RPC_TASK_RUNNING, &(t)->tk_runstate)) #define rpc_set_running(t) (set_bit(RPC_TASK_RUNNING, &(t)->tk_runstate)) -#define rpc_clear_running(t) (clear_bit(RPC_TASK_RUNNING, &(t)->tk_runstate)) +#define rpc_test_and_set_running(t) \ + (test_and_set_bit(RPC_TASK_RUNNING, &(t)->tk_runstate)) +#define rpc_clear_running(t) \ + do { \ + smp_mb__before_clear_bit(); \ + clear_bit(RPC_TASK_RUNNING, &(t)->tk_runstate); \ + smp_mb__after_clear_bit(); \ + } while (0) -#define rpc_set_sleeping(t) (set_bit(RPC_TASK_SLEEPING, &(t)->tk_runstate)) +#define RPC_IS_QUEUED(t) (test_bit(RPC_TASK_QUEUED, &(t)->tk_runstate)) +#define rpc_set_queued(t) (set_bit(RPC_TASK_QUEUED, &(t)->tk_runstate)) +#define rpc_clear_queued(t) \ + do { \ + smp_mb__before_clear_bit(); \ + clear_bit(RPC_TASK_QUEUED, &(t)->tk_runstate); \ + smp_mb__after_clear_bit(); \ + } while (0) -#define rpc_clear_sleeping(t) \ +#define rpc_start_wakeup(t) \ + (test_and_set_bit(RPC_TASK_WAKEUP, &(t)->tk_runstate) == 0) +#define rpc_finish_wakeup(t) \ do { \ smp_mb__before_clear_bit(); \ - clear_bit(RPC_TASK_SLEEPING, &(t)->tk_runstate); \ + clear_bit(RPC_TASK_WAKEUP, &(t)->tk_runstate); \ smp_mb__after_clear_bit(); \ - } while(0) + } while (0) /* * Task priorities. @@ -157,6 +186,7 @@ typedef void (*rpc_action)(struct rpc_ * RPC synchronization objects */ struct rpc_wait_queue { + spinlock_t lock; struct list_head tasks[RPC_NR_PRIORITY]; /* task queue for each priority level */ unsigned long cookie; /* cookie of last task serviced */ unsigned char maxpriority; /* maximum priority (0 if queue is not a priority queue) */ @@ -177,6 +207,7 @@ struct rpc_wait_queue { #ifndef RPC_DEBUG # define RPC_WAITQ_INIT(var,qname) { \ + .lock = SPIN_LOCK_UNLOCKED, \ .tasks = { \ [0] = LIST_HEAD_INIT(var.tasks[0]), \ [1] = LIST_HEAD_INIT(var.tasks[1]), \ @@ -185,6 +216,7 @@ struct rpc_wait_queue { } #else # define RPC_WAITQ_INIT(var,qname) { \ + .lock = SPIN_LOCK_UNLOCKED, \ .tasks = { \ [0] = LIST_HEAD_INIT(var.tasks[0]), \ [1] = LIST_HEAD_INIT(var.tasks[1]), \ @@ -209,13 +241,10 @@ void rpc_killall_tasks(struct rpc_clnt int rpc_execute(struct rpc_task *); void rpc_run_child(struct rpc_task *parent, struct rpc_task *child, rpc_action action); -int rpc_add_wait_queue(struct rpc_wait_queue *, struct rpc_task *); -void rpc_remove_wait_queue(struct rpc_task *); void rpc_init_priority_wait_queue(struct rpc_wait_queue *, const char *); void rpc_init_wait_queue(struct rpc_wait_queue *, const char *); void rpc_sleep_on(struct rpc_wait_queue *, struct rpc_task *, rpc_action action, rpc_action timer); -void rpc_add_timer(struct rpc_task *, rpc_action); void rpc_wake_up_task(struct rpc_task *); void rpc_wake_up(struct rpc_wait_queue *); struct rpc_task *rpc_wake_up_next(struct rpc_wait_queue *); --- linux-2.6.9-rc1/include/linux/suspend.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/include/linux/suspend.h 2004-09-03 00:56:33.440076984 -0700 @@ -23,16 +23,6 @@ typedef struct pbe { #define SWAP_FILENAME_MAXLENGTH 32 -struct suspend_header { - u32 version_code; - unsigned long num_physpages; - char machine[8]; - char version[20]; - int num_cpus; - int page_size; - suspend_pagedir_t *suspend_pagedir; - unsigned int num_pbes; -}; #define SUSPEND_PD_PAGES(x) (((x)*sizeof(struct pbe))/PAGE_SIZE+1) @@ -45,16 +35,12 @@ extern void drain_local_pages(void); /* kernel/power/swsusp.c */ extern int software_suspend(void); -extern unsigned int nr_copy_pages __nosavedata; -extern suspend_pagedir_t *pagedir_nosave __nosavedata; - #else /* CONFIG_SOFTWARE_SUSPEND */ static inline int software_suspend(void) { printk("Warning: fake suspend called\n"); return -EPERM; } -#define software_resume() do { } while(0) #endif /* CONFIG_SOFTWARE_SUSPEND */ @@ -78,12 +64,6 @@ static inline void disable_nonboot_cpus( static inline void enable_nonboot_cpus(void) {} #endif -asmlinkage void do_magic(int is_resume); -asmlinkage void do_magic_resume_1(void); -asmlinkage void do_magic_resume_2(void); -asmlinkage void do_magic_suspend_1(void); -asmlinkage void do_magic_suspend_2(void); - void save_processor_state(void); void restore_processor_state(void); struct saved_context; --- linux-2.6.9-rc1/include/linux/syscalls.h 2004-08-24 00:03:49.000000000 -0700 +++ 25/include/linux/syscalls.h 2004-09-02 20:51:52.000000000 -0700 @@ -162,6 +162,8 @@ asmlinkage long sys_exit(int error_code) asmlinkage void sys_exit_group(int error_code); asmlinkage long sys_wait4(pid_t pid, unsigned int __user *stat_addr, int options, struct rusage __user *ru); +asmlinkage long sys_waitid(int which, pid_t pid, + struct siginfo __user *infop, int options); asmlinkage long sys_waitpid(pid_t pid, unsigned int __user *stat_addr, int options); asmlinkage long sys_set_tid_address(int __user *tidptr); asmlinkage long sys_futex(u32 __user *uaddr, int op, int val, --- linux-2.6.9-rc1/include/linux/sysctl.h 2004-08-24 00:02:48.000000000 -0700 +++ 25/include/linux/sysctl.h 2004-09-03 00:56:47.266974976 -0700 @@ -134,6 +134,7 @@ enum KERN_SPARC_SCONS_PWROFF=64, /* int: serial console power-off halt */ KERN_HZ_TIMER=65, /* int: hz timer on or off */ KERN_UNKNOWN_NMI_PANIC=66, /* int: unknown nmi panic flag */ + KERN_SCHED_TIMESLICE=67, /* int: base timeslice for scheduler */ }; @@ -166,6 +167,7 @@ enum VM_BLOCK_DUMP=24, /* block dump mode */ VM_HUGETLB_GROUP=25, /* permitted hugetlb group */ VM_VFS_CACHE_PRESSURE=26, /* dcache/icache reclaim pressure */ + VM_LEGACY_VA_LAYOUT=27, /* legacy/compatibility virtual address space layout */ }; --- linux-2.6.9-rc1/include/linux/sysfs.h 2004-08-24 00:02:26.000000000 -0700 +++ 25/include/linux/sysfs.h 2004-09-03 00:56:29.373695168 -0700 @@ -9,6 +9,8 @@ #ifndef _SYSFS_H_ #define _SYSFS_H_ +#include + struct kobject; struct module; @@ -57,6 +59,23 @@ struct sysfs_ops { ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t); }; +struct sysfs_dirent { + atomic_t s_count; + struct list_head s_sibling; + struct list_head s_children; + void * s_element; + int s_type; + umode_t s_mode; + struct dentry * s_dentry; +}; + +#define SYSFS_ROOT 0x0001 +#define SYSFS_DIR 0x0002 +#define SYSFS_KOBJ_ATTR 0x0004 +#define SYSFS_KOBJ_BIN_ATTR 0x0008 +#define SYSFS_KOBJ_LINK 0x0020 +#define SYSFS_NOT_PINNED (SYSFS_KOBJ_ATTR | SYSFS_KOBJ_BIN_ATTR | SYSFS_KOBJ_LINK) + #ifdef CONFIG_SYSFS extern int --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/linux/tc_act/tc_gact.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,34 @@ +#ifndef __LINUX_TC_GACT_H +#define __LINUX_TC_GACT_H + +#include + +#define TCA_ACT_GACT 5 +struct tc_gact +{ + tc_gen; + +}; + +struct tc_gact_p +{ +#define PGACT_NONE 0 +#define PGACT_NETRAND 1 +#define PGACT_DETERM 2 +#define MAX_RAND (PGACT_DETERM + 1 ) + __u16 ptype; + __u16 pval; + int paction; +}; + +enum +{ + TCA_GACT_UNSPEC, + TCA_GACT_TM, + TCA_GACT_PARMS, + TCA_GACT_PROB, + __TCA_GACT_MAX +}; +#define TCA_GACT_MAX (__TCA_GACT_MAX - 1) + +#endif --- linux-2.6.9-rc1/include/linux/times.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/include/linux/times.h 2004-09-03 00:57:09.240634472 -0700 @@ -55,6 +55,26 @@ static inline u64 jiffies_64_to_clock_t( } #endif +static inline u64 nsec_to_clock_t(u64 x) +{ +#if (NSEC_PER_SEC % USER_HZ) == 0 + do_div(x, (NSEC_PER_SEC / USER_HZ)); +#elif (USER_HZ % 512) == 0 + x *= USER_HZ/512; + do_div(x, (NSEC_PER_SEC / 512)); +#else + /* + * max relative error 5.7e-8 (1.8s per year) for USER_HZ <= 1024, + * overflow after 64.99 years. + * exact for HZ=60, 72, 90, 120, 144, 180, 300, 600, 900, ... + */ + x *= 9; + do_div(x, (unsigned long)((9ull * NSEC_PER_SEC + (USER_HZ/2)) + / USER_HZ)); +#endif + return x; +} + struct tms { clock_t tms_utime; clock_t tms_stime; --- linux-2.6.9-rc1/include/linux/timex.h 2004-08-24 00:01:52.000000000 -0700 +++ 25/include/linux/timex.h 2004-09-03 00:56:30.925459264 -0700 @@ -47,14 +47,18 @@ * kernel PLL updated to 1994-12-13 specs (rfc-1589) * 1997-08-30 Ulrich Windl * Added new constant NTP_PHASE_LIMIT + * 2004-08-12 Christoph Lameter + * Reworked time interpolation logic */ #ifndef _LINUX_TIMEX_H #define _LINUX_TIMEX_H #include #include +#include #include +#include /* * The following defines establish the engineering parameters of the PLL @@ -320,81 +324,148 @@ extern long pps_stbcnt; /* stability li #ifdef CONFIG_TIME_INTERPOLATION -struct time_interpolator { - /* cache-hot stuff first: */ - unsigned long (*get_offset) (void); - void (*update) (long); - void (*reset) (void); +#define TIME_SOURCE_CPU 0 +#define TIME_SOURCE_MMIO64 1 +#define TIME_SOURCE_MMIO32 2 +#define TIME_SOURCE_FUNCTION 3 + +/* For proper operations time_interpolator clocks must run slightly slower + * than the standard clock since the interpolator may only correct by having + * time jump forward during a tick. A slower clock is usually a side effect + * of the integer divide of the nanoseconds in a second by the frequency. + * The accuracy of the division can be increased by specifying a shift. + * However, this may cause the clock not to be slow enough. + * The interpolator will self-tune the clock by slowing down if no + * resets occur or speeding up if the time jumps per analysis cycle + * become too high. + * + * Setting jitter compensates for a fluctuating timesource by comparing + * to the last value read from the timesource to insure that an earlier value + * is not returned by a later call. The price to pay + * for the compensation is that the timer routines are not as scalable anymore. + */ - /* cache-cold stuff follows here: */ - struct time_interpolator *next; +#define INTERPOLATOR_ADJUST 65536 +#define INTERPOLATOR_MAX_SKIP 10*INTERPOLATOR_ADJUST + +struct time_interpolator { + unsigned short source; /* time source flags */ + unsigned char shift; /* increases accuracy of multiply by shifting. */ + /* Note that bits may be lost if shift is set too high */ + unsigned char jitter; /* if set compensate for fluctuations */ + unsigned nsec_per_cyc; /* set by register_time_interpolator() */ + void *addr; /* address of counter or function */ + unsigned long offset; /* nsec offset at last update of interpolator */ + unsigned long last_counter; /* counter value in units of the counter at last update */ + unsigned long last_cycle; /* Last timer value if TIME_SOURCE_JITTER is set */ unsigned long frequency; /* frequency in counts/second */ long drift; /* drift in parts-per-million (or -1) */ + unsigned long skips; /* skips forward */ + unsigned long ns_skipped; /* nanoseconds skipped */ + struct time_interpolator *next; }; -extern volatile unsigned long last_nsec_offset; -#ifndef __HAVE_ARCH_CMPXCHG -extern spin_lock_t last_nsec_offset_lock; -#endif extern struct time_interpolator *time_interpolator; -extern void register_time_interpolator(struct time_interpolator *); -extern void unregister_time_interpolator(struct time_interpolator *); +static inline unsigned long +time_interpolator_get_cycles(unsigned int src) +{ + unsigned long (*x)(void); -/* Called with xtime WRITE-lock acquired. */ -static inline void -time_interpolator_update(long delta_nsec) + switch (src) + { + case TIME_SOURCE_FUNCTION: + x = time_interpolator->addr; + return x(); + + case TIME_SOURCE_MMIO64 : + return readq(time_interpolator->addr); + + case TIME_SOURCE_MMIO32 : + return readl(time_interpolator->addr); + + default: return get_cycles(); + } +} + +static inline unsigned long +time_interpolator_get_counter(void) { - struct time_interpolator *ti = time_interpolator; + unsigned int src = time_interpolator->source; - if (last_nsec_offset > 0) { -#ifdef __HAVE_ARCH_CMPXCHG - unsigned long new, old; + if (time_interpolator->jitter) + { + unsigned long lcycle; + unsigned long now; do { - old = last_nsec_offset; - if (old > delta_nsec) - new = old - delta_nsec; - else - new = 0; - } while (cmpxchg(&last_nsec_offset, old, new) != old); -#else - /* - * This really hurts, because it serializes gettimeofday(), but without an - * atomic single-word compare-and-exchange, there isn't all that much else - * we can do. - */ - spin_lock(&last_nsec_offset_lock); - { - last_nsec_offset -= min(last_nsec_offset, delta_nsec); - } - spin_unlock(&last_nsec_offset_lock); -#endif + lcycle = time_interpolator->last_cycle; + now = time_interpolator_get_cycles(src); + if (lcycle && time_after(lcycle,now)) return lcycle; + /* Keep track of the last timer value returned. The use of cmpxchg here + * will cause contention in an SMP environment. + */ + } while (unlikely(cmpxchg(&time_interpolator->last_cycle,lcycle,now) != lcycle)); + return now; } - - if (ti) - (*ti->update)(delta_nsec); + else + return time_interpolator_get_cycles(src); } -/* Called with xtime WRITE-lock acquired. */ +extern void register_time_interpolator(struct time_interpolator *); +extern void unregister_time_interpolator(struct time_interpolator *); + static inline void time_interpolator_reset(void) { - struct time_interpolator *ti = time_interpolator; - - last_nsec_offset = 0; - if (ti) - (*ti->reset)(); + time_interpolator->offset = 0; + time_interpolator->last_counter = time_interpolator_get_counter(); } -/* Called with xtime READ-lock acquired. */ +#define GET_TI_NSECS(count,i) ((((count) - i->last_counter) * i->nsec_per_cyc) >> i->shift) + static inline unsigned long time_interpolator_get_offset(void) { - struct time_interpolator *ti = time_interpolator; - if (ti) - return (*ti->get_offset)(); - return last_nsec_offset; + return time_interpolator->offset + + GET_TI_NSECS(time_interpolator_get_counter(),time_interpolator); +} + +static inline void time_interpolator_update(long delta_nsec) +{ + unsigned long counter=time_interpolator_get_counter(); + unsigned long offset=time_interpolator->offset + GET_TI_NSECS(counter,time_interpolator); + + /* The interpolator compensates for late ticks by accumulating + * the late time in time_interpolator->offset. A tick earlier than + * expected will lead to a reset of the offset and a corresponding + * jump of the clock forward. Again this only works if the + * interpolator clock is running slightly slower than the regular clock + * and the tuning logic insures that. + */ + + if (delta_nsec < 0 || (unsigned long) delta_nsec < offset) + time_interpolator->offset = offset - delta_nsec; + else { + time_interpolator->skips++; + time_interpolator->ns_skipped += delta_nsec - offset; + time_interpolator->offset = 0; + } + time_interpolator->last_counter = counter; + + /* Tuning logic for time interpolator invoked every minute or so. + * Decrease interpolator clock speed if no skips occurred and an offset is carried. + * Increase interpolator clock speed if we skip too much time. + */ + if (jiffies % INTERPOLATOR_ADJUST == 0) + { + if (time_interpolator->skips == 0 && time_interpolator->offset > TICK_NSEC) + time_interpolator->nsec_per_cyc--; + if (time_interpolator->ns_skipped > INTERPOLATOR_MAX_SKIP && time_interpolator->offset == 0) + time_interpolator->nsec_per_cyc++; + time_interpolator->skips = 0; + time_interpolator->ns_skipped = 0; + } } #else /* !CONFIG_TIME_INTERPOLATION */ --- linux-2.6.9-rc1/include/linux/topology.h 2004-08-24 00:03:18.000000000 -0700 +++ 25/include/linux/topology.h 2004-09-02 20:51:52.000000000 -0700 @@ -55,7 +55,7 @@ static inline int __next_node_with_cpus( for (node = 0; node < numnodes; node = __next_node_with_cpus(node)) #ifndef node_distance -#define node_distance(from,to) (from != to) +#define node_distance(from,to) ((from) != (to)) #endif #ifndef PENALTY_FOR_NODE_WITH_CPUS #define PENALTY_FOR_NODE_WITH_CPUS (1) --- linux-2.6.9-rc1/include/linux/usb_gadget.h 2004-08-24 00:02:32.000000000 -0700 +++ 25/include/linux/usb_gadget.h 2004-09-02 20:51:52.000000000 -0700 @@ -767,12 +767,6 @@ usb_gadget_disconnect (struct usb_gadget * the (remote) host can't do that any longer; or an error state might * be cleared, to make the device behave identically whether or not * power is maintained. - * - * If the OTG b_hnp_enabled flag is set during a suspend() call, the - * device may use HNP to switch from "B-Peripheral" to "B-Host" mode - * (or back from "A-Peripheral" mode to the original "A-Host") if - * the gadget driver calls usb_gadget_disconnect() before the device - * is resumed. */ struct usb_gadget_driver { char *function; --- linux-2.6.9-rc1/include/linux/usb.h 2004-08-24 00:02:32.000000000 -0700 +++ 25/include/linux/usb.h 2004-09-02 20:51:52.000000000 -0700 @@ -651,8 +651,6 @@ typedef void (*usb_complete_t)(struct ur * it likes with the URB, including resubmitting or freeing it. * @iso_frame_desc: Used to provide arrays of ISO transfer buffers and to * collect the transfer status for each buffer. - * @timeout: If set to zero, the urb will never timeout. Otherwise this is - * the time in jiffies that this urb will timeout in. * * This structure identifies USB transfer requests. URBs must be allocated by * calling usb_alloc_urb() and freed with a call to usb_free_urb(). @@ -682,8 +680,8 @@ typedef void (*usb_complete_t)(struct ur * * Initialization: * - * All URBs submitted must initialize dev, pipe, - * transfer_flags (may be zero), complete, timeout (may be zero). + * All URBs submitted must initialize the dev, pipe, transfer_flags (may be + * zero), and complete fields. * The URB_ASYNC_UNLINK transfer flag affects later invocations of * the usb_unlink_urb() routine. Note: Failure to set URB_ASYNC_UNLINK * with usb_unlink_urb() is deprecated. For synchronous unlinks use @@ -783,7 +781,6 @@ struct urb int number_of_packets; /* (in) number of ISO packets */ int interval; /* (modify) transfer interval (INT/ISO) */ int error_count; /* (return) number of ISO errors */ - int timeout; /* (in) timeout, in jiffies */ void *context; /* (in) context for completion */ usb_complete_t complete; /* (in) completion routine */ struct usb_iso_packet_descriptor iso_frame_desc[0]; /* (in) ISO ONLY */ --- linux-2.6.9-rc1/include/linux/usb_otg.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/include/linux/usb_otg.h 2004-09-02 20:51:52.000000000 -0700 @@ -114,3 +114,5 @@ otg_start_srp(struct otg_transceiver *ot } +/* for OTG controller drivers (and maybe other stuff) */ +extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num); --- linux-2.6.9-rc1/include/linux/wait.h 2004-08-24 00:02:48.000000000 -0700 +++ 25/include/linux/wait.h 2004-09-03 00:57:14.699804552 -0700 @@ -3,11 +3,20 @@ #define WNOHANG 0x00000001 #define WUNTRACED 0x00000002 +#define WSTOPPED WUNTRACED +#define WEXITED 0x00000004 +#define WCONTINUED 0x00000008 +#define WNOWAIT 0x01000000 /* Don't reap, just poll status. */ #define __WNOTHREAD 0x20000000 /* Don't wait on children of other threads in this group */ #define __WALL 0x40000000 /* Wait on all children, regardless of type */ #define __WCLONE 0x80000000 /* Wait only on non-SIGCHLD children */ +/* First argument to waitid: */ +#define P_ALL 0 +#define P_PID 1 +#define P_PGID 2 + #ifdef __KERNEL__ #include @@ -15,6 +24,7 @@ #include #include #include +#include typedef struct __wait_queue wait_queue_t; typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int sync, void *key); @@ -28,6 +38,16 @@ struct __wait_queue { struct list_head task_list; }; +struct wait_bit_key { + void *flags; + int bit_nr; +}; + +struct wait_bit_queue { + struct wait_bit_key key; + wait_queue_t wait; +}; + struct __wait_queue_head { spinlock_t lock; struct list_head task_list; @@ -54,6 +74,9 @@ typedef struct __wait_queue_head wait_qu #define DECLARE_WAIT_QUEUE_HEAD(name) \ wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name) +#define __WAIT_BIT_KEY_INITIALIZER(word, bit) \ + { .flags = word, .bit_nr = bit, } + static inline void init_waitqueue_head(wait_queue_head_t *q) { q->lock = SPIN_LOCK_UNLOCKED; @@ -80,6 +103,15 @@ static inline int waitqueue_active(wait_ return !list_empty(&q->task_list); } +/* + * Used to distinguish between sync and async io wait context: + * sync i/o typically specifies a NULL wait queue entry or a wait + * queue entry bound to a task (current task) to wake up. + * aio specifies a wait queue entry with an async notification + * callback routine, not associated with any task. + */ +#define is_sync_wait(wait) (!(wait) || ((wait)->task)) + extern void FASTCALL(add_wait_queue(wait_queue_head_t *q, wait_queue_t * wait)); extern void FASTCALL(add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t * wait)); extern void FASTCALL(remove_wait_queue(wait_queue_head_t *q, wait_queue_t * wait)); @@ -107,6 +139,13 @@ static inline void __remove_wait_queue(w void FASTCALL(__wake_up(wait_queue_head_t *q, unsigned int mode, int nr, void *key)); extern void FASTCALL(__wake_up_locked(wait_queue_head_t *q, unsigned int mode)); extern void FASTCALL(__wake_up_sync(wait_queue_head_t *q, unsigned int mode, int nr)); +void FASTCALL(__wake_up_bit(wait_queue_head_t *, void *, int)); +int FASTCALL(__wait_on_bit(wait_queue_head_t *, struct wait_bit_queue *, int (*)(void *), unsigned)); +int FASTCALL(__wait_on_bit_lock(wait_queue_head_t *, struct wait_bit_queue *, int (*)(void *), unsigned)); +void FASTCALL(wake_up_bit(void *, int)); +int FASTCALL(out_of_line_wait_on_bit(void *, int, int (*)(void *), unsigned)); +int FASTCALL(out_of_line_wait_on_bit_lock(void *, int, int (*)(void *), unsigned)); +wait_queue_head_t *FASTCALL(bit_waitqueue(void *, int)); #define wake_up(x) __wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 1, NULL) #define wake_up_nr(x, nr) __wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, nr, NULL) @@ -259,6 +298,7 @@ void FASTCALL(prepare_to_wait_exclusive( wait_queue_t *wait, int state)); void FASTCALL(finish_wait(wait_queue_head_t *q, wait_queue_t *wait)); int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key); +int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *key); #define DEFINE_WAIT(name) \ wait_queue_t name = { \ @@ -269,12 +309,69 @@ int autoremove_wake_function(wait_queue_ }, \ } +#define DEFINE_WAIT_BIT(name, word, bit) \ + struct wait_bit_queue name = { \ + .key = __WAIT_BIT_KEY_INITIALIZER(word, bit), \ + .wait = { \ + .task = current, \ + .func = wake_bit_function, \ + .task_list = \ + LIST_HEAD_INIT(name.wait.task_list), \ + }, \ + } + #define init_wait(wait) \ do { \ wait->task = current; \ wait->func = autoremove_wake_function; \ INIT_LIST_HEAD(&wait->task_list); \ } while (0) + +/** + * wait_on_bit - wait for a bit to be cleared + * @word: the word being waited on, a kernel virtual address + * @bit: the bit of the word being waited on + * @action: the function used to sleep, which may take special actions + * @mode: the task state to sleep in + * + * There is a standard hashed waitqueue table for generic use. This + * is the part of the hashtable's accessor API that waits on a bit. + * For instance, if one were to have waiters on a bitflag, one would + * call wait_on_bit() in threads waiting for the bit to clear. + * One uses wait_on_bit() where one is waiting for the bit to clear, + * but has no intention of setting it. + */ +static inline int wait_on_bit(void *word, int bit, + int (*action)(void *), unsigned mode) +{ + if (!test_bit(bit, word)) + return 0; + return out_of_line_wait_on_bit(word, bit, action, mode); +} + +/** + * wait_on_bit_lock - wait for a bit to be cleared, when wanting to set it + * @word: the word being waited on, a kernel virtual address + * @bit: the bit of the word being waited on + * @action: the function used to sleep, which may take special actions + * @mode: the task state to sleep in + * + * There is a standard hashed waitqueue table for generic use. This + * is the part of the hashtable's accessor API that waits on a bit + * when one intends to set it, for instance, trying to lock bitflags. + * For instance, if one were to have waiters trying to set bitflag + * and waiting for it to clear before setting it, one would call + * wait_on_bit() in threads waiting to be able to set the bit. + * One uses wait_on_bit_lock() where one is waiting for the bit to + * clear with the intention of setting it, and when done, clearing it. + */ +static inline int wait_on_bit_lock(void *word, int bit, + int (*action)(void *), unsigned mode) +{ + if (!test_and_set_bit(bit, word)) + return 0; + return out_of_line_wait_on_bit_lock(word, bit, action, mode); +} #endif /* __KERNEL__ */ --- linux-2.6.9-rc1/include/linux/wireless.h 2004-08-24 00:02:59.000000000 -0700 +++ 25/include/linux/wireless.h 2004-09-03 00:56:32.636199192 -0700 @@ -1,10 +1,10 @@ /* * This file define a set of standard wireless extensions * - * Version : 16 2.4.03 + * Version : 17 21.6.04 * * Authors : Jean Tourrilhes - HPL - - * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved. + * Copyright (c) 1997-2004 Jean Tourrilhes, All Rights Reserved. */ #ifndef _LINUX_WIRELESS_H @@ -47,12 +47,12 @@ * # include/net/iw_handler.h * * Note as well that /proc/net/wireless implementation has now moved in : - * # include/linux/wireless.c + * # net/core/wireless.c * * Wireless Events (2002 -> onward) : * -------------------------------- * Events are defined at the end of this file, and implemented in : - * # include/linux/wireless.c + * # net/core/wireless.c * * Other comments : * -------------- @@ -82,7 +82,7 @@ * (there is some stuff that will be added in the future...) * I just plan to increment with each new version. */ -#define WIRELESS_EXT 16 +#define WIRELESS_EXT 17 /* * Changes : @@ -175,6 +175,13 @@ * - Remove IW_MAX_GET_SPY because conflict with enhanced spy support * - Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy" * - Add IW_ENCODE_TEMP and iw_range->encoding_login_index + * + * V16 to V17 + * ---------- + * - Add flags to frequency -> auto/fixed + * - Document (struct iw_quality *)->updated, add new flags (INVALID) + * - Wireless Event capability in struct iw_range + * - Add support for relative TxPower (yick !) */ /**************************** CONSTANTS ****************************/ @@ -251,7 +258,7 @@ /* -------------------- DEV PRIVATE IOCTL LIST -------------------- */ -/* These 16 ioctl are wireless device private. +/* These 32 ioctl are wireless device private, for 16 commands. * Each driver is free to use them for whatever purpose it chooses, * however the driver *must* export the description of those ioctls * with SIOCGIWPRIV and *must* use arguments as defined below. @@ -266,8 +273,8 @@ * We now have 32 commands, so a bit more space ;-). * Also, all 'odd' commands are only usable by root and don't return the * content of ifr/iwr to user (but you are not obliged to use the set/get - * convention, just use every other two command). - * And I repeat : you are not obliged to use them with iwspy, but you + * convention, just use every other two command). More details in iwpriv.c. + * And I repeat : you are not forced to use them with iwpriv, but you * must be compliant with it. */ @@ -352,6 +359,18 @@ #define IW_MODE_SECOND 5 /* Secondary master/repeater (backup) */ #define IW_MODE_MONITOR 6 /* Passive monitor (listen only) */ +/* Statistics flags (bitmask in updated) */ +#define IW_QUAL_QUAL_UPDATED 0x1 /* Value was updated since last read */ +#define IW_QUAL_LEVEL_UPDATED 0x2 +#define IW_QUAL_NOISE_UPDATED 0x4 +#define IW_QUAL_QUAL_INVALID 0x10 /* Driver doesn't provide value */ +#define IW_QUAL_LEVEL_INVALID 0x20 +#define IW_QUAL_NOISE_INVALID 0x40 + +/* Frequency flags */ +#define IW_FREQ_AUTO 0x00 /* Let the driver decides */ +#define IW_FREQ_FIXED 0x01 /* Force a specific value */ + /* Maximum number of size of encoding token available * they are listed in the range structure */ #define IW_MAX_ENCODING_SIZES 8 @@ -390,6 +409,7 @@ #define IW_TXPOW_TYPE 0x00FF /* Type of value */ #define IW_TXPOW_DBM 0x0000 /* Value is in dBm */ #define IW_TXPOW_MWATT 0x0001 /* Value is in mW */ +#define IW_TXPOW_RELATIVE 0x0002 /* Value is in arbitrary units */ #define IW_TXPOW_RANGE 0x1000 /* Range of value between min/max */ /* Retry limits and lifetime flags available */ @@ -418,6 +438,25 @@ /* Max number of char in custom event - use multiple of them if needed */ #define IW_CUSTOM_MAX 256 /* In bytes */ +/* Event capability macros - in (struct iw_range *)->event_capa + * Because we have more than 32 possible events, we use an array of + * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */ +#define IW_EVENT_CAPA_BASE(cmd) ((cmd >= SIOCIWFIRSTPRIV) ? \ + (cmd - SIOCIWFIRSTPRIV + 0x60) : \ + (cmd - SIOCSIWCOMMIT)) +#define IW_EVENT_CAPA_INDEX(cmd) (IW_EVENT_CAPA_BASE(cmd) >> 5) +#define IW_EVENT_CAPA_MASK(cmd) (1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F)) +/* Event capability constants - event autogenerated by the kernel + * This list is valid for most 802.11 devices, customise as needed... */ +#define IW_EVENT_CAPA_K_0 (IW_EVENT_CAPA_MASK(0x8B04) | \ + IW_EVENT_CAPA_MASK(0x8B06) | \ + IW_EVENT_CAPA_MASK(0x8B1A)) +#define IW_EVENT_CAPA_K_1 (IW_EVENT_CAPA_MASK(0x8B2A)) +/* "Easy" macro to set events in iw_range (less efficient) */ +#define IW_EVENT_CAPA_SET(event_capa, cmd) (event_capa[IW_EVENT_CAPA_INDEX(cmd)] |= IW_EVENT_CAPA_MASK(cmd)) +#define IW_EVENT_CAPA_SET_KERNEL(event_capa) {event_capa[0] |= IW_EVENT_CAPA_K_0; event_capa[1] |= IW_EVENT_CAPA_K_1; } + + /****************************** TYPES ******************************/ /* --------------------------- SUBTYPES --------------------------- */ @@ -456,7 +495,7 @@ struct iw_freq __s32 m; /* Mantissa */ __s16 e; /* Exponent */ __u8 i; /* List index (when in range struct) */ - __u8 pad; /* Unused - just for alignement */ + __u8 flags; /* Flags (fixed/auto) */ }; /* @@ -610,11 +649,12 @@ struct iw_range /* Old Frequency (backward compat - moved lower ) */ __u16 old_num_channels; __u8 old_num_frequency; - /* Filler to keep "version" at the same offset */ - __s32 old_freq[6]; + + /* Wireless event capability bitmasks */ + __u32 event_capa[6]; /* signal level threshold range */ - __s32 sensitivity; + __s32 sensitivity; /* Quality of link & SNR stuff */ /* Quality range (link, level, noise) --- linux-2.6.9-rc1/include/linux/writeback.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/include/linux/writeback.h 2004-09-03 00:57:25.150215848 -0700 @@ -68,15 +68,16 @@ struct writeback_control { */ void writeback_inodes(struct writeback_control *wbc); void wake_up_inode(struct inode *inode); -void __wait_on_inode(struct inode * inode); +int inode_wait(void *); void sync_inodes_sb(struct super_block *, int wait); void sync_inodes(int wait); /* writeback.h requires fs.h; it, too, is not included from here. */ static inline void wait_on_inode(struct inode *inode) { - if (inode->i_state & I_LOCK) - __wait_on_inode(inode); + might_sleep(); + wait_on_bit(&inode->i_state, __I_LOCK, inode_wait, + TASK_UNINTERRUPTIBLE); } /* @@ -85,6 +86,7 @@ static inline void wait_on_inode(struct int wakeup_bdflush(long nr_pages); void laptop_io_completion(void); void laptop_sync_completion(void); +void throttle_vm_writeout(void); /* These are exported to sysctl. */ extern int dirty_background_ratio; --- linux-2.6.9-rc1/include/math-emu/op-common.h 2004-08-24 00:02:32.000000000 -0700 +++ 25/include/math-emu/op-common.h 2004-09-02 20:51:52.000000000 -0700 @@ -769,9 +769,9 @@ do { \ X##_c = FP_CLS_NORMAL; \ \ if ((X##_s = (r < 0))) \ - r = -r; \ - \ - ur_ = (unsigned rtype) r; \ + ur_ = (unsigned rtype) -r; \ + else \ + ur_ = (unsigned rtype) r; \ if (rsize <= _FP_W_TYPE_SIZE) \ __FP_CLZ(X##_e, ur_); \ else \ --- linux-2.6.9-rc1/include/media/id.h 2004-08-24 00:02:47.000000000 -0700 +++ 25/include/media/id.h 2004-09-03 00:57:13.634966432 -0700 @@ -35,4 +35,3 @@ #ifndef I2C_ALGO_SAA7134 # define I2C_ALGO_SAA7134 0x090000 #endif - --- linux-2.6.9-rc1/include/media/tuner.h 2004-08-24 00:03:18.000000000 -0700 +++ 25/include/media/tuner.h 2004-09-03 00:57:13.788943024 -0700 @@ -70,6 +70,8 @@ #define TUNER_PHILIPS_FM1236_MK3 43 #define TUNER_PHILIPS_4IN1 44 /* ATI TV Wonder Pro - Conexant */ #define TUNER_MICROTUNE_4049FM5 45 +#define TUNER_LG_NTSC_TAPE 47 +#define TUNER_TNF_8831BGFF 48 #define NOTUNER 0 #define PAL 1 /* PAL_BG */ @@ -88,6 +90,7 @@ #define Samsung 7 #define Microtune 8 #define HITACHI 9 +#define Panasonic 10 #define TUNER_SET_TYPE _IOW('t',1,int) /* set tuner type */ #define TUNER_SET_TVFREQ _IOW('t',2,int) /* set tv freq */ @@ -96,4 +99,18 @@ # define TUNER_SET_MODE _IOW('t',4,int) /* set tuner mode */ #endif +#define TDA9887_SET_CONFIG _IOW('t',5,int) +/* tv card specific */ +# define TDA9887_PRESENT (1<<0) +# define TDA9887_PORT1 (1<<1) +# define TDA9887_PORT2 (1<<2) +# define TDA9887_QSS (1<<3) +# define TDA9887_INTERCARRIER (1<<4) +/* config options */ +# define TDA9887_DEEMPHASIS_MASK (3<<16) +# define TDA9887_DEEMPHASIS_NONE (1<<16) +# define TDA9887_DEEMPHASIS_50 (2<<16) +# define TDA9887_DEEMPHASIS_75 (3<<16) +# define TDA9887_AUTOMUTE (1<<18) + #endif --- linux-2.6.9-rc1/include/net/ip.h 2004-08-24 00:03:17.000000000 -0700 +++ 25/include/net/ip.h 2004-09-02 20:51:52.000000000 -0700 @@ -255,6 +255,7 @@ extern int ip_call_ra_chain(struct sk_bu */ struct sk_buff *ip_defrag(struct sk_buff *skb); +extern void ipfrag_flush(void); extern int ip_frag_nqueues; extern atomic_t ip_frag_mem; --- linux-2.6.9-rc1/include/net/ipv6.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/include/net/ipv6.h 2004-09-03 00:56:41.030923000 -0700 @@ -16,7 +16,7 @@ #define _NET_IPV6_H #include -#include +#include #include #include #include --- linux-2.6.9-rc1/include/net/iw_handler.h 2004-08-24 00:03:50.000000000 -0700 +++ 25/include/net/iw_handler.h 2004-09-03 00:56:32.637199040 -0700 @@ -1,10 +1,10 @@ /* * This file define the new driver API for Wireless Extensions * - * Version : 5 4.12.02 + * Version : 6 21.6.04 * * Authors : Jean Tourrilhes - HPL - - * Copyright (c) 2001-2002 Jean Tourrilhes, All Rights Reserved. + * Copyright (c) 2001-2004 Jean Tourrilhes, All Rights Reserved. */ #ifndef _IW_HANDLER_H @@ -206,7 +206,7 @@ * will be needed... * I just plan to increment with each new version. */ -#define IW_HANDLER_VERSION 5 +#define IW_HANDLER_VERSION 6 /* * Changes : @@ -224,11 +224,18 @@ * V4 to V5 * -------- * - Add new spy support : struct iw_spy_data & prototypes + * + * V5 to V6 + * -------- + * - Change the way we get to spy_data method for added safety + * - Remove spy #ifdef, they are always on -> cleaner code + * - Add IW_DESCR_FLAG_NOMAX flag for very large requests + * - Start migrating get_wireless_stats to struct iw_handler_def */ /**************************** CONSTANTS ****************************/ -/* Enable enhanced spy support. Disable to reduce footprint */ +/* Enhanced spy support available */ #define IW_WIRELESS_SPY #define IW_WIRELESS_THRSPY @@ -258,6 +265,7 @@ #define IW_DESCR_FLAG_EVENT 0x0002 /* Generate an event on SET */ #define IW_DESCR_FLAG_RESTRICT 0x0004 /* GET : request is ROOT only */ /* SET : Omit payload from generated iwevent */ +#define IW_DESCR_FLAG_NOMAX 0x0008 /* GET : no limit on request size */ /* Driver level flags */ #define IW_DESCR_FLAG_WAIT 0x0100 /* Wait for driver event */ @@ -303,31 +311,33 @@ struct iw_handler_def { /* Number of handlers defined (more precisely, index of the * last defined handler + 1) */ - __u16 num_standard; - __u16 num_private; + const __u16 num_standard; + const __u16 num_private; /* Number of private arg description */ - __u16 num_private_args; + const __u16 num_private_args; /* Array of handlers for standard ioctls * We will call dev->wireless_handlers->standard[ioctl - SIOCSIWNAME] */ - iw_handler * standard; + const iw_handler * standard; /* Array of handlers for private ioctls * Will call dev->wireless_handlers->private[ioctl - SIOCIWFIRSTPRIV] */ - iw_handler * private; + const iw_handler * private; /* Arguments of private handler. This one is just a list, so you * can put it in any order you want and should not leave holes... * We will automatically export that to user space... */ - struct iw_priv_args * private_args; + const struct iw_priv_args * private_args; - /* Driver enhanced spy support */ - long spy_offset; /* Spy data offset */ + /* This field will be *removed* in the next version of WE */ + const long spy_offset; /* DO NOT USE */ - /* In the long term, get_wireless_stats will move from - * 'struct net_device' to here, to minimise bloat. */ + /* New location of get_wireless_stats, to de-bloat struct net_device. + * The old pointer in struct net_device will be gradually phased + * out, and drivers are encouraged to use this one... */ + struct iw_statistics* (*get_wireless_stats)(struct net_device *dev); }; /* ---------------------- IOCTL DESCRIPTION ---------------------- */ @@ -374,18 +384,29 @@ struct iw_ioctl_description */ struct iw_spy_data { -#ifdef IW_WIRELESS_SPY /* --- Standard spy support --- */ int spy_number; u_char spy_address[IW_MAX_SPY][ETH_ALEN]; struct iw_quality spy_stat[IW_MAX_SPY]; -#ifdef IW_WIRELESS_THRSPY /* --- Enhanced spy support (event) */ struct iw_quality spy_thr_low; /* Low threshold */ struct iw_quality spy_thr_high; /* High threshold */ u_char spy_thr_under[IW_MAX_SPY]; -#endif /* IW_WIRELESS_THRSPY */ -#endif /* IW_WIRELESS_SPY */ +}; + +/* --------------------- DEVICE WIRELESS DATA --------------------- */ +/* + * This is all the wireless data specific to a device instance that + * is managed by the core of Wireless Extensions. + * We only keep pointer to those structures, so that a driver is free + * to share them between instances. + * This structure should be initialised before registering the device. + * Access to this data follow the same rules as any other struct net_device + * data (i.e. valid as long as struct net_device exist, same locking rules). + */ +struct iw_public_data { + /* Driver enhanced spy support */ + struct iw_spy_data * spy_data; }; /**************************** PROTOTYPES ****************************/ @@ -394,6 +415,9 @@ struct iw_spy_data * Those may be called only within the kernel. */ +/* Data needed by fs/compat_ioctl.c for 32->64 bit conversion */ +extern const char iw_priv_type_size[]; + /* First : function strictly used inside the kernel */ /* Handle /proc/net/wireless, called in net/code/dev.c */ --- linux-2.6.9-rc1/include/net/neighbour.h 2004-08-24 00:03:38.000000000 -0700 +++ 25/include/net/neighbour.h 2004-09-02 20:51:52.000000000 -0700 @@ -46,6 +46,7 @@ #include #include #include +#include #include #include @@ -66,6 +67,8 @@ struct neigh_parms void *sysctl_table; + struct rcu_head rcu_head; + int base_reachable_time; int retrans_time; int gc_staletime; --- linux-2.6.9-rc1/include/net/pkt_act.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/include/net/pkt_act.h 2004-09-02 23:57:37.665596408 -0700 @@ -279,6 +279,7 @@ tcf_hash_init(struct tc_st *parm, struct if (NULL == p) { return tcf_hash_create(parm, est, a, size, ovr, bind); } + return NULL; } #endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/net/tc_act/tc_gact.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,17 @@ +#ifndef __NET_TC_GACT_H +#define __NET_TC_GACT_H + +#include + +struct tcf_gact +{ + tca_gen(gact); +#ifdef CONFIG_GACT_PROB + u16 ptype; + u16 pval; + int paction; +#endif + +}; + +#endif --- linux-2.6.9-rc1/include/net/tcp.h 2004-08-24 00:01:54.000000000 -0700 +++ 25/include/net/tcp.h 2004-09-02 20:51:52.000000000 -0700 @@ -611,7 +611,6 @@ extern int sysctl_tcp_nometrics_save; extern int sysctl_tcp_bic; extern int sysctl_tcp_bic_fast_convergence; extern int sysctl_tcp_bic_low_window; -extern int sysctl_tcp_default_win_scale; extern int sysctl_tcp_moderate_rcvbuf; extern atomic_t tcp_memory_allocated; @@ -1690,68 +1689,10 @@ static inline void tcp_syn_build_options *ptr++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_WINDOW << 16) | (TCPOLEN_WINDOW << 8) | (wscale)); } -/* Determine a window scaling and initial window to offer. - * Based on the assumption that the given amount of space - * will be offered. Store the results in the tp structure. - * NOTE: for smooth operation initial space offering should - * be a multiple of mss if possible. We assume here that mss >= 1. - * This MUST be enforced by all callers. - */ -static inline void tcp_select_initial_window(int __space, __u32 mss, - __u32 *rcv_wnd, - __u32 *window_clamp, - int wscale_ok, - __u8 *rcv_wscale) -{ - unsigned int space = (__space < 0 ? 0 : __space); - - /* If no clamp set the clamp to the max possible scaled window */ - if (*window_clamp == 0) - (*window_clamp) = (65535 << 14); - space = min(*window_clamp, space); - - /* Quantize space offering to a multiple of mss if possible. */ - if (space > mss) - space = (space / mss) * mss; - - /* NOTE: offering an initial window larger than 32767 - * will break some buggy TCP stacks. We try to be nice. - * If we are not window scaling, then this truncates - * our initial window offering to 32k. There should also - * be a sysctl option to stop being nice. - */ - (*rcv_wnd) = min(space, MAX_TCP_WINDOW); - (*rcv_wscale) = 0; - if (wscale_ok) { - /* See RFC1323 for an explanation of the limit to 14 */ - while (space > 65535 && (*rcv_wscale) < 14) { - space >>= 1; - (*rcv_wscale)++; - } - if (*rcv_wscale && sysctl_tcp_app_win && space>=mss && - space - max((space>>sysctl_tcp_app_win), mss>>*rcv_wscale) < 65536/2) - (*rcv_wscale)--; - - *rcv_wscale = max((__u8)sysctl_tcp_default_win_scale, - *rcv_wscale); - } - - /* Set initial window to value enough for senders, - * following RFC1414. Senders, not following this RFC, - * will be satisfied with 2. - */ - if (mss > (1<<*rcv_wscale)) { - int init_cwnd = 4; - if (mss > 1460*3) - init_cwnd = 2; - else if (mss > 1460) - init_cwnd = 3; - if (*rcv_wnd > init_cwnd*mss) - *rcv_wnd = init_cwnd*mss; - } - /* Set the clamp no higher than max representable value */ - (*window_clamp) = min(65535U << (*rcv_wscale), *window_clamp); -} +/* Determine a window scaling and initial window to offer. */ +extern void tcp_select_initial_window(int __space, __u32 mss, + __u32 *rcv_wnd, __u32 *window_clamp, + int wscale_ok, __u8 *rcv_wscale); static inline int tcp_win_from_space(int space) { @@ -1761,13 +1702,13 @@ static inline int tcp_win_from_space(int } /* Note: caller must be prepared to deal with negative returns */ -static inline int tcp_space(struct sock *sk) +static inline int tcp_space(const struct sock *sk) { return tcp_win_from_space(sk->sk_rcvbuf - atomic_read(&sk->sk_rmem_alloc)); } -static inline int tcp_full_space( struct sock *sk) +static inline int tcp_full_space(const struct sock *sk) { return tcp_win_from_space(sk->sk_rcvbuf); } --- linux-2.6.9-rc1/include/net/xfrm.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/include/net/xfrm.h 2004-09-02 20:51:52.000000000 -0700 @@ -428,6 +428,7 @@ u16 xfrm_flowi_sport(struct flowi *fl) switch(fl->proto) { case IPPROTO_TCP: case IPPROTO_UDP: + case IPPROTO_SCTP: port = fl->fl_ip_sport; break; case IPPROTO_ICMP: @@ -447,6 +448,7 @@ u16 xfrm_flowi_dport(struct flowi *fl) switch(fl->proto) { case IPPROTO_TCP: case IPPROTO_UDP: + case IPPROTO_SCTP: port = fl->fl_ip_dport; break; case IPPROTO_ICMP: --- linux-2.6.9-rc1/include/pcmcia/ciscode.h 2004-08-24 00:03:18.000000000 -0700 +++ 25/include/pcmcia/ciscode.h 2004-09-03 00:57:11.582278488 -0700 @@ -135,4 +135,7 @@ #define MANFID_XIRCOM 0x0105 +#define MANFID_POSSIO 0x030c +#define PRODID_POSSIO_GCC 0x0003 + #endif /* _LINUX_CISCODE_H */ --- linux-2.6.9-rc1/include/pcmcia/cs.h 2004-08-24 00:01:50.000000000 -0700 +++ 25/include/pcmcia/cs.h 2004-09-03 00:56:47.825890008 -0700 @@ -318,6 +318,7 @@ typedef struct error_info_t { /* Special stuff for binding drivers to sockets */ typedef struct bind_req_t { struct pcmcia_socket *Socket; + struct pcmcia_device *device; u_char Function; dev_info_t *dev_info; } bind_req_t; @@ -452,6 +453,7 @@ int pcmcia_insert_card(struct pcmcia_soc int pcmcia_set_event_mask(client_handle_t handle, eventmask_t *mask); int pcmcia_report_error(client_handle_t handle, error_info_t *err); struct pci_bus *pcmcia_lookup_bus(client_handle_t handle); +struct device *pcmcia_lookup_device(client_handle_t handle); /* rsrc_mgr.c */ int pcmcia_adjust_resource_info(client_handle_t handle, adjust_t *adj); --- linux-2.6.9-rc1/include/pcmcia/ds.h 2004-08-24 00:01:50.000000000 -0700 +++ 25/include/pcmcia/ds.h 2004-09-03 00:56:47.825890008 -0700 @@ -151,6 +151,21 @@ struct pcmcia_driver { struct device_driver drv; }; +struct pcmcia_device { + struct device dev; + struct pcmcia_bus_socket *socket; + struct list_head device_list; + int function; + int id_mask; + cistpl_vers_1_t vers_1; + cistpl_manfid_t manfid; +}; + +#define to_pcmcia_device(n) container_of(n, struct pcmcia_device, dev) + +#define DEVICE_HAS_VERSION_INFO 0x0001 +#define DEVICE_HAS_MANF_INFO 0x0002 + /* driver registration */ int pcmcia_register_driver(struct pcmcia_driver *driver); void pcmcia_unregister_driver(struct pcmcia_driver *driver); --- linux-2.6.9-rc1/include/pcmcia/ss.h 2004-08-24 00:03:38.000000000 -0700 +++ 25/include/pcmcia/ss.h 2004-09-02 20:51:52.000000000 -0700 @@ -103,7 +103,7 @@ typedef struct pccard_mem_map { u_char map; u_char flags; u_short speed; - u_long sys_start, sys_stop; + u_long static_start; u_int card_start; struct resource *res; } pccard_mem_map; --- linux-2.6.9-rc1/include/scsi/scsi_device.h 2004-08-24 00:01:52.000000000 -0700 +++ 25/include/scsi/scsi_device.h 2004-09-02 20:51:52.000000000 -0700 @@ -129,8 +129,10 @@ struct scsi_device { #define transport_class_to_sdev(class_dev) \ container_of(class_dev, struct scsi_device, transport_classdev) -extern struct scsi_device *scsi_add_device(struct Scsi_Host *, - uint, uint, uint); +extern struct scsi_device *__scsi_add_device(struct Scsi_Host *, + uint, uint, uint, void *hostdata); +#define scsi_add_device(host, channel, target, lun) \ + __scsi_add_device(host, channel, target, lun, NULL) extern void scsi_remove_device(struct scsi_device *); extern int scsi_device_cancel(struct scsi_device *, int); @@ -183,13 +185,47 @@ extern int scsi_set_medium_removal(struc extern int scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage, unsigned char *buffer, int len, int timeout, int retries, struct scsi_mode_data *data); +extern int scsi_test_unit_ready(struct scsi_device *sdev, int timeout, + int retries); extern int scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state); extern int scsi_device_quiesce(struct scsi_device *sdev); extern void scsi_device_resume(struct scsi_device *sdev); extern const char *scsi_device_state_name(enum scsi_device_state); -static int inline scsi_device_online(struct scsi_device *sdev) +static inline int scsi_device_online(struct scsi_device *sdev) { return sdev->sdev_state != SDEV_OFFLINE; } + +/* accessor functions for the SCSI parameters */ +static inline int scsi_device_sync(struct scsi_device *sdev) +{ + return sdev->sdtr; +} +static inline int scsi_device_wide(struct scsi_device *sdev) +{ + return sdev->wdtr; +} +static inline int scsi_device_dt(struct scsi_device *sdev) +{ + return sdev->ppr; +} +static inline int scsi_device_dt_only(struct scsi_device *sdev) +{ + if (sdev->inquiry_len < 57) + return 0; + return (sdev->inquiry[56] & 0x0c) == 0x04; +} +static inline int scsi_device_ius(struct scsi_device *sdev) +{ + if (sdev->inquiry_len < 57) + return 0; + return sdev->inquiry[56] & 0x01; +} +static inline int scsi_device_qas(struct scsi_device *sdev) +{ + if (sdev->inquiry_len < 57) + return 0; + return sdev->inquiry[56] & 0x02; +} #endif /* _SCSI_SCSI_DEVICE_H */ --- linux-2.6.9-rc1/include/scsi/scsi_eh.h 2004-08-24 00:03:38.000000000 -0700 +++ 25/include/scsi/scsi_eh.h 2004-09-02 20:51:52.000000000 -0700 @@ -11,7 +11,6 @@ extern int scsi_delete_timer(struct scsi extern void scsi_report_bus_reset(struct Scsi_Host *, int); extern void scsi_report_device_reset(struct Scsi_Host *, int, int); extern int scsi_block_when_processing_errors(struct scsi_device *); -extern void scsi_sleep(int); /* * Reset request from external source --- linux-2.6.9-rc1/include/scsi/scsi.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/include/scsi/scsi.h 2004-09-02 20:51:52.000000000 -0700 @@ -354,14 +354,19 @@ struct scsi_lun { ((lun) & 0x07)) /* - * SCSI command sets + * struct scsi_device::scsi_level values. For SCSI devices other than those + * prior to SCSI-2 (i.e. over 12 years old) this value is (resp[2] + 1) + * where "resp" is a byte array of the response to an INQUIRY. The scsi_level + * variable is visible to the user via sysfs. */ #define SCSI_UNKNOWN 0 #define SCSI_1 1 #define SCSI_1_CCS 2 #define SCSI_2 3 -#define SCSI_3 4 +#define SCSI_3 4 /* SPC */ +#define SCSI_SPC_2 5 +#define SCSI_SPC_3 6 /* * INQ PERIPHERAL QUALIFIERS --- linux-2.6.9-rc1/include/sound/ac97_codec.h 2004-08-24 00:02:48.000000000 -0700 +++ 25/include/sound/ac97_codec.h 2004-09-02 20:51:52.000000000 -0700 @@ -364,11 +364,21 @@ #define AC97_RATES_MIC_ADC 4 #define AC97_RATES_SPDIF 5 +/* shared controllers */ +enum { + AC97_SHARED_TYPE_NONE, + AC97_SHARED_TYPE_ICH, + AC97_SHARED_TYPE_ATIIXP, + AC97_SHARED_TYPES +}; + /* * */ typedef struct _snd_ac97_bus ac97_bus_t; +typedef struct _snd_ac97_bus_ops ac97_bus_ops_t; +typedef struct _snd_ac97_template ac97_template_t; typedef struct _snd_ac97 ac97_t; enum ac97_pcm_cfg { @@ -405,29 +415,45 @@ struct snd_ac97_build_ops { int (*build_post_spdif) (ac97_t *ac97); }; -struct _snd_ac97_bus { - /* -- lowlevel (hardware) driver specific -- */ +struct _snd_ac97_bus_ops { void (*reset) (ac97_t *ac97); void (*write) (ac97_t *ac97, unsigned short reg, unsigned short val); unsigned short (*read) (ac97_t *ac97, unsigned short reg); void (*wait) (ac97_t *ac97); void (*init) (ac97_t *ac97); +}; + +struct _snd_ac97_bus { + /* -- lowlevel (hardware) driver specific -- */ + ac97_bus_ops_t *ops; void *private_data; void (*private_free) (ac97_bus_t *bus); /* --- */ snd_card_t *card; unsigned short num; /* bus number */ - unsigned short vra: 1, /* bridge supports VRA */ + unsigned short no_vra: 1, /* bridge doesn't support VRA */ isdin: 1;/* independent SDIN */ unsigned int clock; /* AC'97 base clock (usually 48000Hz) */ spinlock_t bus_lock; /* used mainly for slot allocation */ unsigned short used_slots[2][4]; /* actually used PCM slots */ unsigned short pcms_count; /* count of PCMs */ struct ac97_pcm *pcms; + unsigned int shared_type; /* type of shared controller betwen audio and modem */ ac97_t *codec[4]; snd_info_entry_t *proc; }; +struct _snd_ac97_template { + void *private_data; + void (*private_free) (ac97_t *ac97); + struct pci_dev *pci; /* assigned PCI device - used for quirks */ + unsigned short num; /* number of codec: 0 = primary, 1 = secondary */ + unsigned short addr; /* physical address of codec [0-3] */ + unsigned int scaps; /* driver capabilities */ + unsigned int limited_regs; /* allow limited registers only */ + DECLARE_BITMAP(reg_accessed, 0x80); /* bit flags */ +}; + struct _snd_ac97 { /* -- lowlevel (hardware) driver specific -- */ struct snd_ac97_build_ops * build_ops; @@ -441,6 +467,7 @@ struct _snd_ac97 { unsigned short subsystem_vendor; unsigned short subsystem_device; spinlock_t reg_lock; + struct semaphore mutex; /* mutex for AD18xx multi-codecs and paging (2.3) */ unsigned short num; /* number of codec: 0 = primary, 1 = secondary */ unsigned short addr; /* physical address of codec [0-3] */ unsigned int id; /* identification of codec */ @@ -461,7 +488,6 @@ struct _snd_ac97 { unsigned short id[3]; // codec IDs (lower 16-bit word) unsigned short pcmreg[3]; // PCM registers unsigned short codec_cfg[3]; // CODEC_CFG bits - struct semaphore mutex; } ad18xx; unsigned int dev_flags; /* device specific */ } spec; @@ -484,10 +510,14 @@ static inline int ac97_can_amap(ac97_t * { return (ac97->ext_id & AC97_EI_AMAP) != 0; } +static inline int ac97_can_spdif(ac97_t * ac97) +{ + return (ac97->ext_id & AC97_EI_SPDIF) != 0; +} /* functions */ -int snd_ac97_bus(snd_card_t * card, ac97_bus_t * _bus, ac97_bus_t ** rbus); /* create new AC97 bus */ -int snd_ac97_mixer(ac97_bus_t * bus, ac97_t * _ac97, ac97_t ** rac97); /* create mixer controls */ +int snd_ac97_bus(snd_card_t *card, int num, ac97_bus_ops_t *ops, void *private_data, ac97_bus_t **rbus); /* create new AC97 bus */ +int snd_ac97_mixer(ac97_bus_t *bus, ac97_template_t *template, ac97_t **rac97); /* create mixer controls */ void snd_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short value); unsigned short snd_ac97_read(ac97_t *ac97, unsigned short reg); --- linux-2.6.9-rc1/include/sound/asequencer.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/include/sound/asequencer.h 2004-09-02 20:51:52.000000000 -0700 @@ -177,7 +177,7 @@ /* 150-151: kernel events with quote - DO NOT use in user clients */ #define SNDRV_SEQ_EVENT_KERNEL_ERROR 150 -#define SNDRV_SEQ_EVENT_KERNEL_QUOTE 151 +#define SNDRV_SEQ_EVENT_KERNEL_QUOTE 151 /* obsolete */ /* 152-191: reserved */ --- linux-2.6.9-rc1/include/sound/asound.h 2004-08-24 00:02:59.000000000 -0700 +++ 25/include/sound/asound.h 2004-09-02 23:57:36.727738984 -0700 @@ -275,7 +275,6 @@ enum sndrv_pcm_subformat { #define SNDRV_PCM_INFO_HALF_DUPLEX 0x00100000 /* only half duplex */ #define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000 /* playback and capture stream are somewhat correlated */ #define SNDRV_PCM_INFO_SYNC_START 0x00400000 /* pcm support some kind of sync go */ -#define SNDRV_PCM_INFO_NONATOMIC_OPS 0x00800000 /* non-atomic prepare callback */ enum sndrv_pcm_state { SNDRV_PCM_STATE_OPEN = 0, /* stream is open */ --- linux-2.6.9-rc1/include/sound/control.h 2004-08-24 00:02:47.000000000 -0700 +++ 25/include/sound/control.h 2004-09-02 20:51:52.000000000 -0700 @@ -35,8 +35,7 @@ typedef struct sndrv_ctl_elem_value snd_ typedef enum sndrv_ctl_event_type snd_ctl_event_type_t; typedef struct sndrv_ctl_event snd_ctl_event_t; -#define _snd_kcontrol_chip(kcontrol) ((kcontrol)->private_data) -#define snd_kcontrol_chip(kcontrol) snd_magic_cast1(chip_t, _snd_kcontrol_chip(kcontrol), return -ENXIO) +#define snd_kcontrol_chip(kcontrol) ((kcontrol)->private_data) typedef int (snd_kcontrol_info_t) (snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo); typedef int (snd_kcontrol_get_t) (snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); --- linux-2.6.9-rc1/include/sound/core.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/include/sound/core.h 2004-09-02 20:51:52.000000000 -0700 @@ -211,23 +211,27 @@ int snd_card_set_dev_pm_callback(snd_car void *private_data); #define snd_card_set_isa_pm_callback(card,suspend,resume,data) \ snd_card_set_dev_pm_callback(card, PM_ISA_DEV, suspend, resume, data) +#ifdef CONFIG_PCI #ifndef SND_PCI_PM_CALLBACKS int snd_card_pci_suspend(struct pci_dev *dev, u32 state); int snd_card_pci_resume(struct pci_dev *dev); #define SND_PCI_PM_CALLBACKS \ .suspend = snd_card_pci_suspend, .resume = snd_card_pci_resume #endif +#endif #else #define snd_power_lock(card) do { (void)(card); } while (0) #define snd_power_unlock(card) do { (void)(card); } while (0) static inline int snd_power_wait(snd_card_t *card, unsigned int state, struct file *file) { return 0; } #define snd_power_get_state(card) SNDRV_CTL_POWER_D0 #define snd_power_change_state(card, state) do { (void)(card); } while (0) -#define snd_card_set_pm_callback(card,suspend,resume,data) -EINVAL -#define snd_card_set_dev_pm_callback(card,suspend,resume,data) -EINVAL -#define snd_card_set_isa_pm_callback(card,suspend,resume,data) -EINVAL +#define snd_card_set_pm_callback(card,suspend,resume,data) +#define snd_card_set_dev_pm_callback(card,suspend,resume,data) +#define snd_card_set_isa_pm_callback(card,suspend,resume,data) +#ifdef CONFIG_PCI #define SND_PCI_PM_CALLBACKS #endif +#endif /* device.c */ @@ -268,7 +272,7 @@ int snd_oss_init_module(void); #else #define snd_minor_info_oss_init() /*NOP*/ #define snd_minor_info_oss_done() /*NOP*/ -#define snd_oss_init_module() /*NOP*/ +#define snd_oss_init_module() 0 #endif /* memory.c */ @@ -279,10 +283,12 @@ void snd_memory_done(void); int snd_memory_info_init(void); int snd_memory_info_done(void); void *snd_hidden_kmalloc(size_t size, int flags); +void *snd_hidden_kcalloc(size_t n, size_t size, int flags); void snd_hidden_kfree(const void *obj); void *snd_hidden_vmalloc(unsigned long size); void snd_hidden_vfree(void *obj); #define kmalloc(size, flags) snd_hidden_kmalloc(size, flags) +#define kcalloc(n, size, flags) snd_hidden_kcalloc(n, size, flags) #define kfree(obj) snd_hidden_kfree(obj) #define vmalloc(size) snd_hidden_vmalloc(size) #define vfree(obj) snd_hidden_vfree(obj) @@ -300,7 +306,6 @@ void snd_hidden_vfree(void *obj); #define kfree_nocheck(obj) kfree(obj) #define vfree_nocheck(obj) vfree(obj) #endif -void *snd_kcalloc(size_t size, int flags); char *snd_kmalloc_strdup(const char *string, int flags); int copy_to_user_fromio(void __user *dst, unsigned long src, size_t count); int copy_from_user_toio(unsigned long dst, const void __user *src, size_t count); @@ -411,7 +416,7 @@ void snd_verbose_printd(const char *file * not checked. */ #define snd_assert(expr, args...) do {\ - if (!(expr)) {\ + if (unlikely(!(expr))) { \ snd_printk("BUG? (%s) (called from %p)\n", __ASTRING__(expr), __builtin_return_address(0));\ args;\ }\ @@ -427,7 +432,7 @@ void snd_verbose_printd(const char *file * CONFIG_SND_DEBUG is not set but without any error messages. */ #define snd_runtime_check(expr, args...) do {\ - if (!(expr)) {\ + if (unlikely(!(expr))) { \ snd_printk("ERROR (%s) (called from %p)\n", __ASTRING__(expr), __builtin_return_address(0));\ args;\ }\ --- linux-2.6.9-rc1/include/sound/cs46xx.h 2004-08-24 00:02:25.000000000 -0700 +++ 25/include/sound/cs46xx.h 2004-09-02 20:51:52.000000000 -0700 @@ -24,6 +24,7 @@ */ #include "pcm.h" +#include "pcm-indirect.h" #include "rawmidi.h" #include "ac97_codec.h" #include "cs46xx_dsp_spos.h" @@ -1650,14 +1651,7 @@ typedef struct _snd_cs46xx_pcm_t { unsigned int ctl; unsigned int shift; /* Shift count to trasform frames in bytes */ - unsigned int sw_bufsize; - unsigned int sw_data; /* Offset to next dst (or src) in sw ring buffer */ - unsigned int sw_io; - int sw_ready; /* Bytes ready to be transferred to/from hw */ - unsigned int hw_data; /* Offset to next dst (or src) in hw ring buffer */ - unsigned int hw_io; /* Ring buffer hw pointer */ - int hw_ready; /* Bytes ready for play (or captured) in hw ring buffer */ - size_t appl_ptr; /* Last seen appl_ptr */ + snd_pcm_indirect_t pcm_rec; snd_pcm_substream_t *substream; pcm_channel_descriptor_t * pcm_channel; @@ -1695,14 +1689,7 @@ struct _snd_cs46xx { unsigned int ctl; unsigned int shift; /* Shift count to trasform frames in bytes */ - unsigned int sw_bufsize; - unsigned int sw_data; /* Offset to next dst (or src) in sw ring buffer */ - unsigned int sw_io; - int sw_ready; /* Bytes ready to be transferred to/from hw */ - unsigned int hw_data; /* Offset to next dst (or src) in hw ring buffer */ - unsigned int hw_io; /* Ring buffer hw pointer */ - int hw_ready; /* Bytes ready for play (or captured) in hw ring buffer */ - size_t appl_ptr; /* Last seen appl_ptr */ + snd_pcm_indirect_t pcm_rec; snd_pcm_substream_t *substream; } capt; @@ -1723,8 +1710,6 @@ struct _snd_cs46xx { unsigned int midcr; unsigned int uartm; - struct snd_dma_device dma_dev; - int amplifier; void (*amplifier_ctrl)(cs46xx_t *, int); void (*active_ctrl)(cs46xx_t *, int); --- linux-2.6.9-rc1/include/sound/driver.h 2004-08-24 00:02:32.000000000 -0700 +++ 25/include/sound/driver.h 2004-09-02 20:51:52.000000000 -0700 @@ -61,6 +61,4 @@ void snd_wrapper_vfree(void *); #undef vfree #endif -#include "sndmagic.h" - #endif /* __SOUND_DRIVER_H */ --- linux-2.6.9-rc1/include/sound/emu10k1.h 2004-08-24 00:02:24.000000000 -0700 +++ 25/include/sound/emu10k1.h 2004-09-02 20:51:52.000000000 -0700 @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -887,10 +888,7 @@ typedef struct { unsigned char gpr_trigger; /* GPR containing trigger (activate) information (host) */ unsigned char gpr_running; /* GPR containing info if PCM is running (FX8010) */ unsigned char etram[32]; /* external TRAM address & data */ - unsigned int sw_data, hw_data; - unsigned int sw_io, hw_io; - unsigned int sw_ready, hw_ready; - unsigned int appl_ptr; + snd_pcm_indirect_t pcm_rec; unsigned int tram_pos; unsigned int tram_shift; snd_emu10k1_fx8010_irq_t *irq; @@ -935,11 +933,11 @@ struct _snd_emu10k1 { int irq; unsigned long port; /* I/O port number */ - struct resource *res_port; int APS: 1, /* APS flag */ no_ac97: 1, /* no AC'97 */ tos_link: 1, /* tos link detected */ - rear_ac97: 1; /* rear channels are on AC'97 */ + rear_ac97: 1, /* rear channels are on AC'97 */ + spk71:1; /* 7.1 configuration (Audigy 2 ZS) */ unsigned int audigy; /* is Audigy? */ unsigned int revision; /* chip revision */ unsigned int serial; /* serial number */ @@ -947,7 +945,6 @@ struct _snd_emu10k1 { unsigned int card_type; /* EMU10K1_CARD_* */ unsigned int ecard_ctrl; /* ecard control bits */ unsigned long dma_mask; /* PCI DMA mask */ - struct snd_dma_device dma_dev; /* DMA device description */ int max_cache_pages; /* max memory size / PAGE_SIZE */ struct snd_dma_buffer silent_page; /* silent page */ struct snd_dma_buffer ptb_pages; /* page table pages */ @@ -972,7 +969,6 @@ struct _snd_emu10k1 { snd_pcm_t *pcm; snd_pcm_t *pcm_mic; snd_pcm_t *pcm_efx; - snd_pcm_t *pcm_fx8010; spinlock_t synth_lock; void *synth; @@ -1069,6 +1065,15 @@ int snd_emu10k1_audigy_midi(emu10k1_t * /* proc interface */ int snd_emu10k1_proc_init(emu10k1_t * emu); +/* fx8010 irq handler */ +int snd_emu10k1_fx8010_register_irq_handler(emu10k1_t *emu, + snd_fx8010_irq_handler_t *handler, + unsigned char gpr_running, + void *private_data, + snd_emu10k1_fx8010_irq_t **r_irq); +int snd_emu10k1_fx8010_unregister_irq_handler(emu10k1_t *emu, + snd_emu10k1_fx8010_irq_t *irq); + #endif /* __KERNEL__ */ /* @@ -1162,6 +1167,8 @@ int snd_emu10k1_proc_init(emu10k1_t * em #define FXBUS_PCM_RIGHT_FRONT 0x09 #define FXBUS_MIDI_REVERB 0x0c #define FXBUS_MIDI_CHORUS 0x0d +#define FXBUS_PCM_LEFT_SIDE 0x0e +#define FXBUS_PCM_RIGHT_SIDE 0x0f #define FXBUS_PT_LEFT 0x14 #define FXBUS_PT_RIGHT 0x15 @@ -1227,8 +1234,8 @@ int snd_emu10k1_proc_init(emu10k1_t * em #define A_EXTOUT_AFRONT_R 0x09 /* right */ #define A_EXTOUT_ACENTER 0x0a /* analog center */ #define A_EXTOUT_ALFE 0x0b /* analog LFE */ -/* 0x0c ?? */ -/* 0x0d ?? */ +#define A_EXTOUT_ASIDE_L 0x0c /* analog side left - Audigy 2 ZS */ +#define A_EXTOUT_ASIDE_R 0x0d /* right - Audigy 2 ZS */ #define A_EXTOUT_AREAR_L 0x0e /* analog rear left */ #define A_EXTOUT_AREAR_R 0x0f /* right */ #define A_EXTOUT_AC97_L 0x10 /* AC97 left (front) */ --- linux-2.6.9-rc1/include/sound/es1688.h 2004-08-24 00:02:59.000000000 -0700 +++ 25/include/sound/es1688.h 2004-09-02 20:51:52.000000000 -0700 @@ -55,8 +55,6 @@ struct _snd_es1688 { typedef struct _snd_es1688 es1688_t; -#define chip_t es1688_t - /* I/O ports */ #define ES1688P(codec, x) ((codec)->port + e_s_s_ESS1688##x) --- linux-2.6.9-rc1/include/sound/info.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/include/sound/info.h 2004-09-02 20:51:52.000000000 -0700 @@ -91,9 +91,12 @@ struct snd_info_entry { extern int snd_info_check_reserved_words(const char *str); -#ifdef CONFIG_SND_OSSEMUL +#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS) extern int snd_info_minor_register(void); extern int snd_info_minor_unregister(void); +#else +#define snd_info_minor_register() /* NOP */ +#define snd_info_minor_unregister() /* NOP */ #endif --- linux-2.6.9-rc1/include/sound/initval.h 2004-08-24 00:01:51.000000000 -0700 +++ 25/include/sound/initval.h 2004-09-02 20:51:52.000000000 -0700 @@ -21,20 +21,6 @@ * */ -#ifndef MODULE_GENERIC_STRING -#ifdef MODULE -#define MODULE_GENERIC_STRING(name, string) \ -static const char __module_generic_string_##name [] \ - __attribute__ ((unused, __section__(".modstring"))) = #name "=" string; -#else -#define MODULE_GENERIC_STRING(name, string) -#endif -#endif - -#define MODULE_CLASSES(val) MODULE_GENERIC_STRING(info_classes, val) -#define MODULE_DEVICES(val) MODULE_GENERIC_STRING(info_devices, val) -#define MODULE_PARM_SYNTAX(id, val) MODULE_GENERIC_STRING(info_parm_##id, val) - #define SNDRV_AUTO_PORT 1 #define SNDRV_AUTO_IRQ 0xffff #define SNDRV_AUTO_DMA 0xffff @@ -64,25 +50,6 @@ static const char __module_generic_strin #define SNDRV_DEFAULT_DMA_SIZE { [0 ... (SNDRV_CARDS-1)] = SNDRV_AUTO_DMA_SIZE } #define SNDRV_DEFAULT_PTR SNDRV_DEFAULT_STR -#define SNDRV_BOOLEAN_TRUE_DESC "allows:{{0,Disabled},{1,Enabled}},default:1,dialog:check" -#define SNDRV_BOOLEAN_FALSE_DESC "allows:{{0,Disabled},{1,Enabled}},default:0,dialog:check" - -#define SNDRV_ENABLED "enable:(enable)" - -#define SNDRV_INDEX_DESC SNDRV_ENABLED ",allows:{{0,7}},unique,skill:required,dialog:list" -#define SNDRV_ID_DESC SNDRV_ENABLED ",unique" -#define SNDRV_ENABLE_DESC SNDRV_BOOLEAN_FALSE_DESC -#define SNDRV_ISAPNP_DESC SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC -#define SNDRV_DMA8_DESC SNDRV_ENABLED ",allows:{{0,1},{3}},dialog:list" -#define SNDRV_DMA16_DESC SNDRV_ENABLED ",allows:{{5,7}},dialog:list" -#define SNDRV_DMA_DESC SNDRV_ENABLED ",allows:{{0,1},{3},{5,7}},dialog:list" -#define SNDRV_IRQ_DESC SNDRV_ENABLED ",allows:{{5},{7},{9},{10,12},{14,15}},dialog:list" -#define SNDRV_DMA_SIZE_DESC SNDRV_ENABLED ",allows:{{4,128}},default:64,skill:advanced" -#define SNDRV_DMA8_SIZE_DESC SNDRV_ENABLED ",allows:{{4, 64}},default:64,skill:advanced" -#define SNDRV_DMA16_SIZE_DESC SNDRV_ENABLED ",allows:{{4,128}},default:64,skill:advanced" -#define SNDRV_PORT12_DESC SNDRV_ENABLED ",allows:{{0,0x3fff}},base:16" -#define SNDRV_PORT_DESC SNDRV_ENABLED ",allows:{{0,0xffff}},base:16" - #ifdef SNDRV_LEGACY_AUTO_PROBE static int snd_legacy_auto_probe(unsigned long *ports, int (*probe)(unsigned long port)) { --- linux-2.6.9-rc1/include/sound/memalloc.h 2004-08-24 00:01:53.000000000 -0700 +++ 25/include/sound/memalloc.h 2004-09-02 20:51:52.000000000 -0700 @@ -30,9 +30,8 @@ struct device; * buffer device info */ struct snd_dma_device { - int type; /* SNDRV_MEM_TYPE_XXX */ + int type; /* SNDRV_DMA_TYPE_XXX */ struct device *dev; /* generic device */ - unsigned int id; /* a unique ID */ }; #ifndef snd_dma_pci_data @@ -56,6 +55,7 @@ struct snd_dma_device { * info for buffer allocation */ struct snd_dma_buffer { + struct snd_dma_device dev; /* device type */ unsigned char *area; /* virtual pointer */ dma_addr_t addr; /* physical address */ size_t bytes; /* buffer size in bytes */ @@ -76,7 +76,7 @@ struct snd_sg_buf { int tblsize; /* allocated table size */ struct snd_sg_page *table; /* address table */ struct page **page_table; /* page table (for vmap/vunmap) */ - struct snd_dma_device dev; + struct device *dev; }; /* @@ -97,20 +97,21 @@ static inline dma_addr_t snd_sgbuf_get_a /* allocate/release a buffer */ -int snd_dma_alloc_pages(const struct snd_dma_device *dev, size_t size, +int snd_dma_alloc_pages(int type, struct device *dev, size_t size, struct snd_dma_buffer *dmab); -int snd_dma_alloc_pages_fallback(const struct snd_dma_device *dev, size_t size, +int snd_dma_alloc_pages_fallback(int type, struct device *dev, size_t size, struct snd_dma_buffer *dmab); -void snd_dma_free_pages(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab); +void snd_dma_free_pages(struct snd_dma_buffer *dmab); /* buffer-preservation managements */ -size_t snd_dma_get_reserved(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab); -int snd_dma_free_reserved(const struct snd_dma_device *dev); -int snd_dma_set_reserved(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab); + +#define snd_dma_pci_buf_id(pci) (((unsigned int)(pci)->vendor << 16) | (pci)->device) + +size_t snd_dma_get_reserved_buf(struct snd_dma_buffer *dmab, unsigned int id); +int snd_dma_reserve_buf(struct snd_dma_buffer *dmab, unsigned int id); /* basic memory allocation functions */ void *snd_malloc_pages(size_t size, unsigned int gfp_flags); -void *snd_malloc_pages_fallback(size_t size, unsigned int gfp_flags, size_t *res_size); void snd_free_pages(void *ptr, size_t size); #endif /* __SOUND_MEMALLOC_H */ --- linux-2.6.9-rc1/include/sound/pcm.h 2004-08-24 00:02:33.000000000 -0700 +++ 25/include/sound/pcm.h 2004-09-02 23:57:36.728738832 -0700 @@ -52,10 +52,8 @@ typedef struct sndrv_pcm_mmap_control sn typedef struct sndrv_mask snd_mask_t; typedef struct snd_sg_buf snd_pcm_sgbuf_t; -#define _snd_pcm_substream_chip(substream) ((substream)->private_data) -#define snd_pcm_substream_chip(substream) snd_magic_cast1(chip_t, _snd_pcm_substream_chip(substream), return -ENXIO) -#define _snd_pcm_chip(pcm) ((pcm)->private_data) -#define snd_pcm_chip(pcm) snd_magic_cast1(chip_t, _snd_pcm_chip(pcm), return -ENXIO) +#define snd_pcm_substream_chip(substream) ((substream)->private_data) +#define snd_pcm_chip(pcm) ((pcm)->private_data) typedef struct _snd_pcm_file snd_pcm_file_t; typedef struct _snd_pcm_runtime snd_pcm_runtime_t; @@ -99,6 +97,7 @@ typedef struct _snd_pcm_ops { int (*silence)(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count); struct page *(*page)(snd_pcm_substream_t *substream, unsigned long offset); + int (*mmap)(snd_pcm_substream_t *substream, struct vm_area_struct *vma); int (*ack)(snd_pcm_substream_t *substream); } snd_pcm_ops_t; @@ -123,6 +122,8 @@ typedef struct _snd_pcm_ops { #define SNDRV_PCM_TRIGGER_SUSPEND 5 #define SNDRV_PCM_TRIGGER_RESUME 6 +#define SNDRV_PCM_POS_XRUN ((snd_pcm_uframes_t)-1) + /* If you change this don't forget to change rates[] table in pcm_native.c */ #define SNDRV_PCM_RATE_5512 (1<<0) /* 5512Hz */ #define SNDRV_PCM_RATE_8000 (1<<1) /* 8000Hz */ @@ -351,7 +352,8 @@ struct _snd_pcm_runtime { unsigned char *dma_area; /* DMA area */ dma_addr_t dma_addr; /* physical bus address (not accessible from main CPU) */ size_t dma_bytes; /* size of DMA area */ - void *dma_private; /* private DMA data for the memory allocator */ + + struct snd_dma_buffer *dma_buffer_p; /* allocated buffer */ #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) /* -- OSS things -- */ @@ -372,8 +374,8 @@ struct _snd_pcm_substream { char name[32]; /* substream name */ int stream; /* stream (direction) */ size_t buffer_bytes_max; /* limit ring buffer size */ - struct snd_dma_device dma_device; struct snd_dma_buffer dma_buffer; + unsigned int dma_buf_id; size_t dma_max; /* -- hardware operations -- */ unsigned int open_flag: 1; /* lowlevel device has been opened */ @@ -851,7 +853,7 @@ int snd_pcm_format_little_endian(snd_pcm int snd_pcm_format_big_endian(snd_pcm_format_t format); int snd_pcm_format_width(snd_pcm_format_t format); /* in bits */ int snd_pcm_format_physical_width(snd_pcm_format_t format); /* in bits */ -u_int64_t snd_pcm_format_silence_64(snd_pcm_format_t format); +const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format); int snd_pcm_format_set_silence(snd_pcm_format_t format, void *buf, unsigned int frames); snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsignd, int big_endian); ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples); @@ -892,6 +894,22 @@ snd_pcm_sframes_t snd_pcm_lib_readv(snd_ int snd_pcm_limit_hw_rates(snd_pcm_runtime_t *runtime); +static inline void snd_pcm_set_runtime_buffer(snd_pcm_substream_t *substream, + struct snd_dma_buffer *bufp) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + if (bufp) { + runtime->dma_buffer_p = bufp; + runtime->dma_area = bufp->area; + runtime->dma_addr = bufp->addr; + runtime->dma_bytes = bufp->bytes; + } else { + runtime->dma_buffer_p = NULL; + runtime->dma_area = NULL; + runtime->dma_addr = 0; + runtime->dma_bytes = 0; + } +} /* * Timer interface @@ -916,11 +934,33 @@ int snd_pcm_lib_preallocate_pages_for_al int snd_pcm_lib_malloc_pages(snd_pcm_substream_t *substream, size_t size); int snd_pcm_lib_free_pages(snd_pcm_substream_t *substream); -#define snd_pcm_substream_sgbuf(substream) ((substream)->runtime->dma_private) +#define snd_pcm_substream_sgbuf(substream) ((substream)->runtime->dma_buffer_p->private_data) #define snd_pcm_sgbuf_pages(size) snd_sgbuf_aligned_pages(size) #define snd_pcm_sgbuf_get_addr(sgbuf,ofs) snd_sgbuf_get_addr(sgbuf,ofs) struct page *snd_pcm_sgbuf_ops_page(snd_pcm_substream_t *substream, unsigned long offset); +/* handle mmap counter - PCM mmap callback should handle this counter properly */ +static inline void snd_pcm_mmap_data_open(struct vm_area_struct *area) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; + atomic_inc(&substream->runtime->mmap_count); +} + +static inline void snd_pcm_mmap_data_close(struct vm_area_struct *area) +{ + snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; + atomic_dec(&substream->runtime->mmap_count); +} + +/* mmap for io-memory area */ +#if defined(CONFIG_X86) || defined(CONFIG_PPC) || defined(CONFIG_ALPHA) +#define SNDRV_PCM_INFO_MMAP_IOMEM SNDRV_PCM_INFO_MMAP +int snd_pcm_lib_mmap_iomem(snd_pcm_substream_t *substream, struct vm_area_struct *area); +#else +#define SNDRV_PCM_INFO_MMAP_IOMEM 0 +#define snd_pcm_lib_mmap_iomem NULL +#endif + static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max) { *max = dma < 4 ? 64 * 1024 : 128 * 1024; --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/include/sound/pcm-indirect.h 2004-09-02 20:51:52.000000000 -0700 @@ -0,0 +1,177 @@ +/* + * Helper functions for indirect PCM data transfer + * + * Copyright (c) by Takashi Iwai + * Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __SOUND_PCM_INDIRECT_H +#define __SOUND_PCM_INDIRECT_H + +#include + +typedef struct sndrv_pcm_indirect { + unsigned int hw_buffer_size; /* Byte size of hardware buffer */ + unsigned int hw_queue_size; /* Max queue size of hw buffer (0 = buffer size) */ + unsigned int hw_data; /* Offset to next dst (or src) in hw ring buffer */ + unsigned int hw_io; /* Ring buffer hw pointer */ + int hw_ready; /* Bytes ready for play (or captured) in hw ring buffer */ + unsigned int sw_buffer_size; /* Byte size of software buffer */ + unsigned int sw_data; /* Offset to next dst (or src) in sw ring buffer */ + unsigned int sw_io; /* Current software pointer in bytes */ + int sw_ready; /* Bytes ready to be transferred to/from hw */ + snd_pcm_uframes_t appl_ptr; /* Last seen appl_ptr */ +} snd_pcm_indirect_t; + +typedef void (*snd_pcm_indirect_copy_t)(snd_pcm_substream_t *substream, + snd_pcm_indirect_t *rec, size_t bytes); + +/* + * helper function for playback ack callback + */ +static inline void +snd_pcm_indirect_playback_transfer(snd_pcm_substream_t *substream, + snd_pcm_indirect_t *rec, + snd_pcm_indirect_copy_t copy) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr; + snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr; + int qsize; + + if (diff) { + if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) + diff += runtime->boundary; + rec->sw_ready += (int)frames_to_bytes(runtime, diff); + rec->appl_ptr = appl_ptr; + } + qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size; + while (rec->hw_ready < qsize && rec->sw_ready > 0) { + unsigned int hw_to_end = rec->hw_buffer_size - rec->hw_data; + unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data; + unsigned int bytes = qsize - rec->hw_ready; + if (rec->sw_ready < (int)bytes) + bytes = rec->sw_ready; + if (hw_to_end < bytes) + bytes = hw_to_end; + if (sw_to_end < bytes) + bytes = sw_to_end; + if (! bytes) + break; + copy(substream, rec, bytes); + rec->hw_data += bytes; + if (rec->hw_data == rec->hw_buffer_size) + rec->hw_data = 0; + rec->sw_data += bytes; + if (rec->sw_data == rec->sw_buffer_size) + rec->sw_data = 0; + rec->hw_ready += bytes; + rec->sw_ready -= bytes; + } +} + +/* + * helper function for playback pointer callback + * ptr = current byte pointer + */ +static inline snd_pcm_uframes_t +snd_pcm_indirect_playback_pointer(snd_pcm_substream_t *substream, + snd_pcm_indirect_t *rec, unsigned int ptr) +{ + int bytes = ptr - rec->hw_io; + if (bytes < 0) + bytes += rec->hw_buffer_size; + rec->hw_io = ptr; + rec->hw_ready -= bytes; + rec->sw_io += bytes; + if (rec->sw_io >= rec->sw_buffer_size) + rec->sw_io -= rec->sw_buffer_size; + if (substream->ops->ack) + substream->ops->ack(substream); + return bytes_to_frames(substream->runtime, rec->sw_io); +} + + +/* + * helper function for capture ack callback + */ +static inline void +snd_pcm_indirect_capture_transfer(snd_pcm_substream_t *substream, + snd_pcm_indirect_t *rec, + snd_pcm_indirect_copy_t copy) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr; + snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr; + + if (diff) { + if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) + diff += runtime->boundary; + rec->sw_ready -= frames_to_bytes(runtime, diff); + rec->appl_ptr = appl_ptr; + } + while (rec->hw_ready > 0 && + rec->sw_ready < (int)rec->sw_buffer_size) { + size_t hw_to_end = rec->hw_buffer_size - rec->hw_data; + size_t sw_to_end = rec->sw_buffer_size - rec->sw_data; + size_t bytes = rec->sw_buffer_size - rec->sw_ready; + if (rec->hw_ready < (int)bytes) + bytes = rec->hw_ready; + if (hw_to_end < bytes) + bytes = hw_to_end; + if (sw_to_end < bytes) + bytes = sw_to_end; + if (! bytes) + break; + copy(substream, rec, bytes); + rec->hw_data += bytes; + if ((int)rec->hw_data == rec->hw_buffer_size) + rec->hw_data = 0; + rec->sw_data += bytes; + if (rec->sw_data == rec->sw_buffer_size) + rec->sw_data = 0; + rec->hw_ready -= bytes; + rec->sw_ready += bytes; + } +} + +/* + * helper function for capture pointer callback, + * ptr = current byte pointer + */ +static inline snd_pcm_uframes_t +snd_pcm_indirect_capture_pointer(snd_pcm_substream_t *substream, + snd_pcm_indirect_t *rec, unsigned int ptr) +{ + int qsize; + int bytes = ptr - rec->hw_io; + if (bytes < 0) + bytes += rec->hw_buffer_size; + rec->hw_io = ptr; + rec->hw_ready += bytes; + qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size; + if (rec->hw_ready > qsize) + return SNDRV_PCM_POS_XRUN; + rec->sw_io += bytes; + if (rec->sw_io >= rec->sw_buffer_size) + rec->sw_io -= rec->sw_buffer_size; + if (substream->ops->ack) + substream->ops->ack(substream); + return bytes_to_frames(substream->runtime, rec->sw_io); +} + +#endif /* __SOUND_PCM_INDIRECT_H */ --- linux-2.6.9-rc1/include/sound/seq_kernel.h 2004-08-24 00:03:38.000000000 -0700 +++ 25/include/sound/seq_kernel.h 2004-09-02 20:51:52.000000000 -0700 @@ -168,6 +168,9 @@ typedef int (*snd_seq_dump_func_t)(void int snd_seq_expand_var_event(const snd_seq_event_t *event, int count, char *buf, int in_kernel, int size_aligned); int snd_seq_dump_var_event(const snd_seq_event_t *event, snd_seq_dump_func_t func, void *private_data); +/* interface for OSS emulation */ +int snd_seq_set_queue_tempo(int client, snd_seq_queue_tempo_t *tempo); + /* port callback routines */ void snd_port_init_callback(snd_seq_port_callback_t *p); snd_seq_port_callback_t *snd_port_alloc_callback(void); --- linux-2.6.9-rc1/include/sound/sndmagic.h 2004-08-24 00:02:25.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,218 +0,0 @@ -#ifndef __SOUND_SNDMAGIC_H -#define __SOUND_SNDMAGIC_H - -/* - * Magic allocation, deallocation, check - * Copyright (c) 2000 by Abramo Bagnara - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - - -#ifdef CONFIG_SND_DEBUG_MEMORY - -void *_snd_magic_kcalloc(unsigned long magic, size_t size, int flags); -void *_snd_magic_kmalloc(unsigned long magic, size_t size, int flags); - -/** - * snd_magic_kmalloc - allocate a record with a magic-prefix - * @type: the type to allocate a record (like xxx_t) - * @extra: the extra size to allocate in bytes - * @flags: the allocation condition (GFP_XXX) - * - * Allocates a record of the given type with the extra space and - * returns its pointer. The allocated record has a secret magic-key - * to be checked via snd_magic_cast() for safe casts. - * - * The allocated pointer must be released via snd_magic_kfree(). - * - * The "struct xxx" style cannot be used as the type argument - * because the magic-key constant is generated from the type-name - * string. - */ -#define snd_magic_kmalloc(type, extra, flags) \ - (type *) _snd_magic_kmalloc(type##_magic, sizeof(type) + extra, flags) -/** - * snd_magic_kcalloc - allocate a record with a magic-prefix and initialize - * @type: the type to allocate a record (like xxx_t) - * @extra: the extra size to allocate in bytes - * @flags: the allocation condition (GFP_XXX) - * - * Works like snd_magic_kmalloc() but this clears the area with zero - * automatically. - */ -#define snd_magic_kcalloc(type, extra, flags) \ - (type *) _snd_magic_kcalloc(type##_magic, sizeof(type) + extra, flags) - -/** - * snd_magic_kfree - release the allocated area - * @ptr: the pointer allocated via snd_magic_kmalloc() or snd_magic_kcalloc() - * - * Releases the memory area allocated via snd_magic_kmalloc() or - * snd_magic_kcalloc() function. - */ -void snd_magic_kfree(void *ptr); - -static inline unsigned long _snd_magic_value(void *obj) -{ - return obj == NULL ? (unsigned long)-1 : *(((unsigned long *)obj) - 1); -} - -static inline int _snd_magic_bad(void *obj, unsigned long magic) -{ - return _snd_magic_value(obj) != magic; -} - -#define snd_magic_cast1(t, expr, cmd) snd_magic_cast(t, expr, cmd) - -/** - * snd_magic_cast - check and cast the magic-allocated pointer - * @type: the type of record to cast - * @ptr: the magic-allocated pointer - * @action...: the action to do if failed - * - * This macro provides a safe cast for the given type, which was - * allocated via snd_magic_kmalloc() or snd_magic_kcallc(). - * If the pointer is invalid, i.e. the cast-type doesn't match, - * the action arguments are called with a debug message. - */ -#define snd_magic_cast(type, ptr, action...) \ - (type *) ({\ - void *__ptr = ptr;\ - unsigned long __magic = _snd_magic_value(__ptr);\ - if (__magic != type##_magic) {\ - snd_printk("bad MAGIC (0x%lx)\n", __magic);\ - action;\ - }\ - __ptr;\ -}) - -#define snd_device_t_magic 0xa15a00ff -#define snd_pcm_t_magic 0xa15a0101 -#define snd_pcm_file_t_magic 0xa15a0102 -#define snd_pcm_substream_t_magic 0xa15a0103 -#define snd_pcm_proc_private_t_magic 0xa15a0104 -#define snd_pcm_oss_file_t_magic 0xa15a0105 -#define snd_mixer_oss_t_magic 0xa15a0106 -// #define snd_pcm_sgbuf_t_magic 0xa15a0107 - -#define snd_info_private_data_t_magic 0xa15a0201 -#define snd_info_entry_t_magic 0xa15a0202 -#define snd_ctl_file_t_magic 0xa15a0301 -#define snd_kcontrol_t_magic 0xa15a0302 -#define snd_rawmidi_t_magic 0xa15a0401 -#define snd_rawmidi_file_t_magic 0xa15a0402 -#define snd_virmidi_t_magic 0xa15a0403 -#define snd_virmidi_dev_t_magic 0xa15a0404 -#define snd_timer_t_magic 0xa15a0501 -#define snd_timer_user_t_magic 0xa15a0502 -#define snd_hwdep_t_magic 0xa15a0601 -#define snd_seq_device_t_magic 0xa15a0701 - -#define es18xx_t_magic 0xa15a1101 -#define trident_t_magic 0xa15a1201 -#define es1938_t_magic 0xa15a1301 -#define cs46xx_t_magic 0xa15a1401 -#define cs46xx_pcm_t_magic 0xa15a1402 -#define ensoniq_t_magic 0xa15a1501 -#define sonicvibes_t_magic 0xa15a1601 -#define mpu401_t_magic 0xa15a1701 -#define fm801_t_magic 0xa15a1801 -#define ac97_t_magic 0xa15a1901 -#define ac97_bus_t_magic 0xa15a1902 -#define ak4531_t_magic 0xa15a1a01 -#define snd_uart16550_t_magic 0xa15a1b01 -#define emu10k1_t_magic 0xa15a1c01 -#define emu10k1_pcm_t_magic 0xa15a1c02 -#define emu10k1_midi_t_magic 0xa15a1c03 -#define snd_gus_card_t_magic 0xa15a1d01 -#define gus_pcm_private_t_magic 0xa15a1d02 -#define gus_proc_private_t_magic 0xa15a1d03 -#define tea6330t_t_magic 0xa15a1e01 -#define ad1848_t_magic 0xa15a1f01 -#define cs4231_t_magic 0xa15a2001 -#define es1688_t_magic 0xa15a2101 -#define opti93x_t_magic 0xa15a2201 -#define emu8000_t_magic 0xa15a2301 -#define emu8000_proc_private_t_magic 0xa15a2302 -#define snd_emux_t_magic 0xa15a2303 -#define snd_emux_port_t_magic 0xa15a2304 -#define sb_t_magic 0xa15a2401 -#define snd_sb_csp_t_magic 0xa15a2402 -#define snd_card_dummy_t_magic 0xa15a2501 -#define snd_card_dummy_pcm_t_magic 0xa15a2502 -#define opl3_t_magic 0xa15a2601 -#define opl4_t_magic 0xa15a2602 -#define snd_seq_dummy_port_t_magic 0xa15a2701 -#define ice1712_t_magic 0xa15a2801 -#define ad1816a_t_magic 0xa15a2901 -#define intel8x0_t_magic 0xa15a2a01 -#define es1968_t_magic 0xa15a2b01 -#define esschan_t_magic 0xa15a2b02 -#define via82xx_t_magic 0xa15a2c01 -#define pdplus_t_magic 0xa15a2d01 -#define cmipci_t_magic 0xa15a2e01 -#define ymfpci_t_magic 0xa15a2f01 -#define ymfpci_pcm_t_magic 0xa15a2f02 -#define cs4281_t_magic 0xa15a3001 -#define snd_i2c_bus_t_magic 0xa15a3101 -#define snd_i2c_device_t_magic 0xa15a3102 -#define cs8427_t_magic 0xa15a3111 -#define m3_t_magic 0xa15a3201 -#define m3_dma_t_magic 0xa15a3202 -#define nm256_t_magic 0xa15a3301 -#define nm256_dma_t_magic 0xa15a3302 -#define sam9407_t_magic 0xa15a3401 -#define pmac_t_magic 0xa15a3501 -#define ali_t_magic 0xa15a3601 -#define mtpav_t_magic 0xa15a3701 -#define mtpav_port_t_magic 0xa15a3702 -#define korg1212_t_magic 0xa15a3800 -#define opl3sa2_t_magic 0xa15a3900 -#define serialmidi_t_magic 0xa15a3a00 -#define sa11xx_uda1341_t_magic 0xa15a3b00 -#define uda1341_t_magic 0xa15a3c00 -#define l3_client_t_magic 0xa15a3d00 -#define snd_usb_audio_t_magic 0xa15a3e01 -#define usb_mixer_elem_info_t_magic 0xa15a3e02 -#define snd_usb_stream_t_magic 0xa15a3e03 -#define snd_usb_midi_t_magic 0xa15a3f01 -#define snd_usb_midi_out_endpoint_t_magic 0xa15a3f02 -#define snd_usb_midi_in_endpoint_t_magic 0xa15a3f03 -#define ak4117_t_magic 0xa15a4000 -#define psic_t_magic 0xa15a4100 -#define vx_core_t_magic 0xa15a4110 -#define vx_pipe_t_magic 0xa15a4112 -#define azf3328_t_magic 0xa15a4200 -#define snd_card_harmony_t_magic 0xa15a4300 -#define bt87x_t_magic 0xa15a4400 -#define pdacf_t_magic 0xa15a4500 -#define vortex_t_magic 0xa15a4601 -#define atiixp_t_magic 0xa15a4701 -#define amd7930_t_magic 0xa15a4801 - -#else - -#define snd_magic_kcalloc(type, extra, flags) (type *) snd_kcalloc(sizeof(type) + extra, flags) -#define snd_magic_kmalloc(type, extra, flags) (type *) kmalloc(sizeof(type) + extra, flags) -#define snd_magic_cast(type, ptr, retval) (type *) ptr -#define snd_magic_cast1(type, ptr, retval) snd_magic_cast(type, ptr, retval) -#define snd_magic_kfree kfree - -#endif - -#endif /* __SOUND_SNDMAGIC_H */ --- linux-2.6.9-rc1/include/sound/soundfont.h 2004-08-24 00:03:39.000000000 -0700 +++ 25/include/sound/soundfont.h 2004-09-02 20:51:52.000000000 -0700 @@ -95,6 +95,7 @@ typedef struct snd_sf_list { int zone_locked; /* locked time for zone */ int sample_locked; /* locked time for sample */ snd_sf_callback_t callback; /* callback functions */ + int presets_locked; struct semaphore presets_mutex; spinlock_t lock; snd_util_memhdr_t *memhdr; --- linux-2.6.9-rc1/include/sound/timer.h 2004-08-24 00:02:48.000000000 -0700 +++ 25/include/sound/timer.h 2004-09-02 20:51:52.000000000 -0700 @@ -40,8 +40,7 @@ typedef struct sndrv_timer_status snd_ti typedef struct sndrv_timer_read snd_timer_read_t; typedef struct sndrv_timer_tread snd_timer_tread_t; -#define _snd_timer_chip(timer) ((timer)->private_data) -#define snd_timer_chip(timer) snd_magic_cast1(chip_t, _snd_timer_chip(timer), return -ENXIO) +#define snd_timer_chip(timer) ((timer)->private_data) #define SNDRV_TIMER_DEVICES 16 --- linux-2.6.9-rc1/include/sound/trident.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/include/sound/trident.h 2004-09-02 20:51:53.000000000 -0700 @@ -398,7 +398,6 @@ struct _snd_trident { unsigned char bDMAStart; unsigned long port; - struct resource *res_port; unsigned long midi_port; unsigned int spurious_irq_count; --- linux-2.6.9-rc1/include/sound/version.h 2004-08-24 00:02:26.000000000 -0700 +++ 25/include/sound/version.h 2004-09-02 20:51:53.000000000 -0700 @@ -1,3 +1,3 @@ /* include/version.h. Generated by configure. */ -#define CONFIG_SND_VERSION "1.0.4" -#define CONFIG_SND_DATE " (Mon May 17 14:31:44 2004 UTC)" +#define CONFIG_SND_VERSION "1.0.6" +#define CONFIG_SND_DATE " (Sun Aug 15 07:17:53 2004 UTC)" --- linux-2.6.9-rc1/include/sound/vx_core.h 2004-08-24 00:01:53.000000000 -0700 +++ 25/include/sound/vx_core.h 2004-09-02 20:51:53.000000000 -0700 @@ -182,6 +182,7 @@ struct snd_vx_core { /* clock and audio sources */ unsigned int audio_source; /* current audio input source */ unsigned int audio_source_target; + unsigned int clock_mode; /* clock mode (VX_CLOCK_MODE_XXX) */ unsigned int clock_source; /* current clock source (INTERNAL_QUARTZ or UER_SYNC) */ unsigned int freq; /* current frequency */ unsigned int freq_detected; /* detected frequency from digital in */ @@ -364,6 +365,13 @@ enum { UER_SYNC }; +/* clock mode */ +enum { + VX_CLOCK_MODE_AUTO, /* depending on the current audio source */ + VX_CLOCK_MODE_INTERNAL, /* fixed to internal quartz */ + VX_CLOCK_MODE_EXTERNAL /* fixed to UER sync */ +}; + /* SPDIF/UER type */ enum { VX_UER_MODE_CONSUMER, --- linux-2.6.9-rc1/include/sound/ymfpci.h 2004-08-24 00:02:26.000000000 -0700 +++ 25/include/sound/ymfpci.h 2004-09-02 20:51:53.000000000 -0700 @@ -316,7 +316,6 @@ struct _snd_ymfpci { struct gameport gameport; #endif - struct snd_dma_device dma_dev; struct snd_dma_buffer work_ptr; unsigned int bank_size_playback; --- linux-2.6.9-rc1/init/do_mounts_rd.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/init/do_mounts_rd.c 2004-09-02 20:51:53.000000000 -0700 @@ -122,7 +122,8 @@ identify_ramdisk_image(int fd, int start printk(KERN_NOTICE "RAMDISK: ext2 filesystem found at block %d\n", start_block); - nblocks = le32_to_cpu(ext2sb->s_blocks_count); + nblocks = le32_to_cpu(ext2sb->s_blocks_count) << + le32_to_cpu(ext2sb->s_log_block_size); goto done; } @@ -173,10 +174,15 @@ int __init rd_load_image(char *from) } /* - * NOTE NOTE: nblocks suppose that the blocksize is BLOCK_SIZE, so - * rd_load_image will work only with filesystem BLOCK_SIZE wide! - * So make sure to use 1k blocksize while generating ext2fs - * ramdisk-images. + * NOTE NOTE: nblocks is not actually blocks but + * the number of kibibytes of data to load into a ramdisk. + * So any ramdisk block size that is a multiple of 1KiB should + * work when the appropriate ramdisk_blocksize is specified + * on the command line. + * + * The default ramdisk_blocksize is 1KiB and it is generally + * silly to use anything else, so make sure to use 1KiB + * blocksize while generating ext2fs ramdisk-images. */ if (sys_ioctl(out_fd, BLKGETSIZE, (unsigned long)&rd_blocks) < 0) rd_blocks = 0; @@ -184,7 +190,7 @@ int __init rd_load_image(char *from) rd_blocks >>= 1; if (nblocks > rd_blocks) { - printk("RAMDISK: image too big! (%d/%ld blocks)\n", + printk("RAMDISK: image too big! (%dKiB/%ldKiB)\n", nblocks, rd_blocks); goto done; } @@ -211,7 +217,7 @@ int __init rd_load_image(char *from) goto done; } - printk(KERN_NOTICE "RAMDISK: Loading %d blocks [%ld disk%s] into ram disk... ", + printk(KERN_NOTICE "RAMDISK: Loading %dKiB [%ld disk%s] into ram disk... ", nblocks, ((nblocks-1)/devblocks)+1, nblocks>devblocks ? "s" : ""); for (i = 0, disk = 1; i < nblocks; i++) { if (i && (i % devblocks == 0)) { --- linux-2.6.9-rc1/init/Kconfig 2004-08-24 00:03:30.000000000 -0700 +++ 25/init/Kconfig 2004-09-03 00:57:14.974762752 -0700 @@ -121,7 +121,7 @@ config BSD_PROCESS_ACCT_V3 process and it's parent. Note that this file format is incompatible with previous v0/v1/v2 file formats, so you will need updated tools for processing it. A preliminary version of these tools is available - at . + at . config SYSCTL bool "Sysctl support" @@ -149,6 +149,20 @@ config AUDIT logging of avc messages output). Does not do system-call auditing without CONFIG_AUDITSYSCALL. +config KERNEL_EVENTS + bool "Kernel Events Layer" + depends on NET + default y + help + This option enables the kernel events layer, which is a simple + mechanism for kernel-to-user communication over a netlink socket. + The goal of the kernel events layer is to provide a simple and + efficient logging, error, and events system. Specifically, code + is available to link the events into D-BUS. Say Y, unless you + are building a system requiring minimal memory consumption. + + D-BUS is available at http://dbus.freedesktop.org/ + config AUDITSYSCALL bool "Enable system-call auditing support" depends on AUDIT && (X86 || PPC64 || ARCH_S390 || IA64) @@ -268,6 +282,17 @@ config EPOLL Disabling this option will cause the kernel to be built without support for epoll family of system calls. +config CPUSETS + bool "Cpuset support" + depends on SMP + help + This options will let you create and manage CPUSET's which + allow dynamically partitioning a system into sets of CPUs and + Memory Nodes and assigning tasks to run only within those sets. + This is primarily useful on large SMP or NUMA systems. + + Say N if unsure. + source "drivers/block/Kconfig.iosched" config CC_OPTIMIZE_FOR_SIZE @@ -283,8 +308,22 @@ config CC_OPTIMIZE_FOR_SIZE If unsure, say N. +config SHMEM + default y + bool "Use full shmem filesystem" if EMBEDDED && MMU + help + The shmem is an internal filesystem used to manage shared memory. + It is backed by swap and manages resource limits. It is also exported + to userspace as tmpfs if TMPFS is enabled. Disabling this + option replaces shmem and tmpfs with the much simpler ramfs code, + which may be appropriate on small systems without swap. + endmenu # General setup +config TINY_SHMEM + default !SHMEM + bool + menu "Loadable module support" config MODULES --- linux-2.6.9-rc1/init/main.c 2004-08-24 00:02:20.000000000 -0700 +++ 25/init/main.c 2004-09-03 00:57:00.097024512 -0700 @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -110,8 +111,8 @@ EXPORT_SYMBOL(system_state); /* * Boot command-line arguments */ -#define MAX_INIT_ARGS 8 -#define MAX_INIT_ENVS 8 +#define MAX_INIT_ARGS 32 +#define MAX_INIT_ENVS 32 extern void time_init(void); /* Default late time init is NULL. archs can override this later. */ @@ -156,8 +157,6 @@ static char * argv_init[MAX_INIT_ARGS+2] char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, }; static const char *panic_later, *panic_param; -__setup("profile=", profile_setup); - static int __init obsolete_checksetup(char *line) { struct obs_kernel_param *p; @@ -184,15 +183,28 @@ static int __init obsolete_checksetup(ch return 0; } -/* this should be approx 2 Bo*oMips to start (note initial shift), and will - still work even if initially too large, it will just take slightly longer */ +static unsigned long preset_lpj; +static int __init lpj_setup(char *str) +{ + preset_lpj = simple_strtoul(str,NULL,0); + return 1; +} + +__setup("lpj=", lpj_setup); + +/* + * This should be approx 2 Bo*oMips to start (note initial shift), and will + * still work even if initially too large, it will just take slightly longer + */ unsigned long loops_per_jiffy = (1<<12); EXPORT_SYMBOL(loops_per_jiffy); -/* This is the number of bits of precision for the loops_per_jiffy. Each - bit takes on average 1.5/HZ seconds. This (like the original) is a little - better than 1% */ +/* + * This is the number of bits of precision for the loops_per_jiffy. Each + * bit takes on average 1.5/HZ seconds. This (like the original) is a little + * better than 1% + */ #define LPS_PREC 8 void __devinit calibrate_delay(void) @@ -200,40 +212,53 @@ void __devinit calibrate_delay(void) unsigned long ticks, loopbit; int lps_precision = LPS_PREC; - loops_per_jiffy = (1<<12); + if (preset_lpj) { + loops_per_jiffy = preset_lpj; + printk("Calibrating delay loop (skipped)... " + "%lu.%02lu BogoMIPS preset\n", + loops_per_jiffy/(500000/HZ), + (loops_per_jiffy/(5000/HZ)) % 100); + } else { + loops_per_jiffy = (1<<12); - printk("Calibrating delay loop... "); - while ((loops_per_jiffy <<= 1) != 0) { - /* wait for "start of" clock tick */ - ticks = jiffies; - while (ticks == jiffies) - /* nothing */; - /* Go .. */ - ticks = jiffies; - __delay(loops_per_jiffy); - ticks = jiffies - ticks; - if (ticks) - break; - } + printk(KERN_DEBUG "Calibrating delay loop... "); + while ((loops_per_jiffy <<= 1) != 0) { + /* wait for "start of" clock tick */ + ticks = jiffies; + while (ticks == jiffies) + /* nothing */; + /* Go .. */ + ticks = jiffies; + __delay(loops_per_jiffy); + ticks = jiffies - ticks; + if (ticks) + break; + } -/* Do a binary approximation to get loops_per_jiffy set to equal one clock - (up to lps_precision bits) */ - loops_per_jiffy >>= 1; - loopbit = loops_per_jiffy; - while ( lps_precision-- && (loopbit >>= 1) ) { - loops_per_jiffy |= loopbit; - ticks = jiffies; - while (ticks == jiffies); - ticks = jiffies; - __delay(loops_per_jiffy); - if (jiffies != ticks) /* longer than 1 tick */ - loops_per_jiffy &= ~loopbit; + /* + * Do a binary approximation to get loops_per_jiffy set to + * equal one clock (up to lps_precision bits) + */ + loops_per_jiffy >>= 1; + loopbit = loops_per_jiffy; + while (lps_precision-- && (loopbit >>= 1)) { + loops_per_jiffy |= loopbit; + ticks = jiffies; + while (ticks == jiffies) + /* nothing */; + ticks = jiffies; + __delay(loops_per_jiffy); + if (jiffies != ticks) /* longer than 1 tick */ + loops_per_jiffy &= ~loopbit; + } + + /* Round the value and print it */ + printk("%lu.%02lu BogoMIPS (lpj=%lu)\n", + loops_per_jiffy/(500000/HZ), + (loops_per_jiffy/(5000/HZ)) % 100, + loops_per_jiffy); } -/* Round the value and print it */ - printk("%lu.%02lu BogoMIPS\n", - loops_per_jiffy/(500000/HZ), - (loops_per_jiffy/(5000/HZ)) % 100); } static int __init debug_kernel(char *str) @@ -255,8 +280,10 @@ static int __init quiet_kernel(char *str __setup("debug", debug_kernel); __setup("quiet", quiet_kernel); -/* Unknown boot options get handed to init, unless they look like - failed parameters */ +/* + * Unknown boot options get handed to init, unless they look like + * failed parameters + */ static int __init unknown_bootoption(char *param, char *val) { /* Change NUL term back to "=", to make "param" the whole string. */ @@ -267,8 +294,10 @@ static int __init unknown_bootoption(cha if (obsolete_checksetup(param)) return 0; - /* Preemptive maintenance for "why didn't my mispelled command - line work?" */ + /* + * Preemptive maintenance for "why didn't my mispelled command + * line work?" + */ if (strchr(param, '.') && (!val || strchr(param, '.') < val)) { printk(KERN_ERR "Unknown boot option `%s': ignoring\n", param); return 0; @@ -308,7 +337,8 @@ static int __init init_setup(char *str) unsigned int i; execute_command = str; - /* In case LILO is going to boot us with default command line, + /* + * In case LILO is going to boot us with default command line, * it prepends "auto" before the whole cmdline which makes * the shell think it should execute a script with such name. * So we ignore all arguments entered _before_ init=... [MJ] @@ -471,16 +501,15 @@ asmlinkage void __init start_kernel(void * time - but meanwhile we still have a functioning scheduler. */ sched_init(); - build_all_zonelists(); page_alloc_init(); + trap_init(); printk("Kernel command line: %s\n", saved_command_line); parse_early_param(); parse_args("Booting kernel", command_line, __start___param, __stop___param - __start___param, &unknown_bootoption); sort_main_extable(); - trap_init(); rcu_init(); init_IRQ(); pidhash_init(); @@ -534,17 +563,12 @@ asmlinkage void __init start_kernel(void #ifdef CONFIG_PROC_FS proc_root_init(); #endif + cpuset_init(); + check_bugs(); acpi_early_init(); /* before LAPIC and SMP init */ - /* - * We count on the initial thread going ok - * Like idlers init is an unlocked kernel thread, which will - * make syscalls (and thus be locked). - */ - init_idle(current, smp_processor_id()); - /* Do the rest non-__init'ed, we're now alive */ rest_init(); } @@ -679,6 +703,8 @@ static int init(void * unused) smp_init(); sched_init_smp(); + cpuset_init_smp(); + /* * Do this before initcalls, because some drivers want to access * firmware files. --- linux-2.6.9-rc1/ipc/shm.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/ipc/shm.c 2004-09-02 20:51:53.000000000 -0700 @@ -78,7 +78,7 @@ static inline struct shmid_kernel *shm_r static inline int shm_addid(struct shmid_kernel *shp) { - return ipc_addid(&shm_ids, &shp->shm_perm, shm_ctlmni+1); + return ipc_addid(&shm_ids, &shp->shm_perm, shm_ctlmni); } @@ -688,6 +688,10 @@ long do_shmat(int shmid, char __user *sh o_flags = O_RDWR; acc_mode = S_IRUGO | S_IWUGO; } + if (shmflg & SHM_EXEC) { + prot |= PROT_EXEC; + acc_mode |= S_IXUGO; + } /* * We cannot rely on the fs check since SYSV IPC does have an --- linux-2.6.9-rc1/kernel/acct.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/kernel/acct.c 2004-09-03 00:57:09.241634320 -0700 @@ -384,6 +384,8 @@ static void do_acct_process(long exitcod unsigned long vsize; unsigned long flim; u64 elapsed; + u64 run_time; + struct timespec uptime; /* * First check to see if there is enough free_space to continue @@ -401,7 +403,13 @@ static void do_acct_process(long exitcod ac.ac_version = ACCT_VERSION | ACCT_BYTEORDER; strlcpy(ac.ac_comm, current->comm, sizeof(ac.ac_comm)); - elapsed = jiffies_64_to_AHZ(get_jiffies_64() - current->start_time); + /* calculate run_time in nsec*/ + do_posix_clock_monotonic_gettime(&uptime); + run_time = (u64)uptime.tv_sec*NSEC_PER_SEC + uptime.tv_nsec; + run_time -= (u64)current->start_time.tv_sec*NSEC_PER_SEC + + current->start_time.tv_nsec; + /* convert nsec -> AHZ */ + elapsed = nsec_to_AHZ(run_time); #if ACCT_VERSION==3 ac.ac_etime = encode_float(elapsed); #else --- linux-2.6.9-rc1/kernel/audit.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/kernel/audit.c 2004-09-02 20:51:53.000000000 -0700 @@ -708,7 +708,7 @@ void audit_log_d_path(struct audit_buffe audit_log_move(ab); avail = sizeof(ab->tmp) - ab->len; p = d_path(dentry, vfsmnt, ab->tmp + ab->len, avail); - if (p == ERR_PTR(-ENAMETOOLONG)) { + if (IS_ERR(p)) { /* FIXME: can we save some information here? */ audit_log_format(ab, ""); } else { --- linux-2.6.9-rc1/kernel/capability.c 2004-08-24 00:03:13.000000000 -0700 +++ 25/kernel/capability.c 2004-09-02 20:51:53.000000000 -0700 @@ -89,14 +89,12 @@ static inline void cap_set_pg(int pgrp, kernel_cap_t *permitted) { task_t *g, *target; - struct list_head *l; - struct pid *pid; - for_each_task_pid(pgrp, PIDTYPE_PGID, g, l, pid) { + do_each_task_pid(pgrp, PIDTYPE_PGID, g) { target = g; while_each_thread(g, target) security_capset_set(target, effective, inheritable, permitted); - } + } while_each_task_pid(pgrp, PIDTYPE_PGID, g); } /* --- linux-2.6.9-rc1/kernel/compat.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/kernel/compat.c 2004-09-02 20:51:53.000000000 -0700 @@ -160,10 +160,39 @@ asmlinkage long compat_sys_times(struct */ if (tbuf) { struct compat_tms tmp; - tmp.tms_utime = compat_jiffies_to_clock_t(current->utime); - tmp.tms_stime = compat_jiffies_to_clock_t(current->stime); - tmp.tms_cutime = compat_jiffies_to_clock_t(current->cutime); - tmp.tms_cstime = compat_jiffies_to_clock_t(current->cstime); + struct task_struct *tsk = current; + struct task_struct *t; + unsigned long utime, stime, cutime, cstime; + + read_lock(&tasklist_lock); + utime = tsk->signal->utime; + stime = tsk->signal->stime; + t = tsk; + do { + utime += t->utime; + stime += t->stime; + t = next_thread(t); + } while (t != tsk); + + /* + * While we have tasklist_lock read-locked, no dying thread + * can be updating current->signal->[us]time. Instead, + * we got their counts included in the live thread loop. + * However, another thread can come in right now and + * do a wait call that updates current->signal->c[us]time. + * To make sure we always see that pair updated atomically, + * we take the siglock around fetching them. + */ + spin_lock_irq(&tsk->sighand->siglock); + cutime = tsk->signal->cutime; + cstime = tsk->signal->cstime; + spin_unlock_irq(&tsk->sighand->siglock); + read_unlock(&tasklist_lock); + + tmp.tms_utime = compat_jiffies_to_clock_t(utime); + tmp.tms_stime = compat_jiffies_to_clock_t(stime); + tmp.tms_cutime = compat_jiffies_to_clock_t(cutime); + tmp.tms_cstime = compat_jiffies_to_clock_t(cstime); if (copy_to_user(tbuf, &tmp, sizeof(tmp))) return -EFAULT; } @@ -310,7 +339,7 @@ asmlinkage long compat_sys_getrlimit (un return ret; } -static long put_compat_rusage(struct compat_rusage __user *ru, struct rusage *r) +int put_compat_rusage(const struct rusage *r, struct compat_rusage __user *ru) { if (!access_ok(VERIFY_WRITE, ru, sizeof(*ru)) || __put_user(r->ru_utime.tv_sec, &ru->ru_utime.tv_sec) || @@ -348,7 +377,7 @@ asmlinkage long compat_sys_getrusage(int if (ret) return ret; - if (put_compat_rusage(ru, &r)) + if (put_compat_rusage(&r, ru)) return -EFAULT; return 0; @@ -374,7 +403,7 @@ compat_sys_wait4(compat_pid_t pid, compa set_fs (old_fs); if (ret > 0) { - if (put_compat_rusage(ru, &r)) + if (put_compat_rusage(&r, ru)) return -EFAULT; if (stat_addr && put_user(status, stat_addr)) return -EFAULT; --- linux-2.6.9-rc1/kernel/cpu.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/kernel/cpu.c 2004-09-03 00:56:52.903118152 -0700 @@ -89,19 +89,15 @@ static int take_cpu_down(void *unused) { int err; - /* Take offline: makes arch_cpu_down somewhat easier. */ - cpu_clear(smp_processor_id(), cpu_online_map); - /* Ensure this CPU doesn't handle any more interrupts. */ err = __cpu_disable(); if (err < 0) - cpu_set(smp_processor_id(), cpu_online_map); - else - /* Force idle task to run as soon as we yield: it should - immediately notice cpu is offline and die quickly. */ - sched_idle_next(); + return err; - return err; + /* Force idle task to run as soon as we yield: it should + immediately notice cpu is offline and die quickly. */ + sched_idle_next(); + return 0; } int cpu_down(unsigned int cpu) --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/kernel/cpuset.c 2004-09-03 00:57:00.772921760 -0700 @@ -0,0 +1,1503 @@ +/* + * kernel/cpuset.c + * + * Processor and Memory placement constraints for sets of tasks. + * + * Copyright (C) 2003 BULL SA. + * Copyright (C) 2004 Silicon Graphics, Inc. + * + * Portions derived from Patrick Mochel's sysfs code. + * sysfs is Copyright (c) 2001-3 Patrick Mochel + * Portions Copyright (c) 2004 Silicon Graphics, Inc. + * + * 2003-10-10 Written by Simon Derr + * 2003-10-22 Updates by Stephen Hemminger. + * 2004 May-July Rework by Paul Jackson + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of the Linux + * distribution for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define CPUSET_SUPER_MAGIC 0x27e0eb + +struct cpuset { + unsigned long flags; /* "unsigned long" so bitops work */ + cpumask_t cpus_allowed; /* CPUs allowed to tasks in cpuset */ + nodemask_t mems_allowed; /* Memory Nodes allowed to tasks */ + + atomic_t count; /* count tasks using this cpuset */ + + /* + * We link our 'sibling' struct into our parents 'children'. + * Our children link their 'sibling' into our 'children'. + */ + struct list_head sibling; /* my parents children */ + struct list_head children; /* my children */ + + struct cpuset *parent; /* my parent */ + struct dentry *dentry; /* cpuset fs entry */ + + /* + * Copy of global cpuset_mems_generation as of the most + * recent time this cpuset changed its mems_allowed. + */ + int mems_generation; +}; + +/* bits in struct cpuset flags field */ +typedef enum { + CS_CPU_EXCLUSIVE, + CS_MEM_EXCLUSIVE, + CS_REMOVED, + CS_NOTIFY_ON_RELEASE +} cpuset_flagbits_t; + +/* convenient tests for these bits */ +static inline int is_cpu_exclusive(const struct cpuset *cs) +{ + return !!test_bit(CS_CPU_EXCLUSIVE, &cs->flags); +} + +static inline int is_mem_exclusive(const struct cpuset *cs) +{ + return !!test_bit(CS_MEM_EXCLUSIVE, &cs->flags); +} + +static inline int is_removed(const struct cpuset *cs) +{ + return !!test_bit(CS_REMOVED, &cs->flags); +} + +static inline int notify_on_release(const struct cpuset *cs) +{ + return !!test_bit(CS_NOTIFY_ON_RELEASE, &cs->flags); +} + +/* + * Increment this atomic integer everytime any cpuset changes its + * mems_allowed value. Users of cpusets can track this generation + * number, and avoid having to lock and reload mems_allowed unless + * the cpuset they're using changes generation. + * + * A single, global generation is needed because attach_task() could + * reattach a task to a different cpuset, which must not have its + * generation numbers aliased with those of that tasks previous cpuset. + * + * Generations are needed for mems_allowed because one task cannot + * modify anothers memory placement. So we must enable every task, + * on every visit to __alloc_pages(), to efficiently check whether + * its current->cpuset->mems_allowed has changed, requiring an update + * of its current->mems_allowed. + */ +static atomic_t cpuset_mems_generation = ATOMIC_INIT(1); + +static struct cpuset top_cpuset = { + .flags = ((1 << CS_CPU_EXCLUSIVE) | (1 << CS_MEM_EXCLUSIVE)), + .cpus_allowed = CPU_MASK_ALL, + .mems_allowed = NODE_MASK_ALL, + .count = ATOMIC_INIT(0), + .sibling = LIST_HEAD_INIT(top_cpuset.sibling), + .children = LIST_HEAD_INIT(top_cpuset.children), + .parent = NULL, + .dentry = NULL, + .mems_generation = 0, +}; + +static struct vfsmount *cpuset_mount; +static struct super_block *cpuset_sb = NULL; + +/* + * cpuset_sem should be held by anyone who is depending on the children + * or sibling lists of any cpuset, or performing non-atomic operations + * on the flags or *_allowed values of a cpuset, such as raising the + * CS_REMOVED flag bit iff it is not already raised, or reading and + * conditionally modifying the *_allowed values. One kernel global + * cpuset semaphore should be sufficient - these things don't change + * that much. + * + * The code that modifies cpusets holds cpuset_sem across the entire + * operation, from cpuset_common_file_write() down, single threading + * all cpuset modifications (except for counter manipulations from + * fork and exit) across the system. This presumes that cpuset + * modifications are rare - better kept simple and safe, even if slow. + * + * The code that reads cpusets, such as in cpuset_common_file_read() + * and below, only holds cpuset_sem across small pieces of code, such + * as when reading out possibly multi-word cpumasks and nodemasks, as + * the risks are less, and the desire for performance a little greater. + * The proc_cpuset_show() routine needs to hold cpuset_sem to insure + * that no cs->dentry is NULL, as it walks up the cpuset tree to root. + * + * The hooks from fork and exit, cpuset_fork() and cpuset_exit(), don't + * (usually) grab cpuset_sem. These are the two most performance + * critical pieces of code here. The exception occurs on exit(), + * if the last task using a cpuset exits, and the cpuset was marked + * notify_on_release. In that case, the cpuset_sem is taken, the + * path to the released cpuset calculated, and a usermode call made + * to /sbin/cpuset_release_agent with the name of the cpuset (path + * relative to the root of cpuset file system) as the argument. + * + * A cpuset can only be deleted if both its 'count' of using tasks is + * zero, and its list of 'children' cpusets is empty. Since all tasks + * in the system use _some_ cpuset, and since there is always at least + * one task in the system (init, pid == 1), therefore, top_cpuset + * always has either children cpusets and/or using tasks. So no need + * for any special hack to ensure that top_cpuset cannot be deleted. + */ + +static DECLARE_MUTEX(cpuset_sem); + +/* + * A couple of forward declarations required, due to cyclic reference loop: + * cpuset_mkdir -> cpuset_create -> cpuset_populate_dir -> cpuset_add_file + * -> cpuset_create_file -> cpuset_dir_inode_operations -> cpuset_mkdir. + */ + +static int cpuset_mkdir(struct inode *dir, struct dentry *dentry, int mode); +static int cpuset_rmdir(struct inode *unused_dir, struct dentry *dentry); + +static struct backing_dev_info cpuset_backing_dev_info = { + .ra_pages = 0, /* No readahead */ + .memory_backed = 1, /* Does not contribute to dirty memory */ +}; + +static struct inode *cpuset_new_inode(mode_t mode) +{ + struct inode *inode = new_inode(cpuset_sb); + + if (inode) { + inode->i_mode = mode; + inode->i_uid = current->fsuid; + inode->i_gid = current->fsgid; + inode->i_blksize = PAGE_CACHE_SIZE; + inode->i_blocks = 0; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_mapping->backing_dev_info = &cpuset_backing_dev_info; + } + return inode; +} + +static void cpuset_diput(struct dentry *dentry, struct inode *inode) +{ + /* is dentry a directory ? if so, kfree() associated cpuset */ + if (S_ISDIR(inode->i_mode)) { + struct cpuset *cs = (struct cpuset *)dentry->d_fsdata; + BUG_ON(!(is_removed(cs))); + kfree(cs); + } + iput(inode); +} + +static struct dentry_operations cpuset_dops = { + .d_iput = cpuset_diput, +}; + +static struct dentry *cpuset_get_dentry(struct dentry *parent, const char *name) +{ + struct qstr qstr; + struct dentry *d; + + qstr.name = name; + qstr.len = strlen(name); + qstr.hash = full_name_hash(name, qstr.len); + d = lookup_hash(&qstr, parent); + if (d) + d->d_op = &cpuset_dops; + return d; +} + +static void remove_dir(struct dentry *d) +{ + struct dentry *parent = dget(d->d_parent); + + d_delete(d); + simple_rmdir(parent->d_inode, d); + dput(parent); +} + +/* + * NOTE : the dentry must have been dget()'ed + */ +static void cpuset_d_remove_dir(struct dentry *dentry) +{ + struct list_head *node; + + spin_lock(&dcache_lock); + node = dentry->d_subdirs.next; + while (node != &dentry->d_subdirs) { + struct dentry *d = list_entry(node, struct dentry, d_child); + list_del_init(node); + if (d->d_inode) { + d = dget_locked(d); + spin_unlock(&dcache_lock); + d_delete(d); + simple_unlink(dentry->d_inode, d); + dput(d); + spin_lock(&dcache_lock); + } + node = dentry->d_subdirs.next; + } + list_del_init(&dentry->d_child); + spin_unlock(&dcache_lock); + remove_dir(dentry); +} + +static struct super_operations cpuset_ops = { + .statfs = simple_statfs, + .drop_inode = generic_delete_inode, +}; + +static int cpuset_fill_super(struct super_block *sb, void *unused_data, + int unused_silent) +{ + struct inode *inode; + struct dentry *root; + + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + sb->s_magic = CPUSET_SUPER_MAGIC; + sb->s_op = &cpuset_ops; + cpuset_sb = sb; + + inode = cpuset_new_inode(S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR); + if (inode) { + inode->i_op = &simple_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + /* directories start off with i_nlink == 2 (for "." entry) */ + inode->i_nlink++; + } else { + return -ENOMEM; + } + + root = d_alloc_root(inode); + if (!root) { + iput(inode); + return -ENOMEM; + } + sb->s_root = root; + return 0; +} + +static struct super_block *cpuset_get_sb(struct file_system_type *fs_type, + int flags, const char *unused_dev_name, + void *data) +{ + return get_sb_single(fs_type, flags, data, cpuset_fill_super); +} + +static struct file_system_type cpuset_fs_type = { + .name = "cpuset", + .get_sb = cpuset_get_sb, + .kill_sb = kill_litter_super, +}; + +/* struct cftype: + * + * The files in the cpuset filesystem mostly have a very simple read/write + * handling, some common function will take care of it. Nevertheless some cases + * (read tasks) are special and therefore I define this structure for every + * kind of file. + * + * + * When reading/writing to a file: + * - the cpuset to use in file->f_dentry->d_parent->d_fsdata + * - the 'cftype' of the file is file->f_dentry->d_fsdata + */ + +struct cftype { + char *name; + int private; + int (*open) (struct inode *inode, struct file *file); + ssize_t (*read) (struct file *file, char __user *buf, size_t nbytes, + loff_t *ppos); + int (*write) (struct file *file, const char *buf, size_t nbytes, + loff_t *ppos); + int (*release) (struct inode *inode, struct file *file); +}; + +static inline struct cpuset *__d_cs(struct dentry *dentry) +{ + return (struct cpuset *)dentry->d_fsdata; +} + +static inline struct cftype *__d_cft(struct dentry *dentry) +{ + return (struct cftype *)dentry->d_fsdata; +} + +/* + * Call with cpuset_sem held. Writes path of cpuset into buf. + * Returns 0 on success, -errno on error. + */ + +static int cpuset_path(const struct cpuset *cs, char *buf, int buflen) +{ + char *start; + + start = buf + buflen; + + *--start = '\0'; + for (;;) { + int len = cs->dentry->d_name.len; + if ((start -= len) < buf) + return -ENAMETOOLONG; + memcpy(start, cs->dentry->d_name.name, len); + cs = cs->parent; + if (!cs) + break; + if (!cs->parent) + continue; + if (--start < buf) + return -ENAMETOOLONG; + *start = '/'; + } + memmove(buf, start, buf + buflen - start); + return 0; +} + +/* + * Notify userspace when a cpuset is released, by running + * /sbin/cpuset_release_agent with the name of the cpuset (path + * relative to the root of cpuset file system) as the argument. + * + * Most likely, this user command will try to rmdir this cpuset. + * + * This races with the possibility that some other task will be + * attached to this cpuset before it is removed, or that some other + * user task will 'mkdir' a child cpuset of this cpuset. That's ok. + * The presumed 'rmdir' will fail quietly if this cpuset is no longer + * unused, and this cpuset will be reprieved from its death sentence, + * to continue to serve a useful existence. Next time it's released, + * we will get notified again, if it still has 'notify_on_release' set. + * + * Note final arg to call_usermodehelper() is 0 - that means + * don't wait. Since we are holding the global cpuset_sem here, + * and we are asking another thread (started from keventd) to rmdir a + * cpuset, we can't wait - or we'd deadlock with the removing thread + * on cpuset_sem. + */ + +static int cpuset_release_agent(char *cpuset_str) +{ + char *argv[3], *envp[3]; + int i; + + i = 0; + argv[i++] = "/sbin/cpuset_release_agent"; + argv[i++] = cpuset_str; + argv[i] = NULL; + + i = 0; + /* minimal command environment */ + envp[i++] = "HOME=/"; + envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + envp[i] = NULL; + + return call_usermodehelper(argv[0], argv, envp, 0); +} + +/* + * Either cs->count of using tasks transitioned to zero, or the + * cs->children list of child cpusets just became empty. If this + * cs is notify_on_release() and now both the user count is zero and + * the list of children is empty, send notice to user land. + */ + +static void check_for_release(struct cpuset *cs) +{ + if (notify_on_release(cs) && atomic_read(&cs->count) == 0 && + list_empty(&cs->children)) { + char *buf; + + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return; + if (cpuset_path(cs, buf, PAGE_SIZE) < 0) + goto out; + cpuset_release_agent(buf); + out: + kfree(buf); + } +} + +/* + * is_cpuset_subset(p, q) - Is cpuset p a subset of cpuset q? + * + * One cpuset is a subset of another if all its allowed CPUs and + * Memory Nodes are a subset of the other, and its exclusive flags + * are only set if the other's are set. + */ + +static int is_cpuset_subset(const struct cpuset *p, const struct cpuset *q) +{ + return cpus_subset(p->cpus_allowed, q->cpus_allowed) && + nodes_subset(p->mems_allowed, q->mems_allowed) && + is_cpu_exclusive(p) <= is_cpu_exclusive(q) && + is_mem_exclusive(p) <= is_mem_exclusive(q); +} + +/* + * validate_change() - Used to validate that any proposed cpuset change + * follows the structural rules for cpusets. + * + * If we replaced the flag and mask values of the current cpuset + * (cur) with those values in the trial cpuset (trial), would + * our various subset and exclusive rules still be valid? Presumes + * cpuset_sem held. + * + * 'cur' is the address of an actual, in-use cpuset. Operations + * such as list traversal that depend on the actual address of the + * cpuset in the list must use cur below, not trial. + * + * 'trial' is the address of bulk structure copy of cur, with + * perhaps one or more of the fields cpus_allowed, mems_allowed, + * or flags changed to new, trial values. + * + * Return 0 if valid, -errno if not. + */ + +static int validate_change(const struct cpuset *cur, const struct cpuset *trial) +{ + struct cpuset *c, *par = cur->parent; + + /* + * Don't mess with Big Daddy - top_cpuset must remain maximal. + * And besides, the rest of this routine blows chunks if par == 0. + */ + if (cur == &top_cpuset) + return -EPERM; + + /* Any in-use cpuset must have at least ONE cpu and mem */ + if (atomic_read(&trial->count) > 1) { + if (cpus_empty(trial->cpus_allowed)) + return -ENOSPC; + if (nodes_empty(trial->mems_allowed)) + return -ENOSPC; + } + + /* We must be a subset of our parent cpuset */ + if (!is_cpuset_subset(trial, par)) + return -EACCES; + + /* Each of our child cpusets must be a subset of us */ + list_for_each_entry(c, &cur->children, sibling) { + if (!is_cpuset_subset(c, trial)) + return -EBUSY; + } + + /* If either I or some sibling (!= me) is exclusive, we can't overlap */ + list_for_each_entry(c, &par->children, sibling) { + if ((is_cpu_exclusive(trial) || is_cpu_exclusive(c)) && + c != cur && + cpus_intersects(trial->cpus_allowed, c->cpus_allowed) + ) { + return -EINVAL; + } + if ((is_mem_exclusive(trial) || is_mem_exclusive(c)) && + c != cur && + nodes_intersects(trial->mems_allowed, c->mems_allowed) + ) { + return -EINVAL; + } + } + + return 0; +} + +static int update_cpumask(struct cpuset *cs, char *buf) +{ + struct cpuset trialcs; + int retval; + + trialcs = *cs; + retval = cpulist_parse(buf, trialcs.cpus_allowed); + if (retval < 0) + return retval; + cpus_and(trialcs.cpus_allowed, trialcs.cpus_allowed, cpu_online_map); + retval = validate_change(cs, &trialcs); + if (retval == 0) + cs->cpus_allowed = trialcs.cpus_allowed; + return retval; +} + +static int update_nodemask(struct cpuset *cs, char *buf) +{ + struct cpuset trialcs; + int retval; + + trialcs = *cs; + retval = nodelist_parse(buf, trialcs.mems_allowed); + if (retval < 0) + return retval; + retval = validate_change(cs, &trialcs); + if (retval == 0) { + cs->mems_allowed = trialcs.mems_allowed; + atomic_inc(&cpuset_mems_generation); + cs->mems_generation = atomic_read(&cpuset_mems_generation); + } + return retval; +} + +/* + * update_flag - read a 0 or a 1 in a file and update associated flag + * bit: the bit to update (CS_CPU_EXCLUSIVE, CS_MEM_EXCLUSIVE, + * CS_NOTIFY_ON_RELEASE) + * cs: the cpuset to update + * buf: the buffer where we read the 0 or 1 + */ + +static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs, char *buf) +{ + int turning_on; + struct cpuset trialcs; + int err; + + turning_on = (simple_strtoul(buf, NULL, 10) != 0); + + trialcs = *cs; + if (turning_on) + set_bit(bit, &trialcs.flags); + else + clear_bit(bit, &trialcs.flags); + + err = validate_change(cs, &trialcs); + if (err == 0) { + if (turning_on) + set_bit(bit, &cs->flags); + else + clear_bit(bit, &cs->flags); + } + return err; +} + +static int attach_task(struct cpuset *cs, char *buf) +{ + pid_t pid; + struct task_struct *tsk; + struct cpuset *oldcs; + cpumask_t cpus; + + if (sscanf(buf, "%d", &pid) != 1) + return -EIO; + if (cpus_empty(cs->cpus_allowed) || nodes_empty(cs->mems_allowed)) + return -ENOSPC; + + if (pid) { + read_lock(&tasklist_lock); + + tsk = find_task_by_pid(pid); + if (!tsk) { + read_unlock(&tasklist_lock); + return -ESRCH; + } + + get_task_struct(tsk); + read_unlock(&tasklist_lock); + + if ((current->euid) && (current->euid != tsk->uid) + && (current->euid != tsk->suid)) { + put_task_struct(tsk); + return -EACCES; + } + } else { + tsk = current; + get_task_struct(tsk); + } + + task_lock(tsk); + oldcs = tsk->cpuset; + if (!oldcs) { + task_unlock(tsk); + put_task_struct(tsk); + return -ESRCH; + } + atomic_inc(&cs->count); + tsk->cpuset = cs; + task_unlock(tsk); + + /* + * If the tasks current CPU placement overlaps with its new cpuset, + * then let it run in that overlap. Otherwise fallback to simply + * letting it have the run of the CPUs in the new cpuset. + */ + if (cpus_intersects(tsk->cpus_allowed, cs->cpus_allowed)) + cpus_and(cpus, tsk->cpus_allowed, cs->cpus_allowed); + else + cpus = cs->cpus_allowed; + set_cpus_allowed(tsk, cpus); + + put_task_struct(tsk); + if (atomic_dec_and_test(&oldcs->count)) + check_for_release(oldcs); + return 0; +} + +/* The various types of files and directories in a cpuset file system */ + +typedef enum { + FILE_ROOT, + FILE_DIR, + FILE_CPULIST, + FILE_MEMLIST, + FILE_CPU_EXCLUSIVE, + FILE_MEM_EXCLUSIVE, + FILE_NOTIFY_ON_RELEASE, + FILE_TASKLIST, +} cpuset_filetype_t; + +static ssize_t cpuset_common_file_write(struct file *file, const char *userbuf, + size_t nbytes, loff_t *unused_ppos) +{ + struct cpuset *cs = __d_cs(file->f_dentry->d_parent); + struct cftype *cft = __d_cft(file->f_dentry); + cpuset_filetype_t type = cft->private; + char *buffer; + int retval = 0; + + /* Crude upper limit on largest legitimate cpulist user might write. */ + if (nbytes > 100 + 6 * NR_CPUS) + return -E2BIG; + + /* +1 for nul-terminator */ + if ((buffer = kmalloc(nbytes + 1, GFP_KERNEL)) == 0) + return -ENOMEM; + + if (copy_from_user(buffer, userbuf, nbytes)) { + retval = -EFAULT; + goto out1; + } + buffer[nbytes] = 0; /* nul-terminate */ + + down(&cpuset_sem); + + if (is_removed(cs)) { + retval = -ENODEV; + goto out2; + } + + switch (type) { + case FILE_CPULIST: + retval = update_cpumask(cs, buffer); + break; + case FILE_MEMLIST: + retval = update_nodemask(cs, buffer); + break; + case FILE_CPU_EXCLUSIVE: + retval = update_flag(CS_CPU_EXCLUSIVE, cs, buffer); + break; + case FILE_MEM_EXCLUSIVE: + retval = update_flag(CS_MEM_EXCLUSIVE, cs, buffer); + break; + case FILE_NOTIFY_ON_RELEASE: + retval = update_flag(CS_NOTIFY_ON_RELEASE, cs, buffer); + break; + case FILE_TASKLIST: + retval = attach_task(cs, buffer); + break; + default: + retval = -EINVAL; + goto out2; + } + + if (retval == 0) + retval = nbytes; +out2: + up(&cpuset_sem); +out1: + kfree(buffer); + return retval; +} + +static ssize_t cpuset_file_write(struct file *file, const char *buf, + size_t nbytes, loff_t *ppos) +{ + ssize_t retval = 0; + struct cftype *cft = __d_cft(file->f_dentry); + if (!cft) + return -ENODEV; + + /* special function ? */ + if (cft->write) + retval = cft->write(file, buf, nbytes, ppos); + else + retval = cpuset_common_file_write(file, buf, nbytes, ppos); + + return retval; +} + +/* + * These ascii lists should be read in a single call, by using a user + * buffer large enough to hold the entire map. If read in smaller + * chunks, there is no guarantee of atomicity. Since the display format + * used, list of ranges of sequential numbers, is variable length, + * and since these maps can change value dynamically, one could read + * gibberish by doing partial reads while a list was changing. + * A single large read to a buffer that crosses a page boundary is + * ok, because the result being copied to user land is not recomputed + * across a page fault. + */ + +static int cpuset_sprintf_cpulist(char *page, struct cpuset *cs) +{ + cpumask_t mask; + + down(&cpuset_sem); + mask = cs->cpus_allowed; + up(&cpuset_sem); + + return cpulist_scnprintf(page, PAGE_SIZE, mask); +} + +static int cpuset_sprintf_memlist(char *page, struct cpuset *cs) +{ + nodemask_t mask; + + down(&cpuset_sem); + mask = cs->mems_allowed; + up(&cpuset_sem); + + return nodelist_scnprintf(page, PAGE_SIZE, mask); +} + +static ssize_t cpuset_common_file_read(struct file *file, char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct cftype *cft = __d_cft(file->f_dentry); + struct cpuset *cs = __d_cs(file->f_dentry->d_parent); + cpuset_filetype_t type = cft->private; + char *page; + ssize_t retval = 0; + char *s; + char *start; + size_t n; + + if (!(page = (char *)__get_free_page(GFP_KERNEL))) + return -ENOMEM; + + s = page; + + switch (type) { + case FILE_CPULIST: + s += cpuset_sprintf_cpulist(s, cs); + break; + case FILE_MEMLIST: + s += cpuset_sprintf_memlist(s, cs); + break; + case FILE_CPU_EXCLUSIVE: + *s++ = is_cpu_exclusive(cs) ? '1' : '0'; + break; + case FILE_MEM_EXCLUSIVE: + *s++ = is_mem_exclusive(cs) ? '1' : '0'; + break; + case FILE_NOTIFY_ON_RELEASE: + *s++ = notify_on_release(cs) ? '1' : '0'; + break; + default: + retval = -EINVAL; + goto out; + } + *s++ = '\n'; + *s = '\0'; + + start = page + *ppos; + n = s - start; + retval = n - copy_to_user(buf, start, min(n, nbytes)); + *ppos += retval; +out: + free_page((unsigned long)page); + return retval; +} + +static ssize_t cpuset_file_read(struct file *file, char *buf, size_t nbytes, + loff_t *ppos) +{ + ssize_t retval = 0; + struct cftype *cft = __d_cft(file->f_dentry); + if (!cft) + return -ENODEV; + + /* special function ? */ + if (cft->read) + retval = cft->read(file, buf, nbytes, ppos); + else + retval = cpuset_common_file_read(file, buf, nbytes, ppos); + + return retval; +} + +static int cpuset_file_open(struct inode *inode, struct file *file) +{ + int err; + struct cftype *cft; + + err = generic_file_open(inode, file); + if (err) + return err; + + cft = __d_cft(file->f_dentry); + if (!cft) + return -ENODEV; + if (cft->open) + err = cft->open(inode, file); + else + err = 0; + + return err; +} + +static int cpuset_file_release(struct inode *inode, struct file *file) +{ + struct cftype *cft = __d_cft(file->f_dentry); + if (cft->release) + return cft->release(inode, file); + return 0; +} + +static struct file_operations cpuset_file_operations = { + .read = cpuset_file_read, + .write = cpuset_file_write, + .llseek = generic_file_llseek, + .open = cpuset_file_open, + .release = cpuset_file_release, +}; + +static struct inode_operations cpuset_dir_inode_operations = { + .lookup = simple_lookup, + .mkdir = cpuset_mkdir, + .rmdir = cpuset_rmdir, +}; + +static int cpuset_create_file(struct dentry *dentry, int mode) +{ + struct inode *inode; + + if (!dentry) + return -ENOENT; + if (dentry->d_inode) + return -EEXIST; + + inode = cpuset_new_inode(mode); + if (!inode) + return -ENOMEM; + + if (S_ISDIR(mode)) { + inode->i_op = &cpuset_dir_inode_operations; + inode->i_fop = &simple_dir_operations; + + /* start off with i_nlink == 2 (for "." entry) */ + inode->i_nlink++; + } else if (S_ISREG(mode)) { + inode->i_size = 0; + inode->i_fop = &cpuset_file_operations; + } + + d_instantiate(dentry, inode); + dget(dentry); /* Extra count - pin the dentry in core */ + return 0; +} + +/* + * cpuset_create_dir - create a directory for an object. + * cs: the cpuset we create the directory for. + * It must have a valid ->parent field + * And we are going to fill its ->dentry field. + * name: The name to give to the cpuset directory. Will be copied. + * mode: mode to set on new directory. + */ + +static int cpuset_create_dir(struct cpuset *cs, const char *name, int mode) +{ + struct dentry *dentry = NULL; + struct dentry *parent; + int error = 0; + + parent = cs->parent->dentry; + dentry = cpuset_get_dentry(parent, name); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + error = cpuset_create_file(dentry, S_IFDIR | mode); + if (!error) { + dentry->d_fsdata = cs; + parent->d_inode->i_nlink++; + cs->dentry = dentry; + } + dput(dentry); + + return error; +} + +/* MUST be called with dir->d_inode->i_sem held */ + +static int cpuset_add_file(struct dentry *dir, const struct cftype *cft) +{ + struct dentry *dentry; + int error; + + dentry = cpuset_get_dentry(dir, cft->name); + if (!IS_ERR(dentry)) { + error = cpuset_create_file(dentry, 0644 | S_IFREG); + if (!error) + dentry->d_fsdata = (void *)cft; + dput(dentry); + } else + error = PTR_ERR(dentry); + return error; +} + +/* + * Stuff for reading the 'tasks' file. + * + * Reading this file can return large amounts of data if a cpuset has + * *lots* of attached tasks. So it may need several calls to read(), + * but we cannot guarantee that the information we produce is correct + * unless we produce it entirely atomically. + * + * Upon first file read(), a struct ctr_struct is allocated, that + * will have a pointer to an array (also allocated here). The struct + * ctr_struct * is stored in file->private_data. Its resources will + * be freed by release() when the file is closed. The array is used + * to sprintf the PIDs and then used by read(). + */ + +/* cpusets_tasks_read array */ + +struct ctr_struct { + char *buf; + int bufsz; +}; + +/* + * Load into 'pidarray' up to 'npids' of the tasks using cpuset 'cs'. + * Return actual number of pids loaded. + */ +static inline int pid_array_load(pid_t *pidarray, int npids, struct cpuset *cs) +{ + int n = 0; + struct task_struct *g, *p; + + read_lock(&tasklist_lock); + + do_each_thread(g, p) { + if (p->cpuset == cs) { + pidarray[n++] = p->pid; + if (unlikely(n == npids)) + goto array_full; + } + } + while_each_thread(g, p); + +array_full: + read_unlock(&tasklist_lock); + return n; +} + +/* + * In place bubble sort pidarray of npids pid_t's. + */ +static inline void pid_array_sort(pid_t *pidarray, int npids) +{ + int i, j; + + for (i = 0; i < npids - 1; i++) { + for (j = 0; j < npids - 1 - i; j++) + if (pidarray[j + 1] < pidarray[j]) { + pid_t tmp = pidarray[j]; + pidarray[j] = pidarray[j + 1]; + pidarray[j + 1] = tmp; + } + } +} + +/* + * Convert array 'a' of 'npids' pid_t's to a string of newline separated + * decimal pids in 'buf'. Don't write more than 'sz' chars, but return + * count 'cnt' of how many chars would be written if buf were large enough. + */ +static int pid_array_to_buf(char *buf, int sz, pid_t *a, int npids) +{ + int cnt = 0; + int i; + + for (i = 0; i < npids; i++) + cnt += snprintf(buf + cnt, max(sz - cnt, 0), "%d\n", a[i]); + return cnt; +} + +static inline struct ctr_struct *cpuset_tasks_mkctr(struct file *file) +{ + struct cpuset *cs = __d_cs(file->f_dentry->d_parent); + struct ctr_struct *ctr; + pid_t *pidarray; + int npids; + char c; + + ctr = kmalloc(sizeof(*ctr), GFP_KERNEL); + if (!ctr) + goto err0; + + /* + * If cpuset gets more users after we read count, we won't have + * enough space - tough. This race is indistinguishable to the + * caller from the case that the additional cpuset users didn't + * show up until sometime later on. + */ + npids = atomic_read(&cs->count); + pidarray = kmalloc(npids * sizeof(pid_t), GFP_KERNEL); + if (!pidarray) + goto err1; + + npids = pid_array_load(pidarray, npids, cs); + pid_array_sort(pidarray, npids); + + /* Call pid_array_to_buf() twice, first just to get bufsz */ + ctr->bufsz = pid_array_to_buf(&c, sizeof(c), pidarray, npids) + 1; + ctr->buf = kmalloc(ctr->bufsz, GFP_KERNEL); + if (!ctr->buf) + goto err2; + ctr->bufsz = pid_array_to_buf(ctr->buf, ctr->bufsz, pidarray, npids); + + kfree(pidarray); + file->private_data = (void *)ctr; + return ctr; + +err2: + kfree(pidarray); +err1: + kfree(ctr); +err0: + return NULL; +} + +static ssize_t cpuset_tasks_read(struct file *file, char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct ctr_struct *ctr = (struct ctr_struct *)file->private_data; + + /* allocate buffer and fill it on first call to read() */ + if (!ctr) { + ctr = cpuset_tasks_mkctr(file); + if (!ctr) + return -ENOMEM; + } + + if (*ppos + nbytes > ctr->bufsz) + nbytes = ctr->bufsz - *ppos; + if (copy_to_user(buf, ctr->buf + *ppos, nbytes)) + return -EFAULT; + *ppos += nbytes; + return nbytes; +} + +static int cpuset_tasks_release(struct inode *unused_inode, struct file *file) +{ + struct ctr_struct *ctr; + + /* we have nothing to do if no read-access is needed */ + if (!(file->f_mode & FMODE_READ)) + return 0; + + ctr = (struct ctr_struct *)file->private_data; + if (ctr) { + kfree(ctr->buf); + kfree(ctr); + } + return 0; +} + +/* + * for the common functions, 'private' gives the type of file + */ + +static struct cftype cft_tasks = { + .name = "tasks", + .read = cpuset_tasks_read, + .release = cpuset_tasks_release, + .private = FILE_TASKLIST, +}; + +static struct cftype cft_cpus = { + .name = "cpus", + .private = FILE_CPULIST, +}; + +static struct cftype cft_mems = { + .name = "mems", + .private = FILE_MEMLIST, +}; + +static struct cftype cft_cpu_exclusive = { + .name = "cpu_exclusive", + .private = FILE_CPU_EXCLUSIVE, +}; + +static struct cftype cft_mem_exclusive = { + .name = "mem_exclusive", + .private = FILE_MEM_EXCLUSIVE, +}; + +static struct cftype cft_notify_on_release = { + .name = "notify_on_release", + .private = FILE_NOTIFY_ON_RELEASE, +}; + +/* MUST be called with ->d_inode->i_sem held */ +static int cpuset_populate_dir(struct dentry *cs_dentry) +{ + int err; + + if ((err = cpuset_add_file(cs_dentry, &cft_cpus)) < 0) + return err; + if ((err = cpuset_add_file(cs_dentry, &cft_mems)) < 0) + return err; + if ((err = cpuset_add_file(cs_dentry, &cft_cpu_exclusive)) < 0) + return err; + if ((err = cpuset_add_file(cs_dentry, &cft_mem_exclusive)) < 0) + return err; + if ((err = cpuset_add_file(cs_dentry, &cft_notify_on_release)) < 0) + return err; + if ((err = cpuset_add_file(cs_dentry, &cft_tasks)) < 0) + return err; + return 0; +} + +/* + * cpuset_create - create a cpuset + * parent: cpuset that will be parent of the new cpuset. + * name: name of the new cpuset. Will be strcpy'ed. + * mode: mode to set on new inode + * + * Must be called with the semaphore on the parent inode held + */ + +static long cpuset_create(struct cpuset *parent, const char *name, int mode) +{ + struct cpuset *cs; + int err; + + cs = kmalloc(sizeof(*cs), GFP_KERNEL); + if (!cs) + return -ENOMEM; + + down(&cpuset_sem); + cs->flags = 0; + if (notify_on_release(parent)) + set_bit(CS_NOTIFY_ON_RELEASE, &cs->flags); + cs->cpus_allowed = CPU_MASK_NONE; + cs->mems_allowed = NODE_MASK_NONE; + atomic_set(&cs->count, 0); + INIT_LIST_HEAD(&cs->sibling); + INIT_LIST_HEAD(&cs->children); + atomic_inc(&cpuset_mems_generation); + cs->mems_generation = atomic_read(&cpuset_mems_generation); + + cs->parent = parent; + + list_add(&cs->sibling, &cs->parent->children); + + err = cpuset_create_dir(cs, name, mode); + if (err < 0) + goto err; + err = cpuset_populate_dir(cs->dentry); + /* If err < 0, we have a half-filled directory - oh well ;) */ + up(&cpuset_sem); + return 0; +err: + list_del(&cs->sibling); + up(&cpuset_sem); + kfree(cs); + return err; +} + +static int cpuset_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + struct dentry *d_parent = dentry->d_parent; + struct cpuset *c_parent = (struct cpuset *)d_parent->d_fsdata; + + /* the vfs holds inode->i_sem already */ + return cpuset_create(c_parent, dentry->d_name.name, mode | S_IFDIR); +} + +static int cpuset_rmdir(struct inode *unused_dir, struct dentry *dentry) +{ + struct cpuset *cs = (struct cpuset *)dentry->d_fsdata; + struct dentry *d; + struct cpuset *parent; + + /* the vfs holds both inode->i_sem already */ + + down(&cpuset_sem); + if (atomic_read(&cs->count) > 0) { + up(&cpuset_sem); + return -EBUSY; + } + if (!list_empty(&cs->children)) { + up(&cpuset_sem); + return -EBUSY; + } + spin_lock(&cs->dentry->d_lock); + parent = cs->parent; + set_bit(CS_REMOVED, &cs->flags); + list_del(&cs->sibling); /* delete my sibling from parent->children */ + if (list_empty(&parent->children)) + check_for_release(parent); + d = dget(cs->dentry); + cs->dentry = NULL; + spin_unlock(&d->d_lock); + cpuset_d_remove_dir(d); + dput(d); + up(&cpuset_sem); + return 0; +} + +/** + * cpuset_init - initialize cpusets at system boot + * + * Description: Initialize top_cpuset and the cpuset internal file system, + **/ + +int __init cpuset_init(void) +{ + struct dentry *root; + int err; + + top_cpuset.mems_allowed = node_possible_map; + atomic_inc(&cpuset_mems_generation); + top_cpuset.mems_generation = atomic_read(&cpuset_mems_generation); + + init_task.cpuset = &top_cpuset; + + err = register_filesystem(&cpuset_fs_type); + if (err < 0) + goto out; + cpuset_mount = kern_mount(&cpuset_fs_type); + if (IS_ERR(cpuset_mount)) { + printk(KERN_ERR "cpuset: could not mount!\n"); + err = PTR_ERR(cpuset_mount); + cpuset_mount = NULL; + goto out; + } + root = cpuset_mount->mnt_sb->s_root; + root->d_fsdata = &top_cpuset; + root->d_inode->i_nlink++; + top_cpuset.dentry = root; + root->d_inode->i_op = &cpuset_dir_inode_operations; + err = cpuset_populate_dir(root); +out: + return err; +} + +/** + * cpuset_init_smp - initialize cpus_allowed + * + * Description: Initialize cpus_allowed after cpu_possible_map is initialized + **/ + +void __init cpuset_init_smp(void) +{ + top_cpuset.cpus_allowed = cpu_possible_map; +} + +/** + * cpuset_fork - attach newly forked task to its parents cpuset. + * @p: pointer to task_struct of forking parent process. + * + * Description: By default, on fork, a task inherits its + * parents cpuset. The pointer to the shared cpuset is + * automatically copied in fork.c by dup_task_struct(). + * This cpuset_fork() routine need only increment the usage + * counter in that cpuset. + **/ + +void cpuset_fork(struct task_struct *tsk) +{ + atomic_inc(&tsk->cpuset->count); +} + +/** + * cpuset_exit - detach cpuset from exiting task + * @tsk: pointer to task_struct of exiting process + * + * Description: Detach cpuset from @tsk and release it. + * + **/ + +void cpuset_exit(struct task_struct *tsk) +{ + struct cpuset *cs; + + task_lock(tsk); + cs = tsk->cpuset; + tsk->cpuset = NULL; + task_unlock(tsk); + + if (atomic_dec_and_test(&cs->count)) { + down(&cpuset_sem); + check_for_release(cs); + up(&cpuset_sem); + } +} + +/** + * cpuset_cpus_allowed - return cpus_allowed mask from a tasks cpuset. + * @tsk: pointer to task_struct from which to obtain cpuset->cpus_allowed. + * + * Description: Returns the cpumask_t cpus_allowed of the cpuset + * attached to the specified @tsk. + **/ + +const cpumask_t cpuset_cpus_allowed(const struct task_struct *tsk) +{ + cpumask_t mask; + + down(&cpuset_sem); + task_lock((struct task_struct *)tsk); + if (tsk->cpuset) + mask = tsk->cpuset->cpus_allowed; + else + mask = CPU_MASK_ALL; + task_unlock((struct task_struct *)tsk); + up(&cpuset_sem); + + return mask; +} + +void cpuset_init_current_mems_allowed(void) +{ + current->mems_allowed = NODE_MASK_ALL; +} + +/* + * If the current tasks cpusets mems_allowed changed behind our backs, + * update current->mems_allowed and mems_generation to the new value. + * Do not call this routine if in_interrupt(). + */ +void cpuset_update_current_mems_allowed() +{ + struct cpuset *cs = current->cpuset; + + if (!cs) + return; /* task is exiting */ + if (current->cpuset_mems_generation != cs->mems_generation) { + down(&cpuset_sem); + current->mems_allowed = cs->mems_allowed; + current->cpuset_mems_generation = cs->mems_generation; + up(&cpuset_sem); + } +} + +void cpuset_restrict_to_mems_allowed(unsigned long *nodes) +{ + bitmap_and(nodes, nodes, nodes_addr(current->mems_allowed), + MAX_NUMNODES); +} + +/* + * Are any of the nodes on zonelist zl allowed in current->mems_allowed? + */ +int cpuset_zonelist_valid_mems_allowed(struct zonelist *zl) +{ + int i; + + for (i = 0; zl->zones[i]; i++) { + int nid = zl->zones[i]->zone_pgdat->node_id; + + if (node_isset(nid, current->mems_allowed)) + return 1; + } + return 0; +} + +/* + * Is 'current' valid, and is zone z allowed in current->mems_allowed? + */ +int cpuset_zone_allowed(struct zone *z) +{ + return in_interrupt() || + node_isset(z->zone_pgdat->node_id, current->mems_allowed); +} + +/* + * proc_cpuset_show() + * - Print tasks cpuset path into seq_file. + * - Used for /proc//cpuset. + */ + +static int proc_cpuset_show(struct seq_file *m, void *v) +{ + struct cpuset *cs; + struct task_struct *tsk; + char *buf; + int retval = 0; + + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + tsk = m->private; + down(&cpuset_sem); + task_lock(tsk); + cs = tsk->cpuset; + task_unlock(tsk); + if (!cs) { + retval = -EINVAL; + goto out; + } + + retval = cpuset_path(cs, buf, PAGE_SIZE); + if (retval < 0) + goto out; + seq_puts(m, buf); + seq_putc(m, '\n'); +out: + up(&cpuset_sem); + kfree(buf); + return retval; +} + +static int cpuset_open(struct inode *inode, struct file *file) +{ + struct task_struct *tsk = PROC_I(inode)->task; + return single_open(file, proc_cpuset_show, tsk); +} + +struct file_operations proc_cpuset_operations = { + .open = cpuset_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +EXPORT_SYMBOL(proc_cpuset_operations); --- linux-2.6.9-rc1/kernel/exec_domain.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/kernel/exec_domain.c 2004-09-03 00:57:16.182579136 -0700 @@ -44,29 +44,7 @@ struct exec_domain default_exec_domain = static void default_handler(int segment, struct pt_regs *regp) { - u_long pers = 0; - - /* - * This may have been a static linked SVr4 binary, so we would - * have the personality set incorrectly. Or it might have been - * a Solaris/x86 binary. We can tell which because the former - * uses lcall7, while the latter used lcall 0x27. - * Try to find or load the appropriate personality, and fall back - * to just forcing a SEGV. - * - * XXX: this is IA32-specific and should be moved to the MD-tree. - */ - switch (segment) { -#ifdef __i386__ - case 0x07: - pers = abi_defhandler_lcall7; - break; - case 0x27: - pers = PER_SOLARIS; - break; -#endif - } - set_personality(pers); + set_personality(0); if (current_thread_info()->exec_domain->handler != default_handler) current_thread_info()->exec_domain->handler(segment, regp); @@ -228,100 +206,3 @@ sys_personality(u_long personality) EXPORT_SYMBOL(register_exec_domain); EXPORT_SYMBOL(unregister_exec_domain); EXPORT_SYMBOL(__set_personality); - -/* - * We have to have all sysctl handling for the Linux-ABI - * in one place as the dynamic registration of sysctls is - * horribly crufty in Linux <= 2.4. - * - * I hope the new sysctl schemes discussed for future versions - * will obsolete this. - * - * --hch - */ - -u_long abi_defhandler_coff = PER_SCOSVR3; -u_long abi_defhandler_elf = PER_LINUX; -u_long abi_defhandler_lcall7 = PER_SVR4; -u_long abi_defhandler_libcso = PER_SVR4; -u_int abi_traceflg; -int abi_fake_utsname; - -static struct ctl_table abi_table[] = { - { - .ctl_name = ABI_DEFHANDLER_COFF, - .procname = "defhandler_coff", - .data = &abi_defhandler_coff, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_doulongvec_minmax, - }, - { - .ctl_name = ABI_DEFHANDLER_ELF, - .procname = "defhandler_elf", - .data = &abi_defhandler_elf, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_doulongvec_minmax, - }, - { - .ctl_name = ABI_DEFHANDLER_LCALL7, - .procname = "defhandler_lcall7", - .data = &abi_defhandler_lcall7, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_doulongvec_minmax, - }, - { - .ctl_name = ABI_DEFHANDLER_LIBCSO, - .procname = "defhandler_libcso", - .data = &abi_defhandler_libcso, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_doulongvec_minmax, - }, - { - .ctl_name = ABI_TRACE, - .procname = "trace", - .data = &abi_traceflg, - .maxlen = sizeof(u_int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { - .ctl_name = ABI_FAKE_UTSNAME, - .procname = "fake_utsname", - .data = &abi_fake_utsname, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { .ctl_name = 0 } -}; - -static struct ctl_table abi_root_table[] = { - { - .ctl_name = CTL_ABI, - .procname = "abi", - .mode = 0555, - .child = abi_table, - }, - { .ctl_name = 0 } -}; - -static int __init -abi_register_sysctl(void) -{ - register_sysctl_table(abi_root_table, 1); - return 0; -} - -__initcall(abi_register_sysctl); - - -EXPORT_SYMBOL(abi_defhandler_coff); -EXPORT_SYMBOL(abi_defhandler_elf); -EXPORT_SYMBOL(abi_defhandler_lcall7); -EXPORT_SYMBOL(abi_defhandler_libcso); -EXPORT_SYMBOL(abi_traceflg); -EXPORT_SYMBOL(abi_fake_utsname); --- linux-2.6.9-rc1/kernel/exit.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/kernel/exit.c 2004-09-03 00:57:00.206007944 -0700 @@ -14,7 +14,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -23,6 +25,8 @@ #include #include #include +#include +#include #include #include @@ -56,8 +60,6 @@ void release_task(struct task_struct * p struct dentry *proc_dentry; repeat: - BUG_ON(p->state < TASK_ZOMBIE); - atomic_dec(&p->user->processes); spin_lock(&p->proc_lock); proc_dentry = proc_pid_unhash(p); @@ -90,12 +92,7 @@ repeat: zap_leader = (leader->exit_signal == -1); } - p->parent->cutime += p->utime + p->cutime; - p->parent->cstime += p->stime + p->cstime; - p->parent->cmin_flt += p->min_flt + p->cmin_flt; - p->parent->cmaj_flt += p->maj_flt + p->cmaj_flt; - p->parent->cnvcsw += p->nvcsw + p->cnvcsw; - p->parent->cnivcsw += p->nivcsw + p->cnivcsw; + perfctr_release_task(p); sched_exit(p); write_unlock_irq(&tasklist_lock); spin_unlock(&p->proc_lock); @@ -131,16 +128,15 @@ void unhash_process(struct task_struct * int session_of_pgrp(int pgrp) { struct task_struct *p; - struct list_head *l; - struct pid *pid; int sid = -1; read_lock(&tasklist_lock); - for_each_task_pid(pgrp, PIDTYPE_PGID, p, l, pid) + do_each_task_pid(pgrp, PIDTYPE_PGID, p) { if (p->signal->session > 0) { sid = p->signal->session; goto out; } + } while_each_task_pid(pgrp, PIDTYPE_PGID, p); p = find_task_by_pid(pgrp); if (p) sid = p->signal->session; @@ -161,11 +157,9 @@ out: static int will_become_orphaned_pgrp(int pgrp, task_t *ignored_task) { struct task_struct *p; - struct list_head *l; - struct pid *pid; int ret = 1; - for_each_task_pid(pgrp, PIDTYPE_PGID, p, l, pid) { + do_each_task_pid(pgrp, PIDTYPE_PGID, p) { if (p == ignored_task || p->state >= TASK_ZOMBIE || p->real_parent->pid == 1) @@ -175,7 +169,7 @@ static int will_become_orphaned_pgrp(int ret = 0; break; } - } + } while_each_task_pid(pgrp, PIDTYPE_PGID, p); return ret; /* (sighing) "Often!" */ } @@ -194,26 +188,13 @@ static inline int has_stopped_jobs(int p { int retval = 0; struct task_struct *p; - struct list_head *l; - struct pid *pid; - for_each_task_pid(pgrp, PIDTYPE_PGID, p, l, pid) { + do_each_task_pid(pgrp, PIDTYPE_PGID, p) { if (p->state != TASK_STOPPED) continue; - - /* If p is stopped by a debugger on a signal that won't - stop it, then don't count p as stopped. This isn't - perfect but it's a good approximation. */ - if (unlikely (p->ptrace) - && p->exit_code != SIGSTOP - && p->exit_code != SIGTSTP - && p->exit_code != SIGTTOU - && p->exit_code != SIGTTIN) - continue; - retval = 1; break; - } + } while_each_task_pid(pgrp, PIDTYPE_PGID, p); return retval; } @@ -529,10 +510,8 @@ static inline void choose_new_parent(tas * Make sure we're not reparenting to ourselves and that * the parent is not a zombie. */ - if (p == reaper || reaper->state >= TASK_ZOMBIE) - p->real_parent = child_reaper; - else - p->real_parent = reaper; + BUG_ON(p == reaper || reaper->state >= TASK_ZOMBIE); + p->real_parent = reaper; if (p->parent == p->real_parent) BUG(); } @@ -569,6 +548,14 @@ static inline void reparent_thread(task_ if (p->state == TASK_ZOMBIE && p->exit_signal != -1 && thread_group_empty(p)) do_notify_parent(p, p->exit_signal); + else if (p->state == TASK_TRACED) { + /* + * If it was at a trace stop, turn it into + * a normal stop since it's no longer being + * traced. + */ + p->state = TASK_STOPPED; + } } /* @@ -600,9 +587,13 @@ static inline void forget_original_paren struct task_struct *p, *reaper = father; struct list_head *_p, *_n; - reaper = father->group_leader; - if (reaper == father) - reaper = child_reaper; + do { + reaper = next_thread(reaper); + if (reaper == father) { + reaper = child_reaper; + break; + } + } while (reaper->state >= TASK_ZOMBIE); /* * There are only two places where our children can be: @@ -756,7 +747,6 @@ static void exit_notify(struct task_stru if (tsk->exit_signal == -1 && tsk->ptrace == 0) state = TASK_DEAD; tsk->state = state; - tsk->flags |= PF_DEAD; /* * Clear these here so that update_process_times() won't try to deliver @@ -766,20 +756,7 @@ static void exit_notify(struct task_stru tsk->it_prof_value = 0; tsk->rlim[RLIMIT_CPU].rlim_cur = RLIM_INFINITY; - /* - * In the preemption case it must be impossible for the task - * to get runnable again, so use "_raw_" unlock to keep - * preempt_count elevated until we schedule(). - * - * To avoid deadlock on SMP, interrupts must be unmasked. If we - * don't, subsequently called functions (e.g, wait_task_inactive() - * via release_task()) will spin, with interrupt flags - * unwittingly blocked, until the other task sleeps. That task - * may itself be waiting for smp_call_function() to answer and - * complete, and with interrupts blocked that will never happen. - */ - _raw_write_unlock(&tasklist_lock); - local_irq_enable(); + write_unlock_irq(&tasklist_lock); list_for_each_safe(_p, _n, &ptrace_dead) { list_del_init(_p); @@ -791,12 +768,17 @@ static void exit_notify(struct task_stru if (state == TASK_DEAD) release_task(tsk); + /* PF_DEAD causes final put_task_struct after we schedule. */ + preempt_disable(); + tsk->flags |= PF_DEAD; } asmlinkage NORET_TYPE void do_exit(long code) { struct task_struct *tsk = current; + profile_task_exit(tsk); + if (unlikely(in_interrupt())) panic("Aiee, killing interrupt handler!"); if (unlikely(!tsk->pid)) @@ -813,8 +795,6 @@ asmlinkage NORET_TYPE void do_exit(long current->comm, current->pid, preempt_count()); - profile_exit_task(tsk); - if (unlikely(current->ptrace & PT_TRACE_EXIT)) { current->ptrace_message = code; ptrace_notify((PTRACE_EVENT_EXIT << 8) | SIGTRAP); @@ -828,6 +808,8 @@ asmlinkage NORET_TYPE void do_exit(long __exit_fs(tsk); exit_namespace(tsk); exit_thread(); + cpuset_exit(tsk); + exit_keys(tsk); if (tsk->signal->leader) disassociate_ctty(1); @@ -865,9 +847,6 @@ asmlinkage long sys_exit(int error_code) task_t fastcall *next_thread(const task_t *p) { - const struct pid_link *link = p->pids + PIDTYPE_TGID; - const struct list_head *tmp, *head = &link->pidptr->task_list; - #ifdef CONFIG_SMP if (!p->sighand) BUG(); @@ -875,11 +854,7 @@ task_t fastcall *next_thread(const task_ !rwlock_is_locked(&tasklist_lock)) BUG(); #endif - tmp = link->pid_chain.next; - if (tmp == head) - tmp = head->next; - - return pid_task(tmp, PIDTYPE_TGID); + return pid_task(p->pids[PIDTYPE_TGID].pid_list.next, PIDTYPE_TGID); } EXPORT_SYMBOL(next_thread); @@ -967,16 +942,64 @@ static int eligible_child(pid_t pid, int return 1; } +static int wait_noreap_copyout(task_t *p, pid_t pid, uid_t uid, + int why, int status, + struct siginfo __user *infop) +{ + int retval = getrusage(p, RUSAGE_BOTH, &infop->si_rusage); + put_task_struct(p); + if (!retval) + retval = put_user(SIGCHLD, &infop->si_signo); + if (!retval) + retval = put_user(0, &infop->si_errno); + if (!retval) + retval = put_user((short)why, &infop->si_code); + if (!retval) + retval = put_user(pid, &infop->si_pid); + if (!retval) + retval = put_user(uid, &infop->si_uid); + if (!retval) + retval = put_user(status, &infop->si_status); + if (!retval) + retval = pid; + return retval; +} + /* * Handle sys_wait4 work for one task in state TASK_ZOMBIE. We hold * read_lock(&tasklist_lock) on entry. If we return zero, we still hold * the lock and this task is uninteresting. If we return nonzero, we have * released the lock and the system call should return. */ -static int wait_task_zombie(task_t *p, unsigned int __user *stat_addr, struct rusage __user *ru) +static int wait_task_zombie(task_t *p, int noreap, + struct siginfo __user *infop, + int __user *stat_addr, struct rusage __user *ru) { unsigned long state; int retval; + int status; + + if (unlikely(noreap)) { + pid_t pid = p->pid; + uid_t uid = p->uid; + int exit_code = p->exit_code; + int why, status; + + if (unlikely(p->state != TASK_ZOMBIE)) + return 0; + if (unlikely(p->exit_signal == -1 && p->ptrace == 0)) + return 0; + get_task_struct(p); + read_unlock(&tasklist_lock); + if ((exit_code & 0x7f) == 0) { + why = CLD_EXITED; + status = exit_code >> 8; + } else { + why = (exit_code & 0x80) ? CLD_DUMPED : CLD_KILLED; + status = exit_code & 0x7f; + } + return wait_noreap_copyout(p, pid, uid, why, status, infop); + } /* * Try to move the task's state to DEAD @@ -987,12 +1010,45 @@ static int wait_task_zombie(task_t *p, u BUG_ON(state != TASK_DEAD); return 0; } - if (unlikely(p->exit_signal == -1 && p->ptrace == 0)) + if (unlikely(p->exit_signal == -1 && p->ptrace == 0)) { /* * This can only happen in a race with a ptraced thread * dying on another processor. */ return 0; + } + + if (likely(p->real_parent == p->parent) && likely(p->signal)) { + /* + * The resource counters for the group leader are in its + * own task_struct. Those for dead threads in the group + * are in its signal_struct, as are those for the child + * processes it has previously reaped. All these + * accumulate in the parent's signal_struct c* fields. + * + * We don't bother to take a lock here to protect these + * p->signal fields, because they are only touched by + * __exit_signal, which runs with tasklist_lock + * write-locked anyway, and so is excluded here. We do + * need to protect the access to p->parent->signal fields, + * as other threads in the parent group can be right + * here reaping other children at the same time. + */ + spin_lock_irq(&p->parent->sighand->siglock); + p->parent->signal->cutime += + p->utime + p->signal->utime + p->signal->cutime; + p->parent->signal->cstime += + p->stime + p->signal->stime + p->signal->cstime; + p->parent->signal->cmin_flt += + p->min_flt + p->signal->min_flt + p->signal->cmin_flt; + p->parent->signal->cmaj_flt += + p->maj_flt + p->signal->maj_flt + p->signal->cmaj_flt; + p->parent->signal->cnvcsw += + p->nvcsw + p->signal->nvcsw + p->signal->cnvcsw; + p->parent->signal->cnivcsw += + p->nivcsw + p->signal->nivcsw + p->signal->cnivcsw; + spin_unlock_irq(&p->parent->sighand->siglock); + } /* * Now we are sure this task is interesting, and no other @@ -1001,12 +1057,32 @@ static int wait_task_zombie(task_t *p, u read_unlock(&tasklist_lock); retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0; - if (!retval && stat_addr) { - if (p->signal->group_exit) - retval = put_user(p->signal->group_exit_code, stat_addr); - else - retval = put_user(p->exit_code, stat_addr); - } + status = p->signal->group_exit + ? p->signal->group_exit_code : p->exit_code; + if (!retval && stat_addr) + retval = put_user(status, stat_addr); + if (!retval && infop) + retval = put_user(SIGCHLD, &infop->si_signo); + if (!retval && infop) + retval = put_user(0, &infop->si_errno); + if (!retval && infop) { + int why; + + if ((status & 0x7f) == 0) { + why = CLD_EXITED; + status >>= 8; + } else { + why = (status & 0x80) ? CLD_DUMPED : CLD_KILLED; + status &= 0x7f; + } + retval = put_user((short)why, &infop->si_code); + if (!retval) + retval = put_user(status, &infop->si_status); + } + if (!retval && infop) + retval = put_user(p->pid, &infop->si_pid); + if (!retval && infop) + retval = put_user(p->uid, &infop->si_uid); if (retval) { p->state = TASK_ZOMBIE; return retval; @@ -1019,8 +1095,9 @@ static int wait_task_zombie(task_t *p, u __ptrace_unlink(p); p->state = TASK_ZOMBIE; /* - * If this is not a detached task, notify the parent. If it's - * still not detached after that, don't release it now. + * If this is not a detached task, notify the parent. + * If it's still not detached after that, don't release + * it now. */ if (p->exit_signal != -1) { do_notify_parent(p, p->exit_signal); @@ -1042,9 +1119,9 @@ static int wait_task_zombie(task_t *p, u * the lock and this task is uninteresting. If we return nonzero, we have * released the lock and the system call should return. */ -static int wait_task_stopped(task_t *p, int delayed_group_leader, - unsigned int __user *stat_addr, - struct rusage __user *ru) +static int wait_task_stopped(task_t *p, int delayed_group_leader, int noreap, + struct siginfo __user *infop, + int __user *stat_addr, struct rusage __user *ru) { int retval, exit_code; @@ -1067,6 +1144,21 @@ static int wait_task_stopped(task_t *p, */ get_task_struct(p); read_unlock(&tasklist_lock); + + if (unlikely(noreap)) { + pid_t pid = p->pid; + uid_t uid = p->uid; + int why = (p->ptrace & PT_PTRACED) ? CLD_TRAPPED : CLD_STOPPED; + + exit_code = p->exit_code; + if (unlikely(!exit_code) || + unlikely(p->state > TASK_STOPPED)) + goto bail_ref; + return wait_noreap_copyout(p, pid, uid, + why, (exit_code << 8) | 0x7f, + infop); + } + write_lock_irq(&tasklist_lock); /* @@ -1075,7 +1167,7 @@ static int wait_task_stopped(task_t *p, * race with the TASK_ZOMBIE case. */ exit_code = xchg(&p->exit_code, 0); - if (unlikely(p->state > TASK_STOPPED)) { + if (unlikely(p->state >= TASK_ZOMBIE)) { /* * The task resumed and then died. Let the next iteration * catch it in TASK_ZOMBIE. Note that exit_code might @@ -1092,6 +1184,7 @@ static int wait_task_stopped(task_t *p, * resumed, or it resumed and then died. */ write_unlock_irq(&tasklist_lock); +bail_ref: put_task_struct(p); read_lock(&tasklist_lock); return 0; @@ -1106,6 +1199,20 @@ static int wait_task_stopped(task_t *p, retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0; if (!retval && stat_addr) retval = put_user((exit_code << 8) | 0x7f, stat_addr); + if (!retval && infop) + retval = put_user(SIGCHLD, &infop->si_signo); + if (!retval && infop) + retval = put_user(0, &infop->si_errno); + if (!retval && infop) + retval = put_user((short)((p->ptrace & PT_PTRACED) + ? CLD_TRAPPED : CLD_STOPPED), + &infop->si_code); + if (!retval && infop) + retval = put_user(exit_code, &infop->si_status); + if (!retval && infop) + retval = put_user(p->pid, &infop->si_pid); + if (!retval && infop) + retval = put_user(p->uid, &infop->si_uid); if (!retval) retval = p->pid; put_task_struct(p); @@ -1114,15 +1221,13 @@ static int wait_task_stopped(task_t *p, return retval; } -asmlinkage long sys_wait4(pid_t pid,unsigned int __user *stat_addr, int options, struct rusage __user *ru) +static long do_wait(pid_t pid, int options, struct siginfo __user *infop, + int __user *stat_addr, struct rusage __user *ru) { DECLARE_WAITQUEUE(wait, current); struct task_struct *tsk; int flag, retval; - if (options & ~(WNOHANG|WUNTRACED|__WNOTHREAD|__WCLONE|__WALL)) - return -EINVAL; - add_wait_queue(¤t->wait_chldexit,&wait); repeat: flag = 0; @@ -1143,30 +1248,69 @@ repeat: flag = 1; switch (p->state) { + case TASK_TRACED: + if (!(p->ptrace & PT_PTRACED)) + continue; + /*FALLTHROUGH*/ case TASK_STOPPED: if (!(options & WUNTRACED) && !(p->ptrace & PT_PTRACED)) continue; retval = wait_task_stopped(p, ret == 2, + (options & WNOWAIT), + infop, stat_addr, ru); if (retval != 0) /* He released the lock. */ - goto end_wait4; + goto end; break; case TASK_ZOMBIE: /* * Eligible but we cannot release it yet: */ if (ret == 2) + goto check_continued; + if (!likely(options & WEXITED)) continue; - retval = wait_task_zombie(p, stat_addr, ru); + retval = wait_task_zombie( + p, (options & WNOWAIT), + infop, stat_addr, ru); if (retval != 0) /* He released the lock. */ - goto end_wait4; + goto end; + break; + case TASK_DEAD: + continue; + default: +check_continued: + if (!unlikely(options & WCONTINUED)) + continue; + if (unlikely(!p->signal)) + continue; + spin_lock_irq(&p->sighand->siglock); + if (p->signal->stop_state < 0) { + pid_t pid; + uid_t uid; + + if (!(options & WNOWAIT)) + p->signal->stop_state = 0; + spin_unlock_irq(&p->sighand->siglock); + pid = p->pid; + uid = p->uid; + get_task_struct(p); + read_unlock(&tasklist_lock); + retval = wait_noreap_copyout(p, pid, + uid, CLD_CONTINUED, + SIGCONT, infop); + BUG_ON(retval == 0); + goto end; + } + spin_unlock_irq(&p->sighand->siglock); break; } } if (!flag) { - list_for_each (_p,&tsk->ptrace_children) { - p = list_entry(_p,struct task_struct,ptrace_list); + list_for_each(_p, &tsk->ptrace_children) { + p = list_entry(_p, struct task_struct, + ptrace_list); if (!eligible_child(pid, options, p)) continue; flag = 1; @@ -1179,24 +1323,84 @@ repeat: if (tsk->signal != current->signal) BUG(); } while (tsk != current); + read_unlock(&tasklist_lock); if (flag) { retval = 0; if (options & WNOHANG) - goto end_wait4; + goto end; retval = -ERESTARTSYS; if (signal_pending(current)) - goto end_wait4; + goto end; schedule(); goto repeat; } retval = -ECHILD; -end_wait4: +end: current->state = TASK_RUNNING; remove_wait_queue(¤t->wait_chldexit,&wait); + if (infop) { + if (retval > 0) + retval = 0; + else { + /* + * For a WNOHANG return, clear out all the fields + * we would set so the user can easily tell the + * difference. + */ + if (!retval) + retval = put_user(0, &infop->si_signo); + if (!retval) + retval = put_user(0, &infop->si_errno); + if (!retval) + retval = put_user(0, &infop->si_code); + if (!retval) + retval = put_user(0, &infop->si_pid); + if (!retval) + retval = put_user(0, &infop->si_uid); + if (!retval) + retval = put_user(0, &infop->si_status); + } + } return retval; } +asmlinkage long sys_waitid(int which, pid_t pid, + struct siginfo __user *infop, int options) +{ + if (options & ~(WNOHANG|WNOWAIT|WEXITED|WSTOPPED|WCONTINUED)) + return -EINVAL; + if (!(options & (WEXITED|WSTOPPED|WCONTINUED))) + return -EINVAL; + + switch (which) { + case P_ALL: + pid = -1; + break; + case P_PID: + if (pid <= 0) + return -EINVAL; + break; + case P_PGID: + if (pid <= 0) + return -EINVAL; + pid = -pid; + break; + default: + return -EINVAL; + } + + return do_wait(pid, options, infop, NULL, &infop->si_rusage); +} + +asmlinkage long sys_wait4(pid_t pid, unsigned int __user *stat_addr, + int options, struct rusage __user *ru) +{ + if (options & ~(WNOHANG|WUNTRACED|__WNOTHREAD|__WCLONE|__WALL)) + return -EINVAL; + return do_wait(pid, options | WEXITED, NULL, stat_addr, ru); +} + #ifdef __ARCH_WANT_SYS_WAITPID /* --- linux-2.6.9-rc1/kernel/fork.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/kernel/fork.c 2004-09-03 00:57:09.933529136 -0700 @@ -24,10 +24,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -36,6 +38,7 @@ #include #include #include +#include #include #include @@ -76,11 +79,12 @@ int nr_processes(void) static kmem_cache_t *task_struct_cachep; #endif -static void free_task(struct task_struct *tsk) +void free_task(struct task_struct *tsk) { free_thread_info(tsk->thread_info); free_task_struct(tsk); } +EXPORT_SYMBOL(free_task); void __put_task_struct(struct task_struct *tsk) { @@ -93,124 +97,11 @@ void __put_task_struct(struct task_struc security_task_free(tsk); free_uid(tsk->user); put_group_info(tsk->group_info); - free_task(tsk); -} - -void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t * wait) -{ - unsigned long flags; - - wait->flags &= ~WQ_FLAG_EXCLUSIVE; - spin_lock_irqsave(&q->lock, flags); - __add_wait_queue(q, wait); - spin_unlock_irqrestore(&q->lock, flags); -} - -EXPORT_SYMBOL(add_wait_queue); - -void fastcall add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t * wait) -{ - unsigned long flags; - - wait->flags |= WQ_FLAG_EXCLUSIVE; - spin_lock_irqsave(&q->lock, flags); - __add_wait_queue_tail(q, wait); - spin_unlock_irqrestore(&q->lock, flags); -} - -EXPORT_SYMBOL(add_wait_queue_exclusive); - -void fastcall remove_wait_queue(wait_queue_head_t *q, wait_queue_t * wait) -{ - unsigned long flags; - - spin_lock_irqsave(&q->lock, flags); - __remove_wait_queue(q, wait); - spin_unlock_irqrestore(&q->lock, flags); -} - -EXPORT_SYMBOL(remove_wait_queue); - - -/* - * Note: we use "set_current_state()" _after_ the wait-queue add, - * because we need a memory barrier there on SMP, so that any - * wake-function that tests for the wait-queue being active - * will be guaranteed to see waitqueue addition _or_ subsequent - * tests in this thread will see the wakeup having taken place. - * - * The spin_unlock() itself is semi-permeable and only protects - * one way (it only protects stuff inside the critical region and - * stops them from bleeding out - it would still allow subsequent - * loads to move into the the critical region). - */ -void fastcall prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state) -{ - unsigned long flags; - wait->flags &= ~WQ_FLAG_EXCLUSIVE; - spin_lock_irqsave(&q->lock, flags); - if (list_empty(&wait->task_list)) - __add_wait_queue(q, wait); - set_current_state(state); - spin_unlock_irqrestore(&q->lock, flags); + if (!profile_handoff_task(tsk)) + free_task(tsk); } -EXPORT_SYMBOL(prepare_to_wait); - -void fastcall -prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *wait, int state) -{ - unsigned long flags; - - wait->flags |= WQ_FLAG_EXCLUSIVE; - spin_lock_irqsave(&q->lock, flags); - if (list_empty(&wait->task_list)) - __add_wait_queue_tail(q, wait); - set_current_state(state); - spin_unlock_irqrestore(&q->lock, flags); -} - -EXPORT_SYMBOL(prepare_to_wait_exclusive); - -void fastcall finish_wait(wait_queue_head_t *q, wait_queue_t *wait) -{ - unsigned long flags; - - __set_current_state(TASK_RUNNING); - /* - * We can check for list emptiness outside the lock - * IFF: - * - we use the "careful" check that verifies both - * the next and prev pointers, so that there cannot - * be any half-pending updates in progress on other - * CPU's that we haven't seen yet (and that might - * still change the stack area. - * and - * - all other users take the lock (ie we can only - * have _one_ other CPU that looks at or modifies - * the list). - */ - if (!list_empty_careful(&wait->task_list)) { - spin_lock_irqsave(&q->lock, flags); - list_del_init(&wait->task_list); - spin_unlock_irqrestore(&q->lock, flags); - } -} - -EXPORT_SYMBOL(finish_wait); - -int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key) -{ - int ret = default_wake_function(wait, mode, sync, key); - - if (ret) - list_del_init(&wait->task_list); - return ret; -} - -EXPORT_SYMBOL(autoremove_wake_function); - void __init fork_init(unsigned long mempages) { #ifndef __HAVE_ARCH_TASK_STRUCT_ALLOCATOR @@ -261,6 +152,10 @@ static struct task_struct *dup_task_stru tsk->thread_info = ti; ti->task = tsk; + /* initialize list of pages privately reserved by process */ + INIT_LIST_HEAD(&tsk->private_pages); + tsk->private_pages_count = 0; + /* One for us, one for whoever does the "release_task()" (usually parent) */ atomic_set(&tsk->usage,2); return tsk; @@ -280,7 +175,7 @@ static inline int dup_mmap(struct mm_str mm->locked_vm = 0; mm->mmap = NULL; mm->mmap_cache = NULL; - mm->free_area_cache = TASK_UNMAPPED_BASE; + mm->free_area_cache = oldmm->mmap_base; mm->map_count = 0; mm->rss = 0; cpus_clear(mm->cpu_vm_mask); @@ -303,8 +198,11 @@ static inline int dup_mmap(struct mm_str for (mpnt = current->mm->mmap ; mpnt ; mpnt = mpnt->vm_next) { struct file *file; - if(mpnt->vm_flags & VM_DONTCOPY) + if (mpnt->vm_flags & VM_DONTCOPY) { + __vm_stat_account(mm, mpnt->vm_flags, mpnt->vm_file, + -vma_pages(mpnt)); continue; + } charge = 0; if (mpnt->vm_flags & VM_ACCOUNT) { unsigned int len = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT; @@ -467,20 +365,34 @@ void mmput(struct mm_struct *mm) } } -/* - * Checks if the use count of an mm is non-zero and if so - * returns a reference to it after bumping up the use count. - * If the use count is zero, it means this mm is going away, - * so return NULL. +/** + * get_task_mm - acquire a reference to the task's mm + * + * Returns %NULL if the task has no mm. Checks if the use count + * of the mm is non-zero and if so returns a reference to it, after + * bumping up the use count. User must release the mm via mmput() + * after use. Typically used by /proc and ptrace. + * + * If the use count is zero, it means that this mm is going away, + * so return %NULL. This only happens in the case of an AIO daemon + * which has temporarily adopted an mm (see use_mm), in the course + * of its final mmput, before exit_aio has completed. */ -struct mm_struct *mmgrab(struct mm_struct *mm) +struct mm_struct *get_task_mm(struct task_struct *task) { - spin_lock(&mmlist_lock); - if (!atomic_read(&mm->mm_users)) - mm = NULL; - else - atomic_inc(&mm->mm_users); - spin_unlock(&mmlist_lock); + struct mm_struct *mm; + + task_lock(task); + mm = task->mm; + if (mm) { + spin_lock(&mmlist_lock); + if (!atomic_read(&mm->mm_users)) + mm = NULL; + else + atomic_inc(&mm->mm_users); + spin_unlock(&mmlist_lock); + } + task_unlock(task); return mm; } @@ -528,8 +440,7 @@ static int copy_mm(unsigned long clone_f int retval; tsk->min_flt = tsk->maj_flt = 0; - tsk->cmin_flt = tsk->cmaj_flt = 0; - tsk->nvcsw = tsk->nivcsw = tsk->cnvcsw = tsk->cnivcsw = 0; + tsk->nvcsw = tsk->nivcsw = 0; tsk->mm = NULL; tsk->active_mm = NULL; @@ -836,6 +747,10 @@ static inline int copy_signal(unsigned l sig->leader = 0; /* session leadership doesn't inherit */ sig->tty_old_pgrp = 0; + sig->utime = sig->stime = sig->cutime = sig->cstime = 0; + sig->nvcsw = sig->nivcsw = sig->cnvcsw = sig->cnivcsw = 0; + sig->min_flt = sig->maj_flt = sig->cmin_flt = sig->cmaj_flt = 0; + return 0; } @@ -865,12 +780,13 @@ asmlinkage long sys_set_tid_address(int * parts of the process environment (as per the clone * flags). The actual kick-off is left to the caller. */ -struct task_struct *copy_process(unsigned long clone_flags, +static task_t *copy_process(unsigned long clone_flags, unsigned long stack_start, struct pt_regs *regs, unsigned long stack_size, int __user *parent_tidptr, - int __user *child_tidptr) + int __user *child_tidptr, + int pid) { int retval; struct task_struct *p = NULL; @@ -930,13 +846,7 @@ struct task_struct *copy_process(unsigne p->did_exec = 0; copy_flags(clone_flags, p); - if (clone_flags & CLONE_IDLETASK) - p->pid = 0; - else { - p->pid = alloc_pidmap(); - if (p->pid == -1) - goto bad_fork_cleanup; - } + p->pid = pid; retval = -EFAULT; if (clone_flags & CLONE_PARENT_SETTID) if (put_user(p->pid, parent_tidptr)) @@ -960,11 +870,11 @@ struct task_struct *copy_process(unsigne p->real_timer.data = (unsigned long) p; p->utime = p->stime = 0; - p->cutime = p->cstime = 0; p->lock_depth = -1; /* -1 = no lock */ - p->start_time = get_jiffies_64(); + do_posix_clock_monotonic_gettime(&p->start_time); p->security = NULL; p->io_context = NULL; + p->io_wait = NULL; p->audit_context = NULL; #ifdef CONFIG_NUMA p->mempolicy = mpol_copy(p->mempolicy); @@ -975,6 +885,10 @@ struct task_struct *copy_process(unsigne } #endif + p->tgid = p->pid; + if (clone_flags & CLONE_THREAD) + p->tgid = current->tgid; + if ((retval = security_task_alloc(p))) goto bad_fork_cleanup_policy; if ((retval = audit_alloc(p))) @@ -992,8 +906,10 @@ struct task_struct *copy_process(unsigne goto bad_fork_cleanup_sighand; if ((retval = copy_mm(clone_flags, p))) goto bad_fork_cleanup_signal; - if ((retval = copy_namespace(clone_flags, p))) + if ((retval = copy_keys(clone_flags, p))) goto bad_fork_cleanup_mm; + if ((retval = copy_namespace(clone_flags, p))) + goto bad_fork_cleanup_keys; retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs); if (retval) goto bad_fork_cleanup_namespace; @@ -1026,13 +942,23 @@ struct task_struct *copy_process(unsigne * Ok, make it visible to the rest of the system. * We dont wake it up yet. */ - p->tgid = p->pid; p->group_leader = p; INIT_LIST_HEAD(&p->ptrace_children); INIT_LIST_HEAD(&p->ptrace_list); /* Need tasklist lock for parent etc handling! */ write_lock_irq(&tasklist_lock); + + /* + * The task hasn't been attached yet, so cpus_allowed mask cannot + * have changed. The cpus_allowed mask of the parent may have + * changed after it was copied first time, and it may then move to + * another CPU - so we re-copy it here and set the child's CPU to + * the parent's CPU. This avoids alot of nasty races. + */ + p->cpus_allowed = current->cpus_allowed; + set_task_cpu(p, smp_processor_id()); + /* * Check for pending SIGKILL! The new thread should not be allowed * to slip out of an OOM kill. (or normal SIGKILL.) @@ -1044,7 +970,7 @@ struct task_struct *copy_process(unsigne } /* CLONE_PARENT re-uses the old parent */ - if (clone_flags & CLONE_PARENT) + if (clone_flags & (CLONE_PARENT|CLONE_THREAD)) p->real_parent = current->real_parent; else p->real_parent = current; @@ -1063,7 +989,6 @@ struct task_struct *copy_process(unsigne retval = -EAGAIN; goto bad_fork_cleanup_namespace; } - p->tgid = current->tgid; p->group_leader = current->group_leader; if (current->signal->group_stop_count > 0) { @@ -1080,18 +1005,19 @@ struct task_struct *copy_process(unsigne } SET_LINKS(p); - if (p->ptrace & PT_PTRACED) + if (unlikely(p->ptrace & PT_PTRACED)) __ptrace_link(p, current->parent); + cpuset_fork(p); + attach_pid(p, PIDTYPE_PID, p->pid); + attach_pid(p, PIDTYPE_TGID, p->tgid); if (thread_group_leader(p)) { - attach_pid(p, PIDTYPE_TGID, p->tgid); attach_pid(p, PIDTYPE_PGID, process_group(p)); attach_pid(p, PIDTYPE_SID, p->signal->session); if (p->pid) __get_cpu_var(process_counts)++; - } else - link_pid(p, p->pids + PIDTYPE_TGID, &p->group_leader->pids[PIDTYPE_TGID].pid); + } nr_threads++; write_unlock_irq(&tasklist_lock); @@ -1104,6 +1030,8 @@ fork_out: bad_fork_cleanup_namespace: exit_namespace(p); +bad_fork_cleanup_keys: + exit_keys(p); bad_fork_cleanup_mm: exit_mm(p); if (p->active_mm) @@ -1127,8 +1055,6 @@ bad_fork_cleanup_policy: mpol_free(p->mempolicy); #endif bad_fork_cleanup: - if (p->pid > 0) - free_pidmap(p->pid); if (p->binfmt) module_put(p->binfmt->module); bad_fork_cleanup_put_domain: @@ -1142,9 +1068,28 @@ bad_fork_free: goto fork_out; } +struct pt_regs * __init __attribute__((weak)) idle_regs(struct pt_regs *regs) +{ + memset(regs, 0, sizeof(struct pt_regs)); + return regs; +} + +task_t * __init fork_idle(int cpu) +{ + task_t *task; + struct pt_regs regs; + + task = copy_process(CLONE_VM, 0, idle_regs(®s), 0, NULL, NULL, 0); + if (!task) + return ERR_PTR(-ENOMEM); + init_idle(task, cpu); + unhash_process(task); + return task; +} + static inline int fork_traceflag (unsigned clone_flags) { - if (clone_flags & (CLONE_UNTRACED | CLONE_IDLETASK)) + if (clone_flags & CLONE_UNTRACED) return 0; else if (clone_flags & CLONE_VFORK) { if (current->ptrace & PT_TRACE_VFORK) @@ -1173,21 +1118,21 @@ long do_fork(unsigned long clone_flags, { struct task_struct *p; int trace = 0; - long pid; + long pid = alloc_pidmap(); + if (pid < 0) + return -EAGAIN; if (unlikely(current->ptrace)) { trace = fork_traceflag (clone_flags); if (trace) clone_flags |= CLONE_PTRACE; } - p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr); + p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr, pid); /* * Do this prior waking up the new thread - the thread pointer * might get invalid after that point, if the thread exits quickly. */ - pid = IS_ERR(p) ? PTR_ERR(p) : p->pid; - if (!IS_ERR(p)) { struct completion vfork; @@ -1204,31 +1149,10 @@ long do_fork(unsigned long clone_flags, set_tsk_thread_flag(p, TIF_SIGPENDING); } - if (!(clone_flags & CLONE_STOPPED)) { - /* - * Do the wakeup last. On SMP we treat fork() and - * CLONE_VM separately, because fork() has already - * created cache footprint on this CPU (due to - * copying the pagetables), hence migration would - * probably be costy. Threads on the other hand - * have less traction to the current CPU, and if - * there's an imbalance then the scheduler can - * migrate this fresh thread now, before it - * accumulates a larger cache footprint: - */ - if (clone_flags & CLONE_VM) - wake_up_forked_thread(p); - else - wake_up_forked_process(p); - } else { - int cpu = get_cpu(); - + if (!(clone_flags & CLONE_STOPPED)) + wake_up_new_task(p, clone_flags); + else p->state = TASK_STOPPED; - if (cpu_is_offline(task_cpu(p))) - set_task_cpu(p, cpu); - - put_cpu(); - } ++total_forks; if (unlikely (trace)) { @@ -1240,12 +1164,10 @@ long do_fork(unsigned long clone_flags, wait_for_completion(&vfork); if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE)) ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP); - } else - /* - * Let the child process run first, to avoid most of the - * COW overhead when the child exec()s afterwards. - */ - set_need_resched(); + } + } else { + free_pidmap(pid); + pid = PTR_ERR(p); } return pid; } --- linux-2.6.9-rc1/kernel/kallsyms.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/kernel/kallsyms.c 2004-09-03 00:57:08.715714272 -0700 @@ -4,7 +4,12 @@ * Rewritten and vastly simplified by Rusty Russell for in-kernel * module loader: * Copyright 2002 Rusty Russell IBM Corporation - * Stem compression by Andi Kleen. + * + * ChangeLog: + * + * (25/Aug/2004) Paulo Marques + * Changed the compression method from stem compression to "table lookup" + * compression (see scripts/kallsyms.c for a more complete description) */ #include #include @@ -17,7 +22,12 @@ /* These will be re-linked against their real values during the second link stage */ extern unsigned long kallsyms_addresses[] __attribute__((weak)); extern unsigned long kallsyms_num_syms __attribute__((weak)); -extern char kallsyms_names[] __attribute__((weak)); +extern u8 kallsyms_names[] __attribute__((weak)); + +extern u8 kallsyms_token_table[] __attribute__((weak)); +extern u16 kallsyms_token_index[] __attribute__((weak)); + +extern unsigned long kallsyms_markers[] __attribute__((weak)); /* Defined by the linker script. */ extern char _stext[], _etext[], _sinittext[], _einittext[]; @@ -37,21 +47,88 @@ static inline int is_kernel_text(unsigne return 0; } +/* expand a compressed symbol data into the resulting uncompressed string, + given the offset to where the symbol is in the compressed stream */ +static unsigned int kallsyms_expand_symbol(unsigned int off, char *result) +{ + int len, skipped_first = 0; + u8 *tptr, *data; + + /* get the compressed symbol length from the first symbol byte */ + data = &kallsyms_names[off]; + len = *data; + data++; + + /* update the offset to return the offset for the next symbol on + * the compressed stream */ + off += len + 1; + + /* for every byte on the compressed symbol data, copy the table + entry for that byte */ + while(len) { + tptr = &kallsyms_token_table[ kallsyms_token_index[*data] ]; + data++; + len--; + + while (*tptr) { + if(skipped_first) { + *result = *tptr; + result++; + } else + skipped_first = 1; + tptr++; + } + } + + *result = '\0'; + + /* return to offset to the next symbol */ + return off; +} + +/* get symbol type information. This is encoded as a single char at the + * begining of the symbol name */ +static char kallsyms_get_symbol_type(unsigned int off) +{ + /* get just the first code, look it up in the token table, and return the + * first char from this token */ + return kallsyms_token_table[ kallsyms_token_index[ kallsyms_names[off+1] ] ]; +} + + +/* find the offset on the compressed stream given and index in the + * kallsyms array */ +static unsigned int get_symbol_offset(unsigned long pos) +{ + u8 *name; + int i; + + /* use the closest marker we have. We have markers every 256 positions, + * so that should be close enough */ + name = &kallsyms_names[ kallsyms_markers[pos>>8] ]; + + /* sequentially scan all the symbols up to the point we're searching for. + * Every symbol is stored in a [][ bytes of data] format, so we + * just need to add the len to the current pointer for every symbol we + * wish to skip */ + for(i = 0; i < (pos&0xFF); i++) + name = name + (*name) + 1; + + return name - kallsyms_names; +} + /* Lookup the address for this symbol. Returns 0 if not found. */ unsigned long kallsyms_lookup_name(const char *name) { char namebuf[KSYM_NAME_LEN+1]; unsigned long i; - char *knames; + unsigned int off; - for (i = 0, knames = kallsyms_names; i < kallsyms_num_syms; i++) { - unsigned prefix = *knames++; + for (i = 0, off = 0; i < kallsyms_num_syms; i++) { + off = kallsyms_expand_symbol(off, namebuf); - strlcpy(namebuf + prefix, knames, KSYM_NAME_LEN - prefix); if (strcmp(namebuf, name) == 0) return kallsyms_addresses[i]; - - knames += strlen(knames) + 1; } return module_kallsyms_lookup_name(name); } @@ -62,7 +139,7 @@ const char *kallsyms_lookup(unsigned lon unsigned long *offset, char **modname, char *namebuf) { - unsigned long i, best = 0; + unsigned long i, low, high, mid; /* This kernel should never had been booted. */ BUG_ON(!kallsyms_addresses); @@ -71,40 +148,45 @@ const char *kallsyms_lookup(unsigned lon namebuf[0] = 0; if (is_kernel_text(addr) || is_kernel_inittext(addr)) { - unsigned long symbol_end; - char *name = kallsyms_names; + unsigned long symbol_end=0; - /* They're sorted, we could be clever here, but who cares? */ - for (i = 0; i < kallsyms_num_syms; i++) { - if (kallsyms_addresses[i] > kallsyms_addresses[best] && - kallsyms_addresses[i] <= addr) - best = i; + /* do a binary search on the sorted kallsyms_addresses array */ + low = 0; + high = kallsyms_num_syms; + + while (high-low > 1) { + mid = (low + high) / 2; + if (kallsyms_addresses[mid] <= addr) low = mid; + else high = mid; } - /* Grab name */ - for (i = 0; i <= best; i++) { - unsigned prefix = *name++; - strncpy(namebuf + prefix, name, KSYM_NAME_LEN - prefix); - name += strlen(name) + 1; - } + /* search for the first aliased symbol. Aliased symbols are + symbols with the same address */ + while (low && kallsyms_addresses[low - 1] == kallsyms_addresses[low]) + --low; - /* At worst, symbol ends at end of section. */ - if (is_kernel_inittext(addr)) - symbol_end = (unsigned long)_einittext; - else - symbol_end = (unsigned long)_etext; + /* Grab name */ + kallsyms_expand_symbol(get_symbol_offset(low), namebuf); /* Search for next non-aliased symbol */ - for (i = best+1; i < kallsyms_num_syms; i++) { - if (kallsyms_addresses[i] > kallsyms_addresses[best]) { + for (i = low + 1; i < kallsyms_num_syms; i++) { + if (kallsyms_addresses[i] > kallsyms_addresses[low]) { symbol_end = kallsyms_addresses[i]; break; } } - *symbolsize = symbol_end - kallsyms_addresses[best]; + /* if we found no next symbol, we use the end of the section */ + if (!symbol_end) { + if (is_kernel_inittext(addr)) + symbol_end = (unsigned long)_einittext; + else + symbol_end = (unsigned long)_etext; + } + + *symbolsize = symbol_end - kallsyms_addresses[low]; *modname = NULL; - *offset = addr - kallsyms_addresses[best]; + *offset = addr - kallsyms_addresses[low]; return namebuf; } @@ -135,7 +217,7 @@ void __print_symbol(const char *fmt, uns printk(fmt, buffer); } -/* To avoid O(n^2) iteration, we carry prefix along. */ +/* To avoid using get_symbol_offset for every symbol, we carry prefix along. */ struct kallsym_iter { loff_t pos; @@ -168,31 +250,23 @@ static int get_ksymbol_mod(struct kallsy /* Returns space to next name. */ static unsigned long get_ksymbol_core(struct kallsym_iter *iter) { - unsigned stemlen, off = iter->nameoff; + unsigned off = iter->nameoff; - /* First char of each symbol name indicates prefix length - shared with previous name (stem compression). */ - stemlen = kallsyms_names[off++]; - - strlcpy(iter->name+stemlen, kallsyms_names + off, - KSYM_NAME_LEN+1-stemlen); - off += strlen(kallsyms_names + off) + 1; iter->owner = NULL; iter->value = kallsyms_addresses[iter->pos]; - if (is_kernel_text(iter->value) || is_kernel_inittext(iter->value)) - iter->type = 't'; - else - iter->type = 'd'; - upcase_if_global(iter); + iter->type = kallsyms_get_symbol_type(off); + + off = kallsyms_expand_symbol(off, iter->name); + return off - iter->nameoff; } -static void reset_iter(struct kallsym_iter *iter) +static void reset_iter(struct kallsym_iter *iter, loff_t new_pos) { iter->name[0] = '\0'; - iter->nameoff = 0; - iter->pos = 0; + iter->nameoff = get_symbol_offset(new_pos); + iter->pos = new_pos; } /* Returns false if pos at or past end of file. */ @@ -204,16 +278,13 @@ static int update_iter(struct kallsym_it return get_ksymbol_mod(iter); } - /* If we're past the desired position, reset to start. */ - if (pos < iter->pos) - reset_iter(iter); - - /* We need to iterate through the previous symbols: can be slow */ - for (; iter->pos != pos; iter->pos++) { - iter->nameoff += get_ksymbol_core(iter); - cond_resched(); - } - get_ksymbol_core(iter); + /* If we're not on the desired position, reset to new position. */ + if (pos != iter->pos) + reset_iter(iter, pos); + + iter->nameoff += get_ksymbol_core(iter); + iter->pos++; + return 1; } @@ -267,14 +338,15 @@ struct seq_operations kallsyms_op = { static int kallsyms_open(struct inode *inode, struct file *file) { /* We keep iterator in m->private, since normal case is to - * s_start from where we left off, so we avoid O(N^2). */ + * s_start from where we left off, so we avoid doing + * using get_symbol_offset for every symbol */ struct kallsym_iter *iter; int ret; iter = kmalloc(sizeof(*iter), GFP_KERNEL); if (!iter) return -ENOMEM; - reset_iter(iter); + reset_iter(iter, 0); ret = seq_open(file, &kallsyms_op); if (ret == 0) @@ -310,3 +382,4 @@ int __init kallsyms_init(void) __initcall(kallsyms_init); EXPORT_SYMBOL(__print_symbol); +EXPORT_SYMBOL(kallsyms_lookup_name); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/kernel/kevent.c 2004-09-03 00:57:14.975762600 -0700 @@ -0,0 +1,100 @@ +/* + * kernel/kevent.c - sysfs event delivery via netlink socket + * + * Copyright (C) 2004 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004 Novell, Inc. All rights reserved. + * + * Licensed under the GNU GPL v2. + * + * Authors: + * Robert Love + * Kay Sievers + * Arjan van de Ven + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct sock *kevent_sock = NULL; /* kevent's global netlink socket */ + +/** + * send_kevent - send a message to user-space via the kernel events layer + */ +static int do_send_kevent(enum kevent type, int gfp_mask, + const char *object, const char *signal) +{ + struct sk_buff *skb; + char *buffer; + int len; + + if (!kevent_sock) + return -EIO; + + if (!object || !signal) + return -EINVAL; + + len = strlen(object) + 1 + strlen(signal) + 1; + + skb = alloc_skb(len, gfp_mask); + if (!skb) + return -ENOMEM; + + buffer = skb_put(skb, len); + + sprintf(buffer, "%s\n%s", object, signal); + + return netlink_broadcast(kevent_sock, skb, 0, (1 << type), gfp_mask); +} + +static int __send_kevent(enum kevent type, struct kset *kset, + struct kobject *kobj, const char *signal, + int gfp_flags) +{ + const char *path; + int ret; + + path = kobject_get_path(kset, kobj, gfp_flags); + if (!path) + return -ENOMEM; + + ret = do_send_kevent(type, gfp_flags, path, signal); + kfree(path); + + return ret; +} + +int send_kevent(enum kevent type, struct kset *kset, + struct kobject *kobj, const char *signal) +{ + return __send_kevent(type, kset, kobj, signal, GFP_KERNEL); +} +EXPORT_SYMBOL_GPL(send_kevent); + +int send_kevent_atomic(enum kevent type, struct kset *kset, + struct kobject *kobj, const char *signal) +{ + return __send_kevent(type, kset, kobj, signal, GFP_ATOMIC); +} +EXPORT_SYMBOL_GPL(send_kevent_atomic); + +static int kevent_init(void) +{ + kevent_sock = netlink_kernel_create(NETLINK_KEVENT, NULL); + + if (!kevent_sock) { + printk(KERN_ERR + "kevent: unable to create netlink socket!\n"); + return -ENODEV; + } + + return 0; +} + +module_init(kevent_init); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/kernel/kexec.c 2004-09-03 00:56:58.617249472 -0700 @@ -0,0 +1,640 @@ +/* + * kexec.c - kexec system call + * Copyright (C) 2002-2004 Eric Biederman + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * When kexec transitions to the new kernel there is a one-to-one + * mapping between physical and virtual addresses. On processors + * where you can disable the MMU this is trivial, and easy. For + * others it is still a simple predictable page table to setup. + * + * In that environment kexec copies the new kernel to its final + * resting place. This means I can only support memory whose + * physical address can fit in an unsigned long. In particular + * addresses where (pfn << PAGE_SHIFT) > ULONG_MAX cannot be handled. + * If the assembly stub has more restrictive requirements + * KEXEC_SOURCE_MEMORY_LIMIT and KEXEC_DEST_MEMORY_LIMIT can be + * defined more restrictively in . + * + * The code for the transition from the current kernel to the + * the new kernel is placed in the control_code_buffer, whose size + * is given by KEXEC_CONTROL_CODE_SIZE. In the best case only a single + * page of memory is necessary, but some architectures require more. + * Because this memory must be identity mapped in the transition from + * virtual to physical addresses it must live in the range + * 0 - TASK_SIZE, as only the user space mappings are arbitrarily + * modifiable. + * + * The assembly stub in the control code buffer is passed a linked list + * of descriptor pages detailing the source pages of the new kernel, + * and the destination addresses of those source pages. As this data + * structure is not used in the context of the current OS, it must + * be self-contained. + * + * The code has been made to work with highmem pages and will use a + * destination page in its final resting place (if it happens + * to allocate it). The end product of this is that most of the + * physical address space, and most of RAM can be used. + * + * Future directions include: + * - allocating a page table with the control code buffer identity + * mapped, to simplify machine_kexec and make kexec_on_panic more + * reliable. + */ + +/* + * KIMAGE_NO_DEST is an impossible destination address..., for + * allocating pages whose destination address we do not care about. + */ +#define KIMAGE_NO_DEST (-1UL) + +static int kimage_is_destination_range( + struct kimage *image, unsigned long start, unsigned long end); +static struct page *kimage_alloc_page(struct kimage *image, unsigned int gfp_mask, unsigned long dest); + + +static int kimage_alloc(struct kimage **rimage, + unsigned long nr_segments, struct kexec_segment *segments) +{ + int result; + struct kimage *image; + size_t segment_bytes; + unsigned long i; + + /* Allocate a controlling structure */ + result = -ENOMEM; + image = kmalloc(sizeof(*image), GFP_KERNEL); + if (!image) { + goto out; + } + memset(image, 0, sizeof(*image)); + image->head = 0; + image->entry = &image->head; + image->last_entry = &image->head; + + /* Initialize the list of control pages */ + INIT_LIST_HEAD(&image->control_pages); + + /* Initialize the list of destination pages */ + INIT_LIST_HEAD(&image->dest_pages); + + /* Initialize the list of unuseable pages */ + INIT_LIST_HEAD(&image->unuseable_pages); + + /* Read in the segments */ + image->nr_segments = nr_segments; + segment_bytes = nr_segments * sizeof*segments; + result = copy_from_user(image->segment, segments, segment_bytes); + if (result) + goto out; + + /* + * Verify we have good destination addresses. The caller is + * responsible for making certain we don't attempt to load + * the new image into invalid or reserved areas of RAM. This + * just verifies it is an address we can use. + */ + result = -EADDRNOTAVAIL; + for (i = 0; i < nr_segments; i++) { + unsigned long mend; + mend = ((unsigned long)(image->segment[i].mem)) + + image->segment[i].memsz; + if (mend >= KEXEC_DESTINATION_MEMORY_LIMIT) + goto out; + } + + /* + * Find a location for the control code buffer, and add it + * the vector of segments so that it's pages will also be + * counted as destination pages. + */ + result = -ENOMEM; + image->control_code_page = kimage_alloc_control_pages(image, + get_order(KEXEC_CONTROL_CODE_SIZE)); + if (!image->control_code_page) { + printk(KERN_ERR "Could not allocate control_code_buffer\n"); + goto out; + } + + result = 0; + out: + if (result == 0) { + *rimage = image; + } else { + kfree(image); + } + return result; +} + +static int kimage_is_destination_range( + struct kimage *image, unsigned long start, unsigned long end) +{ + unsigned long i; + + for (i = 0; i < image->nr_segments; i++) { + unsigned long mstart, mend; + mstart = (unsigned long)image->segment[i].mem; + mend = mstart + image->segment[i].memsz; + if ((end > mstart) && (start < mend)) { + return 1; + } + } + return 0; +} + +static struct page *kimage_alloc_pages(unsigned int gfp_mask, unsigned int order) +{ + struct page *pages; + pages = alloc_pages(gfp_mask, order); + if (pages) { + unsigned int count, i; + pages->mapping = NULL; + pages->private = order; + count = 1 << order; + for(i = 0; i < count; i++) { + SetPageReserved(pages + i); + } + } + return pages; +} + +static void kimage_free_pages(struct page *page) +{ + unsigned int order, count, i; + order = page->private; + count = 1 << order; + for(i = 0; i < count; i++) { + ClearPageReserved(page + i); + } + __free_pages(page, order); +} + +static void kimage_free_page_list(struct list_head *list) +{ + struct list_head *pos, *next; + list_for_each_safe(pos, next, list) { + struct page *page; + + page = list_entry(pos, struct page, lru); + list_del(&page->lru); + + kimage_free_pages(page); + } +} + +struct page *kimage_alloc_control_pages(struct kimage *image, unsigned int order) +{ + /* Control pages are special, they are the intermediaries + * that are needed while we copy the rest of the pages + * to their final resting place. As such they must + * not conflict with either the destination addresses + * or memory the kernel is already using. + * + * The only case where we really need more than one of + * these are for architectures where we cannot disable + * the MMU and must instead generate an identity mapped + * page table for all of the memory. + * + * At worst this runs in O(N) of the image size. + */ + struct list_head extra_pages; + struct page *pages; + unsigned int count; + + count = 1 << order; + INIT_LIST_HEAD(&extra_pages); + + /* Loop while I can allocate a page and the page allocated + * is a destination page. + */ + do { + unsigned long pfn, epfn, addr, eaddr; + pages = kimage_alloc_pages(GFP_KERNEL, order); + if (!pages) + break; + pfn = page_to_pfn(pages); + epfn = pfn + count; + addr = pfn << PAGE_SHIFT; + eaddr = epfn << PAGE_SHIFT; + if ((epfn >= (KEXEC_CONTROL_MEMORY_LIMIT >> PAGE_SHIFT)) || + kimage_is_destination_range(image, addr, eaddr)) + { + list_add(&pages->lru, &extra_pages); + pages = NULL; + } + } while(!pages); + if (pages) { + /* Remember the allocated page... */ + list_add(&pages->lru, &image->control_pages); + + /* Because the page is already in it's destination + * location we will never allocate another page at + * that address. Therefore kimage_alloc_pages + * will not return it (again) and we don't need + * to give it an entry in image->segment[]. + */ + } + /* Deal with the destination pages I have inadvertently allocated. + * + * Ideally I would convert multi-page allocations into single + * page allocations, and add everyting to image->dest_pages. + * + * For now it is simpler to just free the pages. + */ + kimage_free_page_list(&extra_pages); + return pages; + +} + +static int kimage_add_entry(struct kimage *image, kimage_entry_t entry) +{ + if (*image->entry != 0) { + image->entry++; + } + if (image->entry == image->last_entry) { + kimage_entry_t *ind_page; + struct page *page; + page = kimage_alloc_page(image, GFP_KERNEL, KIMAGE_NO_DEST); + if (!page) { + return -ENOMEM; + } + ind_page = page_address(page); + *image->entry = virt_to_phys(ind_page) | IND_INDIRECTION; + image->entry = ind_page; + image->last_entry = + ind_page + ((PAGE_SIZE/sizeof(kimage_entry_t)) - 1); + } + *image->entry = entry; + image->entry++; + *image->entry = 0; + return 0; +} + +static int kimage_set_destination( + struct kimage *image, unsigned long destination) +{ + int result; + + destination &= PAGE_MASK; + result = kimage_add_entry(image, destination | IND_DESTINATION); + if (result == 0) { + image->destination = destination; + } + return result; +} + + +static int kimage_add_page(struct kimage *image, unsigned long page) +{ + int result; + + page &= PAGE_MASK; + result = kimage_add_entry(image, page | IND_SOURCE); + if (result == 0) { + image->destination += PAGE_SIZE; + } + return result; +} + + +static void kimage_free_extra_pages(struct kimage *image) +{ + /* Walk through and free any extra destination pages I may have */ + kimage_free_page_list(&image->dest_pages); + + /* Walk through and free any unuseable pages I have cached */ + kimage_free_page_list(&image->unuseable_pages); + +} +static int kimage_terminate(struct kimage *image) +{ + int result; + + result = kimage_add_entry(image, IND_DONE); + if (result == 0) { + /* Point at the terminating element */ + image->entry--; + kimage_free_extra_pages(image); + } + return result; +} + +#define for_each_kimage_entry(image, ptr, entry) \ + for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE); \ + ptr = (entry & IND_INDIRECTION)? \ + phys_to_virt((entry & PAGE_MASK)): ptr +1) + +static void kimage_free_entry(kimage_entry_t entry) +{ + struct page *page; + + page = pfn_to_page(entry >> PAGE_SHIFT); + kimage_free_pages(page); +} + +static void kimage_free(struct kimage *image) +{ + kimage_entry_t *ptr, entry; + kimage_entry_t ind = 0; + + if (!image) + return; + kimage_free_extra_pages(image); + for_each_kimage_entry(image, ptr, entry) { + if (entry & IND_INDIRECTION) { + /* Free the previous indirection page */ + if (ind & IND_INDIRECTION) { + kimage_free_entry(ind); + } + /* Save this indirection page until we are + * done with it. + */ + ind = entry; + } + else if (entry & IND_SOURCE) { + kimage_free_entry(entry); + } + } + /* Free the final indirection page */ + if (ind & IND_INDIRECTION) { + kimage_free_entry(ind); + } + + /* Handle any machine specific cleanup */ + machine_kexec_cleanup(image); + + /* Free the kexec control pages... */ + kimage_free_page_list(&image->control_pages); + kfree(image); +} + +static kimage_entry_t *kimage_dst_used(struct kimage *image, unsigned long page) +{ + kimage_entry_t *ptr, entry; + unsigned long destination = 0; + + for_each_kimage_entry(image, ptr, entry) { + if (entry & IND_DESTINATION) { + destination = entry & PAGE_MASK; + } + else if (entry & IND_SOURCE) { + if (page == destination) { + return ptr; + } + destination += PAGE_SIZE; + } + } + return 0; +} + +static struct page *kimage_alloc_page(struct kimage *image, unsigned int gfp_mask, unsigned long destination) +{ + /* + * Here we implement safeguards to ensure that a source page + * is not copied to its destination page before the data on + * the destination page is no longer useful. + * + * To do this we maintain the invariant that a source page is + * either its own destination page, or it is not a + * destination page at all. + * + * That is slightly stronger than required, but the proof + * that no problems will not occur is trivial, and the + * implementation is simply to verify. + * + * When allocating all pages normally this algorithm will run + * in O(N) time, but in the worst case it will run in O(N^2) + * time. If the runtime is a problem the data structures can + * be fixed. + */ + struct page *page; + unsigned long addr; + + /* + * Walk through the list of destination pages, and see if I + * have a match. + */ + list_for_each_entry(page, &image->dest_pages, lru) { + addr = page_to_pfn(page) << PAGE_SHIFT; + if (addr == destination) { + list_del(&page->lru); + return page; + } + } + page = NULL; + while (1) { + kimage_entry_t *old; + + /* Allocate a page, if we run out of memory give up */ + page = kimage_alloc_pages(gfp_mask, 0); + if (!page) { + return 0; + } + /* If the page cannot be used file it away */ + if (page_to_pfn(page) > (KEXEC_SOURCE_MEMORY_LIMIT >> PAGE_SHIFT)) { + list_add(&page->lru, &image->unuseable_pages); + continue; + } + addr = page_to_pfn(page) << PAGE_SHIFT; + + /* If it is the destination page we want use it */ + if (addr == destination) + break; + + /* If the page is not a destination page use it */ + if (!kimage_is_destination_range(image, addr, addr + PAGE_SIZE)) + break; + + /* + * I know that the page is someones destination page. + * See if there is already a source page for this + * destination page. And if so swap the source pages. + */ + old = kimage_dst_used(image, addr); + if (old) { + /* If so move it */ + unsigned long old_addr; + struct page *old_page; + + old_addr = *old & PAGE_MASK; + old_page = pfn_to_page(old_addr >> PAGE_SHIFT); + copy_highpage(page, old_page); + *old = addr | (*old & ~PAGE_MASK); + + /* The old page I have found cannot be a + * destination page, so return it. + */ + addr = old_addr; + page = old_page; + break; + } + else { + /* Place the page on the destination list I + * will use it later. + */ + list_add(&page->lru, &image->dest_pages); + } + } + return page; +} + +static int kimage_load_segment(struct kimage *image, + struct kexec_segment *segment) +{ + unsigned long mstart; + int result; + unsigned long offset; + unsigned long offset_end; + unsigned char *buf; + + result = 0; + buf = segment->buf; + mstart = (unsigned long)segment->mem; + + offset_end = segment->memsz; + + result = kimage_set_destination(image, mstart); + if (result < 0) { + goto out; + } + for (offset = 0; offset < segment->memsz; offset += PAGE_SIZE) { + struct page *page; + char *ptr; + size_t size, leader; + page = kimage_alloc_page(image, GFP_HIGHUSER, mstart + offset); + if (page == 0) { + result = -ENOMEM; + goto out; + } + result = kimage_add_page(image, page_to_pfn(page) << PAGE_SHIFT); + if (result < 0) { + goto out; + } + ptr = kmap(page); + if (segment->bufsz < offset) { + /* We are past the end zero the whole page */ + memset(ptr, 0, PAGE_SIZE); + kunmap(page); + continue; + } + size = PAGE_SIZE; + leader = 0; + if ((offset == 0)) { + leader = mstart & ~PAGE_MASK; + } + if (leader) { + /* We are on the first page zero the unused portion */ + memset(ptr, 0, leader); + size -= leader; + ptr += leader; + } + if (size > (segment->bufsz - offset)) { + size = segment->bufsz - offset; + } + if (size < (PAGE_SIZE - leader)) { + /* zero the trailing part of the page */ + memset(ptr + size, 0, (PAGE_SIZE - leader) - size); + } + result = copy_from_user(ptr, buf + offset, size); + kunmap(page); + if (result) { + result = (result < 0) ? result : -EIO; + goto out; + } + } + out: + return result; +} + +/* + * Exec Kernel system call: for obvious reasons only root may call it. + * + * This call breaks up into three pieces. + * - A generic part which loads the new kernel from the current + * address space, and very carefully places the data in the + * allocated pages. + * + * - A generic part that interacts with the kernel and tells all of + * the devices to shut down. Preventing on-going dmas, and placing + * the devices in a consistent state so a later kernel can + * reinitialize them. + * + * - A machine specific part that includes the syscall number + * and the copies the image to it's final destination. And + * jumps into the image at entry. + * + * kexec does not sync, or unmount filesystems so if you need + * that to happen you need to do that yourself. + */ +struct kimage *kexec_image = NULL; + +asmlinkage long sys_kexec_load(unsigned long entry, unsigned long nr_segments, + struct kexec_segment *segments, unsigned long flags) +{ + struct kimage *image; + int result; + + /* We only trust the superuser with rebooting the system. */ + if (!capable(CAP_SYS_BOOT)) + return -EPERM; + + /* + * In case we need just a little bit of special behavior for + * reboot on panic. + */ + if (flags != 0) + return -EINVAL; + + if (nr_segments > KEXEC_SEGMENT_MAX) + return -EINVAL; + + image = NULL; + result = 0; + + if (nr_segments > 0) { + unsigned long i; + result = kimage_alloc(&image, nr_segments, segments); + if (result) { + goto out; + } + result = machine_kexec_prepare(image); + if (result) { + goto out; + } + image->start = entry; + for (i = 0; i < nr_segments; i++) { + result = kimage_load_segment(image, &image->segment[i]); + if (result) { + goto out; + } + } + result = kimage_terminate(image); + if (result) { + goto out; + } + } + + image = xchg(&kexec_image, image); + + out: + kimage_free(image); + return result; +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/kernel/kprobes.c 2004-09-02 22:09:10.000000000 -0700 @@ -0,0 +1,146 @@ +/* + * Kernel Probes (KProbes) + * kernel/kprobes.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) IBM Corporation, 2002, 2004 + * + * 2002-Oct Created by Vamsi Krishna S Kernel + * Probes initial implementation (includes suggestions from + * Rusty Russell). + * 2004-Aug Updated by Prasanna S Panchamukhi with + * hlists and exceptions notifier as suggested by Andi Kleen. + * 2004-July Suparna Bhattacharya added jumper probes + * interface to access function arguments. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define KPROBE_HASH_BITS 6 +#define KPROBE_TABLE_SIZE (1 << KPROBE_HASH_BITS) + +static struct hlist_head kprobe_table[KPROBE_TABLE_SIZE]; + +unsigned int kprobe_cpu = NR_CPUS; +static spinlock_t kprobe_lock = SPIN_LOCK_UNLOCKED; + +/* Locks kprobe: irqs must be disabled */ +void lock_kprobes(void) +{ + spin_lock(&kprobe_lock); + kprobe_cpu = smp_processor_id(); +} + +void unlock_kprobes(void) +{ + kprobe_cpu = NR_CPUS; + spin_unlock(&kprobe_lock); +} + +/* You have to be holding the kprobe_lock */ +struct kprobe *get_kprobe(void *addr) +{ + struct hlist_head *head; + struct hlist_node *node; + + head = &kprobe_table[hash_ptr(addr, KPROBE_HASH_BITS)]; + hlist_for_each(node, head) { + struct kprobe *p = hlist_entry(node, struct kprobe, hlist); + if (p->addr == addr) + return p; + } + return NULL; +} + +int register_kprobe(struct kprobe *p) +{ + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&kprobe_lock, flags); + INIT_HLIST_NODE(&p->hlist); + if (get_kprobe(p->addr)) { + ret = -EEXIST; + goto out; + } + hlist_add_head(&p->hlist, + &kprobe_table[hash_ptr(p->addr, KPROBE_HASH_BITS)]); + + arch_prepare_kprobe(p); + p->opcode = *p->addr; + *p->addr = BREAKPOINT_INSTRUCTION; + flush_icache_range((unsigned long) p->addr, + (unsigned long) p->addr + sizeof(kprobe_opcode_t)); + out: + spin_unlock_irqrestore(&kprobe_lock, flags); + return ret; +} + +void unregister_kprobe(struct kprobe *p) +{ + unsigned long flags; + spin_lock_irqsave(&kprobe_lock, flags); + *p->addr = p->opcode; + hlist_del(&p->hlist); + flush_icache_range((unsigned long) p->addr, + (unsigned long) p->addr + sizeof(kprobe_opcode_t)); + spin_unlock_irqrestore(&kprobe_lock, flags); +} + +static struct notifier_block kprobe_exceptions_nb = { + .notifier_call = kprobe_exceptions_notify, +}; + +int register_jprobe(struct jprobe *jp) +{ + /* Todo: Verify probepoint is a function entry point */ + jp->kp.pre_handler = setjmp_pre_handler; + jp->kp.break_handler = longjmp_break_handler; + + return register_kprobe(&jp->kp); +} + +void unregister_jprobe(struct jprobe *jp) +{ + unregister_kprobe(&jp->kp); +} + +static int __init init_kprobes(void) +{ + int i, err = 0; + + /* FIXME allocate the probe table, currently defined statically */ + /* initialize all list heads */ + for (i = 0; i < KPROBE_TABLE_SIZE; i++) + INIT_HLIST_HEAD(&kprobe_table[i]); + + err = register_die_notifier(&kprobe_exceptions_nb); + return err; +} + +__initcall(init_kprobes); + +EXPORT_SYMBOL_GPL(register_kprobe); +EXPORT_SYMBOL_GPL(unregister_kprobe); +EXPORT_SYMBOL_GPL(register_jprobe); +EXPORT_SYMBOL_GPL(unregister_jprobe); +EXPORT_SYMBOL_GPL(jprobe_return); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/kernel/lockmeter.c 2004-09-03 00:56:43.082611096 -0700 @@ -0,0 +1,1179 @@ +/* + * Copyright (C) 1999,2000 Silicon Graphics, Inc. + * + * Written by John Hawkes (hawkes@sgi.com) + * Based on klstat.c by Jack Steiner (steiner@sgi.com) + * + * Modified by Ray Bryant (raybry@us.ibm.com) + * Changes Copyright (C) 2000 IBM, Inc. + * Added save of index in spinlock_t to improve efficiency + * of "hold" time reporting for spinlocks + * Added support for hold time statistics for read and write + * locks. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ASSERT(cond) +#define bzero(loc,size) memset(loc,0,size) + +/*<---------------------------------------------------*/ +/* lockmeter.c */ +/*>---------------------------------------------------*/ + +static lstat_control_t lstat_control __cacheline_aligned = + { LSTAT_OFF, SPIN_LOCK_UNLOCKED, SPIN_LOCK_UNLOCKED, + 19 * 0, NR_CPUS * 0, 0, NR_CPUS * 0 }; + +static ushort lstat_make_dir_entry(void *, void *); + +/* + * lstat_lookup + * + * Given a RA, locate the directory entry for the lock. + */ +static ushort +lstat_lookup(void *lock_ptr, void *caller_ra) +{ + ushort index; + lstat_directory_entry_t *dirp; + + dirp = lstat_control.dir; + + index = lstat_control.hashtab[DIRHASH(caller_ra)]; + while (dirp[index].caller_ra != caller_ra) { + if (index == 0) { + return lstat_make_dir_entry(lock_ptr, caller_ra); + } + index = dirp[index].next_stat_index; + } + + if (dirp[index].lock_ptr != NULL && dirp[index].lock_ptr != lock_ptr) { + dirp[index].lock_ptr = NULL; + } + + return index; +} + +/* + * lstat_make_dir_entry + * Called to add a new lock to the lock directory. + */ +static ushort +lstat_make_dir_entry(void *lock_ptr, void *caller_ra) +{ + lstat_directory_entry_t *dirp; + ushort index, hindex; + unsigned long flags; + + /* lock the table without recursively reentering this metering code */ + local_irq_save(flags); + _raw_spin_lock(&lstat_control.directory_lock); + + hindex = DIRHASH(caller_ra); + index = lstat_control.hashtab[hindex]; + dirp = lstat_control.dir; + while (index && dirp[index].caller_ra != caller_ra) + index = dirp[index].next_stat_index; + + if (index == 0) { + if (lstat_control.next_free_dir_index < LSTAT_MAX_STAT_INDEX) { + index = lstat_control.next_free_dir_index++; + lstat_control.dir[index].caller_ra = caller_ra; + lstat_control.dir[index].lock_ptr = lock_ptr; + lstat_control.dir[index].next_stat_index = + lstat_control.hashtab[hindex]; + lstat_control.hashtab[hindex] = index; + } else { + lstat_control.dir_overflow++; + } + } + _raw_spin_unlock(&lstat_control.directory_lock); + local_irq_restore(flags); + return index; +} + +int +lstat_update(void *lock_ptr, void *caller_ra, int action) +{ + int index; + int cpu; + + ASSERT(action < LSTAT_ACT_MAX_VALUES); + + if (lstat_control.state == LSTAT_OFF) + return 0; + + index = lstat_lookup(lock_ptr, caller_ra); + cpu = THIS_CPU_NUMBER; + (*lstat_control.counts[cpu])[index].count[action]++; + (*lstat_control.counts[cpu])[index].acquire_time = get_cycles(); + + return index; +} + +int +lstat_update_time(void *lock_ptr, void *caller_ra, int action, uint32_t ticks) +{ + ushort index; + int cpu; + + ASSERT(action < LSTAT_ACT_MAX_VALUES); + + if (lstat_control.state == LSTAT_OFF) + return 0; + + index = lstat_lookup(lock_ptr, caller_ra); + cpu = THIS_CPU_NUMBER; + (*lstat_control.counts[cpu])[index].count[action]++; + (*lstat_control.counts[cpu])[index].cum_wait_ticks += (uint64_t) ticks; + if ((*lstat_control.counts[cpu])[index].max_wait_ticks < ticks) + (*lstat_control.counts[cpu])[index].max_wait_ticks = ticks; + + (*lstat_control.counts[cpu])[index].acquire_time = get_cycles(); + + return index; +} + +void +_metered_spin_lock(spinlock_t * lock_ptr) +{ + if (lstat_control.state == LSTAT_OFF) { + _raw_spin_lock(lock_ptr); /* do the real lock */ + PUT_INDEX(lock_ptr, 0); /* clean index in case lockmetering */ + /* gets turned on before unlock */ + } else { + void *this_pc = LSTAT_RA(LSTAT_RA_SPIN); + int index; + + if (_raw_spin_trylock(lock_ptr)) { + index = lstat_update(lock_ptr, this_pc, + LSTAT_ACT_NO_WAIT); + } else { + uint32_t start_cycles = get_cycles(); + _raw_spin_lock(lock_ptr); /* do the real lock */ + index = lstat_update_time(lock_ptr, this_pc, + LSTAT_ACT_SPIN, get_cycles() - start_cycles); + } + /* save the index in the lock itself for use in spin unlock */ + PUT_INDEX(lock_ptr, index); + } +} + +int +_metered_spin_trylock(spinlock_t * lock_ptr) +{ + if (lstat_control.state == LSTAT_OFF) { + return _raw_spin_trylock(lock_ptr); + } else { + int retval; + void *this_pc = LSTAT_RA(LSTAT_RA_SPIN); + + if ((retval = _raw_spin_trylock(lock_ptr))) { + int index = lstat_update(lock_ptr, this_pc, + LSTAT_ACT_NO_WAIT); + /* + * save the index in the lock itself for use in spin + * unlock + */ + PUT_INDEX(lock_ptr, index); + } else { + lstat_update(lock_ptr, this_pc, LSTAT_ACT_REJECT); + } + + return retval; + } +} + +void +_metered_spin_unlock(spinlock_t * lock_ptr) +{ + int index = -1; + + if (lstat_control.state != LSTAT_OFF) { + index = GET_INDEX(lock_ptr); + /* + * If statistics were turned off when we set the lock, + * then the index can be zero. If that is the case, + * then collect no stats on this call. + */ + if (index > 0) { + uint32_t hold_time; + int cpu = THIS_CPU_NUMBER; + hold_time = get_cycles() - + (*lstat_control.counts[cpu])[index].acquire_time; + (*lstat_control.counts[cpu])[index].cum_hold_ticks += + (uint64_t) hold_time; + if ((*lstat_control.counts[cpu])[index].max_hold_ticks < + hold_time) + (*lstat_control.counts[cpu])[index]. + max_hold_ticks = hold_time; + } + } + + /* make sure we don't have a stale index value saved */ + PUT_INDEX(lock_ptr, 0); + _raw_spin_unlock(lock_ptr); /* do the real unlock */ +} + +/* + * allocate the next global read lock structure and store its index + * in the rwlock at "lock_ptr". + */ +uint32_t +alloc_rwlock_struct(rwlock_t * rwlock_ptr) +{ + int index; + unsigned long flags; + int cpu = THIS_CPU_NUMBER; + + /* If we've already overflowed, then do a quick exit */ + if (lstat_control.next_free_read_lock_index > + LSTAT_MAX_READ_LOCK_INDEX) { + lstat_control.rwlock_overflow++; + return 0; + } + + local_irq_save(flags); + _raw_spin_lock(&lstat_control.directory_lock); + + /* It is possible this changed while we were waiting for the directory_lock */ + if (lstat_control.state == LSTAT_OFF) { + index = 0; + goto unlock; + } + + /* It is possible someone else got here first and set the index */ + if ((index = GET_RWINDEX(rwlock_ptr)) == 0) { + /* + * we can't turn on read stats for this lock while there are + * readers (this would mess up the running hold time sum at + * unlock time) + */ + if (RWLOCK_READERS(rwlock_ptr) != 0) { + index = 0; + goto unlock; + } + + /* + * if stats are turned on after being off, we may need to + * return an old index from when the statistics were on last + * time. + */ + for (index = 1; index < lstat_control.next_free_read_lock_index; + index++) + if ((*lstat_control.read_lock_counts[cpu])[index]. + lock_ptr == rwlock_ptr) + goto put_index_and_unlock; + + /* allocate the next global read lock structure */ + if (lstat_control.next_free_read_lock_index >= + LSTAT_MAX_READ_LOCK_INDEX) { + lstat_control.rwlock_overflow++; + index = 0; + goto unlock; + } + index = lstat_control.next_free_read_lock_index++; + + /* + * initialize the global read stats data structure for each + * cpu + */ + for (cpu = 0; cpu < num_online_cpus(); cpu++) { + (*lstat_control.read_lock_counts[cpu])[index].lock_ptr = + rwlock_ptr; + } +put_index_and_unlock: + /* store the index for the read lock structure into the lock */ + PUT_RWINDEX(rwlock_ptr, index); + } + +unlock: + _raw_spin_unlock(&lstat_control.directory_lock); + local_irq_restore(flags); + return index; +} + +void +_metered_read_lock(rwlock_t * rwlock_ptr) +{ + void *this_pc; + uint32_t start_cycles; + int index; + int cpu; + unsigned long flags; + int readers_before, readers_after; + uint64_t cycles64; + + if (lstat_control.state == LSTAT_OFF) { + _raw_read_lock(rwlock_ptr); + /* clean index in case lockmetering turns on before an unlock */ + PUT_RWINDEX(rwlock_ptr, 0); + return; + } + + this_pc = LSTAT_RA(LSTAT_RA_READ); + cpu = THIS_CPU_NUMBER; + index = GET_RWINDEX(rwlock_ptr); + + /* allocate the global stats entry for this lock, if needed */ + if (index == 0) + index = alloc_rwlock_struct(rwlock_ptr); + + readers_before = RWLOCK_READERS(rwlock_ptr); + if (_raw_read_trylock(rwlock_ptr)) { + /* + * We have decremented the lock to count a new reader, + * and have confirmed that no writer has it locked. + */ + /* update statistics if enabled */ + if (index > 0) { + local_irq_save(flags); + lstat_update((void *) rwlock_ptr, this_pc, + LSTAT_ACT_NO_WAIT); + /* preserve value of TSC so cum_hold_ticks and start_busy use same value */ + cycles64 = get_cycles64(); + (*lstat_control.read_lock_counts[cpu])[index]. + cum_hold_ticks -= cycles64; + + /* record time and cpu of start of busy period */ + /* this is not perfect (some race conditions are possible) */ + if (readers_before == 0) { + (*lstat_control.read_lock_counts[cpu])[index]. + start_busy = cycles64; + PUT_RW_CPU(rwlock_ptr, cpu); + } + readers_after = RWLOCK_READERS(rwlock_ptr); + if (readers_after > + (*lstat_control.read_lock_counts[cpu])[index]. + max_readers) + (*lstat_control.read_lock_counts[cpu])[index]. + max_readers = readers_after; + local_irq_restore(flags); + } + + return; + } + /* If we get here, then we could not quickly grab the read lock */ + + start_cycles = get_cycles(); /* start counting the wait time */ + + /* Now spin until read_lock is successful */ + _raw_read_lock(rwlock_ptr); + + lstat_update_time((void *) rwlock_ptr, this_pc, LSTAT_ACT_SPIN, + get_cycles() - start_cycles); + + /* update statistics if they are enabled for this lock */ + if (index > 0) { + local_irq_save(flags); + cycles64 = get_cycles64(); + (*lstat_control.read_lock_counts[cpu])[index].cum_hold_ticks -= + cycles64; + + /* this is not perfect (some race conditions are possible) */ + if (readers_before == 0) { + (*lstat_control.read_lock_counts[cpu])[index]. + start_busy = cycles64; + PUT_RW_CPU(rwlock_ptr, cpu); + } + readers_after = RWLOCK_READERS(rwlock_ptr); + if (readers_after > + (*lstat_control.read_lock_counts[cpu])[index].max_readers) + (*lstat_control.read_lock_counts[cpu])[index]. + max_readers = readers_after; + local_irq_restore(flags); + } +} + +void +_metered_read_unlock(rwlock_t * rwlock_ptr) +{ + int index; + int cpu; + unsigned long flags; + uint64_t busy_length; + uint64_t cycles64; + + if (lstat_control.state == LSTAT_OFF) { + _raw_read_unlock(rwlock_ptr); + return; + } + + index = GET_RWINDEX(rwlock_ptr); + cpu = THIS_CPU_NUMBER; + + if (index > 0) { + local_irq_save(flags); + /* + * preserve value of TSC so cum_hold_ticks and busy_ticks are + * consistent. + */ + cycles64 = get_cycles64(); + (*lstat_control.read_lock_counts[cpu])[index].cum_hold_ticks += + cycles64; + (*lstat_control.read_lock_counts[cpu])[index].read_lock_count++; + + /* + * once again, this is not perfect (some race conditions are + * possible) + */ + if (RWLOCK_READERS(rwlock_ptr) == 1) { + int cpu1 = GET_RW_CPU(rwlock_ptr); + uint64_t last_start_busy = + (*lstat_control.read_lock_counts[cpu1])[index]. + start_busy; + (*lstat_control.read_lock_counts[cpu])[index]. + busy_periods++; + if (cycles64 > last_start_busy) { + busy_length = cycles64 - last_start_busy; + (*lstat_control.read_lock_counts[cpu])[index]. + busy_ticks += busy_length; + if (busy_length > + (*lstat_control. + read_lock_counts[cpu])[index]. + max_busy) + (*lstat_control. + read_lock_counts[cpu])[index]. + max_busy = busy_length; + } + } + local_irq_restore(flags); + } + _raw_read_unlock(rwlock_ptr); +} + +void +_metered_write_lock(rwlock_t * rwlock_ptr) +{ + uint32_t start_cycles; + void *this_pc; + uint32_t spin_ticks = 0; /* in anticipation of a potential wait */ + int index; + int write_index = 0; + int cpu; + enum { + writer_writer_conflict, + writer_reader_conflict + } why_wait = writer_writer_conflict; + + if (lstat_control.state == LSTAT_OFF) { + _raw_write_lock(rwlock_ptr); + /* clean index in case lockmetering turns on before an unlock */ + PUT_RWINDEX(rwlock_ptr, 0); + return; + } + + this_pc = LSTAT_RA(LSTAT_RA_WRITE); + cpu = THIS_CPU_NUMBER; + index = GET_RWINDEX(rwlock_ptr); + + /* allocate the global stats entry for this lock, if needed */ + if (index == 0) { + index = alloc_rwlock_struct(rwlock_ptr); + } + + if (_raw_write_trylock(rwlock_ptr)) { + /* We acquired the lock on the first try */ + write_index = lstat_update((void *) rwlock_ptr, this_pc, + LSTAT_ACT_NO_WAIT); + /* save the write_index for use in unlock if stats enabled */ + if (index > 0) + (*lstat_control.read_lock_counts[cpu])[index]. + write_index = write_index; + return; + } + + /* If we get here, then we could not quickly grab the write lock */ + start_cycles = get_cycles(); /* start counting the wait time */ + + why_wait = RWLOCK_READERS(rwlock_ptr) ? + writer_reader_conflict : writer_writer_conflict; + + /* Now set the lock and wait for conflicts to disappear */ + _raw_write_lock(rwlock_ptr); + + spin_ticks = get_cycles() - start_cycles; + + /* update stats -- if enabled */ + if (index > 0 && spin_ticks) { + if (why_wait == writer_reader_conflict) { + /* waited due to a reader holding the lock */ + write_index = lstat_update_time((void *)rwlock_ptr, + this_pc, LSTAT_ACT_SPIN, spin_ticks); + } else { + /* + * waited due to another writer holding the lock + */ + write_index = lstat_update_time((void *)rwlock_ptr, + this_pc, LSTAT_ACT_WW_SPIN, spin_ticks); + (*lstat_control.counts[cpu])[write_index]. + cum_wait_ww_ticks += spin_ticks; + if (spin_ticks > + (*lstat_control.counts[cpu])[write_index]. + max_wait_ww_ticks) { + (*lstat_control.counts[cpu])[write_index]. + max_wait_ww_ticks = spin_ticks; + } + } + + /* save the directory index for use on write_unlock */ + (*lstat_control.read_lock_counts[cpu])[index]. + write_index = write_index; + } +} + +void +_metered_write_unlock(rwlock_t * rwlock_ptr) +{ + int index; + int cpu; + int write_index; + uint32_t hold_time; + + if (lstat_control.state == LSTAT_OFF) { + _raw_write_unlock(rwlock_ptr); + return; + } + + cpu = THIS_CPU_NUMBER; + index = GET_RWINDEX(rwlock_ptr); + + /* update statistics if stats enabled for this lock */ + if (index > 0) { + write_index = + (*lstat_control.read_lock_counts[cpu])[index].write_index; + + hold_time = get_cycles() - + (*lstat_control.counts[cpu])[write_index].acquire_time; + (*lstat_control.counts[cpu])[write_index].cum_hold_ticks += + (uint64_t) hold_time; + if ((*lstat_control.counts[cpu])[write_index].max_hold_ticks < + hold_time) + (*lstat_control.counts[cpu])[write_index]. + max_hold_ticks = hold_time; + } + _raw_write_unlock(rwlock_ptr); +} + +int +_metered_write_trylock(rwlock_t * rwlock_ptr) +{ + int retval; + void *this_pc = LSTAT_RA(LSTAT_RA_WRITE); + + if ((retval = _raw_write_trylock(rwlock_ptr))) { + lstat_update(rwlock_ptr, this_pc, LSTAT_ACT_NO_WAIT); + } else { + lstat_update(rwlock_ptr, this_pc, LSTAT_ACT_REJECT); + } + + return retval; +} + +static void +init_control_space(void) +{ + /* Set all control space pointers to null and indices to "empty" */ + int cpu; + + /* + * Access CPU_CYCLE_FREQUENCY at the outset, which in some + * architectures may trigger a runtime calculation that uses a + * spinlock. Let's do this before lockmetering is turned on. + */ + if (CPU_CYCLE_FREQUENCY == 0) + BUG(); + + lstat_control.hashtab = NULL; + lstat_control.dir = NULL; + for (cpu = 0; cpu < NR_CPUS; cpu++) { + lstat_control.counts[cpu] = NULL; + lstat_control.read_lock_counts[cpu] = NULL; + } +} + +static int +reset_lstat_data(void) +{ + int cpu, flags; + + flags = 0; + lstat_control.next_free_dir_index = 1; /* 0 is for overflows */ + lstat_control.next_free_read_lock_index = 1; + lstat_control.dir_overflow = 0; + lstat_control.rwlock_overflow = 0; + + lstat_control.started_cycles64 = 0; + lstat_control.ending_cycles64 = 0; + lstat_control.enabled_cycles64 = 0; + lstat_control.first_started_time = 0; + lstat_control.started_time = 0; + lstat_control.ending_time = 0; + lstat_control.intervals = 0; + + /* + * paranoia -- in case someone does a "lockstat reset" before + * "lockstat on" + */ + if (lstat_control.hashtab) { + bzero(lstat_control.hashtab, + LSTAT_HASH_TABLE_SIZE * sizeof (short)); + bzero(lstat_control.dir, LSTAT_MAX_STAT_INDEX * + sizeof (lstat_directory_entry_t)); + + for (cpu = 0; cpu < num_online_cpus(); cpu++) { + bzero(lstat_control.counts[cpu], + sizeof (lstat_cpu_counts_t)); + bzero(lstat_control.read_lock_counts[cpu], + sizeof (lstat_read_lock_cpu_counts_t)); + } + } +#ifdef NOTDEF + _raw_spin_unlock(&lstat_control.directory_lock); + local_irq_restore(flags); +#endif + return 1; +} + +static void +release_control_space(void) +{ + /* + * Called when either (1) allocation of kmem + * or (2) when user writes LSTAT_RELEASE to /pro/lockmeter. + * Assume that all pointers have been initialized to zero, + * i.e., nonzero pointers are valid addresses. + */ + int cpu; + + if (lstat_control.hashtab) { + kfree(lstat_control.hashtab); + lstat_control.hashtab = NULL; + } + + if (lstat_control.dir) { + vfree(lstat_control.dir); + lstat_control.dir = NULL; + } + + for (cpu = 0; cpu < NR_CPUS; cpu++) { + if (lstat_control.counts[cpu]) { + vfree(lstat_control.counts[cpu]); + lstat_control.counts[cpu] = NULL; + } + if (lstat_control.read_lock_counts[cpu]) { + kfree(lstat_control.read_lock_counts[cpu]); + lstat_control.read_lock_counts[cpu] = NULL; + } + } +} + +int +get_lockmeter_info_size(void) +{ + return sizeof (lstat_user_request_t) + + num_online_cpus() * sizeof (lstat_cpu_counts_t) + + num_online_cpus() * sizeof (lstat_read_lock_cpu_counts_t) + + (LSTAT_MAX_STAT_INDEX * sizeof (lstat_directory_entry_t)); +} + +ssize_t +get_lockmeter_info(char *buffer, size_t max_len, loff_t * last_index) +{ + lstat_user_request_t req; + struct timeval tv; + ssize_t next_ret_bcount; + ssize_t actual_ret_bcount = 0; + int cpu; + + *last_index = 0; /* a one-shot read */ + + req.lstat_version = LSTAT_VERSION; + req.state = lstat_control.state; + req.maxcpus = num_online_cpus(); + req.cycleval = CPU_CYCLE_FREQUENCY; +#ifdef notyet + req.kernel_magic_addr = (void *) &_etext; + req.kernel_end_addr = (void *) &_etext; +#endif + req.uts = system_utsname; + req.intervals = lstat_control.intervals; + + req.first_started_time = lstat_control.first_started_time; + req.started_time = lstat_control.started_time; + req.started_cycles64 = lstat_control.started_cycles64; + + req.next_free_dir_index = lstat_control.next_free_dir_index; + req.next_free_read_lock_index = lstat_control.next_free_read_lock_index; + req.dir_overflow = lstat_control.dir_overflow; + req.rwlock_overflow = lstat_control.rwlock_overflow; + + if (lstat_control.state == LSTAT_OFF) { + if (req.intervals == 0) { + /* mesasurement is off and no valid data present */ + next_ret_bcount = sizeof (lstat_user_request_t); + req.enabled_cycles64 = 0; + + if ((actual_ret_bcount + next_ret_bcount) > max_len) + return actual_ret_bcount; + + copy_to_user(buffer, (void *) &req, next_ret_bcount); + actual_ret_bcount += next_ret_bcount; + return actual_ret_bcount; + } else { + /* + * measurement is off but valid data present + * fetch time info from lstat_control + */ + req.ending_time = lstat_control.ending_time; + req.ending_cycles64 = lstat_control.ending_cycles64; + req.enabled_cycles64 = lstat_control.enabled_cycles64; + } + } else { + /* + * this must be a read while data active--use current time, + * etc + */ + do_gettimeofday(&tv); + req.ending_time = tv.tv_sec; + req.ending_cycles64 = get_cycles64(); + req.enabled_cycles64 = req.ending_cycles64 - + req.started_cycles64 + lstat_control.enabled_cycles64; + } + + next_ret_bcount = sizeof (lstat_user_request_t); + if ((actual_ret_bcount + next_ret_bcount) > max_len) + return actual_ret_bcount; + + copy_to_user(buffer, (void *) &req, next_ret_bcount); + actual_ret_bcount += next_ret_bcount; + + if (!lstat_control.counts[0]) /* not initialized? */ + return actual_ret_bcount; + + next_ret_bcount = sizeof (lstat_cpu_counts_t); + for (cpu = 0; cpu < num_online_cpus(); cpu++) { + if ((actual_ret_bcount + next_ret_bcount) > max_len) + return actual_ret_bcount; /* leave early */ + copy_to_user(buffer + actual_ret_bcount, + lstat_control.counts[cpu], next_ret_bcount); + actual_ret_bcount += next_ret_bcount; + } + + next_ret_bcount = LSTAT_MAX_STAT_INDEX * + sizeof (lstat_directory_entry_t); + if (((actual_ret_bcount + next_ret_bcount) > max_len) + || !lstat_control.dir) + return actual_ret_bcount; /* leave early */ + + copy_to_user(buffer + actual_ret_bcount, lstat_control.dir, + next_ret_bcount); + actual_ret_bcount += next_ret_bcount; + + next_ret_bcount = sizeof (lstat_read_lock_cpu_counts_t); + for (cpu = 0; cpu < num_online_cpus(); cpu++) { + if (actual_ret_bcount + next_ret_bcount > max_len) + return actual_ret_bcount; + copy_to_user(buffer + actual_ret_bcount, + lstat_control.read_lock_counts[cpu], + next_ret_bcount); + actual_ret_bcount += next_ret_bcount; + } + + return actual_ret_bcount; +} + +/* + * Writing to the /proc lockmeter node enables or disables metering. + * based upon the first byte of the "written" data. + * The following values are defined: + * LSTAT_ON: 1st call: allocates storage, intializes and turns on measurement + * subsequent calls just turn on measurement + * LSTAT_OFF: turns off measurement + * LSTAT_RESET: resets statistics + * LSTAT_RELEASE: releases statistics storage + * + * This allows one to accumulate statistics over several lockstat runs: + * + * lockstat on + * lockstat off + * ...repeat above as desired... + * lockstat get + * ...now start a new set of measurements... + * lockstat reset + * lockstat on + * ... + * + */ +ssize_t +put_lockmeter_info(const char *buffer, size_t len) +{ + int error = 0; + int dirsize, countsize, read_lock_countsize, hashsize; + int cpu; + char put_char; + int i, read_lock_blocks; + unsigned long flags; + rwlock_t *lock_ptr; + struct timeval tv; + + if (len <= 0) + return -EINVAL; + + _raw_spin_lock(&lstat_control.control_lock); + + get_user(put_char, buffer); + switch (put_char) { + + case LSTAT_OFF: + if (lstat_control.state != LSTAT_OFF) { + /* + * To avoid seeing read lock hold times in an + * inconsisent state, we have to follow this protocol + * to turn off statistics + */ + local_irq_save(flags); + /* + * getting this lock will stop any read lock block + * allocations + */ + _raw_spin_lock(&lstat_control.directory_lock); + /* + * keep any more read lock blocks from being + * allocated + */ + lstat_control.state = LSTAT_OFF; + /* record how may read lock blocks there are */ + read_lock_blocks = + lstat_control.next_free_read_lock_index; + _raw_spin_unlock(&lstat_control.directory_lock); + /* now go through the list of read locks */ + cpu = THIS_CPU_NUMBER; + for (i = 1; i < read_lock_blocks; i++) { + lock_ptr = + (*lstat_control.read_lock_counts[cpu])[i]. + lock_ptr; + /* is this saved lock address still valid? */ + if (GET_RWINDEX(lock_ptr) == i) { + /* + * lock address appears to still be + * valid because we only hold one lock + * at a time, this can't cause a + * deadlock unless this is a lock held + * as part of the current system call + * path. At the moment there + * are no READ mode locks held to get + * here from user space, so we solve + * this by skipping locks held in + * write mode. + */ + if (RWLOCK_IS_WRITE_LOCKED(lock_ptr)) { + PUT_RWINDEX(lock_ptr, 0); + continue; + } + /* + * now we know there are no read + * holders of this lock! stop + * statistics collection for this + * lock + */ + _raw_write_lock(lock_ptr); + PUT_RWINDEX(lock_ptr, 0); + _raw_write_unlock(lock_ptr); + } + /* + * it may still be possible for the hold time + * sum to be negative e.g. if a lock is + * reallocated while "busy" we will have to fix + * this up in the data reduction program. + */ + } + local_irq_restore(flags); + lstat_control.intervals++; + lstat_control.ending_cycles64 = get_cycles64(); + lstat_control.enabled_cycles64 += + lstat_control.ending_cycles64 - + lstat_control.started_cycles64; + do_gettimeofday(&tv); + lstat_control.ending_time = tv.tv_sec; + /* + * don't deallocate the structures -- we may do a + * lockstat on to add to the data that is already + * there. Use LSTAT_RELEASE to release storage + */ + } else { + error = -EBUSY; /* already OFF */ + } + break; + + case LSTAT_ON: + if (lstat_control.state == LSTAT_OFF) { +#ifdef DEBUG_LOCKMETER + printk("put_lockmeter_info(cpu=%d): LSTAT_ON\n", + THIS_CPU_NUMBER); +#endif + lstat_control.next_free_dir_index = 1; /* 0 is for overflows */ + + dirsize = LSTAT_MAX_STAT_INDEX * + sizeof (lstat_directory_entry_t); + hashsize = + (1 + LSTAT_HASH_TABLE_SIZE) * sizeof (ushort); + countsize = sizeof (lstat_cpu_counts_t); + read_lock_countsize = + sizeof (lstat_read_lock_cpu_counts_t); +#ifdef DEBUG_LOCKMETER + printk(" dirsize:%d", dirsize); + printk(" hashsize:%d", hashsize); + printk(" countsize:%d", countsize); + printk(" read_lock_countsize:%d\n", + read_lock_countsize); +#endif +#ifdef DEBUG_LOCKMETER + { + int secs; + unsigned long cycles; + uint64_t cycles64; + + do_gettimeofday(&tv); + secs = tv.tv_sec; + do { + do_gettimeofday(&tv); + } while (secs == tv.tv_sec); + cycles = get_cycles(); + cycles64 = get_cycles64(); + secs = tv.tv_sec; + do { + do_gettimeofday(&tv); + } while (secs == tv.tv_sec); + cycles = get_cycles() - cycles; + cycles64 = get_cycles64() - cycles; + printk("lockmeter: cycleFrequency:%d " + "cycles:%d cycles64:%d\n", + CPU_CYCLE_FREQUENCY, cycles, cycles64); + } +#endif + + /* + * if this is the first call, allocate storage and + * initialize + */ + if (!lstat_control.hashtab) { + + spin_lock_init(&lstat_control.directory_lock); + + /* guarantee all pointers at zero */ + init_control_space(); + + lstat_control.hashtab = + kmalloc(hashsize, GFP_KERNEL); + if (!lstat_control.hashtab) { + error = -ENOSPC; +#ifdef DEBUG_LOCKMETER + printk("!!error kmalloc of hashtab\n"); +#endif + } + lstat_control.dir = vmalloc(dirsize); + if (!lstat_control.dir) { + error = -ENOSPC; +#ifdef DEBUG_LOCKMETER + printk("!!error kmalloc of dir\n"); +#endif + } + + for (cpu = 0; cpu < num_online_cpus(); cpu++) { + lstat_control.counts[cpu] = + vmalloc(countsize); + if (!lstat_control.counts[cpu]) { + error = -ENOSPC; +#ifdef DEBUG_LOCKMETER + printk("!!error vmalloc of " + "counts[%d]\n", cpu); +#endif + } + lstat_control.read_lock_counts[cpu] = + (lstat_read_lock_cpu_counts_t *) + kmalloc(read_lock_countsize, + GFP_KERNEL); + if (!lstat_control. + read_lock_counts[cpu]) { + error = -ENOSPC; +#ifdef DEBUG_LOCKMETER + printk("!!error kmalloc of " + "read_lock_counts[%d]\n", + cpu); +#endif + } + } + } + + if (error) { + /* + * One or more kmalloc failures -- free + * everything + */ + release_control_space(); + } else { + + if (!reset_lstat_data()) { + error = -EINVAL; + break; + }; + + /* + * record starting and ending times and the + * like + */ + if (lstat_control.intervals == 0) { + do_gettimeofday(&tv); + lstat_control.first_started_time = + tv.tv_sec; + } + lstat_control.started_cycles64 = get_cycles64(); + do_gettimeofday(&tv); + lstat_control.started_time = tv.tv_sec; + + lstat_control.state = LSTAT_ON; + } + } else { + error = -EBUSY; /* already ON */ + } + break; + + case LSTAT_RESET: + if (lstat_control.state == LSTAT_OFF) { + if (!reset_lstat_data()) + error = -EINVAL; + } else { + error = -EBUSY; /* still on; can't reset */ + } + break; + + case LSTAT_RELEASE: + if (lstat_control.state == LSTAT_OFF) { + release_control_space(); + lstat_control.intervals = 0; + lstat_control.enabled_cycles64 = 0; + } else { + error = -EBUSY; + } + break; + + default: + error = -EINVAL; + } /* switch */ + + _raw_spin_unlock(&lstat_control.control_lock); + return error ? error : len; +} + +#ifdef USER_MODE_TESTING +/* following used for user mode testing */ +void +lockmeter_init() +{ + int dirsize, hashsize, countsize, read_lock_countsize, cpu; + + printf("lstat_control is at %x size=%d\n", &lstat_control, + sizeof (lstat_control)); + printf("sizeof(spinlock_t)=%d\n", sizeof (spinlock_t)); + lstat_control.state = LSTAT_ON; + + lstat_control.directory_lock = SPIN_LOCK_UNLOCKED; + lstat_control.next_free_dir_index = 1; /* 0 is for overflows */ + lstat_control.next_free_read_lock_index = 1; + + dirsize = LSTAT_MAX_STAT_INDEX * sizeof (lstat_directory_entry_t); + hashsize = (1 + LSTAT_HASH_TABLE_SIZE) * sizeof (ushort); + countsize = sizeof (lstat_cpu_counts_t); + read_lock_countsize = sizeof (lstat_read_lock_cpu_counts_t); + + lstat_control.hashtab = (ushort *) malloc(hashsize); + + if (lstat_control.hashtab == 0) { + printf("malloc failure for at line %d in lockmeter.c\n", + __LINE__); + exit(0); + } + + lstat_control.dir = (lstat_directory_entry_t *) malloc(dirsize); + + if (lstat_control.dir == 0) { + printf("malloc failure for at line %d in lockmeter.c\n", cpu, + __LINE__); + exit(0); + } + + for (cpu = 0; cpu < num_online_cpus(); cpu++) { + int j, k; + j = (int) (lstat_control.counts[cpu] = + (lstat_cpu_counts_t *) malloc(countsize)); + k = (int) (lstat_control.read_lock_counts[cpu] = + (lstat_read_lock_cpu_counts_t *) + malloc(read_lock_countsize)); + if (j * k == 0) { + printf("malloc failure for cpu=%d at line %d in " + "lockmeter.c\n", cpu, __LINE__); + exit(0); + } + } + + memset(lstat_control.hashtab, 0, hashsize); + memset(lstat_control.dir, 0, dirsize); + + for (cpu = 0; cpu < num_online_cpus(); cpu++) { + memset(lstat_control.counts[cpu], 0, countsize); + memset(lstat_control.read_lock_counts[cpu], 0, + read_lock_countsize); + } +} + +asm(" \ +.align 4 \ +.globl __write_lock_failed \ +__write_lock_failed: \ + " LOCK "addl $" RW_LOCK_BIAS_STR ",(%eax) \ +1: cmpl $" RW_LOCK_BIAS_STR ",(%eax) \ + jne 1b \ +\ + " LOCK "subl $" RW_LOCK_BIAS_STR ",(%eax) \ + jnz __write_lock_failed \ + ret \ +\ +\ +.align 4 \ +.globl __read_lock_failed \ +__read_lock_failed: \ + lock ; incl (%eax) \ +1: cmpl $1,(%eax) \ + js 1b \ +\ + lock ; decl (%eax) \ + js __read_lock_failed \ + ret \ +"); +#endif + +EXPORT_SYMBOL(_metered_spin_lock); +EXPORT_SYMBOL(_metered_spin_unlock); +EXPORT_SYMBOL(_metered_spin_trylock); +EXPORT_SYMBOL(_metered_read_lock); +EXPORT_SYMBOL(_metered_read_unlock); +EXPORT_SYMBOL(_metered_write_lock); +EXPORT_SYMBOL(_metered_write_trylock); +EXPORT_SYMBOL(_metered_write_unlock); --- linux-2.6.9-rc1/kernel/Makefile 2004-08-24 00:02:23.000000000 -0700 +++ 25/kernel/Makefile 2004-09-03 00:57:14.975762600 -0700 @@ -7,22 +7,27 @@ obj-y = sched.o fork.o exec_domain.o sysctl.o capability.o ptrace.o timer.o user.o \ signal.o sys.o kmod.o workqueue.o pid.o \ rcupdate.o intermodule.o extable.o params.o posix-timers.o \ - kthread.o + kthread.o wait.o obj-$(CONFIG_FUTEX) += futex.o obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o obj-$(CONFIG_SMP) += cpu.o +obj-$(CONFIG_LOCKMETER) += lockmeter.o obj-$(CONFIG_UID16) += uid16.o obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_KALLSYMS) += kallsyms.o obj-$(CONFIG_PM) += power/ obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o +obj-$(CONFIG_KEXEC) += kexec.o obj-$(CONFIG_COMPAT) += compat.o +obj-$(CONFIG_CPUSETS) += cpuset.o obj-$(CONFIG_IKCONFIG) += configs.o obj-$(CONFIG_IKCONFIG_PROC) += configs.o obj-$(CONFIG_STOP_MACHINE) += stop_machine.o obj-$(CONFIG_AUDIT) += audit.o obj-$(CONFIG_AUDITSYSCALL) += auditsc.o +obj-$(CONFIG_KPROBES) += kprobes.o +obj-$(CONFIG_KERNEL_EVENTS) += kevent.o ifneq ($(CONFIG_IA64),y) # According to Alan Modra , the -fno-omit-frame-pointer is --- linux-2.6.9-rc1/kernel/module.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/kernel/module.c 2004-09-03 00:57:27.230899536 -0700 @@ -89,13 +89,6 @@ static inline int strong_try_module_get( return try_module_get(mod); } -/* Stub function for modules which don't have an initfn */ -int init_module(void) -{ - return 0; -} -EXPORT_SYMBOL(init_module); - /* A thread that wants to hold a reference to a module only while it * is running can call ths to safely exit. * nfsd and lockd use this. @@ -529,12 +522,6 @@ EXPORT_SYMBOL(module_refcount); /* This exists whether we can unload or not */ static void free_module(struct module *mod); -/* Stub function for modules which don't have an exitfn */ -void cleanup_module(void) -{ -} -EXPORT_SYMBOL(cleanup_module); - static void wait_for_zero_refcount(struct module *mod) { /* Since we might sleep for some time, drop the semaphore first */ @@ -589,7 +576,7 @@ sys_delete_module(const char __user *nam } /* If it has an init func, it must have an exit func to unload */ - if ((mod->init != init_module && mod->exit == cleanup_module) + if ((mod->init != NULL && mod->exit == NULL) || mod->unsafe) { forced = try_force(flags); if (!forced) { @@ -610,9 +597,11 @@ sys_delete_module(const char __user *nam wait_for_zero_refcount(mod); /* Final destruction now noone is using it. */ - up(&module_mutex); - mod->exit(); - down(&module_mutex); + if (mod->exit != NULL) { + up(&module_mutex); + mod->exit(); + down(&module_mutex); + } free_module(mod); out: @@ -639,7 +628,7 @@ static void print_unload_info(struct seq seq_printf(m, "[unsafe],"); } - if (mod->init != init_module && mod->exit == cleanup_module) { + if (mod->init != NULL && mod->exit == NULL) { printed_something = 1; seq_printf(m, "[permanent],"); } @@ -1538,9 +1527,6 @@ static struct module *load_module(void _ secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; sechdrs[0].sh_addr = 0; - /* And these should exist, but gcc whinges if we don't init them */ - symindex = strindex = 0; - for (i = 1; i < hdr->e_shnum; i++) { if (sechdrs[i].sh_type != SHT_NOBITS && len < sechdrs[i].sh_offset + sechdrs[i].sh_size) @@ -1572,6 +1558,13 @@ static struct module *load_module(void _ } mod = (void *)sechdrs[modindex].sh_addr; + if (symindex == 0) { + printk(KERN_WARNING "%s: module has no symbols (stripped?)\n", + mod->name); + err = -ENOEXEC; + goto free_hdr; + } + /* Optional sections */ exportindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab"); gplindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_gpl"); @@ -1832,7 +1825,7 @@ sys_init_module(void __user *umod, const char __user *uargs) { struct module *mod; - int ret; + int ret = 0; /* Must have permission */ if (!capable(CAP_SYS_MODULE)) @@ -1871,7 +1864,8 @@ sys_init_module(void __user *umod, up(¬ify_mutex); /* Start the module */ - ret = mod->init(); + if (mod->init != NULL) + ret = mod->init(); if (ret < 0) { /* Init routine failed: abort. Try to protect us from buggy refcounters. */ --- linux-2.6.9-rc1/kernel/pid.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/kernel/pid.c 2004-09-03 00:56:42.286732088 -0700 @@ -27,7 +27,7 @@ #include #define pid_hashfn(nr) hash_long((unsigned long)nr, pidhash_shift) -static struct list_head *pid_hash[PIDTYPE_MAX]; +static struct hlist_head *pid_hash[PIDTYPE_MAX]; static int pidhash_shift; int pid_max = PID_MAX_DEFAULT; @@ -122,6 +122,8 @@ return_pid: } if (!offset || !atomic_read(&map->nr_free)) { + if (!offset) + map--; next_map: map = next_free_map(map, &max_steps); if (!map) @@ -146,73 +148,66 @@ failure: return -1; } -fastcall struct pid *find_pid(enum pid_type type, int nr) +struct pid * fastcall find_pid(enum pid_type type, int nr) { - struct list_head *elem, *bucket = &pid_hash[type][pid_hashfn(nr)]; + struct hlist_node *elem; struct pid *pid; - __list_for_each(elem, bucket) { - pid = list_entry(elem, struct pid, hash_chain); + hlist_for_each_entry(pid, elem, + &pid_hash[type][pid_hashfn(nr)], pid_chain) { if (pid->nr == nr) return pid; } return NULL; } -void fastcall link_pid(task_t *task, struct pid_link *link, struct pid *pid) -{ - atomic_inc(&pid->count); - list_add_tail(&link->pid_chain, &pid->task_list); - link->pidptr = pid; -} - int fastcall attach_pid(task_t *task, enum pid_type type, int nr) { - struct pid *pid = find_pid(type, nr); + struct pid *pid, *task_pid; - if (pid) - atomic_inc(&pid->count); - else { - pid = &task->pids[type].pid; - pid->nr = nr; - atomic_set(&pid->count, 1); - INIT_LIST_HEAD(&pid->task_list); - pid->task = task; - get_task_struct(task); - list_add(&pid->hash_chain, &pid_hash[type][pid_hashfn(nr)]); + task_pid = &task->pids[type]; + pid = find_pid(type, nr); + if (pid == NULL) { + hlist_add_head(&task_pid->pid_chain, + &pid_hash[type][pid_hashfn(nr)]); + INIT_LIST_HEAD(&task_pid->pid_list); + } else { + INIT_HLIST_NODE(&task_pid->pid_chain); + list_add_tail(&task_pid->pid_list, &pid->pid_list); } - list_add_tail(&task->pids[type].pid_chain, &pid->task_list); - task->pids[type].pidptr = pid; + task_pid->nr = nr; return 0; } static inline int __detach_pid(task_t *task, enum pid_type type) { - struct pid_link *link = task->pids + type; - struct pid *pid = link->pidptr; + struct pid *pid, *pid_next; int nr; - list_del(&link->pid_chain); - if (!atomic_dec_and_test(&pid->count)) - return 0; - + pid = &task->pids[type]; + if (!hlist_unhashed(&pid->pid_chain)) { + hlist_del(&pid->pid_chain); + if (!list_empty(&pid->pid_list)) { + pid_next = list_entry(pid->pid_list.next, + struct pid, pid_list); + /* insert next pid from pid_list to hash */ + hlist_add_head(&pid_next->pid_chain, + &pid_hash[type][pid_hashfn(pid_next->nr)]); + } + } + list_del(&pid->pid_list); nr = pid->nr; - list_del(&pid->hash_chain); - put_task_struct(pid->task); + pid->nr = 0; return nr; } -static void _detach_pid(task_t *task, enum pid_type type) -{ - __detach_pid(task, type); -} - void fastcall detach_pid(task_t *task, enum pid_type type) { - int nr = __detach_pid(task, type); + int nr; + nr = __detach_pid(task, type); if (!nr) return; @@ -222,16 +217,18 @@ void fastcall detach_pid(task_t *task, e free_pidmap(nr); } -task_t *find_task_by_pid(int nr) +task_t *find_task_by_pid_type(int type, int nr) { - struct pid *pid = find_pid(PIDTYPE_PID, nr); + struct pid *pid; + pid = find_pid(type, nr); if (!pid) return NULL; - return pid_task(pid->task_list.next, PIDTYPE_PID); + + return pid_task(&pid->pid_list, type); } -EXPORT_SYMBOL(find_task_by_pid); +EXPORT_SYMBOL(find_task_by_pid_type); /* * This function switches the PIDs if a non-leader thread calls @@ -240,13 +237,13 @@ EXPORT_SYMBOL(find_task_by_pid); */ void switch_exec_pids(task_t *leader, task_t *thread) { - _detach_pid(leader, PIDTYPE_PID); - _detach_pid(leader, PIDTYPE_TGID); - _detach_pid(leader, PIDTYPE_PGID); - _detach_pid(leader, PIDTYPE_SID); + __detach_pid(leader, PIDTYPE_PID); + __detach_pid(leader, PIDTYPE_TGID); + __detach_pid(leader, PIDTYPE_PGID); + __detach_pid(leader, PIDTYPE_SID); - _detach_pid(thread, PIDTYPE_PID); - _detach_pid(thread, PIDTYPE_TGID); + __detach_pid(thread, PIDTYPE_PID); + __detach_pid(thread, PIDTYPE_TGID); leader->pid = leader->tgid = thread->pid; thread->pid = thread->tgid; @@ -268,18 +265,21 @@ void switch_exec_pids(task_t *leader, ta * machine. From a minimum of 16 slots up to 4096 slots at one gigabyte or * more. */ +#ifdef CONFIG_KGDB +int kgdb_pid_init_done; /* so we don't call prior to... */ +#endif void __init pidhash_init(void) { int i, j, pidhash_size; - unsigned long megabytes = max_pfn >> (20 - PAGE_SHIFT); + unsigned long megabytes = nr_kernel_pages >> (20 - PAGE_SHIFT); pidhash_shift = max(4, fls(megabytes * 4)); pidhash_shift = min(12, pidhash_shift); pidhash_size = 1 << pidhash_shift; - printk("PID hash table entries: %d (order %d: %Zd bytes)\n", + printk("PID hash table entries: %d (order: %d, %Zd bytes)\n", pidhash_size, pidhash_shift, - pidhash_size * sizeof(struct list_head)); + PIDTYPE_MAX * pidhash_size * sizeof(struct hlist_head)); for (i = 0; i < PIDTYPE_MAX; i++) { pid_hash[i] = alloc_bootmem(pidhash_size * @@ -287,8 +287,11 @@ void __init pidhash_init(void) if (!pid_hash[i]) panic("Could not alloc pidhash!\n"); for (j = 0; j < pidhash_size; j++) - INIT_LIST_HEAD(&pid_hash[i][j]); + INIT_HLIST_HEAD(&pid_hash[i][j]); } +#ifdef CONFIG_KGDB + kgdb_pid_init_done++; +#endif } void __init pidmap_init(void) --- linux-2.6.9-rc1/kernel/posix-timers.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/kernel/posix-timers.c 2004-09-03 00:56:30.926459112 -0700 @@ -219,6 +219,11 @@ static __init int init_posix_timers(void .clock_set = do_posix_clock_monotonic_settime }; +#ifdef CONFIG_TIME_INTERPOLATION + /* Clocks are more accurate with time interpolators */ + clock_realtime.res = clock_monotonic.res = NSEC_PER_SEC/time_interpolator->frequency; +#endif + register_posix_clock(CLOCK_REALTIME, &clock_realtime); register_posix_clock(CLOCK_MONOTONIC, &clock_monotonic); --- linux-2.6.9-rc1/kernel/power/disk.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/kernel/power/disk.c 2004-09-03 00:56:34.829865704 -0700 @@ -8,28 +8,30 @@ * */ -#define DEBUG - - #include #include #include #include +#include #include #include +#include #include "power.h" -extern u32 pm_disk_mode; +extern enum suspend_disk_method pm_disk_mode; extern struct pm_ops * pm_ops; -extern int pmdisk_save(void); -extern int pmdisk_write(void); -extern int pmdisk_read(void); -extern int pmdisk_restore(void); -extern int pmdisk_free(void); +extern int swsusp_suspend(void); +extern int swsusp_write(void); +extern int swsusp_read(void); +extern int swsusp_resume(void); +extern int swsusp_free(void); +static int noresume = 0; +char resume_file[256] = CONFIG_PM_STD_PARTITION; + /** * power_down - Shut machine down for hibernate. * @mode: Suspend-to-disk mode @@ -40,28 +42,37 @@ extern int pmdisk_free(void); * there ain't no turning back. */ -static int power_down(u32 mode) +static int power_down(enum suspend_disk_method mode) { unsigned long flags; int error = 0; local_irq_save(flags); - device_power_down(PM_SUSPEND_DISK); switch(mode) { case PM_DISK_PLATFORM: + system_state = SYSTEM_DISK_SUSPEND; + device_power_down(PM_SUSPEND_DISK); error = pm_ops->enter(PM_SUSPEND_DISK); break; case PM_DISK_SHUTDOWN: + system_state = SYSTEM_POWER_OFF; printk("Powering off system\n"); + device_shutdown(); machine_power_off(); break; case PM_DISK_REBOOT: + system_state = SYSTEM_RESTART; + device_shutdown(); machine_restart(NULL); break; + default: + BUG(); } machine_halt(); - device_power_up(); - local_irq_restore(flags); + /* Valid image is on the disk, if we continue we risk serious data corruption + after resume. */ + printk("Please power me down manually\n"); + while(1); return 0; } @@ -99,6 +110,7 @@ static void finish(void) { device_resume(); platform_finish(); + enable_nonboot_cpus(); thaw_processes(); pm_restore_console(); } @@ -108,6 +120,7 @@ static int prepare(void) { int error; + system_state = SYSTEM_SNAPSHOT; pm_prepare_console(); sys_sync(); @@ -126,6 +139,7 @@ static int prepare(void) /* Free memory before shutting down devices. */ free_some_memory(); + disable_nonboot_cpus(); if ((error = device_suspend(PM_SUSPEND_DISK))) goto Finish; @@ -133,6 +147,7 @@ static int prepare(void) Finish: platform_finish(); Thaw: + enable_nonboot_cpus(); thaw_processes(); pm_restore_console(); return error; @@ -161,7 +176,7 @@ int pm_suspend_disk(void) pr_debug("PM: snapshotting memory.\n"); in_suspend = 1; - if ((error = pmdisk_save())) + if ((error = swsusp_suspend())) goto Done; if (in_suspend) { @@ -173,14 +188,14 @@ int pm_suspend_disk(void) mb(); barrier(); - error = pmdisk_write(); + error = swsusp_write(); if (!error) { error = power_down(pm_disk_mode); pr_debug("PM: Power down failed.\n"); } } else pr_debug("PM: Image restored successfully.\n"); - pmdisk_free(); + swsusp_free(); Done: finish(); return error; @@ -188,7 +203,7 @@ int pm_suspend_disk(void) /** - * pm_resume - Resume from a saved image. + * software_resume - Resume from a saved image. * * Called as a late_initcall (so all devices are discovered and * initialized), we call pmdisk to see if we have a saved image or not. @@ -199,45 +214,44 @@ int pm_suspend_disk(void) * */ -static int pm_resume(void) +static int software_resume(void) { int error; + if (noresume) { + /** + * FIXME: If noresume is specified, we need to find the partition + * and reset it back to normal swap space. + */ + return 0; + } + pr_debug("PM: Reading pmdisk image.\n"); - if ((error = pmdisk_read())) + if ((error = swsusp_read())) goto Done; pr_debug("PM: Preparing system for restore.\n"); + system_state = SYSTEM_SNAPSHOT; if ((error = prepare())) goto Free; barrier(); mb(); - /* FIXME: The following (comment and mdelay()) are from swsusp. - * Are they really necessary? - * - * We do not want some readahead with DMA to corrupt our memory, right? - * Do it with disabled interrupts for best effect. That way, if some - * driver scheduled DMA, we have good chance for DMA to finish ;-). - */ - pr_debug("PM: Waiting for DMAs to settle down.\n"); - mdelay(1000); - pr_debug("PM: Restoring saved image.\n"); - pmdisk_restore(); + swsusp_resume(); pr_debug("PM: Restore failed, recovering.n"); finish(); Free: - pmdisk_free(); + swsusp_free(); Done: pr_debug("PM: Resume from disk failed.\n"); return 0; } -late_initcall(pm_resume); +late_initcall(software_resume); static char * pm_disk_modes[] = { @@ -286,7 +300,7 @@ static ssize_t disk_store(struct subsyst int i; int len; char *p; - u32 mode = 0; + enum suspend_disk_method mode = 0; p = memchr(buf, '\n', n); len = p ? p - buf : n; @@ -336,3 +350,22 @@ static int __init pm_disk_init(void) } core_initcall(pm_disk_init); + + +static int __init resume_setup(char *str) +{ + if (noresume) + return 1; + + strncpy( resume_file, str, 255 ); + return 1; +} + +static int __init noresume_setup(char *str) +{ + noresume = 1; + return 1; +} + +__setup("noresume", noresume_setup); +__setup("resume=", resume_setup); --- linux-2.6.9-rc1/kernel/power/Kconfig 2004-08-24 00:03:31.000000000 -0700 +++ 25/kernel/power/Kconfig 2004-09-03 00:56:33.441076832 -0700 @@ -18,6 +18,13 @@ config PM will issue the hlt instruction if nothing is to be done, thereby sending the processor to sleep and saving power. +config PM_DEBUG + bool "Power Management Debug Support" + ---help--- + This option enables verbose debugging support in the Power Management + code. This is helpful when debugging and reporting various PM bugs, + like suspend support. + config SOFTWARE_SUSPEND bool "Software Suspend (EXPERIMENTAL)" depends on EXPERIMENTAL && PM && SWAP @@ -42,33 +49,12 @@ config SOFTWARE_SUSPEND For more information take a look at Documentation/power/swsusp.txt. -config PM_DISK - bool "Suspend-to-Disk Support" - depends on PM && SWAP && X86 && !X86_64 - ---help--- - Suspend-to-disk is a power management state in which the contents - of memory are stored on disk and the entire system is shut down or - put into a low-power state (e.g. ACPI S4). When the computer is - turned back on, the stored image is loaded from disk and execution - resumes from where it left off before suspending. - - This config option enables the core infrastructure necessary to - perform the suspend and resume transition. - - Currently, this suspend-to-disk implementation is based on a forked - version of the swsusp code base. As such, it's still experimental, - and still relies on CONFIG_SWAP. - - More information can be found in Documentation/power/. - - If unsure, Say N. - -config PM_DISK_PARTITION +config PM_STD_PARTITION string "Default resume partition" - depends on PM_DISK + depends on SOFTWARE_SUSPEND default "" ---help--- - The default resume partition is the partition that the pmdisk suspend- + The default resume partition is the partition that the suspend- to-disk implementation will look for a suspended disk image. The partition specified here will be different for almost every user. @@ -77,16 +63,10 @@ config PM_DISK_PARTITION The partition specified can be overridden by specifying: - pmdisk=/dev/ + resume=/dev/ which will set the resume partition to the device specified. - One may also do: - - pmdisk=off - - to inform the kernel not to perform a resume transition. - Note there is currently not a way to specify which device to save the suspended image to. It will simply pick the first available swap device. --- linux-2.6.9-rc1/kernel/power/main.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/kernel/power/main.c 2004-09-03 00:56:34.830865552 -0700 @@ -8,8 +8,6 @@ * */ -#define DEBUG - #include #include #include @@ -24,7 +22,7 @@ DECLARE_MUTEX(pm_sem); struct pm_ops * pm_ops = NULL; -u32 pm_disk_mode = PM_DISK_SHUTDOWN; +enum suspend_disk_method pm_disk_mode = PM_DISK_SHUTDOWN; /** * pm_set_ops - Set the global power method table. @@ -35,8 +33,6 @@ void pm_set_ops(struct pm_ops * ops) { down(&pm_sem); pm_ops = ops; - if (ops->pm_disk_mode && ops->pm_disk_mode < PM_DISK_MAX) - pm_disk_mode = ops->pm_disk_mode; up(&pm_sem); } @@ -50,7 +46,7 @@ void pm_set_ops(struct pm_ops * ops) * the platform can enter the requested state. */ -static int suspend_prepare(u32 state) +static int suspend_prepare(enum suspend_state state) { int error = 0; @@ -69,7 +65,11 @@ static int suspend_prepare(u32 state) goto Thaw; } - if ((error = device_suspend(state))) + /* FIXME: this is suspend confusion biting us. If we pass + state, we'll pass 2 in suspend-to-RAM case; EHCI will + actually break suspend, because it interprets 2 as PCI_D2 + state. Oops. */ + if ((error = device_suspend(3))) goto Finish; return 0; Finish: @@ -82,13 +82,14 @@ static int suspend_prepare(u32 state) } -static int suspend_enter(u32 state) +static int suspend_enter(enum suspend_state state) { int error = 0; unsigned long flags; local_irq_save(flags); - if ((error = device_power_down(state))) + /* FIXME: see suspend_prepare */ + if ((error = device_power_down(3))) goto Done; error = pm_ops->enter(state); device_power_up(); @@ -106,7 +107,7 @@ static int suspend_enter(u32 state) * console that we've allocated. */ -static void suspend_finish(u32 state) +static void suspend_finish(enum suspend_state state) { device_resume(); if (pm_ops && pm_ops->finish) @@ -137,7 +138,7 @@ char * pm_states[] = { * we've woken up). */ -static int enter_state(u32 state) +static int enter_state(enum suspend_state state) { int error; @@ -155,6 +156,7 @@ static int enter_state(u32 state) goto Unlock; } + system_state = SYSTEM_RAM_SUSPEND; pr_debug("PM: Preparing system for suspend\n"); if ((error = suspend_prepare(state))) goto Unlock; @@ -166,9 +168,19 @@ static int enter_state(u32 state) suspend_finish(state); Unlock: up(&pm_sem); + system_state = SYSTEM_RUNNING; return error; } +/* + * This is main interface to the outside world. It needs to be + * called from process context. + */ +int software_suspend(void) +{ + return enter_state(PM_SUSPEND_DISK); +} + /** * pm_suspend - Externally visible function for suspending system. @@ -178,7 +190,7 @@ static int enter_state(u32 state) * structure, and enter (above). */ -int pm_suspend(u32 state) +int pm_suspend(enum suspend_state state) { if (state > PM_SUSPEND_ON && state < PM_SUSPEND_MAX) return enter_state(state); @@ -216,7 +228,7 @@ static ssize_t state_show(struct subsyst static ssize_t state_store(struct subsystem * subsys, const char * buf, size_t n) { - u32 state = PM_SUSPEND_STANDBY; + enum suspend_state state = PM_SUSPEND_STANDBY; char ** s; char *p; int error; --- linux-2.6.9-rc1/kernel/power/Makefile 2004-08-24 00:03:30.000000000 -0700 +++ 25/kernel/power/Makefile 2004-09-03 00:56:33.441076832 -0700 @@ -1,8 +1,11 @@ +ifeq ($(CONFIG_PM_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG +endif + swsusp-smp-$(CONFIG_SMP) += smp.o obj-y := main.o process.o console.o pm.o -obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o $(swsusp-smp-y) -obj-$(CONFIG_PM_DISK) += disk.o pmdisk.o +obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o $(swsusp-smp-y) disk.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o --- linux-2.6.9-rc1/kernel/power/pmdisk.c 2004-08-24 00:02:48.000000000 -0700 +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,1166 +0,0 @@ -/* - * kernel/power/pmdisk.c - Suspend-to-disk implmentation - * - * This STD implementation is initially derived from swsusp (suspend-to-swap). - * The original copyright on that was: - * - * Copyright (C) 1998-2001 Gabor Kuti - * Copyright (C) 1998,2001,2002 Pavel Machek - * - * The additional parts are: - * - * Copyright (C) 2003 Patrick Mochel - * Copyright (C) 2003 Open Source Development Lab - * - * This file is released under the GPLv2. - * - * For more information, please see the text files in Documentation/power/ - * - */ - -#undef DEBUG - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "power.h" - - -extern asmlinkage int pmdisk_arch_suspend(int resume); - -#define __ADDRESS(x) ((unsigned long) phys_to_virt(x)) -#define ADDRESS(x) __ADDRESS((x) << PAGE_SHIFT) -#define ADDRESS2(x) __ADDRESS(__pa(x)) /* Needed for x86-64 where some pages are in memory twice */ - -/* References to section boundaries */ -extern char __nosave_begin, __nosave_end; - -extern int is_head_of_free_region(struct page *); - -/* Variables to be preserved over suspend */ -static int pagedir_order_check; -static int nr_copy_pages_check; - -/* For resume= kernel option */ -static char resume_file[256] = CONFIG_PM_DISK_PARTITION; - -static dev_t resume_device; -/* Local variables that should not be affected by save */ -unsigned int pmdisk_pages __nosavedata = 0; - -/* Suspend pagedir is allocated before final copy, therefore it - must be freed after resume - - Warning: this is evil. There are actually two pagedirs at time of - resume. One is "pagedir_save", which is empty frame allocated at - time of suspend, that must be freed. Second is "pagedir_nosave", - allocated at time of resume, that travels through memory not to - collide with anything. - */ -suspend_pagedir_t *pm_pagedir_nosave __nosavedata = NULL; -static suspend_pagedir_t *pagedir_save; -static int pagedir_order __nosavedata = 0; - - -struct pmdisk_info { - struct new_utsname uts; - u32 version_code; - unsigned long num_physpages; - int cpus; - unsigned long image_pages; - unsigned long pagedir_pages; - swp_entry_t pagedir[768]; -} __attribute__((aligned(PAGE_SIZE))) pmdisk_info; - - - -#define PMDISK_SIG "pmdisk-swap1" - -struct pmdisk_header { - char reserved[PAGE_SIZE - 20 - sizeof(swp_entry_t)]; - swp_entry_t pmdisk_info; - char orig_sig[10]; - char sig[10]; -} __attribute__((packed, aligned(PAGE_SIZE))) pmdisk_header; - -/* - * XXX: We try to keep some more pages free so that I/O operations succeed - * without paging. Might this be more? - */ -#define PAGES_FOR_IO 512 - - -/* - * Saving part... - */ - - -/* We memorize in swapfile_used what swap devices are used for suspension */ -#define SWAPFILE_UNUSED 0 -#define SWAPFILE_SUSPEND 1 /* This is the suspending device */ -#define SWAPFILE_IGNORED 2 /* Those are other swap devices ignored for suspension */ - -static unsigned short swapfile_used[MAX_SWAPFILES]; -static unsigned short root_swap; - - -static int mark_swapfiles(swp_entry_t prev) -{ - int error; - - rw_swap_page_sync(READ, - swp_entry(root_swap, 0), - virt_to_page((unsigned long)&pmdisk_header)); - if (!memcmp("SWAP-SPACE",pmdisk_header.sig,10) || - !memcmp("SWAPSPACE2",pmdisk_header.sig,10)) { - memcpy(pmdisk_header.orig_sig,pmdisk_header.sig,10); - memcpy(pmdisk_header.sig,PMDISK_SIG,10); - pmdisk_header.pmdisk_info = prev; - error = rw_swap_page_sync(WRITE, - swp_entry(root_swap, 0), - virt_to_page((unsigned long) - &pmdisk_header)); - } else { - pr_debug("pmdisk: Partition is not swap space.\n"); - error = -ENODEV; - } - return error; -} - -static int read_swapfiles(void) /* This is called before saving image */ -{ - int i, len; - - len=strlen(resume_file); - root_swap = 0xFFFF; - - swap_list_lock(); - for(i=0; iswap_address; - if (entry.val) - swap_free(entry); - else - break; - (pm_pagedir_nosave + i)->swap_address = (swp_entry_t){0}; - } -} - - -/** - * write_data - Write saved image to swap. - * - * Walk the list of pages in the image and sync each one to swap. - */ - -static int write_data(void) -{ - int error = 0; - int i; - - printk( "Writing data to swap (%d pages): ", pmdisk_pages ); - for (i = 0; i < pmdisk_pages && !error; i++) { - if (!(i%100)) - printk( "." ); - error = write_swap_page((pm_pagedir_nosave+i)->address, - &((pm_pagedir_nosave+i)->swap_address)); - } - printk(" %d Pages done.\n",i); - return error; -} - - -/** - * free_pagedir - Free pages used by the page directory. - */ - -static void free_pagedir_entries(void) -{ - int num = pmdisk_info.pagedir_pages; - int i; - - for (i = 0; i < num; i++) - swap_free(pmdisk_info.pagedir[i]); -} - - -/** - * write_pagedir - Write the array of pages holding the page directory. - * @last: Last swap entry we write (needed for header). - */ - -static int write_pagedir(void) -{ - unsigned long addr = (unsigned long)pm_pagedir_nosave; - int error = 0; - int n = SUSPEND_PD_PAGES(pmdisk_pages); - int i; - - pmdisk_info.pagedir_pages = n; - printk( "Writing pagedir (%d pages)\n", n); - for (i = 0; i < n && !error; i++, addr += PAGE_SIZE) - error = write_swap_page(addr,&pmdisk_info.pagedir[i]); - return error; -} - - -#ifdef DEBUG -static void dump_pmdisk_info(void) -{ - printk(" pmdisk: Version: %u\n",pmdisk_info.version_code); - printk(" pmdisk: Num Pages: %ld\n",pmdisk_info.num_physpages); - printk(" pmdisk: UTS Sys: %s\n",pmdisk_info.uts.sysname); - printk(" pmdisk: UTS Node: %s\n",pmdisk_info.uts.nodename); - printk(" pmdisk: UTS Release: %s\n",pmdisk_info.uts.release); - printk(" pmdisk: UTS Version: %s\n",pmdisk_info.uts.version); - printk(" pmdisk: UTS Machine: %s\n",pmdisk_info.uts.machine); - printk(" pmdisk: UTS Domain: %s\n",pmdisk_info.uts.domainname); - printk(" pmdisk: CPUs: %d\n",pmdisk_info.cpus); - printk(" pmdisk: Image: %ld Pages\n",pmdisk_info.image_pages); - printk(" pmdisk: Pagedir: %ld Pages\n",pmdisk_info.pagedir_pages); -} -#else -static void dump_pmdisk_info(void) -{ - -} -#endif - -static void init_header(void) -{ - memset(&pmdisk_info,0,sizeof(pmdisk_info)); - pmdisk_info.version_code = LINUX_VERSION_CODE; - pmdisk_info.num_physpages = num_physpages; - memcpy(&pmdisk_info.uts,&system_utsname,sizeof(system_utsname)); - - pmdisk_info.cpus = num_online_cpus(); - pmdisk_info.image_pages = pmdisk_pages; -} - -/** - * write_header - Fill and write the suspend header. - * @entry: Location of the last swap entry used. - * - * Allocate a page, fill header, write header. - * - * @entry is the location of the last pagedir entry written on - * entrance. On exit, it contains the location of the header. - */ - -static int write_header(swp_entry_t * entry) -{ - dump_pmdisk_info(); - return write_swap_page((unsigned long)&pmdisk_info,entry); -} - - - -/** - * write_suspend_image - Write entire image and metadata. - * - */ - -static int write_suspend_image(void) -{ - int error; - swp_entry_t prev = { 0 }; - - init_header(); - - if ((error = write_data())) - goto FreeData; - - if ((error = write_pagedir())) - goto FreePagedir; - - if ((error = write_header(&prev))) - goto FreePagedir; - - error = mark_swapfiles(prev); - Done: - return error; - FreePagedir: - free_pagedir_entries(); - FreeData: - free_data(); - goto Done; -} - - - -/** - * saveable - Determine whether a page should be cloned or not. - * @pfn: The page - * - * We save a page if it's Reserved, and not in the range of pages - * statically defined as 'unsaveable', or if it isn't reserved, and - * isn't part of a free chunk of pages. - * If it is part of a free chunk, we update @pfn to point to the last - * page of the chunk. - */ - -static int saveable(unsigned long * pfn) -{ - struct page * page = pfn_to_page(*pfn); - - if (PageNosave(page)) - return 0; - - if (!PageReserved(page)) { - int chunk_size; - - if ((chunk_size = is_head_of_free_region(page))) { - *pfn += chunk_size - 1; - return 0; - } - } else if (PageReserved(page)) { - /* Just copy whole code segment. - * Hopefully it is not that big. - */ - if ((ADDRESS(*pfn) >= (unsigned long) ADDRESS2(&__nosave_begin)) && - (ADDRESS(*pfn) < (unsigned long) ADDRESS2(&__nosave_end))) { - pr_debug("[nosave %lx]\n", ADDRESS(*pfn)); - return 0; - } - /* Hmm, perhaps copying all reserved pages is not - * too healthy as they may contain - * critical bios data? - */ - } - return 1; -} - - - -/** - * count_pages - Determine size of page directory. - * - * Iterate over all the pages in the system and tally the number - * we need to clone. - */ - -static void count_pages(void) -{ - unsigned long pfn; - int n = 0; - - for (pfn = 0; pfn < max_pfn; pfn++) { - if (saveable(&pfn)) - n++; - } - pmdisk_pages = n; -} - - -/** - * copy_pages - Atomically snapshot memory. - * - * Iterate over all the pages in the system and copy each one - * into its corresponding location in the pagedir. - * We rely on the fact that the number of pages that we're snap- - * shotting hasn't changed since we counted them. - */ - -static void copy_pages(void) -{ - struct pbe * p = pagedir_save; - unsigned long pfn; - int n = 0; - - for (pfn = 0; pfn < max_pfn; pfn++) { - if (saveable(&pfn)) { - n++; - p->orig_address = ADDRESS(pfn); - copy_page((void *) p->address, - (void *) p->orig_address); - p++; - } - } - BUG_ON(n != pmdisk_pages); -} - - -/** - * free_image_pages - Free each page allocated for snapshot. - */ - -static void free_image_pages(void) -{ - struct pbe * p; - int i; - - for (i = 0, p = pagedir_save; i < pmdisk_pages; i++, p++) { - ClearPageNosave(virt_to_page(p->address)); - free_page(p->address); - } -} - - -/** - * free_pagedir - Free the page directory. - */ - -static void free_pagedir(void) -{ - free_image_pages(); - free_pages((unsigned long)pagedir_save, pagedir_order); -} - - -static void calc_order(void) -{ - int diff; - int order; - - order = get_bitmask_order(SUSPEND_PD_PAGES(pmdisk_pages)); - pmdisk_pages += 1 << order; - do { - diff = get_bitmask_order(SUSPEND_PD_PAGES(pmdisk_pages)) - order; - if (diff) { - order += diff; - pmdisk_pages += 1 << diff; - } - } while(diff); - pagedir_order = order; -} - - -/** - * alloc_pagedir - Allocate the page directory. - * - * First, determine exactly how many contiguous pages we need, - * allocate them, then mark each 'unsavable'. - */ - -static int alloc_pagedir(void) -{ - calc_order(); - pagedir_save = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC | __GFP_COLD, - pagedir_order); - if(!pagedir_save) - return -ENOMEM; - memset(pagedir_save,0,(1 << pagedir_order) * PAGE_SIZE); - pm_pagedir_nosave = pagedir_save; - return 0; -} - - -/** - * alloc_image_pages - Allocate pages for the snapshot. - * - */ - -static int alloc_image_pages(void) -{ - struct pbe * p; - int i; - - for (i = 0, p = pagedir_save; i < pmdisk_pages; i++, p++) { - p->address = get_zeroed_page(GFP_ATOMIC | __GFP_COLD); - if(!p->address) - goto Error; - SetPageNosave(virt_to_page(p->address)); - } - return 0; - Error: - do { - if (p->address) - free_page(p->address); - p->address = 0; - } while (p-- > pagedir_save); - return -ENOMEM; -} - - -/** - * enough_free_mem - Make sure we enough free memory to snapshot. - * - * Returns TRUE or FALSE after checking the number of available - * free pages. - */ - -static int enough_free_mem(void) -{ - if(nr_free_pages() < (pmdisk_pages + PAGES_FOR_IO)) { - pr_debug("pmdisk: Not enough free pages: Have %d\n", - nr_free_pages()); - return 0; - } - return 1; -} - - -/** - * enough_swap - Make sure we have enough swap to save the image. - * - * Returns TRUE or FALSE after checking the total amount of swap - * space avaiable. - * - * FIXME: si_swapinfo(&i) returns all swap devices information. - * We should only consider resume_device. - */ - -static int enough_swap(void) -{ - struct sysinfo i; - - si_swapinfo(&i); - if (i.freeswap < (pmdisk_pages + PAGES_FOR_IO)) { - pr_debug("pmdisk: Not enough swap. Need %ld\n",i.freeswap); - return 0; - } - return 1; -} - - -/** - * pmdisk_suspend - Atomically snapshot the system. - * - * This must be called with interrupts disabled, to prevent the - * system changing at all from underneath us. - * - * To do this, we count the number of pages in the system that we - * need to save; make sure we have enough memory and swap to clone - * the pages and save them in swap, allocate the space to hold them, - * and then snapshot them all. - */ - -int pmdisk_suspend(void) -{ - int error = 0; - - if ((error = read_swapfiles())) - return error; - - drain_local_pages(); - - pm_pagedir_nosave = NULL; - pr_debug("pmdisk: Counting pages to copy.\n" ); - count_pages(); - - pr_debug("pmdisk: (pages needed: %d + %d free: %d)\n", - pmdisk_pages,PAGES_FOR_IO,nr_free_pages()); - - if (!enough_free_mem()) - return -ENOMEM; - - if (!enough_swap()) - return -ENOSPC; - - if ((error = alloc_pagedir())) { - pr_debug("pmdisk: Allocating pagedir failed.\n"); - return error; - } - if ((error = alloc_image_pages())) { - pr_debug("pmdisk: Allocating image pages failed.\n"); - free_pagedir(); - return error; - } - - nr_copy_pages_check = pmdisk_pages; - pagedir_order_check = pagedir_order; - - /* During allocating of suspend pagedir, new cold pages may appear. - * Kill them - */ - drain_local_pages(); - - /* copy */ - copy_pages(); - - /* - * End of critical section. From now on, we can write to memory, - * but we should not touch disk. This specially means we must _not_ - * touch swap space! Except we must write out our image of course. - */ - - pr_debug("pmdisk: %d pages copied\n", pmdisk_pages ); - return 0; -} - - -/** - * suspend_save_image - Prepare and write saved image to swap. - * - * IRQs are re-enabled here so we can resume devices and safely write - * to the swap devices. We disable them again before we leave. - * - * The second lock_swapdevices() will unlock ignored swap devices since - * writing is finished. - * It is important _NOT_ to umount filesystems at this point. We want - * them synced (in case something goes wrong) but we DO not want to mark - * filesystem clean: it is not. (And it does not matter, if we resume - * correctly, we'll mark system clean, anyway.) - */ - -static int suspend_save_image(void) -{ - int error; - device_resume(); - lock_swapdevices(); - error = write_suspend_image(); - lock_swapdevices(); - return error; -} - -/* - * Magic happens here - */ - -int pmdisk_resume(void) -{ - BUG_ON (nr_copy_pages_check != pmdisk_pages); - BUG_ON (pagedir_order_check != pagedir_order); - - /* Even mappings of "global" things (vmalloc) need to be fixed */ - __flush_tlb_global(); - return 0; -} - -/* pmdisk_arch_suspend() is implemented in arch/?/power/pmdisk.S, - and basically does: - - if (!resume) { - save_processor_state(); - SAVE_REGISTERS - return pmdisk_suspend(); - } - GO_TO_SWAPPER_PAGE_TABLES - COPY_PAGES_BACK - RESTORE_REGISTERS - restore_processor_state(); - return pmdisk_resume(); - - */ - - -/* More restore stuff */ - -#define does_collide(addr) does_collide_order(pm_pagedir_nosave, addr, 0) - -/* - * Returns true if given address/order collides with any orig_address - */ -static int __init does_collide_order(suspend_pagedir_t *pagedir, - unsigned long addr, int order) -{ - int i; - unsigned long addre = addr + (PAGE_SIZE<orig_address >= addr && - (pagedir+i)->orig_address < addre) - return 1; - - return 0; -} - -/* - * We check here that pagedir & pages it points to won't collide with pages - * where we're going to restore from the loaded pages later - */ -static int __init check_pagedir(void) -{ - int i; - - for(i=0; i < pmdisk_pages; i++) { - unsigned long addr; - - do { - addr = get_zeroed_page(GFP_ATOMIC); - if(!addr) - return -ENOMEM; - } while (does_collide(addr)); - - (pm_pagedir_nosave+i)->address = addr; - } - return 0; -} - -static int __init relocate_pagedir(void) -{ - /* - * We have to avoid recursion (not to overflow kernel stack), - * and that's why code looks pretty cryptic - */ - suspend_pagedir_t *old_pagedir = pm_pagedir_nosave; - void **eaten_memory = NULL; - void **c = eaten_memory, *m, *f; - int err; - - pr_debug("pmdisk: Relocating pagedir\n"); - - if(!does_collide_order(old_pagedir, (unsigned long)old_pagedir, pagedir_order)) { - pr_debug("pmdisk: Relocation not necessary\n"); - return 0; - } - - err = -ENOMEM; - while ((m = (void *) __get_free_pages(GFP_ATOMIC, pagedir_order)) != NULL) { - if (!does_collide_order(old_pagedir, (unsigned long)m, - pagedir_order)) { - pm_pagedir_nosave = - memcpy(m, old_pagedir, - PAGE_SIZE << pagedir_order); - err = 0; - break; - } - eaten_memory = m; - printk( "." ); - *eaten_memory = c; - c = eaten_memory; - } - - c = eaten_memory; - while(c) { - printk(":"); - f = c; - c = *c; - free_pages((unsigned long)f, pagedir_order); - } - printk("|\n"); - return err; -} - - -static struct block_device * resume_bdev; - - -/** - * Using bio to read from swap. - * This code requires a bit more work than just using buffer heads - * but, it is the recommended way for 2.5/2.6. - * The following are to signal the beginning and end of I/O. Bios - * finish asynchronously, while we want them to happen synchronously. - * A simple atomic_t, and a wait loop take care of this problem. - */ - -static atomic_t io_done = ATOMIC_INIT(0); - -static void start_io(void) -{ - atomic_set(&io_done,1); -} - -static int end_io(struct bio * bio, unsigned int num, int err) -{ - atomic_set(&io_done,0); - return 0; -} - -static void wait_io(void) -{ - while(atomic_read(&io_done)) - io_schedule(); -} - - -/** - * submit - submit BIO request. - * @rw: READ or WRITE. - * @off physical offset of page. - * @page: page we're reading or writing. - * - * Straight from the textbook - allocate and initialize the bio. - * If we're writing, make sure the page is marked as dirty. - * Then submit it and wait. - */ - -static int submit(int rw, pgoff_t page_off, void * page) -{ - int error = 0; - struct bio * bio; - - bio = bio_alloc(GFP_ATOMIC,1); - if (!bio) - return -ENOMEM; - bio->bi_sector = page_off * (PAGE_SIZE >> 9); - bio_get(bio); - bio->bi_bdev = resume_bdev; - bio->bi_end_io = end_io; - - if (bio_add_page(bio, virt_to_page(page), PAGE_SIZE, 0) < PAGE_SIZE) { - printk("pmdisk: ERROR: adding page to bio at %ld\n",page_off); - error = -EFAULT; - goto Done; - } - - if (rw == WRITE) - bio_set_pages_dirty(bio); - start_io(); - submit_bio(rw | (1 << BIO_RW_SYNC), bio); - wait_io(); - Done: - bio_put(bio); - return error; -} - -static int -read_page(pgoff_t page_off, void * page) -{ - return submit(READ,page_off,page); -} - -static int -write_page(pgoff_t page_off, void * page) -{ - return submit(WRITE,page_off,page); -} - - -extern dev_t __init name_to_dev_t(const char *line); - - -static int __init check_sig(void) -{ - int error; - - memset(&pmdisk_header,0,sizeof(pmdisk_header)); - if ((error = read_page(0,&pmdisk_header))) - return error; - if (!memcmp(PMDISK_SIG,pmdisk_header.sig,10)) { - memcpy(pmdisk_header.sig,pmdisk_header.orig_sig,10); - - /* - * Reset swap signature now. - */ - error = write_page(0,&pmdisk_header); - } else { - pr_debug(KERN_ERR "pmdisk: Invalid partition type.\n"); - return -EINVAL; - } - if (!error) - pr_debug("pmdisk: Signature found, resuming\n"); - return error; -} - - -/* - * Sanity check if this image makes sense with this kernel/swap context - * I really don't think that it's foolproof but more than nothing.. - */ - -static const char * __init sanity_check(void) -{ - dump_pmdisk_info(); - if(pmdisk_info.version_code != LINUX_VERSION_CODE) - return "kernel version"; - if(pmdisk_info.num_physpages != num_physpages) - return "memory size"; - if (strcmp(pmdisk_info.uts.sysname,system_utsname.sysname)) - return "system type"; - if (strcmp(pmdisk_info.uts.release,system_utsname.release)) - return "kernel release"; - if (strcmp(pmdisk_info.uts.version,system_utsname.version)) - return "version"; - if (strcmp(pmdisk_info.uts.machine,system_utsname.machine)) - return "machine"; - if(pmdisk_info.cpus != num_online_cpus()) - return "number of cpus"; - return NULL; -} - - -static int __init check_header(void) -{ - const char * reason = NULL; - int error; - - init_header(); - - if ((error = read_page(swp_offset(pmdisk_header.pmdisk_info), - &pmdisk_info))) - return error; - - /* Is this same machine? */ - if ((reason = sanity_check())) { - printk(KERN_ERR "pmdisk: Resume mismatch: %s\n",reason); - return -EPERM; - } - pmdisk_pages = pmdisk_info.image_pages; - return error; -} - - -static int __init read_pagedir(void) -{ - unsigned long addr; - int i, n = pmdisk_info.pagedir_pages; - int error = 0; - - pagedir_order = get_bitmask_order(n); - - addr =__get_free_pages(GFP_ATOMIC, pagedir_order); - if (!addr) - return -ENOMEM; - pm_pagedir_nosave = (struct pbe *)addr; - - pr_debug("pmdisk: Reading pagedir (%d Pages)\n",n); - - for (i = 0; i < n && !error; i++, addr += PAGE_SIZE) { - unsigned long offset = swp_offset(pmdisk_info.pagedir[i]); - if (offset) - error = read_page(offset, (void *)addr); - else - error = -EFAULT; - } - if (error) - free_pages((unsigned long)pm_pagedir_nosave,pagedir_order); - return error; -} - - -/** - * read_image_data - Read image pages from swap. - * - * You do not need to check for overlaps, check_pagedir() - * already did that. - */ - -static int __init read_image_data(void) -{ - struct pbe * p; - int error = 0; - int i; - - printk( "Reading image data (%d pages): ", pmdisk_pages ); - for(i = 0, p = pm_pagedir_nosave; i < pmdisk_pages && !error; i++, p++) { - if (!(i%100)) - printk( "." ); - error = read_page(swp_offset(p->swap_address), - (void *)p->address); - } - printk(" %d done.\n",i); - return error; -} - - -static int __init read_suspend_image(void) -{ - int error = 0; - - if ((error = check_sig())) - return error; - if ((error = check_header())) - return error; - if ((error = read_pagedir())) - return error; - if ((error = relocate_pagedir())) - goto FreePagedir; - if ((error = check_pagedir())) - goto FreePagedir; - if ((error = read_image_data())) - goto FreePagedir; - Done: - return error; - FreePagedir: - free_pages((unsigned long)pm_pagedir_nosave,pagedir_order); - goto Done; -} - -/** - * pmdisk_save - Snapshot memory - */ - -int pmdisk_save(void) -{ - int error; - -#if defined (CONFIG_HIGHMEM) || defined (CONFIG_DISCONTIGMEM) - pr_debug("pmdisk: not supported with high- or discontig-mem.\n"); - return -EPERM; -#endif - if ((error = arch_prepare_suspend())) - return error; - local_irq_disable(); - save_processor_state(); - error = pmdisk_arch_suspend(0); - restore_processor_state(); - local_irq_enable(); - return error; -} - - -/** - * pmdisk_write - Write saved memory image to swap. - * - * pmdisk_arch_suspend(0) returns after system is resumed. - * - * pmdisk_arch_suspend() copies all "used" memory to "free" memory, - * then unsuspends all device drivers, and writes memory to disk - * using normal kernel mechanism. - */ - -int pmdisk_write(void) -{ - return suspend_save_image(); -} - - -/** - * pmdisk_read - Read saved image from swap. - */ - -int __init pmdisk_read(void) -{ - int error; - - if (!strlen(resume_file)) - return -ENOENT; - - resume_device = name_to_dev_t(resume_file); - pr_debug("pmdisk: Resume From Partition: %s\n", resume_file); - - resume_bdev = open_by_devnum(resume_device, FMODE_READ); - if (!IS_ERR(resume_bdev)) { - set_blocksize(resume_bdev, PAGE_SIZE); - error = read_suspend_image(); - blkdev_put(resume_bdev); - } else - error = PTR_ERR(resume_bdev); - - if (!error) - pr_debug("Reading resume file was successful\n"); - else - pr_debug("pmdisk: Error %d resuming\n", error); - return error; -} - - -/** - * pmdisk_restore - Replace running kernel with saved image. - */ - -int __init pmdisk_restore(void) -{ - int error; - local_irq_disable(); - save_processor_state(); - error = pmdisk_arch_suspend(1); - restore_processor_state(); - local_irq_enable(); - return error; -} - - -/** - * pmdisk_free - Free memory allocated to hold snapshot. - */ - -int pmdisk_free(void) -{ - pr_debug( "Freeing prev allocated pagedir\n" ); - free_pagedir(); - return 0; -} - -static int __init pmdisk_setup(char *str) -{ - if (strlen(str)) { - if (!strcmp(str,"off")) - resume_file[0] = '\0'; - else - strncpy(resume_file, str, 255); - } else - resume_file[0] = '\0'; - return 1; -} - -__setup("pmdisk=", pmdisk_setup); - --- linux-2.6.9-rc1/kernel/power/power.h 2004-08-24 00:03:50.000000000 -0700 +++ 25/kernel/power/power.h 2004-09-03 00:56:33.448075768 -0700 @@ -1,4 +1,5 @@ - +#include +#include /* With SUSPEND_CONSOLE defined, it suspend looks *really* cool, but we probably do not take enough locks for switching consoles, etc, @@ -9,7 +10,20 @@ #endif -#ifdef CONFIG_PM_DISK +struct swsusp_info { + struct new_utsname uts; + u32 version_code; + unsigned long num_physpages; + int cpus; + unsigned long image_pages; + unsigned long pagedir_pages; + suspend_pagedir_t * suspend_pagedir; + swp_entry_t pagedir[768]; +} __attribute__((aligned(PAGE_SIZE))); + + + +#ifdef CONFIG_SOFTWARE_SUSPEND extern int pm_suspend_disk(void); #else @@ -18,7 +32,6 @@ static inline int pm_suspend_disk(void) return -EPERM; } #endif - extern struct semaphore pm_sem; #define power_attr(_name) \ static struct subsys_attribute _name##_attr = { \ --- linux-2.6.9-rc1/kernel/power/process.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/kernel/power/process.c 2004-09-03 00:56:56.206615944 -0700 @@ -25,7 +25,8 @@ static inline int freezeable(struct task (p->flags & PF_NOFREEZE) || (p->state == TASK_ZOMBIE) || (p->state == TASK_DEAD) || - (p->state == TASK_STOPPED)) + (p->state == TASK_STOPPED) || + (p->state == TASK_TRACED)) return 0; return 1; } @@ -70,6 +71,7 @@ int freeze_processes(void) if (!freezeable(p)) continue; if ((p->flags & PF_FROZEN) || + (p->state == TASK_TRACED) || (p->state == TASK_STOPPED)) continue; --- linux-2.6.9-rc1/kernel/power/swsusp.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/kernel/power/swsusp.c 2004-09-03 00:56:34.982842448 -0700 @@ -62,6 +62,7 @@ #include #include #include +#include #include #include @@ -70,25 +71,16 @@ #include "power.h" -unsigned char software_suspend_enabled = 0; - -#define NORESUME 1 -#define RESUME_SPECIFIED 2 - /* References to section boundaries */ extern char __nosave_begin, __nosave_end; extern int is_head_of_free_region(struct page *); -/* Locks */ -spinlock_t suspend_pagedir_lock __nosavedata = SPIN_LOCK_UNLOCKED; - /* Variables to be preserved over suspend */ -static int pagedir_order_check; -static int nr_copy_pages_check; +int pagedir_order_check; +int nr_copy_pages_check; -static int resume_status; -static char resume_file[256] = ""; /* For resume= kernel option */ +extern char resume_file[]; static dev_t resume_device; /* Local variables that should not be affected by save */ unsigned int nr_copy_pages __nosavedata = 0; @@ -107,19 +99,19 @@ unsigned int nr_copy_pages __nosavedata MMU hardware. */ suspend_pagedir_t *pagedir_nosave __nosavedata = NULL; -static suspend_pagedir_t *pagedir_save; -static int pagedir_order __nosavedata = 0; +suspend_pagedir_t *pagedir_save; +int pagedir_order __nosavedata = 0; -struct link { - char dummy[PAGE_SIZE - sizeof(swp_entry_t)]; - swp_entry_t next; -}; +#define SWSUSP_SIG "S1SUSPEND" -union diskpage { - union swap_header swh; - struct link link; - struct suspend_header sh; -}; +struct swsusp_header { + char reserved[PAGE_SIZE - 20 - sizeof(swp_entry_t)]; + swp_entry_t swsusp_info; + char orig_sig[10]; + char sig[10]; +} __attribute__((packed, aligned(PAGE_SIZE))) swsusp_header; + +struct swsusp_info swsusp_info; /* * XXX: We try to keep some more pages free so that I/O operations succeed @@ -131,50 +123,9 @@ static const char name_suspend[] = "Susp static const char name_resume[] = "Resume Machine: "; /* - * Debug - */ -#define DEBUG_DEFAULT -#undef DEBUG_PROCESS -#undef DEBUG_SLOW -#define TEST_SWSUSP 0 /* Set to 1 to reboot instead of halt machine after suspension */ - -#ifdef DEBUG_DEFAULT -# define PRINTK(f, a...) printk(f, ## a) -#else -# define PRINTK(f, a...) do { } while(0) -#endif - -#ifdef DEBUG_SLOW -#define MDELAY(a) mdelay(a) -#else -#define MDELAY(a) do { } while(0) -#endif - -/* * Saving part... */ -static __inline__ int fill_suspend_header(struct suspend_header *sh) -{ - memset((char *)sh, 0, sizeof(*sh)); - - sh->version_code = LINUX_VERSION_CODE; - sh->num_physpages = num_physpages; - strncpy(sh->machine, system_utsname.machine, 8); - strncpy(sh->version, system_utsname.version, 20); - /* FIXME: Is this bogus? --RR */ - sh->num_cpus = num_online_cpus(); - sh->page_size = PAGE_SIZE; - sh->suspend_pagedir = pagedir_nosave; - BUG_ON (pagedir_save != pagedir_nosave); - sh->num_pbes = nr_copy_pages; - /* TODO: needed? mounted fs' last mounted date comparison - * [so they haven't been mounted since last suspend. - * Maybe it isn't.] [we'd need to do this for _all_ fs-es] - */ - return 0; -} - /* We memorize in swapfile_used what swap devices are used for suspension */ #define SWAPFILE_UNUSED 0 #define SWAPFILE_SUSPEND 1 /* This is the suspending device */ @@ -182,47 +133,30 @@ static __inline__ int fill_suspend_heade static unsigned short swapfile_used[MAX_SWAPFILES]; static unsigned short root_swap; -#define MARK_SWAP_SUSPEND 0 -#define MARK_SWAP_RESUME 2 -static void mark_swapfiles(swp_entry_t prev, int mode) +static int mark_swapfiles(swp_entry_t prev) { - swp_entry_t entry; - union diskpage *cur; - struct page *page; - - if (root_swap == 0xFFFF) /* ignored */ - return; + int error; - page = alloc_page(GFP_ATOMIC); - if (!page) - panic("Out of memory in mark_swapfiles"); - cur = page_address(page); - /* XXX: this is dirty hack to get first page of swap file */ - entry = swp_entry(root_swap, 0); - rw_swap_page_sync(READ, entry, page); - - if (mode == MARK_SWAP_RESUME) { - if (!memcmp("S1",cur->swh.magic.magic,2)) - memcpy(cur->swh.magic.magic,"SWAP-SPACE",10); - else if (!memcmp("S2",cur->swh.magic.magic,2)) - memcpy(cur->swh.magic.magic,"SWAPSPACE2",10); - else printk("%sUnable to find suspended-data signature (%.10s - misspelled?\n", - name_resume, cur->swh.magic.magic); + rw_swap_page_sync(READ, + swp_entry(root_swap, 0), + virt_to_page((unsigned long)&swsusp_header)); + if (!memcmp("SWAP-SPACE",swsusp_header.sig,10) || + !memcmp("SWAPSPACE2",swsusp_header.sig,10)) { + memcpy(swsusp_header.orig_sig,swsusp_header.sig,10); + memcpy(swsusp_header.sig,SWSUSP_SIG,10); + swsusp_header.swsusp_info = prev; + error = rw_swap_page_sync(WRITE, + swp_entry(root_swap, 0), + virt_to_page((unsigned long) + &swsusp_header)); } else { - if ((!memcmp("SWAP-SPACE",cur->swh.magic.magic,10))) - memcpy(cur->swh.magic.magic,"S1SUSP....",10); - else if ((!memcmp("SWAPSPACE2",cur->swh.magic.magic,10))) - memcpy(cur->swh.magic.magic,"S2SUSP....",10); - else panic("\nSwapspace is not swapspace (%.10s)\n", cur->swh.magic.magic); - cur->link.next = prev; /* prev is the first/last swap page of the resume area */ - /* link.next lies *no more* in last 4/8 bytes of magic */ + pr_debug("swsusp: Partition is not swap space.\n"); + error = -ENODEV; } - rw_swap_page_sync(WRITE, entry, page); - __free_page(page); + return error; } - /* * Check whether the swap device is the specified resume * device, irrespective of whether they are specified by @@ -243,7 +177,7 @@ static int is_resume_device(const struct resume_device == MKDEV(imajor(inode), iminor(inode)); } -static void read_swapfiles(void) /* This is called before saving image */ +int swsusp_swap_check(void) /* This is called before saving image */ { int i, len; @@ -274,114 +208,209 @@ static void read_swapfiles(void) /* This } } swap_list_unlock(); + return (root_swap != 0xffff) ? 0 : -ENODEV; } -static void lock_swapdevices(void) /* This is called after saving image so modification - will be lost after resume... and that's what we want. */ +/** + * This is called after saving image so modification + * will be lost after resume... and that's what we want. + * we make the device unusable. A new call to + * lock_swapdevices can unlock the devices. + */ +static void lock_swapdevices(void) { int i; swap_list_lock(); for(i = 0; i< MAX_SWAPFILES; i++) if(swapfile_used[i] == SWAPFILE_IGNORED) { - swap_info[i].flags ^= 0xFF; /* we make the device unusable. A new call to - lock_swapdevices can unlock the devices. */ + swap_info[i].flags ^= 0xFF; } swap_list_unlock(); } + + /** - * write_suspend_image - Write entire image to disk. + * write_swap_page - Write one page to a fresh swap location. + * @addr: Address we're writing. + * @loc: Place to store the entry we used. * - * After writing suspend signature to the disk, suspend may no - * longer fail: we have ready-to-run image in swap, and rollback - * would happen on next reboot -- corrupting data. + * Allocate a new swap entry and 'sync' it. Note we discard -EIO + * errors. That is an artifact left over from swsusp. It did not + * check the return of rw_swap_page_sync() at all, since most pages + * written back to swap would return -EIO. + * This is a partial improvement, since we will at least return other + * errors, though we need to eventually fix the damn code. + */ + +static int write_page(unsigned long addr, swp_entry_t * loc) +{ + swp_entry_t entry; + int error = 0; + + entry = get_swap_page(); + if (swp_offset(entry) && + swapfile_used[swp_type(entry)] == SWAPFILE_SUSPEND) { + error = rw_swap_page_sync(WRITE, entry, + virt_to_page(addr)); + if (error == -EIO) + error = 0; + if (!error) + *loc = entry; + } else + error = -ENOSPC; + return error; +} + + +/** + * free_data - Free the swap entries used by the saved image. * - * Note: The buffer we allocate to use to write the suspend header is - * not freed; its not needed since the system is going down anyway - * (plus it causes an oops and I'm lazy^H^H^H^Htoo busy). + * Walk the list of used swap entries and free each one. */ -static int write_suspend_image(void) + +static void data_free(void) { + swp_entry_t entry; int i; - swp_entry_t entry, prev = { 0 }; - int nr_pgdir_pages = SUSPEND_PD_PAGES(nr_copy_pages); - union diskpage *cur, *buffer = (union diskpage *)get_zeroed_page(GFP_ATOMIC); - unsigned long address; - struct page *page; - if (!buffer) - return -ENOMEM; + for (i = 0; i < nr_copy_pages; i++) { + entry = (pagedir_nosave + i)->swap_address; + if (entry.val) + swap_free(entry); + else + break; + (pagedir_nosave + i)->swap_address = (swp_entry_t){0}; + } +} + + +/** + * write_data - Write saved image to swap. + * + * Walk the list of pages in the image and sync each one to swap. + */ + +static int data_write(void) +{ + int error = 0; + int i; printk( "Writing data to swap (%d pages): ", nr_copy_pages ); - for (i=0; iaddress; - page = virt_to_page(address); - rw_swap_page_sync(WRITE, entry, page); - (pagedir_nosave+i)->swap_address = entry; - } - printk( "|\n" ); - printk( "Writing pagedir (%d pages): ", nr_pgdir_pages); - for (i=0; iaddress, + &((pagedir_nosave+i)->swap_address)); + } + printk(" %d Pages done.\n",i); + return error; +} - if(swapfile_used[swp_type(entry)] != SWAPFILE_SUSPEND) - panic("\nNot enough swapspace for pagedir on suspend device" ); +static void dump_info(void) +{ + pr_debug(" swsusp: Version: %u\n",swsusp_info.version_code); + pr_debug(" swsusp: Num Pages: %ld\n",swsusp_info.num_physpages); + pr_debug(" swsusp: UTS Sys: %s\n",swsusp_info.uts.sysname); + pr_debug(" swsusp: UTS Node: %s\n",swsusp_info.uts.nodename); + pr_debug(" swsusp: UTS Release: %s\n",swsusp_info.uts.release); + pr_debug(" swsusp: UTS Version: %s\n",swsusp_info.uts.version); + pr_debug(" swsusp: UTS Machine: %s\n",swsusp_info.uts.machine); + pr_debug(" swsusp: UTS Domain: %s\n",swsusp_info.uts.domainname); + pr_debug(" swsusp: CPUs: %d\n",swsusp_info.cpus); + pr_debug(" swsusp: Image: %ld Pages\n",swsusp_info.image_pages); + pr_debug(" swsusp: Pagedir: %ld Pages\n",swsusp_info.pagedir_pages); +} + +static void init_header(void) +{ + memset(&swsusp_info,0,sizeof(swsusp_info)); + swsusp_info.version_code = LINUX_VERSION_CODE; + swsusp_info.num_physpages = num_physpages; + memcpy(&swsusp_info.uts,&system_utsname,sizeof(system_utsname)); + + swsusp_info.suspend_pagedir = pagedir_nosave; + swsusp_info.cpus = num_online_cpus(); + swsusp_info.image_pages = nr_copy_pages; + dump_info(); +} - BUG_ON (sizeof(swp_entry_t) != sizeof(long)); - BUG_ON (PAGE_SIZE % sizeof(struct pbe)); +static int close_swap(void) +{ + swp_entry_t entry; + int error; - cur->link.next = prev; - page = virt_to_page((unsigned long)cur); - rw_swap_page_sync(WRITE, entry, page); - prev = entry; - } - printk("H"); - BUG_ON (sizeof(struct suspend_header) > PAGE_SIZE-sizeof(swp_entry_t)); - BUG_ON (sizeof(union diskpage) != PAGE_SIZE); - BUG_ON (sizeof(struct link) != PAGE_SIZE); - entry = get_swap_page(); - if (!entry.val) - panic( "\nNot enough swapspace when writing header" ); - if (swapfile_used[swp_type(entry)] != SWAPFILE_SUSPEND) - panic("\nNot enough swapspace for header on suspend device" ); - - cur = (void *) buffer; - if (fill_suspend_header(&cur->sh)) - BUG(); /* Not a BUG_ON(): we want fill_suspend_header to be called, always */ - - cur->link.next = prev; - - page = virt_to_page((unsigned long)cur); - rw_swap_page_sync(WRITE, entry, page); - prev = entry; - - printk( "S" ); - mark_swapfiles(prev, MARK_SWAP_SUSPEND); - printk( "|\n" ); + error = write_page((unsigned long)&swsusp_info,&entry); + if (!error) { + printk( "S" ); + error = mark_swapfiles(entry); + printk( "|\n" ); + } + return error; +} - MDELAY(1000); - return 0; +/** + * free_pagedir - Free pages used by the page directory. + */ + +static void free_pagedir_entries(void) +{ + int num = swsusp_info.pagedir_pages; + int i; + + for (i = 0; i < num; i++) + swap_free(swsusp_info.pagedir[i]); +} + + +/** + * write_pagedir - Write the array of pages holding the page directory. + * @last: Last swap entry we write (needed for header). + */ + +static int write_pagedir(void) +{ + unsigned long addr = (unsigned long)pagedir_nosave; + int error = 0; + int n = SUSPEND_PD_PAGES(nr_copy_pages); + int i; + + swsusp_info.pagedir_pages = n; + printk( "Writing pagedir (%d pages)\n", n); + for (i = 0; i < n && !error; i++, addr += PAGE_SIZE) + error = write_page(addr,&swsusp_info.pagedir[i]); + return error; +} + +/** + * write_suspend_image - Write entire image and metadata. + * + */ + +static int write_suspend_image(void) +{ + int error; + + init_header(); + if ((error = data_write())) + goto FreeData; + + if ((error = write_pagedir())) + goto FreePagedir; + + if ((error = close_swap())) + goto FreePagedir; + Done: + return error; + FreePagedir: + free_pagedir_entries(); + FreeData: + data_free(); + goto Done; } + #ifdef CONFIG_HIGHMEM struct highmem_page { char *data; @@ -438,22 +467,30 @@ static int save_highmem_zone(struct zone } return 0; } +#endif /* CONFIG_HIGHMEM */ + static int save_highmem(void) { +#ifdef CONFIG_HIGHMEM struct zone *zone; int res = 0; + + pr_debug("swsusp: Saving Highmem\n"); for_each_zone(zone) { if (is_highmem(zone)) res = save_highmem_zone(zone); if (res) return res; } +#endif return 0; } static int restore_highmem(void) { +#ifdef CONFIG_HIGHMEM + printk("swsusp: Restoring Highmem\n"); while (highmem_copy) { struct highmem_page *save = highmem_copy; void *kaddr; @@ -465,9 +502,10 @@ static int restore_highmem(void) free_page((long) save->data); kfree(save); } +#endif return 0; } -#endif + static int pfn_is_nosave(unsigned long pfn) { @@ -476,57 +514,82 @@ static int pfn_is_nosave(unsigned long p return (pfn >= nosave_begin_pfn) && (pfn < nosave_end_pfn); } -/* if *pagedir_p != NULL it also copies the counted pages */ -static int count_and_copy_zone(struct zone *zone, struct pbe **pagedir_p) +/** + * saveable - Determine whether a page should be cloned or not. + * @pfn: The page + * + * We save a page if it's Reserved, and not in the range of pages + * statically defined as 'unsaveable', or if it isn't reserved, and + * isn't part of a free chunk of pages. + * If it is part of a free chunk, we update @pfn to point to the last + * page of the chunk. + */ + +static int saveable(struct zone * zone, unsigned long * zone_pfn) { - unsigned long zone_pfn, chunk_size, nr_copy_pages = 0; - struct pbe *pbe = *pagedir_p; - for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) { - struct page *page; - unsigned long pfn = zone_pfn + zone->zone_start_pfn; + unsigned long pfn = *zone_pfn + zone->zone_start_pfn; + unsigned long chunk_size; + struct page * page; - if (!(pfn%1000)) - printk("."); - if (!pfn_valid(pfn)) - continue; - page = pfn_to_page(pfn); - BUG_ON(PageReserved(page) && PageNosave(page)); - if (PageNosave(page)) - continue; - if (PageReserved(page) && pfn_is_nosave(pfn)) { - PRINTK("[nosave pfn 0x%lx]", pfn); - continue; - } - if ((chunk_size = is_head_of_free_region(page))) { - pfn += chunk_size - 1; - zone_pfn += chunk_size - 1; - continue; + if (!pfn_valid(pfn)) + return 0; + + if (!(pfn%1000)) + printk("."); + page = pfn_to_page(pfn); + BUG_ON(PageReserved(page) && PageNosave(page)); + if (PageNosave(page)) + return 0; + if (PageReserved(page) && pfn_is_nosave(pfn)) { + pr_debug("[nosave pfn 0x%lx]", pfn); + return 0; + } + if ((chunk_size = is_head_of_free_region(page))) { + *zone_pfn += chunk_size - 1; + return 0; + } + + return 1; +} + +static void count_data_pages(void) +{ + struct zone *zone; + unsigned long zone_pfn; + + nr_copy_pages = 0; + + for_each_zone(zone) { + if (!is_highmem(zone)) { + for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) + nr_copy_pages += saveable(zone, &zone_pfn); } - nr_copy_pages++; - if (!pbe) - continue; - pbe->orig_address = (long) page_address(page); - /* Copy page is dangerous: it likes to mess with - preempt count on specific cpus. Wrong preempt count is then copied, - oops. */ - copy_page((void *)pbe->address, (void *)pbe->orig_address); - pbe++; } - *pagedir_p = pbe; - return nr_copy_pages; } -static int count_and_copy_data_pages(struct pbe *pagedir_p) + +static void copy_data_pages(void) { - int nr_copy_pages = 0; struct zone *zone; + unsigned long zone_pfn; + struct pbe * pbe = pagedir_nosave; + for_each_zone(zone) { if (!is_highmem(zone)) - nr_copy_pages += count_and_copy_zone(zone, &pagedir_p); + for (zone_pfn = 0; zone_pfn < zone->spanned_pages; ++zone_pfn) { + if (saveable(zone, &zone_pfn)) { + struct page * page; + page = pfn_to_page(zone_pfn + zone->zone_start_pfn); + pbe->orig_address = (long) page_address(page); + /* copy_page is no usable for copying task structs. */ + memcpy((void *)pbe->address, (void *)pbe->orig_address, PAGE_SIZE); + pbe++; + } + } } - return nr_copy_pages; } + static void free_suspend_pagedir_zone(struct zone *zone, unsigned long pagedir) { unsigned long zone_pfn, pagedir_end, pagedir_pfn, pagedir_end_pfn; @@ -547,119 +610,202 @@ static void free_suspend_pagedir_zone(st } } -static void free_suspend_pagedir(unsigned long this_pagedir) +void swsusp_free(void) { + unsigned long p = (unsigned long)pagedir_save; struct zone *zone; for_each_zone(zone) { if (!is_highmem(zone)) - free_suspend_pagedir_zone(zone, this_pagedir); + free_suspend_pagedir_zone(zone, p); } - free_pages(this_pagedir, pagedir_order); + free_pages(p, pagedir_order); } -static suspend_pagedir_t *create_suspend_pagedir(int nr_copy_pages) + +/** + * calc_order - Determine the order of allocation needed for pagedir_save. + * + * This looks tricky, but is just subtle. Please fix it some time. + * Since there are %nr_copy_pages worth of pages in the snapshot, we need + * to allocate enough contiguous space to hold + * (%nr_copy_pages * sizeof(struct pbe)), + * which has the saved/orig locations of the page.. + * + * SUSPEND_PD_PAGES() tells us how many pages we need to hold those + * structures, then we call get_bitmask_order(), which will tell us the + * last bit set in the number, starting with 1. (If we need 30 pages, that + * is 0x0000001e in hex. The last bit is the 5th, which is the order we + * would use to allocate 32 contiguous pages). + * + * Since we also need to save those pages, we add the number of pages that + * we need to nr_copy_pages, and in case of an overflow, do the + * calculation again to update the number of pages needed. + * + * With this model, we will tend to waste a lot of memory if we just cross + * an order boundary. Plus, the higher the order of allocation that we try + * to do, the more likely we are to fail in a low-memory situtation + * (though we're unlikely to get this far in such a case, since swsusp + * requires half of memory to be free anyway). + */ + + +static void calc_order(void) { - int i; - suspend_pagedir_t *pagedir; - struct pbe *p; - struct page *page; + int diff = 0; + int order = 0; + + do { + diff = get_bitmask_order(SUSPEND_PD_PAGES(nr_copy_pages)) - order; + if (diff) { + order += diff; + nr_copy_pages += 1 << diff; + } + } while(diff); + pagedir_order = order; +} + + +/** + * alloc_pagedir - Allocate the page directory. + * + * First, determine exactly how many contiguous pages we need, + * allocate them, then mark each 'unsavable'. + */ + +static int alloc_pagedir(void) +{ + calc_order(); + pagedir_save = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC | __GFP_COLD, + pagedir_order); + if (!pagedir_save) + return -ENOMEM; + memset(pagedir_save, 0, (1 << pagedir_order) * PAGE_SIZE); + pagedir_nosave = pagedir_save; + return 0; +} - pagedir_order = get_bitmask_order(SUSPEND_PD_PAGES(nr_copy_pages)); - p = pagedir = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC | __GFP_COLD, pagedir_order); - if (!pagedir) - return NULL; - - page = virt_to_page(pagedir); - for(i=0; i < 1<address = get_zeroed_page(GFP_ATOMIC | __GFP_COLD); - if (!p->address) { - free_suspend_pagedir((unsigned long) pagedir); - return NULL; - } + if(!p->address) + goto Error; SetPageNosave(virt_to_page(p->address)); - p->orig_address = 0; - p++; } - return pagedir; + return 0; + Error: + do { + if (p->address) + free_page(p->address); + p->address = 0; + } while (p-- > pagedir_save); + return -ENOMEM; } -static int prepare_suspend_processes(void) + +/** + * enough_free_mem - Make sure we enough free memory to snapshot. + * + * Returns TRUE or FALSE after checking the number of available + * free pages. + */ + +static int enough_free_mem(void) { - sys_sync(); /* Syncing needs pdflushd, so do it before stopping processes */ - if (freeze_processes()) { - printk( KERN_ERR "Suspend failed: Not all processes stopped!\n" ); - thaw_processes(); - return 1; + if(nr_free_pages() < (nr_copy_pages + PAGES_FOR_IO)) { + pr_debug("swsusp: Not enough free pages: Have %d\n", + nr_free_pages()); + return 0; } - return 0; + return 1; } -/* - * Try to free as much memory as possible, but do not OOM-kill anyone + +/** + * enough_swap - Make sure we have enough swap to save the image. + * + * Returns TRUE or FALSE after checking the total amount of swap + * space avaiable. * - * Notice: all userland should be stopped at this point, or livelock is possible. + * FIXME: si_swapinfo(&i) returns all swap devices information. + * We should only consider resume_device. */ -static void free_some_memory(void) + +static int enough_swap(void) { - printk("Freeing memory: "); - while (shrink_all_memory(10000)) - printk("."); - printk("|\n"); + struct sysinfo i; + + si_swapinfo(&i); + if (i.freeswap < (nr_copy_pages + PAGES_FOR_IO)) { + pr_debug("swsusp: Not enough swap. Need %ld\n",i.freeswap); + return 0; + } + return 1; } -static int suspend_prepare_image(void) +static int swsusp_alloc(void) { - struct sysinfo i; - unsigned int nr_needed_pages = 0; + int error; + + pr_debug("suspend: (pages needed: %d + %d free: %d)\n", + nr_copy_pages,PAGES_FOR_IO,nr_free_pages()); pagedir_nosave = NULL; - printk( "/critical section: "); -#ifdef CONFIG_HIGHMEM - printk( "handling highmem" ); - if (save_highmem()) { - printk(KERN_CRIT "%sNot enough free pages for highmem\n", name_suspend); + if (!enough_free_mem()) return -ENOMEM; - } - printk(", "); -#endif - printk("counting pages to copy" ); - drain_local_pages(); - nr_copy_pages = count_and_copy_data_pages(NULL); - nr_needed_pages = nr_copy_pages + PAGES_FOR_IO; - - printk(" (pages needed: %d+%d=%d free: %d)\n",nr_copy_pages,PAGES_FOR_IO,nr_needed_pages,nr_free_pages()); - if(nr_free_pages() < nr_needed_pages) { - printk(KERN_CRIT "%sCouldn't get enough free pages, on %d pages short\n", - name_suspend, nr_needed_pages-nr_free_pages()); - root_swap = 0xFFFF; - return -ENOMEM; - } - si_swapinfo(&i); /* FIXME: si_swapinfo(&i) returns all swap devices information. - We should only consider resume_device. */ - if (i.freeswap < nr_needed_pages) { - printk(KERN_CRIT "%sThere's not enough swap space available, on %ld pages short\n", - name_suspend, nr_needed_pages-i.freeswap); + if (!enough_swap()) return -ENOSPC; - } - PRINTK( "Alloc pagedir\n" ); - pagedir_save = pagedir_nosave = create_suspend_pagedir(nr_copy_pages); - if (!pagedir_nosave) { - /* Pagedir is big, one-chunk allocation. It is easily possible for this allocation to fail */ - printk(KERN_CRIT "%sCouldn't allocate continuous pagedir\n", name_suspend); - return -ENOMEM; + if ((error = alloc_pagedir())) { + pr_debug("suspend: Allocating pagedir failed.\n"); + return error; + } + if ((error = alloc_image_pages())) { + pr_debug("suspend: Allocating image pages failed.\n"); + swsusp_free(); + return error; } + nr_copy_pages_check = nr_copy_pages; pagedir_order_check = pagedir_order; + return 0; +} + +int suspend_prepare_image(void) +{ + unsigned int nr_needed_pages = 0; + int error; - drain_local_pages(); /* During allocating of suspend pagedir, new cold pages may appear. Kill them */ - if (nr_copy_pages != count_and_copy_data_pages(pagedir_nosave)) /* copy */ - BUG(); + pr_debug("swsusp: critical section: \n"); + if (save_highmem()) { + printk(KERN_CRIT "%sNot enough free pages for highmem\n", name_suspend); + return -ENOMEM; + } + + drain_local_pages(); + count_data_pages(); + printk("swsusp: Need to copy %u pages\n",nr_copy_pages); + nr_needed_pages = nr_copy_pages + PAGES_FOR_IO; + + error = swsusp_alloc(); + if (error) + return error; + + /* During allocating of suspend pagedir, new cold pages may appear. + * Kill them. + */ + drain_local_pages(); + copy_data_pages(); /* * End of critical section. From now on, we can write to memory, @@ -667,205 +813,86 @@ static int suspend_prepare_image(void) * touch swap space! Except we must write out our image of course. */ - printk( "critical section/: done (%d pages copied)\n", nr_copy_pages ); + printk("swsusp: critical section/: done (%d pages copied)\n", nr_copy_pages ); return 0; } -static void suspend_save_image(void) + +/* It is important _NOT_ to umount filesystems at this point. We want + * them synced (in case something goes wrong) but we DO not want to mark + * filesystem clean: it is not. (And it does not matter, if we resume + * correctly, we'll mark system clean, anyway.) + */ +int swsusp_write(void) { + int error; device_resume(); - lock_swapdevices(); - write_suspend_image(); - lock_swapdevices(); /* This will unlock ignored swap devices since writing is finished */ + error = write_suspend_image(); + /* This will unlock ignored swap devices since writing is finished */ + lock_swapdevices(); + return error; - /* It is important _NOT_ to umount filesystems at this point. We want - * them synced (in case something goes wrong) but we DO not want to mark - * filesystem clean: it is not. (And it does not matter, if we resume - * correctly, we'll mark system clean, anyway.) - */ } -static void suspend_power_down(void) -{ - extern int C_A_D; - C_A_D = 0; - printk(KERN_EMERG "%s%s Trying to power down.\n", name_suspend, TEST_SWSUSP ? "Disable TEST_SWSUSP. NOT ": ""); -#ifdef CONFIG_VT - PRINTK(KERN_EMERG "shift_state: %04x\n", shift_state); - mdelay(1000); - if (TEST_SWSUSP ^ (!!(shift_state & (1 << KG_CTRL)))) - machine_restart(NULL); - else -#endif - { - device_suspend(3); - device_shutdown(); - machine_power_off(); - } - printk(KERN_EMERG "%sProbably not capable for powerdown. System halted.\n", name_suspend); - machine_halt(); - while (1); - /* NOTREACHED */ -} +extern asmlinkage int swsusp_arch_suspend(void); +extern asmlinkage int swsusp_arch_resume(void); -/* - * Magic happens here - */ -asmlinkage void do_magic_resume_1(void) +asmlinkage int swsusp_save(void) { - barrier(); - mb(); - spin_lock_irq(&suspend_pagedir_lock); /* Done to disable interrupts */ + int error = 0; - device_power_down(3); - PRINTK( "Waiting for DMAs to settle down...\n"); - mdelay(1000); /* We do not want some readahead with DMA to corrupt our memory, right? - Do it with disabled interrupts for best effect. That way, if some - driver scheduled DMA, we have good chance for DMA to finish ;-). */ + if ((error = swsusp_swap_check())) + return error; + return suspend_prepare_image(); } -asmlinkage void do_magic_resume_2(void) +int swsusp_suspend(void) { - BUG_ON (nr_copy_pages_check != nr_copy_pages); - BUG_ON (pagedir_order_check != pagedir_order); - - __flush_tlb_global(); /* Even mappings of "global" things (vmalloc) need to be fixed */ - - PRINTK( "Freeing prev allocated pagedir\n" ); - free_suspend_pagedir((unsigned long) pagedir_save); - -#ifdef CONFIG_HIGHMEM - printk( "Restoring highmem\n" ); + int error; + if ((error = arch_prepare_suspend())) + return error; + local_irq_disable(); + save_processor_state(); + error = swsusp_arch_suspend(); + /* Restore control flow magically appears here */ + restore_processor_state(); + local_irq_enable(); restore_highmem(); -#endif - printk("done, devices\n"); - - device_power_up(); - spin_unlock_irq(&suspend_pagedir_lock); - device_resume(); - - /* Fixme: this is too late; we should do this ASAP to avoid "infinite reboots" problem */ - PRINTK( "Fixing swap signatures... " ); - mark_swapfiles(((swp_entry_t) {0}), MARK_SWAP_RESUME); - PRINTK( "ok\n" ); - -#ifdef SUSPEND_CONSOLE - acquire_console_sem(); - update_screen(fg_console); - release_console_sem(); -#endif + return error; } -/* do_magic() is implemented in arch/?/kernel/suspend_asm.S, and basically does: - - if (!resume) { - do_magic_suspend_1(); - save_processor_state(); - SAVE_REGISTERS - do_magic_suspend_2(); - return; - } - GO_TO_SWAPPER_PAGE_TABLES - do_magic_resume_1(); - COPY_PAGES_BACK - RESTORE_REGISTERS - restore_processor_state(); - do_magic_resume_2(); - - */ -asmlinkage void do_magic_suspend_1(void) +asmlinkage int swsusp_restore(void) { - mb(); - barrier(); - BUG_ON(in_atomic()); - spin_lock_irq(&suspend_pagedir_lock); + BUG_ON (nr_copy_pages_check != nr_copy_pages); + BUG_ON (pagedir_order_check != pagedir_order); + + /* Even mappings of "global" things (vmalloc) need to be fixed */ + __flush_tlb_global(); + return 0; } -asmlinkage void do_magic_suspend_2(void) +int swsusp_resume(void) { - int is_problem; - read_swapfiles(); - device_power_down(3); - is_problem = suspend_prepare_image(); - device_power_up(); - spin_unlock_irq(&suspend_pagedir_lock); - if (!is_problem) { - kernel_fpu_end(); /* save_processor_state() does kernel_fpu_begin, and we need to revert it in order to pass in_atomic() checks */ - BUG_ON(in_atomic()); - suspend_save_image(); - suspend_power_down(); /* FIXME: if suspend_power_down is commented out, console is lost after few suspends ?! */ - } - - printk(KERN_EMERG "%sSuspend failed, trying to recover...\n", name_suspend); - MDELAY(1000); /* So user can wait and report us messages if armageddon comes :-) */ - - barrier(); - mb(); - spin_lock_irq(&suspend_pagedir_lock); /* Done to disable interrupts */ - - free_pages((unsigned long) pagedir_nosave, pagedir_order); - spin_unlock_irq(&suspend_pagedir_lock); - - device_resume(); - PRINTK( "Fixing swap signatures... " ); - mark_swapfiles(((swp_entry_t) {0}), MARK_SWAP_RESUME); - PRINTK( "ok\n" ); + int error; + local_irq_disable(); + /* We'll ignore saved state, but this gets preempt count (etc) right */ + save_processor_state(); + error = swsusp_arch_resume(); + /* Code below is only ever reached in case of failure. Otherwise + * execution continues at place where swsusp_arch_suspend was called + */ + BUG_ON(!error); + restore_processor_state(); + restore_highmem(); + local_irq_enable(); + return error; } -/* - * This is main interface to the outside world. It needs to be - * called from process context. - */ -int software_suspend(void) -{ - int res; - if (!software_suspend_enabled) - return -EAGAIN; - - software_suspend_enabled = 0; - might_sleep(); - if (arch_prepare_suspend()) { - printk("%sArchitecture failed to prepare\n", name_suspend); - return -EPERM; - } - if (pm_prepare_console()) - printk( "%sCan't allocate a console... proceeding\n", name_suspend); - if (!prepare_suspend_processes()) { - - /* At this point, all user processes and "dangerous" - kernel threads are stopped. Free some memory, as we - need half of memory free. */ - - free_some_memory(); - disable_nonboot_cpus(); - /* Save state of all device drivers, and stop them. */ - printk("Suspending devices... "); - if ((res = device_suspend(3))==0) { - /* If stopping device drivers worked, we proceed basically into - * suspend_save_image. - * - * do_magic(0) returns after system is resumed. - * - * do_magic() copies all "used" memory to "free" memory, then - * unsuspends all device drivers, and writes memory to disk - * using normal kernel mechanism. - */ - do_magic(0); - } - thaw_processes(); - enable_nonboot_cpus(); - } else - res = -EBUSY; - software_suspend_enabled = 1; - MDELAY(1000); - pm_restore_console(); - return res; -} /* More restore stuff */ @@ -874,7 +901,7 @@ int software_suspend(void) /* * Returns true if given address/order collides with any orig_address */ -static int does_collide_order(suspend_pagedir_t *pagedir, unsigned long addr, +static int __init does_collide_order(suspend_pagedir_t *pagedir, unsigned long addr, int order) { int i; @@ -892,7 +919,7 @@ static int does_collide_order(suspend_pa * We check here that pagedir & pages it points to won't collide with pages * where we're going to restore from the loaded pages later */ -static int check_pagedir(void) +static int __init check_pagedir(void) { int i; @@ -910,7 +937,7 @@ static int check_pagedir(void) return 0; } -static int relocate_pagedir(void) +static int __init swsusp_pagedir_relocate(void) { /* * We have to avoid recursion (not to overflow kernel stack), @@ -953,283 +980,263 @@ static int relocate_pagedir(void) free_pages((unsigned long)f, pagedir_order); } printk("|\n"); - return ret; + return check_pagedir(); } -/* - * Sanity check if this image makes sense with this kernel/swap context - * I really don't think that it's foolproof but more than nothing.. +/** + * Using bio to read from swap. + * This code requires a bit more work than just using buffer heads + * but, it is the recommended way for 2.5/2.6. + * The following are to signal the beginning and end of I/O. Bios + * finish asynchronously, while we want them to happen synchronously. + * A simple atomic_t, and a wait loop take care of this problem. */ -static int sanity_check_failed(char *reason) -{ - printk(KERN_ERR "%s%s\n", name_resume, reason); - return -EPERM; -} +static atomic_t io_done = ATOMIC_INIT(0); -static int sanity_check(struct suspend_header *sh) +static void start_io(void) { - if (sh->version_code != LINUX_VERSION_CODE) - return sanity_check_failed("Incorrect kernel version"); - if (sh->num_physpages != num_physpages) - return sanity_check_failed("Incorrect memory size"); - if (strncmp(sh->machine, system_utsname.machine, 8)) - return sanity_check_failed("Incorrect machine type"); - if (strncmp(sh->version, system_utsname.version, 20)) - return sanity_check_failed("Incorrect version"); - if (sh->num_cpus != num_online_cpus()) - return sanity_check_failed("Incorrect number of cpus"); - if (sh->page_size != PAGE_SIZE) - return sanity_check_failed("Incorrect PAGE_SIZE"); - return 0; + atomic_set(&io_done,1); } -static int bdev_read_page(struct block_device *bdev, long pos, void *buf) +static int end_io(struct bio * bio, unsigned int num, int err) { - struct buffer_head *bh; - BUG_ON (pos%PAGE_SIZE); - bh = __bread(bdev, pos/PAGE_SIZE, PAGE_SIZE); - if (!bh || (!bh->b_data)) { - return -1; - } - memcpy(buf, bh->b_data, PAGE_SIZE); /* FIXME: may need kmap() */ - BUG_ON(!buffer_uptodate(bh)); - brelse(bh); + atomic_set(&io_done,0); return 0; -} +} -static int bdev_write_page(struct block_device *bdev, long pos, void *buf) +static void wait_io(void) { -#if 0 - struct buffer_head *bh; - BUG_ON (pos%PAGE_SIZE); - bh = __bread(bdev, pos/PAGE_SIZE, PAGE_SIZE); - if (!bh || (!bh->b_data)) { - return -1; - } - memcpy(bh->b_data, buf, PAGE_SIZE); /* FIXME: may need kmap() */ - BUG_ON(!buffer_uptodate(bh)); - generic_make_request(WRITE, bh); - if (!buffer_uptodate(bh)) - printk(KERN_CRIT "%sWarning %s: Fixing swap signatures unsuccessful...\n", name_resume, resume_file); - wait_on_buffer(bh); - brelse(bh); - return 0; -#endif - printk(KERN_CRIT "%sWarning %s: Fixing swap signatures unimplemented...\n", name_resume, resume_file); - return 0; + while(atomic_read(&io_done)) + io_schedule(); } -extern dev_t __init name_to_dev_t(const char *line); -static int __init __read_suspend_image(struct block_device *bdev, union diskpage *cur, int noresume) -{ - swp_entry_t next; - int i, nr_pgdir_pages; +static struct block_device * resume_bdev; -#define PREPARENEXT \ - { next = cur->link.next; \ - next.val = swp_offset(next) * PAGE_SIZE; \ - } +/** + * submit - submit BIO request. + * @rw: READ or WRITE. + * @off physical offset of page. + * @page: page we're reading or writing. + * + * Straight from the textbook - allocate and initialize the bio. + * If we're writing, make sure the page is marked as dirty. + * Then submit it and wait. + */ - if (bdev_read_page(bdev, 0, cur)) return -EIO; +static int submit(int rw, pgoff_t page_off, void * page) +{ + int error = 0; + struct bio * bio; - if ((!memcmp("SWAP-SPACE",cur->swh.magic.magic,10)) || - (!memcmp("SWAPSPACE2",cur->swh.magic.magic,10))) { - printk(KERN_ERR "%sThis is normal swap space\n", name_resume ); - return -EINVAL; - } + bio = bio_alloc(GFP_ATOMIC,1); + if (!bio) + return -ENOMEM; + bio->bi_sector = page_off * (PAGE_SIZE >> 9); + bio_get(bio); + bio->bi_bdev = resume_bdev; + bio->bi_end_io = end_io; + + if (bio_add_page(bio, virt_to_page(page), PAGE_SIZE, 0) < PAGE_SIZE) { + printk("swsusp: ERROR: adding page to bio at %ld\n",page_off); + error = -EFAULT; + goto Done; + } + + if (rw == WRITE) + bio_set_pages_dirty(bio); + start_io(); + submit_bio(rw | (1 << BIO_RW_SYNC), bio); + wait_io(); + Done: + bio_put(bio); + return error; +} - PREPARENEXT; /* We have to read next position before we overwrite it */ +int bio_read_page(pgoff_t page_off, void * page) +{ + return submit(READ,page_off,page); +} - if (!memcmp("S1",cur->swh.magic.magic,2)) - memcpy(cur->swh.magic.magic,"SWAP-SPACE",10); - else if (!memcmp("S2",cur->swh.magic.magic,2)) - memcpy(cur->swh.magic.magic,"SWAPSPACE2",10); - else { - if (noresume) - return -EINVAL; - panic("%sUnable to find suspended-data signature (%.10s - misspelled?\n", - name_resume, cur->swh.magic.magic); - } - if (noresume) { - /* We don't do a sanity check here: we want to restore the swap - whatever version of kernel made the suspend image; - We need to write swap, but swap is *not* enabled so - we must write the device directly */ - printk("%s: Fixing swap signatures %s...\n", name_resume, resume_file); - bdev_write_page(bdev, 0, cur); - } +int bio_write_page(pgoff_t page_off, void * page) +{ + return submit(WRITE,page_off,page); +} - printk( "%sSignature found, resuming\n", name_resume ); - MDELAY(1000); +/* + * Sanity check if this image makes sense with this kernel/swap context + * I really don't think that it's foolproof but more than nothing.. + */ - if (bdev_read_page(bdev, next.val, cur)) return -EIO; - if (sanity_check(&cur->sh)) /* Is this same machine? */ - return -EPERM; - PREPARENEXT; +static const char * __init sanity_check(void) +{ + dump_info(); + if(swsusp_info.version_code != LINUX_VERSION_CODE) + return "kernel version"; + if(swsusp_info.num_physpages != num_physpages) + return "memory size"; + if (strcmp(swsusp_info.uts.sysname,system_utsname.sysname)) + return "system type"; + if (strcmp(swsusp_info.uts.release,system_utsname.release)) + return "kernel release"; + if (strcmp(swsusp_info.uts.version,system_utsname.version)) + return "version"; + if (strcmp(swsusp_info.uts.machine,system_utsname.machine)) + return "machine"; + if(swsusp_info.cpus != num_online_cpus()) + return "number of cpus"; + return NULL; +} - pagedir_save = cur->sh.suspend_pagedir; - nr_copy_pages = cur->sh.num_pbes; - nr_pgdir_pages = SUSPEND_PD_PAGES(nr_copy_pages); - pagedir_order = get_bitmask_order(nr_pgdir_pages); - pagedir_nosave = (suspend_pagedir_t *)__get_free_pages(GFP_ATOMIC, pagedir_order); - if (!pagedir_nosave) - return -ENOMEM; +static int __init check_header(void) +{ + const char * reason = NULL; + int error; - PRINTK( "%sReading pagedir, ", name_resume ); + if ((error = bio_read_page(swp_offset(swsusp_header.swsusp_info), &swsusp_info))) + return error; - /* We get pages in reverse order of saving! */ - for (i=nr_pgdir_pages-1; i>=0; i--) { - BUG_ON (!next.val); - cur = (union diskpage *)((char *) pagedir_nosave)+i; - if (bdev_read_page(bdev, next.val, cur)) return -EIO; - PREPARENEXT; + /* Is this same machine? */ + if ((reason = sanity_check())) { + printk(KERN_ERR "swsusp: Resume mismatch: %s\n",reason); + return -EPERM; } - BUG_ON (next.val); + nr_copy_pages = swsusp_info.image_pages; + return error; +} - if (relocate_pagedir()) - return -ENOMEM; - if (check_pagedir()) - return -ENOMEM; +static int __init check_sig(void) +{ + int error; - printk( "Reading image data (%d pages): ", nr_copy_pages ); - for(i=0; i < nr_copy_pages; i++) { - swp_entry_t swap_address = (pagedir_nosave+i)->swap_address; - if (!(i%100)) - printk( "." ); - /* You do not need to check for overlaps... - ... check_pagedir already did this work */ - if (bdev_read_page(bdev, swp_offset(swap_address) * PAGE_SIZE, (char *)((pagedir_nosave+i)->address))) - return -EIO; + memset(&swsusp_header,0,sizeof(swsusp_header)); + if ((error = bio_read_page(0,&swsusp_header))) + return error; + if (!memcmp(SWSUSP_SIG,swsusp_header.sig,10)) { + memcpy(swsusp_header.sig,swsusp_header.orig_sig,10); + + /* + * Reset swap signature now. + */ + error = bio_write_page(0,&swsusp_header); + } else { + pr_debug(KERN_ERR "swsusp: Suspend partition has wrong signature?\n"); + return -EINVAL; } - printk( "|\n" ); - return 0; + if (!error) + pr_debug("swsusp: Signature found, resuming\n"); + return error; } -static int __init read_suspend_image(const char * specialfile, int noresume) + +int __init verify(void) { - union diskpage *cur; - unsigned long scratch_page = 0; int error; - char b[BDEVNAME_SIZE]; - resume_device = name_to_dev_t(specialfile); - scratch_page = get_zeroed_page(GFP_ATOMIC); - cur = (void *) scratch_page; - if (cur) { - struct block_device *bdev; - printk("Resuming from device %s\n", - __bdevname(resume_device, b)); - bdev = open_by_devnum(resume_device, FMODE_READ); - if (IS_ERR(bdev)) { - error = PTR_ERR(bdev); - } else { - set_blocksize(bdev, PAGE_SIZE); - error = __read_suspend_image(bdev, cur, noresume); - blkdev_put(bdev); - } - } else error = -ENOMEM; - - if (scratch_page) - free_page(scratch_page); - switch (error) { - case 0: - PRINTK("Reading resume file was successful\n"); - break; - case -EINVAL: - break; - case -EIO: - printk( "%sI/O error\n", name_resume); - break; - case -ENOENT: - printk( "%s%s: No such file or directory\n", name_resume, specialfile); - break; - case -ENOMEM: - printk( "%sNot enough memory\n", name_resume); - break; - default: - printk( "%sError %d resuming\n", name_resume, error ); - } - MDELAY(1000); + if (!(error = check_sig())) + error = check_header(); return error; } + /** - * software_resume - Resume from a saved image. - * - * Called as a late_initcall (so all devices are discovered and - * initialized), we call swsusp to see if we have a saved image or not. - * If so, we quiesce devices, then restore the saved image. We will - * return above (in pm_suspend_disk() ) if everything goes well. - * Otherwise, we fail gracefully and return to the normally - * scheduled program. + * swsusp_read_data - Read image pages from swap. * + * You do not need to check for overlaps, check_pagedir() + * already did that. */ -static int __init software_resume(void) -{ - if (num_online_cpus() > 1) { - printk(KERN_WARNING "Software Suspend has malfunctioning SMP support. Disabled :(\n"); - return -EINVAL; - } - /* We enable the possibility of machine suspend */ - software_suspend_enabled = 1; - if (!resume_status) - return 0; - printk( "%s", name_resume ); - if (resume_status == NORESUME) { - if(resume_file[0]) - read_suspend_image(resume_file, 1); - printk( "disabled\n" ); - return 0; - } - MDELAY(1000); +static int __init data_read(void) +{ + struct pbe * p; + int error; + int i; - if (pm_prepare_console()) - printk("swsusp: Can't allocate a console... proceeding\n"); + if ((error = swsusp_pagedir_relocate())) + return error; - if (!resume_file[0] && resume_status == RESUME_SPECIFIED) { - printk( "suspension device unspecified\n" ); - return -EINVAL; + printk( "Reading image data (%d pages): ", nr_copy_pages ); + for(i = 0, p = pagedir_nosave; i < nr_copy_pages && !error; i++, p++) { + if (!(i%100)) + printk( "." ); + error = bio_read_page(swp_offset(p->swap_address), + (void *)p->address); } + printk(" %d done.\n",i); + return error; - printk( "resuming from %s\n", resume_file); - if (read_suspend_image(resume_file, 0)) - goto read_failure; - /* FIXME: Should we stop processes here, just to be safer? */ - disable_nonboot_cpus(); - device_suspend(3); - do_magic(1); - panic("This never returns"); - -read_failure: - pm_restore_console(); - return 0; } -late_initcall(software_resume); +extern dev_t __init name_to_dev_t(const char *line); -static int __init resume_setup(char *str) +static int __init read_pagedir(void) { - if (resume_status == NORESUME) - return 1; + unsigned long addr; + int i, n = swsusp_info.pagedir_pages; + int error = 0; - strncpy( resume_file, str, 255 ); - resume_status = RESUME_SPECIFIED; + pagedir_order = get_bitmask_order(n); - return 1; + addr =__get_free_pages(GFP_ATOMIC, pagedir_order); + if (!addr) + return -ENOMEM; + pagedir_nosave = (struct pbe *)addr; + + pr_debug("pmdisk: Reading pagedir (%d Pages)\n",n); + + for (i = 0; i < n && !error; i++, addr += PAGE_SIZE) { + unsigned long offset = swp_offset(swsusp_info.pagedir[i]); + if (offset) + error = bio_read_page(offset, (void *)addr); + else + error = -EFAULT; + } + if (error) + free_pages((unsigned long)pagedir_nosave,pagedir_order); + return error; } -static int __init noresume_setup(char *str) +static int __init read_suspend_image(void) { - resume_status = NORESUME; - return 1; + int error = 0; + + if ((error = verify())) + return error; + if ((error = read_pagedir())) + return error; + if ((error = data_read())) { + free_pages((unsigned long)pagedir_nosave,pagedir_order); + } + return error; } -__setup("noresume", noresume_setup); -__setup("resume=", resume_setup); +/** + * pmdisk_read - Read saved image from swap. + */ + +int __init swsusp_read(void) +{ + int error; + + if (!strlen(resume_file)) + return -ENOENT; -EXPORT_SYMBOL(software_suspend); -EXPORT_SYMBOL(software_suspend_enabled); + resume_device = name_to_dev_t(resume_file); + pr_debug("swsusp: Resume From Partition: %s\n", resume_file); + + resume_bdev = open_by_devnum(resume_device, FMODE_READ); + if (!IS_ERR(resume_bdev)) { + set_blocksize(resume_bdev, PAGE_SIZE); + error = read_suspend_image(); + blkdev_put(resume_bdev); + } else + error = PTR_ERR(resume_bdev); + + if (!error) + pr_debug("Reading resume file was successful\n"); + else + pr_debug("pmdisk: Error %d resuming\n", error); + return error; +} --- linux-2.6.9-rc1/kernel/profile.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/kernel/profile.c 2004-09-02 20:51:53.000000000 -0700 @@ -8,38 +8,44 @@ #include #include #include +#include +#include #include -unsigned int * prof_buffer; -unsigned long prof_len; -unsigned long prof_shift; -int prof_on; +static atomic_t *prof_buffer; +static unsigned long prof_len, prof_shift; +static int prof_on; +static cpumask_t prof_cpu_mask = CPU_MASK_ALL; -int __init profile_setup(char * str) +static int __init profile_setup(char * str) { int par; + + if (!strncmp(str, "schedule", 8)) { + prof_on = 2; + printk(KERN_INFO "kernel schedule profiling enabled\n"); + if (str[7] == ',') + str += 8; + } if (get_option(&str,&par)) { prof_shift = par; prof_on = 1; - printk(KERN_INFO "kernel profiling enabled\n"); + printk(KERN_INFO "kernel profiling enabled (shift: %ld)\n", + prof_shift); } return 1; } +__setup("profile=", profile_setup); void __init profile_init(void) { - unsigned int size; - if (!prof_on) return; /* only text is profiled */ - prof_len = _etext - _stext; - prof_len >>= prof_shift; - - size = prof_len * sizeof(unsigned int) + PAGE_SIZE - 1; - prof_buffer = (unsigned int *) alloc_bootmem(size); + prof_len = (_etext - _stext) >> prof_shift; + prof_buffer = alloc_bootmem(prof_len*sizeof(atomic_t)); } /* Profile event notifications */ @@ -47,31 +53,54 @@ void __init profile_init(void) #ifdef CONFIG_PROFILING static DECLARE_RWSEM(profile_rwsem); -static struct notifier_block * exit_task_notifier; -static struct notifier_block * exit_mmap_notifier; -static struct notifier_block * exec_unmap_notifier; +static rwlock_t handoff_lock = RW_LOCK_UNLOCKED; +static struct notifier_block * task_exit_notifier; +static struct notifier_block * task_free_notifier; +static struct notifier_block * munmap_notifier; -void profile_exit_task(struct task_struct * task) +void profile_task_exit(struct task_struct * task) { down_read(&profile_rwsem); - notifier_call_chain(&exit_task_notifier, 0, task); + notifier_call_chain(&task_exit_notifier, 0, task); up_read(&profile_rwsem); } -void profile_exit_mmap(struct mm_struct * mm) +int profile_handoff_task(struct task_struct * task) { - down_read(&profile_rwsem); - notifier_call_chain(&exit_mmap_notifier, 0, mm); - up_read(&profile_rwsem); + int ret; + read_lock(&handoff_lock); + ret = notifier_call_chain(&task_free_notifier, 0, task); + read_unlock(&handoff_lock); + return (ret == NOTIFY_OK) ? 1 : 0; } -void profile_exec_unmap(struct mm_struct * mm) +void profile_munmap(unsigned long addr) { down_read(&profile_rwsem); - notifier_call_chain(&exec_unmap_notifier, 0, mm); + notifier_call_chain(&munmap_notifier, 0, (void *)addr); up_read(&profile_rwsem); } +int task_handoff_register(struct notifier_block * n) +{ + int err = -EINVAL; + + write_lock(&handoff_lock); + err = notifier_chain_register(&task_free_notifier, n); + write_unlock(&handoff_lock); + return err; +} + +int task_handoff_unregister(struct notifier_block * n) +{ + int err = -EINVAL; + + write_lock(&handoff_lock); + err = notifier_chain_unregister(&task_free_notifier, n); + write_unlock(&handoff_lock); + return err; +} + int profile_event_register(enum profile_type type, struct notifier_block * n) { int err = -EINVAL; @@ -79,14 +108,11 @@ int profile_event_register(enum profile_ down_write(&profile_rwsem); switch (type) { - case EXIT_TASK: - err = notifier_chain_register(&exit_task_notifier, n); + case PROFILE_TASK_EXIT: + err = notifier_chain_register(&task_exit_notifier, n); break; - case EXIT_MMAP: - err = notifier_chain_register(&exit_mmap_notifier, n); - break; - case EXEC_UNMAP: - err = notifier_chain_register(&exec_unmap_notifier, n); + case PROFILE_MUNMAP: + err = notifier_chain_register(&munmap_notifier, n); break; } @@ -103,14 +129,11 @@ int profile_event_unregister(enum profil down_write(&profile_rwsem); switch (type) { - case EXIT_TASK: - err = notifier_chain_unregister(&exit_task_notifier, n); - break; - case EXIT_MMAP: - err = notifier_chain_unregister(&exit_mmap_notifier, n); + case PROFILE_TASK_EXIT: + err = notifier_chain_unregister(&task_exit_notifier, n); break; - case EXEC_UNMAP: - err = notifier_chain_unregister(&exec_unmap_notifier, n); + case PROFILE_MUNMAP: + err = notifier_chain_unregister(&munmap_notifier, n); break; } @@ -150,8 +173,150 @@ void profile_hook(struct pt_regs * regs) EXPORT_SYMBOL_GPL(register_profile_notifier); EXPORT_SYMBOL_GPL(unregister_profile_notifier); +EXPORT_SYMBOL_GPL(task_handoff_register); +EXPORT_SYMBOL_GPL(task_handoff_unregister); #endif /* CONFIG_PROFILING */ EXPORT_SYMBOL_GPL(profile_event_register); EXPORT_SYMBOL_GPL(profile_event_unregister); + +void profile_hit(int type, void *__pc) +{ + unsigned long pc; + + if (prof_on != type || !prof_buffer) + return; + pc = ((unsigned long)__pc - (unsigned long)_stext) >> prof_shift; + atomic_inc(&prof_buffer[min(pc, prof_len - 1)]); +} + +void profile_tick(int type, struct pt_regs *regs) +{ + if (type == CPU_PROFILING) + profile_hook(regs); + if (!user_mode(regs) && cpu_isset(smp_processor_id(), prof_cpu_mask)) + profile_hit(type, (void *)profile_pc(regs)); +} + +#ifdef CONFIG_PROC_FS +#include +#include +#include + +static int prof_cpu_mask_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = cpumask_scnprintf(page, count, *(cpumask_t *)data); + if (count - len < 2) + return -EINVAL; + len += sprintf(page + len, "\n"); + return len; +} + +static int prof_cpu_mask_write_proc (struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + cpumask_t *mask = (cpumask_t *)data; + unsigned long full_count = count, err; + cpumask_t new_value; + + err = cpumask_parse(buffer, count, new_value); + if (err) + return err; + + *mask = new_value; + return full_count; +} + +void create_prof_cpu_mask(struct proc_dir_entry *root_irq_dir) +{ + struct proc_dir_entry *entry; + + /* create /proc/irq/prof_cpu_mask */ + if (!(entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir))) + return; + entry->nlink = 1; + entry->data = (void *)&prof_cpu_mask; + entry->read_proc = prof_cpu_mask_read_proc; + entry->write_proc = prof_cpu_mask_write_proc; +} + +/* + * This function accesses profiling information. The returned data is + * binary: the sampling step and the actual contents of the profile + * buffer. Use of the program readprofile is recommended in order to + * get meaningful info out of these data. + */ +static ssize_t +read_profile(struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + unsigned long p = *ppos; + ssize_t read; + char * pnt; + unsigned int sample_step = 1 << prof_shift; + + if (p >= (prof_len+1)*sizeof(unsigned int)) + return 0; + if (count > (prof_len+1)*sizeof(unsigned int) - p) + count = (prof_len+1)*sizeof(unsigned int) - p; + read = 0; + + while (p < sizeof(unsigned int) && count > 0) { + put_user(*((char *)(&sample_step)+p),buf); + buf++; p++; count--; read++; + } + pnt = (char *)prof_buffer + p - sizeof(atomic_t); + if (copy_to_user(buf,(void *)pnt,count)) + return -EFAULT; + read += count; + *ppos += read; + return read; +} + +/* + * Writing to /proc/profile resets the counters + * + * Writing a 'profiling multiplier' value into it also re-sets the profiling + * interrupt frequency, on architectures that support this. + */ +static ssize_t write_profile(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ +#ifdef CONFIG_SMP + extern int setup_profiling_timer (unsigned int multiplier); + + if (count == sizeof(int)) { + unsigned int multiplier; + + if (copy_from_user(&multiplier, buf, sizeof(int))) + return -EFAULT; + + if (setup_profiling_timer(multiplier)) + return -EINVAL; + } +#endif + + memset(prof_buffer, 0, prof_len * sizeof(atomic_t)); + return count; +} + +static struct file_operations proc_profile_operations = { + .read = read_profile, + .write = write_profile, +}; + +static int __init create_proc_profile(void) +{ + struct proc_dir_entry *entry; + + if (!prof_on) + return 0; + if (!(entry = create_proc_entry("profile", S_IWUSR | S_IRUGO, NULL))) + return 0; + entry->proc_fops = &proc_profile_operations; + entry->size = (1+prof_len) * sizeof(atomic_t); + return 0; +} +module_init(create_proc_profile); +#endif /* CONFIG_PROC_FS */ --- linux-2.6.9-rc1/kernel/ptrace.c 2004-08-24 00:03:49.000000000 -0700 +++ 25/kernel/ptrace.c 2004-09-03 00:56:56.549563808 -0700 @@ -55,6 +55,15 @@ void __ptrace_unlink(task_t *child) REMOVE_LINKS(child); child->parent = child->real_parent; SET_LINKS(child); + + if (child->state == TASK_TRACED) { + /* + * Turn a tracing stop into a normal stop now, + * since with no tracer there would be no way + * to wake it up with SIGCONT or SIGKILL. + */ + child->state = TASK_STOPPED; + } } /* @@ -62,20 +71,35 @@ void __ptrace_unlink(task_t *child) */ int ptrace_check_attach(struct task_struct *child, int kill) { - if (!(child->ptrace & PT_PTRACED)) - return -ESRCH; + int ret = -ESRCH; - if (child->parent != current) - return -ESRCH; + /* + * We take the read lock around doing both checks to close a + * possible race where someone else was tracing our child and + * detached between these two checks. After this locked check, + * we are sure that this is our traced child and that can only + * be changed by us so it's not changing right after this. + */ + read_lock(&tasklist_lock); + if ((child->ptrace & PT_PTRACED) && child->parent == current && + child->signal != NULL) { + ret = 0; + spin_lock_irq(&child->sighand->siglock); + if (child->state == TASK_STOPPED) { + child->state = TASK_TRACED; + } else if (child->state != TASK_TRACED && !kill) { + ret = -ESRCH; + } + spin_unlock_irq(&child->sighand->siglock); + } + read_unlock(&tasklist_lock); - if (!kill) { - if (child->state != TASK_STOPPED) - return -ESRCH; + if (!ret && !kill) { wait_task_inactive(child); } /* All systems go.. */ - return 0; + return ret; } int ptrace_attach(struct task_struct *task) @@ -322,24 +346,3 @@ int ptrace_request(struct task_struct *c return ret; } - -void ptrace_notify(int exit_code) -{ - BUG_ON (!(current->ptrace & PT_PTRACED)); - - /* Let the debugger run. */ - current->exit_code = exit_code; - set_current_state(TASK_STOPPED); - notify_parent(current, SIGCHLD); - schedule(); - - /* - * Signals sent while we were stopped might set TIF_SIGPENDING. - */ - - spin_lock_irq(¤t->sighand->siglock); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); -} - -EXPORT_SYMBOL(ptrace_notify); --- linux-2.6.9-rc1/kernel/rcupdate.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/kernel/rcupdate.c 2004-09-03 00:57:02.204704096 -0700 @@ -72,6 +72,10 @@ DEFINE_PER_CPU(struct rcu_data, rcu_bh_d static DEFINE_PER_CPU(struct tasklet_struct, rcu_tasklet) = {NULL}; static int maxbatch = 10; +static atomic_t rcu_barrier_cpu_count; +static struct semaphore rcu_barrier_sema; +static struct completion rcu_barrier_completion; + /** * call_rcu - Queue an RCU callback for invocation after a grace period. * @head: structure to be used for queueing the RCU updates. @@ -129,6 +133,42 @@ void fastcall call_rcu_bh(struct rcu_hea local_irq_restore(flags); } +static void rcu_barrier_callback(struct rcu_head *notused) +{ + if (atomic_dec_and_test(&rcu_barrier_cpu_count)) + complete(&rcu_barrier_completion); +} + +/* + * Called with preemption disabled, and from cross-cpu IRQ context. + */ +static void rcu_barrier_func(void *notused) +{ + int cpu = smp_processor_id(); + struct rcu_data *rdp = &per_cpu(rcu_data, cpu); + struct rcu_head *head; + + head = &rdp->barrier; + atomic_inc(&rcu_barrier_cpu_count); + call_rcu(head, rcu_barrier_callback); +} + +/** + * rcu_barrier - Wait until all the in-flight RCUs are complete. + */ +void rcu_barrier(void) +{ + BUG_ON(in_interrupt()); + /* Take cpucontrol semaphore to protect against CPU hotplug */ + down(&rcu_barrier_sema); + init_completion(&rcu_barrier_completion); + atomic_set(&rcu_barrier_cpu_count, 0); + on_each_cpu(rcu_barrier_func, NULL, 0, 1); + wait_for_completion(&rcu_barrier_completion); + up(&rcu_barrier_sema); +} +EXPORT_SYMBOL(rcu_barrier); + /* * Invoke the completed RCU callbacks. They are expected to be in * a per-cpu list. @@ -422,6 +462,7 @@ static struct notifier_block __devinitda */ void __init rcu_init(void) { + sema_init(&rcu_barrier_sema, 1); rcu_cpu_notify(&rcu_nb, CPU_UP_PREPARE, (void *)(long)smp_processor_id()); /* Register notifier for non-boot CPUs */ --- linux-2.6.9-rc1/kernel/resource.c 2004-08-24 00:03:12.000000000 -0700 +++ 25/kernel/resource.c 2004-09-02 20:51:53.000000000 -0700 @@ -315,11 +315,12 @@ EXPORT_SYMBOL(allocate_resource); */ int insert_resource(struct resource *parent, struct resource *new) { - int result = 0; + int result; struct resource *first, *next; write_lock(&resource_lock); begin: + result = 0; first = __request_resource(parent, new); if (!first) goto out; @@ -328,15 +329,20 @@ int insert_resource(struct resource *par if (first == parent) goto out; - for (next = first; next->sibling; next = next->sibling) + /* Resource fully contained by the clashing resource? Recurse into it */ + if (first->start <= new->start && first->end >= new->end) { + parent = first; + goto begin; + } + + for (next = first; ; next = next->sibling) { + /* Partial overlap? Bad, and unfixable */ + if (next->start < new->start || next->end > new->end) + goto out; + if (!next->sibling) + break; if (next->sibling->start > new->end) break; - - /* existing resource includes new resource */ - if (next->end >= new->end) { - parent = next; - result = 0; - goto begin; } result = 0; --- linux-2.6.9-rc1/kernel/sched.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/kernel/sched.c 2004-09-03 00:57:00.324989856 -0700 @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -38,149 +39,95 @@ #include #include #include +#include #include +#include #include +#include +#include #include #include -#ifdef CONFIG_NUMA -#define cpu_to_node_mask(cpu) node_to_cpumask(cpu_to_node(cpu)) -#else -#define cpu_to_node_mask(cpu) (cpu_online_map) -#endif - /* * Convert user-nice values [ -20 ... 0 ... 19 ] * to static priority [ MAX_RT_PRIO..MAX_PRIO-1 ], * and back. */ -#define NICE_TO_PRIO(nice) (MAX_RT_PRIO + (nice) + 20) -#define PRIO_TO_NICE(prio) ((prio) - MAX_RT_PRIO - 20) +#define NICE_TO_PRIO(nice) (MAX_RT_PRIO + (nice) + 30) +#define PRIO_TO_NICE(prio) ((prio) - MAX_RT_PRIO - 30) #define TASK_NICE(p) PRIO_TO_NICE((p)->static_prio) /* * 'User priority' is the nice value converted to something we * can work with better when scaling various scheduler parameters, - * it's a [ 0 ... 39 ] range. + * it's a [ 0 ... 58 ] range. */ #define USER_PRIO(p) ((p)-MAX_RT_PRIO) -#define TASK_USER_PRIO(p) USER_PRIO((p)->static_prio) #define MAX_USER_PRIO (USER_PRIO(MAX_PRIO)) -#define AVG_TIMESLICE (MIN_TIMESLICE + ((MAX_TIMESLICE - MIN_TIMESLICE) *\ - (MAX_PRIO-1-NICE_TO_PRIO(0))/(MAX_USER_PRIO - 1))) + +#define US_TO_JIFFIES(x) ((x) * HZ / 1000000) +#define JIFFIES_TO_US(x) ((x) * 1000000 / HZ) /* - * Some helpers for converting nanosecond timing to jiffy resolution + * MIN_TIMESLICE is the timeslice that a minimum priority process gets if there + * is a maximum priority process runnable. MAX_TIMESLICE is derived from the + * formula in task_timeslice. It cannot be changed here. It is the timesilce + * that the maximum priority process will get. Larger timeslices are attainable + * by low priority processes however. */ -#define NS_TO_JIFFIES(TIME) ((TIME) / (1000000000 / HZ)) -#define JIFFIES_TO_NS(TIME) ((TIME) * (1000000000 / HZ)) +int sched_base_timeslice = 64; +int sched_min_base = 1; +int sched_max_base = 10000; + +#define RT_TIMESLICE (50 * 1000 / HZ) /* 50ms */ +#define BASE_TIMESLICE (sched_base_timeslice) +#define MIN_TIMESLICE 1 + +/* Maximum amount of history that will be used to calculate priority */ +#define MAX_SLEEP_SHIFT 19 +#define MAX_SLEEP (1UL << MAX_SLEEP_SHIFT) /* roughly 0.52s */ /* - * These are the 'tuning knobs' of the scheduler: - * - * Minimum timeslice is 10 msecs, default timeslice is 100 msecs, - * maximum timeslice is 200 msecs. Timeslices get refilled after - * they expire. + * Maximum effect that 1 block of activity (run/sleep/etc) can have. This is + * will moderate dicard freak events (eg. SIGSTOP) */ -#define MIN_TIMESLICE ( 10 * HZ / 1000) -#define MAX_TIMESLICE (200 * HZ / 1000) -#define ON_RUNQUEUE_WEIGHT 30 -#define CHILD_PENALTY 95 -#define PARENT_PENALTY 100 -#define EXIT_WEIGHT 3 -#define PRIO_BONUS_RATIO 25 -#define MAX_BONUS (MAX_USER_PRIO * PRIO_BONUS_RATIO / 100) -#define INTERACTIVE_DELTA 2 -#define MAX_SLEEP_AVG (AVG_TIMESLICE * MAX_BONUS) -#define STARVATION_LIMIT (MAX_SLEEP_AVG) -#define NS_MAX_SLEEP_AVG (JIFFIES_TO_NS(MAX_SLEEP_AVG)) -#define CREDIT_LIMIT 100 +#define MAX_SLEEP_AFFECT (MAX_SLEEP/4) /* - * If a task is 'interactive' then we reinsert it in the active - * array after it has expired its current timeslice. (it will not - * continue to run immediately, it will still roundrobin with - * other interactive tasks.) - * - * This part scales the interactivity limit depending on niceness. - * - * We scale it linearly, offset by the INTERACTIVE_DELTA delta. - * Here are a few examples of different nice levels: - * - * TASK_INTERACTIVE(-20): [1,1,1,1,1,1,1,1,1,0,0] - * TASK_INTERACTIVE(-10): [1,1,1,1,1,1,1,0,0,0,0] - * TASK_INTERACTIVE( 0): [1,1,1,1,0,0,0,0,0,0,0] - * TASK_INTERACTIVE( 10): [1,1,0,0,0,0,0,0,0,0,0] - * TASK_INTERACTIVE( 19): [0,0,0,0,0,0,0,0,0,0,0] - * - * (the X axis represents the possible -5 ... 0 ... +5 dynamic - * priority range a task can explore, a value of '1' means the - * task is rated interactive.) - * - * Ie. nice +19 tasks can never get 'interactive' enough to be - * reinserted into the active array. And only heavily CPU-hog nice -20 - * tasks will be expired. Default nice 0 tasks are somewhere between, - * it takes some effort for them to get interactive, but it's not - * too hard. + * The amount of history can be decreased (on fork for example). This puts a + * lower bound on it. */ +#define MIN_HISTORY (MAX_SLEEP/8) -#define CURRENT_BONUS(p) \ - (NS_TO_JIFFIES((p)->sleep_avg) * MAX_BONUS / \ - MAX_SLEEP_AVG) - -#ifdef CONFIG_SMP -#define TIMESLICE_GRANULARITY(p) (MIN_TIMESLICE * \ - (1 << (((MAX_BONUS - CURRENT_BONUS(p)) ? : 1) - 1)) * \ - num_online_cpus()) -#else -#define TIMESLICE_GRANULARITY(p) (MIN_TIMESLICE * \ - (1 << (((MAX_BONUS - CURRENT_BONUS(p)) ? : 1) - 1))) -#endif - -#define SCALE(v1,v1_max,v2_max) \ - (v1) * (v2_max) / (v1_max) - -#define DELTA(p) \ - (SCALE(TASK_NICE(p), 40, MAX_BONUS) + INTERACTIVE_DELTA) - -#define TASK_INTERACTIVE(p) \ - ((p)->prio <= (p)->static_prio - DELTA(p)) - -#define INTERACTIVE_SLEEP(p) \ - (JIFFIES_TO_NS(MAX_SLEEP_AVG * \ - (MAX_BONUS / 2 + DELTA((p)) + 1) / MAX_BONUS - 1)) +#define FORKED_TS_MAX (US_TO_JIFFIES(MIN_HISTORY) ?: 1) -#define HIGH_CREDIT(p) \ - ((p)->interactive_credit > CREDIT_LIMIT) - -#define LOW_CREDIT(p) \ - ((p)->interactive_credit < -CREDIT_LIMIT) - -#define TASK_PREEMPTS_CURR(p, rq) \ - ((p)->prio < (rq)->curr->prio) +/* + * SLEEP_FACTOR is a fixed point factor used to scale history tracking things. + * In particular: total_time, sleep_time, sleep_avg. + */ +#define SLEEP_FACTOR 1024 /* - * BASE_TIMESLICE scales user-nice values [ -20 ... 19 ] - * to time slice values. - * - * The higher a thread's priority, the bigger timeslices - * it gets during one round of execution. But even the lowest - * priority thread gets MIN_TIMESLICE worth of execution time. - * - * task_timeslice() is the interface that is used by the scheduler. + * The scheduler classifies a process as performing one of the following + * activities */ +#define STIME_SLEEP 1 /* Sleeping */ +#define STIME_RUN 2 /* Using CPU */ -#define BASE_TIMESLICE(p) (MIN_TIMESLICE + \ - ((MAX_TIMESLICE - MIN_TIMESLICE) * \ - (MAX_PRIO-1 - (p)->static_prio) / (MAX_USER_PRIO-1))) +#define TASK_PREEMPTS_CURR(p, rq) ( (p)->prio < (rq)->curr->prio ) + +#define task_hot(p, now, sd) ((now) - (p)->timestamp < (sd)->cache_hot_time) -static unsigned int task_timeslice(task_t *p) +enum idle_type { - return BASE_TIMESLICE(p); -} + IDLE, + NOT_IDLE, + NEWLY_IDLE, + MAX_IDLE_TYPES +}; -#define task_hot(p, now, sd) ((now) - (p)->timestamp < (sd)->cache_hot_time) +struct sched_domain; /* * These are the runqueue data structures: @@ -191,6 +138,7 @@ static unsigned int task_timeslice(task_ typedef struct runqueue runqueue_t; struct prio_array { + int min_prio; unsigned int nr_active; unsigned long bitmap[BITMAP_SIZE]; struct list_head queue[MAX_PRIO]; @@ -214,16 +162,17 @@ struct runqueue { #ifdef CONFIG_SMP unsigned long cpu_load; #endif + unsigned long array_sequence; + unsigned long nr_uninterruptible; unsigned long long nr_switches; - unsigned long expired_timestamp, nr_uninterruptible; - unsigned long long timestamp_last_tick; task_t *curr, *idle; struct mm_struct *prev_mm; - prio_array_t *active, *expired, arrays[2]; - int best_expired_prio; atomic_t nr_iowait; + prio_array_t *active, *expired, arrays[2]; #ifdef CONFIG_SMP + unsigned long long timestamp_last_tick; + struct sched_domain *sd; /* For active balancing */ @@ -233,10 +182,186 @@ struct runqueue { task_t *migration_thread; struct list_head migration_queue; #endif + +#ifdef CONFIG_SCHEDSTATS + /* latency stats */ + struct sched_info rq_sched_info; + + /* sys_sched_yield() stats */ + unsigned long yld_exp_empty; + unsigned long yld_act_empty; + unsigned long yld_both_empty; + unsigned long yld_cnt; + + /* schedule() stats */ + unsigned long sched_noswitch; + unsigned long sched_switch; + unsigned long sched_cnt; + unsigned long sched_goidle; + + /* pull_task() stats */ + unsigned long pt_gained[MAX_IDLE_TYPES]; + unsigned long pt_lost[MAX_IDLE_TYPES]; + + /* active_load_balance() stats */ + unsigned long alb_cnt; + unsigned long alb_lost; + unsigned long alb_gained; + unsigned long alb_failed; + + /* try_to_wake_up() stats */ + unsigned long ttwu_cnt; + unsigned long ttwu_attempts; + unsigned long ttwu_moved; + + /* wake_up_new_task() stats */ + unsigned long wunt_cnt; + unsigned long wunt_moved; + + /* sched_migrate_task() stats */ + unsigned long smt_cnt; + + /* sched_balance_exec() stats */ + unsigned long sbe_cnt; +#endif }; static DEFINE_PER_CPU(struct runqueue, runqueues); +/* + * sched-domains (multiprocessor balancing) declarations: + */ +#ifdef CONFIG_SMP +#define SCHED_LOAD_SCALE 128UL /* increase resolution of load */ + +#define SD_BALANCE_NEWIDLE 1 /* Balance when about to become idle */ +#define SD_BALANCE_EXEC 2 /* Balance on exec */ +#define SD_WAKE_IDLE 4 /* Wake to idle CPU on task wakeup */ +#define SD_WAKE_AFFINE 8 /* Wake task to waking CPU */ +#define SD_WAKE_BALANCE 16 /* Perform balancing at task wakeup */ +#define SD_SHARE_CPUPOWER 32 /* Domain members share cpu power */ + +struct sched_group { + struct sched_group *next; /* Must be a circular list */ + cpumask_t cpumask; + + /* + * CPU power of this group, SCHED_LOAD_SCALE being max power for a + * single CPU. This should be read only (except for setup). Although + * it will need to be written to at cpu hot(un)plug time, perhaps the + * cpucontrol semaphore will provide enough exclusion? + */ + unsigned long cpu_power; +}; + +struct sched_domain { + /* These fields must be setup */ + struct sched_domain *parent; /* top domain must be null terminated */ + struct sched_group *groups; /* the balancing groups of the domain */ + cpumask_t span; /* span of all CPUs in this domain */ + unsigned long min_interval; /* Minimum balance interval ms */ + unsigned long max_interval; /* Maximum balance interval ms */ + unsigned int busy_factor; /* less balancing by factor if busy */ + unsigned int imbalance_pct; /* No balance until over watermark */ + unsigned long long cache_hot_time; /* Task considered cache hot (ns) */ + unsigned int cache_nice_tries; /* Leave cache hot tasks for # tries */ + unsigned int per_cpu_gain; /* CPU % gained by adding domain cpus */ + int flags; /* See SD_* */ + + /* Runtime fields. */ + unsigned long last_balance; /* init to jiffies. units in jiffies */ + unsigned int balance_interval; /* initialise to 1. units in ms. */ + unsigned int nr_balance_failed; /* initialise to 0 */ + +#ifdef CONFIG_SCHEDSTATS + /* load_balance() stats */ + unsigned long lb_cnt[MAX_IDLE_TYPES]; + unsigned long lb_failed[MAX_IDLE_TYPES]; + unsigned long lb_imbalance[MAX_IDLE_TYPES]; + unsigned long lb_nobusyg[MAX_IDLE_TYPES]; + unsigned long lb_nobusyq[MAX_IDLE_TYPES]; + + /* sched_balance_exec() stats */ + unsigned long sbe_attempts; + unsigned long sbe_pushed; + + /* try_to_wake_up() stats */ + unsigned long ttwu_wake_affine; + unsigned long ttwu_wake_balance; +#endif +}; + +#ifndef ARCH_HAS_SCHED_TUNE +#ifdef CONFIG_SCHED_SMT +#define ARCH_HAS_SCHED_WAKE_IDLE +/* Common values for SMT siblings */ +#define SD_SIBLING_INIT (struct sched_domain) { \ + .span = CPU_MASK_NONE, \ + .parent = NULL, \ + .groups = NULL, \ + .min_interval = 1, \ + .max_interval = 2, \ + .busy_factor = 8, \ + .imbalance_pct = 110, \ + .cache_hot_time = 0, \ + .cache_nice_tries = 0, \ + .per_cpu_gain = 25, \ + .flags = SD_BALANCE_NEWIDLE \ + | SD_BALANCE_EXEC \ + | SD_WAKE_AFFINE \ + | SD_WAKE_IDLE \ + | SD_SHARE_CPUPOWER, \ + .last_balance = jiffies, \ + .balance_interval = 1, \ + .nr_balance_failed = 0, \ +} +#endif + +/* Common values for CPUs */ +#define SD_CPU_INIT (struct sched_domain) { \ + .span = CPU_MASK_NONE, \ + .parent = NULL, \ + .groups = NULL, \ + .min_interval = 1, \ + .max_interval = 4, \ + .busy_factor = 64, \ + .imbalance_pct = 125, \ + .cache_hot_time = (5*1000/2), \ + .cache_nice_tries = 1, \ + .per_cpu_gain = 100, \ + .flags = SD_BALANCE_NEWIDLE \ + | SD_BALANCE_EXEC \ + | SD_WAKE_AFFINE \ + | SD_WAKE_BALANCE, \ + .last_balance = jiffies, \ + .balance_interval = 1, \ + .nr_balance_failed = 0, \ +} + +/* Arch can override this macro in processor.h */ +#if defined(CONFIG_NUMA) && !defined(SD_NODE_INIT) +#define SD_NODE_INIT (struct sched_domain) { \ + .span = CPU_MASK_NONE, \ + .parent = NULL, \ + .groups = NULL, \ + .min_interval = 8, \ + .max_interval = 32, \ + .busy_factor = 32, \ + .imbalance_pct = 125, \ + .cache_hot_time = (10*1000), \ + .cache_nice_tries = 1, \ + .per_cpu_gain = 100, \ + .flags = SD_BALANCE_EXEC \ + | SD_WAKE_BALANCE, \ + .last_balance = jiffies, \ + .balance_interval = 1, \ + .nr_balance_failed = 0, \ +} +#endif +#endif /* ARCH_HAS_SCHED_TUNE */ +#endif + + #define for_each_domain(cpu, domain) \ for (domain = cpu_rq(cpu)->sd; domain; domain = domain->parent) @@ -279,25 +404,215 @@ static inline void task_rq_unlock(runque spin_unlock_irqrestore(&rq->lock, *flags); } +#ifdef CONFIG_SCHEDSTATS /* - * rq_lock - lock a given runqueue and disable interrupts. + * bump this up when changing the output format or the meaning of an existing + * format, so that tools can adapt (or abort) */ -static runqueue_t *this_rq_lock(void) +#define SCHEDSTAT_VERSION 10 + +static int show_schedstat(struct seq_file *seq, void *v) { - runqueue_t *rq; + int cpu; + enum idle_type itype; - local_irq_disable(); - rq = this_rq(); - spin_lock(&rq->lock); + seq_printf(seq, "version %d\n", SCHEDSTAT_VERSION); + seq_printf(seq, "timestamp %lu\n", jiffies); + for_each_online_cpu(cpu) { + runqueue_t *rq = cpu_rq(cpu); +#ifdef CONFIG_SMP + struct sched_domain *sd; + int dcnt = 0; +#endif - return rq; + /* runqueue-specific stats */ + seq_printf(seq, + "cpu%d %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu " + "%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", + cpu, rq->yld_both_empty, + rq->yld_act_empty, rq->yld_exp_empty, + rq->yld_cnt, rq->sched_noswitch, + rq->sched_switch, rq->sched_cnt, rq->sched_goidle, + rq->alb_cnt, rq->alb_gained, rq->alb_lost, + rq->alb_failed, + rq->ttwu_cnt, rq->ttwu_moved, rq->ttwu_attempts, + rq->wunt_cnt, rq->wunt_moved, + rq->smt_cnt, rq->sbe_cnt, rq->rq_sched_info.cpu_time, + rq->rq_sched_info.run_delay, rq->rq_sched_info.pcnt); + + for (itype = IDLE; itype < MAX_IDLE_TYPES; itype++) + seq_printf(seq, " %lu %lu", rq->pt_gained[itype], + rq->pt_lost[itype]); + seq_printf(seq, "\n"); + +#ifdef CONFIG_SMP + /* domain-specific stats */ + for_each_domain(cpu, sd) { + char mask_str[NR_CPUS]; + + cpumask_scnprintf(mask_str, NR_CPUS, sd->span); + seq_printf(seq, "domain%d %s", dcnt++, mask_str); + for (itype = IDLE; itype < MAX_IDLE_TYPES; itype++) { + seq_printf(seq, " %lu %lu %lu %lu %lu", + sd->lb_cnt[itype], + sd->lb_failed[itype], + sd->lb_imbalance[itype], + sd->lb_nobusyq[itype], + sd->lb_nobusyg[itype]); + } + seq_printf(seq, " %lu %lu %lu %lu\n", + sd->sbe_pushed, sd->sbe_attempts, + sd->ttwu_wake_affine, sd->ttwu_wake_balance); + } +#endif + } + return 0; } +static int schedstat_open(struct inode *inode, struct file *file) +{ + unsigned int size = PAGE_SIZE * (1 + num_online_cpus() / 32); + char *buf = kmalloc(size, GFP_KERNEL); + struct seq_file *m; + int res; + + if (!buf) + return -ENOMEM; + res = single_open(file, show_schedstat, NULL); + if (!res) { + m = file->private_data; + m->buf = buf; + m->size = size; + } else + kfree(buf); + return res; +} + +struct file_operations proc_schedstat_operations = { + .open = schedstat_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +# define schedstat_inc(rq, field) rq->field++; +# define schedstat_add(rq, field, amt) rq->field += amt; +#else /* !CONFIG_SCHEDSTATS */ +# define schedstat_inc(rq, field) do { } while (0); +# define schedstat_add(rq, field, amt) do { } while (0); +#endif + static inline void rq_unlock(runqueue_t *rq) { spin_unlock_irq(&rq->lock); } +#ifdef CONFIG_SCHEDSTATS +/* + * Called when a process is dequeued from the active array and given + * the cpu. We should note that with the exception of interactive + * tasks, the expired queue will become the active queue after the active + * queue is empty, without explicitly dequeuing and requeuing tasks in the + * expired queue. (Interactive tasks may be requeued directly to the + * active queue, thus delaying tasks in the expired queue from running; + * see scheduler_tick()). + * + * This function is only called from sched_info_arrive(), rather than + * dequeue_task(). Even though a task may be queued and dequeued multiple + * times as it is shuffled about, we're really interested in knowing how + * long it was from the *first* time it was queued to the time that it + * finally hit a cpu. + */ +static inline void sched_info_dequeued(task_t *t) +{ + t->sched_info.last_queued = 0; +} + +/* + * Called when a task finally hits the cpu. We can now calculate how + * long it was waiting to run. We also note when it began so that we + * can keep stats on how long its timeslice is. + */ +static inline void sched_info_arrive(task_t *t) +{ + unsigned long now = jiffies, diff = 0; + struct runqueue *rq = task_rq(t); + + if (t->sched_info.last_queued) + diff = now - t->sched_info.last_queued; + sched_info_dequeued(t); + t->sched_info.run_delay += diff; + t->sched_info.last_arrival = now; + t->sched_info.pcnt++; + + if (!rq) + return; + + rq->rq_sched_info.run_delay += diff; + rq->rq_sched_info.pcnt++; +} + +/* + * Called when a process is queued into either the active or expired + * array. The time is noted and later used to determine how long we + * had to wait for us to reach the cpu. Since the expired queue will + * become the active queue after active queue is empty, without dequeuing + * and requeuing any tasks, we are interested in queuing to either. It + * is unusual but not impossible for tasks to be dequeued and immediately + * requeued in the same or another array: this can happen in sched_yield(), + * set_user_nice(), and even load_balance() as it moves tasks from runqueue + * to runqueue. + * + * This function is only called from enqueue_task(), but also only updates + * the timestamp if it is already not set. It's assumed that + * sched_info_dequeued() will clear that stamp when appropriate. + */ +static inline void sched_info_queued(task_t *t) +{ + if (!t->sched_info.last_queued) + t->sched_info.last_queued = jiffies; +} + +/* + * Called when a process ceases being the active-running process, either + * voluntarily or involuntarily. Now we can calculate how long we ran. + */ +static inline void sched_info_depart(task_t *t) +{ + struct runqueue *rq = task_rq(t); + unsigned long diff = jiffies - t->sched_info.last_arrival; + + t->sched_info.cpu_time += diff; + + if (rq) + rq->rq_sched_info.cpu_time += diff; +} + +/* + * Called when tasks are switched involuntarily due, typically, to expiring + * their time slice. (This may also be called when switching to or from + * the idle task.) We are only called when prev != next. + */ +static inline void sched_info_switch(task_t *prev, task_t *next) +{ + struct runqueue *rq = task_rq(prev); + + /* + * prev now departs the cpu. It's not interesting to record + * stats about how efficient we were at scheduling the idle + * process, however. + */ + if (prev != rq->idle) + sched_info_depart(prev); + + if (next != rq->idle) + sched_info_arrive(next); +} +#else +#define sched_info_queued(t) do { } while (0) +#define sched_info_switch(t, next) do { } while (0) +#endif /* CONFIG_SCHEDSTATS */ + /* * Adding/removing a task to/from a priority array: */ @@ -311,7 +626,18 @@ static void dequeue_task(struct task_str static void enqueue_task(struct task_struct *p, prio_array_t *array) { - list_add_tail(&p->run_list, array->queue + p->prio); + struct list_head *entry = array->queue + p->prio; + sched_info_queued(p); + + if (!rt_task(p)) { + /* + * Cycle tasks on the same priority level. This reduces their + * timeslice fluctuations due to higher priority tasks expiring. + */ + if (!list_empty(entry)) + entry = entry->next; + } + list_add_tail(&p->run_list, entry); __set_bit(p->prio, array->bitmap); array->nr_active++; p->array = array; @@ -330,44 +656,122 @@ static inline void enqueue_task_head(str p->array = array; } +static inline unsigned long long clock_us(void) +{ + return sched_clock() >> 10; +} + /* - * effective_prio - return the priority that is based on the static - * priority but is modified by bonuses/penalties. - * - * We scale the actual sleep average [0 .... MAX_SLEEP_AVG] - * into the -5 ... 0 ... +5 bonus/penalty range. - * - * We use 25% of the full 0...39 priority range so that: - * - * 1) nice +19 interactive tasks do not preempt nice 0 CPU hogs. - * 2) nice -20 CPU hogs do not get preempted by nice 0 tasks. + * add_task_time updates a task @p after @time of doing the specified @type + * of activity. See STIME_*. This is used for priority calculation. + */ +static inline void add_task_time(task_t *p, unsigned long long time, unsigned long type) +{ + unsigned long ratio; + unsigned long long tmp; + unsigned long t; + + if (type == STIME_SLEEP) { + if (time > MAX_SLEEP_AFFECT*4) + time = MAX_SLEEP_AFFECT*4; + t = ((unsigned long)time + 3) / 4; + } else { + unsigned long div = 60 - USER_PRIO(p->static_prio); + t = (unsigned long)time * 30; + t = t / div; + t = t * 30; + t = t / div; + } + + ratio = MAX_SLEEP - t; + tmp = (unsigned long long)ratio*p->total_time + MAX_SLEEP/2; + tmp >>= MAX_SLEEP_SHIFT; + p->total_time = (unsigned long)tmp; + + tmp = (unsigned long long)ratio*p->sleep_time + MAX_SLEEP/2; + tmp >>= MAX_SLEEP_SHIFT; + p->sleep_time = (unsigned long)tmp; + + p->total_time += t; + if (type == STIME_SLEEP) + p->sleep_time += t; +} + +static unsigned long task_sleep_avg(task_t *p) +{ + return (SLEEP_FACTOR * p->sleep_time) / (p->total_time + 1); +} + +/* + * The higher a thread's priority, the bigger timeslices + * it gets during one round of execution. But even the lowest + * priority thread gets MIN_TIMESLICE worth of execution time. * - * Both properties are important to certain workloads. + * Timeslices are scaled, so if only low priority processes are running, + * they will all get long timeslices. */ -static int effective_prio(task_t *p) +static int task_timeslice(task_t *p, runqueue_t *rq) { + int idx, base, delta; + int timeslice; + + if (rt_task(p)) + return RT_TIMESLICE; + + idx = min(p->prio, rq->expired->min_prio); + delta = p->prio - idx; + base = BASE_TIMESLICE * (MAX_USER_PRIO + 1) / (delta + 2); + + base = base * 40 / (70 - USER_PRIO(idx)); + base = base * 40 / (70 - USER_PRIO(idx)); + + timeslice = base * 1000 / HZ; + timeslice >>= 5; + if (timeslice < MIN_TIMESLICE) + timeslice = MIN_TIMESLICE; + + return timeslice; +} + +/* + * task_priority: calculates a task's priority based on previous running + * history (see add_task_time). The priority is just a simple linear function + * based on sleep_avg and static_prio. + */ +static int task_priority(task_t *p) +{ + unsigned long sleep_avg; int bonus, prio; if (rt_task(p)) return p->prio; - bonus = CURRENT_BONUS(p) - MAX_BONUS / 2; + sleep_avg = task_sleep_avg(p); + + prio = USER_PRIO(p->static_prio) + 10; + bonus = (((MAX_USER_PRIO + 1) / 3) * sleep_avg + (SLEEP_FACTOR / 2)) + / SLEEP_FACTOR; + prio = MAX_RT_PRIO + prio - bonus; - prio = p->static_prio - bonus; if (prio < MAX_RT_PRIO) - prio = MAX_RT_PRIO; + return MAX_RT_PRIO; if (prio > MAX_PRIO-1) - prio = MAX_PRIO-1; + return MAX_PRIO-1; + return prio; } /* * __activate_task - move a task to the runqueue. */ -static inline void __activate_task(task_t *p, runqueue_t *rq) +static inline void __activate_task(task_t *p, runqueue_t *rq, prio_array_t *array) { - enqueue_task(p, rq->active); + enqueue_task(p, array); rq->nr_running++; + if (!rt_task(p)) { + if (p->prio < array->min_prio) + array->min_prio = p->prio; + } } /* @@ -379,80 +783,6 @@ static inline void __activate_idle_task( rq->nr_running++; } -static void recalc_task_prio(task_t *p, unsigned long long now) -{ - unsigned long long __sleep_time = now - p->timestamp; - unsigned long sleep_time; - - if (__sleep_time > NS_MAX_SLEEP_AVG) - sleep_time = NS_MAX_SLEEP_AVG; - else - sleep_time = (unsigned long)__sleep_time; - - if (likely(sleep_time > 0)) { - /* - * User tasks that sleep a long time are categorised as - * idle and will get just interactive status to stay active & - * prevent them suddenly becoming cpu hogs and starving - * other processes. - */ - if (p->mm && p->activated != -1 && - sleep_time > INTERACTIVE_SLEEP(p)) { - p->sleep_avg = JIFFIES_TO_NS(MAX_SLEEP_AVG - - AVG_TIMESLICE); - if (!HIGH_CREDIT(p)) - p->interactive_credit++; - } else { - /* - * The lower the sleep avg a task has the more - * rapidly it will rise with sleep time. - */ - sleep_time *= (MAX_BONUS - CURRENT_BONUS(p)) ? : 1; - - /* - * Tasks with low interactive_credit are limited to - * one timeslice worth of sleep avg bonus. - */ - if (LOW_CREDIT(p) && - sleep_time > JIFFIES_TO_NS(task_timeslice(p))) - sleep_time = JIFFIES_TO_NS(task_timeslice(p)); - - /* - * Non high_credit tasks waking from uninterruptible - * sleep are limited in their sleep_avg rise as they - * are likely to be cpu hogs waiting on I/O - */ - if (p->activated == -1 && !HIGH_CREDIT(p) && p->mm) { - if (p->sleep_avg >= INTERACTIVE_SLEEP(p)) - sleep_time = 0; - else if (p->sleep_avg + sleep_time >= - INTERACTIVE_SLEEP(p)) { - p->sleep_avg = INTERACTIVE_SLEEP(p); - sleep_time = 0; - } - } - - /* - * This code gives a bonus to interactive tasks. - * - * The boost works by updating the 'average sleep time' - * value here, based on ->timestamp. The more time a - * task spends sleeping, the higher the average gets - - * and the higher the priority boost gets as well. - */ - p->sleep_avg += sleep_time; - - if (p->sleep_avg > NS_MAX_SLEEP_AVG) { - p->sleep_avg = NS_MAX_SLEEP_AVG; - if (!HIGH_CREDIT(p)) - p->interactive_credit++; - } - } - } - - p->prio = effective_prio(p); -} - /* * activate_task - move a task to the runqueue and do priority recalculation * @@ -461,9 +791,10 @@ static void recalc_task_prio(task_t *p, */ static void activate_task(task_t *p, runqueue_t *rq, int local) { - unsigned long long now; + unsigned long long now, sleep; + prio_array_t *array; - now = sched_clock(); + now = clock_us(); #ifdef CONFIG_SMP if (!local) { /* Compensate for drifting sched_clock */ @@ -472,44 +803,34 @@ static void activate_task(task_t *p, run + rq->timestamp_last_tick; } #endif - - recalc_task_prio(p, now); - /* - * This checks to make sure it's not an uninterruptible task - * that is now waking up. + * If we have slept through an active/expired array switch, restart + * our timeslice too. */ - if (!p->activated) { - /* - * Tasks which were woken up by interrupts (ie. hw events) - * are most likely of interactive nature. So we give them - * the credit of extending their sleep time to the period - * of time they spend on the runqueue, waiting for execution - * on a CPU, first time around: - */ - if (in_interrupt()) - p->activated = 2; - else { - /* - * Normal first-time wakeups get a credit too for - * on-runqueue time, but it will be weighted down: - */ - p->activated = 1; - } - } + + sleep = now - p->timestamp; p->timestamp = now; + add_task_time(p, sleep, STIME_SLEEP); + p->prio = task_priority(p); + + array = rq->active; + if (unlikely(p->used_slice == -1)) { + /* This only applys to newly woken children */ + array = rq->expired; + p->used_slice = 0; + } else if (rq->array_sequence != p->array_sequence) + p->used_slice = 0; - __activate_task(p, rq); + __activate_task(p, rq, array); } /* * deactivate_task - remove a task from the runqueue. */ -static void deactivate_task(struct task_struct *p, runqueue_t *rq) +static inline void deactivate_task(struct task_struct *p, runqueue_t *rq) { + p->array_sequence = rq->array_sequence; rq->nr_running--; - if (p->state == TASK_UNINTERRUPTIBLE) - rq->nr_uninterruptible++; dequeue_task(p, p->array); p->array = NULL; } @@ -526,7 +847,8 @@ static void resched_task(task_t *p) { int need_resched, nrpolling; - preempt_disable(); + BUG_ON(!spin_is_locked(&task_rq(p)->lock)); + /* minimise the chance of sending an interrupt to poll_idle() */ nrpolling = test_tsk_thread_flag(p,TIF_POLLING_NRFLAG); need_resched = test_and_set_tsk_thread_flag(p,TIF_NEED_RESCHED); @@ -534,7 +856,6 @@ static void resched_task(task_t *p) if (!need_resched && !nrpolling && (task_cpu(p) != smp_processor_id())) smp_send_reschedule(task_cpu(p)); - preempt_enable(); } #else static inline void resched_task(task_t *p) @@ -740,6 +1061,7 @@ static int try_to_wake_up(task_t * p, un #endif rq = task_rq_lock(p, &flags); + schedstat_inc(rq, ttwu_cnt); old_state = p->state; if (!(old_state & state)) goto out; @@ -787,23 +1109,35 @@ static int try_to_wake_up(task_t * p, un */ imbalance = sd->imbalance_pct + (sd->imbalance_pct - 100) / 2; - if ( ((sd->flags & SD_WAKE_AFFINE) && - !task_hot(p, rq->timestamp_last_tick, sd)) - || ((sd->flags & SD_WAKE_BALANCE) && - imbalance*this_load <= 100*load) ) { + if ((sd->flags & SD_WAKE_AFFINE) && + !task_hot(p, rq->timestamp_last_tick, sd)) { + /* + * This domain has SD_WAKE_AFFINE and p is cache cold + * in this domain. + */ + if (cpu_isset(cpu, sd->span)) { + schedstat_inc(sd, ttwu_wake_affine); + goto out_set_cpu; + } + } else if ((sd->flags & SD_WAKE_BALANCE) && + imbalance*this_load <= 100*load) { /* - * Now sd has SD_WAKE_AFFINE and p is cache cold in sd - * or sd has SD_WAKE_BALANCE and there is an imbalance + * This domain has SD_WAKE_BALANCE and there is + * an imbalance. */ - if (cpu_isset(cpu, sd->span)) + if (cpu_isset(cpu, sd->span)) { + schedstat_inc(sd, ttwu_wake_balance); goto out_set_cpu; + } } } new_cpu = cpu; /* Could not wake to this_cpu. Wake to cpu instead */ out_set_cpu: + schedstat_inc(rq, ttwu_attempts); new_cpu = wake_idle(new_cpu, p); if (new_cpu != cpu && cpu_isset(new_cpu, p->cpus_allowed)) { + schedstat_inc(rq, ttwu_moved); set_task_cpu(p, new_cpu); task_rq_unlock(rq, &flags); /* might preempt at this point */ @@ -820,28 +1154,14 @@ out_set_cpu: out_activate: #endif /* CONFIG_SMP */ - if (old_state == TASK_UNINTERRUPTIBLE) { + if (old_state == TASK_UNINTERRUPTIBLE) rq->nr_uninterruptible--; - /* - * Tasks on involuntary sleep don't earn - * sleep_avg beyond just interactive state. - */ - p->activated = -1; - } - - /* - * Sync wakeups (i.e. those types of wakeups where the waker - * has indicated that it will leave the CPU in short order) - * don't trigger a preemption, if the woken up task will run on - * this cpu. (in this case the 'I will reschedule' promise of - * the waker guarantees that the freshly woken up task is going - * to be considered on this CPU.) - */ activate_task(p, rq, cpu == this_cpu); if (!sync || cpu != this_cpu) { if (TASK_PREEMPTS_CURR(p, rq)) resched_task(rq->curr); } + success = 1; out_running: @@ -854,7 +1174,7 @@ out: int fastcall wake_up_process(task_t * p) { - return try_to_wake_up(p, TASK_STOPPED | + return try_to_wake_up(p, TASK_STOPPED | TASK_TRACED | TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE, 0); } @@ -865,12 +1185,20 @@ int fastcall wake_up_state(task_t *p, un return try_to_wake_up(p, state, 0); } +#ifdef CONFIG_SMP +static int find_idlest_cpu(struct task_struct *p, int this_cpu, + struct sched_domain *sd); +#endif + /* * Perform scheduler related setup for a newly forked process p. * p is forked by current. */ void fastcall sched_fork(task_t *p) { + unsigned long sleep_avg; + runqueue_t *rq; + /* * We mark the process as running here, but have not actually * inserted it onto the runqueue yet. This guarantees that @@ -881,6 +1209,9 @@ void fastcall sched_fork(task_t *p) INIT_LIST_HEAD(&p->run_list); p->array = NULL; spin_lock_init(&p->switch_lock); +#ifdef CONFIG_SCHEDSTATS + memset(&p->sched_info, 0, sizeof(p->sched_info)); +#endif #ifdef CONFIG_PREEMPT /* * During context-switch we hold precisely one spinlock, which @@ -890,107 +1221,123 @@ void fastcall sched_fork(task_t *p) */ p->thread_info->preempt_count = 1; #endif - /* - * Share the timeslice between parent and child, thus the - * total amount of pending timeslices in the system doesn't change, - * resulting in more scheduling fairness. - */ + + preempt_disable(); + rq = this_rq(); + + /* XXX */ + if (unlikely(p->comm[0] == 'X' && p->comm[1] == 'F')) { + static int warned = 0; + if (!warned) { + printk(KERN_INFO "Renicing %s for you\n", p->comm); + warned = 1; + } + p->static_prio = NICE_TO_PRIO(-10); + } + + /* Get MIN_HISTORY of history with the same sleep_avg as parent. */ + sleep_avg = task_sleep_avg(current); + p->total_time = MIN_HISTORY; + p->sleep_time = p->total_time * sleep_avg / SLEEP_FACTOR; + + /* Parent loses 1/4 of sleep time for forking */ + current->sleep_time = 3*current->sleep_time/4; + + p->used_slice = 0; local_irq_disable(); - p->time_slice = (current->time_slice + 1) >> 1; - /* - * The remainder of the first timeslice might be recovered by - * the parent if the child exits early enough. - */ - p->first_time_slice = 1; - current->time_slice >>= 1; - p->timestamp = sched_clock(); - if (!current->time_slice) { - /* - * This case is rare, it happens when the parent has only - * a single jiffy left from its timeslice. Taking the - * runqueue lock is not a problem. - */ - current->time_slice = 1; - preempt_disable(); - scheduler_tick(0, 0); - local_irq_enable(); - preempt_enable(); - } else - local_irq_enable(); + if (unlikely(current->used_slice == -1 || current == rq->idle)) + p->used_slice = -1; + else { + int ts = task_timeslice(current, rq); + current->used_slice += (ts + 3) / 4; + if (current->used_slice >= ts) { + current->used_slice = -1; + set_need_resched(); + } + } + local_irq_enable(); + preempt_enable(); } /* - * wake_up_forked_process - wake up a freshly forked process. + * wake_up_new_task - wake up a newly created task for the first time. * * This function will do some initial scheduler statistics housekeeping - * that must be done for every newly created process. + * that must be done for every newly created context, then puts the task + * on the runqueue and wakes it. */ -void fastcall wake_up_forked_process(task_t * p) +void fastcall wake_up_new_task(task_t * p, unsigned long clone_flags) { unsigned long flags; - runqueue_t *rq = task_rq_lock(current, &flags); + int this_cpu, cpu; + runqueue_t *rq; + prio_array_t *array; BUG_ON(p->state != TASK_RUNNING); - /* - * We decrease the sleep average of forking parents - * and children as well, to keep max-interactive tasks - * from forking tasks that are max-interactive. - */ - current->sleep_avg = JIFFIES_TO_NS(CURRENT_BONUS(current) * - PARENT_PENALTY / 100 * MAX_SLEEP_AVG / MAX_BONUS); + p->prio = task_priority(p); + p->timestamp = clock_us(); - p->sleep_avg = JIFFIES_TO_NS(CURRENT_BONUS(p) * - CHILD_PENALTY / 100 * MAX_SLEEP_AVG / MAX_BONUS); + rq = task_rq_lock(p, &flags); + this_cpu = smp_processor_id(); + cpu = task_cpu(p); - p->interactive_credit = 0; + schedstat_inc(rq, wunt_cnt); - p->prio = effective_prio(p); - set_task_cpu(p, smp_processor_id()); + array = rq->active; + if (unlikely(p->used_slice == -1)) { + p->used_slice = 0; + array = rq->expired; + } else { + int total = task_timeslice(p, rq); + int ts = max((total + 3) / 4, MIN_TIMESLICE); + ts = min(ts, (int)FORKED_TS_MAX); + p->used_slice = total - ts; + } - if (unlikely(!current->array)) - __activate_task(p, rq); - else { - p->prio = current->prio; - list_add_tail(&p->run_list, ¤t->run_list); - p->array = current->array; - p->array->nr_active++; - rq->nr_running++; + if (likely(cpu == this_cpu)) { + if (!(clone_flags & CLONE_VM) && likely(array == rq->active)) { + /* + * The VM isn't cloned, so we're in a good position to + * do child-runs-first in anticipation of an exec. This + * usually avoids a lot of COW overhead. + */ + if (p->prio >= current->prio) { + p->prio = current->prio; + list_add_tail(&p->run_list, ¤t->run_list); + p->array = current->array; + p->array->nr_active++; + rq->nr_running++; + } else + __activate_task(p, rq, array); + + set_need_resched(); + } else { + /* Run child last */ + __activate_task(p, rq, array); + } +#ifdef CONFIG_SMP + } else { + runqueue_t *this_rq = this_rq(); + + /* + * Not the local CPU - must adjust timestamp. This should + * get optimised away in the !CONFIG_SMP case. + */ + p->timestamp = (p->timestamp - this_rq->timestamp_last_tick) + + rq->timestamp_last_tick; + __activate_task(p, rq, array); + if (TASK_PREEMPTS_CURR(p, rq)) + resched_task(rq->curr); + + schedstat_inc(rq, wunt_moved); +#endif } task_rq_unlock(rq, &flags); } -/* - * Potentially available exiting-child timeslices are - * retrieved here - this way the parent does not get - * penalized for creating too many threads. - * - * (this cannot be used to 'generate' timeslices - * artificially, because any timeslice recovered here - * was given away by the parent in the first place.) - */ void fastcall sched_exit(task_t * p) { - unsigned long flags; - runqueue_t *rq; - - local_irq_save(flags); - if (p->first_time_slice) { - p->parent->time_slice += p->time_slice; - if (unlikely(p->parent->time_slice > MAX_TIMESLICE)) - p->parent->time_slice = MAX_TIMESLICE; - } - local_irq_restore(flags); - /* - * If the child was a (relative-) CPU hog then decrease - * the sleep_avg of the parent as well. - */ - rq = task_rq_lock(p->parent, &flags); - if (p->sleep_avg < p->parent->sleep_avg) - p->parent->sleep_avg = p->parent->sleep_avg / - (EXIT_WEIGHT + 1) * EXIT_WEIGHT + p->sleep_avg / - (EXIT_WEIGHT + 1); - task_rq_unlock(rq, &flags); } /** @@ -1085,7 +1432,7 @@ unsigned long nr_running(void) { unsigned long i, sum = 0; - for_each_cpu(i) + for_each_online_cpu(i) sum += cpu_rq(i)->nr_running; return sum; @@ -1121,6 +1468,8 @@ unsigned long nr_iowait(void) return sum; } +#ifdef CONFIG_SMP + /* * double_rq_lock - safely lock two runqueues * @@ -1155,14 +1504,20 @@ static void double_rq_unlock(runqueue_t spin_unlock(&rq2->lock); } -enum idle_type +/* + * double_lock_balance - lock the busiest runqueue, this_rq is locked already. + */ +static void double_lock_balance(runqueue_t *this_rq, runqueue_t *busiest) { - IDLE, - NOT_IDLE, - NEWLY_IDLE, -}; - -#ifdef CONFIG_SMP + if (unlikely(!spin_trylock(&busiest->lock))) { + if (busiest < this_rq) { + spin_unlock(&this_rq->lock); + spin_lock(&busiest->lock); + spin_lock(&this_rq->lock); + } else + spin_lock(&busiest->lock); + } +} /* * find_idlest_cpu - find the least busy runqueue. @@ -1211,89 +1566,6 @@ static int find_idlest_cpu(struct task_s } /* - * wake_up_forked_thread - wake up a freshly forked thread. - * - * This function will do some initial scheduler statistics housekeeping - * that must be done for every newly created context, and it also does - * runqueue balancing. - */ -void fastcall wake_up_forked_thread(task_t * p) -{ - unsigned long flags; - int this_cpu = get_cpu(), cpu; - struct sched_domain *tmp, *sd = NULL; - runqueue_t *this_rq = cpu_rq(this_cpu), *rq; - - /* - * Find the largest domain that this CPU is part of that - * is willing to balance on clone: - */ - for_each_domain(this_cpu, tmp) - if (tmp->flags & SD_BALANCE_CLONE) - sd = tmp; - if (sd) - cpu = find_idlest_cpu(p, this_cpu, sd); - else - cpu = this_cpu; - - local_irq_save(flags); -lock_again: - rq = cpu_rq(cpu); - double_rq_lock(this_rq, rq); - - BUG_ON(p->state != TASK_RUNNING); - - /* - * We did find_idlest_cpu() unlocked, so in theory - * the mask could have changed - just dont migrate - * in this case: - */ - if (unlikely(!cpu_isset(cpu, p->cpus_allowed))) { - cpu = this_cpu; - double_rq_unlock(this_rq, rq); - goto lock_again; - } - /* - * We decrease the sleep average of forking parents - * and children as well, to keep max-interactive tasks - * from forking tasks that are max-interactive. - */ - current->sleep_avg = JIFFIES_TO_NS(CURRENT_BONUS(current) * - PARENT_PENALTY / 100 * MAX_SLEEP_AVG / MAX_BONUS); - - p->sleep_avg = JIFFIES_TO_NS(CURRENT_BONUS(p) * - CHILD_PENALTY / 100 * MAX_SLEEP_AVG / MAX_BONUS); - - p->interactive_credit = 0; - - p->prio = effective_prio(p); - set_task_cpu(p, cpu); - - if (cpu == this_cpu) { - if (unlikely(!current->array)) - __activate_task(p, rq); - else { - p->prio = current->prio; - list_add_tail(&p->run_list, ¤t->run_list); - p->array = current->array; - p->array->nr_active++; - rq->nr_running++; - } - } else { - /* Not the local CPU - must adjust timestamp */ - p->timestamp = (p->timestamp - this_rq->timestamp_last_tick) - + rq->timestamp_last_tick; - __activate_task(p, rq); - if (TASK_PREEMPTS_CURR(p, rq)) - resched_task(rq->curr); - } - - double_rq_unlock(this_rq, rq); - local_irq_restore(flags); - put_cpu(); -} - -/* * If dest_cpu is allowed for this process, migrate the task to it. * This is accomplished by forcing the cpu_allowed mask to only * allow dest_cpu, which will force the cpu onto dest_cpu. Then @@ -1310,6 +1582,7 @@ static void sched_migrate_task(task_t *p || unlikely(cpu_is_offline(dest_cpu))) goto out; + schedstat_inc(rq, smt_cnt); /* force the process onto the specified CPU */ if (migrate_task(p, dest_cpu, &req)) { /* Need to wait for migration thread (might exit: take ref). */ @@ -1326,17 +1599,18 @@ out: } /* - * sched_balance_exec(): find the highest-level, exec-balance-capable + * sched_exec(): find the highest-level, exec-balance-capable * domain and try to migrate the task to the least loaded CPU. * * execve() is a valuable balancing opportunity, because at this point * the task has the smallest effective memory and cache footprint. */ -void sched_balance_exec(void) +void sched_exec(void) { struct sched_domain *tmp, *sd = NULL; int new_cpu, this_cpu = get_cpu(); + schedstat_inc(this_rq(), sbe_cnt); /* Prefer the current CPU if there's only this task running */ if (this_rq()->nr_running <= 1) goto out; @@ -1345,9 +1619,11 @@ void sched_balance_exec(void) if (tmp->flags & SD_BALANCE_EXEC) sd = tmp; + schedstat_inc(sd, sbe_attempts); if (sd) { new_cpu = find_idlest_cpu(current, this_cpu, sd); if (new_cpu != this_cpu) { + schedstat_inc(sd, sbe_pushed); put_cpu(); sched_migrate_task(current, new_cpu); return; @@ -1358,21 +1634,6 @@ out: } /* - * double_lock_balance - lock the busiest runqueue, this_rq is locked already. - */ -static void double_lock_balance(runqueue_t *this_rq, runqueue_t *busiest) -{ - if (unlikely(!spin_trylock(&busiest->lock))) { - if (busiest < this_rq) { - spin_unlock(&this_rq->lock); - spin_lock(&busiest->lock); - spin_lock(&this_rq->lock); - } else - spin_lock(&busiest->lock); - } -} - -/* * pull_task - move a task from a remote runqueue to the local runqueue. * Both runqueues must be locked. */ @@ -1385,6 +1646,10 @@ void pull_task(runqueue_t *src_rq, prio_ set_task_cpu(p, this_cpu); this_rq->nr_running++; enqueue_task(p, this_array); + if (!rt_task(p)) { + if (p->prio < this_array->min_prio) + this_array->min_prio = p->prio; + } p->timestamp = (p->timestamp - src_rq->timestamp_last_tick) + this_rq->timestamp_last_tick; /* @@ -1486,6 +1751,15 @@ skip_queue: idx++; goto skip_bitmap; } + + /* + * Right now, this is the only place pull_task() is called, + * so we can safely collect pull_task() stats here rather than + * inside pull_task(). + */ + schedstat_inc(this_rq, pt_gained[idle]); + schedstat_inc(busiest, pt_lost[idle]); + pull_task(busiest, array, tmp, this_rq, dst_array, this_cpu); pulled++; @@ -1679,15 +1953,20 @@ static int load_balance(int this_cpu, ru unsigned long imbalance; int nr_moved; - spin_lock(&this_rq->lock); + schedstat_inc(sd, lb_cnt[idle]); group = find_busiest_group(sd, this_cpu, &imbalance, idle); - if (!group) + if (!group) { + schedstat_inc(sd, lb_nobusyg[idle]); goto out_balanced; + } busiest = find_busiest_queue(group); - if (!busiest) + if (!busiest) { + schedstat_inc(sd, lb_nobusyq[idle]); goto out_balanced; + } + /* * This should be "impossible", but since load * balancing is inherently racy and statistical, @@ -1698,6 +1977,8 @@ static int load_balance(int this_cpu, ru goto out_balanced; } + schedstat_add(sd, lb_imbalance[idle], imbalance); + nr_moved = 0; if (busiest->nr_running > 1) { /* @@ -1706,14 +1987,14 @@ static int load_balance(int this_cpu, ru * still unbalanced. nr_moved simply stays zero, so it is * correctly treated as an imbalance. */ - double_lock_balance(this_rq, busiest); + double_rq_lock(this_rq, busiest); nr_moved = move_tasks(this_rq, this_cpu, busiest, imbalance, sd, idle); - spin_unlock(&busiest->lock); + double_rq_unlock(this_rq, busiest); } - spin_unlock(&this_rq->lock); if (!nr_moved) { + schedstat_inc(sd, lb_failed[idle]); sd->nr_balance_failed++; if (unlikely(sd->nr_balance_failed > sd->cache_nice_tries+2)) { @@ -1744,8 +2025,6 @@ static int load_balance(int this_cpu, ru return nr_moved; out_balanced: - spin_unlock(&this_rq->lock); - /* tune up the balancing interval */ if (sd->balance_interval < sd->max_interval) sd->balance_interval *= 2; @@ -1768,19 +2047,27 @@ static int load_balance_newidle(int this unsigned long imbalance; int nr_moved = 0; + schedstat_inc(sd, lb_cnt[NEWLY_IDLE]); group = find_busiest_group(sd, this_cpu, &imbalance, NEWLY_IDLE); - if (!group) + if (!group) { + schedstat_inc(sd, lb_nobusyg[NEWLY_IDLE]); goto out; + } busiest = find_busiest_queue(group); - if (!busiest || busiest == this_rq) + if (!busiest || busiest == this_rq) { + schedstat_inc(sd, lb_nobusyq[NEWLY_IDLE]); goto out; + } /* Attempt to move tasks */ double_lock_balance(this_rq, busiest); + schedstat_add(sd, lb_imbalance[NEWLY_IDLE], imbalance); nr_moved = move_tasks(this_rq, this_cpu, busiest, imbalance, sd, NEWLY_IDLE); + if (!nr_moved) + schedstat_inc(sd, lb_failed[NEWLY_IDLE]); spin_unlock(&busiest->lock); @@ -1820,40 +2107,39 @@ static void active_load_balance(runqueue struct sched_group *group, *busy_group; int i; + schedstat_inc(busiest, alb_cnt); if (busiest->nr_running <= 1) return; for_each_domain(busiest_cpu, sd) if (cpu_isset(busiest->push_cpu, sd->span)) break; - if (!sd) { - WARN_ON(1); + if (!sd) return; - } - group = sd->groups; + group = sd->groups; while (!cpu_isset(busiest_cpu, group->cpumask)) - group = group->next; - busy_group = group; + group = group->next; + busy_group = group; - group = sd->groups; - do { + group = sd->groups; + do { cpumask_t tmp; runqueue_t *rq; int push_cpu = 0; - if (group == busy_group) - goto next_group; + if (group == busy_group) + goto next_group; cpus_and(tmp, group->cpumask, cpu_online_map); if (!cpus_weight(tmp)) goto next_group; - for_each_cpu_mask(i, tmp) { + for_each_cpu_mask(i, tmp) { if (!idle_cpu(i)) goto next_group; - push_cpu = i; - } + push_cpu = i; + } rq = cpu_rq(push_cpu); @@ -1866,7 +2152,12 @@ static void active_load_balance(runqueue if (unlikely(busiest == rq)) goto next_group; double_lock_balance(busiest, rq); - move_tasks(rq, push_cpu, busiest, 1, sd, IDLE); + if (move_tasks(rq, push_cpu, busiest, 1, sd, IDLE)) { + schedstat_inc(busiest, alb_lost); + schedstat_inc(rq, alb_gained); + } else { + schedstat_inc(busiest, alb_failed); + } spin_unlock(&rq->lock); next_group: group = group->next; @@ -1938,17 +2229,20 @@ static inline void idle_balance(int cpu, static inline int wake_priority_sleeper(runqueue_t *rq) { + int ret = 0; #ifdef CONFIG_SCHED_SMT + spin_lock(&rq->lock); /* * If an SMT sibling task has been put to sleep for priority * reasons reschedule the idle task to see if it can now run. */ if (rq->nr_running) { resched_task(rq->idle); - return 1; + ret = 1; } + spin_unlock(&rq->lock); #endif - return 0; + return ret; } DEFINE_PER_CPU(struct kernel_stat, kstat); @@ -1956,22 +2250,6 @@ DEFINE_PER_CPU(struct kernel_stat, kstat EXPORT_PER_CPU_SYMBOL(kstat); /* - * We place interactive tasks back into the active array, if possible. - * - * To guarantee that this does not starve expired tasks we ignore the - * interactivity of a task if the first expired task had to wait more - * than a 'reasonable' amount of time. This deadline timeout is - * load-dependent, as the frequency of array switched decreases with - * increasing number of running tasks. We also ignore the interactivity - * if a better static_prio task has expired: - */ -#define EXPIRED_STARVING(rq) \ - ((STARVATION_LIMIT && ((rq)->expired_timestamp && \ - (jiffies - (rq)->expired_timestamp >= \ - STARVATION_LIMIT * ((rq)->nr_running) + 1))) || \ - ((rq)->curr->static_prio > (rq)->best_expired_prio)) - -/* * This function gets called by the timer code, with HZ frequency. * We call it with interrupts disabled. * @@ -1980,12 +2258,16 @@ EXPORT_PER_CPU_SYMBOL(kstat); */ void scheduler_tick(int user_ticks, int sys_ticks) { + enum idle_type cpu_status; int cpu = smp_processor_id(); struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat; runqueue_t *rq = this_rq(); task_t *p = current; + int ts; - rq->timestamp_last_tick = sched_clock(); +#ifdef CONFIG_SMP + rq->timestamp_last_tick = clock_us(); +#endif if (rcu_pending(cpu)) rcu_check_callbacks(cpu, user_ticks); @@ -1999,6 +2281,7 @@ void scheduler_tick(int user_ticks, int sys_ticks = 0; } + cpu_status = NOT_IDLE; if (p == rq->idle) { if (atomic_read(&rq->nr_iowait) > 0) cpustat->iowait += sys_ticks; @@ -2006,8 +2289,8 @@ void scheduler_tick(int user_ticks, int cpustat->idle += sys_ticks; if (wake_priority_sleeper(rq)) goto out; - rebalance_tick(cpu, rq, IDLE); - return; + cpu_status = IDLE; + goto out; } if (TASK_NICE(p) > 0) cpustat->nice += user_ticks; @@ -2016,101 +2299,53 @@ void scheduler_tick(int user_ticks, int cpustat->system += sys_ticks; /* Task might have expired already, but not scheduled off yet */ - if (p->array != rq->active) { - set_tsk_need_resched(p); + if (unlikely(p->used_slice == -1)) goto out; - } - spin_lock(&rq->lock); - /* - * The task was running during this tick - update the - * time slice counter. Note: we do not update a thread's - * priority until it either goes to sleep or uses up its - * timeslice. This makes it possible for interactive tasks - * to use up their timeslices at their highest priority levels. - */ - if (unlikely(rt_task(p))) { - /* - * RR tasks need a special form of timeslice management. - * FIFO tasks have no timeslices. - */ - if ((p->policy == SCHED_RR) && !--p->time_slice) { - p->time_slice = task_timeslice(p); - p->first_time_slice = 0; - set_tsk_need_resched(p); - - /* put it at the end of the queue: */ - dequeue_task(p, rq->active); - enqueue_task(p, rq->active); - } - goto out_unlock; - } - if (!--p->time_slice) { - dequeue_task(p, rq->active); + + if (unlikely(p->policy == SCHED_FIFO)) + goto out; + + /* p was running during this tick. Update its time slice counter. */ + p->used_slice++; + ts = task_timeslice(p, rq); + if (unlikely(p->used_slice >= ts)) { + p->used_slice = -1; set_tsk_need_resched(p); - p->prio = effective_prio(p); - p->time_slice = task_timeslice(p); - p->first_time_slice = 0; - - if (!rq->expired_timestamp) - rq->expired_timestamp = jiffies; - if (!TASK_INTERACTIVE(p) || EXPIRED_STARVING(rq)) { - enqueue_task(p, rq->expired); - if (p->static_prio < rq->best_expired_prio) - rq->best_expired_prio = p->static_prio; - } else - enqueue_task(p, rq->active); - } else { - /* - * Prevent a too long timeslice allowing a task to monopolize - * the CPU. We do this by splitting up the timeslice into - * smaller pieces. - * - * Note: this does not mean the task's timeslices expire or - * get lost in any way, they just might be preempted by - * another task of equal priority. (one with higher - * priority would have preempted this task already.) We - * requeue this task to the end of the list on this priority - * level, which is in essence a round-robin of tasks with - * equal priority. - * - * This only applies to tasks in the interactive - * delta range with at least TIMESLICE_GRANULARITY to requeue. - */ - if (TASK_INTERACTIVE(p) && !((task_timeslice(p) - - p->time_slice) % TIMESLICE_GRANULARITY(p)) && - (p->time_slice >= TIMESLICE_GRANULARITY(p)) && - (p->array == rq->active)) { - - dequeue_task(p, rq->active); - set_tsk_need_resched(p); - p->prio = effective_prio(p); - enqueue_task(p, rq->active); - } } -out_unlock: - spin_unlock(&rq->lock); + out: - rebalance_tick(cpu, rq, NOT_IDLE); + rebalance_tick(cpu, rq, cpu_status); } #ifdef CONFIG_SCHED_SMT -static inline void wake_sleeping_dependent(int cpu, runqueue_t *rq) +static inline void wake_sleeping_dependent(int this_cpu, runqueue_t *this_rq) { - int i; - struct sched_domain *sd = rq->sd; + struct sched_domain *sd = this_rq->sd; cpumask_t sibling_map; + int i; if (!(sd->flags & SD_SHARE_CPUPOWER)) return; + /* + * Unlock the current runqueue because we have to lock in + * CPU order to avoid deadlocks. Caller knows that we might + * unlock. We keep IRQs disabled. + */ + spin_unlock(&this_rq->lock); + cpus_and(sibling_map, sd->span, cpu_online_map); - for_each_cpu_mask(i, sibling_map) { - runqueue_t *smt_rq; - if (i == cpu) - continue; + for_each_cpu_mask(i, sibling_map) + spin_lock(&cpu_rq(i)->lock); + /* + * We clear this CPU from the mask. This both simplifies the + * inner loop and keps this_rq locked when we exit: + */ + cpu_clear(this_cpu, sibling_map); - smt_rq = cpu_rq(i); + for_each_cpu_mask(i, sibling_map) { + runqueue_t *smt_rq = cpu_rq(i); /* * If an SMT sibling task is sleeping due to priority @@ -2119,27 +2354,53 @@ static inline void wake_sleeping_depende if (smt_rq->curr == smt_rq->idle && smt_rq->nr_running) resched_task(smt_rq->idle); } + + for_each_cpu_mask(i, sibling_map) + spin_unlock(&cpu_rq(i)->lock); + /* + * We exit with this_cpu's rq still held and IRQs + * still disabled: + */ } -static inline int dependent_sleeper(int cpu, runqueue_t *rq, task_t *p) +static inline int dependent_sleeper(int this_cpu, runqueue_t *this_rq) { - struct sched_domain *sd = rq->sd; + struct sched_domain *sd = this_rq->sd; cpumask_t sibling_map; + prio_array_t *array; int ret = 0, i; + task_t *p; if (!(sd->flags & SD_SHARE_CPUPOWER)) return 0; + /* + * The same locking rules and details apply as for + * wake_sleeping_dependent(): + */ + spin_unlock(&this_rq->lock); cpus_and(sibling_map, sd->span, cpu_online_map); - for_each_cpu_mask(i, sibling_map) { - runqueue_t *smt_rq; - task_t *smt_curr; + for_each_cpu_mask(i, sibling_map) + spin_lock(&cpu_rq(i)->lock); + cpu_clear(this_cpu, sibling_map); - if (i == cpu) - continue; + /* + * Establish next task to be run - it might have gone away because + * we released the runqueue lock above: + */ + if (!this_rq->nr_running) + goto out_unlock; + array = this_rq->active; + if (!array->nr_active) + array = this_rq->expired; + BUG_ON(!array->nr_active); - smt_rq = cpu_rq(i); - smt_curr = smt_rq->curr; + p = list_entry(array->queue[sched_find_first_bit(array->bitmap)].next, + task_t, run_list); + + for_each_cpu_mask(i, sibling_map) { + runqueue_t *smt_rq = cpu_rq(i); + task_t *smt_curr = smt_rq->curr; /* * If a user task with lower static priority than the @@ -2149,8 +2410,7 @@ static inline int dependent_sleeper(int * task from using an unfair proportion of the * physical cpu's resources. -ck */ - if (((smt_curr->time_slice * (100 - sd->per_cpu_gain) / 100) > - task_timeslice(p) || rt_task(smt_curr)) && + if ((smt_curr->static_prio + 5 < p->static_prio) && p->mm && smt_curr->mm && !rt_task(p)) ret = 1; @@ -2159,20 +2419,22 @@ static inline int dependent_sleeper(int * or wake it up if it has been put to sleep for priority * reasons. */ - if ((((p->time_slice * (100 - sd->per_cpu_gain) / 100) > - task_timeslice(smt_curr) || rt_task(p)) && + if ((p->static_prio + 5 < smt_curr->static_prio && smt_curr->mm && p->mm && !rt_task(smt_curr)) || (smt_curr == smt_rq->idle && smt_rq->nr_running)) - resched_task(smt_curr); + resched_task(smt_curr); } +out_unlock: + for_each_cpu_mask(i, sibling_map) + spin_unlock(&cpu_rq(i)->lock); return ret; } #else -static inline void wake_sleeping_dependent(int cpu, runqueue_t *rq) +static inline void wake_sleeping_dependent(int this_cpu, runqueue_t *this_rq) { } -static inline int dependent_sleeper(int cpu, runqueue_t *rq, task_t *p) +static inline int dependent_sleeper(int this_cpu, runqueue_t *this_rq) { return 0; } @@ -2197,11 +2459,10 @@ asmlinkage void __sched schedule(void) * schedule() atomically, we ignore that path for now. * Otherwise, whine if we are scheduling when we should not be. */ - if (likely(!(current->state & (TASK_DEAD | TASK_ZOMBIE)))) { - if (unlikely(in_atomic())) { - printk(KERN_ERR "bad: scheduling while atomic!\n"); - dump_stack(); - } + if (unlikely(in_atomic()) && + likely(!(current->state & (TASK_DEAD | TASK_ZOMBIE)))) { + printk(KERN_ERR "bad: scheduling while atomic!\n"); + dump_stack(); } need_resched: @@ -2209,20 +2470,21 @@ need_resched: prev = current; rq = this_rq(); - release_kernel_lock(prev); - now = sched_clock(); - if (likely(now - prev->timestamp < NS_MAX_SLEEP_AVG)) - run_time = now - prev->timestamp; - else - run_time = NS_MAX_SLEEP_AVG; - /* - * Tasks with interactive credits get charged less run_time - * at high sleep_avg to delay them losing their interactive - * status + * The idle thread is not allowed to schedule! + * Remove this check after it has been exercised a bit. */ - if (HIGH_CREDIT(prev)) - run_time /= (CURRENT_BONUS(prev) ? : 1); + if (unlikely(current == rq->idle) && current->state != TASK_RUNNING) { + printk(KERN_ERR "bad: scheduling from the idle thread!\n"); + dump_stack(); + } + + release_kernel_lock(prev); + schedstat_inc(rq, sched_cnt); + now = clock_us(); + run_time = now - prev->timestamp; + prev->timestamp = now; + add_task_time(prev, run_time, STIME_RUN); spin_lock_irq(&rq->lock); @@ -2236,19 +2498,62 @@ need_resched: if (unlikely((prev->state & TASK_INTERRUPTIBLE) && unlikely(signal_pending(prev)))) prev->state = TASK_RUNNING; - else + else { deactivate_task(prev, rq); + if (prev->state == TASK_UNINTERRUPTIBLE) + rq->nr_uninterruptible++; + goto no_check_expired; + } + } + + if (unlikely(prev->used_slice == -1)) { + if (rt_task(prev)) { + /* SCHED_FIFO can come in here too, from sched_yield */ + dequeue_task(prev, prev->array); + enqueue_task(prev, rq->active); + } else { + dequeue_task(prev, prev->array); + prev->prio = task_priority(prev); + enqueue_task(prev, rq->expired); + if (prev->prio < rq->expired->min_prio) + rq->expired->min_prio = prev->prio; + } + prev->used_slice = 0; } +no_check_expired: cpu = smp_processor_id(); if (unlikely(!rq->nr_running)) { +go_idle: + rq->array_sequence++; idle_balance(cpu, rq); if (!rq->nr_running) { next = rq->idle; - rq->expired_timestamp = 0; + rq->arrays[0].min_prio = MAX_PRIO; + rq->arrays[1].min_prio = MAX_PRIO; + wake_sleeping_dependent(cpu, rq); + /* + * wake_sleeping_dependent() might have released + * the runqueue, so break out if we got new + * tasks meanwhile: + */ + if (!rq->nr_running) + goto switch_tasks; + } + } else { + if (dependent_sleeper(cpu, rq)) { + schedstat_inc(rq, sched_goidle); + next = rq->idle; goto switch_tasks; } + /* + * dependent_sleeper() releases and reacquires the runqueue + * lock, hence go into the idle loop if the rq went + * empty meanwhile: + */ + if (unlikely(!rq->nr_running)) + goto go_idle; } array = rq->active; @@ -2256,47 +2561,25 @@ need_resched: /* * Switch the active and expired arrays. */ + schedstat_inc(rq, sched_switch); + rq->array_sequence++; rq->active = rq->expired; rq->expired = array; + rq->expired->min_prio = MAX_PRIO; array = rq->active; - rq->expired_timestamp = 0; - rq->best_expired_prio = MAX_PRIO; - } + } else + schedstat_inc(rq, sched_noswitch); idx = sched_find_first_bit(array->bitmap); queue = array->queue + idx; next = list_entry(queue->next, task_t, run_list); - if (dependent_sleeper(cpu, rq, next)) { - next = rq->idle; - goto switch_tasks; - } - - if (!rt_task(next) && next->activated > 0) { - unsigned long long delta = now - next->timestamp; - - if (next->activated == 1) - delta = delta * (ON_RUNQUEUE_WEIGHT * 128 / 100) / 128; - - array = next->array; - dequeue_task(next, array); - recalc_task_prio(next, next->timestamp + delta); - enqueue_task(next, array); - } - next->activated = 0; switch_tasks: prefetch(next); clear_tsk_need_resched(prev); rcu_qsctr_inc(task_cpu(prev)); - prev->sleep_avg -= run_time; - if ((long)prev->sleep_avg <= 0) { - prev->sleep_avg = 0; - if (!(HIGH_CREDIT(prev) || LOW_CREDIT(prev))) - prev->interactive_credit--; - } - prev->timestamp = now; - + sched_info_switch(prev, next); if (likely(prev != next)) { next->timestamp = now; rq->nr_switches++; @@ -2313,7 +2596,7 @@ switch_tasks: reacquire_kernel_lock(current); preempt_enable_no_resched(); - if (test_thread_flag(TIF_NEED_RESCHED)) + if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) goto need_resched; } @@ -2606,6 +2889,13 @@ out_unlock: EXPORT_SYMBOL(set_user_nice); +#ifdef CONFIG_KGDB +struct task_struct *kgdb_get_idle(int this_cpu) +{ + return cpu_rq(this_cpu)->idle; +} +#endif + #ifdef __ARCH_WANT_SYS_NICE /* @@ -2751,6 +3041,7 @@ static int setscheduler(pid_t pid, int p policy != SCHED_NORMAL) goto out_unlock; } + profile_hit(SCHED_PROFILING, __builtin_return_address(0)); /* * Valid priorities for SCHED_FIFO and SCHED_RR are @@ -2776,12 +3067,15 @@ static int setscheduler(pid_t pid, int p array = p->array; if (array) - deactivate_task(p, task_rq(p)); + deactivate_task(p, rq); retval = 0; oldprio = p->prio; __setscheduler(p, policy, lp.sched_priority); + if (policy == SCHED_FIFO || policy == SCHED_RR) + p->used_slice = 0; + if (array) { - __activate_task(p, task_rq(p)); + __activate_task(p, rq, array); /* * Reschedule if we are currently running on this runqueue and * our priority decreased, or if we are not currently running on @@ -2891,6 +3185,34 @@ out_unlock: return retval; } +static int get_user_cpu_mask(unsigned long __user *user_mask_ptr, unsigned len, + cpumask_t *new_mask) +{ + if (len < sizeof(cpumask_t)) { + /* Smaller is ok as long as all online CPUs are covered */ + int i, max = 0; + for_each_online_cpu(i) + max = i; + if (len < (max + 7)/8) + return -EINVAL; + memset(new_mask, 0, sizeof(cpumask_t)); + } else if (len > sizeof(cpumask_t)) { + /* Longer is ok as long as all high bits are 0 */ + int i; + if (len > PAGE_SIZE) + return -EINVAL; + for (i = sizeof(cpumask_t); i < len; i++) { + unsigned char val; + if (get_user(val, (unsigned char *)user_mask_ptr + i)) + return -EFAULT; + if (val) + return -EINVAL; + } + len = sizeof(cpumask_t); + } + return copy_from_user(new_mask, user_mask_ptr, len) ? -EFAULT : 0; +} + /** * sys_sched_setaffinity - set the cpu affinity of a process * @pid: pid of the process @@ -2900,15 +3222,13 @@ out_unlock: asmlinkage long sys_sched_setaffinity(pid_t pid, unsigned int len, unsigned long __user *user_mask_ptr) { - cpumask_t new_mask; + cpumask_t new_mask, cpus_allowed; int retval; task_t *p; - if (len < sizeof(new_mask)) - return -EINVAL; - - if (copy_from_user(&new_mask, user_mask_ptr, sizeof(new_mask))) - return -EFAULT; + retval = get_user_cpu_mask(user_mask_ptr, len, &new_mask); + if (retval) + return retval; lock_cpu_hotplug(); read_lock(&tasklist_lock); @@ -2933,6 +3253,8 @@ asmlinkage long sys_sched_setaffinity(pi !capable(CAP_SYS_NICE)) goto out_unlock; + cpus_allowed = cpuset_cpus_allowed(p); + cpus_and(new_mask, new_mask, cpus_allowed); retval = set_cpus_allowed(p, new_mask); out_unlock: @@ -3004,29 +3326,31 @@ out_unlock: */ asmlinkage long sys_sched_yield(void) { - runqueue_t *rq = this_rq_lock(); - prio_array_t *array = current->array; - prio_array_t *target = rq->expired; - - /* - * We implement yielding by moving the task into the expired - * queue. - * - * (special rule: RT tasks will just roundrobin in the active - * array.) - */ - if (unlikely(rt_task(current))) - target = rq->active; +#ifdef CONFIG_SCHEDSTATS + runqueue_t *rq; +#endif - dequeue_task(current, array); - enqueue_task(current, target); + local_irq_disable(); +#ifdef CONFIG_SCHEDSTATS + rq = this_rq(); + schedstat_inc(rq, yld_cnt); + spin_lock(&rq->lock); + if (current->array->nr_active == 1) { + schedstat_inc(rq, yld_act_empty); + if (!rq->expired->nr_active) + schedstat_inc(rq, yld_both_empty); + } else if (!rq->expired->nr_active) + schedstat_inc(rq, yld_exp_empty); /* * Since we are going to call schedule() anyway, there's * no need to preempt or enable interrupts: */ _raw_spin_unlock(&rq->lock); preempt_enable_no_resched(); +#endif + current->used_slice = -1; + local_irq_enable(); schedule(); @@ -3143,6 +3467,8 @@ long sys_sched_rr_get_interval(pid_t pid int retval = -EINVAL; struct timespec t; task_t *p; + unsigned long flags; + runqueue_t *rq; if (pid < 0) goto out_nounlock; @@ -3157,8 +3483,9 @@ long sys_sched_rr_get_interval(pid_t pid if (retval) goto out_unlock; - jiffies_to_timespec(p->policy & SCHED_FIFO ? - 0 : task_timeslice(p), &t); + rq = task_rq_lock(p, &flags); + jiffies_to_timespec(p->policy & SCHED_FIFO ? 0 : task_timeslice(p, rq), &t); + task_rq_unlock(rq, &flags); read_unlock(&tasklist_lock); retval = copy_to_user(interval, &t, sizeof(t)) ? -EFAULT : 0; out_nounlock: @@ -3191,7 +3518,7 @@ static void show_task(task_t * p) task_t *relative; unsigned state; unsigned long free = 0; - static const char *stat_nam[] = { "R", "S", "D", "T", "Z", "W" }; + static const char *stat_nam[] = { "R", "S", "D", "T", "t", "Z", "X" }; printk("%-13.13s ", p->comm); state = p->state ? __ffs(p->state) + 1 : 0; @@ -3268,21 +3595,19 @@ void show_state(void) void __devinit init_idle(task_t *idle, int cpu) { - runqueue_t *idle_rq = cpu_rq(cpu), *rq = cpu_rq(task_cpu(idle)); + runqueue_t *rq = cpu_rq(cpu); unsigned long flags; - local_irq_save(flags); - double_rq_lock(idle_rq, rq); - - idle_rq->curr = idle_rq->idle = idle; - deactivate_task(idle, rq); idle->array = NULL; idle->prio = MAX_PRIO; idle->state = TASK_RUNNING; + idle->used_slice = 0; set_task_cpu(idle, cpu); - double_rq_unlock(idle_rq, rq); + + spin_lock_irqsave(&rq->lock, flags); + rq->curr = rq->idle = idle; set_tsk_need_resched(idle); - local_irq_restore(flags); + spin_unlock_irqrestore(&rq->lock, flags); /* Set the preempt count _outside_ the spinlocks! */ #ifdef CONFIG_PREEMPT @@ -3334,6 +3659,8 @@ int set_cpus_allowed(task_t *p, cpumask_ migration_req_t req; runqueue_t *rq; + perfctr_set_cpus_allowed(p, new_mask); + rq = task_rq_lock(p, &flags); if (!cpus_intersects(new_mask, cpu_online_map)) { ret = -EINVAL; @@ -3364,7 +3691,7 @@ EXPORT_SYMBOL_GPL(set_cpus_allowed); * Move (not current) task off this cpu, onto dest cpu. We're doing * this because either it can't run here any more (set_cpus_allowed() * away from this CPU, or CPU going down), or because we're - * attempting to rebalance this task on exec (sched_balance_exec). + * attempting to rebalance this task on exec (sched_exec). * * So we race with normal scheduler movements, but that's OK, as long * as the task is no longer on this CPU. @@ -3376,7 +3703,7 @@ static void __migrate_task(struct task_s if (unlikely(cpu_is_offline(dest_cpu))) return; - rq_src = cpu_rq(src_cpu); + rq_src = cpu_rq(src_cpu); rq_dest = cpu_rq(dest_cpu); double_rq_lock(rq_src, rq_dest); @@ -3481,49 +3808,54 @@ wait_to_die: } #ifdef CONFIG_HOTPLUG_CPU -/* migrate_all_tasks - function to migrate all tasks from the dead cpu. */ -static void migrate_all_tasks(int src_cpu) +/* Figure out where task on dead CPU should go, use force if neccessary. */ +static void move_task_off_dead_cpu(int dead_cpu, struct task_struct *tsk) { - struct task_struct *tsk, *t; int dest_cpu; - unsigned int node; + cpumask_t mask; - write_lock_irq(&tasklist_lock); + /* On same node? */ + mask = node_to_cpumask(cpu_to_node(dead_cpu)); + cpus_and(mask, mask, tsk->cpus_allowed); + dest_cpu = any_online_cpu(mask); + + /* On any allowed CPU? */ + if (dest_cpu == NR_CPUS) + dest_cpu = any_online_cpu(tsk->cpus_allowed); + + /* No more Mr. Nice Guy. */ + if (dest_cpu == NR_CPUS) { + tsk->cpus_allowed = cpuset_cpus_allowed(tsk); + if (!cpus_intersects(tsk->cpus_allowed, cpu_online_map)) + cpus_setall(tsk->cpus_allowed); + dest_cpu = any_online_cpu(tsk->cpus_allowed); + + /* + * Don't tell them about moving exiting tasks or + * kernel threads (both mm NULL), since they never + * leave kernel. + */ + if (tsk->mm && printk_ratelimit()) + printk(KERN_INFO "process %d (%s) no " + "longer affine to cpu%d\n", + tsk->pid, tsk->comm, dead_cpu); + } + __migrate_task(tsk, dead_cpu, dest_cpu); +} + +/* Run through task list and migrate tasks from the dead cpu. */ +static void migrate_live_tasks(int src_cpu) +{ + struct task_struct *tsk, *t; - /* watch out for per node tasks, let's stay on this node */ - node = cpu_to_node(src_cpu); + write_lock_irq(&tasklist_lock); do_each_thread(t, tsk) { - cpumask_t mask; if (tsk == current) continue; - if (task_cpu(tsk) != src_cpu) - continue; - - /* Figure out where this task should go (attempting to - * keep it on-node), and check if it can be migrated - * as-is. NOTE that kernel threads bound to more than - * one online cpu will be migrated. */ - mask = node_to_cpumask(node); - cpus_and(mask, mask, tsk->cpus_allowed); - dest_cpu = any_online_cpu(mask); - if (dest_cpu == NR_CPUS) - dest_cpu = any_online_cpu(tsk->cpus_allowed); - if (dest_cpu == NR_CPUS) { - cpus_setall(tsk->cpus_allowed); - dest_cpu = any_online_cpu(tsk->cpus_allowed); - - /* Don't tell them about moving exiting tasks - or kernel threads (both mm NULL), since - they never leave kernel. */ - if (tsk->mm && printk_ratelimit()) - printk(KERN_INFO "process %d (%s) no " - "longer affine to cpu%d\n", - tsk->pid, tsk->comm, src_cpu); - } - - __migrate_task(tsk, src_cpu, dest_cpu); + if (task_cpu(tsk) == src_cpu) + move_task_off_dead_cpu(src_cpu, tsk); } while_each_thread(t, tsk); write_unlock_irq(&tasklist_lock); @@ -3554,6 +3886,47 @@ void sched_idle_next(void) spin_unlock_irqrestore(&rq->lock, flags); } + +static void migrate_dead(unsigned int dead_cpu, task_t *tsk) +{ + struct runqueue *rq = cpu_rq(dead_cpu); + + /* Must be exiting, otherwise would be on tasklist. */ + BUG_ON(tsk->state != TASK_ZOMBIE && tsk->state != TASK_DEAD); + + /* Cannot have done final schedule yet: would have vanished. */ + BUG_ON(tsk->flags & PF_DEAD); + + get_task_struct(tsk); + + /* + * Drop lock around migration; if someone else moves it, + * that's OK. No task can be added to this CPU, so iteration is + * fine. + */ + spin_unlock_irq(&rq->lock); + move_task_off_dead_cpu(dead_cpu, tsk); + spin_lock_irq(&rq->lock); + + put_task_struct(tsk); +} + +/* release_task() removes task from tasklist, so we won't find dead tasks. */ +static void migrate_dead_tasks(unsigned int dead_cpu) +{ + unsigned arr, i; + struct runqueue *rq = cpu_rq(dead_cpu); + + for (arr = 0; arr < 2; arr++) { + for (i = 0; i < MAX_PRIO; i++) { + struct list_head *list = &rq->arrays[arr].queue[i]; + while (!list_empty(list)) + migrate_dead(dead_cpu, + list_entry(list->next, task_t, + run_list)); + } + } +} #endif /* CONFIG_HOTPLUG_CPU */ /* @@ -3593,7 +3966,7 @@ static int migration_call(struct notifie cpu_rq(cpu)->migration_thread = NULL; break; case CPU_DEAD: - migrate_all_tasks(cpu); + migrate_live_tasks(cpu); rq = cpu_rq(cpu); kthread_stop(rq->migration_thread); rq->migration_thread = NULL; @@ -3602,8 +3975,9 @@ static int migration_call(struct notifie deactivate_task(rq->idle, rq); rq->idle->static_prio = MAX_PRIO; __setscheduler(rq->idle, SCHED_NORMAL, 0); + migrate_dead_tasks(cpu); task_rq_unlock(rq, &flags); - BUG_ON(rq->nr_running != 0); + BUG_ON(rq->nr_running != 0); /* No need to migrate the tasks: it was best-effort if * they didn't do lock_cpu_hotplug(). Just wake up @@ -3618,7 +3992,7 @@ static int migration_call(struct notifie complete(&req->done); } spin_unlock_irq(&rq->lock); - break; + break; #endif } return NOTIFY_OK; @@ -3660,7 +4034,7 @@ EXPORT_SYMBOL(kernel_flag); #ifdef CONFIG_SMP /* Attach the domain 'sd' to 'cpu' as its base domain */ -void cpu_attach_domain(struct sched_domain *sd, int cpu) +static void cpu_attach_domain(struct sched_domain *sd, int cpu) { migration_req_t req; unsigned long flags; @@ -3691,123 +4065,326 @@ void cpu_attach_domain(struct sched_doma unlock_cpu_hotplug(); } -#ifdef ARCH_HAS_SCHED_DOMAIN -extern void __init arch_init_sched_domains(void); -#else -static struct sched_group sched_group_cpus[NR_CPUS]; -static DEFINE_PER_CPU(struct sched_domain, cpu_domains); #ifdef CONFIG_NUMA -static struct sched_group sched_group_nodes[MAX_NUMNODES]; -static DEFINE_PER_CPU(struct sched_domain, node_domains); -static void __init arch_init_sched_domains(void) +/** + * find_next_best_node - find the next node to include in a sched_domain + * @node: node whose sched_domain we're building + * @used_nodes: nodes already in the sched_domain + * + * Find the next node to include in a given scheduling domain. Simply + * finds the closest node not already in the @used_nodes map. + * + * Should use nodemask_t. + */ +static int __init find_next_best_node(int node, unsigned long *used_nodes) +{ + int i, n, val, min_val, best_node = 0; + + min_val = INT_MAX; + + for (i = 0; i < numnodes; i++) { + /* Start at @node */ + n = (node + i) % numnodes; + + /* Skip already used nodes */ + if (test_bit(n, used_nodes)) + continue; + + /* Simple min distance search */ + val = node_distance(node, i); + + if (val < min_val) { + min_val = val; + best_node = n; + } + } + + set_bit(best_node, used_nodes); + return best_node; +} + +/** + * sched_domain_node_span - get a cpumask for a node's sched_domain + * @node: node whose cpumask we're constructing + * @size: number of nodes to include in this span + * + * Given a node, construct a good cpumask for its sched_domain to span. It + * should be one that prevents unnecessary balancing, but also spreads tasks + * out optimally. + */ +cpumask_t __init sched_domain_node_span(int node, int size) { int i; - struct sched_group *first_node = NULL, *last_node = NULL; + cpumask_t span; + DECLARE_BITMAP(used_nodes, MAX_NUMNODES); - /* Set up domains */ - for_each_cpu(i) { - int node = cpu_to_node(i); - cpumask_t nodemask = node_to_cpumask(node); - struct sched_domain *node_sd = &per_cpu(node_domains, i); - struct sched_domain *cpu_sd = &per_cpu(cpu_domains, i); - - *node_sd = SD_NODE_INIT; - node_sd->span = cpu_possible_map; - node_sd->groups = &sched_group_nodes[cpu_to_node(i)]; - - *cpu_sd = SD_CPU_INIT; - cpus_and(cpu_sd->span, nodemask, cpu_possible_map); - cpu_sd->groups = &sched_group_cpus[i]; - cpu_sd->parent = node_sd; + cpus_clear(span); + bitmap_zero(used_nodes, MAX_NUMNODES); + + for (i = 0; i < size; i++) { + int next_node = find_next_best_node(node, used_nodes); + cpumask_t nodemask; + + nodemask = node_to_cpumask(next_node); + cpus_or(span, span, nodemask); } - /* Set up groups */ - for (i = 0; i < MAX_NUMNODES; i++) { - cpumask_t tmp = node_to_cpumask(i); - cpumask_t nodemask; - struct sched_group *first_cpu = NULL, *last_cpu = NULL; - struct sched_group *node = &sched_group_nodes[i]; - int j; + return span; +} +#endif /* CONFIG_NUMA */ + +#ifdef CONFIG_SCHED_SMT +static DEFINE_PER_CPU(struct sched_domain, cpu_domains); +static struct sched_group sched_group_cpus[NR_CPUS]; +__init static int cpu_to_cpu_group(int cpu) +{ + return cpu; +} +#endif - cpus_and(nodemask, tmp, cpu_possible_map); +static DEFINE_PER_CPU(struct sched_domain, phys_domains); +static struct sched_group sched_group_phys[NR_CPUS]; +__init static int cpu_to_phys_group(int cpu) +{ +#ifdef CONFIG_SCHED_SMT + return first_cpu(cpu_sibling_map[cpu]); +#else + return cpu; +#endif +} - if (cpus_empty(nodemask)) - continue; +#ifdef CONFIG_NUMA - node->cpumask = nodemask; - node->cpu_power = SCHED_LOAD_SCALE * cpus_weight(node->cpumask); +/* Number of nearby nodes in a node's scheduling domain */ +#define SD_NODES_PER_DOMAIN 4 - for_each_cpu_mask(j, node->cpumask) { - struct sched_group *cpu = &sched_group_cpus[j]; +static DEFINE_PER_CPU(struct sched_domain, node_domains); +static struct sched_group sched_group_nodes[MAX_NUMNODES]; +__init static int cpu_to_node_group(int cpu) +{ + return cpu_to_node(cpu); +} +#endif - cpus_clear(cpu->cpumask); - cpu_set(j, cpu->cpumask); - cpu->cpu_power = SCHED_LOAD_SCALE; +/* Groups for isolated scheduling domains */ +static struct sched_group sched_group_isolated[NR_CPUS]; - if (!first_cpu) - first_cpu = cpu; - if (last_cpu) - last_cpu->next = cpu; - last_cpu = cpu; - } - last_cpu->next = first_cpu; +/* cpus with isolated domains */ +cpumask_t __initdata cpu_isolated_map = CPU_MASK_NONE; - if (!first_node) - first_node = node; - if (last_node) - last_node->next = node; - last_node = node; - } - last_node->next = first_node; +__init static int cpu_to_isolated_group(int cpu) +{ + return cpu; +} - mb(); - for_each_cpu(i) { - struct sched_domain *cpu_sd = &per_cpu(cpu_domains, i); - cpu_attach_domain(cpu_sd, i); +/* Setup the mask of cpus configured for isolated domains */ +static int __init isolated_cpu_setup(char *str) +{ + int ints[NR_CPUS], i; + + str = get_options(str, ARRAY_SIZE(ints), ints); + cpus_clear(cpu_isolated_map); + for (i = 1; i <= ints[0]; i++) + cpu_set(ints[i], cpu_isolated_map); + return 1; +} + +__setup ("isolcpus=", isolated_cpu_setup); + +/* + * init_sched_build_groups takes an array of groups, the cpumask we wish + * to span, and a pointer to a function which identifies what group a CPU + * belongs to. The return value of group_fn must be a valid index into the + * groups[] array, and must be >= 0 and < NR_CPUS (due to the fact that we + * keep track of groups covered with a cpumask_t). + * + * init_sched_build_groups will build a circular linked list of the groups + * covered by the given span, and will set each group's ->cpumask correctly, + * and ->cpu_power to 0. + */ +__init static void init_sched_build_groups(struct sched_group groups[], + cpumask_t span, int (*group_fn)(int cpu)) +{ + struct sched_group *first = NULL, *last = NULL; + cpumask_t covered = CPU_MASK_NONE; + int i; + + for_each_cpu_mask(i, span) { + int group = group_fn(i); + struct sched_group *sg = &groups[group]; + int j; + + if (cpu_isset(i, covered)) + continue; + + sg->cpumask = CPU_MASK_NONE; + sg->cpu_power = 0; + + for_each_cpu_mask(j, span) { + if (group_fn(j) != group) + continue; + + cpu_set(j, covered); + cpu_set(j, sg->cpumask); + } + if (!first) + first = sg; + if (last) + last->next = sg; + last = sg; } + last->next = first; } -#else /* !CONFIG_NUMA */ -static void __init arch_init_sched_domains(void) +__init static void arch_init_sched_domains(void) { int i; - struct sched_group *first_cpu = NULL, *last_cpu = NULL; + cpumask_t cpu_default_map; + + /* + * Setup mask for cpus without special case scheduling requirements. + * For now this just excludes isolated cpus, but could be used to + * exclude other special cases in the future. + */ + cpus_complement(cpu_default_map, cpu_isolated_map); + cpus_and(cpu_default_map, cpu_default_map, cpu_possible_map); /* Set up domains */ for_each_cpu(i) { - struct sched_domain *cpu_sd = &per_cpu(cpu_domains, i); + int group; + struct sched_domain *sd = NULL, *p; + cpumask_t nodemask = node_to_cpumask(cpu_to_node(i)); + + cpus_and(nodemask, nodemask, cpu_default_map); + + /* + * Set up isolated domains. + * Unlike those of other cpus, the domains and groups are + * single level, and span a single cpu. + */ + if (cpu_isset(i, cpu_isolated_map)) { +#ifdef CONFIG_SCHED_SMT + sd = &per_cpu(cpu_domains, i); +#else + sd = &per_cpu(phys_domains, i); +#endif + group = cpu_to_isolated_group(i); + *sd = SD_CPU_INIT; + cpu_set(i, sd->span); + sd->balance_interval = INT_MAX; /* Don't balance */ + sd->flags = 0; /* Avoid WAKE_ */ + sd->groups = &sched_group_isolated[group]; + printk(KERN_INFO "Setting up cpu %d isolated.\n", i); + /* Single level, so continue with next cpu */ + continue; + } + +#ifdef CONFIG_NUMA + sd = &per_cpu(node_domains, i); + group = cpu_to_node_group(i); + *sd = SD_NODE_INIT; + /* FIXME: should be multilevel, in arch code */ + sd->span = sched_domain_node_span(i, SD_NODES_PER_DOMAIN); + cpus_and(sd->span, sd->span, cpu_default_map); + sd->groups = &sched_group_nodes[group]; +#endif + + p = sd; + sd = &per_cpu(phys_domains, i); + group = cpu_to_phys_group(i); + *sd = SD_CPU_INIT; + sd->span = nodemask; + sd->parent = p; + sd->groups = &sched_group_phys[group]; + +#ifdef CONFIG_SCHED_SMT + p = sd; + sd = &per_cpu(cpu_domains, i); + group = cpu_to_cpu_group(i); + *sd = SD_SIBLING_INIT; + sd->span = cpu_sibling_map[i]; + cpus_and(sd->span, sd->span, cpu_default_map); + sd->parent = p; + sd->groups = &sched_group_cpus[group]; +#endif + } + +#ifdef CONFIG_SCHED_SMT + /* Set up CPU (sibling) groups */ + for_each_cpu(i) { + cpumask_t this_sibling_map = cpu_sibling_map[i]; + cpus_and(this_sibling_map, this_sibling_map, cpu_default_map); + if (i != first_cpu(this_sibling_map)) + continue; - *cpu_sd = SD_CPU_INIT; - cpu_sd->span = cpu_possible_map; - cpu_sd->groups = &sched_group_cpus[i]; + init_sched_build_groups(sched_group_cpus, this_sibling_map, + &cpu_to_cpu_group); } +#endif + + /* Set up isolated groups */ + for_each_cpu_mask(i, cpu_isolated_map) { + cpumask_t mask; + cpus_clear(mask); + cpu_set(i, mask); + init_sched_build_groups(sched_group_isolated, mask, + &cpu_to_isolated_group); + } + + /* Set up physical groups */ + for (i = 0; i < MAX_NUMNODES; i++) { + cpumask_t nodemask = node_to_cpumask(i); + + cpus_and(nodemask, nodemask, cpu_default_map); + if (cpus_empty(nodemask)) + continue; + + init_sched_build_groups(sched_group_phys, nodemask, + &cpu_to_phys_group); + } + +#ifdef CONFIG_NUMA + /* Set up node groups */ + init_sched_build_groups(sched_group_nodes, cpu_default_map, + &cpu_to_node_group); +#endif - /* Set up CPU groups */ - for_each_cpu_mask(i, cpu_possible_map) { - struct sched_group *cpu = &sched_group_cpus[i]; + /* Calculate CPU power for physical packages and nodes */ + for_each_cpu_mask(i, cpu_default_map) { + int power; + struct sched_domain *sd; +#ifdef CONFIG_SCHED_SMT + sd = &per_cpu(cpu_domains, i); + power = SCHED_LOAD_SCALE; + sd->groups->cpu_power = power; +#endif - cpus_clear(cpu->cpumask); - cpu_set(i, cpu->cpumask); - cpu->cpu_power = SCHED_LOAD_SCALE; + sd = &per_cpu(phys_domains, i); + power = SCHED_LOAD_SCALE + SCHED_LOAD_SCALE * + (cpus_weight(sd->groups->cpumask)-1) / 10; + sd->groups->cpu_power = power; - if (!first_cpu) - first_cpu = cpu; - if (last_cpu) - last_cpu->next = cpu; - last_cpu = cpu; +#ifdef CONFIG_NUMA + if (i == first_cpu(sd->groups->cpumask)) { + /* Only add "power" once for each physical package. */ + sd = &per_cpu(node_domains, i); + sd->groups->cpu_power += power; + } +#endif } - last_cpu->next = first_cpu; - mb(); /* domains were modified outside the lock */ + /* Attach the domains */ for_each_cpu(i) { - struct sched_domain *cpu_sd = &per_cpu(cpu_domains, i); - cpu_attach_domain(cpu_sd, i); + struct sched_domain *sd; +#ifdef CONFIG_SCHED_SMT + sd = &per_cpu(cpu_domains, i); +#else + sd = &per_cpu(phys_domains, i); +#endif + cpu_attach_domain(sd, i); } } -#endif /* CONFIG_NUMA */ -#endif /* ARCH_HAS_SCHED_DOMAIN */ - #define SCHED_DOMAIN_DEBUG #ifdef SCHED_DOMAIN_DEBUG void sched_domain_debug(void) @@ -3937,7 +4514,6 @@ void __init sched_init(void) spin_lock_init(&rq->lock); rq->active = rq->arrays; rq->expired = rq->arrays + 1; - rq->best_expired_prio = MAX_PRIO; #ifdef CONFIG_SMP rq->sd = &sched_domain_init; @@ -3951,29 +4527,29 @@ void __init sched_init(void) for (j = 0; j < 2; j++) { array = rq->arrays + j; + array->min_prio = MAX_PRIO; for (k = 0; k < MAX_PRIO; k++) { INIT_LIST_HEAD(array->queue + k); __clear_bit(k, array->bitmap); } - // delimiter for bitsearch + /* delimiter for bitsearch */ __set_bit(MAX_PRIO, array->bitmap); } } - /* - * We have to do a little magic to get the first - * thread right in SMP mode. - */ - rq = this_rq(); - rq->curr = current; - rq->idle = current; - set_task_cpu(current, smp_processor_id()); - wake_up_forked_process(current); /* * The boot idle thread does lazy MMU switching as well: */ atomic_inc(&init_mm.mm_count); enter_lazy_tlb(&init_mm, current); + + /* + * Make us the idle thread. Technically, schedule() should not be + * called from this thread, however somewhere below it might be, + * but because we are the idle thread, we just pick up running again + * when this runqueue becomes "idle". + */ + init_idle(current, smp_processor_id()); } #ifdef CONFIG_DEBUG_SPINLOCK_SLEEP --- linux-2.6.9-rc1/kernel/signal.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/kernel/signal.c 2004-09-03 00:56:56.214614728 -0700 @@ -26,6 +26,8 @@ #include #include +extern void k_getrusage(struct task_struct *, int, struct rusage *); + /* * SLAB caches for signal bits. */ @@ -367,6 +369,22 @@ void __exit_signal(struct task_struct *t if (tsk == sig->curr_target) sig->curr_target = next_thread(tsk); tsk->signal = NULL; + /* + * Accumulate here the counters for all threads but the + * group leader as they die, so they can be added into + * the process-wide totals when those are taken. + * The group leader stays around as a zombie as long + * as there are other threads. When it gets reaped, + * the exit.c code will add its counts into these totals. + * We won't ever get here for the group leader, since it + * will have been the last reference on the signal_struct. + */ + sig->utime += tsk->utime; + sig->stime += tsk->stime; + sig->min_flt += tsk->min_flt; + sig->maj_flt += tsk->maj_flt; + sig->nvcsw += tsk->nvcsw; + sig->nivcsw += tsk->nivcsw; spin_unlock(&sighand->siglock); sig = NULL; /* Marker for below. */ } @@ -618,7 +636,8 @@ static int check_kill_permission(int sig /* forward decl */ static void do_notify_parent_cldstop(struct task_struct *tsk, - struct task_struct *parent); + struct task_struct *parent, + int why); /* * Handle magic process-wide effects of stop/continue signals. @@ -660,12 +679,17 @@ static void handle_stop_signal(int sig, * the SIGCHLD was pending on entry to this kill. */ p->signal->group_stop_count = 0; + p->signal->stop_state = 1; + spin_unlock(&p->sighand->siglock); if (p->ptrace & PT_PTRACED) - do_notify_parent_cldstop(p, p->parent); + do_notify_parent_cldstop(p, p->parent, + CLD_STOPPED); else do_notify_parent_cldstop( p->group_leader, - p->group_leader->real_parent); + p->group_leader->real_parent, + CLD_STOPPED); + spin_lock(&p->sighand->siglock); } rm_from_queue(SIG_KERNEL_STOP_MASK, &p->signal->shared_pending); t = p; @@ -696,6 +720,25 @@ static void handle_stop_signal(int sig, t = next_thread(t); } while (t != p); + + if (p->signal->stop_state > 0) { + /* + * We were in fact stopped, and are now continued. + * Notify the parent with CLD_CONTINUED. + */ + p->signal->stop_state = -1; + p->signal->group_exit_code = 0; + spin_unlock(&p->sighand->siglock); + if (p->ptrace & PT_PTRACED) + do_notify_parent_cldstop(p, p->parent, + CLD_CONTINUED); + else + do_notify_parent_cldstop( + p->group_leader, + p->group_leader->real_parent, + CLD_CONTINUED); + spin_lock(&p->sighand->siglock); + } } } @@ -861,11 +904,20 @@ force_sig_specific(int sig, struct task_ static void -__group_complete_signal(int sig, struct task_struct *p, unsigned int mask) +__group_complete_signal(int sig, struct task_struct *p) { + unsigned int mask; struct task_struct *t; /* + * Don't bother zombies and stopped tasks (but + * SIGKILL will punch through stopped state) + */ + mask = TASK_DEAD | TASK_ZOMBIE | TASK_TRACED; + if (sig != SIGKILL) + mask |= TASK_STOPPED; + + /* * Now find a thread we can wake up to take the signal off the queue. * * If the main thread wants the signal, it gets first crack. @@ -966,7 +1018,6 @@ __group_complete_signal(int sig, struct static int __group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p) { - unsigned int mask; int ret = 0; #ifdef CONFIG_SMP @@ -990,14 +1041,6 @@ __group_send_sig_info(int sig, struct si return ret; /* - * Don't bother zombies and stopped tasks (but - * SIGKILL will punch through stopped state) - */ - mask = TASK_DEAD | TASK_ZOMBIE; - if (sig != SIGKILL) - mask |= TASK_STOPPED; - - /* * Put this signal on the shared-pending queue, or fail with EAGAIN. * We always use the shared queue for process-wide signals, * to avoid several races. @@ -1006,7 +1049,7 @@ __group_send_sig_info(int sig, struct si if (unlikely(ret)) return ret; - __group_complete_signal(sig, p, mask); + __group_complete_signal(sig, p); return 0; } @@ -1072,8 +1115,6 @@ int group_send_sig_info(int sig, struct int __kill_pg_info(int sig, struct siginfo *info, pid_t pgrp) { struct task_struct *p; - struct list_head *l; - struct pid *pid; int retval, success; if (pgrp <= 0) @@ -1081,11 +1122,11 @@ int __kill_pg_info(int sig, struct sigin success = 0; retval = -ESRCH; - for_each_task_pid(pgrp, PIDTYPE_PGID, p, l, pid) { + do_each_task_pid(pgrp, PIDTYPE_PGID, p) { int err = group_send_sig_info(sig, info, p); success |= !err; retval = err; - } + } while_each_task_pid(pgrp, PIDTYPE_PGID, p); return success ? 0 : retval; } @@ -1112,8 +1153,6 @@ int kill_sl_info(int sig, struct siginfo *info, pid_t sid) { int err, retval = -EINVAL; - struct pid *pid; - struct list_head *l; struct task_struct *p; if (sid <= 0) @@ -1121,13 +1160,13 @@ kill_sl_info(int sig, struct siginfo *in retval = -ESRCH; read_lock(&tasklist_lock); - for_each_task_pid(sid, PIDTYPE_SID, p, l, pid) { + do_each_task_pid(sid, PIDTYPE_SID, p) { if (!p->signal->leader) continue; err = group_send_sig_info(sig, info, p); if (retval) retval = err; - } + } while_each_task_pid(sid, PIDTYPE_SID, p); read_unlock(&tasklist_lock); out: return retval; @@ -1243,6 +1282,25 @@ force_sig(int sig, struct task_struct *p force_sig_info(sig, (void*)1L, p); } +/* + * When things go south during signal handling, we + * will force a SIGSEGV. And if the signal that caused + * the problem was already a SIGSEGV, we'll want to + * make sure we don't even try to deliver the signal.. + */ +int +force_sigsegv(int sig, struct task_struct *p) +{ + if (sig == SIGSEGV) { + unsigned long flags; + spin_lock_irqsave(&p->sighand->siglock, flags); + p->sighand->action[sig - 1].sa.sa_handler = SIG_DFL; + spin_unlock_irqrestore(&p->sighand->siglock, flags); + } + force_sig(SIGSEGV, p); + return 0; +} + int kill_pg(pid_t pgrp, int sig, int priv) { @@ -1348,7 +1406,6 @@ int send_group_sigqueue(int sig, struct sigqueue *q, struct task_struct *p) { unsigned long flags; - unsigned int mask; int ret = 0; BUG_ON(!(q->flags & SIGQUEUE_PREALLOC)); @@ -1373,13 +1430,6 @@ send_group_sigqueue(int sig, struct sigq q->info.si_overrun++; goto out; } - /* - * Don't bother zombies and stopped tasks (but - * SIGKILL will punch through stopped state) - */ - mask = TASK_DEAD | TASK_ZOMBIE; - if (sig != SIGKILL) - mask |= TASK_STOPPED; /* * Put this signal on the shared-pending queue. @@ -1390,7 +1440,7 @@ send_group_sigqueue(int sig, struct sigq list_add_tail(&q->list, &p->signal->shared_pending.list); sigaddset(&p->signal->shared_pending.signal, sig); - __group_complete_signal(sig, p, mask); + __group_complete_signal(sig, p); out: spin_unlock_irqrestore(&p->sighand->siglock, flags); read_unlock(&tasklist_lock); @@ -1423,21 +1473,24 @@ static void __wake_up_parent(struct task } /* - * Let a parent know about a status change of a child. + * Let a parent know about the death of a child. + * For a stopped/continued status change, use do_notify_parent_cldstop instead. */ void do_notify_parent(struct task_struct *tsk, int sig) { struct siginfo info; unsigned long flags; - int why, status; struct sighand_struct *psig; if (sig == -1) BUG(); - BUG_ON(tsk->group_leader != tsk && tsk->group_leader->state != TASK_ZOMBIE && !tsk->ptrace); - BUG_ON(tsk->group_leader == tsk && !thread_group_empty(tsk) && !tsk->ptrace); + /* do_notify_parent_cldstop should have been called instead. */ + BUG_ON(tsk->state & (TASK_STOPPED|TASK_TRACED)); + + BUG_ON(!tsk->ptrace && + (tsk->group_leader != tsk || !thread_group_empty(tsk))); info.si_signo = sig; info.si_errno = 0; @@ -1445,37 +1498,23 @@ void do_notify_parent(struct task_struct info.si_uid = tsk->uid; /* FIXME: find out whether or not this is supposed to be c*time. */ - info.si_utime = tsk->utime; - info.si_stime = tsk->stime; - - status = tsk->exit_code & 0x7f; - why = SI_KERNEL; /* shouldn't happen */ - switch (tsk->state) { - case TASK_STOPPED: - /* FIXME -- can we deduce CLD_TRAPPED or CLD_CONTINUED? */ - if (tsk->ptrace & PT_PTRACED) - why = CLD_TRAPPED; - else - why = CLD_STOPPED; - break; + info.si_utime = tsk->utime + tsk->signal->utime; + info.si_stime = tsk->stime + tsk->signal->stime; + k_getrusage(tsk, RUSAGE_BOTH, &info.si_rusage); - default: - if (tsk->exit_code & 0x80) - why = CLD_DUMPED; - else if (tsk->exit_code & 0x7f) - why = CLD_KILLED; - else { - why = CLD_EXITED; - status = tsk->exit_code >> 8; - } - break; + info.si_status = tsk->exit_code & 0x7f; + if (tsk->exit_code & 0x80) + info.si_code = CLD_DUMPED; + else if (tsk->exit_code & 0x7f) + info.si_code = CLD_KILLED; + else { + info.si_code = CLD_EXITED; + info.si_status = tsk->exit_code >> 8; } - info.si_code = why; - info.si_status = status; psig = tsk->parent->sighand; spin_lock_irqsave(&psig->siglock, flags); - if (sig == SIGCHLD && tsk->state != TASK_STOPPED && + if (sig == SIGCHLD && (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN || (psig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT))) { /* @@ -1503,26 +1542,9 @@ void do_notify_parent(struct task_struct spin_unlock_irqrestore(&psig->siglock, flags); } - -/* - * We need the tasklist lock because it's the only - * thing that protects out "parent" pointer. - * - * exit.c calls "do_notify_parent()" directly, because - * it already has the tasklist lock. - */ -void -notify_parent(struct task_struct *tsk, int sig) -{ - if (sig != -1) { - read_lock(&tasklist_lock); - do_notify_parent(tsk, sig); - read_unlock(&tasklist_lock); - } -} - static void -do_notify_parent_cldstop(struct task_struct *tsk, struct task_struct *parent) +do_notify_parent_cldstop(struct task_struct *tsk, struct task_struct *parent, + int why) { struct siginfo info; unsigned long flags; @@ -1536,9 +1558,22 @@ do_notify_parent_cldstop(struct task_str /* FIXME: find out whether or not this is supposed to be c*time. */ info.si_utime = tsk->utime; info.si_stime = tsk->stime; + k_getrusage(tsk, RUSAGE_BOTH, &info.si_rusage); - info.si_status = tsk->exit_code & 0x7f; - info.si_code = CLD_STOPPED; + info.si_code = why; + switch (why) { + case CLD_CONTINUED: + info.si_status = SIGCONT; + break; + case CLD_STOPPED: + info.si_status = tsk->signal->group_exit_code & 0x7f; + break; + case CLD_TRAPPED: + info.si_status = tsk->exit_code & 0x7f; + break; + default: + BUG(); + } sighand = parent->sighand; spin_lock_irqsave(&sighand->siglock, flags); @@ -1552,6 +1587,68 @@ do_notify_parent_cldstop(struct task_str spin_unlock_irqrestore(&sighand->siglock, flags); } +/* + * This must be called with current->sighand->siglock held. + * + * This should be the path for all ptrace stops. + * We always set current->last_siginfo while stopped here. + * That makes it a way to test a stopped process for + * being ptrace-stopped vs being job-control-stopped. + */ +static void ptrace_stop(int exit_code, siginfo_t *info) +{ + BUG_ON(!(current->ptrace & PT_PTRACED)); + + /* + * If there is a group stop in progress, + * we must participate in the bookkeeping. + */ + if (current->signal->group_stop_count > 0) + --current->signal->group_stop_count; + + current->last_siginfo = info; + current->exit_code = exit_code; + + /* Let the debugger run. */ + set_current_state(TASK_TRACED); + spin_unlock_irq(¤t->sighand->siglock); + read_lock(&tasklist_lock); + do_notify_parent_cldstop(current, current->parent, CLD_TRAPPED); + read_unlock(&tasklist_lock); + schedule(); + + /* + * We are back. Now reacquire the siglock before touching + * last_siginfo, so that we are sure to have synchronized with + * any signal-sending on another CPU that wants to examine it. + */ + spin_lock_irq(¤t->sighand->siglock); + current->last_siginfo = NULL; + + /* + * Queued signals ignored us while we were stopped for tracing. + * So check for any that we should take before resuming user mode. + */ + recalc_sigpending(); +} + +void ptrace_notify(int exit_code) +{ + siginfo_t info; + + BUG_ON((exit_code & (0x7f | ~0xffff)) != SIGTRAP); + + memset(&info, 0, sizeof info); + info.si_signo = SIGTRAP; + info.si_code = exit_code; + info.si_pid = current->pid; + info.si_uid = current->uid; + + /* Let the debugger run. */ + spin_lock_irq(¤t->sighand->siglock); + ptrace_stop(exit_code, &info); + spin_unlock_irq(¤t->sighand->siglock); +} #ifndef HAVE_ARCH_GET_SIGNAL_TO_DELIVER @@ -1565,13 +1662,15 @@ finish_stop(int stop_count) */ if (stop_count < 0 || (current->ptrace & PT_PTRACED)) { read_lock(&tasklist_lock); - do_notify_parent_cldstop(current, current->parent); + do_notify_parent_cldstop(current, current->parent, + CLD_STOPPED); read_unlock(&tasklist_lock); } else if (stop_count == 0) { read_lock(&tasklist_lock); do_notify_parent_cldstop(current->group_leader, - current->group_leader->real_parent); + current->group_leader->real_parent, + CLD_STOPPED); read_unlock(&tasklist_lock); } @@ -1604,14 +1703,17 @@ do_signal_stop(int signr) stop_count = --sig->group_stop_count; current->exit_code = signr; set_current_state(TASK_STOPPED); + if (stop_count == 0) + sig->stop_state = 1; spin_unlock_irq(&sighand->siglock); } else if (thread_group_empty(current)) { /* * Lock must be held through transition to stopped state. */ - current->exit_code = signr; + current->exit_code = current->signal->group_exit_code = signr; set_current_state(TASK_STOPPED); + sig->stop_state = 1; spin_unlock_irq(&sighand->siglock); } else { @@ -1677,6 +1779,8 @@ do_signal_stop(int signr) current->exit_code = signr; set_current_state(TASK_STOPPED); + if (stop_count == 0) + sig->stop_state = 1; spin_unlock_irq(&sighand->siglock); read_unlock(&tasklist_lock); @@ -1717,6 +1821,8 @@ static inline int handle_group_stop(void * without any associated signal being in our queue. */ stop_count = --current->signal->group_stop_count; + if (stop_count == 0) + current->signal->stop_state = 1; current->exit_code = current->signal->group_exit_code; set_current_state(TASK_STOPPED); spin_unlock_irq(¤t->sighand->siglock); @@ -1724,7 +1830,8 @@ static inline int handle_group_stop(void return 1; } -int get_signal_to_deliver(siginfo_t *info, struct pt_regs *regs, void *cookie) +int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka, + struct pt_regs *regs, void *cookie) { sigset_t *mask = ¤t->blocked; int signr = 0; @@ -1746,25 +1853,10 @@ relock: if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { ptrace_signal_deliver(regs, cookie); - /* - * If there is a group stop in progress, - * we must participate in the bookkeeping. - */ - if (current->signal->group_stop_count > 0) - --current->signal->group_stop_count; - /* Let the debugger run. */ - current->exit_code = signr; - current->last_siginfo = info; - set_current_state(TASK_STOPPED); - spin_unlock_irq(¤t->sighand->siglock); - notify_parent(current, SIGCHLD); - schedule(); - - current->last_siginfo = NULL; + ptrace_stop(signr, info); /* We're back. Did the debugger cancel the sig? */ - spin_lock_irq(¤t->sighand->siglock); signr = current->exit_code; if (signr == 0) continue; @@ -1793,8 +1885,15 @@ relock: ka = ¤t->sighand->action[signr-1]; if (ka->sa.sa_handler == SIG_IGN) /* Do nothing. */ continue; - if (ka->sa.sa_handler != SIG_DFL) /* Run the handler. */ + if (ka->sa.sa_handler != SIG_DFL) { + /* Run the handler. */ + *return_ka = *ka; + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + break; /* will return non-zero "signr" value */ + } /* * Now we are doing the default action for this signal. @@ -1888,7 +1987,7 @@ EXPORT_SYMBOL(kill_proc); EXPORT_SYMBOL(kill_proc_info); EXPORT_SYMBOL(kill_sl); EXPORT_SYMBOL(kill_sl_info); -EXPORT_SYMBOL(notify_parent); +EXPORT_SYMBOL(ptrace_notify); EXPORT_SYMBOL(send_sig); EXPORT_SYMBOL(send_sig_info); EXPORT_SYMBOL(send_group_sig_info); @@ -2071,6 +2170,8 @@ int copy_siginfo_to_user(siginfo_t __use err |= __put_user(from->si_status, &to->si_status); err |= __put_user(from->si_utime, &to->si_utime); err |= __put_user(from->si_stime, &to->si_stime); + err |= __copy_to_user(&to->si_rusage, &from->si_rusage, + sizeof(to->si_rusage)); break; case __SI_RT: /* This is not generated by the kernel as of now. */ case __SI_MESGQ: /* But this is */ --- linux-2.6.9-rc1/kernel/sys.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/kernel/sys.c 2004-09-03 00:56:58.618249320 -0700 @@ -17,12 +17,15 @@ #include #include #include +#include +#include #include #include #include #include #include #include +#include #include #include @@ -224,6 +227,7 @@ cond_syscall(sys_acct) cond_syscall(sys_lookup_dcookie) cond_syscall(sys_swapon) cond_syscall(sys_swapoff) +cond_syscall(sys_kexec_load) cond_syscall(sys_init_module) cond_syscall(sys_delete_module) cond_syscall(sys_socketpair) @@ -280,6 +284,12 @@ cond_syscall(compat_get_mempolicy) cond_syscall(sys_pciconfig_read) cond_syscall(sys_pciconfig_write) cond_syscall(sys_pciconfig_iobase) +cond_syscall(sys_perfctr_info) +cond_syscall(sys_vperfctr_open) +cond_syscall(sys_vperfctr_control) +cond_syscall(sys_vperfctr_unlink) +cond_syscall(sys_vperfctr_iresume) +cond_syscall(sys_vperfctr_read) static int set_one_prio(struct task_struct *p, int niceval, int error) { @@ -310,8 +320,6 @@ asmlinkage long sys_setpriority(int whic { struct task_struct *g, *p; struct user_struct *user; - struct pid *pid; - struct list_head *l; int error = -EINVAL; if (which > 2 || which < 0) @@ -336,8 +344,9 @@ asmlinkage long sys_setpriority(int whic case PRIO_PGRP: if (!who) who = process_group(current); - for_each_task_pid(who, PIDTYPE_PGID, p, l, pid) + do_each_task_pid(who, PIDTYPE_PGID, p) { error = set_one_prio(p, niceval, error); + } while_each_task_pid(who, PIDTYPE_PGID, p); break; case PRIO_USER: if (!who) @@ -371,8 +380,6 @@ out: asmlinkage long sys_getpriority(int which, int who) { struct task_struct *g, *p; - struct list_head *l; - struct pid *pid; struct user_struct *user; long niceval, retval = -ESRCH; @@ -394,11 +401,11 @@ asmlinkage long sys_getpriority(int whic case PRIO_PGRP: if (!who) who = process_group(current); - for_each_task_pid(who, PIDTYPE_PGID, p, l, pid) { + do_each_task_pid(who, PIDTYPE_PGID, p) { niceval = 20 - task_nice(p); if (niceval > retval) retval = niceval; - } + } while_each_task_pid(who, PIDTYPE_PGID, p); break; case PRIO_USER: if (!who) @@ -503,6 +510,24 @@ asmlinkage long sys_reboot(int magic1, i machine_restart(buffer); break; +#ifdef CONFIG_KEXEC + case LINUX_REBOOT_CMD_KEXEC: + { + struct kimage *image; + image = xchg(&kexec_image, 0); + if (!image) { + unlock_kernel(); + return -EINVAL; + } + notifier_call_chain(&reboot_notifier_list, SYS_RESTART, NULL); + system_state = SYSTEM_BOOTING; + device_shutdown(); + printk(KERN_EMERG "Starting new kernel\n"); + machine_shutdown(); + machine_kexec(image); + break; + } +#endif #ifdef CONFIG_SOFTWARE_SUSPEND case LINUX_REBOOT_CMD_SW_SUSPEND: { @@ -601,6 +626,7 @@ asmlinkage long sys_setregid(gid_t rgid, current->fsgid = new_egid; current->egid = new_egid; current->gid = new_rgid; + key_fsgid_changed(current); return 0; } @@ -638,6 +664,8 @@ asmlinkage long sys_setgid(gid_t gid) } else return -EPERM; + + key_fsgid_changed(current); return 0; } @@ -726,6 +754,8 @@ asmlinkage long sys_setreuid(uid_t ruid, current->suid = current->euid; current->fsuid = current->euid; + key_fsuid_changed(current); + return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RE); } @@ -771,6 +801,8 @@ asmlinkage long sys_setuid(uid_t uid) current->fsuid = current->euid = uid; current->suid = new_suid; + key_fsuid_changed(current); + return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_ID); } @@ -817,6 +849,8 @@ asmlinkage long sys_setresuid(uid_t ruid if (suid != (uid_t) -1) current->suid = suid; + key_fsuid_changed(current); + return security_task_post_setuid(old_ruid, old_euid, old_suid, LSM_SETID_RES); } @@ -866,6 +900,8 @@ asmlinkage long sys_setresgid(gid_t rgid current->gid = rgid; if (sgid != (gid_t) -1) current->sgid = sgid; + + key_fsgid_changed(current); return 0; } @@ -907,6 +943,8 @@ asmlinkage long sys_setfsuid(uid_t uid) current->fsuid = uid; } + key_fsuid_changed(current); + security_task_post_setuid(old_fsuid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS); return old_fsuid; @@ -933,6 +971,7 @@ asmlinkage long sys_setfsgid(gid_t gid) wmb(); } current->fsgid = gid; + key_fsgid_changed(current); } return old_fsgid; } @@ -947,10 +986,39 @@ asmlinkage long sys_times(struct tms __u */ if (tbuf) { struct tms tmp; - tmp.tms_utime = jiffies_to_clock_t(current->utime); - tmp.tms_stime = jiffies_to_clock_t(current->stime); - tmp.tms_cutime = jiffies_to_clock_t(current->cutime); - tmp.tms_cstime = jiffies_to_clock_t(current->cstime); + struct task_struct *tsk = current; + struct task_struct *t; + unsigned long utime, stime, cutime, cstime; + + read_lock(&tasklist_lock); + utime = tsk->signal->utime; + stime = tsk->signal->stime; + t = tsk; + do { + utime += t->utime; + stime += t->stime; + t = next_thread(t); + } while (t != tsk); + + /* + * While we have tasklist_lock read-locked, no dying thread + * can be updating current->signal->[us]time. Instead, + * we got their counts included in the live thread loop. + * However, another thread can come in right now and + * do a wait call that updates current->signal->c[us]time. + * To make sure we always see that pair updated atomically, + * we take the siglock around fetching them. + */ + spin_lock_irq(&tsk->sighand->siglock); + cutime = tsk->signal->cutime; + cstime = tsk->signal->cstime; + spin_unlock_irq(&tsk->sighand->siglock); + read_unlock(&tasklist_lock); + + tmp.tms_utime = jiffies_to_clock_t(utime); + tmp.tms_stime = jiffies_to_clock_t(stime); + tmp.tms_cutime = jiffies_to_clock_t(cutime); + tmp.tms_cstime = jiffies_to_clock_t(cstime); if (copy_to_user(tbuf, &tmp, sizeof(struct tms))) return -EFAULT; } @@ -1015,12 +1083,11 @@ asmlinkage long sys_setpgid(pid_t pid, p if (pgid != pid) { struct task_struct *p; - struct pid *pid; - struct list_head *l; - for_each_task_pid(pgid, PIDTYPE_PGID, p, l, pid) + do_each_task_pid(pgid, PIDTYPE_PGID, p) { if (p->signal->session == current->signal->session) goto ok_pgid; + } while_each_task_pid(pgid, PIDTYPE_PGID, p); goto out; } @@ -1533,44 +1600,86 @@ asmlinkage long sys_setrlimit(unsigned i * a lot simpler! (Which we're not doing right now because we're not * measuring them yet). * - * This is SMP safe. Either we are called from sys_getrusage on ourselves - * below (we know we aren't going to exit/disappear and only we change our - * rusage counters), or we are called from wait4() on a process which is - * either stopped or zombied. In the zombied case the task won't get - * reaped till shortly after the call to getrusage(), in both cases the - * task being examined is in a frozen state so the counters won't change. + * This expects to be called with tasklist_lock read-locked or better, + * and the siglock not locked. It may momentarily take the siglock. + * + * When sampling multiple threads for RUSAGE_SELF, under SMP we might have + * races with threads incrementing their own counters. But since word + * reads are atomic, we either get new values or old values and we don't + * care which for the sums. We always take the siglock to protect reading + * the c* fields from p->signal from races with exit.c updating those + * fields when reaping, so a sample either gets all the additions of a + * given child after it's reaped, or none so this sample is before reaping. */ -int getrusage(struct task_struct *p, int who, struct rusage __user *ru) + +void k_getrusage(struct task_struct *p, int who, struct rusage *r) { - struct rusage r; + struct task_struct *t; + unsigned long flags; + unsigned long utime, stime; + + memset((char *) r, 0, sizeof *r); + + if (unlikely(!p->signal)) + return; - memset((char *) &r, 0, sizeof(r)); switch (who) { - case RUSAGE_SELF: - jiffies_to_timeval(p->utime, &r.ru_utime); - jiffies_to_timeval(p->stime, &r.ru_stime); - r.ru_nvcsw = p->nvcsw; - r.ru_nivcsw = p->nivcsw; - r.ru_minflt = p->min_flt; - r.ru_majflt = p->maj_flt; - break; case RUSAGE_CHILDREN: - jiffies_to_timeval(p->cutime, &r.ru_utime); - jiffies_to_timeval(p->cstime, &r.ru_stime); - r.ru_nvcsw = p->cnvcsw; - r.ru_nivcsw = p->cnivcsw; - r.ru_minflt = p->cmin_flt; - r.ru_majflt = p->cmaj_flt; + spin_lock_irqsave(&p->sighand->siglock, flags); + utime = p->signal->cutime; + stime = p->signal->cstime; + r->ru_nvcsw = p->signal->cnvcsw; + r->ru_nivcsw = p->signal->cnivcsw; + r->ru_minflt = p->signal->cmin_flt; + r->ru_majflt = p->signal->cmaj_flt; + spin_unlock_irqrestore(&p->sighand->siglock, flags); + jiffies_to_timeval(utime, &r->ru_utime); + jiffies_to_timeval(stime, &r->ru_stime); break; - default: - jiffies_to_timeval(p->utime + p->cutime, &r.ru_utime); - jiffies_to_timeval(p->stime + p->cstime, &r.ru_stime); - r.ru_nvcsw = p->nvcsw + p->cnvcsw; - r.ru_nivcsw = p->nivcsw + p->cnivcsw; - r.ru_minflt = p->min_flt + p->cmin_flt; - r.ru_majflt = p->maj_flt + p->cmaj_flt; + case RUSAGE_SELF: + spin_lock_irqsave(&p->sighand->siglock, flags); + utime = stime = 0; + goto sum_group; + case RUSAGE_BOTH: + spin_lock_irqsave(&p->sighand->siglock, flags); + utime = p->signal->cutime; + stime = p->signal->cstime; + r->ru_nvcsw = p->signal->cnvcsw; + r->ru_nivcsw = p->signal->cnivcsw; + r->ru_minflt = p->signal->cmin_flt; + r->ru_majflt = p->signal->cmaj_flt; + sum_group: + utime += p->signal->utime; + stime += p->signal->stime; + r->ru_nvcsw += p->signal->nvcsw; + r->ru_nivcsw += p->signal->nivcsw; + r->ru_minflt += p->signal->min_flt; + r->ru_majflt += p->signal->maj_flt; + t = p; + do { + utime += t->utime; + stime += t->stime; + r->ru_nvcsw += t->nvcsw; + r->ru_nivcsw += t->nivcsw; + r->ru_minflt += t->min_flt; + r->ru_majflt += t->maj_flt; + t = next_thread(t); + } while (t != p); + spin_unlock_irqrestore(&p->sighand->siglock, flags); + jiffies_to_timeval(utime, &r->ru_utime); + jiffies_to_timeval(stime, &r->ru_stime); break; + default: + BUG(); } +} + +int getrusage(struct task_struct *p, int who, struct rusage __user *ru) +{ + struct rusage r; + read_lock(&tasklist_lock); + k_getrusage(p, who, &r); + read_unlock(&tasklist_lock); return copy_to_user(ru, &r, sizeof(r)) ? -EFAULT : 0; } @@ -1590,7 +1699,7 @@ asmlinkage long sys_umask(int mask) asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) { - int error; + long error; int sig; error = security_task_prctl(option, arg2, arg3, arg4, arg5); @@ -1660,6 +1769,18 @@ asmlinkage long sys_prctl(int option, un } current->keep_capabilities = arg2; break; + + case PR_GET_KEYRING_ID: + error = get_process_keyring_ID((key_serial_t) arg2, arg3); + break; + case PR_JOIN_SESSION_KEYRING: + error = join_session_keyring_user((const char __user *) arg2); + break; + + case KEYCTL_NEW ... __KEYCTL_LAST: + error = sys_keyctl(option, arg2, arg3, arg4, arg5); + break; + default: error = -EINVAL; break; --- linux-2.6.9-rc1/kernel/sysctl.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/kernel/sysctl.c 2004-09-03 00:56:47.276973456 -0700 @@ -42,6 +42,7 @@ #include #include +#include #ifdef CONFIG_ROOT_NFS #include @@ -64,6 +65,9 @@ extern int sysctl_lower_zone_protection; extern int min_free_kbytes; extern int printk_ratelimit_jiffies; extern int printk_ratelimit_burst; +extern int sched_base_timeslice; +extern int sched_min_base; +extern int sched_max_base; #if defined(CONFIG_X86_LOCAL_APIC) && defined(__i386__) int unknown_nmi_panic; @@ -116,12 +120,6 @@ extern int sysctl_userprocess_debug; extern int sysctl_hz_timer; -#if defined(CONFIG_PPC32) && defined(CONFIG_6xx) -extern unsigned long powersave_nap; -int proc_dol2crvec(ctl_table *table, int write, struct file *filp, - void __user *buffer, size_t *lenp, loff_t *ppos); -#endif - #ifdef CONFIG_BSD_PROCESS_ACCT extern int acct_parm[]; #endif @@ -149,6 +147,10 @@ extern ctl_table random_table[]; extern ctl_table pty_table[]; #endif +#ifdef HAVE_ARCH_PICK_MMAP_LAYOUT +int sysctl_legacy_va_layout; +#endif + /* /proc declarations: */ #ifdef CONFIG_PROC_FS @@ -295,7 +297,7 @@ static ctl_table kern_table[] = { .procname = "tainted", .data = &tainted, .maxlen = sizeof(int), - .mode = 0644, + .mode = 0444, .proc_handler = &proc_dointvec, }, { @@ -361,22 +363,6 @@ static ctl_table kern_table[] = { .proc_handler = &proc_dointvec, }, #endif -#if defined(CONFIG_PPC32) && defined(CONFIG_6xx) - { - .ctl_name = KERN_PPC_POWERSAVE_NAP, - .procname = "powersave-nap", - .data = &powersave_nap, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { - .ctl_name = KERN_PPC_L2CR, - .procname = "l2cr", - .mode = 0644, - .proc_handler = &proc_dol2crvec, - }, -#endif { .ctl_name = KERN_CTLALTDEL, .procname = "ctrl-alt-del", @@ -636,6 +622,18 @@ static ctl_table kern_table[] = { .proc_handler = &proc_unknown_nmi_panic, }, #endif + { + .ctl_name = KERN_SCHED_TIMESLICE, + .procname = "base_timeslice", + .data = &sched_base_timeslice, + .maxlen = sizeof (sched_base_timeslice), + .mode = 0644, + .proc_handler = &proc_dointvec_minmax, + .strategy = &sysctl_intvec, + .extra1 = &sched_min_base, + .extra2 = &sched_max_base, + }, + { .ctl_name = 0 } }; @@ -805,6 +803,18 @@ static ctl_table vm_table[] = { .strategy = &sysctl_intvec, .extra1 = &zero, }, +#ifdef HAVE_ARCH_PICK_MMAP_LAYOUT + { + .ctl_name = VM_LEGACY_VA_LAYOUT, + .procname = "legacy_va_layout", + .data = &sysctl_legacy_va_layout, + .maxlen = sizeof(sysctl_legacy_va_layout), + .mode = 0644, + .proc_handler = &proc_dointvec, + .strategy = &sysctl_intvec, + .extra1 = &zero, + }, +#endif { .ctl_name = 0 } }; @@ -915,6 +925,7 @@ static ctl_table fs_table[] = { .mode = 0644, .proc_handler = &proc_dointvec, }, + { .ctl_name = 0 } }; --- linux-2.6.9-rc1/kernel/timer.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/kernel/timer.c 2004-09-03 00:57:22.711586576 -0700 @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -233,6 +234,8 @@ void add_timer_on(struct timer_list *tim spin_unlock_irqrestore(&base->lock, flags); } +EXPORT_SYMBOL(add_timer_on); + /*** * mod_timer - modify a timer's timeout * @timer: the timer to be modified @@ -619,6 +622,9 @@ static void second_overflow(void) if (xtime.tv_sec % 86400 == 0) { xtime.tv_sec--; wall_to_monotonic.tv_sec++; + /* The timer interpolator will make time change gradually instead + * of an immediate jump by one second. + */ time_interpolator_update(-NSEC_PER_SEC); time_state = TIME_OOP; clock_was_set(); @@ -630,6 +636,7 @@ static void second_overflow(void) if ((xtime.tv_sec + 1) % 86400 == 0) { xtime.tv_sec++; wall_to_monotonic.tv_sec--; + /* Use of time interpolator for a gradual change of time */ time_interpolator_update(NSEC_PER_SEC); time_state = TIME_WAIT; clock_was_set(); @@ -835,6 +842,7 @@ static void update_one_process(struct ta do_process_times(p, user, system); do_it_virt(p, user); do_it_prof(p); + perfctr_sample_thread(&p->thread); } /* @@ -1425,10 +1433,6 @@ void __init init_timers(void) } #ifdef CONFIG_TIME_INTERPOLATION -volatile unsigned long last_nsec_offset; -#ifndef __HAVE_ARCH_CMPXCHG -spinlock_t last_nsec_offset_lock = SPIN_LOCK_UNLOCKED; -#endif struct time_interpolator *time_interpolator; static struct time_interpolator *time_interpolator_list; @@ -1439,18 +1443,24 @@ is_better_time_interpolator(struct time_ { if (!time_interpolator) return 1; - return new->frequency > 2*time_interpolator->frequency || + return new->frequency > 2 * time_interpolator->frequency || (unsigned long)new->drift < (unsigned long)time_interpolator->drift; } void register_time_interpolator(struct time_interpolator *ti) { + unsigned long flags; + + ti->nsec_per_cyc = (NSEC_PER_SEC << ti->shift) / ti->frequency; spin_lock(&time_interpolator_lock); - write_seqlock_irq(&xtime_lock); + write_seqlock_irqsave(&xtime_lock, flags); if (is_better_time_interpolator(ti)) + { time_interpolator = ti; - write_sequnlock_irq(&xtime_lock); + time_interpolator_reset(); + } + write_sequnlock_irqrestore(&xtime_lock, flags); ti->next = time_interpolator_list; time_interpolator_list = ti; @@ -1461,6 +1471,7 @@ void unregister_time_interpolator(struct time_interpolator *ti) { struct time_interpolator *curr, **prev; + unsigned long flags; spin_lock(&time_interpolator_lock); prev = &time_interpolator_list; @@ -1472,7 +1483,7 @@ unregister_time_interpolator(struct time prev = &curr->next; } - write_seqlock_irq(&xtime_lock); + write_seqlock_irqsave(&xtime_lock, flags); if (ti == time_interpolator) { /* we lost the best time-interpolator: */ time_interpolator = NULL; @@ -1480,8 +1491,9 @@ unregister_time_interpolator(struct time for (curr = time_interpolator_list; curr; curr = curr->next) if (is_better_time_interpolator(curr)) time_interpolator = curr; + time_interpolator_reset(); } - write_sequnlock_irq(&xtime_lock); + write_sequnlock_irqrestore(&xtime_lock, flags); spin_unlock(&time_interpolator_lock); } #endif /* CONFIG_TIME_INTERPOLATION */ @@ -1502,3 +1514,19 @@ void msleep(unsigned int msecs) EXPORT_SYMBOL(msleep); +/** + * msleep_interruptible - sleep waiting for waitqueue interruptions + * @msecs: Time in milliseconds to sleep for + */ +unsigned long msleep_interruptible(unsigned int msecs) +{ + unsigned long timeout = msecs_to_jiffies(msecs); + + while (timeout && !signal_pending(current)) { + set_current_state(TASK_INTERRUPTIBLE); + timeout = schedule_timeout(timeout); + } + return jiffies_to_msecs(timeout); +} + +EXPORT_SYMBOL(msleep_interruptible); --- linux-2.6.9-rc1/kernel/user.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/kernel/user.c 2004-09-03 00:56:54.700844856 -0700 @@ -12,6 +12,7 @@ #include #include #include +#include /* * UID task count cache, to get fast user lookup in "alloc_uid" @@ -34,6 +35,10 @@ struct user_struct root_user = { .sigpending = ATOMIC_INIT(0), .mq_bytes = 0, .locked_shm = 0, +#ifdef CONFIG_KEYS + .uid_keyring = &root_user_keyring, + .session_keyring = &root_session_keyring, +#endif }; /* @@ -87,6 +92,8 @@ void free_uid(struct user_struct *up) { if (up && atomic_dec_and_lock(&up->__count, &uidhash_lock)) { uid_hash_remove(up); + key_put(up->uid_keyring); + key_put(up->session_keyring); kmem_cache_free(uid_cachep, up); spin_unlock(&uidhash_lock); } @@ -116,6 +123,11 @@ struct user_struct * alloc_uid(uid_t uid new->mq_bytes = 0; new->locked_shm = 0; + if (alloc_uid_keyring(new) < 0) { + kmem_cache_free(uid_cachep, new); + return NULL; + } + /* * Before adding this, check whether we raced * on adding the same user already.. @@ -123,6 +135,8 @@ struct user_struct * alloc_uid(uid_t uid spin_lock(&uidhash_lock); up = uid_hash_find(uid, hashent); if (up) { + key_put(new->uid_keyring); + key_put(new->session_keyring); kmem_cache_free(uid_cachep, new); } else { uid_hash_insert(new, hashent); @@ -146,8 +160,10 @@ void switch_uid(struct user_struct *new_ old_user = current->user; atomic_inc(&new_user->processes); atomic_dec(&old_user->processes); + switch_uid_keyring(new_user); current->user = new_user; free_uid(old_user); + suid_keys(current); } --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/kernel/wait.c 2004-09-03 00:57:14.839783272 -0700 @@ -0,0 +1,244 @@ +/* + * Generic waiting primitives. + * + * (C) 2004 William Irwin, Oracle + */ +#include +#include +#include +#include +#include +#include +#include + +void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) +{ + unsigned long flags; + + wait->flags &= ~WQ_FLAG_EXCLUSIVE; + spin_lock_irqsave(&q->lock, flags); + __add_wait_queue(q, wait); + spin_unlock_irqrestore(&q->lock, flags); +} +EXPORT_SYMBOL(add_wait_queue); + +void fastcall add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait) +{ + unsigned long flags; + + wait->flags |= WQ_FLAG_EXCLUSIVE; + spin_lock_irqsave(&q->lock, flags); + __add_wait_queue_tail(q, wait); + spin_unlock_irqrestore(&q->lock, flags); +} +EXPORT_SYMBOL(add_wait_queue_exclusive); + +void fastcall remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) +{ + unsigned long flags; + + spin_lock_irqsave(&q->lock, flags); + __remove_wait_queue(q, wait); + spin_unlock_irqrestore(&q->lock, flags); +} +EXPORT_SYMBOL(remove_wait_queue); + + +/* + * Note: we use "set_current_state()" _after_ the wait-queue add, + * because we need a memory barrier there on SMP, so that any + * wake-function that tests for the wait-queue being active + * will be guaranteed to see waitqueue addition _or_ subsequent + * tests in this thread will see the wakeup having taken place. + * + * The spin_unlock() itself is semi-permeable and only protects + * one way (it only protects stuff inside the critical region and + * stops them from bleeding out - it would still allow subsequent + * loads to move into the the critical region). + */ +void fastcall +prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state) +{ + unsigned long flags; + + wait->flags &= ~WQ_FLAG_EXCLUSIVE; + spin_lock_irqsave(&q->lock, flags); + if (list_empty(&wait->task_list)) + __add_wait_queue(q, wait); + /* + * don't alter the task state if this is just going to + * queue an async wait queue callback + */ + if (is_sync_wait(wait)) + set_current_state(state); + spin_unlock_irqrestore(&q->lock, flags); +} +EXPORT_SYMBOL(prepare_to_wait); + +void fastcall +prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *wait, int state) +{ + unsigned long flags; + + wait->flags |= WQ_FLAG_EXCLUSIVE; + spin_lock_irqsave(&q->lock, flags); + if (list_empty(&wait->task_list)) + __add_wait_queue_tail(q, wait); + /* + * don't alter the task state if this is just going to + * queue an async wait queue callback + */ + if (is_sync_wait(wait)) + set_current_state(state); + spin_unlock_irqrestore(&q->lock, flags); +} +EXPORT_SYMBOL(prepare_to_wait_exclusive); + +void fastcall finish_wait(wait_queue_head_t *q, wait_queue_t *wait) +{ + unsigned long flags; + + __set_current_state(TASK_RUNNING); + /* + * We can check for list emptiness outside the lock + * IFF: + * - we use the "careful" check that verifies both + * the next and prev pointers, so that there cannot + * be any half-pending updates in progress on other + * CPU's that we haven't seen yet (and that might + * still change the stack area. + * and + * - all other users take the lock (ie we can only + * have _one_ other CPU that looks at or modifies + * the list). + */ + if (!list_empty_careful(&wait->task_list)) { + spin_lock_irqsave(&q->lock, flags); + list_del_init(&wait->task_list); + spin_unlock_irqrestore(&q->lock, flags); + } +} +EXPORT_SYMBOL(finish_wait); + +int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key) +{ + int ret = default_wake_function(wait, mode, sync, key); + + if (ret) + list_del_init(&wait->task_list); + return ret; +} +EXPORT_SYMBOL(autoremove_wake_function); + +int wake_bit_function(wait_queue_t *wait, unsigned mode, int sync, void *arg) +{ + struct wait_bit_key *key = arg; + struct wait_bit_queue *wait_bit + = container_of(wait, struct wait_bit_queue, wait); + + if (wait_bit->key.flags != key->flags || + wait_bit->key.bit_nr != key->bit_nr || + test_bit(key->bit_nr, key->flags)) + return 0; + else + return autoremove_wake_function(wait, mode, sync, key); +} +EXPORT_SYMBOL(wake_bit_function); + +/* + * To allow interruptible waiting and asynchronous (i.e. nonblocking) + * waiting, the actions of __wait_on_bit() and __wait_on_bit_lock() are + * permitted return codes. Nonzero return codes halt waiting and return. + */ +int __sched fastcall +__wait_on_bit(wait_queue_head_t *wq, struct wait_bit_queue *q, + int (*action)(void *), unsigned mode) +{ + int ret = 0; + + prepare_to_wait(wq, &q->wait, mode); + if (test_bit(q->key.bit_nr, q->key.flags)) + ret = (*action)(q->key.flags); + finish_wait(wq, &q->wait); + return ret; +} +EXPORT_SYMBOL(__wait_on_bit); + +int __sched fastcall out_of_line_wait_on_bit(void *word, int bit, + int (*action)(void *), unsigned mode) +{ + wait_queue_head_t *wq = bit_waitqueue(word, bit); + DEFINE_WAIT_BIT(wait, word, bit); + + return __wait_on_bit(wq, &wait, action, mode); +} +EXPORT_SYMBOL(out_of_line_wait_on_bit); + +int __sched fastcall +__wait_on_bit_lock(wait_queue_head_t *wq, struct wait_bit_queue *q, + int (*action)(void *), unsigned mode) +{ + int ret = 0; + + do { + prepare_to_wait_exclusive(wq, &q->wait, mode); + if (test_bit(q->key.bit_nr, q->key.flags)) { + if ((ret = (*action)(q->key.flags))) + break; + } + } while (test_and_set_bit(q->key.bit_nr, q->key.flags)); + finish_wait(wq, &q->wait); + return ret; +} +EXPORT_SYMBOL(__wait_on_bit_lock); + +int __sched fastcall out_of_line_wait_on_bit_lock(void *word, int bit, + int (*action)(void *), unsigned mode) +{ + wait_queue_head_t *wq = bit_waitqueue(word, bit); + DEFINE_WAIT_BIT(wait, word, bit); + + return __wait_on_bit_lock(wq, &wait, action, mode); +} +EXPORT_SYMBOL(out_of_line_wait_on_bit_lock); + +void fastcall __wake_up_bit(wait_queue_head_t *wq, void *word, int bit) +{ + struct wait_bit_key key = __WAIT_BIT_KEY_INITIALIZER(word, bit); + if (waitqueue_active(wq)) + __wake_up(wq, TASK_INTERRUPTIBLE|TASK_UNINTERRUPTIBLE, 1, &key); +} +EXPORT_SYMBOL(__wake_up_bit); + +/** + * wake_up_bit - wake up a waiter on a bit + * @word: the word being waited on, a kernel virtual address + * @bit: the bit of the word being waited on + * + * There is a standard hashed waitqueue table for generic use. This + * is the part of the hashtable's accessor API that wakes up waiters + * on a bit. For instance, if one were to have waiters on a bitflag, + * one would call wake_up_bit() after clearing the bit. + * + * In order for this to function properly, as it uses waitqueue_active() + * internally, some kind of memory barrier must be done prior to calling + * this. Typically, this will be smp_mb__after_clear_bit(), but in some + * cases where bitflags are manipulated non-atomically under a lock, one + * may need to use a less regular barrier, such fs/inode.c's smp_mb(), + * because spin_unlock() does not guarantee a memory barrier. + */ +void fastcall wake_up_bit(void *word, int bit) +{ + __wake_up_bit(bit_waitqueue(word, bit), word, bit); +} +EXPORT_SYMBOL(wake_up_bit); + +fastcall wait_queue_head_t *bit_waitqueue(void *word, int bit) +{ + const int shift = BITS_PER_LONG == 32 ? 5 : 6; + const struct zone *zone = page_zone(virt_to_page(word)); + unsigned long val = (unsigned long)word << shift | bit; + + return &zone->wait_table[hash_long(val, zone->wait_table_bits)]; +} +EXPORT_SYMBOL(bit_waitqueue); --- linux-2.6.9-rc1/lib/bitmap.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/lib/bitmap.c 2004-09-03 00:56:59.743078320 -0700 @@ -291,6 +291,7 @@ EXPORT_SYMBOL(__bitmap_weight); #define nbits_to_hold_value(val) fls(val) #define roundup_power2(val,modulus) (((val) + (modulus) - 1) & ~((modulus) - 1)) #define unhex(c) (isdigit(c) ? (c - '0') : (toupper(c) - 'A' + 10)) +#define BASEDEC 10 /* fancier cpuset lists input in decimal */ /** * bitmap_scnprintf - convert bitmap to an ASCII hex string. @@ -408,3 +409,187 @@ int bitmap_parse(const char __user *ubuf return 0; } EXPORT_SYMBOL(bitmap_parse); + +/* + * bscnl_emit(buf, buflen, rbot, rtop, bp) + * + * Helper routine for bitmap_scnlistprintf(). Write decimal number + * or range to buf, suppressing output past buf+buflen, with optional + * comma-prefix. Return len of what would be written to buf, if it + * all fit. + */ +static inline int bscnl_emit(char *buf, int buflen, int rbot, int rtop, int len) +{ + if (len > 0) + len += scnprintf(buf + len, buflen - len, ","); + if (rbot == rtop) + len += scnprintf(buf + len, buflen - len, "%d", rbot); + else + len += scnprintf(buf + len, buflen - len, "%d-%d", rbot, rtop); + return len; +} + +/** + * bitmap_scnlistprintf - convert bitmap to list format ASCII string + * @buf: byte buffer into which string is placed + * @buflen: reserved size of @buf, in bytes + * @maskp: pointer to bitmap to convert + * @nmaskbits: size of bitmap, in bits + * + * Output format is a comma-separated list of decimal numbers and + * ranges. Consecutively set bits are shown as two hyphen-separated + * decimal numbers, the smallest and largest bit numbers set in + * the range. Output format is compatible with the format + * accepted as input by bitmap_parselist(). + * + * The return value is the number of characters which would be + * generated for the given input, excluding the trailing '\0', as + * per ISO C99. + */ +int bitmap_scnlistprintf(char *buf, unsigned int buflen, + const unsigned long *maskp, int nmaskbits) +{ + int len = 0; + /* current bit is 'cur', most recently seen range is [rbot, rtop] */ + int cur, rbot, rtop; + + rbot = cur = find_first_bit(maskp, nmaskbits); + while (cur < nmaskbits) { + rtop = cur; + cur = find_next_bit(maskp, nmaskbits, cur+1); + if (cur >= nmaskbits || cur > rtop + 1) { + len = bscnl_emit(buf, buflen, rbot, rtop, len); + rbot = cur; + } + } + return len; +} +EXPORT_SYMBOL(bitmap_scnlistprintf); + +/** + * bitmap_parselist - convert list format ASCII string to bitmap + * @buf: read nul-terminated user string from this buffer + * @mask: write resulting mask here + * @nmaskbits: number of bits in mask to be written + * + * Input format is a comma-separated list of decimal numbers and + * ranges. Consecutively set bits are shown as two hyphen-separated + * decimal numbers, the smallest and largest bit numbers set in + * the range. + * + * Returns 0 on success, -errno on invalid input strings: + * -EINVAL: second number in range smaller than first + * -EINVAL: invalid character in string + * -ERANGE: bit number specified too large for mask + */ +int bitmap_parselist(const char *bp, unsigned long *maskp, int nmaskbits) +{ + unsigned a, b; + + bitmap_zero(maskp, nmaskbits); + do { + if (!isdigit(*bp)) + return -EINVAL; + b = a = simple_strtoul(bp, (char **)&bp, BASEDEC); + if (*bp == '-') { + bp++; + if (!isdigit(*bp)) + return -EINVAL; + b = simple_strtoul(bp, (char **)&bp, BASEDEC); + } + if (!(a <= b)) + return -EINVAL; + if (b >= nmaskbits) + return -ERANGE; + while (a <= b) { + set_bit(a, maskp); + a++; + } + if (*bp == ',') + bp++; + } while (*bp != '\0' && *bp != '\n'); + return 0; +} +EXPORT_SYMBOL(bitmap_parselist); + +/** + * bitmap_find_free_region - find a contiguous aligned mem region + * @bitmap: an array of unsigned longs corresponding to the bitmap + * @bits: number of bits in the bitmap + * @order: region size to find (size is actually 1< BITS_PER_LONG) + return -EINVAL; + + /* make a mask of the order */ + mask = (1ul << (pages - 1)); + mask += mask - 1; + + /* run up the bitmap pages bits at a time */ + for (i = 0; i < bits; i += pages) { + int index = i/BITS_PER_LONG; + int offset = i - (index * BITS_PER_LONG); + if((bitmap[index] & (mask << offset)) == 0) { + /* set region in bimap */ + bitmap[index] |= (mask << offset); + return i; + } + } + return -ENOMEM; +} +EXPORT_SYMBOL(bitmap_find_free_region); + +/** + * bitmap_release_region - release allocated bitmap region + * @bitmap: a pointer to the bitmap + * @pos: the beginning of the region + * @order: the order of the bits to release (number is 1< BITS_PER_LONG. The + * algorithm would be a simple look for multiple zeros in the + * array, but there's no driver today that needs this. If you + * trip this BUG(), you get to code it... */ + BUG_ON(pages > BITS_PER_LONG); + mask += mask - 1; + if (bitmap[index] & (mask << offset)) + return -EBUSY; + bitmap[index] |= (mask << offset); + return 0; +} +EXPORT_SYMBOL(bitmap_allocate_region); --- linux-2.6.9-rc1/lib/Kconfig.debug 2004-08-24 00:03:30.000000000 -0700 +++ 25/lib/Kconfig.debug 2004-09-03 00:56:38.245346472 -0700 @@ -47,7 +47,7 @@ config DEBUG_SPINLOCK config DEBUG_SPINLOCK_SLEEP bool "Sleep-inside-spinlock checking" - depends on DEBUG_KERNEL && (X86 || IA64 || MIPS || PPC32 || ARCH_S390 || SPARC32 || SPARC64) + depends on DEBUG_KERNEL && (X86 || IA64 || MIPS || PPC32 || PPC64 || ARCH_S390 || SPARC32 || SPARC64) help If you say Y here, various routines which may sleep will become very noisy if they are called with a spinlock held. --- linux-2.6.9-rc1/lib/kobject.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/lib/kobject.c 2004-09-02 20:51:53.000000000 -0700 @@ -58,14 +58,11 @@ static int create_dir(struct kobject * k return error; } - static inline struct kobject * to_kobj(struct list_head * entry) { return container_of(entry,struct kobject,entry); } - -#ifdef CONFIG_HOTPLUG static int get_kobj_path_length(struct kset *kset, struct kobject *kobj) { int length = 1; @@ -98,6 +95,31 @@ static void fill_kobj_path(struct kset * pr_debug("%s: path = '%s'\n",__FUNCTION__,path); } +/** + * kobject_get_path - generate and return the path associated with a given kobj + * and kset pair. The result must be freed by the caller with kfree(). + * + * @kset: kset in question, with which to build the path + * @kobj: kobject in question, with which to build the path + * @gfp_mask: the allocation type used to allocate the path + */ +char * kobject_get_path(struct kset *kset, struct kobject *kobj, int gfp_mask) +{ + char *path; + int len; + + len = get_kobj_path_length(kset, kobj); + path = kmalloc(len, gfp_mask); + if (!path) + return NULL; + memset(path, 0x00, len); + fill_kobj_path(kset, kobj, path, len); + + return path; +} + +#ifdef CONFIG_HOTPLUG + #define BUFFER_SIZE 1024 /* should be enough memory for the env */ #define NUM_ENVP 32 /* number of env pointers */ static unsigned long sequence_num; @@ -112,7 +134,6 @@ static void kset_hotplug(const char *act char *scratch; int i = 0; int retval; - int kobj_path_length; char *kobj_path = NULL; char *name = NULL; unsigned long seq; @@ -163,12 +184,9 @@ static void kset_hotplug(const char *act envp [i++] = scratch; scratch += sprintf(scratch, "SEQNUM=%ld", seq) + 1; - kobj_path_length = get_kobj_path_length (kset, kobj); - kobj_path = kmalloc (kobj_path_length, GFP_KERNEL); + kobj_path = kobject_get_path(kset, kobj, GFP_KERNEL); if (!kobj_path) goto exit; - memset (kobj_path, 0x00, kobj_path_length); - fill_kobj_path (kset, kobj, kobj_path, kobj_path_length); envp [i++] = scratch; scratch += sprintf (scratch, "DEVPATH=%s", kobj_path) + 1; @@ -225,10 +243,9 @@ void kobject_hotplug(const char *action, * kobject_init - initialize object. * @kobj: object in question. */ - void kobject_init(struct kobject * kobj) { - atomic_set(&kobj->refcount,1); + kref_init(&kobj->kref); INIT_LIST_HEAD(&kobj->entry); kobj->kset = kset_get(kobj->kset); } @@ -325,7 +342,7 @@ int kobject_register(struct kobject * ko * @kobj: object. * @name: name. * - * If strlen(name) < KOBJ_NAME_LEN, then use a dynamically allocated + * If strlen(name) >= KOBJ_NAME_LEN, then use a dynamically allocated * string that @kobj->k_name points to. Otherwise, use the static * @kobj->name array. */ @@ -429,10 +446,8 @@ void kobject_unregister(struct kobject * struct kobject * kobject_get(struct kobject * kobj) { - if (kobj) { - WARN_ON(!atomic_read(&kobj->refcount)); - atomic_inc(&kobj->refcount); - } + if (kobj) + kref_get(&kobj->kref); return kobj; } @@ -459,17 +474,21 @@ void kobject_cleanup(struct kobject * ko kobject_put(parent); } +static void kobject_release(struct kref *kref) +{ + kobject_cleanup(container_of(kref, struct kobject, kref)); +} + /** * kobject_put - decrement refcount for object. * @kobj: object. * * Decrement the refcount, and if 0, call kobject_cleanup(). */ - void kobject_put(struct kobject * kobj) { - if (atomic_dec_and_test(&kobj->refcount)) - kobject_cleanup(kobj); + if (kobj) + kref_put(&kobj->kref, kobject_release); } @@ -626,7 +645,7 @@ void subsys_remove_file(struct subsystem } } - +EXPORT_SYMBOL(kobject_get_path); EXPORT_SYMBOL(kobject_init); EXPORT_SYMBOL(kobject_register); EXPORT_SYMBOL(kobject_unregister); --- linux-2.6.9-rc1/lib/kref.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/lib/kref.c 2004-09-02 20:51:53.000000000 -0700 @@ -11,48 +11,45 @@ * */ -/* #define DEBUG */ - #include #include /** * kref_init - initialize object. * @kref: object in question. - * @release: pointer to a function that will clean up the object - * when the last reference to the object is released. - * This pointer is required. */ -void kref_init(struct kref *kref, void (*release)(struct kref *kref)) +void kref_init(struct kref *kref) { - WARN_ON(release == NULL); atomic_set(&kref->refcount,1); - kref->release = release; } /** * kref_get - increment refcount for object. * @kref: object. */ -struct kref *kref_get(struct kref *kref) +void kref_get(struct kref *kref) { WARN_ON(!atomic_read(&kref->refcount)); atomic_inc(&kref->refcount); - return kref; } /** * kref_put - decrement refcount for object. * @kref: object. + * @release: pointer to the function that will clean up the object when the + * last reference to the object is released. + * This pointer is required, and it is not acceptable to pass kfree + * in as this function. * - * Decrement the refcount, and if 0, call kref->release(). + * Decrement the refcount, and if 0, call release(). */ -void kref_put(struct kref *kref) +void kref_put(struct kref *kref, void (*release) (struct kref *kref)) { - if (atomic_dec_and_test(&kref->refcount)) { - pr_debug("kref cleaning up\n"); - kref->release(kref); - } + WARN_ON(release == NULL); + WARN_ON(release == (void (*)(struct kref *))kfree); + + if (atomic_dec_and_test(&kref->refcount)) + release(kref); } EXPORT_SYMBOL(kref_init); --- linux-2.6.9-rc1/lib/Makefile 2004-08-24 00:01:53.000000000 -0700 +++ 25/lib/Makefile 2004-09-02 20:51:53.000000000 -0700 @@ -5,12 +5,9 @@ lib-y := errno.o ctype.o string.o vsprintf.o cmdline.o \ bust_spinlocks.o rbtree.o radix-tree.o dump_stack.o \ - kobject.o idr.o div64.o parser.o int_sqrt.o \ + kobject.o kref.o idr.o div64.o parser.o int_sqrt.o \ bitmap.o extable.o -# hack for now till some static code uses krefs, then it can move up above... -obj-y += kref.o - lib-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o --- linux-2.6.9-rc1/lib/radix-tree.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/lib/radix-tree.c 2004-09-03 00:57:03.077571400 -0700 @@ -36,7 +36,7 @@ #else #define RADIX_TREE_MAP_SHIFT 3 /* For more stressful testing */ #endif -#define RADIX_TREE_TAGS 2 +#define RADIX_TREE_TAGS 3 #define RADIX_TREE_MAP_SIZE (1UL << RADIX_TREE_MAP_SHIFT) #define RADIX_TREE_MAP_MASK (RADIX_TREE_MAP_SIZE-1) @@ -133,6 +133,7 @@ int radix_tree_preload(int gfp_mask) out: return ret; } +EXPORT_SYMBOL(radix_tree_preload); static inline void tag_set(struct radix_tree_node *node, int tag, int offset) { @@ -276,14 +277,8 @@ int radix_tree_insert(struct radix_tree_ } EXPORT_SYMBOL(radix_tree_insert); -/** - * radix_tree_lookup - perform lookup operation on a radix tree - * @root: radix tree root - * @index: index key - * - * Lookup the item at the position @index in the radix tree @root. - */ -void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index) +static inline void **__lookup_slot(struct radix_tree_root *root, + unsigned long index) { unsigned int height, shift; struct radix_tree_node **slot; @@ -306,7 +301,36 @@ void *radix_tree_lookup(struct radix_tre height--; } - return *slot; + return (void **)slot; +} + +/** + * radix_tree_lookup_slot - lookup a slot in a radix tree + * @root: radix tree root + * @index: index key + * + * Lookup the slot corresponding to the position @index in the radix tree + * @root. This is useful for update-if-exists operations. + */ +void **radix_tree_lookup_slot(struct radix_tree_root *root, unsigned long index) +{ + return __lookup_slot(root, index); +} +EXPORT_SYMBOL(radix_tree_lookup_slot); + +/** + * radix_tree_lookup - perform lookup operation on a radix tree + * @root: radix tree root + * @index: index key + * + * Lookup the item at the position @index in the radix tree @root. + */ +void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index) +{ + void **slot; + + slot = __lookup_slot(root, index); + return slot != NULL ? *slot : NULL; } EXPORT_SYMBOL(radix_tree_lookup); --- linux-2.6.9-rc1/lib/zlib_inflate/inflate.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/lib/zlib_inflate/inflate.c 2004-09-02 20:51:53.000000000 -0700 @@ -53,8 +53,6 @@ int zlib_inflateInit2_( return Z_VERSION_ERROR; /* initialize state */ - if (z == NULL) - return Z_STREAM_ERROR; z->msg = NULL; z->state = &WS(z)->internal_state; z->state->blocks = NULL; --- linux-2.6.9-rc1/MAINTAINERS 2004-08-24 00:02:58.000000000 -0700 +++ 25/MAINTAINERS 2004-09-03 00:57:25.541156416 -0700 @@ -169,7 +169,7 @@ ACI MIXER DRIVER P: Robert Siemer M: Robert.Siemer@gmx.de L: linux-sound@vger.kernel.org -W: http://www.uni-karlsruhe.de/~Robert.Siemer/Private/ +W: http://www.stud.uni-karlsruhe.de/~uh1b/ S: Maintained ACP/MWAVE MODEM @@ -195,8 +195,14 @@ S: Maintained AD1816 SOUND DRIVER P: Thorsten Knabe -W: http://www.student.informatik.tu-darmstadt.de/~tek/projects/linux.html -W: http://www.tu-darmstadt.de/~tek01/projects/linux.html +M: Thorsten Knabe +W: http://linux.thorsten-knabe.de +S: Maintained + +ADM1025 HARDWARE MONITOR DRIVER +P: Jean Delvare +M: khali@linux-fr.org +L: sensors@stimpy.netroedge.com S: Maintained ADT746X FAN DRIVER @@ -563,7 +569,7 @@ CRYPTO API P: James Morris M: jmorris@redhat.com P: David S. Miller -M: davem@redhat.com +M: davem@davemloft.net W http://samba.org/~jamesm/crypto/ L: linux-kernel@vger.kernel.org S: Maintained @@ -621,6 +627,8 @@ DC390/AM53C974 SCSI driver P: Kurt Garloff M: garloff@suse.de W: http://www.garloff.de/kurt/linux/dc390/ +P: Guennadi Liakhovetski +M: g.liakhovetski@gmx.de S: Maintained DECnet NETWORK LAYER @@ -648,8 +656,9 @@ W: http://sources.redhat.com/dm S: Maintained DEVICE NUMBER REGISTRY -P: John Cagle +P: Torben Mathiasen M: device@lanana.org +W: http://lanana.org/docs/device-list/index.html L: linux-kernel@vger.kernel.org S: Maintained @@ -762,8 +771,8 @@ M: saw@saw.sw.com.sg S: Maintained EMU10K1 SOUND DRIVER -P: Rui Sousa -M: rui.p.m.sousa@clix.pt +P: James Courtier-Dutton +M: James@superbug.demon.co.uk L: emu10k1-devel@lists.sourceforge.net W: http://sourceforge.net/projects/emu10k1/ S: Maintained @@ -827,10 +836,8 @@ M: viro@math.psu.edu S: Maintained FIRMWARE LOADER (request_firmware) -P: Manuel Estrada Sainz -M: ranty@debian.org L: linux-kernel@vger.kernel.org -S: Maintained +S: Orphan FPU EMULATOR P: Bill Metzenthen @@ -952,6 +959,12 @@ L: sensors@stimpy.netroedge.com W: http://www.lm-sensors.nu/ S: Maintained +I2O +P: Markus Lidel +M: markus.lidel@shadowconnect.com +W: http://i2o.shadowconnect.com/ +S: Maintained + i386 BOOT CODE P: Riley H. Williams M: Riley@Williams.Name @@ -1065,6 +1078,13 @@ M: lethal@chaoticdreams.org L: linux-fbdev-devel@lists.sourceforge.net S: Maintained +INPUT (KEYBOARD, MOUSE, JOYSTICK) DRIVERS +P: Vojtech Pavlik +M: vojtech@suse.cz +L: linux-input@atrey.karlin.mff.cuni.cz +L: linux-joystick@atrey.karlin.mff.cuni.cz +S: Maintained + INTEL 810/815 FRAMEBUFFER DRIVER P: Antonino Daplas M: adaplas@pol.net @@ -1189,13 +1209,6 @@ L: jfs-discussion@oss.software.ibm.com W: http://oss.software.ibm.com/jfs/ S: Supported -JOYSTICK DRIVER -P: Vojtech Pavlik -M: vojtech@suse.cz -L: linux-joystick@atrey.karlin.mff.cuni.cz -W: http://www.suse.cz/development/joystick/ -S: Maintained - KCONFIG P: Roman Zippel M: zippel@linux-m68k.org @@ -1229,6 +1242,12 @@ W: http://sf.net/projects/kernel-janitor W: http://developer.osdl.org/rddunlap/kj-patches/ S: Maintained +KGDB FOR I386 PLATFORM +P: George Anzinger +M: george@mvista.com +L: linux-net@vger.kernel.org +S: Supported + KERNEL NFSD P: Neil Brown M: neilb@cse.unsw.edu.au @@ -1237,6 +1256,17 @@ W: http://nfs.sourceforge.net/ W: http://www.cse.unsw.edu.au/~neilb/patches/linux-devel/ S: Maintained +KEXEC +P: Eric Biederman +P: Randy Dunlap +M: ebiederm@xmission.com +M: rddunlap@osdl.org +W: http://www.xmission.com/~ebiederm/files/kexec/ +W: http://developer.osdl.org/rddunlap/kexec/ +L: linux-kernel@vger.kernel.org +L: fastboot@osdl.org +S: Maintained + LANMEDIA WAN CARD DRIVER P: Andrew Stanley-Jones M: asj@lanmedia.com @@ -1277,13 +1307,14 @@ S: Maintained LINUX FOR POWERPC P: Paul Mackerras M: paulus@samba.org -W: http://www.fsmlabs.com/linuxppcbk.html +W: http://www.penguinppc.org/ +L: linuxppc-dev@lists.linuxppc.org S: Supported LINUX FOR POWER MACINTOSH P: Benjamin Herrenschmidt M: benh@kernel.crashing.org -W: http://www.linuxppc.org/ +W: http://www.penguinppc.org/ L: linuxppc-dev@lists.linuxppc.org S: Maintained @@ -1314,9 +1345,11 @@ M: acme@conectiva.com.br S: Maintained LINUX FOR 64BIT POWERPC -P: David Engebretsen (stable kernel) -M: engebret@us.ibm.com -P: Anton Blanchard (development kernel) +P: Paul Mackerras +M: paulus@samba.org +M: paulus@au.ibm.com +P: Anton Blanchard +M: anton@samba.org M: anton@au.ibm.com W: http://linuxppc64.org L: linuxppc64-dev@lists.linuxppc.org @@ -1329,6 +1362,18 @@ L: linux-security-module@wirex.com W: http://lsm.immunix.org S: Supported +LM83 HARDWARE MONITOR DRIVER +P: Jean Delvare +M: khali@linux-fr.org +L: sensors@stimpy.netroedge.com +S: Maintained + +LM90 HARDWARE MONITOR DRIVER +P: Jean Delvare +M: khali@linux-fr.org +L: sensors@stimpy.netroedge.com +S: Maintained + LOGICAL DISK MANAGER SUPPORT (LDM, Windows 2000/XP Dynamic Disks) P: Richard Russon (FlatCap) M: ldm@flatcap.org @@ -1506,7 +1551,7 @@ S: Maintained NETWORKING [IPv4/IPv6] P: David S. Miller -M: davem@redhat.com +M: davem@davemloft.net P: Alexey Kuznetsov M: kuznet@ms2.inr.ac.ru P: Pekka Savola (ipv6) @@ -1613,14 +1658,14 @@ P: David Campbell M: campbell@torque.net P: Andrea Arcangeli M: andrea@e-mind.com -L: linux-parport@torque.net +L: linux-parport@lists.infradead.org W: http://people.redhat.com/twaugh/parport/ S: Maintained PARIDE DRIVERS FOR PARALLEL PORT IDE DEVICES P: Tim Waugh M: tim@cyberelk.net -L: linux-parport@torque.net +L: linux-parport@lists.infradead.org W: http://www.torque.net/linux-pp.html S: Maintained @@ -1686,6 +1731,12 @@ M: george@mvista.com L: linux-net@vger.kernel.org S: Supported +PERFORMANCE-MONITORING COUNTERS DRIVER +P: Mikael Pettersson +M: mikpe@csd.uu.se +W: http://www.csd.uu.se/~mikpe/linux/perfctr/ +S: Maintained + PNP SUPPORT P: Adam Belay M: ambx1@neo.rr.com @@ -1908,6 +1959,12 @@ M: thomas@winischhofer.net W: http://www.winischhofer.net/linuxsisvga.shtml S: Maintained +SMSC47M1 HARDWARE MONITOR DRIVER +P: Jean Delvare +M: khali@linux-fr.org +L: sensors@stimpy.netroedge.com +S: Odd Fixes + SMB FILESYSTEM P: Urban Widmark M: urban@teststation.com @@ -1958,7 +2015,7 @@ S: Maintained UltraSPARC (sparc64): P: David S. Miller -M: davem@redhat.com +M: davem@davemloft.net P: Eddie C. Dost M: ecd@skynet.be P: Jakub Jelinek @@ -2178,12 +2235,11 @@ M: dbrownell@users.sourceforge.net L: linux-usb-devel@lists.sourceforge.net S: Maintained -USB HID/HIDBP/INPUT DRIVERS +USB HID/HIDBP DRIVERS P: Vojtech Pavlik M: vojtech@suse.cz L: linux-usb-users@lists.sourceforge.net L: linux-usb-devel@lists.sourceforge.net -W: http://www.suse.cz/development/input/ S: Maintained USB HUB DRIVER @@ -2412,6 +2468,12 @@ M: johnpol@2ka.mipt.ru L: sensors@stimpy.netroedge.com S: Maintained +W83L785TS HARDWARE MONITOR DRIVER +P: Jean Delvare +M: khali@linux-fr.org +L: sensors@stimpy.netroedge.com +S: Odd Fixes + WAN ROUTER & SANGOMA WANPIPE DRIVERS & API (X.25, FRAME RELAY, PPP, CISCO HDLC) P: Nenad Corbic M: ncorbic@sangoma.com --- linux-2.6.9-rc1/Makefile 2004-08-24 00:03:12.000000000 -0700 +++ 25/Makefile 2004-09-03 00:56:36.981538600 -0700 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 9 -EXTRAVERSION = -rc1 +EXTRAVERSION = -rc1-mm3 NAME=Zonked Quokka # *DOCUMENTATION* @@ -279,8 +279,8 @@ cc-option-yn = $(shell if $(CC) $(CFLAGS # cc-version # Usage gcc-ver := $(call cc-version $(CC)) -cc-version = $(CONFIG_SHELL) $(srctree)/scripts/gcc-version.sh \ - $(if $(1), $(1), $(CC)) +cc-version = $(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-version.sh \ + $(if $(1), $(1), $(CC))) # Look for make include files relative to root of kernel src @@ -483,6 +483,7 @@ endif ifdef CONFIG_DEBUG_INFO CFLAGS += -g +AFLAGS += -g endif # warn about C99 declaration after statement --- linux-2.6.9-rc1/mm/bootmem.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/mm/bootmem.c 2004-09-02 20:51:53.000000000 -0700 @@ -259,6 +259,7 @@ static unsigned long __init free_all_boo unsigned long i, count, total = 0; unsigned long idx; unsigned long *map; + int gofast = 0; BUG_ON(!bdata->node_bootmem_map); @@ -267,14 +268,32 @@ static unsigned long __init free_all_boo page = virt_to_page(phys_to_virt(bdata->node_boot_start)); idx = bdata->node_low_pfn - (bdata->node_boot_start >> PAGE_SHIFT); map = bdata->node_bootmem_map; + /* Check physaddr is O(LOG2(BITS_PER_LONG)) page aligned */ + if (bdata->node_boot_start == 0 || + ffs(bdata->node_boot_start) - PAGE_SHIFT > ffs(BITS_PER_LONG)) + gofast = 1; for (i = 0; i < idx; ) { unsigned long v = ~map[i / BITS_PER_LONG]; - if (v) { + if (gofast && v == ~0UL) { + int j; + + count += BITS_PER_LONG; + __ClearPageReserved(page); + set_page_count(page, 1); + for (j = 1; j < BITS_PER_LONG; j++) { + if (j + 16 < BITS_PER_LONG) + prefetchw(page + j + 16); + __ClearPageReserved(page + j); + } + __free_pages(page, ffs(BITS_PER_LONG)-1); + i += BITS_PER_LONG; + page += BITS_PER_LONG; + } else if (v) { unsigned long m; for (m = 1; m && i < idx; m<<=1, page++, i++) { if (v & m) { count++; - ClearPageReserved(page); + __ClearPageReserved(page); set_page_count(page, 1); __free_page(page); } @@ -294,7 +313,7 @@ static unsigned long __init free_all_boo count = 0; for (i = 0; i < ((bdata->node_low_pfn-(bdata->node_boot_start >> PAGE_SHIFT))/8 + PAGE_SIZE-1)/PAGE_SIZE; i++,page++) { count++; - ClearPageReserved(page); + __ClearPageReserved(page); set_page_count(page, 1); __free_page(page); } --- linux-2.6.9-rc1/mm/filemap.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/mm/filemap.c 2004-09-03 00:57:25.018235912 -0700 @@ -60,7 +60,6 @@ * ->swap_list_lock * ->swap_device_lock (exclusive_swap_page, others) * ->mapping->tree_lock - * ->page_map_lock() (try_to_unmap_file) * * ->i_sem * ->i_mmap_lock (truncate->unmap_mapping_range) @@ -83,16 +82,20 @@ * ->sb_lock (fs/fs-writeback.c) * ->mapping->tree_lock (__sync_single_inode) * + * ->i_mmap_lock + * ->anon_vma.lock (vma_adjust) + * + * ->anon_vma.lock + * ->page_table_lock (anon_vma_prepare and various) + * * ->page_table_lock * ->swap_device_lock (try_to_unmap_one) * ->private_lock (try_to_unmap_one) * ->tree_lock (try_to_unmap_one) * ->zone.lru_lock (follow_page->mark_page_accessed) - * ->page_map_lock() (page_add_anon_rmap) - * ->tree_lock (page_remove_rmap->set_page_dirty) - * ->private_lock (page_remove_rmap->set_page_dirty) - * ->inode_lock (page_remove_rmap->set_page_dirty) - * ->anon_vma.lock (anon_vma_prepare) + * ->private_lock (page_remove_rmap->set_page_dirty) + * ->tree_lock (page_remove_rmap->set_page_dirty) + * ->inode_lock (page_remove_rmap->set_page_dirty) * ->inode_lock (zap_pte_range->set_page_dirty) * ->private_lock (zap_pte_range->__set_page_dirty_buffers) * @@ -114,6 +117,7 @@ void __remove_from_page_cache(struct pag mapping->nrpages--; pagecache_acct(-1); } +EXPORT_SYMBOL(__remove_from_page_cache); void remove_from_page_cache(struct page *page) { @@ -122,14 +126,18 @@ void remove_from_page_cache(struct page if (unlikely(!PageLocked(page))) PAGE_BUG(page); - spin_lock_irq(&mapping->tree_lock); + write_lock_irq(&mapping->tree_lock); __remove_from_page_cache(page); - spin_unlock_irq(&mapping->tree_lock); + write_unlock_irq(&mapping->tree_lock); } +EXPORT_SYMBOL(remove_from_page_cache); -static inline int sync_page(struct page *page) +static int sync_page(void *word) { struct address_space *mapping; + struct page *page; + + page = container_of((page_flags_t *)word, struct page, flags); /* * FIXME, fercrissake. What is this barrier here for? @@ -137,7 +145,8 @@ static inline int sync_page(struct page smp_mb(); mapping = page_mapping(page); if (mapping && mapping->a_ops && mapping->a_ops->sync_page) - return mapping->a_ops->sync_page(page); + mapping->a_ops->sync_page(page); + io_schedule(); return 0; } @@ -274,6 +283,7 @@ int sync_page_range(struct inode *inode, ret = wait_on_page_writeback_range(mapping, start, end); return ret; } +EXPORT_SYMBOL(sync_page_range); /** * filemap_fdatawait - walk the list of under-writeback pages of the given @@ -283,7 +293,13 @@ int sync_page_range(struct inode *inode, */ int filemap_fdatawait(struct address_space *mapping) { - return wait_on_page_writeback_range(mapping, 0, -1); + loff_t i_size = i_size_read(mapping->host); + + if (i_size == 0) + return 0; + + return wait_on_page_writeback_range(mapping, 0, + (i_size - 1) >> PAGE_CACHE_SHIFT); } EXPORT_SYMBOL(filemap_fdatawait); @@ -312,7 +328,7 @@ int add_to_page_cache(struct page *page, int error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM); if (error == 0) { - spin_lock_irq(&mapping->tree_lock); + write_lock_irq(&mapping->tree_lock); error = radix_tree_insert(&mapping->page_tree, offset, page); if (!error) { page_cache_get(page); @@ -322,7 +338,7 @@ int add_to_page_cache(struct page *page, mapping->nrpages++; pagecache_acct(1); } - spin_unlock_irq(&mapping->tree_lock); + write_unlock_irq(&mapping->tree_lock); radix_tree_preload_end(); } return error; @@ -338,6 +354,7 @@ int add_to_page_cache_lru(struct page *p lru_cache_add(page); return ret; } +EXPORT_SYMBOL(add_to_page_cache_lru); /* * In order to wait for pages to become available there must be @@ -349,40 +366,6 @@ int add_to_page_cache_lru(struct page *p * at a cost of "thundering herd" phenomena during rare hash * collisions. */ -struct page_wait_queue { - struct page *page; - int bit; - wait_queue_t wait; -}; - -static int page_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key) -{ - struct page *page = key; - struct page_wait_queue *wq; - - wq = container_of(wait, struct page_wait_queue, wait); - if (wq->page != page || test_bit(wq->bit, &page->flags)) - return 0; - else - return autoremove_wake_function(wait, mode, sync, NULL); -} - -#define __DEFINE_PAGE_WAIT(name, p, b, f) \ - struct page_wait_queue name = { \ - .page = p, \ - .bit = b, \ - .wait = { \ - .task = current, \ - .func = page_wake_function, \ - .flags = f, \ - .task_list = LIST_HEAD_INIT(name.wait.task_list),\ - }, \ - } - -#define DEFINE_PAGE_WAIT(name, p, b) __DEFINE_PAGE_WAIT(name, p, b, 0) -#define DEFINE_PAGE_WAIT_EXCLUSIVE(name, p, b) \ - __DEFINE_PAGE_WAIT(name, p, b, WQ_FLAG_EXCLUSIVE) - static wait_queue_head_t *page_waitqueue(struct page *page) { const struct zone *zone = page_zone(page); @@ -390,30 +373,19 @@ static wait_queue_head_t *page_waitqueue return &zone->wait_table[hash_ptr(page, zone->wait_table_bits)]; } -static void wake_up_page(struct page *page) +static inline void wake_up_page(struct page *page, int bit) { - const unsigned int mode = TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE; - wait_queue_head_t *waitqueue = page_waitqueue(page); - - if (waitqueue_active(waitqueue)) - __wake_up(waitqueue, mode, 1, page); + __wake_up_bit(page_waitqueue(page), &page->flags, bit); } void fastcall wait_on_page_bit(struct page *page, int bit_nr) { - wait_queue_head_t *waitqueue = page_waitqueue(page); - DEFINE_PAGE_WAIT(wait, page, bit_nr); + DEFINE_WAIT_BIT(wait, &page->flags, bit_nr); - do { - prepare_to_wait(waitqueue, &wait.wait, TASK_UNINTERRUPTIBLE); - if (test_bit(bit_nr, &page->flags)) { - sync_page(page); - io_schedule(); - } - } while (test_bit(bit_nr, &page->flags)); - finish_wait(waitqueue, &wait.wait); + if (test_bit(bit_nr, &page->flags)) + __wait_on_bit(page_waitqueue(page), &wait, sync_page, + TASK_UNINTERRUPTIBLE); } - EXPORT_SYMBOL(wait_on_page_bit); /** @@ -437,7 +409,7 @@ void fastcall unlock_page(struct page *p if (!TestClearPageLocked(page)) BUG(); smp_mb__after_clear_bit(); - wake_up_page(page); + wake_up_page(page, PG_locked); } EXPORT_SYMBOL(unlock_page); @@ -453,7 +425,7 @@ void end_page_writeback(struct page *pag BUG(); smp_mb__after_clear_bit(); } - wake_up_page(page); + wake_up_page(page, PG_writeback); } EXPORT_SYMBOL(end_page_writeback); @@ -468,19 +440,11 @@ EXPORT_SYMBOL(end_page_writeback); */ void fastcall __lock_page(struct page *page) { - wait_queue_head_t *wqh = page_waitqueue(page); - DEFINE_PAGE_WAIT_EXCLUSIVE(wait, page, PG_locked); + DEFINE_WAIT_BIT(wait, &page->flags, PG_locked); - while (TestSetPageLocked(page)) { - prepare_to_wait_exclusive(wqh, &wait.wait, TASK_UNINTERRUPTIBLE); - if (PageLocked(page)) { - sync_page(page); - io_schedule(); - } - } - finish_wait(wqh, &wait.wait); + __wait_on_bit_lock(page_waitqueue(page), &wait, sync_page, + TASK_UNINTERRUPTIBLE); } - EXPORT_SYMBOL(__lock_page); /* @@ -491,11 +455,11 @@ struct page * find_get_page(struct addre { struct page *page; - spin_lock_irq(&mapping->tree_lock); + read_lock_irq(&mapping->tree_lock); page = radix_tree_lookup(&mapping->page_tree, offset); if (page) page_cache_get(page); - spin_unlock_irq(&mapping->tree_lock); + read_unlock_irq(&mapping->tree_lock); return page; } @@ -508,11 +472,11 @@ struct page *find_trylock_page(struct ad { struct page *page; - spin_lock_irq(&mapping->tree_lock); + read_lock_irq(&mapping->tree_lock); page = radix_tree_lookup(&mapping->page_tree, offset); if (page && TestSetPageLocked(page)) page = NULL; - spin_unlock_irq(&mapping->tree_lock); + read_unlock_irq(&mapping->tree_lock); return page; } @@ -534,15 +498,15 @@ struct page *find_lock_page(struct addre { struct page *page; - spin_lock_irq(&mapping->tree_lock); + read_lock_irq(&mapping->tree_lock); repeat: page = radix_tree_lookup(&mapping->page_tree, offset); if (page) { page_cache_get(page); if (TestSetPageLocked(page)) { - spin_unlock_irq(&mapping->tree_lock); + read_unlock_irq(&mapping->tree_lock); lock_page(page); - spin_lock_irq(&mapping->tree_lock); + read_lock_irq(&mapping->tree_lock); /* Has the page been truncated while we slept? */ if (page->mapping != mapping || page->index != offset) { @@ -552,7 +516,7 @@ repeat: } } } - spin_unlock_irq(&mapping->tree_lock); + read_unlock_irq(&mapping->tree_lock); return page; } @@ -626,12 +590,12 @@ unsigned find_get_pages(struct address_s unsigned int i; unsigned int ret; - spin_lock_irq(&mapping->tree_lock); + read_lock_irq(&mapping->tree_lock); ret = radix_tree_gang_lookup(&mapping->page_tree, (void **)pages, start, nr_pages); for (i = 0; i < ret; i++) page_cache_get(pages[i]); - spin_unlock_irq(&mapping->tree_lock); + read_unlock_irq(&mapping->tree_lock); return ret; } @@ -645,14 +609,14 @@ unsigned find_get_pages_tag(struct addre unsigned int i; unsigned int ret; - spin_lock_irq(&mapping->tree_lock); + read_lock_irq(&mapping->tree_lock); ret = radix_tree_gang_lookup_tag(&mapping->page_tree, (void **)pages, *index, nr_pages, tag); for (i = 0; i < ret; i++) page_cache_get(pages[i]); if (ret) *index = pages[ret - 1]->index + 1; - spin_unlock_irq(&mapping->tree_lock); + read_unlock_irq(&mapping->tree_lock); return ret; } @@ -695,13 +659,15 @@ EXPORT_SYMBOL(grab_cache_page_nowait); * * This is really ugly. But the goto's actually try to clarify some * of the logic when it comes to error handling etc. - * - note the struct file * is only passed for the use of readpage + * + * Note the struct file* is only passed for the use of readpage. It may be + * NULL. */ void do_generic_mapping_read(struct address_space *mapping, struct file_ra_state *_ra, - struct file * filp, + struct file *filp, loff_t *ppos, - read_descriptor_t * desc, + read_descriptor_t *desc, read_actor_t actor) { struct inode *inode = mapping->host; @@ -716,7 +682,10 @@ void do_generic_mapping_read(struct addr offset = *ppos & ~PAGE_CACHE_MASK; isize = i_size_read(inode); - end_index = isize >> PAGE_CACHE_SHIFT; + if (!isize) + goto out; + + end_index = (isize - 1) >> PAGE_CACHE_SHIFT; if (index > end_index) goto out; @@ -724,6 +693,16 @@ void do_generic_mapping_read(struct addr struct page *page; unsigned long nr, ret; + /* nr is the maximum number of bytes to copy from this page */ + nr = PAGE_CACHE_SIZE; + if (index == end_index) { + nr = ((isize - 1) & ~PAGE_CACHE_MASK) + 1; + if (nr <= offset) { + goto out; + } + } + nr = nr - offset; + cond_resched(); page_cache_readahead(mapping, &ra, filp, index); @@ -736,16 +715,6 @@ find_page: if (!PageUptodate(page)) goto page_not_up_to_date; page_ok: - /* nr is the maximum number of bytes to copy from this page */ - nr = PAGE_CACHE_SIZE; - if (index == end_index) { - nr = isize & ~PAGE_CACHE_MASK; - if (nr <= offset) { - page_cache_release(page); - goto out; - } - } - nr = nr - offset; /* If users can be writing to this page using arbitrary * virtual addresses, take care about potential aliasing @@ -821,11 +790,22 @@ readpage: * another truncate extends the file - this is desired though). */ isize = i_size_read(inode); - end_index = isize >> PAGE_CACHE_SHIFT; - if (index > end_index) { + end_index = (isize - 1) >> PAGE_CACHE_SHIFT; + if (unlikely(!isize || index > end_index)) { page_cache_release(page); goto out; } + + /* nr is the maximum number of bytes to copy from this page */ + nr = PAGE_CACHE_SIZE; + if (index == end_index) { + nr = ((isize - 1) & ~PAGE_CACHE_MASK) + 1; + if (nr <= offset) { + page_cache_release(page); + goto out; + } + } + nr = nr - offset; goto page_ok; readpage_error: @@ -865,7 +845,8 @@ out: *ppos = ((loff_t) index << PAGE_CACHE_SHIFT) + offset; if (cached_page) page_cache_release(cached_page); - file_accessed(filp); + if (filp) + file_accessed(filp); } EXPORT_SYMBOL(do_generic_mapping_read); @@ -885,7 +866,8 @@ int file_read_actor(read_descriptor_t *d */ if (!fault_in_pages_writeable(desc->arg.buf, size)) { kaddr = kmap_atomic(page, KM_USER0); - left = __copy_to_user(desc->arg.buf, kaddr + offset, size); + left = __copy_to_user_inatomic(desc->arg.buf, + kaddr + offset, size); kunmap_atomic(kaddr, KM_USER0); if (left == 0) goto success; @@ -1483,7 +1465,7 @@ repeat: return 0; } -static struct vm_operations_struct generic_file_vm_ops = { +struct vm_operations_struct generic_file_vm_ops = { .nopage = filemap_nopage, .populate = filemap_populate, }; @@ -1682,7 +1664,7 @@ filemap_copy_from_user(struct page *page int left; kaddr = kmap_atomic(page, KM_USER0); - left = __copy_from_user(kaddr + offset, buf, bytes); + left = __copy_from_user_inatomic(kaddr + offset, buf, bytes); kunmap_atomic(kaddr, KM_USER0); if (left != 0) { @@ -1705,7 +1687,7 @@ __filemap_copy_from_user_iovec(char *vad int copy = min(bytes, iov->iov_len - base); base = 0; - left = __copy_from_user(vaddr, buf, copy); + left = __copy_from_user_inatomic(vaddr, buf, copy); copied += copy; bytes -= copy; vaddr += copy; --- linux-2.6.9-rc1/mm/hugetlb.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/mm/hugetlb.c 2004-09-03 00:57:25.665137568 -0700 @@ -21,7 +21,7 @@ static spinlock_t hugetlb_lock = SPIN_LO static void enqueue_huge_page(struct page *page) { - int nid = page_zone(page)->zone_pgdat->node_id; + int nid = page_to_nid(page); list_add(&page->lru, &hugepage_freelists[nid]); free_huge_pages++; free_huge_pages_node[nid]++; @@ -52,7 +52,7 @@ static struct page *alloc_fresh_huge_pag { static int nid = 0; struct page *page; - page = alloc_pages_node(nid, GFP_HIGHUSER|__GFP_COMP, + page = alloc_pages_node(nid, GFP_HIGHUSER|__GFP_COMP|__GFP_NOWARN, HUGETLB_PAGE_ORDER); nid = (nid + 1) % numnodes; if (page) { @@ -123,6 +123,7 @@ static int __init hugetlb_setup(char *s) } __setup("hugepages=", hugetlb_setup); +#ifdef CONFIG_SYSCTL static void update_and_free_page(struct page *page) { int i; @@ -188,7 +189,6 @@ static unsigned long set_max_huge_pages( return nr_huge_pages; } -#ifdef CONFIG_SYSCTL int hugetlb_sysctl_handler(struct ctl_table *table, int write, struct file *file, void __user *buffer, size_t *length, loff_t *ppos) --- linux-2.6.9-rc1/mm/Makefile 2004-08-24 00:02:58.000000000 -0700 +++ 25/mm/Makefile 2004-09-02 20:51:53.000000000 -0700 @@ -5,7 +5,7 @@ mmu-y := nommu.o mmu-$(CONFIG_MMU) := fremap.o highmem.o madvise.o memory.o mincore.o \ mlock.o mmap.o mprotect.o mremap.o msync.o rmap.o \ - shmem.o vmalloc.o + vmalloc.o obj-y := bootmem.o filemap.o mempool.o oom_kill.o fadvise.o \ page_alloc.o page-writeback.o pdflush.o prio_tree.o \ @@ -15,3 +15,6 @@ obj-y := bootmem.o filemap.o mempool.o obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o thrash.o obj-$(CONFIG_HUGETLBFS) += hugetlb.o obj-$(CONFIG_NUMA) += mempolicy.o +obj-$(CONFIG_SHMEM) += shmem.o +obj-$(CONFIG_TINY_SHMEM) += tiny-shmem.o + --- linux-2.6.9-rc1/mm/memory.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/mm/memory.c 2004-09-03 00:56:42.158751544 -0700 @@ -705,6 +705,7 @@ int get_user_pages(struct task_struct *t struct page **pages, struct vm_area_struct **vmas) { int i; + int vm_io; unsigned int flags; /* @@ -753,9 +754,11 @@ int get_user_pages(struct task_struct *t continue; } - if (!vma || (pages && (vma->vm_flags & VM_IO)) - || !(flags & vma->vm_flags)) - return i ? : -EFAULT; + if (!vma) + return i ? i : -EFAULT; + vm_io = vma->vm_flags & VM_IO; + if ((pages && vm_io) || !(flags & vma->vm_flags)) + return i ? i : -EFAULT; if (is_vm_hugetlb_page(vma)) { i = follow_hugetlb_page(mm, vma, pages, vmas, @@ -764,8 +767,15 @@ int get_user_pages(struct task_struct *t } spin_lock(&mm->page_table_lock); do { - struct page *map; + struct page *map = NULL; int lookup_write = write; + + /* + * We don't follow pagetables for VM_IO regions - they + * may have no pageframes. + */ + if (vm_io) + goto no_follow; while (!(map = follow_page(mm, start, lookup_write))) { /* * Shortcut for anonymous pages. We don't want @@ -817,6 +827,7 @@ int get_user_pages(struct task_struct *t if (!PageReserved(pages[i])) page_cache_get(pages[i]); } +no_follow: if (vmas) vmas[i] = vma; i++; @@ -1744,6 +1755,8 @@ int make_pages_present(unsigned long add struct vm_area_struct * vma; vma = find_vma(current->mm, addr); + if (!vma) + return -1; write = (vma->vm_flags & VM_WRITE) != 0; if (addr >= end) BUG(); --- linux-2.6.9-rc1/mm/mempolicy.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/mm/mempolicy.c 2004-09-03 00:57:00.326989552 -0700 @@ -66,6 +66,8 @@ #include #include #include +#include +#include #include #include #include @@ -95,7 +97,7 @@ static int nodes_online(unsigned long *n { DECLARE_BITMAP(online2, MAX_NUMNODES); - bitmap_copy(online2, node_online_map, MAX_NUMNODES); + bitmap_copy(online2, nodes_addr(node_online_map), MAX_NUMNODES); if (bitmap_empty(online2, MAX_NUMNODES)) set_bit(0, online2); if (!bitmap_subset(nodes, online2, MAX_NUMNODES)) @@ -163,6 +165,10 @@ static int get_nodes(unsigned long *node if (copy_from_user(nodes, nmask, nlongs*sizeof(unsigned long))) return -EFAULT; nodes[nlongs-1] &= endmask; + /* Update current mems_allowed */ + cpuset_update_current_mems_allowed(); + /* Ignore nodes not set in current->mems_allowed */ + cpuset_restrict_to_mems_allowed(nodes); return mpol_check_policy(mode, nodes); } @@ -422,7 +428,7 @@ static void get_zonemask(struct mempolic case MPOL_PREFERRED: /* or use current node instead of online map? */ if (p->v.preferred_node < 0) - bitmap_copy(nodes, node_online_map, MAX_NUMNODES); + bitmap_copy(nodes, nodes_addr(node_online_map), MAX_NUMNODES); else __set_bit(p->v.preferred_node, nodes); break; @@ -438,7 +444,7 @@ static int lookup_node(struct mm_struct err = get_user_pages(current, mm, addr & PAGE_MASK, 1, 0, 0, &p, NULL); if (err >= 0) { - err = page_zone(p)->zone_pgdat->node_id; + err = page_to_nid(p); put_page(p); } return err; @@ -573,8 +579,10 @@ static struct zonelist *zonelist_policy( break; case MPOL_BIND: /* Lower zones don't get a policy applied */ + /* Careful: current->mems_allowed might have moved */ if (gfp >= policy_zone) - return policy->v.zonelist; + if (cpuset_zonelist_valid_mems_allowed(policy->v.zonelist)) + return policy->v.zonelist; /*FALL THROUGH*/ case MPOL_INTERLEAVE: /* should not happen */ case MPOL_DEFAULT: @@ -628,7 +636,7 @@ static struct page *alloc_page_interleav struct zonelist *zl; struct page *page; - BUG_ON(!test_bit(nid, node_online_map)); + BUG_ON(!node_online(nid)); zl = NODE_DATA(nid)->node_zonelists + (gfp & GFP_ZONEMASK); page = __alloc_pages(gfp, order, zl); if (page && page_zone(page) == zl->zones[0]) { @@ -665,6 +673,8 @@ alloc_page_vma(unsigned gfp, struct vm_a { struct mempolicy *pol = get_vma_policy(vma, addr); + cpuset_update_current_mems_allowed(); + if (unlikely(pol->policy == MPOL_INTERLEAVE)) { unsigned nid; if (vma) { @@ -702,6 +712,8 @@ struct page *alloc_pages_current(unsigne { struct mempolicy *pol = current->mempolicy; + if (!in_interrupt()) + cpuset_update_current_mems_allowed(); if (!pol || in_interrupt()) pol = &default_policy; if (pol->policy == MPOL_INTERLEAVE) @@ -1017,7 +1029,8 @@ void __init numa_policy_init(void) /* Set interleaving policy for system init. This way not all the data structures allocated at system boot end up in node zero. */ - if (sys_set_mempolicy(MPOL_INTERLEAVE, node_online_map, MAX_NUMNODES) < 0) + if (sys_set_mempolicy(MPOL_INTERLEAVE, nodes_addr(node_online_map), + MAX_NUMNODES) < 0) printk("numa_policy_init: interleaving failed\n"); } --- linux-2.6.9-rc1/mm/mempool.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/mm/mempool.c 2004-09-02 20:51:53.000000000 -0700 @@ -194,6 +194,7 @@ void * mempool_alloc(mempool_t *pool, in DEFINE_WAIT(wait); int gfp_nowait = gfp_mask & ~(__GFP_WAIT | __GFP_IO); + might_sleep_if(gfp_mask & __GFP_WAIT); repeat_alloc: element = pool->alloc(gfp_nowait|__GFP_NOWARN, pool->pool_data); if (likely(element != NULL)) --- linux-2.6.9-rc1/mm/mmap.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/mm/mmap.c 2004-09-03 00:56:38.638286736 -0700 @@ -90,6 +90,7 @@ static void remove_vm_struct(struct vm_a { struct file *file = vma->vm_file; + might_sleep(); if (file) { struct address_space *mapping = file->f_mapping; spin_lock(&mapping->i_mmap_lock); @@ -728,6 +729,32 @@ none: return NULL; } +#ifdef CONFIG_PROC_FS +void __vm_stat_account(struct mm_struct *mm, unsigned long flags, + struct file *file, long pages) +{ + const unsigned long stack_flags + = VM_STACK_FLAGS & (VM_GROWSUP|VM_GROWSDOWN); + +#ifdef CONFIG_HUGETLB + if (flags & VM_HUGETLB) { + if (!(flags & VM_DONTCOPY)) + mm->shared_vm += pages; + return; + } +#endif /* CONFIG_HUGETLB */ + + if (file) + mm->shared_vm += pages; + else if (flags & stack_flags) + mm->stack_vm += pages; + if (flags & VM_EXEC) + mm->exec_vm += pages; + if (flags & (VM_RESERVED|VM_IO)) + mm->reserved_vm += pages; +} +#endif /* CONFIG_PROC_FS */ + /* * The caller must hold down_write(current->mm->mmap_sem). */ @@ -986,6 +1013,7 @@ out: pgoff, flags & MAP_NONBLOCK); down_write(&mm->mmap_sem); } + __vm_stat_account(mm, vm_flags, file, len >> PAGE_SHIFT); return addr; unmap_and_free_vma: @@ -1018,7 +1046,7 @@ EXPORT_SYMBOL(do_mmap_pgoff); * This function "knows" that -ENOMEM has the bits set. */ #ifndef HAVE_ARCH_UNMAPPED_AREA -static inline unsigned long +unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) { @@ -1062,12 +1090,118 @@ full_search: addr = vma->vm_end; } } -#else -extern unsigned long -arch_get_unmapped_area(struct file *, unsigned long, unsigned long, - unsigned long, unsigned long); #endif +void arch_unmap_area(struct vm_area_struct *area) +{ + /* + * Is this a new hole at the lowest possible address? + */ + if (area->vm_start >= TASK_UNMAPPED_BASE && + area->vm_start < area->vm_mm->free_area_cache) + area->vm_mm->free_area_cache = area->vm_start; +} + +/* + * This mmap-allocator allocates new areas top-down from below the + * stack's low limit (the base): + */ +#ifndef HAVE_ARCH_UNMAPPED_AREA_TOPDOWN +unsigned long +arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0, + const unsigned long len, const unsigned long pgoff, + const unsigned long flags) +{ + struct vm_area_struct *vma, *prev_vma; + struct mm_struct *mm = current->mm; + unsigned long base = mm->mmap_base, addr = addr0; + int first_time = 1; + + /* requested length too big for entire address space */ + if (len > TASK_SIZE) + return -ENOMEM; + + /* dont allow allocations above current base */ + if (mm->free_area_cache > base) + mm->free_area_cache = base; + + /* requesting a specific address */ + if (addr) { + addr = PAGE_ALIGN(addr); + vma = find_vma(mm, addr); + if (TASK_SIZE - len >= addr && + (!vma || addr + len <= vma->vm_start)) + return addr; + } + +try_again: + /* make sure it can fit in the remaining address space */ + if (mm->free_area_cache < len) + goto fail; + + /* either no address requested or cant fit in requested address hole */ + addr = (mm->free_area_cache - len) & PAGE_MASK; + do { + /* + * Lookup failure means no vma is above this address, + * i.e. return with success: + */ + if (!(vma = find_vma_prev(mm, addr, &prev_vma))) + return addr; + + /* + * new region fits between prev_vma->vm_end and + * vma->vm_start, use it: + */ + if (addr+len <= vma->vm_start && + (!prev_vma || (addr >= prev_vma->vm_end))) + /* remember the address as a hint for next time */ + return (mm->free_area_cache = addr); + else + /* pull free_area_cache down to the first hole */ + if (mm->free_area_cache == vma->vm_end) + mm->free_area_cache = vma->vm_start; + + /* try just below the current vma->vm_start */ + addr = vma->vm_start-len; + } while (len <= vma->vm_start); + +fail: + /* + * if hint left us with no space for the requested + * mapping then try again: + */ + if (first_time) { + mm->free_area_cache = base; + first_time = 0; + goto try_again; + } + /* + * A failed mmap() very likely causes application failure, + * so fall back to the bottom-up function here. This scenario + * can happen with large stack limits and large mmap() + * allocations. + */ + mm->free_area_cache = TASK_UNMAPPED_BASE; + addr = arch_get_unmapped_area(filp, addr0, len, pgoff, flags); + /* + * Restore the topdown base: + */ + mm->free_area_cache = base; + + return addr; +} +#endif + +void arch_unmap_area_topdown(struct vm_area_struct *area) +{ + /* + * Is this a new hole at the highest possible address? + */ + if (area->vm_end > area->vm_mm->free_area_cache) + area->vm_mm->free_area_cache = area->vm_end; +} + unsigned long get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) @@ -1102,7 +1236,7 @@ get_unmapped_area(struct file *file, uns return file->f_op->get_unmapped_area(file, addr, len, pgoff, flags); - return arch_get_unmapped_area(file, addr, len, pgoff, flags); + return current->mm->get_unmapped_area(file, addr, len, pgoff, flags); } EXPORT_SYMBOL(get_unmapped_area); @@ -1225,6 +1359,7 @@ int expand_stack(struct vm_area_struct * vma->vm_mm->total_vm += grow; if (vma->vm_flags & VM_LOCKED) vma->vm_mm->locked_vm += grow; + __vm_stat_account(vma->vm_mm, vma->vm_flags, vma->vm_file, grow); anon_vma_unlock(vma); return 0; } @@ -1287,6 +1422,7 @@ int expand_stack(struct vm_area_struct * vma->vm_mm->total_vm += grow; if (vma->vm_flags & VM_LOCKED) vma->vm_mm->locked_vm += grow; + __vm_stat_account(vma->vm_mm, vma->vm_flags, vma->vm_file, grow); anon_vma_unlock(vma); return 0; } @@ -1392,13 +1528,8 @@ static void unmap_vma(struct mm_struct * area->vm_mm->total_vm -= len >> PAGE_SHIFT; if (area->vm_flags & VM_LOCKED) area->vm_mm->locked_vm -= len >> PAGE_SHIFT; - /* - * Is this a new hole at the lowest possible address? - */ - if (area->vm_start >= TASK_UNMAPPED_BASE && - area->vm_start < area->vm_mm->free_area_cache) - area->vm_mm->free_area_cache = area->vm_start; - + vm_stat_unaccount(area); + area->vm_mm->unmap_area(area); remove_vm_struct(area); } @@ -1551,10 +1682,6 @@ int do_munmap(struct mm_struct *mm, unsi if (mpnt->vm_start >= end) return 0; - /* Something will probably happen, so notify. */ - if (mpnt->vm_file && (mpnt->vm_flags & VM_EXEC)) - profile_exec_unmap(mm); - /* * If we need to split any vma, do it now to save pain later. * @@ -1597,6 +1724,8 @@ asmlinkage long sys_munmap(unsigned long int ret; struct mm_struct *mm = current->mm; + profile_munmap(addr); + down_write(&mm->mmap_sem); ret = do_munmap(mm, addr, len); up_write(&mm->mmap_sem); @@ -1699,8 +1828,6 @@ void exit_mmap(struct mm_struct *mm) struct vm_area_struct *vma; unsigned long nr_accounted = 0; - profile_exit_mmap(mm); - lru_add_drain(); spin_lock(&mm->page_table_lock); --- linux-2.6.9-rc1/mm/mprotect.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/mm/mprotect.c 2004-09-02 20:51:53.000000000 -0700 @@ -175,9 +175,11 @@ success: * vm_flags and vm_page_prot are protected by the mmap_sem * held in write mode. */ + vm_stat_unaccount(vma); vma->vm_flags = newflags; vma->vm_page_prot = newprot; change_protection(vma, start, end, newprot); + vm_stat_account(vma); return 0; fail: --- linux-2.6.9-rc1/mm/mremap.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/mm/mremap.c 2004-09-02 20:51:53.000000000 -0700 @@ -224,6 +224,7 @@ static unsigned long move_vma(struct vm_ } mm->total_vm += new_len >> PAGE_SHIFT; + __vm_stat_account(mm, vma->vm_flags, vma->vm_file, new_len>>PAGE_SHIFT); if (vm_flags & VM_LOCKED) { mm->locked_vm += new_len >> PAGE_SHIFT; if (new_len > old_len) @@ -360,6 +361,8 @@ unsigned long do_mremap(unsigned long ad addr + new_len, vma->vm_pgoff, NULL); current->mm->total_vm += pages; + __vm_stat_account(vma->vm_mm, vma->vm_flags, + vma->vm_file, pages); if (vma->vm_flags & VM_LOCKED) { current->mm->locked_vm += pages; make_pages_present(addr + old_len, --- linux-2.6.9-rc1/mm/msync.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/mm/msync.c 2004-09-03 00:56:51.108390992 -0700 @@ -92,8 +92,8 @@ static inline int filemap_sync_pmd_range return error; } -static int filemap_sync(struct vm_area_struct * vma, unsigned long address, - size_t size, unsigned int flags) +static int __filemap_sync(struct vm_area_struct *vma, unsigned long address, + size_t size, unsigned int flags) { pgd_t * dir; unsigned long end = address + size; @@ -131,6 +131,30 @@ static int filemap_sync(struct vm_area_s return error; } +#ifdef CONFIG_PREEMPT +static int filemap_sync(struct vm_area_struct *vma, unsigned long address, + size_t size, unsigned int flags) +{ + const size_t chunk = 64 * 1024; /* bytes */ + int error = 0; + + while (size) { + size_t sz = min(size, chunk); + + error |= __filemap_sync(vma, address, sz, flags); + address += sz; + size -= sz; + } + return error; +} +#else +static int filemap_sync(struct vm_area_struct *vma, unsigned long address, + size_t size, unsigned int flags) +{ + return __filemap_sync(vma, address, size, flags); +} +#endif + /* * MS_SYNC syncs the entire file - including mappings. * --- linux-2.6.9-rc1/mm/oom_kill.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/mm/oom_kill.c 2004-09-03 00:57:09.243634016 -0700 @@ -26,6 +26,7 @@ /** * oom_badness - calculate a numeric value for how bad this task has been * @p: task struct of which task we should calculate + * @p: current uptime in seconds * * The formula used is relatively simple and documented inline in the * function. The main rationale is that we want to select a good task @@ -41,9 +42,9 @@ * of least surprise ... (be careful when you change it) */ -static int badness(struct task_struct *p) +static unsigned long badness(struct task_struct *p, unsigned long uptime) { - int points, cpu_time, run_time, s; + unsigned long points, cpu_time, run_time, s; if (!p->mm) return 0; @@ -56,12 +57,16 @@ static int badness(struct task_struct *p points = p->mm->total_vm; /* - * CPU time is in seconds and run time is in minutes. There is no - * particular reason for this other than that it turned out to work - * very well in practice. + * CPU time is in tens of seconds and run time is in thousands + * of seconds. There is no particular reason for this other than + * that it turned out to work very well in practice. */ cpu_time = (p->utime + p->stime) >> (SHIFT_HZ + 3); - run_time = (get_jiffies_64() - p->start_time) >> (SHIFT_HZ + 10); + + if (uptime >= p->start_time.tv_sec) + run_time = (uptime - p->start_time.tv_sec) >> 10; + else + run_time = 0; s = int_sqrt(cpu_time); if (s) @@ -108,13 +113,15 @@ static int badness(struct task_struct *p */ static struct task_struct * select_bad_process(void) { - int maxpoints = 0; + unsigned long maxpoints = 0; struct task_struct *g, *p; struct task_struct *chosen = NULL; + struct timespec uptime; + do_posix_clock_monotonic_gettime(&uptime); do_each_thread(g, p) if (p->pid) { - int points = badness(p); + unsigned long points = badness(p, uptime.tv_sec); if (points > maxpoints) { chosen = p; maxpoints = points; @@ -144,11 +151,10 @@ static void __oom_kill_task(task_t *p) printk(KERN_ERR "Out of Memory: Killed process %d (%s).\n", p->pid, p->comm); /* - * We give our sacrificial lamb high priority and access to - * all the memory it needs. That way it should be able to - * exit() and clear out its resources quickly... + * We give our sacrificial lamb access to all the memory it needs. + * That way it should be able to exit() and clear out its resources + * quickly... */ - p->time_slice = HZ; p->flags |= PF_MEMALLOC | PF_MEMDIE; /* This process has hardware access, be more careful. */ --- linux-2.6.9-rc1/mm/page_alloc.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/mm/page_alloc.c 2004-09-03 00:57:15.630663040 -0700 @@ -31,10 +31,13 @@ #include #include #include +#include +#include #include -DECLARE_BITMAP(node_online_map, MAX_NUMNODES); +nodemask_t node_online_map = NODE_MASK_NONE; +nodemask_t node_possible_map = NODE_MASK_ALL; struct pglist_data *pgdat_list; unsigned long totalram_pages; unsigned long totalhigh_pages; @@ -55,8 +58,8 @@ EXPORT_SYMBOL(zone_table); static char *zone_names[MAX_NR_ZONES] = { "DMA", "Normal", "HighMem" }; int min_free_kbytes = 1024; -static unsigned long __initdata nr_kernel_pages; -static unsigned long __initdata nr_all_pages; +unsigned long __initdata nr_kernel_pages; +unsigned long __initdata nr_all_pages; /* * Temporary debugging check for pages not lying within a given zone. @@ -76,9 +79,9 @@ static void bad_page(const char *functio { printk(KERN_EMERG "Bad page state at %s (in process '%s', page %p)\n", function, current->comm, page); - printk(KERN_EMERG "flags:0x%08lx mapping:%p mapcount:%d count:%d\n", - (unsigned long)page->flags, page->mapping, - (int)page->mapcount, page_count(page)); + printk(KERN_EMERG "flags:0x%0*lx mapping:%p mapcount:%d count:%d\n", + (int)(2*sizeof(page_flags_t)), (unsigned long)page->flags, + page->mapping, page_mapcount(page), page_count(page)); printk(KERN_EMERG "Backtrace:\n"); dump_stack(); printk(KERN_EMERG "Trying to fix it up, but a reboot is needed\n"); @@ -87,13 +90,11 @@ static void bad_page(const char *functio 1 << PG_lru | 1 << PG_active | 1 << PG_dirty | - 1 << PG_maplock | - 1 << PG_anon | 1 << PG_swapcache | 1 << PG_writeback); set_page_count(page, 0); + reset_page_mapcount(page); page->mapping = NULL; - page->mapcount = 0; } #ifndef CONFIG_HUGETLB_PAGE @@ -229,8 +230,6 @@ static inline void free_pages_check(cons 1 << PG_active | 1 << PG_reclaim | 1 << PG_slab | - 1 << PG_maplock | - 1 << PG_anon | 1 << PG_swapcache | 1 << PG_writeback ))) bad_page(function, page); @@ -279,6 +278,8 @@ void __free_pages_ok(struct page *page, LIST_HEAD(list); int i; + arch_free_page(page, order); + mod_page_state(pgfree, 1 << order); for (i = 0 ; i < (1 << order) ; ++i) free_pages_check(__FUNCTION__, page + i); @@ -350,8 +351,6 @@ static void prep_new_page(struct page *p 1 << PG_active | 1 << PG_dirty | 1 << PG_reclaim | - 1 << PG_maplock | - 1 << PG_anon | 1 << PG_swapcache | 1 << PG_writeback ))) bad_page(__FUNCTION__, page); @@ -509,8 +508,12 @@ static void fastcall free_hot_cold_page( struct per_cpu_pages *pcp; unsigned long flags; + arch_free_page(page, 0); + kernel_map_pages(page, 1, 0); inc_page_state(pgfree); + if (PageAnon(page)) + page->mapping = NULL; free_pages_check(__FUNCTION__, page); pcp = &zone->pageset[get_cpu()].pcp[cold]; local_irq_save(flags); @@ -532,6 +535,97 @@ void fastcall free_cold_page(struct page free_hot_cold_page(page, 1); } +static inline struct list_head *get_per_thread_pages(void) +{ + return ¤t->private_pages; +} + +int perthread_pages_reserve(int nrpages, int gfp) +{ + int i; + struct list_head accumulator; + struct list_head *per_thread; + + per_thread = get_per_thread_pages(); + INIT_LIST_HEAD(&accumulator); + list_splice_init(per_thread, &accumulator); + for (i = 0; i < nrpages; ++i) { + struct page *page; + + page = alloc_page(gfp); + if (page != NULL) + list_add(&page->lru, &accumulator); + else { + for (; i > 0; --i) { + page = list_entry(accumulator.next, struct page, lru); + list_del(&page->lru); + page_cache_release(page); + } + return -ENOMEM; + } + } + /* + * Q: why @accumulator is used, instead of directly adding pages to + * the get_per_thread_pages()? + * + * A: because after first page is added to the get_per_thread_pages(), + * next call to the alloc_page() (on the next loop iteration), will + * re-use it. + */ + list_splice(&accumulator, per_thread); + current->private_pages_count += nrpages; + return 0; +} +EXPORT_SYMBOL(perthread_pages_reserve); + +void perthread_pages_release(int nrpages) +{ + struct list_head *per_thread; + + current->private_pages_count -= nrpages; + per_thread = get_per_thread_pages(); + for (; nrpages != 0; --nrpages) { + struct page *page; + + BUG_ON(list_empty(per_thread)); + page = list_entry(per_thread->next, struct page, lru); + list_del(&page->lru); + page_cache_release(page); + } +} +EXPORT_SYMBOL(perthread_pages_release); + +int perthread_pages_count(void) +{ + return current->private_pages_count; +} +EXPORT_SYMBOL(perthread_pages_count); + +static inline struct page * +perthread_pages_alloc(void) +{ + struct list_head *perthread_pages; + + /* + * try to allocate pages from the per-thread private_pages pool. No + * locking is needed: this list can only be modified by the thread + * itself, and not by interrupts or other threads. + */ + perthread_pages = get_per_thread_pages(); + if (!in_interrupt() && !list_empty(perthread_pages)) { + struct page *page; + + page = list_entry(perthread_pages->next, struct page, lru); + list_del(&page->lru); + current->private_pages_count--; + /* + * per-thread page is already initialized, just return it. + */ + return page; + } else + return NULL; +} + /* * Really, prep_compound_page() should be called from __rmqueue_bulk(). But * we cheat by calling it from here, in the order > 0 path. Saves a branch @@ -600,83 +694,89 @@ __alloc_pages(unsigned int gfp_mask, uns { const int wait = gfp_mask & __GFP_WAIT; unsigned long min; - struct zone **zones; + struct zone **zones, *z; struct page *page; struct reclaim_state reclaim_state; struct task_struct *p = current; int i; int alloc_type; int do_retry; + int can_try_harder; might_sleep_if(wait); + /* + * The caller may dip into page reserves a bit more if the caller + * cannot run direct reclaim, or is the caller has realtime scheduling + * policy + */ + can_try_harder = (unlikely(rt_task(p)) && !in_interrupt()) || !wait; + + if (order == 0) { + page = perthread_pages_alloc(); + if (page != NULL) + return page; + } + zones = zonelist->zones; /* the list of zones suitable for gfp_mask */ - if (zones[0] == NULL) /* no zones in the zonelist */ + + if (unlikely(zones[0] == NULL)) { + /* Should this ever happen?? */ return NULL; + } alloc_type = zone_idx(zones[0]); /* Go through the zonelist once, looking for a zone with enough free */ - for (i = 0; zones[i] != NULL; i++) { - struct zone *z = zones[i]; + for (i = 0; (z = zones[i]) != NULL; i++) { + min = z->pages_low + (1<protection[alloc_type]; - min = (1<protection[alloc_type]; + if (z->free_pages < min) + continue; - /* - * We let real-time tasks dip their real-time paws a little - * deeper into reserves. - */ - if (rt_task(p)) - min -= z->pages_low >> 1; + if (!cpuset_zone_allowed(z)) + continue; - if (z->free_pages >= min || - (!wait && z->free_pages >= z->pages_high)) { - page = buffered_rmqueue(z, order, gfp_mask); - if (page) { - zone_statistics(zonelist, z); - goto got_pg; - } - } + page = buffered_rmqueue(z, order, gfp_mask); + if (page) + goto got_pg; } - /* we're somewhat low on memory, failed to find what we needed */ - for (i = 0; zones[i] != NULL; i++) - wakeup_kswapd(zones[i]); - - /* Go through the zonelist again, taking __GFP_HIGH into account */ - for (i = 0; zones[i] != NULL; i++) { - struct zone *z = zones[i]; - - min = (1<protection[alloc_type]; + for (i = 0; (z = zones[i]) != NULL; i++) + wakeup_kswapd(z); + /* + * Go through the zonelist again. Let __GFP_HIGH and allocations + * coming from realtime tasks to go deeper into reserves + */ + for (i = 0; (z = zones[i]) != NULL; i++) { + min = z->pages_min; if (gfp_mask & __GFP_HIGH) - min -= z->pages_low >> 2; - if (rt_task(p)) - min -= z->pages_low >> 1; + min /= 2; + if (can_try_harder) + min -= min / 4; + min += (1<protection[alloc_type]; - if (z->free_pages >= min || - (!wait && z->free_pages >= z->pages_high)) { - page = buffered_rmqueue(z, order, gfp_mask); - if (page) { - zone_statistics(zonelist, z); - goto got_pg; - } - } - } + if (z->free_pages < min) + continue; + + if (!cpuset_zone_allowed(z)) + continue; - /* here we're in the low on memory slow path */ + page = buffered_rmqueue(z, order, gfp_mask); + if (page) + goto got_pg; + } -rebalance: + /* This allocation should allow future memory freeing. */ if ((p->flags & (PF_MEMALLOC | PF_MEMDIE)) && !in_interrupt()) { /* go through the zonelist yet again, ignoring mins */ - for (i = 0; zones[i] != NULL; i++) { - struct zone *z = zones[i]; - + for (i = 0; (z = zones[i]) != NULL; i++) { + if (!cpuset_zone_allowed(z)) + continue; page = buffered_rmqueue(z, order, gfp_mask); - if (page) { - zone_statistics(zonelist, z); + if (page) goto got_pg; - } } goto nopage; } @@ -685,6 +785,8 @@ rebalance: if (!wait) goto nopage; +rebalance: + /* We now go into synchronous reclaim */ p->flags |= PF_MEMALLOC; reclaim_state.reclaimed_slab = 0; p->reclaim_state = &reclaim_state; @@ -695,27 +797,31 @@ rebalance: p->flags &= ~PF_MEMALLOC; /* go through the zonelist yet one more time */ - for (i = 0; zones[i] != NULL; i++) { - struct zone *z = zones[i]; + for (i = 0; (z = zones[i]) != NULL; i++) { + min = z->pages_min; + if (gfp_mask & __GFP_HIGH) + min /= 2; + if (can_try_harder) + min -= min / 4; + min += (1<protection[alloc_type]; - min = (1UL << order) + z->protection[alloc_type]; + if (z->free_pages < min) + continue; - if (z->free_pages >= min || - (!wait && z->free_pages >= z->pages_high)) { - page = buffered_rmqueue(z, order, gfp_mask); - if (page) { - zone_statistics(zonelist, z); - goto got_pg; - } - } + if (!cpuset_zone_allowed(z)) + continue; + + page = buffered_rmqueue(z, order, gfp_mask); + if (page) + goto got_pg; } /* * Don't let big-order allocations loop unless the caller explicitly * requests that. Wait for some write requests to complete then retry. * - * In this implementation, __GFP_REPEAT means __GFP_NOFAIL, but that - * may not be true in other implementations. + * In this implementation, __GFP_REPEAT means __GFP_NOFAIL for order + * <= 3, but that may not be true in other implementations. */ do_retry = 0; if (!(gfp_mask & __GFP_NORETRY)) { @@ -738,6 +844,7 @@ nopage: } return NULL; got_pg: + zone_statistics(zonelist, z); kernel_map_pages(page, 1 << order, 1); return page; } @@ -802,8 +909,8 @@ EXPORT_SYMBOL(__free_pages); fastcall void free_pages(unsigned long addr, unsigned int order) { if (addr != 0) { - BUG_ON(!virt_addr_valid(addr)); - __free_pages(virt_to_page(addr), order); + BUG_ON(!virt_addr_valid((void *)addr)); + __free_pages(virt_to_page((void *)addr), order); } } @@ -965,18 +1072,36 @@ unsigned long __read_page_state(unsigned return ret; } +void __get_zone_counts(unsigned long *active, unsigned long *inactive, + unsigned long *free, struct pglist_data *pgdat) +{ + struct zone *zones = pgdat->node_zones; + int i; + + *active = 0; + *inactive = 0; + *free = 0; + for (i = 0; i < MAX_NR_ZONES; i++) { + *active += zones[i].nr_active; + *inactive += zones[i].nr_inactive; + *free += zones[i].free_pages; + } +} + void get_zone_counts(unsigned long *active, unsigned long *inactive, unsigned long *free) { - struct zone *zone; + struct pglist_data *pgdat; *active = 0; *inactive = 0; *free = 0; - for_each_zone(zone) { - *active += zone->nr_active; - *inactive += zone->nr_inactive; - *free += zone->free_pages; + for_each_pgdat(pgdat) { + unsigned long l, m, n; + __get_zone_counts(&l, &m, &n, pgdat); + *active += l; + *inactive += m; + *free += n; } } @@ -1309,6 +1434,7 @@ void __init build_all_zonelists(void) for(i = 0 ; i < numnodes ; i++) build_zonelists(NODE_DATA(i)); printk("Built %i zonelists\n", numnodes); + cpuset_init_current_mems_allowed(); } /* @@ -1379,14 +1505,16 @@ static void __init calculate_zone_totalp * up by free_all_bootmem() once the early boot process is * done. Non-atomic initialization, single-pass. */ -void __init memmap_init_zone(struct page *start, unsigned long size, int nid, - unsigned long zone, unsigned long start_pfn) +void __init memmap_init_zone(unsigned long size, int nid, unsigned long zone, + unsigned long start_pfn) { + struct page *start = pfn_to_page(start_pfn); struct page *page; for (page = start; page < (start + size); page++) { set_page_zone(page, NODEZONE(nid, zone)); set_page_count(page, 0); + reset_page_mapcount(page); SetPageReserved(page); INIT_LIST_HEAD(&page->lru); #ifdef WANT_PAGE_VIRTUAL @@ -1445,8 +1573,8 @@ void zone_init_free_lists(struct pglist_ } #ifndef __HAVE_ARCH_MEMMAP_INIT -#define memmap_init(start, size, nid, zone, start_pfn) \ - memmap_init_zone((start), (size), (nid), (zone), (start_pfn)) +#define memmap_init(size, nid, zone, start_pfn) \ + memmap_init_zone((size), (nid), (zone), (start_pfn)) #endif /* @@ -1461,7 +1589,6 @@ static void __init free_area_init_core(s unsigned long i, j; const unsigned long zone_required_alignment = 1UL << (MAX_ORDER-1); int cpu, nid = pgdat->node_id; - struct page *lmem_map = pgdat->node_mem_map; unsigned long zone_start_pfn = pgdat->node_start_pfn; pgdat->nr_zones = 0; @@ -1549,35 +1676,41 @@ static void __init free_area_init_core(s pgdat->nr_zones = j+1; - zone->zone_mem_map = lmem_map; + zone->zone_mem_map = pfn_to_page(zone_start_pfn); zone->zone_start_pfn = zone_start_pfn; if ((zone_start_pfn) & (zone_required_alignment-1)) printk("BUG: wrong zone alignment, it will crash\n"); - memmap_init(lmem_map, size, nid, j, zone_start_pfn); + memmap_init(size, nid, j, zone_start_pfn); zone_start_pfn += size; - lmem_map += size; zone_init_free_lists(pgdat, zone, zone->spanned_pages); } } -void __init free_area_init_node(int nid, struct pglist_data *pgdat, - struct page *node_mem_map, unsigned long *zones_size, - unsigned long node_start_pfn, unsigned long *zholes_size) +void __init node_alloc_mem_map(struct pglist_data *pgdat) { unsigned long size; + size = (pgdat->node_spanned_pages + 1) * sizeof(struct page); + pgdat->node_mem_map = alloc_bootmem_node(pgdat, size); +#ifndef CONFIG_DISCONTIGMEM + mem_map = contig_page_data.node_mem_map; +#endif +} + +void __init free_area_init_node(int nid, struct pglist_data *pgdat, + unsigned long *zones_size, unsigned long node_start_pfn, + unsigned long *zholes_size) +{ pgdat->node_id = nid; pgdat->node_start_pfn = node_start_pfn; calculate_zone_totalpages(pgdat, zones_size, zholes_size); - if (!node_mem_map) { - size = (pgdat->node_spanned_pages + 1) * sizeof(struct page); - node_mem_map = alloc_bootmem_node(pgdat, size); - } - pgdat->node_mem_map = node_mem_map; + + if (!pfn_to_page(node_start_pfn)) + node_alloc_mem_map(pgdat); free_area_init_core(pgdat, zones_size, zholes_size); } @@ -1590,9 +1723,8 @@ EXPORT_SYMBOL(contig_page_data); void __init free_area_init(unsigned long *zones_size) { - free_area_init_node(0, &contig_page_data, NULL, zones_size, + free_area_init_node(0, &contig_page_data, zones_size, __pa(PAGE_OFFSET) >> PAGE_SHIFT, NULL); - mem_map = contig_page_data.node_mem_map; } #endif @@ -1851,11 +1983,11 @@ static void setup_per_zone_protection(vo * We never protect zones that don't have memory * in them (j>max_zone) or zones that aren't in * the zonelists for a certain type of - * allocation (j>i). We have to assign these to - * zero because the lower zones take + * allocation (j>=i). We have to assign these + * to zero because the lower zones take * contributions from the higher zones. */ - if (j > max_zone || j > i) { + if (j > max_zone || j >= i) { zone->protection[i] = 0; continue; } @@ -1864,7 +1996,6 @@ static void setup_per_zone_protection(vo */ zone->protection[i] = higherzone_val(zone, max_zone, i); - zone->protection[i] += zone->pages_low; } } } --- linux-2.6.9-rc1/mm/page-writeback.c 2004-08-24 00:03:17.000000000 -0700 +++ 25/mm/page-writeback.c 2004-09-03 00:57:25.151215696 -0700 @@ -153,9 +153,11 @@ get_dirty_limits(struct writeback_state if (dirty_ratio < 5) dirty_ratio = 5; - background_ratio = dirty_background_ratio; - if (background_ratio >= dirty_ratio) - background_ratio = dirty_ratio / 2; + /* + * Keep the ratio between dirty_ratio and background_ratio roughly + * what the sysctls are after dirty_ratio has been scaled (above). + */ + background_ratio = dirty_background_ratio * dirty_ratio/vm_dirty_ratio; background = (background_ratio * total_pages) / 100; dirty = (dirty_ratio * total_pages) / 100; @@ -276,6 +278,28 @@ void balance_dirty_pages_ratelimited(str } EXPORT_SYMBOL(balance_dirty_pages_ratelimited); +void throttle_vm_writeout(void) +{ + struct writeback_state wbs; + long background_thresh; + long dirty_thresh; + + for ( ; ; ) { + get_dirty_limits(&wbs, &background_thresh, &dirty_thresh); + + /* + * Boost the allowable dirty threshold a bit for page + * allocators so they don't get DoS'ed by heavy writers + */ + dirty_thresh += dirty_thresh / 10; /* wheeee... */ + + if (wbs.nr_unstable + wbs.nr_writeback <= dirty_thresh) + break; + blk_congestion_wait(WRITE, HZ/10); + } +} + + /* * writeback at least _min_pages, and keep writing until the amount of dirty * memory is less than the background threshold, or until we're all clean. @@ -582,7 +606,7 @@ int __set_page_dirty_nobuffers(struct pa struct address_space *mapping = page_mapping(page); if (mapping) { - spin_lock_irq(&mapping->tree_lock); + write_lock_irq(&mapping->tree_lock); mapping = page_mapping(page); if (page_mapping(page)) { /* Race with truncate? */ BUG_ON(page_mapping(page) != mapping); @@ -591,7 +615,7 @@ int __set_page_dirty_nobuffers(struct pa radix_tree_tag_set(&mapping->page_tree, page_index(page), PAGECACHE_TAG_DIRTY); } - spin_unlock_irq(&mapping->tree_lock); + write_unlock_irq(&mapping->tree_lock); if (mapping->host) { /* !PageAnon && !swapper_space */ __mark_inode_dirty(mapping->host, @@ -666,17 +690,17 @@ int test_clear_page_dirty(struct page *p unsigned long flags; if (mapping) { - spin_lock_irqsave(&mapping->tree_lock, flags); + write_lock_irqsave(&mapping->tree_lock, flags); if (TestClearPageDirty(page)) { radix_tree_tag_clear(&mapping->page_tree, page_index(page), PAGECACHE_TAG_DIRTY); - spin_unlock_irqrestore(&mapping->tree_lock, flags); + write_unlock_irqrestore(&mapping->tree_lock, flags); if (!mapping->backing_dev_info->memory_backed) dec_page_state(nr_dirty); return 1; } - spin_unlock_irqrestore(&mapping->tree_lock, flags); + write_unlock_irqrestore(&mapping->tree_lock, flags); return 0; } return TestClearPageDirty(page); @@ -723,15 +747,15 @@ int __clear_page_dirty(struct page *page if (mapping) { unsigned long flags; - spin_lock_irqsave(&mapping->tree_lock, flags); + write_lock_irqsave(&mapping->tree_lock, flags); if (TestClearPageDirty(page)) { radix_tree_tag_clear(&mapping->page_tree, page_index(page), PAGECACHE_TAG_DIRTY); - spin_unlock_irqrestore(&mapping->tree_lock, flags); + write_unlock_irqrestore(&mapping->tree_lock, flags); return 1; } - spin_unlock_irqrestore(&mapping->tree_lock, flags); + write_unlock_irqrestore(&mapping->tree_lock, flags); return 0; } return TestClearPageDirty(page); @@ -745,13 +769,13 @@ int test_clear_page_writeback(struct pag if (mapping) { unsigned long flags; - spin_lock_irqsave(&mapping->tree_lock, flags); + write_lock_irqsave(&mapping->tree_lock, flags); ret = TestClearPageWriteback(page); if (ret) radix_tree_tag_clear(&mapping->page_tree, page_index(page), PAGECACHE_TAG_WRITEBACK); - spin_unlock_irqrestore(&mapping->tree_lock, flags); + write_unlock_irqrestore(&mapping->tree_lock, flags); } else { ret = TestClearPageWriteback(page); } @@ -766,7 +790,7 @@ int test_set_page_writeback(struct page if (mapping) { unsigned long flags; - spin_lock_irqsave(&mapping->tree_lock, flags); + write_lock_irqsave(&mapping->tree_lock, flags); ret = TestSetPageWriteback(page); if (!ret) radix_tree_tag_set(&mapping->page_tree, @@ -776,7 +800,7 @@ int test_set_page_writeback(struct page radix_tree_tag_clear(&mapping->page_tree, page_index(page), PAGECACHE_TAG_DIRTY); - spin_unlock_irqrestore(&mapping->tree_lock, flags); + write_unlock_irqrestore(&mapping->tree_lock, flags); } else { ret = TestSetPageWriteback(page); } @@ -794,9 +818,9 @@ int mapping_tagged(struct address_space unsigned long flags; int ret; - spin_lock_irqsave(&mapping->tree_lock, flags); + read_lock_irqsave(&mapping->tree_lock, flags); ret = radix_tree_tagged(&mapping->page_tree, tag); - spin_unlock_irqrestore(&mapping->tree_lock, flags); + read_unlock_irqrestore(&mapping->tree_lock, flags); return ret; } EXPORT_SYMBOL(mapping_tagged); --- linux-2.6.9-rc1/mm/prio_tree.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/mm/prio_tree.c 2004-09-02 20:51:53.000000000 -0700 @@ -81,6 +81,8 @@ static inline unsigned long prio_tree_ma return index_bits_to_maxindex[bits - 1]; } +static void prio_tree_remove(struct prio_tree_root *, struct prio_tree_node *); + /* * Extend a priority search tree so that it can store a node with heap_index * max_heap_index. In the worst case, this algorithm takes O((log n)^2). @@ -90,8 +92,6 @@ static inline unsigned long prio_tree_ma static struct prio_tree_node *prio_tree_expand(struct prio_tree_root *root, struct prio_tree_node *node, unsigned long max_heap_index) { - static void prio_tree_remove(struct prio_tree_root *, - struct prio_tree_node *); struct prio_tree_node *first = NULL, *prev, *last = NULL; if (max_heap_index > prio_tree_maxindex(root->index_bits)) --- linux-2.6.9-rc1/mm/readahead.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/mm/readahead.c 2004-09-03 00:57:01.804764896 -0700 @@ -28,16 +28,15 @@ struct backing_dev_info default_backing_ EXPORT_SYMBOL_GPL(default_backing_dev_info); /* - * Initialise a struct file's readahead state + * Initialise a struct file's readahead state. Assumes that the caller has + * memset *ra to zero. */ void file_ra_state_init(struct file_ra_state *ra, struct address_space *mapping) { - memset(ra, 0, sizeof(*ra)); ra->ra_pages = mapping->backing_dev_info->ra_pages; ra->average = ra->ra_pages / 2; } -EXPORT_SYMBOL(file_ra_state_init); /* * Return max readahead size for this inode in number-of-pages. @@ -234,7 +233,7 @@ __do_page_cache_readahead(struct address /* * Preallocate as many pages as we will need. */ - spin_lock_irq(&mapping->tree_lock); + read_lock_irq(&mapping->tree_lock); for (page_idx = 0; page_idx < nr_to_read; page_idx++) { unsigned long page_offset = offset + page_idx; @@ -245,16 +244,16 @@ __do_page_cache_readahead(struct address if (page) continue; - spin_unlock_irq(&mapping->tree_lock); + read_unlock_irq(&mapping->tree_lock); page = page_cache_alloc_cold(mapping); - spin_lock_irq(&mapping->tree_lock); + read_lock_irq(&mapping->tree_lock); if (!page) break; page->index = page_offset; list_add(&page->lru, &page_pool); ret++; } - spin_unlock_irq(&mapping->tree_lock); + read_unlock_irq(&mapping->tree_lock); /* * Now start the IO. We ignore I/O errors - if the page is not @@ -516,6 +515,7 @@ do_io: out: return; } +EXPORT_SYMBOL(page_cache_readahead); /* @@ -572,6 +572,6 @@ unsigned long max_sane_readahead(unsigne unsigned long inactive; unsigned long free; - get_zone_counts(&active, &inactive, &free); + __get_zone_counts(&active, &inactive, &free, NODE_DATA(numa_node_id())); return min(nr, (inactive + free) / 2); } --- linux-2.6.9-rc1/mm/rmap.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/mm/rmap.c 2004-09-02 20:51:53.000000000 -0700 @@ -18,9 +18,30 @@ */ /* - * Locking: see "Lock ordering" summary in filemap.c. - * In swapout, page_map_lock is held on entry to page_referenced and - * try_to_unmap, so they trylock for i_mmap_lock and page_table_lock. + * Lock ordering in mm: + * + * inode->i_sem (while writing or truncating, not reading or faulting) + * inode->i_alloc_sem + * + * When a page fault occurs in writing from user to file, down_read + * of mmap_sem nests within i_sem; in sys_msync, i_sem nests within + * down_read of mmap_sem; i_sem and down_write of mmap_sem are never + * taken together; in truncation, i_sem is taken outermost. + * + * mm->mmap_sem + * page->flags PG_locked (lock_page) + * mapping->i_mmap_lock + * anon_vma->lock + * mm->page_table_lock + * zone->lru_lock (in mark_page_accessed) + * swap_list_lock (in swap_free etc's swap_info_get) + * swap_device_lock (in swap_duplicate, swap_info_get) + * mapping->private_lock (in __set_page_dirty_buffers) + * inode_lock (in set_page_dirty's __mark_inode_dirty) + * sb_lock (within inode_lock in fs/fs-writeback.c) + * mapping->tree_lock (widely used, in set_page_dirty, + * in arch-dependent flush_dcache_mmap_lock, + * within inode_lock in __sync_single_inode) */ #include @@ -30,6 +51,7 @@ #include #include #include +#include #include @@ -63,28 +85,32 @@ int anon_vma_prepare(struct vm_area_stru might_sleep(); if (unlikely(!anon_vma)) { struct mm_struct *mm = vma->vm_mm; - struct anon_vma *allocated = NULL; + struct anon_vma *allocated, *locked; anon_vma = find_mergeable_anon_vma(vma); - if (!anon_vma) { + if (anon_vma) { + allocated = NULL; + locked = anon_vma; + spin_lock(&locked->lock); + } else { anon_vma = anon_vma_alloc(); if (unlikely(!anon_vma)) return -ENOMEM; allocated = anon_vma; + locked = NULL; } /* page_table_lock to protect against threads */ spin_lock(&mm->page_table_lock); if (likely(!vma->anon_vma)) { - if (!allocated) - spin_lock(&anon_vma->lock); vma->anon_vma = anon_vma; list_add(&vma->anon_vma_node, &anon_vma->head); - if (!allocated) - spin_unlock(&anon_vma->lock); allocated = NULL; } spin_unlock(&mm->page_table_lock); + + if (locked) + spin_unlock(&locked->lock); if (unlikely(allocated)) anon_vma_free(allocated); } @@ -159,16 +185,31 @@ static void anon_vma_ctor(void *data, km void __init anon_vma_init(void) { - anon_vma_cachep = kmem_cache_create("anon_vma", - sizeof(struct anon_vma), 0, SLAB_PANIC, anon_vma_ctor, NULL); + anon_vma_cachep = kmem_cache_create("anon_vma", sizeof(struct anon_vma), + 0, SLAB_DESTROY_BY_RCU|SLAB_PANIC, anon_vma_ctor, NULL); } -/* this needs the page->flags PG_maplock held */ -static inline void clear_page_anon(struct page *page) +/* + * Getting a lock on a stable anon_vma from a page off the LRU is + * tricky: page_lock_anon_vma rely on RCU to guard against the races. + */ +static struct anon_vma *page_lock_anon_vma(struct page *page) { - BUG_ON(!page->mapping); - page->mapping = NULL; - ClearPageAnon(page); + struct anon_vma *anon_vma = NULL; + unsigned long anon_mapping; + + rcu_read_lock(); + anon_mapping = (unsigned long) page->mapping; + if (!(anon_mapping & PAGE_MAPPING_ANON)) + goto out; + if (!page_mapped(page)) + goto out; + + anon_vma = (struct anon_vma *) (anon_mapping - PAGE_MAPPING_ANON); + spin_lock(&anon_vma->lock); +out: + rcu_read_unlock(); + return anon_vma; } /* @@ -190,6 +231,24 @@ vma_address(struct page *page, struct vm } /* + * At what user virtual address is page expected in vma? checking that the + * page matches the vma: currently only used by unuse_process, on anon pages. + */ +unsigned long page_address_in_vma(struct page *page, struct vm_area_struct *vma) +{ + if (PageAnon(page)) { + if ((void *)vma->anon_vma != + (void *)page->mapping - PAGE_MAPPING_ANON) + return -EFAULT; + } else if (page->mapping && !(vma->vm_flags & VM_NONLINEAR)) { + if (vma->vm_file->f_mapping != page->mapping) + return -EFAULT; + } else + return -EFAULT; + return vma_address(page, vma); +} + +/* * Subfunctions of page_referenced: page_referenced_one called * repeatedly from either page_referenced_anon or page_referenced_file. */ @@ -209,8 +268,7 @@ static int page_referenced_one(struct pa if (address == -EFAULT) goto out; - if (!spin_trylock(&mm->page_table_lock)) - goto out; + spin_lock(&mm->page_table_lock); pgd = pgd_offset(mm, address); if (!pgd_present(*pgd)) @@ -243,15 +301,18 @@ out: return referenced; } -static inline int page_referenced_anon(struct page *page) +static int page_referenced_anon(struct page *page) { - unsigned int mapcount = page->mapcount; - struct anon_vma *anon_vma = (struct anon_vma *) page->mapping; + unsigned int mapcount; + struct anon_vma *anon_vma; struct vm_area_struct *vma; int referenced = 0; - spin_lock(&anon_vma->lock); - BUG_ON(list_empty(&anon_vma->head)); + anon_vma = page_lock_anon_vma(page); + if (!anon_vma) + return referenced; + + mapcount = page_mapcount(page); list_for_each_entry(vma, &anon_vma->head, anon_vma_node) { referenced += page_referenced_one(page, vma, &mapcount); if (!mapcount) @@ -271,21 +332,38 @@ static inline int page_referenced_anon(s * of references it found. * * This function is only called from page_referenced for object-based pages. - * - * The spinlock address_space->i_mmap_lock is tried. If it can't be gotten, - * assume a reference count of 0, so try_to_unmap will then have a go. */ -static inline int page_referenced_file(struct page *page) +static int page_referenced_file(struct page *page) { - unsigned int mapcount = page->mapcount; + unsigned int mapcount; struct address_space *mapping = page->mapping; pgoff_t pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT); struct vm_area_struct *vma; struct prio_tree_iter iter; int referenced = 0; - if (!spin_trylock(&mapping->i_mmap_lock)) - return 0; + /* + * The caller's checks on page->mapping and !PageAnon have made + * sure that this is a file page: the check for page->mapping + * excludes the case just before it gets set on an anon page. + */ + BUG_ON(PageAnon(page)); + + /* + * The page lock not only makes sure that page->mapping cannot + * suddenly be NULLified by truncation, it makes sure that the + * structure at mapping cannot be freed and reused yet, + * so we can safely take mapping->i_mmap_lock. + */ + BUG_ON(!PageLocked(page)); + + spin_lock(&mapping->i_mmap_lock); + + /* + * i_mmap_lock does not stabilize mapcount at all, but mapcount + * is more likely to be accurate if we note it after spinning. + */ + mapcount = page_mapcount(page); vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) { if ((vma->vm_flags & (VM_LOCKED|VM_MAYSHARE)) @@ -305,12 +383,12 @@ static inline int page_referenced_file(s /** * page_referenced - test if the page was referenced * @page: the page to test + * @is_locked: caller holds lock on the page * * Quick test_and_clear_referenced for all mappings to a page, * returns the number of ptes which referenced the page. - * Caller needs to hold the rmap lock. */ -int page_referenced(struct page *page) +int page_referenced(struct page *page, int is_locked) { int referenced = 0; @@ -320,11 +398,17 @@ int page_referenced(struct page *page) if (TestClearPageReferenced(page)) referenced++; - if (page->mapcount && page->mapping) { + if (page_mapped(page) && page->mapping) { if (PageAnon(page)) referenced += page_referenced_anon(page); - else + else if (is_locked) + referenced += page_referenced_file(page); + else if (TestSetPageLocked(page)) + referenced++; + else if (page->mapping) { referenced += page_referenced_file(page); + unlock_page(page); + } } return referenced; } @@ -346,36 +430,17 @@ void page_add_anon_rmap(struct page *pag BUG_ON(PageReserved(page)); BUG_ON(!anon_vma); + anon_vma = (void *) anon_vma + PAGE_MAPPING_ANON; index = (address - vma->vm_start) >> PAGE_SHIFT; index += vma->vm_pgoff; index >>= PAGE_CACHE_SHIFT - PAGE_SHIFT; - /* - * Setting and clearing PG_anon must always happen inside - * page_map_lock to avoid races between mapping and - * unmapping on different processes of the same - * shared cow swapcache page. And while we take the - * page_map_lock PG_anon cannot change from under us. - * Actually PG_anon cannot change under fork either - * since fork holds a reference on the page so it cannot - * be unmapped under fork and in turn copy_page_range is - * allowed to read PG_anon outside the page_map_lock. - */ - page_map_lock(page); - if (!page->mapcount) { - BUG_ON(PageAnon(page)); - BUG_ON(page->mapping); - SetPageAnon(page); + if (atomic_inc_and_test(&page->_mapcount)) { page->index = index; page->mapping = (struct address_space *) anon_vma; inc_page_state(nr_mapped); - } else { - BUG_ON(!PageAnon(page)); - BUG_ON(page->index != index); - BUG_ON(page->mapping != (struct address_space *) anon_vma); } - page->mapcount++; - page_map_unlock(page); + /* else checking page index and mapping is racy */ } /** @@ -390,11 +455,8 @@ void page_add_file_rmap(struct page *pag if (!pfn_valid(page_to_pfn(page)) || PageReserved(page)) return; - page_map_lock(page); - if (!page->mapcount) + if (atomic_inc_and_test(&page->_mapcount)) inc_page_state(nr_mapped); - page->mapcount++; - page_map_unlock(page); } /** @@ -406,18 +468,22 @@ void page_add_file_rmap(struct page *pag void page_remove_rmap(struct page *page) { BUG_ON(PageReserved(page)); - BUG_ON(!page->mapcount); - page_map_lock(page); - page->mapcount--; - if (!page->mapcount) { + if (atomic_add_negative(-1, &page->_mapcount)) { + BUG_ON(page_mapcount(page) < 0); + /* + * It would be tidy to reset the PageAnon mapping here, + * but that might overwrite a racing page_add_anon_rmap + * which increments mapcount after us but sets mapping + * before us: so leave the reset to free_hot_cold_page, + * and remember that it's only reliable while mapped. + * Leaving it set also helps swapoff to reinstate ptes + * faster for those pages still in swapcache. + */ if (page_test_and_clear_dirty(page)) set_page_dirty(page); - if (PageAnon(page)) - clear_page_anon(page); dec_page_state(nr_mapped); } - page_map_unlock(page); } /* @@ -444,8 +510,7 @@ static int try_to_unmap_one(struct page * We need the page_table_lock to protect us from page faults, * munmap, fork, etc... */ - if (!spin_trylock(&mm->page_table_lock)) - goto out; + spin_lock(&mm->page_table_lock); pgd = pgd_offset(mm, address); if (!pgd_present(*pgd)) @@ -489,7 +554,7 @@ static int try_to_unmap_one(struct page * ptes from being unmapped, so swapoff can make progress. */ if (PageSwapCache(page) && - page_count(page) != page->mapcount + 2) { + page_count(page) != page_mapcount(page) + 2) { ret = SWAP_FAIL; goto out_unmap; } @@ -515,8 +580,7 @@ static int try_to_unmap_one(struct page } mm->rss--; - BUG_ON(!page->mapcount); - page->mapcount--; + page_remove_rmap(page); page_cache_release(page); out_unmap: @@ -549,7 +613,7 @@ out: #define CLUSTER_SIZE min(32*PAGE_SIZE, PMD_SIZE) #define CLUSTER_MASK (~(CLUSTER_SIZE - 1)) -static int try_to_unmap_cluster(unsigned long cursor, +static void try_to_unmap_cluster(unsigned long cursor, unsigned int *mapcount, struct vm_area_struct *vma) { struct mm_struct *mm = vma->vm_mm; @@ -566,8 +630,7 @@ static int try_to_unmap_cluster(unsigned * We need the page_table_lock to protect us from page faults, * munmap, fork, etc... */ - if (!spin_trylock(&mm->page_table_lock)) - return SWAP_FAIL; + spin_lock(&mm->page_table_lock); address = (vma->vm_start + cursor) & CLUSTER_MASK; end = address + CLUSTER_SIZE; @@ -624,20 +687,21 @@ static int try_to_unmap_cluster(unsigned out_unlock: spin_unlock(&mm->page_table_lock); - return SWAP_AGAIN; } -static inline int try_to_unmap_anon(struct page *page) +static int try_to_unmap_anon(struct page *page) { - struct anon_vma *anon_vma = (struct anon_vma *) page->mapping; + struct anon_vma *anon_vma; struct vm_area_struct *vma; int ret = SWAP_AGAIN; - spin_lock(&anon_vma->lock); - BUG_ON(list_empty(&anon_vma->head)); + anon_vma = page_lock_anon_vma(page); + if (!anon_vma) + return ret; + list_for_each_entry(vma, &anon_vma->head, anon_vma_node) { ret = try_to_unmap_one(page, vma); - if (ret == SWAP_FAIL || !page->mapcount) + if (ret == SWAP_FAIL || !page_mapped(page)) break; } spin_unlock(&anon_vma->lock); @@ -652,11 +716,8 @@ static inline int try_to_unmap_anon(stru * contained in the address_space struct it points to. * * This function is only called from try_to_unmap for object-based pages. - * - * The spinlock address_space->i_mmap_lock is tried. If it can't be gotten, - * return a temporary error. */ -static inline int try_to_unmap_file(struct page *page) +static int try_to_unmap_file(struct page *page) { struct address_space *mapping = page->mapping; pgoff_t pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT); @@ -668,12 +729,10 @@ static inline int try_to_unmap_file(stru unsigned long max_nl_size = 0; unsigned int mapcount; - if (!spin_trylock(&mapping->i_mmap_lock)) - return ret; - + spin_lock(&mapping->i_mmap_lock); vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) { ret = try_to_unmap_one(page, vma); - if (ret == SWAP_FAIL || !page->mapcount) + if (ret == SWAP_FAIL || !page_mapped(page)) goto out; } @@ -692,8 +751,10 @@ static inline int try_to_unmap_file(stru max_nl_size = cursor; } - if (max_nl_size == 0) /* any nonlinears locked or reserved */ + if (max_nl_size == 0) { /* any nonlinears locked or reserved */ + ret = SWAP_FAIL; goto out; + } /* * We don't try to search for this page in the nonlinear vmas, @@ -702,8 +763,9 @@ static inline int try_to_unmap_file(stru * The mapcount of the page we came in with is irrelevant, * but even so use it as a guide to how hard we should try? */ - mapcount = page->mapcount; - page_map_unlock(page); + mapcount = page_mapcount(page); + if (!mapcount) + goto out; cond_resched_lock(&mapping->i_mmap_lock); max_nl_size = (max_nl_size + CLUSTER_SIZE - 1) & CLUSTER_MASK; @@ -719,19 +781,13 @@ static inline int try_to_unmap_file(stru while (vma->vm_mm->rss && cursor < max_nl_cursor && cursor < vma->vm_end - vma->vm_start) { - ret = try_to_unmap_cluster( - cursor, &mapcount, vma); - if (ret == SWAP_FAIL) - break; + try_to_unmap_cluster(cursor, &mapcount, vma); cursor += CLUSTER_SIZE; vma->vm_private_data = (void *) cursor; if ((int)mapcount <= 0) - goto relock; + goto out; } - if (ret != SWAP_FAIL) - vma->vm_private_data = - (void *) max_nl_cursor; - ret = SWAP_AGAIN; + vma->vm_private_data = (void *) max_nl_cursor; } cond_resched_lock(&mapping->i_mmap_lock); max_nl_cursor += CLUSTER_SIZE; @@ -747,8 +803,6 @@ static inline int try_to_unmap_file(stru if (!(vma->vm_flags & VM_RESERVED)) vma->vm_private_data = NULL; } -relock: - page_map_lock(page); out: spin_unlock(&mapping->i_mmap_lock); return ret; @@ -759,11 +813,11 @@ out: * @page: the page to get unmapped * * Tries to remove all the page table entries which are mapping this - * page, used in the pageout path. Caller must hold the page lock - * and its rmap lock. Return values are: + * page, used in the pageout path. Caller must hold the page lock. + * Return values are: * * SWAP_SUCCESS - we succeeded in removing all mappings - * SWAP_AGAIN - we missed a trylock, try again later + * SWAP_AGAIN - we missed a mapping, try again later * SWAP_FAIL - the page is unswappable */ int try_to_unmap(struct page *page) @@ -772,20 +826,13 @@ int try_to_unmap(struct page *page) BUG_ON(PageReserved(page)); BUG_ON(!PageLocked(page)); - BUG_ON(!page->mapcount); if (PageAnon(page)) ret = try_to_unmap_anon(page); else ret = try_to_unmap_file(page); - if (!page->mapcount) { - if (page_test_and_clear_dirty(page)) - set_page_dirty(page); - if (PageAnon(page)) - clear_page_anon(page); - dec_page_state(nr_mapped); + if (!page_mapped(page)) ret = SWAP_SUCCESS; - } return ret; } --- linux-2.6.9-rc1/mm/shmem.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/mm/shmem.c 2004-09-03 00:57:16.453537944 -0700 @@ -179,7 +179,7 @@ static struct backing_dev_info shmem_bac .unplug_io_fn = default_unplug_io_fn, }; -LIST_HEAD(shmem_inodes); +static LIST_HEAD(shmem_inodes); static spinlock_t shmem_ilock = SPIN_LOCK_UNLOCKED; static void shmem_free_block(struct inode *inode) @@ -1332,7 +1332,8 @@ shmem_file_write(struct file *file, cons __get_user(dummy, buf + bytes - 1); kaddr = kmap_atomic(page, KM_USER0); - left = __copy_from_user(kaddr + offset, buf, bytes); + left = __copy_from_user_inatomic(kaddr + offset, + buf, bytes); kunmap_atomic(kaddr, KM_USER0); } if (left) { --- linux-2.6.9-rc1/mm/slab.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/mm/slab.c 2004-09-03 00:57:16.719497512 -0700 @@ -91,10 +91,12 @@ #include #include #include +#include #include #include #include +#include /* * DEBUG - 1 for kmem_cache_create() to honour; SLAB_DEBUG_INITIAL, @@ -139,11 +141,13 @@ SLAB_POISON | SLAB_HWCACHE_ALIGN | \ SLAB_NO_REAP | SLAB_CACHE_DMA | \ SLAB_MUST_HWCACHE_ALIGN | SLAB_STORE_USER | \ - SLAB_RECLAIM_ACCOUNT | SLAB_PANIC) + SLAB_RECLAIM_ACCOUNT | SLAB_PANIC | \ + SLAB_DESTROY_BY_RCU) #else # define CREATE_MASK (SLAB_HWCACHE_ALIGN | SLAB_NO_REAP | \ SLAB_CACHE_DMA | SLAB_MUST_HWCACHE_ALIGN | \ - SLAB_RECLAIM_ACCOUNT | SLAB_PANIC) + SLAB_RECLAIM_ACCOUNT | SLAB_PANIC | \ + SLAB_DESTROY_BY_RCU) #endif /* @@ -190,6 +194,28 @@ struct slab { }; /* + * struct slab_rcu + * + * slab_destroy on a SLAB_DESTROY_BY_RCU cache uses this structure to + * arrange for kmem_freepages to be called via RCU. This is useful if + * we need to approach a kernel structure obliquely, from its address + * obtained without the usual locking. We can lock the structure to + * stabilize it and check it's still at the given address, only if we + * can be sure that the memory has not been meanwhile reused for some + * other kind of object (which our subsystem's lock might corrupt). + * + * rcu_read_lock before reading the address, then rcu_read_unlock after + * taking the spinlock within the structure expected at that address. + * + * We assume struct slab_rcu can overlay struct slab when destroying. + */ +struct slab_rcu { + struct rcu_head head; + kmem_cache_t *cachep; + void *addr; +}; + +/* * struct array_cache * * Per cpu structures @@ -478,8 +504,10 @@ static struct cache_names __initdata cac #undef CACHE }; -struct arraycache_init initarray_cache __initdata = { { 0, BOOT_CPUCACHE_ENTRIES, 1, 0} }; -struct arraycache_init initarray_generic __initdata = { { 0, BOOT_CPUCACHE_ENTRIES, 1, 0} }; +static struct arraycache_init initarray_cache __initdata = + { { 0, BOOT_CPUCACHE_ENTRIES, 1, 0} }; +static struct arraycache_init initarray_generic __initdata = + { { 0, BOOT_CPUCACHE_ENTRIES, 1, 0} }; /* internal cache of cache description objs */ static kmem_cache_t cache_cache = { @@ -497,8 +525,7 @@ static kmem_cache_t cache_cache = { /* Guard access to the cache-chain. */ static struct semaphore cache_chain_sem; - -struct list_head cache_chain; +static struct list_head cache_chain; /* * vm_enough_memory() looks at this to determine how many @@ -513,7 +540,7 @@ EXPORT_SYMBOL(slab_reclaim_pages); * chicken and egg problem: delay the per-cpu array allocation * until the general caches are up. */ -enum { +static enum { NONE, PARTIAL, FULL @@ -796,7 +823,7 @@ void __init kmem_cache_init(void) */ } -int __init cpucache_init(void) +static int __init cpucache_init(void) { int cpu; @@ -873,6 +900,16 @@ static void kmem_freepages(kmem_cache_t atomic_sub(1<gfporder, &slab_reclaim_pages); } +static void kmem_rcu_free(struct rcu_head *head) +{ + struct slab_rcu *slab_rcu = (struct slab_rcu *) head; + kmem_cache_t *cachep = slab_rcu->cachep; + + kmem_freepages(cachep, slab_rcu->addr); + if (OFF_SLAB(cachep)) + kmem_cache_free(cachep->slabp_cache, slab_rcu); +} + #if DEBUG #ifdef CONFIG_DEBUG_PAGEALLOC @@ -928,9 +965,10 @@ static void dump_line(char *data, int of } #endif +#if DEBUG + static void print_objinfo(kmem_cache_t *cachep, void *objp, int lines) { -#if DEBUG int i, size; char *realobj; @@ -941,8 +979,10 @@ static void print_objinfo(kmem_cache_t * } if (cachep->flags & SLAB_STORE_USER) { - printk(KERN_ERR "Last user: [<%p>]", *dbg_userword(cachep, objp)); - print_symbol("(%s)", (unsigned long)*dbg_userword(cachep, objp)); + printk(KERN_ERR "Last user: [<%p>]", + *dbg_userword(cachep, objp)); + print_symbol("(%s)", + (unsigned long)*dbg_userword(cachep, objp)); printk("\n"); } realobj = (char*)objp+obj_dbghead(cachep); @@ -954,11 +994,8 @@ static void print_objinfo(kmem_cache_t * limit = size-i; dump_line(realobj, i, limit); } -#endif } -#if DEBUG - static void check_poison_obj(kmem_cache_t *cachep, void *objp) { char *realobj; @@ -1026,6 +1063,8 @@ static void check_poison_obj(kmem_cache_ */ static void slab_destroy (kmem_cache_t *cachep, struct slab *slabp) { + void *addr = slabp->s_mem - slabp->colouroff; + #if DEBUG int i; for (i = 0; i < cachep->num; i++) { @@ -1061,10 +1100,19 @@ static void slab_destroy (kmem_cache_t * } } #endif - - kmem_freepages(cachep, slabp->s_mem-slabp->colouroff); - if (OFF_SLAB(cachep)) - kmem_cache_free(cachep->slabp_cache, slabp); + + if (unlikely(cachep->flags & SLAB_DESTROY_BY_RCU)) { + struct slab_rcu *slab_rcu; + + slab_rcu = (struct slab_rcu *) slabp; + slab_rcu->cachep = cachep; + slab_rcu->addr = addr; + call_rcu(&slab_rcu->head, kmem_rcu_free); + } else { + kmem_freepages(cachep, addr); + if (OFF_SLAB(cachep)) + kmem_cache_free(cachep->slabp_cache, slabp); + } } /** @@ -1139,9 +1187,15 @@ kmem_cache_create (const char *name, siz */ if ((size < 4096 || fls(size-1) == fls(size-1+3*BYTES_PER_WORD))) flags |= SLAB_RED_ZONE|SLAB_STORE_USER; - flags |= SLAB_POISON; + if (!(flags & SLAB_DESTROY_BY_RCU)) + flags |= SLAB_POISON; #endif + if (flags & SLAB_DESTROY_BY_RCU) + BUG_ON(flags & SLAB_POISON); #endif + if (flags & SLAB_DESTROY_BY_RCU) + BUG_ON(dtor); + /* * Always checks flags, a caller might be expecting debug * support which isn't available. @@ -1553,6 +1607,9 @@ int kmem_cache_destroy (kmem_cache_t * c return 1; } + if (unlikely(cachep->flags & SLAB_DESTROY_BY_RCU)) + synchronize_kernel(); + /* no cpu_online check required here since we clear the percpu * array on cpu offline and set this to NULL. */ @@ -2424,6 +2481,27 @@ void kmem_cache_free (kmem_cache_t *cach EXPORT_SYMBOL(kmem_cache_free); /** + * kcalloc - allocate memory for an array. The memory is set to zero. + * @n: number of elements. + * @size: element size. + * @flags: the type of memory to allocate. + */ +void *kcalloc(size_t n, size_t size, int flags) +{ + void *ret = NULL; + + if (n != 0 && size > INT_MAX / n) + return ret; + + ret = kmalloc(n * size, flags); + if (ret) + memset(ret, 0, n * size); + return ret; +} + +EXPORT_SYMBOL(kcalloc); + +/** * kfree - free previously allocated memory * @objp: pointer returned by kmalloc. * @@ -2947,72 +3025,3 @@ unsigned int ksize(const void *objp) return size; } - -void ptrinfo(unsigned long addr) -{ - struct page *page; - - printk("Dumping data about address %p.\n", (void*)addr); - if (!virt_addr_valid((void*)addr)) { - printk("virt addr invalid.\n"); - return; - } -#ifdef CONFIG_MMU - do { - pgd_t *pgd = pgd_offset_k(addr); - pmd_t *pmd; - if (pgd_none(*pgd)) { - printk("No pgd.\n"); - break; - } - pmd = pmd_offset(pgd, addr); - if (pmd_none(*pmd)) { - printk("No pmd.\n"); - break; - } -#ifdef CONFIG_X86 - if (pmd_large(*pmd)) { - printk("Large page.\n"); - break; - } -#endif - printk("normal page, pte_val 0x%llx\n", - (unsigned long long)pte_val(*pte_offset_kernel(pmd, addr))); - } while(0); -#endif - - page = virt_to_page((void*)addr); - printk("struct page at %p, flags %08lx\n", - page, (unsigned long)page->flags); - if (PageSlab(page)) { - kmem_cache_t *c; - struct slab *s; - unsigned long flags; - int objnr; - void *objp; - - c = GET_PAGE_CACHE(page); - printk("belongs to cache %s.\n",c->name); - - spin_lock_irqsave(&c->spinlock, flags); - s = GET_PAGE_SLAB(page); - printk("slabp %p with %d inuse objects (from %d).\n", - s, s->inuse, c->num); - check_slabp(c,s); - - objnr = (addr-(unsigned long)s->s_mem)/c->objsize; - objp = s->s_mem+c->objsize*objnr; - printk("points into object no %d, starting at %p, len %d.\n", - objnr, objp, c->objsize); - if (objnr >= c->num) { - printk("Bad obj number.\n"); - } else { - kernel_map_pages(virt_to_page(objp), - c->objsize/PAGE_SIZE, 1); - - print_objinfo(c, objp, 2); - } - spin_unlock_irqrestore(&c->spinlock, flags); - - } -} --- linux-2.6.9-rc1/mm/swap.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/mm/swap.c 2004-09-03 00:57:02.696629312 -0700 @@ -258,6 +258,7 @@ void __pagevec_release(struct pagevec *p release_pages(pvec->pages, pagevec_count(pvec), pvec->cold); pagevec_reinit(pvec); } +EXPORT_SYMBOL(__pagevec_release); /* * pagevec_release() for pages which are known to not be on the LRU @@ -387,7 +388,7 @@ unsigned pagevec_lookup_tag(struct pagev nr_pages, pvec->pages); return pagevec_count(pvec); } - +EXPORT_SYMBOL(pagevec_lookup_tag); #ifdef CONFIG_SMP /* --- linux-2.6.9-rc1/mm/swapfile.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/mm/swapfile.c 2004-09-03 00:56:37.642438128 -0700 @@ -290,10 +290,10 @@ static int exclusive_swap_page(struct pa /* Is the only swap cache user the cache itself? */ if (p->swap_map[swp_offset(entry)] == 1) { /* Recheck the page count with the swapcache lock held.. */ - spin_lock_irq(&swapper_space.tree_lock); + write_lock_irq(&swapper_space.tree_lock); if (page_count(page) == 2) retval = 1; - spin_unlock_irq(&swapper_space.tree_lock); + write_unlock_irq(&swapper_space.tree_lock); } swap_info_put(p); } @@ -361,13 +361,13 @@ int remove_exclusive_swap_page(struct pa retval = 0; if (p->swap_map[swp_offset(entry)] == 1) { /* Recheck the page count with the swapcache lock held.. */ - spin_lock_irq(&swapper_space.tree_lock); + write_lock_irq(&swapper_space.tree_lock); if ((page_count(page) == 2) && !PageWriteback(page)) { __delete_from_swap_cache(page); SetPageDirty(page); retval = 1; } - spin_unlock_irq(&swapper_space.tree_lock); + write_unlock_irq(&swapper_space.tree_lock); } swap_info_put(p); @@ -391,12 +391,12 @@ void free_swap_and_cache(swp_entry_t ent p = swap_info_get(entry); if (p) { if (swap_entry_free(p, swp_offset(entry)) == 1) { - spin_lock_irq(&swapper_space.tree_lock); + read_lock_irq(&swapper_space.tree_lock); page = radix_tree_lookup(&swapper_space.page_tree, entry.val); if (page && TestSetPageLocked(page)) page = NULL; - spin_unlock_irq(&swapper_space.tree_lock); + read_unlock_irq(&swapper_space.tree_lock); } swap_info_put(p); } @@ -520,14 +520,24 @@ static unsigned long unuse_pgd(struct vm } /* vma->vm_mm->page_table_lock is held */ -static unsigned long unuse_vma(struct vm_area_struct * vma, pgd_t *pgdir, +static unsigned long unuse_vma(struct vm_area_struct * vma, swp_entry_t entry, struct page *page) { - unsigned long start = vma->vm_start, end = vma->vm_end; + pgd_t *pgdir; + unsigned long start, end; unsigned long foundaddr; - if (start >= end) - BUG(); + if (page->mapping) { + start = page_address_in_vma(page, vma); + if (start == -EFAULT) + return 0; + else + end = start + PAGE_SIZE; + } else { + start = vma->vm_start; + end = vma->vm_end; + } + pgdir = pgd_offset(vma->vm_mm, start); do { foundaddr = unuse_pgd(vma, pgdir, start, end - start, entry, page); @@ -559,9 +569,8 @@ static int unuse_process(struct mm_struc } spin_lock(&mm->page_table_lock); for (vma = mm->mmap; vma; vma = vma->vm_next) { - if (!is_vm_hugetlb_page(vma)) { - pgd_t * pgd = pgd_offset(mm, vma->vm_start); - foundaddr = unuse_vma(vma, pgd, entry, page); + if (vma->anon_vma) { + foundaddr = unuse_vma(vma, entry, page); if (foundaddr) break; } --- linux-2.6.9-rc1/mm/swap_state.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/mm/swap_state.c 2004-09-03 00:56:37.643437976 -0700 @@ -35,7 +35,7 @@ static struct backing_dev_info swap_back struct address_space swapper_space = { .page_tree = RADIX_TREE_INIT(GFP_ATOMIC), - .tree_lock = SPIN_LOCK_UNLOCKED, + .tree_lock = RW_LOCK_UNLOCKED, .a_ops = &swap_aops, .i_mmap_nonlinear = LIST_HEAD_INIT(swapper_space.i_mmap_nonlinear), .backing_dev_info = &swap_backing_dev_info, @@ -74,7 +74,7 @@ static int __add_to_swap_cache(struct pa BUG_ON(PagePrivate(page)); error = radix_tree_preload(gfp_mask); if (!error) { - spin_lock_irq(&swapper_space.tree_lock); + write_lock_irq(&swapper_space.tree_lock); error = radix_tree_insert(&swapper_space.page_tree, entry.val, page); if (!error) { @@ -85,7 +85,7 @@ static int __add_to_swap_cache(struct pa total_swapcache_pages++; pagecache_acct(1); } - spin_unlock_irq(&swapper_space.tree_lock); + write_unlock_irq(&swapper_space.tree_lock); radix_tree_preload_end(); } return error; @@ -171,7 +171,7 @@ int add_to_swap(struct page * page) /* * Add it to the swap cache and mark it dirty */ - err = __add_to_swap_cache(page, entry, GFP_ATOMIC); + err = __add_to_swap_cache(page, entry, GFP_ATOMIC|__GFP_NOWARN); if (pf_flags & PF_MEMALLOC) current->flags |= PF_MEMALLOC; @@ -212,9 +212,9 @@ void delete_from_swap_cache(struct page entry.val = page->private; - spin_lock_irq(&swapper_space.tree_lock); + write_lock_irq(&swapper_space.tree_lock); __delete_from_swap_cache(page); - spin_unlock_irq(&swapper_space.tree_lock); + write_unlock_irq(&swapper_space.tree_lock); swap_free(entry); page_cache_release(page); @@ -313,13 +313,13 @@ struct page * lookup_swap_cache(swp_entr { struct page *page; - spin_lock_irq(&swapper_space.tree_lock); + read_lock_irq(&swapper_space.tree_lock); page = radix_tree_lookup(&swapper_space.page_tree, entry.val); if (page) { page_cache_get(page); INC_CACHE_INFO(find_success); } - spin_unlock_irq(&swapper_space.tree_lock); + read_unlock_irq(&swapper_space.tree_lock); INC_CACHE_INFO(find_total); return page; } @@ -342,12 +342,12 @@ struct page *read_swap_cache_async(swp_e * called after lookup_swap_cache() failed, re-calling * that would confuse statistics. */ - spin_lock_irq(&swapper_space.tree_lock); + read_lock_irq(&swapper_space.tree_lock); found_page = radix_tree_lookup(&swapper_space.page_tree, entry.val); if (found_page) page_cache_get(found_page); - spin_unlock_irq(&swapper_space.tree_lock); + read_unlock_irq(&swapper_space.tree_lock); if (found_page) break; --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/mm/tiny-shmem.c 2004-09-02 20:51:53.000000000 -0700 @@ -0,0 +1,124 @@ +/* + * tiny-shmem.c: simple shmemfs and tmpfs using ramfs code + * + * Matt Mackall January, 2004 + * derived from mm/shmem.c and fs/ramfs/inode.c + * + * This is intended for small system where the benefits of the full + * shmem code (swap-backed and resource-limited) are outweighed by + * their complexity. On systems without swap this code should be + * effectively equivalent, but much lighter weight. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct file_system_type tmpfs_fs_type = { + .name = "tmpfs", + .get_sb = ramfs_get_sb, + .kill_sb = kill_litter_super, +}; + +static struct vfsmount *shm_mnt; + +static int __init init_tmpfs(void) +{ + register_filesystem(&tmpfs_fs_type); +#ifdef CONFIG_TMPFS + devfs_mk_dir("shm"); +#endif + shm_mnt = kern_mount(&tmpfs_fs_type); + return 0; +} +module_init(init_tmpfs) + +/* + * shmem_file_setup - get an unlinked file living in tmpfs + * + * @name: name for dentry (to be seen in /proc//maps + * @size: size to be set for the file + * + */ +struct file *shmem_file_setup(char *name, loff_t size, unsigned long flags) +{ + int error; + struct file *file; + struct inode *inode; + struct dentry *dentry, *root; + struct qstr this; + + if (IS_ERR(shm_mnt)) + return (void *)shm_mnt; + + error = -ENOMEM; + this.name = name; + this.len = strlen(name); + this.hash = 0; /* will go */ + root = shm_mnt->mnt_root; + dentry = d_alloc(root, &this); + if (!dentry) + goto put_memory; + + error = -ENFILE; + file = get_empty_filp(); + if (!file) + goto put_dentry; + + error = -ENOSPC; + inode = ramfs_get_inode(root->d_sb, S_IFREG | S_IRWXUGO, 0); + if (!inode) + goto close_file; + + d_instantiate(dentry, inode); + inode->i_size = size; + inode->i_nlink = 0; /* It is unlinked */ + file->f_vfsmnt = mntget(shm_mnt); + file->f_dentry = dentry; + file->f_mapping = inode->i_mapping; + file->f_op = &ramfs_file_operations; + file->f_mode = FMODE_WRITE | FMODE_READ; + return file; + +close_file: + put_filp(file); +put_dentry: + dput(dentry); +put_memory: + return ERR_PTR(error); +} + +/* + * shmem_zero_setup - setup a shared anonymous mapping + * + * @vma: the vma to be mmapped is prepared by do_mmap_pgoff + */ +int shmem_zero_setup(struct vm_area_struct *vma) +{ + struct file *file; + loff_t size = vma->vm_end - vma->vm_start; + + file = shmem_file_setup("dev/zero", size, vma->vm_flags); + if (IS_ERR(file)) + return PTR_ERR(file); + + if (vma->vm_file) + fput(vma->vm_file); + vma->vm_file = file; + vma->vm_ops = &generic_file_vm_ops; + return 0; +} + +int shmem_unuse(swp_entry_t entry, struct page *page) +{ + return 0; +} + +EXPORT_SYMBOL(shmem_file_setup); --- linux-2.6.9-rc1/mm/truncate.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/mm/truncate.c 2004-09-03 00:57:01.931745592 -0700 @@ -74,25 +74,35 @@ invalidate_complete_page(struct address_ if (PagePrivate(page) && !try_to_release_page(page, 0)) return 0; - spin_lock_irq(&mapping->tree_lock); + /* + * file system may manually remove page from the page + * cache in ->releasepage(). Check for this. + */ + if (page->mapping != mapping) + return 0; + + write_lock_irq(&mapping->tree_lock); if (PageDirty(page)) { - spin_unlock_irq(&mapping->tree_lock); + write_unlock_irq(&mapping->tree_lock); return 0; } __remove_from_page_cache(page); - spin_unlock_irq(&mapping->tree_lock); + write_unlock_irq(&mapping->tree_lock); ClearPageUptodate(page); page_cache_release(page); /* pagecache ref */ return 1; } /** - * truncate_inode_pages - truncate *all* the pages from an offset + * truncate_inode_pages - truncate range of pages specified by start and + * end byte offsets * @mapping: mapping to truncate * @lstart: offset from which to truncate + * @lend: offset to which to truncate * - * Truncate the page cache at a set offset, removing the pages that are beyond - * that offset (and zeroing out partial pages). + * Truncate the page cache, removing the pages that are between + * specified offsets (and zeroing out partial page + * (if lstart is not page aligned)). * * Truncate takes two passes - the first pass is nonblocking. It will not * block on page locks and it will not block on writeback. The second pass @@ -106,12 +116,12 @@ invalidate_complete_page(struct address_ * We pass down the cache-hot hint to the page freeing code. Even if the * mapping is large, it is probably the case that the final pages are the most * recently touched, and freeing happens in ascending file offset order. - * - * Called under (and serialised by) inode->i_sem. */ -void truncate_inode_pages(struct address_space *mapping, loff_t lstart) +void truncate_inode_pages_range(struct address_space *mapping, + loff_t lstart, loff_t lend) { const pgoff_t start = (lstart + PAGE_CACHE_SIZE-1) >> PAGE_CACHE_SHIFT; + pgoff_t end; const unsigned partial = lstart & (PAGE_CACHE_SIZE - 1); struct pagevec pvec; pgoff_t next; @@ -120,13 +130,22 @@ void truncate_inode_pages(struct address if (mapping->nrpages == 0) return; + BUG_ON((lend & (PAGE_CACHE_SIZE - 1)) != (PAGE_CACHE_SIZE - 1)); + end = (lend >> PAGE_CACHE_SHIFT); + pagevec_init(&pvec, 0); next = start; - while (pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) { + while (next <= end && + pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) { for (i = 0; i < pagevec_count(&pvec); i++) { struct page *page = pvec.pages[i]; pgoff_t page_index = page->index; + if (page_index > end) { + next = page_index; + break; + } + if (page_index > next) next = page_index; next++; @@ -155,15 +174,22 @@ void truncate_inode_pages(struct address next = start; for ( ; ; ) { + cond_resched(); if (!pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) { if (next == start) break; next = start; continue; } + if (pvec.pages[0]->index > end) { + pagevec_release(&pvec); + break; + } for (i = 0; i < pagevec_count(&pvec); i++) { struct page *page = pvec.pages[i]; + if (page->index > end) + break; lock_page(page); wait_on_page_writeback(page); if (page->index > next) @@ -175,7 +201,19 @@ void truncate_inode_pages(struct address pagevec_release(&pvec); } } +EXPORT_SYMBOL(truncate_inode_pages_range); +/** + * truncate_inode_pages - truncate *all* the pages from an offset + * @mapping: mapping to truncate + * @lstart: offset from which to truncate + * + * Called under (and serialised by) inode->i_sem. + */ +void truncate_inode_pages(struct address_space *mapping, loff_t lstart) +{ + truncate_inode_pages_range(mapping, lstart, (loff_t)-1); +} EXPORT_SYMBOL(truncate_inode_pages); /** --- linux-2.6.9-rc1/mm/vmalloc.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/mm/vmalloc.c 2004-09-02 20:51:53.000000000 -0700 @@ -179,11 +179,26 @@ int map_vm_area(struct vm_struct *area, return err; } +#define IOREMAP_MAX_ORDER (7 + PAGE_SHIFT) /* 128 pages */ + struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags, unsigned long start, unsigned long end) { struct vm_struct **p, *tmp, *area; - unsigned long addr = start; + unsigned long align = 1; + unsigned long addr; + + if (flags & VM_IOREMAP) { + int bit = fls(size); + + if (bit > IOREMAP_MAX_ORDER) + bit = IOREMAP_MAX_ORDER; + else if (bit < PAGE_SHIFT) + bit = PAGE_SHIFT; + + align = 1ul << bit; + } + addr = ALIGN(start, align); area = kmalloc(sizeof(*area), GFP_KERNEL); if (unlikely(!area)) @@ -200,13 +215,17 @@ struct vm_struct *__get_vm_area(unsigned write_lock(&vmlist_lock); for (p = &vmlist; (tmp = *p) != NULL ;p = &tmp->next) { - if ((unsigned long)tmp->addr < addr) + if ((unsigned long)tmp->addr < addr) { + if((unsigned long)tmp->addr + tmp->size >= addr) + addr = ALIGN(tmp->size + + (unsigned long)tmp->addr, align); continue; + } if ((size + addr) < addr) goto out; if (size + addr <= (unsigned long)tmp->addr) goto found; - addr = tmp->size + (unsigned long)tmp->addr; + addr = ALIGN(tmp->size + (unsigned long)tmp->addr, align); if (addr > end - size) goto out; } --- linux-2.6.9-rc1/mm/vmscan.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/mm/vmscan.c 2004-09-03 01:17:56.768981336 -0700 @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -219,7 +220,7 @@ static int shrink_slab(unsigned long sca return 0; } -/* Must be called with page's rmap lock held. */ +/* Called without lock on whether page is mapped, so answer is unstable */ static inline int page_mapping_inuse(struct page *page) { struct address_space *mapping; @@ -377,26 +378,19 @@ static int shrink_list(struct list_head if (page_mapped(page) || PageSwapCache(page)) sc->nr_scanned++; - page_map_lock(page); - referenced = page_referenced(page); - if (referenced && page_mapping_inuse(page)) { - /* In active use or really unfreeable. Activate it. */ - page_map_unlock(page); + referenced = page_referenced(page, 1); + /* In active use or really unfreeable? Activate it. */ + if (referenced && page_mapping_inuse(page)) goto activate_locked; - } #ifdef CONFIG_SWAP /* * Anonymous process memory has backing store? * Try to allocate it some swap space here. - * - * XXX: implement swap clustering ? */ if (PageAnon(page) && !PageSwapCache(page)) { - page_map_unlock(page); if (!add_to_swap(page)) goto activate_locked; - page_map_lock(page); } #endif /* CONFIG_SWAP */ @@ -411,16 +405,13 @@ static int shrink_list(struct list_head if (page_mapped(page) && mapping) { switch (try_to_unmap(page)) { case SWAP_FAIL: - page_map_unlock(page); goto activate_locked; case SWAP_AGAIN: - page_map_unlock(page); goto keep_locked; case SWAP_SUCCESS: ; /* try to free the page below */ } } - page_map_unlock(page); if (PageDirty(page)) { if (referenced) @@ -477,6 +468,11 @@ static int shrink_list(struct list_head if (PagePrivate(page)) { if (!try_to_release_page(page, sc->gfp_mask)) goto activate_locked; + /* + * file system may manually remove page from the page + * cache in ->releasepage(). Check for this. + */ + mapping = page_mapping(page); if (!mapping && page_count(page) == 1) goto free_it; } @@ -484,7 +480,7 @@ static int shrink_list(struct list_head if (!mapping) goto keep_locked; /* truncate got there first */ - spin_lock_irq(&mapping->tree_lock); + write_lock_irq(&mapping->tree_lock); /* * The non-racy check for busy page. It is critical to check @@ -492,7 +488,7 @@ static int shrink_list(struct list_head * not in use by anybody. (pagecache + us == 2) */ if (page_count(page) != 2 || PageDirty(page)) { - spin_unlock_irq(&mapping->tree_lock); + write_unlock_irq(&mapping->tree_lock); goto keep_locked; } @@ -500,7 +496,7 @@ static int shrink_list(struct list_head if (PageSwapCache(page)) { swp_entry_t swap = { .val = page->private }; __delete_from_swap_cache(page); - spin_unlock_irq(&mapping->tree_lock); + write_unlock_irq(&mapping->tree_lock); swap_free(swap); __put_page(page); /* The pagecache ref */ goto free_it; @@ -508,7 +504,7 @@ static int shrink_list(struct list_head #endif /* CONFIG_SWAP */ __remove_from_page_cache(page); - spin_unlock_irq(&mapping->tree_lock); + write_unlock_irq(&mapping->tree_lock); __put_page(page); free_it: @@ -723,25 +719,12 @@ refill_inactive_zone(struct zone *zone, page = lru_to_page(&l_hold); list_del(&page->lru); if (page_mapped(page)) { - if (!reclaim_mapped) { - list_add(&page->lru, &l_active); - continue; - } - page_map_lock(page); - if (page_referenced(page)) { - page_map_unlock(page); + if (!reclaim_mapped || + (total_swap_pages == 0 && PageAnon(page)) || + page_referenced(page, 0)) { list_add(&page->lru, &l_active); continue; } - page_map_unlock(page); - } - /* - * FIXME: need to consider page_count(page) here if/when we - * reap orphaned pages via the LRU (Daniel's locking stuff) - */ - if (total_swap_pages == 0 && PageAnon(page)) { - list_add(&page->lru, &l_active); - continue; } list_add(&page->lru, &l_inactive); } @@ -848,6 +831,8 @@ shrink_zone(struct zone *zone, struct sc break; } } + + throttle_vm_writeout(); } /* @@ -874,6 +859,9 @@ shrink_caches(struct zone **zones, struc for (i = 0; zones[i] != NULL; i++) { struct zone *zone = zones[i]; + if (!cpuset_zone_allowed(zone)) + continue; + zone->temp_priority = sc->priority; if (zone->prev_priority > sc->priority) zone->prev_priority = sc->priority; @@ -917,6 +905,9 @@ int try_to_free_pages(struct zone **zone for (i = 0; zones[i] != NULL; i++) { struct zone *zone = zones[i]; + if (!cpuset_zone_allowed(zone)) + continue; + zone->temp_priority = DEF_PRIORITY; lru_pages += zone->nr_active + zone->nr_inactive; } @@ -958,8 +949,14 @@ int try_to_free_pages(struct zone **zone if ((gfp_mask & __GFP_FS) && !(gfp_mask & __GFP_NORETRY)) out_of_memory(gfp_mask); out: - for (i = 0; zones[i] != 0; i++) - zones[i]->prev_priority = zones[i]->temp_priority; + for (i = 0; zones[i] != 0; i++) { + struct zone *zone = zones[i]; + + if (!cpuset_zone_allowed(zone)) + continue; + + zone->prev_priority = zone->temp_priority; + } return ret; } @@ -1118,7 +1115,7 @@ out: * If there are applications that are active memory-allocators * (most normal use), this basically shouldn't matter. */ -static int kswapd(void *p) +int kswapd(void *p) { pg_data_t *pgdat = (pg_data_t*)p; struct task_struct *tsk = current; @@ -1167,6 +1164,8 @@ void wakeup_kswapd(struct zone *zone) { if (zone->free_pages > zone->pages_low) return; + if (!cpuset_zone_allowed(zone)) + return; if (!waitqueue_active(&zone->zone_pgdat->kswapd_wait)) return; wake_up_interruptible(&zone->zone_pgdat->kswapd_wait); --- linux-2.6.9-rc1/net/atm/mpoa_proc.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/net/atm/mpoa_proc.c 2004-09-02 20:51:53.000000000 -0700 @@ -121,7 +121,7 @@ static void mpc_stop(struct seq_file *m, /* * READING function - called when the /proc/atm/mpoa file is read from. */ -static ssize_t mpc_show(struct seq_file *m, void *v) +static int mpc_show(struct seq_file *m, void *v) { struct mpoa_client *mpc = v; unsigned char *temp; --- linux-2.6.9-rc1/net/bridge/br_input.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/net/bridge/br_input.c 2004-09-02 20:51:53.000000000 -0700 @@ -45,26 +45,15 @@ static void br_pass_frame_up(struct net_ br_pass_frame_up_finish); } +/* note: already called with rcu_read_lock (preempt_disabled) */ int br_handle_frame_finish(struct sk_buff *skb) { - struct net_bridge *br; - unsigned char *dest; + const unsigned char *dest = skb->mac.ethernet->h_dest; + struct net_bridge_port *p = skb->dev->br_port; + struct net_bridge *br = p->br; struct net_bridge_fdb_entry *dst; - struct net_bridge_port *p; - int passedup; + int passedup = 0; - dest = skb->mac.ethernet->h_dest; - - rcu_read_lock(); - p = rcu_dereference(skb->dev->br_port); - - if (p == NULL || p->state == BR_STATE_DISABLED) { - kfree_skb(skb); - goto out; - } - - br = p->br; - passedup = 0; if (br->dev->flags & IFF_PROMISC) { struct sk_buff *skb2; @@ -99,20 +88,21 @@ int br_handle_frame_finish(struct sk_buf br_flood_forward(br, skb, 0); out: - rcu_read_unlock(); return 0; } -int br_handle_frame(struct sk_buff *skb) +/* + * Called via br_handle_frame_hook. + * Return 0 if *pskb should be processed furthur + * 1 if *pskb is handled + * note: already called with rcu_read_lock (preempt_disabled) + */ +int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb) { - unsigned char *dest; - struct net_bridge_port *p; - - dest = skb->mac.ethernet->h_dest; + struct sk_buff *skb = *pskb; + const unsigned char *dest = skb->mac.ethernet->h_dest; - rcu_read_lock(); - p = skb->dev->br_port; - if (p == NULL || p->state == BR_STATE_DISABLED) + if (p->state == BR_STATE_DISABLED) goto err; if (skb->mac.ethernet->h_source[0] & 1) @@ -128,15 +118,16 @@ int br_handle_frame(struct sk_buff *skb) if (!dest[5]) { NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev, NULL, br_stp_handle_bpdu); - rcu_read_unlock(); - return 0; + return 1; } } else if (p->state == BR_STATE_FORWARDING) { - if (br_should_route_hook && br_should_route_hook(&skb)) { - rcu_read_unlock(); - return -1; + if (br_should_route_hook) { + if (br_should_route_hook(pskb)) + return 0; + skb = *pskb; + dest = skb->mac.ethernet->h_dest; } if (!memcmp(p->br->dev->dev_addr, dest, ETH_ALEN)) @@ -144,12 +135,10 @@ int br_handle_frame(struct sk_buff *skb) NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, br_handle_frame_finish); - rcu_read_unlock(); - return 0; + return 1; } err: - rcu_read_unlock(); kfree_skb(skb); - return 0; + return 1; } --- linux-2.6.9-rc1/net/bridge/br_netfilter.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/net/bridge/br_netfilter.c 2004-09-02 20:51:53.000000000 -0700 @@ -11,6 +11,7 @@ * Jun 19 2003: let arptables see bridged ARP traffic (bdschuym) * Oct 06 2003: filter encapsulated IP/ARP VLAN traffic on untagged bridge * (bdschuym) + * Sep 01 2004: add IPv6 filtering (bdschuym) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -29,9 +30,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include "br_private.h" @@ -39,7 +42,6 @@ #include #endif - #define skb_origaddr(skb) (((struct bridge_skb_cb *) \ (skb->nf_bridge->data))->daddr.ipv4) #define store_orig_dstaddr(skb) (skb_origaddr(skb) = (skb)->nh.iph->daddr) @@ -51,6 +53,7 @@ #ifdef CONFIG_SYSCTL static struct ctl_table_header *brnf_sysctl_header; static int brnf_call_iptables = 1; +static int brnf_call_ip6tables = 1; static int brnf_call_arptables = 1; static int brnf_filter_vlan_tagged = 1; #else @@ -60,6 +63,9 @@ static int brnf_filter_vlan_tagged = 1; #define IS_VLAN_IP (skb->protocol == __constant_htons(ETH_P_8021Q) && \ hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_IP) && \ brnf_filter_vlan_tagged) +#define IS_VLAN_IPV6 (skb->protocol == __constant_htons(ETH_P_8021Q) && \ + hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_IPV6) && \ + brnf_filter_vlan_tagged) #define IS_VLAN_ARP (skb->protocol == __constant_htons(ETH_P_8021Q) && \ hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_ARP) && \ brnf_filter_vlan_tagged) @@ -71,8 +77,7 @@ static int brnf_filter_vlan_tagged = 1; * Currently, we fill in the PMTU entry because netfilter * refragmentation needs it, and the rt_flags entry because * ipt_REJECT needs it. Future netfilter modules might - * require us to fill additional fields. - */ + * require us to fill additional fields. */ static struct net_device __fake_net_device = { .hard_header_len = ETH_HLEN }; @@ -91,6 +96,36 @@ static struct rtable __fake_rtable = { /* PF_BRIDGE/PRE_ROUTING *********************************************/ +/* Undo the changes made for ip6tables PREROUTING and continue the + * bridge PRE_ROUTING hook. */ +static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb) +{ + struct nf_bridge_info *nf_bridge = skb->nf_bridge; + +#ifdef CONFIG_NETFILTER_DEBUG + skb->nf_debug ^= (1 << NF_BR_PRE_ROUTING); +#endif + + if (nf_bridge->mask & BRNF_PKT_TYPE) { + skb->pkt_type = PACKET_OTHERHOST; + nf_bridge->mask ^= BRNF_PKT_TYPE; + } + nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING; + + skb->dst = (struct dst_entry *)&__fake_rtable; + dst_hold(skb->dst); + + skb->dev = nf_bridge->physindev; + if (skb->protocol == __constant_htons(ETH_P_8021Q)) { + skb_push(skb, VLAN_HLEN); + skb->nh.raw -= VLAN_HLEN; + } + NF_HOOK_THRESH(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL, + br_handle_frame_finish, 1); + + return 0; +} + static void __br_dnat_complain(void) { static unsigned long last_complaint; @@ -102,7 +137,6 @@ static void __br_dnat_complain(void) } } - /* This requires some explaining. If DNAT has taken place, * we will need to fix up the destination Ethernet address, * and this is a tricky process. @@ -145,9 +179,7 @@ static void __br_dnat_complain(void) * * --Lennert, 20020411 * --Bart, 20020416 (updated) - * --Bart, 20021007 (updated) - */ - + * --Bart, 20021007 (updated) */ static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb) { #ifdef CONFIG_NETFILTER_DEBUG @@ -195,8 +227,7 @@ static int br_nf_pre_routing_finish(stru if (!ip_route_output_key(&rt, &fl)) { /* Bridged-and-DNAT'ed traffic doesn't - * require ip_forwarding. - */ + * require ip_forwarding. */ if (((struct dst_entry *)rt)->dev == dev) { skb->dst = (struct dst_entry *)rt; goto bridged_dnat; @@ -210,8 +241,7 @@ static int br_nf_pre_routing_finish(stru if (skb->dst->dev == dev) { bridged_dnat: /* Tell br_nf_local_out this is a - * bridged frame - */ + * bridged frame */ nf_bridge->mask |= BRNF_BRIDGED_DNAT; skb->dev = nf_bridge->physindev; if (skb->protocol == @@ -245,12 +275,81 @@ bridged_dnat: return 0; } -/* Replicate the checks that IPv4 does on packet reception. +/* Some common code for IPv4/IPv6 */ +static void setup_pre_routing(struct sk_buff *skb) +{ + struct nf_bridge_info *nf_bridge = skb->nf_bridge; + + if (skb->pkt_type == PACKET_OTHERHOST) { + skb->pkt_type = PACKET_HOST; + nf_bridge->mask |= BRNF_PKT_TYPE; + } + + nf_bridge->mask |= BRNF_NF_BRIDGE_PREROUTING; + nf_bridge->physindev = skb->dev; + skb->dev = bridge_parent(skb->dev); +} + +/* Replicate the checks that IPv6 does on packet reception and pass the packet + * to ip6tables, which doesn't support NAT, so things are fairly simple. */ +static unsigned int br_nf_pre_routing_ipv6(unsigned int hook, + struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, int (*okfn)(struct sk_buff *)) +{ + struct ipv6hdr *hdr; + u32 pkt_len; + struct nf_bridge_info *nf_bridge; + + if (skb->len < sizeof(struct ipv6hdr)) + goto inhdr_error; + + if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) + goto inhdr_error; + + hdr = skb->nh.ipv6h; + + if (hdr->version != 6) + goto inhdr_error; + + pkt_len = ntohs(hdr->payload_len); + + if (pkt_len || hdr->nexthdr != NEXTHDR_HOP) { + if (pkt_len + sizeof(struct ipv6hdr) > skb->len) + goto inhdr_error; + if (pkt_len + sizeof(struct ipv6hdr) < skb->len && + __pskb_trim(skb, pkt_len + sizeof(struct ipv6hdr))) + goto inhdr_error; + } + if (hdr->nexthdr == NEXTHDR_HOP) { + /* We only check the length. A bridge shouldn't do any + * hop-by-hop stuff anyway. */ + unsigned char *raw = (u8*)(skb->nh.ipv6h+1); + + if ((raw + ((int)(raw[1]+1)<<3)) - skb->data > skb_headlen(skb)) + goto inhdr_error; + } +#ifdef CONFIG_NETFILTER_DEBUG + skb->nf_debug ^= (1 << NF_IP6_PRE_ROUTING); +#endif + if ((nf_bridge = nf_bridge_alloc(skb)) == NULL) + return NF_DROP; + setup_pre_routing(skb); + + NF_HOOK(PF_INET6, NF_IP6_PRE_ROUTING, skb, skb->dev, NULL, + br_nf_pre_routing_finish_ipv6); + + return NF_STOLEN; + +inhdr_error: + return NF_DROP; +} + +/* Direct IPv6 traffic to br_nf_pre_routing_ipv6. + * Replicate the checks that IPv4 does on packet reception. * Set skb->dev to the bridge device (i.e. parent of the * receiving device) to make netfilter happy, the REDIRECT * target in particular. Save the original destination IP - * address to be able to detect DNAT afterwards. - */ + * address to be able to detect DNAT afterwards. */ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) @@ -259,25 +358,39 @@ static unsigned int br_nf_pre_routing(un __u32 len; struct sk_buff *skb = *pskb; struct nf_bridge_info *nf_bridge; + struct vlan_ethhdr *hdr = (struct vlan_ethhdr *) + ((*pskb)->mac.ethernet); + + if (skb->protocol == __constant_htons(ETH_P_IPV6) || IS_VLAN_IPV6) { +#ifdef CONFIG_SYSCTL + if (!brnf_call_ip6tables) + return NF_ACCEPT; +#endif + if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL) + goto out; + if (skb->protocol == __constant_htons(ETH_P_8021Q)) { + skb_pull(skb, VLAN_HLEN); + (skb)->nh.raw += VLAN_HLEN; + } + return br_nf_pre_routing_ipv6(hook, skb, in, out, okfn); + } #ifdef CONFIG_SYSCTL if (!brnf_call_iptables) return NF_ACCEPT; #endif - if (skb->protocol != __constant_htons(ETH_P_IP)) { - struct vlan_ethhdr *hdr = (struct vlan_ethhdr *) - ((*pskb)->mac.ethernet); + if (skb->protocol != __constant_htons(ETH_P_IP) && !IS_VLAN_IP) + return NF_ACCEPT; - if (!IS_VLAN_IP) - return NF_ACCEPT; - if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL) - goto out; - skb_pull(*pskb, VLAN_HLEN); - (*pskb)->nh.raw += VLAN_HLEN; - } else if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL) + if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL) goto out; + if (skb->protocol == __constant_htons(ETH_P_8021Q)) { + skb_pull(skb, VLAN_HLEN); + (skb)->nh.raw += VLAN_HLEN; + } + if (!pskb_may_pull(skb, sizeof(struct iphdr))) goto inhdr_error; @@ -305,17 +418,9 @@ static unsigned int br_nf_pre_routing(un #ifdef CONFIG_NETFILTER_DEBUG skb->nf_debug ^= (1 << NF_IP_PRE_ROUTING); #endif - if ((nf_bridge = nf_bridge_alloc(skb)) == NULL) + if ((nf_bridge = nf_bridge_alloc(skb)) == NULL) return NF_DROP; - - if (skb->pkt_type == PACKET_OTHERHOST) { - skb->pkt_type = PACKET_HOST; - nf_bridge->mask |= BRNF_PKT_TYPE; - } - - nf_bridge->mask |= BRNF_NF_BRIDGE_PREROUTING; - nf_bridge->physindev = skb->dev; - skb->dev = bridge_parent(skb->dev); + setup_pre_routing(skb); store_orig_dstaddr(skb); NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL, @@ -336,8 +441,7 @@ out: * packet would pass through PRE_ROUTING again (which already * took place when the packet entered the bridge), but we * register an IPv4 PRE_ROUTING 'sabotage' hook that will - * prevent this from happening. - */ + * prevent this from happening. */ static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) @@ -364,7 +468,7 @@ static int br_nf_forward_finish(struct s skb->nf_debug ^= (1 << NF_BR_FORWARD); #endif - if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP) { + if (skb->protocol != __constant_htons(ETH_P_ARP) && !IS_VLAN_ARP) { in = nf_bridge->physindev; if (nf_bridge->mask & BRNF_PKT_TYPE) { skb->pkt_type = PACKET_OTHERHOST; @@ -385,9 +489,8 @@ static int br_nf_forward_finish(struct s /* This is the 'purely bridged' case. For IP, we pass the packet to * netfilter with indev and outdev set to the bridge device, * but we are still able to filter on the 'real' indev/outdev - * because of the ipt_physdev.c module. For ARP, indev and outdev are the - * bridge ports. - */ + * because of the physdev module. For ARP, indev and outdev are the + * bridge ports. */ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) @@ -395,15 +498,17 @@ static unsigned int br_nf_forward_ip(uns struct sk_buff *skb = *pskb; struct nf_bridge_info *nf_bridge; struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet); + int pf; -#ifdef CONFIG_SYSCTL if (!skb->nf_bridge) return NF_ACCEPT; -#endif - if (skb->protocol != __constant_htons(ETH_P_IP)) { - if (!IS_VLAN_IP) - return NF_ACCEPT; + if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP) + pf = PF_INET; + else + pf = PF_INET6; + + if (skb->protocol == __constant_htons(ETH_P_8021Q)) { skb_pull(*pskb, VLAN_HLEN); (*pskb)->nh.raw += VLAN_HLEN; } @@ -421,7 +526,7 @@ static unsigned int br_nf_forward_ip(uns nf_bridge->mask |= BRNF_BRIDGED; nf_bridge->physoutdev = skb->dev; - NF_HOOK(PF_INET, NF_IP_FORWARD, skb, bridge_parent(in), + NF_HOOK(pf, NF_IP_FORWARD, skb, bridge_parent(in), bridge_parent(out), br_nf_forward_finish); return NF_STOLEN; @@ -483,11 +588,10 @@ static int br_nf_local_out_finish(struct return 0; } - /* This function sees both locally originated IP packets and forwarded * IP packets (in both cases the destination device is a bridge * device). It also sees bridged-and-DNAT'ed packets. - * To be able to filter on the physical bridge devices (with the ipt_physdev.c + * To be able to filter on the physical bridge devices (with the physdev * module), we steal packets destined to a bridge device away from the * PF_INET/FORWARD and PF_INET/OUTPUT hook functions, and give them back later, * when we have determined the real output device. This is done in here. @@ -501,45 +605,44 @@ static int br_nf_local_out_finish(struct * this packet before, and so the packet was locally originated. We fake * the PF_INET/LOCAL_OUT hook. * Finally, if nf_bridge->physindev isn't NULL, then the packet was IP routed, - * so we fake the PF_INET/FORWARD hook. ipv4_sabotage_out() makes sure + * so we fake the PF_INET/FORWARD hook. ip_sabotage_out() makes sure * even routed packets that didn't arrive on a bridge interface have their - * nf_bridge->physindev set. - */ - + * nf_bridge->physindev set. */ static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, - int (*_okfn)(struct sk_buff *)) + int (*okfn)(struct sk_buff *)) { - int (*okfn)(struct sk_buff *skb); - struct net_device *realindev; + struct net_device *realindev, *realoutdev; struct sk_buff *skb = *pskb; struct nf_bridge_info *nf_bridge; struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet); + int pf; -#ifdef CONFIG_SYSCTL if (!skb->nf_bridge) return NF_ACCEPT; -#endif - if (skb->protocol != __constant_htons(ETH_P_IP) && !IS_VLAN_IP) - return NF_ACCEPT; + if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP) + pf = PF_INET; + else + pf = PF_INET6; +#ifdef CONFIG_NETFILTER_DEBUG /* Sometimes we get packets with NULL ->dst here (for example, - * running a dhcp client daemon triggers this). - */ - if (skb->dst == NULL) + * running a dhcp client daemon triggers this). This should now + * be fixed, but let's keep the check around. */ + if (skb->dst == NULL) { + printk(KERN_CRIT "br_netfilter: skb->dst == NULL."); return NF_ACCEPT; + } +#endif nf_bridge = skb->nf_bridge; nf_bridge->physoutdev = skb->dev; realindev = nf_bridge->physindev; /* Bridged, take PF_BRIDGE/FORWARD. - * (see big note in front of br_nf_pre_routing_finish) - */ + * (see big note in front of br_nf_pre_routing_finish) */ if (nf_bridge->mask & BRNF_BRIDGED_DNAT) { - okfn = br_forward_finish; - if (nf_bridge->mask & BRNF_PKT_TYPE) { skb->pkt_type = PACKET_OTHERHOST; nf_bridge->mask ^= BRNF_PKT_TYPE; @@ -550,41 +653,41 @@ static unsigned int br_nf_local_out(unsi } NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, realindev, - skb->dev, okfn); - } else { - struct net_device *realoutdev = bridge_parent(skb->dev); + skb->dev, br_forward_finish); + goto out; + } + realoutdev = bridge_parent(skb->dev); #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) - /* iptables should match -o br0.x */ - if (nf_bridge->netoutdev) - realoutdev = nf_bridge->netoutdev; + /* iptables should match -o br0.x */ + if (nf_bridge->netoutdev) + realoutdev = nf_bridge->netoutdev; #endif - okfn = br_nf_local_out_finish; - if (skb->protocol == __constant_htons(ETH_P_8021Q)) { - skb_pull(skb, VLAN_HLEN); - (*pskb)->nh.raw += VLAN_HLEN; - } - /* IP forwarded traffic has a physindev, locally - * generated traffic hasn't. - */ - if (realindev != NULL) { - if (((nf_bridge->mask & BRNF_DONT_TAKE_PARENT) == 0) && - has_bridge_parent(realindev)) - realindev = bridge_parent(realindev); - NF_HOOK_THRESH(PF_INET, NF_IP_FORWARD, skb, realindev, - realoutdev, okfn, - NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD + 1); - } else { + if (skb->protocol == __constant_htons(ETH_P_8021Q)) { + skb_pull(skb, VLAN_HLEN); + (*pskb)->nh.raw += VLAN_HLEN; + } + /* IP forwarded traffic has a physindev, locally + * generated traffic hasn't. */ + if (realindev != NULL) { + if (!(nf_bridge->mask & BRNF_DONT_TAKE_PARENT) && + has_bridge_parent(realindev)) + realindev = bridge_parent(realindev); + + NF_HOOK_THRESH(pf, NF_IP_FORWARD, skb, realindev, + realoutdev, br_nf_local_out_finish, + NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD + 1); + } else { #ifdef CONFIG_NETFILTER_DEBUG - skb->nf_debug ^= (1 << NF_IP_LOCAL_OUT); + skb->nf_debug ^= (1 << NF_IP_LOCAL_OUT); #endif - NF_HOOK_THRESH(PF_INET, NF_IP_LOCAL_OUT, skb, realindev, - realoutdev, okfn, - NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT + 1); - } + NF_HOOK_THRESH(pf, NF_IP_LOCAL_OUT, skb, realindev, + realoutdev, br_nf_local_out_finish, + NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT + 1); } +out: return NF_STOLEN; } @@ -598,6 +701,7 @@ static unsigned int br_nf_post_routing(u struct nf_bridge_info *nf_bridge = (*pskb)->nf_bridge; struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet); struct net_device *realoutdev = bridge_parent(skb->dev); + int pf; #ifdef CONFIG_NETFILTER_DEBUG /* Be very paranoid. This probably won't happen anymore, but let's @@ -609,19 +713,15 @@ static unsigned int br_nf_post_routing(u } #endif -#ifdef CONFIG_SYSCTL if (!nf_bridge) return NF_ACCEPT; -#endif - if (skb->protocol != __constant_htons(ETH_P_IP) && !IS_VLAN_IP) - return NF_ACCEPT; + if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP) + pf = PF_INET; + else + pf = PF_INET6; #ifdef CONFIG_NETFILTER_DEBUG - /* Sometimes we get packets with NULL ->dst here (for example, - * running a dhcp client daemon triggers this). This should now - * be fixed, but let's keep the check around. - */ if (skb->dst == NULL) { printk(KERN_CRIT "br_netfilter: skb->dst == NULL."); goto print_error; @@ -631,8 +731,7 @@ static unsigned int br_nf_post_routing(u #endif /* We assume any code from br_dev_queue_push_xmit onwards doesn't care - * about the value of skb->pkt_type. - */ + * about the value of skb->pkt_type. */ if (skb->pkt_type == PACKET_OTHERHOST) { skb->pkt_type = PACKET_HOST; nf_bridge->mask |= BRNF_PKT_TYPE; @@ -649,8 +748,8 @@ static unsigned int br_nf_post_routing(u if (nf_bridge->netoutdev) realoutdev = nf_bridge->netoutdev; #endif - NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL, - realoutdev, br_dev_queue_push_xmit); + NF_HOOK(pf, NF_IP_POST_ROUTING, skb, NULL, realoutdev, + br_dev_queue_push_xmit); return NF_STOLEN; @@ -668,12 +767,10 @@ print_error: } -/* IPv4/SABOTAGE *****************************************************/ - -/* Don't hand locally destined packets to PF_INET/PRE_ROUTING - * for the second time. - */ -static unsigned int ipv4_sabotage_in(unsigned int hook, struct sk_buff **pskb, +/* IP/SABOTAGE *****************************************************/ +/* Don't hand locally destined packets to PF_INET(6)/PRE_ROUTING + * for the second time. */ +static unsigned int ip_sabotage_in(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { @@ -686,19 +783,27 @@ static unsigned int ipv4_sabotage_in(uns return NF_ACCEPT; } -/* Postpone execution of PF_INET/FORWARD, PF_INET/LOCAL_OUT - * and PF_INET/POST_ROUTING until we have done the forwarding - * decision in the bridge code and have determined skb->physoutdev. - */ -static unsigned int ipv4_sabotage_out(unsigned int hook, struct sk_buff **pskb, +/* Postpone execution of PF_INET(6)/FORWARD, PF_INET(6)/LOCAL_OUT + * and PF_INET(6)/POST_ROUTING until we have done the forwarding + * decision in the bridge code and have determined nf_bridge->physoutdev. */ +static unsigned int ip_sabotage_out(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { struct sk_buff *skb = *pskb; #ifdef CONFIG_SYSCTL - if (!brnf_call_iptables && !skb->nf_bridge) - return NF_ACCEPT; + if (!skb->nf_bridge) { + struct vlan_ethhdr *hdr = + (struct vlan_ethhdr *)(skb->mac.ethernet); + + if (skb->protocol == __constant_htons(ETH_P_IP) || + IS_VLAN_IP) { + if (!brnf_call_iptables) + return NF_ACCEPT; + } else if (!brnf_call_ip6tables) + return NF_ACCEPT; + } #endif if ((out->hard_start_xmit == br_dev_xmit && @@ -721,8 +826,7 @@ static unsigned int ipv4_sabotage_out(un * will need the indev then. For a brouter, the real indev * can be a bridge port, so we make sure br_nf_local_out() * doesn't use the bridge parent of the indev by using - * the BRNF_DONT_TAKE_PARENT mask. - */ + * the BRNF_DONT_TAKE_PARENT mask. */ if (hook == NF_IP_FORWARD && nf_bridge->physindev == NULL) { nf_bridge->mask &= BRNF_DONT_TAKE_PARENT; nf_bridge->physindev = (struct net_device *)in; @@ -742,8 +846,7 @@ static unsigned int ipv4_sabotage_out(un /* For br_nf_local_out we need (prio = NF_BR_PRI_FIRST), to insure that innocent * PF_BRIDGE/NF_BR_LOCAL_OUT functions don't get bridged traffic as input. * For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because - * ip_refrag() can return NF_STOLEN. - */ + * ip_refrag() can return NF_STOLEN. */ static struct nf_hook_ops br_nf_ops[] = { { .hook = br_nf_pre_routing, .owner = THIS_MODULE, @@ -775,26 +878,46 @@ static struct nf_hook_ops br_nf_ops[] = .pf = PF_BRIDGE, .hooknum = NF_BR_POST_ROUTING, .priority = NF_BR_PRI_LAST, }, - { .hook = ipv4_sabotage_in, + { .hook = ip_sabotage_in, .owner = THIS_MODULE, .pf = PF_INET, .hooknum = NF_IP_PRE_ROUTING, .priority = NF_IP_PRI_FIRST, }, - { .hook = ipv4_sabotage_out, + { .hook = ip_sabotage_in, + .owner = THIS_MODULE, + .pf = PF_INET6, + .hooknum = NF_IP6_PRE_ROUTING, + .priority = NF_IP6_PRI_FIRST, }, + { .hook = ip_sabotage_out, .owner = THIS_MODULE, .pf = PF_INET, .hooknum = NF_IP_FORWARD, .priority = NF_IP_PRI_BRIDGE_SABOTAGE_FORWARD, }, - { .hook = ipv4_sabotage_out, + { .hook = ip_sabotage_out, + .owner = THIS_MODULE, + .pf = PF_INET6, + .hooknum = NF_IP6_FORWARD, + .priority = NF_IP6_PRI_BRIDGE_SABOTAGE_FORWARD, }, + { .hook = ip_sabotage_out, .owner = THIS_MODULE, .pf = PF_INET, .hooknum = NF_IP_LOCAL_OUT, .priority = NF_IP_PRI_BRIDGE_SABOTAGE_LOCAL_OUT, }, - { .hook = ipv4_sabotage_out, + { .hook = ip_sabotage_out, + .owner = THIS_MODULE, + .pf = PF_INET6, + .hooknum = NF_IP6_LOCAL_OUT, + .priority = NF_IP6_PRI_BRIDGE_SABOTAGE_LOCAL_OUT, }, + { .hook = ip_sabotage_out, .owner = THIS_MODULE, .pf = PF_INET, .hooknum = NF_IP_POST_ROUTING, .priority = NF_IP_PRI_FIRST, }, + { .hook = ip_sabotage_out, + .owner = THIS_MODULE, + .pf = PF_INET6, + .hooknum = NF_IP6_POST_ROUTING, + .priority = NF_IP6_PRI_FIRST, }, }; #ifdef CONFIG_SYSCTL @@ -829,6 +952,14 @@ static ctl_table brnf_table[] = { .proc_handler = &brnf_sysctl_call_tables, }, { + .ctl_name = NET_BRIDGE_NF_CALL_IP6TABLES, + .procname = "bridge-nf-call-ip6tables", + .data = &brnf_call_ip6tables, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &brnf_sysctl_call_tables, + }, + { .ctl_name = NET_BRIDGE_NF_FILTER_VLAN_TAGGED, .procname = "bridge-nf-filter-vlan-tagged", .data = &brnf_filter_vlan_tagged, --- linux-2.6.9-rc1/net/bridge/br_private.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/net/bridge/br_private.h 2004-09-02 20:51:53.000000000 -0700 @@ -177,7 +177,7 @@ extern int br_min_mtu(const struct net_b /* br_input.c */ extern int br_handle_frame_finish(struct sk_buff *skb); -extern int br_handle_frame(struct sk_buff *skb); +extern int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb); /* br_ioctl.c */ extern int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); --- linux-2.6.9-rc1/net/core/dev.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/net/core/dev.c 2004-09-03 00:56:36.760572192 -0700 @@ -1144,16 +1144,10 @@ int skb_checksum_help(struct sk_buff **p goto out; } - if (skb_shared(*pskb) || skb_cloned(*pskb)) { - struct sk_buff *newskb = skb_copy(*pskb, GFP_ATOMIC); - if (!newskb) { - ret = -ENOMEM; + if (skb_cloned(*pskb)) { + ret = pskb_expand_head(*pskb, 0, 0, GFP_ATOMIC); + if (ret) goto out; - } - if ((*pskb)->sk) - skb_set_owner_w(newskb, (*pskb)->sk); - kfree_skb(*pskb); - *pskb = newskb; } if (offset > (int)(*pskb)->len) @@ -1532,7 +1526,6 @@ static void sample_queue(unsigned long d } #endif - /** * netif_rx - post buffer to the network code * @skb: buffer to post @@ -1557,7 +1550,7 @@ int netif_rx(struct sk_buff *skb) struct softnet_data *queue; unsigned long flags; -#ifdef CONFIG_NETPOLL_RX +#ifdef CONFIG_NETPOLL if (skb->dev->netpoll_rx && netpoll_rx(skb)) { kfree_skb(skb); return NET_RX_DROP; @@ -1676,43 +1669,34 @@ static void net_tx_action(struct softirq } static __inline__ int deliver_skb(struct sk_buff *skb, - struct packet_type *pt_prev, int last) + struct packet_type *pt_prev) { atomic_inc(&skb->users); return pt_prev->func(skb, skb->dev, pt_prev); } - #if defined(CONFIG_BRIDGE) || defined (CONFIG_BRIDGE_MODULE) -int (*br_handle_frame_hook)(struct sk_buff *skb); +int (*br_handle_frame_hook)(struct net_bridge_port *p, struct sk_buff **pskb); -static __inline__ int handle_bridge(struct sk_buff *skb, - struct packet_type *pt_prev) +static __inline__ int handle_bridge(struct sk_buff **pskb, + struct packet_type **pt_prev, int *ret) { - int ret = NET_RX_DROP; - if (pt_prev) - ret = deliver_skb(skb, pt_prev, 0); + struct net_bridge_port *port; - return ret; -} - -#endif - -static inline int __handle_bridge(struct sk_buff *skb, - struct packet_type **pt_prev, int *ret) -{ -#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) - if (skb->dev->br_port && skb->pkt_type != PACKET_LOOPBACK) { - *ret = handle_bridge(skb, *pt_prev); - if (br_handle_frame_hook(skb) == 0) - return 1; + if ((*pskb)->pkt_type == PACKET_LOOPBACK || + (port = rcu_dereference((*pskb)->dev->br_port)) == NULL) + return 0; + if (*pt_prev) { + *ret = deliver_skb(*pskb, *pt_prev); *pt_prev = NULL; - } -#endif - return 0; + } + + return br_handle_frame_hook(port, pskb); } - +#else +#define handle_bridge(skb, pt_prev, ret) (0) +#endif #ifdef CONFIG_NET_CLS_ACT /* TODO: Maybe we should just force sch_ingress to be compiled in @@ -1761,7 +1745,7 @@ int netif_receive_skb(struct sk_buff *sk int ret = NET_RX_DROP; unsigned short type; -#ifdef CONFIG_NETPOLL_RX +#ifdef CONFIG_NETPOLL if (skb->dev->netpoll_rx && skb->dev->poll && netpoll_rx(skb)) { kfree_skb(skb); return NET_RX_DROP; @@ -1779,27 +1763,27 @@ int netif_receive_skb(struct sk_buff *sk skb->mac_len = skb->nh.raw - skb->mac.raw; pt_prev = NULL; + + rcu_read_lock(); + #ifdef CONFIG_NET_CLS_ACT if (skb->tc_verd & TC_NCLS) { skb->tc_verd = CLR_TC_NCLS(skb->tc_verd); - rcu_read_lock(); goto ncls; } - #endif +#endif - rcu_read_lock(); list_for_each_entry_rcu(ptype, &ptype_all, list) { if (!ptype->dev || ptype->dev == skb->dev) { if (pt_prev) - ret = deliver_skb(skb, pt_prev, 0); + ret = deliver_skb(skb, pt_prev); pt_prev = ptype; } } #ifdef CONFIG_NET_CLS_ACT if (pt_prev) { - atomic_inc(&skb->users); - ret = pt_prev->func(skb, skb->dev, pt_prev); + ret = deliver_skb(skb, pt_prev); pt_prev = NULL; /* noone else should process this after*/ } else { skb->tc_verd = SET_TC_OK2MUNGE(skb->tc_verd); @@ -1818,7 +1802,7 @@ ncls: handle_diverter(skb); - if (__handle_bridge(skb, &pt_prev, &ret)) + if (handle_bridge(&skb, &pt_prev, &ret)) goto out; type = skb->protocol; @@ -1826,7 +1810,7 @@ ncls: if (ptype->type == type && (!ptype->dev || ptype->dev == skb->dev)) { if (pt_prev) - ret = deliver_skb(skb, pt_prev, 0); + ret = deliver_skb(skb, pt_prev); pt_prev = ptype; } } @@ -1915,7 +1899,6 @@ static void net_rx_action(struct softirq unsigned long start_time = jiffies; int budget = netdev_max_backlog; - local_irq_disable(); while (!list_empty(&queue->poll_list)) { @@ -1941,6 +1924,10 @@ static void net_rx_action(struct softirq dev_put(dev); local_irq_disable(); } + +#ifdef CONFIG_KGDBOE + kgdb_process_breakpoint(); +#endif } out: local_irq_enable(); @@ -2786,7 +2773,7 @@ int dev_ioctl(unsigned int cmd, void __u /* Follow me in net/core/wireless.c */ ret = wireless_process_ioctl(&ifr, cmd); rtnl_unlock(); - if (!ret && IW_IS_GET(cmd) && + if (IW_IS_GET(cmd) && copy_to_user(arg, &ifr, sizeof(struct ifreq))) ret = -EFAULT; --- linux-2.6.9-rc1/net/core/neighbour.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/net/core/neighbour.c 2004-09-02 20:51:53.000000000 -0700 @@ -1120,6 +1120,7 @@ struct neigh_parms *neigh_parms_alloc(st if (p) { memcpy(p, &tbl->parms, sizeof(*p)); p->tbl = tbl; + INIT_RCU_HEAD(&p->rcu_head); p->reachable_time = neigh_rand_reach_time(p->base_reachable_time); if (dev && dev->neigh_setup && dev->neigh_setup(dev, p)) { @@ -1135,6 +1136,14 @@ struct neigh_parms *neigh_parms_alloc(st return p; } +static void neigh_rcu_free_parms(struct rcu_head *head) +{ + struct neigh_parms *parms = + container_of(head, struct neigh_parms, rcu_head); + + kfree(parms); +} + void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms) { struct neigh_parms **p; @@ -1146,7 +1155,7 @@ void neigh_parms_release(struct neigh_ta if (*p == parms) { *p = parms->next; write_unlock_bh(&tbl->lock); - kfree(parms); + call_rcu(&parms->rcu_head, neigh_rcu_free_parms); return; } } @@ -1159,6 +1168,7 @@ void neigh_table_init(struct neigh_table { unsigned long now = jiffies; + INIT_RCU_HEAD(&tbl->parms.rcu_head); tbl->parms.reachable_time = neigh_rand_reach_time(tbl->parms.base_reachable_time); --- linux-2.6.9-rc1/net/core/netpoll.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/net/core/netpoll.c 2004-09-02 20:51:53.000000000 -0700 @@ -21,6 +21,7 @@ #include #include #include +#include /* * We maintain a small pool of fully-sized skbs, to make sure the @@ -37,7 +38,11 @@ static struct sk_buff *skbs; static spinlock_t rx_list_lock = SPIN_LOCK_UNLOCKED; static LIST_HEAD(rx_list); -static int trapped; +static atomic_t trapped; +spinlock_t netpoll_poll_lock = SPIN_LOCK_UNLOCKED; + +#define NETPOLL_RX_ENABLED 1 +#define NETPOLL_RX_DROP 2 #define MAX_SKB_SIZE \ (MAX_UDP_CHUNK + sizeof(struct udphdr) + \ @@ -62,7 +67,14 @@ static int checksum_udp(struct sk_buff * void netpoll_poll(struct netpoll *np) { - int budget = 1; + /* + * In cases where there is bi-directional communications, reading + * only one message at a time can lead to packets being dropped by + * the network adapter, forcing superfluous retries and possibly + * timeouts. Thus, we set our budget to a more reasonable value. + */ + int budget = 16; + unsigned long flags; if(!np->dev || !netif_running(np->dev) || !np->dev->poll_controller) return; @@ -71,9 +83,19 @@ void netpoll_poll(struct netpoll *np) np->dev->poll_controller(np->dev); /* If scheduling is stopped, tickle NAPI bits */ - if(trapped && np->dev->poll && - test_bit(__LINK_STATE_RX_SCHED, &np->dev->state)) + spin_lock_irqsave(&netpoll_poll_lock, flags); + if (np->dev->poll && + test_bit(__LINK_STATE_RX_SCHED, &np->dev->state)) { + np->dev->netpoll_rx |= NETPOLL_RX_DROP; + atomic_inc(&trapped); + np->dev->poll(np->dev, &budget); + + atomic_dec(&trapped); + np->dev->netpoll_rx &= ~NETPOLL_RX_DROP; + } + spin_unlock_irqrestore(&netpoll_poll_lock, flags); + zap_completion_queue(); } @@ -169,6 +191,18 @@ repeat: spin_lock(&np->dev->xmit_lock); np->dev->xmit_lock_owner = smp_processor_id(); + /* + * network drivers do not expect to be called if the queue is + * stopped. + */ + if (netif_queue_stopped(np->dev)) { + np->dev->xmit_lock_owner = -1; + spin_unlock(&np->dev->xmit_lock); + + netpoll_poll(np); + goto repeat; + } + status = np->dev->hard_start_xmit(skb, np->dev); np->dev->xmit_lock_owner = -1; spin_unlock(&np->dev->xmit_lock); @@ -207,17 +241,17 @@ void netpoll_send_udp(struct netpoll *np iph = (struct iphdr *)skb_push(skb, sizeof(*iph)); - iph->version = 4; - iph->ihl = 5; + /* iph->version = 4; iph->ihl = 5; */ + put_unaligned(0x54, (unsigned char *)iph); iph->tos = 0; - iph->tot_len = htons(ip_len); + put_unaligned(htonl(ip_len), &(iph->tot_len)); iph->id = 0; iph->frag_off = 0; iph->ttl = 64; iph->protocol = IPPROTO_UDP; iph->check = 0; - iph->saddr = htonl(np->local_ip); - iph->daddr = htonl(np->remote_ip); + put_unaligned(htonl(np->local_ip), &(iph->saddr)); + put_unaligned(htonl(np->remote_ip), &(iph->daddr)); iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); eth = (struct ethhdr *) skb_push(skb, ETH_HLEN); @@ -338,7 +372,8 @@ int netpoll_rx(struct sk_buff *skb) goto out; /* check if netpoll clients need ARP */ - if (skb->protocol == __constant_htons(ETH_P_ARP) && trapped) { + if (skb->protocol == __constant_htons(ETH_P_ARP) && + atomic_read(&trapped)) { arp_reply(skb); return 1; } @@ -401,7 +436,7 @@ int netpoll_rx(struct sk_buff *skb) spin_unlock_irqrestore(&rx_list_lock, flags); out: - return trapped; + return atomic_read(&trapped); } int netpoll_parse_options(struct netpoll *np, char *opt) @@ -594,9 +629,7 @@ int netpoll_setup(struct netpoll *np) if(np->rx_hook) { unsigned long flags; -#ifdef CONFIG_NETPOLL_RX - np->dev->netpoll_rx = 1; -#endif + np->dev->netpoll_rx = NETPOLL_RX_ENABLED; spin_lock_irqsave(&rx_list_lock, flags); list_add(&np->rx_list, &rx_list); @@ -611,30 +644,31 @@ int netpoll_setup(struct netpoll *np) void netpoll_cleanup(struct netpoll *np) { - if(np->rx_hook) { + if (np->rx_hook) { unsigned long flags; spin_lock_irqsave(&rx_list_lock, flags); list_del(&np->rx_list); -#ifdef CONFIG_NETPOLL_RX - if (np->dev) - np->dev->netpoll_rx = 0; -#endif spin_unlock_irqrestore(&rx_list_lock, flags); } + if (np->dev) + np->dev->netpoll_rx = 0; dev_put(np->dev); np->dev = NULL; } int netpoll_trap(void) { - return trapped; + return atomic_read(&trapped); } void netpoll_set_trap(int trap) { - trapped = trap; + if (trap) + atomic_inc(&trapped); + else + atomic_dec(&trapped); } EXPORT_SYMBOL(netpoll_set_trap); --- linux-2.6.9-rc1/net/core/rtnetlink.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/net/core/rtnetlink.c 2004-09-02 20:51:53.000000000 -0700 @@ -166,31 +166,58 @@ static int rtnetlink_fill_ifinfo(struct r->ifi_family = AF_UNSPEC; r->ifi_type = dev->type; r->ifi_index = dev->ifindex; - r->ifi_flags = dev->flags; + r->ifi_flags = dev_get_flags(dev); r->ifi_change = change; - if (!netif_running(dev) || !netif_carrier_ok(dev)) - r->ifi_flags &= ~IFF_RUNNING; - else - r->ifi_flags |= IFF_RUNNING; - RTA_PUT(skb, IFLA_IFNAME, strlen(dev->name)+1, dev->name); + + if (1) { + u32 txqlen = dev->tx_queue_len; + RTA_PUT(skb, IFLA_TXQLEN, sizeof(txqlen), &txqlen); + } + + if (1) { + u32 weight = dev->weight; + RTA_PUT(skb, IFLA_WEIGHT, sizeof(weight), &weight); + } + + if (1) { + struct ifmap map = { + .mem_start = dev->mem_start, + .mem_end = dev->mem_end, + .base_addr = dev->base_addr, + .irq = dev->irq, + .dma = dev->dma, + .port = dev->if_port, + }; + RTA_PUT(skb, IFLA_MAP, sizeof(map), &map); + } + if (dev->addr_len) { RTA_PUT(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr); RTA_PUT(skb, IFLA_BROADCAST, dev->addr_len, dev->broadcast); } + if (1) { - unsigned mtu = dev->mtu; + u32 mtu = dev->mtu; RTA_PUT(skb, IFLA_MTU, sizeof(mtu), &mtu); } - if (dev->ifindex != dev->iflink) - RTA_PUT(skb, IFLA_LINK, sizeof(int), &dev->iflink); + + if (dev->ifindex != dev->iflink) { + u32 iflink = dev->iflink; + RTA_PUT(skb, IFLA_LINK, sizeof(iflink), &iflink); + } + if (dev->qdisc_sleeping) RTA_PUT(skb, IFLA_QDISC, strlen(dev->qdisc_sleeping->ops->id) + 1, dev->qdisc_sleeping->ops->id); - if (dev->master) - RTA_PUT(skb, IFLA_MASTER, sizeof(int), &dev->master->ifindex); + + if (dev->master) { + u32 master = dev->master->ifindex; + RTA_PUT(skb, IFLA_MASTER, sizeof(master), &master); + } + if (dev->get_stats) { unsigned long *stats = (unsigned long*)dev->get_stats(dev); if (stats) { @@ -246,6 +273,30 @@ static int do_setlink(struct sk_buff *sk err = -EINVAL; + if (ifm->ifi_flags) + dev_change_flags(dev, ifm->ifi_flags); + + if (ida[IFLA_MAP - 1]) { + if (!dev->set_config) { + err = -EOPNOTSUPP; + goto out; + } + + if (!netif_device_present(dev)) { + err = -ENODEV; + goto out; + } + + if (ida[IFLA_MAP - 1]->rta_len != RTA_LENGTH(sizeof(struct ifmap))) + goto out; + + err = dev->set_config(dev, (struct ifmap *) + RTA_DATA(ida[IFLA_MAP - 1])); + + if (err) + goto out; + } + if (ida[IFLA_ADDRESS - 1]) { if (!dev->set_mac_address) { err = -EOPNOTSUPP; @@ -270,6 +321,30 @@ static int do_setlink(struct sk_buff *sk dev->addr_len); } + if (ida[IFLA_MTU - 1]) { + if (ida[IFLA_MTU - 1]->rta_len != RTA_LENGTH(sizeof(u32))) + goto out; + err = dev_set_mtu(dev, *((u32 *) RTA_DATA(ida[IFLA_MTU - 1]))); + + if (err) + goto out; + + } + + if (ida[IFLA_TXQLEN - 1]) { + if (ida[IFLA_TXQLEN - 1]->rta_len != RTA_LENGTH(sizeof(u32))) + goto out; + + dev->tx_queue_len = *((u32 *) RTA_DATA(ida[IFLA_TXQLEN - 1])); + } + + if (ida[IFLA_WEIGHT - 1]) { + if (ida[IFLA_WEIGHT - 1]->rta_len != RTA_LENGTH(sizeof(u32))) + goto out; + + dev->weight = *((u32 *) RTA_DATA(ida[IFLA_WEIGHT - 1])); + } + err = 0; out: --- linux-2.6.9-rc1/net/core/utils.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/net/core/utils.c 2004-09-02 20:51:53.000000000 -0700 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -77,10 +78,8 @@ static u32 __net_random(struct nrnd_stat return (state->s1 ^ state->s2 ^ state->s3); } -static void __net_srandom(struct nrnd_state *state, unsigned long entropy) +static void __net_srandom(struct nrnd_state *state, unsigned long s) { - u32 s = state->s1 ^ entropy; - if (s == 0) s = 1; /* default seed is 1 */ @@ -112,24 +111,33 @@ unsigned long net_random(void) void net_srandom(unsigned long entropy) { struct nrnd_state *state = &get_cpu_var(net_rand_state); - __net_srandom(state, entropy); + __net_srandom(state, state->s1^entropy); put_cpu_var(state); } void __init net_random_init(void) { int i; + + for (i = 0; i < NR_CPUS; i++) { + struct nrnd_state *state = &per_cpu(net_rand_state,i); + __net_srandom(state, i+jiffies); + } +} + +static int net_random_reseed(void) +{ + int i; unsigned long seed[NR_CPUS]; get_random_bytes(seed, sizeof(seed)); - for (i = 0; i < NR_CPUS; i++) { struct nrnd_state *state = &per_cpu(net_rand_state,i); - - memset(state, 0, sizeof(*state)); __net_srandom(state, seed[i]); } + return 0; } +late_initcall(net_random_reseed); int net_msg_cost = 5*HZ; int net_msg_burst = 10; --- linux-2.6.9-rc1/net/core/wireless.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/net/core/wireless.c 2004-09-03 00:56:32.643198128 -0700 @@ -2,7 +2,7 @@ * This file implement the Wireless Extensions APIs. * * Authors : Jean Tourrilhes - HPL - - * Copyright (c) 1997-2003 Jean Tourrilhes, All Rights Reserved. + * Copyright (c) 1997-2004 Jean Tourrilhes, All Rights Reserved. * * (As all part of the Linux kernel, this file is GPL) */ @@ -48,6 +48,15 @@ * o Add common spy support : iw_handler_set_spy(), wireless_spy_update() * o Add enhanced spy support : iw_handler_set_thrspy() and event. * o Add WIRELESS_EXT version display in /proc/net/wireless + * + * v6 - 18.06.04 - Jean II + * o Change get_spydata() method for added safety + * o Remove spy #ifdef, they are always on -> cleaner code + * o Allow any size GET request is user specifies length > max + * o Start migrating get_wireless_stats to struct iw_handler_def + * o Add wmb() in iw_handler_set_spy() for non-coherent archs/cpus + * Based on patch from Pavel Roskin : + * o Fix kernel data leak to user space in private handler handling */ /***************************** INCLUDES *****************************/ @@ -69,10 +78,6 @@ /**************************** CONSTANTS ****************************/ -/* Enough lenience, let's make sure things are proper... */ -#define WE_STRICT_WRITE /* Check write buffer size */ -/* I'll probably drop both the define and kernel message in the next version */ - /* Debugging stuff */ #undef WE_IOCTL_DEBUG /* Debug IOCTL API */ #undef WE_EVENT_DEBUG /* Debug Event dispatcher */ @@ -186,6 +191,7 @@ static const struct iw_ioctl_description .token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality), .max_tokens = IW_MAX_AP, + .flags = IW_DESCR_FLAG_NOMAX, }, [SIOCSIWSCAN - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_PARAM, @@ -194,6 +200,7 @@ static const struct iw_ioctl_description .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_SCAN_MAX_DATA, + .flags = IW_DESCR_FLAG_NOMAX, }, [SIOCSIWESSID - SIOCIWFIRST] = { .header_type = IW_HEADER_TYPE_POINT, @@ -296,7 +303,7 @@ static const int standard_event_num = (s sizeof(struct iw_ioctl_description)); /* Size (in bytes) of the various private data types */ -static const char priv_type_size[] = { +const char iw_priv_type_size[] = { 0, /* IW_PRIV_TYPE_NONE */ 1, /* IW_PRIV_TYPE_BYTE */ 1, /* IW_PRIV_TYPE_CHAR */ @@ -363,12 +370,15 @@ static inline iw_handler get_handler(str */ static inline struct iw_statistics *get_wireless_stats(struct net_device *dev) { + /* New location */ + if((dev->wireless_handlers != NULL) && + (dev->wireless_handlers->get_wireless_stats != NULL)) + return dev->wireless_handlers->get_wireless_stats(dev); + + /* Old location, will be phased out in next WE */ return (dev->get_wireless_stats ? dev->get_wireless_stats(dev) : (struct iw_statistics *) NULL); - /* In the future, get_wireless_stats may move from 'struct net_device' - * to 'struct iw_handler_def', to de-bloat struct net_device. - * Definitely worse a thought... */ } /* ---------------------------------------------------------------- */ @@ -403,14 +413,32 @@ static inline int call_commit_handler(st /* ---------------------------------------------------------------- */ /* - * Number of private arguments + * Calculate size of private arguments */ static inline int get_priv_size(__u16 args) { int num = args & IW_PRIV_SIZE_MASK; int type = (args & IW_PRIV_TYPE_MASK) >> 12; - return num * priv_type_size[type]; + return num * iw_priv_type_size[type]; +} + +/* ---------------------------------------------------------------- */ +/* + * Re-calculate the size of private arguments + */ +static inline int adjust_priv_size(__u16 args, + union iwreq_data * wrqu) +{ + int num = wrqu->data.length; + int max = args & IW_PRIV_SIZE_MASK; + int type = (args & IW_PRIV_TYPE_MASK) >> 12; + + /* Make sure the driver doesn't goof up */ + if (max < num) + num = max; + + return num * iw_priv_type_size[type]; } @@ -440,11 +468,14 @@ static __inline__ void wireless_seq_prin seq_printf(seq, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d " "%6d %6d %6d\n", dev->name, stats->status, stats->qual.qual, - stats->qual.updated & 1 ? '.' : ' ', + stats->qual.updated & IW_QUAL_QUAL_UPDATED + ? '.' : ' ', ((__u8) stats->qual.level), - stats->qual.updated & 2 ? '.' : ' ', + stats->qual.updated & IW_QUAL_LEVEL_UPDATED + ? '.' : ' ', ((__u8) stats->qual.noise), - stats->qual.updated & 4 ? '.' : ' ', + stats->qual.updated & IW_QUAL_NOISE_UPDATED + ? '.' : ' ', stats->discard.nwid, stats->discard.code, stats->discard.fragment, stats->discard.retries, stats->discard.misc, stats->miss.beacon); @@ -555,13 +586,15 @@ static inline int ioctl_export_private(s /* Check NULL pointer */ if(iwr->u.data.pointer == NULL) return -EFAULT; -#ifdef WE_STRICT_WRITE + /* Check if there is enough buffer up there */ if(iwr->u.data.length < dev->wireless_handlers->num_private_args) { - printk(KERN_ERR "%s (WE) : Buffer for request SIOCGIWPRIV too small (%d<%d)\n", dev->name, iwr->u.data.length, dev->wireless_handlers->num_private_args); + /* User space can't know in advance how large the buffer + * needs to be. Give it a hint, so that we can support + * any size buffer we want somewhat efficiently... */ + iwr->u.data.length = dev->wireless_handlers->num_private_args; return -E2BIG; } -#endif /* WE_STRICT_WRITE */ /* Set the number of available ioctls. */ iwr->u.data.length = dev->wireless_handlers->num_private_args; @@ -590,7 +623,6 @@ static inline int ioctl_standard_call(st const struct iw_ioctl_description * descr; struct iw_request_info info; int ret = -EINVAL; - int user_size = 0; /* Get the description of the IOCTL */ if((cmd - SIOCIWFIRST) >= standard_ioctl_num) @@ -621,8 +653,14 @@ static inline int ioctl_standard_call(st #endif /* WE_SET_EVENT */ } else { char * extra; + int extra_size; + int user_length = 0; int err; + /* Calculate space needed by arguments. Always allocate + * for max space. Easier, and won't last long... */ + extra_size = descr->max_tokens * descr->token_size; + /* Check what user space is giving us */ if(IW_IS_SET(cmd)) { /* Check NULL pointer */ @@ -639,18 +677,29 @@ static inline int ioctl_standard_call(st if(iwr->u.data.pointer == NULL) return -EFAULT; /* Save user space buffer size for checking */ - user_size = iwr->u.data.length; + user_length = iwr->u.data.length; + + /* Don't check if user_length > max to allow forward + * compatibility. The test user_length < min is + * implied by the test at the end. */ + + /* Support for very large requests */ + if((descr->flags & IW_DESCR_FLAG_NOMAX) && + (user_length > descr->max_tokens)) { + /* Allow userspace to GET more than max so + * we can support any size GET requests. + * There is still a limit : -ENOMEM. */ + extra_size = user_length * descr->token_size; + } } #ifdef WE_IOCTL_DEBUG printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n", - dev->name, descr->max_tokens * descr->token_size); + dev->name, extra_size); #endif /* WE_IOCTL_DEBUG */ - /* Always allocate for max space. Easier, and won't last - * long... */ - extra = kmalloc(descr->max_tokens * descr->token_size, - GFP_KERNEL); + /* Create the kernel buffer */ + extra = kmalloc(extra_size, GFP_KERNEL); if (extra == NULL) { return -ENOMEM; } @@ -676,14 +725,11 @@ static inline int ioctl_standard_call(st /* If we have something to return to the user */ if (!ret && IW_IS_GET(cmd)) { -#ifdef WE_STRICT_WRITE /* Check if there is enough buffer up there */ - if(user_size < iwr->u.data.length) { - printk(KERN_ERR "%s (WE) : Buffer for request %04X too small (%d<%d)\n", dev->name, cmd, user_size, iwr->u.data.length); + if(user_length < iwr->u.data.length) { kfree(extra); return -E2BIG; } -#endif /* WE_STRICT_WRITE */ err = copy_to_user(iwr->u.data.pointer, extra, iwr->u.data.length * @@ -746,7 +792,7 @@ static inline int ioctl_private_call(str iw_handler handler) { struct iwreq * iwr = (struct iwreq *) ifr; - struct iw_priv_args * descr = NULL; + const struct iw_priv_args * descr = NULL; struct iw_request_info info; int extra_size = 0; int i; @@ -786,7 +832,7 @@ static inline int ioctl_private_call(str ((extra_size + offset) <= IFNAMSIZ)) extra_size = 0; } else { - /* Size of set arguments */ + /* Size of get arguments */ extra_size = get_priv_size(descr->get_args); /* Does it fits in iwr ? */ @@ -816,7 +862,7 @@ static inline int ioctl_private_call(str return -EFAULT; /* Does it fits within bounds ? */ - if(iwr->u.data.length > (descr->set_args & + if(iwr->u.data.length > (descr->get_args & IW_PRIV_SIZE_MASK)) return -E2BIG; } else { @@ -856,6 +902,14 @@ static inline int ioctl_private_call(str /* If we have something to return to the user */ if (!ret && IW_IS_GET(cmd)) { + + /* Adjust for the actual length if it's variable, + * avoid leaking kernel bits outside. */ + if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) { + extra_size = adjust_priv_size(descr->get_args, + &(iwr->u)); + } + err = copy_to_user(iwr->u.data.pointer, extra, extra_size); if (err) @@ -1127,9 +1181,25 @@ void wireless_send_event(struct net_devi * One of the main advantage of centralising spy support here is that * it becomes much easier to improve and extend it without having to touch * the drivers. One example is the addition of the Spy-Threshold events. - * Note : IW_WIRELESS_SPY is defined in iw_handler.h */ +/* ---------------------------------------------------------------- */ +/* + * Return the pointer to the spy data in the driver. + * Because this is called on the Rx path via wireless_spy_update(), + * we want it to be efficient... + */ +static inline struct iw_spy_data * get_spydata(struct net_device *dev) +{ + /* This is the new way */ + if(dev->wireless_data) + return(dev->wireless_data->spy_data); + + /* This is the old way. Doesn't work for multi-headed drivers. + * It will be removed in the next version of WE. */ + return (dev->priv + dev->wireless_handlers->spy_offset); +} + /*------------------------------------------------------------------*/ /* * Standard Wireless Handler : set Spy List @@ -1139,16 +1209,30 @@ int iw_handler_set_spy(struct net_device union iwreq_data * wrqu, char * extra) { -#ifdef IW_WIRELESS_SPY - struct iw_spy_data * spydata = (dev->priv + - dev->wireless_handlers->spy_offset); + struct iw_spy_data * spydata = get_spydata(dev); struct sockaddr * address = (struct sockaddr *) extra; + if(!dev->wireless_data) + /* Help user know that driver needs updating */ + printk(KERN_DEBUG "%s (WE) : Driver using old/buggy spy support, please fix driver !\n", + dev->name); + /* Make sure driver is not buggy or using the old API */ + if(!spydata) + return -EOPNOTSUPP; + /* Disable spy collection while we copy the addresses. - * As we don't disable interrupts, we need to do this to avoid races. - * As we are the only writer, this is good enough. */ + * While we copy addresses, any call to wireless_spy_update() + * will NOP. This is OK, as anyway the addresses are changing. */ spydata->spy_number = 0; + /* We want to operate without locking, because wireless_spy_update() + * most likely will happen in the interrupt handler, and therefore + * have it own locking constraints and needs performance. + * The rtnl_lock() make sure we don't race with the other iw_handlers. + * This make sure wireless_spy_update() "see" that the spy list + * is temporarily disabled. */ + wmb(); + /* Are there are addresses to copy? */ if(wrqu->data.length > 0) { int i; @@ -1174,13 +1258,14 @@ int iw_handler_set_spy(struct net_device spydata->spy_address[i][5]); #endif /* WE_SPY_DEBUG */ } + + /* Make sure above is updated before re-enabling */ + wmb(); + /* Enable addresses */ spydata->spy_number = wrqu->data.length; return 0; -#else /* IW_WIRELESS_SPY */ - return -EOPNOTSUPP; -#endif /* IW_WIRELESS_SPY */ } /*------------------------------------------------------------------*/ @@ -1192,12 +1277,14 @@ int iw_handler_get_spy(struct net_device union iwreq_data * wrqu, char * extra) { -#ifdef IW_WIRELESS_SPY - struct iw_spy_data * spydata = (dev->priv + - dev->wireless_handlers->spy_offset); + struct iw_spy_data * spydata = get_spydata(dev); struct sockaddr * address = (struct sockaddr *) extra; int i; + /* Make sure driver is not buggy or using the old API */ + if(!spydata) + return -EOPNOTSUPP; + wrqu->data.length = spydata->spy_number; /* Copy addresses. */ @@ -1214,9 +1301,6 @@ int iw_handler_get_spy(struct net_device for(i = 0; i < spydata->spy_number; i++) spydata->spy_stat[i].updated = 0; return 0; -#else /* IW_WIRELESS_SPY */ - return -EOPNOTSUPP; -#endif /* IW_WIRELESS_SPY */ } /*------------------------------------------------------------------*/ @@ -1228,11 +1312,13 @@ int iw_handler_set_thrspy(struct net_dev union iwreq_data * wrqu, char * extra) { -#ifdef IW_WIRELESS_THRSPY - struct iw_spy_data * spydata = (dev->priv + - dev->wireless_handlers->spy_offset); + struct iw_spy_data * spydata = get_spydata(dev); struct iw_thrspy * threshold = (struct iw_thrspy *) extra; + /* Make sure driver is not buggy or using the old API */ + if(!spydata) + return -EOPNOTSUPP; + /* Just do it */ memcpy(&(spydata->spy_thr_low), &(threshold->low), 2 * sizeof(struct iw_quality)); @@ -1245,9 +1331,6 @@ int iw_handler_set_thrspy(struct net_dev #endif /* WE_SPY_DEBUG */ return 0; -#else /* IW_WIRELESS_THRSPY */ - return -EOPNOTSUPP; -#endif /* IW_WIRELESS_THRSPY */ } /*------------------------------------------------------------------*/ @@ -1259,22 +1342,20 @@ int iw_handler_get_thrspy(struct net_dev union iwreq_data * wrqu, char * extra) { -#ifdef IW_WIRELESS_THRSPY - struct iw_spy_data * spydata = (dev->priv + - dev->wireless_handlers->spy_offset); + struct iw_spy_data * spydata = get_spydata(dev); struct iw_thrspy * threshold = (struct iw_thrspy *) extra; + /* Make sure driver is not buggy or using the old API */ + if(!spydata) + return -EOPNOTSUPP; + /* Just do it */ memcpy(&(threshold->low), &(spydata->spy_thr_low), 2 * sizeof(struct iw_quality)); return 0; -#else /* IW_WIRELESS_THRSPY */ - return -EOPNOTSUPP; -#endif /* IW_WIRELESS_THRSPY */ } -#ifdef IW_WIRELESS_THRSPY /*------------------------------------------------------------------*/ /* * Prepare and send a Spy Threshold event @@ -1312,7 +1393,6 @@ static void iw_send_thrspy_event(struct /* Send event to user space */ wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold); } -#endif /* IW_WIRELESS_THRSPY */ /* ---------------------------------------------------------------- */ /* @@ -1325,12 +1405,14 @@ void wireless_spy_update(struct net_devi unsigned char * address, struct iw_quality * wstats) { -#ifdef IW_WIRELESS_SPY - struct iw_spy_data * spydata = (dev->priv + - dev->wireless_handlers->spy_offset); + struct iw_spy_data * spydata = get_spydata(dev); int i; int match = -1; + /* Make sure driver is not buggy or using the old API */ + if(!spydata) + return; + #ifdef WE_SPY_DEBUG printk(KERN_DEBUG "wireless_spy_update() : offset %ld, spydata %p, address %02X:%02X:%02X:%02X:%02X:%02X\n", dev->wireless_handlers->spy_offset, spydata, address[0], address[1], address[2], address[3], address[4], address[5]); #endif /* WE_SPY_DEBUG */ @@ -1342,7 +1424,7 @@ void wireless_spy_update(struct net_devi sizeof(struct iw_quality)); match = i; } -#ifdef IW_WIRELESS_THRSPY + /* Generate an event if we cross the spy threshold. * To avoid event storms, we have a simple hysteresis : we generate * event only when we go under the low threshold or above the @@ -1362,8 +1444,6 @@ void wireless_spy_update(struct net_devi } } } -#endif /* IW_WIRELESS_THRSPY */ -#endif /* IW_WIRELESS_SPY */ } EXPORT_SYMBOL(iw_handler_get_spy); --- linux-2.6.9-rc1/net/ethernet/eth.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/net/ethernet/eth.c 2004-09-02 20:51:53.000000000 -0700 @@ -164,9 +164,7 @@ unsigned short eth_type_trans(struct sk_ skb->mac.raw=skb->data; skb_pull(skb,ETH_HLEN); eth= skb->mac.ethernet; -#ifdef CONFIG_NET_CLS_ACT skb->input_dev = dev; -#endif if(*eth->h_dest&1) { --- linux-2.6.9-rc1/net/ipv4/arp.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/net/ipv4/arp.c 2004-09-02 20:51:53.000000000 -0700 @@ -695,6 +695,7 @@ void arp_send(int type, int ptype, u32 d static void parp_redo(struct sk_buff *skb) { + nf_reset(skb); arp_rcv(skb, skb->dev, NULL); } --- linux-2.6.9-rc1/net/ipv4/ip_fragment.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/net/ipv4/ip_fragment.c 2004-09-02 20:51:53.000000000 -0700 @@ -241,15 +241,15 @@ static void ipq_kill(struct ipq *ipq) } /* Memory limiting on fragments. Evictor trashes the oldest - * fragment queue until we are back under the low threshold. + * fragment queue until we are back under the threshold. */ -static void ip_evictor(void) +static void __ip_evictor(int threshold) { struct ipq *qp; struct list_head *tmp; int work; - work = atomic_read(&ip_frag_mem) - sysctl_ipfrag_low_thresh; + work = atomic_read(&ip_frag_mem) - threshold; if (work <= 0) return; @@ -274,6 +274,11 @@ static void ip_evictor(void) } } +static inline void ip_evictor(void) +{ + __ip_evictor(sysctl_ipfrag_low_thresh); +} + /* * Oops, a fragment queue timed out. Kill it and send an ICMP reply. */ @@ -684,4 +689,10 @@ void ipfrag_init(void) add_timer(&ipfrag_secret_timer); } +void ipfrag_flush(void) +{ + __ip_evictor(0); +} + EXPORT_SYMBOL(ip_defrag); +EXPORT_SYMBOL(ipfrag_flush); --- linux-2.6.9-rc1/net/ipv4/ip_output.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/net/ipv4/ip_output.c 2004-09-02 20:51:53.000000000 -0700 @@ -78,6 +78,7 @@ #include #include #include +#include #include #include #include @@ -712,7 +713,7 @@ csum_page(struct page *page, int offset, /* * ip_append_data() and ip_append_page() can make one large IP datagram * from many pieces of data. Each pieces will be holded on the socket - * until ip_push_pending_frames() is called. Eache pieces can be a page + * until ip_push_pending_frames() is called. Each piece can be a page * or non-page data. * * Not only UDP, other transport protocols - e.g. raw sockets - can use @@ -780,7 +781,7 @@ int ip_append_data(struct sock *sk, hh_len = LL_RESERVED_SPACE(rt->u.dst.dev); fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0); - maxfraglen = ((mtu-fragheaderlen) & ~7) + fragheaderlen; + maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen; if (inet->cork.length + length > 0xFFFF - fragheaderlen) { ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->dport, mtu-exthdrlen); @@ -792,7 +793,7 @@ int ip_append_data(struct sock *sk, * it won't be fragmented in the future. */ if (transhdrlen && - length + fragheaderlen <= maxfraglen && + length + fragheaderlen <= mtu && rt->u.dst.dev->features&(NETIF_F_IP_CSUM|NETIF_F_NO_CSUM|NETIF_F_HW_CSUM) && !exthdrlen) csummode = CHECKSUM_HW; @@ -804,34 +805,35 @@ int ip_append_data(struct sock *sk, * We use calculated fragment length to generate chained skb, * each of segments is IP fragment ready for sending to network after * adding appropriate IP header. - * - * Mistake is: - * - * If mtu-fragheaderlen is not 0 modulo 8, we generate additional - * small fragment of length (mtu-fragheaderlen)%8, even though - * it is not necessary. Not a big bug, but needs a fix. */ if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) goto alloc_new_skb; while (length > 0) { - if ((copy = maxfraglen - skb->len) <= 0) { + if ((copy = mtu - skb->len) <= 0) { char *data; unsigned int datalen; unsigned int fraglen; + unsigned int fraggap; unsigned int alloclen; + struct sk_buff *skb_prev; BUG_TRAP(copy == 0); alloc_new_skb: - datalen = maxfraglen - fragheaderlen; - if (datalen > length) - datalen = length; + skb_prev = skb; + fraggap = 0; + if (skb_prev) + fraggap = mtu - maxfraglen; + + datalen = mtu - fragheaderlen; + if (datalen > length + fraggap) + datalen = length + fraggap; fraglen = datalen + fragheaderlen; if ((flags & MSG_MORE) && !(rt->u.dst.dev->features&NETIF_F_SG)) - alloclen = maxfraglen; + alloclen = mtu; else alloclen = datalen + fragheaderlen; @@ -875,15 +877,25 @@ alloc_new_skb: data += fragheaderlen; skb->h.raw = data + exthdrlen; - copy = datalen - transhdrlen; - if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, 0, skb) < 0) { + if (fraggap) { + skb->csum = skb_copy_and_csum_bits( + skb_prev, maxfraglen, + data + transhdrlen, fraggap, 0); + skb_prev->csum = csum_sub(skb_prev->csum, + skb->csum); + data += fraggap; + skb_trim(skb_prev, maxfraglen); + } + + copy = datalen - transhdrlen - fraggap; + if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, fraggap, skb) < 0) { err = -EFAULT; kfree_skb(skb); goto error; } offset += copy; - length -= datalen; + length -= datalen - fraggap; transhdrlen = 0; exthdrlen = 0; csummode = CHECKSUM_NONE; @@ -978,7 +990,7 @@ ssize_t ip_append_page(struct sock *sk, int mtu; int len; int err; - unsigned int maxfraglen, fragheaderlen; + unsigned int maxfraglen, fragheaderlen, fraggap; if (inet->hdrincl) return -EPERM; @@ -1000,7 +1012,7 @@ ssize_t ip_append_page(struct sock *sk, mtu = inet->cork.fragsize; fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0); - maxfraglen = ((mtu-fragheaderlen) & ~7) + fragheaderlen; + maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen; if (inet->cork.length + size > 0xFFFF - fragheaderlen) { ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->dport, mtu); @@ -1014,13 +1026,21 @@ ssize_t ip_append_page(struct sock *sk, while (size > 0) { int i; - if ((len = maxfraglen - skb->len) <= 0) { + if ((len = mtu - skb->len) <= 0) { + struct sk_buff *skb_prev; char *data; struct iphdr *iph; + int alloclen; + BUG_TRAP(len == 0); - skb = sock_wmalloc(sk, fragheaderlen + hh_len + 15, 1, - sk->sk_allocation); + skb_prev = skb; + fraggap = 0; + if (skb_prev) + fraggap = mtu - maxfraglen; + + alloclen = fragheaderlen + hh_len + fraggap + 15; + skb = sock_wmalloc(sk, alloclen, 1, sk->sk_allocation); if (unlikely(!skb)) { err = -ENOBUFS; goto error; @@ -1036,11 +1056,20 @@ ssize_t ip_append_page(struct sock *sk, /* * Find where to start putting bytes. */ - data = skb_put(skb, fragheaderlen); + data = skb_put(skb, fragheaderlen + fraggap); skb->nh.iph = iph = (struct iphdr *)data; data += fragheaderlen; skb->h.raw = data; + if (fraggap) { + skb->csum = skb_copy_and_csum_bits( + skb_prev, maxfraglen, + data, fraggap, 0); + skb_prev->csum = csum_sub(skb_prev->csum, + skb->csum); + skb_trim(skb_prev, maxfraglen); + } + /* * Put the packet on the pending queue. */ --- linux-2.6.9-rc1/net/ipv4/netfilter/arp_tables.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/net/ipv4/netfilter/arp_tables.c 2004-09-02 20:51:53.000000000 -0700 @@ -106,7 +106,7 @@ static inline int arp_packet_match(const { char *arpptr = (char *)(arphdr + 1); char *src_devaddr, *tgt_devaddr; - u32 *src_ipaddr, *tgt_ipaddr; + u32 src_ipaddr, tgt_ipaddr; int i, ret; #define FWINV(bool,invflg) ((bool) ^ !!(arpinfo->invflags & invflg)) @@ -145,11 +145,11 @@ static inline int arp_packet_match(const src_devaddr = arpptr; arpptr += dev->addr_len; - src_ipaddr = (u32 *) arpptr; + memcpy(&src_ipaddr, arpptr, sizeof(u32)); arpptr += sizeof(u32); tgt_devaddr = arpptr; arpptr += dev->addr_len; - tgt_ipaddr = (u32 *) arpptr; + memcpy(&tgt_ipaddr, arpptr, sizeof(u32)); if (FWINV(arp_devaddr_compare(&arpinfo->src_devaddr, src_devaddr, dev->addr_len), ARPT_INV_SRCDEVADDR) || @@ -160,19 +160,19 @@ static inline int arp_packet_match(const return 0; } - if (FWINV(((*src_ipaddr) & arpinfo->smsk.s_addr) != arpinfo->src.s_addr, + if (FWINV((src_ipaddr & arpinfo->smsk.s_addr) != arpinfo->src.s_addr, ARPT_INV_SRCIP) || - FWINV((((*tgt_ipaddr) & arpinfo->tmsk.s_addr) != arpinfo->tgt.s_addr), + FWINV(((tgt_ipaddr & arpinfo->tmsk.s_addr) != arpinfo->tgt.s_addr), ARPT_INV_TGTIP)) { dprintf("Source or target IP address mismatch.\n"); dprintf("SRC: %u.%u.%u.%u. Mask: %u.%u.%u.%u. Target: %u.%u.%u.%u.%s\n", - NIPQUAD(*src_ipaddr), + NIPQUAD(src_ipaddr), NIPQUAD(arpinfo->smsk.s_addr), NIPQUAD(arpinfo->src.s_addr), arpinfo->invflags & ARPT_INV_SRCIP ? " (INV)" : ""); dprintf("TGT: %u.%u.%u.%u Mask: %u.%u.%u.%u Target: %u.%u.%u.%u.%s\n", - NIPQUAD(*tgt_ipaddr), + NIPQUAD(tgt_ipaddr), NIPQUAD(arpinfo->tmsk.s_addr), NIPQUAD(arpinfo->tgt.s_addr), arpinfo->invflags & ARPT_INV_TGTIP ? " (INV)" : ""); @@ -193,7 +193,10 @@ static inline int arp_packet_match(const } for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) { - ret |= (((const unsigned long *)outdev)[i] + unsigned long odev; + memcpy(&odev, outdev + i*sizeof(unsigned long), + sizeof(unsigned long)); + ret |= (odev ^ ((const unsigned long *)arpinfo->outiface)[i]) & ((const unsigned long *)arpinfo->outiface_mask)[i]; } --- linux-2.6.9-rc1/net/ipv4/netfilter/ip_conntrack_core.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/net/ipv4/netfilter/ip_conntrack_core.c 2004-09-02 20:51:53.000000000 -0700 @@ -1173,6 +1173,8 @@ void ip_ct_refresh_acct(struct ip_conntr } } +int ip_ct_no_defrag; + /* Returns new sk_buff, or NULL */ struct sk_buff * ip_ct_gather_frags(struct sk_buff *skb) @@ -1181,6 +1183,12 @@ ip_ct_gather_frags(struct sk_buff *skb) #ifdef CONFIG_NETFILTER_DEBUG unsigned int olddebug = skb->nf_debug; #endif + + if (unlikely(ip_ct_no_defrag)) { + kfree_skb(skb); + return NULL; + } + if (sk) { sock_hold(sk); skb_orphan(skb); --- linux-2.6.9-rc1/net/ipv4/netfilter/ip_conntrack_proto_icmp.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/net/ipv4/netfilter/ip_conntrack_proto_icmp.c 2004-09-02 20:51:53.000000000 -0700 @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include --- linux-2.6.9-rc1/net/ipv4/netfilter/ip_conntrack_proto_sctp.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/net/ipv4/netfilter/ip_conntrack_proto_sctp.c 2004-09-02 20:51:53.000000000 -0700 @@ -66,7 +66,7 @@ unsigned long ip_ct_sctp_timeout_shutdow unsigned long ip_ct_sctp_timeout_shutdown_ack_sent = 3 SECS; static unsigned long * sctp_timeouts[] -= { 0, /* SCTP_CONNTRACK_NONE */ += { NULL, /* SCTP_CONNTRACK_NONE */ &ip_ct_sctp_timeout_closed, /* SCTP_CONNTRACK_CLOSED */ &ip_ct_sctp_timeout_cookie_wait, /* SCTP_CONNTRACK_COOKIE_WAIT */ &ip_ct_sctp_timeout_cookie_echoed, /* SCTP_CONNTRACK_COOKIE_ECHOED */ --- linux-2.6.9-rc1/net/ipv4/netfilter/ip_conntrack_proto_tcp.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/net/ipv4/netfilter/ip_conntrack_proto_tcp.c 2004-09-02 20:51:53.000000000 -0700 @@ -33,6 +33,7 @@ #include #include +#include #include #include #include --- linux-2.6.9-rc1/net/ipv4/netfilter/ip_conntrack_proto_udp.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/net/ipv4/netfilter/ip_conntrack_proto_udp.c 2004-09-02 20:51:53.000000000 -0700 @@ -14,6 +14,7 @@ #include #include #include +#include #include unsigned long ip_ct_udp_timeout = 30*HZ; --- linux-2.6.9-rc1/net/ipv4/netfilter/ip_conntrack_standalone.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/net/ipv4/netfilter/ip_conntrack_standalone.c 2004-09-02 20:51:53.000000000 -0700 @@ -73,7 +73,8 @@ static unsigned int seq_print_counters(struct seq_file *s, struct ip_conntrack_counter *counter) { return seq_printf(s, "packets=%llu bytes=%llu ", - counter->packets, counter->bytes); + (unsigned long long)counter->packets, + (unsigned long long)counter->bytes); } #else #define seq_print_counters(x, y) 0 @@ -805,6 +806,12 @@ static int init_or_cleanup(int init) cleanup_defraglocalops: nf_unregister_hook(&ip_conntrack_defrag_local_out_ops); cleanup_defragops: + /* Frag queues may hold fragments with skb->dst == NULL */ + ip_ct_no_defrag = 1; + synchronize_net(); + local_bh_disable(); + ipfrag_flush(); + local_bh_enable(); nf_unregister_hook(&ip_conntrack_defrag_ops); cleanup_proc_stat: proc_net_remove("ip_conntrack_stat"); --- linux-2.6.9-rc1/net/ipv4/netfilter/ip_nat_core.c 2004-08-24 00:01:55.000000000 -0700 +++ 25/net/ipv4/netfilter/ip_nat_core.c 2004-09-02 20:51:53.000000000 -0700 @@ -635,7 +635,7 @@ ip_nat_setup_info(struct ip_conntrack *c /* If there's a helper, assign it; based on new tuple. */ if (!conntrack->master) - info->helper = ip_nat_find_helper(&reply); + info->helper = __ip_nat_find_helper(&reply); /* It's done. */ info->initialized |= (1 << HOOK2MANIP(hooknum)); --- linux-2.6.9-rc1/net/ipv4/netfilter/ip_nat_helper.c 2004-08-24 00:02:19.000000000 -0700 +++ 25/net/ipv4/netfilter/ip_nat_helper.c 2004-09-02 20:51:53.000000000 -0700 @@ -421,12 +421,18 @@ int ip_nat_helper_register(struct ip_nat } struct ip_nat_helper * +__ip_nat_find_helper(const struct ip_conntrack_tuple *tuple) +{ + return LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *, tuple); +} + +struct ip_nat_helper * ip_nat_find_helper(const struct ip_conntrack_tuple *tuple) { struct ip_nat_helper *h; READ_LOCK(&ip_nat_lock); - h = LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *, tuple); + h = __ip_nat_find_helper(tuple); READ_UNLOCK(&ip_nat_lock); return h; --- linux-2.6.9-rc1/net/ipv4/netfilter/ip_nat_standalone.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/net/ipv4/netfilter/ip_nat_standalone.c 2004-09-02 20:51:53.000000000 -0700 @@ -394,4 +394,6 @@ EXPORT_SYMBOL(ip_nat_cheat_check); EXPORT_SYMBOL(ip_nat_mangle_tcp_packet); EXPORT_SYMBOL(ip_nat_mangle_udp_packet); EXPORT_SYMBOL(ip_nat_used_tuple); +EXPORT_SYMBOL(ip_nat_find_helper); +EXPORT_SYMBOL(__ip_nat_find_helper); MODULE_LICENSE("GPL"); --- linux-2.6.9-rc1/net/ipv4/netfilter/ipt_LOG.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/net/ipv4/netfilter/ipt_LOG.c 2004-09-03 00:56:35.509762344 -0700 @@ -41,7 +41,7 @@ MODULE_PARM_DESC(nflog, "register as int static spinlock_t log_lock = SPIN_LOCK_UNLOCKED; /* One level of recursion won't kill us */ -static void dump_packet(const struct ipt_log_info *info, +void dump_packet(const struct ipt_log_info *info, const struct sk_buff *skb, unsigned int iphoff) { @@ -473,5 +473,6 @@ static void __exit fini(void) ipt_unregister_target(&ipt_log_reg); } +EXPORT_SYMBOL_GPL(dump_packet); module_init(init); module_exit(fini); --- linux-2.6.9-rc1/net/ipv4/netfilter/Kconfig 2004-08-24 00:03:31.000000000 -0700 +++ 25/net/ipv4/netfilter/Kconfig 2004-09-02 20:51:53.000000000 -0700 @@ -5,6 +5,7 @@ menu "IP: Netfilter Configuration" depends on INET && NETFILTER +# connection tracking, helpers and protocols config IP_NF_CONNTRACK tristate "Connection tracking (required for masq/NAT)" ---help--- @@ -19,6 +20,28 @@ config IP_NF_CONNTRACK To compile it as a module, choose M here. If unsure, say N. +config IP_NF_CT_ACCT + bool "Connection tracking flow accounting" + depends on IP_NF_CONNTRACK + help + If this option is enabled, the connection tracking code will + keep per-flow packet and byte counters. + + Those counters can be used for flow-based accounting or the + `connbytes' match. + + If unsure, say `N'. + +config IP_NF_CT_PROTO_SCTP + tristate 'SCTP protocol connection tracking support (EXPERIMENTAL)' + depends on IP_NF_CONNTRACK && EXPERIMENTAL + help + With this option enabled, the connection tracking code will + be able to do state tracking on SCTP connections. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + config IP_NF_FTP tristate "FTP protocol support" depends on IP_NF_CONNTRACK @@ -86,7 +109,7 @@ config IP_NF_IPTABLES To compile it as a module, choose M here. If unsure, say N. -# The simple matches. +# The matches. config IP_NF_MATCH_LIMIT tristate "limit match support" depends on IP_NF_IPTABLES @@ -274,7 +297,42 @@ config IP_NF_MATCH_PHYSDEV To compile it as a module, choose M here. If unsure, say N. -# The targets +config IP_NF_MATCH_ADDRTYPE + tristate 'address type match support' + depends on IP_NF_IPTABLES + help + This option allows you to match what routing thinks of an address, + eg. UNICAST, LOCAL, BROADCAST, ... + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +config IP_NF_MATCH_REALM + tristate 'realm match support' + depends on IP_NF_IPTABLES + select NET_CLS_ROUTE + help + This option adds a `realm' match, which allows you to use the realm + key from the routing subsytem inside iptables. + + This match pretty much resembles the CONFIG_NET_CLS_ROUTE4 option + in tc world. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +config IP_NF_MATCH_SCTP + tristate 'SCTP protocol match support' + depends on IP_NF_IPTABLES + help + With this option enabled, you will be able to use the iptables + `sctp' match in order to match on SCTP source/destination ports + and SCTP chunk types. + + If you want to compile it as a module, say M here and read + Documentation/modules.txt. If unsure, say `N'. + +# `filter', generic and specific targets config IP_NF_FILTER tristate "Packet filtering" depends on IP_NF_IPTABLES @@ -295,6 +353,56 @@ config IP_NF_TARGET_REJECT To compile it as a module, choose M here. If unsure, say N. +config IP_NF_TARGET_LOG + tristate "LOG target support" + depends on IP_NF_IPTABLES + help + This option adds a `LOG' target, which allows you to create rules in + any iptables table which records the packet header to the syslog. + + To compile it as a module, choose M here. If unsure, say N. + +config IP_NF_TARGET_ULOG + tristate "ULOG target support" + depends on IP_NF_IPTABLES + ---help--- + This option adds a `ULOG' target, which allows you to create rules in + any iptables table. The packet is passed to a userspace logging + daemon using netlink multicast sockets; unlike the LOG target + which can only be viewed through syslog. + + The apropriate userspace logging daemon (ulogd) may be obtained from + + + To compile it as a module, choose M here. If unsure, say N. + +config IP_NF_TARGET_TCPMSS + tristate "TCPMSS target support" + depends on IP_NF_IPTABLES + ---help--- + This option adds a `TCPMSS' target, which allows you to alter the + MSS value of TCP SYN packets, to control the maximum size for that + connection (usually limiting it to your outgoing interface's MTU + minus 40). + + This is used to overcome criminally braindead ISPs or servers which + block ICMP Fragmentation Needed packets. The symptoms of this + problem are that everything works fine from your Linux + firewall/router, but machines behind it can never exchange large + packets: + 1) Web browsers connect, then hang with no data received. + 2) Small mail works fine, but large emails hang. + 3) ssh works fine, but scp hangs after initial handshaking. + + Workaround: activate this option and add a rule to your firewall + configuration like: + + iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN \ + -j TCPMSS --clamp-mss-to-pmtu + + To compile it as a module, choose M here. If unsure, say N. + +# NAT + specific targets config IP_NF_NAT tristate "Full NAT" depends on IP_NF_IPTABLES && IP_NF_CONNTRACK @@ -408,6 +516,7 @@ config IP_NF_NAT_AMANDA default IP_NF_NAT if IP_NF_AMANDA=y default m if IP_NF_AMANDA=m +# mangle + specific targets config IP_NF_MANGLE tristate "Packet mangling" depends on IP_NF_IPTABLES @@ -478,55 +587,34 @@ config IP_NF_TARGET_CLASSIFY To compile it as a module, choose M here. If unsure, say N. -config IP_NF_TARGET_LOG - tristate "LOG target support" +# raw + specific targets +config IP_NF_RAW + tristate 'raw table support (required for NOTRACK/TRACE)' depends on IP_NF_IPTABLES help - This option adds a `LOG' target, which allows you to create rules in - any iptables table which records the packet header to the syslog. - - To compile it as a module, choose M here. If unsure, say N. - -config IP_NF_TARGET_ULOG - tristate "ULOG target support" - depends on IP_NF_IPTABLES - ---help--- - This option adds a `ULOG' target, which allows you to create rules in - any iptables table. The packet is passed to a userspace logging - daemon using netlink multicast sockets; unlike the LOG target - which can only be viewed through syslog. - - The apropriate userspace logging daemon (ulogd) may be obtained from - - - To compile it as a module, choose M here. If unsure, say N. - -config IP_NF_TARGET_TCPMSS - tristate "TCPMSS target support" - depends on IP_NF_IPTABLES - ---help--- - This option adds a `TCPMSS' target, which allows you to alter the - MSS value of TCP SYN packets, to control the maximum size for that - connection (usually limiting it to your outgoing interface's MTU - minus 40). - - This is used to overcome criminally braindead ISPs or servers which - block ICMP Fragmentation Needed packets. The symptoms of this - problem are that everything works fine from your Linux - firewall/router, but machines behind it can never exchange large - packets: - 1) Web browsers connect, then hang with no data received. - 2) Small mail works fine, but large emails hang. - 3) ssh works fine, but scp hangs after initial handshaking. - - Workaround: activate this option and add a rule to your firewall - configuration like: + This option adds a `raw' table to iptables. This table is the very + first in the netfilter framework and hooks in at the PREROUTING + and OUTPUT chains. + + If you want to compile it as a module, say M here and read + . If unsure, say `N'. + help - iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN \ - -j TCPMSS --clamp-mss-to-pmtu +config IP_NF_TARGET_NOTRACK + tristate 'NOTRACK target support' + depends on IP_NF_RAW + depends on IP_NF_CONNTRACK + help + The NOTRACK target allows a select rule to specify + which packets *not* to enter the conntrack/NAT + subsystem with all the consequences (no ICMP error tracking, + no protocol helpers for the selected packets). + + If you want to compile it as a module, say M here and read + . If unsure, say `N'. - To compile it as a module, choose M here. If unsure, say N. +# ARP tables config IP_NF_ARPTABLES tristate "ARP tables support" help @@ -579,66 +667,5 @@ config IP_NF_COMPAT_IPFWADM To compile it as a module, choose M here. If unsure, say N. -config IP_NF_TARGET_NOTRACK - tristate 'NOTRACK target support' - depends on IP_NF_RAW - depends on IP_NF_CONNTRACK - help - The NOTRACK target allows a select rule to specify - which packets *not* to enter the conntrack/NAT - subsystem with all the consequences (no ICMP error tracking, - no protocol helpers for the selected packets). - - If you want to compile it as a module, say M here and read - . If unsure, say `N'. - -config IP_NF_RAW - tristate 'raw table support (required for NOTRACK/TRACE)' - depends on IP_NF_IPTABLES - help - This option adds a `raw' table to iptables. This table is the very - first in the netfilter framework and hooks in at the PREROUTING - and OUTPUT chains. - - If you want to compile it as a module, say M here and read - . If unsure, say `N'. - help - -config IP_NF_MATCH_ADDRTYPE - tristate 'address type match support' - depends on IP_NF_IPTABLES - help - This option allows you to match what routing thinks of an address, - eg. UNICAST, LOCAL, BROADCAST, ... - - If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. - -config IP_NF_MATCH_REALM - tristate 'realm match support' - depends on IP_NF_IPTABLES - select NET_CLS_ROUTE - help - This option adds a `realm' match, which allows you to use the realm - key from the routing subsytem inside iptables. - - This match pretty much resembles the CONFIG_NET_CLS_ROUTE4 option - in tc world. - - If you want to compile it as a module, say M here and read - Documentation/modules.txt. If unsure, say `N'. - -config IP_NF_CT_ACCT - bool "Connection tracking flow accounting" - depends on IP_NF_CONNTRACK - -config IP_NF_MATCH_SCTP - tristate 'SCTP protocol match support' - depends on IP_NF_IPTABLES - -config IP_NF_CT_PROTO_SCTP - tristate 'SCTP protocol connection tracking support (EXPERIMENTAL)' - depends on IP_NF_CONNTRACK && EXPERIMENTAL - endmenu --- linux-2.6.9-rc1/net/ipv4/sysctl_net_ipv4.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/net/ipv4/sysctl_net_ipv4.c 2004-09-02 20:51:53.000000000 -0700 @@ -667,14 +667,6 @@ ctl_table ipv4_table[] = { .proc_handler = &proc_dointvec, }, { - .ctl_name = NET_TCP_DEFAULT_WIN_SCALE, - .procname = "tcp_default_win_scale", - .data = &sysctl_tcp_default_win_scale, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, - { .ctl_name = NET_TCP_MODERATE_RCVBUF, .procname = "tcp_moderate_rcvbuf", .data = &sysctl_tcp_moderate_rcvbuf, --- linux-2.6.9-rc1/net/ipv4/tcp.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/net/ipv4/tcp.c 2004-09-02 20:51:53.000000000 -0700 @@ -276,8 +276,6 @@ kmem_cache_t *tcp_timewait_cachep; atomic_t tcp_orphan_count = ATOMIC_INIT(0); -int sysctl_tcp_default_win_scale = 7; - int sysctl_tcp_mem[3]; int sysctl_tcp_wmem[3] = { 4 * 1024, 16 * 1024, 128 * 1024 }; int sysctl_tcp_rmem[3] = { 4 * 1024, 87380, 87380 * 2 }; --- linux-2.6.9-rc1/net/ipv4/tcp_output.c 2004-08-24 00:03:17.000000000 -0700 +++ 25/net/ipv4/tcp_output.c 2004-09-02 20:51:53.000000000 -0700 @@ -143,6 +143,65 @@ static __inline__ void tcp_event_ack_sen tcp_clear_xmit_timer(sk, TCP_TIME_DACK); } +/* Determine a window scaling and initial window to offer. + * Based on the assumption that the given amount of space + * will be offered. Store the results in the tp structure. + * NOTE: for smooth operation initial space offering should + * be a multiple of mss if possible. We assume here that mss >= 1. + * This MUST be enforced by all callers. + */ +void tcp_select_initial_window(int __space, __u32 mss, + __u32 *rcv_wnd, __u32 *window_clamp, + int wscale_ok, __u8 *rcv_wscale) +{ + unsigned int space = (__space < 0 ? 0 : __space); + + /* If no clamp set the clamp to the max possible scaled window */ + if (*window_clamp == 0) + (*window_clamp) = (65535 << 14); + space = min(*window_clamp, space); + + /* Quantize space offering to a multiple of mss if possible. */ + if (space > mss) + space = (space / mss) * mss; + + /* NOTE: offering an initial window larger than 32767 + * will break some buggy TCP stacks. We try to be nice. + * If we are not window scaling, then this truncates + * our initial window offering to 32k. There should also + * be a sysctl option to stop being nice. + */ + (*rcv_wnd) = min(space, MAX_TCP_WINDOW); + (*rcv_wscale) = 0; + if (wscale_ok) { + /* Set window scaling on max possible window + * See RFC1323 for an explanation of the limit to 14 + */ + space = max_t(u32, sysctl_tcp_rmem[2], sysctl_rmem_max); + while (space > 65535 && (*rcv_wscale) < 14) { + space >>= 1; + (*rcv_wscale)++; + } + } + + /* Set initial window to value enough for senders, + * following RFC1414. Senders, not following this RFC, + * will be satisfied with 2. + */ + if (mss > (1<<*rcv_wscale)) { + int init_cwnd = 4; + if (mss > 1460*3) + init_cwnd = 2; + else if (mss > 1460) + init_cwnd = 3; + if (*rcv_wnd > init_cwnd*mss) + *rcv_wnd = init_cwnd*mss; + } + + /* Set the clamp no higher than max representable value */ + (*window_clamp) = min(65535U << (*rcv_wscale), *window_clamp); +} + /* Chose a new window to advertise, update state in tcp_opt for the * socket, and return result with RFC1323 scaling applied. The return * value can be stuffed directly into th->window for an outgoing --- linux-2.6.9-rc1/net/ipv4/xfrm4_output.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/net/ipv4/xfrm4_output.c 2004-09-02 20:51:53.000000000 -0700 @@ -58,8 +58,7 @@ static void xfrm4_encap(struct sk_buff * if (!top_iph->frag_off) __ip_select_ident(top_iph, dst, 0); - /* TTL disclosed */ - top_iph->ttl = iph->ttl; + top_iph->ttl = dst_path_metric(dst, RTAX_HOPLIMIT); top_iph->saddr = x->props.saddr.a4; top_iph->daddr = x->id.daddr.a4; --- linux-2.6.9-rc1/net/ipv6/datagram.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/net/ipv6/datagram.c 2004-09-02 20:51:53.000000000 -0700 @@ -38,7 +38,7 @@ int ip6_datagram_connect(struct sock *sk struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr; struct inet_opt *inet = inet_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk); - struct in6_addr *daddr; + struct in6_addr *daddr, *final_p = NULL, final; struct dst_entry *dst; struct flowi fl; struct ip6_flowlabel *flowlabel = NULL; @@ -157,16 +157,27 @@ ipv4_connected: if (flowlabel) { if (flowlabel->opt && flowlabel->opt->srcrt) { struct rt0_hdr *rt0 = (struct rt0_hdr *) flowlabel->opt->srcrt; + ipv6_addr_copy(&final, &fl.fl6_dst); ipv6_addr_copy(&fl.fl6_dst, rt0->addr); + final_p = &final; } } else if (np->opt && np->opt->srcrt) { struct rt0_hdr *rt0 = (struct rt0_hdr *)np->opt->srcrt; + ipv6_addr_copy(&final, &fl.fl6_dst); ipv6_addr_copy(&fl.fl6_dst, rt0->addr); + final_p = &final; } err = ip6_dst_lookup(sk, &dst, &fl); if (err) goto out; + if (final_p) + ipv6_addr_copy(&fl.fl6_dst, final_p); + + if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) { + dst_release(dst); + goto out; + } /* source address lookup done in ip6_dst_lookup */ --- linux-2.6.9-rc1/net/ipv6/ip6_output.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/net/ipv6/ip6_output.c 2004-09-02 20:51:53.000000000 -0700 @@ -54,6 +54,7 @@ #include #include #include +#include static int ip6_fragment(struct sk_buff **pskb, int (*output)(struct sk_buff**)); @@ -796,10 +797,6 @@ int ip6_dst_lookup(struct sock *sk, stru goto out_err_release; } } - if ((err = xfrm_lookup(dst, fl, sk, 0)) < 0) { - err = -ENETUNREACH; - goto out_err_release; - } return 0; @@ -821,7 +818,7 @@ int ip6_append_data(struct sock *sk, int int exthdrlen; int hh_len; int mtu; - int copy = 0; + int copy; int err; int offset = 0; int csummode = CHECKSUM_NONE; @@ -879,29 +876,71 @@ int ip6_append_data(struct sock *sk, int } } + /* + * Let's try using as much space as possible. + * Use MTU if total length of the message fits into the MTU. + * Otherwise, we need to reserve fragment header and + * fragment alignment (= 8-15 octects, in total). + * + * Note that we may need to "move" the data from the tail of + * of the buffer to the new fragment when we split + * the message. + * + * FIXME: It may be fragmented into multiple chunks + * at once if non-fragmentable extension headers + * are too large. + * --yoshfuji + */ + inet->cork.length += length; if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) goto alloc_new_skb; while (length > 0) { - if ((copy = maxfraglen - skb->len) <= 0) { + if ((copy = mtu - skb->len) <= 0) { char *data; unsigned int datalen; unsigned int fraglen; + unsigned int fraggap; unsigned int alloclen; + struct sk_buff *skb_prev; BUG_TRAP(copy == 0); alloc_new_skb: - datalen = maxfraglen - fragheaderlen; - if (datalen > length) - datalen = length; + skb_prev = skb; + + /* There's no room in the current skb */ + fraggap = 0; + if (skb_prev) + fraggap = mtu - maxfraglen; + + datalen = mtu - fragheaderlen; + + if (datalen > length + fraggap) + datalen = length + fraggap; + fraglen = datalen + fragheaderlen; if ((flags & MSG_MORE) && !(rt->u.dst.dev->features&NETIF_F_SG)) - alloclen = maxfraglen; + alloclen = mtu; else - alloclen = fraglen; + alloclen = datalen + fragheaderlen; + + /* + * The last fragment gets additional space at tail. + * Note: we overallocate on fragments with MSG_MODE + * because we have no idea if we're the last one. + */ + if (datalen == length + fraggap) + alloclen += rt->u.dst.trailer_len; + + /* + * We just reserve space for fragment header. + * Note: this may be overallocation if the message + * (without MSG_MORE) fits into the MTU. + */ alloclen += sizeof(struct frag_hdr); + if (transhdrlen) { skb = sock_alloc_send_skb(sk, alloclen + hh_len, @@ -923,7 +962,7 @@ alloc_new_skb: */ skb->ip_summed = csummode; skb->csum = 0; - /* reserve 8 byte for fragmentation */ + /* reserve for fragmentation */ skb_reserve(skb, hh_len+sizeof(struct frag_hdr)); /* @@ -933,15 +972,29 @@ alloc_new_skb: skb->nh.raw = data + exthdrlen; data += fragheaderlen; skb->h.raw = data + exthdrlen; - copy = datalen - transhdrlen; - if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, 0, skb) < 0) { + + if (fraggap) { + skb->csum = skb_copy_and_csum_bits( + skb_prev, maxfraglen, + data + transhdrlen, fraggap, 0); + skb_prev->csum = csum_sub(skb_prev->csum, + skb->csum); + data += fraggap; + skb_trim(skb_prev, maxfraglen); + } + copy = datalen - transhdrlen - fraggap; + if (copy < 0) { + err = -EINVAL; + kfree_skb(skb); + goto error; + } else if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, fraggap, skb) < 0) { err = -EFAULT; kfree_skb(skb); goto error; } offset += copy; - length -= datalen; + length -= datalen - fraggap; transhdrlen = 0; exthdrlen = 0; csummode = CHECKSUM_NONE; --- linux-2.6.9-rc1/net/ipv6/netfilter/ip6t_LOG.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/net/ipv6/netfilter/ip6t_LOG.c 2004-09-02 20:51:53.000000000 -0700 @@ -61,7 +61,7 @@ static u_int8_t ip6_nexthdr(u_int8_t cur repeatedly...with a large stick...no, an even LARGER stick...no, you're still not thinking big enough */ nexthdr = **hdrptr; - hdrlen = *hdrptr[1] * 4 + 8; + hdrlen = (*hdrptr)[1] * 4 + 8; *hdrptr = *hdrptr + hdrlen; break; /*stupid rfc2402 */ @@ -69,7 +69,7 @@ static u_int8_t ip6_nexthdr(u_int8_t cur case IPPROTO_ROUTING: case IPPROTO_HOPOPTS: nexthdr = **hdrptr; - hdrlen = *hdrptr[1] * 8 + 8; + hdrlen = (*hdrptr)[1] * 8 + 8; *hdrptr = *hdrptr + hdrlen; break; case IPPROTO_FRAGMENT: --- linux-2.6.9-rc1/net/ipv6/raw.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/net/ipv6/raw.c 2004-09-02 20:51:53.000000000 -0700 @@ -606,7 +606,7 @@ static int rawv6_sendmsg(struct kiocb *i { struct ipv6_txoptions opt_space; struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *) msg->msg_name; - struct in6_addr *daddr; + struct in6_addr *daddr, *final_p = NULL, final; struct inet_opt *inet = inet_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk); struct raw6_opt *raw_opt = raw6_sk(sk); @@ -729,7 +729,9 @@ static int rawv6_sendmsg(struct kiocb *i /* merge ip6_build_xmit from ip6_output */ if (opt && opt->srcrt) { struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt; + ipv6_addr_copy(&final, &fl.fl6_dst); ipv6_addr_copy(&fl.fl6_dst, rt0->addr); + final_p = &final; } if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst)) @@ -738,6 +740,13 @@ static int rawv6_sendmsg(struct kiocb *i err = ip6_dst_lookup(sk, &dst, &fl); if (err) goto out; + if (final_p) + ipv6_addr_copy(&fl.fl6_dst, final_p); + + if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) { + dst_release(dst); + goto out; + } if (hlimit < 0) { if (ipv6_addr_is_multicast(&fl.fl6_dst)) --- linux-2.6.9-rc1/net/ipv6/route.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/net/ipv6/route.c 2004-09-02 20:51:53.000000000 -0700 @@ -820,9 +820,12 @@ int ip6_route_add(struct in6_rtmsg *rtms */ if ((rtmsg->rtmsg_flags&RTF_REJECT) || (dev && (dev->flags&IFF_LOOPBACK) && !(addr_type&IPV6_ADDR_LOOPBACK))) { - if (dev && dev != &loopback_dev) { - dev_put(dev); - in6_dev_put(idev); + /* hold loopback dev/idev if we haven't done so. */ + if (dev != &loopback_dev) { + if (dev) { + dev_put(dev); + in6_dev_put(idev); + } dev = &loopback_dev; dev_hold(dev); idev = in6_dev_get(dev); --- linux-2.6.9-rc1/net/ipv6/tcp_ipv6.c 2004-08-24 00:02:57.000000000 -0700 +++ 25/net/ipv6/tcp_ipv6.c 2004-09-02 20:51:53.000000000 -0700 @@ -549,7 +549,7 @@ static int tcp_v6_connect(struct sock *s struct inet_opt *inet = inet_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk); struct tcp_opt *tp = tcp_sk(sk); - struct in6_addr *saddr = NULL; + struct in6_addr *saddr = NULL, *final_p = NULL, final; struct flowi fl; struct dst_entry *dst; int addr_type; @@ -666,13 +666,21 @@ static int tcp_v6_connect(struct sock *s if (np->opt && np->opt->srcrt) { struct rt0_hdr *rt0 = (struct rt0_hdr *)np->opt->srcrt; + ipv6_addr_copy(&final, &fl.fl6_dst); ipv6_addr_copy(&fl.fl6_dst, rt0->addr); + final_p = &final; } err = ip6_dst_lookup(sk, &dst, &fl); - if (err) goto failure; + if (final_p) + ipv6_addr_copy(&fl.fl6_dst, final_p); + + if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) { + dst_release(dst); + goto failure; + } if (saddr == NULL) { saddr = &fl.fl6_src; @@ -793,6 +801,12 @@ static void tcp_v6_err(struct sk_buff *s sk->sk_err_soft = -err; goto out; } + + if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) { + sk->sk_err_soft = -err; + goto out; + } + } else dst_hold(dst); @@ -863,6 +877,7 @@ static int tcp_v6_send_synack(struct soc struct ipv6_pinfo *np = inet6_sk(sk); struct sk_buff * skb; struct ipv6_txoptions *opt = NULL; + struct in6_addr * final_p = NULL, final; struct flowi fl; int err = -1; @@ -888,12 +903,18 @@ static int tcp_v6_send_synack(struct soc if (opt && opt->srcrt) { struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt; + ipv6_addr_copy(&final, &fl.fl6_dst); ipv6_addr_copy(&fl.fl6_dst, rt0->addr); + final_p = &final; } err = ip6_dst_lookup(sk, &dst, &fl); if (err) goto done; + if (final_p) + ipv6_addr_copy(&fl.fl6_dst, final_p); + if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) + goto done; } skb = tcp_make_synack(sk, dst, req); @@ -1021,6 +1042,12 @@ static void tcp_v6_send_reset(struct sk_ /* sk = NULL, but it is safe for now. RST socket required. */ if (!ip6_dst_lookup(NULL, &buff->dst, &fl)) { + + if ((xfrm_lookup(&buff->dst, &fl, NULL, 0)) < 0) { + dst_release(buff->dst); + return; + } + ip6_xmit(NULL, buff, &fl, NULL, 0); TCP_INC_STATS_BH(TCP_MIB_OUTSEGS); TCP_INC_STATS_BH(TCP_MIB_OUTRSTS); @@ -1082,6 +1109,10 @@ static void tcp_v6_send_ack(struct sk_bu fl.fl_ip_sport = t1->source; if (!ip6_dst_lookup(NULL, &buff->dst, &fl)) { + if ((xfrm_lookup(&buff->dst, &fl, NULL, 0)) < 0) { + dst_release(buff->dst); + return; + } ip6_xmit(NULL, buff, &fl, NULL, 0); TCP_INC_STATS_BH(TCP_MIB_OUTSEGS); return; @@ -1313,6 +1344,7 @@ static struct sock * tcp_v6_syn_recv_soc } if (dst == NULL) { + struct in6_addr *final_p = NULL, final; struct flowi fl; memset(&fl, 0, sizeof(fl)); @@ -1320,7 +1352,9 @@ static struct sock * tcp_v6_syn_recv_soc ipv6_addr_copy(&fl.fl6_dst, &req->af.v6_req.rmt_addr); if (opt && opt->srcrt) { struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt; + ipv6_addr_copy(&final, &fl.fl6_dst); ipv6_addr_copy(&fl.fl6_dst, rt0->addr); + final_p = &final; } ipv6_addr_copy(&fl.fl6_src, &req->af.v6_req.loc_addr); fl.oif = sk->sk_bound_dev_if; @@ -1329,6 +1363,12 @@ static struct sock * tcp_v6_syn_recv_soc if (ip6_dst_lookup(sk, &dst, &fl)) goto out; + + if (final_p) + ipv6_addr_copy(&fl.fl6_dst, final_p); + + if ((xfrm_lookup(&dst, &fl, sk, 0)) < 0) + goto out; } newsk = tcp_create_openreq_child(sk, req, skb); @@ -1710,6 +1750,7 @@ static int tcp_v6_rebuild_header(struct if (dst == NULL) { struct inet_opt *inet = inet_sk(sk); + struct in6_addr *final_p = NULL, final; struct flowi fl; memset(&fl, 0, sizeof(fl)); @@ -1723,15 +1764,24 @@ static int tcp_v6_rebuild_header(struct if (np->opt && np->opt->srcrt) { struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt; + ipv6_addr_copy(&final, &fl.fl6_dst); ipv6_addr_copy(&fl.fl6_dst, rt0->addr); + final_p = &final; } err = ip6_dst_lookup(sk, &dst, &fl); - if (err) { sk->sk_route_caps = 0; return err; } + if (final_p) + ipv6_addr_copy(&fl.fl6_dst, final_p); + + if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) { + sk->sk_err_soft = -err; + dst_release(dst); + return err; + } ip6_dst_store(sk, dst, NULL); sk->sk_route_caps = dst->dev->features & @@ -1775,6 +1825,12 @@ static int tcp_v6_xmit(struct sk_buff *s return err; } + if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) { + sk->sk_route_caps = 0; + dst_release(dst); + return err; + } + ip6_dst_store(sk, dst, NULL); sk->sk_route_caps = dst->dev->features & ~(NETIF_F_IP_CSUM | NETIF_F_TSO); --- linux-2.6.9-rc1/net/ipv6/udp.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/net/ipv6/udp.c 2004-09-02 20:51:53.000000000 -0700 @@ -627,7 +627,7 @@ static int udpv6_sendmsg(struct kiocb *i struct inet_opt *inet = inet_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk); struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) msg->msg_name; - struct in6_addr *daddr; + struct in6_addr *daddr, *final_p = NULL, final; struct ipv6_txoptions *opt = NULL; struct ip6_flowlabel *flowlabel = NULL; struct flowi *fl = &inet->cork.fl; @@ -783,7 +783,9 @@ do_udp_sendmsg: /* merge ip6_build_xmit from ip6_output */ if (opt && opt->srcrt) { struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt; + ipv6_addr_copy(&final, &fl->fl6_dst); ipv6_addr_copy(&fl->fl6_dst, rt0->addr); + final_p = &final; } if (!fl->oif && ipv6_addr_is_multicast(&fl->fl6_dst)) @@ -792,6 +794,13 @@ do_udp_sendmsg: err = ip6_dst_lookup(sk, &dst, fl); if (err) goto out; + if (final_p) + ipv6_addr_copy(&fl->fl6_dst, final_p); + + if ((err = xfrm_lookup(&dst, fl, sk, 0)) < 0) { + dst_release(dst); + goto out; + } if (hlimit < 0) { if (ipv6_addr_is_multicast(&fl->fl6_dst)) --- linux-2.6.9-rc1/net/ipv6/xfrm6_output.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/net/ipv6/xfrm6_output.c 2004-09-02 20:51:53.000000000 -0700 @@ -64,7 +64,7 @@ static void xfrm6_encap(struct sk_buff * top_iph->flow_lbl[1] = iph->flow_lbl[1]; top_iph->flow_lbl[2] = iph->flow_lbl[2]; top_iph->nexthdr = IPPROTO_IPV6; - top_iph->hop_limit = iph->hop_limit; + top_iph->hop_limit = dst_path_metric(dst, RTAX_HOPLIMIT); ipv6_addr_copy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr); ipv6_addr_copy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr); } --- linux-2.6.9-rc1/net/irda/irlan/irlan_client.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/net/irda/irlan/irlan_client.c 2004-09-03 00:56:32.643198128 -0700 @@ -234,7 +234,7 @@ static void irlan_client_ctrl_disconnect ASSERT(tsap == self->client.tsap_ctrl, return;); /* Remove frames queued on the control channel */ - while ((skb = skb_dequeue(&self->client.txq))) { + while ((skb = skb_dequeue(&self->client.txq)) != NULL) { dev_kfree_skb(skb); } self->client.tx_busy = FALSE; --- linux-2.6.9-rc1/net/irda/irlan/irlan_common.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/net/irda/irlan/irlan_common.c 2004-09-02 20:51:53.000000000 -0700 @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -1181,9 +1182,9 @@ MODULE_AUTHOR("Dag Brattli #include #include +#include #define Nprintk(a...) @@ -69,6 +70,14 @@ struct netlink_opt #define nlk_sk(__sk) ((struct netlink_opt *)(__sk)->sk_protinfo) +struct netlink_work +{ + struct sock *sk; + int len; + struct work_struct work; +}; + +static struct workqueue_struct *netlink_wq; static struct hlist_head nl_table[MAX_LINKS]; static DECLARE_WAIT_QUEUE_HEAD(nl_table_wait); static unsigned nl_nonroot[MAX_LINKS]; @@ -87,6 +96,16 @@ static atomic_t nl_table_users = ATOMIC_ static struct notifier_block *netlink_chain; +/* netlink workqueue handler */ +static void netlink_wq_handler(void *data) +{ + struct netlink_work *work = data; + + work->sk->sk_data_ready(work->sk, work->len); + sock_put(work->sk); + kfree(work); +} + static void netlink_sock_destruct(struct sock *sk) { skb_queue_purge(&sk->sk_receive_queue); @@ -478,6 +497,8 @@ int netlink_attachskb(struct sock *sk, s if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf || test_bit(0, &nlk->state)) { DECLARE_WAITQUEUE(wait, current); + task_t *client; + if (!timeo) { if (!nlk->pid) netlink_overrun(sk); @@ -486,6 +507,19 @@ int netlink_attachskb(struct sock *sk, s return -EAGAIN; } + if (nlk->pid) { + /* Kernel is sending information to user space + * and socket buffer is full: Wake up user + * process */ + client = find_task_by_pid(nlk->pid); + if (!client) { + sock_put(sk); + kfree_skb(skb); + return -EAGAIN; + } + wake_up_process(client); + } + __set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&nlk->wait, &wait); @@ -525,8 +559,24 @@ int netlink_sendskb(struct sock *sk, str #endif skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk, len); - sock_put(sk); + if (!nlk->pid) { + struct netlink_work *nlwork = + kmalloc(sizeof(struct netlink_work), GFP_KERNEL); + + if (!nlwork) { + sock_put(sk); + return -EAGAIN; + } + + INIT_WORK(&nlwork->work, netlink_wq_handler, nlwork); + nlwork->sk = sk; + nlwork->len = len; + queue_work(netlink_wq, &nlwork->work); + } else { + sk->sk_data_ready(sk, len); + sock_put(sk); + } + return len; } @@ -573,7 +623,21 @@ static __inline__ int netlink_broadcast_ skb_orphan(skb); skb_set_owner_r(skb, sk); skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk, skb->len); + + if (!nlk->pid) { + struct netlink_work *nlwork = + kmalloc(sizeof(struct netlink_work), GFP_KERNEL); + + if (!nlwork) + return -1; + + INIT_WORK(&nlwork->work, netlink_wq_handler, nlwork); + nlwork->sk = sk; + nlwork->len = skb->len; + queue_work(netlink_wq, &nlwork->work); + } else + sk->sk_data_ready(sk, skb->len); + return 0; } return -1; @@ -619,13 +683,14 @@ int netlink_broadcast(struct sock *ssk, netlink_overrun(sk); /* Clone failed. Notify ALL listeners. */ failure = 1; + sock_put(sk); } else if (netlink_broadcast_deliver(sk, skb2)) { netlink_overrun(sk); + sock_put(sk); } else { delivered = 1; skb2 = NULL; } - sock_put(sk); } netlink_unlock_table(); @@ -1202,6 +1267,9 @@ static int __init netlink_proto_init(voi #endif /* The netlink device handler may be needed early. */ rtnetlink_init(); + + /* Create a work queue to handle callbacks to modules */ + netlink_wq = create_workqueue("netlink"); return 0; } @@ -1209,6 +1277,7 @@ static void __exit netlink_proto_exit(vo { sock_unregister(PF_NETLINK); proc_net_remove("netlink"); + destroy_workqueue(netlink_wq); } core_initcall(netlink_proto_init); --- linux-2.6.9-rc1/net/packet/af_packet.c 2004-08-24 00:03:14.000000000 -0700 +++ 25/net/packet/af_packet.c 2004-09-02 20:51:53.000000000 -0700 @@ -65,6 +65,8 @@ #include #include #include +#include +#include #include #include #include @@ -172,7 +174,7 @@ struct packet_opt { struct tpacket_stats stats; #ifdef CONFIG_PACKET_MMAP - unsigned long *pg_vec; + char * *pg_vec; unsigned int head; unsigned int frames_per_block; unsigned int frame_size; @@ -197,15 +199,15 @@ struct packet_opt #ifdef CONFIG_PACKET_MMAP -static inline unsigned long packet_lookup_frame(struct packet_opt *po, unsigned int position) +static inline char *packet_lookup_frame(struct packet_opt *po, unsigned int position) { unsigned int pg_vec_pos, frame_offset; - unsigned long frame; + char *frame; pg_vec_pos = position / po->frames_per_block; frame_offset = position % po->frames_per_block; - frame = (unsigned long) (po->pg_vec[pg_vec_pos] + (frame_offset * po->frame_size)); + frame = po->pg_vec[pg_vec_pos] + (frame_offset * po->frame_size); return frame; } @@ -1548,7 +1550,12 @@ static struct vm_operations_struct packe .close =packet_mm_close, }; -static void free_pg_vec(unsigned long *pg_vec, unsigned order, unsigned len) +static inline struct page *pg_vec_endpage(char *one_pg_vec, unsigned int order) +{ + return virt_to_page(one_pg_vec + (PAGE_SIZE << order) - 1); +} + +static void free_pg_vec(char **pg_vec, unsigned order, unsigned len) { int i; @@ -1556,10 +1563,10 @@ static void free_pg_vec(unsigned long *p if (pg_vec[i]) { struct page *page, *pend; - pend = virt_to_page(pg_vec[i] + (PAGE_SIZE << order) - 1); + pend = pg_vec_endpage(pg_vec[i], order); for (page = virt_to_page(pg_vec[i]); page <= pend; page++) ClearPageReserved(page); - free_pages(pg_vec[i], order); + free_pages((unsigned long)pg_vec[i], order); } } kfree(pg_vec); @@ -1568,7 +1575,7 @@ static void free_pg_vec(unsigned long *p static int packet_set_ring(struct sock *sk, struct tpacket_req *req, int closing) { - unsigned long *pg_vec = NULL; + char **pg_vec = NULL; struct packet_opt *po = pkt_sk(sk); int was_running, num, order = 0; int err = 0; @@ -1603,18 +1610,18 @@ static int packet_set_ring(struct sock * err = -ENOMEM; - pg_vec = kmalloc(req->tp_block_nr*sizeof(unsigned long*), GFP_KERNEL); + pg_vec = kmalloc(req->tp_block_nr*sizeof(char *), GFP_KERNEL); if (pg_vec == NULL) goto out; - memset(pg_vec, 0, req->tp_block_nr*sizeof(unsigned long*)); + memset(pg_vec, 0, req->tp_block_nr*sizeof(char **)); for (i=0; itp_block_nr; i++) { struct page *page, *pend; - pg_vec[i] = __get_free_pages(GFP_KERNEL, order); + pg_vec[i] = (char *)__get_free_pages(GFP_KERNEL, order); if (!pg_vec[i]) goto out_free_pgvec; - pend = virt_to_page(pg_vec[i] + (PAGE_SIZE << order) - 1); + pend = pg_vec_endpage(pg_vec[i], order); for (page = virt_to_page(pg_vec[i]); page <= pend; page++) SetPageReserved(page); } @@ -1622,7 +1629,7 @@ static int packet_set_ring(struct sock * l = 0; for (i=0; itp_block_nr; i++) { - unsigned long ptr = pg_vec[i]; + char *ptr = pg_vec[i]; struct tpacket_hdr *header; int k; --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/net/sched/gact.c 2004-09-02 20:51:53.000000000 -0700 @@ -0,0 +1,261 @@ +/* + * net/sched/gact.c Generic actions + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * copyright Jamal Hadi Salim (2002-4) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* use generic hash table */ +#define MY_TAB_SIZE 16 +#define MY_TAB_MASK 15 +static u32 idx_gen; +static struct tcf_gact *tcf_gact_ht[MY_TAB_SIZE]; +static rwlock_t gact_lock = RW_LOCK_UNLOCKED; + +/* ovewrride the defaults */ +#define tcf_st tcf_gact +#define tc_st tc_gact +#define tcf_t_lock gact_lock +#define tcf_ht tcf_gact_ht + +#define CONFIG_NET_ACT_INIT 1 +#include + +#ifdef CONFIG_GACT_PROB +typedef int (*g_rand)(struct tcf_gact *p); +int +gact_net_rand(struct tcf_gact *p) { + if (net_random()%p->pval) + return p->action; + return p->paction; +} + +int +gact_determ(struct tcf_gact *p) { + if (p->stats.packets%p->pval) + return p->action; + return p->paction; +} + + +g_rand gact_rand[MAX_RAND]= { NULL,gact_net_rand, gact_determ}; + +#endif +int +tcf_gact_init(struct rtattr *rta, struct rtattr *est, struct tc_action *a,int ovr,int bind) +{ + struct rtattr *tb[TCA_GACT_MAX]; + struct tc_gact *parm = NULL; + struct tc_gact_p *p_parm = NULL; + struct tcf_gact *p = NULL; + int ret = 0; + int size = sizeof (*p); + + if (rtattr_parse(tb, TCA_GACT_MAX, RTA_DATA(rta), RTA_PAYLOAD(rta)) < 0) + return -1; + + if (NULL == a || NULL == tb[TCA_GACT_PARMS - 1]) { + printk("BUG: tcf_gact_init called with NULL params\n"); + return -1; + } + + parm = RTA_DATA(tb[TCA_GACT_PARMS - 1]); +#ifdef CONFIG_GACT_PROB + if (NULL != tb[TCA_GACT_PROB - 1]) { + p_parm = RTA_DATA(tb[TCA_GACT_PROB - 1]); + } +#endif + + p = tcf_hash_check(parm, a, ovr, bind); + + if (NULL == p) { + p = tcf_hash_create(parm,est,a,size,ovr, bind); + + if (NULL == p) { + return -1; + } else { + p->refcnt = 1; + ret = 1; + goto override; + } + } + + if (ovr) { +override: + p->action = parm->action; +#ifdef CONFIG_GACT_PROB + if (NULL != p_parm) { + p->paction = p_parm->paction; + p->pval = p_parm->pval; + p->ptype = p_parm->ptype; + } else { + p->paction = p->pval = p->ptype = 0; + } +#endif + } + + return ret; +} + +int +tcf_gact_cleanup(struct tc_action *a, int bind) +{ + struct tcf_gact *p; + p = PRIV(a,gact); + if (NULL != p) + return tcf_hash_release(p, bind); + return 0; +} + +int +tcf_gact(struct sk_buff **pskb, struct tc_action *a) +{ + struct tcf_gact *p; + struct sk_buff *skb = *pskb; + int action = TC_ACT_SHOT; + + p = PRIV(a,gact); + + if (NULL == p) { + if (net_ratelimit()) + printk("BUG: tcf_gact called with NULL params\n"); + return -1; + } + + spin_lock(&p->lock); +#ifdef CONFIG_GACT_PROB + if (p->ptype && NULL != gact_rand[p->ptype]) + action = gact_rand[p->ptype](p); + else + action = p->action; +#else + action = p->action; +#endif + p->stats.bytes += skb->len; + p->stats.packets++; + if (TC_ACT_SHOT == action) + p->stats.drops++; + p->tm.lastuse = jiffies; + spin_unlock(&p->lock); + + return action; +} + +int +tcf_gact_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) +{ + unsigned char *b = skb->tail; + struct tc_gact opt; + struct tc_gact_p p_opt; + struct tcf_gact *p; + struct tcf_t t; + + p = PRIV(a,gact); + if (NULL == p) { + printk("BUG: tcf_gact_dump called with NULL params\n"); + goto rtattr_failure; + } + + opt.index = p->index; + opt.refcnt = p->refcnt - ref; + opt.bindcnt = p->bindcnt - bind; + opt.action = p->action; + RTA_PUT(skb, TCA_GACT_PARMS, sizeof (opt), &opt); +#ifdef CONFIG_GACT_PROB + if (p->ptype) { + p_opt.paction = p->paction; + p_opt.pval = p->pval; + p_opt.ptype = p->ptype; + RTA_PUT(skb, TCA_GACT_PROB, sizeof (p_opt), &p_opt); + } +#endif + t.install = jiffies - p->tm.install; + t.lastuse = jiffies - p->tm.lastuse; + t.expires = p->tm.expires; + RTA_PUT(skb, TCA_GACT_TM, sizeof (t), &t); + return skb->len; + + rtattr_failure: + skb_trim(skb, b - skb->data); + return -1; +} + +int +tcf_gact_stats(struct sk_buff *skb, struct tc_action *a) +{ + struct tcf_gact *p; + p = PRIV(a,gact); + if (NULL != p) + return qdisc_copy_stats(skb, &p->stats,p->stats_lock); + + return 1; +} + +struct tc_action_ops act_gact_ops = { + .next = NULL, + .kind = "gact", + .type = TCA_ACT_GACT, + .capab = TCA_CAP_NONE, + .owner = THIS_MODULE, + .act = tcf_gact, + .get_stats = tcf_gact_stats, + .dump = tcf_gact_dump, + .cleanup = tcf_gact_cleanup, + .lookup = tcf_hash_search, + .init = tcf_gact_init, + .walk = tcf_generic_walker +}; + +MODULE_AUTHOR("Jamal Hadi Salim(2002-4)"); +MODULE_DESCRIPTION("Generic Classifier actions"); +MODULE_LICENSE("GPL"); + +static int __init +gact_init_module(void) +{ +#ifdef CONFIG_GACT_PROB + printk("GACT probability on\n"); +#else + printk("GACT probability NOT on\n"); +#endif + return tcf_register_action(&act_gact_ops); +} + +static void __exit +gact_cleanup_module(void) +{ + tcf_unregister_action(&act_gact_ops); +} + +module_init(gact_init_module); +module_exit(gact_cleanup_module); --- linux-2.6.9-rc1/net/sched/Kconfig 2004-08-24 00:03:31.000000000 -0700 +++ 25/net/sched/Kconfig 2004-09-02 20:51:53.000000000 -0700 @@ -317,7 +317,7 @@ config NET_CLS_U32 module will be called cls_u32. config CLS_U32_PERF - bool " U32 classifier performance counters" + bool "U32 classifier performance counters" depends on NET_CLS_U32 help gathers stats that could be used to tune u32 classifier performance. @@ -364,7 +364,7 @@ config NET_CLS_RSVP6 module will be called cls_rsvp6. config NET_CLS_ACT - bool ' Packet ACTION ' + bool "Packet ACTION" depends on EXPERIMENTAL && NET_CLS && NET_QOS ---help--- This option requires you have a new iproute2. It enables @@ -373,7 +373,7 @@ config NET_CLS_ACT You MUST NOT turn this on if you dont have an update iproute2. config NET_ACT_POLICE - tristate ' Policing Actions' + tristate "Policing Actions" depends on NET_CLS_ACT ---help--- If you are using a newer iproute2 select this one, otherwise use one @@ -387,3 +387,15 @@ config NET_CLS_POLICE Say Y to support traffic policing (bandwidth limits). Needed for ingress and egress rate limiting. +config NET_ACT_GACT + tristate "generic Actions" + depends on NET_CLS_ACT + ---help--- + You must have new iproute2 to use this feature + This adds simple filtering actions like drop,accepet etc + +config GACT_PROB + bool "generic Actions probability" + depends on NET_ACT_GACT + ---help--- + Allows generic actions to be randomly or deterministically used --- linux-2.6.9-rc1/net/sched/Makefile 2004-08-24 00:02:47.000000000 -0700 +++ 25/net/sched/Makefile 2004-09-02 20:51:53.000000000 -0700 @@ -8,8 +8,9 @@ obj-$(CONFIG_NET_SCHED) += sch_api.o sc obj-$(CONFIG_NET_ESTIMATOR) += estimator.o obj-$(CONFIG_NET_CLS) += cls_api.o obj-$(CONFIG_NET_CLS_ACT) += act_api.o -obj-$(CONFIG_NET_ACT_POLICE) += police.o -obj-$(CONFIG_NET_CLS_POLICE) += police.o +obj-$(CONFIG_NET_ACT_POLICE) += police.o +obj-$(CONFIG_NET_CLS_POLICE) += police.o +obj-$(CONFIG_NET_ACT_GACT) += gact.o obj-$(CONFIG_NET_SCH_CBQ) += sch_cbq.o obj-$(CONFIG_NET_SCH_HTB) += sch_htb.o obj-$(CONFIG_NET_SCH_HPFQ) += sch_hpfq.o --- linux-2.6.9-rc1/net/sched/sch_netem.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/net/sched/sch_netem.c 2004-09-02 20:51:53.000000000 -0700 @@ -6,6 +6,9 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * + * Many of the algorithms and ideas for this came from + * NIST Net which is not copyrighted. + * * Authors: Stephen Hemminger * Catalin(ux aka Dino) BOIE */ @@ -22,11 +25,31 @@ #include -/* Network emulator - * - * This scheduler can alters spacing and order - * Similar to NISTnet and BSD Dummynet. - */ +/* Network Emulation Queuing algorithm. + ==================================== + + Sources: [1] Mark Carson, Darrin Santay, "NIST Net - A Linux-based + Network Emulation Tool + [2] Luigi Rizzo, DummyNet for FreeBSD + + ---------------------------------------------------------------- + + This started out as a simple way to delay outgoing packets to + test TCP but has grown to include most of the functionality + of a full blown network emulator like NISTnet. It can delay + packets and add random jitter (and correlation). The random + distribution can be loaded from a table as well to provide + normal, Pareto, or experimental curves. Packet loss, + duplication, and reordering can also be emulated. + + This qdisc does not do classification that can be handled in + layering other disciplines. It does not need to do bandwidth + control either since that can be handled by using token + bucket or other rate control. + + The simulator is limited by the Linux timer resolution + and will create packet bursts on the HZ boundary (1ms). +*/ struct netem_sched_data { struct Qdisc *qdisc; @@ -39,6 +62,17 @@ struct netem_sched_data { u32 counter; u32 gap; u32 jitter; + u32 duplicate; + + struct crndstate { + unsigned long last; + unsigned long rho; + } delay_cor, loss_cor, dup_cor; + + struct disttable { + u32 size; + s16 table[0]; + } *delay_dist; }; /* Time stamp put into socket buffer control block */ @@ -46,576 +80,110 @@ struct netem_skb_cb { psched_time_t time_to_send; }; -/* This is the distribution table for the normal distribution produced - * with NISTnet tools. - * The entries represent a scaled inverse of the cumulative distribution - * function. +/* init_crandom - initialize correlated random number generator + * Use entropy source for initial seed. + */ +static void init_crandom(struct crndstate *state, unsigned long rho) +{ + state->rho = rho; + state->last = net_random(); +} + +/* get_crandom - correlated random number generator + * Next number depends on last value. + * rho is scaled to avoid floating point. */ -#define TABLESIZE 2048 -#define TABLEFACTOR 8192 +static unsigned long get_crandom(struct crndstate *state) +{ + u64 value, rho; + unsigned long answer; -static const short disttable[TABLESIZE] = { - -31473, -26739, -25226, -24269, - -23560, -22993, -22518, -22109, - -21749, -21426, -21133, -20865, - -20618, -20389, -20174, -19972, - -19782, -19601, -19430, -19267, - -19112, -18962, -18819, -18681, - -18549, -18421, -18298, -18178, - -18062, -17950, -17841, -17735, - -17632, -17532, -17434, -17339, - -17245, -17155, -17066, -16979, - -16894, -16811, -16729, -16649, - -16571, -16494, -16419, -16345, - -16272, -16201, -16130, -16061, - -15993, -15926, -15861, -15796, - -15732, -15669, -15607, -15546, - -15486, -15426, -15368, -15310, - -15253, -15196, -15140, -15086, - -15031, -14977, -14925, -14872, - -14821, -14769, -14719, -14669, - -14619, -14570, -14522, -14473, - -14426, -14379, -14332, -14286, - -14241, -14196, -14150, -14106, - -14062, -14019, -13976, -13933, - -13890, -13848, -13807, -13765, - -13724, -13684, -13643, -13604, - -13564, -13525, -13486, -13447, - -13408, -13370, -13332, -13295, - -13258, -13221, -13184, -13147, - -13111, -13075, -13040, -13004, - -12969, -12934, -12899, -12865, - -12830, -12796, -12762, -12729, - -12695, -12662, -12629, -12596, - -12564, -12531, -12499, -12467, - -12435, -12404, -12372, -12341, - -12310, -12279, -12248, -12218, - -12187, -12157, -12127, -12097, - -12067, -12038, -12008, -11979, - -11950, -11921, -11892, -11863, - -11835, -11806, -11778, -11750, - -11722, -11694, -11666, -11639, - -11611, -11584, -11557, -11530, - -11503, -11476, -11450, -11423, - -11396, -11370, -11344, -11318, - -11292, -11266, -11240, -11214, - -11189, -11164, -11138, -11113, - -11088, -11063, -11038, -11013, - -10988, -10964, -10939, -10915, - -10891, -10866, -10843, -10818, - -10794, -10770, -10747, -10723, - -10700, -10676, -10652, -10630, - -10606, -10583, -10560, -10537, - -10514, -10491, -10469, -10446, - -10424, -10401, -10378, -10356, - -10334, -10312, -10290, -10267, - -10246, -10224, -10202, -10180, - -10158, -10137, -10115, -10094, - -10072, -10051, -10030, -10009, - -9988, -9967, -9945, -9925, - -9904, -9883, -9862, -9842, - -9821, -9800, -9780, -9760, - -9739, -9719, -9699, -9678, - -9658, -9638, -9618, -9599, - -9578, -9559, -9539, -9519, - -9499, -9480, -9461, -9441, - -9422, -9402, -9383, -9363, - -9344, -9325, -9306, -9287, - -9268, -9249, -9230, -9211, - -9192, -9173, -9155, -9136, - -9117, -9098, -9080, -9062, - -9043, -9025, -9006, -8988, - -8970, -8951, -8933, -8915, - -8897, -8879, -8861, -8843, - -8825, -8807, -8789, -8772, - -8754, -8736, -8718, -8701, - -8683, -8665, -8648, -8630, - -8613, -8595, -8578, -8561, - -8543, -8526, -8509, -8492, - -8475, -8458, -8441, -8423, - -8407, -8390, -8373, -8356, - -8339, -8322, -8305, -8289, - -8272, -8255, -8239, -8222, - -8206, -8189, -8172, -8156, - -8140, -8123, -8107, -8090, - -8074, -8058, -8042, -8025, - -8009, -7993, -7977, -7961, - -7945, -7929, -7913, -7897, - -7881, -7865, -7849, -7833, - -7817, -7802, -7786, -7770, - -7754, -7739, -7723, -7707, - -7692, -7676, -7661, -7645, - -7630, -7614, -7599, -7583, - -7568, -7553, -7537, -7522, - -7507, -7492, -7476, -7461, - -7446, -7431, -7416, -7401, - -7385, -7370, -7356, -7340, - -7325, -7311, -7296, -7281, - -7266, -7251, -7236, -7221, - -7207, -7192, -7177, -7162, - -7148, -7133, -7118, -7104, - -7089, -7075, -7060, -7046, - -7031, -7016, -7002, -6988, - -6973, -6959, -6944, -6930, - -6916, -6901, -6887, -6873, - -6859, -6844, -6830, -6816, - -6802, -6788, -6774, -6760, - -6746, -6731, -6717, -6704, - -6690, -6675, -6661, -6647, - -6633, -6620, -6606, -6592, - -6578, -6564, -6550, -6537, - -6523, -6509, -6495, -6482, - -6468, -6454, -6441, -6427, - -6413, -6400, -6386, -6373, - -6359, -6346, -6332, -6318, - -6305, -6291, -6278, -6264, - -6251, -6238, -6224, -6211, - -6198, -6184, -6171, -6158, - -6144, -6131, -6118, -6105, - -6091, -6078, -6065, -6052, - -6039, -6025, -6012, -5999, - -5986, -5973, -5960, -5947, - -5934, -5921, -5908, -5895, - -5882, -5869, -5856, -5843, - -5830, -5817, -5804, -5791, - -5779, -5766, -5753, -5740, - -5727, -5714, -5702, -5689, - -5676, -5663, -5650, -5638, - -5625, -5612, -5600, -5587, - -5575, -5562, -5549, -5537, - -5524, -5512, -5499, -5486, - -5474, -5461, -5449, -5436, - -5424, -5411, -5399, -5386, - -5374, -5362, -5349, -5337, - -5324, -5312, -5299, -5287, - -5275, -5263, -5250, -5238, - -5226, -5213, -5201, -5189, - -5177, -5164, -5152, -5140, - -5128, -5115, -5103, -5091, - -5079, -5067, -5055, -5043, - -5030, -5018, -5006, -4994, - -4982, -4970, -4958, -4946, - -4934, -4922, -4910, -4898, - -4886, -4874, -4862, -4850, - -4838, -4826, -4814, -4803, - -4791, -4778, -4767, -4755, - -4743, -4731, -4719, -4708, - -4696, -4684, -4672, -4660, - -4649, -4637, -4625, -4613, - -4601, -4590, -4578, -4566, - -4554, -4543, -4531, -4520, - -4508, -4496, -4484, -4473, - -4461, -4449, -4438, -4427, - -4415, -4403, -4392, -4380, - -4368, -4357, -4345, -4334, - -4322, -4311, -4299, -4288, - -4276, -4265, -4253, -4242, - -4230, -4219, -4207, -4196, - -4184, -4173, -4162, -4150, - -4139, -4128, -4116, -4105, - -4094, -4082, -4071, -4060, - -4048, -4037, -4026, -4014, - -4003, -3992, -3980, -3969, - -3958, -3946, -3935, -3924, - -3913, -3901, -3890, -3879, - -3868, -3857, -3845, -3834, - -3823, -3812, -3801, -3790, - -3779, -3767, -3756, -3745, - -3734, -3723, -3712, -3700, - -3689, -3678, -3667, -3656, - -3645, -3634, -3623, -3612, - -3601, -3590, -3579, -3568, - -3557, -3545, -3535, -3524, - -3513, -3502, -3491, -3480, - -3469, -3458, -3447, -3436, - -3425, -3414, -3403, -3392, - -3381, -3370, -3360, -3348, - -3337, -3327, -3316, -3305, - -3294, -3283, -3272, -3262, - -3251, -3240, -3229, -3218, - -3207, -3197, -3185, -3175, - -3164, -3153, -3142, -3132, - -3121, -3110, -3099, -3088, - -3078, -3067, -3056, -3045, - -3035, -3024, -3013, -3003, - -2992, -2981, -2970, -2960, - -2949, -2938, -2928, -2917, - -2906, -2895, -2885, -2874, - -2864, -2853, -2842, -2832, - -2821, -2810, -2800, -2789, - -2778, -2768, -2757, -2747, - -2736, -2725, -2715, -2704, - -2694, -2683, -2673, -2662, - -2651, -2641, -2630, -2620, - -2609, -2599, -2588, -2578, - -2567, -2556, -2546, -2535, - -2525, -2515, -2504, -2493, - -2483, -2472, -2462, -2451, - -2441, -2431, -2420, -2410, - -2399, -2389, -2378, -2367, - -2357, -2347, -2336, -2326, - -2315, -2305, -2295, -2284, - -2274, -2263, -2253, -2243, - -2232, -2222, -2211, -2201, - -2191, -2180, -2170, -2159, - -2149, -2139, -2128, -2118, - -2107, -2097, -2087, -2076, - -2066, -2056, -2046, -2035, - -2025, -2014, -2004, -1994, - -1983, -1973, -1963, -1953, - -1942, -1932, -1921, -1911, - -1901, -1891, -1880, -1870, - -1860, -1849, -1839, -1829, - -1819, -1808, -1798, -1788, - -1778, -1767, -1757, -1747, - -1736, -1726, -1716, -1706, - -1695, -1685, -1675, -1665, - -1654, -1644, -1634, -1624, - -1613, -1603, -1593, -1583, - -1573, -1563, -1552, -1542, - -1532, -1522, -1511, -1501, - -1491, -1481, -1471, -1461, - -1450, -1440, -1430, -1420, - -1409, -1400, -1389, -1379, - -1369, -1359, -1348, -1339, - -1328, -1318, -1308, -1298, - -1288, -1278, -1267, -1257, - -1247, -1237, -1227, -1217, - -1207, -1196, -1186, -1176, - -1166, -1156, -1146, -1135, - -1126, -1115, -1105, -1095, - -1085, -1075, -1065, -1055, - -1044, -1034, -1024, -1014, - -1004, -994, -984, -974, - -964, -954, -944, -933, - -923, -913, -903, -893, - -883, -873, -863, -853, - -843, -833, -822, -812, - -802, -792, -782, -772, - -762, -752, -742, -732, - -722, -712, -702, -691, - -682, -671, -662, -651, - -641, -631, -621, -611, - -601, -591, -581, -571, - -561, -551, -541, -531, - -521, -511, -501, -491, - -480, -471, -460, -451, - -440, -430, -420, -410, - -400, -390, -380, -370, - -360, -350, -340, -330, - -320, -310, -300, -290, - -280, -270, -260, -250, - -240, -230, -220, -210, - -199, -190, -179, -170, - -159, -150, -139, -129, - -119, -109, -99, -89, - -79, -69, -59, -49, - -39, -29, -19, -9, - 1, 11, 21, 31, - 41, 51, 61, 71, - 81, 91, 101, 111, - 121, 131, 141, 152, - 161, 172, 181, 192, - 202, 212, 222, 232, - 242, 252, 262, 272, - 282, 292, 302, 312, - 322, 332, 342, 352, - 362, 372, 382, 392, - 402, 412, 422, 433, - 442, 453, 462, 473, - 483, 493, 503, 513, - 523, 533, 543, 553, - 563, 573, 583, 593, - 603, 613, 623, 633, - 643, 653, 664, 673, - 684, 694, 704, 714, - 724, 734, 744, 754, - 764, 774, 784, 794, - 804, 815, 825, 835, - 845, 855, 865, 875, - 885, 895, 905, 915, - 925, 936, 946, 956, - 966, 976, 986, 996, - 1006, 1016, 1026, 1037, - 1047, 1057, 1067, 1077, - 1087, 1097, 1107, 1117, - 1128, 1138, 1148, 1158, - 1168, 1178, 1188, 1198, - 1209, 1219, 1229, 1239, - 1249, 1259, 1269, 1280, - 1290, 1300, 1310, 1320, - 1330, 1341, 1351, 1361, - 1371, 1381, 1391, 1402, - 1412, 1422, 1432, 1442, - 1452, 1463, 1473, 1483, - 1493, 1503, 1513, 1524, - 1534, 1544, 1554, 1565, - 1575, 1585, 1595, 1606, - 1616, 1626, 1636, 1647, - 1656, 1667, 1677, 1687, - 1697, 1708, 1718, 1729, - 1739, 1749, 1759, 1769, - 1780, 1790, 1800, 1810, - 1821, 1831, 1841, 1851, - 1862, 1872, 1883, 1893, - 1903, 1913, 1923, 1934, - 1944, 1955, 1965, 1975, - 1985, 1996, 2006, 2016, - 2027, 2037, 2048, 2058, - 2068, 2079, 2089, 2099, - 2110, 2120, 2130, 2141, - 2151, 2161, 2172, 2182, - 2193, 2203, 2213, 2224, - 2234, 2245, 2255, 2265, - 2276, 2286, 2297, 2307, - 2318, 2328, 2338, 2349, - 2359, 2370, 2380, 2391, - 2401, 2412, 2422, 2433, - 2443, 2454, 2464, 2475, - 2485, 2496, 2506, 2517, - 2527, 2537, 2548, 2559, - 2569, 2580, 2590, 2601, - 2612, 2622, 2632, 2643, - 2654, 2664, 2675, 2685, - 2696, 2707, 2717, 2728, - 2738, 2749, 2759, 2770, - 2781, 2791, 2802, 2813, - 2823, 2834, 2845, 2855, - 2866, 2877, 2887, 2898, - 2909, 2919, 2930, 2941, - 2951, 2962, 2973, 2984, - 2994, 3005, 3015, 3027, - 3037, 3048, 3058, 3069, - 3080, 3091, 3101, 3113, - 3123, 3134, 3145, 3156, - 3166, 3177, 3188, 3199, - 3210, 3220, 3231, 3242, - 3253, 3264, 3275, 3285, - 3296, 3307, 3318, 3329, - 3340, 3351, 3362, 3373, - 3384, 3394, 3405, 3416, - 3427, 3438, 3449, 3460, - 3471, 3482, 3493, 3504, - 3515, 3526, 3537, 3548, - 3559, 3570, 3581, 3592, - 3603, 3614, 3625, 3636, - 3647, 3659, 3670, 3681, - 3692, 3703, 3714, 3725, - 3736, 3747, 3758, 3770, - 3781, 3792, 3803, 3814, - 3825, 3837, 3848, 3859, - 3870, 3881, 3893, 3904, - 3915, 3926, 3937, 3949, - 3960, 3971, 3983, 3994, - 4005, 4017, 4028, 4039, - 4051, 4062, 4073, 4085, - 4096, 4107, 4119, 4130, - 4141, 4153, 4164, 4175, - 4187, 4198, 4210, 4221, - 4233, 4244, 4256, 4267, - 4279, 4290, 4302, 4313, - 4325, 4336, 4348, 4359, - 4371, 4382, 4394, 4406, - 4417, 4429, 4440, 4452, - 4464, 4475, 4487, 4499, - 4510, 4522, 4533, 4545, - 4557, 4569, 4581, 4592, - 4604, 4616, 4627, 4639, - 4651, 4663, 4674, 4686, - 4698, 4710, 4722, 4734, - 4746, 4758, 4769, 4781, - 4793, 4805, 4817, 4829, - 4841, 4853, 4865, 4877, - 4889, 4900, 4913, 4925, - 4936, 4949, 4961, 4973, - 4985, 4997, 5009, 5021, - 5033, 5045, 5057, 5070, - 5081, 5094, 5106, 5118, - 5130, 5143, 5155, 5167, - 5179, 5191, 5204, 5216, - 5228, 5240, 5253, 5265, - 5278, 5290, 5302, 5315, - 5327, 5340, 5352, 5364, - 5377, 5389, 5401, 5414, - 5426, 5439, 5451, 5464, - 5476, 5489, 5502, 5514, - 5527, 5539, 5552, 5564, - 5577, 5590, 5603, 5615, - 5628, 5641, 5653, 5666, - 5679, 5691, 5704, 5717, - 5730, 5743, 5756, 5768, - 5781, 5794, 5807, 5820, - 5833, 5846, 5859, 5872, - 5885, 5897, 5911, 5924, - 5937, 5950, 5963, 5976, - 5989, 6002, 6015, 6028, - 6042, 6055, 6068, 6081, - 6094, 6108, 6121, 6134, - 6147, 6160, 6174, 6187, - 6201, 6214, 6227, 6241, - 6254, 6267, 6281, 6294, - 6308, 6321, 6335, 6348, - 6362, 6375, 6389, 6403, - 6416, 6430, 6443, 6457, - 6471, 6485, 6498, 6512, - 6526, 6540, 6554, 6567, - 6581, 6595, 6609, 6623, - 6637, 6651, 6665, 6679, - 6692, 6706, 6721, 6735, - 6749, 6763, 6777, 6791, - 6805, 6819, 6833, 6848, - 6862, 6876, 6890, 6905, - 6919, 6933, 6948, 6962, - 6976, 6991, 7005, 7020, - 7034, 7049, 7064, 7078, - 7093, 7107, 7122, 7136, - 7151, 7166, 7180, 7195, - 7210, 7225, 7240, 7254, - 7269, 7284, 7299, 7314, - 7329, 7344, 7359, 7374, - 7389, 7404, 7419, 7434, - 7449, 7465, 7480, 7495, - 7510, 7526, 7541, 7556, - 7571, 7587, 7602, 7618, - 7633, 7648, 7664, 7680, - 7695, 7711, 7726, 7742, - 7758, 7773, 7789, 7805, - 7821, 7836, 7852, 7868, - 7884, 7900, 7916, 7932, - 7948, 7964, 7981, 7997, - 8013, 8029, 8045, 8061, - 8078, 8094, 8110, 8127, - 8143, 8160, 8176, 8193, - 8209, 8226, 8242, 8259, - 8276, 8292, 8309, 8326, - 8343, 8360, 8377, 8394, - 8410, 8428, 8444, 8462, - 8479, 8496, 8513, 8530, - 8548, 8565, 8582, 8600, - 8617, 8634, 8652, 8670, - 8687, 8704, 8722, 8740, - 8758, 8775, 8793, 8811, - 8829, 8847, 8865, 8883, - 8901, 8919, 8937, 8955, - 8974, 8992, 9010, 9029, - 9047, 9066, 9084, 9103, - 9121, 9140, 9159, 9177, - 9196, 9215, 9234, 9253, - 9272, 9291, 9310, 9329, - 9349, 9368, 9387, 9406, - 9426, 9445, 9465, 9484, - 9504, 9524, 9544, 9563, - 9583, 9603, 9623, 9643, - 9663, 9683, 9703, 9723, - 9744, 9764, 9785, 9805, - 9826, 9846, 9867, 9888, - 9909, 9930, 9950, 9971, - 9993, 10013, 10035, 10056, - 10077, 10099, 10120, 10142, - 10163, 10185, 10207, 10229, - 10251, 10273, 10294, 10317, - 10339, 10361, 10384, 10406, - 10428, 10451, 10474, 10496, - 10519, 10542, 10565, 10588, - 10612, 10635, 10658, 10682, - 10705, 10729, 10752, 10776, - 10800, 10824, 10848, 10872, - 10896, 10921, 10945, 10969, - 10994, 11019, 11044, 11069, - 11094, 11119, 11144, 11169, - 11195, 11221, 11246, 11272, - 11298, 11324, 11350, 11376, - 11402, 11429, 11456, 11482, - 11509, 11536, 11563, 11590, - 11618, 11645, 11673, 11701, - 11728, 11756, 11785, 11813, - 11842, 11870, 11899, 11928, - 11957, 11986, 12015, 12045, - 12074, 12104, 12134, 12164, - 12194, 12225, 12255, 12286, - 12317, 12348, 12380, 12411, - 12443, 12475, 12507, 12539, - 12571, 12604, 12637, 12670, - 12703, 12737, 12771, 12804, - 12839, 12873, 12907, 12942, - 12977, 13013, 13048, 13084, - 13120, 13156, 13192, 13229, - 13267, 13304, 13341, 13379, - 13418, 13456, 13495, 13534, - 13573, 13613, 13653, 13693, - 13734, 13775, 13817, 13858, - 13901, 13943, 13986, 14029, - 14073, 14117, 14162, 14206, - 14252, 14297, 14343, 14390, - 14437, 14485, 14533, 14582, - 14631, 14680, 14731, 14782, - 14833, 14885, 14937, 14991, - 15044, 15099, 15154, 15210, - 15266, 15324, 15382, 15441, - 15500, 15561, 15622, 15684, - 15747, 15811, 15877, 15943, - 16010, 16078, 16148, 16218, - 16290, 16363, 16437, 16513, - 16590, 16669, 16749, 16831, - 16915, 17000, 17088, 17177, - 17268, 17362, 17458, 17556, - 17657, 17761, 17868, 17977, - 18090, 18207, 18328, 18452, - 18581, 18715, 18854, 18998, - 19149, 19307, 19472, 19645, - 19828, 20021, 20226, 20444, - 20678, 20930, 21204, 21503, - 21835, 22206, 22630, 23124, - 23721, 24478, 25529, 27316, -}; + if (state->rho == 0) /* no correllation */ + return net_random(); + + value = net_random(); + rho = (u64)state->rho + 1; + answer = (value * ((1ull<<32) - rho) + state->last * rho) >> 32; + state->last = answer; + return answer; +} /* tabledist - return a pseudo-randomly distributed value with mean mu and * std deviation sigma. Uses table lookup to approximate the desired * distribution, and a uniformly-distributed pseudo-random source. */ -static inline int tabledist(int mu, int sigma) +static long tabledist(unsigned long mu, long sigma, + struct crndstate *state, const struct disttable *dist) { - int x; - int index; - int sigmamod, sigmadiv; + long t, x; + unsigned long rnd; if (sigma == 0) return mu; - index = (net_random() & (TABLESIZE-1)); - sigmamod = sigma%TABLEFACTOR; - sigmadiv = sigma/TABLEFACTOR; - x = sigmamod*disttable[index]; + rnd = get_crandom(state); + + /* default uniform distribution */ + if (dist == NULL) + return (rnd % (2*sigma)) - sigma + mu; + t = dist->table[rnd % dist->size]; + x = (sigma % NETEM_DIST_SCALE) * t; if (x >= 0) - x += TABLEFACTOR/2; + x += NETEM_DIST_SCALE/2; else - x -= TABLEFACTOR/2; + x -= NETEM_DIST_SCALE/2; - x /= TABLEFACTOR; - x += sigmadiv*disttable[index]; - x += mu; - return x; + return x / NETEM_DIST_SCALE + (sigma / NETEM_DIST_SCALE) * t + mu; } -/* Enqueue packets with underlying discipline (fifo) - * but mark them with current time first. - */ -static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch) +/* Put skb in the private delayed queue. */ +static int delay_skb(struct Qdisc *sch, struct sk_buff *skb) { struct netem_sched_data *q = qdisc_priv(sch); struct netem_skb_cb *cb = (struct netem_skb_cb *)skb->cb; psched_time_t now; - long delay; + + PSCHED_GET_TIME(now); + PSCHED_TADD2(now, tabledist(q->latency, q->jitter, + &q->delay_cor, q->delay_dist), + cb->time_to_send); + + /* Always queue at tail to keep packets in order */ + if (likely(q->delayed.qlen < q->limit)) { + __skb_queue_tail(&q->delayed, skb); + sch->q.qlen++; + sch->stats.bytes += skb->len; + sch->stats.packets++; + return NET_XMIT_SUCCESS; + } + + sch->stats.drops++; + kfree_skb(skb); + return NET_XMIT_DROP; +} + +static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch) +{ + struct netem_sched_data *q = qdisc_priv(sch); pr_debug("netem_enqueue skb=%p @%lu\n", skb, jiffies); /* Random packet drop 0 => none, ~0 => all */ - if (q->loss && q->loss >= net_random()) { + if (q->loss && q->loss >= get_crandom(&q->loss_cor)) { + pr_debug("netem_enqueue: random loss\n"); sch->stats.drops++; return 0; /* lie about loss so TCP doesn't know */ } + /* Random duplication */ + if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor)) { + struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); + + pr_debug("netem_enqueue: dup %p\n", skb2); + if (skb2) + delay_skb(sch, skb2); + } /* If doing simple delay then gap == 0 so all packets * go into the delayed holding queue @@ -633,27 +201,8 @@ static int netem_enqueue(struct sk_buff } q->counter = 0; - - PSCHED_GET_TIME(now); - if (q->jitter) - delay = tabledist(q->latency, q->jitter); - else - delay = q->latency; - PSCHED_TADD2(now, delay, cb->time_to_send); - - /* Always queue at tail to keep packets in order */ - if (likely(q->delayed.qlen < q->limit)) { - __skb_queue_tail(&q->delayed, skb); - sch->q.qlen++; - sch->stats.bytes += skb->len; - sch->stats.packets++; - return 0; - } - - sch->stats.drops++; - kfree_skb(skb); - return NET_XMIT_DROP; + return delay_skb(sch, skb); } /* Requeue packets but don't change time stamp */ @@ -752,39 +301,97 @@ static int set_fifo_limit(struct Qdisc * return ret; } -static int netem_change(struct Qdisc *sch, struct rtattr *opt) +/* + * Distribution data is a variable size payload containing + * signed 16 bit values. + */ +static int get_dist_table(struct Qdisc *sch, const struct rtattr *attr) { struct netem_sched_data *q = qdisc_priv(sch); - struct tc_netem_qopt *qopt = RTA_DATA(opt); - struct Qdisc *child; - int ret; + unsigned long n = RTA_PAYLOAD(attr)/sizeof(__s16); + const __s16 *data = RTA_DATA(attr); + struct disttable *d; + int i; + + if (n > 65536) + return -EINVAL; - if (opt->rta_len < RTA_LENGTH(sizeof(*qopt))) + d = kmalloc(sizeof(*d) + n*sizeof(d->table[0]), GFP_KERNEL); + if (!d) + return -ENOMEM; + + d->size = n; + for (i = 0; i < n; i++) + d->table[i] = data[i]; + + spin_lock_bh(&sch->dev->queue_lock); + d = xchg(&q->delay_dist, d); + spin_unlock_bh(&sch->dev->queue_lock); + + kfree(d); + return 0; +} + +static int get_correlation(struct Qdisc *sch, const struct rtattr *attr) +{ + struct netem_sched_data *q = qdisc_priv(sch); + const struct tc_netem_corr *c = RTA_DATA(attr); + + if (RTA_PAYLOAD(attr) != sizeof(*c)) return -EINVAL; - child = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops); - if (!child) + init_crandom(&q->delay_cor, c->delay_corr); + init_crandom(&q->loss_cor, c->loss_corr); + init_crandom(&q->dup_cor, c->dup_corr); + return 0; +} + +static int netem_change(struct Qdisc *sch, struct rtattr *opt) +{ + struct netem_sched_data *q = qdisc_priv(sch); + struct tc_netem_qopt *qopt; + int ret; + + if (opt == NULL || RTA_PAYLOAD(opt) < sizeof(*qopt)) return -EINVAL; - ret = set_fifo_limit(child, qopt->limit); + qopt = RTA_DATA(opt); + ret = set_fifo_limit(q->qdisc, qopt->limit); if (ret) { - qdisc_destroy(child); + pr_debug("netem: can't set fifo limit\n"); return ret; } - - sch_tree_lock(sch); - if (child) { - child = xchg(&q->qdisc, child); - if (child != &noop_qdisc) - qdisc_destroy(child); - q->latency = qopt->latency; - q->jitter = qopt->jitter; - q->limit = qopt->limit; - q->gap = qopt->gap; - q->loss = qopt->loss; + q->latency = qopt->latency; + q->jitter = qopt->jitter; + q->limit = qopt->limit; + q->gap = qopt->gap; + q->loss = qopt->loss; + q->duplicate = qopt->duplicate; + + /* Handle nested options after initial queue options. + * Should have put all options in nested format but too late now. + */ + if (RTA_PAYLOAD(opt) > sizeof(*qopt)) { + struct rtattr *tb[TCA_NETEM_MAX]; + if (rtattr_parse(tb, TCA_NETEM_MAX, + RTA_DATA(opt) + sizeof(*qopt), + RTA_PAYLOAD(opt) - sizeof(*qopt))) + return -EINVAL; + + if (tb[TCA_NETEM_CORR-1]) { + ret = get_correlation(sch, tb[TCA_NETEM_CORR-1]); + if (ret) + return ret; + } + + if (tb[TCA_NETEM_DELAY_DIST-1]) { + ret = get_dist_table(sch, tb[TCA_NETEM_DELAY_DIST-1]); + if (ret) + return ret; + } } - sch_tree_unlock(sch); + return 0; } @@ -792,19 +399,29 @@ static int netem_change(struct Qdisc *sc static int netem_init(struct Qdisc *sch, struct rtattr *opt) { struct netem_sched_data *q = qdisc_priv(sch); + int ret; if (!opt) return -EINVAL; skb_queue_head_init(&q->delayed); - q->qdisc = &noop_qdisc; - init_timer(&q->timer); q->timer.function = netem_watchdog; q->timer.data = (unsigned long) sch; q->counter = 0; - return netem_change(sch, opt); + q->qdisc = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops); + if (!q->qdisc) { + pr_debug("netem: qdisc create failed\n"); + return -ENOMEM; + } + + ret = netem_change(sch, opt); + if (ret) { + pr_debug("netem: change failed\n"); + qdisc_destroy(q->qdisc); + } + return ret; } static void netem_destroy(struct Qdisc *sch) @@ -813,22 +430,31 @@ static void netem_destroy(struct Qdisc * del_timer_sync(&q->timer); qdisc_destroy(q->qdisc); + kfree(q->delay_dist); } static int netem_dump(struct Qdisc *sch, struct sk_buff *skb) { - struct netem_sched_data *q = qdisc_priv(sch); + const struct netem_sched_data *q = qdisc_priv(sch); unsigned char *b = skb->tail; + struct rtattr *rta = (struct rtattr *) b; struct tc_netem_qopt qopt; + struct tc_netem_corr cor; qopt.latency = q->latency; qopt.jitter = q->jitter; qopt.limit = q->limit; qopt.loss = q->loss; qopt.gap = q->gap; - + qopt.duplicate = q->duplicate; RTA_PUT(skb, TCA_OPTIONS, sizeof(qopt), &qopt); + cor.delay_corr = q->delay_cor.rho; + cor.loss_corr = q->loss_cor.rho; + cor.dup_corr = q->dup_cor.rho; + RTA_PUT(skb, TCA_NETEM_CORR, sizeof(cor), &cor); + rta->rta_len = skb->tail - b; + return skb->len; rtattr_failure: --- linux-2.6.9-rc1/net/sched/sch_teql.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/net/sched/sch_teql.c 2004-09-02 20:51:53.000000000 -0700 @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -449,7 +450,7 @@ static __init void teql_master_setup(str static LIST_HEAD(master_dev_list); static int max_equalizers = 1; -MODULE_PARM(max_equalizers, "i"); +module_param(max_equalizers, int, 0); MODULE_PARM_DESC(max_equalizers, "Max number of link equalizers"); static int __init teql_init(void) --- linux-2.6.9-rc1/net/sunrpc/auth_gss/gss_spkm3_seal.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/net/sunrpc/auth_gss/gss_spkm3_seal.c 2004-09-02 20:51:53.000000000 -0700 @@ -126,7 +126,7 @@ spkm3_make_token(struct spkm3_ctx *ctx, out_err: if (md5cksum.data) kfree(md5cksum.data); - token->data = 0; + token->data = NULL; token->len = 0; return GSS_S_FAILURE; } --- linux-2.6.9-rc1/net/sunrpc/sched.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/net/sunrpc/sched.c 2004-09-03 00:56:48.781744696 -0700 @@ -41,13 +41,7 @@ static mempool_t *rpc_buffer_mempool; static void __rpc_default_timer(struct rpc_task *task); static void rpciod_killall(void); - -/* - * When an asynchronous RPC task is activated within a bottom half - * handler, or while executing another RPC task, it is put on - * schedq, and rpciod is woken up. - */ -static RPC_WAITQ(schedq, "schedq"); +static void rpc_async_schedule(void *); /* * RPC tasks that create another task (e.g. for contacting the portmapper) @@ -68,26 +62,18 @@ static LIST_HEAD(all_tasks); /* * rpciod-related stuff */ -static DECLARE_WAIT_QUEUE_HEAD(rpciod_idle); -static DECLARE_COMPLETION(rpciod_killer); static DECLARE_MUTEX(rpciod_sema); static unsigned int rpciod_users; -static pid_t rpciod_pid; -static int rpc_inhibit; +static struct workqueue_struct *rpciod_workqueue; /* - * Spinlock for wait queues. Access to the latter also has to be - * interrupt-safe in order to allow timers to wake up sleeping tasks. - */ -static spinlock_t rpc_queue_lock = SPIN_LOCK_UNLOCKED; -/* * Spinlock for other critical sections of code. */ static spinlock_t rpc_sched_lock = SPIN_LOCK_UNLOCKED; /* * Disable the timer for a given RPC task. Should be called with - * rpc_queue_lock and bh_disabled in order to avoid races within + * queue->lock and bh_disabled in order to avoid races within * rpc_run_timer(). */ static inline void @@ -105,16 +91,13 @@ __rpc_disable_timer(struct rpc_task *tas * without calling del_timer_sync(). The latter could cause a * deadlock if called while we're holding spinlocks... */ -static void -rpc_run_timer(struct rpc_task *task) +static void rpc_run_timer(struct rpc_task *task) { void (*callback)(struct rpc_task *); - spin_lock_bh(&rpc_queue_lock); callback = task->tk_timeout_fn; task->tk_timeout_fn = NULL; - spin_unlock_bh(&rpc_queue_lock); - if (callback) { + if (callback && RPC_IS_QUEUED(task)) { dprintk("RPC: %4d running timer\n", task->tk_pid); callback(task); } @@ -140,19 +123,8 @@ __rpc_add_timer(struct rpc_task *task, r } /* - * Set up a timer for an already sleeping task. - */ -void rpc_add_timer(struct rpc_task *task, rpc_action timer) -{ - spin_lock_bh(&rpc_queue_lock); - if (!RPC_IS_RUNNING(task)) - __rpc_add_timer(task, timer); - spin_unlock_bh(&rpc_queue_lock); -} - -/* * Delete any timer for the current task. Because we use del_timer_sync(), - * this function should never be called while holding rpc_queue_lock. + * this function should never be called while holding queue->lock. */ static inline void rpc_delete_timer(struct rpc_task *task) @@ -169,16 +141,17 @@ static void __rpc_add_wait_queue_priorit struct list_head *q; struct rpc_task *t; + INIT_LIST_HEAD(&task->u.tk_wait.links); q = &queue->tasks[task->tk_priority]; if (unlikely(task->tk_priority > queue->maxpriority)) q = &queue->tasks[queue->maxpriority]; - list_for_each_entry(t, q, tk_list) { + list_for_each_entry(t, q, u.tk_wait.list) { if (t->tk_cookie == task->tk_cookie) { - list_add_tail(&task->tk_list, &t->tk_links); + list_add_tail(&task->u.tk_wait.list, &t->u.tk_wait.links); return; } } - list_add_tail(&task->tk_list, q); + list_add_tail(&task->u.tk_wait.list, q); } /* @@ -189,37 +162,21 @@ static void __rpc_add_wait_queue_priorit * improve overall performance. * Everyone else gets appended to the queue to ensure proper FIFO behavior. */ -static int __rpc_add_wait_queue(struct rpc_wait_queue *queue, struct rpc_task *task) +static void __rpc_add_wait_queue(struct rpc_wait_queue *queue, struct rpc_task *task) { - if (task->tk_rpcwait == queue) - return 0; + BUG_ON (RPC_IS_QUEUED(task)); - if (task->tk_rpcwait) { - printk(KERN_WARNING "RPC: doubly enqueued task!\n"); - return -EWOULDBLOCK; - } if (RPC_IS_PRIORITY(queue)) __rpc_add_wait_queue_priority(queue, task); else if (RPC_IS_SWAPPER(task)) - list_add(&task->tk_list, &queue->tasks[0]); + list_add(&task->u.tk_wait.list, &queue->tasks[0]); else - list_add_tail(&task->tk_list, &queue->tasks[0]); - task->tk_rpcwait = queue; + list_add_tail(&task->u.tk_wait.list, &queue->tasks[0]); + task->u.tk_wait.rpc_waitq = queue; + rpc_set_queued(task); dprintk("RPC: %4d added to queue %p \"%s\"\n", task->tk_pid, queue, rpc_qname(queue)); - - return 0; -} - -int rpc_add_wait_queue(struct rpc_wait_queue *q, struct rpc_task *task) -{ - int result; - - spin_lock_bh(&rpc_queue_lock); - result = __rpc_add_wait_queue(q, task); - spin_unlock_bh(&rpc_queue_lock); - return result; } /* @@ -229,12 +186,12 @@ static void __rpc_remove_wait_queue_prio { struct rpc_task *t; - if (!list_empty(&task->tk_links)) { - t = list_entry(task->tk_links.next, struct rpc_task, tk_list); - list_move(&t->tk_list, &task->tk_list); - list_splice_init(&task->tk_links, &t->tk_links); + if (!list_empty(&task->u.tk_wait.links)) { + t = list_entry(task->u.tk_wait.links.next, struct rpc_task, u.tk_wait.list); + list_move(&t->u.tk_wait.list, &task->u.tk_wait.list); + list_splice_init(&task->u.tk_wait.links, &t->u.tk_wait.links); } - list_del(&task->tk_list); + list_del(&task->u.tk_wait.list); } /* @@ -243,31 +200,17 @@ static void __rpc_remove_wait_queue_prio */ static void __rpc_remove_wait_queue(struct rpc_task *task) { - struct rpc_wait_queue *queue = task->tk_rpcwait; - - if (!queue) - return; + struct rpc_wait_queue *queue; + queue = task->u.tk_wait.rpc_waitq; if (RPC_IS_PRIORITY(queue)) __rpc_remove_wait_queue_priority(task); else - list_del(&task->tk_list); - task->tk_rpcwait = NULL; - + list_del(&task->u.tk_wait.list); dprintk("RPC: %4d removed from queue %p \"%s\"\n", task->tk_pid, queue, rpc_qname(queue)); } -void -rpc_remove_wait_queue(struct rpc_task *task) -{ - if (!task->tk_rpcwait) - return; - spin_lock_bh(&rpc_queue_lock); - __rpc_remove_wait_queue(task); - spin_unlock_bh(&rpc_queue_lock); -} - static inline void rpc_set_waitqueue_priority(struct rpc_wait_queue *queue, int priority) { queue->priority = priority; @@ -290,6 +233,7 @@ static void __rpc_init_priority_wait_que { int i; + spin_lock_init(&queue->lock); for (i = 0; i < ARRAY_SIZE(queue->tasks); i++) INIT_LIST_HEAD(&queue->tasks[i]); queue->maxpriority = maxprio; @@ -316,34 +260,31 @@ EXPORT_SYMBOL(rpc_init_wait_queue); * Note: If the task is ASYNC, this must be called with * the spinlock held to protect the wait queue operation. */ -static inline void -rpc_make_runnable(struct rpc_task *task) +static void rpc_make_runnable(struct rpc_task *task) { - if (task->tk_timeout_fn) { - printk(KERN_ERR "RPC: task w/ running timer in rpc_make_runnable!!\n"); + int do_ret; + + BUG_ON(task->tk_timeout_fn); + do_ret = rpc_test_and_set_running(task); + rpc_clear_queued(task); + if (do_ret) return; - } - rpc_set_running(task); if (RPC_IS_ASYNC(task)) { - if (RPC_IS_SLEEPING(task)) { - int status; - status = __rpc_add_wait_queue(&schedq, task); - if (status < 0) { - printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status); - task->tk_status = status; - return; - } - rpc_clear_sleeping(task); - wake_up(&rpciod_idle); + int status; + + INIT_WORK(&task->u.tk_work, rpc_async_schedule, (void *)task); + status = queue_work(task->tk_workqueue, &task->u.tk_work); + if (status < 0) { + printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status); + task->tk_status = status; + return; } - } else { - rpc_clear_sleeping(task); - wake_up(&task->tk_wait); - } + } else + wake_up(&task->u.tk_wait.waitq); } /* - * Place a newly initialized task on the schedq. + * Place a newly initialized task on the workqueue. */ static inline void rpc_schedule_run(struct rpc_task *task) @@ -352,33 +293,18 @@ rpc_schedule_run(struct rpc_task *task) if (RPC_IS_ACTIVATED(task)) return; task->tk_active = 1; - rpc_set_sleeping(task); rpc_make_runnable(task); } /* - * For other people who may need to wake the I/O daemon - * but should (for now) know nothing about its innards - */ -void rpciod_wake_up(void) -{ - if(rpciod_pid==0) - printk(KERN_ERR "rpciod: wot no daemon?\n"); - wake_up(&rpciod_idle); -} - -/* * Prepare for sleeping on a wait queue. * By always appending tasks to the list we ensure FIFO behavior. * NB: An RPC task will only receive interrupt-driven events as long * as it's on a wait queue. */ -static void -__rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task, +static void __rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task, rpc_action action, rpc_action timer) { - int status; - dprintk("RPC: %4d sleep_on(queue \"%s\" time %ld)\n", task->tk_pid, rpc_qname(q), jiffies); @@ -388,49 +314,36 @@ __rpc_sleep_on(struct rpc_wait_queue *q, } /* Mark the task as being activated if so needed */ - if (!RPC_IS_ACTIVATED(task)) { + if (!RPC_IS_ACTIVATED(task)) task->tk_active = 1; - rpc_set_sleeping(task); - } - status = __rpc_add_wait_queue(q, task); - if (status) { - printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status); - task->tk_status = status; - } else { - rpc_clear_running(task); - if (task->tk_callback) { - dprintk(KERN_ERR "RPC: %4d overwrites an active callback\n", task->tk_pid); - BUG(); - } - task->tk_callback = action; - __rpc_add_timer(task, timer); - } + __rpc_add_wait_queue(q, task); + + BUG_ON(task->tk_callback != NULL); + task->tk_callback = action; + __rpc_add_timer(task, timer); } -void -rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task, +void rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task, rpc_action action, rpc_action timer) { /* * Protect the queue operations. */ - spin_lock_bh(&rpc_queue_lock); + spin_lock_bh(&q->lock); __rpc_sleep_on(q, task, action, timer); - spin_unlock_bh(&rpc_queue_lock); + spin_unlock_bh(&q->lock); } /** - * __rpc_wake_up_task - wake up a single rpc_task + * __rpc_do_wake_up_task - wake up a single rpc_task * @task: task to be woken up * - * Caller must hold rpc_queue_lock + * Caller must hold queue->lock, and have cleared the task queued flag. */ -static void -__rpc_wake_up_task(struct rpc_task *task) +static void __rpc_do_wake_up_task(struct rpc_task *task) { - dprintk("RPC: %4d __rpc_wake_up_task (now %ld inh %d)\n", - task->tk_pid, jiffies, rpc_inhibit); + dprintk("RPC: %4d __rpc_wake_up_task (now %ld)\n", task->tk_pid, jiffies); #ifdef RPC_DEBUG if (task->tk_magic != 0xf00baa) { @@ -445,12 +358,9 @@ __rpc_wake_up_task(struct rpc_task *task printk(KERN_ERR "RPC: Inactive task (%p) being woken up!\n", task); return; } - if (RPC_IS_RUNNING(task)) - return; __rpc_disable_timer(task); - if (task->tk_rpcwait != &schedq) - __rpc_remove_wait_queue(task); + __rpc_remove_wait_queue(task); rpc_make_runnable(task); @@ -458,6 +368,18 @@ __rpc_wake_up_task(struct rpc_task *task } /* + * Wake up the specified task + */ +static void __rpc_wake_up_task(struct rpc_task *task) +{ + if (rpc_start_wakeup(task)) { + if (RPC_IS_QUEUED(task)) + __rpc_do_wake_up_task(task); + rpc_finish_wakeup(task); + } +} + +/* * Default timeout handler if none specified by user */ static void @@ -471,14 +393,18 @@ __rpc_default_timer(struct rpc_task *tas /* * Wake up the specified task */ -void -rpc_wake_up_task(struct rpc_task *task) +void rpc_wake_up_task(struct rpc_task *task) { - if (RPC_IS_RUNNING(task)) - return; - spin_lock_bh(&rpc_queue_lock); - __rpc_wake_up_task(task); - spin_unlock_bh(&rpc_queue_lock); + if (rpc_start_wakeup(task)) { + if (RPC_IS_QUEUED(task)) { + struct rpc_wait_queue *queue = task->u.tk_wait.rpc_waitq; + + spin_lock_bh(&queue->lock); + __rpc_do_wake_up_task(task); + spin_unlock_bh(&queue->lock); + } + rpc_finish_wakeup(task); + } } /* @@ -494,11 +420,11 @@ static struct rpc_task * __rpc_wake_up_n */ q = &queue->tasks[queue->priority]; if (!list_empty(q)) { - task = list_entry(q->next, struct rpc_task, tk_list); + task = list_entry(q->next, struct rpc_task, u.tk_wait.list); if (queue->cookie == task->tk_cookie) { if (--queue->nr) goto out; - list_move_tail(&task->tk_list, q); + list_move_tail(&task->u.tk_wait.list, q); } /* * Check if we need to switch queues. @@ -516,7 +442,7 @@ static struct rpc_task * __rpc_wake_up_n else q = q - 1; if (!list_empty(q)) { - task = list_entry(q->next, struct rpc_task, tk_list); + task = list_entry(q->next, struct rpc_task, u.tk_wait.list); goto new_queue; } } while (q != &queue->tasks[queue->priority]); @@ -541,14 +467,14 @@ struct rpc_task * rpc_wake_up_next(struc struct rpc_task *task = NULL; dprintk("RPC: wake_up_next(%p \"%s\")\n", queue, rpc_qname(queue)); - spin_lock_bh(&rpc_queue_lock); + spin_lock_bh(&queue->lock); if (RPC_IS_PRIORITY(queue)) task = __rpc_wake_up_next_priority(queue); else { task_for_first(task, &queue->tasks[0]) __rpc_wake_up_task(task); } - spin_unlock_bh(&rpc_queue_lock); + spin_unlock_bh(&queue->lock); return task; } @@ -557,25 +483,25 @@ struct rpc_task * rpc_wake_up_next(struc * rpc_wake_up - wake up all rpc_tasks * @queue: rpc_wait_queue on which the tasks are sleeping * - * Grabs rpc_queue_lock + * Grabs queue->lock */ void rpc_wake_up(struct rpc_wait_queue *queue) { struct rpc_task *task; struct list_head *head; - spin_lock_bh(&rpc_queue_lock); + spin_lock_bh(&queue->lock); head = &queue->tasks[queue->maxpriority]; for (;;) { while (!list_empty(head)) { - task = list_entry(head->next, struct rpc_task, tk_list); + task = list_entry(head->next, struct rpc_task, u.tk_wait.list); __rpc_wake_up_task(task); } if (head == &queue->tasks[0]) break; head--; } - spin_unlock_bh(&rpc_queue_lock); + spin_unlock_bh(&queue->lock); } /** @@ -583,18 +509,18 @@ void rpc_wake_up(struct rpc_wait_queue * * @queue: rpc_wait_queue on which the tasks are sleeping * @status: status value to set * - * Grabs rpc_queue_lock + * Grabs queue->lock */ void rpc_wake_up_status(struct rpc_wait_queue *queue, int status) { struct list_head *head; struct rpc_task *task; - spin_lock_bh(&rpc_queue_lock); + spin_lock_bh(&queue->lock); head = &queue->tasks[queue->maxpriority]; for (;;) { while (!list_empty(head)) { - task = list_entry(head->next, struct rpc_task, tk_list); + task = list_entry(head->next, struct rpc_task, u.tk_wait.list); task->tk_status = status; __rpc_wake_up_task(task); } @@ -602,7 +528,7 @@ void rpc_wake_up_status(struct rpc_wait_ break; head--; } - spin_unlock_bh(&rpc_queue_lock); + spin_unlock_bh(&queue->lock); } /* @@ -626,22 +552,23 @@ __rpc_atrun(struct rpc_task *task) /* * This is the RPC `scheduler' (or rather, the finite state machine). */ -static int -__rpc_execute(struct rpc_task *task) +static int __rpc_execute(struct rpc_task *task) { int status = 0; dprintk("RPC: %4d rpc_execute flgs %x\n", task->tk_pid, task->tk_flags); - if (!RPC_IS_RUNNING(task)) { - printk(KERN_WARNING "RPC: rpc_execute called for sleeping task!!\n"); - return 0; - } + BUG_ON(RPC_IS_QUEUED(task)); restarted: while (1) { /* + * Garbage collection of pending timers... + */ + rpc_delete_timer(task); + + /* * Execute any pending callback. */ if (RPC_DO_CALLBACK(task)) { @@ -657,7 +584,9 @@ __rpc_execute(struct rpc_task *task) */ save_callback=task->tk_callback; task->tk_callback=NULL; + lock_kernel(); save_callback(task); + unlock_kernel(); } /* @@ -665,43 +594,35 @@ __rpc_execute(struct rpc_task *task) * tk_action may be NULL when the task has been killed * by someone else. */ - if (RPC_IS_RUNNING(task)) { - /* - * Garbage collection of pending timers... - */ - rpc_delete_timer(task); + if (!RPC_IS_QUEUED(task)) { if (!task->tk_action) break; + lock_kernel(); task->tk_action(task); - /* micro-optimization to avoid spinlock */ - if (RPC_IS_RUNNING(task)) - continue; + unlock_kernel(); } /* - * Check whether task is sleeping. + * Lockless check for whether task is sleeping or not. */ - spin_lock_bh(&rpc_queue_lock); - if (!RPC_IS_RUNNING(task)) { - rpc_set_sleeping(task); - if (RPC_IS_ASYNC(task)) { - spin_unlock_bh(&rpc_queue_lock); + if (!RPC_IS_QUEUED(task)) + continue; + rpc_clear_running(task); + if (RPC_IS_ASYNC(task)) { + /* Careful! we may have raced... */ + if (RPC_IS_QUEUED(task)) return 0; - } + if (rpc_test_and_set_running(task)) + return 0; + continue; } - spin_unlock_bh(&rpc_queue_lock); - if (!RPC_IS_SLEEPING(task)) - continue; /* sync task: sleep here */ dprintk("RPC: %4d sync task going to sleep\n", task->tk_pid); - if (current->pid == rpciod_pid) - printk(KERN_ERR "RPC: rpciod waiting on sync task!\n"); - if (RPC_TASK_UNINTERRUPTIBLE(task)) { - __wait_event(task->tk_wait, !RPC_IS_SLEEPING(task)); + __wait_event(task->u.tk_wait.waitq, !RPC_IS_QUEUED(task)); } else { - __wait_event_interruptible(task->tk_wait, !RPC_IS_SLEEPING(task), status); + __wait_event_interruptible(task->u.tk_wait.waitq, !RPC_IS_QUEUED(task), status); /* * When a sync task receives a signal, it exits with * -ERESTARTSYS. In order to catch any callbacks that @@ -715,11 +636,14 @@ __rpc_execute(struct rpc_task *task) rpc_wake_up_task(task); } } + rpc_set_running(task); dprintk("RPC: %4d sync task resuming\n", task->tk_pid); } if (task->tk_exit) { + lock_kernel(); task->tk_exit(task); + unlock_kernel(); /* If tk_action is non-null, the user wants us to restart */ if (task->tk_action) { if (!RPC_ASSASSINATED(task)) { @@ -738,7 +662,6 @@ __rpc_execute(struct rpc_task *task) /* Release all resources associated with the task */ rpc_release_task(task); - return status; } @@ -754,57 +677,16 @@ __rpc_execute(struct rpc_task *task) int rpc_execute(struct rpc_task *task) { - int status = -EIO; - if (rpc_inhibit) { - printk(KERN_INFO "RPC: execution inhibited!\n"); - goto out_release; - } - - status = -EWOULDBLOCK; - if (task->tk_active) { - printk(KERN_ERR "RPC: active task was run twice!\n"); - goto out_err; - } + BUG_ON(task->tk_active); task->tk_active = 1; rpc_set_running(task); return __rpc_execute(task); - out_release: - rpc_release_task(task); - out_err: - return status; } -/* - * This is our own little scheduler for async RPC tasks. - */ -static void -__rpc_schedule(void) +static void rpc_async_schedule(void *arg) { - struct rpc_task *task; - int count = 0; - - dprintk("RPC: rpc_schedule enter\n"); - while (1) { - - task_for_first(task, &schedq.tasks[0]) { - __rpc_remove_wait_queue(task); - spin_unlock_bh(&rpc_queue_lock); - - __rpc_execute(task); - spin_lock_bh(&rpc_queue_lock); - } else { - break; - } - - if (++count >= 200 || need_resched()) { - count = 0; - spin_unlock_bh(&rpc_queue_lock); - schedule(); - spin_lock_bh(&rpc_queue_lock); - } - } - dprintk("RPC: rpc_schedule leave\n"); + __rpc_execute((struct rpc_task *)arg); } /* @@ -862,7 +744,6 @@ void rpc_init_task(struct rpc_task *task task->tk_client = clnt; task->tk_flags = flags; task->tk_exit = callback; - init_waitqueue_head(&task->tk_wait); if (current->uid != current->fsuid || current->gid != current->fsgid) task->tk_flags |= RPC_TASK_SETUID; @@ -873,7 +754,11 @@ void rpc_init_task(struct rpc_task *task task->tk_priority = RPC_PRIORITY_NORMAL; task->tk_cookie = (unsigned long)current; - INIT_LIST_HEAD(&task->tk_links); + + /* Initialize workqueue for async tasks */ + task->tk_workqueue = rpciod_workqueue; + if (!RPC_IS_ASYNC(task)) + init_waitqueue_head(&task->u.tk_wait.waitq); /* Add to global list of all tasks */ spin_lock(&rpc_sched_lock); @@ -944,8 +829,7 @@ cleanup: goto out; } -void -rpc_release_task(struct rpc_task *task) +void rpc_release_task(struct rpc_task *task) { dprintk("RPC: %4d release task\n", task->tk_pid); @@ -963,19 +847,9 @@ rpc_release_task(struct rpc_task *task) list_del(&task->tk_task); spin_unlock(&rpc_sched_lock); - /* Protect the execution below. */ - spin_lock_bh(&rpc_queue_lock); - - /* Disable timer to prevent zombie wakeup */ - __rpc_disable_timer(task); - - /* Remove from any wait queue we're still on */ - __rpc_remove_wait_queue(task); - + BUG_ON (RPC_IS_QUEUED(task)); task->tk_active = 0; - spin_unlock_bh(&rpc_queue_lock); - /* Synchronously delete any running timer */ rpc_delete_timer(task); @@ -1005,10 +879,9 @@ rpc_release_task(struct rpc_task *task) * queue 'childq'. If so returns a pointer to the parent. * Upon failure returns NULL. * - * Caller must hold rpc_queue_lock + * Caller must hold childq.lock */ -static inline struct rpc_task * -rpc_find_parent(struct rpc_task *child) +static inline struct rpc_task *rpc_find_parent(struct rpc_task *child) { struct rpc_task *task, *parent; struct list_head *le; @@ -1021,17 +894,16 @@ rpc_find_parent(struct rpc_task *child) return NULL; } -static void -rpc_child_exit(struct rpc_task *child) +static void rpc_child_exit(struct rpc_task *child) { struct rpc_task *parent; - spin_lock_bh(&rpc_queue_lock); + spin_lock_bh(&childq.lock); if ((parent = rpc_find_parent(child)) != NULL) { parent->tk_status = child->tk_status; __rpc_wake_up_task(parent); } - spin_unlock_bh(&rpc_queue_lock); + spin_unlock_bh(&childq.lock); } /* @@ -1054,22 +926,20 @@ fail: return NULL; } -void -rpc_run_child(struct rpc_task *task, struct rpc_task *child, rpc_action func) +void rpc_run_child(struct rpc_task *task, struct rpc_task *child, rpc_action func) { - spin_lock_bh(&rpc_queue_lock); + spin_lock_bh(&childq.lock); /* N.B. Is it possible for the child to have already finished? */ __rpc_sleep_on(&childq, task, func, NULL); rpc_schedule_run(child); - spin_unlock_bh(&rpc_queue_lock); + spin_unlock_bh(&childq.lock); } /* * Kill all tasks for the given client. * XXX: kill their descendants as well? */ -void -rpc_killall_tasks(struct rpc_clnt *clnt) +void rpc_killall_tasks(struct rpc_clnt *clnt) { struct rpc_task *rovr; struct list_head *le; @@ -1091,93 +961,14 @@ rpc_killall_tasks(struct rpc_clnt *clnt) static DECLARE_MUTEX_LOCKED(rpciod_running); -static inline int -rpciod_task_pending(void) -{ - return !list_empty(&schedq.tasks[0]); -} - - -/* - * This is the rpciod kernel thread - */ -static int -rpciod(void *ptr) -{ - int rounds = 0; - - lock_kernel(); - /* - * Let our maker know we're running ... - */ - rpciod_pid = current->pid; - up(&rpciod_running); - - daemonize("rpciod"); - allow_signal(SIGKILL); - - dprintk("RPC: rpciod starting (pid %d)\n", rpciod_pid); - spin_lock_bh(&rpc_queue_lock); - while (rpciod_users) { - DEFINE_WAIT(wait); - if (signalled()) { - spin_unlock_bh(&rpc_queue_lock); - rpciod_killall(); - flush_signals(current); - spin_lock_bh(&rpc_queue_lock); - } - __rpc_schedule(); - if (current->flags & PF_FREEZE) { - spin_unlock_bh(&rpc_queue_lock); - refrigerator(PF_FREEZE); - spin_lock_bh(&rpc_queue_lock); - } - - if (++rounds >= 64) { /* safeguard */ - spin_unlock_bh(&rpc_queue_lock); - schedule(); - rounds = 0; - spin_lock_bh(&rpc_queue_lock); - } - - dprintk("RPC: rpciod back to sleep\n"); - prepare_to_wait(&rpciod_idle, &wait, TASK_INTERRUPTIBLE); - if (!rpciod_task_pending() && !signalled()) { - spin_unlock_bh(&rpc_queue_lock); - schedule(); - rounds = 0; - spin_lock_bh(&rpc_queue_lock); - } - finish_wait(&rpciod_idle, &wait); - dprintk("RPC: switch to rpciod\n"); - } - spin_unlock_bh(&rpc_queue_lock); - - dprintk("RPC: rpciod shutdown commences\n"); - if (!list_empty(&all_tasks)) { - printk(KERN_ERR "rpciod: active tasks at shutdown?!\n"); - rpciod_killall(); - } - - dprintk("RPC: rpciod exiting\n"); - unlock_kernel(); - - rpciod_pid = 0; - complete_and_exit(&rpciod_killer, 0); - return 0; -} - -static void -rpciod_killall(void) +static void rpciod_killall(void) { unsigned long flags; while (!list_empty(&all_tasks)) { clear_thread_flag(TIF_SIGPENDING); rpc_killall_tasks(NULL); - spin_lock_bh(&rpc_queue_lock); - __rpc_schedule(); - spin_unlock_bh(&rpc_queue_lock); + flush_workqueue(rpciod_workqueue); if (!list_empty(&all_tasks)) { dprintk("rpciod_killall: waiting for tasks to exit\n"); yield(); @@ -1195,28 +986,30 @@ rpciod_killall(void) int rpciod_up(void) { + struct workqueue_struct *wq; int error = 0; down(&rpciod_sema); - dprintk("rpciod_up: pid %d, users %d\n", rpciod_pid, rpciod_users); + dprintk("rpciod_up: users %d\n", rpciod_users); rpciod_users++; - if (rpciod_pid) + if (rpciod_workqueue) goto out; /* * If there's no pid, we should be the first user. */ if (rpciod_users > 1) - printk(KERN_WARNING "rpciod_up: no pid, %d users??\n", rpciod_users); + printk(KERN_WARNING "rpciod_up: no workqueue, %d users??\n", rpciod_users); /* * Create the rpciod thread and wait for it to start. */ - error = kernel_thread(rpciod, NULL, 0); - if (error < 0) { - printk(KERN_WARNING "rpciod_up: create thread failed, error=%d\n", error); + error = -ENOMEM; + wq = create_workqueue("rpciod"); + if (wq == NULL) { + printk(KERN_WARNING "rpciod_up: create workqueue failed, error=%d\n", error); rpciod_users--; goto out; } - down(&rpciod_running); + rpciod_workqueue = wq; error = 0; out: up(&rpciod_sema); @@ -1227,20 +1020,21 @@ void rpciod_down(void) { down(&rpciod_sema); - dprintk("rpciod_down pid %d sema %d\n", rpciod_pid, rpciod_users); + dprintk("rpciod_down sema %d\n", rpciod_users); if (rpciod_users) { if (--rpciod_users) goto out; } else - printk(KERN_WARNING "rpciod_down: pid=%d, no users??\n", rpciod_pid); + printk(KERN_WARNING "rpciod_down: no users??\n"); - if (!rpciod_pid) { + if (!rpciod_workqueue) { dprintk("rpciod_down: Nothing to do!\n"); goto out; } + rpciod_killall(); - kill_proc(rpciod_pid, SIGKILL, 1); - wait_for_completion(&rpciod_killer); + destroy_workqueue(rpciod_workqueue); + rpciod_workqueue = NULL; out: up(&rpciod_sema); } @@ -1258,7 +1052,12 @@ void rpc_show_tasks(void) } printk("-pid- proc flgs status -client- -prog- --rqstp- -timeout " "-rpcwait -action- --exit--\n"); - alltask_for_each(t, le, &all_tasks) + alltask_for_each(t, le, &all_tasks) { + const char *rpc_waitq = "none"; + + if (RPC_IS_QUEUED(t)) + rpc_waitq = rpc_qname(t->u.tk_wait.rpc_waitq); + printk("%05d %04d %04x %06d %8p %6d %8p %08ld %8s %8p %8p\n", t->tk_pid, (t->tk_msg.rpc_proc ? t->tk_msg.rpc_proc->p_proc : -1), @@ -1266,8 +1065,9 @@ void rpc_show_tasks(void) t->tk_client, (t->tk_client ? t->tk_client->cl_prog : 0), t->tk_rqstp, t->tk_timeout, - rpc_qname(t->tk_rpcwait), + rpc_waitq, t->tk_action, t->tk_exit); + } spin_unlock(&rpc_sched_lock); } #endif --- linux-2.6.9-rc1/net/sunrpc/svcauth_unix.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/net/sunrpc/svcauth_unix.c 2004-09-02 20:51:53.000000000 -0700 @@ -90,7 +90,7 @@ static void svcauth_unix_domain_release( struct ip_map { struct cache_head h; - char *m_class; /* e.g. "nfsd" */ + char m_class[8]; /* e.g. "nfsd" */ struct in_addr m_addr; struct unix_domain *m_client; int m_add_change; @@ -104,7 +104,6 @@ void ip_map_put(struct cache_head *item, if (test_bit(CACHE_VALID, &item->flags) && !test_bit(CACHE_NEGATIVE, &item->flags)) auth_domain_put(&im->m_client->h); - kfree(im->m_class); kfree(im); } } @@ -121,8 +120,7 @@ static inline int ip_map_match(struct ip } static inline void ip_map_init(struct ip_map *new, struct ip_map *item) { - new->m_class = item->m_class; - item->m_class = NULL; + strcpy(new->m_class, item->m_class); new->m_addr.s_addr = item->m_addr.s_addr; } static inline void ip_map_update(struct ip_map *new, struct ip_map *item) @@ -171,6 +169,8 @@ static int ip_map_parse(struct cache_det /* class */ len = qword_get(&mesg, class, 50); if (len <= 0) return -EINVAL; + if (len >= sizeof(ipm.m_class)) + return -EINVAL; /* ip address */ len = qword_get(&mesg, buf, 50); @@ -194,9 +194,7 @@ static int ip_map_parse(struct cache_det } else dom = NULL; - ipm.m_class = strdup(class); - if (ipm.m_class == NULL) - return -ENOMEM; + strcpy(ipm.m_class, class); ipm.m_addr.s_addr = htonl((((((b1<<8)|b2)<<8)|b3)<<8)|b4); ipm.h.flags = 0; @@ -212,7 +210,6 @@ static int ip_map_parse(struct cache_det ip_map_put(&ipmp->h, &ip_map_cache); if (dom) auth_domain_put(dom); - if (ipm.m_class) kfree(ipm.m_class); if (!ipmp) return -ENOMEM; cache_flush(); @@ -272,9 +269,7 @@ int auth_unix_add_addr(struct in_addr ad if (dom->flavour != RPC_AUTH_UNIX) return -EINVAL; udom = container_of(dom, struct unix_domain, h); - ip.m_class = strdup("nfsd"); - if (!ip.m_class) - return -ENOMEM; + strcpy(ip.m_class, "nfsd"); ip.m_addr = addr; ip.m_client = udom; ip.m_add_change = udom->addr_changes+1; @@ -282,7 +277,7 @@ int auth_unix_add_addr(struct in_addr ad ip.h.expiry_time = NEVER; ipmp = ip_map_lookup(&ip, 1); - if (ip.m_class) kfree(ip.m_class); + if (ipmp) { ip_map_put(&ipmp->h, &ip_map_cache); return 0; @@ -306,7 +301,7 @@ struct auth_domain *auth_unix_lookup(str struct ip_map key, *ipm; struct auth_domain *rv; - key.m_class = "nfsd"; + strcpy(key.m_class, "nfsd"); key.m_addr = addr; ipm = ip_map_lookup(&key, 0); @@ -368,7 +363,7 @@ svcauth_null_accept(struct svc_rqst *rqs svc_putu32(resv, RPC_AUTH_NULL); svc_putu32(resv, 0); - key.m_class = rqstp->rq_server->sv_program->pg_class; + strcpy(key.m_class, rqstp->rq_server->sv_program->pg_class); key.m_addr = rqstp->rq_addr.sin_addr; ipm = ip_map_lookup(&key, 0); @@ -464,7 +459,7 @@ svcauth_unix_accept(struct svc_rqst *rqs } - key.m_class = rqstp->rq_server->sv_program->pg_class; + strcpy(key.m_class, rqstp->rq_server->sv_program->pg_class); key.m_addr = rqstp->rq_addr.sin_addr; --- linux-2.6.9-rc1/net/sunrpc/xprt.c 2004-08-24 00:03:17.000000000 -0700 +++ 25/net/sunrpc/xprt.c 2004-09-03 00:56:48.640766128 -0700 @@ -1101,7 +1101,7 @@ xprt_write_space(struct sock *sk) goto out; spin_lock_bh(&xprt->sock_lock); - if (xprt->snd_task && xprt->snd_task->tk_rpcwait == &xprt->pending) + if (xprt->snd_task) rpc_wake_up_task(xprt->snd_task); spin_unlock_bh(&xprt->sock_lock); out: --- linux-2.6.9-rc1/scripts/basic/fixdep.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/scripts/basic/fixdep.c 2004-09-02 20:51:53.000000000 -0700 @@ -93,6 +93,14 @@ * (Note: it'd be easy to port over the complete mkdep state machine, * but I don't think the added complexity is worth it) */ +/* + * Note 2: if somebody writes HELLO_CONFIG_BOOM in a file, it will depend onto + * CONFIG_BOOM. This could seem a bug (not too hard to fix), but please do not + * fix it! Some UserModeLinux files (look at arch/um/) call CONFIG_BOOM as + * UML_CONFIG_BOOM, to avoid conflicts with /usr/include/linux/autoconf.h, + * through arch/um/include/uml-config.h; this fixdep "bug" makes sure that + * those files will have correct dependencies. + */ #include #include @@ -310,6 +318,7 @@ void parse_dep_file(void *map, size_t le } memcpy(s, m, p-m); s[p-m] = 0; if (strrcmp(s, "include/linux/autoconf.h") && + strrcmp(s, "arch/um/include/uml-config.h") && strrcmp(s, ".ver")) { printf(" %s \\\n", s); do_config_file(s); --- linux-2.6.9-rc1/scripts/kallsyms.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/scripts/kallsyms.c 2004-09-03 00:57:08.716714120 -0700 @@ -6,6 +6,22 @@ * of the GNU General Public License, incorporated herein by reference. * * Usage: nm -n vmlinux | scripts/kallsyms [--all-symbols] > symbols.S + * + * ChangeLog: + * + * (25/Aug/2004) Paulo Marques + * Changed the compression method from stem compression to "table lookup" + * compression + * + * Table compression uses all the unused char codes on the symbols and + * maps these to the most used substrings (tokens). For instance, it might + * map char code 0xF7 to represent "write_" and then in every symbol where + * "write_" appears it can be replaced by 0xF7, saving 5 bytes. + * The used codes themselves are also placed in the table so that the + * decompresion can work without "special cases". + * Applied to kernel symbols, this usually produces a compression ratio + * of about 50%. + * */ #include @@ -13,10 +29,39 @@ #include #include +/* maximum token length used. It doesn't pay to increase it a lot, because + * very long substrings probably don't repeat themselves too often. */ +#define MAX_TOK_SIZE 11 +#define KSYM_NAME_LEN 127 + +/* we use only a subset of the complete symbol table to gather the token count, + * to speed up compression, at the expense of a little compression ratio */ +#define WORKING_SET 1024 + +/* first find the best token only on the list of tokens that would profit more + * than GOOD_BAD_THRESHOLD. Only if this list is empty go to the "bad" list. + * Increasing this value will put less tokens on the "good" list, so the search + * is faster. However, if the good list runs out of tokens, we must painfully + * search the bad list. */ +#define GOOD_BAD_THRESHOLD 10 + +/* token hash parameters */ +#define HASH_BITS 18 +#define HASH_TABLE_SIZE (1 << HASH_BITS) +#define HASH_MASK (HASH_TABLE_SIZE - 1) +#define HASH_BASE_OFFSET 2166136261U +#define HASH_FOLD(a) ((a)&(HASH_MASK)) + +/* flags to mark symbols */ +#define SYM_FLAG_VALID 1 +#define SYM_FLAG_SAMPLED 2 + struct sym_entry { unsigned long long addr; char type; - char *sym; + unsigned char flags; + unsigned char len; + unsigned char *sym; }; @@ -25,6 +70,26 @@ static int size, cnt; static unsigned long long _stext, _etext, _sinittext, _einittext; static int all_symbols = 0; +struct token { + unsigned char data[MAX_TOK_SIZE]; + unsigned char len; + /* profit: the number of bytes that could be saved by inserting this + * token into the table */ + int profit; + struct token *next; /* next token on the hash list */ + struct token *right; /* next token on the good/bad list */ + struct token *left; /* previous token on the good/bad list */ + struct token *smaller; /* token that is less one letter than this one */ + }; + +struct token bad_head, good_head; +struct token *hash_table[HASH_TABLE_SIZE]; + +/* the table that holds the result of the compression */ +unsigned char best_table[256][MAX_TOK_SIZE+1]; +unsigned char best_table_len[256]; + + static void usage(void) { @@ -59,34 +124,53 @@ read_symbol(FILE *in, struct sym_entry * else if (toupper(s->type) == 'A' || toupper(s->type) == 'U') return -1; - s->sym = strdup(str); + /* include the type field in the symbol name, so that it gets + * compressed together */ + s->len = strlen(str) + 1; + s->sym = (char *) malloc(s->len + 1); + strcpy(s->sym + 1, str); + s->sym[0] = s->type; + return 0; } static int symbol_valid(struct sym_entry *s) { + /* Symbols which vary between passes. Passes 1 and 2 must have + * identical symbol lists. The kallsyms_* symbols below are only added + * after pass 1, they would be included in pass 2 when --all-symbols is + * specified so exclude them to get a stable symbol list. + */ + static char *special_symbols[] = { + "kallsyms_addresses", + "kallsyms_num_syms", + "kallsyms_names", + "kallsyms_markers", + "kallsyms_token_table", + "kallsyms_token_index", + + /* Exclude linker generated symbols which vary between passes */ + "_SDA_BASE_", /* ppc */ + "_SDA2_BASE_", /* ppc */ + NULL }; + int i; + + /* if --all-symbols is not specified, then symbols outside the text + * and inittext sections are discarded */ if (!all_symbols) { if ((s->addr < _stext || s->addr > _etext) && (s->addr < _sinittext || s->addr > _einittext)) return 0; } - /* Exclude symbols which vary between passes. Passes 1 and 2 must have - * identical symbol lists. The kallsyms_* symbols below are only added - * after pass 1, they would be included in pass 2 when --all-symbols is - * specified so exclude them to get a stable symbol list. - */ - if (strstr(s->sym, "_compiled.") || - strcmp(s->sym, "kallsyms_addresses") == 0 || - strcmp(s->sym, "kallsyms_num_syms") == 0 || - strcmp(s->sym, "kallsyms_names") == 0) + /* Exclude symbols which vary between passes. */ + if (strstr(s->sym + 1, "_compiled.")) return 0; - /* Exclude linker generated symbols which vary between passes */ - if (strcmp(s->sym, "_SDA_BASE_") == 0 || /* ppc */ - strcmp(s->sym, "_SDA2_BASE_") == 0) /* ppc */ - return 0; + for (i = 0; special_symbols[i]; i++) + if( strcmp(s->sym + 1, special_symbols[i]) == 0 ) + return 0; return 1; } @@ -108,11 +192,47 @@ read_map(FILE *in) } } +static void output_label(char *label) +{ + printf(".globl %s\n",label); + printf("\tALGN\n"); + printf("%s:\n",label); +} + +/* uncompress a compressed symbol. When this function is called, the best table + * might still be compressed itself, so the function needs to be recursive */ +static int expand_symbol(unsigned char *data, int len, char *result) +{ + int c, rlen, total=0; + + while (len) { + c = *data; + /* if the table holds a single char that is the same as the one + * we are looking for, then end the search */ + if (best_table[c][0]==c && best_table_len[c]==1) { + *result++ = c; + total++; + } else { + /* if not, recurse and expand */ + rlen = expand_symbol(best_table[c], best_table_len[c], result); + total += rlen; + result += rlen; + } + data++; + len--; + } + *result=0; + + return total; +} + static void write_src(void) { - int i, valid = 0; - char *prev; + int i, k, off, valid; + unsigned int best_idx[256]; + unsigned int *markers; + char buf[KSYM_NAME_LEN+1]; printf("#include \n"); printf("#if BITS_PER_LONG == 64\n"); @@ -125,43 +245,399 @@ write_src(void) printf(".data\n"); - printf(".globl kallsyms_addresses\n"); - printf("\tALGN\n"); - printf("kallsyms_addresses:\n"); + output_label("kallsyms_addresses"); + valid = 0; for (i = 0; i < cnt; i++) { - if (!symbol_valid(&table[i])) - continue; - - printf("\tPTR\t%#llx\n", table[i].addr); - valid++; + if (table[i].flags & SYM_FLAG_VALID) { + printf("\tPTR\t%#llx\n", table[i].addr); + valid++; + } } printf("\n"); - printf(".globl kallsyms_num_syms\n"); - printf("\tALGN\n"); - printf("kallsyms_num_syms:\n"); + output_label("kallsyms_num_syms"); printf("\tPTR\t%d\n", valid); printf("\n"); - printf(".globl kallsyms_names\n"); - printf("\tALGN\n"); - printf("kallsyms_names:\n"); - prev = ""; + /* table of offset markers, that give the offset in the compressed stream + * every 256 symbols */ + markers = (unsigned int *) malloc(sizeof(unsigned int)*((valid + 255) / 256)); + + output_label("kallsyms_names"); + valid = 0; + off = 0; for (i = 0; i < cnt; i++) { - int k; - if (!symbol_valid(&table[i])) + if (!table[i].flags & SYM_FLAG_VALID) continue; - for (k = 0; table[i].sym[k] && table[i].sym[k] == prev[k]; ++k) - ; + if ((valid & 0xFF) == 0) + markers[valid >> 8] = off; + + printf("\t.byte 0x%02x", table[i].len); + for (k = 0; k < table[i].len; k++) + printf(", 0x%02x", table[i].sym[k]); + printf("\n"); - printf("\t.byte 0x%02x\n\t.asciz\t\"%s\"\n", k, table[i].sym + k); - prev = table[i].sym; + off += table[i].len + 1; + valid++; + } + printf("\n"); + + output_label("kallsyms_markers"); + for (i = 0; i < ((valid + 255) >> 8); i++) + printf("\tPTR\t%d\n", markers[i]); + printf("\n"); + + free(markers); + + output_label("kallsyms_token_table"); + off = 0; + for (i = 0; i < 256; i++) { + best_idx[i] = off; + expand_symbol(best_table[i],best_table_len[i],buf); + printf("\t.asciz\t\"%s\"\n", buf); + off += strlen(buf) + 1; } printf("\n"); + + output_label("kallsyms_token_index"); + for (i = 0; i < 256; i++) + printf("\t.word\t%d\n", best_idx[i]); + printf("\n"); +} + + +/* table lookup compression functions */ + +static inline unsigned int rehash_token(unsigned int hash, unsigned char data) +{ + return ((hash * 16777619) ^ data); +} + +static unsigned int hash_token(unsigned char *data, int len) +{ + unsigned int hash=HASH_BASE_OFFSET; + int i; + + for (i = 0; i < len; i++) + hash = rehash_token(hash, data[i]); + + return HASH_FOLD(hash); +} + +/* find a token given its data and hash value */ +static struct token *find_token_hash(unsigned char *data, int len, unsigned int hash) +{ + struct token *ptr; + + ptr = hash_table[hash]; + + while (ptr) { + if ((ptr->len == len) && (memcmp(ptr->data, data, len) == 0)) + return ptr; + ptr=ptr->next; + } + + return NULL; +} + +static inline void insert_token_in_group(struct token *head, struct token *ptr) +{ + ptr->right = head->right; + ptr->right->left = ptr; + head->right = ptr; + ptr->left = head; +} + +static inline void remove_token_from_group(struct token *ptr) +{ + ptr->left->right = ptr->right; + ptr->right->left = ptr->left; +} + + +/* build the counts for all the tokens that start with "data", and have lenghts + * from 2 to "len" */ +static void learn_token(unsigned char *data, int len) +{ + struct token *ptr,*last_ptr; + int i, newprofit; + unsigned int hash = HASH_BASE_OFFSET; + unsigned int hashes[MAX_TOK_SIZE + 1]; + + if (len > MAX_TOK_SIZE) + len = MAX_TOK_SIZE; + + /* calculate and store the hash values for all the sub-tokens */ + hash = rehash_token(hash, data[0]); + for (i = 2; i <= len; i++) { + hash = rehash_token(hash, data[i-1]); + hashes[i] = HASH_FOLD(hash); + } + + last_ptr = NULL; + ptr = NULL; + + for (i = len; i >= 2; i--) { + hash = hashes[i]; + + if (!ptr) ptr = find_token_hash(data, i, hash); + + if (!ptr) { + /* create a new token entry */ + ptr = (struct token *) malloc(sizeof(*ptr)); + + memcpy(ptr->data, data, i); + ptr->len = i; + + /* when we create an entry, it's profit is 0 because + * we also take into account the size of the token on + * the compressed table. We then subtract GOOD_BAD_THRESHOLD + * so that the test to see if this token belongs to + * the good or bad list, is a comparison to zero */ + ptr->profit = -GOOD_BAD_THRESHOLD; + + ptr->next = hash_table[hash]; + hash_table[hash] = ptr; + + insert_token_in_group(&bad_head, ptr); + + ptr->smaller = NULL; + } else { + newprofit = ptr->profit + (ptr->len - 1); + /* check to see if this token needs to be moved to a + * different list */ + if((ptr->profit < 0) && (newprofit >= 0)) { + remove_token_from_group(ptr); + insert_token_in_group(&good_head,ptr); + } + ptr->profit = newprofit; + } + + if (last_ptr) last_ptr->smaller = ptr; + last_ptr = ptr; + + ptr = ptr->smaller; + } } +/* decrease the counts for all the tokens that start with "data", and have lenghts + * from 2 to "len". This function is much simpler than learn_token because we have + * more guarantees (tho tokens exist, the ->smaller pointer is set, etc.) + * The two separate functions exist only because of compression performance */ +static void forget_token(unsigned char *data, int len) +{ + struct token *ptr; + int i, newprofit; + unsigned int hash=0; + + if (len > MAX_TOK_SIZE) len = MAX_TOK_SIZE; + + hash = hash_token(data, len); + ptr = find_token_hash(data, len, hash); + + for (i = len; i >= 2; i--) { + + newprofit = ptr->profit - (ptr->len - 1); + if ((ptr->profit >= 0) && (newprofit < 0)) { + remove_token_from_group(ptr); + insert_token_in_group(&bad_head, ptr); + } + ptr->profit=newprofit; + + ptr=ptr->smaller; + } +} + +/* count all the possible tokens in a symbol */ +static void learn_symbol(unsigned char *symbol, int len) +{ + int i; + + for (i = 0; i < len - 1; i++) + learn_token(symbol + i, len - i); +} + +/* decrease the count for all the possible tokens in a symbol */ +static void forget_symbol(unsigned char *symbol, int len) +{ + int i; + + for (i = 0; i < len - 1; i++) + forget_token(symbol + i, len - i); +} + +/* set all the symbol flags and do the initial token count */ +static void build_initial_tok_table(void) +{ + int i, use_it, valid; + + valid = 0; + for (i = 0; i < cnt; i++) { + table[i].flags = 0; + if ( symbol_valid(&table[i]) ) { + table[i].flags |= SYM_FLAG_VALID; + valid++; + } + } + + use_it = 0; + for (i = 0; i < cnt; i++) { + + /* subsample the available symbols. This method is almost like + * a Bresenham's algorithm to get uniformly distributed samples + * across the symbol table */ + if (table[i].flags & SYM_FLAG_VALID) { + + use_it += WORKING_SET; + + if (use_it >= valid) { + table[i].flags |= SYM_FLAG_SAMPLED; + use_it -= valid; + } + } + if (table[i].flags & SYM_FLAG_SAMPLED) + learn_symbol(table[i].sym, table[i].len); + } +} + +/* replace a given token in all the valid symbols. Use the sampled symbols + * to update the counts */ +static void compress_symbols(unsigned char *str, int tlen, int idx) +{ + int i, len, learn, size; + unsigned char *p; + + for (i = 0; i < cnt; i++) { + + if (!(table[i].flags & SYM_FLAG_VALID)) continue; + + len = table[i].len; + learn = 0; + p = table[i].sym; + + do { + /* find the token on the symbol */ + p = (unsigned char *) strstr((char *) p, (char *) str); + if (!p) break; + + if (!learn) { + /* if this symbol was used to count, decrease it */ + if (table[i].flags & SYM_FLAG_SAMPLED) + forget_symbol(table[i].sym, len); + learn = 1; + } + + *p = idx; + size = (len - (p - table[i].sym)) - tlen + 1; + memmove(p + 1, p + tlen, size); + p++; + len -= tlen - 1; + + } while (size >= tlen); + + if(learn) { + table[i].len = len; + /* if this symbol was used to count, learn it again */ + if(table[i].flags & SYM_FLAG_SAMPLED) + learn_symbol(table[i].sym, len); + } + } +} + +/* search the token with the maximum profit */ +static struct token *find_best_token(void) +{ + struct token *ptr,*best,*head; + int bestprofit; + + bestprofit=-10000; + + /* failsafe: if the "good" list is empty search from the "bad" list */ + if(good_head.right == &good_head) head = &bad_head; + else head = &good_head; + + ptr = head->right; + best = NULL; + while (ptr != head) { + if (ptr->profit > bestprofit) { + bestprofit = ptr->profit; + best = ptr; + } + ptr = ptr->right; + } + + return best; +} + +/* this is the core of the algorithm: calculate the "best" table */ +static void optimize_result(void) +{ + struct token *best; + int i; + + /* using the '\0' symbol last allows compress_symbols to use standard + * fast string functions */ + for (i = 255; i >= 0; i--) { + + /* if this table slot is empty (it is not used by an actual + * original char code */ + if (!best_table_len[i]) { + + /* find the token with the breates profit value */ + best = find_best_token(); + + /* place it in the "best" table */ + best_table_len[i] = best->len; + memcpy(best_table[i], best->data, best_table_len[i]); + /* zero terminate the token so that we can use strstr + in compress_symbols */ + best_table[i][best_table_len[i]]='\0'; + + /* replace this token in all the valid symbols */ + compress_symbols(best_table[i], best_table_len[i], i); + } + } +} + +/* start by placing the symbols that are actually used on the table */ +static void insert_real_symbols_in_table(void) +{ + int i, j, c; + + memset(best_table, 0, sizeof(best_table)); + memset(best_table_len, 0, sizeof(best_table_len)); + + for (i = 0; i < cnt; i++) { + if (table[i].flags & SYM_FLAG_VALID) { + for (j = 0; j < table[i].len; j++) { + c = table[i].sym[j]; + best_table[c][0]=c; + best_table_len[c]=1; + } + } + } +} + +static void optimize_token_table(void) +{ + memset(hash_table, 0, sizeof(hash_table)); + + good_head.left = &good_head; + good_head.right = &good_head; + + bad_head.left = &bad_head; + bad_head.right = &bad_head; + + build_initial_tok_table(); + + insert_real_symbols_in_table(); + + optimize_result(); +} + + int main(int argc, char **argv) { @@ -171,6 +647,7 @@ main(int argc, char **argv) usage(); read_map(stdin); + optimize_token_table(); write_src(); return 0; --- linux-2.6.9-rc1/scripts/kconfig/mconf.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/scripts/kconfig/mconf.c 2004-09-03 00:57:12.845086512 -0700 @@ -28,7 +28,7 @@ static const char menu_instructions[] = " selects submenus --->. " "Highlighted letters are hotkeys. " "Pressing includes, excludes, modularizes features. " - "Press to exit, for Help. " + "Press to exit, for Help, for Search. " "Legend: [*] built-in [ ] excluded module < > module capable", radiolist_instructions[] = "Use the arrow keys to navigate this window or " @@ -102,6 +102,7 @@ static void show_textbox(const char *tit static void show_helptext(const char *title, const char *text); static void show_help(struct menu *menu); static void show_readme(void); +static void search_conf(char *); static void cprint_init(void); static int cprint1(const char *fmt, ...); @@ -274,6 +275,88 @@ static int exec_conf(void) return WEXITSTATUS(stat); } +static struct menu *do_search(struct menu *menu, struct symbol *sym) +{ + struct menu *child, *ret; + /* Ignore invisible menus ? + if (!menu_is_visible(menu)) + return NULL; + */ + + if (menu->sym) { + if (menu->sym->name && !strcmp(menu->sym->name, sym->name)) + return menu; + } + for (child = menu->list; child; child = child->next) { + ret = do_search(child, sym); + if (ret) + return ret; + } + return NULL; +} + +static void search_conf(char *search_str) +{ + struct symbol *sym; + struct menu *menu[32] = { 0 }; + int i, j, k; + FILE *fp; + bool hit = false; + + fp = fopen(".search.tmp", "w"); + if (fp == NULL) { + perror("fopen"); + return; + } + for_all_symbols(i, sym) { + if (sym->name && strstr(sym->name, search_str)) { + struct menu *submenu = do_search(&rootmenu, sym); + + j = 0; + while (submenu) { + menu[j++] = submenu; + submenu = submenu->parent; + } + if (j > 0) { + if (sym->prop && sym->prop->text) + fprintf(fp, "%s (%s)\n", + sym->prop->text, sym->name); + else + fprintf(fp, "%s\n", sym->name); + } + for (k = j-2; k > 0; k--) { + const char *prompt; + + if (!hit) + hit = true; + submenu = menu[k]; + prompt = menu_get_prompt(submenu); + if (submenu->sym) + fprintf(fp, " -> %s (%s)\n", + prompt, submenu->sym->name); + else + fprintf(fp, " -> %s\n", prompt); + } + if (hit) + fprintf(fp, "\n"); + } + } + if (!hit) + fprintf(fp, "No matches found."); + fclose(fp); + + do { + cprint_init(); + cprint("--title"); + cprint("Search Results"); + cprint("--textbox"); + cprint(".search.tmp"); + cprint("%d", rows); + cprint("%d", cols); + } while (exec_conf() < 0); + unlink(".search.tmp"); +} + static void build_conf(struct menu *menu) { struct symbol *sym; @@ -463,6 +546,25 @@ static void conf(struct menu *menu) cprint(" Save Configuration to an Alternate File"); } stat = exec_conf(); + if (stat == 26) { + char *search_str; + + if (!strlen(input_buf)) + continue; + search_str = malloc(sizeof(char)*sizeof(input_buf)); + if (search_str == NULL) { + perror("malloc"); + continue; + } + /* Capitalizing the string */ + for (i = 0; input_buf[i]; i++) + search_str[i] = toupper(input_buf[i]); + search_str[i] = '\0'; + /* Searching and displaying all matches */ + search_conf(search_str); + free(search_str); + continue; + } if (stat < 0) continue; --- linux-2.6.9-rc1/scripts/lxdialog/menubox.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/scripts/lxdialog/menubox.c 2004-09-03 00:57:12.845086512 -0700 @@ -276,6 +276,15 @@ dialog_menu (const char *title, const ch while (key != ESC) { key = wgetch(menu); + if ( key == '/' ) { + int ret = dialog_inputbox("Search Configuration Parameter", + "Enter Keyword", height, width, + (char *) NULL); + if (ret == 0) { + fprintf(stderr, "%s", dialog_input_result); + return 26; + } + } if (key < 256 && isalpha(key)) key = tolower(key); --- linux-2.6.9-rc1/scripts/Makefile.host 2004-08-24 00:03:31.000000000 -0700 +++ 25/scripts/Makefile.host 2004-09-02 20:51:53.000000000 -0700 @@ -73,6 +73,7 @@ host-cxxmulti := $(addprefix $(obj)/,$(h host-cxxobjs := $(addprefix $(obj)/,$(host-cxxobjs)) host-cshlib := $(addprefix $(obj)/,$(host-cshlib)) host-cshobjs := $(addprefix $(obj)/,$(host-cshobjs)) +obj-dirs := $(addprefix $(obj)/,$(obj-dirs)) ##### # Handle options to gcc. Support building with separate output directory --- linux-2.6.9-rc1/scripts/mod/modpost.c 2004-08-24 00:02:22.000000000 -0700 +++ 25/scripts/mod/modpost.c 2004-09-03 00:57:27.231899384 -0700 @@ -376,6 +376,10 @@ handle_modversions(struct module *mod, s add_exported_symbol(symname + strlen(KSYMTAB_PFX), mod, NULL); } + if (strcmp(symname, MODULE_SYMBOL_PREFIX "init_module") == 0) + mod->has_init = 1; + if (strcmp(symname, MODULE_SYMBOL_PREFIX "cleanup_module") == 0) + mod->has_cleanup = 1; break; } } @@ -410,9 +414,6 @@ read_symbols(char *modname) if (is_vmlinux(modname)) { unsigned int fake_crc = 0; have_vmlinux = 1; - /* May not have this if !CONFIG_MODULE_UNLOAD: fake it. - If it appears, we'll get the real CRC. */ - add_exported_symbol("cleanup_module", mod, &fake_crc); add_exported_symbol("struct_module", mod, &fake_crc); mod->skip = 1; } @@ -431,14 +432,8 @@ read_symbols(char *modname) * never passed as an argument to an exported function, so * the automatic versioning doesn't pick it up, but it's really * important anyhow */ - if (modversions) { + if (modversions) mod->unres = alloc_symbol("struct_module", mod->unres); - - /* Always version init_module and cleanup_module, in - * case module doesn't have its own. */ - mod->unres = alloc_symbol("init_module", mod->unres); - mod->unres = alloc_symbol("cleanup_module", mod->unres); - } } #define SZ 500 @@ -479,7 +474,7 @@ buf_write(struct buffer *buf, const char /* Header for the generated file */ void -add_header(struct buffer *b) +add_header(struct buffer *b, struct module *mod) { buf_printf(b, "#include \n"); buf_printf(b, "#include \n"); @@ -491,10 +486,12 @@ add_header(struct buffer *b) buf_printf(b, "struct module __this_module\n"); buf_printf(b, "__attribute__((section(\".gnu.linkonce.this_module\"))) = {\n"); buf_printf(b, " .name = __stringify(KBUILD_MODNAME),\n"); - buf_printf(b, " .init = init_module,\n"); - buf_printf(b, "#ifdef CONFIG_MODULE_UNLOAD\n"); - buf_printf(b, " .exit = cleanup_module,\n"); - buf_printf(b, "#endif\n"); + if (mod->has_init) + buf_printf(b, " .init = init_module,\n"); + if (mod->has_cleanup) + buf_printf(b, "#ifdef CONFIG_MODULE_UNLOAD\n" + " .exit = cleanup_module,\n" + "#endif\n"); buf_printf(b, "};\n"); } @@ -723,7 +720,7 @@ main(int argc, char **argv) buf.pos = 0; - add_header(&buf); + add_header(&buf, mod); add_versions(&buf, mod); add_depends(&buf, mod, modules); add_moddevtable(&buf, mod); --- linux-2.6.9-rc1/scripts/mod/modpost.h 2004-08-24 00:01:50.000000000 -0700 +++ 25/scripts/mod/modpost.h 2004-09-03 00:57:27.232899232 -0700 @@ -74,6 +74,8 @@ struct module { struct symbol *unres; int seen; int skip; + int has_init; + int has_cleanup; struct buffer dev_table_buf; }; --- linux-2.6.9-rc1/scripts/mod/sumversion.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/scripts/mod/sumversion.c 2004-09-02 20:51:53.000000000 -0700 @@ -1,4 +1,4 @@ -#include +#include #include #include #include --- linux-2.6.9-rc1/security/commoncap.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/security/commoncap.c 2004-09-03 00:57:15.126739648 -0700 @@ -357,6 +357,11 @@ int cap_vm_enough_memory(long pages) allowed = (totalram_pages - hugetlb_total_pages()) * sysctl_overcommit_ratio / 100; + /* + * Leave the last 3% for root + */ + if (!capable(CAP_SYS_ADMIN)) + allowed -= allowed / 32; allowed += total_swap_pages; if (atomic_read(&vm_committed_space) < allowed) --- linux-2.6.9-rc1/security/Kconfig 2004-08-24 00:03:18.000000000 -0700 +++ 25/security/Kconfig 2004-09-03 00:56:54.700844856 -0700 @@ -4,6 +4,35 @@ menu "Security options" +config KEYS + bool "Enable access key retention support" + help + This option provides support for retaining authentication tokens and + access keys in the kernel. + + It also includes provision of methods by which such keys might be + associated with a process so that network filesystems, encryption + support and the like can find them. + + Furthermore, a special type of key is available that acts as keyring: + a searchable sequence of keys. Each process is equipped with access + to five standard keyrings: UID-specific, GID-specific, session, + process and thread. + + If you are unsure as to whether this is required, answer N. + +config KEYS_DEBUG_PROC_KEYS + bool "Enable the /proc/keys file by which all keys may be viewed" + depends on KEYS + help + This option turns on support for the /proc/keys file through which + all the keys on the system can be listed. + + This option is a slight security risk in that it makes it possible + for anyone to see all the keys on the system. Normally the manager + pretends keys that are inaccessible to a process don't exist as far + as that process is concerned. + config SECURITY bool "Enable different security models" help @@ -26,14 +55,14 @@ config SECURITY_NETWORK config SECURITY_CAPABILITIES tristate "Default Linux Capabilities" - depends on SECURITY!=n + depends on SECURITY help This enables the "default" Linux capabilities functionality. If you are unsure how to answer this question, answer Y. config SECURITY_ROOTPLUG tristate "Root Plug Support" - depends on USB && SECURITY!=n + depends on USB && SECURITY help This is a sample LSM module that should only be used as such. It prevents any programs running with egid == 0 if a specific --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/security/keys/internal.h 2004-09-03 00:56:55.119781168 -0700 @@ -0,0 +1,109 @@ +/* internal.h: authentication token and access key management internal defs + * + * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _INTERNAL_H +#define _INTERNAL_H + +#include +#include + +extern struct key_type key_type_dead; +extern struct key_type key_type_user; + +/*****************************************************************************/ +/* + * keep track of keys for a user + * - this needs to be separate to user_struct to avoid a refcount-loop + * (user_struct pins some keyrings which pin this struct) + * - this also keeps track of keys under request from userspace for this UID + */ +struct key_user { + struct rb_node node; + struct list_head consq; /* construction queue */ + spinlock_t lock; + atomic_t usage; /* for accessing qnkeys & qnbytes */ + atomic_t nkeys; /* number of keys */ + atomic_t nikeys; /* number of instantiated keys */ + uid_t uid; + int qnkeys; /* number of keys allocated to this user */ + int qnbytes; /* number of bytes allocated to this user */ +}; + +#define KEYQUOTA_MAX_KEYS 100 +#define KEYQUOTA_MAX_BYTES 10000 +#define KEYQUOTA_LINK_BYTES 4 /* a link in a keyring is worth 4 bytes */ + +extern struct rb_root key_user_tree; +extern spinlock_t key_user_lock; +extern struct key_user root_key_user; + +extern struct key_user *key_user_lookup(uid_t uid); +extern void key_user_put(struct key_user *user); + + + +extern struct rb_root key_serial_tree; +extern spinlock_t key_serial_lock; +extern struct semaphore key_alloc_sem; +extern struct rw_semaphore key_construction_sem; +extern wait_queue_head_t request_key_conswq; + + +extern void keyring_publish_name(struct key *keyring); + +extern int __key_link(struct key *keyring, struct key *key); + +extern struct key *__keyring_search_one(struct key *keyring, + const struct key_type *type, + const char *description, + key_perm_t perm); + +typedef int (*key_match_func_t)(const struct key *, const void *); + +extern struct key *keyring_search_aux(struct key *keyring, + struct key_type *type, + const void *description, + key_match_func_t match); + +extern struct key *search_process_keyrings_aux(struct key_type *type, + const void *description, + key_match_func_t match); + +extern struct key *find_keyring_by_name(const char *name, key_serial_t bound); + +extern int install_thread_keyring(struct task_struct *tsk); + + +/* + * debugging key validation + */ +#ifdef KEY_DEBUGGING +static void __key_check(const struct key *key) +{ + printk("__key_check: key %p {%08x} should be {%08x}\n", + key, key->magic, KEY_DEBUG_MAGIC); + BUG(); +} + + +static inline void key_check(const struct key *key) +{ + if (key && (IS_ERR(key) || key->magic != KEY_DEBUG_MAGIC)) + __key_check(key); +} + +#else + +#define key_check(key) do {} while(0) + +#endif + +#endif /* _INTERNAL_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/security/keys/key.c 2004-09-03 00:56:54.959805488 -0700 @@ -0,0 +1,1039 @@ +/* key.c: basic authentication token and access key management + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include "internal.h" + +static kmem_cache_t *key_jar; +static key_serial_t key_serial_next = 3; +struct rb_root key_serial_tree; /* tree of keys indexed by serial */ +spinlock_t key_serial_lock = SPIN_LOCK_UNLOCKED; + +struct rb_root key_user_tree; /* tree of quota records indexed by UID */ +spinlock_t key_user_lock = SPIN_LOCK_UNLOCKED; + +static LIST_HEAD(key_types_list); +static DECLARE_RWSEM(key_types_sem); + +static void key_cleanup(void *data); +static DECLARE_WORK(key_cleanup_task, key_cleanup, NULL); + +/* we serialise key instantiation and link */ +DECLARE_RWSEM(key_construction_sem); + +/* any key who's type gets unegistered will be re-typed to this */ +struct key_type key_type_dead = { + .name = "dead", +}; + +/*****************************************************************************/ +/* + * get the key quota record for a user, allocating a new record if one doesn't + * already exist + */ +struct key_user *key_user_lookup(uid_t uid) +{ + struct key_user *candidate = NULL, *user; + struct rb_node *parent = NULL; + struct rb_node **p = &key_user_tree.rb_node; + + try_again: + spin_lock(&key_user_lock); + + /* search the tree for a user record with a matching UID */ + while (*p) { + parent = *p; + user = rb_entry(parent, struct key_user, node); + + if (uid < user->uid) + p = &(*p)->rb_left; + else if (uid > user->uid) + p = &(*p)->rb_right; + else + goto found; + } + + /* if we get here, we failed to find a match in the tree */ + if (!candidate) { + /* allocate a candidate user record if we don't already have + * one */ + spin_unlock(&key_user_lock); + + user = NULL; + candidate = kmalloc(sizeof(struct key_user), GFP_KERNEL); + if (unlikely(!candidate)) + goto out; + + /* the allocation may have scheduled, so we need to repeat the + * search lest someone else added the record whilst we were + * asleep */ + goto try_again; + } + + /* if we get here, then the user record still hadn't appeared on the + * second pass - so we use the candidate record */ + atomic_set(&candidate->usage, 1); + atomic_set(&candidate->nkeys, 0); + atomic_set(&candidate->nikeys, 0); + candidate->uid = uid; + candidate->qnkeys = 0; + candidate->qnbytes = 0; + spin_lock_init(&candidate->lock); + INIT_LIST_HEAD(&candidate->consq); + + rb_link_node(&candidate->node, parent, p); + rb_insert_color(&candidate->node, &key_user_tree); + spin_unlock(&key_user_lock); + user = candidate; + goto out; + + /* okay - we found a user record for this UID */ + found: + atomic_inc(&user->usage); + spin_unlock(&key_user_lock); + if (candidate) + kfree(candidate); + out: + return user; + +} /* end key_user_lookup() */ + +/*****************************************************************************/ +/* + * dispose of a user structure + */ +void key_user_put(struct key_user *user) +{ + if (atomic_dec_and_lock(&user->usage, &key_user_lock)) { + rb_erase(&user->node, &key_user_tree); + spin_unlock(&key_user_lock); + + kfree(user); + } + +} /* end key_user_put() */ + +/*****************************************************************************/ +/* + * insert a key with a fixed serial number + */ +static void __init __key_insert_serial(struct key *key) +{ + struct rb_node *parent, **p; + struct key *xkey; + + parent = NULL; + p = &key_serial_tree.rb_node; + + while (*p) { + parent = *p; + xkey = rb_entry(parent, struct key, serial_node); + + if (key->serial < xkey->serial) + p = &(*p)->rb_left; + else if (key->serial > xkey->serial) + p = &(*p)->rb_right; + else + BUG(); + } + + /* we've found a suitable hole - arrange for this key to occupy it */ + rb_link_node(&key->serial_node, parent, p); + rb_insert_color(&key->serial_node, &key_serial_tree); + +} /* end __key_insert_serial() */ + +/*****************************************************************************/ +/* + * assign a key the next unique serial number + * - we work through all the serial numbers between 2 and 2^31-1 in turn and + * then wrap + */ +static inline void key_alloc_serial(struct key *key) +{ + struct rb_node *parent, **p; + struct key *xkey; + + spin_lock(&key_serial_lock); + + /* propose a likely serial number and look for a hole for it in the + * serial number tree */ + key->serial = key_serial_next; + if (key->serial < 3) + key->serial = 3; + key_serial_next = key->serial + 1; + + parent = NULL; + p = &key_serial_tree.rb_node; + + while (*p) { + parent = *p; + xkey = rb_entry(parent, struct key, serial_node); + + if (key->serial < xkey->serial) + p = &(*p)->rb_left; + else if (key->serial > xkey->serial) + p = &(*p)->rb_right; + else + goto serial_exists; + } + goto insert_here; + + /* we found a key with the proposed serial number - walk the tree from + * that point looking for the next unused serial number */ + serial_exists: + for (;;) { + key->serial = key_serial_next; + if (key->serial < 2) + key->serial = 2; + key_serial_next = key->serial + 1; + + if (!parent->rb_parent) + p = &key_serial_tree.rb_node; + else if (parent->rb_parent->rb_left == parent) + p = &parent->rb_parent->rb_left; + else + p = &parent->rb_parent->rb_right; + + parent = rb_next(parent); + if (!parent) + break; + + xkey = rb_entry(parent, struct key, serial_node); + if (key->serial < xkey->serial) + goto insert_here; + } + + /* we've found a suitable hole - arrange for this key to occupy it */ + insert_here: + rb_link_node(&key->serial_node, parent, p); + rb_insert_color(&key->serial_node, &key_serial_tree); + + spin_unlock(&key_serial_lock); + +} /* end key_alloc_serial() */ + +/*****************************************************************************/ +/* + * allocate a key of the specified type + * - update the user's quota to reflect the existence of the key + * - called from a key-type operation with key_types_sem read-locked by either + * key_create_or_update() or by key_duplicate(); this prevents unregistration + * of the key type + * - upon return the key is as yet uninstantiated; the caller needs to either + * instantiate the key or discard it before returning + */ +struct key *key_alloc(struct key_type *type, const char *desc, + uid_t uid, gid_t gid, key_perm_t perm, + int not_in_quota) +{ + struct key_user *user = NULL; + struct key *key; + size_t desclen, quotalen; + + key = ERR_PTR(-EINVAL); + if (!desc || !*desc) + goto error; + + desclen = strlen(desc) + 1; + quotalen = desclen + type->def_datalen; + + /* get hold of the key tracking for this user */ + user = key_user_lookup(uid); + if (!user) + goto no_memory_1; + + /* check that the user's quota permits allocation of another key and + * its description */ + if (!not_in_quota) { + spin_lock(&user->lock); + if (user->qnkeys + 1 >= KEYQUOTA_MAX_KEYS && + user->qnbytes + quotalen >= KEYQUOTA_MAX_BYTES + ) + goto no_quota; + + user->qnkeys++; + user->qnbytes += quotalen; + spin_unlock(&user->lock); + } + + /* allocate and initialise the key and its description */ + key = kmem_cache_alloc(key_jar, SLAB_KERNEL); + if (!key) + goto no_memory_2; + + if (desc) { + key->description = kmalloc(desclen, GFP_KERNEL); + if (!key->description) + goto no_memory_3; + + memcpy(key->description, desc, desclen); + } + + atomic_set(&key->usage, 1); + rwlock_init(&key->lock); + init_rwsem(&key->sem); + key->type = type; + key->user = user; + key->quotalen = quotalen; + key->datalen = type->def_datalen; + key->uid = uid; + key->gid = gid; + key->perm = perm; + key->flags = 0; + key->expiry = 0; + key->payload.data = NULL; + + if (!not_in_quota) + key->flags |= KEY_FLAG_IN_QUOTA; + + memset(&key->type_data, 0, sizeof(key->type_data)); + +#ifdef KEY_DEBUGGING + key->magic = KEY_DEBUG_MAGIC; +#endif + + /* publish the key by giving it a serial number */ + atomic_inc(&user->nkeys); + key_alloc_serial(key); + + error: + return key; + + no_memory_3: + kmem_cache_free(key_jar, key); + no_memory_2: + if (!not_in_quota) { + spin_lock(&user->lock); + user->qnkeys--; + user->qnbytes -= quotalen; + spin_unlock(&user->lock); + } + key_user_put(user); + no_memory_1: + key = ERR_PTR(-ENOMEM); + goto error; + + no_quota: + spin_unlock(&user->lock); + key_user_put(user); + key = ERR_PTR(-EDQUOT); + goto error; + +} /* end key_alloc() */ + +EXPORT_SYMBOL(key_alloc); + +/*****************************************************************************/ +/* + * reserve an amount of quota for the key's payload + */ +int key_payload_reserve(struct key *key, size_t datalen) +{ + int delta = (int) datalen - key->datalen; + int ret = 0; + + key_check(key); + + /* contemplate the quota adjustment */ + if (delta != 0 && key->flags & KEY_FLAG_IN_QUOTA) { + spin_lock(&key->user->lock); + + if (delta > 0 && + key->user->qnbytes + delta > KEYQUOTA_MAX_BYTES + ) { + ret = -EDQUOT; + } + else { + key->user->qnbytes += delta; + key->quotalen += delta; + } + spin_unlock(&key->user->lock); + } + + /* change the recorded data length if that didn't generate an error */ + if (ret == 0) + key->datalen = datalen; + + return ret; + +} /* end key_payload_reserve() */ + +EXPORT_SYMBOL(key_payload_reserve); + +/*****************************************************************************/ +/* + * instantiate a key and link it into the target keyring atomically + * - called with the target keyring's semaphore writelocked + */ +static int __key_instantiate_and_link(struct key *key, + const void *data, + size_t datalen, + struct key *keyring) +{ + int ret, awaken; + + key_check(key); + key_check(keyring); + + awaken = 0; + ret = -EBUSY; + + down_write(&key_construction_sem); + + /* can't instantiate twice */ + if (!(key->flags & KEY_FLAG_INSTANTIATED)) { + /* instantiate the key */ + ret = key->type->instantiate(key, data, datalen); + + if (ret == 0) { + /* mark the key as being instantiated */ + write_lock(&key->lock); + + atomic_inc(&key->user->nikeys); + key->flags |= KEY_FLAG_INSTANTIATED; + + if (key->flags & KEY_FLAG_USER_CONSTRUCT) { + key->flags &= ~KEY_FLAG_USER_CONSTRUCT; + awaken = 1; + } + + write_unlock(&key->lock); + + /* and link it into the destination keyring */ + if (keyring) + ret = __key_link(keyring, key); + } + } + + up_write(&key_construction_sem); + + /* wake up anyone waiting for a key to be constructed */ + if (awaken) + wake_up_all(&request_key_conswq); + + return ret; + +} /* end __key_instantiate_and_link() */ + +/*****************************************************************************/ +/* + * instantiate a key and link it into the target keyring atomically + */ +int key_instantiate_and_link(struct key *key, + const void *data, + size_t datalen, + struct key *keyring) +{ + int ret; + + if (keyring) + down_write(&keyring->sem); + + ret = __key_instantiate_and_link(key, data, datalen, keyring); + + if (keyring) + up_write(&keyring->sem); + + return ret; +} /* end key_instantiate_and_link() */ + +EXPORT_SYMBOL(key_instantiate_and_link); + +/*****************************************************************************/ +/* + * negatively instantiate a key and link it into the target keyring atomically + */ +int key_negate_and_link(struct key *key, + unsigned timeout, + struct key *keyring) +{ + struct timespec now; + int ret, awaken; + + key_check(key); + key_check(keyring); + + awaken = 0; + ret = -EBUSY; + + if (keyring) + down_write(&keyring->sem); + + down_write(&key_construction_sem); + + /* can't instantiate twice */ + if (!(key->flags & KEY_FLAG_INSTANTIATED)) { + /* mark the key as being negatively instantiated */ + write_lock(&key->lock); + + atomic_inc(&key->user->nikeys); + key->flags |= KEY_FLAG_INSTANTIATED | KEY_FLAG_NEGATIVE; + now = current_kernel_time(); + key->expiry = now.tv_sec + timeout; + + if (key->flags & KEY_FLAG_USER_CONSTRUCT) { + key->flags &= ~KEY_FLAG_USER_CONSTRUCT; + awaken = 1; + } + + write_unlock(&key->lock); + ret = 0; + + /* and link it into the destination keyring */ + if (keyring) + ret = __key_link(keyring, key); + } + + up_write(&key_construction_sem); + + if (keyring) + up_write(&keyring->sem); + + /* wake up anyone waiting for a key to be constructed */ + if (awaken) + wake_up_all(&request_key_conswq); + + return ret; + +} /* end key_negate_and_link() */ + +EXPORT_SYMBOL(key_negate_and_link); + +/*****************************************************************************/ +/* + * do cleaning up in process context so that we don't have to disable + * interrupts all over the place + */ +static void key_cleanup(void *data) +{ + struct rb_node *_n; + struct key *key; + + go_again: + /* look for a dead key in the tree */ + spin_lock(&key_serial_lock); + + for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) { + key = rb_entry(_n, struct key, serial_node); + + if (atomic_read(&key->usage) == 0) + goto found_dead_key; + } + + spin_unlock(&key_serial_lock); + return; + + found_dead_key: + /* we found a dead key - once we've removed it from the tree, we can + * drop the lock */ + rb_erase(&key->serial_node, &key_serial_tree); + spin_unlock(&key_serial_lock); + + /* deal with the user's key tracking and quota */ + if (key->flags & KEY_FLAG_IN_QUOTA) { + spin_lock(&key->user->lock); + key->user->qnkeys--; + key->user->qnbytes -= key->quotalen; + spin_unlock(&key->user->lock); + } + + atomic_dec(&key->user->nkeys); + if (key->flags & KEY_FLAG_INSTANTIATED) + atomic_dec(&key->user->nikeys); + + key_user_put(key->user); + + /* now throw away the key memory */ + if (key->type->destroy) + key->type->destroy(key); + + kfree(key->description); + +#ifdef KEY_DEBUGGING + key->magic = KEY_DEBUG_MAGIC_X; +#endif + kmem_cache_free(key_jar, key); + + /* there may, of course, be more than one key to destroy */ + goto go_again; + +} /* end key_cleanup() */ + +/*****************************************************************************/ +/* + * dispose of a reference to a key + * - when all the references are gone, we schedule the cleanup task to come and + * pull it out of the tree in definite process context + */ +void key_put(struct key *key) +{ + if (key) { + key_check(key); + + if (atomic_dec_and_test(&key->usage)) + schedule_work(&key_cleanup_task); + } + +} /* end key_put() */ + +EXPORT_SYMBOL(key_put); + +/*****************************************************************************/ +/* + * find a key by its serial number + */ +struct key *key_lookup(key_serial_t id) +{ + struct rb_node *n; + struct key *key; + + spin_lock(&key_serial_lock); + + /* search the tree for the specified key */ + n = key_serial_tree.rb_node; + while (n) { + key = rb_entry(n, struct key, serial_node); + + if (id < key->serial) + n = n->rb_left; + else if (id > key->serial) + n = n->rb_right; + else + goto found; + } + + spin_unlock(&key_serial_lock); + + not_found: + key = ERR_PTR(-ENOKEY); + goto error; + + found: + /* pretent doesn't exist if it's dead */ + if (atomic_read(&key->usage) == 0 || + (key->flags & KEY_FLAG_DEAD) || + key->type == &key_type_dead) + goto not_found; + + /* this races with key_put(), but that doesn't matter since key_put() + * doesn't actually change the key + */ + atomic_inc(&key->usage); + + spin_unlock(&key_serial_lock); + error: + return key; + +} /* end key_lookup() */ + +/*****************************************************************************/ +/* + * find and lock the specified key type against removal + * - we return with the sem readlocked + */ +struct key_type *key_type_lookup(const char *type) +{ + struct key_type *ktype; + + down_read(&key_types_sem); + + /* look up the key type to see if it's one of the registered kernel + * types */ + list_for_each_entry(ktype, &key_types_list, link) { + if (strcmp(ktype->name, type) == 0) + goto found_kernel_type; + } + + up_read(&key_types_sem); + ktype = ERR_PTR(-ENOKEY); + + found_kernel_type: + return ktype; + +} /* end key_type_lookup() */ + +/*****************************************************************************/ +/* + * unlock a key type + */ +void key_type_put(struct key_type *ktype) +{ + up_read(&key_types_sem); + +} /* end key_type_put() */ + +/*****************************************************************************/ +/* + * attempt to update an existing key + * - the key has an incremented refcount + * - we need to put the key if we get an error + */ +static inline struct key *__key_update(struct key *key, const void *payload, + size_t plen) +{ + int ret; + + /* need write permission on the key to update it */ + ret = -EACCES; + if (!key_permission(key, KEY_WRITE)) + goto error; + + ret = -EEXIST; + if (!key->type->update) + goto error; + + down_write(&key->sem); + + ret = key->type->update(key, payload, plen); + + if (ret == 0) { + /* updating a negative key instantiates it */ + write_lock(&key->lock); + key->flags &= ~KEY_FLAG_NEGATIVE; + write_unlock(&key->lock); + } + + up_write(&key->sem); + + if (ret < 0) + goto error; + out: + return key; + + error: + key_put(key); + key = ERR_PTR(ret); + goto out; + +} /* end __key_update() */ + +/*****************************************************************************/ +/* + * search the specified keyring for a key of the same description; if one is + * found, update it, otherwise add a new one + */ +struct key *key_create_or_update(struct key *keyring, + const char *type, + const char *description, + const void *payload, + size_t plen, + int not_in_quota) +{ + struct key_type *ktype; + struct key *key = NULL; + key_perm_t perm; + int ret; + + key_check(keyring); + + /* look up the key type to see if it's one of the registered kernel + * types */ + ktype = key_type_lookup(type); + if (IS_ERR(ktype)) { + key = ERR_PTR(PTR_ERR(ktype)); + goto error; + } + + ret = -EINVAL; + if (!ktype->match || !ktype->instantiate) + goto error_2; + + /* search for an existing key of the same type and description in the + * destination keyring + */ + down_write(&keyring->sem); + + key = __keyring_search_one(keyring, ktype, description, 0); + if (!IS_ERR(key)) + goto found_matching_key; + + /* if we're going to allocate a new key, we're going to have to modify + * the keyring */ + ret = -EACCES; + if (!key_permission(keyring, KEY_WRITE)) + goto error_3; + + /* decide on the permissions we want */ + perm = KEY_USR_VIEW | KEY_USR_SEARCH | KEY_USR_LINK; + + if (ktype->read) + perm |= KEY_USR_READ; + + if (ktype == &key_type_keyring || ktype->update) + perm |= KEY_USR_WRITE; + + /* allocate a new key */ + key = key_alloc(ktype, description, current->fsuid, current->fsgid, + perm, not_in_quota); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error_3; + } + + /* instantiate it and link it into the target keyring */ + ret = __key_instantiate_and_link(key, payload, plen, keyring); + if (ret < 0) { + key_put(key); + key = ERR_PTR(ret); + } + + error_3: + up_write(&keyring->sem); + error_2: + key_type_put(ktype); + error: + return key; + + found_matching_key: + /* we found a matching key, so we're going to try to update it + * - we can drop the locks first as we have the key pinned + */ + up_write(&keyring->sem); + key_type_put(ktype); + + key = __key_update(key, payload, plen); + goto error; + +} /* end key_create_or_update() */ + +EXPORT_SYMBOL(key_create_or_update); + +/*****************************************************************************/ +/* + * update a key + */ +int key_update(struct key *key, const void *payload, size_t plen) +{ + int ret; + + key_check(key); + + /* the key must be writable */ + ret = -EACCES; + if (!key_permission(key, KEY_WRITE)) + goto error; + + /* attempt to update it if supported */ + ret = -EOPNOTSUPP; + if (key->type->update) { + down_write(&key->sem); + ret = key->type->update(key, payload, plen); + + if (ret == 0) { + /* updating a negative key instantiates it */ + write_lock(&key->lock); + key->flags &= ~KEY_FLAG_NEGATIVE; + write_unlock(&key->lock); + } + + up_write(&key->sem); + } + + error: + return ret; + +} /* end key_update() */ + +EXPORT_SYMBOL(key_update); + +/*****************************************************************************/ +/* + * duplicate a key, potentially with a revised description + * - must be supported by the keytype (keyrings for instance can be duplicated) + */ +struct key *key_duplicate(struct key *source, const char *desc) +{ + struct key *key; + int ret; + + key_check(source); + + if (!desc) + desc = source->description; + + down_read(&key_types_sem); + + ret = -EINVAL; + if (!source->type->duplicate) + goto error; + + /* allocate and instantiate a key */ + key = key_alloc(source->type, desc, current->fsuid, current->fsgid, + source->perm, 0); + if (IS_ERR(key)) + goto error_k; + + down_read(&source->sem); + ret = key->type->duplicate(key, source); + up_read(&source->sem); + if (ret < 0) + goto error2; + + atomic_inc(&key->user->nikeys); + + write_lock(&key->lock); + key->flags |= KEY_FLAG_INSTANTIATED; + write_unlock(&key->lock); + + error_k: + up_read(&key_types_sem); + out: + return key; + + error2: + key_put(key); + error: + up_read(&key_types_sem); + key = ERR_PTR(ret); + goto out; + +} /* end key_duplicate() */ + +/*****************************************************************************/ +/* + * revoke a key + */ +void key_revoke(struct key *key) +{ + key_check(key); + + /* make sure no one's trying to change or use the key when we mark + * it */ + down_write(&key->sem); + write_lock(&key->lock); + key->flags |= KEY_FLAG_REVOKED; + write_unlock(&key->lock); + up_write(&key->sem); + +} /* end key_revoke() */ + +EXPORT_SYMBOL(key_revoke); + +/*****************************************************************************/ +/* + * register a type of key + */ +int register_key_type(struct key_type *ktype) +{ + struct key_type *p; + int ret; + + ret = -EEXIST; + down_write(&key_types_sem); + + /* disallow key types with the same name */ + list_for_each_entry(p, &key_types_list, link) { + if (strcmp(p->name, ktype->name) == 0) + goto out; + } + + /* store the type */ + list_add(&ktype->link, &key_types_list); + ret = 0; + + out: + up_write(&key_types_sem); + return ret; + +} /* end register_key_type() */ + +EXPORT_SYMBOL(register_key_type); + +/*****************************************************************************/ +/* + * unregister a type of key + */ +void unregister_key_type(struct key_type *ktype) +{ + struct rb_node *_n; + struct key *key; + + down_write(&key_types_sem); + + /* withdraw the key type */ + list_del_init(&ktype->link); + + /* need to withdraw all keys of this type */ + spin_lock(&key_serial_lock); + + for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) { + key = rb_entry(_n, struct key, serial_node); + + if (key->type != ktype) + continue; + + write_lock(&key->lock); + key->type = &key_type_dead; + write_unlock(&key->lock); + + /* there shouldn't be anyone looking at the description or + * payload now */ + if (ktype->destroy) + ktype->destroy(key); + memset(&key->payload, 0xbd, sizeof(key->payload)); + } + + spin_unlock(&key_serial_lock); + up_write(&key_types_sem); + +} /* end unregister_key_type() */ + +EXPORT_SYMBOL(unregister_key_type); + +/*****************************************************************************/ +/* + * initialise the key management stuff + */ +static int __init key_init(void) +{ + /* allocate a slab in which we can store keys */ + key_jar = kmem_cache_create("key_jar", sizeof(struct key), + 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + if (!key_jar) + panic("Cannot create key jar\n"); + + /* add the special key types */ + list_add_tail(&key_type_keyring.link, &key_types_list); + list_add_tail(&key_type_dead.link, &key_types_list); + list_add_tail(&key_type_user.link, &key_types_list); + + /* record the root user tracking */ + rb_link_node(&root_key_user.node, + NULL, + &key_user_tree.rb_node); + + rb_insert_color(&root_key_user.node, + &key_user_tree); + + /* record root's user standard keyrings */ + key_check(&root_user_keyring); + key_check(&root_session_keyring); + + __key_insert_serial(&root_user_keyring); + __key_insert_serial(&root_session_keyring); + + keyring_publish_name(&root_user_keyring); + keyring_publish_name(&root_session_keyring); + + /* link the two root keyrings together */ + key_link(&root_session_keyring, &root_user_keyring); + + return 0; + +} /* end key_init() */ + +subsys_initcall(key_init); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/security/keys/keyctl.c 2004-09-03 00:56:55.754684648 -0700 @@ -0,0 +1,946 @@ +/* keyctl.c: userspace keyctl operations + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +/*****************************************************************************/ +/* + * extract the description of a new key from userspace and either add it as a + * new key to the specified keyring or update a matching key in that keyring + * - the keyring must be writable + * - returns the new key's serial number + * - implements keyctl(KEYCTL_NEW) + */ +static long user_add_key(key_serial_t ringid, + const char __user *_type, + const char __user *_description, + const void __user *_payload) +{ + struct key *keyring, *key; + size_t plen; + char type[32], *description; + void *payload; + long dlen, ret; + + /* draw all the data into kernel space */ + ret = strncpy_from_user(type, _type, sizeof(type) - 1); + if (ret < 0) + goto error; + type[31] = '\0'; + + ret = -EFAULT; + dlen = strnlen_user(_description, PAGE_SIZE - 1); + if (dlen <= 0) + goto error; + + ret = -EINVAL; + if (dlen > PAGE_SIZE - 1) + goto error; + + ret = -ENOMEM; + description = kmalloc(dlen + 1, GFP_KERNEL); + if (!description) + goto error; + + ret = -EFAULT; + if (copy_from_user(description, _description, dlen + 1) != 0) + goto error2; + + /* pull the payload in if one was supplied */ + payload = NULL; + plen = 0; + + if (_payload) { + ret = get_user(plen, (uint16_t *) _payload); + if (ret < 0) + goto error2; + _payload += 2; + + ret = -EINVAL; + if (plen < 0 || plen > PAGE_SIZE) + goto error2; + + ret = -ENOMEM; + payload = (void *) get_zeroed_page(GFP_KERNEL); + if (!payload) + goto error2; + + ret = -EFAULT; + if (copy_from_user(payload, _payload, plen) != 0) + goto error3; + } + + /* find the target keyring (which must be writable) */ + keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto error3; + } + + /* create or update the requested key and add it to the target + * keyring */ + key = key_create_or_update(keyring, type, description, + payload, plen, 0); + if (!IS_ERR(key)) { + ret = key->serial; + key_put(key); + } + else { + ret = PTR_ERR(key); + } + + key_put(keyring); + error3: + free_page((unsigned long) payload); + error2: + kfree(description); + error: + return ret; + +} /* end user_add_key() */ + +/*****************************************************************************/ +/* + * update a key's data payload + * - the key must be writable + * - implements keyctl(KEYCTL_UPDATE) + */ +static long user_update_key(key_serial_t id, const void __user *_payload) +{ + struct key *key; + size_t plen; + void *payload; + long ret; + + /* pull the payload in if one was supplied */ + payload = NULL; + plen = 0; + + if (_payload) { + ret = get_user(plen, (uint16_t *) _payload); + if (ret < 0) + goto error; + _payload += 2; + + ret = -EINVAL; + if (plen < 0 || plen > PAGE_SIZE) + goto error; + + ret = -ENOMEM; + payload = (void *) get_zeroed_page(GFP_KERNEL); + if (!payload) + goto error; + + ret = -EFAULT; + if (copy_from_user(payload, _payload, plen) != 0) + goto error2; + } + + /* find the target key (which must be writable) */ + key = lookup_user_key(id, 0, 0, KEY_WRITE); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error2; + } + + /* update the key */ + ret = key_update(key, payload, plen); + + key_put(key); + error2: + free_page((unsigned long) payload); + error: + return ret; + +} /* end user_update_key() */ + +/*****************************************************************************/ +/* + * revoke a key + * - the key must be writable + * - implements keyctl(KEYCTL_REVOKE) + */ +static long user_revoke_key(key_serial_t id) +{ + struct key *key; + long ret; + + key = lookup_user_key(id, 0, 0, KEY_WRITE); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error; + } + + key_revoke(key); + ret = 0; + + key_put(key); + error: + return 0; + +} /* end user_revoke_key() */ + +/*****************************************************************************/ +/* + * clear the specified process keyring + * - the keyring must be writable + * - implements keyctl(KEYCTL_CLEAR) + */ +static long user_keyring_clear(key_serial_t ringid) +{ + struct key *keyring; + long ret; + + keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto error; + } + + ret = keyring_clear(keyring); + + key_put(keyring); + error: + return ret; + +} /* end user_keyring_clear() */ + +/*****************************************************************************/ +/* + * link a key into a keyring + * - the keyring must be writable + * - the key must be linkable + * - implements keyctl(KEYCTL_LINK) + */ +static long user_keyring_link(key_serial_t ringid, key_serial_t id) +{ + struct key *keyring, *key; + long ret; + + keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto error; + } + + key = lookup_user_key(id, 1, 0, KEY_LINK); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error2; + } + + ret = key_link(keyring, key); + + key_put(key); + error2: + key_put(keyring); + error: + return ret; + +} /* end user_keyring_link() */ + +/*****************************************************************************/ +/* + * unlink the first attachment of a key from a keyring + * - the keyring must be writable + * - we don't need any permissions on the key + * - implements keyctl(KEYCTL_UNLINK) + */ +static long user_keyring_unlink(key_serial_t ringid, key_serial_t id) +{ + struct key *keyring, *key; + long ret; + + keyring = lookup_user_key(ringid, 0, 0, KEY_WRITE); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto error; + } + + key = lookup_user_key(id, 0, 0, 0); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error2; + } + + ret = key_unlink(keyring, key); + + key_put(key); + error2: + key_put(keyring); + error: + return ret; + +} /* end user_keyring_unlink() */ + +/*****************************************************************************/ +/* + * describe a user key + * - the key must have view permission + * - if there's a buffer, we place up to buflen bytes of data into it + * - unless there's an error, we return the amount of description available, + * irrespective of how much we may have copied + * - the description is formatted thus: + * type;uid;gid;perm;description + * - implements keyctl(KEYCTL_DESCRIBE) + */ +static long user_describe_key(key_serial_t keyid, + char __user *buffer, + size_t buflen) +{ + struct key *key; + char *tmpbuf; + long ret; + + key = lookup_user_key(keyid, 0, 1, KEY_VIEW); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error; + } + + /* calculate how much description we're going to return */ + ret = -ENOMEM; + tmpbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!tmpbuf) + goto error2; + + ret = snprintf(tmpbuf, PAGE_SIZE - 1, + "%s;%d;%d;%06x;%s", + key->type->name, + key->uid, + key->gid, + key->perm, + key->description ? key->description :"" + ); + + /* include a NUL char at the end of the data */ + if (ret > PAGE_SIZE - 1) + ret = PAGE_SIZE - 1; + tmpbuf[ret] = 0; + ret++; + + /* consider returning the data */ + if (buffer && buflen > 0) { + if (buflen > ret) + buflen = ret; + + if (copy_to_user(buffer, tmpbuf, buflen) != 0) + ret = -EFAULT; + } + + kfree(tmpbuf); + error2: + key_put(key); + error: + return ret; + +} /* end user_describe_key() */ + +/*****************************************************************************/ +/* + * search the specified keyring for a matching key + * - the start keyring must be searchable + * - nested keyrings may also be searched if they are searchable + * - only keys with search permission may be found + * - if a key is found, it will be attached to the destination keyring if + * there's one specified + * - implements keyctl(KEYCTL_SEARCH) + */ +static long user_keyring_search(key_serial_t ringid, + const char __user *_type, + const char __user *_description, + key_serial_t destringid) +{ + struct key_type *ktype; + struct key *keyring, *key, *dest; + char type[32], *description; + long dlen, ret; + + /* pull the type and description into kernel space */ + ret = strncpy_from_user(type, _type, sizeof(type) - 1); + if (ret < 0) + goto error; + type[31] = '\0'; + + ret = -EFAULT; + dlen = strnlen_user(_description, PAGE_SIZE - 1); + if (dlen <= 0) + goto error; + + ret = -EINVAL; + if (dlen > PAGE_SIZE - 1) + goto error; + + ret = -ENOMEM; + description = kmalloc(dlen + 1, GFP_KERNEL); + if (!description) + goto error; + + ret = -EFAULT; + if (copy_from_user(description, _description, dlen + 1) != 0) + goto error2; + + /* get the keyring at which to begin the search */ + keyring = lookup_user_key(ringid, 0, 0, KEY_SEARCH); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto error2; + } + + /* get the destination keyring if specified */ + dest = NULL; + if (destringid) { + dest = lookup_user_key(destringid, 1, 0, KEY_WRITE); + if (IS_ERR(dest)) { + ret = PTR_ERR(dest); + goto error3; + } + } + + /* find the key type */ + ktype = key_type_lookup(type); + if (IS_ERR(ktype)) { + ret = PTR_ERR(ktype); + goto error4; + } + + /* do the search */ + key = keyring_search(keyring, ktype, description); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + + /* treat lack or presence of a negative key the same */ + if (ret == -EAGAIN) + ret = -ENOKEY; + goto error5; + } + + /* link the resulting key to the destination keyring if we can */ + if (dest) { + ret = -EACCES; + if (!key_permission(key, KEY_LINK)) + goto error6; + + ret = key_link(dest, key); + if (ret < 0) + goto error6; + } + + ret = key->serial; + + error6: + key_put(key); + error5: + key_type_put(ktype); + error4: + key_put(dest); + error3: + key_put(keyring); + error2: + kfree(description); + error: + return ret; + +} /* end user_keyring_search() */ + +/*****************************************************************************/ +/* + * see if the key we're looking at is the target key + */ +static int user_read_key_same(const struct key *key, const void *target) +{ + return key == target; + +} /* end user_read_key_same() */ + +/*****************************************************************************/ +/* + * read a user key's payload + * - the keyring must be readable or the key must be searchable from the + * process's keyrings + * - if there's a buffer, we place up to buflen bytes of data into it + * - unless there's an error, we return the amount of data in the key, + * irrespective of how much we may have copied + * - implements keyctl(KEYCTL_READ) + */ +static long user_read_key(key_serial_t keyid, + char __user *buffer, + size_t buflen) +{ + struct key *key, *skey; + long ret; + + /* find the key first */ + key = lookup_user_key(keyid, 0, 0, 0); + if (!IS_ERR(key)) { + /* see if we can read it directly */ + if (key_permission(key, KEY_READ)) + goto can_read_key; + + /* can't; see if it's searchable from this process's + * keyrings */ + ret = -ENOKEY; + if (key_permission(key, KEY_SEARCH)) { + /* okay - we do have search permission on the key + * itself, but do we have the key? */ + skey = search_process_keyrings_aux(key->type, key, + user_read_key_same); + if (!IS_ERR(skey)) + goto can_read_key2; + } + + goto error2; + } + + ret = -ENOKEY; + goto error; + + /* the key is probably readable - now try to read it */ + can_read_key2: + key_put(skey); + can_read_key: + ret = key_validate(key); + if (ret == 0) { + ret = -EOPNOTSUPP; + if (key->type->read) { + /* read the data with the semaphore held (since we + * might sleep) */ + down_read(&key->sem); + ret = key->type->read(key, buffer, buflen); + up_read(&key->sem); + } + } + + error2: + key_put(key); + error: + return ret; + +} /* end user_read_key() */ + +/*****************************************************************************/ +/* + * change the ownership of a key + * - the keyring owned by the changer + * - if the uid or gid is -1, then that parameter is not changed + * - implements keyctl(KEYCTL_CHOWN) + */ +static long user_chown_key(key_serial_t id, uid_t uid, gid_t gid) +{ + struct key *key; + long ret; + + ret = 0; + if (uid == (uid_t) -1 && gid == (gid_t) -1) + goto error; + + key = lookup_user_key(id, 1, 1, 0); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error; + } + + /* make the changes with the locks held to prevent chown/chown races */ + ret = -EACCES; + down_write(&key->sem); + write_lock(&key->lock); + + if (!capable(CAP_SYS_ADMIN)) { + /* only the sysadmin can chown a key to some other UID */ + if (uid != (uid_t) -1 && key->uid != uid) + goto no_access; + + /* only the sysadmin can set the key's GID to a group other + * than one of those that the current process subscribes to */ + if (gid != (gid_t) -1 && gid != key->gid && !in_group_p(gid)) + goto no_access; + } + + /* change the UID (have to update the quotas) */ + if (uid != (uid_t) -1 && uid != key->uid) { + /* don't support UID changing yet */ + ret = -EOPNOTSUPP; + goto no_access; + } + + /* change the GID */ + if (gid != (gid_t) -1) + key->gid = gid; + + ret = 0; + + no_access: + write_unlock(&key->lock); + up_write(&key->sem); + key_put(key); + error: + return ret; + +} /* end user_chown_key() */ + +/*****************************************************************************/ +/* + * change the permission mask on a key + * - the keyring owned by the changer + * - implements keyctl(KEYCTL_SETPERM) + */ +static long user_setperm_key(key_serial_t id, key_perm_t perm) +{ + struct key *key; + long ret; + + ret = -EINVAL; + if (perm & ~(KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL)) + goto error; + + key = lookup_user_key(id, 1, 1, 0); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error; + } + + /* make the changes with the locks held to prevent chown/chmod + * races */ + ret = -EACCES; + down_write(&key->sem); + write_lock(&key->lock); + + /* if we're not the sysadmin, we can only chmod a key that we + * own */ + if (!capable(CAP_SYS_ADMIN) && key->uid != current->fsuid) + goto no_access; + + /* changing the permissions mask */ + key->perm = perm; + ret = 0; + + no_access: + write_unlock(&key->lock); + up_write(&key->sem); + key_put(key); + error: + return ret; + +} /* end user_setperm_key() */ + +/*****************************************************************************/ +/* + * search the process keyrings for a matching key + * - nested keyrings may also be searched if they have Search permission + * - if a key is found, it will be attached to the destination keyring if + * there's one specified + * - /sbin/request-key will be invoked if _callout_info is non-NULL + * - the _callout_info string will be passed to /sbin/request-key + * - if the _callout_info string is empty, it will be rendered as "-" + * - implements keyctl(KEYCTL_REQUEST_KEY) + */ +static long user_request_key(const char __user *_type, + const char __user *_description, + const char __user *_callout_info, + key_serial_t destringid) +{ + struct key_type *ktype; + struct key *key, *dest; + char type[32], *description, *callout_info; + long dlen, ret; + + /* pull the type into kernel space */ + ret = strncpy_from_user(type, _type, sizeof(type) - 1); + if (ret < 0) + goto error; + type[31] = '\0'; + + /* pull the description into kernel space */ + ret = -EFAULT; + dlen = strnlen_user(_description, PAGE_SIZE - 1); + if (dlen <= 0) + goto error; + + ret = -EINVAL; + if (dlen > PAGE_SIZE - 1) + goto error; + + ret = -ENOMEM; + description = kmalloc(dlen + 1, GFP_KERNEL); + if (!description) + goto error; + + ret = -EFAULT; + if (copy_from_user(description, _description, dlen + 1) != 0) + goto error2; + + /* pull the callout info into kernel space */ + callout_info = NULL; + if (_callout_info) { + ret = -EFAULT; + dlen = strnlen_user(_callout_info, PAGE_SIZE - 1); + if (dlen <= 0) + goto error2; + + ret = -EINVAL; + if (dlen > PAGE_SIZE - 1) + goto error2; + + ret = -ENOMEM; + callout_info = kmalloc(dlen + 1, GFP_KERNEL); + if (!callout_info) + goto error2; + + ret = -EFAULT; + if (copy_from_user(callout_info, _callout_info, dlen + 1) != 0) + goto error3; + } + + /* get the destination keyring if specified */ + dest = NULL; + if (destringid) { + dest = lookup_user_key(destringid, 1, 0, KEY_WRITE); + if (IS_ERR(dest)) { + ret = PTR_ERR(dest); + goto error3; + } + } + + /* find the key type */ + ktype = key_type_lookup(type); + if (IS_ERR(ktype)) { + ret = PTR_ERR(ktype); + goto error4; + } + + /* do the search */ + key = request_key(ktype, description, callout_info); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error5; + } + + /* link the resulting key to the destination keyring */ + if (dest) { + ret = key_link(dest, key); + if (ret < 0) + goto error6; + } + + ret = key->serial; + + error6: + key_put(key); + error5: + key_type_put(ktype); + error4: + key_put(dest); + error3: + kfree(callout_info); + error2: + kfree(description); + error: + return ret; + +} /* end user_request_key() */ + +/*****************************************************************************/ +/* + * instantiate the key with the specified payload, and, if one is given, link + * the key into the keyring + */ +static long user_instantiate_key(key_serial_t id, + const void __user *_payload, + key_serial_t ringid) +{ + struct key *key, *keyring; + size_t plen; + void *payload; + long ret; + + /* pull the payload in if one was supplied */ + payload = NULL; + plen = 0; + + if (_payload) { + ret = get_user(plen, (uint16_t *) _payload); + if (ret < 0) + goto error; + _payload += 2; + + ret = -EINVAL; + if (plen < 0 || plen > PAGE_SIZE) + goto error; + + ret = -ENOMEM; + payload = (void *) get_zeroed_page(GFP_KERNEL); + if (!payload) + goto error; + + ret = -EFAULT; + if (copy_from_user(payload, _payload, plen) != 0) + goto error2; + } + + /* find the target key (which must be writable) */ + key = lookup_user_key(id, 0, 1, KEY_WRITE); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error2; + } + + /* find the destination keyring if present (which must also be + * writable) */ + keyring = NULL; + if (ringid) { + keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto error3; + } + } + + /* instantiate the key and link it into a keyring */ + ret = key_instantiate_and_link(key, payload, plen, keyring); + + key_put(keyring); + error3: + key_put(key); + error2: + free_page((unsigned long) payload); + error: + return ret; + +} /* end user_instantiate_key() */ + +/*****************************************************************************/ +/* + * negatively instantiate the key with the given timeout (in seconds), and, if + * one is given, link the key into the keyring + */ +static long user_negate_key(key_serial_t id, + unsigned timeout, + key_serial_t ringid) +{ + struct key *key, *keyring; + long ret; + + /* find the target key (which must be writable) */ + key = lookup_user_key(id, 0, 1, KEY_WRITE); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error; + } + + /* find the destination keyring if present (which must also be + * writable) */ + keyring = NULL; + if (ringid) { + keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto error2; + } + } + + /* instantiate the key and link it into a keyring */ + ret = key_negate_and_link(key, timeout, keyring); + + key_put(keyring); + error2: + key_put(key); + error: + return ret; + +} /* end user_negate_key() */ + +/*****************************************************************************/ +/* + * the key control system call + * - currently invoked through prctl() + */ +asmlinkage long sys_keyctl(int option, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5) +{ + switch (option) { + case KEYCTL_NEW: + return user_add_key((key_serial_t) arg2, + (const char __user *) arg3, + (const char __user *) arg4, + (const void __user *) arg5); + + case KEYCTL_UPDATE: + return user_update_key((key_serial_t) arg2, + (const void __user *) arg3); + + case KEYCTL_REVOKE: + return user_revoke_key((key_serial_t) arg2); + + case KEYCTL_DESCRIBE: + return user_describe_key((key_serial_t) arg2, + (char __user *) arg3, + (unsigned) arg4); + + case KEYCTL_CLEAR: + return user_keyring_clear((key_serial_t) arg2); + + case KEYCTL_LINK: + return user_keyring_link((key_serial_t) arg2, + (key_serial_t) arg3); + + case KEYCTL_UNLINK: + return user_keyring_unlink((key_serial_t) arg2, + (key_serial_t) arg3); + + case KEYCTL_SEARCH: + return user_keyring_search((key_serial_t) arg2, + (const char __user *) arg3, + (const char __user *) arg4, + (key_serial_t) arg5); + + case KEYCTL_READ: + return user_read_key((key_serial_t) arg2, + (char __user *) arg3, + (size_t) arg4); + + case KEYCTL_CHOWN: + return user_chown_key((key_serial_t) arg2, + (uid_t) arg3, + (gid_t) arg4); + + case KEYCTL_SETPERM: + return user_setperm_key((key_serial_t) arg2, + (key_perm_t) arg3); + + case KEYCTL_REQUEST_KEY: + return user_request_key((const char __user *) arg2, + (const char __user *) arg3, + (const char __user *) arg4, + (key_serial_t) arg5); + + case KEYCTL_INSTANTIATE: + return user_instantiate_key((key_serial_t) arg2, + (const void __user *) arg3, + (key_serial_t) arg4); + + case KEYCTL_NEGATE: + return user_negate_key((key_serial_t) arg2, + (unsigned) arg3, + (key_serial_t) arg4); + + default: + return -EINVAL; + } + +} /* end sys_keyctl() */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/security/keys/keyring.c 2004-09-03 00:56:55.122780712 -0700 @@ -0,0 +1,895 @@ +/* keyring.c: keyring handling + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +/* + * when plumbing the depths of the key tree, this sets a hard limit set on how + * deep we're willing to go + */ +#define KEYRING_SEARCH_MAX_DEPTH 6 + +/* + * we keep all named keyrings in a hash to speed looking them up + */ +#define KEYRING_NAME_HASH_SIZE (1 << 5) + +static struct list_head keyring_name_hash[KEYRING_NAME_HASH_SIZE]; +static rwlock_t keyring_name_lock = RW_LOCK_UNLOCKED; + +static inline unsigned keyring_hash(const char *desc) +{ + unsigned bucket = 0; + + for (; *desc; desc++) + bucket += (unsigned char) *desc; + + return bucket & (KEYRING_NAME_HASH_SIZE - 1); +} + +/* + * the keyring type definition + */ +static int keyring_instantiate(struct key *keyring, + const void *data, size_t datalen); +static int keyring_duplicate(struct key *keyring, const struct key *source); +static int keyring_match(const struct key *keyring, const void *criterion); +static void keyring_destroy(struct key *keyring); +static void keyring_describe(const struct key *keyring, struct seq_file *m); +static long keyring_read(const struct key *keyring, + char __user *buffer, size_t buflen); + +struct key_type key_type_keyring = { + .name = "keyring", + .def_datalen = sizeof(struct keyring_list), + .instantiate = keyring_instantiate, + .duplicate = keyring_duplicate, + .match = keyring_match, + .destroy = keyring_destroy, + .describe = keyring_describe, + .read = keyring_read, +}; + +/* + * semaphore to serialise link/link calls to prevent two link calls in parallel + * introducing a cycle + */ +DECLARE_RWSEM(keyring_serialise_link_sem); + +/*****************************************************************************/ +/* + * publish the name of a keyring so that it can be found by name (if it has + * one) + */ +void keyring_publish_name(struct key *keyring) +{ + int bucket; + + if (keyring->description) { + bucket = keyring_hash(keyring->description); + + write_lock(&keyring_name_lock); + + if (!keyring_name_hash[bucket].next) + INIT_LIST_HEAD(&keyring_name_hash[bucket]); + + list_add_tail(&keyring->type_data.link, + &keyring_name_hash[bucket]); + + write_unlock(&keyring_name_lock); + } + +} /* end keyring_publish_name() */ + +/*****************************************************************************/ +/* + * initialise a keyring + * - we object if we were given any data + */ +static int keyring_instantiate(struct key *keyring, + const void *data, size_t datalen) +{ + int ret; + + ret = -EINVAL; + if (datalen == 0) { + /* make the keyring available by name if it has one */ + keyring_publish_name(keyring); + ret = 0; + } + + return ret; + +} /* end keyring_instantiate() */ + +/*****************************************************************************/ +/* + * duplicate the list of subscribed keys from a source keyring into this one + */ +static int keyring_duplicate(struct key *keyring, const struct key *source) +{ + struct keyring_list *sklist, *klist; + unsigned max; + size_t size; + int loop, ret; + + const unsigned limit = + (PAGE_SIZE - sizeof(*klist)) / sizeof(struct key); + + ret = 0; + sklist = source->payload.subscriptions; + + if (sklist && sklist->nkeys > 0) { + max = sklist->nkeys; + BUG_ON(max > limit); + + max = (max + 3) & ~3; + if (max > limit) + max = limit; + + ret = -ENOMEM; + size = sizeof(*klist) + sizeof(struct key) * max; + klist = kmalloc(size, GFP_KERNEL); + if (!klist) + goto error; + + klist->maxkeys = max; + klist->nkeys = sklist->nkeys; + memcpy(klist->keys, + sklist->keys, + sklist->nkeys * sizeof(struct key)); + + for (loop = klist->nkeys - 1; loop >= 0; loop--) + atomic_inc(&klist->keys[loop]->usage); + + keyring->payload.subscriptions = klist; + ret = 0; + } + + error: + return ret; + +} /* end keyring_duplicate() */ + +/*****************************************************************************/ +/* + * match keyrings on their name + */ +static int keyring_match(const struct key *keyring, const void *description) +{ + return keyring->description && + strcmp(keyring->description, description) == 0; + +} /* end keyring_match() */ + +/*****************************************************************************/ +/* + * dispose of the data dangling from the corpse of a keyring + */ +static void keyring_destroy(struct key *keyring) +{ + struct keyring_list *klist; + int loop; + + if (keyring->description) { + write_lock(&keyring_name_lock); + list_del(&keyring->type_data.link); + write_unlock(&keyring_name_lock); + } + + klist = keyring->payload.subscriptions; + if (klist) { + for (loop = klist->nkeys - 1; loop >= 0; loop--) + key_put(klist->keys[loop]); + kfree(klist); + } + +} /* end keyring_destroy() */ + +/*****************************************************************************/ +/* + * describe the keyring + */ +static void keyring_describe(const struct key *keyring, struct seq_file *m) +{ + struct keyring_list *klist; + + if (keyring->description) { + seq_puts(m, keyring->description); + } + else { + seq_puts(m, "[anon]"); + } + + klist = keyring->payload.subscriptions; + if (klist) + seq_printf(m, ": %u/%u", klist->nkeys, klist->maxkeys); + else + seq_puts(m, ": empty"); + +} /* end keyring_describe() */ + +/*****************************************************************************/ +/* + * read a list of key IDs from the keyring's contents + */ +static long keyring_read(const struct key *keyring, + char __user *buffer, size_t buflen) +{ + struct keyring_list *klist; + struct key *key; + size_t qty, tmp; + int loop, ret; + + ret = 0; + klist = keyring->payload.subscriptions; + + if (klist) { + /* calculate how much data we could return */ + qty = klist->nkeys * sizeof(key_serial_t); + + if (buffer && buflen > 0) { + if (buflen > qty) + buflen = qty; + + /* copy the IDs of the subscribed keys into the + * buffer */ + ret = -EFAULT; + + for (loop = 0; loop < klist->nkeys; loop++) { + key = klist->keys[loop]; + + tmp = sizeof(key_serial_t); + if (tmp > buflen) + tmp = buflen; + + if (copy_to_user(buffer, + &key->serial, + tmp) != 0) + goto error; + + buflen -= tmp; + if (buflen == 0) + break; + buffer += tmp; + } + } + + ret = qty; + } + + error: + return ret; + +} /* end keyring_read() */ + +/*****************************************************************************/ +/* + * allocate a keyring and link into the destination keyring + */ +struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, + int not_in_quota, struct key *dest) +{ + struct key *keyring; + int ret; + + keyring = key_alloc(&key_type_keyring, description, + uid, gid, KEY_USR_ALL, not_in_quota); + + if (!IS_ERR(keyring)) { + ret = key_instantiate_and_link(keyring, NULL, 0, dest); + if (ret < 0) { + key_put(keyring); + keyring = ERR_PTR(ret); + } + } + + return keyring; + +} /* end keyring_alloc() */ + +/*****************************************************************************/ +/* + * search the supplied keyring tree for a key that matches the criterion + * - perform a breadth-then-depth search up to the prescribed limit + * - we only find keys on which we have search permission + * - we use the supplied match function to see if the description (or other + * feature of interest) matches + * - we readlock the keyrings as we search down the tree + * - we return -EAGAIN if we didn't find any matching key + * - we return -ENOKEY if we only found negative matching keys + */ +struct key *keyring_search_aux(struct key *keyring, + struct key_type *type, + const void *description, + key_match_func_t match) +{ + struct { + struct key *keyring; + int kix; + } stack[KEYRING_SEARCH_MAX_DEPTH]; + + struct keyring_list *keylist; + struct timespec now; + struct key *key; + long err; + int sp, psp, kix; + + key_check(keyring); + + /* top keyring must have search permission to begin the search */ + key = ERR_PTR(-EACCES); + if (!key_permission(keyring, KEY_SEARCH)) + goto error; + + key = ERR_PTR(-ENOTDIR); + if (keyring->type != &key_type_keyring) + goto error; + + now = current_kernel_time(); + err = -EAGAIN; + sp = 0; + + /* start processing a new keyring */ + descend: + read_lock(&keyring->lock); + if (keyring->flags & KEY_FLAG_REVOKED) + goto not_this_keyring; + + keylist = keyring->payload.subscriptions; + if (!keylist) + goto not_this_keyring; + + /* iterate through the keys in this keyring first */ + for (kix = 0; kix < keylist->nkeys; kix++) { + key = keylist->keys[kix]; + + /* ignore keys not of this type */ + if (key->type != type) + continue; + + /* skip revoked keys and expired keys */ + if (key->flags & KEY_FLAG_REVOKED) + continue; + + if (key->expiry && now.tv_sec >= key->expiry) + continue; + + /* keys that don't match */ + if (!match(key, description)) + continue; + + /* key must have search permissions */ + if (!key_permission(key, KEY_SEARCH)) + continue; + + /* we set a different error code if we find a negative key */ + if (key->flags & KEY_FLAG_NEGATIVE) { + err = -ENOKEY; + continue; + } + + goto found; + } + + /* search through the keyrings nested in this one */ + kix = 0; + ascend: + while (kix < keylist->nkeys) { + key = keylist->keys[kix]; + if (key->type != &key_type_keyring) + goto next; + + /* recursively search nested keyrings + * - only search keyrings for which we have search permission + */ + if (sp >= KEYRING_SEARCH_MAX_DEPTH) + goto next; + + if (!key_permission(key, KEY_SEARCH)) + goto next; + + /* evade loops in the keyring tree */ + for (psp = 0; psp < sp; psp++) + if (stack[psp].keyring == keyring) + goto next; + + /* stack the current position */ + stack[sp].keyring = keyring; + stack[sp].kix = kix; + sp++; + + /* begin again with the new keyring */ + keyring = key; + goto descend; + + next: + kix++; + } + + /* the keyring we're looking at was disqualified or didn't contain a + * matching key */ + not_this_keyring: + read_unlock(&keyring->lock); + + if (sp > 0) { + /* resume the processing of a keyring higher up in the tree */ + sp--; + keyring = stack[sp].keyring; + keylist = keyring->payload.subscriptions; + kix = stack[sp].kix + 1; + goto ascend; + } + + key = ERR_PTR(err); + goto error; + + /* we found a viable match */ + found: + atomic_inc(&key->usage); + read_unlock(&keyring->lock); + + /* unwind the keyring stack */ + while (sp > 0) { + sp--; + read_unlock(&stack[sp].keyring->lock); + } + + key_check(key); + error: + return key; + +} /* end keyring_search_aux() */ + +/*****************************************************************************/ +/* + * search the supplied keyring tree for a key that matches the criterion + * - perform a breadth-then-depth search up to the prescribed limit + * - we only find keys on which we have search permission + * - we readlock the keyrings as we search down the tree + * - we return -EAGAIN if we didn't find any matching key + * - we return -ENOKEY if we only found negative matching keys + */ +struct key *keyring_search(struct key *keyring, + struct key_type *type, + const char *description) +{ + return keyring_search_aux(keyring, type, description, type->match); + +} /* end keyring_search() */ + +EXPORT_SYMBOL(keyring_search); + +/*****************************************************************************/ +/* + * search the given keyring only (no recursion) + * - keyring must be locked by caller + */ +struct key *__keyring_search_one(struct key *keyring, + const struct key_type *ktype, + const char *description, + key_perm_t perm) +{ + struct keyring_list *klist; + struct key *key; + int loop; + + klist = keyring->payload.subscriptions; + if (klist) { + for (loop = 0; loop < klist->nkeys; loop++) { + key = klist->keys[loop]; + + if (key->type == ktype && + key->type->match(key, description) && + key_permission(key, perm) && + !(key->flags & KEY_FLAG_REVOKED) + ) + goto found; + } + } + + key = ERR_PTR(-ENOKEY); + goto error; + + found: + atomic_inc(&key->usage); + error: + return key; + +} /* end __keyring_search_one() */ + +/*****************************************************************************/ +/* + * find a keyring with the specified name + * - all named keyrings are searched + * - only find keyrings with search permission for the process + * - only find keyrings with a serial number greater than the one specified + */ +struct key *find_keyring_by_name(const char *name, key_serial_t bound) +{ + struct key *keyring; + int bucket; + + keyring = ERR_PTR(-EINVAL); + if (!name) + goto error; + + bucket = keyring_hash(name); + + read_lock(&keyring_name_lock); + + if (keyring_name_hash[bucket].next) { + /* search this hash bucket for a keyring with a matching name + * that's readable and that hasn't been revoked */ + list_for_each_entry(keyring, + &keyring_name_hash[bucket], + type_data.link + ) { + if (keyring->flags & KEY_FLAG_REVOKED) + continue; + + if (strcmp(keyring->description, name) != 0) + continue; + + if (!key_permission(keyring, KEY_SEARCH)) + continue; + + /* found a potential candidate, but we still need to + * check the serial number */ + if (keyring->serial <= bound) + continue; + + /* we've got a match */ + atomic_inc(&keyring->usage); + read_unlock(&keyring_name_lock); + goto error; + } + } + + read_unlock(&keyring_name_lock); + keyring = ERR_PTR(-ENOKEY); + + error: + return keyring; + +} /* end find_keyring_by_name() */ + +/*****************************************************************************/ +/* + * see if a cycle will will be created by inserting acyclic tree B in acyclic + * tree A at the topmost level (ie: as a direct child of A) + * - since we are adding B to A at the top level, checking for cycles should + * just be a matter of seeing if node A is somewhere in tree B + */ +static int keyring_detect_cycle(struct key *A, struct key *B) +{ + struct { + struct key *subtree; + int kix; + } stack[KEYRING_SEARCH_MAX_DEPTH]; + + struct keyring_list *keylist; + struct key *subtree, *key; + int sp, kix, ret; + + ret = -EDEADLK; + if (A == B) + goto error; + + subtree = B; + sp = 0; + + /* start processing a new keyring */ + descend: + read_lock(&subtree->lock); + if (subtree->flags & KEY_FLAG_REVOKED) + goto not_this_keyring; + + keylist = subtree->payload.subscriptions; + if (!keylist) + goto not_this_keyring; + kix = 0; + + ascend: + /* iterate through the remaining keys in this keyring */ + for (; kix < keylist->nkeys; kix++) { + key = keylist->keys[kix]; + + if (key == A) + goto cycle_detected; + + /* recursively check nested keyrings */ + if (key->type == &key_type_keyring) { + if (sp >= KEYRING_SEARCH_MAX_DEPTH) + goto too_deep; + + /* stack the current position */ + stack[sp].subtree = subtree; + stack[sp].kix = kix; + sp++; + + /* begin again with the new keyring */ + subtree = key; + goto descend; + } + } + + /* the keyring we're looking at was disqualified or didn't contain a + * matching key */ + not_this_keyring: + read_unlock(&subtree->lock); + + if (sp > 0) { + /* resume the checking of a keyring higher up in the tree */ + sp--; + subtree = stack[sp].subtree; + keylist = subtree->payload.subscriptions; + kix = stack[sp].kix + 1; + goto ascend; + } + + ret = 0; /* no cycles detected */ + + error: + return ret; + + too_deep: + ret = -ELOOP; + goto error_unwind; + cycle_detected: + ret = -EDEADLK; + error_unwind: + read_unlock(&subtree->lock); + + /* unwind the keyring stack */ + while (sp > 0) { + sp--; + read_unlock(&stack[sp].subtree->lock); + } + + goto error; + +} /* end keyring_detect_cycle() */ + +/*****************************************************************************/ +/* + * link a key into to a keyring + * - must be called with the keyring's semaphore held + */ +int __key_link(struct key *keyring, struct key *key) +{ + struct keyring_list *klist, *nklist; + unsigned max; + size_t size; + int ret; + + ret = -EKEYREVOKED; + if (keyring->flags & KEY_FLAG_REVOKED) + goto error; + + ret = -ENOTDIR; + if (keyring->type != &key_type_keyring) + goto error; + + /* serialise link/link calls to prevent parallel calls causing a + * cycle when applied to two keyring in opposite orders */ + down_write(&keyring_serialise_link_sem); + + /* check that we aren't going to create a cycle adding one keyring to + * another */ + if (key->type == &key_type_keyring) { + ret = keyring_detect_cycle(keyring, key); + if (ret < 0) + goto error2; + } + + /* check that we aren't going to overrun the user's quota */ + ret = key_payload_reserve(keyring, + keyring->datalen + KEYQUOTA_LINK_BYTES); + if (ret < 0) + goto error2; + + klist = keyring->payload.subscriptions; + + if (klist && klist->nkeys < klist->maxkeys) { + /* there's sufficient slack space to add directly */ + atomic_inc(&key->usage); + + write_lock(&keyring->lock); + klist->keys[klist->nkeys++] = key; + write_unlock(&keyring->lock); + + ret = 0; + } + else { + /* grow the key list */ + max = 4; + if (klist) + max += klist->maxkeys; + + ret = -ENFILE; + size = sizeof(*klist) + sizeof(*key) * max; + if (size > PAGE_SIZE) + goto error3; + + ret = -ENOMEM; + nklist = kmalloc(size, GFP_KERNEL); + if (!nklist) + goto error3; + nklist->maxkeys = max; + nklist->nkeys = 0; + + if (klist) { + nklist->nkeys = klist->nkeys; + memcpy(nklist->keys, + klist->keys, + sizeof(struct key *) * klist->nkeys); + } + + /* add the key into the new space */ + atomic_inc(&key->usage); + + write_lock(&keyring->lock); + keyring->payload.subscriptions = nklist; + nklist->keys[nklist->nkeys++] = key; + write_unlock(&keyring->lock); + + /* dispose of the old keyring list */ + kfree(klist); + + ret = 0; + } + + error2: + up_write(&keyring_serialise_link_sem); + error: + return ret; + + error3: + /* undo the quota changes */ + key_payload_reserve(keyring, + keyring->datalen - KEYQUOTA_LINK_BYTES); + goto error2; + +} /* end __key_link() */ + +/*****************************************************************************/ +/* + * link a key to a keyring + */ +int key_link(struct key *keyring, struct key *key) +{ + int ret; + + key_check(keyring); + key_check(key); + + down_write(&keyring->sem); + ret = __key_link(keyring, key); + up_write(&keyring->sem); + + return ret; + +} /* end key_link() */ + +EXPORT_SYMBOL(key_link); + +/*****************************************************************************/ +/* + * unlink the first link to a key from a keyring + */ +int key_unlink(struct key *keyring, struct key *key) +{ + struct keyring_list *klist; + int loop, ret; + + key_check(keyring); + key_check(key); + + ret = -ENOTDIR; + if (keyring->type != &key_type_keyring) + goto error; + + down_write(&keyring->sem); + + klist = keyring->payload.subscriptions; + if (klist) { + /* search the keyring for the key */ + for (loop = 0; loop < klist->nkeys; loop++) + if (klist->keys[loop] == key) + goto key_is_present; + } + + up_write(&keyring->sem); + ret = -ENOENT; + goto error; + + key_is_present: + /* adjust the user's quota */ + key_payload_reserve(keyring, + keyring->datalen - KEYQUOTA_LINK_BYTES); + + /* shuffle down the key pointers + * - it might be worth shrinking the allocated memory, but that runs + * the risk of ENOMEM as we would have to copy + */ + write_lock(&keyring->lock); + + klist->nkeys--; + if (loop < klist->nkeys) + memcpy(&klist->keys[loop], + &klist->keys[loop + 1], + (klist->nkeys - loop) * sizeof(struct key *)); + + write_unlock(&keyring->lock); + + up_write(&keyring->sem); + key_put(key); + ret = 0; + + error: + return ret; + +} /* end key_unlink() */ + +EXPORT_SYMBOL(key_unlink); + +/*****************************************************************************/ +/* + * clear the specified process keyring + * - implements keyctl(KEYCTL_CLEAR) + */ +int keyring_clear(struct key *keyring) +{ + struct keyring_list *klist; + int loop, ret; + + ret = -ENOTDIR; + if (keyring->type == &key_type_keyring) { + /* detach the pointer block with the locks held */ + down_write(&keyring->sem); + + klist = keyring->payload.subscriptions; + if (klist) { + /* adjust the quota */ + key_payload_reserve(keyring, + sizeof(struct keyring_list)); + + write_lock(&keyring->lock); + keyring->payload.subscriptions = NULL; + write_unlock(&keyring->lock); + } + + up_write(&keyring->sem); + + /* free the keys after the locks have been dropped */ + if (klist) { + for (loop = klist->nkeys - 1; loop >= 0; loop--) + key_put(klist->keys[loop]); + + kfree(klist); + } + + ret = 0; + } + + return ret; + +} /* end keyring_clear() */ + +EXPORT_SYMBOL(keyring_clear); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/security/keys/Makefile 2004-09-03 00:56:54.712843032 -0700 @@ -0,0 +1,13 @@ +# +# Makefile for key management +# + +obj-y := \ + key.o \ + keyring.o \ + keyctl.o \ + process_keys.o \ + user_defined.o \ + request_key.o + +obj-$(CONFIG_PROC_FS) += proc.o --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/security/keys/proc.c 2004-09-03 00:56:54.713842880 -0700 @@ -0,0 +1,251 @@ +/* proc.c: proc files for key database enumeration + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS +static int proc_keys_open(struct inode *inode, struct file *file); +static void *proc_keys_start(struct seq_file *p, loff_t *_pos); +static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos); +static void proc_keys_stop(struct seq_file *p, void *v); +static int proc_keys_show(struct seq_file *m, void *v); + +static struct seq_operations proc_keys_ops = { + .start = proc_keys_start, + .next = proc_keys_next, + .stop = proc_keys_stop, + .show = proc_keys_show, +}; + +static struct file_operations proc_keys_fops = { + .open = proc_keys_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif + +static int proc_key_users_open(struct inode *inode, struct file *file); +static void *proc_key_users_start(struct seq_file *p, loff_t *_pos); +static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos); +static void proc_key_users_stop(struct seq_file *p, void *v); +static int proc_key_users_show(struct seq_file *m, void *v); + +static struct seq_operations proc_key_users_ops = { + .start = proc_key_users_start, + .next = proc_key_users_next, + .stop = proc_key_users_stop, + .show = proc_key_users_show, +}; + +static struct file_operations proc_key_users_fops = { + .open = proc_key_users_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +/*****************************************************************************/ +/* + * declare the /proc files + */ +static int __init key_proc_init(void) +{ + struct proc_dir_entry *p; + +#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS + p = create_proc_entry("keys", 0, NULL); + if (!p) + panic("Cannot create /proc/keys\n"); + + p->proc_fops = &proc_keys_fops; +#endif + + p = create_proc_entry("key-users", 0, NULL); + if (!p) + panic("Cannot create /proc/key-users\n"); + + p->proc_fops = &proc_key_users_fops; + + return 0; + +} /* end key_proc_init() */ + +__initcall(key_proc_init); + +/*****************************************************************************/ +/* + * implement "/proc/keys" to provides a list of the keys on the system + */ +#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS + +static int proc_keys_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &proc_keys_ops); + +} + +static void *proc_keys_start(struct seq_file *p, loff_t *_pos) +{ + struct rb_node *_p; + loff_t pos = *_pos; + + spin_lock(&key_serial_lock); + + _p = rb_first(&key_serial_tree); + while (pos > 0 && _p) { + pos--; + _p = rb_next(_p); + } + + return _p; + +} + +static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos) +{ + (*_pos)++; + return rb_next((struct rb_node *) v); + +} + +static void proc_keys_stop(struct seq_file *p, void *v) +{ + spin_unlock(&key_serial_lock); +} + +static int proc_keys_show(struct seq_file *m, void *v) +{ + struct rb_tree *_p = v; + struct key *key = rb_entry(_p, struct key, serial_node); + struct timespec now; + unsigned long timo; + char xbuf[12]; + + now = current_kernel_time(); + + read_lock(&key->lock); + + /* come up with a suitable timeout value */ + if (key->expiry == 0) { + memcpy(xbuf, "perm", 5); + } + else if (now.tv_sec >= key->expiry) { + memcpy(xbuf, "expd", 5); + } + else { + timo = key->expiry - now.tv_sec; + + if (timo < 60) + sprintf(xbuf, "%lus", timo); + else if (timo < 60*60) + sprintf(xbuf, "%lum", timo / 60); + else if (timo < 60*60*24) + sprintf(xbuf, "%luh", timo / (60*60)); + else if (timo < 60*60*24*7) + sprintf(xbuf, "%lud", timo / (60*60*24)); + else + sprintf(xbuf, "%luw", timo / (60*60*24*7)); + } + + seq_printf(m, "%08x %c%c%c%c%c%c %5d %4s %06x %5d %5d %-9.9s ", + key->serial, + key->flags & KEY_FLAG_INSTANTIATED ? 'I' : '-', + key->flags & KEY_FLAG_REVOKED ? 'R' : '-', + key->flags & KEY_FLAG_DEAD ? 'D' : '-', + key->flags & KEY_FLAG_IN_QUOTA ? 'Q' : '-', + key->flags & KEY_FLAG_USER_CONSTRUCT ? 'U' : '-', + key->flags & KEY_FLAG_NEGATIVE ? 'N' : '-', + atomic_read(&key->usage), + xbuf, + key->perm, + key->uid, + key->gid, + key->type->name); + + if (key->type->describe) + key->type->describe(key, m); + seq_putc(m, '\n'); + + read_unlock(&key->lock); + + return 0; + +} + +#endif /* CONFIG_KEYS_DEBUG_PROC_KEYS */ + +/*****************************************************************************/ +/* + * implement "/proc/key-users" to provides a list of the key users + */ +static int proc_key_users_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &proc_key_users_ops); + +} + +static void *proc_key_users_start(struct seq_file *p, loff_t *_pos) +{ + struct rb_node *_p; + loff_t pos = *_pos; + + spin_lock(&key_user_lock); + + _p = rb_first(&key_user_tree); + while (pos > 0 && _p) { + pos--; + _p = rb_next(_p); + } + + return _p; + +} + +static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos) +{ + (*_pos)++; + return rb_next((struct rb_node *) v); + +} + +static void proc_key_users_stop(struct seq_file *p, void *v) +{ + spin_unlock(&key_user_lock); +} + +static int proc_key_users_show(struct seq_file *m, void *v) +{ + struct rb_tree *_p = v; + struct key_user *user = rb_entry(_p, struct key_user, node); + + seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n", + user->uid, + atomic_read(&user->usage), + atomic_read(&user->nkeys), + atomic_read(&user->nikeys), + user->qnkeys, + KEYQUOTA_MAX_KEYS, + user->qnbytes, + KEYQUOTA_MAX_BYTES + ); + + return 0; + +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/security/keys/process_keys.c 2004-09-03 00:56:55.755684496 -0700 @@ -0,0 +1,706 @@ +/* process_keys.c: management of a process's keyrings + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +/* session keyring create vs join semaphore */ +static DECLARE_MUTEX(key_session_sem); + +/* the root user's tracking struct */ +struct key_user root_key_user = { + .usage = ATOMIC_INIT(3), + .consq = LIST_HEAD_INIT(root_key_user.consq), + .lock = SPIN_LOCK_UNLOCKED, + .nkeys = ATOMIC_INIT(2), + .nikeys = ATOMIC_INIT(2), + .uid = 0, +}; + +/* the root user's UID keyring */ +struct key root_user_keyring = { + .usage = ATOMIC_INIT(1), + .serial = 2, + .type = &key_type_keyring, + .user = &root_key_user, + .lock = RW_LOCK_UNLOCKED, + .sem = __RWSEM_INITIALIZER(root_user_keyring.sem), + .perm = KEY_USR_ALL, + .flags = KEY_FLAG_INSTANTIATED, + .description = "_uid.0", +#ifdef KEY_DEBUGGING + .magic = KEY_DEBUG_MAGIC, +#endif +}; + +/* the root user's default session keyring */ +struct key root_session_keyring = { + .usage = ATOMIC_INIT(1), + .serial = 1, + .type = &key_type_keyring, + .user = &root_key_user, + .lock = RW_LOCK_UNLOCKED, + .sem = __RWSEM_INITIALIZER(root_session_keyring.sem), + .perm = KEY_USR_ALL, + .flags = KEY_FLAG_INSTANTIATED, + .description = "_uid_ses.0", +#ifdef KEY_DEBUGGING + .magic = KEY_DEBUG_MAGIC, +#endif +}; + +/*****************************************************************************/ +/* + * allocate the keyrings to be associated with a UID + */ +int alloc_uid_keyring(struct user_struct *user) +{ + struct key *uid_keyring, *session_keyring; + char buf[20]; + int ret; + + /* concoct a UID specific keyring */ + sprintf(buf, "_uid.%u", user->uid); + + uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, 0, NULL); + if (IS_ERR(uid_keyring)) { + ret = PTR_ERR(uid_keyring); + goto error; + } + + /* and a default session keyring with a pointer to the UID specific + * keyring */ + sprintf(buf, "_uid_ses.%u", user->uid); + + session_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, 0, + uid_keyring); + if (IS_ERR(session_keyring)) { + key_put(uid_keyring); + ret = PTR_ERR(session_keyring); + goto error; + } + + /* install the keyrings */ + user->uid_keyring = uid_keyring; + user->session_keyring = session_keyring; + ret = 0; + + error: + return ret; + +} /* end alloc_uid_keyring() */ + +/*****************************************************************************/ +/* + * deal with the UID changing + */ +void switch_uid_keyring(struct user_struct *new_user) +{ +#if 0 /* do nothing for now */ + struct key *old; + + /* switch to the new user's session keyring if we were running under + * root's default session keyring */ + if (new_user->uid != 0 && + current->session_keyring == &root_session_keyring + ) { + atomic_inc(&new_user->session_keyring->usage); + + task_lock(current); + old = current->session_keyring; + current->session_keyring = new_user->session_keyring; + task_unlock(current); + + key_put(old); + } +#endif + +} /* end switch_uid_keyring() */ + +/*****************************************************************************/ +/* + * install a fresh thread keyring, discarding the old one + */ +int install_thread_keyring(struct task_struct *tsk) +{ + struct key *keyring, *old; + char buf[20]; + int ret; + + sprintf(buf, "_tid.%u", tsk->pid); + + keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto error; + } + + task_lock(tsk); + old = tsk->thread_keyring; + tsk->thread_keyring = keyring; + task_unlock(tsk); + + ret = 0; + + key_put(old); + error: + return ret; + +} /* end install_thread_keyring() */ + +/*****************************************************************************/ +/* + * install a fresh process keyring, discarding the old one + */ +static int install_process_keyring(struct task_struct *tsk) +{ + struct key *keyring, *old; + char buf[20]; + int ret; + + sprintf(buf, "_pid.%u", tsk->tgid); + + keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto error; + } + + task_lock(tsk); + old = tsk->process_keyring; + tsk->process_keyring = keyring; + task_unlock(tsk); + + ret = 0; + + key_put(old); + error: + return ret; + +} /* end install_process_keyring() */ + +/*****************************************************************************/ +/* + * install a session keyring, discarding the old one + * - if a keyring is not supplied, an empty one is invented + */ +static int install_session_keyring(struct task_struct *tsk, + struct key *keyring) +{ + struct key *old; + char buf[20]; + int ret; + + /* create an empty session keyring */ + if (!keyring) { + sprintf(buf, "_ses.%u", tsk->tgid); + + keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto error; + } + } + else { + atomic_inc(&keyring->usage); + } + + /* install the keyring */ + task_lock(tsk); + old = tsk->session_keyring; + tsk->session_keyring = keyring; + task_unlock(tsk); + + ret = 0; + + key_put(old); + error: + return ret; + +} /* end install_session_keyring() */ + +/*****************************************************************************/ +/* + * copy the keys for fork + */ +int copy_keys(unsigned long clone_flags, struct task_struct *tsk) +{ + int ret = 0; + + key_check(tsk->session_keyring); + key_check(tsk->process_keyring); + key_check(tsk->thread_keyring); + + if (tsk->session_keyring) + atomic_inc(&tsk->session_keyring->usage); + + if (tsk->process_keyring) { + if (clone_flags & CLONE_THREAD) { + atomic_inc(&tsk->process_keyring->usage); + } + else { + tsk->process_keyring = NULL; + ret = install_process_keyring(tsk); + } + } + + tsk->thread_keyring = NULL; + return ret; + +} /* end copy_keys() */ + +/*****************************************************************************/ +/* + * dispose of keys upon exit + */ +void exit_keys(struct task_struct *tsk) +{ + key_put(tsk->session_keyring); + key_put(tsk->process_keyring); + key_put(tsk->thread_keyring); + +} /* end exit_keys() */ + +/*****************************************************************************/ +/* + * deal with execve() + */ +int exec_keys(struct task_struct *tsk) +{ + struct key *old; + + /* newly exec'd tasks don't get a thread keyring */ + task_lock(tsk); + old = tsk->thread_keyring; + tsk->thread_keyring = NULL; + task_unlock(tsk); + + key_put(old); + + /* newly exec'd tasks get a fresh process keyring */ + return install_process_keyring(tsk); + +} /* end exec_keys() */ + +/*****************************************************************************/ +/* + * deal with SUID programs + * - we might want to make this invent a new session keyring + */ +int suid_keys(struct task_struct *tsk) +{ + return 0; + +} /* end suid_keys() */ + +/*****************************************************************************/ +/* + * the filesystem user ID changed + */ +void key_fsuid_changed(struct task_struct *tsk) +{ + /* update the ownership of the process keyring */ + if (tsk->process_keyring) { + down_write(&tsk->process_keyring->sem); + write_lock(&tsk->process_keyring->lock); + tsk->process_keyring->uid = tsk->fsuid; + write_unlock(&tsk->process_keyring->lock); + up_write(&tsk->process_keyring->sem); + } + + /* update the ownership of the thread keyring */ + if (tsk->thread_keyring) { + down_write(&tsk->thread_keyring->sem); + write_lock(&tsk->thread_keyring->lock); + tsk->thread_keyring->uid = tsk->fsuid; + write_unlock(&tsk->thread_keyring->lock); + up_write(&tsk->thread_keyring->sem); + } + +} /* end key_fsuid_changed() */ + +/*****************************************************************************/ +/* + * the filesystem group ID changed + */ +void key_fsgid_changed(struct task_struct *tsk) +{ + /* update the ownership of the process keyring */ + if (tsk->process_keyring) { + down_write(&tsk->process_keyring->sem); + write_lock(&tsk->process_keyring->lock); + tsk->process_keyring->gid = tsk->fsgid; + write_unlock(&tsk->process_keyring->lock); + up_write(&tsk->process_keyring->sem); + } + + /* update the ownership of the thread keyring */ + if (tsk->thread_keyring) { + down_write(&tsk->thread_keyring->sem); + write_lock(&tsk->thread_keyring->lock); + tsk->thread_keyring->gid = tsk->fsgid; + write_unlock(&tsk->thread_keyring->lock); + up_write(&tsk->thread_keyring->sem); + } + +} /* end key_fsgid_changed() */ + +/*****************************************************************************/ +/* + * search the process keyrings for the first matching key + * - we use the supplied match function to see if the description (or other + * feature of interest) matches + * - we return -EAGAIN if we didn't find any matching key + * - we return -ENOKEY if we found only negative matching keys + */ +struct key *search_process_keyrings_aux(struct key_type *type, + const void *description, + key_match_func_t match) +{ + struct task_struct *tsk = current; + struct key *key, *ret, *err, *session; + + /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were + * searchable, but we failed to find a key or we found a negative key; + * otherwise we want to return a sample error (probably -EACCES) if + * none of the keyrings were searchable + * + * in terms of priority: success > -ENOKEY > -EAGAIN > other error + */ + key = NULL; + ret = NULL; + err = ERR_PTR(-EAGAIN); + + /* search the thread keyring first */ + if (tsk->thread_keyring) { + key = keyring_search_aux(tsk->thread_keyring, type, + description, match); + if (!IS_ERR(key)) + goto found; + + switch (PTR_ERR(key)) { + case -EAGAIN: /* no key */ + if (ret) + break; + case -ENOKEY: /* negative key */ + ret = key; + break; + default: + err = key; + break; + } + } + + /* search the process keyring second */ + if (tsk->process_keyring) { + key = keyring_search_aux(tsk->process_keyring, type, + description, match); + if (!IS_ERR(key)) + goto found; + + switch (PTR_ERR(key)) { + case -EAGAIN: /* no key */ + if (ret) + break; + case -ENOKEY: /* negative key */ + ret = key; + break; + default: + err = key; + break; + } + } + + /* search the session keyring last */ + session = tsk->session_keyring; + if (!session) + session = tsk->user->session_keyring; + + key = keyring_search_aux(session, type, + description, match); + if (!IS_ERR(key)) + goto found; + + switch (PTR_ERR(key)) { + case -EAGAIN: /* no key */ + if (ret) + break; + case -ENOKEY: /* negative key */ + ret = key; + break; + default: + err = key; + break; + } + + /* no key - decide on the error we're going to go for */ + key = ret ? ret : err; + + found: + return key; + +} /* end search_process_keyrings_aux() */ + +/*****************************************************************************/ +/* + * search the process keyrings for the first matching key + * - we return -EAGAIN if we didn't find any matching key + * - we return -ENOKEY if we found only negative matching keys + */ +struct key *search_process_keyrings(struct key_type *type, + const char *description) +{ + return search_process_keyrings_aux(type, description, type->match); + +} /* end search_process_keyrings() */ + +/*****************************************************************************/ +/* + * lookup a key given a key ID from userspace with a given permissions mask + * - don't create special keyrings unless so requested + * - partially constructed keys aren't found unless requested + */ +struct key *lookup_user_key(key_serial_t id, int create, int partial, + key_perm_t perm) +{ + struct task_struct *tsk = current; + struct key *key; + int ret; + + key = ERR_PTR(-ENOKEY); + + switch (id) { + case PR_SPEC_THREAD_KEYRING: + if (!tsk->thread_keyring) { + if (!create) + goto error; + + ret = install_thread_keyring(tsk); + if (ret < 0) { + key = ERR_PTR(ret); + goto error; + } + } + + key = tsk->thread_keyring; + atomic_inc(&key->usage); + break; + + case PR_SPEC_PROCESS_KEYRING: + if (!tsk->process_keyring) { + if (!create) + goto error; + + ret = install_process_keyring(tsk); + if (ret < 0) { + key = ERR_PTR(ret); + goto error; + } + } + + key = tsk->process_keyring; + atomic_inc(&key->usage); + break; + + case PR_SPEC_SESSION_KEYRING: + if (!tsk->session_keyring) { + /* always install a session keyring upon access if one + * doesn't exist yet */ + ret = install_session_keyring( + tsk, tsk->user->session_keyring); + if (ret < 0) + goto error; + } + + key = tsk->session_keyring; + atomic_inc(&key->usage); + break; + + case PR_SPEC_USER_KEYRING: + key = tsk->user->uid_keyring; + atomic_inc(&key->usage); + break; + + case PR_SPEC_USER_SESSION_KEYRING: + key = tsk->user->session_keyring; + atomic_inc(&key->usage); + break; + + case PR_SPEC_GROUP_KEYRING: + /* group keyrings are not yet supported */ + key = ERR_PTR(-EINVAL); + goto error; + + default: + key = ERR_PTR(-EINVAL); + if (id < 1) + goto error; + + key = key_lookup(id); + if (IS_ERR(key)) + goto error; + break; + } + + /* check the status and permissions */ + if (perm) { + ret = key_validate(key); + if (ret < 0) + goto invalid_key; + } + + ret = -EIO; + if (!partial && !(key->flags & KEY_FLAG_INSTANTIATED)) + goto invalid_key; + + ret = -EACCES; + if (!key_permission(key, perm)) + goto invalid_key; + + error: + return key; + + invalid_key: + key_put(key); + key = ERR_PTR(ret); + goto error; + +} /* end lookup_user_key() */ + +/*****************************************************************************/ +/* + * get the ID of the specified process keyring + * - the keyring must have search permission to be found + * - implements prctl(PR_GET_KEYRING_ID) + */ +long get_process_keyring_ID(key_serial_t id, int create) +{ + struct key *key; + long ret; + + key = lookup_user_key(id, create, 0, KEY_SEARCH); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error; + } + + ret = key->serial; + key_put(key); + error: + return ret; + +} /* end get_process_keyring_ID() */ + +/*****************************************************************************/ +/* + * join the named keyring as the session keyring if possible, or attempt to + * create a new one of that name if not + * - if the name is NULL, an empty anonymous keyring is installed instead + * - named session keyring joining is done with a semaphore held + */ +long join_session_keyring(const char *name) +{ + struct task_struct *tsk = current; + struct key *keyring; + long ret; + + /* if no name is provided, install an anonymous keyring */ + if (!name) { + ret = install_session_keyring(tsk, NULL); + if (ret < 0) + goto error; + + ret = tsk->session_keyring->serial; + goto error; + } + + /* allow the user to join or create a named keyring */ + down(&key_session_sem); + + /* look for an existing keyring of this name */ + keyring = find_keyring_by_name(name, 0); + if (PTR_ERR(keyring) == -ENOKEY) { + /* not found - try and create a new one */ + keyring = keyring_alloc(name, tsk->uid, tsk->gid, 0, NULL); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto error; + } + } + else if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto error2; + } + + /* we've got a keyring - now to install it */ + ret = install_session_keyring(tsk, keyring); + if (ret < 0) + goto error2; + + key_put(keyring); + + ret = tsk->session_keyring->serial; + + error2: + up(&key_session_sem); + error: + return ret; + +} /* end join_session_keyring() */ + +/*****************************************************************************/ +/* + * join the session keyring + * - implements prctl(PR_JOIN_SESSION_KEYRING) + */ +long join_session_keyring_user(const char __user *_name) +{ + char *name; + long nlen, ret; + + /* fetch the name from userspace */ + name = NULL; + if (_name) { + ret = -EFAULT; + nlen = strnlen_user(_name, PAGE_SIZE - 1); + if (nlen <= 0) + goto error; + + ret = -EINVAL; + if (nlen > PAGE_SIZE - 1) + goto error; + + ret = -ENOMEM; + name = kmalloc(nlen + 1, GFP_KERNEL); + if (!name) + goto error; + + ret = -EFAULT; + if (copy_from_user(name, _name, nlen + 1) != 0) + goto error2; + } + + /* join the session */ + ret = join_session_keyring(name); + + error2: + kfree(name); + error: + return ret; + +} /* end join_session_keyring_user() */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/security/keys/request_key.c 2004-09-03 00:56:55.756684344 -0700 @@ -0,0 +1,335 @@ +/* request_key.c: request a key from userspace + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include "internal.h" + +struct key_construction { + struct list_head link; /* link in construction queue */ + struct key *key; /* key being constructed */ +}; + +/* when waiting for someone else's keys, you get added to this */ +DECLARE_WAIT_QUEUE_HEAD(request_key_conswq); + +/*****************************************************************************/ +/* + * request userspace finish the construction of a key + * - execute "/sbin/request-key " + * - if callout_info is an empty string, it'll be rendered as a "-" instead + */ +static int call_request_key(struct key *key, + const char *op, + const char *callout_info) +{ + struct task_struct *tsk = current; + char *argv[10], *envp[3], uid_str[12], gid_str[12]; + char key_str[12], keyring_str[3][12]; + int i; + + /* record the UID and GID */ + sprintf(uid_str, "%d", current->fsuid); + sprintf(gid_str, "%d", current->fsgid); + + /* we say which key is under construction */ + sprintf(key_str, "%d", key->serial); + + /* we specify the process's default keyrings */ + task_lock(current); + sprintf(keyring_str[0], "%d", + tsk->thread_keyring ? tsk->thread_keyring->serial : 0); + sprintf(keyring_str[1], "%d", + tsk->process_keyring ? tsk->process_keyring->serial : 0); + sprintf(keyring_str[2], "%d", + (tsk->session_keyring ? + tsk->session_keyring->serial : + tsk->user->session_keyring->serial)); + task_unlock(tsk); + + /* set up a minimal environment */ + i = 0; + envp[i++] = "HOME=/"; + envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + envp[i] = NULL; + + /* set up the argument list */ + i = 0; + argv[i++] = "/sbin/request-key"; + argv[i++] = (char *) op; + argv[i++] = key_str; + argv[i++] = uid_str; + argv[i++] = gid_str; + argv[i++] = keyring_str[0]; + argv[i++] = keyring_str[1]; + argv[i++] = keyring_str[2]; + argv[i++] = callout_info[0] ? (char *) callout_info : "-"; + argv[i] = NULL; + + /* do it */ + return call_usermodehelper(argv[0], argv, envp, 1); + +} /* end call_request_key() */ + +/*****************************************************************************/ +/* + * call out to userspace for the key + * - called with the construction sem held, but the sem is dropped here + * - we ignore program failure and go on key status instead + */ +static struct key *__request_key_construction(struct key_type *type, + const char *description, + const char *callout_info) +{ + struct key_construction cons; + struct timespec now; + struct key *key; + int ret, negative; + + /* create a key and add it to the queue */ + key = key_alloc(type, description, + current->fsuid, current->fsgid, KEY_USR_ALL, 0); + if (IS_ERR(key)) + goto alloc_failed; + + write_lock(&key->lock); + key->flags |= KEY_FLAG_USER_CONSTRUCT; + write_unlock(&key->lock); + + cons.key = key; + list_add_tail(&cons.link, &key->user->consq); + + /* we drop the construction sem here on behalf of the caller */ + up_write(&key_construction_sem); + + /* make the call */ + ret = call_request_key(key, "create", callout_info); + if (ret < 0) + goto request_failed; + + /* if the key wasn't instantiated, then we want to give an error */ + ret = -ENOKEY; + if (!(key->flags & KEY_FLAG_INSTANTIATED)) + goto request_failed; + + down_write(&key_construction_sem); + list_del(&cons.link); + up_write(&key_construction_sem); + + /* also give an error if the key was negatively instantiated */ + check_not_negative: + if (key->flags & KEY_FLAG_NEGATIVE) { + key_put(key); + key = ERR_PTR(-ENOKEY); + } + + out: + return key; + + request_failed: + /* it wasn't instantiated + * - remove from construction queue + * - mark the key as dead + */ + negative = 0; + down_write(&key_construction_sem); + + list_del(&cons.link); + + write_lock(&key->lock); + key->flags &= ~KEY_FLAG_USER_CONSTRUCT; + + /* check it didn't get instantiated between the check and the down */ + if (!(key->flags & KEY_FLAG_INSTANTIATED)) { + key->flags |= KEY_FLAG_INSTANTIATED | KEY_FLAG_NEGATIVE; + negative = 1; + } + + write_unlock(&key->lock); + up_write(&key_construction_sem); + + if (!negative) + goto check_not_negative; /* surprisingly, the key got + * instantiated */ + + /* set the timeout and store in the session keyring if we can */ + now = current_kernel_time(); + key->expiry = now.tv_sec + key_negative_timeout; + + if (current->session_keyring) + key_link(current->session_keyring, key); + key_put(key); + + /* notify anyone who was waiting */ + wake_up_all(&request_key_conswq); + + key = ERR_PTR(ret); + goto out; + + alloc_failed: + up_write(&key_construction_sem); + goto out; + +} /* end __request_key_construction() */ + +/*****************************************************************************/ +/* + * call out to userspace to request the key + * - we check the construction queue first to see if an appropriate key is + * already being constructed by userspace + */ +static struct key *request_key_construction(struct key_type *type, + const char *description, + struct key_user *user, + const char *callout_info) +{ + struct key_construction *pcons; + struct key *key, *ckey; + + DECLARE_WAITQUEUE(myself, current); + + /* see if there's such a key under construction already */ + down_write(&key_construction_sem); + + list_for_each_entry(pcons, &user->consq, link) { + ckey = pcons->key; + + if (ckey->type != type) + continue; + + if (type->match(ckey, description)) + goto found_key_under_construction; + } + + /* see about getting userspace to construct the key */ + key = __request_key_construction(type, description, callout_info); + error: + return key; + + /* someone else has the same key under construction + * - we want to keep an eye on their key + */ + found_key_under_construction: + atomic_inc(&ckey->usage); + up_write(&key_construction_sem); + + /* wait for the key to be completed one way or another */ + add_wait_queue(&request_key_conswq, &myself); + + for (;;) { + set_current_state(TASK_UNINTERRUPTIBLE); + if (!(ckey->flags & KEY_FLAG_USER_CONSTRUCT)) + break; + schedule(); + } + + set_current_state(TASK_RUNNING); + remove_wait_queue(&request_key_conswq, &myself); + + /* we'll need to search this process's keyrings to see if the key is + * now there since we can't automatically assume it's also available + * there */ + key_put(ckey); + ckey = NULL; + + key = NULL; /* request a retry */ + goto error; + +} /* end request_key_construction() */ + +/*****************************************************************************/ +/* + * request a key + * - search the process's keyrings + * - check the list of keys being created or updated + * - call out to userspace for a key if requested (supplementary info can be + * passed) + */ +struct key *request_key(struct key_type *type, + const char *description, + const char *callout_info) +{ + struct key_user *user; + struct key *key; + + /* search all the process keyrings for a key */ + key = search_process_keyrings_aux(type, description, type->match); + + if (PTR_ERR(key) == -EAGAIN) { + /* the search failed, but the keyrings were searchable, so we + * should consult userspace if we can */ + key = ERR_PTR(-ENOKEY); + if (!callout_info) + goto error; + + /* - get hold of the user's construction queue */ + user = key_user_lookup(current->fsuid); + if (IS_ERR(user)) { + key = ERR_PTR(PTR_ERR(user)); + goto error; + } + + for (;;) { + /* ask userspace (returns NULL if it waited on a key + * being constructed) */ + key = request_key_construction(type, description, + user, callout_info); + if (key) + break; + + /* someone else made the key we want, so we need to + * search again as it might now be available to us */ + key = search_process_keyrings_aux(type, description, + type->match); + if (PTR_ERR(key) != -EAGAIN) + break; + } + + key_user_put(user); + } + + error: + return key; + +} /* end request_key() */ + +EXPORT_SYMBOL(request_key); + +/*****************************************************************************/ +/* + * validate a key + */ +int key_validate(struct key *key) +{ + struct timespec now; + int ret; + + /* check it's still accessible */ + ret = -EKEYREVOKED; + if (key->flags & (KEY_FLAG_REVOKED | KEY_FLAG_DEAD)) + goto error; + + /* check it hasn't expired */ + ret = 0; + if (key->expiry) { + now = current_kernel_time(); + if (now.tv_sec >= key->expiry) + ret = -EKEYEXPIRED; + } + + error: + return ret; + +} /* end key_validate() */ + +EXPORT_SYMBOL(key_validate); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/security/keys/user_defined.c 2004-09-03 00:56:54.719841968 -0700 @@ -0,0 +1,191 @@ +/* user_defined.c: user defined key type + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +static int user_instantiate(struct key *key, const void *data, size_t datalen); +static int user_duplicate(struct key *key, const struct key *source); +static int user_update(struct key *key, const void *data, size_t datalen); +static int user_match(const struct key *key, const void *criterion); +static void user_destroy(struct key *key); +static void user_describe(const struct key *user, struct seq_file *m); +static long user_read(const struct key *key, + char __user *buffer, size_t buflen); + +/* + * user defined keys take an arbitrary string as the description and an + * arbitrary blob of data as the payload + */ +struct key_type key_type_user = { + .name = "user", + .instantiate = user_instantiate, + .duplicate = user_duplicate, + .update = user_update, + .match = user_match, + .destroy = user_destroy, + .describe = user_describe, + .read = user_read, +}; + +/*****************************************************************************/ +/* + * instantiate a user defined key + */ +static int user_instantiate(struct key *key, const void *data, size_t datalen) +{ + int ret; + + ret = -EINVAL; + if (datalen <= 0 || datalen > 32767 || !data) + goto error; + + ret = key_payload_reserve(key, datalen); + if (ret < 0) + goto error; + + /* attach the data */ + ret = -ENOMEM; + key->payload.data = kmalloc(datalen, GFP_KERNEL); + if (!key->payload.data) + goto error; + + memcpy(key->payload.data, data, datalen); + ret = 0; + + error: + return ret; + +} /* end user_instantiate() */ + +/*****************************************************************************/ +/* + * duplicate a user defined key + */ +static int user_duplicate(struct key *key, const struct key *source) +{ + int ret; + + /* just copy the payload */ + ret = -ENOMEM; + key->payload.data = kmalloc(source->datalen, GFP_KERNEL); + + if (key->payload.data) { + key->datalen = source->datalen; + memcpy(key->payload.data, source->payload.data, source->datalen); + ret = 0; + } + + return ret; + +} /* end user_duplicate() */ + +/*****************************************************************************/ +/* + * update a user defined key + */ +static int user_update(struct key *key, const void *data, size_t datalen) +{ + void *new, *zap; + int ret; + + ret = -EINVAL; + if (datalen <= 0 || datalen > 32767 || !data) + goto error; + + /* copy the data */ + ret = -ENOMEM; + new = kmalloc(datalen, GFP_KERNEL); + if (!new) + goto error; + + memcpy(new, data, datalen); + + /* check the quota and attach the new data */ + zap = new; + write_lock(&key->lock); + + ret = key_payload_reserve(key, datalen); + + if (ret == 0) { + /* attach the new data, displacing the old */ + zap = key->payload.data; + key->payload.data = new; + key->expiry = 0; + } + + write_unlock(&key->lock); + kfree(zap); + + error: + return ret; + +} /* end user_update() */ + +/*****************************************************************************/ +/* + * match users on their name + */ +static int user_match(const struct key *key, const void *description) +{ + return strcmp(key->description, description) == 0; + +} /* end user_match() */ + +/*****************************************************************************/ +/* + * dispose of the data dangling from the corpse of a user + */ +static void user_destroy(struct key *key) +{ + kfree(key->payload.data); + +} /* end user_destroy() */ + +/*****************************************************************************/ +/* + * describe the user + */ +static void user_describe(const struct key *key, struct seq_file *m) +{ + seq_puts(m, key->description); + + seq_printf(m, ": %u", key->datalen); + +} /* end user_describe() */ + +/*****************************************************************************/ +/* + * read the key data + */ +static long user_read(const struct key *key, + char __user *buffer, size_t buflen) +{ + long ret = key->datalen; + + /* we can return the data as is */ + if (buffer && buflen > 0) { + if (buflen > key->datalen) + buflen = key->datalen; + + if (copy_to_user(buffer, key->payload.data, buflen) != 0) + ret = -EFAULT; + } + + return ret; + +} /* end user_read() */ --- linux-2.6.9-rc1/security/Makefile 2004-08-24 00:03:18.000000000 -0700 +++ 25/security/Makefile 2004-09-03 00:56:54.719841968 -0700 @@ -2,6 +2,7 @@ # Makefile for the kernel security code # +obj-$(CONFIG_KEYS) += keys/ subdir-$(CONFIG_SECURITY_SELINUX) += selinux # if we don't select a security model, use the default capabilities --- linux-2.6.9-rc1/security/security.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/security/security.c 2004-09-02 20:51:53.000000000 -0700 @@ -49,7 +49,7 @@ static void __init do_security_initcalls } /** - * security_scaffolding_startup - initialzes the security scaffolding framework + * security_scaffolding_startup - initializes the security scaffolding framework * * This should be called early in the kernel initialization sequence. */ @@ -183,7 +183,7 @@ int mod_unreg_security (const char *name * capable - calls the currently loaded security module's capable() function with the specified capability * @cap: the requested capability level. * - * This function calls the currently loaded security module's cabable() + * This function calls the currently loaded security module's capable() * function with a pointer to the current task and the specified @cap value. * * This allows the security module to implement the capable function call --- linux-2.6.9-rc1/security/selinux/hooks.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/security/selinux/hooks.c 2004-09-02 20:51:53.000000000 -0700 @@ -43,6 +43,7 @@ #include #include #include +#include #include #include /* for sysctl_local_port_range[] */ #include /* struct or_callable used in sock_rcv_skb */ @@ -62,7 +63,6 @@ #include #include #include -#include #include #include "avc.h" @@ -87,7 +87,7 @@ __setup("enforcing=", enforcing_setup); #endif #ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM -int selinux_enabled = 1; +int selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE; static int __init selinux_enabled_setup(char *str) { @@ -1266,6 +1266,12 @@ static inline u32 file_to_av(struct file int inode_security_set_sid(struct inode *inode, u32 sid) { struct inode_security_struct *isec = inode->i_security; + struct superblock_security_struct *sbsec = inode->i_sb->s_security; + + if (!sbsec->initialized) { + /* Defer initialization to selinux_complete_init. */ + return 0; + } down(&isec->sem); isec->sclass = inode_mode_to_security_class(inode->i_mode); @@ -1726,72 +1732,40 @@ static void selinux_bprm_free_security(s kfree(bsec); } -/* Create an open file that refers to the null device. - Derived from the OpenWall LSM. */ -struct file *open_devnull(void) -{ - struct inode *inode; - struct dentry *dentry; - struct file *file = NULL; - struct inode_security_struct *isec; - dev_t dev; - - inode = new_inode(current->fs->rootmnt->mnt_sb); - if (!inode) - goto out; - - dentry = dget(d_alloc_root(inode)); - if (!dentry) - goto out_iput; - - file = get_empty_filp(); - if (!file) - goto out_dput; - - dev = MKDEV(MEM_MAJOR, 3); /* null device */ - - inode->i_uid = current->fsuid; - inode->i_gid = current->fsgid; - inode->i_blksize = PAGE_SIZE; - inode->i_blocks = 0; - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; - inode->i_state = I_DIRTY; /* so that mark_inode_dirty won't touch us */ - - isec = inode->i_security; - isec->sid = SECINITSID_DEVNULL; - isec->sclass = SECCLASS_CHR_FILE; - isec->initialized = 1; - - file->f_flags = O_RDWR; - file->f_mode = FMODE_READ | FMODE_WRITE; - file->f_dentry = dentry; - file->f_vfsmnt = mntget(current->fs->rootmnt); - file->f_pos = 0; - - init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, dev); - if (inode->i_fop->open(inode, file)) - goto out_fput; - -out: - return file; -out_fput: - mntput(file->f_vfsmnt); - put_filp(file); -out_dput: - dput(dentry); -out_iput: - iput(inode); - file = NULL; - goto out; -} +extern struct vfsmount *selinuxfs_mount; +extern struct dentry *selinux_null; /* Derived from fs/exec.c:flush_old_files. */ static inline void flush_unauthorized_files(struct files_struct * files) { struct avc_audit_data ad; struct file *file, *devnull = NULL; + struct tty_struct *tty = current->signal->tty; long j = -1; + if (tty) { + file_list_lock(); + file = list_entry(tty->tty_files.next, typeof(*file), f_list); + if (file) { + /* Revalidate access to controlling tty. + Use inode_has_perm on the tty inode directly rather + than using file_has_perm, as this particular open + file may belong to another process and we are only + interested in the inode-based check here. */ + struct inode *inode = file->f_dentry->d_inode; + if (inode_has_perm(current, inode, + FILE__READ | FILE__WRITE, + NULL, NULL)) { + /* Reset controlling tty. */ + current->signal->tty = NULL; + current->signal->tty_old_pgrp = 0; + } + } + file_list_unlock(); + } + + /* Revalidate access to inherited open files. */ + AVC_AUDIT_DATA_INIT(&ad,FS); spin_lock(&files->file_lock); @@ -1826,7 +1800,7 @@ static inline void flush_unauthorized_fi if (devnull) { atomic_inc(&devnull->f_count); } else { - devnull = open_devnull(); + devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR); if (!devnull) { put_unused_fd(fd); fput(file); @@ -2822,7 +2796,7 @@ static void selinux_task_to_inode(struct /* Returns error only if unable to parse addresses */ static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad) { - int offset, ihlen, ret; + int offset, ihlen, ret = -EINVAL; struct iphdr _iph, *ih; offset = skb->nh.raw - skb->data; @@ -2836,6 +2810,7 @@ static int selinux_parse_skb_ipv4(struct ad->u.net.v4info.saddr = ih->saddr; ad->u.net.v4info.daddr = ih->daddr; + ret = 0; switch (ih->protocol) { case IPPROTO_TCP: { @@ -2883,7 +2858,7 @@ out: static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad) { u8 nexthdr; - int ret, offset; + int ret = -EINVAL, offset; struct ipv6hdr _ipv6h, *ip6; offset = skb->nh.raw - skb->data; @@ -2893,6 +2868,7 @@ static int selinux_parse_skb_ipv6(struct ipv6_addr_copy(&ad->u.net.v6info.saddr, &ip6->saddr); ipv6_addr_copy(&ad->u.net.v6info.daddr, &ip6->daddr); + ret = 0; nexthdr = ip6->nexthdr; offset += sizeof(_ipv6h); @@ -3082,6 +3058,7 @@ static int selinux_socket_bind(struct so goto out; AVC_AUDIT_DATA_INIT(&ad,NET); ad.u.net.sport = htons(snum); + ad.u.net.family = family; err = avc_has_perm(isec->sid, sid, isec->sclass, SOCKET__NAME_BIND, NULL, &ad); --- linux-2.6.9-rc1/security/selinux/Kconfig 2004-08-24 00:01:54.000000000 -0700 +++ 25/security/selinux/Kconfig 2004-09-02 20:51:53.000000000 -0700 @@ -24,6 +24,21 @@ config SECURITY_SELINUX_BOOTPARAM If you are unsure how to answer this question, answer N. +config SECURITY_SELINUX_BOOTPARAM_VALUE + int "NSA SELinux boot parameter default value" + depends on SECURITY_SELINUX_BOOTPARAM + range 0 1 + default 1 + help + This option sets the default value for the kernel parameter + 'selinux', which allows SELinux to be disabled at boot. If this + option is set to 0 (zero), the SELinux kernel parameter will + default to 0, disabling SELinux at bootup. If this option is + set to 1 (one), the SELinux kernel paramater will default to 1, + enabling SELinux at bootup. + + If you are unsure how to answer this question, answer 1. + config SECURITY_SELINUX_DISABLE bool "NSA SELinux runtime disable" depends on SECURITY_SELINUX --- linux-2.6.9-rc1/security/selinux/selinuxfs.c 2004-08-24 00:03:49.000000000 -0700 +++ 25/security/selinux/selinuxfs.c 2004-09-02 20:51:53.000000000 -0700 @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -68,40 +69,15 @@ enum sel_inos { SEL_DISABLE /* disable SELinux until next reboot */ }; +#define TMPBUFLEN 12 static ssize_t sel_read_enforce(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { - char *page; + char tmpbuf[TMPBUFLEN]; ssize_t length; - ssize_t end; - - if (count < 0 || count > PAGE_SIZE) - return -EINVAL; - if (!(page = (char*)__get_free_page(GFP_KERNEL))) - return -ENOMEM; - memset(page, 0, PAGE_SIZE); - length = scnprintf(page, PAGE_SIZE, "%d", selinux_enforcing); - if (length < 0) { - free_page((unsigned long)page); - return length; - } - - if (*ppos >= length) { - free_page((unsigned long)page); - return 0; - } - if (count + *ppos > length) - count = length - *ppos; - end = count + *ppos; - if (copy_to_user(buf, (char *) page + *ppos, count)) { - count = -EFAULT; - goto out; - } - *ppos = end; -out: - free_page((unsigned long)page); - return count; + length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_enforcing); + return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); } #ifdef CONFIG_SECURITY_SELINUX_DEVELOP @@ -119,10 +95,9 @@ static ssize_t sel_write_enforce(struct /* No partial writes. */ return -EINVAL; } - page = (char*)__get_free_page(GFP_KERNEL); + page = (char*)get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; - memset(page, 0, PAGE_SIZE); length = -EFAULT; if (copy_from_user(page, buf, count)) goto out; @@ -170,10 +145,9 @@ static ssize_t sel_write_disable(struct /* No partial writes. */ return -EINVAL; } - page = (char*)__get_free_page(GFP_KERNEL); + page = (char*)get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; - memset(page, 0, PAGE_SIZE); length = -EFAULT; if (copy_from_user(page, buf, count)) goto out; @@ -204,37 +178,11 @@ static struct file_operations sel_disabl static ssize_t sel_read_policyvers(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { - char *page; + char tmpbuf[TMPBUFLEN]; ssize_t length; - ssize_t end; - if (count < 0 || count > PAGE_SIZE) - return -EINVAL; - if (!(page = (char*)__get_free_page(GFP_KERNEL))) - return -ENOMEM; - memset(page, 0, PAGE_SIZE); - - length = scnprintf(page, PAGE_SIZE, "%u", POLICYDB_VERSION_MAX); - if (length < 0) { - free_page((unsigned long)page); - return length; - } - - if (*ppos >= length) { - free_page((unsigned long)page); - return 0; - } - if (count + *ppos > length) - count = length - *ppos; - end = count + *ppos; - if (copy_to_user(buf, (char *) page + *ppos, count)) { - count = -EFAULT; - goto out; - } - *ppos = end; -out: - free_page((unsigned long)page); - return count; + length = scnprintf(tmpbuf, TMPBUFLEN, "%u", POLICYDB_VERSION_MAX); + return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); } static struct file_operations sel_policyvers_ops = { @@ -247,37 +195,11 @@ static int sel_make_bools(void); static ssize_t sel_read_mls(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { - char *page; + char tmpbuf[TMPBUFLEN]; ssize_t length; - ssize_t end; - - if (count < 0 || count > PAGE_SIZE) - return -EINVAL; - if (!(page = (char*)__get_free_page(GFP_KERNEL))) - return -ENOMEM; - memset(page, 0, PAGE_SIZE); - length = scnprintf(page, PAGE_SIZE, "%d", selinux_mls_enabled); - if (length < 0) { - free_page((unsigned long)page); - return length; - } - - if (*ppos >= length) { - free_page((unsigned long)page); - return 0; - } - if (count + *ppos > length) - count = length - *ppos; - end = count + *ppos; - if (copy_to_user(buf, (char *) page + *ppos, count)) { - count = -EFAULT; - goto out; - } - *ppos = end; -out: - free_page((unsigned long)page); - return count; + length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_mls_enabled); + return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); } static struct file_operations sel_mls_ops = { @@ -352,10 +274,9 @@ static ssize_t sel_write_context(struct /* No partial writes. */ return -EINVAL; } - page = (char*)__get_free_page(GFP_KERNEL); + page = (char*)get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; - memset(page, 0, PAGE_SIZE); length = -EFAULT; if (copy_from_user(page, buf, count)) goto out; @@ -390,103 +311,31 @@ static ssize_t (*write_op[])(struct file [SEL_USER] = sel_write_user, }; -/* an argresp is stored in an allocated page and holds the - * size of the argument or response, along with its content - */ -struct argresp { - ssize_t size; - char data[0]; -}; - -#define PAYLOAD_SIZE (PAGE_SIZE - sizeof(struct argresp)) - -/* - * transaction based IO methods. - * The file expects a single write which triggers the transaction, and then - * possibly a read which collects the result - which is stored in a - * file-local buffer. - */ -static ssize_t TA_write(struct file *file, const char __user *buf, size_t size, loff_t *pos) +static ssize_t selinux_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos) { ino_t ino = file->f_dentry->d_inode->i_ino; - struct argresp *ar; - ssize_t rv = 0; + char *data; + ssize_t rv; if (ino >= sizeof(write_op)/sizeof(write_op[0]) || !write_op[ino]) return -EINVAL; - if (file->private_data) - return -EINVAL; /* only one write allowed per open */ - if (size > PAYLOAD_SIZE - 1) /* allow one byte for null terminator */ - return -EFBIG; - ar = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!ar) - return -ENOMEM; - memset(ar, 0, PAGE_SIZE); /* clear buffer, particularly last byte */ - ar->size = 0; - down(&file->f_dentry->d_inode->i_sem); - if (file->private_data) - rv = -EINVAL; - else - file->private_data = ar; - up(&file->f_dentry->d_inode->i_sem); - if (rv) { - kfree(ar); - return rv; - } - if (copy_from_user(ar->data, buf, size)) - return -EFAULT; + data = simple_transaction_get(file, buf, size); + if (IS_ERR(data)) + return PTR_ERR(data); - rv = write_op[ino](file, ar->data, size); + rv = write_op[ino](file, data, size); if (rv>0) { - ar->size = rv; + simple_transaction_set(file, rv); rv = size; } return rv; } -static ssize_t TA_read(struct file *file, char __user *buf, size_t size, loff_t *pos) -{ - struct argresp *ar; - ssize_t rv = 0; - - if (file->private_data == NULL) - rv = TA_write(file, buf, 0, pos); - if (rv < 0) - return rv; - - ar = file->private_data; - if (!ar) - return 0; - if (*pos >= ar->size) - return 0; - if (*pos + size > ar->size) - size = ar->size - *pos; - if (copy_to_user(buf, ar->data + *pos, size)) - return -EFAULT; - *pos += size; - return size; -} - -static int TA_open(struct inode *inode, struct file *file) -{ - file->private_data = NULL; - return 0; -} - -static int TA_release(struct inode *inode, struct file *file) -{ - void *p = file->private_data; - file->private_data = NULL; - kfree(p); - return 0; -} - static struct file_operations transaction_ops = { - .write = TA_write, - .read = TA_read, - .open = TA_open, - .release = TA_release, + .write = selinux_transaction_write, + .read = simple_transaction_read, + .release = simple_transaction_release, }; /* @@ -534,7 +383,8 @@ static ssize_t sel_write_access(struct f if (length < 0) goto out2; - length = scnprintf(buf, PAYLOAD_SIZE, "%x %x %x %x %u", + length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, + "%x %x %x %x %u", avd.allowed, avd.decided, avd.auditallow, avd.auditdeny, avd.seqno); @@ -588,7 +438,7 @@ static ssize_t sel_write_create(struct f if (length < 0) goto out2; - if (len > PAYLOAD_SIZE) { + if (len > SIMPLE_TRANSACTION_LIMIT) { printk(KERN_ERR "%s: context size (%u) exceeds payload " "max\n", __FUNCTION__, len); length = -ERANGE; @@ -649,7 +499,7 @@ static ssize_t sel_write_relabel(struct if (length < 0) goto out2; - if (len > PAYLOAD_SIZE) { + if (len > SIMPLE_TRANSACTION_LIMIT) { length = -ERANGE; goto out3; } @@ -709,7 +559,7 @@ static ssize_t sel_write_user(struct fil length = rc; goto out3; } - if ((length + len) >= PAYLOAD_SIZE) { + if ((length + len) >= SIMPLE_TRANSACTION_LIMIT) { kfree(newcon); length = -ERANGE; goto out3; @@ -766,11 +616,10 @@ static ssize_t sel_read_bool(struct file ret = -EINVAL; goto out; } - if (!(page = (char*)__get_free_page(GFP_KERNEL))) { + if (!(page = (char*)get_zeroed_page(GFP_KERNEL))) { ret = -ENOMEM; goto out; } - memset(page, 0, PAGE_SIZE); inode = filep->f_dentry->d_inode; cur_enforcing = security_get_bool_value(inode->i_ino - BOOL_INO_OFFSET); @@ -832,12 +681,11 @@ static ssize_t sel_write_bool(struct fil /* No partial writes. */ goto out; } - page = (char*)__get_free_page(GFP_KERNEL); + page = (char*)get_zeroed_page(GFP_KERNEL); if (!page) { length = -ENOMEM; goto out; } - memset(page, 0, PAGE_SIZE); if (copy_from_user(page, buf, count)) goto out; @@ -891,14 +739,12 @@ static ssize_t sel_commit_bools_write(st /* No partial writes. */ goto out; } - page = (char*)__get_free_page(GFP_KERNEL); + page = (char*)get_zeroed_page(GFP_KERNEL); if (!page) { length = -ENOMEM; goto out; } - memset(page, 0, PAGE_SIZE); - if (copy_from_user(page, buf, count)) goto out; @@ -984,9 +830,8 @@ static int sel_make_bools(void) sel_remove_bools(dir); - if (!(page = (char*)__get_free_page(GFP_KERNEL))) + if (!(page = (char*)get_zeroed_page(GFP_KERNEL))) return -ENOMEM; - memset(page, 0, PAGE_SIZE); ret = security_get_bools(&num, &names, &values); if (ret != 0) @@ -1042,12 +887,17 @@ err: goto out; } +#define NULL_FILE_NAME "null" + +struct dentry *selinux_null = NULL; + static int sel_fill_super(struct super_block * sb, void * data, int silent) { int ret; struct dentry *dentry; struct inode *inode; struct qstr qname; + struct inode_security_struct *isec; static struct tree_descr selinux_files[] = { [SEL_LOAD] = {"load", &sel_load_ops, S_IRUSR|S_IWUSR}, @@ -1085,10 +935,29 @@ static int sel_fill_super(struct super_b if (ret) goto out; + qname.name = NULL_FILE_NAME; + qname.len = strlen(qname.name); + qname.hash = full_name_hash(qname.name, qname.len); + dentry = d_alloc(sb->s_root, &qname); + if (!dentry) + return -ENOMEM; + + inode = sel_make_inode(sb, S_IFCHR | S_IRUGO | S_IWUGO); + if (!inode) + goto out; + isec = (struct inode_security_struct*)inode->i_security; + isec->sid = SECINITSID_DEVNULL; + isec->sclass = SECCLASS_CHR_FILE; + isec->initialized = 1; + + init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, MKDEV(MEM_MAJOR, 3)); + d_add(dentry, inode); + selinux_null = dentry; + return 0; out: dput(dentry); - printk(KERN_ERR "security: error creating conditional out_dput\n"); + printk(KERN_ERR "%s: failed while creating inodes\n", __FUNCTION__); return -ENOMEM; } @@ -1104,9 +973,24 @@ static struct file_system_type sel_fs_ty .kill_sb = kill_litter_super, }; +struct vfsmount *selinuxfs_mount; + static int __init init_sel_fs(void) { - return selinux_enabled ? register_filesystem(&sel_fs_type) : 0; + int err; + + if (!selinux_enabled) + return 0; + err = register_filesystem(&sel_fs_type); + if (!err) { + selinuxfs_mount = kern_mount(&sel_fs_type); + if (IS_ERR(selinuxfs_mount)) { + printk(KERN_ERR "selinuxfs: could not mount!\n"); + err = PTR_ERR(selinuxfs_mount); + selinuxfs_mount = NULL; + } + } + return err; } __initcall(init_sel_fs); --- linux-2.6.9-rc1/security/selinux/ss/avtab.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/security/selinux/ss/avtab.c 2004-09-02 20:51:53.000000000 -0700 @@ -28,12 +28,14 @@ (keyp->source_type << 9)) & \ AVTAB_HASH_MASK) +static kmem_cache_t *avtab_node_cachep; + static struct avtab_node* avtab_insert_node(struct avtab *h, int hvalue, struct avtab_node * prev, struct avtab_node * cur, struct avtab_key *key, struct avtab_datum *datum) { struct avtab_node * newnode; - newnode = (struct avtab_node *) kmalloc(sizeof(struct avtab_node),GFP_KERNEL); + newnode = kmem_cache_alloc(avtab_node_cachep, SLAB_KERNEL); if (newnode == NULL) return NULL; memset(newnode, 0, sizeof(struct avtab_node)); @@ -226,7 +228,7 @@ void avtab_destroy(struct avtab *h) while (cur != NULL) { temp = cur; cur = cur->next; - kfree(temp); + kmem_cache_free(avtab_node_cachep, temp); } h->htable[i] = NULL; } @@ -399,3 +401,9 @@ bad: goto out; } +void avtab_cache_init(void) +{ + avtab_node_cachep = kmem_cache_create("avtab_node", + sizeof(struct avtab_node), + 0, SLAB_PANIC, NULL, NULL); +} --- linux-2.6.9-rc1/security/selinux/ss/avtab.h 2004-08-24 00:02:26.000000000 -0700 +++ 25/security/selinux/ss/avtab.h 2004-09-02 20:51:53.000000000 -0700 @@ -78,6 +78,8 @@ struct avtab_node *avtab_search_node(str struct avtab_node *avtab_search_node_next(struct avtab_node *node, int specified); +void avtab_cache_init(void); + #define AVTAB_HASH_BITS 15 #define AVTAB_HASH_BUCKETS (1 << AVTAB_HASH_BITS) #define AVTAB_HASH_MASK (AVTAB_HASH_BUCKETS-1) --- linux-2.6.9-rc1/security/selinux/ss/services.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/security/selinux/ss/services.c 2004-09-02 20:51:53.000000000 -0700 @@ -1034,6 +1034,7 @@ int security_load_policy(void *data, siz LOAD_LOCK; if (!ss_initialized) { + avtab_cache_init(); if (policydb_read(&policydb, fp)) { LOAD_UNLOCK; return -EINVAL; --- linux-2.6.9-rc1/sound/arm/sa11xx-uda1341.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/sound/arm/sa11xx-uda1341.c 2004-09-02 20:51:53.000000000 -0700 @@ -21,7 +21,7 @@ * merged HAL layer (patches from Brian) */ -/* $Id: sa11xx-uda1341.c,v 1.15 2004/05/03 17:36:50 tiwai Exp $ */ +/* $Id: sa11xx-uda1341.c,v 1.18 2004/07/20 15:54:09 cladisch Exp $ */ /*************************************************************************************************** * @@ -108,16 +108,13 @@ MODULE_AUTHOR("Tomas Kasparek "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("SA1100/SA1111 + UDA1341TS driver for ALSA"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{UDA1341,iPAQ H3600 UDA1341TS}}"); +MODULE_SUPPORTED_DEVICE("{{UDA1341,iPAQ H3600 UDA1341TS}}"); static char *id = NULL; /* ID for this card */ module_param(id, charp, 0444); MODULE_PARM_DESC(id, "ID string for SA1100/SA1111 + UDA1341TS soundcard."); -#define chip_t sa11xx_uda1341_t - typedef struct audio_stream { char *id; /* identification string */ int stream_id; /* numeric identification */ @@ -152,10 +149,8 @@ static unsigned int rates[] = { 29400, 32000, 44100, 48000, }; -#define RATES sizeof(rates) / sizeof(rates[0]) - static snd_pcm_hw_constraint_list_t hw_constraints_rates = { - .count = RATES, + .count = ARRAY_SIZE(rates), .list = rates, .mask = 0, }; @@ -869,7 +864,7 @@ static int __init snd_card_sa11xx_uda134 static int snd_sa11xx_uda1341_suspend(snd_card_t *card, unsigned int state) { - sa11xx_uda1341_t *chip = snd_magic_cast(sa11x_uda1341_t, card->pm_private_data, return -EINVAL); + sa11xx_uda1341_t *chip = card->pm_private_data; snd_pcm_suspend_all(chip->pcm); #ifdef HH_VERSION @@ -886,7 +881,7 @@ static int snd_sa11xx_uda1341_suspend(sn static int snd_sa11xx_uda1341_resume(snd_card_t *card, unsigned int state) { - sa11xx_uda1341_t *chip = snd_magic_cast(sa11x_uda1341_t, card->pm_private_data, return -EINVAL); + sa11xx_uda1341_t *chip = card->pm_private_data; sa11xx_uda1341_audio_init(chip); l3_command(chip->uda1341, CMD_RESUME, NULL); @@ -903,7 +898,7 @@ static int snd_sa11xx_uda1341_resume(snd void snd_sa11xx_uda1341_free(snd_card_t *card) { - sa11xx_uda1341_t *chip = snd_magic_cast(sa11xx_uda1341_t, card->private_data, return); + sa11xx_uda1341_t *chip = card->private_data; audio_dma_free(&chip->s[SNDRV_PCM_STREAM_PLAYBACK]); audio_dma_free(&chip->s[SNDRV_PCM_STREAM_CAPTURE]); @@ -925,7 +920,7 @@ static int __init sa11xx_uda1341_init(vo if (card == NULL) return -ENOMEM; - sa11xx_uda1341 = snd_magic_kcalloc(sa11xx_uda1341_t, 0, GFP_KERNEL); + sa11xx_uda1341 = kcalloc(1, sizeof(*sa11xx_uda1341), GFP_KERNEL); if (sa11xx_uda1341 == NULL) return -ENOMEM; spin_lock_init(&chip->s[0].dma_lock); --- linux-2.6.9-rc1/sound/core/control.c 2004-08-24 00:02:16.000000000 -0700 +++ 25/sound/core/control.c 2004-09-02 20:51:53.000000000 -0700 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -62,7 +63,7 @@ static int snd_ctl_open(struct inode *in err = -EFAULT; goto __error2; } - ctl = snd_magic_kcalloc(snd_ctl_file_t, 0, GFP_KERNEL); + ctl = kcalloc(1, sizeof(*ctl), GFP_KERNEL); if (ctl == NULL) { err = -ENOMEM; goto __error; @@ -108,7 +109,7 @@ static int snd_ctl_release(struct inode snd_kcontrol_t *control; unsigned int idx; - ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO); + ctl = file->private_data; fasync_helper(-1, file, 0, &ctl->fasync); file->private_data = NULL; card = ctl->card; @@ -124,7 +125,7 @@ static int snd_ctl_release(struct inode } up_write(&card->controls_rwsem); snd_ctl_empty_read_queue(ctl); - snd_magic_kfree(ctl); + kfree(ctl); module_put(card->module); snd_card_file_remove(card, file); return 0; @@ -155,7 +156,7 @@ void snd_ctl_notify(snd_card_t *card, un goto _found; } } - ev = snd_kcalloc(sizeof(*ev), GFP_ATOMIC); + ev = kcalloc(1, sizeof(*ev), GFP_ATOMIC); if (ev) { ev->id = *id; ev->mask = mask; @@ -188,9 +189,7 @@ snd_kcontrol_t *snd_ctl_new(snd_kcontrol snd_runtime_check(control != NULL, return NULL); snd_runtime_check(control->count > 0, return NULL); - kctl = (snd_kcontrol_t *)snd_magic_kcalloc(snd_kcontrol_t, - sizeof(snd_kcontrol_volatile_t) * control->count, - GFP_KERNEL); + kctl = kcalloc(1, sizeof(*kctl) + sizeof(snd_kcontrol_volatile_t) * control->count, GFP_KERNEL); if (kctl == NULL) return NULL; *kctl = *control; @@ -249,7 +248,7 @@ void snd_ctl_free_one(snd_kcontrol_t * k if (kcontrol) { if (kcontrol->private_free) kcontrol->private_free(kcontrol); - snd_magic_kfree(kcontrol); + kfree(kcontrol); } } @@ -927,7 +926,7 @@ static int snd_ctl_elem_add(snd_ctl_file if (!(info.access & SNDRV_CTL_ELEM_ACCESS_DINDIRECT)) for (idx = 0; idx < 4 && info.dimen.d[idx]; idx++) dimen_size += sizeof(unsigned short); - ue = snd_kcalloc(sizeof(struct user_element) + dimen_size + private_size + extra_size, GFP_KERNEL); + ue = kcalloc(1, sizeof(struct user_element) + dimen_size + private_size + extra_size, GFP_KERNEL); if (ue == NULL) return -ENOMEM; ue->type = info.type; @@ -1022,8 +1021,8 @@ static int snd_ctl_set_power_state(snd_c } #endif -static int snd_ctl_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static inline int _snd_ctl_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) { snd_ctl_file_t *ctl; snd_card_t *card; @@ -1033,7 +1032,7 @@ static int snd_ctl_ioctl(struct inode *i int __user *ip = argp; int err; - ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO); + ctl = file->private_data; card = ctl->card; snd_assert(card != NULL, return -ENXIO); switch (cmd) { @@ -1096,13 +1095,24 @@ static int snd_ctl_ioctl(struct inode *i return -ENOTTY; } +/* FIXME: need to unlock BKL to allow preemption */ +static int snd_ctl_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err; + unlock_kernel(); + err = _snd_ctl_ioctl(inode, file, cmd, arg); + lock_kernel(); + return err; +} + static ssize_t snd_ctl_read(struct file *file, char __user *buffer, size_t count, loff_t * offset) { snd_ctl_file_t *ctl; int err = 0; ssize_t result = 0; - ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO); + ctl = file->private_data; snd_assert(ctl != NULL && ctl->card != NULL, return -ENXIO); if (!ctl->subscribed) return -EBADFD; @@ -1116,7 +1126,7 @@ static ssize_t snd_ctl_read(struct file wait_queue_t wait; if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) { err = -EAGAIN; - goto __end; + goto __end_lock; } init_waitqueue_entry(&wait, current); add_wait_queue(&ctl->change_sleep, &wait); @@ -1137,16 +1147,16 @@ static ssize_t snd_ctl_read(struct file kfree(kev); if (copy_to_user(buffer, &ev, sizeof(snd_ctl_event_t))) { err = -EFAULT; - goto out; + goto __end; } spin_lock_irq(&ctl->read_lock); buffer += sizeof(snd_ctl_event_t); count -= sizeof(snd_ctl_event_t); result += sizeof(snd_ctl_event_t); } -__end: + __end_lock: spin_unlock_irq(&ctl->read_lock); -out: + __end: return result > 0 ? result : err; } @@ -1155,7 +1165,7 @@ static unsigned int snd_ctl_poll(struct unsigned int mask; snd_ctl_file_t *ctl; - ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return 0); + ctl = file->private_data; if (!ctl->subscribed) return 0; poll_wait(file, &ctl->change_sleep, wait); @@ -1175,8 +1185,7 @@ int snd_ctl_register_ioctl(snd_kctl_ioct { snd_kctl_ioctl_t *pn; - pn = (snd_kctl_ioctl_t *) - snd_kcalloc(sizeof(snd_kctl_ioctl_t), GFP_KERNEL); + pn = kcalloc(1, sizeof(snd_kctl_ioctl_t), GFP_KERNEL); if (pn == NULL) return -ENOMEM; pn->fioctl = fcn; @@ -1214,7 +1223,7 @@ static int snd_ctl_fasync(int fd, struct { snd_ctl_file_t *ctl; int err; - ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO); + ctl = file->private_data; err = fasync_helper(fd, file, on, &ctl->fasync); if (err < 0) return err; --- linux-2.6.9-rc1/sound/core/device.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/sound/core/device.c 2004-09-02 20:51:53.000000000 -0700 @@ -47,7 +47,7 @@ int snd_device_new(snd_card_t *card, snd snd_device_t *dev; snd_assert(card != NULL && device_data != NULL && ops != NULL, return -ENXIO); - dev = (snd_device_t *) snd_magic_kcalloc(snd_device_t, 0, GFP_KERNEL); + dev = kcalloc(1, sizeof(*dev), GFP_KERNEL); if (dev == NULL) return -ENOMEM; dev->card = card; @@ -94,7 +94,7 @@ int snd_device_free(snd_card_t *card, vo snd_printk(KERN_ERR "device free failure\n"); } } - snd_magic_kfree(dev); + kfree(dev); return 0; } snd_printd("device free %p (from %p), not found\n", device_data, __builtin_return_address(0)); --- linux-2.6.9-rc1/sound/core/hwdep.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/sound/core/hwdep.c 2004-09-02 20:51:53.000000000 -0700 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -49,7 +50,7 @@ static int snd_hwdep_dev_unregister(snd_ static loff_t snd_hwdep_llseek(struct file * file, loff_t offset, int orig) { - snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + snd_hwdep_t *hw = file->private_data; if (hw->ops.llseek) return hw->ops.llseek(hw, file, offset, orig); return -ENXIO; @@ -57,7 +58,7 @@ static loff_t snd_hwdep_llseek(struct fi static ssize_t snd_hwdep_read(struct file * file, char __user *buf, size_t count, loff_t *offset) { - snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + snd_hwdep_t *hw = file->private_data; if (hw->ops.read) return hw->ops.read(hw, buf, count, offset); return -ENXIO; @@ -65,7 +66,7 @@ static ssize_t snd_hwdep_read(struct fil static ssize_t snd_hwdep_write(struct file * file, const char __user *buf, size_t count, loff_t *offset) { - snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + snd_hwdep_t *hw = file->private_data; if (hw->ops.write) return hw->ops.write(hw, buf, count, offset); return -ENXIO; @@ -157,7 +158,7 @@ static int snd_hwdep_open(struct inode * static int snd_hwdep_release(struct inode *inode, struct file * file) { int err = -ENXIO; - snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + snd_hwdep_t *hw = file->private_data; down(&hw->open_mutex); if (hw->ops.release) { err = hw->ops.release(hw, file); @@ -173,7 +174,7 @@ static int snd_hwdep_release(struct inod static unsigned int snd_hwdep_poll(struct file * file, poll_table * wait) { - snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return 0); + snd_hwdep_t *hw = file->private_data; if (hw->ops.poll) return hw->ops.poll(hw, file, wait); return 0; @@ -231,10 +232,10 @@ static int snd_hwdep_dsp_load(snd_hwdep_ return 0; } -static int snd_hwdep_ioctl(struct inode *inode, struct file * file, - unsigned int cmd, unsigned long arg) +static inline int _snd_hwdep_ioctl(struct inode *inode, struct file * file, + unsigned int cmd, unsigned long arg) { - snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + snd_hwdep_t *hw = file->private_data; void __user *argp = (void __user *)arg; switch (cmd) { case SNDRV_HWDEP_IOCTL_PVERSION: @@ -251,9 +252,20 @@ static int snd_hwdep_ioctl(struct inode return -ENOTTY; } +/* FIXME: need to unlock BKL to allow preemption */ +static int snd_hwdep_ioctl(struct inode *inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int err; + unlock_kernel(); + err = _snd_hwdep_ioctl(inode, file, cmd, arg); + lock_kernel(); + return err; +} + static int snd_hwdep_mmap(struct file * file, struct vm_area_struct * vma) { - snd_hwdep_t *hw = snd_magic_cast(snd_hwdep_t, file->private_data, return -ENXIO); + snd_hwdep_t *hw = file->private_data; if (hw->ops.mmap) return hw->ops.mmap(hw, file, vma); return -ENXIO; @@ -352,7 +364,7 @@ int snd_hwdep_new(snd_card_t * card, cha snd_assert(rhwdep != NULL, return -EINVAL); *rhwdep = NULL; snd_assert(card != NULL, return -ENXIO); - hwdep = snd_magic_kcalloc(snd_hwdep_t, 0, GFP_KERNEL); + hwdep = kcalloc(1, sizeof(*hwdep), GFP_KERNEL); if (hwdep == NULL) return -ENOMEM; hwdep->card = card; @@ -378,19 +390,19 @@ static int snd_hwdep_free(snd_hwdep_t *h snd_assert(hwdep != NULL, return -ENXIO); if (hwdep->private_free) hwdep->private_free(hwdep); - snd_magic_kfree(hwdep); + kfree(hwdep); return 0; } static int snd_hwdep_dev_free(snd_device_t *device) { - snd_hwdep_t *hwdep = snd_magic_cast(snd_hwdep_t, device->device_data, return -ENXIO); + snd_hwdep_t *hwdep = device->device_data; return snd_hwdep_free(hwdep); } static int snd_hwdep_dev_register(snd_device_t *device) { - snd_hwdep_t *hwdep = snd_magic_cast(snd_hwdep_t, device->device_data, return -ENXIO); + snd_hwdep_t *hwdep = device->device_data; int idx, err; char name[32]; @@ -433,7 +445,7 @@ static int snd_hwdep_dev_register(snd_de static int snd_hwdep_dev_unregister(snd_device_t *device) { - snd_hwdep_t *hwdep = snd_magic_cast(snd_hwdep_t, device->device_data, return -ENXIO); + snd_hwdep_t *hwdep = device->device_data; int idx; snd_assert(hwdep != NULL, return -ENXIO); --- linux-2.6.9-rc1/sound/core/info.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/sound/core/info.c 2004-09-02 20:51:53.000000000 -0700 @@ -139,7 +139,7 @@ static loff_t snd_info_entry_llseek(stru struct snd_info_entry *entry; loff_t ret; - data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + data = file->private_data; entry = data->entry; lock_kernel(); switch (entry->content) { @@ -183,7 +183,7 @@ static ssize_t snd_info_entry_read(struc size_t size = 0; loff_t pos; - data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + data = file->private_data; snd_assert(data != NULL, return -ENXIO); pos = *offset; if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) @@ -224,7 +224,7 @@ static ssize_t snd_info_entry_write(stru size_t size = 0; loff_t pos; - data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + data = file->private_data; snd_assert(data != NULL, return -ENXIO); entry = data->entry; pos = *offset; @@ -296,7 +296,7 @@ static int snd_info_entry_open(struct in goto __error; } } - data = snd_magic_kcalloc(snd_info_private_data_t, 0, GFP_KERNEL); + data = kcalloc(1, sizeof(*data), GFP_KERNEL); if (data == NULL) { err = -ENOMEM; goto __error; @@ -305,10 +305,9 @@ static int snd_info_entry_open(struct in switch (entry->content) { case SNDRV_INFO_CONTENT_TEXT: if (mode == O_RDONLY || mode == O_RDWR) { - buffer = (snd_info_buffer_t *) - snd_kcalloc(sizeof(snd_info_buffer_t), GFP_KERNEL); + buffer = kcalloc(1, sizeof(*buffer), GFP_KERNEL); if (buffer == NULL) { - snd_magic_kfree(data); + kfree(data); err = -ENOMEM; goto __error; } @@ -317,7 +316,7 @@ static int snd_info_entry_open(struct in buffer->buffer = vmalloc(buffer->len); if (buffer->buffer == NULL) { kfree(buffer); - snd_magic_kfree(data); + kfree(data); err = -ENOMEM; goto __error; } @@ -325,14 +324,13 @@ static int snd_info_entry_open(struct in data->rbuffer = buffer; } if (mode == O_WRONLY || mode == O_RDWR) { - buffer = (snd_info_buffer_t *) - snd_kcalloc(sizeof(snd_info_buffer_t), GFP_KERNEL); + buffer = kcalloc(1, sizeof(*buffer), GFP_KERNEL); if (buffer == NULL) { if (mode == O_RDWR) { vfree(data->rbuffer->buffer); kfree(data->rbuffer); } - snd_magic_kfree(data); + kfree(data); err = -ENOMEM; goto __error; } @@ -345,7 +343,7 @@ static int snd_info_entry_open(struct in kfree(data->rbuffer); } kfree(buffer); - snd_magic_kfree(data); + kfree(data); err = -ENOMEM; goto __error; } @@ -357,7 +355,7 @@ static int snd_info_entry_open(struct in if (entry->c.ops->open) { if ((err = entry->c.ops->open(entry, mode, &data->file_private_data)) < 0) { - snd_magic_kfree(data); + kfree(data); goto __error; } } @@ -389,7 +387,7 @@ static int snd_info_entry_release(struct int mode; mode = file->f_flags & O_ACCMODE; - data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + data = file->private_data; entry = data->entry; switch (entry->content) { case SNDRV_INFO_CONTENT_TEXT: @@ -417,7 +415,7 @@ static int snd_info_entry_release(struct break; } module_put(entry->module); - snd_magic_kfree(data); + kfree(data); return 0; } @@ -427,7 +425,7 @@ static unsigned int snd_info_entry_poll( struct snd_info_entry *entry; unsigned int mask; - data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + data = file->private_data; if (data == NULL) return 0; entry = data->entry; @@ -447,13 +445,13 @@ static unsigned int snd_info_entry_poll( return mask; } -static int snd_info_entry_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static inline int _snd_info_entry_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) { snd_info_private_data_t *data; struct snd_info_entry *entry; - data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + data = file->private_data; if (data == NULL) return 0; entry = data->entry; @@ -468,13 +466,24 @@ static int snd_info_entry_ioctl(struct i return -ENOTTY; } +/* FIXME: need to unlock BKL to allow preemption */ +static int snd_info_entry_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err; + unlock_kernel(); + err = _snd_info_entry_ioctl(inode, file, cmd, arg); + lock_kernel(); + return err; +} + static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma) { struct inode *inode = file->f_dentry->d_inode; snd_info_private_data_t *data; struct snd_info_entry *entry; - data = snd_magic_cast(snd_info_private_data_t, file->private_data, return -ENXIO); + data = file->private_data; if (data == NULL) return 0; entry = data->entry; @@ -744,12 +753,12 @@ char *snd_info_get_str(char *dest, char static snd_info_entry_t *snd_info_create_entry(const char *name) { snd_info_entry_t *entry; - entry = snd_magic_kcalloc(snd_info_entry_t, 0, GFP_KERNEL); + entry = kcalloc(1, sizeof(*entry), GFP_KERNEL); if (entry == NULL) return NULL; entry->name = snd_kmalloc_strdup(name, GFP_KERNEL); if (entry->name == NULL) { - snd_magic_kfree(entry); + kfree(entry); return NULL; } entry->mode = S_IFREG | S_IRUGO; @@ -805,27 +814,27 @@ snd_info_entry_t *snd_info_create_card_e static int snd_info_dev_free_entry(snd_device_t *device) { - snd_info_entry_t *entry = snd_magic_cast(snd_info_entry_t, device->device_data, return -ENXIO); + snd_info_entry_t *entry = device->device_data; snd_info_free_entry(entry); return 0; } static int snd_info_dev_register_entry(snd_device_t *device) { - snd_info_entry_t *entry = snd_magic_cast(snd_info_entry_t, device->device_data, return -ENXIO); + snd_info_entry_t *entry = device->device_data; return snd_info_register(entry); } static int snd_info_dev_disconnect_entry(snd_device_t *device) { - snd_info_entry_t *entry = snd_magic_cast(snd_info_entry_t, device->device_data, return -ENXIO); + snd_info_entry_t *entry = device->device_data; entry->disconnected = 1; return 0; } static int snd_info_dev_unregister_entry(snd_device_t *device) { - snd_info_entry_t *entry = snd_magic_cast(snd_info_entry_t, device->device_data, return -ENXIO); + snd_info_entry_t *entry = device->device_data; return snd_info_unregister(entry); } @@ -887,7 +896,7 @@ void snd_info_free_entry(snd_info_entry_ kfree((char *)entry->name); if (entry->private_free) entry->private_free(entry); - snd_magic_kfree(entry); + kfree(entry); } /** --- linux-2.6.9-rc1/sound/core/info_oss.c 2004-08-24 00:02:20.000000000 -0700 +++ 25/sound/core/info_oss.c 2004-09-02 20:51:53.000000000 -0700 @@ -134,16 +134,4 @@ int snd_info_minor_unregister(void) return 0; } -#else - -int snd_info_minor_register(void) -{ - return 0; -} - -int snd_info_minor_unregister(void) -{ - return 0; -} - #endif /* CONFIG_SND_OSSEMUL */ --- linux-2.6.9-rc1/sound/core/init.c 2004-08-24 00:02:22.000000000 -0700 +++ 25/sound/core/init.c 2004-09-02 20:51:53.000000000 -0700 @@ -73,7 +73,7 @@ snd_card_t *snd_card_new(int idx, const if (extra_size < 0) extra_size = 0; - card = (snd_card_t *) snd_kcalloc(sizeof(snd_card_t) + extra_size, GFP_KERNEL); + card = kcalloc(1, sizeof(*card) + extra_size, GFP_KERNEL); if (card == NULL) return NULL; if (xid) { @@ -800,6 +800,8 @@ int snd_card_pci_resume(struct pci_dev * return 0; if (card->power_state == SNDRV_CTL_POWER_D0) return 0; + /* restore the PCI config space */ + pci_restore_state(dev, dev->saved_config_space); /* FIXME: correct state value? */ return card->pm_resume(card, 0); } --- linux-2.6.9-rc1/sound/core/ioctl32/ioctl32.c 2004-08-24 00:01:55.000000000 -0700 +++ 25/sound/core/ioctl32/ioctl32.c 2004-09-02 20:51:53.000000000 -0700 @@ -257,7 +257,7 @@ static int get_ctl_type(struct file *fil snd_ctl_elem_info_t info; int err; - ctl = snd_magic_cast(snd_ctl_file_t, file->private_data, return -ENXIO); + ctl = file->private_data; down_read(&ctl->card->controls_rwsem); kctl = snd_ctl_find_id(ctl->card, id); --- linux-2.6.9-rc1/sound/core/ioctl32/pcm32.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/sound/core/ioctl32/pcm32.c 2004-09-02 20:51:53.000000000 -0700 @@ -69,7 +69,7 @@ struct sndrv_pcm_hw_params32 { unsigned char reserved[64]; } __attribute__((packed)); -#define numberof(array) (sizeof(array)/sizeof(array[0])) +#define numberof(array) ARRAY_SIZE(array) #define CVT_sndrv_pcm_hw_params()\ {\ @@ -235,7 +235,7 @@ static int _snd_ioctl32_xfern(unsigned i /* FIXME: need to check whether fop->ioctl is sane */ - pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + pcm_file = file->private_data; substream = pcm_file->substream; snd_assert(substream != NULL && substream->runtime, return -ENXIO); @@ -313,7 +313,7 @@ static void snd_pcm_hw_convert_from_old_ memset(params, 0, sizeof(*params)); params->flags = oparams->flags; - for (i = 0; i < sizeof(oparams->masks) / sizeof(unsigned int); i++) + for (i = 0; i < ARRAY_SIZE(oparams->masks); i++) params->masks[i].bits[0] = oparams->masks[i]; memcpy(params->intervals, oparams->intervals, sizeof(oparams->intervals)); params->rmask = __OLD_TO_NEW_MASK(oparams->rmask); @@ -331,7 +331,7 @@ static void snd_pcm_hw_convert_to_old_pa memset(oparams, 0, sizeof(*oparams)); oparams->flags = params->flags; - for (i = 0; i < sizeof(oparams->masks) / sizeof(unsigned int); i++) + for (i = 0; i < ARRAY_SIZE(oparams->masks); i++) oparams->masks[i] = params->masks[i].bits[0]; memcpy(oparams->intervals, params->intervals, sizeof(oparams->intervals)); oparams->rmask = __NEW_TO_OLD_MASK(params->rmask); @@ -350,8 +350,8 @@ static int _snd_ioctl32_pcm_hw_params_ol mm_segment_t oldseg; int err; - data32 = snd_kcalloc(sizeof(*data32), GFP_KERNEL); - data = snd_kcalloc(sizeof(*data), GFP_KERNEL); + data32 = kcalloc(1, sizeof(*data32), GFP_KERNEL); + data = kcalloc(1, sizeof(*data), GFP_KERNEL); if (data32 == NULL || data == NULL) { err = -ENOMEM; goto __end; @@ -379,6 +379,45 @@ static int _snd_ioctl32_pcm_hw_params_ol return err; } +struct sndrv_pcm_mmap_status32 { + s32 state; + s32 pad1; + u32 hw_ptr; + struct compat_timespec tstamp; + s32 suspended_state; +} __attribute__((packed)); + +struct sndrv_pcm_mmap_control32 { + u32 appl_ptr; + u32 avail_min; +} __attribute__((packed)); + +struct sndrv_pcm_sync_ptr32 { + u32 flags; + union { + struct sndrv_pcm_mmap_status32 status; + unsigned char reserved[64]; + } s; + union { + struct sndrv_pcm_mmap_control32 control; + unsigned char reserved[64]; + } c; +} __attribute__((packed)); + +#define CVT_sndrv_pcm_sync_ptr()\ +{\ + COPY(flags);\ + COPY(s.status.state);\ + COPY(s.status.pad1);\ + COPY(s.status.hw_ptr);\ + COPY(s.status.tstamp.tv_sec);\ + COPY(s.status.tstamp.tv_nsec);\ + COPY(s.status.suspended_state);\ + COPY(c.control.appl_ptr);\ + COPY(c.control.avail_min);\ +} + +DEFINE_ALSA_IOCTL_BIG(pcm_sync_ptr); /* */ @@ -396,6 +435,7 @@ DEFINE_ALSA_IOCTL_ENTRY(pcm_readi, xferi DEFINE_ALSA_IOCTL_ENTRY(pcm_writei, xferi, SNDRV_PCM_IOCTL_WRITEI_FRAMES); DEFINE_ALSA_IOCTL_ENTRY(pcm_readn, xfern, SNDRV_PCM_IOCTL_READN_FRAMES); DEFINE_ALSA_IOCTL_ENTRY(pcm_writen, xfern, SNDRV_PCM_IOCTL_WRITEN_FRAMES); +DEFINE_ALSA_IOCTL_ENTRY(pcm_sync_ptr, pcm_sync_ptr, SNDRV_PCM_IOCTL_SYNC_PTR); /* @@ -416,6 +456,7 @@ enum { SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct sndrv_xfern32), SNDRV_PCM_IOCTL_HW_REFINE_OLD32 = _IOWR('A', 0x10, struct sndrv_pcm_hw_params_old32), SNDRV_PCM_IOCTL_HW_PARAMS_OLD32 = _IOWR('A', 0x11, struct sndrv_pcm_hw_params_old32), + SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct sndrv_pcm_sync_ptr), }; @@ -431,6 +472,8 @@ struct ioctl32_mapper pcm_mappers[] = { { SNDRV_PCM_IOCTL_SW_PARAMS32, AP(pcm_sw_params) }, { SNDRV_PCM_IOCTL_STATUS32, AP(pcm_status) }, { SNDRV_PCM_IOCTL_DELAY32, AP(pcm_delay) }, + MAP_COMPAT(SNDRV_PCM_IOCTL_HWSYNC), + { SNDRV_PCM_IOCTL_SYNC_PTR32, AP(pcm_sync_ptr) }, { SNDRV_PCM_IOCTL_CHANNEL_INFO32, AP(pcm_channel_info) }, MAP_COMPAT(SNDRV_PCM_IOCTL_PREPARE), MAP_COMPAT(SNDRV_PCM_IOCTL_RESET), --- linux-2.6.9-rc1/sound/core/memalloc.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/sound/core/memalloc.c 2004-09-02 20:51:53.000000000 -0700 @@ -45,15 +45,19 @@ MODULE_LICENSE("GPL"); #ifndef SNDRV_CARDS #define SNDRV_CARDS 8 #endif + +/* FIXME: so far only some PCI devices have the preallocation table */ +#ifdef CONFIG_PCI static int enable[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1}; static int boot_devs; module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable cards to allocate buffers."); +#endif /* */ -void *snd_malloc_sgbuf_pages(const struct snd_dma_device *dev, +void *snd_malloc_sgbuf_pages(struct device *device, size_t size, struct snd_dma_buffer *dmab, size_t *res_size); int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab); @@ -66,9 +70,8 @@ static LIST_HEAD(mem_list_head); /* buffer preservation list */ struct snd_mem_list { - struct snd_dma_device dev; struct snd_dma_buffer buffer; - int used; + unsigned int id; struct list_head list; }; @@ -148,6 +151,10 @@ static void *snd_dma_hack_alloc_coherent #endif /* arch */ +#if ! defined(__arm__) +#define NEED_RESERVE_PAGES +#endif + /* * * Generic memory allocators @@ -156,22 +163,28 @@ static void *snd_dma_hack_alloc_coherent static long snd_allocated_pages; /* holding the number of allocated pages */ -static void mark_pages(void *res, int order) +static inline void inc_snd_pages(int order) +{ + snd_allocated_pages += 1 << order; +} + +static inline void dec_snd_pages(int order) +{ + snd_allocated_pages -= 1 << order; +} + +static void mark_pages(struct page *page, int order) { - struct page *page = virt_to_page(res); struct page *last_page = page + (1 << order); while (page < last_page) SetPageReserved(page++); - snd_allocated_pages += 1 << order; } -static void unmark_pages(void *res, int order) +static void unmark_pages(struct page *page, int order) { - struct page *page = virt_to_page(res); struct page *last_page = page + (1 << order); while (page < last_page) ClearPageReserved(page++); - snd_allocated_pages -= 1 << order; } /** @@ -190,43 +203,15 @@ void *snd_malloc_pages(size_t size, unsi snd_assert(size > 0, return NULL); snd_assert(gfp_flags != 0, return NULL); - for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + pg = get_order(size); if ((res = (void *) __get_free_pages(gfp_flags, pg)) != NULL) { - mark_pages(res, pg); + mark_pages(virt_to_page(res), pg); + inc_snd_pages(pg); } return res; } /** - * snd_malloc_pages_fallback - allocate pages with the given size with fallback - * @size: the requested size to allocate in bytes - * @gfp_flags: the allocation conditions, GFP_XXX - * @res_size: the pointer to store the size of buffer actually allocated - * - * Allocates the physically contiguous pages with the given request - * size. When no space is left, this function reduces the size and - * tries to allocate again. The size actually allocated is stored in - * res_size argument. - * - * Returns the pointer of the buffer, or NULL if no enoguh memory. - */ -void *snd_malloc_pages_fallback(size_t size, unsigned int gfp_flags, size_t *res_size) -{ - void *res; - - snd_assert(size > 0, return NULL); - snd_assert(res_size != NULL, return NULL); - do { - if ((res = snd_malloc_pages(size, gfp_flags)) != NULL) { - *res_size = size; - return res; - } - size >>= 1; - } while (size >= PAGE_SIZE); - return NULL; -} - -/** * snd_free_pages - release the pages * @ptr: the buffer pointer to release * @size: the allocated buffer size @@ -239,8 +224,9 @@ void snd_free_pages(void *ptr, size_t si if (ptr == NULL) return; - for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); - unmark_pages(ptr, pg); + pg = get_order(size); + dec_snd_pages(pg); + unmark_pages(virt_to_page(ptr), pg); free_pages((unsigned long) ptr, pg); } @@ -250,6 +236,7 @@ void snd_free_pages(void *ptr, size_t si * */ +/* allocate the coherent DMA pages */ static void *snd_malloc_dev_pages(struct device *dev, size_t size, dma_addr_t *dma) { int pg; @@ -263,28 +250,17 @@ static void *snd_malloc_dev_pages(struct if (pg > 0) gfp_flags |= __GFP_NOWARN; res = dma_alloc_coherent(dev, PAGE_SIZE << pg, dma, gfp_flags); - if (res != NULL) - mark_pages(res, pg); + if (res != NULL) { +#ifdef NEED_RESERVE_PAGES + mark_pages(virt_to_page(res), pg); /* should be dma_to_page() */ +#endif + inc_snd_pages(pg); + } return res; } -static void *snd_malloc_dev_pages_fallback(struct device *dev, size_t size, - dma_addr_t *dma, size_t *res_size) -{ - void *res; - - snd_assert(res_size != NULL, return NULL); - do { - if ((res = snd_malloc_dev_pages(dev, size, dma)) != NULL) { - *res_size = size; - return res; - } - size >>= 1; - } while (size >= PAGE_SIZE); - return NULL; -} - +/* free the coherent DMA pages */ static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr, dma_addr_t dma) { @@ -293,7 +269,10 @@ static void snd_free_dev_pages(struct de if (ptr == NULL) return; pg = get_order(size); - unmark_pages(ptr, pg); + dec_snd_pages(pg); +#ifdef NEED_RESERVE_PAGES + unmark_pages(virt_to_page(ptr), pg); /* should be dma_to_page() */ +#endif dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma); } @@ -308,30 +287,13 @@ static void *snd_malloc_sbus_pages(struc snd_assert(size > 0, return NULL); snd_assert(dma_addr != NULL, return NULL); - for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); + pg = get_order(size); res = sbus_alloc_consistent(sdev, PAGE_SIZE * (1 << pg), dma_addr); - if (res != NULL) { - mark_pages(res, pg); - } + if (res != NULL) + inc_snd_pages(pg); return res; } -static void *snd_malloc_sbus_pages_fallback(struct device *dev, size_t size, - dma_addr_t *dma_addr, size_t *res_size) -{ - void *res; - - snd_assert(res_size != NULL, return NULL); - do { - if ((res = snd_malloc_sbus_pages(dev, size, dma_addr)) != NULL) { - *res_size = size; - return res; - } - size >>= 1; - } while (size >= PAGE_SIZE); - return NULL; -} - static void snd_free_sbus_pages(struct device *dev, size_t size, void *ptr, dma_addr_t dma_addr) { @@ -340,8 +302,8 @@ static void snd_free_sbus_pages(struct d if (ptr == NULL) return; - for (pg = 0; PAGE_SIZE * (1 << pg) < size; pg++); - unmark_pages(ptr, pg); + pg = get_order(size); + dec_snd_pages(pg); sbus_free_consistent(sdev, PAGE_SIZE * (1 << pg), ptr, dma_addr); } @@ -354,24 +316,10 @@ static void snd_free_sbus_pages(struct d */ -/* - * compare the two devices - * returns non-zero if matched. - */ -static int compare_device(const struct snd_dma_device *a, const struct snd_dma_device *b, int allow_unused) -{ - if (a->type != b->type) - return 0; - if (a->id != b->id) { - if (! allow_unused || (a->id != SNDRV_DMA_DEVICE_UNUSED && b->id != SNDRV_DMA_DEVICE_UNUSED)) - return 0; - } - return a->dev == b->dev; -} - /** * snd_dma_alloc_pages - allocate the buffer area according to the given type - * @dev: the buffer device info + * @type: the DMA buffer type + * @device: the device pointer * @size: the buffer size to allocate * @dmab: buffer allocation record to store the allocated data * @@ -381,32 +329,33 @@ static int compare_device(const struct s * Returns zero if the buffer with the given size is allocated successfuly, * other a negative value at error. */ -int snd_dma_alloc_pages(const struct snd_dma_device *dev, size_t size, +int snd_dma_alloc_pages(int type, struct device *device, size_t size, struct snd_dma_buffer *dmab) { - snd_assert(dev != NULL, return -ENXIO); snd_assert(size > 0, return -ENXIO); snd_assert(dmab != NULL, return -ENXIO); + dmab->dev.type = type; + dmab->dev.dev = device; dmab->bytes = 0; - switch (dev->type) { + switch (type) { case SNDRV_DMA_TYPE_CONTINUOUS: - dmab->area = snd_malloc_pages(size, (unsigned long)dev->dev); + dmab->area = snd_malloc_pages(size, (unsigned long)device); dmab->addr = 0; break; #ifdef CONFIG_SBUS case SNDRV_DMA_TYPE_SBUS: - dmab->area = snd_malloc_sbus_pages(dev->dev, size, &dmab->addr); + dmab->area = snd_malloc_sbus_pages(device, size, &dmab->addr); break; #endif case SNDRV_DMA_TYPE_DEV: - dmab->area = snd_malloc_dev_pages(dev->dev, size, &dmab->addr); + dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr); break; case SNDRV_DMA_TYPE_DEV_SG: - snd_malloc_sgbuf_pages(dev, size, dmab, NULL); + snd_malloc_sgbuf_pages(device, size, dmab, NULL); break; default: - printk(KERN_ERR "snd-malloc: invalid device type %d\n", dev->type); + printk(KERN_ERR "snd-malloc: invalid device type %d\n", type); dmab->area = NULL; dmab->addr = 0; return -ENXIO; @@ -419,7 +368,8 @@ int snd_dma_alloc_pages(const struct snd /** * snd_dma_alloc_pages_fallback - allocate the buffer area according to the given type with fallback - * @dev: the buffer device info + * @type: the DMA buffer type + * @device: the device pointer * @size: the buffer size to allocate * @dmab: buffer allocation record to store the allocated data * @@ -431,35 +381,20 @@ int snd_dma_alloc_pages(const struct snd * Returns zero if the buffer with the given size is allocated successfuly, * other a negative value at error. */ -int snd_dma_alloc_pages_fallback(const struct snd_dma_device *dev, size_t size, +int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size, struct snd_dma_buffer *dmab) { - snd_assert(dev != NULL, return -ENXIO); + int err; + snd_assert(size > 0, return -ENXIO); snd_assert(dmab != NULL, return -ENXIO); - dmab->bytes = 0; - switch (dev->type) { - case SNDRV_DMA_TYPE_CONTINUOUS: - dmab->area = snd_malloc_pages_fallback(size, (unsigned long)dev->dev, &dmab->bytes); - dmab->addr = 0; - break; -#ifdef CONFIG_SBUS - case SNDRV_DMA_TYPE_SBUS: - dmab->area = snd_malloc_sbus_pages_fallback(dev->dev, size, &dmab->addr, &dmab->bytes); - break; -#endif - case SNDRV_DMA_TYPE_DEV: - dmab->area = snd_malloc_dev_pages_fallback(dev->dev, size, &dmab->addr, &dmab->bytes); - break; - case SNDRV_DMA_TYPE_DEV_SG: - snd_malloc_sgbuf_pages(dev, size, dmab, &dmab->bytes); - break; - default: - printk(KERN_ERR "snd-malloc: invalid device type %d\n", dev->type); - dmab->area = NULL; - dmab->addr = 0; - return -ENXIO; + while ((err = snd_dma_alloc_pages(type, device, size, dmab)) < 0) { + if (err != -ENOMEM) + return err; + size >>= 1; + if (size <= PAGE_SIZE) + return -ENOMEM; } if (! dmab->area) return -ENOMEM; @@ -469,152 +404,87 @@ int snd_dma_alloc_pages_fallback(const s /** * snd_dma_free_pages - release the allocated buffer - * @dev: the buffer device info - * @dmbab: the buffer allocation record to release + * @dmab: the buffer allocation record to release * * Releases the allocated buffer via snd_dma_alloc_pages(). */ -void snd_dma_free_pages(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab) +void snd_dma_free_pages(struct snd_dma_buffer *dmab) { - switch (dev->type) { + switch (dmab->dev.type) { case SNDRV_DMA_TYPE_CONTINUOUS: snd_free_pages(dmab->area, dmab->bytes); break; #ifdef CONFIG_SBUS case SNDRV_DMA_TYPE_SBUS: - snd_free_sbus_pages(dev->dev, dmab->bytes, dmab->area, dmab->addr); + snd_free_sbus_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr); break; #endif case SNDRV_DMA_TYPE_DEV: - snd_free_dev_pages(dev->dev, dmab->bytes, dmab->area, dmab->addr); + snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr); break; case SNDRV_DMA_TYPE_DEV_SG: snd_free_sgbuf_pages(dmab); break; default: - printk(KERN_ERR "snd-malloc: invalid device type %d\n", dev->type); + printk(KERN_ERR "snd-malloc: invalid device type %d\n", dmab->dev.type); } } -/* - * search for the device - */ -static struct snd_mem_list *mem_list_find(const struct snd_dma_device *dev, int search_empty) -{ - struct list_head *p; - struct snd_mem_list *mem; - - list_for_each(p, &mem_list_head) { - mem = list_entry(p, struct snd_mem_list, list); - if (mem->used && search_empty) - continue; - if (compare_device(&mem->dev, dev, search_empty)) - return mem; - } - return NULL; -} - /** * snd_dma_get_reserved - get the reserved buffer for the given device - * @dev: the buffer device info * @dmab: the buffer allocation record to store + * @id: the buffer id * * Looks for the reserved-buffer list and re-uses if the same buffer - * is found in the list. When the buffer is found, it's marked as used. - * For unmarking the buffer, call snd_dma_free_reserved(). + * is found in the list. When the buffer is found, it's removed from the free list. * * Returns the size of buffer if the buffer is found, or zero if not found. */ -size_t snd_dma_get_reserved(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab) +size_t snd_dma_get_reserved_buf(struct snd_dma_buffer *dmab, unsigned int id) { + struct list_head *p; struct snd_mem_list *mem; - snd_assert(dev && dmab, return 0); + snd_assert(dmab, return 0); down(&list_mutex); - mem = mem_list_find(dev, 1); - if (mem) { - mem->used = 1; - mem->dev = *dev; - *dmab = mem->buffer; - up(&list_mutex); - return dmab->bytes; + list_for_each(p, &mem_list_head) { + mem = list_entry(p, struct snd_mem_list, list); + if (mem->id == id && + ! memcmp(&mem->buffer.dev, &dmab->dev, sizeof(dmab->dev))) { + list_del(p); + *dmab = mem->buffer; + kfree(mem); + up(&list_mutex); + return dmab->bytes; + } } up(&list_mutex); return 0; } /** - * snd_dma_free_reserved - unmark the reserved buffer - * @dev: the buffer device info - * - * Looks for the matching reserved buffer and erases the mark on it - * if found. - * - * Returns zero. - */ -int snd_dma_free_reserved(const struct snd_dma_device *dev) -{ - struct snd_mem_list *mem; - - snd_assert(dev, return -EINVAL); - down(&list_mutex); - mem = mem_list_find(dev, 0); - if (mem) - mem->used = 0; - up(&list_mutex); - return 0; -} - -/** - * snd_dma_set_reserved - reserve the buffer - * @dev: the buffer device info + * snd_dma_reserve_buf - reserve the buffer * @dmab: the buffer to reserve + * @id: the buffer id * * Reserves the given buffer as a reserved buffer. - * When an old reserved buffer already exists, the old one is released - * and replaced with the new one. - * - * When NULL buffer pointer or zero buffer size is given, the existing - * buffer is released and the entry is removed. * * Returns zero if successful, or a negative code at error. */ -int snd_dma_set_reserved(const struct snd_dma_device *dev, struct snd_dma_buffer *dmab) +int snd_dma_reserve_buf(struct snd_dma_buffer *dmab, unsigned int id) { struct snd_mem_list *mem; - snd_assert(dev, return -EINVAL); + snd_assert(dmab, return -EINVAL); + mem = kmalloc(sizeof(*mem), GFP_KERNEL); + if (! mem) + return -ENOMEM; down(&list_mutex); - mem = mem_list_find(dev, 0); - if (mem) { - if (mem->used) - printk(KERN_WARNING "snd-page-alloc: releasing the used block (type=%d, id=0x%x\n", mem->dev.type, mem->dev.id); - snd_dma_free_pages(dev, &mem->buffer); - if (! dmab || ! dmab->bytes) { - /* remove the entry */ - list_del(&mem->list); - kfree(mem); - up(&list_mutex); - return 0; - } - } else { - if (! dmab || ! dmab->bytes) { - up(&list_mutex); - return 0; - } - mem = kmalloc(sizeof(*mem), GFP_KERNEL); - if (! mem) { - up(&list_mutex); - return -ENOMEM; - } - mem->dev = *dev; - list_add_tail(&mem->list, &mem_list_head); - } - /* store the entry */ - mem->used = 1; mem->buffer = *dmab; + mem->id = id; + list_add_tail(&mem->list, &mem_list_head); up(&list_mutex); return 0; } @@ -632,7 +502,7 @@ static void free_all_reserved_pages(void p = mem_list_head.next; mem = list_entry(p, struct snd_mem_list, list); list_del(p); - snd_dma_free_pages(&mem->dev, &mem->buffer); + snd_dma_free_pages(&mem->buffer); kfree(mem); } up(&list_mutex); @@ -654,7 +524,7 @@ struct prealloc_dev { unsigned int buffers; }; -#define HAMMERFALL_BUFFER_SIZE (16*1024*4*(26+1)) +#define HAMMERFALL_BUFFER_SIZE (16*1024*4*(26+1)+0x10000) static struct prealloc_dev prealloc_devices[] __initdata = { { @@ -676,17 +546,6 @@ static struct prealloc_dev prealloc_devi { }, /* terminator */ }; -/* - * compose a snd_dma_device struct for the PCI device - */ -static inline void snd_dma_device_pci(struct snd_dma_device *dev, struct pci_dev *pci, unsigned int id) -{ - memset(dev, 0, sizeof(*dev)); - dev->type = SNDRV_DMA_TYPE_DEV; - dev->dev = snd_dma_pci_data(pci); - dev->id = id; -} - static void __init preallocate_cards(void) { struct pci_dev *pci = NULL; @@ -716,22 +575,13 @@ static void __init preallocate_cards(voi continue; } for (i = 0; i < dev->buffers; i++) { - struct snd_mem_list *mem; - mem = kmalloc(sizeof(*mem), GFP_KERNEL); - if (! mem) { - printk(KERN_WARNING "snd-page-alloc: can't malloc memlist\n"); - break; - } - memset(mem, 0, sizeof(*mem)); - snd_dma_device_pci(&mem->dev, pci, SNDRV_DMA_DEVICE_UNUSED); - if (snd_dma_alloc_pages(&mem->dev, dev->size, &mem->buffer) < 0) { + struct snd_dma_buffer dmab; + memset(&dmab, 0, sizeof(dmab)); + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), + dev->size, &dmab) < 0) printk(KERN_WARNING "snd-page-alloc: cannot allocate buffer pages (size = %d)\n", dev->size); - kfree(mem); - } else { - down(&list_mutex); - list_add_tail(&mem->list, &mem_list_head); - up(&list_mutex); - } + else + snd_dma_reserve_buf(&dmab, snd_dma_pci_buf_id(pci)); } } } @@ -752,48 +602,22 @@ static int snd_mem_proc_read(char *page, struct list_head *p; struct snd_mem_list *mem; int devno; + static char *types[] = { "UNKNOWN", "CONT", "DEV", "DEV-SG", "SBUS" }; down(&list_mutex); - len += sprintf(page + len, "pages : %li bytes (%li pages per %likB)\n", - pages * PAGE_SIZE, pages, PAGE_SIZE / 1024); + len += snprintf(page + len, count - len, + "pages : %li bytes (%li pages per %likB)\n", + pages * PAGE_SIZE, pages, PAGE_SIZE / 1024); devno = 0; list_for_each(p, &mem_list_head) { mem = list_entry(p, struct snd_mem_list, list); devno++; - len += sprintf(page + len, "buffer %d : ", devno); - if (mem->dev.id == SNDRV_DMA_DEVICE_UNUSED) - len += sprintf(page + len, "UNUSED"); - else - len += sprintf(page + len, "ID %08x", mem->dev.id); - len += sprintf(page + len, " : type "); - switch (mem->dev.type) { - case SNDRV_DMA_TYPE_CONTINUOUS: - len += sprintf(page + len, "CONT [%p]", mem->dev.dev); - break; -#ifdef CONFIG_SBUS - case SNDRV_DMA_TYPE_SBUS: - { - struct sbus_dev *sdev = (struct sbus_dev *)(mem->dev.dev); - len += sprintf(page + len, "SBUS [%x]", sdev->slot); - } - break; -#endif - case SNDRV_DMA_TYPE_DEV: - case SNDRV_DMA_TYPE_DEV_SG: - if (mem->dev.dev) { - len += sprintf(page + len, "%s [%s]", - mem->dev.type == SNDRV_DMA_TYPE_DEV_SG ? "DEV-SG" : "DEV", - mem->dev.dev->bus_id); - } else - len += sprintf(page + len, "ISA"); - break; - default: - len += sprintf(page + len, "UNKNOWN"); - break; - } - len += sprintf(page + len, "\n addr = 0x%lx, size = %d bytes, used = %s\n", - (unsigned long)mem->buffer.addr, (int)mem->buffer.bytes, - mem->used ? "yes" : "no"); + len += snprintf(page + len, count - len, + "buffer %d : ID %08x : type %s\n", + devno, mem->id, types[mem->buffer.dev.type]); + len += snprintf(page + len, count - len, + " addr = 0x%lx, size = %d bytes\n", + (unsigned long)mem->buffer.addr, (int)mem->buffer.bytes); } up(&list_mutex); return len; @@ -833,10 +657,8 @@ EXPORT_SYMBOL(snd_dma_alloc_pages); EXPORT_SYMBOL(snd_dma_alloc_pages_fallback); EXPORT_SYMBOL(snd_dma_free_pages); -EXPORT_SYMBOL(snd_dma_get_reserved); -EXPORT_SYMBOL(snd_dma_free_reserved); -EXPORT_SYMBOL(snd_dma_set_reserved); +EXPORT_SYMBOL(snd_dma_get_reserved_buf); +EXPORT_SYMBOL(snd_dma_reserve_buf); EXPORT_SYMBOL(snd_malloc_pages); -EXPORT_SYMBOL(snd_malloc_pages_fallback); EXPORT_SYMBOL(snd_free_pages); --- linux-2.6.9-rc1/sound/core/memory.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/sound/core/memory.c 2004-09-02 20:51:53.000000000 -0700 @@ -71,8 +71,7 @@ void snd_memory_done(void) snd_printk(KERN_ERR "Not freed snd_alloc_kmalloc = %li\n", snd_alloc_kmalloc); if (snd_alloc_vmalloc > 0) snd_printk(KERN_ERR "Not freed snd_alloc_vmalloc = %li\n", snd_alloc_vmalloc); - for (head = snd_alloc_kmalloc_list.prev; - head != &snd_alloc_kmalloc_list; head = head->prev) { + list_for_each_prev(head, &snd_alloc_kmalloc_list) { t = list_entry(head, struct snd_alloc_track, list); if (t->magic != KMALLOC_MAGIC) { snd_printk(KERN_ERR "Corrupted kmalloc\n"); @@ -80,8 +79,7 @@ void snd_memory_done(void) } snd_printk(KERN_ERR "kmalloc(%ld) from %p not freed\n", (long) t->size, t->caller); } - for (head = snd_alloc_vmalloc_list.prev; - head != &snd_alloc_vmalloc_list; head = head->prev) { + list_for_each_prev(head, &snd_alloc_vmalloc_list) { t = list_entry(head, struct snd_alloc_track, list); if (t->magic != VMALLOC_MAGIC) { snd_printk(KERN_ERR "Corrupted vmalloc\n"); @@ -118,6 +116,17 @@ void *snd_hidden_kmalloc(size_t size, in return _snd_kmalloc(size, flags); } +void *snd_hidden_kcalloc(size_t n, size_t size, int flags) +{ + void *ret = NULL; + if (n != 0 && size > INT_MAX / n) + return ret; + ret = _snd_kmalloc(n * size, flags); + if (ret) + memset(ret, 0, n * size); + return ret; +} + void snd_hidden_kfree(const void *obj) { unsigned long flags; @@ -140,46 +149,6 @@ void snd_hidden_kfree(const void *obj) snd_wrapper_kfree(obj); } -void *_snd_magic_kcalloc(unsigned long magic, size_t size, int flags) -{ - unsigned long *ptr; - ptr = _snd_kmalloc(size + sizeof(unsigned long), flags); - if (ptr) { - *ptr++ = magic; - memset(ptr, 0, size); - } - return ptr; -} - -void *_snd_magic_kmalloc(unsigned long magic, size_t size, int flags) -{ - unsigned long *ptr; - ptr = _snd_kmalloc(size + sizeof(unsigned long), flags); - if (ptr) - *ptr++ = magic; - return ptr; -} - -void snd_magic_kfree(void *_ptr) -{ - unsigned long *ptr = _ptr; - if (ptr == NULL) { - snd_printk(KERN_WARNING "null snd_magic_kfree (called from %p)\n", __builtin_return_address(0)); - return; - } - *--ptr = 0; - { - struct snd_alloc_track *t; - t = snd_alloc_track_entry(ptr); - if (t->magic != KMALLOC_MAGIC) { - snd_printk(KERN_ERR "bad snd_magic_kfree (called from %p)\n", __builtin_return_address(0)); - return; - } - } - snd_hidden_kfree(ptr); - return; -} - void *snd_hidden_vmalloc(unsigned long size) { void *ptr; @@ -256,25 +225,6 @@ int __exit snd_memory_info_done(void) #endif /* CONFIG_SND_DEBUG_MEMORY */ /** - * snd_kcalloc - memory allocation and zero-clear - * @size: the size to allocate in bytes - * @flags: allocation conditions, GFP_XXX - * - * Allocates a memory chunk via kmalloc() and initializes it to zero. - * - * Returns the pointer, or NULL if no enoguh memory. - */ -void *snd_kcalloc(size_t size, int flags) -{ - void *ptr; - - ptr = _snd_kmalloc(size, flags); - if (ptr) - memset(ptr, 0, size); - return ptr; -} - -/** * snd_kmalloc_strdup - copy the string * @string: the original string * @flags: allocation conditions, GFP_XXX --- linux-2.6.9-rc1/sound/core/oss/mixer_oss.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/sound/core/oss/mixer_oss.c 2004-09-02 20:51:53.000000000 -0700 @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -51,7 +52,7 @@ static int snd_mixer_oss_open(struct ino err = snd_card_file_add(card, file); if (err < 0) return err; - fmixer = (snd_mixer_oss_file_t *)snd_kcalloc(sizeof(*fmixer), GFP_KERNEL); + fmixer = kcalloc(1, sizeof(*fmixer), GFP_KERNEL); if (fmixer == NULL) { snd_card_file_remove(card, file); return -ENOMEM; @@ -358,10 +359,16 @@ static int snd_mixer_oss_ioctl1(snd_mixe return -ENXIO; } +/* FIXME: need to unlock BKL to allow preemption */ int snd_mixer_oss_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - return snd_mixer_oss_ioctl1((snd_mixer_oss_file_t *) file->private_data, cmd, arg); + int err; + /* FIXME: need to unlock BKL to allow preemption */ + unlock_kernel(); + err = snd_mixer_oss_ioctl1((snd_mixer_oss_file_t *) file->private_data, cmd, arg); + lock_kernel(); + return err; } int snd_mixer_oss_ioctl_card(snd_card_t *card, unsigned int cmd, unsigned long arg) @@ -508,8 +515,8 @@ static void snd_mixer_oss_get_volume1_vo up_read(&card->controls_rwsem); return; } - uinfo = snd_kcalloc(sizeof(*uinfo), GFP_KERNEL); - uctl = snd_kcalloc(sizeof(*uctl), GFP_KERNEL); + uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL); + uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) goto __unalloc; snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc); @@ -544,8 +551,8 @@ static void snd_mixer_oss_get_volume1_sw up_read(&card->controls_rwsem); return; } - uinfo = snd_kcalloc(sizeof(*uinfo), GFP_KERNEL); - uctl = snd_kcalloc(sizeof(*uctl), GFP_KERNEL); + uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL); + uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) goto __unalloc; snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc); @@ -607,8 +614,8 @@ static void snd_mixer_oss_put_volume1_vo down_read(&card->controls_rwsem); if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) return; - uinfo = snd_kcalloc(sizeof(*uinfo), GFP_KERNEL); - uctl = snd_kcalloc(sizeof(*uctl), GFP_KERNEL); + uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL); + uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) goto __unalloc; snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc); @@ -646,8 +653,8 @@ static void snd_mixer_oss_put_volume1_sw up_read(&fmixer->card->controls_rwsem); return; } - uinfo = snd_kcalloc(sizeof(*uinfo), GFP_KERNEL); - uctl = snd_kcalloc(sizeof(*uctl), GFP_KERNEL); + uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL); + uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) goto __unalloc; snd_runtime_check(!kctl->info(kctl, uinfo), goto __unalloc); @@ -767,8 +774,8 @@ static int snd_mixer_oss_get_recsrc2(snd snd_ctl_elem_value_t *uctl; int err, idx; - uinfo = snd_kcalloc(sizeof(*uinfo), GFP_KERNEL); - uctl = snd_kcalloc(sizeof(*uctl), GFP_KERNEL); + uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL); + uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) { err = -ENOMEM; goto __unlock; @@ -814,8 +821,8 @@ static int snd_mixer_oss_put_recsrc2(snd int err; unsigned int idx; - uinfo = snd_kcalloc(sizeof(*uinfo), GFP_KERNEL); - uctl = snd_kcalloc(sizeof(*uctl), GFP_KERNEL); + uinfo = kcalloc(1, sizeof(*uinfo), GFP_KERNEL); + uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL); if (uinfo == NULL || uctl == NULL) { err = -ENOMEM; goto __unlock; @@ -968,8 +975,10 @@ static int snd_mixer_oss_build_input(snd snd_ctl_elem_info_t uinfo; memset(&uinfo, 0, sizeof(uinfo)); - if (kctl->info(kctl, &uinfo)) + if (kctl->info(kctl, &uinfo)) { + up_read(&mixer->card->controls_rwsem); return 0; + } strcpy(str, ptr->name); if (!strcmp(str, "Master")) strcpy(str, "Mix"); @@ -1060,7 +1069,7 @@ static char *oss_mixer_names[SNDRV_OSS_M static void snd_mixer_oss_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) { - snd_mixer_oss_t *mixer = snd_magic_cast(snd_mixer_oss_t, entry->private_data, return); + snd_mixer_oss_t *mixer = entry->private_data; int i; down(&mixer->reg_mutex); @@ -1084,7 +1093,7 @@ static void snd_mixer_oss_proc_read(snd_ static void snd_mixer_oss_proc_write(snd_info_entry_t *entry, snd_info_buffer_t * buffer) { - snd_mixer_oss_t *mixer = snd_magic_cast(snd_mixer_oss_t, entry->private_data, return); + snd_mixer_oss_t *mixer = entry->private_data; char line[128], str[32], idxstr[16], *cptr; int ch, idx; struct snd_mixer_oss_assign_table *tbl; @@ -1210,7 +1219,7 @@ static void snd_mixer_oss_build(snd_mixe }; unsigned int idx; - for (idx = 0; idx < sizeof(table) / sizeof(struct snd_mixer_oss_assign_table); idx++) + for (idx = 0; idx < ARRAY_SIZE(table); idx++) snd_mixer_oss_build_input(mixer, &table[idx], 0, 0); if (mixer->mask_recsrc) { mixer->get_recsrc = snd_mixer_oss_get_recsrc2; @@ -1224,7 +1233,7 @@ static void snd_mixer_oss_build(snd_mixe static int snd_mixer_oss_free1(void *private) { - snd_mixer_oss_t *mixer = snd_magic_cast(snd_mixer_oss_t, private, return -ENXIO); + snd_mixer_oss_t *mixer = private; snd_card_t * card; int idx; @@ -1237,7 +1246,7 @@ static int snd_mixer_oss_free1(void *pri if (chn->private_free) chn->private_free(chn); } - snd_magic_kfree(mixer); + kfree(mixer); return 0; } @@ -1249,7 +1258,7 @@ static int snd_mixer_oss_notify_handler( char name[128]; int idx, err; - mixer = snd_magic_kcalloc(snd_mixer_oss_t, sizeof(snd_mixer_oss_t), GFP_KERNEL); + mixer = kcalloc(2, sizeof(*mixer), GFP_KERNEL); if (mixer == NULL) return -ENOMEM; init_MUTEX(&mixer->reg_mutex); @@ -1259,7 +1268,7 @@ static int snd_mixer_oss_notify_handler( &snd_mixer_oss_reg, name)) < 0) { snd_printk("unable to register OSS mixer device %i:%i\n", card->number, 0); - snd_magic_kfree(mixer); + kfree(mixer); return err; } mixer->oss_dev_alloc = 1; --- linux-2.6.9-rc1/sound/core/oss/pcm_oss.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/sound/core/oss/pcm_oss.c 2004-09-02 20:51:53.000000000 -0700 @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -53,13 +54,10 @@ MODULE_DESCRIPTION("PCM OSS emulation fo MODULE_LICENSE("GPL"); module_param_array(dsp_map, int, boot_devs, 0444); MODULE_PARM_DESC(dsp_map, "PCM device number assigned to 1st OSS device."); -MODULE_PARM_SYNTAX(dsp_map, "default:0,skill:advanced"); module_param_array(adsp_map, int, boot_devs, 0444); MODULE_PARM_DESC(adsp_map, "PCM device number assigned to 2nd OSS device."); -MODULE_PARM_SYNTAX(adsp_map, "default:1,skill:advanced"); module_param(nonblock_open, bool, 0644); MODULE_PARM_DESC(nonblock_open, "Don't block opening busy PCM devices."); -MODULE_PARM_SYNTAX(nonblock_open, "default:0,skill:advanced"); MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM); MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_PCM1); @@ -1177,10 +1175,11 @@ static int snd_pcm_oss_get_formats(snd_p snd_pcm_substream_t *substream; int err; int direct; - snd_pcm_hw_params_t params; + snd_pcm_hw_params_t *params; unsigned int formats = 0; snd_mask_t format_mask; int fmt; + if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) return err; if (atomic_read(&substream->runtime->mmap_count)) { @@ -1194,10 +1193,14 @@ static int snd_pcm_oss_get_formats(snd_p AFMT_S16_LE | AFMT_S16_BE | AFMT_S8 | AFMT_U16_LE | AFMT_U16_BE; - _snd_pcm_hw_params_any(¶ms); - err = snd_pcm_hw_refine(substream, ¶ms); + params = kmalloc(sizeof(*params), GFP_KERNEL); + if (!params) + return -ENOMEM; + _snd_pcm_hw_params_any(params); + err = snd_pcm_hw_refine(substream, params); + format_mask = *hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + kfree(params); snd_assert(err >= 0, return err); - format_mask = *hw_param_mask(¶ms, SNDRV_PCM_HW_PARAM_FORMAT); for (fmt = 0; fmt < 32; ++fmt) { if (snd_mask_test(&format_mask, fmt)) { int f = snd_pcm_oss_format_to(fmt); @@ -1693,7 +1696,7 @@ static int snd_pcm_oss_release_file(snd_ snd_pcm_oss_release_substream(substream); snd_pcm_release_substream(substream); } - snd_magic_kfree(pcm_oss_file); + kfree(pcm_oss_file); return 0; } @@ -1712,7 +1715,7 @@ static int snd_pcm_oss_open_file(struct snd_assert(rpcm_oss_file != NULL, return -EINVAL); *rpcm_oss_file = NULL; - pcm_oss_file = snd_magic_kcalloc(snd_pcm_oss_file_t, 0, GFP_KERNEL); + pcm_oss_file = kcalloc(1, sizeof(*pcm_oss_file), GFP_KERNEL); if (pcm_oss_file == NULL) return -ENOMEM; @@ -1892,7 +1895,7 @@ static int snd_pcm_oss_release(struct in snd_pcm_substream_t *substream; snd_pcm_oss_file_t *pcm_oss_file; - pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO); + pcm_oss_file = file->private_data; substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; if (substream == NULL) substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; @@ -1908,14 +1911,14 @@ static int snd_pcm_oss_release(struct in return 0; } -static int snd_pcm_oss_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static inline int _snd_pcm_oss_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) { snd_pcm_oss_file_t *pcm_oss_file; int __user *p = (int __user *)arg; int res; - pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO); + pcm_oss_file = file->private_data; if (cmd == OSS_GETVERSION) return put_user(SNDRV_OSS_VERSION, p); if (cmd == OSS_ALSAEMULVER) @@ -2068,12 +2071,23 @@ static int snd_pcm_oss_ioctl(struct inod return -EINVAL; } +/* FIXME: need to unlock BKL to allow preemption */ +static int snd_pcm_oss_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err; + unlock_kernel(); + err = _snd_pcm_oss_ioctl(inode, file, cmd, arg); + lock_kernel(); + return err; +} + static ssize_t snd_pcm_oss_read(struct file *file, char __user *buf, size_t count, loff_t *offset) { snd_pcm_oss_file_t *pcm_oss_file; snd_pcm_substream_t *substream; - pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO); + pcm_oss_file = file->private_data; substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; if (substream == NULL) return -ENXIO; @@ -2094,7 +2108,7 @@ static ssize_t snd_pcm_oss_write(struct snd_pcm_substream_t *substream; long result; - pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO); + pcm_oss_file = file->private_data; substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; if (substream == NULL) return -ENXIO; @@ -2131,7 +2145,7 @@ static unsigned int snd_pcm_oss_poll(str unsigned int mask; snd_pcm_substream_t *psubstream = NULL, *csubstream = NULL; - pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return 0); + pcm_oss_file = file->private_data; psubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; @@ -2178,7 +2192,7 @@ static int snd_pcm_oss_mmap(struct file #ifdef OSS_DEBUG printk("pcm_oss: mmap begin\n"); #endif - pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO); + pcm_oss_file = file->private_data; switch ((area->vm_flags & (VM_READ | VM_WRITE))) { case VM_READ | VM_WRITE: substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; @@ -2280,7 +2294,7 @@ static void snd_pcm_oss_proc_write(snd_i snd_info_buffer_t * buffer) { snd_pcm_str_t *pstr = (snd_pcm_str_t *)entry->private_data; - char line[512], str[32], task_name[32], *ptr; + char line[256], str[32], task_name[32], *ptr; int idx1; snd_pcm_oss_setup_t *setup, *setup1, template; --- linux-2.6.9-rc1/sound/core/oss/pcm_plugin.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/sound/core/oss/pcm_plugin.c 2004-09-02 20:51:53.000000000 -0700 @@ -172,7 +172,7 @@ int snd_pcm_plugin_build(snd_pcm_plug_t snd_assert(plug != NULL, return -ENXIO); snd_assert(src_format != NULL && dst_format != NULL, return -ENXIO); - plugin = (snd_pcm_plugin_t *)snd_kcalloc(sizeof(*plugin) + extra, GFP_KERNEL); + plugin = kcalloc(1, sizeof(*plugin) + extra, GFP_KERNEL); if (plugin == NULL) return -ENOMEM; plugin->name = name; @@ -189,7 +189,7 @@ int snd_pcm_plugin_build(snd_pcm_plug_t channels = src_format->channels; else channels = dst_format->channels; - plugin->buf_channels = snd_kcalloc(channels * sizeof(*plugin->buf_channels), GFP_KERNEL); + plugin->buf_channels = kcalloc(channels, sizeof(*plugin->buf_channels), GFP_KERNEL); if (plugin->buf_channels == NULL) { snd_pcm_plugin_free(plugin); return -ENOMEM; @@ -370,7 +370,7 @@ int snd_pcm_plug_slave_format(int format unsigned int i; switch (format) { case SNDRV_PCM_FORMAT_MU_LAW: - for (i = 0; i < sizeof(preferred_formats) / sizeof(preferred_formats[0]); ++i) { + for (i = 0; i < ARRAY_SIZE(preferred_formats); ++i) { int format1 = preferred_formats[i]; if (snd_mask_test(format_mask, format1)) return format1; @@ -468,7 +468,7 @@ int snd_pcm_plug_format_plugins(snd_pcm_ if (srcformat.channels > dstformat.channels) { int sv = srcformat.channels; int dv = dstformat.channels; - route_ttable_entry_t *ttable = snd_kcalloc(dv*sv*sizeof(*ttable), GFP_KERNEL); + route_ttable_entry_t *ttable = kcalloc(dv * sv, sizeof(*ttable), GFP_KERNEL); if (ttable == NULL) return -ENOMEM; #if 1 @@ -531,7 +531,7 @@ int snd_pcm_plug_format_plugins(snd_pcm_ if (srcformat.channels < dstformat.channels) { int sv = srcformat.channels; int dv = dstformat.channels; - route_ttable_entry_t *ttable = snd_kcalloc(dv * sv * sizeof(*ttable), GFP_KERNEL); + route_ttable_entry_t *ttable = kcalloc(dv * sv, sizeof(*ttable), GFP_KERNEL); if (ttable == NULL) return -ENOMEM; #if 0 @@ -846,41 +846,31 @@ int snd_pcm_area_silence(const snd_pcm_c size_t samples, int format) { /* FIXME: sub byte resolution and odd dst_offset */ - char *dst; + unsigned char *dst; unsigned int dst_step; int width; - u_int64_t silence; + const unsigned char *silence; if (!dst_area->addr) return 0; dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8; width = snd_pcm_format_physical_width(format); + if (width <= 0) + return -EINVAL; + if (dst_area->step == (unsigned int) width && width >= 8) + return snd_pcm_format_set_silence(format, dst, samples); silence = snd_pcm_format_silence_64(format); - if (dst_area->step == (unsigned int) width) { - size_t dwords = samples * width / 64; - u_int64_t *dst64 = (u_int64_t *)dst; - - samples -= dwords * 64 / width; - while (dwords-- > 0) - *dst64++ = silence; - if (samples == 0) - return 0; - dst = (char *)dst64; - } + if (! silence) + return -EINVAL; dst_step = dst_area->step / 8; - switch (width) { - case 4: { - u_int8_t s0 = silence & 0xf0; - u_int8_t s1 = silence & 0x0f; + if (width == 4) { + /* Ima ADPCM */ int dstbit = dst_area->first % 8; int dstbit_step = dst_area->step % 8; while (samples-- > 0) { - if (dstbit) { + if (dstbit) *dst &= 0xf0; - *dst |= s1; - } else { + else *dst &= 0x0f; - *dst |= s0; - } dst += dst_step; dstbit += dstbit_step; if (dstbit == 8) { @@ -888,41 +878,12 @@ int snd_pcm_area_silence(const snd_pcm_c dstbit = 0; } } - break; - } - case 8: { - u_int8_t sil = silence; - while (samples-- > 0) { - *dst = sil; - dst += dst_step; - } - break; - } - case 16: { - u_int16_t sil = silence; - while (samples-- > 0) { - *(u_int16_t*)dst = sil; - dst += dst_step; - } - break; - } - case 32: { - u_int32_t sil = silence; - while (samples-- > 0) { - *(u_int32_t*)dst = sil; - dst += dst_step; - } - break; - } - case 64: { + } else { + width /= 8; while (samples-- > 0) { - *(u_int64_t*)dst = silence; + memcpy(dst, silence, width); dst += dst_step; } - break; - } - default: - snd_BUG(); } return 0; } @@ -942,18 +903,18 @@ int snd_pcm_area_copy(const snd_pcm_chan if (!dst_area->addr) return 0; width = snd_pcm_format_physical_width(format); + if (width <= 0) + return -EINVAL; if (src_area->step == (unsigned int) width && - dst_area->step == (unsigned int) width) { + dst_area->step == (unsigned int) width && width >= 8) { size_t bytes = samples * width / 8; - samples -= bytes * 8 / width; memcpy(dst, src, bytes); - if (samples == 0) - return 0; + return 0; } src_step = src_area->step / 8; dst_step = dst_area->step / 8; - switch (width) { - case 4: { + if (width == 4) { + /* Ima ADPCM */ int srcbit = src_area->first % 8; int srcbit_step = src_area->step % 8; int dstbit = dst_area->first % 8; @@ -963,12 +924,11 @@ int snd_pcm_area_copy(const snd_pcm_chan if (srcbit) srcval = *src & 0x0f; else - srcval = *src & 0xf0; + srcval = (*src & 0xf0) >> 4; if (dstbit) - *dst &= 0xf0; + *dst = (*dst & 0xf0) | srcval; else - *dst &= 0x0f; - *dst |= srcval; + *dst = (*dst & 0x0f) | (srcval << 4); src += src_step; srcbit += srcbit_step; if (srcbit == 8) { @@ -982,42 +942,13 @@ int snd_pcm_area_copy(const snd_pcm_chan dstbit = 0; } } - break; - } - case 8: { - while (samples-- > 0) { - *dst = *src; - src += src_step; - dst += dst_step; - } - break; - } - case 16: { - while (samples-- > 0) { - *(u_int16_t*)dst = *(u_int16_t*)src; - src += src_step; - dst += dst_step; - } - break; - } - case 32: { - while (samples-- > 0) { - *(u_int32_t*)dst = *(u_int32_t*)src; - src += src_step; - dst += dst_step; - } - break; - } - case 64: { + } else { + width /= 8; while (samples-- > 0) { - *(u_int64_t*)dst = *(u_int64_t*)src; + memcpy(dst, src, width); src += src_step; dst += dst_step; } - break; - } - default: - snd_BUG(); } return 0; } --- linux-2.6.9-rc1/sound/core/oss/pcm_plugin.h 2004-08-24 00:03:30.000000000 -0700 +++ 25/sound/core/oss/pcm_plugin.h 2004-09-02 20:51:53.000000000 -0700 @@ -35,7 +35,7 @@ static inline size_t bitset_size(int nbi static inline bitset_t *bitset_alloc(int nbits) { - return snd_kcalloc(bitset_size(nbits) * sizeof(bitset_t), GFP_KERNEL); + return kcalloc(bitset_size(nbits), sizeof(bitset_t), GFP_KERNEL); } static inline void bitset_set(bitset_t *bitmap, unsigned int pos) --- linux-2.6.9-rc1/sound/core/oss/route.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/sound/core/oss/route.c 2004-09-02 20:51:53.000000000 -0700 @@ -458,7 +458,7 @@ static int route_load_ttable(snd_pcm_plu dptr->func = route_to_channel; if (nsrcs > 0) { int srcidx; - dptr->srcs = snd_kcalloc(nsrcs * sizeof(*srcs), GFP_KERNEL); + dptr->srcs = kcalloc(nsrcs, sizeof(*srcs), GFP_KERNEL); for(srcidx = 0; srcidx < nsrcs; srcidx++) dptr->srcs[srcidx] = srcs[srcidx]; } else --- linux-2.6.9-rc1/sound/core/pcm.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/sound/core/pcm.c 2004-09-02 23:57:36.729738680 -0700 @@ -585,7 +585,7 @@ int snd_pcm_new_stream(snd_pcm_t *pcm, i } prev = NULL; for (idx = 0, prev = NULL; idx < substream_count; idx++) { - substream = snd_magic_kcalloc(snd_pcm_substream_t, 0, GFP_KERNEL); + substream = kcalloc(1, sizeof(*substream), GFP_KERNEL); if (substream == NULL) return -ENOMEM; substream->pcm = pcm; @@ -600,7 +600,7 @@ int snd_pcm_new_stream(snd_pcm_t *pcm, i prev->next = substream; err = snd_pcm_substream_proc_init(substream); if (err < 0) { - snd_magic_kfree(substream); + kfree(substream); return err; } substream->group = &substream->self_group; @@ -645,7 +645,7 @@ int snd_pcm_new(snd_card_t * card, char snd_assert(rpcm != NULL, return -EINVAL); *rpcm = NULL; snd_assert(card != NULL, return -ENXIO); - pcm = snd_magic_kcalloc(snd_pcm_t, 0, GFP_KERNEL); + pcm = kcalloc(1, sizeof(*pcm), GFP_KERNEL); if (pcm == NULL) return -ENOMEM; pcm->card = card; @@ -681,7 +681,7 @@ static void snd_pcm_free_stream(snd_pcm_ while (substream) { substream_next = substream->next; snd_pcm_substream_proc_done(substream); - snd_magic_kfree(substream); + kfree(substream); substream = substream_next; } snd_pcm_stream_proc_done(pstr); @@ -702,13 +702,13 @@ static int snd_pcm_free(snd_pcm_t *pcm) snd_pcm_lib_preallocate_free_for_all(pcm); snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]); snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_CAPTURE]); - snd_magic_kfree(pcm); + kfree(pcm); return 0; } static int snd_pcm_dev_free(snd_device_t *device) { - snd_pcm_t *pcm = snd_magic_cast(snd_pcm_t, device->device_data, return -ENXIO); + snd_pcm_t *pcm = device->device_data; return snd_pcm_free(pcm); } @@ -783,7 +783,7 @@ int snd_pcm_open_substream(snd_pcm_t *pc if (substream == NULL) return -EAGAIN; - runtime = snd_kcalloc(sizeof(snd_pcm_runtime_t), GFP_KERNEL); + runtime = kcalloc(1, sizeof(*runtime), GFP_KERNEL); if (runtime == NULL) return -ENOMEM; @@ -843,7 +843,7 @@ static int snd_pcm_dev_register(snd_devi snd_pcm_substream_t *substream; struct list_head *list; char str[16]; - snd_pcm_t *pcm = snd_magic_cast(snd_pcm_t, device->device_data, return -ENXIO); + snd_pcm_t *pcm = device->device_data; snd_assert(pcm != NULL && device != NULL, return -ENXIO); down(®ister_mutex); @@ -888,7 +888,7 @@ static int snd_pcm_dev_register(snd_devi static int snd_pcm_dev_disconnect(snd_device_t *device) { - snd_pcm_t *pcm = snd_magic_cast(snd_pcm_t, device->device_data, return -ENXIO); + snd_pcm_t *pcm = device->device_data; struct list_head *list; snd_pcm_substream_t *substream; int idx, cidx; @@ -914,7 +914,7 @@ static int snd_pcm_dev_unregister(snd_de int idx, cidx, devtype; snd_pcm_substream_t *substream; struct list_head *list; - snd_pcm_t *pcm = snd_magic_cast(snd_pcm_t, device->device_data, return -ENXIO); + snd_pcm_t *pcm = device->device_data; snd_assert(pcm != NULL, return -ENXIO); down(®ister_mutex); @@ -1050,6 +1050,9 @@ EXPORT_SYMBOL(snd_pcm_release); EXPORT_SYMBOL(snd_pcm_playback_poll); EXPORT_SYMBOL(snd_pcm_capture_poll); EXPORT_SYMBOL(snd_pcm_mmap_data); +#if SNDRV_PCM_INFO_MMAP_IOMEM +EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem); +#endif /* pcm_misc.c */ EXPORT_SYMBOL(snd_pcm_format_signed); EXPORT_SYMBOL(snd_pcm_format_unsigned); --- linux-2.6.9-rc1/sound/core/pcm_lib.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/sound/core/pcm_lib.c 2004-09-02 20:51:53.000000000 -0700 @@ -128,12 +128,29 @@ void snd_pcm_playback_silence(snd_pcm_su } } +static void xrun(snd_pcm_substream_t *substream) +{ + snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); +#ifdef CONFIG_SND_DEBUG + if (substream->pstr->xrun_debug) { + snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n", + substream->pcm->card->number, + substream->pcm->device, + substream->stream ? 'c' : 'p'); + if (substream->pstr->xrun_debug > 1) + dump_stack(); + } +#endif +} + static inline snd_pcm_uframes_t snd_pcm_update_hw_ptr_pos(snd_pcm_substream_t *substream, snd_pcm_runtime_t *runtime) { snd_pcm_uframes_t pos; pos = substream->ops->pointer(substream); + if (pos == SNDRV_PCM_POS_XRUN) + return pos; /* XRUN */ if (runtime->tstamp_mode & SNDRV_PCM_TSTAMP_MMAP) snd_timestamp_now((snd_timestamp_t*)&runtime->status->tstamp, runtime->tstamp_timespec); #ifdef CONFIG_SND_DEBUG @@ -158,19 +175,10 @@ static inline int snd_pcm_update_hw_ptr_ if (avail > runtime->avail_max) runtime->avail_max = avail; if (avail >= runtime->stop_threshold) { - snd_pcm_stop(substream, - runtime->status->state == SNDRV_PCM_STATE_DRAINING ? - SNDRV_PCM_STATE_SETUP : SNDRV_PCM_STATE_XRUN); -#ifdef CONFIG_SND_DEBUG - if (substream->pstr->xrun_debug) { - snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n", - substream->pcm->card->number, - substream->pcm->device, - substream->stream ? 'c' : 'p'); - if (substream->pstr->xrun_debug > 1) - dump_stack(); - } -#endif + if (substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING) + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); + else + xrun(substream); return -EPIPE; } if (avail >= runtime->control->avail_min) @@ -186,6 +194,10 @@ static inline int snd_pcm_update_hw_ptr_ snd_pcm_sframes_t delta; pos = snd_pcm_update_hw_ptr_pos(substream, runtime); + if (pos == SNDRV_PCM_POS_XRUN) { + xrun(substream); + return -EPIPE; + } if (runtime->period_size == runtime->buffer_size) goto __next_buf; new_hw_ptr = runtime->hw_ptr_base + pos; @@ -230,6 +242,10 @@ int snd_pcm_update_hw_ptr(snd_pcm_substr old_hw_ptr = runtime->status->hw_ptr; pos = snd_pcm_update_hw_ptr_pos(substream, runtime); + if (pos == SNDRV_PCM_POS_XRUN) { + xrun(substream); + return -EPIPE; + } new_hw_ptr = runtime->hw_ptr_base + pos; delta = old_hw_ptr - new_hw_ptr; @@ -887,22 +903,18 @@ int snd_pcm_hw_rule_add(snd_pcm_runtime_ va_list args; va_start(args, dep); if (constrs->rules_num >= constrs->rules_all) { - snd_pcm_hw_rule_t *old = constrs->rules; - if (constrs->rules_all == 0) - constrs->rules_all = 32; - else { - old = constrs->rules; - constrs->rules_all += 10; - } - constrs->rules = snd_kcalloc(constrs->rules_all * sizeof(*c), - GFP_KERNEL); - if (!constrs->rules) + snd_pcm_hw_rule_t *new; + unsigned int new_rules = constrs->rules_all + 16; + new = kcalloc(new_rules, sizeof(*c), GFP_KERNEL); + if (!new) return -ENOMEM; - if (old) { - memcpy(constrs->rules, old, + if (constrs->rules) { + memcpy(new, constrs->rules, constrs->rules_num * sizeof(*c)); - kfree(old); + kfree(constrs->rules); } + constrs->rules = new; + constrs->rules_all = new_rules; } c = &constrs->rules[constrs->rules_num]; c->cond = cond; @@ -911,7 +923,7 @@ int snd_pcm_hw_rule_add(snd_pcm_runtime_ c->private = private; k = 0; while (1) { - snd_assert(k < sizeof(c->deps) / sizeof(c->deps[0]), return -EINVAL); + snd_assert(k < ARRAY_SIZE(c->deps), return -EINVAL); c->deps[k++] = dep; if (dep < 0) break; @@ -1109,7 +1121,7 @@ static int snd_pcm_hw_rule_pow2(snd_pcm_ 1<<24, 1<<25, 1<<26, 1<<27, 1<<28, 1<<29, 1<<30 }; return snd_interval_list(hw_param_interval(params, rule->var), - sizeof(pow2_sizes)/sizeof(int), pow2_sizes, 0); + ARRAY_SIZE(pow2_sizes), pow2_sizes, 0); } /** @@ -1769,12 +1781,14 @@ static int snd_pcm_lib_ioctl_reset(snd_p void *arg) { snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + snd_pcm_stream_lock_irqsave(substream, flags); if (snd_pcm_running(substream) && - snd_pcm_update_hw_ptr(substream) >= 0) { + snd_pcm_update_hw_ptr(substream) >= 0) runtime->status->hw_ptr %= runtime->buffer_size; - return 0; - } - runtime->status->hw_ptr = 0; + else + runtime->status->hw_ptr = 0; + snd_pcm_stream_unlock_irqrestore(substream, flags); return 0; } --- linux-2.6.9-rc1/sound/core/pcm_memory.c 2004-08-24 00:03:17.000000000 -0700 +++ 25/sound/core/pcm_memory.c 2004-09-02 20:51:53.000000000 -0700 @@ -32,12 +32,10 @@ static int preallocate_dma = 1; module_param(preallocate_dma, int, 0444); MODULE_PARM_DESC(preallocate_dma, "Preallocate DMA memory when the PCM devices are initialized."); -MODULE_PARM_SYNTAX(preallocate_dma, SNDRV_BOOLEAN_TRUE_DESC); static int maximum_substreams = 4; module_param(maximum_substreams, int, 0444); MODULE_PARM_DESC(maximum_substreams, "Maximum substreams with preallocated DMA memory."); -MODULE_PARM_SYNTAX(maximum_substreams, SNDRV_BOOLEAN_TRUE_DESC); const static size_t snd_minimum_buffer = 16384; @@ -56,26 +54,21 @@ static int preallocate_pcm_pages(snd_pcm snd_assert(size > 0, return -EINVAL); /* already reserved? */ - if (snd_dma_get_reserved(&substream->dma_device, dmab) > 0) { + if (snd_dma_get_reserved_buf(dmab, substream->dma_buf_id) > 0) { if (dmab->bytes >= size) return 0; /* yes */ - /* no, reset the reserved block */ - /* if we can find bigger pages below, this block will be - * automatically removed in snd_dma_set_reserved(). - */ - snd_dma_free_reserved(&substream->dma_device); + /* no, free the reserved block */ + snd_dma_free_pages(dmab); dmab->bytes = 0; } do { - if ((err = snd_dma_alloc_pages(&substream->dma_device, size, dmab)) < 0) { + if ((err = snd_dma_alloc_pages(dmab->dev.type, dmab->dev.dev, + size, dmab)) < 0) { if (err != -ENOMEM) return err; /* fatal error */ - } else { - /* remember this one */ - snd_dma_set_reserved(&substream->dma_device, dmab); + } else return 0; - } size >>= 1; } while (size >= snd_minimum_buffer); dmab->bytes = 0; /* tell error */ @@ -89,7 +82,10 @@ static void snd_pcm_lib_preallocate_dma_ { if (substream->dma_buffer.area == NULL) return; - snd_dma_free_reserved(&substream->dma_device); + if (substream->dma_buf_id) + snd_dma_reserve_buf(&substream->dma_buffer, substream->dma_buf_id); + else + snd_dma_free_pages(&substream->dma_buffer); substream->dma_buffer.area = NULL; } @@ -108,7 +104,6 @@ int snd_pcm_lib_preallocate_free(snd_pcm snd_info_unregister(substream->proc_prealloc_entry); substream->proc_prealloc_entry = NULL; } - substream->dma_device.type = SNDRV_DMA_TYPE_UNKNOWN; return 0; } @@ -170,18 +165,20 @@ static void snd_pcm_lib_preallocate_proc if (substream->dma_buffer.bytes == size) return; memset(&new_dmab, 0, sizeof(new_dmab)); + new_dmab.dev = substream->dma_buffer.dev; if (size > 0) { - - if (snd_dma_alloc_pages(&substream->dma_device, size, &new_dmab) < 0) { + if (snd_dma_alloc_pages(substream->dma_buffer.dev.type, + substream->dma_buffer.dev.dev, + size, &new_dmab) < 0) { buffer->error = -ENOMEM; return; } substream->buffer_bytes_max = size; - snd_dma_free_reserved(&substream->dma_device); } else { substream->buffer_bytes_max = UINT_MAX; } - snd_dma_set_reserved(&substream->dma_device, &new_dmab); + if (substream->dma_buffer.area) + snd_dma_free_pages(&substream->dma_buffer); substream->dma_buffer = new_dmab; } else { buffer->error = -EINVAL; @@ -196,7 +193,6 @@ static int snd_pcm_lib_preallocate_pages { snd_info_entry_t *entry; - memset(&substream->dma_buffer, 0, sizeof(substream->dma_buffer)); if (size > 0 && preallocate_dma && substream->number < maximum_substreams) preallocate_pcm_pages(substream, size); @@ -219,20 +215,6 @@ static int snd_pcm_lib_preallocate_pages } -/* - * set up the unique pcm id - */ -static inline void setup_pcm_id(snd_pcm_substream_t *subs) -{ - if (! subs->dma_device.id) { - subs->dma_device.id = subs->pcm->device << 16 | - subs->stream << 8 | (subs->number + 1); - if (subs->dma_device.type == SNDRV_DMA_TYPE_CONTINUOUS || - subs->dma_device.dev == NULL) - subs->dma_device.id |= (subs->pcm->card->number + 1) << 24; - } -} - /** * snd_pcm_lib_preallocate_pages - pre-allocation for the given DMA type * @substream: the pcm substream instance @@ -241,7 +223,12 @@ static inline void setup_pcm_id(snd_pcm_ * @size: the requested pre-allocation size in bytes * @max: the max. allowed pre-allocation size * - * Do pre-allocation for the given DMA type. + * Do pre-allocation for the given DMA buffer type. + * + * When substream->dma_buf_id is set, the function tries to look for + * the reserved buffer, and the buffer is not freed but reserved at + * destruction time. The dma_buf_id must be unique for all systems + * (in the same DMA buffer type) e.g. using snd_dma_pci_buf_id(). * * Returns zero if successful, or a negative error code on failure. */ @@ -249,9 +236,8 @@ int snd_pcm_lib_preallocate_pages(snd_pc int type, struct device *data, size_t size, size_t max) { - substream->dma_device.type = type; - substream->dma_device.dev = data; - setup_pcm_id(substream); + substream->dma_buffer.dev.type = type; + substream->dma_buffer.dev.dev = data; return snd_pcm_lib_preallocate_pages1(substream, size, max); } @@ -314,31 +300,38 @@ struct page *snd_pcm_sgbuf_ops_page(snd_ int snd_pcm_lib_malloc_pages(snd_pcm_substream_t *substream, size_t size) { snd_pcm_runtime_t *runtime; - struct snd_dma_buffer dmab; + struct snd_dma_buffer *dmab = NULL; - snd_assert(substream->dma_device.type != SNDRV_DMA_TYPE_UNKNOWN, return -EINVAL); + snd_assert(substream->dma_buffer.dev.type != SNDRV_DMA_TYPE_UNKNOWN, return -EINVAL); snd_assert(substream != NULL, return -EINVAL); runtime = substream->runtime; - snd_assert(runtime != NULL, return -EINVAL); + snd_assert(runtime != NULL, return -EINVAL); - if (runtime->dma_area != NULL) { + if (runtime->dma_buffer_p) { /* perphaps, we might free the large DMA memory region to save some space here, but the actual solution costs us less time */ - if (runtime->dma_bytes >= size) + if (runtime->dma_buffer_p->bytes >= size) { + runtime->dma_bytes = size; return 0; /* ok, do not change */ + } snd_pcm_lib_free_pages(substream); } if (substream->dma_buffer.area != NULL && substream->dma_buffer.bytes >= size) { - dmab = substream->dma_buffer; /* use the pre-allocated buffer */ + dmab = &substream->dma_buffer; /* use the pre-allocated buffer */ } else { - memset(&dmab, 0, sizeof(dmab)); /* allocate a new buffer */ - if (snd_dma_alloc_pages(&substream->dma_device, size, &dmab) < 0) + dmab = kcalloc(1, sizeof(*dmab), GFP_KERNEL); + if (! dmab) + return -ENOMEM; + dmab->dev = substream->dma_buffer.dev; + if (snd_dma_alloc_pages(substream->dma_buffer.dev.type, + substream->dma_buffer.dev.dev, + size, dmab) < 0) { + kfree(dmab); return -ENOMEM; + } } - runtime->dma_area = dmab.area; - runtime->dma_addr = dmab.addr; - runtime->dma_private = dmab.private_data; + snd_pcm_set_runtime_buffer(substream, dmab); runtime->dma_bytes = size; return 1; /* area was changed */ } @@ -360,34 +353,11 @@ int snd_pcm_lib_free_pages(snd_pcm_subst snd_assert(runtime != NULL, return -EINVAL); if (runtime->dma_area == NULL) return 0; - if (runtime->dma_area != substream->dma_buffer.area) { + if (runtime->dma_buffer_p != &substream->dma_buffer) { /* it's a newly allocated buffer. release it now. */ - struct snd_dma_buffer dmab; - memset(&dmab, 0, sizeof(dmab)); - dmab.area = runtime->dma_area; - dmab.addr = runtime->dma_addr; - dmab.bytes = runtime->dma_bytes; - dmab.private_data = runtime->dma_private; - snd_dma_free_pages(&substream->dma_device, &dmab); + snd_dma_free_pages(runtime->dma_buffer_p); + kfree(runtime->dma_buffer_p); } - runtime->dma_area = NULL; - runtime->dma_addr = 0UL; - runtime->dma_bytes = 0; - runtime->dma_private = NULL; + snd_pcm_set_runtime_buffer(substream, NULL); return 0; } - -#ifndef MODULE - -/* format is: snd-pcm=preallocate_dma,maximum_substreams */ - -static int __init alsa_pcm_setup(char *str) -{ - (void)(get_option(&str,&preallocate_dma) == 2 && - get_option(&str,&maximum_substreams) == 2); - return 1; -} - -__setup("snd-pcm=", alsa_pcm_setup); - -#endif /* ifndef MODULE */ --- linux-2.6.9-rc1/sound/core/pcm_misc.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/sound/core/pcm_misc.c 2004-09-02 20:51:53.000000000 -0700 @@ -23,12 +23,169 @@ #include #include #include -#define bswap_16 swab16 -#define bswap_32 swab32 -#define bswap_64 swab64 #define SND_PCM_FORMAT_UNKNOWN (-1) -#define snd_enum_to_int(v) (v) -#define snd_int_to_enum(v) (v) + +struct pcm_format_data { + char width; /* bit width */ + char phys; /* physical bit width */ + char le; /* 0 = big-endian, 1 = little-endian, -1 = others */ + char signd; /* 0 = unsigned, 1 = signed, -1 = others */ + unsigned char silence[8]; /* silence data to fill */ +}; + +static struct pcm_format_data pcm_formats[SNDRV_PCM_FORMAT_LAST+1] = { + [SNDRV_PCM_FORMAT_S8] = { + .width = 8, .phys = 8, .le = -1, .signd = 1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_U8] = { + .width = 8, .phys = 8, .le = -1, .signd = 0, + .silence = { 0x80 }, + }, + [SNDRV_PCM_FORMAT_S16_LE] = { + .width = 16, .phys = 16, .le = 1, .signd = 1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_S16_BE] = { + .width = 16, .phys = 16, .le = 0, .signd = 1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_U16_LE] = { + .width = 16, .phys = 16, .le = 1, .signd = 0, + .silence = { 0x00, 0x80 }, + }, + [SNDRV_PCM_FORMAT_U16_BE] = { + .width = 16, .phys = 16, .le = 0, .signd = 0, + .silence = { 0x80, 0x00 }, + }, + [SNDRV_PCM_FORMAT_S24_LE] = { + .width = 24, .phys = 32, .le = 1, .signd = 1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_S24_BE] = { + .width = 24, .phys = 32, .le = 0, .signd = 1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_U24_LE] = { + .width = 24, .phys = 32, .le = 1, .signd = 0, + .silence = { 0x00, 0x00, 0x80 }, + }, + [SNDRV_PCM_FORMAT_U24_BE] = { + .width = 24, .phys = 32, .le = 0, .signd = 0, + .silence = { 0x80, 0x00, 0x00 }, + }, + [SNDRV_PCM_FORMAT_S32_LE] = { + .width = 32, .phys = 32, .le = 1, .signd = 1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_S32_BE] = { + .width = 32, .phys = 32, .le = 0, .signd = 1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_U32_LE] = { + .width = 32, .phys = 32, .le = 1, .signd = 0, + .silence = { 0x00, 0x00, 0x00, 0x80 }, + }, + [SNDRV_PCM_FORMAT_U32_BE] = { + .width = 32, .phys = 32, .le = 0, .signd = 0, + .silence = { 0x80, 0x00, 0x00, 0x00 }, + }, + [SNDRV_PCM_FORMAT_FLOAT_LE] = { + .width = 32, .phys = 32, .le = 1, .signd = -1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_FLOAT_BE] = { + .width = 32, .phys = 32, .le = 0, .signd = -1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_FLOAT64_LE] = { + .width = 64, .phys = 64, .le = 1, .signd = -1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_FLOAT64_BE] = { + .width = 64, .phys = 64, .le = 0, .signd = -1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE] = { + .width = 32, .phys = 32, .le = 1, .signd = -1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE] = { + .width = 32, .phys = 32, .le = 0, .signd = -1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_MU_LAW] = { + .width = 8, .phys = 8, .le = -1, .signd = -1, + .silence = { 0x7f }, + }, + [SNDRV_PCM_FORMAT_A_LAW] = { + .width = 8, .phys = 8, .le = -1, .signd = -1, + .silence = { 0x55 }, + }, + [SNDRV_PCM_FORMAT_IMA_ADPCM] = { + .width = 4, .phys = 4, .le = -1, .signd = -1, + .silence = {}, + }, + /* FIXME: the following three formats are not defined properly yet */ + [SNDRV_PCM_FORMAT_MPEG] = { + .le = -1, .signd = -1, + }, + [SNDRV_PCM_FORMAT_GSM] = { + .le = -1, .signd = -1, + }, + [SNDRV_PCM_FORMAT_SPECIAL] = { + .le = -1, .signd = -1, + }, + [SNDRV_PCM_FORMAT_S24_3LE] = { + .width = 24, .phys = 24, .le = 1, .signd = 1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_S24_3BE] = { + .width = 24, .phys = 24, .le = 0, .signd = 1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_U24_3LE] = { + .width = 24, .phys = 24, .le = 1, .signd = 0, + .silence = { 0x00, 0x00, 0x80 }, + }, + [SNDRV_PCM_FORMAT_U24_3BE] = { + .width = 24, .phys = 24, .le = 0, .signd = 0, + .silence = { 0x80, 0x00, 0x00 }, + }, + [SNDRV_PCM_FORMAT_S20_3LE] = { + .width = 20, .phys = 24, .le = 1, .signd = 1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_S20_3BE] = { + .width = 20, .phys = 24, .le = 0, .signd = 1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_U20_3LE] = { + .width = 20, .phys = 24, .le = 1, .signd = 0, + .silence = { 0x00, 0x00, 0x08 }, + }, + [SNDRV_PCM_FORMAT_U20_3BE] = { + .width = 20, .phys = 24, .le = 0, .signd = 0, + .silence = { 0x08, 0x00, 0x00 }, + }, + [SNDRV_PCM_FORMAT_S18_3LE] = { + .width = 18, .phys = 24, .le = 1, .signd = 1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_S18_3BE] = { + .width = 18, .phys = 24, .le = 0, .signd = 1, + .silence = {}, + }, + [SNDRV_PCM_FORMAT_U18_3LE] = { + .width = 18, .phys = 24, .le = 1, .signd = 0, + .silence = { 0x00, 0x00, 0x02 }, + }, + [SNDRV_PCM_FORMAT_U18_3BE] = { + .width = 18, .phys = 24, .le = 0, .signd = 0, + .silence = { 0x02, 0x00, 0x00 }, + }, +}; + /** * snd_pcm_format_signed - Check the PCM format is signed linear @@ -39,38 +196,12 @@ */ int snd_pcm_format_signed(snd_pcm_format_t format) { - switch (snd_enum_to_int(format)) { - case SNDRV_PCM_FORMAT_S8: - case SNDRV_PCM_FORMAT_S16_LE: - case SNDRV_PCM_FORMAT_S16_BE: - case SNDRV_PCM_FORMAT_S24_LE: - case SNDRV_PCM_FORMAT_S24_BE: - case SNDRV_PCM_FORMAT_S32_LE: - case SNDRV_PCM_FORMAT_S32_BE: - case SNDRV_PCM_FORMAT_S24_3LE: - case SNDRV_PCM_FORMAT_S24_3BE: - case SNDRV_PCM_FORMAT_S20_3LE: - case SNDRV_PCM_FORMAT_S20_3BE: - case SNDRV_PCM_FORMAT_S18_3LE: - case SNDRV_PCM_FORMAT_S18_3BE: - return 1; - case SNDRV_PCM_FORMAT_U8: - case SNDRV_PCM_FORMAT_U16_LE: - case SNDRV_PCM_FORMAT_U16_BE: - case SNDRV_PCM_FORMAT_U24_LE: - case SNDRV_PCM_FORMAT_U24_BE: - case SNDRV_PCM_FORMAT_U32_LE: - case SNDRV_PCM_FORMAT_U32_BE: - case SNDRV_PCM_FORMAT_U24_3LE: - case SNDRV_PCM_FORMAT_U24_3BE: - case SNDRV_PCM_FORMAT_U20_3LE: - case SNDRV_PCM_FORMAT_U20_3BE: - case SNDRV_PCM_FORMAT_U18_3LE: - case SNDRV_PCM_FORMAT_U18_3BE: - return 0; - default: + int val; + if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) return -EINVAL; - } + if ((val = pcm_formats[format].signd) < 0) + return -EINVAL; + return val; } /** @@ -110,42 +241,12 @@ int snd_pcm_format_linear(snd_pcm_format */ int snd_pcm_format_little_endian(snd_pcm_format_t format) { - switch (snd_enum_to_int(format)) { - case SNDRV_PCM_FORMAT_S16_LE: - case SNDRV_PCM_FORMAT_U16_LE: - case SNDRV_PCM_FORMAT_S24_LE: - case SNDRV_PCM_FORMAT_U24_LE: - case SNDRV_PCM_FORMAT_S32_LE: - case SNDRV_PCM_FORMAT_U32_LE: - case SNDRV_PCM_FORMAT_FLOAT_LE: - case SNDRV_PCM_FORMAT_FLOAT64_LE: - case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: - case SNDRV_PCM_FORMAT_S24_3LE: - case SNDRV_PCM_FORMAT_S20_3LE: - case SNDRV_PCM_FORMAT_S18_3LE: - case SNDRV_PCM_FORMAT_U24_3LE: - case SNDRV_PCM_FORMAT_U20_3LE: - case SNDRV_PCM_FORMAT_U18_3LE: - return 1; - case SNDRV_PCM_FORMAT_S16_BE: - case SNDRV_PCM_FORMAT_U16_BE: - case SNDRV_PCM_FORMAT_S24_BE: - case SNDRV_PCM_FORMAT_U24_BE: - case SNDRV_PCM_FORMAT_S32_BE: - case SNDRV_PCM_FORMAT_U32_BE: - case SNDRV_PCM_FORMAT_FLOAT_BE: - case SNDRV_PCM_FORMAT_FLOAT64_BE: - case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: - case SNDRV_PCM_FORMAT_S24_3BE: - case SNDRV_PCM_FORMAT_S20_3BE: - case SNDRV_PCM_FORMAT_S18_3BE: - case SNDRV_PCM_FORMAT_U24_3BE: - case SNDRV_PCM_FORMAT_U20_3BE: - case SNDRV_PCM_FORMAT_U18_3BE: - return 0; - default: + int val; + if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) return -EINVAL; - } + if ((val = pcm_formats[format].le) < 0) + return -EINVAL; + return val; } /** @@ -190,55 +291,12 @@ int snd_pcm_format_cpu_endian(snd_pcm_fo */ int snd_pcm_format_width(snd_pcm_format_t format) { - switch (snd_enum_to_int(format)) { - case SNDRV_PCM_FORMAT_S8: - case SNDRV_PCM_FORMAT_U8: - return 8; - case SNDRV_PCM_FORMAT_S16_LE: - case SNDRV_PCM_FORMAT_S16_BE: - case SNDRV_PCM_FORMAT_U16_LE: - case SNDRV_PCM_FORMAT_U16_BE: - return 16; - case SNDRV_PCM_FORMAT_S18_3LE: - case SNDRV_PCM_FORMAT_S18_3BE: - case SNDRV_PCM_FORMAT_U18_3LE: - case SNDRV_PCM_FORMAT_U18_3BE: - return 18; - case SNDRV_PCM_FORMAT_S20_3LE: - case SNDRV_PCM_FORMAT_S20_3BE: - case SNDRV_PCM_FORMAT_U20_3LE: - case SNDRV_PCM_FORMAT_U20_3BE: - return 20; - case SNDRV_PCM_FORMAT_S24_LE: - case SNDRV_PCM_FORMAT_S24_BE: - case SNDRV_PCM_FORMAT_U24_LE: - case SNDRV_PCM_FORMAT_U24_BE: - case SNDRV_PCM_FORMAT_S24_3LE: - case SNDRV_PCM_FORMAT_S24_3BE: - case SNDRV_PCM_FORMAT_U24_3LE: - case SNDRV_PCM_FORMAT_U24_3BE: - return 24; - case SNDRV_PCM_FORMAT_S32_LE: - case SNDRV_PCM_FORMAT_S32_BE: - case SNDRV_PCM_FORMAT_U32_LE: - case SNDRV_PCM_FORMAT_U32_BE: - case SNDRV_PCM_FORMAT_FLOAT_LE: - case SNDRV_PCM_FORMAT_FLOAT_BE: - return 32; - case SNDRV_PCM_FORMAT_FLOAT64_LE: - case SNDRV_PCM_FORMAT_FLOAT64_BE: - return 64; - case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: - case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: - return 32; - case SNDRV_PCM_FORMAT_MU_LAW: - case SNDRV_PCM_FORMAT_A_LAW: - return 8; - case SNDRV_PCM_FORMAT_IMA_ADPCM: - return 4; - default: + int val; + if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) return -EINVAL; - } + if ((val = pcm_formats[format].width) == 0) + return -EINVAL; + return val; } /** @@ -250,52 +308,12 @@ int snd_pcm_format_width(snd_pcm_format_ */ int snd_pcm_format_physical_width(snd_pcm_format_t format) { - switch (snd_enum_to_int(format)) { - case SNDRV_PCM_FORMAT_S8: - case SNDRV_PCM_FORMAT_U8: - return 8; - case SNDRV_PCM_FORMAT_S16_LE: - case SNDRV_PCM_FORMAT_S16_BE: - case SNDRV_PCM_FORMAT_U16_LE: - case SNDRV_PCM_FORMAT_U16_BE: - return 16; - case SNDRV_PCM_FORMAT_S18_3LE: - case SNDRV_PCM_FORMAT_S18_3BE: - case SNDRV_PCM_FORMAT_U18_3LE: - case SNDRV_PCM_FORMAT_U18_3BE: - case SNDRV_PCM_FORMAT_S20_3LE: - case SNDRV_PCM_FORMAT_S20_3BE: - case SNDRV_PCM_FORMAT_U20_3LE: - case SNDRV_PCM_FORMAT_U20_3BE: - case SNDRV_PCM_FORMAT_S24_3LE: - case SNDRV_PCM_FORMAT_S24_3BE: - case SNDRV_PCM_FORMAT_U24_3LE: - case SNDRV_PCM_FORMAT_U24_3BE: - return 24; - case SNDRV_PCM_FORMAT_S24_LE: - case SNDRV_PCM_FORMAT_S24_BE: - case SNDRV_PCM_FORMAT_U24_LE: - case SNDRV_PCM_FORMAT_U24_BE: - case SNDRV_PCM_FORMAT_S32_LE: - case SNDRV_PCM_FORMAT_S32_BE: - case SNDRV_PCM_FORMAT_U32_LE: - case SNDRV_PCM_FORMAT_U32_BE: - case SNDRV_PCM_FORMAT_FLOAT_LE: - case SNDRV_PCM_FORMAT_FLOAT_BE: - case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: - case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: - return 32; - case SNDRV_PCM_FORMAT_FLOAT64_LE: - case SNDRV_PCM_FORMAT_FLOAT64_BE: - return 64; - case SNDRV_PCM_FORMAT_MU_LAW: - case SNDRV_PCM_FORMAT_A_LAW: - return 8; - case SNDRV_PCM_FORMAT_IMA_ADPCM: - return 4; - default: + int val; + if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) return -EINVAL; - } + if ((val = pcm_formats[format].phys) == 0) + return -EINVAL; + return val; } /** @@ -307,216 +325,25 @@ int snd_pcm_format_physical_width(snd_pc */ ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples) { - switch (snd_enum_to_int(format)) { - case SNDRV_PCM_FORMAT_S8: - case SNDRV_PCM_FORMAT_U8: - return samples; - case SNDRV_PCM_FORMAT_S16_LE: - case SNDRV_PCM_FORMAT_S16_BE: - case SNDRV_PCM_FORMAT_U16_LE: - case SNDRV_PCM_FORMAT_U16_BE: - return samples * 2; - case SNDRV_PCM_FORMAT_S18_3LE: - case SNDRV_PCM_FORMAT_S18_3BE: - case SNDRV_PCM_FORMAT_U18_3LE: - case SNDRV_PCM_FORMAT_U18_3BE: - case SNDRV_PCM_FORMAT_S20_3LE: - case SNDRV_PCM_FORMAT_S20_3BE: - case SNDRV_PCM_FORMAT_U20_3LE: - case SNDRV_PCM_FORMAT_U20_3BE: - case SNDRV_PCM_FORMAT_S24_3LE: - case SNDRV_PCM_FORMAT_S24_3BE: - case SNDRV_PCM_FORMAT_U24_3LE: - case SNDRV_PCM_FORMAT_U24_3BE: - return samples * 3; - case SNDRV_PCM_FORMAT_S24_LE: - case SNDRV_PCM_FORMAT_S24_BE: - case SNDRV_PCM_FORMAT_U24_LE: - case SNDRV_PCM_FORMAT_U24_BE: - case SNDRV_PCM_FORMAT_S32_LE: - case SNDRV_PCM_FORMAT_S32_BE: - case SNDRV_PCM_FORMAT_U32_LE: - case SNDRV_PCM_FORMAT_U32_BE: - case SNDRV_PCM_FORMAT_FLOAT_LE: - case SNDRV_PCM_FORMAT_FLOAT_BE: - return samples * 4; - case SNDRV_PCM_FORMAT_FLOAT64_LE: - case SNDRV_PCM_FORMAT_FLOAT64_BE: - return samples * 8; - case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: - case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: - return samples * 4; - case SNDRV_PCM_FORMAT_MU_LAW: - case SNDRV_PCM_FORMAT_A_LAW: - return samples; - case SNDRV_PCM_FORMAT_IMA_ADPCM: - if (samples & 1) - return -EINVAL; - return samples / 2; - default: + int phys_width = snd_pcm_format_physical_width(format); + if (phys_width < 0) return -EINVAL; - } + return samples * phys_width / 8; } /** - * snd_pcm_format_silence_64 - return the silent data in 64bit integer + * snd_pcm_format_silence_64 - return the silent data in 8 bytes array * @format: the format to check * - * Returns the silent data in 64bit integer for the given format. + * Returns the format pattern to fill or NULL if error. */ -u_int64_t snd_pcm_format_silence_64(snd_pcm_format_t format) +const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format) { - switch (snd_enum_to_int(format)) { - case SNDRV_PCM_FORMAT_S8: - case SNDRV_PCM_FORMAT_S16_LE: - case SNDRV_PCM_FORMAT_S16_BE: - case SNDRV_PCM_FORMAT_S24_LE: - case SNDRV_PCM_FORMAT_S24_BE: - case SNDRV_PCM_FORMAT_S32_LE: - case SNDRV_PCM_FORMAT_S32_BE: - case SNDRV_PCM_FORMAT_S24_3LE: - case SNDRV_PCM_FORMAT_S24_3BE: - case SNDRV_PCM_FORMAT_S20_3LE: - case SNDRV_PCM_FORMAT_S20_3BE: - case SNDRV_PCM_FORMAT_S18_3LE: - case SNDRV_PCM_FORMAT_S18_3BE: - return 0; - case SNDRV_PCM_FORMAT_U8: - return 0x8080808080808080ULL; -#ifdef SNDRV_LITTLE_ENDIAN - case SNDRV_PCM_FORMAT_U16_LE: - return 0x8000800080008000ULL; - case SNDRV_PCM_FORMAT_U24_LE: - return 0x0080000000800000ULL; - case SNDRV_PCM_FORMAT_U32_LE: - return 0x8000000080000000ULL; - case SNDRV_PCM_FORMAT_U16_BE: - return 0x0080008000800080ULL; - case SNDRV_PCM_FORMAT_U24_BE: - return 0x0000800000008000ULL; - case SNDRV_PCM_FORMAT_U32_BE: - return 0x0000008000000080ULL; - case SNDRV_PCM_FORMAT_U24_3LE: - return 0x0000800000800000ULL; - case SNDRV_PCM_FORMAT_U24_3BE: - return 0x0080000080000080ULL; - case SNDRV_PCM_FORMAT_U20_3LE: - return 0x0000080000080000ULL; - case SNDRV_PCM_FORMAT_U20_3BE: - return 0x0008000008000008ULL; - case SNDRV_PCM_FORMAT_U18_3LE: - return 0x0000020000020000ULL; - case SNDRV_PCM_FORMAT_U18_3BE: - return 0x0002000002000002ULL; -#else - case SNDRV_PCM_FORMAT_U16_LE: - return 0x0080008000800080ULL; - case SNDRV_PCM_FORMAT_U24_LE: - return 0x0000800000008000ULL; - case SNDRV_PCM_FORMAT_U32_LE: - return 0x0000008000000080ULL; - case SNDRV_PCM_FORMAT_U16_BE: - return 0x8000800080008000ULL; - case SNDRV_PCM_FORMAT_U24_BE: - return 0x0080000000800000ULL; - case SNDRV_PCM_FORMAT_U32_BE: - return 0x8000000080000000ULL; - case SNDRV_PCM_FORMAT_U24_3LE: - return 0x0080000080000080ULL; - case SNDRV_PCM_FORMAT_U24_3BE: - return 0x0000800000800000ULL; - case SNDRV_PCM_FORMAT_U20_3LE: - return 0x0008000008000008ULL; - case SNDRV_PCM_FORMAT_U20_3BE: - return 0x0000080000080000ULL; - case SNDRV_PCM_FORMAT_U18_3LE: - return 0x0002000002000002ULL; - case SNDRV_PCM_FORMAT_U18_3BE: - return 0x0000020000020000ULL; -#endif - case SNDRV_PCM_FORMAT_FLOAT_LE: - { - union { - float f; - u_int32_t i; - } u; - u.f = 0.0; -#ifdef SNDRV_LITTLE_ENDIAN - return u.i; -#else - return bswap_32(u.i); -#endif - } - case SNDRV_PCM_FORMAT_FLOAT64_LE: - { - union { - double f; - u_int64_t i; - } u; - u.f = 0.0; -#ifdef SNDRV_LITTLE_ENDIAN - return u.i; -#else - return bswap_64(u.i); -#endif - } - case SNDRV_PCM_FORMAT_FLOAT_BE: - { - union { - float f; - u_int32_t i; - } u; - u.f = 0.0; -#ifdef SNDRV_LITTLE_ENDIAN - return bswap_32(u.i); -#else - return u.i; -#endif - } - case SNDRV_PCM_FORMAT_FLOAT64_BE: - { - union { - double f; - u_int64_t i; - } u; - u.f = 0.0; -#ifdef SNDRV_LITTLE_ENDIAN - return bswap_64(u.i); -#else - return u.i; -#endif - } - case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: - case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: - return 0; - case SNDRV_PCM_FORMAT_MU_LAW: - return 0x7f7f7f7f7f7f7f7fULL; - case SNDRV_PCM_FORMAT_A_LAW: - return 0x5555555555555555ULL; - case SNDRV_PCM_FORMAT_IMA_ADPCM: /* special case */ - case SNDRV_PCM_FORMAT_MPEG: - case SNDRV_PCM_FORMAT_GSM: - case SNDRV_PCM_FORMAT_SPECIAL: - return 0; - default: - return -EINVAL; - } - return 0; -} - -u_int32_t snd_pcm_format_silence_32(snd_pcm_format_t format) -{ - return (u_int32_t)snd_pcm_format_silence_64(format); -} - -u_int16_t snd_pcm_format_silence_16(snd_pcm_format_t format) -{ - return (u_int16_t)snd_pcm_format_silence_64(format); -} - -u_int8_t snd_pcm_format_silence(snd_pcm_format_t format) -{ - return (u_int8_t)snd_pcm_format_silence_64(format); + if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + return NULL; + if (! pcm_formats[format].phys) + return NULL; + return pcm_formats[format].silence; } /** @@ -531,99 +358,73 @@ u_int8_t snd_pcm_format_silence(snd_pcm_ */ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int samples) { + int width; + unsigned char *dst, *pat; + + if (format < 0 || format > SNDRV_PCM_FORMAT_LAST) + return -EINVAL; if (samples == 0) return 0; - switch (snd_pcm_format_width(format)) { - case 4: { - u_int8_t silence = snd_pcm_format_silence_64(format); - unsigned int samples1; - if (samples % 2 != 0) - return -EINVAL; - samples1 = samples / 2; - memset(data, silence, samples1); - break; + width = pcm_formats[format].phys; /* physical width */ + pat = pcm_formats[format].silence; + if (! width) + return -EINVAL; + /* signed or 1 byte data */ + if (pcm_formats[format].signd == 1 || width <= 8) { + unsigned int bytes = samples * width / 8; + memset(data, *pat, bytes); + return 0; } - case 8: { - u_int8_t silence = snd_pcm_format_silence_64(format); - memset(data, silence, samples); - break; + /* non-zero samples, fill using a loop */ + width /= 8; + dst = data; +#if 0 + while (samples--) { + memcpy(dst, pat, width); + dst += width; } - case 16: { - u_int16_t silence = snd_pcm_format_silence_64(format); - if (! silence) - memset(data, 0, samples * 2); - else { - u_int16_t *data16 = data; - while (samples-- > 0) - *data16++ = silence; +#else + /* a bit optimization for constant width */ + switch (width) { + case 2: + while (samples--) { + memcpy(dst, pat, 2); + dst += 2; } break; - } - case 24: { - u_int32_t silence = snd_pcm_format_silence_64(format); - if (! silence) - memset(data, 0, samples * 3); - else { - while (samples-- > 0) { - u_int8_t *data8 = data; -#ifdef SNDRV_LITTLE_ENDIAN - *data8++ = silence >> 0; - *data8++ = silence >> 8; - *data8++ = silence >> 16; -#else - *data8++ = silence >> 16; - *data8++ = silence >> 8; - *data8++ = silence >> 0; -#endif - } + case 3: + while (samples--) { + memcpy(dst, pat, 3); + dst += 3; } break; - } - case 32: { - u_int32_t silence = snd_pcm_format_silence_64(format); - if (! silence) - memset(data, 0, samples * 4); - else { - u_int32_t *data32 = data; - while (samples-- > 0) - *data32++ = silence; + case 4: + while (samples--) { + memcpy(dst, pat, 4); + dst += 4; } break; - } - case 64: { - u_int64_t silence = snd_pcm_format_silence_64(format); - if (! silence) - memset(data, 0, samples * 8); - else { - u_int64_t *data64 = data; - while (samples-- > 0) - *data64++ = silence; + case 8: + while (samples--) { + memcpy(dst, pat, 8); + dst += 8; } break; } - default: - return -EINVAL; - } +#endif return 0; } -static int linear_formats[4*2*2] = { - SNDRV_PCM_FORMAT_S8, - SNDRV_PCM_FORMAT_S8, - SNDRV_PCM_FORMAT_U8, - SNDRV_PCM_FORMAT_U8, - SNDRV_PCM_FORMAT_S16_LE, - SNDRV_PCM_FORMAT_S16_BE, - SNDRV_PCM_FORMAT_U16_LE, - SNDRV_PCM_FORMAT_U16_BE, - SNDRV_PCM_FORMAT_S24_LE, - SNDRV_PCM_FORMAT_S24_BE, - SNDRV_PCM_FORMAT_U24_LE, - SNDRV_PCM_FORMAT_U24_BE, - SNDRV_PCM_FORMAT_S32_LE, - SNDRV_PCM_FORMAT_S32_BE, - SNDRV_PCM_FORMAT_U32_LE, - SNDRV_PCM_FORMAT_U32_BE +/* [width][unsigned][bigendian] */ +static int linear_formats[4][2][2] = { + {{ SNDRV_PCM_FORMAT_S8, SNDRV_PCM_FORMAT_S8}, + { SNDRV_PCM_FORMAT_U8, SNDRV_PCM_FORMAT_U8}}, + {{SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_FORMAT_S16_BE}, + {SNDRV_PCM_FORMAT_U16_LE, SNDRV_PCM_FORMAT_U16_BE}}, + {{SNDRV_PCM_FORMAT_S24_LE, SNDRV_PCM_FORMAT_S24_BE}, + {SNDRV_PCM_FORMAT_U24_LE, SNDRV_PCM_FORMAT_U24_BE}}, + {{SNDRV_PCM_FORMAT_S32_LE, SNDRV_PCM_FORMAT_S32_BE}, + {SNDRV_PCM_FORMAT_U32_LE, SNDRV_PCM_FORMAT_U32_BE}} }; /** @@ -636,23 +437,12 @@ static int linear_formats[4*2*2] = { */ snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsignd, int big_endian) { - switch (width) { - case 8: - width = 0; - break; - case 16: - width = 1; - break; - case 24: - width = 2; - break; - case 32: - width = 3; - break; - default: + if (width & 7) return SND_PCM_FORMAT_UNKNOWN; - } - return snd_int_to_enum(((int(*)[2][2])linear_formats)[width][!!unsignd][!!big_endian]); + width = (width / 8) - 1; + if (width < 0 || width >= 4) + return SND_PCM_FORMAT_UNKNOWN; + return linear_formats[width][!!unsignd][!!big_endian]; } /** --- linux-2.6.9-rc1/sound/core/pcm_native.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/sound/core/pcm_native.c 2004-09-02 23:57:36.732738224 -0700 @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -32,6 +33,7 @@ #include #include #include +#include /* * Compatibility @@ -64,6 +66,7 @@ static int snd_pcm_hw_params_old_user(sn */ rwlock_t snd_pcm_link_rwlock = RW_LOCK_UNLOCKED; +static DECLARE_RWSEM(snd_pcm_link_rwsem); static inline mm_segment_t snd_enter_user(void) @@ -304,13 +307,25 @@ int snd_pcm_hw_refine(snd_pcm_substream_ static int snd_pcm_hw_refine_user(snd_pcm_substream_t * substream, snd_pcm_hw_params_t __user * _params) { - snd_pcm_hw_params_t params; + snd_pcm_hw_params_t *params; int err; - if (copy_from_user(¶ms, _params, sizeof(params))) - return -EFAULT; - err = snd_pcm_hw_refine(substream, ¶ms); - if (copy_to_user(_params, ¶ms, sizeof(params))) - return -EFAULT; + + params = kmalloc(sizeof(*params), GFP_KERNEL); + if (!params) { + err = -ENOMEM; + goto out; + } + if (copy_from_user(params, _params, sizeof(*params))) { + err = -EFAULT; + goto out; + } + err = snd_pcm_hw_refine(substream, params); + if (copy_to_user(_params, params, sizeof(*params))) { + if (!err) + err = -EFAULT; + } +out: + kfree(params); return err; } @@ -325,14 +340,17 @@ static int snd_pcm_hw_params(snd_pcm_sub snd_assert(substream != NULL, return -ENXIO); runtime = substream->runtime; snd_assert(runtime != NULL, return -ENXIO); + snd_pcm_stream_lock_irq(substream); switch (runtime->status->state) { case SNDRV_PCM_STATE_OPEN: case SNDRV_PCM_STATE_SETUP: case SNDRV_PCM_STATE_PREPARED: break; default: + snd_pcm_stream_unlock_irq(substream); return -EBADFD; } + snd_pcm_stream_unlock_irq(substream); #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) if (!substream->oss.oss) #endif @@ -408,13 +426,25 @@ static int snd_pcm_hw_params(snd_pcm_sub static int snd_pcm_hw_params_user(snd_pcm_substream_t * substream, snd_pcm_hw_params_t __user * _params) { - snd_pcm_hw_params_t params; + snd_pcm_hw_params_t *params; int err; - if (copy_from_user(¶ms, _params, sizeof(params))) - return -EFAULT; - err = snd_pcm_hw_params(substream, ¶ms); - if (copy_to_user(_params, ¶ms, sizeof(params))) - return -EFAULT; + + params = kmalloc(sizeof(*params), GFP_KERNEL); + if (!params) { + err = -ENOMEM; + goto out; + } + if (copy_from_user(params, _params, sizeof(*params))) { + err = -EFAULT; + goto out; + } + err = snd_pcm_hw_params(substream, params); + if (copy_to_user(_params, params, sizeof(*params))) { + if (!err) + err = -EFAULT; + } +out: + kfree(params); return err; } @@ -426,13 +456,16 @@ static int snd_pcm_hw_free(snd_pcm_subst snd_assert(substream != NULL, return -ENXIO); runtime = substream->runtime; snd_assert(runtime != NULL, return -ENXIO); + snd_pcm_stream_lock_irq(substream); switch (runtime->status->state) { case SNDRV_PCM_STATE_SETUP: case SNDRV_PCM_STATE_PREPARED: break; default: + snd_pcm_stream_unlock_irq(substream); return -EBADFD; } + snd_pcm_stream_unlock_irq(substream); if (atomic_read(&runtime->mmap_count)) return -EBADFD; if (substream->ops->hw_free == NULL) { @@ -447,11 +480,16 @@ static int snd_pcm_hw_free(snd_pcm_subst static int snd_pcm_sw_params(snd_pcm_substream_t * substream, snd_pcm_sw_params_t *params) { snd_pcm_runtime_t *runtime; + snd_assert(substream != NULL, return -ENXIO); runtime = substream->runtime; snd_assert(runtime != NULL, return -ENXIO); - if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + snd_pcm_stream_lock_irq(substream); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { + snd_pcm_stream_unlock_irq(substream); return -EBADFD; + } + snd_pcm_stream_unlock_irq(substream); if (params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST) return -EINVAL; @@ -579,8 +617,12 @@ static int snd_pcm_channel_info(snd_pcm_ return -EFAULT; channel = info.channel; runtime = substream->runtime; - if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + snd_pcm_stream_lock_irq(substream); + if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { + snd_pcm_stream_unlock_irq(substream); return -EBADFD; + } + snd_pcm_stream_unlock_irq(substream); if (channel >= runtime->channels) return -EINVAL; memset(&info, 0, sizeof(info)); @@ -620,7 +662,7 @@ struct action_ops { */ static int snd_pcm_action_group(struct action_ops *ops, snd_pcm_substream_t *substream, - int state, int atomic_only) + int state, int do_lock) { struct list_head *pos; snd_pcm_substream_t *s = NULL; @@ -628,9 +670,7 @@ static int snd_pcm_action_group(struct a snd_pcm_group_for_each(pos, substream) { s = snd_pcm_group_substream_entry(pos); - if (atomic_only && (s->pcm->info_flags & SNDRV_PCM_INFO_NONATOMIC_OPS)) - continue; - if (s != substream) + if (do_lock && s != substream) spin_lock(&s->self_group.lock); res = ops->pre_action(s, state); if (res < 0) @@ -639,8 +679,6 @@ static int snd_pcm_action_group(struct a if (res >= 0) { snd_pcm_group_for_each(pos, substream) { s = snd_pcm_group_substream_entry(pos); - if (atomic_only && (s->pcm->info_flags & SNDRV_PCM_INFO_NONATOMIC_OPS)) - continue; err = ops->do_action(s, state); if (err < 0) { if (res == 0) @@ -648,17 +686,15 @@ static int snd_pcm_action_group(struct a } else { ops->post_action(s, state); } - if (s != substream) + if (do_lock && s != substream) spin_unlock(&s->self_group.lock); } - } else { + } else if (do_lock) { snd_pcm_substream_t *s1; /* unlock all streams */ snd_pcm_group_for_each(pos, substream) { s1 = snd_pcm_group_substream_entry(pos); - if (atomic_only && (s1->pcm->info_flags & SNDRV_PCM_INFO_NONATOMIC_OPS)) - ; - else if (s1 != substream) + if (s1 != substream) spin_unlock(&s1->self_group.lock); if (s1 == s) /* end */ break; @@ -688,8 +724,6 @@ static int snd_pcm_action_single(struct /* * Note: call with stream lock - * - * NB2: this won't handle the non-atomic callbacks */ static int snd_pcm_action(struct action_ops *ops, snd_pcm_substream_t *substream, @@ -703,7 +737,7 @@ static int snd_pcm_action(struct action_ spin_lock(&substream->group->lock); spin_lock(&substream->self_group.lock); } - res = snd_pcm_action_group(ops, substream, state, 0); + res = snd_pcm_action_group(ops, substream, state, 1); spin_unlock(&substream->group->lock); } else { res = snd_pcm_action_single(ops, substream, state); @@ -713,14 +747,10 @@ static int snd_pcm_action(struct action_ /* * Note: don't use any locks before - * - * NB2: this can handle the non-atomic callbacks if allow_nonatomic = 1 - * when the pcm->info_flags has NONATOMIC_OPS bit, it's handled - * ouside the lock to allow sleep in the callback. */ static int snd_pcm_action_lock_irq(struct action_ops *ops, snd_pcm_substream_t *substream, - int state, int allow_nonatomic) + int state) { int res; @@ -728,43 +758,10 @@ static int snd_pcm_action_lock_irq(struc if (snd_pcm_stream_linked(substream)) { spin_lock(&substream->group->lock); spin_lock(&substream->self_group.lock); - res = snd_pcm_action_group(ops, substream, state, allow_nonatomic); + res = snd_pcm_action_group(ops, substream, state, 1); spin_unlock(&substream->self_group.lock); spin_unlock(&substream->group->lock); - if (res >= 0 && allow_nonatomic) { - /* now process the non-atomic substreams separately - * outside the lock - */ -#define MAX_LINKED_STREAMS 16 /* FIXME: should be variable */ - - struct list_head *pos; - int i, num_s = 0; - snd_pcm_substream_t *s; - snd_pcm_substream_t *subs[MAX_LINKED_STREAMS]; - snd_pcm_group_for_each(pos, substream) { - if (num_s >= MAX_LINKED_STREAMS) { - res = -ENOMEM; - num_s = 0; /* don't proceed */ - break; - } - s = snd_pcm_group_substream_entry(pos); - if (s->pcm->info_flags & SNDRV_PCM_INFO_NONATOMIC_OPS) - subs[num_s++] = s; - } - if (num_s > 0) { - read_unlock_irq(&snd_pcm_link_rwlock); - for (i = 0; i < num_s && res >= 0; i++) - res = snd_pcm_action_single(ops, subs[i], state); - return res; - } - } } else { - if (allow_nonatomic && - (substream->pcm->info_flags & SNDRV_PCM_INFO_NONATOMIC_OPS)) { - read_unlock_irq(&snd_pcm_link_rwlock); - /* process outside the lock */ - return snd_pcm_action_single(ops, substream, state); - } spin_lock(&substream->self_group.lock); res = snd_pcm_action_single(ops, substream, state); spin_unlock(&substream->self_group.lock); @@ -773,6 +770,23 @@ static int snd_pcm_action_lock_irq(struc return res; } +/* + */ +static int snd_pcm_action_nonatomic(struct action_ops *ops, + snd_pcm_substream_t *substream, + int state) +{ + int res; + + down_read(&snd_pcm_link_rwsem); + if (snd_pcm_stream_linked(substream)) + res = snd_pcm_action_group(ops, substream, state, 0); + else + res = snd_pcm_action_single(ops, substream, state); + up_read(&snd_pcm_link_rwsem); + return res; +} + static int snd_pcm_pre_start(snd_pcm_substream_t *substream, int state) { snd_pcm_runtime_t *runtime = substream->runtime; @@ -1038,7 +1052,7 @@ static int snd_pcm_resume(snd_pcm_substr snd_power_lock(card); if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile)) >= 0) - res = snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream, 0, 0); + res = snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream, 0); snd_power_unlock(card); return res; } @@ -1128,7 +1142,7 @@ static struct action_ops snd_pcm_action_ static int snd_pcm_reset(snd_pcm_substream_t *substream) { - return snd_pcm_action_lock_irq(&snd_pcm_action_reset, substream, 0, 0); + return snd_pcm_action_nonatomic(&snd_pcm_action_reset, substream, 0); } static int snd_pcm_pre_prepare(snd_pcm_substream_t * substream, int state) @@ -1176,7 +1190,7 @@ int snd_pcm_prepare(snd_pcm_substream_t snd_power_lock(card); if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile)) >= 0) - res = snd_pcm_action_lock_irq(&snd_pcm_action_prepare, substream, 0, 1); /* allow sleep if specified */ + res = snd_pcm_action_nonatomic(&snd_pcm_action_prepare, substream, 0); snd_power_unlock(card); return res; } @@ -1498,13 +1512,18 @@ static int snd_pcm_link(snd_pcm_substrea snd_pcm_file_t *pcm_file; snd_pcm_substream_t *substream1; - if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) + snd_pcm_stream_lock_irq(substream); + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { + snd_pcm_stream_unlock_irq(substream); return -EBADFD; + } + snd_pcm_stream_unlock_irq(substream); file = snd_pcm_file_fd(fd); if (!file) return -EBADFD; - pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + pcm_file = file->private_data; substream1 = pcm_file->substream; + down_write(&snd_pcm_link_rwsem); write_lock_irq(&snd_pcm_link_rwlock); if (substream->runtime->status->state != substream1->runtime->status->state) { res = -EBADFD; @@ -1528,6 +1547,7 @@ static int snd_pcm_link(snd_pcm_substrea substream1->group = substream->group; _end: write_unlock_irq(&snd_pcm_link_rwlock); + up_write(&snd_pcm_link_rwsem); fput(file); return res; } @@ -1544,6 +1564,7 @@ static int snd_pcm_unlink(snd_pcm_substr struct list_head *pos; int res = 0, count = 0; + down_write(&snd_pcm_link_rwsem); write_lock_irq(&snd_pcm_link_rwlock); if (!snd_pcm_stream_linked(substream)) { res = -EALREADY; @@ -1564,6 +1585,7 @@ static int snd_pcm_unlink(snd_pcm_substr relink_to_local(substream); _end: write_unlock_irq(&snd_pcm_link_rwlock); + up_write(&snd_pcm_link_rwsem); return res; } @@ -1658,13 +1680,12 @@ static int snd_pcm_hw_rule_sample_bits(s static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000 }; -#define RATES (sizeof(rates) / sizeof(rates[0])) - static int snd_pcm_hw_rule_rate(snd_pcm_hw_params_t *params, snd_pcm_hw_rule_t *rule) { snd_pcm_hardware_t *hw = rule->private; - return snd_interval_list(hw_param_interval(params, rule->var), RATES, rates, hw->rates); + return snd_interval_list(hw_param_interval(params, rule->var), + ARRAY_SIZE(rates), rates, hw->rates); } static int snd_pcm_hw_rule_buffer_bytes_max(snd_pcm_hw_params_t *params, @@ -1923,7 +1944,7 @@ static int snd_pcm_release_file(snd_pcm_ substream->ffile = NULL; snd_pcm_remove_file(str, pcm_file); snd_pcm_release_substream(substream); - snd_magic_kfree(pcm_file); + kfree(pcm_file); return 0; } @@ -1940,13 +1961,13 @@ static int snd_pcm_open_file(struct file snd_assert(rpcm_file != NULL, return -EINVAL); *rpcm_file = NULL; - pcm_file = snd_magic_kcalloc(snd_pcm_file_t, 0, GFP_KERNEL); + pcm_file = kcalloc(1, sizeof(*pcm_file), GFP_KERNEL); if (pcm_file == NULL) { return -ENOMEM; } if ((err = snd_pcm_open_substream(pcm, stream, &substream)) < 0) { - snd_magic_kfree(pcm_file); + kfree(pcm_file); return err; } @@ -2050,7 +2071,7 @@ int snd_pcm_release(struct inode *inode, snd_pcm_substream_t *substream; snd_pcm_file_t *pcm_file; - pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + pcm_file = file->private_data; substream = pcm_file->substream; snd_assert(substream != NULL, return -ENXIO); snd_assert(!atomic_read(&substream->runtime->mmap_count), ); @@ -2414,7 +2435,7 @@ static int snd_pcm_common_ioctl1(snd_pcm case SNDRV_PCM_IOCTL_RESET: return snd_pcm_reset(substream); case SNDRV_PCM_IOCTL_START: - return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, 0, 0); + return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, 0); case SNDRV_PCM_IOCTL_LINK: return snd_pcm_link(substream, (int)(unsigned long) arg); case SNDRV_PCM_IOCTL_UNLINK: @@ -2616,26 +2637,36 @@ static int snd_pcm_playback_ioctl(struct unsigned int cmd, unsigned long arg) { snd_pcm_file_t *pcm_file; + int err; - pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + pcm_file = file->private_data; if (((cmd >> 8) & 0xff) != 'A') return -ENOTTY; - return snd_pcm_playback_ioctl1(pcm_file->substream, cmd, (void __user *)arg); + /* FIXME: need to unlock BKL to allow preemption */ + unlock_kernel(); + err = snd_pcm_playback_ioctl1(pcm_file->substream, cmd, (void __user *)arg); + lock_kernel(); + return err; } static int snd_pcm_capture_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { snd_pcm_file_t *pcm_file; + int err; - pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + pcm_file = file->private_data; if (((cmd >> 8) & 0xff) != 'A') return -ENOTTY; - return snd_pcm_capture_ioctl1(pcm_file->substream, cmd, (void __user *)arg); + /* FIXME: need to unlock BKL to allow preemption */ + unlock_kernel(); + err = snd_pcm_capture_ioctl1(pcm_file->substream, cmd, (void __user *)arg); + lock_kernel(); + return err; } int snd_pcm_kernel_playback_ioctl(snd_pcm_substream_t *substream, @@ -2682,7 +2713,7 @@ static ssize_t snd_pcm_read(struct file snd_pcm_runtime_t *runtime; snd_pcm_sframes_t result; - pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + pcm_file = file->private_data; substream = pcm_file->substream; snd_assert(substream != NULL, return -ENXIO); runtime = substream->runtime; @@ -2704,7 +2735,7 @@ static ssize_t snd_pcm_write(struct file snd_pcm_runtime_t *runtime; snd_pcm_sframes_t result; - pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, result = -ENXIO; goto end); + pcm_file = file->private_data; substream = pcm_file->substream; snd_assert(substream != NULL, result = -ENXIO; goto end); runtime = substream->runtime; @@ -2736,7 +2767,7 @@ static ssize_t snd_pcm_readv(struct file void __user **bufs; snd_pcm_uframes_t frames; - pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + pcm_file = file->private_data; substream = pcm_file->substream; snd_assert(substream != NULL, return -ENXIO); runtime = substream->runtime; @@ -2770,7 +2801,7 @@ static ssize_t snd_pcm_writev(struct fil void __user **bufs; snd_pcm_uframes_t frames; - pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, result = -ENXIO; goto end); + pcm_file = file->private_data; substream = pcm_file->substream; snd_assert(substream != NULL, result = -ENXIO; goto end); runtime = substream->runtime; @@ -2805,7 +2836,7 @@ unsigned int snd_pcm_playback_poll(struc unsigned int mask; snd_pcm_uframes_t avail; - pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return 0); + pcm_file = file->private_data; substream = pcm_file->substream; snd_assert(substream != NULL, return -ENXIO); @@ -2843,7 +2874,7 @@ unsigned int snd_pcm_capture_poll(struct unsigned int mask; snd_pcm_uframes_t avail; - pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return 0); + pcm_file = file->private_data; substream = pcm_file->substream; snd_assert(substream != NULL, return -ENXIO); @@ -2877,6 +2908,18 @@ unsigned int snd_pcm_capture_poll(struct return mask; } +/* + * mmap support + */ + +/* + * Only on coherent architectures, we can mmap the status and the control records + * for effcient data transfer. On others, we have to use HWSYNC ioctl... + */ +#if defined(CONFIG_X86) || defined(CONFIG_PPC) || defined(CONFIG_ALPHA) +/* + * mmap status record + */ static struct page * snd_pcm_mmap_status_nopage(struct vm_area_struct *area, unsigned long address, int *type) { snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; @@ -2899,8 +2942,8 @@ static struct vm_operations_struct snd_p .nopage = snd_pcm_mmap_status_nopage, }; -int snd_pcm_mmap_status(snd_pcm_substream_t *substream, struct file *file, - struct vm_area_struct *area) +static int snd_pcm_mmap_status(snd_pcm_substream_t *substream, struct file *file, + struct vm_area_struct *area) { snd_pcm_runtime_t *runtime; long size; @@ -2917,6 +2960,9 @@ int snd_pcm_mmap_status(snd_pcm_substrea return 0; } +/* + * mmap control record + */ static struct page * snd_pcm_mmap_control_nopage(struct vm_area_struct *area, unsigned long address, int *type) { snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; @@ -2956,20 +3002,26 @@ static int snd_pcm_mmap_control(snd_pcm_ area->vm_flags |= VM_RESERVED; return 0; } - -static void snd_pcm_mmap_data_open(struct vm_area_struct *area) +#else /* ! coherent mmap */ +/* + * don't support mmap for status and control records. + */ +static int snd_pcm_mmap_status(snd_pcm_substream_t *substream, struct file *file, + struct vm_area_struct *area) { - snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; - atomic_inc(&substream->runtime->mmap_count); + return -ENXIO; } - -static void snd_pcm_mmap_data_close(struct vm_area_struct *area) +static int snd_pcm_mmap_control(snd_pcm_substream_t *substream, struct file *file, + struct vm_area_struct *area) { - snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; - atomic_dec(&substream->runtime->mmap_count); + return -ENXIO; } +#endif /* coherent mmap */ -static struct page * snd_pcm_mmap_data_nopage(struct vm_area_struct *area, unsigned long address, int *type) +/* + * nopage callback for mmapping a RAM page + */ +static struct page *snd_pcm_mmap_data_nopage(struct vm_area_struct *area, unsigned long address, int *type) { snd_pcm_substream_t *substream = (snd_pcm_substream_t *)area->vm_private_data; snd_pcm_runtime_t *runtime; @@ -3009,6 +3061,52 @@ static struct vm_operations_struct snd_p .nopage = snd_pcm_mmap_data_nopage, }; +/* + * mmap the DMA buffer on RAM + */ +static int snd_pcm_default_mmap(snd_pcm_substream_t *substream, struct vm_area_struct *area) +{ + area->vm_ops = &snd_pcm_vm_ops_data; + area->vm_private_data = substream; + area->vm_flags |= VM_RESERVED; + atomic_inc(&substream->runtime->mmap_count); + return 0; +} + +/* + * mmap the DMA buffer on I/O memory area + */ +#if SNDRV_PCM_INFO_MMAP_IOMEM +static struct vm_operations_struct snd_pcm_vm_ops_data_mmio = +{ + .open = snd_pcm_mmap_data_open, + .close = snd_pcm_mmap_data_close, +}; + +int snd_pcm_lib_mmap_iomem(snd_pcm_substream_t *substream, struct vm_area_struct *area) +{ + long size; + unsigned long offset; + +#ifdef pgprot_noncached + area->vm_page_prot = pgprot_noncached(area->vm_page_prot); +#endif + area->vm_ops = &snd_pcm_vm_ops_data_mmio; + area->vm_flags |= VM_IO; + size = area->vm_end - area->vm_start; + offset = area->vm_pgoff << PAGE_SHIFT; + if (io_remap_page_range(area, area->vm_start, + substream->runtime->dma_addr + offset, + size, area->vm_page_prot)) + return -EAGAIN; + atomic_inc(&substream->runtime->mmap_count); + return 0; +} +#endif /* SNDRV_PCM_INFO_MMAP */ + +/* + * mmap DMA buffer + */ int snd_pcm_mmap_data(snd_pcm_substream_t *substream, struct file *file, struct vm_area_struct *area) { @@ -3041,11 +3139,10 @@ int snd_pcm_mmap_data(snd_pcm_substream_ if (offset > dma_bytes - size) return -EINVAL; - area->vm_ops = &snd_pcm_vm_ops_data; - area->vm_private_data = substream; - area->vm_flags |= VM_RESERVED; - atomic_inc(&runtime->mmap_count); - return 0; + if (substream->ops->mmap) + return substream->ops->mmap(substream, area); + else + return snd_pcm_default_mmap(substream, area); } static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) @@ -3054,7 +3151,7 @@ static int snd_pcm_mmap(struct file *fil snd_pcm_substream_t *substream; unsigned long offset; - pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + pcm_file = file->private_data; substream = pcm_file->substream; snd_assert(substream != NULL, return -ENXIO); @@ -3077,7 +3174,7 @@ static int snd_pcm_fasync(int fd, struct snd_pcm_runtime_t *runtime; int err; - pcm_file = snd_magic_cast(snd_pcm_file_t, file->private_data, return -ENXIO); + pcm_file = file->private_data; substream = pcm_file->substream; snd_assert(substream != NULL, return -ENXIO); runtime = substream->runtime; @@ -3101,7 +3198,7 @@ static void snd_pcm_hw_convert_from_old_ memset(params, 0, sizeof(*params)); params->flags = oparams->flags; - for (i = 0; i < sizeof(oparams->masks) / sizeof(unsigned int); i++) + for (i = 0; i < ARRAY_SIZE(oparams->masks); i++) params->masks[i].bits[0] = oparams->masks[i]; memcpy(params->intervals, oparams->intervals, sizeof(oparams->intervals)); params->rmask = __OLD_TO_NEW_MASK(oparams->rmask); @@ -3119,7 +3216,7 @@ static void snd_pcm_hw_convert_to_old_pa memset(oparams, 0, sizeof(*oparams)); oparams->flags = params->flags; - for (i = 0; i < sizeof(oparams->masks) / sizeof(unsigned int); i++) + for (i = 0; i < ARRAY_SIZE(oparams->masks); i++) oparams->masks[i] = params->masks[i].bits[0]; memcpy(oparams->intervals, params->intervals, sizeof(oparams->intervals)); oparams->rmask = __NEW_TO_OLD_MASK(params->rmask); @@ -3133,31 +3230,68 @@ static void snd_pcm_hw_convert_to_old_pa static int snd_pcm_hw_refine_old_user(snd_pcm_substream_t * substream, struct sndrv_pcm_hw_params_old __user * _oparams) { - snd_pcm_hw_params_t params; - struct sndrv_pcm_hw_params_old oparams; + snd_pcm_hw_params_t *params; + struct sndrv_pcm_hw_params_old *oparams = NULL; int err; - if (copy_from_user(&oparams, _oparams, sizeof(oparams))) - return -EFAULT; - snd_pcm_hw_convert_from_old_params(¶ms, &oparams); - err = snd_pcm_hw_refine(substream, ¶ms); - snd_pcm_hw_convert_to_old_params(&oparams, ¶ms); - if (copy_to_user(_oparams, &oparams, sizeof(oparams))) - return -EFAULT; + + params = kmalloc(sizeof(*params), GFP_KERNEL); + if (!params) { + err = -ENOMEM; + goto out; + } + oparams = kmalloc(sizeof(*oparams), GFP_KERNEL); + if (!oparams) { + err = -ENOMEM; + goto out; + } + + if (copy_from_user(oparams, _oparams, sizeof(*oparams))) { + err = -EFAULT; + goto out; + } + snd_pcm_hw_convert_from_old_params(params, oparams); + err = snd_pcm_hw_refine(substream, params); + snd_pcm_hw_convert_to_old_params(oparams, params); + if (copy_to_user(_oparams, oparams, sizeof(*oparams))) { + if (!err) + err = -EFAULT; + } +out: + kfree(params); + kfree(oparams); return err; } static int snd_pcm_hw_params_old_user(snd_pcm_substream_t * substream, struct sndrv_pcm_hw_params_old __user * _oparams) { - snd_pcm_hw_params_t params; - struct sndrv_pcm_hw_params_old oparams; + snd_pcm_hw_params_t *params; + struct sndrv_pcm_hw_params_old *oparams = NULL; int err; - if (copy_from_user(&oparams, _oparams, sizeof(oparams))) - return -EFAULT; - snd_pcm_hw_convert_from_old_params(¶ms, &oparams); - err = snd_pcm_hw_params(substream, ¶ms); - snd_pcm_hw_convert_to_old_params(&oparams, ¶ms); - if (copy_to_user(_oparams, &oparams, sizeof(oparams))) - return -EFAULT; + + params = kmalloc(sizeof(*params), GFP_KERNEL); + if (!params) { + err = -ENOMEM; + goto out; + } + oparams = kmalloc(sizeof(*oparams), GFP_KERNEL); + if (!oparams) { + err = -ENOMEM; + goto out; + } + if (copy_from_user(oparams, _oparams, sizeof(*oparams))) { + err = -EFAULT; + goto out; + } + snd_pcm_hw_convert_from_old_params(params, oparams); + err = snd_pcm_hw_params(substream, params); + snd_pcm_hw_convert_to_old_params(oparams, params); + if (copy_to_user(_oparams, oparams, sizeof(*oparams))) { + if (!err) + err = -EFAULT; + } +out: + kfree(params); + kfree(oparams); return err; } --- linux-2.6.9-rc1/sound/core/pcm_timer.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/sound/core/pcm_timer.c 2004-09-02 20:51:53.000000000 -0700 @@ -25,8 +25,6 @@ #include #include -#define chip_t snd_pcm_substream_t - /* * Timer functions */ @@ -49,7 +47,7 @@ static unsigned long gcd(unsigned long a void snd_pcm_timer_resolution_change(snd_pcm_substream_t *substream) { - unsigned long rate, mult, fsize, l; + unsigned long rate, mult, fsize, l, post; snd_pcm_runtime_t *runtime = substream->runtime; mult = 1000000000; @@ -63,23 +61,24 @@ void snd_pcm_timer_resolution_change(snd l = gcd(rate, fsize); rate /= l; fsize /= l; + post = 1; while ((mult * fsize) / fsize != mult) { mult /= 2; - rate /= 2; + post *= 2; } if (rate == 0) { snd_printk(KERN_ERR "pcm timer resolution out of range (rate = %u, period_size = %lu)\n", runtime->rate, runtime->period_size); runtime->timer_resolution = -1; return; } - runtime->timer_resolution = mult * fsize / rate; + runtime->timer_resolution = (mult * fsize / rate) * post; } static unsigned long snd_pcm_timer_resolution(snd_timer_t * timer) { snd_pcm_substream_t * substream; - substream = snd_magic_cast(snd_pcm_substream_t, timer->private_data, return -ENXIO); + substream = timer->private_data; return substream->runtime ? substream->runtime->timer_resolution : 0; } @@ -123,7 +122,7 @@ static struct _snd_timer_hardware snd_pc static void snd_pcm_timer_free(snd_timer_t *timer) { - snd_pcm_substream_t *substream = snd_magic_cast(snd_pcm_substream_t, timer->private_data, return); + snd_pcm_substream_t *substream = timer->private_data; substream->timer = NULL; } --- linux-2.6.9-rc1/sound/core/rawmidi.c 2004-08-24 00:01:55.000000000 -0700 +++ 25/sound/core/rawmidi.c 2004-09-02 20:51:53.000000000 -0700 @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -44,10 +45,8 @@ static int amidi_map[SNDRV_CARDS] = {[0 static int boot_devs; module_param_array(midi_map, int, boot_devs, 0444); MODULE_PARM_DESC(midi_map, "Raw MIDI device number assigned to 1st OSS device."); -MODULE_PARM_SYNTAX(midi_map, "default:0,skill:advanced"); module_param_array(amidi_map, int, boot_devs, 0444); MODULE_PARM_DESC(amidi_map, "Raw MIDI device number assigned to 2nd OSS device."); -MODULE_PARM_SYNTAX(amidi_map, "default:1,skill:advanced"); #endif /* CONFIG_SND_OSSEMUL */ static int snd_rawmidi_free(snd_rawmidi_t *rawmidi); @@ -269,7 +268,7 @@ int snd_rawmidi_kernel_open(int cardnum, list2 = list2->next; } if (mode & SNDRV_RAWMIDI_LFLG_INPUT) { - input = snd_kcalloc(sizeof(snd_rawmidi_runtime_t), GFP_KERNEL); + input = kcalloc(1, sizeof(*input), GFP_KERNEL); if (input == NULL) { err = -ENOMEM; goto __error; @@ -291,7 +290,7 @@ int snd_rawmidi_kernel_open(int cardnum, if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) { if (soutput->opened) goto __skip_output; - output = snd_kcalloc(sizeof(snd_rawmidi_runtime_t), GFP_KERNEL); + output = kcalloc(1, sizeof(*output), GFP_KERNEL); if (output == NULL) { err = -ENOMEM; goto __error; @@ -398,7 +397,7 @@ static int snd_rawmidi_open(struct inode if ((file->f_flags & O_APPEND) || maj != CONFIG_SND_MAJOR) /* OSS emul? */ fflags |= SNDRV_RAWMIDI_LFLG_APPEND; fflags |= SNDRV_RAWMIDI_LFLG_NOOPENLOCK; - rawmidi_file = snd_magic_kmalloc(snd_rawmidi_file_t, 0, GFP_KERNEL); + rawmidi_file = kmalloc(sizeof(*rawmidi_file), GFP_KERNEL); if (rawmidi_file == NULL) { snd_card_file_remove(card, file); return -ENOMEM; @@ -447,7 +446,7 @@ static int snd_rawmidi_open(struct inode file->private_data = rawmidi_file; } else { snd_card_file_remove(card, file); - snd_magic_kfree(rawmidi_file); + kfree(rawmidi_file); } up(&rmidi->open_mutex); return err; @@ -512,11 +511,11 @@ static int snd_rawmidi_release(struct in snd_rawmidi_t *rmidi; int err; - rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return -ENXIO); + rfile = file->private_data; err = snd_rawmidi_kernel_release(rfile); rmidi = rfile->rmidi; wake_up(&rmidi->open_wait); - snd_magic_kfree(rfile); + kfree(rfile); snd_card_file_remove(rmidi->card, file); return err; } @@ -675,13 +674,13 @@ static int snd_rawmidi_input_status(snd_ return 0; } -static int snd_rawmidi_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static inline int _snd_rawmidi_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) { snd_rawmidi_file_t *rfile; void __user *argp = (void __user *)arg; - rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return -ENXIO); + rfile = file->private_data; if (((cmd >> 8) & 0xff) != 'W') return -ENOTTY; switch (cmd) { @@ -786,6 +785,17 @@ static int snd_rawmidi_ioctl(struct inod return -ENOTTY; } +/* FIXME: need to unlock BKL to allow preemption */ +static int snd_rawmidi_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err; + unlock_kernel(); + err = _snd_rawmidi_ioctl(inode, file, cmd, arg); + lock_kernel(); + return err; +} + int snd_rawmidi_control_ioctl(snd_card_t * card, snd_ctl_file_t * control, unsigned int cmd, unsigned long arg) { @@ -944,7 +954,7 @@ static ssize_t snd_rawmidi_read(struct f snd_rawmidi_substream_t *substream; snd_rawmidi_runtime_t *runtime; - rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return -ENXIO); + rfile = file->private_data; substream = rfile->input; if (substream == NULL) return -EIO; @@ -974,6 +984,8 @@ static ssize_t snd_rawmidi_read(struct f } spin_unlock_irq(&runtime->lock); count1 = snd_rawmidi_kernel_read1(substream, buf, count, 0); + if (count1 < 0) + return result > 0 ? result : count1; result += count1; buf += count1; count -= count1; @@ -1176,7 +1188,7 @@ static ssize_t snd_rawmidi_write(struct snd_rawmidi_runtime_t *runtime; snd_rawmidi_substream_t *substream; - rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return -ENXIO); + rfile = file->private_data; substream = rfile->output; runtime = substream->runtime; /* we cannot put an atomic message to our buffer */ @@ -1206,14 +1218,14 @@ static ssize_t snd_rawmidi_write(struct spin_unlock_irq(&runtime->lock); count1 = snd_rawmidi_kernel_write1(substream, buf, count, 0); if (count1 < 0) - continue; + return result > 0 ? result : count1; result += count1; buf += count1; if ((size_t)count1 < count && (file->f_flags & O_NONBLOCK)) break; count -= count1; } - while (file->f_flags & O_SYNC) { + if (file->f_flags & O_SYNC) { spin_lock_irq(&runtime->lock); while (runtime->avail != runtime->buffer_size) { wait_queue_t wait; @@ -1241,7 +1253,7 @@ static unsigned int snd_rawmidi_poll(str snd_rawmidi_runtime_t *runtime; unsigned int mask; - rfile = snd_magic_cast(snd_rawmidi_file_t, file->private_data, return 0); + rfile = file->private_data; if (rfile->input != NULL) { runtime = rfile->input->runtime; runtime->trigger = 1; @@ -1276,7 +1288,7 @@ static void snd_rawmidi_proc_info_read(s snd_rawmidi_runtime_t *runtime; struct list_head *list; - rmidi = snd_magic_cast(snd_rawmidi_t, entry->private_data, return); + rmidi = entry->private_data; snd_iprintf(buffer, "%s\n\n", rmidi->name); down(&rmidi->open_mutex); if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT) { @@ -1353,7 +1365,7 @@ static int snd_rawmidi_alloc_substreams( INIT_LIST_HEAD(&stream->substreams); for (idx = 0; idx < count; idx++) { - substream = snd_kcalloc(sizeof(snd_rawmidi_substream_t), GFP_KERNEL); + substream = kcalloc(1, sizeof(*substream), GFP_KERNEL); if (substream == NULL) return -ENOMEM; substream->stream = direction; @@ -1396,7 +1408,7 @@ int snd_rawmidi_new(snd_card_t * card, c snd_assert(rrawmidi != NULL, return -EINVAL); *rrawmidi = NULL; snd_assert(card != NULL, return -ENXIO); - rmidi = snd_magic_kcalloc(snd_rawmidi_t, 0, GFP_KERNEL); + rmidi = kcalloc(1, sizeof(*rmidi), GFP_KERNEL); if (rmidi == NULL) return -ENOMEM; rmidi->card = card; @@ -1439,20 +1451,20 @@ static int snd_rawmidi_free(snd_rawmidi_ snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]); if (rmidi->private_free) rmidi->private_free(rmidi); - snd_magic_kfree(rmidi); + kfree(rmidi); return 0; } static int snd_rawmidi_dev_free(snd_device_t *device) { - snd_rawmidi_t *rmidi = snd_magic_cast(snd_rawmidi_t, device->device_data, return -ENXIO); + snd_rawmidi_t *rmidi = device->device_data; return snd_rawmidi_free(rmidi); } #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) static void snd_rawmidi_dev_seq_free(snd_seq_device_t *device) { - snd_rawmidi_t *rmidi = snd_magic_cast(snd_rawmidi_t, device->private_data, return); + snd_rawmidi_t *rmidi = device->private_data; rmidi->seq_dev = NULL; } #endif @@ -1462,7 +1474,7 @@ static int snd_rawmidi_dev_register(snd_ int idx, err; snd_info_entry_t *entry; char name[16]; - snd_rawmidi_t *rmidi = snd_magic_cast(snd_rawmidi_t, device->device_data, return -ENXIO); + snd_rawmidi_t *rmidi = device->device_data; if (rmidi->device >= SNDRV_RAWMIDI_DEVICES) return -ENOMEM; @@ -1539,7 +1551,7 @@ static int snd_rawmidi_dev_register(snd_ static int snd_rawmidi_dev_disconnect(snd_device_t *device) { - snd_rawmidi_t *rmidi = snd_magic_cast(snd_rawmidi_t, device->device_data, return -ENXIO); + snd_rawmidi_t *rmidi = device->device_data; int idx; down(®ister_mutex); @@ -1552,7 +1564,7 @@ static int snd_rawmidi_dev_disconnect(sn static int snd_rawmidi_dev_unregister(snd_device_t *device) { int idx; - snd_rawmidi_t *rmidi = snd_magic_cast(snd_rawmidi_t, device->device_data, return -ENXIO); + snd_rawmidi_t *rmidi = device->device_data; snd_assert(rmidi != NULL, return -ENXIO); down(®ister_mutex); --- linux-2.6.9-rc1/sound/core/seq/instr/ainstr_fm.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/sound/core/seq/instr/ainstr_fm.c 2004-09-02 20:51:53.000000000 -0700 @@ -29,8 +29,6 @@ MODULE_AUTHOR("Uros Bizjak "); MODULE_DESCRIPTION("Advanced Linux Sound Architecture FM Instrument support."); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_SUPPORTED_DEVICE("sound"); char *snd_seq_fm_id = SNDRV_SEQ_INSTR_ID_OPL2_3; --- linux-2.6.9-rc1/sound/core/seq/instr/ainstr_gf1.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/sound/core/seq/instr/ainstr_gf1.c 2004-09-02 20:51:53.000000000 -0700 @@ -30,8 +30,6 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Advanced Linux Sound Architecture GF1 (GUS) Patch support."); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_SUPPORTED_DEVICE("sound"); char *snd_seq_gf1_id = SNDRV_SEQ_INSTR_ID_GUS_PATCH; @@ -64,7 +62,7 @@ static int snd_seq_gf1_copy_wave_from_st return -EFAULT; *data += sizeof(xp); *len -= sizeof(xp); - wp = (gf1_wave_t *)snd_kcalloc(sizeof(*wp), gfp_mask); + wp = kcalloc(1, sizeof(*wp), gfp_mask); if (wp == NULL) return -ENOMEM; wp->share_id[0] = le32_to_cpu(xp.share_id[0]); --- linux-2.6.9-rc1/sound/core/seq/instr/ainstr_iw.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/sound/core/seq/instr/ainstr_iw.c 2004-09-02 20:51:53.000000000 -0700 @@ -30,8 +30,6 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Advanced Linux Sound Architecture IWFFFF support."); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_SUPPORTED_DEVICE("sound"); char *snd_seq_iwffff_id = SNDRV_SEQ_INSTR_ID_INTERWAVE; @@ -96,7 +94,7 @@ static int snd_seq_iwffff_copy_env_from_ points_size = (le16_to_cpu(rx.nattack) + le16_to_cpu(rx.nrelease)) * 2 * sizeof(__u16); if (points_size > *len) return -EINVAL; - rp = (iwffff_env_record_t *)snd_kcalloc(sizeof(*rp) + points_size, gfp_mask); + rp = kcalloc(1, sizeof(*rp) + points_size, gfp_mask); if (rp == NULL) return -ENOMEM; rp->nattack = le16_to_cpu(rx.nattack); @@ -142,7 +140,7 @@ static int snd_seq_iwffff_copy_wave_from return -EFAULT; *data += sizeof(xp); *len -= sizeof(xp); - wp = (iwffff_wave_t *)snd_kcalloc(sizeof(*wp), gfp_mask); + wp = kcalloc(1, sizeof(*wp), gfp_mask); if (wp == NULL) return -ENOMEM; wp->share_id[0] = le32_to_cpu(xp.share_id[0]); @@ -275,7 +273,7 @@ static int snd_seq_iwffff_put(void *priv snd_seq_iwffff_instr_free(ops, ip, atomic); return -EINVAL; } - lp = (iwffff_layer_t *)snd_kcalloc(sizeof(*lp), gfp_mask); + lp = kcalloc(1, sizeof(*lp), gfp_mask); if (lp == NULL) { snd_seq_iwffff_instr_free(ops, ip, atomic); return -ENOMEM; --- linux-2.6.9-rc1/sound/core/seq/instr/ainstr_simple.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/sound/core/seq/instr/ainstr_simple.c 2004-09-02 20:51:53.000000000 -0700 @@ -30,8 +30,6 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Advanced Linux Sound Architecture Simple Instrument support."); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_SUPPORTED_DEVICE("sound"); char *snd_seq_simple_id = SNDRV_SEQ_INSTR_ID_SIMPLE; --- linux-2.6.9-rc1/sound/core/seq/instr/Makefile 2004-08-24 00:03:31.000000000 -0700 +++ 25/sound/core/seq/instr/Makefile 2004-09-02 20:51:53.000000000 -0700 @@ -17,35 +17,7 @@ snd-ainstr-iw-objs := ainstr_iw.o sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) # Toplevel Module Dependency -obj-$(call sequencer,$(CONFIG_SND_ALS100)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_AZT2320)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_AZT3328)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_DT019X)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_ES18XX)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_OPL3SA2)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_AD1816A)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_CS4232)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_CS4236)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_ES1688)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_GUSCLASSIC)) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o -obj-$(call sequencer,$(CONFIG_SND_GUSMAX)) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o -obj-$(call sequencer,$(CONFIG_SND_GUSEXTREME)) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_INTERWAVE)) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o -obj-$(call sequencer,$(CONFIG_SND_INTERWAVE_STB)) += snd-ainstr-iw.o snd-ainstr-gf1.o snd-ainstr-simple.o -obj-$(call sequencer,$(CONFIG_SND_OPTI92X_AD1848)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_OPTI92X_CS4231)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_OPTI93X)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_SB8)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_SB16)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_WAVEFRONT)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_ALS4000)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_CMIPCI)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_CS4281)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_ES1938)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_FM801)) += snd-ainstr-fm.o -obj-$(call sequencer,$(CONFIG_SND_SONICVIBES)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-ainstr-gf1.o snd-ainstr-simple.o snd-ainstr-iw.o obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-ainstr-simple.o -obj-$(call sequencer,$(CONFIG_SND_YMFPCI)) += snd-ainstr-fm.o - -obj-m := $(sort $(obj-m)) --- linux-2.6.9-rc1/sound/core/seq/Makefile 2004-08-24 00:03:31.000000000 -0700 +++ 25/sound/core/seq/Makefile 2004-09-02 20:51:53.000000000 -0700 @@ -19,9 +19,6 @@ snd-seq-instr-objs := seq_instr.o snd-seq-dummy-objs := seq_dummy.o snd-seq-virmidi-objs := seq_virmidi.o -RAWMIDI_OBJS = snd-seq-midi.o snd-seq-midi-event.o -OPL3_OBJS = snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o - # # this function returns: # "m" - CONFIG_SND_SEQUENCER is m @@ -38,53 +35,8 @@ obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-d # Toplevel Module Dependency obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq-midi-event.o -obj-$(call sequencer,$(CONFIG_SND_SERIAL_U16550)) += $(RAWMIDI_OBJS) -obj-$(call sequencer,$(CONFIG_SND_MTPAV)) += $(RAWMIDI_OBJS) -obj-$(call sequencer,$(CONFIG_SND_MPU401)) += $(RAWMIDI_OBJS) -obj-$(call sequencer,$(CONFIG_SND_ALS100)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_AZT2320)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_AZT3328)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_DT019X)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_ES18XX)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_OPL3SA2)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_AD1816A)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_CS4231)) += $(RAWMIDI_OBJS) -obj-$(call sequencer,$(CONFIG_SND_CS4232)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_CS4236)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_ES1688)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_GUSCLASSIC)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_GUSMAX)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_GUSEXTREME)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_INTERWAVE)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_INTERWAVE_STB)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_OPTI92X_AD1848)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_OPTI92X_CS4231)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_OPTI93X)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_SB8)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_SB16)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) snd-seq-virmidi.o -obj-$(call sequencer,$(CONFIG_SND_ES968)) += $(RAWMIDI_OBJS) -obj-$(call sequencer,$(CONFIG_SND_WAVEFRONT)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_SSCAPE)) += $(RAWMIDI_OBJS) -obj-$(call sequencer,$(CONFIG_SND_ALS4000)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_CMIPCI)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_CS4281)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_ENS1370)) += $(RAWMIDI_OBJS) -obj-$(call sequencer,$(CONFIG_SND_ENS1371)) += $(RAWMIDI_OBJS) -obj-$(call sequencer,$(CONFIG_SND_ES1938)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_ES1968)) += $(RAWMIDI_OBJS) -obj-$(call sequencer,$(CONFIG_SND_FM801)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_ICE1712)) += $(RAWMIDI_OBJS) -obj-$(call sequencer,$(CONFIG_SND_ICE1724)) += $(RAWMIDI_OBJS) -obj-$(call sequencer,$(CONFIG_SND_INTEL8X0)) += $(RAWMIDI_OBJS) -obj-$(call sequencer,$(CONFIG_SND_SONICVIBES)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_VIA82XX)) += $(RAWMIDI_OBJS) -obj-$(call sequencer,$(CONFIG_SND_ALI5451)) += $(RAWMIDI_OBJS) -obj-$(call sequencer,$(CONFIG_SND_CS46XX)) += $(RAWMIDI_OBJS) -obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += $(RAWMIDI_OBJS) snd-seq-midi-emul.o snd-seq-virmidi.o -obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += $(RAWMIDI_OBJS) snd-seq-midi-emul.o snd-seq-instr.o -obj-$(call sequencer,$(CONFIG_SND_YMFPCI)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) -obj-$(call sequencer,$(CONFIG_SND_USB_AUDIO)) += $(RAWMIDI_OBJS) -obj-$(call sequencer,$(CONFIG_SND_HDSP)) += $(RAWMIDI_OBJS) - -obj-m := $(sort $(obj-m)) +obj-$(call sequencer,$(CONFIG_SND_RAWMIDI)) += snd-seq-midi.o snd-seq-midi-event.o +obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o +obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-seq-midi-emul.o snd-seq-virmidi.o +obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-seq-midi-emul.o snd-seq-virmidi.o +obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-seq-midi-emul.o snd-seq-instr.o --- linux-2.6.9-rc1/sound/core/seq/oss/seq_oss.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/sound/core/seq/oss/seq_oss.c 2004-09-02 20:51:53.000000000 -0700 @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -35,7 +36,6 @@ MODULE_AUTHOR("Takashi Iwai "); MODULE_DESCRIPTION("OSS-compatible sequencer module"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); /* Takashi says this is really only for sound-service-0-, but this is OK. */ MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_SEQUENCER); MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_MUSIC); @@ -177,9 +177,14 @@ static int odev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { seq_oss_devinfo_t *dp; + int err; dp = file->private_data; snd_assert(dp != NULL, return -EIO); - return snd_seq_oss_ioctl(dp, cmd, arg); + /* FIXME: need to unlock BKL to allow preemption */ + unlock_kernel(); + err = snd_seq_oss_ioctl(dp, cmd, arg); + lock_kernel(); + return err; } --- linux-2.6.9-rc1/sound/core/seq/oss/seq_oss_init.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/sound/core/seq/oss/seq_oss_init.c 2004-09-02 20:51:53.000000000 -0700 @@ -183,7 +183,7 @@ snd_seq_oss_open(struct file *file, int int i, rc; seq_oss_devinfo_t *dp; - if ((dp = snd_kcalloc(sizeof(*dp), GFP_KERNEL)) == NULL) { + if ((dp = kcalloc(1, sizeof(*dp), GFP_KERNEL)) == NULL) { snd_printk(KERN_ERR "can't malloc device info\n"); return -ENOMEM; } @@ -211,7 +211,7 @@ snd_seq_oss_open(struct file *file, int snd_seq_oss_midi_setup(dp); if (dp->synth_opened == 0 && dp->max_mididev == 0) { - snd_printk(KERN_ERR "no device found\n"); + /* snd_printk(KERN_ERR "no device found\n"); */ rc = -ENODEV; goto _error; } --- linux-2.6.9-rc1/sound/core/seq/oss/seq_oss_ioctl.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/sound/core/seq/oss/seq_oss_ioctl.c 2004-09-02 20:51:53.000000000 -0700 @@ -28,16 +28,54 @@ #include "seq_oss_midi.h" #include "seq_oss_event.h" +static int snd_seq_oss_synth_info_user(seq_oss_devinfo_t *dp, void __user *arg) +{ + struct synth_info info; + + if (copy_from_user(&info, arg, sizeof(info))) + return -EFAULT; + if (snd_seq_oss_synth_make_info(dp, info.device, &info) < 0) + return -EINVAL; + if (copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static int snd_seq_oss_midi_info_user(seq_oss_devinfo_t *dp, void __user *arg) +{ + struct midi_info info; + + if (copy_from_user(&info, arg, sizeof(info))) + return -EFAULT; + if (snd_seq_oss_midi_make_info(dp, info.device, &info) < 0) + return -EINVAL; + if (copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +static int snd_seq_oss_oob_user(seq_oss_devinfo_t *dp, void __user *arg) +{ + unsigned char ev[8]; + snd_seq_event_t tmpev; + + if (copy_from_user(ev, arg, 8)) + return -EFAULT; + memset(&tmpev, 0, sizeof(tmpev)); + snd_seq_oss_fill_addr(dp, &tmpev, dp->addr.port, dp->addr.client); + tmpev.time.tick = 0; + if (! snd_seq_oss_process_event(dp, (evrec_t*)ev, &tmpev)) { + snd_seq_oss_dispatch(dp, &tmpev, 0, 0); + } + return 0; +} + int snd_seq_oss_ioctl(seq_oss_devinfo_t *dp, unsigned int cmd, unsigned long carg) { int dev, val; - struct synth_info inf; - struct midi_info minf; - unsigned char ev[8]; void __user *arg = (void __user *)carg; int __user *p = arg; - snd_seq_event_t tmpev; switch (cmd) { case SNDCTL_TMR_TIMEBASE: @@ -124,35 +162,15 @@ snd_seq_oss_ioctl(seq_oss_devinfo_t *dp, case SNDCTL_SYNTH_INFO: case SNDCTL_SYNTH_ID: debug_printk(("synth info\n")); - if (copy_from_user(&inf, arg, sizeof(inf))) - return -EFAULT; - if (snd_seq_oss_synth_make_info(dp, inf.device, &inf) < 0) - return -EINVAL; - if (copy_to_user(arg, &inf, sizeof(inf))) - return -EFAULT; - return 0; + return snd_seq_oss_synth_info_user(dp, arg); case SNDCTL_SEQ_OUTOFBAND: - debug_printk(("out of bound\n")); - if (copy_from_user(ev, arg, 8)) - return -EFAULT; - memset(&tmpev, 0, sizeof(tmpev)); - snd_seq_oss_fill_addr(dp, &tmpev, dp->addr.port, dp->addr.client); - tmpev.time.tick = 0; - if (! snd_seq_oss_process_event(dp, (evrec_t*)ev, &tmpev)) { - snd_seq_oss_dispatch(dp, &tmpev, 0, 0); - } - return 0; + debug_printk(("out of band\n")); + return snd_seq_oss_oob_user(dp, arg); case SNDCTL_MIDI_INFO: debug_printk(("midi info\n")); - if (copy_from_user(&minf, arg, sizeof(minf))) - return -EFAULT; - if (snd_seq_oss_midi_make_info(dp, minf.device, &minf) < 0) - return -EINVAL; - if (copy_to_user(arg, &minf, sizeof(minf))) - return -EFAULT; - return 0; + return snd_seq_oss_midi_info_user(dp, arg); case SNDCTL_SEQ_THRESHOLD: debug_printk(("threshold\n")); --- linux-2.6.9-rc1/sound/core/seq/oss/seq_oss_midi.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/sound/core/seq/oss/seq_oss_midi.c 2004-09-02 20:51:53.000000000 -0700 @@ -171,7 +171,7 @@ snd_seq_oss_midi_check_new_port(snd_seq_ /* * allocate midi info record */ - if ((mdev = snd_kcalloc(sizeof(*mdev), GFP_KERNEL)) == NULL) { + if ((mdev = kcalloc(1, sizeof(*mdev), GFP_KERNEL)) == NULL) { snd_printk(KERN_ERR "can't malloc midi info\n"); return -ENOMEM; } --- linux-2.6.9-rc1/sound/core/seq/oss/seq_oss_readq.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/sound/core/seq/oss/seq_oss_readq.c 2004-09-02 20:51:53.000000000 -0700 @@ -45,12 +45,12 @@ snd_seq_oss_readq_new(seq_oss_devinfo_t { seq_oss_readq_t *q; - if ((q = snd_kcalloc(sizeof(*q), GFP_KERNEL)) == NULL) { + if ((q = kcalloc(1, sizeof(*q), GFP_KERNEL)) == NULL) { snd_printk(KERN_ERR "can't malloc read queue\n"); return NULL; } - if ((q->q = snd_kcalloc(sizeof(evrec_t) * maxlen, GFP_KERNEL)) == NULL) { + if ((q->q = kcalloc(maxlen, sizeof(evrec_t), GFP_KERNEL)) == NULL) { snd_printk(KERN_ERR "can't malloc read queue buffer\n"); kfree(q); return NULL; @@ -74,7 +74,6 @@ void snd_seq_oss_readq_delete(seq_oss_readq_t *q) { if (q) { - snd_seq_oss_readq_clear(q); /* to be sure */ if (q->q) kfree(q->q); kfree(q); @@ -106,9 +105,9 @@ snd_seq_oss_readq_puts(seq_oss_readq_t * evrec_t rec; int result; + memset(&rec, 0, sizeof(rec)); rec.c[0] = SEQ_MIDIPUTC; rec.c[2] = dev; - rec.c[3] = 0; while (len-- > 0) { rec.c[1] = *data++; @@ -134,7 +133,7 @@ snd_seq_oss_readq_put_event(seq_oss_read return -ENOMEM; } - memcpy(&q->q[q->tail], ev, ev_length(ev)); + memcpy(&q->q[q->tail], ev, sizeof(*ev)); q->tail = (q->tail + 1) % q->maxlen; q->qlen++; @@ -150,50 +149,37 @@ snd_seq_oss_readq_put_event(seq_oss_read /* * pop queue + * caller must hold lock */ -evrec_t * -snd_seq_oss_readq_pick(seq_oss_readq_t *q, int blocking, unsigned long *rflags) +int +snd_seq_oss_readq_pick(seq_oss_readq_t *q, evrec_t *rec) { - evrec_t *p; - - spin_lock_irqsave(&q->lock, *rflags); - if (q->qlen == 0) { - if (blocking) { - spin_unlock(&q->lock); - interruptible_sleep_on_timeout(&q->midi_sleep, - q->pre_event_timeout); - spin_lock(&q->lock); - } - if (q->qlen == 0) { - spin_unlock_irqrestore(&q->lock, *rflags); - return NULL; - } - } - p = q->q + q->head; - - return p; + if (q->qlen == 0) + return -EAGAIN; + memcpy(rec, &q->q[q->head], sizeof(*rec)); + return 0; } /* - * unlock queue + * sleep until ready */ void -snd_seq_oss_readq_unlock(seq_oss_readq_t *q, unsigned long flags) +snd_seq_oss_readq_wait(seq_oss_readq_t *q) { - spin_unlock_irqrestore(&q->lock, flags); + interruptible_sleep_on_timeout(&q->midi_sleep, q->pre_event_timeout); } /* - * drain one record and unlock queue + * drain one record + * caller must hold lock */ void -snd_seq_oss_readq_free(seq_oss_readq_t *q, unsigned long flags) +snd_seq_oss_readq_free(seq_oss_readq_t *q) { if (q->qlen > 0) { q->head = (q->head + 1) % q->maxlen; q->qlen--; } - spin_unlock_irqrestore(&q->lock, flags); } /* @@ -215,6 +201,7 @@ snd_seq_oss_readq_put_timestamp(seq_oss_ { if (curt != q->input_time) { evrec_t rec; + memset(&rec, 0, sizeof(rec)); switch (seq_mode) { case SNDRV_SEQ_OSS_MODE_SYNTH: rec.echo = (curt << 8) | SEQ_WAIT; --- linux-2.6.9-rc1/sound/core/seq/oss/seq_oss_readq.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/sound/core/seq/oss/seq_oss_readq.h 2004-09-02 20:51:53.000000000 -0700 @@ -46,8 +46,11 @@ unsigned int snd_seq_oss_readq_poll(seq_ int snd_seq_oss_readq_puts(seq_oss_readq_t *readq, int dev, unsigned char *data, int len); int snd_seq_oss_readq_put_event(seq_oss_readq_t *readq, evrec_t *ev); int snd_seq_oss_readq_put_timestamp(seq_oss_readq_t *readq, unsigned long curt, int seq_mode); -evrec_t *snd_seq_oss_readq_pick(seq_oss_readq_t *q, int blocking, unsigned long *rflags); -void snd_seq_oss_readq_unlock(seq_oss_readq_t *q, unsigned long flags); -void snd_seq_oss_readq_free(seq_oss_readq_t *q, unsigned long flags); +int snd_seq_oss_readq_pick(seq_oss_readq_t *q, evrec_t *rec); +void snd_seq_oss_readq_wait(seq_oss_readq_t *q); +void snd_seq_oss_readq_free(seq_oss_readq_t *q); + +#define snd_seq_oss_readq_lock(q, flags) spin_lock_irqsave(&(q)->lock, flags) +#define snd_seq_oss_readq_unlock(q, flags) spin_unlock_irqrestore(&(q)->lock, flags) #endif --- linux-2.6.9-rc1/sound/core/seq/oss/seq_oss_rw.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/sound/core/seq/oss/seq_oss_rw.c 2004-09-02 20:51:53.000000000 -0700 @@ -44,35 +44,47 @@ int snd_seq_oss_read(seq_oss_devinfo_t *dp, char __user *buf, int count) { seq_oss_readq_t *readq = dp->readq; - int cnt, pos; - evrec_t *q; + int result = 0, err = 0; + int ev_len; + evrec_t rec; unsigned long flags; if (readq == NULL || ! is_read_mode(dp->file_mode)) - return -EIO; + return -ENXIO; - /* copy queued events to read buffer */ - cnt = count; - pos = 0; - q = snd_seq_oss_readq_pick(readq, !is_nonblock_mode(dp->file_mode), &flags); - if (q == NULL) - return 0; - do { - int ev_len; - /* tansfer the data */ - ev_len = ev_length(q); - if (copy_to_user(buf + pos, q, ev_len)) { + while (count >= SHORT_EVENT_SIZE) { + snd_seq_oss_readq_lock(readq, flags); + err = snd_seq_oss_readq_pick(readq, &rec); + if (err == -EAGAIN && + !is_nonblock_mode(dp->file_mode) && result == 0) { + snd_seq_oss_readq_unlock(readq, flags); + snd_seq_oss_readq_wait(readq); + snd_seq_oss_readq_lock(readq, flags); + if (signal_pending(current)) + err = -ERESTARTSYS; + else + err = snd_seq_oss_readq_pick(readq, &rec); + } + if (err < 0) { snd_seq_oss_readq_unlock(readq, flags); break; } - snd_seq_oss_readq_free(readq, flags); - pos += ev_len; - cnt -= ev_len; - if (cnt < ev_len) + ev_len = ev_length(&rec); + if (ev_len < count) { + snd_seq_oss_readq_unlock(readq, flags); break; - } while ((q = snd_seq_oss_readq_pick(readq, 0, &flags)) != NULL); - - return count - cnt; + } + snd_seq_oss_readq_free(readq); + snd_seq_oss_readq_unlock(readq, flags); + if (copy_to_user(buf, &rec, ev_len)) { + err = -EFAULT; + break; + } + result += ev_len; + buf += ev_len; + count -= ev_len; + } + return result > 0 ? result : err; } @@ -83,56 +95,64 @@ snd_seq_oss_read(seq_oss_devinfo_t *dp, int snd_seq_oss_write(seq_oss_devinfo_t *dp, const char __user *buf, int count, struct file *opt) { - int rc, c, p, ev_size; + int result = 0, err = 0; + int ev_size, fmt; evrec_t rec; if (! is_write_mode(dp->file_mode) || dp->writeq == NULL) - return -EIO; + return -ENXIO; - c = count; - p = 0; - while (c >= SHORT_EVENT_SIZE) { - if (copy_from_user(rec.c, buf + p, SHORT_EVENT_SIZE)) + while (count >= SHORT_EVENT_SIZE) { + if (copy_from_user(&rec, buf, SHORT_EVENT_SIZE)) { + err = -EFAULT; break; - p += SHORT_EVENT_SIZE; - + } if (rec.s.code == SEQ_FULLSIZE) { /* load patch */ - int fmt = (*(unsigned short *)rec.c) & 0xffff; - return snd_seq_oss_synth_load_patch(dp, rec.s.dev, fmt, buf, p, c); - + if (result > 0) { + err = -EINVAL; + break; + } + fmt = (*(unsigned short *)rec.c) & 0xffff; + /* FIXME the return value isn't correct */ + return snd_seq_oss_synth_load_patch(dp, rec.s.dev, + fmt, buf, 0, count); } if (ev_is_long(&rec)) { /* extended code */ if (rec.s.code == SEQ_EXTENDED && - dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) - return -EINVAL; + dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) { + err = -EINVAL; + break; + } ev_size = LONG_EVENT_SIZE; - if (c < ev_size) + if (count < ev_size) break; /* copy the reset 4 bytes */ - if (copy_from_user(rec.c + SHORT_EVENT_SIZE, buf + p, - LONG_EVENT_SIZE - SHORT_EVENT_SIZE)) + if (copy_from_user(rec.c + SHORT_EVENT_SIZE, + buf + SHORT_EVENT_SIZE, + LONG_EVENT_SIZE - SHORT_EVENT_SIZE)) { + err = -EFAULT; break; - p += LONG_EVENT_SIZE - SHORT_EVENT_SIZE; - + } } else { /* old-type code */ - if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) - return -EINVAL; + if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) { + err = -EINVAL; + break; + } ev_size = SHORT_EVENT_SIZE; } /* insert queue */ - if ((rc = insert_queue(dp, &rec, opt)) < 0) + if ((err = insert_queue(dp, &rec, opt)) < 0) break; - c -= ev_size; + result += ev_size; + buf += ev_size; + count -= ev_size; } - - if (count == c && is_nonblock_mode(dp->file_mode)) - return -EAGAIN; - return count - c; + return result > 0 ? result : err; } --- linux-2.6.9-rc1/sound/core/seq/oss/seq_oss_synth.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/sound/core/seq/oss/seq_oss_synth.c 2004-09-02 20:51:53.000000000 -0700 @@ -103,7 +103,7 @@ snd_seq_oss_synth_register(snd_seq_devic snd_seq_oss_reg_t *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev); unsigned long flags; - if ((rec = snd_kcalloc(sizeof(*rec), GFP_KERNEL)) == NULL) { + if ((rec = kcalloc(1, sizeof(*rec), GFP_KERNEL)) == NULL) { snd_printk(KERN_ERR "can't malloc synth info\n"); return -ENOMEM; } @@ -244,7 +244,9 @@ snd_seq_oss_synth_setup(seq_oss_devinfo_ } info->nr_voices = rec->nr_voices; if (info->nr_voices > 0) { - info->ch = snd_kcalloc(sizeof(seq_oss_chinfo_t) * info->nr_voices, GFP_KERNEL); + info->ch = kcalloc(info->nr_voices, sizeof(seq_oss_chinfo_t), GFP_KERNEL); + if (!info->ch) + BUG(); reset_channels(info); } debug_printk(("synth %d assigned\n", i)); @@ -505,7 +507,7 @@ snd_seq_oss_synth_sysex(seq_oss_devinfo_ sysex = dp->synths[dev].sysex; if (sysex == NULL) { - sysex = snd_kcalloc(sizeof(*sysex), GFP_KERNEL); + sysex = kcalloc(1, sizeof(*sysex), GFP_KERNEL); if (sysex == NULL) return -ENOMEM; dp->synths[dev].sysex = sysex; --- linux-2.6.9-rc1/sound/core/seq/oss/seq_oss_timer.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/sound/core/seq/oss/seq_oss_timer.c 2004-09-02 20:51:53.000000000 -0700 @@ -46,7 +46,7 @@ snd_seq_oss_timer_new(seq_oss_devinfo_t { seq_oss_timer_t *rec; - rec = snd_kcalloc(sizeof(*rec), GFP_KERNEL); + rec = kcalloc(1, sizeof(*rec), GFP_KERNEL); if (rec == NULL) return NULL; @@ -168,7 +168,7 @@ snd_seq_oss_timer_start(seq_oss_timer_t tmprec.queue = dp->queue; tmprec.ppq = timer->ppq; tmprec.tempo = timer->tempo; - snd_seq_kernel_client_ctl(dp->cseq, SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO, &tmprec); + snd_seq_set_queue_tempo(dp->cseq, &tmprec); send_timer_event(dp, SNDRV_SEQ_EVENT_START, 0); timer->running = 1; --- linux-2.6.9-rc1/sound/core/seq/oss/seq_oss_writeq.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/sound/core/seq/oss/seq_oss_writeq.c 2004-09-02 20:51:53.000000000 -0700 @@ -37,7 +37,7 @@ snd_seq_oss_writeq_new(seq_oss_devinfo_t seq_oss_writeq_t *q; snd_seq_client_pool_t pool; - if ((q = snd_kcalloc(sizeof(*q), GFP_KERNEL)) == NULL) + if ((q = kcalloc(1, sizeof(*q), GFP_KERNEL)) == NULL) return NULL; q->dp = dp; q->maxlen = maxlen; --- linux-2.6.9-rc1/sound/core/seq/seq.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/sound/core/seq/seq.c 2004-09-02 20:51:53.000000000 -0700 @@ -50,8 +50,6 @@ int seq_default_timer_resolution = 0; /* MODULE_AUTHOR("Frank van de Pol , Jaroslav Kysela "); MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer."); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_SUPPORTED_DEVICE("sound"); static int boot_devs; module_param_array(seq_client_load, int, boot_devs, 0444); @@ -133,6 +131,7 @@ EXPORT_SYMBOL(snd_seq_kernel_client_enqu EXPORT_SYMBOL(snd_seq_kernel_client_dispatch); EXPORT_SYMBOL(snd_seq_kernel_client_ctl); EXPORT_SYMBOL(snd_seq_kernel_client_write_poll); +EXPORT_SYMBOL(snd_seq_set_queue_tempo); /* seq_memory.c */ EXPORT_SYMBOL(snd_seq_expand_var_event); EXPORT_SYMBOL(snd_seq_dump_var_event); --- linux-2.6.9-rc1/sound/core/seq/seq_clientmgr.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/sound/core/seq/seq_clientmgr.c 2004-09-02 20:51:53.000000000 -0700 @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -202,7 +203,7 @@ static client_t *seq_create_client1(int client_t *client; /* init client data */ - client = snd_kcalloc(sizeof(client_t), GFP_KERNEL); + client = kcalloc(1, sizeof(*client), GFP_KERNEL); if (client == NULL) return NULL; client->pool = snd_seq_pool_new(poolsize); @@ -547,36 +548,6 @@ static int update_timestamp_of_queue(snd /* - * expand a quoted event. - */ -static int expand_quoted_event(snd_seq_event_t *event) -{ - snd_seq_event_t *quoted; - - quoted = event->data.quote.event; - if (quoted == NULL) { - snd_printd("seq: quoted event is NULL\n"); - return -EINVAL; - } - - event->type = quoted->type; - event->tag = quoted->tag; - event->source = quoted->source; - /* don't use quoted destination */ - event->data = quoted->data; - /* use quoted timestamp only if subscription/port didn't update it */ - if (event->queue == SNDRV_SEQ_QUEUE_DIRECT) { - event->flags = quoted->flags; - event->queue = quoted->queue; - event->time = quoted->time; - } else { - event->flags = (event->flags & SNDRV_SEQ_TIME_STAMP_MASK) - | (quoted->flags & ~SNDRV_SEQ_TIME_STAMP_MASK); - } - return 0; -} - -/* * deliver an event to the specified destination. * if filter is non-zero, client filter bitmap is tested. * @@ -590,7 +561,7 @@ static int snd_seq_deliver_single_event( client_t *dest = NULL; client_port_t *dest_port = NULL; int result = -ENOENT; - int direct, quoted = 0; + int direct; direct = snd_seq_ev_is_direct(event); @@ -611,14 +582,6 @@ static int snd_seq_deliver_single_event( update_timestamp_of_queue(event, dest_port->time_queue, dest_port->time_real); - if (event->type == SNDRV_SEQ_EVENT_KERNEL_QUOTE) { - quoted = 1; - if (expand_quoted_event(event) < 0) { - result = 0; /* do not send bounce error */ - goto __skip; - } - } - switch (dest->type) { case USER_CLIENT: if (dest->data.user.fifo) @@ -641,14 +604,7 @@ static int snd_seq_deliver_single_event( snd_seq_client_unlock(dest); if (result < 0 && !direct) { - if (quoted) { - /* return directly to the original source */ - dest = snd_seq_client_use_ptr(event->source.client); - result = bounce_error_event(dest, event, result, atomic, hop); - snd_seq_client_unlock(dest); - } else { - result = bounce_error_event(client, event, result, atomic, hop); - } + result = bounce_error_event(client, event, result, atomic, hop); } return result; } @@ -1694,6 +1650,13 @@ static int snd_seq_ioctl_get_queue_tempo /* SET_QUEUE_TEMPO ioctl() */ +int snd_seq_set_queue_tempo(int client, snd_seq_queue_tempo_t *tempo) +{ + if (!snd_seq_queue_check_access(tempo->queue, client)) + return -EPERM; + return snd_seq_queue_timer_set_tempo(tempo->queue, client, tempo); +} + static int snd_seq_ioctl_set_queue_tempo(client_t * client, void __user *arg) { int result; @@ -1702,15 +1665,8 @@ static int snd_seq_ioctl_set_queue_tempo if (copy_from_user(&tempo, arg, sizeof(tempo))) return -EFAULT; - if (snd_seq_queue_check_access(tempo.queue, client->number)) { - result = snd_seq_queue_timer_set_tempo(tempo.queue, client->number, &tempo); - if (result < 0) - return result; - } else { - return -EPERM; - } - - return 0; + result = snd_seq_set_queue_tempo(client->number, &tempo); + return result < 0 ? result : 0; } @@ -2176,10 +2132,15 @@ static int snd_seq_ioctl(struct inode *i unsigned int cmd, unsigned long arg) { client_t *client = (client_t *) file->private_data; + int err; snd_assert(client != NULL, return -ENXIO); - return snd_seq_do_ioctl(client, cmd, (void __user *) arg); + /* FIXME: need to unlock BKL to allow preemption */ + unlock_kernel(); + err = snd_seq_do_ioctl(client, cmd, (void __user *) arg); + lock_kernel(); + return err; } @@ -2261,8 +2222,7 @@ static int kernel_client_enqueue(int cli if (ev->type == SNDRV_SEQ_EVENT_NONE) return 0; /* ignore this */ - if (ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR || - ev->type == SNDRV_SEQ_EVENT_KERNEL_QUOTE) + if (ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR) return -EINVAL; /* quoted events can't be enqueued */ /* fill in client number */ --- linux-2.6.9-rc1/sound/core/seq/seq_device.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/sound/core/seq/seq_device.c 2004-09-02 20:51:53.000000000 -0700 @@ -48,8 +48,6 @@ MODULE_AUTHOR("Takashi Iwai "); MODULE_DESCRIPTION("ALSA sequencer device management"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_SUPPORTED_DEVICE("sound"); /* * driver list @@ -181,7 +179,7 @@ int snd_seq_device_new(snd_card_t *card, if (ops == NULL) return -ENOMEM; - dev = snd_magic_kcalloc(snd_seq_device_t, sizeof(*dev) + argsize, GFP_KERNEL); + dev = kcalloc(1, sizeof(*dev)*2 + argsize, GFP_KERNEL); if (dev == NULL) { unlock_driver(ops); return -ENOMEM; @@ -235,7 +233,7 @@ static int snd_seq_device_free(snd_seq_d free_device(dev, ops); if (dev->private_free) dev->private_free(dev); - snd_magic_kfree(dev); + kfree(dev); unlock_driver(ops); @@ -244,7 +242,7 @@ static int snd_seq_device_free(snd_seq_d static int snd_seq_device_dev_free(snd_device_t *device) { - snd_seq_device_t *dev = snd_magic_cast(snd_seq_device_t, device->device_data, return -ENXIO); + snd_seq_device_t *dev = device->device_data; return snd_seq_device_free(dev); } @@ -253,7 +251,7 @@ static int snd_seq_device_dev_free(snd_d */ static int snd_seq_device_dev_register(snd_device_t *device) { - snd_seq_device_t *dev = snd_magic_cast(snd_seq_device_t, device->device_data, return -ENXIO); + snd_seq_device_t *dev = device->device_data; ops_list_t *ops; ops = find_driver(dev->id, 0); @@ -275,7 +273,7 @@ static int snd_seq_device_dev_register(s */ static int snd_seq_device_dev_disconnect(snd_device_t *device) { - snd_seq_device_t *dev = snd_magic_cast(snd_seq_device_t, device->device_data, return -ENXIO); + snd_seq_device_t *dev = device->device_data; ops_list_t *ops; ops = find_driver(dev->id, 0); @@ -293,7 +291,7 @@ static int snd_seq_device_dev_disconnect */ static int snd_seq_device_dev_unregister(snd_device_t *device) { - snd_seq_device_t *dev = snd_magic_cast(snd_seq_device_t, device->device_data, return -ENXIO); + snd_seq_device_t *dev = device->device_data; return snd_seq_device_free(dev); } --- linux-2.6.9-rc1/sound/core/seq/seq_dummy.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/sound/core/seq/seq_dummy.c 2004-09-02 20:51:53.000000000 -0700 @@ -63,8 +63,6 @@ MODULE_AUTHOR("Takashi Iwai "); MODULE_DESCRIPTION("ALSA sequencer MIDI-through client"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_SUPPORTED_DEVICE("sound"); static int ports = 1; static int duplex = 0; @@ -95,7 +93,7 @@ dummy_unuse(void *private_data, snd_seq_ int i; snd_seq_event_t ev; - p = snd_magic_cast(snd_seq_dummy_port_t, private_data, return -EINVAL); + p = private_data; memset(&ev, 0, sizeof(ev)); if (p->duplex) ev.source.port = p->connect; @@ -122,18 +120,11 @@ dummy_input(snd_seq_event_t *ev, int dir snd_seq_dummy_port_t *p; snd_seq_event_t tmpev; - p = snd_magic_cast(snd_seq_dummy_port_t, private_data, return -EINVAL); + p = private_data; if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM || ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR) return 0; /* ignore system messages */ - /* save the original sender */ - tmpev.type = SNDRV_SEQ_EVENT_KERNEL_QUOTE; - tmpev.flags = (ev->flags & ~SNDRV_SEQ_EVENT_LENGTH_MASK) - | SNDRV_SEQ_EVENT_LENGTH_FIXED; - tmpev.tag = ev->tag; - tmpev.time = ev->time; - tmpev.data.quote.origin = ev->source; - tmpev.data.quote.event = ev; + tmpev = *ev; if (p->duplex) tmpev.source.port = p->connect; else @@ -150,8 +141,8 @@ dummy_free(void *private_data) { snd_seq_dummy_port_t *p; - p = snd_magic_cast(snd_seq_dummy_port_t, private_data, return); - snd_magic_kfree(p); + p = private_data; + kfree(p); } /* @@ -164,7 +155,7 @@ create_port(int idx, int type) snd_seq_port_callback_t pcb; snd_seq_dummy_port_t *rec; - if ((rec = snd_magic_kcalloc(snd_seq_dummy_port_t, 0, GFP_KERNEL)) == NULL) + if ((rec = kcalloc(1, sizeof(*rec), GFP_KERNEL)) == NULL) return NULL; rec->client = my_client; @@ -190,7 +181,7 @@ create_port(int idx, int type) pcb.private_data = rec; pinfo.kernel = &pcb; if (snd_seq_kernel_client_ctl(my_client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo) < 0) { - snd_magic_kfree(rec); + kfree(rec); return NULL; } rec->port = pinfo.addr.port; --- linux-2.6.9-rc1/sound/core/seq/seq_fifo.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/sound/core/seq/seq_fifo.c 2004-09-02 20:51:53.000000000 -0700 @@ -33,7 +33,7 @@ fifo_t *snd_seq_fifo_new(int poolsize) { fifo_t *f; - f = snd_kcalloc(sizeof(fifo_t), GFP_KERNEL); + f = kcalloc(1, sizeof(*f), GFP_KERNEL); if (f == NULL) { snd_printd("malloc failed for snd_seq_fifo_new() \n"); return NULL; --- linux-2.6.9-rc1/sound/core/seq/seq_instr.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/sound/core/seq/seq_instr.c 2004-09-02 20:51:53.000000000 -0700 @@ -29,8 +29,6 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer instrument library."); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_SUPPORTED_DEVICE("sound"); static void snd_instr_lock_ops(snd_seq_kinstr_list_t *list) @@ -53,10 +51,7 @@ static void snd_instr_unlock_ops(snd_seq snd_seq_kcluster_t *snd_seq_cluster_new(int atomic) { - snd_seq_kcluster_t *cluster; - - cluster = (snd_seq_kcluster_t *) snd_kcalloc(sizeof(snd_seq_kcluster_t), atomic ? GFP_ATOMIC : GFP_KERNEL); - return cluster; + return kcalloc(1, sizeof(snd_seq_kcluster_t), atomic ? GFP_ATOMIC : GFP_KERNEL); } void snd_seq_cluster_free(snd_seq_kcluster_t *cluster, int atomic) @@ -70,7 +65,7 @@ snd_seq_kinstr_t *snd_seq_instr_new(int { snd_seq_kinstr_t *instr; - instr = (snd_seq_kinstr_t *) snd_kcalloc(sizeof(snd_seq_kinstr_t) + add_len, atomic ? GFP_ATOMIC : GFP_KERNEL); + instr = kcalloc(1, sizeof(snd_seq_kinstr_t) + add_len, atomic ? GFP_ATOMIC : GFP_KERNEL); if (instr == NULL) return NULL; instr->add_len = add_len; @@ -94,7 +89,7 @@ snd_seq_kinstr_list_t *snd_seq_instr_lis { snd_seq_kinstr_list_t *list; - list = (snd_seq_kinstr_list_t *) snd_kcalloc(sizeof(snd_seq_kinstr_list_t), GFP_KERNEL); + list = kcalloc(1, sizeof(snd_seq_kinstr_list_t), GFP_KERNEL); if (list == NULL) return NULL; spin_lock_init(&list->lock); --- linux-2.6.9-rc1/sound/core/seq/seq_memory.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/sound/core/seq/seq_memory.c 2004-09-02 20:51:53.000000000 -0700 @@ -95,7 +95,7 @@ int snd_seq_dump_var_event(const snd_seq int size = sizeof(buf); if (len < size) size = len; - if (copy_from_user(buf, curptr, size) < 0) + if (copy_from_user(buf, curptr, size)) return -EFAULT; err = func(private_data, buf, size); if (err < 0) @@ -158,7 +158,7 @@ int snd_seq_expand_var_event(const snd_s if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) { if (! in_kernel) return -EINVAL; - if (copy_from_user(buf, event->data.ext.ptr, len) < 0) + if (copy_from_user(buf, event->data.ext.ptr, len)) return -EFAULT; return newlen; } @@ -453,7 +453,7 @@ pool_t *snd_seq_pool_new(int poolsize) pool_t *pool; /* create pool block */ - pool = snd_kcalloc(sizeof(pool_t), GFP_KERNEL); + pool = kcalloc(1, sizeof(*pool), GFP_KERNEL); if (pool == NULL) { snd_printd("seq: malloc failed for pool\n"); return NULL; --- linux-2.6.9-rc1/sound/core/seq/seq_midi.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/sound/core/seq/seq_midi.c 2004-09-02 20:51:53.000000000 -0700 @@ -43,8 +43,6 @@ Possible options for midisynth module: MODULE_AUTHOR("Frank van de Pol , Jaroslav Kysela "); MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI synth."); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_SUPPORTED_DEVICE("sound"); int output_buffer_size = PAGE_SIZE; module_param(output_buffer_size, int, 0644); MODULE_PARM_DESC(output_buffer_size, "Output buffer size in bytes."); @@ -323,7 +321,7 @@ snd_seq_midisynth_register_port(snd_seq_ client = synths[card->number]; if (client == NULL) { newclient = 1; - client = snd_kcalloc(sizeof(seq_midisynth_client_t), GFP_KERNEL); + client = kcalloc(1, sizeof(*client), GFP_KERNEL); if (client == NULL) { up(®ister_mutex); return -ENOMEM; @@ -341,7 +339,7 @@ snd_seq_midisynth_register_port(snd_seq_ } else if (device == 0) set_client_name(client, card, &info); /* use the first device's name */ - msynth = snd_kcalloc(sizeof(seq_midisynth_t) * ports, GFP_KERNEL); + msynth = kcalloc(ports, sizeof(seq_midisynth_t), GFP_KERNEL); if (msynth == NULL) goto __nomem; --- linux-2.6.9-rc1/sound/core/seq/seq_midi_emul.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/sound/core/seq/seq_midi_emul.c 2004-09-02 20:51:53.000000000 -0700 @@ -42,8 +42,6 @@ MODULE_AUTHOR("Takashi Iwai / Steve Ratcliffe"); MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI emulation."); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_SUPPORTED_DEVICE("sound"); /* Prototypes for static functions */ static void note_off(snd_midi_op_t *ops, void *drv, snd_midi_channel_t *chan, int note, int vel); --- linux-2.6.9-rc1/sound/core/seq/seq_midi_event.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/sound/core/seq/seq_midi_event.c 2004-09-02 20:51:53.000000000 -0700 @@ -118,7 +118,7 @@ int snd_midi_event_new(int bufsize, snd_ snd_midi_event_t *dev; *rdev = NULL; - dev = (snd_midi_event_t *)snd_kcalloc(sizeof(snd_midi_event_t), GFP_KERNEL); + dev = kcalloc(1, sizeof(*dev), GFP_KERNEL); if (dev == NULL) return -ENOMEM; if (bufsize > 0) { --- linux-2.6.9-rc1/sound/core/seq/seq_ports.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/sound/core/seq/seq_ports.c 2004-09-02 20:51:53.000000000 -0700 @@ -141,7 +141,7 @@ client_port_t *snd_seq_create_port(clien } /* create a new port */ - new_port = snd_kcalloc(sizeof(client_port_t), GFP_KERNEL); + new_port = kcalloc(1, sizeof(*new_port), GFP_KERNEL); if (! new_port) { snd_printd("malloc failed for registering client port\n"); return NULL; /* failure, out of memory */ @@ -488,7 +488,7 @@ int snd_seq_port_connect(client_t *conne unsigned long flags; int exclusive; - subs = snd_kcalloc(sizeof(*subs), GFP_KERNEL); + subs = kcalloc(1, sizeof(*subs), GFP_KERNEL); if (! subs) return -ENOMEM; --- linux-2.6.9-rc1/sound/core/seq/seq_prioq.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/sound/core/seq/seq_prioq.c 2004-09-02 20:51:53.000000000 -0700 @@ -59,7 +59,7 @@ prioq_t *snd_seq_prioq_new(void) { prioq_t *f; - f = snd_kcalloc(sizeof(prioq_t), GFP_KERNEL); + f = kcalloc(1, sizeof(*f), GFP_KERNEL); if (f == NULL) { snd_printd("oops: malloc failed for snd_seq_prioq_new()\n"); return NULL; --- linux-2.6.9-rc1/sound/core/seq/seq_queue.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/sound/core/seq/seq_queue.c 2004-09-02 20:51:53.000000000 -0700 @@ -111,7 +111,7 @@ static queue_t *queue_new(int owner, int { queue_t *q; - q = snd_kcalloc(sizeof(queue_t), GFP_KERNEL); + q = kcalloc(1, sizeof(*q), GFP_KERNEL); if (q == NULL) { snd_printd("malloc failed for snd_seq_queue_new()\n"); return NULL; --- linux-2.6.9-rc1/sound/core/seq/seq_timer.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/sound/core/seq/seq_timer.c 2004-09-02 20:51:53.000000000 -0700 @@ -59,7 +59,7 @@ seq_timer_t *snd_seq_timer_new(void) { seq_timer_t *tmr; - tmr = snd_kcalloc(sizeof(seq_timer_t), GFP_KERNEL); + tmr = kcalloc(1, sizeof(*tmr), GFP_KERNEL); if (tmr == NULL) { snd_printd("malloc failed for snd_seq_timer_new() \n"); return NULL; --- linux-2.6.9-rc1/sound/core/seq/seq_virmidi.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/sound/core/seq/seq_virmidi.c 2004-09-02 20:51:53.000000000 -0700 @@ -115,7 +115,7 @@ int snd_virmidi_receive(snd_rawmidi_t *r { snd_virmidi_dev_t *rdev; - rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, return -EINVAL); + rdev = rmidi->private_data; return snd_virmidi_dev_receive_event(rdev, ev); } @@ -127,7 +127,7 @@ static int snd_virmidi_event_input(snd_s { snd_virmidi_dev_t *rdev; - rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL); + rdev = private_data; if (!(rdev->flags & SNDRV_VIRMIDI_USE)) return 0; /* ignored */ return snd_virmidi_dev_receive_event(rdev, ev); @@ -138,7 +138,7 @@ static int snd_virmidi_event_input(snd_s */ static void snd_virmidi_input_trigger(snd_rawmidi_substream_t * substream, int up) { - snd_virmidi_t *vmidi = snd_magic_cast(snd_virmidi_t, substream->runtime->private_data, return); + snd_virmidi_t *vmidi = substream->runtime->private_data; if (up) { vmidi->trigger = 1; @@ -152,7 +152,7 @@ static void snd_virmidi_input_trigger(sn */ static void snd_virmidi_output_trigger(snd_rawmidi_substream_t * substream, int up) { - snd_virmidi_t *vmidi = snd_magic_cast(snd_virmidi_t, substream->runtime->private_data, return); + snd_virmidi_t *vmidi = substream->runtime->private_data; int count, res; unsigned char buf[32], *pbuf; @@ -199,17 +199,17 @@ static void snd_virmidi_output_trigger(s */ static int snd_virmidi_input_open(snd_rawmidi_substream_t * substream) { - snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, substream->rmidi->private_data, return -EINVAL); + snd_virmidi_dev_t *rdev = substream->rmidi->private_data; snd_rawmidi_runtime_t *runtime = substream->runtime; snd_virmidi_t *vmidi; unsigned long flags; - vmidi = snd_magic_kcalloc(snd_virmidi_t, 0, GFP_KERNEL); + vmidi = kcalloc(1, sizeof(*vmidi), GFP_KERNEL); if (vmidi == NULL) return -ENOMEM; vmidi->substream = substream; if (snd_midi_event_new(0, &vmidi->parser) < 0) { - snd_magic_kfree(vmidi); + kfree(vmidi); return -ENOMEM; } vmidi->seq_mode = rdev->seq_mode; @@ -228,16 +228,16 @@ static int snd_virmidi_input_open(snd_ra */ static int snd_virmidi_output_open(snd_rawmidi_substream_t * substream) { - snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, substream->rmidi->private_data, return -EINVAL); + snd_virmidi_dev_t *rdev = substream->rmidi->private_data; snd_rawmidi_runtime_t *runtime = substream->runtime; snd_virmidi_t *vmidi; - vmidi = snd_magic_kcalloc(snd_virmidi_t, 0, GFP_KERNEL); + vmidi = kcalloc(1, sizeof(*vmidi), GFP_KERNEL); if (vmidi == NULL) return -ENOMEM; vmidi->substream = substream; if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &vmidi->parser) < 0) { - snd_magic_kfree(vmidi); + kfree(vmidi); return -ENOMEM; } vmidi->seq_mode = rdev->seq_mode; @@ -254,11 +254,11 @@ static int snd_virmidi_output_open(snd_r */ static int snd_virmidi_input_close(snd_rawmidi_substream_t * substream) { - snd_virmidi_t *vmidi = snd_magic_cast(snd_virmidi_t, substream->runtime->private_data, return -EINVAL); + snd_virmidi_t *vmidi = substream->runtime->private_data; snd_midi_event_free(vmidi->parser); list_del(&vmidi->list); substream->runtime->private_data = NULL; - snd_magic_kfree(vmidi); + kfree(vmidi); return 0; } @@ -267,10 +267,10 @@ static int snd_virmidi_input_close(snd_r */ static int snd_virmidi_output_close(snd_rawmidi_substream_t * substream) { - snd_virmidi_t *vmidi = snd_magic_cast(snd_virmidi_t, substream->runtime->private_data, return -EINVAL); + snd_virmidi_t *vmidi = substream->runtime->private_data; snd_midi_event_free(vmidi->parser); substream->runtime->private_data = NULL; - snd_magic_kfree(vmidi); + kfree(vmidi); return 0; } @@ -281,7 +281,7 @@ static int snd_virmidi_subscribe(void *p { snd_virmidi_dev_t *rdev; - rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL); + rdev = private_data; if (!try_module_get(rdev->card->module)) return -EFAULT; rdev->flags |= SNDRV_VIRMIDI_SUBSCRIBE; @@ -295,7 +295,7 @@ static int snd_virmidi_unsubscribe(void { snd_virmidi_dev_t *rdev; - rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL); + rdev = private_data; rdev->flags &= ~SNDRV_VIRMIDI_SUBSCRIBE; module_put(rdev->card->module); return 0; @@ -309,7 +309,7 @@ static int snd_virmidi_use(void *private { snd_virmidi_dev_t *rdev; - rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL); + rdev = private_data; if (!try_module_get(rdev->card->module)) return -EFAULT; rdev->flags |= SNDRV_VIRMIDI_USE; @@ -323,7 +323,7 @@ static int snd_virmidi_unuse(void *priva { snd_virmidi_dev_t *rdev; - rdev = snd_magic_cast(snd_virmidi_dev_t, private_data, return -EINVAL); + rdev = private_data; rdev->flags &= ~SNDRV_VIRMIDI_USE; module_put(rdev->card->module); return 0; @@ -424,7 +424,7 @@ static void snd_virmidi_dev_detach_seq(s */ static int snd_virmidi_dev_register(snd_rawmidi_t *rmidi) { - snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, return -ENXIO); + snd_virmidi_dev_t *rdev = rmidi->private_data; int err; switch (rdev->seq_mode) { @@ -451,7 +451,7 @@ static int snd_virmidi_dev_register(snd_ */ static int snd_virmidi_dev_unregister(snd_rawmidi_t *rmidi) { - snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, return -ENXIO); + snd_virmidi_dev_t *rdev = rmidi->private_data; if (rdev->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH) snd_virmidi_dev_detach_seq(rdev); @@ -471,8 +471,8 @@ static snd_rawmidi_global_ops_t snd_virm */ static void snd_virmidi_free(snd_rawmidi_t *rmidi) { - snd_virmidi_dev_t *rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, return); - snd_magic_kfree(rdev); + snd_virmidi_dev_t *rdev = rmidi->private_data; + kfree(rdev); } /* @@ -493,7 +493,7 @@ int snd_virmidi_new(snd_card_t *card, in &rmidi)) < 0) return err; strcpy(rmidi->name, rmidi->id); - rdev = snd_magic_kcalloc(snd_virmidi_dev_t, 0, GFP_KERNEL); + rdev = kcalloc(1, sizeof(*rdev), GFP_KERNEL); if (rdev == NULL) { snd_device_free(card, rmidi); return -ENOMEM; --- linux-2.6.9-rc1/sound/core/sgbuf.c 2004-08-24 00:03:17.000000000 -0700 +++ 25/sound/core/sgbuf.c 2004-09-02 20:51:53.000000000 -0700 @@ -39,11 +39,13 @@ int snd_free_sgbuf_pages(struct snd_dma_ if (! sgbuf) return -EINVAL; + tmpb.dev.type = SNDRV_DMA_TYPE_DEV; + tmpb.dev.dev = sgbuf->dev; for (i = 0; i < sgbuf->pages; i++) { tmpb.area = sgbuf->table[i].buf; tmpb.addr = sgbuf->table[i].addr; tmpb.bytes = PAGE_SIZE; - snd_dma_free_pages(&sgbuf->dev, &tmpb); + snd_dma_free_pages(&tmpb); } if (dmab->area) vunmap(dmab->area); @@ -59,7 +61,7 @@ int snd_free_sgbuf_pages(struct snd_dma_ return 0; } -void *snd_malloc_sgbuf_pages(const struct snd_dma_device *dev, +void *snd_malloc_sgbuf_pages(struct device *device, size_t size, struct snd_dma_buffer *dmab, size_t *res_size) { @@ -73,8 +75,7 @@ void *snd_malloc_sgbuf_pages(const struc if (! sgbuf) return NULL; memset(sgbuf, 0, sizeof(*sgbuf)); - sgbuf->dev = *dev; - sgbuf->dev.type = SNDRV_DMA_TYPE_DEV; + sgbuf->dev = device; pages = snd_sgbuf_aligned_pages(size); sgbuf->tblsize = sgbuf_align_table(pages); sgbuf->table = kmalloc(sizeof(*sgbuf->table) * sgbuf->tblsize, GFP_KERNEL); @@ -88,7 +89,7 @@ void *snd_malloc_sgbuf_pages(const struc /* allocate each page */ for (i = 0; i < pages; i++) { - if (snd_dma_alloc_pages(&sgbuf->dev, PAGE_SIZE, &tmpb) < 0) { + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, device, PAGE_SIZE, &tmpb) < 0) { if (res_size == NULL) goto _failed; *res_size = size = sgbuf->pages * PAGE_SIZE; --- linux-2.6.9-rc1/sound/core/sound.c 2004-08-24 00:03:17.000000000 -0700 +++ 25/sound/core/sound.c 2004-09-02 20:51:53.000000000 -0700 @@ -44,19 +44,14 @@ static int device_mode = S_IFCHR | S_IRU MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Advanced Linux Sound Architecture driver for soundcards."); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_SUPPORTED_DEVICE("sound"); module_param(major, int, 0444); MODULE_PARM_DESC(major, "Major # for sound driver."); -MODULE_PARM_SYNTAX(major, "default:116,skill:devel"); module_param(cards_limit, int, 0444); MODULE_PARM_DESC(cards_limit, "Count of auto-loadable soundcards."); -MODULE_PARM_SYNTAX(cards_limit, "default:8,skill:advanced"); MODULE_ALIAS_CHARDEV_MAJOR(CONFIG_SND_MAJOR); #ifdef CONFIG_DEVFS_FS module_param(device_mode, int, 0444); MODULE_PARM_DESC(device_mode, "Device file permission mask for devfs."); -MODULE_PARM_SYNTAX(device_mode, "default:0666,base:8"); #endif MODULE_ALIAS_CHARDEV_MAJOR(CONFIG_SND_MAJOR); @@ -339,39 +334,29 @@ int __exit snd_minor_info_done(void) static int __init alsa_sound_init(void) { short controlnum; -#ifdef CONFIG_SND_OSSEMUL int err; -#endif int card; snd_major = major; snd_ecards_limit = cards_limit; for (card = 0; card < SNDRV_CARDS; card++) INIT_LIST_HEAD(&snd_minors_hash[card]); -#ifdef CONFIG_SND_OSSEMUL if ((err = snd_oss_init_module()) < 0) return err; -#endif devfs_mk_dir("snd"); if (register_chrdev(major, "alsa", &snd_fops)) { snd_printk(KERN_ERR "unable to register native major device number %d\n", major); devfs_remove("snd"); return -EIO; } -#ifdef CONFIG_SND_DEBUG_MEMORY snd_memory_init(); -#endif if (snd_info_init() < 0) { -#ifdef CONFIG_SND_DEBUG_MEMORY snd_memory_done(); -#endif unregister_chrdev(major, "alsa"); devfs_remove("snd"); return -ENOMEM; } -#ifdef CONFIG_SND_OSSEMUL snd_info_minor_register(); -#endif for (controlnum = 0; controlnum < cards_limit; controlnum++) { devfs_mk_cdev(MKDEV(major, controlnum<<5), S_IFCHR | device_mode, "snd/controlC%d", controlnum); class_simple_device_add(sound_class, MKDEV(major, controlnum<<5), NULL, "controlC%d", controlnum); @@ -391,13 +376,9 @@ static void __exit alsa_sound_exit(void) class_simple_device_remove(MKDEV(major, controlnum<<5)); } -#ifdef CONFIG_SND_OSSEMUL snd_info_minor_unregister(); -#endif snd_info_done(); -#ifdef CONFIG_SND_DEBUG_MEMORY snd_memory_done(); -#endif if (unregister_chrdev(major, "alsa") != 0) snd_printk(KERN_ERR "unable to unregister major device number %d\n", major); devfs_remove("snd"); @@ -406,24 +387,6 @@ static void __exit alsa_sound_exit(void) module_init(alsa_sound_init) module_exit(alsa_sound_exit) -#ifndef MODULE - -/* format is: snd=major,cards_limit[,device_mode] */ - -static int __init alsa_sound_setup(char *str) -{ - (void)(get_option(&str,&major) == 2 && - get_option(&str,&cards_limit) == 2); -#ifdef CONFIG_DEVFS_FS - (void)(get_option(&str,&device_mode) == 2); -#endif - return 1; -} - -__setup("snd=", alsa_sound_setup); - -#endif /* ifndef MODULE */ - /* sound.c */ EXPORT_SYMBOL(snd_major); EXPORT_SYMBOL(snd_ecards_limit); @@ -439,14 +402,11 @@ EXPORT_SYMBOL(snd_unregister_oss_device) /* memory.c */ #ifdef CONFIG_SND_DEBUG_MEMORY EXPORT_SYMBOL(snd_hidden_kmalloc); +EXPORT_SYMBOL(snd_hidden_kcalloc); EXPORT_SYMBOL(snd_hidden_kfree); EXPORT_SYMBOL(snd_hidden_vmalloc); EXPORT_SYMBOL(snd_hidden_vfree); -EXPORT_SYMBOL(_snd_magic_kmalloc); -EXPORT_SYMBOL(_snd_magic_kcalloc); -EXPORT_SYMBOL(snd_magic_kfree); #endif -EXPORT_SYMBOL(snd_kcalloc); EXPORT_SYMBOL(snd_kmalloc_strdup); EXPORT_SYMBOL(copy_to_user_fromio); EXPORT_SYMBOL(copy_from_user_toio); --- linux-2.6.9-rc1/sound/core/timer.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/sound/core/timer.c 2004-09-02 20:51:53.000000000 -0700 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -46,7 +47,6 @@ static int timer_limit = DEFAULT_TIMER_L MODULE_AUTHOR("Jaroslav Kysela , Takashi Iwai "); MODULE_DESCRIPTION("ALSA timer interface"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); module_param(timer_limit, int, 0444); MODULE_PARM_DESC(timer_limit, "Maximum global timers in system."); @@ -94,7 +94,7 @@ static void snd_timer_reschedule(snd_tim static snd_timer_instance_t *snd_timer_instance_new(char *owner, snd_timer_t *timer) { snd_timer_instance_t *timeri; - timeri = snd_kcalloc(sizeof(snd_timer_instance_t), GFP_KERNEL); + timeri = kcalloc(1, sizeof(*timeri), GFP_KERNEL); if (timeri == NULL) return NULL; timeri->owner = snd_kmalloc_strdup(owner, GFP_KERNEL); @@ -761,7 +761,7 @@ int snd_timer_new(snd_card_t *card, char snd_assert(tid != NULL, return -EINVAL); snd_assert(rtimer != NULL, return -EINVAL); *rtimer = NULL; - timer = snd_magic_kcalloc(snd_timer_t, 0, GFP_KERNEL); + timer = kcalloc(1, sizeof(*timer), GFP_KERNEL); if (timer == NULL) return -ENOMEM; timer->tmr_class = tid->dev_class; @@ -792,19 +792,19 @@ static int snd_timer_free(snd_timer_t *t snd_assert(timer != NULL, return -ENXIO); if (timer->private_free) timer->private_free(timer); - snd_magic_kfree(timer); + kfree(timer); return 0; } int snd_timer_dev_free(snd_device_t *device) { - snd_timer_t *timer = snd_magic_cast(snd_timer_t, device->device_data, return -ENXIO); + snd_timer_t *timer = device->device_data; return snd_timer_free(timer); } int snd_timer_dev_register(snd_device_t *dev) { - snd_timer_t *timer = snd_magic_cast(snd_timer_t, dev->device_data, return -ENXIO); + snd_timer_t *timer = dev->device_data; snd_timer_t *timer1; struct list_head *p; @@ -865,7 +865,7 @@ int snd_timer_unregister(snd_timer_t *ti static int snd_timer_dev_unregister(snd_device_t *device) { - snd_timer_t *timer = snd_magic_cast(snd_timer_t, device->device_data, return -ENXIO); + snd_timer_t *timer = device->device_data; return snd_timer_unregister(timer); } @@ -1018,7 +1018,7 @@ static int snd_timer_register_system(voi return err; strcpy(timer->name, "system timer"); timer->hw = snd_timer_system; - priv = (struct snd_timer_system_private *) snd_kcalloc(sizeof(struct snd_timer_system_private), GFP_KERNEL); + priv = kcalloc(1, sizeof(*priv), GFP_KERNEL); if (priv == NULL) { snd_timer_free(timer); return -ENOMEM; @@ -1086,7 +1086,7 @@ static void snd_timer_user_interrupt(snd unsigned long resolution, unsigned long ticks) { - snd_timer_user_t *tu = snd_magic_cast(snd_timer_user_t, timeri->callback_data, return); + snd_timer_user_t *tu = timeri->callback_data; snd_timer_read_t *r; int prev; @@ -1129,7 +1129,7 @@ static void snd_timer_user_ccallback(snd struct timespec *tstamp, unsigned long resolution) { - snd_timer_user_t *tu = snd_magic_cast(snd_timer_user_t, timeri->callback_data, return); + snd_timer_user_t *tu = timeri->callback_data; snd_timer_tread_t r1; if (event >= SNDRV_TIMER_EVENT_START && event <= SNDRV_TIMER_EVENT_PAUSE) @@ -1148,7 +1148,7 @@ static void snd_timer_user_tinterrupt(sn unsigned long resolution, unsigned long ticks) { - snd_timer_user_t *tu = snd_magic_cast(snd_timer_user_t, timeri->callback_data, return); + snd_timer_user_t *tu = timeri->callback_data; snd_timer_tread_t *r, r1; struct timespec tstamp; int prev, append = 0; @@ -1200,7 +1200,7 @@ static int snd_timer_user_open(struct in { snd_timer_user_t *tu; - tu = snd_magic_kcalloc(snd_timer_user_t, 0, GFP_KERNEL); + tu = kcalloc(1, sizeof(*tu), GFP_KERNEL); if (tu == NULL) return -ENOMEM; spin_lock_init(&tu->qlock); @@ -1209,7 +1209,7 @@ static int snd_timer_user_open(struct in tu->queue_size = 128; tu->queue = (snd_timer_read_t *)kmalloc(tu->queue_size * sizeof(snd_timer_read_t), GFP_KERNEL); if (tu->queue == NULL) { - snd_magic_kfree(tu); + kfree(tu); return -ENOMEM; } file->private_data = tu; @@ -1221,7 +1221,7 @@ static int snd_timer_user_release(struct snd_timer_user_t *tu; if (file->private_data) { - tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + tu = file->private_data; file->private_data = NULL; fasync_helper(-1, file, 0, &tu->fasync); if (tu->timeri) @@ -1230,7 +1230,7 @@ static int snd_timer_user_release(struct kfree(tu->queue); if (tu->tqueue) kfree(tu->tqueue); - snd_magic_kfree(tu); + kfree(tu); } return 0; } @@ -1449,7 +1449,7 @@ static int snd_timer_user_tselect(struct char str[32]; int err; - tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + tu = file->private_data; if (tu->timeri) snd_timer_close(tu->timeri); if (copy_from_user(&tselect, _tselect, sizeof(tselect))) @@ -1495,7 +1495,7 @@ static int snd_timer_user_info(struct fi snd_timer_info_t info; snd_timer_t *t; - tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + tu = file->private_data; snd_assert(tu->timeri != NULL, return -ENXIO); t = tu->timeri->timer; snd_assert(t != NULL, return -ENXIO); @@ -1520,7 +1520,7 @@ static int snd_timer_user_params(struct snd_timer_tread_t *ttr; int err; - tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + tu = file->private_data; snd_assert(tu->timeri != NULL, return -ENXIO); t = tu->timeri->timer; snd_assert(t != NULL, return -ENXIO); @@ -1608,7 +1608,7 @@ static int snd_timer_user_status(struct snd_timer_user_t *tu; snd_timer_status_t status; - tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + tu = file->private_data; snd_assert(tu->timeri != NULL, return -ENXIO); memset(&status, 0, sizeof(status)); status.tstamp = tu->tstamp; @@ -1628,7 +1628,7 @@ static int snd_timer_user_start(struct f int err; snd_timer_user_t *tu; - tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + tu = file->private_data; snd_assert(tu->timeri != NULL, return -ENXIO); snd_timer_stop(tu->timeri); tu->timeri->lost = 0; @@ -1641,7 +1641,7 @@ static int snd_timer_user_stop(struct fi int err; snd_timer_user_t *tu; - tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + tu = file->private_data; snd_assert(tu->timeri != NULL, return -ENXIO); return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0; } @@ -1651,20 +1651,20 @@ static int snd_timer_user_continue(struc int err; snd_timer_user_t *tu; - tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + tu = file->private_data; snd_assert(tu->timeri != NULL, return -ENXIO); tu->timeri->lost = 0; return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0; } -static int snd_timer_user_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static inline int _snd_timer_user_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) { snd_timer_user_t *tu; void __user *argp = (void __user *)arg; int __user *p = argp; - tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + tu = file->private_data; switch (cmd) { case SNDRV_TIMER_IOCTL_PVERSION: return put_user(SNDRV_TIMER_VERSION, p) ? -EFAULT : 0; @@ -1705,12 +1705,23 @@ static int snd_timer_user_ioctl(struct i return -ENOTTY; } +/* FIXME: need to unlock BKL to allow preemption */ +static int snd_timer_user_ioctl(struct inode *inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int err; + unlock_kernel(); + err = _snd_timer_user_ioctl(inode, file, cmd, arg); + lock_kernel(); + return err; +} + static int snd_timer_user_fasync(int fd, struct file * file, int on) { snd_timer_user_t *tu; int err; - tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + tu = file->private_data; err = fasync_helper(fd, file, on, &tu->fasync); if (err < 0) return err; @@ -1723,7 +1734,7 @@ static ssize_t snd_timer_user_read(struc long result = 0, unit; int err = 0; - tu = snd_magic_cast(snd_timer_user_t, file->private_data, return -ENXIO); + tu = file->private_data; unit = tu->tread ? sizeof(snd_timer_tread_t) : sizeof(snd_timer_read_t); spin_lock_irq(&tu->qlock); while ((long)count - result >= unit) { @@ -1785,7 +1796,7 @@ static unsigned int snd_timer_user_poll( unsigned int mask; snd_timer_user_t *tu; - tu = snd_magic_cast(snd_timer_user_t, file->private_data, return 0); + tu = file->private_data; poll_wait(file, &tu->qchange_sleep, wait); --- linux-2.6.9-rc1/sound/drivers/dummy.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/sound/drivers/dummy.c 2004-09-02 20:51:53.000000000 -0700 @@ -34,8 +34,7 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Dummy soundcard (/dev/null)"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{ALSA,Dummy soundcard}}"); +MODULE_SUPPORTED_DEVICE("{{ALSA,Dummy soundcard}}"); #define MAX_PCM_DEVICES 4 #define MAX_PCM_SUBSTREAMS 16 @@ -133,22 +132,16 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for dummy soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for dummy soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable this dummy soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(pcm_devs, int, boot_devs, 0444); MODULE_PARM_DESC(pcm_devs, "PCM devices # (0-4) for dummy driver."); -MODULE_PARM_SYNTAX(pcm_devs, SNDRV_ENABLED ",allows:{{0,4}},default:1,dialog:list"); module_param_array(pcm_substreams, int, boot_devs, 0444); MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-16) for dummy driver."); -MODULE_PARM_SYNTAX(pcm_substreams, SNDRV_ENABLED ",allows:{{1,16}},default:8,dialog:list"); //module_param_array(midi_devs, int, boot_devs, 0444); //MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver."); -//MODULE_PARM_SYNTAX(midi_devs, SNDRV_ENABLED ",allows:{{0,2}},default:8,dialog:list"); #define MIXER_ADDR_MASTER 0 #define MIXER_ADDR_LINE 1 @@ -180,24 +173,10 @@ typedef struct snd_card_dummy_pcm { static snd_card_t *snd_dummy_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; -static int snd_card_dummy_playback_ioctl(snd_pcm_substream_t * substream, - unsigned int cmd, - void *arg) -{ - return snd_pcm_lib_ioctl(substream, cmd, arg); -} - -static int snd_card_dummy_capture_ioctl(snd_pcm_substream_t * substream, - unsigned int cmd, - void *arg) -{ - return snd_pcm_lib_ioctl(substream, cmd, arg); -} - static void snd_card_dummy_pcm_timer_start(snd_pcm_substream_t * substream) { snd_pcm_runtime_t *runtime = substream->runtime; - snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return); + snd_card_dummy_pcm_t *dpcm = runtime->private_data; dpcm->timer.expires = 1 + jiffies; add_timer(&dpcm->timer); @@ -206,7 +185,7 @@ static void snd_card_dummy_pcm_timer_sta static void snd_card_dummy_pcm_timer_stop(snd_pcm_substream_t * substream) { snd_pcm_runtime_t *runtime = substream->runtime; - snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return); + snd_card_dummy_pcm_t *dpcm = runtime->private_data; del_timer(&dpcm->timer); } @@ -240,7 +219,7 @@ static int snd_card_dummy_capture_trigge static int snd_card_dummy_pcm_prepare(snd_pcm_substream_t * substream) { snd_pcm_runtime_t *runtime = substream->runtime; - snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return -ENXIO); + snd_card_dummy_pcm_t *dpcm = runtime->private_data; unsigned int bps; bps = runtime->rate * runtime->channels; @@ -269,7 +248,7 @@ static int snd_card_dummy_capture_prepar static void snd_card_dummy_pcm_timer_function(unsigned long data) { - snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, (void *)data, return); + snd_card_dummy_pcm_t *dpcm = (snd_card_dummy_pcm_t *)data; dpcm->timer.expires = 1 + jiffies; add_timer(&dpcm->timer); @@ -287,7 +266,7 @@ static void snd_card_dummy_pcm_timer_fun static snd_pcm_uframes_t snd_card_dummy_playback_pointer(snd_pcm_substream_t * substream) { snd_pcm_runtime_t *runtime = substream->runtime; - snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return -ENXIO); + snd_card_dummy_pcm_t *dpcm = runtime->private_data; return bytes_to_frames(runtime, dpcm->pcm_buf_pos); } @@ -295,7 +274,7 @@ static snd_pcm_uframes_t snd_card_dummy_ static snd_pcm_uframes_t snd_card_dummy_capture_pointer(snd_pcm_substream_t * substream) { snd_pcm_runtime_t *runtime = substream->runtime; - snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return -ENXIO); + snd_card_dummy_pcm_t *dpcm = runtime->private_data; return bytes_to_frames(runtime, dpcm->pcm_buf_pos); } @@ -338,8 +317,19 @@ static snd_pcm_hardware_t snd_card_dummy static void snd_card_dummy_runtime_free(snd_pcm_runtime_t *runtime) { - snd_card_dummy_pcm_t *dpcm = snd_magic_cast(snd_card_dummy_pcm_t, runtime->private_data, return); - snd_magic_kfree(dpcm); + snd_card_dummy_pcm_t *dpcm = runtime->private_data; + kfree(dpcm); +} + +static int snd_card_dummy_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_card_dummy_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); } static int snd_card_dummy_playback_open(snd_pcm_substream_t * substream) @@ -348,13 +338,9 @@ static int snd_card_dummy_playback_open( snd_card_dummy_pcm_t *dpcm; int err; - dpcm = snd_magic_kcalloc(snd_card_dummy_pcm_t, 0, GFP_KERNEL); + dpcm = kcalloc(1, sizeof(*dpcm), GFP_KERNEL); if (dpcm == NULL) return -ENOMEM; - if ((runtime->dma_area = snd_malloc_pages_fallback(MAX_BUFFER_SIZE, GFP_KERNEL, &runtime->dma_bytes)) == NULL) { - snd_magic_kfree(dpcm); - return -ENOMEM; - } init_timer(&dpcm->timer); dpcm->timer.data = (unsigned long) dpcm; dpcm->timer.function = snd_card_dummy_pcm_timer_function; @@ -370,7 +356,7 @@ static int snd_card_dummy_playback_open( if (substream->pcm->device & 2) runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID); if ((err = add_playback_constraints(runtime)) < 0) { - snd_magic_kfree(dpcm); + kfree(dpcm); return err; } @@ -383,14 +369,9 @@ static int snd_card_dummy_capture_open(s snd_card_dummy_pcm_t *dpcm; int err; - dpcm = snd_magic_kcalloc(snd_card_dummy_pcm_t, 0, GFP_KERNEL); + dpcm = kcalloc(1, sizeof(*dpcm), GFP_KERNEL); if (dpcm == NULL) return -ENOMEM; - if ((runtime->dma_area = snd_malloc_pages_fallback(MAX_BUFFER_SIZE, GFP_KERNEL, &runtime->dma_bytes)) == NULL) { - snd_magic_kfree(dpcm); - return -ENOMEM; - } - memset(runtime->dma_area, 0, runtime->dma_bytes); init_timer(&dpcm->timer); dpcm->timer.data = (unsigned long) dpcm; dpcm->timer.function = snd_card_dummy_pcm_timer_function; @@ -406,7 +387,7 @@ static int snd_card_dummy_capture_open(s if (substream->pcm->device & 2) runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID); if ((err = add_capture_constraints(runtime)) < 0) { - snd_magic_kfree(dpcm); + kfree(dpcm); return err; } @@ -415,24 +396,20 @@ static int snd_card_dummy_capture_open(s static int snd_card_dummy_playback_close(snd_pcm_substream_t * substream) { - snd_pcm_runtime_t *runtime = substream->runtime; - - snd_free_pages(runtime->dma_area, runtime->dma_bytes); return 0; } static int snd_card_dummy_capture_close(snd_pcm_substream_t * substream) { - snd_pcm_runtime_t *runtime = substream->runtime; - - snd_free_pages(runtime->dma_area, runtime->dma_bytes); return 0; } static snd_pcm_ops_t snd_card_dummy_playback_ops = { .open = snd_card_dummy_playback_open, .close = snd_card_dummy_playback_close, - .ioctl = snd_card_dummy_playback_ioctl, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_card_dummy_hw_params, + .hw_free = snd_card_dummy_hw_free, .prepare = snd_card_dummy_playback_prepare, .trigger = snd_card_dummy_playback_trigger, .pointer = snd_card_dummy_playback_pointer, @@ -441,7 +418,9 @@ static snd_pcm_ops_t snd_card_dummy_play static snd_pcm_ops_t snd_card_dummy_capture_ops = { .open = snd_card_dummy_capture_open, .close = snd_card_dummy_capture_close, - .ioctl = snd_card_dummy_capture_ioctl, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_card_dummy_hw_params, + .hw_free = snd_card_dummy_hw_free, .prepare = snd_card_dummy_capture_prepare, .trigger = snd_card_dummy_capture_trigger, .pointer = snd_card_dummy_capture_pointer, @@ -459,6 +438,9 @@ static int __init snd_card_dummy_pcm(snd pcm->private_data = dummy; pcm->info_flags = 0; strcpy(pcm->name, "Dummy PCM"); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + 0, 64*1024); return 0; } @@ -479,7 +461,7 @@ static int snd_dummy_volume_info(snd_kco static int snd_dummy_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - snd_card_dummy_t *dummy = _snd_kcontrol_chip(kcontrol); + snd_card_dummy_t *dummy = snd_kcontrol_chip(kcontrol); unsigned long flags; int addr = kcontrol->private_value; @@ -488,11 +470,11 @@ static int snd_dummy_volume_get(snd_kcon ucontrol->value.integer.value[1] = dummy->mixer_volume[addr][1]; spin_unlock_irqrestore(&dummy->mixer_lock, flags); return 0; -} +} static int snd_dummy_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - snd_card_dummy_t *dummy = _snd_kcontrol_chip(kcontrol); + snd_card_dummy_t *dummy = snd_kcontrol_chip(kcontrol); unsigned long flags; int change, addr = kcontrol->private_value; int left, right; @@ -514,7 +496,7 @@ static int snd_dummy_volume_put(snd_kcon dummy->mixer_volume[addr][1] = right; spin_unlock_irqrestore(&dummy->mixer_lock, flags); return change; -} +} #define DUMMY_CAPSRC(xname, xindex, addr) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ @@ -533,7 +515,7 @@ static int snd_dummy_capsrc_info(snd_kco static int snd_dummy_capsrc_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - snd_card_dummy_t *dummy = _snd_kcontrol_chip(kcontrol); + snd_card_dummy_t *dummy = snd_kcontrol_chip(kcontrol); unsigned long flags; int addr = kcontrol->private_value; @@ -546,7 +528,7 @@ static int snd_dummy_capsrc_get(snd_kcon static int snd_dummy_capsrc_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - snd_card_dummy_t *dummy = _snd_kcontrol_chip(kcontrol); + snd_card_dummy_t *dummy = snd_kcontrol_chip(kcontrol); unsigned long flags; int change, addr = kcontrol->private_value; int left, right; @@ -560,9 +542,7 @@ static int snd_dummy_capsrc_put(snd_kcon dummy->capture_source[addr][1] = right; spin_unlock_irqrestore(&dummy->mixer_lock, flags); return change; -} - -#define DUMMY_CONTROLS (sizeof(snd_dummy_controls)/sizeof(snd_kcontrol_new_t)) +} static snd_kcontrol_new_t snd_dummy_controls[] = { DUMMY_VOLUME("Master Volume", 0, MIXER_ADDR_MASTER), @@ -587,7 +567,7 @@ int __init snd_card_dummy_new_mixer(snd_ spin_lock_init(&dummy->mixer_lock); strcpy(card->mixername, "Dummy Mixer"); - for (idx = 0; idx < DUMMY_CONTROLS; idx++) { + for (idx = 0; idx < ARRAY_SIZE(snd_dummy_controls); idx++) { if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_dummy_controls[idx], dummy))) < 0) return err; } --- linux-2.6.9-rc1/sound/drivers/mpu401/mpu401.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/sound/drivers/mpu401/mpu401.c 2004-09-02 20:51:53.000000000 -0700 @@ -28,7 +28,7 @@ #include #include #ifdef CONFIG_ACPI_BUS -#include +#include #endif #include #include @@ -42,7 +42,6 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("MPU-401 UART"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ @@ -56,24 +55,18 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for MPU-401 device."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for MPU-401 device."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable MPU-401 device."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); #ifdef USE_ACPI_PNP module_param_array(acpipnp, bool, boot_devs, 0444); MODULE_PARM_DESC(acpipnp, "ACPI PnP detection for MPU-401 device."); -MODULE_PARM_SYNTAX(acpipnp, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC); #endif module_param_array(port, long, boot_devs, 0444); MODULE_PARM_DESC(port, "Port # for MPU-401 device."); -MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); module_param_array(irq, int, boot_devs, 0444); MODULE_PARM_DESC(irq, "IRQ # for MPU-401 device."); -MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); #ifndef CONFIG_ACPI_BUS struct acpi_device; @@ -98,13 +91,9 @@ static acpi_status __devinit snd_mpu401_ if (res->id == ACPI_RSTYPE_IRQ) { if (res->data.irq.number_of_interrupts > 0) { -#ifdef CONFIG_IA64 - resources->irq = acpi_register_irq(res->data.irq.interrupts[0], - res->data.irq.active_high_low, - res->data.irq.edge_level); -#else - resources->irq = res->data.irq.interrupts[0]; -#endif + resources->irq = acpi_register_gsi(res->data.irq.interrupts[0], + res->data.irq.edge_level, + res->data.irq.active_high_low); } } else if (res->id == ACPI_RSTYPE_IO) { if (res->data.io.range_length >= 2) { @@ -175,7 +164,7 @@ static int __devinit snd_card_mpu401_pro } #ifdef USE_ACPI_PNP if (device) { - strcat(card->longname, ", bus id "); + strcat(card->longname, ", ACPI id "); strlcat(card->longname, acpi_device_bid(device), sizeof(card->longname)); } #endif --- linux-2.6.9-rc1/sound/drivers/mpu401/mpu401_uart.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/sound/drivers/mpu401/mpu401_uart.c 2004-09-02 20:51:53.000000000 -0700 @@ -123,7 +123,7 @@ static void _snd_mpu401_uart_interrupt(m */ irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - mpu401_t *mpu = snd_magic_cast(mpu401_t, dev_id, return IRQ_NONE); + mpu401_t *mpu = dev_id; if (mpu == NULL) return IRQ_NONE; @@ -137,7 +137,7 @@ irqreturn_t snd_mpu401_uart_interrupt(in */ static void snd_mpu401_uart_timer(unsigned long data) { - mpu401_t *mpu = snd_magic_cast(mpu401_t, (void *)data, return); + mpu401_t *mpu = (mpu401_t *)data; spin_lock(&mpu->timer_lock); /*mpu->mode |= MPU401_MODE_TIMER;*/ @@ -235,7 +235,7 @@ static int snd_mpu401_uart_input_open(sn mpu401_t *mpu; int err; - mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return -ENXIO); + mpu = substream->rmidi->private_data; if (mpu->open_input && (err = mpu->open_input(mpu)) < 0) return err; if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) { @@ -253,7 +253,7 @@ static int snd_mpu401_uart_output_open(s mpu401_t *mpu; int err; - mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return -ENXIO); + mpu = substream->rmidi->private_data; if (mpu->open_output && (err = mpu->open_output(mpu)) < 0) return err; if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) { @@ -270,7 +270,7 @@ static int snd_mpu401_uart_input_close(s { mpu401_t *mpu; - mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return -ENXIO); + mpu = substream->rmidi->private_data; clear_bit(MPU401_MODE_BIT_INPUT, &mpu->mode); mpu->substream_input = NULL; if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) @@ -284,7 +284,7 @@ static int snd_mpu401_uart_output_close( { mpu401_t *mpu; - mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return -ENXIO); + mpu = substream->rmidi->private_data; clear_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode); mpu->substream_output = NULL; if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) @@ -303,7 +303,7 @@ static void snd_mpu401_uart_input_trigge mpu401_t *mpu; int max = 64; - mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return); + mpu = substream->rmidi->private_data; if (up) { if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) { /* first time - flush FIFO */ @@ -394,7 +394,7 @@ static void snd_mpu401_uart_output_trigg unsigned long flags; mpu401_t *mpu; - mpu = snd_magic_cast(mpu401_t, substream->rmidi->private_data, return); + mpu = substream->rmidi->private_data; if (up) { set_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); @@ -441,14 +441,14 @@ static snd_rawmidi_ops_t snd_mpu401_uart static void snd_mpu401_uart_free(snd_rawmidi_t *rmidi) { - mpu401_t *mpu = snd_magic_cast(mpu401_t, rmidi->private_data, return); + mpu401_t *mpu = rmidi->private_data; if (mpu->irq_flags && mpu->irq >= 0) free_irq(mpu->irq, (void *) mpu); if (mpu->res) { release_resource(mpu->res); kfree_nocheck(mpu->res); } - snd_magic_kfree(mpu); + kfree(mpu); } /** @@ -484,7 +484,7 @@ int snd_mpu401_uart_new(snd_card_t * car *rrawmidi = NULL; if ((err = snd_rawmidi_new(card, "MPU-401U", device, 1, 1, &rmidi)) < 0) return err; - mpu = snd_magic_kcalloc(mpu401_t, 0, GFP_KERNEL); + mpu = kcalloc(1, sizeof(*mpu), GFP_KERNEL); if (mpu == NULL) { snd_device_free(card, rmidi); return -ENOMEM; --- linux-2.6.9-rc1/sound/drivers/mtpav.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/sound/drivers/mtpav.c 2004-09-02 20:51:53.000000000 -0700 @@ -69,8 +69,7 @@ MODULE_AUTHOR("Michael T. Mayers"); MODULE_DESCRIPTION("MOTU MidiTimePiece AV multiport MIDI"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{MOTU,MidiTimePiece AV multiport MIDI}}"); +MODULE_SUPPORTED_DEVICE("{{MOTU,MidiTimePiece AV multiport MIDI}}"); // io resources #define MTPAV_IOBASE 0x378 @@ -85,19 +84,14 @@ static int hwports = MTPAV_MAX_PORTS; /* module_param(index, int, 0444); MODULE_PARM_DESC(index, "Index value for MotuMTPAV MIDI."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param(id, charp, 0444); MODULE_PARM_DESC(id, "ID string for MotuMTPAV MIDI."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param(port, long, 0444); MODULE_PARM_DESC(port, "Parallel port # for MotuMTPAV MIDI."); -MODULE_PARM_SYNTAX(port, SNDRV_ENABLED ",allows:{{0x378},{0x278}},dialog:list"); module_param(irq, int, 0444); MODULE_PARM_DESC(irq, "Parallel IRQ # for MotuMTPAV MIDI."); -MODULE_PARM_SYNTAX(irq, SNDRV_ENABLED ",allows:{{7},{5}},dialog:list"); module_param(hwports, int, 0444); MODULE_PARM_DESC(hwports, "Hardware ports # for MotuMTPAV MIDI."); -MODULE_PARM_SYNTAX(hwports, SNDRV_ENABLED ",allows:{{1,8}},dialog:list"); /* * defines @@ -419,7 +413,7 @@ static void snd_mtpav_input_trigger(snd_ static void snd_mtpav_output_timer(unsigned long data) { - mtpav_t *chip = snd_magic_cast(mtpav_t, (void *)data, return); + mtpav_t *chip = (mtpav_t *)data; int p; spin_lock(&chip->spinlock); @@ -587,7 +581,7 @@ static void snd_mtpav_read_bytes(mtpav_t static irqreturn_t snd_mtpav_irqh(int irq, void *dev_id, struct pt_regs *regs) { - mtpav_t *mcard = snd_magic_cast(mtpav_t, dev_id, return IRQ_NONE); + mtpav_t *mcard = dev_id; //printk("irqh()\n"); spin_lock(&mcard->spinlock); @@ -695,7 +689,7 @@ static int snd_mtpav_get_RAWMIDI(mtpav_t static mtpav_t *new_mtpav(void) { - mtpav_t *ncrd = (mtpav_t *) snd_magic_kcalloc(mtpav_t, 0, GFP_KERNEL); + mtpav_t *ncrd = kcalloc(1, sizeof(*ncrd), GFP_KERNEL); if (ncrd != NULL) { spin_lock_init(&ncrd->spinlock); @@ -728,7 +722,7 @@ static void free_mtpav(mtpav_t * crd) release_resource(crd->res_port); kfree_nocheck(crd->res_port); } - snd_magic_kfree(crd); + kfree(crd); } /* --- linux-2.6.9-rc1/sound/drivers/opl3/opl3_lib.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/sound/drivers/opl3/opl3_lib.c 2004-09-02 20:51:53.000000000 -0700 @@ -35,8 +35,6 @@ MODULE_AUTHOR("Jaroslav Kysela private_data, return); + opl3 = hw->private_data; status = inb(opl3->l_port); #if 0 snd_printk("AdLib IRQ status = 0x%x\n", status); @@ -354,13 +352,13 @@ static int snd_opl3_free(opl3_t *opl3) release_resource(opl3->res_r_port); kfree_nocheck(opl3->res_r_port); } - snd_magic_kfree(opl3); + kfree(opl3); return 0; } static int snd_opl3_dev_free(snd_device_t *device) { - opl3_t *opl3 = snd_magic_cast(opl3_t, device->device_data, return -ENXIO); + opl3_t *opl3 = device->device_data; return snd_opl3_free(opl3); } @@ -379,7 +377,7 @@ int snd_opl3_create(snd_card_t * card, *ropl3 = NULL; - opl3 = snd_magic_kcalloc(opl3_t, 0, GFP_KERNEL); + opl3 = kcalloc(1, sizeof(*opl3), GFP_KERNEL); if (opl3 == NULL) return -ENOMEM; --- linux-2.6.9-rc1/sound/drivers/opl3/opl3_midi.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/sound/drivers/opl3/opl3_midi.c 2004-09-02 20:51:53.000000000 -0700 @@ -313,7 +313,7 @@ void snd_opl3_note_on(void *p, int note, fm_instrument_t *fm; unsigned long flags; - opl3 = snd_magic_cast(opl3_t, p, return); + opl3 = p; #ifdef DEBUG_MIDI snd_printk("Note on, ch %i, inst %i, note %i, vel %i\n", @@ -672,7 +672,7 @@ void snd_opl3_note_off(void *p, int note unsigned long flags; - opl3 = snd_magic_cast(opl3_t, p, return); + opl3 = p; #ifdef DEBUG_MIDI snd_printk("Note off, ch %i, inst %i, note %i\n", @@ -712,7 +712,7 @@ void snd_opl3_key_press(void *p, int not { opl3_t *opl3; - opl3 = snd_magic_cast(opl3_t, p, return); + opl3 = p; #ifdef DEBUG_MIDI snd_printk("Key pressure, ch#: %i, inst#: %i\n", chan->number, chan->midi_program); @@ -726,7 +726,7 @@ void snd_opl3_terminate_note(void *p, in { opl3_t *opl3; - opl3 = snd_magic_cast(opl3_t, p, return); + opl3 = p; #ifdef DEBUG_MIDI snd_printk("Terminate note, ch#: %i, inst#: %i\n", chan->number, chan->midi_program); @@ -814,7 +814,7 @@ void snd_opl3_control(void *p, int type, { opl3_t *opl3; - opl3 = snd_magic_cast(opl3_t, p, return); + opl3 = p; #ifdef DEBUG_MIDI snd_printk("Controller, TYPE = %i, ch#: %i, inst#: %i\n", type, chan->number, chan->midi_program); @@ -851,7 +851,7 @@ void snd_opl3_nrpn(void *p, snd_midi_cha { opl3_t *opl3; - opl3 = snd_magic_cast(opl3_t, p, return); + opl3 = p; #ifdef DEBUG_MIDI snd_printk("NRPN, ch#: %i, inst#: %i\n", chan->number, chan->midi_program); @@ -866,7 +866,7 @@ void snd_opl3_sysex(void *p, unsigned ch { opl3_t *opl3; - opl3 = snd_magic_cast(opl3_t, p, return); + opl3 = p; #ifdef DEBUG_MIDI snd_printk("SYSEX\n"); #endif --- linux-2.6.9-rc1/sound/drivers/opl3/opl3_oss.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/sound/drivers/opl3/opl3_oss.c 2004-09-02 20:51:53.000000000 -0700 @@ -57,7 +57,7 @@ static snd_seq_oss_callback_t oss_callba static int snd_opl3_oss_event_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop) { - opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return -EINVAL); + opl3_t *opl3 = private_data; if (ev->type != SNDRV_SEQ_EVENT_OSS) snd_midi_process_event(&opl3_ops, ev, opl3->oss_chset); @@ -68,7 +68,7 @@ static int snd_opl3_oss_event_input(snd_ static void snd_opl3_oss_free_port(void *private_data) { - opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return); + opl3_t *opl3 = private_data; snd_midi_channel_free_set(opl3->oss_chset); } @@ -156,7 +156,7 @@ void snd_opl3_free_seq_oss(opl3_t *opl3) /* open OSS sequencer */ static int snd_opl3_open_seq_oss(snd_seq_oss_arg_t *arg, void *closure) { - opl3_t *opl3 = snd_magic_cast(opl3_t, closure, return -EINVAL); + opl3_t *opl3 = closure; int err; snd_assert(arg != NULL, return -ENXIO); @@ -182,7 +182,7 @@ static int snd_opl3_close_seq_oss(snd_se opl3_t *opl3; snd_assert(arg != NULL, return -ENXIO); - opl3 = snd_magic_cast(opl3_t, arg->private_data, return -EINVAL); + opl3 = arg->private_data; snd_opl3_synth_cleanup(opl3); @@ -213,7 +213,7 @@ static int snd_opl3_load_patch_seq_oss(s int err = -EINVAL; snd_assert(arg != NULL, return -ENXIO); - opl3 = snd_magic_cast(opl3_t, arg->private_data, return -EINVAL); + opl3 = arg->private_data; if ((format == FM_PATCH) || (format == OPL3_PATCH)) { struct sbi_instrument sbi; @@ -241,7 +241,7 @@ static int snd_opl3_load_patch_seq_oss(s } size = sizeof(*put) + sizeof(fm_xinstrument_t); - put = (snd_seq_instr_header_t *)snd_kcalloc(size, GFP_KERNEL); + put = kcalloc(1, size, GFP_KERNEL); if (put == NULL) return -ENOMEM; /* build header */ @@ -325,7 +325,7 @@ static int snd_opl3_ioctl_seq_oss(snd_se opl3_t *opl3; snd_assert(arg != NULL, return -ENXIO); - opl3 = snd_magic_cast(opl3_t, arg->private_data, return -EINVAL); + opl3 = arg->private_data; switch (cmd) { case SNDCTL_FM_LOAD_INSTR: snd_printk("OPL3: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n"); @@ -350,7 +350,7 @@ static int snd_opl3_reset_seq_oss(snd_se opl3_t *opl3; snd_assert(arg != NULL, return -ENXIO); - opl3 = snd_magic_cast(opl3_t, arg->private_data, return -EINVAL); + opl3 = arg->private_data; return 0; } --- linux-2.6.9-rc1/sound/drivers/opl3/opl3_seq.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/sound/drivers/opl3/opl3_seq.c 2004-09-02 20:51:53.000000000 -0700 @@ -30,7 +30,6 @@ MODULE_AUTHOR("Uros Bizjak "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("ALSA driver for OPL3 FM synth"); -MODULE_CLASSES("{sound}"); int use_internal_drums = 0; module_param(use_internal_drums, bool, 0444); @@ -99,7 +98,7 @@ void snd_opl3_synth_cleanup(opl3_t * opl int snd_opl3_synth_use(void *private_data, snd_seq_port_subscribe_t * info) { - opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return -ENXIO); + opl3_t *opl3 = private_data; int err; if ((err = snd_opl3_synth_setup(opl3)) < 0) @@ -126,7 +125,7 @@ int snd_opl3_synth_use(void *private_dat int snd_opl3_synth_unuse(void *private_data, snd_seq_port_subscribe_t * info) { - opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return -ENXIO); + opl3_t *opl3 = private_data; snd_opl3_synth_cleanup(opl3); @@ -151,7 +150,7 @@ snd_midi_op_t opl3_ops = { static int snd_opl3_synth_event_input(snd_seq_event_t * ev, int direct, void *private_data, int atomic, int hop) { - opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return -EINVAL); + opl3_t *opl3 = private_data; if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN && ev->type <= SNDRV_SEQ_EVENT_INSTR_CHANGE) { @@ -169,7 +168,7 @@ static int snd_opl3_synth_event_input(sn static void snd_opl3_synth_free_port(void *private_data) { - opl3_t *opl3 = snd_magic_cast(opl3_t, private_data, return); + opl3_t *opl3 = private_data; snd_midi_channel_free_set(opl3->chset); } --- linux-2.6.9-rc1/sound/drivers/opl3/opl3_synth.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/sound/drivers/opl3/opl3_synth.c 2004-09-02 20:51:53.000000000 -0700 @@ -74,7 +74,7 @@ static int snd_opl3_set_connection(opl3_ */ int snd_opl3_open(snd_hwdep_t * hw, struct file *file) { - opl3_t *opl3 = snd_magic_cast(opl3_t, hw->private_data, return -ENXIO); + opl3_t *opl3 = hw->private_data; down(&opl3->access_mutex); if (opl3->used) { @@ -93,7 +93,7 @@ int snd_opl3_open(snd_hwdep_t * hw, stru int snd_opl3_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg) { - opl3_t *opl3 = snd_magic_cast(opl3_t, hw->private_data, return -ENXIO); + opl3_t *opl3 = hw->private_data; void __user *argp = (void __user *)arg; snd_assert(opl3 != NULL, return -EINVAL); @@ -176,7 +176,7 @@ int snd_opl3_ioctl(snd_hwdep_t * hw, str */ int snd_opl3_release(snd_hwdep_t * hw, struct file *file) { - opl3_t *opl3 = snd_magic_cast(opl3_t, hw->private_data, return -ENXIO); + opl3_t *opl3 = hw->private_data; snd_opl3_reset(opl3); down(&opl3->access_mutex); --- linux-2.6.9-rc1/sound/drivers/opl4/Makefile 2004-08-24 00:03:31.000000000 -0700 +++ 25/sound/drivers/opl4/Makefile 2004-09-02 20:51:53.000000000 -0700 @@ -15,4 +15,4 @@ snd-opl4-synth-objs := opl4_seq.o opl4_s sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1))) obj-$(CONFIG_SND_OPL4_LIB) += snd-opl4-lib.o -obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-opl4-synth.o \ No newline at end of file +obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-opl4-synth.o --- linux-2.6.9-rc1/sound/drivers/opl4/opl4_lib.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/sound/drivers/opl4/opl4_lib.c 2004-09-02 20:51:53.000000000 -0700 @@ -26,7 +26,6 @@ MODULE_AUTHOR("Clemens Ladisch "); MODULE_DESCRIPTION("OPL4 driver"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); static void inline snd_opl4_wait(opl4_t *opl4) { @@ -37,62 +36,68 @@ static void inline snd_opl4_wait(opl4_t void snd_opl4_write(opl4_t *opl4, u8 reg, u8 value) { - unsigned long flags; - - spin_lock_irqsave(&opl4->reg_lock, flags); - snd_opl4_wait(opl4); outb(reg, opl4->pcm_port); snd_opl4_wait(opl4); outb(value, opl4->pcm_port + 1); - - spin_unlock_irqrestore(&opl4->reg_lock, flags); } u8 snd_opl4_read(opl4_t *opl4, u8 reg) { - unsigned long flags; - u8 value; - - spin_lock_irqsave(&opl4->reg_lock, flags); - snd_opl4_wait(opl4); outb(reg, opl4->pcm_port); snd_opl4_wait(opl4); - value = inb(opl4->pcm_port + 1); - - spin_unlock_irqrestore(&opl4->reg_lock, flags); - return value; + return inb(opl4->pcm_port + 1); } void snd_opl4_read_memory(opl4_t *opl4, char *buf, int offset, int size) { - u8 memcfg = snd_opl4_read(opl4, OPL4_REG_MEMORY_CONFIGURATION); + unsigned long flags; + u8 memcfg; + + spin_lock_irqsave(&opl4->reg_lock, flags); + + memcfg = snd_opl4_read(opl4, OPL4_REG_MEMORY_CONFIGURATION); snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg | OPL4_MODE_BIT); snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_HIGH, offset >> 16); snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_MID, offset >> 8); snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_LOW, offset); - for (; size > 0; size--) - *buf++ = snd_opl4_read(opl4, OPL4_REG_MEMORY_DATA); + + snd_opl4_wait(opl4); + outb(OPL4_REG_MEMORY_DATA, opl4->pcm_port); + snd_opl4_wait(opl4); + insb(opl4->pcm_port + 1, buf, size); snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg); + + spin_unlock_irqrestore(&opl4->reg_lock, flags); } void snd_opl4_write_memory(opl4_t *opl4, const char *buf, int offset, int size) { - u8 memcfg = snd_opl4_read(opl4, OPL4_REG_MEMORY_CONFIGURATION); + unsigned long flags; + u8 memcfg; + + spin_lock_irqsave(&opl4->reg_lock, flags); + + memcfg = snd_opl4_read(opl4, OPL4_REG_MEMORY_CONFIGURATION); snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg | OPL4_MODE_BIT); snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_HIGH, offset >> 16); snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_MID, offset >> 8); snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_LOW, offset); - for (; size > 0; size--) - snd_opl4_write(opl4, OPL4_REG_MEMORY_DATA, *buf++); + + snd_opl4_wait(opl4); + outb(OPL4_REG_MEMORY_DATA, opl4->pcm_port); + snd_opl4_wait(opl4); + outsb(opl4->pcm_port + 1, buf, size); snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg); + + spin_unlock_irqrestore(&opl4->reg_lock, flags); } static void snd_opl4_enable_opl4(opl4_t *opl4) @@ -141,7 +146,7 @@ static int snd_opl4_detect(opl4_t *opl4) #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) static void snd_opl4_seq_dev_free(snd_seq_device_t *seq_dev) { - opl4_t *opl4 = snd_magic_cast(opl4_t, seq_dev->private_data, return); + opl4_t *opl4 = seq_dev->private_data; opl4->seq_dev = NULL; } @@ -172,12 +177,12 @@ static void snd_opl4_free(opl4_t *opl4) release_resource(opl4->res_pcm_port); kfree_nocheck(opl4->res_pcm_port); } - snd_magic_kfree(opl4); + kfree(opl4); } static int snd_opl4_dev_free(snd_device_t *device) { - opl4_t *opl4 = snd_magic_cast(opl4_t, device->device_data, return -ENXIO); + opl4_t *opl4 = device->device_data; snd_opl4_free(opl4); return 0; } @@ -199,7 +204,7 @@ int snd_opl4_create(snd_card_t *card, if (ropl4) *ropl4 = NULL; - opl4 = snd_magic_kcalloc(opl4_t, 0, GFP_KERNEL); + opl4 = kcalloc(1, sizeof(*opl4), GFP_KERNEL); if (!opl4) return -ENOMEM; @@ -231,7 +236,7 @@ int snd_opl4_create(snd_card_t *card, } /* opl3 initialization disabled opl4, so reenable */ - snd_opl4_enable_opl4(opl4); + snd_opl4_enable_opl4(opl4); err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, opl4, &ops); if (err < 0) { --- linux-2.6.9-rc1/sound/drivers/opl4/opl4_local.h 2004-08-24 00:02:47.000000000 -0700 +++ 25/sound/drivers/opl4/opl4_local.h 2004-09-02 20:51:53.000000000 -0700 @@ -195,7 +195,6 @@ struct opl4 { opl4_voice_t voices[OPL4_MAX_VOICES]; struct list_head off_voices; struct list_head on_voices; - spinlock_t voices_lock; #endif }; --- linux-2.6.9-rc1/sound/drivers/opl4/opl4_mixer.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/sound/drivers/opl4/opl4_mixer.c 2004-09-02 20:51:53.000000000 -0700 @@ -20,8 +20,6 @@ #include "opl4_local.h" #include -#define chip_t opl4_t - static int snd_opl4_ctl_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; @@ -34,10 +32,13 @@ static int snd_opl4_ctl_info(snd_kcontro static int snd_opl4_ctl_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { opl4_t *opl4 = snd_kcontrol_chip(kcontrol); + unsigned long flags; u8 reg = kcontrol->private_value; u8 value; + spin_lock_irqsave(&opl4->reg_lock, flags); value = snd_opl4_read(opl4, reg); + spin_unlock_irqrestore(&opl4->reg_lock, flags); ucontrol->value.integer.value[0] = 7 - (value & 7); ucontrol->value.integer.value[1] = 7 - ((value >> 3) & 7); return 0; @@ -46,13 +47,16 @@ static int snd_opl4_ctl_get(snd_kcontrol static int snd_opl4_ctl_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { opl4_t *opl4 = snd_kcontrol_chip(kcontrol); + unsigned long flags; u8 reg = kcontrol->private_value; u8 value, old_value; value = (7 - (ucontrol->value.integer.value[0] & 7)) | ((7 - (ucontrol->value.integer.value[1] & 7)) << 3); + spin_lock_irqsave(&opl4->reg_lock, flags); old_value = snd_opl4_read(opl4, reg); snd_opl4_write(opl4, reg, value); + spin_unlock_irqrestore(&opl4->reg_lock, flags); return value != old_value; } @@ -80,9 +84,7 @@ int snd_opl4_create_mixer(opl4_t *opl4) snd_card_t *card = opl4->card; int i, err; -#if 0 /* already set by the codec driver */ - strcpy(card->mixername, "OPL4 Mixer"); -#endif + strcat(card->mixername, ",OPL4"); for (i = 0; i < 2; ++i) { err = snd_ctl_add(card, snd_ctl_new1(&snd_opl4_controls[i], opl4)); --- linux-2.6.9-rc1/sound/drivers/opl4/opl4_proc.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/sound/drivers/opl4/opl4_proc.c 2004-09-02 20:51:53.000000000 -0700 @@ -26,7 +26,7 @@ static int snd_opl4_mem_proc_open(snd_info_entry_t *entry, unsigned short mode, void **file_private_data) { - opl4_t *opl4 = snd_magic_cast(opl4_t, entry->private_data, return -ENXIO); + opl4_t *opl4 = entry->private_data; down(&opl4->access_mutex); if (opl4->memory_access) { @@ -41,7 +41,7 @@ static int snd_opl4_mem_proc_open(snd_in static int snd_opl4_mem_proc_release(snd_info_entry_t *entry, unsigned short mode, void *file_private_data) { - opl4_t *opl4 = snd_magic_cast(opl4_t, entry->private_data, return -ENXIO); + opl4_t *opl4 = entry->private_data; down(&opl4->access_mutex); opl4->memory_access--; @@ -53,7 +53,7 @@ static long snd_opl4_mem_proc_read(snd_i struct file *file, char __user *_buf, unsigned long count, unsigned long pos) { - opl4_t *opl4 = snd_magic_cast(opl4_t, entry->private_data, return -ENXIO); + opl4_t *opl4 = entry->private_data; long size; char* buf; @@ -79,7 +79,7 @@ static long snd_opl4_mem_proc_write(snd_ struct file *file, const char __user *_buf, unsigned long count, unsigned long pos) { - opl4_t *opl4 = snd_magic_cast(opl4_t, entry->private_data, return -ENXIO); + opl4_t *opl4 = entry->private_data; long size; char *buf; --- linux-2.6.9-rc1/sound/drivers/opl4/opl4_seq.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/sound/drivers/opl4/opl4_seq.c 2004-09-02 20:51:53.000000000 -0700 @@ -39,13 +39,11 @@ MODULE_AUTHOR("Clemens Ladisch "); MODULE_DESCRIPTION("OPL4 wavetable synth driver"); MODULE_LICENSE("Dual BSD/GPL"); -MODULE_CLASSES("{sound}"); int volume_boost = 8; module_param(volume_boost, int, 0644); MODULE_PARM_DESC(volume_boost, "Additional volume for OPL4 wavetable sounds."); -MODULE_PARM_SYNTAX(volume_boost, "default:8"); static int snd_opl4_seq_use_inc(opl4_t *opl4) { @@ -61,7 +59,7 @@ static void snd_opl4_seq_use_dec(opl4_t static int snd_opl4_seq_use(void *private_data, snd_seq_port_subscribe_t *info) { - opl4_t *opl4 = snd_magic_cast(opl4_t, private_data, return -ENXIO); + opl4_t *opl4 = private_data; int err; down(&opl4->access_mutex); @@ -88,7 +86,7 @@ static int snd_opl4_seq_use(void *privat static int snd_opl4_seq_unuse(void *private_data, snd_seq_port_subscribe_t *info) { - opl4_t *opl4 = snd_magic_cast(opl4_t, private_data, return -ENXIO); + opl4_t *opl4 = private_data; snd_opl4_synth_shutdown(opl4); @@ -112,7 +110,7 @@ static snd_midi_op_t opl4_ops = { static int snd_opl4_seq_event_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop) { - opl4_t *opl4 = snd_magic_cast(opl4_t, private_data, return -ENXIO); + opl4_t *opl4 = private_data; snd_midi_process_event(&opl4_ops, ev, opl4->chset); return 0; @@ -120,7 +118,7 @@ static int snd_opl4_seq_event_input(snd_ static void snd_opl4_seq_free_port(void *private_data) { - opl4_t *opl4 = snd_magic_cast(opl4_t, private_data, return); + opl4_t *opl4 = private_data; snd_midi_channel_free_set(opl4->chset); } --- linux-2.6.9-rc1/sound/drivers/opl4/opl4_synth.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/sound/drivers/opl4/opl4_synth.c 2004-09-02 20:51:53.000000000 -0700 @@ -272,12 +272,14 @@ static unsigned char snd_opl4_volume_tab */ void snd_opl4_synth_reset(opl4_t *opl4) { + unsigned long flags; int i; + spin_lock_irqsave(&opl4->reg_lock, flags); for (i = 0; i < OPL4_MAX_VOICES; i++) snd_opl4_write(opl4, OPL4_REG_MISC + i, OPL4_DAMP_BIT); + spin_unlock_irqrestore(&opl4->reg_lock, flags); - spin_lock_init(&opl4->voices_lock); INIT_LIST_HEAD(&opl4->off_voices); INIT_LIST_HEAD(&opl4->on_voices); memset(opl4->voices, 0, sizeof(opl4->voices)); @@ -294,11 +296,14 @@ void snd_opl4_synth_reset(opl4_t *opl4) */ void snd_opl4_synth_shutdown(opl4_t *opl4) { + unsigned long flags; int i; + spin_lock_irqsave(&opl4->reg_lock, flags); for (i = 0; i < OPL4_MAX_VOICES; i++) snd_opl4_write(opl4, OPL4_REG_MISC + i, opl4->voices[i].reg_misc & ~OPL4_KEY_ON_BIT); + spin_unlock_irqrestore(&opl4->reg_lock, flags); } /* @@ -311,14 +316,14 @@ static void snd_opl4_do_for_note(opl4_t unsigned long flags; opl4_voice_t *voice; - spin_lock_irqsave(&opl4->voices_lock, flags); + spin_lock_irqsave(&opl4->reg_lock, flags); for (i = 0; i < OPL4_MAX_VOICES; i++) { voice = &opl4->voices[i]; if (voice->chan == chan && voice->note == note) { func(opl4, voice); } } - spin_unlock_irqrestore(&opl4->voices_lock, flags); + spin_unlock_irqrestore(&opl4->reg_lock, flags); } /* @@ -331,14 +336,14 @@ static void snd_opl4_do_for_channel(opl4 unsigned long flags; opl4_voice_t *voice; - spin_lock_irqsave(&opl4->voices_lock, flags); + spin_lock_irqsave(&opl4->reg_lock, flags); for (i = 0; i < OPL4_MAX_VOICES; i++) { voice = &opl4->voices[i]; if (voice->chan == chan) { func(opl4, voice); } } - spin_unlock_irqrestore(&opl4->voices_lock, flags); + spin_unlock_irqrestore(&opl4->reg_lock, flags); } /* @@ -351,13 +356,13 @@ static void snd_opl4_do_for_all(opl4_t * unsigned long flags; opl4_voice_t *voice; - spin_lock_irqsave(&opl4->voices_lock, flags); + spin_lock_irqsave(&opl4->reg_lock, flags); for (i = 0; i < OPL4_MAX_VOICES; i++) { voice = &opl4->voices[i]; if (voice->chan) func(opl4, voice); } - spin_unlock_irqrestore(&opl4->voices_lock, flags); + spin_unlock_irqrestore(&opl4->reg_lock, flags); } static void snd_opl4_update_volume(opl4_t *opl4, opl4_voice_t *voice) @@ -472,7 +477,7 @@ static void snd_opl4_wait_for_wave_heade void snd_opl4_note_on(void *private_data, int note, int vel, snd_midi_channel_t *chan) { - opl4_t *opl4 = snd_magic_cast(opl4_t, private_data, return); + opl4_t *opl4 = private_data; const opl4_region_ptr_t *regions; opl4_voice_t *voice[2]; const opl4_sound_t *sound[2]; @@ -492,7 +497,7 @@ void snd_opl4_note_on(void *private_data } /* allocate and initialize the needed voices */ - spin_lock_irqsave(&opl4->voices_lock, flags); + spin_lock_irqsave(&opl4->reg_lock, flags); for (i = 0; i < voices; i++) { voice[i] = snd_opl4_get_voice(opl4); list_del(&voice[i]->list); @@ -502,7 +507,6 @@ void snd_opl4_note_on(void *private_data voice[i]->velocity = vel & 0x7f; voice[i]->sound = sound[i]; } - spin_unlock_irqrestore(&opl4->voices_lock, flags); /* set tone number (triggers header loading) */ for (i = 0; i < voices; i++) { @@ -522,11 +526,13 @@ void snd_opl4_note_on(void *private_data voice[i]->level_direct = OPL4_LEVEL_DIRECT_BIT; snd_opl4_update_volume(opl4, voice[i]); } + spin_unlock_irqrestore(&opl4->reg_lock, flags); /* wait for completion of loading */ snd_opl4_wait_for_wave_headers(opl4); /* set remaining parameters */ + spin_lock_irqsave(&opl4->reg_lock, flags); for (i = 0; i < voices; i++) { snd_opl4_update_tone_parameters(opl4, voice[i]); voice[i]->reg_lfo_vibrato = voice[i]->sound->reg_lfo_vibrato; @@ -540,6 +546,7 @@ void snd_opl4_note_on(void *private_data snd_opl4_write(opl4, OPL4_REG_MISC + voice[i]->number, voice[i]->reg_misc); } + spin_unlock_irqrestore(&opl4->reg_lock, flags); } static void snd_opl4_voice_off(opl4_t *opl4, opl4_voice_t *voice) @@ -553,7 +560,7 @@ static void snd_opl4_voice_off(opl4_t *o void snd_opl4_note_off(void *private_data, int note, int vel, snd_midi_channel_t *chan) { - opl4_t *opl4 = snd_magic_cast(opl4_t, private_data, return); + opl4_t *opl4 = private_data; snd_opl4_do_for_note(opl4, note, chan, snd_opl4_voice_off); } @@ -569,14 +576,14 @@ static void snd_opl4_terminate_voice(opl void snd_opl4_terminate_note(void *private_data, int note, snd_midi_channel_t *chan) { - opl4_t *opl4 = snd_magic_cast(opl4_t, private_data, return); + opl4_t *opl4 = private_data; snd_opl4_do_for_note(opl4, note, chan, snd_opl4_terminate_voice); } void snd_opl4_control(void *private_data, int type, snd_midi_channel_t *chan) { - opl4_t *opl4 = snd_magic_cast(opl4_t, private_data, return); + opl4_t *opl4 = private_data; switch (type) { case MIDI_CTL_MSB_MODWHEEL: @@ -616,7 +623,7 @@ void snd_opl4_control(void *private_data void snd_opl4_sysex(void *private_data, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset) { - opl4_t *opl4 = snd_magic_cast(opl4_t, private_data, return); + opl4_t *opl4 = private_data; if (parsed == SNDRV_MIDI_SYSEX_GS_MASTER_VOLUME) snd_opl4_do_for_all(opl4, snd_opl4_update_volume); --- linux-2.6.9-rc1/sound/drivers/serial-u16550.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/sound/drivers/serial-u16550.c 2004-09-02 20:51:53.000000000 -0700 @@ -46,8 +46,7 @@ MODULE_DESCRIPTION("MIDI serial u16550"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{ALSA, MIDI serial u16550}}"); +MODULE_SUPPORTED_DEVICE("{{ALSA, MIDI serial u16550}}"); #define SNDRV_SERIAL_SOUNDCANVAS 0 /* Roland Soundcanvas; F5 NN selects part */ #define SNDRV_SERIAL_MS124T 1 /* Midiator MS-124T */ @@ -81,38 +80,27 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for Serial MIDI."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for Serial MIDI."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable UART16550A chip."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(port, long, boot_devs, 0444); MODULE_PARM_DESC(port, "Port # for UART16550A chip."); -MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); module_param_array(irq, int, boot_devs, 0444); MODULE_PARM_DESC(irq, "IRQ # for UART16550A chip."); -MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); module_param_array(speed, int, boot_devs, 0444); MODULE_PARM_DESC(speed, "Speed in bauds."); -MODULE_PARM_SYNTAX(speed, SNDRV_ENABLED ",allows:{9600,19200,38400,57600,115200},dialog:list"); module_param_array(base, int, boot_devs, 0444); MODULE_PARM_DESC(base, "Base for divisor in bauds."); -MODULE_PARM_SYNTAX(base, SNDRV_ENABLED ",allows:{57600,115200,230400,460800},dialog:list"); module_param_array(outs, int, boot_devs, 0444); MODULE_PARM_DESC(outs, "Number of MIDI outputs."); module_param_array(ins, int, boot_devs, 0444); MODULE_PARM_DESC(ins, "Number of MIDI inputs."); module_param_array(droponfull, bool, boot_devs, 0444); MODULE_PARM_DESC(droponfull, "Flag to enable drop-on-full buffer mode"); -MODULE_PARM_SYNTAX(droponfull, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); -MODULE_PARM_SYNTAX(outs, SNDRV_ENABLED ",allows:{{1,16}},dialog:list"); -MODULE_PARM_SYNTAX(ins, SNDRV_ENABLED ",allows:{{1,16}},dialog:list"); module_param_array(adaptor, int, boot_devs, 0444); MODULE_PARM_DESC(adaptor, "Type of adaptor."); -MODULE_PARM_SYNTAX(adaptor, SNDRV_ENABLED ",allows:{{0=Soundcanvas,1=MS-124T,2=MS-124W S/A,3=MS-124W M/B,4=Generic}},dialog:list"); /*#define SNDRV_SERIAL_MS124W_MB_NOCOMBO 1*/ /* Address outs as 0-3 instead of bitmap */ @@ -524,7 +512,7 @@ static void snd_uart16550_do_close(snd_u static int snd_uart16550_input_open(snd_rawmidi_substream_t * substream) { unsigned long flags; - snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return -ENXIO); + snd_uart16550_t *uart = substream->rmidi->private_data; spin_lock_irqsave(&uart->open_lock, flags); if (uart->filemode == SERIAL_MODE_NOT_OPENED) @@ -538,7 +526,7 @@ static int snd_uart16550_input_open(snd_ static int snd_uart16550_input_close(snd_rawmidi_substream_t * substream) { unsigned long flags; - snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return -ENXIO); + snd_uart16550_t *uart = substream->rmidi->private_data; spin_lock_irqsave(&uart->open_lock, flags); uart->filemode &= ~SERIAL_MODE_INPUT_OPEN; @@ -552,7 +540,7 @@ static int snd_uart16550_input_close(snd static void snd_uart16550_input_trigger(snd_rawmidi_substream_t * substream, int up) { unsigned long flags; - snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return); + snd_uart16550_t *uart = substream->rmidi->private_data; spin_lock_irqsave(&uart->open_lock, flags); if (up) { @@ -566,7 +554,7 @@ static void snd_uart16550_input_trigger( static int snd_uart16550_output_open(snd_rawmidi_substream_t * substream) { unsigned long flags; - snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return -ENXIO); + snd_uart16550_t *uart = substream->rmidi->private_data; spin_lock_irqsave(&uart->open_lock, flags); if (uart->filemode == SERIAL_MODE_NOT_OPENED) @@ -580,7 +568,7 @@ static int snd_uart16550_output_open(snd static int snd_uart16550_output_close(snd_rawmidi_substream_t * substream) { unsigned long flags; - snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return -ENXIO); + snd_uart16550_t *uart = substream->rmidi->private_data; spin_lock_irqsave(&uart->open_lock, flags); uart->filemode &= ~SERIAL_MODE_OUTPUT_OPEN; @@ -652,7 +640,7 @@ static void snd_uart16550_output_write(s { unsigned long flags; unsigned char midi_byte, addr_byte; - snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return); + snd_uart16550_t *uart = substream->rmidi->private_data; char first; static unsigned long lasttime=0; @@ -730,7 +718,7 @@ static void snd_uart16550_output_write(s static void snd_uart16550_output_trigger(snd_rawmidi_substream_t * substream, int up) { unsigned long flags; - snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, substream->rmidi->private_data, return); + snd_uart16550_t *uart = substream->rmidi->private_data; spin_lock_irqsave(&uart->open_lock, flags); if (up) { @@ -765,13 +753,13 @@ static int snd_uart16550_free(snd_uart16 release_resource(uart->res_base); kfree_nocheck(uart->res_base); } - snd_magic_kfree(uart); + kfree(uart); return 0; }; static int snd_uart16550_dev_free(snd_device_t *device) { - snd_uart16550_t *uart = snd_magic_cast(snd_uart16550_t, device->device_data, return -ENXIO); + snd_uart16550_t *uart = device->device_data; return snd_uart16550_free(uart); } @@ -791,7 +779,7 @@ static int __init snd_uart16550_create(s int err; - if ((uart = snd_magic_kcalloc(snd_uart16550_t, 0, GFP_KERNEL)) == NULL) + if ((uart = kcalloc(1, sizeof(*uart), GFP_KERNEL)) == NULL) return -ENOMEM; uart->adaptor = adaptor; uart->card = card; --- linux-2.6.9-rc1/sound/drivers/virmidi.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/sound/drivers/virmidi.c 2004-09-02 20:51:53.000000000 -0700 @@ -57,8 +57,7 @@ MODULE_AUTHOR("Takashi Iwai "); MODULE_DESCRIPTION("Dummy soundcard for virtual rawmidi devices"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{ALSA,Virtual rawmidi device}}"); +MODULE_SUPPORTED_DEVICE("{{ALSA,Virtual rawmidi device}}"); #define MAX_MIDI_DEVICES 8 @@ -70,16 +69,12 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for virmidi soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for virmidi soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable this soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(midi_devs, int, boot_devs, 0444); MODULE_PARM_DESC(midi_devs, "MIDI devices # (1-8)"); -MODULE_PARM_SYNTAX(midi_devs, SNDRV_ENABLED ",allows:{{1,8}}"); typedef struct snd_card_virmidi { snd_card_t *card; @@ -113,7 +108,7 @@ static int __init snd_card_virmidi_probe snd_virmidi_dev_t *rdev; if ((err = snd_virmidi_new(card, idx, &rmidi)) < 0) goto __nodev; - rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, continue); + rdev = rmidi->private_data; vmidi->midi[idx] = rmidi; strcpy(rmidi->name, "Virtual Raw MIDI"); rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH; --- linux-2.6.9-rc1/sound/drivers/vx/vx_core.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/sound/drivers/vx/vx_core.c 2004-09-02 20:51:53.000000000 -0700 @@ -506,7 +506,7 @@ static int vx_test_irq_src(vx_core_t *ch */ static void vx_interrupt(unsigned long private_data) { - vx_core_t *chip = snd_magic_cast(vx_core_t, (void*)private_data, return); + vx_core_t *chip = (vx_core_t *) private_data; unsigned int events; if (chip->chip_status & VX_STAT_IS_STALE) @@ -550,7 +550,7 @@ static void vx_interrupt(unsigned long p */ irqreturn_t snd_vx_irq_handler(int irq, void *dev, struct pt_regs *regs) { - vx_core_t *chip = snd_magic_cast(vx_core_t, dev, return IRQ_NONE); + vx_core_t *chip = dev; if (! (chip->chip_status & VX_STAT_CHIP_INIT) || (chip->chip_status & VX_STAT_IS_STALE)) @@ -572,6 +572,7 @@ static void vx_reset_board(vx_core_t *ch if (cold_reset) { chip->audio_source_target = chip->audio_source; chip->clock_source = INTERNAL_QUARTZ; + chip->clock_mode = VX_CLOCK_MODE_AUTO; chip->freq = 48000; chip->uer_detected = VX_UER_MODE_NOT_PRESENT; chip->uer_bits = SNDRV_PCM_DEFAULT_CON_SPDIF; @@ -603,9 +604,10 @@ static void vx_reset_board(vx_core_t *ch static void vx_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) { - vx_core_t *chip = snd_magic_cast(vx_core_t, entry->private_data, return); + vx_core_t *chip = entry->private_data; static char *audio_src_vxp[] = { "Line", "Mic", "Digital" }; static char *audio_src_vx2[] = { "Analog", "Analog", "Digital" }; + static char *clock_mode[] = { "Auto", "Internal", "External" }; static char *clock_src[] = { "Internal", "External" }; static char *uer_type[] = { "Consumer", "Professional", "Not Present" }; @@ -629,6 +631,7 @@ static void vx_proc_read(snd_info_entry_ snd_iprintf(buffer, "Input Source: %s\n", vx_is_pcmcia(chip) ? audio_src_vxp[chip->audio_source] : audio_src_vx2[chip->audio_source]); + snd_iprintf(buffer, "Clock Mode: %s\n", clock_mode[chip->clock_mode]); snd_iprintf(buffer, "Clock Source: %s\n", clock_src[chip->clock_source]); snd_iprintf(buffer, "Frequency: %d\n", chip->freq); snd_iprintf(buffer, "Detected Frequency: %d\n", chip->freq_detected); @@ -731,7 +734,7 @@ vx_core_t *snd_vx_create(snd_card_t *car snd_assert(card && hw && ops, return NULL); - chip = snd_magic_kcalloc(vx_core_t, extra_size, GFP_KERNEL); + chip = kcalloc(1, sizeof(chip) + extra_size, GFP_KERNEL); if (! chip) { snd_printk(KERN_ERR "vx_core: no memory\n"); return NULL; --- linux-2.6.9-rc1/sound/drivers/vx/vx_hwdep.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/sound/drivers/vx/vx_hwdep.c 2004-09-02 20:51:53.000000000 -0700 @@ -44,7 +44,7 @@ static int vx_hwdep_dsp_status(snd_hwdep [VX_TYPE_VXPOCKET] = "vxpocket", [VX_TYPE_VXP440] = "vxp440", }; - vx_core_t *vx = snd_magic_cast(vx_core_t, hw->private_data, return -ENXIO); + vx_core_t *vx = hw->private_data; snd_assert(type_ids[vx->type], return -EINVAL); strcpy(info->id, type_ids[vx->type]); @@ -60,7 +60,7 @@ static int vx_hwdep_dsp_status(snd_hwdep static int vx_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp) { - vx_core_t *vx = snd_magic_cast(vx_core_t, hw->private_data, return -ENXIO); + vx_core_t *vx = hw->private_data; int index, err; snd_assert(vx->ops->load_dsp, return -ENXIO); --- linux-2.6.9-rc1/sound/drivers/vx/vx_mixer.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/sound/drivers/vx/vx_mixer.c 2004-09-02 20:51:53.000000000 -0700 @@ -26,8 +26,6 @@ #include #include "vx_cmd.h" -#define chip_t vx_core_t - /* * write a codec data (24bit) @@ -524,6 +522,54 @@ static snd_kcontrol_new_t vx_control_aud }; /* + * clock mode selection + */ +static int vx_clock_mode_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + static char *texts[3] = { + "Auto", "Internal", "External" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item > 2) + uinfo->value.enumerated.item = 2; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; +} + +static int vx_clock_mode_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + vx_core_t *chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.enumerated.item[0] = chip->clock_mode; + return 0; +} + +static int vx_clock_mode_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + vx_core_t *chip = snd_kcontrol_chip(kcontrol); + down(&chip->mixer_mutex); + if (chip->clock_mode != ucontrol->value.enumerated.item[0]) { + chip->clock_mode = ucontrol->value.enumerated.item[0]; + vx_set_clock(chip, chip->freq); + up(&chip->mixer_mutex); + return 1; + } + up(&chip->mixer_mutex); + return 0; +} + +static snd_kcontrol_new_t vx_control_clock_mode = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Clock Mode", + .info = vx_clock_mode_info, + .get = vx_clock_mode_get, + .put = vx_clock_mode_put, +}; + +/* * Audio Gain */ static int vx_audio_gain_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) @@ -913,6 +959,9 @@ int snd_vx_mixer_new(vx_core_t *chip) /* Audio source */ if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_audio_src, chip))) < 0) return err; + /* clock mode */ + if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_clock_mode, chip))) < 0) + return err; /* IEC958 controls */ if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958_mask, chip))) < 0) return err; --- linux-2.6.9-rc1/sound/drivers/vx/vx_pcm.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/sound/drivers/vx/vx_pcm.c 2004-09-02 20:51:53.000000000 -0700 @@ -48,14 +48,13 @@ #include #include #include +#include #include #include #include #include #include "vx_cmd.h" -#define chip_t vx_core_t - /* * we use a vmalloc'ed (sg-)buffer @@ -381,7 +380,7 @@ static int vx_send_irqa(vx_core_t *chip) */ static int vx_toggle_pipe(vx_core_t *chip, vx_pipe_t *pipe, int state) { - int err, i, cur_state, delay; + int err, i, cur_state; /* Check the pipe is not already in the requested state */ if (vx_get_pipe_state(chip, pipe, &cur_state) < 0) @@ -394,17 +393,14 @@ static int vx_toggle_pipe(vx_core_t *chi * enough sound buffer for this pipe) */ if (state) { - int delay = CAN_START_DELAY; for (i = 0 ; i < MAX_WAIT_FOR_DSP; i++) { - snd_vx_delay(chip, delay); err = vx_pipe_can_start(chip, pipe); if (err > 0) break; /* Wait for a few, before asking again * to avoid flooding the DSP with our requests */ - if ((i % 4 ) == 0) - delay <<= 1; + mdelay(1); } } @@ -418,15 +414,12 @@ static int vx_toggle_pipe(vx_core_t *chi * reaching the expected state before returning * Check one pipe only (since they are synchronous) */ - delay = WAIT_STATE_DELAY; for (i = 0; i < MAX_WAIT_FOR_DSP; i++) { - snd_vx_delay(chip, delay); err = vx_get_pipe_state(chip, pipe, &cur_state); if (err < 0 || cur_state == state) break; err = -EIO; - if ((i % 4 ) == 0) - delay <<= 1; + mdelay(1); } return err < 0 ? -EIO : 0; } @@ -480,7 +473,7 @@ static int vx_alloc_pipe(vx_core_t *chip return err; /* initialize the pipe record */ - pipe = snd_magic_kcalloc(vx_pipe_t, 0, GFP_KERNEL); + pipe = kcalloc(1, sizeof(*pipe), GFP_KERNEL); if (! pipe) { /* release the pipe */ vx_init_rmh(&rmh, CMD_FREE_PIPE); @@ -514,7 +507,7 @@ static int vx_free_pipe(vx_core_t *chip, vx_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->number, 0); vx_send_msg(chip, &rmh); - snd_magic_kfree(pipe); + kfree(pipe); return 0; } @@ -629,7 +622,7 @@ static int vx_pcm_playback_close(snd_pcm if (! subs->runtime->private_data) return -EINVAL; - pipe = snd_magic_cast(vx_pipe_t, subs->runtime->private_data, return -EINVAL); + pipe = subs->runtime->private_data; if (--pipe->references == 0) { chip->playback_pipes[pipe->number] = NULL; @@ -778,8 +771,8 @@ static void vx_pcm_playback_update(vx_co static void vx_pcm_delayed_start(unsigned long arg) { snd_pcm_substream_t *subs = (snd_pcm_substream_t *)arg; - vx_core_t *chip = snd_magic_cast(vx_core_t, subs->pcm->private_data, return); - vx_pipe_t *pipe = snd_magic_cast(vx_pipe_t, subs->runtime->private_data, return); + vx_core_t *chip = subs->pcm->private_data; + vx_pipe_t *pipe = subs->runtime->private_data; int err; /* printk( KERN_DEBUG "DDDD tasklet delayed start jiffies = %ld\n", jiffies);*/ @@ -801,7 +794,7 @@ static void vx_pcm_delayed_start(unsigne static int vx_pcm_trigger(snd_pcm_substream_t *subs, int cmd) { vx_core_t *chip = snd_pcm_substream_chip(subs); - vx_pipe_t *pipe = snd_magic_cast(vx_pipe_t, subs->runtime->private_data, return -EINVAL); + vx_pipe_t *pipe = subs->runtime->private_data; int err; if (chip->chip_status & VX_STAT_IS_STALE) @@ -846,7 +839,7 @@ static int vx_pcm_trigger(snd_pcm_substr static snd_pcm_uframes_t vx_pcm_playback_pointer(snd_pcm_substream_t *subs) { snd_pcm_runtime_t *runtime = subs->runtime; - vx_pipe_t *pipe = snd_magic_cast(vx_pipe_t, runtime->private_data, return -EINVAL); + vx_pipe_t *pipe = runtime->private_data; return pipe->position; } @@ -874,7 +867,7 @@ static int vx_pcm_prepare(snd_pcm_substr { vx_core_t *chip = snd_pcm_substream_chip(subs); snd_pcm_runtime_t *runtime = subs->runtime; - vx_pipe_t *pipe = snd_magic_cast(vx_pipe_t, runtime->private_data, return -EINVAL); + vx_pipe_t *pipe = runtime->private_data; int err, data_mode; // int max_size, nchunks; @@ -1037,7 +1030,7 @@ static int vx_pcm_capture_close(snd_pcm_ if (! subs->runtime->private_data) return -EINVAL; - pipe = snd_magic_cast(vx_pipe_t, subs->runtime->private_data, return -EINVAL); + pipe = subs->runtime->private_data; chip->capture_pipes[pipe->number] = NULL; pipe_out_monitoring = pipe->monitoring_pipe; @@ -1141,7 +1134,7 @@ static void vx_pcm_capture_update(vx_cor static snd_pcm_uframes_t vx_pcm_capture_pointer(snd_pcm_substream_t *subs) { snd_pcm_runtime_t *runtime = subs->runtime; - vx_pipe_t *pipe = snd_magic_cast(vx_pipe_t, runtime->private_data, return -EINVAL); + vx_pipe_t *pipe = runtime->private_data; return bytes_to_frames(runtime, pipe->hw_ptr); } @@ -1265,7 +1258,7 @@ static int vx_init_audio_io(vx_core_t *c */ static void snd_vx_pcm_free(snd_pcm_t *pcm) { - vx_core_t *chip = snd_magic_cast(vx_core_t, pcm->private_data, return); + vx_core_t *chip = pcm->private_data; chip->pcm[pcm->device] = NULL; if (chip->playback_pipes) { kfree(chip->playback_pipes); --- linux-2.6.9-rc1/sound/drivers/vx/vx_uer.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/sound/drivers/vx/vx_uer.c 2004-09-02 20:51:53.000000000 -0700 @@ -263,17 +263,17 @@ int vx_set_clock(vx_core_t *chip, unsign /* change the audio source if possible */ vx_sync_audio_source(chip); - switch (chip->audio_source) { - case VX_AUDIO_SRC_DIGITAL: + if (chip->clock_mode == VX_CLOCK_MODE_EXTERNAL || + (chip->clock_mode == VX_CLOCK_MODE_AUTO && + chip->audio_source == VX_AUDIO_SRC_DIGITAL)) { if (chip->clock_source != UER_SYNC) { vx_change_clock_source(chip, UER_SYNC); mdelay(6); src_changed = 1; } - if (chip->freq == freq) - return 0; - break; - default: + } else if (chip->clock_mode == VX_CLOCK_MODE_INTERNAL || + (chip->clock_mode == VX_CLOCK_MODE_AUTO && + chip->audio_source != VX_AUDIO_SRC_DIGITAL)) { if (chip->clock_source != INTERNAL_QUARTZ) { vx_change_clock_source(chip, INTERNAL_QUARTZ); src_changed = 1; @@ -283,8 +283,9 @@ int vx_set_clock(vx_core_t *chip, unsign vx_set_internal_clock(chip, freq); if (src_changed) vx_modify_board_inputs(chip); - break; } + if (chip->freq == freq) + return 0; chip->freq = freq; vx_modify_board_clock(chip, 1); return 0; --- linux-2.6.9-rc1/sound/i2c/cs8427.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/sound/i2c/cs8427.c 2004-09-02 20:51:53.000000000 -0700 @@ -34,8 +34,6 @@ MODULE_AUTHOR("Jaroslav Kysela >1) /* fixed address */ typedef struct { @@ -109,7 +107,7 @@ int snd_cs8427_reg_read(snd_i2c_device_t static int snd_cs8427_select_corudata(snd_i2c_device_t *device, int udata) { - cs8427_t *chip = snd_magic_cast(cs8427_t, device->private_data, return -ENXIO); + cs8427_t *chip = device->private_data; int err; udata = udata ? CS8427_BSEL : 0; @@ -128,7 +126,7 @@ static int snd_cs8427_send_corudata(snd_ unsigned char *ndata, int count) { - cs8427_t *chip = snd_magic_cast(cs8427_t, device->private_data, return -ENXIO); + cs8427_t *chip = device->private_data; char *hw_data = udata ? chip->playback.hw_udata : chip->playback.hw_status; char data[32]; int err, idx; @@ -159,7 +157,7 @@ static int snd_cs8427_send_corudata(snd_ static void snd_cs8427_free(snd_i2c_device_t *device) { if (device->private_data) - snd_magic_kfree(device->private_data); + kfree(device->private_data); } int snd_cs8427_create(snd_i2c_bus_t *bus, @@ -211,7 +209,7 @@ int snd_cs8427_create(snd_i2c_bus_t *bus if ((err = snd_i2c_device_create(bus, "CS8427", CS8427_ADDR | (addr & 7), &device)) < 0) return err; - chip = device->private_data = snd_magic_kcalloc(cs8427_t, 0, GFP_KERNEL); + chip = device->private_data = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) { snd_i2c_device_free(device); return -ENOMEM; @@ -297,7 +295,7 @@ void snd_cs8427_reset(snd_i2c_device_t * int data; snd_assert(cs8427, return); - chip = snd_magic_cast(cs8427_t, cs8427->private_data, return); + chip = cs8427->private_data; snd_i2c_lock(cs8427->bus); chip->regmap[CS8427_REG_CLOCKSOURCE] &= ~(CS8427_RUN | CS8427_RXDMASK); snd_cs8427_reg_write(cs8427, CS8427_REG_CLOCKSOURCE, chip->regmap[CS8427_REG_CLOCKSOURCE]); @@ -389,7 +387,7 @@ static int snd_cs8427_spdif_get(snd_kcon snd_ctl_elem_value_t * ucontrol) { snd_i2c_device_t *device = snd_kcontrol_chip(kcontrol); - cs8427_t *chip = snd_magic_cast(cs8427_t, device->private_data, return -ENXIO); + cs8427_t *chip = device->private_data; snd_i2c_lock(device->bus); memcpy(ucontrol->value.iec958.status, chip->playback.def_status, 24); @@ -401,7 +399,7 @@ static int snd_cs8427_spdif_put(snd_kcon snd_ctl_elem_value_t * ucontrol) { snd_i2c_device_t *device = snd_kcontrol_chip(kcontrol); - cs8427_t *chip = snd_magic_cast(cs8427_t, device->private_data, return -ENXIO); + cs8427_t *chip = device->private_data; unsigned char *status = kcontrol->private_value ? chip->playback.pcm_status : chip->playback.def_status; snd_pcm_runtime_t *runtime = chip->playback.substream ? chip->playback.substream->runtime : NULL; int err, change; @@ -432,8 +430,6 @@ static int snd_cs8427_spdif_mask_get(snd return 0; } -#define CONTROLS (sizeof(snd_cs8427_iec958_controls)/sizeof(snd_kcontrol_new_t)) - static snd_kcontrol_new_t snd_cs8427_iec958_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -487,13 +483,13 @@ int snd_cs8427_iec958_build(snd_i2c_devi snd_pcm_substream_t *play_substream, snd_pcm_substream_t *cap_substream) { - cs8427_t *chip = snd_magic_cast(cs8427_t, cs8427->private_data, return -ENXIO); + cs8427_t *chip = cs8427->private_data; snd_kcontrol_t *kctl; unsigned int idx; int err; snd_assert(play_substream && cap_substream, return -EINVAL); - for (idx = 0; idx < CONTROLS; idx++) { + for (idx = 0; idx < ARRAY_SIZE(snd_cs8427_iec958_controls); idx++) { kctl = snd_ctl_new1(&snd_cs8427_iec958_controls[idx], cs8427); if (kctl == NULL) return -ENOMEM; @@ -517,7 +513,7 @@ int snd_cs8427_iec958_active(snd_i2c_dev cs8427_t *chip; snd_assert(cs8427, return -ENXIO); - chip = snd_magic_cast(cs8427_t, cs8427->private_data, return -ENXIO); + chip = cs8427->private_data; if (active) memcpy(chip->playback.pcm_status, chip->playback.def_status, 24); chip->playback.pcm_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; @@ -533,7 +529,7 @@ int snd_cs8427_iec958_pcm(snd_i2c_device int err, reset; snd_assert(cs8427, return -ENXIO); - chip = snd_magic_cast(cs8427_t, cs8427->private_data, return -ENXIO); + chip = cs8427->private_data; status = chip->playback.pcm_status; snd_i2c_lock(cs8427->bus); if (status[0] & IEC958_AES0_PROFESSIONAL) { --- linux-2.6.9-rc1/sound/i2c/i2c.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/sound/i2c/i2c.c 2004-09-02 20:51:53.000000000 -0700 @@ -62,13 +62,13 @@ static int snd_i2c_bus_free(snd_i2c_bus_ } if (bus->private_free) bus->private_free(bus); - snd_magic_kfree(bus); + kfree(bus); return 0; } static int snd_i2c_bus_dev_free(snd_device_t *device) { - snd_i2c_bus_t *bus = snd_magic_cast(snd_i2c_bus_t, device->device_data, return -ENXIO); + snd_i2c_bus_t *bus = device->device_data; return snd_i2c_bus_free(bus); } @@ -81,7 +81,7 @@ int snd_i2c_bus_create(snd_card_t *card, }; *ri2c = NULL; - bus = (snd_i2c_bus_t *)snd_magic_kcalloc(snd_i2c_bus_t, 0, GFP_KERNEL); + bus = kcalloc(1, sizeof(*bus), GFP_KERNEL); if (bus == NULL) return -ENOMEM; init_MUTEX(&bus->lock_mutex); @@ -108,7 +108,7 @@ int snd_i2c_device_create(snd_i2c_bus_t *rdevice = NULL; snd_assert(bus != NULL, return -EINVAL); - device = (snd_i2c_device_t *)snd_magic_kcalloc(snd_i2c_device_t, 0, GFP_KERNEL); + device = kcalloc(1, sizeof(*device), GFP_KERNEL); if (device == NULL) return -ENOMEM; device->addr = addr; @@ -125,7 +125,7 @@ int snd_i2c_device_free(snd_i2c_device_t list_del(&device->list); if (device->private_free) device->private_free(device); - snd_magic_kfree(device); + kfree(device); return 0; } --- linux-2.6.9-rc1/sound/i2c/l3/uda1341.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/sound/i2c/l3/uda1341.c 2004-09-02 20:51:53.000000000 -0700 @@ -17,7 +17,7 @@ * 2002-05-12 Tomas Kasparek another code cleanup */ -/* $Id: uda1341.c,v 1.10 2003/10/23 14:34:52 perex Exp $ */ +/* $Id: uda1341.c,v 1.13 2004/07/20 15:54:13 cladisch Exp $ */ #include #include @@ -131,7 +131,6 @@ struct uda1341 { //hack for ALSA magic casting typedef struct l3_client l3_client_t; -#define chip_t l3_client_t /* transfer 8bit integer into string with binary representation */ void int2str_bin8(uint8_t val, char *buf){ @@ -332,7 +331,7 @@ int snd_uda1341_cfg_write(struct l3_clie static void snd_uda1341_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) { - struct l3_client *clnt = snd_magic_cast(l3_client_t, entry->private_data, return); + struct l3_client *clnt = entry->private_data; struct uda1341 *uda = clnt->driver_data; int peak; @@ -397,7 +396,7 @@ static void snd_uda1341_proc_read(snd_in static void snd_uda1341_proc_regs_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) { - struct l3_client *clnt = snd_magic_cast(l3_client_t, entry->private_data, return); + struct l3_client *clnt = entry->private_data; struct uda1341 *uda = clnt->driver_data; int reg; char buf[12]; @@ -618,8 +617,6 @@ static int snd_uda1341_put_2regs(snd_kco /* }}} */ -#define UDA1341_CONTROLS (sizeof(snd_uda1341_controls)/sizeof(snd_kcontrol_new_t)) - static snd_kcontrol_new_t snd_uda1341_controls[] = { UDA1341_SINGLE("Master Playback Switch", CMD_MUTE, data0_2, 2, 1, 1), UDA1341_SINGLE("Master Playback Volume", CMD_VOLUME, data0_0, 0, 63, 1), @@ -653,12 +650,12 @@ static snd_kcontrol_new_t snd_uda1341_co static void uda1341_free(struct l3_client *uda1341) { l3_detach_client(uda1341); // calls kfree for driver_data (uda1341_t) - snd_magic_kfree(uda1341); + kfree(uda1341); } static int uda1341_dev_free(snd_device_t *device) { - struct l3_client *clnt = snd_magic_cast(l3_client_t, device->device_data, return); + struct l3_client *clnt = device->device_data; uda1341_free(clnt); return 0; } @@ -673,7 +670,7 @@ int __init snd_chip_uda1341_mixer_new(sn snd_assert(card != NULL, return -EINVAL); - uda1341 = snd_magic_kcalloc(l3_client_t, 0, GFP_KERNEL); + uda1341 = kcalloc(1, sizeof(*uda1341), GFP_KERNEL); if (uda1341 == NULL) return -ENOMEM; @@ -688,7 +685,7 @@ int __init snd_chip_uda1341_mixer_new(sn return err; } - for (idx = 0; idx < UDA1341_CONTROLS; idx++) { + for (idx = 0; idx < ARRAY_SIZE(snd_uda1341_controls); idx++) { if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_uda1341_controls[idx], uda1341))) < 0) return err; } @@ -710,7 +707,7 @@ static int uda1341_attach(struct l3_clie { struct uda1341 *uda; - uda = snd_magic_kcalloc(uda1341_t, 0, GFP_KERNEL); + uda = kcalloc(1, sizeof(*uda), 0, GFP_KERNEL); if (!uda) return -ENOMEM; @@ -734,7 +731,7 @@ static int uda1341_attach(struct l3_clie static void uda1341_detach(struct l3_client *clnt) { if (clnt->driver_data) - snd_magic_kfree(clnt->driver_data); + kfree(clnt->driver_data); } static int @@ -821,8 +818,7 @@ module_exit(uda1341_exit); MODULE_AUTHOR("Tomas Kasparek "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Philips UDA1341 CODEC driver for ALSA"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{UDA1341,UDA1341TS}}"); +MODULE_SUPPORTED_DEVICE("{{UDA1341,UDA1341TS}}"); EXPORT_SYMBOL(snd_chip_uda1341_mixer_new); --- linux-2.6.9-rc1/sound/i2c/other/ak4117.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/sound/i2c/other/ak4117.c 2004-09-02 20:51:53.000000000 -0700 @@ -33,8 +33,6 @@ MODULE_AUTHOR("Jaroslav Kysela timer); - snd_magic_kfree(chip); + kfree(chip); } static int snd_ak4117_dev_free(snd_device_t *device) { - ak4117_t *chip = snd_magic_cast(ak4117_t, device->device_data, return -ENXIO); + ak4117_t *chip = device->device_data; snd_ak4117_free(chip); return 0; } @@ -85,7 +83,7 @@ int snd_ak4117_create(snd_card_t *card, .dev_free = snd_ak4117_dev_free, }; - chip = (ak4117_t *)snd_magic_kcalloc(ak4117_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; spin_lock_init(&chip->lock); @@ -544,7 +542,7 @@ int snd_ak4117_check_rate_and_errors(ak4 static void snd_ak4117_timer(unsigned long data) { - ak4117_t *chip = snd_magic_cast(ak4117_t, (void *)data, return); + ak4117_t *chip = (ak4117_t *)data; if (chip->init) return; --- linux-2.6.9-rc1/sound/i2c/other/ak4xxx-adda.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/sound/i2c/other/ak4xxx-adda.c 2004-09-02 20:51:53.000000000 -0700 @@ -237,7 +237,7 @@ static int snd_akm4xxx_volume_info(snd_k static int snd_akm4xxx_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { - akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol); + akm4xxx_t *ak = snd_kcontrol_chip(kcontrol); int chip = AK_GET_CHIP(kcontrol->private_value); int addr = AK_GET_ADDR(kcontrol->private_value); int invert = AK_GET_INVERT(kcontrol->private_value); @@ -250,7 +250,7 @@ static int snd_akm4xxx_volume_get(snd_kc static int snd_akm4xxx_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { - akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol); + akm4xxx_t *ak = snd_kcontrol_chip(kcontrol); int chip = AK_GET_CHIP(kcontrol->private_value); int addr = AK_GET_ADDR(kcontrol->private_value); int invert = AK_GET_INVERT(kcontrol->private_value); @@ -277,7 +277,7 @@ static int snd_akm4xxx_ipga_gain_info(sn static int snd_akm4xxx_ipga_gain_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { - akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol); + akm4xxx_t *ak = snd_kcontrol_chip(kcontrol); int chip = AK_GET_CHIP(kcontrol->private_value); int addr = AK_GET_ADDR(kcontrol->private_value); ucontrol->value.integer.value[0] = snd_akm4xxx_get_ipga(ak, chip, addr) & 0x7f; @@ -286,7 +286,7 @@ static int snd_akm4xxx_ipga_gain_get(snd static int snd_akm4xxx_ipga_gain_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { - akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol); + akm4xxx_t *ak = snd_kcontrol_chip(kcontrol); int chip = AK_GET_CHIP(kcontrol->private_value); int addr = AK_GET_ADDR(kcontrol->private_value); unsigned char nval = (ucontrol->value.integer.value[0] % 37) | 0x80; @@ -312,7 +312,7 @@ static int snd_akm4xxx_deemphasis_info(s static int snd_akm4xxx_deemphasis_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) { - akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol); + akm4xxx_t *ak = snd_kcontrol_chip(kcontrol); int chip = AK_GET_CHIP(kcontrol->private_value); int addr = AK_GET_ADDR(kcontrol->private_value); int shift = AK_GET_SHIFT(kcontrol->private_value); @@ -322,7 +322,7 @@ static int snd_akm4xxx_deemphasis_get(sn static int snd_akm4xxx_deemphasis_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { - akm4xxx_t *ak = _snd_kcontrol_chip(kcontrol); + akm4xxx_t *ak = snd_kcontrol_chip(kcontrol); int chip = AK_GET_CHIP(kcontrol->private_value); int addr = AK_GET_ADDR(kcontrol->private_value); int shift = AK_GET_SHIFT(kcontrol->private_value); --- linux-2.6.9-rc1/sound/i2c/tea6330t.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/sound/i2c/tea6330t.c 2004-09-02 20:51:53.000000000 -0700 @@ -30,8 +30,6 @@ MODULE_AUTHOR("Jaroslav Kysela >1) /* fixed address */ #define TEA6330T_SADDR_VOLUME_LEFT 0x00 /* volume left */ @@ -259,8 +257,6 @@ static int snd_tea6330t_put_treble(snd_k return change; } -#define TEA6330T_CONTROLS (sizeof(snd_tea6330t_controls)/sizeof(snd_kcontrol_new_t)) - static snd_kcontrol_new_t snd_tea6330t_controls[] = { TEA6330T_MASTER_SWITCH("Master Playback Switch", 0), TEA6330T_MASTER_VOLUME("Master Playback Volume", 0), @@ -270,8 +266,8 @@ TEA6330T_TREBLE("Tone Control - Treble", static void snd_tea6330_free(snd_i2c_device_t *device) { - tea6330t_t *tea = snd_magic_cast(tea6330t_t, device->private_data, return); - snd_magic_kfree(tea); + tea6330t_t *tea = device->private_data; + kfree(tea); } int snd_tea6330t_update_mixer(snd_card_t * card, @@ -286,11 +282,11 @@ int snd_tea6330t_update_mixer(snd_card_t u8 default_treble, default_bass; unsigned char bytes[7]; - tea = snd_magic_kcalloc(tea6330t_t, 0, GFP_KERNEL); + tea = kcalloc(1, sizeof(*tea), GFP_KERNEL); if (tea == NULL) return -ENOMEM; if ((err = snd_i2c_device_create(bus, "TEA6330T", TEA6330T_ADDR, &device)) < 0) { - snd_magic_kfree(tea); + kfree(tea); return err; } tea->device = device; @@ -336,7 +332,7 @@ int snd_tea6330t_update_mixer(snd_card_t if ((err = snd_component_add(card, "TEA6330T")) < 0) goto __error; - for (idx = 0; idx < TEA6330T_CONTROLS; idx++) { + for (idx = 0; idx < ARRAY_SIZE(snd_tea6330t_controls); idx++) { knew = &snd_tea6330t_controls[idx]; if (tea->treble == 0 && !strcmp(knew->name, "Tone Control - Treble")) continue; --- linux-2.6.9-rc1/sound/isa/ad1816a/ad1816a.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/sound/isa/ad1816a/ad1816a.c 2004-09-02 20:51:53.000000000 -0700 @@ -30,15 +30,12 @@ #include #include -#define chip_t ad1816a_t - #define PFX "ad1816a: " MODULE_AUTHOR("Massimo Piccioni "); MODULE_DESCRIPTION("AD1816A, AD1815"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Highscreen,Sound-Boostar 16 3D}," +MODULE_SUPPORTED_DEVICE("{{Highscreen,Sound-Boostar 16 3D}," "{Analog Devices,AD1815}," "{Analog Devices,AD1816A}," "{TerraTec,Base 64}," @@ -60,34 +57,24 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for ad1816a based soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for ad1816a based soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable ad1816a based soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(port, long, boot_devs, 0444); MODULE_PARM_DESC(port, "Port # for ad1816a driver."); -MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); module_param_array(mpu_port, long, boot_devs, 0444); MODULE_PARM_DESC(mpu_port, "MPU-401 port # for ad1816a driver."); -MODULE_PARM_SYNTAX(mpu_port, SNDRV_PORT12_DESC); module_param_array(fm_port, long, boot_devs, 0444); MODULE_PARM_DESC(fm_port, "FM port # for ad1816a driver."); -MODULE_PARM_SYNTAX(fm_port, SNDRV_PORT12_DESC); module_param_array(irq, int, boot_devs, 0444); MODULE_PARM_DESC(irq, "IRQ # for ad1816a driver."); -MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); module_param_array(mpu_irq, int, boot_devs, 0444); MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for ad1816a driver."); -MODULE_PARM_SYNTAX(mpu_irq, SNDRV_IRQ_DESC); module_param_array(dma1, int, boot_devs, 0444); MODULE_PARM_DESC(dma1, "1st DMA # for ad1816a driver."); -MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC); module_param_array(dma2, int, boot_devs, 0444); MODULE_PARM_DESC(dma2, "2nd DMA # for ad1816a driver."); -MODULE_PARM_SYNTAX(dma2, SNDRV_DMA_DESC); struct snd_card_ad1816a { struct pnp_dev *dev; --- linux-2.6.9-rc1/sound/isa/ad1816a/ad1816a_lib.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/sound/isa/ad1816a/ad1816a_lib.c 2004-09-02 20:51:53.000000000 -0700 @@ -34,8 +34,6 @@ MODULE_AUTHOR("Massimo Piccioni lock); @@ -550,13 +548,13 @@ static int snd_ad1816a_free(ad1816a_t *c snd_dma_disable(chip->dma2); free_dma(chip->dma2); } - snd_magic_kfree(chip); + kfree(chip); return 0; } static int snd_ad1816a_dev_free(snd_device_t *device) { - ad1816a_t *chip = snd_magic_cast(ad1816a_t, device->device_data, return -ENXIO); + ad1816a_t *chip = device->device_data; return snd_ad1816a_free(chip); } @@ -585,7 +583,7 @@ int snd_ad1816a_create(snd_card_t *card, *rchip = NULL; - chip = snd_magic_kcalloc(ad1816a_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; chip->irq = -1; @@ -661,7 +659,7 @@ static snd_pcm_ops_t snd_ad1816a_capture static void snd_ad1816a_pcm_free(snd_pcm_t *pcm) { - ad1816a_t *chip = snd_magic_cast(ad1816a_t, pcm->private_data, return); + ad1816a_t *chip = pcm->private_data; chip->pcm = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -696,7 +694,7 @@ int snd_ad1816a_pcm(ad1816a_t *chip, int static void snd_ad1816a_timer_free(snd_timer_t *timer) { - ad1816a_t *chip = snd_magic_cast(ad1816a_t, timer->private_data, return); + ad1816a_t *chip = timer->private_data; chip->timer = NULL; } @@ -901,8 +899,6 @@ static int snd_ad1816a_put_double(snd_kc return change; } -#define AD1816A_CONTROLS (sizeof(snd_ad1816a_controls)/sizeof(snd_kcontrol_new_t)) - static snd_kcontrol_new_t snd_ad1816a_controls[] = { AD1816A_DOUBLE("Master Playback Switch", AD1816A_MASTER_ATT, 15, 7, 1, 1), AD1816A_DOUBLE("Master Playback Volume", AD1816A_MASTER_ATT, 8, 0, 31, 1), @@ -950,7 +946,7 @@ int snd_ad1816a_mixer(ad1816a_t *chip) strcpy(card->mixername, snd_ad1816a_chip_id(chip)); - for (idx = 0; idx < AD1816A_CONTROLS; idx++) { + for (idx = 0; idx < ARRAY_SIZE(snd_ad1816a_controls); idx++) { if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ad1816a_controls[idx], chip))) < 0) return err; } --- linux-2.6.9-rc1/sound/isa/ad1848/ad1848.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/sound/isa/ad1848/ad1848.c 2004-09-02 20:51:53.000000000 -0700 @@ -30,13 +30,10 @@ #include #include -#define chip_t ad1848_t - MODULE_AUTHOR("Tugrul Galatali , Jaroslav Kysela "); MODULE_DESCRIPTION("AD1848/AD1847/CS4248"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Analog Devices,AD1848}," +MODULE_SUPPORTED_DEVICE("{{Analog Devices,AD1848}," "{Analog Devices,AD1847}," "{Crystal Semiconductors,CS4248}}"); @@ -51,25 +48,18 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for AD1848 soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for AD1848 soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable AD1848 soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(port, long, boot_devs, 0444); MODULE_PARM_DESC(port, "Port # for AD1848 driver."); -MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); module_param_array(irq, int, boot_devs, 0444); MODULE_PARM_DESC(irq, "IRQ # for AD1848 driver."); -MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); module_param_array(dma1, int, boot_devs, 0444); MODULE_PARM_DESC(dma1, "DMA1 # for AD1848 driver."); -MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC); module_param_array(thinkpad, bool, boot_devs, 0444); MODULE_PARM_DESC(thinkpad, "Enable only for the onboard CS4248 of IBM Thinkpad 360/750/755 series."); -MODULE_PARM_SYNTAX(thinkpad, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); static snd_card_t *snd_ad1848_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; --- linux-2.6.9-rc1/sound/isa/ad1848/ad1848_lib.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/sound/isa/ad1848/ad1848_lib.c 2004-09-02 20:51:53.000000000 -0700 @@ -39,8 +39,6 @@ MODULE_AUTHOR("Jaroslav Kysela mode & AD1848_MODE_PLAY) && chip->playback_substream && (chip->mode & AD1848_MODE_RUNNING)) @@ -649,7 +647,7 @@ static void snd_ad1848_thinkpad_twiddle( #ifdef CONFIG_PM static int snd_ad1848_suspend(snd_card_t *card, unsigned int state) { - ad1848_t *chip = snd_magic_cast(ad1848_t, card->pm_private_data, return -EINVAL); + ad1848_t *chip = card->pm_private_data; if (card->power_state == SNDRV_CTL_POWER_D3hot) return 0; @@ -666,7 +664,7 @@ static int snd_ad1848_suspend(snd_card_t static int snd_ad1848_resume(snd_card_t *card, unsigned int state) { - ad1848_t *chip = snd_magic_cast(ad1848_t, card->pm_private_data, return -EINVAL); + ad1848_t *chip = card->pm_private_data; if (card->power_state == SNDRV_CTL_POWER_D0) return 0; @@ -867,13 +865,13 @@ static int snd_ad1848_free(ad1848_t *chi snd_dma_disable(chip->dma); free_dma(chip->dma); } - snd_magic_kfree(chip); + kfree(chip); return 0; } static int snd_ad1848_dev_free(snd_device_t *device) { - ad1848_t *chip = snd_magic_cast(ad1848_t, device->device_data, return -ENXIO); + ad1848_t *chip = device->device_data; return snd_ad1848_free(chip); } @@ -901,7 +899,7 @@ int snd_ad1848_create(snd_card_t * card, int err; *rchip = NULL; - chip = snd_magic_kcalloc(ad1848_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; spin_lock_init(&chip->reg_lock); @@ -977,7 +975,7 @@ static snd_pcm_ops_t snd_ad1848_capture_ static void snd_ad1848_pcm_free(snd_pcm_t *pcm) { - ad1848_t *chip = snd_magic_cast(ad1848_t, pcm->private_data, return); + ad1848_t *chip = pcm->private_data; chip->pcm = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } --- linux-2.6.9-rc1/sound/isa/als100.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/sound/isa/als100.c 2004-09-02 20:51:53.000000000 -0700 @@ -32,15 +32,12 @@ #include #include -#define chip_t sb_t - #define PFX "als100: " MODULE_AUTHOR("Massimo Piccioni "); MODULE_DESCRIPTION("Avance Logic ALS1X0"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Avance Logic,ALS100 - PRO16PNP}," +MODULE_SUPPORTED_DEVICE("{{Avance Logic,ALS100 - PRO16PNP}," "{Avance Logic,ALS110}," "{Avance Logic,ALS120}," "{Avance Logic,ALS200}," @@ -63,34 +60,24 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for als100 based soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for als100 based soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable als100 based soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(port, long, boot_devs, 0444); MODULE_PARM_DESC(port, "Port # for als100 driver."); -MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); module_param_array(mpu_port, long, boot_devs, 0444); MODULE_PARM_DESC(mpu_port, "MPU-401 port # for als100 driver."); -MODULE_PARM_SYNTAX(mpu_port, SNDRV_PORT12_DESC); module_param_array(fm_port, long, boot_devs, 0444); MODULE_PARM_DESC(fm_port, "FM port # for als100 driver."); -MODULE_PARM_SYNTAX(fm_port, SNDRV_PORT12_DESC); module_param_array(irq, int, boot_devs, 0444); MODULE_PARM_DESC(irq, "IRQ # for als100 driver."); -MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); module_param_array(mpu_irq, int, boot_devs, 0444); MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for als100 driver."); -MODULE_PARM_SYNTAX(mpu_irq, SNDRV_IRQ_DESC); module_param_array(dma8, int, boot_devs, 0444); MODULE_PARM_DESC(dma8, "8-bit DMA # for als100 driver."); -MODULE_PARM_SYNTAX(dma8, SNDRV_DMA8_DESC); module_param_array(dma16, int, boot_devs, 0444); MODULE_PARM_DESC(dma16, "16-bit DMA # for als100 driver."); -MODULE_PARM_SYNTAX(dma16, SNDRV_DMA16_DESC); struct snd_card_als100 { int dev_no; --- linux-2.6.9-rc1/sound/isa/azt2320.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/sound/isa/azt2320.c 2004-09-02 20:51:53.000000000 -0700 @@ -43,15 +43,12 @@ #include #include -#define chip_t cs4231_t - #define PFX "azt2320: " MODULE_AUTHOR("Massimo Piccioni "); MODULE_DESCRIPTION("Aztech Systems AZT2320"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Aztech Systems,PRO16V}," +MODULE_SUPPORTED_DEVICE("{{Aztech Systems,PRO16V}," "{Aztech Systems,AZT2320}," "{Aztech Systems,AZT3300}," "{Aztech Systems,AZT2320}," @@ -72,37 +69,26 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for azt2320 based soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for azt2320 based soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable azt2320 based soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(port, long, boot_devs, 0444); MODULE_PARM_DESC(port, "Port # for azt2320 driver."); -MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); module_param_array(wss_port, long, boot_devs, 0444); MODULE_PARM_DESC(wss_port, "WSS Port # for azt2320 driver."); -MODULE_PARM_SYNTAX(wss_port, SNDRV_PORT12_DESC); module_param_array(mpu_port, long, boot_devs, 0444); MODULE_PARM_DESC(mpu_port, "MPU-401 port # for azt2320 driver."); -MODULE_PARM_SYNTAX(mpu_port, SNDRV_PORT12_DESC); module_param_array(fm_port, long, boot_devs, 0444); MODULE_PARM_DESC(fm_port, "FM port # for azt2320 driver."); -MODULE_PARM_SYNTAX(fm_port, SNDRV_PORT12_DESC); module_param_array(irq, int, boot_devs, 0444); MODULE_PARM_DESC(irq, "IRQ # for azt2320 driver."); -MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); module_param_array(mpu_irq, int, boot_devs, 0444); MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for azt2320 driver."); -MODULE_PARM_SYNTAX(mpu_irq, SNDRV_IRQ_DESC); module_param_array(dma1, int, boot_devs, 0444); MODULE_PARM_DESC(dma1, "1st DMA # for azt2320 driver."); -MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC); module_param_array(dma2, int, boot_devs, 0444); MODULE_PARM_DESC(dma2, "2nd DMA # for azt2320 driver."); -MODULE_PARM_SYNTAX(dma2, SNDRV_DMA_DESC); struct snd_card_azt2320 { int dev_no; --- linux-2.6.9-rc1/sound/isa/cmi8330.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/sound/isa/cmi8330.c 2004-09-02 20:51:53.000000000 -0700 @@ -63,8 +63,7 @@ MODULE_AUTHOR("George Talusan "); MODULE_DESCRIPTION("C-Media CMI8330"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{C-Media,CMI8330,isapnp:{CMI0001,@@@0001,@X@0001}}}"); +MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8330,isapnp:{CMI0001,@@@0001,@X@0001}}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; @@ -83,41 +82,30 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for CMI8330 soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for CMI8330 soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable CMI8330 soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); #ifdef CONFIG_PNP module_param_array(isapnp, bool, boot_devs, 0444); MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard."); -MODULE_PARM_SYNTAX(isapnp, SNDRV_ISAPNP_DESC); #endif module_param_array(sbport, long, boot_devs, 0444); MODULE_PARM_DESC(sbport, "Port # for CMI8330 SB driver."); -MODULE_PARM_SYNTAX(sbport, SNDRV_ENABLED ",allows:{{0x220,0x280,0x20}},prefers:{0x220},base:16,dialog:list"); module_param_array(sbirq, int, boot_devs, 0444); MODULE_PARM_DESC(sbirq, "IRQ # for CMI8330 SB driver."); -MODULE_PARM_SYNTAX(sbirq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10},{11},{12}},prefers:{5},dialog:list"); module_param_array(sbdma8, int, boot_devs, 0444); MODULE_PARM_DESC(sbdma8, "DMA8 for CMI8330 SB driver."); -MODULE_PARM_SYNTAX(sbdma8, SNDRV_DMA8_DESC ",prefers:{1}"); module_param_array(sbdma16, int, boot_devs, 0444); MODULE_PARM_DESC(sbdma16, "DMA16 for CMI8330 SB driver."); -MODULE_PARM_SYNTAX(sbdma16, SNDRV_ENABLED ",allows:{{5},{7}},prefers:{5},dialog:list"); module_param_array(wssport, long, boot_devs, 0444); MODULE_PARM_DESC(wssport, "Port # for CMI8330 WSS driver."); -MODULE_PARM_SYNTAX(wssport, SNDRV_ENABLED ",allows:{{0x530},{0xe80,0xf40,0xc0}},prefers:{0x530},base:16,dialog:list"); module_param_array(wssirq, int, boot_devs, 0444); MODULE_PARM_DESC(wssirq, "IRQ # for CMI8330 WSS driver."); -MODULE_PARM_SYNTAX(wssirq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10},{11},{12}},prefers:{11},dialog:list"); module_param_array(wssdma, int, boot_devs, 0444); MODULE_PARM_DESC(wssdma, "DMA for CMI8330 WSS driver."); -MODULE_PARM_SYNTAX(wssdma, SNDRV_DMA8_DESC ",prefers:{0}"); #define CMI8330_RMUX3D 16 #define CMI8330_MUTEMUX 17 @@ -385,7 +373,7 @@ static int __devinit snd_cmi8330_pnp(int static int snd_cmi8330_playback_open(snd_pcm_substream_t * substream) { - struct snd_cmi8330 *chip = (struct snd_cmi8330 *)_snd_pcm_substream_chip(substream); + struct snd_cmi8330 *chip = snd_pcm_substream_chip(substream); /* replace the private_data and call the original open callback */ substream->private_data = chip->streams[SNDRV_PCM_STREAM_PLAYBACK].private_data; @@ -394,7 +382,7 @@ static int snd_cmi8330_playback_open(snd static int snd_cmi8330_capture_open(snd_pcm_substream_t * substream) { - struct snd_cmi8330 *chip = (struct snd_cmi8330 *)_snd_pcm_substream_chip(substream); + struct snd_cmi8330 *chip = snd_pcm_substream_chip(substream); /* replace the private_data and call the original open callback */ substream->private_data = chip->streams[SNDRV_PCM_STREAM_CAPTURE].private_data; --- linux-2.6.9-rc1/sound/isa/cs423x/cs4231.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/sound/isa/cs423x/cs4231.c 2004-09-02 20:51:53.000000000 -0700 @@ -30,13 +30,10 @@ #include #include -#define chip_t cs4231_t - MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Generic CS4231"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Crystal Semiconductors,CS4231}}"); +MODULE_SUPPORTED_DEVICE("{{Crystal Semiconductors,CS4231}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ @@ -51,31 +48,22 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for CS4231 soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for CS4231 soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable CS4231 soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(port, long, boot_devs, 0444); MODULE_PARM_DESC(port, "Port # for CS4231 driver."); -MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); module_param_array(mpu_port, long, boot_devs, 0444); MODULE_PARM_DESC(mpu_port, "MPU-401 port # for CS4231 driver."); -MODULE_PARM_SYNTAX(mpu_port, SNDRV_PORT12_DESC); module_param_array(irq, int, boot_devs, 0444); MODULE_PARM_DESC(irq, "IRQ # for CS4231 driver."); -MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); module_param_array(mpu_irq, int, boot_devs, 0444); MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for CS4231 driver."); -MODULE_PARM_SYNTAX(mpu_irq, SNDRV_IRQ_DESC); module_param_array(dma1, int, boot_devs, 0444); MODULE_PARM_DESC(dma1, "DMA1 # for CS4231 driver."); -MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC); module_param_array(dma2, int, boot_devs, 0444); MODULE_PARM_DESC(dma2, "DMA2 # for CS4231 driver."); -MODULE_PARM_SYNTAX(dma2, SNDRV_DMA_DESC); static snd_card_t *snd_cs4231_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; --- linux-2.6.9-rc1/sound/isa/cs423x/cs4231_lib.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/sound/isa/cs423x/cs4231_lib.c 2004-09-02 20:51:53.000000000 -0700 @@ -43,8 +43,6 @@ MODULE_AUTHOR("Jaroslav Kysela pm_private_data, return -EINVAL); + cs4231_t *chip = card->pm_private_data; if (chip->suspend) { chip->suspend(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); @@ -1417,7 +1415,7 @@ static int snd_cs4231_pm_suspend(snd_car static int snd_cs4231_pm_resume(snd_card_t *card, unsigned int state) { - cs4231_t *chip = snd_magic_cast(cs4231_t, card->pm_private_data, return -EINVAL); + cs4231_t *chip = card->pm_private_data; if (chip->resume) { chip->resume(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D0); @@ -1453,13 +1451,13 @@ static int snd_cs4231_free(cs4231_t *chi } if (chip->timer) snd_device_free(chip->card, chip->timer); - snd_magic_kfree(chip); + kfree(chip); return 0; } static int snd_cs4231_dev_free(snd_device_t *device) { - cs4231_t *chip = snd_magic_cast(cs4231_t, device->device_data, return -ENXIO); + cs4231_t *chip = device->device_data; return snd_cs4231_free(chip); } @@ -1493,7 +1491,7 @@ static int snd_cs4231_new(snd_card_t * c cs4231_t *chip; *rchip = NULL; - chip = snd_magic_kcalloc(cs4231_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; chip->hardware = hardware; @@ -1626,7 +1624,7 @@ static snd_pcm_ops_t snd_cs4231_capture_ static void snd_cs4231_pcm_free(snd_pcm_t *pcm) { - cs4231_t *chip = snd_magic_cast(cs4231_t, pcm->private_data, return); + cs4231_t *chip = pcm->private_data; chip->pcm = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -1686,7 +1684,7 @@ int snd_cs4231_pcm(cs4231_t *chip, int d static void snd_cs4231_timer_free(snd_timer_t *timer) { - cs4231_t *chip = snd_magic_cast(cs4231_t, timer->private_data, return); + cs4231_t *chip = timer->private_data; chip->timer = NULL; } @@ -1898,8 +1896,6 @@ int snd_cs4231_put_double(snd_kcontrol_t return change; } -#define CS4231_CONTROLS (sizeof(snd_cs4231_controls)/sizeof(snd_kcontrol_new_t)) - static snd_kcontrol_new_t snd_cs4231_controls[] = { CS4231_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), CS4231_DOUBLE("PCM Playback Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1), @@ -1938,7 +1934,7 @@ int snd_cs4231_mixer(cs4231_t *chip) strcpy(card->mixername, chip->pcm->name); - for (idx = 0; idx < CS4231_CONTROLS; idx++) { + for (idx = 0; idx < ARRAY_SIZE(snd_cs4231_controls); idx++) { if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4231_controls[idx], chip))) < 0) return err; } --- linux-2.6.9-rc1/sound/isa/cs423x/cs4236.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/sound/isa/cs423x/cs4236.c 2004-09-02 20:51:53.000000000 -0700 @@ -30,14 +30,11 @@ #include #include -#define chip_t cs4231_t - MODULE_AUTHOR("Jaroslav Kysela "); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); #ifdef CS4232 MODULE_DESCRIPTION("Cirrus Logic CS4232"); -MODULE_DEVICES("{{Turtle Beach,TBS-2000}," +MODULE_SUPPORTED_DEVICE("{{Turtle Beach,TBS-2000}," "{Turtle Beach,Tropez Plus}," "{SIC CrystalWave 32}," "{Hewlett Packard,Omnibook 5500}," @@ -45,7 +42,7 @@ MODULE_DEVICES("{{Turtle Beach,TBS-2000} "{Philips,PCA70PS}}"); #else MODULE_DESCRIPTION("Cirrus Logic CS4235-9"); -MODULE_DEVICES("{{Crystal Semiconductors,CS4235}," +MODULE_SUPPORTED_DEVICE("{{Crystal Semiconductors,CS4235}," "{Crystal Semiconductors,CS4236}," "{Crystal Semiconductors,CS4237}," "{Crystal Semiconductors,CS4238}," @@ -99,45 +96,32 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for " IDENT " soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for " IDENT " soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable " IDENT " soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); #ifdef CONFIG_PNP module_param_array(isapnp, bool, boot_devs, 0444); MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard."); -MODULE_PARM_SYNTAX(isapnp, SNDRV_ISAPNP_DESC); #endif module_param_array(port, long, boot_devs, 0444); MODULE_PARM_DESC(port, "Port # for " IDENT " driver."); -MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); module_param_array(cport, long, boot_devs, 0444); MODULE_PARM_DESC(cport, "Control port # for " IDENT " driver."); -MODULE_PARM_SYNTAX(cport, SNDRV_PORT12_DESC); module_param_array(mpu_port, long, boot_devs, 0444); MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " IDENT " driver."); -MODULE_PARM_SYNTAX(mpu_port, SNDRV_PORT12_DESC); module_param_array(fm_port, long, boot_devs, 0444); MODULE_PARM_DESC(fm_port, "FM port # for " IDENT " driver."); -MODULE_PARM_SYNTAX(fm_port, SNDRV_PORT12_DESC); module_param_array(sb_port, long, boot_devs, 0444); MODULE_PARM_DESC(sb_port, "SB port # for " IDENT " driver (optional)."); -MODULE_PARM_SYNTAX(sb_port, SNDRV_PORT12_DESC); module_param_array(irq, int, boot_devs, 0444); MODULE_PARM_DESC(irq, "IRQ # for " IDENT " driver."); -MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); module_param_array(mpu_irq, int, boot_devs, 0444); MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " IDENT " driver."); -MODULE_PARM_SYNTAX(mpu_irq, SNDRV_IRQ_DESC); module_param_array(dma1, int, boot_devs, 0444); MODULE_PARM_DESC(dma1, "DMA1 # for " IDENT " driver."); -MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC); module_param_array(dma2, int, boot_devs, 0444); MODULE_PARM_DESC(dma2, "DMA2 # for " IDENT " driver."); -MODULE_PARM_SYNTAX(dma2, SNDRV_DMA_DESC); struct snd_card_cs4236 { struct resource *res_sb_port; --- linux-2.6.9-rc1/sound/isa/cs423x/cs4236_lib.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/sound/isa/cs423x/cs4236_lib.c 2004-09-02 20:51:53.000000000 -0700 @@ -93,8 +93,6 @@ MODULE_AUTHOR("Jaroslav Kysela hardware == CS4231_HW_CS4235 || chip->hardware == CS4231_HW_CS4239) { - for (idx = 0; idx < CS4235_CONTROLS; idx++) { + for (idx = 0; idx < ARRAY_SIZE(snd_cs4235_controls); idx++) { if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4235_controls[idx], chip))) < 0) return err; } } else { - for (idx = 0; idx < CS4236_CONTROLS; idx++) { + for (idx = 0; idx < ARRAY_SIZE(snd_cs4236_controls); idx++) { if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_controls[idx], chip))) < 0) return err; } @@ -934,15 +920,15 @@ int snd_cs4236_mixer(cs4231_t *chip) switch (chip->hardware) { case CS4231_HW_CS4235: case CS4231_HW_CS4239: - count = CS4236_3D_CONTROLS_CS4235; + count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4235); kcontrol = snd_cs4236_3d_controls_cs4235; break; case CS4231_HW_CS4237B: - count = CS4236_3D_CONTROLS_CS4237; + count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4237); kcontrol = snd_cs4236_3d_controls_cs4237; break; case CS4231_HW_CS4238B: - count = CS4236_3D_CONTROLS_CS4238; + count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4238); kcontrol = snd_cs4236_3d_controls_cs4238; break; default: @@ -955,7 +941,7 @@ int snd_cs4236_mixer(cs4231_t *chip) } if (chip->hardware == CS4231_HW_CS4237B || chip->hardware == CS4231_HW_CS4238B) { - for (idx = 0; idx < CS4236_IEC958_CONTROLS; idx++) { + for (idx = 0; idx < ARRAY_SIZE(snd_cs4236_iec958_controls); idx++) { if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_iec958_controls[idx], chip))) < 0) return err; } --- linux-2.6.9-rc1/sound/isa/dt019x.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/sound/isa/dt019x.c 2004-09-02 20:51:53.000000000 -0700 @@ -33,15 +33,12 @@ #include #include -#define chip_t sb_t - #define PFX "dt019x: " MODULE_AUTHOR("Massimo Piccioni "); MODULE_DESCRIPTION("Diamond Technologies DT-019X / Avance Logic ALS-007"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Diamond Technologies DT-019X}," +MODULE_SUPPORTED_DEVICE("{{Diamond Technologies DT-019X}," "{Avance Logic ALS-007}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ @@ -57,31 +54,22 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for DT-019X based soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for DT-019X based soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable DT-019X based soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(port, long, boot_devs, 0444); MODULE_PARM_DESC(port, "Port # for dt019x driver."); -MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); module_param_array(mpu_port, long, boot_devs, 0444); MODULE_PARM_DESC(mpu_port, "MPU-401 port # for dt019x driver."); -MODULE_PARM_SYNTAX(mpu_port, SNDRV_PORT12_DESC); module_param_array(fm_port, long, boot_devs, 0444); MODULE_PARM_DESC(fm_port, "FM port # for dt019x driver."); -MODULE_PARM_SYNTAX(fm_port, SNDRV_PORT12_DESC); module_param_array(irq, int, boot_devs, 0444); MODULE_PARM_DESC(irq, "IRQ # for dt019x driver."); -MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); module_param_array(mpu_irq, int, boot_devs, 0444); MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for dt019x driver."); -MODULE_PARM_SYNTAX(mpu_irq, SNDRV_IRQ_DESC); module_param_array(dma8, int, boot_devs, 0444); MODULE_PARM_DESC(dma8, "8-bit DMA # for dt019x driver."); -MODULE_PARM_SYNTAX(dma8, SNDRV_DMA8_DESC); struct snd_card_dt019x { struct pnp_dev *dev; --- linux-2.6.9-rc1/sound/isa/es1688/es1688.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/sound/isa/es1688/es1688.c 2004-09-02 20:51:53.000000000 -0700 @@ -37,8 +37,7 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("ESS ESx688 AudioDrive"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{ESS,ES688 PnP AudioDrive,pnp:ESS0100}," +MODULE_SUPPORTED_DEVICE("{{ESS,ES688 PnP AudioDrive,pnp:ESS0100}," "{ESS,ES1688 PnP AudioDrive,pnp:ESS0102}," "{ESS,ES688 AudioDrive,pnp:ESS6881}," "{ESS,ES1688 AudioDrive,pnp:ESS1681}}"); @@ -55,28 +54,20 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for ESx688 soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for ESx688 soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable ESx688 soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(port, long, boot_devs, 0444); MODULE_PARM_DESC(port, "Port # for ESx688 driver."); -MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); module_param_array(mpu_port, long, boot_devs, 0444); MODULE_PARM_DESC(mpu_port, "MPU-401 port # for ESx688 driver."); -MODULE_PARM_SYNTAX(mpu_port, SNDRV_PORT12_DESC); module_param_array(irq, int, boot_devs, 0444); MODULE_PARM_DESC(irq, "IRQ # for ESx688 driver."); -MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); module_param_array(mpu_irq, int, boot_devs, 0444); MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for ESx688 driver."); -MODULE_PARM_SYNTAX(mpu_irq, SNDRV_IRQ_DESC); module_param_array(dma8, int, boot_devs, 0444); MODULE_PARM_DESC(dma8, "8-bit DMA # for ESx688 driver."); -MODULE_PARM_SYNTAX(dma8, SNDRV_DMA8_DESC); static snd_card_t *snd_audiodrive_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; --- linux-2.6.9-rc1/sound/isa/es1688/es1688_lib.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/sound/isa/es1688/es1688_lib.c 2004-09-02 20:51:53.000000000 -0700 @@ -34,7 +34,6 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("ESS ESx688 lowlevel module"); -MODULE_CLASSES("{sound}"); MODULE_LICENSE("GPL"); static int snd_es1688_dsp_command(es1688_t *chip, unsigned char val) @@ -482,7 +481,7 @@ static int snd_es1688_capture_trigger(sn irqreturn_t snd_es1688_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - es1688_t *chip = snd_magic_cast(es1688_t, dev_id, return IRQ_NONE); + es1688_t *chip = dev_id; if (chip->trigger_value == 0x05) /* ok.. playback is active */ snd_pcm_period_elapsed(chip->playback_substream); @@ -616,13 +615,13 @@ static int snd_es1688_free(es1688_t *chi disable_dma(chip->dma8); free_dma(chip->dma8); } - snd_magic_kfree(chip); + kfree(chip); return 0; } static int snd_es1688_dev_free(snd_device_t *device) { - es1688_t *chip = snd_magic_cast(es1688_t, device->device_data, return -ENXIO); + es1688_t *chip = device->device_data; return snd_es1688_free(chip); } @@ -650,7 +649,7 @@ int snd_es1688_create(snd_card_t * card, int err; *rchip = NULL; - chip = snd_magic_kcalloc(es1688_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; chip->irq = -1; @@ -728,7 +727,7 @@ static snd_pcm_ops_t snd_es1688_capture_ static void snd_es1688_pcm_free(snd_pcm_t *pcm) { - es1688_t *chip = snd_magic_cast(es1688_t, pcm->private_data, return); + es1688_t *chip = pcm->private_data; chip->pcm = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -978,8 +977,6 @@ static int snd_es1688_put_double(snd_kco return change; } -#define ES1688_CONTROLS (sizeof(snd_es1688_controls)/sizeof(snd_kcontrol_new_t)) - static snd_kcontrol_new_t snd_es1688_controls[] = { ES1688_DOUBLE("Master Playback Volume", 0, ES1688_MASTER_DEV, ES1688_MASTER_DEV, 4, 0, 15, 0), ES1688_DOUBLE("PCM Playback Volume", 0, ES1688_PCM_DEV, ES1688_PCM_DEV, 4, 0, 15, 0), @@ -1028,7 +1025,7 @@ int snd_es1688_mixer(es1688_t *chip) strcpy(card->mixername, snd_es1688_chip_id(chip)); - for (idx = 0; idx < ES1688_CONTROLS; idx++) { + for (idx = 0; idx < ARRAY_SIZE(snd_es1688_controls); idx++) { if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_es1688_controls[idx], chip))) < 0) return err; } --- linux-2.6.9-rc1/sound/isa/es18xx.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/sound/isa/es18xx.c 2004-09-02 20:51:53.000000000 -0700 @@ -157,8 +157,6 @@ struct _snd_es18xx { typedef struct _snd_es18xx es18xx_t; -#define chip_t es18xx_t - /* Lowlevel */ #define DAC1 0x01 @@ -728,7 +726,7 @@ static int snd_es18xx_playback_trigger(s static irqreturn_t snd_es18xx_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - es18xx_t *chip = snd_magic_cast(es18xx_t, dev_id, return IRQ_NONE); + es18xx_t *chip = dev_id; unsigned char status; if (chip->caps & ES18XX_CONTROL) { @@ -1027,7 +1025,7 @@ static int snd_es18xx_get_hw_switch(snd_ static void snd_es18xx_hwv_free(snd_kcontrol_t *kcontrol) { - es18xx_t *chip = snd_magic_cast(es18xx_t, _snd_kcontrol_chip(kcontrol), return); + es18xx_t *chip = snd_kcontrol_chip(kcontrol); chip->master_volume = NULL; chip->master_switch = NULL; chip->hw_volume = NULL; @@ -1561,7 +1559,7 @@ static snd_pcm_ops_t snd_es18xx_capture_ static void snd_es18xx_pcm_free(snd_pcm_t *pcm) { - es18xx_t *codec = snd_magic_cast(es18xx_t, pcm->private_data, return); + es18xx_t *codec = pcm->private_data; codec->pcm = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -1611,7 +1609,7 @@ int __devinit snd_es18xx_pcm(es18xx_t *c #ifdef CONFIG_PM static int snd_es18xx_suspend(snd_card_t *card, unsigned int state) { - es18xx_t *chip = snd_magic_cast(es18xx_t, card->pm_private_data, return -EINVAL); + es18xx_t *chip = card->pm_private_data; snd_pcm_suspend_all(chip->pcm); @@ -1627,7 +1625,7 @@ static int snd_es18xx_suspend(snd_card_t static int snd_es18xx_resume(snd_card_t *card, unsigned int state) { - es18xx_t *chip = snd_magic_cast(es18xx_t, card->pm_private_data, return -EINVAL); + es18xx_t *chip = card->pm_private_data; /* restore PM register, we won't wake till (not 0x07) i/o activity though */ snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_FM); @@ -1661,13 +1659,13 @@ static int snd_es18xx_free(es18xx_t *chi disable_dma(chip->dma2); free_dma(chip->dma2); } - snd_magic_kfree(chip); + kfree(chip); return 0; } static int snd_es18xx_dev_free(snd_device_t *device) { - es18xx_t *chip = snd_magic_cast(es18xx_t, device->device_data, return -ENXIO); + es18xx_t *chip = device->device_data; return snd_es18xx_free(chip); } @@ -1685,7 +1683,7 @@ static int __devinit snd_es18xx_new_devi int err; *rchip = NULL; - chip = snd_magic_kcalloc(es18xx_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; spin_lock_init(&chip->reg_lock); @@ -1830,8 +1828,7 @@ static int __devinit snd_es18xx_mixer(es MODULE_AUTHOR("Christian Fischbach , Abramo Bagnara "); MODULE_DESCRIPTION("ESS ES18xx AudioDrive"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{ESS,ES1868 PnP AudioDrive}," +MODULE_SUPPORTED_DEVICE("{{ESS,ES1868 PnP AudioDrive}," "{ESS,ES1869 PnP AudioDrive}," "{ESS,ES1878 PnP AudioDrive}," "{ESS,ES1879 PnP AudioDrive}," @@ -1860,36 +1857,26 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for ES18xx soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for ES18xx soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable ES18xx soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); #ifdef CONFIG_PNP module_param_array(isapnp, bool, boot_devs, 0444); MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard."); -MODULE_PARM_SYNTAX(isapnp, SNDRV_ISAPNP_DESC); #endif module_param_array(port, long, boot_devs, 0444); MODULE_PARM_DESC(port, "Port # for ES18xx driver."); -MODULE_PARM_SYNTAX(port, SNDRV_ENABLED ",allows:{{0x220,0x280,0x20}},prefers:{0x220},base:16,dialog:list"); module_param_array(mpu_port, long, boot_devs, 0444); MODULE_PARM_DESC(mpu_port, "MPU-401 port # for ES18xx driver."); -MODULE_PARM_SYNTAX(mpu_port, SNDRV_ENABLED ",allows:{{0x300,0x330,0x30},{0x800,0xffe,0x2}},prefers:{0x330,0x300},base:16,dialog:combo"); module_param_array(fm_port, long, boot_devs, 0444); MODULE_PARM_DESC(fm_port, "FM port # for ES18xx driver."); -MODULE_PARM_SYNTAX(fm_port, SNDRV_ENABLED ",allows:{{0x388},{0x800,0xffc,0x4}},prefers:{0x388},base:16,dialog:combo"); module_param_array(irq, int, boot_devs, 0444); MODULE_PARM_DESC(irq, "IRQ # for ES18xx driver."); -MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC ",prefers:{5}"); module_param_array(dma1, int, boot_devs, 0444); MODULE_PARM_DESC(dma1, "DMA 1 # for ES18xx driver."); -MODULE_PARM_SYNTAX(dma1, SNDRV_DMA8_DESC ",prefers:{1}"); module_param_array(dma2, int, boot_devs, 0444); MODULE_PARM_DESC(dma2, "DMA 2 # for ES18xx driver."); -MODULE_PARM_SYNTAX(dma2, SNDRV_ENABLED ",allows:{{0},{1},{3},{5}},dialog:list,prefers:{0}"); struct snd_audiodrive { #ifdef CONFIG_PNP --- linux-2.6.9-rc1/sound/isa/gus/gusclassic.c 2004-08-24 00:03:49.000000000 -0700 +++ 25/sound/isa/gus/gusclassic.c 2004-09-02 20:51:53.000000000 -0700 @@ -35,8 +35,7 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Gravis UltraSound Classic"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Gravis,UltraSound Classic}}"); +MODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound Classic}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ @@ -53,34 +52,24 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for GUS Classic soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for GUS Classic soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable GUS Classic soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(port, long, boot_devs, 0444); MODULE_PARM_DESC(port, "Port # for GUS Classic driver."); -MODULE_PARM_SYNTAX(port, SNDRV_ENABLED ",allows:{{0x220,0x260,0x10}},dialog:list"); module_param_array(irq, int, boot_devs, 0444); MODULE_PARM_DESC(irq, "IRQ # for GUS Classic driver."); -MODULE_PARM_SYNTAX(irq, SNDRV_ENABLED ",allows:{{3},{5},{9},{11},{12},{15}},dialog:list"); module_param_array(dma1, int, boot_devs, 0444); MODULE_PARM_DESC(dma1, "DMA1 # for GUS Classic driver."); -MODULE_PARM_SYNTAX(dma1, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list"); module_param_array(dma2, int, boot_devs, 0444); MODULE_PARM_DESC(dma2, "DMA2 # for GUS Classic driver."); -MODULE_PARM_SYNTAX(dma2, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list"); module_param_array(joystick_dac, int, boot_devs, 0444); MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS Classic driver."); -MODULE_PARM_SYNTAX(joystick_dac, SNDRV_ENABLED ",allows:{{0,31}}"); module_param_array(channels, int, boot_devs, 0444); MODULE_PARM_DESC(channels, "GF1 channels for GUS Classic driver."); -MODULE_PARM_SYNTAX(channels, SNDRV_ENABLED ",allows:{{14,32}}"); module_param_array(pcm_channels, int, boot_devs, 0444); MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for GUS Classic driver."); -MODULE_PARM_SYNTAX(pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}"); static snd_card_t *snd_gusclassic_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; --- linux-2.6.9-rc1/sound/isa/gus/gus_dram.c 2004-08-24 00:03:49.000000000 -0700 +++ 25/sound/isa/gus/gus_dram.c 2004-09-02 20:51:53.000000000 -0700 @@ -31,12 +31,12 @@ static int snd_gus_dram_poke(snd_gus_car { unsigned long flags; unsigned int size1, size2; - char buffer[512], *pbuffer; + char buffer[256], *pbuffer; while (size > 0) { - if (copy_from_user(buffer, _buffer, 512)) + size1 = size > sizeof(buffer) ? sizeof(buffer) : size; + if (copy_from_user(buffer, _buffer, size1)) return -EFAULT; - size1 = size > 512 ? 512 : size; if (gus->interwave) { spin_lock_irqsave(&gus->reg_lock, flags); snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01); @@ -69,10 +69,10 @@ static int snd_gus_dram_peek(snd_gus_car { unsigned long flags; unsigned int size1, size2; - char buffer[512], *pbuffer; + char buffer[256], *pbuffer; while (size > 0) { - size1 = size > 512 ? 512 : size; + size1 = size > sizeof(buffer) ? sizeof(buffer) : size; if (gus->interwave) { spin_lock_irqsave(&gus->reg_lock, flags); snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, rom ? 0x03 : 0x01); --- linux-2.6.9-rc1/sound/isa/gus/gusextreme.c 2004-08-24 00:03:17.000000000 -0700 +++ 25/sound/isa/gus/gusextreme.c 2004-09-02 20:51:53.000000000 -0700 @@ -38,8 +38,7 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Gravis UltraSound Extreme"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Gravis,UltraSound Extreme}}"); +MODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound Extreme}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ @@ -60,46 +59,32 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for GUS Extreme soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for GUS Extreme soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable GUS Extreme soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(port, long, boot_devs, 0444); MODULE_PARM_DESC(port, "Port # for GUS Extreme driver."); -MODULE_PARM_SYNTAX(port, SNDRV_ENABLED ",allows:{{0x220,0x260,0x20}},dialog:list"); module_param_array(gf1_port, long, boot_devs, 0444); MODULE_PARM_DESC(gf1_port, "GF1 port # for GUS Extreme driver (optional)."); -MODULE_PARM_SYNTAX(gf1_port, SNDRV_ENABLED ",allows:{{0x210,0x270,0x10}},dialog:list"); module_param_array(mpu_port, long, boot_devs, 0444); MODULE_PARM_DESC(mpu_port, "MPU-401 port # for GUS Extreme driver."); -MODULE_PARM_SYNTAX(mpu_port, SNDRV_ENABLED ",allows:{{0x300,0x320,0x10}},dialog:list"); module_param_array(irq, int, boot_devs, 0444); MODULE_PARM_DESC(irq, "IRQ # for GUS Extreme driver."); -MODULE_PARM_SYNTAX(irq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10}},dialog:list"); module_param_array(mpu_irq, int, boot_devs, 0444); MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for GUS Extreme driver."); -MODULE_PARM_SYNTAX(mpu_irq, SNDRV_ENABLED ",allows:{{5},{7},{9},{10}},dialog:list"); module_param_array(gf1_irq, int, boot_devs, 0444); MODULE_PARM_DESC(gf1_irq, "GF1 IRQ # for GUS Extreme driver."); -MODULE_PARM_SYNTAX(gf1_irq, SNDRV_ENABLED ",allows:{{2},{3},{5},{9},{11},{12},{15}},dialog:list"); module_param_array(dma8, int, boot_devs, 0444); MODULE_PARM_DESC(dma8, "8-bit DMA # for GUS Extreme driver."); -MODULE_PARM_SYNTAX(dma8, SNDRV_DMA8_DESC); module_param_array(dma1, int, boot_devs, 0444); MODULE_PARM_DESC(dma1, "GF1 DMA # for GUS Extreme driver."); -MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC); module_param_array(joystick_dac, int, boot_devs, 0444); MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS Extreme driver."); -MODULE_PARM_SYNTAX(joystick_dac, SNDRV_ENABLED ",allows:{{0,31}}"); module_param_array(channels, int, boot_devs, 0444); MODULE_PARM_DESC(channels, "GF1 channels for GUS Extreme driver."); -MODULE_PARM_SYNTAX(channels, SNDRV_ENABLED ",allows:{{14,32}}"); module_param_array(pcm_channels, int, boot_devs, 0444); MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for GUS Extreme driver."); -MODULE_PARM_SYNTAX(pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}"); static snd_card_t *snd_gusextreme_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; --- linux-2.6.9-rc1/sound/isa/gus/gus_instr.c 2004-08-24 00:03:49.000000000 -0700 +++ 25/sound/isa/gus/gus_instr.c 2004-09-02 20:51:53.000000000 -0700 @@ -31,7 +31,7 @@ int snd_gus_iwffff_put_sample(void *private_data, iwffff_wave_t *wave, char __user *data, long len, int atomic) { - snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + snd_gus_card_t *gus = private_data; snd_gf1_mem_block_t *block; int err; @@ -61,7 +61,7 @@ int snd_gus_iwffff_put_sample(void *priv int snd_gus_iwffff_get_sample(void *private_data, iwffff_wave_t *wave, char __user *data, long len, int atomic) { - snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + snd_gus_card_t *gus = private_data; return snd_gus_dram_read(gus, data, wave->address.memory, wave->size, wave->format & IWFFFF_WAVE_ROM ? 1 : 0); @@ -70,7 +70,7 @@ int snd_gus_iwffff_get_sample(void *priv int snd_gus_iwffff_remove_sample(void *private_data, iwffff_wave_t *wave, int atomic) { - snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + snd_gus_card_t *gus = private_data; if (wave->format & IWFFFF_WAVE_ROM) return 0; /* it's probably ok - verify the address? */ @@ -84,7 +84,7 @@ int snd_gus_iwffff_remove_sample(void *p int snd_gus_gf1_put_sample(void *private_data, gf1_wave_t *wave, char __user *data, long len, int atomic) { - snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + snd_gus_card_t *gus = private_data; snd_gf1_mem_block_t *block; int err; @@ -112,7 +112,7 @@ int snd_gus_gf1_put_sample(void *private int snd_gus_gf1_get_sample(void *private_data, gf1_wave_t *wave, char __user *data, long len, int atomic) { - snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + snd_gus_card_t *gus = private_data; return snd_gus_dram_read(gus, data, wave->address.memory, wave->size, 0); } @@ -120,7 +120,7 @@ int snd_gus_gf1_get_sample(void *private int snd_gus_gf1_remove_sample(void *private_data, gf1_wave_t *wave, int atomic) { - snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + snd_gus_card_t *gus = private_data; return snd_gf1_mem_free(&gus->gf1.mem_alloc, wave->address.memory); } @@ -132,7 +132,7 @@ int snd_gus_gf1_remove_sample(void *priv int snd_gus_simple_put_sample(void *private_data, simple_instrument_t *instr, char __user *data, long len, int atomic) { - snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + snd_gus_card_t *gus = private_data; snd_gf1_mem_block_t *block; int err; @@ -159,7 +159,7 @@ int snd_gus_simple_put_sample(void *priv int snd_gus_simple_get_sample(void *private_data, simple_instrument_t *instr, char __user *data, long len, int atomic) { - snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + snd_gus_card_t *gus = private_data; return snd_gus_dram_read(gus, data, instr->address.memory, instr->size, 0); } @@ -167,7 +167,7 @@ int snd_gus_simple_get_sample(void *priv int snd_gus_simple_remove_sample(void *private_data, simple_instrument_t *instr, int atomic) { - snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return -ENXIO); + snd_gus_card_t *gus = private_data; return snd_gf1_mem_free(&gus->gf1.mem_alloc, instr->address.memory); } --- linux-2.6.9-rc1/sound/isa/gus/gus_irq.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/sound/isa/gus/gus_irq.c 2004-09-02 20:51:53.000000000 -0700 @@ -32,7 +32,7 @@ irqreturn_t snd_gus_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - snd_gus_card_t * gus = snd_magic_cast(snd_gus_card_t, dev_id, return IRQ_NONE); + snd_gus_card_t * gus = dev_id; unsigned char status; int loop = 100; int handled = 0; @@ -114,7 +114,7 @@ static void snd_gus_irq_info_read(snd_in snd_gus_voice_t *pvoice; int idx; - gus = snd_magic_cast(snd_gus_card_t, entry->private_data, return); + gus = entry->private_data; snd_iprintf(buffer, "midi out = %u\n", gus->gf1.interrupt_stat_midi_out); snd_iprintf(buffer, "midi in = %u\n", gus->gf1.interrupt_stat_midi_in); snd_iprintf(buffer, "timer1 = %u\n", gus->gf1.interrupt_stat_timer1); --- linux-2.6.9-rc1/sound/isa/gus/gus_main.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/sound/isa/gus/gus_main.c 2004-09-02 20:51:53.000000000 -0700 @@ -35,8 +35,6 @@ MODULE_AUTHOR("Jaroslav Kysela gf1.dma2); free_dma(gus->gf1.dma2); } - snd_magic_kfree(gus); + kfree(gus); return 0; } static int snd_gus_dev_free(snd_device_t *device) { - snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, device->device_data, return -ENXIO); + snd_gus_card_t *gus = device->device_data; return snd_gus_free(gus); } @@ -159,7 +157,7 @@ int snd_gus_create(snd_card_t * card, }; *rgus = NULL; - gus = snd_magic_kcalloc(snd_gus_card_t, 0, GFP_KERNEL); + gus = kcalloc(1, sizeof(*gus), GFP_KERNEL); if (gus == NULL) return -ENOMEM; gus->gf1.irq = -1; @@ -421,7 +419,7 @@ static int snd_gus_check_version(snd_gus static void snd_gus_seq_dev_free(snd_seq_device_t *seq_dev) { - snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, seq_dev->private_data, return); + snd_gus_card_t *gus = seq_dev->private_data; gus->seq_dev = NULL; } --- linux-2.6.9-rc1/sound/isa/gus/gusmax.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/sound/isa/gus/gusmax.c 2004-09-02 20:51:53.000000000 -0700 @@ -36,8 +36,7 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Gravis UltraSound MAX"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Gravis,UltraSound MAX}}"); +MODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound MAX}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ @@ -54,34 +53,24 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for GUS MAX soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for GUS MAX soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable GUS MAX soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(port, long, boot_devs, 0444); MODULE_PARM_DESC(port, "Port # for GUS MAX driver."); -MODULE_PARM_SYNTAX(port, SNDRV_ENABLED ",allows:{{0x220},{0x230},{0x240},{0x250},{0x260}},dialog:list"); module_param_array(irq, int, boot_devs, 0444); MODULE_PARM_DESC(irq, "IRQ # for GUS MAX driver."); -MODULE_PARM_SYNTAX(irq, SNDRV_ENABLED ",allows:{{3},{5},{9},{11},{12},{15}},dialog:list"); module_param_array(dma1, int, boot_devs, 0444); MODULE_PARM_DESC(dma1, "DMA1 # for GUS MAX driver."); -MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC); module_param_array(dma2, int, boot_devs, 0444); MODULE_PARM_DESC(dma2, "DMA2 # for GUS MAX driver."); -MODULE_PARM_SYNTAX(dma2, SNDRV_DMA_DESC); module_param_array(joystick_dac, int, boot_devs, 0444); MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS MAX driver."); -MODULE_PARM_SYNTAX(joystick_dac, SNDRV_ENABLED ",allows:{{0,31}}"); module_param_array(channels, int, boot_devs, 0444); MODULE_PARM_DESC(channels, "Used GF1 channels for GUS MAX driver."); -MODULE_PARM_SYNTAX(channels, SNDRV_ENABLED ",allows:{{14,32}}"); module_param_array(pcm_channels, int, boot_devs, 0444); MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for GUS MAX driver."); -MODULE_PARM_SYNTAX(pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}"); struct snd_gusmax { int irq; --- linux-2.6.9-rc1/sound/isa/gus/gus_mem.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/sound/isa/gus/gus_mem.c 2004-09-02 20:51:53.000000000 -0700 @@ -297,7 +297,7 @@ static void snd_gf1_mem_info_read(snd_in unsigned int total, used; int i; - gus = snd_magic_cast(snd_gus_card_t, entry->private_data, return); + gus = entry->private_data; alloc = &gus->gf1.mem_alloc; down(&alloc->memory_mutex); snd_iprintf(buffer, "8-bit banks : \n "); --- linux-2.6.9-rc1/sound/isa/gus/gus_mem_proc.c 2004-08-24 00:03:12.000000000 -0700 +++ 25/sound/isa/gus/gus_mem_proc.c 2004-09-02 20:51:53.000000000 -0700 @@ -37,7 +37,7 @@ static long snd_gf1_mem_proc_dump(snd_in unsigned long count, unsigned long pos) { long size; - gus_proc_private_t *priv = snd_magic_cast(gus_proc_private_t, entry->private_data, return -ENXIO); + gus_proc_private_t *priv = entry->private_data; snd_gus_card_t *gus = priv->gus; int err; @@ -58,7 +58,7 @@ static long long snd_gf1_mem_proc_llseek long long offset, int orig) { - gus_proc_private_t *priv = snd_magic_cast(gus_proc_private_t, entry->private_data, return -ENXIO); + gus_proc_private_t *priv = entry->private_data; switch (orig) { case 0: /* SEEK_SET */ @@ -80,8 +80,8 @@ static long long snd_gf1_mem_proc_llseek static void snd_gf1_mem_proc_free(snd_info_entry_t *entry) { - gus_proc_private_t *priv = snd_magic_cast(gus_proc_private_t, entry->private_data, return); - snd_magic_kfree(priv); + gus_proc_private_t *priv = entry->private_data; + kfree(priv); } static struct snd_info_entry_ops snd_gf1_mem_proc_ops = { @@ -98,7 +98,7 @@ int snd_gf1_mem_proc_init(snd_gus_card_t for (idx = 0; idx < 4; idx++) { if (gus->gf1.mem_alloc.banks_8[idx].size > 0) { - priv = snd_magic_kcalloc(gus_proc_private_t, 0, GFP_KERNEL); + priv = kcalloc(1, sizeof(*priv), GFP_KERNEL); if (priv == NULL) return -ENOMEM; priv->gus = gus; @@ -115,7 +115,7 @@ int snd_gf1_mem_proc_init(snd_gus_card_t } for (idx = 0; idx < 4; idx++) { if (gus->gf1.rom_present & (1 << idx)) { - priv = snd_magic_kcalloc(gus_proc_private_t, 0, GFP_KERNEL); + priv = kcalloc(1, sizeof(*priv), GFP_KERNEL); if (priv == NULL) return -ENOMEM; priv->rom = 1; --- linux-2.6.9-rc1/sound/isa/gus/gus_mixer.c 2004-08-24 00:01:53.000000000 -0700 +++ 25/sound/isa/gus/gus_mixer.c 2004-09-02 20:51:53.000000000 -0700 @@ -26,8 +26,6 @@ #include #include -#define chip_t snd_gus_card_t - /* * */ @@ -148,16 +146,12 @@ static int snd_ics_put_double(snd_kcontr return change; } -#define GF1_CONTROLS (sizeof(snd_gf1_controls)/sizeof(snd_kcontrol_new_t)) - static snd_kcontrol_new_t snd_gf1_controls[] = { GF1_SINGLE("Master Playback Switch", 0, 1, 1), GF1_SINGLE("Line Switch", 0, 0, 1), GF1_SINGLE("Mic Switch", 0, 2, 0) }; -#define ICS_CONTROLS (sizeof(snd_ics_controls)/sizeof(snd_kcontrol_new_t)) - static snd_kcontrol_new_t snd_ics_controls[] = { GF1_SINGLE("Master Playback Switch", 0, 1, 1), ICS_DOUBLE("Master Playback Volume", 0, SNDRV_ICS_MASTER_DEV), @@ -190,13 +184,13 @@ int snd_gf1_new_mixer(snd_gus_card_t * g } if (!gus->ics_flag) { - max = gus->ess_flag ? 1 : GF1_CONTROLS; + max = gus->ess_flag ? 1 : ARRAY_SIZE(snd_gf1_controls); for (idx = 0; idx < max; idx++) { if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_gf1_controls[idx], gus))) < 0) return err; } } else { - for (idx = 0; idx < ICS_CONTROLS; idx++) { + for (idx = 0; idx < ARRAY_SIZE(snd_ics_controls); idx++) { if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ics_controls[idx], gus))) < 0) return err; } --- linux-2.6.9-rc1/sound/isa/gus/gus_pcm.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/sound/isa/gus/gus_pcm.c 2004-09-02 20:51:53.000000000 -0700 @@ -34,8 +34,6 @@ #include #include "gus_tables.h" -#define chip_t snd_gus_card_t - /* maximum rate */ #define SNDRV_GF1_PCM_RATE 48000 @@ -66,7 +64,7 @@ static int snd_gf1_pcm_use_dma = 1; static void snd_gf1_pcm_block_change_ack(snd_gus_card_t * gus, void *private_data) { - gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, private_data, return); + gus_pcm_private_t *pcmp = private_data; if (pcmp) { atomic_dec(&pcmp->dma_count); @@ -81,7 +79,7 @@ static int snd_gf1_pcm_block_change(snd_ { snd_gf1_dma_block_t block; snd_pcm_runtime_t *runtime = substream->runtime; - gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + gus_pcm_private_t *pcmp = runtime->private_data; count += offset & 31; offset &= ~31; @@ -106,7 +104,7 @@ static int snd_gf1_pcm_block_change(snd_ static void snd_gf1_pcm_trigger_up(snd_pcm_substream_t * substream) { snd_pcm_runtime_t *runtime = substream->runtime; - gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return); + gus_pcm_private_t *pcmp = runtime->private_data; snd_gus_card_t * gus = pcmp->gus; unsigned long flags; unsigned char voice_ctrl, ramp_ctrl; @@ -194,7 +192,7 @@ static void snd_gf1_pcm_interrupt_wave(s snd_gf1_smart_stop_voice(gus, pvoice->number); return; } - pcmp = snd_magic_cast(gus_pcm_private_t, pvoice->private_data, return); + pcmp = pvoice->private_data; if (pcmp == NULL) { snd_printd("snd_gf1_pcm: unknown wave irq?\n"); snd_gf1_smart_stop_voice(gus, pvoice->number); @@ -267,7 +265,7 @@ static void snd_gf1_pcm_interrupt_volume { unsigned short vol; int cvoice; - gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, pvoice->private_data, return); + gus_pcm_private_t *pcmp = pvoice->private_data; /* stop ramp, but leave rollover bit untouched */ spin_lock(&gus->reg_lock); @@ -350,7 +348,7 @@ static int snd_gf1_pcm_playback_copy(snd snd_pcm_uframes_t count) { snd_pcm_runtime_t *runtime = substream->runtime; - gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + gus_pcm_private_t *pcmp = runtime->private_data; unsigned int bpos, len; bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2)); @@ -379,7 +377,7 @@ static int snd_gf1_pcm_playback_silence( snd_pcm_uframes_t count) { snd_pcm_runtime_t *runtime = substream->runtime; - gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + gus_pcm_private_t *pcmp = runtime->private_data; unsigned int bpos, len; bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2)); @@ -406,7 +404,7 @@ static int snd_gf1_pcm_playback_hw_param { snd_gus_card_t *gus = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + gus_pcm_private_t *pcmp = runtime->private_data; int err; if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) @@ -453,7 +451,7 @@ static int snd_gf1_pcm_playback_hw_param static int snd_gf1_pcm_playback_hw_free(snd_pcm_substream_t * substream) { snd_pcm_runtime_t *runtime = substream->runtime; - gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + gus_pcm_private_t *pcmp = runtime->private_data; snd_pcm_lib_free_pages(substream); if (pcmp->pvoices[0]) { @@ -474,7 +472,7 @@ static int snd_gf1_pcm_playback_hw_free( static int snd_gf1_pcm_playback_prepare(snd_pcm_substream_t * substream) { snd_pcm_runtime_t *runtime = substream->runtime; - gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + gus_pcm_private_t *pcmp = runtime->private_data; pcmp->bpos = 0; pcmp->dma_size = snd_pcm_lib_buffer_bytes(substream); @@ -488,7 +486,7 @@ static int snd_gf1_pcm_playback_trigger( { snd_gus_card_t *gus = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + gus_pcm_private_t *pcmp = runtime->private_data; int voice; if (cmd == SNDRV_PCM_TRIGGER_START) { @@ -513,7 +511,7 @@ static snd_pcm_uframes_t snd_gf1_pcm_pla { snd_gus_card_t *gus = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + gus_pcm_private_t *pcmp = runtime->private_data; unsigned int pos; unsigned char voice_ctrl; @@ -657,8 +655,8 @@ static snd_pcm_hardware_t snd_gf1_pcm_ca static void snd_gf1_pcm_playback_free(snd_pcm_runtime_t *runtime) { - gus_pcm_private_t * pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return); - snd_magic_kfree(pcmp); + gus_pcm_private_t * pcmp = runtime->private_data; + kfree(pcmp); } static int snd_gf1_pcm_playback_open(snd_pcm_substream_t *substream) @@ -668,7 +666,7 @@ static int snd_gf1_pcm_playback_open(snd snd_pcm_runtime_t *runtime = substream->runtime; int err; - pcmp = snd_magic_kcalloc(gus_pcm_private_t, 0, GFP_KERNEL); + pcmp = kcalloc(1, sizeof(*pcmp), GFP_KERNEL); if (pcmp == NULL) return -ENOMEM; pcmp->gus = gus; @@ -697,7 +695,7 @@ static int snd_gf1_pcm_playback_close(sn { snd_gus_card_t *gus = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - gus_pcm_private_t *pcmp = snd_magic_cast(gus_pcm_private_t, runtime->private_data, return -ENXIO); + gus_pcm_private_t *pcmp = runtime->private_data; unsigned long jiffies_old; jiffies_old = jiffies; @@ -738,7 +736,7 @@ static int snd_gf1_pcm_capture_close(snd static void snd_gf1_pcm_free(snd_pcm_t *pcm) { - snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, pcm->private_data, return); + snd_gus_card_t *gus = pcm->private_data; gus->pcm = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -790,7 +788,7 @@ static int snd_gf1_pcm_volume_put(snd_kc pvoice = &gus->gf1.voices[idx]; if (!pvoice->pcm) continue; - pcmp = snd_magic_cast(gus_pcm_private_t, pvoice->private_data, return -ENXIO); + pcmp = pvoice->private_data; if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE)) continue; /* load real volume - better precision */ --- linux-2.6.9-rc1/sound/isa/gus/gus_synth.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/sound/isa/gus/gus_synth.c 2004-09-02 20:51:53.000000000 -0700 @@ -134,7 +134,7 @@ static void snd_gus_synth_instr_notify(v int what) { unsigned int idx; - snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, private_data, return); + snd_gus_card_t *gus = private_data; snd_gus_voice_t *pvoice; unsigned long flags; --- linux-2.6.9-rc1/sound/isa/gus/gus_timer.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/sound/isa/gus/gus_timer.c 2004-09-02 20:51:53.000000000 -0700 @@ -26,8 +26,6 @@ #include #include -#define chip_t snd_gus_card_t - /* * Timer 1 - 80us */ @@ -146,13 +144,13 @@ static struct _snd_timer_hardware snd_gf static void snd_gf1_timer1_free(snd_timer_t *timer) { - snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, timer->private_data, return); + snd_gus_card_t *gus = timer->private_data; gus->gf1.timer1 = NULL; } static void snd_gf1_timer2_free(snd_timer_t *timer) { - snd_gus_card_t *gus = snd_magic_cast(snd_gus_card_t, timer->private_data, return); + snd_gus_card_t *gus = timer->private_data; gus->gf1.timer2 = NULL; } --- linux-2.6.9-rc1/sound/isa/gus/gus_uart.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/sound/isa/gus/gus_uart.c 2004-09-02 20:51:53.000000000 -0700 @@ -95,7 +95,7 @@ static int snd_gf1_uart_output_open(snd_ unsigned long flags; snd_gus_card_t *gus; - gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO); + gus = substream->rmidi->private_data; spin_lock_irqsave(&gus->uart_cmd_lock, flags); if (!(gus->gf1.uart_cmd & 0x80)) { /* input active? */ snd_gf1_uart_reset(gus, 0); @@ -115,7 +115,7 @@ static int snd_gf1_uart_input_open(snd_r snd_gus_card_t *gus; int i; - gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO); + gus = substream->rmidi->private_data; spin_lock_irqsave(&gus->uart_cmd_lock, flags); if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) { snd_gf1_uart_reset(gus, 0); @@ -141,7 +141,7 @@ static int snd_gf1_uart_output_close(snd unsigned long flags; snd_gus_card_t *gus; - gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO); + gus = substream->rmidi->private_data; spin_lock_irqsave(&gus->uart_cmd_lock, flags); if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in) snd_gf1_uart_reset(gus, 1); @@ -156,7 +156,7 @@ static int snd_gf1_uart_input_close(snd_ unsigned long flags; snd_gus_card_t *gus; - gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return -ENXIO); + gus = substream->rmidi->private_data; spin_lock_irqsave(&gus->uart_cmd_lock, flags); if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) snd_gf1_uart_reset(gus, 1); @@ -171,7 +171,7 @@ static void snd_gf1_uart_input_trigger(s snd_gus_card_t *gus; unsigned long flags; - gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return); + gus = substream->rmidi->private_data; spin_lock_irqsave(&gus->uart_cmd_lock, flags); if (up) { @@ -191,7 +191,7 @@ static void snd_gf1_uart_output_trigger( char byte; int timeout; - gus = snd_magic_cast(snd_gus_card_t, substream->rmidi->private_data, return); + gus = substream->rmidi->private_data; spin_lock_irqsave(&gus->uart_cmd_lock, flags); if (up) { --- linux-2.6.9-rc1/sound/isa/gus/interwave.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/sound/isa/gus/interwave.c 2004-09-02 20:51:53.000000000 -0700 @@ -41,18 +41,17 @@ #include MODULE_AUTHOR("Jaroslav Kysela "); -MODULE_CLASSES("{sound}"); MODULE_LICENSE("GPL"); #ifndef SNDRV_STB MODULE_DESCRIPTION("AMD InterWave"); -MODULE_DEVICES("{{Gravis,UltraSound Plug & Play}," +MODULE_SUPPORTED_DEVICE("{{Gravis,UltraSound Plug & Play}," "{STB,SoundRage32}," "{MED,MED3210}," "{Dynasonix,Dynasonix Pro}," "{Panasonic,PCA761AW}}"); #else MODULE_DESCRIPTION("AMD InterWave STB with TEA6330T"); -MODULE_DEVICES("{{AMD,InterWave STB with TEA6330T}}"); +MODULE_SUPPORTED_DEVICE("{{AMD,InterWave STB with TEA6330T}}"); #endif static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ @@ -77,47 +76,32 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for InterWave soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for InterWave soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable InterWave soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); -#ifdef CONFIG_PNP module_param_array(isapnp, bool, boot_devs, 0444); MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard."); -MODULE_PARM_SYNTAX(isapnp, SNDRV_ISAPNP_DESC); -#endif module_param_array(port, long, boot_devs, 0444); MODULE_PARM_DESC(port, "Port # for InterWave driver."); -MODULE_PARM_SYNTAX(port, SNDRV_ENABLED ",allows:{{0x210,0x260,0x10}},dialog:list"); #ifdef SNDRV_STB module_param_array(port_tc, long, boot_devs, 0444); MODULE_PARM_DESC(port_tc, "Tone control (TEA6330T - i2c bus) port # for InterWave driver."); -MODULE_PARM_SYNTAX(port_tc, SNDRV_ENABLED ",allows:{{0x350,0x380,0x10}},dialog:list"); #endif module_param_array(irq, int, boot_devs, 0444); MODULE_PARM_DESC(irq, "IRQ # for InterWave driver."); -MODULE_PARM_SYNTAX(irq, SNDRV_ENABLED ",allows:{{3},{5},{9},{11},{12},{15}},dialog:list"); module_param_array(dma1, int, boot_devs, 0444); MODULE_PARM_DESC(dma1, "DMA1 # for InterWave driver."); -MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC); module_param_array(dma2, int, boot_devs, 0444); MODULE_PARM_DESC(dma2, "DMA2 # for InterWave driver."); -MODULE_PARM_SYNTAX(dma2, SNDRV_DMA_DESC); module_param_array(joystick_dac, int, boot_devs, 0444); MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for InterWave driver."); -MODULE_PARM_SYNTAX(joystick_dac, SNDRV_ENABLED ",allows:{{0,31}}"); module_param_array(midi, int, boot_devs, 0444); MODULE_PARM_DESC(midi, "MIDI UART enable for InterWave driver."); -MODULE_PARM_SYNTAX(midi, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); module_param_array(pcm_channels, int, boot_devs, 0444); MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for InterWave driver."); -MODULE_PARM_SYNTAX(pcm_channels, SNDRV_ENABLED ",allows:{{2,16}}"); module_param_array(effect, int, boot_devs, 0444); MODULE_PARM_DESC(effect, "Effects enable for InterWave driver."); -MODULE_PARM_SYNTAX(effect, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); struct snd_interwave { int irq; @@ -411,8 +395,8 @@ static void __devinit snd_interwave_dete int bank_pos, pages; unsigned int i, lmct; int psizes[4]; + unsigned char iwave[8]; unsigned char csum; - struct rom_hdr romh; snd_interwave_reset(gus); snd_gf1_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_read8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01); /* enhanced mode */ @@ -434,7 +418,7 @@ static void __devinit snd_interwave_dete #if 0 printk("lmct = 0x%08x\n", lmct); #endif - for (i = 0; i < sizeof(lmc) / sizeof(unsigned int); i++) + for (i = 0; i < ARRAY_SIZE(lmc); i++) if (lmct == lmc[i]) { #if 0 printk("found !!! %i\n", i); @@ -443,7 +427,7 @@ static void __devinit snd_interwave_dete snd_interwave_bank_sizes(gus, psizes); break; } - if (i >= sizeof(lmc) / sizeof(unsigned int) && !gus->gf1.enh_mode) + if (i >= ARRAY_SIZE(lmc) && !gus->gf1.enh_mode) snd_gf1_write16(gus, SNDRV_GF1_GW_MEMORY_CONFIG, (snd_gf1_look16(gus, SNDRV_GF1_GW_MEMORY_CONFIG) & 0xfff0) | 2); for (i = 0; i < 4; i++) { gus->gf1.mem_alloc.banks_8[i].address = @@ -461,33 +445,29 @@ static void __devinit snd_interwave_dete gus->gf1.rom_banks = 0; gus->gf1.rom_memory = 0; for (bank_pos = 0; bank_pos < 16L * 1024L * 1024L; bank_pos += 4L * 1024L * 1024L) { - for (i = 0; i < sizeof(struct rom_hdr); i++) - *(((unsigned char *) &romh) + i) = snd_gf1_peek(gus, i + bank_pos); + for (i = 0; i < 8; ++i) + iwave[i] = snd_gf1_peek(gus, bank_pos + i); #ifdef CONFIG_SND_DEBUG_ROM printk("ROM at 0x%06x = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", bank_pos, - romh.iwave[0], romh.iwave[1], romh.iwave[2], romh.iwave[3], - romh.iwave[4], romh.iwave[5], romh.iwave[6], romh.iwave[7]); + iwave[0], iwave[1], iwave[2], iwave[3], + iwave[4], iwave[5], iwave[6], iwave[7]); #endif - if (strncmp(romh.iwave, "INTRWAVE", 8)) + if (strncmp(iwave, "INTRWAVE", 8)) continue; /* first check */ csum = 0; - for (i = 0; i < sizeof(struct rom_hdr) - 1; i++) - csum += *(((unsigned char *) &romh) + i); + for (i = 0; i < sizeof(struct rom_hdr); i++) + csum += snd_gf1_peek(gus, bank_pos + i); #ifdef CONFIG_SND_DEBUG_ROM - printk("ROM checksum = 0x%x == 0x%x (computed)\n", romh.csum, (unsigned char) (256 - csum)); + printk("ROM checksum = 0x%x (computed)\n", csum); #endif - if (256 - csum != romh.csum) + if (csum != 0) continue; /* not valid rom */ gus->gf1.rom_banks++; gus->gf1.rom_present |= 1 << (bank_pos >> 22); -#ifdef SNDRV_LITTLE_ENDIAN - gus->gf1.rom_memory = romh.rom_size; -#else - gus->gf1.rom_memory = ((romh.rom_size >> 24) & 0x000000ff) | - ((romh.rom_size >> 8) & 0x0000ff00) | - ((romh.rom_size << 8) & 0x00ff0000) | - ((romh.rom_size << 24) & 0xff000000); -#endif + gus->gf1.rom_memory = snd_gf1_peek(gus, bank_pos + 40) | + (snd_gf1_peek(gus, bank_pos + 41) << 8) | + (snd_gf1_peek(gus, bank_pos + 42) << 16) | + (snd_gf1_peek(gus, bank_pos + 43) << 24); } #if 0 if (gus->gf1.rom_memory > 0) { @@ -523,8 +503,6 @@ static void __devinit snd_interwave_init } -#define INTERWAVE_CONTROLS (sizeof(snd_interwave_controls)/sizeof(snd_kcontrol_new_t)) - static snd_kcontrol_new_t snd_interwave_controls[] = { CS4231_DOUBLE("Master Playback Switch", 0, CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 7, 7, 1, 1), CS4231_DOUBLE("Master Playback Volume", 0, CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 0, 0, 31, 1), @@ -552,7 +530,7 @@ static int __devinit snd_interwave_mixer return err; #endif /* add new master and mic controls */ - for (idx = 0; idx < INTERWAVE_CONTROLS; idx++) + for (idx = 0; idx < ARRAY_SIZE(snd_interwave_controls); idx++) if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_interwave_controls[idx], chip))) < 0) return err; snd_cs4231_out(chip, CS4231_LINE_LEFT_OUTPUT, 0x9f); --- linux-2.6.9-rc1/sound/isa/gus/Makefile 2004-08-24 00:03:39.000000000 -0700 +++ 25/sound/isa/gus/Makefile 2004-09-02 20:51:53.000000000 -0700 @@ -27,14 +27,10 @@ sequencer = $(if $(subst y,,$(CONFIG_SND # Toplevel Module Dependency obj-$(CONFIG_SND_GUSCLASSIC) += snd-gusclassic.o snd-gus-lib.o -obj-$(call sequencer,$(CONFIG_SND_GUSCLASSIC)) += snd-gus-synth.o obj-$(CONFIG_SND_GUSMAX) += snd-gusmax.o snd-gus-lib.o -obj-$(call sequencer,$(CONFIG_SND_GUSMAX)) += snd-gus-synth.o obj-$(CONFIG_SND_GUSEXTREME) += snd-gusextreme.o snd-gus-lib.o -obj-$(call sequencer,$(CONFIG_SND_GUSEXTREME)) += snd-gus-synth.o obj-$(CONFIG_SND_INTERWAVE) += snd-interwave.o snd-gus-lib.o -obj-$(call sequencer,$(CONFIG_SND_INTERWAVE)) += snd-gus-synth.o obj-$(CONFIG_SND_INTERWAVE_STB) += snd-interwave-stb.o snd-gus-lib.o -obj-$(call sequencer,$(CONFIG_SND_INTERWAVE_STB)) += snd-gus-synth.o +obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-gus-synth.o obj-m := $(sort $(obj-m)) --- linux-2.6.9-rc1/sound/isa/Kconfig 2004-08-24 00:03:31.000000000 -0700 +++ 25/sound/isa/Kconfig 2004-09-02 20:51:53.000000000 -0700 @@ -77,11 +77,15 @@ config SND_ES18XX help Say 'Y' or 'M' to include support for ESS AudioDrive ES18xx chips. +config SND_GUS_SYNTH + tristate + config SND_GUSCLASSIC tristate "Gravis UltraSound Classic" depends on SND select SND_RAWMIDI select SND_PCM + select SND_GUS_SYNTH help Say 'Y' or 'M' to include support for Gravis UltraSound Classic soundcard. @@ -91,6 +95,7 @@ config SND_GUSEXTREME select SND_HWDEP select SND_MPU401_UART select SND_PCM + select SND_GUS_SYNTH help Say 'Y' or 'M' to include support for Gravis UltraSound Extreme soundcard. @@ -99,6 +104,7 @@ config SND_GUSMAX depends on SND select SND_RAWMIDI select SND_PCM + select SND_GUS_SYNTH help Say 'Y' or 'M' to include support for Gravis UltraSound MAX soundcard. @@ -107,6 +113,7 @@ config SND_INTERWAVE depends on SND select SND_RAWMIDI select SND_PCM + select SND_GUS_SYNTH help Say 'Y' or 'M' to include support for AMD InterWave based soundcards (Gravis UltraSound Plug & Play, STB SoundRage32, MED3210, Dynasonic Pro, @@ -117,6 +124,7 @@ config SND_INTERWAVE_STB depends on SND select SND_RAWMIDI select SND_PCM + select SND_GUS_SYNTH help Say 'Y' or 'M' to include support for AMD InterWave based soundcards with TEA6330T bass and treble regulator (UltraSound 32-Pro). --- linux-2.6.9-rc1/sound/isa/opl3sa2.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/sound/isa/opl3sa2.c 2004-09-02 20:51:53.000000000 -0700 @@ -37,8 +37,7 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Yamaha OPL3SA2+"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Yamaha,YMF719E-S}," +MODULE_SUPPORTED_DEVICE("{{Yamaha,YMF719E-S}," "{Genius,Sound Maker 3DX}," "{Yamaha,OPL3SA3}," "{Intel,AL440LX sound}," @@ -63,45 +62,32 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for OPL3-SA soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for OPL3-SA soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable OPL3-SA soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); #ifdef CONFIG_PNP module_param_array(isapnp, bool, boot_devs, 0444); MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard."); -MODULE_PARM_SYNTAX(isapnp, SNDRV_ISAPNP_DESC); #endif module_param_array(port, long, boot_devs, 0444); MODULE_PARM_DESC(port, "Port # for OPL3-SA driver."); -MODULE_PARM_SYNTAX(port, SNDRV_ENABLED ",allows:{{0xf86},{0x370},{0x100}},dialog:list"); module_param_array(sb_port, long, boot_devs, 0444); MODULE_PARM_DESC(sb_port, "SB port # for OPL3-SA driver."); -MODULE_PARM_SYNTAX(sb_port, SNDRV_ENABLED ",allows:{{0x220},{0x240},{0x260}},dialog:list"); module_param_array(wss_port, long, boot_devs, 0444); MODULE_PARM_DESC(wss_port, "WSS port # for OPL3-SA driver."); -MODULE_PARM_SYNTAX(wss_port, SNDRV_ENABLED ",allows:{{0x530},{0xe80},{0xf40},{0x604}},dialog:list"); module_param_array(fm_port, long, boot_devs, 0444); MODULE_PARM_DESC(fm_port, "FM port # for OPL3-SA driver."); -MODULE_PARM_SYNTAX(fm_port, SNDRV_ENABLED ",allows:{{0x388}},dialog:list"); module_param_array(midi_port, long, boot_devs, 0444); MODULE_PARM_DESC(midi_port, "MIDI port # for OPL3-SA driver."); -MODULE_PARM_SYNTAX(midi_port, SNDRV_ENABLED ",allows:{{0x330},{0x300}},dialog:list"); module_param_array(irq, int, boot_devs, 0444); MODULE_PARM_DESC(irq, "IRQ # for OPL3-SA driver."); -MODULE_PARM_SYNTAX(irq, SNDRV_ENABLED ",allows:{{0},{1},{3},{5},{9},{11},{12},{15}},dialog:list"); module_param_array(dma1, int, boot_devs, 0444); MODULE_PARM_DESC(dma1, "DMA1 # for OPL3-SA driver."); -MODULE_PARM_SYNTAX(dma1, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list"); module_param_array(dma2, int, boot_devs, 0444); MODULE_PARM_DESC(dma2, "DMA2 # for OPL3-SA driver."); -MODULE_PARM_SYNTAX(dma2, SNDRV_ENABLED ",allows:{{1},{3},{5},{6},{7}},dialog:list"); module_param_array(opl3sa3_ymode, int, boot_devs, 0444); MODULE_PARM_DESC(opl3sa3_ymode, "Speaker size selection for 3D Enhancement mode: Desktop/Large Notebook/Small Notebook/HiFi."); -MODULE_PARM_SYNTAX(opl3sa3_ymode, SNDRV_ENABLED ",allows:{{0,3}},dialog:list"); /* SL Added */ /* control ports */ #define OPL3SA2_PM_CTRL 0x01 @@ -131,7 +117,6 @@ MODULE_PARM_SYNTAX(opl3sa3_ymode, SNDRV_ #define OPL3SA2_PM_D3 (OPL3SA2_PM_ADOWN|OPL3SA2_PM_PSV|OPL3SA2_PM_PDN|OPL3SA2_PM_PDX) typedef struct snd_opl3sa2 opl3sa2_t; -#define chip_t opl3sa2_t struct snd_opl3sa2 { snd_card_t *card; @@ -304,7 +289,7 @@ static int __init snd_opl3sa2_detect(opl static irqreturn_t snd_opl3sa2_interrupt(int irq, void *dev_id, struct pt_regs *regs) { unsigned short status; - opl3sa2_t *chip = snd_magic_cast(opl3sa2_t, dev_id, return IRQ_NONE); + opl3sa2_t *chip = dev_id; int handled = 0; if (chip == NULL || chip->card == NULL) @@ -477,8 +462,6 @@ int snd_opl3sa2_put_double(snd_kcontrol_ return change; } -#define OPL3SA2_CONTROLS (sizeof(snd_opl3sa2_controls)/sizeof(snd_kcontrol_new_t)) - static snd_kcontrol_new_t snd_opl3sa2_controls[] = { OPL3SA2_DOUBLE("Master Playback Switch", 0, 0x07, 0x08, 7, 7, 1, 1), OPL3SA2_DOUBLE("Master Playback Volume", 0, 0x07, 0x08, 0, 0, 15, 1), @@ -486,8 +469,6 @@ OPL3SA2_SINGLE("Mic Playback Switch", 0, OPL3SA2_SINGLE("Mic Playback Volume", 0, 0x09, 0, 31, 1) }; -#define OPL3SA2_TONE_CONTROLS (sizeof(snd_opl3sa2_tone_controls)/sizeof(snd_kcontrol_new_t)) - static snd_kcontrol_new_t snd_opl3sa2_tone_controls[] = { OPL3SA2_DOUBLE("3D Control - Wide", 0, 0x14, 0x14, 4, 0, 7, 0), OPL3SA2_DOUBLE("Tone Control - Bass", 0, 0x15, 0x15, 4, 0, 7, 0), @@ -496,7 +477,7 @@ OPL3SA2_DOUBLE("Tone Control - Treble", static void snd_opl3sa2_master_free(snd_kcontrol_t *kcontrol) { - opl3sa2_t *chip = snd_magic_cast(opl3sa2_t, _snd_kcontrol_chip(kcontrol), return); + opl3sa2_t *chip = snd_kcontrol_chip(kcontrol); chip->master_switch = NULL; chip->master_volume = NULL; } @@ -531,7 +512,7 @@ static int __init snd_opl3sa2_mixer(opl3 if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) return err; /* add OPL3SA2 controls */ - for (idx = 0; idx < OPL3SA2_CONTROLS; idx++) { + for (idx = 0; idx < ARRAY_SIZE(snd_opl3sa2_controls); idx++) { if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_opl3sa2_controls[idx], chip))) < 0) return err; switch (idx) { @@ -540,7 +521,7 @@ static int __init snd_opl3sa2_mixer(opl3 } } if (chip->version > 2) { - for (idx = 0; idx < OPL3SA2_TONE_CONTROLS; idx++) + for (idx = 0; idx < ARRAY_SIZE(snd_opl3sa2_tone_controls); idx++) if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_opl3sa2_tone_controls[idx], chip))) < 0) return err; } @@ -551,7 +532,7 @@ static int __init snd_opl3sa2_mixer(opl3 #ifdef CONFIG_PM static int snd_opl3sa2_suspend(snd_card_t *card, unsigned int state) { - opl3sa2_t *chip = snd_magic_cast(opl3sa2_t, card->pm_private_data, return -EINVAL); + opl3sa2_t *chip = card->pm_private_data; snd_pcm_suspend_all(chip->cs4231->pcm); /* stop before saving regs */ chip->cs4231_suspend(chip->cs4231); @@ -565,7 +546,7 @@ static int snd_opl3sa2_suspend(snd_card_ static int snd_opl3sa2_resume(snd_card_t *card, unsigned int state) { - opl3sa2_t *chip = snd_magic_cast(opl3sa2_t, card->pm_private_data, return -EINVAL); + opl3sa2_t *chip = card->pm_private_data; int i; /* power up */ @@ -656,13 +637,13 @@ static int snd_opl3sa2_free(opl3sa2_t *c release_resource(chip->res_port); kfree_nocheck(chip->res_port); } - snd_magic_kfree(chip); + kfree(chip); return 0; } static int snd_opl3sa2_dev_free(snd_device_t *device) { - opl3sa2_t *chip = snd_magic_cast(opl3sa2_t, device->device_data, return -ENXIO); + opl3sa2_t *chip = device->device_data; return snd_opl3sa2_free(chip); } @@ -707,7 +688,7 @@ static int __devinit snd_opl3sa2_probe(i return -ENOMEM; strcpy(card->driver, "OPL3SA2"); strcpy(card->shortname, "Yamaha OPL3-SA2"); - chip = snd_magic_kcalloc(opl3sa2_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) { err = -ENOMEM; goto __error; --- linux-2.6.9-rc1/sound/isa/opti9xx/opti92x-ad1848.c 2004-08-24 00:02:20.000000000 -0700 +++ 25/sound/isa/opti9xx/opti92x-ad1848.c 2004-09-02 20:51:53.000000000 -0700 @@ -52,19 +52,18 @@ #include MODULE_AUTHOR("Massimo Piccioni "); -MODULE_CLASSES("{sound}"); MODULE_LICENSE("GPL"); #ifdef OPTi93X MODULE_DESCRIPTION("OPTi93X"); -MODULE_DEVICES("{{OPTi,82C931/3}}"); +MODULE_SUPPORTED_DEVICE("{{OPTi,82C931/3}}"); #else /* OPTi93X */ #ifdef CS4231 MODULE_DESCRIPTION("OPTi92X - CS4231"); -MODULE_DEVICES("{{OPTi,82C924 (CS4231)}," +MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (CS4231)}," "{OPTi,82C925 (CS4231)}}"); #else /* CS4231 */ MODULE_DESCRIPTION("OPTi92X - AD1848"); -MODULE_DEVICES("{{OPTi,82C924 (AD1848)}," +MODULE_SUPPORTED_DEVICE("{{OPTi,82C924 (AD1848)}," "{OPTi,82C925 (AD1848)}," "{OAK,Mozart}}"); #endif /* CS4231 */ @@ -86,38 +85,27 @@ static int dma2 = SNDRV_DEFAULT_DMA1; / module_param(index, int, 0444); MODULE_PARM_DESC(index, "Index value for opti9xx based soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param(id, charp, 0444); MODULE_PARM_DESC(id, "ID string for opti9xx based soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); //module_param(enable, bool, 0444); //MODULE_PARM_DESC(enable, "Enable opti9xx soundcard."); -//MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param(isapnp, bool, 0444); MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard."); -MODULE_PARM_SYNTAX(isapnp, SNDRV_ISAPNP_DESC); module_param(port, long, 0444); MODULE_PARM_DESC(port, "WSS port # for opti9xx driver."); -MODULE_PARM_SYNTAX(port, SNDRV_PORT_DESC); module_param(mpu_port, long, 0444); MODULE_PARM_DESC(mpu_port, "MPU-401 port # for opti9xx driver."); -MODULE_PARM_SYNTAX(mpu_port, SNDRV_PORT_DESC); module_param(fm_port, long, 0444); MODULE_PARM_DESC(fm_port, "FM port # for opti9xx driver."); -MODULE_PARM_SYNTAX(fm_port, SNDRV_PORT_DESC); module_param(irq, int, 0444); MODULE_PARM_DESC(irq, "WSS irq # for opti9xx driver."); -MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); module_param(mpu_irq, int, 0444); MODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for opti9xx driver."); -MODULE_PARM_SYNTAX(mpu_irq, SNDRV_IRQ_DESC); module_param(dma1, int, 0444); MODULE_PARM_DESC(dma1, "1st dma # for opti9xx driver."); -MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC); #if defined(CS4231) || defined(OPTi93X) module_param(dma2, int, 0444); MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver."); -MODULE_PARM_SYNTAX(dma2, SNDRV_DMA_DESC); #endif /* CS4231 || OPTi93X */ #define OPTi9XX_HW_DETECT 0 @@ -474,7 +462,6 @@ static int __devinit snd_opti9xx_configu unsigned char dma_bits; unsigned char mpu_port_bits = 0; unsigned char mpu_irq_bits; - unsigned long flags; switch (chip->hardware) { #ifndef OPTi93X @@ -601,13 +588,11 @@ __skip_base: dma_bits |= 0x04; #endif /* CS4231 || OPTi93X */ - spin_lock_irqsave(&chip->lock, flags); #ifndef OPTi93X outb(irq_bits << 3 | dma_bits, chip->wss_base); #else /* OPTi93X */ snd_opti9xx_write(chip, OPTi9XX_MC_REG(3), (irq_bits << 3 | dma_bits)); #endif /* OPTi93X */ - spin_unlock_irqrestore(&chip->lock, flags); __skip_resources: if (chip->hardware > OPTi9XX_HW_82C928) { @@ -664,8 +649,6 @@ __skip_mpu: #ifdef OPTi93X -#define chip_t opti93x_t - static unsigned char snd_opti93x_default_image[32] = { 0x00, /* 00/00 - l_mixout_outctrl */ @@ -767,15 +750,10 @@ static void snd_opti93x_mce_down(opti93x static void snd_opti93x_mute(opti93x_t *chip, int mute) { - unsigned long flags; - - spin_lock_irqsave(&chip->lock, flags); - mute = mute ? 1 : 0; - if (chip->mute == mute) { - spin_unlock_irqrestore(&chip->lock, flags); + if (chip->mute == mute) return; - } + chip->mute = mute; snd_opti93x_mute_reg(chip, OPTi93X_CD_LEFT_INPUT, mute); @@ -800,8 +778,6 @@ static void snd_opti93x_mute(opti93x_t * snd_opti93x_mute_reg(chip, OPTi93X_MIC_RIGHT_INPUT, mute); snd_opti93x_mute_reg(chip, OPTi93X_OUT_LEFT, mute); snd_opti93x_mute_reg(chip, OPTi93X_OUT_RIGHT, mute); - - spin_unlock_irqrestore(&chip->lock, flags); } @@ -822,7 +798,7 @@ static unsigned int snd_opti93x_get_coun static unsigned int rates[] = { 5512, 6615, 8000, 9600, 11025, 16000, 18900, 22050, 27428, 32000, 33075, 37800, 44100, 48000 }; -#define RATES sizeof(rates) / sizeof(rates[0]) +#define RATES ARRAY_SIZE(rates) static snd_pcm_hw_constraint_list_t hw_constraints_rates = { .count = RATES, @@ -873,10 +849,8 @@ static unsigned char snd_opti93x_get_for static void snd_opti93x_playback_format(opti93x_t *chip, unsigned char fmt) { - unsigned long flags; unsigned char mask; - spin_lock_irqsave(&chip->lock, flags); snd_opti93x_mute(chip, 1); snd_opti93x_mce_up(chip); @@ -885,14 +859,10 @@ static void snd_opti93x_playback_format( snd_opti93x_mce_down(chip); snd_opti93x_mute(chip, 0); - spin_unlock_irqrestore(&chip->lock, flags); } static void snd_opti93x_capture_format(opti93x_t *chip, unsigned char fmt) { - unsigned long flags; - - spin_lock_irqsave(&chip->lock, flags); snd_opti93x_mute(chip, 1); snd_opti93x_mce_up(chip); @@ -904,7 +874,6 @@ static void snd_opti93x_capture_format(o snd_opti93x_mce_down(chip); snd_opti93x_mute(chip, 0); - spin_unlock_irqrestore(&chip->lock, flags); } @@ -1128,7 +1097,7 @@ static void snd_opti93x_overrange(opti93 irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - opti93x_t *codec = snd_magic_cast(opti93x_t, dev_id, return IRQ_NONE); + opti93x_t *codec = dev_id; unsigned char status; status = snd_opti9xx_read(codec->chip, OPTi9XX_MC_REG(11)); @@ -1274,13 +1243,13 @@ static int snd_opti93x_free(opti93x_t *c if (chip->irq >= 0) { free_irq(chip->irq, chip); } - snd_magic_kfree(chip); + kfree(chip); return 0; } static int snd_opti93x_dev_free(snd_device_t *device) { - opti93x_t *chip = snd_magic_cast(opti93x_t, device->device_data, return -ENXIO); + opti93x_t *chip = device->device_data; return snd_opti93x_free(chip); } @@ -1305,7 +1274,7 @@ int snd_opti93x_create(snd_card_t *card, opti93x_t *codec; *rcodec = NULL; - codec = snd_magic_kcalloc(opti93x_t, 0, GFP_KERNEL); + codec = kcalloc(1, sizeof(*codec), GFP_KERNEL); if (codec == NULL) return -ENOMEM; codec->irq = -1; @@ -1385,7 +1354,7 @@ static snd_pcm_ops_t snd_opti93x_capture static void snd_opti93x_pcm_free(snd_pcm_t *pcm) { - opti93x_t *codec = snd_magic_cast(opti93x_t, pcm->private_data, return); + opti93x_t *codec = pcm->private_data; codec->pcm = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -1608,8 +1577,6 @@ static int snd_opti93x_put_double(snd_kc return change; } -#define OPTi93X_CONTROLS (sizeof(snd_opti93x_controls)/sizeof(snd_kcontrol_new_t)) - static snd_kcontrol_new_t snd_opti93x_controls[] = { OPTi93X_DOUBLE("Master Playback Switch", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1), OPTi93X_DOUBLE("Master Playback Volume", 0, OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1), @@ -1649,7 +1616,7 @@ int snd_opti93x_mixer(opti93x_t *chip) strcpy(card->mixername, snd_opti93x_chip_id(chip)); - for (idx = 0; idx < OPTi93X_CONTROLS; idx++) { + for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) { knew = snd_opti93x_controls[idx]; if (chip->hardware == OPTi9XX_HW_82C930) { if (strstr(knew.name, "FM")) /* skip FM controls */ --- linux-2.6.9-rc1/sound/isa/sb/emu8000.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/sound/isa/sb/emu8000.c 2004-09-02 20:51:53.000000000 -0700 @@ -345,8 +345,6 @@ send_array(emu8000_t *emu, unsigned shor EMU8000_INIT4_WRITE(emu, i, *p); } -#define NELEM(arr) (sizeof(arr)/sizeof((arr)[0])) - /* * Send initialization arrays to start up, this just follows the @@ -355,18 +353,18 @@ send_array(emu8000_t *emu, unsigned shor static void __init init_arrays(emu8000_t *emu) { - send_array(emu, init1, NELEM(init1)/4); + send_array(emu, init1, ARRAY_SIZE(init1)/4); set_current_state(TASK_INTERRUPTIBLE); schedule_timeout((HZ * (44099 + 1024)) / 44100); /* wait for 1024 clocks */ - send_array(emu, init2, NELEM(init2)/4); - send_array(emu, init3, NELEM(init3)/4); + send_array(emu, init2, ARRAY_SIZE(init2)/4); + send_array(emu, init3, ARRAY_SIZE(init3)/4); EMU8000_HWCF4_WRITE(emu, 0); EMU8000_HWCF5_WRITE(emu, 0x83); EMU8000_HWCF6_WRITE(emu, 0x8000); - send_array(emu, init4, NELEM(init4)/4); + send_array(emu, init4, ARRAY_SIZE(init4)/4); } @@ -821,8 +819,6 @@ snd_emu8000_update_reverb_mode(emu8000_t * mixer interface *----------------------------------------------------------------*/ -#define chip_t emu8000_t - /* * bass/treble */ @@ -1070,7 +1066,7 @@ static int snd_emu8000_free(emu8000_t *h release_resource(hw->res_port3); kfree_nocheck(hw->res_port3); } - snd_magic_kfree(hw); + kfree(hw); return 0; } @@ -1078,7 +1074,7 @@ static int snd_emu8000_free(emu8000_t *h */ static int snd_emu8000_dev_free(snd_device_t *device) { - emu8000_t *hw = snd_magic_cast(emu8000_t, device->device_data, return -ENXIO); + emu8000_t *hw = device->device_data; return snd_emu8000_free(hw); } @@ -1101,7 +1097,7 @@ snd_emu8000_new(snd_card_t *card, int in if (seq_ports <= 0) return 0; - hw = snd_magic_kcalloc(emu8000_t, 0, GFP_KERNEL); + hw = kcalloc(1, sizeof(*hw), GFP_KERNEL); if (hw == NULL) return -ENOMEM; spin_lock_init(&hw->reg_lock); --- linux-2.6.9-rc1/sound/isa/sb/emu8000_callback.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/sound/isa/sb/emu8000_callback.c 2004-09-02 20:51:53.000000000 -0700 @@ -94,7 +94,7 @@ release_voice(snd_emux_voice_t *vp) int dcysusv; emu8000_t *hw; - hw = snd_magic_cast(emu8000_t, vp->hw, return); + hw = vp->hw; dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease; EMU8000_DCYSUS_WRITE(hw, vp->ch, dcysusv); dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease; @@ -109,7 +109,7 @@ terminate_voice(snd_emux_voice_t *vp) { emu8000_t *hw; - hw = snd_magic_cast(emu8000_t, vp->hw, return); + hw = vp->hw; EMU8000_DCYSUSV_WRITE(hw, vp->ch, 0x807F); } @@ -121,7 +121,7 @@ update_voice(snd_emux_voice_t *vp, int u { emu8000_t *hw; - hw = snd_magic_cast(emu8000_t, vp->hw, return); + hw = vp->hw; if (update & SNDRV_EMUX_UPDATE_VOLUME) set_volume(hw, vp); if (update & SNDRV_EMUX_UPDATE_PITCH) @@ -168,7 +168,7 @@ get_voice(snd_emux_t *emu, snd_emux_port } best[END]; struct best *bp; - hw = snd_magic_cast(emu8000_t, emu->hw, return NULL); + hw = emu->hw; for (i = 0; i < END; i++) { best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */; @@ -235,7 +235,7 @@ start_voice(snd_emux_voice_t *vp) snd_midi_channel_t *chan; emu8000_t *hw; - hw = snd_magic_cast(emu8000_t, vp->hw, return -EINVAL); + hw = vp->hw; ch = vp->ch; chan = vp->chan; @@ -313,7 +313,7 @@ trigger_voice(snd_emux_voice_t *vp) unsigned int temp; emu8000_t *hw; - hw = snd_magic_cast(emu8000_t, vp->hw, return); + hw = vp->hw; /* set reverb and pitch target */ temp = vp->reg.parm.reverb; @@ -333,7 +333,7 @@ reset_voice(snd_emux_t *emu, int ch) { emu8000_t *hw; - hw = snd_magic_cast(emu8000_t, emu->hw, return); + hw = emu->hw; EMU8000_DCYSUSV_WRITE(hw, ch, 0x807F); snd_emu8000_tweak_voice(hw, ch); } @@ -457,7 +457,7 @@ sysex(snd_emux_t *emu, char *buf, int le { emu8000_t *hw; - hw = snd_magic_cast(emu8000_t, emu->hw, return); + hw = emu->hw; switch (parsed) { case SNDRV_MIDI_SYSEX_GS_CHORUS_MODE: @@ -482,7 +482,7 @@ oss_ioctl(snd_emux_t *emu, int cmd, int { emu8000_t *hw; - hw = snd_magic_cast(emu8000_t, emu->hw, return -EINVAL); + hw = emu->hw; switch (cmd) { case _EMUX_OSS_REVERB_MODE: @@ -526,7 +526,7 @@ static int load_fx(snd_emux_t *emu, int type, int mode, const void __user *buf, long len) { emu8000_t *hw; - hw = snd_magic_cast(emu8000_t, emu->hw, return -EINVAL); + hw = emu->hw; switch (type) { case SNDRV_EMU8000_LOAD_CHORUS_FX: --- linux-2.6.9-rc1/sound/isa/sb/emu8000_local.h 2004-08-24 00:02:48.000000000 -0700 +++ 25/sound/isa/sb/emu8000_local.h 2004-09-02 20:51:53.000000000 -0700 @@ -29,8 +29,6 @@ #include #include -#define NELEM(arr) (sizeof(arr)/sizeof((arr)[0])) - /* emu8000_patch.c */ int snd_emu8000_sample_new(snd_emux_t *rec, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr, const void __user *data, long count); int snd_emu8000_sample_free(snd_emux_t *rec, snd_sf_sample_t *sp, snd_util_memhdr_t *hdr); --- linux-2.6.9-rc1/sound/isa/sb/emu8000_patch.c 2004-08-24 00:01:55.000000000 -0700 +++ 25/sound/isa/sb/emu8000_patch.c 2004-09-02 20:51:53.000000000 -0700 @@ -155,7 +155,7 @@ snd_emu8000_sample_new(snd_emux_t *rec, int dram_offset, dram_start; emu8000_t *emu; - emu = snd_magic_cast(emu8000_t, rec->hw, return -EINVAL); + emu = rec->hw; snd_assert(sp != NULL, return -EINVAL); if (sp->v.size == 0) --- linux-2.6.9-rc1/sound/isa/sb/emu8000_pcm.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/sound/isa/sb/emu8000_pcm.c 2004-09-02 20:51:53.000000000 -0700 @@ -23,8 +23,6 @@ #include #include -#define chip_t emu8000_t - /* * define the following if you want to use this pcm with non-interleaved mode */ @@ -235,7 +233,7 @@ static int emu8k_pcm_open(snd_pcm_substr emu8k_pcm_t *rec; snd_pcm_runtime_t *runtime = subs->runtime; - rec = snd_kcalloc(sizeof(*rec), GFP_KERNEL); + rec = kcalloc(1, sizeof(*rec), GFP_KERNEL); if (! rec) return -ENOMEM; --- linux-2.6.9-rc1/sound/isa/sb/emu8000_synth.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/sound/isa/sb/emu8000_synth.c 2004-09-02 20:51:53.000000000 -0700 @@ -27,7 +27,6 @@ MODULE_AUTHOR("Takashi Iwai, Steve Ratcliffe"); MODULE_DESCRIPTION("Emu8000 synth plug-in routine"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); /*----------------------------------------------------------------*/ --- linux-2.6.9-rc1/sound/isa/sb/es968.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/sound/isa/sb/es968.c 2004-09-02 20:51:53.000000000 -0700 @@ -29,15 +29,12 @@ #include #include -#define chip_t sb_t - #define PFX "es968: " MODULE_AUTHOR("Massimo Piccioni "); MODULE_DESCRIPTION("ESS AudioDrive ES968"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{ESS,AudioDrive ES968}}"); +MODULE_SUPPORTED_DEVICE("{{ESS,AudioDrive ES968}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ @@ -49,22 +46,16 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for es968 based soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for es968 based soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable es968 based soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(port, long, boot_devs, 0444); MODULE_PARM_DESC(port, "Port # for es968 driver."); -MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); module_param_array(irq, int, boot_devs, 0444); MODULE_PARM_DESC(irq, "IRQ # for es968 driver."); -MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); module_param_array(dma8, int, boot_devs, 0444); MODULE_PARM_DESC(dma8, "8-bit DMA # for es968 driver."); -MODULE_PARM_SYNTAX(dma8, SNDRV_DMA8_DESC); struct snd_card_es968 { struct pnp_dev *dev; @@ -82,7 +73,7 @@ MODULE_DEVICE_TABLE(pnp_card, snd_es968_ static irqreturn_t snd_card_es968_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - sb_t *chip = snd_magic_cast(sb_t, dev_id, return IRQ_NONE); + sb_t *chip = dev_id; if (chip->open & SB_OPEN_PCM) { return snd_sb8dsp_interrupt(chip); --- linux-2.6.9-rc1/sound/isa/sb/sb16.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/sound/isa/sb/sb16.c 2004-09-02 20:51:53.000000000 -0700 @@ -37,8 +37,6 @@ #define SNDRV_LEGACY_FIND_FREE_DMA #include -#define chip_t sb_t - #ifdef SNDRV_SBAWE #define PFX "sbawe: " #else @@ -47,17 +45,16 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); #ifndef SNDRV_SBAWE MODULE_DESCRIPTION("Sound Blaster 16"); -MODULE_DEVICES("{{Creative Labs,SB 16}," +MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB 16}," "{Creative Labs,SB Vibra16S}," "{Creative Labs,SB Vibra16C}," "{Creative Labs,SB Vibra16CL}," "{Creative Labs,SB Vibra16X}}"); #else MODULE_DESCRIPTION("Sound Blaster AWE"); -MODULE_DEVICES("{{Creative Labs,SB AWE 32}," +MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB AWE 32}," "{Creative Labs,SB AWE 64}," "{Creative Labs,SB AWE 64 Gold}}"); #endif @@ -96,53 +93,39 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for SoundBlaster 16 soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for SoundBlaster 16 soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable SoundBlaster 16 soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); #ifdef CONFIG_PNP module_param_array(isapnp, bool, boot_devs, 0444); MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard."); -MODULE_PARM_SYNTAX(isapnp, SNDRV_ISAPNP_DESC); #endif module_param_array(port, long, boot_devs, 0444); MODULE_PARM_DESC(port, "Port # for SB16 driver."); -MODULE_PARM_SYNTAX(port, SNDRV_ENABLED ",allows:{{0x220},{0x240},{0x260},{0x280}},dialog:list"); module_param_array(mpu_port, long, boot_devs, 0444); MODULE_PARM_DESC(mpu_port, "MPU-401 port # for SB16 driver."); -MODULE_PARM_SYNTAX(mpu_port, SNDRV_ENABLED ",allows:{{0x330},{0x300}},dialog:list"); module_param_array(fm_port, long, boot_devs, 0444); MODULE_PARM_DESC(fm_port, "FM port # for SB16 PnP driver."); -MODULE_PARM_SYNTAX(fm_port, SNDRV_ENABLED ",allows:{{0x388},{0x38c},{0x390},{0x394}},dialog:list"); #ifdef SNDRV_SBAWE_EMU8000 module_param_array(awe_port, long, boot_devs, 0444); MODULE_PARM_DESC(awe_port, "AWE port # for SB16 PnP driver."); -MODULE_PARM_SYNTAX(awe_port, SNDRV_ENABLED ",allows:{{0x620},{0x640},{0x660},{0x680}},dialog:list"); #endif module_param_array(irq, int, boot_devs, 0444); MODULE_PARM_DESC(irq, "IRQ # for SB16 driver."); -MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); module_param_array(dma8, int, boot_devs, 0444); MODULE_PARM_DESC(dma8, "8-bit DMA # for SB16 driver."); -MODULE_PARM_SYNTAX(dma8, SNDRV_DMA8_DESC); module_param_array(dma16, int, boot_devs, 0444); MODULE_PARM_DESC(dma16, "16-bit DMA # for SB16 driver."); -MODULE_PARM_SYNTAX(dma16, SNDRV_DMA16_DESC); module_param_array(mic_agc, int, boot_devs, 0444); MODULE_PARM_DESC(mic_agc, "Mic Auto-Gain-Control switch."); -MODULE_PARM_SYNTAX(mic_agc, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC); #ifdef CONFIG_SND_SB16_CSP module_param_array(csp, int, boot_devs, 0444); MODULE_PARM_DESC(csp, "ASP/CSP chip support."); -MODULE_PARM_SYNTAX(csp, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); #endif #ifdef SNDRV_SBAWE_EMU8000 module_param_array(seq_ports, int, boot_devs, 0444); MODULE_PARM_DESC(seq_ports, "Number of sequencer ports for WaveTable synth."); -MODULE_PARM_SYNTAX(seq_ports, SNDRV_ENABLED ",allows:{{0,8}},skill:advanced"); #endif struct snd_card_sb16 { --- linux-2.6.9-rc1/sound/isa/sb/sb16_csp.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/sound/isa/sb/sb16_csp.c 2004-09-02 20:51:53.000000000 -0700 @@ -33,12 +33,9 @@ #include #include -#define chip_t snd_sb_csp_t - MODULE_AUTHOR("Uros Bizjak "); MODULE_DESCRIPTION("ALSA driver for SB16 Creative Signal Processor"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); #ifdef SNDRV_LITTLE_ENDIAN #define CSP_HDR_VALUE(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24)) @@ -127,7 +124,7 @@ int snd_sb_csp_new(sb_t *chip, int devic if ((err = snd_hwdep_new(chip->card, "SB16-CSP", device, &hw)) < 0) return err; - if ((p = snd_magic_kcalloc(snd_sb_csp_t, 0, GFP_KERNEL)) == NULL) { + if ((p = kcalloc(1, sizeof(*p), GFP_KERNEL)) == NULL) { snd_device_free(chip->card, hw); return -ENOMEM; } @@ -165,11 +162,11 @@ int snd_sb_csp_new(sb_t *chip, int devic */ static void snd_sb_csp_free(snd_hwdep_t *hwdep) { - snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, hwdep->private_data, return); + snd_sb_csp_t *p = hwdep->private_data; if (p) { if (p->running & SNDRV_SB_CSP_ST_RUNNING) snd_sb_csp_stop(p); - snd_magic_kfree(p); + kfree(p); } } @@ -180,7 +177,7 @@ static void snd_sb_csp_free(snd_hwdep_t */ static int snd_sb_csp_open(snd_hwdep_t * hw, struct file *file) { - snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, hw->private_data, return -ENXIO); + snd_sb_csp_t *p = hw->private_data; return (snd_sb_csp_use(p)); } @@ -189,7 +186,7 @@ static int snd_sb_csp_open(snd_hwdep_t * */ static int snd_sb_csp_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg) { - snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, hw->private_data, return -ENXIO); + snd_sb_csp_t *p = hw->private_data; snd_sb_csp_info_t info; snd_sb_csp_start_t start_info; int err; @@ -258,7 +255,7 @@ static int snd_sb_csp_ioctl(snd_hwdep_t */ static int snd_sb_csp_release(snd_hwdep_t * hw, struct file *file) { - snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, hw->private_data, return -ENXIO); + snd_sb_csp_t *p = hw->private_data; return (snd_sb_csp_unuse(p)); } @@ -1110,7 +1107,7 @@ static int init_proc_entry(snd_sb_csp_t static void info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) { - snd_sb_csp_t *p = snd_magic_cast(snd_sb_csp_t, entry->private_data, return); + snd_sb_csp_t *p = entry->private_data; snd_iprintf(buffer, "Creative Signal Processor [v%d.%d]\n", (p->version >> 4), (p->version & 0x0f)); snd_iprintf(buffer, "State: %cx%c%c%c\n", ((p->running & SNDRV_SB_CSP_ST_QSOUND) ? 'Q' : '-'), --- linux-2.6.9-rc1/sound/isa/sb/sb16_main.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/sound/isa/sb/sb16_main.c 2004-09-02 20:51:53.000000000 -0700 @@ -49,13 +49,11 @@ MODULE_AUTHOR("Jaroslav Kysela hardware == SB_HW_16CSP) { - snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + snd_sb_csp_t *csp = chip->csp; if (csp->running & SNDRV_SB_CSP_ST_LOADED) { /* manually loaded codec */ @@ -103,7 +101,7 @@ static void snd_sb16_csp_playback_prepar static void snd_sb16_csp_capture_prepare(sb_t *chip, snd_pcm_runtime_t *runtime) { if (chip->hardware == SB_HW_16CSP) { - snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + snd_sb_csp_t *csp = chip->csp; if (csp->running & SNDRV_SB_CSP_ST_LOADED) { /* manually loaded codec */ @@ -141,7 +139,7 @@ static void snd_sb16_csp_capture_prepare static void snd_sb16_csp_update(sb_t *chip) { if (chip->hardware == SB_HW_16CSP) { - snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + snd_sb_csp_t *csp = chip->csp; if (csp->qpos_changed) { spin_lock(&chip->reg_lock); @@ -155,7 +153,7 @@ static void snd_sb16_csp_playback_open(s { /* CSP decoders (QSound excluded) support only 16bit transfers */ if (chip->hardware == SB_HW_16CSP) { - snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + snd_sb_csp_t *csp = chip->csp; if (csp->running & SNDRV_SB_CSP_ST_LOADED) { /* manually loaded codec */ @@ -173,7 +171,7 @@ static void snd_sb16_csp_playback_open(s static void snd_sb16_csp_playback_close(sb_t *chip) { if ((chip->hardware == SB_HW_16CSP) && (chip->open == SNDRV_SB_CSP_MODE_DSP_WRITE)) { - snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + snd_sb_csp_t *csp = chip->csp; if (csp->ops.csp_stop(csp) == 0) { csp->ops.csp_unuse(csp); @@ -186,7 +184,7 @@ static void snd_sb16_csp_capture_open(sb { /* CSP coders support only 16bit transfers */ if (chip->hardware == SB_HW_16CSP) { - snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + snd_sb_csp_t *csp = chip->csp; if (csp->running & SNDRV_SB_CSP_ST_LOADED) { /* manually loaded codec */ @@ -204,7 +202,7 @@ static void snd_sb16_csp_capture_open(sb static void snd_sb16_csp_capture_close(sb_t *chip) { if ((chip->hardware == SB_HW_16CSP) && (chip->open == SNDRV_SB_CSP_MODE_DSP_READ)) { - snd_sb_csp_t *csp = snd_magic_cast(snd_sb_csp_t, chip->csp, return); + snd_sb_csp_t *csp = chip->csp; if (csp->ops.csp_stop(csp) == 0) { csp->ops.csp_unuse(csp); @@ -395,7 +393,7 @@ static int snd_sb16_capture_trigger(snd_ irqreturn_t snd_sb16dsp_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - sb_t *chip = snd_magic_cast(sb_t, dev_id, return IRQ_NONE); + sb_t *chip = dev_id; unsigned char status; int ok; --- linux-2.6.9-rc1/sound/isa/sb/sb8.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/sound/isa/sb/sb8.c 2004-09-02 20:51:53.000000000 -0700 @@ -30,13 +30,10 @@ #define SNDRV_LEGACY_AUTO_PROBE #include -#define chip_t sb_t - MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Sound Blaster 1.0/2.0/Pro"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Creative Labs,SB 1.0/SB 2.0/SB Pro}}"); +MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB 1.0/SB 2.0/SB Pro}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ @@ -48,22 +45,16 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for Sound Blaster soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for Sound Blaster soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable Sound Blaster soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(port, long, boot_devs, 0444); MODULE_PARM_DESC(port, "Port # for SB8 driver."); -MODULE_PARM_SYNTAX(port, SNDRV_PORT12_DESC); module_param_array(irq, int, boot_devs, 0444); MODULE_PARM_DESC(irq, "IRQ # for SB8 driver."); -MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); module_param_array(dma8, int, boot_devs, 0444); MODULE_PARM_DESC(dma8, "8-bit DMA # for SB8 driver."); -MODULE_PARM_SYNTAX(dma8, SNDRV_DMA8_DESC); struct snd_sb8 { struct resource *fm_res; /* used to block FM i/o region for legacy cards */ @@ -73,7 +64,7 @@ static snd_card_t *snd_sb8_cards[SNDRV_C static irqreturn_t snd_sb8_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - sb_t *chip = snd_magic_cast(sb_t, dev_id, return IRQ_NONE); + sb_t *chip = dev_id; if (chip->open & SB_OPEN_PCM) { return snd_sb8dsp_interrupt(chip); --- linux-2.6.9-rc1/sound/isa/sb/sb8_main.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/sound/isa/sb/sb8_main.c 2004-09-02 20:51:53.000000000 -0700 @@ -42,8 +42,6 @@ MODULE_AUTHOR("Jaroslav Kysela rmidi->private_data, return -ENXIO); + chip = substream->rmidi->private_data; valid_open_flags = chip->hardware >= SB_HW_20 ? SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER : 0; spin_lock_irqsave(&chip->open_lock, flags); @@ -100,7 +100,7 @@ static int snd_sb8dsp_midi_output_open(s sb_t *chip; unsigned int valid_open_flags; - chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return -ENXIO); + chip = substream->rmidi->private_data; valid_open_flags = chip->hardware >= SB_HW_20 ? SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER : 0; spin_lock_irqsave(&chip->open_lock, flags); @@ -126,7 +126,7 @@ static int snd_sb8dsp_midi_input_close(s unsigned long flags; sb_t *chip; - chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return -ENXIO); + chip = substream->rmidi->private_data; spin_lock_irqsave(&chip->open_lock, flags); chip->open &= ~(SB_OPEN_MIDI_INPUT | SB_OPEN_MIDI_INPUT_TRIGGER); chip->midi_substream_input = NULL; @@ -144,7 +144,7 @@ static int snd_sb8dsp_midi_output_close( unsigned long flags; sb_t *chip; - chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return -ENXIO); + chip = substream->rmidi->private_data; spin_lock_irqsave(&chip->open_lock, flags); chip->open &= ~(SB_OPEN_MIDI_OUTPUT | SB_OPEN_MIDI_OUTPUT_TRIGGER); chip->midi_substream_output = NULL; @@ -162,7 +162,7 @@ static void snd_sb8dsp_midi_input_trigge unsigned long flags; sb_t *chip; - chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return); + chip = substream->rmidi->private_data; spin_lock_irqsave(&chip->open_lock, flags); if (up) { if (!(chip->open & SB_OPEN_MIDI_INPUT_TRIGGER)) { @@ -188,7 +188,7 @@ static void snd_sb8dsp_midi_output_write int max = 32; /* how big is Tx FIFO? */ - chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return); + chip = substream->rmidi->private_data; while (max-- > 0) { spin_lock_irqsave(&chip->open_lock, flags); if (snd_rawmidi_transmit_peek(substream, &byte, 1) != 1) { @@ -219,7 +219,7 @@ static void snd_sb8dsp_midi_output_write static void snd_sb8dsp_midi_output_timer(unsigned long data) { snd_rawmidi_substream_t * substream = (snd_rawmidi_substream_t *) data; - sb_t * chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return); + sb_t * chip = substream->rmidi->private_data; unsigned long flags; spin_lock_irqsave(&chip->open_lock, flags); @@ -234,7 +234,7 @@ static void snd_sb8dsp_midi_output_trigg unsigned long flags; sb_t *chip; - chip = snd_magic_cast(sb_t, substream->rmidi->private_data, return); + chip = substream->rmidi->private_data; spin_lock_irqsave(&chip->open_lock, flags); if (up) { if (!(chip->open & SB_OPEN_MIDI_OUTPUT_TRIGGER)) { --- linux-2.6.9-rc1/sound/isa/sb/sb_common.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/sound/isa/sb/sb_common.c 2004-09-02 20:51:53.000000000 -0700 @@ -33,12 +33,9 @@ #include #include -#define chip_t sb_t - MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("ALSA lowlevel driver for Sound Blaster cards"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); #define BUSY_LOOPS 100000 @@ -197,13 +194,13 @@ static int snd_sbdsp_free(sb_t *chip) free_dma(chip->dma16); } #endif - snd_magic_kfree(chip); + kfree(chip); return 0; } static int snd_sbdsp_dev_free(snd_device_t *device) { - sb_t *chip = snd_magic_cast(sb_t, device->device_data, return -ENXIO); + sb_t *chip = device->device_data; return snd_sbdsp_free(chip); } @@ -224,7 +221,7 @@ int snd_sbdsp_create(snd_card_t *card, snd_assert(r_chip != NULL, return -EINVAL); *r_chip = NULL; - chip = snd_magic_kcalloc(sb_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; spin_lock_init(&chip->reg_lock); --- linux-2.6.9-rc1/sound/isa/sb/sb_mixer.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/sound/isa/sb/sb_mixer.c 2004-09-02 20:51:53.000000000 -0700 @@ -27,8 +27,6 @@ #include #include -#define chip_t sb_t - #undef IO_DEBUG void snd_sbmixer_write(sb_t *chip, unsigned char reg, unsigned char data) --- linux-2.6.9-rc1/sound/isa/sgalaxy.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/sound/isa/sgalaxy.c 2004-09-02 20:51:53.000000000 -0700 @@ -39,8 +39,7 @@ MODULE_AUTHOR("Christopher Butler "); MODULE_DESCRIPTION("Aztech Sound Galaxy"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Aztech Systems,Sound Galaxy}}"); +MODULE_SUPPORTED_DEVICE("{{Aztech Systems,Sound Galaxy}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ @@ -53,22 +52,16 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for Sound Galaxy soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for Sound Galaxy soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(sbport, long, boot_devs, 0444); MODULE_PARM_DESC(sbport, "Port # for Sound Galaxy SB driver."); -MODULE_PARM_SYNTAX(sbport, SNDRV_ENABLED ",allows:{{0x220},{0x240}},dialog:list"); module_param_array(wssport, long, boot_devs, 0444); MODULE_PARM_DESC(wssport, "Port # for Sound Galaxy WSS driver."); -MODULE_PARM_SYNTAX(wssport, SNDRV_ENABLED ",allows:{{0x530},{0xe80},{0xf40},{0x604}},dialog:list"); module_param_array(irq, int, boot_devs, 0444); MODULE_PARM_DESC(irq, "IRQ # for Sound Galaxy driver."); -MODULE_PARM_SYNTAX(irq, SNDRV_ENABLED ",allows:{{7},{9},{10},{11}},dialog:list"); module_param_array(dma1, int, boot_devs, 0444); MODULE_PARM_DESC(dma1, "DMA1 # for Sound Galaxy driver."); -MODULE_PARM_SYNTAX(dma1, SNDRV_DMA8_DESC); #define SGALAXY_AUXC_LEFT 18 #define SGALAXY_AUXC_RIGHT 19 --- linux-2.6.9-rc1/sound/isa/sscape.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/sound/isa/sscape.c 2004-09-02 20:51:53.000000000 -0700 @@ -36,8 +36,6 @@ #include -#define chip_t cs4231_t - MODULE_AUTHOR("Chris Rankin"); MODULE_DESCRIPTION("ENSONIQ SoundScape PnP driver"); @@ -53,27 +51,21 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index number for SoundScape soundcard"); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "Description for SoundScape card"); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(port, long, boot_devs, 0444); MODULE_PARM_DESC(port, "Port # for SoundScape driver."); -MODULE_PARM_SYNTAX(port, SNDRV_ENABLED); module_param_array(irq, int, boot_devs, 0444); MODULE_PARM_DESC(irq, "IRQ # for SoundScape driver."); -MODULE_PARM_SYNTAX(irq, SNDRV_IRQ_DESC); module_param_array(mpu_irq, int, boot_devs, 0444); MODULE_PARM_DESC(mpu_irq, "MPU401 IRQ # for SoundScape driver."); -MODULE_PARM_SYNTAX(mpu_irq, SNDRV_IRQ_DESC); module_param_array(dma, int, boot_devs, 0444); MODULE_PARM_DESC(dma, "DMA # for SoundScape driver."); -MODULE_PARM_SYNTAX(dma, SNDRV_DMA8_DESC); #ifdef CONFIG_PNP static struct pnp_card_device_id sscape_pnpids[] = { @@ -177,11 +169,8 @@ static inline struct soundscape *get_hwd static struct snd_dma_buffer *get_dmabuf(struct snd_dma_buffer *buf, unsigned long size) { if (buf) { - struct snd_dma_device dev; - memset(&dev, 0, sizeof(dev)); - dev.type = SNDRV_DMA_TYPE_DEV; - dev.dev = snd_dma_isa_data(); - if (snd_dma_alloc_pages_fallback(&dev, size, buf) < 0) { + if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, snd_dma_isa_data(), + size, buf) < 0) { snd_printk(KERN_ERR "sscape: Failed to allocate %lu bytes for DMA\n", size); return NULL; } @@ -195,13 +184,8 @@ static struct snd_dma_buffer *get_dmabuf */ static void free_dmabuf(struct snd_dma_buffer *buf) { - if (buf && buf->area) { - struct snd_dma_device dev; - memset(&dev, 0, sizeof(dev)); - dev.type = SNDRV_DMA_TYPE_DEV; - dev.dev = snd_dma_isa_data(); - snd_dma_free_pages(&dev, buf); - } + if (buf && buf->area) + snd_dma_free_pages(buf); } --- linux-2.6.9-rc1/sound/isa/wavefront/wavefront.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/sound/isa/wavefront/wavefront.c 2004-09-02 20:51:53.000000000 -0700 @@ -30,13 +30,10 @@ #include #include -#define chip_t cs4231_t - MODULE_AUTHOR("Paul Barton-Davis "); MODULE_DESCRIPTION("Turtle Beach Wavefront"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Turtle Beach,Maui/Tropez/Tropez+}}"); +MODULE_SUPPORTED_DEVICE("{{Turtle Beach,Maui/Tropez/Tropez+}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ @@ -56,48 +53,34 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for WaveFront soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for WaveFront soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable WaveFront soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); #ifdef CONFIG_PNP module_param_array(isapnp, bool, boot_devs, 0444); MODULE_PARM_DESC(isapnp, "ISA PnP detection for WaveFront soundcards."); -MODULE_PARM_SYNTAX(isapnp, SNDRV_ISAPNP_DESC); #endif module_param_array(cs4232_pcm_port, long, boot_devs, 0444); MODULE_PARM_DESC(cs4232_pcm_port, "Port # for CS4232 PCM interface."); -MODULE_PARM_SYNTAX(cs4232_pcm_port, SNDRV_PORT12_DESC); module_param_array(cs4232_pcm_irq, int, boot_devs, 0444); MODULE_PARM_DESC(cs4232_pcm_irq, "IRQ # for CS4232 PCM interface."); -MODULE_PARM_SYNTAX(cs4232_pcm_irq, SNDRV_ENABLED ",allows:{{5},{7},{9},{11},{12},{15}},dialog:list"); module_param_array(dma1, int, boot_devs, 0444); MODULE_PARM_DESC(dma1, "DMA1 # for CS4232 PCM interface."); -MODULE_PARM_SYNTAX(dma1, SNDRV_DMA_DESC); module_param_array(dma2, int, boot_devs, 0444); MODULE_PARM_DESC(dma2, "DMA2 # for CS4232 PCM interface."); -MODULE_PARM_SYNTAX(dma2, SNDRV_DMA_DESC); module_param_array(cs4232_mpu_port, long, boot_devs, 0444); MODULE_PARM_DESC(cs4232_mpu_port, "port # for CS4232 MPU-401 interface."); -MODULE_PARM_SYNTAX(cs4232_mpu_port, SNDRV_PORT12_DESC); module_param_array(cs4232_mpu_irq, int, boot_devs, 0444); MODULE_PARM_DESC(cs4232_mpu_irq, "IRQ # for CS4232 MPU-401 interface."); -MODULE_PARM_SYNTAX(cs4232_mpu_irq, SNDRV_ENABLED ",allows:{{9},{11},{12},{15}},dialog:list"); module_param_array(ics2115_irq, int, boot_devs, 0444); MODULE_PARM_DESC(ics2115_irq, "IRQ # for ICS2115."); -MODULE_PARM_SYNTAX(ics2115_irq, SNDRV_ENABLED ",allows:{{9},{11},{12},{15}},dialog:list"); module_param_array(ics2115_port, long, boot_devs, 0444); MODULE_PARM_DESC(ics2115_port, "Port # for ICS2115."); -MODULE_PARM_SYNTAX(ics2115_port, SNDRV_PORT12_DESC); module_param_array(fm_port, long, boot_devs, 0444); MODULE_PARM_DESC(fm_port, "FM port #."); -MODULE_PARM_SYNTAX(fm_port, SNDRV_PORT12_DESC); module_param_array(use_cs4232_midi, bool, boot_devs, 0444); MODULE_PARM_DESC(use_cs4232_midi, "Use CS4232 MPU-401 interface (inaccessibly located inside your computer)"); -MODULE_PARM_SYNTAX(use_cs4232_midi, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); static snd_card_t *snd_wavefront_legacy[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; --- linux-2.6.9-rc1/sound/isa/wavefront/wavefront_fx.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/sound/isa/wavefront/wavefront_fx.c 2004-09-02 20:51:53.000000000 -0700 @@ -486,8 +486,9 @@ snd_wavefront_fx_ioctl (snd_hwdep_t *sde snd_wavefront_card_t *acard; snd_wavefront_t *dev; wavefront_fx_info r; - unsigned short page_data[256]; + unsigned short *page_data = NULL; unsigned short *pd; + int err = 0; snd_assert(sdev->card != NULL, return -ENODEV); @@ -514,23 +515,30 @@ snd_wavefront_fx_ioctl (snd_hwdep_t *sde } else if (r.data[2] == 1) { pd = (unsigned short *) &r.data[3]; } else { - if (r.data[2] > (long)sizeof (page_data)) { + if (r.data[2] > 256) { snd_printk ("cannot write " - "> 255 bytes to FX\n"); + "> 512 bytes to FX\n"); return -EIO; } + page_data = kmalloc(r.data[2] * sizeof(short), GFP_KERNEL); + if (!page_data) + return -ENOMEM; if (copy_from_user (page_data, (unsigned char __user *) r.data[3], - r.data[2])) + r.data[2] * sizeof(short))) { + kfree(page_data); return -EFAULT; + } pd = page_data; } - wavefront_fx_memset (dev, + err = wavefront_fx_memset (dev, r.data[0], /* page */ r.data[1], /* addr */ r.data[2], /* cnt */ pd); + if (page_data) + kfree(page_data); break; default: @@ -538,7 +546,7 @@ snd_wavefront_fx_ioctl (snd_hwdep_t *sde r.request); return -ENOTTY; } - return 0; + return err; } /* YSS225 initialization. --- linux-2.6.9-rc1/sound/isa/wavefront/wavefront_synth.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/sound/isa/wavefront/wavefront_synth.c 2004-09-02 20:51:53.000000000 -0700 @@ -1961,6 +1961,12 @@ wavefront_download_firmware (snd_wavefro break; } + if (section_length < 0 || section_length > WF_SECTION_MAX) { + snd_printk ("invalid firmware section length %d\n", + section_length); + goto failure; + } + if (sys_read (fd, section, section_length) != section_length) { snd_printk ("firmware section " "read error.\n"); --- linux-2.6.9-rc1/sound/oss/ad1848.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/sound/oss/ad1848.c 2004-09-02 20:51:53.000000000 -0700 @@ -1538,7 +1538,7 @@ static void ad1848_init_hw(ad1848_info * ad1848_mixer_reset(devc); } -int ad1848_detect(int io_base, int *ad_flags, int *osp) +int ad1848_detect(struct resource *ports, int *ad_flags, int *osp) { unsigned char tmp; ad1848_info *devc = &adev_info[nr_ad1848_devs]; @@ -1548,6 +1548,7 @@ int ad1848_detect(int io_base, int *ad_f int ad1847_flag = 0; int cs4248_flag = 0; int sscape_flag = 0; + int io_base = ports->start; int i; @@ -1578,11 +1579,6 @@ int ad1848_detect(int io_base, int *ad_f printk(KERN_ERR "ad1848 - Too many audio devices\n"); return 0; } - if (check_region(io_base, 4)) - { - printk(KERN_ERR "ad1848.c: Port %x not free.\n", io_base); - return 0; - } spin_lock_init(&devc->lock); devc->base = io_base; devc->irq_ok = 0; @@ -1951,7 +1947,7 @@ int ad1848_detect(int io_base, int *ad_f return 1; } -int ad1848_init (char *name, int io_base, int irq, int dma_playback, +int ad1848_init (char *name, struct resource *ports, int irq, int dma_playback, int dma_capture, int share_dma, int *osp, struct module *owner) { /* @@ -1986,8 +1982,7 @@ int ad1848_init (char *name, int io_base sprintf(dev_name, "Generic audio codec (%s)", devc->chip_name); - if (!request_region(devc->base, 4, devc->name)) - return -1; + rename_region(ports, devc->name); conf_printf2(dev_name, devc->base, devc->irq, dma_playback, dma_capture); @@ -2522,21 +2517,16 @@ static int init_deskpro(struct address_i return 1; } -int probe_ms_sound(struct address_info *hw_config) +int probe_ms_sound(struct address_info *hw_config, struct resource *ports) { unsigned char tmp; DDB(printk("Entered probe_ms_sound(%x, %d)\n", hw_config->io_base, hw_config->card_subtype)); - if (check_region(hw_config->io_base, 8)) - { - printk(KERN_ERR "MSS: I/O port conflict\n"); - return 0; - } if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */ { /* check_opl3(0x388, hw_config); */ - return ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp); + return ad1848_detect(ports, NULL, hw_config->osp); } if (deskpro_xl && hw_config->card_subtype == 2) /* Compaq Deskpro XL */ @@ -2562,7 +2552,7 @@ int probe_ms_sound(struct address_info * int ret; DDB(printk("I/O address is inactive (%x)\n", tmp)); - if (!(ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp))) + if (!(ret = ad1848_detect(ports, NULL, hw_config->osp))) return 0; return 1; } @@ -2575,7 +2565,7 @@ int probe_ms_sound(struct address_info * MDB(printk(KERN_ERR "No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, (int) inb(hw_config->io_base + 3))); DDB(printk("Trying to detect codec anyway but IRQ/DMA may not work\n")); - if (!(ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp))) + if (!(ret = ad1848_detect(ports, NULL, hw_config->osp))) return 0; hw_config->card_subtype = 1; @@ -2610,10 +2600,10 @@ int probe_ms_sound(struct address_info * printk(KERN_ERR "MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq); return 0; } - return ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp); + return ad1848_detect(ports, NULL, hw_config->osp); } -void attach_ms_sound(struct address_info *hw_config, struct module *owner) +void attach_ms_sound(struct address_info *hw_config, struct resource *ports, struct module *owner) { static signed char interrupt_bits[12] = { @@ -2634,13 +2624,12 @@ void attach_ms_sound(struct address_info if (hw_config->card_subtype == 1) /* Has no IRQ/DMA registers */ { - hw_config->slots[0] = ad1848_init("MS Sound System", hw_config->io_base + 4, + hw_config->slots[0] = ad1848_init("MS Sound System", ports, hw_config->irq, hw_config->dma, hw_config->dma2, 0, hw_config->osp, owner); - request_region(hw_config->io_base, 4, "WSS config"); return; } /* @@ -2651,6 +2640,8 @@ void attach_ms_sound(struct address_info if (bits == -1) { printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq); + release_region(ports->start, 4); + release_region(ports->start - 4, 4); return; } outb((bits | 0x40), config_port); @@ -2694,12 +2685,11 @@ void attach_ms_sound(struct address_info outb((bits | dma_bits[dma] | dma2_bit), config_port); /* Write IRQ+DMA setup */ - hw_config->slots[0] = ad1848_init("MS Sound System", hw_config->io_base + 4, + hw_config->slots[0] = ad1848_init("MS Sound System", ports, hw_config->irq, dma, dma2, 0, hw_config->osp, THIS_MODULE); - request_region(hw_config->io_base, 4, "WSS config"); } void unload_ms_sound(struct address_info *hw_config) @@ -3095,6 +3085,7 @@ static int __init init_ad1848(void) #endif if(io != -1) { + struct resource *ports; if( isapnp == 0 ) { if(irq == -1 || dma == -1) { @@ -3109,9 +3100,22 @@ static int __init init_ad1848(void) cfg.card_subtype = type; } - if(!probe_ms_sound(&cfg)) + ports = request_region(io + 4, 4, "ad1848"); + + if (!ports) + return -EBUSY; + + if (!request_region(io, 4, "WSS config")) { + release_region(io + 4, 4); + return -EBUSY; + } + + if (!probe_ms_sound(&cfg, ports)) { + release_region(io + 4, 4); + release_region(io, 4); return -ENODEV; - attach_ms_sound(&cfg, THIS_MODULE); + } + attach_ms_sound(&cfg, ports, THIS_MODULE); loaded = 1; } return 0; --- linux-2.6.9-rc1/sound/oss/ad1848.h 2004-08-24 00:01:51.000000000 -0700 +++ 25/sound/oss/ad1848.h 2004-09-02 20:51:53.000000000 -0700 @@ -11,15 +11,15 @@ ad1848_control(AD1848_MIXER_REROUTE, ((oldctl)<<8)|(newctl)) -int ad1848_init(char *name, int io_base, int irq, int dma_playback, +int ad1848_init(char *name, struct resource *ports, int irq, int dma_playback, int dma_capture, int share_dma, int *osp, struct module *owner); void ad1848_unload (int io_base, int irq, int dma_playback, int dma_capture, int share_dma); -int ad1848_detect (int io_base, int *flags, int *osp); +int ad1848_detect (struct resource *ports, int *flags, int *osp); int ad1848_control(int cmd, int arg); irqreturn_t adintr(int irq, void *dev_id, struct pt_regs * dummy); -void attach_ms_sound(struct address_info * hw_config, struct module * owner); +void attach_ms_sound(struct address_info * hw_config, struct resource *ports, struct module * owner); -int probe_ms_sound(struct address_info *hw_config); +int probe_ms_sound(struct address_info *hw_config, struct resource *ports); void unload_ms_sound(struct address_info *hw_info); --- linux-2.6.9-rc1/sound/oss/ali5455.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/sound/oss/ali5455.c 2004-09-03 00:56:41.033922544 -0700 @@ -65,7 +65,6 @@ #include #include #include -#include #ifndef PCI_DEVICE_ID_ALI_5455 #define PCI_DEVICE_ID_ALI_5455 0x5455 --- linux-2.6.9-rc1/sound/oss/au1000.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/sound/oss/au1000.c 2004-09-03 00:56:41.035922240 -0700 @@ -67,9 +67,9 @@ #include #include #include +#include #include #include -#include #include #include --- linux-2.6.9-rc1/sound/oss/btaudio.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/sound/oss/btaudio.c 2004-09-02 20:51:53.000000000 -0700 @@ -961,7 +961,7 @@ static int __devinit btaudio_probe(struc /* init hw */ btwrite(0, REG_GPIO_DMA_CTL); btwrite(0, REG_INT_MASK); - btwrite(~0x0UL, REG_INT_STAT); + btwrite(~0U, REG_INT_STAT); pci_set_master(pci_dev); if ((rc = request_irq(bta->irq, btaudio_irq, SA_SHIRQ|SA_INTERRUPT, @@ -1033,7 +1033,7 @@ static void __devexit btaudio_remove(str /* turn off all DMA / IRQs */ btand(~15, REG_GPIO_DMA_CTL); btwrite(0, REG_INT_MASK); - btwrite(~0x0UL, REG_INT_STAT); + btwrite(~0U, REG_INT_STAT); /* unregister devices */ if (digital) { --- linux-2.6.9-rc1/sound/oss/cmpci.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/sound/oss/cmpci.c 2004-09-02 20:51:53.000000000 -0700 @@ -3000,6 +3000,8 @@ static int __devinit cm_probe(struct pci mm_segment_t fs; int i, val, ret; unsigned char reg_mask; + int timeout; + struct resource *ports; struct { unsigned short deviceid; char *devicename; @@ -3184,54 +3186,55 @@ static int __devinit cm_probe(struct pci } #endif #ifdef CONFIG_SOUND_CMPCI_MIDI + switch (s->iomidi) { + case 0x330: + reg_mask = 0; + break; + case 0x320: + reg_mask = 0x20; + break; + case 0x310: + reg_mask = 0x40; + break; + case 0x300: + reg_mask = 0x60; + break; + default: + s->iomidi = 0; + goto skip_mpu; + } + ports = request_region(s->iomidi, 2, "mpu401"); + if (!ports) + goto skip_mpu; /* disable MPU-401 */ maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0x04, 0); s->mpu_data.name = "cmpci mpu"; s->mpu_data.io_base = s->iomidi; s->mpu_data.irq = -s->irq; // tell mpu401 to share irq - if (probe_mpu401(&s->mpu_data)) + if (probe_mpu401(&s->mpu_data, ports)) { + release_region(s->iomidi, 2); s->iomidi = 0; - if (s->iomidi) { - /* set IO based at 0x330 */ - switch (s->iomidi) { - case 0x330: - reg_mask = 0; - break; - case 0x320: - reg_mask = 0x20; - break; - case 0x310: - reg_mask = 0x40; - break; - case 0x300: - reg_mask = 0x60; - break; - default: - s->iomidi = 0; + goto skip_mpu; + } + maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0x60, reg_mask); + /* enable MPU-401 */ + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x04); + /* clear all previously received interrupt */ + for (timeout = 900000; timeout > 0; timeout--) { + if ((inb(s->iomidi + 1) && 0x80) == 0) + inb(s->iomidi); + else break; - } - maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0x60, reg_mask); - /* enable MPU-401 */ - if (s->iomidi) { - int timeout; - - maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x04); - /* clear all previously received interrupt */ - for (timeout = 900000; timeout > 0; timeout--) { - if ((inb(s->iomidi + 1) && 0x80) == 0) - inb(s->iomidi); - else - break; - } - if (!probe_mpu401(&s->mpu_data)) { - s->iomidi = 0; - maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x04); - } else { - attach_mpu401(&s->mpu_data, THIS_MODULE); - s->midi_devc = s->mpu_data.slots[1]; - } - } } + if (!probe_mpu401(&s->mpu_data, ports)) { + release_region(s->iomidi, 2); + s->iomidi = 0; + maskb(s->iobase + CODEC_CMI_FUNCTRL1, ~0, 0x04); + } else { + attach_mpu401(&s->mpu_data, THIS_MODULE); + s->midi_devc = s->mpu_data.slots[1]; + } +skip_mpu: #endif #ifdef CONFIG_SOUND_CMPCI_JOYSTICK /* enable joystick */ --- linux-2.6.9-rc1/sound/oss/cs4232.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/sound/oss/cs4232.c 2004-09-02 20:51:53.000000000 -0700 @@ -128,27 +128,33 @@ static void enable_xctrl(int baseio) outb(((unsigned char) (ENABLE_PINS | regd)), baseio + INDEX_DATA ); } -int __init probe_cs4232(struct address_info *hw_config, int isapnp_configured) +static int __init probe_cs4232(struct address_info *hw_config, int isapnp_configured) { int i, n; int base = hw_config->io_base, irq = hw_config->irq; int dma1 = hw_config->dma, dma2 = hw_config->dma2; + struct resource *ports; + + if (base == -1 || irq == -1 || dma1 == -1) { + printk(KERN_ERR "cs4232: dma, irq and io must be set.\n"); + return 0; + } /* * Verify that the I/O port range is free. */ - if (check_region(base, 4)) - { + ports = request_region(base, 4, "ad1848"); + if (!ports) { printk(KERN_ERR "cs4232.c: I/O port 0x%03x not free\n", base); return 0; } - if (ad1848_detect(hw_config->io_base, NULL, hw_config->osp)) { - return 1; /* The card is already active */ + if (ad1848_detect(ports, NULL, hw_config->osp)) { + goto got_it; /* The card is already active */ } if (isapnp_configured) { printk(KERN_ERR "cs4232.c: ISA PnP configured, but not detected?\n"); - return 0; + goto fail; } /* @@ -241,30 +247,20 @@ int __init probe_cs4232(struct address_i * Then try to detect the codec part of the chip */ - if (ad1848_detect(hw_config->io_base, NULL, hw_config->osp)) - return 1; + if (ad1848_detect(ports, NULL, hw_config->osp)) + goto got_it; sleep(HZ); } +fail: + release_region(base, 4); return 0; -} - -void __init attach_cs4232(struct address_info *hw_config) -{ - int base = hw_config->io_base, - irq = hw_config->irq, - dma1 = hw_config->dma, - dma2 = hw_config->dma2; - - if (base == -1 || irq == -1 || dma1 == -1) { - printk(KERN_ERR "cs4232: dma, irq and io must be set.\n"); - return; - } +got_it: if (dma2 == -1) dma2 = dma1; - hw_config->slots[0] = ad1848_init("Crystal audio controller", base, + hw_config->slots[0] = ad1848_init("Crystal audio controller", ports, irq, dma1, /* Playback DMA */ dma2, /* Capture DMA */ @@ -308,9 +304,9 @@ void __init attach_cs4232(struct address } if (bss) - { enable_xctrl(base); - } + + return 1; } static void __devexit unload_cs4232(struct address_info *hw_config) @@ -423,7 +419,6 @@ static int cs4232_pnp_probe(struct pnp_d kfree(isapnpcfg); return -ENODEV; } - attach_cs4232(isapnpcfg); pnp_set_drvdata(dev,isapnpcfg); return 0; } @@ -486,7 +481,6 @@ static int __init init_cs4232(void) if (probe_cs4232(&cfg,FALSE) == 0) return -ENODEV; - attach_cs4232(&cfg); return 0; } --- linux-2.6.9-rc1/sound/oss/cs4232.h 2004-08-24 00:02:23.000000000 -0700 +++ 25/sound/oss/cs4232.h 2004-09-02 20:51:53.000000000 -0700 @@ -1,5 +1,3 @@ -int probe_cs4232 (struct address_info *hw_config,int useisapnp); -void attach_cs4232 (struct address_info *hw_config); int probe_cs4232_mpu (struct address_info *hw_config); void attach_cs4232_mpu (struct address_info *hw_config); --- linux-2.6.9-rc1/sound/oss/cs46xx.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/sound/oss/cs46xx.c 2004-09-03 00:56:41.039921632 -0700 @@ -94,7 +94,6 @@ #include #include #include -#include #include "cs46xxpm-24.h" #include "cs46xx_wrapper-24.h" @@ -2096,7 +2095,7 @@ static ssize_t cs_read(struct file *file unsigned copied=0; CS_DBGOUT(CS_WAVE_READ | CS_FUNCTION, 4, - printk("cs46xx: cs_read()+ %d\n",count) ); + printk("cs46xx: cs_read()+ %zd\n",count) ); state = (struct cs_state *)card->states[0]; if(!state) return -ENODEV; @@ -2157,9 +2156,9 @@ static ssize_t cs_read(struct file *file } CS_DBGOUT(CS_WAVE_READ, 2, printk(KERN_INFO - "_read() copy_to cnt=%d count=%d ", cnt,count) ); + "_read() copy_to cnt=%d count=%zd ", cnt,count) ); CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO - " .dmasize=%d .count=%d buffer=%p ret=%d\n", + " .dmasize=%d .count=%d buffer=%p ret=%zd\n", dmabuf->dmasize,dmabuf->count,buffer,ret) ); if (cs_copy_to_user(state, buffer, @@ -2184,7 +2183,7 @@ out2: up(&state->sem); set_current_state(TASK_RUNNING); CS_DBGOUT(CS_WAVE_READ | CS_FUNCTION, 4, - printk("cs46xx: cs_read()- %d\n",ret) ); + printk("cs46xx: cs_read()- %zd\n",ret) ); return ret; } @@ -2202,7 +2201,7 @@ static ssize_t cs_write(struct file *fil int cnt; CS_DBGOUT(CS_WAVE_WRITE | CS_FUNCTION, 4, - printk("cs46xx: cs_write called, count = %d\n", count) ); + printk("cs46xx: cs_write called, count = %zd\n", count) ); state = (struct cs_state *)card->states[1]; if(!state) return -ENODEV; @@ -2309,7 +2308,7 @@ out: set_current_state(TASK_RUNNING); CS_DBGOUT(CS_WAVE_WRITE | CS_FUNCTION, 2, - printk("cs46xx: cs_write()- ret=0x%x\n", ret) ); + printk("cs46xx: cs_write()- ret=%zd\n", ret) ); return ret; } --- linux-2.6.9-rc1/sound/oss/emu10k1/midi.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/sound/oss/emu10k1/midi.c 2004-09-02 20:51:53.000000000 -0700 @@ -320,7 +320,6 @@ static ssize_t emu10k1_midi_write(struct { struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) file->private_data; struct midi_hdr *midihdr; - ssize_t ret = 0; unsigned long flags; DPD(4, "emu10k1_midi_write(), count=%#x\n", (u32) count); @@ -344,7 +343,7 @@ static ssize_t emu10k1_midi_write(struct if (copy_from_user(midihdr->data, buffer, count)) { kfree(midihdr->data); kfree(midihdr); - return ret ? ret : -EFAULT; + return -EFAULT; } spin_lock_irqsave(&midi_spinlock, flags); --- linux-2.6.9-rc1/sound/oss/forte.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/sound/oss/forte.c 2004-09-03 00:56:41.041921328 -0700 @@ -45,7 +45,6 @@ #include #include -#include #include #define DRIVER_NAME "forte" --- linux-2.6.9-rc1/sound/oss/gus_card.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/sound/oss/gus_card.c 2004-09-02 20:51:53.000000000 -0700 @@ -160,22 +160,29 @@ irqreturn_t gusintr(int irq, void *dev_i #ifdef CONFIG_SOUND_GUS16 -static int __init probe_gus_db16(struct address_info *hw_config) +static int __init init_gus_db16(struct address_info *hw_config) { - return ad1848_detect(hw_config->io_base, NULL, hw_config->osp); -} + struct resource *ports; + + ports = request_region(hw_config->io_base, 4, "ad1848"); + if (!ports) + return 0; + + if (!ad1848_detect(ports, NULL, hw_config->osp)) { + release_region(hw_config->io_base, 4); + return 0; + } -static void __init attach_gus_db16(struct address_info *hw_config) -{ gus_pcm_volume = 100; gus_wave_volume = 90; - hw_config->slots[3] = ad1848_init("GUS 16 bit sampling", hw_config->io_base, + hw_config->slots[3] = ad1848_init("GUS 16 bit sampling", ports, hw_config->irq, hw_config->dma, hw_config->dma, 0, hw_config->osp, THIS_MODULE); + return 1; } static void __exit unload_gus_db16(struct address_info *hw_config) @@ -244,11 +251,8 @@ static int __init init_gus(void) } #ifdef CONFIG_SOUND_GUS16 - if (probe_gus_db16(&cfg) && gus16) { - /* FIXME: This can't work, can it ? -- Christoph */ - attach_gus_db16(&cfg); + if (gus16 && init_gus_db16(&cfg)) db16 = 1; - } #endif if (!probe_gus(&cfg)) return -ENODEV; --- linux-2.6.9-rc1/sound/oss/gus_wave.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/sound/oss/gus_wave.c 2004-09-02 20:51:53.000000000 -0700 @@ -2942,6 +2942,8 @@ void __init gus_wave_init(struct address } else { + struct resource *ports; + ports = request_region(gus_base + 0x10c, 4, "ad1848"); model_num = "MAX"; gus_type = 0x40; mixer_type = CS4231; @@ -2963,7 +2965,10 @@ void __init gus_wave_init(struct address outb((max_config), gus_base + 0x106); /* UltraMax control */ } - if (ad1848_detect(gus_base + 0x10c, &ad_flags, hw_config->osp)) + if (!ports) + goto no_cs4231; + + if (ad1848_detect(ports, &ad_flags, hw_config->osp)) { char *name = "GUS MAX"; int old_num_mixers = num_mixers; @@ -2977,7 +2982,7 @@ void __init gus_wave_init(struct address if (hw_config->name) name = hw_config->name; - hw_config->slots[1] = ad1848_init(name, gus_base + 0x10c, + hw_config->slots[1] = ad1848_init(name, ports, -irq, gus_dma2, /* Playback DMA */ gus_dma, /* Capture DMA */ 1, /* Share DMA channels with GF1 */ @@ -2992,8 +2997,11 @@ void __init gus_wave_init(struct address AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE); } } - else + else { + release_region(gus_base + 0x10c, 4); + no_cs4231: printk(KERN_WARNING "GUS: No CS4231 ??"); + } #else printk(KERN_ERR "GUS MAX found, but not compiled in\n"); #endif --- linux-2.6.9-rc1/sound/oss/harmony.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/sound/oss/harmony.c 2004-09-02 20:51:53.000000000 -0700 @@ -8,7 +8,7 @@ On older 715 machines you'll find the technically identical chip called 'Vivace'. Both Harmony and Vicace are supported by this driver. - Copyright 2000 (c) Linuxcare Canada, Alex deVries + Copyright 2000 (c) Linuxcare Canada, Alex deVries Copyright 2000-2003 (c) Helge Deller Copyright 2001 (c) Matthieu Delahaye Copyright 2001 (c) Jean-Christophe Vaugeois @@ -1321,7 +1321,7 @@ static void __exit cleanup_harmony(void) } -MODULE_AUTHOR("Alex DeVries "); +MODULE_AUTHOR("Alex DeVries "); MODULE_DESCRIPTION("Harmony sound driver"); MODULE_LICENSE("GPL"); --- linux-2.6.9-rc1/sound/oss/i810_audio.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/sound/oss/i810_audio.c 2004-09-03 00:56:41.043921024 -0700 @@ -101,7 +101,6 @@ #include #include #include -#include #define DRIVER_VERSION "1.01" --- linux-2.6.9-rc1/sound/oss/ite8172.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/sound/oss/ite8172.c 2004-09-03 00:56:41.045920720 -0700 @@ -70,10 +70,10 @@ #include #include #include +#include #include #include #include -#include #include /* --------------------------------------------------------------------- */ --- linux-2.6.9-rc1/sound/oss/kahlua.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/sound/oss/kahlua.c 2004-09-02 20:51:53.000000000 -0700 @@ -156,10 +156,14 @@ static int __devinit probe_one(struct pc hw_config->dma2 = dma16; hw_config->name = "Cyrix XpressAudio"; hw_config->driver_use_1 = SB_NO_MIDI | SB_PCI_IRQ; + + if (!request_region(io, 16, "soundblaster")) + goto err_out_free; if(sb_dsp_detect(hw_config, 0, 0, NULL)==0) { printk(KERN_ERR "kahlua: audio not responding.\n"); + release_region(io, 16); goto err_out_free; } --- linux-2.6.9-rc1/sound/oss/Kconfig 2004-08-24 00:03:31.000000000 -0700 +++ 25/sound/oss/Kconfig 2004-09-02 20:51:53.000000000 -0700 @@ -127,8 +127,8 @@ config SOUND_ES1371 . config SOUND_ESSSOLO1 - tristate "ESS Technology Solo1" - depends on SOUND_PRIME!=n && SOUND && SOUND_GAMEPORT + tristate "ESS Technology Solo1" + depends on SOUND_PRIME!=n && SOUND && SOUND_GAMEPORT && PCI help Say Y or M if you have a PCI sound card utilizing the ESS Technology Solo1 chip. To find out if your sound card uses a @@ -139,7 +139,7 @@ config SOUND_ESSSOLO1 config SOUND_MAESTRO tristate "ESS Maestro, Maestro2, Maestro2E driver" - depends on SOUND_PRIME!=n && SOUND + depends on SOUND_PRIME!=n && SOUND && PCI help Say Y or M if you have a sound system driven by ESS's Maestro line of PCI sound chips. These include the Maestro 1, Maestro 2, and --- linux-2.6.9-rc1/sound/oss/mad16.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/sound/oss/mad16.c 2004-09-02 20:51:53.000000000 -0700 @@ -54,7 +54,6 @@ static int mad16_conf; static int mad16_cdsel; static struct gameport gameport; static spinlock_t lock=SPIN_LOCK_UNLOCKED; -static int already_initialized; #define C928 1 #define MOZART 2 @@ -313,19 +312,6 @@ static int __init detect_mad16(void) static int __init wss_init(struct address_info *hw_config) { - int ad_flags = 0; - - /* - * Verify the WSS parameters - */ - - if (check_region(hw_config->io_base, 8)) - { - printk(KERN_ERR "MSS: I/O port conflict\n"); - return 0; - } - if (!ad1848_detect(hw_config->io_base + 4, &ad_flags, mad16_osp)) - return 0; /* * Check if the IO port returns valid signature. The original MS Sound * system returns 0x04 while some cards (AudioTrix Pro for example) @@ -338,31 +324,20 @@ static int __init wss_init(struct addres DDB(printk("No MSS signature detected on port 0x%x (0x%x)\n", hw_config->io_base, inb(hw_config->io_base + 3))); return 0; } - if (hw_config->irq > 11) - { - printk(KERN_ERR "MSS: Bad IRQ %d\n", hw_config->irq); - return 0; - } - if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) - { - printk(KERN_ERR "MSS: Bad DMA %d\n", hw_config->dma); - return 0; - } /* * Check that DMA0 is not in use with a 8 bit board. */ - if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80) { printk("MSS: Can't use DMA0 with a 8 bit card/slot\n"); return 0; } - if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80) + if (hw_config->irq > 9 && inb(hw_config->io_base + 3) & 0x80) printk(KERN_ERR "MSS: Can't use IRQ%d with a 8 bit card/slot\n", hw_config->irq); return 1; } -static int __init init_c930(struct address_info *hw_config) +static void __init init_c930(struct address_info *hw_config, int base) { unsigned char cfg = 0; @@ -376,25 +351,7 @@ static int __init init_c930(struct addre somewhere else. */ cfg = (cfg & 0x09) ^ 0x07; } - - switch (hw_config->io_base) - { - case 0x530: - cfg |= 0x00; - break; - case 0xe80: - cfg |= 0x10; - break; - case 0xf40: - cfg |= 0x20; - break; - case 0x604: - cfg |= 0x30; - break; - default: - printk(KERN_ERR "MAD16: Invalid codec port %x\n", hw_config->io_base); - return 0; - } + cfg |= base << 4; mad_write(MC1_PORT, cfg); /* MC2 is CD configuration. Don't touch it. */ @@ -414,8 +371,6 @@ static int __init init_c930(struct addre mad_write(MC6_PORT, 0x02); /* Enable WSS, Disable MPU and SB */ mad_write(MC7_PORT, 0xCB); mad_write(MC10_PORT, 0x11); - - return wss_init(hw_config); } static int __init chip_detect(void) @@ -508,20 +463,48 @@ static int __init chip_detect(void) static int __init probe_mad16(struct address_info *hw_config) { int i; - static int valid_ports[] = - { - 0x530, 0xe80, 0xf40, 0x604 - }; unsigned char tmp; unsigned char cs4231_mode = 0; int ad_flags = 0; - if (already_initialized) - return 0; + signed char bits; + + static char dma_bits[4] = { + 1, 2, 0, 3 + }; + + int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3; + int dma = hw_config->dma, dma2 = hw_config->dma2; + unsigned char dma2_bit = 0; + int base; + struct resource *ports; mad16_osp = hw_config->osp; + switch (hw_config->io_base) { + case 0x530: + base = 0; + break; + case 0xe80: + base = 1; + break; + case 0xf40: + base = 2; + break; + case 0x604: + base = 3; + break; + default: + printk(KERN_ERR "MAD16/Mozart: Bad WSS base address 0x%x\n", hw_config->io_base); + return 0; + } + + if (dma != 0 && dma != 1 && dma != 3) { + printk(KERN_ERR "MSS: Bad DMA %d\n", dma); + return 0; + } + /* * Check that all ports return 0xff (bus float) when no password * is written to the password register. @@ -531,9 +514,44 @@ static int __init probe_mad16(struct add if (!chip_detect()) return 0; - if (board_type == C930) - return init_c930(hw_config); + switch (hw_config->irq) { + case 7: + bits = 8; + break; + case 9: + bits = 0x10; + break; + case 10: + bits = 0x18; + break; + case 12: + bits = 0x20; + break; + case 5: /* Also IRQ5 is possible on C930 */ + if (board_type == C930 || c924pnp) { + bits = 0x28; + break; + } + default: + printk(KERN_ERR "MAD16/Mozart: Bad IRQ %d\n", hw_config->irq); + return 0; + } + + ports = request_region(hw_config->io_base + 4, 4, "ad1848"); + if (!ports) { + printk(KERN_ERR "MSS: I/O port conflict\n"); + return 0; + } + if (!request_region(hw_config->io_base, 4, "mad16 WSS config")) { + release_region(hw_config->io_base + 4, 4); + printk(KERN_ERR "MSS: I/O port conflict\n"); + return 0; + } + if (board_type == C930) { + init_c930(hw_config, base); + goto got_it; + } for (i = 0xf8d; i <= 0xf93; i++) { if (!c924pnp) @@ -547,20 +565,7 @@ static int __init probe_mad16(struct add */ tmp = (mad_read(MC1_PORT) & 0x0f) | 0x80; /* Enable WSS, Disable SB */ - - for (i = 0; i < 5; i++) - { - if (i > 3) /* Not a valid port */ - { - printk(KERN_ERR "MAD16/Mozart: Bad WSS base address 0x%x\n", hw_config->io_base); - return 0; - } - if (valid_ports[i] == hw_config->io_base) - { - tmp |= i << 4; /* WSS port select bits */ - break; - } - } + tmp |= base << 4; /* WSS port select bits */ /* * Set optional CD-ROM and joystick settings. @@ -580,8 +585,8 @@ static int __init probe_mad16(struct add mad_write(MC5_PORT, 0x05); mad_write(MC6_PORT, 0x03); } - if (!ad1848_detect(hw_config->io_base + 4, &ad_flags, mad16_osp)) - return 0; + if (!ad1848_detect(ports, &ad_flags, mad16_osp)) + goto fail; if (ad_flags & (AD_F_CS4231 | AD_F_CS4248)) cs4231_mode = 0x02; /* CS4248/CS4231 sync delay switch */ @@ -604,43 +609,19 @@ static int __init probe_mad16(struct add else DDB(printk("port %03x after init = %02x\n", i-0x80, mad_read(i))); } - wss_init(hw_config); - return 1; -} +got_it: + ad_flags = 0; + if (!ad1848_detect(ports, &ad_flags, mad16_osp)) + goto fail; -static void __init attach_mad16(struct address_info *hw_config) -{ - - static signed char interrupt_bits[12] = { - -1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20 - }; - signed char bits; - - static char dma_bits[4] = { - 1, 2, 0, 3 - }; - - int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3; - int ad_flags = 0, dma = hw_config->dma, dma2 = hw_config->dma2; - unsigned char dma2_bit = 0; - - already_initialized = 1; - - if (!ad1848_detect(hw_config->io_base + 4, &ad_flags, mad16_osp)) - return; + if (!wss_init(hw_config)) + goto fail; /* * Set the IRQ and DMA addresses. */ - if (board_type == C930 || c924pnp) - interrupt_bits[5] = 0x28; /* Also IRQ5 is possible on C930 */ - - bits = interrupt_bits[hw_config->irq]; - if (bits == -1) - return; - outb((bits | 0x40), config_port); if ((inb(version_port) & 0x40) == 0) printk(KERN_ERR "[IRQ Conflict?]\n"); @@ -675,27 +656,24 @@ static void __init attach_mad16(struct a outb((bits | dma_bits[dma] | dma2_bit), config_port); /* Write IRQ+DMA setup */ - hw_config->slots[0] = ad1848_init("mad16 WSS", hw_config->io_base + 4, + hw_config->slots[0] = ad1848_init("mad16 WSS", ports, hw_config->irq, dma, dma2, 0, hw_config->osp, THIS_MODULE); - request_region(hw_config->io_base, 4, "mad16 WSS config"); + return 1; + +fail: + release_region(hw_config->io_base + 4, 4); + release_region(hw_config->io_base, 4); + return 0; } static int __init probe_mad16_mpu(struct address_info *hw_config) { - static int mpu_attached; unsigned char tmp; - if (!already_initialized) /* The MSS port must be initialized first */ - return 0; - - if (mpu_attached) /* Don't let them call this twice */ - return 0; - mpu_attached = 1; - if (board_type < C929) /* Early chip. No MPU support. Just SB MIDI */ { @@ -732,8 +710,12 @@ static int __init probe_mad16_mpu(struct mad_write(MC3_PORT, tmp | 0x04); hw_config->driver_use_1 = SB_MIDI_ONLY; - if (!sb_dsp_detect(hw_config, 0, 0, NULL)) + if (!request_region(hw_config->io_base, 16, "soundblaster")) + return 0; + if (!sb_dsp_detect(hw_config, 0, 0, NULL)) { + release_region(hw_config->io_base, 16); return 0; + } if (mad_read(MC1_PORT) & 0x20) hw_config->io_base = 0x240; @@ -1031,15 +1013,18 @@ static int __init init_mad16(void) printk(KERN_ERR "I/O, DMA and irq are mandatory\n"); return -EINVAL; } - - if (!probe_mad16(&cfg)) + + if (!request_region(MC0_PORT, 12, "mad16")) + return -EBUSY; + + if (!probe_mad16(&cfg)) { + release_region(MC0_PORT, 12); return -ENODEV; + } cfg_mpu.io_base = mpu_io; cfg_mpu.irq = mpu_irq; - attach_mad16(&cfg); - found_mpu = probe_mad16_mpu(&cfg_mpu); if (joystick == 1) { @@ -1067,6 +1052,7 @@ static void __exit cleanup_mad16(void) release_region(0x201, 1); } unload_mad16(&cfg); + release_region(MC0_PORT, 12); } module_init(init_mad16); --- linux-2.6.9-rc1/sound/oss/maui.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/sound/oss/maui.c 2004-09-02 20:51:53.000000000 -0700 @@ -301,17 +301,23 @@ static int maui_load_patch(int dev, int static int __init probe_maui(struct address_info *hw_config) { + struct resource *ports; + int this_dev; int i; int tmp1, tmp2, ret; - if (check_region(hw_config->io_base, 8)) + ports = request_region(hw_config->io_base, 2, "mpu401"); + if (!ports) return 0; + if (!request_region(hw_config->io_base + 2, 6, "Maui")) + goto out; + maui_base = hw_config->io_base; maui_osp = hw_config->osp; if (request_irq(hw_config->irq, mauiintr, 0, "Maui", NULL) < 0) - return 0; + goto out2; /* * Initialize the processor if necessary @@ -322,36 +328,30 @@ static int __init probe_maui(struct addr !maui_write(0x9F) || /* Report firmware version */ !maui_short_wait(STAT_RX_AVAIL) || maui_read() == -1 || maui_read() == -1) - if (!maui_init(hw_config->irq)) { - free_irq(hw_config->irq, NULL); - return 0; - } + if (!maui_init(hw_config->irq)) + goto out3; } if (!maui_write(0xCF)) /* Report hardware version */ { printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n"); - free_irq(hw_config->irq, NULL); - return 0; + goto out3; } if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1) { printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n"); - free_irq(hw_config->irq, NULL); - return 0; - } - if (tmp1 == 0xff || tmp2 == 0xff) { - free_irq(hw_config->irq, NULL); - return 0; + goto out3; } + if (tmp1 == 0xff || tmp2 == 0xff) + goto out3; printk(KERN_DEBUG "WaveFront hardware version %d.%d\n", tmp1, tmp2); if (!maui_write(0x9F)) /* Report firmware version */ - return 0; + goto out3; if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1) - return 0; + goto out3; printk(KERN_DEBUG "WaveFront firmware version %d.%d\n", tmp1, tmp2); if (!maui_write(0x85)) /* Report free DRAM */ - return 0; + goto out3; tmp1 = 0; for (i = 0; i < 4; i++) { tmp1 |= maui_read() << (7 * i); @@ -359,20 +359,12 @@ static int __init probe_maui(struct addr printk(KERN_DEBUG "Available DRAM %dk\n", tmp1 / 1024); for (i = 0; i < 1000; i++) - if (probe_mpu401(hw_config)) + if (probe_mpu401(hw_config, ports)) break; - ret = probe_mpu401(hw_config); - - if (ret) - request_region(hw_config->io_base + 2, 6, "Maui"); - - return ret; -} - -static void __init attach_maui(struct address_info *hw_config) -{ - int this_dev; + ret = probe_mpu401(hw_config, ports); + if (!ret) + goto out3; conf_printf("Maui", hw_config); @@ -391,14 +383,22 @@ static void __init attach_maui(struct ad */ synth = midi_devs[this_dev]->converter; - synth->id = "MAUI"; - if (synth != NULL) { + synth->id = "MAUI"; orig_load_patch = synth->load_patch; synth->load_patch = &maui_load_patch; } else printk(KERN_ERR "Maui: Can't install patch loader\n"); } + return 1; + +out3: + free_irq(hw_config->irq, NULL); +out2: + release_region(hw_config->io_base + 2, 6); +out: + release_region(hw_config->io_base, 2); + return 0; } static void __exit unload_maui(struct address_info *hw_config) @@ -445,7 +445,6 @@ static int __init init_maui(void) } if (probe_maui(&cfg) == 0) return -ENODEV; - attach_maui(&cfg); return 0; } --- linux-2.6.9-rc1/sound/oss/mpu401.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/sound/oss/mpu401.c 2004-09-02 20:51:53.000000000 -0700 @@ -1026,12 +1026,6 @@ int attach_mpu401(struct address_info *h spin_unlock_irqrestore(&devc->lock,flags); } - if (!request_region(hw_config->io_base, 2, "mpu401")) - { - ret = -ENOMEM; - goto out_irq; - } - if (devc->version != 0) if (mpu_cmd(m, 0xC5, 0) >= 0) /* Set timebase OK */ if (mpu_cmd(m, 0xE0, 120) >= 0) /* Set tempo OK */ @@ -1044,7 +1038,7 @@ int attach_mpu401(struct address_info *h { printk(KERN_ERR "mpu401: Can't allocate memory\n"); ret = -ENOMEM; - goto out_resource; + goto out_irq; } if (!(devc->capabilities & MPU_CAP_INTLG)) /* No intelligent mode */ { @@ -1126,13 +1120,12 @@ int attach_mpu401(struct address_info *h return 0; -out_resource: - release_region(hw_config->io_base, 2); out_irq: free_irq(devc->irq, (void *)m); out_mididev: sound_unload_mididev(m); out_err: + release_region(hw_config->io_base, 2); return ret; } @@ -1207,16 +1200,11 @@ static void set_uart_mode(int dev, struc } -int probe_mpu401(struct address_info *hw_config) +int probe_mpu401(struct address_info *hw_config, struct resource *ports) { int ok = 0; struct mpu_config tmp_devc; - if (check_region(hw_config->io_base, 2)) - { - printk(KERN_ERR "mpu401: I/O port %x already in use\n\n", hw_config->io_base); - return 0; - } tmp_devc.base = hw_config->io_base; tmp_devc.irq = hw_config->irq; tmp_devc.initialized = 0; @@ -1791,10 +1779,16 @@ static int __init init_mpu401(void) /* Can be loaded either for module use or to provide functions to others */ if (io != -1 && irq != -1) { + struct resource *ports; cfg.irq = irq; cfg.io_base = io; - if (probe_mpu401(&cfg) == 0) + ports = request_region(io, 2, "mpu401"); + if (!ports) + return -EBUSY; + if (probe_mpu401(&cfg, ports) == 0) { + release_region(io, 2); return -ENODEV; + } if ((ret = attach_mpu401(&cfg, THIS_MODULE))) return ret; } --- linux-2.6.9-rc1/sound/oss/mpu401.h 2004-08-24 00:02:32.000000000 -0700 +++ 25/sound/oss/mpu401.h 2004-09-02 20:51:53.000000000 -0700 @@ -6,7 +6,7 @@ void unload_uart401 (struct address_info irqreturn_t uart401intr (int irq, void *dev_id, struct pt_regs * dummy); /* From mpu401.c */ -int probe_mpu401(struct address_info *hw_config); +int probe_mpu401(struct address_info *hw_config, struct resource *ports); int attach_mpu401(struct address_info * hw_config, struct module *owner); void unload_mpu401(struct address_info *hw_info); --- linux-2.6.9-rc1/sound/oss/msnd.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/sound/oss/msnd.c 2004-09-02 20:51:53.000000000 -0700 @@ -207,7 +207,7 @@ int msnd_wait_TXDE(multisound_dev_t *dev register int timeout = 1000; while(timeout-- > 0) - if (inb(io + HP_ISR) & HPISR_TXDE) + if (msnd_inb(io + HP_ISR) & HPISR_TXDE) return 0; return -EIO; @@ -219,7 +219,7 @@ int msnd_wait_HC0(multisound_dev_t *dev) register int timeout = 1000; while(timeout-- > 0) - if (!(inb(io + HP_CVR) & HPCVR_HC)) + if (!(msnd_inb(io + HP_CVR) & HPCVR_HC)) return 0; return -EIO; @@ -231,7 +231,7 @@ int msnd_send_dsp_cmd(multisound_dev_t * spin_lock_irqsave(&dev->lock, flags); if (msnd_wait_HC0(dev) == 0) { - outb(cmd, dev->io + HP_CVR); + msnd_outb(cmd, dev->io + HP_CVR); spin_unlock_irqrestore(&dev->lock, flags); return 0; } @@ -248,9 +248,9 @@ int msnd_send_word(multisound_dev_t *dev register unsigned int io = dev->io; if (msnd_wait_TXDE(dev) == 0) { - outb(high, io + HP_TXH); - outb(mid, io + HP_TXM); - outb(low, io + HP_TXL); + msnd_outb(high, io + HP_TXH); + msnd_outb(mid, io + HP_TXM); + msnd_outb(low, io + HP_TXL); return 0; } @@ -272,8 +272,8 @@ int msnd_upload_host(multisound_dev_t *d if (msnd_send_word(dev, bin[i], bin[i + 1], bin[i + 2]) != 0) return -EIO; - inb(dev->io + HP_RXL); - inb(dev->io + HP_CVR); + msnd_inb(dev->io + HP_RXL); + msnd_inb(dev->io + HP_CVR); return 0; } @@ -289,11 +289,11 @@ int msnd_enable_irq(multisound_dev_t *de spin_lock_irqsave(&dev->lock, flags); if (msnd_wait_TXDE(dev) == 0) { - outb(inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR); + msnd_outb(msnd_inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR); if (dev->type == msndClassic) - outb(dev->irqid, dev->io + HP_IRQM); - outb(inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR); - outb(inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR); + msnd_outb(dev->irqid, dev->io + HP_IRQM); + msnd_outb(msnd_inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR); + msnd_outb(msnd_inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR); enable_irq(dev->irq); msnd_init_queue(dev->DSPQ, dev->dspq_data_buff, dev->dspq_buff_size); spin_unlock_irqrestore(&dev->lock, flags); @@ -320,9 +320,9 @@ int msnd_disable_irq(multisound_dev_t *d spin_lock_irqsave(&dev->lock, flags); if (msnd_wait_TXDE(dev) == 0) { - outb(inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR); + msnd_outb(msnd_inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR); if (dev->type == msndClassic) - outb(HPIRQ_NONE, dev->io + HP_IRQM); + msnd_outb(HPIRQ_NONE, dev->io + HP_IRQM); disable_irq(dev->irq); spin_unlock_irqrestore(&dev->lock, flags); return 0; --- linux-2.6.9-rc1/sound/oss/msnd.h 2004-08-24 00:02:48.000000000 -0700 +++ 25/sound/oss/msnd.h 2004-09-02 20:51:53.000000000 -0700 @@ -154,10 +154,11 @@ #define DSPTOPC_BASED(w) (((w) - DSP_BASE_ADDR) * 2) #ifdef SLOWIO -# undef outb -# undef inb -# define outb outb_p -# define inb inb_p +#define msnd_outb outb_p +#define msnd_inb inb_p +#else +#define msnd_outb outb +#define msnd_inb inb #endif /* JobQueueStruct */ --- linux-2.6.9-rc1/sound/oss/msnd_pinnacle.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/sound/oss/msnd_pinnacle.c 2004-09-02 20:51:53.000000000 -0700 @@ -136,9 +136,9 @@ static void reset_record_queue(void) /* Critical section: bank 1 access */ spin_lock_irqsave(&dev.lock, flags); - outb(HPBLKSEL_1, dev.io + HP_BLKS); + msnd_outb(HPBLKSEL_1, dev.io + HP_BLKS); isa_memset_io(dev.base, 0, DAR_BUFF_SIZE * 3); - outb(HPBLKSEL_0, dev.io + HP_BLKS); + msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS); spin_unlock_irqrestore(&dev.lock, flags); for (n = 0, lpDAQ = dev.base + DARQ_DATA_BUFF; n < 3; ++n, lpDAQ += DAQDS__size) { @@ -830,12 +830,12 @@ static __inline__ int pack_DARQ_to_DARF( /* Read data from the head (unprotected bank 1 access okay since this is only called inside an interrupt) */ - outb(HPBLKSEL_1, dev.io + HP_BLKS); + msnd_outb(HPBLKSEL_1, dev.io + HP_BLKS); msnd_fifo_write( &dev.DARF, (char *)(dev.base + bank * DAR_BUFF_SIZE), size); - outb(HPBLKSEL_0, dev.io + HP_BLKS); + msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS); return 1; } @@ -1091,7 +1091,7 @@ static __inline__ void eval_dsp_msg(regi static irqreturn_t intr(int irq, void *dev_id, struct pt_regs *regs) { /* Send ack to DSP */ - inb(dev.io + HP_RXL); + msnd_inb(dev.io + HP_RXL); /* Evaluate queued DSP messages */ while (isa_readw(dev.DSPQ + JQS_wTail) != isa_readw(dev.DSPQ + JQS_wHead)) { @@ -1120,15 +1120,15 @@ static int reset_dsp(void) { int timeout = 100; - outb(HPDSPRESET_ON, dev.io + HP_DSPR); + msnd_outb(HPDSPRESET_ON, dev.io + HP_DSPR); mdelay(1); #ifndef MSND_CLASSIC - dev.info = inb(dev.io + HP_INFO); + dev.info = msnd_inb(dev.io + HP_INFO); #endif - outb(HPDSPRESET_OFF, dev.io + HP_DSPR); + msnd_outb(HPDSPRESET_OFF, dev.io + HP_DSPR); mdelay(1); while (timeout-- > 0) { - if (inb(dev.io + HP_CVR) == HP_CVR_DEF) + if (msnd_inb(dev.io + HP_CVR) == HP_CVR_DEF) return 0; mdelay(1); } @@ -1202,9 +1202,9 @@ static int init_sma(void) unsigned long flags; #ifdef MSND_CLASSIC - outb(dev.memid, dev.io + HP_MEMM); + msnd_outb(dev.memid, dev.io + HP_MEMM); #endif - outb(HPBLKSEL_0, dev.io + HP_BLKS); + msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS); if (initted) { mastVolLeft = isa_readw(dev.SMA + SMA_wCurrMastVolLeft); mastVolRight = isa_readw(dev.SMA + SMA_wCurrMastVolRight); @@ -1214,9 +1214,9 @@ static int init_sma(void) /* Critical section: bank 1 access */ spin_lock_irqsave(&dev.lock, flags); - outb(HPBLKSEL_1, dev.io + HP_BLKS); + msnd_outb(HPBLKSEL_1, dev.io + HP_BLKS); isa_memset_io(dev.base, 0, 0x8000); - outb(HPBLKSEL_0, dev.io + HP_BLKS); + msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS); spin_unlock_irqrestore(&dev.lock, flags); dev.pwDSPQData = (dev.base + DSPQ_DATA_BUFF); @@ -1289,7 +1289,7 @@ static int __init calibrate_adc(WORD sra static int upload_dsp_code(void) { - outb(HPBLKSEL_0, dev.io + HP_BLKS); + msnd_outb(HPBLKSEL_0, dev.io + HP_BLKS); #ifndef HAVE_DSPCODEH INITCODESIZE = mod_firmware_load(INITCODEFILE, &INITCODE); if (!INITCODE) { @@ -1326,9 +1326,9 @@ static int upload_dsp_code(void) #ifdef MSND_CLASSIC static void reset_proteus(void) { - outb(HPPRORESET_ON, dev.io + HP_PROR); + msnd_outb(HPPRORESET_ON, dev.io + HP_PROR); mdelay(TIME_PRO_RESET); - outb(HPPRORESET_OFF, dev.io + HP_PROR); + msnd_outb(HPPRORESET_OFF, dev.io + HP_PROR); mdelay(TIME_PRO_RESET_DONE); } #endif @@ -1338,8 +1338,8 @@ static int initialize(void) int err, timeout; #ifdef MSND_CLASSIC - outb(HPWAITSTATE_0, dev.io + HP_WAIT); - outb(HPBITMODE_16, dev.io + HP_BITM); + msnd_outb(HPWAITSTATE_0, dev.io + HP_WAIT); + msnd_outb(HPBITMODE_16, dev.io + HP_BITM); reset_proteus(); #endif @@ -1455,9 +1455,9 @@ static void __exit unload_multisound(voi static int __init msnd_write_cfg(int cfg, int reg, int value) { - outb(reg, cfg); - outb(value, cfg + 1); - if (value != inb(cfg + 1)) { + msnd_outb(reg, cfg); + msnd_outb(value, cfg + 1); + if (value != msnd_inb(cfg + 1)) { printk(KERN_ERR LOGNAME ": msnd_write_cfg: I/O error\n"); return -EIO; } --- linux-2.6.9-rc1/sound/oss/nec_vrc5477.c 2004-08-24 00:03:28.000000000 -0700 +++ 25/sound/oss/nec_vrc5477.c 2004-09-03 00:56:41.047920416 -0700 @@ -82,7 +82,6 @@ #include #include #include -#include /* -------------------debug macros -------------------------------------- */ /* #undef VRC5477_AC97_DEBUG */ --- linux-2.6.9-rc1/sound/oss/opl3sa2.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/sound/oss/opl3sa2.c 2004-09-02 20:51:53.000000000 -0700 @@ -539,36 +539,18 @@ static struct mixer_operations opl3sa3_m * Component probe, attach, unload functions */ -static inline int __init probe_opl3sa2_mpu(struct address_info* hw_config) -{ - return probe_mpu401(hw_config); -} - - -static inline int __init attach_opl3sa2_mpu(struct address_info* hw_config) -{ - return attach_mpu401(hw_config, THIS_MODULE); -} - - static inline void __exit unload_opl3sa2_mpu(struct address_info *hw_config) { unload_mpu401(hw_config); } -static inline int __init probe_opl3sa2_mss(struct address_info* hw_config) -{ - return probe_ms_sound(hw_config); -} - - -static void __init attach_opl3sa2_mss(struct address_info* hw_config) +static void __init attach_opl3sa2_mss(struct address_info* hw_config, struct resource *ports) { int initial_mixers; initial_mixers = num_mixers; - attach_ms_sound(hw_config, THIS_MODULE); /* Slot 0 */ + attach_ms_sound(hw_config, ports, THIS_MODULE); /* Slot 0 */ if (hw_config->slots[0] != -1) { /* Did the MSS driver install? */ if(num_mixers == (initial_mixers + 1)) { @@ -957,6 +939,8 @@ static int __init init_opl3sa2(void) for (card = 0; card < max; card++) { /* If a user wants an I/O then assume they meant it */ + struct resource *ports; + int base; if (!isapnp) { if (io == -1 || irq == -1 || dma == -1 || @@ -995,17 +979,30 @@ static int __init init_opl3sa2(void) opl3sa2_clear_slots(&opl3sa2_state[card].cfg_mpu); } + /* FIXME: leak */ if (probe_opl3sa2(&opl3sa2_state[card].cfg, card)) return -ENODEV; + base = opl3sa2_state[card].cfg_mss.io_base; - if (!probe_opl3sa2_mss(&opl3sa2_state[card].cfg_mss)) { + if (!request_region(base, 4, "WSS config")) + goto failed; + + ports = request_region(base + 4, 4, "ad1848"); + if (!ports) + goto failed2; + + if (!probe_ms_sound(&opl3sa2_state[card].cfg_mss, ports)) { /* * If one or more cards are already registered, don't * return an error but print a warning. Note, this * should never really happen unless the hardware or * ISA PnP screwed up. */ + release_region(base + 4, 4); + failed2: + release_region(base, 4); + failed: release_region(opl3sa2_state[card].cfg.io_base, 2); if (opl3sa2_cards_num) { @@ -1021,7 +1018,7 @@ static int __init init_opl3sa2(void) attach_opl3sa2(&opl3sa2_state[card].cfg, card); conf_printf(opl3sa2_state[card].chipset_name, &opl3sa2_state[card].cfg); attach_opl3sa2_mixer(&opl3sa2_state[card].cfg, card); - attach_opl3sa2_mss(&opl3sa2_state[card].cfg_mss); + attach_opl3sa2_mss(&opl3sa2_state[card].cfg_mss, ports); /* ewww =) */ opl3sa2_state[card].card = card; @@ -1054,15 +1051,23 @@ static int __init init_opl3sa2(void) /* Attach MPU if we've been asked to do so, failure isn't fatal */ if (opl3sa2_state[card].cfg_mpu.io_base != -1) { - if (probe_opl3sa2_mpu(&opl3sa2_state[card].cfg_mpu)) { - if (attach_opl3sa2_mpu(&opl3sa2_state[card].cfg_mpu)) { - printk(KERN_ERR PFX "failed to attach MPU401\n"); - opl3sa2_state[card].cfg_mpu.slots[1] = -1; - } + int base = opl3sa2_state[card].cfg_mpu.io_base; + struct resource *ports; + ports = request_region(base, 2, "mpu401"); + if (!ports) + goto out; + if (!probe_mpu401(&opl3sa2_state[card].cfg_mpu, ports)) { + release_region(base, 2); + goto out; + } + if (attach_mpu401(&opl3sa2_state[card].cfg_mpu, THIS_MODULE)) { + printk(KERN_ERR PFX "failed to attach MPU401\n"); + opl3sa2_state[card].cfg_mpu.slots[1] = -1; } } } +out: if (isapnp) { printk(KERN_NOTICE PFX "%d PnP card(s) found.\n", opl3sa2_cards_num); } --- linux-2.6.9-rc1/sound/oss/opl3sa.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/sound/oss/opl3sa.c 2004-09-02 20:51:53.000000000 -0700 @@ -36,10 +36,7 @@ static int sb_initialized; #endif -static int kilroy_was_here; /* Don't detect twice */ -static int mpu_initialized; static spinlock_t lock=SPIN_LOCK_UNLOCKED; -static int *opl3sa_osp; static unsigned char opl3sa_read(int addr) { @@ -106,26 +103,16 @@ static int __init opl3sa_detect(void) * OPL3-SA */ -static int __init probe_opl3sa_wss(struct address_info *hw_config) +static int __init probe_opl3sa_wss(struct address_info *hw_config, struct resource *ports) { - int ret; unsigned char tmp = 0x24; /* WSS enable */ - if (check_region(0xf86, 2)) /* Control port is busy */ - return 0; /* * Check if the IO port returns valid signature. The original MS Sound * system returns 0x04 while some cards (OPL3-SA for example) * return 0x00. */ - if (check_region(hw_config->io_base, 8)) - { - printk(KERN_ERR "OPL3-SA: MSS I/O port conflict (%x)\n", hw_config->io_base); - return 0; - } - opl3sa_osp = hw_config->osp; - if (!opl3sa_detect()) { printk(KERN_ERR "OSS: OPL3-SA chip not found\n"); @@ -152,21 +139,16 @@ static int __init probe_opl3sa_wss(struc } opl3sa_write(0x01, tmp); /* WSS setup register */ - kilroy_was_here = 1; - ret = probe_ms_sound(hw_config); - if (ret) - request_region(0xf86, 2, "OPL3-SA"); - - return ret; + return probe_ms_sound(hw_config, ports); } -static void __init attach_opl3sa_wss(struct address_info *hw_config) +static void __init attach_opl3sa_wss(struct address_info *hw_config, struct resource *ports) { int nm = num_mixers; /* FIXME */ - attach_ms_sound(hw_config, THIS_MODULE); + attach_ms_sound(hw_config, ports, THIS_MODULE); if (num_mixers > nm) /* A mixer was installed */ { AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_CD); @@ -183,14 +165,6 @@ static int __init probe_opl3sa_mpu(struc -1, -1, -1, -1, -1, 1, -1, 2, -1, 3, 4 }; - if (!kilroy_was_here) - return 0; /* OPL3-SA has not been detected earlier */ - - if (mpu_initialized) - { - DDB(printk("OPL3-SA: MPU mode already initialized\n")); - return 0; - } if (hw_config->irq > 10) { printk(KERN_ERR "OPL3-SA: Bad MPU IRQ %d\n", hw_config->irq); @@ -224,7 +198,6 @@ static int __init probe_opl3sa_mpu(struc opl3sa_write(0x03, conf); - mpu_initialized = 1; hw_config->name = "OPL3-SA (MPU401)"; return probe_uart401(hw_config, THIS_MODULE); @@ -281,6 +254,7 @@ MODULE_PARM(mpu_irq,"i"); static int __init init_opl3sa(void) { + struct resource *ports; if (io == -1 || irq == -1 || dma == -1) { printk(KERN_ERR "opl3sa: dma, irq and io must be set.\n"); return -EINVAL; @@ -294,12 +268,31 @@ static int __init init_opl3sa(void) cfg_mpu.io_base = mpu_io; cfg_mpu.irq = mpu_irq; - if (probe_opl3sa_wss(&cfg) == 0) + ports = request_region(io + 4, 4, "ad1848"); + if (!ports) + return -EBUSY; + + if (!request_region(0xf86, 2, "OPL3-SA"))/* Control port is busy */ { + release_region(io + 4, 4); + return 0; + } + + if (!request_region(io, 4, "WSS config")) { + release_region(0x86, 2); + release_region(io + 4, 4); + return 0; + } + + if (probe_opl3sa_wss(&cfg, ports) == 0) { + release_region(0xf86, 2); + release_region(io, 4); + release_region(io + 4, 4); return -ENODEV; + } found_mpu=probe_opl3sa_mpu(&cfg_mpu); - attach_opl3sa_wss(&cfg); + attach_opl3sa_wss(&cfg, ports); return 0; } --- linux-2.6.9-rc1/sound/oss/pss.c 2004-08-24 00:03:49.000000000 -0700 +++ 25/sound/oss/pss.c 2004-09-02 20:51:53.000000000 -0700 @@ -149,6 +149,7 @@ static int pss_initialized; static int nonstandard_microcode; static int pss_cdrom_port = -1; /* Parameter for the PSS cdrom port */ static int pss_enable_joystick; /* Parameter for enabling the joystick */ +static coproc_operations pss_coproc_operations; static void pss_write(pss_confdata *devc, int data) { @@ -174,7 +175,7 @@ static void pss_write(pss_confdata *devc printk(KERN_WARNING "PSS: DSP Command (%04x) Timeout.\n", data); } -int __init probe_pss(struct address_info *hw_config) +static int __init probe_pss(struct address_info *hw_config) { unsigned short id; int irq, dma; @@ -188,13 +189,19 @@ int __init probe_pss(struct address_info if (devc->base != 0x230 && devc->base != 0x250) /* Some cards use these */ return 0; - if (check_region(devc->base, 0x19 /*16*/)) { + if (!request_region(devc->base, 0x10, "PSS mixer, SB emulation")) { printk(KERN_ERR "PSS: I/O port conflict\n"); return 0; } id = inw(REG(PSS_ID)); if ((id >> 8) != 'E') { printk(KERN_ERR "No PSS signature detected at 0x%x (0x%x)\n", devc->base, id); + release_region(devc->base, 0x10); + return 0; + } + if (!request_region(devc->base + 0x10, 0x9, "PSS config")) { + printk(KERN_ERR "PSS: I/O port conflict\n"); + release_region(devc->base, 0x10); return 0; } return 1; @@ -685,7 +692,7 @@ void configure_nonsound_components(void) } } -void __init attach_pss(struct address_info *hw_config) +static int __init attach_pss(struct address_info *hw_config) { unsigned short id; char tmp[100]; @@ -697,10 +704,7 @@ void __init attach_pss(struct address_in devc->ad_mixer_dev = NO_WSS_MIXER; if (!probe_pss(hw_config)) - return; - - request_region(hw_config->io_base, 0x10, "PSS mixer, SB emulation"); - request_region(hw_config->io_base + 0x10, 0x9, "PSS config"); + return 0; id = inw(REG(PSS_ID)) & 0x00ff; @@ -714,17 +718,23 @@ void __init attach_pss(struct address_in if (sound_alloc_dma(hw_config->dma, "PSS")) { printk("pss.c: Can't allocate DMA channel.\n"); - return; + release_region(hw_config->io_base, 0x10); + release_region(hw_config->io_base+0x10, 0x9); + return 0; } if (!set_irq(devc, CONF_PSS, devc->irq)) { printk("PSS: IRQ allocation error.\n"); - return; + release_region(hw_config->io_base, 0x10); + release_region(hw_config->io_base+0x10, 0x9); + return 0; } if (!set_dma(devc, CONF_PSS, devc->dma)) { printk(KERN_ERR "PSS: DMA allocation error\n"); - return; + release_region(hw_config->io_base, 0x10); + release_region(hw_config->io_base+0x10, 0x9); + return 0; } #endif @@ -732,39 +742,38 @@ void __init attach_pss(struct address_in pss_initialized = 1; sprintf(tmp, "ECHO-PSS Rev. %d", id); conf_printf(tmp, hw_config); + return 1; } -int __init probe_pss_mpu(struct address_info *hw_config) +static int __init probe_pss_mpu(struct address_info *hw_config) { + struct resource *ports; int timeout; if (!pss_initialized) return 0; - if (check_region(hw_config->io_base, 2)) - { + ports = request_region(hw_config->io_base, 2, "mpu401"); + + if (!ports) { printk(KERN_ERR "PSS: MPU I/O port conflict\n"); return 0; } - if (!set_io_base(devc, CONF_MIDI, hw_config->io_base)) - { - printk(KERN_ERR "PSS: MIDI base could not be set.\n"); - return 0; + if (!set_io_base(devc, CONF_MIDI, hw_config->io_base)) { + printk(KERN_ERR "PSS: MIDI base could not be set.\n"); + goto fail; + } + if (!set_irq(devc, CONF_MIDI, hw_config->irq)) { + printk(KERN_ERR "PSS: MIDI IRQ allocation error.\n"); + goto fail; } - if (!set_irq(devc, CONF_MIDI, hw_config->irq)) - { - printk(KERN_ERR "PSS: MIDI IRQ allocation error.\n"); - return 0; - } - if (!pss_synthLen) - { + if (!pss_synthLen) { printk(KERN_ERR "PSS: Can't enable MPU. MIDI synth microcode not available.\n"); - return 0; + goto fail; } - if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) - { + if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST)) { printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n"); - return 0; + goto fail; } /* @@ -780,7 +789,16 @@ int __init probe_pss_mpu(struct address_ break; /* No more input */ } - return probe_mpu401(hw_config); + if (!probe_mpu401(hw_config, ports)) + goto fail; + + attach_mpu401(hw_config, THIS_MODULE); /* Slot 1 */ + if (hw_config->slots[1] != -1) /* The MPU driver installed itself */ + midi_devs[hw_config->slots[1]]->coproc = &pss_coproc_operations; + return 1; +fail: + release_region(hw_config->io_base, 2); + return 0; } static int pss_coproc_open(void *dev_info, int sub_device) @@ -1021,39 +1039,36 @@ static coproc_operations pss_coproc_oper &pss_data }; -static void __init attach_pss_mpu(struct address_info *hw_config) -{ - attach_mpu401(hw_config, THIS_MODULE); /* Slot 1 */ - if (hw_config->slots[1] != -1) /* The MPU driver installed itself */ - midi_devs[hw_config->slots[1]]->coproc = &pss_coproc_operations; -} - static int __init probe_pss_mss(struct address_info *hw_config) { volatile int timeout; + struct resource *ports; + int my_mix = -999; /* gcc shut up */ if (!pss_initialized) return 0; - if (check_region(hw_config->io_base, 8)) - { - printk(KERN_ERR "PSS: WSS I/O port conflicts.\n"); - return 0; + if (!request_region(hw_config->io_base, 4, "WSS config")) { + printk(KERN_ERR "PSS: WSS I/O port conflicts.\n"); + return 0; } - if (!set_io_base(devc, CONF_WSS, hw_config->io_base)) - { - printk("PSS: WSS base not settable.\n"); + ports = request_region(hw_config->io_base + 4, 4, "ad1848"); + if (!ports) { + printk(KERN_ERR "PSS: WSS I/O port conflicts.\n"); + release_region(hw_config->io_base, 4); return 0; } - if (!set_irq(devc, CONF_WSS, hw_config->irq)) - { + if (!set_io_base(devc, CONF_WSS, hw_config->io_base)) { + printk("PSS: WSS base not settable.\n"); + goto fail; + } + if (!set_irq(devc, CONF_WSS, hw_config->irq)) { printk("PSS: WSS IRQ allocation error.\n"); - return 0; + goto fail; } - if (!set_dma(devc, CONF_WSS, hw_config->dma)) - { + if (!set_dma(devc, CONF_WSS, hw_config->dma)) { printk(KERN_ERR "PSS: WSS DMA allocation error\n"); - return 0; + goto fail; } /* * For some reason the card returns 0xff in the WSS status register @@ -1071,13 +1086,9 @@ static int __init probe_pss_mss(struct a (timeout < 100000); timeout++) ; - return probe_ms_sound(hw_config); -} + if (!probe_ms_sound(hw_config, ports)) + goto fail; -static void __init attach_pss_mss(struct address_info *hw_config) -{ - int my_mix = -999; /* gcc shut up */ - devc->ad_mixer_dev = NO_WSS_MIXER; if (pss_mixer) { @@ -1088,11 +1099,11 @@ static void __init attach_pss_mss(struct devc)) < 0) { printk(KERN_ERR "Could not install PSS mixer\n"); - return; + goto fail; } } pss_mixer_reset(devc); - attach_ms_sound(hw_config, THIS_MODULE); /* Slot 0 */ + attach_ms_sound(hw_config, ports, THIS_MODULE); /* Slot 0 */ if (hw_config->slots[0] != -1) { @@ -1104,6 +1115,11 @@ static void __init attach_pss_mss(struct devc->ad_mixer_dev = audio_devs[hw_config->slots[0]]->mixer_dev; } } + return 1; +fail: + release_region(hw_config->io_base + 4, 4); + release_region(hw_config->io_base, 4); + return 0; } static inline void __exit unload_pss(struct address_info *hw_config) @@ -1185,6 +1201,8 @@ static int __init init_pss(void) printk(KERN_INFO "PSS: loading in no sound mode.\n"); disable_all_emulations(); configure_nonsound_components(); + release_region(pss_io, 0x10); + release_region(pss_io + 0x10, 0x9); return 0; } @@ -1206,20 +1224,16 @@ static int __init init_pss(void) fw_load = 1; pss_synthLen = mod_firmware_load(pss_firmware, (void *) &pss_synth); } - if (!probe_pss(&cfg)) + if (!attach_pss(&cfg)) return -ENODEV; - attach_pss(&cfg); /* * Attach stuff */ - if (probe_pss_mpu(&cfg_mpu)) { + if (probe_pss_mpu(&cfg_mpu)) pssmpu = 1; - attach_pss_mpu(&cfg_mpu); - } - if (probe_pss_mss(&cfg2)) { + + if (probe_pss_mss(&cfg2)) pssmss = 1; - attach_pss_mss(&cfg2); - } return 0; } --- linux-2.6.9-rc1/sound/oss/rme96xx.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/sound/oss/rme96xx.c 2004-09-03 00:56:41.048920264 -0700 @@ -54,7 +54,7 @@ TODO: #include #include #include -#include +#include #include #include #include --- linux-2.6.9-rc1/sound/oss/sb_card.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/sound/oss/sb_card.c 2004-09-02 20:51:53.000000000 -0700 @@ -100,7 +100,14 @@ MODULE_PARM_DESC(uart401, "When set to /* OSS subsystem card registration shared by PnP and legacy routines */ static int sb_register_oss(struct sb_card_config *scc, struct sb_module_options *sbmo) { - if(!sb_dsp_detect(&scc->conf, 0, 0, sbmo)) { + if (!request_region(scc->conf.io_base, 16, "soundblaster")) { + printk(KERN_ERR "sb: ports busy.\n"); + kfree(scc); + return -EBUSY; + } + + if (!sb_dsp_detect(&scc->conf, 0, 0, sbmo)) { + release_region(scc->conf.io_base, 16); printk(KERN_ERR "sb: Failed DSP Detect.\n"); kfree(scc); return -ENODEV; --- linux-2.6.9-rc1/sound/oss/sb_common.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/sound/oss/sb_common.c 2004-09-02 20:51:53.000000000 -0700 @@ -520,13 +520,6 @@ int sb_dsp_detect(struct address_info *h */ DDB(printk("sb_dsp_detect(%x) entered\n", hw_config->io_base)); - if (check_region(hw_config->io_base, 16)) - { -#ifdef MODULE - printk(KERN_INFO "sb: I/O region in use.\n"); -#endif - return 0; - } devc->lock = SPIN_LOCK_UNLOCKED; devc->type = hw_config->card_subtype; @@ -668,6 +661,7 @@ int sb_dsp_init(struct address_info *hw_ if (devc->base != hw_config->io_base) { DDB(printk("I/O port mismatch\n")); + release_region(devc->base, 16); return 0; } /* @@ -689,6 +683,7 @@ int sb_dsp_init(struct address_info *hw_ if (request_irq(hw_config->irq, sbintr, i, "soundblaster", devc) < 0) { printk(KERN_ERR "SB: Can't allocate IRQ%d\n", hw_config->irq); + release_region(devc->base, 16); return 0; } devc->irq_ok = 0; @@ -697,6 +692,7 @@ int sb_dsp_init(struct address_info *hw_ if (!sb16_set_irq_hw(devc, devc->irq)) /* Unsupported IRQ */ { free_irq(devc->irq, devc); + release_region(devc->base, 16); return 0; } if ((devc->type == 0 || devc->type == MDL_ESS) && @@ -735,7 +731,6 @@ int sb_dsp_init(struct address_info *hw_ } } } /* IRQ setup */ - request_region(hw_config->io_base, 16, "soundblaster"); last_sb = devc; @@ -1224,17 +1219,22 @@ int probe_sbmpu(struct address_info *hw_ #if defined(CONFIG_SOUND_MPU401) if (devc->model == MDL_ESS) { - if (check_region(hw_config->io_base, 2)) - { + struct resource *ports; + ports = request_region(hw_config->io_base, 2, "mpu401"); + if (!ports) { printk(KERN_ERR "sbmpu: I/O port conflict (%x)\n", hw_config->io_base); return 0; } - if (!ess_midi_init(devc, hw_config)) + if (!ess_midi_init(devc, hw_config)) { + release_region(hw_config->io_base, 2); return 0; + } hw_config->name = "ESS1xxx MPU"; devc->midi_irq_cookie = NULL; - if (!probe_mpu401(hw_config)) + if (!probe_mpu401(hw_config, ports)) { + release_region(hw_config->io_base, 2); return 0; + } attach_mpu401(hw_config, owner); if (last_sb->irq == -hw_config->irq) last_sb->midi_irq_cookie=(void *)hw_config->slots[1]; --- linux-2.6.9-rc1/sound/oss/sgalaxy.c 2004-08-24 00:03:14.000000000 -0700 +++ 25/sound/oss/sgalaxy.c 2004-09-02 20:51:53.000000000 -0700 @@ -86,19 +86,31 @@ static int __init sb_cmd( int base, unsi static int __init probe_sgalaxy( struct address_info *ai ) { - if ( check_region( ai->io_base, 8 ) ) { + struct resource *ports; + int n; + + if (!request_region(ai->io_base, 4, "WSS config")) { printk(KERN_ERR "sgalaxy: WSS IO port 0x%03x not available\n", ai->io_base); return 0; } - - if ( ad1848_detect( ai->io_base+4, NULL, ai->osp ) ) - return probe_ms_sound(ai); /* The card is already active, check irq etc... */ - if ( check_region( ai->ai_sgbase, 0x10 ) ) { + ports = request_region(ai->io_base + 4, 4, "ad1848"); + if (!ports) { + printk(KERN_ERR "sgalaxy: WSS IO port 0x%03x not available\n", ai->io_base); + release_region(ai->io_base, 4); + return 0; + } + + if (!request_region( ai->ai_sgbase, 0x10, "SoundGalaxy SB")) { printk(KERN_ERR "sgalaxy: SB IO port 0x%03x not available\n", ai->ai_sgbase); + release_region(ai->io_base + 4, 4); + release_region(ai->io_base, 4); return 0; } + if (ad1848_detect(ports, NULL, ai->osp)) + goto out; /* The card is already active, check irq etc... */ + /* switch to MSS/WSS mode */ sb_rst( ai->ai_sgbase ); @@ -108,16 +120,15 @@ static int __init probe_sgalaxy( struct sleep( HZ/10 ); - return probe_ms_sound(ai); -} +out: + if (!probe_ms_sound(ai, ports)) { + release_region(ai->io_base + 4, 4); + release_region(ai->io_base, 4); + release_region(ai->ai_sgbase, 0x10); + return 0; + } -static void __init attach_sgalaxy( struct address_info *ai ) -{ - int n; - - request_region( ai->ai_sgbase, 0x10, "SoundGalaxy SB" ); - - attach_ms_sound(ai, THIS_MODULE); + attach_ms_sound(ai, ports, THIS_MODULE); n=ai->slots[0]; if (n!=-1 && audio_devs[n]->mixer_dev != -1 ) { @@ -125,6 +136,7 @@ static void __init attach_sgalaxy( struc AD1848_REROUTE( SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH ); /* FM+Wavetable*/ AD1848_REROUTE( SOUND_MIXER_LINE3, SOUND_MIXER_CD ); /* CD */ } + return 1; } static void __exit unload_sgalaxy( struct address_info *ai ) @@ -163,8 +175,6 @@ static int __init init_sgalaxy(void) if ( probe_sgalaxy(&cfg) == 0 ) return -ENODEV; - attach_sgalaxy(&cfg); - return 0; } --- linux-2.6.9-rc1/sound/oss/sscape.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/sound/oss/sscape.c 2004-09-02 20:51:53.000000000 -0700 @@ -600,10 +600,10 @@ static coproc_operations sscape_coproc_o &adev_info }; -static int sscape_detected; +static struct resource *sscape_ports; static int sscape_is_pnp; -void __init attach_sscape(struct address_info *hw_config) +static void __init attach_sscape(struct address_info *hw_config) { #ifndef SSCAPE_REGS /* @@ -638,10 +638,6 @@ void __init attach_sscape(struct address int i, irq_bits = 0xff; - if (sscape_detected != hw_config->io_base) - return; - - request_region(devc->base + 2, 6, "SoundScape"); if (old_hardware) { valid_interrupts = valid_interrupts_old; @@ -650,7 +646,7 @@ void __init attach_sscape(struct address else conf_printf("Ensoniq SoundScape", hw_config); - for (i = 0; i < sizeof(valid_interrupts); i++) + for (i = 0; i < 4; i++) { if (hw_config->irq == valid_interrupts[i]) { @@ -661,45 +657,32 @@ void __init attach_sscape(struct address if (hw_config->irq > 15 || (regs[4] = irq_bits == 0xff)) { printk(KERN_ERR "Invalid IRQ%d\n", hw_config->irq); + release_region(devc->base, 2); + release_region(devc->base + 2, 6); + if (sscape_is_pnp) + release_region(devc->codec, 2); return; } if (!sscape_is_pnp) { spin_lock_irqsave(&devc->lock,flags); - for (i = 1; i < 10; i++) - { - switch (i) - { - case 1: /* Host interrupt enable */ - sscape_write(devc, i, 0xf0); /* All interrupts enabled */ - break; - - case 2: /* DMA A status/trigger register */ - case 3: /* DMA B status/trigger register */ - sscape_write(devc, i, 0x20); /* DMA channel disabled */ - break; - - case 4: /* Host interrupt config reg */ - sscape_write(devc, i, 0xf0 | (irq_bits << 2) | irq_bits); - break; - - case 5: /* Don't destroy CD-ROM DMA config bits (0xc0) */ - sscape_write(devc, i, (regs[i] & 0x3f) | (sscape_read(devc, i) & 0xc0)); - break; - - case 6: /* CD-ROM config (WSS codec actually) */ - sscape_write(devc, i, regs[i]); - break; - - case 9: /* Master control reg. Don't modify CR-ROM bits. Disable SB emul */ - sscape_write(devc, i, (sscape_read(devc, i) & 0xf0) | 0x08); - break; - - default: - sscape_write(devc, i, regs[i]); - } - } + /* Host interrupt enable */ + sscape_write(devc, 1, 0xf0); /* All interrupts enabled */ + /* DMA A status/trigger register */ + sscape_write(devc, 2, 0x20); /* DMA channel disabled */ + /* DMA B status/trigger register */ + sscape_write(devc, 3, 0x20); /* DMA channel disabled */ + /* Host interrupt config reg */ + sscape_write(devc, 4, 0xf0 | (irq_bits << 2) | irq_bits); + /* Don't destroy CD-ROM DMA config bits (0xc0) */ + sscape_write(devc, 5, (regs[5] & 0x3f) | (sscape_read(devc, 5) & 0xc0)); + /* CD-ROM config (WSS codec actually) */ + sscape_write(devc, 6, regs[6]); + sscape_write(devc, 7, regs[7]); + sscape_write(devc, 8, regs[8]); + /* Master control reg. Don't modify CR-ROM bits. Disable SB emul */ + sscape_write(devc, 9, (sscape_read(devc, 9) & 0xf0) | 0x08); spin_unlock_irqrestore(&devc->lock,flags); } #ifdef SSCAPE_DEBUG2 @@ -715,7 +698,7 @@ void __init attach_sscape(struct address } #endif - if (probe_mpu401(hw_config)) + if (probe_mpu401(hw_config, sscape_ports)) hw_config->always_detect = 1; hw_config->name = "SoundScape"; @@ -739,9 +722,6 @@ static int detect_ga(sscape_info * devc) DDB(printk("Entered Soundscape detect_ga(%x)\n", devc->base)); - if (check_region(devc->base, 8)) - return 0; - /* * First check that the address register of "ODIE" is * there and that it has exactly 4 writable bits. @@ -1115,9 +1095,9 @@ static void __init sscape_pnp_init_hw(ss sscape_write( devc, 9, i | 3 ); sscape_write( devc, 3, 0x40); - if (check_region(0x228, 1)) { - outb(0, 0x228); - release_region(0x228,1); + if (request_region(0x228, 1, "sscape setup junk")) { + outb(0, 0x228); + release_region(0x228,1); } sscape_write( devc, 3, (devc -> dma << 4) | 0x80); sscape_write( devc, 9, i ); @@ -1134,50 +1114,46 @@ static int __init detect_sscape_pnp(ssca DDB(printk("Entered detect_sscape_pnp(%x)\n", devc->base)); - if (check_region(devc->base, 8)) { - printk(KERN_ERR "detect_sscape_pnp: port %x is not free\n", devc->base); - return 0; - } - - if (check_region(devc->codec, 2)) { + if (!request_region(devc->codec, 2, "sscape codec")) { printk(KERN_ERR "detect_sscape_pnp: port %x is not free\n", devc->codec); return 0; } - if ( (inb( devc -> base + 2) & 0x78) != 0) return 0; + if ((inb(devc->base + 2) & 0x78) != 0) + goto fail; d = inb ( devc -> base + 4) & 0xF0; - if ( (d & 0x80) != 0) return 0; + if (d & 0x80) + goto fail; if (d == 0) { - devc->codec_type = 1; - devc->ic_type = IC_ODIE; - } - else if ( (d & 0x60) != 0) { - devc->codec_type = 2; - devc->ic_type = IC_OPUS; - } - else if ( (d & 0x40) != 0) { - devc->codec_type = 2; - devc->ic_type = IC_ODIE; - } - else return 0; + devc->codec_type = 1; + devc->ic_type = IC_ODIE; + } else if ( (d & 0x60) != 0) { + devc->codec_type = 2; + devc->ic_type = IC_OPUS; + } else if ( (d & 0x40) != 0) { /* WTF? */ + devc->codec_type = 2; + devc->ic_type = IC_ODIE; + } else + goto fail; sscape_is_pnp = 1; outb(0xFA, devc -> base+4); if ((inb( devc -> base+4) & 0x9F) != 0x0A) - return 0; + goto fail; outb(0xFE, devc -> base+4); if ( (inb(devc -> base+4) & 0x9F) != 0x0E) - return 0; + goto fail; if ( (inb(devc -> base+5) & 0x9F) != 0x0E) - return 0; + goto fail; if (devc->codec_type == 2) { - if (devc -> codec != devc -> base + 8) + if (devc->codec != devc->base + 8) { printk("soundscape warning: incorrect codec port specified\n"); - devc -> codec = devc -> base + 8; + goto fail; + } d = 0x10 | (sscape_read(devc, 9) & 0xCF); sscape_write(devc, 9, d); sscape_write(devc, 6, 0x80); @@ -1193,9 +1169,9 @@ static int __init detect_sscape_pnp(ssca d = inb(devc -> codec); if (d & 0x80) - return 0; + goto fail; if ( inb(devc -> codec + 2) == 0xFF) - return 0; + goto fail; sscape_write(devc, 9, sscape_read(devc, 9) & 0x3F ); @@ -1217,7 +1193,7 @@ static int __init detect_sscape_pnp(ssca sscape_pnp_init_hw(devc); - for (i = 0; i < sizeof(valid_interrupts); i++) + for (i = 0; i < 4; i++) { if (devc->codec_irq == valid_interrupts[i]) { irq_bits = i; @@ -1234,15 +1210,14 @@ static int __init detect_sscape_pnp(ssca sscape_pnp_write_codec( devc, 0, sscape_pnp_read_codec( devc, 0) | 0x20); sscape_pnp_write_codec( devc, 0, sscape_pnp_read_codec( devc, 1) | 0x20); - return 1; + return 1; +fail: + release_region(devc->codec, 2); + return 0; } static int __init probe_sscape(struct address_info *hw_config) { - - if (sscape_detected != 0 && sscape_detected != hw_config->io_base) - return 0; - devc->base = hw_config->io_base; devc->irq = hw_config->irq; devc->dma = hw_config->dma; @@ -1262,12 +1237,21 @@ static int __init probe_sscape(struct ad #endif devc->failed = 1; + sscape_ports = request_region(devc->base, 2, "mpu401"); + if (!sscape_ports) + return 0; + + if (!request_region(devc->base + 2, 6, "SoundScape")) { + release_region(devc->base, 2); + return 0; + } + if (!detect_ga(devc)) { - if (detect_sscape_pnp(devc)) { - sscape_detected = hw_config->io_base; + if (detect_sscape_pnp(devc)) return 1; - } - else return 0; + release_region(devc->base, 2); + release_region(devc->base + 2, 6); + return 0; } if (old_hardware) /* Check that it's really an old Spea/Reveal card. */ @@ -1282,14 +1266,14 @@ static int __init probe_sscape(struct ad inb(devc->base + ODIE_ADDR); } } - sscape_detected = hw_config->io_base; return 1; } -static int __init probe_ss_ms_sound(struct address_info *hw_config) +static int __init init_ss_ms_sound(struct address_info *hw_config) { int i, irq_bits = 0xff; int ad_flags = 0; + struct resource *ports; if (devc->failed) { @@ -1301,7 +1285,7 @@ static int __init probe_ss_ms_sound(stru printk(KERN_ERR "soundscape: Invalid initialization order.\n"); return 0; } - for (i = 0; i < sizeof(valid_interrupts); i++) + for (i = 0; i < 4; i++) { if (hw_config->irq == valid_interrupts[i]) { @@ -1309,37 +1293,27 @@ static int __init probe_ss_ms_sound(stru break; } } - if (hw_config->irq > 15 || irq_bits == 0xff) - { + if (irq_bits == 0xff) { printk(KERN_ERR "soundscape: Invalid MSS IRQ%d\n", hw_config->irq); return 0; } - if (!sscape_is_pnp) { - if (old_hardware) - ad_flags = 0x12345677; /* Tell that we may have a CS4248 chip (Spea-V7 Media FX) */ - return ad1848_detect(hw_config->io_base, &ad_flags, hw_config->osp); - } - else { - if (old_hardware) - ad_flags = 0x12345677; /* Tell that we may have a CS4248 chip (Spea-V7 Media FX) */ - else - ad_flags = 0x87654321; /* Tell that we have a soundscape pnp with 1845 chip */ - return ad1848_detect(hw_config->io_base, &ad_flags, hw_config->osp); - } -} + if (old_hardware) + ad_flags = 0x12345677; /* Tell that we may have a CS4248 chip (Spea-V7 Media FX) */ + else if (sscape_is_pnp) + ad_flags = 0x87654321; /* Tell that we have a soundscape pnp with 1845 chip */ -static void __init attach_ss_ms_sound(struct address_info *hw_config) -{ - /* - * This routine configures the SoundScape card for use with the - * Win Sound System driver. The AD1848 codec interface uses the CD-ROM - * config registers of the "ODIE". - */ + ports = request_region(hw_config->io_base, 4, "ad1848"); + if (!ports) { + printk(KERN_ERR "soundscape: ports busy\n"); + return 0; + } - int i, irq_bits = 0xff; + if (!ad1848_detect(ports, &ad_flags, hw_config->osp)) { + release_region(hw_config->io_base, 4); + return 0; + } - if (!sscape_is_pnp) /*pnp is already setup*/ { /* @@ -1355,14 +1329,6 @@ static void __init attach_ss_ms_sound(st /* * Init the AD1848 (CD-ROM) config reg. */ - for (i = 0; i < sizeof(valid_interrupts); i++) - { - if (hw_config->irq == valid_interrupts[i]) - { - irq_bits = i; - break; - } - } sscape_write(devc, GA_CDCFG_REG, 0x89 | (hw_config->dma << 4) | (irq_bits << 1)); } @@ -1371,7 +1337,7 @@ static void __init attach_ss_ms_sound(st hw_config->slots[0] = ad1848_init( sscape_is_pnp ? "SoundScape" : "SoundScape PNP", - hw_config->io_base, + ports, hw_config->irq, hw_config->dma, hw_config->dma, @@ -1402,13 +1368,15 @@ static void __init attach_ss_ms_sound(st printk("I%d = %02x\n", i, sscape_read(devc, i)); } #endif - + return 1; } static void __exit unload_sscape(struct address_info *hw_config) { release_region(devc->base + 2, 6); unload_mpu401(hw_config); + if (sscape_is_pnp) + release_region(devc->codec, 2); } static void __exit unload_ss_ms_sound(struct address_info *hw_config) @@ -1480,10 +1448,7 @@ static int __init init_sscape(void) attach_sscape(&cfg_mpu); - mss = probe_ss_ms_sound(&cfg); - - if (mss) - attach_ss_ms_sound(&cfg); + mss = init_ss_ms_sound(&cfg); return 0; } --- linux-2.6.9-rc1/sound/oss/swarm_cs4297a.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/sound/oss/swarm_cs4297a.c 2004-09-03 00:56:41.050919960 -0700 @@ -70,6 +70,7 @@ #include #include #include +#include #include #include #include @@ -77,7 +78,6 @@ #include #include #include -#include #include #include --- linux-2.6.9-rc1/sound/oss/trident.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/sound/oss/trident.c 2004-09-03 00:56:41.054919352 -0700 @@ -217,7 +217,6 @@ #include #include #include -#include #include #include --- linux-2.6.9-rc1/sound/oss/trix.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/sound/oss/trix.c 2004-09-02 20:51:53.000000000 -0700 @@ -29,12 +29,6 @@ #include "trix_boot.h" -static int kilroy_was_here; /* Don't detect twice */ -static int sb_initialized; -static int mpu_initialized; - -static int *trix_osp; - static int mpu; static int joystick; @@ -82,20 +76,11 @@ static int trix_set_wss_port(struct addr { unsigned char addr_bits; - if (check_region(0x390, 2)) - { - printk(KERN_ERR "AudioTrix: Config port I/O conflict\n"); - return 0; - } - if (kilroy_was_here) /* Already initialized */ - return 0; - if (trix_read(0x15) != 0x71) /* No ASIC signature */ { MDB(printk(KERN_ERR "No AudioTrix ASIC signature found\n")); return 0; } - kilroy_was_here = 1; /* * Reset some registers. @@ -135,107 +120,114 @@ static int trix_set_wss_port(struct addr * AudioTrix Pro */ -static int __init probe_trix_wss(struct address_info *hw_config) +static int __init init_trix_wss(struct address_info *hw_config) { + static unsigned char dma_bits[4] = { + 1, 2, 0, 3 + }; + struct resource *ports; + int config_port = hw_config->io_base + 0; + int dma1 = hw_config->dma, dma2 = hw_config->dma2; + int old_num_mixers = num_mixers; + u8 config, bits; int ret; + + switch(hw_config->irq) { + case 7: + bits = 8; + break; + case 9: + bits = 0x10; + break; + case 10: + bits = 0x18; + break; + case 11: + bits = 0x20; + break; + default: + printk(KERN_ERR "AudioTrix: Bad WSS IRQ %d\n", hw_config->irq); + return 0; + } + + switch (dma1) { + case 0: + case 1: + case 3: + break; + default: + printk(KERN_ERR "AudioTrix: Bad WSS DMA %d\n", dma1); + return 0; + } + + switch (dma2) { + case -1: + case 0: + case 1: + case 3: + break; + default: + printk(KERN_ERR "AudioTrix: Bad capture DMA %d\n", dma2); + return 0; + } /* * Check if the IO port returns valid signature. The original MS Sound * system returns 0x04 while some cards (AudioTrix Pro for example) * return 0x00. */ - if (check_region(hw_config->io_base, 8)) - { + ports = request_region(hw_config->io_base + 4, 4, "ad1848"); + if (!ports) { printk(KERN_ERR "AudioTrix: MSS I/O port conflict (%x)\n", hw_config->io_base); return 0; } - trix_osp = hw_config->osp; - if (!trix_set_wss_port(hw_config)) + if (!request_region(hw_config->io_base, 4, "MSS config")) { + printk(KERN_ERR "AudioTrix: MSS I/O port conflict (%x)\n", hw_config->io_base); + release_region(hw_config->io_base + 4, 4); return 0; + } + + if (!trix_set_wss_port(hw_config)) + goto fail; + + config = inb(hw_config->io_base + 3); - if ((inb(hw_config->io_base + 3) & 0x3f) != 0x00) + if ((config & 0x3f) != 0x00) { MDB(printk(KERN_ERR "No MSS signature detected on port 0x%x\n", hw_config->io_base)); - return 0; + goto fail; } - if (hw_config->irq > 11) - { - printk(KERN_ERR "AudioTrix: Bad WSS IRQ %d\n", hw_config->irq); - return 0; - } - if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3) - { - printk(KERN_ERR "AudioTrix: Bad WSS DMA %d\n", hw_config->dma); - return 0; - } - if (hw_config->dma2 != -1 && hw_config->dma2 != hw_config->dma) - if (hw_config->dma2 != 0 && hw_config->dma2 != 1 && hw_config->dma2 != 3) - { - printk(KERN_ERR "AudioTrix: Bad capture DMA %d\n", hw_config->dma2); - return 0; - } + /* * Check that DMA0 is not in use with a 8 bit board. */ - if (hw_config->dma == 0 && inb(hw_config->io_base + 3) & 0x80) + if (dma1 == 0 && config & 0x80) { printk(KERN_ERR "AudioTrix: Can't use DMA0 with a 8 bit card slot\n"); - return 0; + goto fail; } - if (hw_config->irq > 7 && hw_config->irq != 9 && inb(hw_config->io_base + 3) & 0x80) + if (hw_config->irq > 9 && config & 0x80) { printk(KERN_ERR "AudioTrix: Can't use IRQ%d with a 8 bit card slot\n", hw_config->irq); - return 0; - } - ret = ad1848_detect(hw_config->io_base + 4, NULL, hw_config->osp); - - if (ret) - { - if(joystick==1) - trix_write(0x15, 0x80); - request_region(0x390, 2, "AudioTrix"); + goto fail; } - return ret; -} -static void __init attach_trix_wss(struct address_info *hw_config) -{ - static unsigned char interrupt_bits[12] = { - 0, 0, 0, 0, 0, 0, 0, 0x08, 0, 0x10, 0x18, 0x20 - }; - char bits; + ret = ad1848_detect(ports, NULL, hw_config->osp); + if (!ret) + goto fail; - static unsigned char dma_bits[4] = { - 1, 2, 0, 3 - }; + if (joystick==1) + trix_write(0x15, 0x80); - int config_port = hw_config->io_base + 0; - int dma1 = hw_config->dma, dma2 = hw_config->dma2; - int old_num_mixers = num_mixers; - - trix_osp = hw_config->osp; - - if (!kilroy_was_here) - { - DDB(printk("AudioTrix: Attach called but not probed yet???\n")); - return; - } - /* * Set the IRQ and DMA addresses. */ - bits = interrupt_bits[hw_config->irq]; - if (bits == 0) - { - printk("AudioTrix: Bad IRQ (%d)\n", hw_config->irq); - return; - } outb((bits | 0x40), config_port); - if (hw_config->dma2 == -1 || hw_config->dma2 == hw_config->dma) + if (dma2 == -1 || dma2 == dma1) { bits |= dma_bits[dma1]; dma2 = dma1; @@ -253,14 +245,13 @@ static void __init attach_trix_wss(struc outb((bits), config_port); /* Write IRQ+DMA setup */ - hw_config->slots[0] = ad1848_init("AudioTrix Pro", hw_config->io_base + 4, + hw_config->slots[0] = ad1848_init("AudioTrix Pro", ports, hw_config->irq, dma1, dma2, 0, hw_config->osp, THIS_MODULE); - request_region(hw_config->io_base, 4, "MSS config"); if (num_mixers > old_num_mixers) /* Mixer got installed */ { @@ -269,6 +260,12 @@ static void __init attach_trix_wss(struc AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH); /* OPL4 */ AD1848_REROUTE(SOUND_MIXER_SPEAKER, SOUND_MIXER_ALTPCM); /* SB */ } + return 1; + +fail: + release_region(hw_config->io_base, 4); + release_region(hw_config->io_base + 4, 4); + return 0; } static int __init probe_trix_sb(struct address_info *hw_config) @@ -276,6 +273,8 @@ static int __init probe_trix_sb(struct a int tmp; unsigned char conf; + extern int sb_be_quiet; + int old_quiet; static signed char irq_translate[] = { -1, -1, -1, 0, 1, 2, -1, 3 }; @@ -283,17 +282,6 @@ static int __init probe_trix_sb(struct a if (trix_boot_len == 0) return 0; /* No boot code -> no fun */ - if (!kilroy_was_here) - return 0; /* AudioTrix Pro has not been detected earlier */ - - if (sb_initialized) - return 0; - - if (check_region(hw_config->io_base, 16)) - { - printk(KERN_ERR "AudioTrix: SB I/O port conflict (%x)\n", hw_config->io_base); - return 0; - } if ((hw_config->io_base & 0xffffff8f) != 0x200) return 0; @@ -307,6 +295,11 @@ static int __init probe_trix_sb(struct a if (tmp != 1 && tmp != 3) return 0; + if (!request_region(hw_config->io_base, 16, "soundblaster")) { + printk(KERN_ERR "AudioTrix: SB I/O port conflict (%x)\n", hw_config->io_base); + return 0; + } + conf = 0x84; /* DMA and IRQ enable */ conf |= hw_config->io_base & 0x70; /* I/O address bits */ conf |= irq_translate[hw_config->irq]; @@ -315,16 +308,12 @@ static int __init probe_trix_sb(struct a trix_write(0x1b, conf); download_boot(hw_config->io_base); - sb_initialized = 1; hw_config->name = "AudioTrix SB"; - return sb_dsp_detect(hw_config, 0, 0, NULL); -} - -static void __init attach_trix_sb(struct address_info *hw_config) -{ - extern int sb_be_quiet; - int old_quiet; + if (!sb_dsp_detect(hw_config, 0, 0, NULL)) { + release_region(hw_config->io_base, 16); + return 0; + } hw_config->driver_use_1 = SB_NO_MIDI | SB_NO_MIXER | SB_NO_RECORDING; @@ -335,6 +324,7 @@ static void __init attach_trix_sb(struct sb_dsp_init(hw_config, THIS_MODULE); sb_be_quiet = old_quiet; + return 1; } static int __init probe_trix_mpu(struct address_info *hw_config) @@ -344,21 +334,6 @@ static int __init probe_trix_mpu(struct -1, -1, -1, 1, 2, 3, -1, 4, -1, 5 }; - if (!kilroy_was_here) - { - DDB(printk("Trix: WSS and SB modes must be initialized before MPU\n")); - return 0; /* AudioTrix Pro has not been detected earlier */ - } - if (!sb_initialized) - { - DDB(printk("Trix: SB mode must be initialized before MPU\n")); - return 0; - } - if (mpu_initialized) - { - DDB(printk("Trix: MPU mode already initialized\n")); - return 0; - } if (hw_config->irq > 9) { printk(KERN_ERR "AudioTrix: Bad MPU IRQ %d\n", hw_config->irq); @@ -389,7 +364,6 @@ static int __init probe_trix_mpu(struct conf |= irq_bits[hw_config->irq] << 4; trix_write(0x19, (trix_read(0x19) & 0x83) | conf); - mpu_initialized = 1; hw_config->name = "AudioTrix Pro"; return probe_uart401(hw_config, THIS_MODULE); } @@ -485,9 +459,16 @@ static int __init init_trix(void) trix_boot_len = mod_firmware_load("/etc/sound/trxpro.bin", (char **) &trix_boot); } - if (!probe_trix_wss(&cfg)) + + if (!request_region(0x390, 2, "AudioTrix")) { + printk(KERN_ERR "AudioTrix: Config port I/O conflict\n"); + return -ENODEV; + } + + if (!init_trix_wss(&cfg)) { + release_region(0x390, 2); return -ENODEV; - attach_trix_wss(&cfg); + } /* * We must attach in the right order to get the firmware @@ -496,8 +477,6 @@ static int __init init_trix(void) if (cfg2.io_base != -1) { sb = probe_trix_sb(&cfg2); - if (sb) - attach_trix_sb(&cfg2); } if (cfg_mpu.io_base != -1) --- linux-2.6.9-rc1/sound/oss/v_midi.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/sound/oss/v_midi.c 2004-09-02 20:51:53.000000000 -0700 @@ -90,11 +90,12 @@ static void v_midi_close (int dev) static int v_midi_out (int dev, unsigned char midi_byte) { vmidi_devc *devc = midi_devs[dev]->devc; - vmidi_devc *pdevc = midi_devs[devc->pair_mididev]->devc; + vmidi_devc *pdevc; if (devc == NULL) - return -(ENXIO); + return -ENXIO; + pdevc = midi_devs[devc->pair_mididev]->devc; if (pdevc->input_opened > 0){ if (MIDIbuf_avail(pdevc->my_mididev) > 500) return 0; --- linux-2.6.9-rc1/sound/oss/waveartist.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/sound/oss/waveartist.c 2004-09-02 20:51:53.000000000 -0700 @@ -1346,18 +1346,20 @@ static int __init probe_waveartist(struc return 0; } - if (check_region(hw_config->io_base, 15)) { + if (!request_region(hw_config->io_base, 15, hw_config->name)) { printk(KERN_WARNING "WaveArtist: I/O port conflict\n"); return 0; } if (hw_config->irq > 15 || hw_config->irq < 0) { + release_region(hw_config->io_base, 15); printk(KERN_WARNING "WaveArtist: Bad IRQ %d\n", hw_config->irq); return 0; } if (hw_config->dma != 3) { + release_region(hw_config->io_base, 15); printk(KERN_WARNING "WaveArtist: Bad DMA %d\n", hw_config->dma); return 0; @@ -1392,8 +1394,6 @@ attach_waveartist(struct address_info *h if (hw->dma != hw->dma2 && hw->dma2 != NO_DMA) devc->audio_flags |= DMA_DUPLEX; - request_region(hw->io_base, 15, devc->hw.name); - devc->mix = mix; devc->dev_no = waveartist_init(devc); --- linux-2.6.9-rc1/sound/oss/wavfront.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/sound/oss/wavfront.c 2004-09-02 20:51:53.000000000 -0700 @@ -1402,7 +1402,7 @@ wavefront_fetch_multisample (wavefront_p num_samples = (1 << log_ns[0]); for (i = 0; i < num_samples; i++) { - char d[2]; + s8 d[2]; if ((d[0] = wavefront_read ()) == -1) { printk (KERN_ERR LOGNAME "upload multisample failed " @@ -2961,8 +2961,8 @@ wffx_memset (int page, if (i != cnt) { printk (KERN_WARNING LOGNAME "FX memset " - "(0x%x, 0x%x, 0x%x, %d) incomplete\n", - page, addr, (int) data, cnt); + "(0x%x, 0x%x, %p, %d) incomplete\n", + page, addr, data, cnt); return -(EIO); } } --- linux-2.6.9-rc1/sound/oss/wf_midi.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/sound/oss/wf_midi.c 2004-09-02 20:51:53.000000000 -0700 @@ -784,7 +784,7 @@ virtual_midi_disable (void) int __init detect_wf_mpu (int irq, int io_base) { - if (check_region (io_base, 2)) { + if (!request_region(io_base, 2, "wavefront midi")) { printk (KERN_WARNING "WF-MPU: I/O port %x already in use.\n", io_base); return -1; @@ -803,11 +803,10 @@ int __init install_wf_mpu (void) if ((phys_dev->devno = sound_alloc_mididev()) < 0){ printk (KERN_ERR "WF-MPU: Too many MIDI devices detected.\n"); + release_region(phys_dev->base, 2); return -1; - } - request_region (phys_dev->base, 2, "wavefront midi"); phys_dev->isvirtual = 0; if (config_wf_mpu (phys_dev)) { --- linux-2.6.9-rc1/sound/parisc/harmony.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/sound/parisc/harmony.c 2004-09-02 20:51:53.000000000 -0700 @@ -80,8 +80,7 @@ MODULE_AUTHOR("Laurent Canet "); MODULE_DESCRIPTION("ALSA Harmony sound driver"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{ALSA,Harmony soundcard}}"); +MODULE_SUPPORTED_DEVICE("{{ALSA,Harmony soundcard}}"); #undef DEBUG #ifdef DEBUG @@ -138,13 +137,10 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for Sun CS4231 soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for Sun CS4231 soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable Sun CS4231 soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); /* Register offset (from base hpa) */ #define REG_ID 0x00 @@ -216,7 +212,6 @@ typedef struct snd_card_harmony { snd_pcm_substream_t *capture_substream; snd_info_entry_t *proc_entry; } snd_card_harmony_t; -#define chip_t snd_card_harmony_t static snd_card_t *snd_harmony_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; @@ -243,10 +238,8 @@ static unsigned int snd_card_harmony_rat 44100, 48000 }; -#define RATES sizeof(snd_card_harmony_rates) / sizeof(snd_card_harmony_rates[0]) - static snd_pcm_hw_constraint_list_t hw_constraint_rates = { - .count = RATES, + .count = ARRAY_SIZE(snd_card_harmony_rates), .list = snd_card_harmony_rates, .mask = 0, }; @@ -283,7 +276,7 @@ static unsigned int snd_card_harmony_rat { unsigned int idx; - for (idx = 0; idx <= RATES; idx++) + for (idx = 0; idx <= ARRAY_SIZE(snd_card_harmony_rates); idx++) if (snd_card_harmony_rates[idx] == rate) return rate_bits[idx]; return HARMONY_SR_44KHZ; /* fallback */ @@ -751,6 +744,8 @@ static int snd_card_harmony_hw_params(sn int err; err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); + if (err > 0 && substream->dma_device.type == SNDRV_DMA_TYPE_CONTINUOUS) + substream->runtime->dma_addr = __pa(substream->runtime->dma_area); DPRINTK(KERN_INFO PFX "HW Params returned %d, dma_addr %lx\n", err, (unsigned long)substream->runtime->dma_addr); return err; @@ -784,7 +779,7 @@ static snd_pcm_ops_t snd_card_harmony_ca .pointer = snd_card_harmony_capture_pointer, }; -static int snd_card_harmony_pcm_init(snd_card_harmony_t *harmony, int device) +static int snd_card_harmony_pcm_init(snd_card_harmony_t *harmony) { snd_pcm_t *pcm; int err; @@ -797,7 +792,7 @@ static int snd_card_harmony_pcm_init(snd snd_harmony_disable_interrupts(harmony); - if ((err = snd_pcm_new(harmony->card, "Harmony", device, 1, 1, &pcm)) < 0) + if ((err = snd_pcm_new(harmony->card, "Harmony", 0, 1, 1, &pcm)) < 0) return err; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_harmony_playback_ops); @@ -811,28 +806,54 @@ static int snd_card_harmony_pcm_init(snd /* initialize graveyard buffer */ harmony->dma_dev.type = SNDRV_DMA_TYPE_DEV; harmony->dma_dev.dev = &harmony->pa_dev->dev; - err = snd_dma_alloc_pages(&harmony->dma_dev, HARMONY_BUF_SIZE*GRAVEYARD_BUFS, + err = snd_dma_alloc_pages(harmony->dma_dev.type, + harmony->dma_dev.dev, + HARMONY_BUF_SIZE*GRAVEYARD_BUFS, &harmony->graveyard_dma); - if (err < 0) + if (err == -ENOMEM) { + /* use continuous buffers */ + harmony->dma_dev.type = SNDRV_DMA_TYPE_CONTINUOUS; + harmony->dma_dev.dev = snd_dma_continuous_data(GFP_KERNEL); + err = snd_dma_alloc_pages(harmony->dma_dev.type, + harmony->dma_dev.dev, + HARMONY_BUF_SIZE*GRAVEYARD_BUFS, + &harmony->graveyard_dma); + } + if (err < 0) { + printk(KERN_ERR PFX "can't allocate graveyard buffer\n"); return err; + } harmony->graveyard_count = 0; /* initialize silence buffers */ - err = snd_dma_alloc_pages(&harmony->dma_dev, HARMONY_BUF_SIZE*SILENCE_BUFS, + err = snd_dma_alloc_pages(harmony->dma_dev.type, + harmony->dma_dev.dev, + HARMONY_BUF_SIZE*SILENCE_BUFS, &harmony->silence_dma); - if (err < 0) + if (err < 0) { + printk(KERN_ERR PFX "can't allocate silence buffer\n"); return err; + } harmony->silence_count = 0; + if (harmony->dma_dev.type == SNDRV_DMA_TYPE_CONTINUOUS) { + harmony->graveyard_dma.addr = __pa(harmony->graveyard_dma.area); + harmony->silence_dma.addr = __pa(harmony->silence_dma.area); + } + harmony->ply_stopped = harmony->cap_stopped = 1; harmony->playback_substream = NULL; harmony->capture_substream = NULL; harmony->graveyard_count = 0; - - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, - &harmony->pa_dev->dev, - MAX_BUFFER_SIZE, MAX_BUFFER_SIZE); + + err = snd_pcm_lib_preallocate_pages_for_all(pcm, harmony->dma_dev.type, + harmony->dma_dev.dev, + MAX_BUFFER_SIZE, MAX_BUFFER_SIZE); + if (err < 0) { + printk(KERN_ERR PFX "buffer allocation error %d\n", err); + // return err; + } return 0; } @@ -871,7 +892,7 @@ static int snd_harmony_mixercontrol_info static int snd_harmony_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - snd_card_harmony_t *harmony = _snd_kcontrol_chip(kcontrol); + snd_card_harmony_t *harmony = snd_kcontrol_chip(kcontrol); int shift_left = (kcontrol->private_value) & 0xff; int shift_right = (kcontrol->private_value >> 8) & 0xff; int mask = (kcontrol->private_value >> 16) & 0xff; @@ -896,7 +917,7 @@ static int snd_harmony_volume_get(snd_kc static int snd_harmony_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - snd_card_harmony_t *harmony = _snd_kcontrol_chip(kcontrol); + snd_card_harmony_t *harmony = snd_kcontrol_chip(kcontrol); int shift_left = (kcontrol->private_value) & 0xff; int shift_right = (kcontrol->private_value >> 8) & 0xff; int mask = (kcontrol->private_value >> 16) & 0xff; @@ -1037,7 +1058,7 @@ static int __init snd_card_harmony_probe snd_card_free(card); return err; } - if ((err = snd_card_harmony_pcm_init(chip, dev)) < 0) { + if ((err = snd_card_harmony_pcm_init(chip)) < 0) { printk(KERN_ERR PFX "PCM Init failed\n"); snd_card_free(card); return err; --- linux-2.6.9-rc1/sound/pci/ac97/ac97_codec.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/sound/pci/ac97/ac97_codec.c 2004-09-02 20:51:53.000000000 -0700 @@ -45,9 +45,6 @@ static int enable_loopback; module_param(enable_loopback, bool, 0444); MODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control"); -MODULE_PARM_SYNTAX(enable_loopback, SNDRV_BOOLEAN_FALSE_DESC); - -#define chip_t ac97_t /* @@ -108,12 +105,13 @@ static const ac97_codec_id_t snd_ac97_co { 0x41445375, 0xffffffff, "AD1985", patch_ad1985, NULL }, { 0x414c4300, 0xffffff00, "ALC100/100P", NULL, NULL }, { 0x414c4710, 0xfffffff0, "ALC200/200P", NULL, NULL }, +{ 0x414c4721, 0xffffffff, "ALC650D", NULL, NULL }, /* already patched */ +{ 0x414c4722, 0xffffffff, "ALC650E", NULL, NULL }, /* already patched */ +{ 0x414c4723, 0xffffffff, "ALC650F", NULL, NULL }, /* already patched */ { 0x414c4720, 0xfffffff0, "ALC650", patch_alc650, NULL }, -{ 0x414c4721, 0xfffffff0, "ALC650D", patch_alc650, NULL }, -{ 0x414c4722, 0xfffffff0, "ALC650E", patch_alc650, NULL }, -{ 0x414c4723, 0xfffffff0, "ALC650F", patch_alc650, NULL }, { 0x414c4760, 0xfffffff0, "ALC655", patch_alc655, NULL }, { 0x414c4780, 0xfffffff0, "ALC658", patch_alc655, NULL }, +{ 0x414c4790, 0xfffffff0, "ALC850", patch_alc850, NULL }, { 0x414c4730, 0xffffffff, "ALC101", NULL, NULL }, { 0x414c4740, 0xfffffff0, "ALC202", NULL, NULL }, { 0x414c4750, 0xfffffff0, "ALC250", NULL, NULL }, @@ -214,6 +212,13 @@ const char *snd_ac97_stereo_enhancements }; /* + * Shared AC97 controllers (ICH, ATIIXP...) + */ +static DECLARE_MUTEX(shared_codec_mutex); +static ac97_t *shared_codec[AC97_SHARED_TYPES][4]; + + +/* * I/O routines */ @@ -274,12 +279,12 @@ void snd_ac97_write(ac97_t *ac97, unsign { if (!snd_ac97_valid_reg(ac97, reg)) return; - if ((ac97->id & 0xffffff00) == 0x414c4300) { + if ((ac97->id & 0xffffff00) == AC97_ID_ALC100) { /* Fix H/W bug of ALC100/100P */ if (reg == AC97_MASTER || reg == AC97_HEADPHONE) - ac97->bus->write(ac97, AC97_RESET, 0); /* reset audio codec */ + ac97->bus->ops->write(ac97, AC97_RESET, 0); /* reset audio codec */ } - ac97->bus->write(ac97, reg, value); + ac97->bus->ops->write(ac97, reg, value); } /** @@ -297,14 +302,14 @@ unsigned short snd_ac97_read(ac97_t *ac9 { if (!snd_ac97_valid_reg(ac97, reg)) return 0; - return ac97->bus->read(ac97, reg); + return ac97->bus->ops->read(ac97, reg); } /* read a register - return the cached value if already read */ static inline unsigned short snd_ac97_read_cache(ac97_t *ac97, unsigned short reg) { if (! test_bit(reg, ac97->reg_accessed)) { - ac97->regs[reg] = ac97->bus->read(ac97, reg); + ac97->regs[reg] = ac97->bus->ops->read(ac97, reg); // set_bit(reg, ac97->reg_accessed); } return ac97->regs[reg]; @@ -327,7 +332,7 @@ void snd_ac97_write_cache(ac97_t *ac97, spin_lock(&ac97->reg_lock); ac97->regs[reg] = value; spin_unlock(&ac97->reg_lock); - ac97->bus->write(ac97, reg, value); + ac97->bus->ops->write(ac97, reg, value); set_bit(reg, ac97->reg_accessed); } @@ -354,7 +359,7 @@ int snd_ac97_update(ac97_t *ac97, unsign if (change) { ac97->regs[reg] = value; spin_unlock(&ac97->reg_lock); - ac97->bus->write(ac97, reg, value); + ac97->bus->ops->write(ac97, reg, value); } else spin_unlock(&ac97->reg_lock); return change; @@ -387,7 +392,7 @@ int snd_ac97_update_bits(ac97_t *ac97, u if (change) { ac97->regs[reg] = new; spin_unlock(&ac97->reg_lock); - ac97->bus->write(ac97, reg, new); + ac97->bus->ops->write(ac97, reg, new); } else spin_unlock(&ac97->reg_lock); return change; @@ -398,7 +403,7 @@ static int snd_ac97_ad18xx_update_pcm_bi int change; unsigned short old, new, cfg; - down(&ac97->spec.ad18xx.mutex); + down(&ac97->mutex); spin_lock(&ac97->reg_lock); old = ac97->spec.ad18xx.pcmreg[codec]; new = (old & ~mask) | value; @@ -408,17 +413,17 @@ static int snd_ac97_ad18xx_update_pcm_bi ac97->spec.ad18xx.pcmreg[codec] = new; spin_unlock(&ac97->reg_lock); /* select single codec */ - ac97->bus->write(ac97, AC97_AD_SERIAL_CFG, + ac97->bus->ops->write(ac97, AC97_AD_SERIAL_CFG, (cfg & ~0x7000) | ac97->spec.ad18xx.unchained[codec] | ac97->spec.ad18xx.chained[codec]); /* update PCM bits */ - ac97->bus->write(ac97, AC97_PCM, new); + ac97->bus->ops->write(ac97, AC97_PCM, new); /* select all codecs */ - ac97->bus->write(ac97, AC97_AD_SERIAL_CFG, + ac97->bus->ops->write(ac97, AC97_AD_SERIAL_CFG, cfg | 0x7000); } else spin_unlock(&ac97->reg_lock); - up(&ac97->spec.ad18xx.mutex); + up(&ac97->mutex); return change; } @@ -545,7 +550,7 @@ int snd_ac97_get_single(snd_kcontrol_t * int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; int mask = (kcontrol->private_value >> 16) & 0xff; - int invert = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0x01; ucontrol->value.integer.value[0] = (snd_ac97_read_cache(ac97, reg) >> shift) & mask; if (invert) @@ -559,7 +564,7 @@ int snd_ac97_put_single(snd_kcontrol_t * int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; int mask = (kcontrol->private_value >> 16) & 0xff; - int invert = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0x01; unsigned short val; val = (ucontrol->value.integer.value[0] & mask); @@ -625,6 +630,40 @@ static int snd_ac97_put_double(snd_kcont (val1 << shift_left) | (val2 << shift_right)); } +int snd_ac97_getput_page(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol, + int (*func)(snd_kcontrol_t *, snd_ctl_elem_value_t *)) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int err; + + if ((ac97->ext_id & AC97_EI_REV_MASK) >= AC97_EI_REV_23 && + (reg >= 0x60 && reg < 0x70)) { + unsigned short page_save; + unsigned short page = (kcontrol->private_value >> 25) & 0x0f; + down(&ac97->mutex); /* lock paging */ + page_save = snd_ac97_read(ac97, AC97_INT_PAGING) & AC97_PAGE_MASK; + snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page); + err = func(kcontrol, ucontrol); + snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page_save); + up(&ac97->mutex); /* unlock paging */ + } else + err = func(kcontrol, ucontrol); + return err; +} + +/* for rev2.3 paging */ +int snd_ac97_page_get_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + return snd_ac97_getput_page(kcontrol, ucontrol, snd_ac97_get_single); +} + +/* for rev2.3 paging */ +int snd_ac97_page_put_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + return snd_ac97_getput_page(kcontrol, ucontrol, snd_ac97_put_single); +} + static const snd_kcontrol_new_t snd_ac97_controls_master_mono[2] = { AC97_SINGLE("Master Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1), AC97_SINGLE("Master Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1) @@ -1009,14 +1048,14 @@ static int snd_ac97_bus_free(ac97_bus_t kfree(bus->pcms); if (bus->private_free) bus->private_free(bus); - snd_magic_kfree(bus); + kfree(bus); } return 0; } static int snd_ac97_bus_dev_free(snd_device_t *device) { - ac97_bus_t *bus = snd_magic_cast(ac97_bus_t, device->device_data, return -ENXIO); + ac97_bus_t *bus = device->device_data; return snd_ac97_bus_free(bus); } @@ -1024,18 +1063,24 @@ static int snd_ac97_free(ac97_t *ac97) { if (ac97) { snd_ac97_proc_done(ac97); - if (ac97->bus) + if (ac97->bus) { ac97->bus->codec[ac97->num] = NULL; + if (ac97->bus->shared_type) { + down(&shared_codec_mutex); + shared_codec[ac97->bus->shared_type-1][ac97->num] = NULL; + up(&shared_codec_mutex); + } + } if (ac97->private_free) ac97->private_free(ac97); - snd_magic_kfree(ac97); + kfree(ac97); } return 0; } static int snd_ac97_dev_free(snd_device_t *device) { - ac97_t *ac97 = snd_magic_cast(ac97_t, device->device_data, return -ENXIO); + ac97_t *ac97 = device->device_data; snd_ac97_powerdown(ac97); /* for avoiding click noises during shut down */ return snd_ac97_free(ac97); } @@ -1120,6 +1165,7 @@ static void snd_ac97_change_volume_param snd_ac97_write_cache(ac97, reg, 0x8000); } +/* check the volume resolution of center/lfe */ static void snd_ac97_change_volume_params2(ac97_t * ac97, int reg, int shift, unsigned char *max) { unsigned short val, val1; @@ -1135,6 +1181,7 @@ static void snd_ac97_change_volume_param snd_ac97_write_cache(ac97, reg, 0x8080); } +/* check whether the volume resolution is 4 or 5 bits */ static void snd_ac97_change_volume_params3(ac97_t * ac97, int reg, unsigned char *max) { unsigned short val, val1; @@ -1150,6 +1197,18 @@ static void snd_ac97_change_volume_param snd_ac97_write_cache(ac97, reg, 0x8000); } +/* check whether the volume is mono or stereo */ +static int snd_ac97_is_stereo_vol(ac97_t *ac97, int reg) +{ + unsigned short val, val1, val2; + val = snd_ac97_read(ac97, reg); + val1 = val | 0x8000 | (0x01 << 8); + snd_ac97_write(ac97, reg, val1); + val2 = snd_ac97_read(ac97, reg); + snd_ac97_write(ac97, reg, val); /* restore */ + return val1 == val2; +} + static inline int printable(unsigned int x) { x &= 0xff; @@ -1178,6 +1237,9 @@ static int snd_ac97_cmute_new(snd_card_t snd_kcontrol_t *kctl; int stereo = 0; + if (! snd_ac97_valid_reg(ac97, reg)) + return 0; + if (ac97->flags & AC97_STEREO_MUTES) { /* check whether both mute bits work */ unsigned short val, val1; @@ -1208,6 +1270,9 @@ static int snd_ac97_cvol_new(snd_card_t int err; snd_kcontrol_new_t tmp = AC97_DOUBLE(name, reg, 8, 0, (unsigned int)max, 1); tmp.index = ac97->num; + + if (! snd_ac97_valid_reg(ac97, reg)) + return 0; if ((err = snd_ctl_add(card, snd_ctl_new1(&tmp, ac97))) < 0) return err; snd_ac97_write_cache(ac97, reg, @@ -1225,6 +1290,9 @@ static int snd_ac97_cmix_new(snd_card_t char name[44]; unsigned char max; + if (! snd_ac97_valid_reg(ac97, reg)) + return 0; + sprintf(name, "%s Switch", pfx); if ((err = snd_ac97_cmute_new(card, name, reg, ac97)) < 0) return err; @@ -1239,6 +1307,8 @@ static int snd_ac97_cmix_new(snd_card_t } +static unsigned int snd_ac97_determine_spdif_rates(ac97_t *ac97); + static int snd_ac97_mixer_build(ac97_t * ac97) { snd_card_t *card = ac97->bus->card; @@ -1293,11 +1363,8 @@ static int snd_ac97_mixer_build(ac97_t * } /* build headphone controls */ - if (snd_ac97_try_volume_mix(ac97, AC97_HEADPHONE) || ac97->id == AC97_ID_STAC9708) { - const char *name = ac97->id == AC97_ID_STAC9708 ? - "Sigmatel Surround Playback" : - "Headphone Playback"; - if ((err = snd_ac97_cmix_new(card, name, AC97_HEADPHONE, 1, ac97)) < 0) + if (snd_ac97_try_volume_mix(ac97, AC97_HEADPHONE)) { + if ((err = snd_ac97_cmix_new(card, "Headphone Playback", AC97_HEADPHONE, 1, ac97)) < 0) return err; } @@ -1332,7 +1399,8 @@ static int snd_ac97_mixer_build(ac97_t * for (idx = 0; idx < 2; idx++) if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0) return err; - snd_ac97_write_cache(ac97, AC97_PC_BEEP, 0x801e); + snd_ac97_write_cache(ac97, AC97_PC_BEEP, + snd_ac97_read(ac97, AC97_PC_BEEP) | 0x801e); } /* build Phone controls */ @@ -1349,15 +1417,26 @@ static int snd_ac97_mixer_build(ac97_t * /* build MIC controls */ snd_ac97_change_volume_params3(ac97, AC97_MIC, &max); - for (idx = 0; idx < 3; idx++) { - if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_mic[idx], ac97))) < 0) + if (snd_ac97_is_stereo_vol(ac97, AC97_MIC)) { + /* build stereo mic */ + if ((err = snd_ac97_cmute_new(card, "Mic Playback Switch", AC97_MIC, ac97)) < 0) return err; - if (idx == 1) { // volume - kctl->private_value &= ~(0xff << 16); - kctl->private_value |= (int)max << 16; + if ((err = snd_ac97_cvol_new(card, "Mic Playback Volume", AC97_MIC, max, ac97)) < 0) + return err; + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic[2], ac97))) < 0) + return err; + } else { + /* build mono mic */ + for (idx = 0; idx < 3; idx++) { + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_mic[idx], ac97))) < 0) + return err; + if (idx == 1) { // volume + kctl->private_value &= ~(0xff << 16); + kctl->private_value |= (int)max << 16; + } } + snd_ac97_write_cache(ac97, AC97_MIC, 0x8000 | max); } - snd_ac97_write_cache(ac97, AC97_MIC, 0x8000 | max); /* build Line controls */ if ((err = snd_ac97_cmix_new(card, "Line Playback", AC97_LINE, 0, ac97)) < 0) @@ -1410,9 +1489,7 @@ static int snd_ac97_mixer_build(ac97_t * if ((err = snd_ac97_cmute_new(card, "PCM Playback Switch", AC97_PCM, ac97)) < 0) return err; /* FIXME: C-Media chips have no PCM volume!! */ - if (/*ac97->id == 0x434d4941 ||*/ - ac97->id == 0x434d4942 || - ac97->id == 0x434d4961) + if (ac97->id == AC97_ID_CM9739) snd_ac97_write_cache(ac97, AC97_PCM, 0x9f1f); else { if ((err = snd_ac97_cvol_new(card, "PCM Playback Volume", AC97_PCM, 31, ac97)) < 0) @@ -1423,8 +1500,10 @@ static int snd_ac97_mixer_build(ac97_t * /* build Capture controls */ if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_src, ac97))) < 0) return err; - if ((err = snd_ac97_cmute_new(card, "Capture Switch", AC97_REC_GAIN, ac97)) < 0) - return err; + if (snd_ac97_try_bit(ac97, AC97_REC_GAIN, 15)) { + if ((err = snd_ac97_cmute_new(card, "Capture Switch", AC97_REC_GAIN, ac97)) < 0) + return err; + } if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_vol, ac97))) < 0) return err; snd_ac97_write_cache(ac97, AC97_REC_SEL, 0x0000); @@ -1520,6 +1599,7 @@ static int snd_ac97_mixer_build(ac97_t * /* set default PCM S/PDIF params */ /* consumer,PCM audio,no copyright,no preemphasis,PCM coder,original,48000Hz */ snd_ac97_write_cache(ac97, AC97_SPDIF, 0x2a20); + ac97->rates[AC97_RATES_SPDIF] = snd_ac97_determine_spdif_rates(ac97); } ac97->spdif_status = SNDRV_PCM_DEFAULT_CON_SPDIF; } @@ -1672,10 +1752,10 @@ static int ac97_reset_wait(ac97_t *ac97, */ /* test if we can write to the record gain volume register */ snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a05); - if (snd_ac97_read(ac97, AC97_REC_GAIN) == 0x8a05) + if ((snd_ac97_read(ac97, AC97_REC_GAIN) & 0x7fff) == 0x0a05) return 0; set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ/100); + schedule_timeout(1); } while (time_after_eq(end_time, jiffies)); return -ENODEV; } @@ -1683,45 +1763,47 @@ static int ac97_reset_wait(ac97_t *ac97, /** * snd_ac97_bus - create an AC97 bus component * @card: the card instance - * @_bus: the template of AC97 bus, callbacks and - * the private data. + * @num: the bus number + * @ops: the bus callbacks table + * @private_data: private data pointer for the new instance * @rbus: the pointer to store the new AC97 bus instance. * * Creates an AC97 bus component. An ac97_bus_t instance is newly - * allocated and initialized from the template (_bus). + * allocated and initialized. * - * The template must include the valid callbacks (at least read and - * write), the bus number (num), and the private data (private_data). - * The other callbacks, wait and reset, are not mandatory. + * The ops table must include valid callbacks (at least read and + * write). The other callbacks, wait and reset, are not mandatory. * * The clock is set to 48000. If another clock is needed, set - * bus->clock manually. + * (*rbus)->clock manually. * * The AC97 bus instance is registered as a low-level device, so you don't * have to release it manually. * * Returns zero if successful, or a negative error code on failure. */ -int snd_ac97_bus(snd_card_t * card, ac97_bus_t * _bus, ac97_bus_t ** rbus) +int snd_ac97_bus(snd_card_t *card, int num, ac97_bus_ops_t *ops, + void *private_data, ac97_bus_t **rbus) { int err; ac97_bus_t *bus; - static snd_device_ops_t ops = { + static snd_device_ops_t dev_ops = { .dev_free = snd_ac97_bus_dev_free, }; snd_assert(card != NULL, return -EINVAL); - snd_assert(_bus != NULL && rbus != NULL, return -EINVAL); - bus = snd_magic_kmalloc(ac97_bus_t, 0, GFP_KERNEL); + snd_assert(rbus != NULL, return -EINVAL); + bus = kcalloc(1, sizeof(*bus), GFP_KERNEL); if (bus == NULL) return -ENOMEM; - *bus = *_bus; bus->card = card; - if (bus->clock == 0) - bus->clock = 48000; + bus->num = num; + bus->ops = ops; + bus->private_data = private_data; + bus->clock = 48000; spin_lock_init(&bus->bus_lock); snd_ac97_bus_proc_init(bus); - if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, bus, &ops)) < 0) { + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, bus, &dev_ops)) < 0) { snd_ac97_bus_free(bus); return err; } @@ -1732,25 +1814,23 @@ int snd_ac97_bus(snd_card_t * card, ac97 /** * snd_ac97_mixer - create an Codec97 component * @bus: the AC97 bus which codec is attached to - * @_ac97: the template of ac97, including index, callbacks and + * @template: the template of ac97, including index, callbacks and * the private data. * @rac97: the pointer to store the new ac97 instance. * * Creates an Codec97 component. An ac97_t instance is newly - * allocated and initialized from the template (_ac97). The codec + * allocated and initialized from the template. The codec * is then initialized by the standard procedure. * - * The template must include the valid callbacks (at least read and - * write), the codec number (num) and address (addr), and the private - * data (private_data). The other callbacks, wait and reset, are not - * mandatory. + * The template must include the codec number (num) and address (addr), + * and the private data (private_data). * * The ac97 instance is registered as a low-level device, so you don't * have to release it manually. * * Returns zero if successful, or a negative error code on failure. */ -int snd_ac97_mixer(ac97_bus_t * bus, ac97_t * _ac97, ac97_t ** rac97) +int snd_ac97_mixer(ac97_bus_t *bus, ac97_template_t *template, ac97_t **rac97) { int err; ac97_t *ac97; @@ -1764,33 +1844,63 @@ int snd_ac97_mixer(ac97_bus_t * bus, ac9 snd_assert(rac97 != NULL, return -EINVAL); *rac97 = NULL; - snd_assert(bus != NULL && _ac97 != NULL, return -EINVAL); - snd_assert(_ac97->num < 4 && bus->codec[_ac97->num] == NULL, return -EINVAL); + snd_assert(bus != NULL && template != NULL, return -EINVAL); + snd_assert(template->num < 4 && bus->codec[template->num] == NULL, return -EINVAL); + + snd_assert(bus->shared_type <= AC97_SHARED_TYPES, return -EINVAL); + if (bus->shared_type) { + /* already shared? */ + down(&shared_codec_mutex); + ac97 = shared_codec[bus->shared_type-1][template->num]; + if (ac97) { + if ((ac97_is_audio(ac97) && (template->scaps & AC97_SCAP_SKIP_AUDIO)) || + (ac97_is_modem(ac97) && (template->scaps & AC97_SCAP_SKIP_MODEM))) { + up(&shared_codec_mutex); + return -EACCES; /* skip this */ + } + } + up(&shared_codec_mutex); + } + card = bus->card; - ac97 = snd_magic_kmalloc(ac97_t, 0, GFP_KERNEL); + ac97 = kcalloc(1, sizeof(*ac97), GFP_KERNEL); if (ac97 == NULL) return -ENOMEM; - *ac97 = *_ac97; + ac97->private_data = template->private_data; + ac97->private_free = template->private_free; ac97->bus = bus; + ac97->pci = template->pci; + ac97->num = template->num; + ac97->addr = template->addr; + ac97->scaps = template->scaps; + ac97->limited_regs = template->limited_regs; + memcpy(ac97->reg_accessed, template->reg_accessed, sizeof(ac97->reg_accessed)); bus->codec[ac97->num] = ac97; spin_lock_init(&ac97->reg_lock); + init_MUTEX(&ac97->mutex); if (ac97->pci) { pci_read_config_word(ac97->pci, PCI_SUBSYSTEM_VENDOR_ID, &ac97->subsystem_vendor); pci_read_config_word(ac97->pci, PCI_SUBSYSTEM_ID, &ac97->subsystem_device); } - if (bus->reset) { - bus->reset(ac97); + if (bus->ops->reset) { + bus->ops->reset(ac97); goto __access_ok; } snd_ac97_write(ac97, AC97_RESET, 0); /* reset to defaults */ - if (bus->wait) - bus->wait(ac97); + if (bus->ops->wait) + bus->ops->wait(ac97); else { udelay(50); - if (ac97_reset_wait(ac97, HZ/2, 0) < 0 && - ac97_reset_wait(ac97, HZ/2, 1) < 0) { + if (ac97->scaps & AC97_SCAP_SKIP_AUDIO) + err = ac97_reset_wait(ac97, HZ/2, 1); + else { + err = ac97_reset_wait(ac97, HZ/2, 0); + if (err < 0) + err = ac97_reset_wait(ac97, HZ/2, 1); + } + if (err < 0) { snd_printk(KERN_WARNING "AC'97 %d does not respond - RESET\n", ac97->num); /* proceed anyway - it's often non-critical */ } @@ -1803,26 +1913,12 @@ int snd_ac97_mixer(ac97_bus_t * bus, ac9 snd_ac97_free(ac97); return -EIO; } - /* AC97 audio codec chip revision detection. */ - /* Currently only Realtek ALC650 detection implemented. */ - switch(ac97->id & 0xfffffff0) { - case 0x414c4720: /* ALC650 */ - reg = snd_ac97_read(ac97, AC97_ALC650_REVISION); - if (((reg & 0x3f) >= 0) && ((reg & 0x3f) < 3)) - ac97->id = 0x414c4720; /* Old version */ - else if (((reg & 0x3f) >= 3) && ((reg & 0x3f) < 0x10)) - ac97->id = 0x414c4721; /* D version */ - else if ((reg&0x30) == 0x10) - ac97->id = 0x414c4722; /* E version */ - else if ((reg&0x30) == 0x20) - ac97->id = 0x414c4723; /* F version */ - } /* test for AC'97 */ if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO) && !(ac97->scaps & AC97_SCAP_AUDIO)) { /* test if we can write to the record gain volume register */ snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a06); - if ((err = snd_ac97_read(ac97, AC97_REC_GAIN)) == 0x8a06) + if (((err = snd_ac97_read(ac97, AC97_REC_GAIN)) & 0x7fff) == 0x0a06) ac97->scaps |= AC97_SCAP_AUDIO; } if (ac97->scaps & AC97_SCAP_AUDIO) { @@ -1848,7 +1944,7 @@ int snd_ac97_mixer(ac97_bus_t * bus, ac9 return -EACCES; } - if (bus->reset) // FIXME: always skipping? + if (bus->ops->reset) // FIXME: always skipping? goto __ready_ok; /* FIXME: add powerdown control */ @@ -1865,9 +1961,9 @@ int snd_ac97_mixer(ac97_bus_t * bus, ac9 if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0f) == 0x0f) goto __ready_ok; set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ/10); + schedule_timeout(1); } while (time_after_eq(end_time, jiffies)); - snd_printk(KERN_ERR "AC'97 %d analog subsections not ready\n", ac97->num); + snd_printk(KERN_WARNING "AC'97 %d analog subsections not ready\n", ac97->num); } /* FIXME: add powerdown control */ @@ -1898,9 +1994,9 @@ int snd_ac97_mixer(ac97_bus_t * bus, ac9 if ((snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS) & tmp) == tmp) goto __ready_ok; set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ/10); + schedule_timeout(1); } while (time_after_eq(end_time, jiffies)); - snd_printk(KERN_ERR "MC'97 %d converters and GPIO not ready (0x%x)\n", ac97->num, snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS)); + snd_printk(KERN_WARNING "MC'97 %d converters and GPIO not ready (0x%x)\n", ac97->num, snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS)); } __ready_ok: @@ -1919,12 +2015,7 @@ int snd_ac97_mixer(ac97_bus_t * bus, ac9 } if (ac97->ext_id & AC97_EI_SPDIF) { /* codec specific code (patch) should override these values */ - if (ac97->flags & AC97_CS_SPDIF) - ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100; - else if (ac97->id == AC97_ID_CM9739) - ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; - else - ac97->rates[AC97_RATES_SPDIF] = snd_ac97_determine_spdif_rates(ac97); + ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_32000; } if (ac97->ext_id & AC97_EI_VRM) { /* MIC VRA support */ snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, 0, &ac97->rates[AC97_RATES_MIC_ADC]); @@ -1940,10 +2031,10 @@ int snd_ac97_mixer(ac97_bus_t * bus, ac9 ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC; } /* additional initializations */ - if (bus->init) - bus->init(ac97); - snd_ac97_get_name(ac97, ac97->id, name, 0); - snd_ac97_get_name(NULL, ac97->id, name, 0); // ac97->id might be changed in the special setup code + if (bus->ops->init) + bus->ops->init(ac97); + snd_ac97_get_name(ac97, ac97->id, name, !ac97_is_audio(ac97)); + snd_ac97_get_name(NULL, ac97->id, name, !ac97_is_audio(ac97)); // ac97->id might be changed in the special setup code if (ac97_is_audio(ac97)) { if (card->mixername[0] == '\0') { strcpy(card->mixername, name); @@ -1995,6 +2086,13 @@ int snd_ac97_mixer(ac97_bus_t * bus, ac9 return err; } *rac97 = ac97; + + if (bus->shared_type) { + down(&shared_codec_mutex); + shared_codec[bus->shared_type-1][ac97->num] = ac97; + up(&shared_codec_mutex); + } + return 0; } @@ -2054,8 +2152,8 @@ void snd_ac97_resume(ac97_t *ac97) { int i, is_ad18xx, codec; - if (ac97->bus->reset) { - ac97->bus->reset(ac97); + if (ac97->bus->ops->reset) { + ac97->bus->ops->reset(ac97); goto __reset_ready; } @@ -2066,23 +2164,33 @@ void snd_ac97_resume(ac97_t *ac97) snd_ac97_write(ac97, AC97_GENERAL_PURPOSE, 0); snd_ac97_write(ac97, AC97_POWERDOWN, ac97->regs[AC97_POWERDOWN]); - ac97->bus->write(ac97, AC97_MASTER, 0x8101); - for (i = 0; i < 10; i++) { - if (snd_ac97_read(ac97, AC97_MASTER) == 0x8101) - break; - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } - /* FIXME: extra delay */ - ac97->bus->write(ac97, AC97_MASTER, 0x8000); - if (snd_ac97_read(ac97, AC97_MASTER) != 0x8000) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ/4); + if (ac97_is_audio(ac97)) { + ac97->bus->ops->write(ac97, AC97_MASTER, 0x8101); + for (i = HZ/10; i >= 0; i--) { + if (snd_ac97_read(ac97, AC97_MASTER) == 0x8101) + break; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } + /* FIXME: extra delay */ + ac97->bus->ops->write(ac97, AC97_MASTER, 0x8000); + if (snd_ac97_read(ac97, AC97_MASTER) != 0x8000) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/4); + } + } else { + for (i = HZ/10; i >= 0; i--) { + unsigned short val = snd_ac97_read(ac97, AC97_EXTENDED_MID); + if (val != 0xffff && (val & 1) != 0) + break; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } } __reset_ready: - if (ac97->bus->init) - ac97->bus->init(ac97); + if (ac97->bus->ops->init) + ac97->bus->ops->init(ac97); is_ad18xx = (ac97->flags & AC97_AD_MULTI); if (is_ad18xx) { @@ -2093,7 +2201,7 @@ __reset_ready: /* select single codec */ snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, ac97->spec.ad18xx.unchained[codec] | ac97->spec.ad18xx.chained[codec]); - ac97->bus->write(ac97, AC97_AD_CODEC_CFG, ac97->spec.ad18xx.codec_cfg[codec]); + ac97->bus->ops->write(ac97, AC97_AD_CODEC_CFG, ac97->spec.ad18xx.codec_cfg[codec]); } /* select all codecs */ snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, 0x7000); @@ -2118,7 +2226,7 @@ __reset_ready: snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, ac97->spec.ad18xx.unchained[codec] | ac97->spec.ad18xx.chained[codec]); /* update PCM bits */ - ac97->bus->write(ac97, AC97_PCM, ac97->spec.ad18xx.pcmreg[codec]); + ac97->bus->ops->write(ac97, AC97_PCM, ac97->spec.ad18xx.pcmreg[codec]); } /* select all codecs */ snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, 0x7000); @@ -2151,42 +2259,57 @@ __reset_ready: /* */ -int snd_ac97_remove_ctl(ac97_t *ac97, const char *name) +static void set_ctl_name(char *dst, const char *src, const char *suffix) +{ + if (suffix) + sprintf(dst, "%s %s", src, suffix); + else + strcpy(dst, src); +} + +int snd_ac97_remove_ctl(ac97_t *ac97, const char *name, const char *suffix) { snd_ctl_elem_id_t id; memset(&id, 0, sizeof(id)); - strcpy(id.name, name); + set_ctl_name(id.name, name, suffix); id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; return snd_ctl_remove_id(ac97->bus->card, &id); } -static snd_kcontrol_t *ctl_find(ac97_t *ac97, const char *name) +static snd_kcontrol_t *ctl_find(ac97_t *ac97, const char *name, const char *suffix) { snd_ctl_elem_id_t sid; memset(&sid, 0, sizeof(sid)); - strcpy(sid.name, name); + set_ctl_name(sid.name, name, suffix); sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER; return snd_ctl_find_id(ac97->bus->card, &sid); } -int snd_ac97_rename_ctl(ac97_t *ac97, const char *src, const char *dst) +int snd_ac97_rename_ctl(ac97_t *ac97, const char *src, const char *dst, const char *suffix) { - snd_kcontrol_t *kctl = ctl_find(ac97, src); + snd_kcontrol_t *kctl = ctl_find(ac97, src, suffix); if (kctl) { - strcpy(kctl->id.name, dst); + set_ctl_name(kctl->id.name, dst, suffix); return 0; } return -ENOENT; } -int snd_ac97_swap_ctl(ac97_t *ac97, const char *s1, const char *s2) +/* rename both Volume and Switch controls - don't check the return value */ +void snd_ac97_rename_vol_ctl(ac97_t *ac97, const char *src, const char *dst) +{ + snd_ac97_rename_ctl(ac97, src, dst, "Switch"); + snd_ac97_rename_ctl(ac97, src, dst, "Volume"); +} + +int snd_ac97_swap_ctl(ac97_t *ac97, const char *s1, const char *s2, const char *suffix) { snd_kcontrol_t *kctl1, *kctl2; - kctl1 = ctl_find(ac97, s1); - kctl2 = ctl_find(ac97, s2); + kctl1 = ctl_find(ac97, s1, suffix); + kctl2 = ctl_find(ac97, s2, suffix); if (kctl1 && kctl2) { - strcpy(kctl1->id.name, s2); - strcpy(kctl2->id.name, s1); + set_ctl_name(kctl1->id.name, s2, suffix); + set_ctl_name(kctl2->id.name, s1, suffix); return 0; } return -ENOENT; @@ -2194,26 +2317,22 @@ int snd_ac97_swap_ctl(ac97_t *ac97, cons static int swap_headphone(ac97_t *ac97, int remove_master) { - /* FIXME: error checks.. */ if (remove_master) { - if (ctl_find(ac97, "Headphone Playback Switch") == NULL) + if (ctl_find(ac97, "Headphone Playback Switch", NULL) == NULL) return 0; - snd_ac97_remove_ctl(ac97, "Master Playback Switch"); - snd_ac97_remove_ctl(ac97, "Master Playback Volume"); - } else { - snd_ac97_rename_ctl(ac97, "Master Playback Switch", "Line-Out Playback Switch"); - snd_ac97_rename_ctl(ac97, "Master Playback Volume", "Line-Out Playback Volume"); - } - snd_ac97_rename_ctl(ac97, "Headphone Playback Switch", "Master Playback Switch"); - snd_ac97_rename_ctl(ac97, "Headphone Playback Volume", "Master Playback Volume"); + snd_ac97_remove_ctl(ac97, "Master Playback", "Switch"); + snd_ac97_remove_ctl(ac97, "Master Playback", "Volume"); + } else + snd_ac97_rename_vol_ctl(ac97, "Master Playback", "Line-Out Playback"); + snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Master Playback"); return 0; } static int swap_surround(ac97_t *ac97) { /* FIXME: error checks.. */ - snd_ac97_swap_ctl(ac97, "Master Playback Switch", "Surround Playback Switch"); - snd_ac97_swap_ctl(ac97, "Master Playback Volume", "Surround Playback Volume"); + snd_ac97_swap_ctl(ac97, "Master Playback", "Surround Playback", "Switch"); + snd_ac97_swap_ctl(ac97, "Master Playback", "Surround Playback", "Volume"); return 0; } --- linux-2.6.9-rc1/sound/pci/ac97/ac97_id.h 2004-08-24 00:03:38.000000000 -0700 +++ 25/sound/pci/ac97/ac97_id.h 2004-09-02 20:51:53.000000000 -0700 @@ -45,7 +45,14 @@ #define AC97_ID_CS4201 0x43525948 #define AC97_ID_CS4205 0x43525958 #define AC97_ID_CS_MASK 0xfffffff8 /* bit 0-2: rev */ +#define AC97_ID_ALC100 0x414c4300 #define AC97_ID_ALC650 0x414c4720 +#define AC97_ID_ALC650D 0x414c4721 +#define AC97_ID_ALC650E 0x414c4722 +#define AC97_ID_ALC650F 0x414c4723 +#define AC97_ID_ALC655 0x414c4760 +#define AC97_ID_ALC658 0x414c4780 +#define AC97_ID_ALC850 0x414c4790 #define AC97_ID_YMF753 0x594d4803 #define AC97_ID_VT1616 0x49434551 #define AC97_ID_CM9738 0x434d4941 --- linux-2.6.9-rc1/sound/pci/ac97/ac97_local.h 2004-08-24 00:02:25.000000000 -0700 +++ 25/sound/pci/ac97/ac97_local.h 2004-09-02 20:51:53.000000000 -0700 @@ -23,10 +23,15 @@ */ #define AC97_SINGLE_VALUE(reg,shift,mask,invert) ((reg) | ((shift) << 8) | ((mask) << 16) | ((invert) << 24)) +#define AC97_PAGE_SINGLE_VALUE(reg,shift,mask,invert,page) ((reg) | ((shift) << 8) | ((mask) << 16) | ((invert) << 24) | ((page) << 25)) #define AC97_SINGLE(xname, reg, shift, mask, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_info_single, \ .get = snd_ac97_get_single, .put = snd_ac97_put_single, \ .private_value = AC97_SINGLE_VALUE(reg, shift, mask, invert) } +#define AC97_PAGE_SINGLE(xname, reg, shift, mask, invert, page) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_info_single, \ + .get = snd_ac97_page_get_single, .put = snd_ac97_page_put_single, \ + .private_value = AC97_PAGE_SINGLE_VALUE(reg, shift, mask, invert, page) } /* ac97_codec.c */ extern const char *snd_ac97_stereo_enhancements[]; @@ -37,10 +42,13 @@ void snd_ac97_get_name(ac97_t *ac97, uns int snd_ac97_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo); int snd_ac97_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); int snd_ac97_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); +int snd_ac97_page_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); +int snd_ac97_page_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol); int snd_ac97_try_bit(ac97_t * ac97, int reg, int bit); -int snd_ac97_remove_ctl(ac97_t *ac97, const char *name); -int snd_ac97_rename_ctl(ac97_t *ac97, const char *src, const char *dst); -int snd_ac97_swap_ctl(ac97_t *ac97, const char *s1, const char *s2); +int snd_ac97_remove_ctl(ac97_t *ac97, const char *name, const char *suffix); +int snd_ac97_rename_ctl(ac97_t *ac97, const char *src, const char *dst, const char *suffix); +int snd_ac97_swap_ctl(ac97_t *ac97, const char *s1, const char *s2, const char *suffix); +void snd_ac97_rename_vol_ctl(ac97_t *ac97, const char *src, const char *dst); /* ac97_proc.c */ void snd_ac97_bus_proc_init(ac97_bus_t * ac97); --- linux-2.6.9-rc1/sound/pci/ac97/ac97_patch.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/sound/pci/ac97/ac97_patch.c 2004-09-02 20:51:53.000000000 -0700 @@ -35,8 +35,6 @@ #include "ac97_id.h" #include "ac97_local.h" -#define chip_t ac97_t - /* * Chip specific initialization */ @@ -51,6 +49,21 @@ static int patch_build_controls(ac97_t * return 0; } +/* set to the page, update bits and restore the page */ +static int ac97_update_bits_page(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value, unsigned short page) +{ + unsigned short page_save; + int ret; + + down(&ac97->mutex); + page_save = snd_ac97_read(ac97, AC97_INT_PAGING) & AC97_PAGE_MASK; + snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page); + ret = snd_ac97_update_bits(ac97, reg, mask, value); + snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, page_save); + up(&ac97->mutex); /* unlock paging */ + return ret; +} + /* The following snd_ac97_ymf753_... items added by David Shust (dshust@shustring.com) */ /* It is possible to indicate to the Yamaha YMF753 the type of speakers being used. */ @@ -204,7 +217,7 @@ static int patch_yamaha_ymf753_3d(ac97_t if ((err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0) return err; strcpy(kctl->id.name, "3D Control - Wide"); - kctl->private_value = AC97_3D_CONTROL | (9 << 8) | (7 << 16); + kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 9, 7, 0); snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&snd_ac97_ymf753_controls_speaker, ac97))) < 0) return err; @@ -315,7 +328,7 @@ static int patch_sigmatel_stac9700_3d(ac if ((err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0) return err; strcpy(kctl->id.name, "3D Control Sigmatel - Depth"); - kctl->private_value = AC97_3D_CONTROL | (3 << 16); + kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 2, 3, 0); snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); return 0; } @@ -328,11 +341,11 @@ static int patch_sigmatel_stac9708_3d(ac if ((err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0) return err; strcpy(kctl->id.name, "3D Control Sigmatel - Depth"); - kctl->private_value = AC97_3D_CONTROL | (3 << 16); + kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 0, 3, 0); if ((err = snd_ctl_add(ac97->bus->card, kctl = snd_ac97_cnew(&snd_ac97_controls_3d[0], ac97))) < 0) return err; strcpy(kctl->id.name, "3D Control Sigmatel - Rear Depth"); - kctl->private_value = AC97_3D_CONTROL | (2 << 8) | (3 << 16); + kctl->private_value = AC97_SINGLE_VALUE(AC97_3D_CONTROL, 2, 3, 0); snd_ac97_write_cache(ac97, AC97_3D_CONTROL, 0x0000); return 0; } @@ -373,22 +386,29 @@ static struct snd_ac97_build_ops patch_s .build_specific = patch_sigmatel_stac97xx_specific }; -static struct snd_ac97_build_ops patch_sigmatel_stac9708_ops = { - .build_3d = patch_sigmatel_stac9708_3d, - .build_specific = patch_sigmatel_stac97xx_specific -}; - int patch_sigmatel_stac9700(ac97_t * ac97) { ac97->build_ops = &patch_sigmatel_stac9700_ops; return 0; } +static int patch_sigmatel_stac9708_specific(ac97_t *ac97) +{ + snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Sigmatel Surround Playback"); + return patch_sigmatel_stac97xx_specific(ac97); +} + +static struct snd_ac97_build_ops patch_sigmatel_stac9708_ops = { + .build_3d = patch_sigmatel_stac9708_3d, + .build_specific = patch_sigmatel_stac9708_specific +}; + int patch_sigmatel_stac9708(ac97_t * ac97) { unsigned int codec72, codec6c; ac97->build_ops = &patch_sigmatel_stac9708_ops; + ac97->caps |= 0x10; /* HP (sigmatel surround) support */ codec72 = snd_ac97_read(ac97, AC97_SIGMATEL_BIAS2) & 0x8000; codec6c = snd_ac97_read(ac97, AC97_SIGMATEL_ANALOG); @@ -467,11 +487,11 @@ static int snd_ac97_stac9758_output_jack int shift = kcontrol->private_value; unsigned short val; - val = ac97->regs[AC97_SIGMATEL_OUTSEL]; - if (!((val >> shift) & 4)) + val = ac97->regs[AC97_SIGMATEL_OUTSEL] >> shift; + if (!(val & 4)) ucontrol->value.enumerated.item[0] = 0; else - ucontrol->value.enumerated.item[0] = 1 + ((val >> shift) & 3); + ucontrol->value.enumerated.item[0] = 1 + (val & 3); return 0; } @@ -487,8 +507,8 @@ static int snd_ac97_stac9758_output_jack val = 0; else val = 4 | (ucontrol->value.enumerated.item[0] - 1); - return snd_ac97_update_bits(ac97, AC97_SIGMATEL_OUTSEL, - 7 << shift, val << shift); + return ac97_update_bits_page(ac97, AC97_SIGMATEL_OUTSEL, + 7 << shift, val << shift, 0); } static int snd_ac97_stac9758_input_jack_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) @@ -521,8 +541,8 @@ static int snd_ac97_stac9758_input_jack_ ac97_t *ac97 = snd_kcontrol_chip(kcontrol); int shift = kcontrol->private_value; - return snd_ac97_update_bits(ac97, AC97_SIGMATEL_INSEL, 7 << shift, - ucontrol->value.enumerated.item[0] << shift); + return ac97_update_bits_page(ac97, AC97_SIGMATEL_INSEL, 7 << shift, + ucontrol->value.enumerated.item[0] << shift, 0); } static int snd_ac97_stac9758_phonesel_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) @@ -550,8 +570,8 @@ static int snd_ac97_stac9758_phonesel_pu { ac97_t *ac97 = snd_kcontrol_chip(kcontrol); - return snd_ac97_update_bits(ac97, AC97_SIGMATEL_IOMISC, 3, - ucontrol->value.enumerated.item[0]); + return ac97_update_bits_page(ac97, AC97_SIGMATEL_IOMISC, 3, + ucontrol->value.enumerated.item[0], 0); } #define STAC9758_OUTPUT_JACK(xname, shift) \ @@ -596,6 +616,14 @@ static int patch_sigmatel_stac9758_speci ARRAY_SIZE(snd_ac97_sigmatel_stac9758_controls)); if (err < 0) return err; + /* DAC-A direct */ + snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Front Playback"); + /* DAC-A to Mix = PCM */ + /* DAC-B direct = Surround */ + /* DAC-B to Mix */ + snd_ac97_rename_vol_ctl(ac97, "Video Playback", "Surround Mix Playback"); + /* DAC-C direct = Center/LFE */ + return 0; } @@ -613,16 +641,16 @@ int patch_sigmatel_stac9758(ac97_t * ac9 AC97_SIGMATEL_VARIOUS }; static unsigned short def_regs[4] = { - /* OUTSEL */ 0xd794, + /* OUTSEL */ 0xd794, /* CL:CL, SR:SR, LO:MX, LI:DS, MI:DS */ /* IOMISC */ 0x2001, - /* INSEL */ 0x0201, + /* INSEL */ 0x0201, /* LI:LI, MI:M1 */ /* VARIOUS */ 0x0040 }; static unsigned short m675_regs[4] = { - /* OUTSEL */ 0x9040, - /* IOMISC */ 0x2102, - /* INSEL */ 0x0203, - /* VARIOUS */ 0x0041 + /* OUTSEL */ 0xfc70, /* CL:MX, SR:MX, LO:DS, LI:MX, MI:DS */ + /* IOMISC */ 0x2102, /* HP amp on */ + /* INSEL */ 0x0203, /* LI:LI, MI:FR */ + /* VARIOUS */ 0x0041 /* stereo mic */ }; unsigned short *pregs = def_regs; int i; @@ -635,6 +663,8 @@ int patch_sigmatel_stac9758(ac97_t * ac9 // patch for SigmaTel ac97->build_ops = &patch_sigmatel_stac9758_ops; + /* FIXME: assume only page 0 for writing cache */ + snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, AC97_PAGE_VENDOR); for (i = 0; i < 4; i++) snd_ac97_write_cache(ac97, regs[i], pregs[i]); @@ -654,8 +684,10 @@ static int patch_cirrus_build_spdif(ac97 { int err; + /* con mask, pro mask, default */ if ((err = patch_build_controls(ac97, &snd_ac97_controls_spdif[0], 3)) < 0) return err; + /* switch, spsa */ if ((err = patch_build_controls(ac97, &snd_ac97_cirrus_controls_spdif[0], 1)) < 0) return err; switch (ac97->id & AC97_ID_CS_MASK) { @@ -714,8 +746,10 @@ static int patch_conexant_build_spdif(ac { int err; + /* con mask, pro mask, default */ if ((err = patch_build_controls(ac97, &snd_ac97_controls_spdif[0], 3)) < 0) return err; + /* switch */ if ((err = patch_build_controls(ac97, &snd_ac97_conexant_controls_spdif[0], 1)) < 0) return err; /* set default PCM S/PDIF params */ @@ -734,6 +768,7 @@ int patch_conexant(ac97_t * ac97) ac97->build_ops = &patch_conexant_ops; ac97->flags |= AC97_CX_SPDIF; ac97->ext_id |= AC97_EI_SPDIF; /* force the detection of spdif */ + ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; /* 48k only */ return 0; } @@ -821,8 +856,6 @@ int patch_ad1881(ac97_t * ac97) unsigned short val; int idx, num; - init_MUTEX(&ac97->spec.ad18xx.mutex); - val = snd_ac97_read(ac97, AC97_AD_SERIAL_CFG); snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, val); codecs[0] = patch_ad1881_unchained(ac97, 0, (1<<12)); @@ -872,6 +905,8 @@ static const snd_kcontrol_new_t snd_ac97 /* AC97_SINGLE("Digital Audio Mode", AC97_AD_MISC, 12, 1, 0), */ /* seems problematic */ AC97_SINGLE("Low Power Mixer", AC97_AD_MISC, 14, 1, 0), AC97_SINGLE("Zero Fill DAC", AC97_AD_MISC, 15, 1, 0), + AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 9, 1, 1), /* inverted */ + AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 8, 1, 1), /* inverted */ }; static int patch_ad1885_specific(ac97_t * ac97) @@ -889,16 +924,10 @@ static struct snd_ac97_build_ops patch_a int patch_ad1885(ac97_t * ac97) { - unsigned short jack; - patch_ad1881(ac97); /* This is required to deal with the Intel D815EEAL2 */ /* i.e. Line out is actually headphone out from codec */ - /* turn off jack sense bits D8 & D9 */ - jack = snd_ac97_read(ac97, AC97_AD_JACK_SPDIF); - snd_ac97_write_cache(ac97, AC97_AD_JACK_SPDIF, jack | 0x0300); - /* set default */ snd_ac97_write_cache(ac97, AC97_AD_MISC, 0x0404); @@ -1114,10 +1143,8 @@ static const snd_kcontrol_new_t snd_ac97 static int patch_ad1888_specific(ac97_t *ac97) { /* rename 0x04 as "Master" and 0x02 as "Master Surround" */ - snd_ac97_rename_ctl(ac97, "Master Playback Switch", "Master Surround Playback Switch"); - snd_ac97_rename_ctl(ac97, "Master Playback Volume", "Master Surround Playback Volume"); - snd_ac97_rename_ctl(ac97, "Headphone Playback Switch", "Master Playback Switch"); - snd_ac97_rename_ctl(ac97, "Headphone Playback Volume", "Master Playback Volume"); + snd_ac97_rename_vol_ctl(ac97, "Master Playback", "Master Surround Playback"); + snd_ac97_rename_vol_ctl(ac97, "Headphone Playback", "Master Playback"); return patch_build_controls(ac97, snd_ac97_ad1888_controls, ARRAY_SIZE(snd_ac97_ad1888_controls)); } @@ -1213,7 +1240,7 @@ int patch_ad1985(ac97_t * ac97) } /* - * realtek ALC65x codecs + * realtek ALC65x/850 codecs */ static int snd_ac97_alc650_mic_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) { @@ -1303,6 +1330,17 @@ int patch_alc650(ac97_t * ac97) ac97->build_ops = &patch_alc650_ops; + /* determine the revision */ + val = snd_ac97_read(ac97, AC97_ALC650_REVISION) & 0x3f; + if (val < 3) + ac97->id = 0x414c4720; /* Old version */ + else if (val < 0x10) + ac97->id = 0x414c4721; /* D version */ + else if (val < 0x20) + ac97->id = 0x414c4722; /* E version */ + else if (val < 0x30) + ac97->id = 0x414c4723; /* F version */ + /* revision E or F */ /* FIXME: what about revision D ? */ ac97->spec.dev_flags = (ac97->id == 0x414c4722 || @@ -1351,20 +1389,19 @@ static int snd_ac97_alc655_mic_get(snd_k static int snd_ac97_alc655_mic_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) { ac97_t *ac97 = snd_kcontrol_chip(kcontrol); - int change; /* misc control; vrefout disable */ snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12, ucontrol->value.integer.value[0] ? (1 << 12) : 0); - change = snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 10, - ucontrol->value.integer.value[0] ? (1 << 10) : 0); - return change; + return ac97_update_bits_page(ac97, AC97_ALC650_MULTICH, 1 << 10, + ucontrol->value.integer.value[0] ? (1 << 10) : 0, + 0); } static const snd_kcontrol_new_t snd_ac97_controls_alc655[] = { - AC97_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0), - AC97_SINGLE("Line-In As Surround", AC97_ALC650_MULTICH, 9, 1, 0), + AC97_PAGE_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0, 0), + AC97_PAGE_SINGLE("Line-In As Surround", AC97_ALC650_MULTICH, 9, 1, 0, 0), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Mic As Center/LFE", @@ -1391,7 +1428,6 @@ static int alc655_iec958_route_info(snd_ texts_658[uinfo->value.enumerated.item] : texts_655[uinfo->value.enumerated.item]); return 0; - } static int alc655_iec958_route_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) @@ -1410,13 +1446,15 @@ static int alc655_iec958_route_get(snd_k static int alc655_iec958_route_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { ac97_t *ac97 = snd_kcontrol_chip(kcontrol); - return snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 3 << 12, - (unsigned short)ucontrol->value.enumerated.item[0]); + + return ac97_update_bits_page(ac97, AC97_ALC650_MULTICH, 3 << 12, + (unsigned short)ucontrol->value.enumerated.item[0], + 0); } static const snd_kcontrol_new_t snd_ac97_spdif_controls_alc655[] = { - AC97_SINGLE("IEC958 Capture Switch", AC97_ALC650_MULTICH, 11, 1, 0), - AC97_SINGLE("IEC958 Input Monitor", AC97_ALC650_MULTICH, 14, 1, 0), + AC97_PAGE_SINGLE("IEC958 Capture Switch", AC97_ALC650_MULTICH, 11, 1, 0, 0), + AC97_PAGE_SINGLE("IEC958 Input Monitor", AC97_ALC650_MULTICH, 14, 1, 0, 0), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "IEC958 Playback Route", @@ -1451,6 +1489,9 @@ int patch_alc655(ac97_t * ac97) ac97->build_ops = &patch_alc655_ops; + /* assume only page 0 for writing cache */ + snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, AC97_PAGE_VENDOR); + /* adjust default values */ val = snd_ac97_read(ac97, 0x7a); /* misc control */ val |= (1 << 1); /* spdif input pin */ @@ -1469,6 +1510,120 @@ int patch_alc655(ac97_t * ac97) return 0; } + +#define AC97_ALC850_JACK_SELECT 0x76 +#define AC97_ALC850_MISC1 0x7a + +static int ac97_alc850_surround_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = ((ac97->regs[AC97_ALC850_JACK_SELECT] >> 12) & 7) == 2; + return 0; +} + +static int ac97_alc850_surround_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + + /* SURR 1kOhm (bit4), Amp (bit5) */ + snd_ac97_update_bits(ac97, AC97_ALC850_MISC1, (1<<4)|(1<<5), + ucontrol->value.integer.value[0] ? (1<<5) : (1<<4)); + /* LINE-IN = 0, SURROUND = 2 */ + return snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 12, + ucontrol->value.integer.value[0] ? (2<<12) : (0<<12)); +} + +static int ac97_alc850_mic_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = ((ac97->regs[AC97_ALC850_JACK_SELECT] >> 4) & 7) == 2; + return 0; +} + +static int ac97_alc850_mic_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + ac97_t *ac97 = snd_kcontrol_chip(kcontrol); + + /* Vref disable (bit12), 1kOhm (bit13) */ + snd_ac97_update_bits(ac97, AC97_ALC850_MISC1, (1<<12)|(1<<13), + ucontrol->value.integer.value[0] ? (1<<12) : (1<<13)); + /* MIC-IN = 1, CENTER-LFE = 2 */ + return snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 4, + ucontrol->value.integer.value[0] ? (2<<4) : (1<<4)); +} + +static const snd_kcontrol_new_t snd_ac97_controls_alc850[] = { + AC97_PAGE_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0, 0), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Line-In As Surround", + .info = snd_ac97_info_single, + .get = ac97_alc850_surround_get, + .put = ac97_alc850_surround_put, + .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */ + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mic As Center/LFE", + .info = snd_ac97_info_single, + .get = ac97_alc850_mic_get, + .put = ac97_alc850_mic_put, + .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */ + }, + +}; + +static int patch_alc850_specific(ac97_t *ac97) +{ + int err; + + if ((err = patch_build_controls(ac97, snd_ac97_controls_alc850, ARRAY_SIZE(snd_ac97_controls_alc850))) < 0) + return err; + if (ac97->ext_id & AC97_EI_SPDIF) { + if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc655, ARRAY_SIZE(snd_ac97_spdif_controls_alc655))) < 0) + return err; + } + return 0; +} + +static struct snd_ac97_build_ops patch_alc850_ops = { + .build_specific = patch_alc850_specific +}; + +int patch_alc850(ac97_t *ac97) +{ + ac97->build_ops = &patch_alc850_ops; + + ac97->spec.dev_flags = 0; /* for IEC958 playback route - ALC655 compatible */ + + /* assume only page 0 for writing cache */ + snd_ac97_update_bits(ac97, AC97_INT_PAGING, AC97_PAGE_MASK, AC97_PAGE_VENDOR); + + /* adjust default values */ + /* set default: spdif-in enabled, + spdif-in monitor off, spdif-in PCM off + center on mic off, surround on line-in off + duplicate front off + */ + snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, 1<<15); + /* SURR_OUT: on, Surr 1kOhm: on, Surr Amp: off, Front 1kOhm: off + * Front Amp: on, Vref: enable, Center 1kOhm: on, Mix: on + */ + snd_ac97_write_cache(ac97, 0x7a, (1<<1)|(1<<4)|(0<<5)|(1<<6)| + (1<<7)|(0<<12)|(1<<13)|(0<<14)); + /* detection UIO2,3: all path floating, UIO3: MIC, Vref2: disable, + * UIO1: FRONT, Vref3: disable, UIO3: LINE, Front-Mic: mute + */ + snd_ac97_write_cache(ac97, 0x76, (0<<0)|(0<<2)|(1<<4)|(1<<7)|(2<<8)| + (1<<11)|(0<<12)|(1<<15)); + + /* full DAC volume */ + snd_ac97_write_cache(ac97, AC97_ALC650_SURR_DAC_VOL, 0x0808); + snd_ac97_write_cache(ac97, AC97_ALC650_LFE_DAC_VOL, 0x0808); + return 0; +} + + /* * C-Media CM97xx codecs */ @@ -1599,8 +1754,10 @@ int patch_cm9739(ac97_t * ac97) /* enable spdif in */ snd_ac97_write_cache(ac97, AC97_CM9739_SPDIF_CTRL, snd_ac97_read(ac97, AC97_CM9739_SPDIF_CTRL) | 0x01); + ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; /* 48k only */ } else { ac97->ext_id &= ~AC97_EI_SPDIF; /* disable extended-id */ + ac97->rates[AC97_RATES_SPDIF] = 0; } /* set-up multi channel */ --- linux-2.6.9-rc1/sound/pci/ac97/ac97_patch.h 2004-08-24 00:01:51.000000000 -0700 +++ 25/sound/pci/ac97/ac97_patch.h 2004-09-02 20:51:53.000000000 -0700 @@ -49,6 +49,7 @@ int patch_ad1981b(ac97_t * ac97); int patch_ad1985(ac97_t * ac97); int patch_alc650(ac97_t * ac97); int patch_alc655(ac97_t * ac97); +int patch_alc850(ac97_t * ac97); int patch_cm9738(ac97_t * ac97); int patch_cm9739(ac97_t * ac97); int patch_vt1616(ac97_t * ac97); --- linux-2.6.9-rc1/sound/pci/ac97/ac97_pcm.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/sound/pci/ac97/ac97_pcm.c 2004-09-02 20:51:53.000000000 -0700 @@ -36,8 +36,6 @@ #include "ac97_id.h" #include "ac97_local.h" -#define chip_t ac97_t - /* * PCM support */ @@ -430,7 +428,7 @@ int snd_ac97_pcm_assign(ac97_bus_t *bus, unsigned int rates; ac97_t *codec; - rpcms = snd_kcalloc(sizeof(struct ac97_pcm) * pcms_count, GFP_KERNEL); + rpcms = kcalloc(pcms_count, sizeof(struct ac97_pcm), GFP_KERNEL); if (rpcms == NULL) return -ENOMEM; memset(avail_slots, 0, sizeof(avail_slots)); @@ -489,7 +487,10 @@ int snd_ac97_pcm_assign(ac97_bus_t *bus, rpcm->r[0].rslots[j] = tmp; rpcm->r[0].codec[j] = bus->codec[j]; rpcm->r[0].rate_table[j] = rate_table[pcm->stream][j]; - rates = get_rates(rpcm, j, tmp, 0); + if (bus->no_vra) + rates = SNDRV_PCM_RATE_48000; + else + rates = get_rates(rpcm, j, tmp, 0); if (pcm->exclusive) avail_slots[pcm->stream][j] &= ~tmp; } --- linux-2.6.9-rc1/sound/pci/ac97/ac97_proc.c 2004-08-24 00:03:49.000000000 -0700 +++ 25/sound/pci/ac97/ac97_proc.c 2004-09-02 20:51:53.000000000 -0700 @@ -290,11 +290,11 @@ static void snd_ac97_proc_read_main(ac97 static void snd_ac97_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) { - ac97_t *ac97 = snd_magic_cast(ac97_t, entry->private_data, return); + ac97_t *ac97 = entry->private_data; + down(&ac97->mutex); if ((ac97->id & 0xffffff40) == AC97_ID_AD1881) { // Analog Devices AD1881/85/86 int idx; - down(&ac97->spec.ad18xx.mutex); for (idx = 0; idx < 3; idx++) if (ac97->spec.ad18xx.id[idx]) { /* select single codec */ @@ -305,7 +305,6 @@ static void snd_ac97_proc_read(snd_info_ } /* select all codecs */ snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, 0x7000); - up(&ac97->spec.ad18xx.mutex); snd_iprintf(buffer, "\nAD18XX configuration\n"); snd_iprintf(buffer, "Unchained : 0x%04x,0x%04x,0x%04x\n", @@ -319,22 +318,25 @@ static void snd_ac97_proc_read(snd_info_ } else { snd_ac97_proc_read_main(ac97, buffer, 0); } + up(&ac97->mutex); } #ifdef CONFIG_SND_DEBUG /* direct register write for debugging */ static void snd_ac97_proc_regs_write(snd_info_entry_t *entry, snd_info_buffer_t *buffer) { - ac97_t *ac97 = snd_magic_cast(ac97_t, entry->private_data, return); + ac97_t *ac97 = entry->private_data; char line[64]; unsigned int reg, val; + down(&ac97->mutex); while (!snd_info_get_line(buffer, line, sizeof(line))) { if (sscanf(line, "%x %x", ®, &val) != 2) continue; - /* register must be odd */ + /* register must be even */ if (reg < 0x80 && (reg & 1) == 0 && val <= 0xffff) snd_ac97_write_cache(ac97, reg, val); } + up(&ac97->mutex); } #endif @@ -351,12 +353,12 @@ static void snd_ac97_proc_regs_read_main static void snd_ac97_proc_regs_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) { - ac97_t *ac97 = snd_magic_cast(ac97_t, entry->private_data, return); + ac97_t *ac97 = entry->private_data; + down(&ac97->mutex); if ((ac97->id & 0xffffff40) == AC97_ID_AD1881) { // Analog Devices AD1881/85/86 int idx; - down(&ac97->spec.ad18xx.mutex); for (idx = 0; idx < 3; idx++) if (ac97->spec.ad18xx.id[idx]) { /* select single codec */ @@ -366,10 +368,10 @@ static void snd_ac97_proc_regs_read(snd_ } /* select all codecs */ snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 0x7000, 0x7000); - up(&ac97->spec.ad18xx.mutex); } else { snd_ac97_proc_regs_read_main(ac97, buffer, 0); } + up(&ac97->mutex); } void snd_ac97_proc_init(ac97_t * ac97) --- linux-2.6.9-rc1/sound/pci/ac97/ak4531_codec.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/sound/pci/ac97/ak4531_codec.c 2004-09-02 20:51:53.000000000 -0700 @@ -30,8 +30,6 @@ MODULE_AUTHOR("Jaroslav Kysela value.integer.value[0] = val; return 0; -} +} static int snd_ak4531_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { @@ -113,7 +111,7 @@ static int snd_ak4531_put_single(snd_kco ak4531->write(ak4531, reg, ak4531->regs[reg] = val); spin_unlock_irqrestore(&ak4531->reg_lock, flags); return change; -} +} #define AK4531_DOUBLE(xname, xindex, left_reg, right_reg, left_shift, right_shift, mask, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ @@ -155,7 +153,7 @@ static int snd_ak4531_get_double(snd_kco ucontrol->value.integer.value[0] = left; ucontrol->value.integer.value[1] = right; return 0; -} +} static int snd_ak4531_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { @@ -192,7 +190,7 @@ static int snd_ak4531_put_double(snd_kco } spin_unlock_irqrestore(&ak4531->reg_lock, flags); return change; -} +} #define AK4531_INPUT_SW(xname, xindex, reg1, reg2, left_shift, right_shift) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ @@ -225,7 +223,7 @@ static int snd_ak4531_get_input_sw(snd_k ucontrol->value.integer.value[3] = (ak4531->regs[reg2] >> right_shift) & 1; spin_unlock_irqrestore(&ak4531->reg_lock, flags); return 0; -} +} static int snd_ak4531_put_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { @@ -250,9 +248,7 @@ static int snd_ak4531_put_input_sw(snd_k ak4531->write(ak4531, reg2, ak4531->regs[reg2] = val2); spin_unlock_irqrestore(&ak4531->reg_lock, flags); return change; -} - -#define AK4531_CONTROLS (sizeof(snd_ak4531_controls)/sizeof(snd_kcontrol_new_t)) +} static snd_kcontrol_new_t snd_ak4531_controls[] = { @@ -315,14 +311,14 @@ static int snd_ak4531_free(ak4531_t *ak4 if (ak4531) { if (ak4531->private_free) ak4531->private_free(ak4531); - snd_magic_kfree(ak4531); + kfree(ak4531); } return 0; } static int snd_ak4531_dev_free(snd_device_t *device) { - ak4531_t *ak4531 = snd_magic_cast(ak4531_t, device->device_data, return -ENXIO); + ak4531_t *ak4531 = device->device_data; return snd_ak4531_free(ak4531); } @@ -367,7 +363,7 @@ int snd_ak4531_mixer(snd_card_t * card, snd_assert(rak4531 != NULL, return -EINVAL); *rak4531 = NULL; snd_assert(card != NULL && _ak4531 != NULL, return -EINVAL); - ak4531 = snd_magic_kcalloc(ak4531_t, 0, GFP_KERNEL); + ak4531 = kcalloc(1, sizeof(*ak4531), GFP_KERNEL); if (ak4531 == NULL) return -ENOMEM; *ak4531 = *_ak4531; @@ -385,7 +381,7 @@ int snd_ak4531_mixer(snd_card_t * card, continue; ak4531->write(ak4531, idx, ak4531->regs[idx] = snd_ak4531_initial_map[idx]); /* recording source is mixer */ } - for (idx = 0; idx < AK4531_CONTROLS; idx++) { + for (idx = 0; idx < ARRAY_SIZE(snd_ak4531_controls); idx++) { if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_ak4531_controls[idx], ak4531))) < 0) { snd_ak4531_free(ak4531); return err; @@ -411,7 +407,7 @@ int snd_ak4531_mixer(snd_card_t * card, static void snd_ak4531_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) { - ak4531_t *ak4531 = snd_magic_cast(ak4531_t, entry->private_data, return); + ak4531_t *ak4531 = entry->private_data; snd_iprintf(buffer, "Asahi Kasei AK4531\n\n"); snd_iprintf(buffer, "Recording source : %s\n" --- linux-2.6.9-rc1/sound/pci/ali5451/ali5451.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/sound/pci/ali5451/ali5451.c 2004-09-02 20:51:53.000000000 -0700 @@ -43,8 +43,7 @@ MODULE_AUTHOR("Matt Wu "); MODULE_DESCRIPTION("ALI M5451"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{ALI,M5451,pci},{ALI,M5451}}"); +MODULE_SUPPORTED_DEVICE("{{ALI,M5451,pci},{ALI,M5451}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ @@ -55,19 +54,14 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for ALI M5451 PCI Audio."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for ALI M5451 PCI Audio."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable ALI 5451 PCI Audio."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(pcm_channels, int, boot_devs, 0444); MODULE_PARM_DESC(pcm_channels, "PCM Channels"); -MODULE_PARM_SYNTAX(pcm_channels, SNDRV_ENABLED ",default:32,allows:{{1,32}}"); module_param_array(spdif, bool, boot_devs, 0444); MODULE_PARM_DESC(spdif, "Support SPDIF I/O"); -MODULE_PARM_SYNTAX(spdif, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); /* * Debug part definitions @@ -171,7 +165,6 @@ MODULE_PARM_SYNTAX(spdif, SNDRV_ENABLED typedef struct snd_stru_ali ali_t; typedef struct snd_ali_stru_voice snd_ali_voice_t; -#define chip_t ali_t typedef struct snd_ali_channel_control { // register data @@ -247,7 +240,6 @@ struct snd_stru_ali { unsigned int hw_initialized: 1; unsigned int spdif_support: 1; - struct resource *res_port; struct pci_dev *pci; struct pci_dev *pci_m1533; @@ -495,7 +487,7 @@ static void snd_ali_codec_write(ac97_t * unsigned short reg, unsigned short val ) { - ali_t *codec = snd_magic_cast(ali_t, ac97->private_data, return); + ali_t *codec = ac97->private_data; snd_ali_printk("codec_write: reg=%xh data=%xh.\n", reg, val); snd_ali_codec_poke(codec, 0, reg, val); @@ -505,7 +497,7 @@ static void snd_ali_codec_write(ac97_t * static unsigned short snd_ali_codec_read(ac97_t *ac97, unsigned short reg) { - ali_t *codec = snd_magic_cast(ali_t, ac97->private_data, return -ENXIO); + ali_t *codec = ac97->private_data; snd_ali_printk("codec_read reg=%xh.\n", reg); return (snd_ali_codec_peek(codec, 0, reg)); @@ -1051,7 +1043,7 @@ static irqreturn_t snd_ali_card_interrup void *dev_id, struct pt_regs *regs) { - ali_t *codec = snd_magic_cast(ali_t, dev_id, return IRQ_NONE); + ali_t *codec = dev_id; if (codec == NULL) return IRQ_NONE; @@ -1247,7 +1239,7 @@ static int snd_ali_trigger(snd_pcm_subst what = whati = capture_flag = 0; snd_pcm_group_for_each(pos, substream) { s = snd_pcm_group_substream_entry(pos); - if ((ali_t *) _snd_pcm_chip(s->pcm) == codec) { + if ((ali_t *) snd_pcm_substream_chip(s) == codec) { pvoice = (snd_ali_voice_t *) s->runtime->private_data; evoice = pvoice->extra; what |= 1 << (pvoice->number & 0x1f); @@ -1528,16 +1520,15 @@ static snd_pcm_uframes_t snd_ali_playbac snd_pcm_runtime_t *runtime = substream->runtime; snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data; unsigned int cso; - unsigned long flags; - spin_lock_irqsave(&codec->reg_lock, flags); + spin_lock(&codec->reg_lock); if (!pvoice->running) { - spin_unlock_irqrestore(&codec->reg_lock, flags); + spin_unlock(&codec->reg_lock); return 0; } outb(pvoice->number, ALI_REG(codec, ALI_GC_CIR)); cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2)); - spin_unlock_irqrestore(&codec->reg_lock, flags); + spin_unlock(&codec->reg_lock); snd_ali_printk("playback pointer returned cso=%xh.\n", cso); return cso; @@ -1720,7 +1711,7 @@ static snd_pcm_ops_t snd_ali_capture_ops static void snd_ali_pcm_free(snd_pcm_t *pcm) { - ali_t *codec = snd_magic_cast(ali_t, pcm->private_data, return); + ali_t *codec = pcm->private_data; codec->pcm = NULL; } @@ -1769,7 +1760,7 @@ static int snd_ali5451_spdif_info(snd_kc static int snd_ali5451_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { unsigned long flags; - ali_t *codec = snd_magic_cast(ali_t, kcontrol->private_data, -ENXIO); + ali_t *codec = kcontrol->private_data; unsigned int enable; enable = ucontrol->value.integer.value[0] ? 1 : 0; @@ -1796,7 +1787,7 @@ static int snd_ali5451_spdif_get(snd_kco static int snd_ali5451_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { unsigned long flags; - ali_t *codec = snd_magic_cast(ali_t, kcontrol->private_data, -ENXIO); + ali_t *codec = kcontrol->private_data; unsigned int change = 0, enable = 0; enable = ucontrol->value.integer.value[0] ? 1 : 0; @@ -1863,30 +1854,29 @@ static snd_kcontrol_new_t snd_ali5451_mi static void snd_ali_mixer_free_ac97_bus(ac97_bus_t *bus) { - ali_t *codec = snd_magic_cast(ali_t, bus->private_data, return); + ali_t *codec = bus->private_data; codec->ac97_bus = NULL; } static void snd_ali_mixer_free_ac97(ac97_t *ac97) { - ali_t *codec = snd_magic_cast(ali_t, ac97->private_data, return); + ali_t *codec = ac97->private_data; codec->ac97 = NULL; } static int __devinit snd_ali_mixer(ali_t * codec) { - ac97_bus_t bus; - ac97_t ac97; + ac97_template_t ac97; unsigned int idx; int err; + static ac97_bus_ops_t ops = { + .write = snd_ali_codec_write, + .read = snd_ali_codec_read, + }; - memset(&bus, 0, sizeof(bus)); - bus.write = snd_ali_codec_write; - bus.read = snd_ali_codec_read; - bus.private_data = codec; - bus.private_free = snd_ali_mixer_free_ac97_bus; - if ((err = snd_ac97_bus(codec->card, &bus, &codec->ac97_bus)) < 0) + if ((err = snd_ac97_bus(codec->card, 0, &ops, codec, &codec->ac97_bus)) < 0) return err; + codec->ac97_bus->private_free = snd_ali_mixer_free_ac97_bus; memset(&ac97, 0, sizeof(ac97)); ac97.private_data = codec; @@ -1907,7 +1897,7 @@ static int __devinit snd_ali_mixer(ali_t #ifdef CONFIG_PM static int ali_suspend(snd_card_t *card, unsigned int state) { - ali_t *chip = snd_magic_cast(ali_t, card->pm_private_data, return -EINVAL); + ali_t *chip = card->pm_private_data; ali_image_t *im; int i, j; @@ -1943,12 +1933,13 @@ static int ali_suspend(snd_card_t *card, outl(0xffffffff, ALI_REG(chip, ALI_STOP)); spin_unlock_irq(&chip->reg_lock); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); return 0; } static int ali_resume(snd_card_t *card, unsigned int state) { - ali_t *chip = snd_magic_cast(ali_t, card->pm_private_data, return -EINVAL); + ali_t *chip = card->pm_private_data; ali_image_t *im; int i, j; @@ -1980,6 +1971,7 @@ static int ali_resume(snd_card_t *card, spin_unlock_irq(&chip->reg_lock); snd_ac97_resume(chip->ac97); + snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } @@ -1993,15 +1985,13 @@ static int snd_ali_free(ali_t * codec) synchronize_irq(codec->irq); free_irq(codec->irq, (void *)codec); } - if (codec->res_port) { - release_resource(codec->res_port); - kfree_nocheck(codec->res_port); - } + if (codec->port) + pci_release_regions(codec->pci); #ifdef CONFIG_PM if (codec->image) kfree(codec->image); #endif - snd_magic_kfree(codec); + kfree(codec); return 0; } @@ -2055,11 +2045,12 @@ static int snd_ali_chip_init(ali_t *code static int __devinit snd_ali_resources(ali_t *codec) { + int err; + snd_ali_printk("resouces allocation ...\n"); - if ((codec->res_port = request_region(codec->port, 0x100, "ALI 5451")) == NULL) { - snd_printk("Unalbe to request io ports.\n"); - return -EBUSY; - } + if ((err = pci_request_regions(codec->pci, "ALI 5451")) < 0) + return err; + codec->port = pci_resource_start(codec->pci, 0); if (request_irq(codec->pci->irq, snd_ali_card_interrupt, SA_INTERRUPT|SA_SHIRQ, "ALI 5451", (void *)codec)) { snd_printk("Unable to request irq.\n"); @@ -2071,7 +2062,7 @@ static int __devinit snd_ali_resources(a } static int snd_ali_dev_free(snd_device_t *device) { - ali_t *codec=snd_magic_cast(ali_t, device->device_data, return -ENXIO); + ali_t *codec=device->device_data; snd_ali_free(codec); return 0; } @@ -2106,7 +2097,7 @@ static int __devinit snd_ali_create(snd_ return -ENXIO; } - if ((codec = snd_magic_kcalloc(ali_t, 0, GFP_KERNEL)) == NULL) + if ((codec = kcalloc(1, sizeof(*codec), GFP_KERNEL)) == NULL) return -ENOMEM; spin_lock_init(&codec->reg_lock); @@ -2115,7 +2106,6 @@ static int __devinit snd_ali_create(snd_ codec->card = card; codec->pci = pci; codec->irq = -1; - codec->port = pci_resource_start(pci, 0); pci_read_config_byte(pci, PCI_REVISION_ID, &codec->revision); codec->spdif_support = spdif_support; --- linux-2.6.9-rc1/sound/pci/als4000.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/sound/pci/als4000.c 2004-09-02 20:51:53.000000000 -0700 @@ -76,8 +76,7 @@ MODULE_AUTHOR("Bart Hartgers "); MODULE_DESCRIPTION("Avance Logic ALS4000"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Avance Logic,ALS4000}}"); +MODULE_SUPPORTED_DEVICE("{{Avance Logic,ALS4000}}"); #if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) #define SUPPORT_JOYSTICK 1 @@ -93,24 +92,18 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for ALS4000 soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for ALS4000 soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable ALS4000 soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_INDEX_DESC); #ifdef SUPPORT_JOYSTICK module_param_array(joystick_port, int, boot_devs, 0444); MODULE_PARM_DESC(joystick_port, "Joystick port address for ALS4000 soundcard. (0 = disabled)"); -MODULE_PARM_SYNTAX(joystick_port, SNDRV_ENABLED); #endif -#define chip_t sb_t - typedef struct { + struct pci_dev *pci; unsigned long gcr; - struct resource *res_gcr; #ifdef SUPPORT_JOYSTICK struct gameport gameport; struct resource *res_joystick; @@ -304,11 +297,10 @@ static int snd_als4000_playback_prepare( static int snd_als4000_capture_trigger(snd_pcm_substream_t * substream, int cmd) { - unsigned long flags; sb_t *chip = snd_pcm_substream_chip(substream); int result = 0; - spin_lock_irqsave(&chip->mixer_lock, flags); + spin_lock(&chip->mixer_lock); if (cmd == SNDRV_PCM_TRIGGER_START) { chip->mode |= SB_RATE_LOCK_CAPTURE; snd_sbmixer_write(chip, 0xde, capture_cmd(chip)); @@ -318,17 +310,16 @@ static int snd_als4000_capture_trigger(s } else { result = -EINVAL; } - spin_unlock_irqrestore(&chip->mixer_lock, flags); + spin_unlock(&chip->mixer_lock); return result; } static int snd_als4000_playback_trigger(snd_pcm_substream_t * substream, int cmd) { - unsigned long flags; sb_t *chip = snd_pcm_substream_chip(substream); int result = 0; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock(&chip->reg_lock); if (cmd == SNDRV_PCM_TRIGGER_START) { chip->mode |= SB_RATE_LOCK_PLAYBACK; snd_sbdsp_command(chip, playback_cmd(chip).dma_on); @@ -338,38 +329,35 @@ static int snd_als4000_playback_trigger( } else { result = -EINVAL; } - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock(&chip->reg_lock); return result; } static snd_pcm_uframes_t snd_als4000_capture_pointer(snd_pcm_substream_t * substream) { - unsigned long flags; sb_t *chip = snd_pcm_substream_chip(substream); unsigned int result; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock(&chip->reg_lock); result = snd_als4000_gcr_read(chip, 0xa4) & 0xffff; - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock(&chip->reg_lock); return bytes_to_frames( substream->runtime, result ); } static snd_pcm_uframes_t snd_als4000_playback_pointer(snd_pcm_substream_t * substream) { - unsigned long flags; sb_t *chip = snd_pcm_substream_chip(substream); unsigned result; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock(&chip->reg_lock); result = snd_als4000_gcr_read(chip, 0xa0) & 0xffff; - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock(&chip->reg_lock); return bytes_to_frames( substream->runtime, result ); } static irqreturn_t snd_als4000_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - sb_t *chip = snd_magic_cast(sb_t, dev_id, return IRQ_NONE); - unsigned long flags; + sb_t *chip = dev_id; unsigned gcr_status; unsigned sb_status; @@ -385,9 +373,9 @@ static irqreturn_t snd_als4000_interrupt /* release the gcr */ outb(gcr_status, chip->alt_port + 0xe); - spin_lock_irqsave(&chip->mixer_lock, flags); + spin_lock(&chip->mixer_lock); sb_status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS); - spin_unlock_irqrestore(&chip->mixer_lock, flags); + spin_unlock(&chip->mixer_lock); if (sb_status & SB_IRQTYPE_8BIT) snd_sb_ack_8bit(chip); @@ -506,7 +494,7 @@ static snd_pcm_ops_t snd_als4000_capture static void snd_als4000_pcm_free(snd_pcm_t *pcm) { - sb_t *chip = snd_magic_cast(sb_t, pcm->private_data, return); + sb_t *chip = pcm->private_data; chip->pcm = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -557,27 +545,26 @@ static void snd_als4000_set_addr(unsigne static void __devinit snd_als4000_configure(sb_t *chip) { - unsigned long flags; unsigned tmp; int i; /* do some more configuration */ - spin_lock_irqsave(&chip->mixer_lock, flags); + spin_lock_irq(&chip->mixer_lock); tmp = snd_sbmixer_read(chip, 0xc0); snd_sbmixer_write(chip, 0xc0, tmp|0x80); /* always select DMA channel 0, since we do not actually use DMA */ snd_sbmixer_write(chip, SB_DSP4_DMASETUP, SB_DMASETUP_DMA0); snd_sbmixer_write(chip, 0xc0, tmp&0x7f); - spin_unlock_irqrestore(&chip->mixer_lock, flags); + spin_unlock_irq(&chip->mixer_lock); - spin_lock_irqsave(&chip->reg_lock,flags); + spin_lock_irq(&chip->reg_lock); /* magic number. Enables interrupts(?) */ snd_als4000_gcr_write(chip, 0x8c, 0x28000); for(i = 0x91; i <= 0x96; ++i) snd_als4000_gcr_write(chip, i, 0); snd_als4000_gcr_write(chip, 0x99, snd_als4000_gcr_read(chip, 0x99)); - spin_unlock_irqrestore(&chip->reg_lock,flags); + spin_unlock_irq(&chip->reg_lock); } static void snd_card_als4000_free( snd_card_t *card ) @@ -595,8 +582,7 @@ static void snd_card_als4000_free( snd_c kfree_nocheck(acard->res_joystick); } #endif - release_resource(acard->res_gcr); - kfree_nocheck(acard->res_gcr); + pci_release_regions(acard->pci); } static int __devinit snd_card_als4000_probe(struct pci_dev *pci, @@ -606,7 +592,6 @@ static int __devinit snd_card_als4000_pr snd_card_t *card; snd_card_als4000_t *acard; unsigned long gcr; - struct resource *res_gcr_port; sb_t *chip; opl3_t *opl3; unsigned short word; @@ -631,11 +616,9 @@ static int __devinit snd_card_als4000_pr return -ENXIO; } + if ((err = pci_request_regions(pci, "ALS4000")) < 0) + return err; gcr = pci_resource_start(pci, 0); - if ((res_gcr_port = request_region(gcr, 0x40, "ALS4000")) == NULL) { - snd_printk("unable to grab region 0x%lx-0x%lx\n", gcr, gcr + 0x40 - 1); - return -EBUSY; - } pci_read_config_word(pci, PCI_COMMAND, &word); pci_write_config_word(pci, PCI_COMMAND, word | PCI_COMMAND_IO); @@ -644,14 +627,13 @@ static int __devinit snd_card_als4000_pr card = snd_card_new(index[dev], id[dev], THIS_MODULE, sizeof( snd_card_als4000_t ) ); if (card == NULL) { - release_resource(res_gcr_port); - kfree_nocheck(res_gcr_port); + pci_release_regions(pci); return -ENOMEM; } acard = (snd_card_als4000_t *)card->private_data; + acard->pci = pci; acard->gcr = gcr; - acard->res_gcr = res_gcr_port; card->private_free = snd_card_als4000_free; /* disable all legacy ISA stuff except for joystick */ --- linux-2.6.9-rc1/sound/pci/atiixp.c 2004-08-24 00:02:57.000000000 -0700 +++ 25/sound/pci/atiixp.c 2004-09-02 20:51:53.000000000 -0700 @@ -1,5 +1,5 @@ /* - * ALSA driver for ATI IXP 150/200/250 AC97 controllers + * ALSA driver for ATI IXP 150/200/250/300 AC97 controllers * * Copyright (c) 2004 Takashi Iwai * @@ -37,8 +37,7 @@ MODULE_AUTHOR("Takashi Iwai "); MODULE_DESCRIPTION("ATI IXP AC97 controller"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{ATI,IXP150/200/250/300}}"); +MODULE_SUPPORTED_DEVICE("{{ATI,IXP150/200/250/300/400}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ @@ -49,19 +48,14 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for ATI IXP controller."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for ATI IXP controller."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable audio part of ATI IXP controller."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(ac97_clock, int, boot_devs, 0444); MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz)."); -MODULE_PARM_SYNTAX(ac97_clock, SNDRV_ENABLED ",default:48000"); module_param_array(spdif_aclink, bool, boot_devs, 0444); MODULE_PARM_DESC(spdif_aclink, "S/PDIF over AC-link."); -MODULE_PARM_SYNTAX(spdif_aclink, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC); /* @@ -197,7 +191,6 @@ MODULE_PARM_SYNTAX(spdif_aclink, SNDRV_E #define ATI_REG_DMA_STATE (7U<<26) -#define ATI_MEM_REGION 256 /* i/o memory size */ #define ATI_MAX_DESCRIPTORS 256 /* max number of descriptor packets */ @@ -207,7 +200,6 @@ MODULE_PARM_SYNTAX(spdif_aclink, SNDRV_E typedef struct snd_atiixp atiixp_t; typedef struct snd_atiixp_dma atiixp_dma_t; typedef struct snd_atiixp_dma_ops atiixp_dma_ops_t; -#define chip_t atiixp_t /* @@ -248,7 +240,6 @@ struct snd_atiixp_dma_ops { */ struct snd_atiixp_dma { const atiixp_dma_ops_t *ops; - struct snd_dma_device desc_dev; struct snd_dma_buffer desc_buf; snd_pcm_substream_t *substream; /* assigned PCM substream */ unsigned int buf_addr, buf_bytes; /* DMA buffer address, bytes */ @@ -266,7 +257,6 @@ struct snd_atiixp { snd_card_t *card; struct pci_dev *pci; - struct resource *res; /* memory i/o */ unsigned long addr; unsigned long remap_addr; int irq; @@ -287,10 +277,6 @@ struct snd_atiixp { int spdif_over_aclink; /* passed from the module option */ struct semaphore open_mutex; /* playback open mutex */ - -#ifdef CONFIG_PM - u32 pci_state[16]; -#endif }; @@ -299,6 +285,7 @@ struct snd_atiixp { static struct pci_device_id snd_atiixp_ids[] = { { 0x1002, 0x4341, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB200 */ { 0x1002, 0x4361, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB300 */ + { 0x1002, 0x4370, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB400 */ { 0, } }; @@ -376,15 +363,13 @@ static int atiixp_build_dma_packets(atii return -ENOMEM; if (dma->desc_buf.area == NULL) { - memset(&dma->desc_dev, 0, sizeof(dma->desc_dev)); - dma->desc_dev.type = SNDRV_DMA_TYPE_DEV; - dma->desc_dev.dev = snd_dma_pci_data(chip->pci); - if (snd_dma_alloc_pages(&dma->desc_dev, ATI_DESC_LIST_SIZE, &dma->desc_buf) < 0) + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), + ATI_DESC_LIST_SIZE, &dma->desc_buf) < 0) return -ENOMEM; dma->period_bytes = dma->periods = 0; /* clear */ } - if (dma->periods == dma->periods && dma->period_bytes == period_bytes) + if (dma->periods == periods && dma->period_bytes == period_bytes) return 0; /* reset DMA before changing the descriptor table */ @@ -426,7 +411,7 @@ static void atiixp_clear_dma_packets(ati { if (dma->desc_buf.area) { writel(0, chip->remap_addr + dma->ops->llp_offset); - snd_dma_free_pages(&dma->desc_dev, &dma->desc_buf); + snd_dma_free_pages(&dma->desc_buf); dma->desc_buf.area = NULL; } } @@ -491,7 +476,7 @@ static void snd_atiixp_codec_write(atiix static unsigned short snd_atiixp_ac97_read(ac97_t *ac97, unsigned short reg) { - atiixp_t *chip = snd_magic_cast(atiixp_t, ac97->private_data, return 0xffff); + atiixp_t *chip = ac97->private_data; unsigned short data; spin_lock(&chip->ac97_lock); data = snd_atiixp_codec_read(chip, ac97->num, reg); @@ -502,7 +487,7 @@ static unsigned short snd_atiixp_ac97_re static void snd_atiixp_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val) { - atiixp_t *chip = snd_magic_cast(atiixp_t, ac97->private_data, return); + atiixp_t *chip = ac97->private_data; spin_lock(&chip->ac97_lock); snd_atiixp_codec_write(chip, ac97->num, reg, val); spin_unlock(&chip->ac97_lock); @@ -653,9 +638,8 @@ static snd_pcm_uframes_t snd_atiixp_pcm_ snd_pcm_runtime_t *runtime = substream->runtime; atiixp_dma_t *dma = (atiixp_dma_t *)runtime->private_data; unsigned int curptr; - unsigned long flags; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock(&chip->reg_lock); curptr = readl(chip->remap_addr + dma->ops->dt_cur); if (curptr < dma->buf_addr) { snd_printdd("curptr = %x, base = %x\n", curptr, dma->buf_addr); @@ -667,7 +651,7 @@ static snd_pcm_uframes_t snd_atiixp_pcm_ curptr = 0; } } - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock(&chip->reg_lock); return bytes_to_frames(runtime, curptr); } @@ -852,7 +836,7 @@ static int snd_atiixp_spdif_prepare(snd_ { atiixp_t *chip = snd_pcm_substream_chip(substream); - spin_lock(&chip->reg_lock); + spin_lock_irq(&chip->reg_lock); if (chip->spdif_over_aclink) { unsigned int data; /* enable slots 10/11 */ @@ -870,7 +854,7 @@ static int snd_atiixp_spdif_prepare(snd_ atiixp_update(chip, CMD, ATI_REG_CMD_SPDF_CONFIG_MASK, 0); atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_SPDF, 0); } - spin_unlock(&chip->reg_lock); + spin_unlock_irq(&chip->reg_lock); return 0; } @@ -880,7 +864,7 @@ static int snd_atiixp_playback_prepare(s atiixp_t *chip = snd_pcm_substream_chip(substream); unsigned int data; - spin_lock(&chip->reg_lock); + spin_lock_irq(&chip->reg_lock); data = atiixp_read(chip, OUT_DMA_SLOT) & ~ATI_REG_OUT_DMA_SLOT_MASK; switch (substream->runtime->channels) { case 8: @@ -915,7 +899,7 @@ static int snd_atiixp_playback_prepare(s atiixp_update(chip, 6CH_REORDER, ATI_REG_6CH_REORDER_EN, substream->runtime->channels >= 6 ? ATI_REG_6CH_REORDER_EN: 0); - spin_unlock(&chip->reg_lock); + spin_unlock_irq(&chip->reg_lock); return 0; } @@ -924,11 +908,11 @@ static int snd_atiixp_capture_prepare(sn { atiixp_t *chip = snd_pcm_substream_chip(substream); - spin_lock(&chip->reg_lock); + spin_lock_irq(&chip->reg_lock); atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_IN, substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE ? ATI_REG_CMD_INTERLEAVE_IN : 0); - spin_unlock(&chip->reg_lock); + spin_unlock_irq(&chip->reg_lock); return 0; } @@ -996,6 +980,7 @@ static snd_pcm_hardware_t snd_atiixp_pcm { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, .rates = SNDRV_PCM_RATE_48000, @@ -1014,7 +999,6 @@ static int snd_atiixp_pcm_open(snd_pcm_s { atiixp_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - unsigned long flags; int err; snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL); @@ -1036,9 +1020,9 @@ static int snd_atiixp_pcm_open(snd_pcm_s runtime->private_data = dma; /* enable DMA bits */ - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); dma->ops->enable_dma(chip, 1); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); dma->opened = 1; return 0; @@ -1308,7 +1292,7 @@ static int __devinit snd_atiixp_pcm_new( */ static irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - atiixp_t *chip = snd_magic_cast(atiixp_t, dev_id, return IRQ_NONE); + atiixp_t *chip = dev_id; unsigned int status; status = atiixp_read(chip, ISR); @@ -1355,10 +1339,14 @@ static irqreturn_t snd_atiixp_interrupt( static int __devinit snd_atiixp_mixer_new(atiixp_t *chip, int clock) { - ac97_bus_t bus, *pbus; - ac97_t ac97; + ac97_bus_t *pbus; + ac97_template_t ac97; int i, err; int codec_count; + static ac97_bus_ops_t ops = { + .write = snd_atiixp_ac97_write, + .read = snd_atiixp_ac97_read, + }; static unsigned int codec_skip[NUM_ATI_CODECS] = { ATI_REG_ISR_CODEC0_NOT_READY, ATI_REG_ISR_CODEC1_NOT_READY, @@ -1368,13 +1356,10 @@ static int __devinit snd_atiixp_mixer_ne if (snd_atiixp_codec_detect(chip) < 0) return -ENXIO; - memset(&bus, 0, sizeof(bus)); - bus.write = snd_atiixp_ac97_write; - bus.read = snd_atiixp_ac97_read; - bus.private_data = chip; - bus.clock = clock; - if ((err = snd_ac97_bus(chip->card, &bus, &pbus)) < 0) + if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus)) < 0) return err; + pbus->clock = clock; + pbus->shared_type = AC97_SHARED_TYPE_ATIIXP; /* shared with modem driver */ chip->ac97_bus = pbus; codec_count = 0; @@ -1387,17 +1372,9 @@ static int __devinit snd_atiixp_mixer_ne ac97.num = i; ac97.scaps = AC97_SCAP_SKIP_MODEM; if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i])) < 0) { - if (chip->codec_not_ready_bits) - /* codec(s) was detected but not available. - * return the error - */ - return err; - else { - /* codec(s) was NOT detected, so just ignore here */ - chip->ac97[i] = NULL; /* to be sure */ - snd_printd("atiixp: codec %d not found\n", i); - continue; - } + chip->ac97[i] = NULL; /* to be sure */ + snd_printdd("atiixp: codec %d not available for audio\n", i); + continue; } codec_count++; } @@ -1419,7 +1396,7 @@ static int __devinit snd_atiixp_mixer_ne */ static int snd_atiixp_suspend(snd_card_t *card, unsigned int state) { - atiixp_t *chip = snd_magic_cast(atiixp_t, card->pm_private_data, return -EINVAL); + atiixp_t *chip = card->pm_private_data; int i; for (i = 0; i < NUM_ATI_PCMDEVS; i++) @@ -1431,7 +1408,6 @@ static int snd_atiixp_suspend(snd_card_t snd_atiixp_aclink_down(chip); snd_atiixp_chip_stop(chip); - pci_save_state(chip->pci, chip->pci_state); pci_set_power_state(chip->pci, 3); pci_disable_device(chip->pci); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); @@ -1440,11 +1416,10 @@ static int snd_atiixp_suspend(snd_card_t static int snd_atiixp_resume(snd_card_t *card, unsigned int state) { - atiixp_t *chip = snd_magic_cast(atiixp_t, card->pm_private_data, return -EINVAL); + atiixp_t *chip = card->pm_private_data; int i; pci_enable_device(chip->pci); - pci_restore_state(chip->pci, chip->pci_state); pci_set_power_state(chip->pci, 0); snd_atiixp_aclink_reset(chip); @@ -1466,7 +1441,7 @@ static int snd_atiixp_resume(snd_card_t static void snd_atiixp_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) { - atiixp_t *chip = snd_magic_cast(atiixp_t, entry->private_data, return); + atiixp_t *chip = entry->private_data; int i; for (i = 0; i < 256; i += 4) @@ -1494,21 +1469,18 @@ static int snd_atiixp_free(atiixp_t *chi snd_atiixp_chip_stop(chip); synchronize_irq(chip->irq); __hw_end: - if (chip->remap_addr) - iounmap((void *) chip->remap_addr); - if (chip->res) { - release_resource(chip->res); - kfree_nocheck(chip->res); - } if (chip->irq >= 0) free_irq(chip->irq, (void *)chip); - snd_magic_kfree(chip); + if (chip->remap_addr) + iounmap((void *) chip->remap_addr); + pci_release_regions(chip->pci); + kfree(chip); return 0; } static int snd_atiixp_dev_free(snd_device_t *device) { - atiixp_t *chip = snd_magic_cast(atiixp_t, device->device_data, return -ENXIO); + atiixp_t *chip = device->device_data; return snd_atiixp_free(chip); } @@ -1528,7 +1500,7 @@ static int __devinit snd_atiixp_create(s if ((err = pci_enable_device(pci)) < 0) return err; - chip = snd_magic_kcalloc(atiixp_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; @@ -1538,13 +1510,12 @@ static int __devinit snd_atiixp_create(s chip->card = card; chip->pci = pci; chip->irq = -1; - chip->addr = pci_resource_start(pci, 0); - if ((chip->res = request_mem_region(chip->addr, ATI_MEM_REGION, "ATI IXP AC97")) == NULL) { - snd_printk(KERN_ERR "unable to grab I/O memory 0x%lx\n", chip->addr); - snd_atiixp_free(chip); - return -EBUSY; + if ((err = pci_request_regions(pci, "ATI IXP AC97")) < 0) { + kfree(chip); + return err; } - chip->remap_addr = (unsigned long) ioremap_nocache(chip->addr, ATI_MEM_REGION); + chip->addr = pci_resource_start(pci, 0); + chip->remap_addr = (unsigned long) ioremap_nocache(chip->addr, pci_resource_len(pci, 0)); if (chip->remap_addr == 0) { snd_printk(KERN_ERR "AC'97 space ioremap problem\n"); snd_atiixp_free(chip); --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/sound/pci/atiixp_modem.c 2004-09-02 20:51:53.000000000 -0700 @@ -0,0 +1,1351 @@ +/* + * ALSA driver for ATI IXP 150/200/250 AC97 modem controllers + * + * Copyright (c) 2004 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Takashi Iwai "); +MODULE_DESCRIPTION("ATI IXP MC97 controller"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{ATI,IXP150/200/250}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000}; +static int boot_devs; + +module_param_array(index, int, boot_devs, 0444); +MODULE_PARM_DESC(index, "Index value for ATI IXP controller."); +module_param_array(id, charp, boot_devs, 0444); +MODULE_PARM_DESC(id, "ID string for ATI IXP controller."); +module_param_array(enable, bool, boot_devs, 0444); +MODULE_PARM_DESC(enable, "Enable audio part of ATI IXP controller."); +module_param_array(ac97_clock, int, boot_devs, 0444); +MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz)."); + + +/* + */ + +#define ATI_REG_ISR 0x00 /* interrupt source */ +#define ATI_REG_ISR_MODEM_IN_XRUN (1U<<0) +#define ATI_REG_ISR_MODEM_IN_STATUS (1U<<1) +#define ATI_REG_ISR_MODEM_OUT1_XRUN (1U<<2) +#define ATI_REG_ISR_MODEM_OUT1_STATUS (1U<<3) +#define ATI_REG_ISR_MODEM_OUT2_XRUN (1U<<4) +#define ATI_REG_ISR_MODEM_OUT2_STATUS (1U<<5) +#define ATI_REG_ISR_MODEM_OUT3_XRUN (1U<<6) +#define ATI_REG_ISR_MODEM_OUT3_STATUS (1U<<7) +#define ATI_REG_ISR_PHYS_INTR (1U<<8) +#define ATI_REG_ISR_PHYS_MISMATCH (1U<<9) +#define ATI_REG_ISR_CODEC0_NOT_READY (1U<<10) +#define ATI_REG_ISR_CODEC1_NOT_READY (1U<<11) +#define ATI_REG_ISR_CODEC2_NOT_READY (1U<<12) +#define ATI_REG_ISR_NEW_FRAME (1U<<13) +#define ATI_REG_ISR_MODEM_GPIO_DATA (1U<<14) + +#define ATI_REG_IER 0x04 /* interrupt enable */ +#define ATI_REG_IER_MODEM_IN_XRUN_EN (1U<<0) +#define ATI_REG_IER_MODEM_STATUS_EN (1U<<1) +#define ATI_REG_IER_MODEM_OUT1_XRUN_EN (1U<<2) +#define ATI_REG_IER_MODEM_OUT2_XRUN_EN (1U<<4) +#define ATI_REG_IER_MODEM_OUT3_XRUN_EN (1U<<6) +#define ATI_REG_IER_PHYS_INTR_EN (1U<<8) +#define ATI_REG_IER_PHYS_MISMATCH_EN (1U<<9) +#define ATI_REG_IER_CODEC0_INTR_EN (1U<<10) +#define ATI_REG_IER_CODEC1_INTR_EN (1U<<11) +#define ATI_REG_IER_CODEC2_INTR_EN (1U<<12) +#define ATI_REG_IER_NEW_FRAME_EN (1U<<13) /* (RO */ +#define ATI_REG_IER_MODEM_GPIO_DATA_EN (1U<<14) /* (WO) modem is running */ +#define ATI_REG_IER_MODEM_SET_BUS_BUSY (1U<<15) + +#define ATI_REG_CMD 0x08 /* command */ +#define ATI_REG_CMD_POWERDOWN (1U<<0) +#define ATI_REG_CMD_MODEM_RECEIVE_EN (1U<<1) /* modem only */ +#define ATI_REG_CMD_MODEM_SEND1_EN (1U<<2) /* modem only */ +#define ATI_REG_CMD_MODEM_SEND2_EN (1U<<3) /* modem only */ +#define ATI_REG_CMD_MODEM_SEND3_EN (1U<<4) /* modem only */ +#define ATI_REG_CMD_MODEM_STATUS_MEM (1U<<5) /* modem only */ +#define ATI_REG_CMD_MODEM_IN_DMA_EN (1U<<8) /* modem only */ +#define ATI_REG_CMD_MODEM_OUT_DMA1_EN (1U<<9) /* modem only */ +#define ATI_REG_CMD_MODEM_OUT_DMA2_EN (1U<<10) /* modem only */ +#define ATI_REG_CMD_MODEM_OUT_DMA3_EN (1U<<11) /* modem only */ +#define ATI_REG_CMD_AUDIO_PRESENT (1U<<20) +#define ATI_REG_CMD_MODEM_GPIO_THRU_DMA (1U<<22) /* modem only */ +#define ATI_REG_CMD_LOOPBACK_EN (1U<<23) +#define ATI_REG_CMD_PACKED_DIS (1U<<24) +#define ATI_REG_CMD_BURST_EN (1U<<25) +#define ATI_REG_CMD_PANIC_EN (1U<<26) +#define ATI_REG_CMD_MODEM_PRESENT (1U<<27) +#define ATI_REG_CMD_ACLINK_ACTIVE (1U<<28) +#define ATI_REG_CMD_AC_SOFT_RESET (1U<<29) +#define ATI_REG_CMD_AC_SYNC (1U<<30) +#define ATI_REG_CMD_AC_RESET (1U<<31) + +#define ATI_REG_PHYS_OUT_ADDR 0x0c +#define ATI_REG_PHYS_OUT_CODEC_MASK (3U<<0) +#define ATI_REG_PHYS_OUT_RW (1U<<2) +#define ATI_REG_PHYS_OUT_ADDR_EN (1U<<8) +#define ATI_REG_PHYS_OUT_ADDR_SHIFT 9 +#define ATI_REG_PHYS_OUT_DATA_SHIFT 16 + +#define ATI_REG_PHYS_IN_ADDR 0x10 +#define ATI_REG_PHYS_IN_READ_FLAG (1U<<8) +#define ATI_REG_PHYS_IN_ADDR_SHIFT 9 +#define ATI_REG_PHYS_IN_DATA_SHIFT 16 + +#define ATI_REG_SLOTREQ 0x14 + +#define ATI_REG_COUNTER 0x18 +#define ATI_REG_COUNTER_SLOT (3U<<0) /* slot # */ +#define ATI_REG_COUNTER_BITCLOCK (31U<<8) + +#define ATI_REG_IN_FIFO_THRESHOLD 0x1c + +#define ATI_REG_MODEM_IN_DMA_LINKPTR 0x20 +#define ATI_REG_MODEM_IN_DMA_DT_START 0x24 /* RO */ +#define ATI_REG_MODEM_IN_DMA_DT_NEXT 0x28 /* RO */ +#define ATI_REG_MODEM_IN_DMA_DT_CUR 0x2c /* RO */ +#define ATI_REG_MODEM_IN_DMA_DT_SIZE 0x30 +#define ATI_REG_MODEM_OUT_FIFO 0x34 /* output threshold */ +#define ATI_REG_MODEM_OUT1_DMA_THRESHOLD_MASK (0xf<<16) +#define ATI_REG_MODEM_OUT1_DMA_THRESHOLD_SHIFT 16 +#define ATI_REG_MODEM_OUT_DMA1_LINKPTR 0x38 +#define ATI_REG_MODEM_OUT_DMA2_LINKPTR 0x3c +#define ATI_REG_MODEM_OUT_DMA3_LINKPTR 0x40 +#define ATI_REG_MODEM_OUT_DMA1_DT_START 0x44 +#define ATI_REG_MODEM_OUT_DMA1_DT_NEXT 0x48 +#define ATI_REG_MODEM_OUT_DMA1_DT_CUR 0x4c +#define ATI_REG_MODEM_OUT_DMA2_DT_START 0x50 +#define ATI_REG_MODEM_OUT_DMA2_DT_NEXT 0x54 +#define ATI_REG_MODEM_OUT_DMA2_DT_CUR 0x58 +#define ATI_REG_MODEM_OUT_DMA3_DT_START 0x5c +#define ATI_REG_MODEM_OUT_DMA3_DT_NEXT 0x60 +#define ATI_REG_MODEM_OUT_DMA3_DT_CUR 0x64 +#define ATI_REG_MODEM_OUT_DMA12_DT_SIZE 0x68 +#define ATI_REG_MODEM_OUT_DMA3_DT_SIZE 0x6c +#define ATI_REG_MODEM_OUT_FIFO_USED 0x70 +#define ATI_REG_MODEM_OUT_GPIO 0x74 +#define ATI_REG_MODEM_OUT_GPIO_EN 1 +#define ATI_REG_MODEM_OUT_GPIO_DATA_SHIFT 5 +#define ATI_REG_MODEM_IN_GPIO 0x78 + +#define ATI_REG_MODEM_MIRROR 0x7c +#define ATI_REG_AUDIO_MIRROR 0x80 + +#define ATI_REG_MODEM_FIFO_FLUSH 0x88 +#define ATI_REG_MODEM_FIFO_OUT1_FLUSH (1U<<0) +#define ATI_REG_MODEM_FIFO_OUT2_FLUSH (1U<<1) +#define ATI_REG_MODEM_FIFO_OUT3_FLUSH (1U<<2) +#define ATI_REG_MODEM_FIFO_IN_FLUSH (1U<<3) + +/* LINKPTR */ +#define ATI_REG_LINKPTR_EN (1U<<0) + +#define ATI_MAX_DESCRIPTORS 256 /* max number of descriptor packets */ + + +/* + */ + +typedef struct snd_atiixp atiixp_t; +typedef struct snd_atiixp_dma atiixp_dma_t; +typedef struct snd_atiixp_dma_ops atiixp_dma_ops_t; + + +/* + * DMA packate descriptor + */ + +typedef struct atiixp_dma_desc { + u32 addr; /* DMA buffer address */ + u16 status; /* status bits */ + u16 size; /* size of the packet in dwords */ + u32 next; /* address of the next packet descriptor */ +} atiixp_dma_desc_t; + +/* + * stream enum + */ +enum { ATI_DMA_PLAYBACK, ATI_DMA_CAPTURE, NUM_ATI_DMAS }; /* DMAs */ +enum { ATI_PCM_OUT, ATI_PCM_IN, NUM_ATI_PCMS }; /* AC97 pcm slots */ +enum { ATI_PCMDEV_ANALOG, NUM_ATI_PCMDEVS }; /* pcm devices */ + +#define NUM_ATI_CODECS 3 + + +/* + * constants and callbacks for each DMA type + */ +struct snd_atiixp_dma_ops { + int type; /* ATI_DMA_XXX */ + unsigned int llp_offset; /* LINKPTR offset */ + unsigned int dt_cur; /* DT_CUR offset */ + void (*enable_dma)(atiixp_t *chip, int on); /* called from open callback */ + void (*enable_transfer)(atiixp_t *chip, int on); /* called from trigger (START/STOP) */ + void (*flush_dma)(atiixp_t *chip); /* called from trigger (STOP only) */ +}; + +/* + * DMA stream + */ +struct snd_atiixp_dma { + const atiixp_dma_ops_t *ops; + struct snd_dma_buffer desc_buf; + snd_pcm_substream_t *substream; /* assigned PCM substream */ + unsigned int buf_addr, buf_bytes; /* DMA buffer address, bytes */ + unsigned int period_bytes, periods; + int opened; + int running; + int pcm_open_flag; + int ac97_pcm_type; /* index # of ac97_pcm to access, -1 = not used */ +}; + +/* + * ATI IXP chip + */ +struct snd_atiixp { + snd_card_t *card; + struct pci_dev *pci; + + struct resource *res; /* memory i/o */ + unsigned long addr; + unsigned long remap_addr; + int irq; + + ac97_bus_t *ac97_bus; + ac97_t *ac97[NUM_ATI_CODECS]; + + spinlock_t reg_lock; + spinlock_t ac97_lock; + + atiixp_dma_t dmas[NUM_ATI_DMAS]; + struct ac97_pcm *pcms[NUM_ATI_PCMS]; + snd_pcm_t *pcmdevs[NUM_ATI_PCMDEVS]; + + int max_channels; /* max. channels for PCM out */ + + unsigned int codec_not_ready_bits; /* for codec detection */ + + int spdif_over_aclink; /* passed from the module option */ + struct semaphore open_mutex; /* playback open mutex */ +}; + + +/* + */ +static struct pci_device_id snd_atiixp_ids[] = { + { 0x1002, 0x434d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB200 */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, snd_atiixp_ids); + + +/* + * lowlevel functions + */ + +/* + * update the bits of the given register. + * return 1 if the bits changed. + */ +static int snd_atiixp_update_bits(atiixp_t *chip, unsigned int reg, + unsigned int mask, unsigned int value) +{ + unsigned long addr = chip->remap_addr + reg; + unsigned int data, old_data; + old_data = data = readl(addr); + data &= ~mask; + data |= value; + if (old_data == data) + return 0; + writel(data, addr); + return 1; +} + +/* + * macros for easy use + */ +#define atiixp_write(chip,reg,value) \ + writel(value, chip->remap_addr + ATI_REG_##reg) +#define atiixp_read(chip,reg) \ + readl(chip->remap_addr + ATI_REG_##reg) +#define atiixp_update(chip,reg,mask,val) \ + snd_atiixp_update_bits(chip, ATI_REG_##reg, mask, val) + +/* delay for one tick */ +#define do_delay() do { \ + set_current_state(TASK_UNINTERRUPTIBLE); \ + schedule_timeout(1); \ +} while (0) + + +/* + * handling DMA packets + * + * we allocate a linear buffer for the DMA, and split it to each packet. + * in a future version, a scatter-gather buffer should be implemented. + */ + +#define ATI_DESC_LIST_SIZE \ + PAGE_ALIGN(ATI_MAX_DESCRIPTORS * sizeof(atiixp_dma_desc_t)) + +/* + * build packets ring for the given buffer size. + * + * IXP handles the buffer descriptors, which are connected as a linked + * list. although we can change the list dynamically, in this version, + * a static RING of buffer descriptors is used. + * + * the ring is built in this function, and is set up to the hardware. + */ +static int atiixp_build_dma_packets(atiixp_t *chip, atiixp_dma_t *dma, + snd_pcm_substream_t *substream, + unsigned int periods, + unsigned int period_bytes) +{ + unsigned int i; + u32 addr, desc_addr; + unsigned long flags; + + if (periods > ATI_MAX_DESCRIPTORS) + return -ENOMEM; + + if (dma->desc_buf.area == NULL) { + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), + ATI_DESC_LIST_SIZE, &dma->desc_buf) < 0) + return -ENOMEM; + dma->period_bytes = dma->periods = 0; /* clear */ + } + + if (dma->periods == periods && dma->period_bytes == period_bytes) + return 0; + + /* reset DMA before changing the descriptor table */ + spin_lock_irqsave(&chip->reg_lock, flags); + writel(0, chip->remap_addr + dma->ops->llp_offset); + dma->ops->enable_dma(chip, 0); + dma->ops->enable_dma(chip, 1); + spin_unlock_irqrestore(&chip->reg_lock, flags); + + /* fill the entries */ + addr = (u32)substream->runtime->dma_addr; + desc_addr = (u32)dma->desc_buf.addr; + for (i = 0; i < periods; i++) { + atiixp_dma_desc_t *desc = &((atiixp_dma_desc_t *)dma->desc_buf.area)[i]; + desc->addr = cpu_to_le32(addr); + desc->status = 0; + desc->size = period_bytes >> 2; /* in dwords */ + desc_addr += sizeof(atiixp_dma_desc_t); + if (i == periods - 1) + desc->next = cpu_to_le32((u32)dma->desc_buf.addr); + else + desc->next = cpu_to_le32(desc_addr); + addr += period_bytes; + } + + writel((u32)dma->desc_buf.addr | ATI_REG_LINKPTR_EN, + chip->remap_addr + dma->ops->llp_offset); + + dma->period_bytes = period_bytes; + dma->periods = periods; + + return 0; +} + +/* + * remove the ring buffer and release it if assigned + */ +static void atiixp_clear_dma_packets(atiixp_t *chip, atiixp_dma_t *dma, snd_pcm_substream_t *substream) +{ + if (dma->desc_buf.area) { + writel(0, chip->remap_addr + dma->ops->llp_offset); + snd_dma_free_pages(&dma->desc_buf); + dma->desc_buf.area = NULL; + } +} + +/* + * AC97 interface + */ +static int snd_atiixp_acquire_codec(atiixp_t *chip) +{ + int timeout = 1000; + + while (atiixp_read(chip, PHYS_OUT_ADDR) & ATI_REG_PHYS_OUT_ADDR_EN) { + if (! timeout--) { + snd_printk(KERN_WARNING "atiixp: codec acquire timeout\n"); + return -EBUSY; + } + udelay(1); + } + return 0; +} + +static unsigned short snd_atiixp_codec_read(atiixp_t *chip, unsigned short codec, unsigned short reg) +{ + unsigned int data; + int timeout; + + if (snd_atiixp_acquire_codec(chip) < 0) + return 0xffff; + data = (reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) | + ATI_REG_PHYS_OUT_ADDR_EN | + ATI_REG_PHYS_OUT_RW | + codec; + atiixp_write(chip, PHYS_OUT_ADDR, data); + if (snd_atiixp_acquire_codec(chip) < 0) + return 0xffff; + timeout = 1000; + do { + data = atiixp_read(chip, PHYS_IN_ADDR); + if (data & ATI_REG_PHYS_IN_READ_FLAG) + return data >> ATI_REG_PHYS_IN_DATA_SHIFT; + udelay(1); + } while (--timeout); + /* time out may happen during reset */ + if (reg < 0x7c) + snd_printk(KERN_WARNING "atiixp: codec read timeout (reg %x)\n", reg); + return 0xffff; +} + + +static void snd_atiixp_codec_write(atiixp_t *chip, unsigned short codec, unsigned short reg, unsigned short val) +{ + unsigned int data; + + if (snd_atiixp_acquire_codec(chip) < 0) + return; + data = ((unsigned int)val << ATI_REG_PHYS_OUT_DATA_SHIFT) | + ((unsigned int)reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) | + ATI_REG_PHYS_OUT_ADDR_EN | codec; + atiixp_write(chip, PHYS_OUT_ADDR, data); +} + + +static unsigned short snd_atiixp_ac97_read(ac97_t *ac97, unsigned short reg) +{ + atiixp_t *chip = ac97->private_data; + unsigned short data; + spin_lock(&chip->ac97_lock); + data = snd_atiixp_codec_read(chip, ac97->num, reg); + spin_unlock(&chip->ac97_lock); + return data; + +} + +static void snd_atiixp_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val) +{ + atiixp_t *chip = ac97->private_data; + spin_lock(&chip->ac97_lock); + snd_atiixp_codec_write(chip, ac97->num, reg, val); + spin_unlock(&chip->ac97_lock); +} + +/* + * reset AC link + */ +static int snd_atiixp_aclink_reset(atiixp_t *chip) +{ + int timeout; + + /* reset powerdoewn */ + if (atiixp_update(chip, CMD, ATI_REG_CMD_POWERDOWN, 0)) + udelay(10); + + /* perform a software reset */ + atiixp_update(chip, CMD, ATI_REG_CMD_AC_SOFT_RESET, ATI_REG_CMD_AC_SOFT_RESET); + atiixp_read(chip, CMD); + udelay(10); + atiixp_update(chip, CMD, ATI_REG_CMD_AC_SOFT_RESET, 0); + + timeout = 10; + while (! (atiixp_read(chip, CMD) & ATI_REG_CMD_ACLINK_ACTIVE)) { + /* do a hard reset */ + atiixp_update(chip, CMD, ATI_REG_CMD_AC_SYNC|ATI_REG_CMD_AC_RESET, + ATI_REG_CMD_AC_SYNC); + atiixp_read(chip, CMD); + do_delay(); + atiixp_update(chip, CMD, ATI_REG_CMD_AC_RESET, ATI_REG_CMD_AC_RESET); + if (--timeout) { + snd_printk(KERN_ERR "atiixp: codec reset timeout\n"); + break; + } + } + + /* deassert RESET and assert SYNC to make sure */ + atiixp_update(chip, CMD, ATI_REG_CMD_AC_SYNC|ATI_REG_CMD_AC_RESET, + ATI_REG_CMD_AC_SYNC|ATI_REG_CMD_AC_RESET); + + return 0; +} + +#ifdef CONFIG_PM +static int snd_atiixp_aclink_down(atiixp_t *chip) +{ + // if (atiixp_read(chip, MODEM_MIRROR) & 0x1) /* modem running, too? */ + // return -EBUSY; + atiixp_update(chip, CMD, + ATI_REG_CMD_POWERDOWN | ATI_REG_CMD_AC_RESET, + ATI_REG_CMD_POWERDOWN); + return 0; +} +#endif + +/* + * auto-detection of codecs + * + * the IXP chip can generate interrupts for the non-existing codecs. + * NEW_FRAME interrupt is used to make sure that the interrupt is generated + * even if all three codecs are connected. + */ + +#define ALL_CODEC_NOT_READY \ + (ATI_REG_ISR_CODEC0_NOT_READY |\ + ATI_REG_ISR_CODEC1_NOT_READY |\ + ATI_REG_ISR_CODEC2_NOT_READY) +#define CODEC_CHECK_BITS (ALL_CODEC_NOT_READY|ATI_REG_ISR_NEW_FRAME) + +static int snd_atiixp_codec_detect(atiixp_t *chip) +{ + int timeout; + + chip->codec_not_ready_bits = 0; + atiixp_write(chip, IER, CODEC_CHECK_BITS); + /* wait for the interrupts */ + timeout = HZ / 10; + while (timeout-- > 0) { + do_delay(); + if (chip->codec_not_ready_bits) + break; + } + atiixp_write(chip, IER, 0); /* disable irqs */ + + if ((chip->codec_not_ready_bits & ALL_CODEC_NOT_READY) == ALL_CODEC_NOT_READY) { + snd_printk(KERN_ERR "atiixp: no codec detected!\n"); + return -ENXIO; + } + return 0; +} + + +/* + * enable DMA and irqs + */ +static int snd_atiixp_chip_start(atiixp_t *chip) +{ + unsigned int reg; + + /* set up spdif, enable burst mode */ + reg = atiixp_read(chip, CMD); + reg |= ATI_REG_CMD_BURST_EN; + if(!(reg & ATI_REG_CMD_MODEM_PRESENT)) + reg |= ATI_REG_CMD_MODEM_PRESENT; + atiixp_write(chip, CMD, reg); + + /* clear all interrupt source */ + atiixp_write(chip, ISR, 0xffffffff); + /* enable irqs */ + atiixp_write(chip, IER, + ATI_REG_IER_MODEM_STATUS_EN | + ATI_REG_IER_MODEM_IN_XRUN_EN | + ATI_REG_IER_MODEM_OUT1_XRUN_EN); + return 0; +} + + +/* + * disable DMA and IRQs + */ +static int snd_atiixp_chip_stop(atiixp_t *chip) +{ + /* clear interrupt source */ + atiixp_write(chip, ISR, atiixp_read(chip, ISR)); + /* disable irqs */ + atiixp_write(chip, IER, 0); + return 0; +} + + +/* + * PCM section + */ + +/* + * pointer callback simplly reads XXX_DMA_DT_CUR register as the current + * position. when SG-buffer is implemented, the offset must be calculated + * correctly... + */ +static snd_pcm_uframes_t snd_atiixp_pcm_pointer(snd_pcm_substream_t *substream) +{ + atiixp_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + atiixp_dma_t *dma = (atiixp_dma_t *)runtime->private_data; + unsigned int curptr; + + spin_lock(&chip->reg_lock); + curptr = readl(chip->remap_addr + dma->ops->dt_cur); + if (curptr < dma->buf_addr) { + snd_printdd("curptr = %x, base = %x\n", curptr, dma->buf_addr); + curptr = 0; + } else { + curptr -= dma->buf_addr; + if (curptr >= dma->buf_bytes) { + snd_printdd("curptr = %x, size = %x\n", curptr, dma->buf_bytes); + curptr = 0; + } + } + spin_unlock(&chip->reg_lock); + return bytes_to_frames(runtime, curptr); +} + +/* + * XRUN detected, and stop the PCM substream + */ +static void snd_atiixp_xrun_dma(atiixp_t *chip, atiixp_dma_t *dma) +{ + if (! dma->substream || ! dma->running) + return; + snd_printdd("atiixp: XRUN detected (DMA %d)\n", dma->ops->type); + snd_pcm_stop(dma->substream, SNDRV_PCM_STATE_XRUN); +} + +/* + * the period ack. update the substream. + */ +static void snd_atiixp_update_dma(atiixp_t *chip, atiixp_dma_t *dma) +{ + if (! dma->substream || ! dma->running) + return; + snd_pcm_period_elapsed(dma->substream); +} + +/* set BUS_BUSY interrupt bit if any DMA is running */ +/* call with spinlock held */ +static void snd_atiixp_check_bus_busy(atiixp_t *chip) +{ + unsigned int bus_busy; + if (atiixp_read(chip, CMD) & (ATI_REG_CMD_MODEM_SEND1_EN | + ATI_REG_CMD_MODEM_RECEIVE_EN)) + bus_busy = ATI_REG_IER_MODEM_SET_BUS_BUSY; + else + bus_busy = 0; + atiixp_update(chip, IER, ATI_REG_IER_MODEM_SET_BUS_BUSY, bus_busy); +} + +/* common trigger callback + * calling the lowlevel callbacks in it + */ +static int snd_atiixp_pcm_trigger(snd_pcm_substream_t *substream, int cmd) +{ + atiixp_t *chip = snd_pcm_substream_chip(substream); + atiixp_dma_t *dma = (atiixp_dma_t *)substream->runtime->private_data; + unsigned int reg = 0; + int i; + + snd_assert(dma->ops->enable_transfer && dma->ops->flush_dma, return -EINVAL); + + if (cmd != SNDRV_PCM_TRIGGER_START && cmd != SNDRV_PCM_TRIGGER_STOP) + return -EINVAL; + + spin_lock(&chip->reg_lock); + + /* hook off/on: via GPIO_OUT */ + for (i = 0; i < NUM_ATI_CODECS; i++) { + if (chip->ac97[i]) { + reg = snd_ac97_read(chip->ac97[i], AC97_GPIO_STATUS); + break; + } + } + if(cmd == SNDRV_PCM_TRIGGER_START) + reg |= AC97_GPIO_LINE1_OH; + else + reg &= ~AC97_GPIO_LINE1_OH; + reg = (reg << ATI_REG_MODEM_OUT_GPIO_DATA_SHIFT) | ATI_REG_MODEM_OUT_GPIO_EN ; + atiixp_write(chip, MODEM_OUT_GPIO, reg); + + if (cmd == SNDRV_PCM_TRIGGER_START) { + dma->ops->enable_transfer(chip, 1); + dma->running = 1; + } else { + dma->ops->enable_transfer(chip, 0); + dma->running = 0; + } + snd_atiixp_check_bus_busy(chip); + if (cmd == SNDRV_PCM_TRIGGER_STOP) { + dma->ops->flush_dma(chip); + snd_atiixp_check_bus_busy(chip); + } + spin_unlock(&chip->reg_lock); + return 0; +} + + +/* + * lowlevel callbacks for each DMA type + * + * every callback is supposed to be called in chip->reg_lock spinlock + */ + +/* flush FIFO of analog OUT DMA */ +static void atiixp_out_flush_dma(atiixp_t *chip) +{ + atiixp_write(chip, MODEM_FIFO_FLUSH, ATI_REG_MODEM_FIFO_OUT1_FLUSH); +} + +/* enable/disable analog OUT DMA */ +static void atiixp_out_enable_dma(atiixp_t *chip, int on) +{ + unsigned int data; + data = atiixp_read(chip, CMD); + if (on) { + if (data & ATI_REG_CMD_MODEM_OUT_DMA1_EN) + return; + atiixp_out_flush_dma(chip); + data |= ATI_REG_CMD_MODEM_OUT_DMA1_EN; + } else + data &= ~ATI_REG_CMD_MODEM_OUT_DMA1_EN; + atiixp_write(chip, CMD, data); +} + +/* start/stop transfer over OUT DMA */ +static void atiixp_out_enable_transfer(atiixp_t *chip, int on) +{ + atiixp_update(chip, CMD, ATI_REG_CMD_MODEM_SEND1_EN, + on ? ATI_REG_CMD_MODEM_SEND1_EN : 0); +} + +/* enable/disable analog IN DMA */ +static void atiixp_in_enable_dma(atiixp_t *chip, int on) +{ + atiixp_update(chip, CMD, ATI_REG_CMD_MODEM_IN_DMA_EN, + on ? ATI_REG_CMD_MODEM_IN_DMA_EN : 0); +} + +/* start/stop analog IN DMA */ +static void atiixp_in_enable_transfer(atiixp_t *chip, int on) +{ + if (on) { + unsigned int data = atiixp_read(chip, CMD); + if (! (data & ATI_REG_CMD_MODEM_RECEIVE_EN)) { + data |= ATI_REG_CMD_MODEM_RECEIVE_EN; + atiixp_write(chip, CMD, data); + } + } else + atiixp_update(chip, CMD, ATI_REG_CMD_MODEM_RECEIVE_EN, 0); +} + +/* flush FIFO of analog IN DMA */ +static void atiixp_in_flush_dma(atiixp_t *chip) +{ + atiixp_write(chip, MODEM_FIFO_FLUSH, ATI_REG_MODEM_FIFO_IN_FLUSH); +} + +/* set up slots and formats for analog OUT */ +static int snd_atiixp_playback_prepare(snd_pcm_substream_t *substream) +{ + atiixp_t *chip = snd_pcm_substream_chip(substream); + unsigned int data; + + spin_lock_irq(&chip->reg_lock); + /* set output threshold */ + data = atiixp_read(chip, MODEM_OUT_FIFO); + data &= ~ATI_REG_MODEM_OUT1_DMA_THRESHOLD_MASK; + data |= 0x04 << ATI_REG_MODEM_OUT1_DMA_THRESHOLD_SHIFT; + atiixp_write(chip, MODEM_OUT_FIFO, data); + spin_unlock_irq(&chip->reg_lock); + return 0; +} + +/* set up slots and formats for analog IN */ +static int snd_atiixp_capture_prepare(snd_pcm_substream_t *substream) +{ + return 0; +} + +/* + * hw_params - allocate the buffer and set up buffer descriptors + */ +static int snd_atiixp_pcm_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *hw_params) +{ + atiixp_t *chip = snd_pcm_substream_chip(substream); + atiixp_dma_t *dma = (atiixp_dma_t *)substream->runtime->private_data; + int err; + int i; + + err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); + if (err < 0) + return err; + dma->buf_addr = substream->runtime->dma_addr; + dma->buf_bytes = params_buffer_bytes(hw_params); + + err = atiixp_build_dma_packets(chip, dma, substream, + params_periods(hw_params), + params_period_bytes(hw_params)); + if (err < 0) + return err; + + /* set up modem rate */ + for (i = 0; i < NUM_ATI_CODECS; i++) { + if (! chip->ac97[i]) + continue; + snd_ac97_write(chip->ac97[i], AC97_LINE1_RATE, params_rate(hw_params)); + snd_ac97_write(chip->ac97[i], AC97_LINE1_LEVEL, 0); + } + + return err; +} + +static int snd_atiixp_pcm_hw_free(snd_pcm_substream_t * substream) +{ + atiixp_t *chip = snd_pcm_substream_chip(substream); + atiixp_dma_t *dma = (atiixp_dma_t *)substream->runtime->private_data; + + atiixp_clear_dma_packets(chip, dma, substream); + snd_pcm_lib_free_pages(substream); + return 0; +} + + +/* + * pcm hardware definition, identical for all DMA types + */ +static snd_pcm_hardware_t snd_atiixp_pcm_hw = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_KNOT, + .rate_min = 8000, + .rate_max = 16000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 256 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 128 * 1024, + .periods_min = 2, + .periods_max = ATI_MAX_DESCRIPTORS, +}; + +static int snd_atiixp_pcm_open(snd_pcm_substream_t *substream, atiixp_dma_t *dma, int pcm_type) +{ + atiixp_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + static unsigned int rates[] = { 8000, 9600, 12000, 16000 }; + static snd_pcm_hw_constraint_list_t hw_constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, + }; + + snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL); + + if (dma->opened) + return -EBUSY; + dma->substream = substream; + runtime->hw = snd_atiixp_pcm_hw; + dma->ac97_pcm_type = pcm_type; + if ((err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates)) < 0) + return err; + if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + runtime->private_data = dma; + + /* enable DMA bits */ + spin_lock_irq(&chip->reg_lock); + dma->ops->enable_dma(chip, 1); + spin_unlock_irq(&chip->reg_lock); + dma->opened = 1; + + return 0; +} + +static int snd_atiixp_pcm_close(snd_pcm_substream_t *substream, atiixp_dma_t *dma) +{ + atiixp_t *chip = snd_pcm_substream_chip(substream); + /* disable DMA bits */ + snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL); + spin_lock_irq(&chip->reg_lock); + dma->ops->enable_dma(chip, 0); + spin_unlock_irq(&chip->reg_lock); + dma->substream = NULL; + dma->opened = 0; + return 0; +} + +/* + */ +static int snd_atiixp_playback_open(snd_pcm_substream_t *substream) +{ + atiixp_t *chip = snd_pcm_substream_chip(substream); + int err; + + down(&chip->open_mutex); + err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_PLAYBACK], 0); + up(&chip->open_mutex); + if (err < 0) + return err; + return 0; +} + +static int snd_atiixp_playback_close(snd_pcm_substream_t *substream) +{ + atiixp_t *chip = snd_pcm_substream_chip(substream); + int err; + down(&chip->open_mutex); + err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]); + up(&chip->open_mutex); + return err; +} + +static int snd_atiixp_capture_open(snd_pcm_substream_t *substream) +{ + atiixp_t *chip = snd_pcm_substream_chip(substream); + return snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_CAPTURE], 1); +} + +static int snd_atiixp_capture_close(snd_pcm_substream_t *substream) +{ + atiixp_t *chip = snd_pcm_substream_chip(substream); + return snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_CAPTURE]); +} + + +/* AC97 playback */ +static snd_pcm_ops_t snd_atiixp_playback_ops = { + .open = snd_atiixp_playback_open, + .close = snd_atiixp_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_atiixp_pcm_hw_params, + .hw_free = snd_atiixp_pcm_hw_free, + .prepare = snd_atiixp_playback_prepare, + .trigger = snd_atiixp_pcm_trigger, + .pointer = snd_atiixp_pcm_pointer, +}; + +/* AC97 capture */ +static snd_pcm_ops_t snd_atiixp_capture_ops = { + .open = snd_atiixp_capture_open, + .close = snd_atiixp_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_atiixp_pcm_hw_params, + .hw_free = snd_atiixp_pcm_hw_free, + .prepare = snd_atiixp_capture_prepare, + .trigger = snd_atiixp_pcm_trigger, + .pointer = snd_atiixp_pcm_pointer, +}; + +static atiixp_dma_ops_t snd_atiixp_playback_dma_ops = { + .type = ATI_DMA_PLAYBACK, + .llp_offset = ATI_REG_MODEM_OUT_DMA1_LINKPTR, + .dt_cur = ATI_REG_MODEM_OUT_DMA1_DT_CUR, + .enable_dma = atiixp_out_enable_dma, + .enable_transfer = atiixp_out_enable_transfer, + .flush_dma = atiixp_out_flush_dma, +}; + +static atiixp_dma_ops_t snd_atiixp_capture_dma_ops = { + .type = ATI_DMA_CAPTURE, + .llp_offset = ATI_REG_MODEM_IN_DMA_LINKPTR, + .dt_cur = ATI_REG_MODEM_IN_DMA_DT_CUR, + .enable_dma = atiixp_in_enable_dma, + .enable_transfer = atiixp_in_enable_transfer, + .flush_dma = atiixp_in_flush_dma, +}; + +static int __devinit snd_atiixp_pcm_new(atiixp_t *chip) +{ + snd_pcm_t *pcm; + int err; + + /* initialize constants */ + chip->dmas[ATI_DMA_PLAYBACK].ops = &snd_atiixp_playback_dma_ops; + chip->dmas[ATI_DMA_CAPTURE].ops = &snd_atiixp_capture_dma_ops; + + /* PCM #0: analog I/O */ + err = snd_pcm_new(chip->card, "ATI IXP MC97", ATI_PCMDEV_ANALOG, 1, 1, &pcm); + if (err < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_atiixp_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_atiixp_capture_ops); + pcm->private_data = chip; + strcpy(pcm->name, "ATI IXP MC97"); + chip->pcmdevs[ATI_PCMDEV_ANALOG] = pcm; + + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), 64*1024, 128*1024); + + return 0; +} + + + +/* + * interrupt handler + */ +static irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + atiixp_t *chip = dev_id; + unsigned int status; + + status = atiixp_read(chip, ISR); + + if (! status) + return IRQ_NONE; + + /* process audio DMA */ + if (status & ATI_REG_ISR_MODEM_OUT1_XRUN) + snd_atiixp_xrun_dma(chip, &chip->dmas[ATI_DMA_PLAYBACK]); + else if (status & ATI_REG_ISR_MODEM_OUT1_STATUS) + snd_atiixp_update_dma(chip, &chip->dmas[ATI_DMA_PLAYBACK]); + if (status & ATI_REG_ISR_MODEM_IN_XRUN) + snd_atiixp_xrun_dma(chip, &chip->dmas[ATI_DMA_CAPTURE]); + else if (status & ATI_REG_ISR_MODEM_IN_STATUS) + snd_atiixp_update_dma(chip, &chip->dmas[ATI_DMA_CAPTURE]); + + /* for codec detection */ + if (status & CODEC_CHECK_BITS) { + unsigned int detected; + detected = status & CODEC_CHECK_BITS; + spin_lock(&chip->reg_lock); + chip->codec_not_ready_bits |= detected; + atiixp_update(chip, IER, detected, 0); /* disable the detected irqs */ + spin_unlock(&chip->reg_lock); + } + + /* ack */ + atiixp_write(chip, ISR, status); + + return IRQ_HANDLED; +} + + +/* + * ac97 mixer section + */ + +static int __devinit snd_atiixp_mixer_new(atiixp_t *chip, int clock) +{ + ac97_bus_t *pbus; + ac97_template_t ac97; + int i, err; + int codec_count; + static ac97_bus_ops_t ops = { + .write = snd_atiixp_ac97_write, + .read = snd_atiixp_ac97_read, + }; + static unsigned int codec_skip[NUM_ATI_CODECS] = { + ATI_REG_ISR_CODEC0_NOT_READY, + ATI_REG_ISR_CODEC1_NOT_READY, + ATI_REG_ISR_CODEC2_NOT_READY, + }; + + if (snd_atiixp_codec_detect(chip) < 0) + return -ENXIO; + + if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus)) < 0) + return err; + pbus->clock = clock; + pbus->shared_type = AC97_SHARED_TYPE_ATIIXP; /* shared with audio driver */ + chip->ac97_bus = pbus; + + codec_count = 0; + for (i = 0; i < NUM_ATI_CODECS; i++) { + if (chip->codec_not_ready_bits & codec_skip[i]) + continue; + memset(&ac97, 0, sizeof(ac97)); + ac97.private_data = chip; + ac97.pci = chip->pci; + ac97.num = i; + ac97.scaps = AC97_SCAP_SKIP_AUDIO; + if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i])) < 0) { + chip->ac97[i] = NULL; /* to be sure */ + snd_printdd("atiixp: codec %d not available for modem\n", i); + continue; + } + codec_count++; + } + + if (! codec_count) { + snd_printk(KERN_ERR "atiixp: no codec available\n"); + return -ENODEV; + } + + /* snd_ac97_tune_hardware(chip->ac97, ac97_quirks); */ + + return 0; +} + + +#ifdef CONFIG_PM +/* + * power management + */ +static int snd_atiixp_suspend(snd_card_t *card, unsigned int state) +{ + atiixp_t *chip = card->pm_private_data; + int i; + + for (i = 0; i < NUM_ATI_PCMDEVS; i++) + if (chip->pcmdevs[i]) + snd_pcm_suspend_all(chip->pcmdevs[i]); + for (i = 0; i < NUM_ATI_CODECS; i++) + if (chip->ac97[i]) + snd_ac97_suspend(chip->ac97[i]); + snd_atiixp_aclink_down(chip); + snd_atiixp_chip_stop(chip); + + pci_set_power_state(chip->pci, 3); + pci_disable_device(chip->pci); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + return 0; +} + +static int snd_atiixp_resume(snd_card_t *card, unsigned int state) +{ + atiixp_t *chip = card->pm_private_data; + int i; + + pci_enable_device(chip->pci); + pci_set_power_state(chip->pci, 0); + + snd_atiixp_aclink_reset(chip); + snd_atiixp_chip_start(chip); + + for (i = 0; i < NUM_ATI_CODECS; i++) + if (chip->ac97[i]) + snd_ac97_resume(chip->ac97[i]); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; +} +#endif /* CONFIG_PM */ + + +/* + * proc interface for register dump + */ + +static void snd_atiixp_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + atiixp_t *chip = entry->private_data; + int i; + + for (i = 0; i < 256; i += 4) + snd_iprintf(buffer, "%02x: %08x\n", i, readl(chip->remap_addr + i)); +} + +static void __devinit snd_atiixp_proc_init(atiixp_t *chip) +{ + snd_info_entry_t *entry; + + if (! snd_card_proc_new(chip->card, "atiixp", &entry)) + snd_info_set_text_ops(entry, chip, 1024, snd_atiixp_proc_read); +} + + + +/* + * destructor + */ + +static int snd_atiixp_free(atiixp_t *chip) +{ + if (chip->irq < 0) + goto __hw_end; + snd_atiixp_chip_stop(chip); + synchronize_irq(chip->irq); + __hw_end: + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + if (chip->remap_addr) + iounmap((void *) chip->remap_addr); + pci_release_regions(chip->pci); + kfree(chip); + return 0; +} + +static int snd_atiixp_dev_free(snd_device_t *device) +{ + atiixp_t *chip = device->device_data; + return snd_atiixp_free(chip); +} + +/* + * constructor for chip instance + */ +static int __devinit snd_atiixp_create(snd_card_t *card, + struct pci_dev *pci, + atiixp_t **r_chip) +{ + static snd_device_ops_t ops = { + .dev_free = snd_atiixp_dev_free, + }; + atiixp_t *chip; + int err; + + if ((err = pci_enable_device(pci)) < 0) + return err; + + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->ac97_lock); + init_MUTEX(&chip->open_mutex); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + if ((err = pci_request_regions(pci, "ATI IXP MC97")) < 0) { + kfree(chip); + return err; + } + chip->addr = pci_resource_start(pci, 0); + chip->remap_addr = (unsigned long) ioremap_nocache(chip->addr, pci_resource_len(pci, 0)); + if (chip->remap_addr == 0) { + snd_printk(KERN_ERR "AC'97 space ioremap problem\n"); + snd_atiixp_free(chip); + return -EIO; + } + + if (request_irq(pci->irq, snd_atiixp_interrupt, SA_INTERRUPT|SA_SHIRQ, card->shortname, (void *)chip)) { + snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); + snd_atiixp_free(chip); + return -EBUSY; + } + chip->irq = pci->irq; + pci_set_master(pci); + synchronize_irq(chip->irq); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_atiixp_free(chip); + return err; + } + + snd_card_set_dev(card, &pci->dev); + + *r_chip = chip; + return 0; +} + + +static int __devinit snd_atiixp_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + snd_card_t *card; + atiixp_t *chip; + unsigned char revision; + int err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + pci_read_config_byte(pci, PCI_REVISION_ID, &revision); + + strcpy(card->driver, "ATIIXP-MODEM"); + strcpy(card->shortname, "ATI IXP Modem"); + if ((err = snd_atiixp_create(card, pci, &chip)) < 0) + goto __error; + + if ((err = snd_atiixp_aclink_reset(chip)) < 0) + goto __error; + + if ((err = snd_atiixp_mixer_new(chip, ac97_clock[dev])) < 0) + goto __error; + + if ((err = snd_atiixp_pcm_new(chip)) < 0) + goto __error; + + snd_atiixp_proc_init(chip); + + snd_atiixp_chip_start(chip); + + sprintf(card->longname, "%s rev %x at 0x%lx, irq %i", + card->shortname, revision, chip->addr, chip->irq); + + snd_card_set_pm_callback(card, snd_atiixp_suspend, snd_atiixp_resume, chip); + + if ((err = snd_card_register(card)) < 0) + goto __error; + + pci_set_drvdata(pci, card); + dev++; + return 0; + + __error: + snd_card_free(card); + return err; +} + +static void __devexit snd_atiixp_remove(struct pci_dev *pci) +{ + snd_card_free(pci_get_drvdata(pci)); + pci_set_drvdata(pci, NULL); +} + +static struct pci_driver driver = { + .name = "ATI IXP MC97 controller", + .id_table = snd_atiixp_ids, + .probe = snd_atiixp_probe, + .remove = __devexit_p(snd_atiixp_remove), + SND_PCI_PM_CALLBACKS +}; + + +static int __init alsa_card_atiixp_init(void) +{ + return pci_module_init(&driver); +} + +static void __exit alsa_card_atiixp_exit(void) +{ + pci_unregister_driver(&driver); +} + +module_init(alsa_card_atiixp_init) +module_exit(alsa_card_atiixp_exit) --- linux-2.6.9-rc1/sound/pci/au88x0/au88x0_a3d.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/sound/pci/au88x0/au88x0_a3d.c 2004-09-02 20:51:53.000000000 -0700 @@ -567,7 +567,7 @@ static int Vort3DRend_Initialize(vortex_ v->xt_mode = mode; /* this_14 */ vortex_XtalkHw_init(v); - vortex_XtalkHw_SetGains(v, asXtalkGainsAllChan); + vortex_XtalkHw_SetGainsAllChan(v); switch (v->xt_mode) { case XT_SPEAKER0: vortex_XtalkHw_ProgramXtalkNarrow(v); @@ -862,9 +862,8 @@ static int vortex_a3d_register_controls( /* HRTF controls. */ for (i = 0; i < NR_A3D; i++) { if ((kcontrol = - snd_ctl_new1(&vortex_a3d_kcontrol, vortex)) == NULL) + snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL) return -ENOMEM; - kcontrol->private_data = &vortex->a3d[i]; kcontrol->id.numid = CTRLID_HRTF; kcontrol->info = snd_vortex_a3d_hrtf_info; kcontrol->put = snd_vortex_a3d_hrtf_put; @@ -874,9 +873,8 @@ static int vortex_a3d_register_controls( /* ITD controls. */ for (i = 0; i < NR_A3D; i++) { if ((kcontrol = - snd_ctl_new1(&vortex_a3d_kcontrol, vortex)) == NULL) + snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL) return -ENOMEM; - kcontrol->private_data = &vortex->a3d[i]; kcontrol->id.numid = CTRLID_ITD; kcontrol->info = snd_vortex_a3d_itd_info; kcontrol->put = snd_vortex_a3d_itd_put; @@ -886,9 +884,8 @@ static int vortex_a3d_register_controls( /* ILD (gains) controls. */ for (i = 0; i < NR_A3D; i++) { if ((kcontrol = - snd_ctl_new1(&vortex_a3d_kcontrol, vortex)) == NULL) + snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL) return -ENOMEM; - kcontrol->private_data = &vortex->a3d[i]; kcontrol->id.numid = CTRLID_GAINS; kcontrol->info = snd_vortex_a3d_ild_info; kcontrol->put = snd_vortex_a3d_ild_put; @@ -898,9 +895,8 @@ static int vortex_a3d_register_controls( /* Filter controls. */ for (i = 0; i < NR_A3D; i++) { if ((kcontrol = - snd_ctl_new1(&vortex_a3d_kcontrol, vortex)) == NULL) + snd_ctl_new1(&vortex_a3d_kcontrol, &vortex->a3d[i])) == NULL) return -ENOMEM; - kcontrol->private_data = &vortex->a3d[i]; kcontrol->id.numid = CTRLID_FILTER; kcontrol->info = snd_vortex_a3d_filter_info; kcontrol->put = snd_vortex_a3d_filter_put; --- linux-2.6.9-rc1/sound/pci/au88x0/au88x0.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/sound/pci/au88x0/au88x0.c 2004-09-02 20:51:53.000000000 -0700 @@ -31,23 +31,16 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(pcifix, int, boot_devs, 0444); MODULE_PARM_DESC(pcifix, "Enable VIA-workaround for " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(pcifix, - SNDRV_ENABLED - ",allows:{{0,Disabled},{1,Latency},{2,Bridge},{3,Both},{255,Auto}},default:4,dialog:check"); MODULE_DESCRIPTION("Aureal vortex"); -MODULE_CLASSES("{sound}"); MODULE_LICENSE("GPL"); -MODULE_DEVICES("{{Aureal Semiconductor Inc., Aureal Vortex Sound Processor}}"); +MODULE_SUPPORTED_DEVICE("{{Aureal Semiconductor Inc., Aureal Vortex Sound Processor}}"); MODULE_DEVICE_TABLE(pci, snd_vortex_ids); @@ -122,8 +115,7 @@ static void __devinit snd_vortex_workaro // (see "Management of Cards and Components") static int snd_vortex_dev_free(snd_device_t * device) { - vortex_t *vortex = snd_magic_cast(vortex_t, device->device_data, - return -ENXIO); + vortex_t *vortex = device->device_data; vortex_gameport_unregister(vortex); vortex_core_shutdown(vortex); @@ -132,7 +124,7 @@ static int snd_vortex_dev_free(snd_devic free_irq(vortex->irq, vortex); pci_release_regions(vortex->pci_dev); pci_disable_device(vortex->pci_dev); - snd_magic_kfree(vortex); + kfree(vortex); return 0; } @@ -159,7 +151,7 @@ snd_vortex_create(snd_card_t * card, str } pci_set_dma_mask(pci, VORTEX_DMA_MASK); - chip = snd_magic_kcalloc(vortex_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; --- linux-2.6.9-rc1/sound/pci/au88x0/au88x0_core.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/sound/pci/au88x0/au88x0_core.c 2004-09-02 20:51:53.000000000 -0700 @@ -2362,7 +2362,7 @@ static void vortex_disable_int(vortex_t static irqreturn_t vortex_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - vortex_t *vortex = snd_magic_cast(vortex_t, dev_id, return IRQ_NONE); + vortex_t *vortex = dev_id; int i, handled; u32 source; --- linux-2.6.9-rc1/sound/pci/au88x0/au88x0_eq.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/sound/pci/au88x0/au88x0_eq.c 2004-09-02 20:51:53.000000000 -0700 @@ -40,135 +40,120 @@ #include "au88x0_eq.h" #include "au88x0_eqdata.c" +#define VORTEX_EQ_BASE 0x2b000 +#define VORTEX_EQ_DEST (VORTEX_EQ_BASE + 0x410) +#define VORTEX_EQ_SOURCE (VORTEX_EQ_BASE + 0x430) +#define VORTEX_EQ_CTRL (VORTEX_EQ_BASE + 0x440) + /* CEqHw.s */ -static void vortex_EqHw_SetTimeConsts(vortex_t * vortex, u16 a, u16 b) +static void vortex_EqHw_SetTimeConsts(vortex_t * vortex, u16 gain, u16 level) { - hwwrite(vortex->mmio, 0x2b3c4, a); - hwwrite(vortex->mmio, 0x2b3c8, b); + hwwrite(vortex->mmio, 0x2b3c4, gain); + hwwrite(vortex->mmio, 0x2b3c8, level); } -static void vortex_EqHw_SetLeftCoefs(vortex_t * vortex, u16 a[]) +static void vortex_EqHw_SetLeftCoefs(vortex_t * vortex, u16 coefs[]) { eqhw_t *eqhw = &(vortex->eq.this04); - int eax, i = 0, n /*esp2c */ = 0; - - if (eqhw->this04 <= n) - return; + int eax, i = 0, n /*esp2c */; - do { - hwwrite(vortex->mmio, 0x2b000 + n * 0x30, a[i + 0]); - hwwrite(vortex->mmio, 0x2b004 + n * 0x30, a[i + 1]); + for (n = 0; n < eqhw->this04; n++) { + hwwrite(vortex->mmio, 0x2b000 + n * 0x30, coefs[i + 0]); + hwwrite(vortex->mmio, 0x2b004 + n * 0x30, coefs[i + 1]); if (eqhw->this08 == 0) { - hwwrite(vortex->mmio, 0x2b008 + n * 0x30, a[i + 2]); - hwwrite(vortex->mmio, 0x2b00c + n * 0x30, a[i + 3]); - eax = a[i + 4]; //esp24; + hwwrite(vortex->mmio, 0x2b008 + n * 0x30, coefs[i + 2]); + hwwrite(vortex->mmio, 0x2b00c + n * 0x30, coefs[i + 3]); + eax = coefs[i + 4]; //esp24; } else { - if (a[2 + i] == 0x8000) + if (coefs[2 + i] == 0x8000) eax = 0x7fff; else - eax = ~a[2 + i]; + eax = ~coefs[2 + i]; hwwrite(vortex->mmio, 0x2b008 + n * 0x30, eax & 0xffff); - if (a[3 + i] == 0x8000) + if (coefs[3 + i] == 0x8000) eax = 0x7fff; else - eax = ~a[3 + i]; + eax = ~coefs[3 + i]; hwwrite(vortex->mmio, 0x2b00c + n * 0x30, eax & 0xffff); - if (a[4 + i] == 0x8000) + if (coefs[4 + i] == 0x8000) eax = 0x7fff; else - eax = ~a[4 + i]; + eax = ~coefs[4 + i]; } hwwrite(vortex->mmio, 0x2b010 + n * 0x30, eax); - n++; i += 5; } - while (n < eqhw->this04); } -static void vortex_EqHw_SetRightCoefs(vortex_t * vortex, u16 a[]) +static void vortex_EqHw_SetRightCoefs(vortex_t * vortex, u16 coefs[]) { eqhw_t *eqhw = &(vortex->eq.this04); - int i = 0, n /*esp2c */ = 0, eax; + int i = 0, n /*esp2c */, eax; - if (eqhw->this04 <= n) - return; - - do { - hwwrite(vortex->mmio, 0x2b1e0 + n * 0x30, a[0 + i]); - hwwrite(vortex->mmio, 0x2b1e4 + n * 0x30, a[1 + i]); + for (n = 0; n < eqhw->this04; n++) { + hwwrite(vortex->mmio, 0x2b1e0 + n * 0x30, coefs[0 + i]); + hwwrite(vortex->mmio, 0x2b1e4 + n * 0x30, coefs[1 + i]); if (eqhw->this08 == 0) { - hwwrite(vortex->mmio, 0x2b1e8 + n * 0x30, a[2 + i]); - hwwrite(vortex->mmio, 0x2b1ec + n * 0x30, a[3 + i]); - eax = a[4 + i]; //*esp24; + hwwrite(vortex->mmio, 0x2b1e8 + n * 0x30, coefs[2 + i]); + hwwrite(vortex->mmio, 0x2b1ec + n * 0x30, coefs[3 + i]); + eax = coefs[4 + i]; //*esp24; } else { - if (a[2 + i] == 0x8000) + if (coefs[2 + i] == 0x8000) eax = 0x7fff; else - eax = ~(a[2 + i]); + eax = ~(coefs[2 + i]); hwwrite(vortex->mmio, 0x2b1e8 + n * 0x30, eax & 0xffff); - if (a[3 + i] == 0x8000) + if (coefs[3 + i] == 0x8000) eax = 0x7fff; else - eax = ~a[3 + i]; + eax = ~coefs[3 + i]; hwwrite(vortex->mmio, 0x2b1ec + n * 0x30, eax & 0xffff); - if (a[4 + i] == 0x8000) + if (coefs[4 + i] == 0x8000) eax = 0x7fff; else - eax = ~a[4 + i]; + eax = ~coefs[4 + i]; } hwwrite(vortex->mmio, 0x2b1f0 + n * 0x30, eax); i += 5; - n++; } - while (n < eqhw->this04); } static void vortex_EqHw_SetLeftStates(vortex_t * vortex, u16 a[], u16 b[]) { eqhw_t *eqhw = &(vortex->eq.this04); - int i = 0, ebx = 0; + int i = 0, ebx; hwwrite(vortex->mmio, 0x2b3fc, a[0]); hwwrite(vortex->mmio, 0x2b400, a[1]); - if (eqhw->this04 < 0) - return; - - do { + for (ebx = 0; ebx < eqhw->this04; ebx++) { hwwrite(vortex->mmio, 0x2b014 + (i * 0xc), b[i]); hwwrite(vortex->mmio, 0x2b018 + (i * 0xc), b[1 + i]); hwwrite(vortex->mmio, 0x2b01c + (i * 0xc), b[2 + i]); hwwrite(vortex->mmio, 0x2b020 + (i * 0xc), b[3 + i]); i += 4; - ebx++; } - while (eqhw->this04 > ebx); } static void vortex_EqHw_SetRightStates(vortex_t * vortex, u16 a[], u16 b[]) { eqhw_t *eqhw = &(vortex->eq.this04); - int i = 0, ebx = 0; + int i = 0, ebx; hwwrite(vortex->mmio, 0x2b404, a[0]); hwwrite(vortex->mmio, 0x2b408, a[1]); - if (eqhw->this04 < 0) - return; - - do { + for (ebx = 0; ebx < eqhw->this04; ebx++) { hwwrite(vortex->mmio, 0x2b1f4 + (i * 0xc), b[i]); hwwrite(vortex->mmio, 0x2b1f8 + (i * 0xc), b[1 + i]); hwwrite(vortex->mmio, 0x2b1fc + (i * 0xc), b[2 + i]); hwwrite(vortex->mmio, 0x2b200 + (i * 0xc), b[3 + i]); i += 4; - ebx++; } - while (ebx < eqhw->this04); } #if 0 @@ -260,60 +245,41 @@ vortex_EqHw_SetRightGainsSingleTarget(vo static void vortex_EqHw_SetLeftGainsTarget(vortex_t * vortex, u16 a[]) { eqhw_t *eqhw = &(vortex->eq.this04); - int ebx = 0; + int ebx; - if (eqhw->this04 < 0) - return; - do { + for (ebx = 0; ebx < eqhw->this04; ebx++) { hwwrite(vortex->mmio, 0x2b02c + ebx * 0x30, a[ebx]); - ebx++; } - while (ebx < eqhw->this04); } static void vortex_EqHw_SetRightGainsTarget(vortex_t * vortex, u16 a[]) { eqhw_t *eqhw = &(vortex->eq.this04); - int ebx = 0; - - if (eqhw->this04 < 0) - return; + int ebx; - do { + for (ebx = 0; ebx < eqhw->this04; ebx++) { hwwrite(vortex->mmio, 0x2b20c + ebx * 0x30, a[ebx]); - ebx++; } - while (ebx < eqhw->this04); } static void vortex_EqHw_SetLeftGainsCurrent(vortex_t * vortex, u16 a[]) { eqhw_t *eqhw = &(vortex->eq.this04); - int ebx = 0; - - if (eqhw->this04 < 0) - return; + int ebx; - do { + for (ebx = 0; ebx < eqhw->this04; ebx++) { hwwrite(vortex->mmio, 0x2b028 + ebx * 0x30, a[ebx]); - ebx++; } - while (ebx < eqhw->this04); } static void vortex_EqHw_SetRightGainsCurrent(vortex_t * vortex, u16 a[]) { eqhw_t *eqhw = &(vortex->eq.this04); - int ebx = 0; - - if (eqhw->this04 < 0) - return; + int ebx; - do { + for (ebx = 0; ebx < eqhw->this04; ebx++) { hwwrite(vortex->mmio, 0x2b208 + ebx * 0x30, a[ebx]); - ebx++; } - while (ebx < eqhw->this04); } #if 0 @@ -384,26 +350,17 @@ static void vortex_EqHw_SetLevels(vortex eqhw_t *eqhw = &(vortex->eq.this04); int ebx; - if (eqhw->this04 < 0) - return; - - ebx = 0; - do { + for (ebx = 0; ebx < eqhw->this04; ebx++) { hwwrite(vortex->mmio, 0x2b024 + ebx * 0x30, a[ebx]); - ebx++; } - while (ebx < eqhw->this04); hwwrite(vortex->mmio, 0x2b3cc, a[eqhw->this04]); hwwrite(vortex->mmio, 0x2b3d8, a[eqhw->this04 + 1]); - ebx = 0; - do { + for (ebx = 0; ebx < eqhw->this04; ebx++) { hwwrite(vortex->mmio, 0x2b204 + ebx * 0x30, a[ebx + (eqhw->this04 + 2)]); - ebx++; } - while (ebx < eqhw->this04); hwwrite(vortex->mmio, 0x2b3e4, a[2 + (eqhw->this04 * 2)]); hwwrite(vortex->mmio, 0x2b3f0, a[3 + (eqhw->this04 * 2)]); @@ -466,12 +423,12 @@ static void vortex_EqHw_GetSampleRate(vo #endif static void vortex_EqHw_Enable(vortex_t * vortex) { - hwwrite(vortex->mmio, 0x2b440, 0xf001); + hwwrite(vortex->mmio, VORTEX_EQ_CTRL, 0xf001); } static void vortex_EqHw_Disable(vortex_t * vortex) { - hwwrite(vortex->mmio, 0x2b440, 0xf000); + hwwrite(vortex->mmio, VORTEX_EQ_CTRL, 0xf000); } /* Reset (zero) buffers */ @@ -479,16 +436,16 @@ static void vortex_EqHw_ZeroIO(vortex_t { int i; for (i = 0; i < 0x8; i++) - hwwrite(vortex->mmio, 0x2b410 + (i << 2), 0x0); + hwwrite(vortex->mmio, VORTEX_EQ_DEST + (i << 2), 0x0); for (i = 0; i < 0x4; i++) - hwwrite(vortex->mmio, 0x2b430 + (i << 2), 0x0); + hwwrite(vortex->mmio, VORTEX_EQ_SOURCE + (i << 2), 0x0); } static void vortex_EqHw_ZeroA3DIO(vortex_t * vortex) { int i; for (i = 0; i < 0x4; i++) - hwwrite(vortex->mmio, 0x2b410 + (i << 2), 0x0); + hwwrite(vortex->mmio, VORTEX_EQ_DEST + (i << 2), 0x0); } static void vortex_EqHw_ZeroState(vortex_t * vortex) @@ -659,11 +616,10 @@ vortex_Eqlzr_SetAllBands(vortex_t * vort if (((eq->this10) * 2 != count) || (eq->this28 == 0)) return 1; - if (0 < count) { - for (i = 0; i < count; i++) { - eq->this130[i] = gains[i]; - } + for (i = 0; i < count; i++) { + eq->this130[i] = gains[i]; } + if (eq->this54) return 0; return vortex_Eqlzr_SetAllBandsFromActiveCoeffSet(vortex); --- linux-2.6.9-rc1/sound/pci/au88x0/au88x0_game.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/sound/pci/au88x0/au88x0_game.c 2004-09-02 20:51:53.000000000 -0700 @@ -96,7 +96,7 @@ static int vortex_game_open(struct gamep static int vortex_gameport_register(vortex_t * vortex) { - if ((vortex->gameport = snd_kcalloc(sizeof(struct gameport), GFP_KERNEL)) == NULL) { + if ((vortex->gameport = kcalloc(1, sizeof(struct gameport), GFP_KERNEL)) == NULL) { return -1; }; --- linux-2.6.9-rc1/sound/pci/au88x0/au88x0.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/sound/pci/au88x0/au88x0.h 2004-09-02 20:51:53.000000000 -0700 @@ -180,8 +180,6 @@ struct snd_vortex { u8 rev; }; -#define chip_t vortex_t - /* Functions. */ /* SRC */ --- linux-2.6.9-rc1/sound/pci/au88x0/au88x0_mixer.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/sound/pci/au88x0/au88x0_mixer.c 2004-09-02 20:51:53.000000000 -0700 @@ -13,14 +13,15 @@ static int __devinit snd_vortex_mixer(vortex_t * vortex) { - ac97_bus_t bus, *pbus; - ac97_t ac97; + ac97_bus_t *pbus; + ac97_template_t ac97; int err; + static ac97_bus_ops_t ops = { + .write = vortex_codec_write, + .read = vortex_codec_read, + }; - memset(&bus, 0, sizeof(bus)); - bus.write = vortex_codec_write; - bus.read = vortex_codec_read; - if ((err = snd_ac97_bus(vortex->card, &bus, &pbus)) < 0) + if ((err = snd_ac97_bus(vortex->card, 0, &ops, NULL, &pbus)) < 0) return err; memset(&ac97, 0, sizeof(ac97)); // Intialize AC97 codec stuff. --- linux-2.6.9-rc1/sound/pci/au88x0/au88x0_mpu401.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/sound/pci/au88x0/au88x0_mpu401.c 2004-09-02 20:51:53.000000000 -0700 @@ -104,7 +104,7 @@ static int __devinit snd_vortex_midi(vor ~CTRL_MIDI_PORT) & ~CTRL_MIDI_EN); return temp; } - mpu = snd_magic_cast(mpu401_t, rmidi->private_data, return -ENOMEM); + mpu = rmidi->private_data; mpu->cport = (unsigned long)(vortex->mmio + (VORTEX_MIDI_CMD >> 2)); #endif vortex->rmidi = rmidi; --- linux-2.6.9-rc1/sound/pci/au88x0/au88x0_pcm.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/sound/pci/au88x0/au88x0_pcm.c 2004-09-02 20:51:53.000000000 -0700 @@ -28,7 +28,6 @@ #include #include "au88x0.h" -#define chip_t vortex_t #define VORTEX_PCM_TYPE(x) (x->name[40]) /* hardware definition */ @@ -189,7 +188,7 @@ static int snd_vortex_pcm_hw_params(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * hw_params) { - chip_t *chip = snd_pcm_substream_chip(substream); + vortex_t *chip = snd_pcm_substream_chip(substream); stream_t *stream = (stream_t *) (substream->runtime->private_data); snd_pcm_sgbuf_t *sgbuf; int err; @@ -250,7 +249,7 @@ snd_vortex_pcm_hw_params(snd_pcm_substre /* hw_free callback */ static int snd_vortex_pcm_hw_free(snd_pcm_substream_t * substream) { - chip_t *chip = snd_pcm_substream_chip(substream); + vortex_t *chip = snd_pcm_substream_chip(substream); stream_t *stream = (stream_t *) (substream->runtime->private_data); // Delete audio routes. @@ -305,7 +304,7 @@ static int snd_vortex_pcm_prepare(snd_pc /* trigger callback */ static int snd_vortex_pcm_trigger(snd_pcm_substream_t * substream, int cmd) { - chip_t *chip = snd_pcm_substream_chip(substream); + vortex_t *chip = snd_pcm_substream_chip(substream); stream_t *stream = (stream_t *) substream->runtime->private_data; int dma = stream->dma; --- linux-2.6.9-rc1/sound/pci/au88x0/au88x0_xtalk.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/sound/pci/au88x0/au88x0_xtalk.c 2004-09-02 20:51:53.000000000 -0700 @@ -540,6 +540,12 @@ vortex_XtalkHw_SetGains(vortex_t * vorte } } +static void +vortex_XtalkHw_SetGainsAllChan(vortex_t * vortex) +{ + vortex_XtalkHw_SetGains(vortex, asXtalkGainsAllChan); +} + #if 0 static void vortex_XtalkHw_GetGains(vortex_t * vortex, xtalk_gains_t gains) { --- linux-2.6.9-rc1/sound/pci/au88x0/au88x0_xtalk.h 2004-08-24 00:02:26.000000000 -0700 +++ 25/sound/pci/au88x0/au88x0_xtalk.h 2004-09-02 20:51:53.000000000 -0700 @@ -45,10 +45,9 @@ typedef short xtalk_instate_t[XTINST_SZ] typedef short xtalk_coefs_t[5][5]; typedef short xtalk_state_t[5][4]; -extern xtalk_gains_t const asXtalkGainsAllChan; - static void vortex_XtalkHw_SetGains(vortex_t * vortex, xtalk_gains_t const gains); +static void vortex_XtalkHw_SetGainsAllChan(vortex_t * vortex); static void vortex_XtalkHw_SetSampleRate(vortex_t * vortex, int sr); static void vortex_XtalkHw_ProgramPipe(vortex_t * vortex); static void vortex_XtalkHw_ProgramPipe(vortex_t * vortex); --- linux-2.6.9-rc1/sound/pci/azt3328.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/sound/pci/azt3328.c 2004-09-02 20:51:53.000000000 -0700 @@ -111,8 +111,7 @@ MODULE_AUTHOR("Andreas Mohr "); MODULE_DESCRIPTION("Aztech AZF3328 (PCI168)"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Aztech,AZF3328}}"); +MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}"); #if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) #define SUPPORT_JOYSTICK 1 @@ -170,35 +169,25 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for AZF3328 soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for AZF3328 soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable AZF3328 soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_INDEX_DESC); #ifdef SUPPORT_JOYSTICK module_param_array(joystick, bool, boot_devs, 0444); MODULE_PARM_DESC(joystick, "Enable joystick for AZF3328 soundcard."); -MODULE_PARM_SYNTAX(joystick, SNDRV_BOOLEAN_FALSE_DESC); #endif typedef struct _snd_azf3328 azf3328_t; -#define chip_t azf3328_t struct _snd_azf3328 { int irq; unsigned long codec_port; - struct resource *res_codec_port; unsigned long io2_port; - struct resource *res_io2_port; unsigned long mpu_port; - struct resource *res_mpu_port; unsigned long synth_port; - struct resource *res_synth_port; unsigned long mixer_port; - struct resource *res_mixer_port; #ifdef SUPPORT_JOYSTICK struct gameport gameport; @@ -553,8 +542,6 @@ static int snd_azf3328_put_mixer_enum(sn return (nreg != oreg); } -#define NUM_CONTROLS(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t)) - static snd_kcontrol_new_t snd_azf3328_mixer_controls[] __devinitdata = { AZF3328_MIXER_SWITCH("Master Playback Switch", IDX_MIXER_PLAY_MASTER, 15, 1), AZF3328_MIXER_VOL_STEREO("Master Playback Volume", IDX_MIXER_PLAY_MASTER, 0x1f, 1), @@ -650,7 +637,7 @@ static int __devinit snd_azf3328_mixer_n /* add mixer controls */ sw = snd_azf3328_mixer_controls; - for (idx = 0; idx < NUM_CONTROLS(snd_azf3328_mixer_controls); idx++, sw++) { + for (idx = 0; idx < ARRAY_SIZE(snd_azf3328_mixer_controls); idx++, sw++) { if ((err = snd_ctl_add(chip->card, snd_ctl_new1(sw, chip))) < 0) return err; } @@ -813,7 +800,6 @@ static int snd_azf3328_capture_prepare(s static int snd_azf3328_playback_trigger(snd_pcm_substream_t * substream, int cmd) { - unsigned long flags; azf3328_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; int result = 0; @@ -830,7 +816,7 @@ static int snd_azf3328_playback_trigger( snd_azf3328_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT, runtime->rate, snd_pcm_format_width(runtime->format), runtime->channels); - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock(&chip->reg_lock); /* stop playback */ status1 = inw(chip->codec_port+IDX_IO_PLAY_FLAGS); status1 &= ~DMA_RESUME; @@ -838,11 +824,11 @@ static int snd_azf3328_playback_trigger( /* FIXME: clear interrupts or what??? */ outw(0xffff, chip->codec_port+IDX_IO_PLAY_IRQMASK); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock(&chip->reg_lock); snd_azf3328_setdmaa(chip, runtime->dma_addr, snd_pcm_lib_period_bytes(substream), snd_pcm_lib_buffer_bytes(substream), 0); - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock(&chip->reg_lock); #ifdef WIN9X /* FIXME: enable playback/recording??? */ status1 |= DMA_PLAY_SOMETHING1 | DMA_PLAY_SOMETHING2; @@ -858,7 +844,7 @@ static int snd_azf3328_playback_trigger( outw(DMA_PLAY_SOMETHING1|DMA_PLAY_SOMETHING2, chip->codec_port+IDX_IO_PLAY_FLAGS); outw(DMA_RESUME|SOMETHING_ALMOST_ALWAYS_SET|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE, chip->codec_port+IDX_IO_PLAY_FLAGS); #endif - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock(&chip->reg_lock); /* now unmute WaveOut */ snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0); @@ -870,7 +856,7 @@ static int snd_azf3328_playback_trigger( /* mute WaveOut */ snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1); - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock(&chip->reg_lock); /* stop playback */ status1 = inw(chip->codec_port+IDX_IO_PLAY_FLAGS); @@ -882,7 +868,7 @@ static int snd_azf3328_playback_trigger( status1 &= ~DMA_PLAY_SOMETHING1; outw(status1, chip->codec_port+IDX_IO_PLAY_FLAGS); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock(&chip->reg_lock); /* now unmute WaveOut */ snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0); @@ -906,7 +892,6 @@ static int snd_azf3328_playback_trigger( * should actually be triggered like that */ static int snd_azf3328_capture_trigger(snd_pcm_substream_t * substream, int cmd) { - unsigned long flags; azf3328_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; int result = 0; @@ -920,7 +905,7 @@ static int snd_azf3328_capture_trigger(s snd_azf3328_setfmt(chip, IDX_IO_REC_SOUNDFORMAT, runtime->rate, snd_pcm_format_width(runtime->format), runtime->channels); - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock(&chip->reg_lock); /* stop recording */ status1 = inw(chip->codec_port+IDX_IO_REC_FLAGS); status1 &= ~DMA_RESUME; @@ -928,11 +913,11 @@ static int snd_azf3328_capture_trigger(s /* FIXME: clear interrupts or what??? */ outw(0xffff, chip->codec_port+IDX_IO_REC_IRQMASK); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock(&chip->reg_lock); snd_azf3328_setdmaa(chip, runtime->dma_addr, snd_pcm_lib_period_bytes(substream), snd_pcm_lib_buffer_bytes(substream), 1); - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock(&chip->reg_lock); #ifdef WIN9X /* FIXME: enable playback/recording??? */ status1 |= DMA_PLAY_SOMETHING1 | DMA_PLAY_SOMETHING2; @@ -948,13 +933,13 @@ static int snd_azf3328_capture_trigger(s outw(DMA_PLAY_SOMETHING1|DMA_PLAY_SOMETHING2, chip->codec_port+IDX_IO_REC_FLAGS); outw(DMA_RESUME|SOMETHING_ALMOST_ALWAYS_SET|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE, chip->codec_port+IDX_IO_REC_FLAGS); #endif - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock(&chip->reg_lock); snd_azf3328_dbgio(chip, "trigger2"); chip->is_playing = 1; break; case SNDRV_PCM_TRIGGER_STOP: - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock(&chip->reg_lock); /* stop recording */ status1 = inw(chip->codec_port+IDX_IO_REC_FLAGS); @@ -966,7 +951,7 @@ static int snd_azf3328_capture_trigger(s status1 &= ~DMA_PLAY_SOMETHING1; outw(status1, chip->codec_port+IDX_IO_REC_FLAGS); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock(&chip->reg_lock); chip->is_playing = 0; break; @@ -990,16 +975,13 @@ static snd_pcm_uframes_t snd_azf3328_pla unsigned long bufptr, playptr; unsigned long result; snd_pcm_uframes_t frmres; - unsigned long flags; - spin_lock_irqsave(&chip->reg_lock, flags); #ifdef QUERY_HARDWARE bufptr = inl(chip->codec_port+IDX_IO_PLAY_DMA_START_1); #else bufptr = substream->runtime->dma_addr; #endif playptr = inl(chip->codec_port+IDX_IO_PLAY_DMA_CURRPOS); - spin_unlock_irqrestore(&chip->reg_lock, flags); result = playptr - bufptr; frmres = bytes_to_frames( substream->runtime, result ); @@ -1013,16 +995,13 @@ static snd_pcm_uframes_t snd_azf3328_cap unsigned long bufptr, recptr; unsigned long result; snd_pcm_uframes_t frmres; - unsigned long flags; - spin_lock_irqsave(&chip->reg_lock, flags); #ifdef QUERY_HARDWARE bufptr = inl(chip->codec_port+IDX_IO_REC_DMA_START_1); #else bufptr = substream->runtime->dma_addr; #endif recptr = inl(chip->codec_port+IDX_IO_REC_DMA_CURRPOS); - spin_unlock_irqrestore(&chip->reg_lock, flags); result = recptr - bufptr; frmres = bytes_to_frames( substream->runtime, result ); @@ -1032,7 +1011,7 @@ static snd_pcm_uframes_t snd_azf3328_cap static irqreturn_t snd_azf3328_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - azf3328_t *chip = snd_magic_cast(azf3328_t, dev_id, return IRQ_NONE); + azf3328_t *chip = dev_id; unsigned int status, which; static unsigned long count; @@ -1232,7 +1211,7 @@ static snd_pcm_ops_t snd_azf3328_capture static void snd_azf3328_pcm_free(snd_pcm_t *pcm) { - azf3328_t *chip = snd_magic_cast(azf3328_t, pcm->private_data, return); + azf3328_t *chip = pcm->private_data; chip->pcm = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -1287,36 +1266,17 @@ static int snd_azf3328_free(azf3328_t *c kfree_nocheck(chip->res_joystick); } #endif - if (chip->res_codec_port) { - release_resource(chip->res_codec_port); - kfree_nocheck(chip->res_codec_port); - } - if (chip->res_io2_port) { - release_resource(chip->res_io2_port); - kfree_nocheck(chip->res_io2_port); - } - if (chip->res_mpu_port) { - release_resource(chip->res_mpu_port); - kfree_nocheck(chip->res_mpu_port); - } - if (chip->res_synth_port) { - release_resource(chip->res_synth_port); - kfree_nocheck(chip->res_synth_port); - } - if (chip->res_mixer_port) { - release_resource(chip->res_mixer_port); - kfree_nocheck(chip->res_mixer_port); - } if (chip->irq >= 0) free_irq(chip->irq, (void *)chip); + pci_release_regions(chip->pci); - snd_magic_kfree(chip); + kfree(chip); return 0; } static int snd_azf3328_dev_free(snd_device_t *device) { - azf3328_t *chip = snd_magic_cast(azf3328_t, device->device_data, return -ENXIO); + azf3328_t *chip = device->device_data; return snd_azf3328_free(chip); } @@ -1345,7 +1305,6 @@ static int __devinit snd_azf3328_create( unsigned long device_type, azf3328_t ** rchip) { - unsigned long flags; azf3328_t *chip; int err; static snd_device_ops_t ops = { @@ -1358,7 +1317,7 @@ static int __devinit snd_azf3328_create( if ((err = pci_enable_device(pci)) < 0) return err; - chip = snd_magic_kcalloc(azf3328_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; spin_lock_init(&chip->reg_lock); @@ -1373,36 +1332,16 @@ static int __devinit snd_azf3328_create( return -ENXIO; } - chip->codec_port = pci_resource_start(pci, 0); - if ((chip->res_codec_port = request_region(chip->codec_port, 0x80, "Aztech AZF3328 I/O")) == NULL) { - snd_printk("unable to grab I/O port at 0x%lx-0x%lx\n", chip->codec_port, chip->codec_port + 0x80 - 1); - snd_azf3328_free(chip); - return -EBUSY; + if ((err = pci_request_regions(pci, "Aztech AZF3328")) < 0) { + kfree(chip); + return err; } + + chip->codec_port = pci_resource_start(pci, 0); chip->io2_port = pci_resource_start(pci, 1); - if ((chip->res_io2_port = request_region(chip->io2_port, 0x08, "Aztech AZF3328 I/O 2")) == NULL) { - snd_printk("unable to grab I/O 2 port at 0x%lx-0x%lx\n", chip->io2_port, chip->io2_port + 0x08 - 1); - snd_azf3328_free(chip); - return -EBUSY; - } chip->mpu_port = pci_resource_start(pci, 2); - if ((chip->res_mpu_port = request_region(chip->mpu_port, 0x04, "Aztech AZF3328 MPU401")) == NULL) { - snd_printk("unable to grab MPU401 port at 0x%lx-0x%lx\n", chip->mpu_port, chip->mpu_port + 0x04 - 1); - snd_azf3328_free(chip); - return -EBUSY; - } chip->synth_port = pci_resource_start(pci, 3); - if ((chip->res_synth_port = request_region(chip->synth_port, 0x08, "Aztech AZF3328 OPL3")) == NULL) { - snd_printk("unable to grab OPL3 port at 0x%lx-0x%lx\n", chip->synth_port, chip->synth_port + 0x08 - 1); - snd_azf3328_free(chip); - return -EBUSY; - } chip->mixer_port = pci_resource_start(pci, 4); - if ((chip->res_mixer_port = request_region(chip->mixer_port, 0x40, "Aztech AZF3328 Mixer")) == NULL) { - snd_printk("unable to grab mixer port at 0x%lx-0x%lx\n", chip->mixer_port, chip->mixer_port + 0x40 - 1); - snd_azf3328_free(chip); - return -EBUSY; - } if (request_irq(pci->irq, snd_azf3328_interrupt, SA_INTERRUPT|SA_SHIRQ, card->shortname, (void *)chip)) { snd_printk("unable to grab IRQ %d\n", pci->irq); @@ -1435,13 +1374,13 @@ static int __devinit snd_azf3328_create( #endif /* standard chip init stuff */ - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); outb(DMA_PLAY_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE, chip->codec_port + IDX_IO_PLAY_FLAGS); outb(DMA_PLAY_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE, chip->codec_port + IDX_IO_SOMETHING_FLAGS); outb(DMA_PLAY_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE, chip->codec_port + IDX_IO_REC_FLAGS); outb(0x0, chip->codec_port + IDX_IO_IRQ63H); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); snd_card_set_dev(card, &pci->dev); --- linux-2.6.9-rc1/sound/pci/bt87x.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/sound/pci/bt87x.c 2004-09-02 20:51:53.000000000 -0700 @@ -38,8 +38,7 @@ MODULE_AUTHOR("Clemens Ladisch "); MODULE_DESCRIPTION("Brooktree Bt87x audio driver"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Brooktree,Bt878}," +MODULE_SUPPORTED_DEVICE("{{Brooktree,Bt878}," "{Brooktree,Bt879}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ @@ -50,16 +49,12 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for Bt87x soundcard"); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for Bt87x soundcard"); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable Bt87x soundcard"); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(digital_rate, int, boot_devs, 0444); MODULE_PARM_DESC(digital_rate, "Digital input rate for Bt87x soundcard"); -MODULE_PARM_SYNTAX(digital_rate, SNDRV_ENABLED); #ifndef PCI_VENDOR_ID_BROOKTREE @@ -152,14 +147,12 @@ MODULE_PARM_SYNTAX(digital_rate, SNDRV_E /* SYNC, one WRITE per line, one extra WRITE per page boundary, SYNC, JUMP */ #define MAX_RISC_SIZE ((1 + 255 + (PAGE_ALIGN(255 * 4092) / PAGE_SIZE - 1) + 1 + 1) * 8) -#define chip_t bt87x_t typedef struct snd_bt87x bt87x_t; struct snd_bt87x { snd_card_t *card; struct pci_dev *pci; void *mmio; - struct resource *res_mmio; int irq; int dig_rate; @@ -168,7 +161,6 @@ struct snd_bt87x { long opened; snd_pcm_substream_t *substream; - struct snd_dma_device dma_dev; struct snd_dma_buffer dma_risc; unsigned int line_bytes; unsigned int lines; @@ -197,10 +189,8 @@ static int snd_bt87x_create_risc(bt87x_t u32 *risc; if (chip->dma_risc.area == NULL) { - memset(&chip->dma_dev, 0, sizeof(chip->dma_dev)); - chip->dma_dev.type = SNDRV_DMA_TYPE_DEV; - chip->dma_dev.dev = snd_dma_pci_data(chip->pci); - if (snd_dma_alloc_pages(&chip->dma_dev, PAGE_ALIGN(MAX_RISC_SIZE), &chip->dma_risc) < 0) + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), + PAGE_ALIGN(MAX_RISC_SIZE), &chip->dma_risc) < 0) return -ENOMEM; } risc = (u32 *)chip->dma_risc.area; @@ -244,14 +234,14 @@ static int snd_bt87x_create_risc(bt87x_t static void snd_bt87x_free_risc(bt87x_t *chip) { if (chip->dma_risc.area) { - snd_dma_free_pages(&chip->dma_dev, &chip->dma_risc); + snd_dma_free_pages(&chip->dma_risc); chip->dma_risc.area = NULL; } } static irqreturn_t snd_bt87x_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - bt87x_t *chip = snd_magic_cast(bt87x_t, dev_id, return IRQ_NONE); + bt87x_t *chip = dev_id; unsigned int status; status = snd_bt87x_readl(chip, REG_INT_STAT); @@ -314,7 +304,7 @@ static snd_pcm_hardware_t snd_bt87x_anal SNDRV_PCM_INFO_MMAP_VALID, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8, .rates = SNDRV_PCM_RATE_KNOT, - .rate_min = 119467, + .rate_min = 119466, .rate_max = 448000, .channels_min = 1, .channels_max = 1, @@ -356,20 +346,21 @@ static int snd_bt87x_set_digital_hw(bt87 static int snd_bt87x_set_analog_hw(bt87x_t *chip, snd_pcm_runtime_t *runtime) { - static unsigned int rates[] = { - 119467, 128000, 137846, 149333, 162909, 179200, - 199111, 224000, 256000, 298667, 358400, 448000 + static ratnum_t analog_clock = { + .num = 1792000, + .den_min = 4, + .den_max = 15, + .den_step = 1 }; - static snd_pcm_hw_constraint_list_t constraint_rates = { - .count = ARRAY_SIZE(rates), - .list = rates, - .mask = 0, + static snd_pcm_hw_constraint_ratnums_t constraint_rates = { + .nrats = 1, + .rats = &analog_clock }; chip->reg_control &= ~CTL_DA_IOM_DA; runtime->hw = snd_bt87x_analog_hw; - return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - &constraint_rates); + return snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &constraint_rates); } static int snd_bt87x_pcm_open(snd_pcm_substream_t *substream) @@ -439,25 +430,22 @@ static int snd_bt87x_prepare(snd_pcm_sub { bt87x_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - unsigned long flags; int decimation; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); chip->reg_control &= ~(CTL_DA_SDR_MASK | CTL_DA_SBR); - decimation = (1792000 + 5) / runtime->rate; + decimation = (1792000 + runtime->rate / 4) / runtime->rate; chip->reg_control |= decimation << CTL_DA_SDR_SHIFT; if (runtime->format == SNDRV_PCM_FORMAT_S8) chip->reg_control |= CTL_DA_SBR; snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return 0; } static int snd_bt87x_start(bt87x_t *chip) { - unsigned long flags; - - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock(&chip->reg_lock); chip->current_line = 0; chip->reg_control |= CTL_FIFO_ENABLE | CTL_RISC_ENABLE | CTL_ACAP_EN; snd_bt87x_writel(chip, REG_RISC_STRT_ADD, chip->dma_risc.addr); @@ -465,20 +453,18 @@ static int snd_bt87x_start(bt87x_t *chip chip->line_bytes | (chip->lines << 16)); snd_bt87x_writel(chip, REG_INT_MASK, MY_INTERRUPTS); snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock(&chip->reg_lock); return 0; } static int snd_bt87x_stop(bt87x_t *chip) { - unsigned long flags; - - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock(&chip->reg_lock); chip->reg_control &= ~(CTL_FIFO_ENABLE | CTL_RISC_ENABLE | CTL_ACAP_EN); snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control); snd_bt87x_writel(chip, REG_INT_MASK, 0); snd_bt87x_writel(chip, REG_INT_STAT, MY_INTERRUPTS); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock(&chip->reg_lock); return 0; } @@ -536,17 +522,16 @@ static int snd_bt87x_capture_volume_get( static int snd_bt87x_capture_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value) { bt87x_t *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; u32 old_control; int changed; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); old_control = chip->reg_control; chip->reg_control = (chip->reg_control & ~CTL_A_GAIN_MASK) | (value->value.integer.value[0] << CTL_A_GAIN_SHIFT); snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control); changed = old_control != chip->reg_control; - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return changed; } @@ -578,17 +563,16 @@ static int snd_bt87x_capture_boost_get(s static int snd_bt87x_capture_boost_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value) { bt87x_t *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; u32 old_control; int changed; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); old_control = chip->reg_control; chip->reg_control = (chip->reg_control & ~CTL_A_G2X) | (value->value.integer.value[0] ? CTL_A_G2X : 0); snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control); changed = chip->reg_control != old_control; - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return changed; } @@ -624,17 +608,16 @@ static int snd_bt87x_capture_source_get( static int snd_bt87x_capture_source_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value) { bt87x_t *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; u32 old_control; int changed; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); old_control = chip->reg_control; chip->reg_control = (chip->reg_control & ~CTL_A_SEL_MASK) | (value->value.enumerated.item[0] << CTL_A_SEL_SHIFT); snd_bt87x_writel(chip, REG_GPIO_DMA_CTL, chip->reg_control); changed = chip->reg_control != old_control; - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return changed; } @@ -655,19 +638,16 @@ static int snd_bt87x_free(bt87x_t *chip) iounmap(chip->mmio); } - if (chip->res_mmio) { - release_resource(chip->res_mmio); - kfree_nocheck(chip->res_mmio); - } if (chip->irq >= 0) free_irq(chip->irq, chip); - snd_magic_kfree(chip); + pci_release_regions(chip->pci); + kfree(chip); return 0; } static int snd_bt87x_dev_free(snd_device_t *device) { - bt87x_t *chip = snd_magic_cast(bt87x_t, device->device_data, return -ENXIO); + bt87x_t *chip = device->device_data; return snd_bt87x_free(chip); } @@ -705,7 +685,7 @@ static int __devinit snd_bt87x_create(sn if (err < 0) return err; - chip = snd_magic_kcalloc(bt87x_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; chip->card = card; @@ -713,13 +693,9 @@ static int __devinit snd_bt87x_create(sn chip->irq = -1; spin_lock_init(&chip->reg_lock); - chip->res_mmio = request_mem_region(pci_resource_start(pci, 0), - pci_resource_len(pci, 0), - "Bt87x audio"); - if (!chip->res_mmio) { - snd_bt87x_free(chip); - snd_printk(KERN_ERR "cannot allocate io memory\n"); - return -EBUSY; + if ((err = pci_request_regions(pci, "Bt87x audio")) < 0) { + kfree(chip); + return err; } chip->mmio = ioremap_nocache(pci_resource_start(pci, 0), pci_resource_len(pci, 0)); --- linux-2.6.9-rc1/sound/pci/cmipci.c 2004-08-24 00:03:13.000000000 -0700 +++ 25/sound/pci/cmipci.c 2004-09-02 20:51:53.000000000 -0700 @@ -43,8 +43,7 @@ MODULE_AUTHOR("Takashi Iwai "); MODULE_DESCRIPTION("C-Media CMI8x38 PCI"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{C-Media,CMI8738}," +MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8738}," "{C-Media,CMI8738B}," "{C-Media,CMI8338A}," "{C-Media,CMI8338B}}"); @@ -66,25 +65,19 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for C-Media PCI soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for C-Media PCI soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable C-Media PCI soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(mpu_port, long, boot_devs, 0444); MODULE_PARM_DESC(mpu_port, "MPU-401 port."); -MODULE_PARM_SYNTAX(mpu_port, SNDRV_ENABLED ",allows:{{0},{0x330},{0x320},{0x310},{0x300}},dialog:list"); module_param_array(fm_port, long, boot_devs, 0444); MODULE_PARM_DESC(fm_port, "FM port."); -MODULE_PARM_SYNTAX(fm_port, SNDRV_ENABLED ",allows:{{0},{0x388},{0x3c8},{0x3e0},{0x3e8}},dialog:list"); module_param_array(soft_ac3, bool, boot_devs, 0444); MODULE_PARM_DESC(soft_ac3, "Sofware-conversion of raw SPDIF packets (model 033 only)."); #ifdef SUPPORT_JOYSTICK module_param_array(joystick_port, int, boot_devs, 0444); MODULE_PARM_DESC(joystick_port, "Joystick port address."); -MODULE_PARM_SYNTAX(joystick_port, SNDRV_ENABLED ",allows:{{0},{1},{0x200},{0x201}},dialog:list"); #endif #ifndef PCI_DEVICE_ID_CMEDIA_CM8738 @@ -405,8 +398,6 @@ MODULE_PARM_SYNTAX(joystick_port, SNDRV_ typedef struct snd_stru_cmipci cmipci_t; typedef struct snd_stru_cmipci_pcm cmipci_pcm_t; -#define chip_t cmipci_t - struct snd_stru_cmipci_pcm { snd_pcm_substream_t *substream; int running; /* dac/adc running? */ @@ -442,7 +433,6 @@ struct snd_stru_cmipci { int irq; unsigned long iobase; - struct resource *res_iobase; unsigned int ctrl; /* FUNCTRL0 current value */ snd_pcm_t *pcm; /* DAC/ADC PCM */ @@ -699,15 +689,13 @@ static snd_pcm_hw_constraint_list_t hw_c static int set_dac_channels(cmipci_t *cm, cmipci_pcm_t *rec, int channels) { - unsigned long flags; - if (channels > 2) { if (! cm->can_multi_ch) return -EINVAL; if (rec->fmt != 0x03) /* stereo 16bit only */ return -EINVAL; - spin_lock_irqsave(&cm->reg_lock, flags); + spin_lock_irq(&cm->reg_lock); snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG); snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC); if (channels > 4) { @@ -724,18 +712,18 @@ static int set_dac_channels(cmipci_t *cm snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C); snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER); } - spin_unlock_irqrestore(&cm->reg_lock, flags); + spin_unlock_irq(&cm->reg_lock); } else { if (cm->can_multi_ch) { - spin_lock_irqsave(&cm->reg_lock, flags); + spin_lock_irq(&cm->reg_lock); snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG); snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D); snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C); snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C); snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER); snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC); - spin_unlock_irqrestore(&cm->reg_lock, flags); + spin_unlock_irq(&cm->reg_lock); } } return 0; @@ -749,7 +737,6 @@ static int set_dac_channels(cmipci_t *cm static int snd_cmipci_pcm_prepare(cmipci_t *cm, cmipci_pcm_t *rec, snd_pcm_substream_t *substream) { - unsigned long flags; unsigned int reg, freq, val; snd_pcm_runtime_t *runtime = substream->runtime; @@ -777,7 +764,7 @@ static int snd_cmipci_pcm_prepare(cmipci rec->period_size = (rec->period_size * runtime->channels) / 2; } - spin_lock_irqsave(&cm->reg_lock, flags); + spin_lock_irq(&cm->reg_lock); /* set buffer address */ reg = rec->ch ? CM_REG_CH1_FRAME1 : CM_REG_CH0_FRAME1; @@ -822,7 +809,7 @@ static int snd_cmipci_pcm_prepare(cmipci //snd_printd("cmipci: chformat = %08x\n", val); rec->running = 0; - spin_unlock_irqrestore(&cm->reg_lock, flags); + spin_unlock_irq(&cm->reg_lock); return 0; } @@ -954,13 +941,12 @@ static int snd_cmipci_spdif_default_get( snd_ctl_elem_value_t *ucontrol) { cmipci_t *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; int i; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); for (i = 0; i < 4; i++) ucontrol->value.iec958.status[i] = (chip->dig_status >> (i * 8)) & 0xff; - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return 0; } @@ -968,17 +954,16 @@ static int snd_cmipci_spdif_default_put( snd_ctl_elem_value_t * ucontrol) { cmipci_t *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; int i, change; unsigned int val; val = 0; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); for (i = 0; i < 4; i++) val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); change = val != chip->dig_status; chip->dig_status = val; - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return change; } @@ -1030,13 +1015,12 @@ static int snd_cmipci_spdif_stream_get(s snd_ctl_elem_value_t *ucontrol) { cmipci_t *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; int i; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); for (i = 0; i < 4; i++) ucontrol->value.iec958.status[i] = (chip->dig_pcm_status >> (i * 8)) & 0xff; - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return 0; } @@ -1044,17 +1028,16 @@ static int snd_cmipci_spdif_stream_put(s snd_ctl_elem_value_t *ucontrol) { cmipci_t *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; int i, change; unsigned int val; val = 0; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); for (i = 0; i < 4; i++) val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); change = val != chip->dig_pcm_status; chip->dig_pcm_status = val; - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return change; } @@ -1072,30 +1055,36 @@ static snd_kcontrol_new_t snd_cmipci_spd */ /* save mixer setting and mute for AC3 playback */ -static void save_mixer_state(cmipci_t *cm) +static int save_mixer_state(cmipci_t *cm) { if (! cm->mixer_insensitive) { + snd_ctl_elem_value_t *val; unsigned int i; + + val = kmalloc(sizeof(*val), GFP_ATOMIC); + if (!val) + return -ENOMEM; for (i = 0; i < CM_SAVED_MIXERS; i++) { snd_kcontrol_t *ctl = cm->mixer_res_ctl[i]; if (ctl) { - snd_ctl_elem_value_t val; int event; - memset(&val, 0, sizeof(val)); - ctl->get(ctl, &val); - cm->mixer_res_status[i] = val.value.integer.value[0]; - val.value.integer.value[0] = cm_saved_mixer[i].toggle_on; + memset(val, 0, sizeof(*val)); + ctl->get(ctl, val); + cm->mixer_res_status[i] = val->value.integer.value[0]; + val->value.integer.value[0] = cm_saved_mixer[i].toggle_on; event = SNDRV_CTL_EVENT_MASK_INFO; - if (cm->mixer_res_status[i] != val.value.integer.value[0]) { - ctl->put(ctl, &val); /* toggle */ + if (cm->mixer_res_status[i] != val->value.integer.value[0]) { + ctl->put(ctl, val); /* toggle */ event |= SNDRV_CTL_EVENT_MASK_VALUE; } ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; snd_ctl_notify(cm->card, event, &ctl->id); } } + kfree(val); cm->mixer_insensitive = 1; } + return 0; } @@ -1103,27 +1092,32 @@ static void save_mixer_state(cmipci_t *c static void restore_mixer_state(cmipci_t *cm) { if (cm->mixer_insensitive) { + snd_ctl_elem_value_t *val; unsigned int i; + + val = kmalloc(sizeof(*val), GFP_KERNEL); + if (!val) + return; cm->mixer_insensitive = 0; /* at first clear this; otherwise the changes will be ignored */ for (i = 0; i < CM_SAVED_MIXERS; i++) { snd_kcontrol_t *ctl = cm->mixer_res_ctl[i]; if (ctl) { - snd_ctl_elem_value_t val; int event; - memset(&val, 0, sizeof(val)); + memset(val, 0, sizeof(*val)); ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; - ctl->get(ctl, &val); + ctl->get(ctl, val); event = SNDRV_CTL_EVENT_MASK_INFO; - if (val.value.integer.value[0] != cm->mixer_res_status[i]) { - val.value.integer.value[0] = cm->mixer_res_status[i]; - ctl->put(ctl, &val); + if (val->value.integer.value[0] != cm->mixer_res_status[i]) { + val->value.integer.value[0] = cm->mixer_res_status[i]; + ctl->put(ctl, val); event |= SNDRV_CTL_EVENT_MASK_VALUE; } snd_ctl_notify(cm->card, event, &ctl->id); } } + kfree(val); } } @@ -1175,17 +1169,17 @@ static void setup_ac3(cmipci_t *cm, snd_ } } -static void setup_spdif_playback(cmipci_t *cm, snd_pcm_substream_t *subs, int up, int do_ac3) +static int setup_spdif_playback(cmipci_t *cm, snd_pcm_substream_t *subs, int up, int do_ac3) { - int rate; - unsigned long flags; + int rate, err; rate = subs->runtime->rate; if (up && do_ac3) - save_mixer_state(cm); + if ((err = save_mixer_state(cm)) < 0) + return err; - spin_lock_irqsave(&cm->reg_lock, flags); + spin_lock_irq(&cm->reg_lock); cm->spdif_playback_avail = up; if (up) { /* they are controlled via "IEC958 Output Switch" */ @@ -1207,7 +1201,8 @@ static void setup_spdif_playback(cmipci_ snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF); setup_ac3(cm, subs, 0, 0); } - spin_unlock_irqrestore(&cm->reg_lock, flags); + spin_unlock_irq(&cm->reg_lock); + return 0; } @@ -1220,13 +1215,15 @@ static int snd_cmipci_playback_prepare(s { cmipci_t *cm = snd_pcm_substream_chip(substream); int rate = substream->runtime->rate; - int do_spdif, do_ac3 = 0; + int err, do_spdif, do_ac3 = 0; + do_spdif = ((rate == 44100 || rate == 48000) && substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE && substream->runtime->channels == 2); if (do_spdif && cm->can_ac3_hw) do_ac3 = cm->dig_pcm_status & IEC958_AES0_NONAUDIO; - setup_spdif_playback(cm, substream, do_spdif, do_ac3); + if ((err = setup_spdif_playback(cm, substream, do_spdif, do_ac3)) < 0) + return err; return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream); } @@ -1234,12 +1231,14 @@ static int snd_cmipci_playback_prepare(s static int snd_cmipci_playback_spdif_prepare(snd_pcm_substream_t *substream) { cmipci_t *cm = snd_pcm_substream_chip(substream); - int do_ac3; + int err, do_ac3; + if (cm->can_ac3_hw) do_ac3 = cm->dig_pcm_status & IEC958_AES0_NONAUDIO; else do_ac3 = 1; /* doesn't matter */ - setup_spdif_playback(cm, substream, 1, do_ac3); + if ((err = setup_spdif_playback(cm, substream, 1, do_ac3)) < 0) + return err; return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream); } @@ -1262,11 +1261,10 @@ static int snd_cmipci_capture_prepare(sn static int snd_cmipci_capture_spdif_prepare(snd_pcm_substream_t *substream) { cmipci_t *cm = snd_pcm_substream_chip(substream); - unsigned long flags; - spin_lock_irqsave(&cm->reg_lock, flags); + spin_lock_irq(&cm->reg_lock); snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF); - spin_unlock_irqrestore(&cm->reg_lock, flags); + spin_unlock_irq(&cm->reg_lock); return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream); } @@ -1274,11 +1272,10 @@ static int snd_cmipci_capture_spdif_prep static int snd_cmipci_capture_spdif_hw_free(snd_pcm_substream_t *subs) { cmipci_t *cm = snd_pcm_substream_chip(subs); - unsigned long flags; - spin_lock_irqsave(&cm->reg_lock, flags); + spin_lock_irq(&cm->reg_lock); snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF); - spin_unlock_irqrestore(&cm->reg_lock, flags); + spin_unlock_irq(&cm->reg_lock); return snd_cmipci_hw_free(subs); } @@ -1289,7 +1286,7 @@ static int snd_cmipci_capture_spdif_hw_f */ static irqreturn_t snd_cmipci_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - cmipci_t *cm = snd_magic_cast(cmipci_t, dev_id, return IRQ_NONE); + cmipci_t *cm = dev_id; unsigned int status, mask = 0; /* fastpath out, to ease interrupt sharing */ @@ -1448,7 +1445,6 @@ static snd_pcm_hardware_t snd_cmipci_cap */ static int open_device_check(cmipci_t *cm, int mode, snd_pcm_substream_t *subs) { - unsigned long flags; int ch = mode & CM_OPEN_CH_MASK; /* FIXME: a file should wait until the device becomes free @@ -1466,9 +1462,9 @@ static int open_device_check(cmipci_t *c if (! (mode & CM_OPEN_DAC)) { /* disable dual DAC mode */ cm->channel[ch].is_dac = 0; - spin_lock_irqsave(&cm->reg_lock, flags); + spin_lock_irq(&cm->reg_lock); snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC); - spin_unlock_irqrestore(&cm->reg_lock, flags); + spin_unlock_irq(&cm->reg_lock); } up(&cm->open_mutex); return 0; @@ -1476,7 +1472,6 @@ static int open_device_check(cmipci_t *c static void close_device_check(cmipci_t *cm, int mode) { - unsigned long flags; int ch = mode & CM_OPEN_CH_MASK; down(&cm->open_mutex); @@ -1490,9 +1485,9 @@ static void close_device_check(cmipci_t if (! cm->channel[ch].is_dac) { /* enable dual DAC mode again */ cm->channel[ch].is_dac = 1; - spin_lock_irqsave(&cm->reg_lock, flags); + spin_lock_irq(&cm->reg_lock); snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC); - spin_unlock_irqrestore(&cm->reg_lock, flags); + spin_unlock_irq(&cm->reg_lock); } } up(&cm->open_mutex); @@ -1840,12 +1835,11 @@ static int snd_cmipci_info_volume(snd_kc static int snd_cmipci_get_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { cmipci_t *cm = snd_kcontrol_chip(kcontrol); - unsigned long flags; cmipci_sb_reg_t reg; int val; cmipci_sb_reg_decode(®, kcontrol->private_value); - spin_lock_irqsave(&cm->reg_lock, flags); + spin_lock_irq(&cm->reg_lock); val = (snd_cmipci_mixer_read(cm, reg.left_reg) >> reg.left_shift) & reg.mask; if (reg.invert) val = reg.mask - val; @@ -1856,14 +1850,13 @@ static int snd_cmipci_get_volume(snd_kco val = reg.mask - val; ucontrol->value.integer.value[1] = val; } - spin_unlock_irqrestore(&cm->reg_lock, flags); + spin_unlock_irq(&cm->reg_lock); return 0; } static int snd_cmipci_put_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { cmipci_t *cm = snd_kcontrol_chip(kcontrol); - unsigned long flags; cmipci_sb_reg_t reg; int change; int left, right, oleft, oright; @@ -1880,7 +1873,7 @@ static int snd_cmipci_put_volume(snd_kco right <<= reg.right_shift; } else right = 0; - spin_lock_irqsave(&cm->reg_lock, flags); + spin_lock_irq(&cm->reg_lock); oleft = snd_cmipci_mixer_read(cm, reg.left_reg); left |= oleft & ~(reg.mask << reg.left_shift); change = left != oleft; @@ -1895,7 +1888,7 @@ static int snd_cmipci_put_volume(snd_kco snd_cmipci_mixer_write(cm, reg.right_reg, right); } else snd_cmipci_mixer_write(cm, reg.left_reg, left); - spin_unlock_irqrestore(&cm->reg_lock, flags); + spin_unlock_irq(&cm->reg_lock); return change; } @@ -1921,15 +1914,14 @@ static int snd_cmipci_info_input_sw(snd_ static int snd_cmipci_get_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { cmipci_t *cm = snd_kcontrol_chip(kcontrol); - unsigned long flags; cmipci_sb_reg_t reg; int val1, val2; cmipci_sb_reg_decode(®, kcontrol->private_value); - spin_lock_irqsave(&cm->reg_lock, flags); + spin_lock_irq(&cm->reg_lock); val1 = snd_cmipci_mixer_read(cm, reg.left_reg); val2 = snd_cmipci_mixer_read(cm, reg.right_reg); - spin_unlock_irqrestore(&cm->reg_lock, flags); + spin_unlock_irq(&cm->reg_lock); ucontrol->value.integer.value[0] = (val1 >> reg.left_shift) & 1; ucontrol->value.integer.value[1] = (val2 >> reg.left_shift) & 1; ucontrol->value.integer.value[2] = (val1 >> reg.right_shift) & 1; @@ -1940,13 +1932,12 @@ static int snd_cmipci_get_input_sw(snd_k static int snd_cmipci_put_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { cmipci_t *cm = snd_kcontrol_chip(kcontrol); - unsigned long flags; cmipci_sb_reg_t reg; int change; int val1, val2, oval1, oval2; cmipci_sb_reg_decode(®, kcontrol->private_value); - spin_lock_irqsave(&cm->reg_lock, flags); + spin_lock_irq(&cm->reg_lock); oval1 = snd_cmipci_mixer_read(cm, reg.left_reg); oval2 = snd_cmipci_mixer_read(cm, reg.right_reg); val1 = oval1 & ~((1 << reg.left_shift) | (1 << reg.right_shift)); @@ -1958,7 +1949,7 @@ static int snd_cmipci_put_input_sw(snd_k change = val1 != oval1 || val2 != oval2; snd_cmipci_mixer_write(cm, reg.left_reg, val1); snd_cmipci_mixer_write(cm, reg.right_reg, val2); - spin_unlock_irqrestore(&cm->reg_lock, flags); + spin_unlock_irq(&cm->reg_lock); return change; } @@ -2011,11 +2002,10 @@ static int snd_cmipci_get_native_mixer(s { cmipci_t *cm = snd_kcontrol_chip(kcontrol); cmipci_sb_reg_t reg; - unsigned long flags; unsigned char oreg, val; cmipci_sb_reg_decode(®, kcontrol->private_value); - spin_lock_irqsave(&cm->reg_lock, flags); + spin_lock_irq(&cm->reg_lock); oreg = inb(cm->iobase + reg.left_reg); val = (oreg >> reg.left_shift) & reg.mask; if (reg.invert) @@ -2027,7 +2017,7 @@ static int snd_cmipci_get_native_mixer(s val = reg.mask - val; ucontrol->value.integer.value[1] = val; } - spin_unlock_irqrestore(&cm->reg_lock, flags); + spin_unlock_irq(&cm->reg_lock); return 0; } @@ -2035,11 +2025,10 @@ static int snd_cmipci_put_native_mixer(s { cmipci_t *cm = snd_kcontrol_chip(kcontrol); cmipci_sb_reg_t reg; - unsigned long flags; unsigned char oreg, nreg, val; cmipci_sb_reg_decode(®, kcontrol->private_value); - spin_lock_irqsave(&cm->reg_lock, flags); + spin_lock_irq(&cm->reg_lock); oreg = inb(cm->iobase + reg.left_reg); val = ucontrol->value.integer.value[0] & reg.mask; if (reg.invert) @@ -2054,7 +2043,7 @@ static int snd_cmipci_put_native_mixer(s nreg |= (val << reg.right_shift); } outb(nreg, cm->iobase + reg.left_reg); - spin_unlock_irqrestore(&cm->reg_lock, flags); + spin_unlock_irq(&cm->reg_lock); return (nreg != oreg); } @@ -2078,8 +2067,6 @@ static int snd_cmipci_put_native_mixer_s } -#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t)) - static snd_kcontrol_new_t snd_cmipci_mixers[] __devinitdata = { CMIPCI_SB_VOL_STEREO("Master Playback Volume", SB_DSP4_MASTER_DEV, 3, 31), CMIPCI_MIXER_SW_MONO("3D Control - Switch", CM_REG_MIXER1, CM_X3DEN_SHIFT, 0), @@ -2137,14 +2124,13 @@ static int snd_cmipci_uswitch_info(snd_k static int _snd_cmipci_uswitch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol, snd_cmipci_switch_args_t *args) { - unsigned long flags; unsigned int val; cmipci_t *cm = snd_kcontrol_chip(kcontrol); - spin_lock_irqsave(&cm->reg_lock, flags); + spin_lock_irq(&cm->reg_lock); if (args->ac3_sensitive && cm->mixer_insensitive) { ucontrol->value.integer.value[0] = 0; - spin_unlock_irqrestore(&cm->reg_lock, flags); + spin_unlock_irq(&cm->reg_lock); return 0; } if (args->is_byte) @@ -2152,7 +2138,7 @@ static int _snd_cmipci_uswitch_get(snd_k else val = snd_cmipci_read(cm, args->reg); ucontrol->value.integer.value[0] = ((val & args->mask) == args->mask_on) ? 1 : 0; - spin_unlock_irqrestore(&cm->reg_lock, flags); + spin_unlock_irq(&cm->reg_lock); return 0; } @@ -2165,15 +2151,14 @@ static int snd_cmipci_uswitch_get(snd_kc static int _snd_cmipci_uswitch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol, snd_cmipci_switch_args_t *args) { - unsigned long flags; unsigned int val; int change; cmipci_t *cm = snd_kcontrol_chip(kcontrol); - spin_lock_irqsave(&cm->reg_lock, flags); + spin_lock_irq(&cm->reg_lock); if (args->ac3_sensitive && cm->mixer_insensitive) { /* ignored */ - spin_unlock_irqrestore(&cm->reg_lock, flags); + spin_unlock_irq(&cm->reg_lock); return 0; } if (args->is_byte) @@ -2192,7 +2177,7 @@ static int _snd_cmipci_uswitch_put(snd_k else snd_cmipci_write(cm, args->reg, val); } - spin_unlock_irqrestore(&cm->reg_lock, flags); + spin_unlock_irq(&cm->reg_lock); return change; } @@ -2345,7 +2330,6 @@ static snd_kcontrol_new_t snd_cmipci_con static int __devinit snd_cmipci_mixer_new(cmipci_t *cm, int pcm_spdif_device) { - unsigned long flags; snd_card_t *card; snd_kcontrol_new_t *sw; snd_kcontrol_t *kctl; @@ -2358,18 +2342,18 @@ static int __devinit snd_cmipci_mixer_ne strcpy(card->mixername, "CMedia PCI"); - spin_lock_irqsave(&cm->reg_lock, flags); + spin_lock_irq(&cm->reg_lock); snd_cmipci_mixer_write(cm, 0x00, 0x00); /* mixer reset */ - spin_unlock_irqrestore(&cm->reg_lock, flags); + spin_unlock_irq(&cm->reg_lock); - for (idx = 0; idx < num_controls(snd_cmipci_mixers); idx++) { + for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_mixers); idx++) { if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cmipci_mixers[idx], cm))) < 0) return err; } /* mixer switches */ sw = snd_cmipci_mixer_switches; - for (idx = 0; idx < num_controls(snd_cmipci_mixer_switches); idx++, sw++) { + for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_mixer_switches); idx++, sw++) { err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); if (err < 0) return err; @@ -2382,7 +2366,7 @@ static int __devinit snd_cmipci_mixer_ne if (cm->device == PCI_DEVICE_ID_CMEDIA_CM8738 || cm->device == PCI_DEVICE_ID_CMEDIA_CM8738B) { sw = snd_cmipci_8738_mixer_switches; - for (idx = 0; idx < num_controls(snd_cmipci_8738_mixer_switches); idx++, sw++) { + for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_8738_mixer_switches); idx++, sw++) { err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); if (err < 0) return err; @@ -2400,7 +2384,7 @@ static int __devinit snd_cmipci_mixer_ne } if (cm->chip_version <= 37) { sw = snd_cmipci_old_mixer_switches; - for (idx = 0; idx < num_controls(snd_cmipci_old_mixer_switches); idx++, sw++) { + for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_old_mixer_switches); idx++, sw++) { err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); if (err < 0) return err; @@ -2409,7 +2393,7 @@ static int __devinit snd_cmipci_mixer_ne } if (cm->chip_version >= 39) { sw = snd_cmipci_extra_mixer_switches; - for (idx = 0; idx < num_controls(snd_cmipci_extra_mixer_switches); idx++, sw++) { + for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_extra_mixer_switches); idx++, sw++) { err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); if (err < 0) return err; @@ -2418,7 +2402,7 @@ static int __devinit snd_cmipci_mixer_ne /* card switches */ sw = snd_cmipci_control_switches; - for (idx = 0; idx < num_controls(snd_cmipci_control_switches); idx++, sw++) { + for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_control_switches); idx++, sw++) { err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); if (err < 0) return err; @@ -2446,7 +2430,7 @@ static int __devinit snd_cmipci_mixer_ne static void snd_cmipci_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) { - cmipci_t *cm = snd_magic_cast(cmipci_t, entry->private_data, return); + cmipci_t *cm = entry->private_data; int i; snd_iprintf(buffer, "%s\n\n", cm->card->longname); @@ -2566,17 +2550,14 @@ static int snd_cmipci_free(cmipci_t *cm) kfree_nocheck(cm->res_joystick); } #endif - if (cm->res_iobase) { - release_resource(cm->res_iobase); - kfree_nocheck(cm->res_iobase); - } - snd_magic_kfree(cm); + pci_release_regions(cm->pci); + kfree(cm); return 0; } static int snd_cmipci_dev_free(snd_device_t *device) { - cmipci_t *cm = snd_magic_cast(cmipci_t, device->device_data, return -ENXIO); + cmipci_t *cm = device->device_data; return snd_cmipci_free(cm); } @@ -2598,7 +2579,7 @@ static int __devinit snd_cmipci_create(s if ((err = pci_enable_device(pci)) < 0) return err; - cm = snd_magic_kcalloc(cmipci_t, 0, GFP_KERNEL); + cm = kcalloc(1, sizeof(*cm), GFP_KERNEL); if (cm == NULL) return -ENOMEM; @@ -2608,16 +2589,16 @@ static int __devinit snd_cmipci_create(s cm->card = card; cm->pci = pci; cm->irq = -1; - cm->iobase = pci_resource_start(pci, 0); cm->channel[0].ch = 0; cm->channel[1].ch = 1; cm->channel[0].is_dac = cm->channel[1].is_dac = 1; /* dual DAC mode */ - if ((cm->res_iobase = request_region(cm->iobase, CM_EXTENT_CODEC, card->driver)) == NULL) { - snd_printk("unable to grab ports 0x%lx-0x%lx\n", cm->iobase, cm->iobase + CM_EXTENT_CODEC - 1); - err = -EBUSY; - goto __error; + if ((err = pci_request_regions(pci, card->driver)) < 0) { + kfree(cm); + return err; } + cm->iobase = pci_resource_start(pci, 0); + if (request_irq(pci->irq, snd_cmipci_interrupt, SA_INTERRUPT|SA_SHIRQ, card->driver, (void *)cm)) { snd_printk("unable to grab IRQ %d\n", pci->irq); err = -EBUSY; @@ -2776,12 +2757,12 @@ static int __devinit snd_cmipci_create(s int i; for (i = 0; ports[i]; i++) { joystick_port[dev] = ports[i]; - cm->res_joystick = request_region(ports[i], 8, "CMIPCI gameport"); + cm->res_joystick = request_region(ports[i], 1, "CMIPCI gameport"); if (cm->res_joystick) break; } } else { - cm->res_joystick = request_region(joystick_port[dev], 8, "CMIPCI gameport"); + cm->res_joystick = request_region(joystick_port[dev], 1, "CMIPCI gameport"); } } if (cm->res_joystick) { --- linux-2.6.9-rc1/sound/pci/cs4281.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/sound/pci/cs4281.c 2004-09-02 20:51:53.000000000 -0700 @@ -40,8 +40,7 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Cirrus Logic CS4281"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Cirrus Logic,CS4281}}"); +MODULE_SUPPORTED_DEVICE("{{Cirrus Logic,CS4281}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ @@ -51,16 +50,12 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for CS4281 soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for CS4281 soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable CS4281 soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(dual_codec, bool, boot_devs, 0444); MODULE_PARM_DESC(dual_codec, "Secondary Codec ID (0 = disabled)."); -MODULE_PARM_SYNTAX(dual_codec, SNDRV_ENABLED ",allows:{{0,3}}"); /* * @@ -441,8 +436,6 @@ MODULE_PARM_SYNTAX(dual_codec, SNDRV_ENA * */ -#define chip_t cs4281_t - typedef struct snd_cs4281 cs4281_t; typedef struct snd_cs4281_dma cs4281_dma_t; @@ -475,8 +468,6 @@ struct snd_cs4281 { unsigned long ba1; /* virtual (accessible) address */ unsigned long ba0_addr; unsigned long ba1_addr; - struct resource *ba0_res; - struct resource *ba1_res; int dual_codec; @@ -575,7 +566,7 @@ static void snd_cs4281_ac97_write(ac97_t * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h * 5. if DCV not cleared, break and return error */ - cs4281_t *chip = snd_magic_cast(cs4281_t, ac97->private_data, return); + cs4281_t *chip = ac97->private_data; int count; /* @@ -613,7 +604,7 @@ static void snd_cs4281_ac97_write(ac97_t static unsigned short snd_cs4281_ac97_read(ac97_t *ac97, unsigned short reg) { - cs4281_t *chip = snd_magic_cast(cs4281_t, ac97->private_data, return -ENXIO); + cs4281_t *chip = ac97->private_data; int count; unsigned short result; // FIXME: volatile is necessary in the following due to a bug of @@ -709,9 +700,8 @@ static int snd_cs4281_trigger(snd_pcm_su { cs4281_dma_t *dma = (cs4281_dma_t *)substream->runtime->private_data; cs4281_t *chip = snd_pcm_substream_chip(substream); - unsigned long flags; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock(&chip->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_PAUSE_PUSH: dma->valDCR |= BA0_DCR_MSK; @@ -738,13 +728,13 @@ static int snd_cs4281_trigger(snd_pcm_su dma->valFCR &= ~BA0_FCR_FEN; break; default: - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock(&chip->reg_lock); return -EINVAL; } snd_cs4281_pokeBA0(chip, dma->regDMR, dma->valDMR); snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR); snd_cs4281_pokeBA0(chip, dma->regDCR, dma->valDCR); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock(&chip->reg_lock); return 0; } @@ -851,11 +841,10 @@ static int snd_cs4281_playback_prepare(s snd_pcm_runtime_t *runtime = substream->runtime; cs4281_dma_t *dma = (cs4281_dma_t *)runtime->private_data; cs4281_t *chip = snd_pcm_substream_chip(substream); - unsigned long flags; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); snd_cs4281_mode(chip, dma, runtime, 0, 1); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return 0; } @@ -864,11 +853,10 @@ static int snd_cs4281_capture_prepare(sn snd_pcm_runtime_t *runtime = substream->runtime; cs4281_dma_t *dma = (cs4281_dma_t *)runtime->private_data; cs4281_t *chip = snd_pcm_substream_chip(substream); - unsigned long flags; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); snd_cs4281_mode(chip, dma, runtime, 1, 1); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return 0; } @@ -1015,7 +1003,7 @@ static snd_pcm_ops_t snd_cs4281_capture_ static void snd_cs4281_pcm_free(snd_pcm_t *pcm) { - cs4281_t *chip = snd_magic_cast(cs4281_t, pcm->private_data, return); + cs4281_t *chip = pcm->private_data; chip->pcm = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -1124,13 +1112,13 @@ static snd_kcontrol_new_t snd_cs4281_pcm static void snd_cs4281_mixer_free_ac97_bus(ac97_bus_t *bus) { - cs4281_t *chip = snd_magic_cast(cs4281_t, bus->private_data, return); + cs4281_t *chip = bus->private_data; chip->ac97_bus = NULL; } static void snd_cs4281_mixer_free_ac97(ac97_t *ac97) { - cs4281_t *chip = snd_magic_cast(cs4281_t, ac97->private_data, return); + cs4281_t *chip = ac97->private_data; if (ac97->num) chip->ac97_secondary = NULL; else @@ -1140,17 +1128,16 @@ static void snd_cs4281_mixer_free_ac97(a static int __devinit snd_cs4281_mixer(cs4281_t * chip) { snd_card_t *card = chip->card; - ac97_bus_t bus; - ac97_t ac97; + ac97_template_t ac97; int err; + static ac97_bus_ops_t ops = { + .write = snd_cs4281_ac97_write, + .read = snd_cs4281_ac97_read, + }; - memset(&bus, 0, sizeof(bus)); - bus.write = snd_cs4281_ac97_write; - bus.read = snd_cs4281_ac97_read; - bus.private_data = chip; - bus.private_free = snd_cs4281_mixer_free_ac97_bus; - if ((err = snd_ac97_bus(card, &bus, &chip->ac97_bus)) < 0) + if ((err = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus)) < 0) return err; + chip->ac97_bus->private_free = snd_cs4281_mixer_free_ac97_bus; memset(&ac97, 0, sizeof(ac97)); ac97.private_data = chip; @@ -1177,7 +1164,7 @@ static int __devinit snd_cs4281_mixer(cs static void snd_cs4281_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) { - cs4281_t *chip = snd_magic_cast(cs4281_t, entry->private_data, return); + cs4281_t *chip = entry->private_data; snd_iprintf(buffer, "Cirrus Logic CS4281\n\n"); snd_iprintf(buffer, "Spurious half IRQs : %u\n", chip->spurious_dhtc_irq); @@ -1189,7 +1176,7 @@ static long snd_cs4281_BA0_read(snd_info unsigned long count, unsigned long pos) { long size; - cs4281_t *chip = snd_magic_cast(cs4281_t, entry->private_data, return -ENXIO); + cs4281_t *chip = entry->private_data; size = count; if (pos + size > CS4281_BA0_SIZE) @@ -1206,7 +1193,7 @@ static long snd_cs4281_BA1_read(snd_info unsigned long count, unsigned long pos) { long size; - cs4281_t *chip = snd_magic_cast(cs4281_t, entry->private_data, return -ENXIO); + cs4281_t *chip = entry->private_data; size = count; if (pos + size > CS4281_BA1_SIZE) @@ -1262,7 +1249,7 @@ static void snd_cs4281_gameport_trigger( cs4281_gameport_t *gp = (cs4281_gameport_t *)gameport; cs4281_t *chip; snd_assert(gp, return); - chip = snd_magic_cast(cs4281_t, gp->chip, return); + chip = gp->chip; snd_cs4281_pokeBA0(chip, BA0_JSPT, 0xff); } @@ -1271,7 +1258,7 @@ static unsigned char snd_cs4281_gameport cs4281_gameport_t *gp = (cs4281_gameport_t *)gameport; cs4281_t *chip; snd_assert(gp, return 0); - chip = snd_magic_cast(cs4281_t, gp->chip, return 0); + chip = gp->chip; return snd_cs4281_peekBA0(chip, BA0_JSPT); } @@ -1283,7 +1270,7 @@ static int snd_cs4281_gameport_cooked_re unsigned js1, js2, jst; snd_assert(gp, return 0); - chip = snd_magic_cast(cs4281_t, gp->chip, return 0); + chip = gp->chip; js1 = snd_cs4281_peekBA0(chip, BA0_JSC1); js2 = snd_cs4281_peekBA0(chip, BA0_JSC2); @@ -1369,28 +1356,21 @@ static int snd_cs4281_free(cs4281_t *chi /* PCI interface - D3 state */ pci_set_power_state(chip->pci, 3); + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); if (chip->ba0) iounmap((void *) chip->ba0); if (chip->ba1) iounmap((void *) chip->ba1); - if (chip->ba0_res) { - release_resource(chip->ba0_res); - kfree_nocheck(chip->ba0_res); - } - if (chip->ba1_res) { - release_resource(chip->ba1_res); - kfree_nocheck(chip->ba1_res); - } - if (chip->irq >= 0) - free_irq(chip->irq, (void *)chip); + pci_release_regions(chip->pci); - snd_magic_kfree(chip); + kfree(chip); return 0; } static int snd_cs4281_dev_free(snd_device_t *device) { - cs4281_t *chip = snd_magic_cast(cs4281_t, device->device_data, return -ENXIO); + cs4281_t *chip = device->device_data; return snd_cs4281_free(chip); } @@ -1415,15 +1395,13 @@ static int __devinit snd_cs4281_create(s *rchip = NULL; if ((err = pci_enable_device(pci)) < 0) return err; - chip = snd_magic_kcalloc(cs4281_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; spin_lock_init(&chip->reg_lock); chip->card = card; chip->pci = pci; chip->irq = -1; - chip->ba0_addr = pci_resource_start(pci, 0); - chip->ba1_addr = pci_resource_start(pci, 1); pci_set_master(pci); if (dual_codec < 0 || dual_codec > 3) { snd_printk(KERN_ERR "invalid dual_codec option %d\n", dual_codec); @@ -1431,16 +1409,13 @@ static int __devinit snd_cs4281_create(s } chip->dual_codec = dual_codec; - if ((chip->ba0_res = request_mem_region(chip->ba0_addr, CS4281_BA0_SIZE, "CS4281 BA0")) == NULL) { - snd_printk(KERN_ERR "unable to grab memory region 0x%lx-0x%lx\n", chip->ba0_addr, chip->ba0_addr + CS4281_BA0_SIZE - 1); - snd_cs4281_free(chip); - return -ENOMEM; - } - if ((chip->ba1_res = request_mem_region(chip->ba1_addr, CS4281_BA1_SIZE, "CS4281 BA1")) == NULL) { - snd_printk(KERN_ERR "unable to grab memory region 0x%lx-0x%lx\n", chip->ba1_addr, chip->ba1_addr + CS4281_BA1_SIZE - 1); - snd_cs4281_free(chip); - return -ENOMEM; + if ((err = pci_request_regions(pci, "CS4281")) < 0) { + kfree(chip); + return err; } + chip->ba0_addr = pci_resource_start(pci, 0); + chip->ba1_addr = pci_resource_start(pci, 1); + if (request_irq(pci->irq, snd_cs4281_interrupt, SA_INTERRUPT|SA_SHIRQ, "CS4281", (void *)chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_cs4281_free(chip); @@ -1448,8 +1423,8 @@ static int __devinit snd_cs4281_create(s } chip->irq = pci->irq; - chip->ba0 = (unsigned long) ioremap_nocache(chip->ba0_addr, CS4281_BA0_SIZE); - chip->ba1 = (unsigned long) ioremap_nocache(chip->ba1_addr, CS4281_BA1_SIZE); + chip->ba0 = (unsigned long) ioremap_nocache(chip->ba0_addr, pci_resource_len(pci, 0)); + chip->ba1 = (unsigned long) ioremap_nocache(chip->ba1_addr, pci_resource_len(pci, 1)); if (!chip->ba0 || !chip->ba1) { snd_cs4281_free(chip); return -ENOMEM; @@ -1713,10 +1688,9 @@ static void snd_cs4281_midi_reset(cs4281 static int snd_cs4281_midi_input_open(snd_rawmidi_substream_t * substream) { - unsigned long flags; - cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return -ENXIO); + cs4281_t *chip = substream->rmidi->private_data; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); chip->midcr |= BA0_MIDCR_RXE; chip->midi_input = substream; if (!(chip->uartm & CS4281_MODE_OUTPUT)) { @@ -1724,16 +1698,15 @@ static int snd_cs4281_midi_input_open(sn } else { snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); } - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return 0; } static int snd_cs4281_midi_input_close(snd_rawmidi_substream_t * substream) { - unsigned long flags; - cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return -ENXIO); + cs4281_t *chip = substream->rmidi->private_data; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); chip->midcr &= ~(BA0_MIDCR_RXE | BA0_MIDCR_RIE); chip->midi_input = NULL; if (!(chip->uartm & CS4281_MODE_OUTPUT)) { @@ -1742,16 +1715,15 @@ static int snd_cs4281_midi_input_close(s snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); } chip->uartm &= ~CS4281_MODE_INPUT; - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return 0; } static int snd_cs4281_midi_output_open(snd_rawmidi_substream_t * substream) { - unsigned long flags; - cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return -ENXIO); + cs4281_t *chip = substream->rmidi->private_data; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); chip->uartm |= CS4281_MODE_OUTPUT; chip->midcr |= BA0_MIDCR_TXE; chip->midi_output = substream; @@ -1760,16 +1732,15 @@ static int snd_cs4281_midi_output_open(s } else { snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); } - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return 0; } static int snd_cs4281_midi_output_close(snd_rawmidi_substream_t * substream) { - unsigned long flags; - cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return -ENXIO); + cs4281_t *chip = substream->rmidi->private_data; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); chip->midcr &= ~(BA0_MIDCR_TXE | BA0_MIDCR_TIE); chip->midi_output = NULL; if (!(chip->uartm & CS4281_MODE_INPUT)) { @@ -1778,14 +1749,14 @@ static int snd_cs4281_midi_output_close( snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); } chip->uartm &= ~CS4281_MODE_OUTPUT; - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return 0; } static void snd_cs4281_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) { unsigned long flags; - cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return); + cs4281_t *chip = substream->rmidi->private_data; spin_lock_irqsave(&chip->reg_lock, flags); if (up) { @@ -1805,7 +1776,7 @@ static void snd_cs4281_midi_input_trigge static void snd_cs4281_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) { unsigned long flags; - cs4281_t *chip = snd_magic_cast(cs4281_t, substream->rmidi->private_data, return); + cs4281_t *chip = substream->rmidi->private_data; unsigned char byte; spin_lock_irqsave(&chip->reg_lock, flags); @@ -1872,7 +1843,7 @@ static int __devinit snd_cs4281_midi(cs4 static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - cs4281_t *chip = snd_magic_cast(cs4281_t, dev_id, return IRQ_NONE); + cs4281_t *chip = dev_id; unsigned int status, dma, val; cs4281_dma_t *cdma; @@ -1919,7 +1890,9 @@ static irqreturn_t snd_cs4281_interrupt( c = snd_cs4281_peekBA0(chip, BA0_MIDRP); if ((chip->midcr & BA0_MIDCR_RIE) == 0) continue; + spin_unlock(&chip->reg_lock); snd_rawmidi_receive(chip->midi_input, &c, 1); + spin_lock(&chip->reg_lock); } while ((snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_TBF) == 0) { if ((chip->midcr & BA0_MIDCR_TIE) == 0) @@ -2034,13 +2007,11 @@ static int saved_regs[SUSPEND_REGISTERS] BA0_PPRVC, }; -#define number_of(array) (sizeof(array) / sizeof(array[0])) - #define CLKCR1_CKRA 0x00010000L static int cs4281_suspend(snd_card_t *card, unsigned int state) { - cs4281_t *chip = snd_magic_cast(cs4281_t, card->pm_private_data, return -EINVAL); + cs4281_t *chip = card->pm_private_data; u32 ulCLK; unsigned int i; @@ -2059,7 +2030,7 @@ static int cs4281_suspend(snd_card_t *ca snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_CHGM); /* remember the status registers */ - for (i = 0; i < number_of(saved_regs); i++) + for (i = 0; i < ARRAY_SIZE(saved_regs); i++) if (saved_regs[i]) chip->suspend_regs[i] = snd_cs4281_peekBA0(chip, saved_regs[i]); @@ -2085,7 +2056,7 @@ static int cs4281_suspend(snd_card_t *ca static int cs4281_resume(snd_card_t *card, unsigned int state) { - cs4281_t *chip = snd_magic_cast(cs4281_t, card->pm_private_data, return -EINVAL); + cs4281_t *chip = card->pm_private_data; unsigned int i; u32 ulCLK; @@ -2098,7 +2069,7 @@ static int cs4281_resume(snd_card_t *car snd_cs4281_chip_init(chip); /* restore the status registers */ - for (i = 0; i < number_of(saved_regs); i++) + for (i = 0; i < ARRAY_SIZE(saved_regs); i++) if (saved_regs[i]) snd_cs4281_pokeBA0(chip, saved_regs[i], chip->suspend_regs[i]); --- linux-2.6.9-rc1/sound/pci/cs46xx/cs46xx.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/sound/pci/cs46xx/cs46xx.c 2004-09-02 20:51:53.000000000 -0700 @@ -37,8 +37,7 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Cirrus Logic Sound Fusion CS46XX"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Cirrus Logic,Sound Fusion (CS4280)}," +MODULE_SUPPORTED_DEVICE("{{Cirrus Logic,Sound Fusion (CS4280)}," "{Cirrus Logic,Sound Fusion (CS4610)}," "{Cirrus Logic,Sound Fusion (CS4612)}," "{Cirrus Logic,Sound Fusion (CS4615)}," @@ -56,22 +55,16 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for the CS46xx soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for the CS46xx soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable CS46xx soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(external_amp, bool, boot_devs, 0444); MODULE_PARM_DESC(external_amp, "Force to enable external amplifer."); -MODULE_PARM_SYNTAX(external_amp, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); module_param_array(thinkpad, bool, boot_devs, 0444); MODULE_PARM_DESC(thinkpad, "Force to enable Thinkpad's CLKRUN control."); -MODULE_PARM_SYNTAX(thinkpad, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); module_param_array(mmap_valid, bool, boot_devs, 0444); MODULE_PARM_DESC(mmap_valid, "Support OSS mmap."); -MODULE_PARM_SYNTAX(mmap_valid, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC); static struct pci_device_id snd_cs46xx_ids[] = { { 0x1013, 0x6001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CS4280 */ --- linux-2.6.9-rc1/sound/pci/cs46xx/cs46xx_lib.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/sound/pci/cs46xx/cs46xx_lib.c 2004-09-02 20:51:53.000000000 -0700 @@ -79,6 +79,8 @@ static unsigned short snd_cs46xx_codec_r (codec_index == CS46XX_SECONDARY_CODEC_INDEX), return -EINVAL); + chip->active_ctrl(chip, 1); + if (codec_index == CS46XX_SECONDARY_CODEC_INDEX) offset = CS46XX_SECONDARY_CODEC_OFFSET; @@ -184,27 +186,22 @@ static unsigned short snd_cs46xx_codec_r //snd_cs46xx_peekBA0(chip, BA0_ACCAD); result = snd_cs46xx_peekBA0(chip, BA0_ACSDA + offset); end: + chip->active_ctrl(chip, -1); return result; } static unsigned short snd_cs46xx_ac97_read(ac97_t * ac97, unsigned short reg) { - cs46xx_t *chip = snd_magic_cast(cs46xx_t, ac97->private_data, return -ENXIO); + cs46xx_t *chip = ac97->private_data; unsigned short val; - int codec_index = -1; + int codec_index = ac97->num; + + snd_assert(codec_index == CS46XX_PRIMARY_CODEC_INDEX || + codec_index == CS46XX_SECONDARY_CODEC_INDEX, + return 0xffff); - /* UGGLY: nr_ac97_codecs == 0 primery codec detection is in progress */ - if (ac97 == chip->ac97[CS46XX_PRIMARY_CODEC_INDEX] || chip->nr_ac97_codecs == 0) - codec_index = CS46XX_PRIMARY_CODEC_INDEX; - /* UGGLY: nr_ac97_codecs == 1 secondary codec detection is in progress */ - else if (ac97 == chip->ac97[CS46XX_SECONDARY_CODEC_INDEX] || chip->nr_ac97_codecs == 1) - codec_index = CS46XX_SECONDARY_CODEC_INDEX; - else - snd_assert(0, return 0xffff); - chip->active_ctrl(chip, 1); val = snd_cs46xx_codec_read(chip, reg, codec_index); - chip->active_ctrl(chip, -1); /* HACK: voyetra uses EAPD bit in the reverse way. * we flip the bit to show the mixer status correctly @@ -227,6 +224,8 @@ static void snd_cs46xx_codec_write(cs46x (codec_index == CS46XX_SECONDARY_CODEC_INDEX), return); + chip->active_ctrl(chip, 1); + /* * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 @@ -271,27 +270,24 @@ static void snd_cs46xx_codec_write(cs46x * ACCTL = 460h, DCV should be reset by now and 460h = 07h */ if (!(snd_cs46xx_peekBA0(chip, BA0_ACCTL) & ACCTL_DCV)) { - return; + goto end; } } snd_printk("AC'97 write problem, codec_index = %d, reg = 0x%x, val = 0x%x\n", codec_index, reg, val); + end: + chip->active_ctrl(chip, -1); } static void snd_cs46xx_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val) { - cs46xx_t *chip = snd_magic_cast(cs46xx_t, ac97->private_data, return); - int codec_index = -1; + cs46xx_t *chip = ac97->private_data; + int codec_index = ac97->num; - /* UGGLY: nr_ac97_codecs == 0 primery codec detection is in progress */ - if (ac97 == chip->ac97[CS46XX_PRIMARY_CODEC_INDEX] || chip->nr_ac97_codecs == 0) - codec_index = CS46XX_PRIMARY_CODEC_INDEX; - /* UGGLY: nr_ac97_codecs == 1 secondary codec detection is in progress */ - else if (ac97 == chip->ac97[CS46XX_SECONDARY_CODEC_INDEX] || chip->nr_ac97_codecs == 1) - codec_index = CS46XX_SECONDARY_CODEC_INDEX; - else - snd_assert(0,return); + snd_assert(codec_index == CS46XX_PRIMARY_CODEC_INDEX || + codec_index == CS46XX_SECONDARY_CODEC_INDEX, + return); /* HACK: voyetra uses EAPD bit in the reverse way. * we flip the bit to show the mixer status correctly @@ -299,9 +295,7 @@ static void snd_cs46xx_ac97_write(ac97_t if (reg == AC97_POWERDOWN && chip->amplifier_ctrl == amp_voyetra) val ^= 0x8000; - chip->active_ctrl(chip, 1); snd_cs46xx_codec_write(chip, reg, val, codec_index); - chip->active_ctrl(chip, -1); } @@ -688,84 +682,35 @@ static void snd_cs46xx_set_capture_sampl * PCM part */ +static void snd_cs46xx_pb_trans_copy(snd_pcm_substream_t *substream, + snd_pcm_indirect_t *rec, size_t bytes) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + cs46xx_pcm_t * cpcm = runtime->private_data; + memcpy(cpcm->hw_buf.area + rec->hw_data, runtime->dma_area + rec->sw_data, bytes); +} + static int snd_cs46xx_playback_transfer(snd_pcm_substream_t *substream) { - /* cs46xx_t *chip = snd_pcm_substream_chip(substream); */ snd_pcm_runtime_t *runtime = substream->runtime; - cs46xx_pcm_t * cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return -ENXIO); - snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr; - snd_pcm_sframes_t diff = appl_ptr - cpcm->appl_ptr; - int buffer_size = runtime->period_size * CS46XX_FRAGS << cpcm->shift; - - if (diff) { - if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) - diff += runtime->boundary; - cpcm->sw_ready += diff * (1 << cpcm->shift); - cpcm->appl_ptr = appl_ptr; - } - while (cpcm->hw_ready < buffer_size && - cpcm->sw_ready > 0) { - size_t hw_to_end = buffer_size - cpcm->hw_data; - size_t sw_to_end = cpcm->sw_bufsize - cpcm->sw_data; - size_t bytes = buffer_size - cpcm->hw_ready; - if (cpcm->sw_ready < (int)bytes) - bytes = cpcm->sw_ready; - if (hw_to_end < bytes) - bytes = hw_to_end; - if (sw_to_end < bytes) - bytes = sw_to_end; - memcpy(cpcm->hw_buf.area + cpcm->hw_data, - runtime->dma_area + cpcm->sw_data, - bytes); - cpcm->hw_data += bytes; - if ((int)cpcm->hw_data == buffer_size) - cpcm->hw_data = 0; - cpcm->sw_data += bytes; - if (cpcm->sw_data == cpcm->sw_bufsize) - cpcm->sw_data = 0; - cpcm->hw_ready += bytes; - cpcm->sw_ready -= bytes; - } + cs46xx_pcm_t * cpcm = runtime->private_data; + snd_pcm_indirect_playback_transfer(substream, &cpcm->pcm_rec, snd_cs46xx_pb_trans_copy); return 0; } -static int snd_cs46xx_capture_transfer(snd_pcm_substream_t *substream) +static void snd_cs46xx_cp_trans_copy(snd_pcm_substream_t *substream, + snd_pcm_indirect_t *rec, size_t bytes) { cs46xx_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr; - snd_pcm_sframes_t diff = appl_ptr - chip->capt.appl_ptr; - int buffer_size = runtime->period_size * CS46XX_FRAGS << chip->capt.shift; - - if (diff) { - if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) - diff += runtime->boundary; - chip->capt.sw_ready -= diff * (1 << chip->capt.shift); - chip->capt.appl_ptr = appl_ptr; - } - while (chip->capt.hw_ready > 0 && - chip->capt.sw_ready < (int)chip->capt.sw_bufsize) { - size_t hw_to_end = buffer_size - chip->capt.hw_data; - size_t sw_to_end = chip->capt.sw_bufsize - chip->capt.sw_data; - size_t bytes = chip->capt.sw_bufsize - chip->capt.sw_ready; - if (chip->capt.hw_ready < (int)bytes) - bytes = chip->capt.hw_ready; - if (hw_to_end < bytes) - bytes = hw_to_end; - if (sw_to_end < bytes) - bytes = sw_to_end; - memcpy(runtime->dma_area + chip->capt.sw_data, - chip->capt.hw_buf.area + chip->capt.hw_data, - bytes); - chip->capt.hw_data += bytes; - if ((int)chip->capt.hw_data == buffer_size) - chip->capt.hw_data = 0; - chip->capt.sw_data += bytes; - if (chip->capt.sw_data == chip->capt.sw_bufsize) - chip->capt.sw_data = 0; - chip->capt.hw_ready -= bytes; - chip->capt.sw_ready += bytes; - } + memcpy(runtime->dma_area + rec->sw_data, + chip->capt.hw_buf.area + rec->hw_data, bytes); +} + +static int snd_cs46xx_capture_transfer(snd_pcm_substream_t *substream) +{ + cs46xx_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_indirect_capture_transfer(substream, &chip->capt.pcm_rec, snd_cs46xx_cp_trans_copy); return 0; } @@ -773,7 +718,7 @@ static snd_pcm_uframes_t snd_cs46xx_play { cs46xx_t *chip = snd_pcm_substream_chip(substream); size_t ptr; - cs46xx_pcm_t *cpcm = snd_magic_cast(cs46xx_pcm_t, substream->runtime->private_data, return -ENXIO); + cs46xx_pcm_t *cpcm = substream->runtime->private_data; snd_assert (cpcm->pcm_channel,return -ENXIO); #ifdef CONFIG_SND_CS46XX_NEW_DSP @@ -789,9 +734,7 @@ static snd_pcm_uframes_t snd_cs46xx_play { cs46xx_t *chip = snd_pcm_substream_chip(substream); size_t ptr; - cs46xx_pcm_t *cpcm = snd_magic_cast(cs46xx_pcm_t, substream->runtime->private_data, return -ENXIO); - ssize_t bytes; - int buffer_size = substream->runtime->period_size * CS46XX_FRAGS << cpcm->shift; + cs46xx_pcm_t *cpcm = substream->runtime->private_data; #ifdef CONFIG_SND_CS46XX_NEW_DSP snd_assert (cpcm->pcm_channel,return -ENXIO); @@ -800,18 +743,7 @@ static snd_pcm_uframes_t snd_cs46xx_play ptr = snd_cs46xx_peek(chip, BA1_PBA); #endif ptr -= cpcm->hw_buf.addr; - - bytes = ptr - cpcm->hw_io; - - if (bytes < 0) - bytes += buffer_size; - cpcm->hw_io = ptr; - cpcm->hw_ready -= bytes; - cpcm->sw_io += bytes; - if (cpcm->sw_io >= cpcm->sw_bufsize) - cpcm->sw_io -= cpcm->sw_bufsize; - snd_cs46xx_playback_transfer(substream); - return cpcm->sw_io >> cpcm->shift; + return snd_pcm_indirect_playback_pointer(substream, &cpcm->pcm_rec, ptr); } static snd_pcm_uframes_t snd_cs46xx_capture_direct_pointer(snd_pcm_substream_t * substream) @@ -825,18 +757,7 @@ static snd_pcm_uframes_t snd_cs46xx_capt { cs46xx_t *chip = snd_pcm_substream_chip(substream); size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_buf.addr; - ssize_t bytes = ptr - chip->capt.hw_io; - int buffer_size = substream->runtime->period_size * CS46XX_FRAGS << chip->capt.shift; - - if (bytes < 0) - bytes += buffer_size; - chip->capt.hw_io = ptr; - chip->capt.hw_ready += bytes; - chip->capt.sw_io += bytes; - if (chip->capt.sw_io >= chip->capt.sw_bufsize) - chip->capt.sw_io -= chip->capt.sw_bufsize; - snd_cs46xx_capture_transfer(substream); - return chip->capt.sw_io >> chip->capt.shift; + return snd_pcm_indirect_capture_pointer(substream, &chip->capt.pcm_rec, ptr); } static int snd_cs46xx_playback_trigger(snd_pcm_substream_t * substream, @@ -847,13 +768,7 @@ static int snd_cs46xx_playback_trigger(s int result = 0; #ifdef CONFIG_SND_CS46XX_NEW_DSP - cs46xx_pcm_t *cpcm = snd_magic_cast(cs46xx_pcm_t, substream->runtime->private_data, return -ENXIO); -#else - spin_lock(&chip->reg_lock); -#endif - -#ifdef CONFIG_SND_CS46XX_NEW_DSP - + cs46xx_pcm_t *cpcm = substream->runtime->private_data; if (! cpcm->pcm_channel) { return -ENXIO; } @@ -872,6 +787,7 @@ static int snd_cs46xx_playback_trigger(s if (substream->runtime->periods != CS46XX_FRAGS) snd_cs46xx_playback_transfer(substream); #else + spin_lock(&chip->reg_lock); if (substream->runtime->periods != CS46XX_FRAGS) snd_cs46xx_playback_transfer(substream); { unsigned int tmp; @@ -879,6 +795,7 @@ static int snd_cs46xx_playback_trigger(s tmp &= 0x0000ffff; snd_cs46xx_poke(chip, BA1_PCTL, chip->play_ctl | tmp); } + spin_unlock(&chip->reg_lock); #endif break; case SNDRV_PCM_TRIGGER_STOP: @@ -891,11 +808,13 @@ static int snd_cs46xx_playback_trigger(s if (!cpcm->pcm_channel->unlinked) cs46xx_dsp_pcm_unlink(chip,cpcm->pcm_channel); #else + spin_lock(&chip->reg_lock); { unsigned int tmp; tmp = snd_cs46xx_peek(chip, BA1_PCTL); tmp &= 0x0000ffff; snd_cs46xx_poke(chip, BA1_PCTL, tmp); } + spin_unlock(&chip->reg_lock); #endif break; default: @@ -903,10 +822,6 @@ static int snd_cs46xx_playback_trigger(s break; } -#ifndef CONFIG_SND_CS46XX_NEW_DSP - spin_unlock(&chip->reg_lock); -#endif - return result; } @@ -987,7 +902,7 @@ static int snd_cs46xx_playback_hw_params int sample_rate = params_rate(hw_params); int period_size = params_period_bytes(hw_params); #endif - cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return -ENXIO); + cpcm = runtime->private_data; #ifdef CONFIG_SND_CS46XX_NEW_DSP snd_assert (sample_rate != 0, return -ENXIO); @@ -1084,7 +999,7 @@ static int snd_cs46xx_playback_hw_free(s snd_pcm_runtime_t *runtime = substream->runtime; cs46xx_pcm_t *cpcm; - cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return -ENXIO); + cpcm = runtime->private_data; /* if play_back open fails, then this function is called and cpcm can actually be NULL here */ @@ -1108,7 +1023,7 @@ static int snd_cs46xx_playback_prepare(s snd_pcm_runtime_t *runtime = substream->runtime; cs46xx_pcm_t *cpcm; - cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return -ENXIO); + cpcm = runtime->private_data; #ifdef CONFIG_SND_CS46XX_NEW_DSP snd_assert (cpcm->pcm_channel != NULL, return -ENXIO); @@ -1143,10 +1058,9 @@ static int snd_cs46xx_playback_prepare(s pfie |= 0x00004000; } - cpcm->sw_bufsize = snd_pcm_lib_buffer_bytes(substream); - cpcm->sw_data = cpcm->sw_io = cpcm->sw_ready = 0; - cpcm->hw_data = cpcm->hw_io = cpcm->hw_ready = 0; - cpcm->appl_ptr = 0; + memset(&cpcm->pcm_rec, 0, sizeof(cpcm->pcm_rec)); + cpcm->pcm_rec.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream); + cpcm->pcm_rec.hw_buffer_size = runtime->period_size * CS46XX_FRAGS << cpcm->shift; #ifdef CONFIG_SND_CS46XX_NEW_DSP @@ -1223,10 +1137,9 @@ static int snd_cs46xx_capture_prepare(sn snd_cs46xx_poke(chip, BA1_CBA, chip->capt.hw_buf.addr); chip->capt.shift = 2; - chip->capt.sw_bufsize = snd_pcm_lib_buffer_bytes(substream); - chip->capt.sw_data = chip->capt.sw_io = chip->capt.sw_ready = 0; - chip->capt.hw_data = chip->capt.hw_io = chip->capt.hw_ready = 0; - chip->capt.appl_ptr = 0; + memset(&chip->capt.pcm_rec, 0, sizeof(chip->capt.pcm_rec)); + chip->capt.pcm_rec.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream); + chip->capt.pcm_rec.hw_buffer_size = runtime->period_size * CS46XX_FRAGS << 2; snd_cs46xx_set_capture_sample_rate(chip, runtime->rate); return 0; @@ -1234,7 +1147,7 @@ static int snd_cs46xx_capture_prepare(sn static irqreturn_t snd_cs46xx_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - cs46xx_t *chip = snd_magic_cast(cs46xx_t, dev_id, return IRQ_NONE); + cs46xx_t *chip = dev_id; u32 status1; #ifdef CONFIG_SND_CS46XX_NEW_DSP dsp_spos_instance_t * ins = chip->dsp_spos_instance; @@ -1265,7 +1178,7 @@ static irqreturn_t snd_cs46xx_interrupt( if (ins->pcm_channels[i].active && ins->pcm_channels[i].private_data && !ins->pcm_channels[i].unlinked) { - cpcm = snd_magic_cast(cs46xx_pcm_t, ins->pcm_channels[i].private_data, continue); + cpcm = ins->pcm_channels[i].private_data; snd_pcm_period_elapsed(cpcm->substream); } } @@ -1275,7 +1188,7 @@ static irqreturn_t snd_cs46xx_interrupt( if (ins->pcm_channels[i].active && ins->pcm_channels[i].private_data && !ins->pcm_channels[i].unlinked) { - cpcm = snd_magic_cast(cs46xx_pcm_t, ins->pcm_channels[i].private_data, continue); + cpcm = ins->pcm_channels[i].private_data; snd_pcm_period_elapsed(cpcm->substream); } } @@ -1302,7 +1215,9 @@ static irqreturn_t snd_cs46xx_interrupt( c = snd_cs46xx_peekBA0(chip, BA0_MIDRP); if ((chip->midcr & MIDCR_RIE) == 0) continue; + spin_unlock(&chip->reg_lock); snd_rawmidi_receive(chip->midi_input, &c, 1); + spin_lock(&chip->reg_lock); } while ((snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_TBF) == 0) { if ((chip->midcr & MIDCR_TIE) == 0) @@ -1370,10 +1285,8 @@ static snd_pcm_hardware_t snd_cs46xx_cap static unsigned int period_sizes[] = { 32, 64, 128, 256, 512, 1024, 2048 }; -#define PERIOD_SIZES sizeof(period_sizes) / sizeof(period_sizes[0]) - static snd_pcm_hw_constraint_list_t hw_constraints_period_sizes = { - .count = PERIOD_SIZES, + .count = ARRAY_SIZE(period_sizes), .list = period_sizes, .mask = 0 }; @@ -1382,10 +1295,8 @@ static snd_pcm_hw_constraint_list_t hw_c static void snd_cs46xx_pcm_free_substream(snd_pcm_runtime_t *runtime) { - cs46xx_pcm_t * cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return); - - if (cpcm) - snd_magic_kfree(cpcm); + cs46xx_pcm_t * cpcm = runtime->private_data; + kfree(cpcm); } static int _cs46xx_playback_open_channel (snd_pcm_substream_t * substream,int pcm_channel_id) @@ -1394,11 +1305,12 @@ static int _cs46xx_playback_open_channel cs46xx_pcm_t * cpcm; snd_pcm_runtime_t *runtime = substream->runtime; - cpcm = snd_magic_kcalloc(cs46xx_pcm_t, 0, GFP_KERNEL); + cpcm = kcalloc(1, sizeof(*cpcm), GFP_KERNEL); if (cpcm == NULL) return -ENOMEM; - if (snd_dma_alloc_pages(&chip->dma_dev, PAGE_SIZE, &cpcm->hw_buf) < 0) { - snd_magic_kfree(cpcm); + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), + PAGE_SIZE, &cpcm->hw_buf) < 0) { + kfree(cpcm); return -ENOMEM; } @@ -1486,7 +1398,8 @@ static int snd_cs46xx_capture_open(snd_p { cs46xx_t *chip = snd_pcm_substream_chip(substream); - if (snd_dma_alloc_pages(&chip->dma_dev, PAGE_SIZE, &chip->capt.hw_buf) < 0) + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), + PAGE_SIZE, &chip->capt.hw_buf) < 0) return -ENOMEM; chip->capt.substream = substream; substream->runtime->hw = snd_cs46xx_capture; @@ -1510,7 +1423,7 @@ static int snd_cs46xx_playback_close(snd snd_pcm_runtime_t *runtime = substream->runtime; cs46xx_pcm_t * cpcm; - cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return -ENXIO); + cpcm = runtime->private_data; /* when playback_open fails, then cpcm can be NULL */ if (!cpcm) return -ENXIO; @@ -1527,7 +1440,7 @@ static int snd_cs46xx_playback_close(snd #endif cpcm->substream = NULL; - snd_dma_free_pages(&chip->dma_dev, &cpcm->hw_buf); + snd_dma_free_pages(&cpcm->hw_buf); chip->active_ctrl(chip, -1); return 0; @@ -1538,7 +1451,7 @@ static int snd_cs46xx_capture_close(snd_ cs46xx_t *chip = snd_pcm_substream_chip(substream); chip->capt.substream = NULL; - snd_dma_free_pages(&chip->dma_dev, &chip->capt.hw_buf); + snd_dma_free_pages(&chip->capt.hw_buf); chip->active_ctrl(chip, -1); return 0; @@ -1664,7 +1577,7 @@ snd_pcm_ops_t snd_cs46xx_capture_indirec static void snd_cs46xx_pcm_free(snd_pcm_t *pcm) { - cs46xx_t *chip = snd_magic_cast(cs46xx_t, pcm->private_data, return); + cs46xx_t *chip = pcm->private_data; chip->pcm = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -1672,21 +1585,21 @@ static void snd_cs46xx_pcm_free(snd_pcm_ #ifdef CONFIG_SND_CS46XX_NEW_DSP static void snd_cs46xx_pcm_rear_free(snd_pcm_t *pcm) { - cs46xx_t *chip = snd_magic_cast(cs46xx_t, pcm->private_data, return); + cs46xx_t *chip = pcm->private_data; chip->pcm_rear = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } static void snd_cs46xx_pcm_center_lfe_free(snd_pcm_t *pcm) { - cs46xx_t *chip = snd_magic_cast(cs46xx_t, pcm->private_data, return); + cs46xx_t *chip = pcm->private_data; chip->pcm_center_lfe = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } static void snd_cs46xx_pcm_iec958_free(snd_pcm_t *pcm) { - cs46xx_t *chip = snd_magic_cast(cs46xx_t, pcm->private_data, return); + cs46xx_t *chip = pcm->private_data; chip->pcm_iec958 = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -1824,14 +1737,14 @@ int __devinit snd_cs46xx_pcm_iec958(cs46 */ static void snd_cs46xx_mixer_free_ac97_bus(ac97_bus_t *bus) { - cs46xx_t *chip = snd_magic_cast(cs46xx_t, bus->private_data, return); + cs46xx_t *chip = bus->private_data; chip->ac97_bus = NULL; } static void snd_cs46xx_mixer_free_ac97(ac97_t *ac97) { - cs46xx_t *chip = snd_magic_cast(cs46xx_t, ac97->private_data, return); + cs46xx_t *chip = ac97->private_data; snd_assert ((ac97 == chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]) || (ac97 == chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]), @@ -2420,7 +2333,7 @@ static void snd_cs46xx_codec_reset (ac97 { unsigned long end_time; int err; - cs46xx_t * chip = snd_magic_cast(cs46xx_t,ac97->private_data,return /* -ENXIO */); + cs46xx_t * chip = ac97->private_data; /* reset to defaults */ snd_ac97_write(ac97, AC97_RESET, 0); @@ -2470,39 +2383,38 @@ static void snd_cs46xx_codec_reset (ac97 int __devinit snd_cs46xx_mixer(cs46xx_t *chip) { snd_card_t *card = chip->card; - ac97_bus_t bus; - ac97_t ac97; + ac97_template_t ac97; snd_ctl_elem_id_t id; int err; unsigned int idx; + static ac97_bus_ops_t ops = { +#ifdef CONFIG_SND_CS46XX_NEW_DSP + .reset = snd_cs46xx_codec_reset, +#endif + .write = snd_cs46xx_ac97_write, + .read = snd_cs46xx_ac97_read, + }; /* detect primary codec */ chip->nr_ac97_codecs = 0; snd_printdd("snd_cs46xx: detecting primary codec\n"); - memset(&bus, 0, sizeof(bus)); - bus.write = snd_cs46xx_ac97_write; - bus.read = snd_cs46xx_ac97_read; -#ifdef CONFIG_SND_CS46XX_NEW_DSP - bus.reset = snd_cs46xx_codec_reset; -#endif - bus.private_data = chip; - bus.private_free = snd_cs46xx_mixer_free_ac97_bus; - if ((err = snd_ac97_bus(card, &bus, &chip->ac97_bus)) < 0) + if ((err = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus)) < 0) return err; + chip->ac97_bus->private_free = snd_cs46xx_mixer_free_ac97_bus; memset(&ac97, 0, sizeof(ac97)); ac97.private_data = chip; ac97.private_free = snd_cs46xx_mixer_free_ac97; - chip->ac97[CS46XX_PRIMARY_CODEC_INDEX] = &ac97; - snd_cs46xx_ac97_write(&ac97, AC97_MASTER, 0x8000); + snd_cs46xx_codec_write(chip, AC97_MASTER, 0x8000, + CS46XX_PRIMARY_CODEC_INDEX); for (idx = 0; idx < 100; ++idx) { - if (snd_cs46xx_ac97_read(&ac97, AC97_MASTER) == 0x8000) + if (snd_cs46xx_codec_read(chip, AC97_MASTER, + CS46XX_PRIMARY_CODEC_INDEX) == 0x8000) goto _ok; set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(HZ/100); } - chip->ac97[CS46XX_PRIMARY_CODEC_INDEX] = NULL; return -ENXIO; _ok: @@ -2519,18 +2431,21 @@ int __devinit snd_cs46xx_mixer(cs46xx_t ac97.private_free = snd_cs46xx_mixer_free_ac97; ac97.num = CS46XX_SECONDARY_CODEC_INDEX; - snd_cs46xx_ac97_write(&ac97, AC97_RESET, 0); + snd_cs46xx_codec_write(chip, AC97_RESET, 0, + CS46XX_SECONDARY_CODEC_INDEX); udelay(10); - if (snd_cs46xx_ac97_read(&ac97, AC97_RESET) & 0x8000) { + if (snd_cs46xx_codec_read(chip, AC97_RESET, + CS46XX_SECONDARY_CODEC_INDEX) & 0x8000) { snd_printdd("snd_cs46xx: seconadry codec not present\n"); goto _no_sec_codec; } - chip->ac97[CS46XX_SECONDARY_CODEC_INDEX] = &ac97; - snd_cs46xx_ac97_write(&ac97, AC97_MASTER, 0x8000); + snd_cs46xx_codec_write(chip, AC97_MASTER, 0x8000, + CS46XX_SECONDARY_CODEC_INDEX); for (idx = 0; idx < 100; ++idx) { - if (snd_cs46xx_ac97_read(&ac97, AC97_MASTER) == 0x8000) { + if (snd_cs46xx_codec_read(chip, AC97_MASTER, + CS46XX_SECONDARY_CODEC_INDEX) == 0x8000) { goto _ok2; } set_current_state(TASK_INTERRUPTIBLE); @@ -2540,7 +2455,6 @@ int __devinit snd_cs46xx_mixer(cs46xx_t _no_sec_codec: snd_printdd("snd_cs46xx: secondary codec did not respond ...\n"); - chip->ac97[CS46XX_SECONDARY_CODEC_INDEX] = NULL; chip->nr_ac97_codecs = 1; /* well, one codec only ... */ @@ -2605,11 +2519,10 @@ static void snd_cs46xx_midi_reset(cs46xx static int snd_cs46xx_midi_input_open(snd_rawmidi_substream_t * substream) { - unsigned long flags; - cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return -ENXIO); + cs46xx_t *chip = substream->rmidi->private_data; chip->active_ctrl(chip, 1); - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); chip->uartm |= CS46XX_MODE_INPUT; chip->midcr |= MIDCR_RXE; chip->midi_input = substream; @@ -2618,16 +2531,15 @@ static int snd_cs46xx_midi_input_open(sn } else { snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); } - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return 0; } static int snd_cs46xx_midi_input_close(snd_rawmidi_substream_t * substream) { - unsigned long flags; - cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return -ENXIO); + cs46xx_t *chip = substream->rmidi->private_data; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); chip->midcr &= ~(MIDCR_RXE | MIDCR_RIE); chip->midi_input = NULL; if (!(chip->uartm & CS46XX_MODE_OUTPUT)) { @@ -2636,19 +2548,18 @@ static int snd_cs46xx_midi_input_close(s snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); } chip->uartm &= ~CS46XX_MODE_INPUT; - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); chip->active_ctrl(chip, -1); return 0; } static int snd_cs46xx_midi_output_open(snd_rawmidi_substream_t * substream) { - unsigned long flags; - cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return -ENXIO); + cs46xx_t *chip = substream->rmidi->private_data; chip->active_ctrl(chip, 1); - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); chip->uartm |= CS46XX_MODE_OUTPUT; chip->midcr |= MIDCR_TXE; chip->midi_output = substream; @@ -2657,16 +2568,15 @@ static int snd_cs46xx_midi_output_open(s } else { snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); } - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return 0; } static int snd_cs46xx_midi_output_close(snd_rawmidi_substream_t * substream) { - unsigned long flags; - cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return -ENXIO); + cs46xx_t *chip = substream->rmidi->private_data; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); chip->midcr &= ~(MIDCR_TXE | MIDCR_TIE); chip->midi_output = NULL; if (!(chip->uartm & CS46XX_MODE_INPUT)) { @@ -2675,7 +2585,7 @@ static int snd_cs46xx_midi_output_close( snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr); } chip->uartm &= ~CS46XX_MODE_OUTPUT; - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); chip->active_ctrl(chip, -1); return 0; } @@ -2683,7 +2593,7 @@ static int snd_cs46xx_midi_output_close( static void snd_cs46xx_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) { unsigned long flags; - cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return); + cs46xx_t *chip = substream->rmidi->private_data; spin_lock_irqsave(&chip->reg_lock, flags); if (up) { @@ -2703,7 +2613,7 @@ static void snd_cs46xx_midi_input_trigge static void snd_cs46xx_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) { unsigned long flags; - cs46xx_t *chip = snd_magic_cast(cs46xx_t, substream->rmidi->private_data, return); + cs46xx_t *chip = substream->rmidi->private_data; unsigned char byte; spin_lock_irqsave(&chip->reg_lock, flags); @@ -2781,7 +2691,7 @@ static void snd_cs46xx_gameport_trigger( cs46xx_gameport_t *gp = (cs46xx_gameport_t *)gameport; cs46xx_t *chip; snd_assert(gp, return); - chip = snd_magic_cast(cs46xx_t, gp->chip, return); + chip = gp->chip; snd_cs46xx_pokeBA0(chip, BA0_JSPT, 0xFF); //outb(gameport->io, 0xFF); } @@ -2790,7 +2700,7 @@ static unsigned char snd_cs46xx_gameport cs46xx_gameport_t *gp = (cs46xx_gameport_t *)gameport; cs46xx_t *chip; snd_assert(gp, return 0); - chip = snd_magic_cast(cs46xx_t, gp->chip, return 0); + chip = gp->chip; return snd_cs46xx_peekBA0(chip, BA0_JSPT); //inb(gameport->io); } @@ -2801,7 +2711,7 @@ static int snd_cs46xx_gameport_cooked_re unsigned js1, js2, jst; snd_assert(gp, return 0); - chip = snd_magic_cast(cs46xx_t, gp->chip, return 0); + chip = gp->chip; js1 = snd_cs46xx_peekBA0(chip, BA0_JSC1); js2 = snd_cs46xx_peekBA0(chip, BA0_JSC2); @@ -3011,13 +2921,13 @@ static int snd_cs46xx_free(cs46xx_t *chi } #endif - snd_magic_kfree(chip); + kfree(chip); return 0; } static int snd_cs46xx_dev_free(snd_device_t *device) { - cs46xx_t *chip = snd_magic_cast(cs46xx_t, device->device_data, return -ENXIO); + cs46xx_t *chip = device->device_data; return snd_cs46xx_free(chip); } @@ -3786,7 +3696,7 @@ static struct cs_card_type __devinitdata #ifdef CONFIG_PM static int snd_cs46xx_suspend(snd_card_t *card, unsigned int state) { - cs46xx_t *chip = snd_magic_cast(cs46xx_t, card->pm_private_data, return -EINVAL); + cs46xx_t *chip = card->pm_private_data; int amp_saved; snd_pcm_suspend_all(chip->pcm); @@ -3810,7 +3720,7 @@ static int snd_cs46xx_suspend(snd_card_t static int snd_cs46xx_resume(snd_card_t *card, unsigned int state) { - cs46xx_t *chip = snd_magic_cast(cs46xx_t, card->pm_private_data, return -EINVAL); + cs46xx_t *chip = card->pm_private_data; int amp_saved; pci_enable_device(chip->pci); @@ -3869,7 +3779,7 @@ int __devinit snd_cs46xx_create(snd_card if ((err = pci_enable_device(pci)) < 0) return err; - chip = snd_magic_kcalloc(cs46xx_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; spin_lock_init(&chip->reg_lock); @@ -3913,10 +3823,6 @@ int __devinit snd_cs46xx_create(snd_card region->base = chip->ba1_addr + BA1_SP_REG; region->size = CS46XX_BA1_REG_SIZE; - memset(&chip->dma_dev, 0, sizeof(chip->dma_dev)); - chip->dma_dev.type = SNDRV_DMA_TYPE_DEV; - chip->dma_dev.dev = snd_dma_pci_data(pci); - /* set up amp and clkrun hack */ pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &ss_vendor); pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &ss_card); --- linux-2.6.9-rc1/sound/pci/cs46xx/cs46xx_lib.h 2004-08-24 00:01:55.000000000 -0700 +++ 25/sound/pci/cs46xx/cs46xx_lib.h 2004-09-02 20:51:53.000000000 -0700 @@ -22,8 +22,6 @@ #ifndef __CS46XX_LIB_H__ #define __CS46XX_LIB_H__ -#define chip_t cs46xx_t - /* * constants */ --- linux-2.6.9-rc1/sound/pci/cs46xx/dsp_spos.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/sound/pci/cs46xx/dsp_spos.c 2004-09-02 20:51:53.000000000 -0700 @@ -74,7 +74,7 @@ static int shadow_and_reallocate_code (c (mop_operands & WIDE_LADD_INSTR_MASK) == 0 && (mop_operands & WIDE_INSTR_MASK) != 0) { wide_op = loval & 0x7f; - for (j = 0;j < sizeof(wide_opcodes) / sizeof(wide_opcode_t); ++j) { + for (j = 0;j < ARRAY_SIZE(wide_opcodes); ++j) { if (wide_opcodes[j] == wide_op) { /* need to reallocate instruction */ address = (hival & 0x00FFF) << 5; @@ -462,7 +462,7 @@ symbol_entry_t * cs46xx_dsp_lookup_symbo static void cs46xx_dsp_proc_symbol_table_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer) { - cs46xx_t *chip = snd_magic_cast(cs46xx_t, entry->private_data, return); + cs46xx_t *chip = entry->private_data; dsp_spos_instance_t * ins = chip->dsp_spos_instance; int i; @@ -489,7 +489,7 @@ static void cs46xx_dsp_proc_symbol_table static void cs46xx_dsp_proc_modules_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer) { - cs46xx_t *chip = snd_magic_cast(cs46xx_t, entry->private_data, return); + cs46xx_t *chip = entry->private_data; dsp_spos_instance_t * ins = chip->dsp_spos_instance; int i,j; @@ -511,7 +511,7 @@ static void cs46xx_dsp_proc_modules_read static void cs46xx_dsp_proc_task_tree_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer) { - cs46xx_t *chip = snd_magic_cast(cs46xx_t, entry->private_data, return); + cs46xx_t *chip = entry->private_data; dsp_spos_instance_t * ins = chip->dsp_spos_instance; int i,j,col; unsigned long dst = chip->region.idx[1].remap_addr + DSP_PARAMETER_BYTE_OFFSET; @@ -538,7 +538,7 @@ static void cs46xx_dsp_proc_task_tree_re static void cs46xx_dsp_proc_scb_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer) { - cs46xx_t *chip = snd_magic_cast(cs46xx_t, entry->private_data, return); + cs46xx_t *chip = entry->private_data; dsp_spos_instance_t * ins = chip->dsp_spos_instance; int i; @@ -570,7 +570,7 @@ static void cs46xx_dsp_proc_scb_read (sn static void cs46xx_dsp_proc_parameter_dump_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer) { - cs46xx_t *chip = snd_magic_cast(cs46xx_t, entry->private_data, return); + cs46xx_t *chip = entry->private_data; /*dsp_spos_instance_t * ins = chip->dsp_spos_instance; */ unsigned int i,col = 0; unsigned long dst = chip->region.idx[1].remap_addr + DSP_PARAMETER_BYTE_OFFSET; @@ -597,7 +597,7 @@ static void cs46xx_dsp_proc_parameter_du static void cs46xx_dsp_proc_sample_dump_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer) { - cs46xx_t *chip = snd_magic_cast(cs46xx_t, entry->private_data, return); + cs46xx_t *chip = entry->private_data; int i,col = 0; unsigned long dst = chip->region.idx[2].remap_addr; @@ -1057,7 +1057,7 @@ int cs46xx_dsp_scb_and_task_init (cs46xx int fifo_addr,fifo_span,valid_slots; - spos_control_block_t sposcb = { + static spos_control_block_t sposcb = { /* 0 */ HFG_TREE_SCB,HFG_STACK, /* 1 */ SPOSCB_ADDR,BG_TREE_SCB_ADDR, /* 2 */ DSP_SPOS_DC,0, @@ -1110,18 +1110,18 @@ int cs46xx_dsp_scb_and_task_init (cs46xx { /* create the null SCB */ - generic_scb_t null_scb = { + static generic_scb_t null_scb = { { 0, 0, 0, 0 }, { 0, 0, 0, 0, 0 }, NULL_SCB_ADDR, NULL_SCB_ADDR, - null_algorithm->address, 0, - 0,0,0, + 0, 0, 0, 0, 0, { 0,0, 0,0, } }; + null_scb.entry_point = null_algorithm->address; ins->the_null_scb = cs46xx_dsp_create_scb(chip, "nullSCB", (u32 *)&null_scb, NULL_SCB_ADDR); ins->the_null_scb->task_entry = null_algorithm; ins->the_null_scb->sub_list_ptr = ins->the_null_scb; @@ -1132,7 +1132,7 @@ int cs46xx_dsp_scb_and_task_init (cs46xx { /* setup foreground task tree */ - task_tree_control_block_t fg_task_tree_hdr = { + static task_tree_control_block_t fg_task_tree_hdr = { { FG_TASK_HEADER_ADDR | (DSP_SPOS_DC << 0x10), DSP_SPOS_DC_DC, DSP_SPOS_DC_DC, @@ -1145,7 +1145,7 @@ int cs46xx_dsp_scb_and_task_init (cs46xx { BG_TREE_SCB_ADDR,TIMINGMASTER_SCB_ADDR, - fg_task_tree_header_code->address, + 0, FG_TASK_HEADER_ADDR + TCBData, }, @@ -1158,7 +1158,7 @@ int cs46xx_dsp_scb_and_task_init (cs46xx }, { - DSP_SPOS_DC,task_tree_thread->address, + DSP_SPOS_DC,0, DSP_SPOS_DC,DSP_SPOS_DC, DSP_SPOS_DC,DSP_SPOS_DC, DSP_SPOS_DC,DSP_SPOS_DC, @@ -1200,13 +1200,15 @@ int cs46xx_dsp_scb_and_task_init (cs46xx } }; + fg_task_tree_hdr.links.entry_point = fg_task_tree_header_code->address; + fg_task_tree_hdr.context_blk.stack0 = task_tree_thread->address; cs46xx_dsp_create_task_tree(chip,"FGtaskTreeHdr",(u32 *)&fg_task_tree_hdr,FG_TASK_HEADER_ADDR,0x35); } { /* setup foreground task tree */ - task_tree_control_block_t bg_task_tree_hdr = { + static task_tree_control_block_t bg_task_tree_hdr = { { DSP_SPOS_DC_DC, DSP_SPOS_DC_DC, DSP_SPOS_DC_DC, @@ -1219,7 +1221,7 @@ int cs46xx_dsp_scb_and_task_init (cs46xx { NULL_SCB_ADDR,NULL_SCB_ADDR, /* Set up the background to do nothing */ - task_tree_header_code->address, + 0, BG_TREE_SCB_ADDR + TCBData, }, @@ -1232,7 +1234,7 @@ int cs46xx_dsp_scb_and_task_init (cs46xx }, { - DSP_SPOS_DC,task_tree_thread->address, + DSP_SPOS_DC,0, DSP_SPOS_DC,DSP_SPOS_DC, DSP_SPOS_DC,DSP_SPOS_DC, DSP_SPOS_DC,DSP_SPOS_DC, @@ -1273,6 +1275,9 @@ int cs46xx_dsp_scb_and_task_init (cs46xx 0,0 } }; + + bg_task_tree_hdr.links.entry_point = task_tree_header_code->address; + bg_task_tree_hdr.context_blk.stack0 = task_tree_thread->address; cs46xx_dsp_create_task_tree(chip,"BGtaskTreeHdr",(u32 *)&bg_task_tree_hdr,BG_TREE_SCB_ADDR,0x35); } @@ -1312,7 +1317,7 @@ int cs46xx_dsp_scb_and_task_init (cs46xx if (!write_back_scb) goto _fail_end; { - mix2_ostream_spb_t mix2_ostream_spb = { + static mix2_ostream_spb_t mix2_ostream_spb = { 0x00020000, 0x0000ffff }; --- linux-2.6.9-rc1/sound/pci/cs46xx/dsp_spos_scb_lib.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/sound/pci/cs46xx/dsp_spos_scb_lib.c 2004-09-02 20:51:53.000000000 -0700 @@ -69,7 +69,7 @@ static void cs46xx_dsp_proc_scb_info_rea proc_scb_info_t * scb_info = (proc_scb_info_t *)entry->private_data; dsp_scb_descriptor_t * scb = scb_info->scb_desc; dsp_spos_instance_t * ins; - cs46xx_t *chip = snd_magic_cast(cs46xx_t, scb_info->chip, return); + cs46xx_t *chip = scb_info->chip; int j,col; unsigned long dst = chip->region.idx[1].remap_addr + DSP_PARAMETER_BYTE_OFFSET; --- linux-2.6.9-rc1/sound/pci/emu10k1/emu10k1.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/sound/pci/emu10k1/emu10k1.c 2004-09-02 20:51:53.000000000 -0700 @@ -31,8 +31,7 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("EMU10K1"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Creative Labs,SB Live!/PCI512/E-mu APS}," +MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB Live!/PCI512/E-mu APS}," "{Creative Labs,SB Audigy}}"); #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) @@ -53,37 +52,25 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for the EMU10K1 soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for the EMU10K1 soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable the EMU10K1 soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(extin, int, boot_devs, 0444); MODULE_PARM_DESC(extin, "Available external inputs for FX8010. Zero=default."); -MODULE_PARM_SYNTAX(extin, SNDRV_ENABLED "allows:{{0,0x0ffff}},base:16"); module_param_array(extout, int, boot_devs, 0444); MODULE_PARM_DESC(extout, "Available external outputs for FX8010. Zero=default."); -MODULE_PARM_SYNTAX(extout, SNDRV_ENABLED "allows:{{0,0x0ffff}},base:16"); module_param_array(seq_ports, int, boot_devs, 0444); MODULE_PARM_DESC(seq_ports, "Allocated sequencer ports for internal synthesizer."); -MODULE_PARM_SYNTAX(seq_ports, SNDRV_ENABLED "allows:{{0,32}}"); module_param_array(max_synth_voices, int, boot_devs, 0444); MODULE_PARM_DESC(max_synth_voices, "Maximum number of voices for WaveTable."); -MODULE_PARM_SYNTAX(max_synth_voices, SNDRV_ENABLED); module_param_array(max_buffer_size, int, boot_devs, 0444); MODULE_PARM_DESC(max_buffer_size, "Maximum sample buffer size in MB."); -MODULE_PARM_SYNTAX(max_buffer_size, SNDRV_ENABLED); module_param_array(enable_ir, bool, boot_devs, 0444); MODULE_PARM_DESC(enable_ir, "Enable IR."); -MODULE_PARM_SYNTAX(enable_ir, SNDRV_ENABLE_DESC); static struct pci_device_id snd_emu10k1_ids[] = { { 0x1102, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* EMU10K1 */ -#if 0 /* FIXME: not working! */ - { 0x1102, 0x0006, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* Dell OEM version (EMU10K1) */ -#endif { 0x1102, 0x0004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, /* Audigy */ { 0, } }; @@ -134,10 +121,6 @@ static int __devinit snd_card_emu10k1_pr snd_card_free(card); return err; } - if ((err = snd_emu10k1_fx8010_pcm(emu, 3, NULL)) < 0) { - snd_card_free(card); - return err; - } if ((err = snd_emu10k1_mixer(emu)) < 0) { snd_card_free(card); return err; --- linux-2.6.9-rc1/sound/pci/emu10k1/emu10k1_callback.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/sound/pci/emu10k1/emu10k1_callback.c 2004-09-02 20:51:53.000000000 -0700 @@ -93,7 +93,7 @@ snd_emu10k1_synth_get_voice(emu10k1_t *h unsigned long flags; int i; - emu = snd_magic_cast(snd_emux_t, hw->synth, return -EINVAL); + emu = hw->synth; spin_lock_irqsave(&emu->voice_lock, flags); lookup_voices(emu, hw, best, 1); /* no OFF voices */ @@ -128,7 +128,7 @@ release_voice(snd_emux_voice_t *vp) int dcysusv; emu10k1_t *hw; - hw = snd_magic_cast(emu10k1_t, vp->hw, return); + hw = vp->hw; dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease; snd_emu10k1_ptr_write(hw, DCYSUSM, vp->ch, dcysusv); dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease | DCYSUSV_CHANNELENABLE_MASK; @@ -145,7 +145,7 @@ terminate_voice(snd_emux_voice_t *vp) emu10k1_t *hw; snd_assert(vp, return); - hw = snd_magic_cast(emu10k1_t, vp->hw, return); + hw = vp->hw; snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK); if (vp->block) { emu10k1_memblk_t *emem; @@ -163,7 +163,7 @@ free_voice(snd_emux_voice_t *vp) { emu10k1_t *hw; - hw = snd_magic_cast(emu10k1_t, vp->hw, return); + hw = vp->hw; if (vp->ch >= 0) { snd_emu10k1_ptr_write(hw, IFATN, vp->ch, 0xff00); snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK); @@ -185,7 +185,7 @@ update_voice(snd_emux_voice_t *vp, int u { emu10k1_t *hw; - hw = snd_magic_cast(emu10k1_t, vp->hw, return); + hw = vp->hw; if (update & SNDRV_EMUX_UPDATE_VOLUME) snd_emu10k1_ptr_write(hw, IFATN_ATTENUATION, vp->ch, vp->avol); if (update & SNDRV_EMUX_UPDATE_PITCH) @@ -282,7 +282,7 @@ get_voice(snd_emux_t *emu, snd_emux_port best_voice_t best[V_END]; int i; - hw = snd_magic_cast(emu10k1_t, emu->hw, return NULL); + hw = emu->hw; lookup_voices(emu, hw, best, 0); for (i = 0; i < V_END; i++) { @@ -317,7 +317,7 @@ start_voice(snd_emux_voice_t *vp) emu10k1_t *hw; emu10k1_memblk_t *emem; - hw = snd_magic_cast(emu10k1_t, vp->hw, return -EINVAL); + hw = vp->hw; ch = vp->ch; snd_assert(ch >= 0, return -EINVAL); chan = vp->chan; @@ -469,7 +469,7 @@ trigger_voice(snd_emux_voice_t *vp) emu10k1_t *hw; emu10k1_memblk_t *emem; - hw = snd_magic_cast(emu10k1_t, vp->hw, return); + hw = vp->hw; emem = (emu10k1_memblk_t *)vp->block; if (! emem || emem->mapped_page < 0) --- linux-2.6.9-rc1/sound/pci/emu10k1/emu10k1_main.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/sound/pci/emu10k1/emu10k1_main.c 2004-09-02 20:51:53.000000000 -0700 @@ -119,8 +119,10 @@ static int __devinit snd_emu10k1_init(em snd_emu10k1_ptr_write(emu, SOLEH, 0, 0); if (emu->audigy){ - snd_emu10k1_ptr_write(emu, 0x5e, 0, 0xf00); /* ?? */ - snd_emu10k1_ptr_write(emu, 0x5f, 0, 0x3); /* ?? */ + /* set SPDIF bypass mode */ + snd_emu10k1_ptr_write(emu, SPBYPASS, 0, SPBYPASS_FORMAT); + /* enable rear left + rear right AC97 slots */ + snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_REAR_RIGHT | AC97SLOT_REAR_LEFT); } /* init envelope engine */ @@ -329,7 +331,7 @@ static int snd_emu10k1_done(emu10k1_t * if (emu->audigy) snd_emu10k1_ptr_write(emu, A_DBG, 0, A_DBG_SINGLE_STEP); else - snd_emu10k1_ptr_write(emu, DBG, 0, 0x8000); + snd_emu10k1_ptr_write(emu, DBG, 0, EMU10K1_DBG_SINGLE_STEP); /* disable channel interrupt */ snd_emu10k1_ptr_write(emu, CLIEL, 0, 0); @@ -541,33 +543,31 @@ static int __devinit snd_emu10k1_ecard_i static int snd_emu10k1_free(emu10k1_t *emu) { - if (emu->res_port != NULL) { /* avoid access to already used hardware */ + if (emu->port) { /* avoid access to already used hardware */ snd_emu10k1_fx8010_tram_setup(emu, 0); snd_emu10k1_done(emu); } if (emu->memhdr) snd_util_memhdr_free(emu->memhdr); if (emu->silent_page.area) - snd_dma_free_pages(&emu->dma_dev, &emu->silent_page); + snd_dma_free_pages(&emu->silent_page); if (emu->ptb_pages.area) - snd_dma_free_pages(&emu->dma_dev, &emu->ptb_pages); + snd_dma_free_pages(&emu->ptb_pages); if (emu->page_ptr_table) vfree(emu->page_ptr_table); if (emu->page_addr_table) vfree(emu->page_addr_table); - if (emu->res_port) { - release_resource(emu->res_port); - kfree_nocheck(emu->res_port); - } if (emu->irq >= 0) free_irq(emu->irq, (void *)emu); - snd_magic_kfree(emu); + if (emu->port) + pci_release_regions(emu->pci); + kfree(emu); return 0; } static int snd_emu10k1_dev_free(snd_device_t *device) { - emu10k1_t *emu = snd_magic_cast(emu10k1_t, device->device_data, return -ENXIO); + emu10k1_t *emu = device->device_data; return snd_emu10k1_free(emu); } @@ -595,7 +595,7 @@ int __devinit snd_emu10k1_create(snd_car if ((err = pci_enable_device(pci)) < 0) return err; - emu = snd_magic_kcalloc(emu10k1_t, 0, GFP_KERNEL); + emu = kcalloc(1, sizeof(*emu), GFP_KERNEL); if (emu == NULL) return -ENOMEM; /* set the DMA transfer mask */ @@ -603,7 +603,7 @@ int __devinit snd_emu10k1_create(snd_car if (pci_set_dma_mask(pci, emu->dma_mask) < 0 || pci_set_consistent_dma_mask(pci, emu->dma_mask) < 0) { snd_printk(KERN_ERR "architecture does not support PCI busmaster DMA with mask 0x%lx\n", emu->dma_mask); - snd_magic_kfree(emu); + kfree(emu); return -ENXIO; } emu->card = card; @@ -620,7 +620,6 @@ int __devinit snd_emu10k1_create(snd_car emu->irq = -1; emu->synth = NULL; emu->get_synth_voice = NULL; - emu->port = pci_resource_start(pci, 0); emu->audigy = is_audigy; if (is_audigy) @@ -628,10 +627,11 @@ int __devinit snd_emu10k1_create(snd_car else emu->gpr_base = FXGPREGBASE; - if ((emu->res_port = request_region(emu->port, 0x20, "EMU10K1")) == NULL) { - snd_emu10k1_free(emu); - return -EBUSY; + if ((err = pci_request_regions(pci, "EMU10K1")) < 0) { + kfree(emu); + return err; } + emu->port = pci_resource_start(pci, 0); if (request_irq(pci->irq, snd_emu10k1_interrupt, SA_INTERRUPT|SA_SHIRQ, "EMU10K1", (void *)emu)) { snd_emu10k1_free(emu); @@ -639,12 +639,9 @@ int __devinit snd_emu10k1_create(snd_car } emu->irq = pci->irq; - memset(&emu->dma_dev, 0, sizeof(emu->dma_dev)); - emu->dma_dev.type = SNDRV_DMA_TYPE_DEV; - emu->dma_dev.dev = snd_dma_pci_data(pci); - emu->max_cache_pages = max_cache_bytes >> PAGE_SHIFT; - if (snd_dma_alloc_pages(&emu->dma_dev, 32 * 1024, &emu->ptb_pages) < 0) { + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), + 32 * 1024, &emu->ptb_pages) < 0) { snd_emu10k1_free(emu); return -ENOMEM; } @@ -656,7 +653,8 @@ int __devinit snd_emu10k1_create(snd_car return -ENOMEM; } - if (snd_dma_alloc_pages(&emu->dma_dev, EMUPAGESIZE, &emu->silent_page) < 0) { + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), + EMUPAGESIZE, &emu->silent_page) < 0) { snd_emu10k1_free(emu); return -ENOMEM; } @@ -682,10 +680,17 @@ int __devinit snd_emu10k1_create(snd_car /* Audigy 2 EX has apparently no effective AC97 controls * (for both input and output), so we skip the AC97 detections */ - snd_printdd(KERN_INFO "Audigy2 EX is detected. skpping ac97.\n"); - emu->no_ac97 = 1; + snd_printdd(KERN_INFO "Audigy2 EX is detected. skipping ac97.\n"); + emu->no_ac97 = 1; } + if (emu->revision == 4 && emu->model == 0x2002) { + /* Audigy 2 ZS */ + snd_printdd(KERN_INFO "Audigy2 ZS is detected. setting 7.1 mode.\n"); + emu->spk71 = 1; + } + + emu->fx8010.fxbus_mask = 0x303f; if (extin_mask == 0) extin_mask = 0x3fcf; --- linux-2.6.9-rc1/sound/pci/emu10k1/emu10k1_patch.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/sound/pci/emu10k1/emu10k1_patch.c 2004-09-02 20:51:53.000000000 -0700 @@ -44,7 +44,7 @@ snd_emu10k1_sample_new(snd_emux_t *rec, unsigned int start_addr; emu10k1_t *emu; - emu = snd_magic_cast(emu10k1_t, rec->hw, return -ENXIO); + emu = rec->hw; snd_assert(sp != NULL, return -EINVAL); snd_assert(hdr != NULL, return -EINVAL); @@ -210,7 +210,7 @@ snd_emu10k1_sample_free(snd_emux_t *rec, { emu10k1_t *emu; - emu = snd_magic_cast(emu10k1_t, rec->hw, return -ENXIO); + emu = rec->hw; snd_assert(sp != NULL, return -EINVAL); snd_assert(hdr != NULL, return -EINVAL); --- linux-2.6.9-rc1/sound/pci/emu10k1/emu10k1_synth.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/sound/pci/emu10k1/emu10k1_synth.c 2004-09-02 20:51:53.000000000 -0700 @@ -85,9 +85,9 @@ int snd_emu10k1_synth_delete_device(snd_ if (dev->driver_data == NULL) return 0; /* not registered actually */ - emu = snd_magic_cast(snd_emux_t, dev->driver_data, return -EINVAL); + emu = dev->driver_data; - hw = snd_magic_cast(emu10k1_t, emu->hw, return -EINVAL); + hw = emu->hw; spin_lock_irqsave(&hw->voice_lock, flags); hw->synth = NULL; hw->get_synth_voice = NULL; --- linux-2.6.9-rc1/sound/pci/emu10k1/emufx.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/sound/pci/emu10k1/emufx.c 2004-09-02 20:51:53.000000000 -0700 @@ -33,8 +33,6 @@ #include #include -#define chip_t emu10k1_t - #if 0 /* for testing purposes - digital out -> capture */ #define EMU10K1_CAPTURE_DIGITAL_OUT #endif @@ -405,7 +403,7 @@ static void snd_emu10k1_fx8010_interrupt } } -static int snd_emu10k1_fx8010_register_irq_handler(emu10k1_t *emu, +int snd_emu10k1_fx8010_register_irq_handler(emu10k1_t *emu, snd_fx8010_irq_handler_t *handler, unsigned char gpr_running, void *private_data, @@ -438,7 +436,7 @@ static int snd_emu10k1_fx8010_register_i return 0; } -static int snd_emu10k1_fx8010_unregister_irq_handler(emu10k1_t *emu, +int snd_emu10k1_fx8010_unregister_irq_handler(emu10k1_t *emu, snd_emu10k1_fx8010_irq_t *irq) { snd_emu10k1_fx8010_irq_t *tmp; @@ -463,312 +461,6 @@ static int snd_emu10k1_fx8010_unregister return 0; } -/* - * PCM streams - */ - -#define INITIAL_TRAM_SHIFT 14 -#define INITIAL_TRAM_POS(size) ((((size) / 2) - INITIAL_TRAM_SHIFT) - 1) - -static void snd_emu10k1_fx8010_playback_irq(emu10k1_t *emu, void *private_data) -{ - snd_pcm_substream_t *substream = snd_magic_cast(snd_pcm_substream_t, private_data, return); - snd_pcm_period_elapsed(substream); -} - -static void snd_emu10k1_fx8010_playback_tram_poke1(unsigned short *dst_left, - unsigned short *dst_right, - unsigned short *src, - unsigned int count, - unsigned int tram_shift) -{ - // printk("tram_poke1: dst_left = 0x%p, dst_right = 0x%p, src = 0x%p, count = 0x%x\n", dst_left, dst_right, src, count); - if ((tram_shift & 1) == 0) { - while (count--) { - *dst_left-- = *src++; - *dst_right-- = *src++; - } - } else { - while (count--) { - *dst_right-- = *src++; - *dst_left-- = *src++; - } - } -} - -static void snd_emu10k1_fx8010_playback_tram_poke(emu10k1_t *emu, - unsigned int *tram_pos, - unsigned int *tram_shift, - unsigned int tram_size, - unsigned short *src, - unsigned int frames) -{ - unsigned int count; - - while (frames > *tram_pos) { - count = *tram_pos + 1; - snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages.area + *tram_pos, - (unsigned short *)emu->fx8010.etram_pages.area + *tram_pos + tram_size / 2, - src, count, *tram_shift); - src += count * 2; - frames -= count; - *tram_pos = (tram_size / 2) - 1; - (*tram_shift)++; - } - snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages.area + *tram_pos, - (unsigned short *)emu->fx8010.etram_pages.area + *tram_pos + tram_size / 2, - src, frames, *tram_shift++); - *tram_pos -= frames; -} - -static int snd_emu10k1_fx8010_playback_transfer(snd_pcm_substream_t *substream) -{ - emu10k1_t *emu = snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; - snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; - snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr; - snd_pcm_sframes_t diff = appl_ptr - pcm->appl_ptr; - snd_pcm_uframes_t buffer_size = pcm->buffer_size / 2; - - if (diff) { - if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) - diff += runtime->boundary; - pcm->sw_ready += diff; - pcm->appl_ptr = appl_ptr; - } - while (pcm->hw_ready < buffer_size && - pcm->sw_ready > 0) { - size_t hw_to_end = buffer_size - pcm->hw_data; - size_t sw_to_end = (runtime->buffer_size << 2) - pcm->sw_data; - size_t tframes = buffer_size - pcm->hw_ready; - if (pcm->sw_ready < tframes) - tframes = pcm->sw_ready; - if (hw_to_end < tframes) - tframes = hw_to_end; - if (sw_to_end < tframes) - tframes = sw_to_end; - snd_emu10k1_fx8010_playback_tram_poke(emu, &pcm->tram_pos, &pcm->tram_shift, - pcm->buffer_size, - (unsigned short *)(runtime->dma_area + (pcm->sw_data << 2)), - tframes); - pcm->hw_data += tframes; - if (pcm->hw_data == buffer_size) - pcm->hw_data = 0; - pcm->sw_data += tframes; - if (pcm->sw_data == runtime->buffer_size) - pcm->sw_data = 0; - pcm->hw_ready += tframes; - pcm->sw_ready -= tframes; - } - return 0; -} - -static int snd_emu10k1_fx8010_playback_hw_params(snd_pcm_substream_t * substream, - snd_pcm_hw_params_t * hw_params) -{ - return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); -} - -static int snd_emu10k1_fx8010_playback_hw_free(snd_pcm_substream_t * substream) -{ - emu10k1_t *emu = snd_pcm_substream_chip(substream); - snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; - unsigned int i; - - for (i = 0; i < pcm->channels; i++) - snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, 0); - snd_pcm_lib_free_pages(substream); - return 0; -} - -static int snd_emu10k1_fx8010_playback_prepare(snd_pcm_substream_t * substream) -{ - emu10k1_t *emu = snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; - snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; - unsigned int i; - - // printk("prepare: etram_pages = 0x%p, dma_area = 0x%x, buffer_size = 0x%x (0x%x)\n", emu->fx8010.etram_pages, runtime->dma_area, runtime->buffer_size, runtime->buffer_size << 2); - pcm->sw_data = pcm->sw_io = pcm->sw_ready = 0; - pcm->hw_data = pcm->hw_io = pcm->hw_ready = 0; - pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size); - pcm->tram_shift = 0; - pcm->appl_ptr = 0; - snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_running, 0, 0); /* reset */ - snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0); /* reset */ - snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_size, 0, runtime->buffer_size); - snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_ptr, 0, 0); /* reset ptr number */ - snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_count, 0, runtime->period_size); - snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_tmpcount, 0, runtime->period_size); - for (i = 0; i < pcm->channels; i++) - snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, (TANKMEMADDRREG_READ|TANKMEMADDRREG_ALIGN) + i * (runtime->buffer_size / pcm->channels)); - return 0; -} - -static int snd_emu10k1_fx8010_playback_trigger(snd_pcm_substream_t * substream, int cmd) -{ - emu10k1_t *emu = snd_pcm_substream_chip(substream); - snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; - unsigned long flags; - int result = 0; - - spin_lock_irqsave(&emu->reg_lock, flags); - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - /* follow thru */ - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: -#ifdef EMU10K1_SET_AC3_IEC958 - { - int i; - for (i = 0; i < 3; i++) { - unsigned int bits; - bits = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | - SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | - 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT | SPCS_NOTAUDIODATA; - snd_emu10k1_ptr_write(emu, SPCS0 + i, 0, bits); - } - } -#endif - result = snd_emu10k1_fx8010_register_irq_handler(emu, snd_emu10k1_fx8010_playback_irq, pcm->gpr_running, substream, &pcm->irq); - if (result < 0) - goto __err; - snd_emu10k1_fx8010_playback_transfer(substream); /* roll the ball */ - snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 1); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - snd_emu10k1_fx8010_unregister_irq_handler(emu, pcm->irq); pcm->irq = NULL; - snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0); - pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size); - pcm->tram_shift = 0; - break; - default: - result = -EINVAL; - break; - } - __err: - spin_unlock_irqrestore(&emu->reg_lock, flags); - return result; -} - -static snd_pcm_uframes_t snd_emu10k1_fx8010_playback_pointer(snd_pcm_substream_t * substream) -{ - emu10k1_t *emu = snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; - snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; - size_t ptr; - snd_pcm_sframes_t frames; - - if (!snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_trigger, 0)) - return 0; - ptr = snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_ptr, 0); - frames = ptr - pcm->hw_io; - if (frames < 0) - frames += runtime->buffer_size; - pcm->hw_io = ptr; - pcm->hw_ready -= frames; - pcm->sw_io += frames; - if (pcm->sw_io >= runtime->buffer_size) - pcm->sw_io -= runtime->buffer_size; - snd_emu10k1_fx8010_playback_transfer(substream); - return pcm->sw_io; -} - -static snd_pcm_hardware_t snd_emu10k1_fx8010_playback = -{ - .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | - /* SNDRV_PCM_INFO_MMAP_VALID | */ SNDRV_PCM_INFO_PAUSE), - .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_48000, - .rate_min = 48000, - .rate_max = 48000, - .channels_min = 1, - .channels_max = 1, - .buffer_bytes_max = (128*1024), - .period_bytes_min = 1024, - .period_bytes_max = (128*1024), - .periods_min = 1, - .periods_max = 1024, - .fifo_size = 0, -}; - -static int snd_emu10k1_fx8010_playback_open(snd_pcm_substream_t * substream) -{ - emu10k1_t *emu = snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; - snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; - - runtime->hw = snd_emu10k1_fx8010_playback; - runtime->hw.channels_min = runtime->hw.channels_max = pcm->channels; - runtime->hw.period_bytes_max = (pcm->buffer_size * 2) / 2; - spin_lock(&emu->reg_lock); - if (pcm->valid == 0) { - spin_unlock(&emu->reg_lock); - return -ENODEV; - } - pcm->opened = 1; - spin_unlock(&emu->reg_lock); - return 0; -} - -static int snd_emu10k1_fx8010_playback_close(snd_pcm_substream_t * substream) -{ - emu10k1_t *emu = snd_pcm_substream_chip(substream); - snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; - - spin_lock(&emu->reg_lock); - pcm->opened = 0; - spin_unlock(&emu->reg_lock); - return 0; -} - -static snd_pcm_ops_t snd_emu10k1_fx8010_playback_ops = { - .open = snd_emu10k1_fx8010_playback_open, - .close = snd_emu10k1_fx8010_playback_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_emu10k1_fx8010_playback_hw_params, - .hw_free = snd_emu10k1_fx8010_playback_hw_free, - .prepare = snd_emu10k1_fx8010_playback_prepare, - .trigger = snd_emu10k1_fx8010_playback_trigger, - .pointer = snd_emu10k1_fx8010_playback_pointer, - .ack = snd_emu10k1_fx8010_playback_transfer, -}; - -static void snd_emu10k1_fx8010_pcm_free(snd_pcm_t *pcm) -{ - emu10k1_t *emu = snd_magic_cast(emu10k1_t, pcm->private_data, return); - emu->pcm_fx8010 = NULL; - snd_pcm_lib_preallocate_free_for_all(pcm); -} - -int snd_emu10k1_fx8010_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm) -{ - snd_pcm_t *pcm; - int err; - - if (rpcm) - *rpcm = NULL; - - if ((err = snd_pcm_new(emu->card, "emu10k1", device, 8, 0, &pcm)) < 0) - return err; - - pcm->private_data = emu; - pcm->private_free = snd_emu10k1_fx8010_pcm_free; - - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_fx8010_playback_ops); - - pcm->info_flags = 0; - strcpy(pcm->name, "EMU10K1 FX8010"); - emu->pcm_fx8010 = pcm; - - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 0); - - if (rpcm) - *rpcm = pcm; - - return 0; -} - /************************************************************************* * EMU10K1 effect manager *************************************************************************/ @@ -1193,7 +885,7 @@ static int snd_emu10k1_ipcm_peek(emu10k1 #define SND_EMU10K1_GPR_CONTROLS 41 #define SND_EMU10K1_INPUTS 10 -#define SND_EMU10K1_PLAYBACK_CHANNELS 6 +#define SND_EMU10K1_PLAYBACK_CHANNELS 8 #define SND_EMU10K1_CAPTURE_CHANNELS 4 static void __devinit snd_emu10k1_init_mono_control(emu10k1_fx8010_control_gpr_t *ctl, const char *name, int gpr, int defval) @@ -1262,9 +954,9 @@ static int __devinit _snd_emu10k1_audigy spin_lock_init(&emu->fx8010.irq_lock); INIT_LIST_HEAD(&emu->fx8010.gpr_ctl); - if ((icode = snd_kcalloc(sizeof(emu10k1_fx8010_code_t), GFP_KERNEL)) == NULL) + if ((icode = kcalloc(1, sizeof(*icode), GFP_KERNEL)) == NULL) return -ENOMEM; - if ((controls = snd_kcalloc(sizeof(emu10k1_fx8010_control_gpr_t) * SND_EMU10K1_GPR_CONTROLS, GFP_KERNEL)) == NULL) { + if ((controls = kcalloc(SND_EMU10K1_GPR_CONTROLS, sizeof(*controls), GFP_KERNEL)) == NULL) { kfree(icode); return -ENOMEM; } @@ -1292,6 +984,14 @@ static int __devinit _snd_emu10k1_audigy A_OP(icode, &ptr, iMAC0, A_GPR(playback+3), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_REAR)); snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Surround Playback Volume", gpr, 100); gpr += 2; + + /* PCM Side Playback (independent from stereo mix) */ + if (emu->spk71) { + A_OP(icode, &ptr, iMAC0, A_GPR(playback+6), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_SIDE)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+7), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_SIDE)); + snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Side Playback Volume", gpr, 100); + gpr += 2; + } /* PCM Center Playback (independent from stereo mix) */ A_OP(icode, &ptr, iMAC0, A_GPR(playback+4), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_CENTER)); @@ -1440,6 +1140,14 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_G A_OP(icode, &ptr, iMAC0, A_GPR(playback+5), A_GPR(playback+5), A_GPR(gpr), A_GPR(tmp)); snd_emu10k1_init_mono_control(&controls[nctl++], "LFE Playback Volume", gpr, 0); gpr++; + + if (emu->spk71) { + /* Stereo Mix Side Playback */ + A_OP(icode, &ptr, iMAC0, A_GPR(playback+6), A_GPR(playback+6), A_GPR(gpr), A_GPR(stereo_mix)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+7), A_GPR(playback+7), A_GPR(gpr+1), A_GPR(stereo_mix+1)); + snd_emu10k1_init_stereo_control(&controls[nctl++], "Side Playback Volume", gpr, 0); + gpr += 2; + } /* * outputs @@ -1467,6 +1175,11 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_G A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 3), A_GPR(playback + 3), A_C_00000000, A_C_00000000); /* rear right */ A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), A_GPR(playback + 4), A_C_00000000, A_C_00000000); /* center */ A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), A_GPR(playback + 5), A_C_00000000, A_C_00000000); /* LFE */ + if (emu->spk71) { + A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 6), A_GPR(playback + 6), A_C_00000000, A_C_00000000); /* side left */ + A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 7), A_GPR(playback + 7), A_C_00000000, A_C_00000000); /* side right */ + } + ctl = &controls[nctl + 0]; ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; @@ -1497,7 +1210,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_G controls[nctl + 1].gpr[z * 2 + j] = TREBLE_GPR + z * 2 + j; } } - for (z = 0; z < 3; z++) { /* front/rear/center-lfe */ + for (z = 0; z < 4; z++) { /* front/rear/center-lfe/side */ int j, k, l, d; for (j = 0; j < 2; j++) { /* left/right */ k = 0xb0 + (z * 8) + (j * 4); @@ -1529,7 +1242,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_G #undef BASS_GPR #undef TREBLE_GPR - for (z = 0; z < 6; z++) { + for (z = 0; z < 8; z++) { A_SWITCH(icode, &ptr, tmp + 0, playback + SND_EMU10K1_PLAYBACK_CHANNELS + z, gpr + 0); A_SWITCH_NEG(icode, &ptr, tmp + 1, gpr + 0); A_SWITCH(icode, &ptr, tmp + 1, playback + z, tmp + 1); @@ -1540,12 +1253,14 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_G /* Master volume (will be renamed later) */ A_OP(icode, &ptr, iMAC0, A_GPR(playback+0+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+0+SND_EMU10K1_PLAYBACK_CHANNELS)); - A_OP(icode, &ptr, iMAC0, A_GPR(playback+1+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr+1), A_GPR(playback+1+SND_EMU10K1_PLAYBACK_CHANNELS)); - A_OP(icode, &ptr, iMAC0, A_GPR(playback+2+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr+1), A_GPR(playback+2+SND_EMU10K1_PLAYBACK_CHANNELS)); - A_OP(icode, &ptr, iMAC0, A_GPR(playback+3+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr+1), A_GPR(playback+3+SND_EMU10K1_PLAYBACK_CHANNELS)); - A_OP(icode, &ptr, iMAC0, A_GPR(playback+4+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr+1), A_GPR(playback+4+SND_EMU10K1_PLAYBACK_CHANNELS)); - A_OP(icode, &ptr, iMAC0, A_GPR(playback+5+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr+1), A_GPR(playback+5+SND_EMU10K1_PLAYBACK_CHANNELS)); - snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Master Playback Volume", gpr, 0); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+1+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+1+SND_EMU10K1_PLAYBACK_CHANNELS)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+2+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+2+SND_EMU10K1_PLAYBACK_CHANNELS)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+3+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+3+SND_EMU10K1_PLAYBACK_CHANNELS)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+4+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+4+SND_EMU10K1_PLAYBACK_CHANNELS)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+5+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+5+SND_EMU10K1_PLAYBACK_CHANNELS)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+6+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+6+SND_EMU10K1_PLAYBACK_CHANNELS)); + A_OP(icode, &ptr, iMAC0, A_GPR(playback+7+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+7+SND_EMU10K1_PLAYBACK_CHANNELS)); + snd_emu10k1_init_mono_control(&controls[nctl++], "Wave Master Playback Volume", gpr, 0); gpr += 2; /* analog speakers */ @@ -1553,6 +1268,8 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_G A_PUT_STEREO_OUTPUT(A_EXTOUT_AREAR_L, A_EXTOUT_AREAR_R, playback+2 + SND_EMU10K1_PLAYBACK_CHANNELS); A_PUT_OUTPUT(A_EXTOUT_ACENTER, playback+4 + SND_EMU10K1_PLAYBACK_CHANNELS); A_PUT_OUTPUT(A_EXTOUT_ALFE, playback+5 + SND_EMU10K1_PLAYBACK_CHANNELS); + if (emu->spk71) + A_PUT_STEREO_OUTPUT(A_EXTOUT_ASIDE_L, A_EXTOUT_ASIDE_R, playback+6 + SND_EMU10K1_PLAYBACK_CHANNELS); /* headphone */ A_PUT_STEREO_OUTPUT(A_EXTOUT_HEADPHONE_L, A_EXTOUT_HEADPHONE_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS); @@ -1674,13 +1391,13 @@ static int __devinit _snd_emu10k1_init_e spin_lock_init(&emu->fx8010.irq_lock); INIT_LIST_HEAD(&emu->fx8010.gpr_ctl); - if ((icode = snd_kcalloc(sizeof(emu10k1_fx8010_code_t), GFP_KERNEL)) == NULL) + if ((icode = kcalloc(1, sizeof(*icode), GFP_KERNEL)) == NULL) return -ENOMEM; - if ((controls = snd_kcalloc(sizeof(emu10k1_fx8010_control_gpr_t) * SND_EMU10K1_GPR_CONTROLS, GFP_KERNEL)) == NULL) { + if ((controls = kcalloc(SND_EMU10K1_GPR_CONTROLS, sizeof(emu10k1_fx8010_control_gpr_t), GFP_KERNEL)) == NULL) { kfree(icode); return -ENOMEM; } - if ((ipcm = snd_kcalloc(sizeof(emu10k1_fx8010_pcm_t), GFP_KERNEL)) == NULL) { + if ((ipcm = kcalloc(1, sizeof(*ipcm), GFP_KERNEL)) == NULL) { kfree(controls); kfree(icode); return -ENOMEM; @@ -2236,13 +1953,14 @@ int snd_emu10k1_fx8010_tram_setup(emu10k snd_emu10k1_ptr_write(emu, TCB, 0, 0); snd_emu10k1_ptr_write(emu, TCBS, 0, 0); if (emu->fx8010.etram_pages.area != NULL) { - snd_dma_free_pages(&emu->dma_dev, &emu->fx8010.etram_pages); + snd_dma_free_pages(&emu->fx8010.etram_pages); emu->fx8010.etram_pages.area = NULL; emu->fx8010.etram_pages.bytes = 0; } if (size > 0) { - if (snd_dma_alloc_pages(&emu->dma_dev, size * 2, &emu->fx8010.etram_pages) < 0) + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), + size * 2, &emu->fx8010.etram_pages) < 0) return -ENOMEM; memset(emu->fx8010.etram_pages.area, 0, size * 2); snd_emu10k1_ptr_write(emu, TCB, 0, emu->fx8010.etram_pages.addr); @@ -2297,7 +2015,7 @@ static int snd_emu10k1_fx8010_info(emu10 static int snd_emu10k1_fx8010_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg) { - emu10k1_t *emu = snd_magic_cast(emu10k1_t, hw->private_data, return -ENXIO); + emu10k1_t *emu = hw->private_data; emu10k1_fx8010_info_t *info; emu10k1_fx8010_code_t *icode; emu10k1_fx8010_pcm_t *ipcm; @@ -2364,7 +2082,7 @@ static int snd_emu10k1_fx8010_ioctl(snd_ case SNDRV_EMU10K1_IOCTL_PCM_PEEK: if (emu->audigy) return -EINVAL; - ipcm = (emu10k1_fx8010_pcm_t *)snd_kcalloc(sizeof(*ipcm), GFP_KERNEL); + ipcm = kcalloc(1, sizeof(*ipcm), GFP_KERNEL); if (ipcm == NULL) return -ENOMEM; if (copy_from_user(ipcm, argp, sizeof(*ipcm))) { --- linux-2.6.9-rc1/sound/pci/emu10k1/emumixer.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/sound/pci/emu10k1/emumixer.c 2004-09-02 20:51:53.000000000 -0700 @@ -32,8 +32,6 @@ #include #include -#define chip_t emu10k1_t - #define AC97_ID_STAC9758 0x83847658 static int snd_emu10k1_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) @@ -421,7 +419,7 @@ static snd_kcontrol_new_t snd_audigy_sha */ static void snd_emu10k1_mixer_free_ac97(ac97_t *ac97) { - emu10k1_t *emu = snd_magic_cast(emu10k1_t, ac97->private_data, return); + emu10k1_t *emu = ac97->private_data; emu->ac97 = NULL; } @@ -512,13 +510,14 @@ int __devinit snd_emu10k1_mixer(emu10k1_ }; if (!emu->no_ac97) { - ac97_bus_t bus, *pbus; - ac97_t ac97; + ac97_bus_t *pbus; + ac97_template_t ac97; + static ac97_bus_ops_t ops = { + .write = snd_emu10k1_ac97_write, + .read = snd_emu10k1_ac97_read, + }; - memset(&bus, 0, sizeof(bus)); - bus.write = snd_emu10k1_ac97_write; - bus.read = snd_emu10k1_ac97_read; - if ((err = snd_ac97_bus(emu->card, &bus, &pbus)) < 0) + if ((err = snd_ac97_bus(emu->card, 0, &ops, NULL, &pbus)) < 0) return err; memset(&ac97, 0, sizeof(ac97)); @@ -528,7 +527,7 @@ int __devinit snd_emu10k1_mixer(emu10k1_ return err; if (emu->audigy) { /* set master volume to 0 dB */ - snd_ac97_write(emu->ac97, AC97_MASTER, 0x0202); + snd_ac97_write(emu->ac97, AC97_MASTER, 0x0000); /* set capture source to mic */ snd_ac97_write(emu->ac97, AC97_REC_SEL, 0x0000); c = audigy_remove_ctls; --- linux-2.6.9-rc1/sound/pci/emu10k1/emupcm.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/sound/pci/emu10k1/emupcm.c 2004-09-02 20:51:53.000000000 -0700 @@ -34,8 +34,6 @@ #include #include -#define chip_t emu10k1_t - static void snd_emu10k1_pcm_interrupt(emu10k1_t *emu, emu10k1_voice_t *voice) { emu10k1_pcm_t *epcm; @@ -354,7 +352,7 @@ static int snd_emu10k1_playback_hw_param { emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + emu10k1_pcm_t *epcm = runtime->private_data; int err; if ((err = snd_emu10k1_pcm_channel_alloc(epcm, params_channels(hw_params))) < 0) @@ -383,7 +381,7 @@ static int snd_emu10k1_playback_hw_free( if (runtime->private_data == NULL) return 0; - epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + epcm = runtime->private_data; if (epcm->extra) { snd_emu10k1_voice_free(epcm->emu, epcm->extra); epcm->extra = NULL; @@ -409,7 +407,7 @@ static int snd_emu10k1_playback_prepare( { emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + emu10k1_pcm_t *epcm = runtime->private_data; unsigned int start_addr, end_addr; start_addr = epcm->start_addr; @@ -443,7 +441,7 @@ static int snd_emu10k1_capture_prepare(s { emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + emu10k1_pcm_t *epcm = runtime->private_data; int idx; snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0); @@ -452,7 +450,11 @@ static int snd_emu10k1_capture_prepare(s snd_emu10k1_ptr_write(emu, ADCCR, 0, 0); break; case CAPTURE_EFX: - snd_emu10k1_ptr_write(emu, FXWC, 0, 0); + if (emu->audigy) { + snd_emu10k1_ptr_write(emu, A_FXWC1, 0, 0); + snd_emu10k1_ptr_write(emu, A_FXWC2, 0, 0); + } else + snd_emu10k1_ptr_write(emu, FXWC, 0, 0); break; default: break; @@ -565,12 +567,11 @@ static int snd_emu10k1_playback_trigger( { emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); - unsigned long flags; + emu10k1_pcm_t *epcm = runtime->private_data; int result = 0; // printk("trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n", (int)emu, cmd, substream->ops->pointer(substream)); - spin_lock_irqsave(&emu->reg_lock, flags); + spin_lock(&emu->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: snd_emu10k1_playback_invalidate_cache(emu, epcm->extra); /* do we need this? */ @@ -593,7 +594,7 @@ static int snd_emu10k1_playback_trigger( result = -EINVAL; break; } - spin_unlock_irqrestore(&emu->reg_lock, flags); + spin_unlock(&emu->reg_lock); return result; } @@ -602,12 +603,11 @@ static int snd_emu10k1_capture_trigger(s { emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); - unsigned long flags; + emu10k1_pcm_t *epcm = runtime->private_data; int result = 0; // printk("trigger - emu10k1 = %p, cmd = %i, pointer = %i\n", emu, cmd, substream->ops->pointer(substream)); - spin_lock_irqsave(&emu->reg_lock, flags); + spin_lock(&emu->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: outl(epcm->capture_ipr, emu->port + IPR); @@ -618,7 +618,11 @@ static int snd_emu10k1_capture_trigger(s snd_emu10k1_ptr_write(emu, ADCCR, 0, epcm->capture_cr_val); break; case CAPTURE_EFX: - snd_emu10k1_ptr_write(emu, FXWC, 0, epcm->capture_cr_val); + if (emu->audigy) { + snd_emu10k1_ptr_write(emu, A_FXWC1, 0, epcm->capture_cr_val); + snd_emu10k1_ptr_write(emu, A_FXWC2, 0, epcm->capture_cr_val2); + } else + snd_emu10k1_ptr_write(emu, FXWC, 0, epcm->capture_cr_val); break; default: break; @@ -637,7 +641,11 @@ static int snd_emu10k1_capture_trigger(s snd_emu10k1_ptr_write(emu, ADCCR, 0, 0); break; case CAPTURE_EFX: - snd_emu10k1_ptr_write(emu, FXWC, 0, 0); + if (emu->audigy) { + snd_emu10k1_ptr_write(emu, A_FXWC1, 0, 0); + snd_emu10k1_ptr_write(emu, A_FXWC2, 0, 0); + } else + snd_emu10k1_ptr_write(emu, FXWC, 0, 0); break; default: break; @@ -646,7 +654,7 @@ static int snd_emu10k1_capture_trigger(s default: result = -EINVAL; } - spin_unlock_irqrestore(&emu->reg_lock, flags); + spin_unlock(&emu->reg_lock); return result; } @@ -654,7 +662,7 @@ static snd_pcm_uframes_t snd_emu10k1_pla { emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + emu10k1_pcm_t *epcm = runtime->private_data; unsigned int ptr; if (!epcm->running) @@ -681,7 +689,7 @@ static snd_pcm_uframes_t snd_emu10k1_cap { emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); + emu10k1_pcm_t *epcm = runtime->private_data; unsigned int ptr; if (!epcm->running) @@ -767,10 +775,10 @@ static void snd_emu10k1_pcm_mixer_notify static void snd_emu10k1_pcm_free_substream(snd_pcm_runtime_t *runtime) { - emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return); + emu10k1_pcm_t *epcm = runtime->private_data; if (epcm) - snd_magic_kfree(epcm); + kfree(epcm); } static int snd_emu10k1_playback_open(snd_pcm_substream_t * substream) @@ -781,7 +789,7 @@ static int snd_emu10k1_playback_open(snd snd_pcm_runtime_t *runtime = substream->runtime; int i, err; - epcm = snd_magic_kcalloc(emu10k1_pcm_t, 0, GFP_KERNEL); + epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL); if (epcm == NULL) return -ENOMEM; epcm->emu = emu; @@ -791,11 +799,11 @@ static int snd_emu10k1_playback_open(snd runtime->private_free = snd_emu10k1_pcm_free_substream; runtime->hw = snd_emu10k1_playback; if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) { - snd_magic_kfree(epcm); + kfree(epcm); return err; } if ((err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX)) < 0) { - snd_magic_kfree(epcm); + kfree(epcm); return err; } mix = &emu->pcm_mixer[substream->number]; @@ -826,7 +834,7 @@ static int snd_emu10k1_capture_open(snd_ snd_pcm_runtime_t *runtime = substream->runtime; emu10k1_pcm_t *epcm; - epcm = snd_magic_kcalloc(emu10k1_pcm_t, 0, GFP_KERNEL); + epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL); if (epcm == NULL) return -ENOMEM; epcm->emu = emu; @@ -842,7 +850,7 @@ static int snd_emu10k1_capture_open(snd_ runtime->hw = snd_emu10k1_capture; emu->capture_interrupt = snd_emu10k1_pcm_ac97adc_interrupt; emu->pcm_capture_substream = substream; - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_capture_period_sizes); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_capture_rates); return 0; } @@ -862,7 +870,7 @@ static int snd_emu10k1_capture_mic_open( emu10k1_pcm_t *epcm; snd_pcm_runtime_t *runtime = substream->runtime; - epcm = snd_magic_kcalloc(emu10k1_pcm_t, 0, GFP_KERNEL); + epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL); if (epcm == NULL) return -ENOMEM; epcm->emu = emu; @@ -881,7 +889,7 @@ static int snd_emu10k1_capture_mic_open( runtime->hw.channels_min = 1; emu->capture_mic_interrupt = snd_emu10k1_pcm_ac97mic_interrupt; emu->pcm_capture_mic_substream = substream; - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_capture_period_sizes); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes); return 0; } @@ -899,11 +907,10 @@ static int snd_emu10k1_capture_efx_open( emu10k1_t *emu = snd_pcm_substream_chip(substream); emu10k1_pcm_t *epcm; snd_pcm_runtime_t *runtime = substream->runtime; - unsigned long flags; int nefx = emu->audigy ? 64 : 32; int idx; - epcm = snd_magic_kcalloc(emu10k1_pcm_t, 0, GFP_KERNEL); + epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL); if (epcm == NULL) return -ENOMEM; epcm->emu = emu; @@ -919,7 +926,7 @@ static int snd_emu10k1_capture_efx_open( runtime->hw = snd_emu10k1_capture; runtime->hw.rates = SNDRV_PCM_RATE_48000; runtime->hw.rate_min = runtime->hw.rate_max = 48000; - spin_lock_irqsave(&emu->reg_lock, flags); + spin_lock_irq(&emu->reg_lock); runtime->hw.channels_min = runtime->hw.channels_max = 0; for (idx = 0; idx < nefx; idx++) { if (emu->efx_voices_mask[idx/32] & (1 << (idx%32))) { @@ -929,10 +936,10 @@ static int snd_emu10k1_capture_efx_open( } epcm->capture_cr_val = emu->efx_voices_mask[0]; epcm->capture_cr_val2 = emu->efx_voices_mask[1]; - spin_unlock_irqrestore(&emu->reg_lock, flags); + spin_unlock_irq(&emu->reg_lock); emu->capture_efx_interrupt = snd_emu10k1_pcm_efx_interrupt; emu->pcm_capture_efx_substream = substream; - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_capture_period_sizes); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes); return 0; } @@ -970,7 +977,7 @@ static snd_pcm_ops_t snd_emu10k1_capture static void snd_emu10k1_pcm_free(snd_pcm_t *pcm) { - emu10k1_t *emu = snd_magic_cast(emu10k1_t, pcm->private_data, return); + emu10k1_t *emu = pcm->private_data; emu->pcm = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -1024,7 +1031,7 @@ static snd_pcm_ops_t snd_emu10k1_capture static void snd_emu10k1_pcm_mic_free(snd_pcm_t *pcm) { - emu10k1_t *emu = snd_magic_cast(emu10k1_t, pcm->private_data, return); + emu10k1_t *emu = pcm->private_data; emu->pcm_mic = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -1070,21 +1077,19 @@ static int snd_emu10k1_pcm_efx_voices_ma static int snd_emu10k1_pcm_efx_voices_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { emu10k1_t *emu = snd_kcontrol_chip(kcontrol); - unsigned long flags; int nefx = emu->audigy ? 64 : 32; int idx; - spin_lock_irqsave(&emu->reg_lock, flags); + spin_lock_irq(&emu->reg_lock); for (idx = 0; idx < nefx; idx++) ucontrol->value.integer.value[idx] = (emu->efx_voices_mask[idx / 32] & (1 << (idx % 32))) ? 1 : 0; - spin_unlock_irqrestore(&emu->reg_lock, flags); + spin_unlock_irq(&emu->reg_lock); return 0; } static int snd_emu10k1_pcm_efx_voices_mask_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { emu10k1_t *emu = snd_kcontrol_chip(kcontrol); - unsigned long flags; unsigned int nval[2], bits; int nefx = emu->audigy ? 64 : 32; int change, idx; @@ -1097,12 +1102,12 @@ static int snd_emu10k1_pcm_efx_voices_ma } if (bits != 1 && bits != 2 && bits != 4 && bits != 8) return -EINVAL; - spin_lock_irqsave(&emu->reg_lock, flags); + spin_lock_irq(&emu->reg_lock); change = (nval[0] != emu->efx_voices_mask[0]) || (nval[1] != emu->efx_voices_mask[1]); emu->efx_voices_mask[0] = nval[0]; emu->efx_voices_mask[1] = nval[1]; - spin_unlock_irqrestore(&emu->reg_lock, flags); + spin_unlock_irq(&emu->reg_lock); return change; } @@ -1125,9 +1130,238 @@ static snd_pcm_ops_t snd_emu10k1_capture .pointer = snd_emu10k1_capture_pointer, }; + +/* EFX playback */ + +#define INITIAL_TRAM_SHIFT 14 +#define INITIAL_TRAM_POS(size) ((((size) / 2) - INITIAL_TRAM_SHIFT) - 1) + +static void snd_emu10k1_fx8010_playback_irq(emu10k1_t *emu, void *private_data) +{ + snd_pcm_substream_t *substream = private_data; + snd_pcm_period_elapsed(substream); +} + +static void snd_emu10k1_fx8010_playback_tram_poke1(unsigned short *dst_left, + unsigned short *dst_right, + unsigned short *src, + unsigned int count, + unsigned int tram_shift) +{ + // printk("tram_poke1: dst_left = 0x%p, dst_right = 0x%p, src = 0x%p, count = 0x%x\n", dst_left, dst_right, src, count); + if ((tram_shift & 1) == 0) { + while (count--) { + *dst_left-- = *src++; + *dst_right-- = *src++; + } + } else { + while (count--) { + *dst_right-- = *src++; + *dst_left-- = *src++; + } + } +} + +static void fx8010_pb_trans_copy(snd_pcm_substream_t *substream, + snd_pcm_indirect_t *rec, size_t bytes) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + unsigned int tram_size = pcm->buffer_size; + unsigned short *src = (unsigned short *)(substream->runtime->dma_area + rec->sw_data); + unsigned int frames = bytes >> 2, count; + unsigned int tram_pos = pcm->tram_pos; + unsigned int tram_shift = pcm->tram_shift; + + while (frames > tram_pos) { + count = tram_pos + 1; + snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages.area + tram_pos, + (unsigned short *)emu->fx8010.etram_pages.area + tram_pos + tram_size / 2, + src, count, tram_shift); + src += count * 2; + frames -= count; + tram_pos = (tram_size / 2) - 1; + tram_shift++; + } + snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages.area + tram_pos, + (unsigned short *)emu->fx8010.etram_pages.area + tram_pos + tram_size / 2, + src, frames, tram_shift++); + tram_pos -= frames; + pcm->tram_pos = tram_pos; + pcm->tram_shift = tram_shift; +} + +static int snd_emu10k1_fx8010_playback_transfer(snd_pcm_substream_t *substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + + snd_pcm_indirect_playback_transfer(substream, &pcm->pcm_rec, fx8010_pb_trans_copy); + return 0; +} + +static int snd_emu10k1_fx8010_playback_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); +} + +static int snd_emu10k1_fx8010_playback_hw_free(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + unsigned int i; + + for (i = 0; i < pcm->channels; i++) + snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, 0); + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int snd_emu10k1_fx8010_playback_prepare(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + unsigned int i; + + // printk("prepare: etram_pages = 0x%p, dma_area = 0x%x, buffer_size = 0x%x (0x%x)\n", emu->fx8010.etram_pages, runtime->dma_area, runtime->buffer_size, runtime->buffer_size << 2); + memset(&pcm->pcm_rec, 0, sizeof(pcm->pcm_rec)); + pcm->pcm_rec.hw_buffer_size = pcm->buffer_size * 2; /* byte size */ + pcm->pcm_rec.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream); + pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size); + pcm->tram_shift = 0; + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_running, 0, 0); /* reset */ + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0); /* reset */ + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_size, 0, runtime->buffer_size); + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_ptr, 0, 0); /* reset ptr number */ + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_count, 0, runtime->period_size); + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_tmpcount, 0, runtime->period_size); + for (i = 0; i < pcm->channels; i++) + snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, (TANKMEMADDRREG_READ|TANKMEMADDRREG_ALIGN) + i * (runtime->buffer_size / pcm->channels)); + return 0; +} + +static int snd_emu10k1_fx8010_playback_trigger(snd_pcm_substream_t * substream, int cmd) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + int result = 0; + + spin_lock(&emu->reg_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* follow thru */ + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: +#ifdef EMU10K1_SET_AC3_IEC958 + { + int i; + for (i = 0; i < 3; i++) { + unsigned int bits; + bits = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | + SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | + 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT | SPCS_NOTAUDIODATA; + snd_emu10k1_ptr_write(emu, SPCS0 + i, 0, bits); + } + } +#endif + result = snd_emu10k1_fx8010_register_irq_handler(emu, snd_emu10k1_fx8010_playback_irq, pcm->gpr_running, substream, &pcm->irq); + if (result < 0) + goto __err; + snd_emu10k1_fx8010_playback_transfer(substream); /* roll the ball */ + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 1); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + snd_emu10k1_fx8010_unregister_irq_handler(emu, pcm->irq); pcm->irq = NULL; + snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0); + pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size); + pcm->tram_shift = 0; + break; + default: + result = -EINVAL; + break; + } + __err: + spin_unlock(&emu->reg_lock); + return result; +} + +static snd_pcm_uframes_t snd_emu10k1_fx8010_playback_pointer(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + size_t ptr; /* byte pointer */ + + if (!snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_trigger, 0)) + return 0; + ptr = snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_ptr, 0) << 2; + return snd_pcm_indirect_playback_pointer(substream, &pcm->pcm_rec, ptr); +} + +static snd_pcm_hardware_t snd_emu10k1_fx8010_playback = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + /* SNDRV_PCM_INFO_MMAP_VALID | */ SNDRV_PCM_INFO_PAUSE), + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 1024, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +static int snd_emu10k1_fx8010_playback_open(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + + runtime->hw = snd_emu10k1_fx8010_playback; + runtime->hw.channels_min = runtime->hw.channels_max = pcm->channels; + runtime->hw.period_bytes_max = (pcm->buffer_size * 2) / 2; + spin_lock_irq(&emu->reg_lock); + if (pcm->valid == 0) { + spin_unlock_irq(&emu->reg_lock); + return -ENODEV; + } + pcm->opened = 1; + spin_unlock_irq(&emu->reg_lock); + return 0; +} + +static int snd_emu10k1_fx8010_playback_close(snd_pcm_substream_t * substream) +{ + emu10k1_t *emu = snd_pcm_substream_chip(substream); + snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; + + spin_lock_irq(&emu->reg_lock); + pcm->opened = 0; + spin_unlock_irq(&emu->reg_lock); + return 0; +} + +static snd_pcm_ops_t snd_emu10k1_fx8010_playback_ops = { + .open = snd_emu10k1_fx8010_playback_open, + .close = snd_emu10k1_fx8010_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_emu10k1_fx8010_playback_hw_params, + .hw_free = snd_emu10k1_fx8010_playback_hw_free, + .prepare = snd_emu10k1_fx8010_playback_prepare, + .trigger = snd_emu10k1_fx8010_playback_trigger, + .pointer = snd_emu10k1_fx8010_playback_pointer, + .ack = snd_emu10k1_fx8010_playback_transfer, +}; + static void snd_emu10k1_pcm_efx_free(snd_pcm_t *pcm) { - emu10k1_t *emu = snd_magic_cast(emu10k1_t, pcm->private_data, return); + emu10k1_t *emu = pcm->private_data; emu->pcm_efx = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -1140,12 +1374,13 @@ int __devinit snd_emu10k1_pcm_efx(emu10k if (rpcm) *rpcm = NULL; - if ((err = snd_pcm_new(emu->card, "emu10k1 efx", device, 0, 1, &pcm)) < 0) + if ((err = snd_pcm_new(emu->card, "emu10k1 efx", device, 8, 1, &pcm)) < 0) return err; pcm->private_data = emu; pcm->private_free = snd_emu10k1_pcm_efx_free; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_fx8010_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_efx_ops); pcm->info_flags = 0; --- linux-2.6.9-rc1/sound/pci/emu10k1/emuproc.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/sound/pci/emu10k1/emuproc.c 2004-09-02 20:51:53.000000000 -0700 @@ -71,31 +71,32 @@ static void snd_emu10k1_proc_spdif_statu static void snd_emu10k1_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) { - static char *outputs[32] = { - /* 00 */ "PCM Left", - /* 01 */ "PCM Right", - /* 02 */ "PCM Surround Left", - /* 03 */ "PCM Surround Right", - /* 04 */ "MIDI Left", - /* 05 */ "MIDI Right", - /* 06 */ "PCM Center", - /* 07 */ "PCM LFE", - /* 08 */ "???", - /* 09 */ "???", - /* 10 */ "???", - /* 11 */ "???", - /* 12 */ "MIDI Reverb", - /* 13 */ "MIDI Chorus", - /* 14 */ "???", + /* FIXME - output names are in emufx.c too */ + static char *creative_outs[32] = { + /* 00 */ "AC97 Left", + /* 01 */ "AC97 Right", + /* 02 */ "Optical IEC958 Left", + /* 03 */ "Optical IEC958 Right", + /* 04 */ "Center", + /* 05 */ "LFE", + /* 06 */ "Headphone Left", + /* 07 */ "Headphone Right", + /* 08 */ "Surround Left", + /* 09 */ "Surround Right", + /* 10 */ "PCM Capture Left", + /* 11 */ "PCM Capture Right", + /* 12 */ "MIC Capture", + /* 13 */ "AC97 Surround Left", + /* 14 */ "AC97 Surround Right", /* 15 */ "???", /* 16 */ "???", - /* 17 */ "???", - /* 18 */ "ADC Left / CDROM S/PDIF Left", - /* 19 */ "ADC Right / CDROM S/PDIF Right", - /* 20 */ "MIC / Zoom Video Left", - /* 21 */ "Zoom Video Right", - /* 22 */ "S/PDIF Left", - /* 23 */ "S/PDIF Right", + /* 17 */ "Analog Center", + /* 18 */ "Analog LFE", + /* 19 */ "???", + /* 20 */ "???", + /* 21 */ "???", + /* 22 */ "???", + /* 23 */ "???", /* 24 */ "???", /* 25 */ "???", /* 26 */ "???", @@ -105,9 +106,78 @@ static void snd_emu10k1_proc_read(snd_in /* 30 */ "???", /* 31 */ "???" }; - emu10k1_t *emu = snd_magic_cast(emu10k1_t, entry->private_data, return); + + static char *audigy_outs[64] = { + /* 00 */ "Digital Front Left", + /* 01 */ "Digital Front Right", + /* 02 */ "Digital Center", + /* 03 */ "Digital LEF", + /* 04 */ "Headphone Left", + /* 05 */ "Headphone Right", + /* 06 */ "Digital Rear Left", + /* 07 */ "Digital Rear Right", + /* 08 */ "Front Left", + /* 09 */ "Front Right", + /* 10 */ "Center", + /* 11 */ "LFE", + /* 12 */ "???", + /* 13 */ "???", + /* 14 */ "Rear Left", + /* 15 */ "Rear Right", + /* 16 */ "AC97 Front Left", + /* 17 */ "AC97 Front Right", + /* 18 */ "ADC Caputre Left", + /* 19 */ "ADC Capture Right", + /* 20 */ "???", + /* 21 */ "???", + /* 22 */ "???", + /* 23 */ "???", + /* 24 */ "???", + /* 25 */ "???", + /* 26 */ "???", + /* 27 */ "???", + /* 28 */ "???", + /* 29 */ "???", + /* 30 */ "???", + /* 31 */ "???", + /* 32 */ "???", + /* 33 */ "???", + /* 34 */ "???", + /* 35 */ "???", + /* 36 */ "???", + /* 37 */ "???", + /* 38 */ "???", + /* 39 */ "???", + /* 40 */ "???", + /* 41 */ "???", + /* 42 */ "???", + /* 43 */ "???", + /* 44 */ "???", + /* 45 */ "???", + /* 46 */ "???", + /* 47 */ "???", + /* 48 */ "???", + /* 49 */ "???", + /* 50 */ "???", + /* 51 */ "???", + /* 52 */ "???", + /* 53 */ "???", + /* 54 */ "???", + /* 55 */ "???", + /* 56 */ "???", + /* 57 */ "???", + /* 58 */ "???", + /* 59 */ "???", + /* 60 */ "???", + /* 61 */ "???", + /* 62 */ "???", + /* 33 */ "???" + }; + + emu10k1_t *emu = entry->private_data; unsigned int val; int nefx = emu->audigy ? 64 : 32; + char **outputs = emu->audigy ? audigy_outs : creative_outs; int idx; snd_iprintf(buffer, "EMU10K1\n\n"); @@ -117,7 +187,7 @@ static void snd_emu10k1_proc_read(snd_in snd_iprintf(buffer, "Card : %s\n", emu->audigy ? "Audigy" : (emu->APS ? "EMU APS" : "Creative")); snd_iprintf(buffer, "Internal TRAM (words) : 0x%x\n", emu->fx8010.itram_size); - snd_iprintf(buffer, "External TRAM (words) : 0x%x\n", emu->fx8010.etram_pages.bytes); + snd_iprintf(buffer, "External TRAM (words) : 0x%x\n", (int)emu->fx8010.etram_pages.bytes); snd_iprintf(buffer, "\n"); if (emu->audigy) { snd_iprintf(buffer, "Effect Send Routing : A=%i, B=%i, C=%i, D=%i\n", @@ -135,7 +205,7 @@ static void snd_emu10k1_proc_read(snd_in snd_iprintf(buffer, "\nCaptured FX Outputs :\n"); for (idx = 0; idx < nefx; idx++) { if (emu->efx_voices_mask[idx/32] & (1 << (idx%32))) - snd_iprintf(buffer, " Output %02i [%s]\n", idx, outputs[idx%32]); + snd_iprintf(buffer, " Output %02i [%s]\n", idx, outputs[idx]); } snd_iprintf(buffer, "\nAll FX Outputs :\n"); for (idx = 0; idx < 32; idx++) @@ -155,7 +225,7 @@ static void snd_emu10k1_proc_acode_read( snd_info_buffer_t * buffer) { u32 pc; - emu10k1_t *emu = snd_magic_cast(emu10k1_t, entry->private_data, return); + emu10k1_t *emu = entry->private_data; snd_iprintf(buffer, "FX8010 Instruction List '%s'\n", emu->fx8010.name); snd_iprintf(buffer, " Code dump :\n"); @@ -195,7 +265,7 @@ static long snd_emu10k1_fx8010_read(snd_ unsigned long count, unsigned long pos) { long size; - emu10k1_t *emu = snd_magic_cast(emu10k1_t, entry->private_data, return -ENXIO); + emu10k1_t *emu = entry->private_data; unsigned int offset; if (!strcmp(entry->name, "fx8010_tram_addr")) { --- linux-2.6.9-rc1/sound/pci/emu10k1/io.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/sound/pci/emu10k1/io.c 2004-09-02 20:51:53.000000000 -0700 @@ -231,7 +231,7 @@ void snd_emu10k1_wait(emu10k1_t *emu, un unsigned short snd_emu10k1_ac97_read(ac97_t *ac97, unsigned short reg) { - emu10k1_t *emu = snd_magic_cast(emu10k1_t, ac97->private_data, return -ENXIO); + emu10k1_t *emu = ac97->private_data; unsigned long flags; unsigned short val; @@ -244,7 +244,7 @@ unsigned short snd_emu10k1_ac97_read(ac9 void snd_emu10k1_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short data) { - emu10k1_t *emu = snd_magic_cast(emu10k1_t, ac97->private_data, return); + emu10k1_t *emu = ac97->private_data; unsigned long flags; spin_lock_irqsave(&emu->emu_lock, flags); --- linux-2.6.9-rc1/sound/pci/emu10k1/irq.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/sound/pci/emu10k1/irq.c 2004-09-02 20:51:53.000000000 -0700 @@ -32,7 +32,7 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - emu10k1_t *emu = snd_magic_cast(emu10k1_t, dev_id, return IRQ_NONE); + emu10k1_t *emu = dev_id; unsigned int status, orig_status; int handled = 0; --- linux-2.6.9-rc1/sound/pci/emu10k1/memory.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/sound/pci/emu10k1/memory.c 2004-09-02 20:51:53.000000000 -0700 @@ -22,6 +22,7 @@ */ #include +#include #include #include #include @@ -291,7 +292,7 @@ snd_util_memblk_t * snd_emu10k1_alloc_pages(emu10k1_t *emu, snd_pcm_substream_t *substream) { snd_pcm_runtime_t *runtime = substream->runtime; - struct snd_sg_buf *sgbuf = runtime->dma_private; + struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream); snd_util_memhdr_t *hdr; emu10k1_memblk_t *blk; int page, err, idx; @@ -441,10 +442,11 @@ static int synth_alloc_pages(emu10k1_t * get_single_page_range(emu->memhdr, blk, &first_page, &last_page); /* allocate kernel pages */ for (page = first_page; page <= last_page; page++) { - if (snd_dma_alloc_pages(&emu->dma_dev, PAGE_SIZE, &dmab) < 0) + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), + PAGE_SIZE, &dmab) < 0) goto __fail; if (! is_valid_page(emu, dmab.addr)) { - snd_dma_free_pages(&emu->dma_dev, &dmab); + snd_dma_free_pages(&dmab); goto __fail; } emu->page_addr_table[page] = dmab.addr; @@ -459,7 +461,7 @@ __fail: dmab.area = emu->page_ptr_table[page]; dmab.addr = emu->page_addr_table[page]; dmab.bytes = PAGE_SIZE; - snd_dma_free_pages(&emu->dma_dev, &dmab); + snd_dma_free_pages(&dmab); emu->page_addr_table[page] = 0; emu->page_ptr_table[page] = NULL; } @@ -476,13 +478,15 @@ static int synth_free_pages(emu10k1_t *e struct snd_dma_buffer dmab; get_single_page_range(emu->memhdr, blk, &first_page, &last_page); + dmab.dev.type = SNDRV_DMA_TYPE_DEV; + dmab.dev.dev = snd_dma_pci_data(emu->pci); for (page = first_page; page <= last_page; page++) { if (emu->page_ptr_table[page] == NULL) continue; dmab.area = emu->page_ptr_table[page]; dmab.addr = emu->page_addr_table[page]; dmab.bytes = PAGE_SIZE; - snd_dma_free_pages(&emu->dma_dev, &dmab); + snd_dma_free_pages(&dmab); emu->page_addr_table[page] = 0; emu->page_ptr_table[page] = NULL; } --- linux-2.6.9-rc1/sound/pci/ens1370.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/sound/pci/ens1370.c 2004-09-02 20:51:53.000000000 -0700 @@ -40,8 +40,6 @@ #include #include -#define chip_t ensoniq_t - #ifndef CHIP1371 #undef CHIP1370 #define CHIP1370 @@ -56,15 +54,14 @@ MODULE_AUTHOR("Jaroslav Kysela , Thomas Sailer "); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); #ifdef CHIP1370 MODULE_DESCRIPTION("Ensoniq AudioPCI ES1370"); -MODULE_DEVICES("{{Ensoniq,AudioPCI-97 ES1370}," +MODULE_SUPPORTED_DEVICE("{{Ensoniq,AudioPCI-97 ES1370}," "{Creative Labs,SB PCI64/128 (ES1370)}}"); #endif #ifdef CHIP1371 MODULE_DESCRIPTION("Ensoniq/Creative AudioPCI ES1371+"); -MODULE_DEVICES("{{Ensoniq,AudioPCI ES1371/73}," +MODULE_SUPPORTED_DEVICE("{{Ensoniq,AudioPCI ES1371/73}," "{Ensoniq,AudioPCI ES1373}," "{Creative Labs,Ectiva EV1938}," "{Creative Labs,SB PCI64/128 (ES1371/73)}," @@ -90,22 +87,17 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for Ensoniq AudioPCI soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for Ensoniq AudioPCI soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable Ensoniq AudioPCI soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); #ifdef SUPPORT_JOYSTICK #ifdef CHIP1371 module_param_array(joystick_port, int, boot_devs, 0444); MODULE_PARM_DESC(joystick_port, "Joystick port address."); -MODULE_PARM_SYNTAX(joystick_port, SNDRV_ENABLED ",allows:{{0},{1},{0x200},{0x208},{0x210},{0x218}},dialog:list"); #else module_param_array(joystick, bool, boot_devs, 0444); MODULE_PARM_DESC(joystick, "Enable joystick."); -MODULE_PARM_SYNTAX(joystick, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); #endif #endif /* SUPPORT_JOYSTICK */ @@ -388,7 +380,6 @@ struct _snd_ensoniq { unsigned long capture3size; unsigned long port; - struct resource *res_port; unsigned int mode; unsigned int uartm; /* UART mode */ @@ -435,7 +426,6 @@ struct _snd_ensoniq { unsigned int spdif_stream; #ifdef CHIP1370 - struct snd_dma_device dma_dev; struct snd_dma_buffer dma_bug; #endif @@ -581,7 +571,7 @@ static void snd_es1371_src_write(ensoniq static void snd_es1370_codec_write(ak4531_t *ak4531, unsigned short reg, unsigned short val) { - ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ak4531->private_data, return); + ensoniq_t *ensoniq = ak4531->private_data; unsigned long flags; unsigned long end_time = jiffies + HZ / 10; @@ -611,7 +601,7 @@ static void snd_es1370_codec_write(ak453 static void snd_es1371_codec_write(ac97_t *ac97, unsigned short reg, unsigned short val) { - ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ac97->private_data, return); + ensoniq_t *ensoniq = ac97->private_data; unsigned long flags; unsigned int t, x; @@ -649,7 +639,7 @@ static void snd_es1371_codec_write(ac97_ static unsigned short snd_es1371_codec_read(ac97_t *ac97, unsigned short reg) { - ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ac97->private_data, return -ENXIO); + ensoniq_t *ensoniq = ac97->private_data; unsigned long flags; unsigned int t, x, fail = 0; @@ -844,7 +834,6 @@ static int snd_ensoniq_hw_free(snd_pcm_s static int snd_ensoniq_playback1_prepare(snd_pcm_substream_t * substream) { - unsigned long flags; ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; unsigned int mode = 0; @@ -855,7 +844,7 @@ static int snd_ensoniq_playback1_prepare mode |= 0x02; if (runtime->channels > 1) mode |= 0x01; - spin_lock_irqsave(&ensoniq->reg_lock, flags); + spin_lock_irq(&ensoniq->reg_lock); ensoniq->ctrl &= ~ES_DAC1_EN; outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE)); @@ -878,13 +867,12 @@ static int snd_ensoniq_playback1_prepare snd_es1371_dac1_rate(ensoniq, runtime->rate); #endif outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); - spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + spin_unlock_irq(&ensoniq->reg_lock); return 0; } static int snd_ensoniq_playback2_prepare(snd_pcm_substream_t * substream) { - unsigned long flags; ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; unsigned int mode = 0; @@ -895,7 +883,7 @@ static int snd_ensoniq_playback2_prepare mode |= 0x02; if (runtime->channels > 1) mode |= 0x01; - spin_lock_irqsave(&ensoniq->reg_lock, flags); + spin_lock_irq(&ensoniq->reg_lock); ensoniq->ctrl &= ~ES_DAC2_EN; outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE)); @@ -917,13 +905,12 @@ static int snd_ensoniq_playback2_prepare snd_es1371_dac2_rate(ensoniq, runtime->rate); #endif outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); - spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + spin_unlock_irq(&ensoniq->reg_lock); return 0; } static int snd_ensoniq_capture_prepare(snd_pcm_substream_t * substream) { - unsigned long flags; ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; unsigned int mode = 0; @@ -934,7 +921,7 @@ static int snd_ensoniq_capture_prepare(s mode |= 0x02; if (runtime->channels > 1) mode |= 0x01; - spin_lock_irqsave(&ensoniq->reg_lock, flags); + spin_lock_irq(&ensoniq->reg_lock); ensoniq->ctrl &= ~ES_ADC_EN; outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE)); @@ -954,7 +941,7 @@ static int snd_ensoniq_capture_prepare(s snd_es1371_adc_rate(ensoniq, runtime->rate); #endif outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); - spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + spin_unlock_irq(&ensoniq->reg_lock); return 0; } @@ -1151,31 +1138,29 @@ static int snd_ensoniq_playback1_close(s static int snd_ensoniq_playback2_close(snd_pcm_substream_t * substream) { - unsigned long flags; ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); ensoniq->playback2_substream = NULL; - spin_lock_irqsave(&ensoniq->reg_lock, flags); + spin_lock_irq(&ensoniq->reg_lock); #ifdef CHIP1370 ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_PLAY2; #endif ensoniq->mode &= ~ES_MODE_PLAY2; - spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + spin_unlock_irq(&ensoniq->reg_lock); return 0; } static int snd_ensoniq_capture_close(snd_pcm_substream_t * substream) { - unsigned long flags; ensoniq_t *ensoniq = snd_pcm_substream_chip(substream); ensoniq->capture_substream = NULL; - spin_lock_irqsave(&ensoniq->reg_lock, flags); + spin_lock_irq(&ensoniq->reg_lock); #ifdef CHIP1370 ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_CAPTURE; #endif ensoniq->mode &= ~ES_MODE_CAPTURE; - spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + spin_unlock_irq(&ensoniq->reg_lock); return 0; } @@ -1214,7 +1199,7 @@ static snd_pcm_ops_t snd_ensoniq_capture static void snd_ensoniq_pcm_free(snd_pcm_t *pcm) { - ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, pcm->private_data, return); + ensoniq_t *ensoniq = pcm->private_data; ensoniq->pcm1 = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -1261,7 +1246,7 @@ static int __devinit snd_ensoniq_pcm(ens static void snd_ensoniq_pcm_free2(snd_pcm_t *pcm) { - ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, pcm->private_data, return); + ensoniq_t *ensoniq = pcm->private_data; ensoniq->pcm2 = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -1565,7 +1550,7 @@ static snd_kcontrol_new_t snd_ens1373_li static void snd_ensoniq_mixer_free_ac97(ac97_t *ac97) { - ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ac97->private_data, return); + ensoniq_t *ensoniq = ac97->private_data; ensoniq->u.es1371.ac97 = NULL; } @@ -1585,14 +1570,15 @@ static struct { static int snd_ensoniq_1371_mixer(ensoniq_t * ensoniq) { snd_card_t *card = ensoniq->card; - ac97_bus_t bus, *pbus; - ac97_t ac97; + ac97_bus_t *pbus; + ac97_template_t ac97; int err, idx; + static ac97_bus_ops_t ops = { + .write = snd_es1371_codec_write, + .read = snd_es1371_codec_read, + }; - memset(&bus, 0, sizeof(bus)); - bus.write = snd_es1371_codec_write; - bus.read = snd_es1371_codec_read; - if ((err = snd_ac97_bus(card, &bus, &pbus)) < 0) + if ((err = snd_ac97_bus(card, 0, &ops, NULL, &pbus)) < 0) return err; memset(&ac97, 0, sizeof(ac97)); @@ -1663,30 +1649,28 @@ static int snd_ensoniq_control_info(snd_ static int snd_ensoniq_control_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); - unsigned long flags; int mask = kcontrol->private_value; - spin_lock_irqsave(&ensoniq->reg_lock, flags); + spin_lock_irq(&ensoniq->reg_lock); ucontrol->value.integer.value[0] = ensoniq->ctrl & mask ? 1 : 0; - spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + spin_unlock_irq(&ensoniq->reg_lock); return 0; } static int snd_ensoniq_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { ensoniq_t *ensoniq = snd_kcontrol_chip(kcontrol); - unsigned long flags; int mask = kcontrol->private_value; unsigned int nval; int change; nval = ucontrol->value.integer.value[0] ? mask : 0; - spin_lock_irqsave(&ensoniq->reg_lock, flags); + spin_lock_irq(&ensoniq->reg_lock); change = (ensoniq->ctrl & mask) != nval; ensoniq->ctrl &= ~mask; ensoniq->ctrl |= nval; outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL)); - spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + spin_unlock_irq(&ensoniq->reg_lock); return change; } @@ -1703,7 +1687,7 @@ ENSONIQ_CONTROL("Mic +5V bias", ES_1370_ static void snd_ensoniq_mixer_free_ak4531(ak4531_t *ak4531) { - ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, ak4531->private_data, return); + ensoniq_t *ensoniq = ak4531->private_data; ensoniq->u.es1370.ak4531 = NULL; } @@ -1785,7 +1769,7 @@ static void snd_ensoniq_joystick_free(en static void snd_ensoniq_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) { - ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, entry->private_data, return); + ensoniq_t *ensoniq = entry->private_data; #ifdef CHIP1370 snd_iprintf(buffer, "Ensoniq AudioPCI ES1370\n\n"); @@ -1833,21 +1817,18 @@ static int snd_ensoniq_free(ensoniq_t *e __hw_end: #ifdef CHIP1370 if (ensoniq->dma_bug.area) - snd_dma_free_pages(&ensoniq->dma_dev, &ensoniq->dma_bug); + snd_dma_free_pages(&ensoniq->dma_bug); #endif - if (ensoniq->res_port) { - release_resource(ensoniq->res_port); - kfree_nocheck(ensoniq->res_port); - } if (ensoniq->irq >= 0) free_irq(ensoniq->irq, (void *)ensoniq); - snd_magic_kfree(ensoniq); + pci_release_regions(ensoniq->pci); + kfree(ensoniq); return 0; } static int snd_ensoniq_dev_free(snd_device_t *device) { - ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, device->device_data, return -ENXIO); + ensoniq_t *ensoniq = device->device_data; return snd_ensoniq_free(ensoniq); } @@ -1894,19 +1875,18 @@ static int __devinit snd_ensoniq_create( *rensoniq = NULL; if ((err = pci_enable_device(pci)) < 0) return err; - ensoniq = snd_magic_kcalloc(ensoniq_t, 0, GFP_KERNEL); + ensoniq = kcalloc(1, sizeof(*ensoniq), GFP_KERNEL); if (ensoniq == NULL) return -ENOMEM; spin_lock_init(&ensoniq->reg_lock); ensoniq->card = card; ensoniq->pci = pci; ensoniq->irq = -1; - ensoniq->port = pci_resource_start(pci, 0); - if ((ensoniq->res_port = request_region(ensoniq->port, 0x40, "Ensoniq AudioPCI")) == NULL) { - snd_printk("unable to grab ports 0x%lx-0x%lx\n", ensoniq->port, ensoniq->port + 0x40 - 1); - snd_ensoniq_free(ensoniq); - return -EBUSY; + if ((err = pci_request_regions(pci, "Ensoniq AudioPCI")) < 0) { + kfree(ensoniq); + return err; } + ensoniq->port = pci_resource_start(pci, 0); if (request_irq(pci->irq, snd_audiopci_interrupt, SA_INTERRUPT|SA_SHIRQ, "Ensoniq AudioPCI", (void *)ensoniq)) { snd_printk("unable to grab IRQ %d\n", pci->irq); snd_ensoniq_free(ensoniq); @@ -1914,10 +1894,8 @@ static int __devinit snd_ensoniq_create( } ensoniq->irq = pci->irq; #ifdef CHIP1370 - memset(&ensoniq->dma_dev, 0, sizeof(ensoniq->dma_dev)); - ensoniq->dma_dev.type = SNDRV_DMA_TYPE_DEV; - ensoniq->dma_dev.dev = snd_dma_pci_data(pci); - if (snd_dma_alloc_pages(&ensoniq->dma_dev, 16, &ensoniq->dma_bug) < 0) { + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), + 16, &ensoniq->dma_bug) < 0) { snd_printk("unable to allocate space for phantom area - dma_bug\n"); snd_ensoniq_free(ensoniq); return -EBUSY; @@ -2074,10 +2052,9 @@ static void snd_ensoniq_midi_interrupt(e static int snd_ensoniq_midi_input_open(snd_rawmidi_substream_t * substream) { - unsigned long flags; - ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return -ENXIO); + ensoniq_t *ensoniq = substream->rmidi->private_data; - spin_lock_irqsave(&ensoniq->reg_lock, flags); + spin_lock_irq(&ensoniq->reg_lock); ensoniq->uartm |= ES_MODE_INPUT; ensoniq->midi_input = substream; if (!(ensoniq->uartm & ES_MODE_OUTPUT)) { @@ -2085,16 +2062,15 @@ static int snd_ensoniq_midi_input_open(s outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL)); outl(ensoniq->ctrl |= ES_UART_EN, ES_REG(ensoniq, CONTROL)); } - spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + spin_unlock_irq(&ensoniq->reg_lock); return 0; } static int snd_ensoniq_midi_input_close(snd_rawmidi_substream_t * substream) { - unsigned long flags; - ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return -ENXIO); + ensoniq_t *ensoniq = substream->rmidi->private_data; - spin_lock_irqsave(&ensoniq->reg_lock, flags); + spin_lock_irq(&ensoniq->reg_lock); if (!(ensoniq->uartm & ES_MODE_OUTPUT)) { outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL)); outl(ensoniq->ctrl &= ~ES_UART_EN, ES_REG(ensoniq, CONTROL)); @@ -2103,16 +2079,15 @@ static int snd_ensoniq_midi_input_close( } ensoniq->midi_input = NULL; ensoniq->uartm &= ~ES_MODE_INPUT; - spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + spin_unlock_irq(&ensoniq->reg_lock); return 0; } static int snd_ensoniq_midi_output_open(snd_rawmidi_substream_t * substream) { - unsigned long flags; - ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return -ENXIO); + ensoniq_t *ensoniq = substream->rmidi->private_data; - spin_lock_irqsave(&ensoniq->reg_lock, flags); + spin_lock_irq(&ensoniq->reg_lock); ensoniq->uartm |= ES_MODE_OUTPUT; ensoniq->midi_output = substream; if (!(ensoniq->uartm & ES_MODE_INPUT)) { @@ -2120,16 +2095,15 @@ static int snd_ensoniq_midi_output_open( outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL)); outl(ensoniq->ctrl |= ES_UART_EN, ES_REG(ensoniq, CONTROL)); } - spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + spin_unlock_irq(&ensoniq->reg_lock); return 0; } static int snd_ensoniq_midi_output_close(snd_rawmidi_substream_t * substream) { - unsigned long flags; - ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return -ENXIO); + ensoniq_t *ensoniq = substream->rmidi->private_data; - spin_lock_irqsave(&ensoniq->reg_lock, flags); + spin_lock_irq(&ensoniq->reg_lock); if (!(ensoniq->uartm & ES_MODE_INPUT)) { outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL)); outl(ensoniq->ctrl &= ~ES_UART_EN, ES_REG(ensoniq, CONTROL)); @@ -2138,14 +2112,14 @@ static int snd_ensoniq_midi_output_close } ensoniq->midi_output = NULL; ensoniq->uartm &= ~ES_MODE_OUTPUT; - spin_unlock_irqrestore(&ensoniq->reg_lock, flags); + spin_unlock_irq(&ensoniq->reg_lock); return 0; } static void snd_ensoniq_midi_input_trigger(snd_rawmidi_substream_t * substream, int up) { unsigned long flags; - ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return); + ensoniq_t *ensoniq = substream->rmidi->private_data; int idx; spin_lock_irqsave(&ensoniq->reg_lock, flags); @@ -2169,7 +2143,7 @@ static void snd_ensoniq_midi_input_trigg static void snd_ensoniq_midi_output_trigger(snd_rawmidi_substream_t * substream, int up) { unsigned long flags; - ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, substream->rmidi->private_data, return); + ensoniq_t *ensoniq = substream->rmidi->private_data; unsigned char byte; spin_lock_irqsave(&ensoniq->reg_lock, flags); @@ -2240,7 +2214,7 @@ static int __devinit snd_ensoniq_midi(en static irqreturn_t snd_audiopci_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, dev_id, return IRQ_NONE); + ensoniq_t *ensoniq = dev_id; unsigned int status, sctrl; if (ensoniq == NULL) --- linux-2.6.9-rc1/sound/pci/es1938.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/sound/pci/es1938.c 2004-09-02 20:51:53.000000000 -0700 @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -63,13 +64,10 @@ #include -#define chip_t es1938_t - MODULE_AUTHOR("Jaromir Koutek "); MODULE_DESCRIPTION("ESS Solo-1"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{ESS,ES1938}," +MODULE_SUPPORTED_DEVICE("{{ESS,ES1938}," "{ESS,ES1946}," "{ESS,ES1969}," "{TerraTec,128i PCI}}"); @@ -88,13 +86,10 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for ESS Solo-1 soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for ESS Solo-1 soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable ESS Solo-1 soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); #define SLIO_REG(chip, x) ((chip)->io_port + ESSIO_REG_##x) @@ -202,19 +197,16 @@ MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_ typedef struct _snd_es1938 es1938_t; +#define SAVED_REG_SIZE 32 /* max. number of registers to save */ + struct _snd_es1938 { int irq; unsigned long io_port; - struct resource *res_io_port; unsigned long sb_port; - struct resource *res_sb_port; unsigned long vc_port; - struct resource *res_vc_port; unsigned long mpu_port; - struct resource *res_mpu_port; unsigned long game_port; - struct resource *res_game_port; unsigned long ddma_port; unsigned char irqmask; @@ -227,6 +219,7 @@ struct _snd_es1938 { struct pci_dev *pci; snd_card_t *card; + snd_pcm_t *pcm; snd_pcm_substream_t *capture_substream; snd_pcm_substream_t *playback1_substream; snd_pcm_substream_t *playback2_substream; @@ -248,6 +241,9 @@ struct _snd_es1938 { #if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) struct gameport gameport; #endif +#ifdef CONFIG_PM + unsigned char saved_regs[SAVED_REG_SIZE]; +#endif }; static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id, struct pt_regs *regs); @@ -573,7 +569,12 @@ static int snd_es1938_playback1_trigger( case SNDRV_PCM_TRIGGER_START: /* According to the documentation this should be: 0x13 but that value may randomly swap stereo channels */ + snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL1, 0x92); + udelay(10); snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL1, 0x93); + /* This two stage init gives the FIFO -> DAC connection time to + * settle before first data from DMA flows in. This should ensure + * no swapping of stereo channels. Report a bug if otherwise :-) */ outb(0x0a, SLIO_REG(chip, AUDIO2MODE)); chip->active |= DAC2; break; @@ -690,6 +691,8 @@ static int snd_es1938_playback1_prepare( chip->dma2_shift = 2 - mono - is8; + snd_es1938_reset_fifo(chip); + /* set clock and counters */ snd_es1938_rate_set(chip, substream, DAC2); @@ -874,9 +877,9 @@ static snd_pcm_hardware_t snd_es1938_cap .rate_max = 48000, .channels_min = 1, .channels_max = 2, - .buffer_bytes_max = 65536, + .buffer_bytes_max = 0x8000, /* DMA controller screws on higher values */ .period_bytes_min = 64, - .period_bytes_max = 65536, + .period_bytes_max = 0x8000, .periods_min = 1, .periods_max = 1024, .fifo_size = 256, @@ -896,9 +899,9 @@ static snd_pcm_hardware_t snd_es1938_pla .rate_max = 48000, .channels_min = 1, .channels_max = 2, - .buffer_bytes_max = 65536, + .buffer_bytes_max = 0x8000, /* DMA controller screws on higher values */ .period_bytes_min = 64, - .period_bytes_max = 65536, + .period_bytes_max = 0x8000, .periods_min = 1, .periods_max = 1024, .fifo_size = 256, @@ -998,13 +1001,11 @@ static void snd_es1938_free_pcm(snd_pcm_ snd_pcm_lib_preallocate_free_for_all(pcm); } -static int __devinit snd_es1938_new_pcm(es1938_t *chip, int device, snd_pcm_t ** rpcm) +static int __devinit snd_es1938_new_pcm(es1938_t *chip, int device) { snd_pcm_t *pcm; int err; - if (rpcm) - *rpcm = NULL; if ((err = snd_pcm_new(chip->card, "es-1938-1946", device, 2, 1, &pcm)) < 0) return err; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es1938_playback_ops); @@ -1018,8 +1019,7 @@ static int __devinit snd_es1938_new_pcm( snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), 64*1024, 64*1024); - if (rpcm) - *rpcm = pcm; + chip->pcm = pcm; return 0; } @@ -1129,7 +1129,7 @@ static int snd_es1938_get_hw_switch(snd_ static void snd_es1938_hwv_free(snd_kcontrol_t *kcontrol) { - es1938_t *chip = snd_magic_cast(es1938_t, _snd_kcontrol_chip(kcontrol), return); + es1938_t *chip = snd_kcontrol_chip(kcontrol); chip->master_volume = NULL; chip->master_switch = NULL; chip->hw_volume = NULL; @@ -1340,43 +1340,106 @@ ES1938_SINGLE("Mic Boost (+26dB)", 0, 0x /* ---------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------------- */ +/* + * initialize the chip - used by resume callback, too + */ +static void snd_es1938_chip_init(es1938_t *chip) +{ + /* reset chip */ + snd_es1938_reset(chip); + + /* configure native mode */ + + /* enable bus master */ + pci_set_master(chip->pci); + + /* disable legacy audio */ + pci_write_config_word(chip->pci, SL_PCI_LEGACYCONTROL, 0x805f); + + /* set DDMA base */ + pci_write_config_word(chip->pci, SL_PCI_DDMACONTROL, chip->ddma_port | 1); + + /* set DMA/IRQ policy */ + pci_write_config_dword(chip->pci, SL_PCI_CONFIG, 0); + + /* enable Audio 1, Audio 2, MPU401 IRQ and HW volume IRQ*/ + outb(0xf0, SLIO_REG(chip, IRQCONTROL)); + + /* reset DMA */ + outb(0, SLDM_REG(chip, DMACLEAR)); +} + +#ifdef CONFIG_PM +/* + * PM support + */ + +static unsigned char saved_regs[SAVED_REG_SIZE+1] = { + 0x14, 0x1a, 0x1c, 0x3a, 0x3c, 0x3e, 0x36, 0x38, + 0x50, 0x52, 0x60, 0x61, 0x62, 0x63, 0x64, 0x68, + 0x69, 0x6a, 0x6b, 0x6d, 0x6e, 0x6f, 0x7c, 0x7d, + 0xa8, 0xb4, +}; + + +static int es1938_suspend(snd_card_t *card, unsigned int state) +{ + es1938_t *chip = card->pm_private_data; + unsigned char *s, *d; + + snd_pcm_suspend_all(chip->pcm); + + /* save mixer-related registers */ + for (s = saved_regs, d = chip->saved_regs; *s; s++, d++) + *d = snd_es1938_reg_read(chip, *s); + + outb(0x00, SLIO_REG(chip, IRQCONTROL)); /* disable irqs */ + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + return 0; +} + +static int es1938_resume(snd_card_t *card, unsigned int state) +{ + es1938_t *chip = card->pm_private_data; + unsigned char *s, *d; + + pci_enable_device(chip->pci); + snd_es1938_chip_init(chip); + + /* restore mixer-related registers */ + for (s = saved_regs, d = chip->saved_regs; *s; s++, d++) { + if (*s < 0xa0) + snd_es1938_mixer_write(chip, *s, *d); + else + snd_es1938_write(chip, *s, *d); + } + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; +} +#endif /* CONFIG_PM */ + static int snd_es1938_free(es1938_t *chip) { + /* disable irqs */ + outb(0x00, SLIO_REG(chip, IRQCONTROL)); /*if (chip->rmidi) snd_es1938_mixer_bits(chip, ESSSB_IREG_MPU401CONTROL, 0x40, 0);*/ #if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) if (chip->gameport.io) gameport_unregister_port(&chip->gameport); #endif - if (chip->res_io_port) { - release_resource(chip->res_io_port); - kfree_nocheck(chip->res_io_port); - } - if (chip->res_sb_port) { - release_resource(chip->res_sb_port); - kfree_nocheck(chip->res_sb_port); - } - if (chip->res_vc_port) { - release_resource(chip->res_vc_port); - kfree_nocheck(chip->res_vc_port); - } - if (chip->res_mpu_port) { - release_resource(chip->res_mpu_port); - kfree_nocheck(chip->res_mpu_port); - } - if (chip->res_game_port) { - release_resource(chip->res_game_port); - kfree_nocheck(chip->res_game_port); - } if (chip->irq >= 0) free_irq(chip->irq, (void *)chip); - snd_magic_kfree(chip); + pci_release_regions(chip->pci); + kfree(chip); return 0; } static int snd_es1938_dev_free(snd_device_t *device) { - es1938_t *chip = snd_magic_cast(es1938_t, device->device_data, return -ENXIO); + es1938_t *chip = device->device_data; return snd_es1938_free(chip); } @@ -1402,43 +1465,22 @@ static int __devinit snd_es1938_create(s return -ENXIO; } - chip = snd_magic_kcalloc(es1938_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; spin_lock_init(&chip->reg_lock); spin_lock_init(&chip->mixer_lock); chip->card = card; chip->pci = pci; - chip->io_port = pci_resource_start(pci, 0); - if ((chip->res_io_port = request_region(chip->io_port, 8, "ESS Solo-1")) == NULL) { - snd_printk("unable to grab region 0x%lx-0x%lx\n", chip->io_port, chip->io_port + 8 - 1); - snd_es1938_free(chip); - return -EBUSY; + if ((err = pci_request_regions(pci, "ESS Solo-1")) < 0) { + kfree(chip); + return err; } + chip->io_port = pci_resource_start(pci, 0); chip->sb_port = pci_resource_start(pci, 1); - if ((chip->res_sb_port = request_region(chip->sb_port, 0x10, "ESS Solo-1 SB")) == NULL) { - snd_printk("unable to grab SB region 0x%lx-0x%lx\n", chip->sb_port, chip->sb_port + 0x10 - 1); - snd_es1938_free(chip); - return -EBUSY; - } chip->vc_port = pci_resource_start(pci, 2); - if ((chip->res_vc_port = request_region(chip->vc_port, 0x10, "ESS Solo-1 VC (DMA)")) == NULL) { - snd_printk("unable to grab VC (DMA) region 0x%lx-0x%lx\n", chip->vc_port, chip->vc_port + 0x10 - 1); - snd_es1938_free(chip); - return -EBUSY; - } chip->mpu_port = pci_resource_start(pci, 3); - if ((chip->res_mpu_port = request_region(chip->mpu_port, 4, "ESS Solo-1 MIDI")) == NULL) { - snd_printk("unable to grab MIDI region 0x%lx-0x%lx\n", chip->mpu_port, chip->mpu_port + 4 - 1); - snd_es1938_free(chip); - return -EBUSY; - } chip->game_port = pci_resource_start(pci, 4); - if ((chip->res_game_port = request_region(chip->game_port, 4, "ESS Solo-1 GAME")) == NULL) { - snd_printk("unable to grab GAME region 0x%lx-0x%lx\n", chip->game_port, chip->game_port + 4 - 1); - snd_es1938_free(chip); - return -EBUSY; - } if (request_irq(pci->irq, snd_es1938_interrupt, SA_INTERRUPT|SA_SHIRQ, "ES1938", (void *)chip)) { snd_printk("unable to grab IRQ %d\n", pci->irq); snd_es1938_free(chip); @@ -1449,32 +1491,12 @@ static int __devinit snd_es1938_create(s snd_printk("create: io: 0x%lx, sb: 0x%lx, vc: 0x%lx, mpu: 0x%lx, game: 0x%lx\n", chip->io_port, chip->sb_port, chip->vc_port, chip->mpu_port, chip->game_port); #endif - /* reset chip */ - snd_es1938_reset(chip); - - /* configure native mode */ - - /* enable bus master */ - pci_set_master(pci); - - /* disable legacy audio */ - pci_write_config_word(pci, SL_PCI_LEGACYCONTROL, 0x805f); - /* set DDMA base */ chip->ddma_port = chip->vc_port + 0x00; /* fix from Thomas Sailer */ - pci_write_config_word(pci, SL_PCI_DDMACONTROL, chip->ddma_port | 1); - /* set DMA/IRQ policy */ - pci_write_config_dword(pci, SL_PCI_CONFIG, 0); + snd_es1938_chip_init(chip); - /* enable Audio 1, Audio 2, MPU401 IRQ and HW volume IRQ*/ - outb(0xf0, SLIO_REG(chip, IRQCONTROL)); - - /* reset DMA */ - outb(0, SLDM_REG(chip, DMACLEAR)); - - /* enable bus mastering */ - pci_set_master(pci); + snd_card_set_pm_callback(card, es1938_suspend, es1938_resume, chip); if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { snd_es1938_free(chip); @@ -1492,7 +1514,7 @@ static int __devinit snd_es1938_create(s * -------------------------------------------------------------------- */ static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - es1938_t *chip = snd_magic_cast(es1938_t, dev_id, return IRQ_NONE); + es1938_t *chip = dev_id; unsigned char status, audiostatus; int handled = 0; @@ -1560,19 +1582,15 @@ static irqreturn_t snd_es1938_interrupt( #define ES1938_DMA_SIZE 64 -static int __devinit snd_es1938_mixer(snd_pcm_t *pcm) +static int __devinit snd_es1938_mixer(es1938_t *chip) { snd_card_t *card; - es1938_t *chip; unsigned int idx; int err; - snd_assert(pcm != NULL && pcm->card != NULL, return -EINVAL); + card = chip->card; - card = pcm->card; - chip = snd_pcm_chip(pcm); - - strcpy(card->mixername, pcm->name); + strcpy(card->mixername, "ESS Solo-1"); for (idx = 0; idx < ARRAY_SIZE(snd_es1938_controls); idx++) { snd_kcontrol_t *kctl; @@ -1608,7 +1626,6 @@ static int __devinit snd_es1938_probe(st static int dev; snd_card_t *card; es1938_t *chip; - snd_pcm_t *pcm; opl3_t *opl3; int idx, err; @@ -1641,11 +1658,11 @@ static int __devinit snd_es1938_probe(st chip->revision, chip->irq); - if ((err = snd_es1938_new_pcm(chip, 0, &pcm)) < 0) { + if ((err = snd_es1938_new_pcm(chip, 0)) < 0) { snd_card_free(card); return err; } - if ((err = snd_es1938_mixer(pcm)) < 0) { + if ((err = snd_es1938_mixer(chip)) < 0) { snd_card_free(card); return err; } @@ -1697,6 +1714,7 @@ static struct pci_driver driver = { .id_table = snd_es1938_ids, .probe = snd_es1938_probe, .remove = __devexit_p(snd_es1938_remove), + SND_PCI_PM_CALLBACKS }; static int __init alsa_card_es1938_init(void) --- linux-2.6.9-rc1/sound/pci/es1968.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/sound/pci/es1968.c 2004-09-02 20:51:53.000000000 -0700 @@ -109,15 +109,12 @@ #include #include -#define chip_t es1968_t - #define CARD_NAME "ESS Maestro1/2" #define DRIVER_NAME "ES1968" MODULE_DESCRIPTION("ESS Maestro"); -MODULE_CLASSES("{sound}"); MODULE_LICENSE("GPL"); -MODULE_DEVICES("{{ESS,Maestro 2e}," +MODULE_SUPPORTED_DEVICE("{{ESS,Maestro 2e}," "{ESS,Maestro 2}," "{ESS,Maestro 1}," "{TerraTec,DMX}}"); @@ -142,35 +139,25 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(total_bufsize, int, boot_devs, 0444); MODULE_PARM_DESC(total_bufsize, "Total buffer size in kB."); -MODULE_PARM_SYNTAX(total_bufsize, SNDRV_ENABLED ",allows:{{1,4096}},skill:advanced"); module_param_array(pcm_substreams_p, int, boot_devs, 0444); MODULE_PARM_DESC(pcm_substreams_p, "PCM Playback substreams for " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(pcm_substreams_p, SNDRV_ENABLED ",allows:{{1,8}}"); module_param_array(pcm_substreams_c, int, boot_devs, 0444); MODULE_PARM_DESC(pcm_substreams_c, "PCM Capture substreams for " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(pcm_substreams_c, SNDRV_ENABLED ",allows:{{0,8}}"); module_param_array(clock, int, boot_devs, 0444); MODULE_PARM_DESC(clock, "Clock on " CARD_NAME " soundcard. (0 = auto-detect)"); -MODULE_PARM_SYNTAX(clock, SNDRV_ENABLED); module_param_array(use_pm, int, boot_devs, 0444); MODULE_PARM_DESC(use_pm, "Toggle power-management. (0 = off, 1 = on, 2 = auto)"); -MODULE_PARM_SYNTAX(use_pm, SNDRV_ENABLED ",allows:{{0,1,2}},default:2,skill:advanced"); module_param_array(enable_mpu, int, boot_devs, 0444); MODULE_PARM_DESC(enable_mpu, "Enable MPU401. (0 = off, 1 = on, 2 = auto)"); -MODULE_PARM_SYNTAX(enable_mpu, SNDRV_ENABLED ",allows:{{0,2}},default:2"); #ifdef SUPPORT_JOYSTICK module_param_array(joystick, bool, boot_devs, 0444); MODULE_PARM_DESC(joystick, "Enable joystick."); -MODULE_PARM_SYNTAX(joystick, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); #endif @@ -522,9 +509,7 @@ enum { /* DMA Hack! */ struct snd_esm_memory { - char *buf; - unsigned long addr; - int size; + struct snd_dma_buffer buf; int empty; /* status */ struct list_head list; }; @@ -573,13 +558,11 @@ struct snd_es1968 { unsigned int clock; /* clock */ /* buffer */ - struct snd_dma_device dma_dev; struct snd_dma_buffer dma; /* Resources... */ int irq; unsigned long io_port; - struct resource *res_io_port; int type; struct pci_dev *pci; snd_card_t *card; @@ -696,7 +679,7 @@ static int snd_es1968_ac97_wait(es1968_t static void snd_es1968_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val) { - es1968_t *chip = snd_magic_cast(es1968_t, ac97->private_data, return); + es1968_t *chip = ac97->private_data; unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); @@ -715,7 +698,7 @@ static void snd_es1968_ac97_write(ac97_t static unsigned short snd_es1968_ac97_read(ac97_t *ac97, unsigned short reg) { u16 data = 0; - es1968_t *chip = snd_magic_cast(es1968_t, ac97->private_data, return 0); + es1968_t *chip = ac97->private_data; unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); @@ -1078,10 +1061,10 @@ static void snd_es1968_playback_setup(es for (channel = 0; channel <= high_apu; channel++) { apu = es->apu[channel]; - snd_es1968_program_wavecache(chip, es, channel, es->memory->addr, 0); + snd_es1968_program_wavecache(chip, es, channel, es->memory->buf.addr, 0); /* Offset to PCMBAR */ - pa = es->memory->addr; + pa = es->memory->buf.addr; pa -= chip->dma.addr; pa >>= 1; /* words */ @@ -1230,20 +1213,20 @@ static void snd_es1968_capture_setup(es1 /* input mixer (left/mono) */ /* parallel in crap, see maestro reg 0xC [8-11] */ init_capture_apu(chip, es, 2, - es->mixbuf->addr, ESM_MIXBUF_SIZE/4, /* in words */ + es->mixbuf->buf.addr, ESM_MIXBUF_SIZE/4, /* in words */ ESM_APU_INPUTMIXER, 0x14); /* SRC (left/mono); get input from inputing apu */ - init_capture_apu(chip, es, 0, es->memory->addr, size, + init_capture_apu(chip, es, 0, es->memory->buf.addr, size, ESM_APU_SRCONVERTOR, es->apu[2]); if (es->fmt & ESS_FMT_STEREO) { /* input mixer (right) */ init_capture_apu(chip, es, 3, - es->mixbuf->addr + ESM_MIXBUF_SIZE/2, + es->mixbuf->buf.addr + ESM_MIXBUF_SIZE/2, ESM_MIXBUF_SIZE/4, /* in words */ ESM_APU_INPUTMIXER, 0x15); /* SRC (right) */ init_capture_apu(chip, es, 1, - es->memory->addr + size*2, size, + es->memory->buf.addr + size*2, size, ESM_APU_SRCONVERTOR, es->apu[3]); } @@ -1281,7 +1264,7 @@ static int snd_es1968_pcm_prepare(snd_pc { es1968_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - esschan_t *es = snd_magic_cast(esschan_t, runtime->private_data, return -ENXIO); + esschan_t *es = runtime->private_data; es->dma_size = snd_pcm_lib_buffer_bytes(substream); es->frag_size = snd_pcm_lib_period_bytes(substream); @@ -1312,10 +1295,9 @@ static int snd_es1968_pcm_prepare(snd_pc static int snd_es1968_pcm_trigger(snd_pcm_substream_t *substream, int cmd) { es1968_t *chip = snd_pcm_substream_chip(substream); - esschan_t *es = snd_magic_cast(esschan_t, substream->runtime->private_data, return -ENXIO); - unsigned long flags; + esschan_t *es = substream->runtime->private_data; - spin_lock_irqsave(&chip->substream_lock, flags); + spin_lock(&chip->substream_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: @@ -1336,14 +1318,14 @@ static int snd_es1968_pcm_trigger(snd_pc snd_es1968_bob_dec(chip); break; } - spin_unlock_irqrestore(&chip->substream_lock, flags); + spin_unlock(&chip->substream_lock); return 0; } static snd_pcm_uframes_t snd_es1968_pcm_pointer(snd_pcm_substream_t *substream) { es1968_t *chip = snd_pcm_substream_chip(substream); - esschan_t *es = snd_magic_cast(esschan_t, substream->runtime->private_data, return -ENXIO); + esschan_t *es = substream->runtime->private_data; unsigned int ptr; ptr = snd_es1968_get_dma_ptr(chip, es) << es->wav_shift; @@ -1408,8 +1390,8 @@ static int calc_available_memory_size(es down(&chip->memory_mutex); list_for_each(p, &chip->buf_list) { esm_memory_t *buf = list_entry(p, esm_memory_t, list); - if (buf->empty && buf->size > max_size) - max_size = buf->size; + if (buf->empty && buf->buf.bytes > max_size) + max_size = buf->buf.bytes; } up(&chip->memory_mutex); if (max_size >= 128*1024) @@ -1427,24 +1409,25 @@ static esm_memory_t *snd_es1968_new_memo down(&chip->memory_mutex); list_for_each(p, &chip->buf_list) { buf = list_entry(p, esm_memory_t, list); - if (buf->empty && buf->size >= size) + if (buf->empty && buf->buf.bytes >= size) goto __found; } up(&chip->memory_mutex); return NULL; __found: - if (buf->size > size) { + if (buf->buf.bytes > size) { esm_memory_t *chunk = kmalloc(sizeof(*chunk), GFP_KERNEL); if (chunk == NULL) { up(&chip->memory_mutex); return NULL; } - chunk->size = buf->size - size; - chunk->buf = buf->buf + size; - chunk->addr = buf->addr + size; + chunk->buf = buf->buf; + chunk->buf.bytes -= size; + chunk->buf.area += size; + chunk->buf.addr += size; chunk->empty = 1; - buf->size = size; + buf->buf.bytes = size; list_add(&chunk->list, &buf->list); } buf->empty = 0; @@ -1462,7 +1445,7 @@ static void snd_es1968_free_memory(es196 if (buf->list.prev != &chip->buf_list) { chunk = list_entry(buf->list.prev, esm_memory_t, list); if (chunk->empty) { - chunk->size += buf->size; + chunk->buf.bytes += buf->buf.bytes; list_del(&buf->list); kfree(buf); buf = chunk; @@ -1471,7 +1454,7 @@ static void snd_es1968_free_memory(es196 if (buf->list.next != &chip->buf_list) { chunk = list_entry(buf->list.next, esm_memory_t, list); if (chunk->empty) { - buf->size += chunk->size; + buf->buf.bytes += chunk->buf.bytes; list_del(&chunk->list); kfree(chunk); } @@ -1485,7 +1468,7 @@ static void snd_es1968_free_dmabuf(es196 if (! chip->dma.area) return; - snd_dma_free_reserved(&chip->dma_dev); + snd_dma_reserve_buf(&chip->dma, snd_dma_pci_buf_id(chip->pci)); while ((p = chip->buf_list.next) != &chip->buf_list) { esm_memory_t *chunk = list_entry(p, esm_memory_t, list); list_del(p); @@ -1499,22 +1482,22 @@ snd_es1968_init_dmabuf(es1968_t *chip) int err; esm_memory_t *chunk; - chip->dma_dev.type = SNDRV_DMA_TYPE_DEV; - chip->dma_dev.dev = snd_dma_pci_data(chip->pci); - chip->dma_dev.id = 0; - if (! snd_dma_get_reserved(&chip->dma_dev, &chip->dma)) { - err = snd_dma_alloc_pages_fallback(&chip->dma_dev, chip->total_bufsize, &chip->dma); + chip->dma.dev.type = SNDRV_DMA_TYPE_DEV; + chip->dma.dev.dev = snd_dma_pci_data(chip->pci); + if (! snd_dma_get_reserved_buf(&chip->dma, snd_dma_pci_buf_id(chip->pci))) { + err = snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + chip->total_bufsize, &chip->dma); if (err < 0 || ! chip->dma.area) { snd_printk("es1968: can't allocate dma pages for size %d\n", chip->total_bufsize); return -ENOMEM; } if ((chip->dma.addr + chip->dma.bytes - 1) & ~((1 << 28) - 1)) { - snd_dma_free_pages(&chip->dma_dev, &chip->dma); + snd_dma_free_pages(&chip->dma); snd_printk("es1968: DMA buffer beyond 256MB.\n"); return -ENOMEM; } - snd_dma_set_reserved(&chip->dma_dev, &chip->dma); } INIT_LIST_HEAD(&chip->buf_list); @@ -1525,9 +1508,10 @@ snd_es1968_init_dmabuf(es1968_t *chip) return -ENOMEM; } memset(chip->dma.area, 0, ESM_MEM_ALIGN); - chunk->buf = chip->dma.area + ESM_MEM_ALIGN; - chunk->addr = chip->dma.addr + ESM_MEM_ALIGN; - chunk->size = chip->dma.bytes - ESM_MEM_ALIGN; + chunk->buf = chip->dma; + chunk->buf.area += ESM_MEM_ALIGN; + chunk->buf.addr += ESM_MEM_ALIGN; + chunk->buf.bytes -= ESM_MEM_ALIGN; chunk->empty = 1; list_add(&chunk->list, &chip->buf_list); @@ -1541,11 +1525,11 @@ static int snd_es1968_hw_params(snd_pcm_ { es1968_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - esschan_t *chan = snd_magic_cast(esschan_t, runtime->private_data, return -ENXIO); + esschan_t *chan = runtime->private_data; int size = params_buffer_bytes(hw_params); if (chan->memory) { - if (chan->memory->size >= size) { + if (chan->memory->buf.bytes >= size) { runtime->dma_bytes = size; return 0; } @@ -1556,9 +1540,7 @@ static int snd_es1968_hw_params(snd_pcm_ // snd_printd("cannot allocate dma buffer: size = %d\n", size); return -ENOMEM; } - runtime->dma_bytes = size; - runtime->dma_area = chan->memory->buf; - runtime->dma_addr = chan->memory->addr; + snd_pcm_set_runtime_buffer(substream, &chan->memory->buf); return 1; /* area was changed */ } @@ -1571,7 +1553,7 @@ static int snd_es1968_hw_free(snd_pcm_su if (runtime->private_data == NULL) return 0; - chan = snd_magic_cast(esschan_t, runtime->private_data, return -ENXIO); + chan = runtime->private_data; if (chan->memory) { snd_es1968_free_memory(chip, chan->memory); chan->memory = NULL; @@ -1616,14 +1598,13 @@ static int snd_es1968_playback_open(snd_ snd_pcm_runtime_t *runtime = substream->runtime; esschan_t *es; int apu1; - unsigned long flags; /* search 2 APUs */ apu1 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_PLAY); if (apu1 < 0) return apu1; - es = snd_magic_kcalloc(esschan_t, 0, GFP_KERNEL); + es = kcalloc(1, sizeof(*es), GFP_KERNEL); if (!es) { snd_es1968_free_apu_pair(chip, apu1); return -ENOMEM; @@ -1645,9 +1626,9 @@ static int snd_es1968_playback_open(snd_ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 1024); #endif - spin_lock_irqsave(&chip->substream_lock, flags); + spin_lock_irq(&chip->substream_lock); list_add(&es->list, &chip->substream_list); - spin_unlock_irqrestore(&chip->substream_lock, flags); + spin_unlock_irq(&chip->substream_lock); return 0; } @@ -1658,7 +1639,6 @@ static int snd_es1968_capture_open(snd_p es1968_t *chip = snd_pcm_substream_chip(substream); esschan_t *es; int apu1, apu2; - unsigned long flags; apu1 = snd_es1968_alloc_apu_pair(chip, ESM_APU_PCM_CAPTURE); if (apu1 < 0) @@ -1669,7 +1649,7 @@ static int snd_es1968_capture_open(snd_p return apu2; } - es = snd_magic_kcalloc(esschan_t, 0, GFP_KERNEL); + es = kcalloc(1, sizeof(*es), GFP_KERNEL); if (!es) { snd_es1968_free_apu_pair(chip, apu1); snd_es1968_free_apu_pair(chip, apu2); @@ -1692,10 +1672,10 @@ static int snd_es1968_capture_open(snd_p if ((es->mixbuf = snd_es1968_new_memory(chip, ESM_MIXBUF_SIZE)) == NULL) { snd_es1968_free_apu_pair(chip, apu1); snd_es1968_free_apu_pair(chip, apu2); - snd_magic_kfree(es); + kfree(es); return -ENOMEM; } - memset(es->mixbuf->buf, 0, ESM_MIXBUF_SIZE); + memset(es->mixbuf->buf.area, 0, ESM_MIXBUF_SIZE); runtime->private_data = es; runtime->hw = snd_es1968_capture; @@ -1705,9 +1685,9 @@ static int snd_es1968_capture_open(snd_p snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 1024); #endif - spin_lock_irqsave(&chip->substream_lock, flags); + spin_lock_irq(&chip->substream_lock); list_add(&es->list, &chip->substream_list); - spin_unlock_irqrestore(&chip->substream_lock, flags); + spin_unlock_irq(&chip->substream_lock); return 0; } @@ -1716,16 +1696,15 @@ static int snd_es1968_playback_close(snd { es1968_t *chip = snd_pcm_substream_chip(substream); esschan_t *es; - unsigned long flags; if (substream->runtime->private_data == NULL) return 0; - es = snd_magic_cast(esschan_t, substream->runtime->private_data, return -ENXIO); - spin_lock_irqsave(&chip->substream_lock, flags); + es = substream->runtime->private_data; + spin_lock_irq(&chip->substream_lock); list_del(&es->list); - spin_unlock_irqrestore(&chip->substream_lock, flags); + spin_unlock_irq(&chip->substream_lock); snd_es1968_free_apu_pair(chip, es->apu[0]); - snd_magic_kfree(es); + kfree(es); return 0; } @@ -1734,18 +1713,17 @@ static int snd_es1968_capture_close(snd_ { es1968_t *chip = snd_pcm_substream_chip(substream); esschan_t *es; - unsigned long flags; if (substream->runtime->private_data == NULL) return 0; - es = snd_magic_cast(esschan_t, substream->runtime->private_data, return -ENXIO); - spin_lock_irqsave(&chip->substream_lock, flags); + es = substream->runtime->private_data; + spin_lock_irq(&chip->substream_lock); list_del(&es->list); - spin_unlock_irqrestore(&chip->substream_lock, flags); + spin_unlock_irq(&chip->substream_lock); snd_es1968_free_memory(chip, es->mixbuf); snd_es1968_free_apu_pair(chip, es->apu[0]); snd_es1968_free_apu_pair(chip, es->apu[2]); - snd_magic_kfree(es); + kfree(es); return 0; } @@ -1783,7 +1761,6 @@ static void __devinit es1968_measure_clo int i, apu; unsigned int pa, offset, t; esm_memory_t *memory; - unsigned long flags; struct timeval start_time, stop_time; if (chip->clock == 0) @@ -1800,11 +1777,11 @@ static void __devinit es1968_measure_clo return; } - memset(memory->buf, 0, CLOCK_MEASURE_BUFSIZE); + memset(memory->buf.area, 0, CLOCK_MEASURE_BUFSIZE); - wave_set_register(chip, apu << 3, (memory->addr - 0x10) & 0xfff8); + wave_set_register(chip, apu << 3, (memory->buf.addr - 0x10) & 0xfff8); - pa = (unsigned int)((memory->addr - chip->dma.addr) >> 1); + pa = (unsigned int)((memory->buf.addr - chip->dma.addr) >> 1); pa |= 0x00400000; /* System RAM (Bit 22) */ /* initialize apu */ @@ -1820,18 +1797,18 @@ static void __devinit es1968_measure_clo apu_set_register(chip, apu, 9, 0xD000); apu_set_register(chip, apu, 10, 0x8F08); apu_set_register(chip, apu, 11, 0x0000); - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); outw(1, chip->io_port + 0x04); /* clear WP interrupts */ outw(inw(chip->io_port + ESM_PORT_HOST_IRQ) | ESM_HIRQ_DSIE, chip->io_port + ESM_PORT_HOST_IRQ); /* enable WP ints */ - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); snd_es1968_apu_set_freq(chip, apu, ((unsigned int)48000 << 16) / chip->clock); /* 48000 Hz */ - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); __apu_set_register(chip, apu, 5, pa & 0xffff); snd_es1968_trigger_apu(chip, apu, ESM_APU_16BITLINEAR); do_gettimeofday(&start_time); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); #if 0 set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ / 20); /* 50 msec */ @@ -1842,11 +1819,11 @@ static void __devinit es1968_measure_clo */ mdelay(50); #endif - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); offset = __apu_get_register(chip, apu, 5); do_gettimeofday(&stop_time); snd_es1968_trigger_apu(chip, apu, 0); /* stop */ - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); /* check the current position */ offset -= (pa & 0xffff); @@ -1879,7 +1856,7 @@ static void __devinit es1968_measure_clo static void snd_es1968_pcm_free(snd_pcm_t *pcm) { - es1968_t *esm = snd_magic_cast(es1968_t, pcm->private_data, return); + es1968_t *esm = pcm->private_data; snd_es1968_free_dmabuf(esm); esm->pcm = NULL; } @@ -1952,7 +1929,7 @@ static void snd_es1968_update_pcm(es1968 */ static void es1968_update_hw_volume(unsigned long private_data) { - es1968_t *chip = snd_magic_cast(es1968_t, (void*)private_data, return); + es1968_t *chip = (es1968_t *) private_data; int x, val; /* Figure out which volume control button was pushed, @@ -1990,8 +1967,6 @@ static void es1968_update_hw_volume(unsi if ((val & 0xff00) < 0x1f00) val += 0x0100; } - if (val == 0x1f1f) - val |= 0x8000; snd_ac97_write_cache(chip->ac97, AC97_MASTER, val); snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id); @@ -2003,7 +1978,7 @@ static void es1968_update_hw_volume(unsi */ static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - es1968_t *chip = snd_magic_cast(es1968_t, dev_id, return IRQ_NONE); + es1968_t *chip = dev_id; u32 event; if (!(event = inb(chip->io_port + 0x1A))) @@ -2042,15 +2017,16 @@ static irqreturn_t snd_es1968_interrupt( static int __devinit snd_es1968_mixer(es1968_t *chip) { - ac97_bus_t bus, *pbus; - ac97_t ac97; + ac97_bus_t *pbus; + ac97_template_t ac97; snd_ctl_elem_id_t id; int err; + static ac97_bus_ops_t ops = { + .write = snd_es1968_ac97_write, + .read = snd_es1968_ac97_read, + }; - memset(&bus, 0, sizeof(bus)); - bus.write = snd_es1968_ac97_write; - bus.read = snd_es1968_ac97_read; - if ((err = snd_ac97_bus(chip->card, &bus, &pbus)) < 0) + if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0) return err; memset(&ac97, 0, sizeof(ac97)); @@ -2420,7 +2396,7 @@ static void snd_es1968_start_irq(es1968_ */ static int es1968_suspend(snd_card_t *card, unsigned int state) { - es1968_t *chip = snd_magic_cast(es1968_t, card->pm_private_data, return -EINVAL); + es1968_t *chip = card->pm_private_data; if (! chip->do_pm) return 0; @@ -2435,7 +2411,7 @@ static int es1968_suspend(snd_card_t *ca static int es1968_resume(snd_card_t *card, unsigned int state) { - es1968_t *chip = snd_magic_cast(es1968_t, card->pm_private_data, return -EINVAL); + es1968_t *chip = card->pm_private_data; if (! chip->do_pm) return 0; @@ -2466,12 +2442,14 @@ static int es1968_resume(snd_card_t *car static int snd_es1968_free(es1968_t *chip) { - if (chip->res_io_port) { + if (chip->io_port) { synchronize_irq(chip->irq); outw(1, chip->io_port + 0x04); /* clear WP interrupts */ outw(0, chip->io_port + ESM_PORT_HOST_IRQ); /* disable IRQ */ } + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); #ifdef SUPPORT_JOYSTICK if (chip->res_joystick) { gameport_unregister_port(&chip->gameport); @@ -2482,19 +2460,14 @@ static int snd_es1968_free(es1968_t *chi snd_es1968_set_acpi(chip, ACPI_D3); chip->master_switch = NULL; chip->master_volume = NULL; - if (chip->res_io_port) { - release_resource(chip->res_io_port); - kfree_nocheck(chip->res_io_port); - } - if (chip->irq >= 0) - free_irq(chip->irq, (void *)chip); - snd_magic_kfree(chip); + pci_release_regions(chip->pci); + kfree(chip); return 0; } static int snd_es1968_dev_free(snd_device_t *device) { - es1968_t *chip = snd_magic_cast(es1968_t, device->device_data, return -ENXIO); + es1968_t *chip = device->device_data; return snd_es1968_free(chip); } @@ -2542,7 +2515,7 @@ static int __devinit snd_es1968_create(s return -ENXIO; } - chip = (es1968_t *) snd_magic_kcalloc(es1968_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (! chip) return -ENOMEM; @@ -2561,12 +2534,11 @@ static int __devinit snd_es1968_create(s chip->playback_streams = play_streams; chip->capture_streams = capt_streams; - chip->io_port = pci_resource_start(pci, 0); - if ((chip->res_io_port = request_region(chip->io_port, 0x100, "ESS Maestro")) == NULL) { - snd_printk("unable to grab region 0x%lx-0x%lx\n", chip->io_port, chip->io_port + 0x100 - 1); - snd_es1968_free(chip); - return -EBUSY; + if ((err = pci_request_regions(pci, "ESS Maestro")) < 0) { + kfree(chip); + return err; } + chip->io_port = pci_resource_start(pci, 0); if (request_irq(pci->irq, snd_es1968_interrupt, SA_INTERRUPT|SA_SHIRQ, "ESS Maestro", (void*)chip)) { snd_printk("unable to grab IRQ %d\n", pci->irq); --- linux-2.6.9-rc1/sound/pci/fm801.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/sound/pci/fm801.c 2004-09-02 20:51:53.000000000 -0700 @@ -40,13 +40,10 @@ #define TEA575X_RADIO 1 #endif -#define chip_t fm801_t - MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("ForteMedia FM801"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{ForteMedia,FM801}," +MODULE_SUPPORTED_DEVICE("{{ForteMedia,FM801}," "{Genius,SoundMaker Live 5.1}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ @@ -64,16 +61,12 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for the FM801 soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for the FM801 soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable FM801 soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(tea575x_tuner, bool, boot_devs, 0444); MODULE_PARM_DESC(tea575x_tuner, "Enable TEA575x tuner."); -MODULE_PARM_SYNTAX(tea575x_tuner, SNDRV_ENABLE_DESC); /* * Direct registers @@ -161,7 +154,6 @@ struct _snd_fm801 { int irq; unsigned long port; /* I/O port number */ - struct resource *res_port; unsigned int multichannel: 1, /* multichannel support */ secondary: 1; /* secondary codec */ unsigned char secondary_addr; /* address of the secondary codec */ @@ -233,7 +225,7 @@ static void snd_fm801_codec_write(ac97_t unsigned short reg, unsigned short val) { - fm801_t *chip = snd_magic_cast(fm801_t, ac97->private_data, return); + fm801_t *chip = ac97->private_data; int idx; /* @@ -264,7 +256,7 @@ static void snd_fm801_codec_write(ac97_t static unsigned short snd_fm801_codec_read(ac97_t *ac97, unsigned short reg) { - fm801_t *chip = snd_magic_cast(fm801_t, ac97->private_data, return -ENXIO); + fm801_t *chip = ac97->private_data; int idx; /* @@ -308,10 +300,8 @@ static unsigned int rates[] = { 38400, 44100, 48000 }; -#define RATES sizeof(rates) / sizeof(rates[0]) - static snd_pcm_hw_constraint_list_t hw_constraints_rates = { - .count = RATES, + .count = ARRAY_SIZE(rates), .list = rates, .mask = 0, }; @@ -336,11 +326,11 @@ static unsigned short snd_fm801_rate_bit { unsigned int idx; - for (idx = 0; idx < 11; idx++) + for (idx = 0; idx < ARRAY_SIZE(rates); idx++) if (rates[idx] == rate) return idx; snd_BUG(); - return RATES - 1; + return ARRAY_SIZE(rates) - 1; } /* @@ -487,42 +477,40 @@ static int snd_fm801_capture_prepare(snd static snd_pcm_uframes_t snd_fm801_playback_pointer(snd_pcm_substream_t * substream) { fm801_t *chip = snd_pcm_substream_chip(substream); - unsigned long flags; size_t ptr; if (!(chip->ply_ctrl & FM801_START)) return 0; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock(&chip->reg_lock); ptr = chip->ply_pos + (chip->ply_count - 1) - inw(FM801_REG(chip, PLY_COUNT)); if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_PLAYBACK) { ptr += chip->ply_count; ptr %= chip->ply_size; } - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock(&chip->reg_lock); return bytes_to_frames(substream->runtime, ptr); } static snd_pcm_uframes_t snd_fm801_capture_pointer(snd_pcm_substream_t * substream) { fm801_t *chip = snd_pcm_substream_chip(substream); - unsigned long flags; size_t ptr; if (!(chip->cap_ctrl & FM801_START)) return 0; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock(&chip->reg_lock); ptr = chip->cap_pos + (chip->cap_count - 1) - inw(FM801_REG(chip, CAP_COUNT)); if (inw(FM801_REG(chip, IRQ_STATUS)) & FM801_IRQ_CAPTURE) { ptr += chip->cap_count; ptr %= chip->cap_size; } - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock(&chip->reg_lock); return bytes_to_frames(substream->runtime, ptr); } static irqreturn_t snd_fm801_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - fm801_t *chip = snd_magic_cast(fm801_t, dev_id, return IRQ_NONE); + fm801_t *chip = dev_id; unsigned short status; unsigned int tmp; @@ -680,7 +668,7 @@ static snd_pcm_ops_t snd_fm801_capture_o static void snd_fm801_pcm_free(snd_pcm_t *pcm) { - fm801_t *chip = snd_magic_cast(fm801_t, pcm->private_data, return); + fm801_t *chip = pcm->private_data; chip->pcm = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -1076,10 +1064,10 @@ static int snd_fm801_get_double(snd_kcon int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; - spin_lock(&chip->reg_lock); + spin_lock_irq(&chip->reg_lock); ucontrol->value.integer.value[0] = (inw(chip->port + reg) >> shift_left) & mask; ucontrol->value.integer.value[1] = (inw(chip->port + reg) >> shift_right) & mask; - spin_unlock(&chip->reg_lock); + spin_unlock_irq(&chip->reg_lock); if (invert) { ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; @@ -1176,13 +1164,13 @@ FM801_SINGLE("IEC958 Playback Switch", F static void snd_fm801_mixer_free_ac97_bus(ac97_bus_t *bus) { - fm801_t *chip = snd_magic_cast(fm801_t, bus->private_data, return); + fm801_t *chip = bus->private_data; chip->ac97_bus = NULL; } static void snd_fm801_mixer_free_ac97(ac97_t *ac97) { - fm801_t *chip = snd_magic_cast(fm801_t, ac97->private_data, return); + fm801_t *chip = ac97->private_data; if (ac97->num == 0) { chip->ac97 = NULL; } else { @@ -1192,18 +1180,17 @@ static void snd_fm801_mixer_free_ac97(ac static int __devinit snd_fm801_mixer(fm801_t *chip) { - ac97_bus_t bus; - ac97_t ac97; + ac97_template_t ac97; unsigned int i; int err; + static ac97_bus_ops_t ops = { + .write = snd_fm801_codec_write, + .read = snd_fm801_codec_read, + }; - memset(&bus, 0, sizeof(bus)); - bus.write = snd_fm801_codec_write; - bus.read = snd_fm801_codec_read; - bus.private_data = chip; - bus.private_free = snd_fm801_mixer_free_ac97_bus; - if ((err = snd_ac97_bus(chip->card, &bus, &chip->ac97_bus)) < 0) + if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0) return err; + chip->ac97_bus->private_free = snd_fm801_mixer_free_ac97_bus; memset(&ac97, 0, sizeof(ac97)); ac97.private_data = chip; @@ -1245,20 +1232,17 @@ static int snd_fm801_free(fm801_t *chip) #ifdef TEA575X_RADIO snd_tea575x_exit(&chip->tea); #endif - if (chip->res_port) { - release_resource(chip->res_port); - kfree_nocheck(chip->res_port); - } if (chip->irq >= 0) free_irq(chip->irq, (void *)chip); + pci_release_regions(chip->pci); - snd_magic_kfree(chip); + kfree(chip); return 0; } static int snd_fm801_dev_free(snd_device_t *device) { - fm801_t *chip = snd_magic_cast(fm801_t, device->device_data, return -ENXIO); + fm801_t *chip = device->device_data; return snd_fm801_free(chip); } @@ -1279,19 +1263,18 @@ static int __devinit snd_fm801_create(sn *rchip = NULL; if ((err = pci_enable_device(pci)) < 0) return err; - chip = snd_magic_kcalloc(fm801_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; spin_lock_init(&chip->reg_lock); chip->card = card; chip->pci = pci; chip->irq = -1; - chip->port = pci_resource_start(pci, 0); - if ((chip->res_port = request_region(chip->port, 0x80, "FM801")) == NULL) { - snd_printk("unable to grab region 0x%lx-0x%lx\n", chip->port, chip->port + 0x80 - 1); - snd_fm801_free(chip); - return -EBUSY; + if ((err = pci_request_regions(pci, "FM801")) < 0) { + kfree(chip); + return err; } + chip->port = pci_resource_start(pci, 0); if (request_irq(pci->irq, snd_fm801_interrupt, SA_INTERRUPT|SA_SHIRQ, "FM801", (void *)chip)) { snd_printk("unable to grab IRQ %d\n", chip->irq); snd_fm801_free(chip); --- linux-2.6.9-rc1/sound/pci/ice1712/ak4xxx.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/sound/pci/ice1712/ak4xxx.c 2004-09-02 20:51:53.000000000 -0700 @@ -33,7 +33,6 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("ICEnsemble ICE17xx <-> AK4xxx AD/DA chip interface"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); static void snd_ice1712_akm4xxx_lock(akm4xxx_t *ak, int chip) { --- linux-2.6.9-rc1/sound/pci/ice1712/aureon.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/sound/pci/ice1712/aureon.c 2004-09-02 20:51:53.000000000 -0700 @@ -75,7 +75,7 @@ #define WM_DAC_ATTEN 0x00 /* DAC1-8 analog attenuation */ #define WM_DAC_MASTER_ATTEN 0x08 /* DAC master analog attenuation */ #define WM_DAC_DIG_ATTEN 0x09 /* DAC1-8 digital attenuation */ -#define WM_DAC_DIG_MATER_ATTEN 0x11 /* DAC master digital attenuation */ +#define WM_DAC_DIG_MASTER_ATTEN 0x11 /* DAC master digital attenuation */ #define WM_PHASE_SWAP 0x12 /* DAC phase */ #define WM_DAC_CTRL1 0x13 /* DAC control bits */ #define WM_MUTE 0x14 /* mute controls */ @@ -149,20 +149,27 @@ static unsigned short wm_get(ice1712_t * } /* + * set the register value of WM codec + */ +static void wm_put_nocache(ice1712_t *ice, int reg, unsigned short val) +{ + aureon_spi_write(ice, AUREON_WM_CS, (reg << 9) | (val & 0x1ff), 16); +} + +/* * set the register value of WM codec and remember it */ static void wm_put(ice1712_t *ice, int reg, unsigned short val) { - aureon_spi_write(ice, AUREON_WM_CS, (reg << 9) | (val & 0x1ff), 16); + wm_put_nocache(ice, reg, val); reg <<= 1; ice->akm[0].images[reg] = val >> 8; ice->akm[0].images[reg + 1] = val; } /* - * DAC mute control */ -static int wm_dac_mute_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +static int aureon_mono_bool_info(snd_kcontrol_t *k, snd_ctl_elem_info_t *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = 1; @@ -171,6 +178,11 @@ static int wm_dac_mute_info(snd_kcontrol return 0; } +/* + * DAC mute control + */ +#define wm_dac_mute_info aureon_mono_bool_info + static int wm_dac_mute_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { ice1712_t *ice = snd_kcontrol_chip(kcontrol); @@ -205,9 +217,10 @@ static int wm_dac_mute_put(snd_kcontrol_ */ static int wm_dac_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) { + int voices = kcontrol->private_value >> 8; uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = 0; /* mute */ + uinfo->count = voices; + uinfo->value.integer.min = 0; /* mute (-101dB) */ uinfo->value.integer.max = 101; /* 0dB */ return 0; } @@ -215,19 +228,20 @@ static int wm_dac_vol_info(snd_kcontrol_ static int wm_dac_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { ice1712_t *ice = snd_kcontrol_chip(kcontrol); - int idx; + int i, idx, ofs, voices; unsigned short vol; + voices = kcontrol->private_value >> 8; + ofs = kcontrol->private_value & 0xff; down(&ice->gpio_mutex); - if (kcontrol->private_value) - idx = WM_DAC_MASTER_ATTEN; - else - idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + WM_DAC_ATTEN; - vol = wm_get(ice, idx) & 0x7f; - if (vol <= 0x1a) - ucontrol->value.integer.value[0] = 0; - else - ucontrol->value.integer.value[0] = vol - 0x1a; + for (i = 0; i < voices; i++) { + idx = WM_DAC_ATTEN + ofs + i; + vol = wm_get(ice, idx) & 0x7f; + if (vol <= 0x1a) + ucontrol->value.integer.value[i] = 0; + else + ucontrol->value.integer.value[i] = vol - 0x1a; + } up(&ice->gpio_mutex); return 0; } @@ -235,23 +249,69 @@ static int wm_dac_vol_get(snd_kcontrol_t static int wm_dac_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { ice1712_t *ice = snd_kcontrol_chip(kcontrol); - int idx; + int i, idx, ofs, voices; unsigned short ovol, nvol; - int change; + int change = 0; + voices = kcontrol->private_value >> 8; + ofs = kcontrol->private_value & 0xff; snd_ice1712_save_gpio_status(ice); - if (kcontrol->private_value) - idx = WM_DAC_MASTER_ATTEN; - else - idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + WM_DAC_ATTEN; - nvol = ucontrol->value.integer.value[0] + 0x1a; - ovol = wm_get(ice, idx) & 0x7f; - change = (ovol != nvol); - if (change) { - if (nvol <= 0x1a && ovol <= 0x1a) - change = 0; - else - wm_put(ice, idx, nvol | 0x180); /* update on zero detect */ + for (i = 0; i < voices; i++) { + idx = WM_DAC_ATTEN + ofs + i; + nvol = ucontrol->value.integer.value[i] + 0x1a; + ovol = wm_get(ice, idx) & 0x7f; + if (ovol != nvol) { + if (nvol <= 0x1a && ovol <= 0x1a) + continue; + wm_put(ice, idx, nvol | 0x80); /* zero-detect, prelatch */ + wm_put_nocache(ice, idx, nvol | 0x180); /* update */ + change = 1; + } + } + snd_ice1712_restore_gpio_status(ice); + return change; +} + +/* digital master volume */ +#define MASTER_0dB 0xff +#define MASTER_RES 128 /* -64dB */ +#define MASTER_MIN (MASTER_0dB - MASTER_RES) +static int wm_master_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; /* mute (-64dB) */ + uinfo->value.integer.max = MASTER_RES; /* 0dB */ + return 0; +} + +static int wm_master_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned short val; + + down(&ice->gpio_mutex); + val = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff; + val = val > MASTER_MIN ? (val - MASTER_MIN) : 0; + ucontrol->value.integer.value[0] = val; + up(&ice->gpio_mutex); + return 0; +} + +static int wm_master_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned short ovol, nvol; + int change = 0; + + snd_ice1712_save_gpio_status(ice); + nvol = ucontrol->value.integer.value[0]; + nvol = (nvol ? (nvol + MASTER_MIN) : 0) & 0xff; + ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff; + if (ovol != nvol) { + wm_put(ice, WM_DAC_DIG_MASTER_ATTEN, nvol); /* prelatch */ + wm_put_nocache(ice, WM_DAC_DIG_MASTER_ATTEN, nvol | 0x100); /* update */ + change = 1; } snd_ice1712_restore_gpio_status(ice); return change; @@ -260,10 +320,10 @@ static int wm_dac_vol_put(snd_kcontrol_t /* * ADC mute control */ -static int wm_adc_mute_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +static int wm_adc_mute_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; + uinfo->count = 2; uinfo->value.integer.min = 0; uinfo->value.integer.max = 1; return 0; @@ -273,10 +333,13 @@ static int wm_adc_mute_get(snd_kcontrol_ { ice1712_t *ice = snd_kcontrol_chip(kcontrol); unsigned short val; + int i; down(&ice->gpio_mutex); - val = wm_get(ice, snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)+WM_ADC_GAIN); - ucontrol->value.integer.value[0] = ~val>>5 & 0x1; + for (i = 0; i < 2; i++) { + val = wm_get(ice, WM_ADC_GAIN + i); + ucontrol->value.integer.value[i] = ~val>>5 & 0x1; + } up(&ice->gpio_mutex); return 0; } @@ -285,14 +348,17 @@ static int wm_adc_mute_put(snd_kcontrol_ { ice1712_t *ice = snd_kcontrol_chip(kcontrol); unsigned short new, old; - int change; + int i, change = 0; snd_ice1712_save_gpio_status(ice); - old = wm_get(ice, snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)+WM_ADC_GAIN); - new = (~ucontrol->value.integer.value[0]<<5&0x20) | (old&~0x20); - change = (new != old); - if (change) - wm_put(ice, snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)+WM_ADC_GAIN, new); + for (i = 0; i < 2; i++) { + old = wm_get(ice, WM_ADC_GAIN + i); + new = (~ucontrol->value.integer.value[i]<<5&0x20) | (old&~0x20); + if (new != old) { + wm_put(ice, snd_ctl_get_ioffidx(kcontrol, &ucontrol->id)+WM_ADC_GAIN, new); + change = 1; + } + } snd_ice1712_restore_gpio_status(ice); return change; @@ -304,7 +370,7 @@ static int wm_adc_mute_put(snd_kcontrol_ static int wm_adc_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; + uinfo->count = 2; uinfo->value.integer.min = 0; /* -12dB */ uinfo->value.integer.max = 0x1f; /* 19dB */ return 0; @@ -313,13 +379,15 @@ static int wm_adc_vol_info(snd_kcontrol_ static int wm_adc_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { ice1712_t *ice = snd_kcontrol_chip(kcontrol); - int idx; + int i, idx; unsigned short vol; down(&ice->gpio_mutex); - idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + WM_ADC_GAIN; - vol = wm_get(ice, idx) & 0x1f; - ucontrol->value.integer.value[0] = vol; + for (i = 0; i < 2; i++) { + idx = WM_ADC_GAIN + i; + vol = wm_get(ice, idx) & 0x1f; + ucontrol->value.integer.value[i] = vol; + } up(&ice->gpio_mutex); return 0; } @@ -327,17 +395,20 @@ static int wm_adc_vol_get(snd_kcontrol_t static int wm_adc_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { ice1712_t *ice = snd_kcontrol_chip(kcontrol); - int idx; + int i, idx; unsigned short ovol, nvol; - int change; + int change = 0; snd_ice1712_save_gpio_status(ice); - idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) + WM_ADC_GAIN; - nvol = ucontrol->value.integer.value[0]; - ovol = wm_get(ice, idx); - change = ((ovol & 0x1f) != nvol); - if (change) - wm_put(ice, idx, nvol | (ovol & ~0x1f)); + for (i = 0; i < 2; i++) { + idx = WM_ADC_GAIN + i; + nvol = ucontrol->value.integer.value[i]; + ovol = wm_get(ice, idx); + if ((ovol & 0x1f) != nvol) { + wm_put(ice, idx, nvol | (ovol & ~0x1f)); + change = 1; + } + } snd_ice1712_restore_gpio_status(ice); return change; } @@ -355,7 +426,7 @@ static int wm_adc_mux_info(snd_kcontrol_ "AC97" //AIN5 }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; + uinfo->count = 2; uinfo->value.enumerated.items = 5; if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; @@ -420,14 +491,7 @@ static int aureon_get_headphone_amp(ice1 return ( tmp & AUREON_HP_SEL )!= 0; } -static int aureon_bool_info(snd_kcontrol_t *k, snd_ctl_elem_info_t *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define aureon_hpamp_info aureon_mono_bool_info static int aureon_hpamp_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { @@ -448,6 +512,9 @@ static int aureon_hpamp_put(snd_kcontrol /* * Deemphasis */ + +#define aureon_deemp_info aureon_mono_bool_info + static int aureon_deemp_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { ice1712_t *ice = snd_kcontrol_chip(kcontrol); @@ -519,44 +586,67 @@ static int aureon_oversampling_put(snd_k * mixers */ -static snd_kcontrol_new_t aureon51_dac_control __devinitdata = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "DAC Volume", - .count = 6, - .info = wm_dac_vol_info, - .get = wm_dac_vol_get, - .put = wm_dac_vol_put, -}; - -static snd_kcontrol_new_t aureon71_dac_control __devinitdata = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "DAC Volume", - .count = 8, - .info = wm_dac_vol_info, - .get = wm_dac_vol_get, - .put = wm_dac_vol_put, -}; - -static snd_kcontrol_new_t wm_controls[] __devinitdata = { +static snd_kcontrol_new_t aureon_dac_controls[] __devinitdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Switch", - .info = wm_dac_mute_info, - .get = wm_dac_mute_get, - .put = wm_dac_mute_put, + .name = "Front Playback Volume", + .info = wm_dac_vol_info, + .get = wm_dac_vol_get, + .put = wm_dac_vol_put, + .private_value = (2 << 8) | 0 }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Master Playback Volume", + .name = "Rear Playback Volume", .info = wm_dac_vol_info, .get = wm_dac_vol_get, .put = wm_dac_vol_put, - .private_value = 1, + .private_value = (2 << 8) | 2 }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "ADC Switch", - .count = 2, + .name = "Center Playback Volume", + .info = wm_dac_vol_info, + .get = wm_dac_vol_get, + .put = wm_dac_vol_put, + .private_value = (1 << 8) | 4 + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "LFE Playback Volume", + .info = wm_dac_vol_info, + .get = wm_dac_vol_get, + .put = wm_dac_vol_put, + .private_value = (1 << 8) | 5 + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Side Playback Volume", + .info = wm_dac_vol_info, + .get = wm_dac_vol_get, + .put = wm_dac_vol_put, + .private_value = (2 << 8) | 6 + } +}; + +static snd_kcontrol_new_t wm_controls[] __devinitdata = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = wm_dac_mute_info, + .get = wm_dac_mute_get, + .put = wm_dac_mute_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Volume", + .info = wm_master_vol_info, + .get = wm_master_vol_get, + .put = wm_master_vol_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Switch", .info = wm_adc_mute_info, .get = wm_adc_mute_get, .put = wm_adc_mute_put, @@ -564,15 +654,14 @@ static snd_kcontrol_new_t wm_controls[] }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "ADC Volume", - .count = 2, + .name = "Capture Volume", .info = wm_adc_vol_info, .get = wm_adc_vol_get, .put = wm_adc_vol_put, }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Route", + .name = "Capture Source", .info = wm_adc_mux_info, .get = wm_adc_mux_get, .put = wm_adc_mux_put, @@ -580,14 +669,14 @@ static snd_kcontrol_new_t wm_controls[] { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Headphone Amplifier Switch", - .info = aureon_bool_info, + .info = aureon_hpamp_info, .get = aureon_hpamp_get, .put = aureon_hpamp_put }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "DAC Deemphasis Switch", - .info = aureon_bool_info, + .info = aureon_deemp_info, .get = aureon_deemp_get, .put = aureon_deemp_put }, @@ -603,15 +692,17 @@ static snd_kcontrol_new_t wm_controls[] static int __devinit aureon_add_controls(ice1712_t *ice) { - unsigned int i; + unsigned int i, counts; int err; + counts = ARRAY_SIZE(aureon_dac_controls); if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON51_SKY) - err = snd_ctl_add(ice->card, snd_ctl_new1(&aureon51_dac_control, ice)); - else - err = snd_ctl_add(ice->card, snd_ctl_new1(&aureon71_dac_control, ice)); - if (err < 0) - return err; + counts--; /* no side */ + for (i = 0; i < counts; i++) { + err = snd_ctl_add(ice->card, snd_ctl_new1(&aureon_dac_controls[i], ice)); + if (err < 0) + return err; + } for (i = 0; i < ARRAY_SIZE(wm_controls); i++) { err = snd_ctl_add(ice->card, snd_ctl_new1(&wm_controls[i], ice)); @@ -718,15 +809,15 @@ static int __devinit aureon_init(ice1712 if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON51_SKY) { ice->num_total_dacs = 6; - ice->num_total_adcs = 6; + ice->num_total_adcs = 2; } else { /* aureon 7.1 and prodigy 7.1 */ ice->num_total_dacs = 8; - ice->num_total_adcs = 8; + ice->num_total_adcs = 2; } /* to remeber the register values */ - ice->akm = snd_kcalloc(sizeof(akm4xxx_t), GFP_KERNEL); + ice->akm = kcalloc(1, sizeof(akm4xxx_t), GFP_KERNEL); if (! ice->akm) return -ENOMEM; ice->akm_codecs = 1; @@ -780,7 +871,7 @@ static int __devinit aureon_init(ice1712 */ static unsigned char aureon51_eeprom[] __devinitdata = { - 0x2a, /* SYSCONF: clock 512, mpu401, spdif-in/ADC, 3DACs */ + 0x0a, /* SYSCONF: clock 512, spdif-in/ADC, 3DACs */ 0x80, /* ACLINK: I2S */ 0xf8, /* I2S: vol, 96k, 24bit, 192k */ 0xc3, /* SPDIF: out-en, out-int, spdif-in */ @@ -796,7 +887,7 @@ static unsigned char aureon51_eeprom[] _ }; static unsigned char aureon71_eeprom[] __devinitdata = { - 0x2b, /* SYSCONF: clock 512, mpu401, spdif-in/ADC, 4DACs */ + 0x0b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */ 0x80, /* ACLINK: I2S */ 0xf8, /* I2S: vol, 96k, 24bit, 192k */ 0xc3, /* SPDIF: out-en, out-int, spdif-in */ @@ -812,7 +903,7 @@ static unsigned char aureon71_eeprom[] _ }; static unsigned char prodigy71_eeprom[] __devinitdata = { - 0x2b, /* SYSCONF: clock 512, mpu401, spdif-in/ADC, 4DACs */ + 0x0b, /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */ 0x80, /* ACLINK: I2S */ 0xf8, /* I2S: vol, 96k, 24bit, 192k */ 0xc3, /* SPDIF: out-en, out-int, spdif-in */ @@ -837,6 +928,7 @@ struct snd_ice1712_card_info snd_vt1724_ .build_controls = aureon_add_controls, .eeprom_size = sizeof(aureon51_eeprom), .eeprom_data = aureon51_eeprom, + .driver = "Aureon51", }, { .subvendor = VT1724_SUBDEVICE_AUREON71_SPACE, @@ -846,6 +938,7 @@ struct snd_ice1712_card_info snd_vt1724_ .build_controls = aureon_add_controls, .eeprom_size = sizeof(aureon71_eeprom), .eeprom_data = aureon71_eeprom, + .driver = "Aureon71", }, { .subvendor = VT1724_SUBDEVICE_AUREON71_UNIVERSE, @@ -855,6 +948,7 @@ struct snd_ice1712_card_info snd_vt1724_ .build_controls = aureon_add_controls, .eeprom_size = sizeof(aureon71_eeprom), .eeprom_data = aureon71_eeprom, + .driver = "Aureon71", }, { .subvendor = VT1724_SUBDEVICE_PRODIGY71, @@ -864,6 +958,7 @@ struct snd_ice1712_card_info snd_vt1724_ .build_controls = aureon_add_controls, .eeprom_size = sizeof(prodigy71_eeprom), .eeprom_data = prodigy71_eeprom, + .driver = "Prodigy71", /* should be identical with Aureon71 */ }, { } /* terminator */ }; --- linux-2.6.9-rc1/sound/pci/ice1712/delta.c 2004-08-24 00:01:54.000000000 -0700 +++ 25/sound/pci/ice1712/delta.c 2004-09-02 20:51:53.000000000 -0700 @@ -126,7 +126,7 @@ static void ap_cs8427_codec_deassert(ice /* sequential write */ static int ap_cs8427_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count) { - ice1712_t *ice = snd_magic_cast(ice1712_t, device->bus->private_data, return -EIO); + ice1712_t *ice = device->bus->private_data; int res = count; unsigned char tmp; @@ -143,7 +143,7 @@ static int ap_cs8427_sendbytes(snd_i2c_d /* sequential read */ static int ap_cs8427_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count) { - ice1712_t *ice = snd_magic_cast(ice1712_t, device->bus->private_data, return -EIO); + ice1712_t *ice = device->bus->private_data; int res = count; unsigned char tmp; --- linux-2.6.9-rc1/sound/pci/ice1712/ews.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/sound/pci/ice1712/ews.c 2004-09-02 20:51:53.000000000 -0700 @@ -45,7 +45,7 @@ /* send SDA and SCL */ static void ewx_i2c_setlines(snd_i2c_bus_t *bus, int clk, int data) { - ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return); + ice1712_t *ice = bus->private_data; unsigned char tmp = 0; if (clk) tmp |= ICE1712_EWX2496_SERIAL_CLOCK; @@ -57,13 +57,13 @@ static void ewx_i2c_setlines(snd_i2c_bus static int ewx_i2c_getclock(snd_i2c_bus_t *bus) { - ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return -EIO); + ice1712_t *ice = bus->private_data; return snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_EWX2496_SERIAL_CLOCK ? 1 : 0; } static int ewx_i2c_getdata(snd_i2c_bus_t *bus, int ack) { - ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return -EIO); + ice1712_t *ice = bus->private_data; int bit; /* set RW pin to low */ snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~ICE1712_EWX2496_RW); @@ -80,7 +80,7 @@ static int ewx_i2c_getdata(snd_i2c_bus_t static void ewx_i2c_start(snd_i2c_bus_t *bus) { - ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return); + ice1712_t *ice = bus->private_data; unsigned char mask; snd_ice1712_save_gpio_status(ice); @@ -99,13 +99,13 @@ static void ewx_i2c_start(snd_i2c_bus_t static void ewx_i2c_stop(snd_i2c_bus_t *bus) { - ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return); + ice1712_t *ice = bus->private_data; snd_ice1712_restore_gpio_status(ice); } static void ewx_i2c_direction(snd_i2c_bus_t *bus, int clock, int data) { - ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return); + ice1712_t *ice = bus->private_data; unsigned char mask = 0; if (clock) @@ -223,6 +223,7 @@ static void snd_ice1712_ews_cs8404_spdif switch (ice->eeprom.subvendor) { case ICE1712_SUBDEVICE_EWS88MT: case ICE1712_SUBDEVICE_EWS88MT_NEW: + case ICE1712_SUBDEVICE_PHASE88: snd_runtime_check(snd_i2c_sendbytes(ice->cs8404, &bits, 1) == 1, goto _error); break; case ICE1712_SUBDEVICE_EWS88D: @@ -410,6 +411,7 @@ static int __devinit snd_ice1712_ews_ini break; case ICE1712_SUBDEVICE_EWS88MT: case ICE1712_SUBDEVICE_EWS88MT_NEW: + case ICE1712_SUBDEVICE_PHASE88: ice->num_total_dacs = 8; ice->num_total_adcs = 8; break; @@ -440,6 +442,7 @@ static int __devinit snd_ice1712_ews_ini break; case ICE1712_SUBDEVICE_EWS88MT: case ICE1712_SUBDEVICE_EWS88MT_NEW: + case ICE1712_SUBDEVICE_PHASE88: if ((err = snd_i2c_device_create(ice->i2c, "CS8404", ICE1712_EWS88MT_CS8404_ADDR, &ice->cs8404)) < 0) return err; if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (1st)", ICE1712_EWS88MT_INPUT_ADDR, &ice->i2cdevs[0])) < 0) @@ -470,6 +473,7 @@ static int __devinit snd_ice1712_ews_ini break; case ICE1712_SUBDEVICE_EWS88MT: case ICE1712_SUBDEVICE_EWS88MT_NEW: + case ICE1712_SUBDEVICE_PHASE88: case ICE1712_SUBDEVICE_EWS88D: /* set up CS8404 */ ice->spdif.ops.open = ews88_open_spdif; @@ -498,6 +502,7 @@ static int __devinit snd_ice1712_ews_ini switch (ice->eeprom.subvendor) { case ICE1712_SUBDEVICE_EWS88MT: case ICE1712_SUBDEVICE_EWS88MT_NEW: + case ICE1712_SUBDEVICE_PHASE88: err = snd_ice1712_akm4xxx_init(ak, &akm_ews88mt, &akm_ews88mt_priv, ice); break; case ICE1712_SUBDEVICE_EWX2496: @@ -924,6 +929,7 @@ static int __devinit snd_ice1712_ews_add case ICE1712_SUBDEVICE_EWX2496: case ICE1712_SUBDEVICE_EWS88MT: case ICE1712_SUBDEVICE_EWS88MT_NEW: + case ICE1712_SUBDEVICE_PHASE88: case ICE1712_SUBDEVICE_DMX6FIRE: err = snd_ice1712_akm4xxx_build_controls(ice); if (err < 0) @@ -942,6 +948,7 @@ static int __devinit snd_ice1712_ews_add break; case ICE1712_SUBDEVICE_EWS88MT: case ICE1712_SUBDEVICE_EWS88MT_NEW: + case ICE1712_SUBDEVICE_PHASE88: err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88mt_input_sense, ice)); if (err < 0) return err; @@ -992,6 +999,13 @@ struct snd_ice1712_card_info snd_ice1712 .build_controls = snd_ice1712_ews_add_controls, }, { + .subvendor = ICE1712_SUBDEVICE_PHASE88, + .name = "TerraTec Phase88", + .model = "phase88", + .chip_init = snd_ice1712_ews_init, + .build_controls = snd_ice1712_ews_add_controls, + }, + { .subvendor = ICE1712_SUBDEVICE_EWS88D, .name = "TerraTec EWS88D", .model = "ews88d", --- linux-2.6.9-rc1/sound/pci/ice1712/ews.h 2004-08-24 00:03:31.000000000 -0700 +++ 25/sound/pci/ice1712/ews.h 2004-09-02 20:51:53.000000000 -0700 @@ -29,13 +29,15 @@ "{TerraTec,EWX 24/96},"\ "{TerraTec,EWS 88MT},"\ "{TerraTec,EWS 88D},"\ - "{TerraTec,DMX 6Fire}," + "{TerraTec,DMX 6Fire},"\ + "{TerraTec,Phase 88}," #define ICE1712_SUBDEVICE_EWX2496 0x3b153011 #define ICE1712_SUBDEVICE_EWS88MT 0x3b151511 #define ICE1712_SUBDEVICE_EWS88MT_NEW 0x3b152511 #define ICE1712_SUBDEVICE_EWS88D 0x3b152b11 #define ICE1712_SUBDEVICE_DMX6FIRE 0x3b153811 +#define ICE1712_SUBDEVICE_PHASE88 0x3b155111 /* entry point */ extern struct snd_ice1712_card_info snd_ice1712_ews_cards[]; --- linux-2.6.9-rc1/sound/pci/ice1712/ice1712.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/sound/pci/ice1712/ice1712.c 2004-09-02 20:51:53.000000000 -0700 @@ -73,8 +73,7 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("ICEnsemble ICE1712 (Envy24)"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{" +MODULE_SUPPORTED_DEVICE("{" HOONTECH_DEVICE_DESC DELTA_DEVICE_DESC EWS_DEVICE_DESC @@ -91,19 +90,14 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for ICE1712 soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for ICE1712 soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable ICE1712 soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(omni, bool, boot_devs, 0444); MODULE_PARM_DESC(omni, "Enable Midiman M-Audio Delta Omni I/O support."); -MODULE_PARM_SYNTAX(omni, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); module_param_array(cs8427_timeout, int, boot_devs, 0444); MODULE_PARM_DESC(cs8427_timeout, "Define reset timeout for cs8427 chip in msec resolution."); -MODULE_PARM_SYNTAX(cs8427_timeout, SNDRV_ENABLED ", allows:{{1,1000}},default=500,skill:advanced"); module_param_array(model, charp, boot_devs, 0444); MODULE_PARM_DESC(model, "Use the given board model."); @@ -416,7 +410,7 @@ int __devinit snd_ice1712_init_cs8427(ic static irqreturn_t snd_ice1712_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - ice1712_t *ice = snd_magic_cast(ice1712_t, dev_id, return IRQ_NONE); + ice1712_t *ice = dev_id; unsigned char status; int handled = 0; @@ -590,7 +584,7 @@ static int snd_ice1712_playback_prepare( rate = (runtime->rate * 8192) / 375; if (rate > 0x000fffff) rate = 0x000fffff; - spin_lock(&ice->reg_lock); + spin_lock_irq(&ice->reg_lock); outb(0, ice->ddma_port + 15); outb(ICE1712_DMA_MODE_WRITE | ICE1712_DMA_AUTOINIT, ice->ddma_port + 0x0b); outl(runtime->dma_addr, ice->ddma_port + 0); @@ -603,7 +597,7 @@ static int snd_ice1712_playback_prepare( snd_ice1712_write(ice, ICE1712_IREG_PBK_COUNT_HI, period_size >> 8); snd_ice1712_write(ice, ICE1712_IREG_PBK_LEFT, 0); snd_ice1712_write(ice, ICE1712_IREG_PBK_RIGHT, 0); - spin_unlock(&ice->reg_lock); + spin_unlock_irq(&ice->reg_lock); return 0; } @@ -626,7 +620,7 @@ static int snd_ice1712_playback_ds_prepa ice->playback_con_active_buf[substream->number] = 0; ice->playback_con_virt_addr[substream->number] = runtime->dma_addr; chn = substream->number * 2; - spin_lock(&ice->reg_lock); + spin_lock_irq(&ice->reg_lock); snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR0, runtime->dma_addr); snd_ice1712_ds_write(ice, chn, ICE1712_DSC_COUNT0, period_size); snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR1, runtime->dma_addr + (runtime->periods > 1 ? period_size + 1 : 0)); @@ -638,7 +632,7 @@ static int snd_ice1712_playback_ds_prepa snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_RATE, rate); snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_VOLUME, 0); } - spin_unlock(&ice->reg_lock); + spin_unlock_irq(&ice->reg_lock); return 0; } @@ -656,13 +650,13 @@ static int snd_ice1712_capture_prepare(s tmp &= ~0x04; if (runtime->channels == 2) tmp &= ~0x02; - spin_lock(&ice->reg_lock); + spin_lock_irq(&ice->reg_lock); outl(ice->capture_con_virt_addr = runtime->dma_addr, ICEREG(ice, CONCAP_ADDR)); outw(buf_size, ICEREG(ice, CONCAP_COUNT)); snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_HI, period_size >> 8); snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_LO, period_size & 0xff); snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp); - spin_unlock(&ice->reg_lock); + spin_unlock_irq(&ice->reg_lock); snd_ac97_set_rate(ice->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); return 0; } @@ -874,7 +868,7 @@ static snd_pcm_ops_t snd_ice1712_capture static void snd_ice1712_pcm_free(snd_pcm_t *pcm) { - ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return); + ice1712_t *ice = pcm->private_data; ice->pcm = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -912,7 +906,7 @@ static int __devinit snd_ice1712_pcm(ice static void snd_ice1712_pcm_free_ds(snd_pcm_t *pcm) { - ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return); + ice1712_t *ice = pcm->private_data; ice->pcm_ds = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -952,10 +946,8 @@ static int __devinit snd_ice1712_pcm_ds( static unsigned int rates[] = { 8000, 9600, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000 }; -#define RATES sizeof(rates) / sizeof(rates[0]) - static snd_pcm_hw_constraint_list_t hw_constraints_rates = { - .count = RATES, + .count = ARRAY_SIZE(rates), .list = rates, .mask = 0, }; @@ -1077,11 +1069,11 @@ static int snd_ice1712_playback_pro_prep ice1712_t *ice = snd_pcm_substream_chip(substream); ice->playback_pro_size = snd_pcm_lib_buffer_bytes(substream); - spin_lock(&ice->reg_lock); + spin_lock_irq(&ice->reg_lock); outl(substream->runtime->dma_addr, ICEMT(ice, PLAYBACK_ADDR)); outw((ice->playback_pro_size >> 2) - 1, ICEMT(ice, PLAYBACK_SIZE)); outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, PLAYBACK_COUNT)); - spin_unlock(&ice->reg_lock); + spin_unlock_irq(&ice->reg_lock); return 0; } @@ -1100,11 +1092,11 @@ static int snd_ice1712_capture_pro_prepa ice1712_t *ice = snd_pcm_substream_chip(substream); ice->capture_pro_size = snd_pcm_lib_buffer_bytes(substream); - spin_lock(&ice->reg_lock); + spin_lock_irq(&ice->reg_lock); outl(substream->runtime->dma_addr, ICEMT(ice, CAPTURE_ADDR)); outw((ice->capture_pro_size >> 2) - 1, ICEMT(ice, CAPTURE_SIZE)); outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, CAPTURE_COUNT)); - spin_unlock(&ice->reg_lock); + spin_unlock_irq(&ice->reg_lock); return 0; } @@ -1238,7 +1230,7 @@ static int snd_ice1712_capture_pro_close static void snd_ice1712_pcm_profi_free(snd_pcm_t *pcm) { - ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return); + ice1712_t *ice = pcm->private_data; ice->pcm_pro = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -1511,21 +1503,26 @@ static int __devinit snd_ice1712_build_p static void snd_ice1712_mixer_free_ac97(ac97_t *ac97) { - ice1712_t *ice = snd_magic_cast(ice1712_t, ac97->private_data, return); + ice1712_t *ice = ac97->private_data; ice->ac97 = NULL; } static int __devinit snd_ice1712_ac97_mixer(ice1712_t * ice) { - int err; - ac97_t ac97; - ac97_bus_t bus, *pbus; + int err, bus_num = 0; + ac97_template_t ac97; + ac97_bus_t *pbus; + static ac97_bus_ops_t con_ops = { + .write = snd_ice1712_ac97_write, + .read = snd_ice1712_ac97_read, + }; + static ac97_bus_ops_t pro_ops = { + .write = snd_ice1712_pro_ac97_write, + .read = snd_ice1712_pro_ac97_read, + }; if (ice_has_con_ac97(ice)) { - memset(&bus, 0, sizeof(bus)); - bus.write = snd_ice1712_ac97_write; - bus.read = snd_ice1712_ac97_read; - if ((err = snd_ac97_bus(ice->card, &bus, &pbus)) < 0) + if ((err = snd_ac97_bus(ice->card, bus_num++, &con_ops, NULL, &pbus)) < 0) return err; memset(&ac97, 0, sizeof(ac97)); ac97.private_data = ice; @@ -1540,10 +1537,7 @@ static int __devinit snd_ice1712_ac97_mi } if (! (ice->eeprom.data[ICE_EEP1_ACLINK] & ICE1712_CFG_PRO_I2S)) { - memset(&bus, 0, sizeof(bus)); - bus.write = snd_ice1712_pro_ac97_write; - bus.read = snd_ice1712_pro_ac97_read; - if ((err = snd_ac97_bus(ice->card, &bus, &pbus)) < 0) + if ((err = snd_ac97_bus(ice->card, bus_num, &pro_ops, NULL, &pbus)) < 0) return err; memset(&ac97, 0, sizeof(ac97)); ac97.private_data = ice; @@ -1570,7 +1564,7 @@ static inline unsigned int eeprom_double static void snd_ice1712_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) { - ice1712_t *ice = snd_magic_cast(ice1712_t, entry->private_data, return); + ice1712_t *ice = entry->private_data; unsigned int idx; snd_iprintf(buffer, "%s\n\n", ice->card->longname); @@ -2468,7 +2462,7 @@ static int __devinit snd_ice1712_build_c static int snd_ice1712_free(ice1712_t *ice) { - if (ice->res_port == NULL) + if (! ice->port) goto __hw_end; /* mask all interrupts */ outb(0xc0, ICEMT(ice, IRQ)); @@ -2479,30 +2473,16 @@ static int snd_ice1712_free(ice1712_t *i synchronize_irq(ice->irq); free_irq(ice->irq, (void *) ice); } - if (ice->res_port) { - release_resource(ice->res_port); - kfree_nocheck(ice->res_port); - } - if (ice->res_ddma_port) { - release_resource(ice->res_ddma_port); - kfree_nocheck(ice->res_ddma_port); - } - if (ice->res_dmapath_port) { - release_resource(ice->res_dmapath_port); - kfree_nocheck(ice->res_dmapath_port); - } - if (ice->res_profi_port) { - release_resource(ice->res_profi_port); - kfree_nocheck(ice->res_profi_port); - } + if (ice->port) + pci_release_regions(ice->pci); snd_ice1712_akm4xxx_free(ice); - snd_magic_kfree(ice); + kfree(ice); return 0; } static int snd_ice1712_dev_free(snd_device_t *device) { - ice1712_t *ice = snd_magic_cast(ice1712_t, device->device_data, return -ENXIO); + ice1712_t *ice = device->device_data; return snd_ice1712_free(ice); } @@ -2531,7 +2511,7 @@ static int __devinit snd_ice1712_create( return -ENXIO; } - ice = snd_magic_kcalloc(ice1712_t, 0, GFP_KERNEL); + ice = kcalloc(1, sizeof(*ice), GFP_KERNEL); if (ice == NULL) return -ENOMEM; ice->omni = omni ? 1 : 0; @@ -2555,36 +2535,21 @@ static int __devinit snd_ice1712_create( ice->card = card; ice->pci = pci; ice->irq = -1; - ice->port = pci_resource_start(pci, 0); - ice->ddma_port = pci_resource_start(pci, 1); - ice->dmapath_port = pci_resource_start(pci, 2); - ice->profi_port = pci_resource_start(pci, 3); pci_set_master(pci); pci_write_config_word(ice->pci, 0x40, 0x807f); pci_write_config_word(ice->pci, 0x42, 0x0006); snd_ice1712_proc_init(ice); synchronize_irq(pci->irq); - if ((ice->res_port = request_region(ice->port, 32, "ICE1712 - Controller")) == NULL) { - snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->port, ice->port + 32 - 1); - snd_ice1712_free(ice); - return -EIO; - } - if ((ice->res_ddma_port = request_region(ice->ddma_port, 16, "ICE1712 - DDMA")) == NULL) { - snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->ddma_port, ice->ddma_port + 16 - 1); - snd_ice1712_free(ice); - return -EIO; - } - if ((ice->res_dmapath_port = request_region(ice->dmapath_port, 16, "ICE1712 - DMA path")) == NULL) { - snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->dmapath_port, ice->dmapath_port + 16 - 1); - snd_ice1712_free(ice); - return -EIO; - } - if ((ice->res_profi_port = request_region(ice->profi_port, 64, "ICE1712 - Professional")) == NULL) { - snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->profi_port, ice->profi_port + 16 - 1); - snd_ice1712_free(ice); - return -EIO; + if ((err = pci_request_regions(pci, "ICE1712")) < 0) { + kfree(ice); + return err; } + ice->port = pci_resource_start(pci, 0); + ice->ddma_port = pci_resource_start(pci, 1); + ice->dmapath_port = pci_resource_start(pci, 2); + ice->profi_port = pci_resource_start(pci, 3); + if (request_irq(pci->irq, snd_ice1712_interrupt, SA_INTERRUPT|SA_SHIRQ, "ICE1712", (void *) ice)) { snd_printk("unable to grab IRQ %d\n", pci->irq); snd_ice1712_free(ice); --- linux-2.6.9-rc1/sound/pci/ice1712/ice1712.h 2004-08-24 00:01:53.000000000 -0700 +++ 25/sound/pci/ice1712/ice1712.h 2004-09-02 20:51:53.000000000 -0700 @@ -294,13 +294,9 @@ struct _snd_ice1712 { int irq; unsigned long port; - struct resource *res_port; unsigned long ddma_port; - struct resource *res_ddma_port; unsigned long dmapath_port; - struct resource *res_dmapath_port; unsigned long profi_port; - struct resource *res_profi_port; struct pci_dev *pci; snd_card_t *card; @@ -368,8 +364,6 @@ struct _snd_ice1712 { struct semaphore gpio_mutex; }; -#define chip_t ice1712_t - /* * gpio access functions --- linux-2.6.9-rc1/sound/pci/ice1712/ice1724.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/sound/pci/ice1712/ice1724.c 2004-09-02 20:51:53.000000000 -0700 @@ -44,16 +44,19 @@ #include "amp.h" #include "revo.h" #include "aureon.h" +#include "vt1720_mobo.h" +#include "pontis.h" MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("VIA ICEnsemble ICE1724/1720 (Envy24HT/PT)"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{" +MODULE_SUPPORTED_DEVICE("{" REVO_DEVICE_DESC AMP_AUDIO2000_DEVICE_DESC AUREON_DEVICE_DESC + VT1720_MOBO_DEVICE_DESC + PONTIS_DEVICE_DESC "{VIA,VT1720}," "{VIA,VT1724}," "{ICEnsemble,Generic ICE1724}," @@ -68,13 +71,10 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for ICE1724 soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for ICE1724 soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable ICE1724 soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(model, charp, boot_devs, 0444); MODULE_PARM_DESC(model, "Use the given board model."); @@ -190,21 +190,26 @@ static void snd_vt1724_set_gpio_dir(ice1 static void snd_vt1724_set_gpio_mask(ice1712_t *ice, unsigned int data) { outw(data, ICEREG1724(ice, GPIO_WRITE_MASK)); - outb((data >> 16) & 0xff, ICEREG1724(ice, GPIO_WRITE_MASK_22)); + if (! ice->vt1720) /* VT1720 supports only 16 GPIO bits */ + outb((data >> 16) & 0xff, ICEREG1724(ice, GPIO_WRITE_MASK_22)); inw(ICEREG1724(ice, GPIO_WRITE_MASK)); /* dummy read for pci-posting */ } static void snd_vt1724_set_gpio_data(ice1712_t *ice, unsigned int data) { outw(data, ICEREG1724(ice, GPIO_DATA)); - outb(data >> 16, ICEREG1724(ice, GPIO_DATA_22)); + if (! ice->vt1720) + outb(data >> 16, ICEREG1724(ice, GPIO_DATA_22)); inw(ICEREG1724(ice, GPIO_DATA)); /* dummy read for pci-posting */ } static unsigned int snd_vt1724_get_gpio_data(ice1712_t *ice) { unsigned int data; - data = (unsigned int)inb(ICEREG1724(ice, GPIO_DATA_22)); + if (! ice->vt1720) + data = (unsigned int)inb(ICEREG1724(ice, GPIO_DATA_22)); + else + data = 0; data = (data << 16) | inw(ICEREG1724(ice, GPIO_DATA)); return data; } @@ -215,7 +220,7 @@ static unsigned int snd_vt1724_get_gpio_ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - ice1712_t *ice = snd_magic_cast(ice1712_t, dev_id, return IRQ_NONE); + ice1712_t *ice = dev_id; unsigned char status; int handled = 0; @@ -230,7 +235,7 @@ static irqreturn_t snd_vt1724_interrupt( if ((status & VT1724_IRQ_MPU_RX)||(status & VT1724_IRQ_MPU_TX)) { if (ice->rmidi[0]) snd_mpu401_uart_interrupt(irq, ice->rmidi[0]->private_data, regs); - outb(VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX, ICEREG1724(ice, IRQSTAT)); + outb(status & (VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX), ICEREG1724(ice, IRQSTAT)); status &= ~(VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX); } if (status & VT1724_IRQ_MTPCM) { @@ -317,6 +322,13 @@ static snd_pcm_hw_constraint_list_t hw_c .mask = 0, }; +struct vt1724_pcm_reg { + unsigned int addr; /* ADDR register offset */ + unsigned int size; /* SIZE register offset */ + unsigned int count; /* COUNT register offset */ + unsigned int start; /* start & pause bit */ +}; + static int snd_vt1724_pcm_trigger(snd_pcm_substream_t *substream, int cmd) { ice1712_t *ice = snd_pcm_substream_chip(substream); @@ -327,8 +339,10 @@ static int snd_vt1724_pcm_trigger(snd_pc what = 0; snd_pcm_group_for_each(pos, substream) { + struct vt1724_pcm_reg *reg; s = snd_pcm_group_substream_entry(pos); - what |= (unsigned long)(s->runtime->private_data); + reg = s->runtime->private_data; + what |= reg->start; snd_pcm_trigger_done(s, substream); } @@ -371,12 +385,26 @@ static int snd_vt1724_pcm_trigger(snd_pc #define DMA_PAUSES (VT1724_RDMA0_PAUSE|VT1724_PDMA0_PAUSE|VT1724_RDMA1_PAUSE|\ VT1724_PDMA1_PAUSE|VT1724_PDMA2_PAUSE|VT1724_PDMA3_PAUSE|VT1724_PDMA4_PAUSE) +static int get_max_rate(ice1712_t *ice) +{ + if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) { + if ((ice->eeprom.data[ICE_EEP2_I2S] & 0x08) && !ice->vt1720) + return 192000; + else + return 96000; + } else + return 48000; +} + static void snd_vt1724_set_pro_rate(ice1712_t *ice, unsigned int rate, int force) { unsigned long flags; unsigned char val, old; unsigned int i; + if (rate > get_max_rate(ice)) + return; + spin_lock_irqsave(&ice->reg_lock, flags); if ((inb(ICEMT1724(ice, DMA_CONTROL)) & DMA_STARTS) || (inb(ICEMT1724(ice, DMA_PAUSE)) & DMA_PAUSES)) { @@ -410,8 +438,10 @@ static void snd_vt1724_set_pro_rate(ice1 val = 0; break; } - outb(val, ICEMT1724(ice, RATE)); - if (rate == ice->cur_rate) { + old = inb(ICEMT1724(ice, RATE)); + if (old != val) + outb(val, ICEMT1724(ice, RATE)); + else if (rate == ice->cur_rate) { spin_unlock_irqrestore(&ice->reg_lock, flags); return; } @@ -419,7 +449,7 @@ static void snd_vt1724_set_pro_rate(ice1 ice->cur_rate = rate; /* check MT02 */ - if (ice->eeprom.data[ICE_EEP2_ACLINK] & 0x80) { + if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) { val = old = inb(ICEMT1724(ice, I2S_FORMAT)); if (rate > 96000) val |= VT1724_MT_I2S_MCLK_128X; /* 128x MCLK */ @@ -446,15 +476,6 @@ static void snd_vt1724_set_pro_rate(ice1 if (ice->akm[i].ops.set_rate_val) ice->akm[i].ops.set_rate_val(&ice->akm[i], rate); } - - /* set up AC97 registers if needed */ - if (! (ice->eeprom.data[ICE_EEP2_ACLINK] & 0x80) && ice->ac97) { - snd_ac97_set_rate(ice->ac97, AC97_PCM_FRONT_DAC_RATE, rate); - snd_ac97_set_rate(ice->ac97, AC97_PCM_SURR_DAC_RATE, rate); - snd_ac97_set_rate(ice->ac97, AC97_PCM_LFE_DAC_RATE, rate); - snd_ac97_set_rate(ice->ac97, AC97_SPDIF, rate); - snd_ac97_set_rate(ice->ac97, AC97_PCM_LR_ADC_RATE, rate); - } } static int snd_vt1724_pcm_hw_params(snd_pcm_substream_t * substream, @@ -518,7 +539,7 @@ static int snd_vt1724_playback_pro_prepa unsigned char val; unsigned int size; - spin_lock(&ice->reg_lock); + spin_lock_irq(&ice->reg_lock); val = (8 - substream->runtime->channels) >> 1; outb(val, ICEMT1724(ice, BURST)); @@ -533,7 +554,7 @@ static int snd_vt1724_playback_pro_prepa outw(size, ICEMT1724(ice, PLAYBACK_COUNT)); outb(size >> 16, ICEMT1724(ice, PLAYBACK_COUNT) + 2); - spin_unlock(&ice->reg_lock); + spin_unlock_irq(&ice->reg_lock); // printk("pro prepare: ch = %d, addr = 0x%x, buffer = 0x%x, period = 0x%x\n", substream->runtime->channels, (unsigned int)substream->runtime->dma_addr, snd_pcm_lib_buffer_bytes(substream), snd_pcm_lib_period_bytes(substream)); return 0; @@ -562,7 +583,9 @@ static snd_pcm_uframes_t snd_vt1724_play ptr = inl(ICEMT1724(ice, PLAYBACK_SIZE)) & 0xffffff; ptr = (ptr + 1) << 2; ptr = bytes_to_frames(substream->runtime, ptr); - if (ptr <= substream->runtime->buffer_size) + if (! ptr) + ; + else if (ptr <= substream->runtime->buffer_size) ptr = substream->runtime->buffer_size - ptr; else { snd_printd("ice1724: invalid ptr %d (size=%d)\n", (int)ptr, (int)substream->runtime->buffer_size); @@ -572,29 +595,23 @@ static snd_pcm_uframes_t snd_vt1724_play return ptr; } -struct vt1724_pcm_reg { - unsigned int addr; /* ADDR register offset */ - unsigned int size; /* SIZE register offset */ - unsigned int count; /* COUNT register offset */ - unsigned int start; /* start bit */ - unsigned int pause; /* pause bit */ -}; - -static int snd_vt1724_pcm_prepare(snd_pcm_substream_t *substream, const struct vt1724_pcm_reg *reg) +static int snd_vt1724_pcm_prepare(snd_pcm_substream_t *substream) { ice1712_t *ice = snd_pcm_substream_chip(substream); + struct vt1724_pcm_reg *reg = substream->runtime->private_data; - spin_lock(&ice->reg_lock); + spin_lock_irq(&ice->reg_lock); outl(substream->runtime->dma_addr, ice->profi_port + reg->addr); outw((snd_pcm_lib_buffer_bytes(substream) >> 2) - 1, ice->profi_port + reg->size); outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ice->profi_port + reg->count); - spin_unlock(&ice->reg_lock); + spin_unlock_irq(&ice->reg_lock); return 0; } -static snd_pcm_uframes_t snd_vt1724_pcm_pointer(snd_pcm_substream_t *substream, const struct vt1724_pcm_reg *reg) +static snd_pcm_uframes_t snd_vt1724_pcm_pointer(snd_pcm_substream_t *substream) { ice1712_t *ice = snd_pcm_substream_chip(substream); + struct vt1724_pcm_reg *reg = substream->runtime->private_data; size_t ptr; if (!(inl(ICEMT1724(ice, DMA_CONTROL)) & reg->start)) @@ -607,7 +624,9 @@ static snd_pcm_uframes_t snd_vt1724_pcm_ ptr = inw(ice->profi_port + reg->size); ptr = (ptr + 1) << 2; ptr = bytes_to_frames(substream->runtime, ptr); - if (ptr <= substream->runtime->buffer_size) + if (! ptr) + ; + else if (ptr <= substream->runtime->buffer_size) ptr = substream->runtime->buffer_size - ptr; else { snd_printd("ice1724: invalid ptr %d (size=%d)\n", (int)ptr, (int)substream->runtime->buffer_size); @@ -617,24 +636,20 @@ static snd_pcm_uframes_t snd_vt1724_pcm_ #endif } -const static struct vt1724_pcm_reg vt1724_capture_pro_reg = { +static struct vt1724_pcm_reg vt1724_playback_pro_reg = { + .addr = VT1724_MT_PLAYBACK_ADDR, + .size = VT1724_MT_PLAYBACK_SIZE, + .count = VT1724_MT_PLAYBACK_COUNT, + .start = VT1724_PDMA0_START, +}; + +static struct vt1724_pcm_reg vt1724_capture_pro_reg = { .addr = VT1724_MT_CAPTURE_ADDR, .size = VT1724_MT_CAPTURE_SIZE, .count = VT1724_MT_CAPTURE_COUNT, .start = VT1724_RDMA0_START, - .pause = VT1724_RDMA0_PAUSE, }; -static int snd_vt1724_capture_pro_prepare(snd_pcm_substream_t * substream) -{ - return snd_vt1724_pcm_prepare(substream, &vt1724_capture_pro_reg); -} - -static snd_pcm_uframes_t snd_vt1724_capture_pro_pointer(snd_pcm_substream_t * substream) -{ - return snd_vt1724_pcm_pointer(substream, &vt1724_capture_pro_reg); -} - static snd_pcm_hardware_t snd_vt1724_playback_pro = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | @@ -698,9 +713,10 @@ static snd_pcm_hardware_t snd_vt1724_2ch static int set_rate_constraints(ice1712_t *ice, snd_pcm_substream_t *substream) { snd_pcm_runtime_t *runtime = substream->runtime; - if (ice->eeprom.data[ICE_EEP2_ACLINK] & 0x80) { + if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) { /* I2S */ - if (ice->eeprom.data[ICE_EEP2_I2S] & 0x08) + /* VT1720 doesn't support more than 96kHz */ + if ((ice->eeprom.data[ICE_EEP2_I2S] & 0x08) && !ice->vt1720) return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_192); else { runtime->hw.rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000; @@ -709,25 +725,14 @@ static int set_rate_constraints(ice1712_ } } else if (ice->ac97) { /* ACLINK */ - int ratec; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - ratec = AC97_RATES_FRONT_DAC; - else - ratec = AC97_RATES_ADC; - runtime->hw.rates = ice->ac97->rates[ratec]; runtime->hw.rate_max = 48000; - if (runtime->hw.rates == SNDRV_PCM_RATE_48000) { - runtime->hw.rate_min = 48000; - return 0; - } else { - runtime->hw.rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000; - return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_48); - } + runtime->hw.rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000; + return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_48); } return 0; } -/* multi-channel playback needs alignment 8x32bit regarless of the channels +/* multi-channel playback needs alignment 8x32bit regardless of the channels * actually used */ #define VT1724_BUFFER_ALIGN 0x20 @@ -738,7 +743,7 @@ static int snd_vt1724_playback_pro_open( ice1712_t *ice = snd_pcm_substream_chip(substream); int chs; - runtime->private_data = (void*)VT1724_PDMA0_START; /* irq/status/trigger bit */ + runtime->private_data = &vt1724_playback_pro_reg; ice->playback_pro_substream = substream; runtime->hw = snd_vt1724_playback_pro; snd_pcm_set_sync(substream); @@ -767,12 +772,16 @@ static int snd_vt1724_capture_pro_open(s ice1712_t *ice = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - runtime->private_data = (void*)VT1724_RDMA0_START; /* irq/status/trigger bit */ + runtime->private_data = &vt1724_capture_pro_reg; ice->capture_pro_substream = substream; runtime->hw = snd_vt1724_2ch_stereo; snd_pcm_set_sync(substream); snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); set_rate_constraints(ice, substream); + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + VT1724_BUFFER_ALIGN); + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + VT1724_BUFFER_ALIGN); return 0; } @@ -814,9 +823,9 @@ static snd_pcm_ops_t snd_vt1724_capture_ .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_vt1724_pcm_hw_params, .hw_free = snd_vt1724_pcm_hw_free, - .prepare = snd_vt1724_capture_pro_prepare, + .prepare = snd_vt1724_pcm_prepare, .trigger = snd_vt1724_pcm_trigger, - .pointer = snd_vt1724_capture_pro_pointer, + .pointer = snd_vt1724_pcm_pointer, }; static int __devinit snd_vt1724_pcm_profi(ice1712_t * ice, int device) @@ -848,40 +857,60 @@ static int __devinit snd_vt1724_pcm_prof * SPDIF PCM */ -const static struct vt1724_pcm_reg vt1724_playback_spdif_reg = { +static struct vt1724_pcm_reg vt1724_playback_spdif_reg = { .addr = VT1724_MT_PDMA4_ADDR, .size = VT1724_MT_PDMA4_SIZE, .count = VT1724_MT_PDMA4_COUNT, .start = VT1724_PDMA4_START, - .pause = VT1724_PDMA4_PAUSE, }; -const static struct vt1724_pcm_reg vt1724_capture_spdif_reg = { +static struct vt1724_pcm_reg vt1724_capture_spdif_reg = { .addr = VT1724_MT_RDMA1_ADDR, .size = VT1724_MT_RDMA1_SIZE, .count = VT1724_MT_RDMA1_COUNT, .start = VT1724_RDMA1_START, - .pause = VT1724_RDMA1_PAUSE, }; -static int snd_vt1724_playback_spdif_prepare(snd_pcm_substream_t * substream) +/* update spdif control bits; call with reg_lock */ +static void update_spdif_bits(ice1712_t *ice, unsigned int val) { - return snd_vt1724_pcm_prepare(substream, &vt1724_playback_spdif_reg); -} + unsigned char cbit, disabled; -static snd_pcm_uframes_t snd_vt1724_playback_spdif_pointer(snd_pcm_substream_t * substream) -{ - return snd_vt1724_pcm_pointer(substream, &vt1724_playback_spdif_reg); + cbit = inb(ICEREG1724(ice, SPDIF_CFG)); + disabled = cbit & ~VT1724_CFG_SPDIF_OUT_EN; + if (cbit != disabled) + outb(disabled, ICEREG1724(ice, SPDIF_CFG)); + outw(val, ICEMT1724(ice, SPDIF_CTRL)); + if (cbit != disabled) + outb(cbit, ICEREG1724(ice, SPDIF_CFG)); + outw(val, ICEMT1724(ice, SPDIF_CTRL)); } -static int snd_vt1724_capture_spdif_prepare(snd_pcm_substream_t * substream) +/* update SPDIF control bits according to the given rate */ +static void update_spdif_rate(ice1712_t *ice, unsigned int rate) { - return snd_vt1724_pcm_prepare(substream, &vt1724_capture_spdif_reg); + unsigned int val, nval; + unsigned long flags; + + spin_lock_irqsave(&ice->reg_lock, flags); + nval = val = inw(ICEMT1724(ice, SPDIF_CTRL)); + nval &= ~(7 << 12); + switch (rate) { + case 44100: break; + case 48000: nval |= 2 << 12; break; + case 32000: nval |= 3 << 12; break; + } + if (val != nval) + update_spdif_bits(ice, nval); + spin_unlock_irqrestore(&ice->reg_lock, flags); } -static snd_pcm_uframes_t snd_vt1724_capture_spdif_pointer(snd_pcm_substream_t * substream) +static int snd_vt1724_playback_spdif_prepare(snd_pcm_substream_t * substream) { - return snd_vt1724_pcm_pointer(substream, &vt1724_capture_spdif_reg); + ice1712_t *ice = snd_pcm_substream_chip(substream); + if (! ice->force_pdma4) + update_spdif_rate(ice, substream->runtime->rate); + return snd_vt1724_pcm_prepare(substream); } static int snd_vt1724_playback_spdif_open(snd_pcm_substream_t *substream) @@ -889,7 +918,7 @@ static int snd_vt1724_playback_spdif_ope ice1712_t *ice = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - runtime->private_data = (void*)VT1724_PDMA4_START; /* irq/status/trigger bit */ + runtime->private_data = &vt1724_playback_spdif_reg; ice->playback_con_substream = substream; if (ice->force_pdma4) { runtime->hw = snd_vt1724_2ch_stereo; @@ -898,6 +927,10 @@ static int snd_vt1724_playback_spdif_ope runtime->hw = snd_vt1724_spdif; snd_pcm_set_sync(substream); snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + VT1724_BUFFER_ALIGN); + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + VT1724_BUFFER_ALIGN); return 0; } @@ -917,7 +950,7 @@ static int snd_vt1724_capture_spdif_open ice1712_t *ice = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - runtime->private_data = (void*)VT1724_RDMA1_START; /* irq/status/trigger bit */ + runtime->private_data = &vt1724_capture_spdif_reg; ice->capture_con_substream = substream; if (ice->force_rdma1) { runtime->hw = snd_vt1724_2ch_stereo; @@ -926,6 +959,10 @@ static int snd_vt1724_capture_spdif_open runtime->hw = snd_vt1724_spdif; snd_pcm_set_sync(substream); snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + VT1724_BUFFER_ALIGN); + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + VT1724_BUFFER_ALIGN); return 0; } @@ -948,7 +985,7 @@ static snd_pcm_ops_t snd_vt1724_playback .hw_free = snd_vt1724_pcm_hw_free, .prepare = snd_vt1724_playback_spdif_prepare, .trigger = snd_vt1724_pcm_trigger, - .pointer = snd_vt1724_playback_spdif_pointer, + .pointer = snd_vt1724_pcm_pointer, }; static snd_pcm_ops_t snd_vt1724_capture_spdif_ops = { @@ -957,9 +994,9 @@ static snd_pcm_ops_t snd_vt1724_capture_ .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_vt1724_pcm_hw_params, .hw_free = snd_vt1724_pcm_hw_free, - .prepare = snd_vt1724_capture_spdif_prepare, + .prepare = snd_vt1724_pcm_prepare, .trigger = snd_vt1724_pcm_trigger, - .pointer = snd_vt1724_capture_spdif_pointer, + .pointer = snd_vt1724_pcm_pointer, }; @@ -1017,27 +1054,24 @@ static int __devinit snd_vt1724_pcm_spdi * independent surround PCMs */ -const static struct vt1724_pcm_reg vt1724_playback_dma_regs[3] = { +static struct vt1724_pcm_reg vt1724_playback_dma_regs[3] = { { .addr = VT1724_MT_PDMA1_ADDR, .size = VT1724_MT_PDMA1_SIZE, .count = VT1724_MT_PDMA1_COUNT, .start = VT1724_PDMA1_START, - .pause = VT1724_PDMA1_PAUSE, }, { .addr = VT1724_MT_PDMA2_ADDR, .size = VT1724_MT_PDMA2_SIZE, .count = VT1724_MT_PDMA2_COUNT, .start = VT1724_PDMA2_START, - .pause = VT1724_PDMA2_PAUSE, }, { .addr = VT1724_MT_PDMA3_ADDR, .size = VT1724_MT_PDMA3_SIZE, .count = VT1724_MT_PDMA3_COUNT, .start = VT1724_PDMA3_START, - .pause = VT1724_PDMA3_PAUSE, }, }; @@ -1046,17 +1080,12 @@ static int snd_vt1724_playback_indep_pre ice1712_t *ice = snd_pcm_substream_chip(substream); unsigned char val; - spin_lock(&ice->reg_lock); + spin_lock_irq(&ice->reg_lock); val = 3 - substream->number; if (inb(ICEMT1724(ice, BURST)) < val) outb(val, ICEMT1724(ice, BURST)); - spin_unlock(&ice->reg_lock); - return snd_vt1724_pcm_prepare(substream, &vt1724_playback_dma_regs[substream->number]); -} - -static snd_pcm_uframes_t snd_vt1724_playback_indep_pointer(snd_pcm_substream_t * substream) -{ - return snd_vt1724_pcm_pointer(substream, &vt1724_playback_dma_regs[substream->number]); + spin_unlock_irq(&ice->reg_lock); + return snd_vt1724_pcm_prepare(substream); } static int snd_vt1724_playback_indep_open(snd_pcm_substream_t *substream) @@ -1071,7 +1100,7 @@ static int snd_vt1724_playback_indep_ope return -EBUSY; /* FIXME: should handle blocking mode properly */ } up(&ice->open_mutex); - runtime->private_data = (void*)(1UL << (substream->number + 4)); + runtime->private_data = &vt1724_playback_dma_regs[substream->number]; ice->playback_con_substream_ds[substream->number] = substream; runtime->hw = snd_vt1724_2ch_stereo; snd_pcm_set_sync(substream); @@ -1100,7 +1129,7 @@ static snd_pcm_ops_t snd_vt1724_playback .hw_free = snd_vt1724_pcm_hw_free, .prepare = snd_vt1724_playback_indep_prepare, .trigger = snd_vt1724_pcm_trigger, - .pointer = snd_vt1724_playback_indep_pointer, + .pointer = snd_vt1724_pcm_pointer, }; @@ -1143,17 +1172,19 @@ static int __devinit snd_vt1724_ac97_mix int err; if (! (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S)) { - ac97_bus_t bus, *pbus; - ac97_t ac97; + ac97_bus_t *pbus; + ac97_template_t ac97; + static ac97_bus_ops_t ops = { + .write = snd_vt1724_ac97_write, + .read = snd_vt1724_ac97_read, + }; + /* cold reset */ outb(inb(ICEMT1724(ice, AC97_CMD)) | 0x80, ICEMT1724(ice, AC97_CMD)); mdelay(5); /* FIXME */ outb(inb(ICEMT1724(ice, AC97_CMD)) & ~0x80, ICEMT1724(ice, AC97_CMD)); - memset(&bus, 0, sizeof(bus)); - bus.write = snd_vt1724_ac97_write; - bus.read = snd_vt1724_ac97_read; - if ((err = snd_ac97_bus(ice->card, &bus, &pbus)) < 0) + if ((err = snd_ac97_bus(ice->card, 0, &ops, NULL, &pbus)) < 0) return err; memset(&ac97, 0, sizeof(ac97)); ac97.private_data = ice; @@ -1181,7 +1212,7 @@ static inline unsigned int eeprom_triple static void snd_vt1724_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) { - ice1712_t *ice = snd_magic_cast(ice1712_t, entry->private_data, return); + ice1712_t *ice = entry->private_data; unsigned int idx; snd_iprintf(buffer, "%s\n\n", ice->card->longname); @@ -1274,7 +1305,7 @@ static unsigned int encode_spdif_bits(sn } } else { /* consumer */ - val |= diga->status[0] & 0x04; /* copyright */ + val |= diga->status[1] & 0x04; /* copyright */ if ((diga->status[0] & IEC958_AES0_CON_EMPHASIS)== IEC958_AES0_CON_EMPHASIS_5015) val |= 1U << 3; val |= (unsigned int)(diga->status[1] & 0x3f) << 4; /* category */ @@ -1326,22 +1357,13 @@ static int snd_vt1724_spdif_default_put( { ice1712_t *ice = snd_kcontrol_chip(kcontrol); unsigned int val, old; - unsigned long flags; val = encode_spdif_bits(&ucontrol->value.iec958); - spin_lock_irqsave(&ice->reg_lock, flags); + spin_lock_irq(&ice->reg_lock); old = inw(ICEMT1724(ice, SPDIF_CTRL)); - if (val != old) { - unsigned char cbit, disabled; - cbit = inb(ICEREG1724(ice, SPDIF_CFG)); - disabled = cbit & ~VT1724_CFG_SPDIF_OUT_EN; - if (cbit != disabled) - outb(disabled, ICEREG1724(ice, SPDIF_CFG)); - outw(val, ICEMT1724(ice, SPDIF_CTRL)); - if (cbit != disabled) - outb(cbit, ICEREG1724(ice, SPDIF_CFG)); - } - spin_unlock_irqrestore(&ice->reg_lock, flags); + if (val != old) + update_spdif_bits(ice, val); + spin_unlock_irq(&ice->reg_lock); return (val != old); } @@ -1415,15 +1437,15 @@ static int snd_vt1724_spdif_sw_put(snd_k { ice1712_t *ice = snd_kcontrol_chip(kcontrol); unsigned char old, val; - unsigned long flags; - spin_lock_irqsave(&ice->reg_lock, flags); + + spin_lock_irq(&ice->reg_lock); old = val = inb(ICEREG1724(ice, SPDIF_CFG)); val &= ~VT1724_CFG_SPDIF_OUT_EN; if (ucontrol->value.integer.value[0]) val |= VT1724_CFG_SPDIF_OUT_EN; if (old != val) outb(val, ICEREG1724(ice, SPDIF_CFG)); - spin_unlock_irqrestore(&ice->reg_lock, flags); + spin_unlock_irq(&ice->reg_lock); return old != val; } @@ -1490,7 +1512,7 @@ int snd_ice1712_gpio_put(snd_kcontrol_t */ static int snd_vt1724_pro_internal_clock_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { - static char *texts[] = { + static char *texts_1724[] = { "8000", /* 0: 6 */ "9600", /* 1: 3 */ "11025", /* 2: 10 */ @@ -1508,12 +1530,32 @@ static int snd_vt1724_pro_internal_clock "192000", /* 14: 14 */ "IEC958 Input", /* 15: -- */ }; + static char *texts_1720[] = { + "8000", /* 0: 6 */ + "9600", /* 1: 3 */ + "11025", /* 2: 10 */ + "12000", /* 3: 2 */ + "16000", /* 4: 5 */ + "22050", /* 5: 9 */ + "24000", /* 6: 1 */ + "32000", /* 7: 4 */ + "44100", /* 8: 8 */ + "48000", /* 9: 0 */ + "64000", /* 10: 15 */ + "88200", /* 11: 11 */ + "96000", /* 12: 7 */ + "IEC958 Input", /* 13: -- */ + }; + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - uinfo->value.enumerated.items = 16; + uinfo->value.enumerated.items = ice->vt1720 ? 14 : 16; if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + strcpy(uinfo->value.enumerated.name, + ice->vt1720 ? texts_1720[uinfo->value.enumerated.item] : + texts_1724[uinfo->value.enumerated.item]); return 0; } @@ -1527,7 +1569,7 @@ static int snd_vt1724_pro_internal_clock spin_lock_irq(&ice->reg_lock); if (is_spdif_master(ice)) { - ucontrol->value.enumerated.item[0] = 15; + ucontrol->value.enumerated.item[0] = ice->vt1720 ? 13 : 15; } else { val = xlate[inb(ICEMT1724(ice, RATE)) & 15]; if (val == 255) { @@ -1544,17 +1586,22 @@ static int snd_vt1724_pro_internal_clock { ice1712_t *ice = snd_kcontrol_chip(kcontrol); unsigned char oval; + int rate; int change = 0; + int spdif = ice->vt1720 ? 13 : 15; spin_lock_irq(&ice->reg_lock); oval = inb(ICEMT1724(ice, RATE)); - if (ucontrol->value.enumerated.item[0] == 15) { + if (ucontrol->value.enumerated.item[0] == spdif) { outb(oval | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE)); } else { - PRO_RATE_DEFAULT = rates[ucontrol->value.integer.value[0] % 15]; - spin_unlock_irq(&ice->reg_lock); - snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 1); - spin_lock_irq(&ice->reg_lock); + rate = rates[ucontrol->value.integer.value[0] % 15]; + if (rate <= get_max_rate(ice)) { + PRO_RATE_DEFAULT = rate; + spin_unlock_irq(&ice->reg_lock); + snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 1); + spin_lock_irq(&ice->reg_lock); + } } change = inb(ICEMT1724(ice, RATE)) != oval; spin_unlock_irq(&ice->reg_lock); @@ -1815,6 +1862,8 @@ static struct snd_ice1712_card_info *car snd_vt1724_revo_cards, snd_vt1724_amp_cards, snd_vt1724_aureon_cards, + snd_vt1720_mobo_cards, + snd_vt1720_pontis_cards, NULL, }; @@ -1822,24 +1871,28 @@ static struct snd_ice1712_card_info *car /* */ -unsigned char snd_vt1724_read_i2c(ice1712_t *ice, unsigned char dev, unsigned char addr) +static void wait_i2c_busy(ice1712_t *ice) { - long t = 0x10000; + int t = 0x10000; + while ((inb(ICEREG1724(ice, I2C_CTRL)) & VT1724_I2C_BUSY) && t--) + ; +} +unsigned char snd_vt1724_read_i2c(ice1712_t *ice, unsigned char dev, unsigned char addr) +{ outb(addr, ICEREG1724(ice, I2C_BYTE_ADDR)); outb(dev & ~VT1724_I2C_WRITE, ICEREG1724(ice, I2C_DEV_ADDR)); - while (t-- > 0 && (inb(ICEREG1724(ice, I2C_CTRL)) & VT1724_I2C_BUSY)) ; + wait_i2c_busy(ice); return inb(ICEREG1724(ice, I2C_DATA)); } void snd_vt1724_write_i2c(ice1712_t *ice, unsigned char dev, unsigned char addr, unsigned char data) { - long t = 0x10000; - + wait_i2c_busy(ice); outb(addr, ICEREG1724(ice, I2C_BYTE_ADDR)); outb(data, ICEREG1724(ice, I2C_DATA)); outb(dev | VT1724_I2C_WRITE, ICEREG1724(ice, I2C_DEV_ADDR)); - while (t-- > 0 && (inb(ICEREG1724(ice, I2C_CTRL)) & VT1724_I2C_BUSY)) ; + wait_i2c_busy(ice); } static int __devinit snd_vt1724_read_eeprom(ice1712_t *ice, const char *modelname) @@ -1930,9 +1983,6 @@ static int __devinit snd_vt1724_chip_ini outb(0, ICEREG1724(ice, POWERDOWN)); - /* read back to check the availability of SPDIF out */ - ice->eeprom.data[ICE_EEP2_SPDIF] = inb(ICEREG1724(ice, SPDIF_CFG)); - return 0; } @@ -1994,10 +2044,9 @@ static int __devinit snd_vt1724_build_co if (ice->num_total_dacs > 0) { snd_kcontrol_new_t tmp = snd_vt1724_mixer_pro_analog_route; - if (ice->vt1720) + tmp.count = ice->num_total_dacs; + if (ice->vt1720 && tmp.count > 2) tmp.count = 2; - else - tmp.count = ice->num_total_dacs; err = snd_ctl_add(ice->card, snd_ctl_new1(&tmp, ice)); if (err < 0) return err; @@ -2012,7 +2061,7 @@ static int __devinit snd_vt1724_build_co static int snd_vt1724_free(ice1712_t *ice) { - if (ice->res_port == NULL) + if (! ice->port) goto __hw_end; /* mask all interrupts */ outb(0xff, ICEMT1724(ice, DMA_INT_MASK)); @@ -2023,22 +2072,15 @@ static int snd_vt1724_free(ice1712_t *ic synchronize_irq(ice->irq); free_irq(ice->irq, (void *) ice); } - if (ice->res_port) { - release_resource(ice->res_port); - kfree_nocheck(ice->res_port); - } - if (ice->res_profi_port) { - release_resource(ice->res_profi_port); - kfree_nocheck(ice->res_profi_port); - } + pci_release_regions(ice->pci); snd_ice1712_akm4xxx_free(ice); - snd_magic_kfree(ice); + kfree(ice); return 0; } static int snd_vt1724_dev_free(snd_device_t *device) { - ice1712_t *ice = snd_magic_cast(ice1712_t, device->device_data, return -ENXIO); + ice1712_t *ice = device->device_data; return snd_vt1724_free(ice); } @@ -2060,7 +2102,7 @@ static int __devinit snd_vt1724_create(s if ((err = pci_enable_device(pci)) < 0) return err; - ice = snd_magic_kcalloc(ice1712_t, 0, GFP_KERNEL); + ice = kcalloc(1, sizeof(*ice), GFP_KERNEL); if (ice == NULL) return -ENOMEM; ice->vt1724 = 1; @@ -2074,24 +2116,17 @@ static int __devinit snd_vt1724_create(s ice->card = card; ice->pci = pci; ice->irq = -1; - ice->port = pci_resource_start(pci, 0); - ice->profi_port = pci_resource_start(pci, 1); pci_set_master(pci); snd_vt1724_proc_init(ice); synchronize_irq(pci->irq); - if ((ice->res_port = request_region(ice->port, 32, "ICE1724 - Controller")) == NULL) { - snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->port, ice->port + 32 - 1); - snd_vt1724_free(ice); - return -EIO; + if ((err = pci_request_regions(pci, "ICE1724")) < 0) { + kfree(ice); + return err; } + ice->port = pci_resource_start(pci, 0); + ice->profi_port = pci_resource_start(pci, 1); - if ((ice->res_profi_port = request_region(ice->profi_port, 128, "ICE1724 - Professional")) == NULL) { - snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->profi_port, ice->profi_port + 16 - 1); - snd_vt1724_free(ice); - return -EIO; - } - if (request_irq(pci->irq, snd_vt1724_interrupt, SA_INTERRUPT|SA_SHIRQ, "ICE1724", (void *) ice)) { snd_printk("unable to grab IRQ %d\n", pci->irq); snd_vt1724_free(ice); --- linux-2.6.9-rc1/sound/pci/ice1712/Makefile 2004-08-24 00:02:32.000000000 -0700 +++ 25/sound/pci/ice1712/Makefile 2004-09-02 20:51:53.000000000 -0700 @@ -5,7 +5,7 @@ snd-ice17xx-ak4xxx-objs := ak4xxx.o snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o -snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o +snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o # Toplevel Module Dependency obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/sound/pci/ice1712/pontis.c 2004-09-02 20:51:53.000000000 -0700 @@ -0,0 +1,849 @@ +/* + * ALSA driver for ICEnsemble VT1724 (Envy24HT) + * + * Lowlevel functions for Pontis MS300 + * + * Copyright (c) 2004 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ice1712.h" +#include "envy24ht.h" +#include "pontis.h" + +/* I2C addresses */ +#define WM_DEV 0x34 +#define CS_DEV 0x20 + +/* WM8776 registers */ +#define WM_HP_ATTEN_L 0x00 /* headphone left attenuation */ +#define WM_HP_ATTEN_R 0x01 /* headphone left attenuation */ +#define WM_HP_MASTER 0x02 /* headphone master (both channels), override LLR */ +#define WM_DAC_ATTEN_L 0x03 /* digital left attenuation */ +#define WM_DAC_ATTEN_R 0x04 +#define WM_DAC_MASTER 0x05 +#define WM_PHASE_SWAP 0x06 /* DAC phase swap */ +#define WM_DAC_CTRL1 0x07 +#define WM_DAC_MUTE 0x08 +#define WM_DAC_CTRL2 0x09 +#define WM_DAC_INT 0x0a +#define WM_ADC_INT 0x0b +#define WM_MASTER_CTRL 0x0c +#define WM_POWERDOWN 0x0d +#define WM_ADC_ATTEN_L 0x0e +#define WM_ADC_ATTEN_R 0x0f +#define WM_ALC_CTRL1 0x10 +#define WM_ALC_CTRL2 0x11 +#define WM_ALC_CTRL3 0x12 +#define WM_NOISE_GATE 0x13 +#define WM_LIMITER 0x14 +#define WM_ADC_MUX 0x15 +#define WM_OUT_MUX 0x16 +#define WM_RESET 0x17 + +/* + * GPIO + */ +#define PONTIS_CS_CS (1<<4) /* CS */ +#define PONTIS_CS_CLK (1<<5) /* CLK */ +#define PONTIS_CS_RDATA (1<<6) /* CS8416 -> VT1720 */ +#define PONTIS_CS_WDATA (1<<7) /* VT1720 -> CS8416 */ + + +/* + * get the current register value of WM codec + */ +static unsigned short wm_get(ice1712_t *ice, int reg) +{ + reg <<= 1; + return ((unsigned short)ice->akm[0].images[reg] << 8) | + ice->akm[0].images[reg + 1]; +} + +/* + * set the register value of WM codec and remember it + */ +static void wm_put_nocache(ice1712_t *ice, int reg, unsigned short val) +{ + unsigned short cval; + cval = (reg << 9) | val; + snd_vt1724_write_i2c(ice, WM_DEV, cval >> 8, cval & 0xff); +} + +static void wm_put(ice1712_t *ice, int reg, unsigned short val) +{ + wm_put_nocache(ice, reg, val); + reg <<= 1; + ice->akm[0].images[reg] = val >> 8; + ice->akm[0].images[reg + 1] = val; +} + +/* + * DAC volume attenuation mixer control (-64dB to 0dB) + */ + +#define DAC_0dB 0xff +#define DAC_RES 128 +#define DAC_MIN (DAC_0dB - DAC_RES) + +static int wm_dac_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; /* mute */ + uinfo->value.integer.max = DAC_RES; /* 0dB, 0.5dB step */ + return 0; +} + +static int wm_dac_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned short val; + int i; + + down(&ice->gpio_mutex); + for (i = 0; i < 2; i++) { + val = wm_get(ice, WM_DAC_ATTEN_L + i) & 0xff; + val = val > DAC_MIN ? (val - DAC_MIN) : 0; + ucontrol->value.integer.value[i] = val; + } + up(&ice->gpio_mutex); + return 0; +} + +static int wm_dac_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned short oval, nval; + int i, idx, change = 0; + + down(&ice->gpio_mutex); + for (i = 0; i < 2; i++) { + nval = ucontrol->value.integer.value[i]; + nval = (nval ? (nval + DAC_MIN) : 0) & 0xff; + idx = WM_DAC_ATTEN_L + i; + oval = wm_get(ice, idx) & 0xff; + if (oval != nval) { + wm_put(ice, idx, nval); + wm_put_nocache(ice, idx, nval | 0x100); + change = 1; + } + } + up(&ice->gpio_mutex); + return change; +} + +/* + * ADC gain mixer control (-64dB to 0dB) + */ + +#define ADC_0dB 0xcf +#define ADC_RES 128 +#define ADC_MIN (ADC_0dB - ADC_RES) + +static int wm_adc_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; /* mute (-64dB) */ + uinfo->value.integer.max = ADC_RES; /* 0dB, 0.5dB step */ + return 0; +} + +static int wm_adc_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned short val; + int i; + + down(&ice->gpio_mutex); + for (i = 0; i < 2; i++) { + val = wm_get(ice, WM_ADC_ATTEN_L + i) & 0xff; + val = val > ADC_MIN ? (val - ADC_MIN) : 0; + ucontrol->value.integer.value[i] = val; + } + up(&ice->gpio_mutex); + return 0; +} + +static int wm_adc_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned short ovol, nvol; + int i, idx, change = 0; + + down(&ice->gpio_mutex); + for (i = 0; i < 2; i++) { + nvol = ucontrol->value.integer.value[i]; + nvol = nvol ? (nvol + ADC_MIN) : 0; + idx = WM_ADC_ATTEN_L + i; + ovol = wm_get(ice, idx) & 0xff; + if (ovol != nvol) { + wm_put(ice, idx, nvol); + change = 1; + } + } + up(&ice->gpio_mutex); + return change; +} + +/* + * ADC input mux mixer control + */ +static int wm_adc_mux_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int wm_adc_mux_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int bit = kcontrol->private_value; + + down(&ice->gpio_mutex); + ucontrol->value.integer.value[0] = (wm_get(ice, WM_ADC_MUX) & (1 << bit)) ? 1 : 0; + up(&ice->gpio_mutex); + return 0; +} + +static int wm_adc_mux_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + int bit = kcontrol->private_value; + unsigned short oval, nval; + int change; + + down(&ice->gpio_mutex); + nval = oval = wm_get(ice, WM_ADC_MUX); + if (ucontrol->value.integer.value[0]) + nval |= (1 << bit); + else + nval &= ~(1 << bit); + change = nval != oval; + if (change) { + wm_put(ice, WM_ADC_MUX, nval); + } + up(&ice->gpio_mutex); + return 0; +} + +/* + * Analog bypass (In -> Out) + */ +static int wm_bypass_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int wm_bypass_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + + down(&ice->gpio_mutex); + ucontrol->value.integer.value[0] = (wm_get(ice, WM_OUT_MUX) & 0x04) ? 1 : 0; + up(&ice->gpio_mutex); + return 0; +} + +static int wm_bypass_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned short val, oval; + int change = 0; + + down(&ice->gpio_mutex); + val = oval = wm_get(ice, WM_OUT_MUX); + if (ucontrol->value.integer.value[0]) + val |= 0x04; + else + val &= ~0x04; + if (val != oval) { + wm_put(ice, WM_OUT_MUX, val); + change = 1; + } + up(&ice->gpio_mutex); + return change; +} + +/* + * Left/Right swap + */ +static int wm_chswap_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int wm_chswap_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + + down(&ice->gpio_mutex); + ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL1) & 0xf0) != 0x90; + up(&ice->gpio_mutex); + return 0; +} + +static int wm_chswap_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned short val, oval; + int change = 0; + + down(&ice->gpio_mutex); + oval = wm_get(ice, WM_DAC_CTRL1); + val = oval & 0x0f; + if (ucontrol->value.integer.value[0]) + val |= 0x60; + else + val |= 0x90; + if (val != oval) { + wm_put(ice, WM_DAC_CTRL1, val); + wm_put_nocache(ice, WM_DAC_CTRL1, val); + change = 1; + } + up(&ice->gpio_mutex); + return change; +} + +/* + * write data in the SPI mode + */ +static void set_gpio_bit(ice1712_t *ice, unsigned int bit, int val) +{ + unsigned int tmp = snd_ice1712_gpio_read(ice); + if (val) + tmp |= bit; + else + tmp &= ~bit; + snd_ice1712_gpio_write(ice, tmp); +} + +static void spi_send_byte(ice1712_t *ice, unsigned char data) +{ + int i; + for (i = 0; i < 8; i++) { + set_gpio_bit(ice, PONTIS_CS_CLK, 0); + udelay(1); + set_gpio_bit(ice, PONTIS_CS_WDATA, data & 0x80); + udelay(1); + set_gpio_bit(ice, PONTIS_CS_CLK, 1); + udelay(1); + data <<= 1; + } +} + +static unsigned int spi_read_byte(ice1712_t *ice) +{ + int i; + unsigned int val = 0; + + for (i = 0; i < 8; i++) { + val <<= 1; + set_gpio_bit(ice, PONTIS_CS_CLK, 0); + udelay(1); + if (snd_ice1712_gpio_read(ice) & PONTIS_CS_RDATA) + val |= 1; + udelay(1); + set_gpio_bit(ice, PONTIS_CS_CLK, 1); + udelay(1); + } + return val; +} + + +static void spi_write(ice1712_t *ice, unsigned int dev, unsigned int reg, unsigned int data) +{ + snd_ice1712_gpio_set_dir(ice, PONTIS_CS_CS|PONTIS_CS_WDATA|PONTIS_CS_CLK); + snd_ice1712_gpio_set_mask(ice, ~(PONTIS_CS_CS|PONTIS_CS_WDATA|PONTIS_CS_CLK)); + set_gpio_bit(ice, PONTIS_CS_CS, 0); + spi_send_byte(ice, dev & ~1); /* WRITE */ + spi_send_byte(ice, reg); /* MAP */ + spi_send_byte(ice, data); /* DATA */ + /* trigger */ + set_gpio_bit(ice, PONTIS_CS_CS, 1); + udelay(1); + /* restore */ + snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask); + snd_ice1712_gpio_set_dir(ice, ice->gpio.direction); +} + +static unsigned int spi_read(ice1712_t *ice, unsigned int dev, unsigned int reg) +{ + unsigned int val; + snd_ice1712_gpio_set_dir(ice, PONTIS_CS_CS|PONTIS_CS_WDATA|PONTIS_CS_CLK); + snd_ice1712_gpio_set_mask(ice, ~(PONTIS_CS_CS|PONTIS_CS_WDATA|PONTIS_CS_CLK)); + set_gpio_bit(ice, PONTIS_CS_CS, 0); + spi_send_byte(ice, dev & ~1); /* WRITE */ + spi_send_byte(ice, reg); /* MAP */ + /* trigger */ + set_gpio_bit(ice, PONTIS_CS_CS, 1); + udelay(1); + set_gpio_bit(ice, PONTIS_CS_CS, 0); + spi_send_byte(ice, dev | 1); /* READ */ + val = spi_read_byte(ice); + /* trigger */ + set_gpio_bit(ice, PONTIS_CS_CS, 1); + udelay(1); + /* restore */ + snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask); + snd_ice1712_gpio_set_dir(ice, ice->gpio.direction); + return val; +} + + +/* + * SPDIF input source + */ +static int cs_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + static char *texts[] = { + "Coax", /* RXP0 */ + "Optical", /* RXP1 */ + "CD", /* RXP2 */ + }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int cs_source_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + + down(&ice->gpio_mutex); + ucontrol->value.enumerated.item[0] = ice->gpio.saved[0]; + up(&ice->gpio_mutex); + return 0; +} + +static int cs_source_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned char val; + int change = 0; + + down(&ice->gpio_mutex); + if (ucontrol->value.enumerated.item[0] != ice->gpio.saved[0]) { + ice->gpio.saved[0] = ucontrol->value.enumerated.item[0] & 3; + val = 0x80 | (ice->gpio.saved[0] << 3); + spi_write(ice, CS_DEV, 0x04, val); + change = 1; + } + up(&ice->gpio_mutex); + return 0; +} + + +/* + * GPIO controls + */ +static int pontis_gpio_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xffff; /* 16bit */ + return 0; +} + +static int pontis_gpio_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + down(&ice->gpio_mutex); + /* 4-7 reserved */ + ucontrol->value.integer.value[0] = (~ice->gpio.write_mask & 0xffff) | 0x00f0; + up(&ice->gpio_mutex); + return 0; +} + +static int pontis_gpio_mask_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned int val; + int changed; + down(&ice->gpio_mutex); + /* 4-7 reserved */ + val = (~ucontrol->value.integer.value[0] & 0xffff) | 0x00f0; + changed = val != ice->gpio.write_mask; + ice->gpio.write_mask = val; + up(&ice->gpio_mutex); + return changed; +} + +static int pontis_gpio_dir_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + down(&ice->gpio_mutex); + /* 4-7 reserved */ + ucontrol->value.integer.value[0] = ice->gpio.direction & 0xff0f; + up(&ice->gpio_mutex); + return 0; +} + +static int pontis_gpio_dir_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned int val; + int changed; + down(&ice->gpio_mutex); + /* 4-7 reserved */ + val = ucontrol->value.integer.value[0] & 0xff0f; + changed = (val != ice->gpio.direction); + ice->gpio.direction = val; + up(&ice->gpio_mutex); + return changed; +} + +static int pontis_gpio_data_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + down(&ice->gpio_mutex); + snd_ice1712_gpio_set_dir(ice, ice->gpio.direction); + snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask); + ucontrol->value.integer.value[0] = snd_ice1712_gpio_read(ice) & 0xffff; + up(&ice->gpio_mutex); + return 0; +} + +static int pontis_gpio_data_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + ice1712_t *ice = snd_kcontrol_chip(kcontrol); + unsigned int val, nval; + int changed = 0; + down(&ice->gpio_mutex); + snd_ice1712_gpio_set_dir(ice, ice->gpio.direction); + snd_ice1712_gpio_set_mask(ice, ice->gpio.write_mask); + val = snd_ice1712_gpio_read(ice) & 0xffff; + nval = ucontrol->value.integer.value[0] & 0xffff; + if (val != nval) { + snd_ice1712_gpio_write(ice, nval); + changed = 1; + } + up(&ice->gpio_mutex); + return changed; +} + +/* + * mixers + */ + +static snd_kcontrol_new_t pontis_controls[] __devinitdata = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Volume", + .info = wm_dac_vol_info, + .get = wm_dac_vol_get, + .put = wm_dac_vol_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Volume", + .info = wm_adc_vol_info, + .get = wm_adc_vol_get, + .put = wm_adc_vol_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "CD Capture Switch", + .info = wm_adc_mux_info, + .get = wm_adc_mux_get, + .put = wm_adc_mux_put, + .private_value = 0, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Line Capture Switch", + .info = wm_adc_mux_info, + .get = wm_adc_mux_get, + .put = wm_adc_mux_put, + .private_value = 1, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog Bypass Switch", + .info = wm_bypass_info, + .get = wm_bypass_get, + .put = wm_bypass_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Swap Output Channels", + .info = wm_chswap_info, + .get = wm_chswap_get, + .put = wm_chswap_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "IEC958 Input Source", + .info = cs_source_info, + .get = cs_source_get, + .put = cs_source_put, + }, + /* FIXME: which interface? */ + { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .name = "GPIO Mask", + .info = pontis_gpio_mask_info, + .get = pontis_gpio_mask_get, + .put = pontis_gpio_mask_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .name = "GPIO Direction", + .info = pontis_gpio_mask_info, + .get = pontis_gpio_dir_get, + .put = pontis_gpio_dir_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .name = "GPIO Data", + .info = pontis_gpio_mask_info, + .get = pontis_gpio_data_get, + .put = pontis_gpio_data_put, + }, +}; + + +/* + * WM codec registers + */ +static void wm_proc_regs_write(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + ice1712_t *ice = (ice1712_t *)entry->private_data; + char line[64]; + unsigned int reg, val; + down(&ice->gpio_mutex); + while (!snd_info_get_line(buffer, line, sizeof(line))) { + if (sscanf(line, "%x %x", ®, &val) != 2) + continue; + if (reg <= 0x17 && val <= 0xffff) + wm_put(ice, reg, val); + } + up(&ice->gpio_mutex); +} + +static void wm_proc_regs_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + ice1712_t *ice = (ice1712_t *)entry->private_data; + int reg, val; + + down(&ice->gpio_mutex); + for (reg = 0; reg <= 0x17; reg++) { + val = wm_get(ice, reg); + snd_iprintf(buffer, "%02x = %04x\n", reg, val); + } + up(&ice->gpio_mutex); +} + +static void wm_proc_init(ice1712_t *ice) +{ + snd_info_entry_t *entry; + if (! snd_card_proc_new(ice->card, "wm_codec", &entry)) { + snd_info_set_text_ops(entry, ice, 1024, wm_proc_regs_read); + entry->mode |= S_IWUSR; + entry->c.text.write_size = 1024; + entry->c.text.write = wm_proc_regs_write; + } +} + +static void cs_proc_regs_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) +{ + ice1712_t *ice = (ice1712_t *)entry->private_data; + int reg, val; + + down(&ice->gpio_mutex); + for (reg = 0; reg <= 0x26; reg++) { + val = spi_read(ice, CS_DEV, reg); + snd_iprintf(buffer, "%02x = %02x\n", reg, val); + } + val = spi_read(ice, CS_DEV, 0x7f); + snd_iprintf(buffer, "%02x = %02x\n", 0x7f, val); + up(&ice->gpio_mutex); +} + +static void cs_proc_init(ice1712_t *ice) +{ + snd_info_entry_t *entry; + if (! snd_card_proc_new(ice->card, "cs_codec", &entry)) { + snd_info_set_text_ops(entry, ice, 1024, cs_proc_regs_read); + } +} + + +static int __devinit pontis_add_controls(ice1712_t *ice) +{ + unsigned int i; + int err; + + for (i = 0; i < ARRAY_SIZE(pontis_controls); i++) { + err = snd_ctl_add(ice->card, snd_ctl_new1(&pontis_controls[i], ice)); + if (err < 0) + return err; + } + + wm_proc_init(ice); + cs_proc_init(ice); + + return 0; +} + + +/* + * initialize the chip + */ +static int __devinit pontis_init(ice1712_t *ice) +{ + static unsigned short wm_inits[] = { + /* These come first to reduce init pop noise */ + WM_ADC_MUX, 0x00c0, /* ADC mute */ + WM_DAC_MUTE, 0x0001, /* DAC softmute */ + WM_DAC_CTRL1, 0x0000, /* DAC mute */ + + WM_POWERDOWN, 0x0008, /* All power-up except HP */ + WM_RESET, 0x0000, /* reset */ + }; + static unsigned short wm_inits2[] = { + WM_MASTER_CTRL, 0x0022, /* 256fs, slave mode */ + WM_DAC_INT, 0x0022, /* I2S, normal polarity, 24bit */ + WM_ADC_INT, 0x0022, /* I2S, normal polarity, 24bit */ + WM_DAC_CTRL1, 0x0090, /* DAC L/R */ + WM_OUT_MUX, 0x0001, /* OUT DAC */ + WM_HP_ATTEN_L, 0x0179, /* HP 0dB */ + WM_HP_ATTEN_R, 0x0179, /* HP 0dB */ + WM_DAC_ATTEN_L, 0x0000, /* DAC 0dB */ + WM_DAC_ATTEN_L, 0x0100, /* DAC 0dB */ + WM_DAC_ATTEN_R, 0x0000, /* DAC 0dB */ + WM_DAC_ATTEN_R, 0x0100, /* DAC 0dB */ + // WM_DAC_MASTER, 0x0100, /* DAC master muted */ + WM_PHASE_SWAP, 0x0000, /* phase normal */ + WM_DAC_CTRL2, 0x0000, /* no deemphasis, no ZFLG */ + WM_ADC_ATTEN_L, 0x0000, /* ADC muted */ + WM_ADC_ATTEN_R, 0x0000, /* ADC muted */ +#if 0 + WM_ALC_CTRL1, 0x007b, /* */ + WM_ALC_CTRL2, 0x0000, /* */ + WM_ALC_CTRL3, 0x0000, /* */ + WM_NOISE_GATE, 0x0000, /* */ +#endif + WM_DAC_MUTE, 0x0000, /* DAC unmute */ + WM_ADC_MUX, 0x0003, /* ADC unmute, both CD/Line On */ + }; + static unsigned char cs_inits[] = { + 0x04, 0x80, /* RUN, RXP0 */ + 0x05, 0x05, /* slave, 24bit */ + 0x01, 0x00, + 0x02, 0x00, + 0x03, 0x00, + }; + unsigned int i; + + ice->vt1720 = 1; + ice->num_total_dacs = 2; + ice->num_total_adcs = 2; + + /* to remeber the register values */ + ice->akm = kcalloc(1, sizeof(akm4xxx_t), GFP_KERNEL); + if (! ice->akm) + return -ENOMEM; + ice->akm_codecs = 1; + + /* HACK - use this as the SPDIF source. + * don't call snd_ice1712_gpio_get/put(), otherwise it's overwritten + */ + ice->gpio.saved[0] = 0; + + /* initialize WM8776 codec */ + for (i = 0; i < ARRAY_SIZE(wm_inits); i += 2) + wm_put(ice, wm_inits[i], wm_inits[i+1]); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + for (i = 0; i < ARRAY_SIZE(wm_inits2); i += 2) + wm_put(ice, wm_inits2[i], wm_inits2[i+1]); + + /* initialize CS8416 codec */ + /* assert PRST#; MT05 bit 7 */ + outb(inb(ICEMT1724(ice, AC97_CMD)) | 0x80, ICEMT1724(ice, AC97_CMD)); + mdelay(5); + /* deassert PRST# */ + outb(inb(ICEMT1724(ice, AC97_CMD)) & ~0x80, ICEMT1724(ice, AC97_CMD)); + + for (i = 0; i < ARRAY_SIZE(cs_inits); i += 2) + spi_write(ice, CS_DEV, cs_inits[i], cs_inits[i+1]); + + return 0; +} + + +/* + * Pontis boards don't provide the EEPROM data at all. + * hence the driver needs to sets up it properly. + */ + +static unsigned char pontis_eeprom[] __devinitdata = { + 0x08, /* SYSCONF: clock 256, mpu401, spdif-in/ADC, 1DAC */ + 0x80, /* ACLINK: I2S */ + 0xf8, /* I2S: vol, 96k, 24bit, 192k */ + 0xc3, /* SPDIF: out-en, out-int, spdif-in */ + 0x07, /* GPIO_DIR */ + 0x00, /* GPIO_DIR1 */ + 0x00, /* GPIO_DIR2 (ignored) */ + 0x0f, /* GPIO_MASK (4-7 reserved for CS8416) */ + 0xff, /* GPIO_MASK1 */ + 0x00, /* GPIO_MASK2 (ignored) */ + 0x06, /* GPIO_STATE (0-low, 1-high, 2-high) */ + 0x00, /* GPIO_STATE1 */ + 0x00, /* GPIO_STATE2 (ignored) */ +}; + +/* entry point */ +struct snd_ice1712_card_info snd_vt1720_pontis_cards[] __devinitdata = { + { + .subvendor = VT1720_SUBDEVICE_PONTIS_MS300, + .name = "Pontis MS300", + .model = "ms300", + .chip_init = pontis_init, + .build_controls = pontis_add_controls, + .eeprom_size = sizeof(pontis_eeprom), + .eeprom_data = pontis_eeprom, + }, + { } /* terminator */ +}; --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/sound/pci/ice1712/pontis.h 2004-09-02 20:51:53.000000000 -0700 @@ -0,0 +1,33 @@ +#ifndef __SOUND_PONTIS_H +#define __SOUND_PONTIS_H + +/* + * ALSA driver for VIA VT1724 (Envy24HT) + * + * Lowlevel functions for Pontis MS300 boards + * + * Copyright (c) 2004 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define PONTIS_DEVICE_DESC "{Pontis,MS300}," + +#define VT1720_SUBDEVICE_PONTIS_MS300 0x00020002 /* a dummy id for MS300 */ + +extern struct snd_ice1712_card_info snd_vt1720_pontis_cards[]; + +#endif /* __SOUND_PONTIS_H */ --- linux-2.6.9-rc1/sound/pci/ice1712/revo.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/sound/pci/ice1712/revo.c 2004-09-02 20:51:53.000000000 -0700 @@ -128,7 +128,7 @@ static int __devinit revo_init(ice1712_t switch (ice->eeprom.subvendor) { case VT1724_SUBDEVICE_REVOLUTION71: ice->num_total_dacs = 8; - ice->num_total_adcs = 4; + ice->num_total_adcs = 2; break; default: snd_BUG(); @@ -136,7 +136,7 @@ static int __devinit revo_init(ice1712_t } /* second stage of initialization, analog parts and others */ - ak = ice->akm = snd_kcalloc(sizeof(akm4xxx_t) * 2, GFP_KERNEL); + ak = ice->akm = kcalloc(2, sizeof(akm4xxx_t), GFP_KERNEL); if (! ak) return -ENOMEM; ice->akm_codecs = 2; --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/sound/pci/ice1712/vt1720_mobo.c 2004-09-02 20:51:53.000000000 -0700 @@ -0,0 +1,106 @@ +/* + * ALSA driver for VT1720/VT1724 (Envy24PT/Envy24HT) + * + * Lowlevel functions for VT1720-based motherboards + * + * Copyright (c) 2004 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ice1712.h" +#include "vt1720_mobo.h" + + +static int __devinit k8x800_init(ice1712_t *ice) +{ + ice->vt1720 = 1; + + /* VT1616 codec */ + ice->num_total_dacs = 6; + ice->num_total_adcs = 2; + + /* WM8728 codec */ + /* FIXME: TODO */ + + return 0; +} + +static int __devinit k8x800_add_controls(ice1712_t *ice) +{ + /* FIXME: needs some quirks for VT1616? */ + return 0; +} + +/* EEPROM image */ + +static unsigned char k8x800_eeprom[] __devinitdata = { + 0x01, /* SYSCONF: clock 256, 1ADC, 2DACs */ + 0x02, /* ACLINK: ACLINK, packed */ + 0x00, /* I2S: - */ + 0x00, /* SPDIF: - */ + 0xff, /* GPIO_DIR */ + 0xff, /* GPIO_DIR1 */ + 0x00, /* - */ + 0xff, /* GPIO_MASK */ + 0xff, /* GPIO_MASK1 */ + 0x00, /* - */ + 0x00, /* GPIO_STATE */ + 0x00, /* GPIO_STATE1 */ + 0x00, /* - */ +}; + + +/* entry point */ +struct snd_ice1712_card_info snd_vt1720_mobo_cards[] __devinitdata = { + { + .subvendor = VT1720_SUBDEVICE_K8X800, + .name = "Albatron K8X800 Pro II", + .model = "k8x800", + .chip_init = k8x800_init, + .build_controls = k8x800_add_controls, + .eeprom_size = sizeof(k8x800_eeprom), + .eeprom_data = k8x800_eeprom, + }, + { + .subvendor = VT1720_SUBDEVICE_ZNF3_150, + .name = "Chaintech ZNF3-150", + /* identical with k8x800 */ + .chip_init = k8x800_init, + .build_controls = k8x800_add_controls, + .eeprom_size = sizeof(k8x800_eeprom), + .eeprom_data = k8x800_eeprom, + }, + { + .subvendor = VT1720_SUBDEVICE_ZNF3_250, + .name = "Chaintech ZNF3-250", + /* identical with k8x800 */ + .chip_init = k8x800_init, + .build_controls = k8x800_add_controls, + .eeprom_size = sizeof(k8x800_eeprom), + .eeprom_data = k8x800_eeprom, + }, + { } /* terminator */ +}; + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/sound/pci/ice1712/vt1720_mobo.h 2004-09-02 20:51:53.000000000 -0700 @@ -0,0 +1,37 @@ +#ifndef __SOUND_VT1720_MOBO_H +#define __SOUND_VT1720_MOBO_H + +/* + * ALSA driver for VT1720/VT1724 (Envy24PT/Envy24HT) + * + * Lowlevel functions for VT1720-based motherboards + * + * Copyright (c) 2004 Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define VT1720_MOBO_DEVICE_DESC "{Albatron,K8X800 Pro II},"\ + "{Chaintech,ZNF3-150},"\ + "{Chaintech,ZNF3-250}," + +#define VT1720_SUBDEVICE_K8X800 0xf217052c +#define VT1720_SUBDEVICE_ZNF3_150 0x0f2741f6 +#define VT1720_SUBDEVICE_ZNF3_250 0x0f2745f6 + +extern struct snd_ice1712_card_info snd_vt1720_mobo_cards[]; + +#endif /* __SOUND_VT1720_MOBO_H */ --- linux-2.6.9-rc1/sound/pci/intel8x0.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/sound/pci/intel8x0.c 2004-09-02 20:51:53.000000000 -0700 @@ -48,8 +48,7 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; SiS 7012; Ali 5455"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Intel,82801AA-ICH}," +MODULE_SUPPORTED_DEVICE("{{Intel,82801AA-ICH}," "{Intel,82901AB-ICH0}," "{Intel,82801BA-ICH2}," "{Intel,82801CA-ICH3}," @@ -75,6 +74,7 @@ static char *id[SNDRV_CARDS] = SNDRV_DEF static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ static int ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; static int ac97_quirk[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = AC97_TUNE_DEFAULT}; +static int buggy_irq[SNDRV_CARDS]; #ifdef SUPPORT_JOYSTICK static int joystick[SNDRV_CARDS]; #endif @@ -85,28 +85,23 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for Intel i8x0 soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for Intel i8x0 soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable Intel i8x0 soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(ac97_clock, int, boot_devs, 0444); MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (0 = auto-detect)."); -MODULE_PARM_SYNTAX(ac97_clock, SNDRV_ENABLED ",default:0"); module_param_array(ac97_quirk, int, boot_devs, 0444); MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware."); -MODULE_PARM_SYNTAX(ac97_quirk, SNDRV_ENABLED ",allows:{{-1,4}},dialog:list,default:-1"); +module_param_array(buggy_irq, bool, boot_devs, 0444); +MODULE_PARM_DESC(buggy_irq, "Enable workaround for buggy interrupts on some motherboards."); #ifdef SUPPORT_JOYSTICK module_param_array(joystick, bool, boot_devs, 0444); MODULE_PARM_DESC(joystick, "Enable joystick for Intel i8x0 soundcard."); -MODULE_PARM_SYNTAX(joystick, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); #endif #ifdef SUPPORT_MIDI module_param_array(mpu_port, int, boot_devs, 0444); MODULE_PARM_DESC(mpu_port, "MPU401 port # for Intel i8x0 driver."); -MODULE_PARM_SYNTAX(mpu_port, SNDRV_ENABLED ",allows:{{0},{0x330},{0x300}},dialog:list"); #endif /* @@ -146,9 +141,15 @@ MODULE_PARM_SYNTAX(mpu_port, SNDRV_ENABL #ifndef PCI_DEVICE_ID_NVIDIA_MCP_AUDIO #define PCI_DEVICE_ID_NVIDIA_MCP_AUDIO 0x01b1 #endif +#ifndef PCI_DEVICE_ID_NVIDIA_CK804_AUDIO +#define PCI_DEVICE_ID_NVIDIA_CK804_AUDIO 0x0059 +#endif #ifndef PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO #define PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO 0x006a #endif +#ifndef PCI_DEVICE_ID_NVIDIA_CK8_AUDIO +#define PCI_DEVICE_ID_NVIDIA_CK8_AUDIO 0x008a +#endif #ifndef PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO #define PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO 0x00da #endif @@ -397,25 +398,18 @@ typedef struct { } ichdev_t; typedef struct _snd_intel8x0 intel8x0_t; -#define chip_t intel8x0_t struct _snd_intel8x0 { unsigned int device_type; - char ac97_name[32]; - char ctrl_name[32]; int irq; unsigned int mmio; unsigned long addr; unsigned long remap_addr; - struct resource *res; unsigned int bm_mmio; unsigned long bmaddr; unsigned long remap_bmaddr; - struct resource *res_bm; - - struct snd_dma_device dma_dev; struct pci_dev *pci; snd_card_t *card; @@ -430,6 +424,7 @@ struct _snd_intel8x0 { int in_ac97_init: 1, in_sdin_init: 1; int fix_nocache: 1; /* workaround for 440MX */ + int buggy_irq: 1; /* workaround for buggy mobos */ ac97_bus_t *ac97_bus; ac97_t *ac97[3]; @@ -444,10 +439,6 @@ struct _snd_intel8x0 { struct snd_dma_buffer bdbars; u32 int_sta_reg; /* interrupt status register */ u32 int_sta_mask; /* interrupt status mask */ - -#ifdef CONFIG_PM - u32 pci_state[64 / sizeof(u32)]; -#endif }; static struct pci_device_id snd_intel8x0_ids[] = { @@ -462,7 +453,10 @@ static struct pci_device_id snd_intel8x0 { 0x8086, 0x7195, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 440MX */ { 0x1039, 0x7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_SIS }, /* SI7012 */ { 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE */ + { 0x10de, 0x003a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* MCP04 */ { 0x10de, 0x006a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE2 */ + { 0x10de, 0x0059, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* CK804 */ + { 0x10de, 0x008a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* CK8 */ { 0x10de, 0x00da, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE3 */ { 0x10de, 0x00ea, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* CK8S */ { 0x1022, 0x746d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD8111 */ @@ -605,7 +599,7 @@ static void snd_intel8x0_codec_write(ac9 unsigned short reg, unsigned short val) { - intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return); + intel8x0_t *chip = ac97->private_data; spin_lock(&chip->ac97_lock); if (snd_intel8x0_codec_semaphore(chip, ac97->num) < 0) { @@ -619,7 +613,7 @@ static void snd_intel8x0_codec_write(ac9 static unsigned short snd_intel8x0_codec_read(ac97_t *ac97, unsigned short reg) { - intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return ~0); + intel8x0_t *chip = ac97->private_data; unsigned short res; unsigned int tmp; @@ -642,6 +636,21 @@ static unsigned short snd_intel8x0_codec return res; } +static void snd_intel8x0_codec_read_test(intel8x0_t *chip, unsigned int codec) +{ + unsigned int tmp; + + spin_lock(&chip->ac97_lock); + if (snd_intel8x0_codec_semaphore(chip, codec) >= 0) { + iagetword(chip, codec * 0x80); + if ((tmp = igetdword(chip, ICHREG(GLOB_STA))) & ICH_RCS) { + /* reset RCS and preserve other R/WC bits */ + iputdword(chip, ICHREG(GLOB_STA), tmp & ~(ICH_SRI|ICH_PRI|ICH_TRI|ICH_GSCI)); + } + } + spin_unlock(&chip->ac97_lock); +} + /* * access to AC97 for Ali5455 */ @@ -669,7 +678,7 @@ static int snd_intel8x0_ali_codec_semaph static unsigned short snd_intel8x0_ali_codec_read(ac97_t *ac97, unsigned short reg) { - intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return ~0); + intel8x0_t *chip = ac97->private_data; unsigned short data = 0xffff; spin_lock(&chip->ac97_lock); @@ -689,7 +698,7 @@ static unsigned short snd_intel8x0_ali_c static void snd_intel8x0_ali_codec_write(ac97_t *ac97, unsigned short reg, unsigned short val) { - intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return); + intel8x0_t *chip = ac97->private_data; spin_lock(&chip->ac97_lock); if (snd_intel8x0_ali_codec_semaphore(chip)) { @@ -778,6 +787,7 @@ static inline void snd_intel8x0_update(i int status, civ, i, step; int ack = 0; + spin_lock(&chip->reg_lock); status = igetbyte(chip, port + ichdev->roff_sr); civ = igetbyte(chip, port + ICH_REG_OFF_CIV); if (!(status & ICH_BCIS)) { @@ -811,10 +821,9 @@ static inline void snd_intel8x0_update(i ack = 1; } } + spin_unlock(&chip->reg_lock); if (ack && ichdev->substream) { - spin_unlock(&chip->reg_lock); snd_pcm_period_elapsed(ichdev->substream); - spin_lock(&chip->reg_lock); } iputbyte(chip, port + ichdev->roff_sr, status & (ICH_FIFOE | ICH_BCIS | ICH_LVBCI)); @@ -822,24 +831,22 @@ static inline void snd_intel8x0_update(i static irqreturn_t snd_intel8x0_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - intel8x0_t *chip = snd_magic_cast(intel8x0_t, dev_id, return IRQ_NONE); + intel8x0_t *chip = dev_id; ichdev_t *ichdev; unsigned int status; unsigned int i; - spin_lock(&chip->reg_lock); status = igetdword(chip, chip->int_sta_reg); + if (status == 0xffffffff) /* we are not yet resumed */ + return IRQ_NONE; + if ((status & chip->int_sta_mask) == 0) { if (status) { /* ack */ iputdword(chip, chip->int_sta_reg, status); - /* some Nforce[2] boards have problems when - IRQ_NONE is returned here. - */ - if (chip->device_type != DEVICE_NFORCE) + if (! chip->buggy_irq) status = 0; } - spin_unlock(&chip->reg_lock); return IRQ_RETVAL(status); } @@ -851,7 +858,6 @@ static irqreturn_t snd_intel8x0_interrup /* ack them */ iputdword(chip, chip->int_sta_reg, status & chip->int_sta_mask); - spin_unlock(&chip->reg_lock); return IRQ_HANDLED; } @@ -1021,8 +1027,10 @@ static void snd_intel8x0_setup_pcm_out(i /* reset to 2ch once to keep the 6 channel data in alignment, * to start from Front Left always */ - iputdword(chip, ICHREG(GLOB_CNT), (cnt & 0xcfffff)); - mdelay(50); /* grrr... */ + if (cnt & ICH_PCM_246_MASK) { + iputdword(chip, ICHREG(GLOB_CNT), cnt & ~ICH_PCM_246_MASK); + msleep(50); /* grrr... */ + } } else if (chip->device_type == DEVICE_INTEL_ICH4) { if (sample_bits > 16) cnt |= ICH_PCM_20BIT; @@ -1041,16 +1049,16 @@ static int snd_intel8x0_pcm_prepare(snd_ ichdev->physbuf = runtime->dma_addr; ichdev->size = snd_pcm_lib_buffer_bytes(substream); ichdev->fragsize = snd_pcm_lib_period_bytes(substream); + spin_lock_irq(&chip->reg_lock); if (ichdev->ichd == ICHD_PCMOUT) { - spin_lock(&chip->reg_lock); snd_intel8x0_setup_pcm_out(chip, runtime->channels, runtime->sample_bits); - spin_unlock(&chip->reg_lock); if (chip->device_type == DEVICE_INTEL_ICH4) { ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1; } } snd_intel8x0_setup_periods(chip, ichdev); + spin_unlock_irq(&chip->reg_lock); return 0; } @@ -1058,17 +1066,25 @@ static snd_pcm_uframes_t snd_intel8x0_pc { intel8x0_t *chip = snd_pcm_substream_chip(substream); ichdev_t *ichdev = get_ichdev(substream); - unsigned long flags; size_t ptr1, ptr; + int civ, timeout = 10; + unsigned int position; - ptr1 = igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << ichdev->pos_shift; - if (ptr1 != 0) - ptr = ichdev->fragsize1 - ptr1; - else - ptr = 0; - spin_lock_irqsave(&chip->reg_lock, flags); - ptr += ichdev->position; - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_lock(&chip->reg_lock); + do { + civ = igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV); + ptr1 = igetword(chip, ichdev->reg_offset + ichdev->roff_picb); + position = ichdev->position; + if (ptr1 == 0) + udelay(1); + if (civ == igetbyte(chip, ichdev->reg_offset + ICH_REG_OFF_CIV) && + ptr1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb)) + break; + } while (timeout--); + ptr1 <<= ichdev->pos_shift; + ptr = ichdev->fragsize1 - ptr1; + ptr += position; + spin_unlock(&chip->reg_lock); if (ptr >= ichdev->size) return 0; return bytes_to_frames(substream->runtime, ptr); @@ -1099,10 +1115,8 @@ static unsigned int channels4[] = { 2, 4, }; -#define CHANNELS4 sizeof(channels4) / sizeof(channels4[0]) - static snd_pcm_hw_constraint_list_t hw_constraints_channels4 = { - .count = CHANNELS4, + .count = ARRAY_SIZE(channels4), .list = channels4, .mask = 0, }; @@ -1111,10 +1125,8 @@ static unsigned int channels6[] = { 2, 4, 6, }; -#define CHANNELS6 sizeof(channels6) / sizeof(channels6[0]) - static snd_pcm_hw_constraint_list_t hw_constraints_channels6 = { - .count = CHANNELS6, + .count = ARRAY_SIZE(channels6), .list = channels6, .mask = 0, }; @@ -1156,8 +1168,10 @@ static int snd_intel8x0_playback_open(sn runtime->hw.channels_max = 4; snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels4); } - if (chip->smp20bit) + if (chip->smp20bit) { runtime->hw.formats |= SNDRV_PCM_FMTBIT_S32_LE; + snd_pcm_hw_constraint_msbits(runtime, 0, 32, 20); + } return 0; } @@ -1249,14 +1263,14 @@ static int snd_intel8x0_spdif_close(snd_ static int snd_intel8x0_ali_ac97spdifout_open(snd_pcm_substream_t * substream) { intel8x0_t *chip = snd_pcm_substream_chip(substream); - unsigned long flags; unsigned int val; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); val = igetdword(chip, ICHREG(ALI_INTERFACECR)); val |= ICH_ALI_IF_AC97SP; + iputdword(chip, ICHREG(ALI_INTERFACECR), val); /* also needs to set ALI_SC_CODEC_SPDF correctly */ - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_AC97SPDIFOUT]); } @@ -1264,14 +1278,14 @@ static int snd_intel8x0_ali_ac97spdifout static int snd_intel8x0_ali_ac97spdifout_close(snd_pcm_substream_t * substream) { intel8x0_t *chip = snd_pcm_substream_chip(substream); - unsigned long flags; unsigned int val; chip->ichd[ALID_AC97SPDIFOUT].substream = NULL; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); val = igetdword(chip, ICHREG(ALI_INTERFACECR)); val &= ~ICH_ALI_IF_AC97SP; - spin_unlock_irqrestore(&chip->reg_lock, flags); + iputdword(chip, ICHREG(ALI_INTERFACECR), val); + spin_unlock_irq(&chip->reg_lock); return 0; } @@ -1627,13 +1641,13 @@ static int __devinit snd_intel8x0_pcm(in static void snd_intel8x0_mixer_free_ac97_bus(ac97_bus_t *bus) { - intel8x0_t *chip = snd_magic_cast(intel8x0_t, bus->private_data, return); + intel8x0_t *chip = bus->private_data; chip->ac97_bus = NULL; } static void snd_intel8x0_mixer_free_ac97(ac97_t *ac97) { - intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return); + intel8x0_t *chip = ac97->private_data; chip->ac97[ac97->num] = NULL; } @@ -1726,12 +1740,30 @@ static struct ac97_quirk ac97_quirks[] _ .name = "Dell Optiplex GX260", /* AD1981A */ .type = AC97_TUNE_HP_ONLY }, + { + .vendor = 0x1028, + .device = 0x012d, + .name = "Dell Precision 450", /* AD1981B*/ + .type = AC97_TUNE_HP_ONLY + }, { /* FIXME: which codec? */ .vendor = 0x103c, .device = 0x00c3, .name = "Hewlett-Packard onboard", .type = AC97_TUNE_HP_ONLY }, + { + .vendor = 0x103c, + .device = 0x12f1, + .name = "HP xw8200", /* AD1981B*/ + .type = AC97_TUNE_HP_ONLY + }, + { + .vendor = 0x103c, + .device = 0x3008, + .name = "HP xw4200", /* AD1981B*/ + .type = AC97_TUNE_HP_ONLY + }, { .vendor = 0x1043, .device = 0x80f3, @@ -1802,6 +1834,12 @@ static struct ac97_quirk ac97_quirks[] _ }, { .vendor = 0x8086, + .device = 0x4d56, + .name = "Intel ICH/AD1885", + .type = AC97_TUNE_HP_ONLY + }, + { + .vendor = 0x8086, .device = 0x6000, .mask = 0xfff0, .name = "Intel ICH5/AD1985", @@ -1828,12 +1866,21 @@ static struct ac97_quirk ac97_quirks[] _ static int __devinit snd_intel8x0_mixer(intel8x0_t *chip, int ac97_clock, int ac97_quirk) { - ac97_bus_t bus, *pbus; - ac97_t ac97, *x97; + ac97_bus_t *pbus; + ac97_template_t ac97; int err; unsigned int i, codecs; unsigned int glob_sta = 0; int spdif_idx = -1; /* disabled */ + ac97_bus_ops_t *ops; + static ac97_bus_ops_t standard_bus_ops = { + .write = snd_intel8x0_codec_write, + .read = snd_intel8x0_codec_read, + }; + static ac97_bus_ops_t ali_bus_ops = { + .write = snd_intel8x0_ali_codec_write, + .read = snd_intel8x0_ali_codec_read, + }; switch (chip->device_type) { case DEVICE_NFORCE: @@ -1849,13 +1896,6 @@ static int __devinit snd_intel8x0_mixer( }; chip->in_ac97_init = 1; - memset(&bus, 0, sizeof(bus)); - bus.private_data = chip; - bus.private_free = snd_intel8x0_mixer_free_ac97_bus; - if (ac97_clock >= 8000 && ac97_clock <= 48000) - bus.clock = ac97_clock; - else - bus.clock = 48000; memset(&ac97, 0, sizeof(ac97)); ac97.private_data = chip; @@ -1863,8 +1903,7 @@ static int __devinit snd_intel8x0_mixer( ac97.scaps = AC97_SCAP_SKIP_MODEM; if (chip->device_type != DEVICE_ALI) { glob_sta = igetdword(chip, ICHREG(GLOB_STA)); - bus.write = snd_intel8x0_codec_write; - bus.read = snd_intel8x0_codec_read; + ops = &standard_bus_ops; if (chip->device_type == DEVICE_INTEL_ICH4) { codecs = 0; if (glob_sta & ICH_PCR) @@ -1875,19 +1914,15 @@ static int __devinit snd_intel8x0_mixer( codecs++; chip->in_sdin_init = 1; for (i = 0; i < codecs; i++) { - ac97.num = i; - snd_intel8x0_codec_read(&ac97, 0); + snd_intel8x0_codec_read_test(chip, i); chip->ac97_sdin[i] = igetbyte(chip, ICHREG(SDM)) & ICH_LDI_MASK; } - ac97.num = 0; chip->in_sdin_init = 0; } else { codecs = glob_sta & ICH_SCR ? 2 : 1; } - bus.vra = 1; } else { - bus.write = snd_intel8x0_ali_codec_write; - bus.read = snd_intel8x0_ali_codec_read; + ops = &ali_bus_ops; codecs = 1; /* detect the secondary codec */ for (i = 0; i < 100; i++) { @@ -1899,23 +1934,28 @@ static int __devinit snd_intel8x0_mixer( iputdword(chip, ICHREG(ALI_RTSR), reg | 0x40); udelay(1); } - /* FIXME: my test board doens't work well with VRA... */ - bus.vra = 0; } - if ((err = snd_ac97_bus(chip->card, &bus, &pbus)) < 0) + if ((err = snd_ac97_bus(chip->card, 0, ops, chip, &pbus)) < 0) goto __err; + pbus->private_free = snd_intel8x0_mixer_free_ac97_bus; + pbus->shared_type = AC97_SHARED_TYPE_ICH; /* shared with modem driver */ + if (ac97_clock >= 8000 && ac97_clock <= 48000) + pbus->clock = ac97_clock; + /* FIXME: my test board doesn't work well with VRA... */ + if (chip->device_type == DEVICE_ALI) + pbus->no_vra = 1; chip->ac97_bus = pbus; + ac97.pci = chip->pci; for (i = 0; i < codecs; i++) { ac97.num = i; - if ((err = snd_ac97_mixer(pbus, &ac97, &x97)) < 0) { + if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97[i])) < 0) { if (err != -EACCES) snd_printk(KERN_ERR "Unable to initialize codec #%d\n", i); if (i == 0) goto __err; continue; } - chip->ac97[i] = x97; } /* tune up the primary codec */ snd_ac97_tune_hardware(chip->ac97[0], ac97_quirks, ac97_quirk); @@ -2179,26 +2219,19 @@ static int snd_intel8x0_free(intel8x0_t /* --- */ synchronize_irq(chip->irq); __hw_end: + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); if (chip->bdbars.area) { if (chip->fix_nocache) fill_nocache(chip->bdbars.area, chip->bdbars.bytes, 0); - snd_dma_free_pages(&chip->dma_dev, &chip->bdbars); + snd_dma_free_pages(&chip->bdbars); } if (chip->remap_addr) iounmap((void *) chip->remap_addr); if (chip->remap_bmaddr) iounmap((void *) chip->remap_bmaddr); - if (chip->res) { - release_resource(chip->res); - kfree_nocheck(chip->res); - } - if (chip->res_bm) { - release_resource(chip->res_bm); - kfree_nocheck(chip->res_bm); - } - if (chip->irq >= 0) - free_irq(chip->irq, (void *)chip); - snd_magic_kfree(chip); + pci_release_regions(chip->pci); + kfree(chip); return 0; } @@ -2208,7 +2241,7 @@ static int snd_intel8x0_free(intel8x0_t */ static int intel8x0_suspend(snd_card_t *card, unsigned int state) { - intel8x0_t *chip = snd_magic_cast(intel8x0_t, card->pm_private_data, return -EINVAL); + intel8x0_t *chip = card->pm_private_data; int i; for (i = 0; i < chip->pcm_devs; i++) @@ -2216,17 +2249,15 @@ static int intel8x0_suspend(snd_card_t * for (i = 0; i < 3; i++) if (chip->ac97[i]) snd_ac97_suspend(chip->ac97[i]); - pci_save_state(chip->pci, chip->pci_state); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); return 0; } static int intel8x0_resume(snd_card_t *card, unsigned int state) { - intel8x0_t *chip = snd_magic_cast(intel8x0_t, card->pm_private_data, return -EINVAL); + intel8x0_t *chip = card->pm_private_data; int i; - pci_restore_state(chip->pci, chip->pci_state); pci_enable_device(chip->pci); pci_set_master(chip->pci); snd_intel8x0_chip_init(chip, 0); @@ -2264,7 +2295,6 @@ static void __devinit intel8x0_measure_a ichdev_t *ichdev; unsigned long port; unsigned long pos, t; - unsigned long flags; struct timeval start_time, stop_time; if (chip->ac97_bus->clock != 48000) @@ -2287,7 +2317,7 @@ static void __devinit intel8x0_measure_a } snd_intel8x0_setup_periods(chip, ichdev); port = ichdev->reg_offset; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); /* trigger */ if (chip->device_type != DEVICE_ALI) iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE | ICH_STARTBM); @@ -2296,7 +2326,7 @@ static void __devinit intel8x0_measure_a iputdword(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot); } do_gettimeofday(&start_time); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); #if 0 set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ / 20); @@ -2304,7 +2334,7 @@ static void __devinit intel8x0_measure_a /* FIXME: schedule() can take too long time and overlap the boundary.. */ mdelay(50); #endif - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); /* check the position */ pos = ichdev->fragsize1; pos -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << ichdev->pos_shift; @@ -2322,7 +2352,7 @@ static void __devinit intel8x0_measure_a ; } iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); t = stop_time.tv_sec - start_time.tv_sec; t *= 1000000; @@ -2346,7 +2376,7 @@ static void __devinit intel8x0_measure_a static void snd_intel8x0_proc_read(snd_info_entry_t * entry, snd_info_buffer_t * buffer) { - intel8x0_t *chip = snd_magic_cast(intel8x0_t, entry->private_data, return); + intel8x0_t *chip = entry->private_data; unsigned int tmp; snd_iprintf(buffer, "Intel8x0\n\n"); @@ -2379,7 +2409,7 @@ static void __devinit snd_intel8x0_proc_ static int snd_intel8x0_dev_free(snd_device_t *device) { - intel8x0_t *chip = snd_magic_cast(intel8x0_t, device->device_data, return -ENXIO); + intel8x0_t *chip = device->device_data; return snd_intel8x0_free(chip); } @@ -2438,7 +2468,7 @@ static int __devinit snd_intel8x0_create if ((err = pci_enable_device(pci)) < 0) return err; - chip = snd_magic_kcalloc(intel8x0_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; spin_lock_init(&chip->reg_lock); @@ -2447,32 +2477,33 @@ static int __devinit snd_intel8x0_create chip->card = card; chip->pci = pci; chip->irq = -1; - snd_intel8x0_proc_init(chip); - sprintf(chip->ac97_name, "%s - AC'97", card->shortname); - sprintf(chip->ctrl_name, "%s - Controller", card->shortname); + if (pci->vendor == PCI_VENDOR_ID_INTEL && pci->device == PCI_DEVICE_ID_INTEL_440MX) chip->fix_nocache = 1; /* enable workaround */ + + /* some Nforce[2] and ICH boards have problems with IRQ handling. + * Needs to return IRQ_HANDLED for unknown irqs. + */ + if (device_type == DEVICE_NFORCE) + chip->buggy_irq = 1; + + if ((err = pci_request_regions(pci, card->shortname)) < 0) { + kfree(chip); + return err; + } + if (device_type == DEVICE_ALI) { /* ALI5455 has no ac97 region */ chip->bmaddr = pci_resource_start(pci, 0); - if ((chip->res_bm = request_region(chip->bmaddr, 256, chip->ctrl_name)) == NULL) { - snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->bmaddr, chip->bmaddr + 64 - 1); - snd_intel8x0_free(chip); - return -EBUSY; - } goto port_inited; } if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) { /* ICH4 and Nforce */ chip->mmio = 1; chip->addr = pci_resource_start(pci, 2); - if ((chip->res = request_mem_region(chip->addr, 512, chip->ac97_name)) == NULL) { - snd_printk("unable to grab I/O memory 0x%lx-0x%lx\n", chip->addr, chip->addr + 512 - 1); - snd_intel8x0_free(chip); - return -EBUSY; - } - chip->remap_addr = (unsigned long) ioremap_nocache(chip->addr, 512); + chip->remap_addr = (unsigned long) ioremap_nocache(chip->addr, + pci_resource_len(pci, 2)); if (chip->remap_addr == 0) { snd_printk("AC'97 space ioremap problem\n"); snd_intel8x0_free(chip); @@ -2480,21 +2511,12 @@ static int __devinit snd_intel8x0_create } } else { chip->addr = pci_resource_start(pci, 0); - if ((chip->res = request_region(chip->addr, 256, chip->ac97_name)) == NULL) { - snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->addr, chip->addr + 256 - 1); - snd_intel8x0_free(chip); - return -EBUSY; - } } if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) { /* ICH4 */ chip->bm_mmio = 1; chip->bmaddr = pci_resource_start(pci, 3); - if ((chip->res_bm = request_mem_region(chip->bmaddr, 256, chip->ctrl_name)) == NULL) { - snd_printk("unable to grab I/O memory 0x%lx-0x%lx\n", chip->bmaddr, chip->bmaddr + 512 - 1); - snd_intel8x0_free(chip); - return -EBUSY; - } - chip->remap_bmaddr = (unsigned long) ioremap_nocache(chip->bmaddr, 256); + chip->remap_bmaddr = (unsigned long) ioremap_nocache(chip->bmaddr, + pci_resource_len(pci, 3)); if (chip->remap_bmaddr == 0) { snd_printk("Controller space ioremap problem\n"); snd_intel8x0_free(chip); @@ -2502,11 +2524,6 @@ static int __devinit snd_intel8x0_create } } else { chip->bmaddr = pci_resource_start(pci, 1); - if ((chip->res_bm = request_region(chip->bmaddr, 64, chip->ctrl_name)) == NULL) { - snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->bmaddr, chip->bmaddr + 64 - 1); - snd_intel8x0_free(chip); - return -EBUSY; - } } port_inited: @@ -2552,13 +2569,11 @@ static int __devinit snd_intel8x0_create ichdev->pos_shift = (device_type == DEVICE_SIS) ? 0 : 1; } - memset(&chip->dma_dev, 0, sizeof(chip->dma_dev)); - chip->dma_dev.type = SNDRV_DMA_TYPE_DEV; - chip->dma_dev.dev = snd_dma_pci_data(pci); - /* allocate buffer descriptor lists */ /* the start of each lists must be aligned to 8 bytes */ - if (snd_dma_alloc_pages(&chip->dma_dev, chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, &chip->bdbars) < 0) { + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), + chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, + &chip->bdbars) < 0) { snd_intel8x0_free(chip); snd_printk(KERN_ERR "intel8x0: cannot allocate buffer descriptors\n"); return -ENOMEM; @@ -2614,6 +2629,9 @@ static struct shortname_table { { PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO, "NVidia nForce2" }, { PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO, "NVidia nForce3" }, { PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO, "NVidia CK8S" }, + { PCI_DEVICE_ID_NVIDIA_CK804_AUDIO, "NVidia CK804" }, + { PCI_DEVICE_ID_NVIDIA_CK8_AUDIO, "NVidia CK8" }, + { 0x003a, "NVidia MCP04" }, { 0x746d, "AMD AMD8111" }, { 0x7445, "AMD AMD768" }, { 0x5455, "ALi M5455" }, @@ -2661,6 +2679,8 @@ static int __devinit snd_intel8x0_probe( snd_card_free(card); return err; } + if (buggy_irq[dev]) + chip->buggy_irq = 1; if ((err = snd_intel8x0_mixer(chip, ac97_clock[dev], ac97_quirk[dev])) < 0) { snd_card_free(card); @@ -2681,6 +2701,8 @@ static int __devinit snd_intel8x0_probe( } else mpu_port[dev] = 0; + snd_intel8x0_proc_init(chip); + sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, chip->addr, chip->irq); --- linux-2.6.9-rc1/sound/pci/intel8x0m.c 2004-08-24 00:02:48.000000000 -0700 +++ 25/sound/pci/intel8x0m.c 2004-09-02 20:51:53.000000000 -0700 @@ -40,10 +40,9 @@ #include MODULE_AUTHOR("Jaroslav Kysela "); -MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440 modem"); +MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; SiS 7013; NVidia MCP/2/2S/3 modems"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Intel,82801AA-ICH}," +MODULE_SUPPORTED_DEVICE("{{Intel,82801AA-ICH}," "{Intel,82901AB-ICH0}," "{Intel,82801BA-ICH2}," "{Intel,82801CA-ICH3}," @@ -60,16 +59,12 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for Intel i8x0 modemcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for Intel i8x0 modemcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable Intel i8x0 modemcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(ac97_clock, int, boot_devs, 0444); MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (0 = auto-detect)."); -MODULE_PARM_SYNTAX(ac97_clock, SNDRV_ENABLED ",default:0"); /* * Direct registers @@ -99,18 +94,20 @@ MODULE_PARM_SYNTAX(ac97_clock, SNDRV_ENA #ifndef PCI_DEVICE_ID_SI_7013 #define PCI_DEVICE_ID_SI_7013 0x7013 #endif -#if 0 -#ifndef PCI_DEVICE_ID_NVIDIA_MCP_AUDIO -#define PCI_DEVICE_ID_NVIDIA_MCP_AUDIO 0x01b1 +#ifndef PCI_DEVICE_ID_NVIDIA_MCP_MODEM +#define PCI_DEVICE_ID_NVIDIA_MCP_MODEM 0x01c1 #endif -#ifndef PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO -#define PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO 0x006a +#ifndef PCI_DEVICE_ID_NVIDIA_MCP2_MODEM +#define PCI_DEVICE_ID_NVIDIA_MCP2_MODEM 0x0069 #endif -#ifndef PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO -#define PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO 0x00da +#ifndef PCI_DEVICE_ID_NVIDIA_MCP2S_MODEM +#define PCI_DEVICE_ID_NVIDIA_MCP2S_MODEM 0x0089 #endif +#ifndef PCI_DEVICE_ID_NVIDIA_MCP3_MODEM +#define PCI_DEVICE_ID_NVIDIA_MCP3_MODEM 0x00d9 #endif + enum { DEVICE_INTEL, DEVICE_SIS, DEVICE_ALI, DEVICE_NFORCE }; #define ICHREG(x) ICH_REG_##x @@ -228,23 +225,18 @@ typedef struct { } ichdev_t; typedef struct _snd_intel8x0m intel8x0_t; -#define chip_t intel8x0_t struct _snd_intel8x0m { unsigned int device_type; - char ac97_name[64]; - char ctrl_name[64]; int irq; unsigned int mmio; unsigned long addr; unsigned long remap_addr; - struct resource *res; unsigned int bm_mmio; unsigned long bmaddr; unsigned long remap_bmaddr; - struct resource *res_bm; struct pci_dev *pci; snd_card_t *card; @@ -261,7 +253,6 @@ struct _snd_intel8x0m { spinlock_t reg_lock; spinlock_t ac97_lock; - struct snd_dma_device dma_dev; struct snd_dma_buffer bdbars; u32 bdbars_count; u32 int_sta_reg; /* interrupt status register */ @@ -278,12 +269,12 @@ static struct pci_device_id snd_intel8x0 { 0x8086, 0x24d6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* ICH5 */ { 0x8086, 0x7196, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* 440MX */ { 0x1022, 0x7446, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD768 */ -#if 0 - /* TODO: support needed */ { 0x1039, 0x7013, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_SIS }, /* SI7013 */ - { 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE */ - { 0x10de, 0x006a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE2 */ - { 0x10de, 0x00da, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE3 */ + { 0x10de, 0x01c1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE */ + { 0x10de, 0x0069, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE2 */ + { 0x10de, 0x0089, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE2s */ + { 0x10de, 0x00d9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE3 */ +#if 0 { 0x1022, 0x746d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD8111 */ { 0x10b9, 0x5455, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALI }, /* Ali5455 */ #endif @@ -328,6 +319,14 @@ static void iputbyte(intel8x0_t *chip, u outb(val, chip->bmaddr + offset); } +static void iputword(intel8x0_t *chip, u32 offset, u16 val) +{ + if (chip->bm_mmio) + writew(val, chip->remap_bmaddr + offset); + else + outw(val, chip->bmaddr + offset); +} + static void iputdword(intel8x0_t *chip, u32 offset, u32 val) { if (chip->bm_mmio) @@ -408,7 +407,7 @@ static void snd_intel8x0_codec_write(ac9 unsigned short reg, unsigned short val) { - intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return); + intel8x0_t *chip = ac97->private_data; spin_lock(&chip->ac97_lock); if (snd_intel8x0m_codec_semaphore(chip, ac97->num) < 0) { @@ -422,7 +421,7 @@ static void snd_intel8x0_codec_write(ac9 static unsigned short snd_intel8x0_codec_read(ac97_t *ac97, unsigned short reg) { - intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return ~0); + intel8x0_t *chip = ac97->private_data; unsigned short res; unsigned int tmp; @@ -542,13 +541,17 @@ static inline void snd_intel8x0_update(i static irqreturn_t snd_intel8x0_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - intel8x0_t *chip = snd_magic_cast(intel8x0_t, dev_id, return IRQ_NONE); + intel8x0_t *chip = dev_id; ichdev_t *ichdev; unsigned int status; unsigned int i; spin_lock(&chip->reg_lock); status = igetdword(chip, chip->int_sta_reg); + if (status == 0xffffffff) { /* we are not yet resumed */ + spin_unlock(&chip->reg_lock); + return IRQ_NONE; + } if ((status & chip->int_sta_mask) == 0) { if (status) iputdword(chip, chip->int_sta_reg, status); @@ -684,9 +687,9 @@ static snd_pcm_hardware_t snd_intel8x0m_ .rate_max = 16000, .channels_min = 1, .channels_max = 1, - .buffer_bytes_max = 32 * 1024, + .buffer_bytes_max = 64 * 1024, .period_bytes_min = 32, - .period_bytes_max = 32 * 1024, + .period_bytes_max = 64 * 1024, .periods_min = 1, .periods_max = 1024, .fifo_size = 0, @@ -818,8 +821,8 @@ static struct ich_pcm_table intel_pcms[] .suffix = "Modem", .playback_ops = &snd_intel8x0m_playback_ops, .capture_ops = &snd_intel8x0m_capture_ops, - .prealloc_size = 4 * 1024, - .prealloc_max_size = 16 * 1024, + .prealloc_size = 32 * 1024, + .prealloc_max_size = 64 * 1024, }, }; @@ -872,32 +875,30 @@ static int __devinit snd_intel8x0_pcm(in static void snd_intel8x0_mixer_free_ac97_bus(ac97_bus_t *bus) { - intel8x0_t *chip = snd_magic_cast(intel8x0_t, bus->private_data, return); + intel8x0_t *chip = bus->private_data; chip->ac97_bus = NULL; } static void snd_intel8x0_mixer_free_ac97(ac97_t *ac97) { - intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return); + intel8x0_t *chip = ac97->private_data; chip->ac97 = NULL; } static int __devinit snd_intel8x0_mixer(intel8x0_t *chip, int ac97_clock) { - ac97_bus_t bus, *pbus; - ac97_t ac97, *x97; + ac97_bus_t *pbus; + ac97_template_t ac97; + ac97_t *x97; int err; unsigned int glob_sta = 0; + static ac97_bus_ops_t ops = { + .write = snd_intel8x0_codec_write, + .read = snd_intel8x0_codec_read, + }; chip->in_ac97_init = 1; - memset(&bus, 0, sizeof(bus)); - bus.private_data = chip; - bus.private_free = snd_intel8x0_mixer_free_ac97_bus; - if (ac97_clock >= 8000 && ac97_clock <= 48000) - bus.clock = ac97_clock; - else - bus.clock = 48000; memset(&ac97, 0, sizeof(ac97)); ac97.private_data = chip; @@ -905,13 +906,15 @@ static int __devinit snd_intel8x0_mixer( ac97.scaps = AC97_SCAP_SKIP_AUDIO; glob_sta = igetdword(chip, ICHREG(GLOB_STA)); - bus.write = snd_intel8x0_codec_write; - bus.read = snd_intel8x0_codec_read; - bus.vra = 1; - if ((err = snd_ac97_bus(chip->card, &bus, &pbus)) < 0) + if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &pbus)) < 0) goto __err; + pbus->private_free = snd_intel8x0_mixer_free_ac97_bus; + pbus->shared_type = AC97_SHARED_TYPE_ICH; /* shared with audio driver */ + if (ac97_clock >= 8000 && ac97_clock <= 48000) + pbus->clock = ac97_clock; chip->ac97_bus = pbus; + ac97.pci = chip->pci; ac97.num = glob_sta & ICH_SCR ? 1 : 0; if ((err = snd_ac97_mixer(pbus, &ac97, &x97)) < 0) { @@ -1016,6 +1019,11 @@ static int snd_intel8x0m_ich_chip_init(i } while (time_after_eq(end_time, jiffies)); } + if (chip->device_type == DEVICE_SIS) { + /* unmute the output on SIS7012 */ + iputword(chip, 0x4c, igetword(chip, 0x4c) | 1); + } + return 0; } @@ -1056,22 +1064,15 @@ static int snd_intel8x0_free(intel8x0_t synchronize_irq(chip->irq); __hw_end: if (chip->bdbars.area) - snd_dma_free_pages(&chip->dma_dev, &chip->bdbars); + snd_dma_free_pages(&chip->bdbars); if (chip->remap_addr) iounmap((void *) chip->remap_addr); if (chip->remap_bmaddr) iounmap((void *) chip->remap_bmaddr); - if (chip->res) { - release_resource(chip->res); - kfree_nocheck(chip->res); - } - if (chip->res_bm) { - release_resource(chip->res_bm); - kfree_nocheck(chip->res_bm); - } if (chip->irq >= 0) free_irq(chip->irq, (void *)chip); - snd_magic_kfree(chip); + pci_release_regions(chip->pci); + kfree(chip); return 0; } @@ -1081,7 +1082,7 @@ static int snd_intel8x0_free(intel8x0_t */ static int intel8x0m_suspend(snd_card_t *card, unsigned int state) { - intel8x0_t *chip = snd_magic_cast(intel8x0_t, card->pm_private_data, return -EINVAL); + intel8x0_t *chip = card->pm_private_data; int i; for (i = 0; i < chip->pcm_devs; i++) @@ -1094,7 +1095,7 @@ static int intel8x0m_suspend(snd_card_t static int intel8x0m_resume(snd_card_t *card, unsigned int state) { - intel8x0_t *chip = snd_magic_cast(intel8x0_t, card->pm_private_data, return -EINVAL); + intel8x0_t *chip = card->pm_private_data; pci_enable_device(chip->pci); pci_set_master(chip->pci); snd_intel8x0_chip_init(chip, 0); @@ -1109,7 +1110,7 @@ static int intel8x0m_resume(snd_card_t * static void snd_intel8x0m_proc_read(snd_info_entry_t * entry, snd_info_buffer_t * buffer) { - intel8x0_t *chip = snd_magic_cast(intel8x0_t, entry->private_data, return); + intel8x0_t *chip = entry->private_data; unsigned int tmp; snd_iprintf(buffer, "Intel8x0m\n\n"); @@ -1135,7 +1136,7 @@ static void __devinit snd_intel8x0m_proc static int snd_intel8x0_dev_free(snd_device_t *device) { - intel8x0_t *chip = snd_magic_cast(intel8x0_t, device->device_data, return -ENXIO); + intel8x0_t *chip = device->device_data; return snd_intel8x0_free(chip); } @@ -1168,7 +1169,7 @@ static int __devinit snd_intel8x0m_creat if ((err = pci_enable_device(pci)) < 0) return err; - chip = snd_magic_kcalloc(intel8x0_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; spin_lock_init(&chip->reg_lock); @@ -1177,29 +1178,23 @@ static int __devinit snd_intel8x0m_creat chip->card = card; chip->pci = pci; chip->irq = -1; - snd_intel8x0m_proc_init(chip); - sprintf(chip->ac97_name, "%s - AC'97", card->shortname); - sprintf(chip->ctrl_name, "%s - Controller", card->shortname); + + if ((err = pci_request_regions(pci, card->shortname)) < 0) { + kfree(chip); + return err; + } + if (device_type == DEVICE_ALI) { /* ALI5455 has no ac97 region */ chip->bmaddr = pci_resource_start(pci, 0); - if ((chip->res_bm = request_region(chip->bmaddr, 256, chip->ctrl_name)) == NULL) { - snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->bmaddr, chip->bmaddr + 256 - 1); - snd_intel8x0_free(chip); - return -EBUSY; - } goto port_inited; } if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) { /* ICH4 and Nforce */ chip->mmio = 1; chip->addr = pci_resource_start(pci, 2); - if ((chip->res = request_mem_region(chip->addr, 512, chip->ac97_name)) == NULL) { - snd_printk("unable to grab I/O memory 0x%lx-0x%lx\n", chip->addr, chip->addr + 512 - 1); - snd_intel8x0_free(chip); - return -EBUSY; - } - chip->remap_addr = (unsigned long) ioremap_nocache(chip->addr, 512); + chip->remap_addr = (unsigned long) ioremap_nocache(chip->addr, + pci_resource_len(pci, 2)); if (chip->remap_addr == 0) { snd_printk("AC'97 space ioremap problem\n"); snd_intel8x0_free(chip); @@ -1207,21 +1202,12 @@ static int __devinit snd_intel8x0m_creat } } else { chip->addr = pci_resource_start(pci, 0); - if ((chip->res = request_region(chip->addr, 256, chip->ac97_name)) == NULL) { - snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->addr, chip->addr + 256 - 1); - snd_intel8x0_free(chip); - return -EBUSY; - } } if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) { /* ICH4 */ chip->bm_mmio = 1; chip->bmaddr = pci_resource_start(pci, 3); - if ((chip->res_bm = request_mem_region(chip->bmaddr, 256, chip->ctrl_name)) == NULL) { - snd_printk("unable to grab I/O memory 0x%lx-0x%lx\n", chip->bmaddr, chip->bmaddr + 512 - 1); - snd_intel8x0_free(chip); - return -EBUSY; - } - chip->remap_bmaddr = (unsigned long) ioremap_nocache(chip->bmaddr, 256); + chip->remap_bmaddr = (unsigned long) ioremap_nocache(chip->bmaddr, + pci_resource_len(pci, 3)); if (chip->remap_bmaddr == 0) { snd_printk("Controller space ioremap problem\n"); snd_intel8x0_free(chip); @@ -1229,11 +1215,6 @@ static int __devinit snd_intel8x0m_creat } } else { chip->bmaddr = pci_resource_start(pci, 1); - if ((chip->res_bm = request_region(chip->bmaddr, 128, chip->ctrl_name)) == NULL) { - snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->bmaddr, chip->bmaddr + 128 - 1); - snd_intel8x0_free(chip); - return -EBUSY; - } } port_inited: @@ -1271,10 +1252,9 @@ static int __devinit snd_intel8x0m_creat /* allocate buffer descriptor lists */ /* the start of each lists must be aligned to 8 bytes */ - memset(&chip->dma_dev, 0, sizeof(chip->dma_dev)); - chip->dma_dev.type = SNDRV_DMA_TYPE_DEV; - chip->dma_dev.dev = snd_dma_pci_data(pci); - if (snd_dma_alloc_pages(&chip->dma_dev, chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, &chip->bdbars) < 0) { + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), + chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, + &chip->bdbars) < 0) { snd_intel8x0_free(chip); return -ENOMEM; } @@ -1320,11 +1300,12 @@ static struct shortname_table { { PCI_DEVICE_ID_INTEL_ICH4_6, "Intel 82801DB-ICH4" }, { PCI_DEVICE_ID_INTEL_ICH5_6, "Intel ICH5" }, { 0x7446, "AMD AMD768" }, -#if 0 { PCI_DEVICE_ID_SI_7013, "SiS SI7013" }, - { PCI_DEVICE_ID_NVIDIA_MCP_AUDIO, "NVidia nForce" }, - { PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO, "NVidia nForce2" }, - { PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO, "NVidia nForce3" }, + { PCI_DEVICE_ID_NVIDIA_MCP_MODEM, "NVidia nForce" }, + { PCI_DEVICE_ID_NVIDIA_MCP2_MODEM, "NVidia nForce2" }, + { PCI_DEVICE_ID_NVIDIA_MCP2S_MODEM, "NVidia nForce2s" }, + { PCI_DEVICE_ID_NVIDIA_MCP3_MODEM, "NVidia nForce3" }, +#if 0 { 0x5455, "ALi M5455" }, { 0x746d, "AMD AMD8111" }, #endif @@ -1353,10 +1334,10 @@ static int __devinit snd_intel8x0m_probe switch (pci_id->driver_data) { case DEVICE_NFORCE: - strcpy(card->driver, "NFORCE"); + strcpy(card->driver, "NFORCE-MODEM"); break; default: - strcpy(card->driver, "ICH"); + strcpy(card->driver, "ICH-MODEM"); break; } @@ -1383,6 +1364,8 @@ static int __devinit snd_intel8x0m_probe return err; } + snd_intel8x0m_proc_init(chip); + sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, chip->addr, chip->irq); --- linux-2.6.9-rc1/sound/pci/Kconfig 2004-08-24 00:02:26.000000000 -0700 +++ 25/sound/pci/Kconfig 2004-09-02 20:51:53.000000000 -0700 @@ -22,6 +22,14 @@ config SND_ATIIXP help Say 'Y' or 'M' to include support for ATI IXP 150/200/250/300 AC97 controller. +config SND_ATIIXP_MODEM + tristate "ATI IXP 150/200/250 Modem" + depends on SND + select SND_AC97_CODEC + help + Say 'Y' or 'M' to include support for ATI IXP 150/200/250 AC97 modem + controller. + config SND_AU8810 tristate "Aureal Advantage" depends on SND @@ -290,12 +298,12 @@ config SND_INTEL8X0 SiS 7012, AMD768/8111, NVidia NForce and ALi 5455 chips. config SND_INTEL8X0M - tristate "Intel i8x0/MX440; AMD768/8111 modems (EXPERIMENTAL)" + tristate "Intel i8x0/MX440; SiS 7013; NForce; AMD768/8111 modems (EXPERIMENTAL)" depends on SND && EXPERIMENTAL select SND_AC97_CODEC help - Say 'Y' or 'M' to include support for Intel8x0 and AMD768/8111 based - modems. + Say 'Y' or 'M' to include support for Intel8x0, SiS 7013, NVidia NForce + and AMD768/8111 based modems. config SND_SONICVIBES tristate "S3 SonicVibes" --- linux-2.6.9-rc1/sound/pci/korg1212/korg1212.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/sound/pci/korg1212/korg1212.c 2004-09-02 20:51:53.000000000 -0700 @@ -343,12 +343,6 @@ struct _snd_korg1212 { unsigned long inIRQ; unsigned long iobase; - struct resource *res_iomem; - struct resource *res_ioport; - struct resource *res_iomem2; - - struct snd_dma_device dma_dev; - struct snd_dma_buffer dma_dsp; struct snd_dma_buffer dma_play; struct snd_dma_buffer dma_rec; @@ -411,8 +405,7 @@ struct _snd_korg1212 { MODULE_DESCRIPTION("korg1212"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{KORG,korg1212}}"); +MODULE_SUPPORTED_DEVICE("{{KORG,korg1212}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ @@ -421,13 +414,10 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for Korg 1212 soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for Korg 1212 soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable Korg 1212 soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); MODULE_AUTHOR("Haroldo Gamal "); static struct pci_device_id snd_korg1212_ids[] = { @@ -637,7 +627,7 @@ static void snd_korg1212_SendStopAndWait /* timer callback for checking the ack of stop request */ static void snd_korg1212_timer_func(unsigned long data) { - korg1212_t *korg1212 = snd_magic_cast(korg1212_t, (void*)data, return); + korg1212_t *korg1212 = (korg1212_t *) data; spin_lock(&korg1212->lock); if (readl(&korg1212->sharedBufferPtr->cardCommand) == 0) { @@ -1143,7 +1133,7 @@ static void snd_korg1212_OnDSPDownloadCo static irqreturn_t snd_korg1212_interrupt(int irq, void *dev_id, struct pt_regs *regs) { u32 doorbellValue; - korg1212_t *korg1212 = snd_magic_cast(korg1212_t, dev_id, return IRQ_NONE); + korg1212_t *korg1212 = dev_id; if(irq != korg1212->irq) return IRQ_NONE; @@ -1407,7 +1397,7 @@ static void snd_korg1212_free_pcm(snd_pc static int snd_korg1212_playback_open(snd_pcm_substream_t *substream) { unsigned long flags; - korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + korg1212_t *korg1212 = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; #if K1212_DEBUG_LEVEL > 0 @@ -1419,8 +1409,7 @@ static int snd_korg1212_playback_open(sn snd_korg1212_OpenCard(korg1212); runtime->hw = snd_korg1212_playback_info; - runtime->dma_area = (char *) korg1212->playDataBufsPtr; - runtime->dma_bytes = K1212_BUF_SIZE; + snd_pcm_set_runtime_buffer(substream, &korg1212->dma_play); spin_lock_irqsave(&korg1212->lock, flags); @@ -1438,7 +1427,7 @@ static int snd_korg1212_playback_open(sn static int snd_korg1212_capture_open(snd_pcm_substream_t *substream) { unsigned long flags; - korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + korg1212_t *korg1212 = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; #if K1212_DEBUG_LEVEL > 0 @@ -1450,8 +1439,7 @@ static int snd_korg1212_capture_open(snd snd_korg1212_OpenCard(korg1212); runtime->hw = snd_korg1212_capture_info; - runtime->dma_area = (char *) korg1212->recordDataBufsPtr; - runtime->dma_bytes = K1212_BUF_SIZE; + snd_pcm_set_runtime_buffer(substream, &korg1212->dma_rec); spin_lock_irqsave(&korg1212->lock, flags); @@ -1468,7 +1456,7 @@ static int snd_korg1212_capture_open(snd static int snd_korg1212_playback_close(snd_pcm_substream_t *substream) { unsigned long flags; - korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + korg1212_t *korg1212 = snd_pcm_substream_chip(substream); #if K1212_DEBUG_LEVEL > 0 K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_playback_close [%s]\n", stateName[korg1212->cardState]); @@ -1490,7 +1478,7 @@ static int snd_korg1212_playback_close(s static int snd_korg1212_capture_close(snd_pcm_substream_t *substream) { unsigned long flags; - korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + korg1212_t *korg1212 = snd_pcm_substream_chip(substream); #if K1212_DEBUG_LEVEL > 0 K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_capture_close [%s]\n", stateName[korg1212->cardState]); @@ -1532,7 +1520,7 @@ static int snd_korg1212_hw_params(snd_pc snd_pcm_hw_params_t *params) { unsigned long flags; - korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + korg1212_t *korg1212 = snd_pcm_substream_chip(substream); int err; #if K1212_DEBUG_LEVEL > 0 @@ -1560,21 +1548,21 @@ static int snd_korg1212_hw_params(snd_pc static int snd_korg1212_prepare(snd_pcm_substream_t *substream) { - korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + korg1212_t *korg1212 = snd_pcm_substream_chip(substream); int rc; #if K1212_DEBUG_LEVEL > 0 K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_prepare [%s]\n", stateName[korg1212->cardState]); #endif - spin_lock(&korg1212->lock); + spin_lock_irq(&korg1212->lock); /* FIXME: we should wait for ack! */ if (korg1212->stop_pending_cnt > 0) { #if K1212_DEBUG_LEVEL > 0 K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_prepare - Stop is pending... [%s]\n", stateName[korg1212->cardState]); #endif - spin_unlock(&korg1212->lock); + spin_unlock_irq(&korg1212->lock); return -EAGAIN; /* writel(0, &korg1212->sharedBufferPtr->cardCommand); @@ -1587,7 +1575,7 @@ static int snd_korg1212_prepare(snd_pcm_ korg1212->currentBuffer = 0; - spin_unlock(&korg1212->lock); + spin_unlock_irq(&korg1212->lock); return rc ? -EINVAL : 0; } @@ -1595,7 +1583,7 @@ static int snd_korg1212_prepare(snd_pcm_ static int snd_korg1212_trigger(snd_pcm_substream_t *substream, int cmd) { - korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + korg1212_t *korg1212 = snd_pcm_substream_chip(substream); int rc; #if K1212_DEBUG_LEVEL > 0 @@ -1640,7 +1628,7 @@ static int snd_korg1212_trigger(snd_pcm_ static snd_pcm_uframes_t snd_korg1212_playback_pointer(snd_pcm_substream_t *substream) { - korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + korg1212_t *korg1212 = snd_pcm_substream_chip(substream); snd_pcm_uframes_t pos; pos = korg1212->currentBuffer * kPlayBufferFrames; @@ -1655,7 +1643,7 @@ static snd_pcm_uframes_t snd_korg1212_pl static snd_pcm_uframes_t snd_korg1212_capture_pointer(snd_pcm_substream_t *substream) { - korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + korg1212_t *korg1212 = snd_pcm_substream_chip(substream); snd_pcm_uframes_t pos; pos = korg1212->currentBuffer * kPlayBufferFrames; @@ -1674,7 +1662,7 @@ static int snd_korg1212_playback_copy(sn void __user *src, snd_pcm_uframes_t count) { - korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + korg1212_t *korg1212 = snd_pcm_substream_chip(substream); #if K1212_DEBUG_LEVEL > 2 K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_playback_copy [%s] %ld %ld\n", stateName[korg1212->cardState], pos, count); @@ -1689,7 +1677,7 @@ static int snd_korg1212_playback_silence snd_pcm_uframes_t pos, snd_pcm_uframes_t count) { - korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + korg1212_t *korg1212 = snd_pcm_substream_chip(substream); #if K1212_DEBUG_LEVEL > 0 K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_playback_silence [%s]\n", stateName[korg1212->cardState]); @@ -1704,7 +1692,7 @@ static int snd_korg1212_capture_copy(snd void __user *dst, snd_pcm_uframes_t count) { - korg1212_t *korg1212 = _snd_pcm_substream_chip(substream); + korg1212_t *korg1212 = snd_pcm_substream_chip(substream); #if K1212_DEBUG_LEVEL > 2 K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_capture_copy [%s] %ld %ld\n", stateName[korg1212->cardState], pos, count); @@ -1749,30 +1737,28 @@ static int snd_korg1212_control_phase_in static int snd_korg1212_control_phase_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) { - korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + korg1212_t *korg1212 = snd_kcontrol_chip(kcontrol); int i = kcontrol->private_value; - spin_lock_irqsave(&korg1212->lock, flags); + spin_lock_irq(&korg1212->lock); u->value.integer.value[0] = korg1212->volumePhase[i]; if (i >= 8) u->value.integer.value[1] = korg1212->volumePhase[i+1]; - spin_unlock_irqrestore(&korg1212->lock, flags); + spin_unlock_irq(&korg1212->lock); return 0; } static int snd_korg1212_control_phase_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) { - korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + korg1212_t *korg1212 = snd_kcontrol_chip(kcontrol); int change = 0; int i, val; - spin_lock_irqsave(&korg1212->lock, flags); + spin_lock_irq(&korg1212->lock); i = kcontrol->private_value; @@ -1798,7 +1784,7 @@ static int snd_korg1212_control_phase_pu } } - spin_unlock_irqrestore(&korg1212->lock, flags); + spin_unlock_irq(&korg1212->lock); return change; } @@ -1814,11 +1800,10 @@ static int snd_korg1212_control_volume_i static int snd_korg1212_control_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) { - korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + korg1212_t *korg1212 = snd_kcontrol_chip(kcontrol); int i; - spin_lock_irqsave(&korg1212->lock, flags); + spin_lock_irq(&korg1212->lock); i = kcontrol->private_value; u->value.integer.value[0] = abs(korg1212->sharedBufferPtr->volumeData[i]); @@ -1826,20 +1811,19 @@ static int snd_korg1212_control_volume_g if (i >= 8) u->value.integer.value[1] = abs(korg1212->sharedBufferPtr->volumeData[i+1]); - spin_unlock_irqrestore(&korg1212->lock, flags); + spin_unlock_irq(&korg1212->lock); return 0; } static int snd_korg1212_control_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) { - korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + korg1212_t *korg1212 = snd_kcontrol_chip(kcontrol); int change = 0; int i; int val; - spin_lock_irqsave(&korg1212->lock, flags); + spin_lock_irq(&korg1212->lock); i = kcontrol->private_value; @@ -1859,7 +1843,7 @@ static int snd_korg1212_control_volume_p } } - spin_unlock_irqrestore(&korg1212->lock, flags); + spin_unlock_irq(&korg1212->lock); return change; } @@ -1878,11 +1862,10 @@ static int snd_korg1212_control_route_in static int snd_korg1212_control_route_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) { - korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + korg1212_t *korg1212 = snd_kcontrol_chip(kcontrol); int i; - spin_lock_irqsave(&korg1212->lock, flags); + spin_lock_irq(&korg1212->lock); i = kcontrol->private_value; u->value.enumerated.item[0] = korg1212->sharedBufferPtr->routeData[i]; @@ -1890,18 +1873,17 @@ static int snd_korg1212_control_route_ge if (i >= 8) u->value.enumerated.item[1] = korg1212->sharedBufferPtr->routeData[i+1]; - spin_unlock_irqrestore(&korg1212->lock, flags); + spin_unlock_irq(&korg1212->lock); return 0; } static int snd_korg1212_control_route_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) { - korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + korg1212_t *korg1212 = snd_kcontrol_chip(kcontrol); int change = 0, i; - spin_lock_irqsave(&korg1212->lock, flags); + spin_lock_irq(&korg1212->lock); i = kcontrol->private_value; @@ -1917,7 +1899,7 @@ static int snd_korg1212_control_route_pu } } - spin_unlock_irqrestore(&korg1212->lock, flags); + spin_unlock_irq(&korg1212->lock); return change; } @@ -1933,26 +1915,24 @@ static int snd_korg1212_control_info(snd static int snd_korg1212_control_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) { - korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + korg1212_t *korg1212 = snd_kcontrol_chip(kcontrol); - spin_lock_irqsave(&korg1212->lock, flags); + spin_lock_irq(&korg1212->lock); u->value.integer.value[0] = korg1212->leftADCInSens; u->value.integer.value[1] = korg1212->rightADCInSens; - spin_unlock_irqrestore(&korg1212->lock, flags); + spin_unlock_irq(&korg1212->lock); return 0; } static int snd_korg1212_control_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) { - korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + korg1212_t *korg1212 = snd_kcontrol_chip(kcontrol); int change = 0; - spin_lock_irqsave(&korg1212->lock, flags); + spin_lock_irq(&korg1212->lock); if (u->value.integer.value[0] != korg1212->leftADCInSens) { korg1212->leftADCInSens = u->value.integer.value[0]; @@ -1963,7 +1943,7 @@ static int snd_korg1212_control_put(snd_ change = 1; } - spin_unlock_irqrestore(&korg1212->lock, flags); + spin_unlock_irq(&korg1212->lock); if (change) snd_korg1212_WriteADCSensitivity(korg1212); @@ -1985,29 +1965,27 @@ static int snd_korg1212_control_sync_inf static int snd_korg1212_control_sync_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + korg1212_t *korg1212 = snd_kcontrol_chip(kcontrol); - spin_lock_irqsave(&korg1212->lock, flags); + spin_lock_irq(&korg1212->lock); ucontrol->value.enumerated.item[0] = korg1212->clkSource; - spin_unlock_irqrestore(&korg1212->lock, flags); + spin_unlock_irq(&korg1212->lock); return 0; } static int snd_korg1212_control_sync_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - korg1212_t *korg1212 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + korg1212_t *korg1212 = snd_kcontrol_chip(kcontrol); unsigned int val; int change; val = ucontrol->value.enumerated.item[0] % 3; - spin_lock_irqsave(&korg1212->lock, flags); + spin_lock_irq(&korg1212->lock); change = val != korg1212->clkSource; snd_korg1212_SetClockSource(korg1212, val); - spin_unlock_irqrestore(&korg1212->lock, flags); + spin_unlock_irq(&korg1212->lock); return change; } @@ -2063,8 +2041,6 @@ static snd_kcontrol_new_t snd_korg1212_c } }; -#define K1212_CONTROL_ELEMENTS (sizeof(snd_korg1212_controls) / sizeof(snd_korg1212_controls[0])) - /* * proc interface */ @@ -2120,29 +2096,13 @@ snd_korg1212_free(korg1212_t *korg1212) korg1212->iobase = 0; } - if (korg1212->res_iomem != NULL) { - release_resource(korg1212->res_iomem); - kfree_nocheck(korg1212->res_iomem); - korg1212->res_iomem = NULL; - } - - if (korg1212->res_ioport != NULL) { - release_resource(korg1212->res_ioport); - kfree_nocheck(korg1212->res_ioport); - korg1212->res_ioport = NULL; - } - - if (korg1212->res_iomem2 != NULL) { - release_resource(korg1212->res_iomem2); - kfree_nocheck(korg1212->res_iomem2); - korg1212->res_iomem2 = NULL; - } + pci_release_regions(korg1212->pci); // ---------------------------------------------------- // free up memory resources used for the DSP download. // ---------------------------------------------------- if (korg1212->dma_dsp.area) { - snd_dma_free_pages(&korg1212->dma_dev, &korg1212->dma_dsp); + snd_dma_free_pages(&korg1212->dma_dsp); korg1212->dma_dsp.area = NULL; } @@ -2152,12 +2112,12 @@ snd_korg1212_free(korg1212_t *korg1212) // free up memory resources used for the Play/Rec Buffers // ------------------------------------------------------ if (korg1212->dma_play.area) { - snd_dma_free_pages(&korg1212->dma_dev, &korg1212->dma_play); + snd_dma_free_pages(&korg1212->dma_play); korg1212->dma_play.area = NULL; } if (korg1212->dma_rec.area) { - snd_dma_free_pages(&korg1212->dma_dev, &korg1212->dma_rec); + snd_dma_free_pages(&korg1212->dma_rec); korg1212->dma_rec.area = NULL; } @@ -2167,17 +2127,17 @@ snd_korg1212_free(korg1212_t *korg1212) // free up memory resources used for the Shared Buffers // ---------------------------------------------------- if (korg1212->dma_shared.area) { - snd_dma_free_pages(&korg1212->dma_dev, &korg1212->dma_shared); + snd_dma_free_pages(&korg1212->dma_shared); korg1212->dma_shared.area = NULL; } - snd_magic_kfree(korg1212); + kfree(korg1212); return 0; } static int snd_korg1212_dev_free(snd_device_t *device) { - korg1212_t *korg1212 = snd_magic_cast(korg1212_t, device->device_data, return -ENXIO); + korg1212_t *korg1212 = device->device_data; #if K1212_DEBUG_LEVEL > 0 K1212_DEBUG_PRINTK("K1212_DEBUG: Freeing device\n"); #endif @@ -2201,7 +2161,7 @@ static int __devinit snd_korg1212_create if ((err = pci_enable_device(pci)) < 0) return err; - korg1212 = snd_magic_kcalloc(korg1212_t, 0, GFP_KERNEL); + korg1212 = kcalloc(1, sizeof(*korg1212), GFP_KERNEL); if (korg1212 == NULL) return -ENOMEM; @@ -2232,6 +2192,11 @@ static int __devinit snd_korg1212_create for (i=0; ivolumePhase[i] = 0; + if ((err = pci_request_regions(pci, "korg1212")) < 0) { + kfree(korg1212); + return err; + } + korg1212->iomem = pci_resource_start(korg1212->pci, 0); korg1212->ioport = pci_resource_start(korg1212->pci, 1); korg1212->iomem2 = pci_resource_start(korg1212->pci, 2); @@ -2252,27 +2217,6 @@ static int __devinit snd_korg1212_create stateName[korg1212->cardState]); #endif - korg1212->res_iomem = request_mem_region(korg1212->iomem, iomem_size, "korg1212"); - if (korg1212->res_iomem == NULL) { - snd_printk(KERN_ERR "unable to grab region 0x%lx-0x%lx\n", - korg1212->iomem, korg1212->iomem + iomem_size - 1); - return -EBUSY; - } - - korg1212->res_ioport = request_region(korg1212->ioport, ioport_size, "korg1212"); - if (korg1212->res_ioport == NULL) { - snd_printk(KERN_ERR "unable to grab region 0x%lx-0x%lx\n", - korg1212->ioport, korg1212->ioport + ioport_size - 1); - return -EBUSY; - } - - korg1212->res_iomem2 = request_mem_region(korg1212->iomem2, iomem2_size, "korg1212"); - if (korg1212->res_iomem2 == NULL) { - snd_printk(KERN_ERR "unable to grab region 0x%lx-0x%lx\n", - korg1212->iomem2, korg1212->iomem2 + iomem2_size - 1); - return -EBUSY; - } - if ((korg1212->iobase = (unsigned long) ioremap(korg1212->iomem, iomem_size)) == 0) { snd_printk(KERN_ERR "unable to remap memory region 0x%lx-0x%lx\n", korg1212->iobase, korg1212->iobase + iomem_size - 1); @@ -2329,11 +2273,8 @@ static int __devinit snd_korg1212_create stateName[korg1212->cardState]); #endif - memset(&korg1212->dma_dev, 0, sizeof(korg1212->dma_dev)); - korg1212->dma_dev.type = SNDRV_DMA_TYPE_DEV; - korg1212->dma_dev.dev = snd_dma_pci_data(korg1212->pci); - - if (snd_dma_alloc_pages(&korg1212->dma_dev, sizeof(KorgSharedBuffer), &korg1212->dma_shared) < 0) { + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), + sizeof(KorgSharedBuffer), &korg1212->dma_shared) < 0) { snd_printk(KERN_ERR "can not allocate shared buffer memory (%Zd bytes)\n", sizeof(KorgSharedBuffer)); return -ENOMEM; } @@ -2348,7 +2289,8 @@ static int __devinit snd_korg1212_create korg1212->DataBufsSize = sizeof(KorgAudioBuffer) * kNumBuffers; - if (snd_dma_alloc_pages(&korg1212->dma_dev, korg1212->DataBufsSize, &korg1212->dma_play) < 0) { + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), + korg1212->DataBufsSize, &korg1212->dma_play) < 0) { snd_printk(KERN_ERR "can not allocate play data buffer memory (%d bytes)\n", korg1212->DataBufsSize); return -ENOMEM; } @@ -2360,7 +2302,8 @@ static int __devinit snd_korg1212_create korg1212->playDataBufsPtr, korg1212->PlayDataPhy, korg1212->DataBufsSize); #endif - if (snd_dma_alloc_pages(&korg1212->dma_dev, korg1212->DataBufsSize, &korg1212->dma_rec) < 0) { + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), + korg1212->DataBufsSize, &korg1212->dma_rec) < 0) { snd_printk(KERN_ERR "can not allocate record data buffer memory (%d bytes)\n", korg1212->DataBufsSize); return -ENOMEM; } @@ -2390,7 +2333,8 @@ static int __devinit snd_korg1212_create korg1212->AdatTimeCodePhy = korg1212->sharedBufferPhy + offsetof(KorgSharedBuffer, AdatTimeCode); - if (snd_dma_alloc_pages(&korg1212->dma_dev, korg1212->dspCodeSize, &korg1212->dma_dsp) < 0) { + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), + korg1212->dspCodeSize, &korg1212->dma_dsp) < 0) { snd_printk(KERN_ERR "can not allocate dsp code memory (%d bytes)\n", korg1212->dspCodeSize); return -ENOMEM; } @@ -2420,7 +2364,7 @@ static int __devinit snd_korg1212_create "VolumeTablePhy = %08x L[%08x]\n" "RoutingTablePhy = %08x L[%08x]\n" "AdatTimeCodePhy = %08x L[%08x]\n", - korg1212->dma_dsp.addr, UpperWordSwap(korg1212->dma_dsp.addr), + (int)korg1212->dma_dsp.addr, UpperWordSwap(korg1212->dma_dsp.addr), korg1212->PlayDataPhy, LowerWordSwap(korg1212->PlayDataPhy), korg1212->RecDataPhy, LowerWordSwap(korg1212->RecDataPhy), korg1212->VolumeTablePhy, LowerWordSwap(korg1212->VolumeTablePhy), @@ -2443,7 +2387,7 @@ static int __devinit snd_korg1212_create //snd_pcm_lib_preallocate_pages_for_all(korg1212->pcm, // K1212_MAX_BUF_SIZE, K1212_MAX_BUF_SIZE, GFP_KERNEL); - for (i = 0; i < K1212_CONTROL_ELEMENTS; i++) { + for (i = 0; i < ARRAY_SIZE(snd_korg1212_controls); i++) { err = snd_ctl_add(korg1212->card, snd_ctl_new1(&snd_korg1212_controls[i], korg1212)); if (err < 0) return err; --- linux-2.6.9-rc1/sound/pci/maestro3.c 2004-08-24 00:02:58.000000000 -0700 +++ 25/sound/pci/maestro3.c 2004-09-02 20:51:53.000000000 -0700 @@ -51,8 +51,7 @@ MODULE_AUTHOR("Zach Brown , Takashi Iwai "); MODULE_DESCRIPTION("ESS Maestro3 PCI"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{ESS,Maestro3 PCI}," +MODULE_SUPPORTED_DEVICE("{{ESS,Maestro3 PCI}," "{ESS,ES1988}," "{ESS,Allegro PCI}," "{ESS,Allegro-1 PCI}," @@ -67,19 +66,14 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable this soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(external_amp, bool, boot_devs, 0444); MODULE_PARM_DESC(external_amp, "Enable external amp for " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(external_amp, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC); module_param_array(amp_gpio, int, boot_devs, 0444); MODULE_PARM_DESC(amp_gpio, "GPIO pin number for external amp. (default = -1)"); -MODULE_PARM_SYNTAX(amp_gpio, SNDRV_ENABLED); #define MAX_PLAYBACKS 2 #define MAX_CAPTURES 1 @@ -776,8 +770,6 @@ MODULE_PARM_SYNTAX(amp_gpio, SNDRV_ENABL typedef struct snd_m3_dma m3_dma_t; typedef struct snd_m3 m3_t; -#define chip_t m3_t - /* quirk lists */ struct m3_quirk { @@ -827,7 +819,6 @@ struct snd_m3 { snd_card_t *card; unsigned long iobase; - struct resource *iobase_res; int irq; int allegro_flag : 1; @@ -1165,12 +1156,11 @@ snd_m3_pcm_trigger(snd_pcm_substream_t * { m3_t *chip = snd_pcm_substream_chip(subs); m3_dma_t *s = (m3_dma_t*)subs->runtime->private_data; - unsigned long flags; int err = -EINVAL; snd_assert(s != NULL, return -ENXIO); - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock(&chip->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: @@ -1191,7 +1181,7 @@ snd_m3_pcm_trigger(snd_pcm_substream_t * } break; } - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock(&chip->reg_lock); return err; } @@ -1477,7 +1467,6 @@ snd_m3_pcm_prepare(snd_pcm_substream_t * m3_t *chip = snd_pcm_substream_chip(subs); snd_pcm_runtime_t *runtime = subs->runtime; m3_dma_t *s = (m3_dma_t*)runtime->private_data; - unsigned long flags; snd_assert(s != NULL, return -ENXIO); @@ -1488,7 +1477,7 @@ snd_m3_pcm_prepare(snd_pcm_substream_t * runtime->rate < 8000) return -EINVAL; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); snd_m3_pcm_setup1(chip, s, subs); @@ -1499,7 +1488,7 @@ snd_m3_pcm_prepare(snd_pcm_substream_t * snd_m3_pcm_setup2(chip, s, runtime); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return 0; } @@ -1573,7 +1562,7 @@ static void snd_m3_update_ptr(m3_t *chip static irqreturn_t snd_m3_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - m3_t *chip = snd_magic_cast(m3_t, dev_id, ); + m3_t *chip = dev_id; u8 status; int i; @@ -1670,20 +1659,19 @@ snd_m3_substream_open(m3_t *chip, snd_pc { int i; m3_dma_t *s; - unsigned long flags; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); for (i = 0; i < chip->num_substreams; i++) { s = &chip->substreams[i]; if (! s->opened) goto __found; } - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return -ENOMEM; __found: s->opened = 1; s->running = 0; - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); subs->runtime->private_data = s; s->substream = subs; @@ -1703,12 +1691,11 @@ static void snd_m3_substream_close(m3_t *chip, snd_pcm_substream_t *subs) { m3_dma_t *s = (m3_dma_t*) subs->runtime->private_data; - unsigned long flags; if (s == NULL) return; /* not opened properly */ - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); if (s->substream && s->running) snd_m3_pcm_stop(chip, s, s->substream); /* does this happen? */ if (s->in_lists) { @@ -1719,7 +1706,7 @@ snd_m3_substream_close(m3_t *chip, snd_p } s->running = 0; s->opened = 0; - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); } static int @@ -1848,7 +1835,7 @@ static int snd_m3_ac97_wait(m3_t *chip) static unsigned short snd_m3_ac97_read(ac97_t *ac97, unsigned short reg) { - m3_t *chip = snd_magic_cast(m3_t, ac97->private_data, return -ENXIO); + m3_t *chip = ac97->private_data; unsigned short ret = 0; unsigned long flags; @@ -1867,7 +1854,7 @@ __error: static void snd_m3_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val) { - m3_t *chip = snd_magic_cast(m3_t, ac97->private_data, return); + m3_t *chip = ac97->private_data; unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); @@ -1983,14 +1970,15 @@ static void snd_m3_ac97_reset(m3_t *chip static int __devinit snd_m3_mixer(m3_t *chip) { - ac97_bus_t bus, *pbus; - ac97_t ac97; + ac97_bus_t *pbus; + ac97_template_t ac97; int err; + static ac97_bus_ops_t ops = { + .write = snd_m3_ac97_write, + .read = snd_m3_ac97_read, + }; - memset(&bus, 0, sizeof(bus)); - bus.write = snd_m3_ac97_write; - bus.read = snd_m3_ac97_read; - if ((err = snd_ac97_bus(chip->card, &bus, &pbus)) < 0) + if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0) return err; memset(&ac97, 0, sizeof(ac97)); @@ -2167,7 +2155,7 @@ static void __devinit snd_m3_assp_init(m KDATA_DMA_XFER0); /* write kernel into code memory.. */ - for (i = 0 ; i < sizeof(assp_kernel_image) / 2; i++) { + for (i = 0 ; i < ARRAY_SIZE(assp_kernel_image); i++) { snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, REV_B_CODE_MEMORY_BEGIN + i, assp_kernel_image[i]); @@ -2179,7 +2167,7 @@ static void __devinit snd_m3_assp_init(m * drop it there. It seems that the minisrc doesn't * need vectors, so we won't bother with them.. */ - for (i = 0; i < sizeof(assp_minisrc_image) / 2; i++) { + for (i = 0; i < ARRAY_SIZE(assp_minisrc_image); i++) { snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE, 0x400 + i, assp_minisrc_image[i]); @@ -2368,22 +2356,21 @@ snd_m3_enable_ints(m3_t *chip) static int snd_m3_free(m3_t *chip) { - unsigned long flags; m3_dma_t *s; int i; if (chip->substreams) { - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); for (i = 0; i < chip->num_substreams; i++) { s = &chip->substreams[i]; /* check surviving pcms; this should not happen though.. */ if (s->substream && s->running) snd_m3_pcm_stop(chip, s, s->substream); } - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); kfree(chip->substreams); } - if (chip->iobase_res) { + if (chip->iobase) { snd_m3_outw(chip, HOST_INT_CTRL, 0); /* disable ints */ } @@ -2392,17 +2379,15 @@ static int snd_m3_free(m3_t *chip) vfree(chip->suspend_mem); #endif - if (chip->irq >= 0) + if (chip->irq >= 0) { synchronize_irq(chip->irq); - - if (chip->iobase_res) { - release_resource(chip->iobase_res); - kfree_nocheck(chip->iobase_res); - } - if (chip->irq >= 0) free_irq(chip->irq, (void *)chip); + } + + if (chip->iobase) + pci_release_regions(chip->pci); - snd_magic_kfree(chip); + kfree(chip); return 0; } @@ -2413,7 +2398,7 @@ static int snd_m3_free(m3_t *chip) #ifdef CONFIG_PM static int m3_suspend(snd_card_t *card, unsigned int state) { - m3_t *chip = snd_magic_cast(m3_t, card->pm_private_data, return -EINVAL); + m3_t *chip = card->pm_private_data; int i, index; if (chip->suspend_mem == NULL) @@ -2444,12 +2429,14 @@ static int m3_suspend(snd_card_t *card, static int m3_resume(snd_card_t *card, unsigned int state) { - m3_t *chip = snd_magic_cast(m3_t, card->pm_private_data, return -EINVAL); + m3_t *chip = card->pm_private_data; int i, index; if (chip->suspend_mem == NULL) return 0; + pci_set_master(chip->pci); + /* first lets just bring everything back. .*/ snd_m3_outw(chip, 0, 0x54); snd_m3_outw(chip, 0, 0x56); @@ -2489,7 +2476,7 @@ static int m3_resume(snd_card_t *card, u static int snd_m3_dev_free(snd_device_t *device) { - m3_t *chip = snd_magic_cast(m3_t, device->device_data, return -ENXIO); + m3_t *chip = device->device_data; return snd_m3_free(chip); } @@ -2519,7 +2506,7 @@ snd_m3_create(snd_card_t *card, struct p return -ENXIO; } - chip = snd_magic_kcalloc(m3_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; @@ -2562,18 +2549,16 @@ snd_m3_create(snd_card_t *card, struct p chip->num_substreams = NR_DSPS; chip->substreams = kmalloc(sizeof(m3_dma_t) * chip->num_substreams, GFP_KERNEL); if (chip->substreams == NULL) { - snd_magic_kfree(chip); + kfree(chip); return -ENOMEM; } memset(chip->substreams, 0, sizeof(m3_dma_t) * chip->num_substreams); - chip->iobase = pci_resource_start(pci, 0); - if ((chip->iobase_res = request_region(chip->iobase, 256, - card->driver)) == NULL) { - snd_printk("unable to grab i/o ports %ld\n", chip->iobase); + if ((err = pci_request_regions(pci, card->driver)) < 0) { snd_m3_free(chip); - return -EBUSY; + return err; } + chip->iobase = pci_resource_start(pci, 0); /* just to be sure */ pci_set_master(pci); --- linux-2.6.9-rc1/sound/pci/Makefile 2004-08-24 00:01:51.000000000 -0700 +++ 25/sound/pci/Makefile 2004-09-02 20:51:53.000000000 -0700 @@ -5,6 +5,7 @@ snd-als4000-objs := als4000.o snd-atiixp-objs := atiixp.o +snd-atiixp-modem-objs := atiixp_modem.o snd-azt3328-objs := azt3328.o snd-bt87x-objs := bt87x.o snd-cmipci-objs := cmipci.o @@ -25,6 +26,7 @@ snd-via82xx-objs := via82xx.o # Toplevel Module Dependency obj-$(CONFIG_SND_ALS4000) += snd-als4000.o obj-$(CONFIG_SND_ATIIXP) += snd-atiixp.o +obj-$(CONFIG_SND_ATIIXP_MODEM) += snd-atiixp-modem.o obj-$(CONFIG_SND_AZT3328) += snd-azt3328.o obj-$(CONFIG_SND_BT87X) += snd-bt87x.o obj-$(CONFIG_SND_CMIPCI) += snd-cmipci.o --- linux-2.6.9-rc1/sound/pci/mixart/mixart.c 2004-08-24 00:03:17.000000000 -0700 +++ 25/sound/pci/mixart/mixart.c 2004-09-02 20:51:53.000000000 -0700 @@ -42,25 +42,19 @@ MODULE_AUTHOR("Digigram "); MODULE_DESCRIPTION("Digigram " CARD_NAME); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Digigram," CARD_NAME "}}"); +MODULE_SUPPORTED_DEVICE("{{Digigram," CARD_NAME "}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ static int boot_devs; -#define chip_t mixart_t - module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for Digigram " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for Digigram " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable Digigram " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); /* */ @@ -247,21 +241,27 @@ mixart_pipe_t* snd_mixart_add_ref_pipe( /* pipe is not yet defined */ if( pipe->status == PIPE_UNDEFINED ) { int err, i; - mixart_streaming_group_t streaming_group_resp; - mixart_streaming_group_req_t streaming_group_req; + struct { + mixart_streaming_group_req_t sgroup_req; + mixart_streaming_group_t sgroup_resp; + } *buf; snd_printdd("add_ref_pipe audio chip(%d) pcm(%d)\n", chip->chip_idx, pcm_number); + buf = kmalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + return NULL; + request.uid = (mixart_uid_t){0,0}; /* should be StreamManagerUID, but zero is OK if there is only one ! */ - request.data = &streaming_group_req; - request.size = sizeof(streaming_group_req); + request.data = &buf->sgroup_req; + request.size = sizeof(buf->sgroup_req); - memset(&streaming_group_req, 0, sizeof(streaming_group_req)); + memset(&buf->sgroup_req, 0, sizeof(buf->sgroup_req)); - streaming_group_req.stream_count = stream_count; - streaming_group_req.channel_count = 2; - streaming_group_req.latency = 256; - streaming_group_req.connector = pipe->uid_left_connector; /* the left connector */ + buf->sgroup_req.stream_count = stream_count; + buf->sgroup_req.channel_count = 2; + buf->sgroup_req.latency = 256; + buf->sgroup_req.connector = pipe->uid_left_connector; /* the left connector */ for (i=0; isgroup_req.stream_info[i].size_max_byte_frame = 1024; + buf->sgroup_req.stream_info[i].size_max_sample_frame = 256; + buf->sgroup_req.stream_info[i].nb_bytes_max_per_sample = MIXART_FLOAT_P__4_0_TO_HEX; /* is 4.0f */ /* find the right bufferinfo_array */ j = (chip->chip_idx * MIXART_MAX_STREAM_PER_CARD) + (pcm_number * (MIXART_PLAYBACK_STREAMS + MIXART_CAPTURE_STREAMS)) + i; if(capture) j += MIXART_PLAYBACK_STREAMS; /* in the array capture is behind playback */ - streaming_group_req.flow_entry[i] = j; + buf->sgroup_req.flow_entry[i] = j; flowinfo = (struct mixart_flowinfo *)chip->mgr->flowinfo.area; flowinfo[j].bufferinfo_array_phy_address = (u32)chip->mgr->bufferinfo.addr + (j * sizeof(mixart_bufferinfo_t)); @@ -294,17 +294,19 @@ mixart_pipe_t* snd_mixart_add_ref_pipe( } } - err = snd_mixart_send_msg(chip->mgr, &request, sizeof(streaming_group_resp), &streaming_group_resp); - if((err < 0) || (streaming_group_resp.status != 0)) { - snd_printk(KERN_ERR "error MSG_STREAM_ADD_**PUT_GROUP err=%x stat=%x !\n", err, streaming_group_resp.status); + err = snd_mixart_send_msg(chip->mgr, &request, sizeof(buf->sgroup_resp), &buf->sgroup_resp); + if((err < 0) || (buf->sgroup_resp.status != 0)) { + snd_printk(KERN_ERR "error MSG_STREAM_ADD_**PUT_GROUP err=%x stat=%x !\n", err, buf->sgroup_resp.status); + kfree(buf); return NULL; } - pipe->group_uid = streaming_group_resp.group; /* id of the pipe, as returned by embedded */ - pipe->stream_count = streaming_group_resp.stream_count; - /* pipe->stream_uid[i] = streaming_group_resp.stream[i].stream_uid; */ + pipe->group_uid = buf->sgroup_resp.group; /* id of the pipe, as returned by embedded */ + pipe->stream_count = buf->sgroup_resp.stream_count; + /* pipe->stream_uid[i] = buf->sgroup_resp.stream[i].stream_uid; */ pipe->status = PIPE_STOPPED; + kfree(buf); } if(monitoring) pipe->monitoring = 1; @@ -458,8 +460,6 @@ static int mixart_sync_nonblock_events(m /* * prepare callback for all pcms - * - * NOTE: this callback is non-atomic (pcm->info_flags |= SNDRV_PCM_INFO_NONATOMIC_OPS) */ static int snd_mixart_prepare(snd_pcm_substream_t *subs) { @@ -620,7 +620,10 @@ static int snd_mixart_hw_params(snd_pcm_ bufferinfo[i].available_length = subs->runtime->dma_bytes; /* bufferinfo[i].buffer_id is already defined */ - snd_printdd("snd_mixart_hw_params(pcm %d) : dma_addr(%x) dma_bytes(%x) subs-number(%d)\n", i, subs->runtime->dma_addr, subs->runtime->dma_bytes, subs->number); + snd_printdd("snd_mixart_hw_params(pcm %d) : dma_addr(%x) dma_bytes(%x) subs-number(%d)\n", i, + bufferinfo[i].buffer_address, + bufferinfo[i].available_length, + subs->number); } up(&mgr->setup_mutex); @@ -901,6 +904,7 @@ static snd_pcm_ops_t snd_mixart_capture_ static void preallocate_buffers(mixart_t *chip, snd_pcm_t *pcm) { +#if 0 snd_pcm_substream_t *subs; int stream; @@ -912,6 +916,7 @@ static void preallocate_buffers(mixart_t subs->stream << 8 | (subs->number + 1) | (chip->chip_idx + 1) << 24; } +#endif snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->mgr->pci), 32*1024, 32*1024); } @@ -937,7 +942,7 @@ static int snd_mixart_pcm_analog(mixart_ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops); - pcm->info_flags = SNDRV_PCM_INFO_NONATOMIC_OPS; + pcm->info_flags = 0; strcpy(pcm->name, name); preallocate_buffers(chip, pcm); @@ -968,7 +973,7 @@ static int snd_mixart_pcm_digital(mixart snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops); - pcm->info_flags = SNDRV_PCM_INFO_NONATOMIC_OPS; + pcm->info_flags = 0; strcpy(pcm->name, name); preallocate_buffers(chip, pcm); @@ -979,13 +984,13 @@ static int snd_mixart_pcm_digital(mixart static int snd_mixart_chip_free(mixart_t *chip) { - snd_magic_kfree(chip); + kfree(chip); return 0; } static int snd_mixart_chip_dev_free(snd_device_t *device) { - mixart_t *chip = snd_magic_cast(mixart_t, device->device_data, return -ENXIO); + mixart_t *chip = device->device_data; return snd_mixart_chip_free(chip); } @@ -1000,7 +1005,7 @@ static int __devinit snd_mixart_create(m .dev_free = snd_mixart_chip_dev_free, }; - mgr->chip[idx] = chip = snd_magic_kcalloc(mixart_t, 0, GFP_KERNEL); + mgr->chip[idx] = chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (! chip) { snd_printk(KERN_ERR "cannot allocate chip\n"); return -ENOMEM; @@ -1074,24 +1079,21 @@ static int snd_mixart_free(mixart_mgr_t for (i = 0; i < 2; i++) { if (mgr->mem[i].virt) iounmap((void *)mgr->mem[i].virt); - if (mgr->mem[i].res) { - release_resource(mgr->mem[i].res); - kfree_nocheck(mgr->mem[i].res); - } } + pci_release_regions(mgr->pci); /* free flowarray */ if(mgr->flowinfo.area) { - snd_dma_free_pages(&mgr->dma_dev, &mgr->flowinfo); + snd_dma_free_pages(&mgr->flowinfo); mgr->flowinfo.area = NULL; } /* free bufferarray */ if(mgr->bufferinfo.area) { - snd_dma_free_pages(&mgr->dma_dev, &mgr->bufferinfo); + snd_dma_free_pages(&mgr->bufferinfo); mgr->bufferinfo.area = NULL; } - snd_magic_kfree(mgr); + kfree(mgr); return 0; } @@ -1157,7 +1159,7 @@ static long snd_mixart_BA0_read(snd_info struct file *file, char __user *buf, unsigned long count, unsigned long pos) { - mixart_mgr_t *mgr = snd_magic_cast(mixart_mgr_t, entry->private_data, return -ENXIO); + mixart_mgr_t *mgr = entry->private_data; count = count & ~3; /* make sure the read size is a multiple of 4 bytes */ if(count <= 0) @@ -1176,7 +1178,7 @@ static long snd_mixart_BA1_read(snd_info struct file *file, char __user *buf, unsigned long count, unsigned long pos) { - mixart_mgr_t *mgr = snd_magic_cast(mixart_mgr_t, entry->private_data, return -ENXIO); + mixart_mgr_t *mgr = entry->private_data; count = count & ~3; /* make sure the read size is a multiple of 4 bytes */ if(count <= 0) @@ -1202,7 +1204,7 @@ static struct snd_info_entry_ops snd_mix static void snd_mixart_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) { - mixart_t *chip = snd_magic_cast(mixart_t, entry->private_data, return); + mixart_t *chip = entry->private_data; u32 ref; snd_iprintf(buffer, "Digigram miXart (alsa card %d)\n\n", chip->chip_idx); @@ -1289,15 +1291,14 @@ static int __devinit snd_mixart_probe(st pci_set_master(pci); /* check if we can restrict PCI DMA transfers to 32 bits */ - if (!pci_dma_supported(pci, 0xffffffff)) { + if (pci_set_dma_mask(pci, 0xffffffff) < 0) { snd_printk(KERN_ERR "architecture does not support 32bit PCI busmaster DMA\n"); return -ENXIO; } - pci_set_dma_mask(pci, 0xffffffff); /* */ - mgr = snd_magic_kcalloc(mixart_mgr_t, 0, GFP_KERNEL); + mgr = kcalloc(1, sizeof(*mgr), GFP_KERNEL); if (! mgr) return -ENOMEM; @@ -1305,19 +1306,14 @@ static int __devinit snd_mixart_probe(st mgr->irq = -1; /* resource assignment */ + if ((err = pci_request_regions(pci, CARD_NAME)) < 0) { + kfree(mgr); + return err; + } for (i = 0; i < 2; i++) { - static int memory_sizes[2] = { - MIXART_BA0_SIZE, /* 16M */ - MIXART_BA1_SIZE /* 4 k */ - }; mgr->mem[i].phys = pci_resource_start(pci, i); - mgr->mem[i].res = request_mem_region(mgr->mem[i].phys, memory_sizes[i], CARD_NAME); - if (! mgr->mem[i].res) { - snd_printk(KERN_ERR "unable to grab memory 0x%lx\n", mgr->mem[i].phys); - snd_mixart_free(mgr); - return -EBUSY; - } - mgr->mem[i].virt = (unsigned long)ioremap_nocache(mgr->mem[i].phys, memory_sizes[i]); + mgr->mem[i].virt = (unsigned long)ioremap_nocache(mgr->mem[i].phys, + pci_resource_len(pci, i)); } if (request_irq(pci->irq, snd_mixart_interrupt, SA_INTERRUPT|SA_SHIRQ, CARD_NAME, (void *)mgr)) { @@ -1331,13 +1327,13 @@ static int __devinit snd_mixart_probe(st sprintf(mgr->longname, "%s at 0x%lx & 0x%lx, irq %i", mgr->shortname, mgr->mem[0].phys, mgr->mem[1].phys, mgr->irq); /* ISR spinlock */ - mgr->lock = SPIN_LOCK_UNLOCKED; + spin_lock_init(&mgr->lock); /* init mailbox */ mgr->msg_fifo_readptr = 0; mgr->msg_fifo_writeptr = 0; - mgr->msg_lock = SPIN_LOCK_UNLOCKED; + spin_lock_init(&mgr->msg_lock); init_MUTEX(&mgr->msg_mutex); init_waitqueue_head(&mgr->msg_sleep); atomic_set(&mgr->msg_processed, 0); @@ -1391,13 +1387,10 @@ static int __devinit snd_mixart_probe(st /* init firmware status (mgr->hwdep->dsp_loaded reset in hwdep_new) */ mgr->board_type = MIXART_DAUGHTER_TYPE_NONE; - memset(&mgr->dma_dev, 0, sizeof(mgr->dma_dev)); - mgr->dma_dev.type = SNDRV_DMA_TYPE_DEV; - mgr->dma_dev.dev = snd_dma_pci_data(mgr->pci); - /* create array of streaminfo */ size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * sizeof(mixart_flowinfo_t)) ); - if (snd_dma_alloc_pages(&mgr->dma_dev, size, &mgr->flowinfo) < 0) { + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), + size, &mgr->flowinfo) < 0) { snd_mixart_free(mgr); return -ENOMEM; } @@ -1406,7 +1399,8 @@ static int __devinit snd_mixart_probe(st /* create array of bufferinfo */ size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * sizeof(mixart_bufferinfo_t)) ); - if (snd_dma_alloc_pages(&mgr->dma_dev, size, &mgr->bufferinfo) < 0) { + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), + size, &mgr->bufferinfo) < 0) { snd_mixart_free(mgr); return -ENOMEM; } --- linux-2.6.9-rc1/sound/pci/mixart/mixart_core.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/sound/pci/mixart/mixart_core.c 2004-09-02 20:51:53.000000000 -0700 @@ -379,7 +379,7 @@ void snd_mixart_msg_tasklet( unsigned lo snd_printk(KERN_ERR "tasklet : error MSG_STREAM_ST***_***PUT_STAGE_PACKET status=%x\n", mixart_msg_data[0]); break; default: - snd_printdd("tasklet received mf(%x) : msg_id(%x) uid(%x, %x) size(%d)\n", + snd_printdd("tasklet received mf(%x) : msg_id(%x) uid(%x, %x) size(%zd)\n", msg, resp.message_id, resp.uid.object_id, resp.uid.desc, resp.size); break; } @@ -403,7 +403,7 @@ void snd_mixart_msg_tasklet( unsigned lo irqreturn_t snd_mixart_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - mixart_mgr_t *mgr = snd_magic_cast(mixart_mgr_t, dev_id, return IRQ_NONE); + mixart_mgr_t *mgr = dev_id; int err; mixart_msg_t resp; --- linux-2.6.9-rc1/sound/pci/mixart/mixart.h 2004-08-24 00:01:53.000000000 -0700 +++ 25/sound/pci/mixart/mixart.h 2004-09-02 20:51:53.000000000 -0700 @@ -115,7 +115,6 @@ struct snd_mixart_mgr { snd_hwdep_t *hwdep; unsigned int board_type; /* read from embedded once elf file is loaded, 250 = miXart8, 251 = with AES, 252 = with Cobranet */ - struct snd_dma_device dma_dev; struct snd_dma_buffer flowinfo; struct snd_dma_buffer bufferinfo; --- linux-2.6.9-rc1/sound/pci/mixart/mixart_hwdep.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/sound/pci/mixart/mixart_hwdep.c 2004-09-02 20:51:53.000000000 -0700 @@ -146,7 +146,7 @@ static int mixart_load_elf(mixart_mgr_t static int mixart_hwdep_dsp_status(snd_hwdep_t *hw, snd_hwdep_dsp_status_t *info) { - mixart_mgr_t *mgr = snd_magic_cast(mixart_mgr_t, hw->private_data, return -ENXIO); + mixart_mgr_t *mgr = hw->private_data; strcpy(info->id, "miXart"); info->num_dsps = MIXART_HARDW_FILES_MAX_INDEX; @@ -346,7 +346,7 @@ static int mixart_first_init(mixart_mgr_ static int mixart_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp) { - mixart_mgr_t* mgr = snd_magic_cast(mixart_mgr_t, hw->private_data, return -ENXIO); + mixart_mgr_t* mgr = hw->private_data; int err, card_index; u32 status_xilinx, status_elf, status_daught; u32 val; --- linux-2.6.9-rc1/sound/pci/mixart/mixart_mixer.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/sound/pci/mixart/mixart_mixer.c 2004-09-02 20:51:53.000000000 -0700 @@ -31,8 +31,6 @@ #include #include "mixart_mixer.h" -#define chip_t mixart_t - static u32 mixart_analog_level[256] = { 0xc2c00000, /* [000] -96.0 dB */ 0xc2bf0000, /* [001] -95.5 dB */ --- linux-2.6.9-rc1/sound/pci/nm256/nm256.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/sound/pci/nm256/nm256.c 2004-09-02 23:57:36.734737920 -0700 @@ -45,8 +45,7 @@ MODULE_AUTHOR("Takashi Iwai "); MODULE_DESCRIPTION("NeoMagic NM256AV/ZX"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{NeoMagic,NM256AV}," +MODULE_SUPPORTED_DEVICE("{{NeoMagic,NM256AV}," "{NeoMagic,NM256ZX}}"); /* @@ -66,31 +65,22 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable this soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(playback_bufsize, int, boot_devs, 0444); MODULE_PARM_DESC(playback_bufsize, "DAC frame size in kB for " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(playback_bufsize, SNDRV_ENABLED); module_param_array(capture_bufsize, int, boot_devs, 0444); MODULE_PARM_DESC(capture_bufsize, "ADC frame size in kB for " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(capture_bufsize, SNDRV_ENABLED); module_param_array(force_ac97, bool, boot_devs, 0444); MODULE_PARM_DESC(force_ac97, "Force to use AC97 codec for " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(force_ac97, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); module_param_array(buffer_top, int, boot_devs, 0444); MODULE_PARM_DESC(buffer_top, "Set the top address of audio buffer for " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(buffer_top, SNDRV_ENABLED); module_param_array(use_cache, bool, boot_devs, 0444); MODULE_PARM_DESC(use_cache, "Enable the cache for coefficient table access."); -MODULE_PARM_SYNTAX(use_cache, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); module_param_array(vaio_hack, bool, boot_devs, 0444); MODULE_PARM_DESC(vaio_hack, "Enable workaround for Sony VAIO notebooks."); -MODULE_PARM_SYNTAX(vaio_hack, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); /* * hw definitions @@ -191,7 +181,6 @@ MODULE_PARM_SYNTAX(vaio_hack, SNDRV_ENAB typedef struct snd_nm256 nm256_t; typedef struct snd_nm256_stream nm256_stream_t; -#define chip_t nm256_t struct snd_nm256_stream { @@ -414,9 +403,8 @@ snd_nm256_load_coefficient(nm256_t *chip static unsigned int samplerates[8] = { 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, }; -#define NUM_SAMPLERATES (sizeof(samplerates) / sizeof(samplerates[0])) static snd_pcm_hw_constraint_list_t constraints_rates = { - .count = NUM_SAMPLERATES, + .count = ARRAY_SIZE(samplerates), .list = samplerates, .mask = 0, }; @@ -428,7 +416,7 @@ static int snd_nm256_fixed_rate(unsigned int rate) { unsigned int i; - for (i = 0; i < NUM_SAMPLERATES; i++) { + for (i = 0; i < ARRAY_SIZE(samplerates); i++) { if (rate == samplerates[i]) return i; } @@ -542,12 +530,11 @@ snd_nm256_playback_trigger(snd_pcm_subst { nm256_t *chip = snd_pcm_substream_chip(substream); nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data; - unsigned long flags; int err = 0; snd_assert(s != NULL, return -ENXIO); - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock(&chip->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: @@ -567,7 +554,7 @@ snd_nm256_playback_trigger(snd_pcm_subst err = -EINVAL; break; } - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock(&chip->reg_lock); return err; } @@ -576,12 +563,11 @@ snd_nm256_capture_trigger(snd_pcm_substr { nm256_t *chip = snd_pcm_substream_chip(substream); nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data; - unsigned long flags; int err = 0; snd_assert(s != NULL, return -ENXIO); - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock(&chip->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: @@ -601,7 +587,7 @@ snd_nm256_capture_trigger(snd_pcm_substr err = -EINVAL; break; } - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock(&chip->reg_lock); return err; } @@ -614,7 +600,6 @@ static int snd_nm256_pcm_prepare(snd_pcm nm256_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; nm256_stream_t *s = (nm256_stream_t*)runtime->private_data; - unsigned long flags; snd_assert(s, return -ENXIO); s->dma_size = frames_to_bytes(runtime, substream->runtime->buffer_size); @@ -622,10 +607,10 @@ static int snd_nm256_pcm_prepare(snd_pcm s->periods = substream->runtime->periods; s->cur_period = 0; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); s->running = 0; snd_nm256_set_format(chip, s, substream); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return 0; } @@ -660,9 +645,9 @@ snd_nm256_capture_pointer(snd_pcm_substr return bytes_to_frames(substream->runtime, curp); } +/* Remapped I/O space can be accessible as pointer on i386 */ +/* This might be changed in the future */ #ifndef __i386__ -/* FIXME: I/O space is not accessible via pointers on all architectures */ - /* * silence / copy for playback */ @@ -757,10 +742,7 @@ snd_nm256_capture_update(nm256_t *chip) */ static snd_pcm_hardware_t snd_nm256_playback = { - .info = -#ifdef __i386__ - SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID| -#endif + .info = SNDRV_PCM_INFO_MMAP_IOMEM |SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | /*SNDRV_PCM_INFO_PAUSE |*/ SNDRV_PCM_INFO_RESUME, @@ -779,10 +761,7 @@ static snd_pcm_hardware_t snd_nm256_play static snd_pcm_hardware_t snd_nm256_capture = { - .info = -#ifdef __i386__ - SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID| -#endif + .info = SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | /*SNDRV_PCM_INFO_PAUSE |*/ SNDRV_PCM_INFO_RESUME, @@ -883,6 +862,7 @@ static snd_pcm_ops_t snd_nm256_playback_ .copy = snd_nm256_playback_copy, .silence = snd_nm256_playback_silence, #endif + .mmap = snd_pcm_lib_mmap_iomem, }; static snd_pcm_ops_t snd_nm256_capture_ops = { @@ -896,6 +876,7 @@ static snd_pcm_ops_t snd_nm256_capture_o #ifndef __i386__ .copy = snd_nm256_capture_copy, #endif + .mmap = snd_pcm_lib_mmap_iomem, }; static int __devinit @@ -932,16 +913,14 @@ snd_nm256_pcm(nm256_t *chip, int device) static void snd_nm256_init_chip(nm256_t *chip) { - unsigned long flags; - - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); /* Reset everything. */ snd_nm256_writeb(chip, 0x0, 0x11); snd_nm256_writew(chip, 0x214, 0); /* stop sounds.. */ //snd_nm256_playback_stop(chip); //snd_nm256_capture_stop(chip); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); } @@ -981,7 +960,7 @@ snd_nm256_intr_check(nm256_t *chip) static irqreturn_t snd_nm256_interrupt(int irq, void *dev_id, struct pt_regs *dummy) { - nm256_t *chip = snd_magic_cast(nm256_t, dev_id, return IRQ_NONE); + nm256_t *chip = dev_id; u16 status; u8 cbyte; @@ -1048,7 +1027,7 @@ snd_nm256_interrupt(int irq, void *dev_i static irqreturn_t snd_nm256_interrupt_zx(int irq, void *dev_id, struct pt_regs *dummy) { - nm256_t *chip = snd_magic_cast(nm256_t, dev_id, return IRQ_NONE); + nm256_t *chip = dev_id; u32 status; u8 cbyte; @@ -1139,7 +1118,7 @@ snd_nm256_ac97_ready(nm256_t *chip) static unsigned short snd_nm256_ac97_read(ac97_t *ac97, unsigned short reg) { - nm256_t *chip = snd_magic_cast(nm256_t, ac97->private_data, return -ENXIO); + nm256_t *chip = ac97->private_data; int res; if (reg >= 128) @@ -1159,7 +1138,7 @@ static void snd_nm256_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val) { - nm256_t *chip = snd_magic_cast(nm256_t, ac97->private_data, return); + nm256_t *chip = ac97->private_data; int tries = 2; u32 base; @@ -1181,10 +1160,9 @@ snd_nm256_ac97_write(ac97_t *ac97, static void snd_nm256_ac97_reset(ac97_t *ac97) { - nm256_t *chip = snd_magic_cast(nm256_t, ac97->private_data, return); - unsigned long flags; + nm256_t *chip = ac97->private_data; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock(&chip->reg_lock); /* Reset the mixer. 'Tis magic! */ snd_nm256_writeb(chip, 0x6c0, 1); if (chip->latitude_workaround) { @@ -1193,20 +1171,25 @@ snd_nm256_ac97_reset(ac97_t *ac97) } snd_nm256_writeb(chip, 0x6cc, 0x80); snd_nm256_writeb(chip, 0x6cc, 0x0); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock(&chip->reg_lock); } /* create an ac97 mixer interface */ static int __devinit snd_nm256_mixer(nm256_t *chip) { - ac97_bus_t bus, *pbus; - ac97_t ac97; + ac97_bus_t *pbus; + ac97_template_t ac97; int i, err; + static ac97_bus_ops_t ops = { + .reset = snd_nm256_ac97_reset, + .write = snd_nm256_ac97_write, + .read = snd_nm256_ac97_read, + }; /* looks like nm256 hangs up when unexpected registers are touched... */ static int mixer_regs[] = { AC97_MASTER, AC97_HEADPHONE, AC97_MASTER_MONO, - AC97_PC_BEEP, AC97_PHONE, AC97_MIC, AC97_LINE, + AC97_PC_BEEP, AC97_PHONE, AC97_MIC, AC97_LINE, AC97_CD, AC97_VIDEO, AC97_AUX, AC97_PCM, AC97_REC_SEL, AC97_REC_GAIN, AC97_GENERAL_PURPOSE, AC97_3D_CONTROL, AC97_EXTENDED_ID, @@ -1214,11 +1197,7 @@ snd_nm256_mixer(nm256_t *chip) -1 }; - memset(&bus, 0, sizeof(bus)); - bus.reset = snd_nm256_ac97_reset; - bus.write = snd_nm256_ac97_write; - bus.read = snd_nm256_ac97_read; - if ((err = snd_ac97_bus(chip->card, &bus, &pbus)) < 0) + if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0) return err; memset(&ac97, 0, sizeof(ac97)); @@ -1290,7 +1269,7 @@ snd_nm256_peek_for_sig(nm256_t *chip) */ static int nm256_suspend(snd_card_t *card, unsigned int state) { - nm256_t *chip = snd_magic_cast(nm256_t, card->pm_private_data, return -EINVAL); + nm256_t *chip = card->pm_private_data; snd_pcm_suspend_all(chip->pcm); snd_ac97_suspend(chip->ac97); @@ -1301,7 +1280,7 @@ static int nm256_suspend(snd_card_t *car static int nm256_resume(snd_card_t *card, unsigned int state) { - nm256_t *chip = snd_magic_cast(nm256_t, card->pm_private_data, return -EINVAL); + nm256_t *chip = card->pm_private_data; /* Perform a full reset on the hardware */ pci_enable_device(chip->pci); @@ -1340,13 +1319,13 @@ static int snd_nm256_free(nm256_t *chip) if (chip->irq >= 0) free_irq(chip->irq, (void*)chip); - snd_magic_kfree(chip); + kfree(chip); return 0; } static int snd_nm256_dev_free(snd_device_t *device) { - nm256_t *chip = snd_magic_cast(nm256_t, device->device_data, return -ENXIO); + nm256_t *chip = device->device_data; return snd_nm256_free(chip); } @@ -1368,7 +1347,7 @@ snd_nm256_create(snd_card_t *card, struc *chip_ret = NULL; - chip = snd_magic_kcalloc(nm256_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; --- linux-2.6.9-rc1/sound/pci/rme32.c 2004-08-24 00:03:39.000000000 -0700 +++ 25/sound/pci/rme32.c 2004-09-02 23:57:36.736737616 -0700 @@ -1,7 +1,8 @@ /* * ALSA driver for RME Digi32, Digi32/8 and Digi32 PRO audio interfaces * - * Copyright (c) 2002, 2003 Martin Langer + * Copyright (c) 2002-2004 Martin Langer , + * Pilo Chambert * * Thanks to : Anders Torger , * Henk Hesselink @@ -52,6 +53,19 @@ * patch would be welcome! * * **************************************************************************** + * + * "The story after the long seeking" -- tiwai + * + * Ok, the situation regarding the full duplex is now improved a bit. + * In the fullduplex mode (given by the module parameter), the hardware buffer + * is split to halves for read and write directions at the DMA pointer. + * That is, the half above the current DMA pointer is used for write, and + * the half below is used for read. To mangle this strange behavior, an + * software intermediate buffer is introduced. This is, of course, not good + * from the viewpoint of the data transfer efficiency. However, this allows + * you to use arbitrary buffer sizes, instead of the fixed I/O buffer size. + * + * **************************************************************************** */ @@ -68,6 +82,7 @@ #include #include #include +#include #include #include @@ -76,22 +91,21 @@ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int fullduplex[SNDRV_CARDS]; // = {[0 ... (SNDRV_CARDS - 1)] = 1}; static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for RME Digi32 soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for RME Digi32 soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable RME Digi32 soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); -MODULE_AUTHOR("Martin Langer "); +module_param_array(fullduplex, bool, boot_devs, 0444); +MODULE_PARM_DESC(fullduplex, "Support full-duplex mode."); +MODULE_AUTHOR("Martin Langer , Pilo Chambert "); MODULE_DESCRIPTION("RME Digi32, Digi32/8, Digi32 PRO"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{RME,Digi32}," "{RME,Digi32/8}," "{RME,Digi32 PRO}}"); +MODULE_SUPPORTED_DEVICE("{{RME,Digi32}," "{RME,Digi32/8}," "{RME,Digi32 PRO}}"); /* Defines for RME Digi32 series */ #define RME32_SPDIF_NCHANNELS 2 @@ -140,7 +154,7 @@ MODULE_DEVICES("{{RME,Digi32}," "{RME,Di #define RME32_WCR_BITPOS_INP_1 7 /* Read control register bits */ -#define RME32_RCR_AUDIO_ADDR_MASK 0x10001 +#define RME32_RCR_AUDIO_ADDR_MASK 0x1ffff #define RME32_RCR_LOCK (1 << 23) /* 1=locked, 0=not locked */ #define RME32_RCR_ERF (1 << 26) /* 1=Error, 0=no Error */ #define RME32_RCR_FREQ_0 (1 << 27) /* CS841x frequency (record) */ @@ -168,6 +182,9 @@ MODULE_DEVICES("{{RME,Digi32}," "{RME,Di /* Block sizes in bytes */ #define RME32_BLOCK_SIZE 8192 +/* Software intermediate buffer (max) size */ +#define RME32_MID_BUFFER_SIZE (1024*1024) + /* Hardware revisions */ #define RME32_32_REVISION 192 #define RME32_328_REVISION_OLD 100 @@ -194,7 +211,6 @@ typedef struct snd_rme32 { spinlock_t lock; int irq; unsigned long port; - struct resource *res_port; unsigned long iobase; u32 wcreg; /* cached write control register value */ @@ -213,9 +229,11 @@ typedef struct snd_rme32 { size_t playback_periodsize; /* in bytes, zero if not used */ size_t capture_periodsize; /* in bytes, zero if not used */ - snd_pcm_uframes_t playback_last_appl_ptr; - size_t playback_ptr; - size_t capture_ptr; + unsigned int fullduplex_mode; + int running; + + snd_pcm_indirect_t playback_pcm; + snd_pcm_indirect_t capture_pcm; snd_card_t *card; snd_pcm_t *spdif_pcm; @@ -243,33 +261,16 @@ static int snd_rme32_playback_prepare(sn static int snd_rme32_capture_prepare(snd_pcm_substream_t * substream); -static int -snd_rme32_playback_trigger(snd_pcm_substream_t * substream, int cmd); - -static int -snd_rme32_capture_trigger(snd_pcm_substream_t * substream, int cmd); - -static snd_pcm_uframes_t -snd_rme32_playback_pointer(snd_pcm_substream_t * substream); - -static snd_pcm_uframes_t -snd_rme32_capture_pointer(snd_pcm_substream_t * substream); +static int snd_rme32_pcm_trigger(snd_pcm_substream_t * substream, int cmd); static void snd_rme32_proc_init(rme32_t * rme32); static int snd_rme32_create_switches(snd_card_t * card, rme32_t * rme32); -static inline unsigned int snd_rme32_playback_ptr(rme32_t * rme32) -{ - - return (readl(rme32->iobase + RME32_IO_GET_POS) - & RME32_RCR_AUDIO_ADDR_MASK) >> rme32->playback_frlog; -} - -static inline unsigned int snd_rme32_capture_ptr(rme32_t * rme32) +static inline unsigned int snd_rme32_pcm_byteptr(rme32_t * rme32) { return (readl(rme32->iobase + RME32_IO_GET_POS) - & RME32_RCR_AUDIO_ADDR_MASK) >> rme32->capture_frlog; + & RME32_RCR_AUDIO_ADDR_MASK); } static int snd_rme32_ratecode(int rate) @@ -285,22 +286,24 @@ static int snd_rme32_ratecode(int rate) return 0; } +/* silence callback for halfduplex mode */ static int snd_rme32_playback_silence(snd_pcm_substream_t * substream, int channel, /* not used (interleaved data) */ snd_pcm_uframes_t pos, snd_pcm_uframes_t count) { - rme32_t *rme32 = _snd_pcm_substream_chip(substream); + rme32_t *rme32 = snd_pcm_substream_chip(substream); count <<= rme32->playback_frlog; pos <<= rme32->playback_frlog; memset_io(rme32->iobase + RME32_IO_DATA_BUFFER + pos, 0, count); return 0; } +/* copy callback for halfduplex mode */ static int snd_rme32_playback_copy(snd_pcm_substream_t * substream, int channel, /* not used (interleaved data) */ snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count) { - rme32_t *rme32 = _snd_pcm_substream_chip(substream); + rme32_t *rme32 = snd_pcm_substream_chip(substream); count <<= rme32->playback_frlog; pos <<= rme32->playback_frlog; if (copy_from_user_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos, @@ -309,11 +312,12 @@ static int snd_rme32_playback_copy(snd_p return 0; } +/* copy callback for halfduplex mode */ static int snd_rme32_capture_copy(snd_pcm_substream_t * substream, int channel, /* not used (interleaved data) */ snd_pcm_uframes_t pos, void __user *dst, snd_pcm_uframes_t count) { - rme32_t *rme32 = _snd_pcm_substream_chip(substream); + rme32_t *rme32 = snd_pcm_substream_chip(substream); count <<= rme32->capture_frlog; pos <<= rme32->capture_frlog; if (copy_to_user_fromio(dst, @@ -324,13 +328,14 @@ static int snd_rme32_capture_copy(snd_pc } /* - * Digital output capabilites (S/PDIF) + * SPDIF I/O capabilites (half-duplex mode) */ -static snd_pcm_hardware_t snd_rme32_playback_spdif_info = { - .info = (SNDRV_PCM_INFO_MMAP | +static snd_pcm_hardware_t snd_rme32_spdif_info = { + .info = (SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE), + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE), .rates = (SNDRV_PCM_RATE_32000 | @@ -349,13 +354,39 @@ static snd_pcm_hardware_t snd_rme32_play }; /* - * Digital input capabilites (S/PDIF) + * ADAT I/O capabilites (half-duplex mode) + */ +static snd_pcm_hardware_t snd_rme32_adat_info = +{ + .info = (SNDRV_PCM_INFO_MMAP_IOMEM | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START), + .formats= SNDRV_PCM_FMTBIT_S16_LE, + .rates = (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + .rate_min = 44100, + .rate_max = 48000, + .channels_min = 8, + .channels_max = 8, + .buffer_bytes_max = RME32_BUFFER_SIZE, + .period_bytes_min = RME32_BLOCK_SIZE, + .period_bytes_max = RME32_BLOCK_SIZE, + .periods_min = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE, + .periods_max = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE, + .fifo_size = 0, +}; + +/* + * SPDIF I/O capabilites (full-duplex mode) */ -static snd_pcm_hardware_t snd_rme32_capture_spdif_info = { +static snd_pcm_hardware_t snd_rme32_spdif_fd_info = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE), + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE), .rates = (SNDRV_PCM_RATE_32000 | @@ -365,23 +396,24 @@ static snd_pcm_hardware_t snd_rme32_capt .rate_max = 48000, .channels_min = 2, .channels_max = 2, - .buffer_bytes_max = RME32_BUFFER_SIZE, + .buffer_bytes_max = RME32_MID_BUFFER_SIZE, .period_bytes_min = RME32_BLOCK_SIZE, .period_bytes_max = RME32_BLOCK_SIZE, - .periods_min = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE, - .periods_max = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE, + .periods_min = 2, + .periods_max = RME32_MID_BUFFER_SIZE / RME32_BLOCK_SIZE, .fifo_size = 0, }; /* - * Digital output capabilites (ADAT) + * ADAT I/O capabilites (full-duplex mode) */ -static snd_pcm_hardware_t snd_rme32_playback_adat_info = +static snd_pcm_hardware_t snd_rme32_adat_fd_info = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE), + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START), .formats= SNDRV_PCM_FMTBIT_S16_LE, .rates = (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000), @@ -389,38 +421,14 @@ static snd_pcm_hardware_t snd_rme32_play .rate_max = 48000, .channels_min = 8, .channels_max = 8, - .buffer_bytes_max = RME32_BUFFER_SIZE, + .buffer_bytes_max = RME32_MID_BUFFER_SIZE, .period_bytes_min = RME32_BLOCK_SIZE, .period_bytes_max = RME32_BLOCK_SIZE, - .periods_min = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE, - .periods_max = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE, + .periods_min = 2, + .periods_max = RME32_MID_BUFFER_SIZE / RME32_BLOCK_SIZE, .fifo_size = 0, }; -/* - * Digital input capabilites (ADAT) - */ -static snd_pcm_hardware_t snd_rme32_capture_adat_info = -{ - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = (SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000), - .rate_min = 44100, - .rate_max = 48000, - .channels_min = 8, - .channels_max = 8, - .buffer_bytes_max = RME32_BUFFER_SIZE, - .period_bytes_min = RME32_BLOCK_SIZE, - .period_bytes_max = RME32_BLOCK_SIZE, - .periods_min = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE, - .periods_max = RME32_BUFFER_SIZE / RME32_BLOCK_SIZE, - .fifo_size = 0, -}; - static void snd_rme32_reset_dac(rme32_t *rme32) { writel(rme32->wcreg | RME32_WCR_PD, @@ -677,11 +685,19 @@ snd_rme32_playback_hw_params(snd_pcm_sub snd_pcm_hw_params_t * params) { int err, rate, dummy; - rme32_t *rme32 = _snd_pcm_substream_chip(substream); + rme32_t *rme32 = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + if (rme32->fullduplex_mode) { + err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + if (err < 0) + return err; + } else { + runtime->dma_area = (void *)(rme32->iobase + RME32_IO_DATA_BUFFER); + runtime->dma_addr = rme32->port + RME32_IO_DATA_BUFFER; + runtime->dma_bytes = RME32_BUFFER_SIZE; + } - if ((err = snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(params))) < 0) - return err; spin_lock_irq(&rme32->lock); if ((rme32->rcreg & RME32_RCR_KMODE) && (rate = snd_rme32_capture_getrate(rme32, &dummy)) > 0) { @@ -719,45 +735,45 @@ snd_rme32_playback_hw_params(snd_pcm_sub return 0; } -static int snd_rme32_playback_hw_free(snd_pcm_substream_t * substream) -{ - snd_pcm_lib_free_pages(substream); - return 0; -} - static int snd_rme32_capture_hw_params(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * params) { - unsigned long flags; int err, isadat, rate; - rme32_t *rme32 = _snd_pcm_substream_chip(substream); + rme32_t *rme32 = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - if ((err = snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(params))) < 0) - return err; - spin_lock_irqsave(&rme32->lock, flags); + if (rme32->fullduplex_mode) { + err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + if (err < 0) + return err; + } else { + runtime->dma_area = (void *)rme32->iobase + RME32_IO_DATA_BUFFER; + runtime->dma_addr = rme32->port + RME32_IO_DATA_BUFFER; + runtime->dma_bytes = RME32_BUFFER_SIZE; + } + + spin_lock_irq(&rme32->lock); /* enable AutoSync for record-preparing */ rme32->wcreg |= RME32_WCR_AUTOSYNC; writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); if ((err = snd_rme32_setformat(rme32, params_format(params))) < 0) { - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); return err; } if ((err = snd_rme32_playback_setrate(rme32, params_rate(params))) < 0) { - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); return err; } if ((rate = snd_rme32_capture_getrate(rme32, &isadat)) > 0) { if ((int)params_rate(params) != rate) { - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); return -EIO; } if ((isadat && runtime->hw.channels_min == 2) || (!isadat && runtime->hw.channels_min == 8)) { - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); return -EIO; } } @@ -769,36 +785,26 @@ snd_rme32_capture_hw_params(snd_pcm_subs if (rme32->playback_periodsize != 0) { if (params_period_size(params) << rme32->capture_frlog != rme32->playback_periodsize) { - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); return -EBUSY; } } rme32->capture_periodsize = params_period_size(params) << rme32->capture_frlog; - spin_unlock_irqrestore(&rme32->lock, flags); - - return 0; -} + spin_unlock_irq(&rme32->lock); -static int snd_rme32_capture_hw_free(snd_pcm_substream_t * substream) -{ - snd_pcm_lib_free_pages(substream); return 0; } -static void snd_rme32_playback_start(rme32_t * rme32, int from_pause) +static int snd_rme32_pcm_hw_free(snd_pcm_substream_t * substream) { - if (!from_pause) { - writel(0, rme32->iobase + RME32_IO_RESET_POS); - rme32->playback_last_appl_ptr = 0; - rme32->playback_ptr = 0; - } - - rme32->wcreg |= RME32_WCR_START; - writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); + rme32_t *rme32 = snd_pcm_substream_chip(substream); + if (! rme32->fullduplex_mode) + return 0; + return snd_pcm_lib_free_pages(substream); } -static void snd_rme32_capture_start(rme32_t * rme32, int from_pause) +static void snd_rme32_pcm_start(rme32_t * rme32, int from_pause) { if (!from_pause) { writel(0, rme32->iobase + RME32_IO_RESET_POS); @@ -808,7 +814,7 @@ static void snd_rme32_capture_start(rme3 writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); } -static void snd_rme32_playback_stop(rme32_t * rme32) +static void snd_rme32_pcm_stop(rme32_t * rme32, int to_pause) { /* * Check if there is an unconfirmed IRQ, if so confirm it, or else @@ -822,16 +828,8 @@ static void snd_rme32_playback_stop(rme3 if (rme32->wcreg & RME32_WCR_SEL) rme32->wcreg |= RME32_WCR_MUTE; writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); -} - -static void snd_rme32_capture_stop(rme32_t * rme32) -{ - rme32->rcreg = readl(rme32->iobase + RME32_IO_CONTROL_REGISTER); - if (rme32->rcreg & RME32_RCR_IRQ) { - writel(0, rme32->iobase + RME32_IO_CONFIRM_ACTION_IRQ); - } - rme32->wcreg &= ~RME32_WCR_START; - writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); + if (! to_pause) + writel(0, rme32->iobase + RME32_IO_RESET_POS); } static irqreturn_t @@ -857,36 +855,46 @@ snd_rme32_interrupt(int irq, void *dev_i static unsigned int period_bytes[] = { RME32_BLOCK_SIZE }; -#define PERIOD_BYTES sizeof(period_bytes) / sizeof(period_bytes[0]) - static snd_pcm_hw_constraint_list_t hw_constraints_period_bytes = { - .count = PERIOD_BYTES, + .count = ARRAY_SIZE(period_bytes), .list = period_bytes, .mask = 0 }; +static void snd_rme32_set_buffer_constraint(rme32_t *rme32, snd_pcm_runtime_t *runtime) +{ + if (! rme32->fullduplex_mode) { + snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + RME32_BUFFER_SIZE, RME32_BUFFER_SIZE); + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + &hw_constraints_period_bytes); + } +} + static int snd_rme32_playback_spdif_open(snd_pcm_substream_t * substream) { - unsigned long flags; int rate, dummy; - rme32_t *rme32 = _snd_pcm_substream_chip(substream); + rme32_t *rme32 = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_set_sync(substream); - spin_lock_irqsave(&rme32->lock, flags); + spin_lock_irq(&rme32->lock); if (rme32->playback_substream != NULL) { - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); return -EBUSY; } rme32->wcreg &= ~RME32_WCR_ADAT; writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); rme32->playback_substream = substream; - rme32->playback_last_appl_ptr = 0; - rme32->playback_ptr = 0; - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); - runtime->hw = snd_rme32_playback_spdif_info; + if (rme32->fullduplex_mode) + runtime->hw = snd_rme32_spdif_fd_info; + else + runtime->hw = snd_rme32_spdif_info; if (rme32->pci->device == PCI_DEVICE_ID_DIGI32_PRO) { runtime->hw.rates |= SNDRV_PCM_RATE_64000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000; runtime->hw.rate_max = 96000; @@ -898,12 +906,8 @@ static int snd_rme32_playback_spdif_open runtime->hw.rate_min = rate; runtime->hw.rate_max = rate; } - snd_pcm_hw_constraint_minmax(runtime, - SNDRV_PCM_HW_PARAM_BUFFER_BYTES, - RME32_BUFFER_SIZE, RME32_BUFFER_SIZE); - snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_BYTES, - &hw_constraints_period_bytes); + + snd_rme32_set_buffer_constraint(rme32, runtime); rme32->wcreg_spdif_stream = rme32->wcreg_spdif; rme32->spdif_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; @@ -914,23 +918,24 @@ static int snd_rme32_playback_spdif_open static int snd_rme32_capture_spdif_open(snd_pcm_substream_t * substream) { - unsigned long flags; int isadat, rate; - rme32_t *rme32 = _snd_pcm_substream_chip(substream); + rme32_t *rme32 = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_set_sync(substream); - spin_lock_irqsave(&rme32->lock, flags); + spin_lock_irq(&rme32->lock); if (rme32->capture_substream != NULL) { - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); return -EBUSY; } rme32->capture_substream = substream; - rme32->capture_ptr = 0; - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); - runtime->hw = snd_rme32_capture_spdif_info; + if (rme32->fullduplex_mode) + runtime->hw = snd_rme32_spdif_fd_info; + else + runtime->hw = snd_rme32_spdif_info; if (RME32_PRO_WITH_8414(rme32)) { runtime->hw.rates |= SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000; runtime->hw.rate_max = 96000; @@ -944,12 +949,7 @@ static int snd_rme32_capture_spdif_open( runtime->hw.rate_max = rate; } - snd_pcm_hw_constraint_minmax(runtime, - SNDRV_PCM_HW_PARAM_BUFFER_BYTES, - RME32_BUFFER_SIZE, RME32_BUFFER_SIZE); - snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_BYTES, - &hw_constraints_period_bytes); + snd_rme32_set_buffer_constraint(rme32, runtime); return 0; } @@ -957,26 +957,26 @@ static int snd_rme32_capture_spdif_open( static int snd_rme32_playback_adat_open(snd_pcm_substream_t *substream) { - unsigned long flags; int rate, dummy; - rme32_t *rme32 = _snd_pcm_substream_chip(substream); + rme32_t *rme32 = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_set_sync(substream); - spin_lock_irqsave(&rme32->lock, flags); + spin_lock_irq(&rme32->lock); if (rme32->playback_substream != NULL) { - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); return -EBUSY; } rme32->wcreg |= RME32_WCR_ADAT; writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); rme32->playback_substream = substream; - rme32->playback_last_appl_ptr = 0; - rme32->playback_ptr = 0; - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); - runtime->hw = snd_rme32_playback_adat_info; + if (rme32->fullduplex_mode) + runtime->hw = snd_rme32_adat_fd_info; + else + runtime->hw = snd_rme32_adat_info; if ((rme32->rcreg & RME32_RCR_KMODE) && (rate = snd_rme32_capture_getrate(rme32, &dummy)) > 0) { /* AutoSync */ @@ -984,22 +984,22 @@ snd_rme32_playback_adat_open(snd_pcm_sub runtime->hw.rate_min = rate; runtime->hw.rate_max = rate; } - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, - RME32_BUFFER_SIZE, RME32_BUFFER_SIZE); - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, - &hw_constraints_period_bytes); + + snd_rme32_set_buffer_constraint(rme32, runtime); return 0; } static int snd_rme32_capture_adat_open(snd_pcm_substream_t *substream) { - unsigned long flags; int isadat, rate; - rme32_t *rme32 = _snd_pcm_substream_chip(substream); + rme32_t *rme32 = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - runtime->hw = snd_rme32_capture_adat_info; + if (rme32->fullduplex_mode) + runtime->hw = snd_rme32_adat_fd_info; + else + runtime->hw = snd_rme32_adat_info; if ((rate = snd_rme32_capture_getrate(rme32, &isadat)) > 0) { if (!isadat) { return -EIO; @@ -1011,33 +1011,28 @@ snd_rme32_capture_adat_open(snd_pcm_subs snd_pcm_set_sync(substream); - spin_lock_irqsave(&rme32->lock, flags); + spin_lock_irq(&rme32->lock); if (rme32->capture_substream != NULL) { - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); return -EBUSY; } rme32->capture_substream = substream; - rme32->capture_ptr = 0; - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, - RME32_BUFFER_SIZE, RME32_BUFFER_SIZE); - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, - &hw_constraints_period_bytes); + snd_rme32_set_buffer_constraint(rme32, runtime); return 0; } static int snd_rme32_playback_close(snd_pcm_substream_t * substream) { - unsigned long flags; - rme32_t *rme32 = _snd_pcm_substream_chip(substream); + rme32_t *rme32 = snd_pcm_substream_chip(substream); int spdif = 0; - spin_lock_irqsave(&rme32->lock, flags); + spin_lock_irq(&rme32->lock); rme32->playback_substream = NULL; rme32->playback_periodsize = 0; spdif = (rme32->wcreg & RME32_WCR_ADAT) == 0; - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); if (spdif) { rme32->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; snd_ctl_notify(rme32->card, SNDRV_CTL_EVENT_MASK_VALUE | @@ -1049,210 +1044,207 @@ static int snd_rme32_playback_close(snd_ static int snd_rme32_capture_close(snd_pcm_substream_t * substream) { - unsigned long flags; - rme32_t *rme32 = _snd_pcm_substream_chip(substream); + rme32_t *rme32 = snd_pcm_substream_chip(substream); - spin_lock_irqsave(&rme32->lock, flags); + spin_lock_irq(&rme32->lock); rme32->capture_substream = NULL; rme32->capture_periodsize = 0; - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock(&rme32->lock); return 0; } static int snd_rme32_playback_prepare(snd_pcm_substream_t * substream) { - rme32_t *rme32 = _snd_pcm_substream_chip(substream); - unsigned long flags; + rme32_t *rme32 = snd_pcm_substream_chip(substream); - spin_lock_irqsave(&rme32->lock, flags); - if (RME32_ISWORKING(rme32)) { - snd_rme32_playback_stop(rme32); + spin_lock_irq(&rme32->lock); + if (rme32->fullduplex_mode) { + memset(&rme32->playback_pcm, 0, sizeof(rme32->playback_pcm)); + rme32->playback_pcm.hw_buffer_size = RME32_BUFFER_SIZE; + rme32->playback_pcm.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream); + } else { + writel(0, rme32->iobase + RME32_IO_RESET_POS); } - writel(0, rme32->iobase + RME32_IO_RESET_POS); if (rme32->wcreg & RME32_WCR_SEL) rme32->wcreg &= ~RME32_WCR_MUTE; writel(rme32->wcreg, rme32->iobase + RME32_IO_CONTROL_REGISTER); - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); return 0; } static int snd_rme32_capture_prepare(snd_pcm_substream_t * substream) { - rme32_t *rme32 = _snd_pcm_substream_chip(substream); - unsigned long flags; + rme32_t *rme32 = snd_pcm_substream_chip(substream); - spin_lock_irqsave(&rme32->lock, flags); - if (RME32_ISWORKING(rme32)) { - snd_rme32_capture_stop(rme32); + spin_lock_irq(&rme32->lock); + if (rme32->fullduplex_mode) { + memset(&rme32->capture_pcm, 0, sizeof(rme32->capture_pcm)); + rme32->capture_pcm.hw_buffer_size = RME32_BUFFER_SIZE; + rme32->capture_pcm.hw_queue_size = RME32_BUFFER_SIZE / 2; + rme32->capture_pcm.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream); + } else { + writel(0, rme32->iobase + RME32_IO_RESET_POS); } - writel(0, rme32->iobase + RME32_IO_RESET_POS); - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); return 0; } static int -snd_rme32_playback_trigger(snd_pcm_substream_t * substream, int cmd) +snd_rme32_pcm_trigger(snd_pcm_substream_t * substream, int cmd) { - rme32_t *rme32 = _snd_pcm_substream_chip(substream); - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - if (!RME32_ISWORKING(rme32)) { - if (substream != rme32->playback_substream) { - return -EBUSY; + rme32_t *rme32 = snd_pcm_substream_chip(substream); + struct list_head *pos; + snd_pcm_substream_t *s; + + spin_lock(&rme32->lock); + snd_pcm_group_for_each(pos, substream) { + s = snd_pcm_group_substream_entry(pos); + if (s != rme32->playback_substream && + s != rme32->capture_substream) + continue; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + rme32->running |= (1 << s->stream); + if (rme32->fullduplex_mode) { + /* remember the current DMA position */ + if (s == rme32->playback_substream) { + rme32->playback_pcm.hw_io = + rme32->playback_pcm.hw_data = snd_rme32_pcm_byteptr(rme32); + } else { + rme32->capture_pcm.hw_io = + rme32->capture_pcm.hw_data = snd_rme32_pcm_byteptr(rme32); + } } - snd_rme32_playback_start(rme32, 0); + break; + case SNDRV_PCM_TRIGGER_STOP: + rme32->running &= ~(1 << s->stream); + break; } - break; - - case SNDRV_PCM_TRIGGER_STOP: - if (RME32_ISWORKING(rme32)) { - if (substream != rme32->playback_substream) { - return -EBUSY; + snd_pcm_trigger_done(s, substream); + } + + /* prefill playback buffer */ + if (cmd == SNDRV_PCM_TRIGGER_START) { + snd_pcm_group_for_each(pos, substream) { + s = snd_pcm_group_substream_entry(pos); + if (s == rme32->playback_substream) { + s->ops->ack(s); + break; } - snd_rme32_playback_stop(rme32); - } - break; - - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (RME32_ISWORKING(rme32)) { - snd_rme32_playback_stop(rme32); - } - break; - - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (!RME32_ISWORKING(rme32)) { - snd_rme32_playback_start(rme32, 1); } - break; - - default: - return -EINVAL; } - return 0; -} - -static int -snd_rme32_capture_trigger(snd_pcm_substream_t * substream, int cmd) -{ - rme32_t *rme32 = _snd_pcm_substream_chip(substream); switch (cmd) { case SNDRV_PCM_TRIGGER_START: - if (!RME32_ISWORKING(rme32)) { - if (substream != rme32->capture_substream) { - return -EBUSY; - } - snd_rme32_capture_start(rme32, 0); - } + if (rme32->running && ! RME32_ISWORKING(rme32)) + snd_rme32_pcm_start(rme32, 0); break; - case SNDRV_PCM_TRIGGER_STOP: - if (RME32_ISWORKING(rme32)) { - if (substream != rme32->capture_substream) { - return -EBUSY; - } - snd_rme32_capture_stop(rme32); - } + if (! rme32->running && RME32_ISWORKING(rme32)) + snd_rme32_pcm_stop(rme32, 0); break; - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (RME32_ISWORKING(rme32)) { - snd_rme32_capture_stop(rme32); - } + if (rme32->running && RME32_ISWORKING(rme32)) + snd_rme32_pcm_stop(rme32, 1); break; - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (!RME32_ISWORKING(rme32)) { - snd_rme32_capture_start(rme32, 1); - } + if (rme32->running && ! RME32_ISWORKING(rme32)) + snd_rme32_pcm_start(rme32, 1); break; - - default: - return -EINVAL; } - + spin_unlock(&rme32->lock); return 0; } +/* pointer callback for halfduplex mode */ static snd_pcm_uframes_t snd_rme32_playback_pointer(snd_pcm_substream_t * substream) { - rme32_t *rme32 = _snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; - snd_pcm_sframes_t diff; - size_t bytes; + rme32_t *rme32 = snd_pcm_substream_chip(substream); + return snd_rme32_pcm_byteptr(rme32) >> rme32->playback_frlog; +} +static snd_pcm_uframes_t +snd_rme32_capture_pointer(snd_pcm_substream_t * substream) +{ + rme32_t *rme32 = snd_pcm_substream_chip(substream); + return snd_rme32_pcm_byteptr(rme32) >> rme32->capture_frlog; +} - if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { - diff = runtime->control->appl_ptr - - rme32->playback_last_appl_ptr; - rme32->playback_last_appl_ptr = runtime->control->appl_ptr; - if (diff != 0 && diff < -(snd_pcm_sframes_t) (runtime->boundary >> 1)) { - diff += runtime->boundary; - } - bytes = diff << rme32->playback_frlog; - if (bytes > RME32_BUFFER_SIZE - rme32->playback_ptr) { - memcpy_toio((void *)(rme32->iobase + RME32_IO_DATA_BUFFER + rme32->playback_ptr), - runtime->dma_area + rme32->playback_ptr, - RME32_BUFFER_SIZE - rme32->playback_ptr); - bytes -= RME32_BUFFER_SIZE - rme32->playback_ptr; - if (bytes > RME32_BUFFER_SIZE) { - bytes = RME32_BUFFER_SIZE; - } - memcpy_toio((void *)(rme32->iobase + RME32_IO_DATA_BUFFER), - runtime->dma_area, bytes); - rme32->playback_ptr = bytes; - } else if (bytes != 0) { - memcpy_toio((void *)(rme32->iobase + RME32_IO_DATA_BUFFER + rme32->playback_ptr), - runtime->dma_area + rme32->playback_ptr, bytes); - rme32->playback_ptr += bytes; - } - } - return snd_rme32_playback_ptr(rme32); + +/* ack and pointer callbacks for fullduplex mode */ +static void snd_rme32_pb_trans_copy(snd_pcm_substream_t *substream, + snd_pcm_indirect_t *rec, size_t bytes) +{ + rme32_t *rme32 = snd_pcm_substream_chip(substream); + memcpy_toio(rme32->iobase + RME32_IO_DATA_BUFFER + rec->hw_data, + substream->runtime->dma_area + rec->sw_data, bytes); +} + +static int snd_rme32_playback_fd_ack(snd_pcm_substream_t *substream) +{ + rme32_t *rme32 = snd_pcm_substream_chip(substream); + snd_pcm_indirect_t *rec, *cprec; + unsigned long flags; + + rec = &rme32->playback_pcm; + cprec = &rme32->capture_pcm; + spin_lock_irqsave(&rme32->lock, flags); + rec->hw_queue_size = RME32_BUFFER_SIZE; + if (rme32->running & (1 << SNDRV_PCM_STREAM_CAPTURE)) + rec->hw_queue_size -= cprec->hw_ready; + spin_unlock_irqrestore(&rme32->lock, flags); + snd_pcm_indirect_playback_transfer(substream, rec, + snd_rme32_pb_trans_copy); + return 0; +} + +static void snd_rme32_cp_trans_copy(snd_pcm_substream_t *substream, + snd_pcm_indirect_t *rec, size_t bytes) +{ + rme32_t *rme32 = snd_pcm_substream_chip(substream); + memcpy_fromio(substream->runtime->dma_area + rec->sw_data, + rme32->iobase + RME32_IO_DATA_BUFFER + rec->hw_data, + bytes); +} + +static int snd_rme32_capture_fd_ack(snd_pcm_substream_t *substream) +{ + rme32_t *rme32 = snd_pcm_substream_chip(substream); + snd_pcm_indirect_capture_transfer(substream, &rme32->capture_pcm, + snd_rme32_cp_trans_copy); + return 0; } static snd_pcm_uframes_t -snd_rme32_capture_pointer(snd_pcm_substream_t * substream) +snd_rme32_playback_fd_pointer(snd_pcm_substream_t * substream) { - rme32_t *rme32 = _snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; - snd_pcm_uframes_t frameptr; - size_t ptr; + rme32_t *rme32 = snd_pcm_substream_chip(substream); + return snd_pcm_indirect_playback_pointer(substream, &rme32->playback_pcm, + snd_rme32_pcm_byteptr(rme32)); +} - frameptr = snd_rme32_capture_ptr(rme32); - if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { - ptr = frameptr << rme32->capture_frlog; - if (ptr > rme32->capture_ptr) { - memcpy_fromio(runtime->dma_area + rme32->capture_ptr, - (void *)(rme32->iobase + RME32_IO_DATA_BUFFER + - rme32->capture_ptr), - ptr - rme32->capture_ptr); - rme32->capture_ptr += ptr - rme32->capture_ptr; - } else if (ptr < rme32->capture_ptr) { - memcpy_fromio(runtime->dma_area + rme32->capture_ptr, - (void *)(rme32->iobase + RME32_IO_DATA_BUFFER + - rme32->capture_ptr), - RME32_BUFFER_SIZE - rme32->capture_ptr); - memcpy_fromio(runtime->dma_area, - (void *)(rme32->iobase + RME32_IO_DATA_BUFFER), - ptr); - rme32->capture_ptr = ptr; - } - } - return frameptr; +static snd_pcm_uframes_t +snd_rme32_capture_fd_pointer(snd_pcm_substream_t * substream) +{ + rme32_t *rme32 = snd_pcm_substream_chip(substream); + return snd_pcm_indirect_capture_pointer(substream, &rme32->capture_pcm, + snd_rme32_pcm_byteptr(rme32)); } +/* for halfduplex mode */ static snd_pcm_ops_t snd_rme32_playback_spdif_ops = { .open = snd_rme32_playback_spdif_open, .close = snd_rme32_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_rme32_playback_hw_params, - .hw_free = snd_rme32_playback_hw_free, + .hw_free = snd_rme32_pcm_hw_free, .prepare = snd_rme32_playback_prepare, - .trigger = snd_rme32_playback_trigger, + .trigger = snd_rme32_pcm_trigger, .pointer = snd_rme32_playback_pointer, .copy = snd_rme32_playback_copy, .silence = snd_rme32_playback_silence, + .mmap = snd_pcm_lib_mmap_iomem, }; static snd_pcm_ops_t snd_rme32_capture_spdif_ops = { @@ -1260,11 +1252,12 @@ static snd_pcm_ops_t snd_rme32_capture_s .close = snd_rme32_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_rme32_capture_hw_params, - .hw_free = snd_rme32_capture_hw_free, + .hw_free = snd_rme32_pcm_hw_free, .prepare = snd_rme32_capture_prepare, - .trigger = snd_rme32_capture_trigger, + .trigger = snd_rme32_pcm_trigger, .pointer = snd_rme32_capture_pointer, .copy = snd_rme32_capture_copy, + .mmap = snd_pcm_lib_mmap_iomem, }; static snd_pcm_ops_t snd_rme32_playback_adat_ops = { @@ -1272,12 +1265,12 @@ static snd_pcm_ops_t snd_rme32_playback_ .close = snd_rme32_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_rme32_playback_hw_params, - .hw_free = snd_rme32_playback_hw_free, .prepare = snd_rme32_playback_prepare, - .trigger = snd_rme32_playback_trigger, + .trigger = snd_rme32_pcm_trigger, .pointer = snd_rme32_playback_pointer, .copy = snd_rme32_playback_copy, .silence = snd_rme32_playback_silence, + .mmap = snd_pcm_lib_mmap_iomem, }; static snd_pcm_ops_t snd_rme32_capture_adat_ops = { @@ -1285,11 +1278,58 @@ static snd_pcm_ops_t snd_rme32_capture_a .close = snd_rme32_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_rme32_capture_hw_params, - .hw_free = snd_rme32_capture_hw_free, .prepare = snd_rme32_capture_prepare, - .trigger = snd_rme32_capture_trigger, + .trigger = snd_rme32_pcm_trigger, .pointer = snd_rme32_capture_pointer, .copy = snd_rme32_capture_copy, + .mmap = snd_pcm_lib_mmap_iomem, +}; + +/* for fullduplex mode */ +static snd_pcm_ops_t snd_rme32_playback_spdif_fd_ops = { + .open = snd_rme32_playback_spdif_open, + .close = snd_rme32_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_rme32_playback_hw_params, + .hw_free = snd_rme32_pcm_hw_free, + .prepare = snd_rme32_playback_prepare, + .trigger = snd_rme32_pcm_trigger, + .pointer = snd_rme32_playback_fd_pointer, + .ack = snd_rme32_playback_fd_ack, +}; + +static snd_pcm_ops_t snd_rme32_capture_spdif_fd_ops = { + .open = snd_rme32_capture_spdif_open, + .close = snd_rme32_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_rme32_capture_hw_params, + .hw_free = snd_rme32_pcm_hw_free, + .prepare = snd_rme32_capture_prepare, + .trigger = snd_rme32_pcm_trigger, + .pointer = snd_rme32_capture_fd_pointer, + .ack = snd_rme32_capture_fd_ack, +}; + +static snd_pcm_ops_t snd_rme32_playback_adat_fd_ops = { + .open = snd_rme32_playback_adat_open, + .close = snd_rme32_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_rme32_playback_hw_params, + .prepare = snd_rme32_playback_prepare, + .trigger = snd_rme32_pcm_trigger, + .pointer = snd_rme32_playback_fd_pointer, + .ack = snd_rme32_playback_fd_ack, +}; + +static snd_pcm_ops_t snd_rme32_capture_adat_fd_ops = { + .open = snd_rme32_capture_adat_open, + .close = snd_rme32_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_rme32_capture_hw_params, + .prepare = snd_rme32_capture_prepare, + .trigger = snd_rme32_pcm_trigger, + .pointer = snd_rme32_capture_fd_pointer, + .ack = snd_rme32_capture_fd_ack, }; static void snd_rme32_free(void *private_data) @@ -1300,8 +1340,7 @@ static void snd_rme32_free(void *private return; } if (rme32->irq >= 0) { - snd_rme32_playback_stop(rme32); - snd_rme32_capture_stop(rme32); + snd_rme32_pcm_stop(rme32, 0); free_irq(rme32->irq, (void *) rme32); rme32->irq = -1; } @@ -1309,9 +1348,9 @@ static void snd_rme32_free(void *private iounmap((void *) rme32->iobase); rme32->iobase = 0; } - if (rme32->res_port != NULL) { - release_resource(rme32->res_port); - rme32->res_port = NULL; + if (rme32->port) { + pci_release_regions(rme32->pci); + rme32->port = 0; } } @@ -1319,7 +1358,6 @@ static void snd_rme32_free_spdif_pcm(snd { rme32_t *rme32 = (rme32_t *) pcm->private_data; rme32->spdif_pcm = NULL; - snd_pcm_lib_preallocate_free_for_all(pcm); } static void @@ -1327,7 +1365,6 @@ snd_rme32_free_adat_pcm(snd_pcm_t *pcm) { rme32_t *rme32 = (rme32_t *) pcm->private_data; rme32->adat_pcm = NULL; - snd_pcm_lib_preallocate_free_for_all(pcm); } static int __devinit snd_rme32_create(rme32_t * rme32) @@ -1336,25 +1373,21 @@ static int __devinit snd_rme32_create(rm int err; rme32->irq = -1; + spin_lock_init(&rme32->lock); if ((err = pci_enable_device(pci)) < 0) return err; + if ((err = pci_request_regions(pci, "RME32")) < 0) + return err; rme32->port = pci_resource_start(rme32->pci, 0); - if ((rme32->res_port = request_mem_region(rme32->port, RME32_IO_SIZE, "RME32")) == NULL) { - snd_printk("unable to grab memory region 0x%lx-0x%lx\n", - rme32->port, rme32->port + RME32_IO_SIZE - 1); - return -EBUSY; - } - if (request_irq(pci->irq, snd_rme32_interrupt, SA_INTERRUPT | SA_SHIRQ, "RME32", (void *) rme32)) { snd_printk("unable to grab IRQ %d\n", pci->irq); return -EBUSY; } rme32->irq = pci->irq; - spin_lock_init(&rme32->lock); if ((rme32->iobase = (unsigned long) ioremap_nocache(rme32->port, RME32_IO_SIZE)) == 0) { snd_printk("unable to remap memory region 0x%lx-0x%lx\n", rme32->port, rme32->port + RME32_IO_SIZE - 1); @@ -1371,18 +1404,22 @@ static int __devinit snd_rme32_create(rm rme32->spdif_pcm->private_data = rme32; rme32->spdif_pcm->private_free = snd_rme32_free_spdif_pcm; strcpy(rme32->spdif_pcm->name, "Digi32 IEC958"); - snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, - &snd_rme32_playback_spdif_ops); - snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE, - &snd_rme32_capture_spdif_ops); - - rme32->spdif_pcm->info_flags = 0; - - snd_pcm_lib_preallocate_pages_for_all(rme32->spdif_pcm, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), - RME32_BUFFER_SIZE, - RME32_BUFFER_SIZE); + if (rme32->fullduplex_mode) { + snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_rme32_playback_spdif_fd_ops); + snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_rme32_capture_spdif_fd_ops); + snd_pcm_lib_preallocate_pages_for_all(rme32->spdif_pcm, SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + 0, RME32_MID_BUFFER_SIZE); + rme32->spdif_pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; + } else { + snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_rme32_playback_spdif_ops); + snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_rme32_capture_spdif_ops); + rme32->spdif_pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; + } /* set up ALSA pcm device for ADAT */ if ((pci->device == PCI_DEVICE_ID_DIGI32) || @@ -1399,18 +1436,22 @@ static int __devinit snd_rme32_create(rm rme32->adat_pcm->private_data = rme32; rme32->adat_pcm->private_free = snd_rme32_free_adat_pcm; strcpy(rme32->adat_pcm->name, "Digi32 ADAT"); - snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, - &snd_rme32_playback_adat_ops); - snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, - &snd_rme32_capture_adat_ops); - - rme32->adat_pcm->info_flags = 0; - - snd_pcm_lib_preallocate_pages_for_all(rme32->adat_pcm, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), - RME32_BUFFER_SIZE, - RME32_BUFFER_SIZE); + if (rme32->fullduplex_mode) { + snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_rme32_playback_adat_fd_ops); + snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_rme32_capture_adat_fd_ops); + snd_pcm_lib_preallocate_pages_for_all(rme32->adat_pcm, SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + 0, RME32_MID_BUFFER_SIZE); + rme32->adat_pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; + } else { + snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_rme32_playback_adat_ops); + snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_rme32_capture_adat_ops); + rme32->adat_pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; + } } @@ -1418,8 +1459,7 @@ static int __devinit snd_rme32_create(rm rme32->capture_periodsize = 0; /* make sure playback/capture is stopped, if by some reason active */ - snd_rme32_playback_stop(rme32); - snd_rme32_capture_stop(rme32); + snd_rme32_pcm_stop(rme32, 0); /* reset DAC */ snd_rme32_reset_dac(rme32); @@ -1464,6 +1504,10 @@ snd_rme32_proc_read(snd_info_entry_t * e snd_iprintf(buffer, " (index #%d)\n", rme32->card->number + 1); snd_iprintf(buffer, "\nGeneral settings\n"); + if (rme32->fullduplex_mode) + snd_iprintf(buffer, " Full-duplex mode\n"); + else + snd_iprintf(buffer, " Half-duplex mode\n"); if (RME32_PRO_WITH_8414(rme32)) { snd_iprintf(buffer, " receiver: CS8414\n"); } else { @@ -1569,26 +1613,24 @@ static int snd_rme32_get_loopback_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme32_t *rme32 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme32_t *rme32 = snd_kcontrol_chip(kcontrol); - spin_lock_irqsave(&rme32->lock, flags); + spin_lock_irq(&rme32->lock); ucontrol->value.integer.value[0] = rme32->wcreg & RME32_WCR_SEL ? 0 : 1; - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); return 0; } static int snd_rme32_put_loopback_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme32_t *rme32 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme32_t *rme32 = snd_kcontrol_chip(kcontrol); unsigned int val; int change; val = ucontrol->value.integer.value[0] ? 0 : RME32_WCR_SEL; - spin_lock_irqsave(&rme32->lock, flags); + spin_lock_irq(&rme32->lock); val = (rme32->wcreg & ~RME32_WCR_SEL) | val; change = val != rme32->wcreg; if (ucontrol->value.integer.value[0]) @@ -1597,7 +1639,7 @@ snd_rme32_put_loopback_control(snd_kcont val |= RME32_WCR_MUTE; writel(rme32->wcreg = val, rme32->iobase + RME32_IO_CONTROL_REGISTER); - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); return change; } @@ -1605,7 +1647,7 @@ static int snd_rme32_info_inputtype_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo) { - rme32_t *rme32 = _snd_kcontrol_chip(kcontrol); + rme32_t *rme32 = snd_kcontrol_chip(kcontrol); static char *texts[4] = { "Optical", "Coaxial", "Internal", "XLR" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; @@ -1635,11 +1677,10 @@ static int snd_rme32_get_inputtype_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme32_t *rme32 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme32_t *rme32 = snd_kcontrol_chip(kcontrol); unsigned int items = 3; - spin_lock_irqsave(&rme32->lock, flags); + spin_lock_irq(&rme32->lock); ucontrol->value.enumerated.item[0] = snd_rme32_getinputtype(rme32); switch (rme32->pci->device) { @@ -1658,15 +1699,14 @@ snd_rme32_get_inputtype_control(snd_kcon ucontrol->value.enumerated.item[0] = items - 1; } - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); return 0; } static int snd_rme32_put_inputtype_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme32_t *rme32 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme32_t *rme32 = snd_kcontrol_chip(kcontrol); unsigned int val; int change, items = 3; @@ -1684,10 +1724,10 @@ snd_rme32_put_inputtype_control(snd_kcon } val = ucontrol->value.enumerated.item[0] % items; - spin_lock_irqsave(&rme32->lock, flags); + spin_lock_irq(&rme32->lock); change = val != (unsigned int)snd_rme32_getinputtype(rme32); snd_rme32_setinputtype(rme32, val); - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); return change; } @@ -1714,28 +1754,26 @@ static int snd_rme32_get_clockmode_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme32_t *rme32 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme32_t *rme32 = snd_kcontrol_chip(kcontrol); - spin_lock_irqsave(&rme32->lock, flags); + spin_lock_irq(&rme32->lock); ucontrol->value.enumerated.item[0] = snd_rme32_getclockmode(rme32); - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); return 0; } static int snd_rme32_put_clockmode_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme32_t *rme32 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme32_t *rme32 = snd_kcontrol_chip(kcontrol); unsigned int val; int change; val = ucontrol->value.enumerated.item[0] % 3; - spin_lock_irqsave(&rme32->lock, flags); + spin_lock_irq(&rme32->lock); change = val != (unsigned int)snd_rme32_getclockmode(rme32); snd_rme32_setclockmode(rme32, val); - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); return change; } @@ -1770,7 +1808,7 @@ static int snd_rme32_control_spdif_info( static int snd_rme32_control_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme32_t *rme32 = _snd_kcontrol_chip(kcontrol); + rme32_t *rme32 = snd_kcontrol_chip(kcontrol); snd_rme32_convert_to_aes(&ucontrol->value.iec958, rme32->wcreg_spdif); @@ -1780,16 +1818,15 @@ static int snd_rme32_control_spdif_get(s static int snd_rme32_control_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme32_t *rme32 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme32_t *rme32 = snd_kcontrol_chip(kcontrol); int change; u32 val; val = snd_rme32_convert_from_aes(&ucontrol->value.iec958); - spin_lock_irqsave(&rme32->lock, flags); + spin_lock_irq(&rme32->lock); change = val != rme32->wcreg_spdif; rme32->wcreg_spdif = val; - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); return change; } @@ -1805,7 +1842,7 @@ static int snd_rme32_control_spdif_strea snd_ctl_elem_value_t * ucontrol) { - rme32_t *rme32 = _snd_kcontrol_chip(kcontrol); + rme32_t *rme32 = snd_kcontrol_chip(kcontrol); snd_rme32_convert_to_aes(&ucontrol->value.iec958, rme32->wcreg_spdif_stream); @@ -1816,18 +1853,17 @@ static int snd_rme32_control_spdif_strea snd_ctl_elem_value_t * ucontrol) { - rme32_t *rme32 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme32_t *rme32 = snd_kcontrol_chip(kcontrol); int change; u32 val; val = snd_rme32_convert_from_aes(&ucontrol->value.iec958); - spin_lock_irqsave(&rme32->lock, flags); + spin_lock_irq(&rme32->lock); change = val != rme32->wcreg_spdif_stream; rme32->wcreg_spdif_stream = val; rme32->wcreg &= ~(RME32_WCR_PRO | RME32_WCR_EMP); writel(rme32->wcreg |= val, rme32->iobase + RME32_IO_CONTROL_REGISTER); - spin_unlock_irqrestore(&rme32->lock, flags); + spin_unlock_irq(&rme32->lock); return change; } @@ -1907,7 +1943,7 @@ static int snd_rme32_create_switches(snd int idx, err; snd_kcontrol_t *kctl; - for (idx = 0; idx < 7; idx++) { + for (idx = 0; idx < (int)ARRAY_SIZE(snd_rme32_controls); idx++) { if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme32_controls[idx], rme32))) < 0) return err; if (idx == 1) /* IEC958 (S/PDIF) Stream */ @@ -1952,6 +1988,8 @@ snd_rme32_probe(struct pci_dev *pci, con rme32->card = card; rme32->pci = pci; snd_card_set_dev(card, &pci->dev); + if (fullduplex[dev]) + rme32->fullduplex_mode = 1; if ((err = snd_rme32_create(rme32)) < 0) { snd_card_free(card); return err; --- linux-2.6.9-rc1/sound/pci/rme9652/hdsp.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/sound/pci/rme9652/hdsp.c 2004-09-02 20:51:53.000000000 -0700 @@ -52,24 +52,18 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for RME Hammerfall DSP interface."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for RME Hammerfall DSP interface."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable/disable specific Hammerfall DSP soundcards."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(precise_ptr, bool, boot_devs, 0444); MODULE_PARM_DESC(precise_ptr, "Enable precise pointer (doesn't work reliably)."); -MODULE_PARM_SYNTAX(precise_ptr, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); module_param_array(line_outs_monitor, bool, boot_devs, 0444); MODULE_PARM_DESC(line_outs_monitor, "Send all input and playback streams to line outs by default."); -MODULE_PARM_SYNTAX(line_outs_monitor, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); MODULE_AUTHOR("Paul Davis , Marcus Andersson, Thomas Charbonnel "); MODULE_DESCRIPTION("RME Hammerfall DSP"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{RME Hammerfall-DSP}," +MODULE_SUPPORTED_DEVICE("{{RME Hammerfall-DSP}," "{RME HDSP-9652}," "{RME HDSP-9632}}"); @@ -462,12 +456,12 @@ struct _hdsp { unsigned char qs_out_channels; unsigned char ds_out_channels; unsigned char ss_out_channels; - void *capture_buffer_unaligned; /* original buffer addresses */ - void *playback_buffer_unaligned; /* original buffer addresses */ + + struct snd_dma_buffer capture_dma_buf; + struct snd_dma_buffer playback_dma_buf; unsigned char *capture_buffer; /* suitably aligned address */ unsigned char *playback_buffer; /* suitably aligned address */ - dma_addr_t capture_buffer_addr; - dma_addr_t playback_buffer_addr; + pid_t capture_pid; pid_t playback_pid; int running; @@ -477,7 +471,6 @@ struct _hdsp { int dev; int irq; unsigned long port; - struct resource *res_port; unsigned long iobase; snd_card_t *card; snd_pcm_t *pcm; @@ -561,50 +554,24 @@ static char channel_map_H9632_qs[HDSP_MA -1, -1 }; -#define HDSP_PREALLOCATE_MEMORY /* via module snd-hdsp_mem */ - -#ifdef HDSP_PREALLOCATE_MEMORY -static void *snd_hammerfall_get_buffer(struct pci_dev *pci, size_t size, dma_addr_t *addrp, int capture) +static int snd_hammerfall_get_buffer(struct pci_dev *pci, struct snd_dma_buffer *dmab, size_t size) { - struct snd_dma_device pdev; - struct snd_dma_buffer dmbuf; - - memset(&pdev, 0, sizeof(pdev)); - pdev.type = SNDRV_DMA_TYPE_DEV; - pdev.dev = snd_dma_pci_data(pci); - pdev.id = capture; - dmbuf.bytes = 0; - if (! snd_dma_get_reserved(&pdev, &dmbuf)) { - if (snd_dma_alloc_pages(&pdev, size, &dmbuf) < 0) - return NULL; - snd_dma_set_reserved(&pdev, &dmbuf); + dmab->dev.type = SNDRV_DMA_TYPE_DEV; + dmab->dev.dev = snd_dma_pci_data(pci); + if (! snd_dma_get_reserved_buf(dmab, snd_dma_pci_buf_id(pci))) { + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), + size, dmab) < 0) + return -ENOMEM; } - *addrp = dmbuf.addr; - return dmbuf.area; + return 0; } -static void snd_hammerfall_free_buffer(struct pci_dev *pci, size_t size, void *ptr, dma_addr_t addr, int capture) +static void snd_hammerfall_free_buffer(struct snd_dma_buffer *dmab, struct pci_dev *pci) { - struct snd_dma_device pdev; - - memset(&pdev, 0, sizeof(pdev)); - pdev.type = SNDRV_DMA_TYPE_DEV; - pdev.dev = snd_dma_pci_data(pci); - pdev.id = capture; - snd_dma_free_reserved(&pdev); + if (dmab->area) + snd_dma_reserve_buf(dmab, snd_dma_pci_buf_id(pci)); } -#else -static void *snd_hammerfall_get_buffer(struct pci_dev *pci, size_t size, dma_addr_t *addrp, int capture) -{ - return snd_malloc_pci_pages(pci, size, addrp); -} - -static void snd_hammerfall_free_buffer(struct pci_dev *pci, size_t size, void *ptr, dma_addr_t addr, int capture) -{ - snd_free_pci_pages(pci, size, ptr, addr); -} -#endif static struct pci_device_id snd_hdsp_ids[] = { { @@ -621,15 +588,15 @@ MODULE_DEVICE_TABLE(pci, snd_hdsp_ids); /* prototypes */ static int __devinit snd_hdsp_create_alsa_devices(snd_card_t *card, hdsp_t *hdsp); static int __devinit snd_hdsp_create_pcm(snd_card_t *card, hdsp_t *hdsp); -static inline int snd_hdsp_enable_io (hdsp_t *hdsp); -static inline void snd_hdsp_initialize_midi_flush (hdsp_t *hdsp); -static inline void snd_hdsp_initialize_channels (hdsp_t *hdsp); -static inline int hdsp_fifo_wait(hdsp_t *hdsp, int count, int timeout); +static int snd_hdsp_enable_io (hdsp_t *hdsp); +static void snd_hdsp_initialize_midi_flush (hdsp_t *hdsp); +static void snd_hdsp_initialize_channels (hdsp_t *hdsp); +static int hdsp_fifo_wait(hdsp_t *hdsp, int count, int timeout); static int hdsp_autosync_ref(hdsp_t *hdsp); static int snd_hdsp_set_defaults(hdsp_t *hdsp); -static inline void snd_hdsp_9652_enable_mixer (hdsp_t *hdsp); +static void snd_hdsp_9652_enable_mixer (hdsp_t *hdsp); -static inline int hdsp_playback_to_output_key (hdsp_t *hdsp, int in, int out) +static int hdsp_playback_to_output_key (hdsp_t *hdsp, int in, int out) { switch (hdsp->firmware_rev) { case 0xa: @@ -642,7 +609,7 @@ static inline int hdsp_playback_to_outpu } } -static inline int hdsp_input_to_output_key (hdsp_t *hdsp, int in, int out) +static int hdsp_input_to_output_key (hdsp_t *hdsp, int in, int out) { switch (hdsp->firmware_rev) { case 0xa: @@ -655,17 +622,17 @@ static inline int hdsp_input_to_output_k } } -static inline void hdsp_write(hdsp_t *hdsp, int reg, int val) +static void hdsp_write(hdsp_t *hdsp, int reg, int val) { writel(val, hdsp->iobase + reg); } -static inline unsigned int hdsp_read(hdsp_t *hdsp, int reg) +static unsigned int hdsp_read(hdsp_t *hdsp, int reg) { return readl (hdsp->iobase + reg); } -static inline int hdsp_check_for_iobox (hdsp_t *hdsp) +static int hdsp_check_for_iobox (hdsp_t *hdsp) { if (hdsp->io_type == H9652 || hdsp->io_type == H9632) return 0; @@ -738,7 +705,7 @@ static int snd_hdsp_load_firmware_from_c return 0; } -static inline int hdsp_get_iobox_version (hdsp_t *hdsp) +static int hdsp_get_iobox_version (hdsp_t *hdsp) { int err; @@ -781,7 +748,7 @@ static inline int hdsp_get_iobox_version } -static inline int hdsp_check_for_firmware (hdsp_t *hdsp) +static int hdsp_check_for_firmware (hdsp_t *hdsp) { if (hdsp->io_type == H9652 || hdsp->io_type == H9632) return 0; if ((hdsp_read (hdsp, HDSP_statusRegister) & HDSP_DllError) != 0) { @@ -793,7 +760,7 @@ static inline int hdsp_check_for_firmwar } -static inline int hdsp_fifo_wait(hdsp_t *hdsp, int count, int timeout) +static int hdsp_fifo_wait(hdsp_t *hdsp, int count, int timeout) { int i; @@ -818,7 +785,7 @@ static inline int hdsp_fifo_wait(hdsp_t return -1; } -static inline int hdsp_read_gain (hdsp_t *hdsp, unsigned int addr) +static int hdsp_read_gain (hdsp_t *hdsp, unsigned int addr) { if (addr >= HDSP_MATRIX_MIXER_SIZE) { return 0; @@ -826,7 +793,7 @@ static inline int hdsp_read_gain (hdsp_t return hdsp->mixer_matrix[addr]; } -static inline int hdsp_write_gain(hdsp_t *hdsp, unsigned int addr, unsigned short data) +static int hdsp_write_gain(hdsp_t *hdsp, unsigned int addr, unsigned short data) { unsigned int ad; @@ -835,7 +802,7 @@ static inline int hdsp_write_gain(hdsp_t if (hdsp->io_type == H9652 || hdsp->io_type == H9632) { - /* from martin björnsen: + /* from martin björnsen: "You can only write dwords to the mixer memory which contain two @@ -889,7 +856,7 @@ static inline int hdsp_write_gain(hdsp_t return 0; } -static inline int snd_hdsp_use_is_exclusive(hdsp_t *hdsp) +static int snd_hdsp_use_is_exclusive(hdsp_t *hdsp) { unsigned long flags; int ret = 1; @@ -903,7 +870,7 @@ static inline int snd_hdsp_use_is_exclus return ret; } -static inline int hdsp_external_sample_rate (hdsp_t *hdsp) +static int hdsp_external_sample_rate (hdsp_t *hdsp) { unsigned int status2 = hdsp_read(hdsp, HDSP_status2Register); unsigned int rate_bits = status2 & HDSP_systemFrequencyMask; @@ -920,7 +887,7 @@ static inline int hdsp_external_sample_r } } -static inline int hdsp_spdif_sample_rate(hdsp_t *hdsp) +static int hdsp_spdif_sample_rate(hdsp_t *hdsp) { unsigned int status = hdsp_read(hdsp, HDSP_statusRegister); unsigned int rate_bits = (status & HDSP_spdifFrequencyMask); @@ -952,7 +919,7 @@ static inline int hdsp_spdif_sample_rate return 0; } -static inline void hdsp_compute_period_size(hdsp_t *hdsp) +static void hdsp_compute_period_size(hdsp_t *hdsp) { hdsp->period_bytes = 1 << ((hdsp_decode_latency(hdsp->control_register) + 8)); } @@ -974,24 +941,24 @@ static snd_pcm_uframes_t hdsp_hw_pointer return position; } -static inline void hdsp_reset_hw_pointer(hdsp_t *hdsp) +static void hdsp_reset_hw_pointer(hdsp_t *hdsp) { hdsp_write (hdsp, HDSP_resetPointer, 0); } -static inline void hdsp_start_audio(hdsp_t *s) +static void hdsp_start_audio(hdsp_t *s) { s->control_register |= (HDSP_AudioInterruptEnable | HDSP_Start); hdsp_write(s, HDSP_controlRegister, s->control_register); } -static inline void hdsp_stop_audio(hdsp_t *s) +static void hdsp_stop_audio(hdsp_t *s) { s->control_register &= ~(HDSP_Start | HDSP_AudioInterruptEnable); hdsp_write(s, HDSP_controlRegister, s->control_register); } -static inline void hdsp_silence_playback(hdsp_t *hdsp) +static void hdsp_silence_playback(hdsp_t *hdsp) { memset(hdsp->playback_buffer, 0, HDSP_DMA_AREA_BYTES); } @@ -1236,7 +1203,7 @@ static int hdsp_set_passthru(hdsp_t *hds MIDI ----------------------------------------------------------------------------*/ -static inline unsigned char snd_hdsp_midi_read_byte (hdsp_t *hdsp, int id) +static unsigned char snd_hdsp_midi_read_byte (hdsp_t *hdsp, int id) { /* the hardware already does the relevant bit-mask with 0xff */ if (id) { @@ -1246,7 +1213,7 @@ static inline unsigned char snd_hdsp_mid } } -static inline void snd_hdsp_midi_write_byte (hdsp_t *hdsp, int id, int val) +static void snd_hdsp_midi_write_byte (hdsp_t *hdsp, int id, int val) { /* the hardware already does the relevant bit-mask with 0xff */ if (id) { @@ -1256,7 +1223,7 @@ static inline void snd_hdsp_midi_write_b } } -static inline int snd_hdsp_midi_input_available (hdsp_t *hdsp, int id) +static int snd_hdsp_midi_input_available (hdsp_t *hdsp, int id) { if (id) { return (hdsp_read(hdsp, HDSP_midiStatusIn1) & 0xff); @@ -1265,7 +1232,7 @@ static inline int snd_hdsp_midi_input_av } } -static inline int snd_hdsp_midi_output_possible (hdsp_t *hdsp, int id) +static int snd_hdsp_midi_output_possible (hdsp_t *hdsp, int id) { int fifo_bytes_used; @@ -1282,7 +1249,7 @@ static inline int snd_hdsp_midi_output_p } } -static inline void snd_hdsp_flush_midi_input (hdsp_t *hdsp, int id) +static void snd_hdsp_flush_midi_input (hdsp_t *hdsp, int id) { while (snd_hdsp_midi_input_available (hdsp, id)) { snd_hdsp_midi_read_byte (hdsp, id); @@ -1429,13 +1396,12 @@ static void snd_hdsp_midi_output_trigger static int snd_hdsp_midi_input_open(snd_rawmidi_substream_t * substream) { hdsp_midi_t *hmidi; - unsigned long flags; hmidi = (hdsp_midi_t *) substream->rmidi->private_data; - spin_lock_irqsave (&hmidi->lock, flags); + spin_lock_irq (&hmidi->lock); snd_hdsp_flush_midi_input (hmidi->hdsp, hmidi->id); hmidi->input = substream; - spin_unlock_irqrestore (&hmidi->lock, flags); + spin_unlock_irq (&hmidi->lock); return 0; } @@ -1443,12 +1409,11 @@ static int snd_hdsp_midi_input_open(snd_ static int snd_hdsp_midi_output_open(snd_rawmidi_substream_t * substream) { hdsp_midi_t *hmidi; - unsigned long flags; hmidi = (hdsp_midi_t *) substream->rmidi->private_data; - spin_lock_irqsave (&hmidi->lock, flags); + spin_lock_irq (&hmidi->lock); hmidi->output = substream; - spin_unlock_irqrestore (&hmidi->lock, flags); + spin_unlock_irq (&hmidi->lock); return 0; } @@ -1456,14 +1421,13 @@ static int snd_hdsp_midi_output_open(snd static int snd_hdsp_midi_input_close(snd_rawmidi_substream_t * substream) { hdsp_midi_t *hmidi; - unsigned long flags; snd_hdsp_midi_input_trigger (substream, 0); hmidi = (hdsp_midi_t *) substream->rmidi->private_data; - spin_lock_irqsave (&hmidi->lock, flags); + spin_lock_irq (&hmidi->lock); hmidi->input = NULL; - spin_unlock_irqrestore (&hmidi->lock, flags); + spin_unlock_irq (&hmidi->lock); return 0; } @@ -1471,14 +1435,13 @@ static int snd_hdsp_midi_input_close(snd static int snd_hdsp_midi_output_close(snd_rawmidi_substream_t * substream) { hdsp_midi_t *hmidi; - unsigned long flags; snd_hdsp_midi_output_trigger (substream, 0); hmidi = (hdsp_midi_t *) substream->rmidi->private_data; - spin_lock_irqsave (&hmidi->lock, flags); + spin_lock_irq (&hmidi->lock); hmidi->output = NULL; - spin_unlock_irqrestore (&hmidi->lock, flags); + spin_unlock_irq (&hmidi->lock); return 0; } @@ -1563,7 +1526,7 @@ static int snd_hdsp_control_spdif_info(s static int snd_hdsp_control_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); snd_hdsp_convert_to_aes(&ucontrol->value.iec958, hdsp->creg_spdif); return 0; @@ -1571,16 +1534,15 @@ static int snd_hdsp_control_spdif_get(sn static int snd_hdsp_control_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); int change; u32 val; val = snd_hdsp_convert_from_aes(&ucontrol->value.iec958); - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); change = val != hdsp->creg_spdif; hdsp->creg_spdif = val; - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); return change; } @@ -1593,7 +1555,7 @@ static int snd_hdsp_control_spdif_stream static int snd_hdsp_control_spdif_stream_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); snd_hdsp_convert_to_aes(&ucontrol->value.iec958, hdsp->creg_spdif_stream); return 0; @@ -1601,18 +1563,17 @@ static int snd_hdsp_control_spdif_stream static int snd_hdsp_control_spdif_stream_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); int change; u32 val; val = snd_hdsp_convert_from_aes(&ucontrol->value.iec958); - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); change = val != hdsp->creg_spdif_stream; hdsp->creg_spdif_stream = val; hdsp->control_register &= ~(HDSP_SPDIFProfessional | HDSP_SPDIFNonAudio | HDSP_SPDIFEmphasis); hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register |= val); - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); return change; } @@ -1653,7 +1614,7 @@ static int hdsp_set_spdif_input(hdsp_t * static int snd_hdsp_info_spdif_in(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { static char *texts[4] = {"Optical", "Coaxial", "Internal", "AES"}; - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; @@ -1666,7 +1627,7 @@ static int snd_hdsp_info_spdif_in(snd_kc static int snd_hdsp_get_spdif_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); ucontrol->value.enumerated.item[0] = hdsp_spdif_in(hdsp); return 0; @@ -1674,19 +1635,18 @@ static int snd_hdsp_get_spdif_in(snd_kco static int snd_hdsp_put_spdif_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); int change; unsigned int val; if (!snd_hdsp_use_is_exclusive(hdsp)) return -EBUSY; val = ucontrol->value.enumerated.item[0] % ((hdsp->io_type == H9632) ? 4 : 3); - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); change = val != hdsp_spdif_in(hdsp); if (change) hdsp_set_spdif_input(hdsp, val); - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); return change; } @@ -1722,7 +1682,7 @@ static int snd_hdsp_info_spdif_bits(snd_ static int snd_hdsp_get_spdif_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = hdsp_spdif_out(hdsp); return 0; @@ -1730,18 +1690,17 @@ static int snd_hdsp_get_spdif_out(snd_kc static int snd_hdsp_put_spdif_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); int change; unsigned int val; if (!snd_hdsp_use_is_exclusive(hdsp)) return -EBUSY; val = ucontrol->value.integer.value[0] & 1; - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); change = (int)val != hdsp_spdif_out(hdsp); hdsp_set_spdif_output(hdsp, val); - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); return change; } @@ -1768,7 +1727,7 @@ static int hdsp_set_spdif_professional(h static int snd_hdsp_get_spdif_professional(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = hdsp_spdif_professional(hdsp); return 0; @@ -1776,18 +1735,17 @@ static int snd_hdsp_get_spdif_profession static int snd_hdsp_put_spdif_professional(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); int change; unsigned int val; if (!snd_hdsp_use_is_exclusive(hdsp)) return -EBUSY; val = ucontrol->value.integer.value[0] & 1; - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); change = (int)val != hdsp_spdif_professional(hdsp); hdsp_set_spdif_professional(hdsp, val); - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); return change; } @@ -1814,7 +1772,7 @@ static int hdsp_set_spdif_emphasis(hdsp_ static int snd_hdsp_get_spdif_emphasis(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = hdsp_spdif_emphasis(hdsp); return 0; @@ -1822,18 +1780,17 @@ static int snd_hdsp_get_spdif_emphasis(s static int snd_hdsp_put_spdif_emphasis(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); int change; unsigned int val; if (!snd_hdsp_use_is_exclusive(hdsp)) return -EBUSY; val = ucontrol->value.integer.value[0] & 1; - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); change = (int)val != hdsp_spdif_emphasis(hdsp); hdsp_set_spdif_emphasis(hdsp, val); - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); return change; } @@ -1860,7 +1817,7 @@ static int hdsp_set_spdif_nonaudio(hdsp_ static int snd_hdsp_get_spdif_nonaudio(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = hdsp_spdif_nonaudio(hdsp); return 0; @@ -1868,18 +1825,17 @@ static int snd_hdsp_get_spdif_nonaudio(s static int snd_hdsp_put_spdif_nonaudio(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); int change; unsigned int val; if (!snd_hdsp_use_is_exclusive(hdsp)) return -EBUSY; val = ucontrol->value.integer.value[0] & 1; - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); change = (int)val != hdsp_spdif_nonaudio(hdsp); hdsp_set_spdif_nonaudio(hdsp, val); - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); return change; } @@ -1895,7 +1851,7 @@ static int snd_hdsp_put_spdif_nonaudio(s static int snd_hdsp_info_spdif_sample_rate(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { static char *texts[] = {"32000", "44100", "48000", "64000", "88200", "96000", "None", "128000", "176400", "192000"}; - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; @@ -1908,7 +1864,7 @@ static int snd_hdsp_info_spdif_sample_ra static int snd_hdsp_get_spdif_sample_rate(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); switch (hdsp_spdif_sample_rate(hdsp)) { case 32000: @@ -1962,7 +1918,7 @@ static int snd_hdsp_info_system_sample_r static int snd_hdsp_get_system_sample_rate(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); ucontrol->value.enumerated.item[0] = hdsp->system_sample_rate; return 0; @@ -1979,7 +1935,7 @@ static int snd_hdsp_get_system_sample_ra static int snd_hdsp_info_autosync_sample_rate(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); static char *texts[] = {"32000", "44100", "48000", "64000", "88200", "96000", "None", "128000", "176400", "192000"}; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; @@ -1992,7 +1948,7 @@ static int snd_hdsp_info_autosync_sample static int snd_hdsp_get_autosync_sample_rate(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); switch (hdsp_external_sample_rate(hdsp)) { case 32000: @@ -2062,7 +2018,7 @@ static int snd_hdsp_info_system_clock_mo static int snd_hdsp_get_system_clock_mode(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); ucontrol->value.enumerated.item[0] = hdsp_system_clock_mode(hdsp); return 0; @@ -2159,7 +2115,7 @@ static int hdsp_set_clock_source(hdsp_t static int snd_hdsp_info_clock_source(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { static char *texts[] = {"AutoSync", "Internal 32.0 kHz", "Internal 44.1 kHz", "Internal 48.0 kHz", "Internal 64.0 kHz", "Internal 88.2 kHz", "Internal 96.0 kHz", "Internal 128 kHz", "Internal 176.4 kHz", "Internal 192.0 KHz" }; - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; @@ -2175,7 +2131,7 @@ static int snd_hdsp_info_clock_source(sn static int snd_hdsp_get_clock_source(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); ucontrol->value.enumerated.item[0] = hdsp_clock_source(hdsp); return 0; @@ -2183,8 +2139,7 @@ static int snd_hdsp_get_clock_source(snd static int snd_hdsp_put_clock_source(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); int change; int val; @@ -2197,13 +2152,13 @@ static int snd_hdsp_put_clock_source(snd } else { if (val > 6) val = 6; } - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); if (val != hdsp_clock_source(hdsp)) { change = (hdsp_set_clock_source(hdsp, val) == 0) ? 1 : 0; } else { change = 0; } - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); return change; } @@ -2266,7 +2221,7 @@ static int snd_hdsp_info_da_gain(snd_kco static int snd_hdsp_get_da_gain(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); ucontrol->value.enumerated.item[0] = hdsp_da_gain(hdsp); return 0; @@ -2274,8 +2229,7 @@ static int snd_hdsp_get_da_gain(snd_kcon static int snd_hdsp_put_da_gain(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); int change; int val; @@ -2284,13 +2238,13 @@ static int snd_hdsp_put_da_gain(snd_kcon val = ucontrol->value.enumerated.item[0]; if (val < 0) val = 0; if (val > 2) val = 2; - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); if (val != hdsp_da_gain(hdsp)) { change = (hdsp_set_da_gain(hdsp, val) == 0) ? 1 : 0; } else { change = 0; } - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); return change; } @@ -2353,7 +2307,7 @@ static int snd_hdsp_info_ad_gain(snd_kco static int snd_hdsp_get_ad_gain(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); ucontrol->value.enumerated.item[0] = hdsp_ad_gain(hdsp); return 0; @@ -2361,8 +2315,7 @@ static int snd_hdsp_get_ad_gain(snd_kcon static int snd_hdsp_put_ad_gain(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); int change; int val; @@ -2371,13 +2324,13 @@ static int snd_hdsp_put_ad_gain(snd_kcon val = ucontrol->value.enumerated.item[0]; if (val < 0) val = 0; if (val > 2) val = 2; - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); if (val != hdsp_ad_gain(hdsp)) { change = (hdsp_set_ad_gain(hdsp, val) == 0) ? 1 : 0; } else { change = 0; } - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); return change; } @@ -2440,7 +2393,7 @@ static int snd_hdsp_info_phone_gain(snd_ static int snd_hdsp_get_phone_gain(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); ucontrol->value.enumerated.item[0] = hdsp_phone_gain(hdsp); return 0; @@ -2448,8 +2401,7 @@ static int snd_hdsp_get_phone_gain(snd_k static int snd_hdsp_put_phone_gain(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); int change; int val; @@ -2458,13 +2410,13 @@ static int snd_hdsp_put_phone_gain(snd_k val = ucontrol->value.enumerated.item[0]; if (val < 0) val = 0; if (val > 2) val = 2; - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); if (val != hdsp_phone_gain(hdsp)) { change = (hdsp_set_phone_gain(hdsp, val) == 0) ? 1 : 0; } else { change = 0; } - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); return change; } @@ -2507,7 +2459,7 @@ static int snd_hdsp_info_xlr_breakout_ca static int snd_hdsp_get_xlr_breakout_cable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); ucontrol->value.enumerated.item[0] = hdsp_xlr_breakout_cable(hdsp); return 0; @@ -2515,18 +2467,17 @@ static int snd_hdsp_get_xlr_breakout_cab static int snd_hdsp_put_xlr_breakout_cable(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); int change; int val; if (!snd_hdsp_use_is_exclusive(hdsp)) return -EBUSY; val = ucontrol->value.integer.value[0] & 1; - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); change = (int)val != hdsp_xlr_breakout_cable(hdsp); hdsp_set_xlr_breakout_cable(hdsp, val); - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); return change; } @@ -2573,7 +2524,7 @@ static int snd_hdsp_info_aeb(snd_kcontro static int snd_hdsp_get_aeb(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); ucontrol->value.enumerated.item[0] = hdsp_aeb(hdsp); return 0; @@ -2581,18 +2532,17 @@ static int snd_hdsp_get_aeb(snd_kcontrol static int snd_hdsp_put_aeb(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); int change; int val; if (!snd_hdsp_use_is_exclusive(hdsp)) return -EBUSY; val = ucontrol->value.integer.value[0] & 1; - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); change = (int)val != hdsp_aeb(hdsp); hdsp_set_aeb(hdsp, val); - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); return change; } @@ -2662,7 +2612,7 @@ static int hdsp_set_pref_sync_ref(hdsp_t static int snd_hdsp_info_pref_sync_ref(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { static char *texts[] = {"Word", "IEC958", "ADAT1", "ADAT Sync", "ADAT2", "ADAT3" }; - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; @@ -2691,7 +2641,7 @@ static int snd_hdsp_info_pref_sync_ref(s static int snd_hdsp_get_pref_sync_ref(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); ucontrol->value.enumerated.item[0] = hdsp_pref_sync_ref(hdsp); return 0; @@ -2699,8 +2649,7 @@ static int snd_hdsp_get_pref_sync_ref(sn static int snd_hdsp_put_pref_sync_ref(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); int change, max; unsigned int val; @@ -2723,10 +2672,10 @@ static int snd_hdsp_put_pref_sync_ref(sn } val = ucontrol->value.enumerated.item[0] % max; - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); change = (int)val != hdsp_pref_sync_ref(hdsp); hdsp_set_pref_sync_ref(hdsp, val); - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); return change; } @@ -2780,7 +2729,7 @@ static int snd_hdsp_info_autosync_ref(sn static int snd_hdsp_get_autosync_ref(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); ucontrol->value.enumerated.item[0] = hdsp_pref_sync_ref(hdsp); return 0; @@ -2806,19 +2755,17 @@ static int snd_hdsp_info_passthru(snd_kc static int snd_hdsp_get_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); ucontrol->value.integer.value[0] = hdsp->passthru; - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); return 0; } static int snd_hdsp_put_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); int change; unsigned int val; int err = 0; @@ -2827,11 +2774,11 @@ static int snd_hdsp_put_passthru(snd_kco return -EBUSY; val = ucontrol->value.integer.value[0] & 1; - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); change = (ucontrol->value.integer.value[0] != hdsp->passthru); if (change) err = hdsp_set_passthru(hdsp, val); - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); return err ? err : change; } @@ -2871,29 +2818,27 @@ static int snd_hdsp_info_line_out(snd_kc static int snd_hdsp_get_line_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); ucontrol->value.integer.value[0] = hdsp_line_out(hdsp); - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); return 0; } static int snd_hdsp_put_line_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); int change; unsigned int val; if (!snd_hdsp_use_is_exclusive(hdsp)) return -EBUSY; val = ucontrol->value.integer.value[0] & 1; - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); change = (int)val != hdsp_line_out(hdsp); hdsp_set_line_output(hdsp, val); - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); return change; } @@ -2920,8 +2865,7 @@ static int snd_hdsp_info_mixer(snd_kcont static int snd_hdsp_get_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); int source; int destination; int addr; @@ -2935,16 +2879,15 @@ static int snd_hdsp_get_mixer(snd_kcontr addr = hdsp_input_to_output_key(hdsp,source, destination); } - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); ucontrol->value.integer.value[2] = hdsp_read_gain (hdsp, addr); - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); return 0; } static int snd_hdsp_put_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); int change; int source; int destination; @@ -2965,11 +2908,11 @@ static int snd_hdsp_put_mixer(snd_kcontr gain = ucontrol->value.integer.value[2]; - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); change = gain != hdsp_read_gain(hdsp, addr); if (change) hdsp_write_gain(hdsp, addr, gain); - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); return change; } @@ -3011,7 +2954,7 @@ static int hdsp_wc_sync_check(hdsp_t *hd static int snd_hdsp_get_wc_sync_check(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); ucontrol->value.enumerated.item[0] = hdsp_wc_sync_check(hdsp); return 0; @@ -3043,7 +2986,7 @@ static int hdsp_spdif_sync_check(hdsp_t static int snd_hdsp_get_spdif_sync_check(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); ucontrol->value.enumerated.item[0] = hdsp_spdif_sync_check(hdsp); return 0; @@ -3074,7 +3017,7 @@ static int hdsp_adatsync_sync_check(hdsp static int snd_hdsp_get_adatsync_sync_check(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); ucontrol->value.enumerated.item[0] = hdsp_adatsync_sync_check(hdsp); return 0; @@ -3105,7 +3048,7 @@ static int hdsp_adat_sync_check(hdsp_t * static int snd_hdsp_get_adat_sync_check(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { int offset; - hdsp_t *hdsp = _snd_kcontrol_chip(kcontrol); + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); offset = ucontrol->id.index - 1; snd_assert(offset >= 0); @@ -3194,10 +3137,6 @@ HDSP_PASSTHRU("Passthru", 0), HDSP_LINE_OUT("Line Out", 0), }; -#define HDSP_CONTROLS (sizeof(snd_hdsp_controls)/sizeof(snd_kcontrol_new_t)) - -#define HDSP_9632_CONTROLS (sizeof(snd_hdsp_9632_controls)/sizeof(snd_kcontrol_new_t)) - static snd_kcontrol_new_t snd_hdsp_96xx_aeb = HDSP_AEB("Analog Extension Board", 0); static snd_kcontrol_new_t snd_hdsp_adat_sync_check = HDSP_ADAT_SYNC_CHECK; @@ -3207,7 +3146,7 @@ int snd_hdsp_create_controls(snd_card_t int err; snd_kcontrol_t *kctl; - for (idx = 0; idx < HDSP_CONTROLS; idx++) { + for (idx = 0; idx < ARRAY_SIZE(snd_hdsp_controls); idx++) { if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_hdsp_controls[idx], hdsp))) < 0) { return err; } @@ -3232,7 +3171,7 @@ int snd_hdsp_create_controls(snd_card_t /* DA, AD and Phone gain and XLR breakout cable controls for H9632 cards */ if (hdsp->io_type == H9632) { - for (idx = 0; idx < HDSP_9632_CONTROLS; idx++) { + for (idx = 0; idx < ARRAY_SIZE(snd_hdsp_9632_controls); idx++) { if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_hdsp_9632_controls[idx], hdsp))) < 0) { return err; } @@ -3583,59 +3522,34 @@ static void __devinit snd_hdsp_proc_init static void snd_hdsp_free_buffers(hdsp_t *hdsp) { - if (hdsp->capture_buffer_unaligned) { - snd_hammerfall_free_buffer(hdsp->pci, HDSP_DMA_AREA_BYTES, - hdsp->capture_buffer_unaligned, - hdsp->capture_buffer_addr, 1); - } - - if (hdsp->playback_buffer_unaligned) { - snd_hammerfall_free_buffer(hdsp->pci, HDSP_DMA_AREA_BYTES, - hdsp->playback_buffer_unaligned, - hdsp->playback_buffer_addr, 0); - } + snd_hammerfall_free_buffer(&hdsp->capture_dma_buf, hdsp->pci); + snd_hammerfall_free_buffer(&hdsp->playback_dma_buf, hdsp->pci); } static int __devinit snd_hdsp_initialize_memory(hdsp_t *hdsp) { - void *pb, *cb; - dma_addr_t pb_addr, cb_addr; unsigned long pb_bus, cb_bus; - cb = snd_hammerfall_get_buffer(hdsp->pci, HDSP_DMA_AREA_BYTES, &cb_addr, 1); - pb = snd_hammerfall_get_buffer(hdsp->pci, HDSP_DMA_AREA_BYTES, &pb_addr, 0); - - if (cb == 0 || pb == 0) { - if (cb) { - snd_hammerfall_free_buffer(hdsp->pci, HDSP_DMA_AREA_BYTES, cb, cb_addr, 1); - } - if (pb) { - snd_hammerfall_free_buffer(hdsp->pci, HDSP_DMA_AREA_BYTES, pb, pb_addr, 0); - } - + if (snd_hammerfall_get_buffer(hdsp->pci, &hdsp->capture_dma_buf, HDSP_DMA_AREA_BYTES) < 0 || + snd_hammerfall_get_buffer(hdsp->pci, &hdsp->playback_dma_buf, HDSP_DMA_AREA_BYTES) < 0) { + if (hdsp->capture_dma_buf.area) + snd_dma_free_pages(&hdsp->capture_dma_buf); printk(KERN_ERR "%s: no buffers available\n", hdsp->card_name); return -ENOMEM; } - /* save raw addresses for use when freeing memory later */ - - hdsp->capture_buffer_unaligned = cb; - hdsp->playback_buffer_unaligned = pb; - hdsp->capture_buffer_addr = cb_addr; - hdsp->playback_buffer_addr = pb_addr; - /* Align to bus-space 64K boundary */ - cb_bus = (cb_addr + 0xFFFF) & ~0xFFFFl; - pb_bus = (pb_addr + 0xFFFF) & ~0xFFFFl; + cb_bus = (hdsp->capture_dma_buf.addr + 0xFFFF) & ~0xFFFFl; + pb_bus = (hdsp->playback_dma_buf.addr + 0xFFFF) & ~0xFFFFl; /* Tell the card where it is */ hdsp_write(hdsp, HDSP_inputBufferAddress, cb_bus); hdsp_write(hdsp, HDSP_outputBufferAddress, pb_bus); - hdsp->capture_buffer = cb + (cb_bus - cb_addr); - hdsp->playback_buffer = pb + (pb_bus - pb_addr); + hdsp->capture_buffer = hdsp->capture_dma_buf.area + (cb_bus - hdsp->capture_dma_buf.addr); + hdsp->playback_buffer = hdsp->playback_dma_buf.area + (pb_bus - hdsp->playback_dma_buf.addr); return 0; } @@ -3809,7 +3723,7 @@ static irqreturn_t snd_hdsp_interrupt(in static snd_pcm_uframes_t snd_hdsp_hw_pointer(snd_pcm_substream_t *substream) { - hdsp_t *hdsp = _snd_pcm_substream_chip(substream); + hdsp_t *hdsp = snd_pcm_substream_chip(substream); return hdsp_hw_pointer(hdsp); } @@ -3836,7 +3750,7 @@ static char *hdsp_channel_buffer_locatio static int snd_hdsp_playback_copy(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count) { - hdsp_t *hdsp = _snd_pcm_substream_chip(substream); + hdsp_t *hdsp = snd_pcm_substream_chip(substream); char *channel_buf; snd_assert(pos + count <= HDSP_CHANNEL_BUFFER_BYTES / 4, return -EINVAL); @@ -3851,7 +3765,7 @@ static int snd_hdsp_playback_copy(snd_pc static int snd_hdsp_capture_copy(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t pos, void __user *dst, snd_pcm_uframes_t count) { - hdsp_t *hdsp = _snd_pcm_substream_chip(substream); + hdsp_t *hdsp = snd_pcm_substream_chip(substream); char *channel_buf; snd_assert(pos + count <= HDSP_CHANNEL_BUFFER_BYTES / 4, return -EINVAL); @@ -3866,7 +3780,7 @@ static int snd_hdsp_capture_copy(snd_pcm static int snd_hdsp_hw_silence(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count) { - hdsp_t *hdsp = _snd_pcm_substream_chip(substream); + hdsp_t *hdsp = snd_pcm_substream_chip(substream); char *channel_buf; channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel); @@ -3878,7 +3792,7 @@ static int snd_hdsp_hw_silence(snd_pcm_s static int snd_hdsp_reset(snd_pcm_substream_t *substream) { snd_pcm_runtime_t *runtime = substream->runtime; - hdsp_t *hdsp = _snd_pcm_substream_chip(substream); + hdsp_t *hdsp = snd_pcm_substream_chip(substream); snd_pcm_substream_t *other; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) other = hdsp->capture_substream; @@ -3906,7 +3820,7 @@ static int snd_hdsp_reset(snd_pcm_substr static int snd_hdsp_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params) { - hdsp_t *hdsp = _snd_pcm_substream_chip(substream); + hdsp_t *hdsp = snd_pcm_substream_chip(substream); int err; pid_t this_pid; pid_t other_pid; @@ -3989,7 +3903,7 @@ static int snd_hdsp_hw_params(snd_pcm_su static int snd_hdsp_channel_info(snd_pcm_substream_t *substream, snd_pcm_channel_info_t *info) { - hdsp_t *hdsp = _snd_pcm_substream_chip(substream); + hdsp_t *hdsp = snd_pcm_substream_chip(substream); int mapped_channel; snd_assert(info->channel < hdsp->max_channels, return -EINVAL); @@ -4026,7 +3940,7 @@ static int snd_hdsp_ioctl(snd_pcm_substr static int snd_hdsp_trigger(snd_pcm_substream_t *substream, int cmd) { - hdsp_t *hdsp = _snd_pcm_substream_chip(substream); + hdsp_t *hdsp = snd_pcm_substream_chip(substream); snd_pcm_substream_t *other; int running; @@ -4105,7 +4019,7 @@ static int snd_hdsp_trigger(snd_pcm_subs static int snd_hdsp_prepare(snd_pcm_substream_t *substream) { - hdsp_t *hdsp = _snd_pcm_substream_chip(substream); + hdsp_t *hdsp = snd_pcm_substream_chip(substream); int result = 0; if (hdsp_check_for_iobox (hdsp)) { @@ -4123,10 +4037,10 @@ static int snd_hdsp_prepare(snd_pcm_subs return -EIO; } - spin_lock(&hdsp->lock); + spin_lock_irq(&hdsp->lock); if (!hdsp->running) hdsp_reset_hw_pointer(hdsp); - spin_unlock(&hdsp->lock); + spin_unlock_irq(&hdsp->lock); return result; } @@ -4183,20 +4097,16 @@ static snd_pcm_hardware_t snd_hdsp_captu static unsigned int hdsp_period_sizes[] = { 64, 128, 256, 512, 1024, 2048, 4096, 8192 }; -#define HDSP_PERIOD_SIZES sizeof(hdsp_period_sizes) / sizeof(hdsp_period_sizes[0]) - static snd_pcm_hw_constraint_list_t hdsp_hw_constraints_period_sizes = { - .count = HDSP_PERIOD_SIZES, + .count = ARRAY_SIZE(hdsp_period_sizes), .list = hdsp_period_sizes, .mask = 0 }; static unsigned int hdsp_9632_sample_rates[] = { 32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000 }; -#define HDSP_9632_SAMPLE_RATES sizeof(hdsp_9632_sample_rates) / sizeof(hdsp_9632_sample_rates[0]) - static snd_pcm_hw_constraint_list_t hdsp_hw_constraints_9632_sample_rates = { - .count = HDSP_9632_SAMPLE_RATES, + .count = ARRAY_SIZE(hdsp_9632_sample_rates), .list = hdsp_9632_sample_rates, .mask = 0 }; @@ -4364,8 +4274,7 @@ static int snd_hdsp_hw_rule_rate_in_chan static int snd_hdsp_playback_open(snd_pcm_substream_t *substream) { - hdsp_t *hdsp = _snd_pcm_substream_chip(substream); - unsigned long flags; + hdsp_t *hdsp = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; if (hdsp_check_for_iobox (hdsp)) { @@ -4383,7 +4292,7 @@ static int snd_hdsp_playback_open(snd_pc return -EIO; } - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); snd_pcm_set_sync(substream); @@ -4399,7 +4308,7 @@ static int snd_hdsp_playback_open(snd_pc hdsp->playback_pid = current->pid; hdsp->playback_substream = substream; - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hdsp_hw_constraints_period_sizes); @@ -4430,15 +4339,14 @@ static int snd_hdsp_playback_open(snd_pc static int snd_hdsp_playback_release(snd_pcm_substream_t *substream) { - hdsp_t *hdsp = _snd_pcm_substream_chip(substream); - unsigned long flags; + hdsp_t *hdsp = snd_pcm_substream_chip(substream); - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); hdsp->playback_pid = -1; hdsp->playback_substream = NULL; - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); hdsp->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; snd_ctl_notify(hdsp->card, SNDRV_CTL_EVENT_MASK_VALUE | @@ -4449,8 +4357,7 @@ static int snd_hdsp_playback_release(snd static int snd_hdsp_capture_open(snd_pcm_substream_t *substream) { - hdsp_t *hdsp = _snd_pcm_substream_chip(substream); - unsigned long flags; + hdsp_t *hdsp = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; if (hdsp_check_for_iobox (hdsp)) { @@ -4468,7 +4375,7 @@ static int snd_hdsp_capture_open(snd_pcm return -EIO; } - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); snd_pcm_set_sync(substream); @@ -4484,7 +4391,7 @@ static int snd_hdsp_capture_open(snd_pcm hdsp->capture_pid = current->pid; hdsp->capture_substream = substream; - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hdsp_hw_constraints_period_sizes); @@ -4509,15 +4416,14 @@ static int snd_hdsp_capture_open(snd_pcm static int snd_hdsp_capture_release(snd_pcm_substream_t *substream) { - hdsp_t *hdsp = _snd_pcm_substream_chip(substream); - unsigned long flags; + hdsp_t *hdsp = snd_pcm_substream_chip(substream); - spin_lock_irqsave(&hdsp->lock, flags); + spin_lock_irq(&hdsp->lock); hdsp->capture_pid = -1; hdsp->capture_substream = NULL; - spin_unlock_irqrestore(&hdsp->lock, flags); + spin_unlock_irq(&hdsp->lock); return 0; } @@ -4830,19 +4736,13 @@ static int __devinit snd_hdsp_create_pcm return 0; } -static inline void snd_hdsp_9652_enable_mixer (hdsp_t *hdsp) +static void snd_hdsp_9652_enable_mixer (hdsp_t *hdsp) { hdsp->control2_register |= HDSP_9652_ENABLE_MIXER; hdsp_write (hdsp, HDSP_control2Reg, hdsp->control2_register); } -static inline void snd_hdsp_9652_disable_mixer (hdsp_t *hdsp) -{ - hdsp->control2_register &= ~HDSP_9652_ENABLE_MIXER; - hdsp_write (hdsp, HDSP_control2Reg, hdsp->control2_register); -} - -static inline int snd_hdsp_enable_io (hdsp_t *hdsp) +static int snd_hdsp_enable_io (hdsp_t *hdsp) { int i; @@ -4858,7 +4758,7 @@ static inline int snd_hdsp_enable_io (hd return 0; } -static inline void snd_hdsp_initialize_channels(hdsp_t *hdsp) +static void snd_hdsp_initialize_channels(hdsp_t *hdsp) { int status, aebi_channels, aebo_channels; @@ -4901,7 +4801,7 @@ static inline void snd_hdsp_initialize_c } } -static inline void snd_hdsp_initialize_midi_flush (hdsp_t *hdsp) +static void snd_hdsp_initialize_midi_flush (hdsp_t *hdsp) { snd_hdsp_flush_midi_input (hdsp, 0); snd_hdsp_flush_midi_input (hdsp, 1); @@ -4980,7 +4880,6 @@ static int __devinit snd_hdsp_create(snd spin_lock_init(&hdsp->midi[0].lock); spin_lock_init(&hdsp->midi[1].lock); hdsp->iobase = 0; - hdsp->res_port = NULL; hdsp->control_register = 0; hdsp->control2_register = 0; hdsp->io_type = Undefined; @@ -5035,13 +4934,9 @@ static int __devinit snd_hdsp_create(snd pci_set_master(hdsp->pci); + if ((err = pci_request_regions(pci, "hdsp")) < 0) + return err; hdsp->port = pci_resource_start(pci, 0); - - if ((hdsp->res_port = request_mem_region(hdsp->port, HDSP_IO_EXTENT, "hdsp")) == NULL) { - snd_printk("unable to grab memory region 0x%lx-0x%lx\n", hdsp->port, hdsp->port + HDSP_IO_EXTENT - 1); - return -EBUSY; - } - if ((hdsp->iobase = (unsigned long) ioremap_nocache(hdsp->port, HDSP_IO_EXTENT)) == 0) { snd_printk("unable to remap region 0x%lx-0x%lx\n", hdsp->port, hdsp->port + HDSP_IO_EXTENT - 1); return -EBUSY; @@ -5114,7 +5009,7 @@ static int __devinit snd_hdsp_create(snd static int snd_hdsp_free(hdsp_t *hdsp) { - if (hdsp->res_port) { + if (hdsp->port) { /* stop the audio, and cancel all interrupts */ hdsp->control_register &= ~(HDSP_Start|HDSP_AudioInterruptEnable|HDSP_Midi0InterruptEnable|HDSP_Midi1InterruptEnable); hdsp_write (hdsp, HDSP_controlRegister, hdsp->control_register); @@ -5128,10 +5023,8 @@ static int snd_hdsp_free(hdsp_t *hdsp) if (hdsp->iobase) iounmap((void *) hdsp->iobase); - if (hdsp->res_port) { - release_resource(hdsp->res_port); - kfree_nocheck(hdsp->res_port); - } + if (hdsp->port) + pci_release_regions(hdsp->pci); return 0; } --- linux-2.6.9-rc1/sound/pci/rme9652/rme9652.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/sound/pci/rme9652/rme9652.c 2004-09-02 20:51:53.000000000 -0700 @@ -46,21 +46,16 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for RME Digi9652 (Hammerfall) soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for RME Digi9652 (Hammerfall) soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable/disable specific RME96{52,36} soundcards."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(precise_ptr, bool, boot_devs, 0444); MODULE_PARM_DESC(precise_ptr, "Enable precise pointer (doesn't work reliably)."); -MODULE_PARM_SYNTAX(precise_ptr, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); MODULE_AUTHOR("Paul Davis , Winfried Ritsch"); MODULE_DESCRIPTION("RME Digi9652/Digi9636"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{RME,Hammerfall}," +MODULE_SUPPORTED_DEVICE("{{RME,Hammerfall}," "{RME,Hammerfall-Light}}"); /* The Hammerfall has two sets of 24 ADAT + 2 S/PDIF channels, one for @@ -217,7 +212,6 @@ typedef struct snd_rme9652 { spinlock_t lock; int irq; unsigned long port; - struct resource *res_port; unsigned long iobase; int precise_ptr; @@ -239,12 +233,11 @@ typedef struct snd_rme9652 { unsigned char ds_channels; unsigned char ss_channels; /* different for hammerfall/hammerfall-light */ - void *capture_buffer_unaligned; /* original buffer addresses */ - void *playback_buffer_unaligned; /* original buffer addresses */ + struct snd_dma_buffer playback_dma_buf; + struct snd_dma_buffer capture_dma_buf; + unsigned char *capture_buffer; /* suitably aligned address */ unsigned char *playback_buffer; /* suitably aligned address */ - dma_addr_t capture_buffer_addr; - dma_addr_t playback_buffer_addr; pid_t capture_pid; pid_t playback_pid; @@ -307,50 +300,24 @@ static char channel_map_9636_ds[26] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; -#define RME9652_PREALLOCATE_MEMORY /* via module snd-hammerfall-mem */ - -#ifdef RME9652_PREALLOCATE_MEMORY -static void *snd_hammerfall_get_buffer(struct pci_dev *pci, size_t size, dma_addr_t *addrp, int capture) +static int snd_hammerfall_get_buffer(struct pci_dev *pci, struct snd_dma_buffer *dmab, size_t size) { - struct snd_dma_device pdev; - struct snd_dma_buffer dmbuf; - - memset(&pdev, 0, sizeof(pdev)); - pdev.type = SNDRV_DMA_TYPE_DEV; - pdev.dev = snd_dma_pci_data(pci); - pdev.id = capture; - dmbuf.bytes = 0; - if (! snd_dma_get_reserved(&pdev, &dmbuf)) { - if (snd_dma_alloc_pages(&pdev, size, &dmbuf) < 0) - return NULL; - snd_dma_set_reserved(&pdev, &dmbuf); + dmab->dev.type = SNDRV_DMA_TYPE_DEV; + dmab->dev.dev = snd_dma_pci_data(pci); + if (! snd_dma_get_reserved_buf(dmab, snd_dma_pci_buf_id(pci))) { + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), + size, dmab) < 0) + return -ENOMEM; } - *addrp = dmbuf.addr; - return dmbuf.area; -} - -static void snd_hammerfall_free_buffer(struct pci_dev *pci, size_t size, void *ptr, dma_addr_t addr, int capture) -{ - struct snd_dma_device pdev; - - memset(&pdev, 0, sizeof(pdev)); - pdev.type = SNDRV_DMA_TYPE_DEV; - pdev.dev = snd_dma_pci_data(pci); - pdev.id = capture; - snd_dma_free_reserved(&pdev); + return 0; } -#else -static void *snd_hammerfall_get_buffer(struct pci_dev *pci, size_t size, dma_addr_t *addrp, int capture) +static void snd_hammerfall_free_buffer(struct snd_dma_buffer *dmab, struct pci_dev *pci) { - return snd_malloc_pci_pages(pci, size, addrp); + if (dmab->area) + snd_dma_reserve_buf(dmab, snd_dma_pci_buf_id(pci)); } -static void snd_hammerfall_free_buffer(struct pci_dev *pci, size_t size, void *ptr, dma_addr_t addr, int capture) -{ - snd_free_pci_pages(pci, size, ptr, addr); -} -#endif static struct pci_device_id snd_rme9652_ids[] = { { @@ -858,7 +825,7 @@ static int snd_rme9652_control_spdif_inf static int snd_rme9652_control_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol); snd_rme9652_convert_to_aes(&ucontrol->value.iec958, rme9652->creg_spdif); return 0; @@ -866,16 +833,15 @@ static int snd_rme9652_control_spdif_get static int snd_rme9652_control_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol); int change; u32 val; val = snd_rme9652_convert_from_aes(&ucontrol->value.iec958); - spin_lock_irqsave(&rme9652->lock, flags); + spin_lock_irq(&rme9652->lock); change = val != rme9652->creg_spdif; rme9652->creg_spdif = val; - spin_unlock_irqrestore(&rme9652->lock, flags); + spin_unlock_irq(&rme9652->lock); return change; } @@ -888,7 +854,7 @@ static int snd_rme9652_control_spdif_str static int snd_rme9652_control_spdif_stream_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol); snd_rme9652_convert_to_aes(&ucontrol->value.iec958, rme9652->creg_spdif_stream); return 0; @@ -896,18 +862,17 @@ static int snd_rme9652_control_spdif_str static int snd_rme9652_control_spdif_stream_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol); int change; u32 val; val = snd_rme9652_convert_from_aes(&ucontrol->value.iec958); - spin_lock_irqsave(&rme9652->lock, flags); + spin_lock_irq(&rme9652->lock); change = val != rme9652->creg_spdif_stream; rme9652->creg_spdif_stream = val; rme9652->control_register &= ~(RME9652_PRO | RME9652_Dolby | RME9652_EMP); rme9652_write(rme9652, RME9652_control_register, rme9652->control_register |= val); - spin_unlock_irqrestore(&rme9652->lock, flags); + spin_unlock_irq(&rme9652->lock); return change; } @@ -977,30 +942,28 @@ static int snd_rme9652_info_adat1_in(snd static int snd_rme9652_get_adat1_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol); - spin_lock_irqsave(&rme9652->lock, flags); + spin_lock_irq(&rme9652->lock); ucontrol->value.enumerated.item[0] = rme9652_adat1_in(rme9652); - spin_unlock_irqrestore(&rme9652->lock, flags); + spin_unlock_irq(&rme9652->lock); return 0; } static int snd_rme9652_put_adat1_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol); int change; unsigned int val; if (!snd_rme9652_use_is_exclusive(rme9652)) return -EBUSY; val = ucontrol->value.enumerated.item[0] % 2; - spin_lock_irqsave(&rme9652->lock, flags); + spin_lock_irq(&rme9652->lock); change = val != rme9652_adat1_in(rme9652); if (change) rme9652_set_adat1_input(rme9652, val); - spin_unlock_irqrestore(&rme9652->lock, flags); + spin_unlock_irq(&rme9652->lock); return change; } @@ -1050,30 +1013,28 @@ static int snd_rme9652_info_spdif_in(snd static int snd_rme9652_get_spdif_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol); - spin_lock_irqsave(&rme9652->lock, flags); + spin_lock_irq(&rme9652->lock); ucontrol->value.enumerated.item[0] = rme9652_spdif_in(rme9652); - spin_unlock_irqrestore(&rme9652->lock, flags); + spin_unlock_irq(&rme9652->lock); return 0; } static int snd_rme9652_put_spdif_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol); int change; unsigned int val; if (!snd_rme9652_use_is_exclusive(rme9652)) return -EBUSY; val = ucontrol->value.enumerated.item[0] % 3; - spin_lock_irqsave(&rme9652->lock, flags); + spin_lock_irq(&rme9652->lock); change = val != rme9652_spdif_in(rme9652); if (change) rme9652_set_spdif_input(rme9652, val); - spin_unlock_irqrestore(&rme9652->lock, flags); + spin_unlock_irq(&rme9652->lock); return change; } @@ -1121,29 +1082,27 @@ static int snd_rme9652_info_spdif_out(sn static int snd_rme9652_get_spdif_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol); - spin_lock_irqsave(&rme9652->lock, flags); + spin_lock_irq(&rme9652->lock); ucontrol->value.integer.value[0] = rme9652_spdif_out(rme9652); - spin_unlock_irqrestore(&rme9652->lock, flags); + spin_unlock_irq(&rme9652->lock); return 0; } static int snd_rme9652_put_spdif_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol); int change; unsigned int val; if (!snd_rme9652_use_is_exclusive(rme9652)) return -EBUSY; val = ucontrol->value.integer.value[0] & 1; - spin_lock_irqsave(&rme9652->lock, flags); + spin_lock_irq(&rme9652->lock); change = (int)val != rme9652_spdif_out(rme9652); rme9652_set_spdif_output(rme9652, val); - spin_unlock_irqrestore(&rme9652->lock, flags); + spin_unlock_irq(&rme9652->lock); return change; } @@ -1210,27 +1169,25 @@ static int snd_rme9652_info_sync_mode(sn static int snd_rme9652_get_sync_mode(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol); - spin_lock_irqsave(&rme9652->lock, flags); + spin_lock_irq(&rme9652->lock); ucontrol->value.enumerated.item[0] = rme9652_sync_mode(rme9652); - spin_unlock_irqrestore(&rme9652->lock, flags); + spin_unlock_irq(&rme9652->lock); return 0; } static int snd_rme9652_put_sync_mode(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol); int change; unsigned int val; val = ucontrol->value.enumerated.item[0] % 3; - spin_lock_irqsave(&rme9652->lock, flags); + spin_lock_irq(&rme9652->lock); change = (int)val != rme9652_sync_mode(rme9652); rme9652_set_sync_mode(rme9652, val); - spin_unlock_irqrestore(&rme9652->lock, flags); + spin_unlock_irq(&rme9652->lock); return change; } @@ -1291,7 +1248,7 @@ static int rme9652_set_sync_pref(rme9652 static int snd_rme9652_info_sync_pref(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { static char *texts[4] = {"IEC958 In", "ADAT1 In", "ADAT2 In", "ADAT3 In"}; - rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol); uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; @@ -1304,19 +1261,17 @@ static int snd_rme9652_info_sync_pref(sn static int snd_rme9652_get_sync_pref(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol); - spin_lock_irqsave(&rme9652->lock, flags); + spin_lock_irq(&rme9652->lock); ucontrol->value.enumerated.item[0] = rme9652_sync_pref(rme9652); - spin_unlock_irqrestore(&rme9652->lock, flags); + spin_unlock_irq(&rme9652->lock); return 0; } static int snd_rme9652_put_sync_pref(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol); int change, max; unsigned int val; @@ -1324,16 +1279,16 @@ static int snd_rme9652_put_sync_pref(snd return -EBUSY; max = rme9652->ss_channels == RME9652_NCHANNELS ? 4 : 3; val = ucontrol->value.enumerated.item[0] % max; - spin_lock_irqsave(&rme9652->lock, flags); + spin_lock_irq(&rme9652->lock); change = (int)val != rme9652_sync_pref(rme9652); rme9652_set_sync_pref(rme9652, val); - spin_unlock_irqrestore(&rme9652->lock, flags); + spin_unlock_irq(&rme9652->lock); return change; } static int snd_rme9652_info_thru(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { - rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol); uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = rme9652->ss_channels; uinfo->value.integer.min = 0; @@ -1343,7 +1298,7 @@ static int snd_rme9652_info_thru(snd_kco static int snd_rme9652_get_thru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol); unsigned int k; u32 thru_bits = rme9652->thru_bits; @@ -1355,8 +1310,7 @@ static int snd_rme9652_get_thru(snd_kcon static int snd_rme9652_put_thru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol); int change; unsigned int chn; u32 thru_bits = 0; @@ -1369,7 +1323,7 @@ static int snd_rme9652_put_thru(snd_kcon thru_bits |= 1 << chn; } - spin_lock_irqsave(&rme9652->lock, flags); + spin_lock_irq(&rme9652->lock); change = thru_bits ^ rme9652->thru_bits; if (change) { for (chn = 0; chn < rme9652->ss_channels; ++chn) { @@ -1378,7 +1332,7 @@ static int snd_rme9652_put_thru(snd_kcon rme9652_set_thru(rme9652,chn,thru_bits&(1<lock, flags); + spin_unlock_irq(&rme9652->lock); return !!change; } @@ -1399,19 +1353,17 @@ static int snd_rme9652_info_passthru(snd static int snd_rme9652_get_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol); - spin_lock_irqsave(&rme9652->lock, flags); + spin_lock_irq(&rme9652->lock); ucontrol->value.integer.value[0] = rme9652->passthru; - spin_unlock_irqrestore(&rme9652->lock, flags); + spin_unlock_irq(&rme9652->lock); return 0; } static int snd_rme9652_put_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol); int change; unsigned int val; int err = 0; @@ -1420,11 +1372,11 @@ static int snd_rme9652_put_passthru(snd_ return -EBUSY; val = ucontrol->value.integer.value[0] & 1; - spin_lock_irqsave(&rme9652->lock, flags); + spin_lock_irq(&rme9652->lock); change = (ucontrol->value.integer.value[0] != rme9652->passthru); if (change) err = rme9652_set_passthru(rme9652, val); - spin_unlock_irqrestore(&rme9652->lock, flags); + spin_unlock_irq(&rme9652->lock); return err ? err : change; } @@ -1447,12 +1399,11 @@ static int snd_rme9652_info_spdif_rate(s static int snd_rme9652_get_spdif_rate(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol); - spin_lock_irqsave(&rme9652->lock, flags); + spin_lock_irq(&rme9652->lock); ucontrol->value.integer.value[0] = rme9652_spdif_sample_rate(rme9652); - spin_unlock_irqrestore(&rme9652->lock, flags); + spin_unlock_irq(&rme9652->lock); return 0; } @@ -1477,7 +1428,7 @@ static int snd_rme9652_info_adat_sync(sn static int snd_rme9652_get_adat_sync(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol); unsigned int mask1, mask2, val; switch (kcontrol->private_value) { @@ -1509,7 +1460,7 @@ static int snd_rme9652_info_tc_valid(snd static int snd_rme9652_get_tc_valid(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme9652_t *rme9652 = _snd_kcontrol_chip(kcontrol); + rme9652_t *rme9652 = snd_kcontrol_chip(kcontrol); ucontrol->value.integer.value[0] = (rme9652_read(rme9652, RME9652_status_register) & RME9652_tc_valid) ? 1 : 0; @@ -1573,8 +1524,6 @@ static int snd_rme9652_get_tc_value(void #endif /* ALSA_HAS_STANDARD_WAY_OF_RETURNING_TIMECODE */ -#define RME9652_CONTROLS (sizeof(snd_rme9652_controls)/sizeof(snd_kcontrol_new_t)) - static snd_kcontrol_new_t snd_rme9652_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -1642,7 +1591,7 @@ int snd_rme9652_create_controls(snd_card int err; snd_kcontrol_t *kctl; - for (idx = 0; idx < RME9652_CONTROLS; idx++) { + for (idx = 0; idx < ARRAY_SIZE(snd_rme9652_controls); idx++) { if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme9652_controls[idx], rme9652))) < 0) return err; if (idx == 1) /* IEC958 (S/PDIF) Stream */ @@ -1847,17 +1796,8 @@ static void __devinit snd_rme9652_proc_i static void snd_rme9652_free_buffers(rme9652_t *rme9652) { - if (rme9652->capture_buffer_unaligned) { - snd_hammerfall_free_buffer(rme9652->pci, RME9652_DMA_AREA_BYTES, - rme9652->capture_buffer_unaligned, - rme9652->capture_buffer_addr, 1); - } - - if (rme9652->playback_buffer_unaligned) { - snd_hammerfall_free_buffer(rme9652->pci, RME9652_DMA_AREA_BYTES, - rme9652->playback_buffer_unaligned, - rme9652->playback_buffer_addr, 0); - } + snd_hammerfall_free_buffer(&rme9652->capture_dma_buf, rme9652->pci); + snd_hammerfall_free_buffer(&rme9652->playback_dma_buf, rme9652->pci); } static int snd_rme9652_free(rme9652_t *rme9652) @@ -1866,57 +1806,40 @@ static int snd_rme9652_free(rme9652_t *r rme9652_stop(rme9652); snd_rme9652_free_buffers(rme9652); - if (rme9652->iobase) - iounmap((void *) rme9652->iobase); - if (rme9652->res_port) { - release_resource(rme9652->res_port); - kfree_nocheck(rme9652->res_port); - } if (rme9652->irq >= 0) free_irq(rme9652->irq, (void *)rme9652); + if (rme9652->iobase) + iounmap((void *) rme9652->iobase); + if (rme9652->port) + pci_release_regions(rme9652->pci); + return 0; } static int __devinit snd_rme9652_initialize_memory(rme9652_t *rme9652) { - void *pb, *cb; - dma_addr_t pb_addr, cb_addr; unsigned long pb_bus, cb_bus; - cb = snd_hammerfall_get_buffer(rme9652->pci, RME9652_DMA_AREA_BYTES, &cb_addr, 1); - pb = snd_hammerfall_get_buffer(rme9652->pci, RME9652_DMA_AREA_BYTES, &pb_addr, 0); - - if (cb == 0 || pb == 0) { - if (cb) { - snd_hammerfall_free_buffer(rme9652->pci, RME9652_DMA_AREA_BYTES, cb, cb_addr, 1); - } - if (pb) { - snd_hammerfall_free_buffer(rme9652->pci, RME9652_DMA_AREA_BYTES, pb, pb_addr, 0); - } - + if (snd_hammerfall_get_buffer(rme9652->pci, &rme9652->capture_dma_buf, RME9652_DMA_AREA_BYTES) < 0 || + snd_hammerfall_get_buffer(rme9652->pci, &rme9652->playback_dma_buf, RME9652_DMA_AREA_BYTES) < 0) { + if (rme9652->capture_dma_buf.area) + snd_dma_free_pages(&rme9652->capture_dma_buf); printk(KERN_ERR "%s: no buffers available\n", rme9652->card_name); return -ENOMEM; } - /* save raw addresses for use when freeing memory later */ - - rme9652->capture_buffer_unaligned = cb; - rme9652->playback_buffer_unaligned = pb; - rme9652->capture_buffer_addr = cb_addr; - rme9652->playback_buffer_addr = pb_addr; - /* Align to bus-space 64K boundary */ - cb_bus = (cb_addr + 0xFFFF) & ~0xFFFFl; - pb_bus = (pb_addr + 0xFFFF) & ~0xFFFFl; + cb_bus = (rme9652->capture_dma_buf.addr + 0xFFFF) & ~0xFFFFl; + pb_bus = (rme9652->playback_dma_buf.addr + 0xFFFF) & ~0xFFFFl; /* Tell the card where it is */ rme9652_write(rme9652, RME9652_rec_buffer, cb_bus); rme9652_write(rme9652, RME9652_play_buffer, pb_bus); - rme9652->capture_buffer = cb + (cb_bus - cb_addr); - rme9652->playback_buffer = pb + (pb_bus - pb_addr); + rme9652->capture_buffer = rme9652->capture_dma_buf.area + (cb_bus - rme9652->capture_dma_buf.addr); + rme9652->playback_buffer = rme9652->playback_dma_buf.area + (pb_bus - rme9652->playback_dma_buf.addr); return 0; } @@ -1984,7 +1907,7 @@ static irqreturn_t snd_rme9652_interrupt static snd_pcm_uframes_t snd_rme9652_hw_pointer(snd_pcm_substream_t *substream) { - rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + rme9652_t *rme9652 = snd_pcm_substream_chip(substream); return rme9652_hw_pointer(rme9652); } @@ -2013,7 +1936,7 @@ static char *rme9652_channel_buffer_loca static int snd_rme9652_playback_copy(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count) { - rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + rme9652_t *rme9652 = snd_pcm_substream_chip(substream); char *channel_buf; snd_assert(pos + count <= RME9652_CHANNEL_BUFFER_BYTES / 4, return -EINVAL); @@ -2030,7 +1953,7 @@ static int snd_rme9652_playback_copy(snd static int snd_rme9652_capture_copy(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t pos, void __user *dst, snd_pcm_uframes_t count) { - rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + rme9652_t *rme9652 = snd_pcm_substream_chip(substream); char *channel_buf; snd_assert(pos + count <= RME9652_CHANNEL_BUFFER_BYTES / 4, return -EINVAL); @@ -2047,7 +1970,7 @@ static int snd_rme9652_capture_copy(snd_ static int snd_rme9652_hw_silence(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count) { - rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + rme9652_t *rme9652 = snd_pcm_substream_chip(substream); char *channel_buf; channel_buf = rme9652_channel_buffer_location (rme9652, @@ -2061,7 +1984,7 @@ static int snd_rme9652_hw_silence(snd_pc static int snd_rme9652_reset(snd_pcm_substream_t *substream) { snd_pcm_runtime_t *runtime = substream->runtime; - rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + rme9652_t *rme9652 = snd_pcm_substream_chip(substream); snd_pcm_substream_t *other; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) other = rme9652->capture_substream; @@ -2089,7 +2012,7 @@ static int snd_rme9652_reset(snd_pcm_sub static int snd_rme9652_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params) { - rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + rme9652_t *rme9652 = snd_pcm_substream_chip(substream); int err; pid_t this_pid; pid_t other_pid; @@ -2154,7 +2077,7 @@ static int snd_rme9652_hw_params(snd_pcm static int snd_rme9652_channel_info(snd_pcm_substream_t *substream, snd_pcm_channel_info_t *info) { - rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + rme9652_t *rme9652 = snd_pcm_substream_chip(substream); int chn; snd_assert(info->channel < RME9652_NCHANNELS, return -EINVAL); @@ -2197,7 +2120,7 @@ static void rme9652_silence_playback(rme static int snd_rme9652_trigger(snd_pcm_substream_t *substream, int cmd) { - rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + rme9652_t *rme9652 = snd_pcm_substream_chip(substream); snd_pcm_substream_t *other; int running; spin_lock(&rme9652->lock); @@ -2260,13 +2183,14 @@ static int snd_rme9652_trigger(snd_pcm_s static int snd_rme9652_prepare(snd_pcm_substream_t *substream) { - rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); + rme9652_t *rme9652 = snd_pcm_substream_chip(substream); + unsigned long flags; int result = 0; - spin_lock(&rme9652->lock); + spin_lock_irqsave(&rme9652->lock, flags); if (!rme9652->running) rme9652_reset_hw_pointer(rme9652); - spin_unlock(&rme9652->lock); + spin_unlock_irqrestore(&rme9652->lock, flags); return result; } @@ -2319,10 +2243,8 @@ static snd_pcm_hardware_t snd_rme9652_ca static unsigned int period_sizes[] = { 64, 128, 256, 512, 1024, 2048, 4096, 8192 }; -#define PERIOD_SIZES sizeof(period_sizes) / sizeof(period_sizes[0]) - static snd_pcm_hw_constraint_list_t hw_constraints_period_sizes = { - .count = PERIOD_SIZES, + .count = ARRAY_SIZE(period_sizes), .list = period_sizes, .mask = 0 }; @@ -2386,11 +2308,10 @@ static int snd_rme9652_hw_rule_rate_chan static int snd_rme9652_playback_open(snd_pcm_substream_t *substream) { - rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); - unsigned long flags; + rme9652_t *rme9652 = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - spin_lock_irqsave(&rme9652->lock, flags); + spin_lock_irq(&rme9652->lock); snd_pcm_set_sync(substream); @@ -2406,7 +2327,7 @@ static int snd_rme9652_playback_open(snd rme9652->playback_pid = current->pid; rme9652->playback_substream = substream; - spin_unlock_irqrestore(&rme9652->lock, flags); + spin_unlock_irq(&rme9652->lock); snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_period_sizes); @@ -2429,15 +2350,14 @@ static int snd_rme9652_playback_open(snd static int snd_rme9652_playback_release(snd_pcm_substream_t *substream) { - rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); - unsigned long flags; + rme9652_t *rme9652 = snd_pcm_substream_chip(substream); - spin_lock_irqsave(&rme9652->lock, flags); + spin_lock_irq(&rme9652->lock); rme9652->playback_pid = -1; rme9652->playback_substream = NULL; - spin_unlock_irqrestore(&rme9652->lock, flags); + spin_unlock_irq(&rme9652->lock); rme9652->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; snd_ctl_notify(rme9652->card, SNDRV_CTL_EVENT_MASK_VALUE | @@ -2448,11 +2368,10 @@ static int snd_rme9652_playback_release( static int snd_rme9652_capture_open(snd_pcm_substream_t *substream) { - rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); - unsigned long flags; + rme9652_t *rme9652 = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - spin_lock_irqsave(&rme9652->lock, flags); + spin_lock_irq(&rme9652->lock); snd_pcm_set_sync(substream); @@ -2468,7 +2387,7 @@ static int snd_rme9652_capture_open(snd_ rme9652->capture_pid = current->pid; rme9652->capture_substream = substream; - spin_unlock_irqrestore(&rme9652->lock, flags); + spin_unlock_irq(&rme9652->lock); snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hw_constraints_period_sizes); @@ -2486,15 +2405,14 @@ static int snd_rme9652_capture_open(snd_ static int snd_rme9652_capture_release(snd_pcm_substream_t *substream) { - rme9652_t *rme9652 = _snd_pcm_substream_chip(substream); - unsigned long flags; + rme9652_t *rme9652 = snd_pcm_substream_chip(substream); - spin_lock_irqsave(&rme9652->lock, flags); + spin_lock_irq(&rme9652->lock); rme9652->capture_pid = -1; rme9652->capture_substream = NULL; - spin_unlock_irqrestore(&rme9652->lock, flags); + spin_unlock_irq(&rme9652->lock); return 0; } @@ -2576,12 +2494,9 @@ static int __devinit snd_rme9652_create( spin_lock_init(&rme9652->lock); + if ((err = pci_request_regions(pci, "rme9652")) < 0) + return err; rme9652->port = pci_resource_start(pci, 0); - if ((rme9652->res_port = request_mem_region(rme9652->port, RME9652_IO_EXTENT, "rme9652")) == NULL) { - snd_printk("unable to grab memory region 0x%lx-0x%lx\n", rme9652->port, rme9652->port + RME9652_IO_EXTENT - 1); - return -EBUSY; - } - rme9652->iobase = (unsigned long) ioremap_nocache(rme9652->port, RME9652_IO_EXTENT); if (rme9652->iobase == 0) { snd_printk("unable to remap region 0x%lx-0x%lx\n", rme9652->port, rme9652->port + RME9652_IO_EXTENT - 1); --- linux-2.6.9-rc1/sound/pci/rme96.c 2004-08-24 00:02:26.000000000 -0700 +++ 25/sound/pci/rme96.c 2004-09-02 23:57:36.738737312 -0700 @@ -47,8 +47,7 @@ MODULE_AUTHOR("Anders Torger playback_frlog; pos <<= rme96->playback_frlog; memset_io(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, @@ -364,7 +355,7 @@ snd_rme96_playback_copy(snd_pcm_substrea void __user *src, snd_pcm_uframes_t count) { - rme96_t *rme96 = _snd_pcm_substream_chip(substream); + rme96_t *rme96 = snd_pcm_substream_chip(substream); count <<= rme96->playback_frlog; pos <<= rme96->playback_frlog; copy_from_user_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, src, @@ -379,7 +370,7 @@ snd_rme96_capture_copy(snd_pcm_substream void __user *dst, snd_pcm_uframes_t count) { - rme96_t *rme96 = _snd_pcm_substream_chip(substream); + rme96_t *rme96 = snd_pcm_substream_chip(substream); count <<= rme96->capture_frlog; pos <<= rme96->capture_frlog; copy_to_user_fromio(dst, rme96->iobase + RME96_IO_REC_BUFFER + pos, @@ -392,7 +383,7 @@ snd_rme96_capture_copy(snd_pcm_substream */ static snd_pcm_hardware_t snd_rme96_playback_spdif_info = { - .info = (SNDRV_PCM_INFO_MMAP | + .info = (SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE), @@ -421,7 +412,7 @@ static snd_pcm_hardware_t snd_rme96_play */ static snd_pcm_hardware_t snd_rme96_capture_spdif_info = { - .info = (SNDRV_PCM_INFO_MMAP | + .info = (SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE), @@ -450,7 +441,7 @@ static snd_pcm_hardware_t snd_rme96_capt */ static snd_pcm_hardware_t snd_rme96_playback_adat_info = { - .info = (SNDRV_PCM_INFO_MMAP | + .info = (SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE), @@ -475,7 +466,7 @@ static snd_pcm_hardware_t snd_rme96_play */ static snd_pcm_hardware_t snd_rme96_capture_adat_info = { - .info = (SNDRV_PCM_INFO_MMAP | + .info = (SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE), @@ -991,28 +982,30 @@ static int snd_rme96_playback_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params) { - unsigned long flags; - rme96_t *rme96 = _snd_pcm_substream_chip(substream); + rme96_t *rme96 = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; int err, rate, dummy; - if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params))) < 0) - return err; - spin_lock_irqsave(&rme96->lock, flags); + runtime->dma_area = (void *)(rme96->iobase + RME96_IO_PLAY_BUFFER); + runtime->dma_addr = rme96->port + RME96_IO_PLAY_BUFFER; + runtime->dma_bytes = RME96_BUFFER_SIZE; + + spin_lock_irq(&rme96->lock); if (!(rme96->wcreg & RME96_WCR_MASTER) && snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG && (rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0) { /* slave clock */ if ((int)params_rate(params) != rate) { - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return -EIO; } } else if ((err = snd_rme96_playback_setrate(rme96, params_rate(params))) < 0) { - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return err; } if ((err = snd_rme96_playback_setformat(rme96, params_format(params))) < 0) { - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return err; } snd_rme96_setframelog(rme96, params_channels(params), 1); @@ -1020,7 +1013,7 @@ snd_rme96_playback_hw_params(snd_pcm_sub if (params_period_size(params) << rme96->playback_frlog != rme96->capture_periodsize) { - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return -EBUSY; } } @@ -1032,50 +1025,44 @@ snd_rme96_playback_hw_params(snd_pcm_sub rme96->wcreg &= ~(RME96_WCR_PRO | RME96_WCR_DOLBY | RME96_WCR_EMP); writel(rme96->wcreg |= rme96->wcreg_spdif_stream, rme96->iobase + RME96_IO_CONTROL_REGISTER); } - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return 0; } static int -snd_rme96_playback_hw_free(snd_pcm_substream_t *substream) -{ - snd_pcm_lib_free_pages(substream); - return 0; -} - -static int snd_rme96_capture_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params) { - unsigned long flags; - rme96_t *rme96 = _snd_pcm_substream_chip(substream); + rme96_t *rme96 = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; int err, isadat, rate; - if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params))) < 0) - return err; - spin_lock_irqsave(&rme96->lock, flags); + runtime->dma_area = (void *)(rme96->iobase + RME96_IO_REC_BUFFER); + runtime->dma_addr = rme96->port + RME96_IO_REC_BUFFER; + runtime->dma_bytes = RME96_BUFFER_SIZE; + + spin_lock_irq(&rme96->lock); if ((err = snd_rme96_capture_setformat(rme96, params_format(params))) < 0) { - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return err; } if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) { if ((err = snd_rme96_capture_analog_setrate(rme96, params_rate(params))) < 0) { - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return err; } } else if ((rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) { if ((int)params_rate(params) != rate) { - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return -EIO; } if ((isadat && runtime->hw.channels_min == 2) || (!isadat && runtime->hw.channels_min == 8)) { - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return -EIO; } } @@ -1084,33 +1071,24 @@ snd_rme96_capture_hw_params(snd_pcm_subs if (params_period_size(params) << rme96->capture_frlog != rme96->playback_periodsize) { - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return -EBUSY; } } rme96->capture_periodsize = params_period_size(params) << rme96->capture_frlog; snd_rme96_set_period_properties(rme96, rme96->capture_periodsize); - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return 0; } -static int -snd_rme96_capture_hw_free(snd_pcm_substream_t *substream) -{ - snd_pcm_lib_free_pages(substream); - return 0; -} - static void snd_rme96_playback_start(rme96_t *rme96, int from_pause) { if (!from_pause) { writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS); - rme96->playback_last_appl_ptr = 0; - rme96->playback_ptr = 0; } rme96->wcreg |= RME96_WCR_START; @@ -1123,7 +1101,6 @@ snd_rme96_capture_start(rme96_t *rme96, { if (!from_pause) { writel(0, rme96->iobase + RME96_IO_RESET_REC_POS); - rme96->capture_ptr = 0; } rme96->wcreg |= RME96_WCR_START_2; @@ -1186,10 +1163,8 @@ snd_rme96_interrupt(int irq, static unsigned int period_bytes[] = { RME96_SMALL_BLOCK_SIZE, RME96_LARGE_BLOCK_SIZE }; -#define PERIOD_BYTES sizeof(period_bytes) / sizeof(period_bytes[0]) - static snd_pcm_hw_constraint_list_t hw_constraints_period_bytes = { - .count = PERIOD_BYTES, + .count = ARRAY_SIZE(period_bytes), .list = period_bytes, .mask = 0 }; @@ -1197,24 +1172,21 @@ static snd_pcm_hw_constraint_list_t hw_c static int snd_rme96_playback_spdif_open(snd_pcm_substream_t *substream) { - unsigned long flags; int rate, dummy; - rme96_t *rme96 = _snd_pcm_substream_chip(substream); + rme96_t *rme96 = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_set_sync(substream); - spin_lock_irqsave(&rme96->lock, flags); + spin_lock_irq(&rme96->lock); if (rme96->playback_substream != NULL) { - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return -EBUSY; } rme96->wcreg &= ~RME96_WCR_ADAT; writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); rme96->playback_substream = substream; - rme96->playback_last_appl_ptr = 0; - rme96->playback_ptr = 0; - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); runtime->hw = snd_rme96_playback_spdif_info; if (!(rme96->wcreg & RME96_WCR_MASTER) && @@ -1239,9 +1211,8 @@ snd_rme96_playback_spdif_open(snd_pcm_su static int snd_rme96_capture_spdif_open(snd_pcm_substream_t *substream) { - unsigned long flags; int isadat, rate; - rme96_t *rme96 = _snd_pcm_substream_chip(substream); + rme96_t *rme96 = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_set_sync(substream); @@ -1258,14 +1229,13 @@ snd_rme96_capture_spdif_open(snd_pcm_sub runtime->hw.rate_max = rate; } - spin_lock_irqsave(&rme96->lock, flags); + spin_lock_irq(&rme96->lock); if (rme96->capture_substream != NULL) { - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return -EBUSY; } rme96->capture_substream = substream; - rme96->capture_ptr = 0; - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); @@ -1276,24 +1246,21 @@ snd_rme96_capture_spdif_open(snd_pcm_sub static int snd_rme96_playback_adat_open(snd_pcm_substream_t *substream) { - unsigned long flags; int rate, dummy; - rme96_t *rme96 = _snd_pcm_substream_chip(substream); + rme96_t *rme96 = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_set_sync(substream); - spin_lock_irqsave(&rme96->lock, flags); + spin_lock_irq(&rme96->lock); if (rme96->playback_substream != NULL) { - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return -EBUSY; } rme96->wcreg |= RME96_WCR_ADAT; writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); rme96->playback_substream = substream; - rme96->playback_last_appl_ptr = 0; - rme96->playback_ptr = 0; - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); runtime->hw = snd_rme96_playback_adat_info; if (!(rme96->wcreg & RME96_WCR_MASTER) && @@ -1313,9 +1280,8 @@ snd_rme96_playback_adat_open(snd_pcm_sub static int snd_rme96_capture_adat_open(snd_pcm_substream_t *substream) { - unsigned long flags; int isadat, rate; - rme96_t *rme96 = _snd_pcm_substream_chip(substream); + rme96_t *rme96 = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_set_sync(substream); @@ -1335,14 +1301,13 @@ snd_rme96_capture_adat_open(snd_pcm_subs runtime->hw.rate_max = rate; } - spin_lock_irqsave(&rme96->lock, flags); + spin_lock_irq(&rme96->lock); if (rme96->capture_substream != NULL) { - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return -EBUSY; } rme96->capture_substream = substream; - rme96->capture_ptr = 0; - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); @@ -1352,18 +1317,17 @@ snd_rme96_capture_adat_open(snd_pcm_subs static int snd_rme96_playback_close(snd_pcm_substream_t *substream) { - unsigned long flags; - rme96_t *rme96 = _snd_pcm_substream_chip(substream); + rme96_t *rme96 = snd_pcm_substream_chip(substream); int spdif = 0; - spin_lock_irqsave(&rme96->lock, flags); + spin_lock_irq(&rme96->lock); if (RME96_ISPLAYING(rme96)) { snd_rme96_playback_stop(rme96); } rme96->playback_substream = NULL; rme96->playback_periodsize = 0; spdif = (rme96->wcreg & RME96_WCR_ADAT) == 0; - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); if (spdif) { rme96->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; snd_ctl_notify(rme96->card, SNDRV_CTL_EVENT_MASK_VALUE | @@ -1375,46 +1339,43 @@ snd_rme96_playback_close(snd_pcm_substre static int snd_rme96_capture_close(snd_pcm_substream_t *substream) { - unsigned long flags; - rme96_t *rme96 = _snd_pcm_substream_chip(substream); + rme96_t *rme96 = snd_pcm_substream_chip(substream); - spin_lock_irqsave(&rme96->lock, flags); + spin_lock_irq(&rme96->lock); if (RME96_ISRECORDING(rme96)) { snd_rme96_capture_stop(rme96); } rme96->capture_substream = NULL; rme96->capture_periodsize = 0; - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return 0; } static int snd_rme96_playback_prepare(snd_pcm_substream_t *substream) { - rme96_t *rme96 = _snd_pcm_substream_chip(substream); - unsigned long flags; + rme96_t *rme96 = snd_pcm_substream_chip(substream); - spin_lock_irqsave(&rme96->lock, flags); + spin_lock_irq(&rme96->lock); if (RME96_ISPLAYING(rme96)) { snd_rme96_playback_stop(rme96); } writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS); - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return 0; } static int snd_rme96_capture_prepare(snd_pcm_substream_t *substream) { - rme96_t *rme96 = _snd_pcm_substream_chip(substream); - unsigned long flags; + rme96_t *rme96 = snd_pcm_substream_chip(substream); - spin_lock_irqsave(&rme96->lock, flags); + spin_lock_irq(&rme96->lock); if (RME96_ISRECORDING(rme96)) { snd_rme96_capture_stop(rme96); } writel(0, rme96->iobase + RME96_IO_RESET_REC_POS); - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return 0; } @@ -1422,7 +1383,7 @@ static int snd_rme96_playback_trigger(snd_pcm_substream_t *substream, int cmd) { - rme96_t *rme96 = _snd_pcm_substream_chip(substream); + rme96_t *rme96 = snd_pcm_substream_chip(substream); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -1465,7 +1426,7 @@ static int snd_rme96_capture_trigger(snd_pcm_substream_t *substream, int cmd) { - rme96_t *rme96 = _snd_pcm_substream_chip(substream); + rme96_t *rme96 = snd_pcm_substream_chip(substream); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -1508,75 +1469,15 @@ snd_rme96_capture_trigger(snd_pcm_substr static snd_pcm_uframes_t snd_rme96_playback_pointer(snd_pcm_substream_t *substream) { - rme96_t *rme96 = _snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; - snd_pcm_sframes_t diff; - size_t bytes; - - if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { - diff = runtime->control->appl_ptr - - rme96->playback_last_appl_ptr; - rme96->playback_last_appl_ptr = runtime->control->appl_ptr; - if (diff != 0 && - diff < -(snd_pcm_sframes_t)(runtime->boundary >> 1)) - { - diff += runtime->boundary; - } - bytes = diff << rme96->playback_frlog; - - if (bytes > RME96_BUFFER_SIZE - rme96->playback_ptr) { - memcpy_toio((void *)(rme96->iobase + RME96_IO_PLAY_BUFFER + - rme96->playback_ptr), - runtime->dma_area + rme96->playback_ptr, - RME96_BUFFER_SIZE - rme96->playback_ptr); - bytes -= RME96_BUFFER_SIZE - rme96->playback_ptr; - if (bytes > RME96_BUFFER_SIZE) { - bytes = RME96_BUFFER_SIZE; - } - memcpy_toio((void *)(rme96->iobase + RME96_IO_PLAY_BUFFER), - runtime->dma_area, - bytes); - rme96->playback_ptr = bytes; - } else if (bytes != 0) { - memcpy_toio((void *)(rme96->iobase + RME96_IO_PLAY_BUFFER + - rme96->playback_ptr), - runtime->dma_area + rme96->playback_ptr, - bytes); - rme96->playback_ptr += bytes; - } - } + rme96_t *rme96 = snd_pcm_substream_chip(substream); return snd_rme96_playback_ptr(rme96); } static snd_pcm_uframes_t snd_rme96_capture_pointer(snd_pcm_substream_t *substream) { - rme96_t *rme96 = _snd_pcm_substream_chip(substream); - snd_pcm_runtime_t *runtime = substream->runtime; - snd_pcm_uframes_t frameptr; - size_t ptr; - - frameptr = snd_rme96_capture_ptr(rme96); - if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { - ptr = frameptr << rme96->capture_frlog; - if (ptr > rme96->capture_ptr) { - memcpy_fromio(runtime->dma_area + rme96->capture_ptr, - (void *)(rme96->iobase + RME96_IO_REC_BUFFER + - rme96->capture_ptr), - ptr - rme96->capture_ptr); - rme96->capture_ptr += ptr - rme96->capture_ptr; - } else if (ptr < rme96->capture_ptr) { - memcpy_fromio(runtime->dma_area + rme96->capture_ptr, - (void *)(rme96->iobase + RME96_IO_REC_BUFFER + - rme96->capture_ptr), - RME96_BUFFER_SIZE - rme96->capture_ptr); - memcpy_fromio(runtime->dma_area, - (void *)(rme96->iobase + RME96_IO_REC_BUFFER), - ptr); - rme96->capture_ptr = ptr; - } - } - return frameptr; + rme96_t *rme96 = snd_pcm_substream_chip(substream); + return snd_rme96_capture_ptr(rme96); } static snd_pcm_ops_t snd_rme96_playback_spdif_ops = { @@ -1584,12 +1485,12 @@ static snd_pcm_ops_t snd_rme96_playback_ .close = snd_rme96_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_rme96_playback_hw_params, - .hw_free = snd_rme96_playback_hw_free, .prepare = snd_rme96_playback_prepare, .trigger = snd_rme96_playback_trigger, .pointer = snd_rme96_playback_pointer, .copy = snd_rme96_playback_copy, .silence = snd_rme96_playback_silence, + .mmap = snd_pcm_lib_mmap_iomem, }; static snd_pcm_ops_t snd_rme96_capture_spdif_ops = { @@ -1597,11 +1498,11 @@ static snd_pcm_ops_t snd_rme96_capture_s .close = snd_rme96_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_rme96_capture_hw_params, - .hw_free = snd_rme96_capture_hw_free, .prepare = snd_rme96_capture_prepare, .trigger = snd_rme96_capture_trigger, .pointer = snd_rme96_capture_pointer, .copy = snd_rme96_capture_copy, + .mmap = snd_pcm_lib_mmap_iomem, }; static snd_pcm_ops_t snd_rme96_playback_adat_ops = { @@ -1609,12 +1510,12 @@ static snd_pcm_ops_t snd_rme96_playback_ .close = snd_rme96_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_rme96_playback_hw_params, - .hw_free = snd_rme96_playback_hw_free, .prepare = snd_rme96_playback_prepare, .trigger = snd_rme96_playback_trigger, .pointer = snd_rme96_playback_pointer, .copy = snd_rme96_playback_copy, .silence = snd_rme96_playback_silence, + .mmap = snd_pcm_lib_mmap_iomem, }; static snd_pcm_ops_t snd_rme96_capture_adat_ops = { @@ -1622,11 +1523,11 @@ static snd_pcm_ops_t snd_rme96_capture_a .close = snd_rme96_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_rme96_capture_hw_params, - .hw_free = snd_rme96_capture_hw_free, .prepare = snd_rme96_capture_prepare, .trigger = snd_rme96_capture_trigger, .pointer = snd_rme96_capture_pointer, .copy = snd_rme96_capture_copy, + .mmap = snd_pcm_lib_mmap_iomem, }; static void @@ -1649,10 +1550,9 @@ snd_rme96_free(void *private_data) iounmap((void *)rme96->iobase); rme96->iobase = 0; } - if (rme96->res_port != NULL) { - release_resource(rme96->res_port); - kfree_nocheck(rme96->res_port); - rme96->res_port = NULL; + if (rme96->port) { + pci_release_regions(rme96->pci); + rme96->port = 0; } } @@ -1661,7 +1561,6 @@ snd_rme96_free_spdif_pcm(snd_pcm_t *pcm) { rme96_t *rme96 = (rme96_t *) pcm->private_data; rme96->spdif_pcm = NULL; - snd_pcm_lib_preallocate_free_for_all(pcm); } static void @@ -1669,7 +1568,6 @@ snd_rme96_free_adat_pcm(snd_pcm_t *pcm) { rme96_t *rme96 = (rme96_t *) pcm->private_data; rme96->adat_pcm = NULL; - snd_pcm_lib_preallocate_free_for_all(pcm); } static int __devinit @@ -1679,24 +1577,21 @@ snd_rme96_create(rme96_t *rme96) int err; rme96->irq = -1; + spin_lock_init(&rme96->lock); if ((err = pci_enable_device(pci)) < 0) return err; + if ((err = pci_request_regions(pci, "RME96")) < 0) + return err; rme96->port = pci_resource_start(rme96->pci, 0); - if ((rme96->res_port = request_mem_region(rme96->port, RME96_IO_SIZE, "RME96")) == NULL) { - snd_printk("unable to grab memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1); - return -EBUSY; - } - if (request_irq(pci->irq, snd_rme96_interrupt, SA_INTERRUPT|SA_SHIRQ, "RME96", (void *)rme96)) { snd_printk("unable to grab IRQ %d\n", pci->irq); return -EBUSY; } rme96->irq = pci->irq; - spin_lock_init(&rme96->lock); if ((rme96->iobase = (unsigned long) ioremap_nocache(rme96->port, RME96_IO_SIZE)) == 0) { snd_printk("unable to remap memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1); return -ENOMEM; @@ -1719,12 +1614,6 @@ snd_rme96_create(rme96_t *rme96) rme96->spdif_pcm->info_flags = 0; - snd_pcm_lib_preallocate_pages_for_all(rme96->spdif_pcm, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), - RME96_BUFFER_SIZE, - RME96_BUFFER_SIZE); - /* set up ALSA pcm device for ADAT */ if (pci->device == PCI_DEVICE_ID_DIGI96) { /* ADAT is not available on the base model */ @@ -1742,12 +1631,6 @@ snd_rme96_create(rme96_t *rme96) snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_adat_ops); rme96->adat_pcm->info_flags = 0; - - snd_pcm_lib_preallocate_pages_for_all(rme96->adat_pcm, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), - RME96_BUFFER_SIZE, - RME96_BUFFER_SIZE); } rme96->playback_periodsize = 0; @@ -1958,28 +1841,26 @@ snd_rme96_info_loopback_control(snd_kcon static int snd_rme96_get_loopback_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme96_t *rme96 = snd_kcontrol_chip(kcontrol); - spin_lock_irqsave(&rme96->lock, flags); + spin_lock_irq(&rme96->lock); ucontrol->value.integer.value[0] = rme96->wcreg & RME96_WCR_SEL ? 0 : 1; - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return 0; } static int snd_rme96_put_loopback_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme96_t *rme96 = snd_kcontrol_chip(kcontrol); unsigned int val; int change; val = ucontrol->value.integer.value[0] ? 0 : RME96_WCR_SEL; - spin_lock_irqsave(&rme96->lock, flags); + spin_lock_irq(&rme96->lock); val = (rme96->wcreg & ~RME96_WCR_SEL) | val; change = val != rme96->wcreg; writel(rme96->wcreg = val, rme96->iobase + RME96_IO_CONTROL_REGISTER); - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return change; } @@ -1987,7 +1868,7 @@ static int snd_rme96_info_inputtype_control(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { static char *_texts[5] = { "Optical", "Coaxial", "Internal", "XLR", "Analog" }; - rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + rme96_t *rme96 = snd_kcontrol_chip(kcontrol); char *texts[5] = { _texts[0], _texts[1], _texts[2], _texts[3], _texts[4] }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; @@ -2023,11 +1904,10 @@ snd_rme96_info_inputtype_control(snd_kco static int snd_rme96_get_inputtype_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme96_t *rme96 = snd_kcontrol_chip(kcontrol); unsigned int items = 3; - spin_lock_irqsave(&rme96->lock, flags); + spin_lock_irq(&rme96->lock); ucontrol->value.enumerated.item[0] = snd_rme96_getinputtype(rme96); switch (rme96->pci->device) { @@ -2057,14 +1937,13 @@ snd_rme96_get_inputtype_control(snd_kcon ucontrol->value.enumerated.item[0] = items - 1; } - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return 0; } static int snd_rme96_put_inputtype_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme96_t *rme96 = snd_kcontrol_chip(kcontrol); unsigned int val; int change, items = 3; @@ -2096,10 +1975,10 @@ snd_rme96_put_inputtype_control(snd_kcon } } - spin_lock_irqsave(&rme96->lock, flags); + spin_lock_irq(&rme96->lock); change = (int)val != snd_rme96_getinputtype(rme96); snd_rme96_setinputtype(rme96, val); - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return change; } @@ -2120,27 +1999,25 @@ snd_rme96_info_clockmode_control(snd_kco static int snd_rme96_get_clockmode_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme96_t *rme96 = snd_kcontrol_chip(kcontrol); - spin_lock_irqsave(&rme96->lock, flags); + spin_lock_irq(&rme96->lock); ucontrol->value.enumerated.item[0] = snd_rme96_getclockmode(rme96); - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return 0; } static int snd_rme96_put_clockmode_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme96_t *rme96 = snd_kcontrol_chip(kcontrol); unsigned int val; int change; val = ucontrol->value.enumerated.item[0] % 3; - spin_lock_irqsave(&rme96->lock, flags); + spin_lock_irq(&rme96->lock); change = (int)val != snd_rme96_getclockmode(rme96); snd_rme96_setclockmode(rme96, val); - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return change; } @@ -2161,28 +2038,26 @@ snd_rme96_info_attenuation_control(snd_k static int snd_rme96_get_attenuation_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme96_t *rme96 = snd_kcontrol_chip(kcontrol); - spin_lock_irqsave(&rme96->lock, flags); + spin_lock_irq(&rme96->lock); ucontrol->value.enumerated.item[0] = snd_rme96_getattenuation(rme96); - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return 0; } static int snd_rme96_put_attenuation_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme96_t *rme96 = snd_kcontrol_chip(kcontrol); unsigned int val; int change; val = ucontrol->value.enumerated.item[0] % 4; - spin_lock_irqsave(&rme96->lock, flags); + spin_lock_irq(&rme96->lock); change = (int)val != snd_rme96_getattenuation(rme96); snd_rme96_setattenuation(rme96, val); - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return change; } @@ -2203,27 +2078,25 @@ snd_rme96_info_montracks_control(snd_kco static int snd_rme96_get_montracks_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme96_t *rme96 = snd_kcontrol_chip(kcontrol); - spin_lock_irqsave(&rme96->lock, flags); + spin_lock_irq(&rme96->lock); ucontrol->value.enumerated.item[0] = snd_rme96_getmontracks(rme96); - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return 0; } static int snd_rme96_put_montracks_control(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme96_t *rme96 = snd_kcontrol_chip(kcontrol); unsigned int val; int change; val = ucontrol->value.enumerated.item[0] % 4; - spin_lock_irqsave(&rme96->lock, flags); + spin_lock_irq(&rme96->lock); change = (int)val != snd_rme96_getmontracks(rme96); snd_rme96_setmontracks(rme96, val); - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return change; } @@ -2258,7 +2131,7 @@ static int snd_rme96_control_spdif_info( static int snd_rme96_control_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + rme96_t *rme96 = snd_kcontrol_chip(kcontrol); snd_rme96_convert_to_aes(&ucontrol->value.iec958, rme96->wcreg_spdif); return 0; @@ -2266,16 +2139,15 @@ static int snd_rme96_control_spdif_get(s static int snd_rme96_control_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme96_t *rme96 = snd_kcontrol_chip(kcontrol); int change; u32 val; val = snd_rme96_convert_from_aes(&ucontrol->value.iec958); - spin_lock_irqsave(&rme96->lock, flags); + spin_lock_irq(&rme96->lock); change = val != rme96->wcreg_spdif; rme96->wcreg_spdif = val; - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return change; } @@ -2288,7 +2160,7 @@ static int snd_rme96_control_spdif_strea static int snd_rme96_control_spdif_stream_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + rme96_t *rme96 = snd_kcontrol_chip(kcontrol); snd_rme96_convert_to_aes(&ucontrol->value.iec958, rme96->wcreg_spdif_stream); return 0; @@ -2296,18 +2168,17 @@ static int snd_rme96_control_spdif_strea static int snd_rme96_control_spdif_stream_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme96_t *rme96 = snd_kcontrol_chip(kcontrol); int change; u32 val; val = snd_rme96_convert_from_aes(&ucontrol->value.iec958); - spin_lock_irqsave(&rme96->lock, flags); + spin_lock_irq(&rme96->lock); change = val != rme96->wcreg_spdif_stream; rme96->wcreg_spdif_stream = val; rme96->wcreg &= ~(RME96_WCR_PRO | RME96_WCR_DOLBY | RME96_WCR_EMP); writel(rme96->wcreg |= val, rme96->iobase + RME96_IO_CONTROL_REGISTER); - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return change; } @@ -2327,7 +2198,7 @@ static int snd_rme96_control_spdif_mask_ static int snd_rme96_dac_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) { - rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); + rme96_t *rme96 = snd_kcontrol_chip(kcontrol); uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; @@ -2339,13 +2210,12 @@ snd_rme96_dac_volume_info(snd_kcontrol_t static int snd_rme96_dac_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) { - rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme96_t *rme96 = snd_kcontrol_chip(kcontrol); - spin_lock_irqsave(&rme96->lock, flags); + spin_lock_irq(&rme96->lock); u->value.integer.value[0] = rme96->vol[0]; u->value.integer.value[1] = rme96->vol[1]; - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return 0; } @@ -2353,14 +2223,13 @@ snd_rme96_dac_volume_get(snd_kcontrol_t static int snd_rme96_dac_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u) { - rme96_t *rme96 = _snd_kcontrol_chip(kcontrol); - unsigned long flags; + rme96_t *rme96 = snd_kcontrol_chip(kcontrol); int change = 0; if (!RME96_HAS_ANALOG_OUT(rme96)) { return -EINVAL; } - spin_lock_irqsave(&rme96->lock, flags); + spin_lock_irq(&rme96->lock); if (u->value.integer.value[0] != rme96->vol[0]) { rme96->vol[0] = u->value.integer.value[0]; change = 1; @@ -2372,7 +2241,7 @@ snd_rme96_dac_volume_put(snd_kcontrol_t if (change) { snd_rme96_apply_dac_volume(rme96); } - spin_unlock_irqrestore(&rme96->lock, flags); + spin_unlock_irq(&rme96->lock); return change; } --- linux-2.6.9-rc1/sound/pci/sonicvibes.c 2004-08-24 00:03:18.000000000 -0700 +++ 25/sound/pci/sonicvibes.c 2004-09-02 20:51:53.000000000 -0700 @@ -44,8 +44,7 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("S3 SonicVibes PCI"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{S3,SonicVibes PCI}}"); +MODULE_SUPPORTED_DEVICE("{{S3,SonicVibes PCI}}"); #ifndef PCI_VENDOR_ID_S3 #define PCI_VENDOR_ID_S3 0x5333 @@ -64,22 +63,16 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for S3 SonicVibes soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for S3 SonicVibes soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable S3 SonicVibes soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(reverb, bool, boot_devs, 0444); MODULE_PARM_DESC(reverb, "Enable reverb (SRAM is present) for S3 SonicVibes soundcard."); -MODULE_PARM_SYNTAX(reverb, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); module_param_array(mge, bool, boot_devs, 0444); MODULE_PARM_DESC(mge, "MIC Gain Enable for S3 SonicVibes soundcard."); -MODULE_PARM_SYNTAX(mge, SNDRV_ENABLED "," SNDRV_ENABLE_DESC); module_param(dmaio, uint, 0444); MODULE_PARM_DESC(dmaio, "DDMA i/o base address for S3 SonicVibes soundcard."); -MODULE_PARM_SYNTAX(dmaio, "global," SNDRV_PORT_DESC); /* * Enhanced port direct registers @@ -207,7 +200,6 @@ MODULE_PARM_SYNTAX(dmaio, "global," SNDR */ typedef struct _snd_sonicvibes sonicvibes_t; -#define chip_t sonicvibes_t struct _snd_sonicvibes { unsigned long dma1size; @@ -215,13 +207,9 @@ struct _snd_sonicvibes { int irq; unsigned long sb_port; - struct resource *res_sb_port; unsigned long enh_port; - struct resource *res_enh_port; unsigned long synth_port; - struct resource *res_synth_port; unsigned long midi_port; - struct resource *res_midi_port; unsigned long game_port; unsigned int dmaa_port; struct resource *res_dmaa; @@ -599,7 +587,7 @@ static int snd_sonicvibes_trigger(sonicv static irqreturn_t snd_sonicvibes_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, dev_id, return IRQ_NONE); + sonicvibes_t *sonic = dev_id; unsigned char status; status = inb(SV_REG(sonic, STATUS)); @@ -689,7 +677,6 @@ static int snd_sonicvibes_hw_free(snd_pc static int snd_sonicvibes_playback_prepare(snd_pcm_substream_t * substream) { - unsigned long flags; sonicvibes_t *sonic = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; unsigned char fmt = 0; @@ -704,17 +691,16 @@ static int snd_sonicvibes_playback_prepa fmt |= 2; snd_sonicvibes_setfmt(sonic, ~3, fmt); snd_sonicvibes_set_dac_rate(sonic, runtime->rate); - spin_lock_irqsave(&sonic->reg_lock, flags); + spin_lock_irq(&sonic->reg_lock); snd_sonicvibes_setdmaa(sonic, runtime->dma_addr, size); snd_sonicvibes_out1(sonic, SV_IREG_DMA_A_UPPER, count >> 8); snd_sonicvibes_out1(sonic, SV_IREG_DMA_A_LOWER, count); - spin_unlock_irqrestore(&sonic->reg_lock, flags); + spin_unlock_irq(&sonic->reg_lock); return 0; } static int snd_sonicvibes_capture_prepare(snd_pcm_substream_t * substream) { - unsigned long flags; sonicvibes_t *sonic = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; unsigned char fmt = 0; @@ -730,11 +716,11 @@ static int snd_sonicvibes_capture_prepar fmt |= 0x20; snd_sonicvibes_setfmt(sonic, ~0x30, fmt); snd_sonicvibes_set_adc_rate(sonic, runtime->rate); - spin_lock_irqsave(&sonic->reg_lock, flags); + spin_lock_irq(&sonic->reg_lock); snd_sonicvibes_setdmac(sonic, runtime->dma_addr, size); snd_sonicvibes_out1(sonic, SV_IREG_DMA_C_UPPER, count >> 8); snd_sonicvibes_out1(sonic, SV_IREG_DMA_C_LOWER, count); - spin_unlock_irqrestore(&sonic->reg_lock, flags); + spin_unlock_irq(&sonic->reg_lock); return 0; } @@ -864,7 +850,7 @@ static snd_pcm_ops_t snd_sonicvibes_capt static void snd_sonicvibes_pcm_free(snd_pcm_t *pcm) { - sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, pcm->private_data, return); + sonicvibes_t *sonic = pcm->private_data; sonic->pcm = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -922,19 +908,17 @@ static int snd_sonicvibes_info_mux(snd_k static int snd_sonicvibes_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); - unsigned long flags; - spin_lock_irqsave(&sonic->reg_lock, flags); + spin_lock_irq(&sonic->reg_lock); ucontrol->value.enumerated.item[0] = ((snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ADC) & SV_RECSRC_OUT) >> 5) - 1; ucontrol->value.enumerated.item[1] = ((snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ADC) & SV_RECSRC_OUT) >> 5) - 1; - spin_unlock_irqrestore(&sonic->reg_lock, flags); + spin_unlock_irq(&sonic->reg_lock); return 0; } static int snd_sonicvibes_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); - unsigned long flags; unsigned short left, right, oval1, oval2; int change; @@ -943,7 +927,7 @@ static int snd_sonicvibes_put_mux(snd_kc return -EINVAL; left = (ucontrol->value.enumerated.item[0] + 1) << 5; right = (ucontrol->value.enumerated.item[1] + 1) << 5; - spin_lock_irqsave(&sonic->reg_lock, flags); + spin_lock_irq(&sonic->reg_lock); oval1 = snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ADC); oval2 = snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ADC); left = (oval1 & ~SV_RECSRC_OUT) | left; @@ -951,7 +935,7 @@ static int snd_sonicvibes_put_mux(snd_kc change = left != oval1 || right != oval2; snd_sonicvibes_out1(sonic, SV_IREG_LEFT_ADC, left); snd_sonicvibes_out1(sonic, SV_IREG_RIGHT_ADC, right); - spin_unlock_irqrestore(&sonic->reg_lock, flags); + spin_unlock_irq(&sonic->reg_lock); return change; } @@ -975,15 +959,14 @@ static int snd_sonicvibes_info_single(sn static int snd_sonicvibes_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); - unsigned long flags; int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; - spin_lock_irqsave(&sonic->reg_lock, flags); + spin_lock_irq(&sonic->reg_lock); ucontrol->value.integer.value[0] = (snd_sonicvibes_in1(sonic, reg)>> shift) & mask; - spin_unlock_irqrestore(&sonic->reg_lock, flags); + spin_unlock_irq(&sonic->reg_lock); if (invert) ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; return 0; @@ -992,7 +975,6 @@ static int snd_sonicvibes_get_single(snd static int snd_sonicvibes_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); - unsigned long flags; int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; int mask = (kcontrol->private_value >> 16) & 0xff; @@ -1004,12 +986,12 @@ static int snd_sonicvibes_put_single(snd if (invert) val = mask - val; val <<= shift; - spin_lock_irqsave(&sonic->reg_lock, flags); + spin_lock_irq(&sonic->reg_lock); oval = snd_sonicvibes_in1(sonic, reg); val = (oval & ~(mask << shift)) | val; change = val != oval; snd_sonicvibes_out1(sonic, reg, val); - spin_unlock_irqrestore(&sonic->reg_lock, flags); + spin_unlock_irq(&sonic->reg_lock); return change; } @@ -1033,7 +1015,6 @@ static int snd_sonicvibes_info_double(sn static int snd_sonicvibes_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); - unsigned long flags; int left_reg = kcontrol->private_value & 0xff; int right_reg = (kcontrol->private_value >> 8) & 0xff; int shift_left = (kcontrol->private_value >> 16) & 0x07; @@ -1041,10 +1022,10 @@ static int snd_sonicvibes_get_double(snd int mask = (kcontrol->private_value >> 24) & 0xff; int invert = (kcontrol->private_value >> 22) & 1; - spin_lock_irqsave(&sonic->reg_lock, flags); + spin_lock_irq(&sonic->reg_lock); ucontrol->value.integer.value[0] = (snd_sonicvibes_in1(sonic, left_reg) >> shift_left) & mask; ucontrol->value.integer.value[1] = (snd_sonicvibes_in1(sonic, right_reg) >> shift_right) & mask; - spin_unlock_irqrestore(&sonic->reg_lock, flags); + spin_unlock_irq(&sonic->reg_lock); if (invert) { ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; @@ -1055,7 +1036,6 @@ static int snd_sonicvibes_get_double(snd static int snd_sonicvibes_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); - unsigned long flags; int left_reg = kcontrol->private_value & 0xff; int right_reg = (kcontrol->private_value >> 8) & 0xff; int shift_left = (kcontrol->private_value >> 16) & 0x07; @@ -1073,7 +1053,7 @@ static int snd_sonicvibes_put_double(snd } val1 <<= shift_left; val2 <<= shift_right; - spin_lock_irqsave(&sonic->reg_lock, flags); + spin_lock_irq(&sonic->reg_lock); oval1 = snd_sonicvibes_in1(sonic, left_reg); oval2 = snd_sonicvibes_in1(sonic, right_reg); val1 = (oval1 & ~(mask << shift_left)) | val1; @@ -1081,12 +1061,10 @@ static int snd_sonicvibes_put_double(snd change = val1 != oval1 || val2 != oval2; snd_sonicvibes_out1(sonic, left_reg, val1); snd_sonicvibes_out1(sonic, right_reg, val2); - spin_unlock_irqrestore(&sonic->reg_lock, flags); + spin_unlock_irq(&sonic->reg_lock); return change; } -#define SONICVIBES_CONTROLS (sizeof(snd_sonicvibes_controls)/sizeof(snd_kcontrol_new_t)) - static snd_kcontrol_new_t snd_sonicvibes_controls[] __devinitdata = { SONICVIBES_DOUBLE("Capture Volume", 0, SV_IREG_LEFT_ADC, SV_IREG_RIGHT_ADC, 0, 0, 15, 0), SONICVIBES_DOUBLE("Aux Playback Switch", 0, SV_IREG_LEFT_AUX1, SV_IREG_RIGHT_AUX1, 7, 7, 1, 1), @@ -1113,7 +1091,7 @@ SONICVIBES_MUX("Capture Source", 0) static void snd_sonicvibes_master_free(snd_kcontrol_t *kcontrol) { - sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, _snd_kcontrol_chip(kcontrol), return); + sonicvibes_t *sonic = snd_kcontrol_chip(kcontrol); sonic->master_mute = NULL; sonic->master_volume = NULL; } @@ -1129,7 +1107,7 @@ static int __devinit snd_sonicvibes_mixe card = sonic->card; strcpy(card->mixername, "S3 SonicVibes"); - for (idx = 0; idx < SONICVIBES_CONTROLS; idx++) { + for (idx = 0; idx < ARRAY_SIZE(snd_sonicvibes_controls); idx++) { if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_sonicvibes_controls[idx], sonic))) < 0) return err; switch (idx) { @@ -1147,7 +1125,7 @@ static int __devinit snd_sonicvibes_mixe static void snd_sonicvibes_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) { - sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, entry->private_data, return); + sonicvibes_t *sonic = entry->private_data; unsigned char tmp; tmp = sonic->srs_space & 0x0f; @@ -1197,22 +1175,8 @@ static int snd_sonicvibes_free(sonicvibe #endif pci_write_config_dword(sonic->pci, 0x40, sonic->dmaa_port); pci_write_config_dword(sonic->pci, 0x48, sonic->dmac_port); - if (sonic->res_sb_port) { - release_resource(sonic->res_sb_port); - kfree_nocheck(sonic->res_sb_port); - } - if (sonic->res_enh_port) { - release_resource(sonic->res_enh_port); - kfree_nocheck(sonic->res_enh_port); - } - if (sonic->res_synth_port) { - release_resource(sonic->res_synth_port); - kfree_nocheck(sonic->res_synth_port); - } - if (sonic->res_midi_port) { - release_resource(sonic->res_midi_port); - kfree_nocheck(sonic->res_midi_port); - } + if (sonic->irq >= 0) + free_irq(sonic->irq, (void *)sonic); if (sonic->res_dmaa) { release_resource(sonic->res_dmaa); kfree_nocheck(sonic->res_dmaa); @@ -1221,15 +1185,14 @@ static int snd_sonicvibes_free(sonicvibe release_resource(sonic->res_dmac); kfree_nocheck(sonic->res_dmac); } - if (sonic->irq >= 0) - free_irq(sonic->irq, (void *)sonic); - snd_magic_kfree(sonic); + pci_release_regions(sonic->pci); + kfree(sonic); return 0; } static int snd_sonicvibes_dev_free(snd_device_t *device) { - sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, device->device_data, return -ENXIO); + sonicvibes_t *sonic = device->device_data; return snd_sonicvibes_free(sonic); } @@ -1257,38 +1220,25 @@ static int __devinit snd_sonicvibes_crea return -ENXIO; } - sonic = snd_magic_kcalloc(sonicvibes_t, 0, GFP_KERNEL); + sonic = kcalloc(1, sizeof(*sonic), GFP_KERNEL); if (sonic == NULL) return -ENOMEM; spin_lock_init(&sonic->reg_lock); sonic->card = card; sonic->pci = pci; sonic->irq = -1; - sonic->sb_port = pci_resource_start(pci, 0); - if ((sonic->res_sb_port = request_region(sonic->sb_port, 0x10, "S3 SonicVibes SB")) == NULL) { - snd_printk("unable to grab SB port at 0x%lx-0x%lx\n", sonic->sb_port, sonic->sb_port + 0x10 - 1); - snd_sonicvibes_free(sonic); - return -EBUSY; + + if ((err = pci_request_regions(pci, "S3 SonicVibes")) < 0) { + kfree(sonic); + return err; } + + sonic->sb_port = pci_resource_start(pci, 0); sonic->enh_port = pci_resource_start(pci, 1); - if ((sonic->res_enh_port = request_region(sonic->enh_port, 0x10, "S3 SonicVibes Enhanced")) == NULL) { - snd_printk("unable to grab PCM port at 0x%lx-0x%lx\n", sonic->enh_port, sonic->enh_port + 0x10 - 1); - snd_sonicvibes_free(sonic); - return -EBUSY; - } sonic->synth_port = pci_resource_start(pci, 2); - if ((sonic->res_synth_port = request_region(sonic->synth_port, 4, "S3 SonicVibes Synth")) == NULL) { - snd_printk("unable to grab synth port at 0x%lx-0x%lx\n", sonic->synth_port, sonic->synth_port + 4 - 1); - snd_sonicvibes_free(sonic); - return -EBUSY; - } sonic->midi_port = pci_resource_start(pci, 3); - if ((sonic->res_midi_port = request_region(sonic->midi_port, 4, "S3 SonicVibes Midi")) == NULL) { - snd_printk("unable to grab MIDI port at 0x%lx-0x%lx\n", sonic->midi_port, sonic->midi_port + 4 - 1); - snd_sonicvibes_free(sonic); - return -EBUSY; - } sonic->game_port = pci_resource_start(pci, 4); + if (request_irq(pci->irq, snd_sonicvibes_interrupt, SA_INTERRUPT|SA_SHIRQ, "S3 SonicVibes", (void *)sonic)) { snd_printk("unable to grab IRQ %d\n", pci->irq); snd_sonicvibes_free(sonic); @@ -1396,8 +1346,6 @@ static int __devinit snd_sonicvibes_crea * MIDI section */ -#define SONICVIBES_MIDI_CONTROLS (sizeof(snd_sonicvibes_midi_controls)/sizeof(snd_kcontrol_new_t)) - static snd_kcontrol_new_t snd_sonicvibes_midi_controls[] __devinitdata = { SONICVIBES_SINGLE("SonicVibes Wave Source RAM", 0, SV_IREG_WAVE_SOURCE, 0, 1, 0), SONICVIBES_SINGLE("SonicVibes Wave Source RAM+ROM", 0, SV_IREG_WAVE_SOURCE, 1, 1, 0), @@ -1408,20 +1356,20 @@ SONICVIBES_SINGLE("SonicVibes External T static int snd_sonicvibes_midi_input_open(mpu401_t * mpu) { - sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, mpu->private_data, return -EIO); + sonicvibes_t *sonic = mpu->private_data; outb(sonic->irqmask &= ~SV_MIDI_MASK, SV_REG(sonic, IRQMASK)); return 0; } static void snd_sonicvibes_midi_input_close(mpu401_t * mpu) { - sonicvibes_t *sonic = snd_magic_cast(sonicvibes_t, mpu->private_data, return); + sonicvibes_t *sonic = mpu->private_data; outb(sonic->irqmask |= SV_MIDI_MASK, SV_REG(sonic, IRQMASK)); } static int __devinit snd_sonicvibes_midi(sonicvibes_t * sonic, snd_rawmidi_t * rmidi) { - mpu401_t * mpu = snd_magic_cast(mpu401_t, rmidi->private_data, return -ENXIO); + mpu401_t * mpu = rmidi->private_data; snd_card_t *card = sonic->card; snd_rawmidi_str_t *dir; unsigned int idx; @@ -1431,7 +1379,7 @@ static int __devinit snd_sonicvibes_midi mpu->open_input = snd_sonicvibes_midi_input_open; mpu->close_input = snd_sonicvibes_midi_input_close; dir = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; - for (idx = 0; idx < SONICVIBES_MIDI_CONTROLS; idx++) + for (idx = 0; idx < ARRAY_SIZE(snd_sonicvibes_midi_controls); idx++) if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_sonicvibes_midi_controls[idx], sonic))) < 0) return err; return 0; --- linux-2.6.9-rc1/sound/pci/trident/trident.c 2004-08-24 00:01:50.000000000 -0700 +++ 25/sound/pci/trident/trident.c 2004-09-02 20:51:53.000000000 -0700 @@ -33,8 +33,7 @@ MODULE_AUTHOR("Jaroslav Kysela , "); MODULE_DESCRIPTION("Trident 4D-WaveDX/NX & SiS SI7018"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Trident,4DWave DX}," +MODULE_SUPPORTED_DEVICE("{{Trident,4DWave DX}," "{Trident,4DWave NX}," "{SiS,SI7018 PCI Audio}," "{Best Union,Miss Melody 4DWave PCI}," @@ -56,19 +55,14 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for Trident 4DWave PCI soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for Trident 4DWave PCI soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable Trident 4DWave PCI soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(pcm_channels, int, boot_devs, 0444); MODULE_PARM_DESC(pcm_channels, "Number of hardware channels assigned for PCM."); -MODULE_PARM_SYNTAX(pcm_channels, SNDRV_ENABLED ",default:32,allows:{{1,32}}"); module_param_array(wavetable_size, int, boot_devs, 0444); MODULE_PARM_DESC(wavetable_size, "Maximum memory size in kB for wavetable synth."); -MODULE_PARM_SYNTAX(wavetable_size, SNDRV_ENABLED ",default:8192,skill:advanced"); static struct pci_device_id snd_trident_ids[] = { { 0x1023, 0x2000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Trident 4DWave DX PCI Audio */ --- linux-2.6.9-rc1/sound/pci/trident/trident_main.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/sound/pci/trident/trident_main.c 2004-09-02 20:51:53.000000000 -0700 @@ -44,8 +44,6 @@ #include -#define chip_t trident_t - static int snd_trident_pcm_mixer_build(trident_t *trident, snd_trident_voice_t * voice, snd_pcm_substream_t *substream); static int snd_trident_pcm_mixer_free(trident_t *trident, snd_trident_voice_t * voice, snd_pcm_substream_t *substream); static irqreturn_t snd_trident_interrupt(int irq, void *dev_id, struct pt_regs *regs); @@ -119,7 +117,7 @@ static unsigned short snd_trident_codec_ unsigned int data = 0, treg; unsigned short count = 0xffff; unsigned long flags; - trident_t *trident = snd_magic_cast(trident_t, ac97->private_data, return -ENXIO); + trident_t *trident = ac97->private_data; spin_lock_irqsave(&trident->reg_lock, flags); if (trident->device == TRIDENT_DEVICE_ID_DX) { @@ -178,7 +176,7 @@ static void snd_trident_codec_write(ac97 unsigned int address, data; unsigned short count = 0xffff; unsigned long flags; - trident_t *trident = snd_magic_cast(trident_t, ac97->private_data, return); + trident_t *trident = ac97->private_data; data = ((unsigned long) wdata) << 16; @@ -908,7 +906,7 @@ static int snd_trident_playback_prepare( snd_trident_voice_t *evoice = voice->extra; snd_trident_pcm_mixer_t *mix = &trident->pcm_mixer[substream->number]; - spin_lock(&trident->reg_lock); + spin_lock_irq(&trident->reg_lock); /* set delta (rate) value */ voice->Delta = snd_trident_convert_rate(runtime->rate); @@ -969,7 +967,7 @@ static int snd_trident_playback_prepare( evoice->ESO = (runtime->period_size * 2) - 1; } - spin_unlock(&trident->reg_lock); + spin_unlock_irq(&trident->reg_lock); return 0; } @@ -1010,7 +1008,7 @@ static int snd_trident_capture_prepare(s snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; unsigned int val, ESO_bytes; - spin_lock(&trident->reg_lock); + spin_lock_irq(&trident->reg_lock); // Initilize the channel and set channel Mode outb(0, TRID_REG(trident, LEGACY_DMAR15)); @@ -1079,7 +1077,7 @@ static int snd_trident_capture_prepare(s snd_trident_write_voice_regs(trident, voice); - spin_unlock(&trident->reg_lock); + spin_unlock_irq(&trident->reg_lock); return 0; } @@ -1150,7 +1148,7 @@ static int snd_trident_si7018_capture_pr snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; snd_trident_voice_t *evoice = voice->extra; - spin_lock(&trident->reg_lock); + spin_lock_irq(&trident->reg_lock); voice->LBA = runtime->dma_addr; voice->Delta = snd_trident_convert_adc_rate(runtime->rate); @@ -1199,7 +1197,7 @@ static int snd_trident_si7018_capture_pr evoice->ESO = (runtime->period_size * 2) - 1; } - spin_unlock(&trident->reg_lock); + spin_unlock_irq(&trident->reg_lock); return 0; } @@ -1221,7 +1219,7 @@ static int snd_trident_foldback_prepare( snd_trident_voice_t *voice = (snd_trident_voice_t *) runtime->private_data; snd_trident_voice_t *evoice = voice->extra; - spin_lock(&trident->reg_lock); + spin_lock_irq(&trident->reg_lock); /* Set channel buffer Address */ if (voice->memblk) @@ -1276,7 +1274,7 @@ static int snd_trident_foldback_prepare( evoice->ESO = (runtime->period_size * 2) - 1; } - spin_unlock(&trident->reg_lock); + spin_unlock_irq(&trident->reg_lock); return 0; } @@ -1367,7 +1365,7 @@ static int snd_trident_spdif_prepare(snd unsigned int RESO, LBAO; unsigned int temp; - spin_lock(&trident->reg_lock); + spin_lock_irq(&trident->reg_lock); if (trident->device != TRIDENT_DEVICE_ID_SI7018) { @@ -1479,7 +1477,7 @@ static int snd_trident_spdif_prepare(snd outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); } - spin_unlock(&trident->reg_lock); + spin_unlock_irq(&trident->reg_lock); return 0; } @@ -1526,7 +1524,7 @@ static int snd_trident_trigger(snd_pcm_s val = inl(TRID_REG(trident, T4D_STIMER)) & 0x00ffffff; snd_pcm_group_for_each(pos, substream) { s = snd_pcm_group_substream_entry(pos); - if ((trident_t *) _snd_pcm_chip(s->pcm) == trident) { + if ((trident_t *) snd_pcm_substream_chip(s) == trident) { voice = (snd_trident_voice_t *) s->runtime->private_data; evoice = voice->extra; what |= 1 << (voice->number & 0x1f); @@ -2127,21 +2125,21 @@ static snd_pcm_ops_t snd_trident_spdif_7 ---------------------------------------------------------------------------*/ static void snd_trident_pcm_free(snd_pcm_t *pcm) { - trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return); + trident_t *trident = pcm->private_data; trident->pcm = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } static void snd_trident_foldback_pcm_free(snd_pcm_t *pcm) { - trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return); + trident_t *trident = pcm->private_data; trident->foldback = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } static void snd_trident_spdif_pcm_free(snd_pcm_t *pcm) { - trident_t *trident = snd_magic_cast(trident_t, pcm->private_data, return); + trident_t *trident = pcm->private_data; trident->spdif = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -2324,13 +2322,12 @@ static int snd_trident_spdif_control_get snd_ctl_elem_value_t * ucontrol) { trident_t *trident = snd_kcontrol_chip(kcontrol); - unsigned long flags; unsigned char val; - spin_lock_irqsave(&trident->reg_lock, flags); + spin_lock_irq(&trident->reg_lock); val = trident->spdif_ctrl; ucontrol->value.integer.value[0] = val == kcontrol->private_value; - spin_unlock_irqrestore(&trident->reg_lock, flags); + spin_unlock_irq(&trident->reg_lock); return 0; } @@ -2338,12 +2335,11 @@ static int snd_trident_spdif_control_put snd_ctl_elem_value_t * ucontrol) { trident_t *trident = snd_kcontrol_chip(kcontrol); - unsigned long flags; unsigned char val; int change; val = ucontrol->value.integer.value[0] ? (unsigned char) kcontrol->private_value : 0x00; - spin_lock_irqsave(&trident->reg_lock, flags); + spin_lock_irq(&trident->reg_lock); /* S/PDIF C Channel bits 0-31 : 48khz, SCMS disabled */ change = trident->spdif_ctrl != val; trident->spdif_ctrl = val; @@ -2362,7 +2358,7 @@ static int snd_trident_spdif_control_put outl(temp, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); } } - spin_unlock_irqrestore(&trident->reg_lock, flags); + spin_unlock_irq(&trident->reg_lock); return change; } @@ -2393,14 +2389,13 @@ static int snd_trident_spdif_default_get snd_ctl_elem_value_t * ucontrol) { trident_t *trident = snd_kcontrol_chip(kcontrol); - unsigned long flags; - spin_lock_irqsave(&trident->reg_lock, flags); + spin_lock_irq(&trident->reg_lock); ucontrol->value.iec958.status[0] = (trident->spdif_bits >> 0) & 0xff; ucontrol->value.iec958.status[1] = (trident->spdif_bits >> 8) & 0xff; ucontrol->value.iec958.status[2] = (trident->spdif_bits >> 16) & 0xff; ucontrol->value.iec958.status[3] = (trident->spdif_bits >> 24) & 0xff; - spin_unlock_irqrestore(&trident->reg_lock, flags); + spin_unlock_irq(&trident->reg_lock); return 0; } @@ -2408,7 +2403,6 @@ static int snd_trident_spdif_default_put snd_ctl_elem_value_t * ucontrol) { trident_t *trident = snd_kcontrol_chip(kcontrol); - unsigned long flags; unsigned int val; int change; @@ -2416,7 +2410,7 @@ static int snd_trident_spdif_default_put (ucontrol->value.iec958.status[1] << 8) | (ucontrol->value.iec958.status[2] << 16) | (ucontrol->value.iec958.status[3] << 24); - spin_lock_irqsave(&trident->reg_lock, flags); + spin_lock_irq(&trident->reg_lock); change = trident->spdif_bits != val; trident->spdif_bits = val; if (trident->device != TRIDENT_DEVICE_ID_SI7018) { @@ -2426,7 +2420,7 @@ static int snd_trident_spdif_default_put if (trident->spdif == NULL) outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS)); } - spin_unlock_irqrestore(&trident->reg_lock, flags); + spin_unlock_irq(&trident->reg_lock); return change; } @@ -2488,14 +2482,13 @@ static int snd_trident_spdif_stream_get( snd_ctl_elem_value_t * ucontrol) { trident_t *trident = snd_kcontrol_chip(kcontrol); - unsigned long flags; - spin_lock_irqsave(&trident->reg_lock, flags); + spin_lock_irq(&trident->reg_lock); ucontrol->value.iec958.status[0] = (trident->spdif_pcm_bits >> 0) & 0xff; ucontrol->value.iec958.status[1] = (trident->spdif_pcm_bits >> 8) & 0xff; ucontrol->value.iec958.status[2] = (trident->spdif_pcm_bits >> 16) & 0xff; ucontrol->value.iec958.status[3] = (trident->spdif_pcm_bits >> 24) & 0xff; - spin_unlock_irqrestore(&trident->reg_lock, flags); + spin_unlock_irq(&trident->reg_lock); return 0; } @@ -2503,7 +2496,6 @@ static int snd_trident_spdif_stream_put( snd_ctl_elem_value_t * ucontrol) { trident_t *trident = snd_kcontrol_chip(kcontrol); - unsigned long flags; unsigned int val; int change; @@ -2511,7 +2503,7 @@ static int snd_trident_spdif_stream_put( (ucontrol->value.iec958.status[1] << 8) | (ucontrol->value.iec958.status[2] << 16) | (ucontrol->value.iec958.status[3] << 24); - spin_lock_irqsave(&trident->reg_lock, flags); + spin_lock_irq(&trident->reg_lock); change = trident->spdif_pcm_bits != val; trident->spdif_pcm_bits = val; if (trident->spdif != NULL) { @@ -2521,7 +2513,7 @@ static int snd_trident_spdif_stream_put( outl(trident->spdif_bits, TRID_REG(trident, SI_SPDIF_CS)); } } - spin_unlock_irqrestore(&trident->reg_lock, flags); + spin_unlock_irq(&trident->reg_lock); return change; } @@ -2554,13 +2546,12 @@ static int snd_trident_ac97_control_get( snd_ctl_elem_value_t * ucontrol) { trident_t *trident = snd_kcontrol_chip(kcontrol); - unsigned long flags; unsigned char val; - spin_lock_irqsave(&trident->reg_lock, flags); + spin_lock_irq(&trident->reg_lock); val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); ucontrol->value.integer.value[0] = (val & (1 << kcontrol->private_value)) ? 1 : 0; - spin_unlock_irqrestore(&trident->reg_lock, flags); + spin_unlock_irq(&trident->reg_lock); return 0; } @@ -2568,11 +2559,10 @@ static int snd_trident_ac97_control_put( snd_ctl_elem_value_t * ucontrol) { trident_t *trident = snd_kcontrol_chip(kcontrol); - unsigned long flags; unsigned char val; int change = 0; - spin_lock_irqsave(&trident->reg_lock, flags); + spin_lock_irq(&trident->reg_lock); val = trident->ac97_ctrl = inl(TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); val &= ~(1 << kcontrol->private_value); if (ucontrol->value.integer.value[0]) @@ -2580,7 +2570,7 @@ static int snd_trident_ac97_control_put( change = val != trident->ac97_ctrl; trident->ac97_ctrl = val; outl(trident->ac97_ctrl = val, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); - spin_unlock_irqrestore(&trident->reg_lock, flags); + spin_unlock_irq(&trident->reg_lock); return change; } @@ -2624,19 +2614,18 @@ static int snd_trident_vol_control_get(s static int snd_trident_vol_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - unsigned long flags; trident_t *trident = snd_kcontrol_chip(kcontrol); unsigned int val; int change = 0; - spin_lock_irqsave(&trident->reg_lock, flags); + spin_lock_irq(&trident->reg_lock); val = trident->musicvol_wavevol; val &= ~(0xffff << kcontrol->private_value); val |= ((255 - (ucontrol->value.integer.value[0] & 0xff)) | ((255 - (ucontrol->value.integer.value[1] & 0xff)) << 8)) << kcontrol->private_value; change = val != trident->musicvol_wavevol; outl(trident->musicvol_wavevol = val, TRID_REG(trident, T4D_MUSICVOL_WAVEVOL)); - spin_unlock_irqrestore(&trident->reg_lock, flags); + spin_unlock_irq(&trident->reg_lock); return change; } @@ -2696,7 +2685,6 @@ static int snd_trident_pcm_vol_control_g static int snd_trident_pcm_vol_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - unsigned long flags; trident_t *trident = snd_kcontrol_chip(kcontrol); snd_trident_pcm_mixer_t *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)]; unsigned int val; @@ -2707,12 +2695,12 @@ static int snd_trident_pcm_vol_control_p } else { val = (255 - (ucontrol->value.integer.value[0] & 255)) << 2; } - spin_lock_irqsave(&trident->reg_lock, flags); + spin_lock_irq(&trident->reg_lock); change = val != mix->vol; mix->vol = val; if (mix->voice != NULL) snd_trident_write_vol_reg(trident, mix->voice, val); - spin_unlock_irqrestore(&trident->reg_lock, flags); + spin_unlock_irq(&trident->reg_lock); return change; } @@ -2760,7 +2748,6 @@ static int snd_trident_pcm_pan_control_g static int snd_trident_pcm_pan_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - unsigned long flags; trident_t *trident = snd_kcontrol_chip(kcontrol); snd_trident_pcm_mixer_t *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)]; unsigned char val; @@ -2770,12 +2757,12 @@ static int snd_trident_pcm_pan_control_p val = ucontrol->value.integer.value[0] & 0x3f; else val = (0x3f - (ucontrol->value.integer.value[0] & 0x3f)) | 0x40; - spin_lock_irqsave(&trident->reg_lock, flags); + spin_lock_irq(&trident->reg_lock); change = val != mix->pan; mix->pan = val; if (mix->voice != NULL) snd_trident_write_pan_reg(trident, mix->voice, val); - spin_unlock_irqrestore(&trident->reg_lock, flags); + spin_unlock_irq(&trident->reg_lock); return change; } @@ -2818,19 +2805,18 @@ static int snd_trident_pcm_rvol_control_ static int snd_trident_pcm_rvol_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - unsigned long flags; trident_t *trident = snd_kcontrol_chip(kcontrol); snd_trident_pcm_mixer_t *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)]; unsigned short val; int change = 0; val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f); - spin_lock_irqsave(&trident->reg_lock, flags); + spin_lock_irq(&trident->reg_lock); change = val != mix->rvol; mix->rvol = val; if (mix->voice != NULL) snd_trident_write_rvol_reg(trident, mix->voice, val); - spin_unlock_irqrestore(&trident->reg_lock, flags); + spin_unlock_irq(&trident->reg_lock); return change; } @@ -2873,19 +2859,18 @@ static int snd_trident_pcm_cvol_control_ static int snd_trident_pcm_cvol_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { - unsigned long flags; trident_t *trident = snd_kcontrol_chip(kcontrol); snd_trident_pcm_mixer_t *mix = &trident->pcm_mixer[snd_ctl_get_ioffnum(kcontrol, &ucontrol->id)]; unsigned short val; int change = 0; val = 0x7f - (ucontrol->value.integer.value[0] & 0x7f); - spin_lock_irqsave(&trident->reg_lock, flags); + spin_lock_irq(&trident->reg_lock); change = val != mix->cvol; mix->cvol = val; if (mix->voice != NULL) snd_trident_write_cvol_reg(trident, mix->voice, val); - spin_unlock_irqrestore(&trident->reg_lock, flags); + spin_unlock_irq(&trident->reg_lock); return change; } @@ -2961,21 +2946,21 @@ static int snd_trident_pcm_mixer_free(tr static int __devinit snd_trident_mixer(trident_t * trident, int pcm_spdif_device) { - ac97_bus_t _bus; - ac97_t _ac97; + ac97_template_t _ac97; snd_card_t * card = trident->card; snd_kcontrol_t *kctl; snd_ctl_elem_value_t *uctl; int idx, err, retries = 2; + static ac97_bus_ops_t ops = { + .write = snd_trident_codec_write, + .read = snd_trident_codec_read, + }; - uctl = (snd_ctl_elem_value_t *)snd_kcalloc(sizeof(*uctl), GFP_KERNEL); + uctl = kcalloc(1, sizeof(*uctl), GFP_KERNEL); if (!uctl) return -ENOMEM; - memset(&_bus, 0, sizeof(_bus)); - _bus.write = snd_trident_codec_write; - _bus.read = snd_trident_codec_read; - if ((err = snd_ac97_bus(trident->card, &_bus, &trident->ac97_bus)) < 0) + if ((err = snd_ac97_bus(trident->card, 0, &ops, NULL, &trident->ac97_bus)) < 0) goto __out; memset(&_ac97, 0, sizeof(_ac97)); @@ -3132,7 +3117,7 @@ static unsigned char snd_trident_gamepor trident_gameport_t *gp = (trident_gameport_t *)gameport; trident_t *chip; snd_assert(gp, return 0); - chip = snd_magic_cast(trident_t, gp->chip, return 0); + chip = gp->chip; return inb(TRID_REG(chip, GAMEPORT_LEGACY)); } @@ -3141,7 +3126,7 @@ static void snd_trident_gameport_trigger trident_gameport_t *gp = (trident_gameport_t *)gameport; trident_t *chip; snd_assert(gp, return); - chip = snd_magic_cast(trident_t, gp->chip, return); + chip = gp->chip; outb(0xff, TRID_REG(chip, GAMEPORT_LEGACY)); } @@ -3152,7 +3137,7 @@ static int snd_trident_gameport_cooked_r int i; snd_assert(gp, return 0); - chip = snd_magic_cast(trident_t, gp->chip, return 0); + chip = gp->chip; *buttons = (~inb(TRID_REG(chip, GAMEPORT_LEGACY)) >> 4) & 0xf; @@ -3169,7 +3154,7 @@ static int snd_trident_gameport_open(str trident_gameport_t *gp = (trident_gameport_t *)gameport; trident_t *chip; snd_assert(gp, return -1); - chip = snd_magic_cast(trident_t, gp->chip, return -1); + chip = gp->chip; switch (mode) { case GAMEPORT_MODE_COOKED: @@ -3280,7 +3265,7 @@ static int snd_trident_sis_reset(trident static void snd_trident_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) { - trident_t *trident = snd_magic_cast(trident_t, entry->private_data, return); + trident_t *trident = entry->private_data; char *s; switch (trident->device) { @@ -3331,7 +3316,7 @@ static void __devinit snd_trident_proc_i static int snd_trident_dev_free(snd_device_t *device) { - trident_t *trident = snd_magic_cast(trident_t, device->device_data, return -ENXIO); + trident_t *trident = device->device_data; return snd_trident_free(trident); } @@ -3354,7 +3339,8 @@ static int __devinit snd_trident_tlb_all /* TLB array must be aligned to 16kB !!! so we allocate 32kB region and correct offset when necessary */ - if (snd_dma_alloc_pages(&trident->dma_dev, 2 * SNDRV_TRIDENT_MAX_PAGES * 4, &trident->tlb.buffer) < 0) { + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci), + 2 * SNDRV_TRIDENT_MAX_PAGES * 4, &trident->tlb.buffer) < 0) { snd_printk(KERN_ERR "trident: unable to allocate TLB buffer\n"); return -ENOMEM; } @@ -3367,7 +3353,8 @@ static int __devinit snd_trident_tlb_all return -ENOMEM; } /* allocate and setup silent page and initialise TLB entries */ - if (snd_dma_alloc_pages(&trident->dma_dev, SNDRV_TRIDENT_PAGE_SIZE, &trident->tlb.silent_page) < 0) { + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci), + SNDRV_TRIDENT_PAGE_SIZE, &trident->tlb.silent_page) < 0) { snd_printk(KERN_ERR "trident: unable to allocate silent page\n"); return -ENOMEM; } @@ -3553,7 +3540,7 @@ int __devinit snd_trident_create(snd_car return -ENXIO; } - trident = snd_magic_kcalloc(trident_t, 0, GFP_KERNEL); + trident = kcalloc(1, sizeof(*trident), GFP_KERNEL); if (trident == NULL) return -ENOMEM; trident->device = (pci->vendor << 16) | pci->device; @@ -3570,18 +3557,17 @@ int __devinit snd_trident_create(snd_car if (max_wavetable_size < 0 ) max_wavetable_size = 0; trident->synth.max_size = max_wavetable_size * 1024; - trident->port = pci_resource_start(pci, 0); trident->irq = -1; trident->midi_port = TRID_REG(trident, T4D_MPU401_BASE); pci_set_master(pci); - trident->port = pci_resource_start(pci, 0); - if ((trident->res_port = request_region(trident->port, 0x100, "Trident Audio")) == NULL) { - snd_printk("unable to grab I/O region 0x%lx-0x%lx\n", trident->port, trident->port + 0x100 - 1); - snd_trident_free(trident); - return -EBUSY; + if ((err = pci_request_regions(pci, "Trident Audio")) < 0) { + kfree(trident); + return err; } + trident->port = pci_resource_start(pci, 0); + if (request_irq(pci->irq, snd_trident_interrupt, SA_INTERRUPT|SA_SHIRQ, "Trident Audio", (void *) trident)) { snd_printk("unable to grab IRQ %d\n", pci->irq); snd_trident_free(trident); @@ -3589,10 +3575,6 @@ int __devinit snd_trident_create(snd_car } trident->irq = pci->irq; - memset(&trident->dma_dev, 0, sizeof(trident->dma_dev)); - trident->dma_dev.type = SNDRV_DMA_TYPE_DEV; - trident->dma_dev.dev = snd_dma_pci_data(pci); - /* allocate 16k-aligned TLB for NX cards */ trident->tlb.entries = NULL; trident->tlb.buffer.area = NULL; @@ -3692,18 +3674,15 @@ int snd_trident_free(trident_t *trident) if (trident->tlb.memhdr) snd_util_memhdr_free(trident->tlb.memhdr); if (trident->tlb.silent_page.area) - snd_dma_free_pages(&trident->dma_dev, &trident->tlb.silent_page); + snd_dma_free_pages(&trident->tlb.silent_page); if (trident->tlb.shadow_entries) vfree(trident->tlb.shadow_entries); - snd_dma_free_pages(&trident->dma_dev, &trident->tlb.buffer); + snd_dma_free_pages(&trident->tlb.buffer); } if (trident->irq >= 0) free_irq(trident->irq, (void *)trident); - if (trident->res_port) { - release_resource(trident->res_port); - kfree_nocheck(trident->res_port); - } - snd_magic_kfree(trident); + pci_release_regions(trident->pci); + kfree(trident); return 0; } @@ -3727,7 +3706,7 @@ int snd_trident_free(trident_t *trident) static irqreturn_t snd_trident_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - trident_t *trident = snd_magic_cast(trident_t, dev_id, return IRQ_NONE); + trident_t *trident = dev_id; unsigned int audio_int, chn_int, stimer, channel, mask, tmp; int delta; snd_trident_voice_t *voice; @@ -3950,7 +3929,7 @@ void snd_trident_clear_voices(trident_t #ifdef CONFIG_PM static int snd_trident_suspend(snd_card_t *card, unsigned int state) { - trident_t *trident = snd_magic_cast(trident_t, card->pm_private_data, return -EINVAL); + trident_t *trident = card->pm_private_data; trident->in_suspend = 1; snd_pcm_suspend_all(trident->pcm); @@ -3976,7 +3955,7 @@ static int snd_trident_suspend(snd_card_ static int snd_trident_resume(snd_card_t *card, unsigned int state) { - trident_t *trident = snd_magic_cast(trident_t, card->pm_private_data, return -EINVAL); + trident_t *trident = card->pm_private_data; pci_enable_device(trident->pci); if (pci_set_dma_mask(trident->pci, 0x3fffffff) < 0 || --- linux-2.6.9-rc1/sound/pci/trident/trident_memory.c 2004-08-24 00:03:17.000000000 -0700 +++ 25/sound/pci/trident/trident_memory.c 2004-09-02 20:51:53.000000000 -0700 @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -189,7 +190,7 @@ snd_trident_alloc_sg_pages(trident_t *tr snd_util_memblk_t *blk; snd_pcm_runtime_t *runtime = substream->runtime; int idx, page; - struct snd_sg_buf *sgbuf = runtime->dma_private; + struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream); snd_assert(runtime->dma_bytes > 0 && runtime->dma_bytes <= SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL); hdr = trident->tlb.memhdr; @@ -274,7 +275,7 @@ snd_trident_alloc_pages(trident_t *tride { snd_assert(trident != NULL, return NULL); snd_assert(substream != NULL, return NULL); - if (substream->dma_device.type == SNDRV_DMA_TYPE_DEV_SG) + if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_SG) return snd_trident_alloc_sg_pages(trident, substream); else return snd_trident_alloc_cont_pages(trident, substream); @@ -367,10 +368,12 @@ static void clear_tlb(trident_t *trident set_silent_tlb(trident, page); if (ptr) { struct snd_dma_buffer dmab; + dmab.dev.type = SNDRV_DMA_TYPE_DEV; + dmab.dev.dev = snd_dma_pci_data(trident->pci); dmab.area = ptr; dmab.addr = addr; dmab.bytes = ALIGN_PAGE_SIZE; - snd_dma_free_pages(&trident->dma_dev, &dmab); + snd_dma_free_pages(&dmab); } } @@ -412,10 +415,11 @@ static int synth_alloc_pages(trident_t * * fortunately Trident page size and kernel PAGE_SIZE is identical! */ for (page = first_page; page <= last_page; page++) { - if (snd_dma_alloc_pages(&hw->dma_dev, ALIGN_PAGE_SIZE, &dmab) < 0) + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(hw->pci), + ALIGN_PAGE_SIZE, &dmab) < 0) goto __fail; if (! is_valid_page(dmab.addr)) { - snd_dma_free_pages(&hw->dma_dev, &dmab); + snd_dma_free_pages(&dmab); goto __fail; } set_tlb_bus(hw, page, (unsigned long)dmab.area, dmab.addr); --- linux-2.6.9-rc1/sound/pci/trident/trident_synth.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/sound/pci/trident/trident_synth.c 2004-09-02 20:51:53.000000000 -0700 @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -506,7 +507,7 @@ static void sample_private1(trident_t * static int snd_trident_simple_put_sample(void *private_data, simple_instrument_t * instr, char __user *data, long len, int atomic) { - trident_t *trident = snd_magic_cast(trident_t, private_data, return -ENXIO); + trident_t *trident = private_data; int size = instr->size; int shift = 0; @@ -540,12 +541,12 @@ static int snd_trident_simple_put_sample instr->address.memory = memblk->offset; } else { struct snd_dma_buffer dmab; - - if (snd_dma_alloc_pages(&trident->dma_dev, size, &dmab) < 0) + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(trident->pci), + size, &dmab) < 0) return -ENOMEM; if (copy_from_user(dmab.area, data, size)) { - snd_dma_free_pages(&trident->dma_dev, &dmab); + snd_dma_free_pages(&dmab); return -EFAULT; } instr->address.ptr = dmab.area; @@ -559,7 +560,7 @@ static int snd_trident_simple_put_sample static int snd_trident_simple_get_sample(void *private_data, simple_instrument_t * instr, char __user *data, long len, int atomic) { - //trident_t *trident = snd_magic_cast(trident_t, private_data, return -ENXIO); + //trident_t *trident = private_data; int size = instr->size; int shift = 0; @@ -580,9 +581,14 @@ static int snd_trident_simple_get_sample static int snd_trident_simple_remove_sample(void *private_data, simple_instrument_t * instr, int atomic) { - trident_t *trident = snd_magic_cast(trident_t, private_data, return -ENXIO); + trident_t *trident = private_data; int size = instr->size; + if (instr->format & SIMPLE_WAVE_16BIT) + size <<= 1; + if (instr->format & SIMPLE_WAVE_STEREO) + size <<= 1; + if (trident->tlb.entries) { snd_util_memblk_t *memblk = (snd_util_memblk_t*)instr->address.ptr; if (memblk) @@ -590,14 +596,15 @@ static int snd_trident_simple_remove_sam else return -EFAULT; } else { - kfree(instr->address.ptr); + struct snd_dma_buffer dmab; + dmab.dev.type = SNDRV_DMA_TYPE_DEV; + dmab.dev.dev = snd_dma_pci_data(trident->pci); + dmab.area = instr->address.ptr; + dmab.addr = instr->address.memory; + dmab.bytes = size; + snd_dma_free_pages(&dmab); } - if (instr->format & SIMPLE_WAVE_16BIT) - size <<= 1; - if (instr->format & SIMPLE_WAVE_STEREO) - size <<= 1; - trident->synth.current_size -= size; if (trident->synth.current_size < 0) /* shouldn't need this check... */ trident->synth.current_size = 0; @@ -838,7 +845,7 @@ static void snd_trident_synth_instr_noti int what) { int idx; - trident_t *trident = snd_magic_cast(trident_t, private_data, return); + trident_t *trident = private_data; snd_trident_voice_t *pvoice; unsigned long flags; --- linux-2.6.9-rc1/sound/pci/via82xx.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/sound/pci/via82xx.c 2004-09-02 20:51:53.000000000 -0700 @@ -67,8 +67,7 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("VIA VT82xx audio"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{VIA,VT82C686A/B/C,pci},{VIA,VT8233A/C,8235}}"); +MODULE_SUPPORTED_DEVICE("{{VIA,VT82C686A/B/C,pci},{VIA,VT8233A/C,8235}}"); #if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) #define SUPPORT_JOYSTICK 1 @@ -88,30 +87,22 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for VIA 82xx bridge."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for VIA 82xx bridge."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable audio part of VIA 82xx bridge."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(mpu_port, long, boot_devs, 0444); MODULE_PARM_DESC(mpu_port, "MPU-401 port. (VT82C686x only)"); -MODULE_PARM_SYNTAX(mpu_port, SNDRV_PORT_DESC); #ifdef SUPPORT_JOYSTICK module_param_array(joystick, bool, boot_devs, 0444); MODULE_PARM_DESC(joystick, "Enable joystick. (VT82C686x only)"); -MODULE_PARM_SYNTAX(joystick, SNDRV_ENABLE_DESC "," SNDRV_BOOLEAN_FALSE_DESC); #endif module_param_array(ac97_clock, int, boot_devs, 0444); MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz)."); -MODULE_PARM_SYNTAX(ac97_clock, SNDRV_ENABLED ",default:48000"); module_param_array(ac97_quirk, int, boot_devs, 0444); MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware."); -MODULE_PARM_SYNTAX(ac97_quirk, SNDRV_ENABLED ",allows:{{-1,4}},dialog:list,default:-1"); module_param_array(dxs_support, int, boot_devs, 0444); MODULE_PARM_DESC(dxs_support, "Support for DXS channels (0 = auto, 1 = enable, 2 = disable, 3 = 48k only, 4 = no VRA)"); -MODULE_PARM_SYNTAX(dxs_support, SNDRV_ENABLED ",allows:{{0,4}},dialog:list"); /* pci ids */ @@ -318,7 +309,6 @@ DEFINE_VIA_REGSET(CAPTURE_8233, 0x60); typedef struct _snd_via82xx via82xx_t; typedef struct via_dev viadev_t; -#define chip_t via82xx_t /* * pcm stream @@ -362,7 +352,6 @@ struct _snd_via82xx { int irq; unsigned long port; - struct resource *res_port; struct resource *mpu_res; int chip_type; unsigned char revision; @@ -375,7 +364,6 @@ struct _snd_via82xx { unsigned char spdif_ctrl_saved; unsigned char capture_src_saved[2]; unsigned int mpu_port_saved; - u32 pci_state[16]; #endif unsigned char playback_volume[4][2]; /* for VIA8233/C/8235; default = 0 */ @@ -405,8 +393,6 @@ struct _snd_via82xx { spinlock_t ac97_lock; snd_info_entry_t *proc_entry; - struct snd_dma_device dma_dev; - #ifdef SUPPORT_JOYSTICK struct gameport gameport; struct resource *res_joystick; @@ -441,7 +427,9 @@ static int build_via_table(viadev_t *dev /* the start of each lists must be aligned to 8 bytes, * but the kernel pages are much bigger, so we don't care */ - if (snd_dma_alloc_pages(&chip->dma_dev, PAGE_ALIGN(VIA_TABLE_SIZE * 2 * 8), &dev->table) < 0) + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), + PAGE_ALIGN(VIA_TABLE_SIZE * 2 * 8), + &dev->table) < 0) return -ENOMEM; } if (! dev->idx_table) { @@ -497,9 +485,8 @@ static int build_via_table(viadev_t *dev static int clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream, struct pci_dev *pci) { - via82xx_t *chip = snd_pcm_substream_chip(substream); if (dev->table.area) { - snd_dma_free_pages(&chip->dma_dev, &dev->table); + snd_dma_free_pages(&dev->table); dev->table.area = NULL; } if (dev->idx_table) { @@ -556,7 +543,7 @@ static int snd_via82xx_codec_valid(via82 static void snd_via82xx_codec_wait(ac97_t *ac97) { - via82xx_t *chip = snd_magic_cast(via82xx_t, ac97->private_data, return); + via82xx_t *chip = ac97->private_data; int err; err = snd_via82xx_codec_ready(chip, ac97->num); /* here we need to wait fairly for long time.. */ @@ -568,7 +555,7 @@ static void snd_via82xx_codec_write(ac97 unsigned short reg, unsigned short val) { - via82xx_t *chip = snd_magic_cast(via82xx_t, ac97->private_data, return); + via82xx_t *chip = ac97->private_data; unsigned int xval; xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY; @@ -583,7 +570,7 @@ static void snd_via82xx_codec_write(ac97 static unsigned short snd_via82xx_codec_read(ac97_t *ac97, unsigned short reg) { - via82xx_t *chip = snd_magic_cast(via82xx_t, ac97->private_data, return ~0); + via82xx_t *chip = ac97->private_data; unsigned int xval, val = 0xffff; int again = 0; @@ -632,11 +619,10 @@ static void snd_via82xx_channel_reset(vi static irqreturn_t snd_via82xx_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - via82xx_t *chip = snd_magic_cast(via82xx_t, dev_id, return IRQ_NONE); + via82xx_t *chip = dev_id; unsigned int status; unsigned int i; - spin_lock(&chip->reg_lock); #if 0 /* FIXME: does it work on via823x? */ if (chip->chip_type != TYPE_VIA686) @@ -644,7 +630,6 @@ static irqreturn_t snd_via82xx_interrupt #endif status = inl(VIAREG(chip, SGD_SHADOW)); if (! (status & chip->intr_mask)) { - spin_unlock(&chip->reg_lock); if (chip->rmidi) /* check mpu401 interrupt */ return snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); @@ -653,6 +638,7 @@ static irqreturn_t snd_via82xx_interrupt // _skip_sgd: /* check status for each stream */ + spin_lock(&chip->reg_lock); for (i = 0; i < chip->num_devs; i++) { viadev_t *viadev = &chip->devs[i]; unsigned char c_status = inb(VIADEV_REG(viadev, OFFSET_STATUS)); @@ -720,6 +706,10 @@ static int snd_via82xx_pcm_trigger(snd_p /* * calculate the linear position at the given sg-buffer index and the rest count */ + +#define check_invalid_pos(viadev,pos) \ + ((pos) < viadev->lastpos && ((pos) >= viadev->bufsize2 || viadev->lastpos < viadev->bufsize2)) + static inline unsigned int calc_linear_pos(viadev_t *viadev, unsigned int idx, unsigned int count) { unsigned int size, res; @@ -728,17 +718,24 @@ static inline unsigned int calc_linear_p res = viadev->idx_table[idx].offset + size - count; /* check the validity of the calculated position */ - if (size < count || (res < viadev->lastpos && (res >= viadev->bufsize2 || viadev->lastpos < viadev->bufsize2))) { + if (size < count) { + snd_printd(KERN_ERR "invalid via82xx_cur_ptr (size = %d, count = %d)\n", (int)size, (int)count); + res = viadev->lastpos; + } else if (check_invalid_pos(viadev, res)) { #ifdef POINTER_DEBUG printk("fail: idx = %i/%i, lastpos = 0x%x, bufsize2 = 0x%x, offsize = 0x%x, size = 0x%x, count = 0x%x\n", idx, viadev->tbl_entries, viadev->lastpos, viadev->bufsize2, viadev->idx_table[idx].offset, viadev->idx_table[idx].size, count); #endif - /* count register returns full size when end of buffer is reached */ - if (size != count) { + if (count && size < count) { snd_printd(KERN_ERR "invalid via82xx_cur_ptr, using last valid pointer\n"); res = viadev->lastpos; } else { - res = viadev->idx_table[idx].offset + size; - if (res < viadev->lastpos && (res >= viadev->bufsize2 || viadev->lastpos < viadev->bufsize2)) { + if (! count) + /* bogus count 0 on the DMA boundary? */ + res = viadev->idx_table[idx].offset; + else + /* count register returns full size when end of buffer is reached */ + res = viadev->idx_table[idx].offset + size; + if (check_invalid_pos(viadev, res)) { snd_printd(KERN_ERR "invalid via82xx_cur_ptr (2), using last valid pointer\n"); res = viadev->lastpos; } @@ -904,7 +901,7 @@ static int via_lock_rate(struct via_rate { int changed = 0; - spin_lock(&rec->lock); + spin_lock_irq(&rec->lock); if (rec->rate != rate) { if (rec->rate && rec->used > 1) /* already set */ changed = -EINVAL; @@ -913,7 +910,7 @@ static int via_lock_rate(struct via_rate changed = 1; } } - spin_unlock(&rec->lock); + spin_unlock_irq(&rec->lock); return changed; } @@ -1035,6 +1032,7 @@ static snd_pcm_hardware_t snd_via82xx_hw .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_PAUSE), .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_48000, @@ -1058,32 +1056,18 @@ static int snd_via82xx_pcm_open(via82xx_ { snd_pcm_runtime_t *runtime = substream->runtime; int err; - unsigned long flags; struct via_rate_lock *ratep; - struct ratetbl { - int rate; - unsigned int bit; - } ratebits[] = { - {8000, SNDRV_PCM_RATE_8000}, - {11025, SNDRV_PCM_RATE_11025}, - {16000, SNDRV_PCM_RATE_16000}, - {22050, SNDRV_PCM_RATE_22050}, - {32000, SNDRV_PCM_RATE_32000}, - {44100, SNDRV_PCM_RATE_44100}, - {48000, SNDRV_PCM_RATE_48000}, - }; - int i; runtime->hw = snd_via82xx_hw; /* set the hw rate condition */ ratep = &chip->rates[viadev->direction]; - spin_lock_irqsave(&ratep->lock, flags); + spin_lock_irq(&ratep->lock); ratep->used++; - if (chip->spdif_on) { - runtime->hw.rates = SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000; - runtime->hw.rate_min = 32000; - runtime->hw.rate_max = 48000; + if (chip->spdif_on && viadev->reg_offset == 0x30) { + /* DXS#3 and spdif is on */ + runtime->hw.rates = chip->ac97->rates[AC97_RATES_SPDIF]; + snd_pcm_limit_hw_rates(runtime); } else if (chip->dxs_fixed && viadev->reg_offset < 0x40) { /* fixed DXS playback rate */ runtime->hw.rates = SNDRV_PCM_RATE_48000; @@ -1091,30 +1075,13 @@ static int snd_via82xx_pcm_open(via82xx_ } else if (! ratep->rate) { int idx = viadev->direction ? AC97_RATES_ADC : AC97_RATES_FRONT_DAC; runtime->hw.rates = chip->ac97->rates[idx]; - for (i = 0; i < (int)ARRAY_SIZE(ratebits); i++) { - if (runtime->hw.rates & ratebits[i].bit) { - runtime->hw.rate_min = ratebits[i].rate; - break; - } - } - for (i = ARRAY_SIZE(ratebits) - 1; i >= 0; i--) { - if (runtime->hw.rates & ratebits[i].bit) { - runtime->hw.rate_max = ratebits[i].rate; - break; - } - } + snd_pcm_limit_hw_rates(runtime); } else { /* a fixed rate */ runtime->hw.rates = SNDRV_PCM_RATE_KNOT; - for (i = 0; i < (int)ARRAY_SIZE(ratebits); i++) { - if (ratep->rate == ratebits[i].rate) { - runtime->hw.rates = ratebits[i].bit; - break; - } - } runtime->hw.rate_max = runtime->hw.rate_min = ratep->rate; } - spin_unlock_irqrestore(&ratep->lock, flags); + spin_unlock_irq(&ratep->lock); /* we may remove following constaint when we modify table entries in interrupt */ @@ -1188,16 +1155,15 @@ static int snd_via82xx_pcm_close(snd_pcm { via82xx_t *chip = snd_pcm_substream_chip(substream); viadev_t *viadev = (viadev_t *)substream->runtime->private_data; - unsigned long flags; struct via_rate_lock *ratep; /* release the rate lock */ ratep = &chip->rates[viadev->direction]; - spin_lock_irqsave(&ratep->lock, flags); + spin_lock_irq(&ratep->lock); ratep->used--; if (! ratep->used) ratep->rate = 0; - spin_unlock_irqrestore(&ratep->lock, flags); + spin_unlock_irq(&ratep->lock); viadev->substream = NULL; return 0; @@ -1363,6 +1329,10 @@ static int __devinit snd_via8233a_pcm_ne snd_dma_pci_data(chip->pci), 64*1024, 128*1024)) < 0) return err; + /* SPDIF supported? */ + if (! ac97_can_spdif(chip->ac97)) + return 0; + /* PCM #1: DXS3 playback (for spdif) */ err = snd_pcm_new(chip->card, chip->card->shortname, 1, 1, 0, &pcm); if (err < 0) @@ -1446,17 +1416,16 @@ static int snd_via8233_capture_source_pu { via82xx_t *chip = snd_kcontrol_chip(kcontrol); unsigned long port = chip->port + (kcontrol->id.index ? (VIA_REG_CAPTURE_CHANNEL + 0x10) : VIA_REG_CAPTURE_CHANNEL); - unsigned long flags; u8 val, oval; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); oval = inb(port); val = oval & ~VIA_REG_CAPTURE_CHANNEL_MIC; if (ucontrol->value.enumerated.item[0]) val |= VIA_REG_CAPTURE_CHANNEL_MIC; if (val != oval) outb(val, port); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return val != oval; } @@ -1567,13 +1536,13 @@ static snd_kcontrol_new_t snd_via8233_dx static void snd_via82xx_mixer_free_ac97_bus(ac97_bus_t *bus) { - via82xx_t *chip = snd_magic_cast(via82xx_t, bus->private_data, return); + via82xx_t *chip = bus->private_data; chip->ac97_bus = NULL; } static void snd_via82xx_mixer_free_ac97(ac97_t *ac97) { - via82xx_t *chip = snd_magic_cast(via82xx_t, ac97->private_data, return); + via82xx_t *chip = ac97->private_data; chip->ac97 = NULL; } @@ -1585,6 +1554,18 @@ static struct ac97_quirk ac97_quirks[] = .type = AC97_TUNE_HP_ONLY }, { + .vendor = 0x1019, + .device = 0x0a81, + .name = "ECS K7VTA3", + .type = AC97_TUNE_HP_ONLY + }, + { + .vendor = 0x1019, + .device = 0x0a85, + .name = "ECS L7VMM2", + .type = AC97_TUNE_HP_ONLY + }, + { .vendor = 0x1849, .device = 0x3059, .name = "ASRock K7VM2", @@ -1613,19 +1594,18 @@ static struct ac97_quirk ac97_quirks[] = static int __devinit snd_via82xx_mixer_new(via82xx_t *chip, int ac97_quirk) { - ac97_bus_t bus; - ac97_t ac97; + ac97_template_t ac97; int err; + static ac97_bus_ops_t ops = { + .write = snd_via82xx_codec_write, + .read = snd_via82xx_codec_read, + .wait = snd_via82xx_codec_wait, + }; - memset(&bus, 0, sizeof(bus)); - bus.write = snd_via82xx_codec_write; - bus.read = snd_via82xx_codec_read; - bus.wait = snd_via82xx_codec_wait; - bus.private_data = chip; - bus.private_free = snd_via82xx_mixer_free_ac97_bus; - bus.clock = chip->ac97_clock; - if ((err = snd_ac97_bus(chip->card, &bus, &chip->ac97_bus)) < 0) + if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0) return err; + chip->ac97_bus->private_free = snd_via82xx_mixer_free_ac97_bus; + chip->ac97_bus->clock = chip->ac97_clock; memset(&ac97, 0, sizeof(ac97)); ac97.private_data = chip; @@ -1653,6 +1633,8 @@ static int snd_via8233_init_misc(via82xx int i, err, caps; unsigned char val; + pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, + chip->old_legacy & ~(VIA_FUNC_ENABLE_SB|VIA_FUNC_ENABLE_FM)); caps = chip->chip_type == TYPE_VIA8233A ? 1 : 2; for (i = 0; i < caps; i++) { snd_via8233_capture_source.index = i; @@ -1660,9 +1642,11 @@ static int snd_via8233_init_misc(via82xx if (err < 0) return err; } - err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_dxs3_spdif_control, chip)); - if (err < 0) - return err; + if (ac97_can_spdif(chip->ac97)) { + err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_dxs3_spdif_control, chip)); + if (err < 0) + return err; + } if (chip->chip_type != TYPE_VIA8233A) { err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_via8233_dxs_volume_control, chip)); if (err < 0) @@ -1672,6 +1656,7 @@ static int snd_via8233_init_misc(via82xx /* select spdif data slot 10/11 */ pci_read_config_byte(chip->pci, VIA8233_SPDIF_CTRL, &val); val = (val & ~VIA8233_SPDIF_SLOT_MASK) | VIA8233_SPDIF_SLOT_1011; + val &= ~VIA8233_SPDIF_DX3; /* SPDIF off as default */ pci_write_config_byte(chip->pci, VIA8233_SPDIF_CTRL, val); return 0; @@ -1686,6 +1671,7 @@ static int snd_via686_init_misc(via82xx_ legacy_cfg = chip->old_legacy_cfg; legacy |= VIA_FUNC_MIDI_IRQMASK; /* FIXME: correct? (disable MIDI) */ legacy &= ~VIA_FUNC_ENABLE_GAME; /* disable joystick */ + legacy &= ~(VIA_FUNC_ENABLE_SB|VIA_FUNC_ENABLE_FM); /* diable SB & FM */ if (chip->revision >= VIA_REV_686_H) { rev_h = 1; if (mpu_port[dev] >= 0x200) { /* force MIDI */ @@ -1766,7 +1752,7 @@ static int snd_via686_init_misc(via82xx_ */ static void snd_via82xx_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) { - via82xx_t *chip = snd_magic_cast(via82xx_t, entry->private_data, return); + via82xx_t *chip = entry->private_data; int i; snd_iprintf(buffer, "%s\n\n", chip->card->longname); @@ -1903,7 +1889,7 @@ static int __devinit snd_via82xx_chip_in */ static int snd_via82xx_suspend(snd_card_t *card, unsigned int state) { - via82xx_t *chip = snd_magic_cast(via82xx_t, card->pm_private_data, return -EINVAL); + via82xx_t *chip = card->pm_private_data; int i; for (i = 0; i < 2; i++) @@ -1921,7 +1907,6 @@ static int snd_via82xx_suspend(snd_card_ chip->capture_src_saved[1] = inb(chip->port + VIA_REG_CAPTURE_CHANNEL + 0x10); } - pci_save_state(chip->pci, chip->pci_state); pci_set_power_state(chip->pci, 3); pci_disable_device(chip->pci); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); @@ -1930,11 +1915,10 @@ static int snd_via82xx_suspend(snd_card_ static int snd_via82xx_resume(snd_card_t *card, unsigned int state) { - via82xx_t *chip = snd_magic_cast(via82xx_t, card->pm_private_data, return -EINVAL); + via82xx_t *chip = card->pm_private_data; int idx, i; pci_enable_device(chip->pci); - pci_restore_state(chip->pci, chip->pci_state); pci_set_power_state(chip->pci, 0); snd_via82xx_chip_init(chip); @@ -1976,16 +1960,13 @@ static int snd_via82xx_free(via82xx_t *c snd_via82xx_channel_reset(chip, &chip->devs[i]); synchronize_irq(chip->irq); __end_hw: + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); if (chip->mpu_res) { release_resource(chip->mpu_res); kfree_nocheck(chip->mpu_res); } - if (chip->res_port) { - release_resource(chip->res_port); - kfree_nocheck(chip->res_port); - } - if (chip->irq >= 0) - free_irq(chip->irq, (void *)chip); + pci_release_regions(chip->pci); if (chip->chip_type == TYPE_VIA686) { #ifdef SUPPORT_JOYSTICK if (chip->res_joystick) { @@ -1997,13 +1978,13 @@ static int snd_via82xx_free(via82xx_t *c pci_write_config_byte(chip->pci, VIA_FUNC_ENABLE, chip->old_legacy); pci_write_config_byte(chip->pci, VIA_PNP_CONTROL, chip->old_legacy_cfg); } - snd_magic_kfree(chip); + kfree(chip); return 0; } static int snd_via82xx_dev_free(snd_device_t *device) { - via82xx_t *chip = snd_magic_cast(via82xx_t, device->device_data, return -ENXIO); + via82xx_t *chip = device->device_data; return snd_via82xx_free(chip); } @@ -2023,7 +2004,7 @@ static int __devinit snd_via82xx_create( if ((err = pci_enable_device(pci)) < 0) return err; - if ((chip = snd_magic_kcalloc(via82xx_t, 0, GFP_KERNEL)) == NULL) + if ((chip = kcalloc(1, sizeof(*chip), GFP_KERNEL)) == NULL) return -ENOMEM; chip->chip_type = chip_type; @@ -2037,19 +2018,14 @@ static int __devinit snd_via82xx_create( chip->pci = pci; chip->irq = -1; - memset(&chip->dma_dev, 0, sizeof(chip->dma_dev)); - chip->dma_dev.type = SNDRV_DMA_TYPE_DEV; - chip->dma_dev.dev = snd_dma_pci_data(pci); - pci_read_config_byte(pci, VIA_FUNC_ENABLE, &chip->old_legacy); pci_read_config_byte(pci, VIA_PNP_CONTROL, &chip->old_legacy_cfg); - chip->port = pci_resource_start(pci, 0); - if ((chip->res_port = request_region(chip->port, 256, card->driver)) == NULL) { - snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->port, chip->port + 256 - 1); - snd_via82xx_free(chip); - return -EBUSY; + if ((err = pci_request_regions(pci, card->driver)) < 0) { + kfree(chip); + return err; } + chip->port = pci_resource_start(pci, 0); if (request_irq(pci->irq, snd_via82xx_interrupt, SA_INTERRUPT|SA_SHIRQ, card->driver, (void *)chip)) { snd_printk("unable to grab IRQ %d\n", pci->irq); @@ -2111,9 +2087,12 @@ static int __devinit check_dxs_list(stru { .vendor = 0x1005, .device = 0x4710, .action = VIA_DXS_ENABLE }, /* Avance Logic Mobo */ { .vendor = 0x1019, .device = 0x0996, .action = VIA_DXS_48K }, { .vendor = 0x1019, .device = 0x0a81, .action = VIA_DXS_NO_VRA }, /* ECS K7VTA3 v8.0 */ + { .vendor = 0x1019, .device = 0x0a85, .action = VIA_DXS_NO_VRA }, /* ECS L7VMM2 */ + { .vendor = 0x1025, .device = 0x0033, .action = VIA_DXS_NO_VRA }, /* Acer Inspire 1353LM */ { .vendor = 0x1043, .device = 0x8095, .action = VIA_DXS_NO_VRA }, /* ASUS A7V8X (FIXME: possibly VIA_DXS_ENABLE?)*/ { .vendor = 0x1043, .device = 0x80a1, .action = VIA_DXS_NO_VRA }, /* ASUS A7V8-X */ { .vendor = 0x1043, .device = 0x80b0, .action = VIA_DXS_NO_VRA }, /* ASUS A7V600 & K8V*/ + { .vendor = 0x1071, .device = 0x8375, .action = VIA_DXS_NO_VRA }, /* Vobis/Yakumo/Mitac notebook */ { .vendor = 0x10cf, .device = 0x118e, .action = VIA_DXS_ENABLE }, /* FSC laptop */ { .vendor = 0x1106, .device = 0x4161, .action = VIA_DXS_NO_VRA }, /* ASRock K7VT2 */ { .vendor = 0x1106, .device = 0xaa01, .action = VIA_DXS_NO_VRA }, /* EPIA MII */ @@ -2126,6 +2105,7 @@ static int __devinit check_dxs_list(stru { .vendor = 0x1462, .device = 0x7120, .action = VIA_DXS_ENABLE }, /* MSI KT4V */ { .vendor = 0x1462, .device = 0x5901, .action = VIA_DXS_NO_VRA }, /* MSI KT6 Delta-SR */ { .vendor = 0x1584, .device = 0x8120, .action = VIA_DXS_ENABLE }, /* Gericom/Targa/Vobis/Uniwill laptop */ + { .vendor = 0x1584, .device = 0x8123, .action = VIA_DXS_NO_VRA }, /* Uniwill (Targa Visionary XP-210) */ { .vendor = 0x161f, .device = 0x202b, .action = VIA_DXS_NO_VRA }, /* Amira Note book */ { .vendor = 0x161f, .device = 0x2032, .action = VIA_DXS_48K }, /* m680x machines */ { .vendor = 0x1631, .device = 0xe004, .action = VIA_DXS_ENABLE }, /* Easy Note 3174, Packard Bell */ --- linux-2.6.9-rc1/sound/pci/vx222/vx222.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/sound/pci/vx222/vx222.c 2004-09-02 20:51:53.000000000 -0700 @@ -28,15 +28,12 @@ #include #include "vx222.h" -#define chip_t vx_core_t - #define CARD_NAME "VX222" MODULE_AUTHOR("Takashi Iwai "); MODULE_DESCRIPTION("Digigram VX222 V2/Mic"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Digigram," CARD_NAME "}}"); +MODULE_SUPPORTED_DEVICE("{{Digigram," CARD_NAME "}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ @@ -47,19 +44,14 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for Digigram " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for Digigram " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable Digigram " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(mic, bool, boot_devs, 0444); MODULE_PARM_DESC(mic, "Enable Microphone."); -MODULE_PARM_SYNTAX(mic, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); module_param_array(ibl, int, boot_devs, 0444); MODULE_PARM_DESC(ibl, "Capture IBL size."); -MODULE_PARM_SYNTAX(ibl, SNDRV_ENABLED); /* */ @@ -119,24 +111,19 @@ static struct snd_vx_hardware vx222_mic_ */ static int snd_vx222_free(vx_core_t *chip) { - int i; struct snd_vx222 *vx = (struct snd_vx222 *)chip; if (chip->irq >= 0) free_irq(chip->irq, (void*)chip); - for (i = 0; i < 2; i++) { - if (vx->port_res[i]) { - release_resource(vx->port_res[i]); - kfree_nocheck(vx->port_res[i]); - } - } - snd_magic_kfree(chip); + if (vx->port[0]) + pci_release_regions(vx->pci); + kfree(chip); return 0; } static int snd_vx222_dev_free(snd_device_t *device) { - vx_core_t *chip = snd_magic_cast(vx_core_t, device->device_data, return -ENXIO); + vx_core_t *chip = device->device_data; return snd_vx222_free(chip); } @@ -164,21 +151,14 @@ static int __devinit snd_vx222_create(sn if (! chip) return -ENOMEM; vx = (struct snd_vx222 *)chip; + vx->pci = pci; - for (i = 0; i < 2; i++) { - if (!(pci_resource_flags(pci, i + 1) & IORESOURCE_IO)) { - snd_printk(KERN_ERR "invalid i/o resource %d\n", i + 1); - snd_vx222_free(chip); - return -ENOMEM; - } - vx->port[i] = pci_resource_start(pci, i + 1); - if ((vx->port_res[i] = request_region(vx->port[i], 0x60, - CARD_NAME)) == NULL) { - snd_printk(KERN_ERR "unable to grab port 0x%lx\n", vx->port[i]); - snd_vx222_free(chip); - return -EBUSY; - } + if ((err = pci_request_regions(pci, CARD_NAME)) < 0) { + snd_vx222_free(chip); + return err; } + for (i = 0; i < 2; i++) + vx->port[i] = pci_resource_start(pci, i + 1); if (request_irq(pci->irq, snd_vx_irq_handler, SA_INTERRUPT|SA_SHIRQ, CARD_NAME, (void *) chip)) { --- linux-2.6.9-rc1/sound/pci/vx222/vx222.h 2004-08-24 00:03:38.000000000 -0700 +++ 25/sound/pci/vx222/vx222.h 2004-09-02 20:51:53.000000000 -0700 @@ -28,8 +28,8 @@ struct snd_vx222 { vx_core_t core; /* h/w config; for PLX and for DSP */ + struct pci_dev *pci; unsigned long port[2]; - struct resource *port_res[2]; unsigned int regCDSP; /* current CDSP register */ unsigned int regCFG; /* current CFG register */ --- linux-2.6.9-rc1/sound/pci/vx222/vx222_ops.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/sound/pci/vx222/vx222_ops.c 2004-09-02 20:51:53.000000000 -0700 @@ -27,8 +27,6 @@ #include #include "vx222.h" -#define chip_t vx_core_t - static int vx2_reg_offset[VX_REG_MAX] = { [VX_ICR] = 0x00, --- linux-2.6.9-rc1/sound/pci/ymfpci/ymfpci.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/sound/pci/ymfpci/ymfpci.c 2004-09-02 20:51:53.000000000 -0700 @@ -33,8 +33,7 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Yamaha DS-XG PCI"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Yamaha,YMF724}," +MODULE_SUPPORTED_DEVICE("{{Yamaha,YMF724}," "{Yamaha,YMF724F}," "{Yamaha,YMF740}," "{Yamaha,YMF740C}," @@ -54,27 +53,20 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for the Yamaha DS-XG PCI soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for the Yamaha DS-XG PCI soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable Yamaha DS-XG soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(mpu_port, long, boot_devs, 0444); MODULE_PARM_DESC(mpu_port, "MPU-401 Port."); -MODULE_PARM_SYNTAX(mpu_port, SNDRV_ENABLED); module_param_array(fm_port, long, boot_devs, 0444); MODULE_PARM_DESC(fm_port, "FM OPL-3 Port."); -MODULE_PARM_SYNTAX(fm_port, SNDRV_ENABLED); #ifdef SUPPORT_JOYSTICK module_param_array(joystick_port, long, boot_devs, 0444); MODULE_PARM_DESC(joystick_port, "Joystick port address"); -MODULE_PARM_SYNTAX(joystick_port, SNDRV_ENABLED); #endif module_param_array(rear_switch, bool, boot_devs, 0444); MODULE_PARM_DESC(rear_switch, "Enable shared rear/line-in switch"); -MODULE_PARM_SYNTAX(rear_switch, SNDRV_ENABLED "," SNDRV_BOOLEAN_FALSE_DESC); static struct pci_device_id snd_ymfpci_ids[] = { { 0x1073, 0x0004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* YMF724 */ --- linux-2.6.9-rc1/sound/pci/ymfpci/ymfpci_main.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/sound/pci/ymfpci/ymfpci_main.c 2004-09-02 20:51:53.000000000 -0700 @@ -42,8 +42,6 @@ #include -#define chip_t ymfpci_t - /* * constants */ @@ -102,7 +100,7 @@ static int snd_ymfpci_codec_ready(ymfpci static void snd_ymfpci_codec_write(ac97_t *ac97, u16 reg, u16 val) { - ymfpci_t *chip = snd_magic_cast(ymfpci_t, ac97->private_data, return); + ymfpci_t *chip = ac97->private_data; u32 cmd; snd_ymfpci_codec_ready(chip, 0); @@ -112,7 +110,7 @@ static void snd_ymfpci_codec_write(ac97_ static u16 snd_ymfpci_codec_read(ac97_t *ac97, u16 reg) { - ymfpci_t *chip = snd_magic_cast(ymfpci_t, ac97->private_data, return -ENXIO); + ymfpci_t *chip = ac97->private_data; if (snd_ymfpci_codec_ready(chip, 0)) return ~0; @@ -330,7 +328,7 @@ static void snd_ymfpci_pcm_interrupt(ymf static void snd_ymfpci_pcm_capture_interrupt(snd_pcm_substream_t *substream) { snd_pcm_runtime_t *runtime = substream->runtime; - ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return); + ymfpci_pcm_t *ypcm = runtime->private_data; ymfpci_t *chip = ypcm->chip; u32 pos, delta; @@ -358,7 +356,7 @@ static int snd_ymfpci_playback_trigger(s int cmd) { ymfpci_t *chip = snd_pcm_substream_chip(substream); - ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, substream->runtime->private_data, return -ENXIO); + ymfpci_pcm_t *ypcm = substream->runtime->private_data; int result = 0; spin_lock(&chip->reg_lock); @@ -395,7 +393,7 @@ static int snd_ymfpci_capture_trigger(sn int cmd) { ymfpci_t *chip = snd_pcm_substream_chip(substream); - ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, substream->runtime->private_data, return -ENXIO); + ymfpci_pcm_t *ypcm = substream->runtime->private_data; int result = 0; u32 tmp; @@ -541,7 +539,8 @@ static void snd_ymfpci_pcm_init_voice(ym static int __devinit snd_ymfpci_ac3_init(ymfpci_t *chip) { - if (snd_dma_alloc_pages(&chip->dma_dev, 4096, &chip->ac3_tmp_base) < 0) + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), + 4096, &chip->ac3_tmp_base) < 0) return -ENOMEM; chip->bank_effect[3][0]->base = @@ -568,7 +567,7 @@ static int snd_ymfpci_ac3_done(ymfpci_t spin_unlock_irq(&chip->reg_lock); // snd_ymfpci_irq_wait(chip); if (chip->ac3_tmp_base.area) { - snd_dma_free_pages(&chip->dma_dev, &chip->ac3_tmp_base); + snd_dma_free_pages(&chip->ac3_tmp_base); chip->ac3_tmp_base.area = NULL; } return 0; @@ -578,7 +577,7 @@ static int snd_ymfpci_playback_hw_params snd_pcm_hw_params_t * hw_params) { snd_pcm_runtime_t *runtime = substream->runtime; - ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + ymfpci_pcm_t *ypcm = runtime->private_data; int err; if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) @@ -596,7 +595,7 @@ static int snd_ymfpci_playback_hw_free(s if (runtime->private_data == NULL) return 0; - ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + ypcm = runtime->private_data; /* wait, until the PCI operations are not finished */ snd_ymfpci_irq_wait(chip); @@ -616,7 +615,7 @@ static int snd_ymfpci_playback_prepare(s { // ymfpci_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + ymfpci_pcm_t *ypcm = runtime->private_data; unsigned int nvoice; ypcm->period_size = runtime->period_size; @@ -654,7 +653,7 @@ static int snd_ymfpci_capture_prepare(sn { ymfpci_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + ymfpci_pcm_t *ypcm = runtime->private_data; snd_ymfpci_capture_bank_t * bank; int nbank; u32 rate, format; @@ -698,7 +697,7 @@ static snd_pcm_uframes_t snd_ymfpci_play { ymfpci_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + ymfpci_pcm_t *ypcm = runtime->private_data; ymfpci_voice_t *voice = ypcm->voices[0]; if (!(ypcm->running && voice)) @@ -710,7 +709,7 @@ static snd_pcm_uframes_t snd_ymfpci_capt { ymfpci_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + ymfpci_pcm_t *ypcm = runtime->private_data; if (!ypcm->running) return 0; @@ -736,7 +735,7 @@ static void snd_ymfpci_irq_wait(ymfpci_t static irqreturn_t snd_ymfpci_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - ymfpci_t *chip = snd_magic_cast(ymfpci_t, dev_id, return IRQ_NONE); + ymfpci_t *chip = dev_id; u32 status, nvoice, mode; ymfpci_voice_t *voice; @@ -830,10 +829,10 @@ static snd_pcm_hardware_t snd_ymfpci_cap static void snd_ymfpci_pcm_free_substream(snd_pcm_runtime_t *runtime) { - ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return); + ymfpci_pcm_t *ypcm = runtime->private_data; if (ypcm) - snd_magic_kfree(ypcm); + kfree(ypcm); } static int snd_ymfpci_playback_open_1(snd_pcm_substream_t * substream) @@ -842,7 +841,7 @@ static int snd_ymfpci_playback_open_1(sn snd_pcm_runtime_t *runtime = substream->runtime; ymfpci_pcm_t *ypcm; - ypcm = snd_magic_kcalloc(ymfpci_pcm_t, 0, GFP_KERNEL); + ypcm = kcalloc(1, sizeof(*ypcm), GFP_KERNEL); if (ypcm == NULL) return -ENOMEM; ypcm->chip = chip; @@ -886,20 +885,19 @@ static int snd_ymfpci_playback_open(snd_ ymfpci_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; ymfpci_pcm_t *ypcm; - unsigned long flags; int err; if ((err = snd_ymfpci_playback_open_1(substream)) < 0) return err; - ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return 0); + ypcm = runtime->private_data; ypcm->output_front = 1; ypcm->output_rear = chip->mode_dup4ch ? 1 : 0; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); if (ypcm->output_rear) { ymfpci_open_extension(chip); chip->rear_opened++; } - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return 0; } @@ -908,22 +906,21 @@ static int snd_ymfpci_playback_spdif_ope ymfpci_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; ymfpci_pcm_t *ypcm; - unsigned long flags; int err; if ((err = snd_ymfpci_playback_open_1(substream)) < 0) return err; - ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return 0); + ypcm = runtime->private_data; ypcm->output_front = 0; ypcm->output_rear = 1; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL, snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) | 2); ymfpci_open_extension(chip); chip->spdif_pcm_bits = chip->spdif_bits; snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_pcm_bits); chip->spdif_opened++; - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); chip->spdif_pcm_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE | @@ -936,18 +933,17 @@ static int snd_ymfpci_playback_4ch_open( ymfpci_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; ymfpci_pcm_t *ypcm; - unsigned long flags; int err; if ((err = snd_ymfpci_playback_open_1(substream)) < 0) return err; - ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return 0); + ypcm = runtime->private_data; ypcm->output_front = 0; ypcm->output_rear = 1; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); ymfpci_open_extension(chip); chip->rear_opened++; - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return 0; } @@ -958,7 +954,7 @@ static int snd_ymfpci_capture_open(snd_p snd_pcm_runtime_t *runtime = substream->runtime; ymfpci_pcm_t *ypcm; - ypcm = snd_magic_kcalloc(ymfpci_pcm_t, 0, GFP_KERNEL); + ypcm = kcalloc(1, sizeof(*ypcm), GFP_KERNEL); if (ypcm == NULL) return -ENOMEM; ypcm->chip = chip; @@ -993,30 +989,28 @@ static int snd_ymfpci_playback_close_1(s static int snd_ymfpci_playback_close(snd_pcm_substream_t * substream) { ymfpci_t *chip = snd_pcm_substream_chip(substream); - ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, substream->runtime->private_data, return -ENXIO); - unsigned long flags; + ymfpci_pcm_t *ypcm = substream->runtime->private_data; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); if (ypcm->output_rear && chip->rear_opened > 0) { chip->rear_opened--; ymfpci_close_extension(chip); } - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return snd_ymfpci_playback_close_1(substream); } static int snd_ymfpci_playback_spdif_close(snd_pcm_substream_t * substream) { ymfpci_t *chip = snd_pcm_substream_chip(substream); - unsigned long flags; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); chip->spdif_opened = 0; ymfpci_close_extension(chip); snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL, snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & ~2); snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); chip->spdif_pcm_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, &chip->spdif_pcm_ctl->id); @@ -1026,14 +1020,13 @@ static int snd_ymfpci_playback_spdif_clo static int snd_ymfpci_playback_4ch_close(snd_pcm_substream_t * substream) { ymfpci_t *chip = snd_pcm_substream_chip(substream); - unsigned long flags; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); if (chip->rear_opened > 0) { chip->rear_opened--; ymfpci_close_extension(chip); } - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return snd_ymfpci_playback_close_1(substream); } @@ -1041,7 +1034,7 @@ static int snd_ymfpci_capture_close(snd_ { ymfpci_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; - ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, return -ENXIO); + ymfpci_pcm_t *ypcm = runtime->private_data; if (ypcm != NULL) { chip->capture_substream[ypcm->capture_bank_number] = NULL; @@ -1074,7 +1067,7 @@ static snd_pcm_ops_t snd_ymfpci_capture_ static void snd_ymfpci_pcm_free(snd_pcm_t *pcm) { - ymfpci_t *chip = snd_magic_cast(ymfpci_t, pcm->private_data, return); + ymfpci_t *chip = pcm->private_data; chip->pcm = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -1120,7 +1113,7 @@ static snd_pcm_ops_t snd_ymfpci_capture_ static void snd_ymfpci_pcm2_free(snd_pcm_t *pcm) { - ymfpci_t *chip = snd_magic_cast(ymfpci_t, pcm->private_data, return); + ymfpci_t *chip = pcm->private_data; chip->pcm2 = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -1166,7 +1159,7 @@ static snd_pcm_ops_t snd_ymfpci_playback static void snd_ymfpci_pcm_spdif_free(snd_pcm_t *pcm) { - ymfpci_t *chip = snd_magic_cast(ymfpci_t, pcm->private_data, return); + ymfpci_t *chip = pcm->private_data; chip->pcm_spdif = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -1211,7 +1204,7 @@ static snd_pcm_ops_t snd_ymfpci_playback static void snd_ymfpci_pcm_4ch_free(snd_pcm_t *pcm) { - ymfpci_t *chip = snd_magic_cast(ymfpci_t, pcm->private_data, return); + ymfpci_t *chip = pcm->private_data; chip->pcm_4ch = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -1254,12 +1247,11 @@ static int snd_ymfpci_spdif_default_get( snd_ctl_elem_value_t * ucontrol) { ymfpci_t *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); ucontrol->value.iec958.status[0] = (chip->spdif_bits >> 0) & 0xff; ucontrol->value.iec958.status[1] = (chip->spdif_bits >> 8) & 0xff; - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return 0; } @@ -1267,18 +1259,17 @@ static int snd_ymfpci_spdif_default_put( snd_ctl_elem_value_t * ucontrol) { ymfpci_t *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; unsigned int val; int change; val = ((ucontrol->value.iec958.status[0] & 0x3e) << 0) | (ucontrol->value.iec958.status[1] << 8); - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); change = chip->spdif_bits != val; chip->spdif_bits = val; if ((snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & 1) && chip->pcm_spdif == NULL) snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return change; } @@ -1302,12 +1293,11 @@ static int snd_ymfpci_spdif_mask_get(snd snd_ctl_elem_value_t * ucontrol) { ymfpci_t *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); ucontrol->value.iec958.status[0] = 0x3e; ucontrol->value.iec958.status[1] = 0xff; - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return 0; } @@ -1331,12 +1321,11 @@ static int snd_ymfpci_spdif_stream_get(s snd_ctl_elem_value_t * ucontrol) { ymfpci_t *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); ucontrol->value.iec958.status[0] = (chip->spdif_pcm_bits >> 0) & 0xff; ucontrol->value.iec958.status[1] = (chip->spdif_pcm_bits >> 8) & 0xff; - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return 0; } @@ -1344,18 +1333,17 @@ static int snd_ymfpci_spdif_stream_put(s snd_ctl_elem_value_t * ucontrol) { ymfpci_t *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; unsigned int val; int change; val = ((ucontrol->value.iec958.status[0] & 0x3e) << 0) | (ucontrol->value.iec958.status[1] << 8); - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); change = chip->spdif_pcm_bits != val; chip->spdif_pcm_bits = val; if ((snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & 2)) snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_pcm_bits); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return change; } @@ -1385,12 +1373,11 @@ static int snd_ymfpci_drec_source_info(s static int snd_ymfpci_drec_source_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value) { ymfpci_t *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; u16 reg; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); reg = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); if (!(reg & 0x100)) value->value.enumerated.item[0] = 0; else @@ -1401,17 +1388,16 @@ static int snd_ymfpci_drec_source_get(sn static int snd_ymfpci_drec_source_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value) { ymfpci_t *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; u16 reg, old_reg; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); old_reg = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL); if (value->value.enumerated.item[0] == 0) reg = old_reg & ~0x100; else reg = (old_reg & ~0x300) | 0x100 | ((value->value.enumerated.item[0] == 2) << 9); snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, reg); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return reg != old_reg; } @@ -1470,7 +1456,6 @@ static int snd_ymfpci_get_single(snd_kco static int snd_ymfpci_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { ymfpci_t *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; int reg = kcontrol->private_value; unsigned int shift = 0, mask = 1, invert = 0; int change; @@ -1485,12 +1470,12 @@ static int snd_ymfpci_put_single(snd_kco if (invert) val = mask - val; val <<= shift; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); oval = snd_ymfpci_readl(chip, reg); val = (oval & ~(mask << shift)) | val; change = val != oval; snd_ymfpci_writel(chip, reg, val); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return change; } @@ -1517,16 +1502,15 @@ static int snd_ymfpci_info_double(snd_kc static int snd_ymfpci_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { ymfpci_t *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; unsigned int reg = kcontrol->private_value; unsigned int shift_left = 0, shift_right = 16, mask = 16383, invert = 0; unsigned int val; if (reg < 0x80 || reg >= 0xc0) return -EINVAL; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); val = snd_ymfpci_readl(chip, reg); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); ucontrol->value.integer.value[0] = (val >> shift_left) & mask; ucontrol->value.integer.value[1] = (val >> shift_right) & mask; if (invert) { @@ -1539,7 +1523,6 @@ static int snd_ymfpci_get_double(snd_kco static int snd_ymfpci_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { ymfpci_t *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; unsigned int reg = kcontrol->private_value; unsigned int shift_left = 0, shift_right = 16, mask = 16383, invert = 0; int change; @@ -1555,12 +1538,12 @@ static int snd_ymfpci_put_double(snd_kco } val1 <<= shift_left; val2 <<= shift_right; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); oval = snd_ymfpci_readl(chip, reg); val1 = (oval & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; change = val1 != oval; snd_ymfpci_writel(chip, reg, val1); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return change; } @@ -1594,8 +1577,6 @@ static int snd_ymfpci_put_dup4ch(snd_kco } -#define YMFPCI_CONTROLS (sizeof(snd_ymfpci_controls)/sizeof(snd_kcontrol_new_t)) - static snd_kcontrol_new_t snd_ymfpci_controls[] __devinitdata = { YMFPCI_DOUBLE("Wave Playback Volume", 0, YDSXGR_NATIVEDACOUTVOL), YMFPCI_DOUBLE("Wave Capture Volume", 0, YDSXGR_NATIVEDACLOOPVOL), @@ -1709,31 +1690,30 @@ static snd_kcontrol_new_t snd_ymfpci_rea static void snd_ymfpci_mixer_free_ac97_bus(ac97_bus_t *bus) { - ymfpci_t *chip = snd_magic_cast(ymfpci_t, bus->private_data, return); + ymfpci_t *chip = bus->private_data; chip->ac97_bus = NULL; } static void snd_ymfpci_mixer_free_ac97(ac97_t *ac97) { - ymfpci_t *chip = snd_magic_cast(ymfpci_t, ac97->private_data, return); + ymfpci_t *chip = ac97->private_data; chip->ac97 = NULL; } int __devinit snd_ymfpci_mixer(ymfpci_t *chip, int rear_switch) { - ac97_bus_t bus; - ac97_t ac97; + ac97_template_t ac97; snd_kcontrol_t *kctl; unsigned int idx; int err; + static ac97_bus_ops_t ops = { + .write = snd_ymfpci_codec_write, + .read = snd_ymfpci_codec_read, + }; - memset(&bus, 0, sizeof(bus)); - bus.write = snd_ymfpci_codec_write; - bus.read = snd_ymfpci_codec_read; - bus.private_data = chip; - bus.private_free = snd_ymfpci_mixer_free_ac97_bus; - if ((err = snd_ac97_bus(chip->card, &bus, &chip->ac97_bus)) < 0) + if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0) return err; + chip->ac97_bus->private_free = snd_ymfpci_mixer_free_ac97_bus; memset(&ac97, 0, sizeof(ac97)); ac97.private_data = chip; @@ -1741,7 +1721,7 @@ int __devinit snd_ymfpci_mixer(ymfpci_t if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0) return err; - for (idx = 0; idx < YMFPCI_CONTROLS; idx++) { + for (idx = 0; idx < ARRAY_SIZE(snd_ymfpci_controls); idx++) { if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_controls[idx], chip))) < 0) return err; } @@ -1854,7 +1834,7 @@ int __devinit snd_ymfpci_timer(ymfpci_t static void snd_ymfpci_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) { - ymfpci_t *chip = snd_magic_cast(ymfpci_t, entry->private_data, return); + ymfpci_t *chip = entry->private_data; int i; snd_iprintf(buffer, "YMFPCI\n\n"); @@ -1975,7 +1955,8 @@ static int __devinit snd_ymfpci_memalloc chip->work_size; /* work_ptr must be aligned to 256 bytes, but it's already covered with the kernel page allocation mechanism */ - if (snd_dma_alloc_pages(&chip->dma_dev, size, &chip->work_ptr) < 0) + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), + size, &chip->work_ptr) < 0) return -ENOMEM; ptr = chip->work_ptr.area; ptr_addr = chip->work_ptr.addr; @@ -2106,7 +2087,7 @@ static int snd_ymfpci_free(ymfpci_t *chi if (chip->reg_area_virt) iounmap((void *)chip->reg_area_virt); if (chip->work_ptr.area) - snd_dma_free_pages(&chip->dma_dev, &chip->work_ptr); + snd_dma_free_pages(&chip->work_ptr); if (chip->irq >= 0) free_irq(chip->irq, (void *)chip); @@ -2117,13 +2098,13 @@ static int snd_ymfpci_free(ymfpci_t *chi pci_write_config_word(chip->pci, 0x40, chip->old_legacy_ctrl); - snd_magic_kfree(chip); + kfree(chip); return 0; } static int snd_ymfpci_dev_free(snd_device_t *device) { - ymfpci_t *chip = snd_magic_cast(ymfpci_t, device->device_data, return -ENXIO); + ymfpci_t *chip = device->device_data; return snd_ymfpci_free(chip); } @@ -2159,7 +2140,7 @@ static int saved_regs_index[] = { static int snd_ymfpci_suspend(snd_card_t *card, unsigned int state) { - ymfpci_t *chip = snd_magic_cast(ymfpci_t, card->pm_private_data, return -EINVAL); + ymfpci_t *chip = card->pm_private_data; unsigned int i; snd_pcm_suspend_all(chip->pcm); @@ -2178,7 +2159,7 @@ static int snd_ymfpci_suspend(snd_card_t static int snd_ymfpci_resume(snd_card_t *card, unsigned int state) { - ymfpci_t *chip = snd_magic_cast(ymfpci_t, card->pm_private_data, return -EINVAL); + ymfpci_t *chip = card->pm_private_data; unsigned int i; pci_enable_device(chip->pci); @@ -2195,11 +2176,10 @@ static int snd_ymfpci_resume(snd_card_t /* start hw again */ if (chip->start_count > 0) { - unsigned long flags; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); snd_ymfpci_writel(chip, YDSXGR_MODE, chip->saved_ydsxgr_mode); chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); } snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; @@ -2223,7 +2203,7 @@ int __devinit snd_ymfpci_create(snd_card if ((err = pci_enable_device(pci)) < 0) return err; - chip = snd_magic_kcalloc(ymfpci_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; chip->old_legacy_ctrl = old_legacy_ctrl; @@ -2252,10 +2232,6 @@ int __devinit snd_ymfpci_create(snd_card } chip->irq = pci->irq; - memset(&chip->dma_dev, 0, sizeof(chip->dma_dev)); - chip->dma_dev.type = SNDRV_DMA_TYPE_DEV; - chip->dma_dev.dev = snd_dma_pci_data(pci); - snd_ymfpci_aclink_reset(pci); if (snd_ymfpci_codec_ready(chip, 0) < 0) { snd_ymfpci_free(chip); --- linux-2.6.9-rc1/sound/pcmcia/pdaudiocf/pdaudiocf.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/sound/pcmcia/pdaudiocf/pdaudiocf.c 2004-09-02 20:51:53.000000000 -0700 @@ -37,8 +37,7 @@ MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Sound Core " CARD_NAME); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Sound Core," CARD_NAME "}}"); +MODULE_SUPPORTED_DEVICE("{{Sound Core," CARD_NAME "}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ @@ -49,13 +48,10 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param(irq_mask, int, 0444); MODULE_PARM_DESC(irq_mask, "IRQ bitmask for " CARD_NAME " soundcard."); module_param_array(irq_list, int, boot_devs, 0444); @@ -103,13 +99,13 @@ static int snd_pdacf_free(pdacf_t *pdacf card_list[pdacf->index] = NULL; pdacf->card = NULL; - snd_magic_kfree(pdacf); + kfree(pdacf); return 0; } static int snd_pdacf_dev_free(snd_device_t *device) { - pdacf_t *chip = snd_magic_cast(pdacf_t, device->device_data, return -ENXIO); + pdacf_t *chip = device->device_data; return snd_pdacf_free(chip); } @@ -152,7 +148,7 @@ static dev_link_t *snd_pdacf_attach(void return NULL; if (snd_device_new(card, SNDRV_DEV_LOWLEVEL, pdacf, &ops) < 0) { - snd_magic_kfree(pdacf); + kfree(pdacf); snd_card_free(card); return NULL; } @@ -258,7 +254,7 @@ static int snd_pdacf_assign_resources(pd */ static void snd_pdacf_detach(dev_link_t *link) { - pdacf_t *chip = snd_magic_cast(pdacf_t, link->priv, return); + pdacf_t *chip = link->priv; snd_printdd(KERN_DEBUG "pdacf_detach called\n"); /* Remove the interface data from the linked list */ @@ -297,7 +293,7 @@ do { last_fn = (fn); if ((last_ret = (re static void pdacf_config(dev_link_t *link) { client_handle_t handle = link->handle; - pdacf_t *pdacf = snd_magic_cast(pdacf_t, link->priv, return); + pdacf_t *pdacf = link->priv; tuple_t tuple; cisparse_t parse; config_info_t conf; --- linux-2.6.9-rc1/sound/pcmcia/pdaudiocf/pdaudiocf_core.c 2004-08-24 00:02:24.000000000 -0700 +++ 25/sound/pcmcia/pdaudiocf/pdaudiocf_core.c 2004-09-02 20:51:53.000000000 -0700 @@ -30,7 +30,7 @@ */ unsigned char pdacf_ak4117_read(void *private_data, unsigned char reg) { - pdacf_t *chip = snd_magic_cast(pdacf_t, private_data, return 0); + pdacf_t *chip = private_data; unsigned long timeout; unsigned long flags; unsigned char res; @@ -62,7 +62,7 @@ unsigned char pdacf_ak4117_read(void *pr void pdacf_ak4117_write(void *private_data, unsigned char reg, unsigned char val) { - pdacf_t *chip = snd_magic_cast(pdacf_t, private_data, return); + pdacf_t *chip = private_data; unsigned long timeout; unsigned long flags; @@ -130,7 +130,7 @@ void pdacf_reinit(pdacf_t *chip, int res static void pdacf_proc_read(snd_info_entry_t * entry, snd_info_buffer_t * buffer) { - pdacf_t *chip = snd_magic_cast(pdacf_t, entry->private_data, return); + pdacf_t *chip = entry->private_data; u16 tmp; snd_iprintf(buffer, "PDAudioCF\n\n"); @@ -151,7 +151,7 @@ pdacf_t *snd_pdacf_create(snd_card_t *ca { pdacf_t *chip; - chip = snd_magic_kcalloc(pdacf_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return NULL; chip->card = card; @@ -257,7 +257,7 @@ void snd_pdacf_powerdown(pdacf_t *chip) int snd_pdacf_suspend(snd_card_t *card, unsigned int state) { - pdacf_t *chip = snd_magic_cast(pdacf_t, card->pm_private_data, return -EINVAL); + pdacf_t *chip = card->pm_private_data; u16 val; snd_pcm_suspend_all(chip->pcm); @@ -278,7 +278,7 @@ static inline int check_signal(pdacf_t * int snd_pdacf_resume(snd_card_t *card, unsigned int state) { - pdacf_t *chip = snd_magic_cast(pdacf_t, card->pm_private_data, return -EINVAL); + pdacf_t *chip = card->pm_private_data; int timeout = 40; pdacf_reinit(chip, 1); --- linux-2.6.9-rc1/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c 2004-08-24 00:02:59.000000000 -0700 +++ 25/sound/pcmcia/pdaudiocf/pdaudiocf_irq.c 2004-09-02 20:51:53.000000000 -0700 @@ -28,7 +28,7 @@ */ irqreturn_t pdacf_interrupt(int irq, void *dev, struct pt_regs *regs) { - pdacf_t *chip = snd_magic_cast(pdacf_t, dev, return IRQ_NONE); + pdacf_t *chip = dev; unsigned short stat; if ((chip->chip_status & (PDAUDIOCF_STAT_IS_STALE| @@ -258,7 +258,7 @@ static void pdacf_transfer(pdacf_t *chip void pdacf_tasklet(unsigned long private_data) { - pdacf_t *chip = snd_magic_cast(pdacf_t, (void *)private_data, return); + pdacf_t *chip = (pdacf_t *) private_data; int size, off, cont, rdp, wdp; if ((chip->chip_status & (PDAUDIOCF_STAT_IS_STALE|PDAUDIOCF_STAT_IS_CONFIGURED)) != PDAUDIOCF_STAT_IS_CONFIGURED) --- linux-2.6.9-rc1/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c 2004-08-24 00:02:57.000000000 -0700 +++ 25/sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c 2004-09-02 20:51:53.000000000 -0700 @@ -28,8 +28,6 @@ #include #include "pdaudiocf.h" -#define chip_t pdacf_t - /* * we use a vmalloc'ed (sg-)buffer @@ -331,7 +329,7 @@ static snd_pcm_ops_t pdacf_pcm_capture_o */ static void snd_pdacf_pcm_free(snd_pcm_t *pcm) { - pdacf_t *chip = snd_magic_cast(pdacf_t, pcm->private_data, return); + pdacf_t *chip = pcm->private_data; chip->pcm = NULL; } --- linux-2.6.9-rc1/sound/pcmcia/vx/vx_entry.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/sound/pcmcia/vx/vx_entry.c 2004-09-02 20:51:53.000000000 -0700 @@ -69,13 +69,13 @@ static int snd_vxpocket_free(vx_core_t * hw->card_list[vxp->index] = NULL; chip->card = NULL; - snd_magic_kfree(chip); + kfree(chip); return 0; } static int snd_vxpocket_dev_free(snd_device_t *device) { - vx_core_t *chip = snd_magic_cast(vx_core_t, device->device_data, return -ENXIO); + vx_core_t *chip = device->device_data; return snd_vxpocket_free(chip); } @@ -121,7 +121,7 @@ dev_link_t *snd_vxpocket_attach(struct s return NULL; if (snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops) < 0) { - snd_magic_kfree(chip); + kfree(chip); snd_card_free(card); return NULL; } @@ -226,7 +226,7 @@ static int snd_vxpocket_assign_resources */ void snd_vxpocket_detach(struct snd_vxp_entry *hw, dev_link_t *link) { - vx_core_t *chip = snd_magic_cast(vx_core_t, link->priv, return); + vx_core_t *chip = link->priv; snd_printdd(KERN_DEBUG "vxpocket_detach called\n"); /* Remove the interface data from the linked list */ @@ -263,7 +263,7 @@ do { last_fn = (fn); if ((last_ret = (re static void vxpocket_config(dev_link_t *link) { client_handle_t handle = link->handle; - vx_core_t *chip = snd_magic_cast(vx_core_t, link->priv, return); + vx_core_t *chip = link->priv; struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip; tuple_t tuple; cisparse_t parse; --- linux-2.6.9-rc1/sound/pcmcia/vx/vxp_mixer.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/sound/pcmcia/vx/vxp_mixer.c 2004-09-02 20:51:53.000000000 -0700 @@ -25,8 +25,6 @@ #include #include "vxpocket.h" -#define chip_t vx_core_t - #define MIC_LEVEL_MIN 0 #define MIC_LEVEL_MAX 8 --- linux-2.6.9-rc1/sound/pcmcia/vx/vxpocket.c 2004-08-24 00:02:22.000000000 -0700 +++ 25/sound/pcmcia/vx/vxpocket.c 2004-09-02 20:51:53.000000000 -0700 @@ -50,8 +50,7 @@ MODULE_AUTHOR("Takashi Iwai "); MODULE_DESCRIPTION("Digigram " CARD_NAME); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Digigram," CARD_NAME "}}"); +MODULE_SUPPORTED_DEVICE("{{Digigram," CARD_NAME "}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ @@ -63,20 +62,16 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param(irq_mask, int, 0444); MODULE_PARM_DESC(irq_mask, "IRQ bitmask for " CARD_NAME " soundcard."); module_param_array(irq_list, int, boot_devs, 0444); MODULE_PARM_DESC(irq_list, "List of Available interrupts for " CARD_NAME " soundcard."); module_param_array(ibl, int, boot_devs, 0444); MODULE_PARM_DESC(ibl, "Capture IBL size for " CARD_NAME " soundcard."); -MODULE_PARM_SYNTAX(ibl, SNDRV_ENABLED); /* --- linux-2.6.9-rc1/sound/pcmcia/vx/vxp_ops.c 2004-08-24 00:02:23.000000000 -0700 +++ 25/sound/pcmcia/vx/vxp_ops.c 2004-09-02 20:51:53.000000000 -0700 @@ -26,8 +26,6 @@ #include #include "vxpocket.h" -#define chip_t vx_core_t - static int vxp_reg_offset[VX_REG_MAX] = { [VX_ICR] = 0x00, // ICR --- linux-2.6.9-rc1/sound/ppc/awacs.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/sound/ppc/awacs.c 2004-09-02 20:51:53.000000000 -0700 @@ -29,8 +29,6 @@ #include #include "pmac.h" -#define chip_t pmac_t - #ifdef CONFIG_ADB_CUDA #define PMAC_AMP_AVAIL @@ -574,11 +572,22 @@ static snd_kcontrol_new_t snd_pmac_awacs AWACS_VOLUME("Master Playback Volume", 2, 6, 1), AWACS_SWITCH("Master Capture Switch", 1, SHIFT_LOOPTHRU, 0), AWACS_VOLUME("Capture Volume", 0, 4, 0), - AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_LINE, 0), AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0), +}; + +/* FIXME: is this correct order? + * screamer (powerbook G3 pismo) seems to have different bits... + */ +static snd_kcontrol_new_t snd_pmac_awacs_mixers2[] __initdata = { + AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_LINE, 0), AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_MIC, 0), }; +static snd_kcontrol_new_t snd_pmac_screamer_mixers2[] __initdata = { + AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0), + AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_LINE, 0), +}; + static snd_kcontrol_new_t snd_pmac_awacs_master_sw __initdata = AWACS_SWITCH("Master Playback Switch", 1, SHIFT_HDMUTE, 1); @@ -602,8 +611,6 @@ static snd_kcontrol_new_t snd_pmac_awacs AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_SPKMUTE, 1); -#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t)) - /* * add new mixer elements to the card */ @@ -818,9 +825,17 @@ snd_pmac_awacs_init(pmac_t *chip) */ strcpy(chip->card->mixername, "PowerMac AWACS"); - if ((err = build_mixers(chip, num_controls(snd_pmac_awacs_mixers), + if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers), snd_pmac_awacs_mixers)) < 0) return err; + if (chip->model == PMAC_SCREAMER) + err = build_mixers(chip, ARRAY_SIZE(snd_pmac_screamer_mixers2), + snd_pmac_screamer_mixers2); + else + err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers2), + snd_pmac_awacs_mixers2); + if (err < 0) + return err; chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_master_sw, chip); if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0) return err; @@ -832,7 +847,7 @@ snd_pmac_awacs_init(pmac_t *chip) * screamer registers. * in this case, it seems the route C is not used. */ - if ((err = build_mixers(chip, num_controls(snd_pmac_awacs_amp_vol), + if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_amp_vol), snd_pmac_awacs_amp_vol)) < 0) return err; /* overwrite */ @@ -846,7 +861,7 @@ snd_pmac_awacs_init(pmac_t *chip) #endif /* PMAC_AMP_AVAIL */ { /* route A = headphone, route C = speaker */ - if ((err = build_mixers(chip, num_controls(snd_pmac_awacs_speaker_vol), + if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_speaker_vol), snd_pmac_awacs_speaker_vol)) < 0) return err; chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_speaker_sw, chip); @@ -855,11 +870,11 @@ snd_pmac_awacs_init(pmac_t *chip) } if (chip->model == PMAC_SCREAMER) { - if ((err = build_mixers(chip, num_controls(snd_pmac_screamer_mic_boost), + if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_screamer_mic_boost), snd_pmac_screamer_mic_boost)) < 0) return err; } else { - if ((err = build_mixers(chip, num_controls(snd_pmac_awacs_mic_boost), + if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mic_boost), snd_pmac_awacs_mic_boost)) < 0) return err; } --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/sound/ppc/beep.c 2004-09-02 20:51:53.000000000 -0700 @@ -0,0 +1,262 @@ +/* + * Beep using pcm + * + * Copyright (c) by Takashi Iwai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pmac.h" + +struct snd_pmac_beep { + int running; /* boolean */ + int volume; /* mixer volume: 0-100 */ + int volume_play; /* currently playing volume */ + int hz; + int nsamples; + short *buf; /* allocated wave buffer */ + unsigned long addr; /* physical address of buffer */ + struct input_dev dev; +}; + +/* + * stop beep if running + */ +void snd_pmac_beep_stop(pmac_t *chip) +{ + pmac_beep_t *beep = chip->beep; + if (beep && beep->running) { + beep->running = 0; + snd_pmac_beep_dma_stop(chip); + } +} + +/* + * Stuff for outputting a beep. The values range from -327 to +327 + * so we can multiply by an amplitude in the range 0..100 to get a + * signed short value to put in the output buffer. + */ +static short beep_wform[256] = { + 0, 40, 79, 117, 153, 187, 218, 245, + 269, 288, 304, 316, 323, 327, 327, 324, + 318, 310, 299, 288, 275, 262, 249, 236, + 224, 213, 204, 196, 190, 186, 183, 182, + 182, 183, 186, 189, 192, 196, 200, 203, + 206, 208, 209, 209, 209, 207, 204, 201, + 197, 193, 188, 183, 179, 174, 170, 166, + 163, 161, 160, 159, 159, 160, 161, 162, + 164, 166, 168, 169, 171, 171, 171, 170, + 169, 167, 163, 159, 155, 150, 144, 139, + 133, 128, 122, 117, 113, 110, 107, 105, + 103, 103, 103, 103, 104, 104, 105, 105, + 105, 103, 101, 97, 92, 86, 78, 68, + 58, 45, 32, 18, 3, -11, -26, -41, + -55, -68, -79, -88, -95, -100, -102, -102, + -99, -93, -85, -75, -62, -48, -33, -16, + 0, 16, 33, 48, 62, 75, 85, 93, + 99, 102, 102, 100, 95, 88, 79, 68, + 55, 41, 26, 11, -3, -18, -32, -45, + -58, -68, -78, -86, -92, -97, -101, -103, + -105, -105, -105, -104, -104, -103, -103, -103, + -103, -105, -107, -110, -113, -117, -122, -128, + -133, -139, -144, -150, -155, -159, -163, -167, + -169, -170, -171, -171, -171, -169, -168, -166, + -164, -162, -161, -160, -159, -159, -160, -161, + -163, -166, -170, -174, -179, -183, -188, -193, + -197, -201, -204, -207, -209, -209, -209, -208, + -206, -203, -200, -196, -192, -189, -186, -183, + -182, -182, -183, -186, -190, -196, -204, -213, + -224, -236, -249, -262, -275, -288, -299, -310, + -318, -324, -327, -327, -323, -316, -304, -288, + -269, -245, -218, -187, -153, -117, -79, -40, +}; + +#define BEEP_SRATE 22050 /* 22050 Hz sample rate */ +#define BEEP_BUFLEN 512 +#define BEEP_VOLUME 15 /* 0 - 100 */ + +static int snd_pmac_beep_event(struct input_dev *dev, unsigned int type, unsigned int code, int hz) +{ + pmac_t *chip; + pmac_beep_t *beep; + unsigned long flags; + int beep_speed = 0; + int srate; + int period, ncycles, nsamples; + int i, j, f; + short *p; + + if (type != EV_SND) + return -1; + + switch (code) { + case SND_BELL: if (hz) hz = 1000; + case SND_TONE: break; + default: return -1; + } + + chip = dev->private; + if (! chip || (beep = chip->beep) == NULL) + return -1; + + if (! hz) { + spin_lock_irqsave(&chip->reg_lock, flags); + if (beep->running) + snd_pmac_beep_stop(chip); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; + } + + beep_speed = snd_pmac_rate_index(chip, &chip->playback, BEEP_SRATE); + srate = chip->freq_table[beep_speed]; + + if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) + hz = 1000; + + spin_lock_irqsave(&chip->reg_lock, flags); + if (chip->playback.running || chip->capture.running || beep->running) { + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; + } + beep->running = 1; + spin_unlock_irqrestore(&chip->reg_lock, flags); + + if (hz == beep->hz && beep->volume == beep->volume_play) { + nsamples = beep->nsamples; + } else { + period = srate * 256 / hz; /* fixed point */ + ncycles = BEEP_BUFLEN * 256 / period; + nsamples = (period * ncycles) >> 8; + f = ncycles * 65536 / nsamples; + j = 0; + p = beep->buf; + for (i = 0; i < nsamples; ++i, p += 2) { + p[0] = p[1] = beep_wform[j >> 8] * beep->volume; + j = (j + f) & 0xffff; + } + beep->hz = hz; + beep->volume_play = beep->volume; + beep->nsamples = nsamples; + } + + spin_lock_irqsave(&chip->reg_lock, flags); + snd_pmac_beep_dma_start(chip, beep->nsamples * 4, beep->addr, beep_speed); + spin_unlock_irqrestore(&chip->reg_lock, flags); + return 0; +} + +/* + * beep volume mixer + */ + +#define chip_t pmac_t + +static int snd_pmac_info_beep(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 100; + return 0; +} + +static int snd_pmac_get_beep(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + snd_assert(chip->beep, return -ENXIO); + ucontrol->value.integer.value[0] = chip->beep->volume; + return 0; +} + +static int snd_pmac_put_beep(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + pmac_t *chip = snd_kcontrol_chip(kcontrol); + int oval; + snd_assert(chip->beep, return -ENXIO); + oval = chip->beep->volume; + chip->beep->volume = ucontrol->value.integer.value[0]; + return oval != chip->beep->volume; +} + +static snd_kcontrol_new_t snd_pmac_beep_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Beep Playback Volume", + .info = snd_pmac_info_beep, + .get = snd_pmac_get_beep, + .put = snd_pmac_put_beep, +}; + +/* Initialize beep stuff */ +int __init snd_pmac_attach_beep(pmac_t *chip) +{ + pmac_beep_t *beep; + int err; + + beep = kmalloc(sizeof(*beep), GFP_KERNEL); + if (! beep) + return -ENOMEM; + + memset(beep, 0, sizeof(*beep)); + beep->buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL); + if (! beep->buf) { + kfree(beep); + return -ENOMEM; + } + beep->addr = virt_to_bus(beep->buf); + + beep->dev.evbit[0] = BIT(EV_SND); + beep->dev.sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE); + beep->dev.event = snd_pmac_beep_event; + beep->dev.private = chip; + + /* FIXME: set more better values */ + beep->dev.name = "PowerMac Beep"; + beep->dev.phys = "powermac/beep"; + beep->dev.id.bustype = BUS_ADB; + beep->dev.id.vendor = 0x001f; + beep->dev.id.product = 0x0001; + beep->dev.id.version = 0x0100; + + beep->volume = BEEP_VOLUME; + beep->running = 0; + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_pmac_beep_mixer, chip))) < 0) { + kfree(beep->buf); + kfree(beep); + return err; + } + + chip->beep = beep; + input_register_device(&beep->dev); + + return 0; +} + +void snd_pmac_detach_beep(pmac_t *chip) +{ + if (chip->beep) { + input_unregister_device(&chip->beep->dev); + kfree(chip->beep->buf); + kfree(chip->beep); + chip->beep = NULL; + } +} --- linux-2.6.9-rc1/sound/ppc/burgundy.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/sound/ppc/burgundy.c 2004-09-02 20:51:53.000000000 -0700 @@ -28,8 +28,6 @@ #include "pmac.h" #include "burgundy.h" -#define chip_t pmac_t - /* Waits for busy flag to clear */ inline static void @@ -324,8 +322,6 @@ BURGUNDY_OUTPUT_SWITCH("Headphone Playba static snd_kcontrol_new_t snd_pmac_burgundy_speaker_sw __initdata = BURGUNDY_OUTPUT_SWITCH("PC Speaker Playback Switch", 0, BURGUNDY_OUTPUT_INTERN, 0, 0); -#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t)) - #ifdef PMAC_SUPPORT_AUTOMUTE /* @@ -420,7 +416,7 @@ int __init snd_pmac_burgundy_init(pmac_t */ strcpy(chip->card->mixername, "PowerMac Burgundy"); - for (i = 0; i < num_controls(snd_pmac_burgundy_mixers); i++) { + for (i = 0; i < ARRAY_SIZE(snd_pmac_burgundy_mixers); i++) { if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_pmac_burgundy_mixers[i], chip))) < 0) return err; } --- linux-2.6.9-rc1/sound/ppc/daca.c 2004-08-24 00:01:55.000000000 -0700 +++ 25/sound/ppc/daca.c 2004-09-02 20:51:53.000000000 -0700 @@ -28,8 +28,6 @@ #include #include "pmac.h" -#define chip_t pmac_t - /* i2c address */ #define DACA_I2C_ADDR 0x4d @@ -217,8 +215,6 @@ static snd_kcontrol_new_t daca_mixers[] }, }; -#define num_controls(ary) (sizeof(ary) / sizeof(snd_kcontrol_new_t)) - #ifdef CONFIG_PMAC_PBOOK static void daca_resume(pmac_t *chip) @@ -272,7 +268,7 @@ int __init snd_pmac_daca_init(pmac_t *ch */ strcpy(chip->card->mixername, "PowerMac DACA"); - for (i = 0; i < num_controls(daca_mixers); i++) { + for (i = 0; i < ARRAY_SIZE(daca_mixers); i++) { if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&daca_mixers[i], chip))) < 0) return err; } --- linux-2.6.9-rc1/sound/ppc/Kconfig 2004-08-24 00:02:32.000000000 -0700 +++ 25/sound/ppc/Kconfig 2004-09-02 20:51:53.000000000 -0700 @@ -6,9 +6,12 @@ menu "ALSA PowerMac devices" comment "ALSA PowerMac requires I2C" depends on SND && I2C=n +comment "ALSA PowerMac requires INPUT" + depends on SND && INPUT=n + config SND_POWERMAC tristate "PowerMac (AWACS, DACA, Burgundy, Tumbler, Keywest)" - depends on SND && I2C + depends on SND && I2C && INPUT select SND_PCM endmenu --- linux-2.6.9-rc1/sound/ppc/Makefile 2004-08-24 00:02:32.000000000 -0700 +++ 25/sound/ppc/Makefile 2004-09-02 20:51:53.000000000 -0700 @@ -3,7 +3,7 @@ # Copyright (c) 2001 by Jaroslav Kysela # -snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o +snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o beep.o # Toplevel Module Dependency obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o --- linux-2.6.9-rc1/sound/ppc/pmac.c 2004-08-24 00:03:49.000000000 -0700 +++ 25/sound/ppc/pmac.c 2004-09-02 20:51:53.000000000 -0700 @@ -36,8 +36,6 @@ #include #endif -#define chip_t pmac_t - #if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) static int snd_pmac_register_sleep_notifier(pmac_t *chip); @@ -52,8 +50,8 @@ static int awacs_freqs[8] = { 44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350 }; /* fixed frequency table for tumbler */ -static int tumbler_freqs[2] = { - 48000, 44100 +static int tumbler_freqs[1] = { + 44100 }; /* @@ -86,7 +84,7 @@ static void snd_pmac_dbdma_free(pmac_dbd * look up frequency table */ -static unsigned int snd_pmac_rate_index(pmac_t *chip, pmac_stream_t *rec, unsigned int rate) +unsigned int snd_pmac_rate_index(pmac_t *chip, pmac_stream_t *rec, unsigned int rate) { int i, ok, found; @@ -203,7 +201,6 @@ static int snd_pmac_pcm_prepare(pmac_t * { int i; volatile struct dbdma_cmd *cp; - unsigned long flags; snd_pcm_runtime_t *runtime = subs->runtime; int rate_index; long offset; @@ -226,15 +223,17 @@ static int snd_pmac_pcm_prepare(pmac_t * /* We really want to execute a DMA stop command, after the AWACS * is initialized. * For reasons I don't understand, it stops the hissing noise - * common to many PowerBook G3 systems (like mine :-). + * common to many PowerBook G3 systems and random noise otherwise + * captured on iBook2's about every third time. -ReneR */ - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock_irq(&chip->reg_lock); snd_pmac_dma_stop(rec); - if (rec->stream == SNDRV_PCM_STREAM_PLAYBACK) { - st_le16(&chip->extra_dma.cmds->command, DBDMA_STOP); - snd_pmac_dma_set_command(rec, &chip->extra_dma); - snd_pmac_dma_run(rec, RUN); - } + st_le16(&chip->extra_dma.cmds->command, DBDMA_STOP); + snd_pmac_dma_set_command(rec, &chip->extra_dma); + snd_pmac_dma_run(rec, RUN); + spin_unlock_irq(&chip->reg_lock); + mdelay(5); + spin_lock_irq(&chip->reg_lock); /* continuous DMA memory type doesn't provide the physical address, * so we need to resolve the address here... */ @@ -252,7 +251,7 @@ static int snd_pmac_pcm_prepare(pmac_t * snd_pmac_dma_stop(rec); snd_pmac_dma_set_command(rec, &rec->cmd); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock_irq(&chip->reg_lock); return 0; } @@ -264,7 +263,6 @@ static int snd_pmac_pcm_prepare(pmac_t * static int snd_pmac_pcm_trigger(pmac_t *chip, pmac_stream_t *rec, snd_pcm_substream_t *subs, int cmd) { - unsigned long flags; volatile struct dbdma_cmd *cp; int i, command; @@ -275,7 +273,7 @@ static int snd_pmac_pcm_trigger(pmac_t * return -EBUSY; command = (subs->stream == SNDRV_PCM_STREAM_PLAYBACK ? OUTPUT_MORE : INPUT_MORE) + INTR_ALWAYS; - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock(&chip->reg_lock); snd_pmac_beep_stop(chip); snd_pmac_pcm_set_format(chip); for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) @@ -284,18 +282,18 @@ static int snd_pmac_pcm_trigger(pmac_t * (void)in_le32(&rec->dma->status); snd_pmac_dma_run(rec, RUN|WAKE); rec->running = 1; - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock(&chip->reg_lock); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: - spin_lock_irqsave(&chip->reg_lock, flags); + spin_lock(&chip->reg_lock); rec->running = 0; /*printk("stopped!!\n");*/ snd_pmac_dma_stop(rec); for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) out_le16(&cp->command, DBDMA_STOP); - spin_unlock_irqrestore(&chip->reg_lock, flags); + spin_unlock(&chip->reg_lock); break; default: @@ -490,14 +488,12 @@ static int snd_pmac_pcm_open(pmac_t *chi snd_pcm_runtime_t *runtime = subs->runtime; int i, j, fflags; static int typical_freqs[] = { - 48000, 44100, 22050, 11025, 0, }; static int typical_freq_flags[] = { - SNDRV_PCM_RATE_48000, SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_22050, SNDRV_PCM_RATE_11025, @@ -651,7 +647,7 @@ int __init snd_pmac_pcm_new(pmac_t *chip pcm->private_data = chip; pcm->private_free = pmac_pcm_free; - pcm->info_flags = 0; + pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; strcpy(pcm->name, chip->card->shortname); chip->pcm = pcm; @@ -683,12 +679,41 @@ static void snd_pmac_dbdma_reset(pmac_t /* + * handling beep + */ +void snd_pmac_beep_dma_start(pmac_t *chip, int bytes, unsigned long addr, int speed) +{ + pmac_stream_t *rec = &chip->playback; + + snd_pmac_dma_stop(rec); + st_le16(&chip->extra_dma.cmds->req_count, bytes); + st_le16(&chip->extra_dma.cmds->xfer_status, 0); + st_le32(&chip->extra_dma.cmds->cmd_dep, chip->extra_dma.addr); + st_le32(&chip->extra_dma.cmds->phy_addr, addr); + st_le16(&chip->extra_dma.cmds->command, OUTPUT_MORE + BR_ALWAYS); + out_le32(&chip->awacs->control, + (in_le32(&chip->awacs->control) & ~0x1f00) + | (speed << 8)); + out_le32(&chip->awacs->byteswap, 0); + snd_pmac_dma_set_command(rec, &chip->extra_dma); + snd_pmac_dma_run(rec, RUN); +} + +void snd_pmac_beep_dma_stop(pmac_t *chip) +{ + snd_pmac_dma_stop(&chip->playback); + st_le16(&chip->extra_dma.cmds->command, DBDMA_STOP); + snd_pmac_pcm_set_format(chip); /* reset format */ +} + + +/* * interrupt handlers */ static irqreturn_t snd_pmac_tx_intr(int irq, void *devid, struct pt_regs *regs) { - pmac_t *chip = snd_magic_cast(pmac_t, devid, return IRQ_NONE); + pmac_t *chip = devid; snd_pmac_pcm_update(chip, &chip->playback); return IRQ_HANDLED; } @@ -697,7 +722,7 @@ snd_pmac_tx_intr(int irq, void *devid, s static irqreturn_t snd_pmac_rx_intr(int irq, void *devid, struct pt_regs *regs) { - pmac_t *chip = snd_magic_cast(pmac_t, devid, return IRQ_NONE); + pmac_t *chip = devid; snd_pmac_pcm_update(chip, &chip->capture); return IRQ_HANDLED; } @@ -706,7 +731,7 @@ snd_pmac_rx_intr(int irq, void *devid, s static irqreturn_t snd_pmac_ctrl_intr(int irq, void *devid, struct pt_regs *regs) { - pmac_t *chip = snd_magic_cast(pmac_t, devid, return IRQ_NONE); + pmac_t *chip = devid; int ctrl = in_le32(&chip->awacs->control); /*printk("pmac: control interrupt.. 0x%x\n", ctrl);*/ @@ -776,6 +801,8 @@ static int snd_pmac_free(pmac_t *chip) if (chip->mixer_free) chip->mixer_free(chip); + snd_pmac_detach_beep(chip); + /* release resources */ if (chip->irq >= 0) free_irq(chip->irq, (void*)chip); @@ -802,7 +829,7 @@ static int snd_pmac_free(pmac_t *chip) release_OF_resource(chip->node, i); } } - snd_magic_kfree(chip); + kfree(chip); return 0; } @@ -812,7 +839,7 @@ static int snd_pmac_free(pmac_t *chip) */ static int snd_pmac_dev_free(snd_device_t *device) { - pmac_t *chip = snd_magic_cast(pmac_t, device->device_data, return -ENXIO); + pmac_t *chip = device->device_data; return snd_pmac_free(chip); } @@ -862,7 +889,7 @@ static int __init snd_pmac_detect(pmac_t chip->can_byte_swap = 1; chip->can_duplex = 1; chip->can_capture = 1; - chip->num_freqs = 8; + chip->num_freqs = ARRAY_SIZE(awacs_freqs); chip->freq_table = awacs_freqs; chip->control_mask = MASK_IEPC | MASK_IEE | 0x11; /* default */ @@ -920,14 +947,14 @@ static int __init snd_pmac_detect(pmac_t chip->can_capture = 0; /* no capture */ chip->can_duplex = 0; // chip->can_byte_swap = 0; /* FIXME: check this */ - chip->num_freqs = 2; + chip->num_freqs = ARRAY_SIZE(tumbler_freqs); chip->freq_table = tumbler_freqs; chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ } if (device_is_compatible(sound, "snapper")) { chip->model = PMAC_SNAPPER; // chip->can_byte_swap = 0; /* FIXME: check this */ - chip->num_freqs = 2; + chip->num_freqs = ARRAY_SIZE(tumbler_freqs); chip->freq_table = tumbler_freqs; chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ } @@ -1069,7 +1096,7 @@ int __init snd_pmac_new(snd_card_t *card snd_runtime_check(chip_return, return -EINVAL); *chip_return = NULL; - chip = snd_magic_kcalloc(pmac_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; chip->card = card; @@ -1206,7 +1233,7 @@ int __init snd_pmac_new(snd_card_t *card static int snd_pmac_suspend(snd_card_t *card, unsigned int state) { - pmac_t *chip = snd_magic_cast(pmac_t, card->pm_private_data, return -EINVAL); + pmac_t *chip = card->pm_private_data; unsigned long flags; if (chip->suspend) @@ -1228,7 +1255,7 @@ static int snd_pmac_suspend(snd_card_t * static int snd_pmac_resume(snd_card_t *card, unsigned int state) { - pmac_t *chip = snd_magic_cast(pmac_t, card->pm_private_data, return -EINVAL); + pmac_t *chip = card->pm_private_data; snd_pmac_sound_feature(chip, 1); if (chip->resume) --- linux-2.6.9-rc1/sound/ppc/pmac.h 2004-08-24 00:02:59.000000000 -0700 +++ 25/sound/ppc/pmac.h 2004-09-02 20:51:53.000000000 -0700 @@ -84,7 +84,7 @@ struct snd_pmac_stream { snd_pcm_substream_t *substream; - unsigned int cur_freqs; /* currently available frequences */ + unsigned int cur_freqs; /* currently available frequencies */ unsigned int cur_formats; /* currently available formats */ }; @@ -155,6 +155,7 @@ struct snd_pmac { void (*mixer_free)(pmac_t *); snd_kcontrol_t *master_sw_ctl; snd_kcontrol_t *speaker_sw_ctl; + snd_kcontrol_t *drc_sw_ctl; /* only used for tumbler -ReneR */ snd_kcontrol_t *hp_detect_ctl; /* lowlevel callbacks */ @@ -173,6 +174,12 @@ struct snd_pmac { int snd_pmac_new(snd_card_t *card, pmac_t **chip_return); int snd_pmac_pcm_new(pmac_t *chip); int snd_pmac_attach_beep(pmac_t *chip); +void snd_pmac_detach_beep(pmac_t *chip); +void snd_pmac_beep_stop(pmac_t *chip); +unsigned int snd_pmac_rate_index(pmac_t *chip, pmac_stream_t *rec, unsigned int rate); + +void snd_pmac_beep_dma_start(pmac_t *chip, int bytes, unsigned long addr, int speed); +void snd_pmac_beep_dma_stop(pmac_t *chip); /* initialize mixer */ int snd_pmac_awacs_init(pmac_t *chip); @@ -206,9 +213,4 @@ int snd_pmac_add_automute(pmac_t *chip); schedule_timeout(((msec) * HZ + 999) / 1000);\ } while (0) -#ifndef PMAC_SUPPORT_PCM_BEEP -#define snd_pmac_attach_beep(chip) 0 -#define snd_pmac_beep_stop(chip) /**/ -#endif - #endif /* __PMAC_H */ --- linux-2.6.9-rc1/sound/ppc/powermac.c 2004-08-24 00:03:49.000000000 -0700 +++ 25/sound/ppc/powermac.c 2004-09-02 20:51:53.000000000 -0700 @@ -30,31 +30,19 @@ #define CHIP_NAME "PMac" MODULE_DESCRIPTION("PowerMac"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Apple,PowerMac}}"); +MODULE_SUPPORTED_DEVICE("{{Apple,PowerMac}}"); MODULE_LICENSE("GPL"); static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ -/* static int enable = 1; */ -#ifdef PMAC_SUPPORT_PCM_BEEP static int enable_beep = 1; -#endif module_param(index, int, 0444); MODULE_PARM_DESC(index, "Index value for " CHIP_NAME " soundchip."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param(id, charp, 0444); MODULE_PARM_DESC(id, "ID string for " CHIP_NAME " soundchip."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); -/* module_param(enable, bool, 0444); - MODULE_PARM_DESC(enable, "Enable this soundchip."); - MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); */ -#ifdef PMAC_SUPPORT_PCM_BEEP module_param(enable_beep, bool, 0444); MODULE_PARM_DESC(enable_beep, "Enable beep using PCM."); -MODULE_PARM_SYNTAX(enable_beep, SNDRV_ENABLED "," SNDRV_BOOLEAN_TRUE_DESC); -#endif /* @@ -133,10 +121,8 @@ static int __init snd_pmac_probe(void) goto __error; chip->initialized = 1; -#ifdef PMAC_SUPPORT_PCM_BEEP if (enable_beep) snd_pmac_attach_beep(chip); -#endif if ((err = snd_card_register(card)) < 0) goto __error; @@ -151,18 +137,14 @@ __error: /* - * MODULE sutff + * MODULE stuff */ static int __init alsa_card_pmac_init(void) { int err; - if ((err = snd_pmac_probe()) < 0) { -#ifdef MODULE - printk(KERN_ERR "no PMac soundchip found\n"); -#endif + if ((err = snd_pmac_probe()) < 0) return err; - } return 0; } --- linux-2.6.9-rc1/sound/ppc/tumbler.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/sound/ppc/tumbler.c 2004-09-02 20:51:53.000000000 -0700 @@ -16,6 +16,11 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Rene Rebe : + * * update from shadow registers on wakeup and headphone plug + * * automatically toggle DRC on headphone plug + * */ @@ -36,8 +41,6 @@ #include "pmac.h" #include "tumbler_volume.h" -#define chip_t pmac_t - /* i2c address for tumbler */ #define TAS_I2C_ADDR 0x34 @@ -759,12 +762,6 @@ static snd_kcontrol_new_t tumbler_mixers DEFINE_MONO("Tone Control - Treble", treble), DEFINE_MONO("PCM Playback Volume", pcm), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "DRC Switch", - .info = snd_pmac_boolean_mono_info, - .get = tumbler_get_drc_switch, - .put = tumbler_put_drc_switch - }, - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "DRC Range", .info = tumbler_info_drc_value, .get = tumbler_get_drc_value, @@ -791,12 +788,6 @@ static snd_kcontrol_new_t snapper_mixers DEFINE_SNAPPER_MONO("Tone Control - Bass", bass), DEFINE_SNAPPER_MONO("Tone Control - Treble", treble), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "DRC Switch", - .info = snd_pmac_boolean_mono_info, - .get = tumbler_get_drc_switch, - .put = tumbler_put_drc_switch - }, - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "DRC Range", .info = tumbler_info_drc_value, .get = tumbler_get_drc_value, @@ -826,6 +817,14 @@ static snd_kcontrol_new_t tumbler_speake .put = tumbler_put_mute_switch, .private_value = TUMBLER_MUTE_AMP, }; +static snd_kcontrol_new_t tumbler_drc_sw __initdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DRC Switch", + .info = snd_pmac_boolean_mono_info, + .get = tumbler_get_drc_switch, + .put = tumbler_put_drc_switch +}; + #ifdef PMAC_SUPPORT_AUTOMUTE /* @@ -847,6 +846,29 @@ static void check_mute(pmac_t *chip, pma } } +static struct work_struct device_change; + +static void +device_change_handler(void *self) +{ + pmac_t *chip = (pmac_t*) self; + pmac_tumbler_t *mix; + + if (!chip) + return; + + mix = chip->mixer_data; + + /* first set the DRC so the speaker do not explode -ReneR */ + if (chip->model == PMAC_TUMBLER) + tumbler_set_drc(mix); + else + snapper_set_drc(mix); + + /* reset the master volume so the correct amplification is applied */ + tumbler_set_master_volume(mix); +} + static void tumbler_update_automute(pmac_t *chip, int do_notify) { if (chip->auto_mute) { @@ -856,14 +878,25 @@ static void tumbler_update_automute(pmac /* mute speaker */ check_mute(chip, &mix->amp_mute, 1, do_notify, chip->speaker_sw_ctl); check_mute(chip, &mix->hp_mute, 0, do_notify, chip->master_sw_ctl); + mix->drc_enable = 0; + } else { /* unmute speaker */ check_mute(chip, &mix->amp_mute, 0, do_notify, chip->speaker_sw_ctl); check_mute(chip, &mix->hp_mute, 1, do_notify, chip->master_sw_ctl); + mix->drc_enable = 1; } - if (do_notify) + if (do_notify) { snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hp_detect_ctl->id); + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, + &chip->drc_sw_ctl->id); + } + + /* finally we need to schedule an update of the mixer values + (master and DRC are enough for now) -ReneR */ + schedule_work(&device_change); + } } #endif /* PMAC_SUPPORT_AUTOMUTE */ @@ -872,7 +905,7 @@ static void tumbler_update_automute(pmac /* interrupt - headphone plug changed */ static irqreturn_t headphone_intr(int irq, void *devid, struct pt_regs *regs) { - pmac_t *chip = snd_magic_cast(pmac_t, devid, return IRQ_NONE); + pmac_t *chip = devid; if (chip->update_automute && chip->initialized) { chip->update_automute(chip, 1); return IRQ_HANDLED; @@ -1114,11 +1147,17 @@ int __init snd_pmac_tumbler_init(pmac_t chip->speaker_sw_ctl = snd_ctl_new1(&tumbler_speaker_sw, chip); if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0) return err; + chip->drc_sw_ctl = snd_ctl_new1(&tumbler_drc_sw, chip); + if ((err = snd_ctl_add(chip->card, chip->drc_sw_ctl)) < 0) + return err; + #ifdef CONFIG_PMAC_PBOOK chip->resume = tumbler_resume; #endif + INIT_WORK(&device_change, device_change_handler, (void *)chip); + #ifdef PMAC_SUPPORT_AUTOMUTE if (mix->headphone_irq >=0 && (err = snd_pmac_add_automute(chip)) < 0) return err; --- linux-2.6.9-rc1/sound/sparc/amd7930.c 2004-08-24 00:02:22.000000000 -0700 +++ 25/sound/sparc/amd7930.c 2004-09-02 20:51:53.000000000 -0700 @@ -54,18 +54,14 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for Sun AMD7930 soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for Sun AMD7930 soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable Sun AMD7930 soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); MODULE_AUTHOR("Thomas K. Dyas and David S. Miller"); MODULE_DESCRIPTION("Sun AMD7930"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Sun,AMD7930}}"); +MODULE_SUPPORTED_DEVICE("{{Sun,AMD7930}}"); /* Device register layout. */ @@ -345,7 +341,6 @@ typedef struct snd_amd7930 { unsigned int regs_size; struct snd_amd7930 *next; } amd7930_t; -#define chip_t amd7930_t static amd7930_t *amd7930_list; @@ -475,7 +470,6 @@ static __const__ __u16 ger_coeff[] = { 0x000b, /* 16.9 dB */ 0x000f /* 18. dB */ }; -#define NR_GER_COEFFS (sizeof(ger_coeff) / sizeof(ger_coeff[0])) /* Update amd7930_map settings and program them into the hardware. * The amd->lock is held and local interrupts are disabled. @@ -487,7 +481,7 @@ static void __amd7930_update_map(amd7930 map->gx = gx_coeff[amd->rgain]; map->stgr = gx_coeff[amd->mgain]; - level = (amd->pgain * (256 + NR_GER_COEFFS)) >> 8; + level = (amd->pgain * (256 + ARRAY_SIZE(ger_coeff))) >> 8; if (level >= 256) { map->ger = ger_coeff[level - 256]; map->gr = gx_coeff[255]; @@ -764,7 +758,7 @@ static snd_pcm_ops_t snd_amd7930_capture static void snd_amd7930_pcm_free(snd_pcm_t *pcm) { - amd7930_t *amd = snd_magic_cast(amd7930_t, pcm->private_data, return); + amd7930_t *amd = pcm->private_data; amd->pcm = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); @@ -916,8 +910,6 @@ static snd_kcontrol_new_t amd7930_contro }, }; -#define NUM_AMD7930_CONTROLS (sizeof(amd7930_controls)/sizeof(snd_kcontrol_new_t)) - static int __init snd_amd7930_mixer(amd7930_t *amd) { snd_card_t *card; @@ -928,7 +920,7 @@ static int __init snd_amd7930_mixer(amd7 card = amd->card; strcpy(card->mixername, card->shortname); - for (idx = 0; idx < NUM_AMD7930_CONTROLS; idx++) { + for (idx = 0; idx < ARRAY_SIZE(amd7930_controls); idx++) { if ((err = snd_ctl_add(card, snd_ctl_new1(&amd7930_controls[idx], amd))) < 0) return err; @@ -947,14 +939,14 @@ static int snd_amd7930_free(amd7930_t *a if (amd->regs) sbus_iounmap(amd->regs, amd->regs_size); - snd_magic_kfree(amd); + kfree(amd); return 0; } static int snd_amd7930_dev_free(snd_device_t *device) { - amd7930_t *amd = snd_magic_cast(amd7930_t, device->device_data, return -ENXIO); + amd7930_t *amd = device->device_data; return snd_amd7930_free(amd); } @@ -976,7 +968,7 @@ static int __init snd_amd7930_create(snd int err; *ramd = NULL; - amd = snd_magic_kcalloc(amd7930_t, 0, GFP_KERNEL); + amd = kcalloc(1, sizeof(*amd), GFP_KERNEL); if (amd == NULL) return -ENOMEM; --- linux-2.6.9-rc1/sound/sparc/cs4231.c 2004-08-24 00:01:52.000000000 -0700 +++ 25/sound/sparc/cs4231.c 2004-09-02 20:51:53.000000000 -0700 @@ -53,18 +53,14 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for Sun CS4231 soundcard."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for Sun CS4231 soundcard."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable Sun CS4231 soundcard."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); MODULE_AUTHOR("Jaroslav Kysela, Derrick J. Brashear and David S. Miller"); MODULE_DESCRIPTION("Sun CS4231"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Sun,CS4231}}"); +MODULE_SUPPORTED_DEVICE("{{Sun,CS4231}}"); typedef struct snd_cs4231 { spinlock_t lock; @@ -112,7 +108,6 @@ typedef struct snd_cs4231 { unsigned int regs_size; struct snd_cs4231 *next; } cs4231_t; -#define chip_t cs4231_t static cs4231_t *cs4231_list; @@ -1232,7 +1227,7 @@ static void snd_cs4231_generic_interrupt #ifdef SBUS_SUPPORT static irqreturn_t snd_cs4231_sbus_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - cs4231_t *chip = snd_magic_cast(cs4231_t, dev_id, return); + cs4231_t *chip = dev_id; u32 csr; csr = sbus_readl(chip->port + APCCSR); @@ -1256,7 +1251,7 @@ static irqreturn_t snd_cs4231_sbus_inter #ifdef EBUS_SUPPORT static void snd_cs4231_ebus_play_callback(struct ebus_dma_info *p, int event, void *cookie) { - cs4231_t *chip = snd_magic_cast(cs4231_t, cookie, return); + cs4231_t *chip = cookie; if (chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE) { snd_pcm_period_elapsed(chip->playback_substream); @@ -1267,7 +1262,7 @@ static void snd_cs4231_ebus_play_callbac static void snd_cs4231_ebus_capture_callback(struct ebus_dma_info *p, int event, void *cookie) { - cs4231_t *chip = snd_magic_cast(cs4231_t, cookie, return); + cs4231_t *chip = cookie; if (chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE) { snd_pcm_period_elapsed(chip->capture_substream); @@ -1547,7 +1542,7 @@ static snd_pcm_ops_t snd_cs4231_capture_ static void snd_cs4231_pcm_free(snd_pcm_t *pcm) { - cs4231_t *chip = snd_magic_cast(cs4231_t, pcm->private_data, return); + cs4231_t *chip = pcm->private_data; chip->pcm = NULL; snd_pcm_lib_preallocate_free_for_all(pcm); } @@ -1592,7 +1587,7 @@ int snd_cs4231_pcm(cs4231_t *chip) static void snd_cs4231_timer_free(snd_timer_t *timer) { - cs4231_t *chip = snd_magic_cast(cs4231_t, timer->private_data, return); + cs4231_t *chip = timer->private_data; chip->timer = NULL; } @@ -1821,8 +1816,6 @@ int snd_cs4231_put_double(snd_kcontrol_t return change; } -#define CS4231_CONTROLS (sizeof(snd_cs4231_controls)/sizeof(snd_kcontrol_new_t)) - #define CS4231_SINGLE(xname, xindex, reg, shift, mask, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_cs4231_info_single, \ @@ -1875,7 +1868,7 @@ int snd_cs4231_mixer(cs4231_t *chip) strcpy(card->mixername, chip->pcm->name); - for (idx = 0; idx < CS4231_CONTROLS; idx++) { + for (idx = 0; idx < ARRAY_SIZE(snd_cs4231_controls); idx++) { if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4231_controls[idx], chip))) < 0) @@ -1950,14 +1943,14 @@ static int snd_cs4231_sbus_free(cs4231_t if (chip->timer) snd_device_free(chip->card, chip->timer); - snd_magic_kfree(chip); + kfree(chip); return 0; } static int snd_cs4231_sbus_dev_free(snd_device_t *device) { - cs4231_t *cp = snd_magic_cast(cs4231_t, device->device_data, return -ENXIO); + cs4231_t *cp = device->device_data; return snd_cs4231_sbus_free(cp); } @@ -1975,7 +1968,7 @@ static int __init snd_cs4231_sbus_create int err; *rchip = NULL; - chip = snd_magic_kcalloc(cs4231_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; @@ -2064,14 +2057,14 @@ static int snd_cs4231_ebus_free(cs4231_t if (chip->timer) snd_device_free(chip->card, chip->timer); - snd_magic_kfree(chip); + kfree(chip); return 0; } static int snd_cs4231_ebus_dev_free(snd_device_t *device) { - cs4231_t *cp = snd_magic_cast(cs4231_t, device->device_data, return -ENXIO); + cs4231_t *cp = device->device_data; return snd_cs4231_ebus_free(cp); } @@ -2089,7 +2082,7 @@ static int __init snd_cs4231_ebus_create int err; *rchip = NULL; - chip = snd_magic_kcalloc(cs4231_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (chip == NULL) return -ENOMEM; --- linux-2.6.9-rc1/sound/synth/emux/emux.c 2004-08-24 00:02:47.000000000 -0700 +++ 25/sound/synth/emux/emux.c 2004-09-02 20:51:53.000000000 -0700 @@ -39,7 +39,7 @@ int snd_emux_new(snd_emux_t **remu) snd_emux_t *emu; *remu = NULL; - emu = snd_magic_kcalloc(snd_emux_t, 0, GFP_KERNEL); + emu = kcalloc(1, sizeof(*emu), GFP_KERNEL); if (emu == NULL) return -ENOMEM; @@ -77,7 +77,7 @@ int snd_emux_register(snd_emux_t *emu, s emu->card = card; emu->name = snd_kmalloc_strdup(name, GFP_KERNEL); - emu->voices = snd_kcalloc(sizeof(snd_emux_voice_t) * emu->max_voices, GFP_KERNEL); + emu->voices = kcalloc(emu->max_voices, sizeof(snd_emux_voice_t), GFP_KERNEL); if (emu->voices == NULL) return -ENOMEM; @@ -143,7 +143,7 @@ int snd_emux_free(snd_emux_t *emu) if (emu->name) kfree(emu->name); - snd_magic_kfree(emu); + kfree(emu); return 0; } --- linux-2.6.9-rc1/sound/synth/emux/emux_effect.c 2004-08-24 00:02:32.000000000 -0700 +++ 25/sound/synth/emux/emux_effect.c 2004-09-02 20:51:53.000000000 -0700 @@ -278,7 +278,7 @@ void snd_emux_create_effect(snd_emux_port_t *p) { int i; - p->effect = snd_kcalloc(sizeof(snd_emux_effect_table_t) * p->chset.max_channels, GFP_KERNEL); + p->effect = kcalloc(p->chset.max_channels, sizeof(snd_emux_effect_table_t), GFP_KERNEL); if (p->effect) { for (i = 0; i < p->chset.max_channels; i++) p->chset.channels[i].private = p->effect + i; --- linux-2.6.9-rc1/sound/synth/emux/emux_hwdep.c 2004-08-24 00:03:30.000000000 -0700 +++ 25/sound/synth/emux/emux_hwdep.c 2004-09-02 20:51:53.000000000 -0700 @@ -104,7 +104,7 @@ snd_emux_hwdep_misc_mode(snd_emux_t *emu static int snd_emux_hwdep_ioctl(snd_hwdep_t * hw, struct file *file, unsigned int cmd, unsigned long arg) { - snd_emux_t *emu = snd_magic_cast(snd_emux_t, hw->private_data, return -ENXIO); + snd_emux_t *emu = hw->private_data; switch (cmd) { case SNDRV_EMUX_IOCTL_VERSION: --- linux-2.6.9-rc1/sound/synth/emux/emux_nrpn.c 2004-08-24 00:01:51.000000000 -0700 +++ 25/sound/synth/emux/emux_nrpn.c 2004-09-02 20:51:53.000000000 -0700 @@ -22,8 +22,6 @@ #include "emux_voice.h" #include -#define NELEM(arr) (sizeof(arr)/sizeof((arr)[0])) - /* * conversion from NRPN/control parameters to Emu8000 raw parameters */ @@ -214,8 +212,6 @@ static nrpn_conv_table awe_effects[] = {26, EMUX_FX_REVERB, fx_reverb}, }; -static int num_awe_effects = NELEM(awe_effects); - /* * GS(SC88) NRPN effects; still experimental @@ -281,8 +277,6 @@ static nrpn_conv_table gs_effects[] = {10, EMUX_FX_LFO1_DELAY, gs_vib_delay}, }; -static int num_gs_effects = NELEM(gs_effects); - /* * NRPN events @@ -292,7 +286,7 @@ snd_emux_nrpn(void *p, snd_midi_channel_ { snd_emux_port_t *port; - port = snd_magic_cast(snd_emux_port_t, p, return); + port = p; snd_assert(port != NULL, return); snd_assert(chan != NULL, return); @@ -305,7 +299,7 @@ snd_emux_nrpn(void *p, snd_midi_channel_ chan->control[MIDI_CTL_LSB_DATA_ENTRY]; val -= 8192; send_converted_effect - (awe_effects, num_awe_effects, + (awe_effects, ARRAY_SIZE(awe_effects), port, chan, chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB], val, EMUX_FX_FLAG_SET); return; @@ -318,7 +312,7 @@ snd_emux_nrpn(void *p, snd_midi_channel_ /* only MSB is valid */ val = chan->control[MIDI_CTL_MSB_DATA_ENTRY]; send_converted_effect - (gs_effects, num_gs_effects, + (gs_effects, ARRAY_SIZE(gs_effects), port, chan, chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB], val, EMUX_FX_FLAG_ADD); return; @@ -362,12 +356,10 @@ static nrpn_conv_table xg_effects[] = {73, EMUX_FX_ENV2_ATTACK, xg_attack}, }; -static int num_xg_effects = NELEM(xg_effects); - int snd_emux_xg_control(snd_emux_port_t *port, snd_midi_channel_t *chan, int param) { - return send_converted_effect(xg_effects, num_xg_effects, + return send_converted_effect(xg_effects, ARRAY_SIZE(xg_effects), port, chan, param, chan->control[param], EMUX_FX_FLAG_ADD); @@ -382,7 +374,7 @@ snd_emux_sysex(void *p, unsigned char *b snd_emux_port_t *port; snd_emux_t *emu; - port = snd_magic_cast(snd_emux_port_t, p, return); + port = p; snd_assert(port != NULL, return); snd_assert(chset != NULL, return); emu = port->emu; --- linux-2.6.9-rc1/sound/synth/emux/emux_oss.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/sound/synth/emux/emux_oss.c 2004-09-02 20:51:53.000000000 -0700 @@ -108,7 +108,7 @@ snd_emux_open_seq_oss(snd_seq_oss_arg_t snd_seq_port_callback_t callback; char tmpname[64]; - emu = snd_magic_cast(snd_emux_t, closure, return -EINVAL); + emu = closure; snd_assert(arg != NULL && emu != NULL, return -ENXIO); down(&emu->register_mutex); @@ -179,7 +179,7 @@ snd_emux_close_seq_oss(snd_seq_oss_arg_t snd_emux_port_t *p; snd_assert(arg != NULL, return -ENXIO); - p = snd_magic_cast(snd_emux_port_t, arg->private_data, return -EINVAL); + p = arg->private_data; snd_assert(p != NULL, return -ENXIO); emu = p->emu; @@ -208,7 +208,7 @@ snd_emux_load_patch_seq_oss(snd_seq_oss_ int rc; snd_assert(arg != NULL, return -ENXIO); - p = snd_magic_cast(snd_emux_port_t, arg->private_data, return -EINVAL); + p = arg->private_data; snd_assert(p != NULL, return -ENXIO); emu = p->emu; @@ -248,7 +248,7 @@ snd_emux_ioctl_seq_oss(snd_seq_oss_arg_t snd_emux_t *emu; snd_assert(arg != NULL, return -ENXIO); - p = snd_magic_cast(snd_emux_port_t, arg->private_data, return -EINVAL); + p = arg->private_data; snd_assert(p != NULL, return -ENXIO); emu = p->emu; @@ -278,7 +278,7 @@ snd_emux_reset_seq_oss(snd_seq_oss_arg_t snd_emux_port_t *p; snd_assert(arg != NULL, return -ENXIO); - p = snd_magic_cast(snd_emux_port_t, arg->private_data, return -EINVAL); + p = arg->private_data; snd_assert(p != NULL, return -ENXIO); snd_emux_reset_port(p); return 0; @@ -296,7 +296,7 @@ snd_emux_event_oss_input(snd_seq_event_t snd_emux_port_t *p; unsigned char cmd, *data; - p = snd_magic_cast(snd_emux_port_t, private_data, return -EINVAL); + p = private_data; snd_assert(p != NULL, return -EINVAL); emu = p->emu; snd_assert(emu != NULL, return -EINVAL); @@ -339,9 +339,11 @@ emuspec_control(snd_emux_t *emu, snd_emu p2 = *(short *) &event[6]; switch (cmd) { +#if 0 /* don't do this atomically */ case _EMUX_OSS_REMOVE_LAST_SAMPLES: snd_soundfont_remove_unlocked(emu->sflist); break; +#endif case _EMUX_OSS_SEND_EFFECT: if (chan) snd_emux_send_effect_oss(port, chan, p1, p2); --- linux-2.6.9-rc1/sound/synth/emux/emux_proc.c 2004-08-24 00:03:32.000000000 -0700 +++ 25/sound/synth/emux/emux_proc.c 2004-09-02 20:51:53.000000000 -0700 @@ -36,7 +36,7 @@ snd_emux_proc_info_read(snd_info_entry_t snd_emux_t *emu; int i; - emu = snd_magic_cast(snd_emux_t, entry->private_data, return); + emu = entry->private_data; down(&emu->register_mutex); if (emu->name) snd_iprintf(buf, "Device: %s\n", emu->name); --- linux-2.6.9-rc1/sound/synth/emux/emux_seq.c 2004-08-24 00:03:50.000000000 -0700 +++ 25/sound/synth/emux/emux_seq.c 2004-09-02 20:51:53.000000000 -0700 @@ -146,14 +146,14 @@ snd_emux_create_port(snd_emux_t *emu, ch int i, type, cap; /* Allocate structures for this channel */ - if ((p = snd_magic_kcalloc(snd_emux_port_t, 0, GFP_KERNEL)) == NULL) { + if ((p = kcalloc(1, sizeof(*p), GFP_KERNEL)) == NULL) { snd_printk("no memory\n"); return NULL; } - p->chset.channels = snd_kcalloc(max_channels * sizeof(snd_midi_channel_t), GFP_KERNEL); + p->chset.channels = kcalloc(max_channels, sizeof(snd_midi_channel_t), GFP_KERNEL); if (p->chset.channels == NULL) { snd_printk("no memory\n"); - snd_magic_kfree(p); + kfree(p); return NULL; } for (i = 0; i < max_channels; i++) @@ -192,14 +192,14 @@ free_port(void *private_data) { snd_emux_port_t *p; - p = snd_magic_cast(snd_emux_port_t, private_data, return); + p = private_data; if (p) { #ifdef SNDRV_EMUX_USE_RAW_EFFECT snd_emux_delete_effect(p); #endif if (p->chset.channels) kfree(p->chset.channels); - snd_magic_kfree(p); + kfree(p); } } @@ -257,7 +257,7 @@ snd_emux_event_input(snd_seq_event_t *ev { snd_emux_port_t *port; - port = snd_magic_cast(snd_emux_port_t, private_data, return -EINVAL); + port = private_data; snd_assert(port != NULL && ev != NULL, return -EINVAL); snd_midi_process_event(&emux_ops, ev, &port->chset); @@ -308,7 +308,7 @@ snd_emux_use(void *private_data, snd_seq snd_emux_port_t *p; snd_emux_t *emu; - p = snd_magic_cast(snd_emux_port_t, private_data, return -EINVAL); + p = private_data; snd_assert(p != NULL, return -EINVAL); emu = p->emu; snd_assert(emu != NULL, return -EINVAL); @@ -329,7 +329,7 @@ snd_emux_unuse(void *private_data, snd_s snd_emux_port_t *p; snd_emux_t *emu; - p = snd_magic_cast(snd_emux_port_t, private_data, return -EINVAL); + p = private_data; snd_assert(p != NULL, return -EINVAL); emu = p->emu; snd_assert(emu != NULL, return -EINVAL); @@ -383,7 +383,7 @@ int snd_emux_init_virmidi(snd_emux_t *em if (emu->midi_ports <= 0) return 0; - emu->vmidi = snd_kcalloc(sizeof(snd_rawmidi_t*) * emu->midi_ports, GFP_KERNEL); + emu->vmidi = kcalloc(emu->midi_ports, sizeof(snd_rawmidi_t*), GFP_KERNEL); if (emu->vmidi == NULL) return -ENOMEM; @@ -392,7 +392,7 @@ int snd_emux_init_virmidi(snd_emux_t *em snd_virmidi_dev_t *rdev; if (snd_virmidi_new(card, emu->midi_devidx + i, &rmidi) < 0) goto __error; - rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, continue); + rdev = rmidi->private_data; sprintf(rmidi->name, "%s Synth MIDI", emu->name); rdev->seq_mode = SNDRV_VIRMIDI_SEQ_ATTACH; rdev->client = emu->client; --- linux-2.6.9-rc1/sound/synth/emux/emux_synth.c 2004-08-24 00:02:33.000000000 -0700 +++ 25/sound/synth/emux/emux_synth.c 2004-09-02 20:51:53.000000000 -0700 @@ -61,7 +61,7 @@ snd_emux_note_on(void *p, int note, int unsigned long flags; snd_emux_port_t *port; - port = snd_magic_cast(snd_emux_port_t, p, return); + port = p; snd_assert(port != NULL && chan != NULL, return); emu = port->emu; @@ -160,7 +160,7 @@ snd_emux_note_off(void *p, int note, int unsigned long flags; snd_emux_port_t *port; - port = snd_magic_cast(snd_emux_port_t, p, return); + port = p; snd_assert(port != NULL && chan != NULL, return); emu = port->emu; @@ -201,7 +201,7 @@ snd_emux_note_off(void *p, int note, int */ void snd_emux_timer_callback(unsigned long data) { - snd_emux_t *emu = snd_magic_cast(snd_emux_t, (void*)data, return); + snd_emux_t *emu = (snd_emux_t*) data; snd_emux_voice_t *vp; int ch, do_again = 0; @@ -238,7 +238,7 @@ snd_emux_key_press(void *p, int note, in unsigned long flags; snd_emux_port_t *port; - port = snd_magic_cast(snd_emux_port_t, p, return); + port = p; snd_assert(port != NULL && chan != NULL, return); emu = port->emu; @@ -322,7 +322,7 @@ snd_emux_control(void *p, int type, snd_ { snd_emux_port_t *port; - port = snd_magic_cast(snd_emux_port_t, p, return); + port = p; snd_assert(port != NULL && chan != NULL, return); switch (type) { @@ -402,7 +402,7 @@ snd_emux_terminate_note(void *p, int not snd_emux_t *emu; snd_emux_port_t *port; - port = snd_magic_cast(snd_emux_port_t, p, return); + port = p; snd_assert(port != NULL && chan != NULL, return); emu = port->emu; --- linux-2.6.9-rc1/sound/synth/emux/soundfont.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/sound/synth/emux/soundfont.c 2004-09-02 20:51:53.000000000 -0700 @@ -64,15 +64,14 @@ static void snd_sf_clear(snd_sf_list_t * /* * lock access to sflist */ -static int -lock_preset(snd_sf_list_t *sflist, int nonblock) +static void +lock_preset(snd_sf_list_t *sflist) { - if (nonblock) { - if (down_trylock(&sflist->presets_mutex)) - return -EBUSY; - } else - down(&sflist->presets_mutex); - return 0; + unsigned long flags; + down(&sflist->presets_mutex); + spin_lock_irqsave(&sflist->lock, flags); + sflist->presets_locked = 1; + spin_unlock_irqrestore(&sflist->lock, flags); } @@ -82,6 +81,10 @@ lock_preset(snd_sf_list_t *sflist, int n static void unlock_preset(snd_sf_list_t *sflist) { + unsigned long flags; + spin_lock_irqsave(&sflist->lock, flags); + sflist->presets_locked = 0; + spin_unlock_irqrestore(&sflist->lock, flags); up(&sflist->presets_mutex); } @@ -143,7 +146,7 @@ snd_soundfont_load(snd_sf_list_t *sflist if (patch.type == SNDRV_SFNT_OPEN_PATCH) { /* grab sflist to open */ - lock_preset(sflist, 0); + lock_preset(sflist); rc = open_patch(sflist, data, count, client); unlock_preset(sflist); return rc; @@ -157,7 +160,7 @@ snd_soundfont_load(snd_sf_list_t *sflist } spin_unlock_irqrestore(&sflist->lock, flags); - lock_preset(sflist, 0); + lock_preset(sflist); rc = -EINVAL; switch (patch.type) { case SNDRV_SFNT_LOAD_INFO: @@ -237,8 +240,10 @@ open_patch(snd_sf_list_t *sflist, const return -ENOMEM; } + spin_lock_irqsave(&sflist->lock, flags); sflist->open_client = client; sflist->currsf = sf; + spin_unlock_irqrestore(&sflist->lock, flags); return 0; } @@ -261,7 +266,7 @@ newsf(snd_sf_list_t *sflist, int type, c } /* not found -- create a new one */ - sf = (snd_soundfont_t*)snd_kcalloc(sizeof(*sf), GFP_KERNEL); + sf = kcalloc(1, sizeof(*sf), GFP_KERNEL); if (sf == NULL) return NULL; sf->id = sflist->fonts_size; @@ -296,8 +301,12 @@ is_identical_font(snd_soundfont_t *sf, i static int close_patch(snd_sf_list_t *sflist) { + unsigned long flags; + + spin_lock_irqsave(&sflist->lock, flags); sflist->currsf = NULL; sflist->open_client = -1; + spin_unlock_irqrestore(&sflist->lock, flags); rebuild_presets(sflist); @@ -337,7 +346,7 @@ sf_zone_new(snd_sf_list_t *sflist, snd_s { snd_sf_zone_t *zp; - if ((zp = snd_kcalloc(sizeof(*zp), GFP_KERNEL)) == NULL) + if ((zp = kcalloc(1, sizeof(*zp), GFP_KERNEL)) == NULL) return NULL; zp->next = sf->zones; sf->zones = zp; @@ -368,7 +377,7 @@ sf_sample_new(snd_sf_list_t *sflist, snd { snd_sf_sample_t *sp; - if ((sp = snd_kcalloc(sizeof(*sp), GFP_KERNEL)) == NULL) + if ((sp = kcalloc(1, sizeof(*sp), GFP_KERNEL)) == NULL) return NULL; sp->next = sf->samples; @@ -1090,7 +1099,7 @@ snd_soundfont_load_guspatch(snd_sf_list_ long count, int client) { int rc; - lock_preset(sflist, 0); + lock_preset(sflist); rc = load_guspatch(sflist, data, count, client); unlock_preset(sflist); return rc; @@ -1197,17 +1206,23 @@ snd_soundfont_search_zone(snd_sf_list_t snd_sf_zone_t **table, int max_layers) { int nvoices; + unsigned long flags; - if (lock_preset(sflist, 1)) + /* this function is supposed to be called atomically, + * so we check the lock. if it's busy, just returns 0 to + * tell the caller the busy state + */ + spin_lock_irqsave(&sflist->lock, flags); + if (sflist->presets_locked) { + spin_unlock_irqrestore(&sflist->lock, flags); return 0; - + } nvoices = search_zones(sflist, notep, vel, preset, bank, table, max_layers, 0); if (! nvoices) { if (preset != def_preset || bank != def_bank) nvoices = search_zones(sflist, notep, vel, def_preset, def_bank, table, max_layers, 0); } - unlock_preset(sflist); - + spin_unlock_irqrestore(&sflist->lock, flags); return nvoices; } @@ -1347,7 +1362,7 @@ snd_sf_new(snd_sf_callback_t *callback, { snd_sf_list_t *sflist; - if ((sflist = snd_kcalloc(sizeof(snd_sf_list_t), GFP_KERNEL)) == NULL) + if ((sflist = kcalloc(1, sizeof(*sflist), GFP_KERNEL)) == NULL) return NULL; init_MUTEX(&sflist->presets_mutex); @@ -1371,7 +1386,7 @@ snd_sf_free(snd_sf_list_t *sflist) if (sflist == NULL) return; - lock_preset(sflist, 0); + lock_preset(sflist); if (sflist->callback.sample_reset) sflist->callback.sample_reset(sflist->callback.private_data); snd_sf_clear(sflist); @@ -1387,7 +1402,7 @@ snd_sf_free(snd_sf_list_t *sflist) int snd_soundfont_remove_samples(snd_sf_list_t *sflist) { - lock_preset(sflist, 0); + lock_preset(sflist); if (sflist->callback.sample_reset) sflist->callback.sample_reset(sflist->callback.private_data); snd_sf_clear(sflist); @@ -1407,8 +1422,7 @@ snd_soundfont_remove_unlocked(snd_sf_lis snd_sf_zone_t *zp, *nextzp; snd_sf_sample_t *sp, *nextsp; - if (lock_preset(sflist, 1)) - return -EBUSY; + lock_preset(sflist); if (sflist->callback.sample_reset) sflist->callback.sample_reset(sflist->callback.private_data); --- linux-2.6.9-rc1/sound/synth/util_mem.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/sound/synth/util_mem.c 2004-09-02 20:51:53.000000000 -0700 @@ -38,7 +38,7 @@ snd_util_memhdr_new(int memsize) { snd_util_memhdr_t *hdr; - hdr = snd_kcalloc(sizeof(*hdr), GFP_KERNEL); + hdr = kcalloc(1, sizeof(*hdr), GFP_KERNEL); if (hdr == NULL) return NULL; hdr->size = memsize; --- linux-2.6.9-rc1/sound/usb/Kconfig 2004-08-24 00:03:19.000000000 -0700 +++ 25/sound/usb/Kconfig 2004-09-02 20:51:53.000000000 -0700 @@ -11,5 +11,15 @@ config SND_USB_AUDIO help Say 'Y' or 'M' to include support for USB audio and USB MIDI devices. +config SND_USB_USX2Y + tristate "Tascam US-122, US-224 and US-428 USB driver" + depends on SND && USB + select SND_HWDEP + select SND_RAWMIDI + select SND_PCM + help + Say 'Y' or 'M' to include support for Tascam USB Audio/MIDI + interfaces or controllers US-122, US-224 and US-428. + endmenu --- linux-2.6.9-rc1/sound/usb/Makefile 2004-08-24 00:02:58.000000000 -0700 +++ 25/sound/usb/Makefile 2004-09-02 20:51:53.000000000 -0700 @@ -2,7 +2,11 @@ # Makefile for ALSA # -snd-usb-audio-objs := usbaudio.o usbmixer.o usbmidi.o +snd-usb-audio-objs := usbaudio.o usbmixer.o +snd-usb-lib-objs := usbmidi.o # Toplevel Module Dependency -obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o +obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o snd-usb-lib.o +obj-$(CONFIG_SND_USB_USX2Y) += snd-usb-lib.o + +obj-$(CONFIG_SND) += usx2y/ --- linux-2.6.9-rc1/sound/usb/usbaudio.c 2004-08-24 00:03:19.000000000 -0700 +++ 25/sound/usb/usbaudio.c 2004-09-02 20:51:53.000000000 -0700 @@ -5,7 +5,7 @@ * * Copyright (c) 2002 by Takashi Iwai * - * Many codes borrowed from audio.c by + * Many codes borrowed from audio.c by * Alan Cox (alan@lxorguk.ukuu.org.uk) * Thomas Sailer (sailer@ife.ee.ethz.ch) * @@ -58,8 +58,7 @@ MODULE_AUTHOR("Takashi Iwai "); MODULE_DESCRIPTION("USB Audio"); MODULE_LICENSE("GPL"); -MODULE_CLASSES("{sound}"); -MODULE_DEVICES("{{Generic,USB Audio}}"); +MODULE_SUPPORTED_DEVICE("{{Generic,USB Audio}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ @@ -73,25 +72,18 @@ static int boot_devs; module_param_array(index, int, boot_devs, 0444); MODULE_PARM_DESC(index, "Index value for the USB audio adapter."); -MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC); module_param_array(id, charp, boot_devs, 0444); MODULE_PARM_DESC(id, "ID string for the USB audio adapter."); -MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC); module_param_array(enable, bool, boot_devs, 0444); MODULE_PARM_DESC(enable, "Enable USB audio adapter."); -MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC); module_param_array(vid, int, boot_devs, 0444); MODULE_PARM_DESC(vid, "Vendor ID for the USB audio device."); -MODULE_PARM_SYNTAX(vid, SNDRV_ENABLED ",allows:{{-1,0xffff}},base:16"); module_param_array(pid, int, boot_devs, 0444); MODULE_PARM_DESC(pid, "Product ID for the USB audio device."); -MODULE_PARM_SYNTAX(pid, SNDRV_ENABLED ",allows:{{-1,0xffff}},base:16"); module_param(nrpacks, int, 0444); MODULE_PARM_DESC(nrpacks, "Max. number of packets per URB."); -MODULE_PARM_SYNTAX(nrpacks, SNDRV_ENABLED ",allows:{{1,10}}"); module_param(async_unlink, bool, 0444); MODULE_PARM_DESC(async_unlink, "Use async unlink mode."); -MODULE_PARM_SYNTAX(async_unlink, SNDRV_BOOLEAN_TRUE_DESC); /* @@ -104,7 +96,7 @@ MODULE_PARM_SYNTAX(async_unlink, SNDRV_B * */ -#define MAX_PACKS 10 +#define MAX_PACKS 10 #define MAX_PACKS_HS (MAX_PACKS * 8) /* in high speed mode */ #define MAX_URBS 5 /* max. 20ms long packets */ #define SYNC_URBS 2 /* always two urbs for sync */ @@ -207,8 +199,6 @@ struct snd_usb_stream { struct list_head list; }; -#define chip_t snd_usb_stream_t - /* * we keep the snd_usb_audio_t instances by ourselves for merging @@ -354,7 +344,7 @@ static int prepare_capture_urb(snd_usb_s if (! urb->bandwidth) { int bustime; bustime = usb_check_bandwidth(urb->dev, urb); - if (bustime < 0) + if (bustime < 0) return bustime; printk("urb %d: bandwidth = %d (packets = %d)\n", ctx->index, bustime, urb->number_of_packets); usb_claim_bandwidth(urb->dev, urb, bustime, 1); @@ -826,7 +816,7 @@ static int start_urbs(snd_usb_substream_ } -/* +/* * wait until all urbs are processed. */ static int wait_clear_urbs(snd_usb_substream_t *subs) @@ -1148,7 +1138,7 @@ static int init_usb_pitch(struct usb_dev if (fmt->attributes & EP_CS_ATTR_PITCH_CONTROL) { data[0] = 1; if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, - USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, PITCH_CONTROL << 8, ep, data, 1, HZ)) < 0) { snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n", dev->devnum, iface, ep); @@ -1174,7 +1164,7 @@ static int init_usb_sample_rate(struct u data[1] = rate >> 8; data[2] = rate >> 16; if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, - USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) { snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep 0x%x\n", dev->devnum, iface, fmt->altsetting, rate, ep); @@ -1183,9 +1173,9 @@ static int init_usb_sample_rate(struct u if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN, SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) { - snd_printk(KERN_ERR "%d:%d:%d: cannot get freq at ep 0x%x\n", + snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep 0x%x\n", dev->devnum, iface, fmt->altsetting, ep); - return err; + return 0; /* some devices don't support reading */ } crate = data[0] | (data[1] << 8) | (data[2] << 16); if (crate != rate) { @@ -1323,7 +1313,7 @@ static int snd_usb_hw_params(snd_pcm_sub ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); if (ret < 0) return ret; - + format = params_format(hw_params); rate = params_rate(hw_params); channels = params_channels(hw_params); @@ -1482,7 +1472,7 @@ static int hw_rule_rate(snd_pcm_hw_param snd_interval_t *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); unsigned int rmin, rmax; int changed; - + hwc_debug("hw_rule_rate: (%d,%d)\n", it->min, it->max); changed = 0; rmin = rmax = 0; @@ -1536,7 +1526,7 @@ static int hw_rule_channels(snd_pcm_hw_p snd_interval_t *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); unsigned int rmin, rmax; int changed; - + hwc_debug("hw_rule_channels: (%d,%d)\n", it->min, it->max); changed = 0; rmin = rmax = 0; @@ -1590,7 +1580,7 @@ static int hw_rule_format(snd_pcm_hw_par u64 fbits; u32 oldbits[2]; int changed; - + hwc_debug("hw_rule_format: %x:%x\n", fmt->bits[0], fmt->bits[1]); fbits = 0; list_for_each(p, &subs->fmt_list) { @@ -1734,13 +1724,13 @@ static int setup_hw_info(snd_pcm_runtime if (check_hw_params_convention(subs)) { hwc_debug("setting extra hw constraints...\n"); - if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, hw_rule_rate, subs, SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0) return err; - if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, subs, SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_RATE, @@ -2001,8 +1991,8 @@ static void proc_dump_substream_status(s static void proc_pcm_format_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) { - snd_usb_stream_t *stream = snd_magic_cast(snd_usb_stream_t, entry->private_data, return); - + snd_usb_stream_t *stream = entry->private_data; + snd_iprintf(buffer, "%s : %s\n", stream->chip->card->longname, stream->pcm->name); if (stream->substream[SNDRV_PCM_STREAM_PLAYBACK].num_formats) { @@ -2089,7 +2079,7 @@ static void snd_usb_audio_stream_free(sn free_substream(&stream->substream[0]); free_substream(&stream->substream[1]); list_del(&stream->list); - snd_magic_kfree(stream); + kfree(stream); } static void snd_usb_audio_pcm_free(snd_pcm_t *pcm) @@ -2146,7 +2136,7 @@ static int add_audio_endpoint(snd_usb_au } /* create a new pcm */ - as = snd_magic_kmalloc(snd_usb_stream_t, 0, GFP_KERNEL); + as = kmalloc(sizeof(*as), GFP_KERNEL); if (! as) return -ENOMEM; memset(as, 0, sizeof(*as)); @@ -2158,13 +2148,13 @@ static int add_audio_endpoint(snd_usb_au stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1, &pcm); if (err < 0) { - snd_magic_kfree(as); + kfree(as); return err; } as->pcm = pcm; pcm->private_data = as; pcm->private_free = snd_usb_audio_pcm_free; - pcm->info_flags = SNDRV_PCM_INFO_NONATOMIC_OPS; + pcm->info_flags = 0; if (chip->pcm_devs > 0) sprintf(pcm->name, "USB Audio #%d", chip->pcm_devs); else @@ -2182,6 +2172,24 @@ static int add_audio_endpoint(snd_usb_au /* + * check if the device uses big-endian samples + */ +static int is_big_endian_format(struct usb_device *dev, struct audioformat *fp) +{ + /* M-Audio */ + if (dev->descriptor.idVendor == 0x0763) { + /* Quattro: captured data only */ + if (dev->descriptor.idProduct == 0x2001 && + fp->endpoint & USB_DIR_IN) + return 1; + /* Audiophile USB */ + if (dev->descriptor.idProduct == 0x2003) + return 1; + } + return 0; +} + +/* * parse the audio format type I descriptor * and returns the corresponding pcm format * @@ -2217,17 +2225,13 @@ static int parse_audio_format_i_type(str pcm_format = SNDRV_PCM_FORMAT_S8; break; case 2: - /* M-Audio audiophile USB workaround */ - if (dev->descriptor.idVendor == 0x0763 && - dev->descriptor.idProduct == 0x2003) + if (is_big_endian_format(dev, fp)) pcm_format = SNDRV_PCM_FORMAT_S16_BE; /* grrr, big endian!! */ else pcm_format = SNDRV_PCM_FORMAT_S16_LE; break; case 3: - /* M-Audio audiophile USB workaround */ - if (dev->descriptor.idVendor == 0x0763 && - dev->descriptor.idProduct == 0x2003) + if (is_big_endian_format(dev, fp)) pcm_format = SNDRV_PCM_FORMAT_S24_3BE; /* grrr, big endian!! */ else pcm_format = SNDRV_PCM_FORMAT_S24_3LE; @@ -2281,7 +2285,7 @@ static int parse_audio_format_rates(stru { int nr_rates = fmt[offset]; if (fmt[0] < offset + 1 + 3 * (nr_rates ? nr_rates : 2)) { - snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n", + snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n", dev->devnum, fp->iface, fp->altsetting); return -1; } @@ -2419,7 +2423,7 @@ static int parse_audio_format(struct usb } #endif return 0; -} +} static int parse_audio_endpoints(snd_usb_audio_t *chip, int iface_no) { @@ -2465,22 +2469,22 @@ static int parse_audio_endpoints(snd_usb } if (fmt[0] < 7) { - snd_printk(KERN_ERR "%d:%u:%d : invalid AS_GENERAL desc\n", + snd_printk(KERN_ERR "%d:%u:%d : invalid AS_GENERAL desc\n", dev->devnum, iface_no, altno); continue; } format = (fmt[6] << 8) | fmt[5]; /* remember the format value */ - + /* get format type */ fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, FORMAT_TYPE); if (!fmt) { - snd_printk(KERN_ERR "%d:%u:%d : no FORMAT_TYPE desc\n", + snd_printk(KERN_ERR "%d:%u:%d : no FORMAT_TYPE desc\n", dev->devnum, iface_no, altno); continue; } if (fmt[0] < 8) { - snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n", + snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n", dev->devnum, iface_no, altno); continue; } @@ -2490,7 +2494,7 @@ static int parse_audio_endpoints(snd_usb if (!csep && altsd->bNumEndpoints >= 2) csep = snd_usb_find_desc(alts->endpoint[1].extra, alts->endpoint[1].extralen, NULL, USB_DT_CS_ENDPOINT); if (!csep || csep[0] < 7 || csep[2] != EP_GENERAL) { - snd_printk(KERN_ERR "%d:%u:%d : no or invalid class specific endpoint descriptor\n", + snd_printk(KERN_ERR "%d:%u:%d : no or invalid class specific endpoint descriptor\n", dev->devnum, iface_no, altno); continue; } @@ -2920,14 +2924,14 @@ static int snd_usb_create_quirk(snd_usb_ */ static void proc_audio_usbbus_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) { - snd_usb_audio_t *chip = snd_magic_cast(snd_usb_audio_t, entry->private_data, return); + snd_usb_audio_t *chip = entry->private_data; if (! chip->shutdown) snd_iprintf(buffer, "%03d/%03d\n", chip->dev->bus->busnum, chip->dev->devnum); } static void proc_audio_usbid_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) { - snd_usb_audio_t *chip = snd_magic_cast(snd_usb_audio_t, entry->private_data, return); + snd_usb_audio_t *chip = entry->private_data; if (! chip->shutdown) snd_iprintf(buffer, "%04x:%04x\n", chip->dev->descriptor.idVendor, chip->dev->descriptor.idProduct); } @@ -2950,13 +2954,13 @@ static void snd_usb_audio_create_proc(sn static int snd_usb_audio_free(snd_usb_audio_t *chip) { - snd_magic_kfree(chip); + kfree(chip); return 0; } static int snd_usb_audio_dev_free(snd_device_t *device) { - snd_usb_audio_t *chip = snd_magic_cast(snd_usb_audio_t, device->device_data, return -ENXIO); + snd_usb_audio_t *chip = device->device_data; return snd_usb_audio_free(chip); } @@ -2975,7 +2979,7 @@ static int snd_usb_audio_create(struct u static snd_device_ops_t ops = { .dev_free = snd_usb_audio_dev_free, }; - + *rchip = NULL; if (snd_usb_get_speed(dev) != USB_SPEED_FULL && @@ -2990,7 +2994,7 @@ static int snd_usb_audio_create(struct u return -ENOMEM; } - chip = snd_magic_kcalloc(snd_usb_audio_t, 0, GFP_KERNEL); + chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); if (! chip) { snd_card_free(card); return -ENOMEM; @@ -3171,7 +3175,7 @@ static void *snd_usb_audio_probe(struct /* * we need to take care of counter, since disconnection can be called also - * many times as well as usb_audio_probe(). + * many times as well as usb_audio_probe(). */ static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr) { @@ -3182,7 +3186,7 @@ static void snd_usb_audio_disconnect(str if (ptr == (void *)-1L) return; - chip = snd_magic_cast(snd_usb_audio_t, ptr, return); + chip = ptr; card = chip->card; down(®ister_mutex); chip->shutdown = 1; --- linux-2.6.9-rc1/sound/usb/usbaudio.h 2004-08-24 00:02:58.000000000 -0700 +++ 25/sound/usb/usbaudio.h 2004-09-02 20:51:53.000000000 -0700 @@ -124,7 +124,6 @@ typedef struct snd_usb_audio snd_usb_audio_t; struct snd_usb_audio { - int index; struct usb_device *dev; snd_card_t *card; @@ -138,15 +137,17 @@ struct snd_usb_audio { int next_midi_device; unsigned int ignore_ctl_error; /* for mixer */ -}; +}; /* * Information about devices with broken descriptors */ +/* special values for .ifnum */ #define QUIRK_NO_INTERFACE -2 #define QUIRK_ANY_INTERFACE -1 +/* quirk type */ #define QUIRK_MIDI_FIXED_ENDPOINT 0 #define QUIRK_MIDI_YAMAHA 1 #define QUIRK_MIDI_MIDIMAN 2 @@ -205,6 +206,8 @@ int snd_usb_ctl_msg(struct usb_device *d int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif); int snd_usb_create_midi_interface(snd_usb_audio_t *chip, struct usb_interface *iface, const snd_usb_audio_quirk_t *quirk); +void snd_usbmidi_input_stop(struct list_head* p); +void snd_usbmidi_input_start(struct list_head* p); void snd_usbmidi_disconnect(struct list_head *p, struct usb_driver *driver); /* --- linux-2.6.9-rc1/sound/usb/usbmidi.c 2004-08-24 00:03:31.000000000 -0700 +++ 25/sound/usb/usbmidi.c 2004-09-02 20:51:53.000000000 -0700 @@ -49,6 +49,11 @@ #include #include "usbaudio.h" +MODULE_AUTHOR("Clemens Ladisch "); +MODULE_DESCRIPTION("USB Audio/MIDI helper module"); +MODULE_LICENSE("Dual BSD/GPL"); + + struct usb_ms_header_descriptor { __u8 bLength; __u8 bDescriptorType; @@ -175,7 +180,7 @@ static void snd_usbmidi_input_packet(snd */ static void snd_usbmidi_in_urb_complete(struct urb* urb, struct pt_regs *regs) { - snd_usb_midi_in_endpoint_t* ep = snd_magic_cast(snd_usb_midi_in_endpoint_t, urb->context, return); + snd_usb_midi_in_endpoint_t* ep = urb->context; if (urb->status == 0) { uint8_t* buffer = (uint8_t*)ep->urb->transfer_buffer; @@ -229,7 +234,7 @@ static void snd_usbmidi_in_midiman_compl static void snd_usbmidi_out_urb_complete(struct urb* urb, struct pt_regs *regs) { - snd_usb_midi_out_endpoint_t* ep = snd_magic_cast(snd_usb_midi_out_endpoint_t, urb->context, return); + snd_usb_midi_out_endpoint_t* ep = urb->context; if (urb->status < 0) { if (snd_usbmidi_urb_error(urb->status) < 0) @@ -393,7 +398,7 @@ static void snd_usbmidi_do_output(snd_us int p; struct urb* urb = ep->urb; unsigned long flags; - + spin_lock_irqsave(&ep->buffer_lock, flags); if (urb->status == -EINPROGRESS || ep->umidi->chip->shutdown) { spin_unlock_irqrestore(&ep->buffer_lock, flags); @@ -417,14 +422,14 @@ static void snd_usbmidi_do_output(snd_us static void snd_usbmidi_out_tasklet(unsigned long data) { - snd_usb_midi_out_endpoint_t* ep = snd_magic_cast(snd_usb_midi_out_endpoint_t, (void*)data, return); - + snd_usb_midi_out_endpoint_t* ep = (snd_usb_midi_out_endpoint_t *) data; + snd_usbmidi_do_output(ep); } static int snd_usbmidi_output_open(snd_rawmidi_substream_t* substream) { - snd_usb_midi_t* umidi = snd_magic_cast(snd_usb_midi_t, substream->rmidi->private_data, return -ENXIO); + snd_usb_midi_t* umidi = substream->rmidi->private_data; usbmidi_out_port_t* port = NULL; int i, j; @@ -503,7 +508,7 @@ static void snd_usbmidi_in_endpoint_dele kfree(ep->urb->transfer_buffer); usb_free_urb(ep->urb); } - snd_magic_kfree(ep); + kfree(ep); } /* @@ -571,7 +576,7 @@ static int snd_usbmidi_in_endpoint_creat int length; rep->in = NULL; - ep = snd_magic_kcalloc(snd_usb_midi_in_endpoint_t, 0, GFP_KERNEL); + ep = kcalloc(1, sizeof(*ep), GFP_KERNEL); if (!ep) return -ENOMEM; ep->umidi = umidi; @@ -631,7 +636,7 @@ static void snd_usbmidi_out_endpoint_del kfree(ep->urb->transfer_buffer); usb_free_urb(ep->urb); } - snd_magic_kfree(ep); + kfree(ep); } /* @@ -647,7 +652,7 @@ static int snd_usbmidi_out_endpoint_crea void* buffer; rep->out = NULL; - ep = snd_magic_kcalloc(snd_usb_midi_out_endpoint_t, 0, GFP_KERNEL); + ep = kcalloc(1, sizeof(*ep), GFP_KERNEL); if (!ep) return -ENOMEM; ep->umidi = umidi; @@ -695,7 +700,7 @@ static void snd_usbmidi_free(snd_usb_mid if (ep->in) snd_usbmidi_in_endpoint_delete(ep->in); } - snd_magic_kfree(umidi); + kfree(umidi); } /* @@ -718,7 +723,7 @@ void snd_usbmidi_disconnect(struct list_ static void snd_usbmidi_rawmidi_free(snd_rawmidi_t* rmidi) { - snd_usb_midi_t* umidi = snd_magic_cast(snd_usb_midi_t, rmidi->private_data, return); + snd_usb_midi_t* umidi = rmidi->private_data; snd_usbmidi_free(umidi); } @@ -964,7 +969,7 @@ static int snd_usbmidi_get_ms_info(snd_u * If the endpoints aren't specified, use the first bulk endpoints in the * first alternate setting of the interface. */ -static int snd_usbmidi_detect_endpoint(snd_usb_midi_t* umidi, +static int snd_usbmidi_detect_endpoint(snd_usb_midi_t* umidi, snd_usb_midi_endpoint_info_t* endpoint) { struct usb_interface* intf; @@ -998,7 +1003,7 @@ static int snd_usbmidi_detect_endpoint(s /* * Detects the endpoints and ports of Yamaha devices. */ -static int snd_usbmidi_detect_yamaha(snd_usb_midi_t* umidi, +static int snd_usbmidi_detect_yamaha(snd_usb_midi_t* umidi, snd_usb_midi_endpoint_info_t* endpoint) { struct usb_interface* intf; @@ -1145,6 +1150,44 @@ static int snd_usbmidi_create_rawmidi(sn } /* + * Temporarily stop input. + */ +void snd_usbmidi_input_stop(struct list_head* p) +{ + snd_usb_midi_t* umidi; + int i; + + umidi = list_entry(p, snd_usb_midi_t, list); + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { + snd_usb_midi_endpoint_t* ep = &umidi->endpoints[i]; + if (ep->in) + usb_unlink_urb(ep->in->urb); + } +} + +static void snd_usbmidi_input_start_ep(snd_usb_midi_in_endpoint_t* ep) +{ + if (ep) { + struct urb* urb = ep->urb; + urb->dev = ep->umidi->chip->dev; + snd_usbmidi_submit_urb(urb, GFP_KERNEL); + } +} + +/* + * Resume input after a call to snd_usbmidi_input_stop(). + */ +void snd_usbmidi_input_start(struct list_head* p) +{ + snd_usb_midi_t* umidi; + int i; + + umidi = list_entry(p, snd_usb_midi_t, list); + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) + snd_usbmidi_input_start_ep(umidi->endpoints[i].in); +} + +/* * Creates and registers everything needed for a MIDI streaming interface. */ int snd_usb_create_midi_interface(snd_usb_audio_t* chip, @@ -1156,7 +1199,7 @@ int snd_usb_create_midi_interface(snd_us int out_ports, in_ports; int i, err; - umidi = snd_magic_kcalloc(snd_usb_midi_t, 0, GFP_KERNEL); + umidi = kcalloc(1, sizeof(*umidi), GFP_KERNEL); if (!umidi) return -ENOMEM; umidi->chip = chip; @@ -1189,7 +1232,7 @@ int snd_usb_create_midi_interface(snd_us } } if (err < 0) { - snd_magic_kfree(umidi); + kfree(umidi); return err; } @@ -1202,7 +1245,7 @@ int snd_usb_create_midi_interface(snd_us } err = snd_usbmidi_create_rawmidi(umidi, out_ports, in_ports); if (err < 0) { - snd_magic_kfree(umidi); + kfree(umidi); return err; } @@ -1219,8 +1262,11 @@ int snd_usb_create_midi_interface(snd_us list_add(&umidi->list, &umidi->chip->midi_list); for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) - if (umidi->endpoints[i].in) - snd_usbmidi_submit_urb(umidi->endpoints[i].in->urb, - GFP_KERNEL); + snd_usbmidi_input_start_ep(umidi->endpoints[i].in); return 0; } + +EXPORT_SYMBOL(snd_usb_create_midi_interface); +EXPORT_SYMBOL(snd_usbmidi_input_stop); +EXPORT_SYMBOL(snd_usbmidi_input_start); +EXPORT_SYMBOL(snd_usbmidi_disconnect); --- linux-2.6.9-rc1/sound/usb/usbmixer.c 2004-08-24 00:02:25.000000000 -0700 +++ 25/sound/usb/usbmixer.c 2004-09-02 20:51:53.000000000 -0700 @@ -5,7 +5,7 @@ * * Copyright (c) 2002 by Takashi Iwai * - * Many codes borrowed from audio.c by + * Many codes borrowed from audio.c by * Alan Cox (alan@lxorguk.ukuu.org.uk) * Thomas Sailer (sailer@ife.ee.ethz.ch) * @@ -77,7 +77,7 @@ struct usb_mixer_elem_info { unsigned int ctrlif; unsigned int id; unsigned int control; /* CS or ICN (high byte) */ - unsigned int cmask; /* channel mask bitmap: 0 = master */ + unsigned int cmask; /* channel mask bitmap: 0 = master */ int channels; int val_type; int min, max, res; @@ -299,7 +299,7 @@ static int get_ctl_value(usb_mixer_elem_ unsigned char buf[2]; int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; int timeout = 10; - + while (timeout-- > 0) { if (snd_usb_ctl_msg(cval->chip->dev, usb_rcvctrlpipe(cval->chip->dev, 0), request, @@ -334,7 +334,7 @@ static int set_ctl_value(usb_mixer_elem_ unsigned char buf[2]; int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; int timeout = 10; - + value_set = convert_bytes_value(cval, value_set); buf[0] = value_set & 0xff; buf[1] = (value_set >> 8) & 0xff; @@ -361,7 +361,7 @@ inline static int set_cur_mix_value(usb_ /* - * parser routines begin here... + * parser routines begin here... */ static int parse_audio_unit(mixer_build_t *state, int unitid); @@ -443,7 +443,7 @@ static struct iterm_name_combo { { 0x0712, "Multi-Track Recorder" }, { 0x0713, "Synthesizer" }, { 0 }, -}; +}; static int get_term_name(mixer_build_t *state, usb_audio_term_t *iterm, unsigned char *name, int maxlen, int term_only) @@ -573,7 +573,7 @@ static struct usb_feature_control_info a static void usb_mixer_elem_free(snd_kcontrol_t *kctl) { if (kctl->private_data) { - snd_magic_kfree((void *)kctl->private_data); + kfree((void *)kctl->private_data); kctl->private_data = NULL; } } @@ -615,7 +615,7 @@ static int get_min_max(usb_mixer_elem_in cval->res = 1; } else { int last_valid_res = cval->res; - + while (cval->res > 1) { if (set_ctl_value(cval, SET_RES, (cval->control << 8) | minchn, cval->res / 2) < 0) break; @@ -634,8 +634,8 @@ static int get_min_max(usb_mixer_elem_in /* get a feature/mixer unit info */ static int mixer_ctl_feature_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) -{ - usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL); +{ + usb_mixer_elem_info_t *cval = kcontrol->private_data; if (cval->val_type == USB_MIXER_BOOLEAN || cval->val_type == USB_MIXER_INV_BOOLEAN) @@ -659,7 +659,7 @@ static int mixer_ctl_feature_info(snd_kc /* get the current value from feature/mixer unit */ static int mixer_ctl_feature_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { - usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL); + usb_mixer_elem_info_t *cval = kcontrol->private_data; int c, cnt, val, err; if (cval->cmask) { @@ -700,7 +700,7 @@ static int mixer_ctl_feature_get(snd_kco /* put the current value to feature/mixer unit */ static int mixer_ctl_feature_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { - usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL); + usb_mixer_elem_info_t *cval = kcontrol->private_data; int c, cnt, val, oval, err; int changed = 0; @@ -774,7 +774,7 @@ static void build_feature_ctl(mixer_buil if (check_ignored_ctl(state, unitid, control)) return; - cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL); + cval = kcalloc(1, sizeof(*cval), GFP_KERNEL); if (! cval) { snd_printk(KERN_ERR "cannot malloc kcontrol\n"); return; @@ -801,7 +801,7 @@ static void build_feature_ctl(mixer_buil kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); if (! kctl) { snd_printk(KERN_ERR "cannot malloc kcontrol\n"); - snd_magic_kfree(cval); + kfree(cval); return; } kctl->private_free = usb_mixer_elem_free; @@ -914,7 +914,7 @@ static int parse_audio_feature_unit(mixe if (master_bits & (1 << i)) build_feature_ctl(state, ftr, 0, i, &iterm, unitid); } - + return 0; } @@ -943,7 +943,7 @@ static void build_mixer_unit_ctl(mixer_b if (check_ignored_ctl(state, unitid, 0)) return; - cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL); + cval = kcalloc(1, sizeof(*cval), GFP_KERNEL); if (! cval) return; @@ -968,7 +968,7 @@ static void build_mixer_unit_ctl(mixer_b kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval); if (! kctl) { snd_printk(KERN_ERR "cannot malloc kcontrol\n"); - snd_magic_kfree(cval); + kfree(cval); return; } kctl->private_free = usb_mixer_elem_free; @@ -1014,7 +1014,7 @@ static int parse_audio_mixer_unit(mixer_ /* get callback for processing/extension unit */ static int mixer_ctl_procunit_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { - usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL); + usb_mixer_elem_info_t *cval = kcontrol->private_data; int err, val; err = get_cur_ctl_value(cval, cval->control << 8, &val); @@ -1032,7 +1032,7 @@ static int mixer_ctl_procunit_get(snd_kc /* put callback for processing/extension unit */ static int mixer_ctl_procunit_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { - usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL); + usb_mixer_elem_info_t *cval = kcontrol->private_data; int val, oval, err; err = get_cur_ctl_value(cval, cval->control << 8, &oval); @@ -1170,7 +1170,7 @@ static int build_audio_procunit(mixer_bu continue; if (check_ignored_ctl(state, unitid, valinfo->control)) continue; - cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL); + cval = kcalloc(1, sizeof(*cval), GFP_KERNEL); if (! cval) { snd_printk(KERN_ERR "cannot malloc kcontrol\n"); return -ENOMEM; @@ -1195,7 +1195,7 @@ static int build_audio_procunit(mixer_bu kctl = snd_ctl_new1(&mixer_procunit_ctl, cval); if (! kctl) { snd_printk(KERN_ERR "cannot malloc kcontrol\n"); - snd_magic_kfree(cval); + kfree(cval); return -ENOMEM; } kctl->private_free = usb_mixer_elem_free; @@ -1243,8 +1243,8 @@ static int parse_audio_extension_unit(mi * use an enumerator type for routing */ static int mixer_ctl_selector_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) -{ - usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL); +{ + usb_mixer_elem_info_t *cval = kcontrol->private_data; char **itemlist = (char **)kcontrol->private_value; snd_assert(itemlist, return -EINVAL); @@ -1260,7 +1260,7 @@ static int mixer_ctl_selector_info(snd_k /* get callback for selector unit */ static int mixer_ctl_selector_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { - usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL); + usb_mixer_elem_info_t *cval = kcontrol->private_data; int val, err; err = get_cur_ctl_value(cval, 0, &val); @@ -1279,7 +1279,7 @@ static int mixer_ctl_selector_get(snd_kc /* put callback for selector unit */ static int mixer_ctl_selector_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) { - usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL); + usb_mixer_elem_info_t *cval = kcontrol->private_data; int val, oval, err; err = get_cur_ctl_value(cval, 0, &oval); @@ -1315,9 +1315,9 @@ static void usb_mixer_selector_elem_free int i, num_ins = 0; if (kctl->private_data) { - usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kctl->private_data,); + usb_mixer_elem_info_t *cval = kctl->private_data; num_ins = cval->max; - snd_magic_kfree(cval); + kfree(cval); kctl->private_data = NULL; } if (kctl->private_value) { @@ -1357,7 +1357,7 @@ static int parse_audio_selector_unit(mix if (check_ignored_ctl(state, unitid, 0)) return 0; - cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL); + cval = kcalloc(1, sizeof(*cval), GFP_KERNEL); if (! cval) { snd_printk(KERN_ERR "cannot malloc kcontrol\n"); return -ENOMEM; @@ -1375,7 +1375,7 @@ static int parse_audio_selector_unit(mix namelist = kmalloc(sizeof(char *) * num_ins, GFP_KERNEL); if (! namelist) { snd_printk(KERN_ERR "cannot malloc\n"); - snd_magic_kfree(cval); + kfree(cval); return -ENOMEM; } #define MAX_ITEM_NAME_LEN 64 @@ -1388,7 +1388,7 @@ static int parse_audio_selector_unit(mix while (--i > 0) kfree(namelist[i]); kfree(namelist); - snd_magic_kfree(cval); + kfree(cval); return -ENOMEM; } if (check_input_term(state, desc[5 + i], &iterm) >= 0) @@ -1400,7 +1400,7 @@ static int parse_audio_selector_unit(mix kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval); if (! kctl) { snd_printk(KERN_ERR "cannot malloc kcontrol\n"); - snd_magic_kfree(cval); + kfree(cval); return -ENOMEM; } kctl->private_value = (unsigned long)namelist; --- linux-2.6.9-rc1/sound/usb/usbmixer_maps.c 2004-08-24 00:03:38.000000000 -0700 +++ 25/sound/usb/usbmixer_maps.c 2004-09-02 20:51:53.000000000 -0700 @@ -32,7 +32,7 @@ struct usbmix_ctl_map { const struct usbmix_name_map *map; int ignore_ctl_error; }; - + /* * USB control mappers for SB Exitigy */ --- linux-2.6.9-rc1/sound/usb/usbquirks.h 2004-08-24 00:02:32.000000000 -0700 +++ 25/sound/usb/usbquirks.h 2004-09-02 20:51:53.000000000 -0700 @@ -91,6 +91,31 @@ YAMAHA_DEVICE(0x1020, "EZ-250i"), YAMAHA_DEVICE(0x1021, "MOTIF ES 6"), YAMAHA_DEVICE(0x1022, "MOTIF ES 7"), YAMAHA_DEVICE(0x1023, "MOTIF ES 8"), +YAMAHA_DEVICE(0x1024, "CVP-301"), +YAMAHA_DEVICE(0x1025, "CVP-303"), +YAMAHA_DEVICE(0x1026, "CVP-305"), +YAMAHA_DEVICE(0x1027, "CVP-307"), +YAMAHA_DEVICE(0x1028, "CVP-309"), +YAMAHA_DEVICE(0x1029, "CVP-309GP"), +YAMAHA_DEVICE(0x102a, "PSR-1500"), +YAMAHA_DEVICE(0x102b, "PSR-3000"), +YAMAHA_DEVICE(0x102e, "ELS-01/01C"), +YAMAHA_DEVICE(0x1030, "PSR-295/293"), +YAMAHA_DEVICE(0x1031, "DGX-205/203"), +YAMAHA_DEVICE(0x1032, "DGX-305"), +YAMAHA_DEVICE(0x1033, "DGX-505"), +YAMAHA_DEVICE(0x1034, NULL), +YAMAHA_DEVICE(0x1035, NULL), +YAMAHA_DEVICE(0x1036, NULL), +YAMAHA_DEVICE(0x1037, NULL), +YAMAHA_DEVICE(0x1038, NULL), +YAMAHA_DEVICE(0x1039, NULL), +YAMAHA_DEVICE(0x103a, NULL), +YAMAHA_DEVICE(0x103b, NULL), +YAMAHA_DEVICE(0x103c, NULL), +YAMAHA_DEVICE(0x2000, "DGP-7"), +YAMAHA_DEVICE(0x2001, "DGP-5"), +YAMAHA_DEVICE(0x2002, NULL), YAMAHA_DEVICE(0x5000, "CS1D"), YAMAHA_DEVICE(0x5001, "DSP1D"), YAMAHA_DEVICE(0x5002, "DME32"), @@ -100,6 +125,11 @@ YAMAHA_DEVICE(0x5005, "ACU16-C"), YAMAHA_DEVICE(0x5006, "NHB32-C"), YAMAHA_DEVICE(0x5007, "DM1000"), YAMAHA_DEVICE(0x5008, "01V96"), +YAMAHA_DEVICE(0x500a, "PM5D"), +YAMAHA_DEVICE(0x500b, "DME64N"), +YAMAHA_DEVICE(0x500c, "DME24N"), +YAMAHA_DEVICE(0x7000, "DTX"), +YAMAHA_DEVICE(0x7010, "UB99"), #undef YAMAHA_DEVICE #undef YAMAHA_INTERFACE @@ -267,7 +297,8 @@ YAMAHA_DEVICE(0x5008, "01V96"), } }, { - /* thanks to Emiliano Grilli for helping researching this data */ + /* thanks to Emiliano Grilli + * for helping researching this data */ USB_DEVICE(0x0582, 0x000c), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "Roland", @@ -830,11 +861,42 @@ YAMAHA_DEVICE(0x5008, "01V96"), .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "M-Audio", .product_name = "Quattro", - .ifnum = 9, - .type = QUIRK_MIDI_MIDIMAN, - .data = & (const snd_usb_midi_endpoint_info_t) { - .out_cables = 0x0001, - .in_cables = 0x0001 + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = & (const snd_usb_audio_quirk_t[]) { + /* + * Interfaces 0-2 are "Windows-compatible", 16-bit only, + * and share endpoints with the other interfaces. + * Ignore them. The other interfaces can do 24 bits, + * but captured samples are big-endian (see usbaudio.c). + */ + { + .ifnum = 4, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 5, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 7, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 8, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 9, + .type = QUIRK_MIDI_MIDIMAN, + .data = & (const snd_usb_midi_endpoint_info_t) { + .out_cables = 0x0001, + .in_cables = 0x0001 + } + }, + { + .ifnum = -1 + } } } }, @@ -915,4 +977,19 @@ YAMAHA_DEVICE(0x5008, "01V96"), } }, +{ + /* + * Some USB MIDI devices don't have an audio control interface, + * so we have to grab MIDI streaming interfaces here. + */ + .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_MIDI_STREAMING, + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_MIDI_STANDARD_INTERFACE + } +}, + #undef USB_DEVICE_VENDOR_SPEC --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/sound/usb/usx2y/Makefile 2004-09-02 20:51:53.000000000 -0700 @@ -0,0 +1,3 @@ +snd-usb-usx2y-objs := usbusx2y.o usbusx2yaudio.o usX2Yhwdep.o + +obj-$(CONFIG_SND_USB_USX2Y) += snd-usb-usx2y.o --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/sound/usb/usx2y/usbus428ctldefs.h 2004-09-02 20:51:53.000000000 -0700 @@ -0,0 +1,108 @@ +/* + * + * Copyright (c) 2003 by Karsten Wiese + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +enum E_In84{ + eFader0 = 0, + eFader1, + eFader2, + eFader3, + eFader4, + eFader5, + eFader6, + eFader7, + eFaderM, + eTransport, + eModifier = 10, + eFilterSelect, + eSelect, + eMute, + + eSwitch = 15, + eWheelGain, + eWheelFreq, + eWheelQ, + eWheelPan, + eWheel = 20 +}; + +#define T_RECORD 1 +#define T_PLAY 2 +#define T_STOP 4 +#define T_F_FWD 8 +#define T_REW 0x10 +#define T_SOLO 0x20 +#define T_REC 0x40 +#define T_NULL 0x80 + + +struct us428_ctls{ + unsigned char Fader[9]; + unsigned char Transport; + unsigned char Modifier; + unsigned char FilterSelect; + unsigned char Select; + unsigned char Mute; + unsigned char UNKNOWN; + unsigned char Switch; + unsigned char Wheel[5]; +}; + +typedef struct us428_ctls us428_ctls_t; + +typedef struct us428_setByte{ + unsigned char Offset, + Value; +}us428_setByte_t; + +enum { + eLT_Volume = 0, + eLT_Light +}; + +typedef struct usX2Y_volume { + unsigned char Channel, + LH, + LL, + RH, + RL; +} usX2Y_volume_t; + +struct us428_lights{ + us428_setByte_t Light[7]; +}; +typedef struct us428_lights us428_lights_t; + +typedef struct { + char type; + union { + usX2Y_volume_t vol; + us428_lights_t lights; + } val; +} us428_p4out_t; + +#define N_us428_ctl_BUFS 16 +#define N_us428_p4out_BUFS 16 +struct us428ctls_sharedmem{ + us428_ctls_t CtlSnapShot[N_us428_ctl_BUFS]; + int CtlSnapShotDiffersAt[N_us428_ctl_BUFS]; + int CtlSnapShotLast, CtlSnapShotRed; + us428_p4out_t p4out[N_us428_p4out_BUFS]; + int p4outLast, p4outSent; +}; +typedef struct us428ctls_sharedmem us428ctls_sharedmem_t; --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/sound/usb/usx2y/usbusx2yaudio.c 2004-09-02 20:51:53.000000000 -0700 @@ -0,0 +1,1027 @@ +/* + * US-428 AUDIO + + * Copyright (c) 2002-2003 by Karsten Wiese + + * based on + + * (Tentative) USB Audio Driver for ALSA + * + * Main and PCM part + * + * Copyright (c) 2002 by Takashi Iwai + * + * Many codes borrowed from audio.c by + * Alan Cox (alan@lxorguk.ukuu.org.uk) + * Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include "usx2y.h" +#include "usbusx2y.h" + + +struct snd_usX2Y_substream { + usX2Ydev_t *usX2Y; + snd_pcm_substream_t *pcm_substream; + + unsigned char endpoint; + unsigned int datapipe; /* the data i/o pipe */ + unsigned int maxpacksize; /* max packet size in bytes */ + + char prepared, + running, + stalled; + + int hwptr; /* free frame position in the buffer (only for playback) */ + int hwptr_done; /* processed frame position in the buffer */ + int transfer_done; /* processed frames since last period update */ + + struct urb *urb[NRURBS]; /* data urb table */ + int next_urb_complete; + struct urb *completed_urb; + char *tmpbuf; /* temporary buffer for playback */ + volatile int submitted_urbs; + wait_queue_head_t wait_queue; +}; + + + + + + +static int usX2Y_urb_capt_retire(snd_usX2Y_substream_t *subs) +{ + struct urb *urb = subs->completed_urb; + snd_pcm_runtime_t *runtime = subs->pcm_substream->runtime; + unsigned char *cp; + int i, len, lens = 0, hwptr_done = subs->hwptr_done; + usX2Ydev_t *usX2Y = subs->usX2Y; + + for (i = 0; i < NRPACKS; i++) { + cp = (unsigned char*)urb->transfer_buffer + urb->iso_frame_desc[i].offset; + if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */ + snd_printdd("activ frame status %i\n", urb->iso_frame_desc[i].status); + return urb->iso_frame_desc[i].status; + } + len = urb->iso_frame_desc[i].actual_length / usX2Y->stride; + if (! len) { + snd_printk("0 == len ERROR!\n"); + continue; + } + + /* copy a data chunk */ + if ((hwptr_done + len) > runtime->buffer_size) { + int cnt = runtime->buffer_size - hwptr_done; + int blen = cnt * usX2Y->stride; + memcpy(runtime->dma_area + hwptr_done * usX2Y->stride, cp, blen); + memcpy(runtime->dma_area, cp + blen, len * usX2Y->stride - blen); + } else { + memcpy(runtime->dma_area + hwptr_done * usX2Y->stride, cp, len * usX2Y->stride); + } + lens += len; + if ((hwptr_done += len) >= runtime->buffer_size) + hwptr_done -= runtime->buffer_size; + } + + subs->hwptr_done = hwptr_done; + subs->transfer_done += lens; + /* update the pointer, call callback if necessary */ + if (subs->transfer_done >= runtime->period_size) { + subs->transfer_done -= runtime->period_size; + snd_pcm_period_elapsed(subs->pcm_substream); + } + return 0; +} +/* + * prepare urb for playback data pipe + * + * we copy the data directly from the pcm buffer. + * the current position to be copied is held in hwptr field. + * since a urb can handle only a single linear buffer, if the total + * transferred area overflows the buffer boundary, we cannot send + * it directly from the buffer. thus the data is once copied to + * a temporary buffer and urb points to that. + */ +static int usX2Y_urb_play_prepare(snd_usX2Y_substream_t *subs, + struct urb *cap_urb, + struct urb *urb) +{ + int count, counts, pack; + usX2Ydev_t* usX2Y = subs->usX2Y; + snd_pcm_runtime_t *runtime = subs->pcm_substream->runtime; + + count = 0; + for (pack = 0; pack < NRPACKS; pack++) { + /* calculate the size of a packet */ + counts = cap_urb->iso_frame_desc[pack].actual_length / usX2Y->stride; + count += counts; + if (counts < 43 || counts > 50) { + snd_printk("should not be here with counts=%i\n", counts); + return -EPIPE; + } + + /* set up descriptor */ + urb->iso_frame_desc[pack].offset = pack ? urb->iso_frame_desc[pack - 1].offset + urb->iso_frame_desc[pack - 1].length : 0; + urb->iso_frame_desc[pack].length = counts * usX2Y->stride; + } + if (subs->hwptr + count > runtime->buffer_size) { + /* err, the transferred area goes over buffer boundary. + * copy the data to the temp buffer. + */ + int len; + len = runtime->buffer_size - subs->hwptr; + urb->transfer_buffer = subs->tmpbuf; + memcpy(subs->tmpbuf, runtime->dma_area + subs->hwptr * usX2Y->stride, len * usX2Y->stride); + memcpy(subs->tmpbuf + len * usX2Y->stride, runtime->dma_area, (count - len) * usX2Y->stride); + subs->hwptr += count; + subs->hwptr -= runtime->buffer_size; + } else { + /* set the buffer pointer */ + urb->transfer_buffer = runtime->dma_area + subs->hwptr * usX2Y->stride; + if ((subs->hwptr += count) >= runtime->buffer_size) + subs->hwptr -= runtime->buffer_size; + } + urb->transfer_buffer_length = count * usX2Y->stride; + return 0; +} + +/* + * process after playback data complete + * + * update the current position and call callback if a period is processed. + */ +inline static int usX2Y_urb_play_retire(snd_usX2Y_substream_t *subs, struct urb *urb) +{ + snd_pcm_runtime_t *runtime = subs->pcm_substream->runtime; + int len = (urb->iso_frame_desc[0].actual_length +#if NRPACKS > 1 + + urb->iso_frame_desc[1].actual_length +#endif + ) / subs->usX2Y->stride; + + subs->transfer_done += len; + subs->hwptr_done += len; + if (subs->hwptr_done >= runtime->buffer_size) + subs->hwptr_done -= runtime->buffer_size; + if (subs->transfer_done >= runtime->period_size) { + subs->transfer_done -= runtime->period_size; + snd_pcm_period_elapsed(subs->pcm_substream); + } + return 0; +} + +inline static int usX2Y_urb_submit(snd_usX2Y_substream_t *subs, struct urb *urb, int frame) +{ + int err; + if (!urb) + return -ENODEV; + urb->start_frame = (frame + NRURBS*NRPACKS) & (1024 - 1); + urb->hcpriv = NULL; + urb->dev = subs->usX2Y->chip.dev; /* we need to set this at each time */ + if ((err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { + snd_printk("%i\n", err); + return err; + } else { + subs->submitted_urbs++; + if (subs->next_urb_complete < 0) + subs->next_urb_complete = 0; + } + return 0; +} + + +static inline int frame_distance(int from, int to) +{ + int distance = to - from; + if (distance < -512) + distance += 1024; + else + if (distance > 511) + distance -= 1024; + return distance; +} + + +static void usX2Y_subs_set_next_urb_complete(snd_usX2Y_substream_t *subs) +{ + int next_urb_complete = subs->next_urb_complete + 1; + int distance; + if (next_urb_complete >= NRURBS) + next_urb_complete = 0; + distance = frame_distance(subs->completed_urb->start_frame, + subs->urb[next_urb_complete]->start_frame); + if (1 == distance) { + subs->next_urb_complete = next_urb_complete; + } else { + snd_printdd("distance %i not set_nuc %i %i %i \n", distance, subs->endpoint, next_urb_complete, subs->urb[next_urb_complete]->status); + subs->next_urb_complete = -1; + } +} + + +static inline void usX2Y_usbframe_complete(snd_usX2Y_substream_t *capsubs, snd_usX2Y_substream_t *playbacksubs, int frame) +{ + { + struct urb *urb; + if ((urb = playbacksubs->completed_urb)) { + if (playbacksubs->prepared) + usX2Y_urb_play_retire(playbacksubs, urb); + usX2Y_subs_set_next_urb_complete(playbacksubs); + } + if (playbacksubs->running) { + if (NULL == urb) + urb = playbacksubs->urb[playbacksubs->next_urb_complete + 1]; + if (urb && 0 == usX2Y_urb_play_prepare(playbacksubs, + capsubs->completed_urb, + urb)) { + if (usX2Y_urb_submit(playbacksubs, urb, frame) < 0) + return; + } else + snd_pcm_stop(playbacksubs->pcm_substream, SNDRV_PCM_STATE_XRUN); + } + playbacksubs->completed_urb = NULL; + } + if (capsubs->running) + usX2Y_urb_capt_retire(capsubs); + usX2Y_subs_set_next_urb_complete(capsubs); + if (capsubs->prepared) + usX2Y_urb_submit(capsubs, capsubs->completed_urb, frame); + capsubs->completed_urb = NULL; +} + + +static void usX2Y_clients_stop(snd_usX2Y_substream_t *subs) +{ + usX2Ydev_t *usX2Y = subs->usX2Y; + int i; + for (i = 0; i < 4; i++) { + snd_usX2Y_substream_t *substream = usX2Y->substream[i]; + if (substream && substream->running) + snd_pcm_stop(substream->pcm_substream, SNDRV_PCM_STATE_XRUN); + } +} + + +static void i_usX2Y_urb_complete(struct urb *urb, struct pt_regs *regs) +{ + snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t*)urb->context; + + subs->submitted_urbs--; + if (urb->status) { + snd_printk("ep=%i stalled with status=%i\n", subs->endpoint, urb->status); + subs->stalled = 1; + usX2Y_clients_stop(subs); + urb->status = 0; + return; + } + if (urb == subs->urb[subs->next_urb_complete]) { + subs->completed_urb = urb; + } else { + snd_printk("Sequence Error!(ep=%i;nuc=%i,frame=%i)\n", + subs->endpoint, subs->next_urb_complete, urb->start_frame); + subs->stalled = 1; + usX2Y_clients_stop(subs); + return; + } + if (waitqueue_active(&subs->wait_queue)) + wake_up(&subs->wait_queue); + { + snd_usX2Y_substream_t *capsubs = subs->usX2Y->substream[SNDRV_PCM_STREAM_CAPTURE], + *playbacksubs = subs->usX2Y->substream[SNDRV_PCM_STREAM_PLAYBACK]; + if (capsubs->completed_urb && + (playbacksubs->completed_urb || + !playbacksubs->prepared || + (playbacksubs->prepared && (playbacksubs->next_urb_complete < 0 || // not started yet + frame_distance(capsubs->completed_urb->start_frame, + playbacksubs->urb[playbacksubs->next_urb_complete]->start_frame) + > 0 || // other expected later + playbacksubs->stalled)))) + usX2Y_usbframe_complete(capsubs, playbacksubs, urb->start_frame); + } +} + + +static int usX2Y_urbs_capt_start(snd_usX2Y_substream_t *subs) +{ + int i, err; + + for (i = 0; i < NRURBS; i++) { + unsigned long pack; + struct urb *urb = subs->urb[i]; + urb->dev = subs->usX2Y->chip.dev; + urb->transfer_flags = URB_ISO_ASAP; + for (pack = 0; pack < NRPACKS; pack++) { + urb->iso_frame_desc[pack].offset = subs->maxpacksize * pack; + urb->iso_frame_desc[pack].length = subs->maxpacksize; + } + urb->transfer_buffer_length = subs->maxpacksize * NRPACKS; + if ((err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { + snd_printk (KERN_ERR "cannot submit datapipe for urb %d, err = %d\n", i, err); + return -EPIPE; + } else { + subs->submitted_urbs++; + } + urb->transfer_flags = 0; + } + subs->stalled = 0; + subs->next_urb_complete = 0; + subs->prepared = 1; + return 0; +} + +/* + * wait until all urbs are processed. + */ +static int usX2Y_urbs_wait_clear(snd_usX2Y_substream_t *subs) +{ + int timeout = HZ; + + do { + if (0 == subs->submitted_urbs) + break; + set_current_state(TASK_UNINTERRUPTIBLE); + snd_printdd("snd_usX2Y_urbs_wait_clear waiting\n"); + schedule_timeout(1); + } while (--timeout > 0); + if (subs->submitted_urbs) + snd_printk(KERN_ERR "timeout: still %d active urbs..\n", subs->submitted_urbs); + return 0; +} +/* + * return the current pcm pointer. just return the hwptr_done value. + */ +static snd_pcm_uframes_t snd_usX2Y_pcm_pointer(snd_pcm_substream_t *substream) +{ + snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)substream->runtime->private_data; + return subs->hwptr_done; +} +/* + * start/stop substream + */ +static int snd_usX2Y_pcm_trigger(snd_pcm_substream_t *substream, int cmd) +{ + snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)substream->runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + snd_printdd("snd_usX2Y_pcm_trigger(START)\n"); + if (subs->usX2Y->substream[SNDRV_PCM_STREAM_CAPTURE]->stalled) + return -EPIPE; + else + subs->running = 1; + break; + case SNDRV_PCM_TRIGGER_STOP: + snd_printdd("snd_usX2Y_pcm_trigger(STOP)\n"); + subs->running = 0; + break; + default: + return -EINVAL; + } + return 0; +} + + + +static void usX2Y_urb_release(struct urb** urb, int free_tb) +{ + if (*urb) { + if (free_tb) + kfree((*urb)->transfer_buffer); + usb_free_urb(*urb); + *urb = NULL; + } +} +/* + * release a substream + */ +static void usX2Y_urbs_release(snd_usX2Y_substream_t *subs) +{ + int i; + snd_printdd("snd_usX2Y_urbs_release() %i\n", subs->endpoint); + usX2Y_urbs_wait_clear(subs); + for (i = 0; i < NRURBS; i++) + usX2Y_urb_release(subs->urb + i, subs != subs->usX2Y->substream[SNDRV_PCM_STREAM_PLAYBACK]); + + if (subs->tmpbuf) { + kfree(subs->tmpbuf); + subs->tmpbuf = NULL; + } +} + +static void usX2Y_substream_prepare(snd_usX2Y_substream_t *subs) +{ + snd_printdd("usX2Y_substream_prepare() ep=%i urb0=%p urb1=%p\n", subs->endpoint, subs->urb[0], subs->urb[1]); + /* reset the pointer */ + subs->hwptr = 0; + subs->hwptr_done = 0; + subs->transfer_done = 0; +} + + +/* + * initialize a substream's urbs + */ +static int usX2Y_urbs_allocate(snd_usX2Y_substream_t *subs) +{ + int i; + int is_playback = subs == subs->usX2Y->substream[SNDRV_PCM_STREAM_PLAYBACK]; + struct usb_device *dev = subs->usX2Y->chip.dev; + + snd_assert(!subs->prepared, return 0); + + if (is_playback) { /* allocate a temporary buffer for playback */ + subs->datapipe = usb_sndisocpipe(dev, subs->endpoint); + subs->maxpacksize = dev->epmaxpacketout[subs->endpoint]; + if (NULL == subs->tmpbuf) { + subs->tmpbuf = kcalloc(NRPACKS, subs->maxpacksize, GFP_KERNEL); + if (NULL == subs->tmpbuf) { + snd_printk(KERN_ERR "cannot malloc tmpbuf\n"); + return -ENOMEM; + } + } + } else { + subs->datapipe = usb_rcvisocpipe(dev, subs->endpoint); + subs->maxpacksize = dev->epmaxpacketin[subs->endpoint]; + } + + /* allocate and initialize data urbs */ + for (i = 0; i < NRURBS; i++) { + struct urb** purb = subs->urb + i; + if (*purb) + continue; + *purb = usb_alloc_urb(NRPACKS, GFP_KERNEL); + if (NULL == *purb) { + usX2Y_urbs_release(subs); + return -ENOMEM; + } + if (!is_playback && !(*purb)->transfer_buffer) { + /* allocate a capture buffer per urb */ + (*purb)->transfer_buffer = kmalloc(subs->maxpacksize*NRPACKS, GFP_KERNEL); + if (NULL == (*purb)->transfer_buffer) { + usX2Y_urbs_release(subs); + return -ENOMEM; + } + } + (*purb)->dev = dev; + (*purb)->pipe = subs->datapipe; + (*purb)->number_of_packets = NRPACKS; + (*purb)->context = subs; + (*purb)->interval = 1; + (*purb)->complete = snd_usb_complete_callback(i_usX2Y_urb_complete); + } + return 0; +} + +static void i_usX2Y_04Int(struct urb* urb, struct pt_regs *regs) +{ + usX2Ydev_t* usX2Y = urb->context; + + if (urb->status) { + snd_printk("snd_usX2Y_04Int() urb->status=%i\n", urb->status); + return; + } + if (0 == --usX2Y->US04->len) + wake_up(&usX2Y->In04WaitQueue); +} +/* + * allocate a buffer, setup samplerate + * + * so far we use a physically linear buffer although packetize transfer + * doesn't need a continuous area. + * if sg buffer is supported on the later version of alsa, we'll follow + * that. + */ +static struct s_c2 +{ + char c1, c2; +} + SetRate44100[] = +{ + { 0x14, 0x08}, // this line sets 44100, well actually a little less + { 0x18, 0x40}, // only tascam / frontier design knows the further lines ....... + { 0x18, 0x42}, + { 0x18, 0x45}, + { 0x18, 0x46}, + { 0x18, 0x48}, + { 0x18, 0x4A}, + { 0x18, 0x4C}, + { 0x18, 0x4E}, + { 0x18, 0x50}, + { 0x18, 0x52}, + { 0x18, 0x54}, + { 0x18, 0x56}, + { 0x18, 0x58}, + { 0x18, 0x5A}, + { 0x18, 0x5C}, + { 0x18, 0x5E}, + { 0x18, 0x60}, + { 0x18, 0x62}, + { 0x18, 0x64}, + { 0x18, 0x66}, + { 0x18, 0x68}, + { 0x18, 0x6A}, + { 0x18, 0x6C}, + { 0x18, 0x6E}, + { 0x18, 0x70}, + { 0x18, 0x72}, + { 0x18, 0x74}, + { 0x18, 0x76}, + { 0x18, 0x78}, + { 0x18, 0x7A}, + { 0x18, 0x7C}, + { 0x18, 0x7E} +}; +static struct s_c2 SetRate48000[] = +{ + { 0x14, 0x09}, // this line sets 48000, well actually a little less + { 0x18, 0x40}, // only tascam / frontier design knows the further lines ....... + { 0x18, 0x42}, + { 0x18, 0x45}, + { 0x18, 0x46}, + { 0x18, 0x48}, + { 0x18, 0x4A}, + { 0x18, 0x4C}, + { 0x18, 0x4E}, + { 0x18, 0x50}, + { 0x18, 0x52}, + { 0x18, 0x54}, + { 0x18, 0x56}, + { 0x18, 0x58}, + { 0x18, 0x5A}, + { 0x18, 0x5C}, + { 0x18, 0x5E}, + { 0x18, 0x60}, + { 0x18, 0x62}, + { 0x18, 0x64}, + { 0x18, 0x66}, + { 0x18, 0x68}, + { 0x18, 0x6A}, + { 0x18, 0x6C}, + { 0x18, 0x6E}, + { 0x18, 0x70}, + { 0x18, 0x73}, + { 0x18, 0x74}, + { 0x18, 0x76}, + { 0x18, 0x78}, + { 0x18, 0x7A}, + { 0x18, 0x7C}, + { 0x18, 0x7E} +}; +#define NOOF_SETRATE_URBS ARRAY_SIZE(SetRate48000) + +static int usX2Y_rate_set(usX2Ydev_t *usX2Y, int rate) +{ + int err = 0, i; + snd_usX2Y_urbSeq_t *us = NULL; + int *usbdata = NULL; + DECLARE_WAITQUEUE(wait, current); + struct s_c2 *ra = rate == 48000 ? SetRate48000 : SetRate44100; + + if (usX2Y->rate != rate) { + do { + us = kmalloc(sizeof(*us) + sizeof(struct urb*) * NOOF_SETRATE_URBS, GFP_KERNEL); + if (NULL == us) { + err = -ENOMEM; + break; + } + memset(us, 0, sizeof(*us) + sizeof(struct urb*) * NOOF_SETRATE_URBS); + usbdata = kmalloc(sizeof(int)*NOOF_SETRATE_URBS, GFP_KERNEL); + if (NULL == usbdata) { + err = -ENOMEM; + break; + } + for (i = 0; i < NOOF_SETRATE_URBS; ++i) { + if (NULL == (us->urb[i] = usb_alloc_urb(0, GFP_KERNEL))) { + err = -ENOMEM; + break; + } + ((char*)(usbdata + i))[0] = ra[i].c1; + ((char*)(usbdata + i))[1] = ra[i].c2; + usb_fill_bulk_urb(us->urb[i], usX2Y->chip.dev, usb_sndbulkpipe(usX2Y->chip.dev, 4), + usbdata + i, 2, i_usX2Y_04Int, usX2Y); +#ifdef OLD_USB + us->urb[i]->transfer_flags = USB_QUEUE_BULK; +#endif + } + if (err) + break; + + add_wait_queue(&usX2Y->In04WaitQueue, &wait); + set_current_state(TASK_INTERRUPTIBLE); + us->submitted = 0; + us->len = NOOF_SETRATE_URBS; + usX2Y->US04 = us; + + do { + signed long timeout = schedule_timeout(HZ/2); + + if (signal_pending(current)) { + err = -ERESTARTSYS; + break; + } + if (0 == timeout) { + err = -ENODEV; + break; + } + usX2Y->rate = rate; + usX2Y->refframes = rate == 48000 ? 47 : 44; + } while (0); + + remove_wait_queue(&usX2Y->In04WaitQueue, &wait); + } while (0); + + if (us) { + us->submitted = 2*NOOF_SETRATE_URBS; + for (i = 0; i < NOOF_SETRATE_URBS; ++i) { + usb_unlink_urb(us->urb[i]); + usb_free_urb(us->urb[i]); + } + usX2Y->US04 = NULL; + kfree(usbdata); + kfree(us); + } + } + + return err; +} + + +static int usX2Y_format_set(usX2Ydev_t *usX2Y, snd_pcm_format_t format) +{ + int alternate, unlink_err, err; + struct list_head* p; + if (format == SNDRV_PCM_FORMAT_S24_3LE) { + alternate = 2; + usX2Y->stride = 6; + } else { + alternate = 1; + usX2Y->stride = 4; + } + list_for_each(p, &usX2Y->chip.midi_list) { + snd_usbmidi_input_stop(p); + } + unlink_err = usb_unlink_urb(usX2Y->In04urb); + if ((err = usb_set_interface(usX2Y->chip.dev, 0, alternate))) { + snd_printk("usb_set_interface error \n"); + return err; + } + if (0 == unlink_err) { + usX2Y->In04urb->dev = usX2Y->chip.dev; + err = usb_submit_urb(usX2Y->In04urb, GFP_KERNEL); + } + list_for_each(p, &usX2Y->chip.midi_list) { + snd_usbmidi_input_start(p); + } + usX2Y->format = format; + usX2Y->rate = 0; + return err; +} + + +static int snd_usX2Y_pcm_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *hw_params) +{ + int err = 0; + unsigned int rate = params_rate(hw_params); + snd_pcm_format_t format = params_format(hw_params); + snd_printdd("snd_usX2Y_hw_params(%p, %p)\n", substream, hw_params); + + { // all pcm substreams off one usX2Y have to operate at the same rate & format + snd_card_t *card = substream->pstr->pcm->card; + struct list_head *list; + list_for_each(list, &card->devices) { + snd_device_t *dev; + snd_pcm_t *pcm; + int s; + dev = snd_device(list); + if (dev->type != SNDRV_DEV_PCM) + continue; + pcm = dev->device_data; + for (s = 0; s < 2; ++s) { + snd_pcm_substream_t *test_substream; + test_substream = pcm->streams[s].substream; + if (test_substream && test_substream != substream && + test_substream->runtime && + ((test_substream->runtime->format && + test_substream->runtime->format != format) || + (test_substream->runtime->rate && + test_substream->runtime->rate != rate))) + return -EINVAL; + } + } + } + if (0 > (err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)))) { + snd_printk("snd_pcm_lib_malloc_pages(%p, %i) returned %i\n", substream, params_buffer_bytes(hw_params), err); + return err; + } + return 0; +} + +/* + * free the buffer + */ +static int snd_usX2Y_pcm_hw_free(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data; + snd_printdd("snd_usX2Y_hw_free(%p)\n", substream); + + if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) { + snd_usX2Y_substream_t *cap_subs = subs->usX2Y->substream[SNDRV_PCM_STREAM_CAPTURE]; + subs->prepared = 0; + usX2Y_urbs_release(subs); + if (!cap_subs->pcm_substream || + !cap_subs->pcm_substream->runtime || + !cap_subs->pcm_substream->runtime->status || + cap_subs->pcm_substream->runtime->status->state < SNDRV_PCM_STATE_PREPARED) { + cap_subs->prepared = 0; + usX2Y_urbs_release(cap_subs); + } + } else { + snd_usX2Y_substream_t *playback_subs = subs->usX2Y->substream[SNDRV_PCM_STREAM_PLAYBACK]; + if (!playback_subs->prepared) { + subs->prepared = 0; + usX2Y_urbs_release(subs); + } + } + + return snd_pcm_lib_free_pages(substream); +} +/* + * prepare callback + * + * set format and initialize urbs + */ +static int snd_usX2Y_pcm_prepare(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data; + snd_usX2Y_substream_t *capsubs = subs->usX2Y->substream[SNDRV_PCM_STREAM_CAPTURE]; + int err = 0; + snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream); + +// Start hardware streams +// SyncStream first.... + if (! capsubs->prepared) { + if (subs->usX2Y->format != runtime->format) + if ((err = usX2Y_format_set(subs->usX2Y, runtime->format)) < 0) + return err; + if (subs->usX2Y->rate != runtime->rate) + if ((err = usX2Y_rate_set(subs->usX2Y, runtime->rate)) < 0) + return err; + snd_printdd("starting capture pipe for playpipe\n"); + usX2Y_urbs_allocate(capsubs); + capsubs->completed_urb = NULL; + { + DECLARE_WAITQUEUE(wait, current); + add_wait_queue(&capsubs->wait_queue, &wait); + if (0 <= (err = usX2Y_urbs_capt_start(capsubs))) { + signed long timeout; + set_current_state(TASK_INTERRUPTIBLE); + timeout = schedule_timeout(HZ/4); + if (signal_pending(current)) + err = -ERESTARTSYS; + else { + snd_printdd("%li\n", HZ/4 - timeout); + if (0 == timeout) + err = -EPIPE; + } + } + remove_wait_queue(&capsubs->wait_queue, &wait); + if (0 > err) + return err; + } + } + + if (subs != capsubs) { + int u; + if (!subs->prepared) { + if ((err = usX2Y_urbs_allocate(subs)) < 0) + return err; + subs->prepared = 1; + } + while (subs->submitted_urbs) + for (u = 0; u < NRURBS; u++) { + snd_printdd("%i\n", subs->urb[u]->status); + while(subs->urb[u]->status || NULL != subs->urb[u]->hcpriv) { + signed long timeout; + snd_printdd("ep=%i waiting for urb=%p status=%i hcpriv=%p\n", + subs->endpoint, subs->urb[u], + subs->urb[u]->status, subs->urb[u]->hcpriv); + set_current_state(TASK_INTERRUPTIBLE); + timeout = schedule_timeout(HZ/10); + if (signal_pending(current)) { + return -ERESTARTSYS; + } + } + } + subs->completed_urb = NULL; + subs->next_urb_complete = -1; + subs->stalled = 0; + } + + usX2Y_substream_prepare(subs); + return err; +} + +static snd_pcm_hardware_t snd_usX2Y_2c = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .rate_min = 44100, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = (2*128*1024), + .period_bytes_min = 64, + .period_bytes_max = (128*1024), + .periods_min = 2, + .periods_max = 1024, + .fifo_size = 0 +}; + + + +static int snd_usX2Y_pcm_open(snd_pcm_substream_t *substream) +{ + snd_usX2Y_substream_t *subs = ((snd_usX2Y_substream_t **) + snd_pcm_substream_chip(substream))[substream->stream]; + snd_pcm_runtime_t *runtime = substream->runtime; + + runtime->hw = snd_usX2Y_2c; + runtime->private_data = subs; + subs->pcm_substream = substream; + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000); + return 0; +} + + + +static int snd_usX2Y_pcm_close(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_usX2Y_substream_t *subs = (snd_usX2Y_substream_t *)runtime->private_data; + int err = 0; + + subs->pcm_substream = NULL; + + return err; +} + + +static snd_pcm_ops_t snd_usX2Y_pcm_ops = +{ + .open = snd_usX2Y_pcm_open, + .close = snd_usX2Y_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_usX2Y_pcm_hw_params, + .hw_free = snd_usX2Y_pcm_hw_free, + .prepare = snd_usX2Y_pcm_prepare, + .trigger = snd_usX2Y_pcm_trigger, + .pointer = snd_usX2Y_pcm_pointer, +}; + + +/* + * free a usb stream instance + */ +static void usX2Y_audio_stream_free(snd_usX2Y_substream_t **usX2Y_substream) +{ + if (NULL != usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]) { + kfree(usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]); + usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK] = NULL; + } + kfree(usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE]); + usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE] = NULL; +} + +static void snd_usX2Y_pcm_private_free(snd_pcm_t *pcm) +{ + snd_usX2Y_substream_t **usX2Y_stream = pcm->private_data; + if (usX2Y_stream) { + snd_pcm_lib_preallocate_free_for_all(pcm); + usX2Y_audio_stream_free(usX2Y_stream); + } +} + +static int usX2Y_audio_stream_new(snd_card_t *card, int playback_endpoint, int capture_endpoint) +{ + snd_pcm_t *pcm; + int err, i; + snd_usX2Y_substream_t **usX2Y_substream = + usX2Y(card)->substream + 2 * usX2Y(card)->chip.pcm_devs; + + for (i = playback_endpoint ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE; + i <= SNDRV_PCM_STREAM_CAPTURE; ++i) { + usX2Y_substream[i] = kcalloc(1, sizeof(snd_usX2Y_substream_t), GFP_KERNEL); + if (NULL == usX2Y_substream[i]) { + snd_printk(KERN_ERR "cannot malloc\n"); + return -ENOMEM; + } + init_waitqueue_head(&usX2Y_substream[i]->wait_queue); + usX2Y_substream[i]->usX2Y = usX2Y(card); + } + + if (playback_endpoint) + usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]->endpoint = playback_endpoint; + usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE]->endpoint = capture_endpoint; + + err = snd_pcm_new(card, NAME_ALLCAPS" Audio", usX2Y(card)->chip.pcm_devs, + playback_endpoint ? 1 : 0, 1, + &pcm); + if (err < 0) { + usX2Y_audio_stream_free(usX2Y_substream); + return err; + } + + if (playback_endpoint) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usX2Y_pcm_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usX2Y_pcm_ops); + + pcm->private_data = usX2Y_substream; + pcm->private_free = snd_usX2Y_pcm_private_free; + pcm->info_flags = 0; + + sprintf(pcm->name, NAME_ALLCAPS" Audio #%d", usX2Y(card)->chip.pcm_devs); + + if ((playback_endpoint && + 0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + 64*1024, 128*1024))) || + 0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + 64*1024, 128*1024))) { + snd_usX2Y_pcm_private_free(pcm); + return err; + } + usX2Y(card)->chip.pcm_devs++; + + return 0; +} + +/* + * free the chip instance + * + * here we have to do not much, since pcm and controls are already freed + * + */ +static int snd_usX2Y_device_dev_free(snd_device_t *device) +{ + return 0; +} + + +/* + * create a chip instance and set its names. + */ +int usX2Y_audio_create(snd_card_t* card) +{ + int err = 0; + static snd_device_ops_t ops = { + .dev_free = snd_usX2Y_device_dev_free, + }; + + INIT_LIST_HEAD(&usX2Y(card)->chip.pcm_list); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, usX2Y(card), &ops)) < 0) { +// snd_usX2Y_audio_free(usX2Y(card)); + return err; + } + + if (0 > (err = usX2Y_audio_stream_new(card, 0xA, 0x8))) + return err; + if (usX2Y(card)->chip.dev->descriptor.idProduct == USB_ID_US428) + if (0 > (err = usX2Y_audio_stream_new(card, 0, 0xA))) + return err; + if (usX2Y(card)->chip.dev->descriptor.idProduct != USB_ID_US122) + err = usX2Y_rate_set(usX2Y(card), 44100); // Lets us428 recognize output-volume settings, disturbs us122. + return err; +} --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/sound/usb/usx2y/usbusx2y.c 2004-09-02 20:51:53.000000000 -0700 @@ -0,0 +1,434 @@ +/* + * usbus428.c - ALSA USB US-428 Driver + * +2004-07-13 Karsten Wiese + Version 0.7.1: + Don't sleep in START/STOP callbacks anymore. + us428 channels C/D not handled just for this version, sorry. + +2004-06-21 Karsten Wiese + Version 0.6.4: + Temporarely suspend midi input + to sanely call usb_set_interface() when setting format. + +2004-06-12 Karsten Wiese + Version 0.6.3: + Made it thus the following rule is enforced: + "All pcm substreams of one usX2Y have to operate at the same rate & format." + +2004-04-06 Karsten Wiese + Version 0.6.0: + Runs on 2.6.5 kernel without any "--with-debug=" things. + us224 reported running. + +2004-01-14 Karsten Wiese + Version 0.5.1: + Runs with 2.6.1 kernel. + +2003-12-30 Karsten Wiese + Version 0.4.1: + Fix 24Bit 4Channel capturing for the us428. + +2003-11-27 Karsten Wiese, Martin Langer + Version 0.4: + us122 support. + us224 could be tested by uncommenting the sections containing USB_ID_US224 + +2003-11-03 Karsten Wiese + Version 0.3: + 24Bit support. + "arecord -D hw:1 -c 2 -r 48000 -M -f S24_3LE|aplay -D hw:1 -c 2 -r 48000 -M -f S24_3LE" works. + +2003-08-22 Karsten Wiese + Version 0.0.8: + Removed EZUSB Firmware. First Stage Firmwaredownload is now done by tascam-firmware downloader. + See: + http://usb-midi-fw.sourceforge.net/tascam-firmware.tar.gz + +2003-06-18 Karsten Wiese + Version 0.0.5: + changed to compile with kernel 2.4.21 and alsa 0.9.4 + +2002-10-16 Karsten Wiese + Version 0.0.4: + compiles again with alsa-current. + USB_ISO_ASAP not used anymore (most of the time), instead + urb->start_frame is calculated here now, some calls inside usb-driver don't need to happen anymore. + + To get the best out of this: + Disable APM-support in the kernel as APM-BIOS calls (once each second) hard disable interrupt for many precious milliseconds. + This helped me much on my slowish PII 400 & PIII 500. + ACPI yet untested but might cause the same bad behaviour. + Use a kernel with lowlatency and preemptiv patches applied. + To autoload snd-usb-midi append a line + post-install snd-usb-us428 modprobe snd-usb-midi + to /etc/modules.conf. + + known problems: + sliders, knobs, lights not yet handled except MASTER Volume slider. + "pcm -c 2" doesn't work. "pcm -c 2 -m direct_interleaved" does. + KDE3: "Enable full duplex operation" deadlocks. + + +2002-08-31 Karsten Wiese + Version 0.0.3: audio also simplex; + simplifying: iso urbs only 1 packet, melted structs. + ASYNC_UNLINK not used anymore: no more crashes so far..... + for alsa 0.9 rc3. + +2002-08-09 Karsten Wiese + Version 0.0.2: midi works with snd-usb-midi, audio (only fullduplex now) with i.e. bristol. + The firmware has been sniffed from win2k us-428 driver 3.09. + + * Copyright (c) 2002 Karsten Wiese + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "usx2y.h" +#include "usbusx2y.h" +#include "usX2Yhwdep.h" + + + +MODULE_AUTHOR("Karsten Wiese "); +MODULE_DESCRIPTION("TASCAM "NAME_ALLCAPS" Version 0.7.2"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{TASCAM(0x1604), "NAME_ALLCAPS"(0x8001)(0x8005)(0x8007) }}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */ +static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ +static int boot_devs; + +module_param_array(index, int, boot_devs, 0444); +MODULE_PARM_DESC(index, "Index value for "NAME_ALLCAPS"."); +module_param_array(id, charp, boot_devs, 0444); +MODULE_PARM_DESC(id, "ID string for "NAME_ALLCAPS"."); +module_param_array(enable, bool, boot_devs, 0444); +MODULE_PARM_DESC(enable, "Enable "NAME_ALLCAPS"."); + + +static int snd_usX2Y_card_used[SNDRV_CARDS]; + +static void usX2Y_usb_disconnect(struct usb_device* usb_device, void* ptr); +static void snd_usX2Y_card_private_free(snd_card_t *card); + +/* + * pipe 4 is used for switching the lamps, setting samplerate, volumes .... + */ +static void i_usX2Y_Out04Int(struct urb* urb, struct pt_regs *regs) +{ +#ifdef CONFIG_SND_DEBUG + if (urb->status) { + int i; + usX2Ydev_t* usX2Y = urb->context; + for (i = 0; i < 10 && usX2Y->AS04.urb[i] != urb; i++); + snd_printdd("i_usX2Y_Out04Int() urb %i status=%i\n", i, urb->status); + } +#endif +} + +static void i_usX2Y_In04Int(struct urb* urb, struct pt_regs *regs) +{ + int err = 0; + usX2Ydev_t *usX2Y = urb->context; + us428ctls_sharedmem_t *us428ctls = usX2Y->us428ctls_sharedmem; + + usX2Y->In04IntCalls++; + + if (urb->status) { + snd_printdd("Interrupt Pipe 4 came back with status=%i\n", urb->status); + return; + } + + // printk("%i:0x%02X ", 8, (int)((unsigned char*)usX2Y->In04Buf)[8]); Master volume shows 0 here if fader is at max during boot ?!? + if (us428ctls) { + int diff = -1; + if (-2 == us428ctls->CtlSnapShotLast) { + diff = 0; + memcpy(usX2Y->In04Last, usX2Y->In04Buf, sizeof(usX2Y->In04Last)); + us428ctls->CtlSnapShotLast = -1; + } else { + int i; + for (i = 0; i < 21; i++) { + if (usX2Y->In04Last[i] != ((char*)usX2Y->In04Buf)[i]) { + if (diff < 0) + diff = i; + usX2Y->In04Last[i] = ((char*)usX2Y->In04Buf)[i]; + } + } + } + if (0 <= diff) { + int n = us428ctls->CtlSnapShotLast + 1; + if (n >= N_us428_ctl_BUFS || n < 0) + n = 0; + memcpy(us428ctls->CtlSnapShot + n, usX2Y->In04Buf, sizeof(us428ctls->CtlSnapShot[0])); + us428ctls->CtlSnapShotDiffersAt[n] = diff; + us428ctls->CtlSnapShotLast = n; + wake_up(&usX2Y->us428ctls_wait_queue_head); + } + } + + + if (usX2Y->US04) { + if (0 == usX2Y->US04->submitted) + do + err = usb_submit_urb(usX2Y->US04->urb[usX2Y->US04->submitted++], GFP_ATOMIC); + while (!err && usX2Y->US04->submitted < usX2Y->US04->len); + } else + if (us428ctls && us428ctls->p4outLast >= 0 && us428ctls->p4outLast < N_us428_p4out_BUFS) { + if (us428ctls->p4outLast != us428ctls->p4outSent) { + int j, send = us428ctls->p4outSent + 1; + if (send >= N_us428_p4out_BUFS) + send = 0; + for (j = 0; j < URBS_AsyncSeq && !err; ++j) + if (0 == usX2Y->AS04.urb[j]->status) { + us428_p4out_t *p4out = us428ctls->p4out + send; // FIXME if more then 1 p4out is new, 1 gets lost. + usb_fill_bulk_urb(usX2Y->AS04.urb[j], usX2Y->chip.dev, + usb_sndbulkpipe(usX2Y->chip.dev, 0x04), &p4out->val.vol, + p4out->type == eLT_Light ? sizeof(us428_lights_t) : 5, + i_usX2Y_Out04Int, usX2Y); + err = usb_submit_urb(usX2Y->AS04.urb[j], GFP_ATOMIC); + us428ctls->p4outSent = send; + break; + } + } + } + + if (err) { + snd_printk("In04Int() usb_submit_urb err=%i\n", err); + } + + urb->dev = usX2Y->chip.dev; + usb_submit_urb(urb, GFP_ATOMIC); +} + +/* + * Prepare some urbs + */ +int usX2Y_AsyncSeq04_init(usX2Ydev_t* usX2Y) +{ + int err = 0, + i; + + if (NULL == (usX2Y->AS04.buffer = kmalloc(URB_DataLen_AsyncSeq*URBS_AsyncSeq, GFP_KERNEL))) { + err = -ENOMEM; + } else + for (i = 0; i < URBS_AsyncSeq; ++i) { + if (NULL == (usX2Y->AS04.urb[i] = usb_alloc_urb(0, GFP_KERNEL))) { + err = -ENOMEM; + break; + } + usb_fill_bulk_urb( usX2Y->AS04.urb[i], usX2Y->chip.dev, + usb_sndbulkpipe(usX2Y->chip.dev, 0x04), + usX2Y->AS04.buffer + URB_DataLen_AsyncSeq*i, 0, + i_usX2Y_Out04Int, usX2Y + ); + } + return err; +} + +int usX2Y_In04_init(usX2Ydev_t* usX2Y) +{ + int err = 0; + if (! (usX2Y->In04urb = usb_alloc_urb(0, GFP_KERNEL))) + return -ENOMEM; + + if (! (usX2Y->In04Buf = kmalloc(21, GFP_KERNEL))) { + usb_free_urb(usX2Y->In04urb); + return -ENOMEM; + } + + init_waitqueue_head(&usX2Y->In04WaitQueue); + usb_fill_int_urb(usX2Y->In04urb, usX2Y->chip.dev, usb_rcvintpipe(usX2Y->chip.dev, 0x4), + usX2Y->In04Buf, 21, + i_usX2Y_In04Int, usX2Y, + 10); + err = usb_submit_urb(usX2Y->In04urb, GFP_KERNEL); + return err; +} + +static void usX2Y_unlinkSeq(snd_usX2Y_AsyncSeq_t* S) +{ + int i; + for (i = 0; i < URBS_AsyncSeq; ++i) { + if (S[i].urb) { + usb_unlink_urb(S->urb[i]); + usb_free_urb(S->urb[i]); + S->urb[i] = NULL; + } + } + if (S->buffer) + kfree(S->buffer); +} + + +static struct usb_device_id snd_usX2Y_usb_id_table[] = { + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x1604, + .idProduct = USB_ID_US428 + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x1604, + .idProduct = USB_ID_US122 + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = 0x1604, + .idProduct = USB_ID_US224 + }, + { /* terminator */ } +}; + +static snd_card_t* usX2Y_create_card(struct usb_device* device) +{ + int dev; + snd_card_t* card; + for (dev = 0; dev < SNDRV_CARDS; ++dev) + if (enable[dev] && !snd_usX2Y_card_used[dev]) + break; + if (dev >= SNDRV_CARDS) + return NULL; + card = snd_card_new(index[dev], id[dev], THIS_MODULE, sizeof(usX2Ydev_t)); + if (!card) + return NULL; + snd_usX2Y_card_used[usX2Y(card)->chip.index = dev] = 1; + card->private_free = snd_usX2Y_card_private_free; + usX2Y(card)->chip.dev = device; + usX2Y(card)->chip.card = card; + init_MUTEX (&usX2Y(card)->open_mutex); + INIT_LIST_HEAD(&usX2Y(card)->chip.midi_list); + strcpy(card->driver, "USB "NAME_ALLCAPS""); + sprintf(card->shortname, "TASCAM "NAME_ALLCAPS""); + sprintf(card->longname, "%s (%x:%x if %d at %03d/%03d)", + card->shortname, + device->descriptor.idVendor, device->descriptor.idProduct, + 0,//us428(card)->usbmidi.ifnum, + usX2Y(card)->chip.dev->bus->busnum, usX2Y(card)->chip.dev->devnum + ); + snd_card_set_dev(card, &device->dev); + return card; +} + + +static void* usX2Y_usb_probe(struct usb_device* device, struct usb_interface *intf, const struct usb_device_id* device_id) +{ + int err; + snd_card_t* card; + if (device->descriptor.idVendor != 0x1604 || + (device->descriptor.idProduct != USB_ID_US122 && + device->descriptor.idProduct != USB_ID_US224 && + device->descriptor.idProduct != USB_ID_US428) || + !(card = usX2Y_create_card(device))) + return NULL; + if ((err = usX2Y_hwdep_new(card, device)) < 0 || + (err = snd_card_register(card)) < 0) { + snd_card_free(card); + return NULL; + } + return card; +} + +/* + * new 2.5 USB kernel API + */ +static int snd_usX2Y_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + void *chip; + chip = usX2Y_usb_probe(interface_to_usbdev(intf), intf, id); + if (chip) { + dev_set_drvdata(&intf->dev, chip); + return 0; + } else + return -EIO; +} + +static void snd_usX2Y_disconnect(struct usb_interface *intf) +{ + usX2Y_usb_disconnect(interface_to_usbdev(intf), + dev_get_drvdata(&intf->dev)); +} + +MODULE_DEVICE_TABLE(usb, snd_usX2Y_usb_id_table); +static struct usb_driver snd_usX2Y_usb_driver = { + .owner = THIS_MODULE, + .name = "snd-usb-usx2y", + .probe = snd_usX2Y_probe, + .disconnect = snd_usX2Y_disconnect, + .id_table = snd_usX2Y_usb_id_table, +}; + +static void snd_usX2Y_card_private_free(snd_card_t *card) +{ + if (usX2Y(card)->In04Buf) + kfree(usX2Y(card)->In04Buf); + usb_free_urb(usX2Y(card)->In04urb); + if (usX2Y(card)->us428ctls_sharedmem) + snd_free_pages(usX2Y(card)->us428ctls_sharedmem, sizeof(*usX2Y(card)->us428ctls_sharedmem)); + if (usX2Y(card)->chip.index >= 0 && usX2Y(card)->chip.index < SNDRV_CARDS) + snd_usX2Y_card_used[usX2Y(card)->chip.index] = 0; +} + +/* + * Frees the device. + */ +static void usX2Y_usb_disconnect(struct usb_device* device, void* ptr) +{ + if (ptr) { + usX2Ydev_t* usX2Y = usX2Y((snd_card_t*)ptr); + struct list_head* p; + if (usX2Y->chip_status == USX2Y_STAT_CHIP_HUP) // on 2.6.1 kernel snd_usbmidi_disconnect() + return; // calls us back. better leave :-) . + usX2Y->chip.shutdown = 1; + usX2Y->chip_status = USX2Y_STAT_CHIP_HUP; + usX2Y_unlinkSeq(&usX2Y->AS04); + usb_unlink_urb(usX2Y->In04urb); + snd_card_disconnect((snd_card_t*)ptr); + /* release the midi resources */ + list_for_each(p, &usX2Y->chip.midi_list) { + snd_usbmidi_disconnect(p, &snd_usX2Y_usb_driver); + } + if (usX2Y->us428ctls_sharedmem) + wake_up(&usX2Y->us428ctls_wait_queue_head); + snd_card_free_in_thread((snd_card_t*)ptr); + } +} + +static int __init snd_usX2Y_module_init(void) +{ + return usb_register(&snd_usX2Y_usb_driver); +} + +static void __exit snd_usX2Y_module_exit(void) +{ + usb_deregister(&snd_usX2Y_usb_driver); +} + +module_init(snd_usX2Y_module_init) +module_exit(snd_usX2Y_module_exit) --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/sound/usb/usx2y/usbusx2y.h 2004-09-02 20:51:53.000000000 -0700 @@ -0,0 +1,61 @@ +#ifndef USBUSX2Y_H +#define USBUSX2Y_H +#include "../usbaudio.h" +#include "usbus428ctldefs.h" + +#define NRURBS 2 /* */ +#define NRPACKS 1 /* FIXME: Currently only 1 works. + usb-frames/ms per urb: 1 and 2 are supported. + setting to 2 will PERHAPS make it easier for slow machines. + Jitter will be higher though. + On my PIII 500Mhz Laptop setting to 1 is the only way to go + for PLAYING synths. i.e. Jack & Aeolus sound quit nicely + at 4 periods 64 frames. + */ + +#define URBS_AsyncSeq 10 +#define URB_DataLen_AsyncSeq 32 +typedef struct { + struct urb* urb[URBS_AsyncSeq]; + char* buffer; +} snd_usX2Y_AsyncSeq_t; + +typedef struct { + int submitted; + int len; + struct urb* urb[0]; +} snd_usX2Y_urbSeq_t; + +typedef struct snd_usX2Y_substream snd_usX2Y_substream_t; + +typedef struct { + snd_usb_audio_t chip; + int stride; + struct urb *In04urb; + void *In04Buf; + char In04Last[24]; + unsigned In04IntCalls; + snd_usX2Y_urbSeq_t *US04; + wait_queue_head_t In04WaitQueue; + snd_usX2Y_AsyncSeq_t AS04; + unsigned int rate, + format; + int refframes; + int chip_status; + struct semaphore open_mutex; + us428ctls_sharedmem_t *us428ctls_sharedmem; + wait_queue_head_t us428ctls_wait_queue_head; + snd_usX2Y_substream_t *substream[4]; +} usX2Ydev_t; + + +#define usX2Y(c) ((usX2Ydev_t*)(c)->private_data) + +int usX2Y_audio_create(snd_card_t* card); + +int usX2Y_AsyncSeq04_init(usX2Ydev_t* usX2Y); +int usX2Y_In04_init(usX2Ydev_t* usX2Y); + +#define NAME_ALLCAPS "US-X2Y" + +#endif --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/sound/usb/usx2y/usx2y.h 2004-09-02 20:51:53.000000000 -0700 @@ -0,0 +1,49 @@ +/* + * Driver for Tascam US-X2Y USB soundcards + * + * Copyright (c) 2003 by Karsten Wiese + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SOUND_USX2Y_COMMON_H +#define __SOUND_USX2Y_COMMON_H + + +#define USX2Y_DRIVER_VERSION 0x0100 /* 0.1.0 */ + + +/* hwdep id string */ +#define SND_USX2Y_LOADER_ID "USX2Y Loader" + +/* hardware type */ +enum { + USX2Y_TYPE_122, + USX2Y_TYPE_224, + USX2Y_TYPE_428, + USX2Y_TYPE_NUMS +}; + +#define USB_ID_US122 0x8007 +#define USB_ID_US224 0x8005 +#define USB_ID_US428 0x8001 + +/* chip status */ +enum { + USX2Y_STAT_CHIP_INIT = (1 << 0), /* all operational */ + USX2Y_STAT_CHIP_HUP = (1 << 31), /* all operational */ +}; + +#endif /* __SOUND_USX2Y_COMMON_H */ --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/sound/usb/usx2y/usX2Yhwdep.c 2004-09-02 20:51:53.000000000 -0700 @@ -0,0 +1,282 @@ +/* + * Driver for Tascam US-X2Y USB soundcards + * + * FPGA Loader + ALSA Startup + * + * Copyright (c) 2003 by Karsten Wiese + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include "usx2y.h" +#include "usbusx2y.h" +#include "usX2Yhwdep.h" + + +static struct page * snd_us428ctls_vm_nopage(struct vm_area_struct *area, unsigned long address, int *type) +{ + unsigned long offset; + struct page * page; + void *vaddr; + + snd_printdd("ENTER, start %lXh, ofs %lXh, pgoff %ld, addr %lXh\n", + area->vm_start, + address - area->vm_start, + (address - area->vm_start) >> PAGE_SHIFT, + address); + + offset = area->vm_pgoff << PAGE_SHIFT; + offset += address - area->vm_start; + snd_assert((offset % PAGE_SIZE) == 0, return NOPAGE_OOM); + vaddr = (char*)((usX2Ydev_t*)area->vm_private_data)->us428ctls_sharedmem + offset; + page = virt_to_page(vaddr); + get_page(page); + snd_printdd( "vaddr=%p made us428ctls_vm_nopage() return %p; offset=%lX\n", vaddr, page, offset); + + if (type) + *type = VM_FAULT_MINOR; + + return page; +} + +static struct vm_operations_struct us428ctls_vm_ops = { + .nopage = snd_us428ctls_vm_nopage, +}; + +static int snd_us428ctls_mmap(snd_hwdep_t * hw, struct file *filp, struct vm_area_struct *area) +{ + unsigned long size = (unsigned long)(area->vm_end - area->vm_start); + usX2Ydev_t *us428 = (usX2Ydev_t*)hw->private_data; + + // FIXME this hwdep interface is used twice: fpga download and mmap for controlling Lights etc. Maybe better using 2 hwdep devs? + // so as long as the device isn't fully initialised yet we return -EBUSY here. + if (!(((usX2Ydev_t*)hw->private_data)->chip_status & USX2Y_STAT_CHIP_INIT)) + return -EBUSY; + + /* if userspace tries to mmap beyond end of our buffer, fail */ + if (size > ((PAGE_SIZE - 1 + sizeof(us428ctls_sharedmem_t)) / PAGE_SIZE) * PAGE_SIZE) { + snd_printd( "%lu > %lu\n", size, (unsigned long)sizeof(us428ctls_sharedmem_t)); + return -EINVAL; + } + + if (!us428->us428ctls_sharedmem) { + init_waitqueue_head(&us428->us428ctls_wait_queue_head); + if(!(us428->us428ctls_sharedmem = snd_malloc_pages(sizeof(us428ctls_sharedmem_t), GFP_KERNEL))) + return -ENOMEM; + memset(us428->us428ctls_sharedmem, -1, sizeof(us428ctls_sharedmem_t)); + us428->us428ctls_sharedmem->CtlSnapShotLast = -2; + } + area->vm_ops = &us428ctls_vm_ops; + area->vm_flags |= VM_RESERVED; + area->vm_private_data = hw->private_data; + return 0; +} + +static unsigned int snd_us428ctls_poll(snd_hwdep_t *hw, struct file *file, poll_table *wait) +{ + unsigned int mask = 0; + usX2Ydev_t *us428 = (usX2Ydev_t*)hw->private_data; + static unsigned LastN; + + if (us428->chip_status & USX2Y_STAT_CHIP_HUP) + return POLLHUP; + + poll_wait(file, &us428->us428ctls_wait_queue_head, wait); + + down(&us428->open_mutex); + if (us428->us428ctls_sharedmem + && us428->us428ctls_sharedmem->CtlSnapShotLast != LastN) { + mask |= POLLIN; + LastN = us428->us428ctls_sharedmem->CtlSnapShotLast; + } + up(&us428->open_mutex); + + return mask; +} + + +static int snd_usX2Y_hwdep_open(snd_hwdep_t *hw, struct file *file) +{ + return 0; +} + +static int snd_usX2Y_hwdep_release(snd_hwdep_t *hw, struct file *file) +{ + return 0; +} + +static int snd_usX2Y_hwdep_dsp_status(snd_hwdep_t *hw, snd_hwdep_dsp_status_t *info) +{ + static char *type_ids[USX2Y_TYPE_NUMS] = { + [USX2Y_TYPE_122] = "us122", + [USX2Y_TYPE_224] = "us224", + [USX2Y_TYPE_428] = "us428", + }; + int id = -1; + + switch (((usX2Ydev_t*)hw->private_data)->chip.dev->descriptor.idProduct) { + case USB_ID_US122: + id = USX2Y_TYPE_122; + break; + case USB_ID_US224: + id = USX2Y_TYPE_224; + break; + case USB_ID_US428: + id = USX2Y_TYPE_428; + break; + } + if (0 > id) + return -ENODEV; + strcpy(info->id, type_ids[id]); + info->num_dsps = 2; // 0: Prepad Data, 1: FPGA Code + if (((usX2Ydev_t*)hw->private_data)->chip_status & USX2Y_STAT_CHIP_INIT) + info->chip_ready = 1; + info->version = USX2Y_DRIVER_VERSION; + return 0; +} + + +static int usX2Y_create_usbmidi(snd_card_t* card ) +{ + static snd_usb_midi_endpoint_info_t quirk_data_1 = { + .out_ep =0x06, + .in_ep = 0x06, + .out_cables = 0x001, + .in_cables = 0x001 + }; + static snd_usb_audio_quirk_t quirk_1 = { + .vendor_name = "TASCAM", + .product_name = NAME_ALLCAPS, + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = &quirk_data_1 + }; + static snd_usb_midi_endpoint_info_t quirk_data_2 = { + .out_ep =0x06, + .in_ep = 0x06, + .out_cables = 0x003, + .in_cables = 0x003 + }; + static snd_usb_audio_quirk_t quirk_2 = { + .vendor_name = "TASCAM", + .product_name = "US428", + .ifnum = 0, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = &quirk_data_2 + }; + struct usb_device *dev = usX2Y(card)->chip.dev; + struct usb_interface *iface = usb_ifnum_to_if(dev, 0); + snd_usb_audio_quirk_t *quirk = dev->descriptor.idProduct == USB_ID_US428 ? &quirk_2 : &quirk_1; + + snd_printdd("usX2Y_create_usbmidi \n"); + return snd_usb_create_midi_interface(&usX2Y(card)->chip, iface, quirk); +} + +static int usX2Y_create_alsa_devices(snd_card_t* card) +{ + int err; + + do { + if ((err = usX2Y_create_usbmidi(card)) < 0) { + snd_printk("usX2Y_create_alsa_devices: usX2Y_create_usbmidi error %i \n", err); + break; + } + if ((err = usX2Y_audio_create(card)) < 0) + break; + if ((err = snd_card_register(card)) < 0) + break; + } while (0); + + return err; +} + +static int snd_usX2Y_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp) +{ + usX2Ydev_t *priv = hw->private_data; + int lret, err = -EINVAL; + snd_printdd( "dsp_load %s\n", dsp->name); + + if (access_ok(VERIFY_READ, dsp->image, dsp->length)) { + struct usb_device* dev = priv->chip.dev; + char *buf = kmalloc(dsp->length, GFP_KERNEL); + if (!buf) + return -ENOMEM; + if (copy_from_user(buf, dsp->image, dsp->length)) { + kfree(buf); + return -EFAULT; + } + err = usb_set_interface(dev, 0, 1); + if (err) + snd_printk("usb_set_interface error \n"); + else + err = usb_bulk_msg(dev, usb_sndbulkpipe(dev, 2), buf, dsp->length, &lret, 6*HZ); + kfree(buf); + } + if (err) + return err; + if (dsp->index == 1) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/4); // give the device some time + err = usX2Y_AsyncSeq04_init(priv); + if (err) { + snd_printk("usX2Y_AsyncSeq04_init error \n"); + return err; + } + err = usX2Y_In04_init(priv); + if (err) { + snd_printk("usX2Y_In04_init error \n"); + return err; + } + err = usX2Y_create_alsa_devices(hw->card); + if (err) { + snd_printk("usX2Y_create_alsa_devices error %i \n", err); + snd_card_free(hw->card); + return err; + } + priv->chip_status |= USX2Y_STAT_CHIP_INIT; + snd_printdd("%s: alsa all started\n", hw->name); + } + return err; +} + + +int usX2Y_hwdep_new(snd_card_t* card, struct usb_device* device) +{ + int err; + snd_hwdep_t *hw; + + if ((err = snd_hwdep_new(card, SND_USX2Y_LOADER_ID, 0, &hw)) < 0) + return err; + + hw->iface = SNDRV_HWDEP_IFACE_USX2Y; + hw->private_data = usX2Y(card); + hw->ops.open = snd_usX2Y_hwdep_open; + hw->ops.release = snd_usX2Y_hwdep_release; + hw->ops.dsp_status = snd_usX2Y_hwdep_dsp_status; + hw->ops.dsp_load = snd_usX2Y_hwdep_dsp_load; + hw->ops.mmap = snd_us428ctls_mmap; + hw->ops.poll = snd_us428ctls_poll; + hw->exclusive = 1; + sprintf(hw->name, "/proc/bus/usb/%03d/%03d", device->bus->busnum, device->devnum); + return 0; +} + --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25/sound/usb/usx2y/usX2Yhwdep.h 2004-09-02 20:51:53.000000000 -0700 @@ -0,0 +1,6 @@ +#ifndef USX2YHWDEP_H +#define USX2YHWDEP_H + +int usX2Y_hwdep_new(snd_card_t* card, struct usb_device* device); + +#endif